Merge "kobject: increase number of kobject uevent pointers to 64" into msm-4.8
diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt
index e6782d5..38271eb 100644
--- a/Documentation/devicetree/bindings/arm/cpus.txt
+++ b/Documentation/devicetree/bindings/arm/cpus.txt
@@ -220,6 +220,20 @@
 			  property identifying a 64-bit zero-initialised
 			  memory location.
 
+	- efficiency
+		Usage: optional.
+		Value type: <u32>
+		Definition:
+			# Specifies the CPU efficiency. The CPU efficiency is
+			a unit less number and it is intended to show relative
+			performance of CPUs when normalized for clock frequency
+			(instructions per cycle performance).
+
+			The efficiency of a CPU can vary across SoCs depending
+			on the cache size, bus interconnect frequencies etc.
+			This value overrides the default efficiency value
+			defined for the corresponding CPU architecture.
+
 	- qcom,saw
 		Usage: required for systems that have an "enable-method"
 		       property value of "qcom,kpss-acc-v1" or
diff --git a/Documentation/devicetree/bindings/arm/msm/spcom.txt b/Documentation/devicetree/bindings/arm/msm/spcom.txt
new file mode 100644
index 0000000..36a07ec
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/msm/spcom.txt
@@ -0,0 +1,11 @@
+Qualcomm Technologies, Inc. Secure Proccessor Communication (spcom)
+
+Required properties:
+-compatible : should be "qcom,spcom"
+-qcom,spcom-ch-names: predefined channels name string
+
+Example:
+    qcom,spcom {
+            compatible = "qcom,spcom";
+            qcom,spcom-ch-names = "sp_kernel" , "sp_ssr";
+    };
diff --git a/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt b/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt
new file mode 100644
index 0000000..7b89497
--- /dev/null
+++ b/Documentation/devicetree/bindings/bluetooth/bluetooth_power.txt
@@ -0,0 +1,59 @@
+* Bluetooth Controller
+Bluetooth controller communicates with the Bluetooth Host using HCI Transport layer.
+HCI Transport layer can be based on UART or USB serial communication protocol.
+
+Required properties:
+  - compatible: Should be set to one of the following:
+	qca,ar3002
+	qca,qca6174
+	qca,wcn3990
+  - qca,bt-reset-gpio: GPIO pin to bring BT Controller out of reset
+
+Optional properties:
+  - qca,bt-vdd-pa-supply: Bluetooth VDD PA regulator handle
+  - qca,bt-vdd-io-supply: Bluetooth VDD IO regulator handle
+  - qca,bt-vdd-ldo-supply: Bluetooth VDD LDO regulator handle. Kept under optional parameters
+		as some of the chipsets doesn't require ldo or it may use from same vddio.
+  - qca,bt-vdd-xtal-supply: Bluetooth VDD XTAL regulator handle
+  - qca,bt-vdd-core-supply: Bluetooth VDD CORE regulator handle
+  - qca,bt-chip-pwd-supply: Chip power down gpio is required when bluetooth module
+		and other modules like wifi co-exist in a singe chip and shares a
+		common gpio to bring chip out of reset.
+  - qca,bt-vdd-pa-voltage-level: specifies VDD PA voltage levels for supply.
+		Should be specified in pairs (min, max), units uV
+  - qca,bt-vdd-io-voltage-level: specifies VDD IO voltage levels for supply.
+		Should be specified in pairs (min, max), units uV
+  - qca,bt-vdd-ldo-voltage-level: specifies VDD LDO voltage levels for supply.
+		Should be specified in pairs (min, max), units uV
+  - qca,bt-vdd-xtal-voltage-level: specifies VDD XTAL voltage levels for supply.
+                Should be specified in pairs (min, max), units uV
+  - qca,bt-vdd-core-voltage-level: specifies VDD CORE voltage levels for supply.
+                Should be specified in pairs (min, max), units uV
+ - qca,bt-vdd-io-current-level: specifies VDD IO current level in microamps
+ - qca,bt-vdd-xtal-current-level: specifies VDD XTAL current level in microamps
+ - qca,bt-vdd-core-current-level: specifies VDD CORE current level in microamps.
+ - qca,bt-vdd-ldo-current-level: specifies VDD LDO current level in microamps.
+ - qca,bt-vdd-pa-current-level: specifies VDD PA current level in microamps.
+ - qca,bt-chip-pwd-current-level: specifies Chip Power current level in microamps.
+
+Example:
+  bt-ar3002 {
+    compatible = "qca,ar3002";
+    qca,bt-reset-gpio = <&pm8941_gpios 34 0>;
+    qca,bt-vdd-io-supply = <&pm8941_s3>;
+    qca,bt-vdd-pa-supply = <&pm8941_l19>;
+    qca,bt-vdd-xtal-supply = <&pm8994_l30>;
+    qca,bt-vdd-core-supply = <&pm8994_s3>;
+    qca,bt-chip-pwd-supply = <&ath_chip_pwd_l>;
+
+    qca,bt-vdd-io-voltage-level = <1800000 1800000>;
+    qca,bt-vdd-pa-voltage-level = <2900000 2900000>;
+    qca,bt-vdd-xtal-voltage-level = <1800000 1800000>;
+    qca,bt-vdd-core-voltage-level = <1300000 1300000>;
+
+    qca,bt-vdd-io-current-level = <1>; /* LPM/PFM */
+    qca,bt-vdd-xtal-current-level = <1>; /* LPM/PFM */
+    qca,bt-vdd-core-current-level = <1>; /* LPM/PFM */
+    qca,bt-vdd-ldo-current-level = <1>; /* LPM/PFM */
+    qca,bt-vdd-pa-current-level = <1>; /* LPM/PFM */
+  };
diff --git a/Documentation/devicetree/bindings/bluetooth/btfm_slim.txt b/Documentation/devicetree/bindings/bluetooth/btfm_slim.txt
new file mode 100644
index 0000000..901db5f
--- /dev/null
+++ b/Documentation/devicetree/bindings/bluetooth/btfm_slim.txt
@@ -0,0 +1,20 @@
+* BTFM Slimbus Slave Driver
+BTFM Slimbus Slave driver configure and initialize slimbus slave device.
+Bluetooth SCO and FM Audio data is transferred over slimbus interface.
+
+Required properties:
+  - compatible: Should be set to one of the following:
+     btfmslim_slave
+  - qcom,btfm-slim-ifd: BTFM slimbus slave device entry name
+
+Optional properties:
+  - qcom,btfm-slim-ifd-elemental-addr: BTFM slimbus slave device enumeration
+  address
+
+Example:
+  btfmslim_codec: wcn3990 {
+    compatible = "qcom,btfmslim_slave";
+    elemental-addr = [00 01 20 02 17 02];
+    qcom,btfm-slim-ifd = "btfmslim_slave_ifd";
+    qcom,btfm-slim-ifd-elemental-addr = [00 00 20 02 17 02];
+  };
diff --git a/Documentation/devicetree/bindings/cnss/icnss.txt b/Documentation/devicetree/bindings/cnss/icnss.txt
index e19a434..15feda3 100644
--- a/Documentation/devicetree/bindings/cnss/icnss.txt
+++ b/Documentation/devicetree/bindings/cnss/icnss.txt
@@ -12,17 +12,9 @@
   - reg-names: Names of the memory regions defined in reg entry
   - interrupts: Copy engine interrupt table
   - qcom,wlan-msa-memory: MSA memory size
-  - clocks: List of clock phandles
-  - clock-names: List of clock names corresponding to the "clocks" property
   - iommus: SMMUs and corresponding Stream IDs needed by WLAN
   - qcom,wlan-smmu-iova-address: I/O virtual address range as <start length>
     format to be used for allocations associated between WLAN and SMMU
-  - <supply-name>-supply: phandle to the regulator device tree node
-                          Required "supply-name" is "vdd-0.8-cx-mx".
-  - qcom,<supply>-config - specifies voltage levels for supply. Should be
-                           specified in pairs (min, max), units uV.  There can
-                           be optional load in uA and Regulator settle delay in
-                           uS.
 
 Optional properties:
   - qcom,icnss-vadc: VADC handle for vph_pwr read APIs.
@@ -34,8 +26,6 @@
         compatible = "qcom,icnss";
         reg = <0x0a000000 0x1000000>;
         reg-names = "membase";
-        clocks = <&clock_gcc clk_aggre2_noc_clk>;
-        clock-names = "smmu_aggre2_noc_clk";
         iommus = <&anoc2_smmu 0x1900>,
                  <&anoc2_smmu 0x1901>;
         qcom,wlan-smmu-iova-address = <0 0x10000000>;
@@ -53,6 +43,4 @@
 		   <0 140 0 /* CE10 */ >,
 		   <0 141 0 /* CE11 */ >;
         qcom,wlan-msa-memory = <0x200000>;
-	vdd-0.8-cx-mx-supply = <&pm8998_l5>;
-	qcom,vdd-0.8-cx-mx-config = <800000 800000 2400 1000>;
     };
diff --git a/Documentation/devicetree/bindings/display/msm/sde.txt b/Documentation/devicetree/bindings/display/msm/sde.txt
index 33de596..e4ba5be 100644
--- a/Documentation/devicetree/bindings/display/msm/sde.txt
+++ b/Documentation/devicetree/bindings/display/msm/sde.txt
@@ -147,12 +147,17 @@
 				indicating the danger luts on sspp.
 - qcom,sde-sspp-safe-lut:	A 3 cell property, with a format of <linear, tile, nrt>,
 				indicating the safe luts on sspp.
-- qcom,sde-sspp-max-rects:	Array of u32 values indicating maximum rectangles supported
-				on each sspp. This property is for multirect feature support.
-				Number of offsets defined should match the number of
-				offsets defined in property: qcom,sde-sspp-off.
 - qcom,sde-sspp-excl-rect:	Array of u32 values indicating exclusion rectangle
 				support on each sspp.
+- qcom,sde-sspp-smart-dma-priority:	Array of u32 values indicating hw pipe
+					priority of secondary rectangles when smart dma
+					is supported. Number of priority values should
+					match the number of offsets defined in
+					qcom,sde-sspp-off node. Zero indicates no support
+					for smart dma for the sspp.
+- qcom,sde-smart-dma-rev:	A string entry indicating the smart dma version
+				supported on the device. Supported entries are
+				"smart_dma_v1" and "smart_dma_v2".
 - qcom,sde-intf-type:		Array of string provides the interface type information.
 				Possible string values
 					"dsi" - dsi display interface
@@ -237,6 +242,11 @@
 				control register. Number of offsets defined should
 				match the number of offsets defined in
 				property: qcom,sde-wb-off
+- qcom,sde-reg-dma-off:         Offset of the register dma hardware block from
+				"regdma_phys" defined in reg property.
+- qcom,sde-reg-dma-version:	Version of the reg dma hardware block.
+- qcom,sde-reg-dma-trigger-off: Offset of the lut dma trigger reg from "mdp_phys"
+				defined in reg property.
 Bus Scaling Data:
 - qcom,msm-bus,name:		String property describing client name.
 - qcom,msm-bus,num-cases:	This is the number of Bus Scaling use cases
@@ -268,10 +278,12 @@
     compatible = "qcom,sde-kms";
     reg = <0x00900000 0x90000>,
           <0x009b0000 0x1040>,
-          <0x009b8000 0x1040>;
+          <0x009b8000 0x1040>,
+          <0x0aeac000 0x00f0>;
     reg-names = "mdp_phys",
       "vbif_phys",
-      "vbif_nrt_phys";
+      "vbif_nrt_phys",
+      "regdma_phys";
     clocks = <&clock_mmss clk_mdss_ahb_clk>,
       <&clock_mmss clk_mdss_axi_clk>,
       <&clock_mmss clk_mdp_clk_src>,
@@ -378,6 +390,11 @@
 				1 1 1 1
 				1 1
 				1 1>;
+    qcom,sde-sspp-smart-dma-priority = <0 0 0 0
+					0 0 0 0
+					0 0
+					1 2>;
+    qcom,sde-smart-dma-rev = "smart_dma_v2";
     qcom,sde-te-off = <0x100>;
     qcom,sde-te2-off = <0x100>;
     qcom,sde-te-size = <0xffff>;
diff --git a/Documentation/devicetree/bindings/dma/sps/sps.txt b/Documentation/devicetree/bindings/dma/sps/sps.txt
new file mode 100644
index 0000000..92dda7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/sps/sps.txt
@@ -0,0 +1,47 @@
+SPS (Smart Peripheral Switch) may be used as a DMA engine to move data
+in either the Peripheral-to-Peripheral (a.k.a. BAM-to-BAM) mode or the
+Peripheral-to-Memory (a.k.a. BAM-System) mode. SPS includes BAM (Bus
+Access Module) hardware block, BAM DMA peripheral, and pipe memory.
+
+Required property:
+  - compatible: should be "qcom,msm_sps" or "qcom,msm_sps_4k"
+
+Optional properties:
+  - reg: offset and size for the memory mapping, including maps for
+    BAM DMA BAM, BAM DMA peripheral, pipe memory and reserved memory.
+  - reg-names: indicates various resources passed to driver (via reg
+    property) by name. "reg-names" examples are "bam_mem", "core_mem"
+    , "pipe_mem" and "res_mem".
+  - interrupts: IRQ line
+  - qcom,device-type: specify the device configuration of BAM DMA and
+    pipe memory. Can be one of
+        1 - With BAM DMA and without pipe memory
+        2 - With BAM DMA and with pipe memory
+        3 - Without BAM DMA and without pipe memory
+  - qcom,pipe-attr-ee: BAM pipes are attributed to a specific EE, with
+    which we can know the pipes belong to apps side and can have the
+    error interrupts at the pipe level.
+  - clocks: This property shall provide a list of entries each of which
+    contains a phandle to clock controller device and a macro that is
+    the clock's name in hardware.These should be "clock_rpm" as clock
+    controller phandle and "clk_pnoc_sps_clk" as macro for "dfab_clk"
+    and "clock_gcc" as clock controller phandle and "clk_gcc_bam_dma_ahb_clk"
+    as macro for "dma_bam_pclk".
+  - clock-names: This property shall contain the clock input names used
+    by driver in same order as the clocks property.These should be "dfab_clk"
+    and "dma_bam_pclk".
+
+Example:
+
+	qcom,sps@f9980000 {
+		compatible = "qcom,msm_sps";
+		reg = <0xf9984000 0x15000>,
+		      <0xf9999000 0xb000>,
+		      <0xfe803000 0x4800>;
+		interrupts = <0 94 0>;
+		qcom,device-type = <2>;
+		qcom,pipe-attr-ee;
+		clocks = <&clock_rpm clk_pnoc_sps_clk>,
+			 <&clock_gcc clk_gcc_bam_dma_ahb_clk>;
+		clock-names = "dfab_clk", "dma_bam_pclk";
+	};
diff --git a/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
new file mode 100644
index 0000000..c1cba82
--- /dev/null
+++ b/Documentation/devicetree/bindings/drm/msm/mdss-dsi-panel.txt
@@ -0,0 +1,742 @@
+QTI mdss-dsi-panel
+
+mdss-dsi-panel is a dsi panel device which supports panels that
+are compatible with MIPI display serial interface specification.
+
+Required properties:
+- compatible:				This property applies to DSI V2 panels only.
+					This property should not be added for panels
+					that work based on version "V6.0"
+					DSI panels that are of different versions
+					are initialized by the drivers for dsi controller.
+					This property specifies the version
+					for DSI HW that this panel will work with
+					"qcom,dsi-panel-v2" = DSI V2.0
+- status:        			This property applies to DSI V2 panels only.
+					This property should not be added for panels
+					that work based on version "V6.0"
+					DSI panels that are of different versions
+					are initialized by the drivers for dsi controller.
+					A string that has to be set to "okay/ok"
+					to enable the panel driver. By default this property
+					will be set to "disable". Will be set to "ok/okay"
+					status for specific platforms.
+- qcom,mdss-dsi-panel-controller:	Specifies the phandle for the DSI controller that
+					this panel will be mapped to.
+- qcom,mdss-dsi-panel-width:		Specifies panel width in pixels.
+- qcom,mdss-dsi-panel-height:		Specifies panel height in pixels.
+- qcom,mdss-dsi-bpp:			Specifies the panel bits per pixel.
+					3  = for rgb111
+					8  = for rgb332
+					12 = for rgb444
+					16 = for rgb565
+					18 = for rgb666
+					24 = for rgb888
+- qcom,mdss-dsi-panel-destination:	A string that specifies the destination display for the panel.
+					"display_1" = DISPLAY_1
+					"display_2" = DISPLAY_2
+- qcom,mdss-dsi-panel-timings:		An array of length 12 that specifies the PHY
+					timing settings for the panel.
+- qcom,mdss-dsi-panel-timings-8996:		An array of length 40 char that specifies the 8996 PHY lane
+					timing settings for the panel.
+- qcom,mdss-dsi-on-command:		A byte stream formed by multiple dcs packets base on
+					qcom dsi controller protocol.
+					byte 0: dcs data type
+					byte 1: set to indicate this is an individual packet
+						 (no chain)
+					byte 2: virtual channel number
+					byte 3: expect ack from client (dcs read command)
+					byte 4: wait number of specified ms after dcs command
+						 transmitted
+					byte 5, 6: 16 bits length in network byte order
+					byte 7 and beyond: number byte of payload
+- qcom,mdss-dsi-off-command:		A byte stream formed by multiple dcs packets base on
+					qcom dsi controller protocol.
+					byte 0: dcs data type
+					byte 1: set to indicate this is an individual packet
+						 (no chain)
+					byte 2: virtual channel number
+					byte 3: expect ack from client (dcs read command)
+					byte 4: wait number of specified ms after dcs command
+						 transmitted
+					byte 5, 6: 16 bits length in network byte order
+					byte 7 and beyond: number byte of payload
+- qcom,mdss-dsi-post-panel-on-command:	same as "qcom,mdss-dsi-on-command" except commands are
+					sent after displaying an image.
+
+Note, if a short DCS packet(i.e packet with Byte 0:dcs data type as 05) mentioned in
+qcom,mdss-dsi-on-command/qcom,mdss-dsi-off-command stream fails to transmit,
+then 3 options can be tried.
+	1. Send the packet as a long packet instead
+				Byte 0: dcs data type = 05 (DCS short Packet)
+				Byte 0: dcs data type = 29 (DCS long Packet)
+	2. Send the packet in one burst by prepending with the next packet in packet stream
+				Byte 1 = 01 (indicates this is an individual packet)
+				Byte 1 = 00 (indicates this will be appended to the next
+					     individual packet in the packet stream)
+	3. Prepend a NULL packet to the short packet and send both in one burst instead of
+	   combining multiple short packets and sending them in one burst.
+
+Optional properties:
+- qcom,mdss-dsi-panel-name:		A string used as a descriptive name of the panel
+- qcom,cmd-sync-wait-broadcast:		Boolean used to broadcast dcs command to panels.
+- qcom,mdss-dsi-fbc-enable:		Boolean used to enable frame buffer compression mode.
+- qcom,mdss-dsi-fbc-slice-height:	Slice height(in lines) of compressed block.
+					Expressed as power of 2. To set as 128 lines,
+					this should be set to 7.
+- qcom,mdss-dsi-fbc-2d-pred-mode:	Boolean to enable 2D map prediction.
+- qcom,mdss-dsi-fbc-ver2-mode:		Boolean to enable FBC 2.0 that supports 1/3
+					compression.
+- qcom,mdss-dsi-fbc-bpp:		Compressed bpp supported by the panel.
+					Specified color order is used as default value.
+- qcom,mdss-dsi-fbc-packing:		Component packing.
+					0 = default value.
+- qcom,mdss-dsi-fbc-quant-error:	Boolean used to enable quantization error calculation.
+- qcom,mdss-dsi-fbc-bias:		Bias for CD.
+					0 = default value.
+- qcom,mdss-dsi-fbc-pat-mode:		Boolean used to enable PAT mode.
+- qcom,mdss-dsi-fbc-vlc-mode:		Boolean used to enable VLC mode.
+- qcom,mdss-dsi-fbc-bflc-mode:		Boolean used to enable BFLC mode.
+- qcom,mdss-dsi-fbc-h-line-budget:	Per line extra budget.
+					0 = default value.
+- qcom,mdss-dsi-fbc-budget-ctrl:		Extra budget level.
+					0 = default value.
+- qcom,mdss-dsi-fbc-block-budget:		Per block budget.
+					0 = default value.
+- qcom,mdss-dsi-fbc-lossless-threshold: Lossless mode threshold.
+					0 = default value.
+- qcom,mdss-dsi-fbc-lossy-threshold:	Lossy mode threshold.
+					0 = default value.
+- qcom,mdss-dsi-fbc-rgb-threshold:	Lossy RGB threshold.
+					0 = default value.
+- qcom,mdss-dsi-fbc-lossy-mode-idx:	Lossy mode index value.
+					0 = default value.
+- qcom,mdss-dsi-fbc-max-pred-err:	Max quantization prediction error.
+					0 = default value
+- qcom,mdss-dsi-h-back-porch:		Horizontal back porch value in pixel.
+					6 = default value.
+- qcom,mdss-dsi-h-front-porch:		Horizontal front porch value in pixel.
+					6 = default value.
+- qcom,mdss-dsi-h-pulse-width:		Horizontal pulse width.
+					2 = default value.
+- qcom,mdss-dsi-h-sync-skew:		Horizontal sync skew value.
+					0 = default value.
+- qcom,mdss-dsi-v-back-porch:		Vertical back porch value in pixel.
+					6 = default value.
+- qcom,mdss-dsi-v-front-porch:		Vertical front porch value in pixel.
+					6 = default value.
+- qcom,mdss-dsi-v-pulse-width:		Vertical pulse width.
+					2 = default value.
+- qcom,mdss-dsi-h-left-border:		Horizontal left border in pixel.
+					0 = default value
+- qcom,mdss-dsi-h-right-border:		Horizontal right border in pixel.
+					0 = default value
+- qcom,mdss-dsi-v-top-border:		Vertical top border in pixel.
+					0 = default value
+- qcom,mdss-dsi-v-bottom-border:	Vertical bottom border in pixel.
+					0 = default value
+- qcom,mdss-dsi-underflow-color:	Specifies the controller settings for the
+					panel under flow color.
+					0xff = default value.
+- qcom,mdss-dsi-border-color:		Defines the border color value if border is present.
+					0 = default value.
+- qcom,mdss-dsi-pan-enable-dynamic-fps:	Boolean used to enable change in frame rate dynamically.
+- qcom,mdss-dsi-pan-fps-update:		A string that specifies when to change the frame rate.
+					"dfps_suspend_resume_mode"= FPS change request is
+					implemented during suspend/resume.
+					"dfps_immediate_clk_mode" = FPS change request is
+					implemented immediately using DSI clocks.
+					"dfps_immediate_porch_mode_hfp" = FPS change request is
+					implemented immediately by changing panel horizontal
+					front porch values.
+					"dfps_immediate_porch_mode_vfp" = FPS change request is
+					implemented immediately by changing panel vertical
+					front porch values.
+- qcom,min-refresh-rate:		Minimum refresh rate supported by the panel.
+- qcom,max-refresh-rate:		Maximum refresh rate supported by the panel. If max refresh
+					rate is not specified, then the frame rate of the panel in
+					qcom,mdss-dsi-panel-framerate is used.
+- qcom,mdss-dsi-bl-pmic-control-type:	A string that specifies the implementation of backlight
+					control for this panel.
+					"bl_ctrl_pwm" = Backlight controlled by PWM gpio.
+					"bl_ctrl_wled" = Backlight controlled by WLED.
+					"bl_ctrl_dcs" = Backlight controlled by DCS commands.
+					other: Unknown backlight control. (default)
+- qcom,mdss-dsi-bl-pwm-pmi:		Boolean to indicate that PWM control is through second pmic chip.
+- qcom,mdss-dsi-bl-pmic-bank-select:	LPG channel for backlight.
+					Required if backlight pmic control type is PWM
+- qcom,mdss-dsi-bl-pmic-pwm-frequency:	PWM period in microseconds.
+					Required if backlight pmic control type is PWM
+- qcom,mdss-dsi-pwm-gpio:		PMIC gpio binding to backlight.
+					Required if backlight pmic control type is PWM
+- qcom,mdss-dsi-bl-min-level:		Specifies the min backlight level supported by the panel.
+					0 = default value.
+- qcom,mdss-dsi-bl-max-level:		Specifies the max backlight level supported by the panel.
+					255 = default value.
+- qcom,mdss-brightness-max-level:	Specifies the max brightness level supported.
+					255 = default value.
+- qcom,mdss-dsi-interleave-mode:	Specifies interleave mode.
+					0 = default value.
+- qcom,mdss-dsi-panel-type:		Specifies the panel operating mode.
+					"dsi_video_mode" = enable video mode (default).
+					"dsi_cmd_mode" = enable command mode.
+- qcom,5v-boost-gpio:			Specifies the panel gpio for display 5v boost.
+- qcom,mdss-dsi-te-check-enable:	Boolean to enable Tear Check configuration.
+- qcom,mdss-dsi-te-using-te-pin:	Boolean to specify whether using hardware vsync.
+- qcom,mdss-dsi-te-pin-select:		Specifies TE operating mode.
+					0 = TE through embedded dcs command
+					1 = TE through TE gpio pin. (default)
+- qcom,mdss-dsi-te-dcs-command:		Inserts the dcs command.
+					1 = default value.
+- qcom,mdss-dsi-wr-mem-start:		DCS command for write_memory_start.
+					0x2c = default value.
+- qcom,mdss-dsi-wr-mem-continue:	DCS command for write_memory_continue.
+					0x3c = default value.
+- qcom,mdss-dsi-h-sync-pulse:		Specifies the pulse mode option for the panel.
+					0 = Don't send hsa/he following vs/ve packet(default)
+					1 = Send hsa/he following vs/ve packet
+- qcom,mdss-dsi-hfp-power-mode:		Boolean to determine DSI lane state during
+					horizontal front porch (HFP) blanking period.
+- qcom,mdss-dsi-hbp-power-mode:		Boolean to determine DSI lane state during
+					horizontal back porch (HBP) blanking period.
+- qcom,mdss-dsi-hsa-power-mode:		Boolean to determine DSI lane state during
+					horizontal sync active (HSA) mode.
+- qcom,mdss-dsi-last-line-interleave	Boolean to determine if last line
+					interleave flag needs to be enabled.
+- qcom,mdss-dsi-bllp-eof-power-mode:	Boolean to determine DSI lane state during
+					blanking low power period (BLLP) EOF mode.
+- qcom,mdss-dsi-bllp-power-mode:	Boolean to determine DSI lane state during
+					blanking low power period (BLLP) mode.
+- qcom,mdss-dsi-traffic-mode:		Specifies the panel traffic mode.
+					"non_burst_sync_pulse" = non burst with sync pulses (default).
+					"non_burst_sync_event" = non burst with sync start event.
+					"burst_mode" = burst mode.
+- qcom,mdss-dsi-pixel-packing:		Specifies if pixel packing is used (in case of RGB666).
+					"tight" = Tight packing (default value).
+					"loose" = Loose packing.
+- qcom,mdss-dsi-virtual-channel-id:	Specifies the virtual channel identefier.
+					0 = default value.
+- qcom,mdss-dsi-color-order:		Specifies the R, G and B channel ordering.
+					"rgb_swap_rgb" = DSI_RGB_SWAP_RGB (default value)
+					"rgb_swap_rbg" = DSI_RGB_SWAP_RBG
+					"rgb_swap_brg" = DSI_RGB_SWAP_BRG
+					"rgb_swap_grb" = DSI_RGB_SWAP_GRB
+					"rgb_swap_gbr" = DSI_RGB_SWAP_GBR
+- qcom,mdss-dsi-lane-0-state:		Boolean that specifies whether data lane 0 is enabled.
+- qcom,mdss-dsi-lane-1-state:		Boolean that specifies whether data lane 1 is enabled.
+- qcom,mdss-dsi-lane-2-state:		Boolean that specifies whether data lane 2 is enabled.
+- qcom,mdss-dsi-lane-3-state:		Boolean that specifies whether data lane 3 is enabled.
+- qcom,mdss-dsi-t-clk-post:		Specifies the byte clock cycles after mode switch.
+					0x03 = default value.
+- qcom,mdss-dsi-t-clk-pre:		Specifies the byte clock cycles before mode switch.
+					0x24 = default value.
+- qcom,mdss-dsi-stream:			Specifies the packet stream to be used.
+					0 = stream 0 (default)
+					1 = stream 1
+- qcom,mdss-dsi-mdp-trigger:		Specifies the trigger mechanism to be used for MDP path.
+					"none" = no trigger
+					"trigger_te" = Tear check signal line used for trigger
+					"trigger_sw" = Triggered by software (default)
+					"trigger_sw_te" = Software trigger and TE
+- qcom,mdss-dsi-dma-trigger:		Specifies the trigger mechanism to be used for DMA path.
+					"none" = no trigger
+					"trigger_te" = Tear check signal line used for trigger
+					"trigger_sw" = Triggered by software (default)
+					"trigger_sw_seof" = Software trigger and start/end of frame trigger.
+					"trigger_sw_te" = Software trigger and TE
+- qcom,mdss-dsi-panel-framerate:	Specifies the frame rate for the panel.
+					60 = 60 frames per second (default)
+- qcom,mdss-dsi-panel-clockrate:	A 64 bit value specifies the panel clock speed in Hz.
+					0 = default value.
+- qcom,mdss-mdp-transfer-time-us:	Specifies the dsi transfer time for command mode
+					panels in microseconds. Driver uses this number to adjust
+					the clock rate according to the expected transfer time.
+					Increasing this value would slow down the mdp processing
+					and can result in slower performance.
+					Decreasing this value can speed up the mdp processing,
+					but this can also impact power consumption.
+					As a rule this time should not be higher than the time
+					that would be expected with the processing at the
+					dsi link rate since anyways this would be the maximum
+					transfer time that could be achieved.
+					If ping pong split enabled, this time should not be higher
+					than two times the dsi link rate time.
+					14000 = default value.
+- qcom,mdss-dsi-on-command-state:	String that specifies the ctrl state for sending ON commands.
+					"dsi_lp_mode" = DSI low power mode (default)
+					"dsi_hs_mode" = DSI high speed mode
+- qcom,mdss-dsi-off-command-state:	String that specifies the ctrl state for sending OFF commands.
+					"dsi_lp_mode" = DSI low power mode (default)
+					"dsi_hs_mode" = DSI high speed mode
+- qcom,mdss-dsi-post-mode-switch-on-command-state:	String that specifies the ctrl state for sending ON commands post mode switch.
+					"dsi_lp_mode" = DSI low power mode (default)
+					"dsi_hs_mode" = DSI high speed mode
+- qcom,mdss-pan-physical-width-dimension:	Specifies panel physical width in mm which corresponds
+					to the physical width in the framebuffer information.
+- qcom,mdss-pan-physical-height-dimension:	Specifies panel physical height in mm which corresponds
+					to the physical height in the framebuffer information.
+- qcom,mdss-dsi-mode-sel-gpio-state:	String that specifies the lcd mode for panel
+					(such as single-port/dual-port), if qcom,panel-mode-gpio
+					binding is defined in dsi controller.
+					"dual_port" = Set GPIO to LOW
+					"single_port" = Set GPIO to HIGH
+					"high" = Set GPIO to HIGH
+					"low" = Set GPIO to LOW
+					The default value is "dual_port".
+- qcom,mdss-tear-check-disable:		Boolean to disable mdp tear check. Tear check is enabled by default to avoid
+					tearing. Other tear-check properties are ignored if this property is present.
+					The below tear check configuration properties can be individually tuned if
+					tear check is enabled.
+- qcom,mdss-tear-check-sync-cfg-height: Specifies the vertical total number of lines.
+					The default value is 0xfff0.
+- qcom,mdss-tear-check-sync-init-val:	Specifies the init value at which the read pointer gets loaded
+					at vsync edge. The reader pointer refers to the line number of
+					panel buffer that is currently being updated.
+					The default value is panel height.
+- qcom,mdss-tear-check-sync-threshold-start:
+					Allows the first ROI line write to an panel when read pointer is
+					between the range of ROI start line and ROI start line plus this
+					setting.
+					The default value is 4.
+- qcom,mdss-tear-check-sync-threshold-continue:
+					The minimum number of lines the write pointer needs to be
+					above the read pointer so that it is safe to write to the panel.
+					(This check is not done for the first ROI line write of an update)
+					The default value is 4.
+- qcom,mdss-tear-check-start-pos:	Specify the y position from which the start_threshold value is
+					added and write is kicked off if the read pointer falls within that
+					region.
+					The default value is panel height.
+- qcom,mdss-tear-check-rd-ptr-trigger-intr:
+					Specify the read pointer value at which an interrupt has to be
+					generated.
+					The default value is panel height + 1.
+- qcom,mdss-tear-check-frame-rate:	Specify the value to be a real frame rate(fps) x 100 factor to tune the
+					timing of TE simulation with more precision.
+					The default value is 6000 with 60 fps.
+- qcom,mdss-dsi-reset-sequence:		An array that lists the
+					sequence of reset gpio values and sleeps
+					Each command will have the format defined
+					as below:
+					--> Reset GPIO value
+					--> Sleep value (in ms)
+- qcom,partial-update-enabled:		Boolean used to enable partial
+					panel update for command mode panels.
+- qcom,mdss-dsi-horizontal-line-idle:	List of width ranges (EC - SC) in pixels indicating
+					additional idle time in dsi clock cycles that is needed
+					to compensate for smaller line width.
+- qcom,partial-update-roi-merge:	Boolean indicates roi combination is need
+					and function has been provided for dcs
+					2A/2B command.
+- qcom,dcs-cmd-by-left:			Boolean to indicate that dcs command are sent
+					through the left DSI controller only in a dual-dsi configuration
+- qcom,mdss-dsi-lp11-init:		Boolean used to enable the DSI clocks and data lanes (low power 11)
+					before issuing hardware reset line.
+- qcom,mdss-dsi-init-delay-us:		Delay in microseconds(us) before performing any DSI activity in lp11
+					mode. This master delay (t_init_delay as per DSI spec) should be sum
+					of DSI internal delay to reach fuctional after power up and minimum
+					delay required by panel to reach functional.
+- qcom,mdss-dsi-rx-eot-ignore:		Boolean used to enable ignoring end of transmission packets.
+- qcom,mdss-dsi-tx-eot-append:		Boolean used to enable appending end of transmission packets.
+- qcom,ulps-enabled:			Boolean to enable support for Ultra Low Power State (ULPS) mode.
+- qcom,suspend-ulps-enabled:		Boolean to enable support for ULPS mode for panels during suspend state.
+- qcom,panel-roi-alignment:		Specifies the panel ROI alignment restrictions on its
+					left, top, width, height alignments and minimum width and
+					height values
+- qcom,esd-check-enabled:		Boolean used to enable ESD recovery feature.
+- qcom,mdss-dsi-panel-status-command:	A byte stream formed by multiple dcs packets based on
+					qcom dsi controller protocol, to read the panel status.
+					This value is used to kick in the ESD recovery.
+					byte 0: dcs data type
+					byte 1: set to indicate this is an individual packet
+						 (no chain)
+					byte 2: virtual channel number
+					byte 3: expect ack from client (dcs read command)
+					byte 4: wait number of specified ms after dcs command
+						 transmitted
+					byte 5, 6: 16 bits length in network byte order
+					byte 7 and beyond: number byte of payload
+- qcom,mdss-dsi-panel-status-command-mode:
+					String that specifies the ctrl state for reading the panel status.
+					"dsi_lp_mode" = DSI low power mode
+					"dsi_hs_mode" = DSI high speed mode
+- qcom,mdss-dsi-panel-status-check-mode:Specifies the panel status check method for ESD recovery.
+					"bta_check" = Uses BTA to check the panel status
+					"reg_read" = Reads panel status register to check the panel status
+					"reg_read_nt35596" = Reads panel status register to check the panel
+							     status for NT35596 panel.
+					"te_signal_check" = Uses TE signal behaviour to check the panel status
+- qcom,mdss-dsi-panel-status-read-length: Integer array that specify the expected read-back length of values
+					  for each of panel registers. Each length is corresponding to number of
+					  returned parameters of register introduced in specification.
+- qcom,mdss-dsi-panel-status-valid-params: Integer array that specify the valid returned values which need to check
+					   for each of register.
+					   Some panel need only check the first few values returned from panel.
+					   So: if this property is the same to qcom,mdss-dsi-panel-status-read-length,
+					   then just ignore this one.
+- qcom,mdss-dsi-panel-status-value:	Multiple integer arrays, each specifies the values of the panel status register
+					which is used to check the panel status. The size of each array is the sum of
+					length specified in qcom,mdss-dsi-panel-status-read-length, and must be equal.
+					This can cover that Some panel may return several alternative values.
+- qcom,mdss-dsi-panel-max-error-count:  Integer value that specifies the maximum number of errors from register
+					read that can be ignored before treating that the panel has gone bad.
+- qcom,dynamic-mode-switch-enabled:		Boolean used to mention whether panel supports
+					dynamic switching from video mode to command mode
+					and vice versa.
+- qcom,dynamic-mode-switch-type:		A string specifies how to perform dynamic mode switch.
+						If qcom,dynamic-mode-switch-enabled is set and no string specified, default value is
+						dynamic-switch-suspend-resume.
+					"dynamic-switch-suspend-resume"= Switch using suspend/resume. Panel will
+						go blank during transition.
+					"dynamic-switch-immediate"= Switch on next frame update. Panel will
+						not go blank for this transition.
+					"dynamic-resolution-switch-immediate"= Switch the panel resolution. Panel will
+						not go blank for this transition.
+- qcom,mdss-dsi-post-mode-switch-on-command:		Multiple dcs packets used for turning on DSI panel
+					after panel has switch modes.
+					Refer to "qcom,mdss-dsi-on-command" section for adding commands.
+- qcom,video-to-cmd-mode-switch-commands:	List of commands that need to be sent
+					to panel in order to switch from video mode to command mode dynamically.
+					Refer to "qcom,mdss-dsi-on-command" section for adding commands.
+- qcom,cmd-to-video-mode-switch-commands:	List of commands that need to be sent
+					to panel in order to switch from command mode to video mode dynamically.
+					Refer to "qcom,mdss-dsi-on-command" section for adding commands.
+- qcom,send-pps-before-switch:		Boolean propety to indicate when PPS commands should be sent,
+					either before or after switch commands during dynamic resolution
+					switch in DSC panels. If the property is not present, the default
+					behavior is to send PPS commands after the switch commands.
+- qcom,mdss-dsi-panel-orientation:	String used to indicate orientation of panel
+					"180" = panel is flipped in both horizontal and vertical directions
+					"hflip" = panel is flipped in horizontal direction
+					"vflip" = panel is flipped in vertical direction
+- qcom,panel-ack-disabled: A boolean property to indicate, whether we need to wait for any ACK from the panel
+			   for any commands that we send.
+- qcom,mdss-dsi-force-clock-lane-hs:	Boolean to force dsi clock lanes to HS mode always.
+
+- qcom,compression-mode:		Select compression mode for panel.
+					"fbc" - frame buffer compression
+					"dsc" - display stream compression.
+					If "dsc" compression is used then config subnodes needs to be defined.
+- qcom,panel-supply-entries:		A node that lists the elements of the supply used to
+					power the DSI panel. There can be more than one instance
+					of this binding, in which case the entry would be appended
+					with the supply entry index. For a detailed description of
+					fields in the supply entry, refer to the qcom,ctrl-supply-entries
+					binding above.
+- qcom,config-select:			Optional property to select default configuration.
+
+[[Optional config sub-nodes]]		These subnodes provide different configurations for a given same panel.
+					Default configuration can be chosen by specifying phandle of the
+					selected subnode in the qcom,config-select.
+Required properties for sub-nodes:	None
+Optional properites:
+- qcom,lm-split:			An array of two values indicating MDP should use two layer
+					mixers to reduce power.
+					Ex: Normally 1080x1920 display uses single DSI and thus one layer
+					    mixer. But if we use two layer mixers then mux the output of
+					    those two mixers into single stream and route it to single DSI
+					    then we can lower the clock requirements of MDP. To use this
+					    configuration we need two fill this array with <540 540>.
+					Both values doesn't have to be same, but recommended, however sum of
+					both values has to be equal to the panel-width.
+					By default two mixer streams are merged using 2D mux, however if
+					2 DSC encoders are used then merge is performed within compression
+					engine.
+- qcom,split-mode:			String property indicating which split mode MDP should use. Valid
+					entries are "pingpong-split" and "dualctl-split".
+					This property is mutually exclusive with qcom,lm-split.
+- qcom,mdss-dsc-version:		An 8 bit value indicates the DSC version supported by panel. Bits[0.3]
+					provides information about minor version while Bits[4.7] provides
+					major version information. It supports only DSC rev 1(Major).1(Minor)
+					right now.
+- qcom,mdss-dsc-scr-version:		Each DSC version can have multiple SCR. This 8 bit value indicates
+					current SCR revision information supported by panel.
+- qcom,mdss-dsc-encoders:		An integer value indicating how many DSC encoders should be used
+					to drive data stream to DSI.
+					Default value is 1 and max value is 2.
+					2 encoder should be used only if qcom,mdss-lm-split or
+					qcom,split-mode with pingpong-split is used.
+- qcom,mdss-dsc-slice-height:		An integer value indicates the dsc slice height.
+- qcom,mdss-dsc-slice-width:		An integer value indicates the dsc slice width.
+					Multiple of slice width should be equal to panel-width.
+					Maximum 2 slices per DSC encoder can be used so if 2 DSC encoders
+					are used then minimum slice width is equal to panel-width/4.
+- qcom,mdss-dsc-slice-per-pkt:		An integer value indicates the slice per dsi packet.
+- qcom,mdss-dsc-bit-per-component: 	An integer value indicates the bits per component before compression.
+- qcom,mdss-dsc-bit-per-pixel:		An integer value indicates the bits per pixel after compression.
+- qcom,mdss-dsc-block-prediction-enable: A boolean value to enable/disable the block prediction at decoder.
+- qcom,mdss-dsc-config-by-manufacture-cmd: A boolean to indicates panel use manufacture command to setup pps
+					instead of standard dcs type 0x0A.
+- qcom,dba-panel:	Indicates whether the current panel is used as a display bridge
+					to a non-DSI interface.
+- qcom,bridge-name:			A string to indicate the name of the bridge chip connected to DSI. qcom,bridge-name
+					is required if qcom,dba-panel is defined for the panel.
+- qcom,adjust-timer-wakeup-ms:		An integer value to indicate the timer delay(in ms) to accommodate
+					s/w delay while configuring the event timer wakeup logic.
+
+- qcom,mdss-dsi-display-timings:	Parent node that lists the different resolutions that the panel supports.
+					Each child represents timings settings for a specific resolution.
+- qcom,mdss-dsi-post-init-delay:        Specifies required number of frames to wait so that panel can be functional
+					to show proper display.
+
+Additional properties added to the second level nodes that represent timings properties:
+- qcom,mdss-dsi-timing-default:		Property that specifies the current child as the default
+					timing configuration that will be used.
+- qcom,mdss-dsi-timing-switch-command:	List of commands that need to be sent
+					to panel when the resolution/timing switch happens dynamically.
+					Refer to "qcom,mdss-dsi-on-command" section for adding commands.
+- qcom,mdss-dsi-timing-switch-command-state:	String that specifies the ctrl state for sending resolution switch
+					commands.
+					"dsi_lp_mode" = DSI low power mode (default)
+					"dsi_hs_mode" = DSI high speed mode
+
+Note, if a given optional qcom,* binding is not present, then the driver will configure
+the default values specified.
+
+Example:
+&mdss_mdp {
+	dsi_sim_vid: qcom,mdss_dsi_sim_video {
+		qcom,mdss-dsi-panel-name = "simulator video mode dsi panel";
+		qcom,mdss-dsi-panel-controller = <&mdss_dsi0>;
+		qcom,mdss-dsi-panel-height = <1280>;
+		qcom,mdss-dsi-panel-width = <720>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-pixel-packing = <0>;
+		qcom,mdss-dsi-panel-destination = "display_1";
+		qcom,cmd-sync-wait-broadcast;
+		qcom,mdss-dsi-fbc-enable;
+		qcom,mdss-dsi-fbc-slice-height = <5>;
+		qcom,mdss-dsi-fbc-2d-pred-mode;
+		qcom,mdss-dsi-fbc-ver2-mode;
+		qcom,mdss-dsi-fbc-bpp = <0>;
+		qcom,mdss-dsi-fbc-packing = <0>;
+		qcom,mdss-dsi-fbc-quant-error;
+		qcom,mdss-dsi-fbc-bias = <0>;
+		qcom,mdss-dsi-fbc-pat-mode;
+		qcom,mdss-dsi-fbc-vlc-mode;
+		qcom,mdss-dsi-fbc-bflc-mode;
+		qcom,mdss-dsi-fbc-h-line-budget = <0>;
+		qcom,mdss-dsi-fbc-budget-ctrl = <0>;
+		qcom,mdss-dsi-fbc-block-budget = <0>;
+		qcom,mdss-dsi-fbc-lossless-threshold = <0>;
+		qcom,mdss-dsi-fbc-lossy-threshold = <0>;
+		qcom,mdss-dsi-fbc-rgb-threshold = <0>;
+		qcom,mdss-dsi-fbc-lossy-mode-idx = <0>;
+		qcom,mdss-dsi-fbc-max-pred-err = <2>;
+		qcom,mdss-dsi-h-front-porch = <140>;
+		qcom,mdss-dsi-h-back-porch = <164>;
+		qcom,mdss-dsi-h-pulse-width = <8>;
+		qcom,mdss-dsi-h-sync-skew = <0>;
+		qcom,mdss-dsi-v-back-porch = <6>;
+		qcom,mdss-dsi-v-front-porch = <1>;
+		qcom,mdss-dsi-v-pulse-width = <1>;
+		qcom,mdss-dsi-h-left-border = <0>;
+		qcom,mdss-dsi-h-right-border = <0>;
+		qcom,mdss-dsi-v-top-border = <0>;
+		qcom,mdss-dsi-v-bottom-border = <0>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-bl-min-level = <1>;
+		qcom,mdss-dsi-bl-max-level = < 15>;
+		qcom,mdss-brightness-max-level = <255>;
+		qcom,mdss-dsi-interleave-mode = <0>;
+		qcom,mdss-dsi-panel-type = "dsi_video_mode";
+		qcom,mdss-dsi-te-check-enable;
+		qcom,mdss-dsi-te-using-te-pin;
+		qcom,mdss-dsi-te-dcs-command = <1>;
+		qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+		qcom,mdss-dsi-wr-mem-start = <0x2c>;
+		qcom,mdss-dsi-te-pin-select = <1>;
+		qcom,mdss-dsi-h-sync-pulse = <1>;
+		qcom,mdss-dsi-hfp-power-mode;
+		qcom,mdss-dsi-hbp-power-mode;
+		qcom,mdss-dsi-hsa-power-mode;
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-last-line-interleave;
+		qcom,mdss-dsi-traffic-mode = <0>;
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-color-order = <0>;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-t-clk-post = <0x20>;
+		qcom,mdss-dsi-t-clk-pre = <0x2c>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-mdp-trigger = <0>;
+		qcom,mdss-dsi-dma-trigger = <0>;
+		qcom,mdss-dsi-panel-framerate = <60>;
+		qcom,mdss-dsi-panel-clockrate = <424000000>;
+		qcom,mdss-mdp-transfer-time-us = <12500>;
+		qcom,mdss-dsi-panel-timings = [7d 25 1d 00 37 33
+					22 27 1e 03 04 00];
+                qcom,mdss-dsi-panel-timings-8996 = [23 20 06 09 05 03 04 a0
+                                23 20 06 09 05 03 04 a0
+                                23 20 06 09 05 03 04 a0
+                                23 20 06 09 05 03 04 a0
+                                23 2e 06 08 05 03 04 a0];
+		qcom,mdss-dsi-on-command = [32 01 00 00 00 00 02 00 00
+					29 01 00 00 10 00 02 FF 99];
+		qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+		qcom,mdss-dsi-off-command = [22 01 00 00 00 00 00];
+		qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+		qcom,mdss-dsi-bl-pmic-control-type = "bl_ctrl_wled";
+		qcom,mdss-dsi-pan-enable-dynamic-fps;
+		qcom,mdss-dsi-pan-fps-update = "dfps_suspend_resume_mode";
+		qcom,min-refresh-rate = <30>;
+		qcom,max-refresh-rate = <60>;
+		qcom,mdss-dsi-bl-pmic-bank-select = <0>;
+		qcom,mdss-dsi-bl-pmic-pwm-frequency = <0>;
+		qcom,mdss-dsi-pwm-gpio = <&pm8941_mpps 5 0>;
+		qcom,5v-boost-gpio = <&pm8994_gpios 14 0>;
+		qcom,mdss-pan-physical-width-dimension = <60>;
+		qcom,mdss-pan-physical-height-dimension = <140>;
+		qcom,mdss-dsi-mode-sel-gpio-state = "dsc_mode";
+		qcom,mdss-tear-check-sync-cfg-height = <0xfff0>;
+		qcom,mdss-tear-check-sync-init-val = <1280>;
+		qcom,mdss-tear-check-sync-threshold-start = <4>;
+		qcom,mdss-tear-check-sync-threshold-continue = <4>;
+		qcom,mdss-tear-check-start-pos = <1280>;
+		qcom,mdss-tear-check-rd-ptr-trigger-intr = <1281>;
+		qcom,mdss-tear-check-frame-rate = <6000>;
+		qcom,mdss-dsi-reset-sequence = <1 2>, <0 10>, <1 10>;
+		qcom,partial-update-enabled;
+		qcom,dcs-cmd-by-left;
+		qcom,mdss-dsi-lp11-init;
+		qcom,mdss-dsi-init-delay-us = <100>;
+		mdss-dsi-rx-eot-ignore;
+		mdss-dsi-tx-eot-append;
+		qcom,ulps-enabled;
+		qcom,suspend-ulps-enabled;
+		qcom,panel-roi-alignment = <4 4 2 2 20 20>;
+		qcom,esd-check-enabled;
+		qcom,mdss-dsi-panel-status-command = [06 01 00 01 05 00 02 0A 08];
+		qcom,mdss-dsi-panel-status-command-state = "dsi_lp_mode";
+		qcom,mdss-dsi-panel-status-check-mode = "reg_read";
+		qcom,mdss-dsi-panel-status-read-length = <8>;
+		qcom,mdss-dsi-panel-max-error-count = <3>;
+		qcom,mdss-dsi-panel-status-value = <0x1c 0x00 0x05 0x02 0x40 0x84 0x06 0x01>;
+		qcom,dynamic-mode-switch-enabled;
+		qcom,dynamic-mode-switch-type = "dynamic-switch-immediate";
+		qcom,mdss-dsi-post-mode-switch-on-command = [32 01 00 00 00 00 02 00 00
+					29 01 00 00 10 00 02 B0 03];
+		qcom,video-to-cmd-mode-switch-commands = [15 01 00 00 00 00 02 C2 0B
+						15 01 00 00 00 00 02 C2 08];
+		qcom,cmd-to-video-mode-switch-commands = [15 01 00 00 00 00 02 C2 03];
+		qcom,send-pps-before-switch;
+		qcom,panel-ack-disabled;
+		qcom,mdss-dsi-horizontal-line-idle = <0 40 256>,
+						<40 120 128>,
+						<128 240 64>;
+		qcom,mdss-dsi-panel-orientation = "180"
+		qcom,mdss-dsi-force-clock-lane-hs;
+		qcom,compression-mode = "dsc";
+		qcom,adjust-timer-wakeup-ms = <1>;
+		qcom,mdss-dsi-display-timings {
+			wqhd {
+				qcom,mdss-dsi-timing-default;
+				qcom,mdss-dsi-panel-width = <720>;
+				qcom,mdss-dsi-panel-height = <2560>;
+				qcom,mdss-dsi-h-front-porch = <20>;
+				qcom,mdss-dsi-h-back-porch = <8>;
+				qcom,mdss-dsi-h-pulse-width = <8>;
+				qcom,mdss-dsi-h-sync-skew = <0>;
+				qcom,mdss-dsi-v-back-porch = <4>;
+				qcom,mdss-dsi-v-front-porch = <728>;
+				qcom,mdss-dsi-v-pulse-width = <4>;
+				qcom,mdss-dsi-panel-framerate = <60>;
+				qcom,mdss-dsi-panel-timings = [E6 38 26 00 68 6E 2A 3C 2C 03 04 00];
+				qcom,mdss-dsi-t-clk-post = <0x02>;
+				qcom,mdss-dsi-t-clk-pre = <0x2a>;
+				qcom,mdss-dsi-on-command = [05 01 00 00 a0 00 02 11 00
+					05 01 00 00 02 00 02 29 00];
+				qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+				qcom,mdss-dsi-timing-switch-command = [
+					29 00 00 00 00 00 02 B0 04
+					29 00 00 00 00 00 02 F1 00];
+				qcom,mdss-dsi-timing-switch-command-state = "dsi_lp_mode";
+
+				qcom,config-select = <&dsi_sim_vid_config0>;
+				dsi_sim_vid_config0: config0 {
+					qcom,lm-split = <360 360>;
+					qcom,mdss-dsc-encoders = <2>;
+					qcom,mdss-dsc-slice-height = <16>;
+					qcom,mdss-dsc-slice-width = <360>;
+					qcom,mdss-dsc-slice-per-pkt = <2>;
+					qcom,mdss-dsc-bit-per-component = <8>;
+					qcom,mdss-dsc-bit-per-pixel = <8>;
+					qcom,mdss-dsc-block-prediction-enable;
+					qcom,mdss-dsc-config-by-manufacture-cmd;
+				};
+			};
+		};
+		qcom,panel-supply-entries {
+			#address-cells = <1>;
+			#size-cells = <0>;
+
+			qcom,panel-supply-entry@0 {
+				reg = <0>;
+				qcom,supply-name = "vdd";
+				qcom,supply-min-voltage = <2800000>;
+				qcom,supply-max-voltage = <2800000>;
+				qcom,supply-enable-load = <100000>;
+				qcom,supply-disable-load = <100>;
+				qcom,supply-pre-on-sleep = <0>;
+				qcom,supply-post-on-sleep = <0>;
+				qcom,supply-pre-off-sleep = <0>;
+				qcom,supply-post-off-sleep = <0>;
+			};
+
+			qcom,panel-supply-entry@1 {
+				reg = <1>;
+				qcom,supply-name = "vddio";
+				qcom,supply-min-voltage = <1800000>;
+				qcom,supply-max-voltage = <1800000>;
+				qcom,supply-enable-load = <100000>;
+				qcom,supply-disable-load = <100>;
+				qcom,supply-pre-on-sleep = <0>;
+				qcom,supply-post-on-sleep = <0>;
+				qcom,supply-pre-off-sleep = <0>;
+				qcom,supply-post-off-sleep = <0>;
+			};
+		};
+
+		qcom,config-select = <&dsi_sim_vid_config0>;
+		qcom,dba-panel;
+		qcom,bridge-name = "adv7533";
+		qcom,mdss-dsc-version = <0x11>;
+		qcom,mdss-dsc-scr-version = <0x1>;
+
+		dsi_sim_vid_config0: config0 {
+			qcom,lm-split = <360 360>;
+			qcom,mdss-dsc-encoders = <2>;
+			qcom,mdss-dsc-slice-height = <16>;
+			qcom,mdss-dsc-slice-width = <360>;
+			qcom,mdss-dsc-slice-per-pkt = <2>;
+			qcom,mdss-dsc-bit-per-component = <8>;
+			qcom,mdss-dsc-bit-per-pixel = <8>;
+			qcom,mdss-dsc-block-prediction-enable;
+			qcom,mdss-dsc-config-by-manufacture-cmd;
+		};
+
+		dsi_sim_vid_config1: config1 {
+			qcom,mdss-dsc-encoders = <1>;
+			qcom,mdss-dsc-slice-height = <16>;
+			qcom,mdss-dsc-slice-width = <360>;
+			qcom,mdss-dsc-slice-per-pkt = <2>;
+			qcom,mdss-dsc-bit-per-component = <8>;
+			qcom,mdss-dsc-bit-per-pixel = <8>;
+			qcom,mdss-dsc-block-prediction-enable;
+			qcom,mdss-dsc-config-by-manufacture-cmd;
+		};
+
+		dsi_sim_vid_config2: config2 {
+			qcom,split-mode = "dualctl-split";
+		};
+
+		dsi_sim_vid_config3: config3 {
+			qcom,split-mode = "pingpong-split";
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/gpio/qpnp-pin.txt b/Documentation/devicetree/bindings/gpio/qpnp-pin.txt
new file mode 100644
index 0000000..0f61ae7
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/qpnp-pin.txt
@@ -0,0 +1,220 @@
+* msm-qpnp-pin
+
+msm-qpnp-pin is a GPIO chip driver for the MSM SPMI implementation.
+It creates a platform_device for every block of device_nodes.
+These device_nodes contained within specify the PMIC pin number associated
+with each gpio chip. The driver will map these to Linux GPIO numbers.
+
+[PMIC GPIO Device Declarations]
+
+-Root Node-
+
+Required properties :
+ - gpio-controller : Specify as gpio-contoller. All child nodes will belong to
+   this gpio_chip.
+ - #gpio-cells: We encode a PMIC pin number and a 32-bit flag field to
+   specify the gpio configuration. This must be set to '2'.
+ - #address-cells: Specify one address field. This must be set to '1'.
+ - #size-cells: Specify one size-cell. This must be set to '1'.
+ - compatible = "qcom,qpnp-pin" : Specify driver matching for this driver.
+ - label: String giving the name for the gpio_chip device. This name
+   should be unique on the system and portray the specifics of the device.
+
+-Child Nodes-
+
+Required properties :
+ - reg : Specify the spmi offset and size for this pin device.
+ - qcom,pin-num : Specify the PMIC pin number for this device.
+
+Optional configuration properties :
+ -  qcom,mode:		indicates whether the pin should be input, output, or
+			both for gpios. mpp pins also support bidirectional,
+			analog in, analog out and current sink.
+			QPNP_PIN_MODE_DIG_IN	 = 0, (GPIO/MPP)
+			QPNP_PIN_MODE_DIG_OUT	 = 1, (GPIO/MPP)
+			QPNP_PIN_MODE_DIG_IN_OUT = 2, (GPIO/MPP)
+			QPNP_PIN_MODE_ANA_PASS_THRU = 3, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_MODE_BIDIR	 = 3, (MPP)
+			QPNP_PIN_MODE_AIN	 = 4, (MPP)
+			QPNP_PIN_MODE_AOUT	 = 5, (MPP)
+			QPNP_PIN_MODE_SINK	 = 6  (MPP)
+
+ - qcom,output-type:	indicates gpio should be configured as CMOS or open
+			drain.
+			QPNP_PIN_OUT_BUF_CMOS		 = 0, (GPIO)
+			QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS = 1, (GPIO)
+			QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS = 2  (GPIO)
+			QPNP_PIN_OUT_BUF_NO_DRIVE	 = 3, (GPIO_LV/GPIO_MV)
+
+ - qcom,invert:		Invert the signal of the gpio line -
+			QPNP_PIN_INVERT_DISABLE = 0 (GPIO/MPP)
+			QPNP_PIN_INVERT_ENABLE	= 1 (GPIO/MPP)
+
+ - qcom,pull:		This parameter should be programmed to different values
+			depending on whether it's GPIO or MPP.
+			For GPIO, it indicates whether a pull up or pull down
+			should be applied. If a pullup is required the
+			current strength needs to be specified.
+			Current values of 30uA, 1.5uA, 31.5uA, 1.5uA with 30uA
+			boost are supported. This value should be one of
+			the QPNP_PIN_GPIO_PULL_*. Note that the hardware ignores
+			this configuration if the GPIO is not set to input or
+			output open-drain mode.
+			QPNP_PIN_PULL_UP_30	 = 0, (GPIO)
+			QPNP_PIN_PULL_UP_1P5	 = 1, (GPIO)
+			QPNP_PIN_PULL_UP_31P5	 = 2, (GPIO)
+			QPNP_PIN_PULL_UP_1P5_30  = 3, (GPIO)
+			QPNP_PIN_PULL_DN	 = 4, (GPIO)
+			QPNP_PIN_PULL_NO	 = 5  (GPIO)
+
+			For MPP, it indicates whether a pullup should be
+			applied for bidirectitional mode only. The hardware
+			ignores the configuration when operating in other modes.
+			This value should be one of the QPNP_PIN_MPP_PULL_*.
+
+			QPNP_PIN_MPP_PULL_UP_0P6KOHM = 0, (MPP)
+			QPNP_PIN_MPP_PULL_UP_OPEN    = 1  (MPP)
+			QPNP_PIN_MPP_PULL_UP_10KOHM  = 2, (MPP)
+			QPNP_PIN_MPP_PULL_UP_30KOHM  = 3, (MPP)
+
+  - qcom,vin-sel:	specifies the voltage level when the output is set to 1.
+			For an input gpio specifies the voltage level at which
+			the input is interpreted as a logical 1.
+			QPNP_PIN_VIN0 = 0, (GPIO/MPP/GPIO_LV/GPIO_MV)
+			QPNP_PIN_VIN1 = 1, (GPIO/MPP/GPIO_MV)
+			QPNP_PIN_VIN2 = 2, (GPIO/MPP)
+			QPNP_PIN_VIN3 = 3, (GPIO/MPP)
+			QPNP_PIN_VIN4 = 4, (GPIO/MPP)
+			QPNP_PIN_VIN5 = 5, (GPIO/MPP)
+			QPNP_PIN_VIN6 = 6, (GPIO/MPP)
+			QPNP_PIN_VIN7 = 7  (GPIO/MPP)
+
+  - qcom,out-strength:	the amount of current supplied for an output gpio.
+			QPNP_PIN_OUT_STRENGTH_LOW  = 1  (GPIO)
+			QPNP_PIN_OUT_STRENGTH_MED  = 2, (GPIO)
+			QPNP_PIN_OUT_STRENGTH_HIGH = 3, (GPIO)
+
+  - qcom,dtest-sel	Route the pin internally to a DTEST line.
+			QPNP_PIN_DIG_IN_CTL_DTEST1 = 1	(GPIO/MPP)
+			QPNP_PIN_DIG_IN_CTL_DTEST2 = 2,	(GPIO/MPP)
+			QPNP_PIN_DIG_IN_CTL_DTEST3 = 3,	(GPIO/MPP)
+			QPNP_PIN_DIG_IN_CTL_DTEST4 = 4,	(GPIO/MPP)
+
+  - qcom,src-sel:	select a function for the pin. Certain pins
+			can be paired (shorted) with each other. Some gpio pins
+			can act as alternate functions.
+			In the context of gpio, this acts as a source select.
+			For mpps, this is an enable select.
+			QPNP_PIN_SEL_FUNC_CONSTANT = 0, (GPIO/MPP)
+			QPNP_PIN_SEL_FUNC_PAIRED   = 1, (GPIO/MPP)
+			QPNP_PIN_SEL_FUNC_1	   = 2, (GPIO/MPP)
+			QPNP_PIN_SEL_FUNC_2	   = 3, (GPIO/MPP)
+			QPNP_PIN_SEL_DTEST1	   = 4, (GPIO/MPP)
+			QPNP_PIN_SEL_DTEST2	   = 5, (GPIO/MPP)
+			QPNP_PIN_SEL_DTEST3	   = 6, (GPIO/MPP)
+			QPNP_PIN_SEL_DTEST4	   = 7  (GPIO/MPP)
+
+			Below are the source-select values for GPIO_LV/MV.
+			QPNP_PIN_LV_MV_SEL_FUNC_CONSTANT   = 0, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_FUNC_PAIRED	   = 1, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_FUNC_1	   = 2, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_FUNC_2	   = 3, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_FUNC_3	   = 4, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_FUNC_4	   = 5, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_DTEST1	   = 6  (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_DTEST2	   = 7, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_DTEST3	   = 8, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_LV_MV_SEL_DTEST4	   = 9, (GPIO_LV/GPIO_MV)
+
+ - qcom,master-en:	1 = Enable features within the
+			pin block based on configurations. (GPIO/MPP)
+			0 = Completely disable the block and
+			let the pin float with high impedance
+			regardless of other settings. (GPIO/MPP)
+ - qcom,aout-ref:	set the analog output reference.
+
+			QPNP_PIN_AOUT_1V25    = 0, (MPP)
+			QPNP_PIN_AOUT_0V625   = 1, (MPP)
+			QPNP_PIN_AOUT_0V3125  = 2, (MPP)
+			QPNP_PIN_AOUT_MPP     = 3, (MPP)
+			QPNP_PIN_AOUT_ABUS1   = 4, (MPP)
+			QPNP_PIN_AOUT_ABUS2   = 5, (MPP)
+			QPNP_PIN_AOUT_ABUS3   = 6, (MPP)
+			QPNP_PIN_AOUT_ABUS4   = 7  (MPP)
+
+ - qcom,ain-route:	Set the destination for analog input.
+			QPNP_PIN_AIN_AMUX_CH5   = 0, (MPP)
+			QPNP_PIN_AIN_AMUX_CH6   = 1, (MPP)
+			QPNP_PIN_AIN_AMUX_CH7   = 2, (MPP)
+			QPNP_PIN_AIN_AMUX_CH8   = 3, (MPP)
+			QPNP_PIN_AIN_AMUX_ABUS1 = 4, (MPP)
+			QPNP_PIN_AIN_AMUX_ABUS2 = 5, (MPP)
+			QPNP_PIN_AIN_AMUX_ABUS3 = 6, (MPP)
+			QPNP_PIN_AIN_AMUX_ABUS4 = 7  (MPP)
+
+ - qcom,cs-out:		Set the the amount of output to sync in mA.
+			QPNP_PIN_CS_OUT_5MA  = 0, (MPP)
+			QPNP_PIN_CS_OUT_10MA = 1, (MPP)
+			QPNP_PIN_CS_OUT_15MA = 2, (MPP)
+			QPNP_PIN_CS_OUT_20MA = 3, (MPP)
+			QPNP_PIN_CS_OUT_25MA = 4, (MPP)
+			QPNP_PIN_CS_OUT_30MA = 5, (MPP)
+			QPNP_PIN_CS_OUT_35MA = 6, (MPP)
+			QPNP_PIN_CS_OUT_40MA = 7  (MPP)
+
+ - qcom,apass-sel:	Set the ATEST channel to route the signal
+			QPNP_PIN_APASS_SEL_ATEST1 = 0, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_APASS_SEL_ATEST2 = 1, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_APASS_SEL_ATEST3 = 2, (GPIO_LV/GPIO_MV)
+			QPNP_PIN_APASS_SEL_ATEST4 = 3, (GPIO_LV/GPIO_MV)
+
+*Note: If any of the configuration properties are not specified, then the
+       qpnp-pin driver will not modify that respective configuration in
+       hardware.
+
+[PMIC GPIO clients]
+
+Required properties :
+ - gpios : Contains 3 fields of the form <&gpio_controller pmic_pin_num flags>
+
+[Example]
+
+qpnp: qcom,spmi@fc4c0000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupt-controller;
+		#interrupt-cells = <3>;
+
+		qcom,pm8941@0 {
+			reg = <0x0>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			pm8941_gpios: gpios {
+				compatible = "qcom,qpnp-pin";
+				gpio-controller;
+				#gpio-cells = <2>;
+				#address-cells = <1>;
+				#size-cells = <1>;
+
+				gpio@c000 {
+					reg = <0xc000 0x100>;
+					qcom,pin-num = <62>;
+				};
+
+				gpio@c100 {
+					reg = <0xc100 0x100>;
+					qcom,pin-num = <20>;
+					qcom,source_sel = <2>;
+					qcom,pull = <5>;
+				};
+			};
+
+			qcom,testgpio@1000 {
+				compatible = "qcom,qpnp-testgpio";
+				reg = <0x1000 0x1000>;
+				gpios = <&pm8941_gpios 62 0x0 &pm8941_gpios 20 0x1>;
+			};
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
new file mode 100644
index 0000000..3315304
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-current.txt
@@ -0,0 +1,104 @@
+Qualcomm Technologies, Inc. QPNP PMIC current ADC driver
+
+QPNP PMIC current ADC (IADC) provides interface to clients to read
+current. A 16 bit ADC is used for current measurements. There are multiple
+peripherals to the IADC and the scope of the driver is to provide interface
+for the USR peripheral of the IADC.
+
+IADC node
+
+Required properties:
+- compatible : should be "qcom,qpnp-iadc" for Current ADC driver.
+- reg : offset and length of the PMIC Arbiter register map.
+- reg-names : resource names used for the physical base address of the PMIC IADC
+	      peripheral, the SMBB_BAT_IF_TRIM_CNST_RDS register.
+	      Should be "iadc-base" for the PMIC IADC peripheral base register.
+	      Should be "batt-id-trim-cnst-rds" for reading the
+	      SMBB_BAT_IF_TRIM_CNST_RDS register.
+- address-cells : Must be one.
+- size-cells : Must be zero.
+- interrupts : The USR bank peripheral IADC interrupt.
+- interrupt-names : Should contain "eoc-int-en-set".
+- qcom,adc-bit-resolution : Bit resolution of the ADC.
+- qcom,adc-vdd-reference : Voltage reference used by the ADC.
+
+Optional properties:
+- qcom,rsense : Use this property when external rsense should be used
+		for current calculation and specify the units in nano-ohms.
+- qcom,iadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the
+		    PMIC type and revision for applying the appropriate temperature
+		    compensation parameters.
+- qcom,use-default-rds-trim : Add this property to check if certain conditions are to be checked
+			      reading the SMBB_BAT_IF_CNST_RDS, IADC_RDS trim register and
+			      manufacturer type. Check the driver for conditions that each of the type.
+			      0 : Select the TypeA to read the IADC and SMBB trim register and
+				  apply the default RSENSE if conditions are met.
+			      1 : Select the TypeB to read the IADC, SMBB trim register and
+				  manufacturer type and apply the default RSENSE if conditions are met.
+			      2 : Select the TypeC to read the IADC, SMBB trim register and
+				  apply the default RSENSE if conditions are met.
+
+Channel node
+NOTE: Atleast one Channel node is required.
+
+Client required property:
+- qcom,<consumer name>-iadc : The phandle to the corresponding iadc device.
+			The consumer name passed to the driver when calling
+			qpnp_get_iadc() is used to associate the client
+			with the corresponding device.
+
+Required properties:
+- label : Channel name used for sysfs entry.
+- reg : AMUX channel number.
+- qcom,channel-num : Channel number associated to the AMUX input.
+- qcom,decimation : Sampling rate to use for the individual channel measurement.
+		    Select from the following unsigned int.
+		    0 : 512
+		    1 : 1K
+		    2 : 2K
+		    3 : 4K
+- qcom,fast-avg-setup : Average number of samples to be used for measurement. Fast averaging
+			provides the option to obtain a single measurement from the ADC that
+			is an average of multiple samples. The value selected is 2^(value)
+			Select from the following unsigned int.
+			0 : 1
+			1 : 2
+			2 : 4
+			3 : 8
+			4 : 16
+			5 : 32
+			6 : 64
+			7 : 128
+			8 : 256
+- qcom,iadc-vadc : Corresponding phandle of the VADC device to read the die_temperature and set
+		simultaneous voltage and current conversion requests.
+
+Example:
+	/* Main Node */
+	qcom,iadc@3200 {
+                        compatible = "qcom,qpnp-iadc";
+                        reg = <0x3200 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+                        interrupts = <0 0x36 0>;
+			interrupt-names = "eoc-int-en-set";
+                        qcom,adc-bit-resolution = <16>;
+                        qcom,adc-vdd-reference = <1800>;
+			qcom,rsense = <1500>;
+			qcom,iadc-vadc = <&pm8941_vadc>;
+
+			/* Channel Node */
+                        chan@0 = {
+                                label = "rsense";
+                                reg = <0>;
+                                qcom,decimation = <0>;
+                                qcom,fast-avg-setup = <0>;
+                        };
+	};
+
+Client device example:
+/* Add to the clients node that needs the IADC */
+client_node {
+	qcom,client-iadc = <&pm8941_iadc>;
+};
diff --git a/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
new file mode 100644
index 0000000..62ba54b
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/qpnp-adc-voltage.txt
@@ -0,0 +1,197 @@
+Qualcomm Technologies, Inc. QPNP PMIC Voltage ADC Arbiter
+
+QPNP PMIC Voltage ADC (VADC) provides interface to clients to read
+Voltage. A 15 bit ADC is used for Voltage measurements. There are multiple
+peripherals to the VADC and the scope of the driver is to provide interface
+for the USR peripheral of the VADC.
+
+VADC node
+
+Required properties:
+- compatible : should be "qcom,qpnp-vadc" for Voltage ADC device driver and
+		"qcom,qpnp-vadc-hc" for VADC_HC voltage ADC device driver.
+- reg : offset and length of the PMIC Aribter register map.
+- address-cells : Must be one.
+- size-cells : Must be zero.
+- interrupts : The USR bank peripheral VADC interrupt.
+- interrupt-names : Should contain "eoc-int-en-set" for EOC,
+		"high-thr-en-set" for high threshold interrupts and
+		"low-thr-en-set" for low threshold interrupts. High and low threshold
+		interrupts are to be enabled if VADC_USR needs to support recurring measurement.
+- qcom,adc-bit-resolution : Bit resolution of the ADC.
+- qcom,adc-vdd-reference : Voltage reference used by the ADC.
+
+Channel nodes
+NOTE: Atleast one Channel node is required.
+
+Optional properties:
+- qcom,vadc-poll-eoc: Use polling instead of interrupts for End of Conversion completion.
+- qcom,pmic-revid : Phandle pointing to the revision peripheral node. Use it to query the
+		    PMIC type and revision for applying the appropriate temperature
+		    compensation parameters.
+-qcom,vadc-meas-int-mode : Enable VADC_USR to handle requests to perform recurring measurements
+			   for any one supported channel along with supporting single conversion
+			   requests.
+- qcom,vadc-recalib-check: Add this property to check if recalibration required due to inaccuracy.
+- qcom,vadc-thermal-node : If present a thermal node is created and the channel is registered as
+			   part of the thermal sysfs which allows clients to use the thermal framework
+			   to set temperature thresholds and receive notification when the temperature
+			   crosses a set threshold, read temperature and enable/set trip types supported
+			   by the thermal framework.
+- hkadc_ldo-supply : Add this property if VADC needs to perform a Software Vote for the HKADC.
+- hkadc_ok-supply : Add this property if the VADC needs to perform a Software vote for the HKADC VREG_OK.
+- qcom,cal-val : Add this property for VADC_HC voltage ADC device to select from the following
+		unsigned int. If the property is not present the default calibration value of
+		using the timer value is chosen.
+		    0 : The calibration values used for measurement are from a timer.
+		    1 : Forces a fresh measurement for calibration values at the same time
+			measurement is taken.
+
+Client required property:
+- qcom,<consumer name>-vadc : The phandle to the corresponding vadc device.
+			The consumer name passed to the driver when calling
+			qpnp_get_vadc() is used to associate the client
+			with the corresponding device.
+
+Required properties:
+- label : Channel name used for sysfs entry.
+- reg : AMUX channel number.
+- qcom,decimation : Sampling rate to use for the individual channel measurement.
+		    Select from following unsigned int for Voltage ADC device.
+		    0 : 512
+		    1 : 1K
+		    2 : 2K
+		    3 : 4K
+		    Select from following unsigned int for VADC_HC voltage ADC device.
+		    0 : 256
+		    1 : 512
+		    2 : 1024
+- qcom,pre-div-channel-scaling : Pre-div used for the channel before the signal
+				 is being measured. Some of the AMUX channels
+				 support dividing the signal from a predetermined
+				 ratio. The configuration for this node is to know
+				 the pre-determined ratio and use it for post scaling.
+				 Select from the following unsigned int.
+				 0 : {1, 1}
+				 1 : {1, 3}
+				 2 : {1, 4}
+				 3 : {1, 6}
+				 4 : {1, 20}
+				 5 : {1, 8}
+				 6 : {10, 81}
+				 7 : {1, 10}
+- qcom,calibration-type : Reference voltage to use for channel calibration.
+			  Channel calibration is dependendent on the channel.
+			  Certain channels like XO_THERM, BATT_THERM use ratiometric
+			  calibration. Most other channels fall under absolute calibration.
+			  Select from the following strings.
+			  "absolute" : Uses the 625mv and 1.25V reference channels.
+			  "ratiometric" : Uses the reference Voltage/GND for calibration.
+- qcom,scale-function : Scaling function used to convert raw ADC code to units specific to
+			a given channel.
+			Select from the following unsigned int.
+			0 : Default scaling to convert raw adc code to voltage.
+			1 : Conversion to temperature based on btm parameters.
+			2 : Returns result in degC for 100k pull-up.
+			3 : Returns current across 0.1 ohm resistor.
+			4 : Returns XO thermistor voltage in degree's Centigrade.
+			5 : Returns result in degC for 150k pull-up.
+			9 : Conversion to temperature based on -15~55 allowable
+			    battery charging tempeature setting for btm parameters.
+- qcom,hw-settle-time : Settling period for the channel before ADC read.
+			Select from the following unsigned int.
+			0 : 0us
+			1 : 100us
+			2 : 200us
+			3 : 300us
+			4 : 400us
+			5 : 500us
+			6 : 600us
+			7 : 700us
+			8 : 800us
+			9 : 900us
+			0xa : 1ms
+			0xb : 2ms
+			0xc : 4ms
+			0xd : 6ms
+			0xe : 8ms
+			0xf : 10ms
+- qcom,fast-avg-setup : Average number of samples to be used for measurement. Fast averaging
+			provides the option to obtain a single measurement from the ADC that
+			is an average of multiple samples. The value selected is 2^(value)
+			Select from the following unsigned int for Voltage ADC device.
+			0 : 1
+			1 : 2
+			2 : 4
+			3 : 8
+			4 : 16
+			5 : 32
+			6 : 64
+			7 : 128
+			8 : 256
+			Select from the following unsigned int for VADC_HC ADC device.
+			0 : 1
+			1 : 2
+			2 : 4
+			3 : 8
+			4 : 16
+
+Example:
+	/* Main Node */
+	qcom,vadc@3100 {
+                        compatible = "qcom,qpnp-vadc";
+                        reg = <0x3100 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+                        interrupts = <0x0 0x31 0x0>;
+			interrupt-names = "eoc-int-en-set";
+                        qcom,adc-bit-resolution = <15>;
+                        qcom,adc-vdd-reference = <1800>;
+
+			/* Channel Node */
+                        chan@0 {
+                                label = "usb_in";
+                                reg = <0>;
+                                qcom,decimation = <0>;
+                                qcom,pre-div-channel-scaling = <4>;
+                                qcom,calibration-type = "absolute";
+                                qcom,scale-function = <0>;
+                                qcom,hw-settle-time = <0>;
+                                qcom,fast-avg-setup = <0>;
+                        };
+	};
+
+Client device example:
+/* Add to the clients node that needs the VADC channel A/D */
+client_node {
+	qcom,client-vadc = <&pm8941_vadc>;
+};
+
+/* Clients have an option of measuring an analog signal through an MPP.
+   MPP block is not part of the VADC block but is an individual PMIC
+   block that has an option to support clients to configure an MPP as
+   an analog input which can be routed through one of the VADC pre-mux
+   inputs. Here is an example of how to configure an MPP as an analog
+   input */
+
+/* Configure MPP4 as an Analog input to AMUX8 and read from channel 0x23 */
+/* MPP DT configuration in the platform DT file*/
+	mpp@a300 { /* MPP 4 */
+		qcom,mode = <4>; /* AIN input */
+		qcom,invert = <1>; /* Enable MPP */
+		qcom,ain-route = <3>; /* AMUX 8 */
+		qcom,master-en = <1>;
+		qcom,src-sel = <0>; /* Function constant */
+	};
+
+/* VADC Channel configuration */
+	chan@23 {
+		label = "mpp4_div3";
+		reg = <0x23>;
+		qcom,decimation = <0>;
+		qcom,pre-div-channel-scaling = <1>;
+		qcom,calibration-type = "absolute";
+		qcom,scale-function = <0>;
+		qcom,hw-settle-time = <0>;
+		qcom,fast-avg-setup = <0>;
+	};
diff --git a/Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt b/Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt
new file mode 100644
index 0000000..6880d30
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/qcom-tadc.txt
@@ -0,0 +1,141 @@
+Qualcomm Technologies, Inc. TADC Specific Bindings
+
+TADC (Telemetry ADC) is a 10 bit resolution ADC which has 8 channels: battery
+temperature, skin temperature, die temperature, battery current, battery
+voltage, input current, input voltage, and OTG current.
+
+=======================
+Required Node Structure
+=======================
+
+A TADC must be described in two levels of devices nodes.
+
+=======================
+First Level Node - TADC
+=======================
+
+- reg
+  Usage:      required
+  Value type: <prop-encoded-array>
+  Definition: Address and size of the TADC register block.
+
+TADC specific properties:
+- compatible
+  Usage:      required
+  Value type: <string>
+  Definition: Must be "qcom,tadc".
+
+- interrupts
+  Usage:      required
+  Value type: <prop-encoded-array>
+  Definition: Peripheral interrupt specifier.
+
+- interrupt-names
+  Usage:      required
+  Value type: <stringlist>
+  Definition: Interrupt names.  This list must match up 1-to-1 with the
+	      interrupts specified in the 'interrupts' property.
+
+=============================================
+Second Level Nodes - TADC Thermistor Channels
+=============================================
+
+- reg
+  Usage:      required
+  Value type: <u32>
+  Definition: The 0 based channel number.
+
+TADC thermistor channel specific properties:
+- qcom,rbias
+  Usage:      required
+  Value type: <u32>
+  Definition: The bias resistor value.
+
+- qcom,therm-at-25degc
+  Usage:      required
+  Value type: <u32>
+  Definition: The thermistor resistance at 25 DegC.
+
+- qcom,beta-coefficient
+  Usage:      required
+  Value type: <u32>
+  Definition: The beta coefficeent or B-parameter of the thermistor.
+
+===============================================
+Second Level Nodes - TADC Scale/Offset Channels
+===============================================
+
+- reg
+  Usage:      required
+  Value type: <u32>
+  Definition: The 0 based channel number.
+
+TADC scale/offset channel specific properties:
+- qcom,scale
+  Usage:      required
+  Value type: <s32>
+  Definition: The RAW scaling factor.
+
+- qcom,offset
+  Usage:      optional
+  Value type: <s32>
+  Definition: The offset after scaling.
+
+=======
+Example
+=======
+
+smb138x_tadc: qcom,tadc@3600 {
+	compatible = "qcom,tadc";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	#io-channel-cells = <1>;
+	interrupts = <0x36 0x0 IRQ_TYPE_EDGE_BOTH>;
+	interrupt-names = "eoc";
+
+	batt_temp@0 {
+		reg = <0>;
+		qcom,rbias = <68100>;
+		qcom,rtherm-at-25degc = <68000>;
+		qcom,beta-coefficient = <3450>;
+	};
+
+	skin_temp@1 {
+		reg = <1>;
+		qcom,rbias = <33000>;
+		qcom,rtherm-at-25degc = <68000>;
+		qcom,beta-coefficient = <3450>;
+	};
+
+	die_temp@2 {
+		reg = <2>;
+		qcom,scale = <(-1032)>;
+		qcom,offset = <344125>;
+	};
+
+	batt_i@3 {
+		reg = <3>;
+		qcom,channel = <3>;
+		qcom,scale = <20000000>;
+	};
+
+	batt_v@4 {
+		reg = <4>;
+		qcom,scale = <5000000>;
+	};
+
+	input_i@5 {
+		reg = <5>;
+		qcom,scale = <14285714>;
+	};
+
+	input_v@6 {
+		reg = <6>;
+		qcom,scale = <25000000>;
+	};
+
+	otg_i@7 {
+		reg = <7>;
+		qcom,scale = <5714286>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
new file mode 100644
index 0000000..da54fb1
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash-v2.txt
@@ -0,0 +1,308 @@
+Qualcomm Technologies Inc. PNP v2 Flash LED
+
+QPNP (Qualcomm Technologies Inc. Plug N Play) Flash LED (Light
+Emitting Diode) driver v2 is used to provide illumination to
+camera sensor when background light is dim to capture good
+picture. It can also be used for flashlight/torch application.
+It is part of PMIC on Qualcomm Technologies Inc. reference platforms.
+
+Main node:
+
+Required properties:
+- compatible		: Should be "qcom,qpnp-flash-led-v2"
+- reg			: Base address and size for flash LED modules
+- qcom,pmic-revid	: phandle of PMIC revid module. This is used to
+			  identify the PMIC subtype.
+
+Optional properties:
+- interrupts		: Specifies the interrupts associated with flash-led.
+- interrupt-names	: Specify the interrupt names associated with interrupts.
+- qcom,hdrm-auto-mode	: Boolean type to select headroom auto mode enabled or not
+- qcom,isc-delay-us	: Integer type to specify short circuit delay. Valid values are 32, 64,
+			  128, 192. Unit is uS.
+- qcom,warmup-delay-us	: Integer type to specify warm up delay. Valid values are 32, 64,
+			  128, 192. Unit is uS.
+- qcom,short-circuit-det	: Boolean property which enables short circuit fault detection.
+- qcom,open-circuit-det		: Boolean property which enables open circuit fault detection.
+- qcom,vph-droop-det	: Boolean property which enables VPH droop detection.
+- qcom,vph-droop-hys-mv	: Integer property to specify VPH droop hysteresis. It is only used if
+			  qcom,vph-droop-det is specified. Valid values are 0, 25, 50 and 75.
+			  Unit is mV.
+- qcom,vph-droop-thresh-mv	: Integer property to specify VPH droop threshold. It is only used if
+				  qcom,vph-droop-det is specified. Valid values are
+				  2500 to 3200 with step size of 100. Unit is mV.
+- qcom,vph-droop-debounce-us	: Integer property to specify VPH droop debounce time. It is only used
+				  if qcom,vph-droop-det is specified. Valid values are 0, 8, 16 and 26.
+				  Unit is uS.
+- qcom,led1n2-iclamp-low-ma	: Integer property to specify current clamp low
+				  level for mitigation. Unit is mA. Allowed
+				  values are same as under qcom,max-current.
+- qcom,led1n2-iclamp-mid-ma	: Integer property to specify current clamp mid
+				  level for mitigation. Unit is mA. Allowed
+				  values are same as under qcom,max-current.
+- qcom,led3-iclamp-low-ma	: Integer property to specify current clamp low
+				  level for mitigation. Unit is mA. Allowed
+				  values are same as under qcom,max-current.
+- qcom,led3-iclamp-mid-ma	: Integer property to specify current clamp mid
+				  level for mitigation. Unit is mA. Allowed
+				  values are same as under qcom,max-current.
+- qcom,vled-max-uv		: Integer property for flash current predictive mitigation.
+				  Default value is 3500000 uV.
+- qcom,ibatt-ocp-threshold-ua	: Integer property for flash current predictive mitigation.
+				  Default value is 4500000 uA.
+- qcom,rparasitic-uohm		: Integer property for flash current predictive mitigation indicating
+				  parasitic component of battery resistance. Default value is 0 uOhm.
+- qcom,lmh-ocv-threshold-uv	: Required property for flash current preemptive LMH mitigation.
+				  Default value is 3700000 uV.
+- qcom,lmh-rbatt-threshold-uohm	: Required property for flash current preemptive LMH mitigation.
+				  Default value is 400000 uOhm.
+- qcom,lmh-mitigation-sel	: Optional property to configure flash current preemptive LMH mitigation.
+				  Accepted values are:
+				  0: MITIGATION_DISABLED
+				  1: MITIGATION_BY_ILED_THRESHOLD
+				  2: MITIGATION_BY_SW
+				  Default value is 2.
+- qcom,chgr-mitigation-sel	: Optional property to configure flash current preemptive charger mitigation.
+				  Accepted values are:
+				  0: MITIGATION_DISABLED
+				  1: MITIGATION_BY_ILED_THRESHOLD
+				  2: MITIGATION_BY_SW
+				  Default value is 2.
+- qcom,lmh-level		: Optional property to configure flash current preemptive LMH mitigation.
+				  Accepted values are 0, 1, and 3. Default value is 0.
+- qcom,iled-thrsh-ma		: Optional property to configure the led current threshold at which HW
+				  preemptive mitigation is triggered. Unit is mA. Default value is 1000.
+				  Accepted values are in the range 0 - 3100, with steps of 100.
+				  0 disables autonomous HW mitigation.
+- qcom,thermal-derate-en	: Boolean property to enable flash current thermal mitigation.
+- qcom,thermal-derate-current	: Array of currrent limits for thermal mitigation. Required if
+				  qcom,thermal-derate-en is specified. Unit is mA. Format is
+				  qcom,thermal-derate-current = <OTST1_LIMIT, OTST2_LIMIT, OTST3_LIMIT>.
+- qcom,otst-ramp-back-up-dis	: Boolean property to disable current ramp
+				  backup after thermal derate trigger is
+				  deasserted.
+- qcom,thermal-derate-slow	: Integer property to specify slow ramping
+				  down thermal rate. Unit is in uS. Allowed
+				  values are: 128, 256, 512, 1024, 2048, 4096,
+				  8192 and 314592.
+- qcom,thermal-derate-fast	: Integer property to specify fast ramping
+				  down thermal rate. Unit is in uS. Allowed
+				  values are: 32, 64, 96, 128, 256, 384 and
+				  512.
+- qcom,thermal-debounce		: Integer property to specify thermal debounce
+				  time. It is only used if qcom,thermal-derate-en
+				  is specified. Unit is in uS. Allowed values
+				  are: 0, 16, 32, 64.
+- qcom,thermal-hysteresis	: Integer property to specify thermal derating
+				  hysteresis. Unit is in deciDegC. It is only
+				  used if qcom,thermal-derate-en is specified.
+				  Allowed values are:
+				  0, 15, 30, 45 for pmi8998.
+				  0, 20, 40, 60 for pm660l.
+- qcom,thermal-thrsh1		: Integer property to specify OTST1 threshold
+				  for thermal mitigation. Unit is in Celsius.
+				  Accepted values are:
+				  85, 79, 73, 67, 109, 103, 97, 91.
+- qcom,thermal-thrsh2		: Integer property to specify OTST2 threshold
+				  for thermal mitigation. Unit is in Celsius.
+				  Accepted values are:
+				  110, 104, 98, 92, 134, 128, 122, 116.
+- qcom,thermal-thrsh3		: Integer property to specify OTST3 threshold
+				  for thermal mitigation. Unit is in Celsius.
+				  Accepted values are:
+				  125, 119, 113, 107, 149, 143, 137, 131.
+- qcom,hw-strobe-option	: Integer type to specify hardware strobe option. Based on the specified
+			  value, additional GPIO configuration may be required to provide strobing
+			  support. Supported values are:
+			  0: Flash strobe is used for LED1, LED2, LED3
+			  1: Flash strobe is used for LED1, LED2 and GPIO10 is used for LED3
+			  2: Flash strobe is used for LED1; GPIO9 is used for LED2; GPIO10 is used for LED3
+- switchX-supply		: phandle of the regulator that needs to be used
+				  as a supply for flash switch_X device.
+
+Child node: Contains settings for each individual LED. Each LED channel needs a flash node and
+torch node for itself, and an individual switch node to serve as an overall switch.
+
+Required Properties:
+- label			: Type of led that will be used, either "flash", "torch", or "switch.
+- qcom,led-name		: Name of the LED.
+- qcom,default-led-trigger	: Trigger for the camera flash and torch. Accepted values are
+			  "flash0_trigger", "flash1_trigger", "flash2_trigger, "torch0_trigger",
+			  "torch1_trigger", "torch2_trigger", and "switch_trigger".
+- qcom,id		: ID for each physical LED equipped. In order to handle situation when
+			  only 1 or 2 LEDs are installed, flash and torch nodes on LED channel 0
+			  should be specified with ID 0; nodes on channel 1 be ID 1, etc. This is
+			  not required for switch node.
+- qcom,max-current	: Maximum current allowed on this LED. Valid values should be
+			  integer from 0 to 1500 inclusive. Flash 2 should have maximum current of
+			  750 per hardware requirement. Unit is mA. For torch, the maximum current
+			  is clamped at 500 mA. This is not required for the switch node.
+- qcom,duration-ms	: Required property for flash nodes but not needed for torch. Integer
+			  type specifying flash duration. Values are from 10ms to 1280ms with
+			  10ms resolution. This is not required for switch node.
+- qcom,led-mask		: Required property for switch nodes. Bitmask to indicate which leds are
+			  controlled by this switch node. Accepted values are in the range 1 to 7,
+			  inclusive. Example:
+			  qcom,led-mask = <4>; /* This switch node controls the flash2/torch2 led. */
+
+Optional properties:
+- qcom,current-ma	: operational current intensity for LED in mA. Accepted values are a
+			  positive integer in the range of 0 to qcom,max-current inclusive.
+- qcom,ires-ua		: Integer type to specify current resolution. Accepted values should be
+			  12500, 10000, 7500, and 5000. Unit is uA.
+- qcom,hdrm-voltage-mv	: Integer type specifying headroom voltage. Values are from 125mV to 500mV
+			  with 25mV resolution. Default setting is 325mV
+- qcom,hdrm-vol-hi-lo-win-mv	: Integer type to specify headroom voltage swing range. Values are
+				  from 0mV to 375mV with 25mV resolution. Default setting is 100mV.
+- pinctrl-names		: Name of the pinctrl configuration that will be used when external GPIOs
+			  are used for enabling/disabling, HW strobing of flash LEDs. For more
+			  information on using pinctrl, please refer
+			  Documentation/devicetree/bindings/pinctrl/msm-pinctrl.txt
+                          Following are the pinctrl configs that can be specified:
+                          "led_enable" : pinctrl config to enable led. This should specify the active
+                          configuration defined for each pin or pin group.
+                          "led_disable" : pinctrl config to disable led. This should specify the sleep
+                          configuration defined for each pin or pin group.
+                          "strobe_enable" : pinctrl config to enable hw-strobe. This should specify the
+                          active configuration defined for each pin or pin group.
+                          "strobe_disable" : pinctrl config to disable hw-strobe. This should specify the
+                          sleep configuration defined for each pin or pin group.
+- qcom,hw-strobe-gpio	: phandle to specify GPIO for hardware strobing. This is used when there is no
+			  pinctrl support or PMIC GPIOs are used.
+- qcom,hw-strobe-sel	: Boolean property to enable hardware strobe. If not defined, software strobe
+			  will be used.
+- qcom,hw-strobe-edge-trigger	: Boolean property to select trigger type. If defined, hw-strobe is set to
+				  be edge triggered. Otherwise, it is level triggered.
+- qcom,hw-strobe-active-low	: Boolean property to select strobe signal polarity. If defined, hw-strobe
+				  signal polarity is set to active-low, else it is active-high.
+Example:
+	qcom,leds@d300 {
+		compatible = "qcom,qpnp-flash-led-v2";
+		status = "okay";
+		reg = <0xd300 0x100>;
+		label = "flash";
+		interrupts = <0x3 0xd3 0x0 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x1 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x2 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x3 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x4 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x5 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x6 IRQ_TYPE_EDGE_BOTH>,
+			     <0x3 0xd3 0x7 IRQ_TYPE_EDGE_BOTH>;
+		interrupt-names = "led-fault-irq",
+				"mitigation-irq",
+				"flash-timer-exp-irq",
+				"all-ramp-down-done-irq",
+				"all-ramp-up-done-irq",
+				"led3-ramp-up-done-irq",
+				"led2-ramp-up-done-irq",
+				"led1-ramp-up-done-irq";
+
+		qcom,hdrm-auto-mode;
+		qcom,isc-delay = <192>;
+		switch0-supply = <&pmi8998_bob>;
+
+		pmi8998_flash0: qcom,flash_0 {
+			label = "flash";
+			qcom,led-name = "led:flash_0";
+			qcom,max-current = <1500>;
+			qcom,default-led-trigger =
+						"flash0_trigger";
+			qcom,id = <0>;
+			qcom,current-ma = <1000>;
+			qcom,duration-ms = <1280>;
+			qcom,ires-ua = <12500>;
+			qcom,hdrm-voltage-mv = <325>;
+			qcom,hdrm-vol-hi-lo-win-mv = <100>;
+		};
+
+		pmi8998_flash1: qcom,flash_1 {
+			label = "flash";
+			qcom,led-name = "led:flash_1";
+			qcom,max-current = <1500>;
+			qcom,default-led-trigger =
+						"flash1_trigger";
+			qcom,id = <1>;
+			qcom,current-ma = <1000>;
+			qcom,duration-ms = <1280>;
+			qcom,ires-ua = <12500>;
+			qcom,hdrm-voltage-mv = <325>;
+			qcom,hdrm-vol-hi-lo-win-mv = <100>;
+		};
+
+		pmi8998_flash2: qcom,flash_2 {
+			label = "flash";
+			qcom,led-name = "led:flash_2";
+			qcom,max-current = <750>;
+			qcom,default-led-trigger =
+						"flash2_trigger";
+			qcom,id = <2>;
+			qcom,current-ma = <500>;
+			qcom,duration-ms = <1280>;
+			qcom,ires-ua = <12500>;
+			qcom,hdrm-voltage-mv = <325>;
+			qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			pinctrl-names = "led_enable","led_disable";
+			pinctrl-0 = <&led_enable>;
+			pinctrl-1 = <&led_disable>;
+		};
+
+		pmi8998_torch0: qcom,torch_0 {
+			label = "torch";
+			qcom,led-name = "led:torch_0";
+			qcom,max-current = <500>;
+			qcom,default-led-trigger =
+						"torch0_trigger";
+			qcom,id = <0>;
+			qcom,current-ma = <300>;
+			qcom,ires-ua = <12500>;
+			qcom,hdrm-voltage-mv = <325>;
+			qcom,hdrm-vol-hi-lo-win-mv = <100>;
+		};
+
+		pmi8998_torch1: qcom,torch_1 {
+			label = "torch";
+			qcom,led-name = "led:torch_1";
+			qcom,max-current = <500>;
+			qcom,default-led-trigger =
+						"torch1_trigger";
+			qcom,id = <1>;
+			qcom,current-ma = <300>;
+			qcom,ires-ua = <12500>;
+			qcom,hdrm-voltage-mv = <325>;
+			qcom,hdrm-vol-hi-lo-win-mv = <100>;
+		};
+
+		pmi8998_torch2: qcom,torch_2 {
+			label = "torch";
+			qcom,led-name = "led:torch_2";
+			qcom,max-current = <500>;
+			qcom,default-led-trigger =
+						"torch2_trigger";
+			qcom,id = <2>;
+			qcom,current-ma = <300>;
+			qcom,ires-ua = <12500>;
+			qcom,hdrm-voltage-mv = <325>;
+			qcom,hdrm-vol-hi-lo-win-mv = <100>;
+			pinctrl-names = "led_enable","led_disable";
+			pinctrl-0 = <&led_enable>;
+			pinctrl-1 = <&led_disable>;
+		};
+
+		pmi8998_switch0: qcom,led_switch_0 {
+			label = "switch";
+			qcom,led-name = "led:switch_0";
+			qcom,led-mask = <3>;
+			qcom,default-led-trigger =
+						"switch0_trigger";
+		};
+
+		pmi8998_switch1: qcom,led_switch_1 {
+			label = "switch";
+			qcom,led-name = "led:switch_1";
+			qcom,led-mask = <4>;
+			qcom,default-led-trigger =
+						"switch1_trigger";
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt
new file mode 100644
index 0000000..ed1ddf5
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-flash.txt
@@ -0,0 +1,180 @@
+Qualcomm Technologies PNP Flash LED
+
+QPNP (Qualcomm Technologies Plug N Play) Flash LED (Light
+Emitting Diode) driver is used to provide illumination to
+camera sensor when background light is dim to capture good
+picture. It can also be used for flashlight/torch application.
+It is part of PMIC on Qualcomm Technologies reference platforms.
+The PMIC is connected to the host processor via SPMI bus.
+
+Required properties:
+- compatible		: should be "qcom,qpnp-flash-led"
+- reg			: base address and size for flash LED modules
+
+Optional properties:
+- qcom,headroom		: headroom to use. Values should be 250, 300,
+			  400 and 500 in mV.
+- qcom,startup-dly	: delay before flashing after flash executed.
+			  Values should 10, 32, 64, and 128 in us.
+- qcom,clamp-curr	: current to clamp at when voltage droop happens.
+			  Values are in integer from 0 to 1000 inclusive,
+			  indicating 0 to 1000 mA.
+- qcom,self-check-enabled : boolean type. self fault check enablement
+- qcom,thermal-derate-enabled : boolean type. derate enablement when module
+				temperature reaches threshold
+- qcom,thermal-derate-threshold : thermal threshold for derate. Values
+				should be 95, 105, 115, 125 in C.
+- qcom,thermal-derate-rate	: derate rate when module temperature
+				reaches threshold. Values should be
+				"1_PERCENT", "1P25_PERCENT", "2_PERCENT",
+				"2P5_PERCENT", "5_PERCENT" in string.
+- qcom,current-ramp-enabled	: boolean type. stepped current ramp enablement
+- qcom,ramp-up-step		: current ramp up rate. Values should be
+				  "0P2US", "0P4US", "0P8US", "1P6US", "3P3US",
+				  "6P7US", "13P5US", "27US".
+- qcom,ramp-dn-step		: current ramp down rate. Values should be
+				  "0P2US", "0P4US", "0P8US", "1P6US", "3P3US",
+				  "6P7US", "13P5US", "27US".
+- qcom,vph-pwr-droop-enabled	: boolean type. VPH power droop enablement. Enablement
+				  allows current clamp when phone power drops below
+				  pre-determined threshold
+- qcom,vph-pwr-droop-threshold	: VPH power threshold for module to clamp current.
+				  Values are 2500 - 3200 in mV with 100 mV steps.
+- qcom,vph-pwr-droop-debounce-time : debounce time for module to confirm a voltage
+				     droop is happening. Values are 0, 10, 32, 64
+				     in us.
+- qcom,pmic-charger-support	: Boolean type. This tells if flash utilizes charger boost
+				  support
+- qcom,headroom-sense-ch0-enabled: Boolean type. This configures headroom sensing enablement
+				   for LED channel 0
+- qcom,headroom-sense-ch1-enabled: Boolean type. This configures headroom sensing enablement
+				   for LED channel 1
+- qcom,power-detect-enabled	: Boolean type. This enables driver to get maximum flash LED
+				  current at current battery level to avoid intensity clamp
+				  when battery voltage is low
+- qcom,otst2-moduled-enabled	: Boolean type. This enables driver to enable MASK to support
+				OTST2 connection.
+- qcom,follow-otst2-rb-disabled	: Boolean type. This allows driver to reset/deset module.
+				By default, driver resets module. This entry allows driver to
+				bypass reset module sequence.
+- qcom,die-current-derate-enabled: Boolean type. This enables driver to get maximum flash LED
+                                   current, based on PMIC die temperature threshold to
+				   avoid significant current derate from hardware. This property
+				   is not needed if PMIC is older than PMI8994v2.0.
+- qcom,die-temp-vadc		: VADC channel source for flash LED. This property is not
+				  needed if PMIC is older than PMI8994v2.0.
+- qcom,die-temp-threshold       : Integer type array for PMIC die temperature threshold.
+				  Array should have at least one value. Values should be in
+				  celcius. This property is not needed if PMIC is older than
+				  PMI8994v2.0.
+- qcom,die-temp-derate-current	: Integer type arrray for PMIC die temperature derate
+				  current. Array should have at least one value. Values
+				  should be in mA. This property is not needed if PMIC is older
+				  than PMI8994v2.0.
+
+Required properties inside child node. Chile node contains settings for each individual LED.
+Each LED hardware needs a node for itself and a switch node to control brightness.
+For the purpose of turning on/off LED and better regulator control, "led:switch" node
+is introduced. "led:switch" acquires several existing properties from other nodes for
+operational simplification. For backward compatibility purpose, switch node can be optional:
+- label			: type of led that will be used, either "flash" or "torch".
+- qcom,led-name		: name of the LED. Accepted values are "led:flash_0",
+			  "led:flash_1", "led:torch_0", "led:torch_1"
+- qcom,default-led-trigger	: trigger for the camera flash and torch. Accepted values are
+				"flash0_trigger", "flash1_trigger", "torch0_trigger", torch1_trigger"
+- qcom,id		: enumerated ID for each physical LED. Accepted values are "0",
+			  "1", etc..
+- qcom,max-current	: maximum current allowed on this LED. Valid values should be
+			  integer from 0 to 1000 inclusive, indicating 0 to 1000 mA.
+- qcom,pmic-revid	: PMIC revision id source. This property is needed for PMI8996
+			revision check.
+
+Optional properties inside child node:
+- qcom,current		: default current intensity for LED. Accepted values should be
+			  integer from 0 t 1000 inclusive, indicating 0 to 1000 mA.
+- qcom,duration	: Duration for flash LED. When duration time expires, hardware will turn off
+		flash LED. Values should be from 10 ms to 1280 ms with 10 ms incremental
+		step. Not applicable to torch. It is required for LED:SWITCH node to handle
+		LED used as flash.
+- reg<n>	: reg<n> (<n> represents number. eg 0,1,2,..) property is to add support for
+		multiple power sources. It includes two properties regulator-name and max-voltage.
+		Required property inside regulator node:
+		- regulator-name	: This denotes this node is a regulator node and which
+					regulator to use.
+		Optional property inside regulator node:
+		- max-voltage		: This specifies max voltage of regulator. Some switch
+					or boost regulator does not need this property.
+
+Example:
+	qcom,leds@d300 {
+		compatible = "qcom,qpnp-flash-led";
+		status = "okay";
+		reg = <0xd300 0x100>;
+		label = "flash";
+		qcom,headroom = <500>;
+		qcom,startup-dly = <128>;
+		qcom,clamp-curr = <200>;
+		qcom,pmic-charger-support;
+		qcom,self-check-enabled;
+		qcom,thermal-derate-enabled;
+		qcom,thermal-derate-threshold = <80>;
+		qcom,thermal-derate-rate = "4_PERCENT";
+		qcom,current-ramp-enabled;
+		qcom,ramp_up_step = "27US";
+		qcom,ramp_dn_step = "27US";
+		qcom,vph-pwr-droop-enabled;
+		qcom,vph-pwr-droop-threshold = <3200>;
+		qcom,vph-pwr-droop-debounce-time = <10>;
+		qcom,headroom-sense-ch0-enabled;
+		qcom,headroom-sense-ch1-enabled;
+		qcom,die-current-derate-enabled;
+		qcom,die-temp-vadc = <&pmi8994_vadc>;
+		qcom,die-temp-threshold = <85 80 75 70 65>;
+		qcom,die-temp-derate-current = <400 800 1200 1600 2000>;
+		qcom,pmic-revid = <&pmi8994_revid>;
+
+		pm8226_flash0: qcom,flash_0 {
+			label = "flash";
+			qcom,led-name = "led:flash_0";
+			qcom,default-led-trigger =
+					"flash0_trigger";
+			qcom,max-current = <1000>;
+			qcom,id = <0>;
+			qcom,duration = <1280>;
+			qcom,current = <625>;
+		};
+
+		pm8226_torch: qcom,torch_0 {
+			label = "torch";
+			qcom,led-name = "led:torch_0";
+			qcom,default-led-trigger =
+					"torch0_trigger";
+			boost-supply = <&pm8226_chg_boost>;
+			qcom,max-current = <200>;
+			qcom,id = <0>;
+			qcom,current = <120>;
+			qcom,max-current = <200>;
+			reg0 {
+				regulator-name =
+					 "pm8226_chg_boost";
+				max-voltage = <3600000>;
+			};
+		};
+
+		pm8226_switch: qcom,switch {
+			lable = "switch";
+			qcom,led-name = "led:switch";
+			qcom,default-led-trigger =
+					"switch_trigger";
+			qcom,id = <2>;
+			qcom,current = <625>;
+			qcom,duration = <1280>;
+			qcom,max-current = <1000>;
+			reg0 {
+				regulator-name =
+					"pm8226_chg_boost";
+				max-voltage = <3600000>;
+			};
+		};
+	};
+
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
new file mode 100644
index 0000000..a77a291
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp-wled.txt
@@ -0,0 +1,121 @@
+Qualcomm Technologies QPNP WLED
+
+QPNP (Qualcomm Technologies Plug N Play) WLED (White Light
+Emitting Diode) driver is used for controlling display
+backlight that is part of PMIC on Qualcomm Technologies
+reference platforms. The PMIC is connected to the host
+processor via SPMI bus.
+
+Required properties:
+- compatible		: should be "qcom,qpnp-wled"
+- reg			: base address and size for wled modules
+- reg-names		: names associated with base addresses. It
+			should be "qpnp-wled-ctrl-base", "qpnp-wled-sink-base",
+			"qpnp-wled-ibb-base", "qpnp-wled-lab-base".
+- qcom,pmic-revid	: phandle of PMIC revid module. This is used to
+			  identify the PMIC subtype.
+
+Optional properties for WLED:
+ - interrupts		: Specifies the interrupts associated with WLED. The available
+			  interrupts are over voltage protection(ovp) and short circuit(sc).
+			  The values for ovp and sc are <0x3 0xd8 0x1> and <0x3 0xd8 0x2>.
+ - interrupt-names	: Specify the interrupt names associated with interrupts. Must be
+			  one of "ovp-irq" or "sc-irq"
+- linux,name		: name of the wled. default is "wled".
+- linux,default-trigger	: trigger for the backlight. default is NONE.
+- qcom,fdbk-output	: string feedback current output for wled module. The accepted values
+			  are "wled1", "wled2", "wled3", "wled4" and "auto". default is "auto".
+- qcom,vref-uv		: maximum reference voltage in uV.
+			  For pmi8994/8952/8996, supported values are from 300000 to 675000
+			  with a step size of 25000, the default value is 350000.
+			  For pmi8998/pm660l, supported values are from 60000 to 397500
+			  with a step size of 22500, the default value is 127500.
+- qcom,switch-freq-khz	: switch frequency in khz. default is 800.
+- qcom,ovp-mv		: Over voltage protection threshold in mV. Default is
+			  29500. Supported values are:
+			  - 31000, 29500, 19400, 17800 for pmi8994/8952/8996.
+			  - 31100, 29600, 19600, 18100 for pmi8998/pm660l.
+			  Should only be used if qcom,disp-type-amoled is not
+			  specified.
+- qcom,ilim-ma		: Current limit threshold in mA.
+			  For pmi8994/8952/8996, default value for LCD is 980mA
+			  and AMOLED is 385mA.
+			  Supported values are:
+			  - 105, 385, 660, 980, 1150, 1420, 1700, 1980.
+			  For pmi8998/pm660l, default value for LCD is
+			  970mA and AMOLED is 620mA.
+			  Supported values are:
+			  - 105, 280, 450, 620, 970, 1150, 1300, 1500.
+- qcom,boost-duty-ns	: maximum boost duty cycle in ns. default is 104.
+- qcom,mod-freq-khz	: modulation frequency in khz. default is 9600.
+- qcom,dim-mode		: dimming mode. supporting dimming modes are "analog",
+			  "digital", and "hybrid". default is "hybrid".
+- qcom,hyb-thres	: threshold value when used in hybrid mode. It represents the
+			  percentage of brightntess at which dimming mode is switched
+			  from "digital" to "analog". the default value is 6.25%. as the
+			  floating point cannot be represented directly, the value is
+			  multiplied by 100. so the default is 625.
+- qcom,sync-dly-us	: delay for current sync in us. default is 400.
+- qcom,fs-curr-ua	: maximum full scale current in ua. default is 25000.
+- qcom,en-9b-dim-res	: boolean, specify if 9-bit dim resultion is needed. otherwise 12-bit is used.
+- qcom,en-phase-stag	: boolean, specify if phase staggering is needed.
+- qcom,en-cabc		: boolean, specify if cabc (content adaptive backlight control) is needed.
+- qcom,disp-type-amoled	: specify if the display is amoled
+- qcom,led-strings-list	: Wled module has four strings of leds numbered from 0 to 3. each string of leds
+			  are operated individually. specify the list of strings used by the device.
+			  any combination of led strings can be used. default value is [00 01 02 03]
+- qcom,en-ext-pfet-sc-pro : Specify if external pfet short circuit protection is needed
+- qcom,cons-sync-write-delay-us : Specify in 'us' the duration of delay between two consecutive writes to
+				  SYNC register.
+- qcom,sc-deb-cycles	: debounce time for short circuit detection
+- qcom,loop-ea-gm	: control the gm for gm stage in control loop. default is 3.
+- qcom,loop-auto-gm-en	: A boolean property to specify if auto gm is enabled.
+- qcom,loop-auto-gm-thresh	: Specify auto gm threshold if "loop-auto-gm-en" is defined.
+				  Supported values are: 0 - 3.
+- qcom,lcd-auto-pfm-thresh	: Specify the auto-pfm threshold, if the headroom voltage level
+				  falls below this threshold and auto PFM is enabled, boost
+				  controller will enter into PFM mode automatically.
+
+Optional properties if 'qcom,disp-type-amoled' is mentioned in DT:
+- qcom,loop-comp-res-kohm	: control to select the compensation resistor in kohm. default is 320.
+- qcom,vref-psm-mv	: reference psm voltage in mv. default for amoled is 450.
+- qcom,avdd-mode-spmi: Boolean property to enable AMOLED_VOUT programming via SPMI. If not specified,
+			AMOLED_VOUT is programmed via S-wire. This can be specified only for newer
+			PMICs like pmi8998/pm660l.
+- qcom,avdd-target-voltage-mv: The voltage required for AMOLED_VOUT. Accepted values are in the range
+				of 5650 to 7900 in steps of 150. Default value is 7600. Unit is in mV.
+				For old revisions, accepted values are: 7900, 7600, 7300, 6400, 6100,
+				5800.
+
+Example:
+	qcom,leds@d800 {
+		compatible = "qcom,qpnp-wled";
+		reg = <0xd800 0x100>,
+			<0xd900 0x100>,
+			<0xdc00 0x100>,
+			<0xde00 0x100>;
+		reg-names = "qpnp-wled-ctrl-base",
+				"qpnp-wled-sink-base",
+				"qpnp-wled-ibb-base",
+				"qpnp-wled-lab-base";
+		interrupts = <0x3 0xd8 0x2>;
+		interrupt-names = "sc-irq";
+		status = "okay";
+		linux,name = "wled";
+		linux,default-trigger = "bkl-trigger";
+		qcom,fdbk-output = "auto";
+		qcom,vref-uv = <350000>;
+		qcom,switch-freq-khz = <800>;
+		qcom,ovp-mv = <29500>;
+		qcom,ilim-ma = <980>;
+		qcom,boost-duty-ns = <26>;
+		qcom,mod-freq-khz = <9600>;
+		qcom,dim-mode = "hybrid";
+		qcom,dim-method = "linear";
+		qcom,hyb-thres = <625>;
+		qcom,sync-dly-us = <800>;
+		qcom,fs-curr-ua = <16000>;
+		qcom,en-phase-stag;
+		qcom,led-strings-list = [00 01 02 03];
+		qcom,en-ext-pfet-sc-pro;
+	};
diff --git a/Documentation/devicetree/bindings/leds/leds-qpnp.txt b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
new file mode 100644
index 0000000..85e0975
--- /dev/null
+++ b/Documentation/devicetree/bindings/leds/leds-qpnp.txt
@@ -0,0 +1,358 @@
+Qualcomm Technologies, Inc. QPNP LEDs
+
+Qualcomm Technologies, Inc. Plug N Play (QPNP) LED modules
+are used for controlling LEDs that are connected to a QPNP PMIC.
+The PMIC is connected to a host processor via the SPMI bus.  Various
+LED modules are supported such as Keypad backlight, WLED (white LED),
+RGB LED and flash LED.
+
+Each LED module is represented as a node of "leds-qpnp". This
+node will further contain the type of LED supported and its
+properties.  At least one child node is required for each LED
+module.  Each must have the required properties below, in addition
+to the properties for the LED type, WLED, Flash, RGB and MPP.
+
+Required properties for each child node, WLED, Flash and RGB:
+- compatible		: should be "qcom,leds-qpnp"
+- qcom,id		: must be one of values supported in enum qpnp_led
+- label			: type of led that will be used, ie "wled"
+- qcom,max-current	: maximum current that the LED can sustain in mA
+- linux,name		: name of the led that is used in led framework
+
+Optional properties for each child node, WLED, Flash, MPP, RGB and KPDBL:
+- qcom,in-order-command-processing : specify if user space requests leds in order
+
+WLED is primarily used as display backlight. Display subsystem uses
+LED triggers for WLED to control the brightness as needed.
+
+Optional properties for WLED:
+- qcom,num-strings: number of wled strings to be configured
+- qcom,num-physical-strings: number of physical wled strings supported
+- qcom,ovp-val: over voltage protection threshold,
+		follows enum wled_ovp_threshold
+- qcom,boost-curr-lim: boot currnet limit, follows enum wled_current_bost_limit
+- qcom,ctrl-delay-us: delay in activation of led
+- qcom,dig-mod-gen-en: digital module generator
+- qcom,cs-out-en: current sink output enable
+- qcom,op-fdbck: selection of output as feedback for the boost, 00 = automatic selection, 01 = select LED1 output, 02 = select LED2 output, 03 = select LED3 output
+- qcom,cp-select: high pole capacitance
+- linux,default-trigger: trigger the led from external modules such as display
+- qcom,default-state:  default state of the led, should be "on" or "off"
+
+Flash is used primarily as a camera or video flash.
+
+Optional properties for flash:
+- qcom,headroom: headroom to use. Values should be 0, 1, 2, 3 for 250mV, 300mV, 400mV and 500mV
+- qcom,duration: duration of the flash and torch, 10ms - 1280ms for flash and 2s - 33s for torch
+- qcom,clamp-curr: current to clamp at, mA
+- qcom,startup-dly: delay before flashing after flash executed. Values should 0, 1, 2, 3 for 10us, 32us, 64us, and 128us
+- qcom,saftey-timer: include for safety timer use, otherwise watchdog timer will be used
+- linux,default-trigger: trigger the led from external modules such as display
+- qcom,default-state:  default state of the led, should be "on" or "off"
+- qcom,torch-enable: set flash led to torch mode functionality and triggers software workaround for torch if hardware does not support
+- qcom,sw_vreg_ok: Specify if software strobe is used to inform the readiness of flash module to fire the flash LED when there is no smbb support
+- qcom,no-smbb-support: Specify if smbb boost is not required and there is a single regulator for both flash and torch.
+- flash-boost-supply: SMBB regulator for LED flash mode
+- torch-boost-supply: SMBB regulator for LED torch mode
+- flash-wa-supply: SMBB regulator for flash workarounds.
+
+RGB Led is a tri-colored led, Red, Blue & Green.
+
+Required properties for RGB led:
+- qcom,mode: mode the led should operate in, options "pwm" and "lpg". "manual" mode is not supported for RGB led.
+
+Required properties for PWM mode only:
+- pwms: Use the phandle of pwm device
+- qcom,pwm-us: time the pwm device will modulate at (us)
+
+Required properties for LPG mode only:
+- pwms: Use the phandle of pwm device
+- qcom,pwm-us: time the pwm device will modulate at (us)
+- qcom,duty-pcts: array of values for duty cycle to go through
+- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for LPG mode only:
+- qcom,pause-lo: pause at low end of cycle
+- qcom,pause-hi: pause at high end of cycle
+- qcom,ramp-step-ms: step between each cycle (ms)
+- qcom,lut-flags: flags to be used in lut configuration
+
+Optional properties for RGB led:
+- linux,default-trigger: trigger the led from external modules such as display
+- qcom,default-state:  default state of the led, should be "on" or "off"
+- qcom,turn-off-delay-ms: delay in millisecond for turning off the led when its default-state is "on". Value is being ignored in case default-state is "off".
+- qcom,use-blink: Use blink sysfs entry for switching into lpg mode.  For optimal use, set default mode to pwm.  All required lpg parameters must be supplied.
+
+MPP LED is an LED controlled through a Multi Purpose Pin.
+
+Optional properties for MPP LED:
+- linux,default-trigger: trigger the led from external modules such as display
+- qcom,default-state: default state of the led, should be "on" or "off"
+- qcom,source-sel: select power source, default 1 (enabled)
+- qcom,mode-ctrl: select operation mode, default 0x60 = Mode Sink
+- qcom,mode: mode the led should operate in, options "pwm", "lpg" and "manual"
+- qcom,vin-ctrl: select input source, supported values are 0 to 3
+- qcom,use-blink: Use blink sysfs entry for switching into lpg mode.  For optimal use, set default mode to pwm.  All required lpg parameters must be supplied.
+- qcom,min-brightness - Lowest possible brightness supported on this LED other than 0.
+- qcom,current-setting: default current value for wled used as button backlight in mA
+- mpp-power-supply: regulator support for MPP LED
+- qcom,mpp-power-max-voltage - maximum voltage for MPP LED regulator. This should not be specified when no regulator is in use.
+- qcom,mpp-power-min-voltage - minimum voltage for MPP LED regulator. This should not be specified when no regulator is in use.
+
+Required properties for PWM mode only:
+- pwms: Use the phandle of pwm device
+- qcom,pwm-us: time the pwm device will modulate at (us)
+
+Required properties for LPG mode only:
+- pwms: Use the phandle of pwm device
+- qcom,pwm-us: time the pwm device will modulate at (us)
+- qcom,duty-pcts: array of values for duty cycle to go through
+- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for LPG mode only:
+- qcom,pause-lo: pause at low end of cycle
+- qcom,pause-hi: pause at high end of cycle
+- qcom,ramp-step-ms: step between each cycle (ms)
+- qcom,lut-flags: flags to be used in lut configuration
+
+Keypad backlight is a backlight source for buttons. It supports four rows
+and the required rows are enabled by specifying values in the properties.
+
+Required properties for keypad backlight:
+- qcom,mode: mode the led should operate in, options "pwm" and "lpg". "manual" mode is not supported for keypad backlight.
+- qcom,row-id: specify the id of the row. Supported values are 0 to 3.
+
+Optional properties for keypad backlight:
+- qcom,row-src-vbst: select source for rows. Specify for vbst and ignore it
+			for vph_pwr.
+- qcom,row-src-en: specify to enable row source
+- qcom,always-on: specify if the module has to be always on
+- qcom,use-blink: Use blink sysfs entry for switching into lpg mode.  For optimal use, set default mode to pwm.  All required lpg parameters must be supplied.
+
+Required properties for PWM mode only:
+- pwms: Use the phandle of pwm device
+- qcom,pwm-us: time the pwm device will modulate at (us)
+
+Required properties for LPG mode only:
+- pwms: Use the phandle of pwm device
+- qcom,pwm-us: time the pwm device will modulate at (us)
+- qcom,duty-pcts: array of values for duty cycle to go through
+- qcom,start-idx: starting point duty-pcts array
+
+Optional properties for LPG mode only:
+- qcom,pause-lo: pause at low end of cycle
+- qcom,pause-hi: pause at high end of cycle
+- qcom,ramp-step-ms: step between each cycle (ms)
+- qcom,lut-flags: flags to be used in lut configuration
+
+GPIO LED is an LED controlled through a PMIC GPIO.
+
+Optional properties for GPIO LED:
+- linux,default-trigger: trigger the led from external modules such as charging
+- qcom,default-state: default state of the led, should be "on" or "off"
+- qcom,turn-off-delay-ms: delay in millisecond for turning off the led when its default-state is "on". Value is being ignored in case default-state is "off".
+- qcom,source-sel: select power source, default 1 (enabled)
+- qcom,mode-ctrl: select operation mode, default 0x60 = Mode Sink
+- qcom,vin-ctrl: select input source, supported values are 0 to 7
+
+Example:
+
+	qcom,leds@a100 {
+		status = "okay";
+		qcom,led_mpp_2 {
+			label = "mpp";
+			linux,name = "button-backlight";
+			linux,default-trigger = "hr-trigger";
+			qcom,default-state = "off";
+			qcom,current-setting = <20>;
+			qcom,max-current = <40>;
+			qcom,id = <6>;
+			qcom,source-sel = <1>;
+			qcom,mode-ctrl = <0x61>;
+			qcom,mode = "manual";
+		};
+	};
+
+	qcom,leds@a200 {
+		status = "okay";
+		qcom,led_mpp_3 {
+			label = "mpp";
+			linux,name = "wled-backlight";
+			linux-default-trigger = "none";
+			qcom,default-state = "on";
+			qcom,max-current = <40>;
+			qcom,id = <6>;
+			qcom,source-sel = <1>;
+			qcom,mode-ctrl = <0x10>;
+			qcom,vin-ctrl = <0x03>;
+			qcom,min-brightness = <20>;
+		};
+	};
+
+	qcom,leds@a300 {
+		status = "okay";
+		qcom,led_mpp_pwm {
+			label = "mpp";
+			linux,name = "green";
+			linux,default-trigger = "none";
+			qcom,default-state = "off";
+			qcom,max-current = <40>;
+			qcom,current-setting = <5>;
+			qcom,id = <6>;
+			qcom,mode = "pwm";
+			qcom,source-sel = <8>;
+			qcom,mode-ctrl = <0x60>;
+			pwms = <&pm8941_pwm_1 0 0>;
+			qcom,pwm-us = <1000>;
+		};
+	};
+
+	qcom,leds@d000 {
+		status = "okay";
+		qcom,rgb_pwm {
+			label = "rgb";
+			linux,name = "led:rgb_red";
+			qcom,mode = "pwm";
+			qcom,pwm-us = <1000>;
+			pwms = <&pm8941_pwm_7 0 0>;
+			qcom,max-current = <12>;
+			qcom,default-state = "off";
+			qcom,id = <3>;
+			linux,default-trigger =
+				"battery-charging";
+		};
+		qcom,rgb_lpg {
+			label = "rgb";
+			linux,name = "led:rgb_green";
+			qcom,mode = "lpg";
+			pwms = <&pm8941_pwm_6 0 0>;
+			qcom,pwm-us = <1000>;
+			qcom,duty-ms = <20>;
+			qcom,start-idx = <1>;
+			qcom,idx-len = <10>;
+			qcom,duty-pcts = [00 19 32 4B 64
+					 64 4B 32 19 00];
+			qcom,max-current = <12>;
+			qcom,default-state = "off";
+			qcom,id = <3>;
+			linux,default-trigger =
+				"battery-charging";
+		};
+
+		qcom,rgb_blink {
+			label = "rgb";
+			linux,name = "led:rgb_blue";
+			qcom,mode = "pwm";
+			pwms = <&pm8941_pwm_5 0 0>;
+			qcom,start-idx = <1>;
+			qcom,idx-len = <10>;
+			qcom,duty-pcts = [00 19 32 4B 64
+					 64 4B 32 19 00];
+			qcom,lut-flags = <3>;
+			qcom,pause-lo = <0>;
+			qcom,pause-hi = <0>;
+			qcom,ramp-step-ms = <255>;
+			qcom,max-current = <12>;
+			qcom,default-state = "on";
+			qcom,turn-off-delay-ms = <500>;
+			qcom,id = <5>;
+			linux,default-trigger = "none";
+			qcom,pwm-us = <1000>;
+			qcom,use-blink;
+		};
+	};
+
+	qcom,leds@d300 {
+			compatible = "qcom,leds-qpnp";
+			status = "okay";
+			flash-boost-supply = <&pm8941_chg_boost>;
+			torch-boost-supply = <&pm8941_boost>;
+			qcom,flash_0 {
+				qcom,max-current = <1000>;
+				qcom,default-state = "off";
+				qcom,headroom = <0>;
+				qcom,duration = <200>;
+				qcom,clamp-curr = <200>;
+				qcom,startup-dly = <1>;
+				qcom,safety-timer;
+				label = "flash";
+				linux,default-trigger =
+					"flash0_trigger";
+				linux,name = "led:flash_0";
+				qcom,current = <625>;
+				qcom,id = <1>;
+				qcom,no-torch-module;
+			};
+	};
+
+	qcom,leds@d800 {
+			compatible = "qcom,leds-qpnp";
+			status = "okay";
+			qcom,wled_0 {
+				linux,default-trigger = "bkl-trigger"
+				label = "wled";
+				qcom,cs-out-en;
+				qcom,op-fdbck = <1>;
+				qcom,default-state "off";
+				qcom,max-current = <25>;
+				qcom,ctrl-delay-us = <0>;
+				qcom,boost-curr-lim = <3>;
+				qcom,cp-sel = <0>;
+				qcom,switch-freq = <2>;
+				qcom,ovp-val = <2>;
+				qcom,num-strings = <1>;
+				qcom,id = <0>;
+				linux,name = "led:wled_backlight";
+			};
+	};
+
+	qcom,leds@e200 {
+		status = "okay";
+
+		qcom,kpdbl1 {
+			label = "kpdbl";
+			linux,name = "kpdbl-pwm-1";
+			qcom,mode = <0>;
+			pwms = <&pm8941_pwm_9 0 0>;
+			qcom,pwm-us = <1000>;
+			qcom,id = <7>;
+			qcom,max-current = <20>;
+			qcom,row-id = <0>;
+			qcom,row-src-en;
+			qcom,always-on;
+		};
+
+		qcom,kpdbl2 {
+			label = "kpdbl";
+			linux,name = "kpdbl-lut-2";
+			qcom,mode = <1>;
+			pwms = <&pm8941_pwm_10 0 0>;
+			qcom,pwm-us = <1000>;
+			qcom,start-idx = <1>;
+			qcom,duty-pcts = [00 00 00 00 64
+					64 00 00 00 00];
+			qcom,id = <7>;
+			qcom,max-current = <20>;
+			qcom,row-id = <1>;
+			qcom,row-src-en;
+		};
+
+	};
+
+	qcom,leds@c900 {
+		compatible = "qcom,leds-qpnp";
+		reg = <0xc900 0x100>;
+		status = "okay";
+		qcom,led_gpio_10 {
+			label = "gpio";
+			linux,name = "led:notification";
+			qcom,max-current = <40>;
+			qcom,id = <8>;
+			linux,default-trigger = "notification";
+			qcom,default-state = "on";
+			qcom,turn-off-delay-ms = <1000>;
+			qcom,source-sel = <1>;
+			qcom,mode-ctrl = <0x10>;
+			qcom,vin-ctrl = <0x02>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/media/video/msm-camera.txt b/Documentation/devicetree/bindings/media/video/msm-camera.txt
new file mode 100644
index 0000000..04548ca
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-camera.txt
@@ -0,0 +1,13 @@
+* Qualcomm Technologies, Inc. MSM Camera
+
+Required properties:
+- compatible :
+    - "qcom,cam-req-mgr"
+- qcom,sensor-manual-probe : specify if sensor probes at kernel boot time or user driven
+
+Example:
+
+   qcom,cam-req-mgr {
+       compatible = "qcom,cam-req-mgr";
+       qcom,sensor-manual-probe;
+   };
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc-bus.txt b/Documentation/devicetree/bindings/media/video/msm-vidc-bus.txt
new file mode 100644
index 0000000..1d4056f
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc-bus.txt
@@ -0,0 +1,49 @@
+* Qualcomm Technologies Inc MSM VIDC BUS
+
+Required properties:
+- compatible : "qcom,msm-vidc,governor,table"
+- name : name of the governor.
+- qcom,bus-table : node containing individual domain nodes, each with:
+  - qcom,codec-mask: a bitmap of supported codec types, every two bits
+    represents a codec type.
+  - qcom,load-busfreq-tbl: load (in macroblocks/sec) and the corresponding
+    bus frequency (in KBps) table.
+
+Optional properties:
+- qcom,low-power-mode: a boolean which indicates whether bus profile need
+  to be used when client enables low-power mode.
+- qcom,ubwc-mode: a boolean which indicates whether the bus profile need
+  to be used when client enables UBWC mode.
+
+Example:
+
+venus-bus-gov {
+	compatible = "qcom,msm-vidc,governor,table";
+	name = "qcom,venus-gov";
+	qcom,bus-freq-table {
+		qcom,profile-dec {
+			qcom,codec-mask = <0xffffffff>;
+			qcom,ubwc-mode;
+			qcom,load-busfreq-tbl =
+				<489600 1205248>,  /* 1080p60D   */
+				<244800 618496>,   /* 1080p30D   */
+				<216000 618496>,   /* 720p60D    */
+				<108000 314368>,   /* 720p30D    */
+				<72000  233472>,   /* VGA60D     */
+				<36000  118784>,   /* VGA30D     */
+				<0      0>;
+		};
+		qcom,profile-enc {
+			qcom,codec-mask = <0x55555555>;
+			qcom,low-power-mode;
+			qcom,load-busfreq-tbl =
+				<244800 787456>,   /* 1080p30E   */
+				<216000 350208>,   /* 720p60E    */
+				<108000 350208>,   /* 720p30E    */
+				<72000  350208>,   /* VGA60E     */
+				<36000  136806>,   /* VGA30E     */
+				<0      0>;
+		};
+	};
+};
+
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc-vmem.txt b/Documentation/devicetree/bindings/media/video/msm-vidc-vmem.txt
new file mode 100644
index 0000000..84a8765
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc-vmem.txt
@@ -0,0 +1,42 @@
+* Qualcomm Technologies Inc MSM VIDC VMEM
+
+Required properties:
+- compatible : "qcom,msm-vmem".
+- interrupts : Contains the interrupt that maps to the VMEM module.
+- reg : A set of 2 start address and size pairs that describe the hardware
+register address space and mappable memory address space.
+- reg-names : Strings that describe the pairs in "reg".  The register address
+space should be called "reg-base" and the memory space should be called "mem-base".
+- clocks : A set of clocks that correspond to the AHB and MAXI clocks that the
+hardware uses.
+- clock-names : A string that describes the "clocks" property.  The AHB clock
+should be named "ahb" and the MAXI clock should be named "maxi".
+- qcom,bank-size : The size of each memory bank, in bytes.
+- vdd-supply: phandle to a regulator that is considered to be the footswitch for vmem.
+- qcom,msm-bus,(name|num-cases,num-paths,vectors-KBps) - Bus to be voted for prior to
+  issuing any IO transactions to vmem.  Refer to Documentation/devicetree/bindings/arm/\
+  msm/msm_bus_adhoc.txt for further details.
+
+Example:
+
+qcom,vmem@880000 {
+	compatible = "qcom,msm-vmem";
+	interrupts = <0 429 0>;
+	reg = <0x880000 0x800>,
+	    <0x6800000 0x100000>;
+	reg-names = "reg-base", "mem-base";
+
+	vdd-supply = <&gdsc_mmagic_video>;
+	clocks = <&clock_mmss clk_vmem_ahb_clk>,
+	       <&clock_mmss clk_vmem_maxi_clk>;
+	clock-names = "ahb", "maxi";
+
+	qcom,bank-size = <131072>;
+
+	qcom,msm-bus,name = "vmem";
+	qcom,msm-bus,num-cases = <2>;
+	qcom,msm-bus,num-paths = <1>;
+	qcom,msm-bus,vectors-KBps =
+	        <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_VMEM_CFG   0   0>,
+	        <MSM_BUS_MASTER_AMPSS_M0 MSM_BUS_SLAVE_VMEM_CFG 500 800>;
+};
diff --git a/Documentation/devicetree/bindings/media/video/msm-vidc.txt b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
new file mode 100644
index 0000000..ce5334d
--- /dev/null
+++ b/Documentation/devicetree/bindings/media/video/msm-vidc.txt
@@ -0,0 +1,289 @@
+* Qualcomm Technologies, Inc. MSM VIDC
+
+[Root level node]
+Venus
+=====
+Required properties:
+- compatible : one of:
+	- "qcom,msm-vidc"
+- qcom,max-hw-load: The maximum load the hardware can support expressed in units
+  of macroblocks per second. The load is a reflection of hardware capability
+  rather than a performance guarantee. Performance is guaranteed only up to
+  advertised capability of the chipset.
+- qcom,firmware-name : firmware file name to be downloaded.
+
+Optional properties:
+- reg : offset and length of the register set for the device.
+- interrupts : should contain the vidc interrupt.
+- qcom,platform-version : mask and shift of the platform version bits
+    in efuse register.
+- qcom,load-freq-tbl : load (in macroblocks/sec) and corresponding vcodec
+  clock required along with codec's config, which is a bitmap that describes
+  what the clock is used for. The bitmaps are as follows:
+    supports mvc encoder = 0x00000001
+    supports mvc decoder = 0x00000003
+    supports h264 encoder = 0x00000004
+    supports h264 decoder = 0x0000000c
+    supports h263 encoder = 0x00000010
+    supports h263 decoder = 0x00000030
+    supports mpeg1 encoder = 0x00000040
+    supports mpeg1 decoder = 0x000000c0
+    supports mpeg2 encoder = 0x00000100
+    supports mpeg2 decoder = 0x00000300
+    supports mpeg4 encoder = 0x00000400
+    supports mpeg4 decoder = 0x00000c00
+    supports divx_311 encoder = 0x00001000
+    supports divx_311 decoder = 0x00003000
+    supports divx encoder = 0x00004000
+    supports divx decoder = 0x0000c000
+    supports vc1 encoder = 0x00010000
+    supports vc1 decoder = 0x00030000
+    supports spark encoder = 0x00040000
+    supports spark decoder = 0x000c0000
+    supports vp6 encoder = 0x00100000
+    supports vp6 decoder = 0x00300000
+    supports vp7 encoder = 0x00400000
+    supports vp7 decoder = 0x00c00000
+    supports vp8 encoder = 0x01000000
+    supports vp8 decoder = 0x03000000
+    supports hevc encoder = 0x04000000
+    supports hevc decoder = 0x0c000000
+    supports hevc_hybrid encoder = 0x10000000
+    supports hevc_hybrid decoder = 0x30000000
+- qcom,dcvs-tbl : specifies the parameter to configure DCVS algorithm. Video
+  instance load (in mbs/sec) and corresponding low and high threshold DCVS
+  load for supported codec formats.
+- qcom,dcvs-limit : specifies the minimum specifications required to kick in
+  DCVS algorithm. Min MBs per frame and the fps for encoder and decoder to
+  kick in DCVS.
+- qcom,imem-ab-tbl : video core frequency in Hz and corresponding imem ab value
+  in kbps. The imem ab value corresponds to the clock frequency at which imem
+  should operate for a particular video core frequency.
+- qcom,reg-presets : list of offset-value pairs for registers to be written.
+  The offsets are from the base offset specified in 'reg'. This is mainly
+  used for QoS, VBIF, etc. presets for video.
+- qcom,qdss-presets : list of physical address and memory allocation size pairs.
+  when fw_debug_mode is set as HFI_DEBUG_MODE_QDSS, all firmware messages will be
+  written to QDSS memory.
+- qcom,imem-size: imem size required by hardware for optimum performance.
+  If imem is not present then there is no need to specify this property.
+- *-supply: A phandle pointing to the appropriate regulator. Number of
+  regulators vary across targets.
+- clock-names: an array of clocks that the driver is supposed to be
+  manipulating. The clocks names here correspond to the clock names used in
+  clk_get(<name>).
+- qcom,clock-configs = an array of bitmaps of clocks' configurations. The index
+  of the bitmap corresponds to the clock at the same index in qcom,clock-names.
+  The bitmaps describes the actions that the device needs to take regarding the
+  clock (i.e. scale it based on load).
+
+  The bitmap is defined as:
+  scalable = 0x1 (if the driver should vary the clock's frequency based on load)
+- qcom,allowed-clock-rates = an array of supported clock rates by the chipset.
+- qcom,clock-freq-tbl = node containing individual domain nodes, each with:
+     - qcom,codec-mask: a bitmap of supported codec types, every two bits
+       represents a codec type.
+     - qcom,cycles-per-mb: number of cycles required to process each macro
+       block.
+     - qcom,low-power-mode-factor: the factor which needs to be multiple with
+       the required frequency to get the final frequency, the factor is
+       represented in Q16 format.
+- qcom,sw-power-collapse = A bool indicating if video hardware core can be
+  power collapsed in idle state.
+- qcom,slave-side-cp = A bool indicating the content protection mode for an
+  ongoing video session. It is true for targets where the mode is slave side.
+- qcom,never-unload-fw = A bool indicating if video firmware should be not be
+  unloaded after all active sessions have closed.  Once a new session starts up
+  after this, the firmware will be ready to go.  This should be set on platforms
+  that desire low-latency video startup and don't mind "leakage" of some memory.
+- qcom,use-non-secure-pil = A bool indicating which type of pil to use to load
+  the fw.
+- qcom,fw-bias = The address at which venus fw is loaded (manually).
+- qcom,enable-idle-indicator = A bool to enable video hardware to generate
+  idle message when it is in idle state.
+- qcom,enable-thermal-mitigation = A bool to enable thermal mitigation when
+  thermal run away occurs.
+- qcom,hfi-version = The hfi packetization version supported by venus firmware.
+  If hfi version is not specified, then packetization type will default to
+  legacy.
+- qcom,vidc-iommu-domains = node containing individual domain nodes, each with:
+     - a unique domain name for the domain node (e.g vidc,domain-ns)
+     - qcom,vidc-domain-phandle: phandle for the domain as defined in
+       <target>-iommu-domains.dtsi (e.g msm8974-v1-iommu-domains.dtsi)
+     - qcom,vidc-buffer-types: bitmap of buffer types that can be mapped into each
+       IOMMU domain.
+       - Buffer types are defined as the following:
+           input = 0x1
+           output = 0x2
+           output2 = 0x4
+           extradata input = 0x8
+           extradata output = 0x10
+           extradata output2 = 0x20
+           internal scratch = 0x40
+           internal scratch1 = 0x80
+           internal scratch2 = 0x100
+           internal persist = 0x200
+           internal persist1 = 0x400
+           internal cmd queue = 0x800
+- qcom,pm-qos-latency-us = The latency used to vote for QOS power manager. This
+value is typically max(latencies of every cluster at all power levels) + 1
+- qcom,max-secure-instances = An int containing max number of concurrent secure
+  instances supported, accounting for venus and system wide limitations like
+  memory, performance etc.
+- qcom,debug-timeout = A bool indicating that FW errors such as SYS_ERROR,
+  SESSION_ERROR and timeouts will be treated as Fatal.
+
+[Second level nodes]
+Context Banks
+=============
+Required properties:
+- compatible : one of:
+	- "qcom,msm-vidc,context-bank"
+- iommus : A phandle parsed by smmu driver. Number of entries will vary
+  across targets.
+
+Optional properties:
+- label - string describing iommu domain usage.
+- buffer-types : bitmap of buffer types that can be mapped into the current
+	IOMMU domain.
+        - Buffer types are defined as the following:
+          input = 0x1
+          output = 0x2
+          output2 = 0x4
+          extradata input = 0x8
+          extradata output = 0x10
+          extradata output2 = 0x20
+          internal scratch = 0x40
+          internal scratch1 = 0x80
+          internal scratch2 = 0x100
+          internal persist = 0x200
+          internal persist1 = 0x400
+          internal cmd queue = 0x800
+- virtual-addr-pool : offset and length of virtual address pool.
+- qcom,fw-context-bank : bool indicating firmware context bank.
+- qcom,secure-context-bank : bool indicating secure context bank.
+
+Buses
+=====
+Required properties:
+- compatible : one of:
+	- "qcom,msm-vidc,bus"
+- label : an arbitrary name
+- qcom,bus-master : an integer descriptor of the bus master. Refer to arch/arm/\
+  boot/dts/include/dt-bindings/msm/msm-bus-ids.h for list of acceptable masters
+- qcom,bus-slave : an integer descriptor of the bus slave. Refer to arch/arm/\
+  boot/dts/include/dt-bindings/msm/msm-bus-ids.h for list of acceptable slaves
+
+Optional properties:
+- qcom,bus-governor : governor to use when scaling bus, generally any commonly
+  found devfreq governor might be used.  In addition to those governors, the
+  custom Venus governors, "msm-vidc-ddr" or "msm-vidc-vmem" are also
+  acceptable values.
+  In the absence of this property the "performance" governor is used.
+- qcom,bus-rage-kbps : an array of two items (<min max>) that indicate the
+  minimum and maximum acceptable votes for the bus.
+  In the absence of this property <0 INT_MAX> is used.
+- qcom,ubwc-10bit : UBWC 10 bit content has different bus requirements,
+  this tag will be used to pick the appropriate bus as per the session profile
+  as shown below in example.
+
+Example:
+
+
+	qcom,vidc@fdc00000 {
+		compatible = "qcom,msm-vidc";
+		reg = <0xfdc00000 0xff000>;
+		interrupts = <0 44 0>;
+		venus-supply = <&gdsc>;
+		venus-core0-supply = <&gdsc1>;
+		venus-core1-supply = <&gdsc2>;
+		qcom,load-freq-tbl =
+			<489600 266670000 0x030fcfff>, /* Legacy decoder 1080p 60fps  */
+			<108000 133330000 0x030fcfff>, /* Legacy decoder 720p 30fps   */
+			<108000 200000000 0x01000414>, /* Legacy encoder 720p 30fps   */
+			<72000 133330000 0x0c000000>, /* HEVC decoder VGA 60fps   */
+			<36000 133330000 0x0c000000>, /* HEVC VGA 30 fps  */
+			<36000 133330000 0x01000414>; /* Legacy encoder VGA 30 fps   */
+		qcom,dcvs-tbl =
+			<972000 972000 19944000 0xffffffff>, /* Legacy decoder UHD 30fps */
+			<489600 489600   972000 0xffffffff>, /* Legacy decoder 1080p 60fps */
+			<244800 244800   489600 0xffffffff>, /* Legacy decoder 1080p 30fps */
+			<829440 489600   972000 0x55555555>; /* Legacy encoder DCI 24fps
+		qcom,dcvs-limit =
+			<32400 30>, /* Encoder UHD */
+			<14400 30>; /* Decoder WQHD */
+		qcom,imem-ab-tbl =
+			<75000000  1500000>, /* imem clk @ 75 Mhz  */
+			<150000000 1500000>, /* imem clk @ 75 Mhz  */
+			<355333333 2500000>, /* imem clk @ 170 Mhz */
+			<533000000 6000000>; /* imem clk @ 320 Mhz */
+
+		qcom,imem-size = <4096>;
+		qcom,firmware-name = "venus";
+		qcom,hfi-version = "3xx";
+		qcom,reg-presets = <0x80004 0x1>,
+			<0x80178 0x00001FFF>;
+		qcom,qdss-presets = <0xFC307000 0x1000>,
+			<0xFC322000 0x1000>;
+		qcom,max-hw-load = <1224450>; /* 4k @ 30 + 1080p @ 30*/
+		qcom,never-unload-fw;
+		clock-names = "foo_clk", "bar_clk", "baz_clk";
+		qcom,clock-configs = <0x3 0x1 0x0>;
+		qcom,sw-power-collapse;
+		qcom,enable-idle-indicator;
+		qcom,buffer-type-tz-usage-table = <0x1 0x1>,
+						<0x1fe 0x2>;
+		qcom,enable-thermal-mitigation;
+		qcom,use-non-secure-pil;
+		qcom,slave-side-cp;
+		qcom,use_dynamic_bw_update;
+		qcom,fw-bias = <0xe000000>;
+		msm_vidc_cb1: msm_vidc_cb1 {
+			compatible = "qcom,msm-vidc,context-bank";
+			label = "venus_ns";
+			iommus = <&venus_smmu 0x0a>,
+				<&venus_smmu 0x0b>,
+				<&venus_smmu 0x0c>;
+			buffer-types = <0xfff>;
+			virtual-addr-pool = <0x5dc00000 0x80000000>;
+			qcom,secure-context-bank;
+		};
+
+		msm_vidc_cb2: msm_vidc_cb2 {
+			compatible = "qcom,msm-vidc,context-bank";
+			qcom,fw-context-bank;
+			iommus = <&venus_smmu 0x100>,
+				<&venus_smmu 0x106>;
+		};
+
+		bus_cnoc {
+			compatible = "qcom,msm-vidc,bus";
+			label = "venus-cnoc";
+			qcom,bus-master = <MSM_BUS_MASTER_AMPSS_M0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_VENUS_CFG>;
+			qcom,bus-governor = "performance";
+			qcom,bus-range-kbps = <1 1>;
+		};
+
+		venus_bus_ddr {
+			compatible = "qcom,msm-vidc,bus";
+			label = "venus-ddr";
+			qcom,bus-master = <MSM_BUS_MASTER_VIDEO_P0>;
+			qcom,bus-slave = <MSM_BUS_SLAVE_EBI_CH0>;
+			qcom,bus-governor = "msm-vidc-ddr";
+			qcom,bus-range-kbps = <1000 3388000>;
+		};
+		qcom,profile-dec-ubwc-10bit {
+			qcom,codec-mask = <0xffffffff>;
+			qcom,ubwc-10bit;
+			qcom,load-busfreq-tbl =
+				<979200 2446336>,  /* UHD30D     */
+				<864000 2108416>,  /* 720p240D   */
+				<489600 1207296>,  /* 1080p60D   */
+				<432000 1058816>,  /* 720p120D   */
+				<244800 616448>,   /* 1080p30D   */
+				<216000 534528>,   /* 720p60D    */
+				<108000 271360>,   /* 720p30D    */
+				<0 0>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt b/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt
new file mode 100644
index 0000000..7e9aee1
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/qcom-i2c-pmic.txt
@@ -0,0 +1,98 @@
+Qualcomm Technologies, Inc. I2C PMIC Interrupt Controller
+Platform Independent Bindings
+
+The I2C PMIC Controller is used by multi-function PMIC devices which communicate
+over the I2C bus. An I2C PMIC controller node typically contains one or more
+child nodes representing the device's peripherals. Each of the peripherals
+typically has its own driver on the platform bus and will be enumerated by this
+controller. The controller exposes a regmap to the peripherals to communicate
+over the I2C bus.
+
+The controller also controls interrupts for all of the peripherals on the bus.
+The controller takes a summary interrupt, deciphers which peripheral triggered
+the interrupt, and which of the peripheral's interrupts were triggered. Finally,
+it calls the handlers for each of the virtual interrupts that were registered.
+
+This document describes the common platform independent bindings that apply
+to all I2C PMIC interrupt controllers.
+
+========================================
+First Level Nodes - I2C PMIC Controllers
+========================================
+
+Platform independent properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: Must be "qcom,i2c-pmic".
+
+- reg
+	Usage:      required
+	Value type: <u32>
+	Definition: 7-bit I2C address of the device.
+
+- interrupt-parent
+	Usage:      optional
+	Value type: <phandle>
+	Definition: phandle of the interrupt controller which services the
+		    summary interrupt.
+
+- interrupts
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: Summary interrupt specifier.
+
+- interrupt-controller
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates this device node is an
+		    interrupt controller.
+
+- #interrupt-cells
+	Usage:      optional
+	Value type: <u32>
+	Definition: Number of cells to encode an interrupt source.
+
+- qcom,periph-map
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of u32 arrays. This provides a mapping between the
+		    summary status register bits and peripheral addresses.
+
+		    The number of arrays should match the number of summary
+		    registers with up to 8 elements each. One element per bit
+		    of the summary status register in order from the least
+		    sigificant bit to the most significant bit.
+
+- pinctrl-names
+	Usage:      optional
+	Value type: <string-list>
+	Definition: Should be "default".
+		    Please refer to pinctrl-bindings.txt
+
+- pinctrl-0
+	Usage:      optional
+	Value type: <phandle-list>
+	Definition: phandle of the pin configuration.
+		    Please refer to pinctrl-bindings.txt
+
+=======
+Example
+=======
+
+&i2c_3 {
+	status = "ok";
+	qcom,smb138x@8 {
+		compatible = "qcom,i2c-pmic";
+		reg = <0x8>;
+		interrupt-parent = <&tlmm_pinmux>;
+		interrupts = <83 0>;
+		interrupt-controller;
+		#interrupt-cells = <3>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&smb_stat_active>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		qcom,periph-map = <0x10 0x11 0x12 0x13 0x14 0x16 0x36>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/pci/msm_pcie.txt b/Documentation/devicetree/bindings/pci/msm_pcie.txt
new file mode 100644
index 0000000..a50e0c2
--- /dev/null
+++ b/Documentation/devicetree/bindings/pci/msm_pcie.txt
@@ -0,0 +1,288 @@
+MSM PCIe
+
+MSM PCI express root complex
+
+Required properties:
+  - compatible: should be "qcom,pci-msm"
+  - cell-index: defines root complex ID.
+  - #address-cells: Should provide a value of 0.
+  - reg: should contain PCIe register maps.
+  - reg-names: indicates various resources passed to driver by name.
+		Should be "parf", "phy", "dm_core", "elbi", "conf", "io", "bars".
+		These correspond to different modules within the PCIe core.
+  - ranges: For details of ranges properties, please refer to:
+    "Documentation\devicetree\bindings\pci\pci.txt"
+  - interrupts: Should be in the format <0 1 2> and it is an index to the
+		interrupt-map that contains PCIe related interrupts.
+  - #interrupt-cells: Should provide a value of 1.
+  - #interrupt-map-mask: should provide a value of 0xffffffff.
+  - interrupt-map:  Must create mapping for the number of interrupts
+		    that are defined in above interrupts property.
+		    For PCIe device node, it should define 12 mappings for
+		    the corresponding PCIe interrupts supporting the specification.
+  - interrupt-names: indicates interrupts passed to driver by name.
+		     Should be "int_msi", "int_a", "int_b", "int_c", "int_d",
+		     "int_pls_pme", "int_pme_legacy", "int_pls_err",
+		     "int_aer_legacy", "int_pls_link_up",
+		     "int_pls_link_down", "int_bridge_flush_n",
+		     "msi_0", "msi_1", "msi_2", "msi_3",
+		     "msi_4", "msi_5", "msi_6", "msi_7",
+		     "msi_8", "msi_9", "msi_10", "msi_11",
+		     "msi_12", "msi_13", "msi_14", "msi_15",
+		     "msi_16", "msi_17", "msi_18", "msi_19",
+		     "msi_20", "msi_21", "msi_22", "msi_23",
+		     "msi_24", "msi_25", "msi_26", "msi_27",
+		     "msi_28", "msi_29", "msi_30", "msi_31"
+		     These correspond to the standard PCIe specification to support
+		     MSIs, virtual IRQ's (INT#), link state notifications.
+  - perst-gpio: PERST GPIO specified by PCIe spec.
+  - wake-gpio: WAKE GPIO specified by PCIe spec.
+  - <supply-name>-supply: phandle to the regulator device tree node.
+    Refer to the schematics for the corresponding voltage regulators.
+    vreg-1.8-supply: phandle to the analog supply for the PCIe controller.
+    vreg-3.3-supply: phandle to the analog supply for the PCIe controller.
+    vreg-0.9-supply: phandle to the analog supply for the PCIe controller.
+
+Optional Properties:
+  - qcom,<supply-name>-voltage-level: specifies voltage levels for supply.
+    Should be specified in pairs (max, min, optimal), units uV.
+  - clkreq-gpio: CLKREQ GPIO specified by PCIe spec.
+  - qcom,ep-gpio: GPIO which enables a certain type of endpoint for link training.
+  - pinctrl-names: The state name of the pin configuration.
+    supports: "default", "sleep"
+  - pinctrl-0: For details of pinctrl properties, please refer to:
+    "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+  - pinctrl-1: For details of pinctrl properties, please refer to:
+    "Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt"
+  - clocks: list of clock phandles
+  - clock-names: list of names of clock inputs.
+		     Should be "pcie_0_pipe_clk", "pcie_0_ref_clk_src",
+				"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
+				"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
+				"pcie_0_ldo";
+  - max-clock-frequency-hz: list of the maximum operating frequencies stored
+				in the same order of clock names;
+  - qcom,l0s-supported: L0s is supported.
+  - qcom,l1-supported: L1 is supported.
+  - qcom,l1ss-supported: L1 sub-states (L1ss) is supported.
+  - qcom,aux-clk-sync: The AUX clock is synchronous to the Core clock to
+    support L1ss.
+  - qcom,common-clk-en: Enables the common clock configuration for the endpoint.
+  - qcom,clk-power-manage-en: Enables the clock power management for the
+    endpoint.
+  - qcom,n-fts: The number of fast training sequences sent when the link state
+    is changed from L0s to L0.
+  - qcom,pcie-phy-ver: version of PCIe PHY.
+  - qcom,phy-sequence: The initialization sequence to bring up the PCIe PHY.
+    Should be specified in groups (offset, value, delay).
+  - qcom,port-phy-sequence: The initialization sequence to bring up the
+    PCIe port PHY.
+    Should be specified in groups (offset, value, delay).
+  - qcom,use-19p2mhz-aux-clk: The frequency of PCIe AUX clock is 19.2MHz.
+  - qcom,ep-wakeirq: The endpoint will issue wake signal when it is up, and the
+    root complex has the capability to enumerate the endpoint for this case.
+  - qcom,msi-gicm-addr: MSI address for GICv2m.
+  - qcom,msi-gicm-base: MSI IRQ base for GICv2m.
+  - qcom,ext-ref-clk: The reference clock is external.
+  - iommus: the phandle and stream IDs for the SMMU used by this root
+    complex. This should be used in separate nodes from the main root
+    complex nodes, and is the only property needed in that case.
+  - qcom,common-phy: There is a common phy for all the Root Complexes.
+  - qcom,smmu-exist: PCIe uses a SMMU.
+  - qcom,smmu-sid-base: The base SMMU SID that PCIe bus driver will use to calculate
+    and assign for each endpoint.
+  - qcom,ep-latency: The time (unit: ms) to wait for the PCIe endpoint to become
+    stable after power on, before de-assert the PERST to the endpoint.
+  - qcom,wr-halt-size: With base 2, this exponent determines the size of the
+    data that PCIe core will halt on for each write transaction.
+  - qcom,cpl-timeout: Completion timeout value. This value specifies the time range
+    which the root complex will send out a completion packet if there is no response
+    from the endpoint.
+  - linux,pci-domain: For details of pci-domains properties, please refer to:
+    "Documentation/devicetree/bindings/pci/pci.txt"
+  - qcom,perst-delay-us-min: The minimum allowed time (unit: us) to sleep after
+    asserting or de-asserting PERST GPIO.
+  - qcom,perst-delay-us-max: The maximum allowed time (unit: us) to sleep after
+    asserting or de-asserting PERST GPIO.
+  - qcom,tlp-rd-size: The max TLP read size (Calculation: 128 times 2 to the
+    tlp-rd-size power).
+  - Refer to "Documentation/devicetree/bindings/arm/msm/msm_bus.txt" for
+    below optional properties:
+	- qcom,msm-bus,name
+	- qcom,msm-bus,num-cases
+	- qcom,msm-bus,num-paths
+	- qcom,msm-bus,vectors-KBps
+  - qcom,scm-dev-id: If present then device id value is passed to secure channel
+	manager(scm) driver. scm driver uses this device id to restore PCIe
+	controller related security configuration after coming out of the controller
+	power collapse.
+  - resets: reset specifier pair consists of phandle for the reset controller
+    and reset lines used by this controller.
+  - reset-names: reset signal name strings sorted in the same order as the resets
+    property.
+
+Example:
+
+	pcie0: qcom,pcie@fc520000 {
+		compatible = "qcom,msm_pcie";
+		cell-index = <0>;
+		#address-cells = <0>;
+		reg = <0xfc520000 0x2000>,
+		      <0xfc526000 0x1000>,
+		      <0xff000000 0x1000>,
+		      <0xff001000 0x1000>,
+		      <0xff100000 0x1000>,
+		      <0xff200000 0x100000>,
+		      <0xff300000 0xd00000>;
+		reg-names = "parf", "dm_core", "elbi",
+				"conf", "io", "bars";
+		ranges = <0x01000000 0x0 0x0c200000 0x0c200000 0x0 0x100000>,
+			<0x02000000 0x0 0x0c300000 0x0c300000 0x0 0xd00000>;
+		interrupt-parent = <&pcie0>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11
+				12 13 14 15 16 17 18 19 20
+				21 22 23 24 25 26 27 28 29
+				30 31 32 33 34 35 36 37 38
+				39 40 41 42 43>;
+		#interrupt-cells = <1>;
+		interrupt-map-mask = <0xffffffff>;
+		interrupt-map = <0x0 0x0 0x0 0 &intc 0 405 0
+				0x0 0x0 0x0 1 &intc 0 244 0
+				0x0 0x0 0x0 2 &intc 0 245 0
+				0x0 0x0 0x0 3 &intc 0 247 0
+				0x0 0x0 0x0 4 &intc 0 248 0
+				0x0 0x0 0x0 5 &intc 0 249 0
+				0x0 0x0 0x0 6 &intc 0 250 0
+				0x0 0x0 0x0 7 &intc 0 251 0
+				0x0 0x0 0x0 8 &intc 0 252 0
+				0x0 0x0 0x0 9 &intc 0 253 0
+				0x0 0x0 0x0 10 &intc 0 254 0
+				0x0 0x0 0x0 11 &intc 0 255 0
+				0x0 0x0 0x0 12 &intc 0 448 0
+				0x0 0x0 0x0 13 &intc 0 449 0
+				0x0 0x0 0x0 14 &intc 0 450 0
+				0x0 0x0 0x0 15 &intc 0 451 0
+				0x0 0x0 0x0 16 &intc 0 452 0
+				0x0 0x0 0x0 17 &intc 0 453 0
+				0x0 0x0 0x0 18 &intc 0 454 0
+				0x0 0x0 0x0 19 &intc 0 455 0
+				0x0 0x0 0x0 20 &intc 0 456 0
+				0x0 0x0 0x0 21 &intc 0 457 0
+				0x0 0x0 0x0 22 &intc 0 458 0
+				0x0 0x0 0x0 23 &intc 0 459 0
+				0x0 0x0 0x0 24 &intc 0 460 0
+				0x0 0x0 0x0 25 &intc 0 461 0
+				0x0 0x0 0x0 26 &intc 0 462 0
+				0x0 0x0 0x0 27 &intc 0 463 0
+				0x0 0x0 0x0 28 &intc 0 464 0
+				0x0 0x0 0x0 29 &intc 0 465 0
+				0x0 0x0 0x0 30 &intc 0 466 0
+				0x0 0x0 0x0 31 &intc 0 467 0
+				0x0 0x0 0x0 32 &intc 0 468 0
+				0x0 0x0 0x0 33 &intc 0 469 0
+				0x0 0x0 0x0 34 &intc 0 470 0
+				0x0 0x0 0x0 35 &intc 0 471 0
+				0x0 0x0 0x0 36 &intc 0 472 0
+				0x0 0x0 0x0 37 &intc 0 473 0
+				0x0 0x0 0x0 38 &intc 0 474 0
+				0x0 0x0 0x0 39 &intc 0 475 0
+				0x0 0x0 0x0 40 &intc 0 476 0
+				0x0 0x0 0x0 41 &intc 0 477 0
+				0x0 0x0 0x0 42 &intc 0 478 0
+				0x0 0x0 0x0 43 &intc 0 479 0>;
+		interrupt-names = "int_msi", "int_a", "int_b", "int_c", "int_d",
+				"int_pls_pme", "int_pme_legacy", "int_pls_err",
+				"int_aer_legacy", "int_pls_link_up",
+				"int_pls_link_down", "int_bridge_flush_n",
+				"msi_0", "msi_1", "msi_2", "msi_3",
+				"msi_4", "msi_5", "msi_6", "msi_7",
+				"msi_8", "msi_9", "msi_10", "msi_11",
+				"msi_12", "msi_13", "msi_14", "msi_15",
+				"msi_16", "msi_17", "msi_18", "msi_19",
+				"msi_20", "msi_21", "msi_22", "msi_23",
+				"msi_24", "msi_25", "msi_26", "msi_27",
+				"msi_28", "msi_29", "msi_30", "msi_31";
+
+		qcom,phy-sequence = <0x804 0x01 0x00
+					0x034 0x14 0x00
+					0x138 0x30 0x00
+					0x048 0x0f 0x00
+					0x15c 0x06 0x00
+					0x090 0x01 0x00
+					0x808 0x03 0x00>;
+		qcom,port-phy-sequence = <0x804 0x01 0x00
+					0x034 0x14 0x00
+					0x138 0x30 0x00
+					0x048 0x0f 0x00
+					0x15c 0x06 0x00
+					0x090 0x01 0x00
+					0x808 0x03 0x00>;
+		perst-gpio = <&msmgpio 70 0>;
+		wake-gpio = <&msmgpio 69 0>;
+		clkreq-gpio = <&msmgpio 68 0>;
+		qcom,ep-gpio = <&tlmm 94 0>;
+
+		gdsc-vdd-supply = <&gdsc_pcie_0>;
+		vreg-1.8-supply = <&pma8084_l12>;
+		vreg-0.9-supply = <&pma8084_l4>;
+		vreg-3.3-supply = <&wlan_vreg>;
+
+		qcom,vreg-1.8-voltage-level = <1800000 1800000 1000>;
+		qcom,vreg-0.9-voltage-level = <950000 950000 24000>;
+
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&pcie0_clkreq_default &pcie0_perst_default &pcie0_wake_default>;
+		pinctrl-1 = <&pcie0_clkreq_sleep &pcie0_perst_sleep &pcie0_wake_sleep>;
+
+		clocks = <&clock_gcc clk_gcc_pcie_0_pipe_clk>,
+			<&clock_rpm clk_ln_bb_clk>,
+			<&clock_gcc clk_gcc_pcie_0_aux_clk>,
+			<&clock_gcc clk_gcc_pcie_0_cfg_ahb_clk>,
+			<&clock_gcc clk_gcc_pcie_0_mstr_axi_clk>,
+			<&clock_gcc clk_gcc_pcie_0_slv_axi_clk>,
+			<&clock_gcc clk_pcie_0_phy_ldo>;
+
+		clock-names = "pcie_0_pipe_clk", "pcie_0_ref_clk_src",
+				"pcie_0_aux_clk", "pcie_0_cfg_ahb_clk",
+				"pcie_0_mstr_axi_clk", "pcie_0_slv_axi_clk",
+				"pcie_0_ldo";
+
+		resets = <&clock_gcc GCC_PCIE_PHY_BCR>,
+			<&clock_gcc GCC_PCIE_PHY_COM_BCR>,
+			<&clock_gcc GCC_PCIE_PHY_NOCSR_COM_PHY_BCR>,
+			<&clock_gcc GCC_PCIE_0_PHY_BCR>;
+
+		reset-names = "pcie_phy_reset", "pcie_phy_com_reset",
+				"pcie_phy_nocsr_com_phy_reset","pcie_0_phy_reset";
+
+		max-clock-frequency-hz = <125000000>, <0>, <1000000>,
+						<0>, <0>, <0>, <0>;
+		qcom,l0s-supported;
+		qcom,l1-supported;
+		qcom,l1ss-supported;
+		qcom,aux-clk-sync;
+		qcom,n-fts = <0x50>;
+		qcom,pcie-phy-ver = <1>;
+		qcom,ep-wakeirq;
+		qcom,msi-gicm-addr = <0xf9040040>;
+		qcom,msi-gicm-base = <0x160>;
+		qcom,ext-ref-clk;
+		qcom,tlp-rd-size = <0x5>;
+		qcom,common-phy;
+		qcom,smmu-exist;
+		qcom,smmu-sid-base = <0x1480>;
+		qcom,ep-latency = <100>;
+		qcom,wr-halt-size = <0xa>; /* 1KB */
+		qcom,cpl-timeout = <0x2>;
+
+		iommus = <&anoc0_smmu>;
+
+		qcom,msm-bus,name = "pcie0";
+		qcom,msm-bus,num-cases = <2>;
+		qcom,msm-bus,num-paths = <1>;
+		qcom,msm-bus,vectors-KBps =
+				<45 512 0 0>,
+				<45 512 500 800>;
+
+		qcom,scm-dev-id = <11>;
+	};
diff --git a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
index ae1a2ad..f842ed6 100644
--- a/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
+++ b/Documentation/devicetree/bindings/pil/pil-q6v5-mss.txt
@@ -79,6 +79,7 @@
 				    current issue.
 - qcom,qdsp6v61-1-1: Boolean- Present if the qdsp version is v61 1.1
 - qcom,qdsp6v62-1-2: Boolean- Present if the qdsp version is v62 1.2
+- qcom,qdsp6v62-1-4: Boolean- Present if the qdsp version is v62 1.4
 - qcom,qdsp6v62-1-5: Boolean- Present if the qdsp version is v62 1.5
 - qcom,qdsp6v65-1-0: Boolean- Present if the qdsp version is v65 1.0
 - qcom,mx-spike-wa: Boolean- Present if we need to assert QDSP6 I/O clamp, memory
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,sdmbat-pinctrl b/Documentation/devicetree/bindings/pinctrl/qcom,sdmbat-pinctrl
new file mode 100644
index 0000000..9616d9d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,sdmbat-pinctrl
@@ -0,0 +1,146 @@
+Qualcomm Technologies, Inc. SDMBAT TLMM block
+
+This binding describes the Top Level Mode Multiplexer block found in the
+SDMBAT platform.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be "qcom,sdmbat-pinctrl"
+
+- reg:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: the base address and size of the TLMM register space.
+
+- interrupts:
+	Usage: required
+	Value type: <prop-encoded-array>
+	Definition: should specify the TLMM summary IRQ.
+
+- interrupt-controller:
+	Usage: required
+	Value type: <none>
+	Definition: identifies this node as an interrupt controller
+
+- #interrupt-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 2. Specifying the pin number and flags, as defined
+		    in <dt-bindings/interrupt-controller/irq.h>
+
+- gpio-controller:
+	Usage: required
+	Value type: <none>
+	Definition: identifies this node as a gpio controller
+
+- #gpio-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: must be 2. Specifying the pin number and flags, as defined
+		    in <dt-bindings/gpio/gpio.h>
+
+Please refer to ../gpio/gpio.txt and ../interrupt-controller/interrupts.txt for
+a general description of GPIO and interrupt bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin, a group, or a list of pins or groups. This configuration can include the
+mux function to select on those pin(s)/group(s), and various pin configuration
+parameters, such as pull-up, drive strength, etc.
+
+
+PIN CONFIGURATION NODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+	Usage: required
+	Value type: <string-array>
+	Definition: List of gpio pins affected by the properties specified in
+		    this subnode.
+
+		    Valid pins are:
+		      gpio0-gpio149
+		        Supports mux, bias and drive-strength
+
+		      sdc1_clk, sdc1_cmd, sdc1_data sdc2_clk, sdc2_cmd,
+		      sdc2_data sdc1_rclk
+		        Supports bias and drive-strength
+
+- function:
+	Usage: required
+	Value type: <string>
+	Definition: Specify the alternative function to be configured for the
+		    specified pins. Functions are only valid for gpio pins.
+		    Valid values are:
+
+		    qup0, qup9, qdss_cti, ddr_pxi0, ddr_bist, atest_tsens2,
+		    vsense_trigger, atest_usb1, qup_l4, GP_PDM1, qup_l5,
+		    mdp_vsync, qup_l6, wlan2_adc1, atest_usb11, ddr_pxi2,
+		    edp_lcd, dbg_out, wlan2_adc0, atest_usb10, m_voc,
+		    tsif1_sync, ddr_pxi3, cam_mclk, pll_bypassnl, qdss_gpio0,
+		    pll_reset, qdss_gpio1, qdss_gpio2, qdss_gpio3, cci_i2c,
+		    qup1, gpio
+
+- bias-disable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as no pull.
+
+- bias-pull-down:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as pull down.
+
+- bias-pull-up:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configued as pull up.
+
+- output-high:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    high.
+		    Not valid for sdc pins.
+
+- output-low:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    low.
+		    Not valid for sdc pins.
+
+- drive-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the drive strength for the specified pins, in mA.
+		    Valid values are: 2, 4, 6, 8, 10, 12, 14 and 16
+
+Example:
+
+	tlmm: pinctrl@03800000 {
+		compatible = "qcom,sdmbat-pinctrl";
+		reg = <0x03800000 0xc00000>;
+		interrupts = <0 208 0>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
diff --git a/Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt
new file mode 100644
index 0000000..add8b7d
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/qcom,wcd-pinctrl.txt
@@ -0,0 +1,138 @@
+Qualcomm Technologies, Inc. WCD GPIO block
+
+This binding describes the GPIO block found in the WCD934X series of
+audio codec's from QTI.
+
+- compatible:
+	Usage: required
+	Value type: <string>
+	Definition: must be "qcom,wcd-pinctrl"
+
+- qcom,num-gpios:
+	Usage: required
+	Value type: <u32>
+	Definition: Number of GPIO's supported by the controller
+
+- gpio-controller:
+	Usage: required
+	Value type: <none>
+	Definition: Mark the device node as a GPIO controller
+
+- #gpio-cells:
+	Usage: required
+	Value type: <u32>
+	Definition: Must be 2;
+		    the first cell will be used to define gpio number and the
+		    second denotes the flags for this gpio
+
+Please refer to ../gpio/gpio.txt for a general description of GPIO bindings.
+
+Please refer to pinctrl-bindings.txt in this directory for details of the
+common pinctrl bindings used by client devices, including the meaning of the
+phrase "pin configuration node".
+
+The pin configuration nodes act as a container for an arbitrary number of
+subnodes. Each of these subnodes represents some desired configuration for a
+pin or a list of pins. This configuration can include the
+mux function to select on those pin(s), and various pin configuration
+parameters, as listed below.
+
+
+SUBNODES:
+
+The name of each subnode is not important; all subnodes should be enumerated
+and processed purely based on their content.
+
+Each subnode only affects those parameters that are explicitly listed. In
+other words, a subnode that lists a mux function but no pin configuration
+parameters implies no information about any pin configuration parameters.
+Similarly, a pin subnode that describes a pullup parameter implies no
+information about e.g. the mux function.
+
+The following generic properties as defined in pinctrl-bindings.txt are valid
+to specify in a pin configuration subnode:
+
+- pins:
+	Usage: required
+	Value type: <string-array>
+	Definition: List of gpio pins affected by the properties specified in
+		    this subnode.  Valid pins are:
+		    gpio1-gpio5 for wcd9340
+
+- bias-disable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configured as no pull.
+
+- bias-pull-down:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins should be configured as pull down.
+
+- bias-pull-up:
+	Usage: optional
+	Value type: <empty>
+	Definition: The specified pins should be configured as pull up.
+
+- qcom,pull-up-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Specifies the strength to use for pull up, if selected.
+
+- bias-high-impedance:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins will put in high-Z mode and disabled.
+
+- input-enable:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are put in input mode.
+
+- output-high:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    high.
+
+- output-low:
+	Usage: optional
+	Value type: <none>
+	Definition: The specified pins are configured in output mode, driven
+		    low.
+
+- qcom,drive-strength:
+	Usage: optional
+	Value type: <u32>
+	Definition: Selects the drive strength for the specified pins.
+
+Example:
+
+	wcd: wcd_pinctrl@5 {
+		compatible = "qcom,wcd-pinctl";
+		qcom,num-gpios = <5>
+		gpio-controller;
+		#gpio-cells = <2>;
+
+		spkr_1_wcd_en_active: spkr_1_wcd_en_active {
+			mux {
+				pins = "gpio2";
+			};
+
+			config {
+				pins = "gpio2";
+				output-high;
+			};
+		};
+
+		spkr_1_wcd_en_sleep: spkr_1_wcd_en_sleep {
+			mux {
+				pins = "gpio2";
+			};
+
+			config {
+				pins = "gpio2";
+				input-enable;
+			};
+		};
+	};
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-coincell.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-coincell.txt
new file mode 100644
index 0000000..4d55f0c
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-coincell.txt
@@ -0,0 +1,44 @@
+Qualcomm Technologies, Inc. QPNP Coincell - coincell battery charger devices
+
+Required properties:
+- compatible:      Must be "qcom,qpnp-coincell".
+- reg:             Specifies the SPMI address and size for this coincell device.
+
+Required structure:
+- A qcom,qpnp-coincell node must be a child of an SPMI node that has specified
+	the spmi-slave-container property.
+
+Optional properties:
+- qcom,rset-ohms:	Specifies the resistance of the current limiting
+			resistor in ohms.  Four values are supported:
+			800, 1200, 1700, and 2100.
+- qcom,vset-millivolts:	Specifies the coincell charging voltage in millivolts.
+			Four values are supported: 2500, 3000, 3100, and 3200.
+- qcom,charge-enable:	Specifies if coincell charging should be enabled or not.
+			0 = disable charging, 1 = enabled charging
+
+If any of the optional properties are not specified, then the hardware default
+values for the unspecified properties will be used instead.
+
+Example:
+	qcom,spmi@fc4c0000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupt-controller;
+		#interrupt-cells = <3>;
+
+		qcom,pm8941@1 {
+			spmi-slave-container;
+			reg = <0x1>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			qcom,coincell@2800 {
+				compatible = "qcom,qpnp-coincell";
+				reg = <0x2800 0x100>;
+				qcom,rset-ohms = <800>;
+				qcom,vset-millivolts = <3100>;
+				qcom,charge-enable = <1>;
+			};
+		};
+	};
diff --git a/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
new file mode 100644
index 0000000..babc452
--- /dev/null
+++ b/Documentation/devicetree/bindings/platform/msm/qpnp-revid.txt
@@ -0,0 +1,17 @@
+QPNP-REVID
+
+QPNP-REVID provides a way to read the PMIC part number and revision.
+
+Required properties:
+- compatible : should be "qcom,qpnp-revid"
+- reg : offset and length of the PMIC peripheral register map.
+
+Optional property:
+- qcom,fab-id-valid: Use this property when support to read Fab
+	identification from REV ID peripheral is available.
+
+Example:
+	qcom,revid@100 {
+		compatible = "qcom,qpnp-revid";
+		reg = <0x100 0x100>;
+	};
diff --git a/Documentation/devicetree/bindings/power/apm.txt b/Documentation/devicetree/bindings/power/apm.txt
new file mode 100644
index 0000000..fa03edf
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/apm.txt
@@ -0,0 +1,73 @@
+MSM Array Power Mux (msm-apm)
+
+In some MSM designs, the CPU and caches contain logic which can be powered by
+multiple rails. The APM controller exists to enable the selection of the power
+rail supplying these arrays. Since a given supply may drop below the array
+SRAM minimum operating voltage, the APM controller can be used to request a
+switch to a power supply that will guarantee logic state retention.
+
+Required properties
+- compatible:	"qcom,msm-apm", "qcom,msm8996pro-apm", "qcom,msm8953-apm"
+- reg:		Specifies physical base address and size of memory mapped regions
+		containing the APM controller, APCS CSR, APC PLL controller, and
+		SPM event registers.
+- reg-names:	List of strings identifying the reg property entries. This list must
+		contain: "pm-apcc-glb", "apcs-csr", "apc0-pll-ctl", "apc1-pll-ctl",
+		"apc0-cpu0-spm", "apc0-cpu1-spm", "apc1-cpu0-spm", "apc1-cpu1-spm",
+		"apc0-l2-spm", and "apc1-l2-spm".
+
+Optional properties
+- qcom,clock-source-override:	Specify this property to request a switch of the APC0 and APC1
+				clock sources to GPLL0 before the APM switch begins and to
+				switch back to the original clock source after the APM switch
+				completes.
+- qcom,apm-post-halt-delay:	The APM controller post halt delay counter value that SW needs
+				to program one time before starting the APM HW controller for
+				msm8953 target.
+- qcom,apm-halt-clk-delay:	The APM controller halt clock delay counter value that SW
+				needs to program one time before starting the APM HW controller
+				for msm8953 target.
+- qcom,apm-resume-clk-delay:	The APM controller resume clock delay counter value that SW
+				needs to program one time before starting the APM HW controller
+				for msm8953 target.
+- qcom,apm-sel-switch-delay:	The APM controller switch selection delay counter value that SW
+				needs to program one time before starting the APM HW controller
+				for msm8953 target.
+
+MSM APM Users
+
+Required properties:
+- qcom,apm-ctrl:		phandle of APM controller device node
+
+Example:
+	apc_apm: apm@099e0000 {
+		compatible = "qcom,msm-apm";
+		reg = <0x099e0000 0x1000>,
+			<0x09820000 0x10000>,
+			<0x06400050 0x8>,
+			<0x06480050 0x8>,
+			<0x09981068 0x8>,
+			<0x09991068 0x8>,
+			<0x099b1068 0x8>,
+			<0x099c1068 0x8>,
+			<0x099a1068 0x8>,
+			<0x099d1068 0x8>;
+		reg-names = "pm-apcc-glb",
+				"apcs-csr",
+				"apc0-pll-ctl",
+				"apc1-pll-ctl",
+				"apc0-cpu0-spm",
+				"apc0-cpu1-spm",
+				"apc1-cpu0-spm",
+				"apc1-cpu1-spm",
+				"apc0-l2-spm",
+				"apc1-l2-spm";
+		qcom,apm-post-halt-delay = <0x2>;
+		qcom,apm-halt-clk-delay = <0x11>;
+		qcom,apm-resume-clk-delay = <0x10>;
+		qcom,apm-sel-switch-delay = <0x01>;
+
+	foo_user {
+		...
+		qcom,apm-ctrl = <&apc_apm>;
+	};
diff --git a/Documentation/devicetree/bindings/pwm/pwm-qpnp.txt b/Documentation/devicetree/bindings/pwm/pwm-qpnp.txt
new file mode 100644
index 0000000..8cb513b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pwm/pwm-qpnp.txt
@@ -0,0 +1,219 @@
+Qualcomm Technologies, Inc. QPNP PWM/LPG controller
+
+qpnp-pwm driver supports Pulse Width Module (PWM) functionality. PWM feature is
+used in range of applications such as varying Display brightness, LED dimming,
+etc. QTI PMICs have a physical device called Light Pulse Generator (LPG). In
+addition to support PWM functionality, the LPG module provides a rich set of
+user defined PWM pattern configurations, such as sawtooth, linear up, linear
+down, triangular patterns etc. The PWM patterns are used in applications such as
+charger driver where the driver uses these patterns to indicate various states
+of charging.
+
+Required device bindings:
+- compatible:		should be "qcom,qpnp-pwm"
+- reg:			Offset and length of the controller's LPG channel register.
+- reg-names:		Name for the above register.
+			"qpnp-lpg-channel-base" = physical base address of the
+			controller's LPG channel register.
+- qcom,lpg-lut-size:	LPG LUT size.
+- qcom,channel-id:	channel Id for the PWM.
+- qcom,supported-sizes:	Supported PWM sizes.
+			Following three pwm sizes lists are supported by PWM/LPG controllers.
+			<6>, <9>;
+			<7>, <8>;
+			<6>, <7>, <9>;
+- qcom,ramp-index:	Ramp index in LUT ramp control register.
+			Each LPG has an index in the LUT ramp control register.
+			One exception is that, if LPG does not support LUT mode
+			and supports only PWM mode then there is no need to
+			provide the ramp-index.
+
+Optional device bindings:
+- qcom,force-pwm-size:	For certain LPG channels, PWM size can be forced.
+			Possible values  6, 7, 8 and 9.
+- qcom,channel-owner:	A string value to supply owner information.
+- qcom,mode-select:	0 = PWM mode
+			1 = LPG mode
+- qcom,dtest-line:  	indicates which DTEST line to be configured for LPG
+			or PWM output. For LPG subtypes, possible values are 1,
+			2, 3 and 4. For PWM subtype, possibe values are 1 and 2.
+- qcom,dtest-output:	indicates the output configuration for DTEST line.
+			For LPG subtypes, possible output values are:
+				0 = Disabled
+				1 = LPG output low
+				2 = LPG output high
+				3,4,5 = DTEST line specific configuration
+				6,7 = Not used
+			For PWM subtype, possible output values are:
+				0 = Disabled
+				1 = pwm_out for DTEST1 or reserved
+				2 = pwm_out for DTEST2 or reserved
+				3 = Not used
+If this binding is specified along with the required bindings of PWM/LPG then
+in addition to configure PWM/LPG the qpnp-pwm driver also enables the feature
+at the probe time. In the case where the binding is not specified the qpnp-pwm
+driver does not enable the feature. Also, it is considered an error to specify
+a particular mode using this binding but not the respective feature subnode.
+
+All PWM devices support both PWM and LPG features within the same device.
+To support each feature, there are some required and optional bindings passed
+through device tree.
+
+The PWM device can enable one feature (either PWM or LPG) at any given time.
+Therefore, the qpnp-pwm driver applies the last PWM or LPG feature configuration
+and enables that feature.
+
+Required bindings to support PWM feature:
+- qcom,period:	PWM period time in microseconds.
+- qcom,duty:	PWM duty time in microseconds.
+- label:	"pwm"
+
+Required bindings to support LPG feature:
+The following bindings are needed to configure LPG mode, where a list of
+duty cycle percentages is populated. The size of the list cannot exceed
+the size of the LPG look-up table.
+
+- reg:			Offset and length of LPG look-up table (LUT). The LPG look-up table is a
+			contiguous address space that is populated with PWM values.
+			The size of PWM value is 9 bit and the size of each
+			entry of the table is 8 bit. Thus, two entries are used
+			to fill each PWM value. The lower entry is used for PWM
+			LSB byte and higher entry is used for PWM MSB bit.
+- reg-names:		Name for the above register.
+			"qpnp-lpg-lut-base" = physical base address of LPG LUT.
+- qcom,period:		PWM period time in microseconds.
+- qcom,duty-percents:	List of entries for look-up table
+- cell-index:		Index of look-up table that should be used to start
+			filling up the duty-pct list. start-idx + size of list
+			cannot exceed the size of look-up table.
+- label:		"lpg"
+
+
+Optional bindings to support LPG feature:
+- qcom,ramp-step-duration:	Time (in ms) to wait before loading next entry of LUT
+- qcom,lpg-lut-pause-hi:	Time (in ms) to wait once pattern reaches to hi
+				index.
+- qcom,lpg-lut-pause-lo:	Time (in ms) to wait once pattern reaches to lo
+				index.
+- qcom,lpg-lut-ramp-direction:	1 = Start the pattern from lo index to hi index.
+				0 = Start the pattern from hi index to lo index.
+- qcom,lpg-lut-pattern-repeat:	1 = Repeat the pattern after the pause once it
+				reaches to last duty cycle.
+				0 = Do not repeat the pattern.
+- qcom,lpg-lut-ramp-toggle:	1 = Toggle the direction of the pattern.
+				0 = Do not toggle the direction.
+- qcom,lpg-lut-enable-pause-hi:	1 = Enable pause time at hi index.
+				0 = Disable pause time at hi index.
+- qcom,lpg-lut-enable-pause-lo:	1 = Enable pause time at lo index.
+				0 = Disable pause time at lo index.
+
+
+Example:
+	qcom,spmi@fc4c0000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		qcom,pm8941@1 {
+			spmi-slave-container;
+			reg = <0x1>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			pwm@b100 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "qcom,qpnp-pwm";
+				reg = <0xb100 0x100>, <0xb040 0x80>;
+				reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+				qcom,channel-id = <0>;
+				qcom,supported-sizes = <6>, <7>, <9>;
+				qcom,ramp-index = <0>;
+				status = "okay";
+			};
+
+			pwm@b200 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "qcom,qpnp-pwm";
+				reg = <0xb200 0x100>, <0xb040 0x80>;
+				reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+				qcom,channel-id = <1>;
+				qcom,supported-sizes = <6>, <7>, <9>;
+				qcom,ramp-index = <1>;
+				qcom,force-pwm-size = <9>;
+				qcom,period = <6000000>;
+				status = "okay";
+
+				qcom,pwm {
+					qcom,duty = <4000000>;
+					label = "pwm";
+				};
+			};
+
+			pwm@b500 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "qcom,qpnp-pwm"
+				reg = <0xb500 0x100>, <0xb040 0x80>;
+				reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+				qcom,channel-id = <4>;
+				qcom,supported-sizes = <6>, <7>, <9>;
+				qcom,ramp-index = <4>;
+				qcom,period = <6000000>;
+				qcom,mode-select = <0>;
+				qcom,channel-owner = "RGB-led";
+				status = "okay";
+
+				qcom,pwm {
+					qcom,duty = <4000000>;
+					label = "pwm";
+				};
+
+				qcom,lpg {
+					qcom,duty-percents = <1 14 28 42 56 84 100
+							100 84 56 42 28 14 1>;
+					cell-index = <0>;
+					qcom,ramp-step-duration = <20>;
+					label = "lpg";
+				};
+			};
+
+			pwm@b300 {
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "qcom,qpnp-pwm";
+				reg = <0xb200 0x100>, <0xb040 0x80>;
+				reg-names = "qpnp-lpg-channel-base", "qpnp-lpg-lut-base";
+				qcom,channel-id = <2>;
+				qcom,supported-sizes = <6>, <7>, <9>;
+				qcom,ramp-index = <1>;
+				qcom,force-pwm-size = <9>;
+				qcom,period = <6000000>;
+				qcom,dtest-line = <3>;
+				qcom,dtest-output = <1>;
+				status = "okay";
+
+				qcom,pwm {
+					qcom,duty = <4000000>;
+					label = "pwm";
+				};
+			};
+		};
+	};
+
+There are couple of ways to configure PWM device channels as shown in above
+example,
+1. The PWM device channel #0 is configured with only required device bindings.
+In this case, the qpnp-pwm driver does not configure any mode by default.
+
+2. The qpnp-pwm driver configures PWM device channel #1 with PWM feature
+configuration, but does not enable the channel since "qcom,mode-select" binding
+is not specified in the devicetree.
+
+3. Both the PWM and LPG configurations are provided for PWM device channel #4.
+The qpnp-pwm driver configures both the modes, but enables PWM mode at the probe
+time. It also sets the channel owner information for the channel.
+
+4. This configuration is pretty similar to #2 above except in this case channel
+#3 is configured for PWM mode. Also it's DTEST3 line is configured to output
+LPG OUT low.
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt
new file mode 100644
index 0000000..2af3cbf
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cpr3-hmss-regulator.txt
@@ -0,0 +1,556 @@
+Qualcomm Technologies, Inc. CPR3 Regulator - HMSS Specific Bindings
+
+HMSS CPR3 controllers each support two CPR threads that monitor the voltage of
+a pair of application processor (HMSS) clusters that are powered by a shared
+regulator supply.  These controllers have a hardware aggregator to combine the
+UP/DOWN requests from each CPR thread into a single unified request.  They also
+have a hardware channel to use these requests to directly change the supply
+voltage at the PMIC via the SPM without software intervention.
+
+HMSS CPR3 controllers also have to take into account the state of the memory
+array power mux (APM) when scaling voltage to ensure that memory always receives
+a sufficiently high voltage.
+
+Both CPR open-loop voltages and CPR target quotients are stored in hardware
+fuses for HMSS CPR3 controllers.
+
+This document describes the HMSS specific CPR3 bindings.
+
+=======================
+Required Node Structure
+=======================
+
+CPR3 regulators must be described in three levels of devices nodes.  The first
+level describes the CPR3 controller.  The second level describes one or more
+hardware threads managed by the controller.  The third level describes one or
+more logical regulators handled by each CPR thread.
+
+All platform independent cpr3-regulator binding guidelines defined in
+cpr3-regulator.txt also apply to cpr3-hmss-regulator devices.
+
+====================================
+First Level Nodes - CPR3 Controllers
+====================================
+
+HMSS specific properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be one of the following:
+		    "qcom,cpr3-msm8996-v1-hmss-regulator",
+		    "qcom,cpr3-msm8996-v2-hmss-regulator",
+		    "qcom,cpr3-msm8996-v3-hmss-regulator",
+		    "qcom,cpr3-msm8996-hmss-regulator",
+		    "qcom,cpr3-msm8996pro-hmss-regulator".
+		    If the SoC revision is not specified, then it is assumed to
+		    be the most recent revision of MSM8996, i.e. v3.
+
+- interrupts
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: CPR interrupt specifier and a hardware closed-loop ceiling
+		    interrupt specifier.
+
+- interrupt-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Interrupt names.  This list must match up 1-to-1 with the
+		    interrupts specified in the 'interrupts' property. "cpr"
+		    and "ceiling" must be specified.
+
+- qcom,apm-ctrl
+	Usage:      required on systems that need APM management
+	Value type: <phandle>
+	Definition: phandle of memory array power mux (APM) controller device
+		    node for the APM that is used by the HMSS VDD supply
+
+- qcom,apm-threshold-voltage
+	Usage:      required if qcom,apm-ctrl is specified
+	Value type: <u32>
+	Definition: Specifies the APM threshold voltage in microvolts.  If the
+		    vdd-supply voltage is greater than or equal to this level,
+		    then the APM is switched to use the vdd-supply. If the
+		    vdd-supply voltage is below this level, then the APM is
+		    switched to use the system-supply.
+
+- qcom,apm-hysteresis-voltage
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the voltage delta in microvolts between the APM
+		    threshold voltage and the highest corner open-loop voltage
+		    which may be used as the ceiling for the corner.  If this
+		    property is not specified, then a value of 0 is assumed.
+
+- qcom,system-supply-max-voltage
+	Usage:      required if qcom,vdd-threadN-ldo-supply is specified for any
+		    CPR3 regulator managed by any CPR3 thread of this controller.
+	Value type: <u32>
+	Definition: Maximum voltage setpoint for the system-supply regulator.
+
+- qcom,mem-acc-supply-threshold-voltage
+	Usage:      required if mem-acc-supply or mem-acc-threadN-supply is
+		    specified and the CPR3 controller or any of the CPR3 regulators
+		    it controls needs to manage mem-acc settings.
+	Value type: <u32>
+	Definition: Specifies the mem-acc-supply threshold voltage in microvolts.
+		    If the vdd-supply voltage is greater than or equal to this
+		    level, then the mem-acc-supply regulator is switched to the
+		    high voltage corner setting. Conversely, if the vdd-supply
+		    voltage is below this level, then the mem-acc-supply regulator
+		    is switched to the low voltage corner setting.
+
+- qcom,mem-acc-supply-corner-map
+	Usage:      required if mem-acc-supply or mem-acc-threadN-supply is
+		    specified and the CPR3 controller or any of the CPR3 regulators
+		    it controls needs to manage mem-acc settings.
+	Value type: <prop-encoded-array>
+	Definition: A tuple containing two integers which defines the mem-acc-supply
+		    corner to use for low and high vdd-supply voltages, respectively.
+
+- qcom,cpr-up-down-delay-time
+	Usage:      required
+	Value type: <u32>
+	Definition: The time to delay in nanoseconds between consecutive CPR
+		    measurements when the last measurement recommended
+		    increasing or decreasing the vdd-supply voltage.
+
+- qcom,cpr-hw-closed-loop
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the HMSS CPR3 controller
+		    should operate in hardware closed-loop mode as opposed to
+		    software closed-loop mode.
+
+- vdd-limit-supply
+	Usage:      required
+	Value type: <phandle>
+	Definition: phandle of the VDD supply limit regulator which controls the
+		    CPR ceiling and floor voltages when operating in hardware
+		    closed-loop mode.
+
+- qcom,cpr-clock-throttling
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the power domains for which CPR processor clock
+		    throttling should be enabled.  This feature reduces the
+		    processor's clock frequency when it is resuming from a low
+		    power mode until CPR is able to raise the supply voltage to
+		    a final settled value.  The following bits may be set:
+			BIT(0) - Power cluster L2 cache
+			BIT(1) - Power cluster core 1
+			BIT(2) - Power cluster core 0
+			BIT(5) - Performance cluster L2 cache
+			BIT(6) - Performance cluster core 1
+			BIT(7) - Performance cluster core 0
+
+- mem-acc-threadN-supply
+	Usage:      optional
+	Value type: <phandle>
+	Definition: phandle of the regulator device which manages mem-acc
+		    configuration for the clusters per CPR thread. 'N' must
+		    match with the hardware thread ID of the thread it controls.
+
+- vdd-threadN-ldo-supply
+	Usage:      optional
+	Value type: <phandle>
+	Definition: phandle of the regulator device which manages LDO and BHS
+		    modes for the clusters per CPR thread. 'N' must match with
+		    the hardware thread ID of the thread it controls.
+
+- vdd-threadN-ldo-ret-supply
+	Usage:      required if vdd-threadN-ldo-supply is specified for
+		    this CPR thread.
+	Value type: <phandle>
+	Definition: phandle of the regulator device which manages LDO retention
+		    modes for the clusters per CPR thread. 'N' must match with
+		    the hardware thread ID of the thread it controls.
+
+=================================================
+Second Level Nodes - CPR Threads for a Controller
+=================================================
+
+HMSS specific properties:
+N/A
+
+===============================================
+Third Level Nodes - CPR Regulators for a Thread
+===============================================
+
+HMSS specific properties:
+- qcom,cpr-fuse-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse corners.  This value must be 5
+		    for HMSS.  These fuse corners are: MinSVS, LowSVS, SVS,
+		    Nominal, and Turbo.  Note that a specific fused target
+		    quotient is available for the LowSVS corner but a fused
+		    open-loop voltage is not available.  The LowSVS open-loop
+		    voltage is calculated using linear interpolation between
+		    the MinSVS and SVS values.
+
+- qcom,cpr-fuse-combos
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse combinations being supported by
+		    the device.  This value is utilized by several other
+		    properties.  Supported values are 1 up to the maximum
+		    possible for a given regulator type.  For HMSS the maximum
+		    supported value is 16.  The first 8 fuse combos correspond
+		    to speed bin fuse value 0 along with CPR revision fuse
+		    values 0 to 7.  The last 8 fuse combos correspond to speed
+		    bin fuse value 1 along with CPR revision fuse values 0 to 7.
+
+- qcom,cpr-speed-bins
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the number of speed bins being supported by the
+		    device.  This value is utilized by several other properties.
+		    Supported values are 1 up to the maximum possible for a
+		    given regulator type.  For HMSS the maximum supported value
+		    is 2.
+
+- qcom,is-cbf-regulator
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the CPR3 regulator
+		    corresponds to the CBF clock domain.  Special fuses are
+		    defined for the CBF CPR3 regulator on MSM8996-Pro chips.
+
+- qcom,ldo-min-headroom-voltage
+	Usage:      required if vdd-threadN-ldo-supply is specified for the
+		    CPR3 thread containing this CPR3 regulator and this CPR3
+		    regulator needs to manage the cluster LDO state.
+	Value type: <u32>
+	Definition: Voltage in microvolts required between the VDD_APCC voltage
+		    and the LDO output in order for the LDO to be operational.
+
+- qcom,ldo-max-headroom-voltage
+	Usage:      required if vdd-threadN-ldo-supply is specified for the
+		    CPR3 thread containing this CPR3 regulator and this CPR3
+		    regulator needs to manage the cluster LDO state.
+	Value type: <u32>
+	Definition: Maximum voltage difference in microvolts between the vdd-supply
+		    voltage and the LDO output voltage in order for active LDO mode
+		    to be operational.
+
+- qcom,ldo-adjust-voltage
+	Usage:      optional
+	Value type: <u32>
+	Definition: Voltage in microvolts used to offset margins between PMIC
+		    output and CPU.
+
+- qcom,ldo-max-voltage
+	Usage:      required if qcom,ldo-min-headroom-voltage is specified for this
+		    CPR3 regulator.
+	Value type: <u32>
+	Definition: Voltage in microvolts which represents the maximum
+		    physically supported voltage output of the LDO hardware.
+
+- qcom,uses-mem-acc
+	Usage:      required if mem-acc-threadN-supply is specified for the
+		    CPR3 thread containing this CPR3 regulator and this CPR3
+		    regulator needs to manage mem-acc settings.
+	Value type: <empty>
+	Definition: Boolean flag which indicates that this CPR3 regulator must
+		    manage mem-acc.
+
+- qcom,ldo-disable
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that LDO mode usage is
+		    disallowed.  If this flag is present, then the
+		    vdd-threadN-ldo-supply mode will not be modified.
+
+- qcom,allow-quotient-interpolation
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that it is acceptable to use
+		    interpolated CPR target quotient values.  These values are
+		    interpolated between the target quotient Fmax fuse values.
+
+- qcom,cpr-pd-bypass-mask
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the power domains associated with this CPR3
+		    regulator.  The following bits may be set:
+			BIT(0) - Power cluster L2 cache
+			BIT(1) - Power cluster core 1
+			BIT(2) - Power cluster core 0
+			BIT(3) - CBF
+			BIT(4) - L3 cache
+			BIT(5) - Performance cluster L2 cache
+			BIT(6) - Performance cluster core 1
+			BIT(7) - Performance cluster core 0
+
+- qcom,cpr-dynamic-floor-corner
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines for each fuse combination
+		    the CPR corner whose closed-loop voltage should be used as
+		    a CPR floor voltage whenever the power domains for this CPR3
+		    regulator are bypassed.  Supported values are 0 and 1 to N.
+		    A value of 0 means that no dynamic floor is needed.  N is
+		    the number of corners defined for this fuse combination in
+		    the qcom,cpr-corners property.
+
+		    The list must contain qcom,cpr-fuse-combos number of
+		    elements in which case the elements are matched to fuse
+		    combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    elements in which case the elements are matched to
+		    speed bins 1-to-1 or exactly 1 element which is used
+		    regardless of the fuse combination and speed bin found
+		    on a given chip.
+
+=======
+Example
+=======
+
+apcc_cpr: cpr3-ctrl@99e8000 {
+	compatible = "qcom,cpr3-msm8996-hmss-regulator";
+	reg = <0x099e8000 0x4000>, <0x00074000 0x1000>;
+	reg-names = "cpr_ctrl", "fuse_base";
+	clocks = <&clock_gcc clk_gcc_hmss_rbcpr_clk>;
+	clock-names = "core_clk";
+	interrupts = <0 48 0>, <0 47 0>;
+	interrupt-names = "cpr", "ceiling";
+	qcom,cpr-interrupt-affinity = <&CPU0 &CPU1>;
+	qcom,cpr-ctrl-name = "apcc";
+
+	qcom,cpr-sensor-time = <1000>;
+	qcom,cpr-loop-time = <5000000>;
+	qcom,cpr-idle-cycles = <15>;
+	qcom,cpr-up-down-delay-time = <3000>;
+	qcom,cpr-step-quot-init-min = <13>;
+	qcom,cpr-step-quot-init-max = <13>;
+	qcom,cpr-count-mode = <2>;
+
+	qcom,apm-ctrl = <&apc_apm>;
+	qcom,apm-threshold-voltage = <850000>;
+	qcom,apm-hysteresis-voltage = <5000>;
+	qcom,system-supply-max-voltage = <1015000>;
+	qcom,mem-acc-supply-threshold-voltage = <700000>;
+	qcom,mem-acc-supply-corner-map = <1 2>;
+
+	vdd-supply = <&pm8994_s11>;
+	qcom,voltage-step = <5000>;
+	vdd-limit-supply = <&pm8994_s11_limit>;
+	mem-acc-thread0-supply = <&apc0_pwrcl_mem_acc_vreg>;
+	mem-acc-thread1-supply = <&apc1_perfcl_mem_acc_vreg>;
+	mem-acc-supply = <&apcc_l3_mem_acc_vreg>;
+	vdd-thread0-ldo-supply = <&kryo0_vreg>;
+	vdd-thread1-ldo-supply = <&kryo1_vreg>;
+	vdd-thread0-ldo-ret-supply = <&kryo0_retention_vreg>;
+	vdd-thread1-ldo-ret-supply = <&kryo1_retention_vreg>;
+
+	qcom,cpr-enable;
+	qcom,cpr-hw-closed-loop;
+	qcom,cpr-clock-throttling = <0x20>;
+
+	qcom,cpr-aging-ref-voltage = <905000>;
+
+	thread@0 {
+		qcom,cpr-thread-id = <0>;
+		qcom,cpr-consecutive-up = <0>;
+		qcom,cpr-consecutive-down = <2>;
+		qcom,cpr-up-threshold = <0>;
+		qcom,cpr-down-threshold = <2>;
+
+		apc0_pwrcl_vreg: regulator-pwrcl {
+			regulator-name = "apc0_pwrcl_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <19>;
+
+			qcom,cpr-pd-bypass-mask = <0x07>;
+			qcom,cpr-fuse-corners = <5>;
+			qcom,cpr-fuse-combos = <1>;
+			qcom,cpr-corners = <19>;
+
+			qcom,ldo-min-headroom-voltage = <150000>;
+			qcom,ldo-max-headroom-voltage = <470000>;
+			qcom,ldo-max-voltage = <805000>;
+			qcom,uses-mem-acc;
+
+			qcom,cpr-corner-fmax-map = <1 2 6 11 19>;
+
+			qcom,cpr-voltage-ceiling =
+				<670000  670000  745000  745000  745000
+				 745000  905000  905000  905000  905000
+				 905000 1015000 1015000 1015000 1015000
+				1015000 1015000 1015000 1015000>;
+			qcom,cpr-voltage-floor =
+				<520000  550000  555000  565000  585000
+				 615000  635000  655000  690000  720000
+				 740000  750000  760000  770000  780000
+				 790000  815000  840000  850000>;
+			qcom,corner-frequencies =
+				<192000000  268800000  307200000
+				 345600000  403200000  480000000
+				 576000000  633600000  729600000
+				 806400000  883200000  960000000
+				1017600000 1113600000 1190400000
+				1267200000 1344000000 1420800000
+				1459200000>;
+
+			qcom,cpr-ro-scaling-factor =
+			      <   0    0    0    0 2222 2275 2506 2491
+			       2649 2640 2886 2866    0    0    0    0>,
+			      <   0    0    0    0 2222 2275 2506 2491
+			       2649 2640 2886 2866    0    0    0    0>,
+			      <   0    0    0    0 2222 2275 2506 2491
+			       2649 2640 2886 2866    0    0    0    0>,
+			      <   0    0    0    0 2147 2226 2310 2312
+			       2450 2447 2603 2600    0    0    0    0>,
+			      <   0    0    0    0 1989 2079 2066 2083
+			       2193 2201 2283 2296    0    0    0    0>;
+
+			qcom,cpr-open-loop-voltage-fuse-adjustment =
+				<0 0 0 0 0>;
+			qcom,cpr-closed-loop-voltage-fuse-adjustment =
+				<0 0 0 0 0>;
+
+			qcom,allow-voltage-interpolation;
+			qcom,allow-quotient-interpolation;
+			qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+			qcom,cpr-aging-max-voltage-adjustment = <25000>;
+			qcom,cpr-aging-ref-corner = <11>;
+			qcom,cpr-aging-ro-scaling-factor = <3200>;
+			qcom,cpr-aging-derate =
+				<1000 1000 1000 1000 1000 1000 1000 1000
+				 1000 1000 1000 1000 1000 1000 1000 1000
+				 1000 1000 1000>;
+			qcom,allow-aging-voltage-adjustment = <1>;
+		};
+
+		apc0_cbf_vreg: regulator-cbf {
+			regulator-name = "apc0_cbf_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <10>;
+			qcom,is-cbf-regulator;
+
+			qcom,cpr-pd-bypass-mask = <0x18>;
+			qcom,cpr-fuse-corners = <5>;
+			qcom,cpr-fuse-combos = <1>;
+			qcom,cpr-corners = <10>;
+
+			qcom,cpr-corner-fmax-map = <1 2 5 9 10>;
+
+			qcom,cpr-voltage-ceiling =
+			       <605000  670000  745000  745000  745000
+				905000  905000  905000  905000 1015000>;
+			qcom,cpr-voltage-floor =
+			       <520000  545000  565000  595000  635000
+				660000  690000  730000  750000  850000>;
+
+			qcom,corner-frequencies =
+				<150000000  307200000  384000000
+				 499200000  595200000  691200000
+				 787200000  883200000  960000000
+				1036800000>;
+
+			qcom,cpr-ro-scaling-factor =
+			      <   0    0    0    0 2222 2275 2506 2491
+			       2649 2640 2886 2866    0    0    0    0>,
+			      <   0    0    0    0 2222 2275 2506 2491
+			       2649 2640 2886 2866    0    0    0    0>,
+			      <   0    0    0    0 2222 2275 2506 2491
+			       2649 2640 2886 2866    0    0    0    0>,
+			      <   0    0    0    0 2147 2226 2310 2312
+			       2450 2447 2603 2600    0    0    0    0>,
+			      <   0    0    0    0 1989 2079 2066 2083
+			       2193 2201 2283 2296    0    0    0    0>;
+
+			qcom,cpr-open-loop-voltage-fuse-adjustment =
+				<0 0 0 0 0>;
+			qcom,cpr-closed-loop-voltage-fuse-adjustment =
+				<0 0 0 0 0>;
+
+			qcom,allow-voltage-interpolation;
+			qcom,allow-quotient-interpolation;
+			qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+			qcom,cpr-aging-max-voltage-adjustment = <25000>;
+			qcom,cpr-aging-ref-corner = <9>;
+			qcom,cpr-aging-ro-scaling-factor = <3200>;
+			qcom,cpr-aging-derate =
+				<1000 1000 1000 1000 1000 1000 1000 1000
+				 1000 1000>;
+			qcom,allow-aging-voltage-adjustment = <1>;
+		};
+	};
+
+	thread@1 {
+		qcom,cpr-thread-id = <1>;
+		qcom,cpr-consecutive-up = <0>;
+		qcom,cpr-consecutive-down = <2>;
+		qcom,cpr-up-threshold = <0>;
+		qcom,cpr-down-threshold = <2>;
+
+		apc1_vreg: regulator {
+			regulator-name = "apc1_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <18>;
+
+			qcom,cpr-pd-bypass-mask = <0xe0>;
+			qcom,cpr-fuse-corners = <5>;
+			qcom,cpr-fuse-combos = <1>;
+			qcom,cpr-corners = <18>;
+
+			qcom,ldo-min-headroom-voltage = <150000>;
+			qcom,ldo-max-headroom-voltage = <470000>;
+			qcom,ldo-max-voltage = <805000>;
+			qcom,uses-mem-acc;
+
+			qcom,cpr-corner-fmax-map = <1 3 5 11 18>;
+
+			qcom,cpr-voltage-ceiling =
+				<670000  670000  670000  745000  745000
+				 905000  905000  905000  905000  905000
+				 905000 1015000 1015000 1015000 1015000
+				1015000 1015000 1015000>;
+			qcom,cpr-voltage-floor =
+				<520000  530000  545000  590000  620000
+				 635000  660000  685000  700000  730000
+				 740000  750000  765000  790000  805000
+				 815000  830000  850000>;
+
+			qcom,corner-frequencies =
+				<307200000  345600000  403200000
+				 480000000  576000000  633600000
+				 729600000  806400000  883200000
+				 960000000 1017600000 1113600000
+				1190400000 1267200000 1344000000
+				1420800000 1497600000 1593600000>;
+
+			qcom,cpr-ro-scaling-factor =
+			      <   0    0    0    0 2212 2273 2517 2506
+			       2663 2650 2908 2891    0    0    0    0>,
+			      <   0    0    0    0 2212 2273 2517 2506
+			       2663 2650 2908 2891    0    0    0    0>,
+			      <   0    0    0    0 2212 2273 2517 2506
+			       2663 2650 2908 2891    0    0    0    0>,
+			      <   0    0    0    0 2152 2237 2321 2337
+			       2475 2469 2636 2612    0    0    0    0>,
+			      <   0    0    0    0 2001 2102 2092 2090
+			       2203 2210 2297 2297    0    0    0    0>;
+
+			qcom,cpr-open-loop-voltage-fuse-adjustment =
+				<0 0 0 5000 0>;
+			qcom,cpr-closed-loop-voltage-fuse-adjustment =
+				<0 0 0 20000 0>;
+
+			qcom,allow-voltage-interpolation;
+			qcom,allow-quotient-interpolation;
+			qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+			qcom,cpr-aging-max-voltage-adjustment = <25000>;
+			qcom,cpr-aging-ref-corner = <11>;
+			qcom,cpr-aging-ro-scaling-factor = <3200>;
+			qcom,cpr-aging-derate =
+				<1000 1000 1000 1000 1000 1000 1000 1000
+				 1000 1000 1000 1000 1000 1000 1000 1000
+				 1000 1000>;
+			qcom,allow-aging-voltage-adjustment = <1>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt
new file mode 100644
index 0000000..baa0cd2
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cpr3-mmss-regulator.txt
@@ -0,0 +1,345 @@
+Qualcomm Technologies, Inc. CPR3 Regulator - MMSS Specific Bindings
+
+MMSS CPR3 controllers each support one CPR thread that monitors the voltage of
+the graphics processor (MMSS) supply regulator.  The CPR open-loop voltages are
+stored in hardware fuses for MMSS CPR3 controllers.  However, the CPR target
+quotients must be defined in device tree.
+
+This document describes the MMSS specific CPR3 bindings.
+
+=======================
+Required Node Structure
+=======================
+
+CPR3 regulators must be described in three levels of devices nodes.  The first
+level describes the CPR3 controller.  The second level describes exacly one
+hardware thread managed by the controller.  The third level describes one or
+more logical regulators handled by the CPR thread.
+
+All platform independent cpr3-regulator binding guidelines defined in
+cpr3-regulator.txt also apply to cpr3-hmss-regulator devices.
+
+====================================
+First Level Nodes - CPR3 Controllers
+====================================
+
+MMSS specific properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be one of the following:
+		    "qcom,cpr3-msm8996-v1-mmss-regulator",
+		    "qcom,cpr3-msm8996-v2-mmss-regulator",
+		    "qcom,cpr3-msm8996-v3-mmss-regulator",
+		    "qcom,cpr3-msm8996-mmss-regulator",
+		    "qcom,cpr3-msm8996pro-mmss-regulator",
+		    "qcom,cpr4-msm8998-v1-mmss-regulator",
+		    "qcom,cpr4-msm8998-v2-mmss-regulator",
+		    "qcom,cpr4-msm8998-mmss-regulator".
+		    If the SoC revision is not specified, then it is assumed to
+		    be the most recent revision (i.e v3 for MSM8996 and v2
+		    for MSM8998).
+
+- clocks
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Array of clock tuples in which each tuple consists of a
+		    phandle to a clock device and a clock ID number.  The
+		    following clocks must be specified: MMSS RBCPR, MMSS RBCPR
+		    AHB, and MMSS MMAGIC AHB.
+
+- clock-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Clock names.  This list must match up 1-to-1 with the clocks
+		    specified in the 'clocks' property. "core_clk", "iface_clk",
+		    and "bus_clk" must be specified.  Note that "iface_clk" is
+		    not required for devices with compatible =
+		    "qcom,cpr4-msm8998-mmss-regulator".
+
+- qcom,cpr-temp-point-map
+	Usage:      Required if qcom,corner-allow-temp-adjustment is specified
+		    for at least one of the CPR3 regulators.
+	Value type: <prop-encoded-array>
+	Definition: The temperature points in decidegrees Celsius which indicate
+		    the range of temperature bands supported. If t1, t2, and t3
+		    are the temperature points, then the temperature bands are:
+		    (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).  1 to 3
+		    temperature points should be specified.
+
+- qcom,cpr-initial-temp-band
+	Usage:      Required if qcom,cpr-temp-point-map is specified.
+	Value type: <u32>
+	Definition: The initial temp band considering 0-based index at which
+		    the baseline target quotients are derived and fused.
+		    Supported values: 0 to number of elements in
+		    qcom,cpr-temp-point-map.
+
+- qcom,cpr-step-quot-fixed
+	Usage:      Optional for controllers with compatible =
+		    "qcom,cpr4-msm8998-mmss-regulator"; unsupported for
+		    all others.
+	Value type: <u32>
+	Definition: Fixed step quotient value used by controller for applying
+		    the SDELTA margin adjustments on the programmed target
+		    quotient values. The step quotient is the number of
+		    additional ring oscillator ticks observed for each
+		    qcom,voltage-step increase in vdd-supply output voltage.
+		    Supported values: 0 - 63.
+
+=================================================
+Second Level Nodes - CPR Threads for a Controller
+=================================================
+
+MMSS specific properties:
+N/A
+
+===============================================
+Third Level Nodes - CPR Regulators for a Thread
+===============================================
+
+MMSS specific properties:
+- qcom,cpr-fuse-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse corners.  This value must be 4
+		    for MMSS.
+
+- qcom,cpr-fuse-combos
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse combinations being supported by
+		    the device.  This value is utilized by several other
+		    properties.  Supported values are 1 up to the maximum
+		    possible for a given regulator type.  For MMSS the maximum
+		    supported value is 8.  These combos correspond to CPR
+		    revision fuse values from 0 to 7 in order.
+
+- qcom,cpr-speed-bins
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the number of speed bins being supported by the
+		    device.  This value is utilized by several other properties.
+		    Supported values are 1 up to the maximum possible for a
+		    given regulator type.  For MMSS the maximum supported value
+		    is 1.
+
+- qcom,mem-acc-voltage
+	Usage:      required if mem-acc-supply is specified for the CPR3 controller
+		    containing this CPR3 regulator
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the mem-acc-supply
+		    corner for each voltage corner in order from lowest to highest.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+- qcom,cpr-target-quotients
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists.  Each tuple defines the
+		    CPR target quotient for each ring oscillator (RO) for a
+		    given corner.  Since CPR3 supports exactly 16 ROs, each
+		    tuple must contain 16 elements corresponding to RO0 through
+		    RO15 in order.  If a given RO is unused for a corner, then
+		    its target quotient should be specified as 0.
+
+		    Each tuple list in the grouping must meet the same size
+		    requirements as those specified for qcom,mem-acc-voltage
+		    above. The tuples in a given list are ordered from the
+		    lowest corner to the highest corner.
+
+- qcom,cpr-ro-scaling-factor
+	Usage:      required if qcom,cpr-closed-loop-voltage-adjustment is
+		    specified
+	Value type: <prop-encoded-array>
+	Definition: The common definition of this property in cpr3-regulator.txt
+		    is accurate for MMSS CPR3 controllers except for this
+		    modification:
+
+		    Each tuple list must contain the number of tuples defined in
+		    the corresponding element of the qcom,cpr-corners property
+		    or the qcom,cpr-speed-bins property as opposed to the value
+		    of the qcom,cpr-fuse-corners property.
+
+- qcom,cpr-fused-closed-loop-voltage-adjustment-map
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR fused
+		    corner closed-loop offset adjustment fuse to utilize for
+		    each voltage corner in order from lowest to highest.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+		    Each tuple element must be either 0 or in the range 1 to
+		    qcom,cpr-fuse-corners.  A value of 0 signifies that no fuse
+		    based adjustment should be applied to the fuse corner.
+		    Values 1 to qcom,cpr-fuse-corners denote the specific fuse
+		    corner that should be used by a given voltage corner.
+
+- qcom,corner-allow-temp-adjustment
+	Usage:      Optional for controllers with compatible =
+		    "qcom,cpr4-msm8998-mmss-regulator"; unsupported for
+		    all others.
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR
+		    temperature adjustment feature enable state for each voltage
+		    corner in order from lowest to highest. Each element in the
+		    tuple should be either 0 (temperature adjustment not
+		    allowed) or 1 (temperature adjustment allowed).
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bin-corners property.  A single tuple may
+		    only be specified if all of the corner counts in
+		    qcom,cpr-corners are the same.
+
+- qcom,cpr-cornerX-temp-core-voltage-adjustment
+	Usage:      Required if qcom,corner-allow-temp-adjustment is specified
+		    for this CPR3 regulator.
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples for cornerX. The possible values
+		    for X are 1 to the max value specified in qcom,cpr-corners.
+		    Each tuple defines the temperature based voltage adjustment
+		    in microvolts for each temperature band from lowest to
+		    highest.  Each tuple must have a number of elements equal to
+		    (the number of elements in qcom,cpr-ctrl-temp-point-map + 1)
+
+		    The tuple list must contain qcom,cpr-fuse-combos number of
+		    tuples in which case the tuples are matched to fuse
+		    combinations 1-to-1 or qcom,cpr-speed-bins number of tuples
+		    in which case the tuples are matched to speed bins 1-to-1 or
+		    exactly 1 list which is used regardless of the fuse
+		    combination and speed bin found on a given chip.
+
+Note that the qcom,cpr-closed-loop-voltage-fuse-adjustment property is not
+meaningful for MMSS CPR3 regulator nodes since target quotients are not defined
+in fuses.
+
+=======
+Example
+=======
+
+gfx_cpr: cpr3-ctrl@838000 {
+	compatible = "qcom,cpr3-msm8996-mmss-regulator";
+	reg = <0x00838000 0x4000>, <0x00074000 0x1000>;
+	reg-names = "cpr_ctrl", "fuse_base";
+	clocks = <&clock_mmss clk_mmss_rbcpr_clk>,
+		 <&clock_mmss clk_mmss_rbcpr_ahb_clk>,
+		 <&clock_mmss clk_mmss_mmagic_ahb_clk>;
+	clock-names = "core_clk", "iface_clk", "bus_clk";
+	interrupts = <0 166 0>;
+	interrupt-names = "cpr";
+	qcom,cpr-ctrl-name = "gfx";
+
+	qcom,cpr-sensor-time = <1000>;
+	qcom,cpr-loop-time = <5000000>;
+	qcom,cpr-idle-cycles = <15>;
+	qcom,cpr-step-quot-init-min = <13>;
+	qcom,cpr-step-quot-init-max = <13>;
+	qcom,cpr-count-mode = <2>;
+
+	vdd-supply = <&pmi8994_s2>;
+	qcom,voltage-step = <5000>;
+
+	qcom,cpr-enable;
+
+	qcom,cpr-aging-ref-voltage = <905000>;
+
+	thread@0 {
+		qcom,cpr-thread-id = <0>;
+		qcom,cpr-consecutive-up = <0>;
+		qcom,cpr-consecutive-down = <2>;
+		qcom,cpr-up-threshold = <0>;
+		qcom,cpr-down-threshold = <2>;
+
+		gfx_vreg: regulator {
+			regulator-name = "gfx_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <4>;
+
+			qcom,cpr-fuse-corners = <4>;
+			qcom,cpr-fuse-combos = <1>;
+			qcom,cpr-corners = <4>;
+
+			qcom,cpr-corner-fmax-map = <1 2 3 4>;
+
+			qcom,cpr-voltage-ceiling =
+				<670000  745000  905000 1015000>;
+			qcom,cpr-voltage-floor =
+				<545000  625000  755000  855000>;
+
+			qcom,mem-acc-voltage = <1 1 2 2>;
+
+			qcom,corner-frequencies =
+				<120000000 205000000 360000000
+				 480000000>;
+
+			qcom,cpr-target-quotients =
+			      <   0    0    0    0  249  232    0  394
+				  0  422    0    0    0    0    0    0>,
+			      <   0    0    0    0  400  363    0  565
+				  0  603    0    0    0    0    0    0>,
+			      <   0    0    0    0  669  601    0  851
+				  0  905    0    0    0    0    0    0>,
+			      <   0    0    0    0  899  806    0 1084
+				  0 1149    0    0    0    0    0    0>;
+
+			qcom,cpr-ro-scaling-factor =
+			      <   0    0    0    0 2268 2004    0 2408
+				  0 2539    0    0    0    0    0    0>,
+			      <   0    0    0    0 2268 2004    0 2408
+				  0 2539    0    0    0    0    0    0>,
+			      <   0    0    0    0 2268 2004    0 2408
+				  0 2539    0    0    0    0    0    0>,
+			      <   0    0    0    0 2268 2004    0 2408
+				  0 2539    0    0    0    0    0    0>;
+
+			qcom,cpr-open-loop-voltage-fuse-adjustment =
+				<35000 0 0 0>;
+			qcom,cpr-closed-loop-voltage-adjustment =
+				<45000 0 0 0>;
+
+			qcom,cpr-fused-closed-loop-voltage-adjustment-map =
+				<2 2 0 4>;
+
+			qcom,allow-voltage-interpolation;
+			qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+			qcom,cpr-aging-max-voltage-adjustment = <25000>;
+			qcom,cpr-aging-ref-corner = <3>;
+			qcom,cpr-aging-ro-scaling-factor = <2950>;
+			qcom,cpr-aging-derate =
+				<1000 1000 1000 1000>;
+			qcom,allow-aging-voltage-adjustment = <1>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
new file mode 100644
index 0000000..af53e59
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cpr3-regulator.txt
@@ -0,0 +1,622 @@
+Qualcomm Technologies, Inc. CPR3 Regulator - Platform Independent Bindings
+
+Core Power Reduction (CPR) version 3 controllers are used by some Qualcomm
+Technologies, Inc. (QTI) SoCs to manage important voltage regulators.  CPR3
+controllers are capable of monitoring several ring oscillator sensing loops
+simultaneously.  The CPR3 controller informs software when the silicon
+conditions require the supply voltage to be increased or decreased.  On certain
+supply rails, the CPR3 controller is able to propagate the voltage increase
+or decrease requests all the way to the PMIC without software involvement.
+
+This document describes the common platform independent bindings that apply
+to all CPR3 controllers.
+
+=======================
+Required Node Structure
+=======================
+
+CPR3 regulators must be described in three levels of devices nodes.  The first
+level describes the CPR3 controller.  The second level describes one or more
+hardware threads managed by the controller.  The third level describes one or
+more logical regulators handled by each CPR thread.
+
+====================================
+First Level Nodes - CPR3 Controllers
+====================================
+
+Platform independent properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: The value to use for this property is defined in the
+		    platform specific cpr3-regulator binding documentation
+		    files.
+
+- reg
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Addresses and sizes for the memory of the CPR3 controller,
+		    the first fuse row, and optionally a register used to check
+		    if aging measurements are possible.
+
+- reg-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Address names. Must include "cpr_ctrl" and "fuse_base".
+		    "aging_allowed" may also be specified.  The strings must be
+		    specified in the same order as the corresponding addresses
+		    are specified in the reg property.
+
+- qcom,cpr-ctrl-name
+	Usage:      required
+	Value type: <string>
+	Definition: Name for this CPR controller
+
+- vdd-supply
+	Usage:      required
+	Value type: <phandle>
+	Definition: phandle of the underlying regulator device that is managed
+		    by this CPR controller.
+
+- system-supply
+	Usage:      optional
+	Value type: <phandle>
+	Definition: phandle of the system-level regulator device which the
+		    vdd-supply depends upon.  Requests for this regulator must
+		    be made before increasing the vdd-supply voltage and after
+		    decreasing the vdd-supply voltage.
+
+- mem-acc-supply
+	Usage:      optional
+	Value type: <phandle>
+	Definition: phandle of the mem-acc regulator device which is used to
+		    configure memory array circuitry settings based upon
+		    the performance operating point. Requests for this regulator
+		    must be made before decreasing the vdd-supply voltage and
+		    after increasing the vdd-supply voltage.
+
+- clocks
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: Array of clock tuples in which each tuple consists of a
+		    phandle to a clock device and a clock ID number. The CPR3
+		    core clock must be specified for some targets. See platform
+		    specific cpr3-regulator binding documentation for additional
+		    clocks that may also need to be specified.
+
+- clock-names
+	Usage:      optional
+	Value type: <stringlist>
+	Definition: Clock names.  This list must match up 1-to-1 with the clocks
+		    specified in the 'clocks' property. "core_clk" must be
+		    specified for some platforms. Other clocks may be required
+		    for some platforms.
+
+- interrupts
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: CPR interrupt specifier and optionally a hardware
+		    closed-loop ceiling interrupt specifier.
+
+- interrupt-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Interrupt names.  This list must match up 1-to-1 with the
+		    interrupts specified in the 'interrupts' property. "cpr"
+		    must be specified.  "ceiling" may be specified for some
+		    platforms.
+
+- qcom,cpr-interrupt-affinity
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of CPU phandles which correspond to the cores that
+		    the "cpr" interrupt should have affinity for.
+
+- qcom,cpr-sensor-time
+	Usage:      required
+	Value type: <u32>
+	Definition: The time in nanoseconds that each CPR sensor within the
+		    sensing loop takes to perform a measurement.
+
+- qcom,cpr-loop-time
+	Usage:      required
+	Value type: <u32>
+	Definition: The time in nanoseconds between consecutive CPR
+		    measurements.
+
+- qcom,cpr-idle-cycles
+	Usage:      required
+	Value type: <u32>
+	Definition: The number of CPR core clock cycles for the CPR controller
+		    to wait in transitional states.
+		    Supported values: 0 - 31.
+
+- qcom,voltage-step
+	Usage:      required
+	Value type: <u32>
+	Definition: The voltage in microvolts of a single step of the VDD supply
+		    regulator being controlled by CPR.
+
+- qcom,cpr-step-quot-init-min
+	Usage:      required
+	Value type: <u32>
+	Definition: The default minimum CPR step quotient value.  The step
+		    quotient is the number of additional ring oscillator ticks
+		    observed for each qcom,voltage-step increase in vdd-supply
+		    output voltage.  Supported values: 0 - 63.
+
+- qcom,cpr-step-quot-init-max
+	Usage:      required
+	Value type: <u32>
+	Definition: The default maximum CPR step quotient value.
+		    Supported values: 0 - 63.
+
+- qcom,cpr-count-mode
+	Usage:      required
+	Value type: <u32>
+	Definition: The CPR counting mode to use during CPR measurements.
+		    Supported values:
+			0 - Read all sensors at once and use the minimum
+			    quotient value observed in repeated measurements.
+			1 - Read all sensors at once and use the maximum
+			    quotient value observed in repeated measurements.
+			2 - Read each sensor once in a sequential, staggered
+			    fashion.
+
+- qcom,cpr-count-repeat
+	Usage:      optional
+	Value type: <u32>
+	Definition: The number of times to read CPR sensors during a single CPR
+		    measurement when using one of the all-at-once count modes.
+
+- qcom,cpr-enable
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the CPR3 controller
+		    should operate in closed-loop mode (i.e. CPR sensing loop
+		    enabled) as opposed to open-loop mode (i.e. CPR sensing loop
+		    disabled) by default.
+
+- qcom,cpr-aging-ref-voltage
+	Usage:      required if qcom,allow-aging-voltage-adjustment is specified
+		    for any third level nodes
+	Value type: <u32>
+	Definition: Specifies the CPR aging reference voltage in microvolts.
+		    This is the voltage that vdd-supply must be set to when
+		    performing an aging measurement.
+
+- qcom,cpr-aging-allowed-reg-mask
+	Usage:      required if "aging_allowed" register is specified
+	Value type: <u32>
+	Definition: Bitmask used to mask off the "aging_allowed" register.
+
+- qcom,cpr-aging-allowed-reg-value
+	Usage:      required if "aging_allowed" register is specified
+	Value type: <u32>
+	Definition: Value required in the masked off "aging_allowed" register
+		    bits in order for a CPR aging measurement to be possible.
+
+- qcom,cpr-panic-reg-addr-list
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: Array of register addresses to be dumped when device resets.
+
+- qcom,cpr-panic-reg-name-list
+	Usage:      optional, though only meaningful if
+		    qcom,cpr-panic-reg-addr-list is specified
+	Value type: <prop-encoded-array>
+	Definition: Address names. Must be specified in the same order
+		    as the corresponding addresses are specified in
+		    the qcom,cpr-panic-reg-addr-list property.
+
+=================================================
+Second Level Nodes - CPR Threads for a Controller
+=================================================
+
+Platform independent properties:
+- qcom,cpr-thread-id
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the hardware thread ID of this thread within the
+		    CPR controller.
+
+- qcom,cpr-consecutive-up
+	Usage:      required
+	Value type: <u32>
+	Definition: The number of consecutive CPR step up events needed to
+		    to trigger an up interrupt.  Supported values: 0 - 15.
+
+- qcom,cpr-consecutive-down
+	Usage:      required
+	Value type: <u32>
+	Definition: The number of consecutive CPR step down events needed to
+		    to trigger a down interrupt.  Supported values: 0 - 15.
+
+- qcom,cpr-up-threshold
+	Usage:      required
+	Value type: <u32>
+	Definition: The number CPR error steps required to generate an up event.
+		    Supported values: 0 - 31.
+
+- qcom,cpr-down-threshold
+	Usage:      required
+	Value type: <u32>
+	Definition: The number CPR error steps required to generate a down
+		    event.  Supported values: 0 - 31.
+
+===============================================
+Third Level Nodes - CPR Regulators for a Thread
+===============================================
+
+Platform independent properties:
+- regulator-name
+	Usage:      required
+	Value type: <string>
+	Definition: Specifies the name for this CPR3 regulator.
+
+- regulator-min-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Minimum corner value which should be 1 to represent the
+		    lowest supported corner.
+
+- regulator-max-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Maximum corner value which should be equal to largest value
+		    listed in qcom,cpr-corners.
+
+- qcom,cpr-fuse-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse corners.  See platform specific
+		    binding files for further requirements.
+
+- qcom,cpr-fuse-combos
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse combinations being supported by
+		    the device.  This value is utilized by several other
+		    properties.  Supported values are 1 up to the maximum
+		    possible for a given regulator type.  See platform specific
+		    binding files for further details.
+
+- qcom,cpr-speed-bins
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the number of speed bins being supported by the
+		    device.  This value is utilized by several other properties.
+		    Supported values are 1 up to the maximum possible for a
+		    given regulator type.  See platform specific binding files
+		    for further details.
+
+		    This property can only be utilized if the number of corners
+		    for all fuse combinations associated with a given speed bin
+		    is the same.
+
+- qcom,cpr-corners
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines how many voltage corners
+		    are to be used for each fuse combination.  The list must
+		    contain either qcom,cpr-fuse-combos number of elements in
+		    which case the corner counts are applied to fuse
+		    combinations 1-to-1 or the list must contain exactly 1
+		    element which is used regardless of the fuse combination
+		    found on a given chip.
+
+- qcom,cpr-speed-bin-corners
+	Usage:      required if qcom,cpr-speed-bins is specified
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines how many voltage corners
+		    are to be used for each speed bin.  The list must contain
+		    qcom,cpr-speed-bins number of elements.
+
+- qcom,cpr-corner-fmax-map
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the highest
+		    (i.e. maximum frequency) 1-based corner value associated
+		    with each fuse-corner.
+
+		    Each tuple must have a number of elements equal to the value
+		    of the qcom,cpr-fuse-corners property.  The elements of a
+		    tuple are ordered from lowest to highest fuse corner.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+- qcom,cpr-voltage-ceiling
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR ceiling
+		    voltage in microvolts for each voltage corner in order from
+		    lowest to highest.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+- qcom,cpr-voltage-floor
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR floor
+		    voltage in microvolts for each voltage corner in order from
+		    lowest to highest.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+- qcom,cpr-floor-to-ceiling-max-range
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the maximum
+		    allowed difference between the final floor voltage and the
+		    final ceiling voltage in microvolts for each voltage corner
+		    in order from lowest to highest.  A negative value may be
+		    specified for an element to indicate that there is no
+		    limitation of the floor to ceiling voltage range for the
+		    corresponding corner.
+
+		    In the case that the initial floor to ceiling voltage is
+		    greater than the max range specified, the floor voltage will
+		    be increased in order to satisfy the max range constraint.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+- qcom,system-voltage
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the system-supply
+		    voltage in microvolts or corners or levels for each voltage
+		    corner in order from lowest to highest.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+- qcom,corner-frequencies
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPU frequency
+		    in Hertz corresponding to each voltage corner in order from
+		    lowest to highest.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+- qcom,allow-voltage-interpolation
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that it is acceptable to use
+		    interpolated open-loop voltage values.  These values are
+		    interpolated between the open-loop voltage Fmax fuse values.
+
+- qcom,cpr-scaled-open-loop-voltage-as-ceiling
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that it is acceptable to use
+		    the interpolated open-loop voltage for each corner as the
+		    CPR ceiling voltage for each corner.
+
+- qcom,cpr-open-loop-voltage-fuse-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the open-loop
+		    voltage adjustment in microvolts for each fused voltage
+		    corner in order from lowest to highest.  This adjustment is
+		    applied to the values read from fuses before the values are
+		    used in interpolation for intermediate corners.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-corner-fmax-map above.
+
+		    The open-loop voltage for a given fuse corner corresponds to
+		    the voltage that is safe to use under all circumstances.
+		    It is used as a starting voltage for CPR and may also be
+		    specified as a ceiling voltage for CPR scaling.
+
+- qcom,cpr-open-loop-voltage-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the open-loop
+		    voltage adjustment in microvolts for each voltage corner in
+		    order from lowest to highest.  This adjustment is applied to
+		    the open-loop voltage values after they have been
+		    interpolated for intermediate corners.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+- qcom,cpr-open-loop-voltage-min-diff
+	Usage:      optional; only meaningful if the
+		    qcom,cpr-open-loop-voltage-adjustment property is specified
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the minimum
+		    allowed open-loop voltage difference in microvolts between
+		    each voltage corner and the one immediately preceding it.
+		    The elements in a tuple are ordered from the lowest to the
+		    highest corner.  The value specified for the first corner is
+		    ignored since there is no corner before it.
+
+		    Negative voltage values may be specified for this property.
+		    A negative value means that the open-loop voltage of a
+		    corner may be lower than that of the preceding corner.
+
+		    The minimum difference is enforced after the open-loop
+		    voltage values have been interpolated for intermediate
+		    corners and after adjustments have been applied.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+		    If this property is not specified, then the minimum
+		    difference is assumed to be 0 uV for all corners.
+
+- qcom,cpr-closed-loop-voltage-fuse-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the closed-loop
+		    voltage adjustment in microvolts for each fused voltage
+		    corner in order from lowest to highest.  This adjustment is
+		    applied to the values read from fuses before the values are
+		    used in interpolation for intermediate corners.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-corner-fmax-map above.
+
+		    The qcom,cpr-ro-scaling-factor property must be specified in
+		    order to utilize this property.
+
+		    The closed-loop voltage for a given fuse corner corresponds
+		    to the voltage that the CPR controller settles the VDD
+		    supply rail to based upon the programmed CPR target
+		    quotients and the current silicon conditions.
+
+- qcom,cpr-closed-loop-voltage-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the closed-loop
+		    voltage adjustment in microvolts for each voltage corner in
+		    order from lowest to highest.  This adjustment is applied to
+		    target quotient values after they have been interpolated
+		    for intermediate corners.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+		    The qcom,cpr-ro-scaling-factor property must be specified in
+		    order to utilize this property.
+
+- qcom,cpr-ro-scaling-factor
+	Usage:      required if qcom,cpr-closed-loop-voltage-fuse-adjustment,
+		    qcom,cpr-closed-loop-voltage-adjustment, or
+		    qcom,allow-aging-voltage-adjustment is specified
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists.  Each tuple defines the
+		    CPR ring oscillator (RO) scaling factor with units of QUOT/V
+		    for each RO for a given fuse corner.  Since CPR3 supports
+		    exactly 16 ROs, each tuple must contain 16 elements
+		    corresponding to RO0 through RO15 in order.  If a given RO
+		    is unused for a fuse corner, then its scaling factor may be
+		    specified as 0.
+
+		    Each tuple list must contain the number of tuples defined in
+		    the qcom,cpr-fuse-corners property.  The tuples in a given
+		    list are ordered from the lowest fuse corner to the highest
+		    fuse corner.
+
+		    The tuple list grouping must contain qcom,cpr-fuse-combos
+		    number of tuple lists in which case the lists are matched to
+		    fuse combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    tuple lists in which case the lists are matched to
+		    speed bins 1-to-1 or exactly 1 list which is used regardless
+		    of the fuse combination and speed bin found on a given chip.
+
+		    The target quotient adjustment to apply for each RO of a
+		    given corner is determined by multiplying the adjustment
+		    value in qcom,cpr-closed-loop-voltage-fuse-adjustment or
+		    qcom,cpr-closed-loop-voltage-adjustment by the relevant RO
+		    scaling factor in this property.
+
+- qcom,allow-aging-voltage-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which specifies if CPR aging adjustment
+		    should be performed for each fuse combination.
+		    Supported per-combo element values:
+			0 - do not perform CPR aging adjustment
+			1 - perform CPR aging adjustment
+
+		    The list must contain qcom,cpr-fuse-combos number of
+		    elements in which case the elements are matched to fuse
+		    combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    elements in which case the elements are matched to
+		    speed bins 1-to-1 or exactly 1 element which is used
+		    regardless of the fuse combination and speed bin found
+		    on a given chip.
+
+- qcom,allow-aging-open-loop-voltage-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which specifies if CPR aging adjustment
+		    should be applied to open-loop voltages for each fuse
+		    combination.  Note that aging adjustment must be allowed via
+		    qcom,allow-aging-voltage-adjustment in order for this
+		    property to have an effect.
+		    Supported per-combo element values:
+			0 - do not perform CPR aging adjustment
+			1 - perform CPR aging adjustment
+
+		    The list must meet the same size requirements as those
+		    specified for qcom,allow-aging-voltage-adjustment above.
+
+- qcom,cpr-aging-max-voltage-adjustment
+	Usage:      required if qcom,allow-aging-voltage-adjustment is specified
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines the maximum CPR aging
+		    voltage margin adjustment in microvolts that may be added
+		    for each fuse combination.  If this property is specified
+		    and the adjustment specified is greater than 0, then aging
+		    adjustments are required for this regulator.
+
+		    The list must meet the same size requirements as those
+		    specified for qcom,allow-aging-voltage-adjustment above.
+
+- qcom,cpr-aging-ref-corner
+	Usage:      required if qcom,allow-aging-voltage-adjustment is specified
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines the CPR reference corner
+		    for this regulator to use during aging measurements for each
+		    fuse combination.
+
+		    The list must meet the same size requirements as those
+		    specified for qcom,allow-aging-voltage-adjustment above.
+
+- qcom,cpr-aging-ro-scaling-factor
+	Usage:      required if qcom,allow-aging-voltage-adjustment is specified
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines the CPR aging ring
+		    oscillator (RO) scaling factor with units of QUOT/V to use
+		    during aging measurements for each fuse combination.
+
+		    The list must meet the same size requirements as those
+		    specified for qcom,allow-aging-voltage-adjustment above.
+
+- qcom,cpr-aging-derate
+	Usage:      optional, though only meaningful if
+		    qcom,allow-aging-voltage-adjustment is specified
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR aging
+		    derating scaling factor to apply to the closed-loop voltage
+		    margin adjustment for each corner.  The individual scaling
+		    factors have units of uV/mV and are ordered from lowest to
+		    highest corner per tuple.  For example, a value of 900
+		    specifies that the voltage adjustment for the corner should
+		    be 90% (900/1000) of that for the reference corner.
+
+		    The list and tuples must meet the same size requirements as
+		    those specified for qcom,cpr-voltage-ceiling above.
+
+		    If this property is not specified, then it is assumed that
+		    no corners require derating (i.e. the scaling factor would
+		    be 1000).
+
+All properties specified within the core regulator framework can also be used in
+third level nodes.  These bindings can be found in:
+Documentation/devicetree/bindings/regulator/regulator.txt.
+
+See platform specific cpr3-regulator binding documentation files for examples.
diff --git a/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
new file mode 100644
index 0000000..29bb2d3
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cpr4-apss-regulator.txt
@@ -0,0 +1,522 @@
+Qualcomm Technologies, Inc. CPR4 Regulator - APSS Specific Bindings
+
+APSS CPR4 controllers each support one CPR thread that monitors the voltage of
+a pair of application processor (APSS) clusters that are powered by a shared
+regulator supply. They also have a hardware channel to use these requests to
+directly change the supply voltage at the PMIC via the SPM without software
+intervention.
+
+APSS CPR4 controllers also have to take into account the state of the memory
+array power mux (APM) when scaling voltage to ensure that memory always receives
+a sufficiently high voltage.
+
+Both CPR open-loop voltages and CPR target quotients are stored in hardware
+fuses for APSS CPR4 controllers.
+
+This document describes the APSS specific CPR4 bindings.
+
+=======================
+Required Node Structure
+=======================
+
+CPR4 regulators must be described in three levels of devices nodes.  The first
+level describes the CPR4 controller.  The second level describes one or more
+hardware threads managed by the controller.  The third level describes one or
+more logical regulators handled by each CPR thread.
+
+All platform independent cpr3-regulator binding guidelines defined in
+cpr3-regulator.txt also apply to cpr4-apss-regulator devices.
+
+====================================
+First Level Nodes - CPR4 Controllers
+====================================
+
+APSS specific properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be one of the following:
+		    "qcom,cpr4-msm8953-apss-regulator";
+
+- interrupts
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: CPR interrupt specifier.
+
+- interrupt-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Interrupt names.  This list must match up 1-to-1 with the
+		    interrupts specified in the 'interrupts' property. "cpr"
+		    must be specified.
+
+- qcom,apm-ctrl
+	Usage:      required on systems that need APM management
+	Value type: <phandle>
+	Definition: phandle of memory array power mux (APM) controller device
+		    node for the APM that is used by the APSS VDD supply
+
+- qcom,apm-threshold-voltage
+	Usage:      required if qcom,apm-ctrl is specified
+	Value type: <u32>
+	Definition: Specifies the APM threshold voltage in microvolts.  If the
+		    vdd-supply voltage is greater than or equal to this level,
+		    then the APM is switched to use the vdd-supply. If the
+		    vdd-supply voltage is below this level, then the APM is
+		    switched to use the system-supply.
+
+- qcom,apm-hysteresis-voltage
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the voltage delta in microvolts between the APM
+		    threshold voltage and the highest corner open-loop voltage
+		    which may be used as the ceiling for the corner.  If this
+		    property is not specified, then a value of 0 is assumed.
+
+- qcom,cpr-hw-closed-loop
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the APSS CPR4 controller
+		    should operate in hardware closed-loop mode as opposed to
+		    software closed-loop mode.
+
+- vdd-limit-supply
+	Usage:      required
+	Value type: <phandle>
+	Definition: phandle of the VDD supply limit regulator which controls the
+		    CPR ceiling and floor voltages when operating in hardware
+		    closed-loop mode.
+
+- qcom,cpr-down-error-step-limit
+	Usage:      required
+	Value type: <u32>
+	Definition: CPR4 hardware closed-loop down error step limit which
+		    defines the maximum number of vdd-supply regulator steps
+		    that the voltage may be reduced as the result of a single
+		    CPR measurement.
+
+- qcom,cpr-up-error-step-limit
+	Usage:      required
+	Value type: <u32>
+	Definition: CPR4 hardware closed-loop up error step limit which defines
+		    the maximum number of vdd-supply regulator steps that the
+                    voltage may be increased as the result of a single
+		    CPR measurement.
+
+- qcom,cpr-saw-use-unit-mV
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the unit used in SAW PVC
+		    interface is mV. Use this for vdd-supply regulators which
+		    do not use PMIC voltage control register LSBs per actually
+		    unique PMIC regulator output voltage.
+
+- qcom,cpr-temp-point-map
+	Usage:      required if qcom,corner-allow-temp-adjustment is specified
+		    for at least one of the CPR3 regulators.
+	Value type: <prop-encoded-array>
+	Definition: The temperature points in decidegrees Celsius which indicate
+		    the range of temperature bands supported. If t1, t2, and t3
+		    are the temperature points, then the temperature bands are:
+		    (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).
+
+- qcom,cpr-initial-temp-band
+	Usage:      required if qcom,corner-allow-temp-adjustment is specified
+		    for at least one of the CPR3 regulators.
+	Value type: <u32>
+	Definition: The initial temp band considering 0-based index at which
+		    the baseline target quotients are derived and fused.
+
+- qcom,cpr-step-quot-fixed
+	Usage:      optional
+	Value type: <u32>
+	Definition: Fixed step quotient value used by controller for applying
+		    the SDELTA margin adjustments on the programmed target
+		    quotient values. The step quotient is the number of
+		    additional ring oscillator ticks observed for each
+		    qcom,voltage-step increase in vdd-supply output voltage.
+		    Supported values: 0 - 63.
+
+- qcom,cpr-voltage-settling-time
+	Usage:      optional
+	Value type: <u32>
+		    The time in nanoseconds that it takes for the vdd-supply
+		    voltage to settle after being increased or decreased by
+		    qcom,voltage-step microvolts. This is used as the wait
+		    time after applying SDELTA voltage margin adjustments.
+
+=================================================
+Second Level Nodes - CPR Threads for a Controller
+=================================================
+
+APSS specific properties:
+N/A
+
+===============================================
+Third Level Nodes - CPR Regulators for a Thread
+===============================================
+
+APSS specific properties:
+- qcom,cpr-fuse-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse corners. This value must be 4
+		    for APSS. These fuse corners are: LowSVS, SVS, Nominal,
+		    and Turbo.
+
+- qcom,cpr-fuse-combos
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse combinations being supported by
+		    the device.  This value is utilized by several other
+		    properties.  Supported values are 1 up to the maximum
+		    possible for a given regulator type.  For APSS the maximum
+		    supported value is 64.  The first 8 fuse combos correspond
+		    to speed bin fuse value 0 along with CPR revision fuse
+		    values 0 to 7.  The second 8 fuse combos correspond to speed
+		    bin fuse value 1 along with CPR revision fuse values 0 to 7.
+		    The last 8 fuse combos correspond to speed bin fuse value 7
+		    along with CPR revision fuse values 0 to 7.
+
+- qcom,cpr-speed-bins
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the number of speed bins being supported by the
+		    device.  This value is utilized by several other properties.
+		    Supported values are 1 up to the maximum possible for a
+		    given regulator type.  For APSS the maximum supported value
+		    is 8.
+
+- qcom,mem-acc-voltage
+	Usage:      required if mem-acc-supply is specified for the CPR4 controller
+		    containing this regulator
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the mem-acc-supply
+		    corner for each voltage corner in order from lowest to highest.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+- qcom,allow-quotient-interpolation
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that it is acceptable to use
+		    interpolated CPR target quotient values.  These values are
+		    interpolated between the target quotient Fmax fuse values.
+
+- qcom,corner-allow-core-count-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR core
+		    count adjustment feature enable state for each voltage
+		    corner in order from lowest to highest. Each element in
+		    the tuple should be either 0 (per-core-count adjustment
+		    not allowed) or 1 (per-core-count adjustment allowed).
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+- qcom,max-core-count
+	Usage:      required if qcom,corner-allow-core-count-adjustment is
+		    specified for this CPR3 regulator.
+	Value type: <u32>
+	Definition: The maximum number of cores considered for core-count vmin
+		    adjustments specified for this regulator voltage corners.
+
+- qcom,corner-allow-temp-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR
+		    temperature adjustment feature enable state for each voltage
+		    corner in order from lowest to highest. Each element in the
+		    tuple should be either 0 (temperature adjustment not
+		    allowed) or 1 (temperature adjustment allowed).
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+- qcom,cpr-cornerX-temp-core-voltage-adjustment
+	Usage:      required if qcom,corner-allow-core-count-adjustment
+		    specified for this CPR3 regulator.
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists for cornerX. The possible
+		    values for X are 1 to the max value specified in
+		    qcom,cpr-corners. Each tuple defines the temperature based
+		    voltage adjustment in microvolts for each temperature band
+		    from lowest to highest for a given number of online cores.
+		    Each tuple must have a number of elements equal to either
+		    (the number of elements in qcom,cpr-ctrl-temp-point-map
+		    + 1), if qcom,cpr-ctrl-temp-point-map is specified, or 1.
+
+		    Each tuple list must contain a number of tuples equal to
+		    either qcom,max-core-count, if qcom,max-core-count is
+		    specified, or 1. The tuples should be ordered from lowest
+		    to highest core count.
+
+		    The tuple list grouping must contain qcom,cpr-fuse-combos
+		    number of tuple lists in which case the lists are matched to
+		    fuse combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    tuple lists in which case the lists are matched to
+		    speed bins 1-to-1 or exactly 1 list which is used regardless
+		    of the fuse combination and speed bin found on a given chip.
+
+- qcom,cpr-num-boost-cores
+	Usage:      required if qcom,allow-boost specified for this CPR3
+		    regulator.
+	Value type: <u32>
+	Definition: Integer value indicates that voltage boost will be applied
+		    when the number of online cores become this value.
+
+- qcom,cpr-boost-temp-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the temperature
+		    based voltage adjustment to boost voltage in microvolts
+		    for each temperature band in order from lowest to highest.
+
+		    The number of elements in each tuple should be equal to either
+		    (the number of elements in qcom,cpr-ctrl-temp-point-map
+		    + 1), if qcom,cpr-ctrl-temp-point-map is specified, or 1.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+- qcom,allow-boost
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which specifies if the voltage boost
+		    feature should be enabled for each fuse combination.
+		    Supported per-combo element values:
+			0 - voltage boost feature disabled
+			1 - voltage boost feature enabled
+
+		    The list must contain qcom,cpr-fuse-combos number of
+		    elements in which case the elements are matched to fuse
+		    combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    elements in which case the elements are matched to
+		    speed bins 1-to-1 or exactly 1 element which is used
+		    regardless of the fuse combination and speed bin found
+		    on a given chip.
+
+- qcom,cpr-boost-voltage-fuse-adjustment
+	Usage:      optional
+	Value type: <u32>
+	Definition: A list of integers which defines the voltage adjustment
+		    in microvolts for the fused boost voltage. This adjustment
+		    is applied to the values read from boost fuses.
+
+		    The list must contain qcom,cpr-fuse-combos number of
+		    elements in which case the elements are matched to fuse
+		    combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    elements in which case the elements are matched to
+		    speed bins 1-to-1 or exactly 1 element which is used
+		    regardless of the fuse combination and speed bin found
+		    on a given chip.
+
+- qcom,cpr-misc-fuse-voltage-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists where each tuple defines
+		    the voltage adjustments in microvolts for each voltage
+		    corner in order from lowest to highest. This adjustment is
+		    applied to both open-loop and closed-loop voltages.
+
+		    Each tuple list must contain a number of tuples equal to
+		    2 to the power of the number of bits selected for misc
+		    voltage adj fuse definition. For MSM8953 the tuple
+		    list must contain 2 tuples for the 1-bit misc fuse.
+		    Tuples in a list should be specified in ascending order
+		    according to the misc fuse value assuming that the fuse
+		    is treated like an unsigned integer.
+
+		    The tuple list grouping must contain qcom,cpr-speed-bins
+		    number of tuple lists in which case the lists are matched to
+		    speed bins 1-to-1 or exactly 1 list which is used regardless
+		    of the speed bin found on a given chip.
+
+=======
+Example
+=======
+
+apc_cpr: cpr4-ctrl@b018000 {
+	compatible = "qcom,cpr4-msm8953-apss-regulator";
+	reg = <0xb018000 0x4000>, <0xa4000 0x1000>;
+	reg-names = "cpr_ctrl", "fuse_base";
+	interrupts = <GIC_SPI 15 IRQ_TYPE_EDGE_RISING>;
+	interrupt-names = "cpr";
+
+	qcom,cpr-ctrl-name = "apc";
+
+	qcom,cpr-sensor-time = <1000>;
+	qcom,cpr-loop-time = <5000000>;
+	qcom,cpr-idle-cycles = <15>;
+	qcom,cpr-step-quot-init-min = <13>;
+	qcom,cpr-step-quot-init-max = <13>;
+	qcom,cpr-count-mode = <2>;		/* Staggered */
+	qcom,cpr-down-error-step-limit = <1>;
+	qcom,cpr-up-error-step-limit = <1>;
+
+	qcom,apm-ctrl = <&apc_apm>;
+	qcom,apm-threshold-voltage = <848000>;
+	qcom,apm-hysteresis-voltage = <5000>;
+
+	vdd-supply = <&pm8953_s5>;
+	qcom,voltage-step = <5000>;
+	vdd-limit-supply = <&pm8953_s5_limit>;
+	mem-acc-supply = <&apc_mem_acc_vreg>;
+
+	qcom,cpr-enable;
+	qcom,cpr-hw-closed-loop;
+
+	qcom,cpr-temp-point-map = <0 250 850>;
+	qcom,cpr-initial-temp-band = <3>;
+	qcom,cpr-step-quot-fixed = <16>;
+	qcom,cpr-voltage-settling-time = <1600>;
+
+	qcom,cpr-panic-reg-addr-list =
+			<0xb1d2c18 0xb1d2900 0x0b1112b0 0xb018798>;
+	qcom,cpr-panic-reg-name-list =
+			"CCI_SAW4_PMIC_STS", "CCI_SAW4_VCTL",
+			"APCS_ALIAS0_APM_CTLER_STATUS",
+			"APCS0_CPR_CORE_ADJ_MODE_REG";
+
+	thread@0 {
+		qcom,cpr-thread-id = <0>;
+		qcom,cpr-consecutive-up = <1>;
+		qcom,cpr-consecutive-down = <1>;
+		qcom,cpr-up-threshold = <1>;
+		qcom,cpr-down-threshold = <1>;
+
+		apc_vreg: regulator {
+			regulator-name = "apc_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <8>;
+
+			qcom,cpr-fuse-corners = <4>;
+			qcom,cpr-fuse-combos = <8>;
+			qcom,cpr-speed-bins = <1>;
+			qcom,cpr-corners = <8>;
+
+			qcom,cpr-corner-fmax-map = <1 2 4 8>;
+
+			qcom,cpr-voltage-ceiling =
+				<645000  720000 790000  865000 920000
+				 990000 1065000 1065000>;
+
+			qcom,cpr-voltage-floor =
+				<590000  625000 690000  755000 800000
+				 855000  920000 920000>;
+
+			qcom,mem-acc-voltage = <1 1 2 2 2 2 2 2>;
+
+			qcom,corner-frequencies =
+				<652800000 1036800000 1401600000
+				1689600000 1843200000 1958400000
+				2150400000 2208000000>;
+
+			qcom,cpr-misc-fuse-voltage-adjustment =
+				/* Speed bin 0; misc fuse 0..1 */
+				<    0     0     0     0
+				     0     0     0     0>,
+				<    0     0 30000     0
+				     0     0     0     0>;
+
+			qcom,allow-voltage-interpolation;
+			qcom,allow-quotient-interpolation;
+			qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+
+			qcom,corner-allow-temp-adjustment =
+					<0 0 0 1 0 1 1 1>;
+
+			qcom,corner-allow-core-count-adjustment =
+					<0 0 0 0 1 1 1 1>;
+			qcom,max-core-count = <8>;
+			qcom,cpr-corner4-temp-core-voltage-adjustment =
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>,
+				<(-20000) (-10000) (-5000) 0>;
+			qcom,cpr-corner5-temp-core-voltage-adjustment =
+				<(-50000) (-50000) (-50000) (-50000)>,
+				<(-50000) (-50000) (-50000) (-50000)>,
+				<(-40000) (-40000) (-40000) (-40000)>,
+				<(-40000) (-40000) (-40000) (-40000)>,
+				<(-30000) (-30000) (-30000) (-30000)>,
+				<(-30000) (-30000) (-30000) (-30000)>,
+				<(-20000) (-20000) (-20000) (-20000)>,
+				<(-20000) (-20000) (-20000) (-20000)>;
+			qcom,cpr-corner6-temp-core-voltage-adjustment =
+				<(-50000) (-40000) (-30000) (-20000)>,
+				<(-50000) (-40000) (-30000) (-20000)>,
+				<(-40000) (-30000) (-20000) (-10000)>,
+				<(-40000) (-30000) (-20000) (-10000)>,
+				<(-30000) (-20000) (-10000)  (-5000)>,
+				<(-30000) (-20000) (-10000)  (-5000)>,
+				<(-20000) (-10000)  (-5000)       0 >,
+				<(-20000) (-10000)  (-5000)       0 >;
+			qcom,cpr-corner7-temp-core-voltage-adjustment =
+				<(-50000) (-40000) (-30000) (-20000)>,
+				<(-50000) (-40000) (-30000) (-20000)>,
+				<(-40000) (-30000) (-20000) (-10000)>,
+				<(-40000) (-30000) (-20000) (-10000)>,
+				<(-30000) (-20000) (-10000)  (-5000)>,
+				<(-30000) (-20000) (-10000)  (-5000)>,
+				<(-20000) (-10000)  (-5000)       0 >,
+				<(-20000) (-10000)  (-5000)       0 >;
+			qcom,cpr-corner8-temp-core-voltage-adjustment =
+				<(-50000) (-40000) (-30000) (-20000)>,
+				<(-50000) (-40000) (-30000) (-20000)>,
+				<(-40000) (-30000) (-20000) (-10000)>,
+				<(-40000) (-30000) (-20000) (-10000)>,
+				<(-30000) (-20000) (-10000)  (-5000)>,
+				<(-30000) (-20000) (-10000)  (-5000)>,
+				<(-20000) (-10000)  (-5000)       0 >,
+				<(-20000) (-10000)  (-5000)       0 >;
+
+			qcom,cpr-num-boost-cores = <4>;
+			qcom,cpr-boost-voltage-fuse-adjustment = <(-10000)>;
+			qcom,cpr-boost-temp-adjustment =
+				<(-20000) (-15000) (-10000) 0>;
+			qcom,allow-boost =
+				<1>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/regulator/cpr4-mmss-ldo-regulator.txt b/Documentation/devicetree/bindings/regulator/cpr4-mmss-ldo-regulator.txt
new file mode 100644
index 0000000..8515285
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cpr4-mmss-ldo-regulator.txt
@@ -0,0 +1,321 @@
+Qualcomm Technologies, Inc. CPR4 Regulator - MMSS LDO Specific Bindings
+
+MMSS LDO CPR4 controllers each support one CPR thread that monitors the voltage
+of the graphics processor (MMSS) supply regulator.  The CPR open-loop voltages
+are stored in hardware fuses for MMSS CPR4 controllers.  However, the CPR target
+quotients must be defined in device tree.
+
+This document describes the MMSS LDO specific CPR4 bindings.
+
+=======================
+Required Node Structure
+=======================
+
+CPR3 regulators must be described in three levels of devices nodes.  The first
+level describes the CPR3 controller.  The second level describes exacly one
+hardware thread managed by the controller.  The third level describes one or
+more logical regulators handled by the CPR thread.
+
+All platform independent cpr3-regulator binding guidelines defined in
+cpr3-regulator.txt also apply to cpr4-mmss-ldo-regulator devices.
+
+====================================
+First Level Nodes - CPR3 Controllers
+====================================
+
+MMSS LDO specific properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be the following:
+		    "qcom,cpr4-sdm660-mmss-ldo-regulator".
+
+- clocks
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Array of clock tuples in which each tuple consists of a
+		    phandle to a clock device and a clock ID number.  The
+		    following clocks must be specified: MMSS RBCPR and MMSS
+		    RBCPR AHB.
+
+- clock-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Clock names.  This list must match up 1-to-1 with the clocks
+		    specified in the 'clocks' property. "core_clk", and "bus_clk"
+		    must be specified.
+
+- qcom,cpr-step-quot-fixed
+	Usage:      Optional
+	Value type: <u32>
+	Definition: Fixed step quotient value used by controller for applying
+		    the SDELTA margin adjustments on the programmed target
+		    quotient values. The step quotient is the number of
+		    additional ring oscillator ticks observed for each
+		    qcom,voltage-step increase in vdd-supply output voltage.
+		    Supported values: 0 - 63.
+
+=================================================
+Second Level Nodes - CPR Threads for a Controller
+=================================================
+
+MMSS specific properties:
+N/A
+
+===============================================
+Third Level Nodes - CPR Regulators for a Thread
+===============================================
+
+MMSS specific properties:
+- qcom,cpr-fuse-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse corners. This value must be 6
+		    for sdm660 GFX LDO. These fuse corners are: MinSVS,
+		    LowSVS, SVS, SVSP, NOM and NOMP. The open-loop voltage fuses
+		    are allocated for LowSVS, SVS, NOM and NOMP corners. The
+		    open-loop voltages for MinSVS and SVSP are derived by
+		    applying fixed offset from LowSVS and NOM open-loop voltages
+		    respectively. The closed-loop offset voltage fuses are
+		    allocated for LowSVS, SVS, NOM and NOMP corners. The MinSVS
+		    and SVSP corners use the closed-loop offset voltage fuses of
+		    LowSVS and NOM corners respectively.
+
+- qcom,cpr-fuse-combos
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse combinations being supported by
+		    the device.  This value is utilized by several other
+		    properties.  Supported values are 1 up to the maximum
+		    possible for a given regulator type.  For MMSS the maximum
+		    supported value is 8.  These combos correspond to CPR
+		    revision fuse values from 0 to 7 in order.
+
+- qcom,mem-acc-voltage
+	Usage:      required if mem-acc-supply is specified for the CPR3 controller
+		    containing this CPR3 regulator
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the mem-acc-supply
+		    corner for each voltage corner in order from lowest to highest.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+- qcom,cpr-target-quotients
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists.  Each tuple defines the
+		    CPR target quotient for each ring oscillator (RO) for a
+		    given corner.  Since CPR3 supports exactly 16 ROs, each
+		    tuple must contain 16 elements corresponding to RO0 through
+		    RO15 in order.  If a given RO is unused for a corner, then
+		    its target quotient should be specified as 0.
+
+		    Each tuple list in the grouping must meet the same size
+		    requirements as those specified for qcom,mem-acc-voltage
+		    above. The tuples in a given list are ordered from the
+		    lowest corner to the highest corner.
+
+- qcom,cpr-ro-scaling-factor
+	Usage:      required if qcom,cpr-closed-loop-voltage-adjustment is
+		    specified
+	Value type: <prop-encoded-array>
+	Definition: The common definition of this property in cpr3-regulator.txt
+		    is accurate for MMSS CPR3 controllers except for this
+		    modification:
+
+		    Each tuple list must contain the number of tuples defined in
+		    the corresponding element of the qcom,cpr-corners property
+		    or the qcom,cpr-speed-bins property as opposed to the value
+		    of the qcom,cpr-fuse-corners property.
+
+- qcom,cpr-fused-closed-loop-voltage-adjustment-map
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR fused
+		    corner closed-loop offset adjustment fuse to utilize for
+		    each voltage corner in order from lowest to highest.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bins property.  A single tuple may only
+		    be specified if all of the corner counts in qcom,cpr-corners
+		    are the same.
+
+		    Each tuple element must be either 0 or in the range 1 to
+		    qcom,cpr-fuse-corners.  A value of 0 signifies that no fuse
+		    based adjustment should be applied to the fuse corner.
+		    Values 1 to qcom,cpr-fuse-corners denote the specific fuse
+		    corner that should be used by a given voltage corner.
+
+- qcom,cpr-corner-allow-ldo-mode
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the LDO mode
+		    allowed state for each voltage corner in order from lowest
+		    to highest. Each element in the tuple should be either
+		    0 (LDO mode not allowed) or 1 (LDO mode allowed).
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bin-corners property.  A single tuple may
+		    only be specified if all of the corner counts in
+		    qcom,cpr-corners are the same.
+
+- qcom,cpr-corner-allow-closed-loop
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR
+		    closed-loop operation allowed state for each voltage corner
+		    in order from lowest to highest. Each element in the tuple
+		    should be either 0 (CPR closed-loop operation not allowed)
+		    or 1 (CPR closed-loop operation allowed).
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the
+		    corresponding element of the qcom,cpr-corners property or
+		    the qcom,cpr-speed-bin-corners property.  A single tuple may
+		    only be specified if all of the corner counts in
+		    qcom,cpr-corners are the same.
+
+Note that the qcom,cpr-closed-loop-voltage-fuse-adjustment property is not
+meaningful for MMSS LDO CPR3 regulator nodes since target quotients are not
+defined in fuses.
+
+=======
+Example
+=======
+
+gfx_cpr: cpr4-ctrl@05061000 {
+	compatible = "qcom,cpr4-sdm660-mmss-ldo-regulator";
+	reg = <0x05061000 0x4000>, <0x00784000 0x1000>;
+	reg-names = "cpr_ctrl", "fuse_base";
+	interrupts = <GIC_SPI 285 IRQ_TYPE_EDGE_RISING>;
+	interrupt-names = "cpr";
+	qcom,cpr-ctrl-name = "gfx";
+
+	qcom,cpr-sensor-time = <1000>;
+	qcom,cpr-loop-time = <5000000>;
+	qcom,cpr-idle-cycles = <15>;
+	qcom,cpr-step-quot-init-min = <8>;
+	qcom,cpr-step-quot-init-max = <12>;
+	qcom,cpr-count-mode = <0>;		/* All at once */
+
+	vdd-supply = <&gfx_stub_vreg>;
+	mem-acc-supply = <&gfx_mem_acc_vreg>;
+	system-supply = <&pm660l_s3_level>; /* vdd_cx */
+	qcom,voltage-step = <5000>;
+	vdd-thread0-ldo-supply = <&gfx_ldo_vreg>;
+
+	qcom,cpr-enable;
+
+	thread@0 {
+		qcom,cpr-thread-id = <0>;
+		qcom,cpr-consecutive-up = <0>;
+		qcom,cpr-consecutive-down = <2>;
+		qcom,cpr-up-threshold = <0>;
+		qcom,cpr-down-threshold = <2>;
+
+		gfx_vreg_corner: regulator {
+			regulator-name = "gfx_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <7>;
+
+			qcom,cpr-fuse-corners = <6>;
+			qcom,cpr-fuse-combos = <8>;
+			qcom,cpr-corners = <7>;
+
+			qcom,cpr-corner-fmax-map = <1 2 3 4 5 6>;
+
+			qcom,cpr-voltage-ceiling =
+				<584000  644000  724000  788000
+				 868000  924000 1068000>;
+			qcom,cpr-voltage-floor =
+				<504000  504000  596000  652000
+				 712000  744000 1068000>;
+
+			qcom,mem-acc-voltage = <1 1 1 2 2 2 2>;
+			qcom,system-voltage =
+				<RPM_SMD_REGULATOR_LEVEL_MIN_SVS>,
+				<RPM_SMD_REGULATOR_LEVEL_LOW_SVS>,
+				<RPM_SMD_REGULATOR_LEVEL_SVS>,
+				<RPM_SMD_REGULATOR_LEVEL_SVS_PLUS>,
+				<RPM_SMD_REGULATOR_LEVEL_NOM>,
+				<RPM_SMD_REGULATOR_LEVEL_NOM_PLUS>,
+				<RPM_SMD_REGULATOR_LEVEL_TURBO>;
+
+			qcom,corner-frequencies =
+				<160000000 266000000 370000000
+				 465000000 588000000 647000000
+				 800000000>;
+
+			qcom,cpr-target-quotients =
+				<0    0    0    0     0    0  185  179
+				291  299  304  319    0    0    0    0>,
+				<0    0    0    0     0    0  287  273
+				425  426  443  453    0    0    0    0>,
+				<0    0    0    0     0    0  414  392
+				584  576  608  612    0    0    0    0>,
+				<0    0    0    0     0    0  459  431
+				684  644  692  679    0    0    0    0>,
+				<0    0    0    0     0    0  577  543
+				798  768  823  810    0    0    0    0>,
+				<0    0    0    0     0    0  669  629
+				886  864  924  911    0    0    0    0>,
+				<0    0    0    0     0    0    0    0
+				 0    0    0    0     0    0    0    0>;
+
+			qcom,cpr-ro-scaling-factor =
+				<  0    0    0    0   0    0 2035 1917
+				1959 2131 2246 2253   0    0    0    0>,
+				<  0    0    0    0   0    0 2035 1917
+				1959 2131 2246 2253   0    0    0    0>,
+				<  0    0    0    0   0    0 2035 1917
+				1959 2131 2246 2253   0    0    0    0>,
+				<  0    0    0    0   0    0 2035 1917
+				1959 2131 2246 2253   0    0    0    0>,
+				<  0    0    0    0   0    0 2035 1917
+				1959 2131 2246 2253   0    0    0    0>,
+				<  0    0    0    0   0    0 2035 1917
+				1959 2131 2246 2253   0    0    0    0>,
+				<  0    0    0    0   0    0    0    0
+				   0    0    0    0   0    0    0    0>;
+
+			qcom,cpr-scaled-open-loop-voltage-as-ceiling;
+			qcom,cpr-corner-ldo-mode-allowed =
+				<1 1 1 1 1 1 0>;
+			qcom,cpr-corner-use-closed-loop =
+				<1 1 1 1 1 1 0>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt
new file mode 100644
index 0000000..ff80035
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/cprh-kbss-regulator.txt
@@ -0,0 +1,459 @@
+Qualcomm Technologies, Inc. CPRh Regulator - KBSS Specific Bindings
+
+KBSS CPRh controllers each support one CPR thread that monitors the voltage
+of a single Kryo-B CPU subystem (KBSS) cluster that is powered by a single
+regulator supply. The DCVSh block interacts with the CPRh controller for full
+hardware DCVS support.
+
+Both CPR open-loop voltages and CPR target quotients are stored in hardware
+fuses for KBSS CPRh controllers.
+
+This document describes the KBSS specific CPRh bindings.
+
+=======================
+Required Node Structure
+=======================
+
+CPRh regulators must be described in three levels of devices nodes.  The first
+level describes the CPRh controller.  The second level describes one hardware
+thread managed by the controller.  The third level describes one regulator
+handled by the CPR thread.
+
+All platform independent cpr3-regulator binding guidelines defined in
+cpr3-regulator.txt also apply to cprh-kbss-regulator devices.
+
+====================================
+First Level Nodes - CPR3 Controllers
+====================================
+
+KBSS specific properties:
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be one of the following:
+		    "qcom,cprh-msm8998-v1-kbss-regulator",
+		    "qcom,cprh-msm8998-v2-kbss-regulator",
+		    "qcom,cprh-msm8998-kbss-regulator",
+		    "qcom,cprh-sdm660-kbss-regulator".
+		    If the SoC revision is not specified, then it is assumed to
+		    be the most recent revision of MSM8998, i.e. v2.
+
+- qcom,cpr-controller-id
+	Usage:      required
+	Value type: <u32>
+	Definition: Identifies the controller number for subsystems that are managed
+		    by multiple CPR controllers. For KBSS, the supported values are 0
+		    and 1, corresponding to each cluster.
+
+- qcom,apm-threshold-voltage
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the APM threshold voltage in microvolts.  The
+		    floor to ceiling range for every corner is adjusted to ensure
+		    it does not intersect this voltage. The value of this property
+		    must match with the APM threshold voltage defined in the OSM
+		    device to ensure that if the VDD_APCC supply voltage is above
+		    this level, then the APM is switched to use VDD_APCC and if
+		    VDD_APCC is below this level, then the APM is switched to use
+		    VDD_MX.
+
+- qcom,apm-crossover-voltage
+	Usage:      required if qcom,apm-threshold-voltage is specified
+	Value type: <u32>
+	Definition: Specifies the APM crossover voltage in microvolts which
+		    corresponds to the voltage the VDD supply must be set at
+		    during an APM switch transition.
+
+- qcom,apm-hysteresis-voltage
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the voltage in microvolts used to adjust floor
+		    voltages with respect to the APM threshold voltage. This
+		    voltage is used to reduce the number of corners whose floor
+		    must be raised to ensure stable operation with manual APM
+		    switching. If this property is not specified, then a value
+		    of 0 is assumed.
+
+- qcom,mem-acc-threshold-voltage
+	Usage:      optional
+	Value type: <u32>
+	Definition: Specifies the highest memory accelerator (MEM ACC) threshold
+		    voltage in microvolts.  The floor to ceiling voltage range
+		    for every corner is adjusted to ensure that it does not
+		    intersect this voltage. The value of this property must
+		    match with the MEM ACC threshold voltage defined in the OSM
+		    device to ensure that MEM ACC settings are switched
+		    appropriately.
+
+- qcom,mem-acc-crossover-voltage
+	Usage:      required if qcom,mem-acc-threshold-voltage is specified
+	Value type: <u32>
+	Definition: Specifies the MEM ACC crossover voltage in microvolts which
+		    corresponds to the voltage the VDD supply must be set to
+		    when switching the MEM ACC configuration.
+
+- qcom,voltage-base
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the voltage in microvolts used by the CRR controller
+		    to resolve open-loop and floor voltages. In particular, this
+		    voltage is added to the programmed open-loop and floor voltages
+		    and it corresponds to the minimum supported setpoint of the
+		    vdd-supply.
+
+- qcom,cpr-saw-use-unit-mV
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the unit used in SAW PVC
+		    interface is mV. Use this for vdd-supply regulators which
+		    do not use PMIC voltage control register LSBs per actually
+		    unique PMIC regulator output voltage.
+
+- qcom,cpr-up-down-delay-time
+	Usage:      required
+	Value type: <u32>
+	Definition: The time to delay in nanoseconds between consecutive CPR
+		    measurements when the last measurement recommended
+		    increasing or decreasing the vdd-supply voltage.
+
+- qcom,cpr-down-error-step-limit
+	Usage:      required
+	Value type: <u32>
+	Definition: CPRh hardware closed-loop down error step limit which
+		    defines the maximum number of vdd-supply regulator steps
+		    that the voltage may be reduced as the result of a single
+		    CPR measurement.
+
+- qcom,cpr-up-error-step-limit
+	Usage:      required
+	Value type: <u32>
+	Definition: CPRh hardware closed-loop up error step limit which defines
+		    the maximum number of vdd-supply regulator steps that the
+                    voltage may be increased as the result of a single
+		    CPR measurement.
+
+- qcom,cpr-step-quot-fixed
+	Usage:      optional
+	Value type: <u32>
+	Definition: Fixed step quotient value used by controller for applying
+		    the SDELTA margin adjustments on the programmed target
+		    quotient values. The step quotient is the number of
+		    additional ring oscillator ticks observed for each
+		    qcom,voltage-step increase in vdd-supply output voltage.
+		    Supported values: 0 - 63.
+
+- qcom,cpr-voltage-settling-time
+	Usage:      optional
+	Value type: <u32>
+		    The time in nanoseconds that it takes for the vdd-supply
+		    voltage to settle after being increased or decreased by
+		    qcom,voltage-step microvolts. This is used as the wait
+		    time after applying SDELTA voltage margin adjustments.
+
+- qcom,cpr-corner-switch-delay-time
+	Usage:      optional
+	Value type: <u32>
+		    The time in nanoseconds that the CPR controller must delay
+		    to allow voltage settling per 1 mV of voltage change after a
+		    corner change.
+
+- qcom,cpr-hw-closed-loop
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that the KBSS CPRh controller
+		    should operate in hardware closed-loop mode as opposed to
+		    open-loop.
+
+- qcom,cpr-temp-point-map
+	Usage:      required if qcom,corner-band-allow-temp-adjustment is specified
+		    for at least one of the CPR3 regulators.
+	Value type: <prop-encoded-array>
+	Definition: The temperature points in decidegrees Celsius which indicate
+		    the range of temperature bands supported. If t1, t2, and t3
+		    are the temperature points, then the temperature bands are:
+		    (-inf, t1], (t1, t2], (t2, t3], and (t3, inf). A maximum of
+		    three temperature points can be specified to define a total
+		    of four different temperature bands.
+
+- qcom,cpr-initial-temp-band
+	Usage:      required if qcom,corner-band-allow-temp-adjustment is specified
+		    for at least one of the CPR3 regulators.
+	Value type: <u32>
+	Definition: The initial temp band considering 0-based index at which
+		    the baseline target quotients are derived and fused.
+
+=================================================
+Second Level Nodes - CPR Threads for a Controller
+=================================================
+
+KBSS specific properties:
+N/A
+
+===============================================
+Third Level Nodes - CPR Regulators for a Thread
+===============================================
+
+KBSS specific properties:
+- qcom,cpr-fuse-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse corners.  This value must be 4
+		    for KBSS.  These fuse corners are: LowSVS, SVS, Nominal,
+		    and Turbo.
+
+- qcom,cpr-fuse-combos
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of fuse combinations being supported by
+		    the device.  This value is utilized by several other
+		    properties.  Supported values are 1 up to the maximum
+		    possible for a given regulator type.  For KBSS the maximum
+		    supported value is 8.  These combos correspond to CPR
+		    revision fuse values 0 to 7 in order.
+
+- qcom,allow-quotient-interpolation
+	Usage:      optional
+	Value type: <empty>
+	Definition: Boolean flag which indicates that it is acceptable to use
+		    interpolated CPR target quotient values.  These values are
+		    interpolated between the target quotient Fmax fuse values.
+
+- qcom,cpr-corner-bands
+	Usage:      required if qcom,corner-band-allow-core-count-adjustment
+		    or qcom,corner-band-allow-temp-adjustment is specified
+		    for this CPR3 regulator.
+	Value type: <prop-encoded-array>
+	Definition: A list of integers which defines how many corner bands
+		    exist for each fuse combination. Supported values are 1 to 4.
+		    The list must contain either qcom,cpr-fuse-combos number of
+		    elements in which case the corner band counts are applied to
+		    fuse combinations 1-to-1 or the list must contain exactly 1
+		    element which is used regardless of the fuse combination
+		    found on a given chip.
+
+- qcom,cpr-speed-bin-corner-bands
+	Usage:      required if qcom,cpr-speed-bins and
+		    qcom,corner-band-allow-core-count-adjustment or
+		    qcom,corner-band-allow-temp-adjustment are specified for
+		    this CPR3 regulator.
+	Value type: <prop-encded-array>
+	Definition: A list of integers which defines how many corner bands
+		    are to be used for each speed bin. The list must contain
+		    qcom,cpr-speed-bins number of elements.
+
+- qcom,corner-band-allow-core-count-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the CPR core
+		    count adjustment feature enable state for each corner band
+		    in order from lowest to highest. Each element in the tuple
+		    should be either 0 (per-core-count adjustment not allowed)
+		    or 1 (per-core-count adjustment allowed). A maximum of four
+		    corner bands may be used to partition the corner space into
+		    contiguous corner ranges. For all corners within a corner band,
+		    the same per-core-count adjustments are applied.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the corresponding
+		    element of the qcom,cpr-corner-bands property.
+
+- qcom,max-core-count
+	Usage:      required if qcom,corner-band-allow-core-count-adjustment is
+		    specified for this CPR3 regulator.
+	Value type: <u32>
+	Definition: The maximum number of cores considered for core-count vmin
+		    adjustments specified for this regulator's corner bands.
+
+- qcom,corner-band-allow-temp-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which each define the temperature
+		    adjustment feature enable state for each corner band
+		    in order from lowest to highest. Each element in the tuple
+		    should be either 0 (temperature adjustment not allowed)
+		    or 1 (temperature adjustment allowed). A maximum of four
+		    corner bands may be used to partition the corner space into
+		    contiguous corner ranges. For all corners within a corner band,
+		    the same temperature adjustments are applied.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the corresponding
+		    element of the qcom,cpr-corner-bands property.
+
+- qcom,cpr-corner-band-map
+	Usage:      required if qcom,corner-band-allow-core-count-adjustment
+		    or qcom,corner-band-allow-temp-adjustment is specified
+		    for this CPR3 regulator.
+	Value type: <prop-encoded-array>
+	Definition: A list of integer tuples which correspond to corner numbers
+		    and define the corner bands to be used for temperature or
+		    per-core-count adjustments. The corner numbers must be specified
+		    in increasing order to result in partitioning the corner space
+		    into contiguous corner ranges. The supported tuple size is 1
+		    to 4 elements. For example, a tuple with corners defined as
+		    c1, c2, c3, c4 results in the following corner band mapping:
+
+		    [c1, c2) -> Corner Band 1
+		    [c2, c3) -> Corner Band 2
+		    [c3, c4) -> Corner Band 3
+		    [c4, inf)-> Corner Band 4
+
+		    Corners less than c1 will have no per-core-count or temperature
+		    adjustments. Adjustments associated with each corner band X are
+		    defined in the corresponding
+		    qcom,cpr-corner-bandX-temp-core-voltage-adjustment property.
+
+		    The list must contain qcom,cpr-fuse-combos number of tuples
+		    in which case the tuples are matched to fuse combinations
+		    1-to-1 or qcom,cpr-speed-bins number of tuples in which case
+		    the tuples are matched to speed bins 1-to-1 or exactly 1
+		    tuple which is used regardless of the fuse combination and
+		    speed bin found on a given chip.
+
+		    Each tuple must be of the length defined in the corresponding
+		    element of the qcom,cpr-corner-bands property.
+
+- qcom,cpr-corner-bandX-temp-core-voltage-adjustment
+	Usage:      required if qcom,corner-band-allow-core-count-adjustment
+		    is specified for this CPR3 regulator.
+	Value type: <prop-encoded-array>
+	Definition: A grouping of integer tuple lists for corner bandX. The possible
+		    values for X are 1 to 4. Each tuple defines the temperature based
+		    voltage adjustment in microvolts for each temperature band
+		    from lowest to highest for a given number of online cores. Each
+		    tuple must have a number of elements equal to either (the number
+		    of elements in qcom,cpr-temp-point-map + 1), if
+		    qcom,cpr-temp-point-map is specified, or 1.
+
+		    Each tuple list must contain a number of tuples equal to
+		    either qcom,max-core-count, if qcom,max-core-count is
+		    specified, or 1. The tuples should be ordered from lowest
+		    to highest core count.
+
+		    The tuple list grouping must contain qcom,cpr-fuse-combos
+		    number of tuple lists in which case the lists are matched to
+		    fuse combinations 1-to-1 or qcom,cpr-speed-bins number of
+		    tuple lists in which case the lists are matched to
+		    speed bins 1-to-1 or exactly 1 list which is used regardless
+		    of the fuse combination and speed bin found on a given chip.
+
+=======
+Example
+=======
+
+apc0_cpr: cprh-ctrl@179c8000 {
+	compatible = "qcom,cprh-msm8998-kbss-regulator";
+	reg = <0x179c8000 0x4000>, <0x00784000 0x1000>;
+	reg-names = "cpr_ctrl", "fuse_base";
+	clocks = <&clock_gcc clk_gcc_hmss_rbcpr_clk>;
+	clock-names = "core_clk";
+	qcom,cpr-ctrl-name = "apc0";
+	qcom,cpr-controller-id = <0>;
+
+	qcom,cpr-sensor-time = <1000>;
+	qcom,cpr-loop-time = <5000000>;
+	qcom,cpr-idle-cycles = <15>;
+	qcom,cpr-up-down-delay-time = <3000>;
+	qcom,cpr-step-quot-init-min = <11>;
+	qcom,cpr-step-quot-init-max = <13>;
+	qcom,cpr-count-mode = <2>;		/* Staggered */
+	qcom,cpr-down-error-step-limit = <1>;
+	qcom,cpr-up-error-step-limit = <1>;
+	qcom,cpr-corner-switch-delay-time = <1600>;
+	qcom,cpr-voltage-settling-time = <1600>;
+
+	qcom,apm-threshold-voltage = <800000>;
+	qcom,apm-hysteresis-voltage = <4000>;
+	qcom,voltage-step = <4000>;
+	qcom,voltage-base = <352000>;
+	qcom,cpr-saw-use-unit-mV;
+
+	qcom,cpr-enable;
+	qcom,cpr-hw-closed-loop;
+
+	qcom,cpr-initial-temp-band = <3>;
+	qcom,cpr-temp-point-map = <0 25 85>;
+
+	thread@0 {
+		qcom,cpr-thread-id = <0>;
+		qcom,cpr-consecutive-up = <2>;
+		qcom,cpr-consecutive-down = <2>;
+		qcom,cpr-up-threshold = <0>;
+		qcom,cpr-down-threshold = <0>;
+
+		apc0_pwrcl_vreg: regulator-pwrcl {
+			regulator-name = "apc0_pwrcl_corner";
+			regulator-min-microvolt = <1>;
+			regulator-max-microvolt = <24>;
+
+			qcom,cpr-fuse-corners = <4>;
+			qcom,cpr-fuse-combos = <1>;
+			qcom,cpr-corners = <23>;
+
+			qcom,cpr-corner-fmax-map = <7 10 17 23>;
+
+			qcom,cpr-voltage-ceiling =
+				<632000  632000  632000  632000  632000
+				 632000  632000  700000  700000  700000
+				 828000  828000  828000  828000  828000
+				 828000  828000 1024000 1024000 1024000
+				1024000 1024000 1024000>;
+
+			qcom,cpr-voltage-floor =
+				<572000  572000  572000  572000  572000
+				 572000  572000  568000  568000  568000
+				 684000  684000  684000  684000  684000
+				 684000  684000  856000  856000  856000
+				 856000  856000  856000>;
+
+			qcom,corner-frequencies =
+				<300000000  345600000  422400000
+				 499200000  576000000  633600000
+				 710400000  806400000  883200000
+				 960000000 1036800000 1113600000
+				1190400000 1248000000 1324800000
+				1401600000 1478400000 1497600000
+				1574400000 1651200000 1728000000
+				1804800000 1881600000>;
+			};
+
+			qcom,cpr-corner-bands = <4>;
+			qcom,corner-band-allow-core-count-adjustment = <1 1 1 1>;
+			qcom,corner-band-allow-temp-adjustment = <1 0 0 0>;
+			qcom,cpr-corner-band-map = <7 14 18 20>;
+			qcom,max-core-count = <4>;
+			qcom,cpr-corner-band1-temp-core-voltage-adjustment =
+				<(-24000) (-20000) (-20000) (-16000)>,
+				<(-16000) (-16000) (-12000) (-8000)>,
+				<(-8000)  (-8000)  (-4000)  (-4000)>,
+				<0     0     0     0>;
+			qcom,cpr-corner-band2-temp-core-voltage-adjustment =
+				<(-36000) 0 0 0>,
+				<(-32000) 0 0 0>,
+				<(-24000) 0 0 0>,
+				<    0 0 0 0>;
+			qcom,cpr-corner-band3-temp-core-voltage-adjustment =
+				<(-40000) 0 0 0>,
+				<(-36000) 0 0 0>,
+				<(-32000) 0 0 0>,
+				<    0 0 0 0>;
+			qcom,cpr-corner-band4-temp-core-voltage-adjustment =
+				<(-44000) 0 0 0>,
+				<(-32000) 0 0 0>,
+				<(-24000) 0 0 0>,
+				<    0 0 0 0>;
+		};
+	};
+}
diff --git a/Documentation/devicetree/bindings/regulator/kryo-regulator.txt b/Documentation/devicetree/bindings/regulator/kryo-regulator.txt
new file mode 100644
index 0000000..ad630ee
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/kryo-regulator.txt
@@ -0,0 +1,151 @@
+Qualcomm Technologies, Inc. Kryo Regulator
+
+The Kryo regulator device is designed for QTI's application processor cores
+that can draw power from a common power rail via a block head switch (BHS)
+or from a configurable LDO when certain power constraints are met. By using
+Kryo regulators, the CPU subsystem is capable of selecting LDO or BHS modes
+for each cluster.
+
+=================
+First Level Nodes
+=================
+
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: must be "qcom,kryo-regulator"
+
+- reg
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Specifies addresses and sizes for the memory mapped regions of the
+		    power gate and LDO registers per cluster and per shared power rail
+		    domain. The third address must correspond to the register space
+		    containing the CPU subsystem HW revision register.
+
+- reg-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Identifies the reg property entries. Must contain the following
+		    strings: "pm-apc", "pm-apcc", and "apcs-csr".
+
+- qcom,ldo-default-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: The default value for LDO voltage in microvolts. Must be
+		    between 520000 uV and 865000 uV.
+
+- qcom,retention-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: The value for retention voltage in microvolts. Must be
+		    between 520000 and 865000 uV.
+
+- qcom,ldo-headroom-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: Voltage in microvolts required between the VDD_APCC voltage supply
+		    and the LDO output in order for the LDO to be operational.
+
+- qcom,vref-functional-step-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: The voltage change in microvolts for each step in the
+		    functional LDO set point.
+
+- qcom,vref-functional-min-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: The minimum configurable functional LDO voltage in microvolts.
+
+- qcom,vref-retention-step-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: The voltage change in microvolts for each step in the
+		    retention LDO set point.
+
+- qcom,vref-retention-min-voltage
+	Usage:      required
+	Value type: <u32>
+	Definition: The minimum configurable retention LDO voltage in microvolts.
+
+- qcom,ldo-config-init
+	Usage:      required
+	Value type: <u32>
+	Definition: Initialization value used to configure the Kryo LDO hardware.
+
+- qcom,apm-config-init
+	Usage:      required
+	Value type: <u32>
+	Definition: Initialization value used to configure the Kryo APM hardware.
+
+- qcom,cluster-num
+	Usage:      required
+	Value type: <u32>
+	Definition: Specifies the number of the cluster this regulator controls.
+
+==================
+Second Level Nodes
+==================
+The second level node represents a regulator which enables control of LDO retention
+mode per Kryo regulator device. This second level node is required.
+
+The following regulator framework properties must be specified for both first and
+second level nodes: regulator-name, regulator-min-microvolt, and regulator-max-microvolt.
+
+Additional core regulator framework properties may also be used. For a full list of
+supported bindings refer to Documentation/devicetree/bindings/regulator/regulator.txt.
+
+=======
+Example
+=======
+
+	kryo0_vreg: regulator@99a2000 {
+		compatible = "qcom,kryo-regulator";
+		regulator-name = "kryo0";
+		reg = <0x99a2000 0x1000>, <0x99e0000 0x1000>,
+		      <0x9820000 0x1000>;
+		reg-names = "pm-apc", "pm-apcc", "apcs-csr";
+		regulator-min-microvolt = <520000>;
+		regulator-max-microvolt = <865000>;
+		qcom,ldo-default-voltage = <750000>;
+		qcom,retention-voltage = <520000>;
+		qcom,ldo-headroom-voltage = <150000>;
+		qcom,vref-functional-step-voltage = <4100>;
+		qcom,vref-functional-min-voltage = <299000>;
+		qcom,vref-retention-step-voltage = <4554>;
+		qcom,vref-retention-min-voltage = <332000>;
+		qcom,ldo-config-init = <0xf1f0e471>;
+		qcom,apm-config-init = <0x0>;
+		qcom,cluster-num = <0>;
+		kryo0_retention_vreg: regulator {
+			regulator-name = "kryo0-retention";
+			regulator-min-microvolt = <332000>;
+			regulator-max-microvolt = <865000>;
+		};
+	};
+
+	kryo1_vreg: regulator@99d2000 {
+		compatible = "qcom,kryo-regulator";
+		regulator-name = "kryo1";
+		reg = <0x99d2000 0x1000>, <0x99e0000 0x1000>,
+		      <0x9820000 0x1000>;
+		reg-names = "pm-apc", "pm-apcc", "apcs-csr";
+		regulator-min-microvolt = <520000>;
+		regulator-max-microvolt = <865000>;
+		qcom,ldo-default-voltage = <750000>;
+		qcom,retention-voltage = <520000>;
+		qcom,ldo-headroom-voltage = <150000>;
+		qcom,vref-functional-step-voltage = <4063>;
+		qcom,vref-functional-min-voltage = <296000>;
+		qcom,vref-retention-step-voltage = <4554>;
+		qcom,vref-retention-min-voltage = <332000>;
+		qcom,ldo-config-init = <0xf1f0e471>;
+		qcom,apm-config-init = <0x0>;
+		qcom,cluster-num = <1>;
+		kryo1_retention_vreg: regulator {
+			regulator-name = "kryo1-retention";
+			regulator-min-microvolt = <332000>;
+			regulator-max-microvolt = <865000>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
new file mode 100644
index 0000000..5515457
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/mem-acc-regulator.txt
@@ -0,0 +1,266 @@
+Qualcomm Technologies, Inc. Memory Accelerator
+
+Memory accelerator configures the power-mode (corner) for the
+accelerator.
+
+Required properties:
+- compatible:			Must be "qcom,mem-acc-regulator"
+- regulator-name:		A string used to describe the regulator
+- regulator-min-microvolt:	Minimum corner value as min constraint, which
+				should be 1 for SVS corner
+- regulator-max-microvolt:	Maximum corner value as max constraint, which
+				should be 4 for SUPER_TURBO or 3 for TURBO
+
+Optional properties:
+- reg:				Register addresses for acc-sel-l1, acc-sel-l2 control, acc-en,
+				MEM ACC eFuse address, acc-l1-custom , acc-l2-custom,
+				mem-acc-type1, mem-acc-type2, mem-acc-type3, mem-acc-type4,
+				mem-acc-type5 and mem-acc-type6.
+- reg-names:			Register names. Must be "acc-sel-l1",
+				"acc-sel-l2", "acc-en", "efuse_addr",
+				"acc-l1-custom", "acc-l2-custom", "mem-acc-type1",
+				"mem-acc-type2", "mem-acc-type3", "mem-acc-type4",
+				"mem-acc-type5", "mem-acc-type6".
+				A given mem-acc-regulator driver must have "acc-sel-l1" or
+				"acc-sel-l2" or "mem-acc-type*" reg-names property and
+				related register address property.
+- qcom,corner-acc-map		Array which maps the APC (application processor)
+				corner value to the accelerator corner. The number of elements
+				in this property defines the number of accelerator corners.
+				Either qcom,corner-acc-map property or qcom,cornerX-reg-config
+				properties should be specified.
+- qcom,acc-en-bit-pos		Array which specifies bit positions in the
+				'acc-en' register. Setting these bits forces the
+				the acclerator to use the corner value specified
+				in the 'acc-sel-l1' and 'acc-sel-l2' register.
+- qcom,acc-sel-l1-bit-size	Integer which specifies the number of bits in
+				the 'acc-sel-l1' register which define each L1
+				select parameter.  If this property is not
+				specified, then a default value of 2 is assumed.
+- qcom,acc-sel-l1-bit-pos	Array which specifies bit positions in the
+				'acc-sel-l1' register. Each element in this array
+				is the LSB of an N-bit value where 'N' is
+				defined by the qcom,acc-sel-l1-bit-size
+				property.  This N-bit value specifies the corner
+				value used by the accelerator for the L1 cache.
+- qcom,acc-sel-l2-bit-size	Integer which specifies the number of bits in
+				the 'acc-sel-l2' register which define each L2
+				select parameter.  If this property is not
+				specified, then a default value of 2 is assumed.
+- qcom,acc-sel-l2-bit-pos	Array which specifies bit positions in the
+				'acc-sel-l2' register. Each element in this array
+				is the LSB of an N-bit value where 'N' is
+				defined by the qcom,acc-sel-l2-bit-size
+				property.  This N-bit value specifies the corner
+				value used by the accelerator for the L2 cache.
+- qcom,l1-acc-custom-data:	Array which maps APC corner values to L1 ACC custom data values.
+				The corresponding custom data is written into the custom register
+				while switching between APC corners. The custom register address
+				is specified by "acc-11-custom" reg-property. The length of the array
+				should be equal to number of APC corners.
+- qcom,l2-acc-custom-data:	Array which maps APC corner values to L2 ACC custom data values.
+				The corresponding custom data is written into the custom register
+				while switching between APC corners. The custom register address
+				is specified by "acc-l2-custom" reg-property. The length of the array
+				should be equal to number of APC corners.
+- qcom,override-acc-fuse-sel:	Array of 4 elements which specify the way to read the override fuse.
+				The override fuse value is used by the qcom,override-fuse-version-map
+				to identify the correct set of override properties.
+				The 4 elements with index [0..4] are:
+				  [0] => the fuse row number of the selector
+				  [1] => LSB bit position of the bits
+				  [2] => number of bits
+				  [3] => fuse reading method, 0 for direct reading or 1 for SCM reading
+- qcom,override-fuse-version-map: Array of integers which each match to a override fuse value.
+				Any element in a tuple may use the value 0xffffffff as a wildcard.
+				The index of the first value (in the array) which matches the override fuse
+				is used to select the right tuples from the other override properties.
+- qcom,override-corner-acc-map:	Array of tuples which overrides the existing acc-corner map
+				(specified by qcom,corner-acc-map) with corner values selected
+				from this property. "qcom,override-corner-acc-map" must contain the
+				same number of tuples as "qcom,override-fuse-version-map". These tuples
+				are then mapped one-to-one in the order specified. If the
+				"qcom,override-fuse-version-map" property is not specified, then
+				"qcom,override-corner-acc-map" must contain a single tuple which is then
+				applied unconditionally.
+- qcom,override-l1-acc-custom-data:	Array of tuples of which overrides the existing l1-acc-custom data
+				(specified by qcom,l1-acc-custom-data), with values specified in this property.
+				The corresponding custom data is written into the custom register while
+				switching between APC corners. The custom register address is specified by
+				"acc-11-custom" reg-property. This property can only be specified if the
+				"qcom,l1-acc-custom-data" is already defined. If the
+				"qcom,override-fuse-version-map" property is specified, then
+				qcom,override-l1-acc-custom-data must contain the same number of tuples
+				as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one
+				in the order specified. If the qcom,override-fuse-version-map property is
+				not specified, then "qcom,override-l1-acc-custom-data" must contain a single
+				tuple which is then applied unconditionally.
+- qcom,override-l2-acc-custom-data:	Array of tuples of which overrides the existing l1-acc-custom data
+				(specified by qcom,l2-acc-custom-data), with values specified in this property.
+				The corresponding custom data is written into the custom register while
+				switching between APC corners. The custom register address is specified by
+				"acc-12-custom" reg-property. This property can only be specified if the
+				"qcom,l2-acc-custom-data" is already defined. If the
+				"qcom,override-fuse-version-map" property is specified, then
+				"qcom,override-l2-acc-custom-data" must contain the same number of tuples
+				as "qcom,override-fuse-version-map". These tuples are then mapped one-to-one
+				in the order specified. If the qcom,override-fuse-version-map property is
+				not specified, then "qcom,override-l2-acc-custom-data" must contain a single
+				tuple which is then applied unconditionally.
+- qcom,mem-acc-type1:		Array which specifies the value to be written to the mem acc type1 register for each fuse
+				corner, from the lowest fuse corner to the highest fuse corner. The length of the array
+				must be equal to the number of APC fuse corners. This property must be present if reg names
+				specifies mem-acc-type1.
+- qcom,mem-acc-type2:		Same as qcom,mem-acc-type1 except for mem acc type2 register.
+- qcom,mem-acc-type3:		Same as qcom,mem-acc-type1 except for mem acc type3 register.
+- qcom,mem-acc-type4:		Same as qcom,mem-acc-type1 except for mem acc type4 register.
+- qcom,mem-acc-type5:		Same as qcom,mem-acc-type1 except for mem acc type5 register.
+- qcom,mem-acc-type6:		Same as qcom,mem-acc-type1 except for mem acc type6 register.
+- qcom,acc-reg-addr-list:	Array of register addresses which need to be programmed during any corner switch.
+				This property can be used when multi register configuration is needed during a corner switch.
+- qcom,acc-init-reg-config:	Array of tuples specify the multi register configuration sequence need to be programmed
+				one time during device boot.
+				The format of each tuple as below:
+					<register-address-index, value>
+				Where register-address-index is used as an index in to qcom,acc-reg-addr-list property
+				to get the required register address and the value is programmed in to the corresponding
+				mapped register address. This property is required if qcom,acc-corner-addr-val-map
+				property specified.
+- qcom,cornerX-reg-config:	Array of tuples specify the multi register configuration sequence need to be programmed
+				when switching from acc corner X to any other corner. The possible values for X are {1, N},
+				where N is the value defined in qcom,num-acc-corners.
+				The format of each tuple as below:
+					<register-address-index, value>
+				Where register-address-index is used as an index in to qcom,acc-reg-addr-list property
+				to get the required register address and the value is programmed in to the corresponding
+				mapped register address. Same index can be used multiple times when the register is
+				required to configure multiple times with different values in the sequence.
+				The number of register configuration sequences should be equal to N, where N is the
+				value specified in qcom,num-acc-corners property. Also, the number of tuples in each
+				register configuration sequence should be same and must be equal to the maximum required
+				register configurations in any sequence. The invalid register configuration can be
+				specified as <(-1) (-1)>.  This property can only be specified when qcom,acc-corner-addr-val-map
+				property already defined. Either this property or qcom,corner-acc-map should be specified.
+- qcom,num-acc-corners:		The number of acc corners supported. This property is required if qcom,cornerX-reg-config
+				property specified.
+- qcom,boot-acc-corner:		The acc corner used during device boot. This property is required if qcom,cornerX-reg-config
+				property specified.
+- qcom,override-cornerX-reg-config:	A grouping of register configuration sequence lists. Each list is same as
+				the qcom,cornerX-reg-config property. The possible values for X are {1, N} where N is
+				the value defined in qcom,num-acc-corners. This property is used to specify the different
+				register configuration sequence lists and select one list among them based on the selected
+				index in qcom,override-fuse-version-map property. The selected list overrides the existing
+				register configuration sequence list specified in "qcom,cornerX-reg-config". If the
+				"qcom,override-fuse-version-map" property is specified, then
+				"qcom,override-cornerX-reg-config" must contain the same number of register
+				configuration sequence lists as the number of tuples in "qcom,override-fuse-version-map".
+				These register configuration sequence lists are then mapped one-to-one
+				in the order specified. If the qcom,override-fuse-version-map property is
+				not specified, then "qcom,override-cornerX-reg-config" must contain a single
+				register configuration sequence list which is then applied unconditionally.
+				This property can only be specified if qcom,cornerX-reg-config property is already defined.
+
+mem_acc_vreg_corner: regulator@fd4aa044 {
+	compatible = "qcom,mem-acc-regulator";
+	reg = <0xfd4aa048 0x1>, <0xfd4aa044 0x1>, <0xfd4af000 0x1>,
+		<0x58000 0x1000>, <0x01942124 0x4>, <0xf900d084 1>,
+		<0xf900d088 1>, <0xf900d08c 1>, <0xf900d090 1>;
+	reg-names = "acc-en", "acc-sel-l1" , "acc-sel-l2",
+		"efuse_addr", "acc-l2-custom", "mem-acc-type1",
+		"mem-acc-type2", "mem-acc-type3", "mem-acc-type4";
+	regulator-name = "mem_acc_corner";
+	regulator-min-microvolt = <1>;
+	regulator-max-microvolt = <3>;
+
+	qcom,acc-en-bit-pos = <0>;
+	qcom,acc-sel-l1-bit-pos = <0>;
+	qcom,acc-sel-l2-bit-pos = <0>;
+	qcom,acc-sel-l1-bit-size = <2>;
+	qcom,acc-sel-l2-bit-size = <2>;
+	qcom,corner-acc-map = <0 1 3>;
+	qcom,l2-acc-custom-data = <0x0 0x3000 0x3000>;
+
+	qcom,override-acc-fuse-sel = <0 52 2 0>;
+	qcom,override-fuse-version-map = <0>,
+					 <2>,
+					 <(-1)>;
+	qcom,override-corner-acc-map =	<0 0 1>,
+					<0 1 2>,
+					<0 1 1>;
+	qcom,overide-l2-acc-custom-data = <0x0	0x0	0x3000>,
+					  <0x0	0x3000	0x3000>,
+					  <0x0	0x0	0x0>;
+	qcom,mem-acc-type1 = <0x02 0x02 0x00>;
+	qcom,mem-acc-type2 = <0x02 0x02 0x00>;
+	qcom,mem-acc-type3 = <0x02 0x02 0x00>;
+	qcom,mem-acc-type4 = <0x02 0x02 0x00>;
+
+	qcom,acc-reg-addr-list = <0x01942130 0x01942124 0x01942120>;
+	qcom,acc-init-reg-config = <1 0x55> <2 0x02>;
+
+	qcom,num-acc-corners = <3>;
+	qcom,boot-acc-corner = <2>;
+	qcom,corner1-reg-config =
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */
+		<  1 0x155>, <  2   0x0>, <  3 0x155>, /* 1 -> 2 */
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>; /* 1 -> 3 */
+
+	qcom,corner2-reg-config =
+		<  1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 1 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */
+		<  1 0x155>, <  2   0x0>, <  3 0x155>; /* 2 -> 3 */
+
+	qcom,corner3-reg-config =
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>, /* 3 -> 1 */
+		<  1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>; /* 3 -> 3 */
+
+	qcom,override-corner1-reg-config =
+		/* 1st fuse version tuple matched */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */
+		<  1 0x155>, <  2   0x0>, <  3 0x155>, /* 1 -> 2 */
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>, /* 1 -> 3 */
+
+		/* 2nd fuse version tuple matched */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */
+		<  1 0x155>, <  2   0x0>, <  3 0x155>, /* 1 -> 2 */
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>, /* 1 -> 3 */
+
+		/* 3rd fuse version tuple matched */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 1 -> 1 */
+		<  1 0x155>, <  3  0x22>, <  3 0x155>, /* 1 -> 2 */
+		<  1   0x0>, <  2 0x155>, <  3 0x144>; /* 1 -> 3 */
+
+	qcom,override-corner2-reg-config =
+		/* 1st fuse version tuple matched */
+		<  1 0x144>, <  1  0x11>, <(-1) (-1)>, /* 2 -> 1 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */
+		<  1 0x155>, <  2   0x0>, <  3 0x155>, /* 2 -> 3 */
+
+		/* 2nd fuse version tuple matched */
+		<  1 0x144>, <  2 0x133>, <(-1) (-1)>, /* 2 -> 1 */
+		<(-1) (-1)>, <  1  0x33>, <(-1) (-1)>, /* 2 -> 2 */
+		<  1 0x133>, <  2   0x0>, <  3 0x155>, /* 2 -> 3 */
+
+		/* 3rd fuse version tuple matched */
+		<  1 0x144>, <  1  0x11>, <(-1) (-1)>, /* 2 -> 1 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 2 -> 2 */
+		<  1 0x155>, <  2  0x22>, <  3 0x155>; /* 2 -> 3 */
+
+
+	qcom,override-corner3-reg-config =
+		/* 1st fuse version tuple matched */
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>, /* 3 -> 1 */
+		<  1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 3 */
+
+		/* 2nd fuse version tuple matched */
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>, /* 3 -> 1 */
+		<  1 0x155>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 2 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>, /* 3 -> 3 */
+
+		/* 3rd fuse version tuple matched */
+		<  1   0x0>, <  2 0x155>, <(-1) (-1)>, /* 3 -> 1 */
+		<  1 0x155>, <  3  0x11>, <(-1) (-1)>, /* 3 -> 2 */
+		<(-1) (-1)>, <(-1) (-1)>, <(-1) (-1)>; /* 3 -> 3 */
+};
diff --git a/Documentation/devicetree/bindings/regulator/msm_gfx_ldo.txt b/Documentation/devicetree/bindings/regulator/msm_gfx_ldo.txt
new file mode 100644
index 0000000..dcbb120
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/msm_gfx_ldo.txt
@@ -0,0 +1,148 @@
+Qualcomm Technologies, Inc. GFX LDO for Graphics
+
+The GPU core on MSM 8953 can be powered by an internal (on-die)
+MSM LDO or BHS based on its operating corner.
+
+This document describes the bindings that apply for the GFX LDO regulator.
+
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be "qcom,msm8953-gfx-ldo" for MSM8953 and
+		    "qcom,sdm660-gfx-ldo" for SDM660
+
+- reg
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Addresses and sizes for the memory of the GFX ldo
+
+- reg-names
+	Usage:      required
+	Value type: <stringlist>
+	Definition: Address names. "ldo_addr", "efuse_addr". Must be
+		    specified in the same order as the corresponding addresses
+		    are specified in the reg property.
+
+- regulator-name
+	Usage:      required
+	Value type: <string>
+	Definition: A string used to describe the regulator.
+
+- regulator-min-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Minimum corner value which should be 1 to represent the
+		    lowest supported corner.
+
+- regulator-max-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Maximum corner value which should be equal to qcom,num-corners.
+
+- qcom,num-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Number of voltage corners present. Many other
+		    properties are sized based upon this value.
+
+- qcom,num-ldo-corners
+	Usage:      required
+	Value type: <u32>
+	Definition: Number of voltage corners defined for the ldo. It is a
+		    subset of qcom,num-corners (i.e. 1 to qcom,num-ldo-corners
+		    are the corners for ldo operation)
+
+- qcom,init-corner
+	Usage:      required
+	Value type: <u32>
+	Definition: The initial-corner at which the GFX rail is powered on.
+
+- qcom,ldo-enable-corner-map
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Integer values ( / 0) which indicate the GFX corners in which
+		    ldo is to enabled. The length of this property
+		    should be equal to qcom,num-corners.
+
+- qcom,ldo-voltage-ceiling
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Array of ceiling voltages in microvolts for voltage corners
+		    ordered from lowest voltage corner to highest voltage corner.
+		    This property must be of length defined by qcom,num-ldo-corners.
+
+- qcom,ldo-voltage-floor
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition: Array of floor voltages in microvolts for voltage corners
+		    ordered from lowest voltage corner to highest voltage corner.
+		    This property must be of length defined by qcom,num-ldo-corners.
+
+- vdd-cx-supply
+	Usage:      optional
+	Value type: <phandle>
+	Definition: Parent regulator supply to the ldo.
+
+- qcom,vdd-cx-corner-map
+	Usage:      required if vdd-cx-supply is specified.
+	Value type: <prop-encoded-array>
+	Definition: Array of integers which define the mapping of the VDD_CX corner
+		    to the corresponding GFX voltage corner. The elements in
+		    the array are ordered from lowest voltage corner to highest
+		    voltage corner.  The length of this property must be equal to
+		    the value defined by qcom,num-corners.
+- mem-acc-supply
+	Usage:      optional
+	Value type: <phandle>
+	Definition: Regulator to vote for the memory accelerator configuration.
+		    Not Present: memory accelerator configuration not supported.
+
+- qcom,mem-acc-corner-map:
+	Usage:      optional
+	Value type: <prop-encoded-aray>
+	Definition: Array of integer which defines the mapping from mem-acc
+		    corner value for each gfx corner. The elements in the array
+		    are ordered from lowest voltage corner to highest voltage corner.
+
+- qcom,ldo-init-voltage-adjustment
+	Usage:      optional
+	Value type: <prop-encoded-aray>
+	Definition: Array of voltages in microvolts which indicate the static
+		    adjustment to be applied to the open-loop voltages for the
+		    LDO supported corners. The length of this property must be
+		    equal to qcom,num-ldo-corners.
+
+=======
+Example
+=======
+
+	gfx_vreg_corner: ldo@0185f000 {
+		compatible = "qcom,msm8953-gfx-ldo";
+		reg = <0x0185f000 0x30>, <0xa0000 0x1000>;
+		reg-names = "ldo_addr", "efuse_addr";
+
+		regulator-name = "msm_gfx_ldo";
+		regulator-min-microvolt = <1>;
+		regulator-max-microvolt = <7>;
+
+		qcom,ldo-voltage-ceiling = <500000 700000 900000>;
+		qcom,ldo-voltage-floor = <400000 600000 800000>;
+
+		qcom,num-corners = <7>;
+		qcom,num-ldo-corners = <3>;
+		qcom,ldo-enable-corner-map = <1 1 1 0 0 0 0>;
+		qcom,init-corner = <5>;
+
+		vdd-cx-supply = <&pm8953_s2_level>;
+		qcom,vdd-cx-corner-map = <RPM_SMD_REGULATOR_LEVEL_LOW_SVS>,
+					<RPM_SMD_REGULATOR_LEVEL_LOW_SVS>,
+					<RPM_SMD_REGULATOR_LEVEL_LOW_SVS>,
+					<RPM_SMD_REGULATOR_LEVEL_SVS>,
+					<RPM_SMD_REGULATOR_LEVEL_NOM>,
+					<RPM_SMD_REGULATOR_LEVEL_NOM_PLUS>,
+					<RPM_SMD_REGULATOR_LEVEL_TURBO>;
+
+		mem-acc-supply = <&gfx_mem_acc>;
+		qcom,mem-acc-corner-map = <1 1 2 2 2 2 2>;
+		qcom,ldo-init-voltage-adjustment = <10000 20000 30000>;
+	};
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
new file mode 100644
index 0000000..d08ca95
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qpnp-labibb-regulator.txt
@@ -0,0 +1,406 @@
+QTI's LAB (LCD/AMOLED BOOST)/IBB (Inverting Buck-Boost) Regulator
+
+LAB can be used as a standalone positive boost power supply for general purpose
+applications. IBB can be used as a standalone negative power supply for general
+applications. Also, LAB and IBB can be used together to provide power supply for
+display panels, LCD or AMOLED.
+
+Main node required properties:
+
+- compatible:			Must be "qcom,qpnp-labibb-regulator"
+- qcom,qpnp-labibb-mode:	A string used to specify the working mode of LAB/IBB
+				regulators when bootloader does not turned on the
+				display panel. Could be "lcd" or "amoled".
+				"lcd" means using LAB and IBB regulators are
+				configured for LCD mode.
+				"amoled" means using LAB and IBB regulators are
+				configured for AMOLED mode.
+- qcom,pmic-revid:		Specifies the phandle of the PMIC revid module.
+				Used to identify the PMIC subtype.
+
+Main node optional properties:
+
+- qcom,qpnp-labibb-touch-to-wake-en:	A boolean property which upon set will
+					enable support for touch-to-wake mode
+					by configuring the required settings
+					in LAB and IBB modules. Make sure the
+					hardware has needed support before
+					enabling this property.
+- qcom,swire-control:			A boolean property which indicates if the LAB/IBB is
+					controlled by the SWIRE interface. Enable only
+					if qcom,qpnp-labibb-mode = "amoled".
+- qcom,labibb-ttw-force-lab-on:		A boolean property which forces LAB to be
+					always on during TTW mode.
+- qcom,skip-2nd-swire-cmd:		A boolean property which indicates if
+					the second SWIRE command needs to be skipped.
+- qcom,swire-2nd-cmd-delay:		An integer value which specifes the
+					delay in millisecs between the first and second
+					SWIRE command. If not specified this value
+					defaults to 20ms. This delay is applied only
+					if 'qcom,skip-2nd-swire-cmd' is defined.
+- qcom,swire-ibb-ps-enable-delay:	An integer value which specifes the delay
+					in millisecs to enable IBB pulse-skipping
+					after the skip-2nd-swire-cmd workaround is applied.
+					If not specified this value default to 200ms.
+					This property is applicable only if
+					'qcom,skip-2nd-swire-cmd' is specified.
+- qcom,labibb-standalone:		A boolean property which forces LAB and
+					IBB to operate in standalone mode. If
+					this is not specified, then LAB and IBB
+					are controlled together in dual mode.
+- parent-supply:			Parent supply that is needed for LAB
+					and IBB regulators. This will be mostly
+					needed when LAB and IBB are operating
+					in standalone mode to vote for MBG.
+
+Following properties are available only for PM660A:
+
+- qcom,pbs-control:			A boolean property which indicates if
+					the LAB/IBB is controlled by the PBS
+					sequencer. If this mode is enabled the
+					PBS sequencer does the SWIRE remapping
+					and program the voltages based on the
+					SWIRE count.
+
+LAB subnode required properties:
+
+- reg:				Specifies the SPMI address and size for this peripheral.
+- reg-names:			Register names. Must be "lab".
+- regulator-name:		A string used to describe the regulator.
+- regulator-min-microvolt:	Minimum voltage in microvolts supported by this regulator.
+- regulator-max-microvolt:	Maximum voltage in microvolts supported by this regulator.
+
+- qcom,qpnp-lab-min-voltage:	The minimum voltage in microvolts LAB regulator can support.
+- qcom,qpnp-lab-step-size:	The step size in microvolts of LAB regulator.
+- qcom,qpnp-lab-slew-rate:	The time in us taken by the regulator to change
+				voltage value in one step.
+
+- qcom,qpnp-lab-init-voltage:		The default initial voltage when the bootloader
+					does not turn on LAB regulator.
+- qcom,qpnp-lab-init-amoled-voltage:	The default output voltage when LAB regulator
+					is configured in amoled mode.
+- qcom,qpnp-lab-init-lcd-voltage: 	The default output voltage when LAB regulator
+					is configured in lcd mode.
+- qcom,qpnp-lab-ps-threshold:		The threshold in mA of Pulse Skip Mode for
+					LAB regulator. Supported values are 20, 30,
+					40 and 50.
+- interrupts:				Specify the interrupts as per the interrupt
+					encoding.
+					Currently "lab-vreg-ok" is required for
+					LCD mode in pmi8998. For AMOLED mode,
+					"lab-vreg-ok" is required only when SWIRE
+					control is enabled and skipping 2nd SWIRE
+					pulse is required in pmi8952/8996.
+- interrupt-names:			Interrupt names to match up 1-to-1 with
+					the interrupts specified in 'interrupts'
+					property.
+
+LAB subnode optional properties:
+
+- qcom,qpnp-lab-current-sense:		If this property is specified, the LAB current
+					sense gain will be programmed for LAB regulator.
+					Otherwise, LAB current sense gain will be
+					default to "1x". A string is used to specify the
+					LAB current sense gain. Could be "0.5x" or "1x"
+					or "1.5x" or "2x". For e.g. "0.5x" means current
+					sense gain is 0.5.
+- qcom,qpnp-lab-ps-enable:		A boolean proerty which upon set will enable
+					pulse skip mode for LAB regulator. Otherwise,
+					it is disabled.
+- qcom,qpnp-lab-full-pull-down:		A boolean property which upon set will enable
+					the pull down strength of LAB regulator to
+					full. Otherwise, the pull down strength is
+					configured to half.
+- qcom,qpnp-lab-pull-down-enable:	A boolean property which upon set will enable
+					the pull down for LAB regulator. Otherwise,
+					it is disabled.
+- qcom,qpnp-lab-max-precharge-enable:	A boolean property which upon set will
+					enable fast precharge. Otherwise, it is
+					disabled.
+- qcom,qpnp-lab-ring-suppression-enable:	A boolean property which upon set will
+						enable ring suppression for LAB
+						regulator. Otherwise, it is disabled.
+- qcom,qpnp-lab-limit-max-current-enable:	A boolean property which upon set will
+						enforce maximum inductor current constraint
+						for LAB regulator. Otherwise, there is no
+						maximum current constraint.
+- qcom,qpnp-lab-switching-clock-frequency:	The PWM switching clock frequency in
+						kHz of Lab regulator, Supported values
+						are: 3200, 2740, 2400, 2130, 1920,
+						1750, 1600, 1480, 1370, 1280, 1200,
+						1130, 1070, 1010, 960, 910.
+- qcom,qpnp-lab-limit-maximum-current:		The maximum inductor current limit in
+						mA of LAB regulator. Supported values
+						are 200, 400, 600, 800, 1000, 1200,
+						1400 and 1600.
+- qcom,qpnp-lab-pfet-size:		PFET size in percentage. Supported values
+					are 25, 50, 75 and 100.
+- qcom,qpnp-lab-nfet-size:		NFET size in percentage. Supported values
+					are 25, 50, 75 and 100.
+- qcom,qpnp-lab-max-precharge-time:	Precharge time in uS for LAB regulator.
+					Supported values are 200, 300, 400 and 500.
+					Suggested values for LCD and AMOLED mode
+					are 500 and 300uS respectively.
+- qcom,qpnp-lab-use-default-voltage:	A boolean property which upon set will
+					use the value specified in
+					qcom,qpnp-lab-init-voltage property.
+					This will be used only if the bootloader
+					doesn't configure the output voltage
+					already. If it it not specified, then
+					output voltage can be configured to
+					any value in the allowed limit.
+
+Following properties are available only for PM660A:
+
+- qcom,qpnp-lab-soft-start:		The soft start time in us of LAB regulator.
+					Supported value are 200, 400, 600 and 800.
+- qcom,qpnp-lab-ldo-pulldown-enable:	This property is used to enable/disable
+					the LDO	pull down.
+					1 - enable pulldown
+					0 - disable pulldown
+- qcom,qpnp-lab-enable-sw-high-psrr:	A boolean property to enable the
+					software high psrr
+					(Power Suppy Rejection Rate) mode.
+- qcom,qpnp-lab-high-psrr-src-select:	This property is used to select the LAB
+					HW high psrr source.
+					The supported values are:
+					0 = Either vph_high or high_psrr enable
+					1 = vph_high only
+					2 = high_psrr enable only
+					3 = Either vph_high or high_psrr enable
+					This property is not valid if the
+					qcom,qpnp-lab-enable-sw-high-psrr property
+					is specified.
+- qcom,qpnp-lab-vref-high-psrr-select:	This property is required if the
+					qcom,qpnp-lab-high-psrr-src-select is
+					specified. The supported values (in mV)
+					are 350, 400, 450 and 500. Once the
+					rejection rate crosses the selected
+					high-psrr voltage the LDO is enabled
+					based on the value specified under
+					qcom,qpnp-lab-high-psrr-src-select
+					property.
+					This property is not valid if the
+					qcom,qpnp-lab-enable-sw-high-psrr property
+					is specified.
+
+IBB subnode required properties:
+
+- reg:				Specifies the SPMI address and size for this peripheral.
+- reg-names:			Register names. Must be "ibb".
+- regulator-name:		A string used to describe the regulator.
+- regulator-min-microvolt:	Minimum voltage in microvolts supported by this regulator.
+- regulator-max-microvolt:	Maximum voltage in microvolts supported by this regulator.
+
+- qcom,qpnp-ibb-min-voltage:	The minimum voltage in microvolts IBB regulator can support.
+- qcom,qpnp-ibb-step-size:	The step size in microvolts of IBB regulator.
+- qcom,qpnp-ibb-soft-start:	The soft start time in us of IBB regulator.
+
+- qcom,qpnp-ibb-init-voltage:	The default initial voltage when the bootloader does
+				not turn on IBB regulator.
+- qcom,qpnp-ibb-init-amoled-voltage:	The default output voltage when IBB regulator
+					is configured in amoled mode.
+- qcom,qpnp-ibb-init-lcd-voltage: 	The default output voltage when IBB regulator
+					is configured in lcd mode.
+
+IBB subnode optional properties:
+
+- qcom,qpnp-ibb-discharge-resistor:	The discharge resistor in Kilo Ohms which
+					controls the soft start time. Supported values
+					are 300, 64, 32 and 16.
+
+- qcom,qpnp-ibb-slew-rate:	The time (in us) taken by the regulator to change
+				voltage value in one step. This property is not
+				applicable to PM660A.
+				The following properties can be used as an
+				alternate.
+					qcom,qpnp-ibb-slew-rate-config
+					qcom,qpnp-ibb-fast-slew-rate
+					qcom,qpnp-ibb-slow-slew-rate
+- qcom,qpnp-ibb-ps-enable:		A boolean property which upon set will enable
+					pulse skip mode for IBB regulator. Otherwise,
+					it is disabled.
+- qcom,qpnp-ibb-num-swire-trans:	The number of SWIRE transactions
+					after which the pulse skipping is
+					enabled. This property is required when
+					qpnp-ibb-smart-ps-enable property is
+					set.
+- qcom,qpnp-ibb-neg-curr-limit:		This property must be set when the
+					qpnp-ibb-smart-ps-enable is specified.
+					The supported values in mA are 1, 2, 3,
+					4, 5, 6 and 7. The recommended value is
+- qcom,qpnp-ibb-full-pull-down:		A boolean property which upon set will
+					enable the pull down strength of IBB
+					regulator to full. Otherwise, the pull
+					down strength is configured to half.
+- qcom,qpnp-ibb-pull-down-enable:	A boolean property which upon set will enable
+					the pull down for IBB regulator. Otherwise,
+					it is disabled.
+- qcom,qpnp-ibb-lab-pwrup-delay:	Power up delay (in us) for IBB regulator when
+					it is enabled or turned on. Supported values
+					are 1000, 2000, 4000 and 8000.
+- qcom,qpnp-ibb-lab-pwrdn-delay:	Power down delay (in us) for IBB regulator
+					when it is disabled or turned off. Supported
+					values are 1000, 2000, 4000 and 8000.
+- qcom,qpnp-ibb-switching-clock-frequency:	The PWM switching clock frequency in
+						kHz of IBB regulator. Supported values
+						are: 3200, 2740, 2400, 2130, 1920,
+						1750, 1600, 1480, 1370, 1280, 1200,
+						1130, 1070, 1010, 960, 910.
+- qcom,qpnp-ibb-limit-maximum-current:		The maximum inductor current limit in
+						mA of IBB regulator. Supported values
+						are: 0, 50, 100, 150, 200, 250, 300,
+						350, 400, 450, 500, 550, 600, 650, 700,
+						750, 800, 850, 900, 950, 1000, 1050,
+						1100, 1150, 1200, 1250, 1300, 1350,
+						1400, 1450, 1500 and 1550.
+- qcom,qpnp-ibb-debounce-cycle:			The debounce cycle of IBB regulator.
+						Supported values are 8, 16, 32 and 64.
+- qcom,qpnp-ibb-en-discharge:			A boolean property which upon set will
+						enable discharge for IBB regulator.
+						Otherwise, it is kept disabled.
+- qcom,qpnp-ibb-ring-suppression-enable:	A boolean property which upon set will
+						enable ring suppression for IBB
+						regulator. Otherwise, it is disabled.
+- qcom,qpnp-ibb-limit-max-current-enable:	A boolean property which upon set will
+						enforce maximum inductor current constraint
+						for IBB regulator. Otherwise, there is no
+						maximum current constraint.
+- qcom,qpnp-ibb-use-default-voltage:		A boolean property which upon set will
+						use the value specified in
+						qcom,qpnp-ibb-init-voltage property.
+						This will be used only if the bootloader
+						doesn't configure the output voltage
+						already. If it it not specified, then
+						output voltage can be configured to
+						any value in the allowed limit.
+- qcom,output-voltage-one-pulse:		The expected voltage (in mV) of VDISN signal
+						on the first SWIRE pulse. This property
+						can be specified only if 'qcom,swire-control'
+						is defined. The minimum and maximum values
+						are 1400mV and 7700mV.
+
+Following properties are available only for PM660A:
+
+- qcom,qpnp-ibb-smart-ps-enable:	A boolean property which upon set
+					enables smart pulse skip mode for IBB
+					regulator. Otherwise, it is disabled.
+					This property is only applicable to
+					PM660A.
+- qcom,qpnp-ibb-enable-pfm-mode:	A boolean property which enables the IBB to work
+					in pfm mode.
+- qcom,qpnp-ibb-pfm-peak-curr:		The PFM peak current limit settings in mA.
+					Supported values are 150, 200, 250, 300,
+					350, 400, 450 and 500. This property is
+					required if the qcom,qpnp-ibb-enable-pfm-mode
+					is true.
+- qcom,qpnp-ibb-pfm-hysteresis:		The PFM hysteresis voltage threshold in mV.
+					Supported values are 0, 25 and 50.
+					This property is required if the
+					qcom,qpnp-ibb-enable-pfm-mode is specified.
+- qcom,qpnp-ibb-overload-blank:		A boolean property which upon set enables
+					the IBB overload blanking.
+- qcom,qpnp-ibb-overload-debounce:	The expected overload debounce time (in ms)
+					values are 1, 2, 4 and 8.
+					This property is required only when the
+					qcom,qpnp-ibb-overload-blank is set.
+- qcom,qpnp-ibb-vreg-ok-debounce:	The expected vreg-ok-debounce time (us)
+					values are 4, 8, 16 and 32.
+					This property is required only when the
+					qcom,qpnp-ibb-overload-blank is set.
+- qcom,qpnp-ibb-slew-rate-config:	A boolean property to configure the
+					ibb fast/slow slew rate.
+					Either qcom,qpnp-ibb-fast-slew-rate or
+					qcom,qpnp-ibb-slow-slew-rate has to be
+					specified. Otherwise the
+					qcom,qpnp-ibb-slow-slew-rate takes precedence
+					over the qcom,qpnp-ibb-fast-slew-rate.
+- qcom,qpnp-ibb-fast-slew-rate:		This property is required if the qcom,
+					qpnp-ibb-slew-rate-config property is
+					specified. Supported values (in us) are
+					100, 200, 500, 1000, 2000, 10000, 12000
+					and 15000.
+- qcom,qpnp-ibb-slow-slew-rate:		This property is required if the qcom,
+					qpnp-ibb-slew-rate-config property is
+					specified. Supported values (in us) are
+					100, 200, 500, 1000, 2000, 10000, 12000
+					and 15000.
+
+Example:
+	qcom,pmi8994@3 {
+		qpnp-labibb-regulator {
+			compatible = "qcom,qpnp-labibb-regulator";
+			#address-cells = <1>;
+			#size-cells = <1>;
+			qcom,qpnp-labibb-mode = "lcd";
+			qcom,pmic-revid = <&pmi8994_revid>;
+			qcom,skip-2nd-swire-cmd;
+
+			lab_regulator: qcom,lab@de00 {
+				reg = <0xde00 0x100>;
+				reg-names = "lab";
+
+				interrupts = <0x3 0xde 0x0
+						IRQ_TYPE_EDGE_RISING>;
+                                interrupt-names = "lab-vreg-ok";
+
+				regulator-name = "lab_reg";
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6000000>;
+
+				qcom,qpnp-lab-min-voltage = <4600000>;
+				qcom,qpnp-lab-step-size = <100000>;
+				qcom,qpnp-lab-slew-rate = <5000>;
+				qcom,qpnp-lab-use-default-voltage;
+				qcom,qpnp-lab-init-voltage = <5500000>;
+				qcom,qpnp-lab-init-amoled-voltage = <4600000>;
+				qcom,qpnp-lab-init-lcd-voltage = <5500000>;
+
+				qcom,qpnp-lab-soft-start = <400>;
+
+				qcom,qpnp-lab-full-pull-down;
+				qcom,qpnp-lab-pull-down-enable;
+				qcom,qpnp-lab-switching-clock-frequency = <1600>;
+				qcom,qpnp-lab-limit-maximum-current = <1600>;
+				qcom,qpnp-lab-limit-max-current-enable;
+				qcom,qpnp-lab-ps-threshold = <40>;
+				qcom,qpnp-lab-ps-enable;
+				qcom,qpnp-lab-nfet-size = <100>;
+				qcom,qpnp-lab-pfet-size = <100>;
+				qcom,qpnp-lab-max-precharge-time = <200>;
+			};
+
+			ibb_regulator: qcom,ibb@dc00 {
+				reg = <0xdc00 0x100>;
+				reg-names = "ibb_reg";
+				regulator-name = "ibb_reg";
+
+				regulator-min-microvolt = <4600000>;
+				regulator-max-microvolt = <6000000>;
+
+				qcom,qpnp-ibb-min-voltage = <1400000>;
+				qcom,qpnp-ibb-step-size = <100000>;
+				qcom,qpnp-ibb-slew-rate = <2000000>;
+				qcom,qpnp-ibb-use-default-voltage;
+				qcom,qpnp-ibb-init-voltage = <5500000>;
+				qcom,qpnp-ibb-init-amoled-voltage = <4000000>;
+				qcom,qpnp-ibb-init-lcd-voltage = <5500000>;
+
+				qcom,qpnp-ibb-soft-start = <400>;
+
+				qcom,qpnp-ibb-discharge-resistor = <300>;
+				qcom,qpnp-ibb-lab-pwrup-delay = <8000>;
+				qcom,qpnp-ibb-lab-pwrdn-delay = <8000>;
+				qcom,qpnp-ibb-en-discharge;
+
+				qcom,qpnp-ibb-full-pull-down;
+				qcom,qpnp-ibb-pull-down-enable;
+				qcom,qpnp-ibb-switching-clock-frequency = <1480>;
+				qcom,qpnp-ibb-limit-maximum-current = <1550>;
+				qcom,qpnp-ibb-debounce-cycle = <16>;
+				qcom,qpnp-ibb-limit-max-current-enable;
+				qcom,qpnp-ibb-ps-enable;
+			};
+
+		};
+	};
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
new file mode 100644
index 0000000..8b3a38da0
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qpnp-lcdb-regulator.txt
@@ -0,0 +1,250 @@
+QPNP LCDB (LCD Bias) Regulator
+
+QPNP LCDB module provides voltage bias to the LCD display panel. The biases
+are positive (VDISP - supported by LDO) and negative (VDISN - supported by
+NCP) voltage signals. The module also supports TTW (touch-to-wake) capability.
+
+This document describes the bindings for QPNP LCDB module.
+
+=======================
+Required Node Structure
+=======================
+
+LCDB module must be described in two level of device nodes.
+
+==============================
+First Level Node - LCDB module
+==============================
+
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be "qcom,qpnp-lcdb-regulator"
+
+- reg
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition:  Base address of the LCDB SPMI peripheral.
+
+Touch-to-wake (TTW) properties:
+
+TTW supports 2 modes of operation - HW and SW. In the HW mode the enable/disable
+logic is controlled by an external signal (pin) where as in the SW mode it is
+is controlled by a pre-configured timer (ton/toff) programmed in the TTW
+register.
+
+Properties below are specific to TTW mode only. They are sepecified in the
+main node.
+
+- qcom,ttw-enable
+	Usage:      optional
+	Value type:  <bool>
+	Definition: Touch to wake-up support enabled.
+
+- qcom,ttw-mode-sw
+	Usage:      optional
+	Value type:  <bool>
+	Definition: Touch to wake supported in SW mode.
+		    If not defined, ttw is enabled by HW pin.
+
+- qcom,attw-toff-ms
+	Usage:      required if 'qcom,ttw-mode-sw' is true.
+	Value type:  <bool>
+	Definition: Off time (in mS) for the VDISP/VDISN signals.
+		    Possible values are 4, 8, 16, 32.
+
+- qcom,attw-ton-ms
+	Usage:      required if 'qcom,ttw-mode-sw' is true.
+	Value type:  <bool>
+	Definition: ON time (in mS) for the VDISP/VDISN signals.
+		    Possible values are 4, 8, 16, 32.
+
+
+========================================
+Second Level Nodes - LDO/NCP/BOOST block
+========================================
+
+LDO / NCP subnode common properties:
+
+Properties below are common to the LDO and NCP bias.
+
+- label
+	Usage:      required
+	Value type: <string>
+	Definition: A string used to describe the bias type.
+		    Possible values are ldo, ncp, bst.
+
+- regulator-name
+	Usage:      required
+	Value type: <string>
+	Definition: A string used to describe the regulator.
+
+- regulator-min-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Minimum voltage (in uV) supported by the bias.
+
+- regulator-max-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Maximum voltage (in uV) supported by the bias.
+
+
+LDO subnode properties:
+
+Properties below are specific to LDO bias only.
+
+- qcom,ldo-voltage-mv
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Voltage (in mV) progammed for the LDO (VDISP).
+		    Possile values are 4000mV to 6000mV. The range
+		    4000mV to 4900mV is in 100mV steps and 4900mV to
+		    6000mV is in 50mV steps.
+
+- qcom,ldo-pd
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Pull-down configuration of LDO. Possible values are:
+		    1 - Enable pull-down
+		    0 - Disable pull-down
+
+- qcom,ldo-pd-strength
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Pull-down strength. Possible values are:
+		    0 - Weak pull-down
+		    1 - Strong pull-down
+
+- qcom,ldo-ilim-ma
+	Usage:      optional
+	Value type:  <u32>
+	Definition:  Current limit (in mA) of the LDO bias.
+		     Possible values are 110, 160, 210, 260, 310, 360, 410, 460.
+
+- qcom,ldo-soft-start-us
+	Usage:      optional
+	Value type:  <u32>
+	Definition:  Soft-start time (in uS) of the LDO bias.
+		     Possible values are 0, 500, 1000, 2000.
+
+
+NCP subnode properties:
+
+Properties below are specific to NCP bias only.
+
+- qcom,ncp-voltage-mv
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Voltage (in mV) progammed for the NCP (VDISN).
+		    Possile values are 4000mV to 6000mV. The range
+		    4000mV to 4900mV is in 100mV steps and 4900mV to
+		    6000mV is in 50mV steps.
+
+- qcom,ncp-pd
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Pull-down configuration of NCP. Possible values are:
+		    1 - Enable pull-down
+		    0 - Disable pull-down
+
+- qcom,ncp-pd-strength
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Pull-down strength. Possible values are:
+		    0 - Weak pull-down
+		    1 - Strong pull-down
+
+- qcom,ncp-ilim-ma
+	Usage:      optional
+	Value type:  <u32>
+	Definition:  Current limit (in mA) of the NCP bias.
+		     Possible values are 260, 460, 640, 810.
+
+- qcom,ncp-soft-start-us
+	Usage:      optional
+	Value type:  <u32>
+	Definition:  Soft-start time (in uS) of the NCP bias.
+		     Possible values are 0, 500, 1000, 2000.
+
+
+BOOST subnode properties:
+
+Properties below are specific to BOOST subnode only.
+
+- qcom,bst-pd
+	Usage:      optional
+	Value type:  <bool>
+	Definition: Pull-down configuration of BOOST. Possible values are:
+		    1 - Enable pull-down
+		    0 - Disable pull-down
+
+- qcom,bst-pd-strength
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Pull-down strength. Possible values are:
+		    0 - Weak pull-down
+		    1 - Strong pull-down
+
+- qcom,bst-ps
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Pulse-skip configuration for boost. Possible values are:
+		    1 - Enable Pulse-skip
+		    0 - Disable Pulse-skip
+
+- qcom,bst-ps-threshold-ma
+	Usage:      optional
+	Value type:  <u32>
+	Definition: Current threshold (in mA) at which pulse-skip is entered.
+		    Possible values are 50, 60, 70, 80.
+
+- qcom,bst-ilim-ma
+	Usage:      optional
+	Value type:  <u32>
+	Definition:  Current limit (in mA) of the BOOST rail.
+		     Possible values are 200 to 1600mA in 200mA steps.
+
+=======
+Example
+=======
+
+pm660l_lcdb: qpnp-lcdb@ec00 {
+	compatible = "qcom,qpnp-lcdb-regulator";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	reg = <0xec00 0x100>;
+
+	qcom,ttw-enable;
+
+	lcdb_ldo_vreg: ldo {
+		label = "ldo";
+		regulator-name = "lcdb_ldo";
+		regulator-min-microvolt = <4000000>;
+		regulator-max-microvolt = <6000000>;
+
+		qcom,ldo-voltage-mv = <5400>;
+		qcom,ldo-pd = <1>;
+		qcom,ldo-pd-strength = <1>;
+	};
+
+	lcdb_ncp_vreg: ncp {
+		label = "ncp";
+		regulator-name = "lcdb_ncp";
+		regulator-min-microvolt = <4000000>;
+		regulator-max-microvolt = <6000000>;
+
+		qcom,ncp-voltage-mv = <5400>;
+		qcom,ncp-pd = <1>;
+		qcom,ncp-pd-strength = <1>;
+	};
+
+	lcdb_bst: bst {
+		label = "bst";
+
+		qcom,bst-pd = <1>;
+		qcom,bst-pd-strength = <1>;
+		qcom,bst-ps = <1>;
+		qcom,bst-ps-threshold-ma = <50>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
new file mode 100644
index 0000000..5d80a04
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qpnp-oledb-regulator.txt
@@ -0,0 +1,230 @@
+QPNP OLEDB (AMOLED AVDD Bias) Regulator
+
+QPNP OLEDB module provides AVDD voltage bias to the AMOLED display panel.
+The supported voltage range is 5V to 8.1V.
+
+This document describes the bindings for QPNP OLEDB module.
+
+=======================
+Required Node Structure
+=======================
+
+- compatible
+	Usage:      required
+	Value type: <string>
+	Definition: should be "qcom,qpnp-oledb-regulator".
+
+- reg
+	Usage:      required
+	Value type: <prop-encoded-array>
+	Definition:  Base address of the OLEDB SPMI peripheral.
+
+- label
+	Usage:      required
+	Value type: <string>
+	Definition: A string used to describe the bias type(oledb).
+
+- regulator-name
+	Usage:      required
+	Value type: <string>
+	Definition: A string used to describe the regulator.
+
+- regulator-min-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Minimum voltage (in uV) supported by the bias (5000000uV).
+
+- regulator-max-microvolt
+	Usage:      required
+	Value type: <u32>
+	Definition: Maximum voltage (in uV) supported by the bias (8100000uV).
+
+- qcom,swire-control
+	Usage:      optional
+	Value type: <bool>
+	Definition: Enables the voltage programming through SWIRE signal.
+
+ qcom,ext-pin-control
+	Usage:      optional
+	Value type: <bool>
+	Definition: Configures the OLED module to be enabled by a external pin.
+
+ qcom,dynamic-ext-pinctl-config
+	Usage:      optional
+	Value type: <bool>
+	Definition:  Used to dynamically enable/disable the OLEDB module
+		     using external pin to avoid the glitches on the voltage
+		     rail.  This property is applicable only if qcom,ext-pin-ctl
+		     property is specified and it is specific to PM660A.
+
+ qcom,pbs-control
+	Usage:      optional
+	Value type: <bool>
+	Definition: PMIC PBS logic directly configures the output voltage update
+		    and pull down control.
+
+ qcom,oledb-init-voltage-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: Sets the AVDD bias voltage (in mV) when the module is
+		    already enabled. Applicable only if the qcom,swire-control
+		    property is not specified. Supported values are from 5.0V
+		    to 8.1V with a step of 100mV.
+
+qcom,oledb-default-voltage-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: Sets the default AVDD bias voltage (in mV) before module
+		    enable. Supported values are from 5.0V to 8.1V with the
+		    step of 100mV.
+
+qcom,bias-gen-warmup-delay-ns
+	Usage:      optional
+	Value type: <u32>
+	Definition: Bias generator warm-up time (ns). Supported values are
+		    6700, 13300, 267000, 534000.
+
+qcom,peak-curr-limit-ma
+	Usage:      optional
+	Value type: <u32>
+	Definition: Peak current limit (in mA). Supported values are 115, 265,
+		    415, 570, 720, 870, 1020, 1170.
+
+qcom,pull-down-enable
+	Usage:      optional
+	Value type: <u32>
+	Definition: Pull down configuration of OLEDB.
+		    1 - Enable pull-down
+		    0 - Disable pull-down
+
+qcom,negative-curr-limit-enable
+	Usage:      optional
+	Value type: <u32>
+	Definition: negative current limit enable/disable.
+			1 = enable negative current limit
+			0 = disable negative current limit
+
+qcom,negative-curr-limit-ma
+	Usage:      optional
+	Value type: <u32>
+	Definition: Negative current limit (in mA). Supported values are
+		    170, 300, 420, 550.
+
+qcom,enable-short-circuit
+	Usage:      optional
+	Value type: <u32>
+	Definition: Short circuit protection enable/disable.
+			1 = enable short circuit protection
+			0 = disable short circuit protection
+
+qcom,short-circuit-dbnc-time
+	usage:      optional
+	Value type: <u32>
+	Definitioan: Short circuit debounce time (in Fsw). Supported
+		     values are 2, 4, 8, 16.
+
+Fast precharge properties:
+-------------------------
+
+qcom,fast-precharge-ppulse-enable
+	usage:      optional
+	Value type: <u32>
+	Definitioan: Fast precharge pfet pulsing enable/disable.
+			1 = enable fast precharge pfet pulsing
+			0 = disable fast precharge pfet pulsing
+
+qcom,precharge-debounce-time-ms
+	usage:      optional
+	Value type: <u32>
+	Definitioan: Fast precharge debounce time (in ms). Supported
+		     values are 1, 2, 4, 8.
+
+qcom,precharge-pulse-period-us
+	usage:      optional
+	Value type: <u32>
+	Definitioan: Fast precharge pulse period (in us). Supported
+		     values are 3, 6, 9, 12.
+
+qcom,precharge-pulse-on-time-us
+	usage:      optional
+	Value type: <u32>
+	Definitioan: Fast precharge pulse on time (in ns). Supported
+		     values are 1200, 1800, 2400, 3000.
+
+Pulse Skip Modulation (PSM) properties:
+--------------------------------------
+
+qcom,psm-enable
+	Usage:      optional
+	Value type: <u32>
+	Definition: Pulse Skip Modulation mode.
+		    1 - Enable PSM mode
+		    0 - Disable PSM mode
+
+qcom,psm-hys-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: PSM hysterysis voltage (in mV).
+		    Supported values are 13mV and 26mV.
+
+qcom,psm-vref-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: Reference voltage(in mV) control for PSM comparator.
+		    Supported values are 440, 510, 580, 650, 715, 780, 850,
+		    and 920.
+
+Pulse Frequency Modulation (PFM) properties:
+-------------------------------------------
+
+qcom,pfm-enable
+	Usage:      optional
+	Value type: <u32>
+	Definition: Pulse Frequency Modulation mode.
+		    1 - Enable PFM mode
+		    0 - Disable PFM mode
+
+qcom,pfm-hys-mv
+	Usage:      optional
+	Value type: <u32>
+	Definition: PFM hysterysis voltage (in mV).
+		    Supported values are 13mV and 26mV.
+
+qcom,pfm-curr-limit-ma
+	Usage:      optional
+	Value type: <u32>
+	Definition: PFM current limit (in mA).
+		    Supported values are 130, 200, 270, 340.
+
+qcom,pfm-off-time-ns
+	Usage:      optional
+	Value type: <u32>
+	Definition: NFET off time at PFM (in ns).
+		    Supported values are 110, 240, 350, 480.
+
+=======
+Example
+=======
+
+pm660a_oledb: qpnp-oledb@e000 {
+	compatible = "qcom,qpnp-oledb-regulator";
+	#address-cells = <1>;
+	#size-cells = <1>;
+	reg = <0xe000 0x100>;
+
+	label = "oledb";
+	regulator-name = "regulator-oledb";
+	regulator-min-microvolt = <5000000>;
+	regulator-max-microvolt = <8100000>;
+
+	qcom,swire-control;
+	qcom,ext-pin-control;
+
+	qcom,oledb-default-voltage-mv = <5000>;
+	qcom,bias-gen-warmup-delay-ns = <6700>;
+	qcom,pull-down-enable = <1>;
+	qcom,peak-curr-limit-ma = <570>;
+
+	qcom, enable-psm = <1>;
+	qcom,psm-hys-mv = <13>;
+};
diff --git a/Documentation/devicetree/bindings/regulator/qpnp-regulator.txt b/Documentation/devicetree/bindings/regulator/qpnp-regulator.txt
new file mode 100644
index 0000000..601903b
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/qpnp-regulator.txt
@@ -0,0 +1,151 @@
+Qualcomm Technologies, Inc. QPNP Regulators
+
+qpnp-regulator is a regulator driver which supports regulators inside of PMICs
+that utilize the MSM SPMI implementation.
+
+Required properties:
+- compatible:      Must be "qcom,qpnp-regulator"
+- reg:             Specifies the SPMI address and size for this regulator device
+		   Note, this is the only property which can be used within a
+		   subnode.
+- regulator-name:  A string used as a descriptive name for regulator outputs
+- parent-supply:   phandle to the parent supply/regulator node
+
+Required structure:
+- A qcom,qpnp-regulator node must be a child of an spmi_device node.
+
+Optional properties:
+- interrupts:                  List of interrupts used by the regulator.
+- interrupt-names:             List of strings defining the names of the
+				interrupts in the 'interrupts' property 1-to-1.
+				Supported values are "ocp" for voltage switch
+				type regulators.  If an OCP interrupt is
+				specified, then the voltage switch will be
+				toggled off and back on when OCP triggers in
+				order to handle high in-rush current.
+- qcom,system-load:            Load in uA present on regulator that is not
+				captured by any consumer request
+- qcom,enable-time:            Time in us to delay after enabling the regulator
+- qcom,auto-mode-enable:       1 = Enable automatic hardware selection of
+				regulator mode (HPM vs LPM); not available on
+				boost type regulators
+			       0 = Disable auto mode selection
+- qcom,bypass-mode-enable:     1 = Enable bypass mode for an LDO type regulator
+				so that it acts like a switch and simply outputs
+				its input voltage
+			       0 = Do not enable bypass mode
+- qcom,ocp-enable:             1 = Allow over current protection (OCP) to be
+				enabled for voltage switch type regulators so
+				that they latch off automatically when over
+				current is detected.  OCP is enabled when in
+				HPM or auto mode.
+			       0 = Disable OCP
+- qcom,ocp-max-retries:        Maximum number of times to try toggling a voltage
+				switch off and back on as a result of
+				consecutive over current events.
+- qcom,ocp-retry-delay:        Time to delay in milliseconds between each
+				voltage switch toggle after an over current
+				event takes place.
+- qcom,pull-down-enable:       1 = Enable output pull down resistor when the
+				regulator is disabled
+			       0 = Disable pull down resistor
+- qcom,soft-start-enable:      1 = Enable soft start for LDO and voltage switch
+				type regulators so that output voltage slowly
+				ramps up when the regulator is enabled
+			       0 = Disable soft start
+- qcom,boost-current-limit:    This property sets the current limit of boost
+				type regulators; supported values are:
+					0 =  300 mA
+					1 =  600 mA
+					2 =  900 mA
+					3 = 1200 mA
+					4 = 1500 mA
+					5 = 1800 mA
+					6 = 2100 mA
+					7 = 2400 mA
+- qcom,pin-ctrl-enable:        Bit mask specifying which hardware pins should be
+				used to enable the regulator, if any; supported
+				bits are:
+					0 = ignore all hardware enable signals
+					BIT(0) = follow HW0_EN signal
+					BIT(1) = follow HW1_EN signal
+					BIT(2) = follow HW2_EN signal
+					BIT(3) = follow HW3_EN signal
+- qcom,pin-ctrl-hpm:           Bit mask specifying which hardware pins should be
+				used to force the regulator into high power
+				mode, if any; supported bits are:
+					0 = ignore all hardware enable signals
+					BIT(0) = follow HW0_EN signal
+					BIT(1) = follow HW1_EN signal
+					BIT(2) = follow HW2_EN signal
+					BIT(3) = follow HW3_EN signal
+					BIT(4) = follow PMIC awake state
+- qcom,vs-soft-start-strength: This property sets the soft start strength for
+				voltage switch type regulators; supported values
+				are:
+					0 = 0.05 uA
+					1 = 0.25 uA
+					2 = 0.55 uA
+					3 = 0.75 uA
+- qcom,hpm-enable:             1 = Enable high power mode (HPM), also referred
+				to as NPM.  HPM consumes more ground current
+				than LPM, but it can source significantly higher
+				load current.  HPM is not available on boost
+				type regulators.  For voltage switch type
+				regulators, HPM implies that over current
+				protection and soft start are active all the
+				time.  This configuration can be overwritten
+				by changing the regulator's mode dynamically.
+			       0 = Do not enable HPM
+- qcom,force-type: 	       Override the type and subtype register values. Useful for some
+				regulators that have invalid types advertised by the hardware.
+				The format is two unsigned integers of the form <type subtype>.
+
+Note, if a given optional qcom,* binding is not present, then the qpnp-regulator
+driver will leave that feature in the default hardware state.
+
+All properties specified within the core regulator framework can also be used.
+These bindings can be found in regulator.txt.
+
+Example:
+	qcom,spmi@fc4c0000 {
+		#address-cells = <1>;
+		#size-cells = <0>;
+		interrupt-controller;
+		#interrupt-cells = <3>;
+
+		qcom,pm8941@1 {
+			reg = <0x1>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			regulator@1400 {
+				regulator-name = "8941_s1";
+				#address-cells = <1>;
+				#size-cells = <1>;
+				compatible = "qcom,qpnp-regulator";
+				reg = <0x1400 0x300>;
+				regulator-min-microvolt = <1300000>;
+				regulator-max-microvolt = <1400000>;
+
+				qcom,ctl@1400 {
+					reg = <0x1400 0x100>;
+				};
+				qcom,ps@1500 {
+					reg = <0x1500 0x100>;
+				};
+				qcom,freq@1600 {
+					reg = <0x1600 0x100>;
+				};
+			};
+
+			regulator@4000 {
+				regulator-name = "8941_l1";
+				reg = <0x4000 0x100>;
+				compatible = "qcom,qpnp-regulator";
+				regulator-min-microvolt = <1225000>;
+				regulator-max-microvolt = <1300000>;
+				qcom,pull-down-enable = <1>;
+			};
+		};
+	};
diff --git a/Documentation/devicetree/bindings/rtc/qpnp-rtc.txt b/Documentation/devicetree/bindings/rtc/qpnp-rtc.txt
new file mode 100644
index 0000000..e0934b2
--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/qpnp-rtc.txt
@@ -0,0 +1,60 @@
+* msm-qpnp-rtc
+
+msm-qpnp-rtc is a RTC driver that supports 32 bit RTC housed inside PMIC.
+Driver utilizes MSM SPMI interface to communicate with the RTC module.
+RTC device is divided into two sub-peripherals one which controls basic RTC
+and other for controlling alarm.
+
+[PMIC RTC Device Declarations]
+
+-Root Node-
+
+Required properties :
+ - compatible:		Must be "qcom,qpnp-rtc"
+ - #address-cells:	The number of cells dedicated to represent an address
+			This must be set to '1'.
+ - #size-cells:		The number of cells dedicated to represent address
+			space range of a peripheral. This must be set to '1'.
+
+Optional properties:
+ - qcom,qpnp-rtc-write:		This property enables/disables rtc write
+				operation. If not mentioned rtc driver keeps
+				rtc writes disabled.
+				0 = Disable rtc writes.
+				1 = Enable rtc writes.
+ - qcom,qpnp-rtc-alarm-pwrup:	This property enables/disables feature of
+				powering up phone (from power down state)
+				through alarm interrupt.
+				If not mentioned rtc driver will disable
+				feature of powring-up phone through alarm.
+				0 = Disable powering up of phone through
+				alarm interrupt.
+				1 = Enable powering up of phone through
+				alarm interrupt.
+
+-Child Nodes-
+
+Required properties :
+ - reg :		Specify the spmi offset and size for device.
+ - interrupts:		Specifies alarm interrupt, only for rtc_alarm
+			sub-peripheral.
+
+Example:
+	qcom,pm8941_rtc {
+		compatible = "qcom,qpnp-rtc";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		qcom,qpnp-rtc-write = <0>;
+		qcom,qpnp-rtc-alarm-pwrup = <0>;
+
+		qcom,pm8941_rtc_rw@6000 {
+			reg = <0x6000 0x100>;
+		};
+
+		qcom,pm8941_rtc_alarm@6100 {
+			reg = <0x6100 0x100>;
+			interrupts = <0x0 0x61 0x1>;
+		};
+	};
+
+
diff --git a/Documentation/devicetree/bindings/scheduler/sched_hmp.txt b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt
new file mode 100644
index 0000000..ba1d4db
--- /dev/null
+++ b/Documentation/devicetree/bindings/scheduler/sched_hmp.txt
@@ -0,0 +1,35 @@
+* HMP scheduler
+
+This file describes the bindings for an optional HMP scheduler
+node (/sched-hmp).
+
+Required properties:
+
+Optional properties:
+
+- boost-policy: The HMP scheduler has two types of task placement boost
+policies.
+
+(1) boost-on-big policy make use of all big CPUs up to their full capacity
+before using the little CPUs. This improves performance on true b.L systems
+where the big CPUs have higher efficiency compared to the little CPUs.
+
+(2) boost-on-all policy place the tasks on the CPU having the highest
+spare capacity. This policy is optimal for SMP like systems.
+
+The scheduler sets the boost policy to boost-on-big on systems which has
+CPUs of different efficiencies. However it is possible that CPUs of the
+same micro architecture to have slight difference in efficiency due to
+other factors like cache size. Selecting the boost-on-big policy based
+on relative difference in efficiency is not optimal on such systems.
+The boost-policy device tree property is introduced to specify the
+required boost type and it overrides the default selection of boost
+type in the scheduler.
+
+The possible values for this property are "boost-on-big" and "boost-on-all".
+
+Example:
+
+sched-hmp {
+	boost-policy = "boost-on-all"
+}
diff --git a/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
new file mode 100644
index 0000000..90d8a35
--- /dev/null
+++ b/Documentation/devicetree/bindings/slimbus/slim-msm-ctrl.txt
@@ -0,0 +1,81 @@
+ Qualcomm Technologies,Inc SLIMBUS controller
+ Qualcomm Technologies,Inc implements 2 type of slimbus controllers:
+1. "qcom,slim-msm": This controller is used if applications processor
+	driver is controlling slimbus master component. This driver is
+	responsible for communicating with slave HW directly using
+	messaging interface, and doing data channel management. Driver
+	also communicates with satellite component (driver implemented
+	by other execution environment, such as ADSP) to get its
+	requirements for data channel and bandwidth requirements.
+2. "qcom,slim-ngd": This controller is used if applications processor
+	driver is controlling slimbus satellite component (also known as
+	Non-ported Generic Device, or NGD). This is light-weight slimbus
+	controller responsible for communicating with slave HW directly
+	over bus messaging interface, and communicating with master component
+	(driver residing on other execution environment, such as ADSP)
+	for bandwidth and data channel management.
+
+Required properties:
+
+ - reg : Offset and length of the register region(s) for the device
+ - reg-names : Register region name(s) referenced in reg above
+	 Required register resource entries are:
+	 "slimbus_physical": Physical adderss of controller register blocks
+	 "slimbus_bam_physical": Physical address of Bus Access Module (BAM)
+				 for this controller
+ - compatible : should be "qcom,slim-msm" if this is master component driver
+ - compatible : should be "qcom,slim-ngd" if this is satellite component driver
+ - cell-index : SLIMBUS number used for this controller
+ - interrupts : Interrupt numbers used by this controller
+ - interrupt-names : Required interrupt resource entries are:
+	"slimbus_irq" : Interrupt for SLIMBUS core
+	"slimbus_bam_irq" : Interrupt for controller core's BAM
+
+Optional property:
+ - reg entry for slew rate : If slew rate control register is provided, this
+	 entry should be used.
+ - reg-name for slew rate: "slimbus_slew_reg"
+ - qcom,min-clk-gear : Minimum clock gear at which this controller can be run
+		 (range: 1-10)
+		 Default value will be 1 if this entry is not specified
+ - qcom,max-clk-gear: Maximum clock gear at which this controller can be run
+		 (range: 1-10)
+		 Default value will be 10 if this entry is not specified
+ - qcom,rxreg-access: This boolean indicates that slimbus RX should use direct
+		 register access to receive data. This flag is only needed if
+		 BAM pipe is not available to receive data from slimbus
+ - qcom,apps-ch-pipes: This value represents BAM pipe-mask used by application
+		 processor for data channels. If this property is not defined,
+		 default mask of 0x3F000000 is used indicating apps can use 6
+		 pipes from 24-29.
+ - qcom,ea-pc: This value represents product code (PC) field of enumeration
+		 address (EA) for the QTI slimbus controller hardware.
+		 This value is needed if data-channels originating from apps
+		 are to be used, so that application processor can query
+		 logical address of the ported generic device to be used.
+		 Other than PC, fields of EA are same across platforms.
+ - qcom,slim-mdm: This value provides the identifier of slimbus component on
+		 external mdm. This property enables the slimbus driver to
+		 register and receive subsytem restart notification from mdm
+		 and follow appropriate steps to ensure communication on the bus
+		 can be resumed after mdm-restart.
+ - qcom,subsys-name: This value provides the subsystem name where slimbus master
+		 is present. This property enables the slimbus driver to
+		 register and receive subsytem restart notification from subsystem
+		 and follow appropriate steps to ensure communication on the bus
+		 can be resumed after subsytem restart. By default slimbus driver
+		 register with ADSP subsystem.
+Example:
+	slim@fe12f000 {
+		cell-index = <1>;
+		compatible = "qcom,slim-msm";
+		reg = <0xfe12f000 0x35000>,
+		      <0xfe104000 0x20000>;
+		reg-names = "slimbus_physical", "slimbus_bam_physical";
+		interrupts = <0 163 0 0 164 0>;
+		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
+		qcom,min-clk-gear = <10>;
+		qcom,rxreg-access;
+		qcom,apps-ch-pipes = <0x60000000>;
+		qcom,ea-pc = <0x30>;
+	};
diff --git a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
index 66ec9c5..d0800d3 100644
--- a/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
+++ b/Documentation/devicetree/bindings/sound/qcom-audio-dev.txt
@@ -1,5 +1,66 @@
 Qualcomm technologies inc audio devices for ALSA sound SoC
 
+* msm-pcm
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-dsp"
+
+ - qcom,msm-pcm-dsp-id : device node id
+
+* msm-pcm-low-latency
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-dsp"
+
+ - qcom,msm-pcm-dsp-id : device node id
+
+   Optional properties
+
+      - qcom,msm-pcm-low-latency : Flag indicating whether
+        the device node is of type low latency.
+
+      - qcom,latency-level : Flag indicating whether the device node
+                            is of type regular low latency or ultra
+                            low latency.
+                            regular : regular low latency stream
+                            ultra : ultra low latency stream
+                            ull-pp : ultra low latency stream with post-processing capability
+
+* msm-pcm-dsp-noirq
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-dsp-noirq";
+
+   Optional properties
+
+      - qcom,msm-pcm-low-latency : Flag indicating whether
+        the device node is of type low latency
+
+      - qcom,latency-level : Flag indicating whether the device node
+                           is of type low latency or ultra low latency
+                           ultra : ultra low latency stream
+                           ull-pp : ultra low latency stream with post-processing capability
+* msm-pcm-routing
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-routing"
+
+* msm-pcm-lpa
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-lpa"
+
+* msm-compr-dsp
+
+Required properties:
+
+ - compatible : "qcom,msm-compr-dsp"
+
 * msm-compress-dsp
 
 Required properties:
@@ -14,8 +75,2460 @@
 	8909 purpose.If the ADSP version is anything other than this
 	we use new ADSP APIs.
 
+* msm-voip-dsp
+
+Required properties:
+
+ - compatible : "qcom,msm-voip-dsp"
+
+* msm-pcm-voice
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-voice"
+ - qcom,destroy-cvd : Flag indicating whether to destroy cvd at
+                      the end of call for low memory targets
+
+* msm-voice-host-pcm
+
+Required properties:
+
+ - compatible : "qcom,msm-voice-host-pcm"
+
+* msm-voice-svc
+
+Required properties:
+
+ - compatible : "qcom,msm-voice-svc"
+
+* msm-stub-codec
+
+Required properties:
+
+ - compatible : "qcom,msm-stub-codec"
+
+* msm-hdmi-dba-codec-rx
+
+Required properties:
+
+ - compatible : "qcom,msm-hdmi-dba-codec-rx"
+ - qcom,dba-bridge-chip: String info to indicate which bridge-chip
+                         is used for HDMI using DBA.
+
+* msm-dai-fe
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-fe"
+
+* msm-pcm-afe
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-afe"
+
+* msm-pcm-dtmf
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-dtmf"
+ - qcom,msm-pcm-dtmf : Enable DTMF driver in Audio. DTMF driver is
+   used for generation and detection of DTMF tones, when user is in
+   active voice call. APR commands are sent from DTMF driver to ADSP.
+
+* msm-dai-stub
+
+[First Level Nodes]
+
+Required properties:
+
+ - compatible : "msm-dai-stub"
+
+[Second Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-stub-dev"
+ - qcom,msm-dai-stub-dev-id : Stub dai port ID value is from 0 to 3.
+   This enables stub CPU dai in Audio. The stub dai is used when
+   there is no real backend in Audio.
+
+* msm-dai-q6-spdif
+
+Optional properties:
+
+ - compatible : "msm-dai-q6-spdif"
+
+* msm-dai-q6-hdmi
+
+Required properties:
+ - compatible : "msm-dai-q6-hdmi"
+ - qcom,msm-dai-q6-dev-id : The hdmi multi channel port ID.
+   It is passed onto the dsp from the apps to form an audio
+   path to the HDMI device. Currently the only supported value
+   is 8, which indicates the rx path used for audio playback
+   on HDMI device.
+
+* msm-lsm-client
+
+Required properties:
+
+ - compatible : "qcom,msm-lsm-client"
+
+* msm-pcm-loopback
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-loopback"
+
+* msm-dai-q6
+
+[First Level Nodes]
+
+Required properties:
+
+ - compatible : "msm-dai-q6"
+
+Optional properties:
+
+ - qcom,ext-spk-amp-supply : External speaker amplifier power supply.
+ - qcom,ext-spk-amp-gpio : External speaker amplifier enable signal.
+
+[Second Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-q6-dev"
+ - qcom,msm-dai-q6-dev-id : The slimbus multi channel port ID
+                            Value is from 16384 to 16397
+                            BT SCO port ID value from 12288 to 12289
+                            RT Proxy port ID values from 224 to 225 and 240 to
+			    241
+                            FM Rx and TX port ID values from 12292 to 12293
+                            incall record Rx and TX port ID values from 32771 to 32772
+                            inCall Music Delivery port ID is 32773
+			    incall Music 2 Delivery port ID is 32770
+
+* msm-auxpcm
+
+Required properties:
+
+ - compatible :                           "qcom,msm-auxpcm-dev"
+
+ - qcom,msm-cpudai-auxpcm-mode:           mode information. The first value is
+                                          for 8khz mode, the second is for
+                                          16khz
+                                          0 - for PCM
+
+ - qcom,msm-cpudai-auxpcm-sync:           sync information. The first value is
+                                          for 8khz mode, the second is for
+                                          16khz
+
+ - qcom,msm-cpudai-auxpcm-frame:          No.of bytes per frame. The first
+                                          value is for 8khz mode, the second
+                                          is for 16khz
+                                          5 - 256BPF
+                                          4 - 128BPF
+
+ - qcom,msm-cpudai-auxpcm-quant:          Type of quantization. The first
+                                          value is for 8khz mode, the second
+                                          is for 16khz
+                                          2 - Linear quantization
+
+ - qcom,msm-cpudai-auxpcm-num-slots:      Number of slots per mode in the
+                                          msm-cpudai-auxpcm-slot-mapping
+                                          array.
+                                          The first value is for 8khz mode, the
+                                          second is for 16khz. Max number of
+                                          slots supported by DSP is 4, anything
+                                          above 4 will be truncated to 4 when
+                                          sent to DSP.
+
+ - qcom,msm-cpudai-auxpcm-slot-mapping:   Array of slot numbers for multi
+                                          slot scenario. The first array
+                                          is for 8khz mode, the second is
+                                          for 16khz. The size of the array
+                                          is determined by the value in
+                                          qcom,msm-cpudai-auxpcm-num-slots
+
+ - qcom,msm-cpudai-auxpcm-data:           Data field - 0. The first value is
+                                          for 8khz mode, the second is for
+                                          16khz
+
+ - qcom,msm-cpudai-auxpcm-pcm-clk-rate:   Clock rate for pcm - 2048000. The
+                                          first value is for 8khz mode, the
+                                          second is for 16KHz mode. When clock
+					  rate is set to zero, then external
+					  clock is assumed.
+
+ - qcom,msm-auxpcm-interface:             name of AUXPCM interface "primary"
+                                          indicates primary AUXPCM interface
+                                          "secondary" indicates secondary
+                                          AUXPCM interface
+Optional properties:
+
+- pinctrl-names:			  Pinctrl state names for each pin
+					  group configuration.
+- pinctrl-x: 				  Defines pinctrl state for each pin
+					  group
+- qcom,msm-cpudai-afe-clk-ver:            Indicates version of AFE clock
+					  interface to be used for enabling
+					  PCM clock. If not defined, selects
+					  default AFE clock interface.
+
+* msm-pcm-hostless
+
+Required properties:
+
+ - compatible : "qcom,msm-pcm-hostless"
+
+* msm-ocmem-audio
+
+Required properties:
+
+ - compatible :                            "qcom,msm-ocmem-audio"
+
+ - qcom,msm_bus,name:                      Client name
+
+ - qcom,msm_bus,num_cases:                 Total number of use cases
+
+ - qcom,msm_bus,active_only:               Context flag for requests in active
+					   or dual (active & sleep) contex
+
+ - qcom,msm_bus,num_paths:                 Total number of master-slave pairs
+
+ - qcom,msm_bus,vectors:                   Arrays of unsigned integers
+					   representing:
+					       master-id, slave-id, arbitrated
+					       bandwidth,
+					       instantaneous bandwidth
+* wcd9xxx_intc
+
+Required properties:
+
+ - compatible :                            "qcom,wcd9xxx-irq"
+
+ - interrupt-controller :                  Mark this device node as an
+					   interrupt controller
+
+ - #interrupt-cells :                      Should be 1
+
+ - interrupt-parent :                      Parent interrupt controller
+
+ - qcom,gpio-connect                       Gpio that connects to parent
+                                           interrupt controller
+
+* audio-ext-clk
+
+Required properties:
+
+ - compatible :                            "qcom,audio-ref-clk"
+
+ - qcom,audio-ref-clk-gpio  :               PMIC or APQ gpio that will be
+                                            requested to enable reference
+                                            or external clock.
+
+Optional properties:
+
+ - qcom,node_has_rpm_clock:                 Boolean property used to indicate
+                                            whether ref. clock can be enabled
+                                            with a gpio toggle or Kernel clock
+                                            API call.
+
+ - clock-names:                             Name of the PMIC clock that needs
+                                            to be enabled for audio ref clock.
+                                            This clock is set as parent.
+
+ - clocks:                                  phandle reference to the parent
+                                            clock.
+
+* audio_slimslave
+
+Required properties:
+
+ - compatible :                            "qcom,audio-slimslave"
+
+ - elemental-addr:                         slimbus slave enumeration address.
+
+* msm-cpe-lsm
+
+Required properties:
+
+ - compatible : "qcom,msm-cpe-lsm"
+ - qcom,msm-cpe-lsm-id : lsm afe port ID. CPE lsm driver uses
+   this property to find out the input afe port ID. Currently
+   only supported values are 1 and 3.
+
+* wcd_us_euro_gpio
+
+Required properties:
+
+ - compatible : "qcom,msm-cdc-pinctrl"
+
+* msm-dai-slim
+
+Required properties:
+
+ - compatible :                            "qcom,msm-dai-slim"
+
+ - elemental-addr:                         slimbus slave enumeration address.
+
+* wcd_gpio_ctrl
+
+Required properties:
+
+ - compatible :                            "qcom,msm-cdc-pinctrl"
+
+ - qcom,cdc-rst-n-gpio :                   TLMM GPIO number
+
+ - pinctrl-names:                          Pinctrl state names for each pin
+                                           group configuration.
+ - pinctrl-x:                              Defines pinctrl state for each pin
+                                           group.
+* msm_cdc_pinctrl
+
+Required properties:
+
+ - compatible :                            "qcom,msm-cdc-pinctrl"
+
+ - pinctrl-names:                          Pinctrl state names for each pin
+                                           group configuration.
+ - pinctrl-x:                              Defines pinctrl state for each pin
+                                           group.
+
+* wcd_dsp_glink
+
+Required properties:
+
+ - compatible :                            "qcom,wcd-dsp-glink"
+
+* msm_ext_disp_audio_codec_rx
+
+Required properties:
+
+ - compatible :                            "qcom,msm-ext-disp-audio-codec-rx"
+
 Example:
 
+	qcom,msm-pcm {
+		compatible = "qcom,msm-pcm-dsp";
+		qcom,msm-pcm-dsp-id = <0>;
+	};
+
+	qcom,msm-pcm-low-latency {
+		compatible = "qcom,msm-pcm-dsp";
+		qcom,msm-pcm-dsp-id = <1>;
+		qcom,msm-pcm-low-latency;
+	};
+
+        qcom,msm-pcm-routing {
+                compatible = "qcom,msm-pcm-routing";
+        };
+
+        qcom,msm-pcm-lpa {
+                compatible = "qcom,msm-pcm-lpa";
+        };
+
+        qcom,msm-compr-dsp {
+                compatible = "qcom,msm-compr-dsp";
+        };
+
         qcom,msm-compress-dsp {
                 compatible = "qcom,msm-compress-dsp";
         };
+
+        qcom,msm-voip-dsp {
+                compatible = "qcom,msm-voip-dsp";
+        };
+
+	qcom,msm-pcm-voice {
+		compatible = "qcom,msm-pcm-voice";
+		qcom,destroy-cvd;
+	};
+
+        qcom,msm-voice-host-pcm {
+                compatible = "qcom,msm-voice-host-pcm";
+        };
+
+        qcom,msm-stub-codec {
+                compatible = "qcom,msm-stub-codec";
+        };
+
+        qcom,msm-dai-fe {
+                compatible = "qcom,msm-dai-fe";
+        };
+
+	qcom,msm-pcm-dtmf {
+		compatible = "qcom,msm-pcm-dtmf";
+	};
+
+	qcom,msm-dai-stub {
+		compatible = "qcom,msm-dai-stub";
+	};
+
+	qcom,msm-dai-q6-spdif {
+		compatible = "qcom,msm-dai-q6-spdif";
+	};
+
+	qcom,msm-dai-q6-hdmi {
+		compatible = "qcom,msm-dai-q6-hdmi";
+		qcom,msm-dai-q6-dev-id = <8>;
+	};
+
+	dai_dp: qcom,msm-dai-q6-dp {
+		compatible = "qcom,msm-dai-q6-hdmi";
+		qcom,msm-dai-q6-dev-id = <24608>;
+	};
+
+	qcom,msm-dai-q6 {
+		compatible = "qcom,msm-dai-q6";
+		qcom,msm-dai-q6-sb-0-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16384>;
+		};
+
+		qcom,msm-dai-q6-sb-0-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16385>;
+		};
+
+		qcom,msm-dai-q6-sb-1-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16386>;
+		};
+
+		qcom,msm-dai-q6-sb-1-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16387>;
+		};
+
+		qcom,msm-dai-q6-sb-3-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16390>;
+		};
+
+		qcom,msm-dai-q6-sb-3-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16391>;
+		};
+
+		qcom,msm-dai-q6-sb-4-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16392>;
+		};
+
+		qcom,msm-dai-q6-sb-4-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16393>;
+		};
+
+		qcom,msm-dai-q6-sb-5-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16395>;
+		};
+
+		qcom,msm-dai-q6-sb-6-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16396>;
+		};
+
+		qcom,msm-dai-q6-sb-6-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <16397>;
+		};
+
+		qcom,msm-dai-q6-bt-sco-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12288>;
+		};
+
+		qcom,msm-dai-q6-bt-sco-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12289>;
+		};
+
+		qcom,msm-dai-q6-int-fm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12292>;
+		};
+
+		qcom,msm-dai-q6-int-fm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <12293>;
+		};
+
+		qcom,msm-dai-q6-be-afe-pcm-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <224>;
+		};
+
+		qcom,msm-dai-q6-be-afe-pcm-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <225>;
+		};
+
+		qcom,msm-dai-q6-afe-proxy-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <241>;
+		};
+
+		qcom,msm-dai-q6-afe-proxy-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <240>;
+		};
+
+		qcom,msm-dai-q6-incall-record-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32771>;
+		};
+
+		qcom,msm-dai-q6-incall-record-tx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32772>;
+		};
+
+		qcom,msm-dai-q6-incall-music-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32773>;
+		};
+
+		qcom,msm-dai-q6-incall-music-2-rx {
+			compatible = "qcom,msm-dai-q6-dev";
+			qcom,msm-dai-q6-dev-id = <32770>;
+		};
+	};
+
+	qcom,msm-pri-auxpcm {
+		qcom,msm-cpudai-auxpcm-mode = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-sync = <1>, <1>;
+		qcom,msm-cpudai-auxpcm-frame = <5>, <4>;
+		qcom,msm-cpudai-auxpcm-quant = <2>, <2>;
+		qcom,msm-cpudai-auxpcm-num-slots = <4>, <4>;
+		qcom,msm-cpudai-auxpcm-slot-mapping = <1 0 0 0>, <1 3 0 0>;
+		qcom,msm-cpudai-auxpcm-data = <0>, <0>;
+		qcom,msm-cpudai-auxpcm-pcm-clk-rate = <2048000>, <2048000>;
+		qcom,msm-auxpcm-interface = "primary";
+		compatible = "qcom,msm-auxpcm-dev";
+		pinctrl-names = "default", "idle";
+		pinctrl-0 = <&pri_aux_pcm_active &pri_aux_pcm_din_active>;
+		pinctrl-1 = <&pri_aux_pcm_sleep &pri_aux_pcm_din_sleep>;
+	};
+
+        qcom,msm-pcm-hostless {
+                compatible = "qcom,msm-pcm-hostless";
+        };
+
+	qcom,msm-ocmem-audio {
+		compatible = "qcom,msm-ocmem-audio";
+		qcom,msm_bus,name = "audio-ocmem";
+		qcom,msm_bus,num_cases = <2>;
+		qcom,msm_bus,active_only = <0>;
+		qcom,msm_bus,num_paths = <1>;
+		qcom,msm_bus,vectors =
+			<11 604 0 0>,
+			<11 604 32505856 325058560>;
+	};
+
+	wcd9xxx_intc: wcd9xxx-irq {
+		compatible = "qcom,wcd9xxx-irq";
+		interrupt-controller;
+		#interrupt-cells = <1>;
+		interrupt-parent = <&msmgpio>;
+		interrupts = <72 0>;
+		interrupt-names = "cdc-int";
+	};
+
+	clock_audio: audio_ext_clk {
+		compatible = "qcom,audio-ref-clk";
+		qcom,audio-ref-clk-gpios = <&pm8994_gpios 15 0>;
+		clock-names = "osr_clk";
+		clocks = <&clock_rpm clk_div_clk1>;
+		qcom,node_has_rpm_clock;
+		#clock-cells = <1>;
+		pinctrl-names = "sleep", "active";
+		pinctrl-0 = <&spkr_i2s_clk_sleep>;
+		pinctrl-1 = <&spkr_i2s_clk_active>;
+	};
+
+	audio_slimslave {
+		compatible = "qcom,audio-slimslave";
+		elemental-addr = [ff ff ff ff 17 02];
+	};
+
+	msm_dai_slim {
+		compatible = "qcom,msm_dai_slim";
+		elemental-addr = [ff ff ff fe 17 02];
+	};
+
+	wcd_gpio_ctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		qcom,cdc-rst-n-gpio = <&tlmm 64 0>;
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&cdc_reset_active>;
+		pinctrl-1 = <&cdc_reset_sleep>;
+	};
+
+	msm_cdc_pinctrl {
+		compatible = "qcom,msm-cdc-pinctrl";
+		pinctrl-names = "aud_active", "aud_sleep";
+		pinctrl-0 = <&cdc_reset_active>;
+		pinctrl-1 = <&cdc_reset_sleep>;
+	};
+
+	wcd_dsp_glink {
+		compatible = "qcom,wcd-dsp-glink";
+	};
+
+	msm_ext_disp_audio_codec_rx {
+		compatible = "qcom,msm-ext-disp-audio-codec-rx";
+	};
+
+
+* MSM8916 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8x16-audio-codec"
+- qcom,model : The user-visible name of this sound card.
+- qcom,msm-snd-card-id : This id is used to recognize the sound card number
+- qcom,msm-codec-type : This property is used to recognize the codec type
+  internal or external.
+- qcom,msm-hs-micbias-type : This property is used to recognize the headset
+  micbias type, internal or external.
+- qcom,msm-ext-pa : This property is used to inform machine driver about
+  the connection of external PA over available MI2S interfaces,
+  following values can be given to this property.
+  primary -> Primary MI2S interface
+  secondary -> Secondary MI2S interface
+  tertiary -> Tertiary MI2S interface
+  quaternary -> Quaternary MI2S interface
+- qcom,msm-mclk-freq : This property is used to inform machine driver about
+mclk frequency needs to be configured for internal and external PA.
+- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL
+switch type on target typically the switch type will be normally open or
+normally close, value for this property 0 for normally close and 1 for
+normally open.
+-  qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND
+switch type on target typically the switch type will be normally open or
+normally close, value for this property 0 for normally close and 1 for
+normally open.
+- qcom,audio-routing : A list of the connections between audio components.
+- pinctrl-names : Pincntrl entries to configure the PDM gpio lines and
+		  cross connection switch gpio accordingly
+- pinctrl-0 : This explains the active state of the PDM gpio lines
+- pinctrl-1 : This explains the suspend state of the PDM gpio lines
+- pinctrl-2 : This explains the active state of the cross connection
+	      gpio lines
+- pinctrl-3 : This explains the suspend state of the cross connection
+              gpio lines
+- qcom,tapan-mclk-clk-freq : Tapan mclk Freq in Hz.
+- qcom,prim-auxpcm-gpio-clk  : GPIO on which Primary AUXPCM clk signal is coming.
+- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming.
+- qcom,prim-auxpcm-gpio-din  : GPIO on which Primary AUXPCM DIN signal is coming.
+- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming.
+- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port
+- qcom,tapan-codec-9302: Indicates that this device node is for WCD9302 audio
+			    codec.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+
+Optional Properties:
+- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+
+Example:
+
+	msm_dig_codec: qcom,msm-int-codec {
+		compatible = "qcom,msm_int_core_codec";
+		qcom,dig-cdc-base-addr = <0xc0f0000>;
+	};
+
+	sound {
+		compatible = "qcom,msm8x16-audio-codec";
+		qcom,model = "msm8x16-snd-card";
+		qcom,msm-snd-card-id = <0>;
+		qcom,msm-codec-type = "internal";
+		qcom,msm-ext-pa = <0>;
+		qcom,msm-mclk-freq = <12288000>;
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,msm-hs-micbias-type = "internal";
+		qcom,cdc-us-euro-gpios = <&msmgpio 120 0>;
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"INT_LDO_H", "MCLK",
+			"MIC BIAS External", "Handset Mic",
+			"MIC BIAS Internal2", "Headset Mic",
+			"MIC BIAS External", "Secondary Mic",
+			"AMIC1", "MIC BIAS External",
+			"AMIC2", "MIC BIAS Internal2",
+			"AMIC3", "MIC BIAS External";
+		pinctrl-names = "cdc_pdm_lines_act",
+				"cdc_pdm_lines_sus",
+				"cross_conn_det_act",
+				"cross_conn_det_sus";
+		pinctrl-0 = <&cdc_pdm_lines_act>;
+		pinctrl-1 = <&cdc_pdm_lines_sus>;
+		pinctrl-2 = <&cross_conn_det_act>;
+		pinctrl-3 = <&cross_conn_det_sus>;
+		qcom,tapan-mclk-clk-freq = <9600000>;
+		qcom,prim-auxpcm-gpio-clk  = <&msm_gpio 63 0>;
+		qcom,prim-auxpcm-gpio-sync = <&msm_gpio 64 0>;
+		qcom,prim-auxpcm-gpio-din  = <&msm_gpio 65 0>;
+		qcom,prim-auxpcm-gpio-dout = <&msm_gpio 66 0>;
+		qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
+		qcom,tapan-codec-9302;
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&lpa>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
+				"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa";
+		asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>, <&dai_dp>,
+				<&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
+				<&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
+				<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
+				"msm-dai-q6-dp.24608",
+				"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+				"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
+				"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
+				"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+				"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+				"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+				"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
+		asoc-codec = <&stub>, <&pm8916_tombak_dig>;
+		asoc-codec-names = "msm-stub-codec.1", "tombak_codec";
+	};
+
+* MSM8974 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8974-audio-taiko"
+- qcom,model : The user-visible name of this sound card.
+- reg : Offset and length of the register region(s) for MI2S/PCM MUX
+- reg-names : Register region name(s) referenced in reg above
+	 Required register resource entries are:
+	 "lpaif_pri_mode_muxsel": Physical address of MUX to select between
+				  Primary PCM and Primary MI2S
+	 "lpaif_sec_mode_muxsel": Physical address of MUX to select between
+				  Secondary PCM and Secondary MI2S
+	 "lpaif_tert_mode_muxsel": Physical address of MUX to select between
+				   Primary PCM and Tertiary MI2S
+	 "lpaif_quat_mode_muxsel": Physical address of MUX to select between
+				   Secondary PCM and Quarternary MI2S
+- qcom,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source.
+- qcom,cdc-mclk-gpios : GPIO on which mclk signal is coming.
+- qcom,taiko-mclk-clk-freq : Taiko mclk Freq in Hz. currently only 9600000Hz
+				is supported.
+- qcom,prim-auxpcm-gpio-clk  : GPIO on which Primary AUXPCM clk signal is coming.
+- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming.
+- qcom,prim-auxpcm-gpio-din  : GPIO on which Primary AUXPCM DIN signal is coming.
+- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming.
+- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port
+		 Possible Values:
+		 prim-gpio-prim : Primary AUXPCM shares GPIOs with Primary MI2S
+		 prim-gpio-tert : Primary AUXPCM shares GPIOs with Tertiary MI2S
+- qcom,sec-auxpcm-gpio-clk  : GPIO on which Secondary AUXPCM clk signal is coming.
+- qcom,sec-auxpcm-gpio-sync : GPIO on which Secondary AUXPCM SYNC signal is coming.
+- qcom,sec-auxpcm-gpio-din  : GPIO on which Secondary AUXPCM DIN signal is coming.
+- qcom,sec-auxpcm-gpio-dout : GPIO on which Secondary AUXPCM DOUT signal is coming.
+- qcom,us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+Optional properties:
+- qcom,hdmi-audio-rx: specifies if HDMI audio support is enabled or not.
+- qcom,ext-ult-spk-amp-gpio : GPIO for enabling of speaker path amplifier.
+
+- qcom,ext-ult-lo-amp-gpio: GPIO to enable external ultrasound lineout
+			    amplifier.
+
+- qcom,headset-jack-type-NO: Adjust GPIO level based on the headset jack type.
+- qcom,tapan-codec-9302: Indicates that this device node is for WCD9302 audio
+			    codec.
+- qcom,mbhc-bias-internal: Flag to indicate if internal micbias should be used
+			   for headset detection.
+- qcom,dock-plug-det-irq: Interrupt line to detect Docking/Undocking of Liquid
+			  device
+- qcom,ext-spk-rear-panel-irq: Interrupt line to detect rear panel speakers
+              jack for Dragon Board.
+- qcom,ext-spk-front-panel-irq: Interrupt line to detect front panel speakers
+              jack for Dragon Board.
+- qcom,ext-mic-front-panel-irq: Interrupt line to detect front panel microphone
+              jack for Dragon Board.
+- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware.
+		Possible Values:
+		4-pole-jack : Jack on the hardware is 4-pole.
+		5-pole-jack : Jack on the hardware is 5-pole.
+		6-pole-jack : Jack on the hardware is 6-pole.
+
+* APQ8074 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,apq8074-audio-taiko"
+
+Example:
+
+sound {
+	compatible = "qcom,msm8974-audio-taiko";
+	qcom,model = "msm8974-taiko-snd-card";
+
+	qcom,audio-routing =
+		"RX_BIAS", "MCLK",
+		"LDO_H", "MCLK",
+		"HEADPHONE", "LDO_H",
+		"Ext Spk Bottom Pos", "LINEOUT1",
+		"Ext Spk Bottom Neg", "LINEOUT3",
+		"Ext Spk Top Pos", "LINEOUT2",
+		"Ext Spk Top Neg", "LINEOUT4",
+		"AMIC1", "MIC BIAS1 Internal1",
+		"MIC BIAS1 Internal1", "Handset Mic",
+		"AMIC2", "MIC BIAS2 External",
+		"MIC BIAS2 External", "Headset Mic",
+		"AMIC3", "MIC BIAS3 Internal1",
+		"MIC BIAS3 Internal1", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS1 Internal2",
+		"MIC BIAS1 Internal2", "ANCLeft Headset Mic",
+		"DMIC1", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic1",
+		"DMIC2", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic2",
+		"DMIC3", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic3",
+		"DMIC4", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic4",
+		"DMIC5", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic5",
+		"DMIC6", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic6";
+
+	qcom,cdc-mclk-gpios = <&pm8941_gpios 15 0>;
+	qcom,taiko-mclk-clk-freq = <9600000>;
+	qcom,us-euro-gpios = <&pm8941_gpios 20 0>;
+
+	qcom,hdmi-audio-rx;
+	qcom,ext-ult-lo-amp-gpio = <&pm8941_gpios 6 0>;
+
+	qcom,ext-mclk-gpio  = <&msmgpio 47 0>;
+	qcom,dock-plug-det-irq = <&pm8841_mpps 2 0>;
+	qcom,prim-auxpcm-gpio-clk  = <&msmgpio 65 0>;
+	qcom,prim-auxpcm-gpio-sync = <&msmgpio 66 0>;
+	qcom,prim-auxpcm-gpio-din  = <&msmgpio 67 0>;
+	qcom,prim-auxpcm-gpio-dout = <&msmgpio 68 0>;
+	qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
+	qcom,sec-auxpcm-gpio-clk  = <&msmgpio 79 0>;
+	qcom,sec-auxpcm-gpio-sync = <&msmgpio 80 0>;
+	qcom,sec-auxpcm-gpio-din  = <&msmgpio 81 0>;
+	qcom,sec-auxpcm-gpio-dout = <&msmgpio 82 0>;
+	qcom,mbhc-audio-jack-type = "4-pole-jack";
+};
+
+* msm-dai-mi2s
+
+[First Level Nodes]
+
+Required properties:
+
+ - compatible : "msm-dai-mi2s"
+
+ [Second Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-q6-mi2s"
+ - qcom,msm-dai-q6-mi2s-dev-id: MSM or MDM can use Slimbus or I2S interface to
+				transfer data to (WCD9XXX) codec.
+				If slimbus interface is used then "msm-dai-q6"
+				needs to be filled with correct data for
+				slimbus interface.
+				The sections "msm-dai-mi2s" is used by MDM or
+				MSM to use I2S interface with codec.
+				This section is used by CPU driver in ASOC MSM
+				to configure MI2S interface. MSM internally
+				has multiple MI2S namely Primary, Secondary,
+				Tertiary and Quaternary MI2S.
+				They are represented with id 0, 1, 2, 3
+				respectively.
+				The field "qcom,msm-dai-q6-mi2s-dev-id"
+				represents which of the MI2S block is used.
+				These MI2S are connected to I2S interface.
+
+ - qcom,msm-mi2s-rx-lines:	Each MI2S interface in MSM has one or more SD
+				lines. These lines are used for data transfer
+				between codec and MSM.
+				This element in indicates which output RX lines
+				are used in the MI2S interface.
+
+ - qcom,msm-mi2s-tx-lines:  	Each MI2S interface in MSM has one or more SD
+				lines. These lines are used for data transfer
+				between codec and MSM.
+				This element in indicates which input TX lines
+				are used in the MI2S interface.
+
+Optional properties:
+
+- pinctrl-names:		Pinctrl state names for each pin group
+				configuration.
+- pinctrl-x: 			Defines pinctrl state for each pin group
+
+Example:
+
+qcom,msm-dai-mi2s {
+		compatible = "qcom,msm-dai-mi2s";
+		qcom,msm-dai-q6-mi2s-prim {
+			compatible = "qcom,msm-dai-q6-mi2s";
+			qcom,msm-dai-q6-mi2s-dev-id = <0>;
+			qcom,msm-mi2s-rx-lines = <2>;
+			qcom,msm-mi2s-tx-lines = <1>;
+			pinctrl-names = "default", "idle";
+			pinctrl-0 = <&tert_mi2s_active &tert_mi2s_sd0_active>;
+			pinctrl-1 = <&tert_mi2s_sleep &tert_mi2s_sd0_sleep>;
+		};
+};
+
+* msm-adsp-loader
+
+Required properties:
+ - compatible : "qcom,adsp-loader"
+ - qcom,adsp-state:
+	It is possible that some MSM use PIL to load the ADSP image. While
+	other MSM may use SBL to load the ADSP image at boot. Audio APR needs
+	state of ADSP to register and enable APR to be used for sending commands
+	to ADSP. so adsp-state represents the state of ADSP to ADSP loader.
+	Value of 0 indicates ADSP loader needs to use PIL and value of 2 means
+	ADSP image is already loaded by SBL.
+
+Optional properties:
+ - qcom,proc-img-to-load;
+	This property can be used to override default ADSP
+	loading by PIL. Based on string input, different proc is
+	loaded. Right now we are adding option "modem"
+	for 8916 purpose. Default image will be "adsp" which
+	will load LPASS Q6 for other targets as expected.
+	"adsp" option need not be explicitly mentioned in
+	DTSI file, as it is default option.
+
+Example:
+
+qcom,msm-adsp-loader {
+	compatible = "qcom,adsp-loader";
+	qcom,adsp-state = <2>;
+	qcom,proc-img-to-load = "modem";
+};
+
+* msm-audio-ion
+
+Required properties:
+ - compatible : "qcom,msm-audio-ion"
+
+Optional properties:
+ - qcom,smmu-version:
+	version ID to provide info regarding smmu version
+	used in chipset. If ARM SMMU HW - use id value as 1,
+	If QSMMU HW - use id value as 2.
+
+ - qcom,smmu-enabled:
+        It is possible that some MSM have SMMU in ADSP.  While other MSM use
+	no SMMU. Audio lib introduce wrapper for ION APIs. The wrapper needs
+        presence of SMMU in ADSP to handle ION APIs differently.
+        Presence of this property means ADSP has SMMU in it.
+ - iommus:
+	A phandle parsed by smmu driver. Number of entries will vary across
+	targets.
+
+Example:
+
+qcom,msm-audio-ion {
+	compatible = "qcom,msm-audio-ion;
+	qcom,smmu-enabled;
+};
+
+* msm-dai-tdm
+
+[First Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-tdm"
+ - qcom,msm-cpudai-tdm-group-id: ID of the group device. TDM interface
+				supports up to 8 groups:
+				Primary RX: 	37120
+				Primary TX: 	37121
+				Secondary RX: 	37136
+				Secondary TX: 	37137
+				Tertiary RX: 	37152
+				Tertiary TX: 	37153
+				Quaternary RX: 	37168
+				Quaternary TX: 	37169
+
+ - qcom,msm-cpudai-tdm-group-num-ports:	Number of ports in
+				msm-cpudai-tdm-group-port-id array.
+				Max number of ports supported by DSP is 8.
+
+ - qcom,msm-cpudai-tdm-group-port-id: Array of TDM port IDs of the group.
+				The size of the array is determined by
+				the value in msm-cpudai-tdm-group-num-ports.
+				Each group supports up to 8 ports:
+				Primary RX: 	36864, 36866, 36868, 36870,
+								36872, 36874, 36876, 36878
+				Primary TX: 	36865, 36867, 36869, 36871,
+								36873, 36875, 36877, 36879
+				Secondary RX: 	36880, 36882, 36884, 36886,
+								36888, 36890, 36892, 36894
+				Secondary TX:	36881, 36883, 36885, 36887,
+								36889, 36891, 36893, 36895
+				Tertiary RX: 	36896, 36898, 36900, 36902,
+								36904, 36906, 36908, 36910
+				Tertiary TX: 	36897, 36899, 36901, 36903,
+								36905, 36907, 36909, 36911
+				Quaternary RX:	36912, 36914, 36916, 36918,
+								36920, 36922, 36924, 36926
+				Quaternary TX:	36913, 36915, 36917, 36919,
+								36921, 36923, 36925, 36927
+
+ - qcom,msm-cpudai-tdm-clk-rate: Clock rate for tdm - 12288000.
+				When clock rate is set to zero,
+				then external clock is assumed.
+
+ [Second Level Nodes]
+
+Required properties:
+
+ - compatible : "qcom,msm-dai-q6-tdm"
+ - qcom,msm-dai-q6-mi2s-dev-id: TDM port ID.
+
+ - qcom,msm-cpudai-tdm-sync-mode: Synchronization setting.
+				0 - Short sync bit mode
+				1 - Long sync mode
+				2 - Short sync slot mode
+
+ - qcom,msm-cpudai-tdm-sync-src: Synchronization source.
+				0 - External source
+				1 - Internal source
+
+ - qcom,msm-cpudai-tdm-data-out: Data out signal to drive with other masters.
+				0 - Disable
+				1 - Enable
+
+ - qcom,msm-cpudai-tdm-invert-sync: Invert the sync.
+				0 - Normal
+				1 - Invert
+
+ - qcom,msm-cpudai-tdm-data-delay: Number of bit clock to delay data
+				with respect to sync edge.
+				0 - 0 bit clock cycle
+				1 - 1 bit clock cycle
+				2 - 2 bit clock cycle
+
+ - qcom,msm-cpudai-tdm-data-align: Indicate how data is packed
+				within the slot. For example, 32 slot width in case of
+				sample bit width is 24.
+				0 - MSB
+				1 - LSB
+
+Optional properties:
+
+ - qcom,msm-cpudai-tdm-header-start-offset: TDM Custom header start offset
+				in bytes from this sub-frame. The bytes is counted from 0.
+				0 is mapped to the 1st byte in or out of
+				the digital serial data line this sub-frame belong to.
+				Supported value: 0, 4, 8.
+
+ - qcom,msm-cpudai-tdm-header-width: Header width per frame followed.
+				2 bytes for MOST/TDM case.
+				Supported value: 2.
+
+ - qcom,msm-cpudai-tdm-header-num-frame-repeat: Number of header followed.
+				Supported value: 8.
+
+ - pinctrl-names: Pinctrl state names for each pin group
+				configuration.
+
+ - pinctrl-x: Defines pinctrl state for each pin group.
+
+Example:
+
+	qcom,msm-dai-tdm-quat-rx {
+		compatible = "qcom,msm-dai-tdm";
+		qcom,msm-cpudai-tdm-group-id = <37168>;
+		qcom,msm-cpudai-tdm-group-num-ports = <1>;
+		qcom,msm-cpudai-tdm-group-port-id = <36912>;
+		qcom,msm-cpudai-tdm-clk-rate = <12288000>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&quat_tdm_active &quat_tdm_dout_active>;
+		pinctrl-1 = <&quat_tdm_sleep &quat_tdm_dout_sleep>;
+		dai_quat_tdm_rx_0: qcom,msm-dai-q6-tdm-quat-rx-0 {
+			compatible = "qcom,msm-dai-q6-tdm";
+			qcom,msm-cpudai-tdm-dev-id = <36912>;
+			qcom,msm-cpudai-tdm-sync-mode = <0>;
+			qcom,msm-cpudai-tdm-sync-src = <1>;
+			qcom,msm-cpudai-tdm-data-out = <0>;
+			qcom,msm-cpudai-tdm-invert-sync = <0>;
+			qcom,msm-cpudai-tdm-data-delay = <0>;
+			qcom,msm-cpudai-tdm-data-align = <0>;
+			qcom,msm-cpudai-tdm-header-start-offset = <0>;
+			qcom,msm-cpudai-tdm-header-width = <2>;
+			qcom,msm-cpudai-tdm-header-num-frame-repeat = <8>;
+		};
+	};
+
+* MSM8996 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8996-asoc-snd-tomtom" for tomtom codec and
+		node is "sound" and "qcom,msm8996-asoc-snd-tasha"
+		for tasha codec and node is "sound-9335"
+- qcom,model : The user-visible name of this sound card.
+- qcom,tomtom-mclk-clk-freq : MCLK frequency value for tomtom codec
+			      and node is "sound"
+- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec
+			     and node is "sound-9335"
+- qcom,audio-routing : A list of the connections between audio components.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+Optional properties:
+- qcom,ext-ult-spk-amp-gpio : GPIO to enable ultrasound emitter amp.
+- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware.
+		Possible Values:
+		4-pole-jack : Jack on the hardware is 4-pole.
+		5-pole-jack : Jack on the hardware is 5-pole.
+		6-pole-jack : Jack on the hardware is 6-pole.
+- clock-names : clock name defined for external clock.
+- clocks : external clock defined for codec clock.
+- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers.
+- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset.
+- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
+- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
+- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+
+Example:
+
+	sound {
+	compatible = "qcom,msm8996-asoc-snd";
+	qcom,model = "msm8996-tomtom-snd-card";
+
+	qcom,audio-routing =
+		"RX_BIAS", "MCLK",
+		"LDO_H", "MCLK",
+		"AIF4 MAD", "MCLK",
+		"ultrasound amp", "LINEOUT1",
+		"ultrasound amp", "LINEOUT3",
+		"AMIC1", "MIC BIAS1 Internal1",
+		"MIC BIAS1 Internal1", "Handset Mic",
+		"AMIC2", "MIC BIAS2 External",
+		"MIC BIAS2 External", "Headset Mic",
+		"AMIC3", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCLeft Headset Mic",
+		"DMIC1", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic1",
+		"DMIC2", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic2",
+		"DMIC3", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic3",
+		"DMIC4", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic4",
+		"DMIC5", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic5",
+		"DMIC6", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic6";
+
+	clock-names = "osr_clk";
+	clocks = <&clock_rpm clk_div_clk1>;
+	qcom,mbhc-audio-jack-type = "6-pole-jack";
+		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>, <&cpe3>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1", "msm-pcm-dsp.2",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
+				"msm-lsm-client", "msm-pcm-routing", "msm-cpe-lsm",
+				"msm-compr-dsp", "msm-cpe-lsm.3";
+		asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>, <&dai_mi2s>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+				<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, <&afe_pcm_rx>,
+				<&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
+				<&incall_record_rx>, <&incall_record_tx>,
+				<&incall_music_rx>, <&incall_music2_rx>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+				"msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.2",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+				"msm-dai-q6-dev.32770";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		qcom,wsa-max-devs = <2>;
+		qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+				<&wsa881x_213>, <&wsa881x_214>;
+		qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft",
+					  "SpkrRight", "SpkrLeft";
+	};
+
+* MSM8952 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8952-audio-codec"
+- qcom,model : The user-visible name of this sound card.
+- reg : Offset and length of the register region(s) for MI2S/PCM MUX
+- reg-names : Register region name(s) referenced in reg above
+	 Required register resource entries are:
+	 "csr_gp_io_mux_mic_ctl": Physical address of MUX that controls
+				controls LPA IF tertiary, quad, PCM0, Digital Codec
+				and Secondary TLMM mux setting for mic path operation.
+	 "csr_gp_io_mux_spkr_ctl": Physical address of MUX that controls
+				IF primary, secondary, Digital Codec and Primary TLMM
+				setting for speaker path operation.
+	 "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel": Physical address of MUX
+				that controls the mux between LPA IF Quad and PCM0
+				path to secondary TLMM
+- qcom,msm-hs-micbias-type : This property is used to recognize the headset
+  micbias type, internal or external.
+- qcom,msm-ext-pa : This property is used to inform machine driver about
+  the connection of external PA over available MI2S interfaces,
+  following values can be given to this property.
+  primary -> Primary MI2S interface
+  secondary -> Secondary MI2S interface
+  tertiary -> Tertiary MI2S interface
+  quaternary -> Quaternary MI2S interface
+- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL
+switch type on target typically the switch type will be normally open or
+normally close, value for this property 0 for normally close and 1 for
+normally open.
+-  qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND
+switch type on target typically the switch type will be normally open or
+normally close, value for this property 0 for normally close and 1 for
+normally open.
+- qcom,audio-routing : A list of the connections between audio components.
+- qcom,msm-gpios : Lists down all the gpio sets that are supported.
+- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets
+mentioned in qcom,msm-gpios.
+- pinctrl-names : The combinations of gpio sets from above that are supported in
+the flavor.
+- pinctrl-# : Pinctrl states as mentioned in pinctrl-names.
+
+Optional properties:
+- qcom,prim-auxpcm-gpio-clk  : GPIO on which Primary AUXPCM clk signal is coming.
+- qcom,prim-auxpcm-gpio-sync : GPIO on which Primary AUXPCM SYNC signal is coming.
+- qcom,prim-auxpcm-gpio-din  : GPIO on which Primary AUXPCM DIN signal is coming.
+- qcom,prim-auxpcm-gpio-dout : GPIO on which Primary AUXPCM DOUT signal is coming.
+- qcom,prim-auxpcm-gpio-set : set of GPIO lines used for Primary AUXPCM port
+- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,msm-micbias1-ext-cap : Boolean. Enable micbias1 external
+capacitor mode.
+- qcom,msm-micbias2-ext-cap : Boolean. Enable micbias2 external
+capacitor mode.
+- qcom,msm-spk-ext-pa : GPIO which enables external speaker pa.
+
+To Configure External Audio Switch
+- qcom,msm-ext-audio-switch : GPIO which controls external switch that switches
+  audio path between headset and speakers.
+- ext-switch-vdd-supply : Power supply that control external audio switch
+- qcom,ext-switch-vdd-voltage : Minimum and maximum voltage in uV to set for
+  power supply.
+- qcom,ext-switch-vdd-op-mode : Maxmum # of uA current the switch will draw
+  from the power supply.
+Example:
+	qcom,msm-ext-audio-switch = <&msm_gpio 2 0>; - gpio # and active_state
+	ext-switch-vdd-supply = <&pm8950_l13>; - Power Rail
+	qcom,ext-switch-vdd-voltage = <3075000 3075000>; - Min, Max uV voltage
+	qcom,ext-switch-vdd-op-mode = <5000>; - Operational current uA
+	Additional needs to add two additional qcom,audio-routings
+			"HEADPHONE", "VDD_EXT_AUDIO_SWITCH"
+			"SPK_OUT", "VDD_EXT_AUDIO_SWITCH"
+
+- qcom,msm-mclk-freq : This property is used to inform machine driver about
+mclk frequency needs to be configured for internal and external PA.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+	codec dai names should match to that of the phandle order given
+	in "asoc-codec".
+- asoc-wsa-codec-names: This property contains list of wsa codec names. The names
+		  should comply with the wsa nodes configurations.
+- asoc-wsa-codec-prefixes: This property contains list of wsa codec prefixes.
+- msm-vdd-wsa-switch-supply: WSA codec supply's regulator device tree node.
+- qcom,msm-vdd-wsa-switch-voltage: WSA codec supply's voltage level in mV.
+- qcom,msm-vdd-wsa-switch-current: WSA codec max current level in mA.
+
+Example:
+	 sound {
+		compatible = "qcom,msm8952-audio-codec";
+		qcom,model = "msm8952-snd-card";
+		reg = <0xc051000 0x4>,
+		      <0xc051004 0x4>,
+		      <0xc055000 0x4>;
+		reg-names = "csr_gp_io_mux_mic_ctl",
+			    "csr_gp_io_mux_spkr_ctl",
+			    "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel";
+		qcom,msm-ext-pa = "primary";
+		qcom,msm-mclk-freq = <9600000>;
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,msm-hs-micbias-type = "internal";
+		qcom,msm-micbias1-ext-cap;
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"SPK_RX_BIAS", "MCLK",
+			"INT_LDO_H", "MCLK",
+			"MIC BIAS External", "Handset Mic",
+			"MIC BIAS Internal2", "Headset Mic",
+			"MIC BIAS External", "Secondary Mic",
+			"AMIC1", "MIC BIAS External",
+			"AMIC2", "MIC BIAS Internal2",
+			"AMIC3", "MIC BIAS External";
+		qcom,msm-gpios =
+			"pri_i2s",
+			"us_eu_gpio";
+		qcom,pinctrl-names =
+			"all_off",
+			"pri_i2s_act",
+			"us_eu_gpio_act",
+			"pri_i2s_us_eu_gpio_act";
+		pinctrl-names =
+			"all_off",
+			"pri_i2s_act",
+			"us_eu_gpio_act",
+			"pri_i2s_us_eu_gpio_act";
+		pinctrl-0 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_sus>;
+		pinctrl-1 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_sus>;
+		pinctrl-2 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_act>;
+		pinctrl-3 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_act>;
+		qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>;
+		qcom,prim-auxpcm-gpio-clk  = <&msm_gpio 63 0>;
+		qcom,prim-auxpcm-gpio-sync = <&msm_gpio 64 0>;
+		qcom,prim-auxpcm-gpio-din  = <&msm_gpio 65 0>;
+		qcom,prim-auxpcm-gpio-dout = <&msm_gpio 66 0>;
+		qcom,prim-auxpcm-gpio-set = "prim-gpio-prim";
+		qcom,tapan-codec-9302;
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&lpa>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
+				"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa";
+		asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
+				<&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
+				<&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
+				<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
+				"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+				"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
+				"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
+				"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+				"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+				"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+				"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
+		asoc-codec = <&stub>, <&pm8916_tombak_dig>;
+		asoc-codec-names = "msm-stub-codec.1", "tombak_codec";
+		asoc-wsa-codec-names = "wsa881x-i2c-codec.8-000f";
+		asoc-wsa-codec-prefixes = "SpkrMono";
+	};
+
+* MSMFALCON ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msmfalcon-asoc-snd"
+- qcom,model : The user-visible name of this sound card.
+- qcom,msm-hs-micbias-type : This property is used to recognize the headset
+  micbias type, internal or external.
+- qcom,msm-mbhc-hphl-swh: This property is used to distinguish headset HPHL
+switch type on target typically the switch type will be normally open or
+normally close, value for this property 0 for normally close and 1 for
+normally open.
+-  qcom,msm-mbhc-gnd-swh: This property is used to distinguish headset GND
+switch type on target typically the switch type will be normally open or
+normally close, value for this property 0 for normally close and 1 for
+normally open.
+- qcom,audio-routing : A list of the connections between audio components.
+- qcom,msm-gpios : Lists down all the gpio sets that are supported.
+- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets
+mentioned in qcom,msm-gpios.
+- pinctrl-names : The combinations of gpio sets from above that are supported in
+the flavor.
+- pinctrl-# : Pinctrl states as mentioned in pinctrl-names.
+
+Optional properties:
+- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,msm-micbias1-ext-cap : Boolean. Enable micbias1 external
+capacitor mode.
+- qcom,msm-micbias2-ext-cap : Boolean. Enable micbias2 external
+capacitor mode.
+- qcom,msm-spk-ext-pa : GPIO which enables external speaker pa.
+- qcom,msm-mclk-freq : This property is used to inform machine driver about
+mclk frequency needs to be configured for internal and external PA.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+	codec dai names should match to that of the phandle order given
+	in "asoc-codec".
+- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
+- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
+- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+
+Example:
+	 sound {
+		compatible = "qcom,msmfalcon-asoc-snd";
+		qcom,model = "msmfalcon-snd-card";
+		qcom,msm-mclk-freq = <9600000>;
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,msm-hs-micbias-type = "internal";
+		qcom,msm-micbias1-ext-cap;
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"SPK_RX_BIAS", "MCLK",
+			"INT_LDO_H", "MCLK",
+			"MIC BIAS External", "Handset Mic",
+			"MIC BIAS Internal2", "Headset Mic",
+			"MIC BIAS External", "Secondary Mic",
+			"AMIC1", "MIC BIAS External",
+			"AMIC2", "MIC BIAS Internal2",
+			"AMIC3", "MIC BIAS External";
+		qcom,msm-gpios =
+			"int_pdm",
+			"us_eu_gpio";
+		qcom,pinctrl-names =
+			"all_off",
+			"int_pdm_act",
+			"us_eu_gpio_act",
+			"int_pdm_us_eu_gpio_act";
+		pinctrl-names =
+			"all_off",
+			"int_pdm_act",
+			"us_eu_gpio_act",
+			"int_pdm_us_eu_gpio_act";
+		pinctrl-0 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_sus>;
+		pinctrl-1 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_sus>;
+		pinctrl-2 = <&cdc_pdm_lines_sus &cdc_pdm_lines_2_sus &cross_conn_det_act>;
+		pinctrl-3 = <&cdc_pdm_lines_act &cdc_pdm_lines_2_act &cross_conn_det_act>;
+		qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>;
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&lpa>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
+				"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa";
+		asoc-cpu = <&dai_pri_auxpcm>, <&dai_hdmi>,
+				<&dai_mi2s0>, <&dai_mi2s1>, <&dai_mi2s2>, <&dai_mi2s3>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_3_rx>, <&sb_3_tx>, <&sb_4_rx>, <&sb_4_tx>,
+				<&bt_sco_rx>, <&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
+				<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-hdmi.8",
+				"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+				"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
+				"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
+				"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+				"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+				"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+				"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		qcom,wsa-max-devs = <2>;
+		qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+				<&wsa881x_213>, <&wsa881x_214>;
+		qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft",
+					  "SpkrRight", "SpkrLeft";
+	};
+
+* MSM8952 Slimbus ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8952-audio-slimbus-codec"
+- qcom,model : The user-visible name of this sound card.
+- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets
+mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs,
+this would list all the 2^N combinations.
+- pinctrl-names : The combinations of gpio sets from above that are supported in
+the flavor. This can be sometimes same as qcom, pinctrl-names i.e with 2^N
+combinations or will have less incase if some combination is not supported.
+- pinctrl-# : Pinctrl states as mentioned in pinctrl-names.
+- reg : Offset and length of the register region(s) for MI2S/PCM MUX
+- reg-names : Register region name(s) referenced in reg above
+	 Required register resource entries are:
+	 "csr_gp_io_mux_mic_ctl": Physical address of MUX that controls
+				controls LPA IF tertiary, quad, PCM0, Digital Codec
+				and Secondary TLMM mux setting for mic path operation.
+	 "csr_gp_io_mux_spkr_ctl": Physical address of MUX that controls
+				IF primary, secondary, Digital Codec and Primary TLMM
+				setting for speaker path operation.
+- qcom,cdc-mclk-gpios : GPIO on which mclk signal is coming.
+- clock-names : clock name defined for external clock.
+- qcom,audio-routing : A list of the connections between audio components.
+  Each entry is a pair of strings, the first being the connection's sink,
+  the second being the connection's source.
+
+Optional Properties:
+- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- qcom,cdc-vdd-spkr-gpios : GPIO which controls PA for VDD speaker
+- qcom,headset-jack-type-NC: Set if the headset jack type is NC (Normally Closed)
+- qcom,tomtom-mclk-clk-freq : Tapan mclk Freq in Hz. currently only 9600000Hz
+				is supported.
+- qcom,msm-ext-pa : This property is used to inform machine driver about
+  the connection of external PA over available MI2S interfaces,
+  following values can be given to this property.
+  primary -> Primary MI2S interface
+  secondary -> Secondary MI2S interface
+  tertiary -> Tertiary MI2S interface
+  quaternary -> Quaternary MI2S interface
+- qcom,mi2s-audio-intf: This property is used to inform machine driver
+  if mi2s backend dailink has to be added as part of the sound card dai-links.
+- qcom,auxpcm-audio-intf: This property is used to inform machine driver
+  if auxpcm backend dailink has to be added as part of the sound card dai-links.
+- qcom,msm-mi2s-master: This property is used to inform machine driver
+  if MSM is the clock master of mi2s. 1 means master and 0 means slave. The
+  first entry is primary mi2s; the second entry is secondary mi2s, and so on.
+- reg: This property provides the AUX PCM/MI2S mux select register addresses
+  and size.
+- reg_names: This property provides the name of the AUX PCM/MI2S mux select
+  registers so the machine driver can retrieve the addresses. The order of the
+  names has to match the order of the registers in "reg" property.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+- asoc-wsa-codec-names: This property contains list of wsa codec names. The names
+			should comply with the wsa nodes configurations.
+- asoc-wsa-codec-prefixes: This property contains list of wsa codec prefixes.
+
+Example:
+	 sound {
+		compatible = "qcom,msm8952-audio-slim-codec";
+		qcom,model = "msm8952-tomtom-snd-card";
+		reg = <0xc051000 0x4>,
+		      <0xc051004 0x4>,
+		      <0xc055000 0x4>;
+		reg-names = "csr_gp_io_mux_mic_ctl",
+			    "csr_gp_io_mux_spkr_ctl",
+			    "csr_gp_io_lpaif_pri_pcm_pri_mode_muxsel";
+		qcom,msm-ext-pa = "primary";
+		qcom,mi2s-audio-intf;
+		qcom,auxpcm-audio-intf;
+		qcom,msm-mi2s-master = <1>, <0>, <1>, <1>;
+		reg = <0x1711a000 0x4>,
+		      <0x1711b000 0x4>,
+		      <0x1711c000 0x4>,
+		      <0x1711d000 0x4>;
+		reg-names = "lpaif_pri_mode_muxsel",
+			    "lpaif_sec_mode_muxsel",
+			    "lpaif_tert_mode_muxsel",
+			    "lpaif_quat_mode_muxsel";
+		qcom,msm-mclk-freq = <9600000>;
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,msm-hs-micbias-type = "internal";
+		qcom,msm-micbias1-ext-cap;
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"SPK_RX_BIAS", "MCLK",
+			"INT_LDO_H", "MCLK",
+			"MIC BIAS External", "Handset Mic",
+			"MIC BIAS Internal2", "Headset Mic",
+			"MIC BIAS External", "Secondary Mic",
+			"AMIC1", "MIC BIAS External",
+			"AMIC2", "MIC BIAS Internal2",
+			"AMIC3", "MIC BIAS External";
+		qcom,msm-gpios =
+			"slim",
+			"us_eu_gpio";
+		qcom,pinctrl-names =
+			"all_off",
+			"slim_act",
+			"us_eu_gpio_act",
+			"slim_us_eu_gpio_act";
+		pinctrl-names =
+			"all_off",
+			"slim_act",
+			"us_eu_gpio_act",
+			"slim_us_eu_gpio_act";
+		pinctrl-0 = <&cdc_slim_lines_sus &cross_conn_det_sus>;
+		pinctrl-1 = <&cdc_slim_lines_act &cross_conn_det_sus>;
+		pinctrl-2 = <&cdc_slim_lines_sus &cross_conn_det_act>;
+		pinctrl-3 = <&cdc_slim_lines_act &cross_conn_det_act>;
+		qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>;
+		qcom,headset-jack-type-NC;
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"SPK_OUT", "MCLK",
+			"AMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic",
+			"AMIC2", "MIC BIAS2 External",
+			"MIC BIAS2 External", "Headset Mic",
+			"AMIC4", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCRight Headset Mic",
+			"AMIC5", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCLeft Headset Mic",
+			"DMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic1",
+			"DMIC2", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic2",
+			"DMIC3", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic3",
+			"DMIC4", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic4";
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&lpa>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless", "msm-pcm-afe",
+				"msm-lsm-client", "msm-pcm-routing", "msm-pcm-lpa";
+		asoc-cpu = <&dai_hdmi>, <&dai_mi2s0>, <&dai_mi2s1>,
+				<&dai_mi2s2>, <&dai_mi2s3>, <&sb_0_rx>, <&sb_0_tx>,
+				<&sb_1_rx>, <&sb_1_tx>, <&sb_3_rx>, <&sb_3_tx>,
+				<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>, <&bt_sco_rx>,
+				<&bt_sco_tx>, <&int_fm_rx>, <&int_fm_tx>, <&afe_pcm_rx>,
+				<&afe_pcm_tx>, <&afe_proxy_rx>, <&afe_proxy_tx>,
+				<&incall_record_rx>, <&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>;
+		asoc-cpu-names = "msm-dai-q6-hdmi.8", "msm-dai-q6-mi2s.0",
+				"msm-dai-q6-mi2s.1", "msm-dai-q6-mi2s.2",
+				"msm-dai-q6-mi2s.3", "msm-dai-q6-dev.16384",
+				"msm-dai-q6-dev.16385", "msm-dai-q6-dev.16386",
+				"msm-dai-q6-dev.16387", "msm-dai-q6-dev.16390",
+				"msm-dai-q6-dev.16391", "msm-dai-q6-dev.16392",
+				"msm-dai-q6-dev.16393", "msm-dai-q6-dev.16395",
+				"msm-dai-q6-dev.12288", "msm-dai-q6-dev.12289",
+				"msm-dai-q6-dev.12292", "msm-dai-q6-dev.12293",
+				"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+				"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+				"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+				"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		asoc-wsa-codec-names = "wsa881x.20170212";
+		asoc-wsa-codec-prefixes = "SpkrLeft";
+	};
+
+* MDM9607 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,mdm9607-audio-tomtom"
+- qcom,model : The user-visible name of this sound card.
+- qcom,audio-routing : A list of the connections between audio components.
+Each entry is a pair of strings, the first being the connection's sink,
+the second being the connection's source.
+- qcom,tomtom-mclk-clk-freq : Master clock value given to codec. Some WCD9XXX
+codec can run at different mclk values. Mclk value can be 9.6MHz or 12.288MHz.
+- pinctrl-names : pinctrl state names for each pin group configuration.
+- pinctrl-x : defines pinctrl state for each pin group
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+
+Example:
+
+sound {
+		compatible = "qcom,mdm9607-audio-tomtom";
+		qcom,model = "mdm9607-tomtom-i2s-snd-card";
+
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"LDO_H", "MCLK",
+			"AMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Handset Mic",
+			"AMIC2", "MIC BIAS2 External",
+			"MIC BIAS2 External", "Headset Mic",
+			"AMIC3", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCRight Headset Mic",
+			"AMIC4", "MIC BIAS2 External",
+			"MIC BIAS2 External", "ANCLeft Headset Mic",
+			"DMIC1", "MIC BIAS1 External",
+			"MIC BIAS1 External", "Digital Mic1",
+			"DMIC3", "MIC BIAS3 External",
+			"MIC BIAS3 External", "Digital Mic3";
+
+		qcom,tomtom-mclk-clk-freq = <12288000>;
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&hostless>, <&afe>, <&routing>,
+				<&pcm_dtmf>, <&host_pcm>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-pcm-hostless", "msm-pcm-afe",
+				"msm-pcm-routing", "msm-pcm-dtmf", "msm-voice-host-pcm";
+		asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&dtmf_tx>,
+				<&rx_capture_tx>, <&rx_playback_rx>,
+				<&tx_capture_tx>, <&tx_playback_rx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-mi2s.0",
+				"msm-dai-stub-dev.4", "msm-dai-stub-dev.5",
+				"msm-dai-stub-dev.6", "msm-dai-stub-dev.7",
+				"msm-dai-stub-dev.8", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+	};
+
+* MDMCALIFORNIUM ASoC Machine driver
+
+- compatible : "qcom,mdm-audio-tasha" for tasha codec and
+                node is "sound"
+- qcom,model : The user-visible name of this sound card.
+- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec
+                             and node is "sound-9335"
+- qcom,audio-routing : A list of the connections between audio components.
+- asoc-platform: This is phandle list containing the references to platform device
+                 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+                       the platform names should match to that of the phandle order
+                       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+            that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+                  cpu dai names should match to that of the phandle order given
+                  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+                  where the id (%d) field represents the back-end AFE port id that
+                  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+              nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+                    codec dai names should match to that of the phandle order given
+                    in "asoc-codec".
+Optional properties:
+- clock-names : clock name defined for external clock.
+- clocks : external clock defined for codec clock.
+- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers.
+- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset.
+
+Example:
+
+	sound {
+	compatible = "qcom,mdm-audio-tasha";
+	qcom,model = "mdm-tasha-i2s-snd-card";
+
+	qcom,audio-routing =
+		"RX_BIAS", "MCLK",
+		"LDO_H", "MCLK",
+		"AIF4 MAD", "MCLK",
+		"ultrasound amp", "LINEOUT1",
+		"ultrasound amp", "LINEOUT3",
+		"AMIC1", "MIC BIAS1 Internal1",
+		"MIC BIAS1 Internal1", "Handset Mic",
+		"AMIC2", "MIC BIAS2 External",
+		"MIC BIAS2 External", "Headset Mic",
+		"AMIC3", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCLeft Headset Mic",
+		"DMIC1", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic1",
+		"DMIC2", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic2",
+		"DMIC3", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic3",
+		"DMIC4", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic4",
+		"DMIC5", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic5",
+		"DMIC6", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic6";
+
+		qcom,tasha-mclk-clk-freq = <12288000>;
+		asoc-platform = <&pcm0>, <&pcm1>, <&voip>, <&voice>,
+				<&loopback>, <&hostless>, <&afe>, <&routing>,
+				<&pcm_dtmf>, <&host_pcm>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-voip-dsp", "msm-pcm-voice", "msm-pcm-loopback",
+				"msm-pcm-hostless", "msm-pcm-afe",
+				"msm-pcm-routing", "msm-pcm-dtmf", "msm-voice-host-pcm";
+		asoc-cpu = <&dai_pri_auxpcm>, <&mi2s_prim>, <&dtmf_tx>,
+				<&rx_capture_tx>, <&rx_playback_rx>,
+				<&tx_capture_tx>, <&tx_playback_rx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-mi2s.0",
+				"msm-dai-stub-dev.4", "msm-dai-stub-dev.5",
+				"msm-dai-stub-dev.6", "msm-dai-stub-dev.7",
+				"msm-dai-stub-dev.8", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		qcom,aux-codec = <&stub_codec>;
+	};
+
+* APQ8096 Automotive ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,apq8096-asoc-snd-auto" for auto codec and
+		node is "sound-auto",
+		"qcom,apq8096-asoc-snd-adp-agave" for adp agave codec and
+		node is "sound-adp-agave",
+		"qcom,apq8096-asoc-snd-adp-mmxf" for adp mmxf codec and
+		node is "sound-adp-mmxf".
+- qcom,model : The user-visible name of this sound card.
+- asoc-platform: This is phandle list containing the references to platform device
+		nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		the platform names should match to that of the phandle order
+		given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+		that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		cpu dai names should match to that of the phandle order given
+		in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		where the id (%d) field represents the back-end AFE port id that
+		this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+		nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		codec dai names should match to that of the phandle order given
+		in "asoc-codec".
+
+Example:
+
+	sound-auto {
+		compatible = "qcom,apq8096-asoc-snd-auto";
+		qcom,model = "apq8096-auto-snd-card";
+
+		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&compr>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-pcm-dsp.2", "msm-voip-dsp",
+				"msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless",
+				"msm-pcm-afe", "msm-lsm-client",
+				"msm-pcm-routing", "msm-compr-dsp";
+		asoc-cpu = <&dai_pri_auxpcm>, <&dai_sec_auxpcm>, <&dai_hdmi>,
+				<&dai_mi2s>, <&dai_mi2s_quat>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>,
+				<&afe_proxy_rx>, <&afe_proxy_tx>,
+				<&incall_record_rx>, <&incall_record_tx>,
+				<&incall_music_rx>, <&incall_music2_rx>,
+				<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_rx_1>,
+				<&dai_tert_tdm_rx_2>, <&dai_tert_tdm_rx_3>,
+				<&dai_tert_tdm_tx_0>, <&dai_tert_tdm_tx_1>,
+				<&dai_tert_tdm_tx_2>, <&dai_tert_tdm_tx_3>,
+				<&dai_quat_tdm_rx_0>, <&dai_quat_tdm_rx_1>,
+				<&dai_quat_tdm_rx_2>, <&dai_quat_tdm_rx_3>,
+				<&dai_quat_tdm_tx_0>, <&dai_quat_tdm_tx_1>,
+				<&dai_quat_tdm_tx_2>, <&dai_quat_tdm_tx_3>;
+		asoc-cpu-names = "msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+				"msm-dai-q6-hdmi.8",
+				"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+				"msm-dai-q6-dev.224", "msm-dai-q6-dev.225",
+				"msm-dai-q6-dev.241", "msm-dai-q6-dev.240",
+				"msm-dai-q6-dev.32771", "msm-dai-q6-dev.32772",
+				"msm-dai-q6-dev.32773", "msm-dai-q6-dev.32770",
+				"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36898",
+				"msm-dai-q6-tdm.36900", "msm-dai-q6-tdm.36902",
+				"msm-dai-q6-tdm.36897", "msm-dai-q6-tdm.36899",
+				"msm-dai-q6-tdm.36901", "msm-dai-q6-tdm.36903",
+				"msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36914",
+				"msm-dai-q6-tdm.36916", "msm-dai-q6-tdm.36918",
+				"msm-dai-q6-tdm.36913", "msm-dai-q6-tdm.36915",
+				"msm-dai-q6-tdm.36917", "msm-dai-q6-tdm.36919";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+	};
+
+* MSMFALCON ASoC Slimbus Machine driver
+
+Required properties:
+- compatible : "qcom,msmfalcon-asoc-snd-tasha" for tasha codec,
+		"qcom,msmfalcon-asoc-snd-tavil" for tavil codec.
+- qcom,model : The user-visible name of this sound card.
+- qcom,msm-mclk-freq : MCLK frequency value for external codec
+- qcom,msm-gpios : Lists down all the gpio sets that are supported.
+- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets
+mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs,
+this would list all the 2^N combinations.
+- pinctrl-names : The combinations of gpio sets from above that are supported in
+the flavor. This can be sometimes same as qcom, pinctrl-names i.e with 2^N
+combinations or will have less incase if some combination is not supported.
+- pinctrl-# : Pinctrl states as mentioned in pinctrl-names.
+- qcom,audio-routing : A list of the connections between audio components.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+Optional properties:
+- qcom,cdc-us-euro-gpios : GPIO on which gnd/mic swap signal is coming.
+- clock-names : clock name defined for external clock.
+- clocks : external clock defined for codec clock.
+- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
+- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
+- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+
+Example:
+
+	sound-9335 {
+	compatible = "qcom,msmfalcon-asoc-snd-tasha";
+	qcom,model = "msmfalcon-tasha-snd-card";
+
+	qcom,audio-routing =
+		"RX_BIAS", "MCLK",
+		"LDO_H", "MCLK",
+		"AIF4 MAD", "MCLK",
+		"ultrasound amp", "LINEOUT1",
+		"ultrasound amp", "LINEOUT3",
+		"AMIC1", "MIC BIAS1 Internal1",
+		"MIC BIAS1 Internal1", "Handset Mic",
+		"AMIC2", "MIC BIAS2 External",
+		"MIC BIAS2 External", "Headset Mic",
+		"AMIC3", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCLeft Headset Mic",
+		"DMIC1", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic1",
+		"DMIC2", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic2",
+		"DMIC3", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic3",
+		"DMIC4", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic4",
+		"DMIC5", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic5",
+		"DMIC6", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic6";
+
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,msm-mclk-freq = <9600000>;
+		qcom,msm-gpios =
+			"slim",
+			"us_eu_gpio";
+		qcom,pinctrl-names =
+			"all_off",
+			"slim_act",
+			"us_eu_gpio_act",
+			"slim_us_eu_gpio_act";
+		pinctrl-names =
+			"all_off",
+			"slim_act",
+			"us_eu_gpio_act",
+			"slim_us_eu_gpio_act";
+		pinctrl-0 = <&cdc_slim_lines_sus &cross_conn_det_sus>;
+		pinctrl-1 = <&cdc_slim_lines_act &cross_conn_det_sus>;
+		pinctrl-2 = <&cdc_slim_lines_sus &cross_conn_det_act>;
+		pinctrl-3 = <&cdc_slim_lines_act &cross_conn_det_act>;
+		qcom,cdc-us-euro-gpios = <&msm_gpio 63 0>;
+		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-pcm-dsp.2", "msm-voip-dsp",
+				"msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless",
+				"msm-pcm-afe", "msm-lsm-client",
+				"msm-pcm-routing", "msm-cpe-lsm",
+				"msm-compr-dsp";
+		asoc-cpu = <&dai_hdmi>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+				<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>, <&sb_5_rx>;
+		asoc-cpu-names = "msm-dai-q6-hdmi.8",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+				"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		qcom,wsa-max-devs = <2>;
+		qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+				<&wsa881x_213>, <&wsa881x_214>;
+		qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft",
+					  "SpkrRight", "SpkrLeft";
+	};
+
+* MSM8998 ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8998-asoc-snd-tasha" for tasha codec,
+		"qcom,msm8998-asoc-snd-tavil" for tavil codec.
+- qcom,model : The user-visible name of this sound card.
+- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec
+- qcom,tavil-mclk-clk-freq : MCLK frequency value for tavil codec
+- qcom,audio-routing : A list of the connections between audio components.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+Optional properties:
+- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware.
+		Possible Values:
+		4-pole-jack : Jack on the hardware is 4-pole.
+		5-pole-jack : Jack on the hardware is 5-pole.
+		6-pole-jack : Jack on the hardware is 6-pole.
+- clock-names : clock name defined for external clock.
+- clocks : external clock defined for codec clock.
+- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers.
+- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset.
+- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
+- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
+- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+- qcom,wcn-btfm : Property to specify if WCN BT/FM chip is used for the target
+
+Example:
+
+	sound-9335 {
+	compatible = "qcom,msm8998-asoc-snd";
+	qcom,model = "msm8998-tasha-snd-card";
+
+	qcom,audio-routing =
+		"RX_BIAS", "MCLK",
+		"LDO_H", "MCLK",
+		"AIF4 MAD", "MCLK",
+		"ultrasound amp", "LINEOUT1",
+		"ultrasound amp", "LINEOUT3",
+		"AMIC1", "MIC BIAS1 Internal1",
+		"MIC BIAS1 Internal1", "Handset Mic",
+		"AMIC2", "MIC BIAS2 External",
+		"MIC BIAS2 External", "Headset Mic",
+		"AMIC3", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCRight Headset Mic",
+		"AMIC4", "MIC BIAS2 External",
+		"MIC BIAS2 External", "ANCLeft Headset Mic",
+		"DMIC1", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic1",
+		"DMIC2", "MIC BIAS1 External",
+		"MIC BIAS1 External", "Digital Mic2",
+		"DMIC3", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic3",
+		"DMIC4", "MIC BIAS3 External",
+		"MIC BIAS3 External", "Digital Mic4",
+		"DMIC5", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic5",
+		"DMIC6", "MIC BIAS4 External",
+		"MIC BIAS4 External", "Digital Mic6";
+
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,tasha-mclk-clk-freq = <9600000>;
+		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-pcm-dsp.2", "msm-voip-dsp",
+				"msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless",
+				"msm-pcm-afe", "msm-lsm-client",
+				"msm-pcm-routing", "msm-cpe-lsm",
+				"msm-compr-dsp";
+		asoc-cpu = <&dai_hdmi>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+				<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>, <&sb_5_rx>;
+		asoc-cpu-names = "msm-dai-q6-hdmi.8",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+				"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		qcom,wsa-max-devs = <2>;
+		qcom,wsa-devs = <&wsa881x_211>, <&wsa881x_212>,
+				<&wsa881x_213>, <&wsa881x_214>;
+		qcom,wsa-aux-dev-prefix = "SpkrRight", "SpkrLeft",
+					  "SpkrRight", "SpkrLeft";
+	};
+
+* MSMSTUB ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msm8998-asoc-snd-stub" for MSM8998 target.
+	       "qcom,msmskunk-asoc-snd-stub" for MSMSKUNK target.
+- qcom,model : The user-visible name of this sound card.
+- qcom,tasha-mclk-clk-freq : MCLK frequency value for tasha codec
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+Optional properties:
+- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
+
+Example:
+
+	sound_msm:sound-9335 {
+		compatible = "qcom,msm8998-asoc-snd-stub";
+		qcom,model = "msm8998-stub-snd-card";
+
+		qcom,tasha-mclk-clk-freq = <9600000>;
+		asoc-platform = <&pcm0>;
+		asoc-platform-names = "msm-pcm-dsp.0";
+		asoc-cpu = <&sb_0_rx>, <&sb_0_tx>;
+		asoc-cpu-names = "msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385";
+		asoc-codec = <&stub_codec>;
+		asoc-codec-names = "msm-stub-codec.1";
+		qcom,wsa-max-devs = <0>;
+	};
+
+* WCD DSP manager driver
+
+Required properties:
+- compatible : "qcom,wcd-dsp-mgr"
+- qcom,wdsp-components : This is phandle list containing the references to the
+			 components of the manager driver. Manager driver will
+			 register to component framework with these phandles.
+- qcom,img-filename : String property to provide the dsp image file name that is
+		     to be read from file system and downloaded to dsp memory
+Optional properties:
+- qcom,wdsp-cmpnt-dev-name : Property that manager driver will parse, but defined
+			     in the child's DT entry that is given to manager driver
+			     with phandle. This property will be used by the manager
+			     driver in case the manager driver cannot match child's
+			     of_node pointer to registered phandle.
+
+Example:
+
+	qcom,wcd-dsp-mgr {
+		compatible = "qcom,wcd-dsp-mgr";
+		qcom,wdsp-components = <&wcd934x_cdc 0>,
+				       <&wcd_spi_0 1>,
+				       <&glink_spi 2>;
+		qcom,img-filename = "cpe_9340";
+	};
+
+Example of child node that would have qcom,wdsp-cmpnt-dev-name property
+
+	wcd934x_cdc: tavil_codec {
+		qcom,wdsp-cmpnt-dev-name = "tavil_codec";
+	};
+
+* MSMSKUNK ASoC Machine driver
+
+Required properties:
+- compatible : "qcom,msmskunk-asoc-snd-tavil"
+- qcom,model : The user-visible name of this sound card.
+- qcom,tavil-mclk-clk-freq : MCLK frequency value for tavil codec
+- qcom,audio-routing : A list of the connections between audio components.
+- asoc-platform: This is phandle list containing the references to platform device
+		 nodes that are used as part of the sound card dai-links.
+- asoc-platform-names: This property contains list of platform names. The order of
+		       the platform names should match to that of the phandle order
+		       given in "asoc-platform".
+- asoc-cpu: This is phandle list containing the references to cpu dai device nodes
+	    that are used as part of the sound card dai-links.
+- asoc-cpu-names: This property contains list of cpu dai names. The order of the
+		  cpu dai names should match to that of the phandle order given
+		  in "asoc-cpu". The cpu names are in the form of "%s.%d" form,
+		  where the id (%d) field represents the back-end AFE port id that
+		  this CPU dai is associated with.
+- asoc-codec: This is phandle list containing the references to codec dai device
+	      nodes that are used as part of the sound card dai-links.
+- asoc-codec-names: This property contains list of codec dai names. The order of the
+		    codec dai names should match to that of the phandle order given
+		    in "asoc-codec".
+Optional properties:
+- qcom,mbhc-audio-jack-type : String to indicate the jack type on the hardware.
+		Possible Values:
+		4-pole-jack : Jack on the hardware is 4-pole.
+		5-pole-jack : Jack on the hardware is 5-pole.
+		6-pole-jack : Jack on the hardware is 6-pole.
+- clock-names : clock name defined for external clock.
+- clocks : external clock defined for codec clock.
+- qcom,hph-en1-gpio : GPIO to enable HiFi amplifiers.
+- qcom,hph-en0-gpio : GPIO to enable HiFi audio route to headset.
+- qcom,wsa-max-devs : Maximum number of WSA881x devices present in the target
+- qcom,wsa-devs : List of phandles for all possible WSA881x devices supported for the target
+- qcom,wsa-aux-dev-prefix : Name prefix with Left/Right configuration for WSA881x device
+- qcom,wcn-btfm : Property to specify if WCN BT/FM chip is used for the target
+
+Example:
+
+	sound-tavil {
+		compatible = "qcom,msmskunk-asoc-snd-tavil";
+		qcom,model = "msmskunk-tavil-snd-card";
+		qcom,ext-disp-audio-rx;
+		qcom,wcn-btfm;
+		qcom,mi2s-audio-intf;
+		qcom,auxpcm-audio-intf;
+		qcom,msm-mi2s-master = <1>, <1>, <1>, <1>;
+
+		reg = <0x1711a000 0x4>,
+		      <0x1711b000 0x4>,
+		      <0x1711c000 0x4>,
+		      <0x1711d000 0x4>;
+		reg-names = "lpaif_pri_mode_muxsel",
+			    "lpaif_sec_mode_muxsel",
+			    "lpaif_tert_mode_muxsel",
+			    "lpaif_quat_mode_muxsel";
+
+		qcom,audio-routing =
+			"RX_BIAS", "MCLK",
+			"MADINPUT", "MCLK",
+			"hifi amp", "LINEOUT1",
+			"hifi amp", "LINEOUT2",
+			"AMIC2", "MIC BIAS2",
+			"MIC BIAS2", "Headset Mic",
+			"AMIC3", "MIC BIAS2",
+			"MIC BIAS2", "ANCRight Headset Mic",
+			"AMIC4", "MIC BIAS2",
+			"MIC BIAS2", "ANCLeft Headset Mic",
+			"AMIC5", "MIC BIAS3",
+			"MIC BIAS3", "Handset Mic",
+			"DMIC0", "MIC BIAS1",
+			"MIC BIAS1", "Digital Mic0",
+			"DMIC1", "MIC BIAS1",
+			"MIC BIAS1", "Digital Mic1",
+			"DMIC2", "MIC BIAS3",
+			"MIC BIAS3", "Digital Mic2",
+			"DMIC3", "MIC BIAS3",
+			"MIC BIAS3", "Digital Mic3",
+			"DMIC4", "MIC BIAS4",
+			"MIC BIAS4", "Digital Mic4",
+			"DMIC5", "MIC BIAS4",
+			"MIC BIAS4", "Digital Mic5",
+			"SpkrLeft IN", "SPK1 OUT",
+			"SpkrRight IN", "SPK2 OUT";
+
+		qcom,msm-mbhc-hphl-swh = <0>;
+		qcom,msm-mbhc-gnd-swh = <0>;
+		qcom,hph-en0-gpio = <&tavil_hph_en0>;
+		qcom,hph-en1-gpio = <&tavil_hph_en1>;
+		qcom,tavil-mclk-clk-freq = <9600000>;
+		asoc-platform = <&pcm0>, <&pcm1>, <&pcm2>, <&voip>, <&voice>,
+				<&loopback>, <&compress>, <&hostless>,
+				<&afe>, <&lsm>, <&routing>, <&cpe>, <&compr>,
+				<&pcm_noirq>;
+		asoc-platform-names = "msm-pcm-dsp.0", "msm-pcm-dsp.1",
+				"msm-pcm-dsp.2", "msm-voip-dsp",
+				"msm-pcm-voice", "msm-pcm-loopback",
+				"msm-compress-dsp", "msm-pcm-hostless",
+				"msm-pcm-afe", "msm-lsm-client",
+				"msm-pcm-routing", "msm-cpe-lsm",
+				"msm-compr-dsp", "msm-pcm-dsp-noirq";
+		asoc-cpu = <&dai_hdmi>, <&dai_dp>,
+				<&dai_mi2s0>, <&dai_mi2s1>,
+				<&dai_mi2s2>, <&dai_mi2s3>,
+				<&dai_pri_auxpcm>, <&dai_sec_auxpcm>,
+				<&dai_tert_auxpcm>, <&dai_quat_auxpcm>,
+				<&sb_0_rx>, <&sb_0_tx>, <&sb_1_rx>, <&sb_1_tx>,
+				<&sb_2_rx>, <&sb_2_tx>, <&sb_3_rx>, <&sb_3_tx>,
+				<&sb_4_rx>, <&sb_4_tx>, <&sb_5_tx>,
+				<&afe_pcm_rx>, <&afe_pcm_tx>, <&afe_proxy_rx>,
+				<&afe_proxy_tx>, <&incall_record_rx>,
+				<&incall_record_tx>, <&incall_music_rx>,
+				<&incall_music_2_rx>, <&sb_5_rx>, <&sb_6_rx>,
+				<&sb_7_rx>, <&sb_7_tx>, <&sb_8_tx>,
+				<&usb_audio_rx>, <&usb_audio_tx>,
+				<&dai_pri_tdm_rx_0>, <&dai_pri_tdm_tx_0>,
+				<&dai_sec_tdm_rx_0>, <&dai_sec_tdm_tx_0>,
+				<&dai_tert_tdm_rx_0>, <&dai_tert_tdm_tx_0>,
+				<&dai_quat_tdm_rx_0>, <&dai_quat_tdm_tx_0>;
+		asoc-cpu-names = "msm-dai-q6-hdmi.8",  "msm-dai-q6-dp.24608",
+				"msm-dai-q6-mi2s.0", "msm-dai-q6-mi2s.1",
+				"msm-dai-q6-mi2s.2", "msm-dai-q6-mi2s.3",
+				"msm-dai-q6-auxpcm.1", "msm-dai-q6-auxpcm.2",
+				"msm-dai-q6-auxpcm.3", "msm-dai-q6-auxpcm.4",
+				"msm-dai-q6-dev.16384", "msm-dai-q6-dev.16385",
+				"msm-dai-q6-dev.16386", "msm-dai-q6-dev.16387",
+				"msm-dai-q6-dev.16388", "msm-dai-q6-dev.16389",
+				"msm-dai-q6-dev.16390", "msm-dai-q6-dev.16391",
+				"msm-dai-q6-dev.16392", "msm-dai-q6-dev.16393",
+				"msm-dai-q6-dev.16395", "msm-dai-q6-dev.224",
+				"msm-dai-q6-dev.225", "msm-dai-q6-dev.241",
+				"msm-dai-q6-dev.240", "msm-dai-q6-dev.32771",
+				"msm-dai-q6-dev.32772", "msm-dai-q6-dev.32773",
+				"msm-dai-q6-dev.32770", "msm-dai-q6-dev.16394",
+				"msm-dai-q6-dev.16396", "msm-dai-q6-dev.16398",
+				"msm-dai-q6-dev.16399", "msm-dai-q6-dev.16401",
+				"msm-dai-q6-dev.28672", "msm-dai-q6-dev.28673",
+				"msm-dai-q6-tdm.36864", "msm-dai-q6-tdm.36865",
+				"msm-dai-q6-tdm.36880", "msm-dai-q6-tdm.36881",
+				"msm-dai-q6-tdm.36896", "msm-dai-q6-tdm.36897",
+				"msm-dai-q6-tdm.36912", "msm-dai-q6-tdm.36913";
+		asoc-codec = <&stub_codec>, <&ext_disp_audio_codec>;
+		asoc-codec-names = "msm-stub-codec.1",
+				   "msm-ext-disp-audio-codec-rx";
+		qcom,wsa-max-devs = <2>;
+		qcom,wsa-devs = <&wsa881x_0211>, <&wsa881x_0212>,
+				<&wsa881x_0213>, <&wsa881x_0214>;
+		qcom,wsa-aux-dev-prefix = "SpkrLeft", "SpkrRight",
+					  "SpkrLeft", "SpkrRight";
+	};
diff --git a/Documentation/devicetree/bindings/sound/wcd-spi.txt b/Documentation/devicetree/bindings/sound/wcd-spi.txt
new file mode 100644
index 0000000..2cd833d
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wcd-spi.txt
@@ -0,0 +1,37 @@
+WCD audio codec SPI driver support
+
+* wcd_spi
+
+The wcd_spi device can be added as child device node to the audio codec device
+node if the audio codec has SPI slave hardware. The codec driver upon probe will
+create spi_device and perform spi_add_device. Note that the SPI framework does
+not parse this DT node and hence the DT properties defined by SPI framework
+cannot be used in wcd_spi device node.
+
+Required properties:
+
+- compatible : "qcom,wcd-spi-v2"
+
+- qcom,master-bus-num : Bus number of the SPI master controller driver. This
+			will be used to get a reference to the SPI master.
+
+- qcom,chip-select : Specifies the chip select number used for spi device
+		     registration.
+
+- qcom,max-frequency : Specifies the max frequency of the SPI interface.
+
+- qcom,mem-base-addr : Defines the memory base address from the SPI
+		       memory map. This will be used as an offset to read
+		       and write memory.
+
+Example:
+
+tavil_codec {
+	wcd_spi_0: wcd_spi {
+		compatible = "qcom,wcd-spi-v2";
+		qcom,master-bus-num = <10>;
+		qcom,chip-select = <0>;
+		qcom,max-frequency = <24000000>;
+		qcom,mem-base-addr = <0x100000>;
+	};
+};
diff --git a/Documentation/devicetree/bindings/sound/wcd_codec.txt b/Documentation/devicetree/bindings/sound/wcd_codec.txt
new file mode 100644
index 0000000..c0a7a24
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wcd_codec.txt
@@ -0,0 +1,640 @@
+WCD audio CODEC
+
+Required properties:
+
+ - compatible : "qcom,tasha-slim-pgd" or "qcom,tasha-i2c-pgd" for Tasha Codec
+		or "qcom,tavil-slim-pgd" for Tavil Codec
+ - elemental-addr: codec slimbus slave PGD enumeration address.(48 bits)
+
+ - qcom,cdc-reset-gpio: gpio used for codec SOC reset.
+                        If this property is not defined, it is expected
+                        to atleast have "qcom,wcd-rst-n-gpio" to be defined.
+ - qcom,wcd-rst-gpio-node: Phandle reference to the DT node having codec reset gpio
+                        configuration. If this property is not defined, it is
+                        expected to atleast define "qcom,cdc-reset-gpio" property.
+
+ - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node.
+ - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-buck-current: buck supply's max current in mA.
+
+ - cdc-vdd-tx-h-supply: phandle of tx-h supply's regulator device tree node.
+ - qcom,cdc-vdd-tx-h-voltage: tx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-tx-h-current: tx-h supply's max current in mA.
+
+ - cdc-vdd-rx-h-supply: phandle of rx-h supply's regulator device tree node.
+ - qcom,cdc-vdd-rx-h-voltage: rx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-rx-h-current: rx-h supply's max current in mA.
+
+ - cdc-vddpx-1-supply: phandle of px-1 supply's regulator device tree node.
+ - qcom,cdc-vddpx-1-voltage: px-1 supply's voltage level min and max in mV.
+ - qcom,cdc-vddpx-1-current: px-1 supply's max current in mA.
+
+ - cdc-vdd-a-1p2v-supply: phandle of 1.2v supply's regulator device tree node.
+ - qcom,cdc-vdd-a-1p2v-voltage: 1.2v supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-a-1p2v-current: 1.2v supply's max current in mA.
+
+ - cdc-vddcx-1-supply: phandle of cx-1 supply's regulator device tree node.
+ - qcom,cdc-vddcx-1-voltage: cx-1 supply's voltage level min and max in mV.
+ - qcom,cdc-vddcx-1-current: cx-1 supply's max current in mA.
+
+ - cdc-vddcx-2-supply: phandle of cx-2 supply's regulator device tree node.
+ - qcom,cdc-vddcx-2-voltage: cx-2 supply's voltage level min and max in mV.
+ - qcom,cdc-vddcx-2-current: cx-2 supply's max current in mA.
+
+ - cdc-vdd-buckhelper-supply: phandle of helper regulator supply's
+				device tree node. This supply is a helper regulator for
+				cdc-vdd-buck-supply regulator.
+ - cdc-vdd-buckhelper-voltage: helper supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-buckhelper-current: helper supply's max current in mA.
+
+ - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec
+			     hardware probe.  Supplies in this list will be
+			     stay enabled.
+
+ - qcom,cdc-micbias-ldoh-v - LDOH output in volts (should be 1.95 V and 3.00 V).
+
+ - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
+   cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+
+ - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1
+				 (should be from 1 to 3).
+ - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2
+				 (should be from 1 to 3).
+ - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3
+				 (should be from 1 to 3).
+ - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4
+				 (should be from 1 to 3).
+   This value represents the connected CFLIT to MIC Bias.
+
+ - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
+ - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
+ - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
+ - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
+ - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for
+			    codec.
+ - qcom,cdc-slim-ifd-dev - namme of the codec slim interface device.
+ - qcom,cdc-slim-ifd-elemental-addr - codec slimbus slave interface device
+				     enumeration address.
+
+Optional properties:
+ - cdc-dmic-sample-rate: Specifies the sample rate of digital mic in HZ. The
+			 values for 9.6MHZ mclk can be 2400000 Hz, 3200000 Hz
+			 and 4800000 Hz.  The values for 12.288MHz mclk can be
+			 3072200 Hz, 4096000 Hz and 6144000 Hz.
+
+ - qcom,cdc-mad-dmic-rate: Specifies the sample rate of digital mic in HZ to be
+			   used by MAD (Microphone Activity Detection) hardware
+			   block on the codec. The valid set of values are same
+			   as that of cdc-dmic-sample-rate, but this rate will
+			   only be used by MAD and all other audio use cases
+			   involving DMIC will use the rate defined by
+			   cdc-dmic-sample-rate.
+
+ - qcom,cdc-ecpp-dmic-rate: Specifies the sample rate of digital mic in HZ to be
+			    used by ECPP (Echo Cancellation Ping Pong) block
+			    on the codec. The valid set of values are same
+			    as that of cdc-dmic-sample-rate, but this rate will
+			    only be used by ECPP and all other audio use cases
+			    involving DMIC will use the rate defined by
+			    cdc-dmic-sample-rate.
+
+ - qcom,cdc-dmic-clk-drv-strength: Specifies the drive strength for digital microphone
+				   clock in the codec. Accepted values are 2,4,8 and 16.
+				   The clock drive strentgh is in uA. Codec driver will
+				   choose default value for particular codec if this
+				   value is not specified in device tree.
+
+ - qcom,cdc-on-demand-supplies: List of supplies which can be enabled
+				dynamically.
+				Supplies in this list are off by default.
+
+- qcom,cdc-cp-supplies: List of supplies required for codec chargepump enable
+				Supplies in this list can be enabled/disabled dynamically and
+				are off by default.
+
+ - qcom,cdc-micbias2-headset-only: Boolean. Allow micbias 2 only to headset mic.
+
+ - qcom,cdc-variant: Indicates codec variant, WCD9XXX indicates all codecs till Taiko
+			WCD9330 indicates wcd9330 audio codec
+
+ - qcom,cdc-micbias1-mv: micbias1 output voltage in milli volts.
+			 This is used when cfilt is not user configurable
+			 and micbias1 is directly controlled with a register
+			 write.
+
+ - qcom,cdc-micbias2-mv: micbias2 output voltage in milli volts.
+			 This is used when cfilt is not user configurable
+			 and micbias2 is directly controlled with a register
+			 write.
+
+ - qcom,cdc-micbias3-mv: micbias3 output voltage in milli volts.
+			 This is used when cfilt is not user configurable
+			 and micbias3 is directly controlled with a register
+			 write.
+
+ - qcom,cdc-micbias4-mv: micbias4 output voltage in milli volts.
+			 This is used when cfilt is not user configurable
+			 and micbias4 is directly controlled with a register
+			 write.
+
+ - clock-names : clock name defined for external clock.
+ - clocks : external clock defined for codec clock.
+
+Example:
+
+taiko_codec {
+	compatible = "qcom,taiko-slim-pgd";
+	elemental-addr = [00 01 A0 00 17 02];
+
+	qcom,cdc-reset-gpio = <&msmgpio 63 0>;
+
+	cdc-vdd-buck-supply = <&pm8941_s2>;
+	qcom,cdc-vdd-buck-voltage = <2150000 2150000>;
+	qcom,cdc-vdd-buck-current = <500000>;
+
+	cdc-vdd-tx-h-supply = <&pm8941_s3>;
+	qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+	qcom,cdc-vdd-tx-h-current = <200000>;
+
+	cdc-vdd-rx-h-supply = <&pm8941_s3>;
+	qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+	qcom,cdc-vdd-rx-h-current = <200000>;
+
+	cdc-vddpx-1-supply = <&pm8941_s3>;
+	qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+	qcom,cdc-vddpx-1-current = <5000>;
+
+	cdc-vdd-a-1p2v-supply = <&pm8941_l1>;
+	qcom,cdc-vdd-a-1p2v-voltage = <1225000 1225000>;
+	qcom,cdc-vdd-a-1p2v-current = <5000>;
+
+	cdc-vddcx-1-supply = <&pm8941_l1>;
+	qcom,cdc-vddcx-1-voltage = <1225000 1225000>;
+	qcom,cdc-vddcx-1-current = <5000>;
+
+	cdc-vddcx-2-supply = <&pm8941_l1>;
+	qcom,cdc-vddcx-2-voltage = <1225000 1225000>;
+	qcom,cdc-vddcx-2-current = <5000>;
+
+	qcom,cdc-static-supplies = "cdc-vdd-buck",
+				   "cdc-vdd-tx-h",
+				   "cdc-vdd-rx-h",
+				   "cdc-vddpx-1",
+				   "cdc-vdd-a-1p2v",
+				   "cdc-vddcx-1",
+				   "cdc-vddcx-2";
+
+	com,cdc-on-demand-supplies = "cdc-vdd-spkdrv";
+
+	qcom,cdc-micbias-ldoh-v = <0x3>;
+	qcom,cdc-micbias-cfilt1-mv = <1800>;
+	qcom,cdc-micbias-cfilt2-mv = <2700>;
+	qcom,cdc-micbias-cfilt3-mv = <1800>;
+	qcom,cdc-micbias1-cfilt-sel = <0x0>;
+	qcom,cdc-micbias2-cfilt-sel = <0x1>;
+	qcom,cdc-micbias3-cfilt-sel = <0x2>;
+	qcom,cdc-micbias4-cfilt-sel = <0x2>;
+	qcom,cdc-micbias1-ext-cap;
+	qcom,cdc-micbias2-ext-cap;
+	qcom,cdc-micbias3-ext-cap;
+	qcom,cdc-micbias4-ext-cap;
+	qcom,cdc-mclk-clk-rate = <9600000>;
+	qcom,cdc-slim-ifd = "taiko-slim-ifd";
+	qcom,cdc-slim-ifd-elemental-addr = [00 00 A0 00 17 02];
+	qcom,cdc-dmic-sample-rate = <4800000>;
+	qcom,cdc-micbias2-headset-only;
+};
+
+Wcd9xxx audio CODEC in I2C mode
+
+ - compatible = "qcom,wcd9xxx-i2c-device";
+ - reg: represents the slave address provided to the I2C driver.
+ - qcom,cdc-reset-gpio: gpio used for codec SOC reset.
+
+ - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node.
+ - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-buck-current: buck supply's max current in mA.
+
+ - cdc-vdd-tx-h-supply: phandle of tx-h supply's regulator device tree node.
+ - qcom,cdc-vdd-tx-h-voltage: tx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-tx-h-current: tx-h supply's max current in mA.
+
+ - cdc-vdd-rx-h-supply: phandle of rx-h supply's regulator device tree node.
+ - qcom,cdc-vdd-rx-h-voltage: rx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-rx-h-current: rx-h supply's max current in mA.
+
+ - cdc-vddpx-1-supply: phandle of px-1 supply's regulator device tree node.
+ - qcom,cdc-vddpx-1-voltage: px-1 supply's voltage level min and max in mV.
+ - qcom,cdc-vddpx-1-current: px-1 supply's max current in mA.
+
+ - cdc-vdd-a-1p2v-supply: phandle of 1.2v supply's regulator device tree node.
+ - qcom,cdc-vdd-a-1p2v-voltage: 1.2v supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-a-1p2v-current: 1.2v supply's max current in mA.
+
+ - cdc-vddcx-1-supply: phandle of cx-1 supply's regulator device tree node.
+ - qcom,cdc-vddcx-1-voltage: cx-1 supply's voltage level min and max in mV.
+ - qcom,cdc-vddcx-1-current: cx-1 supply's max current in mA.
+
+ - cdc-vddcx-2-supply: phandle of cx-2 supply's regulator device tree node.
+ - qcom,cdc-vddcx-2-voltage: cx-2 supply's voltage level min and max in mV.
+ - qcom,cdc-vddcx-2-current: cx-2 supply's max current in mA.
+
+ - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec
+			     hardware probe.  Supplies in this list will be
+			     stay enabled.
+
+ - qcom,cdc-micbias-ldoh-v - LDOH output in volts (should be 1.95 V and 3.00 V).
+
+ - qcom,cdc-micbias-cfilt1-mv - cfilt1 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt2-mv - cfilt2 output voltage in milli volts.
+ - qcom,cdc-micbias-cfilt3-mv - cfilt3 output voltage in milli volts.
+   cfilt voltage can be set to max of qcom,cdc-micbias-ldoh-v - 0.15V.
+
+ - qcom,cdc-micbias1-cfilt-sel = cfilt to use for micbias1
+				 (should be from 1 to 3).
+ - qcom,cdc-micbias2-cfilt-sel = cfilt to use for micbias2
+				 (should be from 1 to 3).
+ - qcom,cdc-micbias3-cfilt-sel = cfilt to use for micbias3
+				 (should be from 1 to 3).
+ - qcom,cdc-micbias4-cfilt-sel = cfilt to use for micbias4
+				 (should be from 1 to 3).
+   This value represents the connected CFLIT to MIC Bias.
+
+ - qcom,cdc-micbias1-ext-cap: Boolean. Enable micbias 1 external capacitor mode.
+ - qcom,cdc-micbias2-ext-cap: Boolean. Enable micbias 2 external capacitor mode.
+ - qcom,cdc-micbias3-ext-cap: Boolean. Enable micbias 3 external capacitor mode.
+ - qcom,cdc-micbias4-ext-cap: Boolean. Enable micbias 4 external capacitor mode.
+ - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for
+			    codec.
+
+Optional properties:
+
+ - cdc-vdd-spkdrv-supply: phandle of spkdrv supply's regulator device tree node.
+ - qcom,cdc-vdd-spkdrv-voltage: spkdrv supply voltage level min and max in mV.
+ - qcom,cdc-vdd-spkdrv-current: spkdrv supply max current in mA.
+
+ - cdc-vdd-spkdrv-supply: phandle of spkdrv supply's regulator device tree node.
+ - qcom,cdc-vdd-spkdrv-voltage: spkdrv supply voltage level min and max in mV.
+ - qcom,cdc-vdd-spkdrv-current: spkdrv supply max current in mA.
+
+ - cdc-vdd-spkdrv-2-supply: phandle of spkdrv2 supply's regulator device tree node.
+ - qcom,cdc-vdd-spkdrv-2-voltage: spkdrv2 supply voltage level min and max in mV.
+ - qcom,cdc-vdd-spkdrv-2-current: spkdrv2 supply max current in mA.
+
+ - qcom,cdc-on-demand-supplies: List of supplies which can be enabled
+				dynamically.
+				Supplies in this list are off by default.
+
+Example:
+i2c@f9925000 {
+	cell-index = <3>;
+	compatible = "qcom,i2c-qup";
+	reg = <0xf9925000 0x1000>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+	reg-names = "qup_phys_addr";
+	interrupts = <0 97 0>;
+	interrupt-names = "qup_err_intr";
+	qcom,i2c-bus-freq = <100000>;
+	qcom,i2c-src-freq = <24000000>;
+
+	wcd9xxx_codec@0d{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x0d>;
+		qcom,cdc-reset-gpio = <&msmgpio 22 0>;
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
+			      20 21 22 23 24 25 26 27 28>;
+
+		cdc-vdd-buck-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-buck-current = <25000>;
+
+		cdc-vdd-tx-h-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-tx-h-current = <25000>;
+
+		cdc-vdd-rx-h-supply = <&pm8019_l11>;
+		qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-rx-h-current = <25000>;
+
+		cdc-vddpx-1-supply = <&pm8019_l11>;
+		qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+		qcom,cdc-vddpx-1-current = <10000>;
+
+		cdc-vdd-a-1p2v-supply = <&pm8019_l9>;
+		qcom,cdc-vdd-a-1p2v-voltage = <1200000 1200000>;
+		qcom,cdc-vdd-a-1p2v-current = <10000>;
+
+		cdc-vddcx-1-supply = <&pm8019_l9>;
+		qcom,cdc-vddcx-1-voltage = <1200000 1200000>;
+		qcom,cdc-vddcx-1-current = <10000>;
+
+		cdc-vddcx-2-supply = <&pm8019_l9>;
+		qcom,cdc-vddcx-2-voltage = <1200000 1200000>;
+		qcom,cdc-vddcx-2-current = <10000>;
+
+		qcom,cdc-static-supplies = "cdc-vdd-buck",
+					   "cdc-vdd-tx-h",
+					   "cdc-vdd-rx-h",
+					   "cdc-vddpx-1",
+					   "cdc-vdd-a-1p2v",
+					   "cdc-vddcx-1",
+					   "cdc-vddcx-2";
+
+		cdc-vdd-spkdrv-supply = <&pmi8994_boost>;
+		qcom,cdc-vdd-spkdrv-voltage = <5000000 5000000>;
+		qcom,cdc-vdd-spkdrv-current = <600000>;
+
+		cdc-vdd-spkdrv-2-supply = <&pmi8994_boost>;
+		qcom,cdc-vdd-spkdrv-2-voltage = <5000000 5000000>;
+		qcom,cdc-vdd-spkdrv-2-current = <600000>;
+
+		qcom,cdc-on-demand-supplies = "cdc-vdd-spkdrv",
+					      "cdc-vdd-spkdrv-2";
+
+		qcom,cdc-micbias-ldoh-v = <0x3>;
+		qcom,cdc-micbias-cfilt1-mv = <1800>;
+		qcom,cdc-micbias-cfilt2-mv = <2700>;
+		qcom,cdc-micbias-cfilt3-mv = <1800>;
+		qcom,cdc-micbias1-cfilt-sel = <0x0>;
+		qcom,cdc-micbias2-cfilt-sel = <0x1>;
+		qcom,cdc-micbias3-cfilt-sel = <0x2>;
+		qcom,cdc-micbias4-cfilt-sel = <0x2>;
+		qcom,cdc-mclk-clk-rate = <12288000>;
+	};
+
+	wcd9xxx_codec@77{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x77>;
+	};
+
+	wcd9xxx_codec@66{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x66>;
+	};
+	wcd9xxx_codec@55{
+		compatible = "qcom,wcd9xxx-i2c";
+		reg = <0x55>;
+	};
+};
+
+Tombak audio CODEC in SPMI mode
+
+ - compatible = "qcom,msm-codec-core",
+ - compatible = "qcom,pmic-codec-digital"
+ - compatible = "qcom,pmic-codec-analog"
+ - reg: represents the slave base address provided to the peripheral.
+ - interrupt-parent : The parent interrupt controller.
+ - interrupts: List of interrupts in given SPMI peripheral.
+ - interrupt-names: Names specificed to above list of interrupts in same
+		    order.
+
+Optional properties:
+
+ - cdc-vdda-cp-supply: phandle of cp supply's regulator device tree node.
+ - qcom,cdc-vdda-cp-voltage: cp supply's voltage level min and max in mV.
+ - qcom,cdc-vdda-cp-current: cp supply's max current in mA.
+
+ - cdc-vdda-rx-h-supply: phandle of vdda-rx-h supply's regulator device tree node.
+ - qcom,cdc-vdda-rx-h-voltage: vdda-rx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdda-rx-h-current: vdda-rx-h supply's max current in mA.
+
+ - cdc-vdda-tx-h-supply: phandle of vdda-tx-h supply's regulator device tree node.
+ - qcom,cdc-vdda-tx-h-voltage: vdda-tx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdda-tx-h-current: vdda-tx-h supply's max current in mA.
+
+ - cdc-vdd-px-supply: phandle of vdd-px supply's regulator device tree node.
+ - qcom,cdc-vdd-px-voltage: vdd-px supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-px-current: vdd-px supply's max current in mA.
+
+ - cdc-vdd-pa-supply: phandle of vdd-pa supply's regulator device tree node.
+ - qcom,cdc-vdd-pa-voltage: vdd-pa supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-pa-current: vdd-pa supply's max current in mA.
+
+ - cdc-vdd-mic-bias-supply: phandle of mic-bias supply's regulator device tree
+			    node.
+ - qcom,cdc-vdd-mic-bias-voltage: mic-bias supply's voltage level min and max
+				  in mV.
+ - qcom,cdc-vdd-mic-bias-current: mic-bias supply's max current in mA.
+
+ - qcom,cdc-mclk-clk-rate: Specifies the master clock rate in Hz required for
+			    codec.
+
+ - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec
+			     hardware probe.  Supplies in this list will be
+			     stay enabled.
+
+ - qcom,cdc-on-demand-supplies: List of supplies which can be enabled
+				dynamically.
+				Supplies in this list are off by default.
+ - qcom,cdc-micbias-cfilt-mv : MICBIAS voltage value
+ - qcom,cdc-boost-voltage: Specifies the analog boost output voltage value.
+				Value from 4000 to 5550 in mV in steps of 50 mV can be given.
+ - qcom,dig-cdc-base-addr: Specifies the digital codec base address for MSM digital
+				core register writes.
+
+Example:
+
+msm_dig_codec: qcom,msm-int-codec {
+	compatible = "qcom,msm_int_core_codec";
+	qcom,dig-cdc-base-addr = <0xc0f0000>;
+};
+
+msm8x16_wcd_codec@f100 {
+	compatible = "qcom,msm_int_pmic_analog_codec";
+	reg = <0xf100 0x100>;
+};
+
+msm8x16_wcd_codec@f000{
+	compatible = "qcom,msm_int_pmic_digital_codec";
+	reg = <0xf000 0x100>;
+	interrupt-parent = <&spmi_bus>;
+	interrupts = <0x1 0xf0 0x0>,
+		     <0x1 0xf0 0x1>,
+		     <0x1 0xf0 0x2>,
+		     <0x1 0xf0 0x3>,
+		     <0x1 0xf0 0x4>,
+		     <0x1 0xf0 0x5>,
+		     <0x1 0xf0 0x6>,
+		     <0x1 0xf0 0x7>;
+	interrupt-names = "spk_cnp_int",
+			  "spk_clip_int",
+			  "spk_ocp_int",
+			  "ins_rem_det1",
+			  "but_rel_det",
+			  "but_press_det",
+			  "ins_rem_det",
+			  "mbhc_int";
+
+	cdc-vdda-cp-supply = <&pm8916_s4>;
+	qcom,cdc-vdda-cp-voltage = <1800000 2200000>;
+	qcom,cdc-vdda-cp-current = <770000>;
+
+	cdc-vdda-rx-h-supply = <&pm8916_l5>;
+	qcom,cdc-vdda-rx-h-voltage = <1800000 1800000>;
+	qcom,cdc-vdda-rx-h-current = <20000>;
+
+	cdc-vdda-tx-h-supply = <&pm8916_l5>;
+	qcom,cdc-vdda-tx-h-voltage = <1800000 1800000>;
+	qcom,cdc-vdda-tx-h-current = <20000>;
+
+	cdc-vdd-px-supply = <&pm8916_s4>;
+	qcom,cdc-vdd-px-voltage = <1800000 2200000>;
+	qcom,cdc-vdd-px-current = <770000>;
+
+	cdc-vdd-pa-supply = <&pm8916_l5>;
+	qcom,cdc-vdd-pa-voltage = <1800000 1800000>;
+	qcom,cdc-vdd-pa-current = <5000>;
+
+	cdc-vdd-mic-bias-supply = <&pm8916_l13>;
+	qcom,cdc-vdd-mic-bias-voltage = <3075000 3075000>;
+	qcom,cdc-vdd-mic-bias-current = <25000>;
+
+	qcom,cdc-mclk-clk-rate = <9600000>;
+
+	qcom,cdc-static-supplies = "cdc-vdda-h",
+				   "cdc-vdd-px",
+				   "cdc-vdd-pa",
+				   "cdc-vdda-cp";
+
+	qcom,cdc-on-demand-supplies = "cdc-vdd-mic-bias";
+	qcom,dig-cdc-base-addr = <0xc0f0000>;
+};
+
+Tasha audio CODEC in I2C mode
+
+ - compatible = "qcom,tasha-i2c-pgd";
+ - reg: represents the slave address provided to the I2C driver.
+ - qcom,cdc-reset-gpio: gpio used for codec SOC reset.
+
+ - cdc-vdd-buck-supply: phandle of buck supply's regulator device tree node.
+ - qcom,cdc-vdd-buck-voltage: buck supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-buck-current: buck supply's max current in mA.
+
+ - cdc-buck-sido-supply: phandle of sido supply's regulator device tree node.
+ - qcom,cdc-buck-sido-voltage = sido supply's voltage level min and max in mV.
+ - qcom,cdc-buck-sido-current = sido supply's max current in mA.
+
+ - cdc-vdd-tx-h-supply: phandle of tx-h supply's regulator device tree node.
+ - qcom,cdc-vdd-tx-h-voltage: tx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-tx-h-current: tx-h supply's max current in mA.
+
+ - cdc-vdd-rx-h-supply: phandle of rx-h supply's regulator device tree node.
+ - qcom,cdc-vdd-rx-h-voltage: rx-h supply's voltage level min and max in mV.
+ - qcom,cdc-vdd-rx-h-current: rx-h supply's max current in mA.
+
+ - cdc-vddpx-1-supply: phandle of px-1 supply's regulator device tree node.
+ - qcom,cdc-vddpx-1-voltage: px-1 supply's voltage level min and max in mV.
+ - qcom,cdc-vddpx-1-current: px-1 supply's max current in mA.
+
+ - qcom,cdc-static-supplies: List of supplies to be enabled prior to codec
+			     hardware probe.  Supplies in this list will be
+			     stay enabled.
+
+ - qcom,cdc-micbias1-mv - micbias1 output voltage in milli volts.
+ - qcom,cdc-micbias2-mv - micbias2 output voltage in milli volts.
+ - qcom,cdc-micbias3-mv - micbias3 output voltage in milli volts.
+ - qcom,cdc-micbias4-mv - micbias4 output voltage in milli volts.
+
+ - qcom,cdc-mclk-clk-rate - Specifies the master clock rate in Hz required for
+			    codec.
+
+ - qcom,cdc-dmic-sample-rate - Specifies dmic sample rate.
+ - qcom,cdc-variant - Specifies codec variant.
+
+Example:
+i2c_3: i2c@78B7000 { /* BLSP1 QUP3 */
+	compatible = "qcom,i2c-msm-v2";
+	#address-cells = <1>;
+	#size-cells = <0>;
+	reg-names = "qup_phys_addr";
+	reg = <0x78B7000 0x600>;
+	interrupt-names = "qup_irq";
+	interrupts = <0 97 0>;
+	dmas = <&dma_blsp1 12 64 0x20000020 0x20>,
+		<&dma_blsp1 13 32 0x20000020 0x20>;
+	dma-names = "tx", "rx";
+	qcom,master-id = <86>;
+	qcom,clk-freq-out = <400000>;
+	qcom,clk-freq-in  = <19200000>;
+	clock-names = "iface_clk", "core_clk";
+	clocks = <&clock_gcc clk_gcc_blsp1_ahb_clk>,
+		 <&clock_gcc clk_gcc_blsp1_qup3_i2c_apps_clk>;
+	pinctrl-names = "i2c_active", "i2c_sleep";
+	pinctrl-0 = <&i2c_3_active>;
+	pinctrl-1 = <&i2c_3_sleep>;
+	status = "disabled";
+
+	wcd9xxx_codec@d{
+		compatible = "qcom,tasha-i2c-pgd";
+		reg = <0x0d>;
+
+		interrupt-parent = <&wcd9xxx_intc>;
+		interrupts = <0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
+				  17 18 19 20 21 22 23 24 25 26 27 28 29
+				  30>;
+
+		qcom,cdc-reset-gpio = <&tlmm_pinmux 90 0>;
+
+		cdc-vdd-buck-supply = <&codec_buck_vreg>;
+		qcom,cdc-vdd-buck-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-buck-current = <650000>;
+
+		cdc-buck-sido-supply = <&codec_buck_vreg>;
+		qcom,cdc-buck-sido-voltage = <1800000 1800000>;
+		qcom,cdc-buck-sido-current = <200000>;
+
+		cdc-vdd-tx-h-supply = <&pmdcalifornium_l6>;
+		qcom,cdc-vdd-tx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-tx-h-current = <25000>;
+
+		cdc-vdd-rx-h-supply = <&pmdcalifornium_l6>;
+		qcom,cdc-vdd-rx-h-voltage = <1800000 1800000>;
+		qcom,cdc-vdd-rx-h-current = <25000>;
+
+		cdc-vddpx-1-supply = <&pmdcalifornium_l6>;
+		qcom,cdc-vddpx-1-voltage = <1800000 1800000>;
+		qcom,cdc-vddpx-1-current = <10000>;
+
+		qcom,cdc-static-supplies = "cdc-vdd-buck",
+					   "cdc-buck-sido",
+					   "cdc-vdd-tx-h",
+					   "cdc-vdd-rx-h",
+					   "cdc-vddpx-1";
+
+		qcom,cdc-micbias1-mv = <1800>;
+		qcom,cdc-micbias2-mv = <1800>;
+		qcom,cdc-micbias3-mv = <1800>;
+		qcom,cdc-micbias4-mv = <1800>;
+		qcom,cdc-mclk-clk-rate = <12288000>;
+		qcom,cdc-dmic-sample-rate = <4800000>;
+
+		swr_master {
+			compatible = "qcom,swr-wcd";
+			#address-cells = <2>;
+			#size-cells = <0>;
+
+			wsa881x_1: wsa881x@20170212 {
+				compatible = "qcom,wsa881x";
+				reg = <0x00 0x20170212>;
+				qcom,spkr-sd-n-gpio = <&tlmm_pinmux 80 0>;
+
+				cdc-vdd-d-supply = <&pmdcalifornium_l6>;
+				qcom,cdc-vdd-d-voltage = <1800000
+							  1800000>;
+				qcom,cdc-vdd-d-current = <2000>;
+
+				cdc-vdd-a-supply = <&pmdcalifornium_l6>;
+				qcom,cdc-vdd-a-voltage = <1800000
+							  1800000>;
+				qcom,cdc-vdd-a-current = <2000>;
+
+				qcom,cdc-static-supplies = "cdc-vdd-d",
+							   "cdc-vdd-a";
+			};
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/sound/wsa881x-analog.txt b/Documentation/devicetree/bindings/sound/wsa881x-analog.txt
new file mode 100644
index 0000000..ca7bc04
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wsa881x-analog.txt
@@ -0,0 +1,88 @@
+wsa881x Audio CODEC
+
+wsa881x Audio CODEC can support either I2C interface or Soundwire interface.
+In Analog mode, this codec will operate as I2C slave device and
+interacts with I2C controller for status, control and interrupt management.
+The wsa881x device nodes will be represented as child nodes to I2C
+master device node in the devicetree. Currently, the below devicetree
+bindings are for I2C mode only and does not include soundwire mode.
+
+Required properties:
+
+ - compatible : "qcom,wsa881x-i2c-codec"
+ - reg : Unique device ID of I2C slave device.
+
+Optional properties:
+- qcom,msm-gpios : Lists down all the gpio sets that are supported.
+- qcom,pinctrl-names : Lists all the possible combinations of the gpio sets
+mentioned in qcom,msm-gpios. Say we have 2^N combinations for N GPIOs, this
+list all 2^N combinations.
+- pinctrl-names : The combinations of gpio sets from above that are supported in
+the flavor. This can be sometimes same as qcom, pinctrl-names i.e with 2^N
+combinations or will have less incase if some combination is not supported.
+- pinctrl-# : Pinctrl states as mentioned in pinctrl-names.
+
+* wsa_intc
+
+Required properties:
+
+ - compatible :                            "qcom,wsa-irq"
+ - interrupt-controller :                  Mark this device node as an
+					   interrupt controller
+ - #interrupt-cells :                      Should be 1
+ - interrupt-parent :                      Parent interrupt controller
+ - interrupts :                            Interrupt number on the parent
+                                           interrupt controller
+ - interrupt-names :                       Name of interrupt on the parent
+                                           interrupt controller
+Example:
+
+wsa881x-i2c-codec@0e {
+	compatible = "qcom,wsa881x-i2c-codec";
+	reg = <0x0e>;
+	qcom,msm-gpios = "wsa_clk",
+			"wsa_reset";
+	qcom,pinctrl-names = "all_off",
+				"wsa_clk",
+				"wsa_active",
+				"wsa_clk_active";
+	pinctrl-names = "all_off",
+			"wsa_clk",
+			"wsa_active",
+			"wsa_clk_active";
+	pinctrl-0 = <&wsa_clk_off &wsa_suspend>;
+	pinctrl-1 = <&wsa_clk_on &wsa_suspend>;
+	pinctrl-2 = <&wsa_clk_off &wsa_active>;
+	pinctrl-3 = <&wsa_clk_on &wsa_active>;
+};
+
+wsa881x-i2c-codec@44 {
+	compatible = "qcom,wsa881x-i2c-codec";
+	reg = <0x44>;
+};
+
+wsa881x-i2c-codec@0f {
+	compatible = "qcom,wsa881x-i2c-codec";
+	reg = <0x0f>;
+	qcom,msm-gpios = "wsa_clk",
+			"wsa_reset";
+	qcom,pinctrl-names = "all_off",
+				"wsa_clk",
+				"wsa_active",
+				"wsa_clk_active";
+	pinctrl-names = "all_off",
+			"wsa_active",
+			"wsa_clk_active";
+	pinctrl-0 = <&wsa_clk_off &wsa_suspend>;
+	pinctrl-1 = <&wsa_clk_off &wsa_active>;
+	pinctrl-2 = <&wsa_clk_on &wsa_active>;
+};
+
+wsa_intc: wsa-irq {
+	compatible = "qcom,wsa-irq";
+	interrupt-controller;
+	#interrupt-cells = <1>;
+	interrupt-parent = <&msm_gpio>;
+	interrupts = <97 0>;
+	interrupt-names = "wsa-int";
+};
diff --git a/Documentation/devicetree/bindings/sound/wsa881x.txt b/Documentation/devicetree/bindings/sound/wsa881x.txt
new file mode 100644
index 0000000..e58ead5
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/wsa881x.txt
@@ -0,0 +1,45 @@
+wsa881x Audio CODEC
+
+wsa881x Audio CODEC can support either Soundwire interface or I2C interface.
+In Soundwire mode, this codec will operate as soundwire slave device and
+uses soundwire framework to interact with soundwire master controller.
+The wsa881x device nodes will be represented as child nodes to soundwire
+master device node in the devicetree. Currently, the below devicetree
+bindings are for soundwire mode only and does not include I2C mode.
+
+Required properties:
+
+ - compatible : "qcom,wsa881x"
+ - reg : Unique device ID of soundwire slave device(48 bits).
+ - qcom,spkr-sd-n-gpio : shutdown gpio pin to keep wsa881x in low power mode.
+ - pinctrl-names : Pincntrl entries to configure the PDM gpio lines and
+		  cross connection switch gpio accordingly
+ - pinctrl-0 : This explains the active state of the shutdown gpio pin
+          to keep wsa881x in low power mode
+ - pinctrl-1 : This explains the suspend state of the shutdown gpio pin
+          to keep wsa881x in low power mode
+
+Optional properties:
+
+Example:
+
+wsa881x@32000 {
+	compatible = "qcom,wsa881x";
+	reg = <0x00 0x032000>;
+	qcom,spkr-sd-n-gpio = <&pmi8994_gpios 2 0>;
+	pinctrl-names = "wsa_spkr_sd_act",
+		"wsa_spkr_sd_sus";
+	pinctrl-0 = <&wsa_spkr_sd_act>;
+	pinctrl-1 = <&wsa_spkr_sd_sus>;
+};
+
+wsa881x@42000 {
+	compatible = "qcom,wsa881x";
+	reg = <0x00 0x042000>;
+	qcom,spkr-sd-n-gpio = <&pmi8994_gpios 3 0>;
+	pinctrl-names = "wsa_spkr_sd_act",
+		"wsa_spkr_sd_sus";
+	pinctrl-0 = <&wsa_spkr_sd_act>;
+	pinctrl-1 = <&wsa_spkr_sd_sus>;
+
+};
diff --git a/Documentation/devicetree/bindings/soundwire/swr-wcd-ctrl.txt b/Documentation/devicetree/bindings/soundwire/swr-wcd-ctrl.txt
new file mode 100644
index 0000000..757ca9a
--- /dev/null
+++ b/Documentation/devicetree/bindings/soundwire/swr-wcd-ctrl.txt
@@ -0,0 +1,31 @@
+Qualcomm technologies inc WCD Codec SoundWire controller
+
+Required properties:
+
+ - compatible : should be "qcom,swr-wcd"
+ - reg : Unique device ID(48 bits) of Soundwire slave device node.
+	 In the below example, wsa881x is soundwire slave device for
+	 which the swr-devid is <0x0 0x032000> where 0x03 represents
+	 device Unique_ID, 0x20 represents Part_Id1 and 0x00
+	 represents part_Id2.
+- #address-cells = <2>;
+- #size-cells = <0>;
+
+Optional property:
+
+ Example:
+	swr_master {
+		compatible = "qcom,swr-wcd";
+		#address-cells = <2>;
+		#size-cells = <0>;
+
+		wsa881x@32000 {
+			compatible = "qcom,wsa881x";
+			reg = <0x00 0x032000>;
+		};
+
+		wsa881x@42000 {
+			compatible = "qcom,wsa881x";
+			reg = <0x00 0x042000>;
+		};
+	};
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
new file mode 100644
index 0000000..702f252
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/qpnp-adc-tm.txt
@@ -0,0 +1,241 @@
+Qualcomm Technologies, Inc. QPNP PMIC thermal monitor ADC driver (VADC_TM)
+
+QPNP PMIC thermal monitoring (TM) provides interface to thermal clients
+to set temperature thresholds and receive notification when the thresholds
+are crossed. A 15 bit ADC is used for measurements. The driver is part
+of the sysfs thermal framework that provides support to read the trip
+points, set threshold for the trip points and enable the trip points.
+Separat kernel api's are provided to usb_id and batt_therm
+to set thresholds and receive threshold notifications.
+
+VADC_TM node
+
+Required properties:
+- compatible : should be "qcom,qpnp-adc-tm" for thermal ADC driver.
+	     : should be "qcom,qpnp-adc-tm-hc" for thermal ADC driver using
+	       refreshed BTM peripheral.
+- reg : offset and length of the PMIC Aribter register map.
+- address-cells : Must be one.
+- size-cells : Must be zero.
+- interrupts : The thermal ADC bank peripheral interrupts for eoc, high and low interrupts.
+- interrupt-names : Should be "eoc-int-en-set", "high-thr-en-set" and "low-thr-en-set"
+		    for qcom,qpnp-adc-tm type.
+		  : Should be "eoc-int-en-set" for qcom,qpnp-adc-tm-hc type.
+- qcom,adc-bit-resolution : Bit resolution of the ADC.
+- qcom,adc-vdd-reference : Voltage reference used by the ADC.
+
+The following properties are required in the main node for qcom,qpnp-adc-tm-hc peripheral.
+- qcom,decimation : Should be present for qcom,qpnp-adc-tm-hc peripheral as its common setting
+		    across all the channels. Sampling rate to use for all the channel measurements.
+		    Select from the following unsigned int.
+		    0 : 512
+		    1 : 1K
+		    2 : 2K
+		    3 : 4K
+- qcom,fast-avg-setup : Should be present for qcom,qpnp-adc-tm-hc peripheral as its common setting
+		    across all the channels. Average number of samples to be used for measurement.
+		    Fast averaging provides the option to obtain a single measurement from the ADC
+		    that is an average of multiple samples. The value selected is 2^(value)
+		    Select from the following unsigned int.
+			0 : 1
+			1 : 2
+			2 : 4
+			3 : 8
+			4 : 16
+			5 : 32
+			6 : 64
+			7 : 128
+			8 : 256
+
+Optional properties:
+- qcom,thermal-node : If present a thermal node is created and the channel is registered as
+		part of the thermal sysfs which allows clients to use the thermal framework
+		to set temperature thresholds and receive notification when the temperature
+		crosses a set threshold, read temperature and enable/set trip types supported
+		by the thermal framework.
+- qcom,meas-interval-timer-idx: If present select from the following timer index to choose
+		a preset configurable measurement interval timer value. The driver defaults
+		to timer 2 with a measurement interval of 1 second if the property is not present.
+		0 : Select Timer 1 for a measurement polling interval of 3.9 milliseconds.
+		1 : Select Timer 2 for a measurement polling interval of 1 second.
+		2 : Select Timer 3 for a measurement polling interval of 4 seconds.
+- qcom,adc-tm-recalib-check: Add this property to check if recalibration required due to inaccuracy.
+- hkadc_ldo-supply : Add this property if VADC needs to perform a Software Vote for the HKADC.
+- hkadc_ok-supply : Add this property if the VADC needs to perform a Software vote for the HKADC VREG_OK.
+
+Client required property:
+- qcom,<consumer name>-adc_tm : The phandle to the corresponding adc_tm device.
+			The consumer name passed to the driver when calling
+			qpnp_get_adc_tm() is used to associate the client
+			with the corresponding device.
+
+Channel nodes
+NOTE: Atleast one Channel node is required.
+
+Required properties:
+- label : Channel name used for sysfs entry.
+- reg : AMUX channel number.
+- qcom,decimation : Sampling rate to use for the individual channel measurement.
+		    Select from the following unsigned int.
+		    0 : 512
+		    1 : 1K
+		    2 : 2K
+		    3 : 4K
+		    Note: This property is not required in the channel node in qcom,qpnp-adc-tm-hc
+		    peripheral.
+- qcom,pre-div-channel-scaling : Pre-div used for the channel before the signal is being measured.
+				 Select from the following unsigned int for the corresponding
+				 numerator/denominator pre-div ratio.
+				 0 : pre-div ratio of {1, 1}
+				 1 : pre-div ratio of {1, 3}
+				 2 : pre-div ratio of {1, 4}
+				 3 : pre-div ratio of {1, 6}
+				 4 : pre-div ratio of {1, 20}
+				 5 : pre-div ratio of {1, 8}
+				 6 : pre-div ratio of {10, 81}
+				 7 : pre-div ratio of {1, 10}
+- qcom,calibration-type : Reference voltage to use for channel calibration.
+			  Channel calibration is dependendent on the channel.
+			  Certain channels like XO_THERM, BATT_THERM use ratiometric
+			  calibration. Most other channels fall under absolute calibration.
+			  Select from the following strings.
+			  "absolute" : Uses the 625mv and 1.25V reference channels.
+			  "ratiometric" : Uses the reference Voltage/GND for calibration.
+- qcom,scale-function : Reverse scaling function used to convert raw ADC code to units specific to
+			a given channel.
+			Select from the following unsigned int.
+			0 : Scaling to convert voltage in uV to raw adc code.
+			1 : Scaling to convert decidegC to raw adc code.
+			2 : Scaling for converting USB_ID reverse scaling.
+			3 : Scaling to convert milldegC to raw ADC code.
+			4 : Scaling to convert smb_batt_therm values to raw ADC code.
+			5 : Scaling to perform reverse calibration for absolute voltage from uV
+			    to raw ADC code.
+			6 : Scaling to convert qrd skuh battery decidegC to raw ADC code.
+- qcom,hw-settle-time : Settling period for the channel before ADC read.
+			Select from the following unsigned int.
+			0 : 0us
+			1 : 100us
+			2 : 200us
+			3 : 300us
+			4 : 400us
+			5 : 500us
+			6 : 600us
+			7 : 700us
+			8 : 800us
+			9 : 900us
+			0xa : 1ms
+			0xb : 2ms
+			0xc : 4ms
+			0xd : 6ms
+			0xe : 8ms
+			0xf : 10ms
+- qcom,fast-avg-setup : Average number of samples to be used for measurement. Fast averaging
+			provides the option to obtain a single measurement from the ADC that
+			is an average of multiple samples. The value selected is 2^(value)
+			Select from
+			0 : 1
+			1 : 2
+			2 : 4
+			3 : 8
+			4 : 16
+			5 : 32
+			6 : 64
+			7 : 128
+			8 : 256
+			Note: This property is not required in the channel node in
+			qcom,qpnp-adc-tm-hc peripheral.
+- qcom,btm-channel-number : Depending on the PMIC version, a max of upto 8 BTM channels.
+			    The BTM channel numbers are statically allocated to the
+			    corresponding channel node.
+- qcom,adc_tm-vadc : phandle to the corresponding VADC device to read the ADC channels.
+
+Client device example:
+/* Add to the clients node that needs the ADC_TM channel A/D */
+client_node {
+	qcom,client-adc_tm = <&pm8941_adc_tm>;
+};
+
+Example for "qcom,qpnp-adc-tm" device:
+	/* Main Node */
+	qcom,vadc@3400 {
+                        compatible = "qcom,qpnp-adc-tm";
+                        reg = <0x3400 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+                        interrupts = <0x0 0x34 0x0>,
+					<0x0 0x34 0x3>,
+					<0x0 0x34 0x4>;
+			interrupt-names = "eoc-int-en-set",
+					  "high-thr-en-set",
+					  "low-thr-en-set";
+                        qcom,adc-bit-resolution = <15>;
+                        qcom,adc-vdd-reference = <1800>;
+			qcom,adc_tm-vadc = <&pm8941_vadc>;
+
+			/* Channel Node to be registered as part of thermal sysfs */
+                        chan@b5 {
+                                label = "pa_therm1";
+				reg = <0xb5>;
+                                qcom,decimation = <0>;
+                                qcom,pre-div-channel-scaling = <0>;
+                                qcom,calibration-type = "absolute";
+                                qcom,scale-function = <2>;
+                                qcom,hw-settle-time = <0>;
+                                qcom,fast-avg-setup = <0>;
+				qcom,btm-channel-number = <0x70>;
+				qcom,thermal-node;
+                        };
+
+			/* Channel Node */
+			chan@6 {
+				label = "vbat_sns";
+				reg = <6>;
+				qcom,decimation = <0>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,fast-avg-setup = <0>;
+				qcom,btm-channel-number = <0x78>;
+			};
+	};
+
+Example for "qcom,qpnp-adc-tm-hc" device:
+	/* Main Node */
+	pm8998_adc_tm: vadc@3400 {
+			compatible = "qcom,qpnp-adc-tm-hc";
+			reg = <0x3400 0x100>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			interrupts = <0x0 0x34 0x0 IRQ_TYPE_EDGE_RISING>;
+			interrupt-names = "eoc-int-en-set";
+			qcom,adc-bit-resolution = <15>;
+			qcom,adc-vdd-reference = <1875>;
+			qcom,adc_tm-vadc = <&pm8998_vadc>;
+			qcom,decimation = <0>;
+			qcom,fast-avg-setup = <0>;
+
+			/* Channel Node to be registered as part of thermal sysfs */
+                        chan@b5 {
+                                label = "pa_therm1";
+				reg = <0xb5>;
+                                qcom,pre-div-channel-scaling = <0>;
+                                qcom,calibration-type = "absolute";
+                                qcom,scale-function = <2>;
+                                qcom,hw-settle-time = <0>;
+				qcom,btm-channel-number = <0x70>;
+				qcom,thermal-node;
+                        };
+
+			/* Channel Node */
+			chan@6 {
+				label = "vbat_sns";
+				reg = <6>;
+				qcom,pre-div-channel-scaling = <1>;
+				qcom,calibration-type = "absolute";
+				qcom,scale-function = <3>;
+				qcom,hw-settle-time = <0>;
+				qcom,btm-channel-number = <0x78>;
+			};
+	};
diff --git a/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
new file mode 100644
index 0000000..bb20644
--- /dev/null
+++ b/Documentation/devicetree/bindings/thermal/qpnp-temp-alarm.txt
@@ -0,0 +1,77 @@
+Qualcomm Technologies, Inc. QPNP Temperature Alarm
+
+QPNP temperature alarm peripherals are found inside of Qualcomm Technologies,
+Inc. PMIC chips that utilize the MSM SPMI implementation.  These peripherals
+provide an interrupt signal and status register to identify high PMIC die
+temperature.
+
+Required properties:
+- compatible:      Must be "qcom,qpnp-temp-alarm".
+- reg:             Specifies the SPMI address and size for this temperature
+		    alarm device.
+- interrupts:      PMIC temperature alarm interrupt
+- label:           A string used as a descriptive name for this thermal device.
+		    This name should be 19 characters or less.
+
+Required structure:
+- A qcom,qpnp-temp-alarm node must be a child of an SPMI node that has specified
+   the spmi-slave-container property
+
+Optional properties:
+- qcom,channel-num:    VADC channel number associated PMIC DIE_TEMP thermistor.
+			If no channel is specified, then the die temperature
+			must be estimated based on the over temperature stage.
+- qcom,threshold-set:  Integer value which specifies which set of threshold
+			temperatures to use for the over temperature stages.
+			Possible values (x = {stage 1 threshold temperature,
+				stage 2 threshold temperature,
+				stage 3 threshold temperature}):
+			 0 = {105 C, 125 C, 145 C}
+			 1 = {110 C, 130 C, 150 C}
+			 2 = {115 C, 135 C, 155 C}
+			 3 = {120 C, 140 C, 160 C}
+- qcom,clock-rate:     Integer value which specifies the temperature monitoring
+			clock rate.  This property is only supported on GEN2
+			temperature alarm peripherals.
+			Supported values:
+			 0 = 100 Hz
+			 1 = 50 Hz
+			 2 = 25 Hz
+			 3 = 12.5 Hz
+- qcom,allow-override: Boolean which controls the ability of software to
+			override shutdowns.  If present, then software is
+			allowed to override automatic PMIC hardware stage 2 and
+			stage 3 over temperature shutdowns.  Otherwise, software
+			is not allowed to override automatic shutdown.
+- qcom,default-temp:   Specifies the default temperature in millicelcius to use
+			if no ADC channel is present to read the real time
+			temperature.
+- qcom,temp_alarm-vadc: Corresponding VADC device's phandle.
+
+Note, if a given optional qcom,* binding is not present, then the default
+hardware state for that feature will be maintained.
+
+Example:
+&spmi_bus {
+	#address-cells = <1>;
+	#size-cells = <0>;
+	interrupt-controller;
+	#interrupt-cells = <3>;
+
+	qcom,pm8941@0 {
+		spmi-slave-container;
+		reg = <0x0>;
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		qcom,temp-alarm@2400 {
+			compatible = "qcom,qpnp-temp-alarm";
+			reg = <0x2400 0x100>;
+			interrupts = <0x0 0x24 0x0>;
+			label = "pm8941_tz";
+			qcom,channel-num = <8>;
+			qcom,threshold-set = <0>;
+			qcom,temp_alarm-vadc = <&pm8941_vadc>;
+		};
+	};
+};
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 7c4cd86..4a81034 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -54,6 +54,8 @@
  - snps,quirk-frame-length-adjustment: Value for GFLADJ_30MHZ field of GFLADJ
 	register for post-silicon frame length adjustment when the
 	fladj_30mhz_sdbnd signal is invalid or incorrect.
+ - snps,disable-clk-gating: If present, disable controller's internal clock
+	gating. Default it is enabled.
 
 This is usually a subnode to DWC3 glue to which it is connected.
 
diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
index e5113aa..17ea0a8 100644
--- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt
+++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt
@@ -55,6 +55,8 @@
 - qcom,disable-host-mode-pm: If present, it disables XHCI PM runtime functionality when USB
   host mode is used.
 - qcom,core-clk-rate: If present, indicates clock frequency to be set for USB master clock.
+- qcom,core-clk-rate-hs: If present, indicates min core clock frequency required to support
+  hs speed.
 - extcon: phandles to external connector devices. First phandle should point to
 	  external connector, which provide "USB" cable events, the second
 	  should point to external connector device, which provide "USB-HOST"
diff --git a/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt
new file mode 100644
index 0000000..54d342c
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/qpnp-pdphy.txt
@@ -0,0 +1,67 @@
+Qualcomm Technologies, Inc. QPNP PD PHY - USB Power Delivery Physical layer
+
+Required properties:
+- compatible:		Must be "qcom,qpnp-pdphy"
+- reg:			The base address for this peripheral
+- vdd-pdphy-supply:	phandle to the VDD supply regulator node
+- interrupts:		Specifies the interrupt associated with the peripheral.
+- interrupt-names:	Specifies the interrupt names for the peripheral. Every
+			available interrupt needs to have an associated name
+			with it to indentify its purpose.
+
+			The following interrupts are required:
+
+			0: sig-tx
+				Triggers when a signal (HardReset or CableReset)
+				has been sent.
+			1: sig-rx
+				Triggers when a signal has been received.
+			2: msg-tx
+				Triggers when a message has been sent and the
+				related GoodCRC has been received.
+			3: msg-rx
+				Triggers when a message has been received and
+				the related GoodCRC was sent successfully.
+			4: msg-tx-failed
+				Triggers when a message failed all its
+				transmission attempts, either due to a non-idle
+				bus or missing GoodCRC reply.
+			5: msg-tx-discarded
+				Triggers when a message is received while a
+				transmission request was in place. The request
+				itself is discarded.
+			6: msg-rx-discarded
+				Triggers when a message was received but had to
+				be discarded due to the RX buffer still in use
+				by SW.
+
+Optional properties:
+- vbus-supply:		Regulator that enables VBUS source output
+- vconn-supply:		Regulator that enables VCONN source output. This will
+			be supplied on the USB CC line that is not used for
+			communication when Ra resistance is detected.
+- qcom,vconn-uses-external-source: Indicates whether VCONN supply is sourced
+			from an external regulator. If omitted, then it is
+			assumed it is connected to VBUS.
+
+Example:
+	qcom,qpnp-pdphy@1700 {
+		compatible = "qcom,qpnp-pdphy";
+		reg = <0x1700 0x100>;
+		vdd-pdphy-supply = <&pm8998_l24>;
+		interrupts = <0x2 0x17 0x0 IRQ_TYPE_EDGE_RISING>,
+			     <0x2 0x17 0x1 IRQ_TYPE_EDGE_RISING>,
+			     <0x2 0x17 0x2 IRQ_TYPE_EDGE_RISING>,
+			     <0x2 0x17 0x3 IRQ_TYPE_EDGE_RISING>,
+			     <0x2 0x17 0x4 IRQ_TYPE_EDGE_RISING>,
+			     <0x2 0x17 0x5 IRQ_TYPE_EDGE_RISING>,
+			     <0x2 0x17 0x6 IRQ_TYPE_EDGE_RISING>;
+
+		interrupt-names = "sig-tx",
+				  "sig-rx",
+				  "msg-tx",
+				  "msg-rx",
+				  "msg-tx-failed",
+				  "msg-tx-discarded",
+				  "msg-rx-discarded";
+	};
diff --git a/Documentation/scheduler/sched-hmp.txt b/Documentation/scheduler/sched-hmp.txt
index 22449ae..09b7dc1 100644
--- a/Documentation/scheduler/sched-hmp.txt
+++ b/Documentation/scheduler/sched-hmp.txt
@@ -43,6 +43,7 @@
    8.8 sched_get_busy
    8.9 sched_freq_alert
    8.10 sched_set_boost
+9. Device Tree bindings
 
 ===============
 1. INTRODUCTION
@@ -724,6 +725,16 @@
 	Default value of sched_select_prev_cpu_us is 2000 (2ms).  This can be
 	turned off by setting it to 0.
 
+e. /proc/sys/kernel/sched_short_burst_ns
+	This threshold controls whether a task is considered as "short-burst"
+	or not. "short-burst" tasks are eligible for packing to avoid overhead
+	associated with waking up an idle CPU. "non-idle" CPUs which are not
+	loaded with IRQs and can accommodate the waking task without exceeding
+	spill limits are considered. The ties are broken with load followed
+	by previous CPU. This tunable does not affect cluster selection.
+	It only affects CPU selection in a given cluster. This packing is
+	skipped for tasks that are eligible for "wake-up-idle" and "boost".
+
 **** 5.2.4 Wakeup Logic for Task "p"
 
 Wakeup task placement logic is as follows:
@@ -1220,6 +1231,23 @@
 task.  Scheduler places small wakee tasks woken up by big sync waker on the
 waker's cluster.
 
+*** 7.19 sched_prefer_sync_wakee_to_waker
+
+Appears at: /proc/sys/kernel/sched_prefer_sync_wakee_to_waker
+
+Default value: 0
+
+The default sync wakee policy has a preference to select an idle CPU in the
+waker cluster compared to the waker CPU running only 1 task. By selecting
+an idle CPU, it eliminates the chance of waker migrating to a different CPU
+after the wakee preempts it. This policy is also not susceptible to the
+incorrect "sync" usage i.e the waker does not goto sleep after waking up
+the wakee.
+
+However LPM exit latency associated with an idle CPU outweigh the above
+benefits on some targets. When this knob is turned on, the waker CPU is
+selected if it has only 1 runnable task.
+
 =========================
 8. HMP SCHEDULER TRACE POINTS
 =========================
@@ -1430,3 +1458,10 @@
 <task>-0     [004] d.h4 12700.711489: sched_set_boost: ref_count=1
 
 - ref_count: A non-zero value indicates boost is in effect
+
+========================
+9. Device Tree bindings
+========================
+
+The device tree bindings for the HMP scheduler are defined in
+Documentation/devicetree/bindings/sched/sched_hmp.txt
diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c
index ece04a4..2d1d821 100644
--- a/arch/arm/kernel/irq.c
+++ b/arch/arm/kernel/irq.c
@@ -37,6 +37,7 @@
 #include <linux/kallsyms.h>
 #include <linux/proc_fs.h>
 #include <linux/export.h>
+#include <linux/cpumask.h>
 
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/hardware/cache-uniphier.h>
@@ -127,6 +128,7 @@ static bool migrate_one_irq(struct irq_desc *desc)
 	const struct cpumask *affinity = irq_data_get_affinity_mask(d);
 	struct irq_chip *c;
 	bool ret = false;
+	struct cpumask available_cpus;
 
 	/*
 	 * If this is a per-CPU interrupt, or the affinity does not
@@ -135,8 +137,15 @@ static bool migrate_one_irq(struct irq_desc *desc)
 	if (irqd_is_per_cpu(d) || !cpumask_test_cpu(smp_processor_id(), affinity))
 		return false;
 
+	cpumask_copy(&available_cpus, affinity);
+	cpumask_andnot(&available_cpus, &available_cpus, cpu_isolated_mask);
+	affinity = &available_cpus;
+
 	if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
-		affinity = cpu_online_mask;
+		cpumask_andnot(&available_cpus, cpu_online_mask,
+			       cpu_isolated_mask);
+		if (cpumask_empty(affinity))
+			affinity = cpu_online_mask;
 		ret = true;
 	}
 
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index 01efdf478..fab57d3 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -1067,6 +1067,8 @@ void __init hyp_mode_check(void)
 #endif
 }
 
+void __init __weak init_random_pool(void) { }
+
 void __init setup_arch(char **cmdline_p)
 {
 	const struct machine_desc *mdesc;
@@ -1149,6 +1151,8 @@ void __init setup_arch(char **cmdline_p)
 
 	if (mdesc->init_early)
 		mdesc->init_early();
+
+	init_random_pool();
 }
 
 
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index ec279d1..c16e7d6 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -311,6 +311,9 @@ void __init init_cpu_topology(void)
 
 	parse_dt_topology();
 
+	for_each_possible_cpu(cpu)
+		update_siblings_masks(cpu);
+
 	/* Set scheduler topology descriptor */
 	set_sched_topology(arm_topology);
 }
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi
new file mode 100644
index 0000000..0b768e0
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-cmd.dtsi
@@ -0,0 +1,91 @@
+/* Copyright (c) 2014-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+	dsi_sim_cmd: qcom,mdss_dsi_sim_cmd{
+		qcom,mdss-dsi-panel-name = "Simulator cmd mode dsi panel";
+		qcom,mdss-dsi-panel-type = "dsi_cmd_mode";
+		qcom,mdss-dsi-panel-framerate = <60>;
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-panel-width = <640>;
+		qcom,mdss-dsi-panel-height = <480>;
+		qcom,mdss-dsi-h-front-porch = <20>;
+		qcom,mdss-dsi-h-back-porch = <20>;
+		qcom,mdss-dsi-h-pulse-width = <16>;
+		qcom,mdss-dsi-h-sync-skew = <0>;
+		qcom,mdss-dsi-v-back-porch = <16>;
+		qcom,mdss-dsi-v-front-porch = <4>;
+		qcom,mdss-dsi-v-pulse-width = <1>;
+		qcom,mdss-dsi-h-left-border = <0>;
+		qcom,mdss-dsi-h-right-border = <0>;
+		qcom,mdss-dsi-v-top-border = <0>;
+		qcom,mdss-dsi-v-bottom-border = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-color-order = "rgb_swap_rgb";
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-h-sync-pulse = <0>;
+		qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-hor-line-idle = <0 40 256>,
+						<40 120 128>,
+						<120 240 64>;
+		qcom,mdss-dsi-panel-timings = [cd 32 22 00 60 64 26 34 29 03
+									04 00];
+		qcom,mdss-dsi-reset-sequence = <1 10>, <0 10>, <1 10>;
+		qcom,mdss-dsi-t-clk-post = <0x03>;
+		qcom,mdss-dsi-t-clk-pre = <0x27>;
+		qcom,mdss-dsi-bl-max-level = <4095>;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-te-pin-select = <1>;
+		qcom,mdss-dsi-wr-mem-start = <0x2c>;
+		qcom,mdss-dsi-wr-mem-continue = <0x3c>;
+		qcom,mdss-dsi-te-dcs-command = <1>;
+		qcom,mdss-dsi-te-check-enable;
+		qcom,mdss-dsi-te-using-te-pin;
+		qcom,mdss-dsi-on-command = [29 01 00 00 00 00 02 b0 03
+			05 01 00 00 0a 00 01 00
+			/* Soft reset, wait 10ms */
+			15 01 00 00 0a 00 02 3a 77
+			/* Set Pixel format (24 bpp) */
+			39 01 00 00 0a 00 05 2a 00 00 04 ff
+			/* Set Column address */
+			39 01 00 00 0a 00 05 2b 00 00 05 9f
+			/* Set page address */
+			15 01 00 00 0a 00 02 35 00
+			/* Set tear on */
+			39 01 00 00 0a 00 03 44 00 00
+			/* Set tear scan line */
+			15 01 00 00 0a 00 02 51 ff
+			/* write display brightness */
+			15 01 00 00 0a 00 02 53 24
+			 /* write control brightness */
+			15 01 00 00 0a 00 02 55 00
+			/* CABC brightness */
+			05 01 00 00 78 00 01 11
+			/* exit sleep mode, wait 120ms */
+			05 01 00 00 10 00 01 29];
+			/* Set display on, wait 16ms */
+		qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+		qcom,mdss-dsi-off-command = [05 01 00 00 32 00 02 28 00
+					05 01 00 00 78 00 02 10 00];
+		qcom,mdss-dsi-off-command-state = "dsi_hs_mode";
+		qcom,panel-ack-disabled;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/dsi-panel-sim-video.dtsi b/arch/arm64/boot/dts/qcom/dsi-panel-sim-video.dtsi
new file mode 100644
index 0000000..a73a796
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/dsi-panel-sim-video.dtsi
@@ -0,0 +1,57 @@
+/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&mdss_mdp {
+	dsi_sim_vid: qcom,mdss_dsi_sim_video {
+		qcom,mdss-dsi-panel-name = "Simulator video mode dsi panel";
+		qcom,mdss-dsi-panel-type = "dsi_video_mode";
+		qcom,mdss-dsi-panel-framerate = <60>;
+		qcom,mdss-dsi-virtual-channel-id = <0>;
+		qcom,mdss-dsi-stream = <0>;
+		qcom,mdss-dsi-panel-width = <640>;
+		qcom,mdss-dsi-panel-height = <480>;
+		qcom,mdss-dsi-h-front-porch = <8>;
+		qcom,mdss-dsi-h-back-porch = <8>;
+		qcom,mdss-dsi-h-pulse-width = <8>;
+		qcom,mdss-dsi-h-sync-skew = <0>;
+		qcom,mdss-dsi-v-back-porch = <6>;
+		qcom,mdss-dsi-v-front-porch = <6>;
+		qcom,mdss-dsi-v-pulse-width = <2>;
+		qcom,mdss-dsi-h-left-border = <0>;
+		qcom,mdss-dsi-h-right-border = <0>;
+		qcom,mdss-dsi-v-top-border = <0>;
+		qcom,mdss-dsi-v-bottom-border = <0>;
+		qcom,mdss-dsi-bpp = <24>;
+		qcom,mdss-dsi-underflow-color = <0xff>;
+		qcom,mdss-dsi-border-color = <0>;
+		qcom,mdss-dsi-on-command = [32 01 00 00 00 00 02 00 00];
+		qcom,mdss-dsi-off-command = [22 01 00 00 00 00 02 00 00];
+		qcom,mdss-dsi-on-command-state = "dsi_lp_mode";
+		qcom,mdss-dsi-off-command-state = "dsi_lp_mode";
+		qcom,mdss-dsi-h-sync-pulse = <0>;
+		qcom,mdss-dsi-traffic-mode = "non_burst_sync_event";
+		qcom,mdss-dsi-bllp-eof-power-mode;
+		qcom,mdss-dsi-bllp-power-mode;
+		qcom,mdss-dsi-lane-0-state;
+		qcom,mdss-dsi-lane-1-state;
+		qcom,mdss-dsi-lane-2-state;
+		qcom,mdss-dsi-lane-3-state;
+		qcom,mdss-dsi-panel-timings =
+					[00 00 00 00 00 00 00 00 00 00 00 00];
+		qcom,mdss-dsi-t-clk-post = <0x04>;
+		qcom,mdss-dsi-t-clk-pre = <0x1b>;
+		qcom,mdss-dsi-dma-trigger = "trigger_sw";
+		qcom,mdss-dsi-mdp-trigger = "none";
+		qcom,mdss-dsi-reset-sequence = <1 0>, <0 0>, <1 0>;
+		qcom,panel-ack-disabled;
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/msmskunk-camera.dtsi b/arch/arm64/boot/dts/qcom/msmskunk-camera.dtsi
new file mode 100644
index 0000000..c197d65
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msmskunk-camera.dtsi
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	qcom,cam-req-mgr {
+		compatible = "qcom,cam-req-mgr";
+		status = "ok";
+	};
+};
diff --git a/arch/arm64/boot/dts/qcom/msmskunk-coresight.dtsi b/arch/arm64/boot/dts/qcom/msmskunk-coresight.dtsi
index 0f7ac44..d36c0ff 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk-coresight.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk-coresight.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -51,6 +51,7 @@
 		arm,buffer-size = <0x400000>;
 
 		coresight-name = "coresight-tmc-etr";
+		coresight-ctis = <&cti0 &cti8>;
 
 		clocks = <&clock_gcc RPMH_QDSS_CLK>,
 			 <&clock_gcc RPMH_QDSS_A_CLK>;
@@ -72,7 +73,7 @@
 		reg-names = "tmc-base";
 
 		coresight-name = "coresight-tmc-etf";
-
+		coresight-ctis = <&cti0 &cti8>;
 		arm,default-sink;
 
 		clocks = <&clock_gcc RPMH_QDSS_CLK>,
@@ -195,4 +196,300 @@
 			};
 		};
 	};
+
+	cti0: cti@6010000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6010000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti0";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti1: cti@6011000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6011000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti1";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti2: cti@6012000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6012000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti2";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti3: cti@6013000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6013000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti3";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti4: cti@6014000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6014000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti4";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti5: cti@6015000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6015000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti5";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti6: cti@6016000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6016000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti6";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti7: cti@6017000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6017000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti7";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti8: cti@6018000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6018000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti8";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti9: cti@6019000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x6019000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti9";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti10: cti@601a000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x601a000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti10";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti11: cti@601b000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x601b000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti11";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti12: cti@601c000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x601c000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti12";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti13: cti@601d000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x601d000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti13";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti14: cti@601e000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x601e000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti14";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti15: cti@601f000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x601f000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti15";
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu0: cti@7420000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7420000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu0";
+		cpu = <&CPU0>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu1: cti@7520000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7520000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu1";
+		cpu = <&CPU1>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu2: cti@7620000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7620000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu2";
+		cpu = <&CPU2>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu3: cti@7720000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7720000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu3";
+		cpu = <&CPU3>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu4: cti@7020000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7020000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu4";
+		cpu = <&CPU4>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu5: cti@7120000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7120000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu5";
+		cpu = <&CPU5>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu6: cti@7220000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7220000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu6";
+		cpu = <&CPU6>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
+
+	cti_cpu7: cti@7320000 {
+		compatible = "arm,coresight-cti";
+		reg = <0x7320000 0x1000>;
+		reg-names = "cti-base";
+
+		coresight-name = "coresight-cti-cpu7";
+		cpu = <&CPU7>;
+
+		clocks = <&clock_gcc RPMH_QDSS_CLK>,
+			 <&clock_gcc RPMH_QDSS_A_CLK>;
+		clock-names = "core_clk", "core_a_clk";
+	};
 };
diff --git a/arch/arm64/boot/dts/qcom/msmskunk-sde.dtsi b/arch/arm64/boot/dts/qcom/msmskunk-sde.dtsi
index a6b7f8e..980ef8c 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk-sde.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk-sde.dtsi
@@ -55,6 +55,8 @@
 					1 5 9 13>;
 		qcom,sde-sspp-excl-rect = <1 1 1 1
 						1 1 1 1>;
+		qcom,sde-sspp-smart-dma-priority = <5 6 7 8 1 2 3 4>;
+		qcom,sde-smart-dma-rev = "smart_dma_v2";
 
 		qcom,sde-mixer-pair-mask = <2 1 6 0 0 3>;
 
diff --git a/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi b/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi
index 74e6172..dd6d4d4 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk-usb.dtsi
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -41,6 +41,9 @@
 		clock-names = "core_clk", "iface_clk", "bus_aggr_clk",
 			"utmi_clk", "sleep_clk", "cfg_ahb_clk", "xo";
 
+		qcom,core-clk-rate = <133333333>;
+		qcom,core-clk-rate-hs = <66666667>;
+
 		resets = <&clock_gcc GCC_USB30_PRIM_BCR>;
 		reset-names = "core_reset";
 
@@ -52,6 +55,7 @@
 			usb-phy = <&qusb_phy0>, <&usb_nop_phy>;
 			tx-fifo-resize;
 			snps,nominal-elastic-buffer;
+			snps,disable-clk-gating;
 			snps,hird_thresh = <0x10>;
 		};
 	};
diff --git a/arch/arm64/boot/dts/qcom/msmskunk.dtsi b/arch/arm64/boot/dts/qcom/msmskunk.dtsi
index c0862f5..eca1a73 100644
--- a/arch/arm64/boot/dts/qcom/msmskunk.dtsi
+++ b/arch/arm64/boot/dts/qcom/msmskunk.dtsi
@@ -795,6 +795,17 @@
 		qcom,gpio-force-stop = <&smp2pgpio_ssr_smp2p_3_out 0 0>;
 	};
 
+	slim_aud: slim@171c0000 {
+		cell-index = <1>;
+		compatible = "qcom,slim-ngd";
+		reg = <0x171c0000 0x2c000>,
+			<0x17184000 0x2a000>;
+		reg-names = "slimbus_physical", "slimbus_bam_physical";
+		interrupts = <0 163 0>, <0 164 0>;
+		interrupt-names = "slimbus_irq", "slimbus_bam_irq";
+		qcom,ea-pc = <0x270>;
+	};
+
 	eud: qcom,msm-eud@88e0000 {
 		compatible = "qcom,msm-eud";
 		interrupt-names = "eud_irq";
@@ -1040,6 +1051,10 @@
 			qcom,dump-node = <&L1_D_103>;
 			qcom,dump-id = <0x87>;
 		};
+		qcom,llcc_d_cache {
+			qcom,dump-node = <&llcc>;
+			qcom,dump-id = <0x121>;
+		};
 	};
 
 	kryo3xx-erp {
@@ -1064,6 +1079,7 @@
 			compatible = "qcom,msmskunk-llcc";
 			#cache-cells = <1>;
 			max-slices = <32>;
+			qcom,dump-size = <0x3c0000>;
 		};
 
 		qcom,llcc-erp {
@@ -1327,6 +1343,11 @@
 		};
 	};
 
+	qcom,sps {
+		compatible = "qcom,msm_sps_4k";
+		qcom,pipe-attr-ee;
+	};
+
 	qcom,msm_gsi {
 		compatible = "qcom,msm_gsi";
 	};
@@ -1454,6 +1475,12 @@
 		>;
 	};
 
+	qcom,ipa_fws {
+		compatible = "qcom,pil-tz-generic";
+		qcom,pas-id = <0xf>;
+		qcom,firmware-name = "ipa_fws";
+	};
+
 	qcom,chd_sliver {
 		compatible = "qcom,core-hang-detect";
 		label = "silver";
@@ -1590,3 +1617,4 @@
 #include "msm-arm-smmu-skunk.dtsi"
 #include "msmskunk-ion.dtsi"
 #include "msmskunk-smp2p.dtsi"
+#include "msmskunk-camera.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdmbat-cdp.dtsi b/arch/arm64/boot/dts/qcom/sdmbat-cdp.dtsi
index 1003478..77151c5 100644
--- a/arch/arm64/boot/dts/qcom/sdmbat-cdp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdmbat-cdp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -11,4 +11,4 @@
  */
 
 #include "msmskunk-cdp.dtsi"
-
+#include "sdmbat-pinctrl.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdmbat-mtp.dtsi b/arch/arm64/boot/dts/qcom/sdmbat-mtp.dtsi
index ad26a14..af7a194 100644
--- a/arch/arm64/boot/dts/qcom/sdmbat-mtp.dtsi
+++ b/arch/arm64/boot/dts/qcom/sdmbat-mtp.dtsi
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -11,4 +11,4 @@
  */
 
 #include "msmskunk-mtp.dtsi"
-
+#include "sdmbat-pinctrl.dtsi"
diff --git a/arch/arm64/boot/dts/qcom/sdmbat-pinctrl.dtsi b/arch/arm64/boot/dts/qcom/sdmbat-pinctrl.dtsi
new file mode 100644
index 0000000..ead34a6
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/sdmbat-pinctrl.dtsi
@@ -0,0 +1,23 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+&soc {
+	tlmm: pinctrl@03800000 {
+		compatible = "qcom,sdmbat-pinctrl";
+		reg = <0x03800000 0xc00000>;
+		interrupts = <0 208 0>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		interrupt-controller;
+		#interrupt-cells = <2>;
+	};
+};
diff --git a/arch/arm64/configs/msmskunk-perf_defconfig b/arch/arm64/configs/msmskunk-perf_defconfig
index 5f3dda2e..843e532 100644
--- a/arch/arm64/configs/msmskunk-perf_defconfig
+++ b/arch/arm64/configs/msmskunk-perf_defconfig
@@ -9,6 +9,7 @@
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
+CONFIG_CGROUP_SCHEDTUNE=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_FREEZER=y
 CONFIG_CGROUP_CPUACCT=y
@@ -18,6 +19,7 @@
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
 CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SCHED_TUNE=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_RD_XZ is not set
 # CONFIG_RD_LZO is not set
@@ -196,6 +198,7 @@
 CONFIG_RMNET_DATA=y
 CONFIG_RMNET_DATA_DEBUG_PKT=y
 CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
 CONFIG_CFG80211=y
 CONFIG_CFG80211_INTERNAL_REGDB=y
 CONFIG_RFKILL=y
@@ -251,14 +254,16 @@
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
 CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
 CONFIG_SPMI=y
+CONFIG_PINCTRL_MSMSKUNK=y
+CONFIG_PINCTRL_SDMBAT=y
 CONFIG_GPIOLIB=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_POWER_RESET_QCOM=y
 CONFIG_POWER_RESET_XGENE=y
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_MFD_SPMI_PMIC=y
-CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_RPMH=y
 CONFIG_REGULATOR_STUB=y
@@ -269,8 +274,11 @@
 CONFIG_VIDEO_ADV_DEBUG=y
 CONFIG_VIDEO_FIXED_MINOR_RANGES=y
 CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_FB=y
-CONFIG_FB_ARMCLCD=y
+CONFIG_SPECTRA_CAMERA=y
+CONFIG_DRM=y
+CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
 CONFIG_LOGO=y
 # CONFIG_LOGO_LINUX_MONO is not set
 # CONFIG_LOGO_LINUX_VGA16 is not set
@@ -311,6 +319,8 @@
 CONFIG_USB_CONFIGFS_F_HID=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
 CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_USB_CONFIGFS_F_CCID=y
+CONFIG_USB_CONFIGFS_F_GSI=y
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_TEST=y
@@ -325,6 +335,8 @@
 CONFIG_ANDROID_LOW_MEMORY_KILLER=y
 CONFIG_ION=y
 CONFIG_ION_MSM=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
 CONFIG_MSM_GCC_SKUNK=y
 CONFIG_MSM_VIDEOCC_SKUNK=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -335,11 +347,11 @@
 CONFIG_IOMMU_TESTS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_MSMSKUNK_LLCC=y
-CONFIG_QCOM_SCM=y
 CONFIG_QCOM_EUD=y
 CONFIG_QCOM_WATCHDOG_V2=y
 CONFIG_QCOM_MEMORY_DUMP_V2=y
 CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
 CONFIG_MSM_SMEM=y
 CONFIG_MSM_GLINK=y
 CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
@@ -369,7 +381,6 @@
 CONFIG_FUSE_FS=y
 CONFIG_MSDOS_FS=y
 CONFIG_VFAT_FS=y
-CONFIG_TMPFS=y
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_ECRYPT_FS=y
 CONFIG_ECRYPT_FS_MESSAGING=y
@@ -388,6 +399,7 @@
 CONFIG_CORESIGHT_LINK_AND_SINK_TMC=y
 CONFIG_CORESIGHT_QCOM_REPLICATOR=y
 CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_CTI=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SMACK=y
diff --git a/arch/arm64/configs/msmskunk_defconfig b/arch/arm64/configs/msmskunk_defconfig
index d576155..728e337 100644
--- a/arch/arm64/configs/msmskunk_defconfig
+++ b/arch/arm64/configs/msmskunk_defconfig
@@ -8,6 +8,7 @@
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_CPU_MAX_BUF_SHIFT=17
 CONFIG_CGROUPS=y
+CONFIG_CGROUP_SCHEDTUNE=y
 CONFIG_CGROUP_SCHED=y
 CONFIG_RT_GROUP_SCHED=y
 CONFIG_CGROUP_FREEZER=y
@@ -18,6 +19,7 @@
 CONFIG_NAMESPACES=y
 # CONFIG_UTS_NS is not set
 # CONFIG_PID_NS is not set
+CONFIG_SCHED_TUNE=y
 CONFIG_BLK_DEV_INITRD=y
 # CONFIG_RD_XZ is not set
 # CONFIG_RD_LZO is not set
@@ -202,6 +204,7 @@
 CONFIG_RMNET_DATA=y
 CONFIG_RMNET_DATA_DEBUG_PKT=y
 CONFIG_BT=y
+CONFIG_MSM_BT_POWER=y
 CONFIG_CFG80211=y
 CONFIG_CFG80211_INTERNAL_REGDB=y
 # CONFIG_CFG80211_CRDA_SUPPORT is not set
@@ -258,14 +261,16 @@
 CONFIG_SPI=y
 CONFIG_SPI_QUP=y
 CONFIG_SPI_SPIDEV=y
+CONFIG_SLIMBUS_MSM_NGD=y
 CONFIG_SPMI=y
+CONFIG_PINCTRL_MSMSKUNK=y
+CONFIG_PINCTRL_SDMBAT=y
 CONFIG_GPIOLIB=y
 CONFIG_GPIO_SYSFS=y
 CONFIG_POWER_RESET_QCOM=y
 CONFIG_POWER_RESET_XGENE=y
 CONFIG_POWER_RESET_SYSCON=y
 CONFIG_MFD_SPMI_PMIC=y
-CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED_VOLTAGE=y
 CONFIG_REGULATOR_RPMH=y
 CONFIG_REGULATOR_STUB=y
@@ -276,8 +281,11 @@
 CONFIG_VIDEO_ADV_DEBUG=y
 CONFIG_VIDEO_FIXED_MINOR_RANGES=y
 CONFIG_V4L_PLATFORM_DRIVERS=y
-CONFIG_FB=y
+CONFIG_SPECTRA_CAMERA=y
+CONFIG_DRM=y
 CONFIG_FB_VIRTUAL=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
 CONFIG_LOGO=y
 # CONFIG_LOGO_LINUX_MONO is not set
 # CONFIG_LOGO_LINUX_VGA16 is not set
@@ -317,6 +325,8 @@
 CONFIG_USB_CONFIGFS_F_HID=y
 CONFIG_USB_CONFIGFS_F_DIAG=y
 CONFIG_USB_CONFIGFS_F_CDEV=y
+CONFIG_USB_CONFIGFS_F_CCID=y
+CONFIG_USB_CONFIGFS_F_GSI=y
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK_MINORS=32
 CONFIG_MMC_TEST=y
@@ -336,6 +346,8 @@
 CONFIG_ANDROID_LOW_MEMORY_KILLER=y
 CONFIG_ION=y
 CONFIG_ION_MSM=y
+CONFIG_SPS=y
+CONFIG_SPS_SUPPORT_NDP_BAM=y
 CONFIG_MSM_GCC_SKUNK=y
 CONFIG_MSM_VIDEOCC_SKUNK=y
 CONFIG_REMOTE_SPINLOCK_MSM=y
@@ -346,11 +358,11 @@
 CONFIG_IOMMU_TESTS=y
 CONFIG_QCOM_LLCC=y
 CONFIG_QCOM_MSMSKUNK_LLCC=y
-CONFIG_QCOM_SCM=y
 CONFIG_QCOM_EUD=y
 CONFIG_QCOM_WATCHDOG_V2=y
 CONFIG_QCOM_MEMORY_DUMP_V2=y
 CONFIG_QCOM_SECURE_BUFFER=y
+CONFIG_QCOM_EARLY_RANDOM=y
 CONFIG_MSM_SMEM=y
 CONFIG_MSM_GLINK=y
 CONFIG_MSM_GLINK_LOOPBACK_SERVER=y
@@ -381,7 +393,6 @@
 CONFIG_FUSE_FS=y
 CONFIG_MSDOS_FS=y
 CONFIG_VFAT_FS=y
-CONFIG_TMPFS=y
 CONFIG_TMPFS_POSIX_ACL=y
 CONFIG_EFIVAR_FS=y
 CONFIG_ECRYPT_FS=y
@@ -436,6 +447,7 @@
 CONFIG_CORESIGHT_SOURCE_ETM4X=y
 CONFIG_CORESIGHT_QCOM_REPLICATOR=y
 CONFIG_CORESIGHT_STM=y
+CONFIG_CORESIGHT_CTI=y
 CONFIG_SECURITY=y
 CONFIG_SECURITY_SELINUX=y
 CONFIG_SECURITY_SMACK=y
diff --git a/arch/arm64/include/asm/assembler.h b/arch/arm64/include/asm/assembler.h
index 359d9d2..e3c80f1 100644
--- a/arch/arm64/include/asm/assembler.h
+++ b/arch/arm64/include/asm/assembler.h
@@ -51,6 +51,18 @@
 	.endm
 
 /*
+ * Save/disable and restore interrupts.
+ */
+	.macro	save_and_disable_irqs, olddaif
+	mrs	\olddaif, daif
+	disable_irq
+	.endm
+
+	.macro	restore_irqs, olddaif
+	msr	daif, \olddaif
+	.endm
+
+/*
  * Enable and disable debug exceptions.
  */
 	.macro	disable_dbg
diff --git a/arch/arm64/include/asm/cacheflush.h b/arch/arm64/include/asm/cacheflush.h
index 012b95c..9377bec 100644
--- a/arch/arm64/include/asm/cacheflush.h
+++ b/arch/arm64/include/asm/cacheflush.h
@@ -40,6 +40,10 @@
  *	the implementation assumes non-aliasing VIPT D-cache and (aliasing)
  *	VIPT or ASID-tagged VIVT I-cache.
  *
+ *	flush_cache_all()
+ *
+ *		Unconditionally clean and invalidate the entire cache.
+ *
  *	flush_cache_mm(mm)
  *
  *		Clean and invalidate all user space cache entries
@@ -65,6 +69,7 @@
  *		- kaddr  - page address
  *		- size   - region size
  */
+extern void flush_cache_all(void);
 extern void flush_cache_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
 extern void flush_icache_range(unsigned long start, unsigned long end);
 extern void __flush_dcache_area(void *addr, size_t len);
diff --git a/arch/arm64/include/asm/pci.h b/arch/arm64/include/asm/pci.h
index b9a7ba9..ddd57a7 100644
--- a/arch/arm64/include/asm/pci.h
+++ b/arch/arm64/include/asm/pci.h
@@ -39,3 +39,8 @@ static inline int pci_proc_domain(struct pci_bus *bus)
 
 #endif  /* __KERNEL__ */
 #endif  /* __ASM_PCI_H */
+
+#ifdef CONFIG_PCI_MSM
+#define arch_setup_msi_irqs arch_setup_msi_irqs
+#define arch_teardown_msi_irqs arch_teardown_msi_irqs
+#endif
diff --git a/arch/arm64/include/asm/proc-fns.h b/arch/arm64/include/asm/proc-fns.h
index 14ad6e4..220633b7 100644
--- a/arch/arm64/include/asm/proc-fns.h
+++ b/arch/arm64/include/asm/proc-fns.h
@@ -28,8 +28,12 @@
 struct mm_struct;
 struct cpu_suspend_ctx;
 
+extern void cpu_cache_off(void);
 extern void cpu_do_idle(void);
 extern void cpu_do_switch_mm(unsigned long pgd_phys, struct mm_struct *mm);
+extern void cpu_reset(unsigned long addr) __attribute__((noreturn));
+void cpu_soft_restart(phys_addr_t cpu_reset,
+		unsigned long addr) __attribute__((noreturn));
 extern void cpu_do_suspend(struct cpu_suspend_ctx *ptr);
 extern u64 cpu_do_resume(phys_addr_t ptr, u64 idmap_ttbr);
 
diff --git a/arch/arm64/include/asm/topology.h b/arch/arm64/include/asm/topology.h
index 8b57339..e708b3d 100644
--- a/arch/arm64/include/asm/topology.h
+++ b/arch/arm64/include/asm/topology.h
@@ -21,6 +21,7 @@ extern struct cpu_topology cpu_topology[NR_CPUS];
 void init_cpu_topology(void);
 void store_cpu_topology(unsigned int cpuid);
 const struct cpumask *cpu_coregroup_mask(int cpu);
+unsigned long arch_get_cpu_efficiency(int cpu);
 
 #ifdef CONFIG_NUMA
 
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 432bcd9..a754fa3 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -19,6 +19,7 @@
 #include <asm/cpu.h>
 #include <asm/cputype.h>
 #include <asm/cpufeature.h>
+#include <asm/elf.h>
 
 #include <linux/bitops.h>
 #include <linux/bug.h>
@@ -112,6 +113,8 @@ static int c_show(struct seq_file *m, void *v)
 	int i, j;
 	bool compat = personality(current->personality) == PER_LINUX32;
 
+	seq_printf(m, "Processor\t: AArch64 Processor rev %d (%s)\n",
+		read_cpuid_id() & 15, ELF_PLATFORM);
 	for_each_present_cpu(i) {
 		struct cpuinfo_arm64 *cpuinfo = &per_cpu(cpu_data, i);
 		u32 midr = cpuinfo->reg_midr;
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 6a9cb77..49f3ae0 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -242,6 +242,8 @@ static void __init request_standard_resources(void)
 
 u64 __cpu_logical_map[NR_CPUS] = { [0 ... NR_CPUS-1] = INVALID_HWID };
 
+void __init __weak init_random_pool(void) { }
+
 void __init setup_arch(char **cmdline_p)
 {
 	pr_info("Boot CPU: AArch64 Processor [%08x]\n", read_cpuid_id());
@@ -326,6 +328,8 @@ void __init setup_arch(char **cmdline_p)
 			"This indicates a broken bootloader or old kernel\n",
 			boot_args[1], boot_args[2], boot_args[3]);
 	}
+
+	init_random_pool();
 }
 
 static int __init topology_init(void)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 082d8d5..86774d3 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -815,6 +815,7 @@ static void ipi_cpu_stop(unsigned int cpu)
 {
 	set_cpu_active(cpu, false);
 
+	flush_cache_all();
 	local_irq_disable();
 
 	while (1)
diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c
index 694f6de..349b131 100644
--- a/arch/arm64/kernel/topology.c
+++ b/arch/arm64/kernel/topology.c
@@ -19,10 +19,34 @@
 #include <linux/nodemask.h>
 #include <linux/of.h>
 #include <linux/sched.h>
+#include <linux/slab.h>
 
 #include <asm/cputype.h>
 #include <asm/topology.h>
 
+/*
+ * cpu power table
+ * This per cpu data structure describes the relative capacity of each core.
+ * On a heteregenous system, cores don't have the same computation capacity
+ * and we reflect that difference in the cpu_power field so the scheduler can
+ * take this difference into account during load balance. A per cpu structure
+ * is preferred because each CPU updates its own cpu_power field during the
+ * load balance except for idle cores. One idle core is selected to run the
+ * rebalance_domains for all idle cores and the cpu_power can be updated
+ * during this sequence.
+ */
+static DEFINE_PER_CPU(unsigned long, cpu_scale);
+
+unsigned long arch_scale_freq_power(struct sched_domain *sd, int cpu)
+{
+	return per_cpu(cpu_scale, cpu);
+}
+
+static void set_power_scale(unsigned int cpu, unsigned long power)
+{
+	per_cpu(cpu_scale, cpu) = power;
+}
+
 static int __init get_cpu_for_node(struct device_node *node)
 {
 	struct device_node *cpu_node;
@@ -161,6 +185,46 @@ static int __init parse_cluster(struct device_node *cluster, int depth)
 	return 0;
 }
 
+struct cpu_efficiency {
+	const char *compatible;
+	unsigned long efficiency;
+};
+
+/*
+ * Table of relative efficiency of each processors
+ * The efficiency value must fit in 20bit and the final
+ * cpu_scale value must be in the range
+ *   0 < cpu_scale < 3*SCHED_CAPACITY_SCALE/2
+ * in order to return at most 1 when DIV_ROUND_CLOSEST
+ * is used to compute the capacity of a CPU.
+ * Processors that are not defined in the table,
+ * use the default SCHED_CAPACITY_SCALE value for cpu_scale.
+ */
+static const struct cpu_efficiency table_efficiency[] = {
+	{ NULL, },
+};
+
+static unsigned long *__cpu_capacity;
+#define cpu_capacity(cpu)	__cpu_capacity[cpu]
+
+static unsigned long middle_capacity = 1;
+
+static DEFINE_PER_CPU(unsigned long, cpu_efficiency) = SCHED_CAPACITY_SCALE;
+
+unsigned long arch_get_cpu_efficiency(int cpu)
+{
+	return per_cpu(cpu_efficiency, cpu);
+}
+EXPORT_SYMBOL(arch_get_cpu_efficiency);
+
+/*
+ * Iterate all CPUs' descriptor in DT and compute the efficiency
+ * (as per table_efficiency). Also calculate a middle efficiency
+ * as close as possible to  (max{eff_i} - min{eff_i}) / 2
+ * This is later used to scale the cpu_power field such that an
+ * 'average' CPU is of middle power. Also see the comments near
+ * table_efficiency[] and update_cpu_power().
+ */
 static int __init parse_dt_topology(void)
 {
 	struct device_node *cn, *map;
@@ -200,6 +264,107 @@ static int __init parse_dt_topology(void)
 	return ret;
 }
 
+static void __init parse_dt_cpu_power(void)
+{
+	const struct cpu_efficiency *cpu_eff;
+	struct device_node *cn;
+	unsigned long min_capacity = ULONG_MAX;
+	unsigned long max_capacity = 0;
+	unsigned long capacity = 0;
+	int cpu;
+
+	__cpu_capacity = kcalloc(nr_cpu_ids, sizeof(*__cpu_capacity),
+				 GFP_NOWAIT);
+
+	for_each_possible_cpu(cpu) {
+		const u32 *rate;
+		int len;
+		u32 efficiency;
+
+		/* Too early to use cpu->of_node */
+		cn = of_get_cpu_node(cpu, NULL);
+		if (!cn) {
+			pr_err("Missing device node for CPU %d\n", cpu);
+			continue;
+		}
+
+		/*
+		 * The CPU efficiency value passed from the device tree
+		 * overrides the value defined in the table_efficiency[]
+		 */
+		if (of_property_read_u32(cn, "efficiency", &efficiency) < 0) {
+
+			for (cpu_eff = table_efficiency;
+					cpu_eff->compatible; cpu_eff++)
+
+				if (of_device_is_compatible(cn,
+						cpu_eff->compatible))
+					break;
+
+			if (cpu_eff->compatible == NULL) {
+				pr_warn("%s: Unknown CPU type\n",
+						cn->full_name);
+				continue;
+			}
+
+			efficiency = cpu_eff->efficiency;
+		}
+
+		per_cpu(cpu_efficiency, cpu) = efficiency;
+
+		rate = of_get_property(cn, "clock-frequency", &len);
+		if (!rate || len != 4) {
+			pr_err("%s: Missing clock-frequency property\n",
+				cn->full_name);
+			continue;
+		}
+
+		capacity = ((be32_to_cpup(rate)) >> 20) * efficiency;
+
+		/* Save min capacity of the system */
+		if (capacity < min_capacity)
+			min_capacity = capacity;
+
+		/* Save max capacity of the system */
+		if (capacity > max_capacity)
+			max_capacity = capacity;
+
+		cpu_capacity(cpu) = capacity;
+	}
+
+	/* If min and max capacities are equal we bypass the update of the
+	 * cpu_scale because all CPUs have the same capacity. Otherwise, we
+	 * compute a middle_capacity factor that will ensure that the capacity
+	 * of an 'average' CPU of the system will be as close as possible to
+	 * SCHED_CAPACITY_SCALE, which is the default value, but with the
+	 * constraint explained near table_efficiency[].
+	 */
+	if (min_capacity == max_capacity)
+		return;
+	else if (4 * max_capacity < (3 * (max_capacity + min_capacity)))
+		middle_capacity = (min_capacity + max_capacity)
+				>> (SCHED_CAPACITY_SHIFT+1);
+	else
+		middle_capacity = ((max_capacity / 3)
+				>> (SCHED_CAPACITY_SHIFT-1)) + 1;
+}
+
+/*
+ * Look for a customed capacity of a CPU in the cpu_topo_data table during the
+ * boot. The update of all CPUs is in O(n^2) for heteregeneous system but the
+ * function returns directly for SMP system.
+ */
+static void update_cpu_power(unsigned int cpu)
+{
+	if (!cpu_capacity(cpu))
+		return;
+
+	set_power_scale(cpu, cpu_capacity(cpu) / middle_capacity);
+
+	pr_info("CPU%u: update cpu_power %lu\n",
+		cpu, arch_scale_freq_power(NULL, cpu));
+}
+
 /*
  * cpu topology table
  */
@@ -272,6 +437,7 @@ void store_cpu_topology(unsigned int cpuid)
 
 topology_populated:
 	update_siblings_masks(cpuid);
+	update_cpu_power(cpuid);
 }
 
 static void __init reset_cpu_topology(void)
@@ -292,14 +458,31 @@ static void __init reset_cpu_topology(void)
 	}
 }
 
+static void __init reset_cpu_power(void)
+{
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu)
+		set_power_scale(cpu, SCHED_CAPACITY_SCALE);
+}
+
 void __init init_cpu_topology(void)
 {
+	int cpu;
+
 	reset_cpu_topology();
 
 	/*
 	 * Discard anything that was parsed if we hit an error so we
 	 * don't use partial information.
 	 */
-	if (of_have_populated_dt() && parse_dt_topology())
+	if (of_have_populated_dt() && parse_dt_topology()) {
 		reset_cpu_topology();
+	} else {
+		for_each_possible_cpu(cpu)
+			update_siblings_masks(cpu);
+	}
+
+	reset_cpu_power();
+	parse_dt_cpu_power();
 }
diff --git a/arch/arm64/mm/cache.S b/arch/arm64/mm/cache.S
index e9a7b3a..db00fc9 100644
--- a/arch/arm64/mm/cache.S
+++ b/arch/arm64/mm/cache.S
@@ -25,6 +25,79 @@
 #include <asm/alternative.h>
 
 /*
+ *	__flush_dcache_all()
+ *
+ *	Flush the whole D-cache.
+ *
+ *	Corrupted registers: x0-x7, x9-x11
+ */
+__flush_dcache_all:
+	dmb	sy				// ensure ordering with previous memory accesses
+	mrs	x0, clidr_el1			// read clidr
+	and	x3, x0, #0x7000000		// extract loc from clidr
+	lsr	x3, x3, #23			// left align loc bit field
+	cbz	x3, finished			// if loc is 0, then no need to clean
+	mov	x10, #0				// start clean at cache level 0
+loop1:
+	add	x2, x10, x10, lsr #1		// work out 3x current cache level
+	lsr	x1, x0, x2			// extract cache type bits from clidr
+	and	x1, x1, #7			// mask of the bits for current cache only
+	cmp	x1, #2				// see what cache we have at this level
+	b.lt	skip				// skip if no cache, or just i-cache
+	save_and_disable_irqs x9		// make CSSELR and CCSIDR access atomic
+	msr	csselr_el1, x10			// select current cache level in csselr
+	isb					// isb to sych the new cssr&csidr
+	mrs	x1, ccsidr_el1			// read the new ccsidr
+	restore_irqs x9
+	and	x2, x1, #7			// extract the length of the cache lines
+	add	x2, x2, #4			// add 4 (line length offset)
+	mov	x4, #0x3ff
+	and	x4, x4, x1, lsr #3		// find maximum number on the way size
+	clz	w5, w4				// find bit position of way size increment
+	mov	x7, #0x7fff
+	and	x7, x7, x1, lsr #13		// extract max number of the index size
+loop2:
+	mov	x9, x4				// create working copy of max way size
+loop3:
+	lsl	x6, x9, x5
+	orr	x11, x10, x6			// factor way and cache number into x11
+	lsl	x6, x7, x2
+	orr	x11, x11, x6			// factor index number into x11
+	dc	cisw, x11			// clean & invalidate by set/way
+	subs	x9, x9, #1			// decrement the way
+	b.ge	loop3
+	subs	x7, x7, #1			// decrement the index
+	b.ge	loop2
+skip:
+	add	x10, x10, #2			// increment cache number
+	cmp	x3, x10
+	b.gt	loop1
+finished:
+	mov	x10, #0				// swith back to cache level 0
+	msr	csselr_el1, x10			// select current cache level in csselr
+	dsb	sy
+	isb
+	ret
+ENDPROC(__flush_dcache_all)
+
+/*
+ *	flush_cache_all()
+ *
+ *	Flush the entire cache system.  The data cache flush is now achieved
+ *	using atomic clean / invalidates working outwards from L1 cache. This
+ *	is done using Set/Way based cache maintenance instructions.  The
+ *	instruction cache can still be invalidated back to the point of
+ *	unification in a single instruction.
+ */
+ENTRY(flush_cache_all)
+	mov	x12, lr
+	bl	__flush_dcache_all
+	mov	x0, #0
+	ic	ialluis				// I+BTB cache invalidate
+	ret	x12
+ENDPROC(flush_cache_all)
+
+/*
  *	flush_icache_range(start,end)
  *
  *	Ensure that the I and D caches are coherent within specified region.
diff --git a/arch/arm64/mm/flush.c b/arch/arm64/mm/flush.c
index 8377329..024db34 100644
--- a/arch/arm64/mm/flush.c
+++ b/arch/arm64/mm/flush.c
@@ -91,4 +91,5 @@ EXPORT_SYMBOL(flush_dcache_page);
 /*
  * Additional functions defined in assembly.
  */
+EXPORT_SYMBOL(flush_cache_all);
 EXPORT_SYMBOL(flush_icache_range);
diff --git a/arch/arm64/mm/proc.S b/arch/arm64/mm/proc.S
index c2adb0c..61330c9 100644
--- a/arch/arm64/mm/proc.S
+++ b/arch/arm64/mm/proc.S
@@ -44,6 +44,52 @@
 #define MAIR(attr, mt)	((attr) << ((mt) * 8))
 
 /*
+ *	cpu_cache_off()
+ *
+ *	Turn the CPU D-cache off.
+ */
+ENTRY(cpu_cache_off)
+	mrs	x0, sctlr_el1
+	bic	x0, x0, #1 << 2			// clear SCTLR.C
+	msr	sctlr_el1, x0
+	isb
+	ret
+ENDPROC(cpu_cache_off)
+
+/*
+ *	cpu_reset(loc)
+ *
+ *	Perform a soft reset of the system.  Put the CPU into the same state
+ *	as it would be if it had been reset, and branch to what would be the
+ *	reset vector. It must be executed with the flat identity mapping.
+ *
+ *	- loc   - location to jump to for soft reset
+ */
+	.align	5
+ENTRY(cpu_reset)
+	mrs	x1, sctlr_el1
+	bic	x1, x1, #1
+	msr	sctlr_el1, x1			// disable the MMU
+	isb
+	ret	x0
+ENDPROC(cpu_reset)
+
+ENTRY(cpu_soft_restart)
+	/* Save address of cpu_reset() and reset address */
+	mov	x19, x0
+	mov	x20, x1
+
+	/* Turn D-cache off */
+	bl	cpu_cache_off
+
+	/* Push out all dirty data, and ensure cache is empty */
+	bl	flush_cache_all
+
+	mov	x0, x20
+	ret	x19
+ENDPROC(cpu_soft_restart)
+
+/*
  *	cpu_do_idle()
  *
  *	Idle the processor (wait for interrupt).
diff --git a/drivers/Kconfig b/drivers/Kconfig
index cc11302..6266a37 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -54,8 +54,12 @@
 
 source "drivers/i2c/Kconfig"
 
+source "drivers/soundwire/Kconfig"
+
 source "drivers/spi/Kconfig"
 
+source "drivers/slimbus/Kconfig"
+
 source "drivers/spmi/Kconfig"
 
 source "drivers/hsi/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index cf40194..e7ebee4 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -80,6 +80,7 @@
 obj-$(CONFIG_MTD)		+= mtd/
 obj-$(CONFIG_SPI)		+= spi/
 obj-$(CONFIG_SPMI)		+= spmi/
+obj-$(CONFIG_SLIMBUS)		+= slimbus/
 obj-$(CONFIG_HSI)		+= hsi/
 obj-y				+= net/
 obj-$(CONFIG_ATM)		+= atm/
diff --git a/drivers/base/core.c b/drivers/base/core.c
index ce057a5..fb9796d 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -72,6 +72,11 @@ int lock_device_hotplug_sysfs(void)
 	return restart_syscall();
 }
 
+void lock_device_hotplug_assert(void)
+{
+	lockdep_assert_held(&device_hotplug_lock);
+}
+
 #ifdef CONFIG_BLOCK
 static inline int device_is_not_partition(struct device *dev)
 {
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 08f512b..d82ce17 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -180,6 +180,58 @@ static struct attribute_group crash_note_cpu_attr_group = {
 };
 #endif
 
+#ifdef CONFIG_HOTPLUG_CPU
+
+static ssize_t show_cpu_isolated(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	ssize_t rc;
+	int cpuid = cpu->dev.id;
+	unsigned int isolated = cpu_isolated(cpuid);
+
+	rc = snprintf(buf, PAGE_SIZE-2, "%d\n", isolated);
+
+	return rc;
+}
+
+static ssize_t __ref store_cpu_isolated(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	int err;
+	int cpuid = cpu->dev.id;
+	unsigned int isolated;
+
+	err = kstrtouint(strstrip((char *)buf), 0, &isolated);
+	if (err)
+		return err;
+
+	if (isolated > 1)
+		return -EINVAL;
+
+	if (isolated)
+		sched_isolate_cpu(cpuid);
+	else
+		sched_unisolate_cpu(cpuid);
+
+	return count;
+}
+
+static DEVICE_ATTR(isolate, 0644, show_cpu_isolated, store_cpu_isolated);
+
+static struct attribute *cpu_isolated_attrs[] = {
+	&dev_attr_isolate.attr,
+	NULL
+};
+
+static struct attribute_group cpu_isolated_attr_group = {
+	.attrs = cpu_isolated_attrs,
+};
+
+#endif
+
 #ifdef CONFIG_SCHED_HMP
 
 static ssize_t show_sched_static_cpu_pwr_cost(struct device *dev,
@@ -254,16 +306,56 @@ static ssize_t __ref store_sched_static_cluster_pwr_cost(struct device *dev,
 	return err;
 }
 
+static ssize_t show_sched_cluser_wake_idle(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	ssize_t rc;
+	int cpuid = cpu->dev.id;
+	unsigned int wake_up_idle;
+
+	wake_up_idle = sched_get_cluster_wake_idle(cpuid);
+
+	rc = scnprintf(buf, PAGE_SIZE-2, "%d\n", wake_up_idle);
+
+	return rc;
+}
+
+static ssize_t __ref store_sched_cluster_wake_idle(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct cpu *cpu = container_of(dev, struct cpu, dev);
+	int err;
+	int cpuid = cpu->dev.id;
+	unsigned int wake_up_idle;
+
+	err = kstrtouint(strstrip((char *)buf), 0, &wake_up_idle);
+	if (err)
+		return err;
+
+	err = sched_set_cluster_wake_idle(cpuid, wake_up_idle);
+
+	if (err >= 0)
+		err = count;
+
+	return err;
+}
+
 static DEVICE_ATTR(sched_static_cpu_pwr_cost, 0644,
 					show_sched_static_cpu_pwr_cost,
 					store_sched_static_cpu_pwr_cost);
 static DEVICE_ATTR(sched_static_cluster_pwr_cost, 0644,
 					show_sched_static_cluster_pwr_cost,
 					store_sched_static_cluster_pwr_cost);
+static DEVICE_ATTR(sched_cluster_wake_up_idle, 0644,
+					show_sched_cluser_wake_idle,
+					store_sched_cluster_wake_idle);
 
 static struct attribute *hmp_sched_cpu_attrs[] = {
 	&dev_attr_sched_static_cpu_pwr_cost.attr,
 	&dev_attr_sched_static_cluster_pwr_cost.attr,
+	&dev_attr_sched_cluster_wake_up_idle.attr,
 	NULL
 };
 
@@ -279,6 +371,9 @@ static const struct attribute_group *common_cpu_attr_groups[] = {
 #ifdef CONFIG_SCHED_HMP
 	&sched_hmp_cpu_attr_group,
 #endif
+#ifdef CONFIG_HOTPLUG_CPU
+	&cpu_isolated_attr_group,
+#endif
 	NULL
 };
 
@@ -289,6 +384,9 @@ static const struct attribute_group *hotplugable_cpu_attr_groups[] = {
 #ifdef CONFIG_SCHED_HMP
 	&sched_hmp_cpu_attr_group,
 #endif
+#ifdef CONFIG_HOTPLUG_CPU
+	&cpu_isolated_attr_group,
+#endif
 	NULL
 };
 
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 3cc9bff..70ed8d9 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -354,4 +354,33 @@
 	  Say Y here to compile support for HCI over Qualcomm SMD into the
 	  kernel or say M to compile as a module.
 
+config MSM_BT_POWER
+	bool "MSM Bluetooth Power Control"
+	depends on ARCH_QCOM && RFKILL
+	help
+	  Provides a parameter to switch on/off power from PMIC
+	  to Bluetooth device.
+
+config BTFM_SLIM
+	bool "MSM Bluetooth/FM Slimbus Driver"
+	select SLIMBUS
+	default MSM_BT_POWER
+	help
+	  This enables BT/FM slimbus driver to get multiple audio channel.
+	  This will make use of slimbus platform driver and slimbus codec
+	  driver to communicate with slimbus machine driver and LPSS which
+	  is Slimbus master.
+
+	  Slimbus slave initialization and configuration will be done through
+	  this driver.
+
+config BTFM_SLIM_WCN3990
+	bool "MSM Bluetooth/FM WCN3990 Device"
+	default BTFM_SLIM
+	depends on BTFM_SLIM
+	help
+	  This enables specific driver handle for WCN3990 device.
+	  It is designed to adapt any future BT/FM device to implement a specific
+	  chip initialization process and control.
+
 endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index b1fc29a..122b184 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -25,6 +25,12 @@
 obj-$(CONFIG_BT_RTL)		+= btrtl.o
 obj-$(CONFIG_BT_QCA)		+= btqca.o
 
+obj-$(CONFIG_MSM_BT_POWER)	+= bluetooth-power.o
+
+obj-$(CONFIG_BTFM_SLIM)		+= btfm_slim.o
+obj-$(CONFIG_BTFM_SLIM)		+= btfm_slim_codec.o
+obj-$(CONFIG_BTFM_SLIM_WCN3990)	+= btfm_slim_wcn3990.o
+
 btmrvl-y			:= btmrvl_main.o
 btmrvl-$(CONFIG_DEBUG_FS)	+= btmrvl_debugfs.o
 
diff --git a/drivers/bluetooth/bluetooth-power.c b/drivers/bluetooth/bluetooth-power.c
new file mode 100644
index 0000000..bfc3648
--- /dev/null
+++ b/drivers/bluetooth/bluetooth-power.c
@@ -0,0 +1,776 @@
+/* Copyright (c) 2009-2010, 2013-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Bluetooth Power Switch Module
+ * controls power to external Bluetooth device
+ * with interface to power management device
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/rfkill.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/bluetooth-power.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+
+#if defined(CONFIG_CNSS)
+#include <net/cnss.h>
+#endif
+
+#include "btfm_slim.h"
+#include <linux/fs.h>
+
+#define BT_PWR_DBG(fmt, arg...)  pr_debug("%s: " fmt "\n", __func__, ## arg)
+#define BT_PWR_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg)
+#define BT_PWR_ERR(fmt, arg...)  pr_err("%s: " fmt "\n", __func__, ## arg)
+
+
+static const struct of_device_id bt_power_match_table[] = {
+	{	.compatible = "qca,ar3002" },
+	{	.compatible = "qca,qca6174" },
+	{	.compatible = "qca,wcn3990" },
+	{}
+};
+
+static struct bluetooth_power_platform_data *bt_power_pdata;
+static struct platform_device *btpdev;
+static bool previous;
+static int pwr_state;
+struct class *bt_class;
+static int bt_major;
+
+static int bt_vreg_init(struct bt_power_vreg_data *vreg)
+{
+	int rc = 0;
+	struct device *dev = &btpdev->dev;
+
+	BT_PWR_DBG("vreg_get for : %s", vreg->name);
+
+	/* Get the regulator handle */
+	vreg->reg = regulator_get(dev, vreg->name);
+	if (IS_ERR(vreg->reg)) {
+		rc = PTR_ERR(vreg->reg);
+		pr_err("%s: regulator_get(%s) failed. rc=%d\n",
+			__func__, vreg->name, rc);
+		goto out;
+	}
+
+	if ((regulator_count_voltages(vreg->reg) > 0)
+			&& (vreg->low_vol_level) && (vreg->high_vol_level))
+		vreg->set_voltage_sup = 1;
+
+out:
+	return rc;
+}
+
+static int bt_vreg_enable(struct bt_power_vreg_data *vreg)
+{
+	int rc = 0;
+
+	BT_PWR_DBG("vreg_en for : %s", vreg->name);
+
+	if (!vreg->is_enabled) {
+		if (vreg->set_voltage_sup) {
+			rc = regulator_set_voltage(vreg->reg,
+						vreg->low_vol_level,
+						vreg->high_vol_level);
+			if (rc < 0) {
+				BT_PWR_ERR("vreg_set_vol(%s) failed rc=%d\n",
+						vreg->name, rc);
+				goto out;
+			}
+		}
+
+		if (vreg->load_uA >= 0) {
+			rc = regulator_set_load(vreg->reg,
+					vreg->load_uA);
+			if (rc < 0) {
+				BT_PWR_ERR("vreg_set_mode(%s) failed rc=%d\n",
+						vreg->name, rc);
+				goto out;
+			}
+		}
+
+		rc = regulator_enable(vreg->reg);
+		if (rc < 0) {
+			BT_PWR_ERR("regulator_enable(%s) failed. rc=%d\n",
+					vreg->name, rc);
+			goto out;
+		}
+		vreg->is_enabled = true;
+	}
+out:
+	return rc;
+}
+
+static int bt_vreg_disable(struct bt_power_vreg_data *vreg)
+{
+	int rc = 0;
+
+	if (!vreg)
+		return rc;
+
+	BT_PWR_DBG("vreg_disable for : %s", vreg->name);
+
+	if (vreg->is_enabled) {
+		rc = regulator_disable(vreg->reg);
+		if (rc < 0) {
+			BT_PWR_ERR("regulator_disable(%s) failed. rc=%d\n",
+					vreg->name, rc);
+			goto out;
+		}
+		vreg->is_enabled = false;
+
+		if (vreg->set_voltage_sup) {
+			/* Set the min voltage to 0 */
+			rc = regulator_set_voltage(vreg->reg, 0,
+					vreg->high_vol_level);
+			if (rc < 0) {
+				BT_PWR_ERR("vreg_set_vol(%s) failed rc=%d\n",
+						vreg->name, rc);
+				goto out;
+			}
+		}
+		if (vreg->load_uA >= 0) {
+			rc = regulator_set_load(vreg->reg, 0);
+			if (rc < 0) {
+				BT_PWR_ERR("vreg_set_mode(%s) failed rc=%d\n",
+						vreg->name, rc);
+			}
+		}
+	}
+out:
+	return rc;
+}
+
+static int bt_configure_vreg(struct bt_power_vreg_data *vreg)
+{
+	int rc = 0;
+
+	BT_PWR_DBG("config %s", vreg->name);
+
+	/* Get the regulator handle for vreg */
+	if (!(vreg->reg)) {
+		rc = bt_vreg_init(vreg);
+		if (rc < 0)
+			return rc;
+	}
+	rc = bt_vreg_enable(vreg);
+
+	return rc;
+}
+
+static int bt_clk_enable(struct bt_power_clk_data *clk)
+{
+	int rc = 0;
+
+	BT_PWR_DBG("%s", clk->name);
+
+	/* Get the clock handle for vreg */
+	if (!clk->clk || clk->is_enabled) {
+		BT_PWR_ERR("error - node: %p, clk->is_enabled:%d",
+			clk->clk, clk->is_enabled);
+		return -EINVAL;
+	}
+
+	rc = clk_prepare_enable(clk->clk);
+	if (rc) {
+		BT_PWR_ERR("failed to enable %s, rc(%d)\n", clk->name, rc);
+		return rc;
+	}
+
+	clk->is_enabled = true;
+	return rc;
+}
+
+static int bt_clk_disable(struct bt_power_clk_data *clk)
+{
+	int rc = 0;
+
+	BT_PWR_DBG("%s", clk->name);
+
+	/* Get the clock handle for vreg */
+	if (!clk->clk || !clk->is_enabled) {
+		BT_PWR_ERR("error - node: %p, clk->is_enabled:%d",
+			clk->clk, clk->is_enabled);
+		return -EINVAL;
+	}
+	clk_disable_unprepare(clk->clk);
+
+	clk->is_enabled = false;
+	return rc;
+}
+
+static int bt_configure_gpios(int on)
+{
+	int rc = 0;
+	int bt_reset_gpio = bt_power_pdata->bt_gpio_sys_rst;
+
+	BT_PWR_DBG("bt_gpio= %d on: %d", bt_reset_gpio, on);
+
+	if (on) {
+		rc = gpio_request(bt_reset_gpio, "bt_sys_rst_n");
+		if (rc) {
+			BT_PWR_ERR("unable to request gpio %d (%d)\n",
+					bt_reset_gpio, rc);
+			return rc;
+		}
+
+		rc = gpio_direction_output(bt_reset_gpio, 0);
+		if (rc) {
+			BT_PWR_ERR("Unable to set direction\n");
+			return rc;
+		}
+		msleep(50);
+		rc = gpio_direction_output(bt_reset_gpio, 1);
+		if (rc) {
+			BT_PWR_ERR("Unable to set direction\n");
+			return rc;
+		}
+		msleep(50);
+	} else {
+		gpio_set_value(bt_reset_gpio, 0);
+		msleep(100);
+	}
+	return rc;
+}
+
+static int bluetooth_power(int on)
+{
+	int rc = 0;
+
+	BT_PWR_DBG("on: %d", on);
+
+	if (on) {
+		if (bt_power_pdata->bt_vdd_io) {
+			rc = bt_configure_vreg(bt_power_pdata->bt_vdd_io);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power vddio config failed");
+				goto out;
+			}
+		}
+		if (bt_power_pdata->bt_vdd_xtal) {
+			rc = bt_configure_vreg(bt_power_pdata->bt_vdd_xtal);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power vddxtal config failed");
+				goto vdd_xtal_fail;
+			}
+		}
+		if (bt_power_pdata->bt_vdd_core) {
+			rc = bt_configure_vreg(bt_power_pdata->bt_vdd_core);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power vddcore config failed");
+				goto vdd_core_fail;
+			}
+		}
+		if (bt_power_pdata->bt_vdd_pa) {
+			rc = bt_configure_vreg(bt_power_pdata->bt_vdd_pa);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power vddpa config failed");
+				goto vdd_pa_fail;
+			}
+		}
+		if (bt_power_pdata->bt_vdd_ldo) {
+			rc = bt_configure_vreg(bt_power_pdata->bt_vdd_ldo);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power vddldo config failed");
+				goto vdd_ldo_fail;
+			}
+		}
+		if (bt_power_pdata->bt_chip_pwd) {
+			rc = bt_configure_vreg(bt_power_pdata->bt_chip_pwd);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power chippwd config failed");
+				goto chip_pwd_fail;
+			}
+		}
+		/* Parse dt_info and check if a target requires clock voting.
+		 * Enable BT clock when BT is on and disable it when BT is off
+		 */
+		if (bt_power_pdata->bt_chip_clk) {
+			rc = bt_clk_enable(bt_power_pdata->bt_chip_clk);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power gpio config failed");
+				goto clk_fail;
+			}
+		}
+		if (bt_power_pdata->bt_gpio_sys_rst > 0) {
+			rc = bt_configure_gpios(on);
+			if (rc < 0) {
+				BT_PWR_ERR("bt_power gpio config failed");
+				goto gpio_fail;
+			}
+		}
+	} else {
+		if (bt_power_pdata->bt_gpio_sys_rst > 0)
+			bt_configure_gpios(on);
+gpio_fail:
+		if (bt_power_pdata->bt_gpio_sys_rst > 0)
+			gpio_free(bt_power_pdata->bt_gpio_sys_rst);
+		if (bt_power_pdata->bt_chip_clk)
+			bt_clk_disable(bt_power_pdata->bt_chip_clk);
+clk_fail:
+		if (bt_power_pdata->bt_chip_pwd)
+			bt_vreg_disable(bt_power_pdata->bt_chip_pwd);
+chip_pwd_fail:
+		if (bt_power_pdata->bt_vdd_ldo)
+			bt_vreg_disable(bt_power_pdata->bt_vdd_ldo);
+vdd_ldo_fail:
+		if (bt_power_pdata->bt_vdd_pa)
+			bt_vreg_disable(bt_power_pdata->bt_vdd_pa);
+vdd_pa_fail:
+		if (bt_power_pdata->bt_vdd_core)
+			bt_vreg_disable(bt_power_pdata->bt_vdd_core);
+vdd_core_fail:
+		if (bt_power_pdata->bt_vdd_xtal)
+			bt_vreg_disable(bt_power_pdata->bt_vdd_xtal);
+vdd_xtal_fail:
+		if (bt_power_pdata->bt_vdd_io)
+			bt_vreg_disable(bt_power_pdata->bt_vdd_io);
+	}
+out:
+	return rc;
+}
+
+static int bluetooth_toggle_radio(void *data, bool blocked)
+{
+	int ret = 0;
+	int (*power_control)(int enable);
+
+	power_control =
+		((struct bluetooth_power_platform_data *)data)->bt_power_setup;
+
+	if (previous != blocked)
+		ret = (*power_control)(!blocked);
+	if (!ret)
+		previous = blocked;
+	return ret;
+}
+
+static const struct rfkill_ops bluetooth_power_rfkill_ops = {
+	.set_block = bluetooth_toggle_radio,
+};
+
+#if defined(CONFIG_CNSS) && defined(CONFIG_CLD_LL_CORE)
+static ssize_t enable_extldo(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	int ret;
+	bool enable = false;
+	struct cnss_platform_cap cap;
+
+	ret = cnss_get_platform_cap(&cap);
+	if (ret) {
+		BT_PWR_ERR("Platform capability info from CNSS not available!");
+		enable = false;
+	} else if (!ret && (cap.cap_flag & CNSS_HAS_EXTERNAL_SWREG)) {
+		enable = true;
+	}
+	return snprintf(buf, 6, "%s", (enable ? "true" : "false"));
+}
+#else
+static ssize_t enable_extldo(struct device *dev, struct device_attribute *attr,
+			char *buf)
+{
+	return snprintf(buf, 6, "%s", "false");
+}
+#endif
+
+static DEVICE_ATTR(extldo, 0444, enable_extldo, NULL);
+
+static int bluetooth_power_rfkill_probe(struct platform_device *pdev)
+{
+	struct rfkill *rfkill;
+	int ret;
+
+	rfkill = rfkill_alloc("bt_power", &pdev->dev, RFKILL_TYPE_BLUETOOTH,
+			      &bluetooth_power_rfkill_ops,
+			      pdev->dev.platform_data);
+
+	if (!rfkill) {
+		dev_err(&pdev->dev, "rfkill allocate failed\n");
+		return -ENOMEM;
+	}
+
+	/* add file into rfkill0 to handle LDO27 */
+	ret = device_create_file(&pdev->dev, &dev_attr_extldo);
+	if (ret < 0)
+		BT_PWR_ERR("device create file error!");
+
+	/* force Bluetooth off during init to allow for user control */
+	rfkill_init_sw_state(rfkill, 1);
+	previous = 1;
+
+	ret = rfkill_register(rfkill);
+	if (ret) {
+		dev_err(&pdev->dev, "rfkill register failed=%d\n", ret);
+		rfkill_destroy(rfkill);
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, rfkill);
+
+	return 0;
+}
+
+static void bluetooth_power_rfkill_remove(struct platform_device *pdev)
+{
+	struct rfkill *rfkill;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	rfkill = platform_get_drvdata(pdev);
+	if (rfkill)
+		rfkill_unregister(rfkill);
+	rfkill_destroy(rfkill);
+	platform_set_drvdata(pdev, NULL);
+}
+
+#define MAX_PROP_SIZE 32
+static int bt_dt_parse_vreg_info(struct device *dev,
+		struct bt_power_vreg_data **vreg_data, const char *vreg_name)
+{
+	int len, ret = 0;
+	const __be32 *prop;
+	char prop_name[MAX_PROP_SIZE];
+	struct bt_power_vreg_data *vreg;
+	struct device_node *np = dev->of_node;
+
+	BT_PWR_DBG("vreg dev tree parse for %s", vreg_name);
+
+	*vreg_data = NULL;
+	snprintf(prop_name, MAX_PROP_SIZE, "%s-supply", vreg_name);
+	if (of_parse_phandle(np, prop_name, 0)) {
+		vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+		if (!vreg) {
+			dev_err(dev, "No memory for vreg: %s\n", vreg_name);
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		vreg->name = vreg_name;
+
+		/* Parse voltage-level from each node */
+		snprintf(prop_name, MAX_PROP_SIZE,
+				"%s-voltage-level", vreg_name);
+		prop = of_get_property(np, prop_name, &len);
+		if (!prop || (len != (2 * sizeof(__be32)))) {
+			dev_warn(dev, "%s %s property\n",
+				prop ? "invalid format" : "no", prop_name);
+		} else {
+			vreg->low_vol_level = be32_to_cpup(&prop[0]);
+			vreg->high_vol_level = be32_to_cpup(&prop[1]);
+		}
+
+		/* Parse current-level from each node */
+		snprintf(prop_name, MAX_PROP_SIZE,
+				"%s-current-level", vreg_name);
+		ret = of_property_read_u32(np, prop_name, &vreg->load_uA);
+		if (ret < 0) {
+			BT_PWR_DBG("%s property is not valid\n", prop_name);
+			vreg->load_uA = -1;
+			ret = 0;
+		}
+
+		*vreg_data = vreg;
+		BT_PWR_DBG("%s: vol=[%d %d]uV, current=[%d]uA\n",
+			vreg->name, vreg->low_vol_level,
+			vreg->high_vol_level,
+			vreg->load_uA);
+	} else
+		BT_PWR_INFO("%s: is not provided in device tree", vreg_name);
+
+err:
+	return ret;
+}
+
+static int bt_dt_parse_clk_info(struct device *dev,
+		struct bt_power_clk_data **clk_data)
+{
+	int ret = -EINVAL;
+	struct bt_power_clk_data *clk = NULL;
+	struct device_node *np = dev->of_node;
+
+	BT_PWR_DBG("");
+
+	*clk_data = NULL;
+	if (of_parse_phandle(np, "clocks", 0)) {
+		clk = devm_kzalloc(dev, sizeof(*clk), GFP_KERNEL);
+		if (!clk) {
+			BT_PWR_ERR("No memory for clocks");
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		/* Allocated 20 bytes size buffer for clock name string */
+		clk->name = devm_kzalloc(dev, 20, GFP_KERNEL);
+
+		/* Parse clock name from node */
+		ret = of_property_read_string_index(np, "clock-names", 0,
+				&(clk->name));
+		if (ret < 0) {
+			BT_PWR_ERR("reading \"clock-names\" failed");
+			return ret;
+		}
+
+		clk->clk = devm_clk_get(dev, clk->name);
+		if (IS_ERR(clk->clk)) {
+			ret = PTR_ERR(clk->clk);
+			BT_PWR_ERR("failed to get %s, ret (%d)",
+				clk->name, ret);
+			clk->clk = NULL;
+			return ret;
+		}
+
+		*clk_data = clk;
+	} else {
+		BT_PWR_ERR("clocks is not provided in device tree");
+	}
+
+err:
+	return ret;
+}
+
+static int bt_power_populate_dt_pinfo(struct platform_device *pdev)
+{
+	int rc;
+
+	BT_PWR_DBG("");
+
+	if (!bt_power_pdata)
+		return -ENOMEM;
+
+	if (pdev->dev.of_node) {
+		bt_power_pdata->bt_gpio_sys_rst =
+			of_get_named_gpio(pdev->dev.of_node,
+						"qca,bt-reset-gpio", 0);
+		if (bt_power_pdata->bt_gpio_sys_rst < 0)
+			BT_PWR_ERR("bt-reset-gpio not provided in device tree");
+
+		rc = bt_dt_parse_vreg_info(&pdev->dev,
+					&bt_power_pdata->bt_vdd_core,
+					"qca,bt-vdd-core");
+		if (rc < 0)
+			BT_PWR_ERR("bt-vdd-core not provided in device tree");
+
+		rc = bt_dt_parse_vreg_info(&pdev->dev,
+					&bt_power_pdata->bt_vdd_io,
+					"qca,bt-vdd-io");
+		if (rc < 0)
+			BT_PWR_ERR("bt-vdd-io not provided in device tree");
+
+		rc = bt_dt_parse_vreg_info(&pdev->dev,
+					&bt_power_pdata->bt_vdd_xtal,
+					"qca,bt-vdd-xtal");
+		if (rc < 0)
+			BT_PWR_ERR("bt-vdd-xtal not provided in device tree");
+
+		rc = bt_dt_parse_vreg_info(&pdev->dev,
+					&bt_power_pdata->bt_vdd_pa,
+					"qca,bt-vdd-pa");
+		if (rc < 0)
+			BT_PWR_ERR("bt-vdd-pa not provided in device tree");
+
+		rc = bt_dt_parse_vreg_info(&pdev->dev,
+					&bt_power_pdata->bt_vdd_ldo,
+					"qca,bt-vdd-ldo");
+		if (rc < 0)
+			BT_PWR_ERR("bt-vdd-ldo not provided in device tree");
+
+		rc = bt_dt_parse_vreg_info(&pdev->dev,
+					&bt_power_pdata->bt_chip_pwd,
+					"qca,bt-chip-pwd");
+		if (rc < 0)
+			BT_PWR_ERR("bt-chip-pwd not provided in device tree");
+
+		rc = bt_dt_parse_clk_info(&pdev->dev,
+					&bt_power_pdata->bt_chip_clk);
+		if (rc < 0)
+			BT_PWR_ERR("clock not provided in device tree");
+	}
+
+	bt_power_pdata->bt_power_setup = bluetooth_power;
+
+	return 0;
+}
+
+static int bt_power_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	bt_power_pdata =
+		kzalloc(sizeof(struct bluetooth_power_platform_data),
+			GFP_KERNEL);
+
+	if (!bt_power_pdata) {
+		BT_PWR_ERR("Failed to allocate memory");
+		return -ENOMEM;
+	}
+
+	if (pdev->dev.of_node) {
+		ret = bt_power_populate_dt_pinfo(pdev);
+		if (ret < 0) {
+			BT_PWR_ERR("Failed to populate device tree info");
+			goto free_pdata;
+		}
+		pdev->dev.platform_data = bt_power_pdata;
+	} else if (pdev->dev.platform_data) {
+		/* Optional data set to default if not provided */
+		if (!((struct bluetooth_power_platform_data *)
+			(pdev->dev.platform_data))->bt_power_setup)
+			((struct bluetooth_power_platform_data *)
+				(pdev->dev.platform_data))->bt_power_setup =
+						bluetooth_power;
+
+		memcpy(bt_power_pdata, pdev->dev.platform_data,
+			sizeof(struct bluetooth_power_platform_data));
+		pwr_state = 0;
+	} else {
+		BT_PWR_ERR("Failed to get platform data");
+		goto free_pdata;
+	}
+
+	if (bluetooth_power_rfkill_probe(pdev) < 0)
+		goto free_pdata;
+
+	btpdev = pdev;
+
+	return 0;
+
+free_pdata:
+	kfree(bt_power_pdata);
+	return ret;
+}
+
+static int bt_power_remove(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "%s\n", __func__);
+
+	bluetooth_power_rfkill_remove(pdev);
+
+	if (bt_power_pdata->bt_chip_pwd->reg)
+		regulator_put(bt_power_pdata->bt_chip_pwd->reg);
+
+	kfree(bt_power_pdata);
+
+	return 0;
+}
+
+int bt_register_slimdev(struct device *dev)
+{
+	BT_PWR_DBG("");
+	if (!bt_power_pdata || (dev == NULL)) {
+		BT_PWR_ERR("Failed to allocate memory");
+		return -EINVAL;
+	}
+	bt_power_pdata->slim_dev = dev;
+	return 0;
+}
+
+static long bt_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	int ret = 0, pwr_cntrl = 0;
+
+	switch (cmd) {
+	case BT_CMD_SLIM_TEST:
+		if (!bt_power_pdata->slim_dev) {
+			BT_PWR_ERR("slim_dev is null\n");
+			return -EINVAL;
+		}
+		ret = btfm_slim_hw_init(
+			bt_power_pdata->slim_dev->platform_data
+		);
+		break;
+	case BT_CMD_PWR_CTRL:
+		pwr_cntrl = (int)arg;
+		BT_PWR_ERR("BT_CMD_PWR_CTRL pwr_cntrl:%d", pwr_cntrl);
+		if (pwr_state != pwr_cntrl) {
+			ret = bluetooth_power(pwr_cntrl);
+			if (!ret)
+				pwr_state = pwr_cntrl;
+		} else {
+			BT_PWR_ERR("BT chip state is already :%d no change d\n"
+				, pwr_state);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return ret;
+}
+
+static struct platform_driver bt_power_driver = {
+	.probe = bt_power_probe,
+	.remove = bt_power_remove,
+	.driver = {
+		.name = "bt_power",
+		.owner = THIS_MODULE,
+		.of_match_table = bt_power_match_table,
+	},
+};
+
+static const struct file_operations bt_dev_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl = bt_ioctl,
+	.compat_ioctl = bt_ioctl,
+};
+
+static int __init bluetooth_power_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&bt_power_driver);
+
+	bt_major = register_chrdev(0, "bt", &bt_dev_fops);
+	if (bt_major < 0) {
+		BTFMSLIM_ERR("failed to allocate char dev\n");
+		goto chrdev_unreg;
+	}
+
+	bt_class = class_create(THIS_MODULE, "bt-dev");
+	if (IS_ERR(bt_class)) {
+		BTFMSLIM_ERR("coudn't create class");
+		goto chrdev_unreg;
+	}
+
+
+	if (device_create(bt_class, NULL, MKDEV(bt_major, 0),
+		NULL, "btpower") == NULL) {
+		BTFMSLIM_ERR("failed to allocate char dev\n");
+		goto chrdev_unreg;
+	}
+	return 0;
+
+chrdev_unreg:
+	unregister_chrdev(bt_major, "bt");
+	class_destroy(bt_class);
+	return ret;
+}
+
+static void __exit bluetooth_power_exit(void)
+{
+	platform_driver_unregister(&bt_power_driver);
+}
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Bluetooth power control driver");
+
+module_init(bluetooth_power_init);
+module_exit(bluetooth_power_exit);
diff --git a/drivers/bluetooth/btfm_slim.c b/drivers/bluetooth/btfm_slim.c
new file mode 100644
index 0000000..dc9bb0b
--- /dev/null
+++ b/drivers/bluetooth/btfm_slim.c
@@ -0,0 +1,563 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <btfm_slim.h>
+#include <btfm_slim_wcn3990.h>
+#include <linux/bluetooth-power.h>
+
+int btfm_slim_write(struct btfmslim *btfmslim,
+		uint16_t reg, int bytes, void *src, uint8_t pgd)
+{
+	int ret, i;
+	struct slim_ele_access msg;
+	int slim_write_tries = SLIM_SLAVE_RW_MAX_TRIES;
+
+	BTFMSLIM_DBG("Write to %s", pgd?"PGD":"IFD");
+	msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	for ( ; slim_write_tries != 0; slim_write_tries--) {
+		mutex_lock(&btfmslim->xfer_lock);
+		ret = slim_change_val_element(pgd ? btfmslim->slim_pgd :
+			&btfmslim->slim_ifd, &msg, src, bytes);
+		mutex_unlock(&btfmslim->xfer_lock);
+		if (ret == 0)
+			break;
+		usleep_range(5000, 5100);
+	}
+
+	if (ret) {
+		BTFMSLIM_ERR("failed (%d)", ret);
+		return ret;
+	}
+
+	for (i = 0; i < bytes; i++)
+		BTFMSLIM_DBG("Write 0x%02x to reg 0x%x", ((uint8_t *)src)[i],
+			reg + i);
+	return 0;
+}
+
+int btfm_slim_write_pgd(struct btfmslim *btfmslim,
+		uint16_t reg, int bytes, void *src)
+{
+	return btfm_slim_write(btfmslim, reg, bytes, src, PGD);
+}
+
+int btfm_slim_write_inf(struct btfmslim *btfmslim,
+		uint16_t reg, int bytes, void *src)
+{
+	return btfm_slim_write(btfmslim, reg, bytes, src, IFD);
+}
+
+int btfm_slim_read(struct btfmslim *btfmslim, unsigned short reg,
+				int bytes, void *dest, uint8_t pgd)
+{
+	int ret, i;
+	struct slim_ele_access msg;
+	int slim_read_tries = SLIM_SLAVE_RW_MAX_TRIES;
+
+	BTFMSLIM_DBG("Read from %s", pgd?"PGD":"IFD");
+	msg.start_offset = SLIM_SLAVE_REG_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	for ( ; slim_read_tries != 0; slim_read_tries--) {
+		mutex_lock(&btfmslim->xfer_lock);
+		ret = slim_request_val_element(pgd ? btfmslim->slim_pgd :
+			&btfmslim->slim_ifd, &msg, dest, bytes);
+		mutex_unlock(&btfmslim->xfer_lock);
+		if (ret == 0)
+			break;
+		usleep_range(5000, 5100);
+	}
+
+	if (ret)
+		BTFMSLIM_ERR("failed (%d)", ret);
+
+	for (i = 0; i < bytes; i++)
+		BTFMSLIM_DBG("Read 0x%02x from reg 0x%x", ((uint8_t *)dest)[i],
+			reg + i);
+
+	return 0;
+}
+
+int btfm_slim_read_pgd(struct btfmslim *btfmslim,
+		uint16_t reg, int bytes, void *dest)
+{
+	return btfm_slim_read(btfmslim, reg, bytes, dest, PGD);
+}
+
+int btfm_slim_read_inf(struct btfmslim *btfmslim,
+		uint16_t reg, int bytes, void *dest)
+{
+	return btfm_slim_read(btfmslim, reg, bytes, dest, IFD);
+}
+
+int btfm_slim_enable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
+	uint8_t rxport, uint32_t rates, uint8_t grp, uint8_t nchan)
+{
+	int ret, i;
+	struct slim_ch prop;
+	struct btfmslim_ch *chan = ch;
+	uint16_t ch_h[2];
+
+	if (!btfmslim || !ch)
+		return -EINVAL;
+
+	BTFMSLIM_DBG("port:%d", ch->port);
+
+	/* Define the channel with below parameters */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = (rates == 48000) ? SLIM_CH_DATAF_NOT_DEFINED
+			: SLIM_CH_DATAF_LPCM_AUDIO;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rates/4000);
+	prop.sampleszbits = 16;
+
+	ch_h[0] = ch->ch_hdl;
+	ch_h[1] = (grp) ? (ch+1)->ch_hdl : 0;
+
+	ret = slim_define_ch(btfmslim->slim_pgd, &prop, ch_h, nchan, grp,
+			&ch->grph);
+	if (ret < 0) {
+		BTFMSLIM_ERR("slim_define_ch failed ret[%d]", ret);
+		goto error;
+	}
+
+	for (i = 0; i < nchan; i++, ch++) {
+		/* Enable port through registration setting */
+		if (btfmslim->vendor_port_en) {
+			ret = btfmslim->vendor_port_en(btfmslim, ch->port,
+					rxport, 1);
+			if (ret < 0) {
+				BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
+					ret);
+				goto error;
+			}
+		}
+
+		if (rxport) {
+			BTFMSLIM_INFO("slim_connect_sink(port: %d, ch: %d)",
+				ch->port, ch->ch);
+			/* Connect Port with channel given by Machine driver*/
+			ret = slim_connect_sink(btfmslim->slim_pgd,
+				&ch->port_hdl, 1, ch->ch_hdl);
+			if (ret < 0) {
+				BTFMSLIM_ERR("slim_connect_sink failed ret[%d]",
+					ret);
+				goto remove_channel;
+			}
+
+		} else {
+			BTFMSLIM_INFO("slim_connect_src(port: %d, ch: %d)",
+				ch->port, ch->ch);
+			/* Connect Port with channel given by Machine driver*/
+			ret = slim_connect_src(btfmslim->slim_pgd, ch->port_hdl,
+				ch->ch_hdl);
+			if (ret < 0) {
+				BTFMSLIM_ERR("slim_connect_src failed ret[%d]",
+					ret);
+				goto remove_channel;
+			}
+		}
+	}
+
+	/* Activate the channel immediately */
+	BTFMSLIM_INFO(
+		"port: %d, ch: %d, grp: %d, ch->grph: 0x%x, ch_hdl: 0x%x",
+		chan->port, chan->ch, grp, chan->grph, chan->ch_hdl);
+	ret = slim_control_ch(btfmslim->slim_pgd, (grp ? chan->grph :
+		chan->ch_hdl), SLIM_CH_ACTIVATE, true);
+	if (ret < 0) {
+		BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
+		goto remove_channel;
+	}
+
+error:
+	return ret;
+
+remove_channel:
+	/* Remove the channel immediately*/
+	ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
+			SLIM_CH_REMOVE, true);
+	if (ret < 0)
+		BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
+
+	return ret;
+}
+
+int btfm_slim_disable_ch(struct btfmslim *btfmslim, struct btfmslim_ch *ch,
+	uint8_t rxport, uint8_t grp, uint8_t nchan)
+{
+	int ret, i;
+
+	if (!btfmslim || !ch)
+		return -EINVAL;
+
+	BTFMSLIM_INFO("port:%d, grp: %d, ch->grph:0x%x, ch->ch_hdl:0x%x ",
+		ch->port, grp, ch->grph, ch->ch_hdl);
+	/* Remove the channel immediately*/
+	ret = slim_control_ch(btfmslim->slim_pgd, (grp ? ch->grph : ch->ch_hdl),
+			SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		BTFMSLIM_ERR("slim_control_ch failed ret[%d]", ret);
+		ret = slim_disconnect_ports(btfmslim->slim_pgd,
+			&ch->port_hdl, 1);
+		if (ret < 0) {
+			BTFMSLIM_ERR("slim_disconnect_ports failed ret[%d]",
+				ret);
+			goto error;
+		}
+	}
+
+	/* Disable port through registration setting */
+	for (i = 0; i < nchan; i++, ch++) {
+		if (btfmslim->vendor_port_en) {
+			ret = btfmslim->vendor_port_en(btfmslim, ch->port,
+				rxport, 0);
+			if (ret < 0) {
+				BTFMSLIM_ERR("vendor_port_en failed ret[%d]",
+					ret);
+				break;
+			}
+		}
+	}
+error:
+	return ret;
+}
+static int btfm_slim_get_logical_addr(struct slim_device *slim)
+{
+	int ret = 0;
+	const unsigned long timeout = jiffies +
+			      msecs_to_jiffies(SLIM_SLAVE_PRESENT_TIMEOUT);
+
+	do {
+		ret = slim_get_logical_addr(slim, slim->e_addr,
+			ARRAY_SIZE(slim->e_addr), &slim->laddr);
+		if (!ret)  {
+			BTFMSLIM_DBG("Assigned l-addr: 0x%x", slim->laddr);
+			break;
+		}
+		/* Give SLIMBUS time to report present and be ready. */
+		usleep_range(1000, 1100);
+		BTFMSLIM_DBG("retyring get logical addr");
+	} while (time_before(jiffies, timeout));
+
+	return ret;
+}
+
+static int btfm_slim_alloc_port(struct btfmslim *btfmslim)
+{
+	int ret = -EINVAL, i;
+	struct btfmslim_ch *rx_chs;
+	struct btfmslim_ch *tx_chs;
+
+	if (!btfmslim)
+		return ret;
+
+	rx_chs = btfmslim->rx_chs;
+	tx_chs = btfmslim->tx_chs;
+
+	if (!rx_chs || !tx_chs)
+		return ret;
+
+	BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
+	for (i = 0 ; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
+		(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, rx_chs++) {
+
+		/* Get Rx port handler from slimbus driver based
+		 * on port number
+		 */
+		ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
+			rx_chs->port, &rx_chs->port_hdl, SLIM_SINK);
+		if (ret < 0) {
+			BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
+				rx_chs->port, SLIM_SINK);
+			return ret;
+		}
+		BTFMSLIM_DBG("    %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
+			rx_chs->name, rx_chs->port, rx_chs->port_hdl,
+			rx_chs->ch, rx_chs->ch_hdl);
+	}
+
+	BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
+	for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) &&
+		(i < BTFM_SLIM_NUM_CODEC_DAIS); i++, tx_chs++) {
+
+		/* Get Tx port handler from slimbus driver based
+		 * on port number
+		 */
+		ret = slim_get_slaveport(btfmslim->slim_pgd->laddr,
+			tx_chs->port, &tx_chs->port_hdl, SLIM_SRC);
+		if (ret < 0) {
+			BTFMSLIM_ERR("slave port failure port#%d - ret[%d]",
+				tx_chs->port, SLIM_SRC);
+			return ret;
+		}
+		BTFMSLIM_DBG("    %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
+			tx_chs->name, tx_chs->port, tx_chs->port_hdl,
+			tx_chs->ch, tx_chs->ch_hdl);
+	}
+	return ret;
+}
+
+int btfm_slim_hw_init(struct btfmslim *btfmslim)
+{
+	int ret;
+
+	BTFMSLIM_DBG("");
+	if (!btfmslim)
+		return -EINVAL;
+
+	if (btfmslim->enabled) {
+		BTFMSLIM_DBG("Already enabled");
+		return 0;
+	}
+	mutex_lock(&btfmslim->io_lock);
+
+	/* Assign Logical Address for PGD (Ported Generic Device)
+	 * enumeration address
+	 */
+	ret = btfm_slim_get_logical_addr(btfmslim->slim_pgd);
+	if (ret) {
+		BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
+		       btfmslim->slim_pgd->name, ret);
+		goto error;
+	}
+
+	/* Assign Logical Address for Ported Generic Device
+	 * enumeration address
+	 */
+	ret = btfm_slim_get_logical_addr(&btfmslim->slim_ifd);
+	if (ret) {
+		BTFMSLIM_ERR("failed to get slimbus %s logical address: %d",
+		       btfmslim->slim_ifd.name, ret);
+		goto error;
+	}
+
+	/* Allocate ports with logical address to get port handler from
+	 * slimbus driver
+	 */
+	ret = btfm_slim_alloc_port(btfmslim);
+	if (ret)
+		goto error;
+
+	/* Start vendor specific initialization and get port information */
+	if (btfmslim->vendor_init)
+		ret = btfmslim->vendor_init(btfmslim);
+
+	/* Only when all registers read/write successfully, it set to
+	 * enabled status
+	 */
+	btfmslim->enabled = 1;
+error:
+	mutex_unlock(&btfmslim->io_lock);
+	return ret;
+}
+
+
+int btfm_slim_hw_deinit(struct btfmslim *btfmslim)
+{
+	int ret = 0;
+
+	if (!btfmslim)
+		return -EINVAL;
+
+	if (!btfmslim->enabled) {
+		BTFMSLIM_DBG("Already disabled");
+		return 0;
+	}
+	mutex_lock(&btfmslim->io_lock);
+	btfmslim->enabled = 0;
+	mutex_unlock(&btfmslim->io_lock);
+	return ret;
+}
+
+static int btfm_slim_get_dt_info(struct btfmslim *btfmslim)
+{
+	int ret = 0;
+	struct slim_device *slim = btfmslim->slim_pgd;
+	struct slim_device *slim_ifd = &btfmslim->slim_ifd;
+	struct property *prop;
+
+	if (!slim || !slim_ifd)
+		return -EINVAL;
+
+	if (slim->dev.of_node) {
+		BTFMSLIM_DBG("Platform data from device tree (%s)",
+			slim->name);
+		ret = of_property_read_string(slim->dev.of_node,
+			"qcom,btfm-slim-ifd", &slim_ifd->name);
+		if (ret) {
+			BTFMSLIM_ERR("Looking up %s property in node %s failed",
+				"qcom,btfm-slim-ifd",
+				 slim->dev.of_node->full_name);
+			return -ENODEV;
+		}
+		BTFMSLIM_DBG("qcom,btfm-slim-ifd (%s)", slim_ifd->name);
+
+		prop = of_find_property(slim->dev.of_node,
+				"qcom,btfm-slim-ifd-elemental-addr", NULL);
+		if (!prop) {
+			BTFMSLIM_ERR("Looking up %s property in node %s failed",
+				"qcom,btfm-slim-ifd-elemental-addr",
+				slim->dev.of_node->full_name);
+			return -ENODEV;
+		} else if (prop->length != 6) {
+			BTFMSLIM_ERR(
+				"invalid codec slim ifd addr. addr length= %d",
+				prop->length);
+			return -ENODEV;
+		}
+		memcpy(slim_ifd->e_addr, prop->value, 6);
+		BTFMSLIM_DBG(
+			"PGD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
+			slim->e_addr[0], slim->e_addr[1], slim->e_addr[2],
+			slim->e_addr[3], slim->e_addr[4], slim->e_addr[5]);
+		BTFMSLIM_DBG(
+			"IFD Enum Addr: %.02x:%.02x:%.02x:%.02x:%.02x: %.02x",
+			slim_ifd->e_addr[0], slim_ifd->e_addr[1],
+			slim_ifd->e_addr[2], slim_ifd->e_addr[3],
+			slim_ifd->e_addr[4], slim_ifd->e_addr[5]);
+	} else {
+		BTFMSLIM_ERR("Platform data is not valid");
+	}
+
+	return ret;
+}
+
+static int btfm_slim_probe(struct slim_device *slim)
+{
+	int ret = 0;
+	struct btfmslim *btfm_slim;
+
+	BTFMSLIM_DBG("");
+	if (!slim->ctrl)
+		return -EINVAL;
+
+	/* Allocation btfmslim data pointer */
+	btfm_slim = kzalloc(sizeof(struct btfmslim), GFP_KERNEL);
+	if (btfm_slim == NULL) {
+		BTFMSLIM_ERR("error, allocation failed");
+		return -ENOMEM;
+	}
+	/* BTFM Slimbus driver control data configuration */
+	btfm_slim->slim_pgd = slim;
+
+	/* Assign vendor specific function */
+	btfm_slim->rx_chs = SLIM_SLAVE_RXPORT;
+	btfm_slim->tx_chs = SLIM_SLAVE_TXPORT;
+	btfm_slim->vendor_init = SLIM_SLAVE_INIT;
+	btfm_slim->vendor_port_en = SLIM_SLAVE_PORT_EN;
+
+	/* Created Mutex for slimbus data transfer */
+	mutex_init(&btfm_slim->io_lock);
+	mutex_init(&btfm_slim->xfer_lock);
+
+	/* Get Device tree node for Interface Device enumeration address */
+	ret = btfm_slim_get_dt_info(btfm_slim);
+	if (ret)
+		goto dealloc;
+
+	/* Add Interface Device for slimbus driver */
+	ret = slim_add_device(btfm_slim->slim_pgd->ctrl, &btfm_slim->slim_ifd);
+	if (ret) {
+		BTFMSLIM_ERR("error, adding SLIMBUS device failed");
+		goto dealloc;
+	}
+
+	/* Platform driver data allocation */
+	slim->dev.platform_data = btfm_slim;
+
+	/* Driver specific data allocation */
+	btfm_slim->dev = &slim->dev;
+	ret = btfm_slim_register_codec(&slim->dev);
+	ret = bt_register_slimdev(&slim->dev);
+	return ret;
+
+dealloc:
+	mutex_destroy(&btfm_slim->io_lock);
+	mutex_destroy(&btfm_slim->xfer_lock);
+	kfree(btfm_slim);
+	return ret;
+}
+static int btfm_slim_remove(struct slim_device *slim)
+{
+	struct btfmslim *btfm_slim = slim->dev.platform_data;
+
+	BTFMSLIM_DBG("");
+	mutex_destroy(&btfm_slim->io_lock);
+	mutex_destroy(&btfm_slim->xfer_lock);
+	snd_soc_unregister_codec(&slim->dev);
+
+	BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_ifd");
+	slim_remove_device(&btfm_slim->slim_ifd);
+
+	kfree(btfm_slim);
+
+	BTFMSLIM_DBG("slim_remove_device() - btfm_slim->slim_pgd");
+	slim_remove_device(slim);
+	return 0;
+}
+
+static const struct slim_device_id btfm_slim_id[] = {
+	{SLIM_SLAVE_COMPATIBLE_STR, 0},
+	{}
+};
+
+static struct slim_driver btfm_slim_driver = {
+	.driver = {
+		.name = "btfmslim-driver",
+		.owner = THIS_MODULE,
+	},
+	.probe = btfm_slim_probe,
+	.remove = btfm_slim_remove,
+	.id_table = btfm_slim_id
+};
+
+static int __init btfm_slim_init(void)
+{
+	int ret;
+
+	BTFMSLIM_DBG("");
+	ret = slim_driver_register(&btfm_slim_driver);
+	if (ret)
+		BTFMSLIM_ERR("Failed to register slimbus driver: %d", ret);
+	return ret;
+}
+
+static void __exit btfm_slim_exit(void)
+{
+	BTFMSLIM_DBG("");
+	slim_driver_unregister(&btfm_slim_driver);
+}
+
+module_init(btfm_slim_init);
+module_exit(btfm_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("BTFM Slimbus Slave driver");
diff --git a/drivers/bluetooth/btfm_slim.h b/drivers/bluetooth/btfm_slim.h
new file mode 100644
index 0000000..00d46a5
--- /dev/null
+++ b/drivers/bluetooth/btfm_slim.h
@@ -0,0 +1,164 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef BTFM_SLIM_H
+#define BTFM_SLIM_H
+#include <linux/slimbus/slimbus.h>
+
+#define BTFMSLIM_DBG(fmt, arg...)  pr_debug("%s: " fmt "\n", __func__, ## arg)
+#define BTFMSLIM_INFO(fmt, arg...) pr_info("%s: " fmt "\n", __func__, ## arg)
+#define BTFMSLIM_ERR(fmt, arg...)  pr_err("%s: " fmt "\n", __func__, ## arg)
+
+/* Vendor specific defines
+ * This should redefines in slimbus slave specific header
+ */
+#define SLIM_SLAVE_COMPATIBLE_STR	"btfmslim_slave"
+#define SLIM_SLAVE_REG_OFFSET		0x0000
+#define SLIM_SLAVE_RXPORT		NULL
+#define SLIM_SLAVE_TXPORT		NULL
+#define SLIM_SLAVE_INIT			NULL
+#define SLIM_SLAVE_PORT_EN		NULL
+
+/* Misc defines */
+#define SLIM_SLAVE_RW_MAX_TRIES		3
+#define SLIM_SLAVE_PRESENT_TIMEOUT	100
+
+#define PGD	1
+#define IFD	0
+
+
+/* Codec driver defines */
+enum {
+	BTFM_FM_SLIM_TX = 0,
+	BTFM_BT_SCO_SLIM_TX,
+	BTFM_BT_SCO_A2DP_SLIM_RX,
+	BTFM_BT_SPLIT_A2DP_SLIM_RX,
+	BTFM_SLIM_NUM_CODEC_DAIS
+};
+
+/* Slimbus Port defines - This should be redefined in specific device file */
+#define BTFM_SLIM_PGD_PORT_LAST				0xFF
+
+struct btfmslim_ch {
+	int id;
+	char *name;
+	uint32_t port_hdl;	/* slimbus port handler */
+	uint16_t port;		/* slimbus port number */
+
+	uint8_t ch;		/* slimbus channel number */
+	uint16_t ch_hdl;	/* slimbus channel handler */
+	uint16_t grph;	/* slimbus group channel handler */
+};
+
+struct btfmslim {
+	struct device *dev;
+	struct slim_device *slim_pgd;
+	struct slim_device slim_ifd;
+	struct mutex io_lock;
+	struct mutex xfer_lock;
+	uint8_t enabled;
+
+	uint32_t num_rx_port;
+	uint32_t num_tx_port;
+
+	struct btfmslim_ch *rx_chs;
+	struct btfmslim_ch *tx_chs;
+
+	int (*vendor_init)(struct btfmslim *btfmslim);
+	int (*vendor_port_en)(struct btfmslim *btfmslim, uint8_t port_num,
+		uint8_t rxport, uint8_t enable);
+};
+
+/**
+ * btfm_slim_hw_init: Initialize slimbus slave device
+ * Returns:
+ * 0: Success
+ * else: Fail
+ */
+int btfm_slim_hw_init(struct btfmslim *btfmslim);
+
+/**
+ * btfm_slim_hw_deinit: Deinitialize slimbus slave device
+ * Returns:
+ * 0: Success
+ * else: Fail
+ */
+int btfm_slim_hw_deinit(struct btfmslim *btfmslim);
+
+/**
+ * btfm_slim_write: write value to pgd or ifd device
+ * @btfmslim: slimbus slave device data pointer.
+ * @reg: slimbus slave register address
+ * @bytes: length of data
+ * @src: data pointer to write
+ * @pgd: selection for device: either PGD or IFD
+ * Returns:
+ * -EINVAL
+ * -ETIMEDOUT
+ * -ENOMEM
+ */
+int btfm_slim_write(struct btfmslim *btfmslim,
+	uint16_t reg, int bytes, void *src, uint8_t pgd);
+
+
+
+/**
+ * btfm_slim_read: read value from pgd or ifd device
+ * @btfmslim: slimbus slave device data pointer.
+ * @reg: slimbus slave register address
+ * @bytes: length of data
+ * @dest: data pointer to read
+ * @pgd: selection for device: either PGD or IFD
+ * Returns:
+ * -EINVAL
+ * -ETIMEDOUT
+ * -ENOMEM
+ */
+int btfm_slim_read(struct btfmslim *btfmslim,
+	uint16_t reg, int bytes, void *dest, uint8_t pgd);
+
+
+/**
+ * btfm_slim_enable_ch: enable channel for slimbus slave port
+ * @btfmslim: slimbus slave device data pointer.
+ * @ch: slimbus slave channel pointer
+ * @rxport: rxport or txport
+ * Returns:
+ * -EINVAL
+ * -ETIMEDOUT
+ * -ENOMEM
+ */
+int btfm_slim_enable_ch(struct btfmslim *btfmslim,
+	struct btfmslim_ch *ch, uint8_t rxport, uint32_t rates,
+	uint8_t grp, uint8_t nchan);
+
+/**
+ * btfm_slim_disable_ch: disable channel for slimbus slave port
+ * @btfmslim: slimbus slave device data pointer.
+ * @ch: slimbus slave channel pointer
+ * @rxport: rxport or txport
+ * Returns:
+ * -EINVAL
+ * -ETIMEDOUT
+ * -ENOMEM
+ */
+int btfm_slim_disable_ch(struct btfmslim *btfmslim,
+	struct btfmslim_ch *ch, uint8_t rxport, uint8_t grp, uint8_t nchan);
+
+/**
+ * btfm_slim_register_codec: Register codec driver in slimbus device node
+ * @dev: device node
+ * Returns:
+ * -ENOMEM
+ * 0
+ */
+int btfm_slim_register_codec(struct device *dev);
+#endif /* BTFM_SLIM_H */
diff --git a/drivers/bluetooth/btfm_slim_codec.c b/drivers/bluetooth/btfm_slim_codec.c
new file mode 100644
index 0000000..86760cd
--- /dev/null
+++ b/drivers/bluetooth/btfm_slim_codec.c
@@ -0,0 +1,410 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/ratelimit.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <btfm_slim.h>
+
+static int btfm_slim_codec_write(struct snd_soc_codec *codec, unsigned int reg,
+	unsigned int value)
+{
+	return 0;
+}
+
+static unsigned int btfm_slim_codec_read(struct snd_soc_codec *codec,
+				unsigned int reg)
+{
+	return 0;
+}
+
+static int btfm_slim_codec_probe(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static int btfm_slim_codec_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static int btfm_slim_dai_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	int ret;
+	struct btfmslim *btfmslim = dai->dev->platform_data;
+
+	BTFMSLIM_DBG("substream = %s  stream = %d",
+		 substream->name, substream->stream);
+	ret = btfm_slim_hw_init(btfmslim);
+	return ret;
+}
+
+static void btfm_slim_dai_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct btfmslim *btfmslim = dai->dev->platform_data;
+
+	BTFMSLIM_DBG("substream = %s  stream = %d",
+		 substream->name, substream->stream);
+	btfm_slim_hw_deinit(btfmslim);
+}
+
+static int btfm_slim_dai_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	BTFMSLIM_DBG("dai_name = %s DAI-ID %x rate %d num_ch %d",
+		dai->name, dai->id, params_rate(params),
+		params_channels(params));
+
+	return 0;
+}
+
+int btfm_slim_dai_prepare(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	int i, ret = -EINVAL;
+	struct btfmslim *btfmslim = dai->dev->platform_data;
+	struct btfmslim_ch *ch;
+	uint8_t rxport, grp = false, nchan = 1;
+
+	BTFMSLIM_DBG("dai->name:%s, dai->id: %d, dai->rate: %d", dai->name,
+		dai->id, dai->rate);
+
+	switch (dai->id) {
+	case BTFM_FM_SLIM_TX:
+		grp = true; nchan = 2;
+		ch = btfmslim->tx_chs;
+		rxport = 0;
+		break;
+	case BTFM_BT_SCO_SLIM_TX:
+		ch = btfmslim->tx_chs;
+		rxport = 0;
+		break;
+	case BTFM_BT_SCO_A2DP_SLIM_RX:
+	case BTFM_BT_SPLIT_A2DP_SLIM_RX:
+		ch = btfmslim->rx_chs;
+		rxport = 1;
+		break;
+	case BTFM_SLIM_NUM_CODEC_DAIS:
+	default:
+		BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
+		return ret;
+	}
+
+	/* Search for dai->id matched port handler */
+	for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
+		(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
+		(ch->id != dai->id); ch++, i++)
+		;
+
+	if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
+		(ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
+		BTFMSLIM_ERR("ch is invalid!!");
+		return ret;
+	}
+
+	ret = btfm_slim_enable_ch(btfmslim, ch, rxport, dai->rate, grp, nchan);
+	return ret;
+}
+
+int btfm_slim_dai_hw_free(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	int i, ret = -EINVAL;
+	struct btfmslim *btfmslim = dai->dev->platform_data;
+	struct btfmslim_ch *ch;
+	uint8_t rxport, grp = false, nchan = 1;
+
+	BTFMSLIM_DBG("dai->name:%s, dai->id: %d, dai->rate: %d", dai->name,
+		dai->id, dai->rate);
+
+	switch (dai->id) {
+	case BTFM_FM_SLIM_TX:
+		grp = true; nchan = 2;
+		ch = btfmslim->tx_chs;
+		rxport = 0;
+		break;
+	case BTFM_BT_SCO_SLIM_TX:
+		ch = btfmslim->tx_chs;
+		rxport = 0;
+		break;
+	case BTFM_BT_SCO_A2DP_SLIM_RX:
+	case BTFM_BT_SPLIT_A2DP_SLIM_RX:
+		ch = btfmslim->rx_chs;
+		rxport = 1;
+		break;
+	case BTFM_SLIM_NUM_CODEC_DAIS:
+	default:
+		BTFMSLIM_ERR("dai->id is invalid:%d", dai->id);
+		return ret;
+	}
+
+	/* Search for dai->id matched port handler */
+	for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) &&
+		(ch->id != BTFM_SLIM_NUM_CODEC_DAIS) &&
+		(ch->id != dai->id); ch++, i++)
+		;
+
+	if ((ch->port == BTFM_SLIM_PGD_PORT_LAST) ||
+		(ch->id == BTFM_SLIM_NUM_CODEC_DAIS)) {
+		BTFMSLIM_ERR("ch is invalid!!");
+		return ret;
+	}
+	ret = btfm_slim_disable_ch(btfmslim, ch, rxport, grp, nchan);
+	return ret;
+}
+
+/* This function will be called once during boot up */
+static int btfm_slim_dai_set_channel_map(struct snd_soc_dai *dai,
+				unsigned int tx_num, unsigned int *tx_slot,
+				unsigned int rx_num, unsigned int *rx_slot)
+{
+	int ret = -EINVAL, i;
+	struct btfmslim *btfmslim = dai->dev->platform_data;
+	struct btfmslim_ch *rx_chs;
+	struct btfmslim_ch *tx_chs;
+
+	BTFMSLIM_DBG("");
+
+	if (!btfmslim)
+		return ret;
+
+	rx_chs = btfmslim->rx_chs;
+	tx_chs = btfmslim->tx_chs;
+
+	if (!rx_chs || !tx_chs)
+		return ret;
+
+	BTFMSLIM_DBG("Rx: id\tname\tport\thdl\tch\tch_hdl");
+	for (i = 0; (rx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < rx_num);
+		i++, rx_chs++) {
+		/* Set Rx Channel number from machine driver and
+		 * get channel handler from slimbus driver
+		 */
+		rx_chs->ch = *(uint8_t *)(rx_slot + i);
+		ret = slim_query_ch(btfmslim->slim_pgd, rx_chs->ch,
+			&rx_chs->ch_hdl);
+		if (ret < 0) {
+			BTFMSLIM_ERR("slim_query_ch failure ch#%d - ret[%d]",
+				rx_chs->ch, ret);
+			goto error;
+		}
+		BTFMSLIM_DBG("    %d\t%s\t%d\t%x\t%d\t%x", rx_chs->id,
+			rx_chs->name, rx_chs->port, rx_chs->port_hdl,
+			rx_chs->ch, rx_chs->ch_hdl);
+	}
+
+	BTFMSLIM_DBG("Tx: id\tname\tport\thdl\tch\tch_hdl");
+	for (i = 0; (tx_chs->port != BTFM_SLIM_PGD_PORT_LAST) && (i < tx_num);
+		i++, tx_chs++) {
+		/* Set Tx Channel number from machine driver and
+		 * get channel handler from slimbus driver
+		 */
+		tx_chs->ch = *(uint8_t *)(tx_slot + i);
+		ret = slim_query_ch(btfmslim->slim_pgd, tx_chs->ch,
+			&tx_chs->ch_hdl);
+		if (ret < 0) {
+			BTFMSLIM_ERR("slim_query_ch failure ch#%d - ret[%d]",
+				tx_chs->ch, ret);
+			goto error;
+		}
+		BTFMSLIM_DBG("    %d\t%s\t%d\t%x\t%d\t%x", tx_chs->id,
+			tx_chs->name, tx_chs->port, tx_chs->port_hdl,
+			tx_chs->ch, tx_chs->ch_hdl);
+	}
+
+error:
+	return ret;
+}
+
+static int btfm_slim_dai_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+{
+	int i, ret = -EINVAL, *slot = NULL, j = 0, num = 1;
+	struct btfmslim *btfmslim = dai->dev->platform_data;
+	struct btfmslim_ch *ch = NULL;
+
+	if (!btfmslim)
+		return ret;
+
+	switch (dai->id) {
+	case BTFM_FM_SLIM_TX:
+		num = 2;
+	case BTFM_BT_SCO_SLIM_TX:
+		if (!tx_slot || !tx_num) {
+			BTFMSLIM_ERR("Invalid tx_slot %p or tx_num %p",
+				tx_slot, tx_num);
+			return -EINVAL;
+		}
+		ch = btfmslim->tx_chs;
+		if (!ch)
+			return -EINVAL;
+		slot = tx_slot;
+		*rx_slot = 0;
+		*tx_num = num;
+		*rx_num = 0;
+		break;
+	case BTFM_BT_SCO_A2DP_SLIM_RX:
+	case BTFM_BT_SPLIT_A2DP_SLIM_RX:
+		if (!rx_slot || !rx_num) {
+			BTFMSLIM_ERR("Invalid rx_slot %p or rx_num %p",
+				 rx_slot, rx_num);
+			return -EINVAL;
+		}
+		ch = btfmslim->rx_chs;
+		if (!ch)
+			return -EINVAL;
+		slot = rx_slot;
+		*tx_slot = 0;
+		*tx_num = 0;
+		*rx_num = num;
+		break;
+	}
+
+	do {
+		if (!ch)
+			return -EINVAL;
+		for (i = 0; (i < BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id !=
+			BTFM_SLIM_NUM_CODEC_DAIS) && (ch->id != dai->id);
+			ch++, i++)
+			;
+
+		if (ch->id == BTFM_SLIM_NUM_CODEC_DAIS ||
+			i == BTFM_SLIM_NUM_CODEC_DAIS) {
+			BTFMSLIM_ERR(
+				"No channel has been allocated for dai (%d)",
+				dai->id);
+			return -EINVAL;
+		}
+		if (!slot)
+			return -EINVAL;
+		*(slot + j) = ch->ch;
+		BTFMSLIM_DBG("id:%d, port:%d, ch:%d, slot: %d", ch->id,
+			ch->port, ch->ch, *(slot + j));
+
+		/* In case it has mulitiple channels */
+		if (++j < num)
+			ch++;
+	} while (j < num);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops btfmslim_dai_ops = {
+	.startup = btfm_slim_dai_startup,
+	.shutdown = btfm_slim_dai_shutdown,
+	.hw_params = btfm_slim_dai_hw_params,
+	.prepare = btfm_slim_dai_prepare,
+	.hw_free = btfm_slim_dai_hw_free,
+	.set_channel_map = btfm_slim_dai_set_channel_map,
+	.get_channel_map = btfm_slim_dai_get_channel_map,
+};
+
+static struct snd_soc_dai_driver btfmslim_dai[] = {
+	{	/* FM Audio data multiple channel  : FM -> qdsp */
+		.name = "btfm_fm_slim_tx",
+		.id = BTFM_FM_SLIM_TX,
+		.capture = {
+			.stream_name = "FM TX Capture",
+			.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 48000,
+			.rate_min = 48000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &btfmslim_dai_ops,
+	},
+	{	/* Bluetooth SCO voice uplink: bt -> modem */
+		.name = "btfm_bt_sco_slim_tx",
+		.id = BTFM_BT_SCO_SLIM_TX,
+		.capture = {
+			.stream_name = "SCO TX Capture",
+			/* 8 KHz or 16 KHz */
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 16000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.ops = &btfmslim_dai_ops,
+	},
+	{	/* Bluetooth SCO voice downlink: modem -> bt or A2DP Playback */
+		.name = "btfm_bt_sco_a2dp_slim_rx",
+		.id = BTFM_BT_SCO_A2DP_SLIM_RX,
+		.playback = {
+			.stream_name = "SCO A2DP RX Playback",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000
+				| SNDRV_PCM_RATE_48000, /* 8 or 16 or 48 Khz*/
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.ops = &btfmslim_dai_ops,
+	},
+	{	/* Bluetooth Split A2DP data: qdsp -> bt */
+		.name = "btfm_bt_split_a2dp_slim_rx",
+		.id = BTFM_BT_SPLIT_A2DP_SLIM_RX,
+		.playback = {
+			.stream_name = "SPLIT A2DP Playback",
+			.rates = SNDRV_PCM_RATE_48000, /* 48 KHz */
+			.formats = SNDRV_PCM_FMTBIT_S16_LE, /* 16 bits */
+			.rate_max = 48000,
+			.rate_min = 48000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.ops = &btfmslim_dai_ops,
+	},
+};
+
+static struct snd_soc_codec_driver btfmslim_codec = {
+	.probe	= btfm_slim_codec_probe,
+	.remove	= btfm_slim_codec_remove,
+	.read		= btfm_slim_codec_read,
+	.write	= btfm_slim_codec_write,
+};
+
+int btfm_slim_register_codec(struct device *dev)
+{
+	int ret = 0;
+
+	BTFMSLIM_DBG("");
+	/* Register Codec driver */
+	ret = snd_soc_register_codec(dev, &btfmslim_codec,
+		btfmslim_dai, ARRAY_SIZE(btfmslim_dai));
+
+	if (ret)
+		BTFMSLIM_ERR("failed to register codec (%d)", ret);
+
+	return ret;
+}
+
+MODULE_DESCRIPTION("BTFM Slimbus Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.c b/drivers/bluetooth/btfm_slim_wcn3990.c
new file mode 100644
index 0000000..c2d5b7b
--- /dev/null
+++ b/drivers/bluetooth/btfm_slim_wcn3990.c
@@ -0,0 +1,130 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slimbus/slimbus.h>
+#include <btfm_slim.h>
+#include <btfm_slim_wcn3990.h>
+
+/* WCN3990 Port assignment */
+struct btfmslim_ch wcn3990_rxport[] = {
+	{.id = BTFM_BT_SCO_A2DP_SLIM_RX, .name = "SCO_A2P_Rx",
+	.port = CHRK_SB_PGD_PORT_RX_SCO},
+	{.id = BTFM_BT_SPLIT_A2DP_SLIM_RX, .name = "A2P_Rx",
+	.port = CHRK_SB_PGD_PORT_RX_A2P},
+	{.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "",
+	.port = BTFM_SLIM_PGD_PORT_LAST},
+};
+
+struct btfmslim_ch wcn3990_txport[] = {
+	{.id = BTFM_FM_SLIM_TX, .name = "FM_Tx1",
+	.port = CHRK_SB_PGD_PORT_TX1_FM},
+	{.id = BTFM_FM_SLIM_TX, .name = "FM_Tx2",
+	.port = CHRK_SB_PGD_PORT_TX2_FM},
+	{.id = BTFM_BT_SCO_SLIM_TX, .name = "SCO_Tx",
+	.port = CHRK_SB_PGD_PORT_TX_SCO},
+	{.id = BTFM_SLIM_NUM_CODEC_DAIS, .name = "",
+	.port = BTFM_SLIM_PGD_PORT_LAST},
+};
+
+/* Function description */
+int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim)
+{
+	int ret = 0;
+	uint8_t reg_val;
+
+	BTFMSLIM_DBG("");
+
+	if (!btfmslim)
+		return -EINVAL;
+
+	/* Get SB_SLAVE_HW_REV_MSB value*/
+	ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_MSB,  1,
+		&reg_val, IFD);
+	if (ret) {
+		BTFMSLIM_ERR("failed to read (%d)", ret);
+		goto error;
+	}
+	BTFMSLIM_DBG("Major Rev: 0x%x, Minor Rev: 0x%x",
+		(reg_val & 0xF0) >> 4, (reg_val & 0x0F));
+
+	/* Get SB_SLAVE_HW_REV_LSB value*/
+	ret = btfm_slim_read(btfmslim, CHRK_SB_SLAVE_HW_REV_LSB,  1,
+		&reg_val, IFD);
+	if (ret) {
+		BTFMSLIM_ERR("failed to read (%d)", ret);
+		goto error;
+	}
+	BTFMSLIM_DBG("Step Rev: 0x%x", reg_val);
+
+error:
+	return ret;
+}
+
+
+int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t port_num,
+	uint8_t rxport, uint8_t enable)
+{
+	int ret = 0;
+	uint8_t reg_val = 0;
+	uint16_t reg;
+
+	BTFMSLIM_DBG("enable(%d)", enable);
+	if (rxport) {
+		/* Port enable */
+		reg = CHRK_SB_PGD_PORT_RX_CFGN(port_num - 0x10);
+	} else { /* txport */
+		/* Multiple Channel Setting - only FM Tx will be multiple
+		 * channel
+		 */
+		if (enable && (port_num == CHRK_SB_PGD_PORT_TX1_FM ||
+			port_num == CHRK_SB_PGD_PORT_TX2_FM)) {
+
+			reg_val = (0x1 << CHRK_SB_PGD_PORT_TX1_FM) |
+				(0x1 << CHRK_SB_PGD_PORT_TX2_FM);
+			reg = CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(port_num);
+			ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
+			if (ret) {
+				BTFMSLIM_ERR("failed to write (%d)", ret);
+				goto error;
+			}
+		}
+
+		/* Enable Tx port hw auto recovery for underrun or
+		 * overrun error
+		 */
+		reg_val = (enable) ? (CHRK_ENABLE_OVERRUN_AUTO_RECOVERY |
+				CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY) : 0x0;
+
+		ret = btfm_slim_write(btfmslim,
+			CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(port_num), 1,
+			&reg_val, IFD);
+		if (ret) {
+			BTFMSLIM_ERR("failed to write (%d)", ret);
+			goto error;
+		}
+
+		/* Port enable */
+		reg = CHRK_SB_PGD_PORT_TX_CFGN(port_num);
+	}
+
+	if (enable)
+		/* Set water mark to 1 and enable the port */
+		reg_val = CHRK_SB_PGD_PORT_ENABLE | CHRK_SB_PGD_PORT_WM_LB;
+	else
+		reg_val = CHRK_SB_PGD_PORT_DISABLE;
+
+	ret = btfm_slim_write(btfmslim, reg, 1, &reg_val, IFD);
+	if (ret)
+		BTFMSLIM_ERR("failed to write (%d)", ret);
+
+error:
+	return ret;
+}
diff --git a/drivers/bluetooth/btfm_slim_wcn3990.h b/drivers/bluetooth/btfm_slim_wcn3990.h
new file mode 100644
index 0000000..6bbdb6b
--- /dev/null
+++ b/drivers/bluetooth/btfm_slim_wcn3990.h
@@ -0,0 +1,140 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef BTFM_SLIM_WCN3990_H
+#define BTFM_SLIM_WCN3990_H
+//#ifdef CONFIG_BTFM_SLIM_WCN3990
+#include <btfm_slim.h>
+
+/* Registers Address */
+#define CHRK_SB_COMP_TEST			0x00000000
+#define CHRK_SB_SLAVE_HW_REV_MSB		0x00000001
+#define CHRK_SB_SLAVE_HW_REV_LSB		0x00000002
+#define CHRK_SB_DEBUG_FEATURES			0x00000005
+#define CHRK_SB_INTF_INT_EN			0x00000010
+#define CHRK_SB_INTF_INT_STATUS			0x00000011
+#define CHRK_SB_INTF_INT_CLR			0x00000012
+#define CHRK_SB_FRM_CFG				0x00000013
+#define CHRK_SB_FRM_STATUS			0x00000014
+#define CHRK_SB_FRM_INT_EN			0x00000015
+#define CHRK_SB_FRM_INT_STATUS			0x00000016
+#define CHRK_SB_FRM_INT_CLR			0x00000017
+#define CHRK_SB_FRM_WAKEUP			0x00000018
+#define CHRK_SB_FRM_CLKCTL_DONE			0x00000019
+#define CHRK_SB_FRM_IE_STATUS			0x0000001A
+#define CHRK_SB_FRM_VE_STATUS			0x0000001B
+#define CHRK_SB_PGD_TX_CFG_STATUS		0x00000020
+#define CHRK_SB_PGD_RX_CFG_STATUS		0x00000021
+#define CHRK_SB_PGD_DEV_INT_EN			0x00000022
+#define CHRK_SB_PGD_DEV_INT_STATUS		0x00000023
+#define CHRK_SB_PGD_DEV_INT_CLR			0x00000024
+#define CHRK_SB_PGD_PORT_INT_EN_RX_0		0x00000030
+#define CHRK_SB_PGD_PORT_INT_EN_RX_1		0x00000031
+#define CHRK_SB_PGD_PORT_INT_EN_TX_0		0x00000032
+#define CHRK_SB_PGD_PORT_INT_EN_TX_1		0x00000033
+#define CHRK_SB_PGD_PORT_INT_STATUS_RX_0	0x00000034
+#define CHRK_SB_PGD_PORT_INT_STATUS_RX_1	0x00000035
+#define CHRK_SB_PGD_PORT_INT_STATUS_TX_0	0x00000036
+#define CHRK_SB_PGD_PORT_INT_STATUS_TX_1	0x00000037
+#define CHRK_SB_PGD_PORT_INT_CLR_RX_0		0x00000038
+#define CHRK_SB_PGD_PORT_INT_CLR_RX_1		0x00000039
+#define CHRK_SB_PGD_PORT_INT_CLR_TX_0		0x0000003A
+#define CHRK_SB_PGD_PORT_INT_CLR_TX_1		0x0000003B
+#define CHRK_SB_PGD_PORT_RX_CFGN(n)		(0x00000040 + n)
+#define CHRK_SB_PGD_PORT_TX_CFGN(n)		(0x00000050 + n)
+#define CHRK_SB_PGD_PORT_INT_RX_SOURCEN(n)	(0x00000060 + n)
+#define CHRK_SB_PGD_PORT_INT_TX_SOURCEN(n)	(0x00000070 + n)
+#define CHRK_SB_PGD_PORT_RX_STATUSN(n)		(0x00000080 + n)
+#define CHRK_SB_PGD_PORT_TX_STATUSN(n)		(0x00000090 + n)
+#define CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_0(n)	(0x00000100 + 0x4*n)
+#define CHRK_SB_PGD_TX_PORTn_MULTI_CHNL_1(n)	(0x00000101 + 0x4*n)
+#define CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_0(n)	(0x00000180 + 0x4*n)
+#define CHRK_SB_PGD_RX_PORTn_MULTI_CHNL_1(n)	(0x00000181 + 0x4*n)
+#define CHRK_SB_PGD_PORT_TX_OR_UR_CFGN(n)	(0x000001F0 + n)
+
+/* Register Bit Setting */
+#define CHRK_ENABLE_OVERRUN_AUTO_RECOVERY	(0x1 << 1)
+#define CHRK_ENABLE_UNDERRUN_AUTO_RECOVERY	(0x1 << 0)
+#define CHRK_SB_PGD_PORT_ENABLE			(0x1 << 0)
+#define CHRK_SB_PGD_PORT_DISABLE		(0x0 << 0)
+#define CHRK_SB_PGD_PORT_WM_L1			(0x1 << 1)
+#define CHRK_SB_PGD_PORT_WM_L2			(0x2 << 1)
+#define CHRK_SB_PGD_PORT_WM_L3			(0x3 << 1)
+#define CHRK_SB_PGD_PORT_WM_LB			(0xB << 1)
+
+#define CHRK_SB_PGD_PORT_RX_NUM			16
+#define CHRK_SB_PGD_PORT_TX_NUM			16
+
+/* PGD Port Map */
+#define CHRK_SB_PGD_PORT_TX_SCO			0
+#define CHRK_SB_PGD_PORT_TX1_FM			1
+#define CHRK_SB_PGD_PORT_TX2_FM			2
+#define CHRK_SB_PGD_PORT_RX_SCO			16
+#define CHRK_SB_PGD_PORT_RX_A2P			17
+
+
+/* Function Prototype */
+
+/*
+ * btfm_slim_chrk_hw_init: Initialize wcn3990 specific slimbus slave device
+ * @btfmslim: slimbus slave device data pointer.
+ * Returns:
+ * 0: Success
+ * else: Fail
+ */
+int btfm_slim_chrk_hw_init(struct btfmslim *btfmslim);
+
+/*
+ * btfm_slim_chrk_enable_rxport: Enable wcn3990 Rx port by given port number
+ * @btfmslim: slimbus slave device data pointer.
+ * @portNum: slimbus slave port number to enable
+ * @rxport: rxport or txport
+ * @enable: enable port or disable port
+ * Returns:
+ * 0: Success
+ * else: Fail
+ */
+int btfm_slim_chrk_enable_port(struct btfmslim *btfmslim, uint8_t portNum,
+	uint8_t rxport, uint8_t enable);
+
+/* Specific defines for wcn3990 slimbus device */
+#define WCN3990_SLIM_REG_OFFSET		0x0800
+
+#ifdef SLIM_SLAVE_REG_OFFSET
+#undef SLIM_SLAVE_REG_OFFSET
+#define SLIM_SLAVE_REG_OFFSET		WCN3990_SLIM_REG_OFFSET
+#endif
+
+/* Assign vendor specific function */
+extern struct btfmslim_ch wcn3990_txport[];
+extern struct btfmslim_ch wcn3990_rxport[];
+
+#ifdef SLIM_SLAVE_RXPORT
+#undef SLIM_SLAVE_RXPORT
+#define SLIM_SLAVE_RXPORT (&wcn3990_rxport[0])
+#endif
+
+#ifdef SLIM_SLAVE_TXPORT
+#undef SLIM_SLAVE_TXPORT
+#define SLIM_SLAVE_TXPORT (&wcn3990_txport[0])
+#endif
+
+#ifdef SLIM_SLAVE_INIT
+#undef SLIM_SLAVE_INIT
+#define SLIM_SLAVE_INIT btfm_slim_chrk_hw_init
+#endif
+
+#ifdef SLIM_SLAVE_PORT_EN
+#undef SLIM_SLAVE_PORT_EN
+#define SLIM_SLAVE_PORT_EN btfm_slim_chrk_enable_port
+#endif
+//#endif /* CONFIG_BTFM_WCN3990 */
+#endif /* BTFM_SLIM_WCN3990_H */
diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c
index a835344..efaa9d1 100644
--- a/drivers/char/adsprpc.c
+++ b/drivers/char/adsprpc.c
@@ -672,9 +672,9 @@ static int overlap_ptr_cmp(const void *a, const void *b)
 	return st == 0 ? ed : st;
 }
 
-static void context_build_overlap(struct smq_invoke_ctx *ctx)
+static int context_build_overlap(struct smq_invoke_ctx *ctx)
 {
-	int i;
+	int i, err = 0;
 	remote_arg_t *lpra = ctx->lpra;
 	int inbufs = REMOTE_SCALARS_INBUFS(ctx->sc);
 	int outbufs = REMOTE_SCALARS_OUTBUFS(ctx->sc);
@@ -684,6 +684,11 @@ static void context_build_overlap(struct smq_invoke_ctx *ctx)
 	for (i = 0; i < nbufs; ++i) {
 		ctx->overs[i].start = (uintptr_t)lpra[i].buf.pv;
 		ctx->overs[i].end = ctx->overs[i].start + lpra[i].buf.len;
+		if (lpra[i].buf.len) {
+			VERIFY(err, ctx->overs[i].end > ctx->overs[i].start);
+			if (err)
+				goto bail;
+		}
 		ctx->overs[i].raix = i;
 		ctx->overps[i] = &ctx->overs[i];
 	}
@@ -709,6 +714,8 @@ static void context_build_overlap(struct smq_invoke_ctx *ctx)
 			max = *ctx->overps[i];
 		}
 	}
+bail:
+	return err;
 }
 
 #define K_COPY_FROM_USER(err, kernel, dst, src, size) \
@@ -781,8 +788,11 @@ static int context_alloc(struct fastrpc_file *fl, uint32_t kernel,
 	}
 
 	ctx->sc = invoke->sc;
-	if (bufs)
-		context_build_overlap(ctx);
+	if (bufs) {
+		VERIFY(err, 0 == context_build_overlap(ctx));
+		if (err)
+			goto bail;
+	}
 	ctx->retval = -1;
 	ctx->pid = current->pid;
 	ctx->tgid = current->tgid;
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 1238349..83db1416 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -460,6 +460,26 @@ void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
 EXPORT_SYMBOL_GPL(clk_hw_set_rate_range);
 
 /*
+ * Aggregate the rate of all child nodes which are enabled and exclude the
+ * child node which requests for clk_aggregate_rate.
+ */
+unsigned long clk_aggregate_rate(struct clk_hw *hw,
+					const struct clk_core *parent)
+{
+	struct clk_core *child;
+	unsigned long aggre_rate = 0;
+
+	hlist_for_each_entry(child, &parent->children, child_node) {
+		if (child->enable_count &&
+				strcmp(child->name, hw->init->name))
+			aggre_rate = max(child->rate, aggre_rate);
+	}
+
+	return aggre_rate;
+}
+EXPORT_SYMBOL_GPL(clk_aggregate_rate);
+
+/*
  * Helper for finding best parent to provide a given frequency. This can be used
  * directly as a determine_rate callback (e.g. for a mux), or from a more
  * complex clock that may combine a mux with other operations.
diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile
index 5ab4129..c227967 100644
--- a/drivers/clk/qcom/Makefile
+++ b/drivers/clk/qcom/Makefile
@@ -9,7 +9,7 @@
 clk-qcom-y += clk-branch.o
 clk-qcom-y += clk-regmap-divider.o
 clk-qcom-y += clk-regmap-mux.o
-clk-qcom-y += reset.o
+clk-qcom-y += reset.o clk-voter.o
 clk-qcom-y += clk-dummy.o
 clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o gdsc-regulator.o
 
diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c
index c8e317a..385cdd7 100644
--- a/drivers/clk/qcom/clk-rcg2.c
+++ b/drivers/clk/qcom/clk-rcg2.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2013, 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -411,6 +411,15 @@ static int clk_rcg2_enable(struct clk_hw *hw)
 	if (!f)
 		return -EINVAL;
 
+	/*
+	 * If CXO is not listed as a supported frequency in the frequency
+	 * table, the above API would return the lowest supported frequency
+	 * instead. This will lead to incorrect configuration of the RCG.
+	 * Check if the RCG rate is CXO and configure it accordingly.
+	 */
+	if (rate == cxo_f.freq)
+		f = &cxo_f;
+
 	clk_rcg2_set_force_enable(hw);
 	clk_rcg2_configure(rcg, f);
 	clk_rcg2_clear_force_enable(hw);
diff --git a/drivers/clk/qcom/clk-voter.c b/drivers/clk/qcom/clk-voter.c
new file mode 100644
index 0000000..b0c7e4a
--- /dev/null
+++ b/drivers/clk/qcom/clk-voter.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+
+#include "clk-voter.h"
+
+static int voter_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long parent_rate)
+{
+	int ret = 0;
+	struct clk_voter *v = to_clk_voter(hw);
+	unsigned long cur_rate, new_rate, other_rate = 0;
+
+	if (v->is_branch)
+		return ret;
+
+	if (v->enabled) {
+		struct clk_hw *parent = clk_hw_get_parent(hw);
+
+		if (!parent)
+			return -EINVAL;
+
+		/*
+		 * Get the aggregate rate without this clock's vote and update
+		 * if the new rate is different than the current rate.
+		 */
+		other_rate = clk_aggregate_rate(hw, parent->core);
+
+		cur_rate = max(other_rate, clk_get_rate(hw->clk));
+		new_rate = max(other_rate, rate);
+
+		if (new_rate != cur_rate) {
+			ret = clk_set_rate(parent->clk, new_rate);
+			if (ret)
+				return ret;
+		}
+	}
+	v->rate =  rate;
+
+	return ret;
+}
+
+static int voter_clk_prepare(struct clk_hw *hw)
+{
+	int ret = 0;
+	unsigned long cur_rate;
+	struct clk_hw *parent;
+	struct clk_voter *v = to_clk_voter(hw);
+
+	parent = clk_hw_get_parent(hw);
+	if (!parent)
+		return -EINVAL;
+
+	if (v->is_branch) {
+		v->enabled = true;
+		return ret;
+	}
+
+	/*
+	 * Increase the rate if this clock is voting for a higher rate
+	 * than the current rate.
+	 */
+	cur_rate = clk_aggregate_rate(hw, parent->core);
+
+	if (v->rate > cur_rate) {
+		ret = clk_set_rate(parent->clk, v->rate);
+		if (ret)
+			return ret;
+	}
+	v->enabled = true;
+
+	return ret;
+}
+
+static void voter_clk_unprepare(struct clk_hw *hw)
+{
+	unsigned long cur_rate, new_rate;
+	struct clk_hw *parent;
+	struct clk_voter *v = to_clk_voter(hw);
+
+
+	parent = clk_hw_get_parent(hw);
+	if (!parent)
+		return;
+	/*
+	 * Decrease the rate if this clock was the only one voting for
+	 * the highest rate.
+	 */
+	v->enabled = false;
+	if (v->is_branch)
+		return;
+
+	new_rate = clk_aggregate_rate(hw, parent->core);
+	cur_rate = max(new_rate, v->rate);
+
+	if (new_rate < cur_rate)
+		clk_set_rate(parent->clk, new_rate);
+}
+
+static int voter_clk_is_enabled(struct clk_hw *hw)
+{
+	struct clk_voter *v = to_clk_voter(hw);
+
+	return v->enabled;
+}
+
+static long voter_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+		unsigned long *parent_rate)
+{
+	struct clk_hw *parent_hw = clk_hw_get_parent(hw);
+
+	if (!parent_hw)
+		return -EINVAL;
+
+	return clk_hw_round_rate(parent_hw, rate);
+}
+
+static unsigned long voter_clk_recalc_rate(struct clk_hw *hw,
+		unsigned long parent_rate)
+{
+	struct clk_voter *v = to_clk_voter(hw);
+
+	return v->rate;
+}
+
+const struct clk_ops clk_ops_voter = {
+	.prepare = voter_clk_prepare,
+	.unprepare = voter_clk_unprepare,
+	.set_rate = voter_clk_set_rate,
+	.is_enabled = voter_clk_is_enabled,
+	.round_rate = voter_clk_round_rate,
+	.recalc_rate = voter_clk_recalc_rate,
+};
diff --git a/drivers/clk/qcom/clk-voter.h b/drivers/clk/qcom/clk-voter.h
new file mode 100644
index 0000000..0ae2b1a
--- /dev/null
+++ b/drivers/clk/qcom/clk-voter.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __QCOM_CLK_VOTER_H__
+#define __QCOM_CLK_VOTER_H__
+
+#include <linux/clk-provider.h>
+#include <linux/platform_device.h>
+
+struct clk_voter {
+	int is_branch;
+	bool enabled;
+	struct clk_hw hw;
+	unsigned long rate;
+};
+
+extern const struct clk_ops clk_ops_voter;
+
+#define to_clk_voter(_hw) container_of(_hw, struct clk_voter, hw)
+
+#define __DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate, _is_branch) \
+	struct clk_voter clk_name = {					 \
+		.is_branch = (_is_branch),				  \
+		.rate = _default_rate,					   \
+		.hw.init = &(struct clk_init_data){			   \
+			.ops = &clk_ops_voter,				   \
+			.name = #clk_name,				   \
+			.parent_names = (const char *[]){ #_parent_name }, \
+			.num_parents = 1,				   \
+		},							   \
+	}
+
+#define DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate) \
+	 __DEFINE_CLK_VOTER(clk_name, _parent_name, _default_rate, 0)
+
+#define DEFINE_CLK_BRANCH_VOTER(clk_name, _parent_name) \
+	 __DEFINE_CLK_VOTER(clk_name, _parent_name, 1000, 1)
+
+#endif
diff --git a/drivers/clk/qcom/videocc-msmskunk.c b/drivers/clk/qcom/videocc-msmskunk.c
index adfece6..670efb5 100644
--- a/drivers/clk/qcom/videocc-msmskunk.c
+++ b/drivers/clk/qcom/videocc-msmskunk.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -68,8 +68,8 @@ static struct pll_vco fabia_vco[] = {
 };
 
 static const struct pll_config video_pll0_config = {
-	.l = 0x15,
-	.frac = 0xaab,
+	.l = 0x10,
+	.frac = 0xaaab,
 };
 
 static struct clk_alpha_pll video_pll0 = {
@@ -85,8 +85,8 @@ static struct clk_alpha_pll video_pll0 = {
 			.ops = &clk_fabia_pll_ops,
 			VDD_CX_FMAX_MAP5(
 				MIN, 200000000,
-				LOW, 660000000,
-				LOW_L1, 1212000000,
+				LOW, 640000000,
+				LOW_L1, 760000000,
 				NOMINAL, 1332000000,
 				HIGH, 1599000000),
 
@@ -95,10 +95,10 @@ static struct clk_alpha_pll video_pll0 = {
 };
 
 static const struct freq_tbl ftbl_video_cc_venus_clk_src[] = {
-	F(101000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0),
-	F(202000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
-	F(269333333, P_VIDEO_PLL0_OUT_MAIN, 1.5, 0, 0),
-	F(404000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
+	F(100000000, P_VIDEO_PLL0_OUT_MAIN, 4, 0, 0),
+	F(200000000, P_VIDEO_PLL0_OUT_MAIN, 2, 0, 0),
+	F(320000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
+	F(380000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
 	F(444000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
 	F(533000000, P_VIDEO_PLL0_OUT_MAIN, 1, 0, 0),
 	{ }
@@ -120,8 +120,8 @@ static struct clk_rcg2 video_cc_venus_clk_src = {
 		VDD_CX_FMAX_MAP6(
 			MIN, 100000000,
 			LOWER, 200000000,
-			LOW, 330000000,
-			LOW_L1, 404000000,
+			LOW, 320000000,
+			LOW_L1, 380000000,
 			NOMINAL, 444000000,
 			HIGH, 533000000),
 	},
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ed37e59..9338ff7 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -362,6 +362,25 @@
 	help
 	  Say yes here to support the PXA GPIO device
 
+config GPIO_QPNP_PIN
+	tristate "Qualcomm Technologies, Inc. QPNP GPIO support"
+	depends on SPMI
+	help
+	  Say 'y' here to include support for the Qualcomm Technologies, Inc.
+	  QPNP GPIO driver.  This driver supports Device Tree and allows a
+	  device_node to be registered as a gpio-controller.  It does not handle
+	  GPIO interrupts directly; they are handled via the SPMI arbiter
+	  interrupt driver.
+
+config GPIO_QPNP_PIN_DEBUG
+	bool "Qualcomm Technologies, Inc. QPNP GPIO debug support"
+	depends on GPIO_QPNP_PIN && DEBUG_FS
+	help
+	  Say 'y' here to include debug support for the Qualcomm Technologies,
+	  Inc. QPNP GPIO driver.  This provides a userspace debug interface to
+	  get and set all of the supported features of PMIC GPIO and MPP pins
+	  including those which are managed by the gpio framework.
+
 config GPIO_RCAR
 	tristate "Renesas R-Car GPIO"
 	depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 1b08983..68418a6 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -91,6 +91,7 @@
 obj-$(CONFIG_GPIO_PISOSR)	+= gpio-pisosr.o
 obj-$(CONFIG_GPIO_PL061)	+= gpio-pl061.o
 obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o
+obj-$(CONFIG_GPIO_QPNP_PIN)	+= qpnp-pin.o
 obj-$(CONFIG_GPIO_RC5T583)	+= gpio-rc5t583.o
 obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o
 obj-$(CONFIG_GPIO_RCAR)		+= gpio-rcar.o
diff --git a/drivers/gpio/qpnp-pin.c b/drivers/gpio/qpnp-pin.c
new file mode 100644
index 0000000..d89aca9
--- /dev/null
+++ b/drivers/gpio/qpnp-pin.c
@@ -0,0 +1,1714 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/export.h>
+#include <linux/qpnp/pin.h>
+
+#define Q_REG_ADDR(q_spec, reg_index)	\
+		((q_spec)->offset + reg_index)
+
+#define Q_REG_STATUS1			0x8
+#define Q_REG_STATUS1_VAL_MASK		0x1
+#define Q_REG_STATUS1_GPIO_EN_REV0_MASK	0x2
+#define Q_REG_STATUS1_GPIO_EN_MASK	0x80
+#define Q_REG_STATUS1_MPP_EN_MASK	0x80
+
+#define Q_NUM_CTL_REGS			0xD
+
+/* revision registers base address offsets */
+#define Q_REG_DIG_MINOR_REV		0x0
+#define Q_REG_DIG_MAJOR_REV		0x1
+#define Q_REG_ANA_MINOR_REV		0x2
+
+/* type registers base address offsets */
+#define Q_REG_TYPE			0x4
+#define Q_REG_SUBTYPE			0x5
+
+/* gpio peripheral type and subtype values */
+#define Q_GPIO_TYPE			0x10
+#define Q_GPIO_SUBTYPE_GPIO_4CH		0x1
+#define Q_GPIO_SUBTYPE_GPIOC_4CH	0x5
+#define Q_GPIO_SUBTYPE_GPIO_8CH		0x9
+#define Q_GPIO_SUBTYPE_GPIOC_8CH	0xD
+#define Q_GPIO_SUBTYPE_GPIO_LV		0x10
+#define Q_GPIO_SUBTYPE_GPIO_MV		0x11
+
+/* mpp peripheral type and subtype values */
+#define Q_MPP_TYPE				0x11
+#define Q_MPP_SUBTYPE_4CH_NO_ANA_OUT		0x3
+#define Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT	0x4
+#define Q_MPP_SUBTYPE_4CH_NO_SINK		0x5
+#define Q_MPP_SUBTYPE_ULT_4CH_NO_SINK		0x6
+#define Q_MPP_SUBTYPE_4CH_FULL_FUNC		0x7
+#define Q_MPP_SUBTYPE_8CH_FULL_FUNC		0xF
+
+/* control register base address offsets */
+#define Q_REG_MODE_CTL			0x40
+#define Q_REG_DIG_VIN_CTL		0x41
+#define Q_REG_DIG_PULL_CTL		0x42
+#define Q_REG_DIG_IN_CTL		0x43
+#define Q_REG_DIG_OUT_SRC_CTL		0x44
+#define Q_REG_DIG_OUT_CTL		0x45
+#define Q_REG_EN_CTL			0x46
+#define Q_REG_AOUT_CTL			0x48
+#define Q_REG_AIN_CTL			0x4A
+#define Q_REG_APASS_SEL_CTL		0x4A
+#define Q_REG_SINK_CTL			0x4C
+
+/* control register regs array indices */
+#define Q_REG_I_MODE_CTL		0
+#define Q_REG_I_DIG_VIN_CTL		1
+#define Q_REG_I_DIG_PULL_CTL		2
+#define Q_REG_I_DIG_IN_CTL		3
+#define Q_REG_I_DIG_OUT_SRC_CTL		4
+#define Q_REG_I_DIG_OUT_CTL		5
+#define Q_REG_I_EN_CTL			6
+#define Q_REG_I_AOUT_CTL		8
+#define Q_REG_I_APASS_SEL_CTL		10
+#define Q_REG_I_AIN_CTL			10
+#define Q_REG_I_SINK_CTL		12
+
+/* control reg: mode */
+#define Q_REG_OUT_INVERT_SHIFT		0
+#define Q_REG_OUT_INVERT_MASK		0x1
+#define Q_REG_SRC_SEL_SHIFT		1
+#define Q_REG_SRC_SEL_MASK		0xE
+#define Q_REG_MODE_SEL_SHIFT		4
+#define Q_REG_MODE_SEL_MASK		0x70
+#define Q_REG_LV_MV_MODE_SEL_SHIFT	0
+#define Q_REG_LV_MV_MODE_SEL_MASK	0x3
+
+/* control reg: dig_out_src (GPIO LV/MV only) */
+#define Q_REG_DIG_OUT_SRC_SRC_SEL_SHIFT 0
+#define Q_REG_DIG_OUT_SRC_SRC_SEL_MASK	0xF
+#define Q_REG_DIG_OUT_SRC_INVERT_SHIFT	7
+#define Q_REG_DIG_OUT_SRC_INVERT_MASK	0x80
+
+/* control reg: dig_vin */
+#define Q_REG_VIN_SHIFT			0
+#define Q_REG_VIN_MASK			0x7
+
+/* control reg: dig_pull */
+#define Q_REG_PULL_SHIFT		0
+#define Q_REG_PULL_MASK			0x7
+
+/* control reg: dig_out */
+#define Q_REG_OUT_STRENGTH_SHIFT	0
+#define Q_REG_OUT_STRENGTH_MASK		0x3
+#define Q_REG_OUT_TYPE_SHIFT		4
+#define Q_REG_OUT_TYPE_MASK		0x30
+
+/* control reg: dig_in_ctl */
+#define Q_REG_DTEST_SEL_SHIFT			0
+#define Q_REG_DTEST_SEL_MASK			0xF
+#define Q_REG_LV_MV_DTEST_SEL_CFG_SHIFT		0
+#define Q_REG_LV_MV_DTEST_SEL_CFG_MASK		0x7
+#define Q_REG_LV_MV_DTEST_SEL_EN_SHIFT		7
+#define Q_REG_LV_MV_DTEST_SEL_EN_MASK		0x80
+
+/* control reg: en */
+#define Q_REG_MASTER_EN_SHIFT		7
+#define Q_REG_MASTER_EN_MASK		0x80
+
+/* control reg: ana_out */
+#define Q_REG_AOUT_REF_SHIFT		0
+#define Q_REG_AOUT_REF_MASK		0x7
+
+/* control reg: ana_in */
+#define Q_REG_AIN_ROUTE_SHIFT		0
+#define Q_REG_AIN_ROUTE_MASK		0x7
+
+/* control reg: sink */
+#define Q_REG_CS_OUT_SHIFT		0
+#define Q_REG_CS_OUT_MASK		0x7
+
+/* control ref: apass_sel */
+#define Q_REG_APASS_SEL_SHIFT		0
+#define Q_REG_APASS_SEL_MASK		0x3
+
+enum qpnp_pin_param_type {
+	Q_PIN_CFG_MODE,
+	Q_PIN_CFG_OUTPUT_TYPE,
+	Q_PIN_CFG_INVERT,
+	Q_PIN_CFG_PULL,
+	Q_PIN_CFG_VIN_SEL,
+	Q_PIN_CFG_OUT_STRENGTH,
+	Q_PIN_CFG_SRC_SEL,
+	Q_PIN_CFG_MASTER_EN,
+	Q_PIN_CFG_AOUT_REF,
+	Q_PIN_CFG_AIN_ROUTE,
+	Q_PIN_CFG_CS_OUT,
+	Q_PIN_CFG_APASS_SEL,
+	Q_PIN_CFG_DTEST_SEL,
+	Q_PIN_CFG_INVALID,
+};
+
+#define Q_NUM_PARAMS			Q_PIN_CFG_INVALID
+
+/* param error checking */
+#define QPNP_PIN_GPIO_MODE_INVALID		3
+#define QPNP_PIN_GPIO_LV_MV_MODE_INVALID	4
+#define QPNP_PIN_MPP_MODE_INVALID		7
+#define QPNP_PIN_INVERT_INVALID			2
+#define QPNP_PIN_OUT_BUF_INVALID		3
+#define QPNP_PIN_GPIO_LV_MV_OUT_BUF_INVALID	4
+#define QPNP_PIN_VIN_4CH_INVALID		5
+#define QPNP_PIN_VIN_8CH_INVALID		8
+#define QPNP_PIN_GPIO_LV_VIN_INVALID		1
+#define QPNP_PIN_GPIO_MV_VIN_INVALID		2
+#define QPNP_PIN_GPIO_PULL_INVALID		6
+#define QPNP_PIN_MPP_PULL_INVALID		4
+#define QPNP_PIN_OUT_STRENGTH_INVALID		4
+#define QPNP_PIN_SRC_INVALID			8
+#define QPNP_PIN_GPIO_LV_MV_SRC_INVALID		16
+#define QPNP_PIN_MASTER_INVALID			2
+#define QPNP_PIN_AOUT_REF_INVALID		8
+#define QPNP_PIN_AIN_ROUTE_INVALID		8
+#define QPNP_PIN_CS_OUT_INVALID			8
+#define QPNP_PIN_APASS_SEL_INVALID		4
+#define QPNP_PIN_DTEST_SEL_INVALID		4
+
+struct qpnp_pin_spec {
+	uint8_t slave;			/* 0-15 */
+	uint16_t offset;		/* 0-255 */
+	uint32_t gpio_chip_idx;		/* offset from gpio_chip base */
+	uint32_t pmic_pin;		/* PMIC pin number */
+	int irq;			/* logical IRQ number */
+	u8 regs[Q_NUM_CTL_REGS];	/* Control regs */
+	u8 num_ctl_regs;		/* usable number on this pin */
+	u8 type;			/* peripheral type */
+	u8 subtype;			/* peripheral subtype */
+	u8 dig_major_rev;
+	struct device_node *node;
+	enum qpnp_pin_param_type params[Q_NUM_PARAMS];
+	struct qpnp_pin_chip *q_chip;
+};
+
+struct qpnp_pin_chip {
+	struct gpio_chip	gpio_chip;
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	struct qpnp_pin_spec	**pmic_pins;
+	struct qpnp_pin_spec	**chip_gpios;
+	uint32_t		pmic_pin_lowest;
+	uint32_t		pmic_pin_highest;
+	struct device_node	*int_ctrl;
+	struct list_head	chip_list;
+	struct dentry		*dfs_dir;
+	bool			chip_registered;
+};
+
+static LIST_HEAD(qpnp_pin_chips);
+static DEFINE_MUTEX(qpnp_pin_chips_lock);
+
+static inline void qpnp_pmic_pin_set_spec(struct qpnp_pin_chip *q_chip,
+					      uint32_t pmic_pin,
+					      struct qpnp_pin_spec *spec)
+{
+	q_chip->pmic_pins[pmic_pin - q_chip->pmic_pin_lowest] = spec;
+}
+
+static inline struct qpnp_pin_spec *qpnp_pmic_pin_get_spec(
+						struct qpnp_pin_chip *q_chip,
+						uint32_t pmic_pin)
+{
+	if (pmic_pin < q_chip->pmic_pin_lowest ||
+	    pmic_pin > q_chip->pmic_pin_highest)
+		return NULL;
+
+	return q_chip->pmic_pins[pmic_pin - q_chip->pmic_pin_lowest];
+}
+
+static inline struct qpnp_pin_spec *qpnp_chip_gpio_get_spec(
+						struct qpnp_pin_chip *q_chip,
+						uint32_t chip_gpio)
+{
+	if (chip_gpio >= q_chip->gpio_chip.ngpio)
+		return NULL;
+
+	return q_chip->chip_gpios[chip_gpio];
+}
+
+static inline void qpnp_chip_gpio_set_spec(struct qpnp_pin_chip *q_chip,
+					      uint32_t chip_gpio,
+					      struct qpnp_pin_spec *spec)
+{
+	q_chip->chip_gpios[chip_gpio] = spec;
+}
+
+static bool is_gpio_lv_mv(struct qpnp_pin_spec *q_spec)
+{
+	if ((q_spec->type == Q_GPIO_TYPE) &&
+		(q_spec->subtype == Q_GPIO_SUBTYPE_GPIO_LV ||
+		q_spec->subtype == Q_GPIO_SUBTYPE_GPIO_MV))
+		return true;
+
+	return false;
+}
+
+/*
+ * Determines whether a specified param's configuration is correct.
+ * This check is two tier. First a check is done whether the hardware
+ * supports this param and value requested. The second check validates
+ * that the configuration is correct, given the fact that the hardware
+ * supports it.
+ *
+ * Returns
+ *	-ENXIO is the hardware does not support this param.
+ *	-EINVAL if the the hardware does support this param, but the
+ *	requested value is outside the supported range.
+ */
+static int qpnp_pin_check_config(enum qpnp_pin_param_type idx,
+				 struct qpnp_pin_spec *q_spec, uint32_t val)
+{
+	u8 subtype = q_spec->subtype;
+
+	switch (idx) {
+	case Q_PIN_CFG_MODE:
+		if (q_spec->type == Q_GPIO_TYPE) {
+			if (is_gpio_lv_mv(q_spec)) {
+				if (val >= QPNP_PIN_GPIO_LV_MV_MODE_INVALID)
+					return -EINVAL;
+			} else if (val >= QPNP_PIN_GPIO_MODE_INVALID) {
+				return -EINVAL;
+			}
+		} else if (q_spec->type == Q_MPP_TYPE) {
+			if (val >= QPNP_PIN_MPP_MODE_INVALID)
+				return -EINVAL;
+			if ((subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+			     subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK) &&
+			     (val == QPNP_PIN_MODE_BIDIR))
+				return -ENXIO;
+		}
+		break;
+	case Q_PIN_CFG_OUTPUT_TYPE:
+		if (q_spec->type != Q_GPIO_TYPE)
+			return -ENXIO;
+		if ((val == QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS ||
+		    val == QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS) &&
+		    (subtype == Q_GPIO_SUBTYPE_GPIOC_4CH ||
+		    (subtype == Q_GPIO_SUBTYPE_GPIOC_8CH)))
+			return -EINVAL;
+		else if (is_gpio_lv_mv(q_spec) &&
+			val >= QPNP_PIN_GPIO_LV_MV_OUT_BUF_INVALID)
+			return -EINVAL;
+		else if (val >= QPNP_PIN_OUT_BUF_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_INVERT:
+		if (val >= QPNP_PIN_INVERT_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_PULL:
+		if (q_spec->type == Q_GPIO_TYPE &&
+		    val >= QPNP_PIN_GPIO_PULL_INVALID)
+			return -EINVAL;
+		if (q_spec->type == Q_MPP_TYPE) {
+			if (val >= QPNP_PIN_MPP_PULL_INVALID)
+				return -EINVAL;
+			if (subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+			    subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK)
+				return -ENXIO;
+		}
+		break;
+	case Q_PIN_CFG_VIN_SEL:
+		if (is_gpio_lv_mv(q_spec)) {
+			if (subtype == Q_GPIO_SUBTYPE_GPIO_LV) {
+				if (val >= QPNP_PIN_GPIO_LV_VIN_INVALID)
+					return -EINVAL;
+			} else {
+				if (val >= QPNP_PIN_GPIO_MV_VIN_INVALID)
+					return -EINVAL;
+			}
+		} else if (val >= QPNP_PIN_VIN_8CH_INVALID) {
+			return -EINVAL;
+		} else if (val >= QPNP_PIN_VIN_4CH_INVALID) {
+			if (q_spec->type == Q_GPIO_TYPE &&
+			   (subtype == Q_GPIO_SUBTYPE_GPIO_4CH ||
+			    subtype == Q_GPIO_SUBTYPE_GPIOC_4CH))
+				return -EINVAL;
+			if (q_spec->type == Q_MPP_TYPE &&
+			   (subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+			    subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
+			    subtype == Q_MPP_SUBTYPE_4CH_FULL_FUNC ||
+			    subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT ||
+			    subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK))
+				return -EINVAL;
+		}
+		break;
+	case Q_PIN_CFG_OUT_STRENGTH:
+		if (q_spec->type != Q_GPIO_TYPE)
+			return -ENXIO;
+		if (val >= QPNP_PIN_OUT_STRENGTH_INVALID ||
+		    val == 0)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_SRC_SEL:
+		if (q_spec->type == Q_MPP_TYPE &&
+		    (val == QPNP_PIN_SEL_FUNC_1 ||
+		     val == QPNP_PIN_SEL_FUNC_2))
+			return -EINVAL;
+		if (is_gpio_lv_mv(q_spec)) {
+			if (val >= QPNP_PIN_GPIO_LV_MV_SRC_INVALID)
+				return -EINVAL;
+		} else if (val >= QPNP_PIN_SRC_INVALID) {
+			return -EINVAL;
+		}
+		break;
+	case Q_PIN_CFG_MASTER_EN:
+		if (val >= QPNP_PIN_MASTER_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_AOUT_REF:
+		if (q_spec->type != Q_MPP_TYPE)
+			return -ENXIO;
+		if (subtype == Q_MPP_SUBTYPE_4CH_NO_ANA_OUT ||
+		    subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT)
+			return -ENXIO;
+		if (val >= QPNP_PIN_AOUT_REF_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_AIN_ROUTE:
+		if (q_spec->type != Q_MPP_TYPE)
+			return -ENXIO;
+		if (val >= QPNP_PIN_AIN_ROUTE_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_CS_OUT:
+		if (q_spec->type != Q_MPP_TYPE)
+			return -ENXIO;
+		if (subtype == Q_MPP_SUBTYPE_4CH_NO_SINK ||
+		    subtype == Q_MPP_SUBTYPE_ULT_4CH_NO_SINK)
+			return -ENXIO;
+		if (val >= QPNP_PIN_CS_OUT_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_APASS_SEL:
+		if (!is_gpio_lv_mv(q_spec))
+			return -ENXIO;
+		if (val >= QPNP_PIN_APASS_SEL_INVALID)
+			return -EINVAL;
+		break;
+	case Q_PIN_CFG_DTEST_SEL:
+		if (val > QPNP_PIN_DTEST_SEL_INVALID)
+			return -EINVAL;
+		break;
+	default:
+		pr_err("invalid param type %u specified\n", idx);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#define Q_CHK_INVALID(idx, q_spec, val) \
+	(qpnp_pin_check_config(idx, q_spec, val) == -EINVAL)
+
+static int qpnp_pin_check_constraints(struct qpnp_pin_spec *q_spec,
+				      struct qpnp_pin_cfg *param)
+{
+	int pin = q_spec->pmic_pin;
+	const char *name;
+
+	name = (q_spec->type == Q_GPIO_TYPE) ? "gpio" : "mpp";
+
+	if (Q_CHK_INVALID(Q_PIN_CFG_MODE, q_spec, param->mode))
+		pr_err("invalid direction value %d for %s %d\n",
+						param->mode, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_INVERT, q_spec, param->invert))
+		pr_err("invalid invert polarity value %d for %s %d\n",
+						param->invert,  name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_SRC_SEL, q_spec, param->src_sel))
+		pr_err("invalid source select value %d for %s %d\n",
+						param->src_sel, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_OUT_STRENGTH,
+						q_spec, param->out_strength))
+		pr_err("invalid out strength value %d for %s %d\n",
+					param->out_strength,  name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_OUTPUT_TYPE,
+						 q_spec, param->output_type))
+		pr_err("invalid out type value %d for %s %d\n",
+					param->output_type,  name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_VIN_SEL, q_spec, param->vin_sel))
+		pr_err("invalid vin select %d value for %s %d\n",
+						param->vin_sel, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_PULL, q_spec, param->pull))
+		pr_err("invalid pull value %d for pin %s %d\n",
+						param->pull,  name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_MASTER_EN, q_spec, param->master_en))
+		pr_err("invalid master_en value %d for %s %d\n",
+						param->master_en, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_AOUT_REF, q_spec, param->aout_ref))
+		pr_err("invalid aout_reg value %d for %s %d\n",
+						param->aout_ref, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_AIN_ROUTE, q_spec, param->ain_route))
+		pr_err("invalid ain_route value %d for %s %d\n",
+						param->ain_route, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_CS_OUT, q_spec, param->cs_out))
+		pr_err("invalid cs_out value %d for %s %d\n",
+						param->cs_out, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_APASS_SEL, q_spec, param->apass_sel))
+		pr_err("invalid apass_sel value %d for %s %d\n",
+						param->apass_sel, name, pin);
+	else if (Q_CHK_INVALID(Q_PIN_CFG_DTEST_SEL, q_spec, param->dtest_sel))
+		pr_err("invalid dtest_sel value %d for %s %d\n",
+					param->dtest_sel, name, pin);
+	else
+		return 0;
+
+	return -EINVAL;
+}
+
+static inline u8 q_reg_get(u8 *reg, int shift, int mask)
+{
+	return (*reg & mask) >> shift;
+}
+
+static inline void q_reg_set(u8 *reg, int shift, int mask, int value)
+{
+	*reg |= (value << shift) & mask;
+}
+
+static inline void q_reg_clr_set(u8 *reg, int shift, int mask, int value)
+{
+	*reg &= ~mask;
+	*reg |= (value << shift) & mask;
+}
+
+/*
+ * Calculate the minimum number of registers that must be read / written
+ * in order to satisfy the full feature set of the given pin.
+ */
+static int qpnp_pin_ctl_regs_init(struct qpnp_pin_spec *q_spec)
+{
+	if (q_spec->type == Q_GPIO_TYPE) {
+		if (is_gpio_lv_mv(q_spec))
+			q_spec->num_ctl_regs = 11;
+		else
+			q_spec->num_ctl_regs = 7;
+	} else if (q_spec->type == Q_MPP_TYPE) {
+		switch (q_spec->subtype) {
+		case Q_MPP_SUBTYPE_4CH_NO_SINK:
+		case Q_MPP_SUBTYPE_ULT_4CH_NO_SINK:
+			q_spec->num_ctl_regs = 12;
+			break;
+		case Q_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+		case Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
+		case Q_MPP_SUBTYPE_4CH_FULL_FUNC:
+		case Q_MPP_SUBTYPE_8CH_FULL_FUNC:
+			q_spec->num_ctl_regs = 13;
+			break;
+		default:
+			pr_err("Invalid MPP subtype 0x%x\n", q_spec->subtype);
+			return -EINVAL;
+		}
+	} else {
+		pr_err("Invalid type 0x%x\n", q_spec->type);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int qpnp_pin_read_regs(struct qpnp_pin_chip *q_chip,
+			      struct qpnp_pin_spec *q_spec)
+{
+	int bytes_left = q_spec->num_ctl_regs;
+	int rc;
+	char *buf_p = &q_spec->regs[0];
+	u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
+
+	while (bytes_left > 0) {
+		rc = regmap_bulk_read(q_chip->regmap, reg_addr, buf_p,
+				      bytes_left < 8 ? bytes_left : 8);
+		if (rc)
+			return rc;
+		bytes_left -= 8;
+		buf_p += 8;
+		reg_addr += 8;
+	}
+	return 0;
+}
+
+static int qpnp_pin_write_regs(struct qpnp_pin_chip *q_chip,
+			       struct qpnp_pin_spec *q_spec)
+{
+	int bytes_left = q_spec->num_ctl_regs;
+	int rc;
+	char *buf_p = &q_spec->regs[0];
+	u16 reg_addr = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
+
+	while (bytes_left > 0) {
+		rc = regmap_bulk_write(q_chip->regmap, reg_addr, buf_p,
+				       bytes_left < 8 ? bytes_left : 8);
+		if (rc)
+			return rc;
+		bytes_left -= 8;
+		buf_p += 8;
+		reg_addr += 8;
+	}
+	return 0;
+}
+
+static int qpnp_pin_cache_regs(struct qpnp_pin_chip *q_chip,
+			       struct qpnp_pin_spec *q_spec)
+{
+	int rc;
+	struct device *dev = &q_chip->pdev->dev;
+
+	rc = qpnp_pin_read_regs(q_chip, q_spec);
+	if (rc)
+		dev_err(dev, "%s: unable to read control regs\n", __func__);
+
+	return rc;
+}
+
+#define Q_HAVE_HW_SP(idx, q_spec, val) \
+	(qpnp_pin_check_config(idx, q_spec, val) == 0)
+
+static int _qpnp_pin_config(struct qpnp_pin_chip *q_chip,
+			    struct qpnp_pin_spec *q_spec,
+			    struct qpnp_pin_cfg *param)
+{
+	struct device *dev = &q_chip->pdev->dev;
+	int rc;
+	u8 shift, mask, *reg;
+
+	rc = qpnp_pin_check_constraints(q_spec, param);
+	if (rc)
+		goto gpio_cfg;
+
+	/* set mode */
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_MODE, q_spec, param->mode)) {
+		if (is_gpio_lv_mv(q_spec)) {
+			shift = Q_REG_LV_MV_MODE_SEL_SHIFT;
+			mask = Q_REG_LV_MV_MODE_SEL_MASK;
+		} else {
+			shift = Q_REG_MODE_SEL_SHIFT;
+			mask = Q_REG_MODE_SEL_MASK;
+		}
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL],
+			shift, mask, param->mode);
+	}
+
+	/* output specific configuration */
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_INVERT, q_spec, param->invert)) {
+		if (is_gpio_lv_mv(q_spec)) {
+			shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
+			mask = Q_REG_DIG_OUT_SRC_INVERT_MASK;
+			reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL];
+		} else {
+			shift = Q_REG_OUT_INVERT_SHIFT;
+			mask = Q_REG_OUT_INVERT_MASK;
+			reg = &q_spec->regs[Q_REG_I_MODE_CTL];
+		}
+		q_reg_clr_set(reg, shift, mask, param->invert);
+	}
+
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_SRC_SEL, q_spec, param->src_sel)) {
+		if (is_gpio_lv_mv(q_spec)) {
+			shift = Q_REG_DIG_OUT_SRC_SRC_SEL_SHIFT;
+			mask = Q_REG_DIG_OUT_SRC_SRC_SEL_MASK;
+			reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL];
+		} else {
+			shift = Q_REG_SRC_SEL_SHIFT;
+			mask = Q_REG_SRC_SEL_MASK;
+			reg = &q_spec->regs[Q_REG_I_MODE_CTL];
+		}
+		q_reg_clr_set(reg, shift, mask, param->src_sel);
+	}
+
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_OUT_STRENGTH, q_spec, param->out_strength))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+			  Q_REG_OUT_STRENGTH_SHIFT, Q_REG_OUT_STRENGTH_MASK,
+			  param->out_strength);
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_OUTPUT_TYPE, q_spec, param->output_type))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+			  Q_REG_OUT_TYPE_SHIFT, Q_REG_OUT_TYPE_MASK,
+			  param->output_type);
+
+	/* input config */
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_DTEST_SEL, q_spec, param->dtest_sel)
+			&& param->dtest_sel) {
+		if (is_gpio_lv_mv(q_spec)) {
+			q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_IN_CTL],
+					Q_REG_LV_MV_DTEST_SEL_CFG_SHIFT,
+					Q_REG_LV_MV_DTEST_SEL_CFG_MASK,
+					param->dtest_sel - 1);
+			q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_IN_CTL],
+					Q_REG_LV_MV_DTEST_SEL_EN_SHIFT,
+					Q_REG_LV_MV_DTEST_SEL_EN_MASK, 0x1);
+		} else {
+			q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_IN_CTL],
+					Q_REG_DTEST_SEL_SHIFT,
+					Q_REG_DTEST_SEL_MASK,
+					BIT(param->dtest_sel - 1));
+		}
+	}
+
+	/* config applicable for both input / output */
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_VIN_SEL, q_spec, param->vin_sel))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
+			  Q_REG_VIN_SHIFT, Q_REG_VIN_MASK,
+			  param->vin_sel);
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_PULL, q_spec, param->pull))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_DIG_PULL_CTL],
+			  Q_REG_PULL_SHIFT, Q_REG_PULL_MASK,
+			  param->pull);
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_MASTER_EN, q_spec, param->master_en))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_EN_CTL],
+			  Q_REG_MASTER_EN_SHIFT, Q_REG_MASTER_EN_MASK,
+			  param->master_en);
+
+	/* mpp specific config */
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_AOUT_REF, q_spec, param->aout_ref))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_AOUT_CTL],
+			  Q_REG_AOUT_REF_SHIFT, Q_REG_AOUT_REF_MASK,
+			  param->aout_ref);
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_AIN_ROUTE, q_spec, param->ain_route))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_AIN_CTL],
+			  Q_REG_AIN_ROUTE_SHIFT, Q_REG_AIN_ROUTE_MASK,
+			  param->ain_route);
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_CS_OUT, q_spec, param->cs_out))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_SINK_CTL],
+			  Q_REG_CS_OUT_SHIFT, Q_REG_CS_OUT_MASK,
+			  param->cs_out);
+	if (Q_HAVE_HW_SP(Q_PIN_CFG_APASS_SEL, q_spec, param->apass_sel))
+		q_reg_clr_set(&q_spec->regs[Q_REG_I_APASS_SEL_CTL],
+			  Q_REG_APASS_SEL_SHIFT, Q_REG_APASS_SEL_MASK,
+			  param->apass_sel);
+
+	rc = qpnp_pin_write_regs(q_chip, q_spec);
+	if (rc) {
+		dev_err(&q_chip->pdev->dev,
+			"%s: unable to write master enable\n",
+								__func__);
+		goto gpio_cfg;
+	}
+
+	return 0;
+
+gpio_cfg:
+	dev_err(dev, "%s: unable to set default config for pmic pin %d\n",
+						__func__, q_spec->pmic_pin);
+
+	return rc;
+}
+
+int qpnp_pin_config(int gpio, struct qpnp_pin_cfg *param)
+{
+	int rc, chip_offset;
+	struct qpnp_pin_chip *q_chip;
+	struct qpnp_pin_spec *q_spec = NULL;
+	struct gpio_chip *gpio_chip;
+
+	if (param == NULL)
+		return -EINVAL;
+
+	mutex_lock(&qpnp_pin_chips_lock);
+	list_for_each_entry(q_chip, &qpnp_pin_chips, chip_list) {
+		gpio_chip = &q_chip->gpio_chip;
+		if (gpio >= gpio_chip->base
+				&& gpio < gpio_chip->base + gpio_chip->ngpio) {
+			chip_offset = gpio - gpio_chip->base;
+			q_spec = qpnp_chip_gpio_get_spec(q_chip, chip_offset);
+			if (WARN_ON(!q_spec)) {
+				mutex_unlock(&qpnp_pin_chips_lock);
+				return -ENODEV;
+			}
+			break;
+		}
+	}
+	mutex_unlock(&qpnp_pin_chips_lock);
+
+	if (!q_spec)
+		return -ENODEV;
+
+	rc = _qpnp_pin_config(q_chip, q_spec, param);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_pin_config);
+
+int qpnp_pin_map(const char *name, uint32_t pmic_pin)
+{
+	struct qpnp_pin_chip *q_chip;
+	struct qpnp_pin_spec *q_spec = NULL;
+
+	if (!name)
+		return -EINVAL;
+
+	mutex_lock(&qpnp_pin_chips_lock);
+	list_for_each_entry(q_chip, &qpnp_pin_chips, chip_list) {
+		if (strcmp(q_chip->gpio_chip.label, name) != 0)
+			continue;
+		if (q_chip->pmic_pin_lowest <= pmic_pin &&
+		    q_chip->pmic_pin_highest >= pmic_pin) {
+			q_spec = qpnp_pmic_pin_get_spec(q_chip, pmic_pin);
+			mutex_unlock(&qpnp_pin_chips_lock);
+			if (WARN_ON(!q_spec))
+				return -ENODEV;
+			return q_chip->gpio_chip.base + q_spec->gpio_chip_idx;
+		}
+	}
+	mutex_unlock(&qpnp_pin_chips_lock);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(qpnp_pin_map);
+
+static int qpnp_pin_to_irq(struct gpio_chip *gpio_chip, unsigned int offset)
+{
+	struct qpnp_pin_chip *q_chip = gpiochip_get_data(gpio_chip);
+	struct qpnp_pin_spec *q_spec;
+	struct of_phandle_args oirq;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (!q_spec)
+		return -EINVAL;
+
+	/* if we have mapped this pin previously return the virq */
+	if (q_spec->irq)
+		return q_spec->irq;
+
+	/* call into irq_domain to get irq mapping */
+	oirq.np = q_chip->int_ctrl;
+	oirq.args[0] = to_spmi_device(q_chip->pdev->dev.parent)->usid;
+	oirq.args[1] = (q_spec->offset >> 8) & 0xFF;
+	oirq.args[2] = 0;
+	oirq.args[3] = IRQ_TYPE_NONE;
+	oirq.args_count = 4;
+
+	q_spec->irq = irq_create_of_mapping(&oirq);
+	if (!q_spec->irq) {
+		dev_err(&q_chip->pdev->dev, "%s: invalid irq for gpio %u\n",
+						__func__, q_spec->pmic_pin);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	return q_spec->irq;
+}
+
+static int qpnp_pin_get(struct gpio_chip *gpio_chip, unsigned int offset)
+{
+	struct qpnp_pin_chip *q_chip = gpiochip_get_data(gpio_chip);
+	struct qpnp_pin_spec *q_spec = NULL;
+	u8 buf, en_mask, shift, mask, reg;
+	unsigned int val;
+	int rc;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	if (is_gpio_lv_mv(q_spec)) {
+		mask = Q_REG_LV_MV_MODE_SEL_MASK;
+		shift = Q_REG_LV_MV_MODE_SEL_SHIFT;
+	} else {
+		mask = Q_REG_MODE_SEL_MASK;
+		shift = Q_REG_MODE_SEL_SHIFT;
+	}
+
+	/* gpio val is from RT status iff input is enabled */
+	if (q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL], shift, mask)
+					== QPNP_PIN_MODE_DIG_IN) {
+		rc = regmap_read(q_chip->regmap,
+				 Q_REG_ADDR(q_spec, Q_REG_STATUS1), &val);
+		if (rc)
+			return rc;
+		buf = val;
+
+		if (q_spec->type == Q_GPIO_TYPE && q_spec->dig_major_rev == 0)
+			en_mask = Q_REG_STATUS1_GPIO_EN_REV0_MASK;
+		else if (q_spec->type == Q_GPIO_TYPE &&
+			 q_spec->dig_major_rev > 0)
+			en_mask = Q_REG_STATUS1_GPIO_EN_MASK;
+		else /* MPP */
+			en_mask = Q_REG_STATUS1_MPP_EN_MASK;
+
+		if (!(buf & en_mask))
+			return -EPERM;
+
+		return buf & Q_REG_STATUS1_VAL_MASK;
+	}
+
+	if (is_gpio_lv_mv(q_spec)) {
+		shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
+		mask = Q_REG_DIG_OUT_SRC_INVERT_MASK;
+		reg = q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL];
+	} else {
+		shift = Q_REG_OUT_INVERT_SHIFT;
+		mask = Q_REG_OUT_INVERT_MASK;
+		reg = q_spec->regs[Q_REG_I_MODE_CTL];
+	}
+
+	return (reg & mask) >> shift;
+}
+
+static int __qpnp_pin_set(struct qpnp_pin_chip *q_chip,
+			   struct qpnp_pin_spec *q_spec, int value)
+{
+	int rc;
+	u8 shift, mask, *reg;
+	u16 address;
+
+	if (!q_chip || !q_spec)
+		return -EINVAL;
+
+	if (is_gpio_lv_mv(q_spec)) {
+		shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
+		mask = Q_REG_DIG_OUT_SRC_INVERT_MASK;
+		reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL];
+		address = Q_REG_ADDR(q_spec, Q_REG_DIG_OUT_SRC_CTL);
+	} else {
+		shift = Q_REG_OUT_INVERT_SHIFT;
+		mask = Q_REG_OUT_INVERT_MASK;
+		reg = &q_spec->regs[Q_REG_I_MODE_CTL];
+		address = Q_REG_ADDR(q_spec, Q_REG_MODE_CTL);
+	}
+
+	q_reg_clr_set(reg, shift, mask, !!value);
+
+	rc = regmap_write(q_chip->regmap, address, *reg);
+	if (rc)
+		dev_err(&q_chip->pdev->dev, "%s: spmi write failed\n",
+								__func__);
+	return rc;
+}
+
+
+static void qpnp_pin_set(struct gpio_chip *gpio_chip,
+		unsigned int offset, int value)
+{
+	struct qpnp_pin_chip *q_chip = gpiochip_get_data(gpio_chip);
+	struct qpnp_pin_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return;
+
+	__qpnp_pin_set(q_chip, q_spec, value);
+}
+
+static int qpnp_pin_set_mode(struct qpnp_pin_chip *q_chip,
+				   struct qpnp_pin_spec *q_spec, int mode)
+{
+	int rc;
+	u8 shift, mask;
+
+	if (!q_chip || !q_spec)
+		return -EINVAL;
+
+	if (qpnp_pin_check_config(Q_PIN_CFG_MODE, q_spec, mode)) {
+		pr_err("invalid mode specification %d\n", mode);
+		return -EINVAL;
+	}
+
+	if (is_gpio_lv_mv(q_spec)) {
+		shift = Q_REG_LV_MV_MODE_SEL_SHIFT;
+		mask = Q_REG_LV_MV_MODE_SEL_MASK;
+	} else {
+		shift = Q_REG_MODE_SEL_SHIFT;
+		mask = Q_REG_MODE_SEL_MASK;
+	}
+
+	q_reg_clr_set(&q_spec->regs[Q_REG_I_MODE_CTL], shift, mask, mode);
+
+	rc = regmap_write(q_chip->regmap, Q_REG_ADDR(q_spec, Q_REG_MODE_CTL),
+			  *&q_spec->regs[Q_REG_I_MODE_CTL]);
+	return rc;
+}
+
+static int qpnp_pin_direction_input(struct gpio_chip *gpio_chip,
+		unsigned int offset)
+{
+	struct qpnp_pin_chip *q_chip = gpiochip_get_data(gpio_chip);
+	struct qpnp_pin_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	return qpnp_pin_set_mode(q_chip, q_spec, QPNP_PIN_MODE_DIG_IN);
+}
+
+static int qpnp_pin_direction_output(struct gpio_chip *gpio_chip,
+		unsigned int offset, int val)
+{
+	int rc;
+	struct qpnp_pin_chip *q_chip = gpiochip_get_data(gpio_chip);
+	struct qpnp_pin_spec *q_spec;
+
+	if (WARN_ON(!q_chip))
+		return -ENODEV;
+
+	q_spec = qpnp_chip_gpio_get_spec(q_chip, offset);
+	if (WARN_ON(!q_spec))
+		return -ENODEV;
+
+	rc = __qpnp_pin_set(q_chip, q_spec, val);
+	if (rc)
+		return rc;
+
+	rc = qpnp_pin_set_mode(q_chip, q_spec, QPNP_PIN_MODE_DIG_OUT);
+
+	return rc;
+}
+
+static int qpnp_pin_of_gpio_xlate(struct gpio_chip *gpio_chip,
+				   const struct of_phandle_args *gpio_spec,
+				   u32 *flags)
+{
+	struct qpnp_pin_chip *q_chip = gpiochip_get_data(gpio_chip);
+	struct qpnp_pin_spec *q_spec;
+
+	if (WARN_ON(gpio_chip->of_gpio_n_cells < 2)) {
+		pr_err("of_gpio_n_cells < 2\n");
+		return -EINVAL;
+	}
+
+	q_spec = qpnp_pmic_pin_get_spec(q_chip, gpio_spec->args[0]);
+	if (!q_spec) {
+		pr_err("no such PMIC gpio %u in device topology\n",
+							gpio_spec->args[0]);
+		return -EINVAL;
+	}
+
+	if (flags)
+		*flags = gpio_spec->args[1];
+
+	return q_spec->gpio_chip_idx;
+}
+
+static int qpnp_pin_apply_config(struct qpnp_pin_chip *q_chip,
+				  struct qpnp_pin_spec *q_spec)
+{
+	struct qpnp_pin_cfg param;
+	struct device_node *node = q_spec->node;
+	int rc;
+	u8 shift, mask, *reg;
+
+	if (is_gpio_lv_mv(q_spec)) {
+		shift = Q_REG_LV_MV_MODE_SEL_SHIFT;
+		mask = Q_REG_LV_MV_MODE_SEL_MASK;
+	} else {
+		shift = Q_REG_MODE_SEL_SHIFT;
+		mask = Q_REG_MODE_SEL_MASK;
+	}
+	param.mode	   = q_reg_get(&q_spec->regs[Q_REG_I_MODE_CTL],
+							shift, mask);
+
+	param.output_type  = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+				       Q_REG_OUT_TYPE_SHIFT,
+				       Q_REG_OUT_TYPE_MASK);
+
+	if (is_gpio_lv_mv(q_spec)) {
+		shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
+		mask = Q_REG_DIG_OUT_SRC_INVERT_MASK;
+		reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL];
+	} else {
+		shift = Q_REG_OUT_INVERT_SHIFT;
+		mask = Q_REG_OUT_INVERT_MASK;
+		reg = &q_spec->regs[Q_REG_I_MODE_CTL];
+	}
+	param.invert	   = q_reg_get(reg, shift, mask);
+
+	param.pull	   = q_reg_get(&q_spec->regs[Q_REG_I_DIG_PULL_CTL],
+				       Q_REG_PULL_SHIFT, Q_REG_PULL_MASK);
+	param.vin_sel	   = q_reg_get(&q_spec->regs[Q_REG_I_DIG_VIN_CTL],
+				       Q_REG_VIN_SHIFT, Q_REG_VIN_MASK);
+	param.out_strength = q_reg_get(&q_spec->regs[Q_REG_I_DIG_OUT_CTL],
+				       Q_REG_OUT_STRENGTH_SHIFT,
+				       Q_REG_OUT_STRENGTH_MASK);
+
+	if (is_gpio_lv_mv(q_spec)) {
+		shift = Q_REG_DIG_OUT_SRC_SRC_SEL_SHIFT;
+		mask = Q_REG_DIG_OUT_SRC_SRC_SEL_MASK;
+		reg = &q_spec->regs[Q_REG_I_DIG_OUT_SRC_CTL];
+	} else {
+		shift = Q_REG_SRC_SEL_SHIFT;
+		mask = Q_REG_SRC_SEL_MASK;
+		reg = &q_spec->regs[Q_REG_I_MODE_CTL];
+	}
+	param.src_sel   = q_reg_get(reg, shift, mask);
+
+	param.master_en    = q_reg_get(&q_spec->regs[Q_REG_I_EN_CTL],
+				       Q_REG_MASTER_EN_SHIFT,
+				       Q_REG_MASTER_EN_MASK);
+	param.aout_ref    = q_reg_get(&q_spec->regs[Q_REG_I_AOUT_CTL],
+				       Q_REG_AOUT_REF_SHIFT,
+				       Q_REG_AOUT_REF_MASK);
+	param.ain_route    = q_reg_get(&q_spec->regs[Q_REG_I_AIN_CTL],
+				       Q_REG_AIN_ROUTE_SHIFT,
+				       Q_REG_AIN_ROUTE_MASK);
+	param.cs_out    = q_reg_get(&q_spec->regs[Q_REG_I_SINK_CTL],
+				       Q_REG_CS_OUT_SHIFT,
+				       Q_REG_CS_OUT_MASK);
+	param.apass_sel    = q_reg_get(&q_spec->regs[Q_REG_I_APASS_SEL_CTL],
+				       Q_REG_APASS_SEL_SHIFT,
+				       Q_REG_APASS_SEL_MASK);
+	if (is_gpio_lv_mv(q_spec)) {
+		param.dtest_sel = q_reg_get(&q_spec->regs[Q_REG_I_DIG_IN_CTL],
+				Q_REG_LV_MV_DTEST_SEL_CFG_SHIFT,
+				Q_REG_LV_MV_DTEST_SEL_CFG_MASK);
+	} else {
+		 param.dtest_sel = q_reg_get(&q_spec->regs[Q_REG_I_DIG_IN_CTL],
+				Q_REG_DTEST_SEL_SHIFT,
+				Q_REG_DTEST_SEL_MASK);
+	}
+
+	of_property_read_u32(node, "qcom,mode",
+		&param.mode);
+	of_property_read_u32(node, "qcom,output-type",
+		&param.output_type);
+	of_property_read_u32(node, "qcom,invert",
+		&param.invert);
+	of_property_read_u32(node, "qcom,pull",
+		&param.pull);
+	of_property_read_u32(node, "qcom,vin-sel",
+		&param.vin_sel);
+	of_property_read_u32(node, "qcom,out-strength",
+		&param.out_strength);
+	of_property_read_u32(node, "qcom,src-sel",
+		&param.src_sel);
+	of_property_read_u32(node, "qcom,master-en",
+		&param.master_en);
+	of_property_read_u32(node, "qcom,aout-ref",
+		&param.aout_ref);
+	of_property_read_u32(node, "qcom,ain-route",
+		&param.ain_route);
+	of_property_read_u32(node, "qcom,cs-out",
+		&param.cs_out);
+	of_property_read_u32(node, "qcom,apass-sel",
+		&param.apass_sel);
+	of_property_read_u32(node, "qcom,dtest-sel",
+		&param.dtest_sel);
+
+	rc = _qpnp_pin_config(q_chip, q_spec, &param);
+
+	return rc;
+}
+
+static int qpnp_pin_free_chip(struct qpnp_pin_chip *q_chip)
+{
+	int i, rc = 0;
+
+	if (q_chip->chip_gpios)
+		for (i = 0; i < q_chip->gpio_chip.ngpio; i++)
+			kfree(q_chip->chip_gpios[i]);
+
+	mutex_lock(&qpnp_pin_chips_lock);
+	list_del(&q_chip->chip_list);
+	mutex_unlock(&qpnp_pin_chips_lock);
+	if (q_chip->chip_registered)
+		gpiochip_remove(&q_chip->gpio_chip);
+
+	kfree(q_chip->chip_gpios);
+	kfree(q_chip->pmic_pins);
+	kfree(q_chip);
+	return rc;
+}
+
+#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
+struct qpnp_pin_reg {
+	uint32_t addr;
+	uint32_t idx;
+	uint32_t shift;
+	uint32_t mask;
+};
+
+static struct dentry *driver_dfs_dir;
+
+static int qpnp_pin_reg_attr(enum qpnp_pin_param_type type,
+			     struct qpnp_pin_reg *cfg,
+			struct qpnp_pin_spec *q_spec)
+{
+	switch (type) {
+	case Q_PIN_CFG_MODE:
+		if (is_gpio_lv_mv(q_spec)) {
+			cfg->shift = Q_REG_LV_MV_MODE_SEL_SHIFT;
+			cfg->mask = Q_REG_LV_MV_MODE_SEL_MASK;
+		} else {
+			cfg->shift = Q_REG_MODE_SEL_SHIFT;
+			cfg->mask = Q_REG_MODE_SEL_MASK;
+		}
+		cfg->addr = Q_REG_MODE_CTL;
+		cfg->idx = Q_REG_I_MODE_CTL;
+		break;
+	case Q_PIN_CFG_OUTPUT_TYPE:
+		cfg->addr = Q_REG_DIG_OUT_CTL;
+		cfg->idx = Q_REG_I_DIG_OUT_CTL;
+		cfg->shift = Q_REG_OUT_TYPE_SHIFT;
+		cfg->mask = Q_REG_OUT_TYPE_MASK;
+		break;
+	case Q_PIN_CFG_INVERT:
+		if (is_gpio_lv_mv(q_spec)) {
+			cfg->addr = Q_REG_DIG_OUT_SRC_CTL;
+			cfg->idx = Q_REG_I_DIG_OUT_SRC_CTL;
+			cfg->shift = Q_REG_DIG_OUT_SRC_INVERT_SHIFT;
+			cfg->mask = Q_REG_DIG_OUT_SRC_INVERT_MASK;
+		} else {
+			cfg->addr = Q_REG_MODE_CTL;
+			cfg->idx = Q_REG_I_MODE_CTL;
+			cfg->shift = Q_REG_OUT_INVERT_SHIFT;
+			cfg->mask = Q_REG_OUT_INVERT_MASK;
+		}
+		break;
+	case Q_PIN_CFG_PULL:
+		cfg->addr = Q_REG_DIG_PULL_CTL;
+		cfg->idx = Q_REG_I_DIG_PULL_CTL;
+		cfg->shift = Q_REG_PULL_SHIFT;
+		cfg->mask = Q_REG_PULL_MASK;
+		break;
+	case Q_PIN_CFG_VIN_SEL:
+		cfg->addr = Q_REG_DIG_VIN_CTL;
+		cfg->idx = Q_REG_I_DIG_VIN_CTL;
+		cfg->shift = Q_REG_VIN_SHIFT;
+		cfg->mask = Q_REG_VIN_MASK;
+		break;
+	case Q_PIN_CFG_OUT_STRENGTH:
+		cfg->addr = Q_REG_DIG_OUT_CTL;
+		cfg->idx = Q_REG_I_DIG_OUT_CTL;
+		cfg->shift = Q_REG_OUT_STRENGTH_SHIFT;
+		cfg->mask = Q_REG_OUT_STRENGTH_MASK;
+		break;
+	case Q_PIN_CFG_SRC_SEL:
+		if (is_gpio_lv_mv(q_spec)) {
+			cfg->addr = Q_REG_DIG_OUT_SRC_CTL;
+			cfg->idx = Q_REG_I_DIG_OUT_SRC_CTL;
+			cfg->shift = Q_REG_DIG_OUT_SRC_SRC_SEL_SHIFT;
+			cfg->mask = Q_REG_DIG_OUT_SRC_SRC_SEL_MASK;
+		} else {
+			cfg->addr = Q_REG_MODE_CTL;
+			cfg->idx = Q_REG_I_MODE_CTL;
+			cfg->shift = Q_REG_SRC_SEL_SHIFT;
+			cfg->mask = Q_REG_SRC_SEL_MASK;
+		}
+		break;
+	case Q_PIN_CFG_MASTER_EN:
+		cfg->addr = Q_REG_EN_CTL;
+		cfg->idx = Q_REG_I_EN_CTL;
+		cfg->shift = Q_REG_MASTER_EN_SHIFT;
+		cfg->mask = Q_REG_MASTER_EN_MASK;
+		break;
+	case Q_PIN_CFG_AOUT_REF:
+		cfg->addr = Q_REG_AOUT_CTL;
+		cfg->idx = Q_REG_I_AOUT_CTL;
+		cfg->shift = Q_REG_AOUT_REF_SHIFT;
+		cfg->mask = Q_REG_AOUT_REF_MASK;
+		break;
+	case Q_PIN_CFG_AIN_ROUTE:
+		cfg->addr = Q_REG_AIN_CTL;
+		cfg->idx = Q_REG_I_AIN_CTL;
+		cfg->shift = Q_REG_AIN_ROUTE_SHIFT;
+		cfg->mask = Q_REG_AIN_ROUTE_MASK;
+		break;
+	case Q_PIN_CFG_CS_OUT:
+		cfg->addr = Q_REG_SINK_CTL;
+		cfg->idx = Q_REG_I_SINK_CTL;
+		cfg->shift = Q_REG_CS_OUT_SHIFT;
+		cfg->mask = Q_REG_CS_OUT_MASK;
+		break;
+	case Q_PIN_CFG_APASS_SEL:
+		cfg->addr = Q_REG_APASS_SEL_CTL;
+		cfg->idx = Q_REG_I_APASS_SEL_CTL;
+		cfg->shift = Q_REG_APASS_SEL_SHIFT;
+		cfg->mask = Q_REG_APASS_SEL_MASK;
+		break;
+	case Q_PIN_CFG_DTEST_SEL:
+		if (is_gpio_lv_mv(q_spec)) {
+			cfg->shift = Q_REG_LV_MV_DTEST_SEL_CFG_SHIFT;
+			cfg->mask = Q_REG_LV_MV_DTEST_SEL_CFG_MASK;
+		} else {
+			cfg->shift = Q_REG_DTEST_SEL_SHIFT;
+			cfg->mask = Q_REG_DTEST_SEL_MASK;
+		}
+		cfg->addr = Q_REG_DIG_IN_CTL;
+		cfg->idx = Q_REG_I_DIG_IN_CTL;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_pin_debugfs_get(void *data, u64 *val)
+{
+	enum qpnp_pin_param_type *idx = data;
+	struct qpnp_pin_spec *q_spec;
+	struct qpnp_pin_reg cfg = {};
+	int rc;
+
+	q_spec = container_of(idx, struct qpnp_pin_spec, params[*idx]);
+
+	rc = qpnp_pin_reg_attr(*idx, &cfg, q_spec);
+	if (rc)
+		return rc;
+
+	*val = q_reg_get(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask);
+	return 0;
+}
+
+static int qpnp_pin_debugfs_set(void *data, u64 val)
+{
+	enum qpnp_pin_param_type *idx = data;
+	struct qpnp_pin_spec *q_spec;
+	struct qpnp_pin_chip *q_chip;
+	struct qpnp_pin_reg cfg = {};
+	int rc;
+
+	q_spec = container_of(idx, struct qpnp_pin_spec, params[*idx]);
+	q_chip = q_spec->q_chip;
+
+	/*
+	 * special handling for GPIO_LV/MV 'dtest-sel'
+	 * if (dtest_sel == 0) then disable dtest-sel
+	 * else enable and set dtest.
+	 */
+	if ((q_spec->subtype == Q_GPIO_SUBTYPE_GPIO_LV ||
+		q_spec->subtype == Q_GPIO_SUBTYPE_GPIO_MV) &&
+				*idx == Q_PIN_CFG_DTEST_SEL) {
+		/* enable/disable DTEST */
+		cfg.shift = Q_REG_LV_MV_DTEST_SEL_EN_SHIFT;
+		cfg.mask = Q_REG_LV_MV_DTEST_SEL_EN_MASK;
+		cfg.addr = Q_REG_DIG_IN_CTL;
+		cfg.idx = Q_REG_I_DIG_IN_CTL;
+		q_reg_clr_set(&q_spec->regs[cfg.idx],
+				cfg.shift, cfg.mask, !!val);
+	}
+
+	rc = qpnp_pin_check_config(*idx, q_spec, val);
+	if (rc)
+		return rc;
+
+	rc = qpnp_pin_reg_attr(*idx, &cfg, q_spec);
+	if (rc)
+		return rc;
+
+	if (*idx == Q_PIN_CFG_DTEST_SEL && val)  {
+		if (is_gpio_lv_mv(q_spec))
+			val -= 1;
+		else
+			val = BIT(val - 1);
+	}
+
+	q_reg_clr_set(&q_spec->regs[cfg.idx], cfg.shift, cfg.mask, val);
+	rc = regmap_write(q_chip->regmap, Q_REG_ADDR(q_spec, cfg.addr),
+			  *&q_spec->regs[cfg.idx]);
+
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(qpnp_pin_fops, qpnp_pin_debugfs_get,
+			qpnp_pin_debugfs_set, "%llu\n");
+
+#define DEBUGFS_BUF_SIZE 11 /* supports 2^32 in decimal */
+
+struct qpnp_pin_debugfs_args {
+	enum qpnp_pin_param_type type;
+	const char *filename;
+};
+
+static struct qpnp_pin_debugfs_args dfs_args[Q_NUM_PARAMS] = {
+	{ Q_PIN_CFG_MODE, "mode" },
+	{ Q_PIN_CFG_OUTPUT_TYPE, "output_type" },
+	{ Q_PIN_CFG_INVERT, "invert" },
+	{ Q_PIN_CFG_PULL, "pull" },
+	{ Q_PIN_CFG_VIN_SEL, "vin_sel" },
+	{ Q_PIN_CFG_OUT_STRENGTH, "out_strength" },
+	{ Q_PIN_CFG_SRC_SEL, "src_sel" },
+	{ Q_PIN_CFG_MASTER_EN, "master_en" },
+	{ Q_PIN_CFG_AOUT_REF, "aout_ref" },
+	{ Q_PIN_CFG_AIN_ROUTE, "ain_route" },
+	{ Q_PIN_CFG_CS_OUT, "cs_out" },
+	{ Q_PIN_CFG_APASS_SEL, "apass_sel" },
+	{ Q_PIN_CFG_DTEST_SEL, "dtest-sel" },
+};
+
+static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
+{
+	struct platform_device *pdev = q_chip->pdev;
+	struct device *dev = &pdev->dev;
+	struct qpnp_pin_spec *q_spec;
+	enum qpnp_pin_param_type *params;
+	enum qpnp_pin_param_type type;
+	char pmic_pin[DEBUGFS_BUF_SIZE];
+	const char *filename;
+	struct dentry *dfs, *dfs_io_dir;
+	int i, j, rc;
+
+	q_chip->dfs_dir = debugfs_create_dir(q_chip->gpio_chip.label,
+							driver_dfs_dir);
+	if (q_chip->dfs_dir == NULL) {
+		dev_err(dev, "%s: cannot register chip debugfs directory %s\n",
+						__func__, dev->of_node->name);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < q_chip->gpio_chip.ngpio; i++) {
+		q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
+		params = q_spec->params;
+		snprintf(pmic_pin, DEBUGFS_BUF_SIZE, "%u", q_spec->pmic_pin);
+		dfs_io_dir = debugfs_create_dir(pmic_pin, q_chip->dfs_dir);
+		if (dfs_io_dir == NULL)
+			goto dfs_err;
+
+		for (j = 0; j < Q_NUM_PARAMS; j++) {
+			type = dfs_args[j].type;
+			filename = dfs_args[j].filename;
+
+			/*
+			 * Use a value of '0' to see if the pin has even basic
+			 * support for a function. Do not create a file if
+			 * it doesn't.
+			 */
+			rc = qpnp_pin_check_config(type, q_spec, 0);
+			if (rc == -ENXIO)
+				continue;
+
+			params[type] = type;
+			dfs = debugfs_create_file(filename, 0644, dfs_io_dir,
+					&q_spec->params[type], &qpnp_pin_fops);
+			if (dfs == NULL)
+				goto dfs_err;
+		}
+	}
+	return 0;
+dfs_err:
+	dev_err(dev, "%s: cannot register debugfs for pmic gpio %u on chip %s\n",
+			__func__, q_spec->pmic_pin, dev->of_node->name);
+	debugfs_remove_recursive(q_chip->dfs_dir);
+	return -ENFILE;
+}
+#else
+static int qpnp_pin_debugfs_create(struct qpnp_pin_chip *q_chip)
+{
+	return 0;
+}
+#endif
+
+static int qpnp_pin_is_valid_pin(struct qpnp_pin_spec *q_spec)
+{
+	if (q_spec->type == Q_GPIO_TYPE)
+		switch (q_spec->subtype) {
+		case Q_GPIO_SUBTYPE_GPIO_4CH:
+		case Q_GPIO_SUBTYPE_GPIOC_4CH:
+		case Q_GPIO_SUBTYPE_GPIO_8CH:
+		case Q_GPIO_SUBTYPE_GPIOC_8CH:
+		case Q_GPIO_SUBTYPE_GPIO_LV:
+		case Q_GPIO_SUBTYPE_GPIO_MV:
+			return 1;
+		}
+	else if (q_spec->type == Q_MPP_TYPE)
+		switch (q_spec->subtype) {
+		case Q_MPP_SUBTYPE_4CH_NO_ANA_OUT:
+		case Q_MPP_SUBTYPE_ULT_4CH_NO_ANA_OUT:
+		case Q_MPP_SUBTYPE_4CH_NO_SINK:
+		case Q_MPP_SUBTYPE_ULT_4CH_NO_SINK:
+		case Q_MPP_SUBTYPE_4CH_FULL_FUNC:
+		case Q_MPP_SUBTYPE_8CH_FULL_FUNC:
+			return 1;
+		}
+
+	return 0;
+}
+
+static int qpnp_pin_probe(struct platform_device *pdev)
+{
+	struct qpnp_pin_chip *q_chip;
+	struct qpnp_pin_spec *q_spec;
+	unsigned int base;
+	struct device_node *child;
+	int i, rc;
+	u32 lowest_gpio = UINT_MAX, highest_gpio = 0;
+	u32 gpio;
+	char version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV + 1];
+	const char *pin_dev_name;
+
+	pin_dev_name = dev_name(&pdev->dev);
+	if (!pin_dev_name) {
+		dev_err(&pdev->dev,
+			"%s: label binding undefined for node %s\n",
+					__func__,
+			pdev->dev.of_node->full_name);
+		return -EINVAL;
+	}
+
+	q_chip = kzalloc(sizeof(*q_chip), GFP_KERNEL);
+	if (!q_chip)
+		return -ENOMEM;
+
+	q_chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!q_chip->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+	q_chip->pdev = pdev;
+	dev_set_drvdata(&pdev->dev, q_chip);
+
+	mutex_lock(&qpnp_pin_chips_lock);
+	list_add(&q_chip->chip_list, &qpnp_pin_chips);
+	mutex_unlock(&qpnp_pin_chips_lock);
+
+	/* first scan through nodes to find the range required for allocation */
+	i = 0;
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "qcom,pin-num", &gpio);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"%s: unable to get qcom,pin-num property\n",
+								__func__);
+			goto err_probe;
+		}
+
+		if (gpio < lowest_gpio)
+			lowest_gpio = gpio;
+		if (gpio > highest_gpio)
+			highest_gpio = gpio;
+		i++;
+	}
+	q_chip->gpio_chip.ngpio = i;
+
+	if (highest_gpio < lowest_gpio) {
+		dev_err(&pdev->dev,
+			"%s: no device nodes specified in topology\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	} else if (lowest_gpio == 0) {
+		dev_err(&pdev->dev, "%s: 0 is not a valid PMIC GPIO\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	}
+
+	q_chip->pmic_pin_lowest = lowest_gpio;
+	q_chip->pmic_pin_highest = highest_gpio;
+
+	/* allocate gpio lookup tables */
+	q_chip->pmic_pins = kzalloc(sizeof(struct qpnp_pin_spec *) *
+					(highest_gpio - lowest_gpio + 1),
+					GFP_KERNEL);
+	q_chip->chip_gpios = kzalloc(sizeof(struct qpnp_pin_spec *) *
+					q_chip->gpio_chip.ngpio,
+					GFP_KERNEL);
+	if (!q_chip->pmic_pins || !q_chip->chip_gpios) {
+		dev_err(&pdev->dev, "%s: unable to allocate memory\n",
+								__func__);
+		rc = -ENOMEM;
+		goto err_probe;
+	}
+
+	/* get interrupt controller device_node */
+	q_chip->int_ctrl = of_irq_find_parent(pdev->dev.of_node);
+	if (!q_chip->int_ctrl) {
+		dev_err(&pdev->dev, "%s: Can't find interrupt parent\n",
+								__func__);
+		rc = -EINVAL;
+		goto err_probe;
+	}
+	i = 0;
+	/* now scan through again and populate the lookup table */
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				child->full_name, rc);
+			goto err_probe;
+		}
+
+		rc = of_property_read_u32(child, "qcom,pin-num", &gpio);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"%s: unable to get qcom,pin-num property\n",
+								__func__);
+			goto err_probe;
+		}
+
+		q_spec = kzalloc(sizeof(struct qpnp_pin_spec), GFP_KERNEL);
+		if (!q_spec) {
+			rc = -ENOMEM;
+			goto err_probe;
+		}
+
+		q_spec->slave = to_spmi_device(pdev->dev.parent)->usid;
+		q_spec->offset = base;
+		q_spec->gpio_chip_idx = i;
+		q_spec->pmic_pin = gpio;
+		q_spec->node = child;
+		q_spec->q_chip = q_chip;
+
+		rc = regmap_bulk_read(q_chip->regmap,
+				Q_REG_ADDR(q_spec, Q_REG_DIG_MAJOR_REV),
+				&version[0],
+				ARRAY_SIZE(version));
+		if (rc) {
+			dev_err(&pdev->dev, "%s: unable to read type regs\n",
+						__func__);
+			goto err_probe;
+		}
+		q_spec->dig_major_rev = version[Q_REG_DIG_MAJOR_REV -
+						Q_REG_DIG_MAJOR_REV];
+		q_spec->type	= version[Q_REG_TYPE - Q_REG_DIG_MAJOR_REV];
+		q_spec->subtype = version[Q_REG_SUBTYPE - Q_REG_DIG_MAJOR_REV];
+
+		if (!qpnp_pin_is_valid_pin(q_spec)) {
+			dev_err(&pdev->dev,
+				"%s: invalid pin type (type=0x%x subtype=0x%x)\n",
+				       __func__, q_spec->type, q_spec->subtype);
+			goto err_probe;
+		}
+
+		rc = qpnp_pin_ctl_regs_init(q_spec);
+		if (rc)
+			goto err_probe;
+
+		/* initialize lookup table params */
+		qpnp_pmic_pin_set_spec(q_chip, gpio, q_spec);
+		qpnp_chip_gpio_set_spec(q_chip, i, q_spec);
+		i++;
+	}
+
+	q_chip->gpio_chip.base = -1;
+	q_chip->gpio_chip.label = pin_dev_name;
+	q_chip->gpio_chip.direction_input = qpnp_pin_direction_input;
+	q_chip->gpio_chip.direction_output = qpnp_pin_direction_output;
+	q_chip->gpio_chip.to_irq = qpnp_pin_to_irq;
+	q_chip->gpio_chip.get = qpnp_pin_get;
+	q_chip->gpio_chip.set = qpnp_pin_set;
+	q_chip->gpio_chip.parent = &pdev->dev;
+	q_chip->gpio_chip.of_xlate = qpnp_pin_of_gpio_xlate;
+	q_chip->gpio_chip.of_gpio_n_cells = 2;
+	q_chip->gpio_chip.can_sleep = 0;
+
+	rc = gpiochip_add_data(&q_chip->gpio_chip, q_chip);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Can't add gpio chip, rc = %d\n",
+								__func__, rc);
+		goto err_probe;
+	}
+
+	q_chip->chip_registered = true;
+	/* now configure gpio config defaults if they exist */
+	for (i = 0; i < q_chip->gpio_chip.ngpio; i++) {
+		q_spec = qpnp_chip_gpio_get_spec(q_chip, i);
+		if (WARN_ON(!q_spec)) {
+			rc = -ENODEV;
+			goto err_probe;
+		}
+
+		rc = qpnp_pin_cache_regs(q_chip, q_spec);
+		if (rc)
+			goto err_probe;
+
+		rc = qpnp_pin_apply_config(q_chip, q_spec);
+		if (rc)
+			goto err_probe;
+	}
+
+	dev_dbg(&pdev->dev, "%s: gpio_chip registered between %d-%u\n",
+			__func__, q_chip->gpio_chip.base,
+			(q_chip->gpio_chip.base + q_chip->gpio_chip.ngpio) - 1);
+
+	rc = qpnp_pin_debugfs_create(q_chip);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: debugfs creation failed\n",
+			__func__);
+		goto err_probe;
+	}
+
+	return 0;
+
+err_probe:
+	qpnp_pin_free_chip(q_chip);
+	return rc;
+}
+
+static int qpnp_pin_remove(struct platform_device *pdev)
+{
+	struct qpnp_pin_chip *q_chip = dev_get_drvdata(&pdev->dev);
+
+	debugfs_remove_recursive(q_chip->dfs_dir);
+
+	return qpnp_pin_free_chip(q_chip);
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{	.compatible = "qcom,qpnp-pin",
+	},
+	{}
+};
+
+static const struct platform_device_id qpnp_pin_id[] = {
+	{ "qcom,qpnp-pin", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_pin_id);
+
+static struct platform_driver qpnp_pin_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-pin",
+		.of_match_table = spmi_match_table,
+	},
+	.probe		= qpnp_pin_probe,
+	.remove		= qpnp_pin_remove,
+	.id_table	= qpnp_pin_id,
+};
+
+static int __init qpnp_pin_init(void)
+{
+#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
+	driver_dfs_dir = debugfs_create_dir("qpnp_pin", NULL);
+	if (driver_dfs_dir == NULL)
+		pr_err("Cannot register top level debugfs directory\n");
+#endif
+
+	return platform_driver_register(&qpnp_pin_driver);
+}
+
+static void __exit qpnp_pin_exit(void)
+{
+#ifdef CONFIG_GPIO_QPNP_PIN_DEBUG
+	debugfs_remove_recursive(driver_dfs_dir);
+#endif
+	platform_driver_unregister(&qpnp_pin_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC gpio driver");
+MODULE_LICENSE("GPL v2");
+
+subsys_initcall(qpnp_pin_init);
+module_exit(qpnp_pin_exit);
diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile
index 9770155..95c113a 100644
--- a/drivers/gpu/drm/msm/Makefile
+++ b/drivers/gpu/drm/msm/Makefile
@@ -47,6 +47,8 @@
 	sde/sde_vbif.o \
 	sde_dbg_evtlog.o \
 	sde_io_util.o \
+	sde/sde_hw_reg_dma_v1_color_proc.o \
+	sde/sde_hw_color_proc_v4.o \
 
 # use drm gpu driver only if qcom_kgsl driver not available
 ifneq ($(CONFIG_QCOM_KGSL),y)
@@ -122,7 +124,10 @@
 	sde/sde_hw_vbif.o \
 	sde/sde_formats.o \
 	sde_power_handle.o \
-	sde/sde_hw_color_processing_v1_7.o
+	sde/sde_hw_color_processing_v1_7.o \
+	sde/sde_reg_dma.o \
+	sde/sde_hw_reg_dma_v1.o \
+
 
 msm_drm-$(CONFIG_DRM_SDE_WB) += sde/sde_wb.o \
 	sde/sde_encoder_phys_wb.o
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index b0679b0..a498a29 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -24,12 +24,10 @@
 struct msm_commit {
 	struct drm_device *dev;
 	struct drm_atomic_state *state;
-	struct work_struct work;
 	uint32_t crtc_mask;
+	struct kthread_work commit_work;
 };
 
-static void commit_worker(struct work_struct *work);
-
 /* block until specified crtcs are no longer pending update, and
  * atomically mark them as pending update
  */
@@ -60,21 +58,6 @@ static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
 	spin_unlock(&priv->pending_crtcs_event.lock);
 }
 
-static struct msm_commit *commit_init(struct drm_atomic_state *state)
-{
-	struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
-
-	if (!c)
-		return NULL;
-
-	c->dev = state->dev;
-	c->state = state;
-
-	INIT_WORK(&c->work, commit_worker);
-
-	return c;
-}
-
 static void commit_destroy(struct msm_commit *c)
 {
 	end_atomic(c->dev->dev_private, c->crtc_mask);
@@ -404,7 +387,7 @@ static void msm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
 /* The (potentially) asynchronous part of the commit.  At this point
  * nothing can fail short of armageddon.
  */
-static void complete_commit(struct msm_commit *c, bool async)
+static void complete_commit(struct msm_commit *c)
 {
 	struct drm_atomic_state *state = c->state;
 	struct drm_device *dev = state->dev;
@@ -445,9 +428,74 @@ static void complete_commit(struct msm_commit *c, bool async)
 	commit_destroy(c);
 }
 
-static void commit_worker(struct work_struct *work)
+static void _msm_drm_commit_work_cb(struct kthread_work *work)
 {
-	complete_commit(container_of(work, struct msm_commit, work), true);
+	struct msm_commit *commit =  NULL;
+
+	if (!work) {
+		DRM_ERROR("%s: Invalid commit work data!\n", __func__);
+		return;
+	}
+
+	commit = container_of(work, struct msm_commit, commit_work);
+
+	complete_commit(commit);
+}
+
+static struct msm_commit *commit_init(struct drm_atomic_state *state)
+{
+	struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
+
+	if (!c)
+		return NULL;
+
+	c->dev = state->dev;
+	c->state = state;
+
+	kthread_init_work(&c->commit_work, _msm_drm_commit_work_cb);
+
+	return c;
+}
+
+/* Start display thread function */
+static int msm_atomic_commit_dispatch(struct drm_device *dev,
+		struct drm_atomic_state *state, struct msm_commit *commit)
+{
+	struct msm_drm_private *priv = dev->dev_private;
+	struct drm_crtc *crtc = NULL;
+	struct drm_crtc_state *crtc_state = NULL;
+	int ret = -EINVAL, i = 0, j = 0;
+
+	for_each_crtc_in_state(state, crtc, crtc_state, i) {
+		for (j = 0; j < priv->num_crtcs; j++) {
+			if (priv->disp_thread[j].crtc_id ==
+						crtc->base.id) {
+				if (priv->disp_thread[j].thread) {
+					kthread_queue_work(
+						&priv->disp_thread[j].worker,
+							&commit->commit_work);
+					/* only return zero if work is
+					 * queued successfully.
+					 */
+					ret = 0;
+				} else {
+					DRM_ERROR(" Error for crtc_id: %d\n",
+						priv->disp_thread[j].crtc_id);
+				}
+				break;
+			}
+		}
+		/*
+		 * TODO: handle cases where there will be more than
+		 * one crtc per commit cycle. Remove this check then.
+		 * Current assumption is there will be only one crtc
+		 * per commit cycle.
+		 */
+		if (j < priv->num_crtcs)
+			break;
+	}
+
+	return ret;
 }
 
 /**
@@ -546,11 +594,17 @@ int msm_atomic_commit(struct drm_device *dev,
 	 */
 
 	if (nonblock) {
-		queue_work(priv->atomic_wq, &c->work);
+		ret = msm_atomic_commit_dispatch(dev, state, c);
+		if (ret) {
+			DRM_ERROR("%s: atomic commit failed\n", __func__);
+			drm_atomic_state_free(state);
+			commit_destroy(c);
+			goto error;
+		}
 		return 0;
 	}
 
-	complete_commit(c, false);
+	complete_commit(c);
 
 	return 0;
 
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index 50d34d16..329381e 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -168,7 +168,7 @@ struct vblank_event {
 	bool enable;
 };
 
-static void vblank_ctrl_worker(struct work_struct *work)
+static void vblank_ctrl_worker(struct kthread_work *work)
 {
 	struct msm_vblank_ctrl *vbl_ctrl = container_of(work,
 						struct msm_vblank_ctrl, work);
@@ -216,7 +216,7 @@ static int vblank_ctrl_queue_work(struct msm_drm_private *priv,
 	list_add_tail(&vbl_ev->node, &vbl_ctrl->event_list);
 	spin_unlock_irqrestore(&vbl_ctrl->lock, flags);
 
-	queue_work(priv->wq, &vbl_ctrl->work);
+	kthread_queue_work(&priv->disp_thread[crtc_id].worker, &vbl_ctrl->work);
 
 	return 0;
 }
@@ -230,17 +230,27 @@ static int msm_drm_uninit(struct device *dev)
 	struct msm_gpu *gpu = priv->gpu;
 	struct msm_vblank_ctrl *vbl_ctrl = &priv->vblank_ctrl;
 	struct vblank_event *vbl_ev, *tmp;
+	int i;
 
 	/* We must cancel and cleanup any pending vblank enable/disable
 	 * work before drm_irq_uninstall() to avoid work re-enabling an
 	 * irq after uninstall has disabled it.
 	 */
-	cancel_work_sync(&vbl_ctrl->work);
+	kthread_flush_work(&vbl_ctrl->work);
 	list_for_each_entry_safe(vbl_ev, tmp, &vbl_ctrl->event_list, node) {
 		list_del(&vbl_ev->node);
 		kfree(vbl_ev);
 	}
 
+	/* clean up display commit worker threads */
+	for (i = 0; i < priv->num_crtcs; i++) {
+		if (priv->disp_thread[i].thread) {
+			kthread_flush_worker(&priv->disp_thread[i].worker);
+			kthread_stop(priv->disp_thread[i].thread);
+			priv->disp_thread[i].thread = NULL;
+		}
+	}
+
 	msm_gem_shrinker_cleanup(ddev);
 
 	drm_kms_helper_poll_fini(ddev);
@@ -263,9 +273,6 @@ static int msm_drm_uninit(struct device *dev)
 	flush_workqueue(priv->wq);
 	destroy_workqueue(priv->wq);
 
-	flush_workqueue(priv->atomic_wq);
-	destroy_workqueue(priv->atomic_wq);
-
 	if (kms && kms->funcs)
 		kms->funcs->destroy(kms);
 
@@ -432,7 +439,7 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
 	struct drm_device *ddev;
 	struct msm_drm_private *priv;
 	struct msm_kms *kms;
-	int ret;
+	int ret, i;
 
 	ddev = drm_dev_alloc(drv, dev);
 	if (!ddev) {
@@ -462,13 +469,12 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
 		goto mdss_init_fail;
 
 	priv->wq = alloc_ordered_workqueue("msm_drm", 0);
-	priv->atomic_wq = alloc_ordered_workqueue("msm:atomic", 0);
 	init_waitqueue_head(&priv->pending_crtcs_event);
 
 	INIT_LIST_HEAD(&priv->client_event_list);
 	INIT_LIST_HEAD(&priv->inactive_list);
 	INIT_LIST_HEAD(&priv->vblank_ctrl.event_list);
-	INIT_WORK(&priv->vblank_ctrl.work, vblank_ctrl_worker);
+	kthread_init_work(&priv->vblank_ctrl.work, vblank_ctrl_worker);
 	spin_lock_init(&priv->vblank_ctrl.lock);
 
 	ret = sde_power_resource_init(pdev, &priv->phandle);
@@ -538,6 +544,28 @@ static int msm_drm_init(struct device *dev, struct drm_driver *drv)
 	}
 	ddev->mode_config.funcs = &mode_config_funcs;
 
+	for (i = 0; i < priv->num_crtcs; i++) {
+		priv->disp_thread[i].crtc_id = priv->crtcs[i]->base.id;
+		kthread_init_worker(&priv->disp_thread[i].worker);
+		priv->disp_thread[i].dev = ddev;
+		priv->disp_thread[i].thread =
+			kthread_run(kthread_worker_fn,
+				&priv->disp_thread[i].worker,
+				"crtc_commit:%d",
+				priv->disp_thread[i].crtc_id);
+
+		if (IS_ERR(priv->disp_thread[i].thread)) {
+			dev_err(dev, "failed to create kthread\n");
+			priv->disp_thread[i].thread = NULL;
+			/* clean up previously created threads if any */
+			for (i -= 1; i >= 0; i--) {
+				kthread_stop(priv->disp_thread[i].thread);
+				priv->disp_thread[i].thread = NULL;
+			}
+			goto fail;
+		}
+	}
+
 	ret = drm_vblank_init(ddev, priv->num_crtcs);
 	if (ret < 0) {
 		dev_err(dev, "failed to initialize vblank\n");
diff --git a/drivers/gpu/drm/msm/msm_drv.h b/drivers/gpu/drm/msm/msm_drv.h
index e6fdeb9..499dfcc 100644
--- a/drivers/gpu/drm/msm/msm_drv.h
+++ b/drivers/gpu/drm/msm/msm_drv.h
@@ -35,6 +35,7 @@
 #include <linux/of_device.h>
 #include <linux/sde_io_util.h>
 #include <asm/sizes.h>
+#include <linux/kthread.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -63,7 +64,7 @@ struct msm_fence_cb;
 
 #define NUM_DOMAINS    4    /* one for KMS, then one per gpu core (?) */
 #define MAX_CRTCS      8
-#define MAX_PLANES     12
+#define MAX_PLANES     20
 #define MAX_ENCODERS   8
 #define MAX_BRIDGES    8
 #define MAX_CONNECTORS 8
@@ -154,7 +155,7 @@ enum msm_mdp_conn_property {
 };
 
 struct msm_vblank_ctrl {
-	struct work_struct work;
+	struct kthread_work work;
 	struct list_head event_list;
 	spinlock_t lock;
 };
@@ -235,6 +236,14 @@ struct msm_drm_event {
 	u8 data[];
 };
 
+/* Commit thread specific structure */
+struct msm_drm_commit {
+	struct drm_device *dev;
+	struct task_struct *thread;
+	unsigned int crtc_id;
+	struct kthread_worker worker;
+};
+
 struct msm_drm_private {
 
 	struct drm_device *dev;
@@ -277,7 +286,6 @@ struct msm_drm_private {
 	struct list_head inactive_list;
 
 	struct workqueue_struct *wq;
-	struct workqueue_struct *atomic_wq;
 
 	/* crtcs pending async atomic updates: */
 	uint32_t pending_crtcs;
@@ -293,6 +301,8 @@ struct msm_drm_private {
 	unsigned int num_crtcs;
 	struct drm_crtc *crtcs[MAX_CRTCS];
 
+	struct msm_drm_commit disp_thread[MAX_CRTCS];
+
 	unsigned int num_encoders;
 	struct drm_encoder *encoders[MAX_ENCODERS];
 
diff --git a/drivers/gpu/drm/msm/sde/sde_color_processing.c b/drivers/gpu/drm/msm/sde/sde_color_processing.c
index 4931e3c..008a527 100644
--- a/drivers/gpu/drm/msm/sde/sde_color_processing.c
+++ b/drivers/gpu/drm/msm/sde/sde_color_processing.c
@@ -33,6 +33,7 @@ struct sde_cp_node {
 	struct list_head active_list;
 	struct list_head dirty_list;
 	bool is_dspp_feature;
+	u32 prop_blob_sz;
 };
 
 struct sde_cp_prop_attach {
@@ -51,6 +52,10 @@ static void dspp_ad_install_property(struct drm_crtc *crtc);
 
 static void dspp_vlut_install_property(struct drm_crtc *crtc);
 
+static void dspp_gamut_install_property(struct drm_crtc *crtc);
+
+static void dspp_gc_install_property(struct drm_crtc *crtc);
+
 typedef void (*dspp_prop_install_func_t)(struct drm_crtc *crtc);
 
 static dspp_prop_install_func_t dspp_prop_install_func[SDE_DSPP_MAX];
@@ -61,6 +66,8 @@ do { \
 	func[SDE_DSPP_HSIC] = dspp_hsic_install_property; \
 	func[SDE_DSPP_AD] = dspp_ad_install_property; \
 	func[SDE_DSPP_VLUT] = dspp_vlut_install_property; \
+	func[SDE_DSPP_GAMUT] = dspp_gamut_install_property; \
+	func[SDE_DSPP_GC] = dspp_gc_install_property; \
 } while (0)
 
 typedef void (*lm_prop_install_func_t)(struct drm_crtc *crtc);
@@ -247,6 +254,12 @@ static int sde_cp_enable_crtc_blob_property(struct drm_crtc *crtc,
 		DRM_ERROR("invalid blob id %lld\n", val);
 		return -EINVAL;
 	}
+	if (blob->length != prop_node->prop_blob_sz) {
+		DRM_ERROR("invalid blob len %zd exp %d feature %d\n",
+		    blob->length, prop_node->prop_blob_sz, prop_node->feature);
+		drm_property_unreference_blob(blob);
+		return -EINVAL;
+	}
 	/* Release refernce to existing payload of the property */
 	if (prop_node->blob_ptr)
 		drm_property_unreference_blob(prop_node->blob_ptr);
@@ -400,7 +413,7 @@ static void sde_cp_crtc_install_range_property(struct drm_crtc *crtc,
 }
 
 static void sde_cp_crtc_create_blob_property(struct drm_crtc *crtc, char *name,
-					     u32 feature)
+			u32 feature, u32 blob_sz)
 {
 	struct drm_property *prop;
 	struct sde_cp_node *prop_node = NULL;
@@ -434,6 +447,7 @@ static void sde_cp_crtc_create_blob_property(struct drm_crtc *crtc, char *name,
 
 	INIT_PROP_ATTACH(&prop_attach, crtc, prop, prop_node,
 				feature, val);
+	prop_node->prop_blob_sz = blob_sz;
 
 	sde_cp_crtc_prop_attach(&prop_attach);
 }
@@ -454,7 +468,7 @@ static void sde_cp_crtc_setfeature(struct sde_cp_node *prop_node,
 	for (i = 0; i < num_mixers && !ret; i++) {
 		hw_lm = sde_crtc->mixers[i].hw_lm;
 		hw_dspp = sde_crtc->mixers[i].hw_dspp;
-
+		hw_cfg.ctl = sde_crtc->mixers[i].hw_ctl;
 		switch (prop_node->feature) {
 		case SDE_CP_CRTC_DSPP_VLUT:
 			if (!hw_dspp || !hw_dspp->ops.setup_vlut) {
@@ -886,7 +900,7 @@ static void dspp_pcc_install_property(struct drm_crtc *crtc)
 	switch (version) {
 	case 1:
 		sde_cp_crtc_create_blob_property(crtc, feature_name,
-					SDE_CP_CRTC_DSPP_PCC);
+			SDE_CP_CRTC_DSPP_PCC, sizeof(struct drm_msm_pcc));
 		break;
 	default:
 		DRM_ERROR("version %d not supported\n", version);
@@ -981,7 +995,56 @@ static void lm_gc_install_property(struct drm_crtc *crtc)
 	switch (version) {
 	case 1:
 		sde_cp_crtc_create_blob_property(crtc, feature_name,
-			SDE_CP_CRTC_LM_GC);
+			SDE_CP_CRTC_LM_GC, sizeof(struct drm_msm_pgc_lut));
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
+static void dspp_gamut_install_property(struct drm_crtc *crtc)
+{
+	char feature_name[256];
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+
+	version = catalog->dspp[0].sblk->gamut.version >> 16;
+	snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+		"SDE_DSPP_GAMUT_V", version);
+	switch (version) {
+	case 4:
+		sde_cp_crtc_create_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_GAMUT,
+			sizeof(struct drm_msm_3d_gamut));
+		break;
+	default:
+		DRM_ERROR("version %d not supported\n", version);
+		break;
+	}
+}
+
+static void dspp_gc_install_property(struct drm_crtc *crtc)
+{
+	char feature_name[256];
+	struct sde_kms *kms = NULL;
+	struct sde_mdss_cfg *catalog = NULL;
+	u32 version;
+
+	kms = get_kms(crtc);
+	catalog = kms->catalog;
+
+	version = catalog->dspp[0].sblk->gc.version >> 16;
+	snprintf(feature_name, ARRAY_SIZE(feature_name), "%s%d",
+		"SDE_DSPP_GC_V", version);
+	switch (version) {
+	case 1:
+		sde_cp_crtc_create_blob_property(crtc, feature_name,
+			SDE_CP_CRTC_DSPP_GC, sizeof(struct drm_msm_pgc_lut));
 		break;
 	default:
 		DRM_ERROR("version %d not supported\n", version);
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c
index a05f566..9832ca5 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.c
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.c
@@ -270,6 +270,9 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
 
 		stage_cfg->stage[lm_idx][pstate->stage][idx] =
 							sde_plane_pipe(plane);
+		stage_cfg->multirect_index
+				[lm_idx][pstate->stage][idx] =
+				pstate->multirect_index;
 		mixer[lm_idx].flush_mask |= flush_mask;
 
 		SDE_DEBUG("crtc %d stage:%d - plane %d sspp %d fb %d\n",
@@ -300,6 +303,9 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
 			idx = right_crtc_zpos_cnt[pstate->stage]++;
 			stage_cfg->stage[RIGHT_MIXER][pstate->stage][idx] =
 							sde_plane_pipe(plane);
+			stage_cfg->multirect_index
+				[RIGHT_MIXER][pstate->stage][idx] =
+				pstate->multirect_index;
 			mixer[RIGHT_MIXER].flush_mask |= flush_mask;
 
 			/* blend config update */
@@ -478,6 +484,111 @@ static void sde_crtc_vblank_cb(void *data)
 	SDE_EVT32_IRQ(DRMID(crtc));
 }
 
+static void sde_crtc_frame_event_work(struct kthread_work *work)
+{
+	struct sde_crtc_frame_event *fevent;
+	struct drm_crtc *crtc;
+	struct sde_crtc *sde_crtc;
+	struct sde_kms *sde_kms;
+	unsigned long flags;
+
+	if (!work) {
+		SDE_ERROR("invalid work handle\n");
+		return;
+	}
+
+	fevent = container_of(work, struct sde_crtc_frame_event, work);
+	if (!fevent->crtc) {
+		SDE_ERROR("invalid crtc\n");
+		return;
+	}
+
+	crtc = fevent->crtc;
+	sde_crtc = to_sde_crtc(crtc);
+
+	sde_kms = _sde_crtc_get_kms(crtc);
+	if (!sde_kms) {
+		SDE_ERROR("invalid kms handle\n");
+		return;
+	}
+
+	SDE_DEBUG("crtc%d event:%u ts:%lld\n", crtc->base.id, fevent->event,
+			ktime_to_ns(fevent->ts));
+
+	if (fevent->event == SDE_ENCODER_FRAME_EVENT_DONE ||
+			fevent->event == SDE_ENCODER_FRAME_EVENT_ERROR) {
+
+		if (atomic_read(&sde_crtc->frame_pending) < 1) {
+			/* this should not happen */
+			SDE_ERROR("crtc%d ts:%lld invalid frame_pending:%d\n",
+					crtc->base.id,
+					ktime_to_ns(fevent->ts),
+					atomic_read(&sde_crtc->frame_pending));
+			SDE_EVT32(DRMID(crtc), fevent->event, 0);
+		} else if (atomic_dec_return(&sde_crtc->frame_pending) == 0) {
+			/* release bandwidth and other resources */
+			SDE_DEBUG("crtc%d ts:%lld last pending\n",
+					crtc->base.id,
+					ktime_to_ns(fevent->ts));
+			SDE_EVT32(DRMID(crtc), fevent->event, 1);
+		} else {
+			SDE_EVT32(DRMID(crtc), fevent->event, 2);
+		}
+	} else {
+		SDE_ERROR("crtc%d ts:%lld unknown event %u\n", crtc->base.id,
+				ktime_to_ns(fevent->ts),
+				fevent->event);
+		SDE_EVT32(DRMID(crtc), fevent->event, 3);
+	}
+
+	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+	list_add_tail(&fevent->list, &sde_crtc->frame_event_list);
+	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+}
+
+static void sde_crtc_frame_event_cb(void *data, u32 event)
+{
+	struct drm_crtc *crtc = (struct drm_crtc *)data;
+	struct sde_crtc *sde_crtc;
+	struct msm_drm_private *priv;
+	struct list_head *list, *next;
+	struct sde_crtc_frame_event *fevent;
+	unsigned long flags;
+	int pipe_id;
+
+	if (!crtc || !crtc->dev || !crtc->dev->dev_private) {
+		SDE_ERROR("invalid parameters\n");
+		return;
+	}
+	sde_crtc = to_sde_crtc(crtc);
+	priv = crtc->dev->dev_private;
+	pipe_id = drm_crtc_index(crtc);
+
+	SDE_DEBUG("crtc%d\n", crtc->base.id);
+
+	SDE_EVT32(DRMID(crtc), event);
+
+	spin_lock_irqsave(&sde_crtc->spin_lock, flags);
+	list_for_each_safe(list, next, &sde_crtc->frame_event_list) {
+		list_del_init(list);
+		break;
+	}
+	spin_unlock_irqrestore(&sde_crtc->spin_lock, flags);
+
+	if (!list) {
+		SDE_ERROR("crtc%d event %d overflow\n",
+				crtc->base.id, event);
+		SDE_EVT32(DRMID(crtc), event);
+		return;
+	}
+
+	fevent = container_of(list, struct sde_crtc_frame_event, list);
+	fevent->event = event;
+	fevent->crtc = crtc;
+	fevent->ts = ktime_get();
+	kthread_queue_work(&priv->disp_thread[pipe_id].worker, &fevent->work);
+}
+
 void sde_crtc_complete_commit(struct drm_crtc *crtc,
 		struct drm_crtc_state *old_state)
 {
@@ -839,12 +950,14 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
 {
 	struct drm_encoder *encoder;
 	struct drm_device *dev;
+	struct sde_crtc *sde_crtc;
 
 	if (!crtc) {
 		SDE_ERROR("invalid argument\n");
 		return;
 	}
 	dev = crtc->dev;
+	sde_crtc = to_sde_crtc(crtc);
 
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->crtc != crtc)
@@ -854,7 +967,29 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
 		 * Encoder will flush/start now, unless it has a tx pending.
 		 * If so, it may delay and flush at an irq event (e.g. ppdone)
 		 */
-		sde_encoder_schedule_kickoff(encoder);
+		sde_encoder_prepare_for_kickoff(encoder);
+	}
+
+	if (atomic_read(&sde_crtc->frame_pending) > 2) {
+		/* framework allows only 1 outstanding + current */
+		SDE_ERROR("crtc%d invalid frame pending\n",
+				crtc->base.id);
+		SDE_EVT32(DRMID(crtc), 0);
+		return;
+	} else if (atomic_inc_return(&sde_crtc->frame_pending) == 1) {
+		/* acquire bandwidth and other resources */
+		SDE_DEBUG("crtc%d first commit\n", crtc->base.id);
+		SDE_EVT32(DRMID(crtc), 1);
+	} else {
+		SDE_DEBUG("crtc%d commit\n", crtc->base.id);
+		SDE_EVT32(DRMID(crtc), 2);
+	}
+
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		if (encoder->crtc != crtc)
+			continue;
+
+		sde_encoder_kickoff(encoder);
 	}
 }
 
@@ -945,10 +1080,12 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
 
 	mutex_lock(&sde_crtc->crtc_lock);
+	SDE_EVT32(DRMID(crtc));
+
 	if (atomic_read(&sde_crtc->vblank_refcount)) {
-		SDE_ERROR("crtc%d invalid vblank refcount %d\n",
-				crtc->base.id,
-				atomic_read(&sde_crtc->vblank_refcount));
+		SDE_ERROR("crtc%d invalid vblank refcount\n",
+				crtc->base.id);
+		SDE_EVT32(DRMID(crtc));
 		drm_for_each_encoder(encoder, crtc->dev) {
 			if (encoder->crtc != crtc)
 				continue;
@@ -958,6 +1095,20 @@ static void sde_crtc_disable(struct drm_crtc *crtc)
 		atomic_set(&sde_crtc->vblank_refcount, 0);
 	}
 
+	if (atomic_read(&sde_crtc->frame_pending)) {
+		/* release bandwidth and other resources */
+		SDE_ERROR("crtc%d invalid frame pending\n",
+				crtc->base.id);
+		SDE_EVT32(DRMID(crtc));
+		atomic_set(&sde_crtc->frame_pending, 0);
+	}
+
+	drm_for_each_encoder(encoder, crtc->dev) {
+		if (encoder->crtc != crtc)
+			continue;
+		sde_encoder_register_frame_event_callback(encoder, NULL, NULL);
+	}
+
 	memset(sde_crtc->mixers, 0, sizeof(sde_crtc->mixers));
 	sde_crtc->num_mixers = 0;
 	mutex_unlock(&sde_crtc->crtc_lock);
@@ -970,6 +1121,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
 	struct sde_hw_mixer *lm;
 	struct drm_display_mode *mode;
 	struct sde_hw_mixer_cfg cfg;
+	struct drm_encoder *encoder;
 	int i;
 
 	if (!crtc) {
@@ -978,6 +1130,7 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
 	}
 
 	SDE_DEBUG("crtc%d\n", crtc->base.id);
+	SDE_EVT32(DRMID(crtc));
 
 	sde_crtc = to_sde_crtc(crtc);
 	mixer = sde_crtc->mixers;
@@ -989,6 +1142,13 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
 
 	drm_mode_debug_printmodeline(mode);
 
+	drm_for_each_encoder(encoder, crtc->dev) {
+		if (encoder->crtc != crtc)
+			continue;
+		sde_encoder_register_frame_event_callback(encoder,
+				sde_crtc_frame_event_cb, (void *)crtc);
+	}
+
 	for (i = 0; i < sde_crtc->num_mixers; i++) {
 		lm = mixer[i].hw_lm;
 		cfg.out_width = sde_crtc_mixer_width(sde_crtc, mode);
@@ -1002,8 +1162,8 @@ static void sde_crtc_enable(struct drm_crtc *crtc)
 struct plane_state {
 	struct sde_plane_state *sde_pstate;
 	const struct drm_plane_state *drm_pstate;
-
 	int stage;
+	u32 pipe_id;
 };
 
 static int pstate_cmp(const void *a, const void *b)
@@ -1028,7 +1188,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		struct drm_crtc_state *state)
 {
 	struct sde_crtc *sde_crtc;
-	struct plane_state pstates[SDE_STAGE_MAX * 2];
+	struct plane_state pstates[SDE_STAGE_MAX * 4];
 	struct sde_crtc_state *cstate;
 
 	const struct drm_plane_state *pstate;
@@ -1036,8 +1196,12 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 	struct drm_display_mode *mode;
 
 	int cnt = 0, rc = 0, mixer_width, i, z_pos;
+
 	int left_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
 	int right_crtc_zpos_cnt[SDE_STAGE_MAX] = {0};
+	struct sde_multirect_plane_states multirect_plane[SDE_STAGE_MAX * 2];
+	int multirect_count = 0;
+	const struct drm_plane_state *pipe_staged[SSPP_MAX];
 
 	if (!crtc) {
 		SDE_ERROR("invalid crtc\n");
@@ -1055,6 +1219,8 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 	mode = &state->adjusted_mode;
 	SDE_DEBUG("%s: check", sde_crtc->name);
 
+	memset(pipe_staged, 0, sizeof(pipe_staged));
+
 	mixer_width = sde_crtc_mixer_width(sde_crtc, mode);
 
 	 /* get plane state for all drm planes associated with crtc state */
@@ -1072,6 +1238,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		pstates[cnt].drm_pstate = pstate;
 		pstates[cnt].stage = sde_plane_get_property(
 				pstates[cnt].sde_pstate, PLANE_PROP_ZPOS);
+		pstates[cnt].pipe_id = sde_plane_pipe(plane);
 
 		/* check dim layer stage with every plane */
 		for (i = 0; i < cstate->num_dim_layers; i++) {
@@ -1084,6 +1251,17 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 			}
 		}
 
+		if (pipe_staged[pstates[cnt].pipe_id]) {
+			multirect_plane[multirect_count].r0 =
+				pipe_staged[pstates[cnt].pipe_id];
+			multirect_plane[multirect_count].r1 = pstate;
+			multirect_count++;
+
+			pipe_staged[pstates[cnt].pipe_id] = NULL;
+		} else {
+			pipe_staged[pstates[cnt].pipe_id] = pstate;
+		}
+
 		cnt++;
 
 		if (CHECK_LAYER_BOUNDS(pstate->crtc_y, pstate->crtc_h,
@@ -1099,6 +1277,15 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		}
 	}
 
+	for (i = 1; i < SSPP_MAX; i++) {
+		if (pipe_staged[i] &&
+			is_sde_plane_virtual(pipe_staged[i]->plane)) {
+			SDE_ERROR("invalid use of virtual plane: %d\n",
+					pipe_staged[i]->plane->base.id);
+			goto end;
+		}
+	}
+
 	/* Check dim layer rect bounds and stage */
 	for (i = 0; i < cstate->num_dim_layers; i++) {
 		if ((CHECK_LAYER_BOUNDS(cstate->dim_layer[i].rect.y,
@@ -1146,7 +1333,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 			goto end;
 		} else if (pstates[i].drm_pstate->crtc_x < mixer_width) {
 			if (left_crtc_zpos_cnt[z_pos] == 2) {
-				SDE_ERROR("> 2 plane @ stage%d on left\n",
+				SDE_ERROR("> 2 planes @ stage %d on left\n",
 					z_pos);
 				rc = -EINVAL;
 				goto end;
@@ -1154,7 +1341,7 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 			left_crtc_zpos_cnt[z_pos]++;
 		} else {
 			if (right_crtc_zpos_cnt[z_pos] == 2) {
-				SDE_ERROR("> 2 plane @ stage%d on right\n",
+				SDE_ERROR("> 2 planes @ stage %d on right\n",
 					z_pos);
 				rc = -EINVAL;
 				goto end;
@@ -1165,6 +1352,17 @@ static int sde_crtc_atomic_check(struct drm_crtc *crtc,
 		SDE_DEBUG("%s: zpos %d", sde_crtc->name, z_pos);
 	}
 
+	for (i = 0; i < multirect_count; i++) {
+		if (sde_plane_validate_multirect_v2(&multirect_plane[i])) {
+			SDE_ERROR(
+			"multirect validation failed for planes (%d - %d)\n",
+					multirect_plane[i].r0->plane->base.id,
+					multirect_plane[i].r1->plane->base.id);
+			rc = -EINVAL;
+			break;
+		}
+	}
+
 end:
 	return rc;
 }
@@ -1273,6 +1471,16 @@ static void sde_crtc_install_properties(struct drm_crtc *crtc,
 		sde_kms_info_add_keystr(info, "qseed_type", "qseed2");
 	if (catalog->qseed_type == SDE_SSPP_SCALER_QSEED3)
 		sde_kms_info_add_keystr(info, "qseed_type", "qseed3");
+
+	if (sde_is_custom_client()) {
+		if (catalog->smart_dma_rev == SDE_SSPP_SMART_DMA_V1)
+			sde_kms_info_add_keystr(info,
+					"smart_dma_rev", "smart_dma_v1");
+		if (catalog->smart_dma_rev == SDE_SSPP_SMART_DMA_V2)
+			sde_kms_info_add_keystr(info,
+					"smart_dma_rev", "smart_dma_v2");
+	}
+
 	sde_kms_info_add_keyint(info, "has_src_split", catalog->has_src_split);
 	msm_property_set_blob(&sde_crtc->property_info, &sde_crtc->blob_info,
 			info->data, info->len, CRTC_PROP_INFO);
@@ -1557,6 +1765,7 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 	struct sde_crtc *sde_crtc = NULL;
 	struct msm_drm_private *priv = NULL;
 	struct sde_kms *kms = NULL;
+	int i;
 
 	priv = dev->dev_private;
 	kms = to_sde_kms(priv->kms);
@@ -1569,6 +1778,18 @@ struct drm_crtc *sde_crtc_init(struct drm_device *dev, struct drm_plane *plane)
 	crtc->dev = dev;
 	atomic_set(&sde_crtc->vblank_refcount, 0);
 
+	spin_lock_init(&sde_crtc->spin_lock);
+	atomic_set(&sde_crtc->frame_pending, 0);
+
+	INIT_LIST_HEAD(&sde_crtc->frame_event_list);
+	for (i = 0; i < ARRAY_SIZE(sde_crtc->frame_events); i++) {
+		INIT_LIST_HEAD(&sde_crtc->frame_events[i].list);
+		list_add(&sde_crtc->frame_events[i].list,
+				&sde_crtc->frame_event_list);
+		kthread_init_work(&sde_crtc->frame_events[i].work,
+				sde_crtc_frame_event_work);
+	}
+
 	drm_crtc_init_with_planes(dev, crtc, plane, NULL, &sde_crtc_funcs,
 				NULL);
 
diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.h b/drivers/gpu/drm/msm/sde/sde_crtc.h
index 8ad9685..d06955d 100644
--- a/drivers/gpu/drm/msm/sde/sde_crtc.h
+++ b/drivers/gpu/drm/msm/sde/sde_crtc.h
@@ -26,6 +26,9 @@
 
 #define SDE_CRTC_NAME_SIZE	12
 
+/* define the maximum number of in-flight frame events */
+#define SDE_CRTC_FRAME_EVENT_SIZE	2
+
 /**
  * struct sde_crtc_mixer: stores the map for each virtual pipeline in the CRTC
  * @hw_lm:	LM HW Driver context
@@ -45,6 +48,22 @@ struct sde_crtc_mixer {
 };
 
 /**
+ * struct sde_crtc_frame_event: stores crtc frame event for crtc processing
+ * @work:	base work structure
+ * @crtc:	Pointer to crtc handling this event
+ * @list:	event list
+ * @ts:		timestamp at queue entry
+ * @event:	event identifier
+ */
+struct sde_crtc_frame_event {
+	struct kthread_work work;
+	struct drm_crtc *crtc;
+	struct list_head list;
+	ktime_t ts;
+	u32 event;
+};
+
+/**
  * struct sde_crtc - virtualized CRTC data structure
  * @base          : Base drm crtc structure
  * @name          : ASCII description of this crtc
@@ -53,7 +72,6 @@ struct sde_crtc_mixer {
  * @mixer         : List of active mixers
  * @event         : Pointer to last received drm vblank event. If there is a
  *                  pending vblank event, this will be non-null.
- * @pending       : Whether or not an update is pending
  * @vsync_count   : Running count of received vsync events
  * @drm_requested_vblank : Whether vblanks have been enabled in the encoder
  * @property_info : Opaque structure for generic property support
@@ -67,6 +85,10 @@ struct sde_crtc_mixer {
  * @active_list   : list of color processing features are active
  * @dirty_list    : list of color processing features are dirty
  * @crtc_lock     : crtc lock around create, destroy and access.
+ * @frame_pending : Whether or not an update is pending
+ * @frame_events  : static allocation of in-flight frame events
+ * @frame_event_list : available frame event list
+ * @spin_lock     : spin lock for frame event, transaction status, etc...
  */
 struct sde_crtc {
 	struct drm_crtc base;
@@ -99,6 +121,11 @@ struct sde_crtc {
 	struct list_head dirty_list;
 
 	struct mutex crtc_lock;
+
+	atomic_t frame_pending;
+	struct sde_crtc_frame_event frame_events[SDE_CRTC_FRAME_EVENT_SIZE];
+	struct list_head frame_event_list;
+	spinlock_t spin_lock;
 };
 
 #define to_sde_crtc(x) container_of(x, struct sde_crtc, base)
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c
index ebbf0a7..ebae360 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.c
@@ -39,6 +39,9 @@
 #define SDE_ERROR_ENC(e, fmt, ...) SDE_ERROR("enc%d " fmt,\
 		(e) ? (e)->base.base.id : -1, ##__VA_ARGS__)
 
+/* timeout in frames waiting for frame done */
+#define SDE_ENCODER_FRAME_DONE_TIMEOUT	60
+
 /*
  * Two to anticipate panels that can do cmd/vid dynamic switching
  * plan is to create all possible physical encoder types, and switch between
@@ -75,6 +78,14 @@
  * @debugfs_root:		Debug file system root file node
  * @enc_lock:			Lock around physical encoder create/destroy and
 				access.
+ * @frame_busy_mask:		Bitmask tracking which phys_enc we are still
+ *				busy processing current command.
+ *				Bit0 = phys_encs[0] etc.
+ * @crtc_frame_event_cb:	callback handler for frame event
+ * @crtc_frame_event_cb_data:	callback handler private data
+ * @crtc_frame_event:		callback event
+ * @frame_done_timeout:		frame done timeout in Hz
+ * @frame_done_timer:		watchdog timer for frame done event
  */
 struct sde_encoder_virt {
 	struct drm_encoder base;
@@ -93,6 +104,13 @@ struct sde_encoder_virt {
 
 	struct dentry *debugfs_root;
 	struct mutex enc_lock;
+	DECLARE_BITMAP(frame_busy_mask, MAX_PHYS_ENCODERS_PER_VIRTUAL);
+	void (*crtc_frame_event_cb)(void *, u32 event);
+	void *crtc_frame_event_cb_data;
+	u32 crtc_frame_event;
+
+	atomic_t frame_done_timeout;
+	struct timer_list frame_done_timer;
 };
 
 #define to_sde_encoder_virt(x) container_of(x, struct sde_encoder_virt, base)
@@ -511,6 +529,11 @@ static void sde_encoder_virt_disable(struct drm_encoder *drm_enc)
 
 	SDE_EVT32(DRMID(drm_enc));
 
+	if (atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
+		SDE_ERROR("enc%d timeout pending\n", drm_enc->base.id);
+		del_timer_sync(&sde_enc->frame_done_timer);
+	}
+
 	for (i = 0; i < sde_enc->num_phys_encs; i++) {
 		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
 
@@ -594,6 +617,7 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc,
 		return;
 
 	atomic_inc(&phy_enc->underrun_cnt);
+	SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt));
 }
 
 void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
@@ -626,6 +650,56 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *drm_enc,
 	}
 }
 
+void sde_encoder_register_frame_event_callback(struct drm_encoder *drm_enc,
+		void (*frame_event_cb)(void *, u32 event),
+		void *frame_event_cb_data)
+{
+	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+	unsigned long lock_flags;
+	bool enable;
+
+	enable = frame_event_cb ? true : false;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+	SDE_DEBUG_ENC(sde_enc, "\n");
+	SDE_EVT32(DRMID(drm_enc), enable, 0);
+
+	spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
+	sde_enc->crtc_frame_event_cb = frame_event_cb;
+	sde_enc->crtc_frame_event_cb_data = frame_event_cb_data;
+	spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
+}
+
+static void sde_encoder_frame_done_callback(
+		struct drm_encoder *drm_enc,
+		struct sde_encoder_phys *ready_phys, u32 event)
+{
+	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+	unsigned int i;
+
+	/* One of the physical encoders has become idle */
+	for (i = 0; i < sde_enc->num_phys_encs; i++)
+		if (sde_enc->phys_encs[i] == ready_phys) {
+			clear_bit(i, sde_enc->frame_busy_mask);
+			sde_enc->crtc_frame_event |= event;
+			SDE_EVT32(DRMID(drm_enc), i,
+					sde_enc->frame_busy_mask[0]);
+		}
+
+	if (!sde_enc->frame_busy_mask[0]) {
+		atomic_set(&sde_enc->frame_done_timeout, 0);
+		del_timer(&sde_enc->frame_done_timer);
+
+		if (sde_enc->crtc_frame_event_cb)
+			sde_enc->crtc_frame_event_cb(
+					sde_enc->crtc_frame_event_cb_data,
+					sde_enc->crtc_frame_event);
+	}
+}
+
 /**
  * _sde_encoder_trigger_flush - trigger flush for a physical encoder
  * drm_enc: Pointer to drm encoder structure
@@ -741,6 +815,7 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
 	}
 
 	pending_flush = 0x0;
+	sde_enc->crtc_frame_event = 0;
 
 	/* update pending counts and trigger kickoff ctl flush atomically */
 	spin_lock_irqsave(&sde_enc->enc_spinlock, lock_flags);
@@ -756,6 +831,8 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
 		if (!ctl)
 			continue;
 
+		set_bit(i, sde_enc->frame_busy_mask);
+
 		if (!phys->ops.needs_single_flush ||
 				!phys->ops.needs_single_flush(phys))
 			_sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0);
@@ -776,7 +853,7 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
 	spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
 }
 
-void sde_encoder_schedule_kickoff(struct drm_encoder *drm_enc)
+void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc)
 {
 	struct sde_encoder_virt *sde_enc;
 	struct sde_encoder_phys *phys;
@@ -797,8 +874,29 @@ void sde_encoder_schedule_kickoff(struct drm_encoder *drm_enc)
 		if (phys && phys->ops.prepare_for_kickoff)
 			phys->ops.prepare_for_kickoff(phys);
 	}
+}
 
-	/* all phys encs are ready to go, trigger the kickoff */
+void sde_encoder_kickoff(struct drm_encoder *drm_enc)
+{
+	struct sde_encoder_virt *sde_enc;
+	struct sde_encoder_phys *phys;
+	unsigned int i;
+
+	if (!drm_enc) {
+		SDE_ERROR("invalid encoder\n");
+		return;
+	}
+	sde_enc = to_sde_encoder_virt(drm_enc);
+
+	SDE_DEBUG_ENC(sde_enc, "\n");
+
+	atomic_set(&sde_enc->frame_done_timeout,
+			SDE_ENCODER_FRAME_DONE_TIMEOUT * 1000 /
+			drm_enc->crtc->state->adjusted_mode.vrefresh);
+	mod_timer(&sde_enc->frame_done_timer, jiffies +
+		((atomic_read(&sde_enc->frame_done_timeout) * HZ) / 1000));
+
+	/* All phys encs are ready to go, trigger the kickoff */
 	_sde_encoder_kickoff_phys(sde_enc);
 
 	/* allow phys encs to handle any post-kickoff business */
@@ -1093,6 +1191,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,
 	struct sde_encoder_virt_ops parent_ops = {
 		sde_encoder_vblank_callback,
 		sde_encoder_underrun_callback,
+		sde_encoder_frame_done_callback,
 	};
 	struct sde_enc_phys_init_params phys_params;
 
@@ -1195,6 +1294,34 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,
 	return ret;
 }
 
+static void sde_encoder_frame_done_timeout(unsigned long data)
+{
+	struct drm_encoder *drm_enc = (struct drm_encoder *) data;
+	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
+	struct msm_drm_private *priv;
+
+	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
+		SDE_ERROR("invalid parameters\n");
+		return;
+	}
+	priv = drm_enc->dev->dev_private;
+
+	if (!sde_enc->frame_busy_mask[0] || !sde_enc->crtc_frame_event_cb) {
+		SDE_DEBUG("enc%d invalid timeout\n", drm_enc->base.id);
+		SDE_EVT32(DRMID(drm_enc),
+				sde_enc->frame_busy_mask[0], 0);
+		return;
+	} else if (!atomic_xchg(&sde_enc->frame_done_timeout, 0)) {
+		SDE_ERROR("enc%d invalid timeout\n", drm_enc->base.id);
+		SDE_EVT32(DRMID(drm_enc), 0, 1);
+		return;
+	}
+
+	SDE_EVT32(DRMID(drm_enc), 0, 2);
+	sde_enc->crtc_frame_event_cb(sde_enc->crtc_frame_event_cb_data,
+			SDE_ENCODER_FRAME_EVENT_ERROR);
+}
+
 struct drm_encoder *sde_encoder_init(
 		struct drm_device *dev,
 		struct msm_display_info *disp_info)
@@ -1225,6 +1352,10 @@ struct drm_encoder *sde_encoder_init(
 	drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);
 	bs_init(sde_enc);
 
+	atomic_set(&sde_enc->frame_done_timeout, 0);
+	setup_timer(&sde_enc->frame_done_timer, sde_encoder_frame_done_timeout,
+			(unsigned long) sde_enc);
+
 	_sde_encoder_init_debugfs(drm_enc, sde_enc, sde_kms);
 
 	SDE_DEBUG_ENC(sde_enc, "created\n");
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.h b/drivers/gpu/drm/msm/sde/sde_encoder.h
index 0818a9d..ab8ff5a 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder.h
@@ -24,6 +24,9 @@
 #include "msm_prop.h"
 #include "sde_hw_mdss.h"
 
+#define SDE_ENCODER_FRAME_EVENT_DONE	BIT(0)
+#define SDE_ENCODER_FRAME_EVENT_ERROR	BIT(1)
+
 /**
  * Encoder functions and data types
  * @intfs:	Interfaces this encoder is using, INTF_MODE_NONE if unused
@@ -59,16 +62,30 @@ void sde_encoder_register_vblank_callback(struct drm_encoder *encoder,
 		void (*cb)(void *), void *data);
 
 /**
- * sde_encoder_schedule_kickoff - Register a callback with the encoder to
- *	trigger a double buffer flip of the ctl path (i.e. ctl flush and start)
- *	at the appropriate time.
+ * sde_encoder_register_frame_event_callback - provide callback to encoder that
+ *	will be called after the request is complete, or other events.
+ * @encoder:	encoder pointer
+ * @cb:		callback pointer, provide NULL to deregister
+ * @data:	user data provided to callback
+ */
+void sde_encoder_register_frame_event_callback(struct drm_encoder *encoder,
+		void (*cb)(void *, u32), void *data);
+
+/**
+ * sde_encoder_prepare_for_kickoff - schedule double buffer flip of the ctl
+ *	path (i.e. ctl flush and start) at next appropriate time.
  *	Immediately: if no previous commit is outstanding.
- *	Delayed: Save the callback, and return. Does not block. Callback will
- *	be triggered later. E.g. cmd encoder will trigger at pp_done irq
- *	irq if it outstanding.
+ *	Delayed: Block until next trigger can be issued.
  * @encoder:	encoder pointer
  */
-void sde_encoder_schedule_kickoff(struct drm_encoder *encoder);
+void sde_encoder_prepare_for_kickoff(struct drm_encoder *encoder);
+
+/**
+ * sde_encoder_kickoff - trigger a double buffer flip of the ctl path
+ *	(i.e. ctl flush and start) immediately.
+ * @encoder:	encoder pointer
+ */
+void sde_encoder_kickoff(struct drm_encoder *drm_enc);
 
 /**
  * sde_encoder_wait_nxt_committed - Wait for hardware to have flushed the
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
index 7a11db3..2ac0a64 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys.h
@@ -56,12 +56,16 @@ struct sde_encoder_phys;
  *			Note: This is called from IRQ handler context.
  * @handle_underrun_virt: Notify virtual encoder of underrun IRQ reception
  *			Note: This is called from IRQ handler context.
+ * @handle_frame_done:	Notify virtual encoder that this phys encoder
+ *			completes last request frame.
  */
 struct sde_encoder_virt_ops {
 	void (*handle_vblank_virt)(struct drm_encoder *,
 			struct sde_encoder_phys *phys);
 	void (*handle_underrun_virt)(struct drm_encoder *,
 			struct sde_encoder_phys *phys);
+	void (*handle_frame_done)(struct drm_encoder *,
+			struct sde_encoder_phys *phys, u32 event);
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
index 5b8ee63..64c70a2 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_cmd.c
@@ -104,6 +104,11 @@ static void sde_encoder_phys_cmd_pp_tx_done_irq(void *arg, int irq_idx)
 
 	phys_enc = &cmd_enc->base;
 
+	/* notify all synchronous clients first, then asynchronous clients */
+	if (phys_enc->parent_ops.handle_frame_done)
+		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+				phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
+
 	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);
 	new_cnt = atomic_add_unless(&phys_enc->pending_kickoff_cnt, -1, 0);
 	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);
@@ -191,6 +196,10 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
 					phys_enc->hw_pp->idx - PINGPONG_0);
 			SDE_ERROR_CMDENC(cmd_enc, "pp:%d kickoff timed out\n",
 					phys_enc->hw_pp->idx - PINGPONG_0);
+			if (phys_enc->parent_ops.handle_frame_done)
+				phys_enc->parent_ops.handle_frame_done(
+						phys_enc->parent, phys_enc,
+						SDE_ENCODER_FRAME_EVENT_ERROR);
 			ret = -ETIMEDOUT;
 		}
 	} else {
@@ -220,6 +229,7 @@ static int sde_encoder_phys_cmd_register_irq(struct sde_encoder_phys *phys_enc,
 {
 	struct sde_encoder_phys_cmd *cmd_enc =
 			to_sde_encoder_phys_cmd(phys_enc);
+	int idx_lookup = 0;
 	int ret = 0;
 
 	if (!phys_enc) {
@@ -227,8 +237,10 @@ static int sde_encoder_phys_cmd_register_irq(struct sde_encoder_phys *phys_enc,
 		return -EINVAL;
 	}
 
+	idx_lookup = (intr_type == SDE_IRQ_TYPE_INTF_UNDER_RUN) ?
+			cmd_enc->intf_idx : phys_enc->hw_pp->idx;
 	cmd_enc->irq_idx[idx] = sde_core_irq_idx_lookup(phys_enc->sde_kms,
-			intr_type, phys_enc->hw_pp->idx);
+			intr_type, idx_lookup);
 	if (cmd_enc->irq_idx[idx] < 0) {
 		SDE_ERROR_CMDENC(cmd_enc,
 			"failed to lookup IRQ index for %s with pp=%d\n",
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
index abad24d..b5f170c 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c
@@ -570,16 +570,22 @@ static void sde_encoder_phys_vid_get_hw_resources(
 	hw_res->intfs[vid_enc->hw_intf->idx - INTF_0] = INTF_MODE_VIDEO;
 }
 
-static int sde_encoder_phys_vid_wait_for_commit_done(
-		struct sde_encoder_phys *phys_enc)
+static int sde_encoder_phys_vid_wait_for_vblank(
+		struct sde_encoder_phys *phys_enc, bool notify)
 {
 	struct sde_encoder_phys_vid *vid_enc =
 			to_sde_encoder_phys_vid(phys_enc);
 	u32 irq_status;
 	int ret;
 
-	if (!sde_encoder_phys_vid_is_master(phys_enc))
+	if (!sde_encoder_phys_vid_is_master(phys_enc)) {
+		/* always signal done for slave video encoder */
+		if (notify && phys_enc->parent_ops.handle_frame_done)
+			phys_enc->parent_ops.handle_frame_done(
+					phys_enc->parent, phys_enc,
+					SDE_ENCODER_FRAME_EVENT_DONE);
 		return 0;
+	}
 
 	if (phys_enc->enable_state != SDE_ENC_ENABLED) {
 		SDE_ERROR("encoder not enabled\n");
@@ -603,6 +609,10 @@ static int sde_encoder_phys_vid_wait_for_commit_done(
 			SDE_EVT32(DRMID(phys_enc->parent),
 					vid_enc->hw_intf->idx - INTF_0);
 			SDE_DEBUG_VIDENC(vid_enc, "done, irq not triggered\n");
+			if (notify && phys_enc->parent_ops.handle_frame_done)
+				phys_enc->parent_ops.handle_frame_done(
+						phys_enc->parent, phys_enc,
+						SDE_ENCODER_FRAME_EVENT_DONE);
 			sde_encoder_phys_vid_vblank_irq(vid_enc,
 					INTR_IDX_VSYNC);
 			ret = 0;
@@ -610,15 +620,33 @@ static int sde_encoder_phys_vid_wait_for_commit_done(
 			SDE_EVT32(DRMID(phys_enc->parent),
 					vid_enc->hw_intf->idx - INTF_0);
 			SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n");
+			if (notify && phys_enc->parent_ops.handle_frame_done)
+				phys_enc->parent_ops.handle_frame_done(
+						phys_enc->parent, phys_enc,
+						SDE_ENCODER_FRAME_EVENT_ERROR);
 			ret = -ETIMEDOUT;
 		}
 	} else {
+		if (notify && phys_enc->parent_ops.handle_frame_done)
+			phys_enc->parent_ops.handle_frame_done(
+					phys_enc->parent, phys_enc,
+					SDE_ENCODER_FRAME_EVENT_DONE);
 		ret = 0;
 	}
 
 	return 0;
 }
 
+static int sde_encoder_phys_vid_wait_for_commit_done(
+		struct sde_encoder_phys *phys_enc)
+{
+	int ret;
+
+	ret = sde_encoder_phys_vid_wait_for_vblank(phys_enc, true);
+
+	return ret;
+}
+
 static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
 {
 	struct sde_encoder_phys_vid *vid_enc;
@@ -662,7 +690,7 @@ static void sde_encoder_phys_vid_disable(struct sde_encoder_phys *phys_enc)
 	 * scanout buffer) don't latch properly..
 	 */
 	if (sde_encoder_phys_vid_is_master(phys_enc)) {
-		ret = sde_encoder_phys_vid_wait_for_commit_done(phys_enc);
+		ret = sde_encoder_phys_vid_wait_for_vblank(phys_enc, false);
 		if (ret) {
 			atomic_set(&phys_enc->pending_kickoff_cnt, 0);
 			SDE_ERROR_VIDENC(vid_enc,
diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
index 768f59c..3aa4e16 100644
--- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
+++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_wb.c
@@ -537,10 +537,14 @@ static void sde_encoder_phys_wb_done_irq(void *arg, int irq_idx)
 	SDE_DEBUG("[wb:%d,%u]\n", hw_wb->idx - WB_0,
 			wb_enc->frame_count);
 
-	complete_all(&wb_enc->wbdone_complete);
+	if (phys_enc->parent_ops.handle_frame_done)
+		phys_enc->parent_ops.handle_frame_done(phys_enc->parent,
+				phys_enc, SDE_ENCODER_FRAME_EVENT_DONE);
 
 	phys_enc->parent_ops.handle_vblank_virt(phys_enc->parent,
 			phys_enc);
+
+	complete_all(&wb_enc->wbdone_complete);
 }
 
 /**
@@ -689,6 +693,10 @@ static int sde_encoder_phys_wb_wait_for_commit_done(
 		} else {
 			SDE_ERROR("wb:%d kickoff timed out\n",
 					wb_enc->wb_dev->wb_idx - WB_0);
+			if (phys_enc->parent_ops.handle_frame_done)
+				phys_enc->parent_ops.handle_frame_done(
+						phys_enc->parent, phys_enc,
+						SDE_ENCODER_FRAME_EVENT_ERROR);
 			rc = -ETIMEDOUT;
 		}
 	}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
index 7aecec0..7d233e6 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.c
@@ -46,9 +46,6 @@
 /* default hardware block size if dtsi entry is not present */
 #define DEFAULT_SDE_HW_BLOCK_LEN 0x100
 
-/* default rects for multi rect case */
-#define DEFAULT_SDE_SSPP_MAX_RECTS 1
-
 /* total number of intf - dp, dsi, hdmi */
 #define INTF_COUNT			3
 
@@ -106,6 +103,7 @@ enum sde_prop {
 	CDP,
 	SRC_SPLIT,
 	DIM_LAYER,
+	SMART_DMA_REV,
 	SDE_PROP_MAX,
 };
 
@@ -118,11 +116,11 @@ enum {
 	SSPP_CLK_STATUS,
 	SSPP_DANGER,
 	SSPP_SAFE,
-	SSPP_MAX_RECTS,
 	SSPP_SCALE_SIZE,
 	SSPP_VIG_BLOCKS,
 	SSPP_RGB_BLOCKS,
 	SSPP_EXCL_RECT,
+	SSPP_SMART_DMA,
 	SSPP_PROP_MAX,
 };
 
@@ -227,6 +225,13 @@ enum {
 	VBIF_PROP_MAX,
 };
 
+enum {
+	REG_DMA_OFF,
+	REG_DMA_VERSION,
+	REG_DMA_TRIGGER_OFF,
+	REG_DMA_PROP_MAX
+};
+
 /*************************************************************
  * dts property definition
  *************************************************************/
@@ -276,6 +281,7 @@ static struct sde_prop_type sde_prop[] = {
 	{CDP, "qcom,sde-has-cdp", false, PROP_TYPE_BOOL},
 	{SRC_SPLIT, "qcom,sde-has-src-split", false, PROP_TYPE_BOOL},
 	{DIM_LAYER, "qcom,sde-has-dim-layer", false, PROP_TYPE_BOOL},
+	{SMART_DMA_REV, "qcom,sde-smart-dma-rev", false, PROP_TYPE_STRING},
 };
 
 static struct sde_prop_type sspp_prop[] = {
@@ -289,11 +295,12 @@ static struct sde_prop_type sspp_prop[] = {
 		PROP_TYPE_BIT_OFFSET_ARRAY},
 	{SSPP_DANGER, "qcom,sde-sspp-danger-lut", false, PROP_TYPE_U32_ARRAY},
 	{SSPP_SAFE, "qcom,sde-sspp-safe-lut", false, PROP_TYPE_U32_ARRAY},
-	{SSPP_MAX_RECTS, "qcom,sde-sspp-max-rects", false, PROP_TYPE_U32_ARRAY},
 	{SSPP_SCALE_SIZE, "qcom,sde-sspp-scale-size", false, PROP_TYPE_U32},
 	{SSPP_VIG_BLOCKS, "qcom,sde-sspp-vig-blocks", false, PROP_TYPE_NODE},
 	{SSPP_RGB_BLOCKS, "qcom,sde-sspp-rgb-blocks", false, PROP_TYPE_NODE},
 	{SSPP_EXCL_RECT, "qcom,sde-sspp-excl-rect", false, PROP_TYPE_U32_ARRAY},
+	{SSPP_SMART_DMA, "qcom,sde-sspp-smart-dma-priority", false,
+		PROP_TYPE_U32_ARRAY},
 };
 
 static struct sde_prop_type vig_prop[] = {
@@ -406,6 +413,16 @@ static struct sde_prop_type vbif_prop[] = {
 		PROP_TYPE_U32_ARRAY},
 };
 
+static struct sde_prop_type reg_dma_prop[REG_DMA_PROP_MAX] = {
+	[REG_DMA_OFF] =  {REG_DMA_OFF, "qcom,sde-reg-dma-off", false,
+		PROP_TYPE_U32},
+	[REG_DMA_VERSION] = {REG_DMA_VERSION, "qcom,sde-reg-dma-version",
+		false, PROP_TYPE_U32},
+	[REG_DMA_TRIGGER_OFF] = {REG_DMA_TRIGGER_OFF,
+		"qcom,sde-reg-dma-trigger-off", false,
+		PROP_TYPE_U32},
+};
+
 /*************************************************************
  * static API list
  *************************************************************/
@@ -883,6 +900,13 @@ static int sde_sspp_parse_dt(struct device_node *np,
 		sblk->maxlinewidth = sde_cfg->max_sspp_linewidth;
 
 		set_bit(SDE_SSPP_SRC, &sspp->features);
+
+		sblk->smart_dma_priority =
+			PROP_VALUE_ACCESS(prop_value, SSPP_SMART_DMA, i);
+
+		if (sblk->smart_dma_priority && sde_cfg->smart_dma_rev)
+			set_bit(sde_cfg->smart_dma_rev, &sspp->features);
+
 		sblk->src_blk.id = SDE_SSPP_SRC;
 
 		of_property_read_string_index(np,
@@ -1798,7 +1822,7 @@ static int sde_pp_parse_dt(struct device_node *np, struct sde_mdss_cfg *sde_cfg)
 
 static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
 {
-	int rc, len, prop_count[SDE_PROP_MAX];
+	int rc, dma_rc, len, prop_count[SDE_PROP_MAX];
 	struct sde_prop_value *prop_value = NULL;
 	bool prop_exists[SDE_PROP_MAX];
 	const char *type;
@@ -1862,16 +1886,38 @@ static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
 		cfg->mdp[0].highest_bank_bit = DEFAULT_SDE_HIGHEST_BANK_BIT;
 
 	rc = of_property_read_string(np, sde_prop[QSEED_TYPE].prop_name, &type);
-	if (!rc && !strcmp(type, "qseedv3"))
+	if (!rc && !strcmp(type, "qseedv3")) {
 		cfg->qseed_type = SDE_SSPP_SCALER_QSEED3;
-	else if (!rc && !strcmp(type, "qseedv2"))
+	} else if (!rc && !strcmp(type, "qseedv2")) {
 		cfg->qseed_type = SDE_SSPP_SCALER_QSEED2;
+	} else if (rc) {
+		SDE_DEBUG("invalid QSEED configuration\n");
+		rc = 0;
+	}
 
 	rc = of_property_read_string(np, sde_prop[CSC_TYPE].prop_name, &type);
-	if (!rc && !strcmp(type, "csc"))
+	if (!rc && !strcmp(type, "csc")) {
 		cfg->csc_type = SDE_SSPP_CSC;
-	else if (!rc && !strcmp(type, "csc-10bit"))
+	} else if (!rc && !strcmp(type, "csc-10bit")) {
 		cfg->csc_type = SDE_SSPP_CSC_10BIT;
+	} else if (rc) {
+		SDE_DEBUG("invalid csc configuration\n");
+		rc = 0;
+	}
+
+	/*
+	 * Current SDE support only Smart DMA 2.0.
+	 * No support for Smart DMA 1.0 yet.
+	 */
+	cfg->smart_dma_rev = 0;
+	dma_rc = of_property_read_string(np, sde_prop[SMART_DMA_REV].prop_name,
+			&type);
+	if (!dma_rc && !strcmp(type, "smart_dma_v2")) {
+		cfg->smart_dma_rev = SDE_SSPP_SMART_DMA_V2;
+	} else if (!dma_rc && !strcmp(type, "smart_dma_v1")) {
+		SDE_ERROR("smart dma 1.0 is not supported in SDE\n");
+		cfg->smart_dma_rev = 0;
+	}
 
 	cfg->has_src_split = PROP_VALUE_ACCESS(prop_value, SRC_SPLIT, 0);
 	cfg->has_dim_layer = PROP_VALUE_ACCESS(prop_value, DIM_LAYER, 0);
@@ -1880,6 +1926,39 @@ static int sde_parse_dt(struct device_node *np, struct sde_mdss_cfg *cfg)
 	return rc;
 }
 
+static int sde_parse_reg_dma_dt(struct device_node *np,
+		struct sde_mdss_cfg *sde_cfg)
+{
+	u32 val;
+	int rc = 0;
+	int i = 0;
+
+	sde_cfg->reg_dma_count = 0;
+	for (i = 0; i < REG_DMA_PROP_MAX; i++) {
+		rc = of_property_read_u32(np, reg_dma_prop[i].prop_name,
+				&val);
+		if (rc)
+			break;
+		switch (i) {
+		case REG_DMA_OFF:
+			sde_cfg->dma_cfg.base = val;
+			break;
+		case REG_DMA_VERSION:
+			sde_cfg->dma_cfg.version = val;
+			break;
+		case REG_DMA_TRIGGER_OFF:
+			sde_cfg->dma_cfg.trigger_sel_off = val;
+			break;
+		default:
+			break;
+		}
+	}
+	if (!rc && i == REG_DMA_PROP_MAX)
+		sde_cfg->reg_dma_count = 1;
+	/* reg dma is optional feature hence return 0 */
+	return 0;
+}
+
 static void sde_hardware_caps(struct sde_mdss_cfg *sde_cfg, uint32_t hw_rev)
 {
 	switch (hw_rev) {
@@ -1981,6 +2060,10 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev)
 	if (rc)
 		goto end;
 
+	rc = sde_parse_reg_dma_dt(np, sde_cfg);
+	if (rc)
+		goto end;
+
 	sde_hardware_caps(sde_cfg, hw_rev);
 
 	return sde_cfg;
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
index 44365b5..5e35e4e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_catalog.h
@@ -93,6 +93,8 @@ enum {
  * @SDE_SSPP_CURSOR,         SSPP can be used as a cursor layer
  * @SDE_SSPP_QOS,            SSPP support QoS control, danger/safe/creq
  * @SDE_SSPP_EXCL_RECT,      SSPP supports exclusion rect
+ * @SDE_SSPP_SMART_DMA_V1,   SmartDMA 1.0 support
+ * @SDE_SSPP_SMART_DMA_V2,   SmartDMA 2.0 support
  * @SDE_SSPP_MAX             maximum value
  */
 enum {
@@ -109,6 +111,8 @@ enum {
 	SDE_SSPP_CURSOR,
 	SDE_SSPP_QOS,
 	SDE_SSPP_EXCL_RECT,
+	SDE_SSPP_SMART_DMA_V1,
+	SDE_SSPP_SMART_DMA_V2,
 	SDE_SSPP_MAX
 };
 
@@ -317,6 +321,7 @@ struct sde_format_extended {
  * @creq_vblank: creq priority during vertical blanking
  * @danger_vblank: danger priority during vertical blanking
  * @pixel_ram_size: size of latency hiding and de-tiling buffer in bytes
+ * @smart_dma_priority: hw priority of rect1 of multirect pipe
  * @src_blk:
  * @scaler_blk:
  * @csc_blk:
@@ -342,6 +347,7 @@ struct sde_sspp_sub_blks {
 	u32 maxupscale;
 	u32 maxhdeciexp; /* max decimation is 2^value */
 	u32 maxvdeciexp; /* max decimation is 2^value */
+	u32 smart_dma_priority;
 	struct sde_src_blk src_blk;
 	struct sde_scaler_blk scaler_blk;
 	struct sde_pp_blk csc_blk;
@@ -599,6 +605,19 @@ struct sde_vbif_cfg {
 	struct sde_vbif_dynamic_ot_tbl dynamic_ot_rd_tbl;
 	struct sde_vbif_dynamic_ot_tbl dynamic_ot_wr_tbl;
 };
+/**
+ * struct sde_reg_dma_cfg - information of lut dma blocks
+ * @id                 enum identifying this block
+ * @base               register offset of this block
+ * @features           bit mask identifying sub-blocks/features
+ * @version            version of lutdma hw block
+ * @trigger_sel_off    offset to trigger select registers of lutdma
+ */
+struct sde_reg_dma_cfg {
+	SDE_HW_BLK_INFO;
+	u32 version;
+	u32 trigger_sel_off;
+};
 
 /**
  * struct sde_mdss_cfg - information of MDSS HW
@@ -614,6 +633,7 @@ struct sde_vbif_cfg {
  * @highest_bank_bit   highest memory bit setting for tile buffers.
  * @qseed_type         qseed2 or qseed3 support.
  * @csc_type           csc or csc_10bit support.
+ * @smart_dma_rev      Supported version of SmartDMA feature.
  * @has_src_split      source split feature status
  * @has_cdp            Client driver prefetch feature status
  */
@@ -627,6 +647,7 @@ struct sde_mdss_cfg {
 	u32 highest_bank_bit;
 	u32 qseed_type;
 	u32 csc_type;
+	u32 smart_dma_rev;
 	bool has_src_split;
 	bool has_cdp;
 	bool has_dim_layer;
@@ -663,6 +684,9 @@ struct sde_mdss_cfg {
 
 	u32 vbif_count;
 	struct sde_vbif_cfg vbif[MAX_BLOCKS];
+
+	u32 reg_dma_count;
+	struct sde_reg_dma_cfg dma_cfg;
 	/* Add additional block data structures here */
 };
 
@@ -705,4 +729,13 @@ struct sde_mdss_cfg *sde_hw_catalog_init(struct drm_device *dev, u32 hw_rev);
  */
 void sde_hw_catalog_deinit(struct sde_mdss_cfg *sde_cfg);
 
+/**
+ * sde_hw_sspp_multirect_enabled - check multirect enabled for the sspp
+ * @cfg:          pointer to sspp cfg
+ */
+static inline bool sde_hw_sspp_multirect_enabled(const struct sde_sspp_cfg *cfg)
+{
+	return test_bit(SDE_SSPP_SMART_DMA_V1, &cfg->features) ||
+			 test_bit(SDE_SSPP_SMART_DMA_V2, &cfg->features);
+}
 #endif /* _SDE_HW_CATALOG_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
index c7cbb93..dad039e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_cdm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -55,19 +55,6 @@ static u32 cosite_v_coeff[] = {0x00080004};
  */
 static u32 offsite_v_coeff[] = {0x00060002};
 
-/* Limited Range rgb2yuv coeff with clamp and bias values for CSC 10 module */
-static struct sde_csc_cfg rgb2yuv_cfg = {
-	{
-		0x0083, 0x0102, 0x0032,
-		0x1fb5, 0x1f6c, 0x00e1,
-		0x00e1, 0x1f45, 0x1fdc
-	},
-	{ 0x00, 0x00, 0x00 },
-	{ 0x0040, 0x0200, 0x0200 },
-	{ 0x000, 0x3ff, 0x000, 0x3ff, 0x000, 0x3ff },
-	{ 0x040, 0x3ac, 0x040, 0x3c0, 0x040, 0x3c0 },
-};
-
 static struct sde_cdm_cfg *_cdm_offset(enum sde_cdm cdm,
 		struct sde_mdss_cfg *m,
 		void __iomem *addr,
@@ -327,12 +314,6 @@ struct sde_hw_cdm *sde_hw_cdm_init(enum sde_cdm idx,
 	_setup_cdm_ops(&c->ops, c->cdm_hw_cap->features);
 	c->hw_mdp = hw_mdp;
 
-	/*
-	 * Perform any default initialization for the chroma down module
-	 * @setup default csc coefficients
-	 */
-	sde_hw_cdm_setup_csc_10bit(c, &rgb2yuv_cfg);
-
 	return c;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
new file mode 100644
index 0000000..28479ab
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_common_v4.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _SDE_HW_COLOR_PROC_COMMON_V4_H_
+#define _SDE_HW_COLOR_PROC_COMMON_V4_H_
+
+#define GAMUT_TABLE_SEL_OFF 0x4
+#define GAMUT_SCALEA_OFFSET_OFF 0x10
+#define GAMUT_SCALEB_OFFSET_OFF 0x50
+#define GAMUT_LOWER_COLOR_OFF 0xc
+#define GAMUT_UPPER_COLOR_OFF 0x8
+#define GAMUT_TABLE0_SEL BIT(12)
+#define GAMUT_MAP_EN BIT(1)
+#define GAMUT_EN BIT(0)
+#define GAMUT_MODE_13B_OFF 640
+
+enum {
+	gamut_mode_17 = 0,
+	gamut_mode_5,
+	gamut_mode_13a,
+	gamut_mode_13b,
+};
+
+#define GC_C0_OFF 0x4
+#define GC_C0_INDEX_OFF 0x8
+#define GC_8B_ROUND_EN BIT(1)
+#define GC_EN BIT(0)
+#define GC_TBL_NUM 3
+#define GC_LUT_SWAP_OFF 0x1c
+
+#endif /* _SDE_HW_COLOR_PROC_COMMON_V4_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_v4.c b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_v4.c
new file mode 100644
index 0000000..42d1480
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_v4.c
@@ -0,0 +1,111 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <drm/msm_drm_pp.h>
+#include "sde_hw_color_proc_common_v4.h"
+#include "sde_hw_color_proc_v4.h"
+
+static int sde_write_3d_gamut(struct sde_hw_blk_reg_map *hw,
+		struct drm_msm_3d_gamut *payload, u32 base,
+		u32 *opcode)
+{
+	u32 reg, tbl_len, tbl_off, scale_off, i, j;
+	u32 *scale_data;
+
+	if (!payload || !opcode || !hw) {
+		DRM_ERROR("invalid payload %pK opcode %pK hw %pK\n",
+			payload, opcode, hw);
+		return -EINVAL;
+	}
+
+	switch (payload->mode) {
+	case GAMUT_3D_MODE_17:
+		tbl_len = GAMUT_3D_MODE17_TBL_SZ;
+		tbl_off = 0;
+		scale_off = GAMUT_SCALEA_OFFSET_OFF;
+		*opcode = gamut_mode_17 << 2;
+		break;
+	case GAMUT_3D_MODE_13:
+		*opcode = (*opcode & (BIT(4) - 1)) >> 2;
+		if (*opcode == gamut_mode_13a)
+			*opcode = gamut_mode_13b;
+		else
+			*opcode = gamut_mode_13a;
+		tbl_len = GAMUT_3D_MODE13_TBL_SZ;
+		tbl_off = (*opcode == gamut_mode_13a) ? 0 :
+			GAMUT_MODE_13B_OFF;
+		scale_off = (*opcode == gamut_mode_13a) ?
+			GAMUT_SCALEA_OFFSET_OFF : GAMUT_SCALEB_OFFSET_OFF;
+		*opcode <<= 2;
+		break;
+	case GAMUT_3D_MODE_5:
+		*opcode = gamut_mode_5 << 2;
+		*opcode |= GAMUT_MAP_EN;
+		tbl_len = GAMUT_3D_MODE5_TBL_SZ;
+		tbl_off = 0;
+		scale_off = GAMUT_SCALEB_OFFSET_OFF;
+		break;
+	default:
+		DRM_ERROR("invalid mode %d\n", payload->mode);
+		return -EINVAL;
+	}
+
+	if (payload->flags & GAMUT_3D_MAP_EN)
+		*opcode |= GAMUT_MAP_EN;
+	*opcode |= GAMUT_EN;
+
+	for (i = 0; i < GAMUT_3D_TBL_NUM; i++) {
+		reg = GAMUT_TABLE0_SEL << i;
+		reg |= ((tbl_off) & (BIT(11) - 1));
+		SDE_REG_WRITE(hw, base + GAMUT_TABLE_SEL_OFF, reg);
+		for (j = 0; j < tbl_len; j++) {
+			SDE_REG_WRITE(hw, base + GAMUT_LOWER_COLOR_OFF,
+					payload->col[i][j].c2_c1);
+			SDE_REG_WRITE(hw, base + GAMUT_UPPER_COLOR_OFF,
+					payload->col[i][j].c0);
+		}
+	}
+
+	if ((*opcode & GAMUT_MAP_EN)) {
+		scale_data = &payload->scale_off[0][0];
+		tbl_off = base + scale_off;
+		tbl_len = GAMUT_3D_SCALE_OFF_TBL_NUM * GAMUT_3D_SCALE_OFF_SZ;
+		for (i = 0; i < tbl_len; i++)
+			SDE_REG_WRITE(hw, tbl_off + (i * sizeof(u32)),
+					scale_data[i]);
+	}
+	SDE_REG_WRITE(hw, base, *opcode);
+	return 0;
+}
+
+void sde_setup_dspp_3d_gamutv4(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_3d_gamut *payload;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	u32 op_mode;
+
+	if (!ctx || !cfg) {
+		DRM_ERROR("invalid param ctx %pK cfg %pK\n", ctx, cfg);
+		return;
+	}
+
+	op_mode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->gamut.base);
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable gamut feature\n");
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gamut.base, 0);
+		return;
+	}
+
+	payload = hw_cfg->payload;
+	sde_write_3d_gamut(&ctx->hw, payload, ctx->cap->sblk->gamut.base,
+		&op_mode);
+
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_proc_v4.h b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_v4.h
new file mode 100644
index 0000000..250830e
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_proc_v4.h
@@ -0,0 +1,26 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _SDE_HW_COLOR_PROC_V4_H_
+#define _SDE_HW_COLOR_PROC_V4_H_
+
+#include "sde_hw_util.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_dspp.h"
+/**
+ * sde_setup_dspp_3d_gamutv4 - Function for 3d gamut v4 version feature
+ *                             programming.
+ * @ctx: dspp ctx pointer
+ * @cfg: pointer to sde_hw_cp_cfg
+ */
+void sde_setup_dspp_3d_gamutv4(struct sde_hw_dspp *ctx, void *cfg);
+
+#endif /* _SDE_HW_COLOR_PROC_V4_H_ */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
index a30e1a5..3e531f0 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -14,5 +14,7 @@
 #define _SDE_HW_COLOR_PROCESSING_H
 
 #include "sde_hw_color_processing_v1_7.h"
+#include "sde_hw_reg_dma_v1_color_proc.h"
+#include "sde_hw_color_proc_v4.h"
 
 #endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
index f1f66f3..ab2c473 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -101,6 +101,14 @@
 #define SSPP	0
 #define DSPP	1
 
+#define PGC_C0_OFF 0x4
+#define PGC_C0_INDEX_OFF 0x8
+#define PGC_8B_ROUND_EN BIT(1)
+#define PGC_EN BIT(0)
+#define PGC_TBL_NUM 3
+#define PGC_LUT_SWAP_OFF 0x1c
+
+
 static void __setup_pa_hue(struct sde_hw_blk_reg_map *hw,
 			const struct sde_pp_blk *blk, uint32_t hue,
 			int location)
@@ -451,3 +459,49 @@ void sde_setup_dspp_pa_vlut_v1_7(struct sde_hw_dspp *ctx, void *cfg)
 	op_mode |= DSPP_OP_PA_EN | DSPP_OP_PA_LUTV_EN;
 	SDE_REG_WRITE(&ctx->hw, base, op_mode);
 }
+
+void sde_setup_dspp_gc_v1_7(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_pgc_lut *payload = NULL;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	u32 c0_off, c1_off, c2_off, i;
+
+	if (!hw_cfg || (hw_cfg->payload && hw_cfg->len !=
+			sizeof(struct drm_msm_pgc_lut))) {
+		DRM_ERROR("hw %pK payload %pK payloadsize %d exp size %zd\n",
+			  hw_cfg, ((hw_cfg) ? hw_cfg->payload : NULL),
+			  ((hw_cfg) ? hw_cfg->len : 0),
+			  sizeof(struct drm_msm_pgc_lut));
+		return;
+	}
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("Disable pgc feature\n");
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, 0);
+		return;
+	}
+	payload = hw_cfg->payload;
+
+	/* Initialize index offsets */
+	c0_off = ctx->cap->sblk->gc.base + PGC_C0_INDEX_OFF;
+	c1_off = c0_off + (sizeof(u32) * 2);
+	c2_off = c1_off + (sizeof(u32) * 2);
+	SDE_REG_WRITE(&ctx->hw, c0_off, 0);
+	SDE_REG_WRITE(&ctx->hw, c1_off, 0);
+	SDE_REG_WRITE(&ctx->hw, c2_off, 0);
+
+	/* Initialize table offsets */
+	c0_off = ctx->cap->sblk->gc.base + PGC_C0_OFF;
+	c1_off = c0_off + (sizeof(u32) * 2);
+	c2_off = c1_off + (sizeof(u32) * 2);
+
+	for (i = 0; i < PGC_TBL_LEN; i++) {
+		SDE_REG_WRITE(&ctx->hw, c0_off, payload->c0[i]);
+		SDE_REG_WRITE(&ctx->hw, c1_off, payload->c1[i]);
+		SDE_REG_WRITE(&ctx->hw, c2_off, payload->c2[i]);
+	}
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base + PGC_LUT_SWAP_OFF,
+			BIT(0));
+	i = BIT(0) | ((payload->flags & PGC_8B_ROUND) ? BIT(1) : 0);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, i);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
index 0f9bc0e..25e446b7 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -75,4 +75,11 @@ void sde_setup_dspp_pa_hue_v1_7(struct sde_hw_dspp *ctx, void *cfg);
  */
 void sde_setup_dspp_pa_vlut_v1_7(struct sde_hw_dspp *ctx, void *cfg);
 
+/**
+ * sde_setup_dspp_gc_v1_7 - setup DSPP gc feature in v1.7 hardware
+ * @ctx: Pointer to DSPP context
+ * @cfg: Pointer to gc data
+ */
+void sde_setup_dspp_gc_v1_7(struct sde_hw_dspp *ctx, void *cfg);
+
 #endif
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
index 56d9f2a..19e3a7a5 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.c
@@ -20,6 +20,8 @@
 	(0x40 + (((lm) - LM_0) * 0x004))
 #define   CTL_LAYER_EXT2(lm)             \
 	(0x70 + (((lm) - LM_0) * 0x004))
+#define   CTL_LAYER_EXT3(lm)             \
+	(0xA0 + (((lm) - LM_0) * 0x004))
 #define   CTL_TOP                       0x014
 #define   CTL_FLUSH                     0x018
 #define   CTL_START                     0x01C
@@ -281,7 +283,8 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
 	enum sde_lm lm, struct sde_hw_stage_cfg *stage_cfg, u32 index)
 {
 	struct sde_hw_blk_reg_map *c = &ctx->hw;
-	u32 mixercfg, mixercfg_ext, mix, ext, mixercfg_ext2;
+	u32 mixercfg = 0, mixercfg_ext = 0, mix, ext;
+	u32 mixercfg_ext2 = 0, mixercfg_ext3 = 0;
 	int i, j;
 	u8 stages;
 	int pipes_per_stage;
@@ -300,8 +303,6 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
 		pipes_per_stage = 1;
 
 	mixercfg = BIT(24); /* always set BORDER_OUT */
-	mixercfg_ext = 0;
-	mixercfg_ext2 = 0;
 
 	for (i = 0; i <= stages; i++) {
 		/* overflow to ext register if 'i + 1 > 7' */
@@ -309,22 +310,41 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
 		ext = i >= 7;
 
 		for (j = 0 ; j < pipes_per_stage; j++) {
+			enum sde_sspp_multirect_index rect_index =
+				stage_cfg->multirect_index[index][i][j];
+
 			switch (stage_cfg->stage[index][i][j]) {
 			case SSPP_VIG0:
-				mixercfg |= mix << 0;
-				mixercfg_ext |= ext << 0;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 0;
+				} else {
+					mixercfg |= mix << 0;
+					mixercfg_ext |= ext << 0;
+				}
 				break;
 			case SSPP_VIG1:
-				mixercfg |= mix << 3;
-				mixercfg_ext |= ext << 2;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 4;
+				} else {
+					mixercfg |= mix << 3;
+					mixercfg_ext |= ext << 2;
+				}
 				break;
 			case SSPP_VIG2:
-				mixercfg |= mix << 6;
-				mixercfg_ext |= ext << 4;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 8;
+				} else {
+					mixercfg |= mix << 6;
+					mixercfg_ext |= ext << 4;
+				}
 				break;
 			case SSPP_VIG3:
-				mixercfg |= mix << 26;
-				mixercfg_ext |= ext << 6;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext3 |= ((i + 1) & 0xF) << 12;
+				} else {
+					mixercfg |= mix << 26;
+					mixercfg_ext |= ext << 6;
+				}
 				break;
 			case SSPP_RGB0:
 				mixercfg |= mix << 9;
@@ -343,20 +363,36 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
 				mixercfg_ext |= ext << 14;
 				break;
 			case SSPP_DMA0:
-				mixercfg |= mix << 18;
-				mixercfg_ext |= ext << 16;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 8;
+				} else {
+					mixercfg |= mix << 18;
+					mixercfg_ext |= ext << 16;
+				}
 				break;
 			case SSPP_DMA1:
-				mixercfg |= mix << 21;
-				mixercfg_ext |= ext << 18;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 12;
+				} else {
+					mixercfg |= mix << 21;
+					mixercfg_ext |= ext << 18;
+				}
 				break;
 			case SSPP_DMA2:
-				mix = (i + 1) & 0xf;
-				mixercfg_ext2 |= mix << 0;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 16;
+				} else {
+					mix |= (i + 1) & 0xF;
+					mixercfg_ext2 |= mix << 0;
+				}
 				break;
 			case SSPP_DMA3:
-				mix = (i + 1) & 0xf;
-				mixercfg_ext2 |= mix << 4;
+				if (rect_index == SDE_SSPP_RECT_1) {
+					mixercfg_ext2 |= ((i + 1) & 0xF) << 20;
+				} else {
+					mix |= (i + 1) & 0xF;
+					mixercfg_ext2 |= mix << 4;
+				}
 				break;
 			case SSPP_CURSOR0:
 				mixercfg_ext |= ((i + 1) & 0xF) << 20;
@@ -373,6 +409,7 @@ static void sde_hw_ctl_setup_blendstage(struct sde_hw_ctl *ctx,
 	SDE_REG_WRITE(c, CTL_LAYER(lm), mixercfg);
 	SDE_REG_WRITE(c, CTL_LAYER_EXT(lm), mixercfg_ext);
 	SDE_REG_WRITE(c, CTL_LAYER_EXT2(lm), mixercfg_ext2);
+	SDE_REG_WRITE(c, CTL_LAYER_EXT3(lm), mixercfg_ext3);
 }
 
 static void sde_hw_ctl_intf_cfg(struct sde_hw_ctl *ctx,
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
index 2fb7b37..670a03d 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_ctl.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -16,6 +16,7 @@
 #include "sde_hw_mdss.h"
 #include "sde_hw_util.h"
 #include "sde_hw_catalog.h"
+#include "sde_hw_sspp.h"
 
 /**
  * sde_ctl_mode_sel: Interface mode selection
@@ -30,10 +31,13 @@ enum sde_ctl_mode_sel {
 struct sde_hw_ctl;
 /**
  * struct sde_hw_stage_cfg - blending stage cfg
- * @stage
+ * @stage : SSPP_ID at each stage
+ * @multirect_index: index of the rectangle of SSPP.
  */
 struct sde_hw_stage_cfg {
 	enum sde_sspp stage[CRTC_DUAL_MIXERS][SDE_STAGE_MAX][PIPES_PER_STAGE];
+	enum sde_sspp_multirect_index multirect_index[CRTC_DUAL_MIXERS]
+					[SDE_STAGE_MAX][PIPES_PER_STAGE];
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
index 66c03f0..51ab26e 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_dspp.c
@@ -36,33 +36,9 @@ static struct sde_dspp_cfg *_dspp_offset(enum sde_dspp dspp,
 	return ERR_PTR(-EINVAL);
 }
 
-void sde_dspp_setup_histogram(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_read_histogram(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_update_igc(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_setup_sharpening(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_setup_danger_safe(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
-void sde_dspp_setup_dither(struct sde_hw_dspp *ctx, void *cfg)
-{
-}
-
 static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
 {
-	int i = 0;
+	int i = 0, ret;
 
 	for (i = 0; i < SDE_DSPP_MAX; i++) {
 		if (!test_bit(i, &features))
@@ -81,8 +57,43 @@ static void _setup_dspp_ops(struct sde_hw_dspp *c, unsigned long features)
 		case SDE_DSPP_VLUT:
 			if (c->cap->sblk->vlut.version ==
 				(SDE_COLOR_PROCESS_VER(0x1, 0x7))) {
-				c->ops.setup_vlut = sde_setup_dspp_pa_vlut_v1_7;
+				c->ops.setup_vlut =
+				    sde_setup_dspp_pa_vlut_v1_7;
+			} else if (c->cap->sblk->vlut.version ==
+					(SDE_COLOR_PROCESS_VER(0x1, 0x8))) {
+				ret = reg_dmav1_init_dspp_op_v4(i, c->idx);
+				if (ret)
+					c->ops.setup_vlut =
+					reg_dmav1_setup_dspp_vlutv18;
 			}
+			break;
+		case SDE_DSPP_GAMUT:
+			if (c->cap->sblk->gamut.version ==
+					SDE_COLOR_PROCESS_VER(0x4, 0)) {
+				ret = reg_dmav1_init_dspp_op_v4(i, c->idx);
+				if (!ret)
+					c->ops.setup_gamut =
+						reg_dmav1_setup_dspp_3d_gamutv4;
+				else
+					c->ops.setup_gamut =
+						sde_setup_dspp_3d_gamutv4;
+			}
+			break;
+		case SDE_DSPP_GC:
+			if (c->cap->sblk->gc.version ==
+					SDE_COLOR_PROCESS_VER(0x1, 8)) {
+				ret = reg_dmav1_init_dspp_op_v4(i, c->idx);
+				if (!ret)
+					c->ops.setup_gc =
+						reg_dmav1_setup_dspp_gcv18;
+				/** programming for v18 through ahb is same
+				 * as v17 hence assign v17 function
+				 */
+				else
+					c->ops.setup_gc =
+						sde_setup_dspp_gc_v1_7;
+			}
+			break;
 		default:
 			break;
 		}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
index 4993036..e68e3c9 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_interrupts.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -817,21 +817,11 @@ static int sde_hw_intr_disable_irq(struct sde_hw_intr *intr, int irq_idx)
 
 static int sde_hw_intr_clear_irqs(struct sde_hw_intr *intr)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(sde_intr_set); i++)
-		SDE_REG_WRITE(&intr->hw, sde_intr_set[i].clr_off, 0xffffffff);
-
 	return 0;
 }
 
 static int sde_hw_intr_disable_irqs(struct sde_hw_intr *intr)
 {
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(sde_intr_set); i++)
-		SDE_REG_WRITE(&intr->hw, sde_intr_set[i].en_off, 0x00000000);
-
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
index 1640248..f799ad1 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_mdss.h
@@ -438,10 +438,12 @@ struct sde_mdss_color {
  * struct sde_hw_cp_cfg: hardware dspp/lm feature payload.
  * @payload: Feature specific payload.
  * @len: Length of the payload.
+ * @ctl: control pointer associated with dspp/lm.
  */
 struct sde_hw_cp_cfg {
 	void *payload;
 	u32 len;
+	void *ctl;
 };
 
 /**
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
new file mode 100644
index 0000000..678c84a
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.c
@@ -0,0 +1,650 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include "sde_hw_mdss.h"
+#include "sde_hw_ctl.h"
+#include "sde_hw_reg_dma_v1.h"
+#include "msm_drv.h"
+
+#define GUARD_BYTES (BIT(8) - 1)
+#define ALIGNED_OFFSET (U32_MAX & ~(GUARD_BYTES))
+#define ADDR_ALIGN BIT(8)
+#define MAX_RELATIVE_OFF (BIT(20) - 1)
+
+#define DECODE_SEL_OP (BIT(HW_BLK_SELECT))
+#define REG_WRITE_OP ((BIT(REG_SINGLE_WRITE)) | (BIT(REG_BLK_WRITE_SINGLE)) | \
+	(BIT(REG_BLK_WRITE_INC)) | (BIT(REG_BLK_WRITE_MULTIPLE)))
+
+#define REG_DMA_OPS (DECODE_SEL_OP | REG_WRITE_OP)
+#define IS_OP_ALLOWED(op, buf_op) (BIT(op) & buf_op)
+
+#define REG_DMA_OP_MODE_OFF 0x4
+
+#define REG_DMA_CTL0_QUEUE_0_CMD0_OFF 0x14
+#define REG_DMA_CTL0_RESET_OFF 0xE4
+#define REG_DMA_CTL_TRIGGER_OFF 0xD4
+
+#define SET_UP_REG_DMA_REG(hw, reg_dma) \
+	do { \
+		(hw).base_off = (reg_dma)->addr; \
+		(hw).blk_off = (reg_dma)->caps->base; \
+		(hw).hwversion = (reg_dma)->caps->version; \
+} while (0)
+
+#define SIZE_DWORD(x) ((x) / (sizeof(u32)))
+#define NOT_WORD_ALIGNED(x) ((x) & 0x3)
+
+
+#define GRP_VIG_HW_BLK_SELECT (VIG0 | VIG1 | VIG2 | VIG3)
+#define GRP_DSPP_HW_BLK_SELECT (DSPP0 | DSPP1 | DSPP2 | DSPP3)
+#define BUFFER_SPACE_LEFT(cfg) ((cfg)->dma_buf->buffer_size - \
+			(cfg)->dma_buf->index)
+
+#define REG_DMA_DECODE_SEL 0x180AC060
+#define SINGLE_REG_WRITE_OPCODE (BIT(28))
+#define REL_ADDR_OPCODE (BIT(27))
+#define HW_INDEX_REG_WRITE_OPCODE (BIT(28) | BIT(29))
+#define AUTO_INC_REG_WRITE_OPCODE (BIT(30))
+#define BLK_REG_WRITE_OPCODE (BIT(30) | BIT(28))
+
+#define WRAP_MIN_SIZE 2
+#define WRAP_MAX_SIZE (BIT(4) - 1)
+#define MAX_DWORDS_SZ (BIT(14) - 1)
+
+typedef int (*reg_dma_internal_ops) (struct sde_reg_dma_setup_ops_cfg *cfg);
+
+static struct sde_hw_reg_dma *reg_dma;
+static u32 ops_mem_size[REG_DMA_SETUP_OPS_MAX] = {
+	[REG_BLK_WRITE_SINGLE] = sizeof(u32) * 2,
+	[REG_BLK_WRITE_INC] = sizeof(u32) * 2,
+	[REG_BLK_WRITE_MULTIPLE] = sizeof(u32) * 2,
+	[HW_BLK_SELECT] = sizeof(u32) * 2,
+	[REG_SINGLE_WRITE] = sizeof(u32) * 2
+};
+
+static u32 queue_sel[DMA_CTL_QUEUE_MAX] = {
+	[DMA_CTL_QUEUE0] = BIT(0),
+	[DMA_CTL_QUEUE1] = BIT(4),
+};
+
+static u32 reg_dma_ctl_queue_off[CTL_MAX];
+static u32 dspp_read_sel[DSPP_HIST_MAX] = {
+	[DSPP0_HIST] = 0,
+	[DSPP1_HIST] = 1,
+	[DSPP2_HIST] = 2,
+	[DSPP3_HIST] = 3,
+};
+
+static u32 v1_supported[REG_DMA_FEATURES_MAX]  = {
+	[GAMUT] = GRP_VIG_HW_BLK_SELECT | GRP_DSPP_HW_BLK_SELECT,
+	[VLUT] = GRP_DSPP_HW_BLK_SELECT,
+	[GC] = GRP_DSPP_HW_BLK_SELECT,
+};
+
+static int validate_dma_cfg(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int validate_write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int validate_write_reg(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int validate_write_multi_lut_reg(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int write_single_reg(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int write_multi_reg_index(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int write_multi_reg_inc(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int write_multi_lut_reg(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int reset_reg_dma_buffer_v1(struct sde_reg_dma_buffer *lut_buf);
+static int check_support_v1(enum sde_reg_dma_features feature,
+		enum sde_reg_dma_blk blk, bool *is_supported);
+static int setup_payload_v1(struct sde_reg_dma_setup_ops_cfg *cfg);
+static int kick_off_v1(struct sde_reg_dma_kickoff_cfg *cfg);
+static int reset_v1(struct sde_hw_ctl *ctl);
+static struct sde_reg_dma_buffer *alloc_reg_dma_buf_v1(u32 size);
+static int dealloc_reg_dma_v1(struct sde_reg_dma_buffer *lut_buf);
+
+static reg_dma_internal_ops write_dma_op_params[REG_DMA_SETUP_OPS_MAX] = {
+	[HW_BLK_SELECT] = write_decode_sel,
+	[REG_SINGLE_WRITE] = write_single_reg,
+	[REG_BLK_WRITE_SINGLE] = write_multi_reg_inc,
+	[REG_BLK_WRITE_INC] = write_multi_reg_index,
+	[REG_BLK_WRITE_MULTIPLE] = write_multi_lut_reg,
+};
+
+static reg_dma_internal_ops validate_dma_op_params[REG_DMA_SETUP_OPS_MAX] = {
+	[HW_BLK_SELECT] = validate_write_decode_sel,
+	[REG_SINGLE_WRITE] = validate_write_reg,
+	[REG_BLK_WRITE_SINGLE] = validate_write_reg,
+	[REG_BLK_WRITE_INC] = validate_write_reg,
+	[REG_BLK_WRITE_MULTIPLE] = validate_write_multi_lut_reg,
+};
+
+static void get_decode_sel(unsigned long blk, u32 *decode_sel)
+{
+	int i = 0;
+
+	*decode_sel = 0;
+	for_each_set_bit(i, &blk, 31) {
+		switch (BIT(i)) {
+		case VIG0:
+			*decode_sel |= BIT(0);
+			break;
+		case VIG1:
+			*decode_sel |= BIT(1);
+			break;
+		case VIG2:
+			*decode_sel |= BIT(2);
+			break;
+		case VIG3:
+			*decode_sel |= BIT(3);
+			break;
+		case DSPP0:
+			*decode_sel |= BIT(17);
+			break;
+		case DSPP1:
+			*decode_sel |= BIT(18);
+			break;
+		case DSPP2:
+			*decode_sel |= BIT(19);
+			break;
+		case DSPP3:
+			*decode_sel |= BIT(20);
+			break;
+		case SSPP_IGC:
+			*decode_sel |= BIT(4);
+			break;
+		case DSPP_IGC:
+			*decode_sel |= BIT(21);
+			break;
+		default:
+			DRM_ERROR("block not supported %zx\n", BIT(i));
+			break;
+		}
+	}
+}
+
+static int write_multi_reg(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u8 *loc = NULL;
+
+	loc =  (u8 *)cfg->dma_buf->vaddr + cfg->dma_buf->index;
+	memcpy(loc, cfg->data, cfg->data_size);
+	cfg->dma_buf->index += cfg->data_size;
+	cfg->dma_buf->next_op_allowed = REG_WRITE_OP;
+	cfg->dma_buf->ops_completed |= REG_WRITE_OP;
+
+	return 0;
+}
+
+int write_multi_reg_index(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 *loc = NULL;
+
+	loc =  (u32 *)((u8 *)cfg->dma_buf->vaddr +
+			cfg->dma_buf->index);
+	loc[0] = HW_INDEX_REG_WRITE_OPCODE;
+	loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF);
+	loc[1] = SIZE_DWORD(cfg->data_size);
+	cfg->dma_buf->index += ops_mem_size[cfg->ops];
+
+	return write_multi_reg(cfg);
+}
+
+int write_multi_reg_inc(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 *loc = NULL;
+
+	loc =  (u32 *)((u8 *)cfg->dma_buf->vaddr +
+			cfg->dma_buf->index);
+	loc[0] = AUTO_INC_REG_WRITE_OPCODE;
+	loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF);
+	loc[1] = SIZE_DWORD(cfg->data_size);
+	cfg->dma_buf->index += ops_mem_size[cfg->ops];
+
+	return write_multi_reg(cfg);
+}
+
+static int write_multi_lut_reg(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 *loc = NULL;
+
+	loc =  (u32 *)((u8 *)cfg->dma_buf->vaddr +
+			cfg->dma_buf->index);
+	loc[0] = BLK_REG_WRITE_OPCODE;
+	loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF);
+	loc[1] = (cfg->inc) ? 0 : BIT(31);
+	loc[1] |= (cfg->wrap_size & WRAP_MAX_SIZE) << 16;
+	loc[1] |= ((SIZE_DWORD(cfg->data_size)) & MAX_DWORDS_SZ);
+	cfg->dma_buf->next_op_allowed = REG_WRITE_OP;
+	cfg->dma_buf->index += ops_mem_size[cfg->ops];
+
+	return write_multi_reg(cfg);
+}
+
+static int write_single_reg(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 *loc = NULL;
+
+	loc =  (u32 *)((u8 *)cfg->dma_buf->vaddr +
+			cfg->dma_buf->index);
+	loc[0] = SINGLE_REG_WRITE_OPCODE;
+	loc[0] |= (cfg->blk_offset & MAX_RELATIVE_OFF);
+	loc[1] = *cfg->data;
+	cfg->dma_buf->index += ops_mem_size[cfg->ops];
+	cfg->dma_buf->ops_completed |= REG_WRITE_OP;
+	cfg->dma_buf->next_op_allowed = REG_WRITE_OP;
+
+	return 0;
+}
+
+static int write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 *loc = NULL;
+
+	loc =  (u32 *)((u8 *)cfg->dma_buf->vaddr +
+			cfg->dma_buf->index);
+	loc[0] = REG_DMA_DECODE_SEL;
+	get_decode_sel(cfg->blk, &loc[1]);
+	cfg->dma_buf->index += sizeof(u32) * 2;
+	cfg->dma_buf->ops_completed |= DECODE_SEL_OP;
+	cfg->dma_buf->next_op_allowed = REG_WRITE_OP;
+
+	return 0;
+}
+
+static int validate_write_multi_lut_reg(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	int rc;
+
+	rc = validate_write_reg(cfg);
+	if (rc)
+		return rc;
+
+	if (cfg->wrap_size < WRAP_MIN_SIZE || cfg->wrap_size > WRAP_MAX_SIZE) {
+		DRM_ERROR("invalid wrap sz %d min %d max %zd\n",
+			cfg->wrap_size, WRAP_MIN_SIZE, WRAP_MAX_SIZE);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int validate_write_reg(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 remain_len, write_len;
+
+	remain_len = BUFFER_SPACE_LEFT(cfg);
+	write_len = ops_mem_size[cfg->ops] + cfg->data_size;
+	if (remain_len < write_len) {
+		DRM_ERROR("buffer is full sz %d needs %d bytes\n",
+				remain_len, write_len);
+		return -EINVAL;
+	}
+
+	if (!cfg->data) {
+		DRM_ERROR("invalid data %pK size %d exp sz %d\n", cfg->data,
+			cfg->data_size, write_len);
+		return -EINVAL;
+	}
+	if ((SIZE_DWORD(cfg->data_size)) > MAX_DWORDS_SZ ||
+	    NOT_WORD_ALIGNED(cfg->data_size)) {
+		DRM_ERROR("Invalid data size %d max %zd align %x\n",
+			cfg->data_size, MAX_DWORDS_SZ,
+			NOT_WORD_ALIGNED(cfg->data_size));
+		return -EINVAL;
+	}
+
+	if (cfg->blk_offset > MAX_RELATIVE_OFF ||
+			NOT_WORD_ALIGNED(cfg->blk_offset)) {
+		DRM_ERROR("invalid offset %d max %zd align %x\n",
+				cfg->blk_offset, MAX_RELATIVE_OFF,
+				NOT_WORD_ALIGNED(cfg->blk_offset));
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int validate_write_decode_sel(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	u32 remain_len;
+
+	remain_len = BUFFER_SPACE_LEFT(cfg);
+	if (remain_len < ops_mem_size[HW_BLK_SELECT]) {
+		DRM_ERROR("buffer is full needs %d bytes\n",
+				ops_mem_size[HW_BLK_SELECT]);
+		return -EINVAL;
+	}
+
+	if (!cfg->blk) {
+		DRM_ERROR("blk set as 0\n");
+		return -EINVAL;
+	}
+	/* DSPP and VIG can't be combined */
+	if ((cfg->blk & GRP_VIG_HW_BLK_SELECT) &&
+		(cfg->blk & GRP_DSPP_HW_BLK_SELECT)) {
+		DRM_ERROR("invalid blk combination %x\n",
+			    cfg->blk);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int validate_dma_cfg(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	int rc = 0;
+	bool supported;
+
+	if (!cfg || cfg->ops >= REG_DMA_SETUP_OPS_MAX || !cfg->dma_buf) {
+		DRM_ERROR("invalid param cfg %pK ops %d dma_buf %pK\n",
+			cfg, ((cfg) ? cfg->ops : REG_DMA_SETUP_OPS_MAX),
+			((cfg) ? cfg->dma_buf : NULL));
+		return -EINVAL;
+	}
+
+	rc = check_support_v1(cfg->feature, cfg->blk, &supported);
+	if (rc || !supported) {
+		DRM_ERROR("check support failed rc %d supported %d\n",
+				rc, supported);
+		rc = -EINVAL;
+		return rc;
+	}
+
+	if (cfg->dma_buf->index >= cfg->dma_buf->buffer_size ||
+	    NOT_WORD_ALIGNED(cfg->dma_buf->index)) {
+		DRM_ERROR("Buf Overflow index %d max size %d align %x\n",
+			cfg->dma_buf->index, cfg->dma_buf->buffer_size,
+			NOT_WORD_ALIGNED(cfg->dma_buf->index));
+		return -EINVAL;
+	}
+
+	if (cfg->dma_buf->iova & GUARD_BYTES || !cfg->dma_buf->vaddr) {
+		DRM_ERROR("iova not aligned to %zx iova %x kva %pK",
+				ADDR_ALIGN, cfg->dma_buf->iova,
+				cfg->dma_buf->vaddr);
+		return -EINVAL;
+	}
+	if (!IS_OP_ALLOWED(cfg->ops, cfg->dma_buf->next_op_allowed)) {
+		DRM_ERROR("invalid op %x allowed %x\n", cfg->ops,
+				cfg->dma_buf->next_op_allowed);
+		return -EINVAL;
+	}
+
+	if (!validate_dma_op_params[cfg->ops] ||
+	    !write_dma_op_params[cfg->ops]) {
+		DRM_ERROR("invalid op %d validate %pK write %pK\n", cfg->ops,
+			validate_dma_op_params[cfg->ops],
+			write_dma_op_params[cfg->ops]);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+static int validate_kick_off_v1(struct sde_reg_dma_kickoff_cfg *cfg)
+{
+
+	if (!cfg || !cfg->ctl || !cfg->dma_buf) {
+		DRM_ERROR("invalid cfg %pK ctl %pK dma_buf %pK\n",
+			cfg, ((!cfg) ? NULL : cfg->ctl),
+			((!cfg) ? NULL : cfg->dma_buf));
+		return -EINVAL;
+	}
+
+	if (cfg->ctl->idx < CTL_0 && cfg->ctl->idx >= CTL_MAX) {
+		DRM_ERROR("invalid ctl idx %d\n", cfg->ctl->idx);
+		return -EINVAL;
+	}
+
+	if (cfg->op >= REG_DMA_OP_MAX) {
+		DRM_ERROR("invalid op %d\n", cfg->op);
+		return -EINVAL;
+	}
+
+	if ((cfg->op == REG_DMA_WRITE) &&
+	     (!(cfg->dma_buf->ops_completed & DECODE_SEL_OP) ||
+	     !(cfg->dma_buf->ops_completed & REG_WRITE_OP))) {
+		DRM_ERROR("incomplete write ops %x\n",
+				cfg->dma_buf->ops_completed);
+		return -EINVAL;
+	}
+
+	if (cfg->op == REG_DMA_READ && cfg->block_select >= DSPP_HIST_MAX) {
+		DRM_ERROR("invalid block for read %d\n", cfg->block_select);
+		return -EINVAL;
+	}
+
+	/* Only immediate triggers are supported now hence hardcode */
+	cfg->trigger_mode = (cfg->op == REG_DMA_READ) ? (READ_TRIGGER) :
+				(WRITE_TRIGGER);
+
+	if (cfg->dma_buf->iova & GUARD_BYTES) {
+		DRM_ERROR("Address is not aligned to %zx iova %x", ADDR_ALIGN,
+				cfg->dma_buf->iova);
+		return -EINVAL;
+	}
+
+	if (cfg->queue_select >= DMA_CTL_QUEUE_MAX) {
+		DRM_ERROR("invalid queue selected %d\n", cfg->queue_select);
+		return -EINVAL;
+	}
+
+	if (SIZE_DWORD(cfg->dma_buf->index) > MAX_DWORDS_SZ ||
+			!cfg->dma_buf->index) {
+		DRM_ERROR("invalid dword size %zd max %zd\n",
+			SIZE_DWORD(cfg->dma_buf->index), MAX_DWORDS_SZ);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int write_kick_off_v1(struct sde_reg_dma_kickoff_cfg *cfg)
+{
+	u32 cmd1;
+	struct sde_hw_blk_reg_map hw;
+
+	cmd1 = (cfg->op == REG_DMA_READ) ?
+		(dspp_read_sel[cfg->block_select] << 30) : 0;
+	cmd1 |= (cfg->last_command) ? BIT(24) : 0;
+	cmd1 |= (cfg->op == REG_DMA_READ) ? (2 << 22) : 0;
+	cmd1 |= (cfg->op == REG_DMA_WRITE) ? (BIT(22)) : 0;
+	cmd1 |= (SIZE_DWORD(cfg->dma_buf->index) & MAX_DWORDS_SZ);
+
+	SET_UP_REG_DMA_REG(hw, reg_dma);
+	SDE_REG_WRITE(&hw, REG_DMA_OP_MODE_OFF, BIT(0));
+	SDE_REG_WRITE(&hw, reg_dma_ctl_queue_off[cfg->ctl->idx],
+			cfg->dma_buf->iova);
+	SDE_REG_WRITE(&hw, reg_dma_ctl_queue_off[cfg->ctl->idx] + 0x4,
+			cmd1);
+	SDE_REG_WRITE(&cfg->ctl->hw, REG_DMA_CTL_TRIGGER_OFF,
+			queue_sel[cfg->queue_select]);
+
+	return 0;
+}
+
+int init_v1(struct sde_hw_reg_dma *cfg)
+{
+	int i = 0;
+
+	if (!cfg)
+		return -EINVAL;
+
+	reg_dma = cfg;
+	reg_dma->ops.check_support = check_support_v1;
+	reg_dma->ops.setup_payload = setup_payload_v1;
+	reg_dma->ops.kick_off = kick_off_v1;
+	reg_dma->ops.reset = reset_v1;
+	reg_dma->ops.alloc_reg_dma_buf = alloc_reg_dma_buf_v1;
+	reg_dma->ops.dealloc_reg_dma = dealloc_reg_dma_v1;
+	reg_dma->ops.reset_reg_dma_buf = reset_reg_dma_buffer_v1;
+
+	reg_dma_ctl_queue_off[CTL_0] = REG_DMA_CTL0_QUEUE_0_CMD0_OFF;
+	for (i = CTL_1; i < ARRAY_SIZE(reg_dma_ctl_queue_off); i++)
+		reg_dma_ctl_queue_off[i] = reg_dma_ctl_queue_off[i - 1] +
+			(sizeof(u32) * 4);
+
+	return 0;
+}
+
+static int check_support_v1(enum sde_reg_dma_features feature,
+		     enum sde_reg_dma_blk blk,
+		     bool *is_supported)
+{
+	int ret = 0;
+
+	if (!is_supported)
+		return -EINVAL;
+
+	if (feature >= REG_DMA_FEATURES_MAX || blk >= MDSS) {
+		*is_supported = false;
+		return ret;
+	}
+
+	*is_supported = (blk & v1_supported[feature]) ? true : false;
+	return ret;
+}
+
+static int setup_payload_v1(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	int rc = 0;
+
+	rc = validate_dma_cfg(cfg);
+
+	if (!rc)
+		rc = validate_dma_op_params[cfg->ops](cfg);
+
+	if (!rc)
+		rc = write_dma_op_params[cfg->ops](cfg);
+
+	return rc;
+}
+
+
+static int kick_off_v1(struct sde_reg_dma_kickoff_cfg *cfg)
+{
+	int rc = 0;
+
+	rc = validate_kick_off_v1(cfg);
+	if (rc)
+		return rc;
+
+	rc = write_kick_off_v1(cfg);
+	return rc;
+}
+
+int reset_v1(struct sde_hw_ctl *ctl)
+{
+	struct sde_hw_blk_reg_map hw;
+	u32 index, val;
+
+	if (!ctl || ctl->idx > CTL_MAX) {
+		DRM_ERROR("invalid ctl %pK ctl idx %d\n",
+			ctl, ((ctl) ? ctl->idx : 0));
+		return -EINVAL;
+	}
+
+	index = ctl->idx - CTL_0;
+	SET_UP_REG_DMA_REG(hw, reg_dma);
+	SDE_REG_WRITE(&hw, REG_DMA_OP_MODE_OFF, BIT(0));
+	SDE_REG_WRITE(&hw, (REG_DMA_CTL0_RESET_OFF + index * sizeof(u32)),
+			BIT(0));
+
+	index = 0;
+	do {
+		udelay(1000);
+		index++;
+		val = SDE_REG_READ(&hw,
+			(REG_DMA_CTL0_RESET_OFF + index * sizeof(u32)));
+	} while (index < 2 && val);
+
+	return 0;
+}
+
+static struct sde_reg_dma_buffer *alloc_reg_dma_buf_v1(u32 size)
+{
+	struct sde_reg_dma_buffer *dma_buf = NULL;
+	u32 iova_aligned, offset;
+	u32 rsize = size + GUARD_BYTES;
+	int rc = 0;
+
+	if (!size || SIZE_DWORD(size) > MAX_DWORDS_SZ) {
+		DRM_ERROR("invalid buffer size %d\n", size);
+		return ERR_PTR(-EINVAL);
+	}
+
+	dma_buf = kzalloc(sizeof(*dma_buf), GFP_KERNEL);
+	if (!dma_buf)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_lock(&reg_dma->drm_dev->struct_mutex);
+	dma_buf->buf = msm_gem_new(reg_dma->drm_dev,
+				    rsize, MSM_BO_UNCACHED);
+	mutex_unlock(&reg_dma->drm_dev->struct_mutex);
+	if (IS_ERR_OR_NULL(dma_buf->buf)) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	rc = msm_gem_get_iova(dma_buf->buf, 0, &dma_buf->iova);
+	if (rc) {
+		DRM_ERROR("failed to get the iova rc %d\n", rc);
+		goto free_gem;
+	}
+
+	dma_buf->vaddr = msm_gem_get_vaddr(dma_buf->buf);
+	if (IS_ERR_OR_NULL(dma_buf->vaddr)) {
+		DRM_ERROR("failed to get va rc %d\n", rc);
+		rc = -EINVAL;
+		goto put_iova;
+	}
+
+	dma_buf->buffer_size = size;
+	iova_aligned = (dma_buf->iova + GUARD_BYTES) & ALIGNED_OFFSET;
+	offset = iova_aligned - dma_buf->iova;
+	dma_buf->iova = dma_buf->iova + offset;
+	dma_buf->vaddr = (void *)(((u8 *)dma_buf->vaddr) + offset);
+	dma_buf->next_op_allowed = DECODE_SEL_OP;
+
+	return dma_buf;
+
+put_iova:
+	msm_gem_put_iova(dma_buf->buf, 0);
+free_gem:
+	msm_gem_free_object(dma_buf->buf);
+fail:
+	kfree(dma_buf);
+	return ERR_PTR(rc);
+}
+
+static int dealloc_reg_dma_v1(struct sde_reg_dma_buffer *dma_buf)
+{
+	if (!dma_buf) {
+		DRM_ERROR("invalid param reg_buf %pK\n", dma_buf);
+		return -EINVAL;
+	}
+
+	if (dma_buf->buf) {
+		msm_gem_put_iova(dma_buf->buf, 0);
+		mutex_lock(&reg_dma->drm_dev->struct_mutex);
+		msm_gem_free_object(dma_buf->buf);
+		mutex_unlock(&reg_dma->drm_dev->struct_mutex);
+	}
+
+	kfree(dma_buf);
+	return 0;
+}
+
+static int reset_reg_dma_buffer_v1(struct sde_reg_dma_buffer *lut_buf)
+{
+	if (!lut_buf)
+		return -EINVAL;
+
+	lut_buf->index = 0;
+	lut_buf->ops_completed = 0;
+	lut_buf->next_op_allowed = DECODE_SEL_OP;
+	return 0;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.h b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.h
new file mode 100644
index 0000000..8e37d38
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1.h
@@ -0,0 +1,23 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _SDE_HW_REG_DMA_V1_H
+#define _SDE_HW_REG_DMA_V1_H
+
+#include "sde_reg_dma.h"
+
+/**
+ * init_v1() - initialize the reg dma v1 driver by installing v1 ops
+ * @reg_dma - reg_dma hw info structure exposing capabilities.
+ */
+int init_v1(struct sde_hw_reg_dma *reg_dma);
+
+#endif /* _SDE_HW_REG_DMA_V1_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
new file mode 100644
index 0000000..3bc5a77
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.c
@@ -0,0 +1,553 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <drm/msm_drm_pp.h>
+#include "sde_reg_dma.h"
+#include "sde_hw_reg_dma_v1_color_proc.h"
+#include "sde_hw_color_proc_common_v4.h"
+
+/* Reserve space of 128 words for LUT dma payload set-up */
+#define REG_DMA_HEADERS_BUFFER_SZ (sizeof(u32) * 128)
+
+#define VLUT_MEM_SIZE ((128 * sizeof(u32)) + REG_DMA_HEADERS_BUFFER_SZ)
+#define VLUT_LEN (128 * sizeof(u32))
+#define PA_OP_MODE_OFF 0x800
+#define PA_LUTV_OPMODE_OFF 0x84c
+
+#define GAMUT_LUT_MEM_SIZE ((sizeof(struct drm_msm_3d_gamut)) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+#define GAMUT_SCALE_OFF_LEN (GAMUT_3D_SCALE_OFF_SZ * \
+		GAMUT_3D_SCALE_OFF_TBL_NUM * sizeof(u32))
+
+#define GC_LUT_MEM_SIZE ((sizeof(struct drm_msm_pgc_lut)) + \
+		REG_DMA_HEADERS_BUFFER_SZ)
+
+#define REG_MASK(n) ((BIT(n)) - 1)
+
+static struct sde_reg_dma_buffer *dspp_buf[REG_DMA_FEATURES_MAX][DSPP_MAX];
+
+static u32 feature_map[SDE_DSPP_MAX] = {
+	[SDE_DSPP_VLUT] = VLUT,
+	[SDE_DSPP_GAMUT] = GAMUT,
+	[SDE_DSPP_IGC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_PCC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_GC] = GC,
+	[SDE_DSPP_HSIC] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_MEMCOLOR] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_SIXZONE] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_DITHER] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_HIST] = REG_DMA_FEATURES_MAX,
+	[SDE_DSPP_AD] = REG_DMA_FEATURES_MAX,
+};
+
+static u32 feature_reg_dma_sz[SDE_DSPP_MAX] = {
+	[SDE_DSPP_VLUT] = VLUT_MEM_SIZE,
+	[SDE_DSPP_GAMUT] = GAMUT_LUT_MEM_SIZE,
+	[SDE_DSPP_GC] = GC_LUT_MEM_SIZE,
+};
+
+static u32 dspp_mapping[DSPP_MAX] = {
+	[DSPP_0] = DSPP0,
+	[DSPP_1] = DSPP1,
+	[DSPP_2] = DSPP2,
+	[DSPP_3] = DSPP3,
+};
+
+#define REG_DMA_INIT_OPS(cfg, block, reg_dma_feature, feature_dma_buf) \
+	do { \
+		memset(&cfg, 0, sizeof(cfg)); \
+		(cfg).blk = block; \
+		(cfg).feature = reg_dma_feature; \
+		(cfg).dma_buf = feature_dma_buf; \
+	} while (0)
+
+#define REG_DMA_SETUP_OPS(cfg, block_off, data_ptr, data_len, op, \
+		wrap_sz, wrap_inc) \
+	do { \
+		(cfg).ops = op; \
+		(cfg).blk_offset = block_off; \
+		(cfg).data_size = data_len; \
+		(cfg).data = data_ptr; \
+		(cfg).inc = wrap_inc; \
+		(cfg).wrap_size = wrap_sz; \
+	} while (0)
+
+#define REG_DMA_SETUP_KICKOFF(cfg, hw_ctl, feature_dma_buf, ops, ctl_q, \
+		mode) \
+	do { \
+		memset(&cfg, 0, sizeof(cfg)); \
+		(cfg).ctl = hw_ctl; \
+		(cfg).dma_buf = feature_dma_buf; \
+		(cfg).op = ops; \
+		(cfg).queue_select = ctl_q; \
+		(cfg).trigger_mode = mode; \
+	} while (0)
+
+static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 sz);
+static int reg_dma_dspp_check(struct sde_hw_dspp *ctx, void *cfg,
+		enum sde_reg_dma_features feature);
+static int reg_dma_blk_select(enum sde_reg_dma_features feature,
+		enum sde_reg_dma_blk blk, struct sde_reg_dma_buffer *dma_buf);
+static int reg_dma_write(enum sde_reg_dma_setup_ops ops, u32 off, u32 data_sz,
+			u32 *data, struct sde_reg_dma_buffer *dma_buf,
+			enum sde_reg_dma_features feature,
+			enum sde_reg_dma_blk blk);
+static int reg_dma_kick_off(enum sde_reg_dma_op op, enum sde_reg_dma_queue q,
+		enum sde_reg_dma_trigger_mode mode,
+		struct sde_reg_dma_buffer *dma_buf, struct sde_hw_ctl *ctl);
+
+static int reg_dma_buf_init(struct sde_reg_dma_buffer **buf, u32 size)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+
+	dma_ops = sde_reg_dma_get_ops();
+	if (IS_ERR_OR_NULL(dma_ops))
+		return -ENOTSUPP;
+
+	if (!buf) {
+		DRM_ERROR("invalid buf\n");
+		return -EINVAL;
+	}
+
+	/* buffer already initialized */
+	if (*buf)
+		return 0;
+
+	*buf = dma_ops->alloc_reg_dma_buf(size);
+	if (IS_ERR_OR_NULL(*buf))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int reg_dma_dspp_check(struct sde_hw_dspp *ctx, void *cfg,
+		enum sde_reg_dma_features feature)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+
+	if (!cfg || !ctx) {
+		DRM_ERROR("invalid cfg %pK ctx %pK\n", cfg, ctx);
+		return -EINVAL;
+	}
+
+	dma_ops = sde_reg_dma_get_ops();
+	if (IS_ERR_OR_NULL(dma_ops))
+		return -EINVAL;
+
+	if (!hw_cfg->ctl || ctx->idx >= DSPP_MAX ||
+		feature >= REG_DMA_FEATURES_MAX) {
+		DRM_ERROR("invalid ctl %pK dspp idx %d feature %d\n",
+			hw_cfg->ctl, ctx->idx, feature);
+		return -EINVAL;
+	}
+
+	if (!dspp_buf[feature][ctx->idx]) {
+		DRM_ERROR("invalid dma_buf\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int reg_dma_blk_select(enum sde_reg_dma_features feature,
+		enum sde_reg_dma_blk blk, struct sde_reg_dma_buffer *dma_buf)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	int rc = 0;
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dma_buf);
+	memset(&dma_write_cfg, 0, sizeof(dma_write_cfg));
+	dma_write_cfg.blk = blk;
+	dma_write_cfg.feature = feature;
+	dma_write_cfg.ops = HW_BLK_SELECT;
+	dma_write_cfg.dma_buf = dma_buf;
+
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc)
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+
+	return rc;
+}
+
+static int reg_dma_write(enum sde_reg_dma_setup_ops ops, u32 off, u32 data_sz,
+			u32 *data, struct sde_reg_dma_buffer *dma_buf,
+			enum sde_reg_dma_features feature,
+			enum sde_reg_dma_blk blk)
+{
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	int rc;
+
+	dma_ops = sde_reg_dma_get_ops();
+	memset(&dma_write_cfg, 0, sizeof(dma_write_cfg));
+
+	dma_write_cfg.ops = ops;
+	dma_write_cfg.blk_offset = off;
+	dma_write_cfg.data_size = data_sz;
+	dma_write_cfg.data = data;
+	dma_write_cfg.dma_buf = dma_buf;
+	dma_write_cfg.feature = feature;
+	dma_write_cfg.blk = blk;
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc)
+		DRM_ERROR("write single reg failed ret %d\n", rc);
+
+	return rc;
+}
+
+static int reg_dma_kick_off(enum sde_reg_dma_op op, enum sde_reg_dma_queue q,
+		enum sde_reg_dma_trigger_mode mode,
+		struct sde_reg_dma_buffer *dma_buf, struct sde_hw_ctl *ctl)
+{
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	int rc;
+
+	dma_ops = sde_reg_dma_get_ops();
+	memset(&kick_off, 0, sizeof(kick_off));
+	kick_off.ctl = ctl;
+	kick_off.dma_buf = dma_buf;
+	kick_off.op = op;
+	kick_off.queue_select = q;
+	kick_off.trigger_mode = mode;
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc)
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+
+	return rc;
+}
+
+int reg_dmav1_init_dspp_op_v4(int feature, enum sde_dspp idx)
+{
+	int rc = -ENOTSUPP;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	bool is_supported = false;
+
+	if (feature >= SDE_DSPP_MAX || idx >= DSPP_MAX) {
+		DRM_ERROR("invalid feature %x max %x dspp idx %x max %xd\n",
+			feature, SDE_DSPP_MAX, idx, DSPP_MAX);
+		return rc;
+	}
+
+	if (feature_map[feature] >= REG_DMA_FEATURES_MAX) {
+		DRM_ERROR("invalid feature map %d for feature %d\n",
+			feature_map[feature], feature);
+		return -ENOTSUPP;
+	}
+
+	dma_ops = sde_reg_dma_get_ops();
+	if (IS_ERR_OR_NULL(dma_ops))
+		return -ENOTSUPP;
+
+	rc = dma_ops->check_support(feature_map[feature], dspp_mapping[idx],
+			&is_supported);
+	if (!rc)
+		rc = (is_supported) ? 0 : -ENOTSUPP;
+
+	if (!rc)
+		rc = reg_dma_buf_init(&dspp_buf[feature_map[feature]][idx],
+				feature_reg_dma_sz[feature]);
+
+	return rc;
+}
+
+int reg_dmav1_init_sspp_op_v4(int feature, enum sde_sspp idx)
+{
+	return -ENOTSUPP;
+}
+
+void reg_dmav1_setup_dspp_vlutv18(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_pa_vlut *payload = NULL;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	u32 op_mode;
+	u32 *data = NULL;
+	int i, j, rc = 0;
+
+	rc = reg_dma_dspp_check(ctx, cfg, VLUT);
+	if (rc)
+		return;
+
+	op_mode = SDE_REG_READ(&ctx->hw, PA_OP_MODE_OFF);
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("Disable vlut feature\n");
+		SDE_REG_WRITE(&ctx->hw, PA_LUTV_OPMODE_OFF, 0);
+		if (op_mode & (~(BIT(20))))
+			op_mode = 0;
+		SDE_REG_WRITE(&ctx->hw, PA_OP_MODE_OFF, op_mode);
+		return;
+	}
+
+	rc = reg_dma_blk_select(VLUT, dspp_mapping[ctx->idx],
+			dspp_buf[VLUT][ctx->idx]);
+	if (rc) {
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+		return;
+	}
+
+	data = kzalloc(VLUT_LEN, GFP_KERNEL);
+	if (!data)
+		return;
+
+	payload = hw_cfg->payload;
+	DRM_DEBUG_DRIVER("Enable vlut feature flags %llx\n", payload->flags);
+	for (i = 0, j = 0; i < ARRAY_SIZE(payload->val); i += 2, j++)
+		data[j] = (payload->val[i] & REG_MASK(10)) |
+			((payload->val[i + 1] & REG_MASK(10)) << 16);
+
+	rc = reg_dma_write(REG_BLK_WRITE_SINGLE, ctx->cap->sblk->vlut.base,
+			VLUT_LEN, data,
+			dspp_buf[VLUT][ctx->idx], VLUT,
+			dspp_mapping[ctx->idx]);
+	if (rc) {
+		DRM_ERROR("write single reg failed ret %d\n", rc);
+		goto exit;
+	}
+
+	rc = reg_dma_kick_off(REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE,
+			dspp_buf[VLUT][ctx->idx], hw_cfg->ctl);
+	if (rc) {
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+		goto exit;
+	}
+	SDE_REG_WRITE(&ctx->hw, PA_LUTV_OPMODE_OFF, BIT(0));
+	SDE_REG_WRITE(&ctx->hw, PA_OP_MODE_OFF, op_mode | BIT(20));
+
+exit:
+	kfree(data);
+}
+
+static int sde_gamut_get_mode_info(struct drm_msm_3d_gamut *payload,
+		u32 *tbl_len, u32 *tbl_off, u32 *opcode, u32 *scale_off)
+{
+	int rc = 0;
+
+	if (payload->mode > GAMUT_3D_MODE_13) {
+		DRM_ERROR("invalid mode %d", payload->mode);
+		return -EINVAL;
+	}
+
+	switch (payload->mode) {
+	case GAMUT_3D_MODE_17:
+		*tbl_len = GAMUT_3D_MODE17_TBL_SZ * sizeof(u32) * 2;
+		*tbl_off = 0;
+		*scale_off = GAMUT_SCALEA_OFFSET_OFF;
+		*opcode = gamut_mode_17 << 2;
+		break;
+	case GAMUT_3D_MODE_5:
+		*tbl_len = GAMUT_3D_MODE5_TBL_SZ * sizeof(u32) * 2;
+		*tbl_off = 0;
+		*scale_off = GAMUT_SCALEB_OFFSET_OFF;
+		*opcode = gamut_mode_5 << 2;
+		*opcode |= GAMUT_MAP_EN;
+		break;
+	case GAMUT_3D_MODE_13:
+		*tbl_len = GAMUT_3D_MODE13_TBL_SZ * sizeof(u32) * 2;
+		*opcode = (*opcode & (BIT(4) - 1)) >> 2;
+		if (*opcode == gamut_mode_13a)
+			*opcode = gamut_mode_13b;
+		else
+			*opcode = gamut_mode_13a;
+		*tbl_off = (*opcode == gamut_mode_13a) ? 0 :
+			GAMUT_MODE_13B_OFF;
+		*scale_off = (*opcode == gamut_mode_13a) ?
+			GAMUT_SCALEA_OFFSET_OFF : GAMUT_SCALEB_OFFSET_OFF;
+		*opcode <<= 2;
+		*opcode |= GAMUT_MAP_EN;
+		break;
+	default:
+		rc = -EINVAL;
+		break;
+	}
+	if (payload->flags & GAMUT_3D_MAP_EN)
+		*opcode |= GAMUT_MAP_EN;
+	*opcode |= GAMUT_EN;
+
+	return rc;
+}
+
+void reg_dmav1_setup_dspp_3d_gamutv4(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_3d_gamut *payload;
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	u32 op_mode, reg, tbl_len, tbl_off, scale_off, i;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	int rc;
+
+	rc = reg_dma_dspp_check(ctx, cfg, GAMUT);
+	if (rc)
+		return;
+
+	op_mode = SDE_REG_READ(&ctx->hw, ctx->cap->sblk->gamut.base);
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable gamut feature\n");
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gamut.base, 0);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_3d_gamut)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+				hw_cfg->len, sizeof(struct drm_msm_3d_gamut));
+		return;
+	}
+	payload = hw_cfg->payload;
+	rc = sde_gamut_get_mode_info(payload, &tbl_len, &tbl_off, &op_mode,
+			&scale_off);
+	if (rc) {
+		DRM_ERROR("invalid mode info rc %d\n", rc);
+		return;
+	}
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dspp_buf[GAMUT][ctx->idx]);
+
+	REG_DMA_INIT_OPS(dma_write_cfg, dspp_mapping[ctx->idx], GAMUT,
+			dspp_buf[GAMUT][ctx->idx]);
+
+	REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+		return;
+	}
+	for (i = 0; i < GAMUT_3D_TBL_NUM; i++) {
+		reg = GAMUT_TABLE0_SEL << i;
+		reg |= ((tbl_off) & (BIT(11) - 1));
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->gamut.base + GAMUT_TABLE_SEL_OFF,
+			&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("write tbl sel reg failed ret %d\n", rc);
+			return;
+		}
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+		    ctx->cap->sblk->gamut.base + GAMUT_LOWER_COLOR_OFF,
+		    &payload->col[i][0].c0, tbl_len,
+		    REG_BLK_WRITE_MULTIPLE, 2, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("write color reg failed ret %d\n", rc);
+			return;
+		}
+	}
+
+	if (op_mode & GAMUT_MAP_EN) {
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->gamut.base + scale_off,
+			payload->scale_off[0], GAMUT_SCALE_OFF_LEN,
+			REG_BLK_WRITE_SINGLE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("write scale/off reg failed ret %d\n", rc);
+			return;
+		}
+	}
+
+	REG_DMA_SETUP_OPS(dma_write_cfg,
+		ctx->cap->sblk->gamut.base,
+		&op_mode, sizeof(op_mode), REG_SINGLE_WRITE, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("opmode write single reg failed ret %d\n", rc);
+		return;
+	}
+
+	REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, dspp_buf[GAMUT][ctx->idx],
+			REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc)
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+}
+
+void reg_dmav1_setup_dspp_gcv18(struct sde_hw_dspp *ctx, void *cfg)
+{
+	struct drm_msm_pgc_lut *lut_cfg;
+	struct sde_hw_reg_dma_ops *dma_ops;
+	struct sde_reg_dma_kickoff_cfg kick_off;
+	struct sde_hw_cp_cfg *hw_cfg = cfg;
+	struct sde_reg_dma_setup_ops_cfg dma_write_cfg;
+	int rc, i = 0;
+	u32 reg;
+
+	rc = reg_dma_dspp_check(ctx, cfg, GAMUT);
+	if (rc)
+		return;
+
+	if (!hw_cfg->payload) {
+		DRM_DEBUG_DRIVER("disable pgc feature\n");
+		SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, 0);
+		return;
+	}
+
+	if (hw_cfg->len != sizeof(struct drm_msm_pgc_lut)) {
+		DRM_ERROR("invalid size of payload len %d exp %zd\n",
+				hw_cfg->len, sizeof(struct drm_msm_pgc_lut));
+		return;
+	}
+	lut_cfg = hw_cfg->payload;
+
+	dma_ops = sde_reg_dma_get_ops();
+	dma_ops->reset_reg_dma_buf(dspp_buf[GC][ctx->idx]);
+
+	REG_DMA_INIT_OPS(dma_write_cfg, dspp_mapping[ctx->idx], GC,
+			dspp_buf[GC][ctx->idx]);
+
+	REG_DMA_SETUP_OPS(dma_write_cfg, 0, NULL, 0, HW_BLK_SELECT, 0, 0);
+	rc = dma_ops->setup_payload(&dma_write_cfg);
+	if (rc) {
+		DRM_ERROR("write decode select failed ret %d\n", rc);
+		return;
+	}
+
+	for (i = 0; i < GC_TBL_NUM; i++) {
+		reg = 0;
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->gc.base + GC_C0_INDEX_OFF +
+			(i * sizeof(u32) * 2),
+			&reg, sizeof(reg), REG_SINGLE_WRITE, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("index init failed ret %d\n", rc);
+			return;
+		}
+
+		REG_DMA_SETUP_OPS(dma_write_cfg,
+			ctx->cap->sblk->gc.base + GC_C0_OFF +
+			(i * sizeof(u32) * 2),
+			lut_cfg->c0 + ARRAY_SIZE(lut_cfg->c0),
+			PGC_TBL_LEN * sizeof(u32),
+			REG_BLK_WRITE_INC, 0, 0);
+		rc = dma_ops->setup_payload(&dma_write_cfg);
+		if (rc) {
+			DRM_ERROR("index init failed ret %d\n", rc);
+			return;
+		}
+	}
+
+	REG_DMA_SETUP_KICKOFF(kick_off, hw_cfg->ctl, dspp_buf[GC][ctx->idx],
+			REG_DMA_WRITE, DMA_CTL_QUEUE0, WRITE_IMMEDIATE);
+	rc = dma_ops->kick_off(&kick_off);
+	if (rc) {
+		DRM_ERROR("failed to kick off ret %d\n", rc);
+		return;
+	}
+
+	reg = GC_EN | ((lut_cfg->flags & PGC_8B_ROUND) ? GC_8B_ROUND_EN : 0);
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base + GC_LUT_SWAP_OFF,
+			BIT(0));
+	SDE_REG_WRITE(&ctx->hw, ctx->cap->sblk->gc.base, reg);
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
new file mode 100644
index 0000000..e3bf142
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_hw_reg_dma_v1_color_proc.h
@@ -0,0 +1,54 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _SDE_HW_REG_DMA_V1_COLOR_PROC_H
+#define _SDE_HW_REG_DMA_V1_COLOR_PROC_H
+
+#include "sde_hw_util.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_dspp.h"
+
+/**
+ * reg_dmav1_init_dspp_op_v4() - initialize the dspp feature op for sde v4
+ *                               using reg dma v1.
+ * @feature: dspp feature
+ * idx: dspp idx
+ */
+int reg_dmav1_init_dspp_op_v4(int feature, enum sde_dspp idx);
+
+/**
+ * reg_dma_init_sspp_op_v4() - initialize the sspp feature op for sde v4
+ * @feature: sspp feature
+ * @idx: sspp idx
+ */
+int reg_dmav1_init_sspp_op_v4(int feature, enum sde_sspp idx);
+
+/**
+ * reg_dmav1_setup_dspp_vlutv18() - vlut v18 implementation using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_vlutv18(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * reg_dmav1_setup_3d_gamutv4() - gamut v4 implementation using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_3d_gamutv4(struct sde_hw_dspp *ctx, void *cfg);
+
+/**
+ * reg_dmav1_setup_dspp_gc_v18() - gc v18 implementation using reg dma v1.
+ * @ctx: dspp ctx info
+ * @cfg: pointer to struct sde_hw_cp_cfg
+ */
+void reg_dmav1_setup_dspp_gcv18(struct sde_hw_dspp *ctx, void *cfg);
+#endif /* _SDE_HW_REG_DMA_V1_COLOR_PROC_H */
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
index 3ebe6cd..1b98683 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.c
@@ -32,8 +32,21 @@
 #define SSPP_SRC_FORMAT                    0x30
 #define SSPP_SRC_UNPACK_PATTERN            0x34
 #define SSPP_SRC_OP_MODE                   0x38
-#define MDSS_MDP_OP_DEINTERLACE            BIT(22)
 
+/* SSPP_MULTIRECT*/
+#define SSPP_SRC_SIZE_REC1                 0x16C
+#define SSPP_SRC_XY_REC1                   0x168
+#define SSPP_OUT_SIZE_REC1                 0x160
+#define SSPP_OUT_XY_REC1                   0x164
+#define SSPP_SRC_FORMAT_REC1               0x174
+#define SSPP_SRC_UNPACK_PATTERN_REC1       0x178
+#define SSPP_SRC_OP_MODE_REC1              0x17C
+#define SSPP_MULTIRECT_OPMODE              0x170
+#define SSPP_SRC_CONSTANT_COLOR_REC1       0x180
+#define SSPP_EXCL_REC_SIZE_REC1            0x184
+#define SSPP_EXCL_REC_XY_REC1              0x188
+
+#define MDSS_MDP_OP_DEINTERLACE            BIT(22)
 #define MDSS_MDP_OP_DEINTERLACE_ODD        BIT(23)
 #define MDSS_MDP_OP_IGC_ROM_1              BIT(18)
 #define MDSS_MDP_OP_IGC_ROM_0              BIT(17)
@@ -205,6 +218,32 @@ static inline int _sspp_subblk_offset(struct sde_hw_pipe *ctx,
 	return rc;
 }
 
+static void sde_hw_sspp_setup_multirect(struct sde_hw_pipe *ctx,
+		enum sde_sspp_multirect_index index,
+		enum sde_sspp_multirect_mode mode)
+{
+	u32 mode_mask;
+	u32 idx;
+
+	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
+		return;
+
+	if (index == SDE_SSPP_RECT_SOLO) {
+		/**
+		 * if rect index is RECT_SOLO, we cannot expect a
+		 * virtual plane sharing the same SSPP id. So we go
+		 * and disable multirect
+		 */
+		mode_mask = 0;
+	} else {
+		mode_mask = SDE_REG_READ(&ctx->hw, SSPP_MULTIRECT_OPMODE + idx);
+		mode_mask |= index;
+		mode_mask |= (mode == SDE_SSPP_MULTIRECT_TIME_MX) ? 0x4 : 0x0;
+	}
+
+	SDE_REG_WRITE(&ctx->hw, SSPP_MULTIRECT_OPMODE + idx, mode_mask);
+}
+
 static void _sspp_setup_opmode(struct sde_hw_pipe *ctx,
 		u32 mask, u8 en)
 {
@@ -248,24 +287,44 @@ static void _sspp_setup_csc10_opmode(struct sde_hw_pipe *ctx,
  * Setup source pixel format, flip,
  */
 static void sde_hw_sspp_setup_format(struct sde_hw_pipe *ctx,
-		const struct sde_format *fmt, u32 flags)
+		const struct sde_format *fmt, u32 flags,
+		enum sde_sspp_multirect_index rect_mode)
 {
 	struct sde_hw_blk_reg_map *c;
 	u32 chroma_samp, unpack, src_format;
 	u32 secure = 0;
 	u32 opmode = 0;
+	u32 op_mode_off, unpack_pat_off, format_off;
 	u32 idx;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx) || !fmt)
 		return;
 
+	if (rect_mode == SDE_SSPP_RECT_SOLO || rect_mode == SDE_SSPP_RECT_0) {
+		op_mode_off = SSPP_SRC_OP_MODE;
+		unpack_pat_off = SSPP_SRC_UNPACK_PATTERN;
+		format_off = SSPP_SRC_FORMAT;
+	} else {
+		op_mode_off = SSPP_SRC_OP_MODE_REC1;
+		unpack_pat_off = SSPP_SRC_UNPACK_PATTERN_REC1;
+		format_off = SSPP_SRC_FORMAT_REC1;
+	}
+
 	c = &ctx->hw;
-	opmode = SDE_REG_READ(c, SSPP_SRC_OP_MODE + idx);
+	opmode = SDE_REG_READ(c, op_mode_off + idx);
 	opmode &= ~(MDSS_MDP_OP_FLIP_LR | MDSS_MDP_OP_FLIP_UD |
 			MDSS_MDP_OP_BWC_EN | MDSS_MDP_OP_PE_OVERRIDE);
 
-	if (flags & SDE_SSPP_SECURE_OVERLAY_SESSION)
-		secure = 0xF;
+	if (flags & SDE_SSPP_SECURE_OVERLAY_SESSION) {
+		secure = SDE_REG_READ(c, SSPP_SRC_ADDR_SW_STATUS + idx);
+
+		if (rect_mode == SDE_SSPP_RECT_SOLO)
+			secure |= 0xF;
+		else if (rect_mode == SDE_SSPP_RECT_0)
+			secure |= 0x5;
+		else if (rect_mode == SDE_SSPP_RECT_1)
+			secure |= 0xA;
+	}
 
 	if (flags & SDE_SSPP_FLIP_LR)
 		opmode |= MDSS_MDP_OP_FLIP_LR;
@@ -327,9 +386,9 @@ static void sde_hw_sspp_setup_format(struct sde_hw_pipe *ctx,
 			VIG_CSC_10_EN | VIG_CSC_10_SRC_DATAFMT,
 			SDE_FORMAT_IS_YUV(fmt));
 
-	SDE_REG_WRITE(c, SSPP_SRC_FORMAT + idx, src_format);
-	SDE_REG_WRITE(c, SSPP_SRC_UNPACK_PATTERN + idx, unpack);
-	SDE_REG_WRITE(c, SSPP_SRC_OP_MODE + idx, opmode);
+	SDE_REG_WRITE(c, format_off + idx, src_format);
+	SDE_REG_WRITE(c, unpack_pat_off + idx, unpack);
+	SDE_REG_WRITE(c, op_mode_off + idx, opmode);
 	SDE_REG_WRITE(c, SSPP_SRC_ADDR_SW_STATUS + idx, secure);
 
 	/* clear previous UBWC error */
@@ -597,10 +656,8 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 		|| !scaler3_cfg || !ctx || !ctx->cap || !ctx->cap->sblk)
 		return;
 
-	if (!scaler3_cfg->enable) {
-		SDE_REG_WRITE(&ctx->hw, QSEED3_OP_MODE + idx, 0x0);
-		return;
-	}
+	if (!scaler3_cfg->enable)
+		goto end;
 
 	op_mode |= BIT(0);
 	op_mode |= (scaler3_cfg->y_rgb_filter_cfg & 0x3) << 16;
@@ -610,9 +667,6 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 		op_mode |= (scaler3_cfg->uv_filter_cfg & 0x3) << 24;
 	}
 
-	if (!SDE_FORMAT_IS_DX(sspp->layout.format))
-		op_mode |= BIT(14);
-
 	op_mode |= (scaler3_cfg->blend_cfg & 1) << 31;
 	op_mode |= (scaler3_cfg->dir_en) ? BIT(4) : 0;
 
@@ -640,10 +694,6 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 		_sde_hw_sspp_setup_scaler3_lut(ctx, scaler3_cfg);
 
 	if (ctx->cap->sblk->scaler_blk.version == 0x1002) {
-		if (sspp->layout.format->alpha_enable) {
-			op_mode |= BIT(10);
-			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x1) << 30;
-		}
 		phase_init =
 			((scaler3_cfg->init_phase_x[0] & 0x3F) << 0) |
 			((scaler3_cfg->init_phase_y[0] & 0x3F) << 8) |
@@ -651,10 +701,6 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 			((scaler3_cfg->init_phase_y[1] & 0x3F) << 24);
 		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT + idx, phase_init);
 	} else {
-		if (sspp->layout.format->alpha_enable) {
-			op_mode |= BIT(10);
-			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x3) << 29;
-		}
 		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_Y_H + idx,
 			scaler3_cfg->init_phase_x[0] & 0x1FFFFF);
 		SDE_REG_WRITE(&ctx->hw, QSEED3_PHASE_INIT_Y_V + idx,
@@ -685,6 +731,17 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 
 	SDE_REG_WRITE(&ctx->hw, QSEED3_DST_SIZE + idx, dst);
 
+end:
+	if (!SDE_FORMAT_IS_DX(sspp->layout.format))
+		op_mode |= BIT(14);
+
+	if (sspp->layout.format->alpha_enable) {
+		op_mode |= BIT(10);
+		if (ctx->cap->sblk->scaler_blk.version == 0x1002)
+			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x1) << 30;
+		else
+			op_mode |= (scaler3_cfg->alpha_filter_cfg & 0x3) << 29;
+	}
 	SDE_REG_WRITE(&ctx->hw, QSEED3_OP_MODE + idx, op_mode);
 }
 
@@ -694,10 +751,12 @@ static void _sde_hw_sspp_setup_scaler3(struct sde_hw_pipe *ctx,
 static void sde_hw_sspp_setup_rects(struct sde_hw_pipe *ctx,
 		struct sde_hw_pipe_cfg *cfg,
 		struct sde_hw_pixel_ext *pe_ext,
+		enum sde_sspp_multirect_index rect_index,
 		void *scale_cfg)
 {
 	struct sde_hw_blk_reg_map *c;
 	u32 src_size, src_xy, dst_size, dst_xy, ystride0, ystride1;
+	u32 src_size_off, src_xy_off, out_size_off, out_xy_off;
 	u32 decimation = 0;
 	u32 idx;
 
@@ -706,6 +765,18 @@ static void sde_hw_sspp_setup_rects(struct sde_hw_pipe *ctx,
 
 	c = &ctx->hw;
 
+	if (rect_index == SDE_SSPP_RECT_SOLO || rect_index == SDE_SSPP_RECT_0) {
+		src_size_off = SSPP_SRC_SIZE;
+		src_xy_off = SSPP_SRC_XY;
+		out_size_off = SSPP_OUT_SIZE;
+		out_xy_off = SSPP_OUT_XY;
+	} else {
+		src_size_off = SSPP_SRC_SIZE_REC1;
+		src_xy_off = SSPP_SRC_XY_REC1;
+		out_size_off = SSPP_OUT_SIZE_REC1;
+		out_xy_off = SSPP_OUT_XY_REC1;
+	}
+
 	/* program pixel extension override */
 	if (pe_ext)
 		sde_hw_sspp_setup_pe_config(ctx, pe_ext);
@@ -716,10 +787,23 @@ static void sde_hw_sspp_setup_rects(struct sde_hw_pipe *ctx,
 	dst_xy = (cfg->dst_rect.y << 16) | (cfg->dst_rect.x);
 	dst_size = (cfg->dst_rect.h << 16) | (cfg->dst_rect.w);
 
-	ystride0 = (cfg->layout.plane_pitch[0]) |
+	if (rect_index == SDE_SSPP_RECT_SOLO) {
+		ystride0 = (cfg->layout.plane_pitch[0]) |
 			(cfg->layout.plane_pitch[1] << 16);
-	ystride1 = (cfg->layout.plane_pitch[2]) |
+		ystride1 = (cfg->layout.plane_pitch[2]) |
 			(cfg->layout.plane_pitch[3] << 16);
+	} else {
+		ystride0 = SDE_REG_READ(c, SSPP_SRC_YSTRIDE0 + idx);
+		ystride1 = SDE_REG_READ(c, SSPP_SRC_YSTRIDE1 + idx);
+
+		if (rect_index == SDE_SSPP_RECT_0) {
+			ystride0 |= cfg->layout.plane_pitch[0];
+			ystride1 |= cfg->layout.plane_pitch[2];
+		}  else {
+			ystride0 |= cfg->layout.plane_pitch[0] << 16;
+			ystride1 |= cfg->layout.plane_pitch[2] << 16;
+		}
+	}
 
 	/* program scaler, phase registers, if pipes supporting scaling */
 	if (ctx->cap->features & SDE_SSPP_SCALER) {
@@ -730,10 +814,10 @@ static void sde_hw_sspp_setup_rects(struct sde_hw_pipe *ctx,
 	}
 
 	/* rectangle register programming */
-	SDE_REG_WRITE(c, SSPP_SRC_SIZE + idx, src_size);
-	SDE_REG_WRITE(c, SSPP_SRC_XY + idx, src_xy);
-	SDE_REG_WRITE(c, SSPP_OUT_SIZE + idx, dst_size);
-	SDE_REG_WRITE(c, SSPP_OUT_XY + idx, dst_xy);
+	SDE_REG_WRITE(c, src_size_off + idx, src_size);
+	SDE_REG_WRITE(c, src_xy_off + idx, src_xy);
+	SDE_REG_WRITE(c, out_size_off + idx, dst_size);
+	SDE_REG_WRITE(c, out_xy_off + idx, dst_xy);
 
 	SDE_REG_WRITE(c, SSPP_SRC_YSTRIDE0 + idx, ystride0);
 	SDE_REG_WRITE(c, SSPP_SRC_YSTRIDE1 + idx, ystride1);
@@ -746,31 +830,48 @@ static void sde_hw_sspp_setup_rects(struct sde_hw_pipe *ctx,
  * @excl_rect: Exclusion rect configs
  */
 static void _sde_hw_sspp_setup_excl_rect(struct sde_hw_pipe *ctx,
-		struct sde_rect *excl_rect)
+		struct sde_rect *excl_rect,
+		enum sde_sspp_multirect_index rect_index)
 {
 	struct sde_hw_blk_reg_map *c;
 	u32 size, xy;
 	u32 idx;
+	u32 reg_xy, reg_size;
+	u32 excl_ctrl, enable_bit;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx) || !excl_rect)
 		return;
 
+	if (rect_index == SDE_SSPP_RECT_0 || rect_index == SDE_SSPP_RECT_SOLO) {
+		reg_xy = SSPP_EXCL_REC_XY;
+		reg_size = SSPP_EXCL_REC_SIZE;
+		enable_bit = BIT(0);
+	} else {
+		reg_xy = SSPP_EXCL_REC_XY_REC1;
+		reg_size = SSPP_EXCL_REC_SIZE_REC1;
+		enable_bit = BIT(1);
+	}
+
 	c = &ctx->hw;
 
 	xy = (excl_rect->y << 16) | (excl_rect->x);
 	size = (excl_rect->h << 16) | (excl_rect->w);
 
+	excl_ctrl = SDE_REG_READ(c, SSPP_EXCL_REC_CTL + idx);
 	if (!size) {
-		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx, 0);
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx,
+				excl_ctrl & ~enable_bit);
 	} else {
-		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx, BIT(0));
-		SDE_REG_WRITE(c, SSPP_EXCL_REC_SIZE + idx, size);
-		SDE_REG_WRITE(c, SSPP_EXCL_REC_XY + idx, xy);
+		SDE_REG_WRITE(c, SSPP_EXCL_REC_CTL + idx,
+				excl_ctrl | enable_bit);
+		SDE_REG_WRITE(c, reg_size + idx, size);
+		SDE_REG_WRITE(c, reg_xy + idx, xy);
 	}
 }
 
 static void sde_hw_sspp_setup_sourceaddress(struct sde_hw_pipe *ctx,
-		struct sde_hw_pipe_cfg *cfg)
+		struct sde_hw_pipe_cfg *cfg,
+		enum sde_sspp_multirect_index rect_mode)
 {
 	int i;
 	u32 idx;
@@ -778,9 +879,21 @@ static void sde_hw_sspp_setup_sourceaddress(struct sde_hw_pipe *ctx,
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
-	for (i = 0; i < ARRAY_SIZE(cfg->layout.plane_addr); i++)
-		SDE_REG_WRITE(&ctx->hw, SSPP_SRC0_ADDR + idx + i * 0x4,
-			cfg->layout.plane_addr[i]);
+	if (rect_mode == SDE_SSPP_RECT_SOLO) {
+		for (i = 0; i < ARRAY_SIZE(cfg->layout.plane_addr); i++)
+			SDE_REG_WRITE(&ctx->hw, SSPP_SRC0_ADDR + idx + i * 0x4,
+					cfg->layout.plane_addr[i]);
+	} else if (rect_mode == SDE_SSPP_RECT_0) {
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC0_ADDR + idx,
+				cfg->layout.plane_addr[0]);
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC2_ADDR + idx,
+				cfg->layout.plane_addr[2]);
+	} else {
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC1_ADDR + idx,
+				cfg->layout.plane_addr[0]);
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC3_ADDR + idx,
+				cfg->layout.plane_addr[2]);
+	}
 }
 
 static void sde_hw_sspp_setup_csc(struct sde_hw_pipe *ctx,
@@ -815,14 +928,19 @@ static void sde_hw_sspp_setup_sharpening(struct sde_hw_pipe *ctx,
 	SDE_REG_WRITE(c, VIG_0_QSEED2_SHARP + idx + 0xC, cfg->noise_thr);
 }
 
-static void sde_hw_sspp_setup_solidfill(struct sde_hw_pipe *ctx, u32 color)
+static void sde_hw_sspp_setup_solidfill(struct sde_hw_pipe *ctx, u32 color, enum
+		sde_sspp_multirect_index rect_index)
 {
 	u32 idx;
 
 	if (_sspp_subblk_offset(ctx, SDE_SSPP_SRC, &idx))
 		return;
 
-	SDE_REG_WRITE(&ctx->hw, SSPP_SRC_CONSTANT_COLOR + idx, color);
+	if (rect_index == SDE_SSPP_RECT_SOLO || rect_index == SDE_SSPP_RECT_0)
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC_CONSTANT_COLOR + idx, color);
+	else
+		SDE_REG_WRITE(&ctx->hw, SSPP_SRC_CONSTANT_COLOR_REC1 + idx,
+				color);
 }
 
 static void sde_hw_sspp_setup_danger_safe_lut(struct sde_hw_pipe *ctx,
@@ -900,6 +1018,9 @@ static void _setup_layer_ops(struct sde_hw_pipe *c,
 	if (test_bit(SDE_SSPP_SCALER_QSEED2, &features))
 		c->ops.setup_sharpening = sde_hw_sspp_setup_sharpening;
 
+	if (sde_hw_sspp_multirect_enabled(c->cap))
+		c->ops.setup_multirect = sde_hw_sspp_setup_multirect;
+
 	if (test_bit(SDE_SSPP_SCALER_QSEED3, &features))
 		c->ops.setup_scaler = _sde_hw_sspp_setup_scaler3;
 	else
diff --git a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
index d7101fd..d81f673 100644
--- a/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
+++ b/drivers/gpu/drm/msm/sde/sde_hw_sspp.h
@@ -50,6 +50,28 @@ enum {
 	SDE_SSPP_COMP_MAX
 };
 
+/**
+ * SDE_SSPP_RECT_SOLO - multirect disabled
+ * SDE_SSPP_RECT_0 - rect0 of a multirect pipe
+ * SDE_SSPP_RECT_1 - rect1 of a multirect pipe
+ *
+ * Note: HW supports multirect with either RECT0 or
+ * RECT1. Considering no benefit of such configs over
+ * SOLO mode and to keep the plane management simple,
+ * we dont support single rect multirect configs.
+ */
+enum sde_sspp_multirect_index {
+	SDE_SSPP_RECT_SOLO = 0,
+	SDE_SSPP_RECT_0,
+	SDE_SSPP_RECT_1,
+};
+
+enum sde_sspp_multirect_mode {
+	SDE_SSPP_MULTIRECT_NONE = 0,
+	SDE_SSPP_MULTIRECT_PARALLEL,
+	SDE_SSPP_MULTIRECT_TIME_MX,
+};
+
 enum {
 	SDE_FRAME_LINEAR,
 	SDE_FRAME_TILE_A4X,
@@ -252,6 +274,8 @@ struct sde_hw_scaler3_cfg {
  *              4: Read 1 line/pixel drop 3  lines/pixels
  *              8: Read 1 line/pixel drop 7 lines/pixels
  *              16: Read 1 line/pixel drop 15 line/pixels
+ * @index:     index of the rectangle of SSPP
+ * @mode:      parallel or time multiplex multirect mode
  */
 struct sde_hw_pipe_cfg {
 	struct sde_hw_fmt_layout layout;
@@ -259,6 +283,8 @@ struct sde_hw_pipe_cfg {
 	struct sde_rect dst_rect;
 	u8 horz_decimation;
 	u8 vert_decimation;
+	enum sde_sspp_multirect_index index;
+	enum sde_sspp_multirect_mode mode;
 };
 
 /**
@@ -292,37 +318,45 @@ struct sde_hw_sspp_ops {
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
 	 * @flags: Extra flags for format config
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_format)(struct sde_hw_pipe *ctx,
-			const struct sde_format *fmt, u32 flags);
+			const struct sde_format *fmt, u32 flags,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_rects - setup pipe ROI rectangles
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
 	 * @pe_ext: Pointer to pixel ext settings
+	 * @index: rectangle index in multirect
 	 * @scale_cfg: Pointer to scaler settings
 	 */
 	void (*setup_rects)(struct sde_hw_pipe *ctx,
 			struct sde_hw_pipe_cfg *cfg,
 			struct sde_hw_pixel_ext *pe_ext,
+			enum sde_sspp_multirect_index index,
 			void *scale_cfg);
 
 	/**
 	 * setup_excl_rect - setup pipe exclusion rectangle
 	 * @ctx: Pointer to pipe context
 	 * @excl_rect: Pointer to exclclusion rect structure
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_excl_rect)(struct sde_hw_pipe *ctx,
-			struct sde_rect *excl_rect);
+			struct sde_rect *excl_rect,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_sourceaddress - setup pipe source addresses
 	 * @ctx: Pointer to pipe context
 	 * @cfg: Pointer to pipe config structure
+	 * @index: rectangle index in multirect
 	 */
 	void (*setup_sourceaddress)(struct sde_hw_pipe *ctx,
-			struct sde_hw_pipe_cfg *cfg);
+			struct sde_hw_pipe_cfg *cfg,
+			enum sde_sspp_multirect_index index);
 
 	/**
 	 * setup_csc - setup color space coversion
@@ -336,8 +370,21 @@ struct sde_hw_sspp_ops {
 	 * @ctx: Pointer to pipe context
 	 * @const_color: Fill color value
 	 * @flags: Pipe flags
+	 * @index: rectangle index in multirect
 	 */
-	void (*setup_solidfill)(struct sde_hw_pipe *ctx, u32 color);
+	void (*setup_solidfill)(struct sde_hw_pipe *ctx, u32 color,
+			enum sde_sspp_multirect_index index);
+
+	/**
+	 * setup_multirect - setup multirect configuration
+	 * @ctx: Pointer to pipe context
+	 * @index: rectangle index in multirect
+	 * @mode: parallel fetch / time multiplex multirect mode
+	 */
+
+	void (*setup_multirect)(struct sde_hw_pipe *ctx,
+			enum sde_sspp_multirect_index index,
+			enum sde_sspp_multirect_mode mode);
 
 	/**
 	 * setup_sharpening - setup sharpening
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c
index c4cc729..5aadae0 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.c
+++ b/drivers/gpu/drm/msm/sde/sde_kms.c
@@ -37,6 +37,7 @@
 #include "sde_encoder.h"
 #include "sde_plane.h"
 #include "sde_crtc.h"
+#include "sde_reg_dma.h"
 
 #define CREATE_TRACE_POINTS
 #include "sde_trace.h"
@@ -715,8 +716,12 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
 	struct msm_drm_private *priv;
 	struct sde_mdss_cfg *catalog;
 
-	int primary_planes_idx, i, ret;
-	int max_crtc_count, max_plane_count;
+	int primary_planes_idx = 0, i, ret;
+	int max_crtc_count;
+
+	u32 sspp_id[MAX_PLANES];
+	u32 master_plane_id[MAX_PLANES];
+	u32 num_virt_planes = 0;
 
 	if (!sde_kms || !sde_kms->dev || !sde_kms->dev->dev) {
 		SDE_ERROR("invalid sde_kms\n");
@@ -735,11 +740,9 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
 		(void)_sde_kms_setup_displays(dev, priv, sde_kms);
 
 	max_crtc_count = min(catalog->mixer_count, priv->num_encoders);
-	max_plane_count = min_t(u32, catalog->sspp_count, MAX_PLANES);
 
 	/* Create the planes */
-	primary_planes_idx = 0;
-	for (i = 0; i < max_plane_count; i++) {
+	for (i = 0; i < catalog->sspp_count; i++) {
 		bool primary = true;
 
 		if (catalog->sspp[i].features & BIT(SDE_SSPP_CURSOR)
@@ -747,7 +750,7 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
 			primary = false;
 
 		plane = sde_plane_init(dev, catalog->sspp[i].id, primary,
-				(1UL << max_crtc_count) - 1);
+				(1UL << max_crtc_count) - 1, 0);
 		if (IS_ERR(plane)) {
 			SDE_ERROR("sde_plane_init failed\n");
 			ret = PTR_ERR(plane);
@@ -757,6 +760,27 @@ static int _sde_kms_drm_obj_init(struct sde_kms *sde_kms)
 
 		if (primary)
 			primary_planes[primary_planes_idx++] = plane;
+
+		if (sde_hw_sspp_multirect_enabled(&catalog->sspp[i]) &&
+			sde_is_custom_client()) {
+			int priority =
+				catalog->sspp[i].sblk->smart_dma_priority;
+			sspp_id[priority - 1] = catalog->sspp[i].id;
+			master_plane_id[priority - 1] = plane->base.id;
+			num_virt_planes++;
+		}
+	}
+
+	/* Initialize smart DMA virtual planes */
+	for (i = 0; i < num_virt_planes; i++) {
+		plane = sde_plane_init(dev, sspp_id[i], false,
+			(1UL << max_crtc_count) - 1, master_plane_id[i]);
+		if (IS_ERR(plane)) {
+			SDE_ERROR("sde_plane for virtual SSPP init failed\n");
+			ret = PTR_ERR(plane);
+			goto fail;
+		}
+		priv->planes[priv->num_planes++] = plane;
 	}
 
 	max_crtc_count = min(max_crtc_count, primary_planes_idx);
@@ -837,11 +861,13 @@ static void _sde_kms_hw_destroy(struct sde_kms *sde_kms,
 	_sde_debugfs_destroy(sde_kms);
 	_sde_kms_mmu_destroy(sde_kms);
 
-	for (i = 0; i < sde_kms->catalog->vbif_count; i++) {
-		u32 vbif_idx = sde_kms->catalog->vbif[i].id;
+	if (sde_kms->catalog) {
+		for (i = 0; i < sde_kms->catalog->vbif_count; i++) {
+			u32 vbif_idx = sde_kms->catalog->vbif[i].id;
 
-		if ((vbif_idx < VBIF_MAX) && sde_kms->hw_vbif[vbif_idx])
-			sde_hw_vbif_destroy(sde_kms->hw_vbif[vbif_idx]);
+			if ((vbif_idx < VBIF_MAX) && sde_kms->hw_vbif[vbif_idx])
+				sde_hw_vbif_destroy(sde_kms->hw_vbif[vbif_idx]);
+		}
 	}
 
 	if (sde_kms->rm_init)
@@ -925,7 +951,7 @@ static const struct msm_kms_funcs kms_funcs = {
 /* the caller api needs to turn on clock before calling it */
 static inline void _sde_kms_core_hw_rev_init(struct sde_kms *sde_kms)
 {
-	sde_kms->core_rev = readl_relaxed(sde_kms->mmio + 0x0);
+	return;
 }
 
 static int _sde_kms_mmu_destroy(struct sde_kms *sde_kms)
@@ -1041,6 +1067,13 @@ static int sde_kms_hw_init(struct msm_kms *kms)
 		SDE_DEBUG("VBIF NRT is not defined");
 	}
 
+	sde_kms->reg_dma = msm_ioremap(dev->platformdev, "regdma_phys",
+			"REG_DMA");
+	if (IS_ERR(sde_kms->reg_dma)) {
+		sde_kms->reg_dma = NULL;
+		SDE_DEBUG("REG_DMA is not defined");
+	}
+
 	sde_kms->core_client = sde_power_client_create(&priv->phandle, "core");
 	if (IS_ERR_OR_NULL(sde_kms->core_client)) {
 		rc = PTR_ERR(sde_kms->core_client);
@@ -1068,6 +1101,14 @@ static int sde_kms_hw_init(struct msm_kms *kms)
 		goto power_error;
 	}
 
+	/* Initialize reg dma block which is a singleton */
+	rc = sde_reg_dma_init(sde_kms->reg_dma, sde_kms->catalog,
+			sde_kms->dev);
+	if (rc) {
+		SDE_ERROR("failed: reg dma init failed\n");
+		goto power_error;
+	}
+
 	rc = sde_rm_init(&sde_kms->rm, sde_kms->catalog, sde_kms->mmio,
 			sde_kms->dev);
 	if (rc) {
diff --git a/drivers/gpu/drm/msm/sde/sde_kms.h b/drivers/gpu/drm/msm/sde/sde_kms.h
index 558c47a..0550b19 100644
--- a/drivers/gpu/drm/msm/sde/sde_kms.h
+++ b/drivers/gpu/drm/msm/sde/sde_kms.h
@@ -131,7 +131,7 @@ struct sde_kms {
 	struct dentry *debugfs_vbif;
 
 	/* io/register spaces: */
-	void __iomem *mmio, *vbif[VBIF_MAX];
+	void __iomem *mmio, *vbif[VBIF_MAX], *reg_dma;
 
 	struct regulator *vdd;
 	struct regulator *mmagic;
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.c b/drivers/gpu/drm/msm/sde/sde_plane.c
index 746c19d..8ff08dc 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.c
+++ b/drivers/gpu/drm/msm/sde/sde_plane.c
@@ -23,11 +23,13 @@
 #include <uapi/drm/msm_drm_pp.h>
 
 #include "msm_prop.h"
+#include "msm_drv.h"
 
 #include "sde_kms.h"
 #include "sde_fence.h"
 #include "sde_formats.h"
 #include "sde_hw_sspp.h"
+#include "sde_hw_catalog_format.h"
 #include "sde_trace.h"
 #include "sde_crtc.h"
 #include "sde_vbif.h"
@@ -54,6 +56,15 @@
 
 #define SDE_PLANE_COLOR_FILL_FLAG	BIT(31)
 
+/* multirect rect index */
+enum {
+	R0,
+	R1,
+	R_MAX
+};
+
+#define TX_MODE_BUFFER_LINE_THRES 2
+
 /* dirty bits for update function */
 #define SDE_PLANE_DIRTY_RECTS	0x1
 #define SDE_PLANE_DIRTY_FORMAT	0x2
@@ -103,6 +114,7 @@ struct sde_plane {
 	uint32_t color_fill;
 	bool is_error;
 	bool is_rt_pipe;
+	bool is_virtual;
 
 	struct sde_hw_pixel_ext pixel_ext;
 	bool pixel_ext_usr;
@@ -586,7 +598,8 @@ static inline void _sde_plane_set_scanout(struct drm_plane *plane,
 	else if (ret)
 		SDE_ERROR_PLANE(psde, "failed to get format layout, %d\n", ret);
 	else if (psde->pipe_hw->ops.setup_sourceaddress)
-		psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg);
+		psde->pipe_hw->ops.setup_sourceaddress(psde->pipe_hw, pipe_cfg,
+						pstate->multirect_index);
 }
 
 static int _sde_plane_setup_scaler3_lut(struct sde_plane *psde,
@@ -1055,6 +1068,8 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
 		uint32_t color, uint32_t alpha)
 {
 	const struct sde_format *fmt;
+	const struct drm_plane *plane;
+	const struct sde_plane_state *pstate;
 
 	if (!psde) {
 		SDE_ERROR("invalid plane\n");
@@ -1066,6 +1081,9 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
 		return -EINVAL;
 	}
 
+	plane = &psde->base;
+	pstate = to_sde_plane_state(plane->state);
+
 	SDE_DEBUG_PLANE(psde, "\n");
 
 	/*
@@ -1077,7 +1095,8 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
 	/* update sspp */
 	if (fmt && psde->pipe_hw->ops.setup_solidfill) {
 		psde->pipe_hw->ops.setup_solidfill(psde->pipe_hw,
-				(color & 0xFFFFFF) | ((alpha & 0xFF) << 24));
+				(color & 0xFFFFFF) | ((alpha & 0xFF) << 24),
+				pstate->multirect_index);
 
 		/* override scaler/decimation if solid fill */
 		psde->pipe_cfg.src_rect.x = 0;
@@ -1089,11 +1108,13 @@ static int _sde_plane_color_fill(struct sde_plane *psde,
 
 		if (psde->pipe_hw->ops.setup_format)
 			psde->pipe_hw->ops.setup_format(psde->pipe_hw,
-					fmt, SDE_SSPP_SOLID_FILL);
+					fmt, SDE_SSPP_SOLID_FILL,
+					pstate->multirect_index);
 
 		if (psde->pipe_hw->ops.setup_rects)
 			psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
 					&psde->pipe_cfg, &psde->pixel_ext,
+					pstate->multirect_index,
 					psde->scaler3_cfg);
 	}
 
@@ -1221,13 +1242,21 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
 
 			psde->pipe_hw->ops.setup_rects(psde->pipe_hw,
 					&psde->pipe_cfg, &psde->pixel_ext,
+					pstate->multirect_index,
 					psde->scaler3_cfg);
 		}
 
 		/* update excl rect */
 		if (psde->pipe_hw->ops.setup_excl_rect)
 			psde->pipe_hw->ops.setup_excl_rect(psde->pipe_hw,
-					&pstate->excl_rect);
+					&pstate->excl_rect,
+					pstate->multirect_index);
+
+		if (psde->pipe_hw->ops.setup_multirect)
+			psde->pipe_hw->ops.setup_multirect(
+					psde->pipe_hw,
+					pstate->multirect_index,
+					pstate->multirect_mode);
 	}
 
 	if ((pstate->dirty & SDE_PLANE_DIRTY_FORMAT) &&
@@ -1243,7 +1272,8 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
 			src_flags |= SDE_SSPP_FLIP_UD;
 
 		/* update format */
-		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags);
+		psde->pipe_hw->ops.setup_format(psde->pipe_hw, fmt, src_flags,
+				pstate->multirect_index);
 
 		/* update csc */
 		if (SDE_FORMAT_IS_YUV(fmt))
@@ -1277,6 +1307,98 @@ static int _sde_plane_mode_set(struct drm_plane *plane,
 	/* clear dirty */
 	pstate->dirty = 0x0;
 
+	/* clear multirect mode*/
+	pstate->multirect_index = SDE_SSPP_RECT_SOLO;
+	pstate->multirect_mode = SDE_SSPP_MULTIRECT_NONE;
+	return 0;
+}
+
+int sde_plane_validate_multirect_v2(struct sde_multirect_plane_states *plane)
+{
+	struct sde_plane_state *pstate[R_MAX];
+	const struct drm_plane_state *drm_state[R_MAX];
+	struct sde_rect src[R_MAX], dst[R_MAX];
+	struct sde_plane *sde_plane[R_MAX];
+	const struct sde_format *fmt[R_MAX];
+	bool q16_data = true;
+	int i, max_sspp_linewidth;
+	int buffer_lines = TX_MODE_BUFFER_LINE_THRES;
+
+	for (i = 0; i < R_MAX; i++) {
+		const struct msm_format *msm_fmt;
+
+		drm_state[i] = i ? plane->r1 : plane->r0;
+		pstate[i] = to_sde_plane_state(drm_state[i]);
+		sde_plane[i] = to_sde_plane(drm_state[i]->plane);
+
+		if (pstate[i] == NULL) {
+			SDE_ERROR("SDE plane state of plane id %d is NULL\n",
+				drm_state[i]->plane->base.id);
+			return -EINVAL;
+		}
+
+		POPULATE_RECT(&src[i], drm_state[i]->src_x, drm_state[i]->src_y,
+			drm_state[i]->src_w, drm_state[i]->src_h, q16_data);
+		POPULATE_RECT(&dst[i], drm_state[i]->crtc_x,
+				drm_state[i]->crtc_y, drm_state[i]->crtc_w,
+				drm_state[i]->crtc_h, !q16_data);
+
+		if (src[i].w != dst[i].w || src[i].h != dst[i].h) {
+			SDE_ERROR_PLANE(sde_plane[i],
+				"scaling is not supported in multirect mode\n");
+			return -EINVAL;
+		}
+
+		msm_fmt = msm_framebuffer_format(drm_state[i]->fb);
+		fmt[i] = to_sde_format(msm_fmt);
+		if (SDE_FORMAT_IS_YUV(fmt[i])) {
+			SDE_ERROR_PLANE(sde_plane[i],
+				"Unsupported format for multirect mode\n");
+			return -EINVAL;
+		}
+	}
+
+	max_sspp_linewidth = sde_plane[R0]->pipe_sblk->maxlinewidth;
+
+	/* Validate RECT's and set the mode */
+
+	/* Prefer PARALLEL FETCH Mode over TIME_MX Mode */
+	if (src[R0].w <= max_sspp_linewidth/2 &&
+			src[R1].w <= max_sspp_linewidth/2) {
+		if (dst[R0].x <= dst[R1].x) {
+			pstate[R0]->multirect_index = SDE_SSPP_RECT_0;
+			pstate[R1]->multirect_index = SDE_SSPP_RECT_1;
+		} else {
+			pstate[R0]->multirect_index = SDE_SSPP_RECT_1;
+			pstate[R1]->multirect_index = SDE_SSPP_RECT_0;
+		}
+
+		pstate[R0]->multirect_mode = SDE_SSPP_MULTIRECT_PARALLEL;
+		pstate[R1]->multirect_mode = SDE_SSPP_MULTIRECT_PARALLEL;
+		goto done;
+	}
+
+	/* TIME_MX Mode */
+	if (SDE_FORMAT_IS_UBWC(fmt[R0]))
+		buffer_lines = 2 * fmt[R0]->tile_height;
+
+	if (dst[R1].y >= dst[R0].y + dst[R0].h + buffer_lines) {
+		pstate[R0]->multirect_index = SDE_SSPP_RECT_0;
+		pstate[R1]->multirect_index = SDE_SSPP_RECT_1;
+	} else if (dst[R0].y >= dst[R1].y + dst[R1].h + buffer_lines) {
+		pstate[R0]->multirect_index = SDE_SSPP_RECT_1;
+		pstate[R1]->multirect_index = SDE_SSPP_RECT_0;
+	} else {
+		SDE_ERROR(
+			"No multirect mode possible for the planes (%d - %d)\n",
+			drm_state[R0]->plane->base.id,
+			drm_state[R1]->plane->base.id);
+		return -EINVAL;
+	}
+
+	pstate[R0]->multirect_mode = SDE_SSPP_MULTIRECT_TIME_MX;
+	pstate[R1]->multirect_mode = SDE_SSPP_MULTIRECT_TIME_MX;
+done:
 	return 0;
 }
 
@@ -1343,6 +1465,10 @@ static void _sde_plane_atomic_check_mode_changed(struct sde_plane *psde,
 		   pstate->excl_rect.y != old_pstate->excl_rect.y) {
 		SDE_DEBUG_PLANE(psde, "excl rect updated\n");
 		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
+	} else if (pstate->multirect_index != old_pstate->multirect_index ||
+			pstate->multirect_mode != old_pstate->multirect_mode) {
+		SDE_DEBUG_PLANE(psde, "multirect config updated\n");
+		pstate->dirty |= SDE_PLANE_DIRTY_RECTS;
 	}
 
 	if (!state->fb || !old_state->fb) {
@@ -1614,7 +1740,7 @@ static void sde_plane_atomic_update(struct drm_plane *plane,
 
 /* helper to install properties which are common to planes and crtcs */
 static void _sde_plane_install_properties(struct drm_plane *plane,
-	struct sde_mdss_cfg *catalog)
+	struct sde_mdss_cfg *catalog, u32 master_plane_id)
 {
 	static const struct drm_prop_enum_list e_blend_op[] = {
 		{SDE_DRM_BLEND_OP_NOT_DEFINED,    "not_defined"},
@@ -1666,62 +1792,71 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
 	msm_property_install_range(&psde->property_info, "input_fence",
 		0x0, 0, INR_OPEN_MAX, 0, PLANE_PROP_INPUT_FENCE);
 
-	if (psde->pipe_sblk->maxhdeciexp) {
-		msm_property_install_range(&psde->property_info, "h_decimate",
-			0x0, 0, psde->pipe_sblk->maxhdeciexp, 0,
-			PLANE_PROP_H_DECIMATE);
-	}
+	if (!master_plane_id) {
+		if (psde->pipe_sblk->maxhdeciexp) {
+			msm_property_install_range(&psde->property_info,
+					"h_decimate", 0x0, 0,
+					psde->pipe_sblk->maxhdeciexp, 0,
+					PLANE_PROP_H_DECIMATE);
+		}
 
-	if (psde->pipe_sblk->maxvdeciexp) {
-		msm_property_install_range(&psde->property_info, "v_decimate",
-				0x0, 0, psde->pipe_sblk->maxvdeciexp, 0,
-				PLANE_PROP_V_DECIMATE);
-	}
+		if (psde->pipe_sblk->maxvdeciexp) {
+			msm_property_install_range(&psde->property_info,
+					"v_decimate", 0x0, 0,
+					psde->pipe_sblk->maxvdeciexp, 0,
+					PLANE_PROP_V_DECIMATE);
+		}
 
-	if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
-		msm_property_install_volatile_range(&psde->property_info,
-			"scaler_v2", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2);
-		msm_property_install_blob(&psde->property_info, "lut_ed", 0,
-			PLANE_PROP_SCALER_LUT_ED);
-		msm_property_install_blob(&psde->property_info, "lut_cir", 0,
-			PLANE_PROP_SCALER_LUT_CIR);
-		msm_property_install_blob(&psde->property_info, "lut_sep", 0,
-			PLANE_PROP_SCALER_LUT_SEP);
-	} else if (psde->features & SDE_SSPP_SCALER) {
-		msm_property_install_volatile_range(&psde->property_info,
-			"scaler_v1", 0x0, 0, ~0, 0, PLANE_PROP_SCALER_V1);
-	}
+		if (psde->features & BIT(SDE_SSPP_SCALER_QSEED3)) {
+			msm_property_install_volatile_range(
+					&psde->property_info, "scaler_v2",
+					0x0, 0, ~0, 0, PLANE_PROP_SCALER_V2);
+			msm_property_install_blob(&psde->property_info,
+					"lut_ed", 0, PLANE_PROP_SCALER_LUT_ED);
+			msm_property_install_blob(&psde->property_info,
+					"lut_cir", 0,
+					PLANE_PROP_SCALER_LUT_CIR);
+			msm_property_install_blob(&psde->property_info,
+					"lut_sep", 0,
+					PLANE_PROP_SCALER_LUT_SEP);
+		} else if (psde->features & SDE_SSPP_SCALER) {
+			msm_property_install_volatile_range(
+					&psde->property_info, "scaler_v1", 0x0,
+					0, ~0, 0, PLANE_PROP_SCALER_V1);
+		}
 
-	if (psde->features & BIT(SDE_SSPP_CSC)) {
-		msm_property_install_volatile_range(&psde->property_info,
-			"csc_v1", 0x0, 0, ~0, 0, PLANE_PROP_CSC_V1);
-	}
+		if (psde->features & BIT(SDE_SSPP_CSC) ||
+		    psde->features & BIT(SDE_SSPP_CSC_10BIT))
+			msm_property_install_volatile_range(
+					&psde->property_info, "csc_v1", 0x0,
+					0, ~0, 0, PLANE_PROP_CSC_V1);
 
-	if (psde->features & BIT(SDE_SSPP_HSIC)) {
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_HUE_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_HUE_ADJUST);
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_SATURATION_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_SATURATION_ADJUST);
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_VALUE_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_VALUE_ADJUST);
-		snprintf(feature_name, sizeof(feature_name), "%s%d",
-			"SDE_SSPP_CONTRAST_V",
-			psde->pipe_sblk->hsic_blk.version >> 16);
-		msm_property_install_range(&psde->property_info,
-			feature_name, 0, 0, 0xFFFFFFFF, 0,
-			PLANE_PROP_CONTRAST_ADJUST);
+		if (psde->features & BIT(SDE_SSPP_HSIC)) {
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_HUE_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_HUE_ADJUST);
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_SATURATION_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_SATURATION_ADJUST);
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_VALUE_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_VALUE_ADJUST);
+			snprintf(feature_name, sizeof(feature_name), "%s%d",
+				"SDE_SSPP_CONTRAST_V",
+				psde->pipe_sblk->hsic_blk.version >> 16);
+			msm_property_install_range(&psde->property_info,
+				feature_name, 0, 0, 0xFFFFFFFF, 0,
+				PLANE_PROP_CONTRAST_ADJUST);
+		}
 	}
 
 	if (psde->features & BIT(SDE_SSPP_EXCL_RECT))
@@ -1754,6 +1889,13 @@ static void _sde_plane_install_properties(struct drm_plane *plane,
 	sde_kms_info_reset(info);
 
 	format_list = psde->pipe_sblk->format_list;
+
+	if (master_plane_id) {
+		sde_kms_info_add_keyint(info, "primary_smart_plane_id",
+				master_plane_id);
+		format_list = plane_formats;
+	}
+
 	if (format_list) {
 		sde_kms_info_start(info, "pixel_formats");
 		while (format_list->fourcc_format) {
@@ -2248,6 +2390,11 @@ enum sde_sspp sde_plane_pipe(struct drm_plane *plane)
 	return plane ? to_sde_plane(plane)->pipe : SSPP_NONE;
 }
 
+bool is_sde_plane_virtual(struct drm_plane *plane)
+{
+	return plane ? to_sde_plane(plane)->is_virtual : false;
+}
+
 static ssize_t _sde_plane_danger_read(struct file *file,
 			char __user *buff, size_t count, loff_t *ppos)
 {
@@ -2411,9 +2558,10 @@ static void _sde_plane_init_debugfs(struct sde_plane *psde, struct sde_kms *kms)
 /* initialize plane */
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool primary_plane,
-		unsigned long possible_crtcs)
+		unsigned long possible_crtcs, u32 master_plane_id)
 {
 	struct drm_plane *plane = NULL;
+	const struct sde_format_extended *format_list;
 	struct sde_plane *psde;
 	struct msm_drm_private *priv;
 	struct sde_kms *kms;
@@ -2454,6 +2602,7 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
 	plane = &psde->base;
 	psde->pipe = pipe;
 	psde->mmu_id = kms->mmu_id[MSM_SMMU_DOMAIN_UNSECURE];
+	psde->is_virtual = (master_plane_id != 0);
 
 	/* initialize underlying h/w driver */
 	psde->pipe_hw = sde_hw_sspp_init(pipe, kms->mmio, kms->catalog);
@@ -2485,11 +2634,15 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
 		}
 	}
 
-	/* add plane to DRM framework */
-	psde->nformats = sde_populate_formats(psde->pipe_sblk->format_list,
-			psde->formats,
-			0,
-			ARRAY_SIZE(psde->formats));
+	format_list = psde->pipe_sblk->format_list;
+
+	if (master_plane_id)
+		format_list = plane_formats;
+
+	psde->nformats = sde_populate_formats(plane_formats,
+				psde->formats,
+				0,
+				ARRAY_SIZE(psde->formats));
 
 	if (!psde->nformats) {
 		SDE_ERROR("[%u]no valid formats for plane\n", pipe);
@@ -2516,7 +2669,7 @@ struct drm_plane *sde_plane_init(struct drm_device *dev,
 			PLANE_PROP_COUNT, PLANE_PROP_BLOBCOUNT,
 			sizeof(struct sde_plane_state));
 
-	_sde_plane_install_properties(plane, kms->catalog);
+	_sde_plane_install_properties(plane, kms->catalog, master_plane_id);
 
 	/* save user friendly pipe name for later */
 	snprintf(psde->pipe_name, SDE_NAME_SIZE, "plane%u", plane->base.id);
diff --git a/drivers/gpu/drm/msm/sde/sde_plane.h b/drivers/gpu/drm/msm/sde/sde_plane.h
index bbf6af6..9d7056e 100644
--- a/drivers/gpu/drm/msm/sde/sde_plane.h
+++ b/drivers/gpu/drm/msm/sde/sde_plane.h
@@ -33,6 +33,8 @@
  * @stage:	assigned by crtc blender
  * @excl_rect:	exclusion rect values
  * @dirty:	bitmask for which pipe h/w config functions need to be updated
+ * @multirect_index: index of the rectangle of SSPP
+ * @multirect_mode: parallel or time multiplex multirect mode
  * @pending:	whether the current update is still pending
  */
 struct sde_plane_state {
@@ -43,9 +45,21 @@ struct sde_plane_state {
 	enum sde_stage stage;
 	struct sde_rect excl_rect;
 	uint32_t dirty;
+	uint32_t multirect_index;
+	uint32_t multirect_mode;
 	bool pending;
 };
 
+/**
+ * struct sde_multirect_plane_states: Defines multirect pair of drm plane states
+ * @r0: drm plane configured on rect 0
+ * @r1: drm plane configured on rect 1
+ */
+struct sde_multirect_plane_states {
+	const struct drm_plane_state *r0;
+	const struct drm_plane_state *r1;
+};
+
 #define to_sde_plane_state(x) \
 	container_of(x, struct sde_plane_state, base)
 
@@ -66,6 +80,14 @@ struct sde_plane_state {
 enum sde_sspp sde_plane_pipe(struct drm_plane *plane);
 
 /**
+ * is_sde_plane_virtual - check for virtual plane
+ * @plane: Pointer to DRM plane object
+ * returns: true - if the plane is virtual
+ *          false - if the plane is primary
+ */
+bool is_sde_plane_virtual(struct drm_plane *plane);
+
+/**
  * sde_plane_flush - final plane operations before commit flush
  * @plane: Pointer to drm plane structure
  */
@@ -77,10 +99,22 @@ void sde_plane_flush(struct drm_plane *plane);
  * @pipe:  sde hardware pipe identifier
  * @primary_plane: true if this pipe is primary plane for crtc
  * @possible_crtcs: bitmask of crtc that can be attached to the given pipe
+ * @master_plane_id: primary plane id of a multirect pipe. 0 value passed for
+ *                   a regular plane initialization. A non-zero primary plane
+ *                   id will be passed for a virtual pipe initialization.
+ *
  */
 struct drm_plane *sde_plane_init(struct drm_device *dev,
 		uint32_t pipe, bool primary_plane,
-		unsigned long possible_crtcs);
+		unsigned long possible_crtcs, u32 master_plane_id);
+
+/**
+ * sde_plane_validate_multirecti_v2 - validate the multirect planes
+ *				      against hw limitations
+ * @plane: drm plate states of the multirect pair
+ */
+
+int sde_plane_validate_multirect_v2(struct sde_multirect_plane_states *plane);
 
 /**
  * sde_plane_wait_input_fence - wait for input fence object
diff --git a/drivers/gpu/drm/msm/sde/sde_reg_dma.c b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
new file mode 100644
index 0000000..cc115c5
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_reg_dma.c
@@ -0,0 +1,105 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sde_reg_dma.h"
+#include "sde_hw_reg_dma_v1.h"
+
+static int default_check_support(enum sde_reg_dma_features feature,
+		     enum sde_reg_dma_blk blk,
+		     bool *is_supported)
+{
+
+	if (!is_supported)
+		return -EINVAL;
+
+	*is_supported = false;
+	return 0;
+}
+
+static int default_setup_payload(struct sde_reg_dma_setup_ops_cfg *cfg)
+{
+	DRM_ERROR("not implemented\n");
+	return -EINVAL;
+}
+
+static int default_kick_off(struct sde_reg_dma_kickoff_cfg *cfg)
+{
+	DRM_ERROR("not implemented\n");
+	return -EINVAL;
+
+}
+
+static int default_reset(struct sde_hw_ctl *ctl)
+{
+	DRM_ERROR("not implemented\n");
+	return -EINVAL;
+}
+
+struct sde_reg_dma_buffer *default_alloc_reg_dma_buf(u32 size)
+{
+	DRM_ERROR("not implemented\n");
+	return ERR_PTR(-EINVAL);
+}
+
+int default_dealloc_reg_dma(struct sde_reg_dma_buffer *lut_buf)
+{
+	DRM_ERROR("not implemented\n");
+	return -EINVAL;
+}
+
+static int default_buf_reset_reg_dma(struct sde_reg_dma_buffer *lut_buf)
+{
+	DRM_ERROR("not implemented\n");
+	return -EINVAL;
+}
+
+static struct sde_hw_reg_dma reg_dma = {
+	.ops = {default_check_support, default_setup_payload,
+		default_kick_off, default_reset, default_alloc_reg_dma_buf,
+		default_dealloc_reg_dma, default_buf_reset_reg_dma},
+};
+
+int sde_reg_dma_init(void __iomem *addr, struct sde_mdss_cfg *m,
+		struct drm_device *dev)
+{
+	int rc = 0;
+
+	if (!addr || !m || !dev) {
+		DRM_DEBUG("invalid addr %pK catalog %pK dev %pK\n", addr, m,
+				dev);
+		return 0;
+	}
+
+	reg_dma.drm_dev = dev;
+	reg_dma.caps = &m->dma_cfg;
+	reg_dma.addr = addr;
+
+	if (!m->reg_dma_count)
+		return 0;
+
+	switch (reg_dma.caps->version) {
+	case 1:
+		rc = init_v1(&reg_dma);
+		if (rc)
+			DRM_DEBUG("init v1 dma ops failed\n");
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+struct sde_hw_reg_dma_ops *sde_reg_dma_get_ops(void)
+{
+	return &reg_dma.ops;
+}
diff --git a/drivers/gpu/drm/msm/sde/sde_reg_dma.h b/drivers/gpu/drm/msm/sde/sde_reg_dma.h
new file mode 100644
index 0000000..c8e464d
--- /dev/null
+++ b/drivers/gpu/drm/msm/sde/sde_reg_dma.h
@@ -0,0 +1,301 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SDE_REG_DMA_H
+#define _SDE_REG_DMA_H
+
+#include "msm_drv.h"
+#include "sde_hw_catalog.h"
+#include "sde_hw_mdss.h"
+#include "sde_hw_top.h"
+#include "sde_hw_util.h"
+
+/**
+ * enum sde_reg_dma_op - defines operations supported by reg dma
+ * @REG_DMA_READ: Read the histogram into buffer provided
+ * @REG_DMA_WRITE: Write the reg dma configuration into MDP block
+ * @REG_DMA_OP_MAX: Max operation which indicates that op is invalid
+ */
+enum sde_reg_dma_op {
+	REG_DMA_READ,
+	REG_DMA_WRITE,
+	REG_DMA_OP_MAX
+};
+
+/**
+ * enum sde_reg_dma_read_sel - defines the blocks for histogram read
+ * @DSPP0_HIST: select dspp0
+ * @DSPP1_HIST: select dspp1
+ * @DSPP2_HIST: select dspp2
+ * @DSPP3_HIST: select dspp3
+ * @DSPP_HIST_MAX: invalid selection
+ */
+enum sde_reg_dma_read_sel {
+	DSPP0_HIST,
+	DSPP1_HIST,
+	DSPP2_HIST,
+	DSPP3_HIST,
+	DSPP_HIST_MAX,
+};
+
+/**
+ * enum sde_reg_dma_features - defines features supported by reg dma
+ * @QSEED: qseed feature
+ * @GAMUT: gamut feature
+ * @IGC: inverse gamma correction
+ * @PCC: polynomical color correction
+ * @VLUT: PA vlut
+ * @MEM_COLOR: memory color
+ * @SIX_ZONE: six zone
+ * @HSIC: Hue, saturation and contrast
+ * @GC: gamma correction
+ * @REG_DMA_FEATURES_MAX: invalid selection
+ */
+enum sde_reg_dma_features {
+	QSEED,
+	GAMUT,
+	IGC,
+	PCC,
+	VLUT,
+	MEM_COLOR,
+	SIX_ZONE,
+	HSIC,
+	GC,
+	REG_DMA_FEATURES_MAX,
+};
+
+/**
+ * enum sde_reg_dma_queue - defines reg dma write queue values
+ * @DMA_CTL_QUEUE0: select queue0
+ * @DMA_CTL_QUEUE1: select queue1
+ * @DMA_CTL_QUEUE_MAX: invalid selection
+ */
+enum sde_reg_dma_queue {
+	DMA_CTL_QUEUE0,
+	DMA_CTL_QUEUE1,
+	DMA_CTL_QUEUE_MAX,
+};
+
+/**
+ * enum sde_reg_dma_trigger_mode - defines reg dma ops trigger mode
+ * @WRITE_IMMEDIATE: trigger write op immediately
+ * @WRITE_TRIGGER: trigger write op when sw trigger is issued
+ * @READ_IMMEDIATE: trigger read op immediately
+ * @READ_TRIGGER: trigger read op when sw trigger is issued
+ * @TIGGER_MAX: invalid trigger selection
+ */
+enum sde_reg_dma_trigger_mode {
+	WRITE_IMMEDIATE,
+	WRITE_TRIGGER,
+	READ_IMMEDIATE,
+	READ_TRIGGER,
+	TIGGER_MAX,
+};
+
+/**
+ * enum sde_reg_dma_setup_ops - defines reg dma write configuration
+ * @HW_BLK_SELECT: op for selecting the hardware block
+ * @REG_SINGLE_WRITE: op for writing single register value
+ *                    at the address provided
+ * @REG_BLK_WRITE_SINGLE: op for writing multiple registers using hw index
+ *                        register
+ * @REG_BLK_WRITE_INC: op for writing multiple registers using auto address
+ *                     increment
+ * @REG_BLK_WRITE_MULTIPLE: op for writing hw index based registers at
+ *                         non-consecutive location
+ * @REG_DMA_SETUP_OPS_MAX: invalid operation
+ */
+enum sde_reg_dma_setup_ops {
+	HW_BLK_SELECT,
+	REG_SINGLE_WRITE,
+	REG_BLK_WRITE_SINGLE,
+	REG_BLK_WRITE_INC,
+	REG_BLK_WRITE_MULTIPLE,
+	REG_DMA_SETUP_OPS_MAX,
+};
+
+/**
+ * enum sde_reg_dma_blk - defines blocks for which reg dma op should be
+ *                        performed
+ * @VIG0: select vig0 block
+ * @VIG1: select vig1 block
+ * @VIG2: select vig2 block
+ * @VIG3: select vig3 block
+ * @LM0: select lm0 block
+ * @LM1: select lm1 block
+ * @LM2: select lm2 block
+ * @LM3: select lm3 block
+ * @DSPP0: select dspp0 block
+ * @DSPP1: select dspp1 block
+ * @DSPP2: select dspp2 block
+ * @DSPP3: select dspp3 block
+ * @DMA0: select dma0 block
+ * @DMA1: select dma1 block
+ * @DMA2: select dma2 block
+ * @DMA3: select dma3 block
+ * @SSPP_IGC: select sspp igc block
+ * @DSPP_IGC: select dspp igc block
+ * @MDSS: select mdss block
+ */
+enum sde_reg_dma_blk {
+	VIG0  = BIT(0),
+	VIG1  = BIT(1),
+	VIG2  = BIT(2),
+	VIG3  = BIT(3),
+	LM0   = BIT(4),
+	LM1   = BIT(5),
+	LM2   = BIT(6),
+	LM3   = BIT(7),
+	DSPP0 = BIT(8),
+	DSPP1 = BIT(9),
+	DSPP2 = BIT(10),
+	DSPP3 = BIT(11),
+	DMA0  = BIT(12),
+	DMA1  = BIT(13),
+	DMA2  = BIT(14),
+	DMA3  = BIT(15),
+	SSPP_IGC = BIT(16),
+	DSPP_IGC = BIT(17),
+	MDSS  = BIT(31)
+};
+
+/**
+ * struct sde_reg_dma_buffer - defines reg dma buffer structure.
+ * @drm_gem_object *buf: drm gem handle for the buffer
+ * @buffer_size: buffer size
+ * @index: write pointer index
+ * @iova: device address
+ * @vaddr: cpu address
+ * @next_op_allowed: operation allowed on the buffer
+ * @ops_completed: operations completed on buffer
+ */
+struct sde_reg_dma_buffer {
+	struct drm_gem_object *buf;
+	u32 buffer_size;
+	u32 index;
+	u32 iova;
+	void *vaddr;
+	u32 next_op_allowed;
+	u32 ops_completed;
+};
+
+/**
+ * struct sde_reg_dma_setup_ops_cfg - defines structure for reg dma ops on the
+ *                                    reg dma buffer.
+ * @sde_reg_dma_setup_ops ops: ops to be performed
+ * @sde_reg_dma_blk blk: block on which op needs to be performed
+ * @sde_reg_dma_features feature: feature on which op needs to be done
+ * @wrap_size: valid for REG_BLK_WRITE_MULTIPLE, indicates reg index location
+ *             size
+ * @inc: valid for REG_BLK_WRITE_MULTIPLE indicates whether reg index location
+ *       needs an increment or decrement.
+ *       0 - decrement
+ *       1 - increment
+ * @blk_offset: offset for blk, valid for HW_BLK_SELECT op only
+ * @sde_reg_dma_buffer *dma_buf: reg dma buffer on which op needs to be
+ *                                performed
+ * @data: pointer to payload which has to be written into reg dma buffer for
+ *        selected op.
+ * @data_size: size of payload in data
+ */
+struct sde_reg_dma_setup_ops_cfg {
+	enum sde_reg_dma_setup_ops ops;
+	enum sde_reg_dma_blk blk;
+	enum sde_reg_dma_features feature;
+	u32 wrap_size;
+	u32 inc;
+	u32 blk_offset;
+	struct sde_reg_dma_buffer *dma_buf;
+	u32 *data;
+	u32 data_size;
+};
+
+/**
+ * struct sde_reg_dma_kickoff_cfg - commit reg dma buffer to hw engine
+ * @ctl: ctl for which reg dma buffer needs to be committed.
+ * @dma_buf: reg dma buffer with iova address and size info
+ * @block_select: histogram read select
+ * @trigger_mode: reg dma ops trigger mode
+ * @queue_select: queue on which reg dma buffer will be submitted
+ * @last_command: last command for this vsync
+ */
+struct sde_reg_dma_kickoff_cfg {
+	struct sde_hw_ctl *ctl;
+	enum sde_reg_dma_op op;
+	struct sde_reg_dma_buffer *dma_buf;
+	enum sde_reg_dma_read_sel block_select;
+	enum sde_reg_dma_trigger_mode trigger_mode;
+	enum sde_reg_dma_queue queue_select;
+	u32 last_command;
+};
+
+/**
+ * struct sde_hw_reg_dma_ops - ops supported by reg dma frame work, based on
+ *                             version of reg dma appropriate ops will be
+ *                             installed during driver probe.
+ * @check_support: checks if reg dma is supported on this platform for a
+ *                 feature
+ * @setup_payload: setup reg dma buffer based on ops and payload provided by
+ *                 client
+ * @kick_off: submit the reg dma buffer to hw enginge
+ * @reset: reset the reg dma hw enginge for a ctl
+ * @alloc_reg_dma_buf: allocate reg dma buffer
+ * @dealloc_reg_dma: de-allocate reg dma buffer
+ * @reset_reg_dma_buf: reset the buffer to init state
+ */
+struct sde_hw_reg_dma_ops {
+	int (*check_support)(enum sde_reg_dma_features feature,
+			     enum sde_reg_dma_blk blk,
+			     bool *is_supported);
+	int (*setup_payload)(struct sde_reg_dma_setup_ops_cfg *cfg);
+	int (*kick_off)(struct sde_reg_dma_kickoff_cfg *cfg);
+	int (*reset)(struct sde_hw_ctl *ctl);
+	struct sde_reg_dma_buffer* (*alloc_reg_dma_buf)(u32 size);
+	int (*dealloc_reg_dma)(struct sde_reg_dma_buffer *lut_buf);
+	int (*reset_reg_dma_buf)(struct sde_reg_dma_buffer *buf);
+};
+
+/**
+ * struct sde_hw_reg_dma - structure to hold reg dma hw info
+ * @drm_dev: drm driver dev handle
+ * @caps: reg dma hw caps on the platform
+ * @ops: reg dma ops supported on the platform
+ * @addr: reg dma hw block base address
+ */
+struct sde_hw_reg_dma {
+	struct drm_device *drm_dev;
+	const struct sde_reg_dma_cfg *caps;
+	struct sde_hw_reg_dma_ops ops;
+	void __iomem *addr;
+};
+
+/**
+ * sde_reg_dma_init() - function called to initialize reg dma during sde
+ *                         drm driver probe. If reg dma is supported by sde
+ *                         ops for reg dma version will be installed.
+ *                         if reg dma is not supported by sde default ops will
+ *                         be installed. check_support of default ops will
+ *                         return false, hence the clients should fall back to
+ *                         AHB programming.
+ * @addr: reg dma block base address
+ * @m: catalog which contains sde hw capabilities and offsets
+ * @dev: drm driver device handle
+ */
+int sde_reg_dma_init(void __iomem *addr, struct sde_mdss_cfg *m,
+		struct drm_device *dev);
+
+/**
+ * sde_reg_dma_get_ops() - singleton module, ops is returned to the clients
+ *                            who call this api.
+ */
+struct sde_hw_reg_dma_ops *sde_reg_dma_get_ops(void);
+#endif /* _SDE_REG_DMA_H */
diff --git a/drivers/gpu/msm/adreno-gpulist.h b/drivers/gpu/msm/adreno-gpulist.h
index e616338..f143938 100644
--- a/drivers/gpu/msm/adreno-gpulist.h
+++ b/drivers/gpu/msm/adreno-gpulist.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2002,2007-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2002,2007-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -289,9 +289,11 @@ static const struct adreno_gpu_core adreno_gpulist[] = {
 		.major = 1,
 		.minor = 2,
 		.patchid = ANY_ID,
-		.features = ADRENO_64BIT,
+		.features = ADRENO_PREEMPTION | ADRENO_64BIT |
+			ADRENO_CONTENT_PROTECTION | ADRENO_CPZ_RETENTION,
 		.pm4fw_name = "a530_pm4.fw",
 		.pfpfw_name = "a530_pfp.fw",
+		.zap_name = "a512_zap",
 		.gpudev = &adreno_a5xx_gpudev,
 		.gmem_size = (SZ_256K + SZ_16K),
 		.num_protected_regs = 0x20,
diff --git a/drivers/gpu/msm/adreno_a5xx.c b/drivers/gpu/msm/adreno_a5xx.c
index 4ffce21..b02b9a5 100644
--- a/drivers/gpu/msm/adreno_a5xx.c
+++ b/drivers/gpu/msm/adreno_a5xx.c
@@ -194,7 +194,8 @@ static void a5xx_platform_setup(struct adreno_device *adreno_dev)
 		/* A510 has 3 XIN ports in VBIF */
 		gpudev->vbif_xin_halt_ctrl0_mask =
 				A510_VBIF_XIN_HALT_CTRL0_MASK;
-	} else if (adreno_is_a540(adreno_dev)) {
+	} else if (adreno_is_a540(adreno_dev) ||
+		adreno_is_a512(adreno_dev)) {
 		gpudev->snapshot_data->sect_sizes->cp_merciu = 1024;
 	}
 
@@ -483,7 +484,7 @@ static int a5xx_regulator_enable(struct adreno_device *adreno_dev)
 		kgsl_regwrite(device, A5XX_RBBM_CLOCK_CNTL, 0x00000055);
 		a5xx_hwcg_set(adreno_dev, true);
 		/* Turn on sp_input_clk at HM level */
-		kgsl_regrmw(device, A5XX_RBBM_CLOCK_CNTL, 3, 0);
+		kgsl_regrmw(device, A5XX_RBBM_CLOCK_CNTL, 0xFF, 0);
 		return 0;
 	}
 
@@ -533,6 +534,9 @@ static void a5xx_regulator_disable(struct adreno_device *adreno_dev)
 	unsigned int reg;
 	struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
 
+	if (adreno_is_a512(adreno_dev))
+		return;
+
 	/* If feature is not supported or not enabled */
 	if (!ADRENO_FEATURE(adreno_dev, ADRENO_SPTP_PC) ||
 		!test_bit(ADRENO_SPTP_PC_CTRL, &adreno_dev->pwrctrl_flag)) {
@@ -1118,6 +1122,65 @@ static const struct kgsl_hwcg_reg a540_hwcg_regs[] = {
 	{A5XX_RBBM_CLOCK_HYST_GPMU, 0x00000004}
 };
 
+static const struct kgsl_hwcg_reg a512_hwcg_regs[] = {
+	{A5XX_RBBM_CLOCK_CNTL_SP0, 0x02222222},
+	{A5XX_RBBM_CLOCK_CNTL_SP1, 0x02222222},
+	{A5XX_RBBM_CLOCK_CNTL2_SP0, 0x02222220},
+	{A5XX_RBBM_CLOCK_CNTL2_SP1, 0x02222220},
+	{A5XX_RBBM_CLOCK_HYST_SP0, 0x0000F3CF},
+	{A5XX_RBBM_CLOCK_HYST_SP1, 0x0000F3CF},
+	{A5XX_RBBM_CLOCK_DELAY_SP0, 0x00000080},
+	{A5XX_RBBM_CLOCK_DELAY_SP1, 0x00000080},
+	{A5XX_RBBM_CLOCK_CNTL_TP0, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL_TP1, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL2_TP0, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL2_TP1, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL3_TP0, 0x00002222},
+	{A5XX_RBBM_CLOCK_CNTL3_TP1, 0x00002222},
+	{A5XX_RBBM_CLOCK_HYST_TP0, 0x77777777},
+	{A5XX_RBBM_CLOCK_HYST_TP1, 0x77777777},
+	{A5XX_RBBM_CLOCK_HYST2_TP0, 0x77777777},
+	{A5XX_RBBM_CLOCK_HYST2_TP1, 0x77777777},
+	{A5XX_RBBM_CLOCK_HYST3_TP0, 0x00007777},
+	{A5XX_RBBM_CLOCK_HYST3_TP1, 0x00007777},
+	{A5XX_RBBM_CLOCK_DELAY_TP0, 0x11111111},
+	{A5XX_RBBM_CLOCK_DELAY_TP1, 0x11111111},
+	{A5XX_RBBM_CLOCK_DELAY2_TP0, 0x11111111},
+	{A5XX_RBBM_CLOCK_DELAY2_TP1, 0x11111111},
+	{A5XX_RBBM_CLOCK_DELAY3_TP0, 0x00001111},
+	{A5XX_RBBM_CLOCK_DELAY3_TP1, 0x00001111},
+	{A5XX_RBBM_CLOCK_CNTL_UCHE, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL2_UCHE, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL3_UCHE, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL4_UCHE, 0x00222222},
+	{A5XX_RBBM_CLOCK_HYST_UCHE, 0x00444444},
+	{A5XX_RBBM_CLOCK_DELAY_UCHE, 0x00000002},
+	{A5XX_RBBM_CLOCK_CNTL_RB0, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL_RB1, 0x22222222},
+	{A5XX_RBBM_CLOCK_CNTL2_RB0, 0x00222222},
+	{A5XX_RBBM_CLOCK_CNTL2_RB1, 0x00222222},
+	{A5XX_RBBM_CLOCK_CNTL_CCU0, 0x00022220},
+	{A5XX_RBBM_CLOCK_CNTL_CCU1, 0x00022220},
+	{A5XX_RBBM_CLOCK_CNTL_RAC, 0x05522222},
+	{A5XX_RBBM_CLOCK_CNTL2_RAC, 0x00555555},
+	{A5XX_RBBM_CLOCK_HYST_RB_CCU0, 0x04040404},
+	{A5XX_RBBM_CLOCK_HYST_RB_CCU1, 0x04040404},
+	{A5XX_RBBM_CLOCK_HYST_RAC, 0x07444044},
+	{A5XX_RBBM_CLOCK_DELAY_RB_CCU_L1_0, 0x00000002},
+	{A5XX_RBBM_CLOCK_DELAY_RB_CCU_L1_1, 0x00000002},
+	{A5XX_RBBM_CLOCK_DELAY_RAC, 0x00010011},
+	{A5XX_RBBM_CLOCK_CNTL_TSE_RAS_RBBM, 0x04222222},
+	{A5XX_RBBM_CLOCK_MODE_GPC, 0x02222222},
+	{A5XX_RBBM_CLOCK_MODE_VFD, 0x00002222},
+	{A5XX_RBBM_CLOCK_HYST_TSE_RAS_RBBM, 0x00000000},
+	{A5XX_RBBM_CLOCK_HYST_GPC, 0x04104004},
+	{A5XX_RBBM_CLOCK_HYST_VFD, 0x00000000},
+	{A5XX_RBBM_CLOCK_DELAY_HLSQ, 0x00000000},
+	{A5XX_RBBM_CLOCK_DELAY_TSE_RAS_RBBM, 0x00004000},
+	{A5XX_RBBM_CLOCK_DELAY_GPC, 0x00000200},
+	{A5XX_RBBM_CLOCK_DELAY_VFD, 0x00002222},
+};
+
 static const struct {
 	int (*devfunc)(struct adreno_device *adreno_dev);
 	const struct kgsl_hwcg_reg *regs;
@@ -1125,6 +1188,7 @@ static const struct {
 } a5xx_hwcg_registers[] = {
 	{ adreno_is_a540, a540_hwcg_regs, ARRAY_SIZE(a540_hwcg_regs) },
 	{ adreno_is_a530, a530_hwcg_regs, ARRAY_SIZE(a530_hwcg_regs) },
+	{ adreno_is_a512, a512_hwcg_regs, ARRAY_SIZE(a512_hwcg_regs) },
 	{ adreno_is_a510, a510_hwcg_regs, ARRAY_SIZE(a510_hwcg_regs) },
 	{ adreno_is_a505, a50x_hwcg_regs, ARRAY_SIZE(a50x_hwcg_regs) },
 	{ adreno_is_a506, a50x_hwcg_regs, ARRAY_SIZE(a50x_hwcg_regs) },
@@ -1868,6 +1932,11 @@ static void a5xx_start(struct adreno_device *adreno_dev)
 		kgsl_regwrite(device, A5XX_CP_MERCIU_SIZE, 0x20);
 		kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_2, 0x40000030);
 		kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_1, 0x20100D0A);
+	} else if (adreno_is_a540(adreno_dev) || adreno_is_a512(adreno_dev)) {
+		kgsl_regwrite(device, A5XX_CP_MEQ_THRESHOLDS, 0x40);
+		kgsl_regwrite(device, A5XX_CP_MERCIU_SIZE, 0x400);
+		kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_2, 0x80000060);
+		kgsl_regwrite(device, A5XX_CP_ROQ_THRESHOLDS_1, 0x40201B16);
 	} else {
 		kgsl_regwrite(device, A5XX_CP_MEQ_THRESHOLDS, 0x40);
 		kgsl_regwrite(device, A5XX_CP_MERCIU_SIZE, 0x40);
diff --git a/drivers/gpu/msm/adreno_a5xx_snapshot.c b/drivers/gpu/msm/adreno_a5xx_snapshot.c
index 9fd7cb4..c1e0a70 100644
--- a/drivers/gpu/msm/adreno_a5xx_snapshot.c
+++ b/drivers/gpu/msm/adreno_a5xx_snapshot.c
@@ -943,11 +943,13 @@ void a5xx_snapshot(struct adreno_device *adreno_dev,
 	a5xx_snapshot_debugbus(device, snapshot);
 
 	/* Preemption record */
-	FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
-		kgsl_snapshot_add_section(device,
-			KGSL_SNAPSHOT_SECTION_GPU_OBJECT_V2,
-			snapshot, snapshot_preemption_record,
-			&rb->preemption_desc);
+	if (adreno_is_preemption_enabled(adreno_dev)) {
+		FOR_EACH_RINGBUFFER(adreno_dev, rb, i) {
+			kgsl_snapshot_add_section(device,
+				KGSL_SNAPSHOT_SECTION_GPU_OBJECT_V2,
+				snapshot, snapshot_preemption_record,
+				&rb->preemption_desc);
+		}
 	}
 
 }
diff --git a/drivers/gpu/msm/adreno_dispatch.c b/drivers/gpu/msm/adreno_dispatch.c
index 7ad94b8..2cb39ee 100644
--- a/drivers/gpu/msm/adreno_dispatch.c
+++ b/drivers/gpu/msm/adreno_dispatch.c
@@ -359,6 +359,13 @@ static inline void _pop_drawobj(struct adreno_context *drawctxt)
 	drawctxt->queued--;
 }
 
+static void _retire_sparseobj(struct kgsl_drawobj_sparse *sparseobj,
+				struct adreno_context *drawctxt)
+{
+	kgsl_sparse_bind(drawctxt->base.proc_priv, sparseobj);
+	_retire_timestamp(DRAWOBJ(sparseobj));
+}
+
 static int _retire_markerobj(struct kgsl_drawobj_cmd *cmdobj,
 				struct adreno_context *drawctxt)
 {
@@ -436,6 +443,8 @@ static struct kgsl_drawobj *_process_drawqueue_get_next_drawobj(
 				return drawobj;
 		} else if (drawobj->type == SYNCOBJ_TYPE)
 			ret = _retire_syncobj(SYNCOBJ(drawobj), drawctxt);
+		else
+			return ERR_PTR(-EINVAL);
 
 		if (ret == -EAGAIN)
 			return ERR_PTR(-EAGAIN);
@@ -671,6 +680,76 @@ static int sendcmd(struct adreno_device *adreno_dev,
 	return 0;
 }
 
+
+/*
+ * Retires all sync objs from the sparse context
+ * queue and returns one of the below
+ * a) next sparseobj
+ * b) -EAGAIN for syncobj with syncpoints pending
+ * c) -EINVAL for unexpected drawobj
+ * d) NULL for no sparseobj
+ */
+static struct kgsl_drawobj_sparse *_get_next_sparseobj(
+				struct adreno_context *drawctxt)
+{
+	struct kgsl_drawobj *drawobj;
+	unsigned int i = drawctxt->drawqueue_head;
+	int ret = 0;
+
+	if (drawctxt->drawqueue_head == drawctxt->drawqueue_tail)
+		return NULL;
+
+	for (i = drawctxt->drawqueue_head; i != drawctxt->drawqueue_tail;
+			i = DRAWQUEUE_NEXT(i, ADRENO_CONTEXT_DRAWQUEUE_SIZE)) {
+
+		drawobj = drawctxt->drawqueue[i];
+
+		if (drawobj == NULL)
+			return NULL;
+
+		if (drawobj->type == SYNCOBJ_TYPE)
+			ret = _retire_syncobj(SYNCOBJ(drawobj), drawctxt);
+		else if (drawobj->type == SPARSEOBJ_TYPE)
+			return SPARSEOBJ(drawobj);
+		else
+			return ERR_PTR(-EINVAL);
+
+		if (ret == -EAGAIN)
+			return ERR_PTR(-EAGAIN);
+
+		continue;
+	}
+
+	return NULL;
+}
+
+static int _process_drawqueue_sparse(
+		struct adreno_context *drawctxt)
+{
+	struct kgsl_drawobj_sparse *sparseobj;
+	int ret = 0;
+	unsigned int i;
+
+	for (i = 0; i < ADRENO_CONTEXT_DRAWQUEUE_SIZE; i++) {
+
+		spin_lock(&drawctxt->lock);
+		sparseobj = _get_next_sparseobj(drawctxt);
+		if (IS_ERR_OR_NULL(sparseobj)) {
+			if (IS_ERR(sparseobj))
+				ret = PTR_ERR(sparseobj);
+			spin_unlock(&drawctxt->lock);
+			return ret;
+		}
+
+		_pop_drawobj(drawctxt);
+		spin_unlock(&drawctxt->lock);
+
+		_retire_sparseobj(sparseobj, drawctxt);
+	}
+
+	return 0;
+}
+
 /**
  * dispatcher_context_sendcmds() - Send commands from a context to the GPU
  * @adreno_dev: Pointer to the adreno device struct
@@ -690,6 +769,9 @@ static int dispatcher_context_sendcmds(struct adreno_device *adreno_dev,
 	int inflight = _drawqueue_inflight(dispatch_q);
 	unsigned int timestamp;
 
+	if (drawctxt->base.flags & KGSL_CONTEXT_SPARSE)
+		return _process_drawqueue_sparse(drawctxt);
+
 	if (dispatch_q->inflight >= inflight) {
 		spin_lock(&drawctxt->lock);
 		_process_drawqueue_get_next_drawobj(drawctxt);
@@ -1125,6 +1207,31 @@ static void _queue_drawobj(struct adreno_context *drawctxt,
 	trace_adreno_cmdbatch_queued(drawobj, drawctxt->queued);
 }
 
+static int _queue_sparseobj(struct adreno_device *adreno_dev,
+	struct adreno_context *drawctxt, struct kgsl_drawobj_sparse *sparseobj,
+	uint32_t *timestamp, unsigned int user_ts)
+{
+	struct kgsl_drawobj *drawobj = DRAWOBJ(sparseobj);
+	int ret;
+
+	ret = get_timestamp(drawctxt, drawobj, timestamp, user_ts);
+	if (ret)
+		return ret;
+
+	/*
+	 * See if we can fastpath this thing - if nothing is
+	 * queued bind/unbind without queueing the context
+	 */
+	if (!drawctxt->queued)
+		return 1;
+
+	drawctxt->queued_timestamp = *timestamp;
+	_queue_drawobj(drawctxt, drawobj);
+
+	return 0;
+}
+
+
 static int _queue_markerobj(struct adreno_device *adreno_dev,
 	struct adreno_context *drawctxt, struct kgsl_drawobj_cmd *markerobj,
 	uint32_t *timestamp, unsigned int user_ts)
@@ -1142,7 +1249,6 @@ static int _queue_markerobj(struct adreno_device *adreno_dev,
 	 */
 	if (!drawctxt->queued && kgsl_check_timestamp(drawobj->device,
 			drawobj->context, drawctxt->queued_timestamp)) {
-		trace_adreno_cmdbatch_queued(drawobj, drawctxt->queued);
 		_retire_timestamp(drawobj);
 		return 1;
 	}
@@ -1213,7 +1319,7 @@ static void _queue_syncobj(struct adreno_context *drawctxt,
 }
 
 /**
- * adreno_dispactcher_queue_drawobj() - Queue a new draw object in the context
+ * adreno_dispactcher_queue_cmds() - Queue a new draw object in the context
  * @dev_priv: Pointer to the device private struct
  * @context: Pointer to the kgsl draw context
  * @drawobj: Pointer to the array of drawobj's being submitted
@@ -1235,6 +1341,9 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
 	int ret;
 	unsigned int i, user_ts;
 
+	if (!count)
+		return -EINVAL;
+
 	ret = _check_context_state(&drawctxt->base);
 	if (ret)
 		return ret;
@@ -1284,6 +1393,20 @@ int adreno_dispatcher_queue_cmds(struct kgsl_device_private *dev_priv,
 			_queue_syncobj(drawctxt, SYNCOBJ(drawobj[i]),
 						timestamp);
 			break;
+		case SPARSEOBJ_TYPE:
+			ret = _queue_sparseobj(adreno_dev, drawctxt,
+					SPARSEOBJ(drawobj[i]),
+					timestamp, user_ts);
+			if (ret == 1) {
+				spin_unlock(&drawctxt->lock);
+				_retire_sparseobj(SPARSEOBJ(drawobj[i]),
+						drawctxt);
+				return 0;
+			} else if (ret) {
+				spin_unlock(&drawctxt->lock);
+				return ret;
+			}
+			break;
 		default:
 			spin_unlock(&drawctxt->lock);
 			return -EINVAL;
diff --git a/drivers/gpu/msm/adreno_drawctxt.c b/drivers/gpu/msm/adreno_drawctxt.c
index 4da6763..cd7ffe7 100644
--- a/drivers/gpu/msm/adreno_drawctxt.c
+++ b/drivers/gpu/msm/adreno_drawctxt.c
@@ -349,7 +349,8 @@ adreno_drawctxt_create(struct kgsl_device_private *dev_priv,
 		KGSL_CONTEXT_IFH_NOP |
 		KGSL_CONTEXT_SECURE |
 		KGSL_CONTEXT_PREEMPT_STYLE_MASK |
-		KGSL_CONTEXT_NO_SNAPSHOT);
+		KGSL_CONTEXT_NO_SNAPSHOT |
+		KGSL_CONTEXT_SPARSE);
 
 	/* Check for errors before trying to initialize */
 
diff --git a/drivers/gpu/msm/kgsl.c b/drivers/gpu/msm/kgsl.c
index e50442a..56eae50 100644
--- a/drivers/gpu/msm/kgsl.c
+++ b/drivers/gpu/msm/kgsl.c
@@ -1448,6 +1448,17 @@ long kgsl_ioctl_device_waittimestamp_ctxtid(
 	return result;
 }
 
+static inline bool _check_context_is_sparse(struct kgsl_context *context,
+			uint64_t flags)
+{
+	if ((context->flags & KGSL_CONTEXT_SPARSE) ||
+		(flags & KGSL_DRAWOBJ_SPARSE))
+		return true;
+
+	return false;
+}
+
+
 long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
 				      unsigned int cmd, void *data)
 {
@@ -1472,6 +1483,11 @@ long kgsl_ioctl_rb_issueibcmds(struct kgsl_device_private *dev_priv,
 	if (context == NULL)
 		return -EINVAL;
 
+	if (_check_context_is_sparse(context, param->flags)) {
+		kgsl_context_put(context);
+		return -EINVAL;
+	}
+
 	cmdobj = kgsl_drawobj_cmd_create(device, context, param->flags,
 					CMDOBJ_TYPE);
 	if (IS_ERR(cmdobj)) {
@@ -1567,6 +1583,11 @@ long kgsl_ioctl_submit_commands(struct kgsl_device_private *dev_priv,
 	if (context == NULL)
 		return -EINVAL;
 
+	if (_check_context_is_sparse(context, param->flags)) {
+		kgsl_context_put(context);
+		return -EINVAL;
+	}
+
 	if (type & SYNCOBJ_TYPE) {
 		struct kgsl_drawobj_sync *syncobj =
 				kgsl_drawobj_sync_create(device, context);
@@ -1641,6 +1662,11 @@ long kgsl_ioctl_gpu_command(struct kgsl_device_private *dev_priv,
 	if (context == NULL)
 		return -EINVAL;
 
+	if (_check_context_is_sparse(context, param->flags)) {
+		kgsl_context_put(context);
+		return -EINVAL;
+	}
+
 	if (type & SYNCOBJ_TYPE) {
 		struct kgsl_drawobj_sync *syncobj =
 				kgsl_drawobj_sync_create(device, context);
@@ -3752,6 +3778,128 @@ long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv,
 	return ret;
 }
 
+long kgsl_ioctl_gpu_sparse_command(struct kgsl_device_private *dev_priv,
+		unsigned int cmd, void *data)
+{
+	struct kgsl_gpu_sparse_command *param = data;
+	struct kgsl_device *device = dev_priv->device;
+	struct kgsl_context *context;
+	struct kgsl_drawobj *drawobj[2];
+	struct kgsl_drawobj_sparse *sparseobj;
+	long result;
+	unsigned int i = 0;
+
+	/* Make sure sparse and syncpoint count isn't too big */
+	if (param->numsparse > KGSL_MAX_SPARSE ||
+		param->numsyncs > KGSL_MAX_SYNCPOINTS)
+		return -EINVAL;
+
+	/* Make sure there is atleast one sparse or sync */
+	if (param->numsparse == 0 && param->numsyncs == 0)
+		return -EINVAL;
+
+	/* Only Sparse commands are supported in this ioctl */
+	if (!(param->flags & KGSL_DRAWOBJ_SPARSE) || (param->flags &
+			(KGSL_DRAWOBJ_SUBMIT_IB_LIST | KGSL_DRAWOBJ_MARKER
+			| KGSL_DRAWOBJ_SYNC)))
+		return -EINVAL;
+
+	context = kgsl_context_get_owner(dev_priv, param->context_id);
+	if (context == NULL)
+		return -EINVAL;
+
+	/* Restrict bind commands to bind context */
+	if (!(context->flags & KGSL_CONTEXT_SPARSE)) {
+		kgsl_context_put(context);
+		return -EINVAL;
+	}
+
+	if (param->numsyncs) {
+		struct kgsl_drawobj_sync *syncobj = kgsl_drawobj_sync_create(
+				device, context);
+		if (IS_ERR(syncobj)) {
+			result = PTR_ERR(syncobj);
+			goto done;
+		}
+
+		drawobj[i++] = DRAWOBJ(syncobj);
+		result = kgsl_drawobj_sync_add_synclist(device, syncobj,
+				to_user_ptr(param->synclist),
+				param->syncsize, param->numsyncs);
+		if (result)
+			goto done;
+	}
+
+	if (param->numsparse) {
+		sparseobj = kgsl_drawobj_sparse_create(device, context,
+					param->flags);
+		if (IS_ERR(sparseobj)) {
+			result = PTR_ERR(sparseobj);
+			goto done;
+		}
+
+		sparseobj->id = param->id;
+		drawobj[i++] = DRAWOBJ(sparseobj);
+		result = kgsl_drawobj_sparse_add_sparselist(device, sparseobj,
+				param->id, to_user_ptr(param->sparselist),
+				param->sparsesize, param->numsparse);
+		if (result)
+			goto done;
+	}
+
+	result = dev_priv->device->ftbl->queue_cmds(dev_priv, context,
+					drawobj, i, &param->timestamp);
+
+done:
+	/*
+	 * -EPROTO is a "success" error - it just tells the user that the
+	 * context had previously faulted
+	 */
+	if (result && result != -EPROTO)
+		while (i--)
+			kgsl_drawobj_destroy(drawobj[i]);
+
+	kgsl_context_put(context);
+	return result;
+}
+
+void kgsl_sparse_bind(struct kgsl_process_private *private,
+		struct kgsl_drawobj_sparse *sparseobj)
+{
+	struct kgsl_sparseobj_node *sparse_node;
+	struct kgsl_mem_entry *virt_entry = NULL;
+	long ret = 0;
+	char *name;
+
+	virt_entry = kgsl_sharedmem_find_id_flags(private, sparseobj->id,
+			KGSL_MEMFLAGS_SPARSE_VIRT);
+	if (virt_entry == NULL)
+		return;
+
+	list_for_each_entry(sparse_node, &sparseobj->sparselist, node) {
+		if (sparse_node->obj.flags & KGSL_SPARSE_BIND) {
+			ret = sparse_bind_range(private, &sparse_node->obj,
+					virt_entry);
+			name = "bind";
+		} else {
+			ret = sparse_unbind_range(&sparse_node->obj,
+					virt_entry);
+			name = "unbind";
+		}
+
+		if (ret)
+			KGSL_CORE_ERR("kgsl: Unable to '%s' ret %ld virt_id %d, phys_id %d, virt_offset %16.16llX, phys_offset %16.16llX, size %16.16llX, flags %16.16llX\n",
+				name, ret, sparse_node->virt_id,
+				sparse_node->obj.id,
+				sparse_node->obj.virtoffset,
+				sparse_node->obj.physoffset,
+				sparse_node->obj.size, sparse_node->obj.flags);
+	}
+
+	kgsl_mem_entry_put(virt_entry);
+}
+EXPORT_SYMBOL(kgsl_sparse_bind);
+
 long kgsl_ioctl_gpuobj_info(struct kgsl_device_private *dev_priv,
 		unsigned int cmd, void *data)
 {
@@ -4607,7 +4755,7 @@ static void kgsl_core_exit(void)
 		kgsl_driver.class = NULL;
 	}
 
-	kgsl_drawobj_exit();
+	kgsl_drawobjs_cache_exit();
 
 	kgsl_memfree_exit();
 	unregister_chrdev_region(kgsl_driver.major, KGSL_DEVICE_MAX);
@@ -4684,7 +4832,7 @@ static int __init kgsl_core_init(void)
 
 	kgsl_events_init();
 
-	result = kgsl_drawobj_init();
+	result = kgsl_drawobjs_cache_init();
 	if (result)
 		goto err;
 
diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h
index c60a071..3f1c86e 100644
--- a/drivers/gpu/msm/kgsl.h
+++ b/drivers/gpu/msm/kgsl.h
@@ -99,6 +99,7 @@ static inline void KGSL_STATS_ADD(uint64_t size, atomic_long_t *stat,
 
 #define KGSL_MAX_NUMIBS 100000
 #define KGSL_MAX_SYNCPOINTS 32
+#define KGSL_MAX_SPARSE 1000
 
 struct kgsl_device;
 struct kgsl_context;
@@ -425,6 +426,8 @@ long kgsl_ioctl_sparse_bind(struct kgsl_device_private *dev_priv,
 					unsigned int cmd, void *data);
 long kgsl_ioctl_sparse_unbind(struct kgsl_device_private *dev_priv,
 					unsigned int cmd, void *data);
+long kgsl_ioctl_gpu_sparse_command(struct kgsl_device_private *dev_priv,
+					unsigned int cmd, void *data);
 
 void kgsl_mem_entry_destroy(struct kref *kref);
 
diff --git a/drivers/gpu/msm/kgsl_compat.c b/drivers/gpu/msm/kgsl_compat.c
index e0e6a2b..1c89ed5 100644
--- a/drivers/gpu/msm/kgsl_compat.c
+++ b/drivers/gpu/msm/kgsl_compat.c
@@ -364,6 +364,8 @@ static const struct kgsl_ioctl kgsl_compat_ioctl_funcs[] = {
 			kgsl_ioctl_sparse_virt_free),
 	KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_BIND,
 			kgsl_ioctl_sparse_bind),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPU_SPARSE_COMMAND,
+			kgsl_ioctl_gpu_sparse_command),
 };
 
 long kgsl_compat_ioctl(struct file *filep, unsigned int cmd, unsigned long arg)
diff --git a/drivers/gpu/msm/kgsl_device.h b/drivers/gpu/msm/kgsl_device.h
index ed3f78a..ae164bc 100644
--- a/drivers/gpu/msm/kgsl_device.h
+++ b/drivers/gpu/msm/kgsl_device.h
@@ -207,6 +207,18 @@ struct kgsl_memobj_node {
 	unsigned long priv;
 };
 
+/**
+ * struct kgsl_sparseobj_node - Sparse object descriptor
+ * @node: Local list node for the sparse cmdbatch
+ * @virt_id: Virtual ID to bind/unbind
+ * @obj:  struct kgsl_sparse_binding_object
+ */
+struct kgsl_sparseobj_node {
+	struct list_head node;
+	unsigned int virt_id;
+	struct kgsl_sparse_binding_object obj;
+};
+
 struct kgsl_device {
 	struct device *dev;
 	const char *name;
@@ -644,6 +656,9 @@ long kgsl_ioctl_copy_in(unsigned int kernel_cmd, unsigned int user_cmd,
 long kgsl_ioctl_copy_out(unsigned int kernel_cmd, unsigned int user_cmd,
 		unsigned long arg, unsigned char *ptr);
 
+void kgsl_sparse_bind(struct kgsl_process_private *private,
+		struct kgsl_drawobj_sparse *sparse);
+
 /**
  * kgsl_context_put() - Release context reference count
  * @context: Pointer to the KGSL context to be released
diff --git a/drivers/gpu/msm/kgsl_drawobj.c b/drivers/gpu/msm/kgsl_drawobj.c
index 01c3a06..910f405 100644
--- a/drivers/gpu/msm/kgsl_drawobj.c
+++ b/drivers/gpu/msm/kgsl_drawobj.c
@@ -37,10 +37,12 @@
 #include "kgsl_compat.h"
 
 /*
- * Define an kmem cache for the memobj structures since we allocate and free
- * them so frequently
+ * Define an kmem cache for the memobj & sparseobj structures since we
+ * allocate and free them so frequently
  */
 static struct kmem_cache *memobjs_cache;
+static struct kmem_cache *sparseobjs_cache;
+
 
 static void drawobj_destroy_object(struct kref *kref)
 {
@@ -60,6 +62,9 @@ static void drawobj_destroy_object(struct kref *kref)
 	case MARKEROBJ_TYPE:
 		kfree(CMDOBJ(drawobj));
 		break;
+	case SPARSEOBJ_TYPE:
+		kfree(SPARSEOBJ(drawobj));
+		break;
 	}
 }
 
@@ -211,6 +216,18 @@ static inline void memobj_list_free(struct list_head *list)
 	}
 }
 
+static void drawobj_destroy_sparse(struct kgsl_drawobj *drawobj)
+{
+	struct kgsl_sparseobj_node *mem, *tmpmem;
+	struct list_head *list = &SPARSEOBJ(drawobj)->sparselist;
+
+	/* Free the sparse mem here */
+	list_for_each_entry_safe(mem, tmpmem, list, node) {
+		list_del_init(&mem->node);
+		kmem_cache_free(sparseobjs_cache, mem);
+	}
+}
+
 static void drawobj_destroy_sync(struct kgsl_drawobj *drawobj)
 {
 	struct kgsl_drawobj_sync *syncobj = SYNCOBJ(drawobj);
@@ -297,6 +314,8 @@ void kgsl_drawobj_destroy(struct kgsl_drawobj *drawobj)
 		drawobj_destroy_sync(drawobj);
 	else if (drawobj->type & (CMDOBJ_TYPE | MARKEROBJ_TYPE))
 		drawobj_destroy_cmd(drawobj);
+	else if (drawobj->type == SPARSEOBJ_TYPE)
+		drawobj_destroy_sparse(drawobj);
 	else
 		return;
 
@@ -610,16 +629,26 @@ int kgsl_drawobj_cmd_add_ibdesc(struct kgsl_device *device,
 	return 0;
 }
 
-static inline int drawobj_init(struct kgsl_device *device,
-	struct kgsl_context *context, struct kgsl_drawobj *drawobj,
+static void *_drawobj_create(struct kgsl_device *device,
+	struct kgsl_context *context, unsigned int size,
 	unsigned int type)
 {
+	void *obj = kzalloc(size, GFP_KERNEL);
+	struct kgsl_drawobj *drawobj;
+
+	if (obj == NULL)
+		return ERR_PTR(-ENOMEM);
+
 	/*
 	 * Increase the reference count on the context so it doesn't disappear
 	 * during the lifetime of this object
 	 */
-	if (!_kgsl_context_get(context))
-		return -ENOENT;
+	if (!_kgsl_context_get(context)) {
+		kfree(obj);
+		return ERR_PTR(-ENOENT);
+	}
+
+	drawobj = obj;
 
 	kref_init(&drawobj->refcount);
 
@@ -627,7 +656,28 @@ static inline int drawobj_init(struct kgsl_device *device,
 	drawobj->context = context;
 	drawobj->type = type;
 
-	return 0;
+	return obj;
+}
+
+/**
+ * kgsl_drawobj_sparse_create() - Create a new sparse obj structure
+ * @device: Pointer to a KGSL device struct
+ * @context: Pointer to a KGSL context struct
+ * @flags: Flags for the sparse obj
+ *
+ * Allocate an new kgsl_drawobj_sparse structure
+ */
+struct kgsl_drawobj_sparse *kgsl_drawobj_sparse_create(
+		struct kgsl_device *device,
+		struct kgsl_context *context, unsigned int flags)
+{
+	struct kgsl_drawobj_sparse *sparseobj = _drawobj_create(device,
+		context, sizeof(*sparseobj), SPARSEOBJ_TYPE);
+
+	if (!IS_ERR(sparseobj))
+		INIT_LIST_HEAD(&sparseobj->sparselist);
+
+	return sparseobj;
 }
 
 /**
@@ -641,18 +691,13 @@ static inline int drawobj_init(struct kgsl_device *device,
 struct kgsl_drawobj_sync *kgsl_drawobj_sync_create(struct kgsl_device *device,
 		struct kgsl_context *context)
 {
-	struct kgsl_drawobj_sync *syncobj = kzalloc(sizeof(*syncobj),
-							GFP_KERNEL);
-	if (syncobj == NULL)
-		return ERR_PTR(-ENOMEM);
-
-	if (drawobj_init(device, context, DRAWOBJ(syncobj), SYNCOBJ_TYPE)) {
-		kfree(syncobj);
-		return ERR_PTR(-ENOENT);
-	}
+	struct kgsl_drawobj_sync *syncobj = _drawobj_create(device,
+		context, sizeof(*syncobj), SYNCOBJ_TYPE);
 
 	/* Add a timer to help debug sync deadlocks */
-	setup_timer(&syncobj->timer, syncobj_timer, (unsigned long) syncobj);
+	if (!IS_ERR(syncobj))
+		setup_timer(&syncobj->timer, syncobj_timer,
+				(unsigned long) syncobj);
 
 	return syncobj;
 }
@@ -671,27 +716,13 @@ struct kgsl_drawobj_cmd *kgsl_drawobj_cmd_create(struct kgsl_device *device,
 		struct kgsl_context *context, unsigned int flags,
 		unsigned int type)
 {
-	struct kgsl_drawobj_cmd *cmdobj = kzalloc(sizeof(*cmdobj), GFP_KERNEL);
-	struct kgsl_drawobj *drawobj;
+	struct kgsl_drawobj_cmd *cmdobj = _drawobj_create(device,
+		context, sizeof(*cmdobj),
+		(type & (CMDOBJ_TYPE | MARKEROBJ_TYPE)));
 
-	if (cmdobj == NULL)
-		return ERR_PTR(-ENOMEM);
-
-	type &= CMDOBJ_TYPE | MARKEROBJ_TYPE;
-	if (type == 0) {
-		kfree(cmdobj);
-		return ERR_PTR(-EINVAL);
-	}
-
-	drawobj = DRAWOBJ(cmdobj);
-
-	if (drawobj_init(device, context, drawobj, type)) {
-		kfree(cmdobj);
-		return ERR_PTR(-ENOENT);
-	}
-
-	/* sanitize our flags for drawobj's */
-	drawobj->flags = flags & (KGSL_DRAWOBJ_CTX_SWITCH
+	if (!IS_ERR(cmdobj)) {
+		/* sanitize our flags for drawobj's */
+		cmdobj->base.flags = flags & (KGSL_DRAWOBJ_CTX_SWITCH
 				| KGSL_DRAWOBJ_MARKER
 				| KGSL_DRAWOBJ_END_OF_FRAME
 				| KGSL_DRAWOBJ_PWR_CONSTRAINT
@@ -699,8 +730,9 @@ struct kgsl_drawobj_cmd *kgsl_drawobj_cmd_create(struct kgsl_device *device,
 				| KGSL_DRAWOBJ_PROFILING
 				| KGSL_DRAWOBJ_PROFILING_KTIME);
 
-	INIT_LIST_HEAD(&cmdobj->cmdlist);
-	INIT_LIST_HEAD(&cmdobj->memlist);
+		INIT_LIST_HEAD(&cmdobj->cmdlist);
+		INIT_LIST_HEAD(&cmdobj->memlist);
+	}
 
 	return cmdobj;
 }
@@ -864,7 +896,7 @@ int kgsl_drawobj_sync_add_syncpoints(struct kgsl_device *device,
 	return 0;
 }
 
-static int drawobj_add_object(struct list_head *head,
+static int kgsl_drawobj_add_memobject(struct list_head *head,
 		struct kgsl_command_object *obj)
 {
 	struct kgsl_memobj_node *mem;
@@ -884,6 +916,62 @@ static int drawobj_add_object(struct list_head *head,
 	return 0;
 }
 
+static int kgsl_drawobj_add_sparseobject(struct list_head *head,
+		struct kgsl_sparse_binding_object *obj, unsigned int virt_id)
+{
+	struct kgsl_sparseobj_node *mem;
+
+	mem = kmem_cache_alloc(sparseobjs_cache, GFP_KERNEL);
+	if (mem == NULL)
+		return -ENOMEM;
+
+	mem->virt_id = virt_id;
+	mem->obj.id = obj->id;
+	mem->obj.virtoffset = obj->virtoffset;
+	mem->obj.physoffset = obj->physoffset;
+	mem->obj.size = obj->size;
+	mem->obj.flags = obj->flags;
+
+	list_add_tail(&mem->node, head);
+	return 0;
+}
+
+int kgsl_drawobj_sparse_add_sparselist(struct kgsl_device *device,
+		struct kgsl_drawobj_sparse *sparseobj, unsigned int id,
+		void __user *ptr, unsigned int size, unsigned int count)
+{
+	struct kgsl_sparse_binding_object obj;
+	int i, ret = 0;
+
+	ret = _verify_input_list(count, ptr, size);
+	if (ret <= 0)
+		return ret;
+
+	for (i = 0; i < count; i++) {
+		memset(&obj, 0, sizeof(obj));
+
+		ret = _copy_from_user(&obj, ptr, sizeof(obj), size);
+		if (ret)
+			return ret;
+
+		if (!(obj.flags & (KGSL_SPARSE_BIND | KGSL_SPARSE_UNBIND)))
+			return -EINVAL;
+
+		ret = kgsl_drawobj_add_sparseobject(&sparseobj->sparselist,
+			&obj, id);
+		if (ret)
+			return ret;
+
+		ptr += sizeof(obj);
+	}
+
+	sparseobj->size = size;
+	sparseobj->count = count;
+
+	return 0;
+}
+
+
 #define CMDLIST_FLAGS \
 	(KGSL_CMDLIST_IB | \
 	 KGSL_CMDLIST_CTXTSWITCH_PREAMBLE | \
@@ -922,7 +1010,7 @@ int kgsl_drawobj_cmd_add_cmdlist(struct kgsl_device *device,
 			return -EINVAL;
 		}
 
-		ret = drawobj_add_object(&cmdobj->cmdlist, &obj);
+		ret = kgsl_drawobj_add_memobject(&cmdobj->cmdlist, &obj);
 		if (ret)
 			return ret;
 
@@ -967,7 +1055,8 @@ int kgsl_drawobj_cmd_add_memlist(struct kgsl_device *device,
 			add_profiling_buffer(device, cmdobj, obj.gpuaddr,
 				obj.size, obj.id, obj.offset);
 		else {
-			ret = drawobj_add_object(&cmdobj->memlist, &obj);
+			ret = kgsl_drawobj_add_memobject(&cmdobj->memlist,
+				&obj);
 			if (ret)
 				return ret;
 		}
@@ -1018,19 +1107,19 @@ int kgsl_drawobj_sync_add_synclist(struct kgsl_device *device,
 	return 0;
 }
 
-void kgsl_drawobj_exit(void)
+void kgsl_drawobjs_cache_exit(void)
 {
-	if (memobjs_cache != NULL)
-		kmem_cache_destroy(memobjs_cache);
+	kmem_cache_destroy(memobjs_cache);
+	kmem_cache_destroy(sparseobjs_cache);
 }
 
-int kgsl_drawobj_init(void)
+int kgsl_drawobjs_cache_init(void)
 {
 	memobjs_cache = KMEM_CACHE(kgsl_memobj_node, 0);
-	if (memobjs_cache == NULL) {
-		KGSL_CORE_ERR("failed to create memobjs_cache");
+	sparseobjs_cache = KMEM_CACHE(kgsl_sparseobj_node, 0);
+
+	if (!memobjs_cache || !sparseobjs_cache)
 		return -ENOMEM;
-	}
 
 	return 0;
 }
diff --git a/drivers/gpu/msm/kgsl_drawobj.h b/drivers/gpu/msm/kgsl_drawobj.h
index 89ed944..fd9d2bc 100644
--- a/drivers/gpu/msm/kgsl_drawobj.h
+++ b/drivers/gpu/msm/kgsl_drawobj.h
@@ -18,10 +18,13 @@
 	container_of(obj, struct kgsl_drawobj_sync, base)
 #define CMDOBJ(obj) \
 	container_of(obj, struct kgsl_drawobj_cmd, base)
+#define SPARSEOBJ(obj) \
+	container_of(obj, struct kgsl_drawobj_sparse, base)
 
 #define CMDOBJ_TYPE     BIT(0)
 #define MARKEROBJ_TYPE  BIT(1)
 #define SYNCOBJ_TYPE    BIT(2)
+#define SPARSEOBJ_TYPE  BIT(3)
 
 /**
  * struct kgsl_drawobj - KGSL drawobj descriptor
@@ -45,7 +48,7 @@ struct kgsl_drawobj {
  * struct kgsl_drawobj_cmd - KGSL command obj, This covers marker
  * cmds also since markers are special form of cmds that do not
  * need their cmds to be executed.
- * @base: Base kgsl_drawobj
+ * @base: Base kgsl_drawobj, this needs to be the first entry
  * @priv: Internal flags
  * @global_ts: The ringbuffer timestamp corresponding to this
  *    command obj
@@ -123,6 +126,22 @@ struct kgsl_drawobj_sync_event {
 	struct kgsl_device *device;
 };
 
+/**
+ * struct kgsl_drawobj_sparse - KGSl sparse obj descriptor
+ * @base: Base kgsl_obj, this needs to be the first entry
+ * @id: virtual id of the bind/unbind
+ * @sparselist: list of binds/unbinds
+ * @size: Size of kgsl_sparse_bind_object
+ * @count: Number of elements in list
+ */
+struct kgsl_drawobj_sparse {
+	struct kgsl_drawobj base;
+	unsigned int id;
+	struct list_head sparselist;
+	unsigned int size;
+	unsigned int count;
+};
+
 #define KGSL_DRAWOBJ_FLAGS \
 	{ KGSL_DRAWOBJ_MARKER, "MARKER" }, \
 	{ KGSL_DRAWOBJ_CTX_SWITCH, "CTX_SWITCH" }, \
@@ -172,9 +191,15 @@ int kgsl_drawobj_sync_add_synclist(struct kgsl_device *device,
 int kgsl_drawobj_sync_add_sync(struct kgsl_device *device,
 		struct kgsl_drawobj_sync *syncobj,
 		struct kgsl_cmd_syncpoint *sync);
+struct kgsl_drawobj_sparse *kgsl_drawobj_sparse_create(
+		struct kgsl_device *device,
+		struct kgsl_context *context, unsigned int flags);
+int kgsl_drawobj_sparse_add_sparselist(struct kgsl_device *device,
+		struct kgsl_drawobj_sparse *sparseobj, unsigned int id,
+		void __user *ptr, unsigned int size, unsigned int count);
 
-int kgsl_drawobj_init(void);
-void kgsl_drawobj_exit(void);
+int kgsl_drawobjs_cache_init(void);
+void kgsl_drawobjs_cache_exit(void);
 
 void kgsl_dump_syncpoints(struct kgsl_device *device,
 	struct kgsl_drawobj_sync *syncobj);
diff --git a/drivers/gpu/msm/kgsl_ioctl.c b/drivers/gpu/msm/kgsl_ioctl.c
index 2c57816..bfce4d4 100644
--- a/drivers/gpu/msm/kgsl_ioctl.c
+++ b/drivers/gpu/msm/kgsl_ioctl.c
@@ -94,6 +94,8 @@ static const struct kgsl_ioctl kgsl_ioctl_funcs[] = {
 			kgsl_ioctl_sparse_virt_free),
 	KGSL_IOCTL_FUNC(IOCTL_KGSL_SPARSE_BIND,
 			kgsl_ioctl_sparse_bind),
+	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPU_SPARSE_COMMAND,
+			kgsl_ioctl_gpu_sparse_command),
 };
 
 long kgsl_ioctl_copy_in(unsigned int kernel_cmd, unsigned int user_cmd,
diff --git a/drivers/gpu/msm/kgsl_pool.c b/drivers/gpu/msm/kgsl_pool.c
index 6ecbab4..bb92b8b 100644
--- a/drivers/gpu/msm/kgsl_pool.c
+++ b/drivers/gpu/msm/kgsl_pool.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -299,13 +299,49 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
 	struct page *p = NULL;
 	int order = get_order(*page_size);
 	int pool_idx;
+	size_t size = 0;
 
 	if ((pages == NULL) || pages_len < (*page_size >> PAGE_SHIFT))
 		return -EINVAL;
 
+	/* If the pool is not configured get pages from the system */
+	if (!kgsl_num_pools) {
+		gfp_t gfp_mask = kgsl_gfp_mask(order);
+
+		page = alloc_pages(gfp_mask, order);
+		if (page == NULL) {
+			/* Retry with lower order pages */
+			if (order > 0) {
+				size = PAGE_SIZE << --order;
+				goto eagain;
+
+			} else
+				return -ENOMEM;
+		}
+		_kgsl_pool_zero_page(page, order);
+		goto done;
+	}
+
 	pool = _kgsl_get_pool_from_order(order);
-	if (pool == NULL)
-		return -EINVAL;
+	if (pool == NULL) {
+		/* Retry with lower order pages */
+		if (order > 0) {
+			size = PAGE_SIZE << --order;
+			goto eagain;
+		} else {
+			/*
+			 * Fall back to direct allocation in case
+			 * pool with zero order is not present
+			 */
+			gfp_t gfp_mask = kgsl_gfp_mask(order);
+
+			page = alloc_pages(gfp_mask, order);
+			if (page == NULL)
+				return -ENOMEM;
+			_kgsl_pool_zero_page(page, order);
+			goto done;
+		}
+	}
 
 	pool_idx = kgsl_pool_idx_lookup(order);
 	page = _kgsl_pool_get_page(pool);
@@ -316,10 +352,9 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
 
 		/* Only allocate non-reserved memory for certain pools */
 		if (!pool->allocation_allowed && pool_idx > 0) {
-			*page_size = PAGE_SIZE <<
+			size = PAGE_SIZE <<
 					kgsl_pools[pool_idx-1].pool_order;
-			*align = ilog2(*page_size);
-			return -EAGAIN;
+			goto eagain;
 		}
 
 		page = alloc_pages(gfp_mask, order);
@@ -327,10 +362,9 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
 		if (!page) {
 			if (pool_idx > 0) {
 				/* Retry with lower order pages */
-				*page_size = PAGE_SIZE <<
+				size = PAGE_SIZE <<
 					kgsl_pools[pool_idx-1].pool_order;
-				*align = ilog2(*page_size);
-				return -EAGAIN;
+				goto eagain;
 			} else
 				return -ENOMEM;
 		}
@@ -338,6 +372,7 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
 		_kgsl_pool_zero_page(page, order);
 	}
 
+done:
 	for (j = 0; j < (*page_size >> PAGE_SHIFT); j++) {
 		p = nth_page(page, j);
 		pages[pcount] = p;
@@ -345,6 +380,12 @@ int kgsl_pool_alloc_page(int *page_size, struct page **pages,
 	}
 
 	return pcount;
+
+eagain:
+	*page_size = kgsl_get_page_size(size,
+			ilog2(size));
+	*align = ilog2(*page_size);
+	return -EAGAIN;
 }
 
 void kgsl_pool_free_page(struct page *page)
diff --git a/drivers/gpu/msm/kgsl_pwrctrl.c b/drivers/gpu/msm/kgsl_pwrctrl.c
index c43c210..ea13419 100644
--- a/drivers/gpu/msm/kgsl_pwrctrl.c
+++ b/drivers/gpu/msm/kgsl_pwrctrl.c
@@ -2012,6 +2012,42 @@ static int _isense_clk_set_rate(struct kgsl_pwrctrl *pwr, int level)
 	return clk_set_rate(pwr->grp_clks[pwr->isense_clk_indx], rate);
 }
 
+static inline void _close_pcl(struct kgsl_pwrctrl *pwr)
+{
+	if (pwr->pcl)
+		msm_bus_scale_unregister_client(pwr->pcl);
+
+	pwr->pcl = 0;
+}
+
+static inline void _close_ocmem_pcl(struct kgsl_pwrctrl *pwr)
+{
+	if (pwr->ocmem_pcl)
+		msm_bus_scale_unregister_client(pwr->ocmem_pcl);
+
+	pwr->ocmem_pcl = 0;
+}
+
+static inline void _close_regulators(struct kgsl_pwrctrl *pwr)
+{
+	int i;
+
+	for (i = 0; i < KGSL_MAX_REGULATORS; i++)
+		pwr->regulators[i].reg = NULL;
+}
+
+static inline void _close_clks(struct kgsl_device *device)
+{
+	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
+	int i;
+
+	for (i = 0; i < KGSL_MAX_CLKS; i++)
+		pwr->grp_clks[i] = NULL;
+
+	if (pwr->gpu_bimc_int_clk)
+		devm_clk_put(&device->pdev->dev, pwr->gpu_bimc_int_clk);
+}
+
 int kgsl_pwrctrl_init(struct kgsl_device *device)
 {
 	int i, k, m, n = 0, result;
@@ -2029,7 +2065,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 
 	result = _get_clocks(device);
 	if (result)
-		return result;
+		goto error_cleanup_clks;
 
 	/* Make sure we have a source clk for freq setting */
 	if (pwr->grp_clks[0] == NULL)
@@ -2047,7 +2083,8 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 
 	if (pwr->num_pwrlevels == 0) {
 		KGSL_PWR_ERR(device, "No power levels are defined\n");
-		return -EINVAL;
+		result = -EINVAL;
+		goto error_cleanup_clks;
 	}
 
 	/* Initialize the user and thermal clock constraints */
@@ -2077,7 +2114,7 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 
 	result = get_regulators(device);
 	if (result)
-		return result;
+		goto error_cleanup_regulators;
 
 	pwr->power_flags = 0;
 
@@ -2097,8 +2134,10 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 			pwr->ocmem_pcl = msm_bus_scale_register_client
 					(ocmem_scale_table);
 
-		if (!pwr->ocmem_pcl)
-			return -EINVAL;
+		if (!pwr->ocmem_pcl) {
+			result = -EINVAL;
+			goto error_disable_pm;
+		}
 	}
 
 	/* Bus width in bytes, set it to zero if not found */
@@ -2128,14 +2167,18 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 		 * from the driver.
 		 */
 		pwr->pcl = msm_bus_scale_register_client(bus_scale_table);
-		if (pwr->pcl == 0)
-			return -EINVAL;
+		if (pwr->pcl == 0) {
+			result = -EINVAL;
+			goto error_cleanup_ocmem_pcl;
+		}
 	}
 
 	pwr->bus_ib = kzalloc(bus_scale_table->num_usecases *
 		sizeof(*pwr->bus_ib), GFP_KERNEL);
-	if (pwr->bus_ib == NULL)
-		return -ENOMEM;
+	if (pwr->bus_ib == NULL) {
+		result = -ENOMEM;
+		goto error_cleanup_pcl;
+	}
 
 	/*
 	 * Pull the BW vote out of the bus table.  They will be used to
@@ -2194,36 +2237,26 @@ int kgsl_pwrctrl_init(struct kgsl_device *device)
 		&pwr->tzone_name);
 
 	return result;
+
+error_cleanup_pcl:
+	_close_pcl(pwr);
+error_cleanup_ocmem_pcl:
+	_close_ocmem_pcl(pwr);
+error_disable_pm:
+	pm_runtime_disable(&pdev->dev);
+error_cleanup_regulators:
+	_close_regulators(pwr);
+error_cleanup_clks:
+	_close_clks(device);
+	return result;
 }
 
 void kgsl_pwrctrl_close(struct kgsl_device *device)
 {
 	struct kgsl_pwrctrl *pwr = &device->pwrctrl;
-	int i;
 
 	KGSL_PWR_INFO(device, "close device %d\n", device->id);
 
-	pm_runtime_disable(&device->pdev->dev);
-
-	if (pwr->pcl)
-		msm_bus_scale_unregister_client(pwr->pcl);
-
-	pwr->pcl = 0;
-
-	if (pwr->ocmem_pcl)
-		msm_bus_scale_unregister_client(pwr->ocmem_pcl);
-
-	pwr->ocmem_pcl = 0;
-
-	for (i = 0; i < KGSL_MAX_REGULATORS; i++)
-		pwr->regulators[i].reg = NULL;
-
-	for (i = 0; i < KGSL_MAX_REGULATORS; i++)
-		pwr->grp_clks[i] = NULL;
-
-	if (pwr->gpu_bimc_int_clk)
-		devm_clk_put(&device->pdev->dev, pwr->gpu_bimc_int_clk);
-
 	pwr->power_flags = 0;
 
 	if (!IS_ERR_OR_NULL(pwr->sysfs_pwr_limit)) {
@@ -2232,6 +2265,16 @@ void kgsl_pwrctrl_close(struct kgsl_device *device)
 		pwr->sysfs_pwr_limit = NULL;
 	}
 	kfree(pwr->bus_ib);
+
+	_close_pcl(pwr);
+
+	_close_ocmem_pcl(pwr);
+
+	pm_runtime_disable(&device->pdev->dev);
+
+	_close_regulators(pwr);
+
+	_close_clks(device);
 }
 
 /**
diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c
index 01aac1e..73e6c53 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.c
+++ b/drivers/gpu/msm/kgsl_sharedmem.c
@@ -613,25 +613,6 @@ int kgsl_cache_range_op(struct kgsl_memdesc *memdesc, uint64_t offset,
 }
 EXPORT_SYMBOL(kgsl_cache_range_op);
 
-#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
-static inline int get_page_size(size_t size, unsigned int align)
-{
-	if (align >= ilog2(SZ_1M) && size >= SZ_1M)
-		return SZ_1M;
-	else if (align >= ilog2(SZ_64K) && size >= SZ_64K)
-		return SZ_64K;
-	else if (align >= ilog2(SZ_8K) && size >= SZ_8K)
-		return SZ_8K;
-	else
-		return PAGE_SIZE;
-}
-#else
-static inline int get_page_size(size_t size, unsigned int align)
-{
-	return PAGE_SIZE;
-}
-#endif
-
 int
 kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
 			uint64_t size)
@@ -648,7 +629,7 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
 
 	align = (memdesc->flags & KGSL_MEMALIGN_MASK) >> KGSL_MEMALIGN_SHIFT;
 
-	page_size = get_page_size(size, align);
+	page_size = kgsl_get_page_size(size, align);
 
 	/*
 	 * The alignment cannot be less than the intended page size - it can be
@@ -719,7 +700,7 @@ kgsl_sharedmem_page_alloc_user(struct kgsl_memdesc *memdesc,
 		memdesc->page_count += page_count;
 
 		/* Get the needed page size for the next iteration */
-		page_size = get_page_size(len, align);
+		page_size = kgsl_get_page_size(len, align);
 	}
 
 	/* Call to the hypervisor to lock any secure buffer allocations */
diff --git a/drivers/gpu/msm/kgsl_sharedmem.h b/drivers/gpu/msm/kgsl_sharedmem.h
index 1ef31ef..10b37ae 100644
--- a/drivers/gpu/msm/kgsl_sharedmem.h
+++ b/drivers/gpu/msm/kgsl_sharedmem.h
@@ -365,4 +365,30 @@ static inline void kgsl_free_sgt(struct sg_table *sgt)
 	}
 }
 
+/**
+ * kgsl_get_page_size() - Get supported pagesize
+ * @size: Size of the page
+ * @align: Desired alignment of the size
+ *
+ * Return supported pagesize
+ */
+#ifndef CONFIG_ALLOC_BUFFERS_IN_4K_CHUNKS
+static inline int kgsl_get_page_size(size_t size, unsigned int align)
+{
+	if (align >= ilog2(SZ_1M) && size >= SZ_1M)
+		return SZ_1M;
+	else if (align >= ilog2(SZ_64K) && size >= SZ_64K)
+		return SZ_64K;
+	else if (align >= ilog2(SZ_8K) && size >= SZ_8K)
+		return SZ_8K;
+	else
+		return PAGE_SIZE;
+}
+#else
+static inline int kgsl_get_page_size(size_t size, unsigned int align)
+{
+	return PAGE_SIZE;
+}
+#endif
+
 #endif /* __KGSL_SHAREDMEM_H */
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 45cef3d..c8174f9 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1243,6 +1243,26 @@
 	  These devices are hard to detect and rarely found on mainstream
 	  hardware.  If unsure, say N.
 
+config SENSORS_QPNP_ADC_VOLTAGE
+	tristate "Support for Qualcomm Technologies Inc. QPNP Voltage ADC"
+	depends on SPMI
+	help
+	  This is the VADC arbiter driver for Qualcomm Technologies Inc. QPNP ADC Chip.
+
+	  The driver supports reading the HKADC, XOADC through the ADC AMUX arbiter.
+	  The VADC includes support for the conversion sequencer. The driver supports
+	  reading the ADC through the AMUX channels for external pull-ups simultaneously.
+
+config SENSORS_QPNP_ADC_CURRENT
+	tristate "Support for Qualcomm Technologies Inc. QPNP current ADC"
+	depends on SPMI
+	help
+	  This is the IADC driver for Qualcomm Technologies Inc. QPNP ADC Chip.
+
+	  The driver supports single mode operation to read from upto seven channel
+	  configuration that include reading the external/internal Rsense, CSP_EX,
+	  CSN_EX pair along with the gain and offset calibration.
+
 source drivers/hwmon/pmbus/Kconfig
 
 config SENSORS_PWM_FAN
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index aecf4ba..7b9a113 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -166,6 +166,8 @@
 obj-$(CONFIG_SENSORS_WM831X)	+= wm831x-hwmon.o
 obj-$(CONFIG_SENSORS_WM8350)	+= wm8350-hwmon.o
 obj-$(CONFIG_SENSORS_XGENE)	+= xgene-hwmon.o
+obj-$(CONFIG_SENSORS_QPNP_ADC_VOLTAGE)	+= qpnp-adc-voltage.o qpnp-adc-common.o
+obj-$(CONFIG_SENSORS_QPNP_ADC_CURRENT)	+= qpnp-adc-current.o qpnp-adc-common.o
 
 obj-$(CONFIG_PMBUS)		+= pmbus/
 
diff --git a/drivers/hwmon/qpnp-adc-common.c b/drivers/hwmon/qpnp-adc-common.c
new file mode 100644
index 0000000..33b760f
--- /dev/null
+++ b/drivers/hwmon/qpnp-adc-common.c
@@ -0,0 +1,2101 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+#define KELVINMIL_DEGMIL	273160
+#define QPNP_VADC_LDO_VOLTAGE_MIN	1800000
+#define QPNP_VADC_LDO_VOLTAGE_MAX	1800000
+#define QPNP_VADC_OK_VOLTAGE_MIN	1000000
+#define QPNP_VADC_OK_VOLTAGE_MAX	1000000
+#define PMI_CHG_SCALE_1		-138890
+#define PMI_CHG_SCALE_2		391750000000
+#define QPNP_VADC_HC_VREF_CODE		0x4000
+#define QPNP_VADC_HC_VDD_REFERENCE_MV	1875
+/* Clamp negative ADC code to 0 */
+#define QPNP_VADC_HC_MAX_CODE		0x7FFF
+
+/*
+ * Units for temperature below (on x axis) is in 0.1DegC as
+ * required by the battery driver. Note the resolution used
+ * here to compute the table was done for DegC to milli-volts.
+ * In consideration to limit the size of the table for the given
+ * temperature range below, the result is linearly interpolated
+ * and provided to the battery driver in the units desired for
+ * their framework which is 0.1DegC. True resolution of 0.1DegC
+ * will result in the below table size to increase by 10 times.
+ */
+static const struct qpnp_vadc_map_pt adcmap_btm_threshold[] = {
+	{-300,	1642},
+	{-200,	1544},
+	{-100,	1414},
+	{0,	1260},
+	{10,	1244},
+	{20,	1228},
+	{30,	1212},
+	{40,	1195},
+	{50,	1179},
+	{60,	1162},
+	{70,	1146},
+	{80,	1129},
+	{90,	1113},
+	{100,	1097},
+	{110,	1080},
+	{120,	1064},
+	{130,	1048},
+	{140,	1032},
+	{150,	1016},
+	{160,	1000},
+	{170,	985},
+	{180,	969},
+	{190,	954},
+	{200,	939},
+	{210,	924},
+	{220,	909},
+	{230,	894},
+	{240,	880},
+	{250,	866},
+	{260,	852},
+	{270,	838},
+	{280,	824},
+	{290,	811},
+	{300,	798},
+	{310,	785},
+	{320,	773},
+	{330,	760},
+	{340,	748},
+	{350,	736},
+	{360,	725},
+	{370,	713},
+	{380,	702},
+	{390,	691},
+	{400,	681},
+	{410,	670},
+	{420,	660},
+	{430,	650},
+	{440,	640},
+	{450,	631},
+	{460,	622},
+	{470,	613},
+	{480,	604},
+	{490,	595},
+	{500,	587},
+	{510,	579},
+	{520,	571},
+	{530,	563},
+	{540,	556},
+	{550,	548},
+	{560,	541},
+	{570,	534},
+	{580,	527},
+	{590,	521},
+	{600,	514},
+	{610,	508},
+	{620,	502},
+	{630,	496},
+	{640,	490},
+	{650,	485},
+	{660,	281},
+	{670,	274},
+	{680,	267},
+	{690,	260},
+	{700,	254},
+	{710,	247},
+	{720,	241},
+	{730,	235},
+	{740,	229},
+	{750,	224},
+	{760,	218},
+	{770,	213},
+	{780,	208},
+	{790,	203}
+};
+
+static const struct qpnp_vadc_map_pt adcmap_qrd_btm_threshold[] = {
+	{-200,	1540},
+	{-180,	1517},
+	{-160,	1492},
+	{-140,	1467},
+	{-120,	1440},
+	{-100,	1412},
+	{-80,	1383},
+	{-60,	1353},
+	{-40,	1323},
+	{-20,	1292},
+	{0,	1260},
+	{20,	1228},
+	{40,	1196},
+	{60,	1163},
+	{80,	1131},
+	{100,	1098},
+	{120,	1066},
+	{140,	1034},
+	{160,	1002},
+	{180,	971},
+	{200,	941},
+	{220,	911},
+	{240,	882},
+	{260,	854},
+	{280,	826},
+	{300,	800},
+	{320,	774},
+	{340,	749},
+	{360,	726},
+	{380,	703},
+	{400,	681},
+	{420,	660},
+	{440,	640},
+	{460,	621},
+	{480,	602},
+	{500,	585},
+	{520,	568},
+	{540,	552},
+	{560,	537},
+	{580,	523},
+	{600,	510},
+	{620,	497},
+	{640,	485},
+	{660,	473},
+	{680,	462},
+	{700,	452},
+	{720,	442},
+	{740,	433},
+	{760,	424},
+	{780,	416},
+	{800,	408},
+};
+
+static const struct qpnp_vadc_map_pt adcmap_qrd_skuaa_btm_threshold[] = {
+	{-200,	1476},
+	{-180,	1450},
+	{-160,	1422},
+	{-140,	1394},
+	{-120,	1365},
+	{-100,	1336},
+	{-80,	1306},
+	{-60,	1276},
+	{-40,	1246},
+	{-20,	1216},
+	{0,	1185},
+	{20,	1155},
+	{40,	1126},
+	{60,	1096},
+	{80,	1068},
+	{100,	1040},
+	{120,	1012},
+	{140,	986},
+	{160,	960},
+	{180,	935},
+	{200,	911},
+	{220,	888},
+	{240,	866},
+	{260,	844},
+	{280,	824},
+	{300,	805},
+	{320,	786},
+	{340,	769},
+	{360,	752},
+	{380,	737},
+	{400,	722},
+	{420,	707},
+	{440,	694},
+	{460,	681},
+	{480,	669},
+	{500,	658},
+	{520,	648},
+	{540,	637},
+	{560,	628},
+	{580,	619},
+	{600,	611},
+	{620,	603},
+	{640,	595},
+	{660,	588},
+	{680,	582},
+	{700,	575},
+	{720,	569},
+	{740,	564},
+	{760,	559},
+	{780,	554},
+	{800,	549},
+};
+
+static const struct qpnp_vadc_map_pt adcmap_qrd_skug_btm_threshold[] = {
+	{-200,	1338},
+	{-180,	1307},
+	{-160,	1276},
+	{-140,	1244},
+	{-120,	1213},
+	{-100,	1182},
+	{-80,	1151},
+	{-60,	1121},
+	{-40,	1092},
+	{-20,	1063},
+	{0,	1035},
+	{20,	1008},
+	{40,	982},
+	{60,	957},
+	{80,	933},
+	{100,	910},
+	{120,	889},
+	{140,	868},
+	{160,	848},
+	{180,	830},
+	{200,	812},
+	{220,	795},
+	{240,	780},
+	{260,	765},
+	{280,	751},
+	{300,	738},
+	{320,	726},
+	{340,	714},
+	{360,	704},
+	{380,	694},
+	{400,	684},
+	{420,	675},
+	{440,	667},
+	{460,	659},
+	{480,	652},
+	{500,	645},
+	{520,	639},
+	{540,	633},
+	{560,	627},
+	{580,	622},
+	{600,	617},
+	{620,	613},
+	{640,	608},
+	{660,	604},
+	{680,	600},
+	{700,	597},
+	{720,	593},
+	{740,	590},
+	{760,	587},
+	{780,	585},
+	{800,	582},
+};
+
+static const struct qpnp_vadc_map_pt adcmap_qrd_skuh_btm_threshold[] = {
+	{-200,	1531},
+	{-180,	1508},
+	{-160,	1483},
+	{-140,	1458},
+	{-120,	1432},
+	{-100,	1404},
+	{-80,	1377},
+	{-60,	1348},
+	{-40,	1319},
+	{-20,	1290},
+	{0,	1260},
+	{20,	1230},
+	{40,	1200},
+	{60,	1171},
+	{80,	1141},
+	{100,	1112},
+	{120,	1083},
+	{140,	1055},
+	{160,	1027},
+	{180,	1000},
+	{200,	973},
+	{220,	948},
+	{240,	923},
+	{260,	899},
+	{280,	876},
+	{300,	854},
+	{320,	832},
+	{340,	812},
+	{360,	792},
+	{380,	774},
+	{400,	756},
+	{420,	739},
+	{440,	723},
+	{460,	707},
+	{480,	692},
+	{500,	679},
+	{520,	665},
+	{540,	653},
+	{560,	641},
+	{580,	630},
+	{600,	619},
+	{620,	609},
+	{640,	600},
+	{660,	591},
+	{680,	583},
+	{700,	575},
+	{720,	567},
+	{740,	560},
+	{760,	553},
+	{780,	547},
+	{800,	541},
+	{820,	535},
+	{840,	530},
+	{860,	524},
+	{880,	520},
+};
+
+static const struct qpnp_vadc_map_pt adcmap_qrd_skut1_btm_threshold[] = {
+	{-400,	1759},
+	{-350,	1742},
+	{-300,	1720},
+	{-250,	1691},
+	{-200,	1654},
+	{-150,	1619},
+	{-100,	1556},
+	{-50,	1493},
+	{0,	1422},
+	{50,	1345},
+	{100,	1264},
+	{150,	1180},
+	{200,	1097},
+	{250,	1017},
+	{300,	942},
+	{350,	873},
+	{400,	810},
+	{450,	754},
+	{500,	706},
+	{550,	664},
+	{600,	627},
+	{650,	596},
+	{700,	570},
+	{750,	547},
+	{800,	528},
+	{850,	512},
+	{900,	499},
+	{950,	487},
+	{1000,	477},
+};
+
+/* Voltage to temperature */
+static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb[] = {
+	{1758,	-40},
+	{1742,	-35},
+	{1719,	-30},
+	{1691,	-25},
+	{1654,	-20},
+	{1608,	-15},
+	{1551,	-10},
+	{1483,	-5},
+	{1404,	0},
+	{1315,	5},
+	{1218,	10},
+	{1114,	15},
+	{1007,	20},
+	{900,	25},
+	{795,	30},
+	{696,	35},
+	{605,	40},
+	{522,	45},
+	{448,	50},
+	{383,	55},
+	{327,	60},
+	{278,	65},
+	{237,	70},
+	{202,	75},
+	{172,	80},
+	{146,	85},
+	{125,	90},
+	{107,	95},
+	{92,	100},
+	{79,	105},
+	{68,	110},
+	{59,	115},
+	{51,	120},
+	{44,	125}
+};
+
+/* Voltage to temperature */
+static const struct qpnp_vadc_map_pt adcmap_150k_104ef_104fb[] = {
+	{1738,	-40},
+	{1714,	-35},
+	{1682,	-30},
+	{1641,	-25},
+	{1589,	-20},
+	{1526,	-15},
+	{1451,	-10},
+	{1363,	-5},
+	{1266,	0},
+	{1159,	5},
+	{1048,	10},
+	{936,	15},
+	{825,	20},
+	{720,	25},
+	{622,	30},
+	{533,	35},
+	{454,	40},
+	{385,	45},
+	{326,	50},
+	{275,	55},
+	{232,	60},
+	{195,	65},
+	{165,	70},
+	{139,	75},
+	{118,	80},
+	{100,	85},
+	{85,	90},
+	{73,	95},
+	{62,	100},
+	{53,	105},
+	{46,	110},
+	{40,	115},
+	{34,	120},
+	{30,	125}
+};
+
+static const struct qpnp_vadc_map_pt adcmap_smb_batt_therm[] = {
+	{-300,	1625},
+	{-200,	1515},
+	{-100,	1368},
+	{0,	1192},
+	{10,	1173},
+	{20,	1154},
+	{30,	1135},
+	{40,	1116},
+	{50,	1097},
+	{60,	1078},
+	{70,	1059},
+	{80,	1040},
+	{90,	1020},
+	{100,	1001},
+	{110,	982},
+	{120,	963},
+	{130,	944},
+	{140,	925},
+	{150,	907},
+	{160,	888},
+	{170,	870},
+	{180,	851},
+	{190,	833},
+	{200,	815},
+	{210,	797},
+	{220,	780},
+	{230,	762},
+	{240,	745},
+	{250,	728},
+	{260,	711},
+	{270,	695},
+	{280,	679},
+	{290,	663},
+	{300,	647},
+	{310,	632},
+	{320,	616},
+	{330,	602},
+	{340,	587},
+	{350,	573},
+	{360,	559},
+	{370,	545},
+	{380,	531},
+	{390,	518},
+	{400,	505},
+	{410,	492},
+	{420,	480},
+	{430,	465},
+	{440,	456},
+	{450,	445},
+	{460,	433},
+	{470,	422},
+	{480,	412},
+	{490,	401},
+	{500,	391},
+	{510,	381},
+	{520,	371},
+	{530,	362},
+	{540,	352},
+	{550,	343},
+	{560,	335},
+	{570,	326},
+	{580,	318},
+	{590,	309},
+	{600,	302},
+	{610,	294},
+	{620,	286},
+	{630,	279},
+	{640,	272},
+	{650,	265},
+	{660,	258},
+	{670,	252},
+	{680,	245},
+	{690,	239},
+	{700,	233},
+	{710,	227},
+	{720,	221},
+	{730,	216},
+	{740,	211},
+	{750,	205},
+	{760,	200},
+	{770,	195},
+	{780,	190},
+	{790,	186}
+};
+
+/* Voltage to temperature */
+static const struct qpnp_vadc_map_pt adcmap_ncp03wf683[] = {
+	{1742,	-40},
+	{1718,	-35},
+	{1687,	-30},
+	{1647,	-25},
+	{1596,	-20},
+	{1534,	-15},
+	{1459,	-10},
+	{1372,	-5},
+	{1275,	0},
+	{1169,	5},
+	{1058,	10},
+	{945,	15},
+	{834,	20},
+	{729,	25},
+	{630,	30},
+	{541,	35},
+	{461,	40},
+	{392,	45},
+	{332,	50},
+	{280,	55},
+	{236,	60},
+	{199,	65},
+	{169,	70},
+	{142,	75},
+	{121,	80},
+	{102,	85},
+	{87,	90},
+	{74,	95},
+	{64,	100},
+	{55,	105},
+	{47,	110},
+	{40,	115},
+	{35,	120},
+	{30,	125}
+};
+
+/*
+ * Voltage to temperature table for 100k pull up for NTCG104EF104 with
+ * 1.875V reference.
+ */
+static const struct qpnp_vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
+	{ 1831,	-40 },
+	{ 1814,	-35 },
+	{ 1791,	-30 },
+	{ 1761,	-25 },
+	{ 1723,	-20 },
+	{ 1675,	-15 },
+	{ 1616,	-10 },
+	{ 1545,	-5 },
+	{ 1463,	0 },
+	{ 1370,	5 },
+	{ 1268,	10 },
+	{ 1160,	15 },
+	{ 1049,	20 },
+	{ 937,	25 },
+	{ 828,	30 },
+	{ 726,	35 },
+	{ 630,	40 },
+	{ 544,	45 },
+	{ 467,	50 },
+	{ 399,	55 },
+	{ 340,	60 },
+	{ 290,	65 },
+	{ 247,	70 },
+	{ 209,	75 },
+	{ 179,	80 },
+	{ 153,	85 },
+	{ 130,	90 },
+	{ 112,	95 },
+	{ 96,	100 },
+	{ 82,	105 },
+	{ 71,	110 },
+	{ 62,	115 },
+	{ 53,	120 },
+	{ 46,	125 },
+};
+
+static int32_t qpnp_adc_map_voltage_temp(const struct qpnp_vadc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if (pts == NULL)
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].x < pts[1].x)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].x < input)) {
+			/*
+			 * table entry is less than measured
+			 * value and table is descending, stop.
+			 */
+			break;
+		} else if ((descending == 0) &&
+				(pts[i].x > input)) {
+			/*
+			 * table entry is greater than measured
+			 * value and table is ascending, stop.
+			 */
+			break;
+		}
+		i++;
+	}
+
+	if (i == 0)
+		*output = pts[0].y;
+	else if (i == tablesize)
+		*output = pts[tablesize-1].y;
+	else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((int32_t) ((pts[i].y - pts[i-1].y)*
+			(input - pts[i-1].x))/
+			(pts[i].x - pts[i-1].x))+
+			pts[i-1].y);
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_adc_map_temp_voltage(const struct qpnp_vadc_map_pt *pts,
+		uint32_t tablesize, int32_t input, int64_t *output)
+{
+	bool descending = 1;
+	uint32_t i = 0;
+
+	if (pts == NULL)
+		return -EINVAL;
+
+	/* Check if table is descending or ascending */
+	if (tablesize > 1) {
+		if (pts[0].y < pts[1].y)
+			descending = 0;
+	}
+
+	while (i < tablesize) {
+		if ((descending == 1) && (pts[i].y < input)) {
+			/* Table entry is less than measured value. */
+			/* Table is descending, stop. */
+			break;
+		} else if ((descending == 0) && (pts[i].y > input)) {
+			/* Table entry is greater than measured value. */
+			/* Table is ascending, stop. */
+			break;
+		}
+		i++;
+	}
+
+	if (i == 0) {
+		*output = pts[0].x;
+	} else if (i == tablesize) {
+		*output = pts[tablesize-1].x;
+	} else {
+		/* result is between search_index and search_index-1 */
+		/* interpolate linearly */
+		*output = (((int32_t) ((pts[i].x - pts[i-1].x)*
+			(input - pts[i-1].y))/
+			(pts[i].y - pts[i-1].y))+
+			pts[i-1].x);
+	}
+
+	return 0;
+}
+
+static void qpnp_adc_scale_with_calib_param(int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		int64_t *scale_voltage)
+{
+	*scale_voltage = (adc_code -
+		chan_properties->adc_graph[chan_properties->calib_type].adc_gnd)
+		* chan_properties->adc_graph[chan_properties->calib_type].dx;
+	*scale_voltage = div64_s64(*scale_voltage,
+		chan_properties->adc_graph[chan_properties->calib_type].dy);
+
+	if (chan_properties->calib_type == CALIB_ABSOLUTE)
+		*scale_voltage +=
+		chan_properties->adc_graph[chan_properties->calib_type].dx;
+
+	if (*scale_voltage < 0)
+		*scale_voltage = 0;
+}
+
+int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t pmic_voltage = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	if (adc_properties->adc_hc) {
+		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+		if (adc_code > QPNP_VADC_HC_MAX_CODE)
+			adc_code = 0;
+		pmic_voltage = (int64_t) adc_code;
+		pmic_voltage *= (int64_t) (adc_properties->adc_vdd_reference
+							* 1000);
+		pmic_voltage = div64_s64(pmic_voltage,
+					QPNP_VADC_HC_VREF_CODE);
+	} else {
+		if (!chan_properties->adc_graph[CALIB_ABSOLUTE].dy)
+			return -EINVAL;
+		qpnp_adc_scale_with_calib_param(adc_code, adc_properties,
+					chan_properties, &pmic_voltage);
+	}
+
+	if (pmic_voltage > 0) {
+		/* 2mV/K */
+		adc_chan_result->measurement = pmic_voltage*
+			chan_properties->offset_gain_denominator;
+
+		do_div(adc_chan_result->measurement,
+			chan_properties->offset_gain_numerator * 2);
+	} else
+		adc_chan_result->measurement = 0;
+
+	/* Change to .001 deg C */
+	adc_chan_result->measurement -= KELVINMIL_DEGMIL;
+	adc_chan_result->physical = (int32_t) adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_pmic_therm);
+
+int32_t qpnp_adc_scale_millidegc_pmic_voltage_thr(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph btm_param;
+	int64_t low_output = 0, high_output = 0;
+	int rc = 0, sign = 0;
+
+	/* Convert to Kelvin and account for voltage to be written as 2mV/K */
+	low_output = (param->low_temp + KELVINMIL_DEGMIL) * 2;
+	/* Convert to Kelvin and account for voltage to be written as 2mV/K */
+	high_output = (param->high_temp + KELVINMIL_DEGMIL) * 2;
+
+	if (param->adc_tm_hc) {
+		low_output *= QPNP_VADC_HC_VREF_CODE;
+		do_div(low_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		high_output *= QPNP_VADC_HC_VREF_CODE;
+		do_div(high_output, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+	} else {
+		rc = qpnp_get_vadc_gain_and_offset(chip, &btm_param,
+							CALIB_ABSOLUTE);
+		if (rc < 0) {
+		pr_err("Could not acquire gain and offset\n");
+		return rc;
+		}
+
+		/* Convert to voltage threshold */
+		low_output = (low_output - QPNP_ADC_625_UV) * btm_param.dy;
+		if (low_output < 0) {
+			sign = 1;
+			low_output = -low_output;
+		}
+		do_div(low_output, QPNP_ADC_625_UV);
+		if (sign)
+			low_output = -low_output;
+		low_output += btm_param.adc_gnd;
+
+		sign = 0;
+		/* Convert to voltage threshold */
+		high_output = (high_output - QPNP_ADC_625_UV) * btm_param.dy;
+		if (high_output < 0) {
+			sign = 1;
+			high_output = -high_output;
+		}
+		do_div(high_output, QPNP_ADC_625_UV);
+		if (sign)
+			high_output = -high_output;
+		high_output += btm_param.adc_gnd;
+	}
+
+	*low_threshold = (uint32_t) low_output;
+	*high_threshold = (uint32_t) high_output;
+
+	pr_debug("high_temp:%d, low_temp:%d\n", param->high_temp,
+				param->low_temp);
+	pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold,
+				*low_threshold);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_millidegc_pmic_voltage_thr);
+
+/* Scales the ADC code to degC using the mapping
+ * table for the XO thermistor.
+ */
+int32_t qpnp_adc_tdkntcg_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t xo_thm_voltage = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	if (adc_properties->adc_hc) {
+		/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+		if (adc_code > QPNP_VADC_HC_MAX_CODE)
+			adc_code = 0;
+		xo_thm_voltage = (int64_t) adc_code;
+		xo_thm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
+							* 1000);
+		xo_thm_voltage = div64_s64(xo_thm_voltage,
+					QPNP_VADC_HC_VREF_CODE * 1000);
+		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+			xo_thm_voltage, &adc_chan_result->physical);
+	} else {
+		qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &xo_thm_voltage);
+
+		if (chan_properties->calib_type == CALIB_ABSOLUTE)
+			do_div(xo_thm_voltage, 1000);
+
+		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb),
+			xo_thm_voltage, &adc_chan_result->physical);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_tdkntcg_therm);
+
+int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+
+	adc_chan_result->measurement = bat_voltage;
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_btm_threshold,
+			ARRAY_SIZE(adcmap_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_batt_therm);
+
+int32_t qpnp_adc_scale_qrd_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+
+	adc_chan_result->measurement = bat_voltage;
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_qrd_btm_threshold,
+			ARRAY_SIZE(adcmap_qrd_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_batt_therm);
+
+int32_t qpnp_adc_scale_qrd_skuaa_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+
+	adc_chan_result->measurement = bat_voltage;
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_qrd_skuaa_btm_threshold,
+			ARRAY_SIZE(adcmap_qrd_skuaa_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_skuaa_batt_therm);
+
+int32_t qpnp_adc_scale_qrd_skug_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+	adc_chan_result->measurement = bat_voltage;
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_qrd_skug_btm_threshold,
+			ARRAY_SIZE(adcmap_qrd_skug_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_skug_batt_therm);
+
+int32_t qpnp_adc_scale_qrd_skuh_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_qrd_skuh_btm_threshold,
+			ARRAY_SIZE(adcmap_qrd_skuh_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_skuh_batt_therm);
+
+int32_t qpnp_adc_scale_qrd_skut1_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_qrd_skut1_btm_threshold,
+			ARRAY_SIZE(adcmap_qrd_skut1_btm_threshold),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_qrd_skut1_batt_therm);
+
+int32_t qpnp_adc_scale_smb_batt_therm(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t bat_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &bat_voltage);
+
+	return qpnp_adc_map_temp_voltage(
+			adcmap_smb_batt_therm,
+			ARRAY_SIZE(adcmap_smb_batt_therm),
+			bat_voltage,
+			&adc_chan_result->physical);
+}
+EXPORT_SYMBOL(qpnp_adc_scale_smb_batt_therm);
+
+int32_t qpnp_adc_scale_therm_pu1(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t therm_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &therm_voltage);
+
+	qpnp_adc_map_voltage_temp(adcmap_150k_104ef_104fb,
+		ARRAY_SIZE(adcmap_150k_104ef_104fb),
+		therm_voltage, &adc_chan_result->physical);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_therm_pu1);
+
+int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t therm_voltage = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties)
+		return -EINVAL;
+
+	if (adc_properties->adc_hc) {
+		/* (ADC code * vref_vadc (1.875V) * 1000) / (0x4000 * 1000) */
+		if (adc_code > QPNP_VADC_HC_MAX_CODE)
+			adc_code = 0;
+		therm_voltage = (int64_t) adc_code;
+		therm_voltage *= (int64_t) (adc_properties->adc_vdd_reference
+							* 1000);
+		therm_voltage = div64_s64(therm_voltage,
+					(QPNP_VADC_HC_VREF_CODE * 1000));
+
+		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+			therm_voltage, &adc_chan_result->physical);
+	} else {
+		qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &therm_voltage);
+
+		if (chan_properties->calib_type == CALIB_ABSOLUTE)
+			do_div(therm_voltage, 1000);
+
+		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb),
+			therm_voltage, &adc_chan_result->physical);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_therm_pu2);
+
+int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *chip,
+		const struct qpnp_adc_properties *adc_properties,
+					uint32_t reg, int64_t *result)
+{
+	int64_t adc_voltage = 0;
+	struct qpnp_vadc_linear_graph param1;
+	int negative_offset = 0;
+
+	if (adc_properties->adc_hc) {
+		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+		if (reg > QPNP_VADC_HC_MAX_CODE)
+			reg = 0;
+		adc_voltage = (int64_t) reg;
+		adc_voltage *= QPNP_VADC_HC_VDD_REFERENCE_MV;
+		adc_voltage = div64_s64(adc_voltage,
+					QPNP_VADC_HC_VREF_CODE);
+		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+			adc_voltage, result);
+	} else {
+		qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
+
+		adc_voltage = (reg - param1.adc_gnd) * param1.adc_vref;
+		if (adc_voltage < 0) {
+			negative_offset = 1;
+			adc_voltage = -adc_voltage;
+		}
+
+		do_div(adc_voltage, param1.dy);
+
+		qpnp_adc_map_voltage_temp(adcmap_100k_104ef_104fb,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb),
+			adc_voltage, result);
+		if (negative_offset)
+			adc_voltage = -adc_voltage;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_scale_voltage_therm_pu2);
+
+int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *chip,
+			const struct qpnp_adc_properties *adc_properties,
+				struct qpnp_adc_tm_config *param)
+{
+	struct qpnp_vadc_linear_graph param1;
+	int rc;
+
+	if (adc_properties->adc_hc) {
+		rc = qpnp_adc_map_temp_voltage(
+			adcmap_100k_104ef_104fb_1875_vref,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+			param->low_thr_temp, &param->low_thr_voltage);
+		if (rc)
+			return rc;
+		param->low_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
+		do_div(param->low_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+
+		rc = qpnp_adc_map_temp_voltage(
+			adcmap_100k_104ef_104fb_1875_vref,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+			param->high_thr_temp, &param->high_thr_voltage);
+		if (rc)
+			return rc;
+		param->high_thr_voltage *= QPNP_VADC_HC_VREF_CODE;
+		do_div(param->high_thr_voltage, QPNP_VADC_HC_VDD_REFERENCE_MV);
+	} else {
+		qpnp_get_vadc_gain_and_offset(chip, &param1, CALIB_RATIOMETRIC);
+
+		rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb),
+			param->low_thr_temp, &param->low_thr_voltage);
+		if (rc)
+			return rc;
+
+		param->low_thr_voltage *= param1.dy;
+		do_div(param->low_thr_voltage, param1.adc_vref);
+		param->low_thr_voltage += param1.adc_gnd;
+
+		rc = qpnp_adc_map_temp_voltage(adcmap_100k_104ef_104fb,
+			ARRAY_SIZE(adcmap_100k_104ef_104fb),
+			param->high_thr_temp, &param->high_thr_voltage);
+		if (rc)
+			return rc;
+
+		param->high_thr_voltage *= param1.dy;
+		do_div(param->high_thr_voltage, param1.adc_vref);
+		param->high_thr_voltage += param1.adc_gnd;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_scale_therm_voltage_pu2);
+
+int32_t qpnp_adc_scale_therm_ncp03(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t therm_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &therm_voltage);
+
+	qpnp_adc_map_voltage_temp(adcmap_ncp03wf683,
+		ARRAY_SIZE(adcmap_ncp03wf683),
+		therm_voltage, &adc_chan_result->physical);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_therm_ncp03);
+
+int32_t qpnp_adc_scale_batt_id(struct qpnp_vadc_chip *chip,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t batt_id_voltage = 0;
+
+	qpnp_adc_scale_with_calib_param(adc_code,
+			adc_properties, chan_properties, &batt_id_voltage);
+
+	adc_chan_result->physical = batt_id_voltage;
+	adc_chan_result->physical = adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_batt_id);
+
+int32_t qpnp_adc_scale_default(struct qpnp_vadc_chip *vadc,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int64_t scale_voltage = 0;
+
+	if (!chan_properties || !chan_properties->offset_gain_numerator ||
+		!chan_properties->offset_gain_denominator || !adc_properties
+		|| !adc_chan_result)
+		return -EINVAL;
+
+	if (adc_properties->adc_hc) {
+		/* (ADC code * vref_vadc (1.875V)) / 0x4000 */
+		if (adc_code > QPNP_VADC_HC_MAX_CODE)
+			adc_code = 0;
+		scale_voltage = (int64_t) adc_code;
+		scale_voltage *= (adc_properties->adc_vdd_reference * 1000);
+		scale_voltage = div64_s64(scale_voltage,
+						QPNP_VADC_HC_VREF_CODE);
+	} else {
+		qpnp_adc_scale_with_calib_param(adc_code, adc_properties,
+					chan_properties, &scale_voltage);
+		if (!chan_properties->calib_type == CALIB_ABSOLUTE)
+			scale_voltage *= 1000;
+	}
+
+
+	scale_voltage *= chan_properties->offset_gain_denominator;
+	scale_voltage = div64_s64(scale_voltage,
+				chan_properties->offset_gain_numerator);
+	adc_chan_result->measurement = scale_voltage;
+	/*
+	 * Note: adc_chan_result->measurement is in the unit of
+	 * adc_properties.adc_reference. For generic channel processing,
+	 * channel measurement is a scale/ratio relative to the adc
+	 * reference input
+	 */
+	adc_chan_result->physical = adc_chan_result->measurement;
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_default);
+
+int32_t qpnp_adc_usb_scaler(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph usb_param;
+
+	qpnp_get_vadc_gain_and_offset(chip, &usb_param, CALIB_RATIOMETRIC);
+
+	*low_threshold = param->low_thr * usb_param.dy;
+	do_div(*low_threshold, usb_param.adc_vref);
+	*low_threshold += usb_param.adc_gnd;
+
+	*high_threshold = param->high_thr * usb_param.dy;
+	do_div(*high_threshold, usb_param.adc_vref);
+	*high_threshold += usb_param.adc_gnd;
+
+	pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
+				param->low_thr);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_usb_scaler);
+
+int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph vbatt_param;
+	int rc = 0, sign = 0;
+	int64_t low_thr = 0, high_thr = 0;
+
+	if (param->adc_tm_hc) {
+		low_thr = (param->low_thr/param->gain_den);
+		low_thr *= param->gain_num;
+		low_thr *= QPNP_VADC_HC_VREF_CODE;
+		do_div(low_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		*low_threshold = low_thr;
+
+		high_thr = (param->high_thr/param->gain_den);
+		high_thr *= param->gain_num;
+		high_thr *= QPNP_VADC_HC_VREF_CODE;
+		do_div(high_thr, (QPNP_VADC_HC_VDD_REFERENCE_MV * 1000));
+		*high_threshold = high_thr;
+	} else {
+		rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param,
+							CALIB_ABSOLUTE);
+		if (rc < 0)
+			return rc;
+
+		low_thr = (((param->low_thr/param->gain_den) -
+				QPNP_ADC_625_UV) * vbatt_param.dy);
+		if (low_thr < 0) {
+			sign = 1;
+			low_thr = -low_thr;
+		}
+		low_thr = low_thr * param->gain_num;
+		do_div(low_thr, QPNP_ADC_625_UV);
+		if (sign)
+			low_thr = -low_thr;
+		*low_threshold = low_thr + vbatt_param.adc_gnd;
+
+		sign = 0;
+		high_thr = (((param->high_thr/param->gain_den) -
+				QPNP_ADC_625_UV) * vbatt_param.dy);
+		if (high_thr < 0) {
+			sign = 1;
+			high_thr = -high_thr;
+		}
+		high_thr = high_thr * param->gain_num;
+		do_div(high_thr, QPNP_ADC_625_UV);
+		if (sign)
+			high_thr = -high_thr;
+		*high_threshold = high_thr + vbatt_param.adc_gnd;
+	}
+
+	pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
+				param->low_thr);
+	pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold,
+				*low_threshold);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_absolute_rthr);
+
+int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	return qpnp_adc_absolute_rthr(chip, param, low_threshold,
+							high_threshold);
+}
+EXPORT_SYMBOL(qpnp_adc_vbatt_rscaler);
+
+int32_t qpnp_vadc_absolute_rthr(struct qpnp_vadc_chip *chip,
+		const struct qpnp_vadc_chan_properties *chan_prop,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph vbatt_param;
+	int rc = 0, sign = 0;
+	int64_t low_thr = 0, high_thr = 0;
+
+	if (!chan_prop || !chan_prop->offset_gain_numerator ||
+		!chan_prop->offset_gain_denominator)
+		return -EINVAL;
+
+	rc = qpnp_get_vadc_gain_and_offset(chip, &vbatt_param, CALIB_ABSOLUTE);
+	if (rc < 0)
+		return rc;
+
+	low_thr = (((param->low_thr)/(int)chan_prop->offset_gain_denominator
+					- QPNP_ADC_625_UV) * vbatt_param.dy);
+	if (low_thr < 0) {
+		sign = 1;
+		low_thr = -low_thr;
+	}
+	low_thr = low_thr * chan_prop->offset_gain_numerator;
+	do_div(low_thr, QPNP_ADC_625_UV);
+	if (sign)
+		low_thr = -low_thr;
+	*low_threshold = low_thr + vbatt_param.adc_gnd;
+
+	sign = 0;
+	high_thr = (((param->high_thr)/(int)chan_prop->offset_gain_denominator
+					- QPNP_ADC_625_UV) * vbatt_param.dy);
+	if (high_thr < 0) {
+		sign = 1;
+		high_thr = -high_thr;
+	}
+	high_thr = high_thr * chan_prop->offset_gain_numerator;
+	do_div(high_thr, QPNP_ADC_625_UV);
+	if (sign)
+		high_thr = -high_thr;
+	*high_threshold = high_thr + vbatt_param.adc_gnd;
+
+	pr_debug("high_volt:%d, low_volt:%d\n", param->high_thr,
+				param->low_thr);
+	pr_debug("adc_code_high:%x, adc_code_low:%x\n", *high_threshold,
+				*low_threshold);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_vadc_absolute_rthr);
+
+int32_t qpnp_adc_btm_scaler(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph btm_param;
+	int64_t low_output = 0, high_output = 0;
+	int rc = 0;
+
+	if (param->adc_tm_hc) {
+		pr_err("Update scaling for VADC_TM_HC\n");
+		return -EINVAL;
+	}
+
+	qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
+
+	pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
+				param->low_temp);
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		(param->low_temp),
+		&low_output);
+	if (rc) {
+		pr_debug("low_temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("low_output:%lld\n", low_output);
+	low_output *= btm_param.dy;
+	do_div(low_output, btm_param.adc_vref);
+	low_output += btm_param.adc_gnd;
+
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_btm_threshold,
+		ARRAY_SIZE(adcmap_btm_threshold),
+		(param->high_temp),
+		&high_output);
+	if (rc) {
+		pr_debug("high temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("high_output:%lld\n", high_output);
+	high_output *= btm_param.dy;
+	do_div(high_output, btm_param.adc_vref);
+	high_output += btm_param.adc_gnd;
+
+	/* btm low temperature correspondes to high voltage threshold */
+	*low_threshold = high_output;
+	/* btm high temperature correspondes to low voltage threshold */
+	*high_threshold = low_output;
+
+	pr_debug("high_volt:%d, low_volt:%d\n", *high_threshold,
+				*low_threshold);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_btm_scaler);
+
+int32_t qpnp_adc_qrd_skuh_btm_scaler(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph btm_param;
+	int64_t low_output = 0, high_output = 0;
+	int rc = 0;
+
+	if (param->adc_tm_hc) {
+		pr_err("Update scaling for VADC_TM_HC\n");
+		return -EINVAL;
+	}
+
+	qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
+
+	pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
+				param->low_temp);
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_qrd_skuh_btm_threshold,
+		ARRAY_SIZE(adcmap_qrd_skuh_btm_threshold),
+		(param->low_temp),
+		&low_output);
+	if (rc) {
+		pr_debug("low_temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("low_output:%lld\n", low_output);
+	low_output *= btm_param.dy;
+	do_div(low_output, btm_param.adc_vref);
+	low_output += btm_param.adc_gnd;
+
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_qrd_skuh_btm_threshold,
+		ARRAY_SIZE(adcmap_qrd_skuh_btm_threshold),
+		(param->high_temp),
+		&high_output);
+	if (rc) {
+		pr_debug("high temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("high_output:%lld\n", high_output);
+	high_output *= btm_param.dy;
+	do_div(high_output, btm_param.adc_vref);
+	high_output += btm_param.adc_gnd;
+
+	/* btm low temperature correspondes to high voltage threshold */
+	*low_threshold = high_output;
+	/* btm high temperature correspondes to low voltage threshold */
+	*high_threshold = low_output;
+
+	pr_debug("high_volt:%d, low_volt:%d\n", *high_threshold,
+				*low_threshold);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_qrd_skuh_btm_scaler);
+
+int32_t qpnp_adc_qrd_skut1_btm_scaler(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph btm_param;
+	int64_t low_output = 0, high_output = 0;
+	int rc = 0;
+
+	if (param->adc_tm_hc) {
+		pr_err("Update scaling for VADC_TM_HC\n");
+		return -EINVAL;
+	}
+
+	qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
+
+	pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
+				param->low_temp);
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_qrd_skut1_btm_threshold,
+		ARRAY_SIZE(adcmap_qrd_skut1_btm_threshold),
+		(param->low_temp),
+		&low_output);
+	if (rc) {
+		pr_debug("low_temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("low_output:%lld\n", low_output);
+	low_output *= btm_param.dy;
+	do_div(low_output, btm_param.adc_vref);
+	low_output += btm_param.adc_gnd;
+
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_qrd_skut1_btm_threshold,
+		ARRAY_SIZE(adcmap_qrd_skut1_btm_threshold),
+		(param->high_temp),
+		&high_output);
+	if (rc) {
+		pr_debug("high temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("high_output:%lld\n", high_output);
+	high_output *= btm_param.dy;
+	do_div(high_output, btm_param.adc_vref);
+	high_output += btm_param.adc_gnd;
+
+	/* btm low temperature correspondes to high voltage threshold */
+	*low_threshold = high_output;
+	/* btm high temperature correspondes to low voltage threshold */
+	*high_threshold = low_output;
+
+	pr_debug("high_volt:%d, low_volt:%d\n", *high_threshold,
+				*low_threshold);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_qrd_skut1_btm_scaler);
+
+int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *chip,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{
+	struct qpnp_vadc_linear_graph btm_param;
+	int64_t low_output = 0, high_output = 0;
+	int rc = 0;
+
+	if (param->adc_tm_hc) {
+		pr_err("Update scaling for VADC_TM_HC\n");
+		return -EINVAL;
+	}
+
+	qpnp_get_vadc_gain_and_offset(chip, &btm_param, CALIB_RATIOMETRIC);
+
+	pr_debug("warm_temp:%d and cool_temp:%d\n", param->high_temp,
+				param->low_temp);
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_smb_batt_therm,
+		ARRAY_SIZE(adcmap_smb_batt_therm),
+		(param->low_temp),
+		&low_output);
+	if (rc) {
+		pr_debug("low_temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("low_output:%lld\n", low_output);
+	low_output *= btm_param.dy;
+	do_div(low_output, btm_param.adc_vref);
+	low_output += btm_param.adc_gnd;
+
+	rc = qpnp_adc_map_voltage_temp(
+		adcmap_smb_batt_therm,
+		ARRAY_SIZE(adcmap_smb_batt_therm),
+		(param->high_temp),
+		&high_output);
+	if (rc) {
+		pr_debug("high temp mapping failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("high_output:%lld\n", high_output);
+	high_output *= btm_param.dy;
+	do_div(high_output, btm_param.adc_vref);
+	high_output += btm_param.adc_gnd;
+
+	/* btm low temperature correspondes to high voltage threshold */
+	*low_threshold = high_output;
+	/* btm high temperature correspondes to low voltage threshold */
+	*high_threshold = low_output;
+
+	pr_debug("high_volt:%d, low_volt:%d\n", *high_threshold,
+				*low_threshold);
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_smb_btm_rscaler);
+
+int32_t qpnp_adc_scale_pmi_chg_temp(struct qpnp_vadc_chip *vadc,
+		int32_t adc_code,
+		const struct qpnp_adc_properties *adc_properties,
+		const struct qpnp_vadc_chan_properties *chan_properties,
+		struct qpnp_vadc_result *adc_chan_result)
+{
+	int rc = 0;
+
+	rc = qpnp_adc_scale_default(vadc, adc_code, adc_properties,
+			chan_properties, adc_chan_result);
+	if (rc < 0)
+		return rc;
+
+	pr_debug("raw_code:%x, v_adc:%lld\n", adc_code,
+						adc_chan_result->physical);
+	adc_chan_result->physical = (int64_t) ((PMI_CHG_SCALE_1) *
+					(adc_chan_result->physical * 2));
+	adc_chan_result->physical = (int64_t) (adc_chan_result->physical +
+							PMI_CHG_SCALE_2);
+	adc_chan_result->physical = (int64_t) adc_chan_result->physical;
+	adc_chan_result->physical = div64_s64(adc_chan_result->physical,
+								1000000);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_scale_pmi_chg_temp);
+
+int32_t qpnp_adc_enable_voltage(struct qpnp_adc_drv *adc)
+{
+	int rc = 0;
+
+	if (adc->hkadc_ldo) {
+		rc = regulator_enable(adc->hkadc_ldo);
+		if (rc < 0) {
+			pr_err("Failed to enable hkadc ldo\n");
+			return rc;
+		}
+	}
+
+	if (adc->hkadc_ldo_ok) {
+		rc = regulator_enable(adc->hkadc_ldo_ok);
+		if (rc < 0) {
+			pr_err("Failed to enable hkadc ok signal\n");
+			return rc;
+		}
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_enable_voltage);
+
+void qpnp_adc_disable_voltage(struct qpnp_adc_drv *adc)
+{
+	if (adc->hkadc_ldo)
+		regulator_disable(adc->hkadc_ldo);
+
+	if (adc->hkadc_ldo_ok)
+		regulator_disable(adc->hkadc_ldo_ok);
+
+}
+EXPORT_SYMBOL(qpnp_adc_disable_voltage);
+
+void qpnp_adc_free_voltage_resource(struct qpnp_adc_drv *adc)
+{
+	if (adc->hkadc_ldo)
+		regulator_put(adc->hkadc_ldo);
+
+	if (adc->hkadc_ldo_ok)
+		regulator_put(adc->hkadc_ldo_ok);
+}
+EXPORT_SYMBOL(qpnp_adc_free_voltage_resource);
+
+int qpnp_adc_get_revid_version(struct device *dev)
+{
+	struct pmic_revid_data *revid_data;
+	struct device_node *revid_dev_node;
+
+	revid_dev_node = of_parse_phandle(dev->of_node,
+						"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		pr_debug("Missing qcom,pmic-revid property\n");
+		return -EINVAL;
+	}
+
+	revid_data = get_revid_data(revid_dev_node);
+	if (IS_ERR(revid_data)) {
+		pr_debug("revid error rc = %ld\n", PTR_ERR(revid_data));
+		return -EINVAL;
+	}
+
+	if (!revid_data)
+		return -EINVAL;
+
+	if ((revid_data->rev1 == PM8941_V3P1_REV1) &&
+		(revid_data->rev2 == PM8941_V3P1_REV2) &&
+		(revid_data->rev3 == PM8941_V3P1_REV3) &&
+		(revid_data->rev4 == PM8941_V3P1_REV4) &&
+		(revid_data->pmic_subtype == PM8941_SUBTYPE))
+		return QPNP_REV_ID_8941_3_1;
+	else if ((revid_data->rev1 == PM8941_V3P0_REV1) &&
+		(revid_data->rev2 == PM8941_V3P0_REV2) &&
+		(revid_data->rev3 == PM8941_V3P0_REV3) &&
+		(revid_data->rev4 == PM8941_V3P0_REV4) &&
+		(revid_data->pmic_subtype == PM8941_SUBTYPE))
+		return QPNP_REV_ID_8941_3_0;
+	else if ((revid_data->rev1 == PM8941_V2P0_REV1) &&
+		(revid_data->rev2 == PM8941_V2P0_REV2) &&
+		(revid_data->rev3 == PM8941_V2P0_REV3) &&
+		(revid_data->rev4 == PM8941_V2P0_REV4) &&
+		(revid_data->pmic_subtype == PM8941_SUBTYPE))
+		return QPNP_REV_ID_8941_2_0;
+	else if ((revid_data->rev1 == PM8226_V2P2_REV1) &&
+		(revid_data->rev2 == PM8226_V2P2_REV2) &&
+		(revid_data->rev3 == PM8226_V2P2_REV3) &&
+		(revid_data->rev4 == PM8226_V2P2_REV4) &&
+		(revid_data->pmic_subtype == PM8226_SUBTYPE))
+		return QPNP_REV_ID_8026_2_2;
+	else if ((revid_data->rev1 == PM8226_V2P1_REV1) &&
+		(revid_data->rev2 == PM8226_V2P1_REV2) &&
+		(revid_data->rev3 == PM8226_V2P1_REV3) &&
+		(revid_data->rev4 == PM8226_V2P1_REV4) &&
+		(revid_data->pmic_subtype == PM8226_SUBTYPE))
+		return QPNP_REV_ID_8026_2_1;
+	else if ((revid_data->rev1 == PM8226_V2P0_REV1) &&
+		(revid_data->rev2 == PM8226_V2P0_REV2) &&
+		(revid_data->rev3 == PM8226_V2P0_REV3) &&
+		(revid_data->rev4 == PM8226_V2P0_REV4) &&
+		(revid_data->pmic_subtype == PM8226_SUBTYPE))
+		return QPNP_REV_ID_8026_2_0;
+	else if ((revid_data->rev1 == PM8226_V1P0_REV1) &&
+		(revid_data->rev2 == PM8226_V1P0_REV2) &&
+		(revid_data->rev3 == PM8226_V1P0_REV3) &&
+		(revid_data->rev4 == PM8226_V1P0_REV4) &&
+		(revid_data->pmic_subtype == PM8226_SUBTYPE))
+		return QPNP_REV_ID_8026_1_0;
+	else if ((revid_data->rev1 == PM8110_V1P0_REV1) &&
+		(revid_data->rev2 == PM8110_V1P0_REV2) &&
+		(revid_data->rev3 == PM8110_V1P0_REV3) &&
+		(revid_data->rev4 == PM8110_V1P0_REV4) &&
+		(revid_data->pmic_subtype == PM8110_SUBTYPE))
+		return QPNP_REV_ID_8110_1_0;
+	else if ((revid_data->rev1 == PM8110_V2P0_REV1) &&
+		(revid_data->rev2 == PM8110_V2P0_REV2) &&
+		(revid_data->rev3 == PM8110_V2P0_REV3) &&
+		(revid_data->rev4 == PM8110_V2P0_REV4) &&
+		(revid_data->pmic_subtype == PM8110_SUBTYPE))
+		return QPNP_REV_ID_8110_2_0;
+	else if ((revid_data->rev1 == PM8916_V1P0_REV1) &&
+		(revid_data->rev2 == PM8916_V1P0_REV2) &&
+		(revid_data->rev3 == PM8916_V1P0_REV3) &&
+		(revid_data->rev4 == PM8916_V1P0_REV4) &&
+		(revid_data->pmic_subtype == PM8916_SUBTYPE))
+		return QPNP_REV_ID_8916_1_0;
+	else if ((revid_data->rev1 == PM8916_V1P1_REV1) &&
+		(revid_data->rev2 == PM8916_V1P1_REV2) &&
+		(revid_data->rev3 == PM8916_V1P1_REV3) &&
+		(revid_data->rev4 == PM8916_V1P1_REV4) &&
+		(revid_data->pmic_subtype == PM8916_SUBTYPE))
+		return QPNP_REV_ID_8916_1_1;
+	else if ((revid_data->rev1 == PM8916_V2P0_REV1) &&
+		(revid_data->rev2 == PM8916_V2P0_REV2) &&
+		(revid_data->rev3 == PM8916_V2P0_REV3) &&
+		(revid_data->rev4 == PM8916_V2P0_REV4) &&
+		(revid_data->pmic_subtype == PM8916_SUBTYPE))
+		return QPNP_REV_ID_8916_2_0;
+	else if ((revid_data->rev1 == PM8909_V1P0_REV1) &&
+		(revid_data->rev2 == PM8909_V1P0_REV2) &&
+		(revid_data->rev3 == PM8909_V1P0_REV3) &&
+		(revid_data->rev4 == PM8909_V1P0_REV4) &&
+		(revid_data->pmic_subtype == PM8909_SUBTYPE))
+		return QPNP_REV_ID_8909_1_0;
+	else if ((revid_data->rev1 == PM8909_V1P1_REV1) &&
+		(revid_data->rev2 == PM8909_V1P1_REV2) &&
+		(revid_data->rev3 == PM8909_V1P1_REV3) &&
+		(revid_data->rev4 == PM8909_V1P1_REV4) &&
+		(revid_data->pmic_subtype == PM8909_SUBTYPE))
+		return QPNP_REV_ID_8909_1_1;
+	else if ((revid_data->rev4 == PM8950_V1P0_REV4) &&
+		(revid_data->pmic_subtype == PM8950_SUBTYPE))
+		return QPNP_REV_ID_PM8950_1_0;
+	else
+		return -EINVAL;
+}
+EXPORT_SYMBOL(qpnp_adc_get_revid_version);
+
+int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
+			struct qpnp_adc_drv *adc_qpnp)
+{
+	struct device_node *node = pdev->dev.of_node;
+	unsigned int base;
+	struct device_node *child;
+	struct qpnp_adc_amux *adc_channel_list;
+	struct qpnp_adc_properties *adc_prop;
+	struct qpnp_adc_amux_properties *amux_prop;
+	int count_adc_channel_list = 0, decimation = 0, rc = 0, i = 0;
+	int decimation_tm_hc = 0, fast_avg_setup_tm_hc = 0, cal_val_hc = 0;
+	bool adc_hc;
+
+	if (!node)
+		return -EINVAL;
+
+	for_each_child_of_node(node, child)
+		count_adc_channel_list++;
+
+	if (!count_adc_channel_list) {
+		pr_err("No channel listing\n");
+		return -EINVAL;
+	}
+
+	adc_qpnp->pdev = pdev;
+
+	adc_prop = devm_kzalloc(&pdev->dev,
+				sizeof(struct qpnp_adc_properties),
+					GFP_KERNEL);
+	if (!adc_prop)
+		return -ENOMEM;
+
+	adc_channel_list = devm_kzalloc(&pdev->dev,
+		((sizeof(struct qpnp_adc_amux)) * count_adc_channel_list),
+				GFP_KERNEL);
+	if (!adc_channel_list)
+		return -ENOMEM;
+
+	amux_prop = devm_kzalloc(&pdev->dev,
+		sizeof(struct qpnp_adc_amux_properties) +
+		sizeof(struct qpnp_vadc_chan_properties), GFP_KERNEL);
+	if (!amux_prop) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_qpnp->adc_channels = adc_channel_list;
+	adc_qpnp->amux_prop = amux_prop;
+	adc_hc = adc_qpnp->adc_hc;
+	adc_prop->adc_hc = adc_hc;
+
+	if (of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+		rc = of_property_read_u32(node, "qcom,decimation",
+						&decimation_tm_hc);
+		if (rc) {
+			pr_err("Invalid decimation property\n");
+			return -EINVAL;
+		}
+
+		rc = of_property_read_u32(node,
+			"qcom,fast-avg-setup", &fast_avg_setup_tm_hc);
+		if (rc) {
+			pr_err("Invalid fast average setup with %d\n", rc);
+			return -EINVAL;
+		}
+
+		if ((fast_avg_setup_tm_hc) > ADC_FAST_AVG_SAMPLE_16) {
+			pr_err("Max average support is 2^16\n");
+			return -EINVAL;
+		}
+	}
+
+	for_each_child_of_node(node, child) {
+		int channel_num, scaling = 0, post_scaling = 0;
+		int fast_avg_setup, calib_type = 0, rc, hw_settle_time = 0;
+		const char *calibration_param, *channel_name;
+
+		channel_name = of_get_property(child,
+				"label", NULL) ? : child->name;
+		if (!channel_name) {
+			pr_err("Invalid channel name\n");
+			return -EINVAL;
+		}
+
+		rc = of_property_read_u32(child, "reg", &channel_num);
+		if (rc) {
+			pr_err("Invalid channel num\n");
+			return -EINVAL;
+		}
+
+		if (!of_device_is_compatible(node, "qcom,qpnp-iadc")) {
+			rc = of_property_read_u32(child,
+				"qcom,hw-settle-time", &hw_settle_time);
+			if (rc) {
+				pr_err("Invalid channel hw settle time property\n");
+				return -EINVAL;
+			}
+			rc = of_property_read_u32(child,
+				"qcom,pre-div-channel-scaling", &scaling);
+			if (rc) {
+				pr_err("Invalid channel scaling property\n");
+				return -EINVAL;
+			}
+			rc = of_property_read_u32(child,
+				"qcom,scale-function", &post_scaling);
+			if (rc) {
+				pr_err("Invalid channel post scaling property\n");
+				return -EINVAL;
+			}
+			rc = of_property_read_string(child,
+				"qcom,calibration-type", &calibration_param);
+			if (rc) {
+				pr_err("Invalid calibration type\n");
+				return -EINVAL;
+			}
+
+			if (!strcmp(calibration_param, "absolute")) {
+				if (adc_hc)
+					calib_type = ADC_HC_ABS_CAL;
+				else
+					calib_type = CALIB_ABSOLUTE;
+			} else if (!strcmp(calibration_param, "ratiometric")) {
+				if (adc_hc)
+					calib_type = ADC_HC_RATIO_CAL;
+				else
+					calib_type = CALIB_RATIOMETRIC;
+			} else if (!strcmp(calibration_param, "no_cal")) {
+				if (adc_hc)
+					calib_type = ADC_HC_NO_CAL;
+				else {
+					pr_err("%s: Invalid calibration property\n",
+						__func__);
+					return -EINVAL;
+				}
+			} else {
+				pr_err("%s: Invalid calibration property\n",
+						__func__);
+				return -EINVAL;
+			}
+		}
+
+		/* ADC_TM_HC fast avg setting is common across channels */
+		if (!of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+			rc = of_property_read_u32(child,
+				"qcom,fast-avg-setup", &fast_avg_setup);
+			if (rc) {
+				pr_err("Invalid channel fast average setup\n");
+				return -EINVAL;
+			}
+		} else {
+			fast_avg_setup = fast_avg_setup_tm_hc;
+		}
+
+		/* ADC_TM_HC decimation setting is common across channels */
+		if (!of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+			rc = of_property_read_u32(child,
+				"qcom,decimation", &decimation);
+			if (rc) {
+				pr_err("Invalid decimation\n");
+				return -EINVAL;
+			}
+		} else {
+			decimation = decimation_tm_hc;
+		}
+
+		if (of_device_is_compatible(node, "qcom,qpnp-vadc-hc")) {
+			rc = of_property_read_u32(child, "qcom,cal-val",
+							&cal_val_hc);
+			if (rc) {
+				pr_debug("Use calibration value from timer\n");
+				adc_channel_list[i].cal_val = ADC_TIMER_CAL;
+			} else {
+				adc_channel_list[i].cal_val = cal_val_hc;
+			}
+		}
+
+		/* Individual channel properties */
+		adc_channel_list[i].name = (char *)channel_name;
+		adc_channel_list[i].channel_num = channel_num;
+		adc_channel_list[i].adc_decimation = decimation;
+		adc_channel_list[i].fast_avg_setup = fast_avg_setup;
+		if (!of_device_is_compatible(node, "qcom,qpnp-iadc")) {
+			adc_channel_list[i].chan_path_prescaling = scaling;
+			adc_channel_list[i].adc_scale_fn = post_scaling;
+			adc_channel_list[i].hw_settle_time = hw_settle_time;
+			adc_channel_list[i].calib_type = calib_type;
+		}
+		i++;
+	}
+
+	/* Get the ADC VDD reference voltage and ADC bit resolution */
+	rc = of_property_read_u32(node, "qcom,adc-vdd-reference",
+			&adc_prop->adc_vdd_reference);
+	if (rc) {
+		pr_err("Invalid adc vdd reference property\n");
+		return -EINVAL;
+	}
+	rc = of_property_read_u32(node, "qcom,adc-bit-resolution",
+			&adc_prop->bitresolution);
+	if (rc) {
+		pr_err("Invalid adc bit resolution property\n");
+		return -EINVAL;
+	}
+	adc_qpnp->adc_prop = adc_prop;
+
+	/* Get the peripheral address */
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+
+	adc_qpnp->slave = to_spmi_device(pdev->dev.parent)->usid;
+	adc_qpnp->offset = base;
+
+	/* Register the ADC peripheral interrupt */
+	adc_qpnp->adc_irq_eoc = platform_get_irq_byname(pdev,
+							"eoc-int-en-set");
+	if (adc_qpnp->adc_irq_eoc < 0) {
+		pr_err("Invalid irq\n");
+		return -ENXIO;
+	}
+
+	init_completion(&adc_qpnp->adc_rslt_completion);
+
+	if (of_get_property(node, "hkadc_ldo-supply", NULL)) {
+		adc_qpnp->hkadc_ldo = regulator_get(&pdev->dev, "hkadc_ldo");
+		if (IS_ERR(adc_qpnp->hkadc_ldo)) {
+			pr_err("hkadc_ldo-supply node not found\n");
+			return -EINVAL;
+		}
+
+		rc = regulator_set_voltage(adc_qpnp->hkadc_ldo,
+				QPNP_VADC_LDO_VOLTAGE_MIN,
+				QPNP_VADC_LDO_VOLTAGE_MAX);
+		if (rc < 0) {
+			pr_err("setting voltage for hkadc_ldo failed\n");
+			return rc;
+		}
+
+		rc = regulator_set_load(adc_qpnp->hkadc_ldo, 100000);
+		if (rc < 0) {
+			pr_err("hkadc_ldo optimum mode failed%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (of_get_property(node, "hkadc_ok-supply", NULL)) {
+		adc_qpnp->hkadc_ldo_ok = regulator_get(&pdev->dev,
+				"hkadc_ok");
+		if (IS_ERR(adc_qpnp->hkadc_ldo_ok)) {
+			pr_err("hkadc_ok node not found\n");
+			return -EINVAL;
+		}
+
+		rc = regulator_set_voltage(adc_qpnp->hkadc_ldo_ok,
+				QPNP_VADC_OK_VOLTAGE_MIN,
+				QPNP_VADC_OK_VOLTAGE_MAX);
+		if (rc < 0) {
+			pr_err("setting voltage for hkadc-ldo-ok failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_adc_get_devicetree_data);
diff --git a/drivers/hwmon/qpnp-adc-current.c b/drivers/hwmon/qpnp-adc-current.c
new file mode 100644
index 0000000..a597ef9
--- /dev/null
+++ b/drivers/hwmon/qpnp-adc-current.c
@@ -0,0 +1,1658 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#ifdef CONFIG_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/platform_device.h>
+#ifdef CONFIG_WAKELOCK
+#include <linux/wakelock.h>
+#endif
+
+/* QPNP IADC register definition */
+#define QPNP_IADC_REVISION1				0x0
+#define QPNP_IADC_REVISION2				0x1
+#define QPNP_IADC_REVISION3				0x2
+#define QPNP_IADC_REVISION4				0x3
+#define QPNP_IADC_PERPH_TYPE				0x4
+#define QPNP_IADC_PERH_SUBTYPE				0x5
+
+#define QPNP_IADC_SUPPORTED_REVISION2			1
+
+#define QPNP_STATUS1					0x8
+#define QPNP_STATUS1_OP_MODE				4
+#define QPNP_STATUS1_MULTI_MEAS_EN			BIT(3)
+#define QPNP_STATUS1_MEAS_INTERVAL_EN_STS		BIT(2)
+#define QPNP_STATUS1_REQ_STS				BIT(1)
+#define QPNP_STATUS1_EOC				BIT(0)
+#define QPNP_STATUS1_REQ_STS_EOC_MASK			0x3
+#define QPNP_STATUS2					0x9
+#define QPNP_STATUS2_CONV_SEQ_STATE_SHIFT		4
+#define QPNP_STATUS2_FIFO_NOT_EMPTY_FLAG		BIT(1)
+#define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS		BIT(0)
+#define QPNP_CONV_TIMEOUT_ERR				2
+
+#define QPNP_IADC_MODE_CTL				0x40
+#define QPNP_OP_MODE_SHIFT				4
+#define QPNP_USE_BMS_DATA				BIT(4)
+#define QPNP_VADC_SYNCH_EN				BIT(2)
+#define QPNP_OFFSET_RMV_EN				BIT(1)
+#define QPNP_ADC_TRIM_EN				BIT(0)
+#define QPNP_IADC_EN_CTL1				0x46
+#define QPNP_IADC_ADC_EN				BIT(7)
+#define QPNP_ADC_CH_SEL_CTL				0x48
+#define QPNP_ADC_DIG_PARAM				0x50
+#define QPNP_ADC_CLK_SEL_MASK				0x3
+#define QPNP_ADC_DEC_RATIO_SEL_MASK			0xc
+#define QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT		2
+
+#define QPNP_CONV_REQ					0x52
+#define QPNP_CONV_REQ_SET				BIT(7)
+#define QPNP_CONV_SEQ_CTL				0x54
+#define QPNP_CONV_SEQ_HOLDOFF_SHIFT			4
+#define QPNP_CONV_SEQ_TRIG_CTL				0x55
+#define QPNP_FAST_AVG_CTL				0x5a
+
+#define QPNP_M0_LOW_THR_LSB				0x5c
+#define QPNP_M0_LOW_THR_MSB				0x5d
+#define QPNP_M0_HIGH_THR_LSB				0x5e
+#define QPNP_M0_HIGH_THR_MSB				0x5f
+#define QPNP_M1_LOW_THR_LSB				0x69
+#define QPNP_M1_LOW_THR_MSB				0x6a
+#define QPNP_M1_HIGH_THR_LSB				0x6b
+#define QPNP_M1_HIGH_THR_MSB				0x6c
+
+#define QPNP_DATA0					0x60
+#define QPNP_DATA1					0x61
+#define QPNP_CONV_TIMEOUT_ERR				2
+
+#define QPNP_IADC_SEC_ACCESS				0xD0
+#define QPNP_IADC_SEC_ACCESS_DATA			0xA5
+#define QPNP_IADC_MSB_OFFSET				0xF2
+#define QPNP_IADC_LSB_OFFSET				0xF3
+#define QPNP_IADC_NOMINAL_RSENSE			0xF4
+#define QPNP_IADC_ATE_GAIN_CALIB_OFFSET			0xF5
+#define QPNP_INT_TEST_VAL				0xE1
+
+#define QPNP_IADC_ADC_CH_SEL_CTL			0x48
+#define QPNP_IADC_ADC_CHX_SEL_SHIFT			3
+
+#define QPNP_IADC_ADC_DIG_PARAM				0x50
+#define QPNP_IADC_CLK_SEL_SHIFT				1
+#define QPNP_IADC_DEC_RATIO_SEL				3
+
+#define QPNP_IADC_CONV_REQUEST				0x52
+#define QPNP_IADC_CONV_REQ				BIT(7)
+
+#define QPNP_IADC_DATA0					0x60
+#define QPNP_IADC_DATA1					0x61
+
+#define QPNP_ADC_CONV_TIME_MIN				2000
+#define QPNP_ADC_CONV_TIME_MAX				2100
+#define QPNP_ADC_ERR_COUNT				20
+
+#define QPNP_ADC_GAIN_NV				17857
+#define QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL	0
+#define QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR		10000000
+#define QPNP_IADC_NANO_VOLTS_FACTOR			1000000
+#define QPNP_IADC_CALIB_SECONDS				300000
+#define QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT		15625
+#define QPNP_IADC_DIE_TEMP_CALIB_OFFSET			5000
+
+#define QPNP_RAW_CODE_16_BIT_MSB_MASK			0xff00
+#define QPNP_RAW_CODE_16_BIT_LSB_MASK			0xff
+#define QPNP_BIT_SHIFT_8				8
+#define QPNP_RSENSE_MSB_SIGN_CHECK			0x80
+#define QPNP_ADC_COMPLETION_TIMEOUT			HZ
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK			0x7
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_0		0
+#define SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2		2
+#define QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST	127
+#define QPNP_IADC_RSENSE_DEFAULT_VALUE			7800000
+#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF		9000000
+#define QPNP_IADC_RSENSE_DEFAULT_TYPEB_SMIC		9700000
+
+struct qpnp_iadc_comp {
+	bool	ext_rsense;
+	u8	id;
+	u8	sys_gain;
+	u8	revision_dig_major;
+	u8	revision_ana_minor;
+};
+
+struct qpnp_iadc_chip {
+	struct device				*dev;
+	struct qpnp_adc_drv			*adc;
+	int32_t					rsense;
+	bool					external_rsense;
+	bool					default_internal_rsense;
+	struct device				*iadc_hwmon;
+	struct list_head			list;
+	int64_t					die_temp;
+	struct delayed_work			iadc_work;
+	bool					iadc_mode_sel;
+	struct qpnp_iadc_comp			iadc_comp;
+	struct qpnp_vadc_chip			*vadc_dev;
+	struct work_struct			trigger_completion_work;
+	bool					skip_auto_calibrations;
+	bool					iadc_poll_eoc;
+	u16					batt_id_trim_cnst_rds;
+	int					rds_trim_default_type;
+	int					max_channels_available;
+	bool					rds_trim_default_check;
+	int32_t					rsense_workaround_value;
+	struct sensor_device_attribute		sens_attr[0];
+};
+
+LIST_HEAD(qpnp_iadc_device_list);
+
+enum qpnp_iadc_rsense_rds_workaround {
+	QPNP_IADC_RDS_DEFAULT_TYPEA,
+	QPNP_IADC_RDS_DEFAULT_TYPEB,
+	QPNP_IADC_RDS_DEFAULT_TYPEC,
+};
+
+static int32_t qpnp_iadc_read_reg(struct qpnp_iadc_chip *iadc,
+						uint32_t reg, u8 *data)
+{
+	int rc;
+	uint val;
+
+	rc = regmap_read(iadc->adc->regmap, (iadc->adc->offset + reg), &val);
+	if (rc < 0) {
+		pr_err("qpnp iadc read reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+	*data = (u8)val;
+
+	return 0;
+}
+
+static int32_t qpnp_iadc_write_reg(struct qpnp_iadc_chip *iadc,
+						uint32_t reg, u8 data)
+{
+	int rc;
+	u8 *buf;
+
+	buf = &data;
+	rc = regmap_write(iadc->adc->regmap, (iadc->adc->offset + reg), *buf);
+	if (rc < 0) {
+		pr_err("qpnp iadc write reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_iadc_is_valid(struct qpnp_iadc_chip *iadc)
+{
+	struct qpnp_iadc_chip *iadc_chip = NULL;
+
+	list_for_each_entry(iadc_chip, &qpnp_iadc_device_list, list)
+		if (iadc == iadc_chip)
+			return 0;
+
+	return -EINVAL;
+}
+
+static void qpnp_iadc_trigger_completion(struct work_struct *work)
+{
+	struct qpnp_iadc_chip *iadc = container_of(work,
+			struct qpnp_iadc_chip, trigger_completion_work);
+
+	if (qpnp_iadc_is_valid(iadc) < 0)
+		return;
+
+	complete(&iadc->adc->adc_rslt_completion);
+}
+
+static irqreturn_t qpnp_iadc_isr(int irq, void *dev_id)
+{
+	struct qpnp_iadc_chip *iadc = dev_id;
+
+	schedule_work(&iadc->trigger_completion_work);
+
+	return IRQ_HANDLED;
+}
+
+static int32_t qpnp_iadc_enable(struct qpnp_iadc_chip *dev, bool state)
+{
+	int rc = 0;
+	u8 data = 0;
+
+	data = QPNP_IADC_ADC_EN;
+	if (state) {
+		rc = qpnp_iadc_write_reg(dev, QPNP_IADC_EN_CTL1,
+					data);
+		if (rc < 0) {
+			pr_err("IADC enable failed\n");
+			return rc;
+		}
+	} else {
+		rc = qpnp_iadc_write_reg(dev, QPNP_IADC_EN_CTL1,
+					(~data & QPNP_IADC_ADC_EN));
+		if (rc < 0) {
+			pr_err("IADC disable failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_iadc_status_debug(struct qpnp_iadc_chip *dev)
+{
+	int rc = 0;
+	u8 mode = 0, status1 = 0, chan = 0, dig = 0, en = 0;
+
+	rc = qpnp_iadc_read_reg(dev, QPNP_IADC_MODE_CTL, &mode);
+	if (rc < 0) {
+		pr_err("mode ctl register read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(dev, QPNP_ADC_DIG_PARAM, &dig);
+	if (rc < 0) {
+		pr_err("digital param read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(dev, QPNP_IADC_ADC_CH_SEL_CTL, &chan);
+	if (rc < 0) {
+		pr_err("channel read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(dev, QPNP_STATUS1, &status1);
+	if (rc < 0) {
+		pr_err("status1 read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(dev, QPNP_IADC_EN_CTL1, &en);
+	if (rc < 0) {
+		pr_err("en read failed with %d\n", rc);
+		return rc;
+	}
+
+	pr_debug("EOC not set with status:%x, dig:%x, ch:%x, mode:%x, en:%x\n",
+			status1, dig, chan, mode, en);
+
+	rc = qpnp_iadc_enable(dev, false);
+	if (rc < 0) {
+		pr_err("IADC disable failed with %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_iadc_read_conversion_result(struct qpnp_iadc_chip *iadc,
+								int16_t *data)
+{
+	uint8_t rslt_lsb, rslt_msb;
+	uint16_t rslt;
+	int32_t rc;
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_DATA0, &rslt_lsb);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_DATA1, &rslt_msb);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	rslt = (rslt_msb << 8) | rslt_lsb;
+	*data = rslt;
+
+	rc = qpnp_iadc_enable(iadc, false);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+#define QPNP_IADC_PM8026_2_REV2	4
+#define QPNP_IADC_PM8026_2_REV3	2
+
+#define QPNP_COEFF_1					969000
+#define QPNP_COEFF_2					32
+#define QPNP_COEFF_3_TYPEA				1700000
+#define QPNP_COEFF_3_TYPEB				1000000
+#define QPNP_COEFF_4					100
+#define QPNP_COEFF_5					15
+#define QPNP_COEFF_6					100000
+#define QPNP_COEFF_7					21
+#define QPNP_COEFF_8					100000000
+#define QPNP_COEFF_9					38
+#define QPNP_COEFF_10					40
+#define QPNP_COEFF_11					7
+#define QPNP_COEFF_12					11
+#define QPNP_COEFF_13					37
+#define QPNP_COEFF_14					39
+#define QPNP_COEFF_15					9
+#define QPNP_COEFF_16					11
+#define QPNP_COEFF_17					851200
+#define QPNP_COEFF_18					296500
+#define QPNP_COEFF_19					222400
+#define QPNP_COEFF_20					813800
+#define QPNP_COEFF_21					1059100
+#define QPNP_COEFF_22					5000000
+#define QPNP_COEFF_23					3722500
+#define QPNP_COEFF_24					84
+#define QPNP_COEFF_25					33
+#define QPNP_COEFF_26					22
+#define QPNP_COEFF_27					53
+#define QPNP_COEFF_28					48
+
+static int32_t qpnp_iadc_comp(int64_t *result, struct qpnp_iadc_chip *iadc,
+							int64_t die_temp)
+{
+	int64_t temp_var = 0, sys_gain_coeff = 0, old;
+	int32_t coeff_a = 0, coeff_b = 0;
+	int version = 0;
+
+	version = qpnp_adc_get_revid_version(iadc->dev);
+	if (version == -EINVAL)
+		return 0;
+
+	old = *result;
+	*result = *result * 1000000;
+
+	if (iadc->iadc_comp.sys_gain > 127)
+		sys_gain_coeff = -QPNP_COEFF_6 *
+				(iadc->iadc_comp.sys_gain - 128);
+	else
+		sys_gain_coeff = QPNP_COEFF_6 *
+				iadc->iadc_comp.sys_gain;
+
+	switch (version) {
+	case QPNP_REV_ID_8941_3_1:
+		switch (iadc->iadc_comp.id) {
+		case COMP_ID_GF:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				coeff_a = QPNP_COEFF_2;
+				coeff_b = -QPNP_COEFF_3_TYPEA;
+			} else {
+				if (*result < 0) {
+					/* charge */
+					coeff_a = QPNP_COEFF_5;
+					coeff_b = QPNP_COEFF_6;
+				} else {
+					/* discharge */
+					coeff_a = -QPNP_COEFF_7;
+					coeff_b = QPNP_COEFF_6;
+				}
+			}
+			break;
+		case COMP_ID_TSMC:
+		default:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				coeff_a = QPNP_COEFF_2;
+				coeff_b = -QPNP_COEFF_3_TYPEB;
+			} else {
+				if (*result < 0) {
+					/* charge */
+					coeff_a = QPNP_COEFF_5;
+					coeff_b = QPNP_COEFF_6;
+				} else {
+					/* discharge */
+					coeff_a = -QPNP_COEFF_7;
+					coeff_b = QPNP_COEFF_6;
+				}
+			}
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_2_1:
+	case QPNP_REV_ID_8026_2_2:
+		/* pm8026 rev 2.1 and 2.2 */
+		switch (iadc->iadc_comp.id) {
+		case COMP_ID_GF:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = 0;
+					coeff_b = 0;
+				} else {
+					coeff_a = QPNP_COEFF_25;
+					coeff_b = 0;
+				}
+			} else {
+				if (*result < 0) {
+					/* charge */
+					coeff_a = 0;
+					coeff_b = 0;
+				} else {
+					/* discharge */
+					coeff_a = 0;
+					coeff_b = 0;
+				}
+			}
+			break;
+		case COMP_ID_TSMC:
+		default:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = 0;
+					coeff_b = 0;
+				} else {
+					coeff_a = QPNP_COEFF_26;
+					coeff_b = 0;
+				}
+			} else {
+				if (*result < 0) {
+					/* charge */
+					coeff_a = 0;
+					coeff_b = 0;
+				} else {
+					/* discharge */
+					coeff_a = 0;
+					coeff_b = 0;
+				}
+			}
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_1_0:
+		/* pm8026 rev 1.0 */
+		switch (iadc->iadc_comp.id) {
+		case COMP_ID_GF:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = QPNP_COEFF_9;
+					coeff_b = -QPNP_COEFF_17;
+				} else {
+					coeff_a = QPNP_COEFF_10;
+					coeff_b = QPNP_COEFF_18;
+				}
+			} else {
+				if (*result < 0) {
+					/* charge */
+					coeff_a = -QPNP_COEFF_11;
+					coeff_b = 0;
+				} else {
+					/* discharge */
+					coeff_a = -QPNP_COEFF_17;
+					coeff_b = -QPNP_COEFF_19;
+				}
+			}
+			break;
+		case COMP_ID_TSMC:
+		default:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = QPNP_COEFF_13;
+					coeff_b = -QPNP_COEFF_20;
+				} else {
+					coeff_a = QPNP_COEFF_14;
+					coeff_b = QPNP_COEFF_21;
+				}
+			} else {
+				if (*result < 0) {
+					/* charge */
+					coeff_a = -QPNP_COEFF_15;
+					coeff_b = 0;
+				} else {
+					/* discharge */
+					coeff_a = -QPNP_COEFF_12;
+					coeff_b = -QPNP_COEFF_19;
+				}
+			}
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8110_1_0:
+		/* pm8110 rev 1.0 */
+		switch (iadc->iadc_comp.id) {
+		case COMP_ID_GF:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = QPNP_COEFF_24;
+					coeff_b = -QPNP_COEFF_22;
+				} else {
+					coeff_a = QPNP_COEFF_24;
+					coeff_b = -QPNP_COEFF_23;
+				}
+			}
+			break;
+		case COMP_ID_SMIC:
+		default:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = QPNP_COEFF_24;
+					coeff_b = -QPNP_COEFF_22;
+				} else {
+					coeff_a = QPNP_COEFF_24;
+					coeff_b = -QPNP_COEFF_23;
+				}
+			}
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8110_2_0:
+		die_temp -= 25000;
+		/* pm8110 rev 2.0 */
+		switch (iadc->iadc_comp.id) {
+		case COMP_ID_GF:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = 0;
+					coeff_b = 0;
+				} else {
+					coeff_a = QPNP_COEFF_27;
+					coeff_b = 0;
+				}
+			}
+			break;
+		case COMP_ID_SMIC:
+		default:
+			if (!iadc->iadc_comp.ext_rsense) {
+				/* internal rsense */
+				if (*result < 0) {
+					/* charge */
+					coeff_a = 0;
+					coeff_b = 0;
+				} else {
+					coeff_a = QPNP_COEFF_28;
+					coeff_b = 0;
+				}
+			}
+			break;
+		}
+		break;
+	default:
+	case QPNP_REV_ID_8026_2_0:
+		/* pm8026 rev 1.0 */
+		coeff_a = 0;
+		coeff_b = 0;
+		break;
+	}
+
+	temp_var = (coeff_a * die_temp) + coeff_b;
+	temp_var = div64_s64(temp_var, QPNP_COEFF_4);
+	temp_var = 1000 * (1000000 - temp_var);
+
+	if (!iadc->iadc_comp.ext_rsense) {
+		/* internal rsense */
+		*result = div64_s64(*result * 1000, temp_var);
+	}
+
+	if (iadc->iadc_comp.ext_rsense) {
+		/* external rsense */
+		sys_gain_coeff = (1000000 +
+			div64_s64(sys_gain_coeff, QPNP_COEFF_4));
+		temp_var = div64_s64(temp_var * sys_gain_coeff, 1000000);
+		*result = div64_s64(*result * 1000, temp_var);
+	}
+	pr_debug("%lld compensated into %lld, a: %d, b: %d, sys_gain: %lld\n",
+			old, *result, coeff_a, coeff_b, sys_gain_coeff);
+
+	return 0;
+}
+
+int32_t qpnp_iadc_comp_result(struct qpnp_iadc_chip *iadc, int64_t *result)
+{
+	return qpnp_iadc_comp(result, iadc, iadc->die_temp);
+}
+EXPORT_SYMBOL(qpnp_iadc_comp_result);
+
+static int qpnp_iadc_rds_trim_update_check(struct qpnp_iadc_chip *iadc)
+{
+	int rc = 0;
+	u8 trim2_val = 0;
+	uint smbb_batt_trm_data = 0;
+	u8 smbb_batt_trm_cnst_rds = 0;
+
+	if (!iadc->rds_trim_default_check) {
+		pr_debug("No internal rds trim check needed\n");
+		return 0;
+	}
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE, &trim2_val);
+	if (rc < 0) {
+		pr_err("qpnp adc trim2_fullscale1 reg read failed %d\n", rc);
+		return rc;
+	}
+
+	rc = regmap_read(iadc->adc->regmap, iadc->batt_id_trim_cnst_rds,
+			 &smbb_batt_trm_data);
+	if (rc < 0) {
+		pr_err("batt_id trim_cnst rds reg read failed %d\n", rc);
+		return rc;
+	}
+
+	smbb_batt_trm_cnst_rds = (u8)smbb_batt_trm_data &
+				SMBB_BAT_IF_TRIM_CNST_RDS_MASK;
+
+	pr_debug("n_trim:0x%x smb_trm:0x%02x\n", trim2_val, smbb_batt_trm_data);
+
+	if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEA) {
+
+		if ((smbb_batt_trm_cnst_rds ==
+				SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
+		(trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+			iadc->rsense_workaround_value =
+					QPNP_IADC_RSENSE_DEFAULT_VALUE;
+			iadc->default_internal_rsense = true;
+		}
+	} else if (iadc->rds_trim_default_type ==
+						QPNP_IADC_RDS_DEFAULT_TYPEB) {
+		if ((smbb_batt_trm_cnst_rds >=
+				SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
+		(trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+			iadc->rsense_workaround_value =
+					QPNP_IADC_RSENSE_DEFAULT_VALUE;
+				iadc->default_internal_rsense = true;
+		} else if ((smbb_batt_trm_cnst_rds <
+				SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
+			(trim2_val ==
+				QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+			if (iadc->iadc_comp.id == COMP_ID_GF) {
+				iadc->rsense_workaround_value =
+					QPNP_IADC_RSENSE_DEFAULT_TYPEB_GF;
+				iadc->default_internal_rsense = true;
+			} else if (iadc->iadc_comp.id == COMP_ID_SMIC) {
+				iadc->rsense_workaround_value =
+					QPNP_IADC_RSENSE_DEFAULT_TYPEB_SMIC;
+				iadc->default_internal_rsense = true;
+			}
+		}
+	} else if (iadc->rds_trim_default_type == QPNP_IADC_RDS_DEFAULT_TYPEC) {
+
+		if ((smbb_batt_trm_cnst_rds >
+				SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_0) &&
+		(smbb_batt_trm_cnst_rds <=
+				SMBB_BAT_IF_TRIM_CNST_RDS_MASK_CONST_2) &&
+		(trim2_val == QPNP_IADC1_USR_TRIM2_ADC_FULLSCALE1_CONST)) {
+			iadc->rsense_workaround_value =
+					QPNP_IADC_RSENSE_DEFAULT_VALUE;
+			iadc->default_internal_rsense = true;
+		}
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_iadc_comp_info(struct qpnp_iadc_chip *iadc)
+{
+	int rc = 0;
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_INT_TEST_VAL, &iadc->iadc_comp.id);
+	if (rc < 0) {
+		pr_err("qpnp adc comp id failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_REVISION2,
+					&iadc->iadc_comp.revision_dig_major);
+	if (rc < 0) {
+		pr_err("qpnp adc revision2 read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_REVISION3,
+					&iadc->iadc_comp.revision_ana_minor);
+	if (rc < 0) {
+		pr_err("qpnp adc revision3 read failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_ATE_GAIN_CALIB_OFFSET,
+						&iadc->iadc_comp.sys_gain);
+	if (rc < 0) {
+		pr_err("full scale read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (iadc->external_rsense)
+		iadc->iadc_comp.ext_rsense = true;
+
+	pr_debug("fab id = %u, revision_dig_major = %u, revision_ana_minor = %u sys gain = %u, external_rsense = %d\n",
+			iadc->iadc_comp.id,
+			iadc->iadc_comp.revision_dig_major,
+			iadc->iadc_comp.revision_ana_minor,
+			iadc->iadc_comp.sys_gain,
+			iadc->iadc_comp.ext_rsense);
+	return rc;
+}
+
+static int32_t qpnp_iadc_configure(struct qpnp_iadc_chip *iadc,
+					enum qpnp_iadc_channels channel,
+					uint16_t *raw_code, uint32_t mode_sel)
+{
+	u8 qpnp_iadc_mode_reg = 0, qpnp_iadc_ch_sel_reg = 0;
+	u8 qpnp_iadc_conv_req = 0, qpnp_iadc_dig_param_reg = 0;
+	u8 status1 = 0;
+	uint32_t count = 0;
+	int32_t rc = 0;
+
+	qpnp_iadc_ch_sel_reg = channel;
+
+	qpnp_iadc_dig_param_reg |= iadc->adc->amux_prop->decimation <<
+					QPNP_IADC_DEC_RATIO_SEL;
+	if (iadc->iadc_mode_sel)
+		qpnp_iadc_mode_reg |= (QPNP_ADC_TRIM_EN | QPNP_VADC_SYNCH_EN);
+	else
+		qpnp_iadc_mode_reg |= QPNP_ADC_TRIM_EN;
+
+	qpnp_iadc_conv_req = QPNP_IADC_CONV_REQ;
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_IADC_MODE_CTL, qpnp_iadc_mode_reg);
+	if (rc) {
+		pr_err("qpnp adc read adc failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_IADC_ADC_CH_SEL_CTL,
+						qpnp_iadc_ch_sel_reg);
+	if (rc) {
+		pr_err("qpnp adc read adc failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_ADC_DIG_PARAM,
+						qpnp_iadc_dig_param_reg);
+	if (rc) {
+		pr_err("qpnp adc read adc failed with %d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_FAST_AVG_CTL,
+					iadc->adc->amux_prop->fast_avg_setup);
+	if (rc < 0) {
+		pr_err("qpnp adc fast averaging configure error\n");
+		return rc;
+	}
+
+	if (!iadc->iadc_poll_eoc)
+		reinit_completion(&iadc->adc->adc_rslt_completion);
+
+	rc = qpnp_iadc_enable(iadc, true);
+	if (rc)
+		return rc;
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_CONV_REQ, qpnp_iadc_conv_req);
+	if (rc) {
+		pr_err("qpnp adc read adc failed with %d\n", rc);
+		return rc;
+	}
+
+	if (iadc->iadc_poll_eoc) {
+		while (status1 != QPNP_STATUS1_EOC) {
+			rc = qpnp_iadc_read_reg(iadc, QPNP_STATUS1, &status1);
+			if (rc < 0)
+				return rc;
+			status1 &= QPNP_STATUS1_REQ_STS_EOC_MASK;
+			usleep_range(QPNP_ADC_CONV_TIME_MIN,
+					QPNP_ADC_CONV_TIME_MAX);
+			count++;
+			if (count > QPNP_ADC_ERR_COUNT) {
+				pr_err("retry error exceeded\n");
+				rc = qpnp_iadc_status_debug(iadc);
+				if (rc < 0)
+					pr_err("IADC status debug failed\n");
+				rc = -EINVAL;
+				return rc;
+			}
+		}
+	} else {
+		rc = wait_for_completion_timeout(
+				&iadc->adc->adc_rslt_completion,
+				QPNP_ADC_COMPLETION_TIMEOUT);
+		if (!rc) {
+			rc = qpnp_iadc_read_reg(iadc, QPNP_STATUS1, &status1);
+			if (rc < 0)
+				return rc;
+			status1 &= QPNP_STATUS1_REQ_STS_EOC_MASK;
+			if (status1 == QPNP_STATUS1_EOC)
+				pr_debug("End of conversion status set\n");
+			else {
+				rc = qpnp_iadc_status_debug(iadc);
+				if (rc < 0) {
+					pr_err("status debug failed %d\n", rc);
+					return rc;
+				}
+				return -EINVAL;
+			}
+		}
+	}
+
+	rc = qpnp_iadc_read_conversion_result(iadc, raw_code);
+	if (rc) {
+		pr_err("qpnp adc read adc failed with %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+#define IADC_CENTER	0xC000
+#define IADC_READING_RESOLUTION_N	542535
+#define IADC_READING_RESOLUTION_D	100000
+static int32_t qpnp_convert_raw_offset_voltage(struct qpnp_iadc_chip *iadc)
+{
+	s64 numerator;
+
+	if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+		pr_err("raw offset errors! raw_gain:0x%x and raw_offset:0x%x\n",
+			iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
+		return -EINVAL;
+	}
+
+	numerator = iadc->adc->calib.offset_raw - IADC_CENTER;
+	numerator *= IADC_READING_RESOLUTION_N;
+	iadc->adc->calib.offset_uv = div_s64(numerator,
+						IADC_READING_RESOLUTION_D);
+
+	numerator = iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw;
+	numerator *= IADC_READING_RESOLUTION_N;
+
+	iadc->adc->calib.gain_uv = div_s64(numerator,
+						IADC_READING_RESOLUTION_D);
+
+	pr_debug("gain_uv:%d offset_uv:%d\n",
+			iadc->adc->calib.gain_uv, iadc->adc->calib.offset_uv);
+	return 0;
+}
+
+#define IADC_IDEAL_RAW_GAIN	3291
+int32_t qpnp_iadc_calibrate_for_trim(struct qpnp_iadc_chip *iadc,
+							bool batfet_closed)
+{
+	uint8_t rslt_lsb, rslt_msb;
+	int32_t rc = 0, version = 0;
+	uint16_t raw_data;
+	uint32_t mode_sel = 0;
+	bool iadc_offset_ch_batfet_check;
+
+	if (qpnp_iadc_is_valid(iadc) < 0)
+		return -EPROBE_DEFER;
+
+	mutex_lock(&iadc->adc->adc_lock);
+
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("acquiring iadc eoc wakelock\n");
+		pm_stay_awake(iadc->dev);
+	}
+
+	iadc->adc->amux_prop->decimation = DECIMATION_TYPE1;
+	iadc->adc->amux_prop->fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
+
+	rc = qpnp_iadc_configure(iadc, GAIN_CALIBRATION_17P857MV,
+						&raw_data, mode_sel);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		goto fail;
+	}
+
+	iadc->adc->calib.gain_raw = raw_data;
+
+	/*
+	 * there is a features on PM8941 in the BMS where if the batfet is
+	 * opened the BMS reads from INTERNAL_RSENSE (channel 0) actually go to
+	 * OFFSET_CALIBRATION_CSP_CSN (channel 5). Hence if batfet is opened
+	 * we have to calibrate based on OFFSET_CALIBRATION_CSP_CSN even for
+	 * internal rsense.
+	 */
+	version = qpnp_adc_get_revid_version(iadc->dev);
+	if ((version == QPNP_REV_ID_8941_3_1) ||
+			(version == QPNP_REV_ID_8941_3_0) ||
+			(version == QPNP_REV_ID_8941_2_0))
+		iadc_offset_ch_batfet_check = true;
+	else
+		iadc_offset_ch_batfet_check = false;
+
+	if ((iadc_offset_ch_batfet_check && !batfet_closed) ||
+						(iadc->external_rsense)) {
+		/* external offset calculation */
+		rc = qpnp_iadc_configure(iadc, OFFSET_CALIBRATION_CSP_CSN,
+						&raw_data, mode_sel);
+		if (rc < 0) {
+			pr_err("qpnp adc result read failed with %d\n", rc);
+			goto fail;
+		}
+	} else {
+		/* internal offset calculation */
+		rc = qpnp_iadc_configure(iadc, OFFSET_CALIBRATION_CSP2_CSN2,
+						&raw_data, mode_sel);
+		if (rc < 0) {
+			pr_err("qpnp adc result read failed with %d\n", rc);
+			goto fail;
+		}
+	}
+
+	iadc->adc->calib.offset_raw = raw_data;
+	if (rc < 0) {
+		pr_err("qpnp adc offset/gain calculation failed\n");
+		goto fail;
+	}
+
+	if (iadc->iadc_comp.revision_dig_major == QPNP_IADC_PM8026_2_REV2
+		&& iadc->iadc_comp.revision_ana_minor ==
+						QPNP_IADC_PM8026_2_REV3)
+		iadc->adc->calib.gain_raw =
+			iadc->adc->calib.offset_raw + IADC_IDEAL_RAW_GAIN;
+
+	pr_debug("raw gain:0x%x, raw offset:0x%x\n",
+		iadc->adc->calib.gain_raw, iadc->adc->calib.offset_raw);
+
+	rc = qpnp_convert_raw_offset_voltage(iadc);
+	if (rc < 0) {
+		pr_err("qpnp raw_voltage conversion failed\n");
+		goto fail;
+	}
+
+	rslt_msb = (raw_data & QPNP_RAW_CODE_16_BIT_MSB_MASK) >>
+							QPNP_BIT_SHIFT_8;
+	rslt_lsb = raw_data & QPNP_RAW_CODE_16_BIT_LSB_MASK;
+
+	pr_debug("trim values:lsb:0x%x and msb:0x%x\n", rslt_lsb, rslt_msb);
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_IADC_SEC_ACCESS,
+					QPNP_IADC_SEC_ACCESS_DATA);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for sec access\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_IADC_MSB_OFFSET,
+						rslt_msb);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for MSB write\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_IADC_SEC_ACCESS,
+					QPNP_IADC_SEC_ACCESS_DATA);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for sec access\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_write_reg(iadc, QPNP_IADC_LSB_OFFSET,
+						rslt_lsb);
+	if (rc < 0) {
+		pr_err("qpnp iadc configure error for LSB write\n");
+		goto fail;
+	}
+fail:
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("releasing iadc eoc wakelock\n");
+		pm_relax(iadc->dev);
+	}
+	mutex_unlock(&iadc->adc->adc_lock);
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_iadc_calibrate_for_trim);
+
+static void qpnp_iadc_work(struct work_struct *work)
+{
+	struct qpnp_iadc_chip *iadc = container_of(work,
+			struct qpnp_iadc_chip, iadc_work.work);
+	int rc = 0;
+
+	if (!iadc->skip_auto_calibrations) {
+		rc = qpnp_iadc_calibrate_for_trim(iadc, true);
+		if (rc)
+			pr_debug("periodic IADC calibration failed\n");
+	}
+
+	schedule_delayed_work(&iadc->iadc_work,
+		round_jiffies_relative(msecs_to_jiffies
+				(QPNP_IADC_CALIB_SECONDS)));
+}
+
+static int32_t qpnp_iadc_version_check(struct qpnp_iadc_chip *iadc)
+{
+	uint8_t revision;
+	int rc;
+
+	rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_REVISION2, &revision);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (revision < QPNP_IADC_SUPPORTED_REVISION2) {
+		pr_err("IADC Version not supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+struct qpnp_iadc_chip *qpnp_get_iadc(struct device *dev, const char *name)
+{
+	struct qpnp_iadc_chip *iadc;
+	struct device_node *node = NULL;
+	char prop_name[QPNP_MAX_PROP_NAME_LEN];
+
+	snprintf(prop_name, QPNP_MAX_PROP_NAME_LEN, "qcom,%s-iadc", name);
+
+	node = of_parse_phandle(dev->of_node, prop_name, 0);
+	if (node == NULL)
+		return ERR_PTR(-ENODEV);
+
+	list_for_each_entry(iadc, &qpnp_iadc_device_list, list)
+		if (iadc->adc->pdev->dev.of_node == node)
+			return iadc;
+	return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(qpnp_get_iadc);
+
+int32_t qpnp_iadc_get_rsense(struct qpnp_iadc_chip *iadc, int32_t *rsense)
+{
+	uint8_t	rslt_rsense = 0;
+	int32_t	rc = 0, sign_bit = 0;
+
+	if (qpnp_iadc_is_valid(iadc) < 0)
+		return -EPROBE_DEFER;
+
+	if (iadc->external_rsense) {
+		*rsense = iadc->rsense;
+	} else if (iadc->default_internal_rsense) {
+		*rsense = iadc->rsense_workaround_value;
+	} else {
+
+		rc = qpnp_iadc_read_reg(iadc, QPNP_IADC_NOMINAL_RSENSE,
+							&rslt_rsense);
+		if (rc < 0) {
+			pr_err("qpnp adc rsense read failed with %d\n", rc);
+			return rc;
+		}
+
+		pr_debug("rsense:0%x\n", rslt_rsense);
+
+		if (rslt_rsense & QPNP_RSENSE_MSB_SIGN_CHECK)
+			sign_bit = 1;
+
+		rslt_rsense &= ~QPNP_RSENSE_MSB_SIGN_CHECK;
+
+		if (sign_bit)
+			*rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR -
+			(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
+		else
+			*rsense = QPNP_IADC_INTERNAL_RSENSE_N_OHMS_FACTOR +
+			(rslt_rsense * QPNP_IADC_RSENSE_LSB_N_OHMS_PER_BIT);
+	}
+	pr_debug("rsense value is %d\n", *rsense);
+
+	if (*rsense == 0)
+		pr_err("incorrect rsens value:%d rslt_rsense:%d\n",
+				*rsense, rslt_rsense);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_iadc_get_rsense);
+
+static int32_t qpnp_check_pmic_temp(struct qpnp_iadc_chip *iadc)
+{
+	struct qpnp_vadc_result result_pmic_therm;
+	int64_t die_temp_offset;
+	int rc = 0;
+
+	rc = qpnp_vadc_read(iadc->vadc_dev, DIE_TEMP, &result_pmic_therm);
+	if (rc < 0)
+		return rc;
+
+	die_temp_offset = result_pmic_therm.physical -
+			iadc->die_temp;
+	if (die_temp_offset < 0)
+		die_temp_offset = -die_temp_offset;
+
+	if (die_temp_offset > QPNP_IADC_DIE_TEMP_CALIB_OFFSET) {
+		iadc->die_temp = result_pmic_therm.physical;
+		if (!iadc->skip_auto_calibrations) {
+			rc = qpnp_iadc_calibrate_for_trim(iadc, true);
+			if (rc)
+				pr_err("IADC calibration failed rc = %d\n", rc);
+		}
+	}
+
+	return rc;
+}
+
+int32_t qpnp_iadc_read(struct qpnp_iadc_chip *iadc,
+				enum qpnp_iadc_channels channel,
+				struct qpnp_iadc_result *result)
+{
+	int32_t rc, rsense_n_ohms, sign = 0, num, mode_sel = 0;
+	int32_t rsense_u_ohms = 0;
+	int64_t result_current;
+	uint16_t raw_data;
+	int dt_index = 0;
+
+	if (qpnp_iadc_is_valid(iadc) < 0)
+		return -EPROBE_DEFER;
+
+	if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+		pr_err("raw offset errors! run iadc calibration again\n");
+		return -EINVAL;
+	}
+
+	rc = qpnp_check_pmic_temp(iadc);
+	if (rc) {
+		pr_err("Error checking pmic therm temp\n");
+		return rc;
+	}
+
+	mutex_lock(&iadc->adc->adc_lock);
+
+	while (((enum qpnp_iadc_channels)
+		iadc->adc->adc_channels[dt_index].channel_num
+		!= channel) && (dt_index < iadc->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= iadc->max_channels_available) {
+		pr_err("not a valid IADC channel\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	iadc->adc->amux_prop->decimation =
+			iadc->adc->adc_channels[dt_index].adc_decimation;
+	iadc->adc->amux_prop->fast_avg_setup =
+			iadc->adc->adc_channels[dt_index].fast_avg_setup;
+
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("acquiring iadc eoc wakelock\n");
+		pm_stay_awake(iadc->dev);
+	}
+
+	rc = qpnp_iadc_configure(iadc, channel, &raw_data, mode_sel);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		goto fail;
+	}
+
+	rc = qpnp_iadc_get_rsense(iadc, &rsense_n_ohms);
+	pr_debug("current raw:0%x and rsense:%d\n",
+			raw_data, rsense_n_ohms);
+	rsense_u_ohms = rsense_n_ohms/1000;
+	num = raw_data - iadc->adc->calib.offset_raw;
+	if (num < 0) {
+		sign = 1;
+		num = -num;
+	}
+
+	result->result_uv = (num * QPNP_ADC_GAIN_NV)/
+		(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+	result_current = result->result_uv;
+	result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
+	/* Intentional fall through. Process the result w/o comp */
+	do_div(result_current, rsense_u_ohms);
+
+	if (sign) {
+		result->result_uv = -result->result_uv;
+		result_current = -result_current;
+	}
+	result_current *= -1;
+	rc = qpnp_iadc_comp_result(iadc, &result_current);
+	if (rc < 0)
+		pr_err("Error during compensating the IADC\n");
+	rc = 0;
+	result_current *= -1;
+
+	result->result_ua = (int32_t) result_current;
+fail:
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("releasing iadc eoc wakelock\n");
+		pm_relax(iadc->dev);
+	}
+	mutex_unlock(&iadc->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_iadc_read);
+
+int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_chip *iadc,
+					struct qpnp_iadc_calib *result)
+{
+	int rc;
+
+	if (qpnp_iadc_is_valid(iadc) < 0)
+		return -EPROBE_DEFER;
+
+	rc = qpnp_check_pmic_temp(iadc);
+	if (rc) {
+		pr_err("Error checking pmic therm temp\n");
+		return rc;
+	}
+
+	mutex_lock(&iadc->adc->adc_lock);
+	result->gain_raw = iadc->adc->calib.gain_raw;
+	result->ideal_gain_nv = QPNP_ADC_GAIN_NV;
+	result->gain_uv = iadc->adc->calib.gain_uv;
+	result->offset_raw = iadc->adc->calib.offset_raw;
+	result->ideal_offset_uv =
+				QPNP_OFFSET_CALIBRATION_SHORT_CADC_LEADS_IDEAL;
+	result->offset_uv = iadc->adc->calib.offset_uv;
+	pr_debug("raw gain:0%x, raw offset:0%x\n",
+			result->gain_raw, result->offset_raw);
+	pr_debug("gain_uv:%d offset_uv:%d\n",
+			result->gain_uv, result->offset_uv);
+	mutex_unlock(&iadc->adc->adc_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_iadc_get_gain_and_offset);
+
+int qpnp_iadc_skip_calibration(struct qpnp_iadc_chip *iadc)
+{
+	iadc->skip_auto_calibrations = true;
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_iadc_skip_calibration);
+
+int qpnp_iadc_resume_calibration(struct qpnp_iadc_chip *iadc)
+{
+	iadc->skip_auto_calibrations = false;
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_iadc_resume_calibration);
+
+int32_t qpnp_iadc_vadc_sync_read(struct qpnp_iadc_chip *iadc,
+	enum qpnp_iadc_channels i_channel, struct qpnp_iadc_result *i_result,
+	enum qpnp_vadc_channels v_channel, struct qpnp_vadc_result *v_result)
+{
+	int rc = 0, mode_sel = 0, num = 0, rsense_n_ohms = 0, sign = 0;
+	int dt_index = 0;
+	uint16_t raw_data;
+	int32_t rsense_u_ohms = 0;
+	int64_t result_current;
+
+	if (qpnp_iadc_is_valid(iadc) < 0)
+		return -EPROBE_DEFER;
+
+	if ((iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw) == 0) {
+		pr_err("raw offset errors! run iadc calibration again\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&iadc->adc->adc_lock);
+
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("acquiring iadc eoc wakelock\n");
+		pm_stay_awake(iadc->dev);
+	}
+
+	iadc->iadc_mode_sel = true;
+
+	rc = qpnp_vadc_iadc_sync_request(iadc->vadc_dev, v_channel);
+	if (rc) {
+		pr_err("Configuring VADC failed\n");
+		goto fail;
+	}
+
+	while (((enum qpnp_iadc_channels)
+		iadc->adc->adc_channels[dt_index].channel_num
+		!= i_channel) && (dt_index < iadc->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= iadc->max_channels_available) {
+		pr_err("not a valid IADC channel\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	iadc->adc->amux_prop->decimation =
+			iadc->adc->adc_channels[dt_index].adc_decimation;
+	iadc->adc->amux_prop->fast_avg_setup =
+			iadc->adc->adc_channels[dt_index].fast_avg_setup;
+
+	rc = qpnp_iadc_configure(iadc, i_channel, &raw_data, mode_sel);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		goto fail_release_vadc;
+	}
+
+	rc = qpnp_iadc_get_rsense(iadc, &rsense_n_ohms);
+	pr_debug("current raw:0%x and rsense:%d\n",
+			raw_data, rsense_n_ohms);
+	rsense_u_ohms = rsense_n_ohms/1000;
+	num = raw_data - iadc->adc->calib.offset_raw;
+	if (num < 0) {
+		sign = 1;
+		num = -num;
+	}
+
+	i_result->result_uv = (num * QPNP_ADC_GAIN_NV)/
+		(iadc->adc->calib.gain_raw - iadc->adc->calib.offset_raw);
+	result_current = i_result->result_uv;
+	result_current *= QPNP_IADC_NANO_VOLTS_FACTOR;
+	/* Intentional fall through. Process the result w/o comp */
+	if (!rsense_u_ohms) {
+		pr_err("rsense error=%d\n", rsense_u_ohms);
+		goto fail_release_vadc;
+	}
+
+	do_div(result_current, rsense_u_ohms);
+
+	if (sign) {
+		i_result->result_uv = -i_result->result_uv;
+		result_current = -result_current;
+	}
+	result_current *= -1;
+	rc = qpnp_iadc_comp_result(iadc, &result_current);
+	if (rc < 0)
+		pr_err("Error during compensating the IADC\n");
+	rc = 0;
+	result_current *= -1;
+
+	i_result->result_ua = (int32_t) result_current;
+
+fail_release_vadc:
+	rc = qpnp_vadc_iadc_sync_complete_request(iadc->vadc_dev, v_channel,
+							v_result);
+	if (rc)
+		pr_err("Releasing VADC failed\n");
+fail:
+	iadc->iadc_mode_sel = false;
+
+	if (iadc->iadc_poll_eoc) {
+		pr_debug("releasing iadc eoc wakelock\n");
+		pm_relax(iadc->dev);
+	}
+	mutex_unlock(&iadc->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_iadc_vadc_sync_read);
+
+static ssize_t qpnp_iadc_show(struct device *dev,
+			struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct qpnp_iadc_chip *iadc = dev_get_drvdata(dev);
+	struct qpnp_iadc_result result;
+	int rc = -1;
+
+	rc = qpnp_iadc_read(iadc, attr->index, &result);
+
+	if (rc)
+		return 0;
+
+	return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
+					"Result:%d\n", result.result_ua);
+}
+
+static struct sensor_device_attribute qpnp_adc_attr =
+	SENSOR_ATTR(NULL, 0444, qpnp_iadc_show, NULL, 0);
+
+static int32_t qpnp_iadc_init_hwmon(struct qpnp_iadc_chip *iadc,
+						struct platform_device *pdev)
+{
+	struct device_node *child;
+	struct device_node *node = pdev->dev.of_node;
+	int rc = 0, i = 0, channel;
+
+	for_each_child_of_node(node, child) {
+		channel = iadc->adc->adc_channels[i].channel_num;
+		qpnp_adc_attr.index = iadc->adc->adc_channels[i].channel_num;
+		qpnp_adc_attr.dev_attr.attr.name =
+						iadc->adc->adc_channels[i].name;
+		memcpy(&iadc->sens_attr[i], &qpnp_adc_attr,
+						sizeof(qpnp_adc_attr));
+		sysfs_attr_init(&iadc->sens_attr[i].dev_attr.attr);
+		rc = device_create_file(&pdev->dev,
+				&iadc->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"device_create_file failed for dev %s\n",
+				iadc->adc->adc_channels[i].name);
+			goto hwmon_err_sens;
+		}
+		i++;
+	}
+
+	return 0;
+hwmon_err_sens:
+	pr_err("Init HWMON failed for qpnp_iadc with %d\n", rc);
+	return rc;
+}
+
+static int qpnp_iadc_probe(struct platform_device *pdev)
+{
+	struct qpnp_iadc_chip *iadc;
+	struct qpnp_adc_drv *adc_qpnp;
+	struct device_node *node = pdev->dev.of_node;
+	struct device_node *child;
+	unsigned int base;
+	int rc, count_adc_channel_list = 0, i = 0;
+
+	for_each_child_of_node(node, child)
+		count_adc_channel_list++;
+
+	if (!count_adc_channel_list) {
+		pr_err("No channel listing\n");
+		return -EINVAL;
+	}
+
+	iadc = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_iadc_chip) +
+		(sizeof(struct sensor_device_attribute) *
+				count_adc_channel_list), GFP_KERNEL);
+	if (!iadc) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	adc_qpnp = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_adc_drv),
+			GFP_KERNEL);
+	if (!adc_qpnp)
+		return -ENOMEM;
+
+	adc_qpnp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!adc_qpnp->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	iadc->dev = &(pdev->dev);
+	iadc->adc = adc_qpnp;
+
+	rc = qpnp_adc_get_devicetree_data(pdev, iadc->adc);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to read device tree\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "batt-id-trim-cnst-rds",
+				  &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find batt-id-trim-cnst-rds in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+	iadc->batt_id_trim_cnst_rds = base;
+	rc = of_property_read_u32(node, "qcom,use-default-rds-trim",
+			&iadc->rds_trim_default_type);
+	if (rc)
+		pr_debug("No trim workaround needed\n");
+	else {
+		pr_debug("Use internal RDS trim workaround\n");
+		iadc->rds_trim_default_check = true;
+	}
+
+	iadc->vadc_dev = qpnp_get_vadc(&pdev->dev, "iadc");
+	if (IS_ERR(iadc->vadc_dev)) {
+		rc = PTR_ERR(iadc->vadc_dev);
+		if (rc != -EPROBE_DEFER)
+			pr_err("vadc property missing, rc=%d\n", rc);
+		return rc;
+	}
+
+	mutex_init(&iadc->adc->adc_lock);
+
+	rc = of_property_read_u32(node, "qcom,rsense",
+			&iadc->rsense);
+	if (rc)
+		pr_debug("Defaulting to internal rsense\n");
+	else {
+		pr_debug("Use external rsense\n");
+		iadc->external_rsense = true;
+	}
+
+	iadc->iadc_poll_eoc = of_property_read_bool(node,
+						"qcom,iadc-poll-eoc");
+	if (!iadc->iadc_poll_eoc) {
+		rc = devm_request_irq(&pdev->dev, iadc->adc->adc_irq_eoc,
+				qpnp_iadc_isr, IRQF_TRIGGER_RISING,
+				"qpnp_iadc_interrupt", iadc);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to request adc irq\n");
+			return rc;
+		}
+		enable_irq_wake(iadc->adc->adc_irq_eoc);
+	}
+
+	rc = qpnp_iadc_init_hwmon(iadc, pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to initialize qpnp hwmon adc\n");
+		return rc;
+	}
+	iadc->iadc_hwmon = hwmon_device_register(&iadc->adc->pdev->dev);
+
+	rc = qpnp_iadc_version_check(iadc);
+	if (rc) {
+		dev_err(&pdev->dev, "IADC version not supported\n");
+		goto fail;
+	}
+
+	iadc->max_channels_available = count_adc_channel_list;
+	INIT_WORK(&iadc->trigger_completion_work, qpnp_iadc_trigger_completion);
+	INIT_DELAYED_WORK(&iadc->iadc_work, qpnp_iadc_work);
+	rc = qpnp_iadc_comp_info(iadc);
+	if (rc) {
+		dev_err(&pdev->dev, "abstracting IADC comp info failed!\n");
+		goto fail;
+	}
+
+	rc = qpnp_iadc_rds_trim_update_check(iadc);
+	if (rc) {
+		dev_err(&pdev->dev, "Rds trim update failed!\n");
+		goto fail;
+	}
+
+	dev_set_drvdata(&pdev->dev, iadc);
+	list_add(&iadc->list, &qpnp_iadc_device_list);
+	rc = qpnp_iadc_calibrate_for_trim(iadc, true);
+	if (rc)
+		dev_err(&pdev->dev, "failed to calibrate for USR trim\n");
+
+	if (iadc->iadc_poll_eoc)
+		device_init_wakeup(iadc->dev, 1);
+
+	schedule_delayed_work(&iadc->iadc_work,
+			round_jiffies_relative(msecs_to_jiffies
+					(QPNP_IADC_CALIB_SECONDS)));
+	return 0;
+fail:
+	for_each_child_of_node(node, child) {
+		device_remove_file(&pdev->dev, &iadc->sens_attr[i].dev_attr);
+		i++;
+	}
+	hwmon_device_unregister(iadc->iadc_hwmon);
+
+	return rc;
+}
+
+static int qpnp_iadc_remove(struct platform_device *pdev)
+{
+	struct qpnp_iadc_chip *iadc = dev_get_drvdata(&pdev->dev);
+	struct device_node *node = pdev->dev.of_node;
+	struct device_node *child;
+	int i = 0;
+
+	cancel_delayed_work(&iadc->iadc_work);
+	for_each_child_of_node(node, child) {
+		device_remove_file(&pdev->dev, &iadc->sens_attr[i].dev_attr);
+		i++;
+	}
+	hwmon_device_unregister(iadc->iadc_hwmon);
+	if (iadc->iadc_poll_eoc)
+		pm_relax(iadc->dev);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id qpnp_iadc_match_table[] = {
+	{	.compatible = "qcom,qpnp-iadc",
+	},
+	{}
+};
+
+static struct platform_driver qpnp_iadc_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-iadc",
+		.of_match_table = qpnp_iadc_match_table,
+	},
+	.probe		= qpnp_iadc_probe,
+	.remove		= qpnp_iadc_remove,
+};
+
+static int __init qpnp_iadc_init(void)
+{
+	return platform_driver_register(&qpnp_iadc_driver);
+}
+module_init(qpnp_iadc_init);
+
+static void __exit qpnp_iadc_exit(void)
+{
+	platform_driver_unregister(&qpnp_iadc_driver);
+}
+module_exit(qpnp_iadc_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC current ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hwmon/qpnp-adc-voltage.c b/drivers/hwmon/qpnp-adc-voltage.c
new file mode 100644
index 0000000..6cd63b2
--- /dev/null
+++ b/drivers/hwmon/qpnp-adc-voltage.c
@@ -0,0 +1,2908 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/thermal.h>
+
+/* QPNP VADC register definition */
+#define QPNP_VADC_REVISION1				0x0
+#define QPNP_VADC_REVISION2				0x1
+#define QPNP_VADC_REVISION3				0x2
+#define QPNP_VADC_REVISION4				0x3
+#define QPNP_VADC_PERPH_TYPE				0x4
+#define QPNP_VADC_PERH_SUBTYPE				0x5
+
+#define QPNP_VADC_SUPPORTED_REVISION2			1
+
+#define QPNP_VADC_STATUS1					0x8
+#define QPNP_VADC_STATUS1_OP_MODE				4
+#define QPNP_VADC_STATUS1_MEAS_INTERVAL_EN_STS			BIT(2)
+#define QPNP_VADC_STATUS1_REQ_STS				BIT(1)
+#define QPNP_VADC_STATUS1_EOC					BIT(0)
+#define QPNP_VADC_STATUS1_REQ_STS_EOC_MASK			0x3
+#define QPNP_VADC_STATUS1_OP_MODE_MASK				0x18
+#define QPNP_VADC_MEAS_INT_MODE					0x2
+#define QPNP_VADC_MEAS_INT_MODE_MASK				0x10
+
+#define QPNP_VADC_STATUS2					0x9
+#define QPNP_VADC_STATUS2_CONV_SEQ_STATE				6
+#define QPNP_VADC_STATUS2_FIFO_NOT_EMPTY_FLAG			BIT(1)
+#define QPNP_VADC_STATUS2_CONV_SEQ_TIMEOUT_STS			BIT(0)
+#define QPNP_VADC_STATUS2_CONV_SEQ_STATE_SHIFT			4
+#define QPNP_VADC_CONV_TIMEOUT_ERR				2
+
+#define QPNP_VADC_MODE_CTL					0x40
+#define QPNP_VADC_OP_MODE_SHIFT					3
+#define QPNP_VADC_VREF_XO_THM_FORCE				BIT(2)
+#define QPNP_VADC_AMUX_TRIM_EN					BIT(1)
+#define QPNP_VADC_TRIM_EN					BIT(0)
+#define QPNP_VADC_EN_CTL1					0x46
+#define QPNP_VADC_EN						BIT(7)
+#define QPNP_VADC_CH_SEL_CTL					0x48
+#define QPNP_VADC_DIG_PARAM					0x50
+#define QPNP_VADC_DIG_DEC_RATIO_SEL_SHIFT			3
+#define QPNP_VADC_HW_SETTLE_DELAY				0x51
+#define QPNP_VADC_CONV_REQ					0x52
+#define QPNP_VADC_CONV_REQ_SET					BIT(7)
+#define QPNP_VADC_CONV_SEQ_CTL					0x54
+#define QPNP_VADC_CONV_SEQ_HOLDOFF_SHIFT				4
+#define QPNP_VADC_CONV_SEQ_TRIG_CTL				0x55
+#define QPNP_VADC_MEAS_INTERVAL_CTL				0x57
+#define QPNP_VADC_MEAS_INTERVAL_OP_CTL				0x59
+#define QPNP_VADC_MEAS_INTERVAL_OP_SET				BIT(7)
+
+#define QPNP_VADC_CONV_SEQ_FALLING_EDGE				0x0
+#define QPNP_VADC_CONV_SEQ_RISING_EDGE				0x1
+#define QPNP_VADC_CONV_SEQ_EDGE_SHIFT				7
+#define QPNP_VADC_FAST_AVG_CTL					0x5a
+
+#define QPNP_VADC_LOW_THR_LSB					0x5c
+#define QPNP_VADC_LOW_THR_MSB					0x5d
+#define QPNP_VADC_HIGH_THR_LSB					0x5e
+#define QPNP_VADC_HIGH_THR_MSB					0x5f
+#define QPNP_VADC_ACCESS					0xd0
+#define QPNP_VADC_ACCESS_DATA					0xa5
+#define QPNP_VADC_PERH_RESET_CTL3				0xda
+#define QPNP_FOLLOW_OTST2_RB					BIT(3)
+#define QPNP_FOLLOW_WARM_RB					BIT(2)
+#define QPNP_FOLLOW_SHUTDOWN1_RB				BIT(1)
+#define QPNP_FOLLOW_SHUTDOWN2_RB				BIT(0)
+
+#define QPNP_INT_TEST_VAL					0xE1
+
+#define QPNP_VADC_DATA0						0x60
+#define QPNP_VADC_DATA1						0x61
+#define QPNP_VADC_CONV_TIMEOUT_ERR				2
+#define QPNP_VADC_CONV_TIME_MIN					1000
+#define QPNP_VADC_CONV_TIME_MAX					1100
+#define QPNP_ADC_COMPLETION_TIMEOUT				HZ
+#define QPNP_VADC_ERR_COUNT					20
+#define QPNP_OP_MODE_SHIFT					3
+
+#define QPNP_VADC_THR_LSB_MASK(val)				(val & 0xff)
+#define QPNP_VADC_THR_MSB_MASK(val)			((val & 0xff00) >> 8)
+#define QPNP_MIN_TIME						2000
+#define QPNP_MAX_TIME						2000
+#define QPNP_RETRY						100
+#define QPNP_VADC_ABSOLUTE_RECALIB_OFFSET			8
+#define QPNP_VADC_RATIOMETRIC_RECALIB_OFFSET			12
+#define QPNP_VADC_RECALIB_MAXCNT				10
+#define QPNP_VADC_OFFSET_DUMP					8
+#define QPNP_VADC_REG_DUMP					14
+
+/* QPNP VADC refreshed register set */
+#define QPNP_VADC_HC1_STATUS1					0x8
+
+#define QPNP_VADC_HC1_DATA_HOLD_CTL				0x3f
+#define QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD			BIT(1)
+
+#define QPNP_VADC_HC1_ADC_DIG_PARAM				0x42
+#define QPNP_VADC_HC1_CAL_VAL					BIT(6)
+#define QPNP_VADC_HC1_CAL_VAL_SHIFT				6
+#define QPNP_VADC_HC1_CAL_SEL_MASK				0x30
+#define QPNP_VADC_HC1_CAL_SEL_SHIFT				4
+#define QPNP_VADC_HC1_DEC_RATIO_SEL				0xc
+#define QPNP_VADC_HC1_DEC_RATIO_SHIFT				2
+#define QPNP_VADC_HC1_FAST_AVG_CTL				0x43
+#define QPNP_VADC_HC1_FAST_AVG_SAMPLES_MASK			0x7
+#define QPNP_VADC_HC1_ADC_CH_SEL_CTL				0x44
+#define QPNP_VADC_HC1_DELAY_CTL					0x45
+#define QPNP_VADC_HC1_DELAY_CTL_MASK				0xf
+#define QPNP_VADC_MC1_EN_CTL1					0x46
+#define QPNP_VADC_HC1_ADC_EN					BIT(7)
+#define QPNP_VADC_MC1_CONV_REQ					0x47
+#define QPNP_VADC_HC1_CONV_REQ_START				BIT(7)
+
+#define QPNP_VADC_HC1_VBAT_MIN_THR0				0x48
+#define QPNP_VADC_HC1_VBAT_MIN_THR1				0x49
+
+#define QPNP_VADC_HC1_DATA0					0x50
+#define QPNP_VADC_HC1_DATA1					0x51
+#define QPNP_VADC_HC1_DATA_CHECK_USR				0x8000
+
+#define QPNP_VADC_HC1_VBAT_MIN_DATA0				0x52
+#define QPNP_VADC_MC1_VBAT_MIN_DATA1				0x53
+
+/*
+ * Conversion time varies between 213uS to 6827uS based on the decimation,
+ * clock rate, fast average samples with no measurement in queue.
+ */
+#define QPNP_VADC_HC1_CONV_TIME_MIN_US				213
+#define QPNP_VADC_HC1_CONV_TIME_MAX_US				214
+#define QPNP_VADC_HC1_ERR_COUNT					1600
+
+struct qpnp_vadc_mode_state {
+	bool				meas_int_mode;
+	bool				meas_int_request_in_queue;
+	bool				vadc_meas_int_enable;
+	struct qpnp_adc_tm_btm_param	*param;
+	struct qpnp_adc_amux		vadc_meas_amux;
+};
+
+struct qpnp_vadc_thermal_data {
+	bool thermal_node;
+	int thermal_chan;
+	enum qpnp_vadc_channels vadc_channel;
+	struct thermal_zone_device *tz_dev;
+	struct qpnp_vadc_chip *vadc_dev;
+};
+
+struct qpnp_vadc_chip {
+	struct device			*dev;
+	struct qpnp_adc_drv		*adc;
+	struct list_head		list;
+	struct device			*vadc_hwmon;
+	bool				vadc_init_calib;
+	int				max_channels_available;
+	bool				vadc_iadc_sync_lock;
+	u8				id;
+	struct work_struct		trigger_completion_work;
+	bool				vadc_poll_eoc;
+	bool				vadc_recalib_check;
+	u8				revision_ana_minor;
+	u8				revision_dig_major;
+	struct work_struct		trigger_high_thr_work;
+	struct work_struct		trigger_low_thr_work;
+	struct qpnp_vadc_mode_state	*state_copy;
+	struct qpnp_vadc_thermal_data	*vadc_therm_chan;
+	struct power_supply		*vadc_chg_vote;
+	bool				vadc_hc;
+	int				vadc_debug_count;
+	struct sensor_device_attribute	sens_attr[0];
+};
+
+LIST_HEAD(qpnp_vadc_device_list);
+
+static struct qpnp_vadc_scale_fn vadc_scale_fn[] = {
+	[SCALE_DEFAULT] = {qpnp_adc_scale_default},
+	[SCALE_BATT_THERM] = {qpnp_adc_scale_batt_therm},
+	[SCALE_PMIC_THERM] = {qpnp_adc_scale_pmic_therm},
+	[SCALE_XOTHERM] = {qpnp_adc_tdkntcg_therm},
+	[SCALE_THERM_100K_PULLUP] = {qpnp_adc_scale_therm_pu2},
+	[SCALE_THERM_150K_PULLUP] = {qpnp_adc_scale_therm_pu1},
+	[SCALE_QRD_BATT_THERM] = {qpnp_adc_scale_qrd_batt_therm},
+	[SCALE_QRD_SKUAA_BATT_THERM] = {qpnp_adc_scale_qrd_skuaa_batt_therm},
+	[SCALE_SMB_BATT_THERM] = {qpnp_adc_scale_smb_batt_therm},
+	[SCALE_QRD_SKUG_BATT_THERM] = {qpnp_adc_scale_qrd_skug_batt_therm},
+	[SCALE_QRD_SKUH_BATT_THERM] = {qpnp_adc_scale_qrd_skuh_batt_therm},
+	[SCALE_NCP_03WF683_THERM] = {qpnp_adc_scale_therm_ncp03},
+	[SCALE_QRD_SKUT1_BATT_THERM] = {qpnp_adc_scale_qrd_skut1_batt_therm},
+	[SCALE_PMI_CHG_TEMP] = {qpnp_adc_scale_pmi_chg_temp},
+};
+
+static struct qpnp_vadc_rscale_fn adc_vadc_rscale_fn[] = {
+	[SCALE_RVADC_ABSOLUTE] = {qpnp_vadc_absolute_rthr},
+};
+
+static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc);
+
+static int32_t qpnp_vadc_read_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
+						u8 *data, int len)
+{
+	int rc;
+
+	rc = regmap_bulk_read(vadc->adc->regmap,
+		(vadc->adc->offset + reg), data, len);
+	if (rc < 0) {
+		pr_err("qpnp adc read reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_vadc_write_reg(struct qpnp_vadc_chip *vadc, int16_t reg,
+						u8 *buf, int len)
+{
+	int rc;
+
+	rc = regmap_bulk_write(vadc->adc->regmap,
+		(vadc->adc->offset + reg), buf, len);
+	if (rc < 0) {
+		pr_err("qpnp adc write reg %d failed with %d\n", reg, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_vadc_is_valid(struct qpnp_vadc_chip *vadc)
+{
+	struct qpnp_vadc_chip *vadc_chip = NULL;
+
+	list_for_each_entry(vadc_chip, &qpnp_vadc_device_list, list)
+		if (vadc == vadc_chip)
+			return 0;
+
+	return -EINVAL;
+}
+
+static int32_t qpnp_vadc_warm_rst_configure(struct qpnp_vadc_chip *vadc)
+{
+	int rc = 0;
+	u8 data = 0, buf = 0;
+
+	buf = QPNP_VADC_ACCESS_DATA;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ACCESS, &buf, 1);
+	if (rc < 0) {
+		pr_err("VADC write access failed\n");
+		return rc;
+	}
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_PERH_RESET_CTL3, &data, 1);
+	if (rc < 0) {
+		pr_err("VADC perh reset ctl3 read failed\n");
+		return rc;
+	}
+
+	buf = QPNP_VADC_ACCESS_DATA;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_ACCESS, &buf, 1);
+	if (rc < 0) {
+		pr_err("VADC write access failed\n");
+		return rc;
+	}
+
+	data |= QPNP_FOLLOW_WARM_RB;
+
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_PERH_RESET_CTL3, &data, 1);
+	if (rc < 0) {
+		pr_err("VADC perh reset ctl3 write failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_vadc_mode_select(struct qpnp_vadc_chip *vadc, u8 mode_ctl)
+{
+	int rc;
+
+	mode_ctl |= (QPNP_VADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN);
+
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MODE_CTL, &mode_ctl, 1);
+	if (rc < 0)
+		pr_err("vadc write mode selection err:%d\n", rc);
+
+	return rc;
+}
+
+static int32_t qpnp_vadc_enable(struct qpnp_vadc_chip *vadc, bool state)
+{
+	int rc = 0;
+	u8 data = 0;
+
+	data = QPNP_VADC_EN;
+	if (state) {
+		if (vadc->adc->hkadc_ldo && vadc->adc->hkadc_ldo_ok) {
+			rc = qpnp_adc_enable_voltage(vadc->adc);
+			if (rc) {
+				pr_err("failed enabling VADC LDO\n");
+				return rc;
+			}
+		}
+
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_EN_CTL1, &data, 1);
+		if (rc < 0) {
+			pr_err("VADC enable failed\n");
+			return rc;
+		}
+	} else {
+		data = (~data & QPNP_VADC_EN);
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_EN_CTL1, &data, 1);
+		if (rc < 0) {
+			pr_err("VADC disable failed\n");
+			return rc;
+		}
+
+		if (vadc->adc->hkadc_ldo && vadc->adc->hkadc_ldo_ok)
+			qpnp_adc_disable_voltage(vadc->adc);
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_vadc_status_debug(struct qpnp_vadc_chip *vadc)
+{
+	int rc = 0, i = 0;
+	u8 buf[8], offset = 0;
+
+	if (vadc->vadc_debug_count < 3) {
+		for (i = 0; i < QPNP_VADC_REG_DUMP; i++) {
+			rc = qpnp_vadc_read_reg(vadc, offset, buf, 8);
+			if (rc) {
+				pr_err("debug register dump failed\n");
+				return rc;
+			}
+			offset += QPNP_VADC_OFFSET_DUMP;
+			pr_err("row%d: 0%x 0%x 0%x 0%x 0%x 0%x 0%x 0%x\n",
+				i, buf[0], buf[1], buf[2], buf[3], buf[4],
+				buf[5], buf[6], buf[7]);
+		}
+	} else
+		pr_debug("VADC peripheral dumps got printed before\n");
+
+	vadc->vadc_debug_count++;
+
+	rc = qpnp_vadc_enable(vadc, false);
+	if (rc < 0) {
+		pr_err("VADC disable failed with %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_vadc_hc_check_conversion_status(struct qpnp_vadc_chip *vadc)
+{
+	int rc = 0, count = 0;
+	u8 status1 = 0;
+
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
+		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+		if (rc < 0)
+			return rc;
+		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+		if (status1 == QPNP_VADC_STATUS1_EOC)
+			break;
+		usleep_range(QPNP_VADC_HC1_CONV_TIME_MIN_US,
+				QPNP_VADC_HC1_CONV_TIME_MAX_US);
+		count++;
+		if (count > QPNP_VADC_HC1_ERR_COUNT) {
+			pr_err("retry error exceeded\n");
+			rc = qpnp_vadc_status_debug(vadc);
+			if (rc < 0)
+				pr_err("VADC disable failed with %d\n", rc);
+			return -EINVAL;
+		}
+	}
+
+	return rc;
+}
+
+static int qpnp_vadc_hc_read_data(struct qpnp_vadc_chip *vadc, int *data)
+{
+	int rc = 0;
+	u8 buf = 0, rslt_lsb = 0, rslt_msb = 0;
+
+	/* Set hold bit */
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
+	if (rc) {
+		pr_err("debug register dump failed\n");
+		return rc;
+	}
+	buf |= QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
+	if (rc) {
+		pr_err("debug register dump failed\n");
+		return rc;
+	}
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA0, &rslt_lsb, 1);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed for data0\n");
+		return rc;
+	}
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_DATA1, &rslt_msb, 1);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed for data1\n");
+		return rc;
+	}
+
+	*data = (rslt_msb << 8) | rslt_lsb;
+
+	if (*data == QPNP_VADC_HC1_DATA_CHECK_USR) {
+		pr_err("Invalid data :0x%x\n", *data);
+		return -EINVAL;
+	}
+
+	rc = qpnp_vadc_enable(vadc, false);
+	if (rc) {
+		pr_err("VADC disable failed\n");
+		return rc;
+	}
+
+	/* De-assert hold bit */
+	buf &= ~QPNP_VADC_HC1_DATA_HOLD_CTL_FIELD;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_DATA_HOLD_CTL, &buf, 1);
+	if (rc)
+		pr_err("de-asserting hold bit failed\n");
+
+	return rc;
+}
+
+static void qpnp_vadc_hc_update_adc_dig_param(struct qpnp_vadc_chip *vadc,
+			struct qpnp_adc_amux_properties *amux_prop, u8 *data)
+{
+	/* Update CAL value */
+	*data &= ~QPNP_VADC_HC1_CAL_VAL;
+	*data |= (amux_prop->cal_val << QPNP_VADC_HC1_CAL_VAL_SHIFT);
+
+	/* Update CAL select */
+	*data &= ~QPNP_VADC_HC1_CAL_SEL_MASK;
+	*data |= (amux_prop->calib_type << QPNP_VADC_HC1_CAL_SEL_SHIFT);
+
+	/* Update Decimation ratio select */
+	*data &= ~QPNP_VADC_HC1_DEC_RATIO_SEL;
+	*data |= (amux_prop->decimation << QPNP_VADC_HC1_DEC_RATIO_SHIFT);
+
+	pr_debug("VADC_DIG_PARAM value:0x%x\n", *data);
+}
+
+static int qpnp_vadc_hc_configure(struct qpnp_vadc_chip *vadc,
+				struct qpnp_adc_amux_properties *amux_prop)
+{
+	int rc = 0;
+	u8 buf[6];
+
+	/* Read registers 0x42 through 0x46 */
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
+	if (rc < 0) {
+		pr_err("qpnp adc configure block read failed\n");
+		return rc;
+	}
+
+	/* ADC Digital param selection */
+	qpnp_vadc_hc_update_adc_dig_param(vadc, amux_prop, &buf[0]);
+
+	/* Update fast average sample value */
+	buf[1] &= (u8) ~QPNP_VADC_HC1_FAST_AVG_SAMPLES_MASK;
+	buf[1] |= amux_prop->fast_avg_setup;
+
+	/* Select ADC channel */
+	buf[2] = amux_prop->amux_channel;
+
+	/* Select hw settle delay for the channel */
+	buf[3] &= (u8) ~QPNP_VADC_HC1_DELAY_CTL_MASK;
+	buf[3] |= amux_prop->hw_settle_time;
+
+	/* Select ADC enable */
+	buf[4] |= QPNP_VADC_HC1_ADC_EN;
+
+	/* Select CONV request */
+	buf[5] |= QPNP_VADC_HC1_CONV_REQ_START;
+
+	if (!vadc->vadc_poll_eoc)
+		reinit_completion(&vadc->adc->adc_rslt_completion);
+
+	pr_debug("dig:0x%x, fast_avg:0x%x, channel:0x%x, hw_settle:0x%x\n",
+		buf[0], buf[1], buf[2], buf[3]);
+
+	/* Block register write from 0x42 through 0x46 */
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HC1_ADC_DIG_PARAM, buf, 6);
+	if (rc < 0) {
+		pr_err("qpnp adc block register configure failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *vadc,
+				enum qpnp_vadc_channels channel,
+				struct qpnp_vadc_result *result)
+{
+	int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
+	struct qpnp_adc_amux_properties amux_prop;
+
+	if (qpnp_vadc_is_valid(vadc))
+		return -EPROBE_DEFER;
+
+	mutex_lock(&vadc->adc->adc_lock);
+
+	while ((vadc->adc->adc_channels[dt_index].channel_num
+		!= channel) && (dt_index < vadc->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= vadc->max_channels_available) {
+		pr_err("not a valid VADC channel:%d\n", channel);
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	if (!vadc->vadc_init_calib) {
+		rc = qpnp_vadc_calib_device(vadc);
+		if (rc) {
+			pr_err("Calibration failed\n");
+			goto fail_unlock;
+		} else {
+			vadc->vadc_init_calib = true;
+		}
+	}
+
+	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
+	if (calib_type >= ADC_HC_CAL_SEL_NONE) {
+		pr_err("not a valid calib_type\n");
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	amux_prop.decimation =
+			vadc->adc->adc_channels[dt_index].adc_decimation;
+	amux_prop.calib_type = vadc->adc->adc_channels[dt_index].calib_type;
+	amux_prop.cal_val = vadc->adc->adc_channels[dt_index].cal_val;
+	amux_prop.fast_avg_setup =
+			vadc->adc->adc_channels[dt_index].fast_avg_setup;
+	amux_prop.amux_channel = channel;
+	amux_prop.hw_settle_time =
+			vadc->adc->adc_channels[dt_index].hw_settle_time;
+
+	rc = qpnp_vadc_hc_configure(vadc, &amux_prop);
+	if (rc < 0) {
+		pr_err("Configuring VADC channel failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	if (vadc->vadc_poll_eoc) {
+		rc = qpnp_vadc_hc_check_conversion_status(vadc);
+		if (rc < 0) {
+			pr_err("polling mode conversion failed\n");
+			goto fail_unlock;
+		}
+	} else {
+		rc = wait_for_completion_timeout(
+					&vadc->adc->adc_rslt_completion,
+					QPNP_ADC_COMPLETION_TIMEOUT);
+		if (!rc) {
+			rc = qpnp_vadc_hc_check_conversion_status(vadc);
+			if (rc < 0) {
+				pr_err("interrupt mode conversion failed\n");
+				goto fail_unlock;
+			}
+			pr_debug("End of conversion status set\n");
+		}
+	}
+
+	rc = qpnp_vadc_hc_read_data(vadc, &result->adc_code);
+	if (rc) {
+		pr_err("qpnp vadc read adc code failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	amux_prescaling =
+		vadc->adc->adc_channels[dt_index].chan_path_prescaling;
+
+	if (amux_prescaling >= PATH_SCALING_NONE) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
+		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
+	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
+		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+
+	scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
+	if (scale_type >= SCALE_NONE) {
+		rc = -EBADF;
+		goto fail_unlock;
+	}
+
+	/* Note: Scaling functions for VADC_HC do not need offset/gain */
+	vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
+		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
+
+	pr_debug("channel=0x%x, adc_code=0x%x adc_result=%lld\n",
+			channel, result->adc_code, result->physical);
+
+fail_unlock:
+	mutex_unlock(&vadc->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_vadc_hc_read);
+
+static int32_t qpnp_vadc_configure(struct qpnp_vadc_chip *vadc,
+			struct qpnp_adc_amux_properties *chan_prop)
+{
+	u8 decimation = 0, conv_sequence = 0, conv_sequence_trig = 0;
+	u8 mode_ctrl = 0, meas_int_op_ctl_data = 0, buf = 0;
+	int rc = 0;
+
+	/* Mode selection */
+	mode_ctrl |= ((chan_prop->mode_sel << QPNP_VADC_OP_MODE_SHIFT) |
+			(QPNP_VADC_TRIM_EN | QPNP_VADC_AMUX_TRIM_EN));
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MODE_CTL, &mode_ctrl, 1);
+	if (rc < 0) {
+		pr_err("Mode configure write error\n");
+		return rc;
+	}
+
+	/* Channel selection */
+	buf = chan_prop->amux_channel;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_CH_SEL_CTL, &buf, 1);
+	if (rc < 0) {
+		pr_err("Channel configure error\n");
+		return rc;
+	}
+
+	/* Digital parameter setup */
+	decimation = chan_prop->decimation <<
+				QPNP_VADC_DIG_DEC_RATIO_SEL_SHIFT;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_DIG_PARAM, &decimation, 1);
+	if (rc < 0) {
+		pr_err("Digital parameter configure write error\n");
+		return rc;
+	}
+
+	/* HW settling time delay */
+	buf = chan_prop->hw_settle_time;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HW_SETTLE_DELAY, &buf, 1);
+	if (rc < 0) {
+		pr_err("HW settling time setup error\n");
+		return rc;
+	}
+
+	pr_debug("mode:%d, channel:%d, decimation:%d, hw_settle:%d\n",
+		mode_ctrl, chan_prop->amux_channel, decimation,
+					chan_prop->hw_settle_time);
+
+	if (chan_prop->mode_sel == (ADC_OP_NORMAL_MODE <<
+					QPNP_VADC_OP_MODE_SHIFT)) {
+		/* Normal measurement mode */
+		buf = chan_prop->fast_avg_setup;
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_FAST_AVG_CTL,
+								&buf, 1);
+		if (rc < 0) {
+			pr_err("Fast averaging configure error\n");
+			return rc;
+		}
+		/* Ensure MEAS_INTERVAL_OP_CTL is set to 0 */
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MEAS_INTERVAL_OP_CTL,
+						&meas_int_op_ctl_data, 1);
+		if (rc < 0) {
+			pr_err("Measurement interval OP configure error\n");
+			return rc;
+		}
+	} else if (chan_prop->mode_sel == (ADC_OP_CONVERSION_SEQUENCER <<
+					QPNP_VADC_OP_MODE_SHIFT)) {
+		/* Conversion sequence mode */
+		conv_sequence = ((ADC_SEQ_HOLD_100US <<
+				QPNP_VADC_CONV_SEQ_HOLDOFF_SHIFT) |
+				ADC_CONV_SEQ_TIMEOUT_5MS);
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_CONV_SEQ_CTL,
+							&conv_sequence, 1);
+		if (rc < 0) {
+			pr_err("Conversion sequence error\n");
+			return rc;
+		}
+
+		conv_sequence_trig = ((QPNP_VADC_CONV_SEQ_RISING_EDGE <<
+				QPNP_VADC_CONV_SEQ_EDGE_SHIFT) |
+				chan_prop->trigger_channel);
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_CONV_SEQ_TRIG_CTL,
+							&conv_sequence_trig, 1);
+		if (rc < 0) {
+			pr_err("Conversion trigger error\n");
+			return rc;
+		}
+	} else if (chan_prop->mode_sel == ADC_OP_MEASUREMENT_INTERVAL) {
+		buf = QPNP_VADC_MEAS_INTERVAL_OP_SET;
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MEAS_INTERVAL_OP_CTL,
+					&buf, 1);
+		if (rc < 0) {
+			pr_err("Measurement interval OP configure error\n");
+			return rc;
+		}
+	}
+
+	if (!vadc->vadc_poll_eoc)
+		reinit_completion(&vadc->adc->adc_rslt_completion);
+
+	rc = qpnp_vadc_enable(vadc, true);
+	if (rc)
+		return rc;
+
+	if (!vadc->vadc_iadc_sync_lock) {
+		/* Request conversion */
+		buf = QPNP_VADC_CONV_REQ_SET;
+		rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_CONV_REQ, &buf, 1);
+		if (rc < 0) {
+			pr_err("Request conversion failed\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_vadc_read_conversion_result(struct qpnp_vadc_chip *vadc,
+								int32_t *data)
+{
+	uint8_t rslt_lsb, rslt_msb;
+	int rc = 0, status = 0;
+
+	status = qpnp_vadc_read_reg(vadc, QPNP_VADC_DATA0, &rslt_lsb, 1);
+	if (status < 0) {
+		pr_err("qpnp adc result read failed for data0\n");
+		goto fail;
+	}
+
+	status = qpnp_vadc_read_reg(vadc, QPNP_VADC_DATA1, &rslt_msb, 1);
+	if (status < 0) {
+		pr_err("qpnp adc result read failed for data1\n");
+		goto fail;
+	}
+
+	*data = (rslt_msb << 8) | rslt_lsb;
+
+fail:
+	rc = qpnp_vadc_enable(vadc, false);
+	if (rc)
+		return rc;
+
+	return status;
+}
+
+static int32_t qpnp_vadc_read_status(struct qpnp_vadc_chip *vadc, int mode_sel)
+{
+	u8 status1, status2, status2_conv_seq_state;
+	u8 status_err = QPNP_VADC_CONV_TIMEOUT_ERR;
+	int rc;
+
+	switch (mode_sel) {
+	case (ADC_OP_CONVERSION_SEQUENCER << QPNP_VADC_OP_MODE_SHIFT):
+		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+		if (rc) {
+			pr_err("qpnp_vadc read mask interrupt failed\n");
+			return rc;
+		}
+
+		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS2, &status2, 1);
+		if (rc) {
+			pr_err("qpnp_vadc read mask interrupt failed\n");
+			return rc;
+		}
+
+		if (!(status2 & ~QPNP_VADC_STATUS2_CONV_SEQ_TIMEOUT_STS) &&
+			(status1 & (~QPNP_VADC_STATUS1_REQ_STS |
+						QPNP_VADC_STATUS1_EOC))) {
+			rc = status_err;
+			return rc;
+		}
+
+		status2_conv_seq_state = status2 >>
+					QPNP_VADC_STATUS2_CONV_SEQ_STATE_SHIFT;
+		if (status2_conv_seq_state != ADC_CONV_SEQ_IDLE) {
+			pr_err("qpnp vadc seq error with status %d\n",
+						status2);
+			rc = -EINVAL;
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static void qpnp_vadc_work(struct work_struct *work)
+{
+	struct qpnp_vadc_chip *vadc = container_of(work,
+			struct qpnp_vadc_chip, trigger_completion_work);
+
+	if (qpnp_vadc_is_valid(vadc) < 0)
+		return;
+
+	complete(&vadc->adc->adc_rslt_completion);
+}
+
+static void qpnp_vadc_low_thr_fn(struct work_struct *work)
+{
+	struct qpnp_vadc_chip *vadc = container_of(work,
+			struct qpnp_vadc_chip, trigger_low_thr_work);
+
+	vadc->state_copy->meas_int_mode = false;
+	vadc->state_copy->meas_int_request_in_queue = false;
+	vadc->state_copy->param->threshold_notification(
+			ADC_TM_LOW_STATE,
+			vadc->state_copy->param->btm_ctx);
+}
+
+static void qpnp_vadc_high_thr_fn(struct work_struct *work)
+{
+	struct qpnp_vadc_chip *vadc = container_of(work,
+			struct qpnp_vadc_chip, trigger_high_thr_work);
+
+	vadc->state_copy->meas_int_mode = false;
+	vadc->state_copy->meas_int_request_in_queue = false;
+	vadc->state_copy->param->threshold_notification(
+			ADC_TM_HIGH_STATE,
+			vadc->state_copy->param->btm_ctx);
+}
+
+static irqreturn_t qpnp_vadc_isr(int irq, void *dev_id)
+{
+	struct qpnp_vadc_chip *vadc = dev_id;
+
+	schedule_work(&vadc->trigger_completion_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_vadc_low_thr_isr(int irq, void *data)
+{
+	struct qpnp_vadc_chip *vadc = data;
+	u8 mode_ctl = 0, mode = 0;
+	int rc = 0;
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_MODE_CTL, &mode, 1);
+	if (rc < 0) {
+		pr_err("mode ctl register read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (!(mode & QPNP_VADC_MEAS_INT_MODE_MASK)) {
+		pr_debug("Spurious VADC threshold 0x%x\n", mode);
+		return IRQ_HANDLED;
+	}
+
+	mode_ctl = ADC_OP_NORMAL_MODE;
+	/* Set measurement in single measurement mode */
+	qpnp_vadc_mode_select(vadc, mode_ctl);
+	qpnp_vadc_enable(vadc, false);
+	schedule_work(&vadc->trigger_low_thr_work);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t qpnp_vadc_high_thr_isr(int irq, void *data)
+{
+	struct qpnp_vadc_chip *vadc = data;
+	u8 mode_ctl = 0, mode = 0;
+	int rc = 0;
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_MODE_CTL, &mode, 1);
+	if (rc < 0) {
+		pr_err("mode ctl register read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (!(mode & QPNP_VADC_MEAS_INT_MODE_MASK)) {
+		pr_debug("Spurious VADC threshold 0x%x\n", mode);
+		return IRQ_HANDLED;
+	}
+
+	mode_ctl = ADC_OP_NORMAL_MODE;
+	/* Set measurement in single measurement mode */
+	qpnp_vadc_mode_select(vadc, mode_ctl);
+	qpnp_vadc_enable(vadc, false);
+	schedule_work(&vadc->trigger_high_thr_work);
+
+	return IRQ_HANDLED;
+}
+
+static int32_t qpnp_vadc_version_check(struct qpnp_vadc_chip *dev)
+{
+	uint8_t revision;
+	int rc;
+
+	rc = qpnp_vadc_read_reg(dev, QPNP_VADC_REVISION2, &revision, 1);
+	if (rc < 0) {
+		pr_err("qpnp adc result read failed with %d\n", rc);
+		return rc;
+	}
+
+	if (revision < QPNP_VADC_SUPPORTED_REVISION2) {
+		pr_err("VADC Version not supported\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int32_t
+	qpnp_vadc_channel_post_scaling_calib_check(struct qpnp_vadc_chip *vadc,
+								int channel)
+{
+	int version, rc = 0;
+
+	version = qpnp_adc_get_revid_version(vadc->dev);
+
+	if (version == QPNP_REV_ID_PM8950_1_0) {
+		if ((channel == LR_MUX7_HW_ID) ||
+			(channel == P_MUX2_1_1) ||
+			(channel == LR_MUX3_XO_THERM) ||
+			(channel == LR_MUX3_BUF_XO_THERM_BUF) ||
+			(channel == P_MUX4_1_1)) {
+			vadc->adc->amux_prop->chan_prop->calib_type =
+								CALIB_ABSOLUTE;
+			return rc;
+		}
+	}
+
+	return -EINVAL;
+}
+
+#define QPNP_VBAT_COEFF_1	3000
+#define QPNP_VBAT_COEFF_2	45810000
+#define QPNP_VBAT_COEFF_3	100000
+#define QPNP_VBAT_COEFF_4	3500
+#define QPNP_VBAT_COEFF_5	80000000
+#define QPNP_VBAT_COEFF_6	4400
+#define QPNP_VBAT_COEFF_7	32200000
+#define QPNP_VBAT_COEFF_8	3880
+#define QPNP_VBAT_COEFF_9	5770
+#define QPNP_VBAT_COEFF_10	3660
+#define QPNP_VBAT_COEFF_11	5320
+#define QPNP_VBAT_COEFF_12	8060000
+#define QPNP_VBAT_COEFF_13	102640000
+#define QPNP_VBAT_COEFF_14	22220000
+#define QPNP_VBAT_COEFF_15	83060000
+#define QPNP_VBAT_COEFF_16	2810
+#define QPNP_VBAT_COEFF_17	5260
+#define QPNP_VBAT_COEFF_18	8027
+#define QPNP_VBAT_COEFF_19	2347
+#define QPNP_VBAT_COEFF_20	6043
+#define QPNP_VBAT_COEFF_21	1914
+#define QPNP_VBAT_OFFSET_SMIC	9446
+#define QPNP_VBAT_OFFSET_GF	9441
+#define QPNP_OCV_OFFSET_SMIC	4596
+#define QPNP_OCV_OFFSET_GF	5896
+#define QPNP_VBAT_COEFF_22	6800
+#define QPNP_VBAT_COEFF_23	3500
+#define QPNP_VBAT_COEFF_24	4360
+#define QPNP_VBAT_COEFF_25	8060
+#define QPNP_VBAT_COEFF_26	7895
+#define QPNP_VBAT_COEFF_27	5658
+#define QPNP_VBAT_COEFF_28	5760
+#define QPNP_VBAT_COEFF_29	7900
+#define QPNP_VBAT_COEFF_30	5660
+#define QPNP_VBAT_COEFF_31	3620
+#define QPNP_VBAT_COEFF_32	1230
+#define QPNP_VBAT_COEFF_33	5760
+#define QPNP_VBAT_COEFF_34	4080
+#define QPNP_VBAT_COEFF_35	7000
+#define QPNP_VBAT_COEFF_36	3040
+#define QPNP_VBAT_COEFF_37	3850
+#define QPNP_VBAT_COEFF_38	5000
+#define QPNP_VBAT_COEFF_39	2610
+#define QPNP_VBAT_COEFF_40	4190
+#define QPNP_VBAT_COEFF_41	5800
+#define QPNP_VBAT_COEFF_42	2620
+#define QPNP_VBAT_COEFF_43	4030
+#define QPNP_VBAT_COEFF_44	3230
+#define QPNP_VBAT_COEFF_45	3450
+#define QPNP_VBAT_COEFF_46	2120
+#define QPNP_VBAT_COEFF_47	3560
+#define QPNP_VBAT_COEFF_48	2190
+#define QPNP_VBAT_COEFF_49	4180
+#define QPNP_VBAT_COEFF_50	27800000
+#define QPNP_VBAT_COEFF_51	5110
+#define QPNP_VBAT_COEFF_52	34444000
+
+static int32_t qpnp_ocv_comp(int64_t *result,
+			struct qpnp_vadc_chip *vadc, int64_t die_temp)
+{
+	int64_t temp_var = 0, offset = 0;
+	int64_t old = *result;
+	int version;
+
+	version = qpnp_adc_get_revid_version(vadc->dev);
+	if (version == -EINVAL)
+		return 0;
+
+	if (version == QPNP_REV_ID_8026_2_2) {
+		if (die_temp > 25000)
+			return 0;
+	}
+
+	switch (version) {
+	case QPNP_REV_ID_8941_3_1:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			 temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_4));
+			break;
+		default:
+		case COMP_ID_GF:
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_1));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_1_0:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			temp_var = (((die_temp *
+			(-QPNP_VBAT_COEFF_10))
+			- QPNP_VBAT_COEFF_14));
+			break;
+		default:
+		case COMP_ID_GF:
+			temp_var = (((die_temp *
+			(-QPNP_VBAT_COEFF_8))
+			+ QPNP_VBAT_COEFF_12));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_2_0:
+	case QPNP_REV_ID_8026_2_1:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_10));
+			break;
+		default:
+		case COMP_ID_GF:
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_8));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_2_2:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			*result -= QPNP_VBAT_COEFF_22;
+			temp_var = (die_temp - 25000) *
+					QPNP_VBAT_COEFF_24;
+			break;
+		default:
+		case COMP_ID_GF:
+			*result -= QPNP_VBAT_COEFF_22;
+			temp_var = (die_temp - 25000) *
+					QPNP_VBAT_COEFF_25;
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8110_2_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			*result -= QPNP_OCV_OFFSET_SMIC;
+			if (die_temp < 25000)
+				temp_var = QPNP_VBAT_COEFF_18;
+			else
+				temp_var = QPNP_VBAT_COEFF_19;
+			temp_var = (die_temp - 25000) * temp_var;
+			break;
+		default:
+		case COMP_ID_GF:
+			*result -= QPNP_OCV_OFFSET_GF;
+			if (die_temp < 25000)
+				temp_var = QPNP_VBAT_COEFF_20;
+			else
+				temp_var = QPNP_VBAT_COEFF_21;
+			temp_var = (die_temp - 25000) * temp_var;
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8916_1_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			if (die_temp < 25000)
+				temp_var = QPNP_VBAT_COEFF_26;
+			else
+				temp_var = QPNP_VBAT_COEFF_27;
+			temp_var = (die_temp - 25000) * temp_var;
+			break;
+		default:
+		case COMP_ID_GF:
+			offset = QPNP_OCV_OFFSET_GF;
+			if (die_temp < 25000)
+				temp_var = QPNP_VBAT_COEFF_26;
+			else
+				temp_var = QPNP_VBAT_COEFF_27;
+			temp_var = (die_temp - 25000) * temp_var;
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8916_1_1:
+		switch (vadc->id) {
+		/* FAB_ID is zero */
+		case COMP_ID_GF:
+			if (die_temp < 25000)
+				temp_var = QPNP_VBAT_COEFF_29;
+			else
+				temp_var = QPNP_VBAT_COEFF_30;
+			temp_var = (die_temp - 25000) * temp_var;
+			break;
+		/* FAB_ID is non-zero */
+		default:
+			if (die_temp < 25000)
+				temp_var = QPNP_VBAT_COEFF_31;
+			else
+				temp_var = (-QPNP_VBAT_COEFF_32);
+			temp_var = (die_temp - 25000) * temp_var;
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8916_2_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			offset = (-QPNP_VBAT_COEFF_38);
+			if (die_temp < 0)
+				temp_var = die_temp * QPNP_VBAT_COEFF_36;
+			else if (die_temp > 40000)
+				temp_var = ((die_temp - 40000) *
+						(-QPNP_VBAT_COEFF_37));
+			break;
+		case COMP_ID_TSMC:
+			if (die_temp < 10000)
+				temp_var = ((die_temp - 10000) *
+						QPNP_VBAT_COEFF_41);
+			else if (die_temp > 50000)
+				temp_var = ((die_temp - 50000) *
+						(-QPNP_VBAT_COEFF_42));
+			break;
+		default:
+		case COMP_ID_GF:
+			if (die_temp < 20000)
+				temp_var = ((die_temp - 20000) *
+						QPNP_VBAT_COEFF_45);
+			else if (die_temp > 40000)
+				temp_var = ((die_temp - 40000) *
+						(-QPNP_VBAT_COEFF_46));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8909_1_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			temp_var = (-QPNP_VBAT_COEFF_50);
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8909_1_1:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			temp_var = (QPNP_VBAT_COEFF_52);
+			break;
+		}
+		break;
+	default:
+		temp_var = 0;
+		break;
+	}
+
+	temp_var = div64_s64(temp_var, QPNP_VBAT_COEFF_3);
+
+	temp_var = 1000000 + temp_var;
+
+	*result = *result * temp_var;
+
+	if (offset)
+		*result -= offset;
+
+	*result = div64_s64(*result, 1000000);
+	pr_debug("%lld compensated into %lld\n", old, *result);
+
+	return 0;
+}
+
+static int32_t qpnp_vbat_sns_comp(int64_t *result,
+			struct qpnp_vadc_chip *vadc, int64_t die_temp)
+{
+	int64_t temp_var = 0, offset = 0;
+	int64_t old = *result;
+	int version;
+
+	version = qpnp_adc_get_revid_version(vadc->dev);
+	if (version == -EINVAL)
+		return 0;
+
+	if (version != QPNP_REV_ID_8941_3_1) {
+		/* min(die_temp_c, 60_degC) */
+		if (die_temp > 60000)
+			die_temp = 60000;
+	}
+
+	switch (version) {
+	case QPNP_REV_ID_8941_3_1:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_1));
+			break;
+		default:
+		case COMP_ID_GF:
+			/* min(die_temp_c, 60_degC) */
+			if (die_temp > 60000)
+				die_temp = 60000;
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_1));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_1_0:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			temp_var = (((die_temp *
+			(-QPNP_VBAT_COEFF_11))
+			+ QPNP_VBAT_COEFF_15));
+			break;
+		default:
+		case COMP_ID_GF:
+			temp_var = (((die_temp *
+			(-QPNP_VBAT_COEFF_9))
+			+ QPNP_VBAT_COEFF_13));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_2_0:
+	case QPNP_REV_ID_8026_2_1:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_11));
+			break;
+		default:
+		case COMP_ID_GF:
+			temp_var = ((die_temp - 25000) *
+			(-QPNP_VBAT_COEFF_9));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8026_2_2:
+		switch (vadc->id) {
+		case COMP_ID_TSMC:
+			*result -= QPNP_VBAT_COEFF_23;
+			temp_var = 0;
+			break;
+		default:
+		case COMP_ID_GF:
+			*result -= QPNP_VBAT_COEFF_23;
+			temp_var = 0;
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8110_2_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			*result -= QPNP_VBAT_OFFSET_SMIC;
+			temp_var = ((die_temp - 25000) *
+			(QPNP_VBAT_COEFF_17));
+			break;
+		default:
+		case COMP_ID_GF:
+			*result -= QPNP_VBAT_OFFSET_GF;
+			temp_var = ((die_temp - 25000) *
+			(QPNP_VBAT_COEFF_16));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8916_1_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			temp_var = ((die_temp - 25000) *
+			(QPNP_VBAT_COEFF_28));
+			break;
+		default:
+		case COMP_ID_GF:
+			temp_var = ((die_temp - 25000) *
+			(QPNP_VBAT_COEFF_28));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8916_1_1:
+		switch (vadc->id) {
+		/* FAB_ID is zero */
+		case COMP_ID_GF:
+			temp_var = ((die_temp - 25000) *
+			(QPNP_VBAT_COEFF_33));
+			break;
+		/* FAB_ID is non-zero */
+		default:
+			offset = QPNP_VBAT_COEFF_35;
+			if (die_temp > 50000) {
+				temp_var = ((die_temp - 25000) *
+				(QPNP_VBAT_COEFF_34));
+			}
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8916_2_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			if (die_temp < 0) {
+				temp_var = (die_temp *
+					QPNP_VBAT_COEFF_39);
+			} else if (die_temp > 40000) {
+				temp_var = ((die_temp - 40000) *
+				(-QPNP_VBAT_COEFF_40));
+			}
+			break;
+		case COMP_ID_TSMC:
+			if (die_temp < 10000)
+				temp_var = ((die_temp - 10000) *
+					QPNP_VBAT_COEFF_43);
+			else if (die_temp > 50000)
+				temp_var = ((die_temp - 50000) *
+						(-QPNP_VBAT_COEFF_44));
+			break;
+		default:
+		case COMP_ID_GF:
+			if (die_temp < 20000)
+				temp_var = ((die_temp - 20000) *
+					QPNP_VBAT_COEFF_47);
+			else if (die_temp > 40000)
+				temp_var = ((die_temp - 40000) *
+						(-QPNP_VBAT_COEFF_48));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8909_1_0:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			if (die_temp < 30000)
+				temp_var = (-QPNP_VBAT_COEFF_50);
+			else if (die_temp > 30000)
+				temp_var = (((die_temp - 30000) *
+					(-QPNP_VBAT_COEFF_49)) +
+					(-QPNP_VBAT_COEFF_50));
+			break;
+		}
+		break;
+	case QPNP_REV_ID_8909_1_1:
+		switch (vadc->id) {
+		case COMP_ID_SMIC:
+			if (die_temp < 30000)
+				temp_var = (QPNP_VBAT_COEFF_52);
+			else if (die_temp > 30000)
+				temp_var = (((die_temp - 30000) *
+					(-QPNP_VBAT_COEFF_51)) +
+					(QPNP_VBAT_COEFF_52));
+			break;
+		}
+		break;
+	default:
+		temp_var = 0;
+		break;
+	}
+
+	temp_var = div64_s64(temp_var, QPNP_VBAT_COEFF_3);
+
+	temp_var = 1000000 + temp_var;
+
+	*result = *result * temp_var;
+
+	if (offset)
+		*result -= offset;
+
+	*result = div64_s64(*result, 1000000);
+	pr_debug("%lld compensated into %lld\n", old, *result);
+
+	return 0;
+}
+
+int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *vadc,
+					int64_t *result, bool is_pon_ocv)
+{
+	struct qpnp_vadc_result die_temp_result;
+	int rc = 0;
+
+	rc = qpnp_vadc_is_valid(vadc);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_vadc_conv_seq_request(vadc, ADC_SEQ_NONE,
+			DIE_TEMP, &die_temp_result);
+	if (rc < 0) {
+		pr_err("Error reading die_temp\n");
+		return rc;
+	}
+
+	pr_debug("die-temp = %lld\n", die_temp_result.physical);
+
+	if (is_pon_ocv)
+		rc = qpnp_ocv_comp(result, vadc, die_temp_result.physical);
+	else
+		rc = qpnp_vbat_sns_comp(result, vadc,
+				die_temp_result.physical);
+
+	if (rc < 0)
+		pr_err("Error with vbat compensation\n");
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_vbat_sns_comp_result);
+
+static void qpnp_vadc_625mv_channel_sel(struct qpnp_vadc_chip *vadc,
+				uint32_t *ref_channel_sel)
+{
+	uint32_t dt_index = 0;
+
+	/* Check if the buffered 625mV channel exists */
+	while ((vadc->adc->adc_channels[dt_index].channel_num
+		!= SPARE1) && (dt_index < vadc->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= vadc->max_channels_available) {
+		pr_debug("Use default 625mV ref channel\n");
+		*ref_channel_sel = REF_625MV;
+	} else {
+		pr_debug("Use buffered 625mV ref channel\n");
+		*ref_channel_sel = SPARE1;
+	}
+}
+
+int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
+					enum qpnp_adc_calib_type calib_type,
+					int *calib_data)
+{
+	struct qpnp_adc_amux_properties conv;
+	int rc, count = 0, calib_read = 0;
+	u8 status1 = 0;
+
+	if (vadc->vadc_hc) {
+		if (calib_type == ADC_HC_ABS_CAL)
+			conv.amux_channel = VADC_CALIB_VREF_1P25;
+		else if (calib_type == CALIB_RATIOMETRIC)
+			conv.amux_channel = VADC_CALIB_VREF;
+	} else {
+		if (calib_type == CALIB_ABSOLUTE)
+			conv.amux_channel = REF_125V;
+		else if (calib_type == CALIB_RATIOMETRIC)
+			conv.amux_channel = VDD_VADC;
+	}
+
+	conv.decimation = DECIMATION_TYPE2;
+	conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
+	conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
+	conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
+	conv.cal_val = calib_type;
+
+	if (vadc->vadc_hc) {
+		rc = qpnp_vadc_hc_configure(vadc, &conv);
+		if (rc) {
+			pr_err("qpnp_vadc configure failed with %d\n", rc);
+			goto calib_fail;
+		}
+	} else {
+		rc = qpnp_vadc_configure(vadc, &conv);
+		if (rc) {
+			pr_err("qpnp_vadc configure failed with %d\n", rc);
+			goto calib_fail;
+		}
+	}
+
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
+		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+		if (rc < 0)
+			return rc;
+		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+		usleep_range(QPNP_VADC_CONV_TIME_MIN,
+				QPNP_VADC_CONV_TIME_MAX);
+		count++;
+		if (count > QPNP_VADC_ERR_COUNT) {
+			rc = -ENODEV;
+			goto calib_fail;
+		}
+	}
+
+	if (vadc->vadc_hc) {
+		rc = qpnp_vadc_hc_read_data(vadc, &calib_read);
+		if (rc) {
+			pr_err("qpnp vadc read adc code failed with %d\n", rc);
+			goto calib_fail;
+		}
+	} else {
+		rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
+		if (rc) {
+			pr_err("qpnp adc read adc failed with %d\n", rc);
+			goto calib_fail;
+		}
+	}
+
+	*calib_data = calib_read;
+calib_fail:
+	return rc;
+}
+
+
+int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
+					enum qpnp_adc_calib_type calib_type,
+					int *calib_data)
+{
+	struct qpnp_adc_amux_properties conv;
+	int rc, count = 0, calib_read = 0;
+	u8 status1 = 0;
+	uint32_t ref_channel_sel = 0;
+
+	if (vadc->vadc_hc) {
+		conv.amux_channel = VADC_VREF_GND;
+	} else {
+		if (calib_type == CALIB_ABSOLUTE) {
+			qpnp_vadc_625mv_channel_sel(vadc, &ref_channel_sel);
+			conv.amux_channel = ref_channel_sel;
+		} else if (calib_type == CALIB_RATIOMETRIC)
+			conv.amux_channel = GND_REF;
+	}
+
+	conv.decimation = DECIMATION_TYPE2;
+	conv.mode_sel = ADC_OP_NORMAL_MODE << QPNP_VADC_OP_MODE_SHIFT;
+	conv.hw_settle_time = ADC_CHANNEL_HW_SETTLE_DELAY_0US;
+	conv.fast_avg_setup = ADC_FAST_AVG_SAMPLE_1;
+	conv.cal_val = calib_type;
+
+	if (vadc->vadc_hc) {
+		rc = qpnp_vadc_hc_configure(vadc, &conv);
+		if (rc) {
+			pr_err("qpnp_vadc configure failed with %d\n", rc);
+			goto calib_fail;
+		}
+	} else {
+		rc = qpnp_vadc_configure(vadc, &conv);
+		if (rc) {
+			pr_err("qpnp_vadc configure failed with %d\n", rc);
+			goto calib_fail;
+		}
+	}
+
+	while (status1 != QPNP_VADC_STATUS1_EOC) {
+		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+		if (rc < 0)
+			return rc;
+		status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+		usleep_range(QPNP_VADC_CONV_TIME_MIN,
+				QPNP_VADC_CONV_TIME_MAX);
+		count++;
+		if (count > QPNP_VADC_ERR_COUNT) {
+			rc = -ENODEV;
+			goto calib_fail;
+		}
+	}
+
+	if (vadc->vadc_hc) {
+		rc = qpnp_vadc_hc_read_data(vadc, &calib_read);
+		if (rc) {
+			pr_err("qpnp vadc read adc code failed with %d\n", rc);
+			goto calib_fail;
+		}
+	} else {
+		rc = qpnp_vadc_read_conversion_result(vadc, &calib_read);
+		if (rc) {
+			pr_err("qpnp adc read adc failed with %d\n", rc);
+			goto calib_fail;
+		}
+	}
+	*calib_data = calib_read;
+calib_fail:
+	return rc;
+}
+
+static int32_t qpnp_vadc_calib_device(struct qpnp_vadc_chip *vadc)
+{
+	int rc, calib_read_1 = 0, calib_read_2 = 0;
+	enum qpnp_adc_calib_type calib_type;
+
+	if (vadc->vadc_hc)
+		calib_type = ADC_HC_ABS_CAL;
+	else
+		calib_type = CALIB_ABSOLUTE;
+
+	rc = qpnp_vadc_calib_vref(vadc, calib_type, &calib_read_1);
+	if (rc) {
+		pr_err("qpnp adc absolute vref calib failed with %d\n", rc);
+		goto calib_fail;
+	}
+	rc = qpnp_vadc_calib_gnd(vadc, calib_type, &calib_read_2);
+	if (rc) {
+		pr_err("qpnp adc absolute gnd calib failed with %d\n", rc);
+		goto calib_fail;
+	}
+	pr_debug("absolute reference raw: 1.25V:0x%x, 625mV/GND:0x%x\n",
+				calib_read_1, calib_read_2);
+
+	if (calib_read_1 == calib_read_2) {
+		pr_err("absolute reference raw: 1.25V:0x%x625mV:0x%x\n",
+			calib_read_2, calib_read_1);
+		rc = -EINVAL;
+		goto calib_fail;
+	}
+
+	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy =
+				(calib_read_1 - calib_read_2);
+
+	if (calib_type == CALIB_ABSOLUTE)
+		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
+						= QPNP_ADC_625_UV;
+	else if (calib_type == ADC_HC_ABS_CAL)
+		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx
+						= QPNP_ADC_1P25_UV;
+	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref =
+					calib_read_1;
+	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd =
+					calib_read_2;
+
+	calib_read_1 = 0;
+	calib_read_2 = 0;
+	rc = qpnp_vadc_calib_vref(vadc, CALIB_RATIOMETRIC, &calib_read_1);
+	if (rc) {
+		pr_err("qpnp adc ratiometric vref calib failed with %d\n", rc);
+		goto calib_fail;
+	}
+	rc = qpnp_vadc_calib_gnd(vadc, CALIB_RATIOMETRIC, &calib_read_2);
+	if (rc) {
+		pr_err("qpnp adc ratiometric gnd calib failed with %d\n", rc);
+		goto calib_fail;
+	}
+	pr_debug("ratiometric reference raw: VDD:0x%x GND:0x%x\n",
+				calib_read_1, calib_read_2);
+
+	if (calib_read_1 == calib_read_2) {
+		pr_err("ratiometric reference raw: VDD:0x%x GND:0x%x\n",
+				calib_read_1, calib_read_2);
+		rc = -EINVAL;
+		goto calib_fail;
+	}
+
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dy =
+					(calib_read_1 - calib_read_2);
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx =
+					vadc->adc->adc_prop->adc_vdd_reference;
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_vref
+					= calib_read_1;
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd
+					= calib_read_2;
+
+calib_fail:
+	return rc;
+}
+
+int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *vadc,
+				struct qpnp_vadc_linear_graph *param,
+				enum qpnp_adc_calib_type calib_type)
+{
+	int rc = 0;
+	struct qpnp_vadc_result result;
+
+	rc = qpnp_vadc_is_valid(vadc);
+	if (rc < 0)
+		return rc;
+
+	if (!vadc->vadc_init_calib) {
+		if (vadc->vadc_hc) {
+			rc = qpnp_vadc_hc_read(vadc, VADC_CALIB_VREF_1P25,
+								&result);
+			if (rc) {
+				pr_debug("vadc read failed with rc = %d\n", rc);
+				return rc;
+			}
+		} else {
+			rc = qpnp_vadc_read(vadc, REF_125V, &result);
+			if (rc) {
+				pr_debug("vadc read failed with rc = %d\n", rc);
+				return rc;
+			}
+		}
+	}
+
+	switch (calib_type) {
+	case CALIB_RATIOMETRIC:
+	param->dy =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dy;
+	param->dx =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].dx;
+	param->adc_vref = vadc->adc->adc_prop->adc_vdd_reference;
+	param->adc_gnd =
+	vadc->adc->amux_prop->chan_prop->adc_graph[CALIB_RATIOMETRIC].adc_gnd;
+	break;
+	case CALIB_ABSOLUTE:
+	case ADC_HC_ABS_CAL:
+	param->dy =
+	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy;
+	param->dx =
+	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx;
+	param->adc_vref = vadc->adc->adc_prop->adc_vdd_reference;
+	param->adc_gnd =
+	vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd;
+	break;
+	default:
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_get_vadc_gain_and_offset);
+
+static int32_t qpnp_vadc_wait_for_req_sts_check(struct qpnp_vadc_chip *vadc)
+{
+	u8 status1 = 0;
+	int rc, count = 0;
+
+	/* Re-enable the peripheral */
+	rc = qpnp_vadc_enable(vadc, true);
+	if (rc) {
+		pr_err("vadc re-enable peripheral failed with %d\n", rc);
+		return rc;
+	}
+
+	/* The VADC_TM bank needs to be disabled for new conversion request */
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+	if (rc) {
+		pr_err("vadc read status1 failed with %d\n", rc);
+		return rc;
+	}
+
+	/* Disable the bank if a conversion is occurring */
+	while ((status1 & QPNP_VADC_STATUS1_REQ_STS) && (count < QPNP_RETRY)) {
+		/* Wait time is based on the optimum sampling rate
+		 * and adding enough time buffer to account for ADC conversions
+		 * occurring on different peripheral banks
+		 */
+		usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME);
+		rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+		if (rc < 0) {
+			pr_err("vadc disable failed with %d\n", rc);
+			return rc;
+		}
+		count++;
+	}
+
+	if (count >= QPNP_RETRY)
+		pr_err("QPNP vadc status req bit did not fall low!!\n");
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status1, 1);
+
+	/* Disable the peripheral */
+	rc = qpnp_vadc_enable(vadc, false);
+	if (rc < 0)
+		pr_err("vadc peripheral disable failed with %d\n", rc);
+
+	return rc;
+}
+
+static int32_t qpnp_vadc_manage_meas_int_requests(struct qpnp_vadc_chip *chip)
+{
+	struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev);
+	int rc = 0, dt_index = 0;
+	u8 mode_ctl = 0;
+
+	pr_debug("meas_int_mode:0x%x, mode_ctl:%0x\n",
+		vadc->state_copy->meas_int_mode, mode_ctl);
+
+	if (vadc->state_copy->meas_int_mode) {
+		pr_debug("meas interval in progress. Procced to disable it\n");
+		/* measurement interval in progress. Proceed to disable it */
+		mode_ctl = ADC_OP_NORMAL_MODE;
+		rc = qpnp_vadc_mode_select(vadc, mode_ctl);
+		if (rc < 0) {
+			pr_err("NORM mode select failed with %d\n", rc);
+			return rc;
+		}
+
+		/* Disable bank */
+		rc = qpnp_vadc_enable(vadc, false);
+		if (rc) {
+			pr_err("Disable bank failed with %d\n", rc);
+			return rc;
+		}
+
+		/* Check if a conversion is in progress */
+		rc = qpnp_vadc_wait_for_req_sts_check(vadc);
+		if (rc < 0) {
+			pr_err("req_sts check failed with %d\n", rc);
+			return rc;
+		}
+
+		vadc->state_copy->meas_int_mode = false;
+		vadc->state_copy->meas_int_request_in_queue = true;
+	} else if (vadc->state_copy->meas_int_request_in_queue) {
+		/* put the meas interval back in queue */
+		pr_debug("put meas interval back in queue\n");
+		vadc->adc->amux_prop->amux_channel =
+				vadc->state_copy->vadc_meas_amux.channel_num;
+		while ((vadc->adc->adc_channels[dt_index].channel_num
+			!= vadc->adc->amux_prop->amux_channel) &&
+			(dt_index < vadc->max_channels_available))
+			dt_index++;
+		if (dt_index >= vadc->max_channels_available) {
+			pr_err("not a valid VADC channel\n");
+			rc = -EINVAL;
+			return rc;
+		}
+
+		vadc->adc->amux_prop->decimation =
+			vadc->adc->amux_prop->decimation;
+		vadc->adc->amux_prop->hw_settle_time =
+			vadc->adc->amux_prop->hw_settle_time;
+		vadc->adc->amux_prop->fast_avg_setup =
+			vadc->adc->amux_prop->fast_avg_setup;
+		vadc->adc->amux_prop->mode_sel = ADC_OP_MEASUREMENT_INTERVAL;
+		rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop);
+		if (rc) {
+			pr_err("vadc configure failed with %d\n", rc);
+			return rc;
+		}
+
+		vadc->state_copy->meas_int_mode = true;
+		vadc->state_copy->meas_int_request_in_queue = false;
+	}
+	dev_set_drvdata(vadc->dev, vadc);
+
+	return 0;
+}
+
+struct qpnp_vadc_chip *qpnp_get_vadc(struct device *dev, const char *name)
+{
+	struct qpnp_vadc_chip *vadc;
+	struct device_node *node = NULL;
+	char prop_name[QPNP_MAX_PROP_NAME_LEN];
+
+	snprintf(prop_name, QPNP_MAX_PROP_NAME_LEN, "qcom,%s-vadc", name);
+
+	node = of_parse_phandle(dev->of_node, prop_name, 0);
+	if (node == NULL)
+		return ERR_PTR(-ENODEV);
+
+	list_for_each_entry(vadc, &qpnp_vadc_device_list, list)
+		if (vadc->adc->pdev->dev.of_node == node)
+			return vadc;
+	return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(qpnp_get_vadc);
+
+int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *vadc,
+				enum qpnp_vadc_trigger trigger_channel,
+					enum qpnp_vadc_channels channel,
+					struct qpnp_vadc_result *result)
+{
+	int rc = 0, scale_type, amux_prescaling, dt_index = 0, calib_type = 0;
+	uint32_t ref_channel, count = 0, local_idx = 0;
+	int32_t vref_calib = 0, gnd_calib = 0, new_vref_calib = 0, offset = 0;
+	int32_t calib_offset = 0;
+	u8 status1 = 0;
+
+	if (qpnp_vadc_is_valid(vadc))
+		return -EPROBE_DEFER;
+
+	mutex_lock(&vadc->adc->adc_lock);
+
+	if (vadc->state_copy->vadc_meas_int_enable)
+		qpnp_vadc_manage_meas_int_requests(vadc);
+
+	if (channel == REF_625MV) {
+		qpnp_vadc_625mv_channel_sel(vadc, &ref_channel);
+		channel = ref_channel;
+	}
+
+	vadc->adc->amux_prop->amux_channel = channel;
+
+	while ((vadc->adc->adc_channels[dt_index].channel_num
+		!= channel) && (dt_index < vadc->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= vadc->max_channels_available) {
+		pr_err("not a valid VADC channel\n");
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
+	if (calib_type >= CALIB_NONE) {
+		pr_err("not a valid calib_type\n");
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+	calib_offset = (calib_type == CALIB_ABSOLUTE) ?
+		QPNP_VADC_ABSOLUTE_RECALIB_OFFSET :
+		QPNP_VADC_RATIOMETRIC_RECALIB_OFFSET;
+	rc = qpnp_vadc_version_check(vadc);
+	if (rc)
+		goto fail_unlock;
+	if (vadc->vadc_recalib_check) {
+		rc = qpnp_vadc_calib_vref(vadc, calib_type, &vref_calib);
+		if (rc) {
+			pr_err("Calibration failed\n");
+			goto fail_unlock;
+		}
+	} else if (!vadc->vadc_init_calib) {
+		rc = qpnp_vadc_calib_device(vadc);
+		if (rc) {
+			pr_err("Calibration failed\n");
+			goto fail_unlock;
+		} else {
+			vadc->vadc_init_calib = true;
+		}
+	}
+
+recalibrate:
+	status1 = 0;
+	vadc->adc->amux_prop->decimation =
+			vadc->adc->adc_channels[dt_index].adc_decimation;
+	vadc->adc->amux_prop->hw_settle_time =
+			vadc->adc->adc_channels[dt_index].hw_settle_time;
+	vadc->adc->amux_prop->fast_avg_setup =
+			vadc->adc->adc_channels[dt_index].fast_avg_setup;
+
+	if (trigger_channel < ADC_SEQ_NONE)
+		vadc->adc->amux_prop->mode_sel = (ADC_OP_CONVERSION_SEQUENCER
+						<< QPNP_VADC_OP_MODE_SHIFT);
+	else if (trigger_channel == ADC_SEQ_NONE)
+		vadc->adc->amux_prop->mode_sel = (ADC_OP_NORMAL_MODE
+						<< QPNP_VADC_OP_MODE_SHIFT);
+	else {
+		pr_err("Invalid trigger channel:%d\n", trigger_channel);
+		goto fail_unlock;
+	}
+
+	vadc->adc->amux_prop->trigger_channel = trigger_channel;
+
+	rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop);
+	if (rc) {
+		pr_err("qpnp vadc configure failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	if (vadc->vadc_poll_eoc) {
+		while (status1 != QPNP_VADC_STATUS1_EOC) {
+			rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1,
+							&status1, 1);
+			if (rc < 0)
+				goto fail_unlock;
+			status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+			if (status1 == QPNP_VADC_STATUS1_EOC)
+				break;
+			usleep_range(QPNP_VADC_CONV_TIME_MIN,
+					QPNP_VADC_CONV_TIME_MAX);
+			count++;
+			if (count > QPNP_VADC_ERR_COUNT) {
+				pr_err("retry error exceeded\n");
+				rc = qpnp_vadc_status_debug(vadc);
+				if (rc < 0)
+					pr_err("VADC disable failed\n");
+				rc = -EINVAL;
+				goto fail_unlock;
+			}
+		}
+	} else {
+		rc = wait_for_completion_timeout(
+					&vadc->adc->adc_rslt_completion,
+					QPNP_ADC_COMPLETION_TIMEOUT);
+		if (!rc) {
+			rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1,
+							&status1, 1);
+			if (rc < 0)
+				goto fail_unlock;
+			status1 &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+			if (status1 == QPNP_VADC_STATUS1_EOC)
+				pr_debug("End of conversion status set\n");
+			else {
+				rc = qpnp_vadc_status_debug(vadc);
+				if (rc < 0)
+					pr_err("VADC disable failed\n");
+				rc = -EINVAL;
+				goto fail_unlock;
+			}
+		}
+	}
+
+	if (trigger_channel < ADC_SEQ_NONE) {
+		rc = qpnp_vadc_read_status(vadc,
+					vadc->adc->amux_prop->mode_sel);
+		if (rc)
+			pr_debug("Conversion sequence timed out - %d\n", rc);
+	}
+
+	rc = qpnp_vadc_read_conversion_result(vadc, &result->adc_code);
+	if (rc) {
+		pr_err("qpnp vadc read adc code failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	if (vadc->vadc_recalib_check) {
+		rc = qpnp_vadc_calib_gnd(vadc, calib_type, &gnd_calib);
+		if (rc) {
+			pr_err("Calibration failed\n");
+			goto fail_unlock;
+		}
+		rc = qpnp_vadc_calib_vref(vadc, calib_type, &new_vref_calib);
+		if (rc < 0) {
+			pr_err("qpnp vadc calib read failed with %d\n", rc);
+			goto fail_unlock;
+		}
+
+		if (local_idx >= QPNP_VADC_RECALIB_MAXCNT) {
+			pr_err("invalid recalib count=%d\n", local_idx);
+			rc = -EINVAL;
+			goto fail_unlock;
+		}
+		pr_debug(
+			"chan=%d, calib=%s, vref_calib=0x%x, gnd_calib=0x%x, new_vref_calib=0x%x\n",
+			channel,
+			((calib_type == CALIB_ABSOLUTE) ?
+			"ABSOLUTE" : "RATIOMETRIC"),
+			vref_calib, gnd_calib, new_vref_calib);
+
+		offset = (new_vref_calib - vref_calib);
+		if (offset < 0)
+			offset = -offset;
+		if (offset <= calib_offset) {
+			pr_debug(
+				"qpnp vadc recalibration not required,offset:%d\n",
+								offset);
+			local_idx = 0;
+		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dy =
+						(vref_calib - gnd_calib);
+		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].dx =
+			(calib_type == CALIB_ABSOLUTE) ? QPNP_ADC_625_UV :
+					vadc->adc->adc_prop->adc_vdd_reference;
+		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_vref
+								= vref_calib;
+		vadc->adc->amux_prop->chan_prop->adc_graph[calib_type].adc_gnd
+								= gnd_calib;
+		} else {
+			vref_calib = new_vref_calib;
+			local_idx = local_idx + 1;
+			if (local_idx >= QPNP_VADC_RECALIB_MAXCNT) {
+				pr_err(
+				"qpnp_vadc recalibration failed, count=%d",
+								local_idx);
+			} else {
+				pr_debug(
+				"qpnp vadc recalibration requested,offset:%d\n",
+								offset);
+				offset = 0;
+				goto recalibrate;
+			}
+		}
+	}
+
+	amux_prescaling =
+		vadc->adc->adc_channels[dt_index].chan_path_prescaling;
+
+	if (amux_prescaling >= PATH_SCALING_NONE) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
+		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
+	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
+		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+	vadc->adc->amux_prop->chan_prop->calib_type =
+		vadc->adc->adc_channels[dt_index].calib_type;
+
+	scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
+	if (scale_type >= SCALE_NONE) {
+		rc = -EBADF;
+		goto fail_unlock;
+	}
+
+	if ((qpnp_vadc_channel_post_scaling_calib_check(vadc, channel)) < 0)
+		pr_debug("Post scaling calib type not updated\n");
+
+	vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
+		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
+
+	pr_debug("channel=%d, adc_code=%d adc_result=%lld\n",
+			channel, result->adc_code, result->physical);
+
+fail_unlock:
+	if (vadc->state_copy->vadc_meas_int_enable)
+		qpnp_vadc_manage_meas_int_requests(vadc);
+
+	mutex_unlock(&vadc->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_vadc_conv_seq_request);
+
+int32_t qpnp_vadc_read(struct qpnp_vadc_chip *vadc,
+				enum qpnp_vadc_channels channel,
+				struct qpnp_vadc_result *result)
+{
+	struct qpnp_vadc_result die_temp_result;
+	int rc = 0;
+	enum power_supply_property prop;
+	union power_supply_propval ret = {0, };
+
+	if (vadc->vadc_hc) {
+		rc = qpnp_vadc_hc_read(vadc, channel, result);
+		if (rc < 0) {
+			pr_err("Error reading vadc_hc channel %d\n", channel);
+			return rc;
+		}
+
+		return 0;
+	}
+
+	if (channel == VBAT_SNS) {
+		rc = qpnp_vadc_conv_seq_request(vadc, ADC_SEQ_NONE,
+				channel, result);
+		if (rc < 0) {
+			pr_err("Error reading vbatt\n");
+			return rc;
+		}
+
+		rc = qpnp_vadc_conv_seq_request(vadc, ADC_SEQ_NONE,
+				DIE_TEMP, &die_temp_result);
+		if (rc < 0) {
+			pr_err("Error reading die_temp\n");
+			return rc;
+		}
+
+		rc = qpnp_vbat_sns_comp(&result->physical, vadc,
+						die_temp_result.physical);
+		if (rc < 0)
+			pr_err("Error with vbat compensation\n");
+
+		return 0;
+	} else if (channel == SPARE2) {
+		/* chg temp channel */
+		if (!vadc->vadc_chg_vote) {
+			vadc->vadc_chg_vote =
+				power_supply_get_by_name("battery");
+			if (!vadc->vadc_chg_vote) {
+				pr_err("no vadc_chg_vote found\n");
+				return -EINVAL;
+			}
+		}
+
+		prop = POWER_SUPPLY_PROP_FORCE_TLIM;
+		ret.intval = 1;
+
+		rc = power_supply_set_property(vadc->vadc_chg_vote,
+								prop, &ret);
+		if (rc) {
+			pr_err("error enabling the charger circuitry vote\n");
+			return rc;
+		}
+
+		rc = qpnp_vadc_conv_seq_request(vadc, ADC_SEQ_NONE,
+				channel, result);
+		if (rc < 0)
+			pr_err("Error reading die_temp\n");
+
+		ret.intval = 0;
+		rc = power_supply_set_property(vadc->vadc_chg_vote,
+								prop, &ret);
+		if (rc) {
+			pr_err("error enabling the charger circuitry vote\n");
+			return rc;
+		}
+
+		return 0;
+	} else
+		return qpnp_vadc_conv_seq_request(vadc, ADC_SEQ_NONE,
+				channel, result);
+}
+EXPORT_SYMBOL(qpnp_vadc_read);
+
+static void qpnp_vadc_lock(struct qpnp_vadc_chip *vadc)
+{
+	mutex_lock(&vadc->adc->adc_lock);
+}
+
+static void qpnp_vadc_unlock(struct qpnp_vadc_chip *vadc)
+{
+	mutex_unlock(&vadc->adc->adc_lock);
+}
+
+int32_t qpnp_vadc_iadc_sync_request(struct qpnp_vadc_chip *vadc,
+				enum qpnp_vadc_channels channel)
+{
+	int rc = 0, dt_index = 0, calib_type = 0;
+
+	if (qpnp_vadc_is_valid(vadc))
+		return -EPROBE_DEFER;
+
+	qpnp_vadc_lock(vadc);
+
+
+	vadc->adc->amux_prop->amux_channel = channel;
+
+	while ((vadc->adc->adc_channels[dt_index].channel_num
+		!= channel) && (dt_index < vadc->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= vadc->max_channels_available) {
+		pr_err("not a valid VADC channel\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	calib_type = vadc->adc->adc_channels[dt_index].calib_type;
+	if (!vadc->vadc_init_calib) {
+		rc = qpnp_vadc_version_check(vadc);
+		if (rc)
+			goto fail;
+
+		rc = qpnp_vadc_calib_device(vadc);
+		if (rc) {
+			pr_err("Calibration failed\n");
+			goto fail;
+		} else
+			vadc->vadc_init_calib = true;
+	}
+
+	vadc->adc->amux_prop->decimation =
+			vadc->adc->adc_channels[dt_index].adc_decimation;
+	vadc->adc->amux_prop->hw_settle_time =
+			vadc->adc->adc_channels[dt_index].hw_settle_time;
+	vadc->adc->amux_prop->fast_avg_setup =
+			vadc->adc->adc_channels[dt_index].fast_avg_setup;
+	vadc->adc->amux_prop->mode_sel = (ADC_OP_NORMAL_MODE
+					<< QPNP_VADC_OP_MODE_SHIFT);
+	vadc->vadc_iadc_sync_lock = true;
+
+	rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop);
+	if (rc) {
+		pr_err("qpnp vadc configure failed with %d\n", rc);
+		goto fail;
+	}
+
+	return rc;
+fail:
+	vadc->vadc_iadc_sync_lock = false;
+	qpnp_vadc_unlock(vadc);
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_vadc_iadc_sync_request);
+
+int32_t qpnp_vadc_iadc_sync_complete_request(struct qpnp_vadc_chip *vadc,
+					enum qpnp_vadc_channels channel,
+						struct qpnp_vadc_result *result)
+{
+	int rc = 0, scale_type, amux_prescaling, dt_index = 0;
+
+	vadc->adc->amux_prop->amux_channel = channel;
+
+	while ((vadc->adc->adc_channels[dt_index].channel_num
+		!= channel) && (dt_index < vadc->max_channels_available))
+		dt_index++;
+
+	rc = qpnp_vadc_read_conversion_result(vadc, &result->adc_code);
+	if (rc) {
+		pr_err("qpnp vadc read adc code failed with %d\n", rc);
+		goto fail;
+	}
+
+	amux_prescaling =
+		vadc->adc->adc_channels[dt_index].chan_path_prescaling;
+
+	if (amux_prescaling >= PATH_SCALING_NONE) {
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
+		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
+	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
+		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+
+	scale_type = vadc->adc->adc_channels[dt_index].adc_scale_fn;
+	if (scale_type >= SCALE_NONE) {
+		rc = -EBADF;
+		goto fail;
+	}
+
+	vadc_scale_fn[scale_type].chan(vadc, result->adc_code,
+		vadc->adc->adc_prop, vadc->adc->amux_prop->chan_prop, result);
+
+fail:
+	vadc->vadc_iadc_sync_lock = false;
+	qpnp_vadc_unlock(vadc);
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_vadc_iadc_sync_complete_request);
+
+static int32_t qpnp_vadc_thr_update(struct qpnp_vadc_chip *vadc,
+					int32_t high_thr, int32_t low_thr)
+{
+	int rc = 0;
+	u8 buf = 0;
+
+	pr_debug("client requested high:%d and low:%d\n",
+		high_thr, low_thr);
+
+	buf = QPNP_VADC_THR_LSB_MASK(low_thr);
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_LOW_THR_LSB, &buf, 1);
+	if (rc < 0) {
+		pr_err("low threshold lsb setting failed, err:%d\n", rc);
+		return rc;
+	}
+
+	buf = QPNP_VADC_THR_MSB_MASK(low_thr);
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_LOW_THR_MSB, &buf, 1);
+	if (rc < 0) {
+		pr_err("low threshold msb setting failed, err:%d\n", rc);
+		return rc;
+	}
+
+	buf = QPNP_VADC_THR_LSB_MASK(high_thr);
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HIGH_THR_LSB, &buf, 1);
+	if (rc < 0) {
+		pr_err("high threshold lsb setting failed, err:%d\n", rc);
+		return rc;
+	}
+
+	buf = QPNP_VADC_THR_MSB_MASK(high_thr);
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_HIGH_THR_MSB, &buf, 1);
+	if (rc < 0) {
+		pr_err("high threshold msb setting failed, err:%d\n", rc);
+		return rc;
+	}
+
+	pr_debug("client requested high:%d and low:%d\n", high_thr, low_thr);
+
+	return rc;
+}
+
+int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip,
+					struct qpnp_adc_tm_btm_param *param)
+{
+	uint32_t channel, scale_type = 0;
+	uint32_t low_thr = 0, high_thr = 0;
+	int rc = 0, idx = 0, amux_prescaling = 0;
+	struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev);
+	u8 buf = 0;
+
+	if (qpnp_vadc_is_valid(vadc))
+		return -EPROBE_DEFER;
+
+	if (!vadc->state_copy->vadc_meas_int_enable) {
+		pr_err("Recurring measurement interval not available\n");
+		return -EINVAL;
+	}
+
+	if (param->threshold_notification == NULL) {
+		pr_debug("No notification for high/low temp??\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&vadc->adc->adc_lock);
+
+	channel = param->channel;
+	while (idx < vadc->max_channels_available) {
+		if (vadc->adc->adc_channels[idx].channel_num == channel)
+			break;
+		idx++;
+	}
+
+	if (idx >= vadc->max_channels_available)  {
+		pr_err("not a valid VADC channel\n");
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	scale_type = vadc->adc->adc_channels[idx].adc_scale_fn;
+	if (scale_type >= SCALE_RVADC_SCALE_NONE) {
+		rc = -EBADF;
+		goto fail_unlock;
+	}
+
+	amux_prescaling =
+		vadc->adc->adc_channels[idx].chan_path_prescaling;
+
+	if (amux_prescaling >= PATH_SCALING_NONE) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	vadc->adc->amux_prop->chan_prop->offset_gain_numerator =
+		qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
+	vadc->adc->amux_prop->chan_prop->offset_gain_denominator =
+		 qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+	vadc->adc->amux_prop->chan_prop->calib_type =
+		vadc->adc->adc_channels[idx].calib_type;
+
+	pr_debug("channel:%d, scale_type:%d, dt_idx:%d",
+					channel, scale_type, idx);
+	vadc->adc->amux_prop->amux_channel = channel;
+	vadc->adc->amux_prop->decimation =
+			vadc->adc->adc_channels[idx].adc_decimation;
+	vadc->adc->amux_prop->hw_settle_time =
+			vadc->adc->adc_channels[idx].hw_settle_time;
+	vadc->adc->amux_prop->fast_avg_setup =
+			vadc->adc->adc_channels[idx].fast_avg_setup;
+	vadc->adc->amux_prop->mode_sel = ADC_OP_MEASUREMENT_INTERVAL;
+	adc_vadc_rscale_fn[scale_type].chan(vadc,
+			vadc->adc->amux_prop->chan_prop, param,
+			&low_thr, &high_thr);
+
+	if (param->timer_interval >= ADC_MEAS1_INTERVAL_NONE) {
+		pr_err("Invalid timer interval :%d\n", param->timer_interval);
+		goto fail_unlock;
+	}
+
+	buf = param->timer_interval;
+	rc = qpnp_vadc_write_reg(vadc, QPNP_VADC_MEAS_INTERVAL_CTL, &buf, 1);
+	if (rc) {
+		pr_err("vadc meas timer failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	rc = qpnp_vadc_thr_update(vadc, high_thr, low_thr);
+	if (rc) {
+		pr_err("vadc thr update failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	rc = qpnp_vadc_configure(vadc, vadc->adc->amux_prop);
+	if (rc) {
+		pr_err("vadc configure failed with %d\n", rc);
+		goto fail_unlock;
+	}
+
+	vadc->state_copy->meas_int_mode = true;
+	vadc->state_copy->param = param;
+	vadc->state_copy->vadc_meas_amux.channel_num = channel;
+	vadc->state_copy->vadc_meas_amux.adc_decimation =
+				vadc->adc->amux_prop->decimation;
+	vadc->state_copy->vadc_meas_amux.hw_settle_time =
+				vadc->adc->amux_prop->hw_settle_time;
+	vadc->state_copy->vadc_meas_amux.fast_avg_setup =
+				vadc->adc->amux_prop->fast_avg_setup;
+	vadc->state_copy->meas_int_request_in_queue = false;
+	dev_set_drvdata(vadc->dev, vadc);
+
+fail_unlock:
+	mutex_unlock(&vadc->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_vadc_channel_monitor);
+
+int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip)
+{
+	struct qpnp_vadc_chip *vadc = dev_get_drvdata(chip->dev);
+	u8 mode_ctl = 0;
+
+	if (qpnp_vadc_is_valid(vadc))
+		return -EPROBE_DEFER;
+
+	if (!vadc->state_copy->vadc_meas_int_enable) {
+		pr_err("Recurring measurement interval not available\n");
+		return -EINVAL;
+	}
+
+	vadc->state_copy->meas_int_mode = false;
+	vadc->state_copy->meas_int_request_in_queue = false;
+	dev_set_drvdata(vadc->dev, vadc);
+	mode_ctl = ADC_OP_NORMAL_MODE;
+	/* Set measurement in single measurement mode */
+	qpnp_vadc_mode_select(vadc, mode_ctl);
+	qpnp_vadc_enable(vadc, false);
+
+	return 0;
+}
+EXPORT_SYMBOL(qpnp_vadc_end_channel_monitor);
+
+static ssize_t qpnp_adc_show(struct device *dev,
+			struct device_attribute *devattr, char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+	struct qpnp_vadc_chip *vadc = dev_get_drvdata(dev);
+	struct qpnp_vadc_result result;
+	int rc = -1;
+
+	rc = qpnp_vadc_read(vadc, attr->index, &result);
+
+	if (rc) {
+		pr_err("VADC read error with %d\n", rc);
+		return 0;
+	}
+
+	return snprintf(buf, QPNP_ADC_HWMON_NAME_LENGTH,
+		"Result:%lld Raw:%x\n", result.physical, result.adc_code);
+}
+
+static struct sensor_device_attribute qpnp_adc_attr =
+	SENSOR_ATTR(NULL, 0444, qpnp_adc_show, NULL, 0);
+
+static int32_t qpnp_vadc_init_hwmon(struct qpnp_vadc_chip *vadc,
+					struct platform_device *pdev)
+{
+	struct device_node *child;
+	struct device_node *node = pdev->dev.of_node;
+	int rc = 0, i = 0, channel;
+
+	for_each_child_of_node(node, child) {
+		channel = vadc->adc->adc_channels[i].channel_num;
+		qpnp_adc_attr.index = vadc->adc->adc_channels[i].channel_num;
+		qpnp_adc_attr.dev_attr.attr.name =
+						vadc->adc->adc_channels[i].name;
+		memcpy(&vadc->sens_attr[i], &qpnp_adc_attr,
+						sizeof(qpnp_adc_attr));
+		sysfs_attr_init(&vadc->sens_attr[i].dev_attr.attr);
+		rc = device_create_file(&pdev->dev,
+				&vadc->sens_attr[i].dev_attr);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"device_create_file failed for dev %s\n",
+				vadc->adc->adc_channels[i].name);
+			goto hwmon_err_sens;
+		}
+		i++;
+	}
+
+	return 0;
+hwmon_err_sens:
+	pr_err("Init HWMON failed for qpnp_adc with %d\n", rc);
+	return rc;
+}
+
+static int qpnp_vadc_get_temp(struct thermal_zone_device *thermal,
+			     int *temp)
+{
+	struct qpnp_vadc_thermal_data *vadc_therm = thermal->devdata;
+	struct qpnp_vadc_chip *vadc = vadc_therm->vadc_dev;
+	struct qpnp_vadc_result result;
+	int rc = 0;
+
+	rc = qpnp_vadc_read(vadc,
+				vadc_therm->vadc_channel, &result);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			pr_err("VADC read error with %d\n", rc);
+		return rc;
+	}
+
+	*temp = result.physical;
+
+	return rc;
+}
+
+static struct thermal_zone_device_ops qpnp_vadc_thermal_ops = {
+	.get_temp = qpnp_vadc_get_temp,
+};
+
+static int32_t qpnp_vadc_init_thermal(struct qpnp_vadc_chip *vadc,
+					struct platform_device *pdev)
+{
+	struct device_node *child;
+	struct device_node *node = pdev->dev.of_node;
+	int rc = 0, i = 0;
+	bool thermal_node = false;
+
+	if (node == NULL)
+		goto thermal_err_sens;
+	for_each_child_of_node(node, child) {
+		char name[QPNP_THERMALNODE_NAME_LENGTH];
+
+		vadc->vadc_therm_chan[i].vadc_channel =
+			vadc->adc->adc_channels[i].channel_num;
+		vadc->vadc_therm_chan[i].thermal_chan = i;
+		thermal_node = of_property_read_bool(child,
+					"qcom,vadc-thermal-node");
+		if (thermal_node) {
+			/* Register with the thermal zone */
+			vadc->vadc_therm_chan[i].thermal_node = true;
+			snprintf(name, sizeof(name), "%s",
+				vadc->adc->adc_channels[i].name);
+			vadc->vadc_therm_chan[i].vadc_dev = vadc;
+			vadc->vadc_therm_chan[i].tz_dev =
+				thermal_zone_device_register(name,
+				0, 0, &vadc->vadc_therm_chan[i],
+				&qpnp_vadc_thermal_ops, NULL, 0, 0);
+			if (IS_ERR(vadc->vadc_therm_chan[i].tz_dev)) {
+				pr_err("thermal device register failed.\n");
+				goto thermal_err_sens;
+			}
+		}
+		i++;
+		thermal_node = false;
+	}
+	return 0;
+thermal_err_sens:
+	pr_err("Init HWMON failed for qpnp_adc with %d\n", rc);
+	return rc;
+}
+
+static const struct of_device_id qpnp_vadc_match_table[] = {
+	{	.compatible = "qcom,qpnp-vadc",
+	},
+	{	.compatible = "qcom,qpnp-vadc-hc",
+	},
+	{}
+};
+
+static int qpnp_vadc_probe(struct platform_device *pdev)
+{
+	struct qpnp_vadc_chip *vadc;
+	struct qpnp_adc_drv *adc_qpnp;
+	struct qpnp_vadc_thermal_data *adc_thermal;
+	struct device_node *node = pdev->dev.of_node;
+	struct device_node *child;
+	const struct of_device_id *id;
+	int rc, count_adc_channel_list = 0, i = 0;
+	u8 fab_id = 0;
+
+	for_each_child_of_node(node, child)
+		count_adc_channel_list++;
+
+	if (!count_adc_channel_list) {
+		pr_err("No channel listing\n");
+		return -EINVAL;
+	}
+
+	id = of_match_node(qpnp_vadc_match_table, node);
+	if (id == NULL) {
+		pr_err("qpnp_vadc_match of_node prop not present\n");
+		return -ENODEV;
+	}
+
+	vadc = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_vadc_chip) +
+		(sizeof(struct sensor_device_attribute) *
+				count_adc_channel_list), GFP_KERNEL);
+	if (!vadc) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	vadc->dev = &(pdev->dev);
+	adc_qpnp = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_adc_drv),
+			GFP_KERNEL);
+	if (!adc_qpnp)
+		return -ENOMEM;
+
+	adc_qpnp->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!adc_qpnp->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	vadc->state_copy = devm_kzalloc(&pdev->dev,
+			sizeof(struct qpnp_vadc_mode_state), GFP_KERNEL);
+	if (!vadc->state_copy)
+		return -ENOMEM;
+
+	vadc->adc = adc_qpnp;
+	adc_thermal = devm_kzalloc(&pdev->dev,
+			(sizeof(struct qpnp_vadc_thermal_data) *
+				count_adc_channel_list), GFP_KERNEL);
+	if (!adc_thermal) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	vadc->vadc_therm_chan = adc_thermal;
+	if (!strcmp(id->compatible, "qcom,qpnp-vadc-hc")) {
+		vadc->vadc_hc = true;
+		vadc->adc->adc_hc = true;
+	}
+
+	rc = qpnp_adc_get_devicetree_data(pdev, vadc->adc);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to read device tree\n");
+		return rc;
+	}
+	mutex_init(&vadc->adc->adc_lock);
+
+	rc = qpnp_vadc_init_hwmon(vadc, pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to initialize qpnp hwmon adc\n");
+		return rc;
+	}
+	vadc->vadc_hwmon = hwmon_device_register(&vadc->adc->pdev->dev);
+	rc = qpnp_vadc_init_thermal(vadc, pdev);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to initialize qpnp thermal adc\n");
+		return rc;
+	}
+	vadc->vadc_init_calib = false;
+	vadc->max_channels_available = count_adc_channel_list;
+	rc = qpnp_vadc_read_reg(vadc, QPNP_INT_TEST_VAL, &fab_id, 1);
+	if (rc < 0) {
+		pr_err("qpnp adc comp id failed with %d\n", rc);
+		goto err_setup;
+	}
+	vadc->id = fab_id;
+	pr_debug("fab_id = %d\n", fab_id);
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_REVISION2,
+				&vadc->revision_dig_major, 1);
+	if (rc < 0) {
+		pr_err("qpnp adc dig_major rev read failed with %d\n", rc);
+		goto err_setup;
+	}
+
+	rc = qpnp_vadc_read_reg(vadc, QPNP_VADC_REVISION3,
+				&vadc->revision_ana_minor, 1);
+	if (rc < 0) {
+		pr_err("qpnp adc ana_minor rev read failed with %d\n", rc);
+		goto err_setup;
+	}
+
+	rc = qpnp_vadc_warm_rst_configure(vadc);
+	if (rc < 0) {
+		pr_err("Setting perp reset on warm reset failed %d\n", rc);
+		goto err_setup;
+	}
+
+	INIT_WORK(&vadc->trigger_completion_work, qpnp_vadc_work);
+
+	vadc->vadc_recalib_check = of_property_read_bool(node,
+						"qcom,vadc-recalib-check");
+
+	vadc->vadc_poll_eoc = of_property_read_bool(node,
+						"qcom,vadc-poll-eoc");
+	if (!vadc->vadc_poll_eoc) {
+		rc = devm_request_irq(&pdev->dev, vadc->adc->adc_irq_eoc,
+				qpnp_vadc_isr, IRQF_TRIGGER_RISING,
+				"qpnp_vadc_interrupt", vadc);
+		if (rc) {
+			dev_err(&pdev->dev,
+			"failed to request adc irq with error %d\n", rc);
+			goto err_setup;
+		} else {
+			enable_irq_wake(vadc->adc->adc_irq_eoc);
+		}
+	} else
+		device_init_wakeup(vadc->dev, 1);
+
+	vadc->state_copy->vadc_meas_int_enable = of_property_read_bool(node,
+						"qcom,vadc-meas-int-mode");
+	if (vadc->state_copy->vadc_meas_int_enable) {
+		vadc->adc->adc_high_thr_irq = platform_get_irq_byname(pdev,
+								      "high-thr-en-set");
+		if (vadc->adc->adc_high_thr_irq < 0) {
+			pr_err("Invalid irq\n");
+			rc = -ENXIO;
+			goto err_setup;
+		}
+
+		vadc->adc->adc_low_thr_irq = platform_get_irq_byname(pdev,
+								     "low-thr-en-set");
+		if (vadc->adc->adc_low_thr_irq < 0) {
+			pr_err("Invalid irq\n");
+			rc = -ENXIO;
+			goto err_setup;
+		}
+
+		rc = devm_request_irq(&pdev->dev, vadc->adc->adc_high_thr_irq,
+					qpnp_vadc_high_thr_isr,
+			IRQF_TRIGGER_RISING, "qpnp_vadc_high_interrupt", vadc);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to request adc irq\n");
+			goto err_setup;
+		} else {
+			enable_irq_wake(vadc->adc->adc_high_thr_irq);
+		}
+
+		rc = devm_request_irq(&pdev->dev, vadc->adc->adc_low_thr_irq,
+					qpnp_vadc_low_thr_isr,
+			IRQF_TRIGGER_RISING, "qpnp_vadc_low_interrupt", vadc);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to request adc irq\n");
+			goto err_setup;
+		} else {
+			enable_irq_wake(vadc->adc->adc_low_thr_irq);
+		}
+		INIT_WORK(&vadc->trigger_high_thr_work,
+						qpnp_vadc_high_thr_fn);
+		INIT_WORK(&vadc->trigger_low_thr_work, qpnp_vadc_low_thr_fn);
+	}
+
+	vadc->vadc_iadc_sync_lock = false;
+	dev_set_drvdata(&pdev->dev, vadc);
+	list_add(&vadc->list, &qpnp_vadc_device_list);
+
+	return 0;
+
+err_setup:
+	for_each_child_of_node(node, child) {
+		device_remove_file(&pdev->dev, &vadc->sens_attr[i].dev_attr);
+		if (vadc->vadc_therm_chan[i].thermal_node)
+			thermal_zone_device_unregister(
+					vadc->vadc_therm_chan[i].tz_dev);
+		i++;
+	}
+	hwmon_device_unregister(vadc->vadc_hwmon);
+
+	return rc;
+}
+
+static int qpnp_vadc_remove(struct platform_device *pdev)
+{
+	struct qpnp_vadc_chip *vadc = dev_get_drvdata(&pdev->dev);
+	struct device_node *node = pdev->dev.of_node;
+	struct device_node *child;
+	int i = 0;
+
+	for_each_child_of_node(node, child) {
+		device_remove_file(&pdev->dev, &vadc->sens_attr[i].dev_attr);
+		if (vadc->vadc_therm_chan[i].thermal_node)
+			thermal_zone_device_unregister(
+					vadc->vadc_therm_chan[i].tz_dev);
+		i++;
+	}
+	hwmon_device_unregister(vadc->vadc_hwmon);
+	list_del(&vadc->list);
+	if (vadc->adc->hkadc_ldo && vadc->adc->hkadc_ldo_ok)
+		qpnp_adc_free_voltage_resource(vadc->adc);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static int qpnp_vadc_suspend_noirq(struct device *dev)
+{
+	struct qpnp_vadc_chip *vadc = dev_get_drvdata(dev);
+	u8 status = 0;
+
+	qpnp_vadc_read_reg(vadc, QPNP_VADC_STATUS1, &status, 1);
+	if (((status & QPNP_VADC_STATUS1_OP_MODE_MASK) >>
+		QPNP_VADC_OP_MODE_SHIFT) == QPNP_VADC_MEAS_INT_MODE) {
+		pr_debug("Meas interval in progress\n");
+	} else if (vadc->vadc_poll_eoc) {
+		status &= QPNP_VADC_STATUS1_REQ_STS_EOC_MASK;
+		pr_debug("vadc conversion status=%d\n", status);
+		if (status != QPNP_VADC_STATUS1_EOC) {
+			pr_err(
+				"Aborting suspend, adc conversion requested while suspending\n");
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static const struct dev_pm_ops qpnp_vadc_pm_ops = {
+	.suspend_noirq	= qpnp_vadc_suspend_noirq,
+};
+
+static struct platform_driver qpnp_vadc_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-vadc",
+		.of_match_table	= qpnp_vadc_match_table,
+		.pm		= &qpnp_vadc_pm_ops,
+	},
+	.probe		= qpnp_vadc_probe,
+	.remove		= qpnp_vadc_remove,
+};
+
+static int __init qpnp_vadc_init(void)
+{
+	return platform_driver_register(&qpnp_vadc_driver);
+}
+module_init(qpnp_vadc_init);
+
+static void __exit qpnp_vadc_exit(void)
+{
+	platform_driver_unregister(&qpnp_vadc_driver);
+}
+module_exit(qpnp_vadc_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC Voltage ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 99c0514..2efd8d0 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -408,6 +408,15 @@
 	  To compile this driver as a module, choose M here: the module will
 	  be called qcom-spmi-vadc.
 
+config QCOM_TADC
+	tristate "Qualcomm Technologies, Inc. TADC driver"
+	depends on MFD_I2C_PMIC
+	help
+	  Say yes here to support the Qualcomm Technologies, Inc. telemetry ADC.
+	  The TADC provides battery temperature, skin temperature,
+	  die temperature, battery voltage, battery current, input voltage,
+	  input current, and OTG current.
+
 config ROCKCHIP_SARADC
 	tristate "Rockchip SARADC driver"
 	depends on ARCH_ROCKCHIP || (ARM && COMPILE_TEST)
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..1da80bd 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -39,6 +39,7 @@
 obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
 obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
 obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
+obj-$(CONFIG_QCOM_TADC) += qcom-tadc.o
 obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_STX104) += stx104.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
diff --git a/drivers/iio/adc/qcom-tadc.c b/drivers/iio/adc/qcom-tadc.c
new file mode 100644
index 0000000..9241288
--- /dev/null
+++ b/drivers/iio/adc/qcom-tadc.c
@@ -0,0 +1,1089 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "TADC: %s: " fmt, __func__
+
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define TADC_REVISION1_REG			0x00
+#define TADC_REVISION2_REG			0x01
+#define TADC_REVISION3_REG			0x02
+#define TADC_REVISION4_REG			0x03
+#define TADC_PERPH_TYPE_REG			0x04
+#define TADC_PERPH_SUBTYPE_REG			0x05
+
+/* TADC register definitions */
+#define TADC_SW_CH_CONV_REG(chip)		(chip->tadc_base + 0x06)
+#define TADC_MBG_ERR_REG(chip)			(chip->tadc_base + 0x07)
+#define TADC_EN_CTL_REG(chip)			(chip->tadc_base + 0x46)
+#define TADC_CONV_REQ_REG(chip)			(chip->tadc_base + 0x51)
+#define TADC_HWTRIG_CONV_CH_EN_REG(chip)	(chip->tadc_base + 0x52)
+#define TADC_HW_SETTLE_DELAY_REG(chip)		(chip->tadc_base + 0x53)
+#define TADC_LONG_HW_SETTLE_DLY_EN_REG(chip)	(chip->tadc_base + 0x54)
+#define TADC_LONG_HW_SETTLE_DLY_REG(chip)	(chip->tadc_base + 0x55)
+#define TADC_ADC_BUF_CH_REG(chip)		(chip->tadc_base + 0x56)
+#define TADC_ADC_AAF_CH_REG(chip)		(chip->tadc_base + 0x57)
+#define TADC_ADC_DATA_RDBK_REG(chip)		(chip->tadc_base + 0x58)
+#define TADC_CH1_ADC_LO_REG(chip)		(chip->tadc_base + 0x60)
+#define TADC_CH1_ADC_HI_REG(chip)		(chip->tadc_base + 0x61)
+#define TADC_CH2_ADC_LO_REG(chip)		(chip->tadc_base + 0x62)
+#define TADC_CH2_ADC_HI_REG(chip)		(chip->tadc_base + 0x63)
+#define TADC_CH3_ADC_LO_REG(chip)		(chip->tadc_base + 0x64)
+#define TADC_CH3_ADC_HI_REG(chip)		(chip->tadc_base + 0x65)
+#define TADC_CH4_ADC_LO_REG(chip)		(chip->tadc_base + 0x66)
+#define TADC_CH4_ADC_HI_REG(chip)		(chip->tadc_base + 0x67)
+#define TADC_CH5_ADC_LO_REG(chip)		(chip->tadc_base + 0x68)
+#define TADC_CH5_ADC_HI_REG(chip)		(chip->tadc_base + 0x69)
+#define TADC_CH6_ADC_LO_REG(chip)		(chip->tadc_base + 0x70)
+#define TADC_CH6_ADC_HI_REG(chip)		(chip->tadc_base + 0x71)
+#define TADC_CH7_ADC_LO_REG(chip)		(chip->tadc_base + 0x72)
+#define TADC_CH7_ADC_HI_REG(chip)		(chip->tadc_base + 0x73)
+#define TADC_CH8_ADC_LO_REG(chip)		(chip->tadc_base + 0x74)
+#define TADC_CH8_ADC_HI_REG(chip)		(chip->tadc_base + 0x75)
+
+/* TADC_CMP register definitions */
+#define TADC_CMP_THR1_CMP_REG(chip)		(chip->tadc_cmp_base + 0x51)
+#define TADC_CMP_THR1_CH1_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x52)
+#define TADC_CMP_THR1_CH1_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x53)
+#define TADC_CMP_THR1_CH2_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x54)
+#define TADC_CMP_THR1_CH2_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x55)
+#define TADC_CMP_THR1_CH3_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x56)
+#define TADC_CMP_THR1_CH3_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x57)
+#define TADC_CMP_THR2_CMP_REG(chip)		(chip->tadc_cmp_base + 0x67)
+#define TADC_CMP_THR2_CH1_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x68)
+#define TADC_CMP_THR2_CH1_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x69)
+#define TADC_CMP_THR2_CH2_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x6A)
+#define TADC_CMP_THR2_CH2_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x6B)
+#define TADC_CMP_THR2_CH3_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x6C)
+#define TADC_CMP_THR2_CH3_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x6D)
+#define TADC_CMP_THR3_CMP_REG(chip)		(chip->tadc_cmp_base + 0x7D)
+#define TADC_CMP_THR3_CH1_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x7E)
+#define TADC_CMP_THR3_CH1_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x7F)
+#define TADC_CMP_THR3_CH2_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x80)
+#define TADC_CMP_THR3_CH2_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x81)
+#define TADC_CMP_THR3_CH3_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x82)
+#define TADC_CMP_THR3_CH3_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x83)
+#define TADC_CMP_THR4_CMP_REG(chip)		(chip->tadc_cmp_base + 0x93)
+#define TADC_CMP_THR4_CH1_CMP_LO_REG(chip)	(chip->tadc_cmp_base + 0x94)
+#define TADC_CMP_THR4_CH1_CMP_HI_REG(chip)	(chip->tadc_cmp_base + 0x95)
+#define TADC_CMP_THR1_CH1_HYST_REG(chip)	(chip->tadc_cmp_base + 0xB0)
+#define TADC_CMP_THR2_CH1_HYST_REG(chip)	(chip->tadc_cmp_base + 0xB1)
+#define TADC_CMP_THR3_CH1_HYST_REG(chip)	(chip->tadc_cmp_base + 0xB2)
+#define TADC_CMP_THR4_CH1_HYST_REG(chip)	(chip->tadc_cmp_base + 0xB3)
+
+/* 10 bits of resolution */
+#define TADC_RESOLUTION			1024
+/* number of hardware channels */
+#define TADC_NUM_CH			8
+
+enum tadc_chan_id {
+	TADC_THERM1 = 0,
+	TADC_THERM2,
+	TADC_DIE_TEMP,
+	TADC_BATT_I,
+	TADC_BATT_V,
+	TADC_INPUT_I,
+	TADC_INPUT_V,
+	TADC_OTG_I,
+	/* virtual channels */
+	TADC_BATT_P,
+	TADC_INPUT_P,
+	TADC_THERM1_THR1,
+	TADC_THERM1_THR2,
+	TADC_THERM1_THR3,
+	TADC_THERM1_THR4,
+	TADC_THERM2_THR1,
+	TADC_THERM2_THR2,
+	TADC_THERM2_THR3,
+	TADC_DIE_TEMP_THR1,
+	TADC_DIE_TEMP_THR2,
+	TADC_DIE_TEMP_THR3,
+	TADC_CHAN_ID_MAX,
+};
+
+#define TADC_CHAN(_name, _type, _channel, _info_mask)	\
+{							\
+	.type			= _type,		\
+	.channel		= _channel,		\
+	.info_mask_separate	= _info_mask,		\
+	.extend_name		= _name,		\
+}
+
+#define TADC_THERM_CHAN(_name, _channel)		\
+TADC_CHAN(_name, IIO_TEMP, _channel,			\
+	BIT(IIO_CHAN_INFO_RAW) |			\
+	BIT(IIO_CHAN_INFO_PROCESSED))
+
+#define TADC_TEMP_CHAN(_name, _channel)			\
+TADC_CHAN(_name, IIO_TEMP, _channel,			\
+	BIT(IIO_CHAN_INFO_RAW) |			\
+	BIT(IIO_CHAN_INFO_PROCESSED) |			\
+	BIT(IIO_CHAN_INFO_SCALE) |			\
+	BIT(IIO_CHAN_INFO_OFFSET))
+
+#define TADC_CURRENT_CHAN(_name, _channel)		\
+TADC_CHAN(_name, IIO_CURRENT, _channel,			\
+	BIT(IIO_CHAN_INFO_RAW) |			\
+	BIT(IIO_CHAN_INFO_PROCESSED) |			\
+	BIT(IIO_CHAN_INFO_SCALE))
+
+
+#define TADC_VOLTAGE_CHAN(_name, _channel)		\
+TADC_CHAN(_name, IIO_VOLTAGE, _channel,			\
+	BIT(IIO_CHAN_INFO_RAW) |			\
+	BIT(IIO_CHAN_INFO_PROCESSED) |			\
+	BIT(IIO_CHAN_INFO_SCALE))
+
+#define TADC_POWER_CHAN(_name, _channel)		\
+TADC_CHAN(_name, IIO_POWER, _channel,			\
+	BIT(IIO_CHAN_INFO_PROCESSED))
+
+static const struct iio_chan_spec tadc_iio_chans[] = {
+	[TADC_THERM1]		= TADC_THERM_CHAN(
+					"batt", TADC_THERM1),
+	[TADC_THERM2]		= TADC_THERM_CHAN(
+					"skin", TADC_THERM2),
+	[TADC_DIE_TEMP]		= TADC_TEMP_CHAN(
+					"die", TADC_DIE_TEMP),
+	[TADC_BATT_I]		= TADC_CURRENT_CHAN(
+					"batt", TADC_BATT_I),
+	[TADC_BATT_V]		= TADC_VOLTAGE_CHAN(
+					"batt", TADC_BATT_V),
+	[TADC_INPUT_I]		= TADC_CURRENT_CHAN(
+					"input", TADC_INPUT_I),
+	[TADC_INPUT_V]		= TADC_VOLTAGE_CHAN(
+					"input", TADC_INPUT_V),
+	[TADC_OTG_I]		= TADC_CURRENT_CHAN(
+					"otg", TADC_OTG_I),
+	[TADC_BATT_P]		= TADC_POWER_CHAN(
+					"batt", TADC_BATT_P),
+	[TADC_INPUT_P]		= TADC_POWER_CHAN(
+					"input", TADC_INPUT_P),
+	[TADC_THERM1_THR1]	= TADC_THERM_CHAN(
+					"batt_warm", TADC_THERM1_THR1),
+	[TADC_THERM1_THR2]	= TADC_THERM_CHAN(
+					"batt_cool", TADC_THERM1_THR2),
+	[TADC_THERM1_THR3]	= TADC_THERM_CHAN(
+					"batt_cold", TADC_THERM1_THR3),
+	[TADC_THERM1_THR4]	= TADC_THERM_CHAN(
+					"batt_hot", TADC_THERM1_THR4),
+	[TADC_THERM2_THR1]	= TADC_THERM_CHAN(
+					"skin_lb", TADC_THERM2_THR1),
+	[TADC_THERM2_THR2]	= TADC_THERM_CHAN(
+					"skin_ub", TADC_THERM2_THR2),
+	[TADC_THERM2_THR3]	= TADC_THERM_CHAN(
+					"skin_rst", TADC_THERM2_THR3),
+	[TADC_DIE_TEMP_THR1]	= TADC_THERM_CHAN(
+					"die_lb", TADC_DIE_TEMP_THR1),
+	[TADC_DIE_TEMP_THR2]	= TADC_THERM_CHAN(
+					"die_ub", TADC_DIE_TEMP_THR2),
+	[TADC_DIE_TEMP_THR3]	= TADC_THERM_CHAN(
+					"die_rst", TADC_DIE_TEMP_THR3),
+};
+
+struct tadc_therm_thr {
+	int	addr_lo;
+	int	addr_hi;
+};
+
+struct tadc_chan_data {
+	s32			scale;
+	s32			offset;
+	u32			rbias;
+	const struct tadc_pt	*table;
+	size_t			tablesize;
+	struct tadc_therm_thr	thr[4];
+};
+
+struct tadc_chip {
+	struct device		*dev;
+	struct regmap		*regmap;
+	u32			tadc_base;
+	u32			tadc_cmp_base;
+	struct tadc_chan_data	chans[TADC_NUM_CH];
+	struct completion	eoc_complete;
+	struct mutex		write_lock;
+};
+
+struct tadc_pt {
+	s32 x;
+	s32 y;
+};
+
+/*
+ * Thermistor tables are generated by the B-parameter equation which is a
+ * simplifed version of the Steinhart-Hart equation.
+ *
+ * (1 / T) = (1 / T0) + (1 / B) * ln(R / R0)
+ *
+ * Where R0 is the resistance at temperature T0, and T0 is typically room
+ * temperature (25C).
+ */
+static const struct tadc_pt tadc_therm_3450b_68k[] = {
+	{ 4151,		120000 },
+	{ 4648,		115000 },
+	{ 5220,		110000 },
+	{ 5880,		105000 },
+	{ 6644,		100000 },
+	{ 7533,		95000 },
+	{ 8571,		90000 },
+	{ 9786,		85000 },
+	{ 11216,	80000 },
+	{ 12906,	75000 },
+	{ 14910,	70000 },
+	{ 17300,	65000 },
+	{ 20163,	60000 },
+	{ 23609,	55000 },
+	{ 27780,	50000 },
+	{ 32855,	45000 },
+	{ 39065,	40000 },
+	{ 46712,	35000 },
+	{ 56185,	30000 },
+	{ 68000,	25000 },
+	{ 82837,	20000 },
+	{ 101604,	15000 },
+	{ 125525,	10000 },
+	{ 156261,	5000 },
+	{ 196090,	0 },
+	{ 248163,	-5000 },
+	{ 316887,	-10000 },
+	{ 408493,	-15000 },
+	{ 531889,	-20000 },
+	{ 699966,	-25000 },
+	{ 931618,	-30000 },
+	{ 1254910,	-35000 },
+	{ 1712127,	-40000 },
+};
+
+static bool tadc_is_reg_locked(struct tadc_chip *chip, u16 reg)
+{
+	if ((reg & 0xFF00) == chip->tadc_cmp_base)
+		return true;
+
+	if (reg == TADC_HWTRIG_CONV_CH_EN_REG(chip))
+		return true;
+
+	return false;
+}
+
+static int tadc_read(struct tadc_chip *chip, u16 reg, u8 *val, size_t count)
+{
+	int rc = 0;
+
+	rc = regmap_bulk_read(chip->regmap, reg, val, count);
+	if (rc < 0)
+		pr_err("Couldn't read 0x%04x rc=%d\n", reg, rc);
+
+	return rc;
+}
+
+static int tadc_write(struct tadc_chip *chip, u16 reg, u8 data)
+{
+	int rc = 0;
+
+	mutex_lock(&chip->write_lock);
+	if (tadc_is_reg_locked(chip, reg)) {
+		rc = regmap_write(chip->regmap, (reg & 0xFF00) | 0xD0, 0xA5);
+		if (rc < 0) {
+			pr_err("Couldn't unlock secure register rc=%d\n", rc);
+			goto unlock;
+		}
+	}
+
+	rc = regmap_write(chip->regmap, reg, data);
+	if (rc < 0) {
+		pr_err("Couldn't write 0x%02x to 0x%04x rc=%d\n",
+								data, reg, rc);
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&chip->write_lock);
+	return rc;
+}
+static int tadc_bulk_write(struct tadc_chip *chip, u16 reg, u8 *data,
+								size_t count)
+{
+	int rc = 0, i;
+
+	mutex_lock(&chip->write_lock);
+	for (i = 0; i < count; ++i, ++reg) {
+		if (tadc_is_reg_locked(chip, reg)) {
+			rc = regmap_write(chip->regmap,
+						(reg & 0xFF00) | 0xD0, 0xA5);
+			if (rc < 0) {
+				pr_err("Couldn't unlock secure register rc=%d\n",
+								rc);
+				goto unlock;
+			}
+		}
+
+		rc = regmap_write(chip->regmap, reg, data[i]);
+		if (rc < 0) {
+			pr_err("Couldn't write 0x%02x to 0x%04x rc=%d\n",
+							data[i], reg, rc);
+			goto unlock;
+		}
+	}
+
+unlock:
+	mutex_unlock(&chip->write_lock);
+	return rc;
+}
+
+static int tadc_lerp(const struct tadc_pt *pts, size_t size, bool inv,
+							s32 input, s32 *output)
+{
+	int i;
+	s64 temp;
+	bool ascending;
+
+	if (pts == NULL) {
+		pr_err("Table is NULL\n");
+		return -EINVAL;
+	}
+
+	if (size < 1) {
+		pr_err("Table has no entries\n");
+		return -ENOENT;
+	}
+
+	if (size == 1) {
+		*output = inv ? pts[0].x : pts[0].y;
+		return 0;
+	}
+
+	ascending = inv ? (pts[0].y < pts[1].y) : (pts[0].x < pts[1].x);
+	if (ascending ? (input <= (inv ? pts[0].y : pts[0].x)) :
+			(input >= (inv ? pts[0].y : pts[0].x))) {
+		*output = inv ? pts[0].x : pts[0].y;
+		return 0;
+	}
+
+	if (ascending ? (input >= (inv ? pts[size - 1].y : pts[size - 1].x)) :
+			(input <= (inv ? pts[size - 1].y : pts[size - 1].x))) {
+		*output = inv ? pts[size - 1].x : pts[size - 1].y;
+		return 0;
+	}
+
+	for (i = 1; i < size; i++)
+		if (ascending ? (input <= (inv ? pts[i].y : pts[i].x)) :
+				(input >= (inv ? pts[i].y : pts[i].x)))
+			break;
+
+	if (inv) {
+		temp = (s64)(pts[i].x - pts[i - 1].x) *
+						(s64)(input - pts[i - 1].y);
+		temp = div_s64(temp, pts[i].y - pts[i - 1].y);
+		*output = temp + pts[i - 1].x;
+	} else {
+		temp = (s64)(pts[i].y - pts[i - 1].y) *
+						(s64)(input - pts[i - 1].x);
+		temp = div_s64(temp, pts[i].x - pts[i - 1].x);
+		*output = temp + pts[i - 1].y;
+	}
+
+	return 0;
+}
+
+/*
+ * Process the result of a thermistor reading.
+ *
+ * The voltage input to the ADC is a result of a voltage divider circuit.
+ * Vout = (Rtherm / (Rbias + Rtherm)) * Vbias
+ *
+ * The ADC value is based on the output voltage of the voltage divider, and the
+ * bias voltage.
+ * ADC = (Vin * 1024) / Vbias
+ *
+ * Combine these equations and solve for Rtherm
+ * Rtherm = (ADC * Rbias) / (1024 - ADC)
+ */
+static int tadc_get_processed_therm(const struct tadc_chan_data *chan_data,
+							s16 adc, s32 *result)
+{
+	s32 rtherm;
+
+	rtherm = div_s64((s64)adc * chan_data->rbias, TADC_RESOLUTION - adc);
+	return tadc_lerp(chan_data->table, chan_data->tablesize, false, rtherm,
+									result);
+}
+
+static int tadc_get_raw_therm(const struct tadc_chan_data *chan_data,
+							int mdegc, int *result)
+{
+	int rc;
+	s32 rtherm;
+
+	rc = tadc_lerp(chan_data->table, chan_data->tablesize, true, mdegc,
+								&rtherm);
+	if (rc < 0) {
+		pr_err("Couldn't interpolate %d\n rc=%d", mdegc, rc);
+		return rc;
+	}
+
+	*result = div64_s64((s64)rtherm * TADC_RESOLUTION,
+						(s64)chan_data->rbias + rtherm);
+	return 0;
+}
+
+static int tadc_read_channel(struct tadc_chip *chip, u16 address, int *adc)
+{
+	u8 val[2];
+	int rc;
+
+	rc = tadc_read(chip, address, val, ARRAY_SIZE(val));
+	if (rc < 0) {
+		pr_err("Couldn't read channel rc=%d\n", rc);
+		return rc;
+	}
+
+	/* the 10th bit is the sign bit for all channels */
+	*adc = sign_extend32(val[0] | val[1] << BITS_PER_BYTE, 10);
+	return rc;
+}
+
+static int tadc_write_channel(struct tadc_chip *chip, u16 address, int adc)
+{
+	u8 val[2];
+	int rc;
+
+	/* the 10th bit is the sign bit for all channels */
+	adc = sign_extend32(adc, 10);
+	val[0] = (u8)adc;
+	val[1] = (u8)(adc >> BITS_PER_BYTE);
+	rc = tadc_bulk_write(chip, address, val, 2);
+	if (rc < 0) {
+		pr_err("Couldn't write to channel rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+#define CONVERSION_TIMEOUT_MS 100
+static int tadc_do_conversion(struct tadc_chip *chip, u8 channels, s16 *adc)
+{
+	unsigned long timeout, timeleft;
+	u8 val[TADC_NUM_CH * 2];
+	int rc, i;
+
+	rc = tadc_read(chip, TADC_MBG_ERR_REG(chip), val, 1);
+	if (rc < 0) {
+		pr_err("Couldn't read mbg error status rc=%d\n", rc);
+		return rc;
+	}
+
+	if (val[0] != 0) {
+		tadc_write(chip, TADC_EN_CTL_REG(chip), 0);
+		tadc_write(chip, TADC_EN_CTL_REG(chip), 0x80);
+	}
+
+	rc = tadc_write(chip, TADC_CONV_REQ_REG(chip), channels);
+	if (rc < 0) {
+		pr_err("Couldn't write conversion request rc=%d\n", rc);
+		return rc;
+	}
+
+	timeout = msecs_to_jiffies(CONVERSION_TIMEOUT_MS);
+	timeleft = wait_for_completion_timeout(&chip->eoc_complete, timeout);
+
+	if (timeleft == 0) {
+		rc = tadc_read(chip, TADC_SW_CH_CONV_REG(chip), val, 1);
+		if (rc < 0) {
+			pr_err("Couldn't read conversion status rc=%d\n", rc);
+			return rc;
+		}
+
+		if (val[0] != channels) {
+			pr_err("Conversion timed out\n");
+			return -ETIMEDOUT;
+		}
+	}
+
+	rc = tadc_read(chip, TADC_CH1_ADC_LO_REG(chip), val, ARRAY_SIZE(val));
+	if (rc < 0) {
+		pr_err("Couldn't read adc channels rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < TADC_NUM_CH; i++)
+		adc[i] = (s16)(val[i * 2] | (u16)val[i * 2 + 1] << 8);
+
+	return jiffies_to_msecs(timeout - timeleft);
+}
+
+static int tadc_read_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int *val, int *val2,
+		long mask)
+{
+	struct tadc_chip *chip = iio_priv(indio_dev);
+	struct tadc_chan_data *chan_data = NULL;
+	int rc, offset = 0, scale, scale2, scale_type;
+	s16 adc[TADC_NUM_CH];
+
+	switch (chan->channel) {
+	case TADC_THERM1_THR1:
+	case TADC_THERM1_THR2:
+	case TADC_THERM1_THR3:
+	case TADC_THERM1_THR4:
+		chan_data = &chip->chans[TADC_THERM1];
+		break;
+	case TADC_THERM2_THR1:
+	case TADC_THERM2_THR2:
+	case TADC_THERM2_THR3:
+		chan_data = &chip->chans[TADC_THERM2];
+		break;
+	case TADC_DIE_TEMP_THR1:
+	case TADC_DIE_TEMP_THR2:
+	case TADC_DIE_TEMP_THR3:
+		chan_data = &chip->chans[TADC_DIE_TEMP];
+		break;
+	default:
+		if (chan->channel >= ARRAY_SIZE(chip->chans)) {
+			pr_err("Channel %d is out of bounds\n", chan->channel);
+			return -EINVAL;
+		}
+
+		chan_data = &chip->chans[chan->channel];
+		break;
+	}
+
+	if (!chan_data)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->channel) {
+		case TADC_THERM1_THR1:
+		case TADC_THERM2_THR1:
+		case TADC_DIE_TEMP_THR1:
+			rc = tadc_read_channel(chip,
+					chan_data->thr[0].addr_lo, val);
+			break;
+		case TADC_THERM1_THR2:
+		case TADC_THERM2_THR2:
+		case TADC_DIE_TEMP_THR2:
+			rc = tadc_read_channel(chip,
+					chan_data->thr[1].addr_lo, val);
+			break;
+		case TADC_THERM1_THR3:
+		case TADC_THERM2_THR3:
+		case TADC_DIE_TEMP_THR3:
+			rc = tadc_read_channel(chip,
+					chan_data->thr[2].addr_lo, val);
+			break;
+		case TADC_THERM1_THR4:
+			rc = tadc_read_channel(chip,
+					chan_data->thr[3].addr_lo, val);
+			break;
+		default:
+			rc = tadc_do_conversion(chip, BIT(chan->channel), adc);
+			if (rc >= 0)
+				*val = adc[chan->channel];
+			break;
+		}
+
+		if (rc < 0) {
+			pr_err("Couldn't read channel %d\n", chan->channel);
+			return rc;
+		}
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_PROCESSED:
+		switch (chan->channel) {
+		case TADC_THERM1:
+		case TADC_THERM2:
+		case TADC_THERM1_THR1:
+		case TADC_THERM1_THR2:
+		case TADC_THERM1_THR3:
+		case TADC_THERM1_THR4:
+		case TADC_THERM2_THR1:
+		case TADC_THERM2_THR2:
+		case TADC_THERM2_THR3:
+			rc = tadc_read_raw(indio_dev, chan, val, NULL,
+							IIO_CHAN_INFO_RAW);
+			if (rc < 0)
+				return rc;
+
+			rc = tadc_get_processed_therm(chan_data, *val, val);
+			if (rc < 0) {
+				pr_err("Couldn't process 0x%04x from channel %d rc=%d\n",
+						*val, chan->channel, rc);
+				return rc;
+			}
+			break;
+		case TADC_BATT_P:
+			rc = tadc_do_conversion(chip,
+				BIT(TADC_BATT_I) | BIT(TADC_BATT_V), adc);
+			if (rc < 0) {
+				pr_err("Couldn't read battery current and voltage channels rc=%d\n",
+									rc);
+				return rc;
+			}
+
+			*val = adc[TADC_BATT_I] * adc[TADC_BATT_V];
+			break;
+		case TADC_INPUT_P:
+			rc = tadc_do_conversion(chip,
+				BIT(TADC_INPUT_I) | BIT(TADC_INPUT_V), adc);
+			if (rc < 0) {
+				pr_err("Couldn't read input current and voltage channels rc=%d\n",
+									rc);
+				return rc;
+			}
+
+			*val = adc[TADC_INPUT_I] * adc[TADC_INPUT_V];
+			break;
+		default:
+			rc = tadc_read_raw(indio_dev, chan, val, NULL,
+							IIO_CHAN_INFO_RAW);
+			if (rc < 0)
+				return rc;
+
+			/* offset is optional */
+			rc = tadc_read_raw(indio_dev, chan, &offset, NULL,
+							IIO_CHAN_INFO_OFFSET);
+			if (rc < 0)
+				return rc;
+
+			scale_type = tadc_read_raw(indio_dev, chan,
+					&scale, &scale2, IIO_CHAN_INFO_SCALE);
+			switch (scale_type) {
+			case IIO_VAL_INT:
+				*val = *val * scale + offset;
+				break;
+			case IIO_VAL_FRACTIONAL:
+				*val = div_s64((s64)*val * scale + offset,
+									scale2);
+				break;
+			default:
+				return -EINVAL;
+			}
+			break;
+		}
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		switch (chan->channel) {
+		case TADC_DIE_TEMP:
+		case TADC_DIE_TEMP_THR1:
+		case TADC_DIE_TEMP_THR2:
+			*val = chan_data->scale;
+			return IIO_VAL_INT;
+		case TADC_BATT_I:
+		case TADC_BATT_V:
+		case TADC_INPUT_I:
+		case TADC_INPUT_V:
+		case TADC_OTG_I:
+			*val = chan_data->scale;
+			*val2 = TADC_RESOLUTION;
+			return IIO_VAL_FRACTIONAL;
+		}
+
+		return -EINVAL;
+	case IIO_CHAN_INFO_OFFSET:
+		*val = chan_data->offset;
+		return IIO_VAL_INT;
+	}
+
+	return -EINVAL;
+}
+
+static int tadc_write_raw(struct iio_dev *indio_dev,
+		struct iio_chan_spec const *chan, int val, int val2,
+		long mask)
+{
+	struct tadc_chip *chip = iio_priv(indio_dev);
+	const struct tadc_chan_data *chan_data;
+	int rc, raw;
+	s32 rem;
+
+	switch (chan->channel) {
+	case TADC_THERM1_THR1:
+	case TADC_THERM1_THR2:
+	case TADC_THERM1_THR3:
+	case TADC_THERM1_THR4:
+		chan_data = &chip->chans[TADC_THERM1];
+		break;
+	case TADC_THERM2_THR1:
+	case TADC_THERM2_THR2:
+	case TADC_THERM2_THR3:
+		chan_data = &chip->chans[TADC_THERM2];
+		break;
+	case TADC_DIE_TEMP_THR1:
+	case TADC_DIE_TEMP_THR2:
+	case TADC_DIE_TEMP_THR3:
+		chan_data = &chip->chans[TADC_DIE_TEMP];
+		break;
+	default:
+		if (chan->channel >= ARRAY_SIZE(chip->chans)) {
+			pr_err("Channel %d is out of bounds\n", chan->channel);
+			return -EINVAL;
+		}
+
+		chan_data = &chip->chans[chan->channel];
+		break;
+	}
+
+	if (!chan_data)
+		return -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_PROCESSED:
+		switch (chan->channel) {
+		case TADC_THERM1_THR1:
+		case TADC_THERM1_THR2:
+		case TADC_THERM1_THR3:
+		case TADC_THERM1_THR4:
+		case TADC_THERM2_THR1:
+		case TADC_THERM2_THR2:
+		case TADC_THERM2_THR3:
+			rc = tadc_get_raw_therm(chan_data, val, &raw);
+			if (rc < 0) {
+				pr_err("Couldn't get raw value rc=%d\n", rc);
+				return rc;
+			}
+			break;
+		case TADC_DIE_TEMP_THR1:
+		case TADC_DIE_TEMP_THR2:
+		case TADC_DIE_TEMP_THR3:
+			/* DIV_ROUND_CLOSEST does not like negative numbers */
+			raw = div_s64_rem(val - chan_data->offset,
+							chan_data->scale, &rem);
+			if (abs(rem) >= abs(chan_data->scale / 2))
+				raw++;
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		rc = tadc_write_raw(indio_dev, chan, raw, 0,
+							IIO_CHAN_INFO_RAW);
+		if (rc < 0) {
+			pr_err("Couldn't write raw rc=%d\n", rc);
+			return rc;
+		}
+
+		break;
+	case IIO_CHAN_INFO_RAW:
+		switch (chan->channel) {
+		case TADC_THERM1_THR1:
+		case TADC_THERM2_THR1:
+		case TADC_DIE_TEMP_THR1:
+			rc = tadc_write_channel(chip,
+					chan_data->thr[0].addr_lo, val);
+			break;
+		case TADC_THERM1_THR2:
+		case TADC_THERM2_THR2:
+		case TADC_DIE_TEMP_THR2:
+			rc = tadc_write_channel(chip,
+					chan_data->thr[1].addr_lo, val);
+			break;
+		case TADC_THERM1_THR3:
+		case TADC_THERM2_THR3:
+		case TADC_DIE_TEMP_THR3:
+			rc = tadc_write_channel(chip,
+					chan_data->thr[2].addr_lo, val);
+			break;
+		case TADC_THERM1_THR4:
+			rc = tadc_write_channel(chip,
+					chan_data->thr[3].addr_lo, val);
+			break;
+		default:
+			return -EINVAL;
+		}
+
+		if (rc < 0) {
+			pr_err("Couldn't write channel %d\n", chan->channel);
+			return rc;
+		}
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static irqreturn_t handle_eoc(int irq, void *dev_id)
+{
+	struct tadc_chip *chip = dev_id;
+
+	complete(&chip->eoc_complete);
+	return IRQ_HANDLED;
+}
+
+static int tadc_set_therm_table(struct tadc_chan_data *chan_data, u32 beta,
+				u32 rtherm)
+{
+	if (beta == 3450 && rtherm == 68000) {
+		chan_data->table = tadc_therm_3450b_68k;
+		chan_data->tablesize = ARRAY_SIZE(tadc_therm_3450b_68k);
+		return 0;
+	}
+
+	return -ENOENT;
+}
+
+static int tadc_parse_dt(struct tadc_chip *chip)
+{
+	struct device_node *child, *node;
+	struct tadc_chan_data *chan_data;
+	u32 chan_id, rtherm, beta;
+	int rc = 0;
+
+	node = chip->dev->of_node;
+	for_each_available_child_of_node(node, child) {
+		rc = of_property_read_u32(child, "reg", &chan_id);
+		if (rc < 0) {
+			pr_err("Couldn't find channel for %s rc=%d",
+							child->name, rc);
+			return rc;
+		}
+
+		if (chan_id > TADC_NUM_CH - 1) {
+			pr_err("Channel %d is out of range [0, %d]\n",
+						chan_id, TADC_NUM_CH - 1);
+			return -EINVAL;
+		}
+
+		chan_data = &chip->chans[chan_id];
+		if (chan_id == TADC_THERM1 || chan_id == TADC_THERM2) {
+			rc = of_property_read_u32(child,
+					"qcom,rbias", &chan_data->rbias);
+			if (rc < 0) {
+				pr_err("Couldn't read qcom,rbias rc=%d\n", rc);
+				return rc;
+			}
+
+			rc = of_property_read_u32(child,
+					"qcom,beta-coefficient", &beta);
+			if (rc < 0) {
+				pr_err("Couldn't read qcom,beta-coefficient rc=%d\n",
+									rc);
+				return rc;
+			}
+
+			rc = of_property_read_u32(child,
+					"qcom,rtherm-at-25degc", &rtherm);
+			if (rc < 0) {
+				pr_err("Couldn't read qcom,rtherm-at-25degc rc=%d\n",
+					rc);
+				return rc;
+			}
+
+			rc = tadc_set_therm_table(chan_data, beta, rtherm);
+			if (rc < 0) {
+				pr_err("Couldn't set therm table rc=%d\n", rc);
+				return rc;
+			}
+		} else {
+			rc = of_property_read_s32(child, "qcom,scale",
+							&chan_data->scale);
+			if (rc < 0) {
+				pr_err("Couldn't read scale rc=%d\n", rc);
+				return rc;
+			}
+
+			of_property_read_s32(child, "qcom,offset",
+							&chan_data->offset);
+		}
+	}
+
+	return rc;
+}
+
+static int tadc_init_hw(struct tadc_chip *chip)
+{
+	int rc;
+
+	chip->chans[TADC_THERM1].thr[0].addr_lo =
+					TADC_CMP_THR1_CH1_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM1].thr[0].addr_hi =
+					TADC_CMP_THR1_CH1_CMP_HI_REG(chip);
+	chip->chans[TADC_THERM1].thr[1].addr_lo =
+					TADC_CMP_THR2_CH1_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM1].thr[1].addr_hi =
+					TADC_CMP_THR2_CH1_CMP_HI_REG(chip);
+	chip->chans[TADC_THERM1].thr[2].addr_lo =
+					TADC_CMP_THR3_CH1_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM1].thr[2].addr_hi =
+					TADC_CMP_THR3_CH1_CMP_HI_REG(chip);
+	chip->chans[TADC_THERM1].thr[3].addr_lo =
+					TADC_CMP_THR4_CH1_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM1].thr[3].addr_hi =
+					TADC_CMP_THR4_CH1_CMP_HI_REG(chip);
+
+	chip->chans[TADC_THERM2].thr[0].addr_lo =
+					TADC_CMP_THR1_CH2_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM2].thr[0].addr_hi =
+					TADC_CMP_THR1_CH2_CMP_HI_REG(chip);
+	chip->chans[TADC_THERM2].thr[1].addr_lo =
+					TADC_CMP_THR2_CH2_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM2].thr[1].addr_hi =
+					TADC_CMP_THR2_CH2_CMP_HI_REG(chip);
+	chip->chans[TADC_THERM2].thr[2].addr_lo =
+					TADC_CMP_THR3_CH2_CMP_LO_REG(chip);
+	chip->chans[TADC_THERM2].thr[2].addr_hi =
+					TADC_CMP_THR3_CH2_CMP_HI_REG(chip);
+
+	chip->chans[TADC_DIE_TEMP].thr[0].addr_lo =
+					TADC_CMP_THR1_CH3_CMP_LO_REG(chip);
+	chip->chans[TADC_DIE_TEMP].thr[0].addr_hi =
+					TADC_CMP_THR1_CH3_CMP_HI_REG(chip);
+	chip->chans[TADC_DIE_TEMP].thr[1].addr_lo =
+					TADC_CMP_THR2_CH3_CMP_LO_REG(chip);
+	chip->chans[TADC_DIE_TEMP].thr[1].addr_hi =
+					TADC_CMP_THR2_CH3_CMP_HI_REG(chip);
+	chip->chans[TADC_DIE_TEMP].thr[2].addr_lo =
+					TADC_CMP_THR3_CH3_CMP_LO_REG(chip);
+	chip->chans[TADC_DIE_TEMP].thr[2].addr_hi =
+					TADC_CMP_THR3_CH3_CMP_HI_REG(chip);
+
+	rc = tadc_write(chip, TADC_CMP_THR1_CMP_REG(chip), 0);
+	if (rc < 0) {
+		pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = tadc_write(chip, TADC_CMP_THR2_CMP_REG(chip), 0);
+	if (rc < 0) {
+		pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = tadc_write(chip, TADC_CMP_THR3_CMP_REG(chip), 0);
+	if (rc < 0) {
+		pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+		return rc;
+	}
+
+	/* enable all temperature hardware triggers */
+	rc = tadc_write(chip, TADC_HWTRIG_CONV_CH_EN_REG(chip),
+							BIT(TADC_THERM1) |
+							BIT(TADC_THERM2) |
+							BIT(TADC_DIE_TEMP));
+	if (rc < 0) {
+		pr_err("Couldn't enable hardware triggers rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static const struct iio_info tadc_info = {
+	.read_raw		= &tadc_read_raw,
+	.write_raw		= &tadc_write_raw,
+	.driver_module		= THIS_MODULE,
+};
+
+static int tadc_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct iio_dev *indio_dev;
+	struct tadc_chip *chip;
+	int rc, irq;
+
+	indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*chip));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	chip = iio_priv(indio_dev);
+	chip->dev = &pdev->dev;
+	init_completion(&chip->eoc_complete);
+
+	rc = of_property_read_u32(node, "reg", &chip->tadc_base);
+	if (rc < 0) {
+		pr_err("Couldn't read base address rc=%d\n", rc);
+		return rc;
+	}
+	chip->tadc_cmp_base = chip->tadc_base + 0x100;
+
+	mutex_init(&chip->write_lock);
+	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
+	if (!chip->regmap) {
+		pr_err("Couldn't get regmap\n");
+		return -ENODEV;
+	}
+
+	rc = tadc_parse_dt(chip);
+	if (rc < 0) {
+		pr_err("Couldn't parse device tree rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = tadc_init_hw(chip);
+	if (rc < 0) {
+		pr_err("Couldn't initialize hardware rc=%d\n", rc);
+		return rc;
+	}
+
+	irq = of_irq_get_byname(node, "eoc");
+	if (irq < 0) {
+		pr_err("Couldn't get eoc irq rc=%d\n", irq);
+		return irq;
+	}
+
+	rc = devm_request_threaded_irq(chip->dev, irq, NULL, handle_eoc,
+						IRQF_ONESHOT, "eoc", chip);
+	if (rc < 0) {
+		pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
+		return rc;
+	}
+
+	indio_dev->dev.parent = chip->dev;
+	indio_dev->name = pdev->name;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->info = &tadc_info;
+	indio_dev->channels = tadc_iio_chans;
+	indio_dev->num_channels = ARRAY_SIZE(tadc_iio_chans);
+
+	rc = devm_iio_device_register(chip->dev, indio_dev);
+	if (rc < 0) {
+		pr_err("Couldn't register IIO device rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int tadc_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id tadc_match_table[] = {
+	{ .compatible = "qcom,tadc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, tadc_match_table);
+
+static struct platform_driver tadc_driver = {
+	.driver	= {
+		.name		= "qcom-tadc",
+		.of_match_table	= tadc_match_table,
+	},
+	.probe	= tadc_probe,
+	.remove	= tadc_remove,
+};
+module_platform_driver(tadc_driver);
+
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. TADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c4757e6..643254b 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -746,3 +746,21 @@ int iio_write_channel_raw(struct iio_channel *chan, int val)
 	return ret;
 }
 EXPORT_SYMBOL_GPL(iio_write_channel_raw);
+
+int iio_write_channel_processed(struct iio_channel *chan, int val)
+{
+	int ret;
+
+	mutex_lock(&chan->indio_dev->info_exist_lock);
+	if (chan->indio_dev->info == NULL) {
+		ret = -ENODEV;
+		goto err_unlock;
+	}
+
+	ret = iio_channel_write(chan, val, 0, IIO_CHAN_INFO_PROCESSED);
+err_unlock:
+	mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(iio_write_channel_processed);
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 7a628c6..5f144a6 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -659,6 +659,43 @@
 	  This option enabled support for the LEDs on the Mellanox
 	  boards. Say Y to enabled these.
 
+config LEDS_QPNP
+	tristate "Support for QPNP LEDs"
+	depends on LEDS_CLASS && SPMI
+	help
+	  This driver supports the LED functionality of Qualcomm Technologies,
+	  Inc. QPNP PMICs.  It primarily supports controlling tri-color RGB
+	  LEDs in both PWM and light pattern generator (LPG) modes.  For older
+	  PMICs, it also supports WLEDs and flash LEDs.
+
+config LEDS_QPNP_FLASH
+	tristate "Support for QPNP Flash LEDs"
+	depends on LEDS_CLASS && SPMI
+	help
+	  This driver supports the flash LED functionality of Qualcomm
+	  Technologies, Inc. QPNP PMICs.  This driver supports PMICs up through
+	  PM8994.  It can configure the flash LED target current for several
+	  independent channels.
+
+config LEDS_QPNP_FLASH_V2
+	tristate "Support for QPNP V2 Flash LEDs"
+	depends on LEDS_CLASS && MFD_SPMI_PMIC && !LEDS_QPNP_FLASH
+	help
+	  This driver supports the flash V2 LED functionality of Qualcomm
+	  Technologies, Inc. QPNP PMICs.  This driver supports PMICs starting
+	  from PMI8998.  It can configure the flash LED target current for
+	  several independent channels.  It also supports various over current
+	  and over temperature mitigation features.
+
+config LEDS_QPNP_WLED
+	tristate "Support for QPNP WLED"
+	depends on LEDS_CLASS && SPMI
+	help
+	  This driver supports the WLED (White LED) functionality of Qualcomm
+	  Technologies, Inc. QPNP PMICs.  WLED is used for LCD backlight with
+	  variable brightness.  It also supports outputting the Avdd supply for
+	  AMOLED displays.
+
 comment "LED Triggers"
 source "drivers/leds/trigger/Kconfig"
 
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3965070..e0ca2e8 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -71,6 +71,10 @@
 obj-$(CONFIG_LEDS_IS31FL32XX)		+= leds-is31fl32xx.o
 obj-$(CONFIG_LEDS_PM8058)		+= leds-pm8058.o
 obj-$(CONFIG_LEDS_MLXCPLD)		+= leds-mlxcpld.o
+obj-$(CONFIG_LEDS_QPNP)			+= leds-qpnp.o
+obj-$(CONFIG_LEDS_QPNP_FLASH)		+= leds-qpnp-flash.o
+obj-$(CONFIG_LEDS_QPNP_FLASH_V2)	+= leds-qpnp-flash-v2.o
+obj-$(CONFIG_LEDS_QPNP_WLED)		+= leds-qpnp-wled.o
 
 # LED SPI Drivers
 obj-$(CONFIG_LEDS_DAC124S085)		+= leds-dac124s085.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
index aa84e5b..8cb3265 100644
--- a/drivers/leds/led-class.c
+++ b/drivers/leds/led-class.c
@@ -53,7 +53,7 @@ static ssize_t brightness_store(struct device *dev,
 	if (ret)
 		goto unlock;
 
-	if (state == LED_OFF)
+	if (state == LED_OFF && !(led_cdev->flags & LED_KEEP_TRIGGER))
 		led_trigger_remove(led_cdev);
 	led_set_brightness(led_cdev, state);
 
diff --git a/drivers/leds/leds-qpnp-flash-v2.c b/drivers/leds/leds-qpnp-flash-v2.c
new file mode 100644
index 0000000..08809a9
--- /dev/null
+++ b/drivers/leds/leds-qpnp-flash-v2.c
@@ -0,0 +1,2265 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"flashv2: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_gpio.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/leds-qpnp-flash.h>
+#include <linux/leds-qpnp-flash-v2.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <linux/log2.h>
+#include "leds.h"
+
+#define	FLASH_LED_REG_LED_STATUS1(base)		(base + 0x08)
+#define	FLASH_LED_REG_LED_STATUS2(base)		(base + 0x09)
+#define	FLASH_LED_REG_INT_RT_STS(base)		(base + 0x10)
+#define	FLASH_LED_REG_SAFETY_TMR(base)		(base + 0x40)
+#define	FLASH_LED_REG_TGR_CURRENT(base)		(base + 0x43)
+#define	FLASH_LED_REG_MOD_CTRL(base)		(base + 0x46)
+#define	FLASH_LED_REG_IRES(base)		(base + 0x47)
+#define	FLASH_LED_REG_STROBE_CFG(base)		(base + 0x48)
+#define	FLASH_LED_REG_STROBE_CTRL(base)		(base + 0x49)
+#define	FLASH_LED_EN_LED_CTRL(base)		(base + 0x4C)
+#define	FLASH_LED_REG_HDRM_PRGM(base)		(base + 0x4D)
+#define	FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(base)	(base + 0x50)
+#define	FLASH_LED_REG_WARMUP_DELAY(base)	(base + 0x51)
+#define	FLASH_LED_REG_ISC_DELAY(base)		(base + 0x52)
+#define	FLASH_LED_REG_THERMAL_RMP_DN_RATE(base)	(base + 0x55)
+#define	FLASH_LED_REG_THERMAL_THRSH1(base)	(base + 0x56)
+#define	FLASH_LED_REG_THERMAL_THRSH2(base)	(base + 0x57)
+#define	FLASH_LED_REG_THERMAL_THRSH3(base)	(base + 0x58)
+#define	FLASH_LED_REG_THERMAL_HYSTERESIS(base)	(base + 0x59)
+#define	FLASH_LED_REG_THERMAL_DEBOUNCE(base)	(base + 0x5A)
+#define	FLASH_LED_REG_VPH_DROOP_THRESHOLD(base)	(base + 0x61)
+#define	FLASH_LED_REG_VPH_DROOP_DEBOUNCE(base)	(base + 0x62)
+#define	FLASH_LED_REG_ILED_GRT_THRSH(base)	(base + 0x67)
+#define	FLASH_LED_REG_LED1N2_ICLAMP_LOW(base)	(base + 0x68)
+#define	FLASH_LED_REG_LED1N2_ICLAMP_MID(base)	(base + 0x69)
+#define	FLASH_LED_REG_LED3_ICLAMP_LOW(base)	(base + 0x6A)
+#define	FLASH_LED_REG_LED3_ICLAMP_MID(base)	(base + 0x6B)
+#define	FLASH_LED_REG_MITIGATION_SEL(base)	(base + 0x6E)
+#define	FLASH_LED_REG_MITIGATION_SW(base)	(base + 0x6F)
+#define	FLASH_LED_REG_LMH_LEVEL(base)		(base + 0x70)
+#define	FLASH_LED_REG_CURRENT_DERATE_EN(base)	(base + 0x76)
+
+#define	FLASH_LED_HDRM_VOL_MASK			GENMASK(7, 4)
+#define	FLASH_LED_CURRENT_MASK			GENMASK(6, 0)
+#define	FLASH_LED_ENABLE_MASK			GENMASK(2, 0)
+#define	FLASH_HW_STROBE_MASK			GENMASK(2, 0)
+#define	FLASH_LED_ISC_WARMUP_DELAY_MASK		GENMASK(1, 0)
+#define	FLASH_LED_CURRENT_DERATE_EN_MASK	GENMASK(2, 0)
+#define	FLASH_LED_VPH_DROOP_DEBOUNCE_MASK	GENMASK(1, 0)
+#define	FLASH_LED_CHGR_MITIGATION_SEL_MASK	GENMASK(5, 4)
+#define	FLASH_LED_LMH_MITIGATION_SEL_MASK	GENMASK(1, 0)
+#define	FLASH_LED_ILED_GRT_THRSH_MASK		GENMASK(5, 0)
+#define	FLASH_LED_LMH_LEVEL_MASK		GENMASK(1, 0)
+#define	FLASH_LED_VPH_DROOP_HYSTERESIS_MASK	GENMASK(5, 4)
+#define	FLASH_LED_VPH_DROOP_THRESHOLD_MASK	GENMASK(2, 0)
+#define	FLASH_LED_THERMAL_HYSTERESIS_MASK	GENMASK(1, 0)
+#define	FLASH_LED_THERMAL_DEBOUNCE_MASK		GENMASK(1, 0)
+#define	FLASH_LED_THERMAL_THRSH_MASK		GENMASK(2, 0)
+#define	FLASH_LED_MOD_CTRL_MASK			BIT(7)
+#define	FLASH_LED_HW_SW_STROBE_SEL_BIT		BIT(2)
+#define	FLASH_LED_VPH_DROOP_FAULT_MASK		BIT(4)
+#define	FLASH_LED_LMH_MITIGATION_EN_MASK	BIT(0)
+#define	FLASH_LED_CHGR_MITIGATION_EN_MASK	BIT(4)
+#define	THERMAL_OTST1_RAMP_CTRL_MASK		BIT(7)
+#define	THERMAL_OTST1_RAMP_CTRL_SHIFT		7
+#define	THERMAL_DERATE_SLOW_SHIFT		4
+#define	THERMAL_DERATE_SLOW_MASK		GENMASK(6, 4)
+#define	THERMAL_DERATE_FAST_MASK		GENMASK(2, 0)
+
+#define	VPH_DROOP_DEBOUNCE_US_TO_VAL(val_us)	(val_us / 8)
+#define	VPH_DROOP_HYST_MV_TO_VAL(val_mv)	(val_mv / 25)
+#define	VPH_DROOP_THRESH_MV_TO_VAL(val_mv)	((val_mv / 100) - 25)
+#define	VPH_DROOP_THRESH_VAL_TO_UV(val)		((val + 25) * 100000)
+#define	MITIGATION_THRSH_MA_TO_VAL(val_ma)	(val_ma / 100)
+#define	CURRENT_MA_TO_REG_VAL(curr_ma, ires_ua)	((curr_ma * 1000) / ires_ua - 1)
+#define	SAFETY_TMR_TO_REG_VAL(duration_ms)	((duration_ms / 10) - 1)
+#define	THERMAL_HYST_TEMP_TO_VAL(val, divisor)	(val / divisor)
+
+#define	FLASH_LED_ISC_WARMUP_DELAY_SHIFT	6
+#define	FLASH_LED_WARMUP_DELAY_DEFAULT		2
+#define	FLASH_LED_ISC_DELAY_DEFAULT		3
+#define	FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT	2
+#define	FLASH_LED_VPH_DROOP_HYST_SHIFT		4
+#define	FLASH_LED_VPH_DROOP_HYST_DEFAULT	2
+#define	FLASH_LED_VPH_DROOP_THRESH_DEFAULT	5
+#define	FLASH_LED_DEBOUNCE_MAX			3
+#define	FLASH_LED_HYSTERESIS_MAX		3
+#define	FLASH_LED_VPH_DROOP_THRESH_MAX		7
+#define	THERMAL_DERATE_SLOW_MAX			314592
+#define	THERMAL_DERATE_FAST_MAX			512
+#define	THERMAL_DEBOUNCE_TIME_MAX		64
+#define	THERMAL_DERATE_HYSTERESIS_MAX		3
+#define	FLASH_LED_THERMAL_THRSH_MIN		3
+#define	FLASH_LED_THERMAL_THRSH_MAX		7
+#define	FLASH_LED_THERMAL_OTST_LEVELS		3
+#define	FLASH_LED_VLED_MAX_DEFAULT_UV		3500000
+#define	FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA	4500000
+#define	FLASH_LED_RPARA_DEFAULT_UOHM		0
+#define	FLASH_LED_SAFETY_TMR_ENABLE		BIT(7)
+#define	FLASH_LED_LMH_LEVEL_DEFAULT		0
+#define	FLASH_LED_LMH_MITIGATION_ENABLE		1
+#define	FLASH_LED_LMH_MITIGATION_DISABLE	0
+#define	FLASH_LED_CHGR_MITIGATION_ENABLE	BIT(4)
+#define	FLASH_LED_CHGR_MITIGATION_DISABLE	0
+#define	FLASH_LED_MITIGATION_SEL_DEFAULT	2
+#define	FLASH_LED_MITIGATION_SEL_MAX		2
+#define	FLASH_LED_CHGR_MITIGATION_SEL_SHIFT	4
+#define	FLASH_LED_MITIGATION_THRSH_DEFAULT	0xA
+#define	FLASH_LED_MITIGATION_THRSH_MAX		0x1F
+#define	FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV	3700000
+#define	FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM	400000
+#define	FLASH_LED_IRES_BASE			3
+#define	FLASH_LED_IRES_DIVISOR			2500
+#define	FLASH_LED_IRES_MIN_UA			5000
+#define	FLASH_LED_IRES_DEFAULT_UA		12500
+#define	FLASH_LED_IRES_DEFAULT_VAL		0x00
+#define	FLASH_LED_HDRM_VOL_SHIFT		4
+#define	FLASH_LED_HDRM_VOL_DEFAULT_MV		0x80
+#define	FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV	0x04
+#define	FLASH_LED_HDRM_VOL_BASE_MV		125
+#define	FLASH_LED_HDRM_VOL_STEP_MV		25
+#define	FLASH_LED_STROBE_CFG_DEFAULT		0x00
+#define	FLASH_LED_HW_STROBE_OPTION_1		0x00
+#define	FLASH_LED_HW_STROBE_OPTION_2		0x01
+#define	FLASH_LED_HW_STROBE_OPTION_3		0x02
+#define	FLASH_LED_ENABLE			BIT(0)
+#define	FLASH_LED_MOD_ENABLE			BIT(7)
+#define	FLASH_LED_DISABLE			0x00
+#define	FLASH_LED_SAFETY_TMR_DISABLED		0x13
+#define	FLASH_LED_MIN_CURRENT_MA		25
+#define	FLASH_LED_MAX_TOTAL_CURRENT_MA		3750
+
+/* notifier call chain for flash-led irqs */
+static ATOMIC_NOTIFIER_HEAD(irq_notifier_list);
+
+enum flash_led_type {
+	FLASH_LED_TYPE_FLASH,
+	FLASH_LED_TYPE_TORCH,
+};
+
+enum {
+	LED1 = 0,
+	LED2,
+	LED3,
+};
+
+/*
+ * Configurations for each individual LED
+ */
+struct flash_node_data {
+	struct platform_device		*pdev;
+	struct led_classdev		cdev;
+	struct pinctrl			*pinctrl;
+	struct pinctrl_state		*gpio_state_active;
+	struct pinctrl_state		*gpio_state_suspend;
+	struct pinctrl_state		*hw_strobe_state_active;
+	struct pinctrl_state		*hw_strobe_state_suspend;
+	int				hw_strobe_gpio;
+	int				ires_ua;
+	int				max_current;
+	int				current_ma;
+	u8				duration;
+	u8				id;
+	u8				type;
+	u8				ires;
+	u8				hdrm_val;
+	u8				current_reg_val;
+	u8				trigger;
+	bool				led_on;
+};
+
+
+struct flash_switch_data {
+	struct platform_device		*pdev;
+	struct regulator		*vreg;
+	struct led_classdev		cdev;
+	int				led_mask;
+	bool				regulator_on;
+	bool				enabled;
+};
+
+/*
+ * Flash LED configuration read from device tree
+ */
+struct flash_led_platform_data {
+	struct pmic_revid_data	*pmic_rev_id;
+	int			*thermal_derate_current;
+	int			all_ramp_up_done_irq;
+	int			all_ramp_down_done_irq;
+	int			led_fault_irq;
+	int			ibatt_ocp_threshold_ua;
+	int			vled_max_uv;
+	int			rpara_uohm;
+	int			lmh_rbatt_threshold_uohm;
+	int			lmh_ocv_threshold_uv;
+	int			thermal_derate_slow;
+	int			thermal_derate_fast;
+	int			thermal_hysteresis;
+	int			thermal_debounce;
+	int			thermal_thrsh1;
+	int			thermal_thrsh2;
+	int			thermal_thrsh3;
+	u32			led1n2_iclamp_low_ma;
+	u32			led1n2_iclamp_mid_ma;
+	u32			led3_iclamp_low_ma;
+	u32			led3_iclamp_mid_ma;
+	u8			isc_delay;
+	u8			warmup_delay;
+	u8			current_derate_en_cfg;
+	u8			vph_droop_threshold;
+	u8			vph_droop_hysteresis;
+	u8			vph_droop_debounce;
+	u8			lmh_mitigation_sel;
+	u8			chgr_mitigation_sel;
+	u8			lmh_level;
+	u8			iled_thrsh_val;
+	u8			hw_strobe_option;
+	bool			hdrm_auto_mode_en;
+	bool			thermal_derate_en;
+	bool			otst_ramp_bkup_en;
+};
+
+/*
+ * Flash LED data structure containing flash LED attributes
+ */
+struct qpnp_flash_led {
+	struct flash_led_platform_data	*pdata;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct flash_node_data		*fnode;
+	struct flash_switch_data	*snode;
+	struct power_supply		*bms_psy;
+	struct notifier_block		nb;
+	spinlock_t			lock;
+	int				num_fnodes;
+	int				num_snodes;
+	int				enable;
+	u16				base;
+	bool				trigger_lmh;
+	bool				trigger_chgr;
+};
+
+static int thermal_derate_slow_table[] = {
+	128, 256, 512, 1024, 2048, 4096, 8192, 314592,
+};
+
+static int thermal_derate_fast_table[] = {
+	32, 64, 96, 128, 256, 384, 512,
+};
+
+static int otst1_threshold_table[] = {
+	85, 79, 73, 67, 109, 103, 97, 91,
+};
+
+static int otst2_threshold_table[] = {
+	110, 104, 98, 92, 134, 128, 122, 116,
+};
+
+static int otst3_threshold_table[] = {
+	125, 119, 113, 107, 149, 143, 137, 131,
+};
+
+static int qpnp_flash_led_read(struct qpnp_flash_led *led, u16 addr, u8 *data)
+{
+	int rc;
+	uint val;
+
+	rc = regmap_read(led->regmap, addr, &val);
+	if (rc < 0) {
+		pr_err("Unable to read from 0x%04X rc = %d\n", addr, rc);
+		return rc;
+	}
+
+	pr_debug("Read 0x%02X from addr 0x%04X\n", val, addr);
+	*data = (u8)val;
+	return 0;
+}
+
+static int qpnp_flash_led_write(struct qpnp_flash_led *led, u16 addr, u8 data)
+{
+	int rc;
+
+	rc = regmap_write(led->regmap, addr, data);
+	if (rc < 0) {
+		pr_err("Unable to write to 0x%04X rc = %d\n", addr, rc);
+		return rc;
+	}
+
+	pr_debug("Wrote 0x%02X to addr 0x%04X\n", data, addr);
+	return 0;
+}
+
+static int
+qpnp_flash_led_masked_read(struct qpnp_flash_led *led, u16 addr, u8 mask,
+								u8 *val)
+{
+	int rc;
+
+	rc = qpnp_flash_led_read(led, addr, val);
+	if (rc < 0)
+		return rc;
+
+	*val &= mask;
+	return rc;
+}
+
+static int
+qpnp_flash_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask,
+								u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(led->regmap, addr, mask, val);
+	if (rc < 0)
+		pr_err("Unable to update bits from 0x%04X, rc = %d\n", addr,
+			rc);
+	else
+		pr_debug("Wrote 0x%02X to addr 0x%04X\n", val, addr);
+
+	return rc;
+}
+
+static enum
+led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev)
+{
+	return led_cdev->brightness;
+}
+
+static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
+{
+	int rc, i, addr_offset;
+	u8 val = 0, mask;
+
+	for (i = 0; i < led->num_fnodes; i++) {
+		addr_offset = led->fnode[i].id;
+		rc = qpnp_flash_led_write(led,
+			FLASH_LED_REG_HDRM_PRGM(led->base + addr_offset),
+			led->fnode[i].hdrm_val);
+		if (rc < 0)
+			return rc;
+
+		val |= 0x1 << led->fnode[i].id;
+	}
+
+	rc = qpnp_flash_led_write(led,
+				FLASH_LED_REG_HDRM_AUTO_MODE_CTRL(led->base),
+				val);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_ISC_DELAY(led->base),
+			FLASH_LED_ISC_WARMUP_DELAY_MASK,
+			led->pdata->isc_delay);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_WARMUP_DELAY(led->base),
+			FLASH_LED_ISC_WARMUP_DELAY_MASK,
+			led->pdata->warmup_delay);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_CURRENT_DERATE_EN(led->base),
+			FLASH_LED_CURRENT_DERATE_EN_MASK,
+			led->pdata->current_derate_en_cfg);
+	if (rc < 0)
+		return rc;
+
+	val = (led->pdata->otst_ramp_bkup_en << THERMAL_OTST1_RAMP_CTRL_SHIFT);
+	mask = THERMAL_OTST1_RAMP_CTRL_MASK;
+	if (led->pdata->thermal_derate_slow >= 0) {
+		val |= (led->pdata->thermal_derate_slow <<
+				THERMAL_DERATE_SLOW_SHIFT);
+		mask |= THERMAL_DERATE_SLOW_MASK;
+	}
+
+	if (led->pdata->thermal_derate_fast >= 0) {
+		val |= led->pdata->thermal_derate_fast;
+		mask |= THERMAL_DERATE_FAST_MASK;
+	}
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_RMP_DN_RATE(led->base),
+			mask, val);
+	if (rc < 0)
+		return rc;
+
+	if (led->pdata->thermal_debounce >= 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_THERMAL_DEBOUNCE(led->base),
+				FLASH_LED_THERMAL_DEBOUNCE_MASK,
+				led->pdata->thermal_debounce);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->thermal_hysteresis >= 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_THERMAL_HYSTERESIS(led->base),
+				FLASH_LED_THERMAL_HYSTERESIS_MASK,
+				led->pdata->thermal_hysteresis);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->thermal_thrsh1 >= 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_THERMAL_THRSH1(led->base),
+				FLASH_LED_THERMAL_THRSH_MASK,
+				led->pdata->thermal_thrsh1);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->thermal_thrsh2 >= 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_THERMAL_THRSH2(led->base),
+				FLASH_LED_THERMAL_THRSH_MASK,
+				led->pdata->thermal_thrsh2);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->thermal_thrsh3 >= 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_THERMAL_THRSH3(led->base),
+				FLASH_LED_THERMAL_THRSH_MASK,
+				led->pdata->thermal_thrsh3);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_VPH_DROOP_DEBOUNCE(led->base),
+			FLASH_LED_VPH_DROOP_DEBOUNCE_MASK,
+			led->pdata->vph_droop_debounce);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base),
+			FLASH_LED_VPH_DROOP_THRESHOLD_MASK,
+			led->pdata->vph_droop_threshold);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_VPH_DROOP_THRESHOLD(led->base),
+			FLASH_LED_VPH_DROOP_HYSTERESIS_MASK,
+			led->pdata->vph_droop_hysteresis);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_MITIGATION_SEL(led->base),
+			FLASH_LED_LMH_MITIGATION_SEL_MASK,
+			led->pdata->lmh_mitigation_sel);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_MITIGATION_SEL(led->base),
+			FLASH_LED_CHGR_MITIGATION_SEL_MASK,
+			led->pdata->chgr_mitigation_sel);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_LMH_LEVEL(led->base),
+			FLASH_LED_LMH_LEVEL_MASK,
+			led->pdata->lmh_level);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_ILED_GRT_THRSH(led->base),
+			FLASH_LED_ILED_GRT_THRSH_MASK,
+			led->pdata->iled_thrsh_val);
+	if (rc < 0)
+		return rc;
+
+	if (led->pdata->led1n2_iclamp_low_ma) {
+		val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_low_ma,
+						led->fnode[0].ires_ua);
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_LED1N2_ICLAMP_LOW(led->base),
+				FLASH_LED_CURRENT_MASK, val);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->led1n2_iclamp_mid_ma) {
+		val = CURRENT_MA_TO_REG_VAL(led->pdata->led1n2_iclamp_mid_ma,
+						led->fnode[0].ires_ua);
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_LED1N2_ICLAMP_MID(led->base),
+				FLASH_LED_CURRENT_MASK, val);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->led3_iclamp_low_ma) {
+		val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_low_ma,
+						led->fnode[3].ires_ua);
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_LED3_ICLAMP_LOW(led->base),
+				FLASH_LED_CURRENT_MASK, val);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (led->pdata->led3_iclamp_mid_ma) {
+		val = CURRENT_MA_TO_REG_VAL(led->pdata->led3_iclamp_mid_ma,
+						led->fnode[3].ires_ua);
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_LED3_ICLAMP_MID(led->base),
+				FLASH_LED_CURRENT_MASK, val);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_flash_led_hw_strobe_enable(struct flash_node_data *fnode,
+						int hw_strobe_option, bool on)
+{
+	int rc = 0;
+
+	/*
+	 * If the LED controlled by this fnode is not GPIO controlled
+	 * for the given strobe_option, return.
+	 */
+	if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_1)
+		return 0;
+	else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_2
+						&& fnode->id != LED3)
+		return 0;
+	else if (hw_strobe_option == FLASH_LED_HW_STROBE_OPTION_3
+						&& fnode->id == LED1)
+		return 0;
+
+	if (gpio_is_valid(fnode->hw_strobe_gpio)) {
+		gpio_set_value(fnode->hw_strobe_gpio, on ? 1 : 0);
+	} else if (fnode->hw_strobe_state_active &&
+					fnode->hw_strobe_state_suspend) {
+		rc = pinctrl_select_state(fnode->pinctrl,
+			on ? fnode->hw_strobe_state_active :
+			fnode->hw_strobe_state_suspend);
+		if (rc < 0) {
+			pr_err("failed to change hw strobe pin state\n");
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int qpnp_flash_led_regulator_enable(struct qpnp_flash_led *led,
+				struct flash_switch_data *snode, bool on)
+{
+	int rc = 0;
+
+	if (!snode || !snode->vreg)
+		return 0;
+
+	if (snode->regulator_on == on)
+		return 0;
+
+	if (on)
+		rc = regulator_enable(snode->vreg);
+	else
+		rc = regulator_disable(snode->vreg);
+
+	if (rc < 0) {
+		pr_err("regulator_%s failed, rc=%d\n",
+			on ? "enable" : "disable", rc);
+		return rc;
+	}
+
+	snode->regulator_on = on ? true : false;
+	return 0;
+}
+
+static int get_property_from_fg(struct qpnp_flash_led *led,
+		enum power_supply_property prop, int *val)
+{
+	int rc;
+	union power_supply_propval pval = {0, };
+
+	if (!led->bms_psy) {
+		pr_err("no bms psy found\n");
+		return -EINVAL;
+	}
+
+	rc = power_supply_get_property(led->bms_psy, prop, &pval);
+	if (rc) {
+		pr_err("bms psy doesn't support reading prop %d rc = %d\n",
+			prop, rc);
+		return rc;
+	}
+
+	*val = pval.intval;
+	return rc;
+}
+
+#define VOLTAGE_HDRM_DEFAULT_MV	350
+static int qpnp_flash_led_get_voltage_headroom(struct qpnp_flash_led *led)
+{
+	int i, voltage_hdrm_mv = 0, voltage_hdrm_max = 0;
+
+	for (i = 0; i < led->num_fnodes; i++) {
+		if (led->fnode[i].led_on) {
+			if (led->fnode[i].id < 2) {
+				if (led->fnode[i].current_ma < 750)
+					voltage_hdrm_mv = 125;
+				else if (led->fnode[i].current_ma < 1000)
+					voltage_hdrm_mv = 175;
+				else if (led->fnode[i].current_ma < 1250)
+					voltage_hdrm_mv = 250;
+				else
+					voltage_hdrm_mv = 350;
+			} else {
+				if (led->fnode[i].current_ma < 375)
+					voltage_hdrm_mv = 125;
+				else if (led->fnode[i].current_ma < 500)
+					voltage_hdrm_mv = 175;
+				else if (led->fnode[i].current_ma < 625)
+					voltage_hdrm_mv = 250;
+				else
+					voltage_hdrm_mv = 350;
+			}
+
+			voltage_hdrm_max = max(voltage_hdrm_max,
+						voltage_hdrm_mv);
+		}
+	}
+
+	if (!voltage_hdrm_max)
+		return VOLTAGE_HDRM_DEFAULT_MV;
+
+	return voltage_hdrm_max;
+}
+
+#define UCONV			1000000LL
+#define MCONV			1000LL
+#define FLASH_VDIP_MARGIN	50000
+#define BOB_EFFICIENCY		900LL
+#define VIN_FLASH_MIN_UV	3300000LL
+static int qpnp_flash_led_calc_max_current(struct qpnp_flash_led *led)
+{
+	int ocv_uv, rbatt_uohm, ibat_now, voltage_hdrm_mv, rc;
+	int64_t ibat_flash_ua, avail_flash_ua, avail_flash_power_fw;
+	int64_t ibat_safe_ua, vin_flash_uv, vph_flash_uv, vph_flash_vdip;
+
+	/* RESISTANCE = esr_uohm + rslow_uohm */
+	rc = get_property_from_fg(led, POWER_SUPPLY_PROP_RESISTANCE,
+			&rbatt_uohm);
+	if (rc < 0) {
+		pr_err("bms psy does not support resistance, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* If no battery is connected, return max possible flash current */
+	if (!rbatt_uohm)
+		return FLASH_LED_MAX_TOTAL_CURRENT_MA;
+
+	rc = get_property_from_fg(led, POWER_SUPPLY_PROP_VOLTAGE_OCV, &ocv_uv);
+	if (rc < 0) {
+		pr_err("bms psy does not support OCV, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = get_property_from_fg(led, POWER_SUPPLY_PROP_CURRENT_NOW,
+			&ibat_now);
+	if (rc < 0) {
+		pr_err("bms psy does not support current, rc=%d\n", rc);
+		return rc;
+	}
+
+	rbatt_uohm += led->pdata->rpara_uohm;
+	voltage_hdrm_mv = qpnp_flash_led_get_voltage_headroom(led);
+	vph_flash_vdip =
+		VPH_DROOP_THRESH_VAL_TO_UV(led->pdata->vph_droop_threshold)
+							+ FLASH_VDIP_MARGIN;
+
+	/* Check if LMH_MITIGATION needs to be triggered */
+	if (!led->trigger_lmh && (ocv_uv < led->pdata->lmh_ocv_threshold_uv ||
+			rbatt_uohm > led->pdata->lmh_rbatt_threshold_uohm)) {
+		led->trigger_lmh = true;
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MITIGATION_SW(led->base),
+				FLASH_LED_LMH_MITIGATION_EN_MASK,
+				FLASH_LED_LMH_MITIGATION_ENABLE);
+		if (rc < 0) {
+			pr_err("trigger lmh mitigation failed, rc=%d\n", rc);
+			return rc;
+		}
+
+		/* Wait for LMH mitigation to take effect */
+		udelay(100);
+
+		return qpnp_flash_led_calc_max_current(led);
+	}
+
+	/*
+	 * Calculate the maximum current that can pulled out of the battery
+	 * before the battery voltage dips below a safe threshold.
+	 */
+	ibat_safe_ua = div_s64((ocv_uv - vph_flash_vdip) * UCONV,
+				rbatt_uohm);
+
+	if (ibat_safe_ua <= led->pdata->ibatt_ocp_threshold_ua) {
+		/*
+		 * If the calculated current is below the OCP threshold, then
+		 * use it as the possible flash current.
+		 */
+		ibat_flash_ua = ibat_safe_ua - ibat_now;
+		vph_flash_uv = vph_flash_vdip;
+	} else {
+		/*
+		 * If the calculated current is above the OCP threshold, then
+		 * use the ocp threshold instead.
+		 *
+		 * Any higher current will be tripping the battery OCP.
+		 */
+		ibat_flash_ua = led->pdata->ibatt_ocp_threshold_ua - ibat_now;
+		vph_flash_uv = ocv_uv - div64_s64((int64_t)rbatt_uohm
+				* led->pdata->ibatt_ocp_threshold_ua, UCONV);
+	}
+	/* Calculate the input voltage of the flash module. */
+	vin_flash_uv = max((led->pdata->vled_max_uv +
+				(voltage_hdrm_mv * MCONV)), VIN_FLASH_MIN_UV);
+	/* Calculate the available power for the flash module. */
+	avail_flash_power_fw = BOB_EFFICIENCY * vph_flash_uv * ibat_flash_ua;
+	/*
+	 * Calculate the available amount of current the flash module can draw
+	 * before collapsing the battery. (available power/ flash input voltage)
+	 */
+	avail_flash_ua = div64_s64(avail_flash_power_fw, vin_flash_uv * MCONV);
+	pr_debug("avail_iflash=%lld, ocv=%d, ibat=%d, rbatt=%d, trigger_lmh=%d\n",
+		avail_flash_ua, ocv_uv, ibat_now, rbatt_uohm, led->trigger_lmh);
+	return min(FLASH_LED_MAX_TOTAL_CURRENT_MA,
+			(int)(div64_s64(avail_flash_ua, MCONV)));
+}
+
+static int qpnp_flash_led_calc_thermal_current_lim(struct qpnp_flash_led *led)
+{
+	int thermal_current_lim = 0;
+	int rc;
+	u8 thermal_thrsh1, thermal_thrsh2, thermal_thrsh3, otst_status;
+
+	/* Store THERMAL_THRSHx register values */
+	rc = qpnp_flash_led_masked_read(led,
+			FLASH_LED_REG_THERMAL_THRSH1(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			&thermal_thrsh1);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_read(led,
+			FLASH_LED_REG_THERMAL_THRSH2(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			&thermal_thrsh2);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_read(led,
+			FLASH_LED_REG_THERMAL_THRSH3(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			&thermal_thrsh3);
+	if (rc < 0)
+		return rc;
+
+	/* Lower THERMAL_THRSHx thresholds to minimum */
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_THRSH1(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			FLASH_LED_THERMAL_THRSH_MIN);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_THRSH2(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			FLASH_LED_THERMAL_THRSH_MIN);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_THRSH3(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			FLASH_LED_THERMAL_THRSH_MIN);
+	if (rc < 0)
+		return rc;
+
+	/* Check THERMAL_OTST status */
+	rc = qpnp_flash_led_read(led,
+			FLASH_LED_REG_LED_STATUS2(led->base),
+			&otst_status);
+	if (rc < 0)
+		return rc;
+
+	/* Look up current limit based on THERMAL_OTST status */
+	if (otst_status)
+		thermal_current_lim =
+			led->pdata->thermal_derate_current[otst_status >> 1];
+
+	/* Restore THERMAL_THRESHx registers to original values */
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_THRSH1(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			thermal_thrsh1);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_THRSH2(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			thermal_thrsh2);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_THERMAL_THRSH3(led->base),
+			FLASH_LED_THERMAL_THRSH_MASK,
+			thermal_thrsh3);
+	if (rc < 0)
+		return rc;
+
+	return thermal_current_lim;
+}
+
+static int qpnp_flash_led_get_max_avail_current(struct qpnp_flash_led *led)
+{
+	int max_avail_current, thermal_current_lim = 0;
+
+	led->trigger_lmh = false;
+	max_avail_current = qpnp_flash_led_calc_max_current(led);
+	if (led->pdata->thermal_derate_en)
+		thermal_current_lim =
+			qpnp_flash_led_calc_thermal_current_lim(led);
+
+	if (thermal_current_lim)
+		max_avail_current = min(max_avail_current, thermal_current_lim);
+
+	return max_avail_current;
+}
+
+static void qpnp_flash_led_node_set(struct flash_node_data *fnode, int value)
+{
+	int prgm_current_ma = value;
+
+	if (value <= 0)
+		prgm_current_ma = 0;
+	else if (value < FLASH_LED_MIN_CURRENT_MA)
+		prgm_current_ma = FLASH_LED_MIN_CURRENT_MA;
+
+	prgm_current_ma = min(prgm_current_ma, fnode->max_current);
+	fnode->current_ma = prgm_current_ma;
+	fnode->cdev.brightness = prgm_current_ma;
+	fnode->current_reg_val = CURRENT_MA_TO_REG_VAL(prgm_current_ma,
+					fnode->ires_ua);
+	fnode->led_on = prgm_current_ma != 0;
+}
+
+static int qpnp_flash_led_switch_disable(struct flash_switch_data *snode)
+{
+	struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+	int i, rc, addr_offset;
+
+	rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_EN_LED_CTRL(led->base),
+				snode->led_mask, FLASH_LED_DISABLE);
+	if (rc < 0)
+		return rc;
+
+	if (led->trigger_lmh) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MITIGATION_SW(led->base),
+				FLASH_LED_LMH_MITIGATION_EN_MASK,
+				FLASH_LED_LMH_MITIGATION_DISABLE);
+		if (rc < 0) {
+			pr_err("disable lmh mitigation failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (!led->trigger_chgr) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MITIGATION_SW(led->base),
+				FLASH_LED_CHGR_MITIGATION_EN_MASK,
+				FLASH_LED_CHGR_MITIGATION_DISABLE);
+		if (rc < 0) {
+			pr_err("disable chgr mitigation failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	led->enable--;
+	if (led->enable == 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MOD_CTRL(led->base),
+				FLASH_LED_MOD_CTRL_MASK, FLASH_LED_DISABLE);
+		if (rc < 0)
+			return rc;
+	}
+
+	for (i = 0; i < led->num_fnodes; i++) {
+		if (!led->fnode[i].led_on ||
+				!(snode->led_mask & BIT(led->fnode[i].id)))
+			continue;
+
+		addr_offset = led->fnode[i].id;
+		rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset),
+			FLASH_LED_CURRENT_MASK, 0);
+		if (rc < 0)
+			return rc;
+
+		led->fnode[i].led_on = false;
+
+		if (led->fnode[i].pinctrl) {
+			rc = pinctrl_select_state(led->fnode[i].pinctrl,
+					led->fnode[i].gpio_state_suspend);
+			if (rc < 0) {
+				pr_err("failed to disable GPIO, rc=%d\n", rc);
+				return rc;
+			}
+		}
+
+		if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
+			rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
+					led->pdata->hw_strobe_option, false);
+			if (rc < 0) {
+				pr_err("Unable to disable hw strobe, rc=%d\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	snode->enabled = false;
+	return 0;
+}
+
+static int qpnp_flash_led_switch_set(struct flash_switch_data *snode, bool on)
+{
+	struct qpnp_flash_led *led = dev_get_drvdata(&snode->pdev->dev);
+	int rc, i, addr_offset;
+	u8 val, mask;
+
+	if (snode->enabled == on) {
+		pr_debug("Switch node is already %s!\n",
+			on ? "enabled" : "disabled");
+		return 0;
+	}
+
+	if (!on) {
+		rc = qpnp_flash_led_switch_disable(snode);
+		return rc;
+	}
+
+	/* Iterate over all leds for this switch node */
+	val = 0;
+	for (i = 0; i < led->num_fnodes; i++)
+		if (snode->led_mask & BIT(led->fnode[i].id))
+			val |= led->fnode[i].ires << (led->fnode[i].id * 2);
+
+	rc = qpnp_flash_led_masked_write(led, FLASH_LED_REG_IRES(led->base),
+						FLASH_LED_CURRENT_MASK, val);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_flash_led_masked_write(led,
+					FLASH_LED_REG_STROBE_CFG(led->base),
+					FLASH_LED_ENABLE_MASK,
+					led->pdata->hw_strobe_option);
+	if (rc < 0)
+		return rc;
+
+	val = 0;
+	for (i = 0; i < led->num_fnodes; i++) {
+		if (!led->fnode[i].led_on ||
+				!(snode->led_mask & BIT(led->fnode[i].id)))
+			continue;
+
+		addr_offset = led->fnode[i].id;
+		if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT)
+			mask = FLASH_HW_STROBE_MASK;
+		else
+			mask = FLASH_LED_HW_SW_STROBE_SEL_BIT;
+		rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_STROBE_CTRL(led->base + addr_offset),
+			mask, led->fnode[i].trigger);
+		if (rc < 0)
+			return rc;
+
+		rc = qpnp_flash_led_masked_write(led,
+			FLASH_LED_REG_TGR_CURRENT(led->base + addr_offset),
+			FLASH_LED_CURRENT_MASK, led->fnode[i].current_reg_val);
+		if (rc < 0)
+			return rc;
+
+		rc = qpnp_flash_led_write(led,
+			FLASH_LED_REG_SAFETY_TMR(led->base + addr_offset),
+			led->fnode[i].duration);
+		if (rc < 0)
+			return rc;
+
+		val |= FLASH_LED_ENABLE << led->fnode[i].id;
+
+		if (led->fnode[i].pinctrl) {
+			rc = pinctrl_select_state(led->fnode[i].pinctrl,
+					led->fnode[i].gpio_state_active);
+			if (rc < 0) {
+				pr_err("failed to enable GPIO rc=%d\n", rc);
+				return rc;
+			}
+		}
+
+		if (led->fnode[i].trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
+			rc = qpnp_flash_led_hw_strobe_enable(&led->fnode[i],
+					led->pdata->hw_strobe_option, true);
+			if (rc < 0) {
+				pr_err("Unable to enable hw strobe rc=%d\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	if (led->enable == 0) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MOD_CTRL(led->base),
+				FLASH_LED_MOD_CTRL_MASK, FLASH_LED_MOD_ENABLE);
+		if (rc < 0)
+			return rc;
+	}
+	led->enable++;
+
+	if (led->trigger_lmh) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MITIGATION_SW(led->base),
+				FLASH_LED_LMH_MITIGATION_EN_MASK,
+				FLASH_LED_LMH_MITIGATION_ENABLE);
+		if (rc < 0) {
+			pr_err("trigger lmh mitigation failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (led->trigger_chgr) {
+		rc = qpnp_flash_led_masked_write(led,
+				FLASH_LED_REG_MITIGATION_SW(led->base),
+				FLASH_LED_CHGR_MITIGATION_EN_MASK,
+				FLASH_LED_CHGR_MITIGATION_ENABLE);
+		if (rc < 0) {
+			pr_err("trigger chgr mitigation failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	rc = qpnp_flash_led_masked_write(led,
+					FLASH_LED_EN_LED_CTRL(led->base),
+					snode->led_mask, val);
+	if (rc < 0)
+		return rc;
+
+	snode->enabled = true;
+	return 0;
+}
+
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+					int *max_current)
+{
+	struct led_classdev *led_cdev;
+	struct flash_switch_data *snode;
+	struct qpnp_flash_led *led;
+	int rc;
+
+	if (!trig) {
+		pr_err("Invalid led_trigger provided\n");
+		return -EINVAL;
+	}
+
+	led_cdev = trigger_to_lcdev(trig);
+	if (!led_cdev) {
+		pr_err("Invalid led_cdev in trigger %s\n", trig->name);
+		return -EINVAL;
+	}
+
+	snode = container_of(led_cdev, struct flash_switch_data, cdev);
+	led = dev_get_drvdata(&snode->pdev->dev);
+
+	if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
+		pr_err("Invalid options %d\n", options);
+		return -EINVAL;
+	}
+
+	if (options & ENABLE_REGULATOR) {
+		rc = qpnp_flash_led_regulator_enable(led, snode, true);
+		if (rc < 0) {
+			pr_err("enable regulator failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (options & DISABLE_REGULATOR) {
+		rc = qpnp_flash_led_regulator_enable(led, snode, false);
+		if (rc < 0) {
+			pr_err("disable regulator failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (options & QUERY_MAX_CURRENT) {
+		rc = qpnp_flash_led_get_max_avail_current(led);
+		if (rc < 0) {
+			pr_err("query max current failed, rc=%d\n", rc);
+			return rc;
+		}
+		*max_current = rc;
+	}
+
+	led->trigger_chgr = false;
+	if (options & PRE_FLASH)
+		led->trigger_chgr = true;
+
+	return 0;
+}
+
+static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	struct flash_node_data *fnode = NULL;
+	struct flash_switch_data *snode = NULL;
+	struct qpnp_flash_led *led = NULL;
+	int rc;
+
+	/*
+	 * strncmp() must be used here since a prefix comparison is required
+	 * in order to support names like led:switch_0 and led:flash_1.
+	 */
+	if (!strncmp(led_cdev->name, "led:switch", strlen("led:switch"))) {
+		snode = container_of(led_cdev, struct flash_switch_data, cdev);
+		led = dev_get_drvdata(&snode->pdev->dev);
+	} else if (!strncmp(led_cdev->name, "led:flash", strlen("led:flash")) ||
+			!strncmp(led_cdev->name, "led:torch",
+						strlen("led:torch"))) {
+		fnode = container_of(led_cdev, struct flash_node_data, cdev);
+		led = dev_get_drvdata(&fnode->pdev->dev);
+	}
+
+	if (!led) {
+		pr_err("Failed to get flash driver data\n");
+		return;
+	}
+
+	spin_lock(&led->lock);
+	if (snode) {
+		rc = qpnp_flash_led_switch_set(snode, value > 0);
+		if (rc < 0)
+			pr_err("Failed to set flash LED switch rc=%d\n", rc);
+	} else if (fnode) {
+		qpnp_flash_led_node_set(fnode, value);
+	}
+
+	spin_unlock(&led->lock);
+}
+
+/* sysfs show function for flash_max_current */
+static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	int rc;
+	struct flash_switch_data *snode;
+	struct qpnp_flash_led *led;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+
+	snode = container_of(led_cdev, struct flash_switch_data, cdev);
+	led = dev_get_drvdata(&snode->pdev->dev);
+
+	rc = qpnp_flash_led_get_max_avail_current(led);
+	if (rc < 0)
+		pr_err("query max current failed, rc=%d\n", rc);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", rc);
+}
+
+/* sysfs attributes exported by flash_led */
+static struct device_attribute qpnp_flash_led_attrs[] = {
+	__ATTR(max_current, 0664, qpnp_flash_led_max_current_show, NULL),
+};
+
+static int flash_led_psy_notifier_call(struct notifier_block *nb,
+		unsigned long ev, void *v)
+{
+	struct power_supply *psy = v;
+	struct qpnp_flash_led *led =
+			container_of(nb, struct qpnp_flash_led, nb);
+
+	if (ev != PSY_EVENT_PROP_CHANGED)
+		return NOTIFY_OK;
+
+	if (!strcmp(psy->desc->name, "bms")) {
+		led->bms_psy = power_supply_get_by_name("bms");
+		if (!led->bms_psy)
+			pr_err("Failed to get bms power_supply\n");
+		else
+			power_supply_unreg_notifier(&led->nb);
+	}
+
+	return NOTIFY_OK;
+}
+
+static int flash_led_psy_register_notifier(struct qpnp_flash_led *led)
+{
+	int rc;
+
+	led->nb.notifier_call = flash_led_psy_notifier_call;
+	rc = power_supply_reg_notifier(&led->nb);
+	if (rc < 0) {
+		pr_err("Couldn't register psy notifier, rc = %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/* irq handler */
+static irqreturn_t qpnp_flash_led_irq_handler(int irq, void *_led)
+{
+	struct qpnp_flash_led *led = _led;
+	enum flash_led_irq_type irq_type = INVALID_IRQ;
+	int rc;
+	u8 irq_status, led_status1, led_status2;
+
+	pr_debug("irq received, irq=%d\n", irq);
+
+	rc = qpnp_flash_led_read(led,
+			FLASH_LED_REG_INT_RT_STS(led->base), &irq_status);
+	if (rc < 0) {
+		pr_err("Failed to read interrupt status reg, rc=%d\n", rc);
+		goto exit;
+	}
+
+	if (irq == led->pdata->all_ramp_up_done_irq)
+		irq_type = ALL_RAMP_UP_DONE_IRQ;
+	else if (irq == led->pdata->all_ramp_down_done_irq)
+		irq_type = ALL_RAMP_DOWN_DONE_IRQ;
+	else if (irq == led->pdata->led_fault_irq)
+		irq_type = LED_FAULT_IRQ;
+
+	if (irq_type == ALL_RAMP_UP_DONE_IRQ)
+		atomic_notifier_call_chain(&irq_notifier_list,
+						irq_type, NULL);
+
+	if (irq_type == LED_FAULT_IRQ) {
+		rc = qpnp_flash_led_read(led,
+			FLASH_LED_REG_LED_STATUS1(led->base), &led_status1);
+		if (rc < 0) {
+			pr_err("Failed to read led_status1 reg, rc=%d\n", rc);
+			goto exit;
+		}
+
+		rc = qpnp_flash_led_read(led,
+			FLASH_LED_REG_LED_STATUS2(led->base), &led_status2);
+		if (rc < 0) {
+			pr_err("Failed to read led_status2 reg, rc=%d\n", rc);
+			goto exit;
+		}
+
+		if (led_status1)
+			pr_emerg("led short/open fault detected! led_status1=%x\n",
+				led_status1);
+
+		if (led_status2 & FLASH_LED_VPH_DROOP_FAULT_MASK)
+			pr_emerg("led vph_droop fault detected!\n");
+	}
+
+	pr_debug("irq handled, irq_type=%x, irq_status=%x\n", irq_type,
+		irq_status);
+
+exit:
+	return IRQ_HANDLED;
+}
+
+int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_register(&irq_notifier_list, nb);
+}
+
+int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb)
+{
+	return atomic_notifier_chain_unregister(&irq_notifier_list, nb);
+}
+
+static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
+			struct flash_node_data *fnode, struct device_node *node)
+{
+	const char *temp_string;
+	int rc;
+	u32 val;
+	bool strobe_sel = 0, edge_trigger = 0, active_high = 0;
+
+	fnode->pdev = led->pdev;
+	fnode->cdev.brightness_set = qpnp_flash_led_brightness_set;
+	fnode->cdev.brightness_get = qpnp_flash_led_brightness_get;
+
+	rc = of_property_read_string(node, "qcom,led-name", &fnode->cdev.name);
+	if (rc < 0) {
+		pr_err("Unable to read flash LED names\n");
+		return rc;
+	}
+
+	rc = of_property_read_string(node, "label", &temp_string);
+	if (!rc) {
+		if (!strcmp(temp_string, "flash")) {
+			fnode->type = FLASH_LED_TYPE_FLASH;
+		} else if (!strcmp(temp_string, "torch")) {
+			fnode->type = FLASH_LED_TYPE_TORCH;
+		} else {
+			pr_err("Wrong flash LED type\n");
+			return rc;
+		}
+	} else {
+		pr_err("Unable to read flash LED label\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,id", &val);
+	if (!rc) {
+		fnode->id = (u8)val;
+	} else {
+		pr_err("Unable to read flash LED ID\n");
+		return rc;
+	}
+
+	rc = of_property_read_string(node, "qcom,default-led-trigger",
+						&fnode->cdev.default_trigger);
+	if (rc < 0) {
+		pr_err("Unable to read trigger name\n");
+		return rc;
+	}
+
+	fnode->ires_ua = FLASH_LED_IRES_DEFAULT_UA;
+	fnode->ires = FLASH_LED_IRES_DEFAULT_VAL;
+	rc = of_property_read_u32(node, "qcom,ires-ua", &val);
+	if (!rc) {
+		fnode->ires_ua = val;
+		fnode->ires = FLASH_LED_IRES_BASE -
+			(val - FLASH_LED_IRES_MIN_UA) / FLASH_LED_IRES_DIVISOR;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read current resolution rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,max-current", &val);
+	if (!rc) {
+		if (val < FLASH_LED_MIN_CURRENT_MA)
+			val = FLASH_LED_MIN_CURRENT_MA;
+		fnode->max_current = val;
+		fnode->cdev.max_brightness = val;
+	} else {
+		pr_err("Unable to read max current, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,current-ma", &val);
+	if (!rc) {
+		if (val < FLASH_LED_MIN_CURRENT_MA ||
+				val > fnode->max_current)
+			pr_warn("Invalid operational current specified, capping it\n");
+		if (val < FLASH_LED_MIN_CURRENT_MA)
+			val = FLASH_LED_MIN_CURRENT_MA;
+		if (val > fnode->max_current)
+			val = fnode->max_current;
+		fnode->current_ma = val;
+		fnode->cdev.brightness = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read operational current, rc=%d\n", rc);
+		return rc;
+	}
+
+	fnode->duration = FLASH_LED_SAFETY_TMR_DISABLED;
+	rc = of_property_read_u32(node, "qcom,duration-ms", &val);
+	if (!rc) {
+		fnode->duration = (u8)(SAFETY_TMR_TO_REG_VAL(val) |
+					FLASH_LED_SAFETY_TMR_ENABLE);
+	} else if (rc == -EINVAL) {
+		if (fnode->type == FLASH_LED_TYPE_FLASH) {
+			pr_err("Timer duration is required for flash LED\n");
+			return rc;
+		}
+	} else {
+		pr_err("Unable to read timer duration\n");
+		return rc;
+	}
+
+	fnode->hdrm_val = FLASH_LED_HDRM_VOL_DEFAULT_MV;
+	rc = of_property_read_u32(node, "qcom,hdrm-voltage-mv", &val);
+	if (!rc) {
+		val = (val - FLASH_LED_HDRM_VOL_BASE_MV) /
+						FLASH_LED_HDRM_VOL_STEP_MV;
+		fnode->hdrm_val = (val << FLASH_LED_HDRM_VOL_SHIFT) &
+							FLASH_LED_HDRM_VOL_MASK;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read headroom voltage\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,hdrm-vol-hi-lo-win-mv", &val);
+	if (!rc) {
+		fnode->hdrm_val |= (val / FLASH_LED_HDRM_VOL_STEP_MV) &
+						~FLASH_LED_HDRM_VOL_MASK;
+	} else if (rc == -EINVAL) {
+		fnode->hdrm_val |= FLASH_LED_HDRM_VOL_HI_LO_WIN_DEFAULT_MV;
+	} else {
+		pr_err("Unable to read hdrm hi-lo window voltage\n");
+		return rc;
+	}
+
+	strobe_sel = of_property_read_bool(node, "qcom,hw-strobe-sel");
+	if (strobe_sel) {
+		edge_trigger = of_property_read_bool(node,
+						"qcom,hw-strobe-edge-trigger");
+		active_high = !of_property_read_bool(node,
+						"qcom,hw-strobe-active-low");
+	}
+	fnode->trigger = (strobe_sel << 2) | (edge_trigger << 1) | active_high;
+
+	if (fnode->trigger & FLASH_LED_HW_SW_STROBE_SEL_BIT) {
+		if (of_find_property(node, "qcom,hw-strobe-gpio", NULL)) {
+			fnode->hw_strobe_gpio = of_get_named_gpio(node,
+						"qcom,hw-strobe-gpio", 0);
+			if (fnode->hw_strobe_gpio < 0) {
+				pr_err("Invalid gpio specified\n");
+				return fnode->hw_strobe_gpio;
+			}
+			gpio_direction_output(fnode->hw_strobe_gpio, 0);
+		} else {
+			fnode->hw_strobe_gpio = -1;
+			fnode->hw_strobe_state_active =
+				pinctrl_lookup_state(fnode->pinctrl,
+				"strobe_enable");
+			if (IS_ERR_OR_NULL(fnode->hw_strobe_state_active)) {
+				pr_err("No active pin for hardware strobe, rc=%ld\n",
+					PTR_ERR(fnode->hw_strobe_state_active));
+				fnode->hw_strobe_state_active = NULL;
+			}
+
+			fnode->hw_strobe_state_suspend =
+				pinctrl_lookup_state(fnode->pinctrl,
+				"strobe_disable");
+			if (IS_ERR_OR_NULL(fnode->hw_strobe_state_suspend)) {
+				pr_err("No suspend pin for hardware strobe, rc=%ld\n",
+					PTR_ERR(fnode->hw_strobe_state_suspend)
+					);
+				fnode->hw_strobe_state_suspend = NULL;
+			}
+		}
+	}
+
+	rc = led_classdev_register(&led->pdev->dev, &fnode->cdev);
+	if (rc < 0) {
+		pr_err("Unable to register led node %d\n", fnode->id);
+		return rc;
+	}
+
+	fnode->cdev.dev->of_node = node;
+
+	fnode->pinctrl = devm_pinctrl_get(fnode->cdev.dev);
+	if (IS_ERR_OR_NULL(fnode->pinctrl)) {
+		pr_debug("No pinctrl defined\n");
+		fnode->pinctrl = NULL;
+	} else {
+		fnode->gpio_state_active =
+			pinctrl_lookup_state(fnode->pinctrl, "led_enable");
+		if (IS_ERR_OR_NULL(fnode->gpio_state_active)) {
+			pr_err("Cannot lookup LED active state\n");
+			devm_pinctrl_put(fnode->pinctrl);
+			fnode->pinctrl = NULL;
+			return PTR_ERR(fnode->gpio_state_active);
+		}
+
+		fnode->gpio_state_suspend =
+			pinctrl_lookup_state(fnode->pinctrl, "led_disable");
+		if (IS_ERR_OR_NULL(fnode->gpio_state_suspend)) {
+			pr_err("Cannot lookup LED disable state\n");
+			devm_pinctrl_put(fnode->pinctrl);
+			fnode->pinctrl = NULL;
+			return PTR_ERR(fnode->gpio_state_suspend);
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_flash_led_parse_and_register_switch(struct qpnp_flash_led *led,
+						struct flash_switch_data *snode,
+						struct device_node *node)
+{
+	int rc = 0, num;
+	char reg_name[16], reg_sup_name[16];
+
+	rc = of_property_read_string(node, "qcom,led-name", &snode->cdev.name);
+	if (rc < 0) {
+		pr_err("Failed to read switch node name, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = sscanf(snode->cdev.name, "led:switch_%d", &num);
+	if (!rc) {
+		pr_err("No number for switch device?\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_string(node, "qcom,default-led-trigger",
+					&snode->cdev.default_trigger);
+	if (rc < 0) {
+		pr_err("Unable to read trigger name, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,led-mask", &snode->led_mask);
+	if (rc < 0) {
+		pr_err("Unable to read led mask rc=%d\n", rc);
+		return rc;
+	}
+
+	if (snode->led_mask < 1 || snode->led_mask > 7) {
+		pr_err("Invalid value for led-mask\n");
+		return -EINVAL;
+	}
+
+	scnprintf(reg_name, sizeof(reg_name), "switch%d-supply", num);
+	if (of_find_property(led->pdev->dev.of_node, reg_name, NULL)) {
+		scnprintf(reg_sup_name, sizeof(reg_sup_name), "switch%d", num);
+		snode->vreg = devm_regulator_get(&led->pdev->dev, reg_sup_name);
+		if (IS_ERR_OR_NULL(snode->vreg)) {
+			rc = PTR_ERR(snode->vreg);
+			if (rc != -EPROBE_DEFER)
+				pr_err("Failed to get regulator, rc=%d\n", rc);
+			snode->vreg = NULL;
+			return rc;
+		}
+	}
+
+	snode->pdev = led->pdev;
+	snode->cdev.brightness_set = qpnp_flash_led_brightness_set;
+	snode->cdev.brightness_get = qpnp_flash_led_brightness_get;
+	snode->cdev.flags |= LED_KEEP_TRIGGER;
+	rc = led_classdev_register(&led->pdev->dev, &snode->cdev);
+	if (rc < 0) {
+		pr_err("Unable to register led switch node\n");
+		return rc;
+	}
+
+	snode->cdev.dev->of_node = node;
+	return 0;
+}
+
+static int get_code_from_table(int *table, int len, int value)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (value == table[i])
+			break;
+	}
+
+	if (i == len) {
+		pr_err("Couldn't find %d from table\n", value);
+		return -ENODATA;
+	}
+
+	return i;
+}
+
+static int qpnp_flash_led_parse_common_dt(struct qpnp_flash_led *led,
+						struct device_node *node)
+{
+	struct device_node *revid_node;
+	int rc;
+	u32 val;
+	bool short_circuit_det, open_circuit_det, vph_droop_det;
+
+	revid_node = of_parse_phandle(node, "qcom,pmic-revid", 0);
+	if (!revid_node) {
+		pr_err("Missing qcom,pmic-revid property - driver failed\n");
+		return -EINVAL;
+	}
+
+	led->pdata->pmic_rev_id = get_revid_data(revid_node);
+	if (IS_ERR_OR_NULL(led->pdata->pmic_rev_id)) {
+		pr_err("Unable to get pmic_revid rc=%ld\n",
+			PTR_ERR(led->pdata->pmic_rev_id));
+		/*
+		 * the revid peripheral must be registered, any failure
+		 * here only indicates that the rev-id module has not
+		 * probed yet.
+		 */
+		return -EPROBE_DEFER;
+	}
+
+	pr_debug("PMIC subtype %d Digital major %d\n",
+		led->pdata->pmic_rev_id->pmic_subtype,
+		led->pdata->pmic_rev_id->rev4);
+
+	led->pdata->hdrm_auto_mode_en = of_property_read_bool(node,
+							"qcom,hdrm-auto-mode");
+
+	led->pdata->isc_delay = FLASH_LED_ISC_DELAY_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,isc-delay-us", &val);
+	if (!rc) {
+		led->pdata->isc_delay =
+				val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read ISC delay, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->warmup_delay = FLASH_LED_WARMUP_DELAY_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,warmup-delay-us", &val);
+	if (!rc) {
+		led->pdata->warmup_delay =
+				val >> FLASH_LED_ISC_WARMUP_DELAY_SHIFT;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read WARMUP delay, rc=%d\n", rc);
+		return rc;
+	}
+
+	short_circuit_det =
+		of_property_read_bool(node, "qcom,short-circuit-det");
+	open_circuit_det = of_property_read_bool(node, "qcom,open-circuit-det");
+	vph_droop_det = of_property_read_bool(node, "qcom,vph-droop-det");
+	led->pdata->current_derate_en_cfg = (vph_droop_det << 2) |
+				(open_circuit_det << 1) | short_circuit_det;
+
+	led->pdata->thermal_derate_en =
+		of_property_read_bool(node, "qcom,thermal-derate-en");
+
+	if (led->pdata->thermal_derate_en) {
+		led->pdata->thermal_derate_current =
+			devm_kcalloc(&led->pdev->dev,
+					FLASH_LED_THERMAL_OTST_LEVELS,
+					sizeof(int), GFP_KERNEL);
+		if (!led->pdata->thermal_derate_current)
+			return -ENOMEM;
+
+		rc = of_property_read_u32_array(node,
+					"qcom,thermal-derate-current",
+					led->pdata->thermal_derate_current,
+					FLASH_LED_THERMAL_OTST_LEVELS);
+		if (rc < 0) {
+			pr_err("Unable to read thermal current limits, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	led->pdata->otst_ramp_bkup_en =
+		!of_property_read_bool(node, "qcom,otst-ramp-back-up-dis");
+
+	led->pdata->thermal_derate_slow = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-derate-slow", &val);
+	if (!rc) {
+		if (val < 0 || val > THERMAL_DERATE_SLOW_MAX) {
+			pr_err("Invalid thermal_derate_slow %d\n", val);
+			return -EINVAL;
+		}
+
+		led->pdata->thermal_derate_slow =
+			get_code_from_table(thermal_derate_slow_table,
+				ARRAY_SIZE(thermal_derate_slow_table), val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal derate slow, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->thermal_derate_fast = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-derate-fast", &val);
+	if (!rc) {
+		if (val < 0 || val > THERMAL_DERATE_FAST_MAX) {
+			pr_err("Invalid thermal_derate_fast %d\n", val);
+			return -EINVAL;
+		}
+
+		led->pdata->thermal_derate_fast =
+			get_code_from_table(thermal_derate_fast_table,
+				ARRAY_SIZE(thermal_derate_fast_table), val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal derate fast, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->thermal_debounce = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-debounce", &val);
+	if (!rc) {
+		if (val < 0 || val > THERMAL_DEBOUNCE_TIME_MAX) {
+			pr_err("Invalid thermal_debounce %d\n", val);
+			return -EINVAL;
+		}
+
+		if (val >= 0 && val < 16)
+			led->pdata->thermal_debounce = 0;
+		else
+			led->pdata->thermal_debounce = ilog2(val) - 3;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal debounce, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->thermal_hysteresis = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-hysteresis", &val);
+	if (!rc) {
+		if (led->pdata->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+			val = THERMAL_HYST_TEMP_TO_VAL(val, 20);
+		else
+			val = THERMAL_HYST_TEMP_TO_VAL(val, 15);
+
+		if (val < 0 || val > THERMAL_DERATE_HYSTERESIS_MAX) {
+			pr_err("Invalid thermal_derate_hysteresis %d\n", val);
+			return -EINVAL;
+		}
+
+		led->pdata->thermal_hysteresis = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal hysteresis, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->thermal_thrsh1 = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-thrsh1", &val);
+	if (!rc) {
+		led->pdata->thermal_thrsh1 =
+			get_code_from_table(otst1_threshold_table,
+				ARRAY_SIZE(otst1_threshold_table), val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal thrsh1, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->thermal_thrsh2 = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-thrsh2", &val);
+	if (!rc) {
+		led->pdata->thermal_thrsh2 =
+			get_code_from_table(otst2_threshold_table,
+				ARRAY_SIZE(otst2_threshold_table), val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal thrsh2, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->thermal_thrsh3 = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,thermal-thrsh3", &val);
+	if (!rc) {
+		led->pdata->thermal_thrsh3 =
+			get_code_from_table(otst3_threshold_table,
+				ARRAY_SIZE(otst3_threshold_table), val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read thermal thrsh3, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->vph_droop_debounce = FLASH_LED_VPH_DROOP_DEBOUNCE_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,vph-droop-debounce-us", &val);
+	if (!rc) {
+		led->pdata->vph_droop_debounce =
+			VPH_DROOP_DEBOUNCE_US_TO_VAL(val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read VPH droop debounce, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->pdata->vph_droop_debounce > FLASH_LED_DEBOUNCE_MAX) {
+		pr_err("Invalid VPH droop debounce specified\n");
+		return -EINVAL;
+	}
+
+	led->pdata->vph_droop_threshold = FLASH_LED_VPH_DROOP_THRESH_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,vph-droop-threshold-mv", &val);
+	if (!rc) {
+		led->pdata->vph_droop_threshold =
+			VPH_DROOP_THRESH_MV_TO_VAL(val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read VPH droop threshold, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->pdata->vph_droop_threshold > FLASH_LED_VPH_DROOP_THRESH_MAX) {
+		pr_err("Invalid VPH droop threshold specified\n");
+		return -EINVAL;
+	}
+
+	led->pdata->vph_droop_hysteresis =
+			FLASH_LED_VPH_DROOP_HYST_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,vph-droop-hysteresis-mv", &val);
+	if (!rc) {
+		led->pdata->vph_droop_hysteresis =
+			VPH_DROOP_HYST_MV_TO_VAL(val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read VPH droop hysteresis, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->pdata->vph_droop_hysteresis > FLASH_LED_HYSTERESIS_MAX) {
+		pr_err("Invalid VPH droop hysteresis specified\n");
+		return -EINVAL;
+	}
+
+	led->pdata->vph_droop_hysteresis <<= FLASH_LED_VPH_DROOP_HYST_SHIFT;
+
+	rc = of_property_read_u32(node, "qcom,hw-strobe-option", &val);
+	if (!rc) {
+		led->pdata->hw_strobe_option = (u8)val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse hw strobe option, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,led1n2-iclamp-low-ma", &val);
+	if (!rc) {
+		led->pdata->led1n2_iclamp_low_ma = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read led1n2_iclamp_low current, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,led1n2-iclamp-mid-ma", &val);
+	if (!rc) {
+		led->pdata->led1n2_iclamp_mid_ma = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read led1n2_iclamp_mid current, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,led3-iclamp-low-ma", &val);
+	if (!rc) {
+		led->pdata->led3_iclamp_low_ma = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read led3_iclamp_low current, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,led3-iclamp-mid-ma", &val);
+	if (!rc) {
+		led->pdata->led3_iclamp_mid_ma = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to read led3_iclamp_mid current, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->vled_max_uv = FLASH_LED_VLED_MAX_DEFAULT_UV;
+	rc = of_property_read_u32(node, "qcom,vled-max-uv", &val);
+	if (!rc) {
+		led->pdata->vled_max_uv = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse vled_max voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->ibatt_ocp_threshold_ua =
+		FLASH_LED_IBATT_OCP_THRESH_DEFAULT_UA;
+	rc = of_property_read_u32(node, "qcom,ibatt-ocp-threshold-ua", &val);
+	if (!rc) {
+		led->pdata->ibatt_ocp_threshold_ua = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse ibatt_ocp threshold, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->rpara_uohm = FLASH_LED_RPARA_DEFAULT_UOHM;
+	rc = of_property_read_u32(node, "qcom,rparasitic-uohm", &val);
+	if (!rc) {
+		led->pdata->rpara_uohm = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse rparasitic, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->lmh_ocv_threshold_uv =
+		FLASH_LED_LMH_OCV_THRESH_DEFAULT_UV;
+	rc = of_property_read_u32(node, "qcom,lmh-ocv-threshold-uv", &val);
+	if (!rc) {
+		led->pdata->lmh_ocv_threshold_uv = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse lmh ocv threshold, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->lmh_rbatt_threshold_uohm =
+		FLASH_LED_LMH_RBATT_THRESH_DEFAULT_UOHM;
+	rc = of_property_read_u32(node, "qcom,lmh-rbatt-threshold-uohm", &val);
+	if (!rc) {
+		led->pdata->lmh_rbatt_threshold_uohm = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse lmh rbatt threshold, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->lmh_level = FLASH_LED_LMH_LEVEL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,lmh-level", &val);
+	if (!rc) {
+		led->pdata->lmh_level = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse lmh_level, rc=%d\n", rc);
+		return rc;
+	}
+
+	led->pdata->lmh_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,lmh-mitigation-sel", &val);
+	if (!rc) {
+		led->pdata->lmh_mitigation_sel = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse lmh_mitigation_sel, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->pdata->lmh_mitigation_sel > FLASH_LED_MITIGATION_SEL_MAX) {
+		pr_err("Invalid lmh_mitigation_sel specified\n");
+		return -EINVAL;
+	}
+
+	led->pdata->chgr_mitigation_sel = FLASH_LED_MITIGATION_SEL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,chgr-mitigation-sel", &val);
+	if (!rc) {
+		led->pdata->chgr_mitigation_sel = val;
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse chgr_mitigation_sel, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->pdata->chgr_mitigation_sel > FLASH_LED_MITIGATION_SEL_MAX) {
+		pr_err("Invalid chgr_mitigation_sel specified\n");
+		return -EINVAL;
+	}
+
+	led->pdata->chgr_mitigation_sel <<= FLASH_LED_CHGR_MITIGATION_SEL_SHIFT;
+
+	led->pdata->iled_thrsh_val = FLASH_LED_MITIGATION_THRSH_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,iled-thrsh-ma", &val);
+	if (!rc) {
+		led->pdata->iled_thrsh_val = MITIGATION_THRSH_MA_TO_VAL(val);
+	} else if (rc != -EINVAL) {
+		pr_err("Unable to parse iled_thrsh_val, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (led->pdata->iled_thrsh_val > FLASH_LED_MITIGATION_THRSH_MAX) {
+		pr_err("Invalid iled_thrsh_val specified\n");
+		return -EINVAL;
+	}
+
+	led->pdata->all_ramp_up_done_irq =
+		of_irq_get_byname(node, "all-ramp-up-done-irq");
+	if (led->pdata->all_ramp_up_done_irq < 0)
+		pr_debug("all-ramp-up-done-irq not used\n");
+
+	led->pdata->all_ramp_down_done_irq =
+		of_irq_get_byname(node, "all-ramp-down-done-irq");
+	if (led->pdata->all_ramp_down_done_irq < 0)
+		pr_debug("all-ramp-down-done-irq not used\n");
+
+	led->pdata->led_fault_irq =
+		of_irq_get_byname(node, "led-fault-irq");
+	if (led->pdata->led_fault_irq < 0)
+		pr_debug("led-fault-irq not used\n");
+
+	return 0;
+}
+
+static int qpnp_flash_led_probe(struct platform_device *pdev)
+{
+	struct qpnp_flash_led *led;
+	struct device_node *node, *temp;
+	const char *temp_string;
+	unsigned int base;
+	int rc, i = 0, j = 0;
+
+	node = pdev->dev.of_node;
+	if (!node) {
+		pr_err("No flash LED nodes defined\n");
+		return -ENODEV;
+	}
+
+	rc = of_property_read_u32(node, "reg", &base);
+	if (rc < 0) {
+		pr_err("Couldn't find reg in node %s, rc = %d\n",
+			node->full_name, rc);
+		return rc;
+	}
+
+	led = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_flash_led),
+								GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!led->regmap) {
+		pr_err("Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	led->base = base;
+	led->pdev = pdev;
+	led->pdata = devm_kzalloc(&pdev->dev,
+			sizeof(struct flash_led_platform_data), GFP_KERNEL);
+	if (!led->pdata)
+		return -ENOMEM;
+
+	rc = qpnp_flash_led_parse_common_dt(led, node);
+	if (rc < 0) {
+		pr_err("Failed to parse common flash LED device tree\n");
+		return rc;
+	}
+
+	for_each_available_child_of_node(node, temp) {
+		rc = of_property_read_string(temp, "label", &temp_string);
+		if (rc < 0) {
+			pr_err("Failed to parse label, rc=%d\n", rc);
+			return rc;
+		}
+
+		if (!strcmp("switch", temp_string)) {
+			led->num_snodes++;
+		} else if (!strcmp("flash", temp_string) ||
+				!strcmp("torch", temp_string)) {
+			led->num_fnodes++;
+		} else {
+			pr_err("Invalid label for led node\n");
+			return -EINVAL;
+		}
+	}
+
+	if (!led->num_fnodes) {
+		pr_err("No LED nodes defined\n");
+		return -ECHILD;
+	}
+
+	led->fnode = devm_kcalloc(&pdev->dev, led->num_fnodes,
+				sizeof(*led->fnode),
+				GFP_KERNEL);
+	if (!led->fnode)
+		return -ENOMEM;
+
+	led->snode = devm_kcalloc(&pdev->dev, led->num_snodes,
+				sizeof(*led->snode),
+				GFP_KERNEL);
+	if (!led->snode)
+		return -ENOMEM;
+
+	temp = NULL;
+	i = 0;
+	j = 0;
+	for_each_available_child_of_node(node, temp) {
+		rc = of_property_read_string(temp, "label", &temp_string);
+		if (rc < 0) {
+			pr_err("Failed to parse label, rc=%d\n", rc);
+			return rc;
+		}
+
+		if (!strcmp("flash", temp_string) ||
+				!strcmp("torch", temp_string)) {
+			rc = qpnp_flash_led_parse_each_led_dt(led,
+					&led->fnode[i++], temp);
+			if (rc < 0) {
+				pr_err("Unable to parse flash node %d rc=%d\n",
+					i, rc);
+				goto error_led_register;
+			}
+		}
+
+		if (!strcmp("switch", temp_string)) {
+			rc = qpnp_flash_led_parse_and_register_switch(led,
+					&led->snode[j++], temp);
+			if (rc < 0) {
+				pr_err("Unable to parse and register switch node, rc=%d\n",
+					rc);
+				goto error_switch_register;
+			}
+		}
+	}
+
+	/* setup irqs */
+	if (led->pdata->all_ramp_up_done_irq >= 0) {
+		rc = devm_request_threaded_irq(&led->pdev->dev,
+			led->pdata->all_ramp_up_done_irq,
+			NULL, qpnp_flash_led_irq_handler,
+			IRQF_ONESHOT,
+			"qpnp_flash_led_all_ramp_up_done_irq", led);
+		if (rc < 0) {
+			pr_err("Unable to request all_ramp_up_done(%d) IRQ(err:%d)\n",
+				led->pdata->all_ramp_up_done_irq, rc);
+			goto error_switch_register;
+		}
+	}
+
+	if (led->pdata->all_ramp_down_done_irq >= 0) {
+		rc = devm_request_threaded_irq(&led->pdev->dev,
+			led->pdata->all_ramp_down_done_irq,
+			NULL, qpnp_flash_led_irq_handler,
+			IRQF_ONESHOT,
+			"qpnp_flash_led_all_ramp_down_done_irq", led);
+		if (rc < 0) {
+			pr_err("Unable to request all_ramp_down_done(%d) IRQ(err:%d)\n",
+				led->pdata->all_ramp_down_done_irq, rc);
+			goto error_switch_register;
+		}
+	}
+
+	if (led->pdata->led_fault_irq >= 0) {
+		rc = devm_request_threaded_irq(&led->pdev->dev,
+			led->pdata->led_fault_irq,
+			NULL, qpnp_flash_led_irq_handler,
+			IRQF_ONESHOT,
+			"qpnp_flash_led_fault_irq", led);
+		if (rc < 0) {
+			pr_err("Unable to request led_fault(%d) IRQ(err:%d)\n",
+				led->pdata->led_fault_irq, rc);
+			goto error_switch_register;
+		}
+	}
+
+	led->bms_psy = power_supply_get_by_name("bms");
+	if (!led->bms_psy) {
+		rc = flash_led_psy_register_notifier(led);
+		if (rc < 0) {
+			pr_err("Couldn't register psy notifier, rc = %d\n", rc);
+			goto error_switch_register;
+		}
+	}
+
+	rc = qpnp_flash_led_init_settings(led);
+	if (rc < 0) {
+		pr_err("Failed to initialize flash LED, rc=%d\n", rc);
+		goto unreg_notifier;
+	}
+
+	for (i = 0; i < led->num_snodes; i++) {
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
+			rc = sysfs_create_file(&led->snode[i].cdev.dev->kobj,
+					&qpnp_flash_led_attrs[j].attr);
+			if (rc < 0) {
+				pr_err("sysfs creation failed, rc=%d\n", rc);
+				goto sysfs_fail;
+			}
+		}
+	}
+
+	spin_lock_init(&led->lock);
+
+	dev_set_drvdata(&pdev->dev, led);
+
+	return 0;
+
+sysfs_fail:
+	for (--j; j >= 0; j--)
+		sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+				&qpnp_flash_led_attrs[j].attr);
+
+	for (--i; i >= 0; i--) {
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
+			sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+					&qpnp_flash_led_attrs[j].attr);
+	}
+
+	i = led->num_snodes;
+unreg_notifier:
+	power_supply_unreg_notifier(&led->nb);
+error_switch_register:
+	while (i > 0)
+		led_classdev_unregister(&led->snode[--i].cdev);
+	i = led->num_fnodes;
+error_led_register:
+	while (i > 0)
+		led_classdev_unregister(&led->fnode[--i].cdev);
+
+	return rc;
+}
+
+static int qpnp_flash_led_remove(struct platform_device *pdev)
+{
+	struct qpnp_flash_led *led = dev_get_drvdata(&pdev->dev);
+	int i, j;
+
+	for (i = 0; i < led->num_snodes; i++) {
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
+			sysfs_remove_file(&led->snode[i].cdev.dev->kobj,
+					&qpnp_flash_led_attrs[j].attr);
+
+		if (led->snode[i].regulator_on)
+			qpnp_flash_led_regulator_enable(led,
+					&led->snode[i], false);
+	}
+
+	while (i > 0)
+		led_classdev_unregister(&led->snode[--i].cdev);
+
+	i = led->num_fnodes;
+	while (i > 0)
+		led_classdev_unregister(&led->fnode[--i].cdev);
+
+	power_supply_unreg_notifier(&led->nb);
+	return 0;
+}
+
+const struct of_device_id qpnp_flash_led_match_table[] = {
+	{ .compatible = "qcom,qpnp-flash-led-v2",},
+	{ },
+};
+
+static struct platform_driver qpnp_flash_led_driver = {
+	.driver		= {
+		.name = "qcom,qpnp-flash-led-v2",
+		.of_match_table = qpnp_flash_led_match_table,
+	},
+	.probe		= qpnp_flash_led_probe,
+	.remove		= qpnp_flash_led_remove,
+};
+
+static int __init qpnp_flash_led_init(void)
+{
+	return platform_driver_register(&qpnp_flash_led_driver);
+}
+late_initcall(qpnp_flash_led_init);
+
+static void __exit qpnp_flash_led_exit(void)
+{
+	platform_driver_unregister(&qpnp_flash_led_driver);
+}
+module_exit(qpnp_flash_led_exit);
+
+MODULE_DESCRIPTION("QPNP Flash LED driver v2");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qpnp-flash-v2");
diff --git a/drivers/leds/leds-qpnp-flash.c b/drivers/leds/leds-qpnp-flash.c
new file mode 100644
index 0000000..c27c059
--- /dev/null
+++ b/drivers/leds/leds-qpnp-flash.c
@@ -0,0 +1,2649 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/errno.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/workqueue.h>
+#include <linux/power_supply.h>
+#include <linux/leds-qpnp-flash.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include "leds.h"
+
+#define FLASH_LED_PERIPHERAL_SUBTYPE(base)			(base + 0x05)
+#define FLASH_SAFETY_TIMER(base)				(base + 0x40)
+#define FLASH_MAX_CURRENT(base)					(base + 0x41)
+#define FLASH_LED0_CURRENT(base)				(base + 0x42)
+#define FLASH_LED1_CURRENT(base)				(base + 0x43)
+#define	FLASH_CLAMP_CURRENT(base)				(base + 0x44)
+#define FLASH_MODULE_ENABLE_CTRL(base)				(base + 0x46)
+#define	FLASH_LED_STROBE_CTRL(base)				(base + 0x47)
+#define FLASH_LED_TMR_CTRL(base)				(base + 0x48)
+#define FLASH_HEADROOM(base)					(base + 0x4A)
+#define	FLASH_STARTUP_DELAY(base)				(base + 0x4B)
+#define FLASH_MASK_ENABLE(base)					(base + 0x4C)
+#define FLASH_VREG_OK_FORCE(base)				(base + 0x4F)
+#define FLASH_FAULT_DETECT(base)				(base + 0x51)
+#define	FLASH_THERMAL_DRATE(base)				(base + 0x52)
+#define	FLASH_CURRENT_RAMP(base)				(base + 0x54)
+#define	FLASH_VPH_PWR_DROOP(base)				(base + 0x5A)
+#define	FLASH_HDRM_SNS_ENABLE_CTRL0(base)			(base + 0x5C)
+#define	FLASH_HDRM_SNS_ENABLE_CTRL1(base)			(base + 0x5D)
+#define	FLASH_LED_UNLOCK_SECURE(base)				(base + 0xD0)
+#define FLASH_PERPH_RESET_CTRL(base)				(base + 0xDA)
+#define	FLASH_TORCH(base)					(base + 0xE4)
+
+#define FLASH_STATUS_REG_MASK					0xFF
+#define FLASH_LED_FAULT_STATUS(base)				(base + 0x08)
+#define INT_LATCHED_STS(base)					(base + 0x18)
+#define IN_POLARITY_HIGH(base)					(base + 0x12)
+#define INT_SET_TYPE(base)					(base + 0x11)
+#define INT_EN_SET(base)					(base + 0x15)
+#define INT_LATCHED_CLR(base)					(base + 0x14)
+
+#define	FLASH_HEADROOM_MASK					0x03
+#define FLASH_STARTUP_DLY_MASK					0x03
+#define	FLASH_VREG_OK_FORCE_MASK				0xC0
+#define	FLASH_FAULT_DETECT_MASK					0x80
+#define	FLASH_THERMAL_DERATE_MASK				0xBF
+#define FLASH_SECURE_MASK					0xFF
+#define FLASH_TORCH_MASK					0x03
+#define FLASH_CURRENT_MASK					0x7F
+#define FLASH_TMR_MASK						0x03
+#define FLASH_TMR_SAFETY					0x00
+#define FLASH_SAFETY_TIMER_MASK					0x7F
+#define FLASH_MODULE_ENABLE_MASK				0xE0
+#define FLASH_STROBE_MASK					0xC0
+#define FLASH_CURRENT_RAMP_MASK					0xBF
+#define FLASH_VPH_PWR_DROOP_MASK				0xF3
+#define FLASH_LED_HDRM_SNS_ENABLE_MASK				0x81
+#define	FLASH_MASK_MODULE_CONTRL_MASK				0xE0
+#define FLASH_FOLLOW_OTST2_RB_MASK				0x08
+
+#define FLASH_LED_TRIGGER_DEFAULT				"none"
+#define FLASH_LED_HEADROOM_DEFAULT_MV				500
+#define FLASH_LED_STARTUP_DELAY_DEFAULT_US			128
+#define FLASH_LED_CLAMP_CURRENT_DEFAULT_MA			200
+#define	FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C		80
+#define	FLASH_LED_RAMP_UP_STEP_DEFAULT_US			3
+#define	FLASH_LED_RAMP_DN_STEP_DEFAULT_US			3
+#define	FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV		3200
+#define	FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US	10
+#define FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT		2
+#define FLASH_RAMP_UP_DELAY_US_MIN				1000
+#define	FLASH_RAMP_UP_DELAY_US_MAX				1001
+#define FLASH_RAMP_DN_DELAY_US_MIN				2160
+#define	FLASH_RAMP_DN_DELAY_US_MAX				2161
+#define FLASH_BOOST_REGULATOR_PROBE_DELAY_MS			2000
+#define	FLASH_TORCH_MAX_LEVEL					0x0F
+#define	FLASH_MAX_LEVEL						0x4F
+#define	FLASH_LED_FLASH_HW_VREG_OK				0x40
+#define	FLASH_LED_FLASH_SW_VREG_OK				0x80
+#define FLASH_LED_STROBE_TYPE_HW				0x04
+#define	FLASH_DURATION_DIVIDER					10
+#define	FLASH_LED_HEADROOM_DIVIDER				100
+#define	FLASH_LED_HEADROOM_OFFSET				2
+#define	FLASH_LED_MAX_CURRENT_MA				1000
+#define	FLASH_LED_THERMAL_THRESHOLD_MIN				95
+#define	FLASH_LED_THERMAL_DEVIDER				10
+#define	FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV			2500
+#define	FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER			100
+#define	FLASH_LED_HDRM_SNS_ENABLE				0x81
+#define	FLASH_LED_HDRM_SNS_DISABLE				0x01
+#define	FLASH_LED_UA_PER_MA					1000
+#define	FLASH_LED_MASK_MODULE_MASK2_ENABLE			0x20
+#define	FLASH_LED_MASK3_ENABLE_SHIFT				7
+#define	FLASH_LED_MODULE_CTRL_DEFAULT				0x60
+#define	FLASH_LED_CURRENT_READING_DELAY_MIN			5000
+#define	FLASH_LED_CURRENT_READING_DELAY_MAX			5001
+#define	FLASH_LED_OPEN_FAULT_DETECTED				0xC
+
+#define FLASH_UNLOCK_SECURE					0xA5
+#define FLASH_LED_TORCH_ENABLE					0x00
+#define FLASH_LED_TORCH_DISABLE					0x03
+#define FLASH_MODULE_ENABLE					0x80
+#define FLASH_LED0_TRIGGER					0x80
+#define FLASH_LED1_TRIGGER					0x40
+#define FLASH_LED0_ENABLEMENT					0x40
+#define FLASH_LED1_ENABLEMENT					0x20
+#define FLASH_LED_DISABLE					0x00
+#define	FLASH_LED_MIN_CURRENT_MA				13
+#define FLASH_SUBTYPE_DUAL					0x01
+#define FLASH_SUBTYPE_SINGLE					0x02
+
+/*
+ * ID represents physical LEDs for individual control purpose.
+ */
+enum flash_led_id {
+	FLASH_LED_0 = 0,
+	FLASH_LED_1,
+	FLASH_LED_SWITCH,
+};
+
+enum flash_led_type {
+	FLASH = 0,
+	TORCH,
+	SWITCH,
+};
+
+enum thermal_derate_rate {
+	RATE_1_PERCENT = 0,
+	RATE_1P25_PERCENT,
+	RATE_2_PERCENT,
+	RATE_2P5_PERCENT,
+	RATE_5_PERCENT,
+};
+
+enum current_ramp_steps {
+	RAMP_STEP_0P2_US = 0,
+	RAMP_STEP_0P4_US,
+	RAMP_STEP_0P8_US,
+	RAMP_STEP_1P6_US,
+	RAMP_STEP_3P3_US,
+	RAMP_STEP_6P7_US,
+	RAMP_STEP_13P5_US,
+	RAMP_STEP_27US,
+};
+
+struct flash_regulator_data {
+	struct regulator	*regs;
+	const char		*reg_name;
+	u32			max_volt_uv;
+};
+
+/*
+ * Configurations for each individual LED
+ */
+struct flash_node_data {
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct led_classdev		cdev;
+	struct work_struct		work;
+	struct flash_regulator_data	*reg_data;
+	u16				max_current;
+	u16				prgm_current;
+	u16				prgm_current2;
+	u16				duration;
+	u8				id;
+	u8				type;
+	u8				trigger;
+	u8				enable;
+	u8				num_regulators;
+	bool				flash_on;
+};
+
+/*
+ * Flash LED configuration read from device tree
+ */
+struct flash_led_platform_data {
+	unsigned int			temp_threshold_num;
+	unsigned int			temp_derate_curr_num;
+	unsigned int			*die_temp_derate_curr_ma;
+	unsigned int			*die_temp_threshold_degc;
+	u16				ramp_up_step;
+	u16				ramp_dn_step;
+	u16				vph_pwr_droop_threshold;
+	u16				headroom;
+	u16				clamp_current;
+	u8				thermal_derate_threshold;
+	u8				vph_pwr_droop_debounce_time;
+	u8				startup_dly;
+	u8				thermal_derate_rate;
+	bool				pmic_charger_support;
+	bool				self_check_en;
+	bool				thermal_derate_en;
+	bool				current_ramp_en;
+	bool				vph_pwr_droop_en;
+	bool				hdrm_sns_ch0_en;
+	bool				hdrm_sns_ch1_en;
+	bool				power_detect_en;
+	bool				mask3_en;
+	bool				follow_rb_disable;
+	bool				die_current_derate_en;
+};
+
+struct qpnp_flash_led_buffer {
+	size_t rpos;
+	size_t wpos;
+	size_t len;
+	char data[0];
+};
+
+/*
+ * Flash LED data structure containing flash LED attributes
+ */
+struct qpnp_flash_led {
+	struct pmic_revid_data		*revid_data;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct flash_led_platform_data	*pdata;
+	struct pinctrl			*pinctrl;
+	struct pinctrl_state		*gpio_state_active;
+	struct pinctrl_state		*gpio_state_suspend;
+	struct flash_node_data		*flash_node;
+	struct power_supply		*battery_psy;
+	struct workqueue_struct		*ordered_workq;
+	struct qpnp_vadc_chip		*vadc_dev;
+	struct mutex			flash_led_lock;
+	struct qpnp_flash_led_buffer	*log;
+	struct dentry			*dbgfs_root;
+	int				num_leds;
+	u32				buffer_cnt;
+	u16				base;
+	u16				current_addr;
+	u16				current2_addr;
+	u8				peripheral_type;
+	u8				fault_reg;
+	bool				gpio_enabled;
+	bool				charging_enabled;
+	bool				strobe_debug;
+	bool				dbg_feature_en;
+	bool				open_fault;
+};
+
+static u8 qpnp_flash_led_ctrl_dbg_regs[] = {
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+	0x4A, 0x4B, 0x4C, 0x4F, 0x51, 0x52, 0x54, 0x55, 0x5A, 0x5C, 0x5D,
+};
+
+static int flash_led_dbgfs_file_open(struct qpnp_flash_led *led,
+					struct file *file)
+{
+	struct qpnp_flash_led_buffer *log;
+	size_t logbufsize = SZ_4K;
+
+	log = kzalloc(logbufsize, GFP_KERNEL);
+	if (!log)
+		return -ENOMEM;
+
+	log->rpos = 0;
+	log->wpos = 0;
+	log->len = logbufsize - sizeof(*log);
+	led->log = log;
+
+	led->buffer_cnt = 1;
+	file->private_data = led;
+
+	return 0;
+}
+
+static int flash_led_dfs_open(struct inode *inode, struct file *file)
+{
+	struct qpnp_flash_led *led = inode->i_private;
+
+	return flash_led_dbgfs_file_open(led, file);
+}
+
+static int flash_led_dfs_close(struct inode *inode, struct file *file)
+{
+	struct qpnp_flash_led *led = file->private_data;
+
+	if (led && led->log) {
+		file->private_data = NULL;
+		kfree(led->log);
+	}
+
+	return 0;
+}
+
+static int print_to_log(struct qpnp_flash_led_buffer *log,
+					const char *fmt, ...)
+{
+	va_list args;
+	int cnt;
+	char *log_buf = &log->data[log->wpos];
+	size_t size = log->len - log->wpos;
+
+	va_start(args, fmt);
+	cnt = vscnprintf(log_buf, size, fmt, args);
+	va_end(args);
+
+	log->wpos += cnt;
+	return cnt;
+}
+
+static ssize_t flash_led_dfs_latched_reg_read(struct file *fp, char __user *buf,
+					size_t count, loff_t *ppos) {
+	struct qpnp_flash_led *led = fp->private_data;
+	struct qpnp_flash_led_buffer *log = led->log;
+	uint val;
+	int rc;
+	size_t len;
+	size_t ret;
+
+	if (log->rpos >= log->wpos && led->buffer_cnt == 0)
+		return 0;
+
+	rc = regmap_read(led->regmap, INT_LATCHED_STS(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"Unable to read from address %x, rc(%d)\n",
+				INT_LATCHED_STS(led->base), rc);
+		return -EINVAL;
+	}
+	led->buffer_cnt--;
+
+	rc = print_to_log(log, "0x%05X ", INT_LATCHED_STS(led->base));
+	if (rc == 0)
+		return rc;
+
+	rc = print_to_log(log, "0x%02X ", val);
+	if (rc == 0)
+		return rc;
+
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+	len = min(count, log->wpos - log->rpos);
+
+	ret = copy_to_user(buf, &log->data[log->rpos], len);
+	if (ret) {
+		pr_err("error copy register value to user\n");
+		return -EFAULT;
+	}
+
+	len -= ret;
+	*ppos += len;
+	log->rpos += len;
+
+	return len;
+}
+
+static ssize_t flash_led_dfs_fault_reg_read(struct file *fp, char __user *buf,
+					size_t count, loff_t *ppos) {
+	struct qpnp_flash_led *led = fp->private_data;
+	struct qpnp_flash_led_buffer *log = led->log;
+	int rc;
+	size_t len;
+	size_t ret;
+
+	if (log->rpos >= log->wpos && led->buffer_cnt == 0)
+		return 0;
+
+	led->buffer_cnt--;
+
+	rc = print_to_log(log, "0x%05X ", FLASH_LED_FAULT_STATUS(led->base));
+	if (rc == 0)
+		return rc;
+
+	rc = print_to_log(log, "0x%02X ", led->fault_reg);
+	if (rc == 0)
+		return rc;
+
+	if (log->wpos > 0 && log->data[log->wpos - 1] == ' ')
+		log->data[log->wpos - 1] = '\n';
+
+	len = min(count, log->wpos - log->rpos);
+
+	ret = copy_to_user(buf, &log->data[log->rpos], len);
+	if (ret) {
+		pr_err("error copy register value to user\n");
+		return -EFAULT;
+	}
+
+	len -= ret;
+	*ppos += len;
+	log->rpos += len;
+
+	return len;
+}
+
+static ssize_t flash_led_dfs_fault_reg_enable(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos) {
+
+	u8 *val;
+	int pos = 0;
+	int cnt = 0;
+	int data;
+	size_t ret = 0;
+
+	struct qpnp_flash_led *led = file->private_data;
+	char *kbuf = kmalloc(count + 1, GFP_KERNEL);
+
+	if (!kbuf)
+		return -ENOMEM;
+
+	ret = copy_from_user(kbuf, buf, count);
+	if (!ret) {
+		pr_err("failed to copy data from user\n");
+		ret = -EFAULT;
+		goto free_buf;
+	}
+
+	count -= ret;
+	*ppos += count;
+	kbuf[count] = '\0';
+	val = kbuf;
+	while (sscanf(kbuf + pos, "%i", &data) == 1) {
+		pos++;
+		val[cnt++] = data & 0xff;
+	}
+
+	if (!cnt)
+		goto free_buf;
+
+	ret = count;
+	if (*val == 1)
+		led->strobe_debug = true;
+	else
+		led->strobe_debug = false;
+
+free_buf:
+	kfree(kbuf);
+	return ret;
+}
+
+static ssize_t flash_led_dfs_dbg_enable(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos) {
+
+	u8 *val;
+	int pos = 0;
+	int cnt = 0;
+	int data;
+	size_t ret = 0;
+	struct qpnp_flash_led *led = file->private_data;
+	char *kbuf = kmalloc(count + 1, GFP_KERNEL);
+
+	if (!kbuf)
+		return -ENOMEM;
+
+	ret = copy_from_user(kbuf, buf, count);
+	if (ret == count) {
+		pr_err("failed to copy data from user\n");
+		ret = -EFAULT;
+		goto free_buf;
+	}
+	count -= ret;
+	*ppos += count;
+	kbuf[count] = '\0';
+	val = kbuf;
+	while (sscanf(kbuf + pos, "%i", &data) == 1) {
+		pos++;
+		val[cnt++] = data & 0xff;
+	}
+
+	if (!cnt)
+		goto free_buf;
+
+	ret = count;
+	if (*val == 1)
+		led->dbg_feature_en = true;
+	else
+		led->dbg_feature_en = false;
+
+free_buf:
+	kfree(kbuf);
+	return ret;
+}
+
+static const struct file_operations flash_led_dfs_latched_reg_fops = {
+	.open		= flash_led_dfs_open,
+	.release	= flash_led_dfs_close,
+	.read		= flash_led_dfs_latched_reg_read,
+};
+
+static const struct file_operations flash_led_dfs_strobe_reg_fops = {
+	.open		= flash_led_dfs_open,
+	.release	= flash_led_dfs_close,
+	.read		= flash_led_dfs_fault_reg_read,
+	.write		= flash_led_dfs_fault_reg_enable,
+};
+
+static const struct file_operations flash_led_dfs_dbg_feature_fops = {
+	.open		= flash_led_dfs_open,
+	.release	= flash_led_dfs_close,
+	.write		= flash_led_dfs_dbg_enable,
+};
+
+static int
+qpnp_led_masked_write(struct qpnp_flash_led *led, u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(led->regmap, addr, mask, val);
+	if (rc)
+		dev_err(&led->pdev->dev,
+			"Unable to update_bits to addr=%x, rc(%d)\n", addr, rc);
+
+	dev_dbg(&led->pdev->dev, "Write 0x%02X to addr 0x%02X\n", val, addr);
+
+	return rc;
+}
+
+static int qpnp_flash_led_get_allowed_die_temp_curr(struct qpnp_flash_led *led,
+							int64_t die_temp_degc)
+{
+	int die_temp_curr_ma;
+
+	if (die_temp_degc >= led->pdata->die_temp_threshold_degc[0])
+		die_temp_curr_ma =  0;
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[1])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[0];
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[2])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[1];
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[3])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[2];
+	else if (die_temp_degc >= led->pdata->die_temp_threshold_degc[4])
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[3];
+	else
+		die_temp_curr_ma = led->pdata->die_temp_derate_curr_ma[4];
+
+	return die_temp_curr_ma;
+}
+
+static int64_t qpnp_flash_led_get_die_temp(struct qpnp_flash_led *led)
+{
+	struct qpnp_vadc_result die_temp_result;
+	int rc;
+
+	rc = qpnp_vadc_read(led->vadc_dev, SPARE2, &die_temp_result);
+	if (rc) {
+		pr_err("failed to read the die temp\n");
+		return -EINVAL;
+	}
+
+	return die_temp_result.physical;
+}
+
+static int qpnp_get_pmic_revid(struct qpnp_flash_led *led)
+{
+	struct device_node *revid_dev_node;
+
+	revid_dev_node = of_parse_phandle(led->pdev->dev.of_node,
+				"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		dev_err(&led->pdev->dev,
+			"qcom,pmic-revid property missing\n");
+		return -EINVAL;
+	}
+
+	led->revid_data = get_revid_data(revid_dev_node);
+	if (IS_ERR(led->revid_data)) {
+		pr_err("Couldn't get revid data rc = %ld\n",
+				PTR_ERR(led->revid_data));
+		return PTR_ERR(led->revid_data);
+	}
+
+	return 0;
+}
+
+static int
+qpnp_flash_led_get_max_avail_current(struct flash_node_data *flash_node,
+					struct qpnp_flash_led *led)
+{
+	union power_supply_propval prop;
+	int64_t chg_temp_milidegc, die_temp_degc;
+	int max_curr_avail_ma = 2000;
+	int allowed_die_temp_curr_ma = 2000;
+	int rc;
+
+	if (led->pdata->power_detect_en) {
+		if (!led->battery_psy) {
+			dev_err(&led->pdev->dev,
+				"Failed to query power supply\n");
+			return -EINVAL;
+		}
+
+		/*
+		 * When charging is enabled, enforce this new enablement
+		 * sequence to reduce fuel gauge reading resolution.
+		 */
+		if (led->charging_enabled) {
+			rc = qpnp_led_masked_write(led,
+				FLASH_MODULE_ENABLE_CTRL(led->base),
+				FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Module enable reg write failed\n");
+				return -EINVAL;
+			}
+
+			usleep_range(FLASH_LED_CURRENT_READING_DELAY_MIN,
+				FLASH_LED_CURRENT_READING_DELAY_MAX);
+		}
+
+		power_supply_get_property(led->battery_psy,
+				POWER_SUPPLY_PROP_FLASH_CURRENT_MAX, &prop);
+		if (!prop.intval) {
+			dev_err(&led->pdev->dev,
+				"battery too low for flash\n");
+			return -EINVAL;
+		}
+
+		max_curr_avail_ma = (prop.intval / FLASH_LED_UA_PER_MA);
+	}
+
+	/*
+	 * When thermal mitigation is available, this logic will execute to
+	 * derate current based upon the PMIC die temperature.
+	 */
+	if (led->pdata->die_current_derate_en) {
+		chg_temp_milidegc = qpnp_flash_led_get_die_temp(led);
+		if (chg_temp_milidegc < 0)
+			return -EINVAL;
+
+		die_temp_degc = div_s64(chg_temp_milidegc, 1000);
+		allowed_die_temp_curr_ma =
+			qpnp_flash_led_get_allowed_die_temp_curr(led,
+								die_temp_degc);
+		if (allowed_die_temp_curr_ma < 0)
+			return -EINVAL;
+	}
+
+	max_curr_avail_ma = (max_curr_avail_ma >= allowed_die_temp_curr_ma)
+				? allowed_die_temp_curr_ma : max_curr_avail_ma;
+
+	return max_curr_avail_ma;
+}
+
+static ssize_t qpnp_flash_led_die_temp_store(struct device *dev,
+					struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	unsigned long val;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	/*'0' for disable die_temp feature; non-zero to enable feature*/
+	if (val == 0)
+		led->pdata->die_current_derate_en = false;
+	else
+		led->pdata->die_current_derate_en = true;
+
+	return count;
+}
+
+static ssize_t qpnp_led_strobe_type_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct flash_node_data *flash_node;
+	unsigned long state;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = -EINVAL;
+
+	ret = kstrtoul(buf, 10, &state);
+	if (ret)
+		return ret;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+
+	/* '0' for sw strobe; '1' for hw strobe */
+	if (state == 1)
+		flash_node->trigger |= FLASH_LED_STROBE_TYPE_HW;
+	else
+		flash_node->trigger &= ~FLASH_LED_STROBE_TYPE_HW;
+
+	return count;
+}
+
+static ssize_t qpnp_flash_led_dump_regs_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	int rc, i, count = 0;
+	u16 addr;
+	uint val;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+	for (i = 0; i < ARRAY_SIZE(qpnp_flash_led_ctrl_dbg_regs); i++) {
+		addr = led->base + qpnp_flash_led_ctrl_dbg_regs[i];
+		rc = regmap_read(led->regmap, addr, &val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Unable to read from addr=%x, rc(%d)\n",
+				addr, rc);
+			return -EINVAL;
+		}
+
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				"REG_0x%x = 0x%02x\n", addr, val);
+
+		if (count >= PAGE_SIZE)
+			return PAGE_SIZE - 1;
+	}
+
+	return count;
+}
+
+static ssize_t qpnp_flash_led_current_derate_store(struct device *dev,
+				struct device_attribute *attr,
+				const char *buf, size_t count)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	unsigned long val;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+
+	ret = kstrtoul(buf, 10, &val);
+	if (ret)
+		return ret;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	/*'0' for disable derate feature; non-zero to enable derate feature */
+	if (val == 0)
+		led->pdata->power_detect_en = false;
+	else
+		led->pdata->power_detect_en = true;
+
+	return count;
+}
+
+static ssize_t qpnp_flash_led_max_current_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct qpnp_flash_led *led;
+	struct flash_node_data *flash_node;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	int max_curr_avail_ma = 0;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	if (led->flash_node[0].flash_on)
+		max_curr_avail_ma += led->flash_node[0].max_current;
+	if (led->flash_node[1].flash_on)
+		max_curr_avail_ma += led->flash_node[1].max_current;
+
+	if (led->pdata->power_detect_en ||
+			led->pdata->die_current_derate_en) {
+		max_curr_avail_ma =
+			qpnp_flash_led_get_max_avail_current(flash_node, led);
+
+		if (max_curr_avail_ma < 0)
+			return -EINVAL;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", max_curr_avail_ma);
+}
+
+static struct device_attribute qpnp_flash_led_attrs[] = {
+	__ATTR(strobe, 0664, NULL, qpnp_led_strobe_type_store),
+	__ATTR(reg_dump, 0664, qpnp_flash_led_dump_regs_show, NULL),
+	__ATTR(enable_current_derate, 0664, NULL,
+		qpnp_flash_led_current_derate_store),
+	__ATTR(max_allowed_current, 0664, qpnp_flash_led_max_current_show,
+		NULL),
+	__ATTR(enable_die_temp_current_derate, 0664, NULL,
+		qpnp_flash_led_die_temp_store),
+};
+
+static int qpnp_flash_led_get_thermal_derate_rate(const char *rate)
+{
+	/*
+	 * return 5% derate as default value if user specifies
+	 * a value un-supported
+	 */
+	if (strcmp(rate, "1_PERCENT") == 0)
+		return RATE_1_PERCENT;
+	else if (strcmp(rate, "1P25_PERCENT") == 0)
+		return RATE_1P25_PERCENT;
+	else if (strcmp(rate, "2_PERCENT") == 0)
+		return RATE_2_PERCENT;
+	else if (strcmp(rate, "2P5_PERCENT") == 0)
+		return RATE_2P5_PERCENT;
+	else if (strcmp(rate, "5_PERCENT") == 0)
+		return RATE_5_PERCENT;
+	else
+		return RATE_5_PERCENT;
+}
+
+static int qpnp_flash_led_get_ramp_step(const char *step)
+{
+	/*
+	 * return 27 us as default value if user specifies
+	 * a value un-supported
+	 */
+	if (strcmp(step, "0P2_US") == 0)
+		return RAMP_STEP_0P2_US;
+	else if (strcmp(step, "0P4_US") == 0)
+		return RAMP_STEP_0P4_US;
+	else if (strcmp(step, "0P8_US") == 0)
+		return RAMP_STEP_0P8_US;
+	else if (strcmp(step, "1P6_US") == 0)
+		return RAMP_STEP_1P6_US;
+	else if (strcmp(step, "3P3_US") == 0)
+		return RAMP_STEP_3P3_US;
+	else if (strcmp(step, "6P7_US") == 0)
+		return RAMP_STEP_6P7_US;
+	else if (strcmp(step, "13P5_US") == 0)
+		return RAMP_STEP_13P5_US;
+	else
+		return RAMP_STEP_27US;
+}
+
+static u8 qpnp_flash_led_get_droop_debounce_time(u8 val)
+{
+	/*
+	 * return 10 us as default value if user specifies
+	 * a value un-supported
+	 */
+	switch (val) {
+	case 0:
+		return 0;
+	case 10:
+		return 1;
+	case 32:
+		return 2;
+	case 64:
+		return 3;
+	default:
+		return 1;
+	}
+}
+
+static u8 qpnp_flash_led_get_startup_dly(u8 val)
+{
+	/*
+	 * return 128 us as default value if user specifies
+	 * a value un-supported
+	 */
+	switch (val) {
+	case 10:
+		return 0;
+	case 32:
+		return 1;
+	case 64:
+		return 2;
+	case 128:
+		return 3;
+	default:
+		return 3;
+	}
+}
+
+static int
+qpnp_flash_led_get_peripheral_type(struct qpnp_flash_led *led)
+{
+	int rc;
+	uint val;
+
+	rc = regmap_read(led->regmap,
+			 FLASH_LED_PERIPHERAL_SUBTYPE(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"Unable to read peripheral subtype\n");
+		return -EINVAL;
+	}
+
+	return val;
+}
+
+static int qpnp_flash_led_module_disable(struct qpnp_flash_led *led,
+				struct flash_node_data *flash_node)
+{
+	union power_supply_propval psy_prop;
+	int rc;
+	uint val, tmp;
+
+	rc = regmap_read(led->regmap, FLASH_LED_STROBE_CTRL(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Unable to read strobe reg\n");
+		return -EINVAL;
+	}
+
+	tmp = (~flash_node->trigger) & val;
+	if (!tmp) {
+		if (flash_node->type == TORCH) {
+			rc = qpnp_led_masked_write(led,
+				FLASH_LED_UNLOCK_SECURE(led->base),
+				FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Secure reg write failed\n");
+				return -EINVAL;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_TORCH(led->base),
+				FLASH_TORCH_MASK, FLASH_LED_TORCH_DISABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Torch reg write failed\n");
+				return -EINVAL;
+			}
+		}
+
+		if (led->battery_psy &&
+			led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
+						!led->revid_data->rev3) {
+			psy_prop.intval = false;
+			rc = power_supply_set_property(led->battery_psy,
+					POWER_SUPPLY_PROP_FLASH_TRIGGER,
+							&psy_prop);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Failed to enble charger i/p current limit\n");
+				return -EINVAL;
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+				FLASH_MODULE_ENABLE_CTRL(led->base),
+				FLASH_MODULE_ENABLE_MASK,
+				FLASH_LED_MODULE_CTRL_DEFAULT);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Module disable failed\n");
+			return -EINVAL;
+		}
+
+		if (led->pinctrl) {
+			rc = pinctrl_select_state(led->pinctrl,
+					led->gpio_state_suspend);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"failed to disable GPIO\n");
+				return -EINVAL;
+			}
+			led->gpio_enabled = false;
+		}
+
+		if (led->battery_psy) {
+			psy_prop.intval = false;
+			rc = power_supply_set_property(led->battery_psy,
+						POWER_SUPPLY_PROP_FLASH_ACTIVE,
+							&psy_prop);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Failed to setup OTG pulse skip enable\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (flash_node->trigger & FLASH_LED0_TRIGGER) {
+		rc = qpnp_led_masked_write(led,
+				led->current_addr,
+				FLASH_CURRENT_MASK, 0x00);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"current register write failed\n");
+			return -EINVAL;
+		}
+	}
+
+	if (flash_node->trigger & FLASH_LED1_TRIGGER) {
+		rc = qpnp_led_masked_write(led,
+				led->current2_addr,
+				FLASH_CURRENT_MASK, 0x00);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"current register write failed\n");
+			return -EINVAL;
+		}
+	}
+
+	if (flash_node->id == FLASH_LED_SWITCH)
+		flash_node->trigger &= FLASH_LED_STROBE_TYPE_HW;
+
+	return 0;
+}
+
+static enum
+led_brightness qpnp_flash_led_brightness_get(struct led_classdev *led_cdev)
+{
+	return led_cdev->brightness;
+}
+
+static int flash_regulator_parse_dt(struct qpnp_flash_led *led,
+					struct flash_node_data *flash_node) {
+
+	int i = 0, rc;
+	struct device_node *node = flash_node->cdev.dev->of_node;
+	struct device_node *temp = NULL;
+	const char *temp_string;
+	u32 val;
+
+	flash_node->reg_data = devm_kzalloc(&led->pdev->dev,
+					sizeof(struct flash_regulator_data *) *
+						flash_node->num_regulators,
+						GFP_KERNEL);
+	if (!flash_node->reg_data) {
+		dev_err(&led->pdev->dev,
+				"Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	for_each_child_of_node(node, temp) {
+		rc = of_property_read_string(temp, "regulator-name",
+							&temp_string);
+		if (!rc)
+			flash_node->reg_data[i].reg_name = temp_string;
+		else {
+			dev_err(&led->pdev->dev,
+					"Unable to read regulator name\n");
+			return rc;
+		}
+
+		rc = of_property_read_u32(temp, "max-voltage", &val);
+		if (!rc) {
+			flash_node->reg_data[i].max_volt_uv = val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+					"Unable to read max voltage\n");
+			return rc;
+		}
+
+		i++;
+	}
+
+	return 0;
+}
+
+static int flash_regulator_setup(struct qpnp_flash_led *led,
+				struct flash_node_data *flash_node, bool on)
+{
+	int i, rc = 0;
+
+	if (on == false) {
+		i = flash_node->num_regulators;
+		goto error_regulator_setup;
+	}
+
+	for (i = 0; i < flash_node->num_regulators; i++) {
+		flash_node->reg_data[i].regs =
+			regulator_get(flash_node->cdev.dev,
+					flash_node->reg_data[i].reg_name);
+		if (IS_ERR(flash_node->reg_data[i].regs)) {
+			rc = PTR_ERR(flash_node->reg_data[i].regs);
+			dev_err(&led->pdev->dev,
+					"Failed to get regulator\n");
+			goto error_regulator_setup;
+		}
+
+		if (regulator_count_voltages(flash_node->reg_data[i].regs)
+									> 0) {
+			rc = regulator_set_voltage(flash_node->reg_data[i].regs,
+					flash_node->reg_data[i].max_volt_uv,
+					flash_node->reg_data[i].max_volt_uv);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"regulator set voltage failed\n");
+				regulator_put(flash_node->reg_data[i].regs);
+				goto error_regulator_setup;
+			}
+		}
+	}
+
+	return rc;
+
+error_regulator_setup:
+	while (i--) {
+		if (regulator_count_voltages(flash_node->reg_data[i].regs)
+									> 0) {
+			regulator_set_voltage(flash_node->reg_data[i].regs,
+				0, flash_node->reg_data[i].max_volt_uv);
+		}
+
+		regulator_put(flash_node->reg_data[i].regs);
+	}
+
+	return rc;
+}
+
+static int flash_regulator_enable(struct qpnp_flash_led *led,
+				struct flash_node_data *flash_node, bool on)
+{
+	int i, rc = 0;
+
+	if (on == false) {
+		i = flash_node->num_regulators;
+		goto error_regulator_enable;
+	}
+
+	for (i = 0; i < flash_node->num_regulators; i++) {
+		rc = regulator_enable(flash_node->reg_data[i].regs);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"regulator enable failed\n");
+			goto error_regulator_enable;
+		}
+	}
+
+	return rc;
+
+error_regulator_enable:
+	while (i--)
+		regulator_disable(flash_node->reg_data[i].regs);
+
+	return rc;
+}
+
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+					int *max_current)
+{
+	struct led_classdev *led_cdev = trigger_to_lcdev(trig);
+	struct flash_node_data *flash_node;
+	struct qpnp_flash_led *led;
+	int rc;
+
+	if (!led_cdev) {
+		pr_err("Invalid led_trigger provided\n");
+		return -EINVAL;
+	}
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	if (!(options & FLASH_LED_PREPARE_OPTIONS_MASK)) {
+		dev_err(&led->pdev->dev, "Invalid options %d\n", options);
+		return -EINVAL;
+	}
+
+	if (options & ENABLE_REGULATOR) {
+		rc = flash_regulator_enable(led, flash_node, true);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"enable regulator failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (options & DISABLE_REGULATOR) {
+		rc = flash_regulator_enable(led, flash_node, false);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"disable regulator failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (options & QUERY_MAX_CURRENT) {
+		rc = qpnp_flash_led_get_max_avail_current(flash_node, led);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"query max current failed, rc=%d\n", rc);
+			return rc;
+		}
+		*max_current = rc;
+	}
+
+	return 0;
+}
+
+static void qpnp_flash_led_work(struct work_struct *work)
+{
+	struct flash_node_data *flash_node = container_of(work,
+				struct flash_node_data, work);
+	struct qpnp_flash_led *led = dev_get_drvdata(&flash_node->pdev->dev);
+	union power_supply_propval psy_prop;
+	int rc, brightness = flash_node->cdev.brightness;
+	int max_curr_avail_ma = 0;
+	int total_curr_ma = 0;
+	int i;
+	u8 val;
+	uint temp;
+
+	mutex_lock(&led->flash_led_lock);
+
+	if (!brightness)
+		goto turn_off;
+
+	if (led->open_fault) {
+		dev_err(&led->pdev->dev, "Open fault detected\n");
+		mutex_unlock(&led->flash_led_lock);
+		return;
+	}
+
+	if (!flash_node->flash_on && flash_node->num_regulators > 0) {
+		rc = flash_regulator_enable(led, flash_node, true);
+		if (rc) {
+			mutex_unlock(&led->flash_led_lock);
+			return;
+		}
+	}
+
+	if (!led->gpio_enabled && led->pinctrl) {
+		rc = pinctrl_select_state(led->pinctrl,
+						led->gpio_state_active);
+		if (rc) {
+			dev_err(&led->pdev->dev, "failed to enable GPIO\n");
+			goto error_enable_gpio;
+		}
+		led->gpio_enabled = true;
+	}
+
+	if (led->dbg_feature_en) {
+		rc = qpnp_led_masked_write(led,
+						INT_SET_TYPE(led->base),
+						FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"INT_SET_TYPE write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					IN_POLARITY_HIGH(led->base),
+					FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"IN_POLARITY_HIGH write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					INT_EN_SET(led->base),
+					FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev, "INT_EN_SET write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					INT_LATCHED_CLR(led->base),
+					FLASH_STATUS_REG_MASK, 0x1F);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"INT_LATCHED_CLR write failed\n");
+			goto exit_flash_led_work;
+		}
+	}
+
+	if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
+					flash_node->id != FLASH_LED_SWITCH) {
+		led->flash_node[led->num_leds - 1].trigger |=
+						(0x80 >> flash_node->id);
+		if (flash_node->id == FLASH_LED_0)
+			led->flash_node[led->num_leds - 1].prgm_current =
+						flash_node->prgm_current;
+		else if (flash_node->id == FLASH_LED_1)
+			led->flash_node[led->num_leds - 1].prgm_current2 =
+						flash_node->prgm_current;
+	}
+
+	if (flash_node->type == TORCH) {
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_UNLOCK_SECURE(led->base),
+			FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Secure reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_TORCH(led->base),
+			FLASH_TORCH_MASK, FLASH_LED_TORCH_ENABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Torch reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (flash_node->id == FLASH_LED_SWITCH) {
+			val = (u8)(flash_node->prgm_current *
+						FLASH_TORCH_MAX_LEVEL
+						/ flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+						led->current_addr,
+						FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Torch reg write failed\n");
+				goto exit_flash_led_work;
+			}
+
+			val = (u8)(flash_node->prgm_current2 *
+						FLASH_TORCH_MAX_LEVEL
+						/ flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+					led->current2_addr,
+					FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Torch reg write failed\n");
+				goto exit_flash_led_work;
+			}
+		} else {
+			val = (u8)(flash_node->prgm_current *
+						FLASH_TORCH_MAX_LEVEL /
+						flash_node->max_current);
+			if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(led,
+						led->current_addr,
+						FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			} else {
+				rc = qpnp_led_masked_write(led,
+						led->current2_addr,
+						FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_MAX_CURRENT(led->base),
+			FLASH_CURRENT_MASK, FLASH_TORCH_MAX_LEVEL);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Max current reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_MODULE_ENABLE_CTRL(led->base),
+			FLASH_MODULE_ENABLE_MASK, FLASH_MODULE_ENABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Module enable reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (led->pdata->hdrm_sns_ch0_en ||
+						led->pdata->hdrm_sns_ch1_en) {
+			if (flash_node->id == FLASH_LED_SWITCH) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					flash_node->trigger &
+					FLASH_LED0_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					flash_node->trigger &
+					FLASH_LED1_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_1) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			(flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
+						| FLASH_LED_STROBE_TYPE_HW
+							: flash_node->trigger |
+						FLASH_LED_STROBE_TYPE_HW),
+							flash_node->trigger);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Strobe reg write failed\n");
+			goto exit_flash_led_work;
+		}
+	} else if (flash_node->type == FLASH) {
+		if (flash_node->trigger & FLASH_LED0_TRIGGER)
+			max_curr_avail_ma += flash_node->max_current;
+		if (flash_node->trigger & FLASH_LED1_TRIGGER)
+			max_curr_avail_ma += flash_node->max_current;
+
+		psy_prop.intval = true;
+		rc = power_supply_set_property(led->battery_psy,
+						POWER_SUPPLY_PROP_FLASH_ACTIVE,
+								&psy_prop);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to setup OTG pulse skip enable\n");
+			goto exit_flash_led_work;
+		}
+
+		if (led->pdata->power_detect_en ||
+					led->pdata->die_current_derate_en) {
+			if (led->battery_psy) {
+				power_supply_get_property(led->battery_psy,
+					POWER_SUPPLY_PROP_STATUS,
+					&psy_prop);
+				if (psy_prop.intval < 0) {
+					dev_err(&led->pdev->dev,
+						"Invalid battery status\n");
+					goto exit_flash_led_work;
+				}
+
+				if (psy_prop.intval ==
+						POWER_SUPPLY_STATUS_CHARGING)
+					led->charging_enabled = true;
+				else if (psy_prop.intval ==
+					POWER_SUPPLY_STATUS_DISCHARGING
+					|| psy_prop.intval ==
+					POWER_SUPPLY_STATUS_NOT_CHARGING)
+					led->charging_enabled = false;
+			}
+			max_curr_avail_ma =
+				qpnp_flash_led_get_max_avail_current
+							(flash_node, led);
+			if (max_curr_avail_ma < 0) {
+				dev_err(&led->pdev->dev,
+					"Failed to get max avail curr\n");
+				goto exit_flash_led_work;
+			}
+		}
+
+		if (flash_node->id == FLASH_LED_SWITCH) {
+			if (flash_node->trigger & FLASH_LED0_TRIGGER)
+				total_curr_ma += flash_node->prgm_current;
+			if (flash_node->trigger & FLASH_LED1_TRIGGER)
+				total_curr_ma += flash_node->prgm_current2;
+
+			if (max_curr_avail_ma < total_curr_ma) {
+				flash_node->prgm_current =
+					(flash_node->prgm_current *
+					max_curr_avail_ma) / total_curr_ma;
+				flash_node->prgm_current2 =
+					(flash_node->prgm_current2 *
+					max_curr_avail_ma) / total_curr_ma;
+			}
+
+			val = (u8)(flash_node->prgm_current *
+				FLASH_MAX_LEVEL / flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+				led->current_addr, FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Current register write failed\n");
+				goto exit_flash_led_work;
+			}
+
+			val = (u8)(flash_node->prgm_current2 *
+				FLASH_MAX_LEVEL / flash_node->max_current);
+			rc = qpnp_led_masked_write(led,
+				led->current2_addr, FLASH_CURRENT_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Current register write failed\n");
+				goto exit_flash_led_work;
+			}
+		} else {
+			if (max_curr_avail_ma < flash_node->prgm_current) {
+				dev_err(&led->pdev->dev,
+					"battery only supprots %d mA\n",
+					max_curr_avail_ma);
+				flash_node->prgm_current =
+					 (u16)max_curr_avail_ma;
+			}
+
+			val = (u8)(flash_node->prgm_current *
+					 FLASH_MAX_LEVEL
+					/ flash_node->max_current);
+			if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(
+					led,
+					led->current_addr,
+					FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_1) {
+				rc = qpnp_led_masked_write(
+					led,
+					led->current2_addr,
+					FLASH_CURRENT_MASK, val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"current reg write failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		val = (u8)((flash_node->duration - FLASH_DURATION_DIVIDER)
+						/ FLASH_DURATION_DIVIDER);
+		rc = qpnp_led_masked_write(led,
+			FLASH_SAFETY_TIMER(led->base),
+			FLASH_SAFETY_TIMER_MASK, val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Safety timer reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_MAX_CURRENT(led->base),
+			FLASH_CURRENT_MASK, FLASH_MAX_LEVEL);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Max current reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (!led->charging_enabled) {
+			rc = qpnp_led_masked_write(led,
+				FLASH_MODULE_ENABLE_CTRL(led->base),
+				FLASH_MODULE_ENABLE, FLASH_MODULE_ENABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Module enable reg write failed\n");
+				goto exit_flash_led_work;
+			}
+
+			usleep_range(FLASH_RAMP_UP_DELAY_US_MIN,
+						FLASH_RAMP_UP_DELAY_US_MAX);
+		}
+
+		if (led->revid_data->pmic_subtype == PMI8996_SUBTYPE &&
+						!led->revid_data->rev3) {
+			rc = power_supply_set_property(led->battery_psy,
+						POWER_SUPPLY_PROP_FLASH_TRIGGER,
+							&psy_prop);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Failed to disable charger i/p curr limit\n");
+				goto exit_flash_led_work;
+			}
+		}
+
+		if (led->pdata->hdrm_sns_ch0_en ||
+					led->pdata->hdrm_sns_ch1_en) {
+			if (flash_node->id == FLASH_LED_SWITCH) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					(flash_node->trigger &
+					FLASH_LED0_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE));
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					(flash_node->trigger &
+					FLASH_LED1_TRIGGER ?
+					FLASH_LED_HDRM_SNS_ENABLE :
+					FLASH_LED_HDRM_SNS_DISABLE));
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense enable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_0) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			} else if (flash_node->id == FLASH_LED_1) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_ENABLE);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+					goto exit_flash_led_work;
+				}
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			(flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
+						| FLASH_LED_STROBE_TYPE_HW
+							: flash_node->trigger |
+						FLASH_LED_STROBE_TYPE_HW),
+							flash_node->trigger);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Strobe reg write failed\n");
+			goto exit_flash_led_work;
+		}
+
+		if (led->strobe_debug && led->dbg_feature_en) {
+			udelay(2000);
+			rc = regmap_read(led->regmap,
+					 FLASH_LED_FAULT_STATUS(led->base),
+					 &temp);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"Unable to read from addr= %x, rc(%d)\n",
+				FLASH_LED_FAULT_STATUS(led->base), rc);
+				goto exit_flash_led_work;
+			}
+			led->fault_reg = temp;
+		}
+	} else {
+		pr_err("Both Torch and Flash cannot be select at same time\n");
+		for (i = 0; i < led->num_leds; i++)
+			led->flash_node[i].flash_on = false;
+		goto turn_off;
+	}
+
+	flash_node->flash_on = true;
+	mutex_unlock(&led->flash_led_lock);
+
+	return;
+
+turn_off:
+	if (led->flash_node[led->num_leds - 1].id == FLASH_LED_SWITCH &&
+					flash_node->id != FLASH_LED_SWITCH)
+		led->flash_node[led->num_leds - 1].trigger &=
+						~(0x80 >> flash_node->id);
+	if (flash_node->type == TORCH) {
+		/*
+		 * Checking LED fault status detects hardware open fault.
+		 * If fault occurs, all subsequent LED enablement requests
+		 * will be rejected to protect hardware.
+		 */
+		rc = regmap_read(led->regmap,
+			FLASH_LED_FAULT_STATUS(led->base), &temp);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to read out fault status register\n");
+			goto exit_flash_led_work;
+		}
+
+		led->open_fault |= (val & FLASH_LED_OPEN_FAULT_DETECTED);
+	}
+
+	rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			(flash_node->id == FLASH_LED_SWITCH ? FLASH_STROBE_MASK
+						| FLASH_LED_STROBE_TYPE_HW
+						: flash_node->trigger
+						| FLASH_LED_STROBE_TYPE_HW),
+						FLASH_LED_DISABLE);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Strobe disable failed\n");
+		goto exit_flash_led_work;
+	}
+
+	usleep_range(FLASH_RAMP_DN_DELAY_US_MIN, FLASH_RAMP_DN_DELAY_US_MAX);
+exit_flash_hdrm_sns:
+	if (led->pdata->hdrm_sns_ch0_en) {
+		if (flash_node->id == FLASH_LED_0 ||
+				flash_node->id == FLASH_LED_SWITCH) {
+			rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL0(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_DISABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+				goto exit_flash_hdrm_sns;
+			}
+		}
+	}
+
+	if (led->pdata->hdrm_sns_ch1_en) {
+		if (flash_node->id == FLASH_LED_1 ||
+				flash_node->id == FLASH_LED_SWITCH) {
+			rc = qpnp_led_masked_write(led,
+					FLASH_HDRM_SNS_ENABLE_CTRL1(led->base),
+					FLASH_LED_HDRM_SNS_ENABLE_MASK,
+					FLASH_LED_HDRM_SNS_DISABLE);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Headroom sense disable failed\n");
+				goto exit_flash_hdrm_sns;
+			}
+		}
+	}
+exit_flash_led_work:
+	rc = qpnp_flash_led_module_disable(led, flash_node);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Module disable failed\n");
+		goto exit_flash_led_work;
+	}
+error_enable_gpio:
+	if (flash_node->flash_on && flash_node->num_regulators > 0)
+		flash_regulator_enable(led, flash_node, false);
+
+	flash_node->flash_on = false;
+	mutex_unlock(&led->flash_led_lock);
+}
+
+static void qpnp_flash_led_brightness_set(struct led_classdev *led_cdev,
+						enum led_brightness value)
+{
+	struct flash_node_data *flash_node;
+	struct qpnp_flash_led *led;
+
+	flash_node = container_of(led_cdev, struct flash_node_data, cdev);
+	led = dev_get_drvdata(&flash_node->pdev->dev);
+
+	if (value < LED_OFF) {
+		pr_err("Invalid brightness value\n");
+		return;
+	}
+
+	if (value > flash_node->cdev.max_brightness)
+		value = flash_node->cdev.max_brightness;
+
+	flash_node->cdev.brightness = value;
+	if (led->flash_node[led->num_leds - 1].id ==
+						FLASH_LED_SWITCH) {
+		if (flash_node->type == TORCH)
+			led->flash_node[led->num_leds - 1].type = TORCH;
+		else if (flash_node->type == FLASH)
+			led->flash_node[led->num_leds - 1].type = FLASH;
+
+		led->flash_node[led->num_leds - 1].max_current
+						= flash_node->max_current;
+
+		if (flash_node->id == FLASH_LED_0 ||
+					 flash_node->id == FLASH_LED_1) {
+			if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
+				value = FLASH_LED_MIN_CURRENT_MA;
+
+			flash_node->prgm_current = value;
+			flash_node->flash_on = value ? true : false;
+		} else if (flash_node->id == FLASH_LED_SWITCH) {
+			if (!value) {
+				flash_node->prgm_current = 0;
+				flash_node->prgm_current2 = 0;
+			}
+		}
+	} else {
+		if (value < FLASH_LED_MIN_CURRENT_MA && value != 0)
+			value = FLASH_LED_MIN_CURRENT_MA;
+		flash_node->prgm_current = value;
+	}
+
+	queue_work(led->ordered_workq, &flash_node->work);
+}
+
+static int qpnp_flash_led_init_settings(struct qpnp_flash_led *led)
+{
+	int rc;
+	u8 val, temp_val;
+	uint val_int;
+
+	rc = qpnp_led_masked_write(led,
+			FLASH_MODULE_ENABLE_CTRL(led->base),
+			FLASH_MODULE_ENABLE_MASK,
+			FLASH_LED_MODULE_CTRL_DEFAULT);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Module disable failed\n");
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			FLASH_STROBE_MASK, FLASH_LED_DISABLE);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Strobe disable failed\n");
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led,
+					FLASH_LED_TMR_CTRL(led->base),
+					FLASH_TMR_MASK, FLASH_TMR_SAFETY);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"LED timer ctrl reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	val = (u8)(led->pdata->headroom / FLASH_LED_HEADROOM_DIVIDER -
+						FLASH_LED_HEADROOM_OFFSET);
+	rc = qpnp_led_masked_write(led,
+						FLASH_HEADROOM(led->base),
+						FLASH_HEADROOM_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Headroom reg write failed\n");
+		return rc;
+	}
+
+	val = qpnp_flash_led_get_startup_dly(led->pdata->startup_dly);
+
+	rc = qpnp_led_masked_write(led,
+					FLASH_STARTUP_DELAY(led->base),
+						FLASH_STARTUP_DLY_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Startup delay reg write failed\n");
+		return rc;
+	}
+
+	val = (u8)(led->pdata->clamp_current * FLASH_MAX_LEVEL /
+						FLASH_LED_MAX_CURRENT_MA);
+	rc = qpnp_led_masked_write(led,
+					FLASH_CLAMP_CURRENT(led->base),
+						FLASH_CURRENT_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Clamp current reg write failed\n");
+		return rc;
+	}
+
+	if (led->pdata->pmic_charger_support)
+		val = FLASH_LED_FLASH_HW_VREG_OK;
+	else
+		val = FLASH_LED_FLASH_SW_VREG_OK;
+	rc = qpnp_led_masked_write(led,
+					FLASH_VREG_OK_FORCE(led->base),
+						FLASH_VREG_OK_FORCE_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "VREG OK force reg write failed\n");
+		return rc;
+	}
+
+	if (led->pdata->self_check_en)
+		val = FLASH_MODULE_ENABLE;
+	else
+		val = FLASH_LED_DISABLE;
+	rc = qpnp_led_masked_write(led,
+					FLASH_FAULT_DETECT(led->base),
+						FLASH_FAULT_DETECT_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Fault detect reg write failed\n");
+		return rc;
+	}
+
+	val = 0x0;
+	val |= led->pdata->mask3_en << FLASH_LED_MASK3_ENABLE_SHIFT;
+	val |= FLASH_LED_MASK_MODULE_MASK2_ENABLE;
+	rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
+				FLASH_MASK_MODULE_CONTRL_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Mask module enable failed\n");
+		return rc;
+	}
+
+	rc = regmap_read(led->regmap, FLASH_PERPH_RESET_CTRL(led->base),
+			&val_int);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read from address %x, rc(%d)\n",
+			FLASH_PERPH_RESET_CTRL(led->base), rc);
+		return -EINVAL;
+	}
+	val = (u8)val_int;
+
+	if (led->pdata->follow_rb_disable) {
+		rc = qpnp_led_masked_write(led,
+				FLASH_LED_UNLOCK_SECURE(led->base),
+				FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Secure reg write failed\n");
+			return -EINVAL;
+		}
+
+		val |= FLASH_FOLLOW_OTST2_RB_MASK;
+		rc = qpnp_led_masked_write(led,
+				FLASH_PERPH_RESET_CTRL(led->base),
+				FLASH_FOLLOW_OTST2_RB_MASK, val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"failed to reset OTST2_RB bit\n");
+			return rc;
+		}
+	} else {
+		rc = qpnp_led_masked_write(led,
+				FLASH_LED_UNLOCK_SECURE(led->base),
+				FLASH_SECURE_MASK, FLASH_UNLOCK_SECURE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Secure reg write failed\n");
+			return -EINVAL;
+		}
+
+		val &= ~FLASH_FOLLOW_OTST2_RB_MASK;
+		rc = qpnp_led_masked_write(led,
+				FLASH_PERPH_RESET_CTRL(led->base),
+				FLASH_FOLLOW_OTST2_RB_MASK, val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"failed to reset OTST2_RB bit\n");
+			return rc;
+		}
+	}
+
+	if (!led->pdata->thermal_derate_en)
+		val = 0x0;
+	else {
+		val = led->pdata->thermal_derate_en << 7;
+		val |= led->pdata->thermal_derate_rate << 3;
+		val |= (led->pdata->thermal_derate_threshold -
+				FLASH_LED_THERMAL_THRESHOLD_MIN) /
+				FLASH_LED_THERMAL_DEVIDER;
+	}
+	rc = qpnp_led_masked_write(led,
+					FLASH_THERMAL_DRATE(led->base),
+					FLASH_THERMAL_DERATE_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Thermal derate reg write failed\n");
+		return rc;
+	}
+
+	if (!led->pdata->current_ramp_en)
+		val = 0x0;
+	else {
+		val = led->pdata->current_ramp_en << 7;
+		val |= led->pdata->ramp_up_step << 3;
+		val |= led->pdata->ramp_dn_step;
+	}
+	rc = qpnp_led_masked_write(led,
+						FLASH_CURRENT_RAMP(led->base),
+						FLASH_CURRENT_RAMP_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Current ramp reg write failed\n");
+		return rc;
+	}
+
+	if (!led->pdata->vph_pwr_droop_en)
+		val = 0x0;
+	else {
+		val = led->pdata->vph_pwr_droop_en << 7;
+		val |= ((led->pdata->vph_pwr_droop_threshold -
+				FLASH_LED_VPH_DROOP_THRESHOLD_MIN_MV) /
+				FLASH_LED_VPH_DROOP_THRESHOLD_DIVIDER) << 4;
+		temp_val =
+			qpnp_flash_led_get_droop_debounce_time(
+				led->pdata->vph_pwr_droop_debounce_time);
+		if (temp_val == 0xFF) {
+			dev_err(&led->pdev->dev, "Invalid debounce time\n");
+			return temp_val;
+		}
+
+		val |= temp_val;
+	}
+	rc = qpnp_led_masked_write(led,
+						FLASH_VPH_PWR_DROOP(led->base),
+						FLASH_VPH_PWR_DROOP_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev, "VPH PWR droop reg write failed\n");
+		return rc;
+	}
+
+	led->battery_psy = power_supply_get_by_name("battery");
+	if (!led->battery_psy) {
+		dev_err(&led->pdev->dev,
+			"Failed to get battery power supply\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_flash_led_parse_each_led_dt(struct qpnp_flash_led *led,
+					struct flash_node_data *flash_node)
+{
+	const char *temp_string;
+	struct device_node *node = flash_node->cdev.dev->of_node;
+	struct device_node *temp = NULL;
+	int rc = 0, num_regs = 0;
+	u32 val;
+
+	rc = of_property_read_string(node, "label", &temp_string);
+	if (!rc) {
+		if (strcmp(temp_string, "flash") == 0)
+			flash_node->type = FLASH;
+		else if (strcmp(temp_string, "torch") == 0)
+			flash_node->type = TORCH;
+		else if (strcmp(temp_string, "switch") == 0)
+			flash_node->type = SWITCH;
+		else {
+			dev_err(&led->pdev->dev, "Wrong flash LED type\n");
+			return -EINVAL;
+		}
+	} else if (rc < 0) {
+		dev_err(&led->pdev->dev, "Unable to read flash type\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,current", &val);
+	if (!rc) {
+		if (val < FLASH_LED_MIN_CURRENT_MA)
+			val = FLASH_LED_MIN_CURRENT_MA;
+		flash_node->prgm_current = val;
+	} else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read current\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,id", &val);
+	if (!rc)
+		flash_node->id = (u8)val;
+	else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read led ID\n");
+		return rc;
+	}
+
+	if (flash_node->type == SWITCH || flash_node->type == FLASH) {
+		rc = of_property_read_u32(node, "qcom,duration", &val);
+		if (!rc)
+			flash_node->duration = (u16)val;
+		else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev, "Unable to read duration\n");
+			return rc;
+		}
+	}
+
+	switch (led->peripheral_type) {
+	case FLASH_SUBTYPE_SINGLE:
+		flash_node->trigger = FLASH_LED0_TRIGGER;
+		break;
+	case FLASH_SUBTYPE_DUAL:
+		if (flash_node->id == FLASH_LED_0)
+			flash_node->trigger = FLASH_LED0_TRIGGER;
+		else if (flash_node->id == FLASH_LED_1)
+			flash_node->trigger = FLASH_LED1_TRIGGER;
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid peripheral type\n");
+	}
+
+	while ((temp = of_get_next_child(node, temp))) {
+		if (of_find_property(temp, "regulator-name", NULL))
+			num_regs++;
+	}
+
+	if (num_regs)
+		flash_node->num_regulators = num_regs;
+
+	return rc;
+}
+
+static int qpnp_flash_led_parse_common_dt(
+				struct qpnp_flash_led *led,
+						struct device_node *node)
+{
+	int rc;
+	u32 val, temp_val;
+	const char *temp;
+
+	led->pdata->headroom = FLASH_LED_HEADROOM_DEFAULT_MV;
+	rc = of_property_read_u32(node, "qcom,headroom", &val);
+	if (!rc)
+		led->pdata->headroom = (u16)val;
+	else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read headroom\n");
+		return rc;
+	}
+
+	led->pdata->startup_dly = FLASH_LED_STARTUP_DELAY_DEFAULT_US;
+	rc = of_property_read_u32(node, "qcom,startup-dly", &val);
+	if (!rc)
+		led->pdata->startup_dly = (u8)val;
+	else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read startup delay\n");
+		return rc;
+	}
+
+	led->pdata->clamp_current = FLASH_LED_CLAMP_CURRENT_DEFAULT_MA;
+	rc = of_property_read_u32(node, "qcom,clamp-current", &val);
+	if (!rc) {
+		if (val < FLASH_LED_MIN_CURRENT_MA)
+			val = FLASH_LED_MIN_CURRENT_MA;
+		led->pdata->clamp_current = (u16)val;
+	} else if (rc != -EINVAL) {
+		dev_err(&led->pdev->dev, "Unable to read clamp current\n");
+		return rc;
+	}
+
+	led->pdata->pmic_charger_support =
+			of_property_read_bool(node,
+						"qcom,pmic-charger-support");
+
+	led->pdata->self_check_en =
+			of_property_read_bool(node, "qcom,self-check-enabled");
+
+	led->pdata->thermal_derate_en =
+			of_property_read_bool(node,
+						"qcom,thermal-derate-enabled");
+
+	if (led->pdata->thermal_derate_en) {
+		led->pdata->thermal_derate_rate =
+				FLASH_LED_THERMAL_DERATE_RATE_DEFAULT_PERCENT;
+		rc = of_property_read_string(node, "qcom,thermal-derate-rate",
+									&temp);
+		if (!rc) {
+			temp_val =
+				qpnp_flash_led_get_thermal_derate_rate(temp);
+			if (temp_val < 0) {
+				dev_err(&led->pdev->dev,
+					"Invalid thermal derate rate\n");
+				return -EINVAL;
+			}
+
+			led->pdata->thermal_derate_rate = (u8)temp_val;
+		} else {
+			dev_err(&led->pdev->dev,
+				"Unable to read thermal derate rate\n");
+			return -EINVAL;
+		}
+
+		led->pdata->thermal_derate_threshold =
+				FLASH_LED_THERMAL_DERATE_THRESHOLD_DEFAULT_C;
+		rc = of_property_read_u32(node, "qcom,thermal-derate-threshold",
+									&val);
+		if (!rc)
+			led->pdata->thermal_derate_threshold = (u8)val;
+		else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+				"Unable to read thermal derate threshold\n");
+			return rc;
+		}
+	}
+
+	led->pdata->current_ramp_en =
+			of_property_read_bool(node,
+						"qcom,current-ramp-enabled");
+	if (led->pdata->current_ramp_en) {
+		led->pdata->ramp_up_step = FLASH_LED_RAMP_UP_STEP_DEFAULT_US;
+		rc = of_property_read_string(node, "qcom,ramp_up_step", &temp);
+		if (!rc) {
+			temp_val = qpnp_flash_led_get_ramp_step(temp);
+			if (temp_val < 0) {
+				dev_err(&led->pdev->dev,
+					"Invalid ramp up step values\n");
+				return -EINVAL;
+			}
+			led->pdata->ramp_up_step = (u8)temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+					"Unable to read ramp up steps\n");
+			return rc;
+		}
+
+		led->pdata->ramp_dn_step = FLASH_LED_RAMP_DN_STEP_DEFAULT_US;
+		rc = of_property_read_string(node, "qcom,ramp_dn_step", &temp);
+		if (!rc) {
+			temp_val = qpnp_flash_led_get_ramp_step(temp);
+			if (temp_val < 0) {
+				dev_err(&led->pdev->dev,
+					"Invalid ramp down step values\n");
+				return rc;
+			}
+			led->pdata->ramp_dn_step = (u8)temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+					"Unable to read ramp down steps\n");
+			return rc;
+		}
+	}
+
+	led->pdata->vph_pwr_droop_en = of_property_read_bool(node,
+						"qcom,vph-pwr-droop-enabled");
+	if (led->pdata->vph_pwr_droop_en) {
+		led->pdata->vph_pwr_droop_threshold =
+				FLASH_LED_VPH_PWR_DROOP_THRESHOLD_DEFAULT_MV;
+		rc = of_property_read_u32(node,
+					"qcom,vph-pwr-droop-threshold", &val);
+		if (!rc) {
+			led->pdata->vph_pwr_droop_threshold = (u16)val;
+		} else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+				"Unable to read VPH PWR droop threshold\n");
+			return rc;
+		}
+
+		led->pdata->vph_pwr_droop_debounce_time =
+			FLASH_LED_VPH_PWR_DROOP_DEBOUNCE_TIME_DEFAULT_US;
+		rc = of_property_read_u32(node,
+				"qcom,vph-pwr-droop-debounce-time", &val);
+		if (!rc)
+			led->pdata->vph_pwr_droop_debounce_time = (u8)val;
+		else if (rc != -EINVAL) {
+			dev_err(&led->pdev->dev,
+				"Unable to read VPH PWR droop debounce time\n");
+			return rc;
+		}
+	}
+
+	led->pdata->hdrm_sns_ch0_en = of_property_read_bool(node,
+						"qcom,headroom-sense-ch0-enabled");
+
+	led->pdata->hdrm_sns_ch1_en = of_property_read_bool(node,
+						"qcom,headroom-sense-ch1-enabled");
+
+	led->pdata->power_detect_en = of_property_read_bool(node,
+						"qcom,power-detect-enabled");
+
+	led->pdata->mask3_en = of_property_read_bool(node,
+						"qcom,otst2-module-enabled");
+
+	led->pdata->follow_rb_disable = of_property_read_bool(node,
+						"qcom,follow-otst2-rb-disabled");
+
+	led->pdata->die_current_derate_en = of_property_read_bool(node,
+					"qcom,die-current-derate-enabled");
+
+	if (led->pdata->die_current_derate_en) {
+		led->vadc_dev = qpnp_get_vadc(&led->pdev->dev, "die-temp");
+		if (IS_ERR(led->vadc_dev)) {
+			pr_err("VADC channel property Missing\n");
+			return -EINVAL;
+		}
+
+		if (of_find_property(node, "qcom,die-temp-threshold",
+				&led->pdata->temp_threshold_num)) {
+			if (led->pdata->temp_threshold_num > 0) {
+				led->pdata->die_temp_threshold_degc =
+				devm_kzalloc(&led->pdev->dev,
+						led->pdata->temp_threshold_num,
+						GFP_KERNEL);
+
+				if (led->pdata->die_temp_threshold_degc
+								== NULL) {
+					dev_err(&led->pdev->dev,
+					"failed to allocate die temp array\n");
+					return -ENOMEM;
+				}
+				led->pdata->temp_threshold_num /=
+							sizeof(unsigned int);
+
+				rc = of_property_read_u32_array(node,
+						"qcom,die-temp-threshold",
+				led->pdata->die_temp_threshold_degc,
+						led->pdata->temp_threshold_num);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"couldn't read temp threshold rc=%d\n",
+								rc);
+					return rc;
+				}
+			}
+		}
+
+		if (of_find_property(node, "qcom,die-temp-derate-current",
+					&led->pdata->temp_derate_curr_num)) {
+			if (led->pdata->temp_derate_curr_num > 0) {
+				led->pdata->die_temp_derate_curr_ma =
+					devm_kzalloc(&led->pdev->dev,
+					led->pdata->temp_derate_curr_num,
+					GFP_KERNEL);
+				if (led->pdata->die_temp_derate_curr_ma
+								== NULL) {
+					dev_err(&led->pdev->dev,
+						"failed to allocate die derate current array\n");
+					return -ENOMEM;
+				}
+				led->pdata->temp_derate_curr_num /=
+						sizeof(unsigned int);
+
+				rc = of_property_read_u32_array(node,
+						"qcom,die-temp-derate-current",
+				led->pdata->die_temp_derate_curr_ma,
+				led->pdata->temp_derate_curr_num);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"couldn't read temp limits rc =%d\n",
+								rc);
+					return rc;
+				}
+			}
+		}
+		if (led->pdata->temp_threshold_num !=
+					led->pdata->temp_derate_curr_num) {
+			pr_err("Both array size are not same\n");
+			return -EINVAL;
+		}
+	}
+
+	led->pinctrl = devm_pinctrl_get(&led->pdev->dev);
+	if (IS_ERR_OR_NULL(led->pinctrl)) {
+		dev_err(&led->pdev->dev, "Unable to acquire pinctrl\n");
+		led->pinctrl = NULL;
+		return 0;
+	}
+
+	led->gpio_state_active = pinctrl_lookup_state(led->pinctrl,
+							"flash_led_enable");
+	if (IS_ERR_OR_NULL(led->gpio_state_active)) {
+		dev_err(&led->pdev->dev, "Cannot lookup LED active state\n");
+		devm_pinctrl_put(led->pinctrl);
+		led->pinctrl = NULL;
+		return PTR_ERR(led->gpio_state_active);
+	}
+
+	led->gpio_state_suspend = pinctrl_lookup_state(led->pinctrl,
+							"flash_led_disable");
+	if (IS_ERR_OR_NULL(led->gpio_state_suspend)) {
+		dev_err(&led->pdev->dev, "Cannot lookup LED disable state\n");
+		devm_pinctrl_put(led->pinctrl);
+		led->pinctrl = NULL;
+		return PTR_ERR(led->gpio_state_suspend);
+	}
+
+	return 0;
+}
+
+static int qpnp_flash_led_probe(struct platform_device *pdev)
+{
+	struct qpnp_flash_led *led;
+	unsigned int base;
+	struct device_node *node, *temp;
+	struct dentry *root, *file;
+	int rc, i = 0, j, num_leds = 0;
+	u32 val;
+
+	root = NULL;
+	node = pdev->dev.of_node;
+	if (node == NULL) {
+		dev_info(&pdev->dev, "No flash device defined\n");
+		return -ENODEV;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+
+	led = devm_kzalloc(&pdev->dev, sizeof(*led), GFP_KERNEL);
+	if (!led)
+		return -ENOMEM;
+
+	led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!led->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	led->base = base;
+	led->pdev = pdev;
+	led->current_addr = FLASH_LED0_CURRENT(led->base);
+	led->current2_addr = FLASH_LED1_CURRENT(led->base);
+
+	led->pdata = devm_kzalloc(&pdev->dev, sizeof(*led->pdata), GFP_KERNEL);
+	if (!led->pdata)
+		return -ENOMEM;
+
+	led->peripheral_type = (u8)qpnp_flash_led_get_peripheral_type(led);
+	if (led->peripheral_type < 0) {
+		dev_err(&pdev->dev, "Failed to get peripheral type\n");
+		return rc;
+	}
+
+	rc = qpnp_flash_led_parse_common_dt(led, node);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"Failed to get common config for flash LEDs\n");
+		return rc;
+	}
+
+	rc = qpnp_flash_led_init_settings(led);
+	if (rc) {
+		dev_err(&pdev->dev, "Failed to initialize flash LED\n");
+		return rc;
+	}
+
+	rc = qpnp_get_pmic_revid(led);
+	if (rc)
+		return rc;
+
+	temp = NULL;
+	while ((temp = of_get_next_child(node, temp)))
+		num_leds++;
+
+	if (!num_leds)
+		return -ECHILD;
+
+	led->flash_node = devm_kzalloc(&pdev->dev,
+			(sizeof(struct flash_node_data) * num_leds),
+			GFP_KERNEL);
+	if (!led->flash_node) {
+		dev_err(&pdev->dev, "Unable to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	mutex_init(&led->flash_led_lock);
+
+	led->ordered_workq = alloc_ordered_workqueue("flash_led_workqueue", 0);
+	if (!led->ordered_workq) {
+		dev_err(&pdev->dev, "Failed to allocate ordered workqueue\n");
+		return -ENOMEM;
+	}
+
+	for_each_child_of_node(node, temp) {
+		led->flash_node[i].cdev.brightness_set =
+						qpnp_flash_led_brightness_set;
+		led->flash_node[i].cdev.brightness_get =
+						qpnp_flash_led_brightness_get;
+		led->flash_node[i].pdev = pdev;
+
+		INIT_WORK(&led->flash_node[i].work, qpnp_flash_led_work);
+		rc = of_property_read_string(temp, "qcom,led-name",
+						&led->flash_node[i].cdev.name);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+					"Unable to read flash name\n");
+			return rc;
+		}
+
+		rc = of_property_read_string(temp, "qcom,default-led-trigger",
+				&led->flash_node[i].cdev.default_trigger);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+					"Unable to read trigger name\n");
+			return rc;
+		}
+
+		rc = of_property_read_u32(temp, "qcom,max-current", &val);
+		if (!rc) {
+			if (val < FLASH_LED_MIN_CURRENT_MA)
+				val = FLASH_LED_MIN_CURRENT_MA;
+			led->flash_node[i].max_current = (u16)val;
+			led->flash_node[i].cdev.max_brightness = val;
+		} else {
+			dev_err(&led->pdev->dev,
+					"Unable to read max current\n");
+			return rc;
+		}
+		rc = led_classdev_register(&pdev->dev,
+						&led->flash_node[i].cdev);
+		if (rc) {
+			dev_err(&pdev->dev, "Unable to register led\n");
+			goto error_led_register;
+		}
+
+		led->flash_node[i].cdev.dev->of_node = temp;
+
+		rc = qpnp_flash_led_parse_each_led_dt(led, &led->flash_node[i]);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"Failed to parse config for each LED\n");
+			goto error_led_register;
+		}
+
+		if (led->flash_node[i].num_regulators) {
+			rc = flash_regulator_parse_dt(led, &led->flash_node[i]);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"Unable to parse regulator data\n");
+				goto error_led_register;
+			}
+
+			rc = flash_regulator_setup(led, &led->flash_node[i],
+									true);
+			if (rc) {
+				dev_err(&pdev->dev,
+					"Unable to set up regulator\n");
+				goto error_led_register;
+			}
+		}
+
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++) {
+			rc =
+			sysfs_create_file(&led->flash_node[i].cdev.dev->kobj,
+					&qpnp_flash_led_attrs[j].attr);
+			if (rc)
+				goto error_led_register;
+		}
+
+		i++;
+	}
+
+	led->num_leds = i;
+
+	root = debugfs_create_dir("flashLED", NULL);
+	if (IS_ERR_OR_NULL(root)) {
+		pr_err("Error creating top level directory err%ld",
+			(long)root);
+		if (PTR_ERR(root) == -ENODEV)
+			pr_err("debugfs is not enabled in kernel");
+		goto error_led_debugfs;
+	}
+
+	led->dbgfs_root = root;
+	file = debugfs_create_file("enable_debug", 0600, root, led,
+					&flash_led_dfs_dbg_feature_fops);
+	if (!file) {
+		pr_err("error creating 'enable_debug' entry\n");
+		goto error_led_debugfs;
+	}
+
+	file = debugfs_create_file("latched", 0600, root, led,
+					&flash_led_dfs_latched_reg_fops);
+	if (!file) {
+		pr_err("error creating 'latched' entry\n");
+		goto error_led_debugfs;
+	}
+
+	file = debugfs_create_file("strobe", 0600, root, led,
+					&flash_led_dfs_strobe_reg_fops);
+	if (!file) {
+		pr_err("error creating 'strobe' entry\n");
+		goto error_led_debugfs;
+	}
+
+	dev_set_drvdata(&pdev->dev, led);
+
+	return 0;
+
+error_led_debugfs:
+	i = led->num_leds - 1;
+	j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
+error_led_register:
+	for (; i >= 0; i--) {
+		for (; j >= 0; j--)
+			sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
+						&qpnp_flash_led_attrs[j].attr);
+		j = ARRAY_SIZE(qpnp_flash_led_attrs) - 1;
+		led_classdev_unregister(&led->flash_node[i].cdev);
+	}
+	debugfs_remove_recursive(root);
+	mutex_destroy(&led->flash_led_lock);
+	destroy_workqueue(led->ordered_workq);
+
+	return rc;
+}
+
+static int qpnp_flash_led_remove(struct platform_device *pdev)
+{
+	struct qpnp_flash_led *led  = dev_get_drvdata(&pdev->dev);
+	int i, j;
+
+	for (i = led->num_leds - 1; i >= 0; i--) {
+		if (led->flash_node[i].reg_data) {
+			if (led->flash_node[i].flash_on)
+				flash_regulator_enable(led,
+						&led->flash_node[i], false);
+			flash_regulator_setup(led, &led->flash_node[i],
+								false);
+		}
+		for (j = 0; j < ARRAY_SIZE(qpnp_flash_led_attrs); j++)
+			sysfs_remove_file(&led->flash_node[i].cdev.dev->kobj,
+						&qpnp_flash_led_attrs[j].attr);
+		led_classdev_unregister(&led->flash_node[i].cdev);
+	}
+	debugfs_remove_recursive(led->dbgfs_root);
+	mutex_destroy(&led->flash_led_lock);
+	destroy_workqueue(led->ordered_workq);
+
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = "qcom,qpnp-flash-led",},
+	{ },
+};
+
+static struct platform_driver qpnp_flash_led_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-flash-led",
+		.of_match_table	= spmi_match_table,
+	},
+	.probe		= qpnp_flash_led_probe,
+	.remove		= qpnp_flash_led_remove,
+};
+
+static int __init qpnp_flash_led_init(void)
+{
+	return platform_driver_register(&qpnp_flash_led_driver);
+}
+late_initcall(qpnp_flash_led_init);
+
+static void __exit qpnp_flash_led_exit(void)
+{
+	platform_driver_unregister(&qpnp_flash_led_driver);
+}
+module_exit(qpnp_flash_led_exit);
+
+MODULE_DESCRIPTION("QPNP Flash LED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qpnp-flash");
diff --git a/drivers/leds/leds-qpnp-wled.c b/drivers/leds/leds-qpnp-wled.c
new file mode 100644
index 0000000..1e24c79
--- /dev/null
+++ b/drivers/leds/leds-qpnp-wled.c
@@ -0,0 +1,2221 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/errno.h>
+#include <linux/leds.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/leds-qpnp-wled.h>
+#include <linux/qpnp/qpnp-revid.h>
+
+/* base addresses */
+#define QPNP_WLED_CTRL_BASE		"qpnp-wled-ctrl-base"
+#define QPNP_WLED_SINK_BASE		"qpnp-wled-sink-base"
+
+/* ctrl registers */
+#define QPNP_WLED_FAULT_STATUS(b)	(b + 0x08)
+#define QPNP_WLED_EN_REG(b)		(b + 0x46)
+#define QPNP_WLED_FDBK_OP_REG(b)	(b + 0x48)
+#define QPNP_WLED_VREF_REG(b)		(b + 0x49)
+#define QPNP_WLED_BOOST_DUTY_REG(b)	(b + 0x4B)
+#define QPNP_WLED_SWITCH_FREQ_REG(b)	(b + 0x4C)
+#define QPNP_WLED_OVP_REG(b)		(b + 0x4D)
+#define QPNP_WLED_ILIM_REG(b)		(b + 0x4E)
+#define QPNP_WLED_AMOLED_VOUT_REG(b)	(b + 0x4F)
+#define QPNP_WLED_SOFTSTART_RAMP_DLY(b) (b + 0x53)
+#define QPNP_WLED_VLOOP_COMP_RES_REG(b)	(b + 0x55)
+#define QPNP_WLED_VLOOP_COMP_GM_REG(b)	(b + 0x56)
+#define QPNP_WLED_PSM_CTRL_REG(b)	(b + 0x5B)
+#define QPNP_WLED_LCD_AUTO_PFM_REG(b)	(b + 0x5C)
+#define QPNP_WLED_SC_PRO_REG(b)		(b + 0x5E)
+#define QPNP_WLED_SWIRE_AVDD_REG(b)	(b + 0x5F)
+#define QPNP_WLED_CTRL_SPARE_REG(b)	(b + 0xDF)
+#define QPNP_WLED_TEST1_REG(b)		(b + 0xE2)
+#define QPNP_WLED_TEST4_REG(b)		(b + 0xE5)
+#define QPNP_WLED_REF_7P7_TRIM_REG(b)	(b + 0xF2)
+
+#define QPNP_WLED_7P7_TRIM_MASK		GENMASK(3, 0)
+#define QPNP_WLED_EN_MASK		0x7F
+#define QPNP_WLED_EN_SHIFT		7
+#define QPNP_WLED_FDBK_OP_MASK		0xF8
+#define QPNP_WLED_VREF_MASK		GENMASK(3, 0)
+
+#define QPNP_WLED_VLOOP_COMP_RES_MASK			0xF0
+#define QPNP_WLED_VLOOP_COMP_RES_OVERWRITE		0x80
+#define QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM	320
+#define QPNP_WLED_LOOP_COMP_RES_STEP_KOHM		20
+#define QPNP_WLED_LOOP_COMP_RES_MIN_KOHM		20
+#define QPNP_WLED_LOOP_COMP_RES_MAX_KOHM		320
+#define QPNP_WLED_VLOOP_COMP_GM_MASK			GENMASK(3, 0)
+#define QPNP_WLED_VLOOP_COMP_GM_OVERWRITE		0x80
+#define QPNP_WLED_VLOOP_COMP_AUTO_GM_EN			BIT(6)
+#define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK	GENMASK(5, 4)
+#define QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT	4
+#define QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994	0x03
+#define QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998		0x09
+#define QPNP_WLED_LOOP_GM_DFLT_WLED			0x09
+#define QPNP_WLED_LOOP_EA_GM_MIN			0x0
+#define QPNP_WLED_LOOP_EA_GM_MAX			0xF
+#define QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX		3
+#define QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH		1
+#define QPNP_WLED_VREF_PSM_MASK				0xF8
+#define QPNP_WLED_VREF_PSM_STEP_MV			50
+#define QPNP_WLED_VREF_PSM_MIN_MV			400
+#define QPNP_WLED_VREF_PSM_MAX_MV			750
+#define QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV		450
+#define QPNP_WLED_PSM_CTRL_OVERWRITE			0x80
+#define QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH		1
+#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX		0xF
+#define QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT			7
+#define QPNP_WLED_LCD_AUTO_PFM_EN_BIT			BIT(7)
+#define QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK		GENMASK(3, 0)
+
+#define QPNP_WLED_ILIM_MASK		GENMASK(2, 0)
+#define QPNP_WLED_ILIM_OVERWRITE	BIT(7)
+#define PMI8994_WLED_ILIM_MIN_MA	105
+#define PMI8994_WLED_ILIM_MAX_MA	1980
+#define PMI8994_WLED_DFLT_ILIM_MA	980
+#define PMI8994_AMOLED_DFLT_ILIM_MA	385
+#define PMI8998_WLED_ILIM_MAX_MA	1500
+#define PMI8998_WLED_DFLT_ILIM_MA	970
+#define PMI8998_AMOLED_DFLT_ILIM_MA	620
+#define QPNP_WLED_BOOST_DUTY_MASK	0xFC
+#define QPNP_WLED_BOOST_DUTY_STEP_NS	52
+#define QPNP_WLED_BOOST_DUTY_MIN_NS	26
+#define QPNP_WLED_BOOST_DUTY_MAX_NS	156
+#define QPNP_WLED_DEF_BOOST_DUTY_NS	104
+#define QPNP_WLED_SWITCH_FREQ_MASK	0x70
+#define QPNP_WLED_SWITCH_FREQ_800_KHZ	800
+#define QPNP_WLED_SWITCH_FREQ_1600_KHZ	1600
+#define QPNP_WLED_SWITCH_FREQ_OVERWRITE 0x80
+#define QPNP_WLED_OVP_MASK		GENMASK(1, 0)
+#define QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT	BIT(6)
+#define QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT	BIT(5)
+#define QPNP_WLED_TEST4_EN_CLAMP_BIT		BIT(4)
+#define QPNP_WLED_TEST4_EN_SOFT_START_BIT	BIT(1)
+#define QPNP_WLED_TEST4_EN_VREF_UP			\
+		(QPNP_WLED_TEST4_EN_SH_FOR_SS_BIT |	\
+		QPNP_WLED_TEST4_EN_CLAMP_BIT |		\
+		QPNP_WLED_TEST4_EN_SOFT_START_BIT)
+#define QPNP_WLED_TEST4_EN_IIND_UP	0x1
+
+/* sink registers */
+#define QPNP_WLED_CURR_SINK_REG(b)	(b + 0x46)
+#define QPNP_WLED_SYNC_REG(b)		(b + 0x47)
+#define QPNP_WLED_MOD_REG(b)		(b + 0x4A)
+#define QPNP_WLED_HYB_THRES_REG(b)	(b + 0x4B)
+#define QPNP_WLED_MOD_EN_REG(b, n)	(b + 0x50 + (n * 0x10))
+#define QPNP_WLED_SYNC_DLY_REG(b, n)	(QPNP_WLED_MOD_EN_REG(b, n) + 0x01)
+#define QPNP_WLED_FS_CURR_REG(b, n)	(QPNP_WLED_MOD_EN_REG(b, n) + 0x02)
+#define QPNP_WLED_CABC_REG(b, n)	(QPNP_WLED_MOD_EN_REG(b, n) + 0x06)
+#define QPNP_WLED_BRIGHT_LSB_REG(b, n)	(QPNP_WLED_MOD_EN_REG(b, n) + 0x07)
+#define QPNP_WLED_BRIGHT_MSB_REG(b, n)	(QPNP_WLED_MOD_EN_REG(b, n) + 0x08)
+#define QPNP_WLED_SINK_TEST5_REG(b)	(b + 0xE6)
+
+#define QPNP_WLED_MOD_FREQ_1200_KHZ	1200
+#define QPNP_WLED_MOD_FREQ_2400_KHZ	2400
+#define QPNP_WLED_MOD_FREQ_9600_KHZ	9600
+#define QPNP_WLED_MOD_FREQ_19200_KHZ	19200
+#define QPNP_WLED_MOD_FREQ_MASK		0x3F
+#define QPNP_WLED_MOD_FREQ_SHIFT	6
+#define QPNP_WLED_ACC_CLK_FREQ_MASK	0xE7
+#define QPNP_WLED_ACC_CLK_FREQ_SHIFT	3
+#define QPNP_WLED_PHASE_STAG_MASK	0xDF
+#define QPNP_WLED_PHASE_STAG_SHIFT	5
+#define QPNP_WLED_DIM_RES_MASK		0xFD
+#define QPNP_WLED_DIM_RES_SHIFT		1
+#define QPNP_WLED_DIM_HYB_MASK		0xFB
+#define QPNP_WLED_DIM_HYB_SHIFT		2
+#define QPNP_WLED_DIM_ANA_MASK		0xFE
+#define QPNP_WLED_HYB_THRES_MASK	0xF8
+#define QPNP_WLED_HYB_THRES_MIN		78
+#define QPNP_WLED_DEF_HYB_THRES		625
+#define QPNP_WLED_HYB_THRES_MAX		10000
+#define QPNP_WLED_MOD_EN_MASK		0x7F
+#define QPNP_WLED_MOD_EN_SHFT		7
+#define QPNP_WLED_MOD_EN		1
+#define QPNP_WLED_GATE_DRV_MASK		0xFE
+#define QPNP_WLED_SYNC_DLY_MASK		0xF8
+#define QPNP_WLED_SYNC_DLY_MIN_US	0
+#define QPNP_WLED_SYNC_DLY_MAX_US	1400
+#define QPNP_WLED_SYNC_DLY_STEP_US	200
+#define QPNP_WLED_DEF_SYNC_DLY_US	400
+#define QPNP_WLED_FS_CURR_MASK		0xF0
+#define QPNP_WLED_FS_CURR_MIN_UA	0
+#define QPNP_WLED_FS_CURR_MAX_UA	30000
+#define QPNP_WLED_FS_CURR_STEP_UA	2500
+#define QPNP_WLED_CABC_MASK		0x7F
+#define QPNP_WLED_CABC_SHIFT		7
+#define QPNP_WLED_CURR_SINK_SHIFT	4
+#define QPNP_WLED_BRIGHT_LSB_MASK	0xFF
+#define QPNP_WLED_BRIGHT_MSB_SHIFT	8
+#define QPNP_WLED_BRIGHT_MSB_MASK	0x0F
+#define QPNP_WLED_SYNC			0x0F
+#define QPNP_WLED_SYNC_RESET		0x00
+
+#define QPNP_WLED_SINK_TEST5_HYB	0x14
+#define QPNP_WLED_SINK_TEST5_DIG	0x1E
+#define QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT	BIT(3)
+
+#define QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE	0x0B
+#define QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE	0x05
+
+#define QPNP_WLED_DISP_SEL_REG(b)	(b + 0x44)
+#define QPNP_WLED_MODULE_RDY_REG(b)	(b + 0x45)
+#define QPNP_WLED_MODULE_EN_REG(b)	(b + 0x46)
+#define QPNP_WLED_MODULE_RDY_MASK	0x7F
+#define QPNP_WLED_MODULE_RDY_SHIFT	7
+#define QPNP_WLED_MODULE_EN_MASK	BIT(7)
+#define QPNP_WLED_MODULE_EN_SHIFT	7
+#define QPNP_WLED_DISP_SEL_MASK		0x7F
+#define QPNP_WLED_DISP_SEL_SHIFT	7
+#define QPNP_WLED_EN_SC_DEB_CYCLES_MASK	0x79
+#define QPNP_WLED_EN_DEB_CYCLES_MASK	0xF9
+#define QPNP_WLED_EN_SC_SHIFT		7
+#define QPNP_WLED_SC_PRO_EN_DSCHGR	0x8
+#define QPNP_WLED_SC_DEB_CYCLES_MIN     2
+#define QPNP_WLED_SC_DEB_CYCLES_MAX     16
+#define QPNP_WLED_SC_DEB_CYCLES_SUB     2
+#define QPNP_WLED_SC_DEB_CYCLES_DFLT    4
+#define QPNP_WLED_EXT_FET_DTEST2	0x09
+
+#define QPNP_WLED_SEC_ACCESS_REG(b)    (b + 0xD0)
+#define QPNP_WLED_SEC_UNLOCK           0xA5
+
+#define QPNP_WLED_MAX_STRINGS		4
+#define WLED_MAX_LEVEL_4095		4095
+#define QPNP_WLED_RAMP_DLY_MS		20
+#define QPNP_WLED_TRIGGER_NONE		"none"
+#define QPNP_WLED_STR_SIZE		20
+#define QPNP_WLED_MIN_MSLEEP		20
+#define QPNP_WLED_SC_DLY_MS		20
+
+#define NUM_SUPPORTED_AVDD_VOLTAGES	6
+#define QPNP_WLED_DFLT_AVDD_MV		7600
+#define QPNP_WLED_AVDD_MIN_MV		5650
+#define QPNP_WLED_AVDD_MAX_MV		7900
+#define QPNP_WLED_AVDD_STEP_MV		150
+#define QPNP_WLED_AVDD_MIN_TRIM_VAL	0x0
+#define QPNP_WLED_AVDD_MAX_TRIM_VAL	0xF
+#define QPNP_WLED_AVDD_SEL_SPMI_BIT	BIT(7)
+#define QPNP_WLED_AVDD_SET_BIT		BIT(4)
+
+#define NUM_SUPPORTED_OVP_THRESHOLDS	4
+#define NUM_SUPPORTED_ILIM_THRESHOLDS	8
+
+#define QPNP_WLED_AVDD_MV_TO_REG(val) \
+		((val - QPNP_WLED_AVDD_MIN_MV) / QPNP_WLED_AVDD_STEP_MV)
+
+/* output feedback mode */
+enum qpnp_wled_fdbk_op {
+	QPNP_WLED_FDBK_AUTO,
+	QPNP_WLED_FDBK_WLED1,
+	QPNP_WLED_FDBK_WLED2,
+	QPNP_WLED_FDBK_WLED3,
+	QPNP_WLED_FDBK_WLED4,
+};
+
+/* dimming modes */
+enum qpnp_wled_dim_mode {
+	QPNP_WLED_DIM_ANALOG,
+	QPNP_WLED_DIM_DIGITAL,
+	QPNP_WLED_DIM_HYBRID,
+};
+
+/* wled ctrl debug registers */
+static u8 qpnp_wled_ctrl_dbg_regs[] = {
+	0x44, 0x46, 0x48, 0x49, 0x4b, 0x4c, 0x4d, 0x4e, 0x50, 0x51, 0x52, 0x53,
+	0x54, 0x55, 0x56, 0x57, 0x58, 0x5a, 0x5b, 0x5d, 0x5e, 0xe2
+};
+
+/* wled sink debug registers */
+static u8 qpnp_wled_sink_dbg_regs[] = {
+	0x46, 0x47, 0x48, 0x4a, 0x4b,
+	0x50, 0x51, 0x52, 0x53,	0x56, 0x57, 0x58,
+	0x60, 0x61, 0x62, 0x63,	0x66, 0x67, 0x68,
+	0x70, 0x71, 0x72, 0x73,	0x76, 0x77, 0x78,
+	0x80, 0x81, 0x82, 0x83,	0x86, 0x87, 0x88,
+	0xe6,
+};
+
+static int qpnp_wled_avdd_target_voltages[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+	7900, 7600, 7300, 6400, 6100, 5800,
+};
+
+static u8 qpnp_wled_ovp_reg_settings[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+	0x0, 0x0, 0x1, 0x2, 0x2, 0x3,
+};
+
+static int qpnp_wled_avdd_trim_adjustments[NUM_SUPPORTED_AVDD_VOLTAGES] = {
+	3, 0, -2, 7, 3, 3,
+};
+
+static int qpnp_wled_ovp_thresholds_pmi8994[NUM_SUPPORTED_OVP_THRESHOLDS] = {
+	31000, 29500, 19400, 17800,
+};
+
+static int qpnp_wled_ovp_thresholds_pmi8998[NUM_SUPPORTED_OVP_THRESHOLDS] = {
+	31100, 29600, 19600, 18100,
+};
+
+static int qpnp_wled_ilim_settings_pmi8994[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
+	105, 385, 660, 980, 1150, 1420, 1700, 1980,
+};
+
+static int qpnp_wled_ilim_settings_pmi8998[NUM_SUPPORTED_ILIM_THRESHOLDS] = {
+	105, 280, 450, 620, 970, 1150, 1300, 1500,
+};
+
+struct wled_vref_setting {
+	u32 min_uv;
+	u32 max_uv;
+	u32 step_uv;
+	u32 default_uv;
+};
+
+static struct wled_vref_setting vref_setting_pmi8994 = {
+	300000, 675000, 25000, 350000,
+};
+static struct wled_vref_setting vref_setting_pmi8998 = {
+	60000, 397500, 22500, 127500,
+};
+
+/**
+ *  qpnp_wled - wed data structure
+ *  @ cdev - led class device
+ *  @ pdev - platform device
+ *  @ work - worker for led operation
+ *  @ lock - mutex lock for exclusive access
+ *  @ fdbk_op - output feedback mode
+ *  @ dim_mode - dimming mode
+ *  @ ovp_irq - over voltage protection irq
+ *  @ sc_irq - short circuit irq
+ *  @ sc_cnt - short circuit irq count
+ *  @ avdd_target_voltage_mv - target voltage for AVDD module in mV
+ *  @ ctrl_base - base address for wled ctrl
+ *  @ sink_base - base address for wled sink
+ *  @ mod_freq_khz - modulator frequency in KHZ
+ *  @ hyb_thres - threshold for hybrid dimming
+ *  @ sync_dly_us - sync delay in us
+ *  @ vref_uv - ref voltage in uv
+ *  @ vref_psm_mv - ref psm voltage in mv
+ *  @ loop_comp_res_kohm - control to select the compensation resistor
+ *  @ loop_ea_gm - control to select the gm for the gm stage in control loop
+ *  @ sc_deb_cycles - debounce time for short circuit detection
+ *  @ switch_freq_khz - switching frequency in KHZ
+ *  @ ovp_mv - over voltage protection in mv
+ *  @ ilim_ma - current limiter in ma
+ *  @ boost_duty_ns - boost duty cycle in ns
+ *  @ fs_curr_ua - full scale current in ua
+ *  @ ramp_ms - delay between ramp steps in ms
+ *  @ ramp_step - ramp step size
+ *  @ cons_sync_write_delay_us - delay between two consecutive writes to SYNC
+ *  @ strings - supported list of strings
+ *  @ num_strings - number of strings
+ *  @ loop_auto_gm_thresh - the clamping level for auto gm
+ *  @ lcd_auto_pfm_thresh - the threshold for lcd auto pfm mode
+ *  @ loop_auto_gm_en - select if auto gm is enabled
+ *  @ lcd_auto_pfm_en - select if auto pfm is enabled in lcd mode
+ *  @ avdd_mode_spmi - enable avdd programming via spmi
+ *  @ en_9b_dim_res - enable or disable 9bit dimming
+ *  @ en_phase_stag - enable or disable phase staggering
+ *  @ en_cabc - enable or disable cabc
+ *  @ disp_type_amoled - type of display: LCD/AMOLED
+ *  @ en_ext_pfet_sc_pro - enable sc protection on external pfet
+ */
+struct qpnp_wled {
+	struct led_classdev	cdev;
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	struct pmic_revid_data	*pmic_rev_id;
+	struct work_struct	work;
+	struct mutex		lock;
+	struct mutex		bus_lock;
+	enum qpnp_wled_fdbk_op	fdbk_op;
+	enum qpnp_wled_dim_mode	dim_mode;
+	int			ovp_irq;
+	int			sc_irq;
+	u32			sc_cnt;
+	u32			avdd_target_voltage_mv;
+	u16			ctrl_base;
+	u16			sink_base;
+	u16			mod_freq_khz;
+	u16			hyb_thres;
+	u16			sync_dly_us;
+	u32			vref_uv;
+	u16			vref_psm_mv;
+	u16			loop_comp_res_kohm;
+	u16			loop_ea_gm;
+	u16			sc_deb_cycles;
+	u16			switch_freq_khz;
+	u16			ovp_mv;
+	u16			ilim_ma;
+	u16			boost_duty_ns;
+	u16			fs_curr_ua;
+	u16			ramp_ms;
+	u16			ramp_step;
+	u16			cons_sync_write_delay_us;
+	u8			strings[QPNP_WLED_MAX_STRINGS];
+	u8			num_strings;
+	u8			loop_auto_gm_thresh;
+	u8			lcd_auto_pfm_thresh;
+	bool			loop_auto_gm_en;
+	bool			lcd_auto_pfm_en;
+	bool			avdd_mode_spmi;
+	bool			en_9b_dim_res;
+	bool			en_phase_stag;
+	bool			en_cabc;
+	bool			disp_type_amoled;
+	bool			en_ext_pfet_sc_pro;
+	bool			prev_state;
+	bool			ovp_irq_disabled;
+};
+
+/* helper to read a pmic register */
+static int qpnp_wled_read_reg(struct qpnp_wled *wled, u16 addr, u8 *data)
+{
+	int rc;
+	uint val;
+
+	rc = regmap_read(wled->regmap, addr, &val);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev,
+			"Error reading address: %x(%d)\n", addr, rc);
+		return rc;
+	}
+
+	*data = (u8)val;
+	return 0;
+}
+
+/* helper to write a pmic register */
+static int qpnp_wled_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
+{
+	int rc;
+
+	mutex_lock(&wled->bus_lock);
+	rc = regmap_write(wled->regmap, addr, data);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+			addr, rc);
+		goto out;
+	}
+
+	dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+	mutex_unlock(&wled->bus_lock);
+	return rc;
+}
+
+static int qpnp_wled_masked_write_reg(struct qpnp_wled *wled, u16 addr,
+					u8 mask, u8 data)
+{
+	int rc;
+
+	mutex_lock(&wled->bus_lock);
+	rc = regmap_update_bits(wled->regmap, addr, mask, data);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+			addr, rc);
+		goto out;
+	}
+
+	dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+	mutex_unlock(&wled->bus_lock);
+	return rc;
+}
+
+static int qpnp_wled_sec_write_reg(struct qpnp_wled *wled, u16 addr, u8 data)
+{
+	int rc;
+	u8 reg = QPNP_WLED_SEC_UNLOCK;
+	u16 base_addr = addr & 0xFF00;
+
+	mutex_lock(&wled->bus_lock);
+	rc = regmap_write(wled->regmap, QPNP_WLED_SEC_ACCESS_REG(base_addr),
+			reg);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+			QPNP_WLED_SEC_ACCESS_REG(base_addr), rc);
+		goto out;
+	}
+
+	rc = regmap_write(wled->regmap, addr, data);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Error writing address: %x(%d)\n",
+			addr, rc);
+		goto out;
+	}
+
+	dev_dbg(&wled->pdev->dev, "wrote: WLED_0x%x = 0x%x\n", addr, data);
+out:
+	mutex_unlock(&wled->bus_lock);
+	return rc;
+}
+
+static int qpnp_wled_swire_avdd_config(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 val;
+
+	if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
+		wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
+		return 0;
+
+	if (!wled->disp_type_amoled || wled->avdd_mode_spmi)
+		return 0;
+
+	val = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
+	rc = qpnp_wled_write_reg(wled,
+			QPNP_WLED_SWIRE_AVDD_REG(wled->ctrl_base), val);
+	return rc;
+}
+
+static int qpnp_wled_sync_reg_toggle(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 reg;
+
+	/* sync */
+	reg = QPNP_WLED_SYNC;
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
+			reg);
+	if (rc < 0)
+		return rc;
+
+	if (wled->cons_sync_write_delay_us)
+		usleep_range(wled->cons_sync_write_delay_us,
+				wled->cons_sync_write_delay_us + 1);
+
+	reg = QPNP_WLED_SYNC_RESET;
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_SYNC_REG(wled->sink_base),
+			reg);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+/* set wled to a level of brightness */
+static int qpnp_wled_set_level(struct qpnp_wled *wled, int level)
+{
+	int i, rc;
+	u8 reg;
+
+	/* set brightness registers */
+	for (i = 0; i < wled->num_strings; i++) {
+		reg = level & QPNP_WLED_BRIGHT_LSB_MASK;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_BRIGHT_LSB_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc < 0)
+			return rc;
+
+		reg = level >> QPNP_WLED_BRIGHT_MSB_SHIFT;
+		reg = reg & QPNP_WLED_BRIGHT_MSB_MASK;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_BRIGHT_MSB_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = qpnp_wled_sync_reg_toggle(wled);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_wled_module_en(struct qpnp_wled *wled,
+				u16 base_addr, bool state)
+{
+	int rc;
+
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_MODULE_EN_REG(base_addr),
+			QPNP_WLED_MODULE_EN_MASK,
+			state << QPNP_WLED_MODULE_EN_SHIFT);
+	if (rc < 0)
+		return rc;
+
+	if (wled->ovp_irq > 0) {
+		if (state && wled->ovp_irq_disabled) {
+			/*
+			 * Wait for at least 10ms before enabling OVP fault
+			 * interrupt after enabling the module so that soft
+			 * start is completed. Keep OVP interrupt disabled
+			 * when the module is disabled.
+			 */
+			usleep_range(10000, 11000);
+			enable_irq(wled->ovp_irq);
+			wled->ovp_irq_disabled = false;
+		} else if (!state && !wled->ovp_irq_disabled) {
+			disable_irq(wled->ovp_irq);
+			wled->ovp_irq_disabled = true;
+		}
+	}
+
+	return 0;
+}
+
+/* sysfs store function for ramp */
+static ssize_t qpnp_wled_ramp_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	int i, rc;
+
+	mutex_lock(&wled->lock);
+
+	if (!wled->cdev.brightness) {
+		rc = qpnp_wled_module_en(wled, wled->ctrl_base, true);
+		if (rc) {
+			dev_err(&wled->pdev->dev, "wled enable failed\n");
+			goto unlock_mutex;
+		}
+	}
+
+	/* ramp up */
+	for (i = 0; i <= wled->cdev.max_brightness;) {
+		rc = qpnp_wled_set_level(wled, i);
+		if (rc) {
+			dev_err(&wled->pdev->dev, "wled set level failed\n");
+			goto restore_brightness;
+		}
+
+		if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
+			usleep_range(wled->ramp_ms * USEC_PER_MSEC,
+					wled->ramp_ms * USEC_PER_MSEC);
+		else
+			msleep(wled->ramp_ms);
+
+		if (i == wled->cdev.max_brightness)
+			break;
+
+		i += wled->ramp_step;
+		if (i > wled->cdev.max_brightness)
+			i = wled->cdev.max_brightness;
+	}
+
+	/* ramp down */
+	for (i = wled->cdev.max_brightness; i >= 0;) {
+		rc = qpnp_wled_set_level(wled, i);
+		if (rc) {
+			dev_err(&wled->pdev->dev, "wled set level failed\n");
+			goto restore_brightness;
+		}
+
+		if (wled->ramp_ms < QPNP_WLED_MIN_MSLEEP)
+			usleep_range(wled->ramp_ms * USEC_PER_MSEC,
+					wled->ramp_ms * USEC_PER_MSEC);
+		else
+			msleep(wled->ramp_ms);
+
+		if (i == 0)
+			break;
+
+		i -= wled->ramp_step;
+		if (i < 0)
+			i = 0;
+	}
+
+	dev_info(&wled->pdev->dev, "wled ramp complete\n");
+
+restore_brightness:
+	/* restore the old brightness */
+	qpnp_wled_set_level(wled, wled->cdev.brightness);
+	if (!wled->cdev.brightness) {
+		rc = qpnp_wled_module_en(wled, wled->ctrl_base, false);
+		if (rc)
+			dev_err(&wled->pdev->dev, "wled enable failed\n");
+	}
+unlock_mutex:
+	mutex_unlock(&wled->lock);
+
+	return count;
+}
+
+static int qpnp_wled_dump_regs(struct qpnp_wled *wled, u16 base_addr,
+				u8 dbg_regs[], u8 size, char *label,
+				int count, char *buf)
+{
+	int i, rc;
+	u8 reg;
+
+	for (i = 0; i < size; i++) {
+		rc = qpnp_wled_read_reg(wled, base_addr + dbg_regs[i], &reg);
+		if (rc < 0)
+			return rc;
+
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				"%s: REG_0x%x = 0x%x\n", label,
+				base_addr + dbg_regs[i], reg);
+
+		if (count >= PAGE_SIZE)
+			return PAGE_SIZE - 1;
+	}
+
+	return count;
+}
+
+/* sysfs show function for debug registers */
+static ssize_t qpnp_wled_dump_regs_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	int count = 0;
+
+	count = qpnp_wled_dump_regs(wled, wled->ctrl_base,
+			qpnp_wled_ctrl_dbg_regs,
+			ARRAY_SIZE(qpnp_wled_ctrl_dbg_regs),
+			"wled_ctrl", count, buf);
+
+	if (count < 0 || count == PAGE_SIZE - 1)
+		return count;
+
+	count = qpnp_wled_dump_regs(wled, wled->sink_base,
+			qpnp_wled_sink_dbg_regs,
+			ARRAY_SIZE(qpnp_wled_sink_dbg_regs),
+			"wled_sink", count, buf);
+
+	if (count < 0 || count == PAGE_SIZE - 1)
+		return count;
+
+	return count;
+}
+
+/* sysfs show function for ramp delay in each step */
+static ssize_t qpnp_wled_ramp_ms_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_ms);
+}
+
+/* sysfs store function for ramp delay in each step */
+static ssize_t qpnp_wled_ramp_ms_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	int data, rc;
+
+	rc = kstrtoint(buf, 10, &data);
+	if (rc)
+		return rc;
+
+	wled->ramp_ms = data;
+	return count;
+}
+
+/* sysfs show function for ramp step */
+static ssize_t qpnp_wled_ramp_step_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", wled->ramp_step);
+}
+
+/* sysfs store function for ramp step */
+static ssize_t qpnp_wled_ramp_step_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	int data, rc;
+
+	rc = kstrtoint(buf, 10, &data);
+	if (rc)
+		return rc;
+
+	wled->ramp_step = data;
+	return count;
+}
+
+/* sysfs show function for dim mode */
+static ssize_t qpnp_wled_dim_mode_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	char *str;
+
+	if (wled->dim_mode == QPNP_WLED_DIM_ANALOG)
+		str = "analog";
+	else if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL)
+		str = "digital";
+	else
+		str = "hybrid";
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", str);
+}
+
+/* sysfs store function for dim mode*/
+static ssize_t qpnp_wled_dim_mode_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	char str[QPNP_WLED_STR_SIZE + 1];
+	int rc, temp;
+	u8 reg;
+
+	if (snprintf(str, QPNP_WLED_STR_SIZE, "%s", buf) > QPNP_WLED_STR_SIZE)
+		return -EINVAL;
+
+	if (strcmp(str, "analog") == 0)
+		temp = QPNP_WLED_DIM_ANALOG;
+	else if (strcmp(str, "digital") == 0)
+		temp = QPNP_WLED_DIM_DIGITAL;
+	else
+		temp = QPNP_WLED_DIM_HYBRID;
+
+	if (temp == wled->dim_mode)
+		return count;
+
+	rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
+	if (rc < 0)
+		return rc;
+
+	if (temp == QPNP_WLED_DIM_HYBRID) {
+		reg &= QPNP_WLED_DIM_HYB_MASK;
+		reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
+	} else {
+		reg &= QPNP_WLED_DIM_HYB_MASK;
+		reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
+		reg &= QPNP_WLED_DIM_ANA_MASK;
+		reg |= temp;
+	}
+
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
+	if (rc)
+		return rc;
+
+	wled->dim_mode = temp;
+
+	return count;
+}
+
+/* sysfs show function for full scale current in ua*/
+static ssize_t qpnp_wled_fs_curr_ua_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", wled->fs_curr_ua);
+}
+
+/* sysfs store function for full scale current in ua*/
+static ssize_t qpnp_wled_fs_curr_ua_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(dev);
+	int data, i, rc, temp;
+	u8 reg;
+
+	rc = kstrtoint(buf, 10, &data);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < wled->num_strings; i++) {
+		if (data < QPNP_WLED_FS_CURR_MIN_UA)
+			data = QPNP_WLED_FS_CURR_MIN_UA;
+		else if (data > QPNP_WLED_FS_CURR_MAX_UA)
+			data = QPNP_WLED_FS_CURR_MAX_UA;
+
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_FS_CURR_REG(wled->sink_base,
+					wled->strings[i]), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_FS_CURR_MASK;
+		temp = data / QPNP_WLED_FS_CURR_STEP_UA;
+		reg |= temp;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_FS_CURR_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc)
+			return rc;
+	}
+
+	wled->fs_curr_ua = data;
+
+	rc = qpnp_wled_sync_reg_toggle(wled);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
+		return rc;
+	}
+
+	return count;
+}
+
+/* sysfs attributes exported by wled */
+static struct device_attribute qpnp_wled_attrs[] = {
+	__ATTR(dump_regs, 0664, qpnp_wled_dump_regs_show, NULL),
+	__ATTR(dim_mode, 0664, qpnp_wled_dim_mode_show,
+		qpnp_wled_dim_mode_store),
+	__ATTR(fs_curr_ua, 0664, qpnp_wled_fs_curr_ua_show,
+		qpnp_wled_fs_curr_ua_store),
+	__ATTR(start_ramp, 0664, NULL, qpnp_wled_ramp_store),
+	__ATTR(ramp_ms, 0664, qpnp_wled_ramp_ms_show, qpnp_wled_ramp_ms_store),
+	__ATTR(ramp_step, 0664, qpnp_wled_ramp_step_show,
+		qpnp_wled_ramp_step_store),
+};
+
+/* worker for setting wled brightness */
+static void qpnp_wled_work(struct work_struct *work)
+{
+	struct qpnp_wled *wled;
+	int level, rc;
+
+	wled = container_of(work, struct qpnp_wled, work);
+
+	level = wled->cdev.brightness;
+
+	mutex_lock(&wled->lock);
+
+	if (level) {
+		rc = qpnp_wled_set_level(wled, level);
+		if (rc) {
+			dev_err(&wled->pdev->dev, "wled set level failed\n");
+			goto unlock_mutex;
+		}
+	}
+
+	if (!!level != wled->prev_state) {
+		if (!!level) {
+			/*
+			 * For AMOLED display in pmi8998, SWIRE_AVDD_DEFAULT has
+			 * to be reconfigured every time the module is enabled.
+			 */
+			rc = qpnp_wled_swire_avdd_config(wled);
+			if (rc < 0) {
+				pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
+					rc);
+				goto unlock_mutex;
+			}
+		}
+
+		rc = qpnp_wled_module_en(wled, wled->ctrl_base, !!level);
+		if (rc) {
+			dev_err(&wled->pdev->dev, "wled %sable failed\n",
+						level ? "en" : "dis");
+			goto unlock_mutex;
+		}
+	}
+
+	wled->prev_state = !!level;
+unlock_mutex:
+	mutex_unlock(&wled->lock);
+}
+
+/* get api registered with led classdev for wled brightness */
+static enum led_brightness qpnp_wled_get(struct led_classdev *led_cdev)
+{
+	struct qpnp_wled *wled;
+
+	wled = container_of(led_cdev, struct qpnp_wled, cdev);
+
+	return wled->cdev.brightness;
+}
+
+/* set api registered with led classdev for wled brightness */
+static void qpnp_wled_set(struct led_classdev *led_cdev,
+				enum led_brightness level)
+{
+	struct qpnp_wled *wled;
+
+	wled = container_of(led_cdev, struct qpnp_wled, cdev);
+
+	if (level < LED_OFF)
+		level = LED_OFF;
+	else if (level > wled->cdev.max_brightness)
+		level = wled->cdev.max_brightness;
+
+	wled->cdev.brightness = level;
+	schedule_work(&wled->work);
+}
+
+static int qpnp_wled_set_disp(struct qpnp_wled *wled, u16 base_addr)
+{
+	int rc;
+	u8 reg;
+
+	/* display type */
+	rc = qpnp_wled_read_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr), &reg);
+	if (rc < 0)
+		return rc;
+
+	reg &= QPNP_WLED_DISP_SEL_MASK;
+	reg |= (wled->disp_type_amoled << QPNP_WLED_DISP_SEL_SHIFT);
+
+	rc = qpnp_wled_sec_write_reg(wled, QPNP_WLED_DISP_SEL_REG(base_addr),
+			reg);
+	if (rc)
+		return rc;
+
+	if (wled->disp_type_amoled) {
+		/* Configure the PSM CTRL register for AMOLED */
+		if (wled->vref_psm_mv < QPNP_WLED_VREF_PSM_MIN_MV)
+			wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MIN_MV;
+		else if (wled->vref_psm_mv > QPNP_WLED_VREF_PSM_MAX_MV)
+			wled->vref_psm_mv = QPNP_WLED_VREF_PSM_MAX_MV;
+
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), &reg);
+		if (rc < 0)
+			return rc;
+
+		reg &= QPNP_WLED_VREF_PSM_MASK;
+		reg |= ((wled->vref_psm_mv - QPNP_WLED_VREF_PSM_MIN_MV)/
+			QPNP_WLED_VREF_PSM_STEP_MV);
+		reg |= QPNP_WLED_PSM_CTRL_OVERWRITE;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_PSM_CTRL_REG(wled->ctrl_base), reg);
+		if (rc)
+			return rc;
+
+		/* Configure the VLOOP COMP RES register for AMOLED */
+		if (wled->loop_comp_res_kohm < QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)
+			wled->loop_comp_res_kohm =
+					QPNP_WLED_LOOP_COMP_RES_MIN_KOHM;
+		else if (wled->loop_comp_res_kohm >
+					QPNP_WLED_LOOP_COMP_RES_MAX_KOHM)
+			wled->loop_comp_res_kohm =
+					QPNP_WLED_LOOP_COMP_RES_MAX_KOHM;
+
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
+				&reg);
+		if (rc < 0)
+			return rc;
+
+		reg &= QPNP_WLED_VLOOP_COMP_RES_MASK;
+		reg |= ((wled->loop_comp_res_kohm -
+				 QPNP_WLED_LOOP_COMP_RES_MIN_KOHM)/
+				 QPNP_WLED_LOOP_COMP_RES_STEP_KOHM);
+		reg |= QPNP_WLED_VLOOP_COMP_RES_OVERWRITE;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_VLOOP_COMP_RES_REG(wled->ctrl_base),
+				reg);
+		if (rc)
+			return rc;
+
+		/* Configure the CTRL TEST4 register for AMOLED */
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_TEST4_REG(wled->ctrl_base), &reg);
+		if (rc < 0)
+			return rc;
+
+		reg |= QPNP_WLED_TEST4_EN_IIND_UP;
+		rc = qpnp_wled_sec_write_reg(wled,
+				QPNP_WLED_TEST4_REG(base_addr), reg);
+		if (rc)
+			return rc;
+	} else {
+		/*
+		 * enable VREF_UP to avoid false ovp on low brightness for LCD
+		 */
+		reg = QPNP_WLED_TEST4_EN_VREF_UP
+				| QPNP_WLED_TEST4_EN_DEB_BYPASS_ILIM_BIT;
+		rc = qpnp_wled_sec_write_reg(wled,
+				QPNP_WLED_TEST4_REG(base_addr), reg);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+/* ovp irq handler */
+static irqreturn_t qpnp_wled_ovp_irq_handler(int irq, void *_wled)
+{
+	struct qpnp_wled *wled = _wled;
+	int rc;
+	u8 val;
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
+	if (rc < 0) {
+		pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	pr_err("WLED OVP fault detected, fault_status= %x\n", val);
+	return IRQ_HANDLED;
+}
+
+/* short circuit irq handler */
+static irqreturn_t qpnp_wled_sc_irq_handler(int irq, void *_wled)
+{
+	struct qpnp_wled *wled = _wled;
+	int rc;
+	u8 val;
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_FAULT_STATUS(wled->ctrl_base), &val);
+	if (rc < 0) {
+		pr_err("Error in reading WLED_FAULT_STATUS rc=%d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	pr_err("WLED short circuit detected %d times fault_status=%x\n",
+		++wled->sc_cnt, val);
+	mutex_lock(&wled->lock);
+	qpnp_wled_module_en(wled, wled->ctrl_base, false);
+	msleep(QPNP_WLED_SC_DLY_MS);
+	qpnp_wled_module_en(wled, wled->ctrl_base, true);
+	mutex_unlock(&wled->lock);
+
+	return IRQ_HANDLED;
+}
+
+static bool is_avdd_trim_adjustment_required(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 reg = 0;
+
+	/*
+	 * AVDD trim adjustment is not required for pmi8998/pm660l and not
+	 * supported for pmi8994.
+	 */
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PMI8994_SUBTYPE)
+		return false;
+
+	/*
+	 * Configure TRIM_REG only if disp_type_amoled and it has
+	 * not already been programmed by bootloader.
+	 */
+	if (!wled->disp_type_amoled)
+		return false;
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_CTRL_SPARE_REG(wled->ctrl_base), &reg);
+	if (rc < 0)
+		return false;
+
+	return !(reg & QPNP_WLED_AVDD_SET_BIT);
+}
+
+static int qpnp_wled_gm_config(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 mask = 0, reg = 0;
+
+	/* Configure the LOOP COMP GM register */
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+		if (wled->loop_auto_gm_en)
+			reg |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN;
+
+		if (wled->loop_auto_gm_thresh >
+				QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX)
+			wled->loop_auto_gm_thresh =
+				QPNP_WLED_LOOP_AUTO_GM_THRESH_MAX;
+
+		reg |= wled->loop_auto_gm_thresh <<
+			QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_SHIFT;
+		mask |= QPNP_WLED_VLOOP_COMP_AUTO_GM_EN |
+			QPNP_WLED_VLOOP_COMP_AUTO_GM_THRESH_MASK;
+	}
+
+	if (wled->loop_ea_gm < QPNP_WLED_LOOP_EA_GM_MIN)
+		wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MIN;
+	else if (wled->loop_ea_gm > QPNP_WLED_LOOP_EA_GM_MAX)
+		wled->loop_ea_gm = QPNP_WLED_LOOP_EA_GM_MAX;
+
+	reg |= wled->loop_ea_gm | QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
+	mask |= QPNP_WLED_VLOOP_COMP_GM_MASK |
+		QPNP_WLED_VLOOP_COMP_GM_OVERWRITE;
+
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_VLOOP_COMP_GM_REG(wled->ctrl_base), mask,
+			reg);
+	if (rc)
+		pr_err("write VLOOP_COMP_GM_REG failed, rc=%d]\n", rc);
+
+	return rc;
+}
+
+static int qpnp_wled_ovp_config(struct qpnp_wled *wled)
+{
+	int rc, i, *ovp_table;
+	u8 reg;
+
+	/*
+	 * Configure the OVP register based on ovp_mv only if display type is
+	 * not AMOLED.
+	 */
+	if (wled->disp_type_amoled)
+		return 0;
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		ovp_table = qpnp_wled_ovp_thresholds_pmi8998;
+	else
+		ovp_table = qpnp_wled_ovp_thresholds_pmi8994;
+
+	for (i = 0; i < NUM_SUPPORTED_OVP_THRESHOLDS; i++) {
+		if (wled->ovp_mv == ovp_table[i])
+			break;
+	}
+
+	if (i == NUM_SUPPORTED_OVP_THRESHOLDS) {
+		dev_err(&wled->pdev->dev,
+			"Invalid ovp threshold specified in device tree\n");
+		return -EINVAL;
+	}
+
+	reg = i & QPNP_WLED_OVP_MASK;
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_OVP_REG(wled->ctrl_base),
+			QPNP_WLED_OVP_MASK, reg);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+static int qpnp_wled_avdd_trim_config(struct qpnp_wled *wled)
+{
+	int rc, i;
+	u8 reg;
+
+	for (i = 0; i < NUM_SUPPORTED_AVDD_VOLTAGES; i++) {
+		if (wled->avdd_target_voltage_mv ==
+				qpnp_wled_avdd_target_voltages[i])
+			break;
+	}
+
+	if (i == NUM_SUPPORTED_AVDD_VOLTAGES) {
+		dev_err(&wled->pdev->dev,
+			"Invalid avdd target voltage specified in device tree\n");
+		return -EINVAL;
+	}
+
+	/* Update WLED_OVP register based on desired target voltage */
+	reg = qpnp_wled_ovp_reg_settings[i];
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_OVP_REG(wled->ctrl_base),
+			QPNP_WLED_OVP_MASK, reg);
+	if (rc)
+		return rc;
+
+	/* Update WLED_TRIM register based on desired target voltage */
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), &reg);
+	if (rc)
+		return rc;
+
+	reg += qpnp_wled_avdd_trim_adjustments[i];
+	if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL ||
+			(s8)reg > QPNP_WLED_AVDD_MAX_TRIM_VAL) {
+		dev_dbg(&wled->pdev->dev,
+			 "adjusted trim %d is not within range, capping it\n",
+			 (s8)reg);
+		if ((s8)reg < QPNP_WLED_AVDD_MIN_TRIM_VAL)
+			reg = QPNP_WLED_AVDD_MIN_TRIM_VAL;
+		else
+			reg = QPNP_WLED_AVDD_MAX_TRIM_VAL;
+	}
+
+	reg &= QPNP_WLED_7P7_TRIM_MASK;
+	rc = qpnp_wled_sec_write_reg(wled,
+			QPNP_WLED_REF_7P7_TRIM_REG(wled->ctrl_base), reg);
+	if (rc < 0)
+		dev_err(&wled->pdev->dev, "Write to 7P7_TRIM register failed, rc=%d\n",
+			rc);
+	return rc;
+}
+
+static int qpnp_wled_avdd_mode_config(struct qpnp_wled *wled)
+{
+	int rc;
+	u8 reg = 0;
+
+	/*
+	 * At present, configuring the mode to SPMI/SWIRE for controlling
+	 * AVDD voltage is available only in pmi8998/pm660l.
+	 */
+	if (wled->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE &&
+		wled->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
+		return 0;
+
+	/* AMOLED_VOUT should be configured for AMOLED */
+	if (!wled->disp_type_amoled)
+		return 0;
+
+	/* Configure avdd register */
+	if (wled->avdd_target_voltage_mv > QPNP_WLED_AVDD_MAX_MV) {
+		dev_dbg(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
+			QPNP_WLED_AVDD_MAX_MV);
+		wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MAX_MV;
+	} else if (wled->avdd_target_voltage_mv < QPNP_WLED_AVDD_MIN_MV) {
+		dev_info(&wled->pdev->dev, "Capping avdd target voltage to %d\n",
+			QPNP_WLED_AVDD_MIN_MV);
+		wled->avdd_target_voltage_mv = QPNP_WLED_AVDD_MIN_MV;
+	}
+
+	if (wled->avdd_mode_spmi) {
+		reg = QPNP_WLED_AVDD_MV_TO_REG(wled->avdd_target_voltage_mv);
+		reg |= QPNP_WLED_AVDD_SEL_SPMI_BIT;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_AMOLED_VOUT_REG(wled->ctrl_base),
+				reg);
+		if (rc < 0)
+			pr_err("Write to AMOLED_VOUT register failed, rc=%d\n",
+				rc);
+	} else {
+		rc = qpnp_wled_swire_avdd_config(wled);
+		if (rc < 0)
+			pr_err("Write to SWIRE_AVDD_DEFAULT register failed rc:%d\n",
+				rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_wled_ilim_config(struct qpnp_wled *wled)
+{
+	int rc, i, *ilim_table;
+	u8 reg;
+
+	if (wled->ilim_ma < PMI8994_WLED_ILIM_MIN_MA)
+		wled->ilim_ma = PMI8994_WLED_ILIM_MIN_MA;
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+		ilim_table = qpnp_wled_ilim_settings_pmi8998;
+		if (wled->ilim_ma > PMI8998_WLED_ILIM_MAX_MA)
+			wled->ilim_ma = PMI8998_WLED_ILIM_MAX_MA;
+	} else {
+		ilim_table = qpnp_wled_ilim_settings_pmi8994;
+		if (wled->ilim_ma > PMI8994_WLED_ILIM_MAX_MA)
+			wled->ilim_ma = PMI8994_WLED_ILIM_MAX_MA;
+	}
+
+	for (i = 0; i < NUM_SUPPORTED_ILIM_THRESHOLDS; i++) {
+		if (wled->ilim_ma == ilim_table[i])
+			break;
+	}
+
+	if (i == NUM_SUPPORTED_ILIM_THRESHOLDS) {
+		dev_err(&wled->pdev->dev,
+			"Invalid ilim threshold specified in device tree\n");
+		return -EINVAL;
+	}
+
+	reg = (i & QPNP_WLED_ILIM_MASK) | QPNP_WLED_ILIM_OVERWRITE;
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_ILIM_REG(wled->ctrl_base),
+			QPNP_WLED_ILIM_MASK | QPNP_WLED_ILIM_OVERWRITE, reg);
+	if (rc < 0)
+		dev_err(&wled->pdev->dev, "Write to ILIM register failed, rc=%d\n",
+			rc);
+	return rc;
+}
+
+static int qpnp_wled_vref_config(struct qpnp_wled *wled)
+{
+
+	struct wled_vref_setting vref_setting;
+	int rc;
+	u8 reg = 0;
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		vref_setting = vref_setting_pmi8998;
+	else
+		vref_setting = vref_setting_pmi8994;
+
+	if (wled->vref_uv < vref_setting.min_uv)
+		wled->vref_uv = vref_setting.min_uv;
+	else if (wled->vref_uv > vref_setting.max_uv)
+		wled->vref_uv = vref_setting.max_uv;
+
+	reg |= DIV_ROUND_CLOSEST(wled->vref_uv - vref_setting.min_uv,
+					vref_setting.step_uv);
+
+	rc = qpnp_wled_masked_write_reg(wled,
+			QPNP_WLED_VREF_REG(wled->ctrl_base),
+			QPNP_WLED_VREF_MASK, reg);
+	if (rc)
+		pr_err("Write VREF_REG failed, rc=%d\n", rc);
+
+	return rc;
+}
+
+/* Configure WLED registers */
+static int qpnp_wled_config(struct qpnp_wled *wled)
+{
+	int rc, i, temp;
+	u8 reg = 0;
+
+	/* Configure display type */
+	rc = qpnp_wled_set_disp(wled, wled->ctrl_base);
+	if (rc < 0)
+		return rc;
+
+	/* Configure the FEEDBACK OUTPUT register */
+	rc = qpnp_wled_read_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
+			&reg);
+	if (rc < 0)
+		return rc;
+	reg &= QPNP_WLED_FDBK_OP_MASK;
+	reg |= wled->fdbk_op;
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_FDBK_OP_REG(wled->ctrl_base),
+			reg);
+	if (rc)
+		return rc;
+
+	/* Configure the VREF register */
+	rc = qpnp_wled_vref_config(wled);
+	if (rc < 0) {
+		pr_err("Error in configuring wled vref, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Configure VLOOP_COMP_GM register */
+	rc = qpnp_wled_gm_config(wled);
+	if (rc < 0) {
+		pr_err("Error in configureing wled gm, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Configure the ILIM register */
+	rc = qpnp_wled_ilim_config(wled);
+	if (rc < 0) {
+		pr_err("Error in configuring wled ilim, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Configure auto PFM mode for LCD mode only */
+	if ((wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		&& !wled->disp_type_amoled) {
+		reg = 0;
+		reg |= wled->lcd_auto_pfm_thresh;
+		reg |= wled->lcd_auto_pfm_en <<
+			QPNP_WLED_LCD_AUTO_PFM_EN_SHIFT;
+		rc = qpnp_wled_masked_write_reg(wled,
+				QPNP_WLED_LCD_AUTO_PFM_REG(wled->ctrl_base),
+				QPNP_WLED_LCD_AUTO_PFM_EN_BIT |
+				QPNP_WLED_LCD_AUTO_PFM_THRESH_MASK, reg);
+		if (rc < 0) {
+			pr_err("Write LCD_AUTO_PFM failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Configure the Soft start Ramp delay: for AMOLED - 0,for LCD - 2 */
+	reg = (wled->disp_type_amoled) ? 0 : 2;
+	rc = qpnp_wled_write_reg(wled,
+			QPNP_WLED_SOFTSTART_RAMP_DLY(wled->ctrl_base), reg);
+	if (rc)
+		return rc;
+
+	/* Configure the MAX BOOST DUTY register */
+	if (wled->boost_duty_ns < QPNP_WLED_BOOST_DUTY_MIN_NS)
+		wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MIN_NS;
+	else if (wled->boost_duty_ns > QPNP_WLED_BOOST_DUTY_MAX_NS)
+		wled->boost_duty_ns = QPNP_WLED_BOOST_DUTY_MAX_NS;
+
+	rc = qpnp_wled_read_reg(wled, QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base),
+			&reg);
+	if (rc < 0)
+		return rc;
+	reg &= QPNP_WLED_BOOST_DUTY_MASK;
+	reg |= (wled->boost_duty_ns / QPNP_WLED_BOOST_DUTY_STEP_NS);
+	rc = qpnp_wled_write_reg(wled,
+			QPNP_WLED_BOOST_DUTY_REG(wled->ctrl_base), reg);
+	if (rc)
+		return rc;
+
+	/* Configure the SWITCHING FREQ register */
+	if (wled->switch_freq_khz == QPNP_WLED_SWITCH_FREQ_1600_KHZ)
+		temp = QPNP_WLED_SWITCH_FREQ_1600_KHZ_CODE;
+	else
+		temp = QPNP_WLED_SWITCH_FREQ_800_KHZ_CODE;
+
+	rc = qpnp_wled_read_reg(wled,
+			QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), &reg);
+	if (rc < 0)
+		return rc;
+	reg &= QPNP_WLED_SWITCH_FREQ_MASK;
+	reg |= (temp | QPNP_WLED_SWITCH_FREQ_OVERWRITE);
+	rc = qpnp_wled_write_reg(wled,
+			QPNP_WLED_SWITCH_FREQ_REG(wled->ctrl_base), reg);
+	if (rc)
+		return rc;
+
+	rc = qpnp_wled_ovp_config(wled);
+	if (rc < 0) {
+		pr_err("Error in configuring OVP threshold, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (is_avdd_trim_adjustment_required(wled)) {
+		rc = qpnp_wled_avdd_trim_config(wled);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = qpnp_wled_avdd_mode_config(wled);
+	if (rc < 0)
+		return rc;
+
+	/* Configure the MODULATION register */
+	if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_1200_KHZ) {
+		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_1200_KHZ;
+		temp = 3;
+	} else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_2400_KHZ) {
+		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_2400_KHZ;
+		temp = 2;
+	} else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_9600_KHZ) {
+		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
+		temp = 1;
+	} else if (wled->mod_freq_khz <= QPNP_WLED_MOD_FREQ_19200_KHZ) {
+		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_19200_KHZ;
+		temp = 0;
+	} else {
+		wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
+		temp = 1;
+	}
+
+	rc = qpnp_wled_read_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), &reg);
+	if (rc < 0)
+		return rc;
+	reg &= QPNP_WLED_MOD_FREQ_MASK;
+	reg |= (temp << QPNP_WLED_MOD_FREQ_SHIFT);
+
+	reg &= QPNP_WLED_PHASE_STAG_MASK;
+	reg |= (wled->en_phase_stag << QPNP_WLED_PHASE_STAG_SHIFT);
+
+	reg &= QPNP_WLED_ACC_CLK_FREQ_MASK;
+	reg |= (temp << QPNP_WLED_ACC_CLK_FREQ_SHIFT);
+
+	reg &= QPNP_WLED_DIM_RES_MASK;
+	reg |= (wled->en_9b_dim_res << QPNP_WLED_DIM_RES_SHIFT);
+
+	if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
+		reg &= QPNP_WLED_DIM_HYB_MASK;
+		reg |= (1 << QPNP_WLED_DIM_HYB_SHIFT);
+	} else {
+		reg &= QPNP_WLED_DIM_HYB_MASK;
+		reg |= (0 << QPNP_WLED_DIM_HYB_SHIFT);
+		reg &= QPNP_WLED_DIM_ANA_MASK;
+		reg |= wled->dim_mode;
+	}
+
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_MOD_REG(wled->sink_base), reg);
+	if (rc)
+		return rc;
+
+	/* Configure the HYBRID THRESHOLD register */
+	if (wled->hyb_thres < QPNP_WLED_HYB_THRES_MIN)
+		wled->hyb_thres = QPNP_WLED_HYB_THRES_MIN;
+	else if (wled->hyb_thres > QPNP_WLED_HYB_THRES_MAX)
+		wled->hyb_thres = QPNP_WLED_HYB_THRES_MAX;
+
+	rc = qpnp_wled_read_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
+			&reg);
+	if (rc < 0)
+		return rc;
+	reg &= QPNP_WLED_HYB_THRES_MASK;
+	temp = fls(wled->hyb_thres / QPNP_WLED_HYB_THRES_MIN) - 1;
+	reg |= temp;
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_HYB_THRES_REG(wled->sink_base),
+			reg);
+	if (rc)
+		return rc;
+
+	/* Configure TEST5 register */
+	if (wled->dim_mode == QPNP_WLED_DIM_DIGITAL) {
+		reg = QPNP_WLED_SINK_TEST5_DIG;
+	} else {
+		reg = QPNP_WLED_SINK_TEST5_HYB;
+		if (wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+			reg |= QPNP_WLED_SINK_TEST5_HVG_PULL_STR_BIT;
+	}
+
+	rc = qpnp_wled_sec_write_reg(wled,
+			QPNP_WLED_SINK_TEST5_REG(wled->sink_base), reg);
+	if (rc)
+		return rc;
+
+	/* disable all current sinks and enable selected strings */
+	reg = 0x00;
+	rc = qpnp_wled_write_reg(wled, QPNP_WLED_CURR_SINK_REG(wled->sink_base),
+			reg);
+
+	for (i = 0; i < wled->num_strings; i++) {
+		if (wled->strings[i] >= QPNP_WLED_MAX_STRINGS) {
+			dev_err(&wled->pdev->dev, "Invalid string number\n");
+			return -EINVAL;
+		}
+
+		/* MODULATOR */
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_MOD_EN_REG(wled->sink_base,
+					wled->strings[i]), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_MOD_EN_MASK;
+		reg |= (QPNP_WLED_MOD_EN << QPNP_WLED_MOD_EN_SHFT);
+
+		if (wled->dim_mode == QPNP_WLED_DIM_HYBRID)
+			reg &= QPNP_WLED_GATE_DRV_MASK;
+		else
+			reg |= ~QPNP_WLED_GATE_DRV_MASK;
+
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_MOD_EN_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc)
+			return rc;
+
+		/* SYNC DELAY */
+		if (wled->sync_dly_us > QPNP_WLED_SYNC_DLY_MAX_US)
+			wled->sync_dly_us = QPNP_WLED_SYNC_DLY_MAX_US;
+
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
+					wled->strings[i]), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_SYNC_DLY_MASK;
+		temp = wled->sync_dly_us / QPNP_WLED_SYNC_DLY_STEP_US;
+		reg |= temp;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_SYNC_DLY_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc)
+			return rc;
+
+		/* FULL SCALE CURRENT */
+		if (wled->fs_curr_ua > QPNP_WLED_FS_CURR_MAX_UA)
+			wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
+
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_FS_CURR_REG(wled->sink_base,
+					wled->strings[i]), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_FS_CURR_MASK;
+		temp = wled->fs_curr_ua / QPNP_WLED_FS_CURR_STEP_UA;
+		reg |= temp;
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_FS_CURR_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc)
+			return rc;
+
+		/* CABC */
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_CABC_REG(wled->sink_base,
+					wled->strings[i]), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_CABC_MASK;
+		reg |= (wled->en_cabc << QPNP_WLED_CABC_SHIFT);
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_CABC_REG(wled->sink_base,
+					wled->strings[i]), reg);
+		if (rc)
+			return rc;
+
+		/* Enable CURRENT SINK */
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_CURR_SINK_REG(wled->sink_base), &reg);
+		if (rc < 0)
+			return rc;
+		temp = wled->strings[i] + QPNP_WLED_CURR_SINK_SHIFT;
+		reg |= (1 << temp);
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_CURR_SINK_REG(wled->sink_base), reg);
+		if (rc)
+			return rc;
+	}
+
+	rc = qpnp_wled_sync_reg_toggle(wled);
+	if (rc < 0) {
+		dev_err(&wled->pdev->dev, "Failed to toggle sync reg %d\n", rc);
+		return rc;
+	}
+
+	/* setup ovp and sc irqs */
+	if (wled->ovp_irq >= 0) {
+		rc = devm_request_threaded_irq(&wled->pdev->dev, wled->ovp_irq,
+				NULL, qpnp_wled_ovp_irq_handler, IRQF_ONESHOT,
+				"qpnp_wled_ovp_irq", wled);
+		if (rc < 0) {
+			dev_err(&wled->pdev->dev,
+				"Unable to request ovp(%d) IRQ(err:%d)\n",
+				wled->ovp_irq, rc);
+			return rc;
+		}
+	}
+
+	if (wled->sc_irq >= 0) {
+		wled->sc_cnt = 0;
+		rc = devm_request_threaded_irq(&wled->pdev->dev, wled->sc_irq,
+				NULL, qpnp_wled_sc_irq_handler, IRQF_ONESHOT,
+				"qpnp_wled_sc_irq", wled);
+		if (rc < 0) {
+			dev_err(&wled->pdev->dev,
+				"Unable to request sc(%d) IRQ(err:%d)\n",
+				wled->sc_irq, rc);
+			return rc;
+		}
+
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_EN_SC_DEB_CYCLES_MASK;
+		reg |= 1 << QPNP_WLED_EN_SC_SHIFT;
+
+		if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
+			wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
+		else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
+			wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
+		temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
+		reg |= (temp << 1);
+
+		if (wled->disp_type_amoled)
+			reg |= QPNP_WLED_SC_PRO_EN_DSCHGR;
+
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
+		if (rc)
+			return rc;
+
+		if (wled->en_ext_pfet_sc_pro) {
+			reg = QPNP_WLED_EXT_FET_DTEST2;
+			rc = qpnp_wled_sec_write_reg(wled,
+					QPNP_WLED_TEST1_REG(wled->ctrl_base),
+					reg);
+			if (rc)
+				return rc;
+		}
+	} else {
+		rc = qpnp_wled_read_reg(wled,
+				QPNP_WLED_SC_PRO_REG(wled->ctrl_base), &reg);
+		if (rc < 0)
+			return rc;
+		reg &= QPNP_WLED_EN_DEB_CYCLES_MASK;
+
+		if (wled->sc_deb_cycles < QPNP_WLED_SC_DEB_CYCLES_MIN)
+			wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MIN;
+		else if (wled->sc_deb_cycles > QPNP_WLED_SC_DEB_CYCLES_MAX)
+			wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_MAX;
+		temp = fls(wled->sc_deb_cycles) - QPNP_WLED_SC_DEB_CYCLES_SUB;
+		reg |= (temp << 1);
+
+		rc = qpnp_wled_write_reg(wled,
+				QPNP_WLED_SC_PRO_REG(wled->ctrl_base), reg);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+/* parse wled dtsi parameters */
+static int qpnp_wled_parse_dt(struct qpnp_wled *wled)
+{
+	struct platform_device *pdev = wled->pdev;
+	struct property *prop;
+	const char *temp_str;
+	u32 temp_val;
+	int rc, i;
+	u8 *strings;
+
+	wled->cdev.name = "wled";
+	rc = of_property_read_string(pdev->dev.of_node,
+			"linux,name", &wled->cdev.name);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(&pdev->dev, "Unable to read led name\n");
+		return rc;
+	}
+
+	wled->cdev.default_trigger = QPNP_WLED_TRIGGER_NONE;
+	rc = of_property_read_string(pdev->dev.of_node, "linux,default-trigger",
+					&wled->cdev.default_trigger);
+	if (rc && (rc != -EINVAL)) {
+		dev_err(&pdev->dev, "Unable to read led trigger\n");
+		return rc;
+	}
+
+	wled->disp_type_amoled = of_property_read_bool(pdev->dev.of_node,
+				"qcom,disp-type-amoled");
+	if (wled->disp_type_amoled) {
+		wled->vref_psm_mv = QPNP_WLED_VREF_PSM_DFLT_AMOLED_MV;
+		rc = of_property_read_u32(pdev->dev.of_node,
+				"qcom,vref-psm-mv", &temp_val);
+		if (!rc) {
+			wled->vref_psm_mv = temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read vref-psm\n");
+			return rc;
+		}
+
+		wled->loop_comp_res_kohm =
+			QPNP_WLED_LOOP_COMP_RES_DFLT_AMOLED_KOHM;
+		rc = of_property_read_u32(pdev->dev.of_node,
+				"qcom,loop-comp-res-kohm", &temp_val);
+		if (!rc) {
+			wled->loop_comp_res_kohm = temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read loop-comp-res-kohm\n");
+			return rc;
+		}
+
+		wled->avdd_mode_spmi = of_property_read_bool(pdev->dev.of_node,
+				"qcom,avdd-mode-spmi");
+
+		wled->avdd_target_voltage_mv = QPNP_WLED_DFLT_AVDD_MV;
+		rc = of_property_read_u32(pdev->dev.of_node,
+				"qcom,avdd-target-voltage-mv", &temp_val);
+		if (!rc) {
+			wled->avdd_target_voltage_mv = temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read avdd target voltage\n");
+			return rc;
+		}
+	}
+
+	if (wled->disp_type_amoled) {
+		if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+			wled->loop_ea_gm =
+				QPNP_WLED_LOOP_GM_DFLT_AMOLED_PMI8998;
+		else
+			wled->loop_ea_gm =
+				QPNP_WLED_LOOP_EA_GM_DFLT_AMOLED_PMI8994;
+	} else {
+		wled->loop_ea_gm = QPNP_WLED_LOOP_GM_DFLT_WLED;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,loop-ea-gm", &temp_val);
+	if (!rc) {
+		wled->loop_ea_gm = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read loop-ea-gm\n");
+		return rc;
+	}
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+		wled->loop_auto_gm_en =
+			of_property_read_bool(pdev->dev.of_node,
+					"qcom,loop-auto-gm-en");
+		wled->loop_auto_gm_thresh = QPNP_WLED_LOOP_AUTO_GM_DFLT_THRESH;
+		rc = of_property_read_u8(pdev->dev.of_node,
+				"qcom,loop-auto-gm-thresh",
+				&wled->loop_auto_gm_thresh);
+		if (rc && rc != -EINVAL) {
+			dev_err(&pdev->dev,
+				"Unable to read loop-auto-gm-thresh\n");
+			return rc;
+		}
+	}
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+
+		if (wled->pmic_rev_id->rev4 == PMI8998_V2P0_REV4)
+			wled->lcd_auto_pfm_en = false;
+		else
+			wled->lcd_auto_pfm_en = true;
+
+		wled->lcd_auto_pfm_thresh = QPNP_WLED_LCD_AUTO_PFM_DFLT_THRESH;
+		rc = of_property_read_u8(pdev->dev.of_node,
+				"qcom,lcd-auto-pfm-thresh",
+				&wled->lcd_auto_pfm_thresh);
+		if (rc && rc != -EINVAL) {
+			dev_err(&pdev->dev,
+				"Unable to read lcd-auto-pfm-thresh\n");
+			return rc;
+		}
+
+		if (wled->lcd_auto_pfm_thresh >
+				QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX)
+			wled->lcd_auto_pfm_thresh =
+				QPNP_WLED_LCD_AUTO_PFM_THRESH_MAX;
+	}
+
+	wled->sc_deb_cycles = QPNP_WLED_SC_DEB_CYCLES_DFLT;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,sc-deb-cycles", &temp_val);
+	if (!rc) {
+		wled->sc_deb_cycles = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read sc debounce cycles\n");
+		return rc;
+	}
+
+	wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
+	rc = of_property_read_string(pdev->dev.of_node,
+			"qcom,fdbk-output", &temp_str);
+	if (!rc) {
+		if (strcmp(temp_str, "wled1") == 0)
+			wled->fdbk_op = QPNP_WLED_FDBK_WLED1;
+		else if (strcmp(temp_str, "wled2") == 0)
+			wled->fdbk_op = QPNP_WLED_FDBK_WLED2;
+		else if (strcmp(temp_str, "wled3") == 0)
+			wled->fdbk_op = QPNP_WLED_FDBK_WLED3;
+		else if (strcmp(temp_str, "wled4") == 0)
+			wled->fdbk_op = QPNP_WLED_FDBK_WLED4;
+		else
+			wled->fdbk_op = QPNP_WLED_FDBK_AUTO;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read feedback output\n");
+		return rc;
+	}
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+			wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		wled->vref_uv = vref_setting_pmi8998.default_uv;
+	else
+		wled->vref_uv = vref_setting_pmi8994.default_uv;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,vref-uv", &temp_val);
+	if (!rc) {
+		wled->vref_uv = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read vref\n");
+		return rc;
+	}
+
+	wled->switch_freq_khz = QPNP_WLED_SWITCH_FREQ_800_KHZ;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,switch-freq-khz", &temp_val);
+	if (!rc) {
+		wled->switch_freq_khz = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read switch freq\n");
+		return rc;
+	}
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		wled->ovp_mv = 29600;
+	else
+		wled->ovp_mv = 29500;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,ovp-mv", &temp_val);
+	if (!rc) {
+		wled->ovp_mv = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read ovp\n");
+		return rc;
+	}
+
+	if (wled->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE ||
+		wled->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+		if (wled->disp_type_amoled)
+			wled->ilim_ma = PMI8998_AMOLED_DFLT_ILIM_MA;
+		else
+			wled->ilim_ma = PMI8998_WLED_DFLT_ILIM_MA;
+	} else {
+		if (wled->disp_type_amoled)
+			wled->ilim_ma = PMI8994_AMOLED_DFLT_ILIM_MA;
+		else
+			wled->ilim_ma = PMI8994_WLED_DFLT_ILIM_MA;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,ilim-ma", &temp_val);
+	if (!rc) {
+		wled->ilim_ma = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read ilim\n");
+		return rc;
+	}
+
+	wled->boost_duty_ns = QPNP_WLED_DEF_BOOST_DUTY_NS;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,boost-duty-ns", &temp_val);
+	if (!rc) {
+		wled->boost_duty_ns = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read boost duty\n");
+		return rc;
+	}
+
+	wled->mod_freq_khz = QPNP_WLED_MOD_FREQ_9600_KHZ;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,mod-freq-khz", &temp_val);
+	if (!rc) {
+		wled->mod_freq_khz = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read modulation freq\n");
+		return rc;
+	}
+
+	wled->dim_mode = QPNP_WLED_DIM_HYBRID;
+	rc = of_property_read_string(pdev->dev.of_node,
+			"qcom,dim-mode", &temp_str);
+	if (!rc) {
+		if (strcmp(temp_str, "analog") == 0)
+			wled->dim_mode = QPNP_WLED_DIM_ANALOG;
+		else if (strcmp(temp_str, "digital") == 0)
+			wled->dim_mode = QPNP_WLED_DIM_DIGITAL;
+		else
+			wled->dim_mode = QPNP_WLED_DIM_HYBRID;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read dim mode\n");
+		return rc;
+	}
+
+	if (wled->dim_mode == QPNP_WLED_DIM_HYBRID) {
+		wled->hyb_thres = QPNP_WLED_DEF_HYB_THRES;
+		rc = of_property_read_u32(pdev->dev.of_node,
+				"qcom,hyb-thres", &temp_val);
+		if (!rc) {
+			wled->hyb_thres = temp_val;
+		} else if (rc != -EINVAL) {
+			dev_err(&pdev->dev, "Unable to read hyb threshold\n");
+			return rc;
+		}
+	}
+
+	wled->sync_dly_us = QPNP_WLED_DEF_SYNC_DLY_US;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,sync-dly-us", &temp_val);
+	if (!rc) {
+		wled->sync_dly_us = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read sync delay\n");
+		return rc;
+	}
+
+	wled->fs_curr_ua = QPNP_WLED_FS_CURR_MAX_UA;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,fs-curr-ua", &temp_val);
+	if (!rc) {
+		wled->fs_curr_ua = temp_val;
+	} else if (rc != -EINVAL) {
+		dev_err(&pdev->dev, "Unable to read full scale current\n");
+		return rc;
+	}
+
+	wled->cons_sync_write_delay_us = 0;
+	rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,cons-sync-write-delay-us", &temp_val);
+	if (!rc)
+		wled->cons_sync_write_delay_us = temp_val;
+
+	wled->en_9b_dim_res = of_property_read_bool(pdev->dev.of_node,
+			"qcom,en-9b-dim-res");
+	wled->en_phase_stag = of_property_read_bool(pdev->dev.of_node,
+			"qcom,en-phase-stag");
+	wled->en_cabc = of_property_read_bool(pdev->dev.of_node,
+			"qcom,en-cabc");
+
+	prop = of_find_property(pdev->dev.of_node,
+			"qcom,led-strings-list", &temp_val);
+	if (!prop || !temp_val || temp_val > QPNP_WLED_MAX_STRINGS) {
+		dev_err(&pdev->dev, "Invalid strings info, use default");
+		wled->num_strings = QPNP_WLED_MAX_STRINGS;
+		for (i = 0; i < wled->num_strings; i++)
+			wled->strings[i] = i;
+	} else {
+		wled->num_strings = temp_val;
+		strings = prop->value;
+		for (i = 0; i < wled->num_strings; ++i)
+			wled->strings[i] = strings[i];
+	}
+
+	wled->ovp_irq = platform_get_irq_byname(pdev, "ovp-irq");
+	if (wled->ovp_irq < 0)
+		dev_dbg(&pdev->dev, "ovp irq is not used\n");
+
+	wled->sc_irq = platform_get_irq_byname(pdev, "sc-irq");
+	if (wled->sc_irq < 0)
+		dev_dbg(&pdev->dev, "sc irq is not used\n");
+
+	wled->en_ext_pfet_sc_pro = of_property_read_bool(pdev->dev.of_node,
+					"qcom,en-ext-pfet-sc-pro");
+
+	return 0;
+}
+
+static int qpnp_wled_probe(struct platform_device *pdev)
+{
+	struct qpnp_wled *wled;
+	struct device_node *revid_node;
+	int rc = 0, i;
+	const __be32 *prop;
+
+	wled = devm_kzalloc(&pdev->dev, sizeof(*wled), GFP_KERNEL);
+	if (!wled)
+		return -ENOMEM;
+		wled->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+		if (!wled->regmap) {
+			dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+			return -EINVAL;
+		}
+
+	wled->pdev = pdev;
+
+	revid_node = of_parse_phandle(pdev->dev.of_node, "qcom,pmic-revid", 0);
+	if (!revid_node) {
+		pr_err("Missing qcom,pmic-revid property - driver failed\n");
+		return -EINVAL;
+	}
+
+	wled->pmic_rev_id = get_revid_data(revid_node);
+	if (IS_ERR_OR_NULL(wled->pmic_rev_id)) {
+		pr_err("Unable to get pmic_revid rc=%ld\n",
+			PTR_ERR(wled->pmic_rev_id));
+		/*
+		 * the revid peripheral must be registered, any failure
+		 * here only indicates that the rev-id module has not
+		 * probed yet.
+		 */
+		return -EPROBE_DEFER;
+	}
+
+	pr_debug("PMIC subtype %d Digital major %d\n",
+		wled->pmic_rev_id->pmic_subtype, wled->pmic_rev_id->rev4);
+
+	prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_SINK_BASE,
+			0, 0);
+	if (!prop) {
+		dev_err(&pdev->dev, "Couldnt find sink's addr rc %d\n", rc);
+		return rc;
+	}
+	wled->sink_base = be32_to_cpu(*prop);
+
+	prop = of_get_address_by_name(pdev->dev.of_node, QPNP_WLED_CTRL_BASE,
+			0, 0);
+	if (!prop) {
+		dev_err(&pdev->dev, "Couldnt find ctrl's addr rc = %d\n", rc);
+		return rc;
+	}
+	wled->ctrl_base = be32_to_cpu(*prop);
+
+	dev_set_drvdata(&pdev->dev, wled);
+
+	rc = qpnp_wled_parse_dt(wled);
+	if (rc) {
+		dev_err(&pdev->dev, "DT parsing failed\n");
+		return rc;
+	}
+
+	mutex_init(&wled->bus_lock);
+	rc = qpnp_wled_config(wled);
+	if (rc) {
+		dev_err(&pdev->dev, "wled config failed\n");
+		return rc;
+	}
+
+	mutex_init(&wled->lock);
+	INIT_WORK(&wled->work, qpnp_wled_work);
+	wled->ramp_ms = QPNP_WLED_RAMP_DLY_MS;
+	wled->ramp_step = 1;
+
+	wled->cdev.brightness_set = qpnp_wled_set;
+	wled->cdev.brightness_get = qpnp_wled_get;
+
+	wled->cdev.max_brightness = WLED_MAX_LEVEL_4095;
+
+	rc = led_classdev_register(&pdev->dev, &wled->cdev);
+	if (rc) {
+		dev_err(&pdev->dev, "wled registration failed(%d)\n", rc);
+		goto wled_register_fail;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++) {
+		rc = sysfs_create_file(&wled->cdev.dev->kobj,
+				&qpnp_wled_attrs[i].attr);
+		if (rc < 0) {
+			dev_err(&pdev->dev, "sysfs creation failed\n");
+			goto sysfs_fail;
+		}
+	}
+
+	return 0;
+
+sysfs_fail:
+	for (i--; i >= 0; i--)
+		sysfs_remove_file(&wled->cdev.dev->kobj,
+				&qpnp_wled_attrs[i].attr);
+	led_classdev_unregister(&wled->cdev);
+wled_register_fail:
+	cancel_work_sync(&wled->work);
+	mutex_destroy(&wled->lock);
+	return rc;
+}
+
+static int qpnp_wled_remove(struct platform_device *pdev)
+{
+	struct qpnp_wled *wled = dev_get_drvdata(&pdev->dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(qpnp_wled_attrs); i++)
+		sysfs_remove_file(&wled->cdev.dev->kobj,
+				&qpnp_wled_attrs[i].attr);
+
+	led_classdev_unregister(&wled->cdev);
+	cancel_work_sync(&wled->work);
+	mutex_destroy(&wled->lock);
+
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = "qcom,qpnp-wled",},
+	{ },
+};
+
+static struct platform_driver qpnp_wled_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-wled",
+		.of_match_table	= spmi_match_table,
+	},
+	.probe		= qpnp_wled_probe,
+	.remove		= qpnp_wled_remove,
+};
+
+static int __init qpnp_wled_init(void)
+{
+	return platform_driver_register(&qpnp_wled_driver);
+}
+module_init(qpnp_wled_init);
+
+static void __exit qpnp_wled_exit(void)
+{
+	platform_driver_unregister(&qpnp_wled_driver);
+}
+module_exit(qpnp_wled_exit);
+
+MODULE_DESCRIPTION("QPNP WLED driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qpnp-wled");
diff --git a/drivers/leds/leds-qpnp.c b/drivers/leds/leds-qpnp.c
new file mode 100644
index 0000000..85a6be8
--- /dev/null
+++ b/drivers/leds/leds-qpnp.c
@@ -0,0 +1,4188 @@
+/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/err.h>
+#include <linux/spinlock.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/qpnp/pwm.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+
+#define WLED_MOD_EN_REG(base, n)	(base + 0x60 + n*0x10)
+#define WLED_IDAC_DLY_REG(base, n)	(WLED_MOD_EN_REG(base, n) + 0x01)
+#define WLED_FULL_SCALE_REG(base, n)	(WLED_IDAC_DLY_REG(base, n) + 0x01)
+#define WLED_MOD_SRC_SEL_REG(base, n)	(WLED_FULL_SCALE_REG(base, n) + 0x01)
+
+/* wled control registers */
+#define WLED_OVP_INT_STATUS(base)		(base + 0x10)
+#define WLED_BRIGHTNESS_CNTL_LSB(base, n)	(base + 0x40 + 2*n)
+#define WLED_BRIGHTNESS_CNTL_MSB(base, n)	(base + 0x41 + 2*n)
+#define WLED_MOD_CTRL_REG(base)			(base + 0x46)
+#define WLED_SYNC_REG(base)			(base + 0x47)
+#define WLED_FDBCK_CTRL_REG(base)		(base + 0x48)
+#define WLED_SWITCHING_FREQ_REG(base)		(base + 0x4C)
+#define WLED_OVP_CFG_REG(base)			(base + 0x4D)
+#define WLED_BOOST_LIMIT_REG(base)		(base + 0x4E)
+#define WLED_CURR_SINK_REG(base)		(base + 0x4F)
+#define WLED_HIGH_POLE_CAP_REG(base)		(base + 0x58)
+#define WLED_CURR_SINK_MASK		0xE0
+#define WLED_CURR_SINK_SHFT		0x05
+#define WLED_DISABLE_ALL_SINKS		0x00
+#define WLED_DISABLE_1_2_SINKS		0x80
+#define WLED_SWITCH_FREQ_MASK		0x0F
+#define WLED_OVP_VAL_MASK		0x03
+#define WLED_OVP_INT_MASK		0x02
+#define WLED_OVP_VAL_BIT_SHFT		0x00
+#define WLED_BOOST_LIMIT_MASK		0x07
+#define WLED_BOOST_LIMIT_BIT_SHFT	0x00
+#define WLED_BOOST_ON			0x80
+#define WLED_BOOST_OFF			0x00
+#define WLED_EN_MASK			0x80
+#define WLED_NO_MASK			0x00
+#define WLED_CP_SELECT_MAX		0x03
+#define WLED_CP_SELECT_MASK		0x02
+#define WLED_USE_EXT_GEN_MOD_SRC	0x01
+#define WLED_CTL_DLY_STEP		200
+#define WLED_CTL_DLY_MAX		1400
+#define WLED_MAX_CURR			25
+#define WLED_NO_CURRENT			0x00
+#define WLED_OVP_DELAY			1000
+#define WLED_OVP_DELAY_INT		200
+#define WLED_OVP_DELAY_LOOP		100
+#define WLED_MSB_MASK			0x0F
+#define WLED_MAX_CURR_MASK		0x1F
+#define WLED_OP_FDBCK_MASK		0x07
+#define WLED_OP_FDBCK_BIT_SHFT		0x00
+#define WLED_OP_FDBCK_DEFAULT		0x00
+
+#define WLED_SET_ILIM_CODE		0x01
+
+#define WLED_MAX_LEVEL			4095
+#define WLED_8_BIT_MASK			0xFF
+#define WLED_4_BIT_MASK			0x0F
+#define WLED_8_BIT_SHFT			0x08
+#define WLED_MAX_DUTY_CYCLE		0xFFF
+
+#define WLED_SYNC_VAL			0x07
+#define WLED_SYNC_RESET_VAL		0x00
+
+#define PMIC_VER_8026			0x04
+#define PMIC_VER_8941			0x01
+#define PMIC_VERSION_REG		0x0105
+
+#define WLED_DEFAULT_STRINGS		0x01
+#define WLED_THREE_STRINGS		0x03
+#define WLED_MAX_TRIES			5
+#define WLED_DEFAULT_OVP_VAL		0x02
+#define WLED_BOOST_LIM_DEFAULT		0x03
+#define WLED_CP_SEL_DEFAULT		0x00
+#define WLED_CTRL_DLY_DEFAULT		0x00
+#define WLED_SWITCH_FREQ_DEFAULT	0x0B
+
+#define FLASH_SAFETY_TIMER(base)	(base + 0x40)
+#define FLASH_MAX_CURR(base)		(base + 0x41)
+#define FLASH_LED_0_CURR(base)		(base + 0x42)
+#define FLASH_LED_1_CURR(base)		(base + 0x43)
+#define FLASH_CLAMP_CURR(base)		(base + 0x44)
+#define FLASH_LED_TMR_CTRL(base)	(base + 0x48)
+#define FLASH_HEADROOM(base)		(base + 0x4A)
+#define FLASH_STARTUP_DELAY(base)	(base + 0x4B)
+#define FLASH_MASK_ENABLE(base)		(base + 0x4C)
+#define FLASH_VREG_OK_FORCE(base)	(base + 0x4F)
+#define FLASH_ENABLE_CONTROL(base)	(base + 0x46)
+#define FLASH_LED_STROBE_CTRL(base)	(base + 0x47)
+#define FLASH_WATCHDOG_TMR(base)	(base + 0x49)
+#define FLASH_FAULT_DETECT(base)	(base + 0x51)
+#define FLASH_PERIPHERAL_SUBTYPE(base)	(base + 0x05)
+#define FLASH_CURRENT_RAMP(base)	(base + 0x54)
+
+#define FLASH_MAX_LEVEL			0x4F
+#define TORCH_MAX_LEVEL			0x0F
+#define	FLASH_NO_MASK			0x00
+
+#define FLASH_MASK_1			0x20
+#define FLASH_MASK_REG_MASK		0xE0
+#define FLASH_HEADROOM_MASK		0x03
+#define FLASH_SAFETY_TIMER_MASK		0x7F
+#define FLASH_CURRENT_MASK		0xFF
+#define FLASH_MAX_CURRENT_MASK		0x7F
+#define FLASH_TMR_MASK			0x03
+#define FLASH_TMR_WATCHDOG		0x03
+#define FLASH_TMR_SAFETY		0x00
+#define FLASH_FAULT_DETECT_MASK		0X80
+#define FLASH_HW_VREG_OK		0x40
+#define FLASH_SW_VREG_OK                0x80
+#define FLASH_VREG_MASK			0xC0
+#define FLASH_STARTUP_DLY_MASK		0x02
+#define FLASH_CURRENT_RAMP_MASK		0xBF
+
+#define FLASH_ENABLE_ALL		0xE0
+#define FLASH_ENABLE_MODULE		0x80
+#define FLASH_ENABLE_MODULE_MASK	0x80
+#define FLASH_DISABLE_ALL		0x00
+#define FLASH_ENABLE_MASK		0xE0
+#define FLASH_ENABLE_LED_0		0xC0
+#define FLASH_ENABLE_LED_1		0xA0
+#define FLASH_INIT_MASK			0xE0
+#define	FLASH_SELFCHECK_ENABLE		0x80
+#define FLASH_WATCHDOG_MASK		0x1F
+#define FLASH_RAMP_STEP_27US		0xBF
+
+#define FLASH_HW_SW_STROBE_SEL_MASK	0x04
+#define FLASH_STROBE_MASK		0xC7
+#define FLASH_LED_0_OUTPUT		0x80
+#define FLASH_LED_1_OUTPUT		0x40
+#define FLASH_TORCH_OUTPUT		0xC0
+
+#define FLASH_CURRENT_PRGM_MIN		1
+#define FLASH_CURRENT_PRGM_SHIFT	1
+#define FLASH_CURRENT_MAX		0x4F
+#define FLASH_CURRENT_TORCH		0x07
+
+#define FLASH_DURATION_200ms		0x13
+#define TORCH_DURATION_12s		0x0A
+#define FLASH_CLAMP_200mA		0x0F
+
+#define FLASH_SUBTYPE_DUAL		0x01
+#define FLASH_SUBTYPE_SINGLE		0x02
+
+#define FLASH_RAMP_UP_DELAY_US		1000
+#define FLASH_RAMP_DN_DELAY_US		2160
+
+#define LED_TRIGGER_DEFAULT		"none"
+
+#define RGB_LED_SRC_SEL(base)		(base + 0x45)
+#define RGB_LED_EN_CTL(base)		(base + 0x46)
+#define RGB_LED_ATC_CTL(base)		(base + 0x47)
+
+#define RGB_MAX_LEVEL			LED_FULL
+#define RGB_LED_ENABLE_RED		0x80
+#define RGB_LED_ENABLE_GREEN		0x40
+#define RGB_LED_ENABLE_BLUE		0x20
+#define RGB_LED_SOURCE_VPH_PWR		0x01
+#define RGB_LED_ENABLE_MASK		0xE0
+#define RGB_LED_SRC_MASK		0x03
+#define QPNP_LED_PWM_FLAGS	(PM_PWM_LUT_LOOP | PM_PWM_LUT_RAMP_UP)
+#define QPNP_LUT_RAMP_STEP_DEFAULT	255
+#define	PWM_LUT_MAX_SIZE		63
+#define	PWM_GPLED_LUT_MAX_SIZE		31
+#define RGB_LED_DISABLE			0x00
+
+#define MPP_MAX_LEVEL			LED_FULL
+#define LED_MPP_MODE_CTRL(base)		(base + 0x40)
+#define LED_MPP_VIN_CTRL(base)		(base + 0x41)
+#define LED_MPP_EN_CTRL(base)		(base + 0x46)
+#define LED_MPP_SINK_CTRL(base)		(base + 0x4C)
+
+#define LED_MPP_CURRENT_MIN		5
+#define LED_MPP_CURRENT_MAX		40
+#define LED_MPP_VIN_CTRL_DEFAULT	0
+#define LED_MPP_CURRENT_PER_SETTING	5
+#define LED_MPP_SOURCE_SEL_DEFAULT	LED_MPP_MODE_ENABLE
+
+#define LED_MPP_SINK_MASK		0x07
+#define LED_MPP_MODE_MASK		0x7F
+#define LED_MPP_VIN_MASK		0x03
+#define LED_MPP_EN_MASK			0x80
+#define LED_MPP_SRC_MASK		0x0F
+#define LED_MPP_MODE_CTRL_MASK		0x70
+
+#define LED_MPP_MODE_SINK		(0x06 << 4)
+#define LED_MPP_MODE_ENABLE		0x01
+#define LED_MPP_MODE_OUTPUT		0x10
+#define LED_MPP_MODE_DISABLE		0x00
+#define LED_MPP_EN_ENABLE		0x80
+#define LED_MPP_EN_DISABLE		0x00
+
+#define MPP_SOURCE_DTEST1		0x08
+
+#define GPIO_MAX_LEVEL			LED_FULL
+#define LED_GPIO_MODE_CTRL(base)	(base + 0x40)
+#define LED_GPIO_VIN_CTRL(base)		(base + 0x41)
+#define LED_GPIO_EN_CTRL(base)		(base + 0x46)
+
+#define LED_GPIO_VIN_CTRL_DEFAULT	0
+#define LED_GPIO_SOURCE_SEL_DEFAULT	LED_GPIO_MODE_ENABLE
+
+#define LED_GPIO_MODE_MASK		0x3F
+#define LED_GPIO_VIN_MASK		0x0F
+#define LED_GPIO_EN_MASK		0x80
+#define LED_GPIO_SRC_MASK		0x0F
+#define LED_GPIO_MODE_CTRL_MASK		0x30
+
+#define LED_GPIO_MODE_ENABLE	0x01
+#define LED_GPIO_MODE_DISABLE	0x00
+#define LED_GPIO_MODE_OUTPUT		0x10
+#define LED_GPIO_EN_ENABLE		0x80
+#define LED_GPIO_EN_DISABLE		0x00
+
+#define KPDBL_MAX_LEVEL			LED_FULL
+#define KPDBL_ROW_SRC_SEL(base)		(base + 0x40)
+#define KPDBL_ENABLE(base)		(base + 0x46)
+#define KPDBL_ROW_SRC(base)		(base + 0xE5)
+
+#define KPDBL_ROW_SRC_SEL_VAL_MASK	0x0F
+#define KPDBL_ROW_SCAN_EN_MASK		0x80
+#define KPDBL_ROW_SCAN_VAL_MASK		0x0F
+#define KPDBL_ROW_SCAN_EN_SHIFT		7
+#define KPDBL_MODULE_EN			0x80
+#define KPDBL_MODULE_DIS		0x00
+#define KPDBL_MODULE_EN_MASK		0x80
+#define NUM_KPDBL_LEDS			4
+#define KPDBL_MASTER_BIT_INDEX		0
+
+/**
+ * enum qpnp_leds - QPNP supported led ids
+ * @QPNP_ID_WLED - White led backlight
+ */
+enum qpnp_leds {
+	QPNP_ID_WLED = 0,
+	QPNP_ID_FLASH1_LED0,
+	QPNP_ID_FLASH1_LED1,
+	QPNP_ID_RGB_RED,
+	QPNP_ID_RGB_GREEN,
+	QPNP_ID_RGB_BLUE,
+	QPNP_ID_LED_MPP,
+	QPNP_ID_KPDBL,
+	QPNP_ID_LED_GPIO,
+	QPNP_ID_MAX,
+};
+
+/* current boost limit */
+enum wled_current_boost_limit {
+	WLED_CURR_LIMIT_105mA,
+	WLED_CURR_LIMIT_385mA,
+	WLED_CURR_LIMIT_525mA,
+	WLED_CURR_LIMIT_805mA,
+	WLED_CURR_LIMIT_980mA,
+	WLED_CURR_LIMIT_1260mA,
+	WLED_CURR_LIMIT_1400mA,
+	WLED_CURR_LIMIT_1680mA,
+};
+
+/* over voltage protection threshold */
+enum wled_ovp_threshold {
+	WLED_OVP_35V,
+	WLED_OVP_32V,
+	WLED_OVP_29V,
+	WLED_OVP_27V,
+};
+
+enum flash_headroom {
+	HEADROOM_250mV = 0,
+	HEADROOM_300mV,
+	HEADROOM_400mV,
+	HEADROOM_500mV,
+};
+
+enum flash_startup_dly {
+	DELAY_10us = 0,
+	DELAY_32us,
+	DELAY_64us,
+	DELAY_128us,
+};
+
+enum led_mode {
+	PWM_MODE = 0,
+	LPG_MODE,
+	MANUAL_MODE,
+};
+
+static u8 wled_debug_regs[] = {
+	/* brightness registers */
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+	/* common registers */
+	0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+	0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+	/* LED1 */
+	0x60, 0x61, 0x62, 0x63, 0x66,
+	/* LED2 */
+	0x70, 0x71, 0x72, 0x73, 0x76,
+	/* LED3 */
+	0x80, 0x81, 0x82, 0x83, 0x86,
+};
+
+static u8 flash_debug_regs[] = {
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x48, 0x49, 0x4b, 0x4c,
+	0x4f, 0x46, 0x47,
+};
+
+static u8 rgb_pwm_debug_regs[] = {
+	0x45, 0x46, 0x47,
+};
+
+static u8 mpp_debug_regs[] = {
+	0x40, 0x41, 0x42, 0x45, 0x46, 0x4c,
+};
+
+static u8 kpdbl_debug_regs[] = {
+	0x40, 0x46, 0xb1, 0xb3, 0xb4, 0xe5,
+};
+
+static u8 gpio_debug_regs[] = {
+	0x40, 0x41, 0x42, 0x45, 0x46,
+};
+
+/**
+ *  pwm_config_data - pwm configuration data
+ *  @lut_params - lut parameters to be used by pwm driver
+ *  @pwm_device - pwm device
+ *  @pwm_period_us - period for pwm, in us
+ *  @mode - mode the led operates in
+ *  @old_duty_pcts - storage for duty pcts that may need to be reused
+ *  @default_mode - default mode of LED as set in device tree
+ *  @use_blink - use blink sysfs entry
+ *  @blinking - device is currently blinking w/LPG mode
+ */
+struct pwm_config_data {
+	struct lut_params	lut_params;
+	struct pwm_device	*pwm_dev;
+	u32			pwm_period_us;
+	struct pwm_duty_cycles	*duty_cycles;
+	int	*old_duty_pcts;
+	u8	mode;
+	u8	default_mode;
+	bool	pwm_enabled;
+	bool use_blink;
+	bool blinking;
+};
+
+/**
+ *  wled_config_data - wled configuration data
+ *  @num_strings - number of wled strings to be configured
+ *  @num_physical_strings - physical number of strings supported
+ *  @ovp_val - over voltage protection threshold
+ *  @boost_curr_lim - boot current limit
+ *  @cp_select - high pole capacitance
+ *  @ctrl_delay_us - delay in activation of led
+ *  @dig_mod_gen_en - digital module generator
+ *  @cs_out_en - current sink output enable
+ *  @op_fdbck - selection of output as feedback for the boost
+ */
+struct wled_config_data {
+	u8	num_strings;
+	u8	num_physical_strings;
+	u8	ovp_val;
+	u8	boost_curr_lim;
+	u8	cp_select;
+	u8	ctrl_delay_us;
+	u8	switch_freq;
+	u8	op_fdbck;
+	u8	pmic_version;
+	bool	dig_mod_gen_en;
+	bool	cs_out_en;
+};
+
+/**
+ *  mpp_config_data - mpp configuration data
+ *  @pwm_cfg - device pwm configuration
+ *  @current_setting - current setting, 5ma-40ma in 5ma increments
+ *  @source_sel - source selection
+ *  @mode_ctrl - mode control
+ *  @vin_ctrl - input control
+ *  @min_brightness - minimum brightness supported
+ *  @pwm_mode - pwm mode in use
+ *  @max_uV - maximum regulator voltage
+ *  @min_uV - minimum regulator voltage
+ *  @mpp_reg - regulator to power mpp based LED
+ *  @enable - flag indicating LED on or off
+ */
+struct mpp_config_data {
+	struct pwm_config_data	*pwm_cfg;
+	u8	current_setting;
+	u8	source_sel;
+	u8	mode_ctrl;
+	u8	vin_ctrl;
+	u8	min_brightness;
+	u8 pwm_mode;
+	u32	max_uV;
+	u32	min_uV;
+	struct regulator *mpp_reg;
+	bool	enable;
+};
+
+/**
+ *  flash_config_data - flash configuration data
+ *  @current_prgm - current to be programmed, scaled by max level
+ *  @clamp_curr - clamp current to use
+ *  @headroom - headroom value to use
+ *  @duration - duration of the flash
+ *  @enable_module - enable address for particular flash
+ *  @trigger_flash - trigger flash
+ *  @startup_dly - startup delay for flash
+ *  @strobe_type - select between sw and hw strobe
+ *  @peripheral_subtype - module peripheral subtype
+ *  @current_addr - address to write for current
+ *  @second_addr - address of secondary flash to be written
+ *  @safety_timer - enable safety timer or watchdog timer
+ *  @torch_enable - enable flash LED torch mode
+ *  @flash_reg_get - flash regulator attached or not
+ *  @flash_wa_reg_get - workaround regulator attached or not
+ *  @flash_on - flash status, on or off
+ *  @torch_on - torch status, on or off
+ *  @vreg_ok - specifies strobe type, sw or hw
+ *  @no_smbb_support - specifies if smbb boost is not required and there is a
+    single regulator for both flash and torch
+ *  @flash_boost_reg - boost regulator for flash
+ *  @torch_boost_reg - boost regulator for torch
+ *  @flash_wa_reg - flash regulator for wa
+ */
+struct flash_config_data {
+	u8	current_prgm;
+	u8	clamp_curr;
+	u8	headroom;
+	u8	duration;
+	u8	enable_module;
+	u8	trigger_flash;
+	u8	startup_dly;
+	u8	strobe_type;
+	u8	peripheral_subtype;
+	u16	current_addr;
+	u16	second_addr;
+	bool	safety_timer;
+	bool	torch_enable;
+	bool	flash_reg_get;
+	bool    flash_wa_reg_get;
+	bool	flash_on;
+	bool	torch_on;
+	bool	vreg_ok;
+	bool    no_smbb_support;
+	struct regulator *flash_boost_reg;
+	struct regulator *torch_boost_reg;
+	struct regulator *flash_wa_reg;
+};
+
+/**
+ *  kpdbl_config_data - kpdbl configuration data
+ *  @pwm_cfg - device pwm configuration
+ *  @mode - running mode: pwm or lut
+ *  @row_id - row id of the led
+ *  @row_src_vbst - 0 for vph_pwr and 1 for vbst
+ *  @row_src_en - enable row source
+ *  @always_on - always on row
+ *  @lut_params - lut parameters to be used by pwm driver
+ *  @duty_cycles - duty cycles for lut
+ *  @pwm_mode - pwm mode in use
+ */
+struct kpdbl_config_data {
+	struct pwm_config_data	*pwm_cfg;
+	u32	row_id;
+	bool	row_src_vbst;
+	bool	row_src_en;
+	bool	always_on;
+	struct pwm_duty_cycles  *duty_cycles;
+	struct lut_params	lut_params;
+	u8	pwm_mode;
+};
+
+/**
+ *  rgb_config_data - rgb configuration data
+ *  @pwm_cfg - device pwm configuration
+ *  @enable - bits to enable led
+ */
+struct rgb_config_data {
+	struct pwm_config_data	*pwm_cfg;
+	u8	enable;
+};
+
+/**
+ *  gpio_config_data - gpio configuration data
+ *  @source_sel - source selection
+ *  @mode_ctrl - mode control
+ *  @vin_ctrl - input control
+ *  @enable - flag indicating LED on or off
+ */
+struct gpio_config_data {
+	u8	source_sel;
+	u8	mode_ctrl;
+	u8	vin_ctrl;
+	bool	enable;
+};
+
+/**
+ * struct qpnp_led_data - internal led data structure
+ * @led_classdev - led class device
+ * @delayed_work - delayed work for turning off the LED
+ * @workqueue - dedicated workqueue to handle concurrency
+ * @work - workqueue for led
+ * @id - led index
+ * @base_reg - base register given in device tree
+ * @lock - to protect the transactions
+ * @reg - cached value of led register
+ * @num_leds - number of leds in the module
+ * @max_current - maximum current supported by LED
+ * @default_on - true: default state max, false, default state 0
+ * @turn_off_delay_ms - number of msec before turning off the LED
+ */
+struct qpnp_led_data {
+	struct led_classdev		cdev;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct delayed_work		dwork;
+	struct workqueue_struct		*workqueue;
+	struct work_struct		work;
+	int				id;
+	u16				base;
+	u8				reg;
+	u8				num_leds;
+	struct mutex			lock;
+	struct wled_config_data		*wled_cfg;
+	struct flash_config_data	*flash_cfg;
+	struct kpdbl_config_data	*kpdbl_cfg;
+	struct rgb_config_data		*rgb_cfg;
+	struct mpp_config_data		*mpp_cfg;
+	struct gpio_config_data		*gpio_cfg;
+	int				max_current;
+	bool				default_on;
+	bool				in_order_command_processing;
+	int				turn_off_delay_ms;
+};
+
+static DEFINE_MUTEX(flash_lock);
+static struct pwm_device *kpdbl_master;
+static u32 kpdbl_master_period_us;
+DECLARE_BITMAP(kpdbl_leds_in_use, NUM_KPDBL_LEDS);
+static bool is_kpdbl_master_turn_on;
+
+static int
+qpnp_led_masked_write(struct qpnp_led_data *led, u16 addr, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(led->regmap, addr, mask, val);
+	if (rc)
+		dev_err(&led->pdev->dev,
+			"Unable to regmap_update_bits to addr=%x, rc(%d)\n",
+			addr, rc);
+	return rc;
+}
+
+static void qpnp_dump_regs(struct qpnp_led_data *led, u8 regs[], u8 array_size)
+{
+	int i;
+	u8 val;
+
+	pr_debug("===== %s LED register dump start =====\n", led->cdev.name);
+	for (i = 0; i < array_size; i++) {
+		regmap_bulk_read(led->regmap, led->base + regs[i], &val,
+				 sizeof(val));
+		pr_debug("%s: 0x%x = 0x%x\n", led->cdev.name,
+					led->base + regs[i], val);
+	}
+	pr_debug("===== %s LED register dump end =====\n", led->cdev.name);
+}
+
+static int qpnp_wled_sync(struct qpnp_led_data *led)
+{
+	int rc;
+	u8 val;
+
+	/* sync */
+	val = WLED_SYNC_VAL;
+	rc = regmap_write(led->regmap, WLED_SYNC_REG(led->base), val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"WLED set sync reg failed(%d)\n", rc);
+		return rc;
+	}
+
+	val = WLED_SYNC_RESET_VAL;
+	rc = regmap_write(led->regmap, WLED_SYNC_REG(led->base), val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"WLED reset sync reg failed(%d)\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int qpnp_wled_set(struct qpnp_led_data *led)
+{
+	int rc, duty, level, tries = 0;
+	u8 val, i, num_wled_strings;
+	uint sink_val, ilim_val, ovp_val;
+
+	num_wled_strings = led->wled_cfg->num_strings;
+
+	level = led->cdev.brightness;
+
+	if (level > WLED_MAX_LEVEL)
+		level = WLED_MAX_LEVEL;
+	if (level == 0) {
+		for (i = 0; i < num_wled_strings; i++) {
+			rc = qpnp_led_masked_write(led,
+				WLED_FULL_SCALE_REG(led->base, i),
+				WLED_MAX_CURR_MASK, WLED_NO_CURRENT);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Write max current failure (%d)\n",
+					rc);
+				return rc;
+			}
+		}
+
+		rc = qpnp_wled_sync(led);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED sync failed(%d)\n", rc);
+			return rc;
+		}
+
+		rc = regmap_read(led->regmap, WLED_CURR_SINK_REG(led->base),
+				 &sink_val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED read sink reg failed(%d)\n", rc);
+			return rc;
+		}
+
+		if (led->wled_cfg->pmic_version == PMIC_VER_8026) {
+			val = WLED_DISABLE_ALL_SINKS;
+			rc = regmap_write(led->regmap,
+					  WLED_CURR_SINK_REG(led->base), val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"WLED write sink reg failed(%d)\n", rc);
+				return rc;
+			}
+
+			usleep_range(WLED_OVP_DELAY, WLED_OVP_DELAY + 10);
+		} else if (led->wled_cfg->pmic_version == PMIC_VER_8941) {
+			if (led->wled_cfg->num_physical_strings <=
+					WLED_THREE_STRINGS) {
+				val = WLED_DISABLE_1_2_SINKS;
+				rc = regmap_write(led->regmap,
+						  WLED_CURR_SINK_REG(led->base),
+						  val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"WLED write sink reg failed");
+					return rc;
+				}
+
+				rc = regmap_read(led->regmap,
+					 WLED_BOOST_LIMIT_REG(led->base),
+					 &ilim_val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"Unable to read boost reg");
+				}
+				val = WLED_SET_ILIM_CODE;
+				rc = regmap_write(led->regmap,
+					  WLED_BOOST_LIMIT_REG(led->base),
+					  val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"WLED write sink reg failed");
+					return rc;
+				}
+				usleep_range(WLED_OVP_DELAY,
+					     WLED_OVP_DELAY + 10);
+			} else {
+				val = WLED_DISABLE_ALL_SINKS;
+				rc = regmap_write(led->regmap,
+						  WLED_CURR_SINK_REG(led->base),
+						  val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"WLED write sink reg failed");
+					return rc;
+				}
+
+				msleep(WLED_OVP_DELAY_INT);
+				while (tries < WLED_MAX_TRIES) {
+					rc = regmap_read(led->regmap,
+						 WLED_OVP_INT_STATUS(led->base),
+						 &ovp_val);
+					if (rc) {
+						dev_err(&led->pdev->dev,
+						"Unable to read boost reg");
+					}
+
+					if (ovp_val & WLED_OVP_INT_MASK)
+						break;
+
+					msleep(WLED_OVP_DELAY_LOOP);
+					tries++;
+				}
+				usleep_range(WLED_OVP_DELAY,
+					     WLED_OVP_DELAY + 10);
+			}
+		}
+
+		val = WLED_BOOST_OFF;
+		rc = regmap_write(led->regmap, WLED_MOD_CTRL_REG(led->base),
+				  val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED write ctrl reg failed(%d)\n", rc);
+			return rc;
+		}
+
+		for (i = 0; i < num_wled_strings; i++) {
+			rc = qpnp_led_masked_write(led,
+				WLED_FULL_SCALE_REG(led->base, i),
+				WLED_MAX_CURR_MASK, (u8)led->max_current);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Write max current failure (%d)\n",
+					rc);
+				return rc;
+			}
+		}
+
+		rc = qpnp_wled_sync(led);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED sync failed(%d)\n", rc);
+			return rc;
+		}
+
+		if (led->wled_cfg->pmic_version == PMIC_VER_8941) {
+			if (led->wled_cfg->num_physical_strings <=
+					WLED_THREE_STRINGS) {
+				rc = regmap_write(led->regmap,
+					  WLED_BOOST_LIMIT_REG(led->base),
+					  ilim_val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"WLED write sink reg failed");
+					return rc;
+				}
+			} else {
+				/* restore OVP to original value */
+				rc = regmap_write(led->regmap,
+						  WLED_OVP_CFG_REG(led->base),
+						  *&led->wled_cfg->ovp_val);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"WLED write sink reg failed");
+					return rc;
+				}
+			}
+		}
+
+		/* re-enable all sinks */
+		rc = regmap_write(led->regmap, WLED_CURR_SINK_REG(led->base),
+				  sink_val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED write sink reg failed(%d)\n", rc);
+			return rc;
+		}
+
+	} else {
+		val = WLED_BOOST_ON;
+		rc = regmap_write(led->regmap, WLED_MOD_CTRL_REG(led->base),
+				  val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED write ctrl reg failed(%d)\n", rc);
+			return rc;
+		}
+	}
+
+	duty = (WLED_MAX_DUTY_CYCLE * level) / WLED_MAX_LEVEL;
+
+	/* program brightness control registers */
+	for (i = 0; i < num_wled_strings; i++) {
+		rc = qpnp_led_masked_write(led,
+			WLED_BRIGHTNESS_CNTL_MSB(led->base, i), WLED_MSB_MASK,
+			(duty >> WLED_8_BIT_SHFT) & WLED_4_BIT_MASK);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED set brightness MSB failed(%d)\n", rc);
+			return rc;
+		}
+		val = duty & WLED_8_BIT_MASK;
+		rc = regmap_write(led->regmap,
+				  WLED_BRIGHTNESS_CNTL_LSB(led->base, i), val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED set brightness LSB failed(%d)\n", rc);
+			return rc;
+		}
+	}
+
+	rc = qpnp_wled_sync(led);
+	if (rc) {
+		dev_err(&led->pdev->dev, "WLED sync failed(%d)\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+static int qpnp_mpp_set(struct qpnp_led_data *led)
+{
+	int rc;
+	u8 val;
+	int duty_us, duty_ns, period_us;
+
+	if (led->cdev.brightness) {
+		if (led->mpp_cfg->mpp_reg && !led->mpp_cfg->enable) {
+			rc = regulator_set_voltage(led->mpp_cfg->mpp_reg,
+					led->mpp_cfg->min_uV,
+					led->mpp_cfg->max_uV);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Regulator voltage set failed rc=%d\n",
+									rc);
+				return rc;
+			}
+
+			rc = regulator_enable(led->mpp_cfg->mpp_reg);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Regulator enable failed(%d)\n", rc);
+				goto err_reg_enable;
+			}
+		}
+
+		led->mpp_cfg->enable = true;
+
+		if (led->cdev.brightness < led->mpp_cfg->min_brightness) {
+			dev_warn(&led->pdev->dev, "brightness is less than supported, set to minimum supported\n");
+			led->cdev.brightness = led->mpp_cfg->min_brightness;
+		}
+
+		if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+			if (!led->mpp_cfg->pwm_cfg->blinking) {
+				led->mpp_cfg->pwm_cfg->mode =
+					led->mpp_cfg->pwm_cfg->default_mode;
+				led->mpp_cfg->pwm_mode =
+					led->mpp_cfg->pwm_cfg->default_mode;
+			}
+		}
+		if (led->mpp_cfg->pwm_mode == PWM_MODE) {
+			/*config pwm for brightness scaling*/
+			period_us = led->mpp_cfg->pwm_cfg->pwm_period_us;
+			if (period_us > INT_MAX / NSEC_PER_USEC) {
+				duty_us = (period_us * led->cdev.brightness) /
+					LED_FULL;
+				rc = pwm_config_us(
+					led->mpp_cfg->pwm_cfg->pwm_dev,
+					duty_us,
+					period_us);
+			} else {
+				duty_ns = ((period_us * NSEC_PER_USEC) /
+					LED_FULL) * led->cdev.brightness;
+				rc = pwm_config(
+					led->mpp_cfg->pwm_cfg->pwm_dev,
+					duty_ns,
+					period_us * NSEC_PER_USEC);
+			}
+			if (rc < 0) {
+				dev_err(&led->pdev->dev, "Failed to configure pwm for new values\n");
+				goto err_mpp_reg_write;
+			}
+		}
+
+		if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+			pwm_enable(led->mpp_cfg->pwm_cfg->pwm_dev);
+		else {
+			if (led->cdev.brightness < LED_MPP_CURRENT_MIN)
+				led->cdev.brightness = LED_MPP_CURRENT_MIN;
+			else {
+				/*
+				 * PMIC supports LED intensity from 5mA - 40mA
+				 * in steps of 5mA. Brightness is rounded to
+				 * 5mA or nearest lower supported values
+				 */
+				led->cdev.brightness /= LED_MPP_CURRENT_MIN;
+				led->cdev.brightness *= LED_MPP_CURRENT_MIN;
+			}
+
+			val = (led->cdev.brightness / LED_MPP_CURRENT_MIN) - 1;
+
+			rc = qpnp_led_masked_write(led,
+					LED_MPP_SINK_CTRL(led->base),
+					LED_MPP_SINK_MASK, val);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Failed to write sink control reg\n");
+				goto err_mpp_reg_write;
+			}
+		}
+
+		val = (led->mpp_cfg->source_sel & LED_MPP_SRC_MASK) |
+			(led->mpp_cfg->mode_ctrl & LED_MPP_MODE_CTRL_MASK);
+
+		rc = qpnp_led_masked_write(led,
+			LED_MPP_MODE_CTRL(led->base), LED_MPP_MODE_MASK,
+			val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led mode reg\n");
+			goto err_mpp_reg_write;
+		}
+
+		rc = qpnp_led_masked_write(led,
+				LED_MPP_EN_CTRL(led->base), LED_MPP_EN_MASK,
+				LED_MPP_EN_ENABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Failed to write led enable reg\n");
+			goto err_mpp_reg_write;
+		}
+	} else {
+		if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+			led->mpp_cfg->pwm_cfg->mode =
+				led->mpp_cfg->pwm_cfg->default_mode;
+			led->mpp_cfg->pwm_mode =
+				led->mpp_cfg->pwm_cfg->default_mode;
+			pwm_disable(led->mpp_cfg->pwm_cfg->pwm_dev);
+		}
+		rc = qpnp_led_masked_write(led,
+					LED_MPP_MODE_CTRL(led->base),
+					LED_MPP_MODE_MASK,
+					LED_MPP_MODE_DISABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led mode reg\n");
+			goto err_mpp_reg_write;
+		}
+
+		rc = qpnp_led_masked_write(led,
+					LED_MPP_EN_CTRL(led->base),
+					LED_MPP_EN_MASK,
+					LED_MPP_EN_DISABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led enable reg\n");
+			goto err_mpp_reg_write;
+		}
+
+		if (led->mpp_cfg->mpp_reg && led->mpp_cfg->enable) {
+			rc = regulator_disable(led->mpp_cfg->mpp_reg);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"MPP regulator disable failed(%d)\n",
+					rc);
+				return rc;
+			}
+
+			rc = regulator_set_voltage(led->mpp_cfg->mpp_reg,
+						0, led->mpp_cfg->max_uV);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"MPP regulator voltage set failed(%d)\n",
+					rc);
+				return rc;
+			}
+		}
+
+		led->mpp_cfg->enable = false;
+	}
+
+	if (led->mpp_cfg->pwm_mode != MANUAL_MODE)
+		led->mpp_cfg->pwm_cfg->blinking = false;
+	qpnp_dump_regs(led, mpp_debug_regs, ARRAY_SIZE(mpp_debug_regs));
+
+	return 0;
+
+err_mpp_reg_write:
+	if (led->mpp_cfg->mpp_reg)
+		regulator_disable(led->mpp_cfg->mpp_reg);
+err_reg_enable:
+	if (led->mpp_cfg->mpp_reg)
+		regulator_set_voltage(led->mpp_cfg->mpp_reg, 0,
+							led->mpp_cfg->max_uV);
+	led->mpp_cfg->enable = false;
+
+	return rc;
+}
+
+static int qpnp_gpio_set(struct qpnp_led_data *led)
+{
+	int rc, val;
+
+	if (led->cdev.brightness) {
+		val = (led->gpio_cfg->source_sel & LED_GPIO_SRC_MASK) |
+			(led->gpio_cfg->mode_ctrl & LED_GPIO_MODE_CTRL_MASK);
+
+		rc = qpnp_led_masked_write(led,
+			 LED_GPIO_MODE_CTRL(led->base),
+			 LED_GPIO_MODE_MASK,
+			 val);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led mode reg\n");
+			goto err_gpio_reg_write;
+		}
+
+		rc = qpnp_led_masked_write(led,
+			 LED_GPIO_EN_CTRL(led->base),
+			 LED_GPIO_EN_MASK,
+			 LED_GPIO_EN_ENABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led enable reg\n");
+			goto err_gpio_reg_write;
+		}
+
+		led->gpio_cfg->enable = true;
+	} else {
+		rc = qpnp_led_masked_write(led,
+				LED_GPIO_MODE_CTRL(led->base),
+				LED_GPIO_MODE_MASK,
+				LED_GPIO_MODE_DISABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led mode reg\n");
+			goto err_gpio_reg_write;
+		}
+
+		rc = qpnp_led_masked_write(led,
+				LED_GPIO_EN_CTRL(led->base),
+				LED_GPIO_EN_MASK,
+				LED_GPIO_EN_DISABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+					"Failed to write led enable reg\n");
+			goto err_gpio_reg_write;
+		}
+
+		led->gpio_cfg->enable = false;
+	}
+
+	qpnp_dump_regs(led, gpio_debug_regs, ARRAY_SIZE(gpio_debug_regs));
+
+	return 0;
+
+err_gpio_reg_write:
+	led->gpio_cfg->enable = false;
+
+	return rc;
+}
+
+static int qpnp_flash_regulator_operate(struct qpnp_led_data *led, bool on)
+{
+	int rc, i;
+	struct qpnp_led_data *led_array;
+	bool regulator_on = false;
+
+	led_array = dev_get_drvdata(&led->pdev->dev);
+	if (!led_array) {
+		dev_err(&led->pdev->dev, "Unable to get LED array\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < led->num_leds; i++)
+		regulator_on |= led_array[i].flash_cfg->flash_on;
+
+	if (!on)
+		goto regulator_turn_off;
+
+	if (!regulator_on && !led->flash_cfg->flash_on) {
+		for (i = 0; i < led->num_leds; i++) {
+			if (led_array[i].flash_cfg->flash_reg_get) {
+				if (led_array[i].flash_cfg->flash_wa_reg_get) {
+					rc = regulator_enable(
+						led_array[i].flash_cfg->
+							flash_wa_reg);
+					if (rc) {
+						dev_err(&led->pdev->dev, "Flash wa regulator enable failed(%d)\n",
+							rc);
+						return rc;
+					}
+				}
+
+				rc = regulator_enable(
+				       led_array[i].flash_cfg->flash_boost_reg);
+				if (rc) {
+					if (led_array[i].flash_cfg->
+							flash_wa_reg_get)
+						/*
+						 * Disable flash wa regulator
+						 * when flash boost regulator
+						 * enable fails
+						 */
+						regulator_disable(
+							led_array[i].flash_cfg->
+								flash_wa_reg);
+					dev_err(&led->pdev->dev, "Flash boost regulator enable failed(%d)\n",
+						rc);
+					return rc;
+				}
+				led->flash_cfg->flash_on = true;
+			}
+			break;
+		}
+	}
+
+	return 0;
+
+regulator_turn_off:
+	if (regulator_on && led->flash_cfg->flash_on) {
+		for (i = 0; i < led->num_leds; i++) {
+			if (led_array[i].flash_cfg->flash_reg_get) {
+				rc = qpnp_led_masked_write(led,
+					FLASH_ENABLE_CONTROL(led->base),
+					FLASH_ENABLE_MASK,
+					FLASH_DISABLE_ALL);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"Enable reg write failed(%d)\n",
+						rc);
+				}
+
+				rc = regulator_disable(
+				       led_array[i].flash_cfg->flash_boost_reg);
+				if (rc) {
+					dev_err(&led->pdev->dev, "Flash boost regulator disable failed(%d)\n",
+						rc);
+					return rc;
+				}
+				if (led_array[i].flash_cfg->flash_wa_reg_get) {
+					rc = regulator_disable(
+						led_array[i].flash_cfg->
+							flash_wa_reg);
+					if (rc) {
+						dev_err(&led->pdev->dev, "Flash_wa regulator disable failed(%d)\n",
+							rc);
+						return rc;
+					}
+				}
+				led->flash_cfg->flash_on = false;
+			}
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_torch_regulator_operate(struct qpnp_led_data *led, bool on)
+{
+	int rc;
+
+	if (!on)
+		goto regulator_turn_off;
+
+	if (!led->flash_cfg->torch_on) {
+		rc = regulator_enable(led->flash_cfg->torch_boost_reg);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Regulator enable failed(%d)\n", rc);
+				return rc;
+		}
+		led->flash_cfg->torch_on = true;
+	}
+	return 0;
+
+regulator_turn_off:
+	if (led->flash_cfg->torch_on) {
+		rc = qpnp_led_masked_write(led,	FLASH_ENABLE_CONTROL(led->base),
+				FLASH_ENABLE_MODULE_MASK, FLASH_DISABLE_ALL);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Enable reg write failed(%d)\n", rc);
+		}
+
+		rc = regulator_disable(led->flash_cfg->torch_boost_reg);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Regulator disable failed(%d)\n", rc);
+			return rc;
+		}
+		led->flash_cfg->torch_on = false;
+	}
+	return 0;
+}
+
+static int qpnp_flash_set(struct qpnp_led_data *led)
+{
+	int rc, error;
+	int val = led->cdev.brightness;
+
+	if (led->flash_cfg->torch_enable)
+		led->flash_cfg->current_prgm =
+			(val * TORCH_MAX_LEVEL / led->max_current);
+	else
+		led->flash_cfg->current_prgm =
+			(val * FLASH_MAX_LEVEL / led->max_current);
+
+	/* Set led current */
+	if (val > 0) {
+		if (led->flash_cfg->torch_enable) {
+			if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_DUAL) {
+				if (!led->flash_cfg->no_smbb_support)
+					rc = qpnp_torch_regulator_operate(led,
+									true);
+				else
+					rc = qpnp_flash_regulator_operate(led,
+									true);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Torch regulator operate failed(%d)\n",
+					rc);
+					return rc;
+				}
+			} else if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_SINGLE) {
+				rc = qpnp_flash_regulator_operate(led, true);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Flash regulator operate failed(%d)\n",
+					rc);
+					goto error_flash_set;
+				}
+			}
+
+			qpnp_led_masked_write(led, FLASH_MAX_CURR(led->base),
+				FLASH_CURRENT_MASK,
+				TORCH_MAX_LEVEL);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Max current reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			qpnp_led_masked_write(led,
+				FLASH_LED_TMR_CTRL(led->base),
+				FLASH_TMR_MASK,
+				FLASH_TMR_WATCHDOG);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Timer control reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				led->flash_cfg->current_addr,
+				FLASH_CURRENT_MASK,
+				led->flash_cfg->current_prgm);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Current reg write failed(%d)\n", rc);
+				goto error_reg_write;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				led->flash_cfg->second_addr,
+				FLASH_CURRENT_MASK,
+				led->flash_cfg->current_prgm);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"2nd Current reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			qpnp_led_masked_write(led,
+				FLASH_WATCHDOG_TMR(led->base),
+				FLASH_WATCHDOG_MASK,
+				led->flash_cfg->duration);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Max current reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_ENABLE_CONTROL(led->base),
+				FLASH_ENABLE_MASK,
+				led->flash_cfg->enable_module);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Enable reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			if (!led->flash_cfg->strobe_type)
+				led->flash_cfg->trigger_flash &=
+						~FLASH_HW_SW_STROBE_SEL_MASK;
+			else
+				led->flash_cfg->trigger_flash |=
+						FLASH_HW_SW_STROBE_SEL_MASK;
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_LED_STROBE_CTRL(led->base),
+				led->flash_cfg->trigger_flash,
+				led->flash_cfg->trigger_flash);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"LED %d strobe reg write failed(%d)\n",
+					led->id, rc);
+				goto error_reg_write;
+			}
+		} else {
+			rc = qpnp_flash_regulator_operate(led, true);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Flash regulator operate failed(%d)\n",
+					rc);
+				goto error_flash_set;
+			}
+
+			qpnp_led_masked_write(led,
+				FLASH_LED_TMR_CTRL(led->base),
+				FLASH_TMR_MASK,
+				FLASH_TMR_SAFETY);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Timer control reg write failed(%d)\n",
+					rc);
+				goto error_reg_write;
+			}
+
+			/* Set flash safety timer */
+			rc = qpnp_led_masked_write(led,
+				FLASH_SAFETY_TIMER(led->base),
+				FLASH_SAFETY_TIMER_MASK,
+				led->flash_cfg->duration);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Safety timer reg write failed(%d)\n",
+					rc);
+				goto error_flash_set;
+			}
+
+			/* Set max current */
+			rc = qpnp_led_masked_write(led,
+				FLASH_MAX_CURR(led->base), FLASH_CURRENT_MASK,
+				FLASH_MAX_LEVEL);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Max current reg write failed(%d)\n",
+					rc);
+				goto error_flash_set;
+			}
+
+			/* Set clamp current */
+			rc = qpnp_led_masked_write(led,
+				FLASH_CLAMP_CURR(led->base),
+				FLASH_CURRENT_MASK,
+				led->flash_cfg->clamp_curr);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Clamp current reg write failed(%d)\n",
+					rc);
+				goto error_flash_set;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				led->flash_cfg->current_addr,
+				FLASH_CURRENT_MASK,
+				led->flash_cfg->current_prgm);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Current reg write failed(%d)\n", rc);
+				goto error_flash_set;
+			}
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_ENABLE_CONTROL(led->base),
+				led->flash_cfg->enable_module,
+				led->flash_cfg->enable_module);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Enable reg write failed(%d)\n", rc);
+				goto error_flash_set;
+			}
+
+			/*
+			 * Add 1ms delay for bharger enter stable state
+			 */
+			usleep_range(FLASH_RAMP_UP_DELAY_US,
+				     FLASH_RAMP_UP_DELAY_US + 10);
+
+			if (!led->flash_cfg->strobe_type)
+				led->flash_cfg->trigger_flash &=
+						~FLASH_HW_SW_STROBE_SEL_MASK;
+			else
+				led->flash_cfg->trigger_flash |=
+						FLASH_HW_SW_STROBE_SEL_MASK;
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_LED_STROBE_CTRL(led->base),
+				led->flash_cfg->trigger_flash,
+				led->flash_cfg->trigger_flash);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"LED %d strobe reg write failed(%d)\n",
+				led->id, rc);
+				goto error_flash_set;
+			}
+		}
+	} else {
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_STROBE_CTRL(led->base),
+			led->flash_cfg->trigger_flash,
+			FLASH_DISABLE_ALL);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"LED %d flash write failed(%d)\n", led->id, rc);
+			if (led->flash_cfg->torch_enable)
+				goto error_torch_set;
+			else
+				goto error_flash_set;
+		}
+
+		if (led->flash_cfg->torch_enable) {
+			if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_DUAL) {
+				if (!led->flash_cfg->no_smbb_support)
+					rc = qpnp_torch_regulator_operate(led,
+									false);
+				else
+					rc = qpnp_flash_regulator_operate(led,
+									false);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"Torch regulator operate failed(%d)\n",
+						rc);
+					return rc;
+				}
+			} else if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_SINGLE) {
+				rc = qpnp_flash_regulator_operate(led, false);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+						"Flash regulator operate failed(%d)\n",
+						rc);
+					return rc;
+				}
+			}
+		} else {
+			/*
+			 * Disable module after ramp down complete for stable
+			 * behavior
+			 */
+			usleep_range(FLASH_RAMP_UP_DELAY_US,
+				     FLASH_RAMP_UP_DELAY_US + 10);
+
+			rc = qpnp_led_masked_write(led,
+				FLASH_ENABLE_CONTROL(led->base),
+				led->flash_cfg->enable_module &
+				~FLASH_ENABLE_MODULE_MASK,
+				FLASH_DISABLE_ALL);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Enable reg write failed(%d)\n", rc);
+				if (led->flash_cfg->torch_enable)
+					goto error_torch_set;
+				else
+					goto error_flash_set;
+			}
+
+			rc = qpnp_flash_regulator_operate(led, false);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Flash regulator operate failed(%d)\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
+
+	return 0;
+
+error_reg_write:
+	if (led->flash_cfg->peripheral_subtype == FLASH_SUBTYPE_SINGLE)
+		goto error_flash_set;
+
+error_torch_set:
+	if (!led->flash_cfg->no_smbb_support)
+		error = qpnp_torch_regulator_operate(led, false);
+	else
+		error = qpnp_flash_regulator_operate(led, false);
+	if (error) {
+		dev_err(&led->pdev->dev,
+			"Torch regulator operate failed(%d)\n", rc);
+		return error;
+	}
+	return rc;
+
+error_flash_set:
+	error = qpnp_flash_regulator_operate(led, false);
+	if (error) {
+		dev_err(&led->pdev->dev,
+			"Flash regulator operate failed(%d)\n", rc);
+		return error;
+	}
+	return rc;
+}
+
+static int qpnp_kpdbl_set(struct qpnp_led_data *led)
+{
+	int rc;
+	int duty_us, duty_ns, period_us;
+
+	if (led->cdev.brightness) {
+		if (!led->kpdbl_cfg->pwm_cfg->blinking)
+			led->kpdbl_cfg->pwm_cfg->mode =
+				led->kpdbl_cfg->pwm_cfg->default_mode;
+
+		if (bitmap_empty(kpdbl_leds_in_use, NUM_KPDBL_LEDS)) {
+			rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+					KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+					"Enable reg write failed(%d)\n", rc);
+				return rc;
+			}
+		}
+
+		/* On some platforms, GPLED1 channel should always be enabled
+		 * for the other GPLEDs 2/3/4 to glow. Before enabling GPLED
+		 * 2/3/4, first check if GPLED1 is already enabled. If GPLED1
+		 * channel is not enabled, then enable the GPLED1 channel but
+		 * with a 0 brightness
+		 */
+		if (!led->kpdbl_cfg->always_on &&
+			!test_bit(KPDBL_MASTER_BIT_INDEX, kpdbl_leds_in_use) &&
+						kpdbl_master) {
+			rc = pwm_config_us(kpdbl_master, 0,
+					kpdbl_master_period_us);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"pwm config failed\n");
+				return rc;
+			}
+
+			rc = pwm_enable(kpdbl_master);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"pwm enable failed\n");
+				return rc;
+			}
+			set_bit(KPDBL_MASTER_BIT_INDEX,
+						kpdbl_leds_in_use);
+		}
+
+		if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
+			period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
+			if (period_us > INT_MAX / NSEC_PER_USEC) {
+				duty_us = (period_us * led->cdev.brightness) /
+					KPDBL_MAX_LEVEL;
+				rc = pwm_config_us(
+					led->kpdbl_cfg->pwm_cfg->pwm_dev,
+					duty_us,
+					period_us);
+			} else {
+				duty_ns = ((period_us * NSEC_PER_USEC) /
+					KPDBL_MAX_LEVEL) * led->cdev.brightness;
+				rc = pwm_config(
+					led->kpdbl_cfg->pwm_cfg->pwm_dev,
+					duty_ns,
+					period_us * NSEC_PER_USEC);
+			}
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"pwm config failed\n");
+				return rc;
+			}
+		}
+
+		rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev, "pwm enable failed\n");
+			return rc;
+		}
+
+		set_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use);
+
+		/* is_kpdbl_master_turn_on will be set to true when GPLED1
+		 * channel is enabled and has a valid brightness value
+		 */
+		if (led->kpdbl_cfg->always_on)
+			is_kpdbl_master_turn_on = true;
+
+	} else {
+		led->kpdbl_cfg->pwm_cfg->mode =
+			led->kpdbl_cfg->pwm_cfg->default_mode;
+
+		/* Before disabling GPLED1, check if any other GPLED 2/3/4 is
+		 * on. If any of the other GPLED 2/3/4 is on, then have the
+		 * GPLED1 channel enabled with 0 brightness.
+		 */
+		if (led->kpdbl_cfg->always_on) {
+			if (bitmap_weight(kpdbl_leds_in_use,
+						NUM_KPDBL_LEDS) > 1) {
+				rc = pwm_config_us(
+					led->kpdbl_cfg->pwm_cfg->pwm_dev, 0,
+					led->kpdbl_cfg->pwm_cfg->pwm_period_us);
+				if (rc < 0) {
+					dev_err(&led->pdev->dev,
+						"pwm config failed\n");
+					return rc;
+				}
+
+				rc = pwm_enable(led->kpdbl_cfg->pwm_cfg->
+							pwm_dev);
+				if (rc < 0) {
+					dev_err(&led->pdev->dev,
+						"pwm enable failed\n");
+					return rc;
+				}
+			} else {
+				if (kpdbl_master) {
+					pwm_disable(kpdbl_master);
+					clear_bit(KPDBL_MASTER_BIT_INDEX,
+						kpdbl_leds_in_use);
+					rc = qpnp_led_masked_write(
+						led, KPDBL_ENABLE(led->base),
+						KPDBL_MODULE_EN_MASK,
+						KPDBL_MODULE_DIS);
+					if (rc) {
+						dev_err(&led->pdev->dev, "Failed to write led enable reg\n");
+						return rc;
+					}
+				}
+			}
+			is_kpdbl_master_turn_on = false;
+		} else {
+			pwm_disable(led->kpdbl_cfg->pwm_cfg->pwm_dev);
+			clear_bit(led->kpdbl_cfg->row_id, kpdbl_leds_in_use);
+			if (bitmap_weight(kpdbl_leds_in_use,
+				NUM_KPDBL_LEDS) == 1 && kpdbl_master &&
+						!is_kpdbl_master_turn_on) {
+				pwm_disable(kpdbl_master);
+				clear_bit(KPDBL_MASTER_BIT_INDEX,
+					kpdbl_leds_in_use);
+				rc = qpnp_led_masked_write(
+					led, KPDBL_ENABLE(led->base),
+					KPDBL_MODULE_EN_MASK, KPDBL_MODULE_DIS);
+				if (rc) {
+					dev_err(&led->pdev->dev,
+					"Failed to write led enable reg\n");
+					return rc;
+				}
+				is_kpdbl_master_turn_on = false;
+			}
+		}
+	}
+
+	led->kpdbl_cfg->pwm_cfg->blinking = false;
+
+	qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
+
+	return 0;
+}
+
+static int qpnp_rgb_set(struct qpnp_led_data *led)
+{
+	int rc;
+	int duty_us, duty_ns, period_us;
+
+	if (led->cdev.brightness) {
+		if (!led->rgb_cfg->pwm_cfg->blinking)
+			led->rgb_cfg->pwm_cfg->mode =
+				led->rgb_cfg->pwm_cfg->default_mode;
+		if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+			period_us = led->rgb_cfg->pwm_cfg->pwm_period_us;
+			if (period_us > INT_MAX / NSEC_PER_USEC) {
+				duty_us = (period_us * led->cdev.brightness) /
+					LED_FULL;
+				rc = pwm_config_us(
+					led->rgb_cfg->pwm_cfg->pwm_dev,
+					duty_us,
+					period_us);
+			} else {
+				duty_ns = ((period_us * NSEC_PER_USEC) /
+					LED_FULL) * led->cdev.brightness;
+				rc = pwm_config(
+					led->rgb_cfg->pwm_cfg->pwm_dev,
+					duty_ns,
+					period_us * NSEC_PER_USEC);
+			}
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"pwm config failed\n");
+				return rc;
+			}
+		}
+		rc = qpnp_led_masked_write(led,
+			RGB_LED_EN_CTL(led->base),
+			led->rgb_cfg->enable, led->rgb_cfg->enable);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to write led enable reg\n");
+			return rc;
+		}
+
+		if (led->rgb_cfg->pwm_cfg->pwm_enabled) {
+			pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
+			led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
+		}
+
+		rc = pwm_enable(led->rgb_cfg->pwm_cfg->pwm_dev);
+		if (!rc)
+			led->rgb_cfg->pwm_cfg->pwm_enabled = 1;
+	} else {
+		led->rgb_cfg->pwm_cfg->mode =
+			led->rgb_cfg->pwm_cfg->default_mode;
+		pwm_disable(led->rgb_cfg->pwm_cfg->pwm_dev);
+		led->rgb_cfg->pwm_cfg->pwm_enabled = 0;
+		rc = qpnp_led_masked_write(led,
+			RGB_LED_EN_CTL(led->base),
+			led->rgb_cfg->enable, RGB_LED_DISABLE);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to write led enable reg\n");
+			return rc;
+		}
+	}
+
+	led->rgb_cfg->pwm_cfg->blinking = false;
+	qpnp_dump_regs(led, rgb_pwm_debug_regs, ARRAY_SIZE(rgb_pwm_debug_regs));
+
+	return 0;
+}
+
+static void qpnp_led_set(struct led_classdev *led_cdev,
+				enum led_brightness value)
+{
+	struct qpnp_led_data *led;
+
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+	if (value < LED_OFF) {
+		dev_err(&led->pdev->dev, "Invalid brightness value\n");
+		return;
+	}
+
+	if (value > led->cdev.max_brightness)
+		value = led->cdev.max_brightness;
+
+	led->cdev.brightness = value;
+	if (led->in_order_command_processing)
+		queue_work(led->workqueue, &led->work);
+	else
+		schedule_work(&led->work);
+}
+
+static void __qpnp_led_work(struct qpnp_led_data *led,
+				enum led_brightness value)
+{
+	int rc;
+
+	if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
+		mutex_lock(&flash_lock);
+	else
+		mutex_lock(&led->lock);
+
+	switch (led->id) {
+	case QPNP_ID_WLED:
+		rc = qpnp_wled_set(led);
+		if (rc < 0)
+			dev_err(&led->pdev->dev,
+				"WLED set brightness failed (%d)\n", rc);
+		break;
+	case QPNP_ID_FLASH1_LED0:
+	case QPNP_ID_FLASH1_LED1:
+		rc = qpnp_flash_set(led);
+		if (rc < 0)
+			dev_err(&led->pdev->dev,
+				"FLASH set brightness failed (%d)\n", rc);
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		rc = qpnp_rgb_set(led);
+		if (rc < 0)
+			dev_err(&led->pdev->dev,
+				"RGB set brightness failed (%d)\n", rc);
+		break;
+	case QPNP_ID_LED_MPP:
+		rc = qpnp_mpp_set(led);
+		if (rc < 0)
+			dev_err(&led->pdev->dev,
+					"MPP set brightness failed (%d)\n", rc);
+		break;
+	case QPNP_ID_LED_GPIO:
+		rc = qpnp_gpio_set(led);
+		if (rc < 0)
+			dev_err(&led->pdev->dev,
+					"GPIO set brightness failed (%d)\n",
+					rc);
+		break;
+	case QPNP_ID_KPDBL:
+		rc = qpnp_kpdbl_set(led);
+		if (rc < 0)
+			dev_err(&led->pdev->dev,
+				"KPDBL set brightness failed (%d)\n", rc);
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid LED(%d)\n", led->id);
+		break;
+	}
+	if (led->id == QPNP_ID_FLASH1_LED0 || led->id == QPNP_ID_FLASH1_LED1)
+		mutex_unlock(&flash_lock);
+	else
+		mutex_unlock(&led->lock);
+
+}
+
+static void qpnp_led_work(struct work_struct *work)
+{
+	struct qpnp_led_data *led = container_of(work,
+					struct qpnp_led_data, work);
+
+	__qpnp_led_work(led, led->cdev.brightness);
+}
+
+static int qpnp_led_set_max_brightness(struct qpnp_led_data *led)
+{
+	switch (led->id) {
+	case QPNP_ID_WLED:
+		led->cdev.max_brightness = WLED_MAX_LEVEL;
+		break;
+	case QPNP_ID_FLASH1_LED0:
+	case QPNP_ID_FLASH1_LED1:
+		led->cdev.max_brightness = led->max_current;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		led->cdev.max_brightness = RGB_MAX_LEVEL;
+		break;
+	case QPNP_ID_LED_MPP:
+		if (led->mpp_cfg->pwm_mode == MANUAL_MODE)
+			led->cdev.max_brightness = led->max_current;
+		else
+			led->cdev.max_brightness = MPP_MAX_LEVEL;
+		break;
+	case QPNP_ID_LED_GPIO:
+			led->cdev.max_brightness = led->max_current;
+		break;
+	case QPNP_ID_KPDBL:
+		led->cdev.max_brightness = KPDBL_MAX_LEVEL;
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid LED(%d)\n", led->id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static enum led_brightness qpnp_led_get(struct led_classdev *led_cdev)
+{
+	struct qpnp_led_data *led;
+
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	return led->cdev.brightness;
+}
+
+static void qpnp_led_turn_off_delayed(struct work_struct *work)
+{
+	struct delayed_work *dwork = to_delayed_work(work);
+	struct qpnp_led_data *led
+		= container_of(dwork, struct qpnp_led_data, dwork);
+
+	led->cdev.brightness = LED_OFF;
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+}
+
+static void qpnp_led_turn_off(struct qpnp_led_data *led)
+{
+	INIT_DELAYED_WORK(&led->dwork, qpnp_led_turn_off_delayed);
+	schedule_delayed_work(&led->dwork,
+		msecs_to_jiffies(led->turn_off_delay_ms));
+}
+
+static int qpnp_wled_init(struct qpnp_led_data *led)
+{
+	int rc, i;
+	u8 num_wled_strings, val = 0;
+
+	num_wled_strings = led->wled_cfg->num_strings;
+
+	/* verify ranges */
+	if (led->wled_cfg->ovp_val > WLED_OVP_27V) {
+		dev_err(&led->pdev->dev, "Invalid ovp value\n");
+		return -EINVAL;
+	}
+
+	if (led->wled_cfg->boost_curr_lim > WLED_CURR_LIMIT_1680mA) {
+		dev_err(&led->pdev->dev, "Invalid boost current limit\n");
+		return -EINVAL;
+	}
+
+	if (led->wled_cfg->cp_select > WLED_CP_SELECT_MAX) {
+		dev_err(&led->pdev->dev, "Invalid pole capacitance\n");
+		return -EINVAL;
+	}
+
+	if (led->max_current > WLED_MAX_CURR) {
+		dev_err(&led->pdev->dev, "Invalid max current\n");
+		return -EINVAL;
+	}
+
+	if ((led->wled_cfg->ctrl_delay_us % WLED_CTL_DLY_STEP) ||
+		(led->wled_cfg->ctrl_delay_us > WLED_CTL_DLY_MAX)) {
+		dev_err(&led->pdev->dev, "Invalid control delay\n");
+		return -EINVAL;
+	}
+
+	/* program over voltage protection threshold */
+	rc = qpnp_led_masked_write(led, WLED_OVP_CFG_REG(led->base),
+		WLED_OVP_VAL_MASK,
+		(led->wled_cfg->ovp_val << WLED_OVP_VAL_BIT_SHFT));
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"WLED OVP reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* program current boost limit */
+	rc = qpnp_led_masked_write(led, WLED_BOOST_LIMIT_REG(led->base),
+		WLED_BOOST_LIMIT_MASK, led->wled_cfg->boost_curr_lim);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"WLED boost limit reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* program output feedback */
+	rc = qpnp_led_masked_write(led, WLED_FDBCK_CTRL_REG(led->base),
+		WLED_OP_FDBCK_MASK,
+		(led->wled_cfg->op_fdbck << WLED_OP_FDBCK_BIT_SHFT));
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"WLED fdbck ctrl reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* program switch frequency */
+	rc = qpnp_led_masked_write(led,
+		WLED_SWITCHING_FREQ_REG(led->base),
+		WLED_SWITCH_FREQ_MASK, led->wled_cfg->switch_freq);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"WLED switch freq reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* program current sink */
+	if (led->wled_cfg->cs_out_en) {
+		for (i = 0; i < led->wled_cfg->num_strings; i++)
+			val |= 1 << i;
+		rc = qpnp_led_masked_write(led, WLED_CURR_SINK_REG(led->base),
+			WLED_CURR_SINK_MASK, (val << WLED_CURR_SINK_SHFT));
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED curr sink reg write failed(%d)\n", rc);
+			return rc;
+		}
+	}
+
+	/* program high pole capacitance */
+	rc = qpnp_led_masked_write(led, WLED_HIGH_POLE_CAP_REG(led->base),
+		WLED_CP_SELECT_MASK, led->wled_cfg->cp_select);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+				"WLED pole cap reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* program modulator, current mod src and cabc */
+	for (i = 0; i < num_wled_strings; i++) {
+		rc = qpnp_led_masked_write(led, WLED_MOD_EN_REG(led->base, i),
+			WLED_NO_MASK, WLED_EN_MASK);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED mod enable reg write failed(%d)\n", rc);
+			return rc;
+		}
+
+		if (led->wled_cfg->dig_mod_gen_en) {
+			rc = qpnp_led_masked_write(led,
+				WLED_MOD_SRC_SEL_REG(led->base, i),
+				WLED_NO_MASK, WLED_USE_EXT_GEN_MOD_SRC);
+			if (rc) {
+				dev_err(&led->pdev->dev,
+				"WLED dig mod en reg write failed(%d)\n", rc);
+			}
+		}
+
+		rc = qpnp_led_masked_write(led,
+			WLED_FULL_SCALE_REG(led->base, i), WLED_MAX_CURR_MASK,
+			(u8)led->max_current);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"WLED max current reg write failed(%d)\n", rc);
+			return rc;
+		}
+
+	}
+
+	/* Reset WLED enable register */
+	rc = qpnp_led_masked_write(led, WLED_MOD_CTRL_REG(led->base),
+		WLED_8_BIT_MASK, WLED_BOOST_OFF);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"WLED write ctrl reg failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* dump wled registers */
+	qpnp_dump_regs(led, wled_debug_regs, ARRAY_SIZE(wled_debug_regs));
+
+	return 0;
+}
+
+static ssize_t led_mode_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	unsigned long state;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = -EINVAL;
+
+	ret = kstrtoul(buf, 10, &state);
+	if (ret)
+		return ret;
+
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	/* '1' to enable torch mode; '0' to switch to flash mode */
+	if (state == 1)
+		led->flash_cfg->torch_enable = true;
+	else
+		led->flash_cfg->torch_enable = false;
+
+	return count;
+}
+
+static ssize_t led_strobe_type_store(struct device *dev,
+			struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	unsigned long state;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = -EINVAL;
+
+	ret = kstrtoul(buf, 10, &state);
+	if (ret)
+		return ret;
+
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	/* '0' for sw strobe; '1' for hw strobe */
+	if (state == 1)
+		led->flash_cfg->strobe_type = 1;
+	else
+		led->flash_cfg->strobe_type = 0;
+
+	return count;
+}
+
+static int qpnp_pwm_init(struct pwm_config_data *pwm_cfg,
+					struct platform_device *pdev,
+					const char *name)
+{
+	int rc, start_idx, idx_len, lut_max_size;
+
+	if (pwm_cfg->pwm_dev) {
+		if (pwm_cfg->mode == LPG_MODE) {
+			start_idx =
+			pwm_cfg->duty_cycles->start_idx;
+			idx_len =
+			pwm_cfg->duty_cycles->num_duty_pcts;
+
+			if (strnstr(name, "kpdbl", sizeof("kpdbl")))
+				lut_max_size = PWM_GPLED_LUT_MAX_SIZE;
+			else
+				lut_max_size = PWM_LUT_MAX_SIZE;
+
+			if (idx_len >= lut_max_size && start_idx) {
+				dev_err(&pdev->dev,
+					"Wrong LUT size or index\n");
+				return -EINVAL;
+			}
+
+			if ((start_idx + idx_len) > lut_max_size) {
+				dev_err(&pdev->dev, "Exceed LUT limit\n");
+				return -EINVAL;
+			}
+			rc = pwm_lut_config(pwm_cfg->pwm_dev,
+				pwm_cfg->pwm_period_us,
+				pwm_cfg->duty_cycles->duty_pcts,
+				pwm_cfg->lut_params);
+			if (rc < 0) {
+				dev_err(&pdev->dev, "Failed to configure pwm LUT\n");
+				return rc;
+			}
+		}
+	} else {
+		dev_err(&pdev->dev, "Invalid PWM device\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static ssize_t pwm_us_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	u32 pwm_us;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u32 previous_pwm_us;
+	struct pwm_config_data *pwm_cfg;
+
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	ret = kstrtou32(buf, 10, &pwm_us);
+	if (ret)
+		return ret;
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid LED id type for pwm_us\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	previous_pwm_us = pwm_cfg->pwm_period_us;
+
+	pwm_cfg->pwm_period_us = pwm_us;
+	pwm_free(pwm_cfg->pwm_dev);
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret) {
+		pwm_cfg->pwm_period_us = previous_pwm_us;
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		dev_err(&led->pdev->dev,
+			"Failed to initialize pwm with new pwm_us value\n");
+		return ret;
+	}
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+}
+
+static ssize_t pause_lo_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	u32 pause_lo;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u32 previous_pause_lo;
+	struct pwm_config_data *pwm_cfg;
+
+	ret = kstrtou32(buf, 10, &pause_lo);
+	if (ret)
+		return ret;
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		break;
+	default:
+		dev_err(&led->pdev->dev,
+			"Invalid LED id type for pause lo\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	previous_pause_lo = pwm_cfg->lut_params.lut_pause_lo;
+
+	pwm_free(pwm_cfg->pwm_dev);
+	pwm_cfg->lut_params.lut_pause_lo = pause_lo;
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret) {
+		pwm_cfg->lut_params.lut_pause_lo = previous_pause_lo;
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		dev_err(&led->pdev->dev,
+			"Failed to initialize pwm with new pause lo value\n");
+		return ret;
+	}
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+}
+
+static ssize_t pause_hi_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	u32 pause_hi;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u32 previous_pause_hi;
+	struct pwm_config_data *pwm_cfg;
+
+	ret = kstrtou32(buf, 10, &pause_hi);
+	if (ret)
+		return ret;
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		break;
+	default:
+		dev_err(&led->pdev->dev,
+			"Invalid LED id type for pause hi\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	previous_pause_hi = pwm_cfg->lut_params.lut_pause_hi;
+
+	pwm_free(pwm_cfg->pwm_dev);
+	pwm_cfg->lut_params.lut_pause_hi = pause_hi;
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret) {
+		pwm_cfg->lut_params.lut_pause_hi = previous_pause_hi;
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		dev_err(&led->pdev->dev,
+			"Failed to initialize pwm with new pause hi value\n");
+		return ret;
+	}
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+}
+
+static ssize_t start_idx_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	u32 start_idx;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u32 previous_start_idx;
+	struct pwm_config_data *pwm_cfg;
+
+	ret = kstrtou32(buf, 10, &start_idx);
+	if (ret)
+		return ret;
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		break;
+	default:
+		dev_err(&led->pdev->dev,
+			"Invalid LED id type for start idx\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	previous_start_idx = pwm_cfg->duty_cycles->start_idx;
+	pwm_cfg->duty_cycles->start_idx = start_idx;
+	pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
+	pwm_free(pwm_cfg->pwm_dev);
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret) {
+		pwm_cfg->duty_cycles->start_idx = previous_start_idx;
+		pwm_cfg->lut_params.start_idx = pwm_cfg->duty_cycles->start_idx;
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		dev_err(&led->pdev->dev,
+			"Failed to initialize pwm with new start idx value\n");
+		return ret;
+	}
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+}
+
+static ssize_t ramp_step_ms_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	u32 ramp_step_ms;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u32 previous_ramp_step_ms;
+	struct pwm_config_data *pwm_cfg;
+
+	ret = kstrtou32(buf, 10, &ramp_step_ms);
+	if (ret)
+		return ret;
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		break;
+	default:
+		dev_err(&led->pdev->dev,
+			"Invalid LED id type for ramp step\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	previous_ramp_step_ms = pwm_cfg->lut_params.ramp_step_ms;
+
+	pwm_free(pwm_cfg->pwm_dev);
+	pwm_cfg->lut_params.ramp_step_ms = ramp_step_ms;
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret) {
+		pwm_cfg->lut_params.ramp_step_ms = previous_ramp_step_ms;
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		dev_err(&led->pdev->dev,
+			"Failed to initialize pwm with new ramp step value\n");
+		return ret;
+	}
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+}
+
+static ssize_t lut_flags_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	u32 lut_flags;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret;
+	u32 previous_lut_flags;
+	struct pwm_config_data *pwm_cfg;
+
+	ret = kstrtou32(buf, 10, &lut_flags);
+	if (ret)
+		return ret;
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		break;
+	default:
+		dev_err(&led->pdev->dev,
+			"Invalid LED id type for lut flags\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	previous_lut_flags = pwm_cfg->lut_params.flags;
+
+	pwm_free(pwm_cfg->pwm_dev);
+	pwm_cfg->lut_params.flags = lut_flags;
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret) {
+		pwm_cfg->lut_params.flags = previous_lut_flags;
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		qpnp_led_set(&led->cdev, led->cdev.brightness);
+		dev_err(&led->pdev->dev,
+			"Failed to initialize pwm with new lut flags value\n");
+		return ret;
+	}
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+}
+
+static ssize_t duty_pcts_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	int num_duty_pcts = 0;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	char *buffer;
+	ssize_t ret;
+	int i = 0;
+	int max_duty_pcts;
+	struct pwm_config_data *pwm_cfg;
+	u32 previous_num_duty_pcts;
+	int value;
+	int *previous_duty_pcts;
+
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		pwm_cfg = led->mpp_cfg->pwm_cfg;
+		max_duty_pcts = PWM_LUT_MAX_SIZE;
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		pwm_cfg = led->rgb_cfg->pwm_cfg;
+		max_duty_pcts = PWM_LUT_MAX_SIZE;
+		break;
+	case QPNP_ID_KPDBL:
+		pwm_cfg = led->kpdbl_cfg->pwm_cfg;
+		max_duty_pcts = PWM_GPLED_LUT_MAX_SIZE;
+		break;
+	default:
+		dev_err(&led->pdev->dev,
+			"Invalid LED id type for duty pcts\n");
+		return -EINVAL;
+	}
+
+	if (pwm_cfg->mode == LPG_MODE)
+		pwm_cfg->blinking = true;
+
+	buffer = (char *)buf;
+
+	for (i = 0; i < max_duty_pcts; i++) {
+		if (buffer == NULL)
+			break;
+		ret = sscanf((const char *)buffer, "%u,%s", &value, buffer);
+		pwm_cfg->old_duty_pcts[i] = value;
+		num_duty_pcts++;
+		if (ret <= 1)
+			break;
+	}
+
+	if (num_duty_pcts >= max_duty_pcts) {
+		dev_err(&led->pdev->dev,
+			"Number of duty pcts given exceeds max (%d)\n",
+			max_duty_pcts);
+		return -EINVAL;
+	}
+
+	previous_num_duty_pcts = pwm_cfg->duty_cycles->num_duty_pcts;
+	previous_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
+
+	pwm_cfg->duty_cycles->num_duty_pcts = num_duty_pcts;
+	pwm_cfg->duty_cycles->duty_pcts = pwm_cfg->old_duty_pcts;
+	pwm_cfg->old_duty_pcts = previous_duty_pcts;
+	pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
+
+	pwm_free(pwm_cfg->pwm_dev);
+	ret = qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	if (ret)
+		goto restore;
+
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return count;
+
+restore:
+	dev_err(&led->pdev->dev,
+		"Failed to initialize pwm with new duty pcts value\n");
+	pwm_cfg->duty_cycles->num_duty_pcts = previous_num_duty_pcts;
+	pwm_cfg->old_duty_pcts = pwm_cfg->duty_cycles->duty_pcts;
+	pwm_cfg->duty_cycles->duty_pcts = previous_duty_pcts;
+	pwm_cfg->lut_params.idx_len = pwm_cfg->duty_cycles->num_duty_pcts;
+	pwm_free(pwm_cfg->pwm_dev);
+	qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+	qpnp_led_set(&led->cdev, led->cdev.brightness);
+	return ret;
+}
+
+static void led_blink(struct qpnp_led_data *led,
+			struct pwm_config_data *pwm_cfg)
+{
+	int rc;
+
+	flush_work(&led->work);
+	mutex_lock(&led->lock);
+	if (pwm_cfg->use_blink) {
+		if (led->cdev.brightness) {
+			pwm_cfg->blinking = true;
+			if (led->id == QPNP_ID_LED_MPP)
+				led->mpp_cfg->pwm_mode = LPG_MODE;
+			else if (led->id == QPNP_ID_KPDBL)
+				led->kpdbl_cfg->pwm_mode = LPG_MODE;
+			pwm_cfg->mode = LPG_MODE;
+		} else {
+			pwm_cfg->blinking = false;
+			pwm_cfg->mode = pwm_cfg->default_mode;
+			if (led->id == QPNP_ID_LED_MPP)
+				led->mpp_cfg->pwm_mode = pwm_cfg->default_mode;
+			else if (led->id == QPNP_ID_KPDBL)
+				led->kpdbl_cfg->pwm_mode =
+						pwm_cfg->default_mode;
+		}
+		pwm_free(pwm_cfg->pwm_dev);
+		qpnp_pwm_init(pwm_cfg, led->pdev, led->cdev.name);
+		if (led->id == QPNP_ID_RGB_RED || led->id == QPNP_ID_RGB_GREEN
+				|| led->id == QPNP_ID_RGB_BLUE) {
+			rc = qpnp_rgb_set(led);
+			if (rc < 0)
+				dev_err(&led->pdev->dev,
+				"RGB set brightness failed (%d)\n", rc);
+		} else if (led->id == QPNP_ID_LED_MPP) {
+			rc = qpnp_mpp_set(led);
+			if (rc < 0)
+				dev_err(&led->pdev->dev,
+				"MPP set brightness failed (%d)\n", rc);
+		} else if (led->id == QPNP_ID_KPDBL) {
+			rc = qpnp_kpdbl_set(led);
+			if (rc < 0)
+				dev_err(&led->pdev->dev,
+				"KPDBL set brightness failed (%d)\n", rc);
+		}
+	}
+	mutex_unlock(&led->lock);
+}
+
+static ssize_t blink_store(struct device *dev,
+	struct device_attribute *attr,
+	const char *buf, size_t count)
+{
+	struct qpnp_led_data *led;
+	unsigned long blinking;
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	ssize_t ret = -EINVAL;
+
+	ret = kstrtoul(buf, 10, &blinking);
+	if (ret)
+		return ret;
+	led = container_of(led_cdev, struct qpnp_led_data, cdev);
+	led->cdev.brightness = blinking ? led->cdev.max_brightness : 0;
+
+	switch (led->id) {
+	case QPNP_ID_LED_MPP:
+		led_blink(led, led->mpp_cfg->pwm_cfg);
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		led_blink(led, led->rgb_cfg->pwm_cfg);
+		break;
+	case QPNP_ID_KPDBL:
+		led_blink(led, led->kpdbl_cfg->pwm_cfg);
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid LED id type for blink\n");
+		return -EINVAL;
+	}
+	return count;
+}
+
+static DEVICE_ATTR(led_mode, 0664, NULL, led_mode_store);
+static DEVICE_ATTR(strobe, 0664, NULL, led_strobe_type_store);
+static DEVICE_ATTR(pwm_us, 0664, NULL, pwm_us_store);
+static DEVICE_ATTR(pause_lo, 0664, NULL, pause_lo_store);
+static DEVICE_ATTR(pause_hi, 0664, NULL, pause_hi_store);
+static DEVICE_ATTR(start_idx, 0664, NULL, start_idx_store);
+static DEVICE_ATTR(ramp_step_ms, 0664, NULL, ramp_step_ms_store);
+static DEVICE_ATTR(lut_flags, 0664, NULL, lut_flags_store);
+static DEVICE_ATTR(duty_pcts, 0664, NULL, duty_pcts_store);
+static DEVICE_ATTR(blink, 0664, NULL, blink_store);
+
+static struct attribute *led_attrs[] = {
+	&dev_attr_led_mode.attr,
+	&dev_attr_strobe.attr,
+	NULL
+};
+
+static const struct attribute_group led_attr_group = {
+	.attrs = led_attrs,
+};
+
+static struct attribute *pwm_attrs[] = {
+	&dev_attr_pwm_us.attr,
+	NULL
+};
+
+static struct attribute *lpg_attrs[] = {
+	&dev_attr_pause_lo.attr,
+	&dev_attr_pause_hi.attr,
+	&dev_attr_start_idx.attr,
+	&dev_attr_ramp_step_ms.attr,
+	&dev_attr_lut_flags.attr,
+	&dev_attr_duty_pcts.attr,
+	NULL
+};
+
+static struct attribute *blink_attrs[] = {
+	&dev_attr_blink.attr,
+	NULL
+};
+
+static const struct attribute_group pwm_attr_group = {
+	.attrs = pwm_attrs,
+};
+
+static const struct attribute_group lpg_attr_group = {
+	.attrs = lpg_attrs,
+};
+
+static const struct attribute_group blink_attr_group = {
+	.attrs = blink_attrs,
+};
+
+static int qpnp_flash_init(struct qpnp_led_data *led)
+{
+	int rc;
+
+	led->flash_cfg->flash_on = false;
+
+	rc = qpnp_led_masked_write(led,
+		FLASH_LED_STROBE_CTRL(led->base),
+		FLASH_STROBE_MASK, FLASH_DISABLE_ALL);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"LED %d flash write failed(%d)\n", led->id, rc);
+		return rc;
+	}
+
+	/* Disable flash LED module */
+	rc = qpnp_led_masked_write(led, FLASH_ENABLE_CONTROL(led->base),
+		FLASH_ENABLE_MASK, FLASH_DISABLE_ALL);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Enable reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	if (led->flash_cfg->torch_enable)
+		return 0;
+
+	/* Set headroom */
+	rc = qpnp_led_masked_write(led, FLASH_HEADROOM(led->base),
+		FLASH_HEADROOM_MASK, led->flash_cfg->headroom);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Headroom reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set startup delay */
+	rc = qpnp_led_masked_write(led,
+		FLASH_STARTUP_DELAY(led->base), FLASH_STARTUP_DLY_MASK,
+		led->flash_cfg->startup_dly);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Startup delay reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set timer control - safety or watchdog */
+	if (led->flash_cfg->safety_timer) {
+		rc = qpnp_led_masked_write(led,
+			FLASH_LED_TMR_CTRL(led->base),
+			FLASH_TMR_MASK, FLASH_TMR_SAFETY);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"LED timer ctrl reg write failed(%d)\n",
+				rc);
+			return rc;
+		}
+	}
+
+	/* Set Vreg force */
+	if (led->flash_cfg->vreg_ok)
+		rc = qpnp_led_masked_write(led,	FLASH_VREG_OK_FORCE(led->base),
+			FLASH_VREG_MASK, FLASH_SW_VREG_OK);
+	else
+		rc = qpnp_led_masked_write(led, FLASH_VREG_OK_FORCE(led->base),
+			FLASH_VREG_MASK, FLASH_HW_VREG_OK);
+
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Vreg OK reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set self fault check */
+	rc = qpnp_led_masked_write(led, FLASH_FAULT_DETECT(led->base),
+		FLASH_FAULT_DETECT_MASK, FLASH_SELFCHECK_ENABLE);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Fault detect reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set mask enable */
+	rc = qpnp_led_masked_write(led, FLASH_MASK_ENABLE(led->base),
+		FLASH_MASK_REG_MASK, FLASH_MASK_1);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Mask enable reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	/* Set current ramp */
+	rc = qpnp_led_masked_write(led, FLASH_CURRENT_RAMP(led->base),
+		FLASH_CURRENT_RAMP_MASK, FLASH_RAMP_STEP_27US);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Current ramp reg write failed(%d)\n", rc);
+		return rc;
+	}
+
+	led->flash_cfg->strobe_type = 0;
+
+	/* dump flash registers */
+	qpnp_dump_regs(led, flash_debug_regs, ARRAY_SIZE(flash_debug_regs));
+
+	return 0;
+}
+
+static int qpnp_kpdbl_init(struct qpnp_led_data *led)
+{
+	int rc;
+	uint val;
+
+	/* select row source - vbst or vph */
+	rc = regmap_read(led->regmap, KPDBL_ROW_SRC_SEL(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read from addr=%x, rc(%d)\n",
+			KPDBL_ROW_SRC_SEL(led->base), rc);
+		return rc;
+	}
+
+	if (led->kpdbl_cfg->row_src_vbst)
+		val |= 1 << led->kpdbl_cfg->row_id;
+	else
+		val &= ~(1 << led->kpdbl_cfg->row_id);
+
+	rc = regmap_write(led->regmap, KPDBL_ROW_SRC_SEL(led->base), val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read from addr=%x, rc(%d)\n",
+			KPDBL_ROW_SRC_SEL(led->base), rc);
+		return rc;
+	}
+
+	/* row source enable */
+	rc = regmap_read(led->regmap, KPDBL_ROW_SRC(led->base), &val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read from addr=%x, rc(%d)\n",
+			KPDBL_ROW_SRC(led->base), rc);
+		return rc;
+	}
+
+	if (led->kpdbl_cfg->row_src_en)
+		val |= KPDBL_ROW_SCAN_EN_MASK | (1 << led->kpdbl_cfg->row_id);
+	else
+		val &= ~(1 << led->kpdbl_cfg->row_id);
+
+	rc = regmap_write(led->regmap, KPDBL_ROW_SRC(led->base), val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to write to addr=%x, rc(%d)\n",
+			KPDBL_ROW_SRC(led->base), rc);
+		return rc;
+	}
+
+	/* enable module */
+	rc = qpnp_led_masked_write(led, KPDBL_ENABLE(led->base),
+		KPDBL_MODULE_EN_MASK, KPDBL_MODULE_EN);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Enable module write failed(%d)\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_pwm_init(led->kpdbl_cfg->pwm_cfg, led->pdev,
+				led->cdev.name);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Failed to initialize pwm\n");
+		return rc;
+	}
+
+	if (led->kpdbl_cfg->always_on) {
+		kpdbl_master = led->kpdbl_cfg->pwm_cfg->pwm_dev;
+		kpdbl_master_period_us = led->kpdbl_cfg->pwm_cfg->pwm_period_us;
+	}
+
+	/* dump kpdbl registers */
+	qpnp_dump_regs(led, kpdbl_debug_regs, ARRAY_SIZE(kpdbl_debug_regs));
+
+	return 0;
+}
+
+static int qpnp_rgb_init(struct qpnp_led_data *led)
+{
+	int rc;
+
+	rc = qpnp_led_masked_write(led, RGB_LED_SRC_SEL(led->base),
+		RGB_LED_SRC_MASK, RGB_LED_SOURCE_VPH_PWR);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Failed to write led source select register\n");
+		return rc;
+	}
+
+	rc = qpnp_pwm_init(led->rgb_cfg->pwm_cfg, led->pdev, led->cdev.name);
+	if (rc) {
+		dev_err(&led->pdev->dev, "Failed to initialize pwm\n");
+		return rc;
+	}
+	/* Initialize led for use in auto trickle charging mode */
+	rc = qpnp_led_masked_write(led, RGB_LED_ATC_CTL(led->base),
+		led->rgb_cfg->enable, led->rgb_cfg->enable);
+
+	return 0;
+}
+
+static int qpnp_mpp_init(struct qpnp_led_data *led)
+{
+	int rc;
+	u8 val;
+
+
+	if (led->max_current < LED_MPP_CURRENT_MIN ||
+		led->max_current > LED_MPP_CURRENT_MAX) {
+		dev_err(&led->pdev->dev,
+			"max current for mpp is not valid\n");
+		return -EINVAL;
+	}
+
+	val = (led->mpp_cfg->current_setting / LED_MPP_CURRENT_PER_SETTING) - 1;
+
+	if (val < 0)
+		val = 0;
+
+	rc = qpnp_led_masked_write(led, LED_MPP_VIN_CTRL(led->base),
+		LED_MPP_VIN_MASK, led->mpp_cfg->vin_ctrl);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Failed to write led vin control reg\n");
+		return rc;
+	}
+
+	rc = qpnp_led_masked_write(led, LED_MPP_SINK_CTRL(led->base),
+		LED_MPP_SINK_MASK, val);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Failed to write sink control reg\n");
+		return rc;
+	}
+
+	if (led->mpp_cfg->pwm_mode != MANUAL_MODE) {
+		rc = qpnp_pwm_init(led->mpp_cfg->pwm_cfg, led->pdev,
+					led->cdev.name);
+		if (rc) {
+			dev_err(&led->pdev->dev,
+				"Failed to initialize pwm\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_gpio_init(struct qpnp_led_data *led)
+{
+	int rc;
+
+	rc = qpnp_led_masked_write(led, LED_GPIO_VIN_CTRL(led->base),
+		LED_GPIO_VIN_MASK, led->gpio_cfg->vin_ctrl);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Failed to write led vin control reg\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_led_initialize(struct qpnp_led_data *led)
+{
+	int rc = 0;
+
+	switch (led->id) {
+	case QPNP_ID_WLED:
+		rc = qpnp_wled_init(led);
+		if (rc)
+			dev_err(&led->pdev->dev,
+				"WLED initialize failed(%d)\n", rc);
+		break;
+	case QPNP_ID_FLASH1_LED0:
+	case QPNP_ID_FLASH1_LED1:
+		rc = qpnp_flash_init(led);
+		if (rc)
+			dev_err(&led->pdev->dev,
+				"FLASH initialize failed(%d)\n", rc);
+		break;
+	case QPNP_ID_RGB_RED:
+	case QPNP_ID_RGB_GREEN:
+	case QPNP_ID_RGB_BLUE:
+		rc = qpnp_rgb_init(led);
+		if (rc)
+			dev_err(&led->pdev->dev,
+				"RGB initialize failed(%d)\n", rc);
+		break;
+	case QPNP_ID_LED_MPP:
+		rc = qpnp_mpp_init(led);
+		if (rc)
+			dev_err(&led->pdev->dev,
+				"MPP initialize failed(%d)\n", rc);
+		break;
+	case QPNP_ID_LED_GPIO:
+		rc = qpnp_gpio_init(led);
+		if (rc)
+			dev_err(&led->pdev->dev,
+				"GPIO initialize failed(%d)\n", rc);
+		break;
+	case QPNP_ID_KPDBL:
+		rc = qpnp_kpdbl_init(led);
+		if (rc)
+			dev_err(&led->pdev->dev,
+				"KPDBL initialize failed(%d)\n", rc);
+		break;
+	default:
+		dev_err(&led->pdev->dev, "Invalid LED(%d)\n", led->id);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int qpnp_get_common_configs(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	int rc;
+	u32 val;
+	const char *temp_string;
+
+	led->cdev.default_trigger = LED_TRIGGER_DEFAULT;
+	rc = of_property_read_string(node, "linux,default-trigger",
+		&temp_string);
+	if (!rc)
+		led->cdev.default_trigger = temp_string;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->default_on = false;
+	rc = of_property_read_string(node, "qcom,default-state",
+		&temp_string);
+	if (!rc) {
+		if (strcmp(temp_string, "on") == 0)
+			led->default_on = true;
+	} else if (rc != -EINVAL)
+		return rc;
+
+	led->turn_off_delay_ms = 0;
+	rc = of_property_read_u32(node, "qcom,turn-off-delay-ms", &val);
+	if (!rc)
+		led->turn_off_delay_ms = val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	return 0;
+}
+
+/*
+ * Handlers for alternative sources of platform_data
+ */
+static int qpnp_get_config_wled(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	u32 val;
+	uint tmp;
+	int rc;
+
+	led->wled_cfg = devm_kzalloc(&led->pdev->dev,
+				sizeof(struct wled_config_data), GFP_KERNEL);
+	if (!led->wled_cfg)
+		return -ENOMEM;
+
+	rc = regmap_read(led->regmap, PMIC_VERSION_REG, &tmp);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read pmic ver, rc(%d)\n", rc);
+	}
+	led->wled_cfg->pmic_version = (u8)tmp;
+
+	led->wled_cfg->num_strings = WLED_DEFAULT_STRINGS;
+	rc = of_property_read_u32(node, "qcom,num-strings", &val);
+	if (!rc)
+		led->wled_cfg->num_strings = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->num_physical_strings = led->wled_cfg->num_strings;
+	rc = of_property_read_u32(node, "qcom,num-physical-strings", &val);
+	if (!rc)
+		led->wled_cfg->num_physical_strings = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->ovp_val = WLED_DEFAULT_OVP_VAL;
+	rc = of_property_read_u32(node, "qcom,ovp-val", &val);
+	if (!rc)
+		led->wled_cfg->ovp_val = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->boost_curr_lim = WLED_BOOST_LIM_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,boost-curr-lim", &val);
+	if (!rc)
+		led->wled_cfg->boost_curr_lim = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->cp_select = WLED_CP_SEL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,cp-sel", &val);
+	if (!rc)
+		led->wled_cfg->cp_select = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->ctrl_delay_us = WLED_CTRL_DLY_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,ctrl-delay-us", &val);
+	if (!rc)
+		led->wled_cfg->ctrl_delay_us = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->op_fdbck = WLED_OP_FDBCK_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,op-fdbck", &val);
+	if (!rc)
+		led->wled_cfg->op_fdbck = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->switch_freq = WLED_SWITCH_FREQ_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,switch-freq", &val);
+	if (!rc)
+		led->wled_cfg->switch_freq = (u8) val;
+	else if (rc != -EINVAL)
+		return rc;
+
+	led->wled_cfg->dig_mod_gen_en =
+		of_property_read_bool(node, "qcom,dig-mod-gen-en");
+
+	led->wled_cfg->cs_out_en =
+		of_property_read_bool(node, "qcom,cs-out-en");
+
+	return 0;
+}
+
+static int qpnp_get_config_flash(struct qpnp_led_data *led,
+				struct device_node *node, bool *reg_set)
+{
+	int rc;
+	u32 val;
+	uint tmp;
+
+	led->flash_cfg = devm_kzalloc(&led->pdev->dev,
+				sizeof(struct flash_config_data), GFP_KERNEL);
+	if (!led->flash_cfg)
+		return -ENOMEM;
+
+	rc = regmap_read(led->regmap, FLASH_PERIPHERAL_SUBTYPE(led->base),
+			&tmp);
+	if (rc) {
+		dev_err(&led->pdev->dev,
+			"Unable to read from addr=%x, rc(%d)\n",
+			FLASH_PERIPHERAL_SUBTYPE(led->base), rc);
+	}
+	led->flash_cfg->peripheral_subtype = (u8)tmp;
+
+	led->flash_cfg->torch_enable =
+		of_property_read_bool(node, "qcom,torch-enable");
+
+	led->flash_cfg->no_smbb_support =
+		of_property_read_bool(node, "qcom,no-smbb-support");
+
+	if (of_find_property(of_get_parent(node), "flash-wa-supply",
+					NULL) && (!*reg_set)) {
+		led->flash_cfg->flash_wa_reg =
+			devm_regulator_get(&led->pdev->dev, "flash-wa");
+		if (IS_ERR_OR_NULL(led->flash_cfg->flash_wa_reg)) {
+			rc = PTR_ERR(led->flash_cfg->flash_wa_reg);
+			if (rc != EPROBE_DEFER) {
+				dev_err(&led->pdev->dev,
+					"Flash wa regulator get failed(%d)\n",
+					rc);
+			}
+		} else {
+			led->flash_cfg->flash_wa_reg_get = true;
+		}
+	}
+
+	if (led->id == QPNP_ID_FLASH1_LED0) {
+		led->flash_cfg->enable_module = FLASH_ENABLE_LED_0;
+		led->flash_cfg->current_addr = FLASH_LED_0_CURR(led->base);
+		led->flash_cfg->trigger_flash = FLASH_LED_0_OUTPUT;
+		if (!*reg_set) {
+			led->flash_cfg->flash_boost_reg =
+				regulator_get(&led->pdev->dev,
+							"flash-boost");
+			if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
+				rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
+				dev_err(&led->pdev->dev,
+					"Regulator get failed(%d)\n", rc);
+				goto error_get_flash_reg;
+			}
+			led->flash_cfg->flash_reg_get = true;
+			*reg_set = true;
+		} else
+			led->flash_cfg->flash_reg_get = false;
+
+		if (led->flash_cfg->torch_enable) {
+			led->flash_cfg->second_addr =
+						FLASH_LED_1_CURR(led->base);
+		}
+	} else if (led->id == QPNP_ID_FLASH1_LED1) {
+		led->flash_cfg->enable_module = FLASH_ENABLE_LED_1;
+		led->flash_cfg->current_addr = FLASH_LED_1_CURR(led->base);
+		led->flash_cfg->trigger_flash = FLASH_LED_1_OUTPUT;
+		if (!*reg_set) {
+			led->flash_cfg->flash_boost_reg =
+					regulator_get(&led->pdev->dev,
+								"flash-boost");
+			if (IS_ERR(led->flash_cfg->flash_boost_reg)) {
+				rc = PTR_ERR(led->flash_cfg->flash_boost_reg);
+				dev_err(&led->pdev->dev,
+					"Regulator get failed(%d)\n", rc);
+				goto error_get_flash_reg;
+			}
+			led->flash_cfg->flash_reg_get = true;
+			*reg_set = true;
+		} else
+			led->flash_cfg->flash_reg_get = false;
+
+		if (led->flash_cfg->torch_enable) {
+			led->flash_cfg->second_addr =
+						FLASH_LED_0_CURR(led->base);
+		}
+	} else {
+		dev_err(&led->pdev->dev, "Unknown flash LED name given\n");
+		return -EINVAL;
+	}
+
+	if (led->flash_cfg->torch_enable) {
+		if (of_find_property(of_get_parent(node), "torch-boost-supply",
+									NULL)) {
+			if (!led->flash_cfg->no_smbb_support) {
+				led->flash_cfg->torch_boost_reg =
+					regulator_get(&led->pdev->dev,
+								"torch-boost");
+				if (IS_ERR(led->flash_cfg->torch_boost_reg)) {
+					rc = PTR_ERR(led->flash_cfg->
+							torch_boost_reg);
+					dev_err(&led->pdev->dev,
+					"Torch regulator get failed(%d)\n", rc);
+					goto error_get_torch_reg;
+				}
+			}
+			led->flash_cfg->enable_module = FLASH_ENABLE_MODULE;
+		} else
+			led->flash_cfg->enable_module = FLASH_ENABLE_ALL;
+		led->flash_cfg->trigger_flash = FLASH_TORCH_OUTPUT;
+
+		rc = of_property_read_u32(node, "qcom,duration", &val);
+		if (!rc)
+			led->flash_cfg->duration = ((u8) val) - 2;
+		else if (rc == -EINVAL)
+			led->flash_cfg->duration = TORCH_DURATION_12s;
+		else {
+			if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_SINGLE)
+				goto error_get_flash_reg;
+			else if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_DUAL)
+				goto error_get_torch_reg;
+		}
+
+		rc = of_property_read_u32(node, "qcom,current", &val);
+		if (!rc)
+			led->flash_cfg->current_prgm = (val *
+				TORCH_MAX_LEVEL / led->max_current);
+		else {
+			if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_SINGLE)
+				goto error_get_flash_reg;
+			else if (led->flash_cfg->peripheral_subtype ==
+							FLASH_SUBTYPE_DUAL)
+				goto error_get_torch_reg;
+			goto error_get_torch_reg;
+		}
+
+		return 0;
+	}
+
+	rc = of_property_read_u32(node, "qcom,duration", &val);
+	if (!rc)
+		led->flash_cfg->duration = (u8)((val - 10) / 10);
+	else if (rc == -EINVAL)
+		led->flash_cfg->duration = FLASH_DURATION_200ms;
+	else
+		goto error_get_flash_reg;
+
+	rc = of_property_read_u32(node, "qcom,current", &val);
+	if (!rc)
+		led->flash_cfg->current_prgm = val * FLASH_MAX_LEVEL
+						/ led->max_current;
+	else
+		goto error_get_flash_reg;
+
+	rc = of_property_read_u32(node, "qcom,headroom", &val);
+	if (!rc)
+		led->flash_cfg->headroom = (u8) val;
+	else if (rc == -EINVAL)
+		led->flash_cfg->headroom = HEADROOM_500mV;
+	else
+		goto error_get_flash_reg;
+
+	rc = of_property_read_u32(node, "qcom,clamp-curr", &val);
+	if (!rc)
+		led->flash_cfg->clamp_curr = (val *
+				FLASH_MAX_LEVEL / led->max_current);
+	else if (rc == -EINVAL)
+		led->flash_cfg->clamp_curr = FLASH_CLAMP_200mA;
+	else
+		goto error_get_flash_reg;
+
+	rc = of_property_read_u32(node, "qcom,startup-dly", &val);
+	if (!rc)
+		led->flash_cfg->startup_dly = (u8) val;
+	else if (rc == -EINVAL)
+		led->flash_cfg->startup_dly = DELAY_128us;
+	else
+		goto error_get_flash_reg;
+
+	led->flash_cfg->safety_timer =
+		of_property_read_bool(node, "qcom,safety-timer");
+
+	led->flash_cfg->vreg_ok =
+		of_property_read_bool(node, "qcom,sw_vreg_ok");
+
+	return 0;
+
+error_get_torch_reg:
+	if (led->flash_cfg->no_smbb_support)
+		regulator_put(led->flash_cfg->flash_boost_reg);
+	else
+		regulator_put(led->flash_cfg->torch_boost_reg);
+
+error_get_flash_reg:
+	regulator_put(led->flash_cfg->flash_boost_reg);
+	return rc;
+
+}
+
+static int qpnp_get_config_pwm(struct pwm_config_data *pwm_cfg,
+				struct platform_device *pdev,
+				struct device_node *node)
+{
+	struct property *prop;
+	int rc, i, lut_max_size;
+	u32 val;
+	u8 *temp_cfg;
+	const char *led_label;
+
+	pwm_cfg->pwm_dev = of_pwm_get(node, NULL);
+
+	if (IS_ERR(pwm_cfg->pwm_dev)) {
+		rc = PTR_ERR(pwm_cfg->pwm_dev);
+		dev_err(&pdev->dev, "Cannot get PWM device rc:(%d)\n", rc);
+		pwm_cfg->pwm_dev = NULL;
+		return rc;
+	}
+
+	if (pwm_cfg->mode != MANUAL_MODE) {
+		rc = of_property_read_u32(node, "qcom,pwm-us", &val);
+		if (!rc)
+			pwm_cfg->pwm_period_us = val;
+		else
+			return rc;
+	}
+
+	pwm_cfg->use_blink =
+		of_property_read_bool(node, "qcom,use-blink");
+
+	if (pwm_cfg->mode == LPG_MODE || pwm_cfg->use_blink) {
+		pwm_cfg->duty_cycles =
+			devm_kzalloc(&pdev->dev,
+			sizeof(struct pwm_duty_cycles), GFP_KERNEL);
+		if (!pwm_cfg->duty_cycles) {
+			dev_err(&pdev->dev, "Unable to allocate memory\n");
+			rc = -ENOMEM;
+			goto bad_lpg_params;
+		}
+
+		prop = of_find_property(node, "qcom,duty-pcts",
+			&pwm_cfg->duty_cycles->num_duty_pcts);
+		if (!prop) {
+			dev_err(&pdev->dev, "Looking up property node qcom,duty-pcts failed\n");
+			rc =  -ENODEV;
+			goto bad_lpg_params;
+		} else if (!pwm_cfg->duty_cycles->num_duty_pcts) {
+			dev_err(&pdev->dev, "Invalid length of duty pcts\n");
+			rc =  -EINVAL;
+			goto bad_lpg_params;
+		}
+
+		rc = of_property_read_string(node, "label", &led_label);
+
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Failure reading label, rc = %d\n", rc);
+			return rc;
+		}
+
+		if (strcmp(led_label, "kpdbl") == 0)
+			lut_max_size = PWM_GPLED_LUT_MAX_SIZE;
+		else
+			lut_max_size = PWM_LUT_MAX_SIZE;
+
+		pwm_cfg->duty_cycles->duty_pcts =
+			devm_kzalloc(&pdev->dev,
+			sizeof(int) * lut_max_size,
+			GFP_KERNEL);
+		if (!pwm_cfg->duty_cycles->duty_pcts) {
+			dev_err(&pdev->dev, "Unable to allocate memory\n");
+			rc = -ENOMEM;
+			goto bad_lpg_params;
+		}
+
+		pwm_cfg->old_duty_pcts =
+			devm_kzalloc(&pdev->dev,
+			sizeof(int) * lut_max_size,
+			GFP_KERNEL);
+		if (!pwm_cfg->old_duty_pcts) {
+			dev_err(&pdev->dev, "Unable to allocate memory\n");
+			rc = -ENOMEM;
+			goto bad_lpg_params;
+		}
+
+		temp_cfg = devm_kzalloc(&pdev->dev,
+				pwm_cfg->duty_cycles->num_duty_pcts *
+				sizeof(u8), GFP_KERNEL);
+		if (!temp_cfg) {
+			dev_err(&pdev->dev, "Failed to allocate memory for duty pcts\n");
+			rc = -ENOMEM;
+			goto bad_lpg_params;
+		}
+
+		memcpy(temp_cfg, prop->value,
+			pwm_cfg->duty_cycles->num_duty_pcts);
+
+		for (i = 0; i < pwm_cfg->duty_cycles->num_duty_pcts; i++)
+			pwm_cfg->duty_cycles->duty_pcts[i] =
+				(int) temp_cfg[i];
+
+		rc = of_property_read_u32(node, "qcom,start-idx", &val);
+		if (!rc) {
+			pwm_cfg->lut_params.start_idx = val;
+			pwm_cfg->duty_cycles->start_idx = val;
+		} else
+			goto bad_lpg_params;
+
+		pwm_cfg->lut_params.lut_pause_hi = 0;
+		rc = of_property_read_u32(node, "qcom,pause-hi", &val);
+		if (!rc)
+			pwm_cfg->lut_params.lut_pause_hi = val;
+		else if (rc != -EINVAL)
+			goto bad_lpg_params;
+
+		pwm_cfg->lut_params.lut_pause_lo = 0;
+		rc = of_property_read_u32(node, "qcom,pause-lo", &val);
+		if (!rc)
+			pwm_cfg->lut_params.lut_pause_lo = val;
+		else if (rc != -EINVAL)
+			goto bad_lpg_params;
+
+		pwm_cfg->lut_params.ramp_step_ms =
+				QPNP_LUT_RAMP_STEP_DEFAULT;
+		rc = of_property_read_u32(node, "qcom,ramp-step-ms", &val);
+		if (!rc)
+			pwm_cfg->lut_params.ramp_step_ms = val;
+		else if (rc != -EINVAL)
+			goto bad_lpg_params;
+
+		pwm_cfg->lut_params.flags = QPNP_LED_PWM_FLAGS;
+		rc = of_property_read_u32(node, "qcom,lut-flags", &val);
+		if (!rc)
+			pwm_cfg->lut_params.flags = (u8) val;
+		else if (rc != -EINVAL)
+			goto bad_lpg_params;
+
+		pwm_cfg->lut_params.idx_len =
+			pwm_cfg->duty_cycles->num_duty_pcts;
+	}
+	return 0;
+
+bad_lpg_params:
+	pwm_cfg->use_blink = false;
+	if (pwm_cfg->mode == PWM_MODE) {
+		dev_err(&pdev->dev, "LPG parameters not set for blink mode, defaulting to PWM mode\n");
+		return 0;
+	}
+	return rc;
+};
+
+static int qpnp_led_get_mode(const char *mode)
+{
+	if (strcmp(mode, "manual") == 0)
+		return MANUAL_MODE;
+	else if (strcmp(mode, "pwm") == 0)
+		return PWM_MODE;
+	else if (strcmp(mode, "lpg") == 0)
+		return LPG_MODE;
+	else
+		return -EINVAL;
+};
+
+static int qpnp_get_config_kpdbl(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	int rc;
+	u32 val;
+	u8 led_mode;
+	const char *mode;
+
+	led->kpdbl_cfg = devm_kzalloc(&led->pdev->dev,
+				sizeof(struct kpdbl_config_data), GFP_KERNEL);
+	if (!led->kpdbl_cfg)
+		return -ENOMEM;
+
+	rc = of_property_read_string(node, "qcom,mode", &mode);
+	if (!rc) {
+		led_mode = qpnp_led_get_mode(mode);
+		if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
+			dev_err(&led->pdev->dev, "Selected mode not supported for kpdbl.\n");
+			return -EINVAL;
+		}
+		led->kpdbl_cfg->pwm_cfg = devm_kzalloc(&led->pdev->dev,
+					sizeof(struct pwm_config_data),
+					GFP_KERNEL);
+		if (!led->kpdbl_cfg->pwm_cfg)
+			return -ENOMEM;
+
+		led->kpdbl_cfg->pwm_cfg->mode = led_mode;
+		led->kpdbl_cfg->pwm_cfg->default_mode = led_mode;
+	} else {
+		return rc;
+	}
+
+	rc = qpnp_get_config_pwm(led->kpdbl_cfg->pwm_cfg, led->pdev,  node);
+	if (rc < 0)
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,row-id", &val);
+	if (!rc)
+		led->kpdbl_cfg->row_id = val;
+	else
+		return rc;
+
+	led->kpdbl_cfg->row_src_vbst =
+			of_property_read_bool(node, "qcom,row-src-vbst");
+
+	led->kpdbl_cfg->row_src_en =
+			of_property_read_bool(node, "qcom,row-src-en");
+
+	led->kpdbl_cfg->always_on =
+			of_property_read_bool(node, "qcom,always-on");
+
+	return 0;
+}
+
+static int qpnp_get_config_rgb(struct qpnp_led_data *led,
+				struct device_node *node)
+{
+	int rc;
+	u8 led_mode;
+	const char *mode;
+
+	led->rgb_cfg = devm_kzalloc(&led->pdev->dev,
+				sizeof(struct rgb_config_data), GFP_KERNEL);
+	if (!led->rgb_cfg)
+		return -ENOMEM;
+
+	if (led->id == QPNP_ID_RGB_RED)
+		led->rgb_cfg->enable = RGB_LED_ENABLE_RED;
+	else if (led->id == QPNP_ID_RGB_GREEN)
+		led->rgb_cfg->enable = RGB_LED_ENABLE_GREEN;
+	else if (led->id == QPNP_ID_RGB_BLUE)
+		led->rgb_cfg->enable = RGB_LED_ENABLE_BLUE;
+	else
+		return -EINVAL;
+
+	rc = of_property_read_string(node, "qcom,mode", &mode);
+	if (!rc) {
+		led_mode = qpnp_led_get_mode(mode);
+		if ((led_mode == MANUAL_MODE) || (led_mode == -EINVAL)) {
+			dev_err(&led->pdev->dev, "Selected mode not supported for rgb\n");
+			return -EINVAL;
+		}
+		led->rgb_cfg->pwm_cfg = devm_kzalloc(&led->pdev->dev,
+					sizeof(struct pwm_config_data),
+					GFP_KERNEL);
+		if (!led->rgb_cfg->pwm_cfg) {
+			dev_err(&led->pdev->dev,
+				"Unable to allocate memory\n");
+			return -ENOMEM;
+		}
+		led->rgb_cfg->pwm_cfg->mode = led_mode;
+		led->rgb_cfg->pwm_cfg->default_mode = led_mode;
+	} else {
+		return rc;
+	}
+
+	rc = qpnp_get_config_pwm(led->rgb_cfg->pwm_cfg, led->pdev, node);
+	if (rc < 0)
+		return rc;
+
+	return 0;
+}
+
+static int qpnp_get_config_mpp(struct qpnp_led_data *led,
+		struct device_node *node)
+{
+	int rc;
+	u32 val;
+	u8 led_mode;
+	const char *mode;
+
+	led->mpp_cfg = devm_kzalloc(&led->pdev->dev,
+			sizeof(struct mpp_config_data), GFP_KERNEL);
+	if (!led->mpp_cfg)
+		return -ENOMEM;
+
+	if (of_find_property(of_get_parent(node), "mpp-power-supply", NULL)) {
+		led->mpp_cfg->mpp_reg =
+				regulator_get(&led->pdev->dev,
+							"mpp-power");
+		if (IS_ERR(led->mpp_cfg->mpp_reg)) {
+			rc = PTR_ERR(led->mpp_cfg->mpp_reg);
+			dev_err(&led->pdev->dev,
+				"MPP regulator get failed(%d)\n", rc);
+			return rc;
+		}
+	}
+
+	if (led->mpp_cfg->mpp_reg) {
+		rc = of_property_read_u32(of_get_parent(node),
+					"qcom,mpp-power-max-voltage", &val);
+		if (!rc)
+			led->mpp_cfg->max_uV = val;
+		else
+			goto err_config_mpp;
+
+		rc = of_property_read_u32(of_get_parent(node),
+					"qcom,mpp-power-min-voltage", &val);
+		if (!rc)
+			led->mpp_cfg->min_uV = val;
+		else
+			goto err_config_mpp;
+	} else {
+		rc = of_property_read_u32(of_get_parent(node),
+					"qcom,mpp-power-max-voltage", &val);
+		if (!rc)
+			dev_warn(&led->pdev->dev, "No regulator specified\n");
+
+		rc = of_property_read_u32(of_get_parent(node),
+					"qcom,mpp-power-min-voltage", &val);
+		if (!rc)
+			dev_warn(&led->pdev->dev, "No regulator specified\n");
+	}
+
+	led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
+	rc = of_property_read_u32(node, "qcom,current-setting", &val);
+	if (!rc) {
+		if (led->mpp_cfg->current_setting < LED_MPP_CURRENT_MIN)
+			led->mpp_cfg->current_setting = LED_MPP_CURRENT_MIN;
+		else if (led->mpp_cfg->current_setting > LED_MPP_CURRENT_MAX)
+			led->mpp_cfg->current_setting = LED_MPP_CURRENT_MAX;
+		else
+			led->mpp_cfg->current_setting = (u8) val;
+	} else if (rc != -EINVAL)
+		goto err_config_mpp;
+
+	led->mpp_cfg->source_sel = LED_MPP_SOURCE_SEL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,source-sel", &val);
+	if (!rc)
+		led->mpp_cfg->source_sel = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_mpp;
+
+	led->mpp_cfg->mode_ctrl = LED_MPP_MODE_SINK;
+	rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
+	if (!rc)
+		led->mpp_cfg->mode_ctrl = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_mpp;
+
+	led->mpp_cfg->vin_ctrl = LED_MPP_VIN_CTRL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
+	if (!rc)
+		led->mpp_cfg->vin_ctrl = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_mpp;
+
+	led->mpp_cfg->min_brightness = 0;
+	rc = of_property_read_u32(node, "qcom,min-brightness", &val);
+	if (!rc)
+		led->mpp_cfg->min_brightness = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_mpp;
+
+	rc = of_property_read_string(node, "qcom,mode", &mode);
+	if (!rc) {
+		led_mode = qpnp_led_get_mode(mode);
+		led->mpp_cfg->pwm_mode = led_mode;
+		if (led_mode == MANUAL_MODE)
+			return MANUAL_MODE;
+		else if (led_mode == -EINVAL) {
+			dev_err(&led->pdev->dev, "Selected mode not supported for mpp\n");
+			rc = -EINVAL;
+			goto err_config_mpp;
+		}
+		led->mpp_cfg->pwm_cfg = devm_kzalloc(&led->pdev->dev,
+					sizeof(struct pwm_config_data),
+					GFP_KERNEL);
+		if (!led->mpp_cfg->pwm_cfg) {
+			dev_err(&led->pdev->dev,
+				"Unable to allocate memory\n");
+			rc = -ENOMEM;
+			goto err_config_mpp;
+		}
+		led->mpp_cfg->pwm_cfg->mode = led_mode;
+		led->mpp_cfg->pwm_cfg->default_mode = led_mode;
+	} else {
+		return rc;
+	}
+
+	rc = qpnp_get_config_pwm(led->mpp_cfg->pwm_cfg, led->pdev, node);
+	if (rc < 0)
+		goto err_config_mpp;
+
+	return 0;
+
+err_config_mpp:
+	if (led->mpp_cfg->mpp_reg)
+		regulator_put(led->mpp_cfg->mpp_reg);
+	return rc;
+}
+
+static int qpnp_get_config_gpio(struct qpnp_led_data *led,
+		struct device_node *node)
+{
+	int rc;
+	u32 val;
+
+	led->gpio_cfg = devm_kzalloc(&led->pdev->dev,
+			sizeof(struct gpio_config_data), GFP_KERNEL);
+	if (!led->gpio_cfg)
+		return -ENOMEM;
+
+	led->gpio_cfg->source_sel = LED_GPIO_SOURCE_SEL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,source-sel", &val);
+	if (!rc)
+		led->gpio_cfg->source_sel = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_gpio;
+
+	led->gpio_cfg->mode_ctrl = LED_GPIO_MODE_OUTPUT;
+	rc = of_property_read_u32(node, "qcom,mode-ctrl", &val);
+	if (!rc)
+		led->gpio_cfg->mode_ctrl = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_gpio;
+
+	led->gpio_cfg->vin_ctrl = LED_GPIO_VIN_CTRL_DEFAULT;
+	rc = of_property_read_u32(node, "qcom,vin-ctrl", &val);
+	if (!rc)
+		led->gpio_cfg->vin_ctrl = (u8) val;
+	else if (rc != -EINVAL)
+		goto err_config_gpio;
+
+	return 0;
+
+err_config_gpio:
+	return rc;
+}
+
+static int qpnp_leds_probe(struct platform_device *pdev)
+{
+	struct qpnp_led_data *led, *led_array;
+	unsigned int base;
+	struct device_node *node, *temp;
+	int rc, i, num_leds = 0, parsed_leds = 0;
+	const char *led_label;
+	bool regulator_probe = false;
+
+	node = pdev->dev.of_node;
+	if (node == NULL)
+		return -ENODEV;
+
+	temp = NULL;
+	while ((temp = of_get_next_child(node, temp)))
+		num_leds++;
+
+	if (!num_leds)
+		return -ECHILD;
+
+	led_array = devm_kcalloc(&pdev->dev, num_leds, sizeof(*led_array),
+				GFP_KERNEL);
+	if (!led_array)
+		return -ENOMEM;
+
+	for_each_child_of_node(node, temp) {
+		led = &led_array[parsed_leds];
+		led->num_leds = num_leds;
+		led->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+		if (!led->regmap) {
+			dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+			return -EINVAL;
+		}
+		led->pdev = pdev;
+
+		rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				pdev->dev.of_node->full_name, rc);
+			goto fail_id_check;
+		}
+		led->base = base;
+
+		rc = of_property_read_string(temp, "label", &led_label);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"Failure reading label, rc = %d\n", rc);
+			goto fail_id_check;
+		}
+
+		rc = of_property_read_string(temp, "linux,name",
+			&led->cdev.name);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"Failure reading led name, rc = %d\n", rc);
+			goto fail_id_check;
+		}
+
+		rc = of_property_read_u32(temp, "qcom,max-current",
+			&led->max_current);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"Failure reading max_current, rc =  %d\n", rc);
+			goto fail_id_check;
+		}
+
+		rc = of_property_read_u32(temp, "qcom,id", &led->id);
+		if (rc < 0) {
+			dev_err(&led->pdev->dev,
+				"Failure reading led id, rc =  %d\n", rc);
+			goto fail_id_check;
+		}
+
+		rc = qpnp_get_common_configs(led, temp);
+		if (rc) {
+			dev_err(&led->pdev->dev, "Failure reading common led configuration, rc = %d\n",
+				rc);
+			goto fail_id_check;
+		}
+
+		led->cdev.brightness_set    = qpnp_led_set;
+		led->cdev.brightness_get    = qpnp_led_get;
+
+		if (strcmp(led_label, "wled") == 0) {
+			rc = qpnp_get_config_wled(led, temp);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Unable to read wled config data\n");
+				goto fail_id_check;
+			}
+		} else if (strcmp(led_label, "flash") == 0) {
+			if (!of_find_property(node, "flash-boost-supply", NULL))
+				regulator_probe = true;
+			rc = qpnp_get_config_flash(led, temp, &regulator_probe);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Unable to read flash config data\n");
+				goto fail_id_check;
+			}
+		} else if (strcmp(led_label, "rgb") == 0) {
+			rc = qpnp_get_config_rgb(led, temp);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Unable to read rgb config data\n");
+				goto fail_id_check;
+			}
+		} else if (strcmp(led_label, "mpp") == 0) {
+			rc = qpnp_get_config_mpp(led, temp);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+						"Unable to read mpp config data\n");
+				goto fail_id_check;
+			}
+		} else if (strcmp(led_label, "gpio") == 0) {
+			rc = qpnp_get_config_gpio(led, temp);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+						"Unable to read gpio config data\n");
+				goto fail_id_check;
+			}
+		} else if (strcmp(led_label, "kpdbl") == 0) {
+			bitmap_zero(kpdbl_leds_in_use, NUM_KPDBL_LEDS);
+			is_kpdbl_master_turn_on = false;
+			rc = qpnp_get_config_kpdbl(led, temp);
+			if (rc < 0) {
+				dev_err(&led->pdev->dev,
+					"Unable to read kpdbl config data\n");
+				goto fail_id_check;
+			}
+		} else {
+			dev_err(&led->pdev->dev, "No LED matching label\n");
+			rc = -EINVAL;
+			goto fail_id_check;
+		}
+
+		if (led->id != QPNP_ID_FLASH1_LED0 &&
+					led->id != QPNP_ID_FLASH1_LED1)
+			mutex_init(&led->lock);
+
+		led->in_order_command_processing = of_property_read_bool
+				(temp, "qcom,in-order-command-processing");
+
+		if (led->in_order_command_processing) {
+			/*
+			 * the command order from user space needs to be
+			 * maintained use ordered workqueue to prevent
+			 * concurrency
+			 */
+			led->workqueue = alloc_ordered_workqueue
+							("led_workqueue", 0);
+			if (!led->workqueue) {
+				rc = -ENOMEM;
+				goto fail_id_check;
+			}
+		}
+
+		INIT_WORK(&led->work, qpnp_led_work);
+
+		rc =  qpnp_led_initialize(led);
+		if (rc < 0)
+			goto fail_id_check;
+
+		rc = qpnp_led_set_max_brightness(led);
+		if (rc < 0)
+			goto fail_id_check;
+
+		rc = led_classdev_register(&pdev->dev, &led->cdev);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"unable to register led %d,rc=%d\n",
+						 led->id, rc);
+			goto fail_id_check;
+		}
+
+		if (led->id == QPNP_ID_FLASH1_LED0 ||
+			led->id == QPNP_ID_FLASH1_LED1) {
+			rc = sysfs_create_group(&led->cdev.dev->kobj,
+							&led_attr_group);
+			if (rc)
+				goto fail_id_check;
+
+		}
+
+		if (led->id == QPNP_ID_LED_MPP) {
+			if (!led->mpp_cfg->pwm_cfg)
+				break;
+			if (led->mpp_cfg->pwm_cfg->mode == PWM_MODE) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&pwm_attr_group);
+				if (rc)
+					goto fail_id_check;
+			}
+			if (led->mpp_cfg->pwm_cfg->use_blink) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&blink_attr_group);
+				if (rc)
+					goto fail_id_check;
+
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&lpg_attr_group);
+				if (rc)
+					goto fail_id_check;
+			} else if (led->mpp_cfg->pwm_cfg->mode == LPG_MODE) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&lpg_attr_group);
+				if (rc)
+					goto fail_id_check;
+			}
+		} else if ((led->id == QPNP_ID_RGB_RED) ||
+			(led->id == QPNP_ID_RGB_GREEN) ||
+			(led->id == QPNP_ID_RGB_BLUE)) {
+			if (led->rgb_cfg->pwm_cfg->mode == PWM_MODE) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&pwm_attr_group);
+				if (rc)
+					goto fail_id_check;
+			}
+			if (led->rgb_cfg->pwm_cfg->use_blink) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&blink_attr_group);
+				if (rc)
+					goto fail_id_check;
+
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&lpg_attr_group);
+				if (rc)
+					goto fail_id_check;
+			} else if (led->rgb_cfg->pwm_cfg->mode == LPG_MODE) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&lpg_attr_group);
+				if (rc)
+					goto fail_id_check;
+			}
+		} else if (led->id == QPNP_ID_KPDBL) {
+			if (led->kpdbl_cfg->pwm_cfg->mode == PWM_MODE) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&pwm_attr_group);
+				if (rc)
+					goto fail_id_check;
+			}
+			if (led->kpdbl_cfg->pwm_cfg->use_blink) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&blink_attr_group);
+				if (rc)
+					goto fail_id_check;
+
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&lpg_attr_group);
+				if (rc)
+					goto fail_id_check;
+			} else if (led->kpdbl_cfg->pwm_cfg->mode == LPG_MODE) {
+				rc = sysfs_create_group(&led->cdev.dev->kobj,
+					&lpg_attr_group);
+				if (rc)
+					goto fail_id_check;
+			}
+		}
+
+		/* configure default state */
+		if (led->default_on) {
+			led->cdev.brightness = led->cdev.max_brightness;
+			__qpnp_led_work(led, led->cdev.brightness);
+			if (led->turn_off_delay_ms > 0)
+				qpnp_led_turn_off(led);
+		} else
+			led->cdev.brightness = LED_OFF;
+
+		parsed_leds++;
+	}
+	dev_set_drvdata(&pdev->dev, led_array);
+	return 0;
+
+fail_id_check:
+	for (i = 0; i < parsed_leds; i++) {
+		if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
+				led_array[i].id != QPNP_ID_FLASH1_LED1)
+			mutex_destroy(&led_array[i].lock);
+		if (led_array[i].in_order_command_processing)
+			destroy_workqueue(led_array[i].workqueue);
+		led_classdev_unregister(&led_array[i].cdev);
+	}
+
+	return rc;
+}
+
+static int qpnp_leds_remove(struct platform_device *pdev)
+{
+	struct qpnp_led_data *led_array  = dev_get_drvdata(&pdev->dev);
+	int i, parsed_leds = led_array->num_leds;
+
+	for (i = 0; i < parsed_leds; i++) {
+		cancel_work_sync(&led_array[i].work);
+		if (led_array[i].id != QPNP_ID_FLASH1_LED0 &&
+				led_array[i].id != QPNP_ID_FLASH1_LED1)
+			mutex_destroy(&led_array[i].lock);
+
+		if (led_array[i].in_order_command_processing)
+			destroy_workqueue(led_array[i].workqueue);
+		led_classdev_unregister(&led_array[i].cdev);
+		switch (led_array[i].id) {
+		case QPNP_ID_WLED:
+			break;
+		case QPNP_ID_FLASH1_LED0:
+		case QPNP_ID_FLASH1_LED1:
+			if (led_array[i].flash_cfg->flash_reg_get)
+				regulator_put(
+				       led_array[i].flash_cfg->flash_boost_reg);
+			if (led_array[i].flash_cfg->torch_enable)
+				if (!led_array[i].flash_cfg->no_smbb_support)
+					regulator_put(led_array[i].
+					flash_cfg->torch_boost_reg);
+			sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&led_attr_group);
+			break;
+		case QPNP_ID_RGB_RED:
+		case QPNP_ID_RGB_GREEN:
+		case QPNP_ID_RGB_BLUE:
+			if (led_array[i].rgb_cfg->pwm_cfg->mode == PWM_MODE)
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&pwm_attr_group);
+			if (led_array[i].rgb_cfg->pwm_cfg->use_blink) {
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&blink_attr_group);
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&lpg_attr_group);
+			} else if (led_array[i].rgb_cfg->pwm_cfg->mode
+				   == LPG_MODE)
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&lpg_attr_group);
+			break;
+		case QPNP_ID_LED_MPP:
+			if (!led_array[i].mpp_cfg->pwm_cfg)
+				break;
+			if (led_array[i].mpp_cfg->pwm_cfg->mode == PWM_MODE)
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&pwm_attr_group);
+			if (led_array[i].mpp_cfg->pwm_cfg->use_blink) {
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&blink_attr_group);
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&lpg_attr_group);
+			} else if (led_array[i].mpp_cfg->pwm_cfg->mode
+				   == LPG_MODE)
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&lpg_attr_group);
+			if (led_array[i].mpp_cfg->mpp_reg)
+				regulator_put(led_array[i].mpp_cfg->mpp_reg);
+			break;
+		case QPNP_ID_KPDBL:
+			if (led_array[i].kpdbl_cfg->pwm_cfg->mode == PWM_MODE)
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&pwm_attr_group);
+			if (led_array[i].kpdbl_cfg->pwm_cfg->use_blink) {
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&blink_attr_group);
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&lpg_attr_group);
+			} else if (led_array[i].kpdbl_cfg->pwm_cfg->mode
+				   == LPG_MODE)
+				sysfs_remove_group(&led_array[i].cdev.dev->kobj,
+							&lpg_attr_group);
+			break;
+		default:
+			dev_err(&led_array->pdev->dev,
+					"Invalid LED(%d)\n",
+					led_array[i].id);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = "qcom,leds-qpnp",},
+	{ },
+};
+#else
+#define spmi_match_table NULL
+#endif
+
+static struct platform_driver qpnp_leds_driver = {
+	.driver		= {
+		.name		= "qcom,leds-qpnp",
+		.of_match_table	= spmi_match_table,
+	},
+	.probe		= qpnp_leds_probe,
+	.remove		= qpnp_leds_remove,
+};
+
+static int __init qpnp_led_init(void)
+{
+	return platform_driver_register(&qpnp_leds_driver);
+}
+module_init(qpnp_led_init);
+
+static void __exit qpnp_led_exit(void)
+{
+	platform_driver_unregister(&qpnp_leds_driver);
+}
+module_exit(qpnp_led_exit);
+
+MODULE_DESCRIPTION("QPNP LEDs driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("leds:leds-qpnp");
+
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
index 7d38e6b..dbb9a36 100644
--- a/drivers/leds/leds.h
+++ b/drivers/leds/leds.h
@@ -21,6 +21,22 @@ static inline int led_get_brightness(struct led_classdev *led_cdev)
 	return led_cdev->brightness;
 }
 
+static inline struct led_classdev *trigger_to_lcdev(struct led_trigger *trig)
+{
+	struct led_classdev *led_cdev;
+
+	read_lock(&trig->leddev_list_lock);
+	list_for_each_entry(led_cdev, &trig->led_cdevs, trig_list) {
+		if (!strcmp(led_cdev->default_trigger, trig->name)) {
+			read_unlock(&trig->leddev_list_lock);
+			return led_cdev;
+		}
+	}
+
+	read_unlock(&trig->leddev_list_lock);
+	return NULL;
+}
+
 void led_init_core(struct led_classdev *led_cdev);
 void led_stop_software_blink(struct led_classdev *led_cdev);
 void led_set_brightness_nopm(struct led_classdev *led_cdev,
diff --git a/drivers/media/platform/Kconfig b/drivers/media/platform/Kconfig
index d3170d5..02dd73a 100644
--- a/drivers/media/platform/Kconfig
+++ b/drivers/media/platform/Kconfig
@@ -120,6 +120,7 @@
 source "drivers/media/platform/xilinx/Kconfig"
 source "drivers/media/platform/rcar-vin/Kconfig"
 source "drivers/media/platform/atmel/Kconfig"
+source "drivers/media/platform/msm/Kconfig"
 
 config VIDEO_TI_CAL
 	tristate "TI CAL (Camera Adaptation Layer) driver"
diff --git a/drivers/media/platform/Makefile b/drivers/media/platform/Makefile
index 40b18d1..0929cd1 100644
--- a/drivers/media/platform/Makefile
+++ b/drivers/media/platform/Makefile
@@ -66,3 +66,5 @@
 obj-$(CONFIG_VIDEO_MEDIATEK_VPU)	+= mtk-vpu/
 
 obj-$(CONFIG_VIDEO_MEDIATEK_VCODEC)	+= mtk-vcodec/
+
+obj-y  += msm/
diff --git a/drivers/media/platform/msm/Kconfig b/drivers/media/platform/msm/Kconfig
index f2ff6eb..19567ad 100644
--- a/drivers/media/platform/msm/Kconfig
+++ b/drivers/media/platform/msm/Kconfig
@@ -12,3 +12,5 @@
       Qualcomm Technologies, Inc. Spectra camera and video capture.
       Enabling this adds support for the camera driver stack including sensor,
       IFE and postprocessing drivers.
+
+source "drivers/media/platform/msm/vidc/Kconfig"
diff --git a/drivers/media/platform/msm/Makefile b/drivers/media/platform/msm/Makefile
new file mode 100644
index 0000000..7605fdb
--- /dev/null
+++ b/drivers/media/platform/msm/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the QCOM specific video device drivers
+# based on V4L2.
+#
+obj-$(CONFIG_MSM_VIDC_V4L2) += vidc/
diff --git a/drivers/media/platform/msm/vidc/Kconfig b/drivers/media/platform/msm/vidc/Kconfig
new file mode 100644
index 0000000..db12cae
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/Kconfig
@@ -0,0 +1,11 @@
+#
+# VIDEO CORE
+#
+
+menuconfig MSM_VIDC_V4L2
+	tristate "Qualcomm Technologies, Inc. MSM V4L2 based video driver"
+		depends on ARCH_QCOM && VIDEO_V4L2
+		select VIDEOBUF2_CORE
+
+source "drivers/media/platform/msm/vidc/vmem/Kconfig"
+source "drivers/media/platform/msm/vidc/governors/Kconfig"
diff --git a/drivers/media/platform/msm/vidc/Makefile b/drivers/media/platform/msm/vidc/Makefile
new file mode 100644
index 0000000..7bad081
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/Makefile
@@ -0,0 +1,22 @@
+ccflags-y += -I$(srctree)/drivers/media/platform/msm/vidc/
+
+msm-vidc-objs := msm_v4l2_vidc.o \
+                                msm_vidc_common.o \
+                                msm_vidc.o \
+                                msm_vdec.o \
+                                msm_venc.o \
+                                msm_smem.o \
+                                msm_vidc_debug.o \
+                                msm_vidc_res_parse.o \
+                                venus_hfi.o \
+                                hfi_response_handler.o \
+                                hfi_packetization.o \
+                                vidc_hfi.o \
+                                venus_boot.o \
+                                msm_vidc_clocks.o
+
+obj-$(CONFIG_MSM_VIDC_V4L2) := msm-vidc.o
+
+obj-$(CONFIG_MSM_VIDC_V4L2) += governors/
+
+obj-$(CONFIG_MSM_VIDC_VMEM) += vmem/
diff --git a/drivers/media/platform/msm/vidc/governors/Kconfig b/drivers/media/platform/msm/vidc/governors/Kconfig
new file mode 100644
index 0000000..d667d65
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/governors/Kconfig
@@ -0,0 +1,6 @@
+menuconfig MSM_VIDC_GOVERNORS
+	tristate "Clock and bandwidth governors for QTI MSM V4L2 based video driver"
+	depends on MSM_VIDC_V4L2 && PM_DEVFREQ
+	help
+	Chooses a set of devfreq governors aimed at providing accurate bandwidth
+	or clock frequency values for MSM V4L2 video driver.
diff --git a/drivers/media/platform/msm/vidc/governors/Makefile b/drivers/media/platform/msm/vidc/governors/Makefile
new file mode 100644
index 0000000..695a3ae
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/governors/Makefile
@@ -0,0 +1,7 @@
+ccflags-y := -I$(srctree)/drivers/devfreq/ \
+	-I$(srctree)/drivers/media/platform/msm/vidc/ \
+        -I$(srctree)/drivers/media/platform/msm/vidc/governors/
+
+msm-vidc-dyn-gov-objs := msm_vidc_dyn_gov.o
+
+obj-$(CONFIG_MSM_VIDC_GOVERNORS) := msm-vidc-dyn-gov.o
diff --git a/drivers/media/platform/msm/vidc/governors/fixedpoint.h b/drivers/media/platform/msm/vidc/governors/fixedpoint.h
new file mode 100644
index 0000000..da0781f
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/governors/fixedpoint.h
@@ -0,0 +1,72 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifdef _FIXP_ARITH_H
+#error "This implementation is meant to override fixp-arith.h, don't use both"
+#endif
+
+#ifndef __FP_H__
+#define __FP_H__
+
+/*
+ * Normally would typedef'ed, but checkpatch doesn't like typedef.
+ * Also should be normally typedef'ed to intmax_t but that doesn't seem to be
+ * available in the kernel
+ */
+#define fp_t size_t
+
+/* (Arbitrarily) make the first 25% of the bits to be the fractional bits */
+#define FP_FRACTIONAL_BITS ((sizeof(fp_t) * 8) / 4)
+
+#define FP(__i, __f_n, __f_d) \
+	((((fp_t)(__i)) << FP_FRACTIONAL_BITS) + \
+	(((__f_n) << FP_FRACTIONAL_BITS) / (__f_d)))
+
+#define FP_INT(__i) FP(__i, 0, 1)
+#define FP_ONE FP_INT(1)
+#define FP_ZERO FP_INT(0)
+
+static inline size_t fp_frac_base(void)
+{
+	return GENMASK(FP_FRACTIONAL_BITS - 1, 0);
+}
+
+static inline size_t fp_frac(fp_t a)
+{
+	return a & GENMASK(FP_FRACTIONAL_BITS - 1, 0);
+}
+
+static inline size_t fp_int(fp_t a)
+{
+	return a >> FP_FRACTIONAL_BITS;
+}
+
+static inline size_t fp_round(fp_t a)
+{
+	/* is the fractional part >= frac_max / 2? */
+	bool round_up = fp_frac(a) >= fp_frac_base() / 2;
+
+	return fp_int(a) + round_up;
+}
+
+static inline fp_t fp_mult(fp_t a, fp_t b)
+{
+	return (a * b) >> FP_FRACTIONAL_BITS;
+}
+
+
+static inline fp_t fp_div(fp_t a, fp_t b)
+{
+	return (a << FP_FRACTIONAL_BITS) / b;
+}
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
new file mode 100644
index 0000000..ed9f7e4
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/governors/msm_vidc_dyn_gov.c
@@ -0,0 +1,1154 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include "governor.h"
+#include "fixedpoint.h"
+#include "msm_vidc_internal.h"
+#include "msm_vidc_debug.h"
+#include "vidc_hfi_api.h"
+
+static bool debug;
+module_param(debug, bool, 0644);
+
+enum governor_mode {
+	GOVERNOR_DDR,
+	GOVERNOR_VMEM,
+	GOVERNOR_VMEM_PLUS,
+};
+
+struct governor {
+	enum governor_mode mode;
+	struct devfreq_governor devfreq_gov;
+};
+
+enum scenario {
+	SCENARIO_WORST,
+	SCENARIO_SUSTAINED_WORST,
+	SCENARIO_AVERAGE,
+	SCENARIO_MAX,
+};
+
+/*
+ * Minimum dimensions that the governor is willing to calculate
+ * bandwidth for.  This means that anything bandwidth(0, 0) ==
+ * bandwidth(BASELINE_DIMENSIONS.width, BASELINE_DIMENSIONS.height)
+ */
+const struct {
+	int height, width;
+} BASELINE_DIMENSIONS = {
+	.width = 1280,
+	.height = 720,
+};
+
+/*
+ * These are hardcoded AB values that the governor votes for in certain
+ * situations, where a certain bus frequency is desired.  It isn't exactly
+ * scalable since different platforms have different bus widths, but we'll
+ * deal with that in the future.
+ */
+const unsigned long NOMINAL_BW_MBPS = 6000 /* ideally 320 Mhz */,
+	SVS_BW_MBPS = 2000 /* ideally 100 Mhz */;
+
+/* converts Mbps to bps (the "b" part can be bits or bytes based on context) */
+#define kbps(__mbps) ((__mbps) * 1000)
+#define bps(__mbps) (kbps(__mbps) * 1000)
+
+#define GENERATE_SCENARIO_PROFILE(__average, __worst) {                        \
+	[SCENARIO_AVERAGE] = (__average),                                      \
+	[SCENARIO_WORST] =  (__worst),                                         \
+	[SCENARIO_SUSTAINED_WORST] = (__worst),                                \
+}
+
+#define GENERATE_COMPRESSION_PROFILE(__bpp, __average, __worst) {              \
+	.bpp = __bpp,                                                          \
+	.ratio = GENERATE_SCENARIO_PROFILE(__average, __worst),                \
+}
+
+/*
+ * The below table is a structural representation of the following table:
+ *  Resolution |    Bitrate |              Compression Ratio          |
+ * ............|............|.........................................|
+ * Width Height|Average High|Avg_8bpc Worst_8bpc Avg_10bpc Worst_10bpc|
+ *  1280    720|      7   14|    1.69       1.28      1.49        1.23|
+ *  1920   1080|     20   40|    1.69       1.28      1.49        1.23|
+ *  2560   1440|     32   64|     2.2       1.26      1.97        1.22|
+ *  3840   2160|     42   84|     2.2       1.26      1.97        1.22|
+ *  4096   2160|     44   88|     2.2       1.26      1.97        1.22|
+ *  4096   2304|     48   96|     2.2       1.26      1.97        1.22|
+ */
+#define COMPRESSION_RATIO_MAX 2
+static struct lut {
+	int frame_size; /* width x height */
+	unsigned long bitrate[SCENARIO_MAX];
+	struct {
+		int bpp;
+		fp_t ratio[SCENARIO_MAX];
+	} compression_ratio[COMPRESSION_RATIO_MAX];
+} const LUT[] = {
+	{
+		.frame_size = 1280 * 720,
+		.bitrate = GENERATE_SCENARIO_PROFILE(7, 14),
+		.compression_ratio = {
+			GENERATE_COMPRESSION_PROFILE(8,
+					FP(1, 69, 100),
+					FP(1, 28, 100)),
+			GENERATE_COMPRESSION_PROFILE(10,
+					FP(1, 49, 100),
+					FP(1, 23, 100)),
+		}
+	},
+	{
+		.frame_size = 1920 * 1088,
+		.bitrate = GENERATE_SCENARIO_PROFILE(20, 40),
+		.compression_ratio = {
+			GENERATE_COMPRESSION_PROFILE(8,
+					FP(1, 69, 100),
+					FP(1, 28, 100)),
+			GENERATE_COMPRESSION_PROFILE(10,
+					FP(1, 49, 100),
+					FP(1, 23, 100)),
+		}
+	},
+	{
+		.frame_size = 2560 * 1440,
+		.bitrate = GENERATE_SCENARIO_PROFILE(32, 64),
+		.compression_ratio = {
+			GENERATE_COMPRESSION_PROFILE(8,
+					FP(2, 20, 100),
+					FP(1, 26, 100)),
+			GENERATE_COMPRESSION_PROFILE(10,
+					FP(1, 97, 100),
+					FP(1, 22, 100)),
+		}
+	},
+	{
+		.frame_size = 3840 * 2160,
+		.bitrate = GENERATE_SCENARIO_PROFILE(42, 84),
+		.compression_ratio = {
+			GENERATE_COMPRESSION_PROFILE(8,
+					FP(2, 20, 100),
+					FP(1, 26, 100)),
+			GENERATE_COMPRESSION_PROFILE(10,
+					FP(1, 97, 100),
+					FP(1, 22, 100)),
+		}
+	},
+	{
+		.frame_size = 4096 * 2160,
+		.bitrate = GENERATE_SCENARIO_PROFILE(44, 88),
+		.compression_ratio = {
+			GENERATE_COMPRESSION_PROFILE(8,
+					FP(2, 20, 100),
+					FP(1, 26, 100)),
+			GENERATE_COMPRESSION_PROFILE(10,
+					FP(1, 97, 100),
+					FP(1, 22, 100)),
+		}
+	},
+	{
+		.frame_size = 4096 * 2304,
+		.bitrate = GENERATE_SCENARIO_PROFILE(48, 96),
+		.compression_ratio = {
+			GENERATE_COMPRESSION_PROFILE(8,
+					FP(2, 20, 100),
+					FP(1, 26, 100)),
+			GENERATE_COMPRESSION_PROFILE(10,
+					FP(1, 97, 100),
+					FP(1, 22, 100)),
+		}
+	},
+};
+
+static struct lut const *__lut(int width, int height)
+{
+	int frame_size = height * width, c = 0;
+
+	do {
+		if (LUT[c].frame_size >= frame_size)
+			return &LUT[c];
+	} while (++c < ARRAY_SIZE(LUT));
+
+	return &LUT[ARRAY_SIZE(LUT) - 1];
+}
+
+static fp_t __compression_ratio(struct lut const *entry, int bpp,
+		enum scenario s)
+{
+	int c = 0;
+
+	for (c = 0; c < COMPRESSION_RATIO_MAX; ++c) {
+		if (entry->compression_ratio[c].bpp == bpp)
+			return entry->compression_ratio[c].ratio[s];
+	}
+
+	WARN(true, "Shouldn't be here, LUT possibly corrupted?\n");
+	return FP_ZERO; /* impossible */
+}
+
+#define DUMP_HEADER_MAGIC 0xdeadbeef
+#define DUMP_FP_FMT "%FP" /* special format for fp_t */
+struct dump {
+	char *key;
+	char *format;
+	size_t val;
+};
+
+static void __dump(struct dump dump[], int len)
+{
+	int c = 0;
+
+	for (c = 0; c < len; ++c) {
+		char format_line[128] = "", formatted_line[128] = "";
+
+		if (dump[c].val == DUMP_HEADER_MAGIC) {
+			snprintf(formatted_line, sizeof(formatted_line), "%s\n",
+					dump[c].key);
+		} else {
+			bool fp_format = !strcmp(dump[c].format, DUMP_FP_FMT);
+
+			if (!fp_format) {
+				snprintf(format_line, sizeof(format_line),
+						"    %-35s: %s\n", dump[c].key,
+						dump[c].format);
+				snprintf(formatted_line, sizeof(formatted_line),
+						format_line, dump[c].val);
+			} else {
+				size_t integer_part, fractional_part;
+
+				integer_part = fp_int(dump[c].val);
+				fractional_part = fp_frac(dump[c].val);
+				snprintf(formatted_line, sizeof(formatted_line),
+						"    %-35s: %zd + %zd/%zd\n",
+						dump[c].key, integer_part,
+						fractional_part,
+						fp_frac_base());
+
+
+			}
+		}
+
+		dprintk(VIDC_DBG, "%s", formatted_line);
+	}
+}
+
+static unsigned long __calculate_vpe(struct vidc_bus_vote_data *d,
+		enum governor_mode gm)
+{
+	return 0;
+}
+
+static bool __ubwc(enum hal_uncompressed_format f)
+{
+	switch (f) {
+	case HAL_COLOR_FORMAT_NV12_UBWC:
+	case HAL_COLOR_FORMAT_NV12_TP10_UBWC:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static int __bpp(enum hal_uncompressed_format f)
+{
+	switch (f) {
+	case HAL_COLOR_FORMAT_NV12:
+	case HAL_COLOR_FORMAT_NV21:
+	case HAL_COLOR_FORMAT_NV12_UBWC:
+		return 8;
+	case HAL_COLOR_FORMAT_NV12_TP10_UBWC:
+		return 10;
+	default:
+		dprintk(VIDC_ERR,
+				"What's this?  We don't support this colorformat (%x)",
+				f);
+		return INT_MAX;
+	}
+}
+
+static unsigned long __calculate_vmem_plus_ab(struct vidc_bus_vote_data *d)
+{
+	unsigned long i = 0, vmem_plus = 0;
+
+	if (!d->imem_ab_tbl || !d->imem_ab_tbl_size) {
+		vmem_plus = 1; /* Vote for the min ab value */
+		goto exit;
+	}
+
+	/* Pick up vmem frequency based on venus core frequency */
+	for (i = 0; i < d->imem_ab_tbl_size; i++) {
+		if (d->imem_ab_tbl[i].core_freq == d->core_freq) {
+			vmem_plus = d->imem_ab_tbl[i].imem_ab;
+			break;
+		}
+	}
+
+	/*
+	 * Incase we get an unsupported freq throw a warning
+	 * and set ab to the minimum value.
+	 */
+	if (!vmem_plus) {
+		vmem_plus = 1;
+		dprintk(VIDC_WARN,
+			"could not calculate vmem ab value due to core freq mismatch\n");
+		WARN_ON(1);
+	}
+
+exit:
+	return vmem_plus;
+}
+
+static unsigned long __calculate_decoder(struct vidc_bus_vote_data *d,
+		enum governor_mode gm) {
+	/*
+	 * XXX: Don't fool around with any of the hardcoded numbers unless you
+	 * know /exactly/ what you're doing.  Many of these numbers are
+	 * measured heuristics and hardcoded numbers taken from the firmware.
+	 */
+	/* Decoder parameters */
+	enum scenario scenario;
+	int width, height, lcu_size, dpb_bpp, opb_bpp, fps;
+	bool unified_dpb_opb, dpb_compression_enabled, opb_compression_enabled;
+	fp_t dpb_opb_scaling_ratio, dpb_compression_factor,
+		opb_compression_factor, qsmmu_bw_overhead_factor;
+	int vmem_size; /* in kB */
+
+	/* Derived parameters */
+	int lcu_per_frame, tnbr_per_lcu_10bpc, tnbr_per_lcu_8bpc, tnbr_per_lcu,
+		colocated_bytes_per_lcu, vmem_line_buffer, vmem_chroma_cache,
+		vmem_luma_cache, vmem_chroma_luma_cache;
+	unsigned long bitrate;
+	fp_t bins_to_bit_factor, dpb_write_factor, ten_bpc_packing_factor,
+		ten_bpc_bpp_factor, vsp_read_factor, vsp_write_factor,
+		ocmem_usage_lcu_factor, ref_ocmem_bw_factor_read,
+		ref_ocmem_bw_factor_write, bw_for_1x_8bpc, dpb_bw_for_1x,
+		motion_vector_complexity, row_cache_penalty, opb_bw;
+
+	/* Output parameters */
+	struct {
+		fp_t vsp_read, vsp_write, collocated_read, collocated_write,
+			line_buffer_read, line_buffer_write, recon_read,
+			recon_write, opb_read, opb_write, dpb_read, dpb_write,
+			total;
+	} ddr, vmem;
+
+	unsigned long ret = 0;
+
+	/* Decoder parameters setup */
+	scenario = SCENARIO_WORST;
+
+	width = max(d->width, BASELINE_DIMENSIONS.width);
+	height = max(d->height, BASELINE_DIMENSIONS.height);
+
+	lcu_size = 32;
+
+	dpb_bpp = d->num_formats >= 1 ? __bpp(d->color_formats[0]) : INT_MAX;
+	opb_bpp = d->num_formats >= 2 ?  __bpp(d->color_formats[1]) : dpb_bpp;
+
+	fps = d->fps;
+
+	unified_dpb_opb = d->num_formats == 1;
+
+	dpb_opb_scaling_ratio = FP_ONE;
+
+	dpb_compression_enabled = d->num_formats >= 1 &&
+		__ubwc(d->color_formats[0]);
+	opb_compression_enabled = d->num_formats >= 2 &&
+		__ubwc(d->color_formats[1]);
+
+	dpb_compression_factor = !dpb_compression_enabled ? FP_ONE :
+		__compression_ratio(__lut(width, height), dpb_bpp, scenario);
+
+	opb_compression_factor = !opb_compression_enabled ? FP_ONE :
+		__compression_ratio(__lut(width, height), opb_bpp, scenario);
+
+	vmem_size = 512; /* in kB */
+
+	/* Derived parameters setup */
+	lcu_per_frame = DIV_ROUND_UP(width, lcu_size) *
+		DIV_ROUND_UP(height, lcu_size);
+
+	bitrate = __lut(width, height)->bitrate[scenario];
+
+	bins_to_bit_factor = FP(1, 60, 100);
+
+	dpb_write_factor = scenario == SCENARIO_AVERAGE ?
+		FP_ONE : FP(1, 5, 100);
+
+	ten_bpc_packing_factor = FP(1, 67, 1000);
+	ten_bpc_bpp_factor = FP(1, 1, 4);
+
+	vsp_read_factor = bins_to_bit_factor + FP_INT(2);
+	vsp_write_factor = bins_to_bit_factor;
+
+	tnbr_per_lcu_10bpc = lcu_size == 16 ? 384 + 192 :
+				lcu_size == 32 ? 640 + 256 :
+						1280 + 384;
+	tnbr_per_lcu_8bpc = lcu_size == 16 ? 256 + 192 :
+				lcu_size == 32 ? 512 + 256 :
+						1024 + 384;
+	tnbr_per_lcu = dpb_bpp == 10 ? tnbr_per_lcu_10bpc : tnbr_per_lcu_8bpc;
+
+	colocated_bytes_per_lcu = lcu_size == 16 ? 16 :
+				lcu_size == 32 ? 64 : 256;
+
+	ocmem_usage_lcu_factor = lcu_size == 16 ? FP(1, 8, 10) :
+				lcu_size == 32 ? FP(1, 2, 10) :
+						FP_ONE;
+	ref_ocmem_bw_factor_read = vmem_size < 296 ? FP_ZERO :
+				vmem_size < 648 ? FP(0, 1, 4) :
+						FP(0, 55, 100);
+	ref_ocmem_bw_factor_write = vmem_size < 296 ? FP_ZERO :
+				vmem_size < 648 ? FP(0, 7, 10) :
+						FP(1, 4, 10);
+
+	/* Prelim b/w calculation */
+	bw_for_1x_8bpc = fp_mult(FP_INT(width * height * fps),
+			fp_mult(FP(1, 50, 100), dpb_write_factor));
+	bw_for_1x_8bpc = fp_div(bw_for_1x_8bpc, FP_INT(bps(1)));
+
+	dpb_bw_for_1x = dpb_bpp == 8 ? bw_for_1x_8bpc :
+		fp_mult(bw_for_1x_8bpc, fp_mult(ten_bpc_packing_factor,
+					ten_bpc_bpp_factor));
+	/* VMEM adjustments */
+	vmem_line_buffer = tnbr_per_lcu * DIV_ROUND_UP(width, lcu_size) / 1024;
+	vmem_chroma_cache = dpb_bpp == 10 ? 176 : 128;
+	vmem_luma_cache = dpb_bpp == 10 ? 353 : 256;
+	vmem_chroma_luma_cache = vmem_chroma_cache + vmem_luma_cache;
+
+	motion_vector_complexity = scenario == SCENARIO_AVERAGE ?
+		FP(2, 66, 100) : FP_INT(4);
+
+	row_cache_penalty = FP_ZERO;
+	if (vmem_size < vmem_line_buffer + vmem_chroma_cache)
+		row_cache_penalty = fp_mult(FP(0, 5, 100),
+				motion_vector_complexity);
+	else if (vmem_size < vmem_line_buffer + vmem_luma_cache)
+		row_cache_penalty = fp_mult(FP(0, 7, 100),
+				motion_vector_complexity);
+	else if (vmem_size < vmem_line_buffer + vmem_chroma_cache
+			+ vmem_luma_cache)
+		row_cache_penalty = fp_mult(FP(0, 3, 100),
+				motion_vector_complexity);
+	else
+		row_cache_penalty = FP_ZERO;
+
+
+	opb_bw = unified_dpb_opb ? FP_ZERO :
+		fp_div(fp_div(bw_for_1x_8bpc, dpb_opb_scaling_ratio),
+				opb_compression_factor);
+
+	/* B/W breakdown on a per buffer type basis for VMEM */
+	vmem.vsp_read = FP_ZERO;
+	vmem.vsp_write = FP_ZERO;
+
+	vmem.collocated_read = FP_ZERO;
+	vmem.collocated_write = FP_ZERO;
+
+	vmem.line_buffer_read = FP_INT(tnbr_per_lcu *
+			lcu_per_frame * fps / bps(1));
+	vmem.line_buffer_write = vmem.line_buffer_read;
+
+	vmem.recon_read = FP_ZERO;
+	vmem.recon_write = FP_ZERO;
+
+	vmem.opb_read = FP_ZERO;
+	vmem.opb_write = FP_ZERO;
+
+	vmem.dpb_read = fp_mult(ocmem_usage_lcu_factor, fp_mult(
+					ref_ocmem_bw_factor_read,
+					dpb_bw_for_1x));
+	vmem.dpb_write = fp_mult(ocmem_usage_lcu_factor, fp_mult(
+					ref_ocmem_bw_factor_write,
+					dpb_bw_for_1x));
+
+	vmem.total = vmem.vsp_read + vmem.vsp_write +
+		vmem.collocated_read + vmem.collocated_write +
+		vmem.line_buffer_read + vmem.line_buffer_write +
+		vmem.recon_read + vmem.recon_write +
+		vmem.opb_read + vmem.opb_write +
+		vmem.dpb_read + vmem.dpb_write;
+
+	/*
+	 * Attempt to force VMEM to a certain frequency for 4K
+	 */
+	if (width * height * fps >= 3840 * 2160 * 60)
+		vmem.total = FP_INT(NOMINAL_BW_MBPS);
+	else if (width * height * fps >= 3840 * 2160 * 30)
+		vmem.total = FP_INT(SVS_BW_MBPS);
+
+	/* ........................................ for DDR */
+	ddr.vsp_read = fp_div(fp_mult(FP_INT(bitrate),
+				vsp_read_factor), FP_INT(8));
+	ddr.vsp_write = fp_div(fp_mult(FP_INT(bitrate),
+				vsp_write_factor), FP_INT(8));
+
+	ddr.collocated_read = FP_INT(lcu_per_frame *
+			colocated_bytes_per_lcu * fps / bps(1));
+	ddr.collocated_write = FP_INT(lcu_per_frame *
+			colocated_bytes_per_lcu * fps / bps(1));
+
+	ddr.line_buffer_read = vmem_size ? FP_ZERO : vmem.line_buffer_read;
+	ddr.line_buffer_write = vmem_size ? FP_ZERO : vmem.line_buffer_write;
+
+	ddr.recon_read = FP_ZERO;
+	ddr.recon_write = fp_div(dpb_bw_for_1x, dpb_compression_factor);
+
+	ddr.opb_read = FP_ZERO;
+	ddr.opb_write = opb_bw;
+
+	ddr.dpb_read = fp_div(fp_mult(dpb_bw_for_1x,
+				motion_vector_complexity + row_cache_penalty),
+			dpb_compression_factor);
+	ddr.dpb_write = FP_ZERO;
+
+	ddr.total = ddr.vsp_read + ddr.vsp_write +
+		ddr.collocated_read + ddr.collocated_write +
+		ddr.line_buffer_read + ddr.line_buffer_write +
+		ddr.recon_read + ddr.recon_write +
+		ddr.opb_read + ddr.opb_write +
+		ddr.dpb_read + ddr.dpb_write;
+
+	qsmmu_bw_overhead_factor = FP(1, 3, 100);
+	ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor);
+
+	/* Dump all the variables for easier debugging */
+	if (debug) {
+		struct dump dump[] = {
+		{"DECODER PARAMETERS", "", DUMP_HEADER_MAGIC},
+		{"content", "%d", scenario},
+		{"LCU size", "%d", lcu_size},
+		{"DPB bitdepth", "%d", dpb_bpp},
+		{"frame rate", "%d", fps},
+		{"DPB/OPB unified", "%d", unified_dpb_opb},
+		{"DPB/OPB downscaling ratio", DUMP_FP_FMT,
+			dpb_opb_scaling_ratio},
+		{"DPB compression", "%d", dpb_compression_enabled},
+		{"OPB compression", "%d", opb_compression_enabled},
+		{"DPB compression factor", DUMP_FP_FMT,
+			dpb_compression_factor},
+		{"OPB compression factor", DUMP_FP_FMT,
+			opb_compression_factor},
+		{"VMEM size", "%dkB", vmem_size},
+		{"frame width", "%d", width},
+		{"frame height", "%d", height},
+
+		{"DERIVED PARAMETERS (1)", "", DUMP_HEADER_MAGIC},
+		{"LCUs/frame", "%d", lcu_per_frame},
+		{"bitrate (Mbit/sec)", "%d", bitrate},
+		{"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor},
+		{"DPB write factor", DUMP_FP_FMT, dpb_write_factor},
+		{"10bpc packing factor", DUMP_FP_FMT,
+			ten_bpc_packing_factor},
+		{"10bpc,BPP factor", DUMP_FP_FMT, ten_bpc_bpp_factor},
+		{"VSP read factor", DUMP_FP_FMT, vsp_read_factor},
+		{"VSP write factor", DUMP_FP_FMT, vsp_write_factor},
+		{"TNBR/LCU_10bpc", "%d", tnbr_per_lcu_10bpc},
+		{"TNBR/LCU_8bpc", "%d", tnbr_per_lcu_8bpc},
+		{"TNBR/LCU", "%d", tnbr_per_lcu},
+		{"colocated bytes/LCU", "%d", colocated_bytes_per_lcu},
+		{"OCMEM usage LCU factor", DUMP_FP_FMT,
+			ocmem_usage_lcu_factor},
+		{"ref OCMEM b/w factor (read)", DUMP_FP_FMT,
+			ref_ocmem_bw_factor_read},
+		{"ref OCMEM b/w factor (write)", DUMP_FP_FMT,
+			ref_ocmem_bw_factor_write},
+		{"B/W for 1x (NV12 8bpc)", DUMP_FP_FMT, bw_for_1x_8bpc},
+		{"DPB B/W For 1x (NV12)", DUMP_FP_FMT, dpb_bw_for_1x},
+
+		{"VMEM", "", DUMP_HEADER_MAGIC},
+		{"line buffer", "%d", vmem_line_buffer},
+		{"chroma cache", "%d", vmem_chroma_cache},
+		{"luma cache", "%d", vmem_luma_cache},
+		{"luma & chroma cache", "%d", vmem_chroma_luma_cache},
+
+		{"DERIVED PARAMETERS (2)", "", DUMP_HEADER_MAGIC},
+		{"MV complexity", DUMP_FP_FMT, motion_vector_complexity},
+		{"row cache penalty", DUMP_FP_FMT, row_cache_penalty},
+		{"OPB B/W (single instance)", DUMP_FP_FMT, opb_bw},
+
+		{"INTERMEDIATE DDR B/W", "", DUMP_HEADER_MAGIC},
+		{"VSP read", DUMP_FP_FMT, ddr.vsp_read},
+		{"VSP write", DUMP_FP_FMT, ddr.vsp_write},
+		{"collocated read", DUMP_FP_FMT, ddr.collocated_read},
+		{"collocated write", DUMP_FP_FMT, ddr.collocated_write},
+		{"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read},
+		{"line buffer write", DUMP_FP_FMT, ddr.line_buffer_write},
+		{"recon read", DUMP_FP_FMT, ddr.recon_read},
+		{"recon write", DUMP_FP_FMT, ddr.recon_write},
+		{"OPB read", DUMP_FP_FMT, ddr.opb_read},
+		{"OPB write", DUMP_FP_FMT, ddr.opb_write},
+		{"DPB read", DUMP_FP_FMT, ddr.dpb_read},
+		{"DPB write", DUMP_FP_FMT, ddr.dpb_write},
+
+		{"INTERMEDIATE VMEM B/W", "", DUMP_HEADER_MAGIC},
+		{"VSP read", "%d", vmem.vsp_read},
+		{"VSP write", DUMP_FP_FMT, vmem.vsp_write},
+		{"collocated read", DUMP_FP_FMT, vmem.collocated_read},
+		{"collocated write", DUMP_FP_FMT, vmem.collocated_write},
+		{"line buffer read", DUMP_FP_FMT, vmem.line_buffer_read},
+		{"line buffer write", DUMP_FP_FMT, vmem.line_buffer_write},
+		{"recon read", DUMP_FP_FMT, vmem.recon_read},
+		{"recon write", DUMP_FP_FMT, vmem.recon_write},
+		{"OPB read", DUMP_FP_FMT, vmem.opb_read},
+		{"OPB write", DUMP_FP_FMT, vmem.opb_write},
+		{"DPB read", DUMP_FP_FMT, vmem.dpb_read},
+		{"DPB write", DUMP_FP_FMT, vmem.dpb_write},
+		};
+		__dump(dump, ARRAY_SIZE(dump));
+	}
+
+	switch (gm) {
+	case GOVERNOR_DDR:
+		ret = kbps(fp_round(ddr.total));
+		break;
+	case GOVERNOR_VMEM:
+		ret = kbps(fp_round(vmem.total));
+		break;
+	case GOVERNOR_VMEM_PLUS:
+		ret = __calculate_vmem_plus_ab(d);
+		break;
+	default:
+		dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__);
+	}
+
+	return ret;
+}
+
+static unsigned long __calculate_encoder(struct vidc_bus_vote_data *d,
+		enum governor_mode gm)
+{
+	/*
+	 * XXX: Don't fool around with any of the hardcoded numbers unless you
+	 * know /exactly/ what you're doing.  Many of these numbers are
+	 * measured heuristics and hardcoded numbers taken from the firmware.
+	 */
+	/* Encoder Parameters */
+	enum scenario scenario, bitrate_scenario;
+	enum hal_video_codec standard;
+	int width, height, fps, vmem_size;
+	enum hal_uncompressed_format dpb_color_format;
+	enum hal_uncompressed_format original_color_format;
+	bool dpb_compression_enabled, original_compression_enabled,
+		two_stage_encoding, low_power, rotation, cropping_or_scaling;
+	fp_t dpb_compression_factor, original_compression_factor,
+		qsmmu_bw_overhead_factor;
+	bool b_frames_enabled;
+
+	/* Derived Parameters */
+	int lcu_size;
+	enum gop {
+		GOP_IBBP,
+		GOP_IPPP,
+	} gop;
+	unsigned long bitrate;
+	fp_t bins_to_bit_factor, chroma_luma_factor_dpb, one_frame_bw_dpb,
+		 chroma_luma_factor_original, one_frame_bw_original,
+		 line_buffer_size_per_lcu, line_buffer_size, line_buffer_bw,
+		 original_vmem_requirement, bw_increase_p, bw_increase_b;
+	int collocated_mv_per_lcu, max_transaction_size,
+		search_window_size_vertical_p, search_window_factor_p,
+		search_window_factor_bw_p, vmem_size_p, available_vmem_p,
+		search_window_size_vertical_b, search_window_factor_b,
+		search_window_factor_bw_b, vmem_size_b, available_vmem_b;
+
+	/* Output paramaters */
+	struct {
+		fp_t vsp_read, vsp_write, collocated_read, collocated_write,
+			line_buffer_read, line_buffer_write, original_read,
+			original_write, dpb_read, dpb_write, total;
+	} ddr, vmem;
+
+	unsigned long ret = 0;
+
+	/* Encoder Parameters setup */
+	scenario = SCENARIO_WORST;
+
+	standard = d->codec;
+	width = max(d->width, BASELINE_DIMENSIONS.width);
+	height = max(d->height, BASELINE_DIMENSIONS.height);
+
+	dpb_color_format = HAL_COLOR_FORMAT_NV12_UBWC;
+	original_color_format = d->num_formats >= 1 ?
+		d->color_formats[0] : HAL_UNUSED_COLOR;
+
+	fps = d->fps;
+	bitrate_scenario = SCENARIO_WORST;
+
+	dpb_compression_enabled = __ubwc(dpb_color_format);
+	original_compression_enabled = __ubwc(original_color_format);
+
+	two_stage_encoding = false;
+	low_power = d->power_mode == VIDC_POWER_LOW;
+	b_frames_enabled = false;
+
+	dpb_compression_factor = !dpb_compression_enabled ? FP_ONE :
+		__compression_ratio(__lut(width, height),
+				__bpp(dpb_color_format), scenario);
+	original_compression_factor = !original_compression_enabled ? FP_ONE :
+		__compression_ratio(__lut(width, height),
+				__bpp(original_color_format), scenario);
+
+	rotation = false;
+	cropping_or_scaling = false;
+	vmem_size = 512; /* in kB */
+
+	/* Derived Parameters */
+	lcu_size = 16;
+	gop = b_frames_enabled ? GOP_IBBP : GOP_IPPP;
+	bitrate = __lut(width, height)->bitrate[bitrate_scenario];
+	bins_to_bit_factor = FP(1, 6, 10);
+
+	/*
+	 * FIXME: Minor color format related hack: a lot of the derived params
+	 * depend on the YUV bitdepth as a variable.  However, we don't have
+	 * appropriate enums defined yet (hence no support).  As a result omit
+	 * a lot of the checks (which should look like the snippet below) in
+	 * favour of hardcoding.
+	 *      dpb_color_format == YUV420 ? 0.5 :
+	 *      dpb_color_format == YUV422 ? 1.0 : 2.0
+	 * Similar hacks are annotated inline in code with the string "CF hack"
+	 * for documentation purposes.
+	 */
+	chroma_luma_factor_dpb = FP(0, 1, 2);
+	one_frame_bw_dpb = fp_mult(FP_ONE + chroma_luma_factor_dpb,
+			fp_div(FP_INT(width * height * fps),
+				FP_INT(1000 * 1000)));
+
+	chroma_luma_factor_original = FP(0, 1, 2); /* XXX: CF hack */
+	one_frame_bw_original = fp_mult(FP_ONE + chroma_luma_factor_original,
+			fp_div(FP_INT(width * height * fps),
+				FP_INT(1000 * 1000)));
+
+	line_buffer_size_per_lcu = FP_ZERO;
+	if (lcu_size == 16)
+		line_buffer_size_per_lcu = FP_INT(128) + fp_mult(FP_INT(256),
+					FP_ONE /*XXX: CF hack */);
+	else
+		line_buffer_size_per_lcu = FP_INT(192) + fp_mult(FP_INT(512),
+					FP_ONE /*XXX: CF hack */);
+
+	line_buffer_size = fp_div(
+			fp_mult(FP_INT(width / lcu_size),
+				line_buffer_size_per_lcu),
+			FP_INT(1024));
+	line_buffer_bw = fp_mult(line_buffer_size,
+			fp_div(FP_INT((height / lcu_size /
+				(two_stage_encoding ? 2 : 1) - 1) * fps),
+				FP_INT(1000)));
+
+	collocated_mv_per_lcu = lcu_size == 16 ? 16 : 64;
+	max_transaction_size = 256;
+
+	original_vmem_requirement = FP_INT(3 *
+			(two_stage_encoding ? 2 : 1) * lcu_size);
+	original_vmem_requirement = fp_mult(original_vmem_requirement,
+			(FP_ONE + chroma_luma_factor_original));
+	original_vmem_requirement += FP_INT((cropping_or_scaling ? 3 : 0) * 2);
+	original_vmem_requirement = fp_mult(original_vmem_requirement,
+			FP_INT(max_transaction_size));
+	original_vmem_requirement = fp_div(original_vmem_requirement,
+			FP_INT(1024));
+
+	search_window_size_vertical_p = low_power ? 32 :
+					b_frames_enabled ? 80 :
+					width > 2048 ? 64 : 48;
+	search_window_factor_p = search_window_size_vertical_p * 2 / lcu_size;
+	search_window_factor_bw_p = !two_stage_encoding ?
+		search_window_size_vertical_p * 2 / lcu_size + 1 :
+		(search_window_size_vertical_p * 2 / lcu_size + 2) / 2;
+	vmem_size_p = (search_window_factor_p * width + 128 * 2) *
+		lcu_size / 2 / 1024; /* XXX: CF hack */
+	bw_increase_p = fp_mult(one_frame_bw_dpb,
+			FP_INT(search_window_factor_bw_p - 1) / 3);
+	available_vmem_p = min_t(int, 3, (vmem_size - fp_int(line_buffer_size) -
+			fp_int(original_vmem_requirement)) / vmem_size_p);
+
+	search_window_size_vertical_b = 48;
+	search_window_factor_b = search_window_size_vertical_b * 2 / lcu_size;
+	search_window_factor_bw_b = !two_stage_encoding ?
+		search_window_size_vertical_b * 2 / lcu_size + 1 :
+		(search_window_size_vertical_b * 2 / lcu_size + 2) / 2;
+	vmem_size_b = (search_window_factor_b * width + 128 * 2) * lcu_size /
+		2 / 1024;
+	bw_increase_b = fp_mult(one_frame_bw_dpb,
+			FP_INT((search_window_factor_bw_b - 1) / 3));
+	available_vmem_b = min_t(int, 6, (vmem_size - fp_int(line_buffer_size) -
+			fp_int(original_vmem_requirement)) / vmem_size_b);
+
+	/* Output parameters for DDR */
+	ddr.vsp_read = fp_mult(fp_div(FP_INT(bitrate), FP_INT(8)),
+			bins_to_bit_factor);
+	ddr.vsp_write = ddr.vsp_read + fp_div(FP_INT(bitrate), FP_INT(8));
+
+	ddr.collocated_read = fp_div(FP_INT(DIV_ROUND_UP(width, lcu_size) *
+			DIV_ROUND_UP(height, lcu_size) *
+			collocated_mv_per_lcu * fps), FP_INT(1000 * 1000));
+	ddr.collocated_write = ddr.collocated_read;
+
+	ddr.line_buffer_read = (FP_INT(vmem_size) >= line_buffer_size +
+		original_vmem_requirement) ? FP_ZERO : line_buffer_bw;
+	ddr.line_buffer_write = ddr.line_buffer_read;
+
+	ddr.original_read = fp_div(one_frame_bw_original,
+			original_compression_factor);
+	ddr.original_write = FP_ZERO;
+
+	ddr.dpb_read = FP_ZERO;
+	if (gop == GOP_IPPP) {
+		ddr.dpb_read = one_frame_bw_dpb + fp_mult(bw_increase_p,
+			FP_INT(3 - available_vmem_p));
+	} else if (scenario == SCENARIO_WORST ||
+			scenario == SCENARIO_SUSTAINED_WORST) {
+		ddr.dpb_read = fp_mult(one_frame_bw_dpb, FP_INT(2));
+		ddr.dpb_read += fp_mult(FP_INT(6 - available_vmem_b),
+				bw_increase_b);
+	} else {
+		fp_t part_p, part_b;
+
+		part_p = one_frame_bw_dpb + fp_mult(bw_increase_p,
+				FP_INT(3 - available_vmem_p));
+		part_p = fp_div(part_p, FP_INT(3));
+
+		part_b = fp_mult(one_frame_bw_dpb, 2) +
+			fp_mult(FP_INT(6 - available_vmem_b), bw_increase_b);
+		part_b = fp_mult(part_b, FP(0, 2, 3));
+
+		ddr.dpb_read = part_p + part_b;
+	}
+
+	ddr.dpb_read = fp_div(ddr.dpb_read, dpb_compression_factor);
+	ddr.dpb_write = fp_div(one_frame_bw_dpb, dpb_compression_factor);
+
+	ddr.total = ddr.vsp_read + ddr.vsp_write +
+		ddr.collocated_read + ddr.collocated_write +
+		ddr.line_buffer_read + ddr.line_buffer_write +
+		ddr.original_read + ddr.original_write +
+		ddr.dpb_read + ddr.dpb_write;
+
+	qsmmu_bw_overhead_factor = FP(1, 3, 100);
+	ddr.total = fp_mult(ddr.total, qsmmu_bw_overhead_factor);
+
+	/* ................. for VMEM */
+	vmem.vsp_read = FP_ZERO;
+	vmem.vsp_write = FP_ZERO;
+
+	vmem.collocated_read = FP_ZERO;
+	vmem.collocated_write = FP_ZERO;
+
+	vmem.line_buffer_read = line_buffer_bw - ddr.line_buffer_read;
+	vmem.line_buffer_write = vmem.line_buffer_read;
+
+	vmem.original_read = FP_INT(vmem_size) >= original_vmem_requirement ?
+		ddr.original_read : FP_ZERO;
+	vmem.original_write = vmem.original_read;
+
+	vmem.dpb_read = FP_ZERO;
+	if (gop == GOP_IPPP) {
+		fp_t temp = fp_mult(one_frame_bw_dpb,
+			FP_INT(search_window_factor_bw_p * available_vmem_p));
+		temp = fp_div(temp, FP_INT(3));
+
+		vmem.dpb_read = temp;
+	} else if (scenario != SCENARIO_AVERAGE) {
+		fp_t temp = fp_mult(one_frame_bw_dpb, FP_INT(2));
+
+		temp = fp_mult(temp, FP_INT(search_window_factor_bw_b *
+					available_vmem_b));
+		temp = fp_div(temp, FP_INT(6));
+
+		vmem.dpb_read = temp;
+	} else {
+		fp_t part_p, part_b;
+
+		part_p = fp_mult(one_frame_bw_dpb, FP_INT(
+					search_window_factor_bw_p *
+					available_vmem_p));
+		part_p = fp_div(part_p, FP_INT(3 * 3));
+
+		part_b = fp_mult(one_frame_bw_dpb, FP_INT(2 *
+					search_window_factor_bw_b *
+					available_vmem_b));
+		part_b = fp_div(part_b, FP_INT(6));
+		part_b = fp_mult(part_b, FP(0, 2, 3));
+
+		vmem.dpb_read = part_p + part_b;
+	}
+
+	vmem.dpb_write = FP_ZERO;
+	if (gop == GOP_IPPP) {
+		fp_t temp = fp_mult(one_frame_bw_dpb,
+				FP_INT(available_vmem_p));
+		temp = fp_div(temp, FP_INT(3));
+
+		vmem.dpb_write = temp;
+	} else if (scenario != SCENARIO_AVERAGE) {
+		fp_t temp = fp_mult(one_frame_bw_dpb,
+				FP_INT(2 * available_vmem_b));
+		temp = fp_div(temp, FP_INT(6));
+
+		vmem.dpb_write = temp;
+	} else {
+		fp_t part_b, part_p;
+
+		part_b = fp_mult(one_frame_bw_dpb, FP_INT(available_vmem_p));
+		part_b = fp_div(part_b, FP_INT(9));
+
+		part_p = fp_mult(one_frame_bw_dpb, FP_INT(
+					2 * available_vmem_b));
+		part_p = fp_div(part_p, FP_INT(6));
+		part_b = fp_mult(part_b, FP(0, 2, 3));
+
+		vmem.dpb_write = part_p + part_b;
+	}
+
+	vmem.total = vmem.vsp_read + vmem.vsp_write +
+		vmem.collocated_read + vmem.collocated_write +
+		vmem.line_buffer_read + vmem.line_buffer_write +
+		vmem.original_read + vmem.original_write +
+		vmem.dpb_read + vmem.dpb_write;
+
+	/*
+	 * When in low power mode, attempt to force the VMEM clocks a certain
+	 * frequency that DCVS would prefer
+	 */
+	if (width * height >= 3840 * 2160 && low_power)
+		vmem.total = FP_INT(NOMINAL_BW_MBPS);
+
+	if (debug) {
+		struct dump dump[] = {
+		{"ENCODER PARAMETERS", "", DUMP_HEADER_MAGIC},
+		{"scenario", "%d", scenario},
+		{"standard", "%#x", standard},
+		{"width", "%d", width},
+		{"height", "%d", height},
+		{"DPB format", "%#x", dpb_color_format},
+		{"original frame format", "%#x", original_color_format},
+		{"fps", "%d", fps},
+		{"target bitrate", "%d", bitrate_scenario},
+		{"DPB compression enable", "%d", dpb_compression_enabled},
+		{"original compression enable", "%d",
+			original_compression_enabled},
+		{"two stage encoding", "%d", two_stage_encoding},
+		{"low power mode", "%d", low_power},
+		{"DPB compression factor", DUMP_FP_FMT,
+			dpb_compression_factor},
+		{"original compression factor", DUMP_FP_FMT,
+			original_compression_factor},
+		{"rotation", "%d", rotation},
+		{"cropping or scaling", "%d", cropping_or_scaling},
+		{"VMEM size (KB)", "%d", vmem_size},
+
+		{"DERIVED PARAMETERS", "", DUMP_HEADER_MAGIC},
+		{"LCU size", "%d", lcu_size},
+		{"GOB pattern", "%d", gop},
+		{"bitrate (Mbit/sec)", "%lu", bitrate},
+		{"bins to bit factor", DUMP_FP_FMT, bins_to_bit_factor},
+		{"B-frames enabled", "%d", b_frames_enabled},
+		{"search window size vertical (B)", "%d",
+			search_window_size_vertical_b},
+		{"search window factor (B)", "%d", search_window_factor_b},
+		{"search window factor BW (B)", "%d",
+			search_window_factor_bw_b},
+		{"VMEM size (B)", "%d", vmem_size_b},
+		{"bw increase (MB/s) (B)", DUMP_FP_FMT, bw_increase_b},
+		{"available VMEM (B)", "%d", available_vmem_b},
+		{"search window size vertical (P)", "%d",
+			search_window_size_vertical_p},
+		{"search window factor (P)", "%d", search_window_factor_p},
+		{"search window factor BW (P)", "%d",
+			search_window_factor_bw_p},
+		{"VMEM size (P)", "%d", vmem_size_p},
+		{"bw increase (MB/s) (P)", DUMP_FP_FMT, bw_increase_p},
+		{"available VMEM (P)", "%d", available_vmem_p},
+		{"chroma/luma factor DPB", DUMP_FP_FMT,
+			chroma_luma_factor_dpb},
+		{"one frame BW DPB (MB/s)", DUMP_FP_FMT, one_frame_bw_dpb},
+		{"chroma/Luma factor original", DUMP_FP_FMT,
+			chroma_luma_factor_original},
+		{"one frame BW original (MB/s)", DUMP_FP_FMT,
+			one_frame_bw_original},
+		{"line buffer size per LCU", DUMP_FP_FMT,
+			line_buffer_size_per_lcu},
+		{"line buffer size (KB)", DUMP_FP_FMT, line_buffer_size},
+		{"line buffer BW (MB/s)", DUMP_FP_FMT, line_buffer_bw},
+		{"collocated MVs per LCU", "%d", collocated_mv_per_lcu},
+		{"original VMEM requirement (KB)", DUMP_FP_FMT,
+			original_vmem_requirement},
+
+		{"INTERMEDIATE B/W DDR", "", DUMP_HEADER_MAGIC},
+		{"VSP read", DUMP_FP_FMT, ddr.vsp_read},
+		{"VSP read", DUMP_FP_FMT, ddr.vsp_write},
+		{"collocated read", DUMP_FP_FMT, ddr.collocated_read},
+		{"collocated read", DUMP_FP_FMT, ddr.collocated_write},
+		{"line buffer read", DUMP_FP_FMT, ddr.line_buffer_read},
+		{"line buffer read", DUMP_FP_FMT, ddr.line_buffer_write},
+		{"original read", DUMP_FP_FMT, ddr.original_read},
+		{"original read", DUMP_FP_FMT, ddr.original_write},
+		{"DPB read", DUMP_FP_FMT, ddr.dpb_read},
+		{"DPB write", DUMP_FP_FMT, ddr.dpb_write},
+
+		{"INTERMEDIATE B/W VMEM", "", DUMP_HEADER_MAGIC},
+		{"VSP read", DUMP_FP_FMT, vmem.vsp_read},
+		{"VSP read", DUMP_FP_FMT, vmem.vsp_write},
+		{"collocated read", DUMP_FP_FMT, vmem.collocated_read},
+		{"collocated read", DUMP_FP_FMT, vmem.collocated_write},
+		{"line buffer read", DUMP_FP_FMT, vmem.line_buffer_read},
+		{"line buffer read", DUMP_FP_FMT, vmem.line_buffer_write},
+		{"original read", DUMP_FP_FMT, vmem.original_read},
+		{"original read", DUMP_FP_FMT, vmem.original_write},
+		{"DPB read", DUMP_FP_FMT, vmem.dpb_read},
+		{"DPB write", DUMP_FP_FMT, vmem.dpb_write},
+		};
+		__dump(dump, ARRAY_SIZE(dump));
+	}
+
+	switch (gm) {
+	case GOVERNOR_DDR:
+		ret = kbps(fp_round(ddr.total));
+		break;
+	case GOVERNOR_VMEM:
+		ret = kbps(fp_round(vmem.total));
+		break;
+	case GOVERNOR_VMEM_PLUS:
+		ret = __calculate_vmem_plus_ab(d);
+		break;
+	default:
+		dprintk(VIDC_ERR, "%s - Unknown governor\n", __func__);
+	}
+
+	return ret;
+}
+
+static unsigned long __calculate(struct vidc_bus_vote_data *d,
+		enum governor_mode gm)
+{
+	unsigned long (*calc[])(struct vidc_bus_vote_data *,
+			enum governor_mode) = {
+		[HAL_VIDEO_DOMAIN_VPE] = __calculate_vpe,
+		[HAL_VIDEO_DOMAIN_ENCODER] = __calculate_encoder,
+		[HAL_VIDEO_DOMAIN_DECODER] = __calculate_decoder,
+	};
+
+	return calc[d->domain](d, gm);
+}
+
+
+static int __get_target_freq(struct devfreq *dev, unsigned long *freq)
+{
+	unsigned long ab_kbps = 0, c = 0;
+	struct devfreq_dev_status stats = {0};
+	struct msm_vidc_gov_data *vidc_data = NULL;
+	struct governor *gov = NULL;
+
+	if (!dev || !freq)
+		return -EINVAL;
+
+	gov = container_of(dev->governor,
+			struct governor, devfreq_gov);
+	dev->profile->get_dev_status(dev->dev.parent, &stats);
+	vidc_data = (struct msm_vidc_gov_data *)stats.private_data;
+
+	for (c = 0; c < vidc_data->data_count; ++c) {
+		if (vidc_data->data->power_mode == VIDC_POWER_TURBO) {
+			*freq = INT_MAX;
+			goto exit;
+		}
+	}
+
+	for (c = 0; c < vidc_data->data_count; ++c)
+		ab_kbps += __calculate(&vidc_data->data[c], gov->mode);
+
+	*freq = clamp(ab_kbps, dev->min_freq, dev->max_freq ?: UINT_MAX);
+exit:
+	return 0;
+}
+
+static int __event_handler(struct devfreq *devfreq, unsigned int event,
+		void *data)
+{
+	int rc = 0;
+
+	if (!devfreq)
+		return -EINVAL;
+
+	switch (event) {
+	case DEVFREQ_GOV_START:
+	case DEVFREQ_GOV_RESUME:
+		mutex_lock(&devfreq->lock);
+		rc = update_devfreq(devfreq);
+		mutex_unlock(&devfreq->lock);
+		break;
+	}
+
+	return rc;
+}
+
+static struct governor governors[] = {
+	{
+		.mode = GOVERNOR_DDR,
+		.devfreq_gov = {
+			.name = "msm-vidc-ddr",
+			.get_target_freq = __get_target_freq,
+			.event_handler = __event_handler,
+		},
+	},
+	{
+		.mode = GOVERNOR_VMEM,
+		.devfreq_gov = {
+			.name = "msm-vidc-vmem",
+			.get_target_freq = __get_target_freq,
+			.event_handler = __event_handler,
+		},
+	},
+	{
+		.mode = GOVERNOR_VMEM_PLUS,
+		.devfreq_gov = {
+			.name = "msm-vidc-vmem+",
+			.get_target_freq = __get_target_freq,
+			.event_handler = __event_handler,
+		},
+	},
+};
+
+static int __init msm_vidc_bw_gov_init(void)
+{
+	int c = 0, rc = 0;
+
+	for (c = 0; c < ARRAY_SIZE(governors); ++c) {
+		dprintk(VIDC_DBG, "Adding governor %s\n",
+				governors[c].devfreq_gov.name);
+
+		rc = devfreq_add_governor(&governors[c].devfreq_gov);
+		if (rc) {
+			dprintk(VIDC_ERR, "Error adding governor %s: %d\n",
+				governors[c].devfreq_gov.name, rc);
+			break;
+		}
+	}
+
+	return rc;
+}
+module_init(msm_vidc_bw_gov_init);
+
+static void __exit msm_vidc_bw_gov_exit(void)
+{
+	int c = 0;
+
+	for (c = 0; c < ARRAY_SIZE(governors); ++c) {
+		dprintk(VIDC_DBG, "Removing governor %s\n",
+				governors[c].devfreq_gov.name);
+		devfreq_remove_governor(&governors[c].devfreq_gov);
+	}
+}
+module_exit(msm_vidc_bw_gov_exit);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.c b/drivers/media/platform/msm/vidc/hfi_packetization.c
new file mode 100644
index 0000000..f75a33fa
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.c
@@ -0,0 +1,2568 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/errno.h>
+#include <linux/log2.h>
+#include <linux/hash.h>
+#include "hfi_packetization.h"
+#include "msm_vidc_debug.h"
+
+/* Set up look-up tables to convert HAL_* to HFI_*.
+ *
+ * The tables below mostly take advantage of the fact that most
+ * HAL_* types are defined bitwise. So if we index them normally
+ * when declaring the tables, we end up with huge arrays with wasted
+ * space.  So before indexing them, we apply log2 to use a more
+ * sensible index.
+ */
+static int profile_table[] = {
+	[ilog2(HAL_H264_PROFILE_BASELINE)] = HFI_H264_PROFILE_BASELINE,
+	[ilog2(HAL_H264_PROFILE_MAIN)] = HFI_H264_PROFILE_MAIN,
+	[ilog2(HAL_H264_PROFILE_HIGH)] = HFI_H264_PROFILE_HIGH,
+	[ilog2(HAL_H264_PROFILE_CONSTRAINED_BASE)] =
+		HFI_H264_PROFILE_CONSTRAINED_BASE,
+	[ilog2(HAL_H264_PROFILE_CONSTRAINED_HIGH)] =
+		HFI_H264_PROFILE_CONSTRAINED_HIGH,
+	[ilog2(HAL_VPX_PROFILE_VERSION_1)] = HFI_VPX_PROFILE_VERSION_1,
+	[ilog2(HAL_MVC_PROFILE_STEREO_HIGH)] = HFI_H264_PROFILE_STEREO_HIGH,
+};
+
+static int entropy_mode[] = {
+	[ilog2(HAL_H264_ENTROPY_CAVLC)] = HFI_H264_ENTROPY_CAVLC,
+	[ilog2(HAL_H264_ENTROPY_CABAC)] = HFI_H264_ENTROPY_CABAC,
+};
+
+static int cabac_model[] = {
+	[ilog2(HAL_H264_CABAC_MODEL_0)] = HFI_H264_CABAC_MODEL_0,
+	[ilog2(HAL_H264_CABAC_MODEL_1)] = HFI_H264_CABAC_MODEL_1,
+	[ilog2(HAL_H264_CABAC_MODEL_2)] = HFI_H264_CABAC_MODEL_2,
+};
+
+static int statistics_mode[] = {
+	[ilog2(HAL_STATISTICS_MODE_DEFAULT)] = HFI_STATISTICS_MODE_DEFAULT,
+	[ilog2(HAL_STATISTICS_MODE_1)] = HFI_STATISTICS_MODE_1,
+	[ilog2(HAL_STATISTICS_MODE_2)] = HFI_STATISTICS_MODE_2,
+	[ilog2(HAL_STATISTICS_MODE_3)] = HFI_STATISTICS_MODE_3,
+};
+
+static int color_format[] = {
+	[ilog2(HAL_COLOR_FORMAT_MONOCHROME)] = HFI_COLOR_FORMAT_MONOCHROME,
+	[ilog2(HAL_COLOR_FORMAT_NV12)] = HFI_COLOR_FORMAT_NV12,
+	[ilog2(HAL_COLOR_FORMAT_NV21)] = HFI_COLOR_FORMAT_NV21,
+	[ilog2(HAL_COLOR_FORMAT_NV12_4x4TILE)] = HFI_COLOR_FORMAT_NV12_4x4TILE,
+	[ilog2(HAL_COLOR_FORMAT_NV21_4x4TILE)] = HFI_COLOR_FORMAT_NV21_4x4TILE,
+	[ilog2(HAL_COLOR_FORMAT_YUYV)] = HFI_COLOR_FORMAT_YUYV,
+	[ilog2(HAL_COLOR_FORMAT_YVYU)] = HFI_COLOR_FORMAT_YVYU,
+	[ilog2(HAL_COLOR_FORMAT_UYVY)] = HFI_COLOR_FORMAT_UYVY,
+	[ilog2(HAL_COLOR_FORMAT_VYUY)] = HFI_COLOR_FORMAT_VYUY,
+	[ilog2(HAL_COLOR_FORMAT_RGB565)] = HFI_COLOR_FORMAT_RGB565,
+	[ilog2(HAL_COLOR_FORMAT_BGR565)] = HFI_COLOR_FORMAT_BGR565,
+	[ilog2(HAL_COLOR_FORMAT_RGB888)] = HFI_COLOR_FORMAT_RGB888,
+	[ilog2(HAL_COLOR_FORMAT_BGR888)] = HFI_COLOR_FORMAT_BGR888,
+	[ilog2(HAL_COLOR_FORMAT_RGBA8888)] = HFI_COLOR_FORMAT_RGBA8888,
+	/* UBWC Color formats*/
+	[ilog2(HAL_COLOR_FORMAT_NV12_UBWC)] =  HFI_COLOR_FORMAT_NV12_UBWC,
+	[ilog2(HAL_COLOR_FORMAT_NV12_TP10_UBWC)] =
+			HFI_COLOR_FORMAT_YUV420_TP10_UBWC,
+	[ilog2(HAL_COLOR_FORMAT_RGBA8888_UBWC)] =
+			HFI_COLOR_FORMAT_RGBA8888_UBWC,
+};
+
+static int nal_type[] = {
+	[ilog2(HAL_NAL_FORMAT_STARTCODES)] = HFI_NAL_FORMAT_STARTCODES,
+	[ilog2(HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER)] =
+		HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER,
+	[ilog2(HAL_NAL_FORMAT_ONE_BYTE_LENGTH)] =
+		HFI_NAL_FORMAT_ONE_BYTE_LENGTH,
+	[ilog2(HAL_NAL_FORMAT_TWO_BYTE_LENGTH)] =
+		HFI_NAL_FORMAT_TWO_BYTE_LENGTH,
+	[ilog2(HAL_NAL_FORMAT_FOUR_BYTE_LENGTH)] =
+		HFI_NAL_FORMAT_FOUR_BYTE_LENGTH,
+};
+
+static inline int hal_to_hfi_type(int property, int hal_type)
+{
+	if (hal_type <= 0 || roundup_pow_of_two(hal_type) != hal_type) {
+		/*
+		 * Not a power of 2, it's not going
+		 * to be in any of the tables anyway
+		 */
+		return -EINVAL;
+	}
+
+	if (hal_type)
+		hal_type = ilog2(hal_type);
+
+	switch (property) {
+	case HAL_PARAM_PROFILE_LEVEL_CURRENT:
+		return (hal_type >= ARRAY_SIZE(profile_table)) ?
+			-ENOTSUPP : profile_table[hal_type];
+	case HAL_PARAM_VENC_H264_ENTROPY_CONTROL:
+		return (hal_type >= ARRAY_SIZE(entropy_mode)) ?
+			-ENOTSUPP : entropy_mode[hal_type];
+	case HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL:
+		return (hal_type >= ARRAY_SIZE(cabac_model)) ?
+			-ENOTSUPP : cabac_model[hal_type];
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT:
+		return (hal_type >= ARRAY_SIZE(color_format)) ?
+			-ENOTSUPP : color_format[hal_type];
+	case HAL_PARAM_NAL_STREAM_FORMAT_SELECT:
+		return (hal_type >= ARRAY_SIZE(nal_type)) ?
+			-ENOTSUPP : nal_type[hal_type];
+	case HAL_PARAM_VENC_MBI_STATISTICS_MODE:
+		return (hal_type >= ARRAY_SIZE(statistics_mode)) ?
+			-ENOTSUPP : statistics_mode[hal_type];
+	default:
+		return -ENOTSUPP;
+	}
+}
+
+u32 get_hfi_layout(enum hal_buffer_layout_type hal_buf_layout)
+{
+	u32 hfi_layout;
+
+	switch (hal_buf_layout) {
+	case HAL_BUFFER_LAYOUT_TOP_BOTTOM:
+		hfi_layout = HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM;
+		break;
+	case HAL_BUFFER_LAYOUT_SEQ:
+		hfi_layout = HFI_MVC_BUFFER_LAYOUT_SEQ;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid buffer layout: %#x\n",
+			hal_buf_layout);
+		hfi_layout = HFI_MVC_BUFFER_LAYOUT_SEQ;
+		break;
+	}
+	return hfi_layout;
+}
+
+enum hal_domain vidc_get_hal_domain(u32 hfi_domain)
+{
+	enum hal_domain hal_domain = 0;
+
+	switch (hfi_domain) {
+	case HFI_VIDEO_DOMAIN_VPE:
+		hal_domain = HAL_VIDEO_DOMAIN_VPE;
+		break;
+	case HFI_VIDEO_DOMAIN_ENCODER:
+		hal_domain = HAL_VIDEO_DOMAIN_ENCODER;
+		break;
+	case HFI_VIDEO_DOMAIN_DECODER:
+		hal_domain = HAL_VIDEO_DOMAIN_DECODER;
+		break;
+	default:
+		dprintk(VIDC_ERR, "%s: invalid domain %x\n",
+			__func__, hfi_domain);
+		hal_domain = 0;
+		break;
+	}
+	return hal_domain;
+}
+
+enum hal_video_codec vidc_get_hal_codec(u32 hfi_codec)
+{
+	enum hal_video_codec hal_codec = 0;
+
+	switch (hfi_codec) {
+	case HFI_VIDEO_CODEC_H264:
+		hal_codec = HAL_VIDEO_CODEC_H264;
+		break;
+	case HFI_VIDEO_CODEC_H263:
+		hal_codec = HAL_VIDEO_CODEC_H263;
+		break;
+	case HFI_VIDEO_CODEC_MPEG1:
+		hal_codec = HAL_VIDEO_CODEC_MPEG1;
+		break;
+	case HFI_VIDEO_CODEC_MPEG2:
+		hal_codec = HAL_VIDEO_CODEC_MPEG2;
+		break;
+	case HFI_VIDEO_CODEC_MPEG4:
+		hal_codec = HAL_VIDEO_CODEC_MPEG4;
+		break;
+	case HFI_VIDEO_CODEC_DIVX_311:
+		hal_codec = HAL_VIDEO_CODEC_DIVX_311;
+		break;
+	case HFI_VIDEO_CODEC_DIVX:
+		hal_codec = HAL_VIDEO_CODEC_DIVX;
+		break;
+	case HFI_VIDEO_CODEC_VC1:
+		hal_codec = HAL_VIDEO_CODEC_VC1;
+		break;
+	case HFI_VIDEO_CODEC_SPARK:
+		hal_codec = HAL_VIDEO_CODEC_SPARK;
+		break;
+	case HFI_VIDEO_CODEC_VP8:
+		hal_codec = HAL_VIDEO_CODEC_VP8;
+		break;
+	case HFI_VIDEO_CODEC_HEVC:
+		hal_codec = HAL_VIDEO_CODEC_HEVC;
+		break;
+	case HFI_VIDEO_CODEC_VP9:
+		hal_codec = HAL_VIDEO_CODEC_VP9;
+		break;
+	case HFI_VIDEO_CODEC_HEVC_HYBRID:
+		hal_codec = HAL_VIDEO_CODEC_HEVC_HYBRID;
+		break;
+	default:
+		dprintk(VIDC_INFO, "%s: invalid codec 0x%x\n",
+			__func__, hfi_codec);
+		hal_codec = 0;
+		break;
+	}
+	return hal_codec;
+}
+
+
+u32 vidc_get_hfi_domain(enum hal_domain hal_domain)
+{
+	u32 hfi_domain;
+
+	switch (hal_domain) {
+	case HAL_VIDEO_DOMAIN_VPE:
+		hfi_domain = HFI_VIDEO_DOMAIN_VPE;
+		break;
+	case HAL_VIDEO_DOMAIN_ENCODER:
+		hfi_domain = HFI_VIDEO_DOMAIN_ENCODER;
+		break;
+	case HAL_VIDEO_DOMAIN_DECODER:
+		hfi_domain = HFI_VIDEO_DOMAIN_DECODER;
+		break;
+	default:
+		dprintk(VIDC_ERR, "%s: invalid domain 0x%x\n",
+			__func__, hal_domain);
+		hfi_domain = 0;
+		break;
+	}
+	return hfi_domain;
+}
+
+u32 vidc_get_hfi_codec(enum hal_video_codec hal_codec)
+{
+	u32 hfi_codec = 0;
+
+	switch (hal_codec) {
+	case HAL_VIDEO_CODEC_MVC:
+	case HAL_VIDEO_CODEC_H264:
+		hfi_codec = HFI_VIDEO_CODEC_H264;
+		break;
+	case HAL_VIDEO_CODEC_H263:
+		hfi_codec = HFI_VIDEO_CODEC_H263;
+		break;
+	case HAL_VIDEO_CODEC_MPEG1:
+		hfi_codec = HFI_VIDEO_CODEC_MPEG1;
+		break;
+	case HAL_VIDEO_CODEC_MPEG2:
+		hfi_codec = HFI_VIDEO_CODEC_MPEG2;
+		break;
+	case HAL_VIDEO_CODEC_MPEG4:
+		hfi_codec = HFI_VIDEO_CODEC_MPEG4;
+		break;
+	case HAL_VIDEO_CODEC_DIVX_311:
+		hfi_codec = HFI_VIDEO_CODEC_DIVX_311;
+		break;
+	case HAL_VIDEO_CODEC_DIVX:
+		hfi_codec = HFI_VIDEO_CODEC_DIVX;
+		break;
+	case HAL_VIDEO_CODEC_VC1:
+		hfi_codec = HFI_VIDEO_CODEC_VC1;
+		break;
+	case HAL_VIDEO_CODEC_SPARK:
+		hfi_codec = HFI_VIDEO_CODEC_SPARK;
+		break;
+	case HAL_VIDEO_CODEC_VP8:
+		hfi_codec = HFI_VIDEO_CODEC_VP8;
+		break;
+	case HAL_VIDEO_CODEC_HEVC:
+		hfi_codec = HFI_VIDEO_CODEC_HEVC;
+		break;
+	case HAL_VIDEO_CODEC_VP9:
+		hfi_codec = HFI_VIDEO_CODEC_VP9;
+		break;
+	case HAL_VIDEO_CODEC_HEVC_HYBRID:
+		hfi_codec = HFI_VIDEO_CODEC_HEVC_HYBRID;
+		break;
+	default:
+		dprintk(VIDC_INFO, "%s: invalid codec 0x%x\n",
+			__func__, hal_codec);
+		hfi_codec = 0;
+		break;
+	}
+	return hfi_codec;
+}
+
+static void create_pkt_enable(void *pkt, u32 type, bool enable)
+{
+	u32 *pkt_header = pkt;
+	u32 *pkt_type = &pkt_header[0];
+	struct hfi_enable *hfi_enable = (struct hfi_enable *)&pkt_header[1];
+
+	*pkt_type = type;
+	hfi_enable->enable = enable;
+}
+
+int create_pkt_cmd_sys_init(struct hfi_cmd_sys_init_packet *pkt,
+			   u32 arch_type)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->packet_type = HFI_CMD_SYS_INIT;
+	pkt->size = sizeof(struct hfi_cmd_sys_init_packet);
+	pkt->arch_type = arch_type;
+	return rc;
+}
+
+int create_pkt_cmd_sys_pc_prep(struct hfi_cmd_sys_pc_prep_packet *pkt)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->packet_type = HFI_CMD_SYS_PC_PREP;
+	pkt->size = sizeof(struct hfi_cmd_sys_pc_prep_packet);
+	return rc;
+}
+
+int create_pkt_cmd_sys_idle_indicator(
+	struct hfi_cmd_sys_set_property_packet *pkt,
+	u32 enable)
+{
+	struct hfi_enable *hfi;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+		sizeof(struct hfi_enable) + sizeof(u32);
+	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IDLE_INDICATOR;
+	hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+	hfi->enable = enable;
+	return 0;
+}
+
+int create_pkt_cmd_sys_debug_config(
+	struct hfi_cmd_sys_set_property_packet *pkt,
+	u32 mode)
+{
+	struct hfi_debug_config *hfi;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+		sizeof(struct hfi_debug_config) + sizeof(u32);
+	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_DEBUG_CONFIG;
+	hfi = (struct hfi_debug_config *) &pkt->rg_property_data[1];
+	hfi->debug_config = mode;
+	hfi->debug_mode = HFI_DEBUG_MODE_QUEUE;
+	if (msm_vidc_fw_debug_mode
+			<= (HFI_DEBUG_MODE_QUEUE | HFI_DEBUG_MODE_QDSS))
+		hfi->debug_mode = msm_vidc_fw_debug_mode;
+	return 0;
+}
+
+int create_pkt_cmd_sys_coverage_config(
+	struct hfi_cmd_sys_set_property_packet *pkt,
+	u32 mode)
+{
+	if (!pkt) {
+		dprintk(VIDC_ERR, "In %s(), No input packet\n", __func__);
+		return -EINVAL;
+	}
+
+	pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+		sizeof(u32);
+	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CONFIG_COVERAGE;
+	pkt->rg_property_data[1] = mode;
+	dprintk(VIDC_DBG, "Firmware coverage mode %d\n",
+			pkt->rg_property_data[1]);
+	return 0;
+}
+
+int create_pkt_cmd_sys_set_resource(
+		struct hfi_cmd_sys_set_resource_packet *pkt,
+		struct vidc_resource_hdr *resource_hdr,
+		void *resource_value)
+{
+	int rc = 0;
+
+	if (!pkt || !resource_hdr || !resource_value)
+		return -EINVAL;
+
+	pkt->packet_type = HFI_CMD_SYS_SET_RESOURCE;
+	pkt->size = sizeof(struct hfi_cmd_sys_set_resource_packet);
+	pkt->resource_handle = hash32_ptr(resource_hdr->resource_handle);
+
+	switch (resource_hdr->resource_id) {
+	case VIDC_RESOURCE_OCMEM:
+	case VIDC_RESOURCE_VMEM:
+	{
+		struct hfi_resource_ocmem *hfioc_mem =
+			(struct hfi_resource_ocmem *)
+			&pkt->rg_resource_data[0];
+
+		phys_addr_t imem_addr = (phys_addr_t)resource_value;
+
+		pkt->resource_type = HFI_RESOURCE_OCMEM;
+		pkt->size += sizeof(struct hfi_resource_ocmem) - sizeof(u32);
+		hfioc_mem->size = (u32)resource_hdr->size;
+		hfioc_mem->mem = imem_addr;
+		break;
+	}
+	default:
+		dprintk(VIDC_ERR, "Invalid resource_id %d\n",
+					resource_hdr->resource_id);
+		rc = -ENOTSUPP;
+	}
+
+	return rc;
+}
+
+int create_pkt_cmd_sys_release_resource(
+		struct hfi_cmd_sys_release_resource_packet *pkt,
+		struct vidc_resource_hdr *resource_hdr)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_sys_release_resource_packet);
+	pkt->packet_type = HFI_CMD_SYS_RELEASE_RESOURCE;
+	pkt->resource_handle = hash32_ptr(resource_hdr->resource_handle);
+
+	switch (resource_hdr->resource_id) {
+	case VIDC_RESOURCE_OCMEM:
+	case VIDC_RESOURCE_VMEM:
+		pkt->resource_type = HFI_RESOURCE_OCMEM;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid resource_id %d\n",
+					resource_hdr->resource_id);
+		rc = -ENOTSUPP;
+	}
+
+	return rc;
+}
+
+int create_pkt_cmd_sys_ping(struct hfi_cmd_sys_ping_packet *pkt)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_sys_ping_packet);
+	pkt->packet_type = HFI_CMD_SYS_PING;
+
+	return rc;
+}
+
+inline int create_pkt_cmd_sys_session_init(
+		struct hfi_cmd_sys_session_init_packet *pkt,
+		struct hal_session *session,
+		u32 session_domain, u32 session_codec)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_sys_session_init_packet);
+	pkt->packet_type = HFI_CMD_SYS_SESSION_INIT;
+	pkt->session_id = hash32_ptr(session);
+	pkt->session_domain = vidc_get_hfi_domain(session_domain);
+	pkt->session_codec = vidc_get_hfi_codec(session_codec);
+	if (!pkt->session_codec)
+		return -EINVAL;
+
+	return rc;
+}
+
+int create_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt,
+			int pkt_type, struct hal_session *session)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	/*
+	 * Legacy packetization should skip sending any 3xx specific session
+	 * cmds. Add 3xx specific packetization to the switch case below.
+	 */
+	switch (pkt_type) {
+	case HFI_CMD_SESSION_CONTINUE:
+		dprintk(VIDC_INFO,
+			"%s - skip sending %x for legacy hfi\n",
+			__func__, pkt_type);
+		return -EPERM;
+	default:
+		break;
+	}
+
+	pkt->size = sizeof(struct vidc_hal_session_cmd_pkt);
+	pkt->packet_type = pkt_type;
+	pkt->session_id = hash32_ptr(session);
+
+	return rc;
+}
+
+int create_3x_pkt_cmd_session_cmd(struct vidc_hal_session_cmd_pkt *pkt,
+			int pkt_type, struct hal_session *session)
+{
+	int rc = 0;
+
+	if (!pkt)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct vidc_hal_session_cmd_pkt);
+	pkt->packet_type = pkt_type;
+	pkt->session_id = hash32_ptr(session);
+
+	return rc;
+}
+
+int create_pkt_cmd_sys_power_control(
+	struct hfi_cmd_sys_set_property_packet *pkt, u32 enable)
+{
+	struct hfi_enable *hfi;
+
+	if (!pkt) {
+		dprintk(VIDC_ERR, "No input packet\n");
+		return -EINVAL;
+	}
+
+	pkt->size = sizeof(struct hfi_cmd_sys_set_property_packet) +
+		sizeof(struct hfi_enable) + sizeof(u32);
+	pkt->packet_type = HFI_CMD_SYS_SET_PROPERTY;
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL;
+	hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+	hfi->enable = enable;
+	return 0;
+}
+
+static u32 get_hfi_buffer(int hal_buffer)
+{
+	u32 buffer;
+
+	switch (hal_buffer) {
+	case HAL_BUFFER_INPUT:
+		buffer = HFI_BUFFER_INPUT;
+		break;
+	case HAL_BUFFER_OUTPUT:
+		buffer = HFI_BUFFER_OUTPUT;
+		break;
+	case HAL_BUFFER_OUTPUT2:
+		buffer = HFI_BUFFER_OUTPUT2;
+		break;
+	case HAL_BUFFER_EXTRADATA_INPUT:
+		buffer = HFI_BUFFER_EXTRADATA_INPUT;
+		break;
+	case HAL_BUFFER_EXTRADATA_OUTPUT:
+		buffer = HFI_BUFFER_EXTRADATA_OUTPUT;
+		break;
+	case HAL_BUFFER_EXTRADATA_OUTPUT2:
+		buffer = HFI_BUFFER_EXTRADATA_OUTPUT2;
+		break;
+	case HAL_BUFFER_INTERNAL_SCRATCH:
+		buffer = HFI_BUFFER_INTERNAL_SCRATCH;
+		break;
+	case HAL_BUFFER_INTERNAL_SCRATCH_1:
+		buffer = HFI_BUFFER_INTERNAL_SCRATCH_1;
+		break;
+	case HAL_BUFFER_INTERNAL_SCRATCH_2:
+		buffer = HFI_BUFFER_INTERNAL_SCRATCH_2;
+		break;
+	case HAL_BUFFER_INTERNAL_PERSIST:
+		buffer = HFI_BUFFER_INTERNAL_PERSIST;
+		break;
+	case HAL_BUFFER_INTERNAL_PERSIST_1:
+		buffer = HFI_BUFFER_INTERNAL_PERSIST_1;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid buffer: %#x\n",
+				hal_buffer);
+		buffer = 0;
+		break;
+	}
+	return buffer;
+}
+
+static int get_hfi_extradata_index(enum hal_extradata_id index)
+{
+	int ret = 0;
+
+	switch (index) {
+	case HAL_EXTRADATA_MB_QUANTIZATION:
+		ret = HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION;
+		break;
+	case HAL_EXTRADATA_INTERLACE_VIDEO:
+		ret = HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_VC1_FRAMEDISP:
+		ret = HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_VC1_SEQDISP:
+		ret = HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_TIMESTAMP:
+		ret = HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_S3D_FRAME_PACKING:
+		ret = HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_FRAME_RATE:
+		ret = HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_PANSCAN_WINDOW:
+		ret = HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_RECOVERY_POINT_SEI:
+		ret = HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_MULTISLICE_INFO:
+		ret = HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO;
+		break;
+	case HAL_EXTRADATA_NUM_CONCEALED_MB:
+		ret = HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB;
+		break;
+	case HAL_EXTRADATA_ASPECT_RATIO:
+	case HAL_EXTRADATA_INPUT_CROP:
+	case HAL_EXTRADATA_DIGITAL_ZOOM:
+	case HAL_EXTRADATA_OUTPUT_CROP:
+		ret = HFI_PROPERTY_PARAM_INDEX_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_MPEG2_SEQDISP:
+		ret = HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_STREAM_USERDATA:
+		ret = HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_FRAME_QP:
+		ret = HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_FRAME_BITS_INFO:
+		ret = HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_LTR_INFO:
+		ret = HFI_PROPERTY_PARAM_VENC_LTR_INFO;
+		break;
+	case HAL_EXTRADATA_METADATA_MBI:
+		ret = HFI_PROPERTY_PARAM_VENC_MBI_DUMPING;
+		break;
+	case HAL_EXTRADATA_VQZIP_SEI:
+		ret = HFI_PROPERTY_PARAM_VDEC_VQZIP_SEI_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_YUV_STATS:
+		ret = HFI_PROPERTY_PARAM_VENC_YUVSTAT_INFO_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_ROI_QP:
+		ret = HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI:
+		ret =
+		HFI_PROPERTY_PARAM_VDEC_MASTERING_DISPLAY_COLOUR_SEI_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI:
+		ret = HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_PQ_INFO:
+		ret = HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_VUI_DISPLAY_INFO:
+		ret = HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA;
+		break;
+	case HAL_EXTRADATA_VPX_COLORSPACE:
+		ret = HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA;
+		break;
+	default:
+		dprintk(VIDC_WARN, "Extradata index not found: %d\n", index);
+		break;
+	}
+	return ret;
+}
+
+static int get_hfi_extradata_id(enum hal_extradata_id index)
+{
+	int ret = 0;
+
+	switch (index) {
+	case HAL_EXTRADATA_ASPECT_RATIO:
+		ret = MSM_VIDC_EXTRADATA_ASPECT_RATIO;
+		break;
+	case HAL_EXTRADATA_INPUT_CROP:
+		ret = MSM_VIDC_EXTRADATA_INPUT_CROP;
+		break;
+	case HAL_EXTRADATA_DIGITAL_ZOOM:
+		ret = MSM_VIDC_EXTRADATA_DIGITAL_ZOOM;
+		break;
+	case HAL_EXTRADATA_OUTPUT_CROP:
+		ret = MSM_VIDC_EXTRADATA_OUTPUT_CROP;
+		break;
+	default:
+		ret = get_hfi_extradata_index(index);
+		break;
+	}
+	return ret;
+}
+
+static u32 get_hfi_buf_mode(enum buffer_mode_type hal_buf_mode)
+{
+	u32 buf_mode;
+
+	switch (hal_buf_mode) {
+	case HAL_BUFFER_MODE_STATIC:
+		buf_mode = HFI_BUFFER_MODE_STATIC;
+		break;
+	case HAL_BUFFER_MODE_RING:
+		buf_mode = HFI_BUFFER_MODE_RING;
+		break;
+	case HAL_BUFFER_MODE_DYNAMIC:
+		buf_mode = HFI_BUFFER_MODE_DYNAMIC;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid buffer mode: %#x\n",
+				hal_buf_mode);
+		buf_mode = 0;
+		break;
+	}
+	return buf_mode;
+}
+
+static u32 get_hfi_ltr_mode(enum ltr_mode ltr_mode_type)
+{
+	u32 ltrmode;
+
+	switch (ltr_mode_type) {
+	case HAL_LTR_MODE_DISABLE:
+		ltrmode = HFI_LTR_MODE_DISABLE;
+		break;
+	case HAL_LTR_MODE_MANUAL:
+		ltrmode = HFI_LTR_MODE_MANUAL;
+		break;
+	case HAL_LTR_MODE_PERIODIC:
+		ltrmode = HFI_LTR_MODE_PERIODIC;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid ltr mode: %#x\n",
+			ltr_mode_type);
+		ltrmode = HFI_LTR_MODE_DISABLE;
+		break;
+	}
+	return ltrmode;
+}
+
+int create_pkt_cmd_session_set_buffers(
+		struct hfi_cmd_session_set_buffers_packet *pkt,
+		struct hal_session *session,
+		struct vidc_buffer_addr_info *buffer_info)
+{
+	int rc = 0;
+	int i = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->packet_type = HFI_CMD_SESSION_SET_BUFFERS;
+	pkt->session_id = hash32_ptr(session);
+	pkt->buffer_size = buffer_info->buffer_size;
+	pkt->min_buffer_size = buffer_info->buffer_size;
+	pkt->num_buffers = buffer_info->num_buffers;
+
+	if (buffer_info->buffer_type == HAL_BUFFER_OUTPUT ||
+		buffer_info->buffer_type == HAL_BUFFER_OUTPUT2) {
+		struct hfi_buffer_info *buff;
+
+		pkt->extra_data_size = buffer_info->extradata_size;
+
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) -
+				sizeof(u32) + (buffer_info->num_buffers *
+				sizeof(struct hfi_buffer_info));
+		buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+		for (i = 0; i < pkt->num_buffers; i++) {
+			buff->buffer_addr =
+				(u32)buffer_info->align_device_addr;
+			buff->extra_data_addr =
+				(u32)buffer_info->extradata_addr;
+		}
+	} else {
+		pkt->extra_data_size = 0;
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
+			((buffer_info->num_buffers - 1) * sizeof(u32));
+		for (i = 0; i < pkt->num_buffers; i++) {
+			pkt->rg_buffer_info[i] =
+				(u32)buffer_info->align_device_addr;
+		}
+	}
+
+	pkt->buffer_type = get_hfi_buffer(buffer_info->buffer_type);
+	if (!pkt->buffer_type)
+		return -EINVAL;
+
+	return rc;
+}
+
+int create_pkt_cmd_session_release_buffers(
+		struct hfi_cmd_session_release_buffer_packet *pkt,
+		struct hal_session *session,
+		struct vidc_buffer_addr_info *buffer_info)
+{
+	int rc = 0;
+	int i = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->packet_type = HFI_CMD_SESSION_RELEASE_BUFFERS;
+	pkt->session_id = hash32_ptr(session);
+	pkt->buffer_size = buffer_info->buffer_size;
+	pkt->num_buffers = buffer_info->num_buffers;
+
+	if (buffer_info->buffer_type == HAL_BUFFER_OUTPUT ||
+		buffer_info->buffer_type == HAL_BUFFER_OUTPUT2) {
+		struct hfi_buffer_info *buff;
+
+		buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+		for (i = 0; i < pkt->num_buffers; i++) {
+			buff->buffer_addr =
+				(u32)buffer_info->align_device_addr;
+			buff->extra_data_addr =
+				(u32)buffer_info->extradata_addr;
+		}
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) -
+				sizeof(u32) + (buffer_info->num_buffers *
+				sizeof(struct hfi_buffer_info));
+	} else {
+		for (i = 0; i < pkt->num_buffers; i++) {
+			pkt->rg_buffer_info[i] =
+				(u32)buffer_info->align_device_addr;
+		}
+		pkt->extra_data_size = 0;
+		pkt->size = sizeof(struct hfi_cmd_session_set_buffers_packet) +
+			((buffer_info->num_buffers - 1) * sizeof(u32));
+	}
+	pkt->response_req = buffer_info->response_required;
+	pkt->buffer_type = get_hfi_buffer(buffer_info->buffer_type);
+	if (!pkt->buffer_type)
+		return -EINVAL;
+	return rc;
+}
+
+int create_pkt_cmd_session_etb_decoder(
+	struct hfi_cmd_session_empty_buffer_compressed_packet *pkt,
+	struct hal_session *session, struct vidc_frame_data *input_frame)
+{
+	int rc = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->size =
+		sizeof(struct hfi_cmd_session_empty_buffer_compressed_packet);
+	pkt->packet_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+	pkt->session_id = hash32_ptr(session);
+	pkt->time_stamp_hi = upper_32_bits(input_frame->timestamp);
+	pkt->time_stamp_lo = lower_32_bits(input_frame->timestamp);
+	pkt->flags = input_frame->flags;
+	pkt->mark_target = input_frame->mark_target;
+	pkt->mark_data = input_frame->mark_data;
+	pkt->offset = input_frame->offset;
+	pkt->alloc_len = input_frame->alloc_len;
+	pkt->filled_len = input_frame->filled_len;
+	pkt->input_tag = input_frame->clnt_data;
+	pkt->packet_buffer = (u32)input_frame->device_addr;
+
+	trace_msm_v4l2_vidc_buffer_event_start("ETB",
+		input_frame->device_addr, input_frame->timestamp,
+		input_frame->alloc_len, input_frame->filled_len,
+		input_frame->offset);
+
+	if (!pkt->packet_buffer)
+		rc = -EINVAL;
+	return rc;
+}
+
+int create_pkt_cmd_session_etb_encoder(
+	struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet *pkt,
+	struct hal_session *session, struct vidc_frame_data *input_frame)
+{
+	int rc = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct
+		hfi_cmd_session_empty_buffer_uncompressed_plane0_packet);
+	pkt->packet_type = HFI_CMD_SESSION_EMPTY_BUFFER;
+	pkt->session_id = hash32_ptr(session);
+	pkt->view_id = 0;
+	pkt->time_stamp_hi = upper_32_bits(input_frame->timestamp);
+	pkt->time_stamp_lo = lower_32_bits(input_frame->timestamp);
+	pkt->flags = input_frame->flags;
+	pkt->mark_target = input_frame->mark_target;
+	pkt->mark_data = input_frame->mark_data;
+	pkt->offset = input_frame->offset;
+	pkt->alloc_len = input_frame->alloc_len;
+	pkt->filled_len = input_frame->filled_len;
+	pkt->input_tag = input_frame->clnt_data;
+	pkt->packet_buffer = (u32)input_frame->device_addr;
+	pkt->extra_data_buffer = (u32)input_frame->extradata_addr;
+
+	trace_msm_v4l2_vidc_buffer_event_start("ETB",
+		input_frame->device_addr, input_frame->timestamp,
+		input_frame->alloc_len, input_frame->filled_len,
+		input_frame->offset);
+
+	if (!pkt->packet_buffer)
+		rc = -EINVAL;
+	return rc;
+}
+
+int create_pkt_cmd_session_ftb(struct hfi_cmd_session_fill_buffer_packet *pkt,
+		struct hal_session *session,
+		struct vidc_frame_data *output_frame)
+{
+	int rc = 0;
+
+	if (!pkt || !session || !output_frame)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_fill_buffer_packet);
+	pkt->packet_type = HFI_CMD_SESSION_FILL_BUFFER;
+	pkt->session_id = hash32_ptr(session);
+
+	if (output_frame->buffer_type == HAL_BUFFER_OUTPUT)
+		pkt->stream_id = 0;
+	else if (output_frame->buffer_type == HAL_BUFFER_OUTPUT2)
+		pkt->stream_id = 1;
+
+	if (!output_frame->device_addr)
+		return -EINVAL;
+
+	pkt->packet_buffer = (u32)output_frame->device_addr;
+	pkt->extra_data_buffer = (u32)output_frame->extradata_addr;
+	pkt->alloc_len = output_frame->alloc_len;
+	pkt->filled_len = output_frame->filled_len;
+	pkt->offset = output_frame->offset;
+	pkt->rgData[0] = output_frame->extradata_size;
+
+	trace_msm_v4l2_vidc_buffer_event_start("FTB",
+		output_frame->device_addr, output_frame->timestamp,
+		output_frame->alloc_len, output_frame->filled_len,
+		output_frame->offset);
+	dprintk(VIDC_DBG, "### Q OUTPUT BUFFER ###: %d, %d, %d\n",
+			pkt->alloc_len, pkt->filled_len, pkt->offset);
+
+	return rc;
+}
+
+int create_pkt_cmd_session_parse_seq_header(
+		struct hfi_cmd_session_parse_sequence_header_packet *pkt,
+		struct hal_session *session, struct vidc_seq_hdr *seq_hdr)
+{
+	int rc = 0;
+
+	if (!pkt || !session || !seq_hdr)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_parse_sequence_header_packet);
+	pkt->packet_type = HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER;
+	pkt->session_id = hash32_ptr(session);
+	pkt->header_len = seq_hdr->seq_hdr_len;
+	if (!seq_hdr->seq_hdr)
+		return -EINVAL;
+	pkt->packet_buffer = (u32)seq_hdr->seq_hdr;
+	return rc;
+}
+
+int create_pkt_cmd_session_get_seq_hdr(
+		struct hfi_cmd_session_get_sequence_header_packet *pkt,
+		struct hal_session *session, struct vidc_seq_hdr *seq_hdr)
+{
+	int rc = 0;
+
+	if (!pkt || !session || !seq_hdr)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_get_sequence_header_packet);
+	pkt->packet_type = HFI_CMD_SESSION_GET_SEQUENCE_HEADER;
+	pkt->session_id = hash32_ptr(session);
+	pkt->buffer_len = seq_hdr->seq_hdr_len;
+	if (!seq_hdr->seq_hdr)
+		return -EINVAL;
+	pkt->packet_buffer = (u32)seq_hdr->seq_hdr;
+	return rc;
+}
+
+int create_pkt_cmd_session_get_buf_req(
+		struct hfi_cmd_session_get_property_packet *pkt,
+		struct hal_session *session)
+{
+	int rc = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_get_property_packet);
+	pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY;
+	pkt->session_id = hash32_ptr(session);
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS;
+
+	return rc;
+}
+
+int create_pkt_cmd_session_flush(struct hfi_cmd_session_flush_packet *pkt,
+			struct hal_session *session, enum hal_flush flush_mode)
+{
+	int rc = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_flush_packet);
+	pkt->packet_type = HFI_CMD_SESSION_FLUSH;
+	pkt->session_id = hash32_ptr(session);
+	switch (flush_mode) {
+	case HAL_FLUSH_INPUT:
+		pkt->flush_type = HFI_FLUSH_INPUT;
+		break;
+	case HAL_FLUSH_OUTPUT:
+		pkt->flush_type = HFI_FLUSH_OUTPUT;
+		break;
+	case HAL_FLUSH_ALL:
+		pkt->flush_type = HFI_FLUSH_ALL;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid flush mode: %#x\n", flush_mode);
+		return -EINVAL;
+	}
+	return rc;
+}
+
+int create_pkt_cmd_session_get_property(
+		struct hfi_cmd_session_get_property_packet *pkt,
+		struct hal_session *session, enum hal_property ptype)
+{
+	int rc = 0;
+
+	if (!pkt || !session) {
+		dprintk(VIDC_ERR, "%s Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	pkt->size = sizeof(struct hfi_cmd_session_get_property_packet);
+	pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY;
+	pkt->session_id = hash32_ptr(session);
+	pkt->num_properties = 1;
+	switch (ptype) {
+	case HAL_PARAM_PROFILE_LEVEL_CURRENT:
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+		break;
+	default:
+		dprintk(VIDC_ERR, "%s cmd:%#x not supported\n", __func__,
+			ptype);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+int create_3x_pkt_cmd_session_get_property(
+		struct hfi_cmd_session_get_property_packet *pkt,
+		struct hal_session *session, enum hal_property ptype)
+{
+	int rc = 0;
+
+	if (!pkt || !session) {
+		dprintk(VIDC_ERR, "%s Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	pkt->size = sizeof(struct hfi_cmd_session_get_property_packet);
+	pkt->packet_type = HFI_CMD_SESSION_GET_PROPERTY;
+	pkt->session_id = hash32_ptr(session);
+	pkt->num_properties = 1;
+	switch (ptype) {
+	case HAL_CONFIG_VDEC_ENTROPY:
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VDEC_ENTROPY;
+		break;
+	default:
+		rc = create_pkt_cmd_session_get_property(pkt,
+				session, ptype);
+	}
+	return rc;
+}
+
+int create_pkt_cmd_session_set_property(
+		struct hfi_cmd_session_set_property_packet *pkt,
+		struct hal_session *session,
+		enum hal_property ptype, void *pdata)
+{
+	int rc = 0;
+
+	if (!pkt || !session)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_set_property_packet);
+	pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY;
+	pkt->session_id = hash32_ptr(session);
+	pkt->num_properties = 1;
+
+	switch (ptype) {
+	case HAL_CONFIG_FRAME_RATE:
+	{
+		u32 buffer_type;
+		struct hfi_frame_rate *hfi;
+		struct hal_frame_rate *prop = (struct hal_frame_rate *) pdata;
+
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_FRAME_RATE;
+		hfi = (struct hfi_frame_rate *) &pkt->rg_property_data[1];
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+
+		hfi->frame_rate = prop->frame_rate;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_frame_rate);
+		break;
+	}
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT:
+	{
+		u32 buffer_type;
+		struct hfi_uncompressed_format_select *hfi;
+		struct hal_uncompressed_format_select *prop =
+			(struct hal_uncompressed_format_select *) pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT;
+
+		hfi = (struct hfi_uncompressed_format_select *)
+					&pkt->rg_property_data[1];
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+		hfi->format = hal_to_hfi_type(
+				HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+				prop->format);
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_uncompressed_format_select);
+		break;
+	}
+	case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO:
+		break;
+	case HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO:
+		break;
+	case HAL_PARAM_EXTRA_DATA_HEADER_CONFIG:
+		break;
+	case HAL_PARAM_FRAME_SIZE:
+	{
+		struct hfi_frame_size *hfi;
+		struct hal_frame_size *prop = (struct hal_frame_size *) pdata;
+		u32 buffer_type;
+
+		pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_FRAME_SIZE;
+		hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1];
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+
+		hfi->height = prop->height;
+		hfi->width = prop->width;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size);
+		break;
+	}
+	case HAL_CONFIG_REALTIME:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_CONFIG_REALTIME,
+			(((struct hal_enable *) pdata)->enable));
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_BUFFER_COUNT_ACTUAL:
+	{
+		struct hfi_buffer_count_actual *hfi;
+		struct hal_buffer_count_actual *prop =
+			(struct hal_buffer_count_actual *) pdata;
+		u32 buffer_type;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL;
+		hfi = (struct hfi_buffer_count_actual *)
+			&pkt->rg_property_data[1];
+		hfi->buffer_count_actual = prop->buffer_count_actual;
+
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+
+		pkt->size += sizeof(u32) + sizeof(struct
+				hfi_buffer_count_actual);
+
+		break;
+	}
+	case HAL_PARAM_NAL_STREAM_FORMAT_SELECT:
+	{
+		struct hfi_nal_stream_format_select *hfi;
+		struct hal_nal_stream_format_select *prop =
+			(struct hal_nal_stream_format_select *)pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT;
+		hfi = (struct hfi_nal_stream_format_select *)
+			&pkt->rg_property_data[1];
+		dprintk(VIDC_DBG, "data is :%d\n",
+				prop->nal_stream_format_select);
+		hfi->nal_stream_format_select = hal_to_hfi_type(
+				HAL_PARAM_NAL_STREAM_FORMAT_SELECT,
+				prop->nal_stream_format_select);
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_nal_stream_format_select);
+		break;
+	}
+	case HAL_PARAM_VDEC_OUTPUT_ORDER:
+	{
+		int *data = (int *) pdata;
+
+		pkt->rg_property_data[0] =
+				HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER;
+		switch (*data) {
+		case HAL_OUTPUT_ORDER_DECODE:
+			pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DECODE;
+			break;
+		case HAL_OUTPUT_ORDER_DISPLAY:
+			pkt->rg_property_data[1] = HFI_OUTPUT_ORDER_DISPLAY;
+			break;
+		default:
+			dprintk(VIDC_ERR, "invalid output order: %#x\n",
+						  *data);
+			break;
+		}
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_PICTURE_TYPE_DECODE:
+	{
+		struct hfi_enable_picture *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE;
+		hfi = (struct hfi_enable_picture *) &pkt->rg_property_data[1];
+		hfi->picture_type =
+			((struct hfi_enable_picture *)pdata)->picture_type;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_MULTI_STREAM:
+	{
+		struct hfi_multi_stream *hfi;
+		struct hal_multi_stream *prop =
+			(struct hal_multi_stream *) pdata;
+		u32 buffer_type;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+		hfi = (struct hfi_multi_stream *) &pkt->rg_property_data[1];
+
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+		hfi->enable = prop->enable;
+		hfi->width = prop->width;
+		hfi->height = prop->height;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_multi_stream);
+		break;
+	}
+	case HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT:
+	{
+		struct hfi_display_picture_buffer_count *hfi;
+		struct hal_display_picture_buffer_count *prop =
+			(struct hal_display_picture_buffer_count *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT;
+		hfi = (struct hfi_display_picture_buffer_count *)
+			&pkt->rg_property_data[1];
+		hfi->count = prop->count;
+		hfi->enable = prop->enable;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_display_picture_buffer_count);
+		break;
+	}
+	case HAL_PARAM_DIVX_FORMAT:
+	{
+		int *data = pdata;
+
+		pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_DIVX_FORMAT;
+		switch (*data) {
+		case HAL_DIVX_FORMAT_4:
+			pkt->rg_property_data[1] = HFI_DIVX_FORMAT_4;
+			break;
+		case HAL_DIVX_FORMAT_5:
+			pkt->rg_property_data[1] = HFI_DIVX_FORMAT_5;
+			break;
+		case HAL_DIVX_FORMAT_6:
+			pkt->rg_property_data[1] = HFI_DIVX_FORMAT_6;
+			break;
+		default:
+			dprintk(VIDC_ERR, "Invalid divx format: %#x\n", *data);
+			break;
+		}
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_SYNC_FRAME_DECODE:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VENC_REQUEST_IFRAME:
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME;
+		pkt->size += sizeof(u32);
+		break;
+	case HAL_PARAM_VENC_MPEG4_SHORT_HEADER:
+		break;
+	case HAL_PARAM_VENC_MPEG4_AC_PREDICTION:
+		break;
+	case HAL_CONFIG_VENC_TARGET_BITRATE:
+	{
+		struct hfi_bitrate *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE;
+		hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1];
+		hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate;
+		hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
+		break;
+	}
+	case HAL_CONFIG_VENC_MAX_BITRATE:
+	{
+		struct hfi_bitrate *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE;
+		hfi = (struct hfi_bitrate *) &pkt->rg_property_data[1];
+		hfi->bit_rate = ((struct hal_bitrate *)pdata)->bit_rate;
+		hfi->layer_id = ((struct hal_bitrate *)pdata)->layer_id;
+
+		pkt->size += sizeof(u32) + sizeof(struct hfi_bitrate);
+		break;
+	}
+	case HAL_PARAM_PROFILE_LEVEL_CURRENT:
+	{
+		struct hfi_profile_level *hfi;
+		struct hal_profile_level *prop =
+			(struct hal_profile_level *) pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT;
+		hfi = (struct hfi_profile_level *)
+			&pkt->rg_property_data[1];
+		hfi->level = prop->level;
+		hfi->profile = hal_to_hfi_type(HAL_PARAM_PROFILE_LEVEL_CURRENT,
+				prop->profile);
+		if (hfi->profile <= 0) {
+			hfi->profile = HFI_H264_PROFILE_HIGH;
+			dprintk(VIDC_WARN,
+					"Profile %d not supported, falling back to high\n",
+					prop->profile);
+		}
+
+		if (!hfi->level) {
+			hfi->level = 1;
+			dprintk(VIDC_WARN,
+					"Level %d not supported, falling back to high\n",
+					prop->level);
+		}
+
+		pkt->size += sizeof(u32) + sizeof(struct hfi_profile_level);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_ENTROPY_CONTROL:
+	{
+		struct hfi_h264_entropy_control *hfi;
+		struct hal_h264_entropy_control *prop =
+			(struct hal_h264_entropy_control *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL;
+		hfi = (struct hfi_h264_entropy_control *)
+			&pkt->rg_property_data[1];
+		hfi->entropy_mode = hal_to_hfi_type(
+		   HAL_PARAM_VENC_H264_ENTROPY_CONTROL,
+		   prop->entropy_mode);
+		if (hfi->entropy_mode == HAL_H264_ENTROPY_CABAC)
+			hfi->cabac_model = hal_to_hfi_type(
+			   HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL,
+			   prop->cabac_model);
+		pkt->size += sizeof(u32) + sizeof(
+			struct hfi_h264_entropy_control);
+		break;
+	}
+	case HAL_PARAM_VENC_RATE_CONTROL:
+	{
+		u32 *rc;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_RATE_CONTROL;
+		rc = (u32 *)pdata;
+		switch ((enum hal_rate_control) *rc) {
+		case HAL_RATE_CONTROL_OFF:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_OFF;
+			break;
+		case HAL_RATE_CONTROL_CBR_CFR:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_CBR_CFR;
+			break;
+		case HAL_RATE_CONTROL_CBR_VFR:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_CBR_VFR;
+			break;
+		case HAL_RATE_CONTROL_VBR_CFR:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_CFR;
+			break;
+		case HAL_RATE_CONTROL_VBR_VFR:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_VBR_VFR;
+			break;
+		case HAL_RATE_CONTROL_MBR_CFR:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_CFR;
+			break;
+		case HAL_RATE_CONTROL_MBR_VFR:
+			pkt->rg_property_data[1] = HFI_RATE_CONTROL_MBR_VFR;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+					"Invalid Rate control setting: %pK\n",
+					pdata);
+			break;
+		}
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION:
+	{
+		struct hfi_mpeg4_time_resolution *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION;
+		hfi = (struct hfi_mpeg4_time_resolution *)
+			&pkt->rg_property_data[1];
+		hfi->time_increment_resolution =
+			((struct hal_mpeg4_time_resolution *)pdata)->
+					time_increment_resolution;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION:
+	{
+		struct hfi_mpeg4_header_extension *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION;
+		hfi = (struct hfi_mpeg4_header_extension *)
+			&pkt->rg_property_data[1];
+		hfi->header_extension = (u32)(unsigned long) pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_H264_DEBLOCK_CONTROL:
+	{
+		struct hfi_h264_db_control *hfi;
+		struct hal_h264_db_control *prop =
+			(struct hal_h264_db_control *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		hfi = (struct hfi_h264_db_control *) &pkt->rg_property_data[1];
+		switch (prop->mode) {
+		case HAL_H264_DB_MODE_DISABLE:
+			hfi->mode = HFI_H264_DB_MODE_DISABLE;
+			break;
+		case HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY:
+			hfi->mode = HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY;
+			break;
+		case HAL_H264_DB_MODE_ALL_BOUNDARY:
+			hfi->mode = HFI_H264_DB_MODE_ALL_BOUNDARY;
+			break;
+		default:
+			dprintk(VIDC_ERR, "Invalid deblocking mode: %#x\n",
+						  prop->mode);
+			break;
+		}
+		hfi->slice_alpha_offset = prop->slice_alpha_offset;
+		hfi->slice_beta_offset = prop->slice_beta_offset;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_h264_db_control);
+		break;
+	}
+	case HAL_PARAM_VENC_SESSION_QP:
+	{
+		struct hfi_quantization *hfi;
+		struct hal_quantization *hal_quant =
+			(struct hal_quantization *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_SESSION_QP;
+		hfi = (struct hfi_quantization *) &pkt->rg_property_data[1];
+		hfi->qp_i = hal_quant->qpi;
+		hfi->qp_p = hal_quant->qpp;
+		hfi->qp_b = hal_quant->qpb;
+		hfi->layer_id = hal_quant->layer_id;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_quantization);
+		break;
+	}
+	case HAL_PARAM_VENC_SESSION_QP_RANGE:
+	{
+		struct hfi_quantization_range *hfi;
+		struct hfi_quantization_range *hal_range =
+			(struct hfi_quantization_range *) pdata;
+		u32 min_qp, max_qp;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE;
+		hfi = (struct hfi_quantization_range *)
+				&pkt->rg_property_data[1];
+
+		min_qp = hal_range->min_qp;
+		max_qp = hal_range->max_qp;
+
+		/*
+		 * We'll be packing in the qp, so make sure we
+		 * won't be losing data when masking
+		 */
+		if (min_qp > 0xff || max_qp > 0xff) {
+			dprintk(VIDC_ERR, "qp value out of range\n");
+			rc = -ERANGE;
+			break;
+		}
+
+		/*
+		 * When creating the packet, pack the qp value as
+		 * 0xiippbb, where ii = qp range for I-frames,
+		 * pp = qp range for P-frames, etc.
+		 */
+		hfi->min_qp = min_qp | min_qp << 8 | min_qp << 16;
+		hfi->max_qp = max_qp | max_qp << 8 | max_qp << 16;
+		hfi->layer_id = hal_range->layer_id;
+
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_quantization_range);
+		break;
+	}
+	case HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED:
+	{
+		struct hfi_quantization_range *hfi;
+		struct hfi_quantization_range *hal_range =
+			(struct hfi_quantization_range *) pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE;
+		hfi = (struct hfi_quantization_range *)
+				&pkt->rg_property_data[1];
+
+		hfi->min_qp = hal_range->min_qp;
+		hfi->max_qp = hal_range->max_qp;
+		hfi->layer_id = hal_range->layer_id;
+
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_quantization_range);
+		break;
+	}
+	case HAL_PARAM_VENC_SEARCH_RANGE:
+	{
+		struct hfi_vc1e_perf_cfg_type *hfi;
+		struct hal_vc1e_perf_cfg_type *hal_mv_searchrange =
+			(struct hal_vc1e_perf_cfg_type *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG;
+		hfi = (struct hfi_vc1e_perf_cfg_type *)
+				&pkt->rg_property_data[1];
+		hfi->search_range_x_subsampled[0] =
+			hal_mv_searchrange->i_frame.x_subsampled;
+		hfi->search_range_x_subsampled[1] =
+			hal_mv_searchrange->p_frame.x_subsampled;
+		hfi->search_range_x_subsampled[2] =
+			hal_mv_searchrange->b_frame.x_subsampled;
+		hfi->search_range_y_subsampled[0] =
+			hal_mv_searchrange->i_frame.y_subsampled;
+		hfi->search_range_y_subsampled[1] =
+			hal_mv_searchrange->p_frame.y_subsampled;
+		hfi->search_range_y_subsampled[2] =
+			hal_mv_searchrange->b_frame.y_subsampled;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_vc1e_perf_cfg_type);
+		break;
+	}
+	case HAL_PARAM_VENC_MAX_NUM_B_FRAMES:
+	{
+		struct hfi_max_num_b_frames *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES;
+		hfi = (struct hfi_max_num_b_frames *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_max_num_b_frames *) pdata,
+				sizeof(struct hfi_max_num_b_frames));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_max_num_b_frames);
+		break;
+	}
+	case HAL_CONFIG_VENC_INTRA_PERIOD:
+	{
+		struct hfi_intra_period *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD;
+		hfi = (struct hfi_intra_period *) &pkt->rg_property_data[1];
+		memcpy(hfi, (struct hfi_intra_period *) pdata,
+				sizeof(struct hfi_intra_period));
+		pkt->size += sizeof(u32) + sizeof(struct hfi_intra_period);
+		break;
+	}
+	case HAL_CONFIG_VENC_IDR_PERIOD:
+	{
+		struct hfi_idr_period *hfi;
+
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD;
+		hfi = (struct hfi_idr_period *) &pkt->rg_property_data[1];
+		hfi->idr_period = ((struct hfi_idr_period *) pdata)->idr_period;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_CONCEAL_COLOR:
+	{
+		struct hfi_conceal_color *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR;
+		hfi = (struct hfi_conceal_color *) &pkt->rg_property_data[1];
+		if (hfi)
+			hfi->conceal_color =
+				((struct hfi_conceal_color *) pdata)->
+				conceal_color;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VPE_OPERATIONS:
+	{
+		struct hfi_operations_type *hfi;
+		struct hal_operations *prop =
+			(struct hal_operations *) pdata;
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VPE_OPERATIONS;
+		hfi = (struct hfi_operations_type *) &pkt->rg_property_data[1];
+		switch (prop->rotate) {
+		case HAL_ROTATE_NONE:
+			hfi->rotation = HFI_ROTATE_NONE;
+			break;
+		case HAL_ROTATE_90:
+			hfi->rotation = HFI_ROTATE_90;
+			break;
+		case HAL_ROTATE_180:
+			hfi->rotation = HFI_ROTATE_180;
+			break;
+		case HAL_ROTATE_270:
+			hfi->rotation = HFI_ROTATE_270;
+			break;
+		default:
+			dprintk(VIDC_ERR, "Invalid rotation setting: %#x\n",
+				prop->rotate);
+			rc = -EINVAL;
+			break;
+		}
+		switch (prop->flip) {
+		case HAL_FLIP_NONE:
+			hfi->flip = HFI_FLIP_NONE;
+			break;
+		case HAL_FLIP_HORIZONTAL:
+			hfi->flip = HFI_FLIP_HORIZONTAL;
+			break;
+		case HAL_FLIP_VERTICAL:
+			hfi->flip = HFI_FLIP_VERTICAL;
+			break;
+		default:
+			dprintk(VIDC_ERR, "Invalid flip setting: %#x\n",
+				prop->flip);
+			rc = -EINVAL;
+			break;
+		}
+		pkt->size += sizeof(u32) + sizeof(struct hfi_operations_type);
+		break;
+	}
+	case HAL_PARAM_VENC_INTRA_REFRESH:
+	{
+		struct hfi_intra_refresh *hfi;
+		struct hal_intra_refresh *prop =
+			(struct hal_intra_refresh *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
+		hfi = (struct hfi_intra_refresh *) &pkt->rg_property_data[1];
+		switch (prop->mode) {
+		case HAL_INTRA_REFRESH_NONE:
+			hfi->mode = HFI_INTRA_REFRESH_NONE;
+			break;
+		case HAL_INTRA_REFRESH_ADAPTIVE:
+			hfi->mode = HFI_INTRA_REFRESH_ADAPTIVE;
+			break;
+		case HAL_INTRA_REFRESH_CYCLIC:
+			hfi->mode = HFI_INTRA_REFRESH_CYCLIC;
+			break;
+		case HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE:
+			hfi->mode = HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE;
+			break;
+		case HAL_INTRA_REFRESH_RANDOM:
+			hfi->mode = HFI_INTRA_REFRESH_RANDOM;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+					"Invalid intra refresh setting: %#x\n",
+					prop->mode);
+			break;
+		}
+		hfi->air_mbs = prop->air_mbs;
+		hfi->air_ref = prop->air_ref;
+		hfi->cir_mbs = prop->cir_mbs;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_intra_refresh);
+		break;
+	}
+	case HAL_PARAM_VENC_MULTI_SLICE_CONTROL:
+	{
+		struct hfi_multi_slice_control *hfi;
+		struct hal_multi_slice_control *prop =
+			(struct hal_multi_slice_control *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL;
+		hfi = (struct hfi_multi_slice_control *)
+			&pkt->rg_property_data[1];
+		switch (prop->multi_slice) {
+		case HAL_MULTI_SLICE_OFF:
+			hfi->multi_slice = HFI_MULTI_SLICE_OFF;
+			break;
+		case HAL_MULTI_SLICE_GOB:
+			hfi->multi_slice = HFI_MULTI_SLICE_GOB;
+			break;
+		case HAL_MULTI_SLICE_BY_MB_COUNT:
+			hfi->multi_slice = HFI_MULTI_SLICE_BY_MB_COUNT;
+			break;
+		case HAL_MULTI_SLICE_BY_BYTE_COUNT:
+			hfi->multi_slice = HFI_MULTI_SLICE_BY_BYTE_COUNT;
+			break;
+		default:
+			dprintk(VIDC_ERR, "Invalid slice settings: %#x\n",
+				prop->multi_slice);
+			break;
+		}
+		hfi->slice_size = prop->slice_size;
+		pkt->size += sizeof(u32) + sizeof(struct
+					hfi_multi_slice_control);
+		break;
+	}
+	case HAL_PARAM_INDEX_EXTRADATA:
+	{
+		struct hfi_index_extradata_config *hfi;
+		struct hal_extradata_enable *extra = pdata;
+		int id = 0;
+
+		pkt->rg_property_data[0] =
+			get_hfi_extradata_index(extra->index);
+		hfi = (struct hfi_index_extradata_config *)
+			&pkt->rg_property_data[1];
+		hfi->enable = extra->enable;
+		id = get_hfi_extradata_id(extra->index);
+		if (id)
+			hfi->index_extra_data_id = id;
+		else {
+			dprintk(VIDC_WARN,
+				"Failed to find extradata id: %d\n",
+				id);
+			rc = -EINVAL;
+		}
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_index_extradata_config);
+		break;
+	}
+	case HAL_PARAM_VENC_SLICE_DELIVERY_MODE:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_VUI_TIMING_INFO:
+	{
+		struct hfi_h264_vui_timing_info *hfi;
+		struct hal_h264_vui_timing_info *timing_info = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO;
+
+		hfi = (struct hfi_h264_vui_timing_info *)&pkt->
+			rg_property_data[1];
+		hfi->enable = timing_info->enable;
+		hfi->fixed_frame_rate = timing_info->fixed_frame_rate;
+		hfi->time_scale = timing_info->time_scale;
+
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_h264_vui_timing_info);
+		break;
+	}
+	case HAL_CONFIG_VPE_DEINTERLACE:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_CONFIG_VPE_DEINTERLACE,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_GENERATE_AUDNAL:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_BUFFER_ALLOC_MODE:
+	{
+		u32 buffer_type;
+		u32 buffer_mode;
+		struct hfi_buffer_alloc_mode *hfi;
+		struct hal_buffer_alloc_mode *alloc_info = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE;
+		hfi = (struct hfi_buffer_alloc_mode *)
+			&pkt->rg_property_data[1];
+		buffer_type = get_hfi_buffer(alloc_info->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+		buffer_mode = get_hfi_buf_mode(alloc_info->buffer_mode);
+		if (buffer_mode)
+			hfi->buffer_mode = buffer_mode;
+		else
+			return -EINVAL;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_buffer_alloc_mode);
+		break;
+	}
+	case HAL_PARAM_VDEC_FRAME_ASSEMBLY:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VDEC_SCS_THRESHOLD:
+	{
+		struct hfi_scs_threshold *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD;
+		hfi = (struct hfi_scs_threshold *) &pkt->rg_property_data[1];
+		hfi->threshold_value =
+			((struct hal_scs_threshold *) pdata)->threshold_value;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_scs_threshold);
+		break;
+	}
+	case HAL_PARAM_MVC_BUFFER_LAYOUT:
+	{
+		struct hfi_mvc_buffer_layout_descp_type *hfi;
+		struct hal_mvc_buffer_layout *layout_info = pdata;
+
+		pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT;
+		hfi = (struct hfi_mvc_buffer_layout_descp_type *)
+			&pkt->rg_property_data[1];
+		hfi->layout_type = get_hfi_layout(layout_info->layout_type);
+		hfi->bright_view_first = layout_info->bright_view_first;
+		hfi->ngap = layout_info->ngap;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_mvc_buffer_layout_descp_type);
+		break;
+	}
+	case HAL_PARAM_VENC_LTRMODE:
+	{
+		struct hfi_ltr_mode *hfi;
+		struct hal_ltr_mode *hal = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_LTRMODE;
+		hfi = (struct hfi_ltr_mode *) &pkt->rg_property_data[1];
+		hfi->ltr_mode = get_hfi_ltr_mode(hal->mode);
+		hfi->ltr_count = hal->count;
+		hfi->trust_mode = hal->trust_mode;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_ltr_mode);
+		break;
+	}
+	case HAL_CONFIG_VENC_USELTRFRAME:
+	{
+		struct hfi_ltr_use *hfi;
+		struct hal_ltr_use *hal = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_USELTRFRAME;
+		hfi = (struct hfi_ltr_use *) &pkt->rg_property_data[1];
+		hfi->frames = hal->frames;
+		hfi->ref_ltr = hal->ref_ltr;
+		hfi->use_constrnt = hal->use_constraint;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_ltr_use);
+		break;
+	}
+	case HAL_CONFIG_VENC_MARKLTRFRAME:
+	{
+		struct hfi_ltr_mark *hfi;
+		struct hal_ltr_mark *hal = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME;
+		hfi = (struct hfi_ltr_mark *) &pkt->rg_property_data[1];
+		hfi->mark_frame = hal->mark_frame;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_ltr_mark);
+		break;
+	}
+	case HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER;
+		pkt->rg_property_data[1] = *(u32 *)pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VENC_HIER_P_NUM_FRAMES:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER;
+		pkt->rg_property_data[1] = *(u32 *)pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_ENABLE_INITIAL_QP:
+	{
+		struct hfi_initial_quantization *hfi;
+		struct hal_initial_quantization *quant = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_INITIAL_QP;
+		hfi = (struct hfi_initial_quantization *)
+			&pkt->rg_property_data[1];
+		hfi->init_qp_enable = quant->init_qp_enable;
+		hfi->qp_i = quant->qpi;
+		hfi->qp_p = quant->qpp;
+		hfi->qp_b = quant->qpb;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_initial_quantization);
+		break;
+	}
+	case HAL_PARAM_VPE_COLOR_SPACE_CONVERSION:
+	{
+		struct hfi_vpe_color_space_conversion *hfi = NULL;
+		struct hal_vpe_color_space_conversion *hal = pdata;
+
+		pkt->rg_property_data[0] =
+				HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION;
+		hfi = (struct hfi_vpe_color_space_conversion *)
+			&pkt->rg_property_data[1];
+		memcpy(hfi->csc_matrix, hal->csc_matrix,
+				sizeof(hfi->csc_matrix));
+		memcpy(hfi->csc_bias, hal->csc_bias, sizeof(hfi->csc_bias));
+		memcpy(hfi->csc_limit, hal->csc_limit, sizeof(hfi->csc_limit));
+		pkt->size += sizeof(u32) +
+				sizeof(struct hfi_vpe_color_space_conversion);
+		break;
+	}
+	case HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_NAL_SVC_EXT:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_CONFIG_VENC_PERF_MODE:
+	{
+		u32 hfi_perf_mode = 0;
+		enum hal_perf_mode hal_perf_mode = *(enum hal_perf_mode *)pdata;
+
+		switch (hal_perf_mode) {
+		case HAL_PERF_MODE_POWER_SAVE:
+			hfi_perf_mode = HFI_VENC_PERFMODE_POWER_SAVE;
+			break;
+		case HAL_PERF_MODE_POWER_MAX_QUALITY:
+			hfi_perf_mode = HFI_VENC_PERFMODE_MAX_QUALITY;
+			break;
+		default:
+			return -ENOTSUPP;
+		}
+
+		pkt->rg_property_data[0] = HFI_PROPERTY_CONFIG_VENC_PERF_MODE;
+		pkt->rg_property_data[1] = hfi_perf_mode;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_HIER_B_MAX_ENH_LAYERS:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER;
+		pkt->rg_property_data[1] = *(u32 *)pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VDEC_NON_SECURE_OUTPUT2:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_HIER_P_HYBRID_MODE:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE;
+		pkt->rg_property_data[1] =
+			((struct hfi_hybrid_hierp *)pdata)->layers;
+		pkt->size += sizeof(u32) +
+			sizeof(struct hfi_hybrid_hierp);
+		break;
+	}
+	case HAL_PARAM_VENC_MBI_STATISTICS_MODE:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_MBI_DUMPING;
+		pkt->rg_property_data[1] = hal_to_hfi_type(
+			HAL_PARAM_VENC_MBI_STATISTICS_MODE,
+				*(u32 *)pdata);
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VENC_FRAME_QP:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_FRAME_QP;
+		pkt->rg_property_data[1] = *(u32 *)pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VENC_BASELAYER_PRIORITYID:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID;
+		pkt->rg_property_data[1] = *(u32 *)pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO:
+	{
+		struct hfi_aspect_ratio *hfi = NULL;
+		struct hal_aspect_ratio *hal = pdata;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO;
+		hfi = (struct hfi_aspect_ratio *)
+			&pkt->rg_property_data[1];
+		memcpy(hfi, hal,
+			sizeof(struct hfi_aspect_ratio));
+		pkt->size += sizeof(u32) +
+				sizeof(struct hfi_aspect_ratio);
+		break;
+	}
+	case HAL_PARAM_VENC_BITRATE_TYPE:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VENC_BITRATE_TYPE,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VENC_CONSTRAINED_INTRA_PRED,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_TRANSFORM_8x8:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+			HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM,
+			((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_VIDEO_SIGNAL_INFO:
+	{
+		struct hal_video_signal_info *hal = pdata;
+		struct hfi_video_signal_metadata *signal_info =
+			(struct hfi_video_signal_metadata *)
+			&pkt->rg_property_data[1];
+
+		signal_info->enable = true;
+		signal_info->video_format = MSM_VIDC_NTSC;
+		signal_info->video_full_range = hal->full_range;
+		signal_info->color_description = MSM_VIDC_COLOR_DESC_PRESENT;
+		signal_info->color_primaries = hal->color_space;
+		signal_info->transfer_characteristics = hal->transfer_chars;
+		signal_info->matrix_coeffs = hal->matrix_coeffs;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO;
+		pkt->size += sizeof(u32) + sizeof(*signal_info);
+		break;
+	}
+	case HAL_PARAM_VENC_IFRAMESIZE_TYPE:
+	{
+		enum hal_iframesize_type hal =
+			*(enum hal_iframesize_type *)pdata;
+		struct hfi_iframe_size *hfi = (struct hfi_iframe_size *)
+			&pkt->rg_property_data[1];
+
+		switch (hal) {
+		case HAL_IFRAMESIZE_TYPE_DEFAULT:
+			hfi->type = HFI_IFRAME_SIZE_DEFAULT;
+			break;
+		case HAL_IFRAMESIZE_TYPE_MEDIUM:
+			hfi->type = HFI_IFRAME_SIZE_MEDIUM;
+			break;
+		case HAL_IFRAMESIZE_TYPE_HUGE:
+			hfi->type = HFI_IFRAME_SIZE_HIGH;
+			break;
+		case HAL_IFRAMESIZE_TYPE_UNLIMITED:
+			hfi->type = HFI_IFRAME_SIZE_UNLIMITED;
+			break;
+		default:
+			return -ENOTSUPP;
+		}
+		pkt->rg_property_data[0] = HFI_PROPERTY_PARAM_VENC_IFRAMESIZE;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_iframe_size);
+		break;
+	}
+	/* FOLLOWING PROPERTIES ARE NOT IMPLEMENTED IN CORE YET */
+	case HAL_CONFIG_BUFFER_REQUIREMENTS:
+	case HAL_CONFIG_PRIORITY:
+	case HAL_CONFIG_BATCH_INFO:
+	case HAL_PARAM_METADATA_PASS_THROUGH:
+	case HAL_SYS_IDLE_INDICATOR:
+	case HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
+	case HAL_PARAM_INTERLACE_FORMAT_SUPPORTED:
+	case HAL_PARAM_CHROMA_SITE:
+	case HAL_PARAM_PROPERTIES_SUPPORTED:
+	case HAL_PARAM_PROFILE_LEVEL_SUPPORTED:
+	case HAL_PARAM_CAPABILITY_SUPPORTED:
+	case HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED:
+	case HAL_PARAM_MULTI_VIEW_FORMAT:
+	case HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE:
+	case HAL_PARAM_CODEC_SUPPORTED:
+	case HAL_PARAM_VDEC_MULTI_VIEW_SELECT:
+	case HAL_PARAM_VDEC_MB_QUANTIZATION:
+	case HAL_PARAM_VDEC_NUM_CONCEALED_MB:
+	case HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING:
+	case HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING:
+	case HAL_CONFIG_BUFFER_COUNT_ACTUAL:
+	case HAL_CONFIG_VDEC_MULTI_STREAM:
+	case HAL_PARAM_VENC_MULTI_SLICE_INFO:
+	case HAL_CONFIG_VENC_TIMESTAMP_SCALE:
+	case HAL_PARAM_BUFFER_SIZE_MINIMUM:
+	default:
+		dprintk(VIDC_ERR, "DEFAULT: Calling %#x\n", ptype);
+		rc = -ENOTSUPP;
+		break;
+	}
+	return rc;
+}
+
+static int get_hfi_ssr_type(enum hal_ssr_trigger_type type)
+{
+	int rc = HFI_TEST_SSR_HW_WDOG_IRQ;
+
+	switch (type) {
+	case SSR_ERR_FATAL:
+		rc = HFI_TEST_SSR_SW_ERR_FATAL;
+		break;
+	case SSR_SW_DIV_BY_ZERO:
+		rc = HFI_TEST_SSR_SW_DIV_BY_ZERO;
+		break;
+	case SSR_HW_WDOG_IRQ:
+		rc = HFI_TEST_SSR_HW_WDOG_IRQ;
+		break;
+	default:
+		dprintk(VIDC_WARN,
+			"SSR trigger type not recognized, using WDOG.\n");
+	}
+	return rc;
+}
+
+int create_pkt_ssr_cmd(enum hal_ssr_trigger_type type,
+		struct hfi_cmd_sys_test_ssr_packet *pkt)
+{
+	if (!pkt) {
+		dprintk(VIDC_ERR, "Invalid params, device: %pK\n", pkt);
+		return -EINVAL;
+	}
+	pkt->size = sizeof(struct hfi_cmd_sys_test_ssr_packet);
+	pkt->packet_type = HFI_CMD_SYS_TEST_SSR;
+	pkt->trigger_type = get_hfi_ssr_type(type);
+	return 0;
+}
+
+int create_pkt_cmd_sys_image_version(
+		struct hfi_cmd_sys_get_property_packet *pkt)
+{
+	if (!pkt) {
+		dprintk(VIDC_ERR, "%s invalid param :%pK\n", __func__, pkt);
+		return -EINVAL;
+	}
+	pkt->size = sizeof(struct hfi_cmd_sys_get_property_packet);
+	pkt->packet_type = HFI_CMD_SYS_GET_PROPERTY;
+	pkt->num_properties = 1;
+	pkt->rg_property_data[0] = HFI_PROPERTY_SYS_IMAGE_VERSION;
+	return 0;
+}
+
+static int create_3x_pkt_cmd_session_set_property(
+		struct hfi_cmd_session_set_property_packet *pkt,
+		struct hal_session *session,
+		enum hal_property ptype, void *pdata)
+{
+	int rc = 0;
+
+	if (!pkt || !session || !pdata)
+		return -EINVAL;
+
+	pkt->size = sizeof(struct hfi_cmd_session_set_property_packet);
+	pkt->packet_type = HFI_CMD_SESSION_SET_PROPERTY;
+	pkt->session_id = hash32_ptr(session);
+	pkt->num_properties = 1;
+
+	/*
+	 * Any session set property which is different in 3XX packetization
+	 * should be added as a new case below. All unchanged session set
+	 * properties will be handled in the default case.
+	 */
+	switch (ptype) {
+	case HAL_PARAM_VDEC_MULTI_STREAM:
+	{
+		u32 buffer_type;
+		struct hfi_3x_multi_stream *hfi;
+		struct hal_multi_stream *prop =
+			(struct hal_multi_stream *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM;
+		hfi = (struct hfi_3x_multi_stream *) &pkt->rg_property_data[1];
+
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+		hfi->enable = prop->enable;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_3x_multi_stream);
+		break;
+	}
+	case HAL_PARAM_VENC_INTRA_REFRESH:
+	{
+		struct hfi_3x_intra_refresh *hfi;
+		struct hal_intra_refresh *prop =
+			(struct hal_intra_refresh *) pdata;
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH;
+		hfi = (struct hfi_3x_intra_refresh *) &pkt->rg_property_data[1];
+		hfi->mbs = 0;
+		switch (prop->mode) {
+		case HAL_INTRA_REFRESH_NONE:
+			hfi->mode = HFI_INTRA_REFRESH_NONE;
+			break;
+		case HAL_INTRA_REFRESH_ADAPTIVE:
+			hfi->mode = HFI_INTRA_REFRESH_ADAPTIVE;
+			hfi->mbs = prop->air_mbs;
+			break;
+		case HAL_INTRA_REFRESH_CYCLIC:
+			hfi->mode = HFI_INTRA_REFRESH_CYCLIC;
+			hfi->mbs = prop->cir_mbs;
+			break;
+		case HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE:
+			hfi->mode = HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE;
+			hfi->mbs = prop->air_mbs;
+			break;
+		case HAL_INTRA_REFRESH_RANDOM:
+			hfi->mode = HFI_INTRA_REFRESH_RANDOM;
+			hfi->mbs = prop->air_mbs;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+				"Invalid intra refresh setting: %d\n",
+				prop->mode);
+			break;
+		}
+		pkt->size += sizeof(u32) + sizeof(struct hfi_3x_intra_refresh);
+		break;
+	}
+	case HAL_PARAM_SYNC_BASED_INTERRUPT:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	case HAL_PARAM_VENC_VQZIP_SEI:
+	{
+		create_pkt_enable(pkt->rg_property_data,
+				HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE,
+				((struct hal_enable *)pdata)->enable);
+		pkt->size += sizeof(u32) + sizeof(struct hfi_enable);
+		break;
+	}
+	/* Deprecated param on Venus 3xx */
+	case HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER:
+	{
+		rc = -ENOTSUPP;
+		break;
+	}
+	case HAL_PARAM_BUFFER_SIZE_MINIMUM:
+	{
+		struct hfi_buffer_size_minimum *hfi;
+		struct hal_buffer_size_minimum *prop =
+			(struct hal_buffer_size_minimum *) pdata;
+		u32 buffer_type;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM;
+
+		hfi = (struct hfi_buffer_size_minimum *)
+			&pkt->rg_property_data[1];
+		hfi->buffer_size = prop->buffer_size;
+
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+
+		pkt->size += sizeof(u32) + sizeof(struct
+				hfi_buffer_count_actual);
+		break;
+	}
+	case HAL_PARAM_VENC_H264_PIC_ORDER_CNT:
+	{
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE;
+		pkt->rg_property_data[1] = *(u32 *)pdata;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_PARAM_VENC_LOW_LATENCY:
+	{
+		struct hfi_enable *hfi;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE;
+		hfi = (struct hfi_enable *) &pkt->rg_property_data[1];
+		hfi->enable = ((struct hal_enable *) pdata)->enable;
+		pkt->size += sizeof(u32) * 2;
+		break;
+	}
+	case HAL_CONFIG_VENC_BLUR_RESOLUTION:
+	{
+		struct hfi_frame_size *hfi;
+		struct hal_frame_size *prop = (struct hal_frame_size *) pdata;
+		u32 buffer_type;
+
+		pkt->rg_property_data[0] =
+			HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE;
+		hfi = (struct hfi_frame_size *) &pkt->rg_property_data[1];
+		buffer_type = get_hfi_buffer(prop->buffer_type);
+		if (buffer_type)
+			hfi->buffer_type = buffer_type;
+		else
+			return -EINVAL;
+
+		hfi->height = prop->height;
+		hfi->width = prop->width;
+		pkt->size += sizeof(u32) + sizeof(struct hfi_frame_size);
+		break;
+	}
+	default:
+		rc = create_pkt_cmd_session_set_property(pkt,
+				session, ptype, pdata);
+	}
+	return rc;
+}
+
+int create_pkt_cmd_session_sync_process(
+		struct hfi_cmd_session_sync_process_packet *pkt,
+		struct hal_session *session)
+{
+	if (!pkt || !session)
+		return -EINVAL;
+
+	*pkt = (struct hfi_cmd_session_sync_process_packet) {0};
+	pkt->size = sizeof(*pkt);
+	pkt->packet_type = HFI_CMD_SESSION_SYNC;
+	pkt->session_id = hash32_ptr(session);
+	pkt->sync_id = 0;
+
+	return 0;
+}
+
+static struct hfi_packetization_ops hfi_default = {
+	.sys_init = create_pkt_cmd_sys_init,
+	.sys_pc_prep = create_pkt_cmd_sys_pc_prep,
+	.sys_idle_indicator = create_pkt_cmd_sys_idle_indicator,
+	.sys_power_control = create_pkt_cmd_sys_power_control,
+	.sys_set_resource = create_pkt_cmd_sys_set_resource,
+	.sys_debug_config = create_pkt_cmd_sys_debug_config,
+	.sys_coverage_config = create_pkt_cmd_sys_coverage_config,
+	.sys_release_resource = create_pkt_cmd_sys_release_resource,
+	.sys_ping = create_pkt_cmd_sys_ping,
+	.sys_image_version = create_pkt_cmd_sys_image_version,
+	.ssr_cmd = create_pkt_ssr_cmd,
+	.session_init = create_pkt_cmd_sys_session_init,
+	.session_cmd = create_pkt_cmd_session_cmd,
+	.session_set_buffers = create_pkt_cmd_session_set_buffers,
+	.session_release_buffers = create_pkt_cmd_session_release_buffers,
+	.session_etb_decoder = create_pkt_cmd_session_etb_decoder,
+	.session_etb_encoder = create_pkt_cmd_session_etb_encoder,
+	.session_ftb = create_pkt_cmd_session_ftb,
+	.session_parse_seq_header = create_pkt_cmd_session_parse_seq_header,
+	.session_get_seq_hdr = create_pkt_cmd_session_get_seq_hdr,
+	.session_get_buf_req = create_pkt_cmd_session_get_buf_req,
+	.session_flush = create_pkt_cmd_session_flush,
+	.session_get_property = create_pkt_cmd_session_get_property,
+	.session_set_property = create_pkt_cmd_session_set_property,
+};
+
+struct hfi_packetization_ops *get_venus_3x_ops(void)
+{
+	static struct hfi_packetization_ops hfi_venus_3x;
+
+	hfi_venus_3x = hfi_default;
+
+	/* Override new HFI functions for HFI_PACKETIZATION_3XX here. */
+	hfi_venus_3x.session_set_property =
+		create_3x_pkt_cmd_session_set_property;
+	hfi_venus_3x.session_get_property =
+		create_3x_pkt_cmd_session_get_property;
+	hfi_venus_3x.session_cmd = create_3x_pkt_cmd_session_cmd;
+	hfi_venus_3x.session_sync_process = create_pkt_cmd_session_sync_process;
+
+	return &hfi_venus_3x;
+}
+
+struct hfi_packetization_ops *hfi_get_pkt_ops_handle(
+			enum hfi_packetization_type type)
+{
+	dprintk(VIDC_DBG, "%s selected\n",
+		type == HFI_PACKETIZATION_LEGACY ? "legacy packetization" :
+		type == HFI_PACKETIZATION_3XX ? "3xx packetization" :
+		"Unknown hfi");
+
+	switch (type) {
+	case HFI_PACKETIZATION_LEGACY:
+		return &hfi_default;
+	case HFI_PACKETIZATION_3XX:
+		return get_venus_3x_ops();
+	}
+
+	return NULL;
+}
diff --git a/drivers/media/platform/msm/vidc/hfi_packetization.h b/drivers/media/platform/msm/vidc/hfi_packetization.h
new file mode 100644
index 0000000..a6e7afa
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/hfi_packetization.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __HFI_PACKETIZATION__
+#define __HFI_PACKETIZATION__
+
+#include <linux/types.h>
+#include "vidc_hfi_helper.h"
+#include "vidc_hfi.h"
+#include "vidc_hfi_api.h"
+
+#define call_hfi_pkt_op(q, op, args...)			\
+	(((q) && (q)->pkt_ops && (q)->pkt_ops->op) ?	\
+	((q)->pkt_ops->op(args)) : 0)
+
+enum hfi_packetization_type {
+	HFI_PACKETIZATION_LEGACY,
+	HFI_PACKETIZATION_3XX,
+};
+
+struct hfi_packetization_ops {
+	int (*sys_init)(struct hfi_cmd_sys_init_packet *pkt, u32 arch_type);
+	int (*sys_pc_prep)(struct hfi_cmd_sys_pc_prep_packet *pkt);
+	int (*sys_idle_indicator)(struct hfi_cmd_sys_set_property_packet *pkt,
+		u32 enable);
+	int (*sys_power_control)(struct hfi_cmd_sys_set_property_packet *pkt,
+		u32 enable);
+	int (*sys_set_resource)(
+		struct hfi_cmd_sys_set_resource_packet *pkt,
+		struct vidc_resource_hdr *resource_hdr,
+		void *resource_value);
+	int (*sys_debug_config)(struct hfi_cmd_sys_set_property_packet *pkt,
+			u32 mode);
+	int (*sys_coverage_config)(struct hfi_cmd_sys_set_property_packet *pkt,
+			u32 mode);
+	int (*sys_release_resource)(
+		struct hfi_cmd_sys_release_resource_packet *pkt,
+		struct vidc_resource_hdr *resource_hdr);
+	int (*sys_ping)(struct hfi_cmd_sys_ping_packet *pkt);
+	int (*sys_image_version)(struct hfi_cmd_sys_get_property_packet *pkt);
+	int (*ssr_cmd)(enum hal_ssr_trigger_type type,
+		struct hfi_cmd_sys_test_ssr_packet *pkt);
+	int (*session_init)(
+		struct hfi_cmd_sys_session_init_packet *pkt,
+		struct hal_session *session,
+		u32 session_domain, u32 session_codec);
+	int (*session_cmd)(struct vidc_hal_session_cmd_pkt *pkt,
+		int pkt_type, struct hal_session *session);
+	int (*session_set_buffers)(
+		struct hfi_cmd_session_set_buffers_packet *pkt,
+		struct hal_session *session,
+		struct vidc_buffer_addr_info *buffer_info);
+	int (*session_release_buffers)(
+		struct hfi_cmd_session_release_buffer_packet *pkt,
+		struct hal_session *session,
+		struct vidc_buffer_addr_info *buffer_info);
+	int (*session_etb_decoder)(
+		struct hfi_cmd_session_empty_buffer_compressed_packet *pkt,
+		struct hal_session *session,
+		struct vidc_frame_data *input_frame);
+	int (*session_etb_encoder)(
+		struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+		*pkt, struct hal_session *session,
+		struct vidc_frame_data *input_frame);
+	int (*session_ftb)(struct hfi_cmd_session_fill_buffer_packet *pkt,
+		struct hal_session *session,
+		struct vidc_frame_data *output_frame);
+	int (*session_parse_seq_header)(
+		struct hfi_cmd_session_parse_sequence_header_packet *pkt,
+		struct hal_session *session, struct vidc_seq_hdr *seq_hdr);
+	int (*session_get_seq_hdr)(
+		struct hfi_cmd_session_get_sequence_header_packet *pkt,
+		struct hal_session *session, struct vidc_seq_hdr *seq_hdr);
+	int (*session_get_buf_req)(
+		struct hfi_cmd_session_get_property_packet *pkt,
+		struct hal_session *session);
+	int (*session_flush)(struct hfi_cmd_session_flush_packet *pkt,
+		struct hal_session *session, enum hal_flush flush_mode);
+	int (*session_get_property)(
+		struct hfi_cmd_session_get_property_packet *pkt,
+		struct hal_session *session, enum hal_property ptype);
+	int (*session_set_property)(
+		struct hfi_cmd_session_set_property_packet *pkt,
+		struct hal_session *session,
+		enum hal_property ptype, void *pdata);
+	int (*session_sync_process)(
+		struct hfi_cmd_session_sync_process_packet *pkt,
+		struct hal_session *session);
+};
+
+struct hfi_packetization_ops *hfi_get_pkt_ops_handle(
+		enum hfi_packetization_type);
+#endif
diff --git a/drivers/media/platform/msm/vidc/hfi_response_handler.c b/drivers/media/platform/msm/vidc/hfi_response_handler.c
new file mode 100644
index 0000000..c8eed4b
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/hfi_response_handler.c
@@ -0,0 +1,1955 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/hash.h>
+#include <soc/qcom/smem.h>
+#include "vidc_hfi_helper.h"
+#include "vidc_hfi_io.h"
+#include "msm_vidc_debug.h"
+#include "vidc_hfi.h"
+
+static enum vidc_status hfi_parse_init_done_properties(
+		struct msm_vidc_capability *capability,
+		u32 num_sessions, u8 *data_ptr, u32 num_properties,
+		u32 rem_bytes, u32 codec, u32 domain);
+
+static enum vidc_status hfi_map_err_status(u32 hfi_err)
+{
+	enum vidc_status vidc_err;
+
+	switch (hfi_err) {
+	case HFI_ERR_NONE:
+	case HFI_ERR_SESSION_SAME_STATE_OPERATION:
+		vidc_err = VIDC_ERR_NONE;
+		break;
+	case HFI_ERR_SYS_FATAL:
+		vidc_err = VIDC_ERR_HW_FATAL;
+		break;
+	case HFI_ERR_SYS_VERSION_MISMATCH:
+	case HFI_ERR_SYS_INVALID_PARAMETER:
+	case HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE:
+	case HFI_ERR_SESSION_INVALID_PARAMETER:
+	case HFI_ERR_SESSION_INVALID_SESSION_ID:
+	case HFI_ERR_SESSION_INVALID_STREAM_ID:
+		vidc_err = VIDC_ERR_BAD_PARAM;
+		break;
+	case HFI_ERR_SYS_INSUFFICIENT_RESOURCES:
+	case HFI_ERR_SYS_UNSUPPORTED_DOMAIN:
+	case HFI_ERR_SYS_UNSUPPORTED_CODEC:
+	case HFI_ERR_SESSION_UNSUPPORTED_PROPERTY:
+	case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
+	case HFI_ERR_SESSION_INSUFFICIENT_RESOURCES:
+	case HFI_ERR_SESSION_UNSUPPORTED_STREAM:
+		vidc_err = VIDC_ERR_NOT_SUPPORTED;
+		break;
+	case HFI_ERR_SYS_MAX_SESSIONS_REACHED:
+		vidc_err = VIDC_ERR_MAX_CLIENTS;
+		break;
+	case HFI_ERR_SYS_SESSION_IN_USE:
+		vidc_err = VIDC_ERR_CLIENT_PRESENT;
+		break;
+	case HFI_ERR_SESSION_FATAL:
+		vidc_err = VIDC_ERR_CLIENT_FATAL;
+		break;
+	case HFI_ERR_SESSION_BAD_POINTER:
+		vidc_err = VIDC_ERR_BAD_PARAM;
+		break;
+	case HFI_ERR_SESSION_INCORRECT_STATE_OPERATION:
+		vidc_err = VIDC_ERR_BAD_STATE;
+		break;
+	case HFI_ERR_SESSION_STREAM_CORRUPT:
+	case HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED:
+		vidc_err = VIDC_ERR_BITSTREAM_ERR;
+		break;
+	case HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED:
+		vidc_err = VIDC_ERR_IFRAME_EXPECTED;
+		break;
+	case HFI_ERR_SESSION_START_CODE_NOT_FOUND:
+		vidc_err = VIDC_ERR_START_CODE_NOT_FOUND;
+		break;
+	case HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING:
+	default:
+		vidc_err = VIDC_ERR_FAIL;
+		break;
+	}
+	return vidc_err;
+}
+
+static enum msm_vidc_pixel_depth get_hal_pixel_depth(u32 hfi_bit_depth)
+{
+	switch (hfi_bit_depth) {
+	case HFI_BITDEPTH_8: return MSM_VIDC_BIT_DEPTH_8;
+	case HFI_BITDEPTH_9:
+	case HFI_BITDEPTH_10: return MSM_VIDC_BIT_DEPTH_10;
+	}
+	dprintk(VIDC_ERR, "Unsupported bit depth: %d\n", hfi_bit_depth);
+	return MSM_VIDC_BIT_DEPTH_UNSUPPORTED;
+}
+
+static int hfi_process_sess_evt_seq_changed(u32 device_id,
+		struct hfi_msg_event_notify_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_event event_notify = {0};
+	int num_properties_changed;
+	struct hfi_frame_size *frame_sz;
+	struct hfi_profile_level *profile_level;
+	struct hfi_bit_depth *pixel_depth;
+	struct hfi_pic_struct *pic_struct;
+	u8 *data_ptr;
+	int prop_id;
+	enum msm_vidc_pixel_depth luma_bit_depth, chroma_bit_depth;
+	struct hfi_colour_space *colour_info;
+
+	if (sizeof(struct hfi_msg_event_notify_packet) > pkt->size) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_init_done: bad_pkt_size\n");
+		return -E2BIG;
+	}
+
+	event_notify.device_id = device_id;
+	event_notify.session_id = (void *)(uintptr_t)pkt->session_id;
+	event_notify.status = VIDC_ERR_NONE;
+	num_properties_changed = pkt->event_data2;
+	switch (pkt->event_data1) {
+	case HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES:
+		event_notify.hal_event_type =
+			HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES;
+		break;
+	case HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES:
+		event_notify.hal_event_type =
+			HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES;
+		break;
+	default:
+		break;
+	}
+
+	if (num_properties_changed) {
+		data_ptr = (u8 *) &pkt->rg_ext_event_data[0];
+		do {
+			prop_id = (int) *((u32 *)data_ptr);
+			switch (prop_id) {
+			case HFI_PROPERTY_PARAM_FRAME_SIZE:
+				data_ptr = data_ptr + sizeof(u32);
+				frame_sz =
+					(struct hfi_frame_size *) data_ptr;
+				event_notify.width = frame_sz->width;
+				event_notify.height = frame_sz->height;
+				dprintk(VIDC_DBG, "height: %d width: %d\n",
+					frame_sz->height, frame_sz->width);
+				data_ptr +=
+					sizeof(struct hfi_frame_size);
+				break;
+			case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+				data_ptr = data_ptr + sizeof(u32);
+				profile_level =
+					(struct hfi_profile_level *) data_ptr;
+				dprintk(VIDC_DBG, "profile: %d level: %d\n",
+					profile_level->profile,
+					profile_level->level);
+				data_ptr +=
+					sizeof(struct hfi_profile_level);
+				break;
+			case HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH:
+				data_ptr = data_ptr + sizeof(u32);
+				pixel_depth = (struct hfi_bit_depth *) data_ptr;
+				/*
+				 * Luma and chroma can have different bitdepths.
+				 * Driver should rely on luma and chroma
+				 * bitdepth for determining output bitdepth
+				 * type.
+				 *
+				 * pixel_depth->bitdepth will include luma
+				 * bitdepth info in bits 0..15 and chroma
+				 * bitdept in bits 16..31.
+				 */
+				luma_bit_depth = get_hal_pixel_depth(
+					pixel_depth->bit_depth &
+					GENMASK(15, 0));
+				chroma_bit_depth = get_hal_pixel_depth(
+					(pixel_depth->bit_depth &
+					GENMASK(31, 16)) >> 16);
+				if (luma_bit_depth == MSM_VIDC_BIT_DEPTH_10 ||
+					chroma_bit_depth ==
+						MSM_VIDC_BIT_DEPTH_10)
+					event_notify.bit_depth =
+						MSM_VIDC_BIT_DEPTH_10;
+				else
+					event_notify.bit_depth = luma_bit_depth;
+				dprintk(VIDC_DBG,
+					"bitdepth(%d), luma_bit_depth(%d), chroma_bit_depth(%d)\n",
+					event_notify.bit_depth, luma_bit_depth,
+					chroma_bit_depth);
+				data_ptr += sizeof(struct hfi_bit_depth);
+				break;
+			case HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT:
+				data_ptr = data_ptr + sizeof(u32);
+				pic_struct = (struct hfi_pic_struct *) data_ptr;
+				event_notify.pic_struct =
+					pic_struct->progressive_only;
+				dprintk(VIDC_DBG,
+					"Progressive only flag: %d\n",
+						pic_struct->progressive_only);
+				data_ptr +=
+					sizeof(struct hfi_pic_struct);
+				break;
+			case HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE:
+				data_ptr = data_ptr + sizeof(u32);
+				colour_info =
+					(struct hfi_colour_space *) data_ptr;
+				event_notify.colour_space =
+					colour_info->colour_space;
+				dprintk(VIDC_DBG,
+					"Colour space value is: %d\n",
+						colour_info->colour_space);
+				data_ptr +=
+					sizeof(struct hfi_colour_space);
+				break;
+			default:
+				dprintk(VIDC_ERR,
+					"%s cmd: %#x not supported\n",
+					__func__, prop_id);
+				break;
+			}
+			num_properties_changed--;
+		} while (num_properties_changed > 0);
+	}
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_EVENT_CHANGE,
+		.response.event = event_notify,
+	};
+
+	return 0;
+}
+
+static int hfi_process_evt_release_buffer_ref(u32 device_id,
+		struct hfi_msg_event_notify_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_event event_notify = {0};
+	struct hfi_msg_release_buffer_ref_event_packet *data;
+
+	dprintk(VIDC_DBG,
+			"RECEIVED: EVENT_NOTIFY - release_buffer_reference\n");
+	if (sizeof(struct hfi_msg_event_notify_packet)
+		> pkt->size) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_init_done: bad_pkt_size\n");
+		return -E2BIG;
+	}
+
+	data = (struct hfi_msg_release_buffer_ref_event_packet *)
+				pkt->rg_ext_event_data;
+
+	event_notify.device_id = device_id;
+	event_notify.session_id = (void *)(uintptr_t)pkt->session_id;
+	event_notify.status = VIDC_ERR_NONE;
+	event_notify.hal_event_type = HAL_EVENT_RELEASE_BUFFER_REFERENCE;
+	event_notify.packet_buffer = data->packet_buffer;
+	event_notify.extra_data_buffer = data->extra_data_buffer;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_EVENT_CHANGE,
+		.response.event = event_notify,
+	};
+
+	return 0;
+}
+
+static int hfi_process_sys_error(u32 device_id, struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	cmd_done.device_id = device_id;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SYS_ERROR,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_error(u32 device_id,
+		struct hfi_msg_event_notify_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->event_data1);
+	dprintk(VIDC_INFO, "Received: SESSION_ERROR with event id : %d\n",
+		pkt->event_data1);
+	switch (pkt->event_data1) {
+	case HFI_ERR_SESSION_INVALID_SCALE_FACTOR:
+	case HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE:
+	case HFI_ERR_SESSION_UNSUPPORTED_SETTING:
+	case HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED:
+		cmd_done.status = VIDC_ERR_NONE;
+		dprintk(VIDC_INFO, "Non Fatal: HFI_EVENT_SESSION_ERROR\n");
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_RESPONSE_UNUSED,
+			.response.cmd = cmd_done,
+		};
+		return 0;
+	default:
+		dprintk(VIDC_ERR, "HFI_EVENT_SESSION_ERROR\n");
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_SESSION_ERROR,
+			.response.cmd = cmd_done,
+		};
+		return 0;
+	}
+}
+
+static int hfi_process_event_notify(u32 device_id,
+		struct hfi_msg_event_notify_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	dprintk(VIDC_DBG, "Received: EVENT_NOTIFY\n");
+
+	if (pkt->size < sizeof(struct hfi_msg_event_notify_packet)) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -E2BIG;
+	}
+
+	switch (pkt->event_id) {
+	case HFI_EVENT_SYS_ERROR:
+		dprintk(VIDC_ERR, "HFI_EVENT_SYS_ERROR: %d, %#x\n",
+			pkt->event_data1, pkt->event_data2);
+		return hfi_process_sys_error(device_id, info);
+	case HFI_EVENT_SESSION_ERROR:
+		dprintk(VIDC_INFO, "HFI_EVENT_SESSION_ERROR[%#x]\n",
+				pkt->session_id);
+		return hfi_process_session_error(device_id, pkt, info);
+
+	case HFI_EVENT_SESSION_SEQUENCE_CHANGED:
+		dprintk(VIDC_INFO, "HFI_EVENT_SESSION_SEQUENCE_CHANGED[%#x]\n",
+			pkt->session_id);
+		return hfi_process_sess_evt_seq_changed(device_id, pkt, info);
+
+	case HFI_EVENT_RELEASE_BUFFER_REFERENCE:
+		dprintk(VIDC_INFO, "HFI_EVENT_RELEASE_BUFFER_REFERENCE[%#x]\n",
+			pkt->session_id);
+		return hfi_process_evt_release_buffer_ref(device_id, pkt, info);
+
+	case HFI_EVENT_SESSION_PROPERTY_CHANGED:
+	default:
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_RESPONSE_UNUSED,
+		};
+
+		return 0;
+	}
+}
+
+static int hfi_process_sys_init_done(u32 device_id,
+		struct hfi_msg_sys_init_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+	enum vidc_status status = VIDC_ERR_NONE;
+
+	dprintk(VIDC_DBG, "RECEIVED: SYS_INIT_DONE\n");
+	if (sizeof(struct hfi_msg_sys_init_done_packet) > pkt->size) {
+		dprintk(VIDC_ERR, "%s: bad_pkt_size: %d\n", __func__,
+				pkt->size);
+		return -E2BIG;
+	}
+	if (!pkt->num_properties) {
+		dprintk(VIDC_ERR,
+				"hal_process_sys_init_done: no_properties\n");
+		status = VIDC_ERR_FAIL;
+		goto err_no_prop;
+	}
+
+	status = hfi_map_err_status(pkt->error_type);
+	if (status) {
+		dprintk(VIDC_ERR, "%s: status %#x\n",
+			__func__, status);
+		goto err_no_prop;
+	}
+
+err_no_prop:
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = NULL;
+	cmd_done.status = (u32)status;
+	cmd_done.size = sizeof(struct vidc_hal_sys_init_done);
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SYS_INIT_DONE,
+		.response.cmd = cmd_done,
+	};
+	return 0;
+}
+
+static int hfi_process_sys_rel_resource_done(u32 device_id,
+		struct hfi_msg_sys_release_resource_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+	enum vidc_status status = VIDC_ERR_NONE;
+	u32 pkt_size;
+
+	dprintk(VIDC_DBG, "RECEIVED: SYS_RELEASE_RESOURCE_DONE\n");
+	pkt_size = sizeof(struct hfi_msg_sys_release_resource_done_packet);
+	if (pkt_size > pkt->size) {
+		dprintk(VIDC_ERR,
+			"hal_process_sys_rel_resource_done: bad size: %d\n",
+			pkt->size);
+		return -E2BIG;
+	}
+
+	status = hfi_map_err_status(pkt->error_type);
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = NULL;
+	cmd_done.status = (u32) status;
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SYS_RELEASE_RESOURCE_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+enum hal_capability get_hal_cap_type(u32 capability_type)
+{
+	enum hal_capability hal_cap = 0;
+
+	switch (capability_type) {
+	case HFI_CAPABILITY_FRAME_WIDTH:
+		hal_cap = HAL_CAPABILITY_FRAME_WIDTH;
+		break;
+	case HFI_CAPABILITY_FRAME_HEIGHT:
+		hal_cap = HAL_CAPABILITY_FRAME_HEIGHT;
+		break;
+	case HFI_CAPABILITY_MBS_PER_FRAME:
+		hal_cap = HAL_CAPABILITY_MBS_PER_FRAME;
+		break;
+	case HFI_CAPABILITY_MBS_PER_SECOND:
+		hal_cap = HAL_CAPABILITY_MBS_PER_SECOND;
+		break;
+	case HFI_CAPABILITY_FRAMERATE:
+		hal_cap = HAL_CAPABILITY_FRAMERATE;
+		break;
+	case HFI_CAPABILITY_SCALE_X:
+		hal_cap = HAL_CAPABILITY_SCALE_X;
+		break;
+	case HFI_CAPABILITY_SCALE_Y:
+		hal_cap = HAL_CAPABILITY_SCALE_Y;
+		break;
+	case HFI_CAPABILITY_BITRATE:
+		hal_cap = HAL_CAPABILITY_BITRATE;
+		break;
+	case HFI_CAPABILITY_BFRAME:
+		hal_cap = HAL_CAPABILITY_BFRAME;
+		break;
+	case HFI_CAPABILITY_PEAKBITRATE:
+		hal_cap = HAL_CAPABILITY_PEAKBITRATE;
+		break;
+	case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS:
+		hal_cap = HAL_CAPABILITY_HIER_P_NUM_ENH_LAYERS;
+		break;
+	case HFI_CAPABILITY_ENC_LTR_COUNT:
+		hal_cap = HAL_CAPABILITY_ENC_LTR_COUNT;
+		break;
+	case HFI_CAPABILITY_CP_OUTPUT2_THRESH:
+		hal_cap = HAL_CAPABILITY_SECURE_OUTPUT2_THRESHOLD;
+		break;
+	case HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS:
+		hal_cap = HAL_CAPABILITY_HIER_B_NUM_ENH_LAYERS;
+		break;
+	case HFI_CAPABILITY_LCU_SIZE:
+		hal_cap = HAL_CAPABILITY_LCU_SIZE;
+		break;
+	case HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS:
+		hal_cap = HAL_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS;
+		break;
+	case HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE:
+		hal_cap = HAL_CAPABILITY_MBS_PER_SECOND_POWER_SAVE;
+		break;
+	default:
+		dprintk(VIDC_DBG, "%s: unknown capablity %#x\n",
+			__func__, capability_type);
+		break;
+	}
+
+	return hal_cap;
+}
+
+static inline void copy_cap_prop(
+		struct hfi_capability_supported *in,
+		struct msm_vidc_capability *capability)
+{
+	struct hal_capability_supported *out = NULL;
+
+	if (!in || !capability) {
+		dprintk(VIDC_ERR, "%s Invalid input parameters\n",
+			__func__);
+		return;
+	}
+
+	switch (in->capability_type) {
+	case HFI_CAPABILITY_FRAME_WIDTH:
+		out = &capability->width;
+		break;
+	case HFI_CAPABILITY_FRAME_HEIGHT:
+		out = &capability->height;
+		break;
+	case HFI_CAPABILITY_MBS_PER_FRAME:
+		out = &capability->mbs_per_frame;
+		break;
+	case HFI_CAPABILITY_MBS_PER_SECOND:
+		out = &capability->mbs_per_sec;
+		break;
+	case HFI_CAPABILITY_FRAMERATE:
+		out = &capability->frame_rate;
+		break;
+	case HFI_CAPABILITY_SCALE_X:
+		out = &capability->scale_x;
+		break;
+	case HFI_CAPABILITY_SCALE_Y:
+		out = &capability->scale_y;
+		break;
+	case HFI_CAPABILITY_BITRATE:
+		out = &capability->bitrate;
+		break;
+	case HFI_CAPABILITY_BFRAME:
+		out = &capability->bframe;
+		break;
+	case HFI_CAPABILITY_PEAKBITRATE:
+		out = &capability->peakbitrate;
+		break;
+	case HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS:
+		out = &capability->hier_p;
+		break;
+	case HFI_CAPABILITY_ENC_LTR_COUNT:
+		out = &capability->ltr_count;
+		break;
+	case HFI_CAPABILITY_CP_OUTPUT2_THRESH:
+		out = &capability->secure_output2_threshold;
+		break;
+	case HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS:
+		out = &capability->hier_b;
+		break;
+	case HFI_CAPABILITY_LCU_SIZE:
+		out = &capability->lcu_size;
+		break;
+	case HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS:
+		out = &capability->hier_p_hybrid;
+		break;
+	case HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE:
+		out = &capability->mbs_per_sec_power_save;
+		break;
+	default:
+		dprintk(VIDC_DBG, "%s: unknown capablity %#x\n",
+			__func__, in->capability_type);
+		break;
+	}
+
+	if (out) {
+		out->capability_type = get_hal_cap_type(in->capability_type);
+		out->min = in->min;
+		out->max = in->max;
+		out->step_size = in->step_size;
+	}
+}
+
+static int hfi_fill_codec_info(u8 *data_ptr,
+		struct vidc_hal_sys_init_done *sys_init_done) {
+	u32 i;
+	u32 codecs = 0, codec_count = 0, size = 0;
+	struct msm_vidc_capability *capability;
+	u32 prop_id = *((u32 *)data_ptr);
+	u8 *orig_data_ptr = data_ptr;
+
+	if (prop_id ==  HFI_PROPERTY_PARAM_CODEC_SUPPORTED) {
+		struct hfi_codec_supported *prop;
+
+		data_ptr = data_ptr + sizeof(u32);
+		prop = (struct hfi_codec_supported *) data_ptr;
+		sys_init_done->dec_codec_supported =
+			prop->decoder_codec_supported;
+		sys_init_done->enc_codec_supported =
+			prop->encoder_codec_supported;
+		size = sizeof(struct hfi_codec_supported) + sizeof(u32);
+	} else {
+		dprintk(VIDC_WARN,
+			"%s: prop_id %#x, expected codec_supported property\n",
+			__func__, prop_id);
+	}
+
+	codecs = sys_init_done->dec_codec_supported;
+	for (i = 0; i < 8 * sizeof(codecs); i++) {
+		if ((1 << i) & codecs) {
+			capability =
+				&sys_init_done->capabilities[codec_count++];
+			capability->codec =
+				vidc_get_hal_codec((1 << i) & codecs);
+			capability->domain =
+				vidc_get_hal_domain(HFI_VIDEO_DOMAIN_DECODER);
+		}
+	}
+	codecs = sys_init_done->enc_codec_supported;
+	for (i = 0; i < 8 * sizeof(codecs); i++) {
+		if ((1 << i) & codecs) {
+			capability =
+				&sys_init_done->capabilities[codec_count++];
+			capability->codec =
+				vidc_get_hal_codec((1 << i) & codecs);
+			capability->domain =
+				vidc_get_hal_domain(HFI_VIDEO_DOMAIN_ENCODER);
+		}
+	}
+	sys_init_done->codec_count = codec_count;
+
+	prop_id = *((u32 *)(orig_data_ptr + size));
+	if (prop_id == HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED) {
+		struct hfi_max_sessions_supported *prop =
+			(struct hfi_max_sessions_supported *)
+			(orig_data_ptr + size + sizeof(u32));
+
+		sys_init_done->max_sessions_supported = prop->max_sessions;
+		size += sizeof(struct hfi_max_sessions_supported) + sizeof(u32);
+		dprintk(VIDC_DBG, "max_sessions_supported %d\n",
+				prop->max_sessions);
+	}
+	return size;
+}
+
+enum vidc_status hfi_process_session_init_done_prop_read(
+		struct hfi_msg_sys_session_init_done_packet *pkt,
+		struct vidc_hal_session_init_done *session_init_done)
+{
+	enum vidc_status status = VIDC_ERR_NONE;
+	struct msm_vidc_capability *capability = NULL;
+	u32 rem_bytes, num_properties;
+	u8 *data_ptr;
+
+	rem_bytes = pkt->size - sizeof(struct
+			hfi_msg_sys_session_init_done_packet) + sizeof(u32);
+	if (!rem_bytes) {
+		dprintk(VIDC_ERR, "%s: invalid property info\n", __func__);
+		return VIDC_ERR_FAIL;
+	}
+
+	status = hfi_map_err_status(pkt->error_type);
+	if (status) {
+		dprintk(VIDC_ERR, "%s: error status 0x%x\n", __func__, status);
+		return status;
+	}
+
+	data_ptr = (u8 *)&pkt->rg_property_data[0];
+	num_properties = pkt->num_properties;
+
+	capability = &session_init_done->capability;
+	status = hfi_parse_init_done_properties(
+			capability, 1, data_ptr, num_properties, rem_bytes,
+			vidc_get_hfi_codec(capability->codec),
+			vidc_get_hfi_domain(capability->domain));
+	if (status) {
+		dprintk(VIDC_ERR, "%s: parse status 0x%x\n", __func__, status);
+		return status;
+	}
+
+	return status;
+}
+
+static int copy_caps_to_sessions(struct hfi_capability_supported *cap,
+		u32 num_caps, struct msm_vidc_capability *capabilities,
+		u32 num_sessions, u32 codecs, u32 domain)
+{
+	u32 i = 0, j = 0;
+	struct msm_vidc_capability *capability;
+	u32 sess_codec;
+	u32 sess_domain;
+
+	/*
+	 * iterate over num_sessions and copy all the capabilities
+	 * to matching sessions.
+	 */
+	for (i = 0; i < num_sessions; i++) {
+		sess_codec = 0;
+		sess_domain = 0;
+		capability = &capabilities[i];
+
+		if (capability->codec)
+			sess_codec =
+				vidc_get_hfi_codec(capability->codec);
+		if (capability->domain)
+			sess_domain =
+				vidc_get_hfi_domain(capability->domain);
+
+		if (!(sess_codec & codecs && sess_domain & domain))
+			continue;
+
+		for (j = 0; j < num_caps; j++)
+			copy_cap_prop(&cap[j], capability);
+	}
+
+	return 0;
+}
+
+static int copy_alloc_mode_to_sessions(
+		struct hfi_buffer_alloc_mode_supported *prop,
+		struct msm_vidc_capability *capabilities,
+		u32 num_sessions, u32 codecs, u32 domain)
+{
+	u32 i = 0, j = 0;
+	struct msm_vidc_capability *capability;
+	u32 sess_codec;
+	u32 sess_domain;
+
+	/*
+	 * iterate over num_sessions and copy all the entries
+	 * to matching sessions.
+	 */
+	for (i = 0; i < num_sessions; i++) {
+		sess_codec = 0;
+		sess_domain = 0;
+		capability = &capabilities[i];
+
+		if (capability->codec)
+			sess_codec =
+				vidc_get_hfi_codec(capability->codec);
+		if (capability->domain)
+			sess_domain =
+				vidc_get_hfi_domain(capability->domain);
+
+		if (!(sess_codec & codecs && sess_domain & domain))
+			continue;
+
+		for (j = 0; j < prop->num_entries; j++) {
+			if (prop->buffer_type == HFI_BUFFER_OUTPUT ||
+				prop->buffer_type == HFI_BUFFER_OUTPUT2) {
+				switch (prop->rg_data[j]) {
+				case HFI_BUFFER_MODE_STATIC:
+					capability->alloc_mode_out |=
+						HAL_BUFFER_MODE_STATIC;
+					break;
+				case HFI_BUFFER_MODE_RING:
+					capability->alloc_mode_out |=
+						HAL_BUFFER_MODE_RING;
+					break;
+				case HFI_BUFFER_MODE_DYNAMIC:
+					capability->alloc_mode_out |=
+						HAL_BUFFER_MODE_DYNAMIC;
+					break;
+				}
+			} else if (prop->buffer_type == HFI_BUFFER_INPUT) {
+				switch (prop->rg_data[j]) {
+				case HFI_BUFFER_MODE_STATIC:
+					capability->alloc_mode_in |=
+						HAL_BUFFER_MODE_STATIC;
+					break;
+				case HFI_BUFFER_MODE_RING:
+					capability->alloc_mode_in |=
+						HAL_BUFFER_MODE_RING;
+					break;
+				case HFI_BUFFER_MODE_DYNAMIC:
+					capability->alloc_mode_in |=
+						HAL_BUFFER_MODE_DYNAMIC;
+					break;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static enum vidc_status hfi_parse_init_done_properties(
+		struct msm_vidc_capability *capabilities,
+		u32 num_sessions, u8 *data_ptr, u32 num_properties,
+		u32 rem_bytes, u32 codecs, u32 domain)
+{
+	enum vidc_status status = VIDC_ERR_NONE;
+	u32 prop_id, next_offset;
+
+	while (status == VIDC_ERR_NONE && num_properties &&
+			rem_bytes >= sizeof(u32)) {
+
+		prop_id = *((u32 *)data_ptr);
+		next_offset = sizeof(u32);
+
+		switch (prop_id) {
+		case HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED:
+		{
+			struct hfi_codec_mask_supported *prop =
+				(struct hfi_codec_mask_supported *)
+				(data_ptr + next_offset);
+
+			codecs = prop->codecs;
+			domain = prop->video_domains;
+			next_offset += sizeof(struct hfi_codec_mask_supported);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED:
+		{
+			struct hfi_capability_supported_info *prop =
+				(struct hfi_capability_supported_info *)
+				(data_ptr + next_offset);
+
+			if ((rem_bytes - next_offset) < prop->num_capabilities *
+				sizeof(struct hfi_capability_supported)) {
+				status = VIDC_ERR_BAD_PARAM;
+				break;
+			}
+			next_offset += sizeof(u32) +
+				prop->num_capabilities *
+				sizeof(struct hfi_capability_supported);
+
+			copy_caps_to_sessions(&prop->rg_data[0],
+					prop->num_capabilities,
+					capabilities, num_sessions,
+					codecs, domain);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED:
+		{
+			struct hfi_uncompressed_format_supported *prop =
+				(struct hfi_uncompressed_format_supported *)
+				(data_ptr + next_offset);
+			u32 num_format_entries;
+			char *fmt_ptr;
+			struct hfi_uncompressed_plane_info *plane_info;
+
+			if ((rem_bytes - next_offset) < sizeof(*prop)) {
+				status = VIDC_ERR_BAD_PARAM;
+				break;
+			}
+			num_format_entries = prop->format_entries;
+			next_offset = sizeof(*prop);
+			fmt_ptr = (char *)&prop->rg_format_info[0];
+
+			while (num_format_entries) {
+				u32 bytes_to_skip;
+
+				plane_info =
+				(struct hfi_uncompressed_plane_info *) fmt_ptr;
+
+				if ((rem_bytes - next_offset) <
+						sizeof(*plane_info)) {
+					status = VIDC_ERR_BAD_PARAM;
+					break;
+				}
+				bytes_to_skip = sizeof(*plane_info) -
+					sizeof(struct
+					hfi_uncompressed_plane_constraints) +
+					plane_info->num_planes *
+					sizeof(struct
+					hfi_uncompressed_plane_constraints);
+
+				fmt_ptr += bytes_to_skip;
+				next_offset += bytes_to_skip;
+				num_format_entries--;
+			}
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED:
+		{
+			struct hfi_properties_supported *prop =
+				(struct hfi_properties_supported *)
+				(data_ptr + next_offset);
+			next_offset += sizeof(*prop) - sizeof(u32)
+				+ prop->num_properties * sizeof(u32);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED:
+		{
+			struct msm_vidc_capability capability;
+			char *ptr = NULL;
+			u32 count = 0;
+			u32 prof_count = 0;
+			struct hfi_profile_level *prof_level;
+			struct hfi_profile_level_supported *prop =
+				(struct hfi_profile_level_supported *)
+				(data_ptr + next_offset);
+
+			ptr = (char *) &prop->rg_profile_level[0];
+			prof_count = prop->profile_count;
+			next_offset += sizeof(u32);
+
+			if (prof_count > MAX_PROFILE_COUNT) {
+				prof_count = MAX_PROFILE_COUNT;
+				dprintk(VIDC_WARN,
+					"prop count exceeds max profile count\n");
+				break;
+			}
+			while (prof_count) {
+				prof_level = (struct hfi_profile_level *)ptr;
+				capability.
+				profile_level.profile_level[count].profile
+					= prof_level->profile;
+				capability.
+				profile_level.profile_level[count].level
+					= prof_level->level;
+				prof_count--;
+				count++;
+				ptr += sizeof(struct hfi_profile_level);
+				next_offset += sizeof(struct hfi_profile_level);
+			}
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED:
+		{
+			next_offset +=
+				sizeof(struct hfi_interlace_format_supported);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED:
+		{
+			next_offset +=
+				sizeof(struct hfi_nal_stream_format_supported);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT:
+		{
+			next_offset += sizeof(u32);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE:
+		{
+			next_offset += sizeof(u32);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH:
+		{
+			next_offset +=
+				sizeof(struct hfi_intra_refresh);
+			num_properties--;
+			break;
+		}
+		case HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED:
+		{
+			struct hfi_buffer_alloc_mode_supported *prop =
+				(struct hfi_buffer_alloc_mode_supported *)
+				(data_ptr + next_offset);
+
+			if (prop->num_entries >= 32) {
+				dprintk(VIDC_ERR,
+					"%s - num_entries: %d from f/w seems suspect\n",
+					__func__, prop->num_entries);
+				break;
+			}
+			next_offset +=
+				sizeof(struct hfi_buffer_alloc_mode_supported) -
+				sizeof(u32) + prop->num_entries * sizeof(u32);
+
+			copy_alloc_mode_to_sessions(prop,
+					capabilities, num_sessions,
+					codecs, domain);
+
+			num_properties--;
+			break;
+		}
+		default:
+			dprintk(VIDC_DBG,
+				"%s: default case - data_ptr %pK, prop_id 0x%x\n",
+				__func__, data_ptr, prop_id);
+			break;
+		}
+		rem_bytes -= next_offset;
+		data_ptr += next_offset;
+	}
+
+	return status;
+}
+
+enum vidc_status hfi_process_sys_init_done_prop_read(
+	struct hfi_msg_sys_init_done_packet *pkt,
+	struct vidc_hal_sys_init_done *sys_init_done)
+{
+	enum vidc_status status = VIDC_ERR_NONE;
+	u32 rem_bytes, bytes_read, num_properties;
+	u8 *data_ptr;
+	u32 codecs = 0, domain = 0;
+
+	if (!pkt || !sys_init_done) {
+		dprintk(VIDC_ERR,
+			"hfi_msg_sys_init_done: Invalid input\n");
+		return VIDC_ERR_FAIL;
+	}
+
+	rem_bytes = pkt->size - sizeof(struct
+			hfi_msg_sys_init_done_packet) + sizeof(u32);
+
+	if (!rem_bytes) {
+		dprintk(VIDC_ERR,
+			"hfi_msg_sys_init_done: missing_prop_info\n");
+		return VIDC_ERR_FAIL;
+	}
+
+	status = hfi_map_err_status(pkt->error_type);
+	if (status) {
+		dprintk(VIDC_ERR, "%s: status %#x\n", __func__, status);
+		return status;
+	}
+
+	data_ptr = (u8 *) &pkt->rg_property_data[0];
+	num_properties = pkt->num_properties;
+	dprintk(VIDC_DBG,
+		"%s: data_start %pK, num_properties %#x\n",
+		__func__, data_ptr, num_properties);
+	if (!num_properties) {
+		sys_init_done->capabilities = NULL;
+		dprintk(VIDC_DBG,
+			"Venus didn't set any properties in SYS_INIT_DONE");
+		return status;
+	}
+	bytes_read = hfi_fill_codec_info(data_ptr, sys_init_done);
+	data_ptr += bytes_read;
+	rem_bytes -= bytes_read;
+	num_properties--;
+
+	status = hfi_parse_init_done_properties(
+			sys_init_done->capabilities,
+			VIDC_MAX_SESSIONS, data_ptr, num_properties,
+			rem_bytes, codecs, domain);
+	if (status) {
+		dprintk(VIDC_ERR, "%s: parse status %#x\n",
+			__func__, status);
+		return status;
+	}
+
+	return status;
+}
+
+static void hfi_process_sess_get_prop_dec_entropy(
+	struct hfi_msg_session_property_info_packet *prop,
+	enum hal_h264_entropy *entropy)
+{
+	u32 req_bytes, hfi_entropy;
+
+	req_bytes = prop->size - sizeof(
+			struct hfi_msg_session_property_info_packet);
+
+	if (!req_bytes || req_bytes % sizeof(hfi_entropy)) {
+		dprintk(VIDC_ERR, "%s: bad packet: %d\n", __func__, req_bytes);
+		return;
+	}
+
+	hfi_entropy = prop->rg_property_data[1];
+	*entropy =
+		hfi_entropy == HFI_H264_ENTROPY_CAVLC ? HAL_H264_ENTROPY_CAVLC :
+		hfi_entropy == HFI_H264_ENTROPY_CABAC ? HAL_H264_ENTROPY_CABAC :
+							HAL_UNUSED_ENTROPY;
+}
+
+static void hfi_process_sess_get_prop_profile_level(
+	struct hfi_msg_session_property_info_packet *prop,
+	struct hfi_profile_level *profile_level)
+{
+	struct hfi_profile_level *hfi_profile_level;
+	u32 req_bytes;
+
+	dprintk(VIDC_DBG, "Entered %s\n", __func__);
+	if (!prop) {
+		dprintk(VIDC_ERR,
+			"hal_process_sess_get_profile_level: bad_prop: %pK\n",
+			prop);
+		return;
+	}
+	req_bytes = prop->size - sizeof(
+			struct hfi_msg_session_property_info_packet);
+
+	if (!req_bytes || req_bytes % sizeof(struct hfi_profile_level)) {
+		dprintk(VIDC_ERR,
+			"hal_process_sess_get_profile_level: bad_pkt: %d\n",
+			req_bytes);
+		return;
+	}
+	hfi_profile_level = (struct hfi_profile_level *)
+				&prop->rg_property_data[1];
+	profile_level->profile = hfi_profile_level->profile;
+	profile_level->level = hfi_profile_level->level;
+	dprintk(VIDC_DBG, "%s profile: %d level: %d\n",
+		__func__, profile_level->profile,
+		profile_level->level);
+}
+
+static void hfi_process_sess_get_prop_buf_req(
+	struct hfi_msg_session_property_info_packet *prop,
+	struct buffer_requirements *buffreq)
+{
+	struct hfi_buffer_requirements *hfi_buf_req;
+	u32 req_bytes;
+
+	if (!prop) {
+		dprintk(VIDC_ERR,
+			"hal_process_sess_get_prop_buf_req: bad_prop: %pK\n",
+			prop);
+		return;
+	}
+
+	req_bytes = prop->size - sizeof(
+			struct hfi_msg_session_property_info_packet);
+	if (!req_bytes || req_bytes % sizeof(struct hfi_buffer_requirements) ||
+		!prop->rg_property_data[1]) {
+		dprintk(VIDC_ERR,
+			"hal_process_sess_get_prop_buf_req: bad_pkt: %d\n",
+			req_bytes);
+		return;
+	}
+
+	hfi_buf_req = (struct hfi_buffer_requirements *)
+		&prop->rg_property_data[1];
+
+	if (!hfi_buf_req) {
+		dprintk(VIDC_ERR, "%s - invalid buffer req pointer\n",
+			__func__);
+		return;
+	}
+
+	while (req_bytes) {
+		if (hfi_buf_req->buffer_size &&
+			hfi_buf_req->buffer_count_min > hfi_buf_req->
+			buffer_count_actual)
+			dprintk(VIDC_WARN,
+				"Bad buffer requirements for %#x: min %d, actual %d\n",
+				hfi_buf_req->buffer_type,
+				hfi_buf_req->buffer_count_min,
+				hfi_buf_req->buffer_count_actual);
+
+		dprintk(VIDC_DBG, "got buffer requirements for: %d\n",
+					hfi_buf_req->buffer_type);
+		switch (hfi_buf_req->buffer_type) {
+		case HFI_BUFFER_INPUT:
+			memcpy(&buffreq->buffer[0], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[0].buffer_type = HAL_BUFFER_INPUT;
+			break;
+		case HFI_BUFFER_OUTPUT:
+			memcpy(&buffreq->buffer[1], hfi_buf_req,
+			sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[1].buffer_type = HAL_BUFFER_OUTPUT;
+			break;
+		case HFI_BUFFER_OUTPUT2:
+			memcpy(&buffreq->buffer[2], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[2].buffer_type = HAL_BUFFER_OUTPUT2;
+			break;
+		case HFI_BUFFER_EXTRADATA_INPUT:
+			memcpy(&buffreq->buffer[3], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[3].buffer_type =
+				HAL_BUFFER_EXTRADATA_INPUT;
+			break;
+		case HFI_BUFFER_EXTRADATA_OUTPUT:
+			memcpy(&buffreq->buffer[4], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[4].buffer_type =
+				HAL_BUFFER_EXTRADATA_OUTPUT;
+			break;
+		case HFI_BUFFER_EXTRADATA_OUTPUT2:
+			memcpy(&buffreq->buffer[5], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[5].buffer_type =
+				HAL_BUFFER_EXTRADATA_OUTPUT2;
+			break;
+		case HFI_BUFFER_INTERNAL_SCRATCH:
+			memcpy(&buffreq->buffer[6], hfi_buf_req,
+			sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[6].buffer_type =
+				HAL_BUFFER_INTERNAL_SCRATCH;
+			break;
+		case HFI_BUFFER_INTERNAL_SCRATCH_1:
+			memcpy(&buffreq->buffer[7], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[7].buffer_type =
+				HAL_BUFFER_INTERNAL_SCRATCH_1;
+			break;
+		case HFI_BUFFER_INTERNAL_SCRATCH_2:
+			memcpy(&buffreq->buffer[8], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[8].buffer_type =
+				HAL_BUFFER_INTERNAL_SCRATCH_2;
+			break;
+		case HFI_BUFFER_INTERNAL_PERSIST:
+			memcpy(&buffreq->buffer[9], hfi_buf_req,
+			sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[9].buffer_type =
+				HAL_BUFFER_INTERNAL_PERSIST;
+			break;
+		case HFI_BUFFER_INTERNAL_PERSIST_1:
+			memcpy(&buffreq->buffer[10], hfi_buf_req,
+				sizeof(struct hfi_buffer_requirements));
+			buffreq->buffer[10].buffer_type =
+				HAL_BUFFER_INTERNAL_PERSIST_1;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+			"hal_process_sess_get_prop_buf_req: bad_buffer_type: %d\n",
+			hfi_buf_req->buffer_type);
+			break;
+		}
+		req_bytes -= sizeof(struct hfi_buffer_requirements);
+		hfi_buf_req++;
+	}
+}
+
+static int hfi_process_session_prop_info(u32 device_id,
+		struct hfi_msg_session_property_info_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+	struct hfi_profile_level profile_level = {0};
+	enum hal_h264_entropy entropy = {0};
+	struct buffer_requirements buff_req = { { {0} } };
+
+	dprintk(VIDC_DBG, "Received SESSION_PROPERTY_INFO[%#x]\n",
+			pkt->session_id);
+
+	if (pkt->size < sizeof(struct hfi_msg_session_property_info_packet)) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_prop_info: bad_pkt_size\n");
+		return -E2BIG;
+	} else if (!pkt->num_properties) {
+		dprintk(VIDC_ERR,
+			"hal_process_session_prop_info: no_properties\n");
+		return -EINVAL;
+	}
+
+	switch (pkt->rg_property_data[0]) {
+	case HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS:
+		hfi_process_sess_get_prop_buf_req(pkt, &buff_req);
+		cmd_done.device_id = device_id;
+		cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+		cmd_done.status = VIDC_ERR_NONE;
+		cmd_done.data.property.buf_req = buff_req;
+		cmd_done.size = sizeof(buff_req);
+
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_SESSION_PROPERTY_INFO,
+			.response.cmd = cmd_done,
+		};
+
+		return 0;
+	case HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT:
+		hfi_process_sess_get_prop_profile_level(pkt, &profile_level);
+		cmd_done.device_id = device_id;
+		cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+		cmd_done.status = VIDC_ERR_NONE;
+		cmd_done.data.property.profile_level =
+			(struct hal_profile_level) {
+				.profile = profile_level.profile,
+				.level = profile_level.level,
+			};
+		cmd_done.size = sizeof(struct hal_profile_level);
+
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_SESSION_PROPERTY_INFO,
+			.response.cmd = cmd_done,
+		};
+		return 0;
+	case HFI_PROPERTY_CONFIG_VDEC_ENTROPY:
+		hfi_process_sess_get_prop_dec_entropy(pkt, &entropy);
+		cmd_done.device_id = device_id;
+		cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+		cmd_done.status = VIDC_ERR_NONE;
+		cmd_done.data.property.h264_entropy = entropy;
+		cmd_done.size = sizeof(enum hal_h264_entropy);
+
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_SESSION_PROPERTY_INFO,
+			.response.cmd = cmd_done,
+		};
+		return 0;
+	default:
+		dprintk(VIDC_DBG,
+				"hal_process_session_prop_info: unknown_prop_id: %x\n",
+				pkt->rg_property_data[0]);
+		return -ENOTSUPP;
+	}
+}
+
+static int hfi_process_session_init_done(u32 device_id,
+		struct hfi_msg_sys_session_init_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+	struct vidc_hal_session_init_done session_init_done = { {0} };
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_INIT_DONE[%x]\n", pkt->session_id);
+
+	if (sizeof(struct hfi_msg_sys_session_init_done_packet) > pkt->size) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_init_done: bad_pkt_size\n");
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	if (!cmd_done.status) {
+		cmd_done.status = hfi_process_session_init_done_prop_read(
+			pkt, &session_init_done);
+	}
+
+	cmd_done.data.session_init_done = session_init_done;
+	cmd_done.size = sizeof(struct vidc_hal_session_init_done);
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_INIT_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_load_res_done(u32 device_id,
+		struct hfi_msg_session_load_resources_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_LOAD_RESOURCES_DONE[%#x]\n",
+		pkt->session_id);
+
+	if (sizeof(struct hfi_msg_session_load_resources_done_packet) !=
+		pkt->size) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_load_res_done: bad packet size: %d\n",
+				pkt->size);
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_LOAD_RESOURCE_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_flush_done(u32 device_id,
+		struct hfi_msg_session_flush_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_FLUSH_DONE[%#x]\n",
+			pkt->session_id);
+
+	if (sizeof(struct hfi_msg_session_flush_done_packet) != pkt->size) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_flush_done: bad packet size: %d\n",
+				pkt->size);
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = sizeof(u32);
+
+	switch (pkt->flush_type) {
+	case HFI_FLUSH_OUTPUT:
+		cmd_done.data.flush_type = HAL_FLUSH_OUTPUT;
+		break;
+	case HFI_FLUSH_INPUT:
+		cmd_done.data.flush_type = HAL_FLUSH_INPUT;
+		break;
+	case HFI_FLUSH_ALL:
+		cmd_done.data.flush_type = HAL_FLUSH_ALL;
+		break;
+	default:
+		dprintk(VIDC_ERR,
+				"%s: invalid flush type!", __func__);
+		return -EINVAL;
+	}
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_FLUSH_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_etb_done(u32 device_id,
+		struct hfi_msg_session_empty_buffer_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_data_done data_done = {0};
+	struct hfi_picture_type *hfi_picture_type = NULL;
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_ETB_DONE[%#x]\n", pkt->session_id);
+
+	if (!pkt || pkt->size <
+		sizeof(struct hfi_msg_session_empty_buffer_done_packet)) {
+		dprintk(VIDC_ERR,
+				"hal_process_session_etb_done: bad_pkt_size\n");
+		return -E2BIG;
+	}
+
+	data_done.device_id = device_id;
+	data_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	data_done.status = hfi_map_err_status(pkt->error_type);
+	data_done.size = sizeof(struct msm_vidc_cb_data_done);
+	data_done.clnt_data = pkt->input_tag;
+	data_done.input_done.offset = pkt->offset;
+	data_done.input_done.filled_len = pkt->filled_len;
+	data_done.input_done.packet_buffer =
+		(ion_phys_addr_t)pkt->packet_buffer;
+	data_done.input_done.extra_data_buffer =
+		(ion_phys_addr_t)pkt->extra_data_buffer;
+	data_done.input_done.status =
+		hfi_map_err_status(pkt->error_type);
+	hfi_picture_type = (struct hfi_picture_type *)&pkt->rgData[0];
+	if (hfi_picture_type->is_sync_frame) {
+		if (hfi_picture_type->picture_type)
+			data_done.input_done.flags =
+				hfi_picture_type->picture_type;
+		else
+			dprintk(VIDC_DBG,
+				"Non-Sync frame sent for H264/HEVC\n");
+	}
+
+	trace_msm_v4l2_vidc_buffer_event_end("ETB",
+		(u32)pkt->packet_buffer, -1, -1,
+		pkt->filled_len, pkt->offset);
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_ETB_DONE,
+		.response.data = data_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_ftb_done(
+		u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_data_done data_done = {0};
+	bool is_decoder = false, is_encoder = false;
+
+	if (!msg_hdr) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	is_encoder = msg_hdr->size == sizeof(struct
+			hfi_msg_session_fill_buffer_done_compressed_packet) + 4;
+	is_decoder = msg_hdr->size == sizeof(struct
+			hfi_msg_session_fbd_uncompressed_plane0_packet) + 4;
+
+	if (!(is_encoder ^ is_decoder)) {
+		dprintk(VIDC_ERR, "Ambiguous packet (%#x) received (size %d)\n",
+				msg_hdr->packet, msg_hdr->size);
+		return -EBADHANDLE;
+	}
+
+	if (is_encoder) {
+		struct hfi_msg_session_fill_buffer_done_compressed_packet *pkt =
+		(struct hfi_msg_session_fill_buffer_done_compressed_packet *)
+		msg_hdr;
+		dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%#x]\n",
+				pkt->session_id);
+		if (sizeof(struct
+			hfi_msg_session_fill_buffer_done_compressed_packet)
+			> pkt->size) {
+			dprintk(VIDC_ERR,
+				"hal_process_session_ftb_done: bad_pkt_size\n");
+			return -E2BIG;
+		} else if (pkt->error_type != HFI_ERR_NONE) {
+			dprintk(VIDC_ERR,
+				"got buffer back with error %x\n",
+				pkt->error_type);
+			/* Proceed with the FBD */
+		}
+
+		data_done.device_id = device_id;
+		data_done.session_id = (void *)(uintptr_t)pkt->session_id;
+		data_done.status = hfi_map_err_status(pkt->error_type);
+		data_done.size = sizeof(struct msm_vidc_cb_data_done);
+		data_done.clnt_data = 0;
+
+		data_done.output_done.timestamp_hi = pkt->time_stamp_hi;
+		data_done.output_done.timestamp_lo = pkt->time_stamp_lo;
+		data_done.output_done.flags1 = pkt->flags;
+		data_done.output_done.mark_target = pkt->mark_target;
+		data_done.output_done.mark_data = pkt->mark_data;
+		data_done.output_done.stats = pkt->stats;
+		data_done.output_done.offset1 = pkt->offset;
+		data_done.output_done.alloc_len1 = pkt->alloc_len;
+		data_done.output_done.filled_len1 = pkt->filled_len;
+		data_done.output_done.picture_type = pkt->picture_type;
+		data_done.output_done.packet_buffer1 =
+			(ion_phys_addr_t)pkt->packet_buffer;
+		data_done.output_done.extra_data_buffer =
+			(ion_phys_addr_t)pkt->extra_data_buffer;
+		data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT;
+	} else /* if (is_decoder) */ {
+		struct hfi_msg_session_fbd_uncompressed_plane0_packet *pkt =
+		(struct	hfi_msg_session_fbd_uncompressed_plane0_packet *)
+		msg_hdr;
+
+		dprintk(VIDC_DBG, "RECEIVED: SESSION_FTB_DONE[%#x]\n",
+				pkt->session_id);
+		if (sizeof(
+			struct hfi_msg_session_fbd_uncompressed_plane0_packet) >
+			pkt->size) {
+			dprintk(VIDC_ERR,
+					"hal_process_session_ftb_done: bad_pkt_size\n");
+			return -E2BIG;
+		}
+
+		data_done.device_id = device_id;
+		data_done.session_id = (void *)(uintptr_t)pkt->session_id;
+		data_done.status = hfi_map_err_status(pkt->error_type);
+		data_done.size = sizeof(struct msm_vidc_cb_data_done);
+		data_done.clnt_data = 0;
+
+		data_done.output_done.stream_id = pkt->stream_id;
+		data_done.output_done.view_id = pkt->view_id;
+		data_done.output_done.timestamp_hi = pkt->time_stamp_hi;
+		data_done.output_done.timestamp_lo = pkt->time_stamp_lo;
+		data_done.output_done.flags1 = pkt->flags;
+		data_done.output_done.mark_target = pkt->mark_target;
+		data_done.output_done.mark_data = pkt->mark_data;
+		data_done.output_done.stats = pkt->stats;
+		data_done.output_done.alloc_len1 = pkt->alloc_len;
+		data_done.output_done.filled_len1 = pkt->filled_len;
+		data_done.output_done.offset1 = pkt->offset;
+		data_done.output_done.frame_width = pkt->frame_width;
+		data_done.output_done.frame_height = pkt->frame_height;
+		data_done.output_done.start_x_coord = pkt->start_x_coord;
+		data_done.output_done.start_y_coord = pkt->start_y_coord;
+		data_done.output_done.input_tag1 = pkt->input_tag;
+		data_done.output_done.picture_type = pkt->picture_type;
+		data_done.output_done.packet_buffer1 = pkt->packet_buffer;
+		data_done.output_done.extra_data_buffer =
+			pkt->extra_data_buffer;
+
+		if (!pkt->stream_id)
+			data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT;
+		else if (pkt->stream_id == 1)
+			data_done.output_done.buffer_type = HAL_BUFFER_OUTPUT2;
+	}
+
+	trace_msm_v4l2_vidc_buffer_event_end("FTB",
+		(u32)data_done.output_done.packet_buffer1,
+		(((u64)data_done.output_done.timestamp_hi) << 32)
+		+ ((u64)data_done.output_done.timestamp_lo),
+		data_done.output_done.alloc_len1,
+		data_done.output_done.filled_len1,
+		data_done.output_done.offset1);
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_FTB_DONE,
+		.response.data = data_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_start_done(u32 device_id,
+		struct hfi_msg_session_start_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_START_DONE[%#x]\n",
+			pkt->session_id);
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_start_done_packet)) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size\n",
+			__func__);
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_START_DONE,
+		.response.cmd = cmd_done,
+	};
+	return 0;
+}
+
+static int hfi_process_session_stop_done(u32 device_id,
+		struct hfi_msg_session_stop_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_STOP_DONE[%#x]\n",
+			pkt->session_id);
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_stop_done_packet)) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size\n",
+			__func__);
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_STOP_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_rel_res_done(u32 device_id,
+		struct hfi_msg_session_release_resources_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_RELEASE_RESOURCES_DONE[%#x]\n",
+		pkt->session_id);
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_session_release_resources_done_packet)) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size\n",
+			__func__);
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_RELEASE_RESOURCE_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_rel_buf_done(u32 device_id,
+		struct hfi_msg_session_release_buffers_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	if (!pkt || pkt->size <
+		sizeof(struct hfi_msg_session_release_buffers_done_packet)) {
+		dprintk(VIDC_ERR, "bad packet/packet size %d\n",
+			pkt ? pkt->size : 0);
+		return -E2BIG;
+	}
+	dprintk(VIDC_DBG, "RECEIVED:SESSION_RELEASE_BUFFER_DONE[%#x]\n",
+			pkt->session_id);
+
+	cmd_done.device_id = device_id;
+	cmd_done.size = sizeof(struct msm_vidc_cb_cmd_done);
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	if (pkt->rg_buffer_info) {
+		cmd_done.data.buffer_info =
+			*(struct hal_buffer_info *)pkt->rg_buffer_info;
+		cmd_done.size = sizeof(struct hal_buffer_info);
+	} else {
+		dprintk(VIDC_ERR, "invalid payload in rel_buff_done\n");
+	}
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_RELEASE_BUFFER_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_end_done(u32 device_id,
+		struct hfi_msg_sys_session_end_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_END_DONE[%#x]\n", pkt->session_id);
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_sys_session_end_done_packet)) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size\n", __func__);
+		return -E2BIG;
+	}
+
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_END_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_abort_done(u32 device_id,
+	struct hfi_msg_sys_session_abort_done_packet *pkt,
+	struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	dprintk(VIDC_DBG, "RECEIVED: SESSION_ABORT_DONE[%#x]\n",
+			pkt->session_id);
+
+	if (!pkt || pkt->size !=
+		sizeof(struct hfi_msg_sys_session_abort_done_packet)) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size: %d\n",
+				__func__, pkt ? pkt->size : 0);
+		return -E2BIG;
+	}
+	cmd_done.device_id = device_id;
+	cmd_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	cmd_done.status = hfi_map_err_status(pkt->error_type);
+	cmd_done.size = 0;
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_ABORT_DONE,
+		.response.cmd = cmd_done,
+	};
+
+	return 0;
+}
+
+static int hfi_process_session_get_seq_hdr_done(
+		u32 device_id,
+		struct hfi_msg_session_get_sequence_header_done_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	struct msm_vidc_cb_data_done data_done = {0};
+
+	if (!pkt || pkt->size !=
+		sizeof(struct
+		hfi_msg_session_get_sequence_header_done_packet)) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size\n",
+			__func__);
+		return -E2BIG;
+	}
+
+	dprintk(VIDC_DBG, "RECEIVED:SESSION_GET_SEQ_HDR_DONE[%#x]\n",
+			pkt->session_id);
+
+	data_done.device_id = device_id;
+	data_done.size = sizeof(struct msm_vidc_cb_data_done);
+	data_done.session_id = (void *)(uintptr_t)pkt->session_id;
+	data_done.status = hfi_map_err_status(pkt->error_type);
+	data_done.output_done.packet_buffer1 =
+		(ion_phys_addr_t)pkt->sequence_header;
+	data_done.output_done.filled_len1 = pkt->header_len;
+	dprintk(VIDC_INFO, "seq_hdr: %#x, Length: %d\n",
+			pkt->sequence_header, pkt->header_len);
+
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_SESSION_GET_SEQ_HDR_DONE,
+		.response.data = data_done,
+	};
+
+	return 0;
+}
+
+static void hfi_process_sys_get_prop_image_version(
+		struct hfi_msg_sys_property_info_packet *pkt)
+{
+	int i = 0;
+	u32 smem_block_size = 0;
+	u8 *smem_table_ptr;
+	char version[256];
+	const u32 version_string_size = 128;
+	const u32 smem_image_index_venus = 14 * 128;
+	u8 *str_image_version;
+	int req_bytes;
+
+	req_bytes = pkt->size - sizeof(*pkt);
+	if (req_bytes < version_string_size ||
+			!pkt->rg_property_data[1] ||
+			pkt->num_properties > 1) {
+		dprintk(VIDC_ERR,
+				"hfi_process_sys_get_prop_image_version: bad_pkt: %d\n",
+				req_bytes);
+		return;
+	}
+	str_image_version = (u8 *)&pkt->rg_property_data[1];
+	/*
+	 * The version string returned by firmware includes null
+	 * characters at the start and in between. Replace the null
+	 * characters with space, to print the version info.
+	 */
+	for (i = 0; i < version_string_size; i++) {
+		if (str_image_version[i] != '\0')
+			version[i] = str_image_version[i];
+		else
+			version[i] = ' ';
+	}
+	version[i] = '\0';
+	dprintk(VIDC_DBG, "F/W version: %s\n", version);
+
+	smem_table_ptr = smem_get_entry(SMEM_IMAGE_VERSION_TABLE,
+			&smem_block_size, 0, SMEM_ANY_HOST_FLAG);
+	if ((smem_image_index_venus + version_string_size) <= smem_block_size &&
+			smem_table_ptr)
+		memcpy(smem_table_ptr + smem_image_index_venus,
+				str_image_version, version_string_size);
+}
+
+static int hfi_process_sys_property_info(u32 device_id,
+		struct hfi_msg_sys_property_info_packet *pkt,
+		struct msm_vidc_cb_info *info)
+{
+	if (!pkt) {
+		dprintk(VIDC_ERR, "%s: invalid param\n", __func__);
+		return -EINVAL;
+	} else if (pkt->size < sizeof(*pkt)) {
+		dprintk(VIDC_ERR,
+				"hfi_process_sys_property_info: bad_pkt_size\n");
+		return -E2BIG;
+	} else if (!pkt->num_properties) {
+		dprintk(VIDC_ERR,
+				"hfi_process_sys_property_info: no_properties\n");
+		return -EINVAL;
+	}
+
+	switch (pkt->rg_property_data[0]) {
+	case HFI_PROPERTY_SYS_IMAGE_VERSION:
+		hfi_process_sys_get_prop_image_version(pkt);
+
+		*info = (struct msm_vidc_cb_info) {
+			.response_type =  HAL_RESPONSE_UNUSED,
+		};
+		return 0;
+	default:
+		dprintk(VIDC_DBG,
+				"hfi_process_sys_property_info: unknown_prop_id: %x\n",
+				pkt->rg_property_data[0]);
+		return -ENOTSUPP;
+	}
+
+}
+
+static int hfi_process_ignore(u32 device_id,
+		struct vidc_hal_msg_pkt_hdr *msg_hdr,
+		struct msm_vidc_cb_info *info)
+{
+	*info = (struct msm_vidc_cb_info) {
+		.response_type =  HAL_RESPONSE_UNUSED,
+	};
+
+	return 0;
+}
+
+int hfi_process_msg_packet(u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr,
+		struct msm_vidc_cb_info *info)
+{
+	typedef int (*pkt_func_def)(u32, void *, struct msm_vidc_cb_info *info);
+	pkt_func_def pkt_func = NULL;
+
+	if (!info || !msg_hdr || msg_hdr->size < VIDC_IFACEQ_MIN_PKT_SIZE) {
+		dprintk(VIDC_ERR, "%s: bad packet/packet size\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	dprintk(VIDC_DBG, "Parse response %#x\n", msg_hdr->packet);
+	switch (msg_hdr->packet) {
+	case HFI_MSG_EVENT_NOTIFY:
+		pkt_func = (pkt_func_def)hfi_process_event_notify;
+		break;
+	case  HFI_MSG_SYS_INIT_DONE:
+		pkt_func = (pkt_func_def)hfi_process_sys_init_done;
+		break;
+	case HFI_MSG_SYS_SESSION_INIT_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_init_done;
+		break;
+	case HFI_MSG_SYS_PROPERTY_INFO:
+		pkt_func = (pkt_func_def)hfi_process_sys_property_info;
+		break;
+	case HFI_MSG_SYS_SESSION_END_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_end_done;
+		break;
+	case HFI_MSG_SESSION_LOAD_RESOURCES_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_load_res_done;
+		break;
+	case HFI_MSG_SESSION_START_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_start_done;
+		break;
+	case HFI_MSG_SESSION_STOP_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_stop_done;
+		break;
+	case HFI_MSG_SESSION_EMPTY_BUFFER_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_etb_done;
+		break;
+	case HFI_MSG_SESSION_FILL_BUFFER_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_ftb_done;
+		break;
+	case HFI_MSG_SESSION_FLUSH_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_flush_done;
+		break;
+	case HFI_MSG_SESSION_PROPERTY_INFO:
+		pkt_func = (pkt_func_def)hfi_process_session_prop_info;
+		break;
+	case HFI_MSG_SESSION_RELEASE_RESOURCES_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_rel_res_done;
+		break;
+	case HFI_MSG_SYS_RELEASE_RESOURCE:
+		pkt_func = (pkt_func_def)hfi_process_sys_rel_resource_done;
+		break;
+	case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE:
+		pkt_func = (pkt_func_def) hfi_process_session_get_seq_hdr_done;
+		break;
+	case HFI_MSG_SESSION_RELEASE_BUFFERS_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_rel_buf_done;
+		break;
+	case HFI_MSG_SYS_SESSION_ABORT_DONE:
+		pkt_func = (pkt_func_def)hfi_process_session_abort_done;
+		break;
+	case HFI_MSG_SESSION_SYNC_DONE:
+		pkt_func = (pkt_func_def)hfi_process_ignore;
+		break;
+	default:
+		dprintk(VIDC_DBG, "Unable to parse message: %#x\n",
+				msg_hdr->packet);
+		break;
+	}
+
+	return pkt_func ? pkt_func(device_id, msg_hdr, info) : -ENOTSUPP;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_smem.c b/drivers/media/platform/msm/vidc/msm_smem.c
new file mode 100644
index 0000000..a949c55
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_smem.c
@@ -0,0 +1,705 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/dma-iommu.h>
+#include <linux/dma-buf.h>
+#include <linux/dma-direction.h>
+#include <linux/iommu.h>
+#include <linux/msm_dma_iommu_mapping.h>
+#include <linux/msm_ion.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include "media/msm_vidc.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_resources.h"
+
+struct smem_client {
+	int mem_type;
+	void *clnt;
+	struct msm_vidc_platform_resources *res;
+	enum session_type session_type;
+};
+
+static int get_device_address(struct smem_client *smem_client,
+		struct ion_handle *hndl, unsigned long align,
+		ion_phys_addr_t *iova, unsigned long *buffer_size,
+		unsigned long flags, enum hal_buffer buffer_type,
+		struct dma_mapping_info *mapping_info)
+{
+	int rc = 0;
+	struct ion_client *clnt = NULL;
+	struct dma_buf *buf = NULL;
+	struct dma_buf_attachment *attach;
+	struct sg_table *table = NULL;
+	struct context_bank_info *cb = NULL;
+
+	if (!iova || !buffer_size || !hndl || !smem_client || !mapping_info) {
+		dprintk(VIDC_ERR, "Invalid params: %pK, %pK, %pK, %pK\n",
+				smem_client, hndl, iova, buffer_size);
+		return -EINVAL;
+	}
+
+	clnt = smem_client->clnt;
+	if (!clnt) {
+		dprintk(VIDC_ERR, "Invalid client\n");
+		return -EINVAL;
+	}
+
+	if (is_iommu_present(smem_client->res)) {
+		cb = msm_smem_get_context_bank(smem_client, flags & SMEM_SECURE,
+				buffer_type);
+		if (!cb) {
+			dprintk(VIDC_ERR,
+				"%s: Failed to get context bank device\n",
+				 __func__);
+			rc = -EIO;
+			goto mem_map_failed;
+		}
+
+		/* Convert an Ion handle to a dma buf */
+		buf = ion_share_dma_buf(clnt, hndl);
+		if (IS_ERR_OR_NULL(buf)) {
+			rc = PTR_ERR(buf) ?: -ENOMEM;
+			dprintk(VIDC_ERR, "Share ION buf to DMA failed\n");
+			goto mem_map_failed;
+		}
+
+		/* Prepare a dma buf for dma on the given device */
+		attach = dma_buf_attach(buf, cb->dev);
+		if (IS_ERR_OR_NULL(attach)) {
+			rc = PTR_ERR(attach) ?: -ENOMEM;
+			dprintk(VIDC_ERR, "Failed to attach dmabuf\n");
+			goto mem_buf_attach_failed;
+		}
+
+		/* Get the scatterlist for the given attachment */
+		table = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
+		if (IS_ERR_OR_NULL(table)) {
+			rc = PTR_ERR(table) ?: -ENOMEM;
+			dprintk(VIDC_ERR, "Failed to map table\n");
+			goto mem_map_table_failed;
+		}
+
+		/* debug trace's need to be updated later */
+		trace_msm_smem_buffer_iommu_op_start("MAP", 0, 0,
+			align, *iova, *buffer_size);
+
+		/* Map a scatterlist into an SMMU */
+		rc = msm_dma_map_sg_lazy(cb->dev, table->sgl, table->nents,
+				DMA_BIDIRECTIONAL, buf);
+		if (rc != table->nents) {
+			dprintk(VIDC_ERR,
+				"Mapping failed with rc(%d), expected rc(%d)\n",
+				rc, table->nents);
+			rc = -ENOMEM;
+			goto mem_map_sg_failed;
+		}
+		if (table->sgl) {
+			dprintk(VIDC_DBG,
+				"%s: CB : %s, DMA buf: %pK, device: %pK, attach: %pK, table: %pK, table sgl: %pK, rc: %d, dma_address: %pa\n",
+				__func__, cb->name, buf, cb->dev, attach,
+				table, table->sgl, rc,
+				&table->sgl->dma_address);
+
+			*iova = table->sgl->dma_address;
+			*buffer_size = table->sgl->dma_length;
+		} else {
+			dprintk(VIDC_ERR, "sgl is NULL\n");
+			rc = -ENOMEM;
+			goto mem_map_sg_failed;
+		}
+
+		mapping_info->dev = cb->dev;
+		mapping_info->mapping = cb->mapping;
+		mapping_info->table = table;
+		mapping_info->attach = attach;
+		mapping_info->buf = buf;
+
+		trace_msm_smem_buffer_iommu_op_end("MAP", 0, 0,
+			align, *iova, *buffer_size);
+	} else {
+		dprintk(VIDC_DBG, "Using physical memory address\n");
+		rc = ion_phys(clnt, hndl, iova, (size_t *)buffer_size);
+		if (rc) {
+			dprintk(VIDC_ERR, "ion memory map failed - %d\n", rc);
+			goto mem_map_failed;
+		}
+	}
+
+	dprintk(VIDC_DBG, "mapped ion handle %pK to %pa\n", hndl, iova);
+	return 0;
+mem_map_sg_failed:
+	dma_buf_unmap_attachment(attach, table, DMA_BIDIRECTIONAL);
+mem_map_table_failed:
+	dma_buf_detach(buf, attach);
+mem_buf_attach_failed:
+	dma_buf_put(buf);
+mem_map_failed:
+	return rc;
+}
+
+static void put_device_address(struct smem_client *smem_client,
+	struct ion_handle *hndl, u32 flags,
+	struct dma_mapping_info *mapping_info,
+	enum hal_buffer buffer_type)
+{
+	struct ion_client *clnt = NULL;
+
+	if (!hndl || !smem_client || !mapping_info) {
+		dprintk(VIDC_WARN, "Invalid params: %pK, %pK\n",
+				smem_client, hndl);
+		return;
+	}
+
+	if (!mapping_info->dev || !mapping_info->table ||
+		!mapping_info->buf || !mapping_info->attach) {
+		dprintk(VIDC_WARN, "Invalid params:\n");
+		return;
+	}
+
+	clnt = smem_client->clnt;
+	if (!clnt) {
+		dprintk(VIDC_WARN, "Invalid client\n");
+		return;
+	}
+	if (is_iommu_present(smem_client->res)) {
+		dprintk(VIDC_DBG,
+			"Calling dma_unmap_sg - device: %pK, address: %pa, buf: %pK, table: %pK, attach: %pK\n",
+			mapping_info->dev,
+			&mapping_info->table->sgl->dma_address,
+			mapping_info->buf, mapping_info->table,
+			mapping_info->attach);
+
+		trace_msm_smem_buffer_iommu_op_start("UNMAP", 0, 0, 0, 0, 0);
+		msm_dma_unmap_sg(mapping_info->dev, mapping_info->table->sgl,
+			mapping_info->table->nents, DMA_BIDIRECTIONAL,
+			mapping_info->buf);
+		dma_buf_unmap_attachment(mapping_info->attach,
+			mapping_info->table, DMA_BIDIRECTIONAL);
+		dma_buf_detach(mapping_info->buf, mapping_info->attach);
+		dma_buf_put(mapping_info->buf);
+		trace_msm_smem_buffer_iommu_op_end("UNMAP", 0, 0, 0, 0, 0);
+	}
+}
+
+static int ion_user_to_kernel(struct smem_client *client, int fd, u32 offset,
+		struct msm_smem *mem, enum hal_buffer buffer_type)
+{
+	struct ion_handle *hndl = NULL;
+	ion_phys_addr_t iova = 0;
+	unsigned long buffer_size = 0;
+	int rc = 0;
+	unsigned long align = SZ_4K;
+	unsigned long ion_flags = 0;
+
+#ifndef CONFIG_ION
+	hndl = ion_import_dma_buf(client->clnt, fd);
+#endif
+	dprintk(VIDC_DBG, "%s ion handle: %pK\n", __func__, hndl);
+	if (IS_ERR_OR_NULL(hndl)) {
+		dprintk(VIDC_ERR, "Failed to get handle: %pK, %d, %d, %pK\n",
+				client, fd, offset, hndl);
+		rc = -ENOMEM;
+		goto fail_import_fd;
+	}
+	mem->kvaddr = NULL;
+	rc = ion_handle_get_flags(client->clnt, hndl, &ion_flags);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to get ion flags: %d\n", rc);
+		goto fail_device_address;
+	}
+
+	mem->buffer_type = buffer_type;
+	if (ion_flags & ION_FLAG_CACHED)
+		mem->flags |= SMEM_CACHED;
+
+	if (ion_flags & ION_FLAG_SECURE)
+		mem->flags |= SMEM_SECURE;
+
+	rc = get_device_address(client, hndl, align, &iova, &buffer_size,
+				mem->flags, buffer_type, &mem->mapping_info);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to get device address: %d\n", rc);
+		goto fail_device_address;
+	}
+
+	mem->mem_type = client->mem_type;
+	mem->smem_priv = hndl;
+	mem->device_addr = iova;
+	mem->size = buffer_size;
+	if ((u32)mem->device_addr != iova) {
+		dprintk(VIDC_ERR, "iova(%pa) truncated to %#x",
+			&iova, (u32)mem->device_addr);
+		goto fail_device_address;
+	}
+	dprintk(VIDC_DBG,
+		"%s: ion_handle = %pK, fd = %d, device_addr = %pa, size = %zx, kvaddr = %pK, buffer_type = %d, flags = %#lx\n",
+		__func__, mem->smem_priv, fd, &mem->device_addr, mem->size,
+		mem->kvaddr, mem->buffer_type, mem->flags);
+	return rc;
+fail_device_address:
+	ion_free(client->clnt, hndl);
+fail_import_fd:
+	return rc;
+}
+
+static int get_secure_flag_for_buffer_type(
+		struct smem_client *client, enum hal_buffer buffer_type)
+{
+
+	if (!client) {
+		dprintk(VIDC_ERR, "%s - invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (buffer_type) {
+	case HAL_BUFFER_INPUT:
+		if (client->session_type == MSM_VIDC_ENCODER)
+			return ION_FLAG_CP_PIXEL;
+		else
+			return ION_FLAG_CP_BITSTREAM;
+	case HAL_BUFFER_OUTPUT:
+	case HAL_BUFFER_OUTPUT2:
+		if (client->session_type == MSM_VIDC_ENCODER)
+			return ION_FLAG_CP_BITSTREAM;
+		else
+			return ION_FLAG_CP_PIXEL;
+	case HAL_BUFFER_INTERNAL_SCRATCH:
+		return ION_FLAG_CP_BITSTREAM;
+	case HAL_BUFFER_INTERNAL_SCRATCH_1:
+		return ION_FLAG_CP_NON_PIXEL;
+	case HAL_BUFFER_INTERNAL_SCRATCH_2:
+		return ION_FLAG_CP_PIXEL;
+	case HAL_BUFFER_INTERNAL_PERSIST:
+		return ION_FLAG_CP_BITSTREAM;
+	case HAL_BUFFER_INTERNAL_PERSIST_1:
+		return ION_FLAG_CP_NON_PIXEL;
+	default:
+		WARN(1, "No matching secure flag for buffer type : %x\n",
+				buffer_type);
+		return -EINVAL;
+	}
+}
+
+static int alloc_ion_mem(struct smem_client *client, size_t size, u32 align,
+	u32 flags, enum hal_buffer buffer_type, struct msm_smem *mem,
+	int map_kernel)
+{
+	struct ion_handle *hndl;
+	ion_phys_addr_t iova = 0;
+	unsigned long buffer_size = 0;
+	unsigned long heap_mask = 0;
+	int rc = 0;
+	int ion_flags = 0;
+
+	align = ALIGN(align, SZ_4K);
+	size = ALIGN(size, SZ_4K);
+
+	if (is_iommu_present(client->res)) {
+		heap_mask = ION_HEAP(ION_IOMMU_HEAP_ID);
+	} else {
+		dprintk(VIDC_DBG,
+			"allocate shared memory from adsp heap size %zx align %d\n",
+			size, align);
+		heap_mask = ION_HEAP(ION_ADSP_HEAP_ID);
+	}
+
+	if (flags & SMEM_CACHED)
+		ion_flags |= ION_FLAG_CACHED;
+
+	if (flags & SMEM_SECURE) {
+		int secure_flag =
+			get_secure_flag_for_buffer_type(client, buffer_type);
+		if (secure_flag < 0) {
+			rc = secure_flag;
+			goto fail_shared_mem_alloc;
+		}
+
+		ion_flags |= ION_FLAG_SECURE | secure_flag;
+		heap_mask = ION_HEAP(ION_SECURE_HEAP_ID);
+
+		if (client->res->slave_side_cp) {
+			heap_mask = ION_HEAP(ION_CP_MM_HEAP_ID);
+			size = ALIGN(size, SZ_1M);
+			align = ALIGN(size, SZ_1M);
+		}
+	}
+
+	trace_msm_smem_buffer_ion_op_start("ALLOC", (u32)buffer_type,
+		heap_mask, size, align, flags, map_kernel);
+	hndl = ion_alloc(client->clnt, size, align, heap_mask, ion_flags);
+	if (IS_ERR_OR_NULL(hndl)) {
+		dprintk(VIDC_ERR,
+		"Failed to allocate shared memory = %pK, %zx, %d, %#x\n",
+		client, size, align, flags);
+		rc = -ENOMEM;
+		goto fail_shared_mem_alloc;
+	}
+	trace_msm_smem_buffer_ion_op_end("ALLOC", (u32)buffer_type,
+		heap_mask, size, align, flags, map_kernel);
+	mem->mem_type = client->mem_type;
+	mem->smem_priv = hndl;
+	mem->flags = flags;
+	mem->buffer_type = buffer_type;
+	if (map_kernel) {
+		mem->kvaddr = ion_map_kernel(client->clnt, hndl);
+		if (IS_ERR_OR_NULL(mem->kvaddr)) {
+			dprintk(VIDC_ERR,
+				"Failed to map shared mem in kernel\n");
+			rc = -EIO;
+			goto fail_map;
+		}
+	} else {
+		mem->kvaddr = NULL;
+	}
+
+	rc = get_device_address(client, hndl, align, &iova, &buffer_size,
+				flags, buffer_type, &mem->mapping_info);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to get device address: %d\n",
+			rc);
+		goto fail_device_address;
+	}
+	mem->device_addr = iova;
+	if ((u32)mem->device_addr != iova) {
+		dprintk(VIDC_ERR, "iova(%pa) truncated to %#x",
+			&iova, (u32)mem->device_addr);
+		goto fail_device_address;
+	}
+	mem->size = size;
+	dprintk(VIDC_DBG,
+		"%s: ion_handle = %pK, device_addr = %pa, size = %#zx, kvaddr = %pK, buffer_type = %#x, flags = %#lx\n",
+		__func__, mem->smem_priv, &mem->device_addr,
+		mem->size, mem->kvaddr, mem->buffer_type, mem->flags);
+	return rc;
+fail_device_address:
+	if (mem->kvaddr)
+		ion_unmap_kernel(client->clnt, hndl);
+fail_map:
+	ion_free(client->clnt, hndl);
+fail_shared_mem_alloc:
+	return rc;
+}
+
+static void free_ion_mem(struct smem_client *client, struct msm_smem *mem)
+{
+	dprintk(VIDC_DBG,
+		"%s: ion_handle = %pK, device_addr = %pa, size = %#zx, kvaddr = %pK, buffer_type = %#x\n",
+		__func__, mem->smem_priv, &mem->device_addr,
+		mem->size, mem->kvaddr, mem->buffer_type);
+
+	if (mem->device_addr)
+		put_device_address(client, mem->smem_priv, mem->flags,
+			&mem->mapping_info, mem->buffer_type);
+
+	if (mem->kvaddr)
+		ion_unmap_kernel(client->clnt, mem->smem_priv);
+	if (mem->smem_priv) {
+		trace_msm_smem_buffer_ion_op_start("FREE",
+				(u32)mem->buffer_type, -1, mem->size, -1,
+				mem->flags, -1);
+		dprintk(VIDC_DBG,
+			"%s: Freeing handle %pK, client: %pK\n",
+			__func__, mem->smem_priv, client->clnt);
+		ion_free(client->clnt, mem->smem_priv);
+		trace_msm_smem_buffer_ion_op_end("FREE", (u32)mem->buffer_type,
+			-1, mem->size, -1, mem->flags, -1);
+	}
+}
+
+static void *ion_new_client(void)
+{
+	struct ion_client *client = NULL;
+
+	client = msm_ion_client_create("video_client");
+	if (!client)
+		dprintk(VIDC_ERR, "Failed to create smem client\n");
+	return client;
+};
+
+static void ion_delete_client(struct smem_client *client)
+{
+	ion_client_destroy(client->clnt);
+}
+
+struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
+		enum hal_buffer buffer_type)
+{
+	struct smem_client *client = clt;
+	int rc = 0;
+	struct msm_smem *mem;
+
+	if (fd < 0) {
+		dprintk(VIDC_ERR, "Invalid fd: %d\n", fd);
+		return NULL;
+	}
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem) {
+		dprintk(VIDC_ERR, "Failed to allocate shared mem\n");
+		return NULL;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		rc = ion_user_to_kernel(clt, fd, offset, mem, buffer_type);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Mem type not supported\n");
+		rc = -EINVAL;
+		break;
+	}
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to allocate shared memory\n");
+		kfree(mem);
+		mem = NULL;
+	}
+	return mem;
+}
+
+bool msm_smem_compare_buffers(void *clt, int fd, void *priv)
+{
+	struct smem_client *client = clt;
+	struct ion_handle *handle = NULL;
+	bool ret = false;
+
+	if (!clt || !priv) {
+		dprintk(VIDC_ERR, "Invalid params: %pK, %pK\n",
+			clt, priv);
+		return false;
+	}
+#ifndef CONFIG_ION
+	handle = ion_import_dma_buf(client->clnt, fd);
+#endif
+	ret = handle == priv;
+	(!IS_ERR_OR_NULL(handle)) ? ion_free(client->clnt, handle) : 0;
+	return ret;
+}
+
+static int ion_cache_operations(struct smem_client *client,
+	struct msm_smem *mem, enum smem_cache_ops cache_op)
+{
+	unsigned long ionflag = 0;
+	int rc = 0;
+	int msm_cache_ops = 0;
+
+	if (!mem || !client) {
+		dprintk(VIDC_ERR, "Invalid params: %pK, %pK\n",
+			mem, client);
+		return -EINVAL;
+	}
+	rc = ion_handle_get_flags(client->clnt,	mem->smem_priv,
+		&ionflag);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"ion_handle_get_flags failed: %d\n", rc);
+		goto cache_op_failed;
+	}
+	if (ION_IS_CACHED(ionflag)) {
+		switch (cache_op) {
+		case SMEM_CACHE_CLEAN:
+			msm_cache_ops = ION_IOC_CLEAN_CACHES;
+			break;
+		case SMEM_CACHE_INVALIDATE:
+			msm_cache_ops = ION_IOC_INV_CACHES;
+			break;
+		case SMEM_CACHE_CLEAN_INVALIDATE:
+			msm_cache_ops = ION_IOC_CLEAN_INV_CACHES;
+			break;
+		default:
+			dprintk(VIDC_ERR, "cache operation not supported\n");
+			rc = -EINVAL;
+			goto cache_op_failed;
+		}
+		rc = msm_ion_do_cache_op(client->clnt,
+			(struct ion_handle *)mem->smem_priv,
+			0, (unsigned long)mem->size,
+			msm_cache_ops);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"cache operation failed %d\n", rc);
+			goto cache_op_failed;
+		}
+	}
+cache_op_failed:
+	return rc;
+}
+
+int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
+		enum smem_cache_ops cache_op)
+{
+	struct smem_client *client = clt;
+	int rc = 0;
+
+	if (!client) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n",
+			client);
+		return -EINVAL;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		rc = ion_cache_operations(client, mem, cache_op);
+		if (rc)
+			dprintk(VIDC_ERR,
+			"Failed cache operations: %d\n", rc);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Mem type not supported\n");
+		break;
+	}
+	return rc;
+}
+
+void *msm_smem_new_client(enum smem_type mtype,
+		void *platform_resources, enum session_type stype)
+{
+	struct smem_client *client = NULL;
+	void *clnt = NULL;
+	struct msm_vidc_platform_resources *res = platform_resources;
+
+	switch (mtype) {
+	case SMEM_ION:
+		clnt = ion_new_client();
+		break;
+	default:
+		dprintk(VIDC_ERR, "Mem type not supported\n");
+		break;
+	}
+	if (clnt) {
+		client = kzalloc(sizeof(*client), GFP_KERNEL);
+		if (client) {
+			client->mem_type = mtype;
+			client->clnt = clnt;
+			client->res = res;
+			client->session_type = stype;
+		}
+	} else {
+		dprintk(VIDC_ERR, "Failed to create new client: mtype = %d\n",
+			mtype);
+	}
+	return client;
+}
+
+struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
+		enum hal_buffer buffer_type, int map_kernel)
+{
+	struct smem_client *client;
+	int rc = 0;
+	struct msm_smem *mem;
+
+	client = clt;
+	if (!client) {
+		dprintk(VIDC_ERR, "Invalid  client passed\n");
+		return NULL;
+	}
+	if (!size) {
+		dprintk(VIDC_ERR, "No need to allocate memory of size: %zx\n",
+			size);
+		return NULL;
+	}
+	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
+	if (!mem) {
+		dprintk(VIDC_ERR, "Failed to allocate shared mem\n");
+		return NULL;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		rc = alloc_ion_mem(client, size, align, flags, buffer_type,
+					mem, map_kernel);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Mem type not supported\n");
+		rc = -EINVAL;
+		break;
+	}
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to allocate shared memory\n");
+		kfree(mem);
+		mem = NULL;
+	}
+	return mem;
+}
+
+void msm_smem_free(void *clt, struct msm_smem *mem)
+{
+	struct smem_client *client = clt;
+
+	if (!client || !mem) {
+		dprintk(VIDC_ERR, "Invalid  client/handle passed\n");
+		return;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		free_ion_mem(client, mem);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Mem type not supported\n");
+		break;
+	}
+	kfree(mem);
+};
+
+void msm_smem_delete_client(void *clt)
+{
+	struct smem_client *client = clt;
+
+	if (!client) {
+		dprintk(VIDC_ERR, "Invalid  client passed\n");
+		return;
+	}
+	switch (client->mem_type) {
+	case SMEM_ION:
+		ion_delete_client(client);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Mem type not supported\n");
+		break;
+	}
+	kfree(client);
+}
+
+struct context_bank_info *msm_smem_get_context_bank(void *clt,
+			bool is_secure, enum hal_buffer buffer_type)
+{
+	struct smem_client *client = clt;
+	struct context_bank_info *cb = NULL, *match = NULL;
+
+	if (!clt) {
+		dprintk(VIDC_ERR, "%s - invalid params\n", __func__);
+		return NULL;
+	}
+
+	/*
+	 * HAL_BUFFER_INPUT is directly mapped to bitstream CB in DT
+	 * as the buffer type structure was initially designed
+	 * just for decoder. For Encoder, input should be mapped to
+	 * pixel CB. So swap the buffer types just in this local scope.
+	 */
+	if (is_secure && client->session_type == MSM_VIDC_ENCODER) {
+		if (buffer_type == HAL_BUFFER_INPUT)
+			buffer_type = HAL_BUFFER_OUTPUT;
+		else if (buffer_type == HAL_BUFFER_OUTPUT)
+			buffer_type = HAL_BUFFER_INPUT;
+	}
+
+	list_for_each_entry(cb, &client->res->context_banks, list) {
+		if (cb->is_secure == is_secure &&
+				cb->buffer_type & buffer_type) {
+			match = cb;
+			dprintk(VIDC_DBG,
+				"context bank found for CB : %s, device: %pK mapping: %pK\n",
+				match->name, match->dev, match->mapping);
+			break;
+		}
+	}
+
+	return match;
+}
diff --git a/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
new file mode 100644
index 0000000..7136444
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_v4l2_vidc.c
@@ -0,0 +1,794 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/io.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_common.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_internal.h"
+#include "msm_vidc_res_parse.h"
+#include "msm_vidc_resources.h"
+#include "venus_boot.h"
+#include "vidc_hfi_api.h"
+
+#define BASE_DEVICE_NUMBER 32
+
+struct msm_vidc_drv *vidc_driver;
+
+uint32_t msm_vidc_pwr_collapse_delay = 3000;
+
+static inline struct msm_vidc_inst *get_vidc_inst(struct file *filp, void *fh)
+{
+	return container_of(filp->private_data,
+					struct msm_vidc_inst, event_handler);
+}
+
+static int msm_v4l2_open(struct file *filp)
+{
+	struct video_device *vdev = video_devdata(filp);
+	struct msm_video_device *vid_dev =
+		container_of(vdev, struct msm_video_device, vdev);
+	struct msm_vidc_core *core = video_drvdata(filp);
+	struct msm_vidc_inst *vidc_inst;
+
+	trace_msm_v4l2_vidc_open_start("msm_v4l2_open start");
+	vidc_inst = msm_vidc_open(core->id, vid_dev->type);
+	if (!vidc_inst) {
+		dprintk(VIDC_ERR,
+		"Failed to create video instance, core: %d, type = %d\n",
+		core->id, vid_dev->type);
+		return -ENOMEM;
+	}
+	clear_bit(V4L2_FL_USES_V4L2_FH, &vdev->flags);
+	filp->private_data = &(vidc_inst->event_handler);
+	trace_msm_v4l2_vidc_open_end("msm_v4l2_open end");
+	return 0;
+}
+
+static int msm_v4l2_close(struct file *filp)
+{
+	int rc = 0;
+	struct msm_vidc_inst *vidc_inst;
+
+	trace_msm_v4l2_vidc_close_start("msm_v4l2_close start");
+	vidc_inst = get_vidc_inst(filp, NULL);
+	rc = msm_vidc_release_buffers(vidc_inst,
+			V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	if (rc)
+		dprintk(VIDC_WARN,
+			"Failed in %s for release output buffers\n", __func__);
+
+	rc = msm_vidc_close(vidc_inst);
+	trace_msm_v4l2_vidc_close_end("msm_v4l2_close end");
+	return rc;
+}
+
+static int msm_v4l2_querycap(struct file *filp, void *fh,
+			struct v4l2_capability *cap)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, fh);
+
+	return msm_vidc_querycap((void *)vidc_inst, cap);
+}
+
+int msm_v4l2_enum_fmt(struct file *file, void *fh,
+					struct v4l2_fmtdesc *f)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_enum_fmt((void *)vidc_inst, f);
+}
+
+int msm_v4l2_s_fmt(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_s_fmt((void *)vidc_inst, f);
+}
+
+int msm_v4l2_g_fmt(struct file *file, void *fh,
+					struct v4l2_format *f)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_g_fmt((void *)vidc_inst, f);
+}
+
+int msm_v4l2_s_ctrl(struct file *file, void *fh,
+					struct v4l2_control *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_s_ctrl((void *)vidc_inst, a);
+}
+
+int msm_v4l2_g_ctrl(struct file *file, void *fh,
+					struct v4l2_control *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_g_ctrl((void *)vidc_inst, a);
+}
+
+int msm_v4l2_s_ext_ctrl(struct file *file, void *fh,
+					struct v4l2_ext_controls *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_s_ext_ctrl((void *)vidc_inst, a);
+}
+
+int msm_v4l2_reqbufs(struct file *file, void *fh,
+				struct v4l2_requestbuffers *b)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_reqbufs((void *)vidc_inst, b);
+}
+
+int msm_v4l2_qbuf(struct file *file, void *fh,
+				struct v4l2_buffer *b)
+{
+	return msm_vidc_qbuf(get_vidc_inst(file, fh), b);
+}
+
+int msm_v4l2_dqbuf(struct file *file, void *fh,
+				struct v4l2_buffer *b)
+{
+	return msm_vidc_dqbuf(get_vidc_inst(file, fh), b);
+}
+
+int msm_v4l2_streamon(struct file *file, void *fh,
+				enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_streamon((void *)vidc_inst, i);
+}
+
+int msm_v4l2_streamoff(struct file *file, void *fh,
+				enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_streamoff((void *)vidc_inst, i);
+}
+
+static int msm_v4l2_subscribe_event(struct v4l2_fh *fh,
+				const struct v4l2_event_subscription *sub)
+{
+	struct msm_vidc_inst *vidc_inst = container_of(fh,
+			struct msm_vidc_inst, event_handler);
+
+	return msm_vidc_subscribe_event((void *)vidc_inst, sub);
+}
+
+static int msm_v4l2_unsubscribe_event(struct v4l2_fh *fh,
+				const struct v4l2_event_subscription *sub)
+{
+	struct msm_vidc_inst *vidc_inst = container_of(fh,
+			struct msm_vidc_inst, event_handler);
+
+	return msm_vidc_unsubscribe_event((void *)vidc_inst, sub);
+}
+
+static int msm_v4l2_decoder_cmd(struct file *file, void *fh,
+				struct v4l2_decoder_cmd *dec)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_comm_cmd((void *)vidc_inst, (union msm_v4l2_cmd *)dec);
+}
+
+static int msm_v4l2_encoder_cmd(struct file *file, void *fh,
+				struct v4l2_encoder_cmd *enc)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_comm_cmd((void *)vidc_inst, (union msm_v4l2_cmd *)enc);
+}
+static int msm_v4l2_s_parm(struct file *file, void *fh,
+			struct v4l2_streamparm *a)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_comm_s_parm(vidc_inst, a);
+}
+static int msm_v4l2_g_parm(struct file *file, void *fh,
+		struct v4l2_streamparm *a)
+{
+	return 0;
+}
+
+static int msm_v4l2_enum_framesizes(struct file *file, void *fh,
+				struct v4l2_frmsizeenum *fsize)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(file, fh);
+
+	return msm_vidc_enum_framesizes((void *)vidc_inst, fsize);
+}
+
+static const struct v4l2_ioctl_ops msm_v4l2_ioctl_ops = {
+	.vidioc_querycap = msm_v4l2_querycap,
+	.vidioc_enum_fmt_vid_cap_mplane = msm_v4l2_enum_fmt,
+	.vidioc_enum_fmt_vid_out_mplane = msm_v4l2_enum_fmt,
+	.vidioc_s_fmt_vid_cap_mplane = msm_v4l2_s_fmt,
+	.vidioc_s_fmt_vid_out_mplane = msm_v4l2_s_fmt,
+	.vidioc_g_fmt_vid_cap_mplane = msm_v4l2_g_fmt,
+	.vidioc_g_fmt_vid_out_mplane = msm_v4l2_g_fmt,
+	.vidioc_reqbufs = msm_v4l2_reqbufs,
+	.vidioc_qbuf = msm_v4l2_qbuf,
+	.vidioc_dqbuf = msm_v4l2_dqbuf,
+	.vidioc_streamon = msm_v4l2_streamon,
+	.vidioc_streamoff = msm_v4l2_streamoff,
+	.vidioc_s_ctrl = msm_v4l2_s_ctrl,
+	.vidioc_g_ctrl = msm_v4l2_g_ctrl,
+	.vidioc_s_ext_ctrls = msm_v4l2_s_ext_ctrl,
+	.vidioc_subscribe_event = msm_v4l2_subscribe_event,
+	.vidioc_unsubscribe_event = msm_v4l2_unsubscribe_event,
+	.vidioc_decoder_cmd = msm_v4l2_decoder_cmd,
+	.vidioc_encoder_cmd = msm_v4l2_encoder_cmd,
+	.vidioc_s_parm = msm_v4l2_s_parm,
+	.vidioc_g_parm = msm_v4l2_g_parm,
+	.vidioc_enum_framesizes = msm_v4l2_enum_framesizes,
+};
+
+static const struct v4l2_ioctl_ops msm_v4l2_enc_ioctl_ops = {
+};
+
+static unsigned int msm_v4l2_poll(struct file *filp,
+	struct poll_table_struct *pt)
+{
+	struct msm_vidc_inst *vidc_inst = get_vidc_inst(filp, NULL);
+
+	return msm_vidc_poll((void *)vidc_inst, filp, pt);
+}
+
+static const struct v4l2_file_operations msm_v4l2_vidc_fops = {
+	.owner = THIS_MODULE,
+	.open = msm_v4l2_open,
+	.release = msm_v4l2_close,
+	.unlocked_ioctl = video_ioctl2,
+	.poll = msm_v4l2_poll,
+};
+
+void msm_vidc_release_video_device(struct video_device *pvdev)
+{
+}
+
+static int read_platform_resources(struct msm_vidc_core *core,
+		struct platform_device *pdev)
+{
+	int rc = 0;
+
+	if (!core || !pdev) {
+		dprintk(VIDC_ERR, "%s: Invalid params %pK %pK\n",
+			__func__, core, pdev);
+		return -EINVAL;
+	}
+
+	core->hfi_type = VIDC_HFI_VENUS;
+	core->resources.pdev = pdev;
+	if (pdev->dev.of_node) {
+		/* Target supports DT, parse from it */
+		rc = read_platform_resources_from_dt(&core->resources);
+	} else {
+		dprintk(VIDC_ERR, "pdev node is NULL\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_vidc_initialize_core(struct platform_device *pdev,
+				struct msm_vidc_core *core)
+{
+	int i = 0;
+	int rc = 0;
+
+	if (!core)
+		return -EINVAL;
+	rc = read_platform_resources(core, pdev);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to get platform resources\n");
+		return rc;
+	}
+
+	INIT_LIST_HEAD(&core->instances);
+	mutex_init(&core->lock);
+
+	core->state = VIDC_CORE_UNINIT;
+	for (i = SYS_MSG_INDEX(SYS_MSG_START);
+		i <= SYS_MSG_INDEX(SYS_MSG_END); i++) {
+		init_completion(&core->completions[i]);
+	}
+
+	INIT_DELAYED_WORK(&core->fw_unload_work, msm_vidc_fw_unload_handler);
+	return rc;
+}
+
+static ssize_t msm_vidc_link_name_show(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	struct msm_vidc_core *core = dev_get_drvdata(dev);
+
+	if (core)
+		if (dev == &core->vdev[MSM_VIDC_DECODER].vdev.dev)
+			return snprintf(buf, PAGE_SIZE, "venus_dec");
+		else if (dev == &core->vdev[MSM_VIDC_ENCODER].vdev.dev)
+			return snprintf(buf, PAGE_SIZE, "venus_enc");
+		else
+			return 0;
+	else
+		return 0;
+}
+
+static DEVICE_ATTR(link_name, 0444, msm_vidc_link_name_show, NULL);
+
+static ssize_t store_pwr_collapse_delay(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	unsigned long val = 0;
+	int rc = 0;
+
+	rc = kstrtoul(buf, 0, &val);
+	if (rc)
+		return rc;
+	else if (!val)
+		return -EINVAL;
+	msm_vidc_pwr_collapse_delay = val;
+	return count;
+}
+
+static ssize_t show_pwr_collapse_delay(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", msm_vidc_pwr_collapse_delay);
+}
+
+static DEVICE_ATTR(pwr_collapse_delay, 0644, show_pwr_collapse_delay,
+		store_pwr_collapse_delay);
+
+static ssize_t show_thermal_level(struct device *dev,
+		struct device_attribute *attr,
+		char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%d\n", vidc_driver->thermal_level);
+}
+
+static ssize_t store_thermal_level(struct device *dev,
+		struct device_attribute *attr,
+		const char *buf, size_t count)
+{
+	int rc = 0, val = 0;
+
+	rc = kstrtoint(buf, 0, &val);
+	if (rc || val < 0) {
+		dprintk(VIDC_WARN,
+			"Invalid thermal level value: %s\n", buf);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG, "Thermal level old %d new %d\n",
+			vidc_driver->thermal_level, val);
+
+	if (val == vidc_driver->thermal_level)
+		return count;
+	vidc_driver->thermal_level = val;
+
+	msm_comm_handle_thermal_event();
+	return count;
+}
+
+static DEVICE_ATTR(thermal_level, 0644, show_thermal_level,
+		store_thermal_level);
+
+static ssize_t show_platform_version(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	return scnprintf(buf, PAGE_SIZE, "%d",
+			vidc_driver->platform_version);
+}
+
+static ssize_t store_platform_version(struct device *dev,
+		struct device_attribute *attr, const char *buf,
+		size_t count)
+{
+	dprintk(VIDC_WARN, "store platform version is not allowed\n");
+	return count;
+}
+
+static DEVICE_ATTR(platform_version, 0444, show_platform_version,
+		store_platform_version);
+
+static struct attribute *msm_vidc_core_attrs[] = {
+		&dev_attr_pwr_collapse_delay.attr,
+		&dev_attr_thermal_level.attr,
+		&dev_attr_platform_version.attr,
+		NULL
+};
+
+static struct attribute_group msm_vidc_core_attr_group = {
+		.attrs = msm_vidc_core_attrs,
+};
+
+static const struct of_device_id msm_vidc_dt_match[] = {
+	{.compatible = "qcom,msm-vidc"},
+	{.compatible = "qcom,msm-vidc,context-bank"},
+	{.compatible = "qcom,msm-vidc,bus"},
+	{}
+};
+
+static int msm_vidc_probe_vidc_device(struct platform_device *pdev)
+{
+	int rc = 0;
+	void __iomem *base;
+	struct resource *res;
+	struct msm_vidc_core *core;
+	struct device *dev;
+	int nr = BASE_DEVICE_NUMBER;
+
+	core = kzalloc(sizeof(*core), GFP_KERNEL);
+	if (!core || !vidc_driver) {
+		dprintk(VIDC_ERR,
+			"Failed to allocate memory for device core\n");
+		rc = -ENOMEM;
+		goto err_no_mem;
+	}
+
+	dev_set_drvdata(&pdev->dev, core);
+	rc = msm_vidc_initialize_core(pdev, core);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to init core\n");
+		goto err_core_init;
+	}
+	rc = sysfs_create_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to create attributes\n");
+		goto err_core_init;
+	}
+
+	core->id = MSM_VIDC_CORE_VENUS;
+
+	rc = v4l2_device_register(&pdev->dev, &core->v4l2_dev);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to register v4l2 device\n");
+		goto err_v4l2_register;
+	}
+
+	/* setup the decoder device */
+	core->vdev[MSM_VIDC_DECODER].vdev.release =
+		msm_vidc_release_video_device;
+	core->vdev[MSM_VIDC_DECODER].vdev.fops = &msm_v4l2_vidc_fops;
+	core->vdev[MSM_VIDC_DECODER].vdev.ioctl_ops = &msm_v4l2_ioctl_ops;
+	core->vdev[MSM_VIDC_DECODER].vdev.vfl_dir = VFL_DIR_M2M;
+	core->vdev[MSM_VIDC_DECODER].type = MSM_VIDC_DECODER;
+	core->vdev[MSM_VIDC_DECODER].vdev.v4l2_dev = &core->v4l2_dev;
+	rc = video_register_device(&core->vdev[MSM_VIDC_DECODER].vdev,
+					VFL_TYPE_GRABBER, nr);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to register video decoder device");
+		goto err_dec_register;
+	}
+
+	video_set_drvdata(&core->vdev[MSM_VIDC_DECODER].vdev, core);
+	dev = &core->vdev[MSM_VIDC_DECODER].vdev.dev;
+	rc = device_create_file(dev, &dev_attr_link_name);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to create link name sysfs for decoder");
+		goto err_dec_attr_link_name;
+	}
+
+	/* setup the encoder device */
+	core->vdev[MSM_VIDC_ENCODER].vdev.release =
+		msm_vidc_release_video_device;
+	core->vdev[MSM_VIDC_ENCODER].vdev.fops = &msm_v4l2_vidc_fops;
+	core->vdev[MSM_VIDC_ENCODER].vdev.ioctl_ops = &msm_v4l2_ioctl_ops;
+	core->vdev[MSM_VIDC_ENCODER].vdev.vfl_dir = VFL_DIR_M2M;
+	core->vdev[MSM_VIDC_ENCODER].type = MSM_VIDC_ENCODER;
+	core->vdev[MSM_VIDC_ENCODER].vdev.v4l2_dev = &core->v4l2_dev;
+	rc = video_register_device(&core->vdev[MSM_VIDC_ENCODER].vdev,
+				VFL_TYPE_GRABBER, nr + 1);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to register video encoder device");
+		goto err_enc_register;
+	}
+
+	video_set_drvdata(&core->vdev[MSM_VIDC_ENCODER].vdev, core);
+	dev = &core->vdev[MSM_VIDC_ENCODER].vdev.dev;
+	rc = device_create_file(dev, &dev_attr_link_name);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to create link name sysfs for encoder");
+		goto err_enc_attr_link_name;
+	}
+
+	/* finish setting up the 'core' */
+	mutex_lock(&vidc_driver->lock);
+	if (vidc_driver->num_cores  + 1 > MSM_VIDC_CORES_MAX) {
+		mutex_unlock(&vidc_driver->lock);
+		dprintk(VIDC_ERR, "Maximum cores already exist, core_no = %d\n",
+				vidc_driver->num_cores);
+		goto err_cores_exceeded;
+	}
+	vidc_driver->num_cores++;
+	mutex_unlock(&vidc_driver->lock);
+
+	core->device = vidc_hfi_initialize(core->hfi_type, core->id,
+				&core->resources, &handle_cmd_response);
+	if (IS_ERR_OR_NULL(core->device)) {
+		mutex_lock(&vidc_driver->lock);
+		vidc_driver->num_cores--;
+		mutex_unlock(&vidc_driver->lock);
+
+		rc = PTR_ERR(core->device) ?: -EBADHANDLE;
+		if (rc != -EPROBE_DEFER)
+			dprintk(VIDC_ERR, "Failed to create HFI device\n");
+		else
+			dprintk(VIDC_DBG, "msm_vidc: request probe defer\n");
+		goto err_cores_exceeded;
+	}
+
+	mutex_lock(&vidc_driver->lock);
+	list_add_tail(&core->list, &vidc_driver->cores);
+	mutex_unlock(&vidc_driver->lock);
+
+	core->debugfs_root = msm_vidc_debugfs_init_core(
+		core, vidc_driver->debugfs_root);
+
+	vidc_driver->platform_version = 0;
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
+	if (!res) {
+		dprintk(VIDC_DBG, "failed to get efuse resource\n");
+	} else {
+		base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+		if (!base) {
+			dprintk(VIDC_ERR,
+				"failed efuse ioremap: res->start %#x, size %d\n",
+				(u32)res->start, (u32)resource_size(res));
+		} else {
+			u32 efuse = 0;
+			struct platform_version_table *pf_ver_tbl =
+				core->resources.pf_ver_tbl;
+
+			efuse = readl_relaxed(base);
+			vidc_driver->platform_version =
+				(efuse & pf_ver_tbl->version_mask) >>
+				pf_ver_tbl->version_shift;
+			dprintk(VIDC_DBG,
+				"efuse 0x%x, platform version 0x%x\n",
+				efuse, vidc_driver->platform_version);
+
+			devm_iounmap(&pdev->dev, base);
+		}
+	}
+
+	dprintk(VIDC_DBG, "populating sub devices\n");
+	/*
+	 * Trigger probe for each sub-device i.e. qcom,msm-vidc,context-bank.
+	 * When msm_vidc_probe is called for each sub-device, parse the
+	 * context-bank details and store it in core->resources.context_banks
+	 * list.
+	 */
+	rc = of_platform_populate(pdev->dev.of_node, msm_vidc_dt_match, NULL,
+			&pdev->dev);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to trigger probe for sub-devices\n");
+		goto err_fail_sub_device_probe;
+	}
+
+	return rc;
+
+err_fail_sub_device_probe:
+	vidc_hfi_deinitialize(core->hfi_type, core->device);
+err_cores_exceeded:
+	device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev,
+			&dev_attr_link_name);
+err_enc_attr_link_name:
+	video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
+err_enc_register:
+	device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev,
+			&dev_attr_link_name);
+err_dec_attr_link_name:
+	video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+err_dec_register:
+	v4l2_device_unregister(&core->v4l2_dev);
+err_v4l2_register:
+	sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
+err_core_init:
+	dev_set_drvdata(&pdev->dev, NULL);
+	kfree(core);
+err_no_mem:
+	return rc;
+}
+
+static int msm_vidc_probe_context_bank(struct platform_device *pdev)
+{
+	return read_context_bank_resources_from_dt(pdev);
+}
+
+static int msm_vidc_probe_bus(struct platform_device *pdev)
+{
+	return read_bus_resources_from_dt(pdev);
+}
+
+static int msm_vidc_probe(struct platform_device *pdev)
+{
+	/*
+	 * Sub devices probe will be triggered by of_platform_populate() towards
+	 * the end of the probe function after msm-vidc device probe is
+	 * completed. Return immediately after completing sub-device probe.
+	 */
+	if (of_device_is_compatible(pdev->dev.of_node, "qcom,msm-vidc")) {
+		return msm_vidc_probe_vidc_device(pdev);
+	} else if (of_device_is_compatible(pdev->dev.of_node,
+		"qcom,msm-vidc,bus")) {
+		return msm_vidc_probe_bus(pdev);
+	} else if (of_device_is_compatible(pdev->dev.of_node,
+		"qcom,msm-vidc,context-bank")) {
+		return msm_vidc_probe_context_bank(pdev);
+	}
+
+	/* How did we end up here? */
+	MSM_VIDC_ERROR(1);
+	return -EINVAL;
+}
+
+static int msm_vidc_remove(struct platform_device *pdev)
+{
+	int rc = 0;
+	struct msm_vidc_core *core;
+
+	if (!pdev) {
+		dprintk(VIDC_ERR, "%s invalid input %pK", __func__, pdev);
+		return -EINVAL;
+	}
+
+	core = dev_get_drvdata(&pdev->dev);
+	if (!core) {
+		dprintk(VIDC_ERR, "%s invalid core", __func__);
+		return -EINVAL;
+	}
+
+	if (core->resources.use_non_secure_pil)
+		venus_boot_deinit();
+
+	vidc_hfi_deinitialize(core->hfi_type, core->device);
+	device_remove_file(&core->vdev[MSM_VIDC_ENCODER].vdev.dev,
+				&dev_attr_link_name);
+	video_unregister_device(&core->vdev[MSM_VIDC_ENCODER].vdev);
+	device_remove_file(&core->vdev[MSM_VIDC_DECODER].vdev.dev,
+				&dev_attr_link_name);
+	video_unregister_device(&core->vdev[MSM_VIDC_DECODER].vdev);
+	v4l2_device_unregister(&core->v4l2_dev);
+
+	msm_vidc_free_platform_resources(&core->resources);
+	sysfs_remove_group(&pdev->dev.kobj, &msm_vidc_core_attr_group);
+	dev_set_drvdata(&pdev->dev, NULL);
+	mutex_destroy(&core->lock);
+	kfree(core);
+	return rc;
+}
+
+static int msm_vidc_pm_suspend(struct device *dev)
+{
+	int rc = 0;
+	struct msm_vidc_core *core;
+
+	/*
+	 * Bail out if
+	 * - driver possibly not probed yet
+	 * - not the main device. We don't support power management on
+	 *   subdevices (e.g. context banks)
+	 */
+	if (!dev || !dev->driver ||
+		!of_device_is_compatible(dev->of_node, "qcom,msm-vidc"))
+		return 0;
+
+	core = dev_get_drvdata(dev);
+	if (!core) {
+		dprintk(VIDC_ERR, "%s invalid core\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_vidc_suspend(core->id);
+	if (rc == -ENOTSUPP)
+		rc = 0;
+	else if (rc)
+		dprintk(VIDC_WARN, "Failed to suspend: %d\n", rc);
+
+
+	return rc;
+}
+
+static int msm_vidc_pm_resume(struct device *dev)
+{
+	dprintk(VIDC_INFO, "%s\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops msm_vidc_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(msm_vidc_pm_suspend, msm_vidc_pm_resume)
+};
+
+MODULE_DEVICE_TABLE(of, msm_vidc_dt_match);
+
+static struct platform_driver msm_vidc_driver = {
+	.probe = msm_vidc_probe,
+	.remove = msm_vidc_remove,
+	.driver = {
+		.name = "msm_vidc_v4l2",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_vidc_dt_match,
+		.pm = &msm_vidc_pm_ops,
+	},
+};
+
+static int __init msm_vidc_init(void)
+{
+	int rc = 0;
+
+	vidc_driver = kzalloc(sizeof(*vidc_driver),
+						GFP_KERNEL);
+	if (!vidc_driver) {
+		dprintk(VIDC_ERR,
+			"Failed to allocate memroy for msm_vidc_drv\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&vidc_driver->cores);
+	mutex_init(&vidc_driver->lock);
+	vidc_driver->debugfs_root = msm_vidc_debugfs_init_drv();
+	if (!vidc_driver->debugfs_root)
+		dprintk(VIDC_ERR,
+			"Failed to create debugfs for msm_vidc\n");
+
+	rc = platform_driver_register(&msm_vidc_driver);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to register platform driver\n");
+		kfree(vidc_driver);
+		vidc_driver = NULL;
+	}
+
+	return rc;
+}
+
+static void __exit msm_vidc_exit(void)
+{
+	platform_driver_unregister(&msm_vidc_driver);
+	debugfs_remove_recursive(vidc_driver->debugfs_root);
+	mutex_destroy(&vidc_driver->lock);
+	kfree(vidc_driver);
+	vidc_driver = NULL;
+}
+
+module_init(msm_vidc_init);
+module_exit(msm_vidc_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.c b/drivers/media/platform/msm/vidc/msm_vdec.c
new file mode 100644
index 0000000..dedd15f
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vdec.c
@@ -0,0 +1,2370 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/slab.h>
+#include <soc/qcom/scm.h>
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "vidc_hfi_api.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_clocks.h"
+
+#define MSM_VDEC_DVC_NAME "msm_vdec_8974"
+#define MIN_NUM_OUTPUT_BUFFERS 4
+#define MIN_NUM_CAPTURE_BUFFERS 6
+#define MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS 1
+#define MAX_NUM_OUTPUT_BUFFERS VB2_MAX_FRAME
+#define DEFAULT_VIDEO_CONCEAL_COLOR_BLACK 0x8010
+#define MB_SIZE_IN_PIXEL (16 * 16)
+#define MAX_OPERATING_FRAME_RATE (300 << 16)
+#define OPERATING_FRAME_RATE_STEP (1 << 16)
+
+static const char *const mpeg_video_vidc_divx_format[] = {
+	"DIVX Format 3",
+	"DIVX Format 4",
+	"DIVX Format 5",
+	"DIVX Format 6",
+	NULL
+};
+static const char *const mpeg_video_stream_format[] = {
+	"NAL Format Start Codes",
+	"NAL Format One NAL Per Buffer",
+	"NAL Format One Byte Length",
+	"NAL Format Two Byte Length",
+	"NAL Format Four Byte Length",
+	NULL
+};
+static const char *const mpeg_video_output_order[] = {
+	"Display Order",
+	"Decode Order",
+	NULL
+};
+static const char *const mpeg_vidc_video_alloc_mode_type[] = {
+	"Buffer Allocation Static",
+	"Buffer Allocation Ring Buffer",
+	"Buffer Allocation Dynamic Buffer"
+};
+
+static const char *const perf_level[] = {
+	"Nominal",
+	"Performance",
+	"Turbo"
+};
+
+static const char *const h263_level[] = {
+	"1.0",
+	"2.0",
+	"3.0",
+	"4.0",
+	"4.5",
+	"5.0",
+	"6.0",
+	"7.0",
+};
+
+static const char *const h263_profile[] = {
+	"Baseline",
+	"H320 Coding",
+	"Backward Compatible",
+	"ISWV2",
+	"ISWV3",
+	"High Compression",
+	"Internet",
+	"Interlace",
+	"High Latency",
+};
+
+static const char *const vp8_profile_level[] = {
+	"Unused",
+	"0.0",
+	"1.0",
+	"2.0",
+	"3.0",
+};
+
+static const char *const mpeg2_profile[] = {
+	"Simple",
+	"Main",
+	"422",
+	"Snr Scalable",
+	"Spatial Scalable",
+	"High",
+};
+
+static const char *const mpeg2_level[] = {
+	"0",
+	"1",
+	"2",
+	"3",
+};
+static const char *const mpeg_vidc_video_entropy_mode[] = {
+	"CAVLC Entropy Mode",
+	"CABAC Entropy Mode",
+};
+
+static const char *const mpeg_vidc_video_h264_mvc_layout[] = {
+	"Frame packing arrangement sequential",
+	"Frame packing arrangement top-bottom",
+};
+
+static const char *const mpeg_vidc_video_dpb_color_format[] = {
+	"DPB Color Format None",
+	"DPB Color Format UBWC",
+	"DPB Color Format UBWC TP10",
+};
+
+static struct msm_vidc_ctrl msm_vdec_ctrls[] = {
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT,
+		.name = "NAL Format",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_NAL_PER_BUFFER) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_BYTE_LENGTH) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_TWO_BYTE_LENGTH) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH)
+		),
+		.qmenu = mpeg_video_stream_format,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER,
+		.name = "Output Order",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE)
+			),
+		.qmenu = mpeg_video_output_order,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE,
+		.name = "Picture Type Decoding",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE,
+		.name = "Sync Frame Decode",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE,
+		.name = "Secure mode",
+		.type = V4L2_CTRL_TYPE_BUTTON,
+		.minimum = 0,
+		.maximum = 0,
+		.default_value = 0,
+		.step = 0,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA,
+		.name = "Extradata Type",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
+		.maximum = V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE,
+		.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI) |
+			(1 <<
+			V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE)
+			),
+		.qmenu = mpeg_video_vidc_extradata,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE,
+		.name = "Video decoder multi stream",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum =
+			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY,
+		.maximum =
+			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY,
+		.default_value =
+			V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY,
+		.menu_skip_mask = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+		.name = "H264 Profile",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+		.default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.menu_skip_mask = 0,
+		.flags = V4L2_CTRL_FLAG_VOLATILE,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+		.name = "H264 Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.maximum = V4L2_MPEG_VIDEO_H264_LEVEL_5_2,
+		.default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.menu_skip_mask = 0,
+		.flags = V4L2_CTRL_FLAG_VOLATILE,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
+		.name = "VP8 Profile Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1)
+		),
+		.qmenu = vp8_profile_level,
+		.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE,
+		.name = "MPEG2 Profile",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_422) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SNR_SCALABLE) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SPATIAL_SCALABLE) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH)
+		),
+		.qmenu = mpeg2_profile,
+		.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL,
+		.name = "MPEG2 Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3)
+		),
+		.qmenu = mpeg2_level,
+		.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR,
+		.name = "Picture concealed color",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0x0,
+		.maximum = 0xffffff,
+		.default_value = DEFAULT_VIDEO_CONCEAL_COLOR_BLACK,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT,
+		.name = "Buffer size limit",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = INT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT,
+		.name = "Video decoder dpb color format",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC) |
+			(1 << V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC)
+			),
+		.qmenu = mpeg_vidc_video_dpb_color_format,
+	},
+	{
+		.id = V4L2_CID_VIDC_QBUF_MODE,
+		.name = "Allows batching of buffers for power savings",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_VIDC_QBUF_STANDARD,
+		.maximum = V4L2_VIDC_QBUF_BATCHED,
+		.default_value = V4L2_VIDC_QBUF_STANDARD,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+		.name = "Entropy Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+		.default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
+		(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+		),
+		.qmenu = mpeg_vidc_video_entropy_mode,
+		.flags = V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY,
+		.name = "Session Priority",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE,
+		.name = "Set Decoder Operating rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_OPERATING_FRAME_RATE,
+		.default_value = 0,
+		.step = OPERATING_FRAME_RATE_STEP,
+	},
+};
+
+#define NUM_CTRLS ARRAY_SIZE(msm_vdec_ctrls)
+
+static int vdec_hal_to_v4l2(int id, int value);
+
+static u32 get_frame_size_nv12(int plane,
+					u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height);
+}
+
+static u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_UBWC, width, height);
+}
+
+static u32 get_frame_size_compressed_full_yuv(int plane,
+					u32 max_mbs_per_frame, u32 size_per_mb)
+{
+	return (max_mbs_per_frame * size_per_mb * 3 / 2);
+}
+
+static u32 get_frame_size_compressed(int plane,
+					u32 max_mbs_per_frame, u32 size_per_mb)
+{
+	return (max_mbs_per_frame * size_per_mb * 3/2)/2;
+}
+
+static u32 get_frame_size_nv12_ubwc_10bit(int plane, u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_BPP10_UBWC, width, height);
+}
+
+static u32 get_frame_size(struct msm_vidc_inst *inst,
+					const struct msm_vidc_format *fmt,
+					int fmt_type, int plane)
+{
+	u32 frame_size = 0;
+
+	if (fmt_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		frame_size = fmt->get_frame_size(plane,
+					inst->capability.mbs_per_frame.max,
+					MB_SIZE_IN_PIXEL);
+		if (inst->buffer_size_limit &&
+			(inst->buffer_size_limit < frame_size)) {
+			frame_size = inst->buffer_size_limit;
+			dprintk(VIDC_DBG, "input buffer size limited to %d\n",
+				frame_size);
+		} else {
+			dprintk(VIDC_DBG, "set input buffer size to %d\n",
+				frame_size);
+		}
+	} else if (fmt_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		frame_size = fmt->get_frame_size(plane,
+					inst->capability.height.max,
+					inst->capability.width.max);
+		dprintk(VIDC_DBG, "set output buffer size to %d\n",
+			frame_size);
+	} else {
+		dprintk(VIDC_WARN, "Wrong format type\n");
+	}
+	return frame_size;
+}
+
+static int is_ctrl_valid_for_codec(struct msm_vidc_inst *inst,
+					struct v4l2_ctrl *ctrl)
+{
+	int rc = 0;
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264_MVC &&
+			ctrl->val != V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH) {
+			dprintk(VIDC_ERR,
+					"Profile %#x not supported for MVC\n",
+					ctrl->val);
+			rc = -ENOTSUPP;
+			break;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		if (inst->fmts[OUTPUT_PORT].fourcc == V4L2_PIX_FMT_H264_MVC &&
+			ctrl->val >= V4L2_MPEG_VIDEO_H264_LEVEL_5_2) {
+			dprintk(VIDC_ERR, "Level %#x not supported for MVC\n",
+					ctrl->val);
+			rc = -ENOTSUPP;
+			break;
+		}
+		break;
+	default:
+		break;
+	}
+	return rc;
+}
+
+struct msm_vidc_format vdec_formats[] = {
+	{
+		.name = "YCbCr Semiplanar 4:2:0",
+		.description = "Y/CbCr 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.num_planes = 2,
+		.get_frame_size = get_frame_size_nv12,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "UBWC YCbCr Semiplanar 4:2:0",
+		.description = "UBWC Y/CbCr 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV12_UBWC,
+		.num_planes = 2,
+		.get_frame_size = get_frame_size_nv12_ubwc,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "UBWC YCbCr Semiplanar 4:2:0 10bit",
+		.description = "UBWC Y/CbCr 4:2:0 10bit",
+		.fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC,
+		.num_planes = 2,
+		.get_frame_size = get_frame_size_nv12_ubwc_10bit,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "Mpeg4",
+		.description = "Mpeg4 compressed format",
+		.fourcc = V4L2_PIX_FMT_MPEG4,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "Mpeg2",
+		.description = "Mpeg2 compressed format",
+		.fourcc = V4L2_PIX_FMT_MPEG2,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "H263",
+		.description = "H263 compressed format",
+		.fourcc = V4L2_PIX_FMT_H263,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "VC1",
+		.description = "VC-1 compressed format",
+		.fourcc = V4L2_PIX_FMT_VC1_ANNEX_G,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "VC1 SP",
+		.description = "VC-1 compressed format G",
+		.fourcc = V4L2_PIX_FMT_VC1_ANNEX_L,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "H264",
+		.description = "H264 compressed format",
+		.fourcc = V4L2_PIX_FMT_H264,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "H264_MVC",
+		.description = "H264_MVC compressed format",
+		.fourcc = V4L2_PIX_FMT_H264_MVC,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "HEVC",
+		.description = "HEVC compressed format",
+		.fourcc = V4L2_PIX_FMT_HEVC,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "VP8",
+		.description = "VP8 compressed format",
+		.fourcc = V4L2_PIX_FMT_VP8,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "VP9",
+		.description = "VP9 compressed format",
+		.fourcc = V4L2_PIX_FMT_VP9,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed_full_yuv,
+		.type = OUTPUT_PORT,
+	},
+};
+
+int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct buf_queue *q;
+
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG, "Calling streamon\n");
+	mutex_lock(&q->lock);
+	rc = vb2_streamon(&q->vb2_bufq, i);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_ERR, "streamon failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct buf_queue *q;
+
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG, "Calling streamoff\n");
+	mutex_lock(&q->lock);
+	rc = vb2_streamoff(&q->vb2_bufq, i);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_ERR, "streamoff failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_vdec_prepare_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	struct vidc_buffer_addr_info buffer_info;
+	int extra_idx = 0;
+	int i;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			inst->core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+			"Core %pK in bad state, ignoring prepare buf\n",
+				inst->core);
+		goto exit;
+	}
+
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (b->length != inst->fmts[CAPTURE_PORT].num_planes) {
+			dprintk(VIDC_ERR,
+			"Planes mismatch: needed: %d, allocated: %d\n",
+			inst->fmts[CAPTURE_PORT].num_planes,
+			b->length);
+			rc = -EINVAL;
+			break;
+		}
+		for (i = 0; i < min_t(int, b->length, VIDEO_MAX_PLANES); ++i) {
+			dprintk(VIDC_DBG,
+			"prepare plane: %d, device_addr = %#lx, size = %d\n",
+			i, b->m.planes[i].m.userptr,
+			b->m.planes[i].length);
+		}
+
+		buffer_info.buffer_size = b->m.planes[0].length;
+		buffer_info.buffer_type = msm_comm_get_hal_output_buffer(inst);
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr = b->m.planes[0].m.userptr;
+
+		extra_idx = EXTRADATA_IDX(b->length);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES &&
+			b->m.planes[extra_idx].m.userptr) {
+			buffer_info.extradata_addr =
+				b->m.planes[extra_idx].m.userptr;
+			buffer_info.extradata_size =
+				b->m.planes[extra_idx].length;
+			dprintk(VIDC_DBG, "extradata: %pa, length = %d\n",
+				&buffer_info.extradata_addr,
+				buffer_info.extradata_size);
+		} else {
+			buffer_info.extradata_addr = 0;
+			buffer_info.extradata_size = 0;
+		}
+
+		rc = call_hfi_op(hdev, session_set_buffers,
+				(void *)inst->session, &buffer_info);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"vidc_hal_session_set_buffers failed\n");
+		}
+		break;
+	default:
+		dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+exit:
+	return rc;
+}
+
+int msm_vdec_release_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	struct vidc_buffer_addr_info buffer_info;
+	struct msm_vidc_core *core;
+	int extra_idx = 0;
+	int i;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	core = inst->core;
+	hdev = inst->core->device;
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+			"Core %pK in bad state, ignoring release output buf\n",
+				core);
+		goto exit;
+	}
+
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (b->length != inst->fmts[CAPTURE_PORT].num_planes) {
+			dprintk(VIDC_ERR,
+			"Planes mismatch: needed: %d, to release: %d\n",
+			inst->fmts[CAPTURE_PORT].num_planes, b->length);
+			rc = -EINVAL;
+			break;
+		}
+
+		for (i = 0; i < b->length; ++i) {
+			dprintk(VIDC_DBG,
+			"Release plane: %d device_addr = %#lx, size = %d\n",
+			i, b->m.planes[i].m.userptr,
+			b->m.planes[i].length);
+		}
+
+		buffer_info.buffer_size = b->m.planes[0].length;
+		buffer_info.buffer_type = msm_comm_get_hal_output_buffer(inst);
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr = b->m.planes[0].m.userptr;
+		buffer_info.response_required = false;
+
+		extra_idx = EXTRADATA_IDX(b->length);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES
+			&& b->m.planes[extra_idx].m.userptr)
+			buffer_info.extradata_addr =
+				b->m.planes[extra_idx].m.userptr;
+		else
+			buffer_info.extradata_addr = 0;
+
+		rc = call_hfi_op(hdev, session_release_buffers,
+			(void *)inst->session, &buffer_info);
+		if (rc)
+			dprintk(VIDC_ERR,
+			"vidc_hal_session_release_buffers failed\n");
+		break;
+	default:
+		dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+exit:
+	return rc;
+}
+
+int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct buf_queue *q = NULL;
+	int rc = 0;
+
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		dprintk(VIDC_ERR, "Failed to find buffer queue for type = %d\n"
+			, b->type);
+		return -EINVAL;
+	}
+
+	mutex_lock(&q->lock);
+	rc = vb2_qbuf(&q->vb2_bufq, b);
+	mutex_unlock(&q->lock);
+
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to qbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_vdec_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct buf_queue *q = NULL;
+	int rc = 0;
+
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		dprintk(VIDC_ERR, "Failed to find buffer queue for type = %d\n"
+			, b->type);
+		return -EINVAL;
+	}
+	mutex_lock(&q->lock);
+	rc = vb2_dqbuf(&q->vb2_bufq, b, true);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_vdec_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b)
+{
+	struct buf_queue *q = NULL;
+	int rc = 0;
+
+	if (!inst || !b) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, buffer = %pK\n", inst, b);
+		return -EINVAL;
+	}
+
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		dprintk(VIDC_ERR, "Failed to find buffer queue for type = %d\n"
+			, b->type);
+		return -EINVAL;
+	}
+
+	mutex_lock(&q->lock);
+	rc = vb2_reqbufs(&q->vb2_bufq, b);
+	mutex_unlock(&q->lock);
+
+	if (rc)
+		dprintk(VIDC_DBG, "Failed to get reqbufs, %d\n", rc);
+	return rc;
+}
+
+int msm_vdec_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	struct hfi_device *hdev;
+	int rc = 0, i = 0, stride = 0, scanlines = 0, color_format = 0;
+	unsigned int *plane_sizes = NULL, extra_idx = 0;
+
+	if (!inst || !f || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, format = %pK\n", inst, f);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		fmt = &inst->fmts[CAPTURE_PORT];
+	else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		fmt = &inst->fmts[OUTPUT_PORT];
+	else
+		return -ENOTSUPP;
+
+	f->fmt.pix_mp.pixelformat = fmt->fourcc;
+	f->fmt.pix_mp.num_planes = fmt->num_planes;
+	if (inst->in_reconfig) {
+		inst->prop.height[OUTPUT_PORT] = inst->reconfig_height;
+		inst->prop.width[OUTPUT_PORT] = inst->reconfig_width;
+
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"%s: unsupported session\n", __func__);
+			goto exit;
+		}
+	}
+
+	f->fmt.pix_mp.height = inst->prop.height[CAPTURE_PORT];
+	f->fmt.pix_mp.width = inst->prop.width[CAPTURE_PORT];
+	stride = inst->prop.width[CAPTURE_PORT];
+	scanlines = inst->prop.height[CAPTURE_PORT];
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		plane_sizes = &inst->bufq[OUTPUT_PORT].plane_sizes[0];
+		for (i = 0; i < fmt->num_planes; ++i) {
+			if (!plane_sizes[i]) {
+				f->fmt.pix_mp.plane_fmt[i].sizeimage =
+					get_frame_size(inst, fmt, f->type, i);
+				plane_sizes[i] = f->fmt.pix_mp.plane_fmt[i].
+					sizeimage;
+			} else
+				f->fmt.pix_mp.plane_fmt[i].sizeimage =
+					plane_sizes[i];
+		}
+		f->fmt.pix_mp.height = inst->prop.height[OUTPUT_PORT];
+		f->fmt.pix_mp.width = inst->prop.width[OUTPUT_PORT];
+		f->fmt.pix_mp.plane_fmt[0].bytesperline =
+			(__u16)inst->prop.width[OUTPUT_PORT];
+		f->fmt.pix_mp.plane_fmt[0].reserved[0] =
+			(__u16)inst->prop.height[OUTPUT_PORT];
+	} else {
+		switch (fmt->fourcc) {
+		case V4L2_PIX_FMT_NV12:
+			color_format = COLOR_FMT_NV12;
+			break;
+		case V4L2_PIX_FMT_NV12_UBWC:
+			color_format = COLOR_FMT_NV12_UBWC;
+			break;
+		case V4L2_PIX_FMT_NV12_TP10_UBWC:
+			color_format = COLOR_FMT_NV12_BPP10_UBWC;
+			break;
+		default:
+			dprintk(VIDC_WARN, "Color format not recognized\n");
+			rc = -ENOTSUPP;
+			goto exit;
+		}
+
+		stride = VENUS_Y_STRIDE(color_format,
+				inst->prop.width[CAPTURE_PORT]);
+		scanlines = VENUS_Y_SCANLINES(color_format,
+				inst->prop.height[CAPTURE_PORT]);
+
+		f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			fmt->get_frame_size(0,
+			inst->prop.height[CAPTURE_PORT],
+			inst->prop.width[CAPTURE_PORT]);
+
+		extra_idx = EXTRADATA_IDX(fmt->num_planes);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+			f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+				VENUS_EXTRADATA_SIZE(
+					inst->prop.height[CAPTURE_PORT],
+					inst->prop.width[CAPTURE_PORT]);
+		}
+
+		for (i = 0; i < fmt->num_planes; ++i)
+			inst->bufq[CAPTURE_PORT].plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
+
+		f->fmt.pix_mp.height = inst->prop.height[CAPTURE_PORT];
+		f->fmt.pix_mp.width = inst->prop.width[CAPTURE_PORT];
+		f->fmt.pix_mp.plane_fmt[0].bytesperline =
+			(__u16)stride;
+		f->fmt.pix_mp.plane_fmt[0].reserved[0] =
+			(__u16)scanlines;
+	}
+
+exit:
+	return rc;
+}
+
+int msm_vdec_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	struct msm_vidc_format *fmt = NULL;
+	struct hal_frame_size frame_sz;
+	unsigned int extra_idx = 0;
+	int rc = 0;
+	int ret = 0;
+	int i;
+	int max_input_size = 0;
+
+	if (!inst || !f) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->fmt.pix_mp.pixelformat,
+			CAPTURE_PORT);
+		if (!fmt || fmt->type != CAPTURE_PORT) {
+			dprintk(VIDC_ERR,
+				"Format: %d not supported on CAPTURE port\n",
+				f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto err_invalid_fmt;
+		}
+		memcpy(&inst->fmts[fmt->type], fmt,
+				sizeof(struct msm_vidc_format));
+
+		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
+		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		msm_comm_set_color_format(inst,
+				msm_comm_get_hal_output_buffer(inst),
+				f->fmt.pix_mp.pixelformat);
+
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY) {
+			frame_sz.buffer_type = HAL_BUFFER_OUTPUT2;
+			frame_sz.width = inst->prop.width[CAPTURE_PORT];
+			frame_sz.height = inst->prop.height[CAPTURE_PORT];
+			dprintk(VIDC_DBG,
+				"buffer type = %d width = %d, height = %d\n",
+				frame_sz.buffer_type, frame_sz.width,
+				frame_sz.height);
+			ret = msm_comm_try_set_prop(inst,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		}
+
+		f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			inst->fmts[fmt->type].get_frame_size(0,
+			f->fmt.pix_mp.height, f->fmt.pix_mp.width);
+
+		extra_idx = EXTRADATA_IDX(inst->fmts[fmt->type].num_planes);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+			f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+				VENUS_EXTRADATA_SIZE(
+					inst->prop.height[CAPTURE_PORT],
+					inst->prop.width[CAPTURE_PORT]);
+		}
+
+		f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes;
+		for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) {
+			inst->bufq[CAPTURE_PORT].plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
+		}
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
+		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+
+		fmt = msm_comm_get_pixel_fmt_fourcc(vdec_formats,
+				ARRAY_SIZE(vdec_formats),
+				f->fmt.pix_mp.pixelformat,
+				OUTPUT_PORT);
+		if (!fmt || fmt->type != OUTPUT_PORT) {
+			dprintk(VIDC_ERR,
+			"Format: %d not supported on OUTPUT port\n",
+			f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto err_invalid_fmt;
+		}
+		memcpy(&inst->fmts[fmt->type], fmt,
+				sizeof(struct msm_vidc_format));
+
+		rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to initialize instance\n");
+			goto err_invalid_fmt;
+		}
+
+		if (!(get_hal_codec(inst->fmts[fmt->type].fourcc) &
+			inst->core->dec_codec_supported)) {
+			dprintk(VIDC_ERR,
+				"Codec(%#x) is not present in the supported codecs list(%#x)\n",
+				get_hal_codec(inst->fmts[fmt->type].fourcc),
+				inst->core->dec_codec_supported);
+			rc = -EINVAL;
+			goto err_invalid_fmt;
+		}
+
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to open instance\n");
+			goto err_invalid_fmt;
+		}
+
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto err_invalid_fmt;
+		}
+
+		frame_sz.buffer_type = HAL_BUFFER_INPUT;
+		frame_sz.width = inst->prop.width[OUTPUT_PORT];
+		frame_sz.height = inst->prop.height[OUTPUT_PORT];
+		dprintk(VIDC_DBG,
+			"buffer type = %d width = %d, height = %d\n",
+			frame_sz.buffer_type, frame_sz.width,
+			frame_sz.height);
+		msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
+
+		max_input_size = get_frame_size(
+inst, &inst->fmts[fmt->type], f->type, 0);
+		if (f->fmt.pix_mp.plane_fmt[0].sizeimage > max_input_size ||
+			!f->fmt.pix_mp.plane_fmt[0].sizeimage) {
+			f->fmt.pix_mp.plane_fmt[0].sizeimage = max_input_size;
+		}
+
+		f->fmt.pix_mp.num_planes = inst->fmts[fmt->type].num_planes;
+		for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) {
+			inst->bufq[OUTPUT_PORT].plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
+		}
+
+	}
+err_invalid_fmt:
+	return rc;
+}
+
+int msm_vdec_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap)
+{
+	if (!inst || !cap) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, cap = %pK\n", inst, cap);
+		return -EINVAL;
+	}
+	strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, MSM_VDEC_DVC_NAME, sizeof(cap->card));
+	cap->bus_info[0] = 0;
+	cap->version = MSM_VIDC_VERSION;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+						V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+						V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	memset(cap->reserved, 0, sizeof(cap->reserved));
+	return 0;
+}
+
+int msm_vdec_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+
+	if (!inst || !f) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, f = %pK\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->index, CAPTURE_PORT);
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(vdec_formats,
+			ARRAY_SIZE(vdec_formats), f->index, OUTPUT_PORT);
+		f->flags = V4L2_FMT_FLAG_COMPRESSED;
+	}
+
+	memset(f->reserved, 0, sizeof(f->reserved));
+	if (fmt) {
+		strlcpy(f->description, fmt->description,
+				sizeof(f->description));
+		f->pixelformat = fmt->fourcc;
+	} else {
+		dprintk(VIDC_DBG, "No more formats found\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int set_actual_buffer_count(struct msm_vidc_inst *inst,
+			int count, enum hal_buffer type)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	struct hal_buffer_count_actual buf_count;
+
+	hdev = inst->core->device;
+
+	buf_count.buffer_type = type;
+	buf_count.buffer_count_actual = count;
+	rc = call_hfi_op(hdev, session_set_property,
+		inst->session, HAL_PARAM_BUFFER_COUNT_ACTUAL, &buf_count);
+	if (rc)
+		dprintk(VIDC_ERR,
+			"Failed to set actual buffer count %d for buffer type %d\n",
+			count, type);
+	return rc;
+}
+
+static int msm_vdec_queue_setup(
+	struct vb2_queue *q,
+	unsigned int *num_buffers, unsigned int *num_planes,
+	unsigned int sizes[], struct device *alloc_devs[])
+{
+	int i, rc = 0;
+	struct msm_vidc_inst *inst;
+	struct hal_buffer_requirements *bufreq;
+	int extra_idx = 0;
+	int min_buff_count = 0;
+
+	if (!q || !num_buffers || !num_planes
+		|| !sizes || !q->drv_priv) {
+		dprintk(VIDC_ERR, "Invalid input, q = %pK, %pK, %pK\n",
+			q, num_buffers, num_planes);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_comm_try_get_bufreqs(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"%s: Failed : Buffer requirements\n", __func__);
+		goto exit;
+	}
+
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		*num_planes = inst->fmts[OUTPUT_PORT].num_planes;
+		if (*num_buffers < MIN_NUM_OUTPUT_BUFFERS ||
+				*num_buffers > MAX_NUM_OUTPUT_BUFFERS)
+			*num_buffers = MIN_NUM_OUTPUT_BUFFERS;
+		for (i = 0; i < *num_planes; i++) {
+			sizes[i] = get_frame_size(inst,
+					&inst->fmts[OUTPUT_PORT], q->type, i);
+		}
+		rc = set_actual_buffer_count(inst, *num_buffers,
+			HAL_BUFFER_INPUT);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		dprintk(VIDC_DBG, "Getting bufreqs on capture plane\n");
+		*num_planes = inst->fmts[CAPTURE_PORT].num_planes;
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to open instance\n");
+			break;
+		}
+		rc = msm_comm_try_get_bufreqs(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to get buffer requirements: %d\n", rc);
+			break;
+		}
+
+		bufreq = get_buff_req_buffer(inst,
+			msm_comm_get_hal_output_buffer(inst));
+		if (!bufreq) {
+			dprintk(VIDC_ERR,
+				"No buffer requirement for buffer type %x\n",
+				HAL_BUFFER_OUTPUT);
+			rc = -EINVAL;
+			break;
+		}
+
+		/* Pretend as if FW itself is asking for
+		 * additional buffers.
+		 * *num_buffers += MSM_VIDC_ADDITIONAL_BUFS_FOR_DCVS
+		 * is wrong since it will end up increasing the count
+		 * on every call to reqbufs if *num_bufs is larger
+		 * than min requirement.
+		 */
+		*num_buffers = max(*num_buffers, bufreq->buffer_count_min
+			+ msm_dcvs_get_extra_buff_count(inst));
+
+		min_buff_count = (!!(inst->flags & VIDC_THUMBNAIL)) ?
+			MIN_NUM_THUMBNAIL_MODE_CAPTURE_BUFFERS :
+				MIN_NUM_CAPTURE_BUFFERS;
+
+		*num_buffers = clamp_val(*num_buffers,
+			min_buff_count, VB2_MAX_FRAME);
+
+		dprintk(VIDC_DBG, "Set actual output buffer count: %d\n",
+				*num_buffers);
+		rc = set_actual_buffer_count(inst, *num_buffers,
+					msm_comm_get_hal_output_buffer(inst));
+		if (rc)
+			break;
+
+		if (*num_buffers != bufreq->buffer_count_actual) {
+			rc = msm_comm_try_get_bufreqs(inst);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Failed to get buf req, %d\n", rc);
+				break;
+			}
+		}
+		dprintk(VIDC_DBG, "count =  %d, size = %d, alignment = %d\n",
+				inst->buff_req.buffer[1].buffer_count_actual,
+				inst->buff_req.buffer[1].buffer_size,
+				inst->buff_req.buffer[1].buffer_alignment);
+		sizes[0] = inst->bufq[CAPTURE_PORT].plane_sizes[0];
+
+		/*
+		 * Set actual buffer count to firmware for DPB buffers.
+		 * Firmware mandates setting of minimum buffer size
+		 * and actual buffer count for both OUTPUT and OUTPUT2.
+		 * Hence we are setting back the same buffer size
+		 * information back to firmware.
+		 */
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY) {
+			bufreq = get_buff_req_buffer(inst,
+					HAL_BUFFER_OUTPUT);
+			if (!bufreq) {
+				rc = -EINVAL;
+				break;
+			}
+
+			rc = set_actual_buffer_count(inst,
+				bufreq->buffer_count_actual,
+				HAL_BUFFER_OUTPUT);
+			if (rc)
+				break;
+		}
+
+		extra_idx =
+			EXTRADATA_IDX(inst->fmts[CAPTURE_PORT].num_planes);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+			sizes[extra_idx] =
+				VENUS_EXTRADATA_SIZE(
+					inst->prop.height[CAPTURE_PORT],
+					inst->prop.width[CAPTURE_PORT]);
+		}
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+exit:
+	return rc;
+}
+
+static inline int set_max_internal_buffers_size(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct {
+		enum hal_buffer type;
+		struct hal_buffer_requirements *req;
+		size_t size;
+	} internal_buffers[] = {
+		{ HAL_BUFFER_INTERNAL_SCRATCH, NULL, 0},
+		{ HAL_BUFFER_INTERNAL_SCRATCH_1, NULL, 0},
+		{ HAL_BUFFER_INTERNAL_SCRATCH_2, NULL, 0},
+		{ HAL_BUFFER_INTERNAL_PERSIST, NULL, 0},
+		{ HAL_BUFFER_INTERNAL_PERSIST_1, NULL, 0},
+	};
+
+	struct hal_frame_size frame_sz;
+	int i;
+
+	frame_sz.buffer_type = HAL_BUFFER_INPUT;
+	frame_sz.width = inst->capability.width.max;
+	frame_sz.height =
+		(inst->capability.mbs_per_frame.max * 256) /
+		inst->capability.width.max;
+
+	dprintk(VIDC_DBG,
+		"Max buffer reqs, buffer type = %d width = %d, height = %d, max_mbs_per_frame = %d\n",
+		frame_sz.buffer_type, frame_sz.width,
+		frame_sz.height, inst->capability.mbs_per_frame.max);
+
+	msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
+	rc = msm_comm_try_get_bufreqs(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"%s Failed to get max buf req, %d\n", __func__, rc);
+		return 0;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(internal_buffers); i++) {
+		internal_buffers[i].req =
+			get_buff_req_buffer(inst, internal_buffers[i].type);
+		internal_buffers[i].size = internal_buffers[i].req ?
+			internal_buffers[i].req->buffer_size : 0;
+	}
+
+	frame_sz.buffer_type = HAL_BUFFER_INPUT;
+	frame_sz.width = inst->prop.width[OUTPUT_PORT];
+	frame_sz.height = inst->prop.height[OUTPUT_PORT];
+
+	msm_comm_try_set_prop(inst, HAL_PARAM_FRAME_SIZE, &frame_sz);
+	rc = msm_comm_try_get_bufreqs(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"%s Failed to get back old buf req, %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	dprintk(VIDC_DBG,
+			"Old buffer reqs, buffer type = %d width = %d, height = %d\n",
+			frame_sz.buffer_type, frame_sz.width,
+			frame_sz.height);
+
+	for (i = 0; i < ARRAY_SIZE(internal_buffers); i++) {
+		if (internal_buffers[i].req) {
+			internal_buffers[i].req->buffer_size =
+				internal_buffers[i].size;
+			dprintk(VIDC_DBG,
+				"Changing buffer type : %d size to : %zd\n",
+				internal_buffers[i].type,
+				internal_buffers[i].size);
+		}
+	}
+	return 0;
+}
+
+static inline int start_streaming(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	bool slave_side_cp = inst->core->resources.slave_side_cp;
+	struct hal_buffer_size_minimum b;
+	unsigned int buffer_size;
+	struct msm_vidc_format *fmt = NULL;
+
+	fmt = &inst->fmts[CAPTURE_PORT];
+	buffer_size = fmt->get_frame_size(0,
+		inst->prop.height[CAPTURE_PORT],
+		inst->prop.width[CAPTURE_PORT]);
+	hdev = inst->core->device;
+
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY) {
+		rc = msm_vidc_check_scaling_supported(inst);
+		b.buffer_type = HAL_BUFFER_OUTPUT2;
+	} else {
+		b.buffer_type = HAL_BUFFER_OUTPUT;
+	}
+
+	b.buffer_size = buffer_size;
+	rc = call_hfi_op(hdev, session_set_property,
+		 inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM,
+		 &b);
+	if (rc) {
+		dprintk(VIDC_ERR, "H/w scaling is not in valid range\n");
+		return -EINVAL;
+	}
+	if ((inst->flags & VIDC_SECURE) && !inst->in_reconfig &&
+		!slave_side_cp) {
+		rc = set_max_internal_buffers_size(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set max scratch buffer size: %d\n",
+				rc);
+			goto fail_start;
+		}
+	}
+	rc = msm_comm_set_scratch_buffers(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to set scratch buffers: %d\n", rc);
+		goto fail_start;
+	}
+	rc = msm_comm_set_persist_buffers(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to set persist buffers: %d\n", rc);
+		goto fail_start;
+	}
+
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY) {
+		rc = msm_comm_set_output_buffers(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set output buffers: %d\n", rc);
+			goto fail_start;
+		}
+	}
+
+	/*
+	 * For seq_changed_insufficient, driver should set session_continue
+	 * to firmware after the following sequence
+	 * - driver raises insufficient event to v4l2 client
+	 * - all output buffers have been flushed and freed
+	 * - v4l2 client queries buffer requirements and splits/combines OPB-DPB
+	 * - v4l2 client sets new set of buffers to firmware
+	 * - v4l2 client issues CONTINUE to firmware to resume decoding of
+	 *   submitted ETBs.
+	 */
+	if (inst->in_reconfig) {
+		dprintk(VIDC_DBG, "send session_continue after reconfig\n");
+		rc = call_hfi_op(hdev, session_continue,
+			(void *) inst->session);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s - failed to send session_continue\n",
+				__func__);
+			goto fail_start;
+		}
+	}
+	inst->in_reconfig = false;
+
+	msm_comm_scale_clocks_and_bus(inst);
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to start done state\n", inst);
+		goto fail_start;
+	}
+	msm_dcvs_init_load(inst);
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY) {
+		rc = msm_comm_queue_output_buffers(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to queue output buffers: %d\n", rc);
+			goto fail_start;
+		}
+	}
+
+fail_start:
+	return rc;
+}
+
+static inline int stop_streaming(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+	if (rc)
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to start done state\n", inst);
+	return rc;
+}
+
+static int msm_vdec_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!q || !q->drv_priv) {
+		dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+	dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n",
+		q->type, inst);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming)
+			rc = start_streaming(inst);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming)
+			rc = start_streaming(inst);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		goto stream_start_failed;
+	}
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Streamon failed on: %d capability for inst: %pK\n",
+			q->type, inst);
+		goto stream_start_failed;
+	}
+
+	rc = msm_comm_qbuf(inst, NULL);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to commit buffers queued before STREAM_ON to hardware: %d\n",
+				rc);
+		goto stream_start_failed;
+	}
+
+stream_start_failed:
+	return rc;
+}
+
+static void msm_vdec_stop_streaming(struct vb2_queue *q)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+
+	if (!q || !q->drv_priv) {
+		dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q);
+		return;
+	}
+
+	inst = q->drv_priv;
+	dprintk(VIDC_DBG, "Streamoff called on: %d capability\n", q->type);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (!inst->bufq[CAPTURE_PORT].vb2_bufq.streaming)
+			rc = stop_streaming(inst);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (!inst->bufq[OUTPUT_PORT].vb2_bufq.streaming)
+			rc = stop_streaming(inst);
+		break;
+	default:
+		dprintk(VIDC_ERR,
+			"Q-type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+
+	msm_comm_scale_clocks_and_bus(inst);
+
+	if (rc)
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK, cap = %d to state: %d\n",
+			inst, q->type, MSM_VIDC_RELEASE_RESOURCES_DONE);
+}
+
+static void msm_vdec_buf_queue(struct vb2_buffer *vb)
+{
+	int rc = msm_comm_qbuf(vb2_get_drv_priv(vb->vb2_queue), vb);
+
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to queue buffer: %d\n", rc);
+}
+
+static void msm_vdec_buf_cleanup(struct vb2_buffer *vb)
+{
+	int rc = 0;
+	struct buf_queue *q = NULL;
+	struct msm_vidc_inst *inst = NULL;
+
+	if (!vb) {
+		dprintk(VIDC_ERR, "%s : Invalid vb pointer %pK",
+			__func__, vb);
+		return;
+	}
+
+	inst = vb2_get_drv_priv(vb->vb2_queue);
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s : Invalid inst pointer",
+			__func__);
+		return;
+	}
+
+	q = msm_comm_get_vb2q(inst, vb->type);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"%s : Failed to find buffer queue for type = %d\n",
+				__func__, vb->type);
+		return;
+	}
+
+	if (q->vb2_bufq.streaming) {
+		dprintk(VIDC_DBG, "%d PORT is streaming\n",
+			vb->type);
+		return;
+	}
+
+	rc = msm_vidc_release_buffers(inst, vb->type);
+	if (rc)
+		dprintk(VIDC_ERR, "%s : Failed to release buffers : %d\n",
+			__func__, rc);
+}
+
+static const struct vb2_ops msm_vdec_vb2q_ops = {
+	.queue_setup = msm_vdec_queue_setup,
+	.start_streaming = msm_vdec_start_streaming,
+	.buf_queue = msm_vdec_buf_queue,
+	.buf_cleanup = msm_vdec_buf_cleanup,
+	.stop_streaming = msm_vdec_stop_streaming,
+};
+
+const struct vb2_ops *msm_vdec_get_vb2q_ops(void)
+{
+	return &msm_vdec_vb2q_ops;
+}
+
+int msm_vdec_inst_init(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid input = %pK\n", inst);
+		return -EINVAL;
+	}
+	inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT;
+	inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH;
+	inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT;
+	inst->prop.width[OUTPUT_PORT] = DEFAULT_WIDTH;
+	inst->capability.height.min = MIN_SUPPORTED_HEIGHT;
+	inst->capability.height.max = DEFAULT_HEIGHT;
+	inst->capability.width.min = MIN_SUPPORTED_WIDTH;
+	inst->capability.width.max = DEFAULT_WIDTH;
+	inst->capability.alloc_mode_in = HAL_BUFFER_MODE_STATIC;
+	inst->capability.alloc_mode_out = HAL_BUFFER_MODE_STATIC;
+	inst->capability.secure_output2_threshold.min = 0;
+	inst->capability.secure_output2_threshold.max = 0;
+	inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC;
+	inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC;
+	inst->prop.fps = DEFAULT_FPS;
+	inst->operating_rate = 0;
+	memcpy(&inst->fmts[OUTPUT_PORT], &vdec_formats[2],
+			sizeof(struct msm_vidc_format));
+	memcpy(&inst->fmts[CAPTURE_PORT], &vdec_formats[0],
+			sizeof(struct msm_vidc_format));
+	return rc;
+}
+
+static int try_get_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	union hal_get_property hprop;
+
+	if (!inst || !inst->core || !inst->core->device || !ctrl) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+	/*
+	 * HACK: unlock the control prior to querying the hardware.  Otherwise
+	 * lower level code that attempts to do g_ctrl() will end up deadlocking
+	 * us.
+	 */
+	v4l2_ctrl_unlock(ctrl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE:
+		rc = msm_comm_try_get_prop(inst,
+				HAL_PARAM_PROFILE_LEVEL_CURRENT, &hprop);
+		if (rc) {
+			dprintk(VIDC_ERR, "%s: Failed getting profile: %d",
+					__func__, rc);
+			break;
+		}
+		ctrl->val = vdec_hal_to_v4l2(ctrl->id,
+				hprop.profile_level.profile);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL:
+		rc = msm_comm_try_get_prop(inst,
+				HAL_PARAM_PROFILE_LEVEL_CURRENT, &hprop);
+		if (rc) {
+			dprintk(VIDC_ERR, "%s: Failed getting level: %d",
+					__func__, rc);
+			break;
+		}
+
+		ctrl->val = vdec_hal_to_v4l2(ctrl->id,
+				hprop.profile_level.level);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		rc = msm_comm_try_get_prop(inst,
+				HAL_CONFIG_VDEC_ENTROPY, &hprop);
+		if (rc) {
+			dprintk(VIDC_ERR, "%s: Failed getting entropy type: %d",
+					__func__, rc);
+			break;
+		}
+		switch (hprop.h264_entropy) {
+		case HAL_H264_ENTROPY_CAVLC:
+			ctrl->val = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC;
+			break;
+		case HAL_H264_ENTROPY_CABAC:
+			ctrl->val = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC;
+			break;
+		case HAL_UNUSED_ENTROPY:
+			rc = -ENOTSUPP;
+			break;
+		}
+		break;
+	default:
+		/*
+		 * Other controls aren't really volatile, shouldn't need to
+		 * modify ctrl->value
+		 */
+		break;
+	}
+	v4l2_ctrl_lock(ctrl);
+
+	return rc;
+}
+
+static int vdec_v4l2_to_hal(int id, int value)
+{
+	switch (id) {
+		/* H264 */
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+			return HAL_H264_PROFILE_BASELINE;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+			return HAL_H264_PROFILE_CONSTRAINED_BASE;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH:
+			return HAL_H264_PROFILE_CONSTRAINED_HIGH;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+			return HAL_H264_PROFILE_MAIN;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+			return HAL_H264_PROFILE_EXTENDED;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+			return HAL_H264_PROFILE_HIGH;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
+			return HAL_H264_PROFILE_HIGH10;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
+			return HAL_H264_PROFILE_HIGH422;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
+			return HAL_H264_PROFILE_HIGH444;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+			return HAL_H264_LEVEL_1;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+			return HAL_H264_LEVEL_1b;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+			return HAL_H264_LEVEL_11;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+			return HAL_H264_LEVEL_12;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+			return HAL_H264_LEVEL_13;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+			return HAL_H264_LEVEL_2;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+			return HAL_H264_LEVEL_21;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+			return HAL_H264_LEVEL_22;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+			return HAL_H264_LEVEL_3;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+			return HAL_H264_LEVEL_31;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+			return HAL_H264_LEVEL_32;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+			return HAL_H264_LEVEL_4;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+			return HAL_H264_LEVEL_41;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+			return HAL_H264_LEVEL_42;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+			return HAL_H264_LEVEL_5;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+			return HAL_H264_LEVEL_51;
+		default:
+			goto unknown_value;
+		}
+	}
+unknown_value:
+	dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value);
+	return -EINVAL;
+}
+
+static int vdec_hal_to_v4l2(int id, int value)
+{
+	switch (id) {
+		/* H264 */
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		switch (value) {
+		case HAL_H264_PROFILE_BASELINE:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE;
+		case HAL_H264_PROFILE_CONSTRAINED_BASE:
+			return
+			V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE;
+		case HAL_H264_PROFILE_MAIN:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_MAIN;
+		case HAL_H264_PROFILE_EXTENDED:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED;
+		case HAL_H264_PROFILE_HIGH:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH;
+		case HAL_H264_PROFILE_HIGH10:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10;
+		case HAL_H264_PROFILE_HIGH422:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422;
+		case HAL_H264_PROFILE_HIGH444:
+			return V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		switch (value) {
+		case HAL_H264_LEVEL_1:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_1_0;
+		case HAL_H264_LEVEL_1b:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_1B;
+		case HAL_H264_LEVEL_11:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_1_1;
+		case HAL_H264_LEVEL_12:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_1_2;
+		case HAL_H264_LEVEL_13:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_1_3;
+		case HAL_H264_LEVEL_2:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_2_0;
+		case HAL_H264_LEVEL_21:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_2_1;
+		case HAL_H264_LEVEL_22:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_2_2;
+		case HAL_H264_LEVEL_3:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_3_0;
+		case HAL_H264_LEVEL_31:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_3_1;
+		case HAL_H264_LEVEL_32:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_3_2;
+		case HAL_H264_LEVEL_4:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_4_0;
+		case HAL_H264_LEVEL_41:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_4_1;
+		case HAL_H264_LEVEL_42:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_4_2;
+		case HAL_H264_LEVEL_5:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_5_0;
+		case HAL_H264_LEVEL_51:
+			return V4L2_MPEG_VIDEO_H264_LEVEL_5_1;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE:
+	case V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL:
+		/*
+		 * Extremely dirty hack: we haven't implemented g_ctrl of
+		 * any of these controls and have no intention of doing
+		 * so in the near future.  So just return 0 so that we
+		 * don't see the annoying "Unknown control" errors at the
+		 * bottom of this function.
+		 */
+		return 0;
+	}
+
+unknown_value:
+	dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value);
+	return -EINVAL;
+}
+
+static struct v4l2_ctrl *get_ctrl_from_cluster(int id,
+		struct v4l2_ctrl **cluster, int ncontrols)
+{
+	int c;
+
+	for (c = 0; c < ncontrols; ++c)
+		if (cluster[c]->id == id)
+			return cluster[c];
+	return NULL;
+}
+
+static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct hal_nal_stream_format_supported stream_format;
+	struct hal_enable_picture enable_picture;
+	struct hal_enable hal_property;
+	enum hal_property property_id = 0;
+	u32 property_val = 0;
+	void *pdata = NULL;
+	struct hfi_device *hdev;
+	struct hal_extradata_enable extra;
+	struct hal_buffer_alloc_mode alloc_mode;
+	struct hal_multi_stream multi_stream;
+	struct v4l2_ctrl *temp_ctrl = NULL;
+	struct hal_profile_level profile_level;
+	struct hal_frame_size frame_sz;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	rc = is_ctrl_valid_for_codec(inst, ctrl);
+	if (rc)
+		return rc;
+
+	/* Small helper macro for quickly getting a control and err checking */
+#define TRY_GET_CTRL(__ctrl_id) ({ \
+		struct v4l2_ctrl *__temp; \
+		__temp = get_ctrl_from_cluster( \
+			__ctrl_id, \
+			ctrl->cluster, ctrl->ncontrols); \
+		if (!__temp) { \
+			dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \
+				#__ctrl_id, __ctrl_id); \
+			/* Clusters are hardcoded, if we can't find */ \
+			/* something then things are massively screwed up */ \
+			MSM_VIDC_ERROR(1); \
+		} \
+		__temp; \
+	})
+
+	v4l2_ctrl_unlock(ctrl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT:
+		property_id = HAL_PARAM_NAL_STREAM_FORMAT_SELECT;
+		stream_format.nal_stream_format_supported = BIT(ctrl->val);
+		pdata = &stream_format;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER:
+		property_id = HAL_PARAM_VDEC_OUTPUT_ORDER;
+		property_val = ctrl->val;
+		pdata = &property_val;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE:
+		property_id = HAL_PARAM_VDEC_PICTURE_TYPE_DECODE;
+		if (ctrl->val ==
+			V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON)
+			enable_picture.picture_type = HAL_PICTURE_I;
+		else
+			enable_picture.picture_type = HAL_PICTURE_I |
+				HAL_PICTURE_P | HAL_PICTURE_B |
+				HAL_PICTURE_IDR;
+		pdata = &enable_picture;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE:
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE:
+			inst->flags &= ~VIDC_THUMBNAIL;
+			break;
+		case V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE:
+			inst->flags |= VIDC_THUMBNAIL;
+			break;
+		}
+
+		property_id = HAL_PARAM_VDEC_SYNC_FRAME_DECODE;
+		hal_property.enable = ctrl->val;
+		pdata = &hal_property;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
+		inst->flags |= VIDC_SECURE;
+		dprintk(VIDC_DBG, "Setting secure mode to: %d\n",
+				!!(inst->flags & VIDC_SECURE));
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA:
+		property_id = HAL_PARAM_INDEX_EXTRADATA;
+		extra.index = msm_comm_get_hal_extradata_index(ctrl->val);
+		extra.enable = 1;
+		pdata = &extra;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE:
+		if (ctrl->val && !(inst->capability.pixelprocess_capabilities &
+				HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY)) {
+			dprintk(VIDC_ERR, "Downscaling not supported: %#x\n",
+				ctrl->id);
+			rc = -ENOTSUPP;
+			break;
+		}
+		switch (ctrl->val) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY:
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT;
+			multi_stream.enable = true;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed : Enabling OUTPUT port : %d\n",
+					rc);
+				break;
+			}
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT2;
+			multi_stream.enable = false;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"Failed:Disabling OUTPUT2 port : %d\n",
+					rc);
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY:
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT2;
+			multi_stream.enable = true;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed :Enabling OUTPUT2 port : %d\n",
+					rc);
+					break;
+			}
+			multi_stream.buffer_type = HAL_BUFFER_OUTPUT;
+			multi_stream.enable = false;
+			pdata = &multi_stream;
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_VDEC_MULTI_STREAM,
+				pdata);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed disabling OUTPUT port : %d\n",
+					rc);
+				break;
+			}
+
+			frame_sz.buffer_type = HAL_BUFFER_OUTPUT2;
+			frame_sz.width = inst->prop.width[CAPTURE_PORT];
+			frame_sz.height = inst->prop.height[CAPTURE_PORT];
+			pdata = &frame_sz;
+			dprintk(VIDC_DBG,
+				"buffer type = %d width = %d, height = %d\n",
+				frame_sz.buffer_type, frame_sz.width,
+				frame_sz.height);
+			rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_FRAME_SIZE, pdata);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"Failed setting OUTPUT2 size : %d\n",
+					rc);
+
+			alloc_mode.buffer_mode =
+				inst->buffer_mode_set[CAPTURE_PORT];
+			alloc_mode.buffer_type = HAL_BUFFER_OUTPUT2;
+			rc = call_hfi_op(hdev, session_set_property,
+				inst->session, HAL_PARAM_BUFFER_ALLOC_MODE,
+				&alloc_mode);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"Failed to set alloc_mode on OUTPUT2: %d\n",
+					rc);
+			break;
+		default:
+			dprintk(VIDC_ERR,
+				"Failed : Unsupported multi stream setting\n");
+			rc = -ENOTSUPP;
+			break;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR:
+		property_id = HAL_PARAM_VDEC_CONCEAL_COLOR;
+		property_val = ctrl->val;
+		pdata = &property_val;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_LEVEL);
+		property_id =
+			HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.profile = vdec_v4l2_to_hal(ctrl->id,
+				ctrl->val);
+		profile_level.level = vdec_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_PROFILE);
+		property_id =
+			HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.level = vdec_v4l2_to_hal(ctrl->id,
+				ctrl->val);
+		profile_level.profile = vdec_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT:
+		dprintk(VIDC_DBG,
+			"Limiting input buffer size from %u to %u\n",
+			inst->buffer_size_limit, ctrl->val);
+		inst->buffer_size_limit = ctrl->val;
+		break;
+	case V4L2_CID_VIDC_QBUF_MODE:
+		property_id = HAL_PARAM_SYNC_BASED_INTERRUPT;
+		hal_property.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED;
+		pdata = &hal_property;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY:
+		property_id = HAL_CONFIG_REALTIME;
+		/* firmware has inverted values for realtime and
+		 * non-realtime priority
+		 */
+		hal_property.enable = !(ctrl->val);
+		pdata = &hal_property;
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE:
+			inst->flags &= ~VIDC_REALTIME;
+			break;
+		case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE:
+			inst->flags |= VIDC_REALTIME;
+			break;
+		default:
+			dprintk(VIDC_WARN,
+				"inst(%pK) invalid priority ctrl value %#x\n",
+				inst, ctrl->val);
+			break;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE:
+		dprintk(VIDC_DBG,
+			"inst(%pK) operating rate changed from %d to %d\n",
+			inst, inst->operating_rate >> 16, ctrl->val >> 16);
+		inst->operating_rate = ctrl->val;
+		break;
+	default:
+		break;
+	}
+
+	v4l2_ctrl_lock(ctrl);
+#undef TRY_GET_CTRL
+
+	if (!rc && property_id) {
+		dprintk(VIDC_DBG,
+			"Control: HAL property=%#x,ctrl: id=%#x,value=%#x\n",
+			property_id, ctrl->id, ctrl->val);
+		rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, property_id, pdata);
+	}
+
+	return rc;
+}
+
+static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
+	struct v4l2_ext_controls *ctrl)
+{
+	int rc = 0, i = 0, fourcc = 0;
+	struct v4l2_ext_control *ext_control;
+	struct v4l2_control control;
+
+	if (!inst || !inst->core || !ctrl) {
+		dprintk(VIDC_ERR,
+			"%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	ext_control = ctrl->controls;
+	control.id =
+		V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE;
+
+	for (i = 0; i < ctrl->count; i++) {
+		switch (ext_control[i].id) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE:
+			control.value = ext_control[i].value;
+
+			rc = msm_comm_s_ctrl(inst, &control);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"%s Failed setting stream output mode : %d\n",
+					__func__, rc);
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT:
+			switch (ext_control[i].value) {
+			case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE:
+				if (!msm_comm_g_ctrl_for_id(inst, control.id)) {
+					rc = msm_comm_release_output_buffers(
+						inst);
+					if (rc)
+						dprintk(VIDC_ERR,
+							"%s Release output buffers failed\n",
+							__func__);
+				}
+				break;
+			case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC:
+			case V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC:
+				if (ext_control[i].value ==
+					V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC)
+					fourcc = V4L2_PIX_FMT_NV12_UBWC;
+				else
+					fourcc = V4L2_PIX_FMT_NV12_TP10_UBWC;
+				if (msm_comm_g_ctrl_for_id(inst, control.id)) {
+					rc = msm_comm_set_color_format(inst,
+						HAL_BUFFER_OUTPUT, fourcc);
+					if (rc) {
+						dprintk(VIDC_ERR,
+							"%s Failed setting output color format : %d\n",
+							__func__, rc);
+						break;
+					}
+					rc = msm_comm_try_get_bufreqs(inst);
+					if (rc)
+						dprintk(VIDC_ERR,
+							"%s Failed to get buffer requirements : %d\n",
+							__func__, rc);
+				}
+				break;
+			default:
+				dprintk(VIDC_ERR,
+					"%s Unsupported output color format\n",
+					__func__);
+				rc = -ENOTSUPP;
+				break;
+			}
+			break;
+		default:
+			dprintk(VIDC_ERR
+				, "%s Unsupported set control %d",
+				__func__, ext_control[i].id);
+			rc = -ENOTSUPP;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int msm_vdec_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+	int rc = 0, c = 0;
+
+	struct msm_vidc_inst *inst = container_of(ctrl->handler,
+				struct msm_vidc_inst, ctrl_handler);
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to start done state\n", inst);
+		goto failed_open_done;
+	}
+
+	for (c = 0; c < ctrl->ncontrols; ++c) {
+		if (ctrl->cluster[c]->is_new) {
+			rc = try_set_ctrl(inst, ctrl->cluster[c]);
+			if (rc) {
+				dprintk(VIDC_ERR, "Failed setting %x\n",
+						ctrl->cluster[c]->id);
+				break;
+			}
+		}
+	}
+
+failed_open_done:
+	return rc;
+}
+
+static int msm_vdec_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	int rc = 0, c = 0;
+
+	struct msm_vidc_inst *inst = container_of(ctrl->handler,
+				struct msm_vidc_inst, ctrl_handler);
+	struct v4l2_ctrl *master = ctrl->cluster[0];
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to start done state\n", inst);
+		goto failed_open_done;
+	}
+	for (c = 0; c < master->ncontrols; ++c) {
+		int d = 0;
+
+		for (d = 0; d < NUM_CTRLS; ++d) {
+			if (master->cluster[c]->id == inst->ctrls[d]->id &&
+				inst->ctrls[d]->flags &
+				V4L2_CTRL_FLAG_VOLATILE) {
+				rc = try_get_ctrl(inst, master->cluster[c]);
+				if (rc) {
+					dprintk(VIDC_ERR, "Failed getting %x\n",
+							master->cluster[c]->id);
+					return rc;
+				}
+				break;
+			}
+		}
+	}
+	return rc;
+
+failed_open_done:
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to get hal property\n");
+	return rc;
+}
+
+static const struct v4l2_ctrl_ops msm_vdec_ctrl_ops = {
+
+	.s_ctrl = msm_vdec_op_s_ctrl,
+	.g_volatile_ctrl = msm_vdec_op_g_volatile_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_vdec_get_ctrl_ops(void)
+{
+	return &msm_vdec_ctrl_ops;
+}
+
+int msm_vdec_s_ext_ctrl(struct msm_vidc_inst *inst,
+	struct v4l2_ext_controls *ctrl)
+{
+	int rc = 0;
+
+	rc = try_set_ext_ctrl(inst, ctrl);
+	if (rc) {
+		dprintk(VIDC_ERR, "Error setting extended control\n");
+		return rc;
+	}
+	return rc;
+}
+
+int msm_vdec_ctrl_init(struct msm_vidc_inst *inst)
+{
+	return msm_comm_ctrl_init(inst, msm_vdec_ctrls,
+		ARRAY_SIZE(msm_vdec_ctrls), &msm_vdec_ctrl_ops);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vdec.h b/drivers/media/platform/msm/vidc/msm_vdec.h
new file mode 100644
index 0000000..7b0ef13
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vdec.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012, 2015-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _MSM_VDEC_H_
+#define _MSM_VDEC_H_
+
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+
+int msm_vdec_inst_init(struct msm_vidc_inst *inst);
+int msm_vdec_ctrl_init(struct msm_vidc_inst *inst);
+int msm_vdec_querycap(void *instance, struct v4l2_capability *cap);
+int msm_vdec_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_vdec_s_fmt(void *instance, struct v4l2_format *f);
+int msm_vdec_g_fmt(void *instance, struct v4l2_format *f);
+int msm_vdec_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
+int msm_vdec_reqbufs(void *instance, struct v4l2_requestbuffers *b);
+int msm_vdec_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_vdec_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+int msm_vdec_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+int msm_vdec_cmd(struct msm_vidc_inst *inst, struct v4l2_decoder_cmd *dec);
+int msm_vdec_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
+struct vb2_ops *msm_vdec_get_vb2q_ops(void);
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_venc.c b/drivers/media/platform/msm/vidc/msm_venc.c
new file mode 100644
index 0000000..6c57b2e
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_venc.c
@@ -0,0 +1,3453 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include "msm_vidc_internal.h"
+#include "msm_vidc_common.h"
+#include "vidc_hfi_api.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_clocks.h"
+
+#define MSM_VENC_DVC_NAME "msm_venc_8974"
+#define MIN_NUM_OUTPUT_BUFFERS 4
+#define MIN_NUM_CAPTURE_BUFFERS 4
+#define MIN_BIT_RATE 32000
+#define MAX_BIT_RATE 300000000
+#define DEFAULT_BIT_RATE 64000
+#define BIT_RATE_STEP 100
+#define DEFAULT_FRAME_RATE 15
+#define MAX_OPERATING_FRAME_RATE (300 << 16)
+#define OPERATING_FRAME_RATE_STEP (1 << 16)
+#define MAX_SLICE_BYTE_SIZE ((MAX_BIT_RATE)>>3)
+#define MIN_SLICE_BYTE_SIZE 512
+#define MAX_SLICE_MB_SIZE ((4096 * 2304) >> 8)
+#define I_FRAME_QP 26
+#define P_FRAME_QP 28
+#define B_FRAME_QP 30
+#define MAX_INTRA_REFRESH_MBS ((4096 * 2304) >> 8)
+#define MAX_NUM_B_FRAMES 4
+#define MAX_LTR_FRAME_COUNT 10
+#define MAX_HYBRID_HIER_P_LAYERS 6
+
+#define L_MODE V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED_AT_SLICE_BOUNDARY
+#define CODING V4L2_MPEG_VIDEO_MPEG4_PROFILE_ADVANCED_CODING_EFFICIENCY
+#define BITSTREAM_RESTRICT_ENABLED \
+	V4L2_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT_ENABLED
+#define BITSTREAM_RESTRICT_DISABLED \
+	V4L2_MPEG_VIDC_VIDEO_H264_VUI_BITSTREAM_RESTRICT_DISABLED
+#define MIN_TIME_RESOLUTION 1
+#define MAX_TIME_RESOLUTION 0xFFFFFF
+#define DEFAULT_TIME_RESOLUTION 0x7530
+
+/*
+ * Default 601 to 709 conversion coefficients for resolution: 176x144 negative
+ * coeffs are converted to s4.9 format (e.g. -22 converted to ((1 << 13) - 22)
+ * 3x3 transformation matrix coefficients in s4.9 fixed point format
+ */
+static u32 vpe_csc_601_to_709_matrix_coeff[HAL_MAX_MATRIX_COEFFS] = {
+	470, 8170, 8148, 0, 490, 50, 0, 34, 483
+};
+
+/* offset coefficients in s9 fixed point format */
+static u32 vpe_csc_601_to_709_bias_coeff[HAL_MAX_BIAS_COEFFS] = {
+	34, 0, 4
+};
+
+/* clamping value for Y/U/V([min,max] for Y/U/V) */
+static u32 vpe_csc_601_to_709_limit_coeff[HAL_MAX_LIMIT_COEFFS] = {
+	16, 235, 16, 240, 16, 240
+};
+
+static const char *const mpeg_video_rate_control[] = {
+	"No Rate Control",
+	"VBR VFR",
+	"VBR CFR",
+	"CBR VFR",
+	"CBR CFR",
+	"MBR CFR",
+	"MBR VFR",
+	NULL
+};
+
+static const char *const mpeg_video_rotation[] = {
+	"No Rotation",
+	"90 Degree Rotation",
+	"180 Degree Rotation",
+	"270 Degree Rotation",
+	NULL
+};
+
+static const char *const h264_video_entropy_cabac_model[] = {
+	"Model 0",
+	"Model 1",
+	"Model 2",
+	NULL
+};
+
+static const char *const h263_level[] = {
+	"1.0",
+	"2.0",
+	"3.0",
+	"4.0",
+	"4.5",
+	"5.0",
+	"6.0",
+	"7.0",
+};
+
+static const char *const h263_profile[] = {
+	"Baseline",
+	"H320 Coding",
+	"Backward Compatible",
+	"ISWV2",
+	"ISWV3",
+	"High Compression",
+	"Internet",
+	"Interlace",
+	"High Latency",
+};
+
+static const char *const hevc_tier_level[] = {
+	"Main Tier Level 1",
+	"Main Tier Level 2",
+	"Main Tier Level 2.1",
+	"Main Tier Level 3",
+	"Main Tier Level 3.1",
+	"Main Tier Level 4",
+	"Main Tier Level 4.1",
+	"Main Tier Level 5",
+	"Main Tier Level 5.1",
+	"Main Tier Level 5.2",
+	"Main Tier Level 6",
+	"Main Tier Level 6.1",
+	"Main Tier Level 6.2",
+	"High Tier Level 1",
+	"High Tier Level 2",
+	"High Tier Level 2.1",
+	"High Tier Level 3",
+	"High Tier Level 3.1",
+	"High Tier Level 4",
+	"High Tier Level 4.1",
+	"High Tier Level 5",
+	"High Tier Level 5.1",
+	"High Tier Level 5.2",
+	"High Tier Level 6",
+	"High Tier Level 6.1",
+	"High Tier Level 6.2",
+};
+
+static const char *const hevc_profile[] = {
+	"Main",
+	"Main10",
+	"Main Still Pic",
+};
+
+static const char *const vp8_profile_level[] = {
+	"Unused",
+	"0.0",
+	"1.0",
+	"2.0",
+	"3.0",
+};
+
+static const char *const perf_level[] = {
+	"Nominal",
+	"Performance",
+	"Turbo"
+};
+
+static const char *const mbi_statistics[] = {
+	"Camcorder Default",
+	"Mode 1",
+	"Mode 2",
+	"Mode 3"
+};
+
+static const char *const intra_refresh_modes[] = {
+	"None",
+	"Cyclic",
+	"Adaptive",
+	"Cyclic Adaptive",
+	"Random"
+};
+
+static const char *const timestamp_mode[] = {
+	"Honor",
+	"Ignore",
+};
+
+static const char *const iframe_sizes[] = {
+	"Default",
+	"Medium",
+	"Huge",
+	"Unlimited"
+};
+
+static struct msm_vidc_ctrl msm_venc_ctrls[] = {
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD,
+		.name = "IDR Period",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = INT_MAX,
+		.default_value = DEFAULT_FRAME_RATE,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES,
+		.name = "Intra Period for P frames",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = INT_MAX,
+		.default_value = 2*DEFAULT_FRAME_RATE-1,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES,
+		.name = "Intra Period for B frames",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = INT_MAX,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME,
+		.name = "Request I Frame",
+		.type = V4L2_CTRL_TYPE_BUTTON,
+		.minimum = 0,
+		.maximum = 0,
+		.default_value = 0,
+		.step = 0,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL,
+		.name = "Video Framerate and Bitrate Control",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR)
+		),
+		.qmenu = mpeg_video_rate_control,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+		.name = "Bitrate Control",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+		.maximum = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR,
+		.default_value = V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+		.step = 0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) |
+		(1 << V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+		),
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_BITRATE,
+		.name = "Bit Rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_BIT_RATE,
+		.maximum = MAX_BIT_RATE,
+		.default_value = DEFAULT_BIT_RATE,
+		.step = BIT_RATE_STEP,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+		.name = "Peak Bit Rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_BIT_RATE,
+		.maximum = MAX_BIT_RATE,
+		.default_value = DEFAULT_BIT_RATE,
+		.step = BIT_RATE_STEP,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE,
+		.name = "Entropy Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.maximum = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC,
+		.default_value = V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC) |
+		(1 << V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC)
+		),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL,
+		.name = "CABAC Model",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2)
+		),
+		.qmenu = h264_video_entropy_cabac_model,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+		.name = "H264 Profile",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.maximum = V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH,
+		.default_value = V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+		.name = "H264 Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.maximum = V4L2_MPEG_VIDEO_H264_LEVEL_5_2,
+		.default_value = V4L2_MPEG_VIDEO_H264_LEVEL_1_0,
+		.menu_skip_mask = 0,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
+		.name = "VP8 Profile Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1)
+		),
+		.qmenu = vp8_profile_level,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE,
+		.name = "HEVC Profile",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN,
+		.menu_skip_mask =  ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC)
+		),
+		.qmenu = hevc_profile,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL,
+		.name = "HEVC Tier and Level",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2,
+		.default_value =
+			V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5) |
+		(1 << V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1)
+		),
+		.qmenu = hevc_tier_level,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
+		.name = "Rotation",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270)
+		),
+		.qmenu = mpeg_video_rotation,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP,
+		.name = "I Frame Quantization",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = I_FRAME_QP,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP,
+		.name = "P Frame Quantization",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = P_FRAME_QP,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP,
+		.name = "B Frame Quantization",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = B_FRAME_QP,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_MIN_QP,
+		.name = "H264 Minimum QP",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = 1,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_MAX_QP,
+		.name = "H264 Maximum QP",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 51,
+		.default_value = 51,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE,
+		.name = "Slice Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+		.maximum = V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES,
+		.default_value = V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE) |
+		(1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) |
+		(1 << V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES)
+		),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES,
+		.name = "Slice Byte Size",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_SLICE_BYTE_SIZE,
+		.maximum = MAX_SLICE_BYTE_SIZE,
+		.default_value = MIN_SLICE_BYTE_SIZE,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB,
+		.name = "Slice MB Size",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = MAX_SLICE_MB_SIZE,
+		.default_value = 1,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE,
+		.name = "Slice delivery mode",
+		.type = V4L2_CTRL_TYPE_BUTTON,
+		.minimum = 0,
+		.maximum = 1,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE,
+		.name = "Intra Refresh Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE,
+		.menu_skip_mask = ~(
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC) |
+		(1 << V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM)
+		),
+		.qmenu = intra_refresh_modes,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_IR_MBS,
+		.name = "Intra Refresh AIR MBS",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_INTRA_REFRESH_MBS,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA,
+		.name = "H.264 Loop Filter Alpha Offset",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = -6,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA,
+		.name = "H.264 Loop Filter Beta Offset",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = -6,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+		.name = "H.264 Loop Filter Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED,
+		.maximum = L_MODE,
+		.default_value = V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED) |
+		(1 << V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED) |
+		(1 << L_MODE)
+		),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_HEADER_MODE,
+		.name = "Sequence Header Mode",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE,
+		.maximum = V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+		.default_value =
+			V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME,
+		.menu_skip_mask = ~(
+		(1 << V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE) |
+		(1 << V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME)
+		),
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_SECURE,
+		.name = "Secure mode",
+		.type = V4L2_CTRL_TYPE_BUTTON,
+		.minimum = 0,
+		.maximum = 0,
+		.default_value = 0,
+		.step = 0,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA,
+		.name = "Extradata Type",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_MPEG_VIDC_EXTRADATA_NONE,
+		.maximum = V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO,
+		.default_value = V4L2_MPEG_VIDC_EXTRADATA_NONE,
+		.menu_skip_mask = ~(
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_NONE) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_LTR) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS)|
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_ROI_QP) |
+			(1 << V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO)
+			),
+		.qmenu = mpeg_video_vidc_extradata,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VUI_TIMING_INFO,
+		.name = "H264 VUI Timing Info",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_DISABLED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_ENABLED,
+		.default_value =
+			V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_DISABLED,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER,
+		.name = "H264 AU Delimiter",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED,
+		.step = 1,
+		.default_value =
+			V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDEO_CYCLIC_INTRA_REFRESH_MB,
+		.name = "Intra Refresh CIR MBS",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_INTRA_REFRESH_MBS,
+		.default_value = 0,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY,
+		.name = "Preserve Text Qualty",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_DISABLED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_ENABLED,
+		.default_value =
+			V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_DISABLED,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME,
+		.name = "H264 Use LTR",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (MAX_LTR_FRAME_COUNT - 1),
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT,
+		.name = "Ltr Count",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_LTR_FRAME_COUNT,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE,
+		.name = "Ltr Mode",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME,
+		.name = "H264 Mark LTR",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = (MAX_LTR_FRAME_COUNT - 1),
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS,
+		.name = "Set Hier P num layers",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE,
+		.name = "VP8 Error Resilience mode",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_DISABLED,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_ENABLED,
+		.default_value =
+			V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_DISABLED,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE,
+		.name = "I-Frame X coordinate search range",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 4,
+		.maximum = 128,
+		.default_value = 4,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE,
+		.name = "I-Frame Y coordinate search range",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 4,
+		.maximum = 128,
+		.default_value = 4,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE,
+		.name = "P-Frame X coordinate search range",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 4,
+		.maximum = 128,
+		.default_value = 4,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE,
+		.name = "P-Frame Y coordinate search range",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 4,
+		.maximum = 128,
+		.default_value = 4,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE,
+		.name = "B-Frame X coordinate search range",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 4,
+		.maximum = 128,
+		.default_value = 4,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE,
+		.name = "B-Frame Y coordinate search range",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 4,
+		.maximum = 128,
+		.default_value = 4,
+		.step = 1,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS,
+		.name = "Set Hier B num layers",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 3,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE,
+		.name = "Set Hybrid Hier P mode",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 5,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_VIDC_QBUF_MODE,
+		.name = "Allows batching of buffers for power savings",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_VIDC_QBUF_STANDARD,
+		.maximum = V4L2_VIDC_QBUF_BATCHED,
+		.default_value = V4L2_VIDC_QBUF_STANDARD,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS,
+		.name = "Set Max Hier P num layers sessions",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID,
+		.name = "Set Base Layer ID for Hier-P",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 6,
+		.default_value = 0,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH,
+		.name = "SAR Width",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 4096,
+		.default_value = 1,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT,
+		.name = "SAR Height",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 1,
+		.maximum = 2160,
+		.default_value = 1,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY,
+		.name = "Session Priority",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI,
+		.name = "VQZIP SEI",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_DISABLE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_ENABLE,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE,
+		.name = "Layer wise bitrate for H264/H265 Hybrid HP",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MIN_BIT_RATE,
+		.maximum = MAX_BIT_RATE,
+		.default_value = DEFAULT_BIT_RATE,
+		.step = BIT_RATE_STEP,
+		.menu_skip_mask = 0,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE,
+		.name = "Set Encoder Operating rate",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = MAX_OPERATING_FRAME_RATE,
+		.default_value = 0,
+		.step = OPERATING_FRAME_RATE_STEP,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE,
+		.name = "BITRATE TYPE",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_DISABLE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC,
+		.name = "Set VPE Color space conversion coefficients",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_DISABLE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE,
+		.name = "Low Latency Mode",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH,
+		.name = "Set Blur width",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 2048,
+		.default_value = 0,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT,
+		.name = "Set Blur height",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = 0,
+		.maximum = 2048,
+		.default_value = 0,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8,
+		.name = "Transform 8x8",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE,
+		.maximum = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE,
+		.default_value = V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE,
+		.name = "Set Color space",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MSM_VIDC_BT709_5,
+		.maximum = MSM_VIDC_BT2020,
+		.default_value = MSM_VIDC_BT601_6_625,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE,
+		.name = "Set Color space range",
+		.type = V4L2_CTRL_TYPE_BOOLEAN,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_ENABLE,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE,
+		.step = 1,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS,
+		.name = "Set Color space transfer characterstics",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MSM_VIDC_TRANSFER_BT709_5,
+		.maximum = MSM_VIDC_TRANSFER_BT_2020_12,
+		.default_value = MSM_VIDC_TRANSFER_601_6_625,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS,
+		.name = "Set Color space matrix coefficients",
+		.type = V4L2_CTRL_TYPE_INTEGER,
+		.minimum = MSM_VIDC_MATRIX_BT_709_5,
+		.maximum = MSM_VIDC_MATRIX_BT_2020_CONST,
+		.default_value = MSM_VIDC_MATRIX_601_6_625,
+		.step = 1,
+		.qmenu = NULL,
+	},
+	{
+		.id = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE,
+		.name = "Bounds of I-frame size",
+		.type = V4L2_CTRL_TYPE_MENU,
+		.minimum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT,
+		.maximum = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED,
+		.default_value = V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT,
+		.menu_skip_mask = ~(
+			(1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT) |
+			(1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM) |
+			(1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE) |
+			(1 << V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED)),
+		.qmenu = iframe_sizes,
+	},
+
+};
+
+#define NUM_CTRLS ARRAY_SIZE(msm_venc_ctrls)
+
+static u32 get_frame_size_nv12(int plane, u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12, width, height);
+}
+
+static u32 get_frame_size_nv12_ubwc(int plane, u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV12_UBWC, width, height);
+}
+
+static u32 get_frame_size_rgba(int plane, u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_RGBA8888, width, height);
+}
+
+static u32 get_frame_size_nv21(int plane, u32 height, u32 width)
+{
+	return VENUS_BUFFER_SIZE(COLOR_FMT_NV21, width, height);
+}
+
+static u32 get_frame_size_compressed(int plane, u32 height, u32 width)
+{
+	int sz = ALIGN(height, 32) * ALIGN(width, 32) * 3 / 2;
+
+	return ALIGN(sz, SZ_4K);
+}
+
+static struct msm_vidc_format venc_formats[] = {
+	{
+		.name = "YCbCr Semiplanar 4:2:0",
+		.description = "Y/CbCr 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV12,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv12,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "UBWC YCbCr Semiplanar 4:2:0",
+		.description = "UBWC Y/CbCr 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV12_UBWC,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv12_ubwc,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "RGBA 8:8:8:8",
+		.description = "RGBA 8:8:8:8",
+		.fourcc = V4L2_PIX_FMT_RGB32,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_rgba,
+		.type = OUTPUT_PORT,
+	},
+	{
+		.name = "H264",
+		.description = "H264 compressed format",
+		.fourcc = V4L2_PIX_FMT_H264,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "VP8",
+		.description = "VP8 compressed format",
+		.fourcc = V4L2_PIX_FMT_VP8,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "HEVC",
+		.description = "HEVC compressed format",
+		.fourcc = V4L2_PIX_FMT_HEVC,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_compressed,
+		.type = CAPTURE_PORT,
+	},
+	{
+		.name = "YCrCb Semiplanar 4:2:0",
+		.description = "Y/CrCb 4:2:0",
+		.fourcc = V4L2_PIX_FMT_NV21,
+		.num_planes = 1,
+		.get_frame_size = get_frame_size_nv21,
+		.type = OUTPUT_PORT,
+	},
+};
+
+static void msm_venc_update_plane_count(struct msm_vidc_inst *inst, int type)
+{
+	struct v4l2_ctrl *ctrl = NULL;
+	u32 extradata = 0;
+
+	if (!inst)
+		return;
+
+	inst->fmts[type].num_planes = 1;
+
+	ctrl = v4l2_ctrl_find(&inst->ctrl_handler,
+		V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA);
+
+	if (ctrl)
+		extradata = v4l2_ctrl_g_ctrl(ctrl);
+
+	if (type == CAPTURE_PORT) {
+		switch (extradata) {
+		case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
+		case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
+		case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
+		case V4L2_MPEG_VIDC_EXTRADATA_LTR:
+		case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
+			inst->fmts[CAPTURE_PORT].num_planes = 2;
+		default:
+			break;
+		}
+	} else if (type == OUTPUT_PORT) {
+		switch (extradata) {
+		case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP:
+		case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM:
+		case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO:
+		case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS:
+		case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP:
+		case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO:
+			inst->fmts[OUTPUT_PORT].num_planes = 2;
+			break;
+		default:
+			break;
+		}
+	}
+}
+
+static int msm_venc_set_csc(struct msm_vidc_inst *inst);
+
+static int msm_venc_queue_setup(struct vb2_queue *q,
+	unsigned int *num_buffers, unsigned int *num_planes,
+	unsigned int sizes[], struct device *alloc_devs[])
+{
+	int i, temp, rc = 0;
+	struct msm_vidc_inst *inst;
+	struct hal_buffer_count_actual new_buf_count;
+	enum hal_property property_id;
+	struct hfi_device *hdev;
+	struct hal_buffer_requirements *buff_req;
+	u32 extra_idx = 0;
+	struct hal_buffer_requirements *buff_req_buffer = NULL;
+
+	if (!q || !q->drv_priv) {
+		dprintk(VIDC_ERR, "Invalid input\n");
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to open instance\n");
+		return rc;
+	}
+
+	rc = msm_comm_try_get_bufreqs(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to get buffer requirements: %d\n", rc);
+		return rc;
+	}
+
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		*num_planes = 1;
+
+		buff_req = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+		if (buff_req) {
+			/*
+			 * Pretend as if the FW itself is asking for additional
+			 * buffers, which are required for DCVS
+			 */
+			unsigned int min_req_buffers =
+				buff_req->buffer_count_min +
+				msm_dcvs_get_extra_buff_count(inst);
+			*num_buffers = max(*num_buffers, min_req_buffers);
+		}
+
+		if (*num_buffers < MIN_NUM_CAPTURE_BUFFERS ||
+				*num_buffers > VB2_MAX_FRAME) {
+			int temp = *num_buffers;
+
+			*num_buffers = clamp_val(*num_buffers,
+					MIN_NUM_CAPTURE_BUFFERS,
+					VB2_MAX_FRAME);
+			dprintk(VIDC_INFO,
+				"Changing buffer count on CAPTURE_MPLANE from %d to %d for best effort encoding\n",
+				temp, *num_buffers);
+		}
+
+		msm_venc_update_plane_count(inst, CAPTURE_PORT);
+		*num_planes = inst->fmts[CAPTURE_PORT].num_planes;
+
+		for (i = 0; i < *num_planes; i++) {
+			int extra_idx = EXTRADATA_IDX(*num_planes);
+
+			buff_req_buffer = get_buff_req_buffer(inst,
+					HAL_BUFFER_OUTPUT);
+
+			sizes[i] = buff_req_buffer ?
+				buff_req_buffer->buffer_size : 0;
+
+			if (extra_idx && i == extra_idx &&
+					extra_idx < VIDEO_MAX_PLANES) {
+				buff_req_buffer = get_buff_req_buffer(inst,
+						HAL_BUFFER_EXTRADATA_OUTPUT);
+				if (!buff_req_buffer) {
+					dprintk(VIDC_ERR,
+						"%s: failed - invalid buffer req\n",
+						__func__);
+					return -EINVAL;
+				}
+
+				sizes[i] = buff_req_buffer->buffer_size;
+			}
+		}
+
+		dprintk(VIDC_DBG, "actual output buffer count set to fw = %d\n",
+				*num_buffers);
+		property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
+		new_buf_count.buffer_type = HAL_BUFFER_OUTPUT;
+		new_buf_count.buffer_count_actual = *num_buffers;
+		rc = call_hfi_op(hdev, session_set_property, inst->session,
+			property_id, &new_buf_count);
+
+		break;
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		*num_planes = 1;
+
+		*num_buffers = inst->buff_req.buffer[0].buffer_count_actual =
+			max(*num_buffers, inst->buff_req.buffer[0].
+				buffer_count_min);
+
+		temp = *num_buffers;
+
+		*num_buffers = clamp_val(*num_buffers,
+				MIN_NUM_OUTPUT_BUFFERS,
+				VB2_MAX_FRAME);
+		dprintk(VIDC_INFO,
+			"Changing buffer count on OUTPUT_MPLANE from %d to %d for best effort encoding\n",
+			temp, *num_buffers);
+
+		property_id = HAL_PARAM_BUFFER_COUNT_ACTUAL;
+		new_buf_count.buffer_type = HAL_BUFFER_INPUT;
+		new_buf_count.buffer_count_actual = *num_buffers;
+
+		dprintk(VIDC_DBG, "actual input buffer count set to fw = %d\n",
+				*num_buffers);
+
+		msm_venc_update_plane_count(inst, OUTPUT_PORT);
+		*num_planes = inst->fmts[OUTPUT_PORT].num_planes;
+
+		rc = call_hfi_op(hdev, session_set_property, inst->session,
+					property_id, &new_buf_count);
+		if (rc)
+			dprintk(VIDC_ERR, "failed to set count to fw\n");
+
+		dprintk(VIDC_DBG, "size = %d, alignment = %d, count = %d\n",
+				inst->buff_req.buffer[0].buffer_size,
+				inst->buff_req.buffer[0].buffer_alignment,
+				inst->buff_req.buffer[0].buffer_count_actual);
+		sizes[0] = inst->fmts[OUTPUT_PORT].get_frame_size(
+				0, inst->prop.height[OUTPUT_PORT],
+				inst->prop.width[OUTPUT_PORT]);
+
+		extra_idx =
+			EXTRADATA_IDX(inst->fmts[OUTPUT_PORT].num_planes);
+		if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+			buff_req_buffer = get_buff_req_buffer(inst,
+				HAL_BUFFER_EXTRADATA_INPUT);
+			if (!buff_req_buffer) {
+				dprintk(VIDC_ERR,
+					"%s: failed - invalid buffer req\n",
+					__func__);
+				return -EINVAL;
+			}
+
+			sizes[extra_idx] = buff_req_buffer->buffer_size;
+		}
+
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid q type = %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static int msm_venc_toggle_hier_p(struct msm_vidc_inst *inst, int layers)
+{
+	int num_enh_layers = 0;
+	u32 property_id = 0;
+	struct hfi_device *hdev = NULL;
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_VP8)
+		return 0;
+
+	num_enh_layers = layers ? : 0;
+	dprintk(VIDC_DBG, "%s Hier-P in firmware\n",
+			num_enh_layers ? "Enable" : "Disable");
+
+	hdev = inst->core->device;
+	property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS;
+	rc = call_hfi_op(hdev, session_set_property,
+			(void *)inst->session, property_id,
+			(void *)&num_enh_layers);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"%s: failed with error = %d\n", __func__, rc);
+	}
+	return rc;
+}
+
+static inline int msm_venc_power_save_mode_enable(struct msm_vidc_inst *inst)
+{
+	u32 rc = 0;
+	u32 prop_id = 0, power_save_min = 0, power_save_max = 0, inst_load = 0;
+	void *pdata = NULL;
+	struct hfi_device *hdev = NULL;
+	enum hal_perf_mode venc_mode;
+	enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD |
+		LOAD_CALC_IGNORE_THUMBNAIL_LOAD;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	inst_load = msm_comm_get_inst_load(inst, quirks);
+	power_save_min = inst->capability.mbs_per_sec_power_save.min;
+	power_save_max = inst->capability.mbs_per_sec_power_save.max;
+
+	if (!power_save_min || !power_save_max)
+		return rc;
+
+	hdev = inst->core->device;
+	if (inst_load >= power_save_min && inst_load <= power_save_max) {
+		prop_id = HAL_CONFIG_VENC_PERF_MODE;
+		venc_mode = HAL_PERF_MODE_POWER_SAVE;
+		pdata = &venc_mode;
+		rc = call_hfi_op(hdev, session_set_property,
+				(void *)inst->session, prop_id, pdata);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: Failed to set power save mode for inst: %pK\n",
+				__func__, inst);
+			goto fail_power_mode_set;
+		}
+		inst->flags |= VIDC_LOW_POWER;
+		dprintk(VIDC_INFO, "Power Save Mode set for inst: %pK\n", inst);
+	}
+
+fail_power_mode_set:
+	return rc;
+}
+
+static inline int start_streaming(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	msm_venc_power_save_mode_enable(inst);
+	if (inst->capability.pixelprocess_capabilities &
+		HAL_VIDEO_ENCODER_SCALING_CAPABILITY)
+		rc = msm_vidc_check_scaling_supported(inst);
+	if (rc) {
+		dprintk(VIDC_ERR, "H/w scaling is not in valid range\n");
+		return -EINVAL;
+	}
+	rc = msm_comm_try_get_bufreqs(inst);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to get Buffer Requirements : %d\n", rc);
+		goto fail_start;
+	}
+	rc = msm_comm_set_scratch_buffers(inst);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to set scratch buffers: %d\n", rc);
+		goto fail_start;
+	}
+	rc = msm_comm_set_persist_buffers(inst);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to set persist buffers: %d\n", rc);
+		goto fail_start;
+	}
+
+	msm_comm_scale_clocks_and_bus(inst);
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_START_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to start done state\n", inst);
+		goto fail_start;
+	}
+	msm_dcvs_init_load(inst);
+
+fail_start:
+	return rc;
+}
+
+static int msm_venc_start_streaming(struct vb2_queue *q, unsigned int count)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+
+	if (!q || !q->drv_priv) {
+		dprintk(VIDC_ERR, "Invalid input, q = %pK\n", q);
+		return -EINVAL;
+	}
+	inst = q->drv_priv;
+	dprintk(VIDC_DBG, "Streamon called on: %d capability for inst: %pK\n",
+		q->type, inst);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		if (inst->bufq[CAPTURE_PORT].vb2_bufq.streaming)
+			rc = start_streaming(inst);
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (inst->bufq[OUTPUT_PORT].vb2_bufq.streaming)
+			rc = start_streaming(inst);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Queue type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		goto stream_start_failed;
+	}
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Streamon failed on: %d capability for inst: %pK\n",
+			q->type, inst);
+		goto stream_start_failed;
+	}
+
+	rc = msm_comm_qbuf(inst, NULL);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to commit buffers queued before STREAM_ON to hardware: %d\n",
+				rc);
+		goto stream_start_failed;
+	}
+
+stream_start_failed:
+	return rc;
+}
+
+static void msm_venc_stop_streaming(struct vb2_queue *q)
+{
+	struct msm_vidc_inst *inst;
+	int rc = 0;
+
+	if (!q || !q->drv_priv) {
+		dprintk(VIDC_ERR, "%s - Invalid input, q = %pK\n", __func__, q);
+		return;
+	}
+
+	inst = q->drv_priv;
+	dprintk(VIDC_DBG, "Streamoff called on: %d capability\n", q->type);
+	switch (q->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Q-type is not supported: %d\n", q->type);
+		rc = -EINVAL;
+		break;
+	}
+
+	msm_comm_scale_clocks_and_bus(inst);
+
+	if (rc)
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK, cap = %d to state: %d\n",
+			inst, q->type, MSM_VIDC_CLOSE_DONE);
+}
+
+static void msm_venc_buf_queue(struct vb2_buffer *vb)
+{
+	int rc = msm_comm_qbuf(vb2_get_drv_priv(vb->vb2_queue), vb);
+
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to queue buffer: %d\n", rc);
+}
+
+static void msm_venc_buf_cleanup(struct vb2_buffer *vb)
+{
+	int rc = 0;
+	struct buf_queue *q = NULL;
+	struct msm_vidc_inst *inst = NULL;
+
+	if (!vb) {
+		dprintk(VIDC_ERR, "%s : Invalid vb pointer %pK",
+			__func__, vb);
+		return;
+	}
+
+	inst = vb2_get_drv_priv(vb->vb2_queue);
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s : Invalid inst pointer",
+			__func__);
+		return;
+	}
+
+	q = msm_comm_get_vb2q(inst, vb->type);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"%s : Failed to find buffer queue for type = %d\n",
+				__func__, vb->type);
+		return;
+	}
+
+	if (q->vb2_bufq.streaming) {
+		dprintk(VIDC_DBG, "%d PORT is streaming\n",
+			vb->type);
+		return;
+	}
+
+	rc = msm_vidc_release_buffers(inst, vb->type);
+	if (rc)
+		dprintk(VIDC_ERR, "%s : Failed to release buffers : %d\n",
+			__func__, rc);
+}
+
+static const struct vb2_ops msm_venc_vb2q_ops = {
+	.queue_setup = msm_venc_queue_setup,
+	.start_streaming = msm_venc_start_streaming,
+	.buf_queue = msm_venc_buf_queue,
+	.buf_cleanup = msm_venc_buf_cleanup,
+	.stop_streaming = msm_venc_stop_streaming,
+};
+
+const struct vb2_ops *msm_venc_get_vb2q_ops(void)
+{
+	return &msm_venc_vb2q_ops;
+}
+
+static struct v4l2_ctrl *get_ctrl_from_cluster(int id,
+		struct v4l2_ctrl **cluster, int ncontrols)
+{
+	int c;
+
+	for (c = 0; c < ncontrols; ++c)
+		if (cluster[c]->id == id)
+			return cluster[c];
+	return NULL;
+}
+
+/* Helper function to translate V4L2_* to HAL_* */
+static inline int venc_v4l2_to_hal(int id, int value)
+{
+	switch (id) {
+	/* H264 */
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_H264_PROFILE_BASELINE:
+			return HAL_H264_PROFILE_BASELINE;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_BASELINE:
+			return HAL_H264_PROFILE_CONSTRAINED_BASE;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_MAIN:
+			return HAL_H264_PROFILE_MAIN;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_EXTENDED:
+			return HAL_H264_PROFILE_EXTENDED;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH:
+			return HAL_H264_PROFILE_HIGH;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_10:
+			return HAL_H264_PROFILE_HIGH10;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_422:
+			return HAL_H264_PROFILE_HIGH422;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_HIGH_444_PREDICTIVE:
+			return HAL_H264_PROFILE_HIGH444;
+		case V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH:
+			return HAL_H264_PROFILE_CONSTRAINED_HIGH;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_0:
+			return HAL_H264_LEVEL_1;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1B:
+			return HAL_H264_LEVEL_1b;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_1:
+			return HAL_H264_LEVEL_11;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_2:
+			return HAL_H264_LEVEL_12;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_1_3:
+			return HAL_H264_LEVEL_13;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_0:
+			return HAL_H264_LEVEL_2;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_1:
+			return HAL_H264_LEVEL_21;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_2_2:
+			return HAL_H264_LEVEL_22;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_0:
+			return HAL_H264_LEVEL_3;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_1:
+			return HAL_H264_LEVEL_31;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_3_2:
+			return HAL_H264_LEVEL_32;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_0:
+			return HAL_H264_LEVEL_4;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_1:
+			return HAL_H264_LEVEL_41;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_4_2:
+			return HAL_H264_LEVEL_42;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_0:
+			return HAL_H264_LEVEL_5;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_1:
+			return HAL_H264_LEVEL_51;
+		case V4L2_MPEG_VIDEO_H264_LEVEL_5_2:
+			return HAL_H264_LEVEL_52;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CAVLC:
+			return HAL_H264_ENTROPY_CAVLC;
+		case V4L2_MPEG_VIDEO_H264_ENTROPY_MODE_CABAC:
+			return HAL_H264_ENTROPY_CABAC;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL:
+		switch (value) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0:
+			return HAL_H264_CABAC_MODEL_0;
+		case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1:
+			return HAL_H264_CABAC_MODEL_1;
+		case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2:
+			return HAL_H264_CABAC_MODEL_2;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+		switch (value) {
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0:
+			return HAL_VPX_PROFILE_VERSION_0;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1:
+			return HAL_VPX_PROFILE_VERSION_1;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2:
+			return HAL_VPX_PROFILE_VERSION_2;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3:
+			return HAL_VPX_PROFILE_VERSION_3;
+		case V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED:
+			return HAL_VPX_PROFILE_UNUSED;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE:
+		switch (value) {
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN:
+			return HAL_HEVC_PROFILE_MAIN;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10:
+			return HAL_HEVC_PROFILE_MAIN10;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC:
+			return HAL_HEVC_PROFILE_MAIN_STILL_PIC;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL:
+		switch (value) {
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1:
+			return HAL_HEVC_MAIN_TIER_LEVEL_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2:
+			return HAL_HEVC_MAIN_TIER_LEVEL_2;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1:
+			return HAL_HEVC_MAIN_TIER_LEVEL_2_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3:
+			return HAL_HEVC_MAIN_TIER_LEVEL_3;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1:
+			return HAL_HEVC_MAIN_TIER_LEVEL_3_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4:
+			return HAL_HEVC_MAIN_TIER_LEVEL_4;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1:
+			return HAL_HEVC_MAIN_TIER_LEVEL_4_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5:
+			return HAL_HEVC_MAIN_TIER_LEVEL_5;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1:
+			return HAL_HEVC_MAIN_TIER_LEVEL_5_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_2:
+			return HAL_HEVC_MAIN_TIER_LEVEL_5_2;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6:
+			return HAL_HEVC_MAIN_TIER_LEVEL_6;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_1:
+			return HAL_HEVC_MAIN_TIER_LEVEL_6_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_2:
+			return HAL_HEVC_MAIN_TIER_LEVEL_6_2;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1:
+			return HAL_HEVC_HIGH_TIER_LEVEL_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2:
+			return HAL_HEVC_HIGH_TIER_LEVEL_2;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1:
+			return HAL_HEVC_HIGH_TIER_LEVEL_2_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3:
+			return HAL_HEVC_HIGH_TIER_LEVEL_3;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1:
+			return HAL_HEVC_HIGH_TIER_LEVEL_3_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4:
+			return HAL_HEVC_HIGH_TIER_LEVEL_4;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1:
+			return HAL_HEVC_HIGH_TIER_LEVEL_4_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5:
+			return HAL_HEVC_HIGH_TIER_LEVEL_5;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1:
+			return HAL_HEVC_HIGH_TIER_LEVEL_5_1;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2:
+			return HAL_HEVC_HIGH_TIER_LEVEL_5_2;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6:
+			return HAL_HEVC_HIGH_TIER_LEVEL_6;
+		case V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_1:
+			return HAL_HEVC_HIGH_TIER_LEVEL_6_1;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION:
+		switch (value) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE:
+			return HAL_ROTATE_NONE;
+		case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90:
+			return HAL_ROTATE_90;
+		case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180:
+			return HAL_ROTATE_180;
+		case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270:
+			return HAL_ROTATE_270;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+		switch (value) {
+		case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_DISABLED:
+			return HAL_H264_DB_MODE_DISABLE;
+		case V4L2_MPEG_VIDEO_H264_LOOP_FILTER_MODE_ENABLED:
+			return HAL_H264_DB_MODE_ALL_BOUNDARY;
+		case L_MODE:
+			return HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY;
+		default:
+			goto unknown_value;
+		}
+	case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE:
+		switch (value) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT:
+			return HAL_IFRAMESIZE_TYPE_DEFAULT;
+		case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM:
+			return HAL_IFRAMESIZE_TYPE_MEDIUM;
+		case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE:
+			return HAL_IFRAMESIZE_TYPE_HUGE;
+		case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED:
+			return HAL_IFRAMESIZE_TYPE_UNLIMITED;
+		default:
+			goto unknown_value;
+		}
+	}
+
+unknown_value:
+	dprintk(VIDC_WARN, "Unknown control (%x, %d)\n", id, value);
+	return -EINVAL;
+}
+
+static int try_set_ctrl(struct msm_vidc_inst *inst, struct v4l2_ctrl *ctrl)
+{
+	int rc = 0;
+	struct hal_request_iframe request_iframe;
+	struct hal_bitrate bitrate;
+	struct hal_profile_level profile_level;
+	struct hal_h264_entropy_control h264_entropy_control;
+	struct hal_quantization quantization;
+	struct hal_intra_period intra_period;
+	struct hal_idr_period idr_period;
+	struct hal_operations operations;
+	struct hal_intra_refresh intra_refresh;
+	struct hal_multi_slice_control multi_slice_control;
+	struct hal_h264_db_control h264_db_control;
+	struct hal_enable enable;
+	struct hal_quantization_range qp_range;
+	struct hal_preserve_text_quality preserve_text_quality;
+	u32 property_id = 0, property_val = 0;
+	void *pdata = NULL;
+	struct v4l2_ctrl *temp_ctrl = NULL;
+	struct hfi_device *hdev;
+	struct hal_extradata_enable extra;
+	struct hal_ltr_use use_ltr;
+	struct hal_ltr_mark mark_ltr;
+	struct hal_hybrid_hierp hyb_hierp;
+	u32 hier_p_layers = 0, hier_b_layers = 0;
+	int max_hierp_layers;
+	int baselayerid = 0;
+	struct hal_video_signal_info signal_info = {0};
+	enum hal_iframesize_type iframesize_type = HAL_IFRAMESIZE_TYPE_DEFAULT;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	/* Small helper macro for quickly getting a control and err checking */
+#define TRY_GET_CTRL(__ctrl_id) ({ \
+		struct v4l2_ctrl *__temp; \
+		__temp = get_ctrl_from_cluster( \
+			__ctrl_id, \
+			ctrl->cluster, ctrl->ncontrols); \
+		if (!__temp) { \
+			dprintk(VIDC_ERR, "Can't find %s (%x) in cluster\n", \
+				#__ctrl_id, __ctrl_id); \
+			/* Clusters are hardcoded, if we can't find */ \
+			/* something then things are massively screwed up */ \
+			MSM_VIDC_ERROR(1); \
+		} \
+		__temp; \
+	})
+
+	/*
+	 * Unlock the control prior to setting to the hardware. Otherwise
+	 * lower level code that attempts to do a get_ctrl() will end up
+	 * deadlocking.
+	 */
+	v4l2_ctrl_unlock(ctrl);
+
+	switch (ctrl->id) {
+	case V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD:
+		if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_H264 &&
+			inst->fmts[CAPTURE_PORT].fourcc !=
+				V4L2_PIX_FMT_H264_NO_SC &&
+			inst->fmts[CAPTURE_PORT].fourcc !=
+				V4L2_PIX_FMT_HEVC) {
+			dprintk(VIDC_ERR,
+				"Control %#x only valid for H264 and HEVC\n",
+				ctrl->id);
+			rc = -ENOTSUPP;
+			break;
+		}
+
+		property_id = HAL_CONFIG_VENC_IDR_PERIOD;
+		idr_period.idr_period = ctrl->val;
+		pdata = &idr_period;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES:
+	case V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES:
+	{
+		int num_p, num_b;
+		u32 max_num_b_frames;
+
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES);
+		num_b = temp_ctrl->val;
+
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES);
+		num_p = temp_ctrl->val;
+
+		if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES)
+			num_p = ctrl->val;
+		else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES)
+			num_b = ctrl->val;
+
+		max_num_b_frames = num_b ? MAX_NUM_B_FRAMES : 0;
+		property_id = HAL_PARAM_VENC_MAX_NUM_B_FRAMES;
+		pdata = &max_num_b_frames;
+		rc = call_hfi_op(hdev, session_set_property,
+			(void *)inst->session, property_id, pdata);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed : Setprop MAX_NUM_B_FRAMES %d\n",
+				rc);
+			break;
+		}
+
+		property_id = HAL_CONFIG_VENC_INTRA_PERIOD;
+		intra_period.pframes = num_p;
+		intra_period.bframes = num_b;
+
+		/*
+		 *Incase firmware does not have B-Frame support,
+		 *offload the b-frame count to p-frame to make up
+		 *for the requested Intraperiod
+		 */
+		if (!inst->capability.bframe.max) {
+			intra_period.pframes = num_p + num_b;
+			intra_period.bframes = 0;
+			dprintk(VIDC_DBG,
+				"No bframe support, changing pframe from %d to %d\n",
+				num_p, intra_period.pframes);
+		}
+		pdata = &intra_period;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME:
+		property_id = HAL_CONFIG_VENC_REQUEST_IFRAME;
+		request_iframe.enable = true;
+		pdata = &request_iframe;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL:
+	case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+	{
+		int final_mode = 0;
+		struct v4l2_ctrl update_ctrl = {.id = 0};
+
+		/* V4L2_CID_MPEG_VIDEO_BITRATE_MODE and _RATE_CONTROL
+		 * manipulate the same thing.  If one control's state
+		 * changes, try to mirror the state in the other control's
+		 * value
+		 */
+		if (ctrl->id == V4L2_CID_MPEG_VIDEO_BITRATE_MODE) {
+			if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR) {
+				final_mode = HAL_RATE_CONTROL_VBR_CFR;
+				update_ctrl.val =
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR;
+			} else {/* ...if (ctrl->val == _BITRATE_MODE_CBR) */
+				final_mode = HAL_RATE_CONTROL_CBR_CFR;
+				update_ctrl.val =
+				V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR;
+			}
+
+			update_ctrl.id = V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL;
+
+		} else if (ctrl->id == V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL) {
+			switch (ctrl->val) {
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF:
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR:
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR:
+				update_ctrl.val =
+					V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+				break;
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR:
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR:
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR:
+			case V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR:
+				update_ctrl.val =
+					V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
+				break;
+			}
+
+			final_mode = ctrl->val;
+			update_ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE;
+		}
+
+		if (update_ctrl.id) {
+			temp_ctrl = TRY_GET_CTRL(update_ctrl.id);
+			temp_ctrl->val = update_ctrl.val;
+		}
+
+		property_id = HAL_PARAM_VENC_RATE_CONTROL;
+		property_val = final_mode;
+		pdata = &property_val;
+
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_BITRATE:
+	{
+		property_id = HAL_CONFIG_VENC_TARGET_BITRATE;
+		bitrate.bit_rate = ctrl->val;
+		bitrate.layer_id = 0;
+		pdata = &bitrate;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+	{
+		struct v4l2_ctrl *avg_bitrate = TRY_GET_CTRL(
+			V4L2_CID_MPEG_VIDEO_BITRATE);
+
+		if (ctrl->val < avg_bitrate->val) {
+			dprintk(VIDC_ERR,
+				"Peak bitrate (%d) is lower than average bitrate (%d)\n",
+				ctrl->val, avg_bitrate->val);
+			rc = -EINVAL;
+			break;
+		} else if (ctrl->val < avg_bitrate->val * 2) {
+			dprintk(VIDC_WARN,
+				"Peak bitrate (%d) ideally should be twice the average bitrate (%d)\n",
+				ctrl->val, avg_bitrate->val);
+		}
+
+		property_id = HAL_CONFIG_VENC_MAX_BITRATE;
+		bitrate.bit_rate = ctrl->val;
+		bitrate.layer_id = 0;
+		pdata = &bitrate;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE:
+		temp_ctrl = TRY_GET_CTRL(
+			V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL);
+
+		property_id = HAL_PARAM_VENC_H264_ENTROPY_CONTROL;
+		h264_entropy_control.entropy_mode = venc_v4l2_to_hal(
+			V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, ctrl->val);
+		h264_entropy_control.cabac_model = venc_v4l2_to_hal(
+			V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL,
+			temp_ctrl->val);
+		pdata = &h264_entropy_control;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE);
+
+		property_id = HAL_PARAM_VENC_H264_ENTROPY_CONTROL;
+		h264_entropy_control.cabac_model = venc_v4l2_to_hal(
+			V4L2_CID_MPEG_VIDEO_H264_ENTROPY_MODE, ctrl->val);
+		h264_entropy_control.entropy_mode = venc_v4l2_to_hal(
+			V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL,
+			temp_ctrl->val);
+		pdata = &h264_entropy_control;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL);
+
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.profile = venc_v4l2_to_hal(ctrl->id,
+						ctrl->val);
+		profile_level.level = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE);
+
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.level = venc_v4l2_to_hal(ctrl->id,
+							ctrl->val);
+		profile_level.profile = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_PROFILE:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_LEVEL);
+
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.profile = venc_v4l2_to_hal(ctrl->id,
+							ctrl->val);
+		profile_level.level = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_LEVEL,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		dprintk(VIDC_DBG, "\nprofile: %d\n",
+			   profile_level.profile);
+		break;
+	case V4L2_CID_MPEG_VIDEO_H264_LEVEL:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_PROFILE);
+
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.level = venc_v4l2_to_hal(ctrl->id,
+							ctrl->val);
+		profile_level.profile = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_PROFILE,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		dprintk(VIDC_DBG, "\nLevel: %d\n",
+			   profile_level.level);
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL:
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.profile = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL,
+				ctrl->val);
+		profile_level.level = HAL_VPX_PROFILE_UNUSED;
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE:
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL);
+
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.profile = venc_v4l2_to_hal(ctrl->id,
+							ctrl->val);
+		profile_level.level = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE);
+
+		property_id = HAL_PARAM_PROFILE_LEVEL_CURRENT;
+		profile_level.level = venc_v4l2_to_hal(ctrl->id,
+							ctrl->val);
+		profile_level.profile = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE,
+				temp_ctrl->val);
+		pdata = &profile_level;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_ROTATION:
+	{
+		if (!(inst->capability.pixelprocess_capabilities &
+			HAL_VIDEO_ENCODER_ROTATION_CAPABILITY)) {
+			dprintk(VIDC_ERR, "Rotation not supported: %#x\n",
+				ctrl->id);
+			rc = -ENOTSUPP;
+			break;
+		}
+		property_id = HAL_CONFIG_VPE_OPERATIONS;
+		operations.rotate = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_ROTATION,
+				ctrl->val);
+		operations.flip = HAL_FLIP_NONE;
+		pdata = &operations;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP: {
+		struct v4l2_ctrl *qpp, *qpb;
+
+		qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP);
+		qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP);
+
+		property_id = HAL_PARAM_VENC_SESSION_QP;
+		quantization.qpi = ctrl->val;
+		quantization.qpp = qpp->val;
+		quantization.qpb = qpb->val;
+		quantization.layer_id = 0;
+
+		pdata = &quantization;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP: {
+		struct v4l2_ctrl *qpi, *qpb;
+
+		qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP);
+		qpb = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP);
+
+		property_id = HAL_PARAM_VENC_SESSION_QP;
+		quantization.qpp = ctrl->val;
+		quantization.qpi = qpi->val;
+		quantization.qpb = qpb->val;
+		quantization.layer_id = 0;
+
+		pdata = &quantization;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_B_FRAME_QP: {
+		struct v4l2_ctrl *qpi, *qpp;
+
+		qpi = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_I_FRAME_QP);
+		qpp = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_P_FRAME_QP);
+
+		property_id = HAL_PARAM_VENC_SESSION_QP;
+		quantization.qpb = ctrl->val;
+		quantization.qpi = qpi->val;
+		quantization.qpp = qpp->val;
+		quantization.layer_id = 0;
+
+		pdata = &quantization;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_MIN_QP: {
+		struct v4l2_ctrl *qp_max;
+
+		qp_max = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MAX_QP);
+		if (ctrl->val >= qp_max->val) {
+			dprintk(VIDC_ERR,
+					"Bad range: Min QP (%d) > Max QP(%d)\n",
+					ctrl->val, qp_max->val);
+			rc = -ERANGE;
+			break;
+		}
+
+		property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+		qp_range.layer_id = 0;
+		qp_range.max_qp = qp_max->val;
+		qp_range.min_qp = ctrl->val;
+
+		pdata = &qp_range;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_MAX_QP: {
+		struct v4l2_ctrl *qp_min;
+
+		qp_min = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_H264_MIN_QP);
+		if (ctrl->val <= qp_min->val) {
+			dprintk(VIDC_ERR,
+					"Bad range: Max QP (%d) < Min QP(%d)\n",
+					ctrl->val, qp_min->val);
+			rc = -ERANGE;
+			break;
+		}
+
+		property_id = HAL_PARAM_VENC_SESSION_QP_RANGE;
+		qp_range.layer_id = 0;
+		qp_range.max_qp = ctrl->val;
+		qp_range.min_qp = qp_min->val;
+
+		pdata = &qp_range;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE: {
+		int temp = 0;
+
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB:
+			temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB;
+			break;
+		case V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_BYTES:
+			temp = V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES;
+			break;
+		case V4L2_MPEG_VIDEO_MULTI_SLICE_MODE_SINGLE:
+		default:
+			temp = 0;
+			break;
+		}
+
+		if (temp)
+			temp_ctrl = TRY_GET_CTRL(temp);
+
+		property_id = HAL_PARAM_VENC_MULTI_SLICE_CONTROL;
+		multi_slice_control.multi_slice = ctrl->val;
+		multi_slice_control.slice_size = temp ? temp_ctrl->val : 0;
+
+		pdata = &multi_slice_control;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_BYTES:
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MAX_MB:
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE);
+
+		property_id = HAL_PARAM_VENC_MULTI_SLICE_CONTROL;
+		multi_slice_control.multi_slice = temp_ctrl->val;
+		multi_slice_control.slice_size = ctrl->val;
+		pdata = &multi_slice_control;
+		break;
+	case V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE: {
+		bool codec_avc =
+			inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264 ||
+			inst->fmts[CAPTURE_PORT].fourcc ==
+							V4L2_PIX_FMT_H264_NO_SC;
+
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDEO_MULTI_SLICE_MODE);
+		if (codec_avc && temp_ctrl->val ==
+				V4L2_MPEG_VIDEO_MULTI_SICE_MODE_MAX_MB) {
+			property_id = HAL_PARAM_VENC_SLICE_DELIVERY_MODE;
+			enable.enable = true;
+		} else {
+			dprintk(VIDC_WARN,
+				"Failed : slice delivery mode is not valid\n");
+			enable.enable = false;
+		}
+		pdata = &enable;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE: {
+		struct v4l2_ctrl *air_mbs, *air_ref, *cir_mbs;
+		bool is_cont_intra_supported = false;
+
+		air_mbs = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_IR_MBS);
+
+		is_cont_intra_supported =
+		(inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_H264) ||
+		(inst->fmts[CAPTURE_PORT].fourcc == V4L2_PIX_FMT_HEVC);
+
+		if (is_cont_intra_supported) {
+			if (ctrl->val != HAL_INTRA_REFRESH_NONE)
+				enable.enable = true;
+			else
+				enable.enable = false;
+
+			rc = call_hfi_op(hdev, session_set_property,
+				(void *)inst->session,
+				HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED, &enable);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed to set constrained intra\n");
+				rc = -EINVAL;
+				break;
+			}
+		}
+
+		property_id = HAL_PARAM_VENC_INTRA_REFRESH;
+
+		intra_refresh.mode = ctrl->val;
+		intra_refresh.air_mbs = air_mbs->val;
+		intra_refresh.air_ref = air_ref->val;
+		intra_refresh.cir_mbs = cir_mbs->val;
+
+		pdata = &intra_refresh;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_IR_MBS: {
+		struct v4l2_ctrl *ir_mode, *air_ref, *cir_mbs;
+
+		ir_mode = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE);
+
+		property_id = HAL_PARAM_VENC_INTRA_REFRESH;
+
+		intra_refresh.air_mbs = ctrl->val;
+		intra_refresh.mode = ir_mode->val;
+		intra_refresh.air_ref = air_ref->val;
+		intra_refresh.cir_mbs = cir_mbs->val;
+
+		pdata = &intra_refresh;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE:
+	{
+		struct v4l2_ctrl *alpha, *beta;
+
+		alpha = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA);
+		beta = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA);
+
+		property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		h264_db_control.slice_alpha_offset = alpha->val;
+		h264_db_control.slice_beta_offset = beta->val;
+		h264_db_control.mode = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+				ctrl->val);
+		pdata = &h264_db_control;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA:
+	{
+		struct v4l2_ctrl *mode, *beta;
+
+		mode = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE);
+		beta = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA);
+
+		property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		h264_db_control.slice_alpha_offset = ctrl->val;
+		h264_db_control.slice_beta_offset = beta->val;
+		h264_db_control.mode = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+				mode->val);
+		pdata = &h264_db_control;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA:
+	{
+		struct v4l2_ctrl *mode, *alpha;
+
+		mode = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE);
+		alpha = TRY_GET_CTRL(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA);
+		property_id = HAL_PARAM_VENC_H264_DEBLOCK_CONTROL;
+		h264_db_control.slice_alpha_offset = alpha->val;
+		h264_db_control.slice_beta_offset = ctrl->val;
+		h264_db_control.mode = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_MODE,
+				mode->val);
+		pdata = &h264_db_control;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDEO_HEADER_MODE:
+		property_id = HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER;
+
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDEO_HEADER_MODE_SEPARATE:
+			enable.enable = 0;
+			break;
+		case V4L2_MPEG_VIDEO_HEADER_MODE_JOINED_WITH_1ST_FRAME:
+		default:
+			rc = -ENOTSUPP;
+			break;
+		}
+		pdata = &enable;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_SECURE:
+		inst->flags |= VIDC_SECURE;
+		dprintk(VIDC_INFO, "Setting secure mode to: %d\n",
+				!!(inst->flags & VIDC_SECURE));
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA:
+		property_id = HAL_PARAM_INDEX_EXTRADATA;
+		extra.index = msm_comm_get_hal_extradata_index(ctrl->val);
+		extra.enable = 1;
+		pdata = &extra;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER:
+		property_id = HAL_PARAM_VENC_H264_GENERATE_AUDNAL;
+
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED:
+			enable.enable = 0;
+			break;
+		case V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED:
+			enable.enable = 1;
+			break;
+		default:
+			rc = -ENOTSUPP;
+			break;
+		}
+
+		pdata = &enable;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY:
+		property_id = HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY;
+		preserve_text_quality.enable = ctrl->val;
+		pdata = &preserve_text_quality;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME:
+		property_id = HAL_CONFIG_VENC_USELTRFRAME;
+		use_ltr.ref_ltr = ctrl->val;
+		use_ltr.use_constraint = false;
+		use_ltr.frames = 0;
+		pdata = &use_ltr;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME:
+		property_id = HAL_CONFIG_VENC_MARKLTRFRAME;
+		mark_ltr.mark_frame = ctrl->val;
+		pdata = &mark_ltr;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS:
+		property_id = HAL_CONFIG_VENC_HIER_P_NUM_FRAMES;
+		hier_p_layers = ctrl->val;
+		if (hier_p_layers > inst->capability.hier_p.max) {
+			dprintk(VIDC_ERR,
+				"Error setting hier p num layers %d max supported is %d\n",
+				hier_p_layers, inst->capability.hier_p.max);
+			rc = -ENOTSUPP;
+			break;
+		}
+		pdata = &hier_p_layers;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE:
+		property_id = HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE;
+		enable.enable = ctrl->val;
+		pdata = &enable;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS:
+		if (inst->fmts[CAPTURE_PORT].fourcc != V4L2_PIX_FMT_HEVC) {
+			dprintk(VIDC_ERR, "Hier B supported for HEVC only\n");
+			rc = -ENOTSUPP;
+			break;
+		}
+		property_id = HAL_PARAM_VENC_HIER_B_MAX_ENH_LAYERS;
+		hier_b_layers = ctrl->val;
+		pdata = &hier_b_layers;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE:
+		property_id = HAL_PARAM_VENC_HIER_P_HYBRID_MODE;
+		hyb_hierp.layers = ctrl->val;
+		pdata = &hyb_hierp;
+		break;
+	case V4L2_CID_VIDC_QBUF_MODE:
+		property_id = HAL_PARAM_SYNC_BASED_INTERRUPT;
+		enable.enable = ctrl->val == V4L2_VIDC_QBUF_BATCHED;
+		pdata = &enable;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS:
+		property_id = HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS;
+		max_hierp_layers = ctrl->val;
+		if (max_hierp_layers > inst->capability.hier_p.max) {
+			dprintk(VIDC_ERR,
+				"Error max HP layers(%d)>max supported(%d)\n",
+				max_hierp_layers, inst->capability.hier_p.max);
+			rc = -ENOTSUPP;
+			break;
+		}
+		pdata = &max_hierp_layers;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID:
+		property_id = HAL_CONFIG_VENC_BASELAYER_PRIORITYID;
+		baselayerid = ctrl->val;
+		pdata = &baselayerid;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI:
+		property_id = HAL_PARAM_VENC_VQZIP_SEI;
+		enable.enable = ctrl->val;
+		pdata = &enable;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY:
+		property_id = HAL_CONFIG_REALTIME;
+		/* firmware has inverted values for realtime and
+		 * non-realtime priority
+		 */
+		enable.enable = !(ctrl->val);
+		pdata = &enable;
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE:
+			inst->flags &= ~VIDC_REALTIME;
+			break;
+		case V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE:
+			inst->flags |= VIDC_REALTIME;
+			break;
+		default:
+			dprintk(VIDC_WARN,
+				"inst(%pK) invalid priority ctrl value %#x\n",
+				inst, ctrl->val);
+			break;
+		}
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE:
+		dprintk(VIDC_DBG,
+			"inst(%pK) operating rate changed from %d to %d\n",
+			inst, inst->operating_rate >> 16, ctrl->val >> 16);
+		inst->operating_rate = ctrl->val;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE:
+	{
+		property_id = HAL_PARAM_VENC_BITRATE_TYPE;
+		enable.enable = ctrl->val;
+		pdata = &enable;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE:
+	{
+		signal_info.color_space = ctrl->val;
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE);
+		signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS);
+		signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS);
+		signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0;
+		property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+		pdata = &signal_info;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE:
+	{
+		signal_info.full_range = ctrl->val;
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE);
+		signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS);
+		signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS);
+		signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0;
+		property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+		pdata = &signal_info;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS:
+	{
+		signal_info.transfer_chars = ctrl->val;
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE);
+		signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE);
+		signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS);
+		signal_info.matrix_coeffs = temp_ctrl ? temp_ctrl->val : 0;
+		property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+		pdata = &signal_info;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS:
+	{
+		signal_info.matrix_coeffs = ctrl->val;
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE);
+		signal_info.full_range = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl =
+			TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS);
+		signal_info.transfer_chars = temp_ctrl ? temp_ctrl->val : 0;
+		temp_ctrl = TRY_GET_CTRL(V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE);
+		signal_info.color_space = temp_ctrl ? temp_ctrl->val : 0;
+		property_id = HAL_PARAM_VENC_VIDEO_SIGNAL_INFO;
+		pdata = &signal_info;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC:
+		if (ctrl->val == V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE) {
+			rc = msm_venc_set_csc(inst);
+			if (rc)
+				dprintk(VIDC_ERR, "fail to set csc: %d\n", rc);
+		}
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE:
+	{
+		property_id = HAL_PARAM_VENC_LOW_LATENCY;
+		if (ctrl->val ==
+			V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE)
+			enable.enable = 1;
+		else
+			enable.enable = 0;
+		pdata = &enable;
+		break;
+	}
+	case V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8:
+		property_id = HAL_PARAM_VENC_H264_TRANSFORM_8x8;
+		switch (ctrl->val) {
+		case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE:
+			enable.enable = 1;
+			break;
+		case V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE:
+			enable.enable = 0;
+			break;
+		default:
+			dprintk(VIDC_ERR,
+				"Invalid H264 8x8 transform control value %d\n",
+				ctrl->val);
+			rc = -ENOTSUPP;
+			break;
+		}
+		pdata = &enable;
+		break;
+	case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE:
+		property_id = HAL_PARAM_VENC_IFRAMESIZE_TYPE;
+		iframesize_type = venc_v4l2_to_hal(
+				V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE,
+				ctrl->val);
+		pdata = &iframesize_type;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Unsupported index: %x\n", ctrl->id);
+		rc = -ENOTSUPP;
+		break;
+	}
+
+	v4l2_ctrl_lock(ctrl);
+#undef TRY_GET_CTRL
+
+	if (!rc && property_id) {
+		dprintk(VIDC_DBG, "Control: HAL property=%x,ctrl_value=%d\n",
+				property_id,
+				ctrl->val);
+		rc = call_hfi_op(hdev, session_set_property,
+				(void *)inst->session, property_id, pdata);
+	}
+
+	return rc;
+}
+
+static int try_set_ext_ctrl(struct msm_vidc_inst *inst,
+	struct v4l2_ext_controls *ctrl)
+{
+	int rc = 0, i;
+	struct v4l2_ext_control *control;
+	struct hfi_device *hdev;
+	struct hal_ltr_mode ltr_mode;
+	struct hal_vc1e_perf_cfg_type search_range = { {0} };
+	u32 property_id = 0;
+	void *pdata = NULL;
+	struct msm_vidc_capability *cap = NULL;
+	struct hal_aspect_ratio sar;
+	struct hal_bitrate bitrate;
+	struct hal_frame_size blur_res;
+
+	if (!inst || !inst->core || !inst->core->device || !ctrl) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+	cap = &inst->capability;
+
+	control = ctrl->controls;
+	for (i = 0; i < ctrl->count; i++) {
+		switch (control[i].id) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE:
+			if (control[i].value !=
+				V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE) {
+				rc = msm_venc_toggle_hier_p(inst, false);
+				if (rc)
+					break;
+			}
+			ltr_mode.mode = control[i].value;
+			ltr_mode.trust_mode = 1;
+			property_id = HAL_PARAM_VENC_LTRMODE;
+			pdata = &ltr_mode;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT:
+			ltr_mode.count =  control[i].value;
+			if (ltr_mode.count > cap->ltr_count.max) {
+				dprintk(VIDC_ERR,
+					"Invalid LTR count %d. Supported max: %d\n",
+					ltr_mode.count,
+					cap->ltr_count.max);
+				/*
+				 * FIXME: Return an error (-EINVALID)
+				 * here once VP8 supports LTR count
+				 * capability
+				 */
+				ltr_mode.count = 1;
+			}
+			ltr_mode.trust_mode = 1;
+			property_id = HAL_PARAM_VENC_LTRMODE;
+			pdata = &ltr_mode;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE:
+			search_range.i_frame.x_subsampled = control[i].value;
+			property_id = HAL_PARAM_VENC_SEARCH_RANGE;
+			pdata = &search_range;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE:
+			search_range.i_frame.y_subsampled = control[i].value;
+			property_id = HAL_PARAM_VENC_SEARCH_RANGE;
+			pdata = &search_range;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE:
+			search_range.p_frame.x_subsampled = control[i].value;
+			property_id = HAL_PARAM_VENC_SEARCH_RANGE;
+			pdata = &search_range;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE:
+			search_range.p_frame.y_subsampled = control[i].value;
+			property_id = HAL_PARAM_VENC_SEARCH_RANGE;
+			pdata = &search_range;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE:
+			search_range.b_frame.x_subsampled = control[i].value;
+			property_id = HAL_PARAM_VENC_SEARCH_RANGE;
+			pdata = &search_range;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE:
+			search_range.b_frame.y_subsampled = control[i].value;
+			property_id = HAL_PARAM_VENC_SEARCH_RANGE;
+			pdata = &search_range;
+			break;
+		case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH:
+			sar.aspect_width = control[i].value;
+			property_id = HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO;
+			pdata = &sar;
+			break;
+		case V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT:
+			sar.aspect_height = control[i].value;
+			property_id = HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO;
+			pdata = &sar;
+			break;
+		case V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE:
+		{
+			if (control[i].value) {
+				bitrate.layer_id = i;
+				bitrate.bit_rate = control[i].value;
+				property_id = HAL_CONFIG_VENC_TARGET_BITRATE;
+				pdata = &bitrate;
+				dprintk(VIDC_DBG, "bitrate for layer(%d)=%d\n",
+					i, bitrate.bit_rate);
+				rc = call_hfi_op(hdev, session_set_property,
+					(void *)inst->session, property_id,
+					 pdata);
+				if (rc) {
+					dprintk(VIDC_DBG, "prop %x failed\n",
+						property_id);
+					return rc;
+				}
+				if (i == MAX_HYBRID_HIER_P_LAYERS - 1) {
+					dprintk(VIDC_DBG, "HAL property=%x\n",
+						property_id);
+					property_id = 0;
+					rc = 0;
+				}
+			}
+			break;
+		}
+		case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH:
+			property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION;
+			blur_res.width = control[i].value;
+			blur_res.buffer_type = HAL_BUFFER_INPUT;
+			property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION;
+			pdata = &blur_res;
+			break;
+		case V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT:
+			blur_res.height = control[i].value;
+			blur_res.buffer_type = HAL_BUFFER_INPUT;
+			property_id = HAL_CONFIG_VENC_BLUR_RESOLUTION;
+			pdata = &blur_res;
+			break;
+		default:
+			dprintk(VIDC_ERR, "Invalid id set: %d\n",
+				control[i].id);
+			rc = -ENOTSUPP;
+			break;
+		}
+		if (rc)
+			break;
+	}
+
+	if (!rc && property_id) {
+		dprintk(VIDC_DBG, "Control: HAL property=%x\n", property_id);
+		rc = call_hfi_op(hdev, session_set_property,
+				(void *)inst->session, property_id, pdata);
+	}
+	return rc;
+}
+
+static int msm_venc_op_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+
+	int rc = 0, c = 0;
+
+	struct msm_vidc_inst *inst = container_of(ctrl->handler,
+					struct msm_vidc_inst, ctrl_handler);
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to start done state\n", inst);
+		goto failed_open_done;
+	}
+
+	for (c = 0; c < ctrl->ncontrols; ++c) {
+		if (ctrl->cluster[c]->is_new) {
+			struct v4l2_ctrl *temp = ctrl->cluster[c];
+
+			rc = try_set_ctrl(inst, temp);
+			if (rc) {
+				dprintk(VIDC_ERR, "Failed setting %s (%x)\n",
+						v4l2_ctrl_get_name(temp->id),
+						temp->id);
+				break;
+			}
+		}
+	}
+failed_open_done:
+	if (rc)
+		dprintk(VIDC_ERR, "Failed setting control: %x (%s)",
+				ctrl->id, v4l2_ctrl_get_name(ctrl->id));
+	return rc;
+}
+
+static int msm_venc_op_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
+{
+	return 0;
+}
+
+static const struct v4l2_ctrl_ops msm_venc_ctrl_ops = {
+
+	.s_ctrl = msm_venc_op_s_ctrl,
+	.g_volatile_ctrl = msm_venc_op_g_volatile_ctrl,
+};
+
+const struct v4l2_ctrl_ops *msm_venc_get_ctrl_ops(void)
+{
+	return &msm_venc_ctrl_ops;
+}
+
+int msm_venc_inst_init(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid input = %pK\n", inst);
+		return -EINVAL;
+	}
+	inst->prop.height[CAPTURE_PORT] = DEFAULT_HEIGHT;
+	inst->prop.width[CAPTURE_PORT] = DEFAULT_WIDTH;
+	inst->prop.height[OUTPUT_PORT] = DEFAULT_HEIGHT;
+	inst->prop.width[OUTPUT_PORT] = DEFAULT_WIDTH;
+	inst->capability.height.min = MIN_SUPPORTED_HEIGHT;
+	inst->capability.height.max = DEFAULT_HEIGHT;
+	inst->capability.width.min = MIN_SUPPORTED_WIDTH;
+	inst->capability.width.max = DEFAULT_WIDTH;
+	inst->capability.alloc_mode_in = HAL_BUFFER_MODE_STATIC;
+	inst->capability.alloc_mode_out = HAL_BUFFER_MODE_STATIC;
+	inst->capability.secure_output2_threshold.min = 0;
+	inst->capability.secure_output2_threshold.max = 0;
+	inst->buffer_mode_set[OUTPUT_PORT] = HAL_BUFFER_MODE_STATIC;
+	inst->buffer_mode_set[CAPTURE_PORT] = HAL_BUFFER_MODE_STATIC;
+	inst->prop.fps = DEFAULT_FPS;
+	inst->capability.pixelprocess_capabilities = 0;
+	inst->operating_rate = 0;
+	memcpy(&inst->fmts[CAPTURE_PORT], &venc_formats[4],
+			sizeof(struct msm_vidc_format));
+	memcpy(&inst->fmts[OUTPUT_PORT], &venc_formats[0],
+			sizeof(struct msm_vidc_format));
+	return rc;
+}
+
+int msm_venc_s_ext_ctrl(struct msm_vidc_inst *inst,
+	struct v4l2_ext_controls *ctrl)
+{
+	int rc = 0;
+
+	rc = try_set_ext_ctrl(inst, ctrl);
+	if (rc) {
+		dprintk(VIDC_ERR, "Error setting extended control\n");
+		return rc;
+	}
+	return rc;
+}
+
+int msm_venc_querycap(struct msm_vidc_inst *inst, struct v4l2_capability *cap)
+{
+	if (!inst || !cap) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, cap = %pK\n", inst, cap);
+		return -EINVAL;
+	}
+	strlcpy(cap->driver, MSM_VIDC_DRV_NAME, sizeof(cap->driver));
+	strlcpy(cap->card, MSM_VENC_DVC_NAME, sizeof(cap->card));
+	cap->bus_info[0] = 0;
+	cap->version = MSM_VIDC_VERSION;
+	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE_MPLANE |
+						V4L2_CAP_VIDEO_OUTPUT_MPLANE |
+						V4L2_CAP_STREAMING;
+	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
+	memset(cap->reserved, 0, sizeof(cap->reserved));
+	return 0;
+}
+
+int msm_venc_enum_fmt(struct msm_vidc_inst *inst, struct v4l2_fmtdesc *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+
+	if (!inst || !f) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, f = %pK\n", inst, f);
+		return -EINVAL;
+	}
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(venc_formats,
+			ARRAY_SIZE(venc_formats), f->index, CAPTURE_PORT);
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_index(venc_formats,
+			ARRAY_SIZE(venc_formats), f->index, OUTPUT_PORT);
+		f->flags = V4L2_FMT_FLAG_COMPRESSED;
+	}
+
+	memset(f->reserved, 0, sizeof(f->reserved));
+	if (fmt) {
+		strlcpy(f->description, fmt->description,
+				sizeof(f->description));
+		f->pixelformat = fmt->fourcc;
+	} else {
+		dprintk(VIDC_DBG, "No more formats found\n");
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static int msm_venc_set_csc(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	int count = 0;
+	struct hal_vpe_color_space_conversion vpe_csc;
+
+	while (count < HAL_MAX_MATRIX_COEFFS) {
+		if (count < HAL_MAX_BIAS_COEFFS)
+			vpe_csc.csc_bias[count] =
+				vpe_csc_601_to_709_bias_coeff[count];
+		if (count < HAL_MAX_LIMIT_COEFFS)
+			vpe_csc.csc_limit[count] =
+				vpe_csc_601_to_709_limit_coeff[count];
+		vpe_csc.csc_matrix[count] =
+			vpe_csc_601_to_709_matrix_coeff[count];
+		count = count + 1;
+	}
+	rc = msm_comm_try_set_prop(inst,
+			HAL_PARAM_VPE_COLOR_SPACE_CONVERSION, &vpe_csc);
+	if (rc)
+		dprintk(VIDC_ERR, "Setting VPE coefficients failed\n");
+
+	return rc;
+}
+
+int msm_venc_s_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	int i;
+	struct hfi_device *hdev;
+
+	if (!inst || !f) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, format = %pK\n", inst, f);
+		return -EINVAL;
+	}
+
+	if (!inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
+			ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
+			CAPTURE_PORT);
+		if (!fmt || fmt->type != CAPTURE_PORT) {
+			dprintk(VIDC_ERR,
+				"Format: %d not supported on CAPTURE port\n",
+				f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto exit;
+		}
+
+		memcpy(&inst->fmts[fmt->type], fmt,
+				sizeof(struct msm_vidc_format));
+
+		msm_venc_update_plane_count(inst, CAPTURE_PORT);
+		fmt->num_planes = inst->fmts[CAPTURE_PORT].num_planes;
+
+		rc = msm_comm_try_state(inst, MSM_VIDC_OPEN_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to open instance\n");
+			goto exit;
+		}
+
+		inst->prop.width[CAPTURE_PORT] = f->fmt.pix_mp.width;
+		inst->prop.height[CAPTURE_PORT] = f->fmt.pix_mp.height;
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto exit;
+		}
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		struct hal_frame_size frame_sz;
+
+		inst->prop.width[OUTPUT_PORT] = f->fmt.pix_mp.width;
+		inst->prop.height[OUTPUT_PORT] = f->fmt.pix_mp.height;
+
+		rc = msm_vidc_check_session_supported(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: session not supported\n", __func__);
+			goto exit;
+		}
+
+		frame_sz.buffer_type = HAL_BUFFER_INPUT;
+		frame_sz.width = inst->prop.width[OUTPUT_PORT];
+		frame_sz.height = inst->prop.height[OUTPUT_PORT];
+		dprintk(VIDC_DBG, "width = %d, height = %d\n",
+				frame_sz.width, frame_sz.height);
+		rc = call_hfi_op(hdev, session_set_property, (void *)
+			inst->session, HAL_PARAM_FRAME_SIZE, &frame_sz);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set framesize for Output port\n");
+			goto exit;
+		}
+
+		fmt = msm_comm_get_pixel_fmt_fourcc(venc_formats,
+			ARRAY_SIZE(venc_formats), f->fmt.pix_mp.pixelformat,
+			OUTPUT_PORT);
+		if (!fmt || fmt->type != OUTPUT_PORT) {
+			dprintk(VIDC_ERR,
+				"Format: %d not supported on OUTPUT port\n",
+				f->fmt.pix_mp.pixelformat);
+			rc = -EINVAL;
+			goto exit;
+		}
+		memcpy(&inst->fmts[fmt->type], fmt,
+				sizeof(struct msm_vidc_format));
+
+		msm_venc_update_plane_count(inst, OUTPUT_PORT);
+		fmt->num_planes = inst->fmts[OUTPUT_PORT].num_planes;
+
+		msm_comm_set_color_format(inst, HAL_BUFFER_INPUT, fmt->fourcc);
+	} else {
+		dprintk(VIDC_ERR, "%s - Unsupported buf type: %d\n",
+			__func__, f->type);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	f->fmt.pix_mp.num_planes = fmt->num_planes;
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		struct hal_frame_size frame_sz = {0};
+		struct hal_buffer_requirements *bufreq = NULL;
+
+		frame_sz.width = inst->prop.width[CAPTURE_PORT];
+		frame_sz.height = inst->prop.height[CAPTURE_PORT];
+		frame_sz.buffer_type = HAL_BUFFER_OUTPUT;
+		rc = call_hfi_op(hdev, session_set_property, (void *)
+				inst->session, HAL_PARAM_FRAME_SIZE,
+				&frame_sz);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"Failed to set OUTPUT framesize\n");
+			goto exit;
+		}
+		rc = msm_comm_try_get_bufreqs(inst);
+		if (rc) {
+			dprintk(VIDC_WARN,
+				"%s : Getting buffer reqs failed: %d\n",
+					__func__, rc);
+			goto exit;
+		}
+		bufreq = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+		f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			bufreq ? bufreq->buffer_size : 0;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		struct hal_buffer_requirements *bufreq = NULL;
+		int extra_idx = 0;
+
+		for (i = 0; i < inst->fmts[fmt->type].num_planes; ++i) {
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+				inst->fmts[fmt->type].get_frame_size(i,
+				f->fmt.pix_mp.height, f->fmt.pix_mp.width);
+		}
+		extra_idx = EXTRADATA_IDX(inst->fmts[fmt->type].num_planes);
+		if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+			bufreq = get_buff_req_buffer(inst,
+					HAL_BUFFER_EXTRADATA_INPUT);
+			f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+				bufreq ? bufreq->buffer_size : 0;
+		}
+	}
+exit:
+	return rc;
+}
+
+int msm_venc_g_fmt(struct msm_vidc_inst *inst, struct v4l2_format *f)
+{
+	const struct msm_vidc_format *fmt = NULL;
+	int rc = 0;
+	int i;
+	u32 height, width, num_planes;
+	unsigned int extra_idx = 0;
+	struct hal_buffer_requirements *bufreq = NULL;
+
+	if (!inst || !f) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, format = %pK\n", inst, f);
+		return -EINVAL;
+	}
+
+	rc = msm_comm_try_get_bufreqs(inst);
+	if (rc) {
+		dprintk(VIDC_WARN, "Getting buffer requirements failed: %d\n",
+				rc);
+		return rc;
+	}
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		fmt = &inst->fmts[CAPTURE_PORT];
+		height = inst->prop.height[CAPTURE_PORT];
+		width = inst->prop.width[CAPTURE_PORT];
+		msm_venc_update_plane_count(inst, CAPTURE_PORT);
+		num_planes = inst->fmts[CAPTURE_PORT].num_planes;
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		fmt = &inst->fmts[OUTPUT_PORT];
+		height = inst->prop.height[OUTPUT_PORT];
+		width = inst->prop.width[OUTPUT_PORT];
+		msm_venc_update_plane_count(inst, OUTPUT_PORT);
+		num_planes = inst->fmts[OUTPUT_PORT].num_planes;
+	} else {
+		dprintk(VIDC_ERR, "Invalid type: %x\n", f->type);
+		return -ENOTSUPP;
+	}
+
+	f->fmt.pix_mp.pixelformat = fmt->fourcc;
+	f->fmt.pix_mp.height = height;
+	f->fmt.pix_mp.width = width;
+	f->fmt.pix_mp.num_planes = num_planes;
+
+	if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		for (i = 0; i < num_planes; ++i) {
+			f->fmt.pix_mp.plane_fmt[i].sizeimage =
+				fmt->get_frame_size(i, height, width);
+		}
+	} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		bufreq = get_buff_req_buffer(inst,
+				HAL_BUFFER_OUTPUT);
+
+		f->fmt.pix_mp.plane_fmt[0].sizeimage =
+			bufreq ? bufreq->buffer_size : 0;
+	}
+	extra_idx = EXTRADATA_IDX(num_planes);
+	if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+		if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+			bufreq = get_buff_req_buffer(inst,
+						HAL_BUFFER_EXTRADATA_OUTPUT);
+		else if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+			bufreq = get_buff_req_buffer(inst,
+						HAL_BUFFER_EXTRADATA_INPUT);
+
+		f->fmt.pix_mp.plane_fmt[extra_idx].sizeimage =
+			bufreq ? bufreq->buffer_size : 0;
+	}
+
+	for (i = 0; i < num_planes; ++i) {
+		if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+			inst->bufq[OUTPUT_PORT].plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
+		} else if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			inst->bufq[CAPTURE_PORT].plane_sizes[i] =
+				f->fmt.pix_mp.plane_fmt[i].sizeimage;
+		}
+	}
+
+	return rc;
+}
+
+int msm_venc_reqbufs(struct msm_vidc_inst *inst, struct v4l2_requestbuffers *b)
+{
+	struct buf_queue *q = NULL;
+	int rc = 0;
+
+	if (!inst || !b) {
+		dprintk(VIDC_ERR,
+			"Invalid input, inst = %pK, buffer = %pK\n", inst, b);
+		return -EINVAL;
+	}
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		dprintk(VIDC_ERR,
+		"Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+
+	mutex_lock(&q->lock);
+	rc = vb2_reqbufs(&q->vb2_bufq, b);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_DBG, "Failed to get reqbufs, %d\n", rc);
+	return rc;
+}
+
+int msm_venc_prepare_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int rc = 0;
+	int i;
+	struct vidc_buffer_addr_info buffer_info = {0};
+	struct hfi_device *hdev;
+	int extra_idx = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			inst->core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+			"Core %pK in bad state, ignoring prepare buf\n",
+				inst->core);
+		goto exit;
+	}
+
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		if (b->length != inst->fmts[CAPTURE_PORT].num_planes) {
+			dprintk(VIDC_ERR,
+				"Planes mismatch: needed: %d, allocated: %d\n",
+				inst->fmts[CAPTURE_PORT].num_planes,
+				b->length);
+			rc = -EINVAL;
+			break;
+		}
+
+		for (i = 0; i < min_t(int, b->length, VIDEO_MAX_PLANES); i++) {
+			dprintk(VIDC_DBG, "device_addr = %#lx, size = %d\n",
+				b->m.planes[i].m.userptr,
+				b->m.planes[i].length);
+		}
+		buffer_info.buffer_size = b->m.planes[0].length;
+		buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr =
+			b->m.planes[0].m.userptr;
+
+		extra_idx = EXTRADATA_IDX(b->length);
+		if (extra_idx && (extra_idx < VIDEO_MAX_PLANES)) {
+			buffer_info.extradata_addr =
+				b->m.planes[extra_idx].m.userptr;
+			dprintk(VIDC_DBG, "extradata: %#lx\n",
+					b->m.planes[extra_idx].m.userptr);
+			buffer_info.extradata_size =
+				b->m.planes[extra_idx].length;
+		}
+
+		rc = call_hfi_op(hdev, session_set_buffers,
+				(void *)inst->session, &buffer_info);
+		if (rc)
+			dprintk(VIDC_ERR,
+					"vidc_hal_session_set_buffers failed\n");
+		break;
+	default:
+		dprintk(VIDC_ERR,
+			"Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+exit:
+	return rc;
+}
+
+int msm_venc_release_buf(struct msm_vidc_inst *inst,
+					struct v4l2_buffer *b)
+{
+	int i, rc = 0, extra_idx = 0;
+	struct vidc_buffer_addr_info buffer_info = {0};
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move inst: %pK to release res done state\n",
+			inst);
+		goto exit;
+	}
+	switch (b->type) {
+	case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+		break;
+	case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: {
+		if (b->length !=
+			inst->fmts[CAPTURE_PORT].num_planes) {
+			dprintk(VIDC_ERR,
+					"Planes mismatch: needed: %d, to release: %d\n",
+					inst->fmts[CAPTURE_PORT].num_planes,
+					b->length);
+			rc = -EINVAL;
+			break;
+		}
+		for (i = 0; i < b->length; i++) {
+			dprintk(VIDC_DBG,
+				"Release device_addr = %#lx, size = %d, %d\n",
+				b->m.planes[i].m.userptr,
+				b->m.planes[i].length, inst->state);
+		}
+		buffer_info.buffer_size = b->m.planes[0].length;
+		buffer_info.buffer_type = HAL_BUFFER_OUTPUT;
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr =
+			b->m.planes[0].m.userptr;
+		extra_idx = EXTRADATA_IDX(b->length);
+		if (extra_idx && (extra_idx < VIDEO_MAX_PLANES))
+			buffer_info.extradata_addr =
+			b->m.planes[extra_idx].m.userptr;
+		buffer_info.response_required = false;
+		rc = call_hfi_op(hdev, session_release_buffers,
+				(void *)inst->session, &buffer_info);
+		if (rc)
+			dprintk(VIDC_ERR,
+					"vidc_hal_session_release_buffers failed\n");
+		}
+		break;
+	default:
+		dprintk(VIDC_ERR, "Buffer type not recognized: %d\n", b->type);
+		break;
+	}
+exit:
+	return rc;
+}
+
+int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct buf_queue *q = NULL;
+	int rc = 0;
+
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+	mutex_lock(&q->lock);
+	rc = vb2_qbuf(&q->vb2_bufq, b);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to qbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct buf_queue *q = NULL;
+	int rc = 0;
+
+	q = msm_comm_get_vb2q(inst, b->type);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"Failed to find buffer queue for type = %d\n", b->type);
+		return -EINVAL;
+	}
+	mutex_lock(&q->lock);
+	rc = vb2_dqbuf(&q->vb2_bufq, b, true);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_DBG, "Failed to dqbuf, %d\n", rc);
+	return rc;
+}
+
+int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct buf_queue *q;
+
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG, "Calling streamon\n");
+	mutex_lock(&q->lock);
+	rc = vb2_streamon(&q->vb2_bufq, i);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_ERR, "streamon failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i)
+{
+	int rc = 0;
+	struct buf_queue *q;
+
+	q = msm_comm_get_vb2q(inst, i);
+	if (!q) {
+		dprintk(VIDC_ERR,
+			"Failed to find buffer queue for type = %d\n", i);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG, "Calling streamoff on port: %d\n", i);
+	mutex_lock(&q->lock);
+	rc = vb2_streamoff(&q->vb2_bufq, i);
+	mutex_unlock(&q->lock);
+	if (rc)
+		dprintk(VIDC_ERR, "streamoff failed on port: %d\n", i);
+	return rc;
+}
+
+int msm_venc_ctrl_init(struct msm_vidc_inst *inst)
+{
+	return msm_comm_ctrl_init(inst, msm_venc_ctrls,
+			ARRAY_SIZE(msm_venc_ctrls), &msm_venc_ctrl_ops);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_venc.h b/drivers/media/platform/msm/vidc/msm_venc.h
new file mode 100644
index 0000000..5a40fac
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_venc.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _MSM_VENC_H_
+#define _MSM_VENC_H_
+
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+
+int msm_venc_inst_init(struct msm_vidc_inst *inst);
+int msm_venc_ctrl_init(struct msm_vidc_inst *inst);
+int msm_venc_querycap(void *instance, struct v4l2_capability *cap);
+int msm_venc_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_venc_s_fmt(void *instance, struct v4l2_format *f);
+int msm_venc_g_fmt(void *instance, struct v4l2_format *f);
+int msm_venc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
+int msm_venc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
+int msm_venc_prepare_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_release_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_qbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_dqbuf(struct msm_vidc_inst *inst, struct v4l2_buffer *b);
+int msm_venc_streamon(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+int msm_venc_streamoff(struct msm_vidc_inst *inst, enum v4l2_buf_type i);
+int msm_venc_cmd(struct msm_vidc_inst *inst, struct v4l2_encoder_cmd *enc);
+int msm_venc_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
+struct vb2_ops *msm_venc_get_vb2q_ops(void);
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc.c b/drivers/media/platform/msm/vidc/msm_vidc.c
new file mode 100644
index 0000000..188f86f
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc.c
@@ -0,0 +1,1418 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/dma-direction.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_internal.h"
+#include "msm_vidc_debug.h"
+#include "msm_vdec.h"
+#include "msm_venc.h"
+#include "msm_vidc_common.h"
+#include <linux/delay.h>
+#include "vidc_hfi_api.h"
+#include "msm_vidc_clocks.h"
+
+#define MAX_EVENTS 30
+
+static int get_poll_flags(void *instance)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct vb2_queue *outq = &inst->bufq[OUTPUT_PORT].vb2_bufq;
+	struct vb2_queue *capq = &inst->bufq[CAPTURE_PORT].vb2_bufq;
+	struct vb2_buffer *out_vb = NULL;
+	struct vb2_buffer *cap_vb = NULL;
+	unsigned long flags;
+	int rc = 0;
+
+	if (v4l2_event_pending(&inst->event_handler))
+		rc |= POLLPRI;
+
+	spin_lock_irqsave(&capq->done_lock, flags);
+	if (!list_empty(&capq->done_list))
+		cap_vb = list_first_entry(&capq->done_list, struct vb2_buffer,
+								done_entry);
+	if (cap_vb && (cap_vb->state == VB2_BUF_STATE_DONE
+				|| cap_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLIN | POLLRDNORM;
+	spin_unlock_irqrestore(&capq->done_lock, flags);
+
+	spin_lock_irqsave(&outq->done_lock, flags);
+	if (!list_empty(&outq->done_list))
+		out_vb = list_first_entry(&outq->done_list, struct vb2_buffer,
+								done_entry);
+	if (out_vb && (out_vb->state == VB2_BUF_STATE_DONE
+				|| out_vb->state == VB2_BUF_STATE_ERROR))
+		rc |= POLLOUT | POLLWRNORM;
+	spin_unlock_irqrestore(&outq->done_lock, flags);
+
+	return rc;
+}
+
+int msm_vidc_poll(void *instance, struct file *filp,
+		struct poll_table_struct *wait)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct vb2_queue *outq = NULL;
+	struct vb2_queue *capq = NULL;
+
+	if (!inst)
+		return -EINVAL;
+
+	outq = &inst->bufq[OUTPUT_PORT].vb2_bufq;
+	capq = &inst->bufq[CAPTURE_PORT].vb2_bufq;
+
+	poll_wait(filp, &inst->event_handler.wait, wait);
+	poll_wait(filp, &capq->done_wq, wait);
+	poll_wait(filp, &outq->done_wq, wait);
+	return get_poll_flags(inst);
+}
+EXPORT_SYMBOL(msm_vidc_poll);
+
+int msm_vidc_querycap(void *instance, struct v4l2_capability *cap)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !cap)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_querycap(instance, cap);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_querycap(instance, cap);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_querycap);
+
+int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !f)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_enum_fmt(instance, f);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_enum_fmt(instance, f);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_enum_fmt);
+
+int msm_vidc_s_fmt(void *instance, struct v4l2_format *f)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !f)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_s_fmt(instance, f);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_s_fmt(instance, f);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_s_fmt);
+
+int msm_vidc_g_fmt(void *instance, struct v4l2_format *f)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !f)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_g_fmt(instance, f);
+	else if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_g_fmt(instance, f);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_g_fmt);
+
+int msm_vidc_s_ctrl(void *instance, struct v4l2_control *control)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !control)
+		return -EINVAL;
+
+	return msm_comm_s_ctrl(instance, control);
+}
+EXPORT_SYMBOL(msm_vidc_s_ctrl);
+
+int msm_vidc_g_ctrl(void *instance, struct v4l2_control *control)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !control)
+		return -EINVAL;
+
+	return msm_comm_g_ctrl(instance, control);
+}
+EXPORT_SYMBOL(msm_vidc_g_ctrl);
+
+int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *control)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !control)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_s_ext_ctrl(instance, control);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_s_ext_ctrl(instance, control);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_s_ext_ctrl);
+
+int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !b)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_reqbufs(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_reqbufs(instance, b);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_reqbufs);
+
+struct buffer_info *get_registered_buf(struct msm_vidc_inst *inst,
+		struct v4l2_buffer *b, int idx, int *plane)
+{
+	struct buffer_info *temp;
+	struct buffer_info *ret = NULL;
+	int i;
+	int fd = b->m.planes[idx].reserved[0];
+	u32 buff_off = b->m.planes[idx].reserved[1];
+	u32 size = b->m.planes[idx].length;
+	ion_phys_addr_t device_addr = b->m.planes[idx].m.userptr;
+
+	if (fd < 0 || !plane) {
+		dprintk(VIDC_ERR, "Invalid input\n");
+		goto err_invalid_input;
+	}
+
+	WARN(!mutex_is_locked(&inst->registeredbufs.lock),
+		"Registered buf lock is not acquired for %s", __func__);
+
+	*plane = 0;
+	list_for_each_entry(temp, &inst->registeredbufs.list, list) {
+		for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) {
+			bool ion_hndl_matches = temp->handle[i] ?
+				msm_smem_compare_buffers(inst->mem_client, fd,
+				temp->handle[i]->smem_priv) : false;
+			bool device_addr_matches = device_addr ==
+						temp->device_addr[i];
+			bool contains_within = CONTAINS(temp->buff_off[i],
+					temp->size[i], buff_off) ||
+				CONTAINS(buff_off, size, temp->buff_off[i]);
+			bool overlaps = OVERLAPS(buff_off, size,
+					temp->buff_off[i], temp->size[i]);
+
+			if (!temp->inactive &&
+				(ion_hndl_matches || device_addr_matches) &&
+				(contains_within || overlaps)) {
+				dprintk(VIDC_DBG,
+						"This memory region is already mapped\n");
+				ret = temp;
+				*plane = i;
+				break;
+			}
+		}
+		if (ret)
+			break;
+	}
+
+err_invalid_input:
+	return ret;
+}
+
+static struct msm_smem *get_same_fd_buffer(struct msm_vidc_inst *inst, int fd)
+{
+	struct buffer_info *temp;
+	struct msm_smem *same_fd_handle = NULL;
+	int i;
+
+	if (!fd)
+		return NULL;
+
+	if (!inst || fd < 0) {
+		dprintk(VIDC_ERR, "%s: Invalid input\n", __func__);
+		goto err_invalid_input;
+	}
+
+	mutex_lock(&inst->registeredbufs.lock);
+	list_for_each_entry(temp, &inst->registeredbufs.list, list) {
+		for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) {
+			bool ion_hndl_matches = temp->handle[i] ?
+				msm_smem_compare_buffers(inst->mem_client, fd,
+				temp->handle[i]->smem_priv) : false;
+			if (ion_hndl_matches && temp->mapped[i])  {
+				temp->same_fd_ref[i]++;
+				dprintk(VIDC_INFO,
+				"Found same fd buffer\n");
+				same_fd_handle = temp->handle[i];
+				break;
+			}
+		}
+		if (same_fd_handle)
+			break;
+	}
+	mutex_unlock(&inst->registeredbufs.lock);
+
+err_invalid_input:
+	return same_fd_handle;
+}
+
+struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list,
+				ion_phys_addr_t device_addr)
+{
+	struct buffer_info *temp = NULL;
+	bool found = false;
+	int i;
+
+	if (!buf_list || !device_addr) {
+		dprintk(VIDC_ERR,
+			"Invalid input- device_addr: %pa buf_list: %pK\n",
+			&device_addr, buf_list);
+		goto err_invalid_input;
+	}
+
+	mutex_lock(&buf_list->lock);
+	list_for_each_entry(temp, &buf_list->list, list) {
+		for (i = 0; i < min(temp->num_planes, VIDEO_MAX_PLANES); i++) {
+			if (!temp->inactive &&
+				temp->device_addr[i] == device_addr)  {
+				dprintk(VIDC_INFO,
+				"Found same fd buffer\n");
+				found = true;
+				break;
+			}
+		}
+
+		if (found)
+			break;
+	}
+	mutex_unlock(&buf_list->lock);
+
+err_invalid_input:
+	return temp;
+}
+
+static inline void populate_buf_info(struct buffer_info *binfo,
+			struct v4l2_buffer *b, u32 i)
+{
+	if (i >= VIDEO_MAX_PLANES) {
+		dprintk(VIDC_ERR, "%s: Invalid input\n", __func__);
+		return;
+	}
+	binfo->type = b->type;
+	binfo->fd[i] = b->m.planes[i].reserved[0];
+	binfo->buff_off[i] = b->m.planes[i].reserved[1];
+	binfo->size[i] = b->m.planes[i].length;
+	binfo->uvaddr[i] = b->m.planes[i].m.userptr;
+	binfo->num_planes = b->length;
+	binfo->memory = b->memory;
+	binfo->v4l2_index = b->index;
+	binfo->timestamp.tv_sec = b->timestamp.tv_sec;
+	binfo->timestamp.tv_usec = b->timestamp.tv_usec;
+	dprintk(VIDC_DBG, "%s: fd[%d] = %d b->index = %d",
+			__func__, i, binfo->fd[i], b->index);
+}
+
+static inline void repopulate_v4l2_buffer(struct v4l2_buffer *b,
+					struct buffer_info *binfo)
+{
+	int i = 0;
+
+	b->type = binfo->type;
+	b->length = binfo->num_planes;
+	b->memory = binfo->memory;
+	b->index = binfo->v4l2_index;
+	b->timestamp.tv_sec = binfo->timestamp.tv_sec;
+	b->timestamp.tv_usec = binfo->timestamp.tv_usec;
+	binfo->dequeued = false;
+	for (i = 0; i < binfo->num_planes; ++i) {
+		b->m.planes[i].reserved[0] = binfo->fd[i];
+		b->m.planes[i].reserved[1] = binfo->buff_off[i];
+		b->m.planes[i].length = binfo->size[i];
+		b->m.planes[i].m.userptr = binfo->device_addr[i];
+		dprintk(VIDC_DBG, "%s %d %d %d %pa\n", __func__, binfo->fd[i],
+				binfo->buff_off[i], binfo->size[i],
+				&binfo->device_addr[i]);
+	}
+}
+
+static struct msm_smem *map_buffer(struct msm_vidc_inst *inst,
+		struct v4l2_plane *p, enum hal_buffer buffer_type)
+{
+	struct msm_smem *handle = NULL;
+
+	handle = msm_comm_smem_user_to_kernel(inst,
+				p->reserved[0],
+				p->reserved[1],
+				buffer_type);
+	if (!handle) {
+		dprintk(VIDC_ERR,
+			"%s: Failed to get device buffer address\n", __func__);
+		return NULL;
+	}
+	return handle;
+}
+
+static inline enum hal_buffer get_hal_buffer_type(
+		struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	if (b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return HAL_BUFFER_INPUT;
+	else if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return HAL_BUFFER_OUTPUT;
+	else
+		return -EINVAL;
+}
+
+static inline bool is_dynamic_output_buffer_mode(struct v4l2_buffer *b,
+				struct msm_vidc_inst *inst)
+{
+	return b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+		inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC;
+}
+
+
+static inline void save_v4l2_buffer(struct v4l2_buffer *b,
+						struct buffer_info *binfo)
+{
+	int i = 0;
+
+	for (i = 0; i < b->length; ++i) {
+		if (EXTRADATA_IDX(b->length) &&
+			(i == EXTRADATA_IDX(b->length)) &&
+			!b->m.planes[i].length) {
+			continue;
+		}
+		populate_buf_info(binfo, b, i);
+	}
+}
+
+int map_and_register_buf(struct msm_vidc_inst *inst, struct v4l2_buffer *b)
+{
+	struct buffer_info *binfo = NULL;
+	struct buffer_info *temp = NULL, *iterator = NULL;
+	int plane = 0;
+	int i = 0, rc = 0;
+	struct msm_smem *same_fd_handle = NULL;
+
+	if (!b || !inst) {
+		dprintk(VIDC_ERR, "%s: invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+	if (!binfo) {
+		dprintk(VIDC_ERR, "Out of memory\n");
+		rc = -ENOMEM;
+		goto exit;
+	}
+	if (b->length > VIDEO_MAX_PLANES) {
+		dprintk(VIDC_ERR, "Num planes exceeds max: %d, %d\n",
+			b->length, VIDEO_MAX_PLANES);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	dprintk(VIDC_DBG, "[MAP] Create binfo = %pK fd = %d type = %d\n",
+			binfo, b->m.planes[0].reserved[0], b->type);
+
+	for (i = 0; i < b->length; ++i) {
+		rc = 0;
+		if (EXTRADATA_IDX(b->length) &&
+			(i == EXTRADATA_IDX(b->length)) &&
+			!b->m.planes[i].length) {
+			continue;
+		}
+		mutex_lock(&inst->registeredbufs.lock);
+		temp = get_registered_buf(inst, b, i, &plane);
+		if (temp && !is_dynamic_output_buffer_mode(b, inst)) {
+			dprintk(VIDC_DBG,
+				"This memory region has already been prepared\n");
+			rc = 0;
+			mutex_unlock(&inst->registeredbufs.lock);
+			goto exit;
+		}
+
+		if (temp && is_dynamic_output_buffer_mode(b, inst) && !i) {
+			/*
+			 * Buffer is already present in registered list
+			 * increment ref_count, populate new values of v4l2
+			 * buffer in existing buffer_info struct.
+			 *
+			 * We will use the saved buffer info and queue it when
+			 * we receive RELEASE_BUFFER_REFERENCE EVENT from f/w.
+			 */
+			dprintk(VIDC_DBG, "[MAP] Buffer already prepared\n");
+			temp->inactive = false;
+			list_for_each_entry(iterator,
+				&inst->registeredbufs.list, list) {
+				if (iterator == temp) {
+					rc = buf_ref_get(inst, temp);
+					save_v4l2_buffer(b, temp);
+					break;
+				}
+			}
+		}
+		mutex_unlock(&inst->registeredbufs.lock);
+		/*
+		 * rc == 1,
+		 * buffer is mapped, fw has released all reference, so skip
+		 * mapping and queue it immediately.
+		 *
+		 * rc == 2,
+		 * buffer is mapped and fw is holding a reference, hold it in
+		 * the driver and queue it later when fw has released
+		 */
+		if (rc == 1) {
+			rc = 0;
+			goto exit;
+		} else if (rc == 2) {
+			rc = -EEXIST;
+			goto exit;
+		}
+
+		same_fd_handle = get_same_fd_buffer(
+				inst, b->m.planes[i].reserved[0]);
+
+		populate_buf_info(binfo, b, i);
+		if (same_fd_handle) {
+			binfo->device_addr[i] =
+			same_fd_handle->device_addr + binfo->buff_off[i];
+			b->m.planes[i].m.userptr = binfo->device_addr[i];
+			binfo->mapped[i] = false;
+			binfo->handle[i] = same_fd_handle;
+		} else {
+			binfo->handle[i] = map_buffer(inst, &b->m.planes[i],
+					get_hal_buffer_type(inst, b));
+			if (!binfo->handle[i]) {
+				rc = -EINVAL;
+				goto exit;
+			}
+
+			binfo->mapped[i] = true;
+			binfo->device_addr[i] = binfo->handle[i]->device_addr +
+				binfo->buff_off[i];
+			b->m.planes[i].m.userptr = binfo->device_addr[i];
+		}
+
+		/* We maintain one ref count for all planes*/
+		if (!i && is_dynamic_output_buffer_mode(b, inst)) {
+			rc = buf_ref_get(inst, binfo);
+			if (rc < 0)
+				goto exit;
+		}
+		dprintk(VIDC_DBG,
+			"%s: [MAP] binfo = %pK, handle[%d] = %pK, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n",
+			__func__, binfo, i, binfo->handle[i],
+			&binfo->device_addr[i], binfo->fd[i],
+			binfo->buff_off[i], binfo->mapped[i]);
+	}
+
+	mutex_lock(&inst->registeredbufs.lock);
+	list_add_tail(&binfo->list, &inst->registeredbufs.list);
+	mutex_unlock(&inst->registeredbufs.lock);
+	return 0;
+
+exit:
+	kfree(binfo);
+	return rc;
+}
+int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
+			struct buffer_info *binfo)
+{
+	int i = 0;
+	struct buffer_info *temp = NULL;
+	bool found = false, keep_node = false;
+
+	if (!inst || !binfo) {
+		dprintk(VIDC_ERR, "%s invalid param: %pK %pK\n",
+			__func__, inst, binfo);
+		return -EINVAL;
+	}
+
+	WARN(!mutex_is_locked(&inst->registeredbufs.lock),
+		"Registered buf lock is not acquired for %s", __func__);
+
+	/*
+	 * Make sure the buffer to be unmapped and deleted
+	 * from the registered list is present in the list.
+	 */
+	list_for_each_entry(temp, &inst->registeredbufs.list, list) {
+		if (temp == binfo) {
+			found = true;
+			break;
+		}
+	}
+
+	/*
+	 * Free the buffer info only if
+	 * - buffer info has not been deleted from registered list
+	 * - vidc client has called dqbuf on the buffer
+	 * - no references are held on the buffer
+	 */
+	if (!found || !temp || !temp->pending_deletion || !temp->dequeued)
+		goto exit;
+
+	for (i = 0; i < temp->num_planes; i++) {
+		dprintk(VIDC_DBG,
+			"%s: [UNMAP] binfo = %pK, handle[%d] = %pK, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n",
+			__func__, temp, i, temp->handle[i],
+			&temp->device_addr[i], temp->fd[i],
+			temp->buff_off[i], temp->mapped[i]);
+		/*
+		 * Unmap the handle only if the buffer has been mapped and no
+		 * other buffer has a reference to this buffer.
+		 * In case of buffers with same fd, we will map the buffer only
+		 * once and subsequent buffers will refer to the mapped buffer's
+		 * device address.
+		 * For buffers which share the same fd, do not unmap and keep
+		 * the buffer info in registered list.
+		 */
+		if (temp->handle[i] && temp->mapped[i] &&
+			!temp->same_fd_ref[i]) {
+			msm_comm_smem_free(inst,
+				temp->handle[i]);
+		}
+
+		if (temp->same_fd_ref[i])
+			keep_node = true;
+		else {
+			temp->fd[i] = 0;
+			temp->handle[i] = 0;
+			temp->device_addr[i] = 0;
+			temp->uvaddr[i] = 0;
+		}
+	}
+	if (!keep_node) {
+		dprintk(VIDC_DBG, "[UNMAP] AND-FREED binfo: %pK\n", temp);
+		list_del(&temp->list);
+		kfree(temp);
+	} else {
+		temp->inactive = true;
+		dprintk(VIDC_DBG, "[UNMAP] NOT-FREED binfo: %pK\n", temp);
+	}
+exit:
+	return 0;
+}
+
+
+int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
+			struct buffer_info *binfo)
+{
+	struct v4l2_buffer b = {0};
+	struct v4l2_plane plane[VIDEO_MAX_PLANES] = { {0} };
+
+	if (!binfo) {
+		dprintk(VIDC_ERR, "%s invalid param: %pK\n", __func__, binfo);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG, "%s fd[0] = %d\n", __func__, binfo->fd[0]);
+
+	b.m.planes = plane;
+	repopulate_v4l2_buffer(&b, binfo);
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_qbuf(inst, &b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_qbuf(inst, &b);
+
+	return -EINVAL;
+}
+
+int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
+				struct buffer_info *binfo)
+{
+	int i = 0;
+	int rc = 0;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst);
+		return -EINVAL;
+	}
+
+	if (!binfo) {
+		dprintk(VIDC_ERR, "%s: invalid buffer info: %pK\n",
+			__func__, inst);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < binfo->num_planes; i++) {
+		if (binfo->handle[i]) {
+			struct msm_smem smem = *binfo->handle[i];
+
+			smem.offset = (unsigned int)(binfo->buff_off[i]);
+			smem.size   = binfo->size[i];
+			rc = msm_comm_smem_cache_operations(inst,
+				&smem, SMEM_CACHE_INVALIDATE);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"%s: Failed to clean caches: %d\n",
+					__func__, rc);
+				return -EINVAL;
+			}
+		} else
+			dprintk(VIDC_DBG, "%s: NULL handle for plane %d\n",
+					__func__, i);
+	}
+	return 0;
+}
+
+static bool valid_v4l2_buffer(struct v4l2_buffer *b,
+		struct msm_vidc_inst *inst) {
+	enum vidc_ports port =
+		!V4L2_TYPE_IS_MULTIPLANAR(b->type) ? MAX_PORT_NUM :
+		b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ? CAPTURE_PORT :
+		b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ? OUTPUT_PORT :
+								MAX_PORT_NUM;
+
+	return port != MAX_PORT_NUM &&
+		inst->fmts[port].num_planes == b->length;
+}
+
+int msm_vidc_prepare_buf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst))
+		return -EINVAL;
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+		inst->core->state == VIDC_CORE_INVALID)
+		return -EINVAL;
+
+	if (is_dynamic_output_buffer_mode(b, inst))
+		return 0;
+
+	if (map_and_register_buf(inst, b))
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_prepare_buf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_prepare_buf(instance, b);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_prepare_buf);
+
+int msm_vidc_release_buffers(void *instance, int buffer_type)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct buffer_info *bi, *dummy;
+	struct v4l2_buffer buffer_info;
+	struct v4l2_plane plane[VIDEO_MAX_PLANES];
+	int i, rc = 0;
+
+	if (!inst)
+		return -EINVAL;
+
+	if (!inst->in_reconfig &&
+		inst->state > MSM_VIDC_LOAD_RESOURCES &&
+		inst->state < MSM_VIDC_RELEASE_RESOURCES_DONE) {
+		rc = msm_comm_try_state(inst, MSM_VIDC_RELEASE_RESOURCES_DONE);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"Failed to move inst: %pK to release res done\n",
+					inst);
+		}
+	}
+
+	/*
+	 * In dynamic buffer mode, driver needs to release resources,
+	 * but not call release buffers on firmware, as the buffers
+	 * were never registered with firmware.
+	 */
+	if (buffer_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+		inst->buffer_mode_set[CAPTURE_PORT] ==
+				HAL_BUFFER_MODE_DYNAMIC) {
+		goto free_and_unmap;
+	}
+
+	mutex_lock(&inst->registeredbufs.lock);
+	list_for_each_entry(bi, &inst->registeredbufs.list, list) {
+		bool release_buf = false;
+
+		if (bi->type == buffer_type) {
+			buffer_info.type = bi->type;
+			for (i = 0; i < min(bi->num_planes, VIDEO_MAX_PLANES);
+						i++) {
+				plane[i].reserved[0] = bi->fd[i];
+				plane[i].reserved[1] = bi->buff_off[i];
+				plane[i].length = bi->size[i];
+				plane[i].m.userptr = bi->device_addr[i];
+				buffer_info.m.planes = plane;
+				dprintk(VIDC_DBG,
+					"Releasing buffer: %d, %d, %d\n",
+					buffer_info.m.planes[i].reserved[0],
+					buffer_info.m.planes[i].reserved[1],
+					buffer_info.m.planes[i].length);
+			}
+			buffer_info.length = bi->num_planes;
+			release_buf = true;
+		}
+
+		if (!release_buf)
+			continue;
+		if (inst->session_type == MSM_VIDC_DECODER)
+			rc = msm_vdec_release_buf(instance,
+				&buffer_info);
+		if (inst->session_type == MSM_VIDC_ENCODER)
+			rc = msm_venc_release_buf(instance,
+				&buffer_info);
+		if (rc)
+			dprintk(VIDC_ERR,
+				"Failed Release buffer: %d, %d, %d\n",
+				buffer_info.m.planes[0].reserved[0],
+				buffer_info.m.planes[0].reserved[1],
+				buffer_info.m.planes[0].length);
+	}
+	mutex_unlock(&inst->registeredbufs.lock);
+
+free_and_unmap:
+	mutex_lock(&inst->registeredbufs.lock);
+	list_for_each_entry_safe(bi, dummy, &inst->registeredbufs.list, list) {
+		if (bi->type == buffer_type) {
+			list_del(&bi->list);
+			for (i = 0; i < bi->num_planes; i++) {
+				if (bi->handle[i] && bi->mapped[i]) {
+					dprintk(VIDC_DBG,
+						"%s: [UNMAP] binfo = %pK, handle[%d] = %pK, device_addr = %pa, fd = %d, offset = %d, mapped = %d\n",
+						__func__, bi, i, bi->handle[i],
+						&bi->device_addr[i], bi->fd[i],
+						bi->buff_off[i], bi->mapped[i]);
+					msm_comm_smem_free(inst,
+							bi->handle[i]);
+				}
+			}
+			kfree(bi);
+		}
+	}
+	mutex_unlock(&inst->registeredbufs.lock);
+	return rc;
+}
+EXPORT_SYMBOL(msm_vidc_release_buffers);
+
+int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct buffer_info *binfo;
+	int plane = 0;
+	int rc = 0;
+	int i;
+
+	if (!inst || !inst->core || !b || !valid_v4l2_buffer(b, inst))
+		return -EINVAL;
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+		inst->core->state == VIDC_CORE_INVALID)
+		return -EINVAL;
+
+	rc = map_and_register_buf(inst, b);
+	if (rc == -EEXIST) {
+		if (atomic_read(&inst->in_flush) &&
+			is_dynamic_output_buffer_mode(b, inst)) {
+			dprintk(VIDC_ERR,
+				"Flush in progress, do not hold any buffers in driver\n");
+			msm_comm_flush_dynamic_buffers(inst);
+		}
+		return 0;
+	}
+	if (rc)
+		return rc;
+
+	for (i = 0; i < b->length; ++i) {
+		if (EXTRADATA_IDX(b->length) &&
+			(i == EXTRADATA_IDX(b->length)) &&
+			!b->m.planes[i].length) {
+			b->m.planes[i].m.userptr = 0;
+			continue;
+		}
+		mutex_lock(&inst->registeredbufs.lock);
+		binfo = get_registered_buf(inst, b, i, &plane);
+		mutex_unlock(&inst->registeredbufs.lock);
+		if (!binfo) {
+			dprintk(VIDC_ERR,
+				"This buffer is not registered: %d, %d, %d\n",
+				b->m.planes[i].reserved[0],
+				b->m.planes[i].reserved[1],
+				b->m.planes[i].length);
+			goto err_invalid_buff;
+		}
+		b->m.planes[i].m.userptr = binfo->device_addr[i];
+		dprintk(VIDC_DBG, "Queueing device address = %pa\n",
+				&binfo->device_addr[i]);
+
+		if (binfo->handle[i] &&
+			(b->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)) {
+			rc = msm_comm_smem_cache_operations(inst,
+					binfo->handle[i], SMEM_CACHE_CLEAN);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed to clean caches: %d\n", rc);
+				goto err_invalid_buff;
+			}
+		}
+	}
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_qbuf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_qbuf(instance, b);
+
+err_invalid_buff:
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_qbuf);
+
+int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct buffer_info *buffer_info = NULL;
+	int i = 0, rc = 0;
+
+	if (!inst || !b || !valid_v4l2_buffer(b, inst))
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		rc = msm_vdec_dqbuf(instance, b);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		rc = msm_venc_dqbuf(instance, b);
+
+	if (rc)
+		return rc;
+
+	for (i = 0; i < b->length; i++) {
+		if (EXTRADATA_IDX(b->length) &&
+			i == EXTRADATA_IDX(b->length)) {
+			continue;
+		}
+		buffer_info = device_to_uvaddr(&inst->registeredbufs,
+			b->m.planes[i].m.userptr);
+
+		if (!buffer_info) {
+			dprintk(VIDC_ERR,
+				"%s no buffer info registered for buffer addr: %#lx\n",
+				__func__, b->m.planes[i].m.userptr);
+			return -EINVAL;
+		}
+
+		b->m.planes[i].m.userptr = buffer_info->uvaddr[i];
+		b->m.planes[i].reserved[0] = buffer_info->fd[i];
+		b->m.planes[i].reserved[1] = buffer_info->buff_off[i];
+		if (!b->m.planes[i].m.userptr) {
+			dprintk(VIDC_ERR,
+			"%s: Failed to find user virtual address, %#lx, %d, %d\n",
+			__func__, b->m.planes[i].m.userptr, b->type, i);
+			return -EINVAL;
+		}
+	}
+
+	if (!buffer_info) {
+		dprintk(VIDC_ERR,
+			"%s: error - no buffer info found in registered list\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	rc = output_buffer_cache_invalidate(inst, buffer_info);
+	if (rc)
+		return rc;
+
+
+	if (is_dynamic_output_buffer_mode(b, inst)) {
+		buffer_info->dequeued = true;
+
+		dprintk(VIDC_DBG, "[DEQUEUED]: fd[0] = %d\n",
+			buffer_info->fd[0]);
+		mutex_lock(&inst->registeredbufs.lock);
+		rc = unmap_and_deregister_buf(inst, buffer_info);
+		mutex_unlock(&inst->registeredbufs.lock);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_vidc_dqbuf);
+
+int msm_vidc_streamon(void *instance, enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_streamon(instance, i);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_streamon(instance, i);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_streamon);
+
+int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i)
+{
+	struct msm_vidc_inst *inst = instance;
+
+	if (!inst)
+		return -EINVAL;
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		return msm_vdec_streamoff(instance, i);
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		return msm_venc_streamoff(instance, i);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(msm_vidc_streamoff);
+
+int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct msm_vidc_capability *capability = NULL;
+
+	if (!inst || !fsize) {
+		dprintk(VIDC_ERR, "%s: invalid parameter: %pK %pK\n",
+				__func__, inst, fsize);
+		return -EINVAL;
+	}
+	if (!inst->core)
+		return -EINVAL;
+
+	capability = &inst->capability;
+	fsize->type = V4L2_FRMSIZE_TYPE_STEPWISE;
+	fsize->stepwise.min_width = capability->width.min;
+	fsize->stepwise.max_width = capability->width.max;
+	fsize->stepwise.step_width = capability->width.step_size;
+	fsize->stepwise.min_height = capability->height.min;
+	fsize->stepwise.max_height = capability->height.max;
+	fsize->stepwise.step_height = capability->height.step_size;
+	return 0;
+}
+EXPORT_SYMBOL(msm_vidc_enum_framesizes);
+
+static void *vidc_get_userptr(struct device *dev, unsigned long vaddr,
+			unsigned long size, enum dma_data_direction dma_dir)
+{
+	return (void *)0xdeadbeef;
+}
+
+static void vidc_put_userptr(void *buf_priv)
+{
+}
+
+static const struct vb2_mem_ops msm_vidc_vb2_mem_ops = {
+	.get_userptr = vidc_get_userptr,
+	.put_userptr = vidc_put_userptr,
+};
+
+static inline int vb2_bufq_init(struct msm_vidc_inst *inst,
+		enum v4l2_buf_type type, enum session_type sess)
+{
+	struct vb2_queue *q = NULL;
+
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		q = &inst->bufq[CAPTURE_PORT].vb2_bufq;
+	} else if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		q = &inst->bufq[OUTPUT_PORT].vb2_bufq;
+	} else {
+		dprintk(VIDC_ERR, "buf_type = %d not recognised\n", type);
+		return -EINVAL;
+	}
+
+	q->type = type;
+	q->io_modes = VB2_MMAP | VB2_USERPTR;
+	q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
+
+	if (sess == MSM_VIDC_DECODER)
+		q->ops = msm_vdec_get_vb2q_ops();
+	else if (sess == MSM_VIDC_ENCODER)
+		q->ops = msm_venc_get_vb2q_ops();
+	q->mem_ops = &msm_vidc_vb2_mem_ops;
+	q->drv_priv = inst;
+	q->allow_zero_bytesused = 1;
+	return vb2_queue_init(q);
+}
+
+static int setup_event_queue(void *inst,
+				struct video_device *pvdev)
+{
+	int rc = 0;
+	struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst;
+
+	v4l2_fh_init(&vidc_inst->event_handler, pvdev);
+	v4l2_fh_add(&vidc_inst->event_handler);
+
+	return rc;
+}
+
+int msm_vidc_subscribe_event(void *inst,
+	const struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst;
+
+	if (!inst || !sub)
+		return -EINVAL;
+
+	rc = v4l2_event_subscribe(&vidc_inst->event_handler,
+		sub, MAX_EVENTS, NULL);
+	return rc;
+}
+EXPORT_SYMBOL(msm_vidc_subscribe_event);
+
+int msm_vidc_unsubscribe_event(void *inst,
+	const struct v4l2_event_subscription *sub)
+{
+	int rc = 0;
+	struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst;
+
+	if (!inst || !sub)
+		return -EINVAL;
+
+	rc = v4l2_event_unsubscribe(&vidc_inst->event_handler, sub);
+	return rc;
+}
+EXPORT_SYMBOL(msm_vidc_unsubscribe_event);
+
+int msm_vidc_dqevent(void *inst, struct v4l2_event *event)
+{
+	int rc = 0;
+	struct msm_vidc_inst *vidc_inst = (struct msm_vidc_inst *)inst;
+
+	if (!inst || !event)
+		return -EINVAL;
+
+	rc = v4l2_event_dequeue(&vidc_inst->event_handler, event, false);
+	return rc;
+}
+EXPORT_SYMBOL(msm_vidc_dqevent);
+
+static bool msm_vidc_check_for_inst_overload(struct msm_vidc_core *core)
+{
+	u32 instance_count = 0;
+	u32 secure_instance_count = 0;
+	struct msm_vidc_inst *inst = NULL;
+	bool overload = false;
+
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list) {
+		instance_count++;
+		/* This flag is not updated yet for the current instance */
+		if (inst->flags & VIDC_SECURE)
+			secure_instance_count++;
+	}
+	mutex_unlock(&core->lock);
+
+	/* Instance count includes current instance as well. */
+
+	if ((instance_count > core->resources.max_inst_count) ||
+		(secure_instance_count > core->resources.max_secure_inst_count))
+		overload = true;
+	return overload;
+}
+
+void *msm_vidc_open(int core_id, int session_type)
+{
+	struct msm_vidc_inst *inst = NULL;
+	struct msm_vidc_core *core = NULL;
+	int rc = 0;
+	int i = 0;
+
+	if (core_id >= MSM_VIDC_CORES_MAX ||
+			session_type >= MSM_VIDC_MAX_DEVICES) {
+		dprintk(VIDC_ERR, "Invalid input, core_id = %d, session = %d\n",
+			core_id, session_type);
+		goto err_invalid_core;
+	}
+	core = get_vidc_core(core_id);
+	if (!core) {
+		dprintk(VIDC_ERR,
+			"Failed to find core for core_id = %d\n", core_id);
+		goto err_invalid_core;
+	}
+
+	inst = kzalloc(sizeof(*inst), GFP_KERNEL);
+	if (!inst) {
+		dprintk(VIDC_ERR, "Failed to allocate memory\n");
+		rc = -ENOMEM;
+		goto err_invalid_core;
+	}
+
+	pr_info(VIDC_DBG_TAG "Opening video instance: %pK, %d\n",
+		VIDC_MSG_PRIO2STRING(VIDC_INFO), inst, session_type);
+	mutex_init(&inst->sync_lock);
+	mutex_init(&inst->bufq[CAPTURE_PORT].lock);
+	mutex_init(&inst->bufq[OUTPUT_PORT].lock);
+	mutex_init(&inst->lock);
+
+	INIT_MSM_VIDC_LIST(&inst->pendingq);
+	INIT_MSM_VIDC_LIST(&inst->scratchbufs);
+	INIT_MSM_VIDC_LIST(&inst->persistbufs);
+	INIT_MSM_VIDC_LIST(&inst->pending_getpropq);
+	INIT_MSM_VIDC_LIST(&inst->outputbufs);
+	INIT_MSM_VIDC_LIST(&inst->registeredbufs);
+
+	kref_init(&inst->kref);
+
+	inst->session_type = session_type;
+	inst->state = MSM_VIDC_CORE_UNINIT_DONE;
+	inst->core = core;
+	inst->bit_depth = MSM_VIDC_BIT_DEPTH_8;
+	inst->instant_bitrate = 0;
+	inst->pic_struct = MSM_VIDC_PIC_STRUCT_PROGRESSIVE;
+	inst->colour_space = MSM_VIDC_BT601_6_525;
+
+	for (i = SESSION_MSG_INDEX(SESSION_MSG_START);
+		i <= SESSION_MSG_INDEX(SESSION_MSG_END); i++) {
+		init_completion(&inst->completions[i]);
+	}
+	inst->mem_client = msm_smem_new_client(SMEM_ION,
+					&inst->core->resources, session_type);
+	if (!inst->mem_client) {
+		dprintk(VIDC_ERR, "Failed to create memory client\n");
+		goto fail_mem_client;
+	}
+	if (session_type == MSM_VIDC_DECODER) {
+		msm_vdec_inst_init(inst);
+		rc = msm_vdec_ctrl_init(inst);
+	} else if (session_type == MSM_VIDC_ENCODER) {
+		msm_venc_inst_init(inst);
+		rc = msm_venc_ctrl_init(inst);
+	}
+
+	if (rc)
+		goto fail_bufq_capture;
+
+	msm_dcvs_init(inst);
+	rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+			session_type);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to initialize vb2 queue on capture port\n");
+		goto fail_bufq_capture;
+	}
+	rc = vb2_bufq_init(inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+			session_type);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to initialize vb2 queue on capture port\n");
+		goto fail_bufq_output;
+	}
+
+	setup_event_queue(inst, &core->vdev[session_type].vdev);
+
+	mutex_lock(&core->lock);
+	list_add_tail(&inst->list, &core->instances);
+	mutex_unlock(&core->lock);
+
+	rc = msm_comm_try_state(inst, MSM_VIDC_CORE_INIT_DONE);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to move video instance to init state\n");
+		goto fail_init;
+	}
+
+	if (msm_vidc_check_for_inst_overload(core)) {
+		dprintk(VIDC_ERR,
+			"Instance count reached Max limit, rejecting session");
+		goto fail_init;
+	}
+
+	inst->debugfs_root =
+		msm_vidc_debugfs_init_inst(inst, core->debugfs_root);
+
+	return inst;
+fail_init:
+	mutex_lock(&core->lock);
+	list_del(&inst->list);
+	mutex_unlock(&core->lock);
+
+	v4l2_fh_del(&inst->event_handler);
+	v4l2_fh_exit(&inst->event_handler);
+	vb2_queue_release(&inst->bufq[OUTPUT_PORT].vb2_bufq);
+fail_bufq_output:
+	vb2_queue_release(&inst->bufq[CAPTURE_PORT].vb2_bufq);
+fail_bufq_capture:
+	msm_comm_ctrl_deinit(inst);
+	msm_smem_delete_client(inst->mem_client);
+fail_mem_client:
+	mutex_destroy(&inst->sync_lock);
+	mutex_destroy(&inst->bufq[CAPTURE_PORT].lock);
+	mutex_destroy(&inst->bufq[OUTPUT_PORT].lock);
+	mutex_destroy(&inst->lock);
+
+	DEINIT_MSM_VIDC_LIST(&inst->pendingq);
+	DEINIT_MSM_VIDC_LIST(&inst->scratchbufs);
+	DEINIT_MSM_VIDC_LIST(&inst->persistbufs);
+	DEINIT_MSM_VIDC_LIST(&inst->pending_getpropq);
+	DEINIT_MSM_VIDC_LIST(&inst->outputbufs);
+	DEINIT_MSM_VIDC_LIST(&inst->registeredbufs);
+
+	kfree(inst);
+	inst = NULL;
+err_invalid_core:
+	return inst;
+}
+EXPORT_SYMBOL(msm_vidc_open);
+
+static void cleanup_instance(struct msm_vidc_inst *inst)
+{
+	struct vb2_buf_entry *entry, *dummy;
+
+	if (inst) {
+
+		mutex_lock(&inst->pendingq.lock);
+		list_for_each_entry_safe(entry, dummy, &inst->pendingq.list,
+				list) {
+			list_del(&entry->list);
+			kfree(entry);
+		}
+		mutex_unlock(&inst->pendingq.lock);
+
+		if (msm_comm_release_scratch_buffers(inst, false)) {
+			dprintk(VIDC_ERR,
+				"Failed to release scratch buffers\n");
+		}
+
+		if (msm_comm_release_persist_buffers(inst)) {
+			dprintk(VIDC_ERR,
+				"Failed to release persist buffers\n");
+		}
+
+		if (msm_comm_release_output_buffers(inst)) {
+			dprintk(VIDC_ERR,
+				"Failed to release output buffers\n");
+		}
+
+		if (inst->extradata_handle)
+			msm_comm_smem_free(inst, inst->extradata_handle);
+
+		debugfs_remove_recursive(inst->debugfs_root);
+
+		mutex_lock(&inst->pending_getpropq.lock);
+		WARN_ON(!list_empty(&inst->pending_getpropq.list));
+		mutex_unlock(&inst->pending_getpropq.lock);
+	}
+}
+
+int msm_vidc_destroy(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+	int i = 0;
+
+	if (!inst || !inst->core)
+		return -EINVAL;
+
+	core = inst->core;
+
+	mutex_lock(&core->lock);
+	/* inst->list lives in core->instances */
+	list_del(&inst->list);
+	mutex_unlock(&core->lock);
+
+	msm_comm_ctrl_deinit(inst);
+
+	DEINIT_MSM_VIDC_LIST(&inst->pendingq);
+	DEINIT_MSM_VIDC_LIST(&inst->scratchbufs);
+	DEINIT_MSM_VIDC_LIST(&inst->persistbufs);
+	DEINIT_MSM_VIDC_LIST(&inst->pending_getpropq);
+	DEINIT_MSM_VIDC_LIST(&inst->outputbufs);
+	DEINIT_MSM_VIDC_LIST(&inst->registeredbufs);
+
+	v4l2_fh_del(&inst->event_handler);
+	v4l2_fh_exit(&inst->event_handler);
+
+	for (i = 0; i < MAX_PORT_NUM; i++)
+		vb2_queue_release(&inst->bufq[i].vb2_bufq);
+
+	mutex_destroy(&inst->sync_lock);
+	mutex_destroy(&inst->bufq[CAPTURE_PORT].lock);
+	mutex_destroy(&inst->bufq[OUTPUT_PORT].lock);
+	mutex_destroy(&inst->lock);
+
+	pr_info(VIDC_DBG_TAG "Closed video instance: %pK\n",
+			VIDC_MSG_PRIO2STRING(VIDC_INFO), inst);
+	kfree(inst);
+	return 0;
+}
+
+int msm_vidc_close(void *instance)
+{
+	void close_helper(struct kref *kref)
+	{
+		struct msm_vidc_inst *inst = container_of(kref,
+				struct msm_vidc_inst, kref);
+
+		msm_vidc_destroy(inst);
+	}
+
+	struct msm_vidc_inst *inst = instance;
+	struct buffer_info *bi, *dummy;
+	int rc = 0;
+
+	if (!inst || !inst->core)
+		return -EINVAL;
+
+
+	mutex_lock(&inst->registeredbufs.lock);
+	list_for_each_entry_safe(bi, dummy, &inst->registeredbufs.list, list) {
+		if (bi->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+			int i = 0;
+
+			list_del(&bi->list);
+
+			for (i = 0; i < min(bi->num_planes, VIDEO_MAX_PLANES);
+					i++) {
+				if (bi->handle[i] && bi->mapped[i])
+					msm_comm_smem_free(inst, bi->handle[i]);
+			}
+
+			kfree(bi);
+		}
+	}
+	mutex_unlock(&inst->registeredbufs.lock);
+
+	cleanup_instance(inst);
+	if (inst->state != MSM_VIDC_CORE_INVALID &&
+		inst->core->state != VIDC_CORE_INVALID)
+		rc = msm_comm_try_state(inst, MSM_VIDC_CORE_UNINIT);
+	else
+		rc = msm_comm_force_cleanup(inst);
+	if (rc)
+		dprintk(VIDC_ERR,
+			"Failed to move video instance to uninit state\n");
+
+	msm_comm_session_clean(inst);
+	msm_smem_delete_client(inst->mem_client);
+
+	kref_put(&inst->kref, close_helper);
+	return 0;
+}
+EXPORT_SYMBOL(msm_vidc_close);
+
+int msm_vidc_suspend(int core_id)
+{
+	return msm_comm_suspend(core_id);
+}
+EXPORT_SYMBOL(msm_vidc_suspend);
+
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.c b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
new file mode 100644
index 0000000..d891644
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.c
@@ -0,0 +1,648 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "msm_vidc_common.h"
+#include "vidc_hfi_api.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_clocks.h"
+
+#define IS_VALID_DCVS_SESSION(__cur_mbpf, __min_mbpf) \
+		((__cur_mbpf) >= (__min_mbpf))
+
+static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst);
+static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst);
+static int msm_dcvs_dec_scale_clocks(struct msm_vidc_inst *inst, bool fbd);
+
+int msm_dcvs_try_enable(struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s: Invalid args: %p\n", __func__, inst);
+		return -EINVAL;
+	}
+	inst->dcvs_mode = msm_dcvs_check_supported(inst);
+	return 0;
+}
+
+static inline int msm_dcvs_get_mbs_per_frame(struct msm_vidc_inst *inst)
+{
+	int height, width;
+
+	if (!inst->in_reconfig) {
+		height = max(inst->prop.height[CAPTURE_PORT],
+				inst->prop.height[OUTPUT_PORT]);
+		width = max(inst->prop.width[CAPTURE_PORT],
+				inst->prop.width[OUTPUT_PORT]);
+	} else {
+		height = inst->reconfig_height;
+		width = inst->reconfig_width;
+	}
+
+	return NUM_MBS_PER_FRAME(height, width);
+}
+
+static inline int msm_dcvs_count_active_instances(struct msm_vidc_core *core,
+	enum session_type session_type)
+{
+	int active_instances = 0;
+	struct msm_vidc_inst *temp = NULL;
+
+	if (!core) {
+		dprintk(VIDC_ERR, "%s: Invalid args: %pK\n", __func__, core);
+		return -EINVAL;
+	}
+
+	/* DCVS condition is as following
+	 * Decoder DCVS : Only for ONE decoder session.
+	 * Encoder DCVS : Only for ONE encoder session + ONE decoder session
+	 */
+	mutex_lock(&core->lock);
+	list_for_each_entry(temp, &core->instances, list) {
+		if (temp->state >= MSM_VIDC_OPEN_DONE &&
+			temp->state < MSM_VIDC_STOP_DONE &&
+			(temp->session_type == session_type ||
+			 temp->session_type == MSM_VIDC_ENCODER))
+			active_instances++;
+	}
+	mutex_unlock(&core->lock);
+	return active_instances;
+}
+
+static bool msm_dcvs_check_codec_supported(int fourcc,
+		unsigned long codecs_supported, enum session_type type)
+{
+	int codec_bit, session_type_bit;
+	bool codec_type, session_type;
+	unsigned long session;
+
+	session = VIDC_VOTE_DATA_SESSION_VAL(get_hal_codec(fourcc),
+		get_hal_domain(type));
+
+	if (!codecs_supported || !session)
+		return false;
+
+	/* ffs returns a 1 indexed, test_bit takes a 0 indexed...index */
+	codec_bit = ffs(session) - 1;
+	session_type_bit = codec_bit + 1;
+
+	codec_type =
+		test_bit(codec_bit, &codecs_supported) ==
+		test_bit(codec_bit, &session);
+	session_type =
+		test_bit(session_type_bit, &codecs_supported) ==
+		test_bit(session_type_bit, &session);
+
+	return codec_type && session_type;
+}
+
+static void msm_dcvs_update_dcvs_params(int idx, struct msm_vidc_inst *inst)
+{
+	struct dcvs_stats *dcvs = NULL;
+	struct msm_vidc_platform_resources *res = NULL;
+	struct dcvs_table *table = NULL;
+
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
+		return;
+	}
+
+	dcvs = &inst->dcvs;
+	res = &inst->core->resources;
+	table = res->dcvs_tbl;
+
+	dcvs->load_low = table[idx].load_low;
+	dcvs->load_high = table[idx].load_high;
+	dcvs->supported_codecs = table[idx].supported_codecs;
+}
+
+static void msm_dcvs_enc_check_and_scale_clocks(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (inst->session_type == MSM_VIDC_ENCODER &&
+		msm_vidc_enc_dcvs_mode) {
+		rc = msm_dcvs_enc_scale_clocks(inst);
+		if (rc) {
+			dprintk(VIDC_DBG,
+				"ENC_DCVS: error while scaling clocks\n");
+		}
+	}
+}
+
+static void msm_dcvs_dec_check_and_scale_clocks(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (inst->session_type == MSM_VIDC_DECODER &&
+		msm_vidc_dec_dcvs_mode) {
+		msm_dcvs_monitor_buffer(inst);
+		rc = msm_dcvs_dec_scale_clocks(inst, false);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"%s: Failed to scale clocks in DCVS: %d\n",
+					__func__, rc);
+		}
+	}
+}
+
+void msm_dcvs_check_and_scale_clocks(struct msm_vidc_inst *inst, bool is_etb)
+{
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
+		return;
+	}
+	msm_dcvs_try_enable(inst);
+	if (!inst->dcvs_mode) {
+		dprintk(VIDC_DBG, "DCVS is not enabled\n");
+		return;
+	}
+
+	if (is_etb)
+		msm_dcvs_enc_check_and_scale_clocks(inst);
+	else
+		msm_dcvs_dec_check_and_scale_clocks(inst);
+}
+
+static inline int get_pending_bufs_fw(struct msm_vidc_inst *inst)
+{
+	int fw_out_qsize = 0, buffers_in_driver = 0;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
+		return -EINVAL;
+	}
+
+	if (inst->state >= MSM_VIDC_OPEN_DONE &&
+		inst->state < MSM_VIDC_STOP_DONE) {
+		fw_out_qsize = inst->count.ftb - inst->count.fbd;
+		buffers_in_driver = inst->buffers_held_in_driver;
+	}
+
+	return fw_out_qsize + buffers_in_driver;
+}
+
+static inline void msm_dcvs_print_dcvs_stats(struct dcvs_stats *dcvs)
+{
+	dprintk(VIDC_DBG,
+		"DCVS: Load_Low %d, Load High %d\n",
+		dcvs->load_low,
+		dcvs->load_high);
+
+	dprintk(VIDC_DBG,
+		"DCVS: ThrDispBufLow %d, ThrDispBufHigh %d\n",
+		dcvs->threshold_disp_buf_low,
+		dcvs->threshold_disp_buf_high);
+
+	dprintk(VIDC_DBG,
+		"DCVS: min_threshold %d, max_threshold %d\n",
+		dcvs->min_threshold, dcvs->max_threshold);
+}
+
+void msm_dcvs_init_load(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+	struct hal_buffer_requirements *output_buf_req;
+	struct dcvs_stats *dcvs;
+	struct dcvs_table *table;
+	struct msm_vidc_platform_resources *res = NULL;
+	int i, num_rows, fourcc;
+
+	dprintk(VIDC_DBG, "Init DCVS Load\n");
+
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
+		return;
+	}
+
+	core = inst->core;
+	dcvs = &inst->dcvs;
+	res = &core->resources;
+	dcvs->load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS);
+
+	num_rows = res->dcvs_tbl_size;
+	table = res->dcvs_tbl;
+
+	if (!num_rows || !table) {
+		dprintk(VIDC_ERR,
+				"%s: Dcvs table entry not found.\n", __func__);
+		return;
+	}
+
+	fourcc = inst->session_type == MSM_VIDC_DECODER ?
+				inst->fmts[OUTPUT_PORT].fourcc :
+				inst->fmts[CAPTURE_PORT].fourcc;
+
+	for (i = 0; i < num_rows; i++) {
+		bool matches = msm_dcvs_check_codec_supported(
+					fourcc,
+					table[i].supported_codecs,
+					inst->session_type);
+		if (!matches)
+			continue;
+
+		if (dcvs->load > table[i].load) {
+			msm_dcvs_update_dcvs_params(i, inst);
+			break;
+		}
+	}
+
+	if (inst->session_type == MSM_VIDC_ENCODER)
+		goto print_stats;
+
+	output_buf_req = get_buff_req_buffer(inst,
+		msm_comm_get_hal_output_buffer(inst));
+
+	if (!output_buf_req) {
+		dprintk(VIDC_ERR,
+			"%s: No buffer requirement for buffer type %x\n",
+			__func__, HAL_BUFFER_OUTPUT);
+		return;
+	}
+
+	dcvs->transition_turbo = false;
+
+	/* calculating the min and max threshold */
+	if (output_buf_req->buffer_count_actual) {
+		dcvs->min_threshold = output_buf_req->buffer_count_actual -
+			output_buf_req->buffer_count_min -
+			msm_dcvs_get_extra_buff_count(inst) + 1;
+		dcvs->max_threshold = output_buf_req->buffer_count_actual;
+		if (dcvs->max_threshold <= dcvs->min_threshold)
+			dcvs->max_threshold =
+				dcvs->min_threshold + DCVS_BUFFER_SAFEGUARD;
+		dcvs->threshold_disp_buf_low = dcvs->min_threshold;
+		dcvs->threshold_disp_buf_high = dcvs->max_threshold;
+	}
+
+print_stats:
+	msm_dcvs_print_dcvs_stats(dcvs);
+}
+
+void msm_dcvs_init(struct msm_vidc_inst *inst)
+{
+	dprintk(VIDC_DBG, "Init DCVS Struct\n");
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
+		return;
+	}
+
+	inst->dcvs = (struct dcvs_stats){ {0} };
+	inst->dcvs.threshold_disp_buf_high = DCVS_NOMINAL_THRESHOLD;
+	inst->dcvs.threshold_disp_buf_low = DCVS_TURBO_THRESHOLD;
+}
+
+void msm_dcvs_monitor_buffer(struct msm_vidc_inst *inst)
+{
+	int new_ftb, i, prev_buf_count;
+	int fw_pending_bufs, total_output_buf, buffers_outside_fw;
+	struct dcvs_stats *dcvs;
+	struct hal_buffer_requirements *output_buf_req;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, inst);
+		return;
+	}
+	dcvs = &inst->dcvs;
+
+	mutex_lock(&inst->lock);
+	output_buf_req = get_buff_req_buffer(inst,
+				msm_comm_get_hal_output_buffer(inst));
+	if (!output_buf_req) {
+		dprintk(VIDC_ERR, "%s : Get output buffer req failed %pK\n",
+			__func__, inst);
+		mutex_unlock(&inst->lock);
+		return;
+	}
+
+	total_output_buf = output_buf_req->buffer_count_actual;
+	fw_pending_bufs = get_pending_bufs_fw(inst);
+	mutex_unlock(&inst->lock);
+
+	buffers_outside_fw = total_output_buf - fw_pending_bufs;
+	dcvs->num_ftb[dcvs->ftb_index] = buffers_outside_fw;
+	dcvs->ftb_index = (dcvs->ftb_index + 1) % DCVS_FTB_WINDOW;
+
+	if (dcvs->ftb_counter < DCVS_FTB_WINDOW)
+		dcvs->ftb_counter++;
+
+	dprintk(VIDC_PROF,
+		"DCVS: ftb_counter %d\n", dcvs->ftb_counter);
+
+	if (dcvs->ftb_counter == DCVS_FTB_WINDOW) {
+		new_ftb = 0;
+		for (i = 0; i < dcvs->ftb_counter; i++) {
+			if (dcvs->num_ftb[i] > new_ftb)
+				new_ftb = dcvs->num_ftb[i];
+		}
+
+		dcvs->threshold_disp_buf_high = new_ftb;
+		if (dcvs->threshold_disp_buf_high <=
+			dcvs->threshold_disp_buf_low +
+			DCVS_BUFFER_SAFEGUARD) {
+			dcvs->threshold_disp_buf_high =
+				dcvs->threshold_disp_buf_low +
+				DCVS_BUFFER_SAFEGUARD
+				+ (DCVS_BUFFER_SAFEGUARD == 0);
+		}
+
+		dcvs->threshold_disp_buf_high =
+			clamp(dcvs->threshold_disp_buf_high,
+				dcvs->min_threshold,
+				dcvs->max_threshold);
+	}
+
+	if (dcvs->ftb_counter == DCVS_FTB_WINDOW &&
+			dcvs->load == dcvs->load_low) {
+		prev_buf_count =
+			dcvs->num_ftb[((dcvs->ftb_index - 2 +
+				DCVS_FTB_WINDOW) % DCVS_FTB_WINDOW)];
+		if (prev_buf_count == dcvs->threshold_disp_buf_low &&
+			buffers_outside_fw <= dcvs->threshold_disp_buf_low) {
+			dcvs->transition_turbo = true;
+		} else if (buffers_outside_fw > dcvs->threshold_disp_buf_low &&
+			(buffers_outside_fw -
+			 (prev_buf_count - buffers_outside_fw))
+			< dcvs->threshold_disp_buf_low){
+			dcvs->transition_turbo = true;
+		}
+	}
+
+	dprintk(VIDC_PROF,
+		"DCVS: total_output_buf %d buffers_outside_fw %d load %d transition_turbo %d\n",
+		total_output_buf, buffers_outside_fw, dcvs->load_low,
+		dcvs->transition_turbo);
+}
+
+static int msm_dcvs_enc_scale_clocks(struct msm_vidc_inst *inst)
+{
+	int rc = 0, fw_pending_bufs = 0, total_input_buf = 0;
+	struct msm_vidc_core *core;
+	struct dcvs_stats *dcvs;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	core = inst->core;
+	dcvs = &inst->dcvs;
+
+	mutex_lock(&inst->lock);
+	total_input_buf = inst->buff_req.buffer[0].buffer_count_actual;
+	fw_pending_bufs = (inst->count.etb - inst->count.ebd);
+	mutex_unlock(&inst->lock);
+
+	dprintk(VIDC_PROF,
+		"DCVS: total_input_buf %d, fw_pending_bufs %d\n",
+		total_input_buf, fw_pending_bufs);
+
+	if (dcvs->etb_counter < total_input_buf) {
+		dcvs->etb_counter++;
+		if (dcvs->etb_counter != total_input_buf)
+			return rc;
+	}
+
+	dprintk(VIDC_PROF,
+		"DCVS: total_input_buf %d, fw_pending_bufs %d etb_counter %d  dcvs->load %d\n",
+		total_input_buf, fw_pending_bufs,
+		dcvs->etb_counter, dcvs->load);
+
+	if (fw_pending_bufs <= DCVS_ENC_LOW_THR &&
+		dcvs->load > dcvs->load_low) {
+		dcvs->load = dcvs->load_low;
+		dcvs->prev_freq_lowered = true;
+	} else {
+		dcvs->prev_freq_lowered = false;
+	}
+
+	if (fw_pending_bufs >= DCVS_ENC_HIGH_THR &&
+		dcvs->load <= dcvs->load_low) {
+		dcvs->load = dcvs->load_high;
+		dcvs->prev_freq_increased = true;
+	} else {
+		dcvs->prev_freq_increased = false;
+	}
+
+	if (dcvs->prev_freq_lowered || dcvs->prev_freq_increased) {
+		dprintk(VIDC_PROF,
+			"DCVS: (Scaling Clock %s)  etb clock set = %d total_input_buf = %d fw_pending_bufs %d\n",
+			dcvs->prev_freq_lowered ? "Lower" : "Higher",
+			dcvs->load, total_input_buf, fw_pending_bufs);
+
+		rc = msm_comm_scale_clocks_load(core, dcvs->load,
+				LOAD_CALC_NO_QUIRKS);
+		if (rc) {
+			dprintk(VIDC_PROF,
+				"Failed to set clock rate in FBD: %d\n", rc);
+		}
+	} else {
+		dprintk(VIDC_PROF,
+			"DCVS: etb clock load_old = %d total_input_buf = %d fw_pending_bufs %d\n",
+			dcvs->load, total_input_buf, fw_pending_bufs);
+	}
+
+	return rc;
+}
+
+
+/*
+ * In DCVS scale_clocks will be done both in qbuf and FBD
+ * 1 indicates call made from fbd that lowers clock
+ * 0 indicates call made from qbuf that increases clock
+ * based on DCVS algorithm
+ */
+
+static int msm_dcvs_dec_scale_clocks(struct msm_vidc_inst *inst, bool fbd)
+{
+	int rc = 0;
+	int fw_pending_bufs = 0;
+	int total_output_buf = 0;
+	int buffers_outside_fw = 0;
+	struct msm_vidc_core *core;
+	struct hal_buffer_requirements *output_buf_req;
+	struct dcvs_stats *dcvs;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	core = inst->core;
+	dcvs = &inst->dcvs;
+	mutex_lock(&inst->lock);
+	fw_pending_bufs = get_pending_bufs_fw(inst);
+
+	output_buf_req = get_buff_req_buffer(inst,
+		msm_comm_get_hal_output_buffer(inst));
+	mutex_unlock(&inst->lock);
+	if (!output_buf_req) {
+		dprintk(VIDC_ERR,
+			"%s: No buffer requirement for buffer type %x\n",
+			__func__, HAL_BUFFER_OUTPUT);
+		return -EINVAL;
+	}
+
+	/* Total number of output buffers */
+	total_output_buf = output_buf_req->buffer_count_actual;
+
+	/* Buffers outside FW are with display */
+	buffers_outside_fw = total_output_buf - fw_pending_bufs;
+
+	if (buffers_outside_fw >= dcvs->threshold_disp_buf_high &&
+		!dcvs->prev_freq_increased &&
+		dcvs->load > dcvs->load_low) {
+		dcvs->load = dcvs->load_low;
+		dcvs->prev_freq_lowered = true;
+		dcvs->prev_freq_increased = false;
+	} else if (dcvs->transition_turbo && dcvs->load == dcvs->load_low) {
+		dcvs->load = dcvs->load_high;
+		dcvs->prev_freq_increased = true;
+		dcvs->prev_freq_lowered = false;
+		dcvs->transition_turbo = false;
+	} else {
+		dcvs->prev_freq_increased = false;
+		dcvs->prev_freq_lowered = false;
+	}
+
+	if (dcvs->prev_freq_lowered || dcvs->prev_freq_increased) {
+		dprintk(VIDC_PROF,
+			"DCVS: clock set = %d tot_output_buf = %d buffers_outside_fw %d threshold_high %d transition_turbo %d\n",
+			dcvs->load, total_output_buf, buffers_outside_fw,
+			dcvs->threshold_disp_buf_high, dcvs->transition_turbo);
+
+		rc = msm_comm_scale_clocks_load(core, dcvs->load,
+				LOAD_CALC_NO_QUIRKS);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to set clock rate in FBD: %d\n", rc);
+		}
+	} else {
+		dprintk(VIDC_PROF,
+			"DCVS: clock old = %d tot_output_buf = %d buffers_outside_fw %d threshold_high %d transition_turbo %d\n",
+			dcvs->load, total_output_buf, buffers_outside_fw,
+			dcvs->threshold_disp_buf_high, dcvs->transition_turbo);
+	}
+	return rc;
+}
+
+static bool msm_dcvs_check_supported(struct msm_vidc_inst *inst)
+{
+	int num_mbs_per_frame = 0, instance_count = 0;
+	long int instance_load = 0;
+	long int dcvs_limit = 0;
+	struct msm_vidc_inst *temp = NULL;
+	struct msm_vidc_core *core;
+	struct hal_buffer_requirements *output_buf_req;
+	struct dcvs_stats *dcvs;
+	bool is_codec_supported = false;
+	bool is_dcvs_supported = true;
+	struct msm_vidc_platform_resources *res = NULL;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
+		return -EINVAL;
+	}
+
+	core = inst->core;
+	dcvs = &inst->dcvs;
+	res = &core->resources;
+
+	if (!res->dcvs_limit) {
+		dprintk(VIDC_WARN,
+				"%s: dcvs limit table not found\n", __func__);
+		return false;
+	}
+	instance_count = msm_dcvs_count_active_instances(core,
+		inst->session_type);
+	num_mbs_per_frame = msm_dcvs_get_mbs_per_frame(inst);
+	instance_load = msm_comm_get_inst_load(inst, LOAD_CALC_NO_QUIRKS);
+	dcvs_limit =
+		(long int)res->dcvs_limit[inst->session_type].min_mbpf *
+		res->dcvs_limit[inst->session_type].fps;
+	inst->dcvs.extra_buffer_count = 0;
+
+	if (!IS_VALID_DCVS_SESSION(num_mbs_per_frame,
+				res->dcvs_limit[inst->session_type].min_mbpf)) {
+		inst->dcvs.extra_buffer_count = 0;
+		is_dcvs_supported = false;
+		goto dcvs_decision_done;
+
+	}
+
+	if (inst->session_type == MSM_VIDC_DECODER) {
+		inst->dcvs.extra_buffer_count = DCVS_DEC_EXTRA_OUTPUT_BUFFERS;
+		output_buf_req = get_buff_req_buffer(inst,
+				msm_comm_get_hal_output_buffer(inst));
+		if (!output_buf_req) {
+			dprintk(VIDC_ERR,
+					"%s: No buffer requirement for buffer type %x\n",
+					__func__, HAL_BUFFER_OUTPUT);
+			return false;
+		}
+		is_codec_supported =
+			msm_dcvs_check_codec_supported(
+				inst->fmts[OUTPUT_PORT].fourcc,
+				inst->dcvs.supported_codecs,
+				inst->session_type);
+		if (!is_codec_supported ||
+				!msm_vidc_dec_dcvs_mode) {
+			inst->dcvs.extra_buffer_count = 0;
+			is_dcvs_supported = false;
+			goto dcvs_decision_done;
+		}
+		if (msm_comm_turbo_session(inst) ||
+			!IS_VALID_DCVS_SESSION(instance_load, dcvs_limit) ||
+			instance_count > 1)
+			is_dcvs_supported = false;
+	}
+	if (inst->session_type == MSM_VIDC_ENCODER) {
+		inst->dcvs.extra_buffer_count = DCVS_ENC_EXTRA_OUTPUT_BUFFERS;
+		is_codec_supported =
+			msm_dcvs_check_codec_supported(
+				inst->fmts[CAPTURE_PORT].fourcc,
+				inst->dcvs.supported_codecs,
+				inst->session_type);
+		if (!is_codec_supported ||
+				!msm_vidc_enc_dcvs_mode) {
+			inst->dcvs.extra_buffer_count = 0;
+			is_dcvs_supported = false;
+			goto dcvs_decision_done;
+		}
+		if (msm_comm_turbo_session(inst) ||
+			!IS_VALID_DCVS_SESSION(instance_load, dcvs_limit) ||
+				instance_count > 1)
+			is_dcvs_supported = false;
+	}
+dcvs_decision_done:
+	if (!is_dcvs_supported) {
+		msm_comm_scale_clocks(core);
+		if (instance_count > 1) {
+			mutex_lock(&core->lock);
+			list_for_each_entry(temp, &core->instances, list)
+				temp->dcvs_mode = false;
+			mutex_unlock(&core->lock);
+		}
+	}
+	return is_dcvs_supported;
+}
+
+int msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s Invalid args\n", __func__);
+		return 0;
+	}
+
+	return inst->dcvs.extra_buffer_count;
+}
+
+
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_clocks.h b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
new file mode 100644
index 0000000..383c27e1
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_clocks.h
@@ -0,0 +1,40 @@
+/* Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VIDC_CLOCKS_H_
+#define _MSM_VIDC_CLOCKS_H_
+#include "msm_vidc_internal.h"
+
+/* Low threshold for encoder dcvs */
+#define DCVS_ENC_LOW_THR 4
+/* High threshold for encoder dcvs */
+#define DCVS_ENC_HIGH_THR 9
+/* extra o/p buffers in case of encoder dcvs */
+#define DCVS_ENC_EXTRA_OUTPUT_BUFFERS 2
+/* extra o/p buffers in case of decoder dcvs */
+#define DCVS_DEC_EXTRA_OUTPUT_BUFFERS 4
+/* Default threshold to reduce the core frequency */
+#define DCVS_NOMINAL_THRESHOLD 8
+/* Default threshold to increase the core frequency */
+#define DCVS_TURBO_THRESHOLD 4
+
+/* Considering one safeguard buffer */
+#define DCVS_BUFFER_SAFEGUARD (DCVS_DEC_EXTRA_OUTPUT_BUFFERS - 1)
+
+void msm_dcvs_init(struct msm_vidc_inst *inst);
+void msm_dcvs_init_load(struct msm_vidc_inst *inst);
+void msm_dcvs_monitor_buffer(struct msm_vidc_inst *inst);
+void msm_dcvs_check_and_scale_clocks(struct msm_vidc_inst *inst, bool is_etb);
+int  msm_dcvs_get_extra_buff_count(struct msm_vidc_inst *inst);
+int msm_dcvs_try_enable(struct msm_vidc_inst *inst);
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.c b/drivers/media/platform/msm/vidc/msm_vidc_common.c
new file mode 100644
index 0000000..8106815
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.c
@@ -0,0 +1,5355 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/bitops.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <asm/div64.h>
+#include "msm_vidc_common.h"
+#include "vidc_hfi_api.h"
+#include "msm_vidc_debug.h"
+#include "msm_vidc_clocks.h"
+
+#define IS_ALREADY_IN_STATE(__p, __d) ({\
+	int __rc = (__p >= __d);\
+	__rc; \
+})
+
+#define SUM_ARRAY(__arr, __start, __end) ({\
+		int __index;\
+		typeof((__arr)[0]) __sum = 0;\
+		for (__index = (__start); __index <= (__end); __index++) {\
+			if (__index >= 0 && __index < ARRAY_SIZE(__arr))\
+				__sum += __arr[__index];\
+		} \
+		__sum;\
+})
+
+#define V4L2_EVENT_SEQ_CHANGED_SUFFICIENT \
+		V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT
+#define V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT \
+		V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT
+#define V4L2_EVENT_RELEASE_BUFFER_REFERENCE \
+		V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE
+
+#define MAX_SUPPORTED_INSTANCES 16
+
+const char *const mpeg_video_vidc_extradata[] = {
+	"Extradata none",
+	"Extradata MB Quantization",
+	"Extradata Interlace Video",
+	"Extradata VC1 Framedisp",
+	"Extradata VC1 Seqdisp",
+	"Extradata timestamp",
+	"Extradata S3D Frame Packing",
+	"Extradata Frame Rate",
+	"Extradata Panscan Window",
+	"Extradata Recovery point SEI",
+	"Extradata Multislice info",
+	"Extradata number of concealed MB",
+	"Extradata metadata filler",
+	"Extradata input crop",
+	"Extradata digital zoom",
+	"Extradata aspect ratio",
+	"Extradata mpeg2 seqdisp",
+	"Extradata stream userdata",
+	"Extradata frame QP",
+	"Extradata frame bits info",
+	"Extradata LTR",
+	"Extradata macroblock metadata",
+	"Extradata VQZip SEI",
+	"Extradata YUV Stats",
+	"Extradata ROI QP",
+	"Extradata output crop",
+	"Extradata display colour SEI",
+	"Extradata light level SEI",
+	"Extradata PQ Info",
+	"Extradata display VUI",
+	"Extradata vpx color space",
+};
+
+struct getprop_buf {
+	struct list_head list;
+	void *data;
+};
+
+static void msm_comm_generate_session_error(struct msm_vidc_inst *inst);
+static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst);
+static void handle_session_error(enum hal_command_response cmd, void *data);
+static void msm_vidc_print_running_insts(struct msm_vidc_core *core);
+static void msm_comm_print_debug_info(struct msm_vidc_inst *inst);
+
+bool msm_comm_turbo_session(struct msm_vidc_inst *inst)
+{
+	return !!(inst->flags & VIDC_TURBO);
+}
+
+static inline bool is_thumbnail_session(struct msm_vidc_inst *inst)
+{
+	return !!(inst->flags & VIDC_THUMBNAIL);
+}
+
+static inline bool is_low_power_session(struct msm_vidc_inst *inst)
+{
+	return !!(inst->flags & VIDC_LOW_POWER);
+}
+
+static inline bool is_realtime_session(struct msm_vidc_inst *inst)
+{
+	return !!(inst->flags & VIDC_REALTIME);
+}
+
+int msm_comm_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl)
+{
+	return v4l2_g_ctrl(&inst->ctrl_handler, ctrl);
+}
+
+int msm_comm_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl)
+{
+	return v4l2_s_ctrl(NULL, &inst->ctrl_handler, ctrl);
+}
+
+int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id)
+{
+	int rc = 0;
+	struct v4l2_control ctrl = {
+		.id = id,
+	};
+
+	rc = msm_comm_g_ctrl(inst, &ctrl);
+	return rc ?: ctrl.value;
+}
+
+static struct v4l2_ctrl **get_super_cluster(struct msm_vidc_inst *inst,
+				int num_ctrls)
+{
+	int c = 0;
+	struct v4l2_ctrl **cluster = kmalloc(sizeof(struct v4l2_ctrl *) *
+			num_ctrls, GFP_KERNEL);
+
+	if (!cluster || !inst)
+		return NULL;
+
+	for (c = 0; c < num_ctrls; c++)
+		cluster[c] =  inst->ctrls[c];
+
+	return cluster;
+}
+
+int msm_comm_ctrl_init(struct msm_vidc_inst *inst,
+		struct msm_vidc_ctrl *drv_ctrls, u32 num_ctrls,
+		const struct v4l2_ctrl_ops *ctrl_ops)
+{
+	int idx = 0;
+	struct v4l2_ctrl_config ctrl_cfg = {0};
+	int ret_val = 0;
+
+	if (!inst || !drv_ctrls || !ctrl_ops || !num_ctrls) {
+		dprintk(VIDC_ERR, "%s - invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	inst->ctrls = kcalloc(num_ctrls, sizeof(struct v4l2_ctrl *),
+				GFP_KERNEL);
+	if (!inst->ctrls) {
+		dprintk(VIDC_ERR, "%s - failed to allocate ctrl\n", __func__);
+		return -ENOMEM;
+	}
+
+	ret_val = v4l2_ctrl_handler_init(&inst->ctrl_handler, num_ctrls);
+
+	if (ret_val) {
+		dprintk(VIDC_ERR, "CTRL ERR: Control handler init failed, %d\n",
+				inst->ctrl_handler.error);
+		return ret_val;
+	}
+
+	for (; idx < num_ctrls; idx++) {
+		struct v4l2_ctrl *ctrl = NULL;
+
+		if (1) {
+			/*add private control*/
+			ctrl_cfg.def = drv_ctrls[idx].default_value;
+			ctrl_cfg.flags = 0;
+			ctrl_cfg.id = drv_ctrls[idx].id;
+			ctrl_cfg.max = drv_ctrls[idx].maximum;
+			ctrl_cfg.min = drv_ctrls[idx].minimum;
+			ctrl_cfg.menu_skip_mask =
+				drv_ctrls[idx].menu_skip_mask;
+			ctrl_cfg.name = drv_ctrls[idx].name;
+			ctrl_cfg.ops = ctrl_ops;
+			ctrl_cfg.step = drv_ctrls[idx].step;
+			ctrl_cfg.type = drv_ctrls[idx].type;
+			ctrl_cfg.qmenu = drv_ctrls[idx].qmenu;
+
+			ctrl = v4l2_ctrl_new_custom(&inst->ctrl_handler,
+					&ctrl_cfg, NULL);
+		} else {
+			if (drv_ctrls[idx].type == V4L2_CTRL_TYPE_MENU) {
+				ctrl = v4l2_ctrl_new_std_menu(
+					&inst->ctrl_handler,
+					ctrl_ops,
+					drv_ctrls[idx].id,
+					drv_ctrls[idx].maximum,
+					drv_ctrls[idx].menu_skip_mask,
+					drv_ctrls[idx].default_value);
+			} else {
+				ctrl = v4l2_ctrl_new_std(&inst->ctrl_handler,
+					ctrl_ops,
+					drv_ctrls[idx].id,
+					drv_ctrls[idx].minimum,
+					drv_ctrls[idx].maximum,
+					drv_ctrls[idx].step,
+					drv_ctrls[idx].default_value);
+			}
+		}
+
+		if (!ctrl) {
+			dprintk(VIDC_ERR, "%s - invalid ctrl %s\n", __func__,
+				 drv_ctrls[idx].name);
+			return -EINVAL;
+		}
+
+		ret_val = inst->ctrl_handler.error;
+		if (ret_val) {
+			dprintk(VIDC_ERR,
+				"Error adding ctrl (%s) to ctrl handle, %d\n",
+				drv_ctrls[idx].name, inst->ctrl_handler.error);
+			return ret_val;
+		}
+
+		ctrl->flags |= drv_ctrls[idx].flags;
+		inst->ctrls[idx] = ctrl;
+	}
+
+	/* Construct a super cluster of all controls */
+	inst->cluster = get_super_cluster(inst, num_ctrls);
+	if (!inst->cluster) {
+		dprintk(VIDC_WARN,
+			"Failed to setup super cluster\n");
+		return -EINVAL;
+	}
+
+	v4l2_ctrl_cluster(num_ctrls, inst->cluster);
+
+	return ret_val;
+}
+
+int msm_comm_ctrl_deinit(struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	kfree(inst->ctrls);
+	kfree(inst->cluster);
+	v4l2_ctrl_handler_free(&inst->ctrl_handler);
+
+	return 0;
+}
+
+enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst)
+{
+	switch (msm_comm_g_ctrl_for_id(inst,
+				V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE)) {
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY:
+			return HAL_VIDEO_DECODER_SECONDARY;
+		case V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY:
+		default:
+			return HAL_VIDEO_DECODER_PRIMARY;
+	}
+}
+
+static int msm_comm_get_mbs_per_sec(struct msm_vidc_inst *inst)
+{
+	int output_port_mbs, capture_port_mbs;
+	int fps;
+
+	output_port_mbs = inst->in_reconfig ?
+			NUM_MBS_PER_FRAME(inst->reconfig_width,
+				inst->reconfig_height) :
+			NUM_MBS_PER_FRAME(inst->prop.width[OUTPUT_PORT],
+				inst->prop.height[OUTPUT_PORT]);
+
+	capture_port_mbs = NUM_MBS_PER_FRAME(inst->prop.width[CAPTURE_PORT],
+		inst->prop.height[CAPTURE_PORT]);
+
+	if (inst->operating_rate) {
+		fps = (inst->operating_rate >> 16) ?
+			inst->operating_rate >> 16 : 1;
+		/*
+		 * Check if operating rate is less than fps.
+		 * If Yes, then use fps to scale clocks
+		 */
+		fps = fps > inst->prop.fps ? fps : inst->prop.fps;
+		return max(output_port_mbs, capture_port_mbs) * fps;
+	} else {
+		return max(output_port_mbs, capture_port_mbs) * inst->prop.fps;
+	}
+}
+
+int msm_comm_get_inst_load(struct msm_vidc_inst *inst,
+		enum load_calc_quirks quirks)
+{
+	int load = 0;
+
+	mutex_lock(&inst->lock);
+
+	if (!(inst->state >= MSM_VIDC_OPEN_DONE &&
+		inst->state < MSM_VIDC_STOP_DONE))
+		goto exit;
+
+	load = msm_comm_get_mbs_per_sec(inst);
+
+	if (is_thumbnail_session(inst)) {
+		if (quirks & LOAD_CALC_IGNORE_THUMBNAIL_LOAD)
+			load = 0;
+	}
+
+	if (msm_comm_turbo_session(inst)) {
+		if (!(quirks & LOAD_CALC_IGNORE_TURBO_LOAD))
+			load = inst->core->resources.max_load;
+	}
+
+	/*  Clock and Load calculations for REALTIME/NON-REALTIME
+	 *                        OPERATING RATE SET/NO OPERATING RATE SET
+	 *
+	 *                 | OPERATING RATE SET   | OPERATING RATE NOT SET |
+	 * ----------------|--------------------- |------------------------|
+	 * REALTIME        | load = res * op_rate |  load = res * fps      |
+	 *                 | clk  = res * op_rate |  clk  = res * fps      |
+	 * ----------------|----------------------|------------------------|
+	 * NON-REALTIME    | load = res * 1 fps   |  load = res * 1 fps    |
+	 *                 | clk  = res * op_rate |  clk  = res * fps      |
+	 * ----------------|----------------------|------------------------|
+	 */
+
+	if (!is_realtime_session(inst) &&
+		(quirks & LOAD_CALC_IGNORE_NON_REALTIME_LOAD)) {
+		if (!inst->prop.fps) {
+			dprintk(VIDC_INFO, "instance:%pK fps = 0\n", inst);
+			load = 0;
+		} else {
+			load = msm_comm_get_mbs_per_sec(inst) / inst->prop.fps;
+		}
+	}
+
+exit:
+	mutex_unlock(&inst->lock);
+	return load;
+}
+
+int msm_comm_get_load(struct msm_vidc_core *core,
+	enum session_type type, enum load_calc_quirks quirks)
+{
+	struct msm_vidc_inst *inst = NULL;
+	int num_mbs_per_sec = 0;
+
+	if (!core) {
+		dprintk(VIDC_ERR, "Invalid args: %pK\n", core);
+		return -EINVAL;
+	}
+
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list) {
+		if (inst->session_type != type)
+			continue;
+
+		num_mbs_per_sec += msm_comm_get_inst_load(inst, quirks);
+	}
+	mutex_unlock(&core->lock);
+
+	return num_mbs_per_sec;
+}
+
+enum hal_domain get_hal_domain(int session_type)
+{
+	enum hal_domain domain;
+
+	switch (session_type) {
+	case MSM_VIDC_ENCODER:
+		domain = HAL_VIDEO_DOMAIN_ENCODER;
+		break;
+	case MSM_VIDC_DECODER:
+		domain = HAL_VIDEO_DOMAIN_DECODER;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Wrong domain\n");
+		domain = HAL_UNUSED_DOMAIN;
+		break;
+	}
+
+	return domain;
+}
+
+enum hal_video_codec get_hal_codec(int fourcc)
+{
+	enum hal_video_codec codec;
+
+	switch (fourcc) {
+	case V4L2_PIX_FMT_H264:
+	case V4L2_PIX_FMT_H264_NO_SC:
+		codec = HAL_VIDEO_CODEC_H264;
+		break;
+	case V4L2_PIX_FMT_H264_MVC:
+		codec = HAL_VIDEO_CODEC_MVC;
+		break;
+	case V4L2_PIX_FMT_H263:
+		codec = HAL_VIDEO_CODEC_H263;
+		break;
+	case V4L2_PIX_FMT_MPEG1:
+		codec = HAL_VIDEO_CODEC_MPEG1;
+		break;
+	case V4L2_PIX_FMT_MPEG2:
+		codec = HAL_VIDEO_CODEC_MPEG2;
+		break;
+	case V4L2_PIX_FMT_MPEG4:
+		codec = HAL_VIDEO_CODEC_MPEG4;
+		break;
+	case V4L2_PIX_FMT_VC1_ANNEX_G:
+	case V4L2_PIX_FMT_VC1_ANNEX_L:
+		codec = HAL_VIDEO_CODEC_VC1;
+		break;
+	case V4L2_PIX_FMT_VP8:
+		codec = HAL_VIDEO_CODEC_VP8;
+		break;
+	case V4L2_PIX_FMT_VP9:
+		codec = HAL_VIDEO_CODEC_VP9;
+		break;
+	case V4L2_PIX_FMT_HEVC:
+		codec = HAL_VIDEO_CODEC_HEVC;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Wrong codec: %d\n", fourcc);
+		codec = HAL_UNUSED_CODEC;
+		break;
+	}
+
+	return codec;
+}
+
+static enum hal_uncompressed_format get_hal_uncompressed(int fourcc)
+{
+	enum hal_uncompressed_format format = HAL_UNUSED_COLOR;
+
+	switch (fourcc) {
+	case V4L2_PIX_FMT_NV12:
+		format = HAL_COLOR_FORMAT_NV12;
+		break;
+	case V4L2_PIX_FMT_NV21:
+		format = HAL_COLOR_FORMAT_NV21;
+		break;
+	case V4L2_PIX_FMT_NV12_UBWC:
+		format = HAL_COLOR_FORMAT_NV12_UBWC;
+		break;
+	case V4L2_PIX_FMT_NV12_TP10_UBWC:
+		format = HAL_COLOR_FORMAT_NV12_TP10_UBWC;
+		break;
+	case V4L2_PIX_FMT_RGB32:
+		format = HAL_COLOR_FORMAT_RGBA8888;
+		break;
+	default:
+		format = HAL_UNUSED_COLOR;
+		break;
+	}
+
+	return format;
+}
+
+static int msm_comm_vote_bus(struct msm_vidc_core *core)
+{
+	int rc = 0, vote_data_count = 0, i = 0;
+	struct hfi_device *hdev;
+	struct msm_vidc_inst *inst = NULL;
+	struct vidc_bus_vote_data *vote_data = NULL;
+	unsigned long core_freq = 0;
+
+	if (!core) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, core);
+		return -EINVAL;
+	}
+
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "%s Invalid device handle: %pK\n",
+			__func__, hdev);
+		return -EINVAL;
+	}
+
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list)
+		++vote_data_count;
+
+	vote_data = kcalloc(vote_data_count, sizeof(*vote_data),
+			GFP_TEMPORARY);
+	if (!vote_data) {
+		dprintk(VIDC_ERR, "%s: failed to allocate memory\n", __func__);
+		rc = -ENOMEM;
+		goto fail_alloc;
+	}
+
+	core_freq = call_hfi_op(hdev, get_core_clock_rate,
+			hdev->hfi_device_data, 0);
+
+	list_for_each_entry(inst, &core->instances, list) {
+		int codec = 0, yuv = 0;
+
+		codec = inst->session_type == MSM_VIDC_DECODER ?
+			inst->fmts[OUTPUT_PORT].fourcc :
+			inst->fmts[CAPTURE_PORT].fourcc;
+
+		yuv = inst->session_type == MSM_VIDC_DECODER ?
+			inst->fmts[CAPTURE_PORT].fourcc :
+			inst->fmts[OUTPUT_PORT].fourcc;
+
+		vote_data[i].domain = get_hal_domain(inst->session_type);
+		vote_data[i].codec = get_hal_codec(codec);
+		vote_data[i].width =  max(inst->prop.width[CAPTURE_PORT],
+			inst->prop.width[OUTPUT_PORT]);
+		vote_data[i].height = max(inst->prop.height[CAPTURE_PORT],
+			inst->prop.height[OUTPUT_PORT]);
+
+		if (inst->operating_rate)
+			vote_data[i].fps = (inst->operating_rate >> 16) ?
+				inst->operating_rate >> 16 : 1;
+		else
+			vote_data[i].fps = inst->prop.fps;
+
+		if (msm_comm_turbo_session(inst))
+			vote_data[i].power_mode = VIDC_POWER_TURBO;
+		else if (is_low_power_session(inst))
+			vote_data[i].power_mode = VIDC_POWER_LOW;
+		else
+			vote_data[i].power_mode = VIDC_POWER_NORMAL;
+		if (i == 0) {
+			vote_data[i].imem_ab_tbl = core->resources.imem_ab_tbl;
+			vote_data[i].imem_ab_tbl_size =
+				core->resources.imem_ab_tbl_size;
+			vote_data[i].core_freq = core_freq;
+		}
+
+		/*
+		 * TODO: support for OBP-DBP split mode hasn't been yet
+		 * implemented, once it is, this part of code needs to be
+		 * revisited since passing in accurate information to the bus
+		 * governor will drastically reduce bandwidth
+		 */
+		vote_data[i].color_formats[0] = get_hal_uncompressed(yuv);
+		vote_data[i].num_formats = 1;
+		i++;
+	}
+	mutex_unlock(&core->lock);
+
+	rc = call_hfi_op(hdev, vote_bus, hdev->hfi_device_data, vote_data,
+			vote_data_count);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to scale bus: %d\n", rc);
+
+	kfree(vote_data);
+	return rc;
+
+fail_alloc:
+	mutex_unlock(&core->lock);
+	return rc;
+}
+
+struct msm_vidc_core *get_vidc_core(int core_id)
+{
+	struct msm_vidc_core *core;
+	int found = 0;
+
+	if (core_id > MSM_VIDC_CORES_MAX) {
+		dprintk(VIDC_ERR, "Core id = %d is greater than max = %d\n",
+			core_id, MSM_VIDC_CORES_MAX);
+		return NULL;
+	}
+	mutex_lock(&vidc_driver->lock);
+	list_for_each_entry(core, &vidc_driver->cores, list) {
+		if (core->id == core_id) {
+			found = 1;
+			break;
+		}
+	}
+	mutex_unlock(&vidc_driver->lock);
+	if (found)
+		return core;
+	return NULL;
+}
+
+const struct msm_vidc_format *msm_comm_get_pixel_fmt_index(
+	const struct msm_vidc_format fmt[], int size, int index, int fmt_type)
+{
+	int i, k = 0;
+
+	if (!fmt || index < 0) {
+		dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK, index = %d\n",
+						fmt, index);
+		return NULL;
+	}
+	for (i = 0; i < size; i++) {
+		if (fmt[i].type != fmt_type)
+			continue;
+		if (k == index)
+			break;
+		k++;
+	}
+	if (i == size) {
+		dprintk(VIDC_INFO, "Format not found\n");
+		return NULL;
+	}
+	return &fmt[i];
+}
+struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc(
+	struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type)
+{
+	int i;
+
+	if (!fmt) {
+		dprintk(VIDC_ERR, "Invalid inputs, fmt = %pK\n", fmt);
+		return NULL;
+	}
+	for (i = 0; i < size; i++) {
+		if (fmt[i].fourcc == fourcc)
+			break;
+	}
+	if (i == size) {
+		dprintk(VIDC_INFO, "Format not found\n");
+		return NULL;
+	}
+	return &fmt[i];
+}
+
+struct buf_queue *msm_comm_get_vb2q(
+		struct msm_vidc_inst *inst, enum v4l2_buf_type type)
+{
+	if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+		return &inst->bufq[CAPTURE_PORT];
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE)
+		return &inst->bufq[OUTPUT_PORT];
+	return NULL;
+}
+
+static void handle_sys_init_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_core *core;
+	struct vidc_hal_sys_init_done *sys_init_msg;
+	u32 index;
+
+	if (!IS_HAL_SYS_CMD(cmd)) {
+		dprintk(VIDC_ERR, "%s - invalid cmd\n", __func__);
+		return;
+	}
+
+	index = SYS_MSG_INDEX(cmd);
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for sys init\n");
+		return;
+	}
+	core = get_vidc_core(response->device_id);
+	if (!core) {
+		dprintk(VIDC_ERR, "Wrong device_id received\n");
+		return;
+	}
+	sys_init_msg = &response->data.sys_init_done;
+	if (!sys_init_msg) {
+		dprintk(VIDC_ERR, "sys_init_done message not proper\n");
+		return;
+	}
+
+	core->enc_codec_supported = sys_init_msg->enc_codec_supported;
+	core->dec_codec_supported = sys_init_msg->dec_codec_supported;
+
+	/* This should come from sys_init_done */
+	core->resources.max_inst_count =
+		sys_init_msg->max_sessions_supported ? :
+		MAX_SUPPORTED_INSTANCES;
+
+	core->resources.max_secure_inst_count =
+		core->resources.max_secure_inst_count ? :
+		core->resources.max_inst_count;
+
+	if (core->id == MSM_VIDC_CORE_VENUS &&
+		(core->dec_codec_supported & HAL_VIDEO_CODEC_H264))
+		core->dec_codec_supported |=
+			HAL_VIDEO_CODEC_MVC;
+
+	core->codec_count = sys_init_msg->codec_count;
+	memcpy(core->capabilities, sys_init_msg->capabilities,
+		sys_init_msg->codec_count * sizeof(struct msm_vidc_capability));
+
+	dprintk(VIDC_DBG,
+		"%s: supported_codecs[%d]: enc = %#x, dec = %#x\n",
+		__func__, core->codec_count, core->enc_codec_supported,
+		core->dec_codec_supported);
+
+	complete(&(core->completions[index]));
+}
+
+static void put_inst(struct msm_vidc_inst *inst)
+{
+	void put_inst_helper(struct kref *kref)
+	{
+		struct msm_vidc_inst *inst = container_of(kref,
+				struct msm_vidc_inst, kref);
+
+		msm_vidc_destroy(inst);
+	}
+
+	if (!inst)
+		return;
+
+	kref_put(&inst->kref, put_inst_helper);
+}
+
+static struct msm_vidc_inst *get_inst(struct msm_vidc_core *core,
+		void *session_id)
+{
+	struct msm_vidc_inst *inst = NULL;
+	bool matches = false;
+
+	if (!core || !session_id)
+		return NULL;
+
+	mutex_lock(&core->lock);
+	/*
+	 * This is as good as !list_empty(!inst->list), but at this point
+	 * we don't really know if inst was kfree'd via close syscall before
+	 * hardware could respond.  So manually walk thru the list of active
+	 * sessions
+	 */
+	list_for_each_entry(inst, &core->instances, list) {
+		if (inst == session_id) {
+			/*
+			 * Even if the instance is valid, we really shouldn't
+			 * be receiving or handling callbacks when we've deleted
+			 * our session with HFI
+			 */
+			matches = !!inst->session;
+			break;
+		}
+	}
+
+	/*
+	 * kref_* is atomic_int backed, so no need for inst->lock.  But we can
+	 * always acquire inst->lock and release it in put_inst for a stronger
+	 * locking system.
+	 */
+	inst = (matches && kref_get_unless_zero(&inst->kref)) ? inst : NULL;
+	mutex_unlock(&core->lock);
+
+	return inst;
+}
+
+static void handle_session_release_buf_done(enum hal_command_response cmd,
+	void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct internal_buf *buf;
+	struct list_head *ptr, *next;
+	struct hal_buffer_info *buffer;
+	u32 buf_found = false;
+	u32 address;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Invalid release_buf_done response\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	buffer = &response->data.buffer_info;
+	address = buffer->buffer_addr;
+
+	mutex_lock(&inst->scratchbufs.lock);
+	list_for_each_safe(ptr, next, &inst->scratchbufs.list) {
+		buf = list_entry(ptr, struct internal_buf, list);
+		if (address == (u32)buf->handle->device_addr) {
+			dprintk(VIDC_DBG, "releasing scratch: %pa\n",
+					&buf->handle->device_addr);
+			buf_found = true;
+		}
+	}
+	mutex_unlock(&inst->scratchbufs.lock);
+
+	mutex_lock(&inst->persistbufs.lock);
+	list_for_each_safe(ptr, next, &inst->persistbufs.list) {
+		buf = list_entry(ptr, struct internal_buf, list);
+		if (address == (u32)buf->handle->device_addr) {
+			dprintk(VIDC_DBG, "releasing persist: %pa\n",
+					&buf->handle->device_addr);
+			buf_found = true;
+		}
+	}
+	mutex_unlock(&inst->persistbufs.lock);
+
+	if (!buf_found)
+		dprintk(VIDC_ERR, "invalid buffer received from firmware");
+	if (IS_HAL_SESSION_CMD(cmd))
+		complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
+	else
+		dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
+
+	put_inst(inst);
+}
+
+static void handle_sys_release_res_done(
+		enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_core *core;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for sys init\n");
+		return;
+	}
+	core = get_vidc_core(response->device_id);
+	if (!core) {
+		dprintk(VIDC_ERR, "Wrong device_id received\n");
+		return;
+	}
+	complete(&core->completions[
+			SYS_MSG_INDEX(HAL_SYS_RELEASE_RESOURCE_DONE)]);
+}
+
+static void change_inst_state(struct msm_vidc_inst *inst,
+	enum instance_state state)
+{
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid parameter %s\n", __func__);
+		return;
+	}
+	mutex_lock(&inst->lock);
+	if (inst->state == MSM_VIDC_CORE_INVALID) {
+		dprintk(VIDC_DBG,
+			"Inst: %pK is in bad state can't change state to %d\n",
+			inst, state);
+		goto exit;
+	}
+	dprintk(VIDC_DBG, "Moved inst: %pK from state: %d to state: %d\n",
+		   inst, inst->state, state);
+	inst->state = state;
+exit:
+	mutex_unlock(&inst->lock);
+}
+
+static int signal_session_msg_receipt(enum hal_command_response cmd,
+		struct msm_vidc_inst *inst)
+{
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid(%pK) instance id\n", inst);
+		return -EINVAL;
+	}
+	if (IS_HAL_SESSION_CMD(cmd)) {
+		complete(&inst->completions[SESSION_MSG_INDEX(cmd)]);
+	} else {
+		dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int wait_for_sess_signal_receipt(struct msm_vidc_inst *inst,
+	enum hal_command_response cmd)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!IS_HAL_SESSION_CMD(cmd)) {
+		dprintk(VIDC_ERR, "Invalid inst cmd response: %d\n", cmd);
+		return -EINVAL;
+	}
+	hdev = (struct hfi_device *)(inst->core->device);
+	rc = wait_for_completion_timeout(
+		&inst->completions[SESSION_MSG_INDEX(cmd)],
+		msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
+	if (!rc) {
+		dprintk(VIDC_ERR, "Wait interrupted or timed out: %d\n",
+				SESSION_MSG_INDEX(cmd));
+		msm_comm_kill_session(inst);
+		call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+		dprintk(VIDC_ERR,
+			"sess resp timeout can potentially crash the system\n");
+		msm_comm_print_debug_info(inst);
+		msm_vidc_handle_hw_error(inst->core);
+		rc = -EIO;
+	} else {
+		rc = 0;
+	}
+	return rc;
+}
+
+static int wait_for_state(struct msm_vidc_inst *inst,
+	enum instance_state flipped_state,
+	enum instance_state desired_state,
+	enum hal_command_response hal_cmd)
+{
+	int rc = 0;
+
+	if (IS_ALREADY_IN_STATE(flipped_state, desired_state)) {
+		dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n",
+						inst, inst->state);
+		goto err_same_state;
+	}
+	dprintk(VIDC_DBG, "Waiting for hal_cmd: %d\n", hal_cmd);
+	rc = wait_for_sess_signal_receipt(inst, hal_cmd);
+	if (!rc)
+		change_inst_state(inst, desired_state);
+err_same_state:
+	return rc;
+}
+
+void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type)
+{
+	struct v4l2_event event = {.id = 0, .type = event_type};
+
+	v4l2_event_queue_fh(&inst->event_handler, &event);
+}
+
+static void msm_comm_generate_max_clients_error(struct msm_vidc_inst *inst)
+{
+	enum hal_command_response cmd = HAL_SESSION_ERROR;
+	struct msm_vidc_cb_cmd_done response = {0};
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
+		return;
+	}
+	dprintk(VIDC_ERR, "%s: Too many clients\n", __func__);
+	response.session_id = inst;
+	response.status = VIDC_ERR_MAX_CLIENTS;
+	handle_session_error(cmd, (void *)&response);
+}
+
+static void print_cap(const char *type,
+		struct hal_capability_supported *cap)
+{
+	dprintk(VIDC_DBG,
+		"%-24s: %-8d %-8d %-8d\n",
+		type, cap->min, cap->max, cap->step_size);
+}
+
+static void handle_session_init_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst = NULL;
+	struct vidc_hal_session_init_done *session_init_done = NULL;
+	struct msm_vidc_capability *capability = NULL;
+	struct hfi_device *hdev;
+	struct msm_vidc_core *core;
+	u32 i, codec;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+				"Failed to get valid response for session init\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+		response->session_id);
+
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	if (response->status) {
+		dprintk(VIDC_ERR,
+			"Session init response from FW : %#x\n",
+			response->status);
+		if (response->status == VIDC_ERR_MAX_CLIENTS)
+			msm_comm_generate_max_clients_error(inst);
+		else
+			msm_comm_generate_session_error(inst);
+
+		signal_session_msg_receipt(cmd, inst);
+		put_inst(inst);
+		return;
+	}
+
+	core = inst->core;
+	hdev = inst->core->device;
+	codec = inst->session_type == MSM_VIDC_DECODER ?
+			inst->fmts[OUTPUT_PORT].fourcc :
+			inst->fmts[CAPTURE_PORT].fourcc;
+
+	/* check if capabilities are available for this session */
+	for (i = 0; i < VIDC_MAX_SESSIONS; i++) {
+		if (core->capabilities[i].codec ==
+				get_hal_codec(codec) &&
+			core->capabilities[i].domain ==
+				get_hal_domain(inst->session_type)) {
+			capability = &core->capabilities[i];
+			break;
+		}
+	}
+
+	if (capability) {
+		dprintk(VIDC_DBG,
+			"%s: capabilities available for codec 0x%x, domain %#x\n",
+			__func__, capability->codec, capability->domain);
+		memcpy(&inst->capability, capability,
+			sizeof(struct msm_vidc_capability));
+	} else {
+		session_init_done = (struct vidc_hal_session_init_done *)
+				&response->data.session_init_done;
+		if (!session_init_done) {
+			dprintk(VIDC_ERR,
+				"%s: Failed to get valid response for session init\n",
+				__func__);
+			return;
+		}
+		capability = &session_init_done->capability;
+		dprintk(VIDC_DBG,
+			"%s: got capabilities for codec 0x%x, domain 0x%x\n",
+			__func__, capability->codec,
+			capability->domain);
+		memcpy(&inst->capability, capability,
+			sizeof(struct msm_vidc_capability));
+	}
+	inst->capability.pixelprocess_capabilities =
+		call_hfi_op(hdev, get_core_capabilities, hdev->hfi_device_data);
+
+	dprintk(VIDC_DBG,
+		"Capability type : min      max      step size\n");
+	print_cap("width", &inst->capability.width);
+	print_cap("height", &inst->capability.height);
+	print_cap("mbs_per_frame", &inst->capability.mbs_per_frame);
+	print_cap("frame_rate", &inst->capability.frame_rate);
+	print_cap("scale_x", &inst->capability.scale_x);
+	print_cap("scale_y", &inst->capability.scale_y);
+	print_cap("hier_p", &inst->capability.hier_p);
+	print_cap("ltr_count", &inst->capability.ltr_count);
+	print_cap("mbs_per_sec_low_power",
+		&inst->capability.mbs_per_sec_power_save);
+
+	signal_session_msg_receipt(cmd, inst);
+	put_inst(inst);
+}
+
+static void handle_event_change(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_inst *inst = NULL;
+	struct msm_vidc_cb_event *event_notify = data;
+	int event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+	struct v4l2_event seq_changed_event = {0};
+	int rc = 0;
+	struct hfi_device *hdev;
+	u32 *ptr = NULL;
+
+	if (!event_notify) {
+		dprintk(VIDC_WARN, "Got an empty event from hfi\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(event_notify->device_id),
+			event_notify->session_id);
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		goto err_bad_event;
+	}
+	hdev = inst->core->device;
+
+	switch (event_notify->hal_event_type) {
+	case HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES:
+		event = V4L2_EVENT_SEQ_CHANGED_SUFFICIENT;
+
+		if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY) {
+			struct hal_frame_size frame_sz;
+
+			frame_sz.buffer_type = HAL_BUFFER_OUTPUT2;
+			frame_sz.width = event_notify->width;
+			frame_sz.height = event_notify->height;
+			dprintk(VIDC_DBG,
+				"Update OPB dimensions to firmware if buffer requirements are sufficient\n");
+			rc = msm_comm_try_set_prop(inst,
+				HAL_PARAM_FRAME_SIZE, &frame_sz);
+		}
+
+		dprintk(VIDC_DBG,
+			"send session_continue after sufficient event\n");
+		rc = call_hfi_op(hdev, session_continue,
+				(void *) inst->session);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s - failed to send session_continue\n",
+				__func__);
+			goto err_bad_event;
+		}
+		break;
+	case HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES:
+		event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+		break;
+	case HAL_EVENT_RELEASE_BUFFER_REFERENCE:
+	{
+		struct v4l2_event buf_event = {0};
+		struct buffer_info *binfo = NULL, *temp = NULL;
+		u32 *ptr = NULL;
+
+		dprintk(VIDC_DBG, "%s - inst: %pK buffer: %pa extra: %pa\n",
+				__func__, inst, &event_notify->packet_buffer,
+				&event_notify->extra_data_buffer);
+
+		if (inst->state == MSM_VIDC_CORE_INVALID ||
+				inst->core->state == VIDC_CORE_INVALID) {
+			dprintk(VIDC_DBG,
+					"Event release buf ref received in invalid state - discard\n");
+			goto err_bad_event;
+		}
+
+		/*
+		 * Get the buffer_info entry for the
+		 * device address.
+		 */
+		binfo = device_to_uvaddr(&inst->registeredbufs,
+				event_notify->packet_buffer);
+		if (!binfo) {
+			dprintk(VIDC_ERR,
+					"%s buffer not found in registered list\n",
+					__func__);
+			goto err_bad_event;
+		}
+
+		/* Fill event data to be sent to client*/
+		buf_event.type = V4L2_EVENT_RELEASE_BUFFER_REFERENCE;
+		ptr = (u32 *)buf_event.u.data;
+		ptr[0] = binfo->fd[0];
+		ptr[1] = binfo->buff_off[0];
+
+		dprintk(VIDC_DBG,
+				"RELEASE REFERENCE EVENT FROM F/W - fd = %d offset = %d\n",
+				ptr[0], ptr[1]);
+
+		/* Decrement buffer reference count*/
+		mutex_lock(&inst->registeredbufs.lock);
+		list_for_each_entry(temp, &inst->registeredbufs.list,
+				list) {
+			if (temp == binfo) {
+				buf_ref_put(inst, binfo);
+				break;
+			}
+		}
+
+		/*
+		 * Release buffer and remove from list
+		 * if reference goes to zero.
+		 */
+		if (unmap_and_deregister_buf(inst, binfo))
+			dprintk(VIDC_ERR,
+					"%s: buffer unmap failed\n", __func__);
+		mutex_unlock(&inst->registeredbufs.lock);
+
+		/*send event to client*/
+		v4l2_event_queue_fh(&inst->event_handler, &buf_event);
+		goto err_bad_event;
+	}
+	default:
+		break;
+	}
+
+	/* Bit depth and pic struct changed event are combined into a single
+	 * event (insufficient event) for the userspace. Currently bitdepth
+	 * changes is only for HEVC and interlaced support is for all
+	 * codecs except HEVC
+	 * event data is now as follows:
+	 * u32 *ptr = seq_changed_event.u.data;
+	 * ptr[0] = height
+	 * ptr[1] = width
+	 * ptr[2] = flag to indicate bit depth or/and pic struct changed
+	 * ptr[3] = bit depth
+	 * ptr[4] = pic struct (progressive or interlaced)
+	 * ptr[5] = colour space
+	 */
+
+	ptr = (u32 *)seq_changed_event.u.data;
+
+	if (ptr != NULL) {
+		ptr[2] = 0x0;
+		ptr[3] = inst->bit_depth;
+		ptr[4] = inst->pic_struct;
+		ptr[5] = inst->colour_space;
+
+		if (inst->bit_depth != event_notify->bit_depth) {
+			inst->bit_depth = event_notify->bit_depth;
+			ptr[2] |= V4L2_EVENT_BITDEPTH_FLAG;
+			ptr[3] = inst->bit_depth;
+			event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+			dprintk(VIDC_DBG,
+				"V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to bit-depth change\n");
+		}
+
+		if (inst->pic_struct != event_notify->pic_struct) {
+			inst->pic_struct = event_notify->pic_struct;
+			event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+			ptr[2] |= V4L2_EVENT_PICSTRUCT_FLAG;
+			ptr[4] = inst->pic_struct;
+			dprintk(VIDC_DBG,
+				"V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to pic-struct change\n");
+		}
+
+		if (inst->bit_depth == MSM_VIDC_BIT_DEPTH_10
+				&& inst->colour_space !=
+					event_notify->colour_space) {
+			inst->colour_space = event_notify->colour_space;
+			event = V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT;
+			ptr[2] |= V4L2_EVENT_COLOUR_SPACE_FLAG;
+			ptr[5] = inst->colour_space;
+			dprintk(VIDC_DBG,
+				"V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT due to colour space change\n");
+		}
+
+	}
+
+	if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
+		dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT\n");
+		inst->reconfig_height = event_notify->height;
+		inst->reconfig_width = event_notify->width;
+		inst->in_reconfig = true;
+	} else {
+		dprintk(VIDC_DBG, "V4L2_EVENT_SEQ_CHANGED_SUFFICIENT\n");
+		dprintk(VIDC_DBG,
+			"event_notify->height = %d event_notify->width = %d\n",
+			event_notify->height,
+			event_notify->width);
+		inst->prop.height[OUTPUT_PORT] = event_notify->height;
+		inst->prop.width[OUTPUT_PORT] = event_notify->width;
+	}
+
+	if (inst->session_type == MSM_VIDC_DECODER)
+		msm_dcvs_init_load(inst);
+
+	rc = msm_vidc_check_session_supported(inst);
+	if (!rc) {
+		seq_changed_event.type = event;
+		if (event == V4L2_EVENT_SEQ_CHANGED_INSUFFICIENT) {
+			u32 *ptr = NULL;
+
+			ptr = (u32 *)seq_changed_event.u.data;
+			ptr[0] = event_notify->height;
+			ptr[1] = event_notify->width;
+		}
+		v4l2_event_queue_fh(&inst->event_handler, &seq_changed_event);
+	} else if (rc == -ENOTSUPP) {
+		msm_vidc_queue_v4l2_event(inst,
+				V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED);
+	} else if (rc == -EBUSY) {
+		msm_vidc_queue_v4l2_event(inst,
+				V4L2_EVENT_MSM_VIDC_HW_OVERLOAD);
+	}
+
+err_bad_event:
+	put_inst(inst);
+}
+
+static void handle_session_prop_info(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct getprop_buf *getprop;
+	struct msm_vidc_inst *inst;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for prop info\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	getprop = kzalloc(sizeof(*getprop), GFP_KERNEL);
+	if (!getprop) {
+		dprintk(VIDC_ERR, "%s: getprop kzalloc failed\n", __func__);
+		goto err_prop_info;
+	}
+
+	getprop->data = kmemdup(&response->data.property,
+			sizeof(union hal_get_property), GFP_KERNEL);
+	if (!getprop->data) {
+		dprintk(VIDC_ERR, "%s: kmemdup failed\n", __func__);
+		kfree(getprop);
+		goto err_prop_info;
+	}
+
+	mutex_lock(&inst->pending_getpropq.lock);
+	list_add_tail(&getprop->list, &inst->pending_getpropq.list);
+	mutex_unlock(&inst->pending_getpropq.lock);
+
+	signal_session_msg_receipt(cmd, inst);
+err_prop_info:
+	put_inst(inst);
+}
+
+static void handle_load_resource_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for load resource\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	if (response->status) {
+		dprintk(VIDC_ERR,
+				"Load resource response from FW : %#x\n",
+				response->status);
+		msm_comm_generate_session_error(inst);
+	}
+
+	put_inst(inst);
+}
+
+static void handle_start_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Failed to get valid response for start\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	signal_session_msg_receipt(cmd, inst);
+	put_inst(inst);
+}
+
+static void handle_stop_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Failed to get valid response for stop\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	signal_session_msg_receipt(cmd, inst);
+	put_inst(inst);
+}
+
+static void handle_release_res_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for release resource\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	signal_session_msg_receipt(cmd, inst);
+	put_inst(inst);
+}
+
+void validate_output_buffers(struct msm_vidc_inst *inst)
+{
+	struct internal_buf *binfo;
+	u32 buffers_owned_by_driver = 0;
+	struct hal_buffer_requirements *output_buf;
+
+	output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+
+	if (!output_buf) {
+		dprintk(VIDC_DBG,
+			"This output buffer not required, buffer_type: %x\n",
+			HAL_BUFFER_OUTPUT);
+		return;
+	}
+	mutex_lock(&inst->outputbufs.lock);
+	list_for_each_entry(binfo, &inst->outputbufs.list, list) {
+		if (binfo->buffer_ownership != DRIVER) {
+			dprintk(VIDC_DBG,
+				"This buffer is with FW %pa\n",
+				&binfo->handle->device_addr);
+			continue;
+		}
+		buffers_owned_by_driver++;
+	}
+	mutex_unlock(&inst->outputbufs.lock);
+
+	if (buffers_owned_by_driver != output_buf->buffer_count_actual)
+		dprintk(VIDC_WARN,
+			"OUTPUT Buffer count mismatch %d of %d\n",
+			buffers_owned_by_driver,
+			output_buf->buffer_count_actual);
+}
+
+int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst)
+{
+	struct internal_buf *binfo;
+	struct hfi_device *hdev;
+	struct msm_smem *handle;
+	struct vidc_frame_data frame_data = {0};
+	struct hal_buffer_requirements *output_buf, *extra_buf;
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+
+	output_buf = get_buff_req_buffer(inst, HAL_BUFFER_OUTPUT);
+	if (!output_buf) {
+		dprintk(VIDC_DBG,
+			"This output buffer not required, buffer_type: %x\n",
+			HAL_BUFFER_OUTPUT);
+		return 0;
+	}
+	dprintk(VIDC_DBG,
+		"output: num = %d, size = %d\n",
+		output_buf->buffer_count_actual,
+		output_buf->buffer_size);
+
+	extra_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT);
+
+	mutex_lock(&inst->outputbufs.lock);
+	list_for_each_entry(binfo, &inst->outputbufs.list, list) {
+		if (binfo->buffer_ownership != DRIVER)
+			continue;
+		handle = binfo->handle;
+		frame_data.alloc_len = output_buf->buffer_size;
+		frame_data.filled_len = 0;
+		frame_data.offset = 0;
+		frame_data.device_addr = handle->device_addr;
+		frame_data.flags = 0;
+		frame_data.extradata_addr = handle->device_addr +
+		output_buf->buffer_size;
+		frame_data.buffer_type = HAL_BUFFER_OUTPUT;
+		frame_data.extradata_size = extra_buf ?
+			extra_buf->buffer_size : 0;
+		rc = call_hfi_op(hdev, session_ftb,
+			(void *) inst->session, &frame_data);
+		binfo->buffer_ownership = FIRMWARE;
+	}
+	mutex_unlock(&inst->outputbufs.lock);
+
+	return 0;
+}
+
+static void handle_session_flush(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct v4l2_event flush_event = {0};
+	u32 *ptr = NULL;
+	enum hal_flush flush_type;
+	int rc;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Failed to get valid response for flush\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	if (msm_comm_get_stream_output_mode(inst) ==
+			HAL_VIDEO_DECODER_SECONDARY) {
+		validate_output_buffers(inst);
+		if (!inst->in_reconfig) {
+			rc = msm_comm_queue_output_buffers(inst);
+			if (rc) {
+				dprintk(VIDC_ERR,
+						"Failed to queue output buffers: %d\n",
+						rc);
+			}
+		}
+	}
+	atomic_dec(&inst->in_flush);
+	flush_event.type = V4L2_EVENT_MSM_VIDC_FLUSH_DONE;
+	ptr = (u32 *)flush_event.u.data;
+
+	flush_type = response->data.flush_type;
+	switch (flush_type) {
+	case HAL_FLUSH_INPUT:
+		ptr[0] = V4L2_QCOM_CMD_FLUSH_OUTPUT;
+		break;
+	case HAL_FLUSH_OUTPUT:
+		ptr[0] = V4L2_QCOM_CMD_FLUSH_CAPTURE;
+		break;
+	case HAL_FLUSH_ALL:
+		ptr[0] |= V4L2_QCOM_CMD_FLUSH_CAPTURE;
+		ptr[0] |= V4L2_QCOM_CMD_FLUSH_OUTPUT;
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid flush type received!");
+		goto exit;
+	}
+
+	dprintk(VIDC_DBG,
+		"Notify flush complete, flush_type: %x\n", flush_type);
+	v4l2_event_queue_fh(&inst->event_handler, &flush_event);
+
+exit:
+	put_inst(inst);
+}
+
+static void handle_session_error(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct hfi_device *hdev = NULL;
+	struct msm_vidc_inst *inst = NULL;
+	int event = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for session error\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	hdev = inst->core->device;
+	dprintk(VIDC_WARN, "Session error received for session %pK\n", inst);
+	change_inst_state(inst, MSM_VIDC_CORE_INVALID);
+
+	if (response->status == VIDC_ERR_MAX_CLIENTS) {
+		dprintk(VIDC_WARN, "Too many clients, rejecting %pK", inst);
+		event = V4L2_EVENT_MSM_VIDC_MAX_CLIENTS;
+
+		/*
+		 * Clean the HFI session now. Since inst->state is moved to
+		 * INVALID, forward thread doesn't know FW has valid session
+		 * or not. This is the last place driver knows that there is
+		 * no session in FW. Hence clean HFI session now.
+		 */
+
+		msm_comm_session_clean(inst);
+	} else if (response->status == VIDC_ERR_NOT_SUPPORTED) {
+		dprintk(VIDC_WARN, "Unsupported bitstream in %pK", inst);
+		event = V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED;
+	} else {
+		dprintk(VIDC_WARN, "Unknown session error (%d) for %pK\n",
+				response->status, inst);
+		event = V4L2_EVENT_MSM_VIDC_SYS_ERROR;
+	}
+
+	msm_vidc_queue_v4l2_event(inst, event);
+	put_inst(inst);
+}
+
+static void msm_comm_clean_notify_client(struct msm_vidc_core *core)
+{
+	struct msm_vidc_inst *inst = NULL;
+
+	if (!core) {
+		dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
+		return;
+	}
+
+	dprintk(VIDC_WARN, "%s: Core %pK\n", __func__, core);
+	mutex_lock(&core->lock);
+	core->state = VIDC_CORE_INVALID;
+
+	list_for_each_entry(inst, &core->instances, list) {
+		mutex_lock(&inst->lock);
+		inst->state = MSM_VIDC_CORE_INVALID;
+		mutex_unlock(&inst->lock);
+		dprintk(VIDC_WARN,
+			"%s Send sys error for inst %pK\n", __func__, inst);
+		msm_vidc_queue_v4l2_event(inst,
+				V4L2_EVENT_MSM_VIDC_SYS_ERROR);
+	}
+	mutex_unlock(&core->lock);
+}
+
+static void handle_sys_error(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_core *core = NULL;
+	struct hfi_device *hdev = NULL;
+	struct msm_vidc_inst *inst = NULL;
+	int rc = 0;
+
+	subsystem_crashed("venus");
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for sys error\n");
+		return;
+	}
+
+	core = get_vidc_core(response->device_id);
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Got SYS_ERR but unable to identify core\n");
+		return;
+	}
+
+	mutex_lock(&core->lock);
+	if (core->state == VIDC_CORE_INVALID ||
+		core->state == VIDC_CORE_UNINIT) {
+		dprintk(VIDC_ERR,
+			"%s: Core already moved to state %d\n",
+			 __func__, core->state);
+		mutex_unlock(&core->lock);
+		return;
+	}
+	mutex_unlock(&core->lock);
+
+	dprintk(VIDC_WARN, "SYS_ERROR %d received for core %pK\n", cmd, core);
+	msm_comm_clean_notify_client(core);
+
+	hdev = core->device;
+	mutex_lock(&core->lock);
+	if (core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_DBG, "Calling core_release\n");
+		rc = call_hfi_op(hdev, core_release,
+						 hdev->hfi_device_data);
+		if (rc) {
+			dprintk(VIDC_ERR, "core_release failed\n");
+			mutex_unlock(&core->lock);
+			return;
+		}
+		core->state = VIDC_CORE_UNINIT;
+	}
+	mutex_unlock(&core->lock);
+
+	msm_vidc_print_running_insts(core);
+	call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+	dprintk(VIDC_ERR,
+		"SYS_ERROR can potentially crash the system\n");
+
+	/*
+	 * For SYS_ERROR, there will not be any inst pointer.
+	 * Just grab one of the inst from instances list and
+	 * use it.
+	 */
+
+	mutex_lock(&core->lock);
+	inst = list_first_entry_or_null(&core->instances,
+		struct msm_vidc_inst, list);
+	mutex_unlock(&core->lock);
+
+	msm_comm_print_debug_info(inst);
+
+	msm_vidc_handle_hw_error(core);
+}
+
+void msm_comm_session_clean(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev = NULL;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid params\n", __func__);
+		return;
+	}
+
+	hdev = inst->core->device;
+	mutex_lock(&inst->lock);
+	if (hdev && inst->session) {
+		dprintk(VIDC_DBG, "cleaning up instance: %pK\n", inst);
+		rc = call_hfi_op(hdev, session_clean,
+				(void *)inst->session);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Session clean failed :%pK\n", inst);
+		}
+		inst->session = NULL;
+	}
+	mutex_unlock(&inst->lock);
+}
+
+static void handle_session_close(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_cmd_done *response = data;
+	struct msm_vidc_inst *inst;
+
+	if (!response) {
+		dprintk(VIDC_ERR,
+			"Failed to get valid response for session close\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	signal_session_msg_receipt(cmd, inst);
+	show_stats(inst);
+	put_inst(inst);
+}
+
+static struct vb2_buffer *get_vb_from_device_addr(struct buf_queue *bufq,
+		unsigned long dev_addr)
+{
+	struct vb2_buffer *vb = NULL;
+	struct vb2_queue *q = NULL;
+	int found = 0;
+
+	if (!bufq) {
+		dprintk(VIDC_ERR, "Invalid parameter\n");
+		return NULL;
+	}
+	q = &bufq->vb2_bufq;
+	mutex_lock(&bufq->lock);
+	list_for_each_entry(vb, &q->queued_list, queued_entry) {
+		if (vb->planes[0].m.userptr == dev_addr &&
+			vb->state == VB2_BUF_STATE_ACTIVE) {
+			found = 1;
+			dprintk(VIDC_DBG, "Found v4l2_buf index : %d\n",
+					vb->index);
+			break;
+		}
+	}
+	mutex_unlock(&bufq->lock);
+	if (!found) {
+		dprintk(VIDC_DBG,
+			"Failed to find buffer in queued list: %#lx, qtype = %d\n",
+			dev_addr, q->type);
+		vb = NULL;
+	}
+	return vb;
+}
+
+static void handle_ebd(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_data_done *response = data;
+	struct vb2_buffer *vb;
+	struct msm_vidc_inst *inst;
+	struct vidc_hal_ebd *empty_buf_done;
+	struct vb2_v4l2_buffer *vbuf = NULL;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	vb = get_vb_from_device_addr(&inst->bufq[OUTPUT_PORT],
+			response->input_done.packet_buffer);
+	if (vb) {
+		vbuf = to_vb2_v4l2_buffer(vb);
+		vb->planes[0].bytesused = response->input_done.filled_len;
+		vb->planes[0].data_offset = response->input_done.offset;
+		if (vb->planes[0].data_offset > vb->planes[0].length)
+			dprintk(VIDC_INFO, "data_offset overflow length\n");
+		if (vb->planes[0].bytesused > vb->planes[0].length)
+			dprintk(VIDC_INFO, "bytesused overflow length\n");
+		if (vb->planes[0].m.userptr !=
+			response->clnt_data)
+			dprintk(VIDC_INFO, "Client data != bufaddr\n");
+		empty_buf_done = (struct vidc_hal_ebd *)&response->input_done;
+		if (empty_buf_done) {
+			if (empty_buf_done->status == VIDC_ERR_NOT_SUPPORTED) {
+				dprintk(VIDC_INFO,
+					"Failed : Unsupported input stream\n");
+				vbuf->flags |=
+					V4L2_QCOM_BUF_INPUT_UNSUPPORTED;
+			}
+			if (empty_buf_done->status == VIDC_ERR_BITSTREAM_ERR) {
+				dprintk(VIDC_INFO,
+					"Failed : Corrupted input stream\n");
+				vbuf->flags |=
+					V4L2_QCOM_BUF_DATA_CORRUPT;
+			}
+			if (empty_buf_done->flags & HAL_BUFFERFLAG_SYNCFRAME)
+				vbuf->flags |=
+					V4L2_QCOM_BUF_FLAG_IDRFRAME |
+					V4L2_BUF_FLAG_KEYFRAME;
+		}
+		dprintk(VIDC_DBG,
+			"Got ebd from hal: device_addr: %pa, alloc: %d, status: %#x, pic_type: %#x, flags: %#x\n",
+			&empty_buf_done->packet_buffer,
+			empty_buf_done->alloc_len, empty_buf_done->status,
+			empty_buf_done->picture_type, empty_buf_done->flags);
+
+		mutex_lock(&inst->bufq[OUTPUT_PORT].lock);
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+		mutex_unlock(&inst->bufq[OUTPUT_PORT].lock);
+		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_EBD);
+	}
+
+	put_inst(inst);
+}
+
+int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo)
+{
+	int cnt = 0;
+
+	if (!inst || !binfo)
+		return -EINVAL;
+
+	atomic_inc(&binfo->ref_count);
+	cnt = atomic_read(&binfo->ref_count);
+	if (cnt > 2) {
+		dprintk(VIDC_DBG, "%s: invalid ref_cnt: %d\n", __func__, cnt);
+		cnt = -EINVAL;
+	}
+	if (cnt == 2)
+		inst->buffers_held_in_driver++;
+
+	dprintk(VIDC_DBG, "REF_GET[%d] fd[0] = %d\n", cnt, binfo->fd[0]);
+
+	return cnt;
+}
+
+int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo)
+{
+	int rc = 0;
+	int cnt;
+	bool release_buf = false;
+	bool qbuf_again = false;
+
+	if (!inst || !binfo)
+		return -EINVAL;
+
+	atomic_dec(&binfo->ref_count);
+	cnt = atomic_read(&binfo->ref_count);
+	dprintk(VIDC_DBG, "REF_PUT[%d] fd[0] = %d\n", cnt, binfo->fd[0]);
+	if (!cnt)
+		release_buf = true;
+	else if (cnt == 1)
+		qbuf_again = true;
+	else {
+		dprintk(VIDC_DBG, "%s: invalid ref_cnt: %d\n", __func__, cnt);
+		cnt = -EINVAL;
+	}
+
+	if (cnt < 0)
+		return cnt;
+
+	if (release_buf) {
+		/*
+		 * We can not delete binfo here as we need to set the user
+		 * virtual address saved in binfo->uvaddr to the dequeued v4l2
+		 * buffer.
+		 *
+		 * We will set the pending_deletion flag to true here and delete
+		 * binfo from registered list in dqbuf after setting the uvaddr.
+		 */
+		dprintk(VIDC_DBG, "fd[0] = %d -> pending_deletion = true\n",
+			binfo->fd[0]);
+		binfo->pending_deletion = true;
+	} else if (qbuf_again) {
+		inst->buffers_held_in_driver--;
+		rc = qbuf_dynamic_buf(inst, binfo);
+		if (!rc)
+			return rc;
+	}
+	return cnt;
+}
+
+static void handle_dynamic_buffer(struct msm_vidc_inst *inst,
+		ion_phys_addr_t device_addr, u32 flags)
+{
+	struct buffer_info *binfo = NULL, *temp = NULL;
+
+	/*
+	 * Update reference count and release OR queue back the buffer,
+	 * only when firmware is not holding a reference.
+	 */
+	if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) {
+		binfo = device_to_uvaddr(&inst->registeredbufs, device_addr);
+		if (!binfo) {
+			dprintk(VIDC_ERR,
+				"%s buffer not found in registered list\n",
+				__func__);
+			return;
+		}
+		if (flags & HAL_BUFFERFLAG_READONLY) {
+			dprintk(VIDC_DBG,
+				"FBD fd[0] = %d -> Reference with f/w, addr: %pa\n",
+				binfo->fd[0], &device_addr);
+		} else {
+			dprintk(VIDC_DBG,
+				"FBD fd[0] = %d -> FBD_ref_released, addr: %pa\n",
+				binfo->fd[0], &device_addr);
+
+			mutex_lock(&inst->registeredbufs.lock);
+			list_for_each_entry(temp, &inst->registeredbufs.list,
+							list) {
+				if (temp == binfo) {
+					buf_ref_put(inst, binfo);
+					break;
+				}
+			}
+			mutex_unlock(&inst->registeredbufs.lock);
+		}
+	}
+}
+
+static int handle_multi_stream_buffers(struct msm_vidc_inst *inst,
+		ion_phys_addr_t dev_addr)
+{
+	struct internal_buf *binfo;
+	struct msm_smem *handle;
+	bool found = false;
+
+	mutex_lock(&inst->outputbufs.lock);
+	list_for_each_entry(binfo, &inst->outputbufs.list, list) {
+		handle = binfo->handle;
+		if (handle && dev_addr == handle->device_addr) {
+			if (binfo->buffer_ownership == DRIVER) {
+				dprintk(VIDC_ERR,
+					"FW returned same buffer: %pa\n",
+					&dev_addr);
+				break;
+			}
+			binfo->buffer_ownership = DRIVER;
+			found = true;
+			break;
+		}
+	}
+	mutex_unlock(&inst->outputbufs.lock);
+
+	if (!found) {
+		dprintk(VIDC_ERR,
+			"Failed to find output buffer in queued list: %pa\n",
+			&dev_addr);
+	}
+
+	return 0;
+}
+
+enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst)
+{
+	if (msm_comm_get_stream_output_mode(inst) ==
+		HAL_VIDEO_DECODER_SECONDARY)
+		return HAL_BUFFER_OUTPUT2;
+	else
+		return HAL_BUFFER_OUTPUT;
+}
+
+static void handle_fbd(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_data_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct vb2_buffer *vb = NULL;
+	struct vidc_hal_fbd *fill_buf_done;
+	enum hal_buffer buffer_type;
+	int extra_idx = 0;
+	int64_t time_usec = 0;
+	struct vb2_v4l2_buffer *vbuf = NULL;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
+	buffer_type = msm_comm_get_hal_output_buffer(inst);
+	if (fill_buf_done->buffer_type == buffer_type) {
+		vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT],
+				fill_buf_done->packet_buffer1);
+	} else {
+		if (handle_multi_stream_buffers(inst,
+				fill_buf_done->packet_buffer1))
+			dprintk(VIDC_ERR,
+				"Failed : Output buffer not found %pa\n",
+				&fill_buf_done->packet_buffer1);
+		goto err_handle_fbd;
+	}
+
+	if (vb) {
+		vbuf = to_vb2_v4l2_buffer(vb);
+		vb->planes[0].bytesused = fill_buf_done->filled_len1;
+		vb->planes[0].data_offset = fill_buf_done->offset1;
+		if (vb->planes[0].data_offset > vb->planes[0].length)
+			dprintk(VIDC_INFO,
+				"fbd:Overflow data_offset = %d; length = %d\n",
+				vb->planes[0].data_offset,
+				vb->planes[0].length);
+		if (vb->planes[0].bytesused > vb->planes[0].length)
+			dprintk(VIDC_INFO,
+				"fbd:Overflow bytesused = %d; length = %d\n",
+				vb->planes[0].bytesused,
+				vb->planes[0].length);
+		if (!(fill_buf_done->flags1 &
+			HAL_BUFFERFLAG_TIMESTAMPINVALID)) {
+			time_usec = fill_buf_done->timestamp_hi;
+			time_usec = (time_usec << 32) |
+				fill_buf_done->timestamp_lo;
+		} else {
+			time_usec = 0;
+			dprintk(VIDC_DBG,
+					"Set zero timestamp for buffer %pa, filled: %d, (hi:%u, lo:%u)\n",
+					&fill_buf_done->packet_buffer1,
+					fill_buf_done->filled_len1,
+					fill_buf_done->timestamp_hi,
+					fill_buf_done->timestamp_lo);
+		}
+		vbuf->flags = 0;
+//		vb->timestamp = (u64)
+//			ns_to_timeval(time_usec * NSEC_PER_USEC);
+		extra_idx =
+			EXTRADATA_IDX(inst->fmts[CAPTURE_PORT].num_planes);
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+			vb->planes[extra_idx].m.userptr =
+				(unsigned long)fill_buf_done->extra_data_buffer;
+			vb->planes[extra_idx].bytesused =
+				vb->planes[extra_idx].length;
+			vb->planes[extra_idx].data_offset = 0;
+		}
+
+		handle_dynamic_buffer(inst, fill_buf_done->packet_buffer1,
+					fill_buf_done->flags1);
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_READONLY)
+			vbuf->flags |= V4L2_QCOM_BUF_FLAG_READONLY;
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOS)
+			vbuf->flags |= V4L2_QCOM_BUF_FLAG_EOS;
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_CODECCONFIG)
+			vbuf->flags &= ~V4L2_QCOM_BUF_FLAG_CODECCONFIG;
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_SYNCFRAME)
+			vbuf->flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_EOSEQ)
+			vbuf->flags |= V4L2_QCOM_BUF_FLAG_EOSEQ;
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DECODEONLY)
+			vbuf->flags |= V4L2_QCOM_BUF_FLAG_DECODEONLY;
+		if (fill_buf_done->flags1 & HAL_BUFFERFLAG_DATACORRUPT)
+			vbuf->flags |= V4L2_QCOM_BUF_DATA_CORRUPT;
+
+		switch (fill_buf_done->picture_type) {
+		case HAL_PICTURE_IDR:
+			vbuf->flags |= V4L2_QCOM_BUF_FLAG_IDRFRAME;
+			vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+			break;
+		case HAL_PICTURE_I:
+			vbuf->flags |= V4L2_BUF_FLAG_KEYFRAME;
+			break;
+		case HAL_PICTURE_P:
+			vbuf->flags |= V4L2_BUF_FLAG_PFRAME;
+			break;
+		case HAL_PICTURE_B:
+			vbuf->flags |= V4L2_BUF_FLAG_BFRAME;
+			break;
+		case HAL_FRAME_NOTCODED:
+		case HAL_UNUSED_PICT:
+			/* Do we need to care about these? */
+		case HAL_FRAME_YUV:
+			break;
+		default:
+			break;
+		}
+
+		inst->count.fbd++;
+
+		if (extra_idx && extra_idx < VIDEO_MAX_PLANES) {
+			dprintk(VIDC_DBG,
+				"extradata: userptr = %pK;"
+				" bytesused = %d; length = %d\n",
+				(u8 *)vb->planes[extra_idx].m.userptr,
+				vb->planes[extra_idx].bytesused,
+				vb->planes[extra_idx].length);
+		}
+		dprintk(VIDC_DBG,
+		"Got fbd from hal: device_addr: %pa, alloc: %d, filled: %d, offset: %d, ts: %lld, flags: %#x, crop: %d %d %d %d, pic_type: %#x, mark_data: %#x\n",
+		&fill_buf_done->packet_buffer1, fill_buf_done->alloc_len1,
+		fill_buf_done->filled_len1, fill_buf_done->offset1, time_usec,
+		fill_buf_done->flags1, fill_buf_done->start_x_coord,
+		fill_buf_done->start_y_coord, fill_buf_done->frame_width,
+		fill_buf_done->frame_height, fill_buf_done->picture_type,
+		fill_buf_done->mark_data);
+
+		mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
+		vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+		mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
+		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FBD);
+	}
+
+err_handle_fbd:
+	put_inst(inst);
+}
+
+static void handle_seq_hdr_done(enum hal_command_response cmd, void *data)
+{
+	struct msm_vidc_cb_data_done *response = data;
+	struct msm_vidc_inst *inst;
+	struct vb2_buffer *vb;
+	struct vidc_hal_fbd *fill_buf_done;
+	struct vb2_v4l2_buffer *vbuf;
+
+	if (!response) {
+		dprintk(VIDC_ERR, "Invalid response from vidc_hal\n");
+		return;
+	}
+
+	inst = get_inst(get_vidc_core(response->device_id),
+			response->session_id);
+	if (!inst) {
+		dprintk(VIDC_WARN, "Got a response for an inactive session\n");
+		return;
+	}
+
+	fill_buf_done = (struct vidc_hal_fbd *)&response->output_done;
+	vb = get_vb_from_device_addr(&inst->bufq[CAPTURE_PORT],
+				fill_buf_done->packet_buffer1);
+	if (!vb) {
+		dprintk(VIDC_ERR,
+				"Failed to find video buffer for seq_hdr_done: %pa\n",
+				&fill_buf_done->packet_buffer1);
+		goto err_seq_hdr_done;
+	}
+	vbuf = to_vb2_v4l2_buffer(vb);
+//	vb->timestamp = (u64) ns_to_timeval(0);
+
+	vb->planes[0].bytesused = fill_buf_done->filled_len1;
+	vb->planes[0].data_offset = fill_buf_done->offset1;
+
+	vbuf->flags = V4L2_QCOM_BUF_FLAG_CODECCONFIG;
+
+	dprintk(VIDC_DBG, "Filled length = %d; offset = %d; flags %x\n",
+				vb->planes[0].bytesused,
+				vb->planes[0].data_offset,
+				vbuf->flags);
+	mutex_lock(&inst->bufq[CAPTURE_PORT].lock);
+	vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+	mutex_unlock(&inst->bufq[CAPTURE_PORT].lock);
+
+err_seq_hdr_done:
+	put_inst(inst);
+}
+
+void handle_cmd_response(enum hal_command_response cmd, void *data)
+{
+	dprintk(VIDC_DBG, "Command response = %d\n", cmd);
+	switch (cmd) {
+	case HAL_SYS_INIT_DONE:
+		handle_sys_init_done(cmd, data);
+		break;
+	case HAL_SYS_RELEASE_RESOURCE_DONE:
+		handle_sys_release_res_done(cmd, data);
+		break;
+	case HAL_SESSION_INIT_DONE:
+		handle_session_init_done(cmd, data);
+		break;
+	case HAL_SESSION_PROPERTY_INFO:
+		handle_session_prop_info(cmd, data);
+		break;
+	case HAL_SESSION_LOAD_RESOURCE_DONE:
+		handle_load_resource_done(cmd, data);
+		break;
+	case HAL_SESSION_START_DONE:
+		handle_start_done(cmd, data);
+		break;
+	case HAL_SESSION_ETB_DONE:
+		handle_ebd(cmd, data);
+		break;
+	case HAL_SESSION_FTB_DONE:
+		handle_fbd(cmd, data);
+		break;
+	case HAL_SESSION_STOP_DONE:
+		handle_stop_done(cmd, data);
+		break;
+	case HAL_SESSION_RELEASE_RESOURCE_DONE:
+		handle_release_res_done(cmd, data);
+		break;
+	case HAL_SESSION_END_DONE:
+	case HAL_SESSION_ABORT_DONE:
+		handle_session_close(cmd, data);
+		break;
+	case HAL_SESSION_EVENT_CHANGE:
+		handle_event_change(cmd, data);
+		break;
+	case HAL_SESSION_FLUSH_DONE:
+		handle_session_flush(cmd, data);
+		break;
+	case HAL_SESSION_GET_SEQ_HDR_DONE:
+		handle_seq_hdr_done(cmd, data);
+		break;
+	case HAL_SYS_WATCHDOG_TIMEOUT:
+	case HAL_SYS_ERROR:
+		handle_sys_error(cmd, data);
+		break;
+	case HAL_SESSION_ERROR:
+		handle_session_error(cmd, data);
+		break;
+	case HAL_SESSION_RELEASE_BUFFER_DONE:
+		handle_session_release_buf_done(cmd, data);
+		break;
+	default:
+		dprintk(VIDC_DBG, "response unhandled: %d\n", cmd);
+		break;
+	}
+}
+
+int msm_comm_scale_clocks(struct msm_vidc_core *core)
+{
+	int num_mbs_per_sec, enc_mbs_per_sec, dec_mbs_per_sec;
+
+	enc_mbs_per_sec =
+		msm_comm_get_load(core, MSM_VIDC_ENCODER, LOAD_CALC_NO_QUIRKS);
+	dec_mbs_per_sec	=
+		msm_comm_get_load(core, MSM_VIDC_DECODER, LOAD_CALC_NO_QUIRKS);
+
+	if (enc_mbs_per_sec >= dec_mbs_per_sec) {
+	/*
+	 * If Encoder load is higher, use that load. Encoder votes for higher
+	 * clock. Since Encoder and Deocder run on parallel cores, this clock
+	 * should suffice decoder usecases.
+	 */
+		num_mbs_per_sec = enc_mbs_per_sec;
+	} else {
+	/*
+	 * If Decoder load is higher, it's tricky to decide clock. Decoder
+	 * higher load might results less clocks than Encoder smaller load.
+	 * At this point driver doesn't know which clock to vote. Hence use
+	 * total load.
+	 */
+		num_mbs_per_sec = enc_mbs_per_sec + dec_mbs_per_sec;
+	}
+
+	return msm_comm_scale_clocks_load(core, num_mbs_per_sec,
+			LOAD_CALC_NO_QUIRKS);
+}
+
+int msm_comm_scale_clocks_load(struct msm_vidc_core *core,
+		int num_mbs_per_sec, enum load_calc_quirks quirks)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	struct msm_vidc_inst *inst = NULL;
+	unsigned long instant_bitrate = 0;
+	int num_sessions = 0;
+	struct vidc_clk_scale_data clk_scale_data = { {0} };
+	int codec = 0;
+
+	if (!core) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, core);
+		return -EINVAL;
+	}
+
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "%s Invalid device handle: %pK\n",
+			__func__, hdev);
+		return -EINVAL;
+	}
+
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list) {
+
+		codec = inst->session_type == MSM_VIDC_DECODER ?
+			inst->fmts[OUTPUT_PORT].fourcc :
+			inst->fmts[CAPTURE_PORT].fourcc;
+
+		if (msm_comm_turbo_session(inst))
+			clk_scale_data.power_mode[num_sessions] =
+				VIDC_POWER_TURBO;
+		else if (is_low_power_session(inst))
+			clk_scale_data.power_mode[num_sessions] =
+				VIDC_POWER_LOW;
+		else
+			clk_scale_data.power_mode[num_sessions] =
+				VIDC_POWER_NORMAL;
+
+		if (inst->dcvs_mode)
+			clk_scale_data.load[num_sessions] = inst->dcvs.load;
+		else
+			clk_scale_data.load[num_sessions] =
+				msm_comm_get_inst_load(inst, quirks);
+
+		clk_scale_data.session[num_sessions] =
+				VIDC_VOTE_DATA_SESSION_VAL(
+				get_hal_codec(codec),
+				get_hal_domain(inst->session_type));
+		num_sessions++;
+
+		if (inst->instant_bitrate > instant_bitrate)
+			instant_bitrate = inst->instant_bitrate;
+
+	}
+	clk_scale_data.num_sessions = num_sessions;
+	mutex_unlock(&core->lock);
+
+
+	rc = call_hfi_op(hdev, scale_clocks,
+		hdev->hfi_device_data, num_mbs_per_sec,
+		&clk_scale_data, instant_bitrate);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to set clock rate: %d\n", rc);
+
+	return rc;
+}
+
+void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
+		return;
+	}
+	core = inst->core;
+	hdev = core->device;
+
+	if (msm_comm_scale_clocks(core)) {
+		dprintk(VIDC_WARN,
+				"Failed to scale clocks. Performance might be impacted\n");
+	}
+	if (msm_comm_vote_bus(core)) {
+		dprintk(VIDC_WARN,
+				"Failed to scale DDR bus. Performance might be impacted\n");
+	}
+}
+
+static inline enum msm_vidc_thermal_level msm_comm_vidc_thermal_level(int level)
+{
+	switch (level) {
+	case 0:
+		return VIDC_THERMAL_NORMAL;
+	case 1:
+		return VIDC_THERMAL_LOW;
+	case 2:
+		return VIDC_THERMAL_HIGH;
+	default:
+		return VIDC_THERMAL_CRITICAL;
+	}
+}
+
+static unsigned long msm_comm_get_clock_rate(struct msm_vidc_core *core)
+{
+	struct hfi_device *hdev;
+	unsigned long freq = 0;
+
+	if (!core || !core->device) {
+		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	hdev = core->device;
+
+	freq = call_hfi_op(hdev, get_core_clock_rate, hdev->hfi_device_data, 1);
+	dprintk(VIDC_DBG, "clock freq %ld\n", freq);
+
+	return freq;
+}
+
+static bool is_core_turbo(struct msm_vidc_core *core, unsigned long freq)
+{
+	int i = 0;
+	struct msm_vidc_platform_resources *res = &core->resources;
+	struct load_freq_table *table = res->load_freq_tbl;
+	u32 max_freq = 0;
+
+	for (i = 0; i < res->load_freq_tbl_size; i++) {
+		if (max_freq < table[i].freq)
+			max_freq = table[i].freq;
+	}
+	return freq >= max_freq;
+}
+
+static bool is_thermal_permissible(struct msm_vidc_core *core)
+{
+	enum msm_vidc_thermal_level tl;
+	unsigned long freq = 0;
+	bool is_turbo = false;
+
+	if (!core->resources.thermal_mitigable)
+		return true;
+
+	if (msm_vidc_thermal_mitigation_disabled) {
+		dprintk(VIDC_DBG,
+			"Thermal mitigation not enabled. debugfs %d\n",
+			msm_vidc_thermal_mitigation_disabled);
+		return true;
+	}
+
+	tl = msm_comm_vidc_thermal_level(vidc_driver->thermal_level);
+	freq = msm_comm_get_clock_rate(core);
+
+	is_turbo = is_core_turbo(core, freq);
+	dprintk(VIDC_DBG,
+		"Core freq %ld Thermal level %d Turbo mode %d\n",
+		freq, tl, is_turbo);
+
+	if (is_turbo && tl >= VIDC_THERMAL_LOW) {
+		dprintk(VIDC_ERR,
+			"Video session not allowed. Turbo mode %d Thermal level %d\n",
+			is_turbo, tl);
+		return false;
+	}
+	return true;
+}
+
+static int msm_comm_session_abort(struct msm_vidc_inst *inst)
+{
+	int rc = 0, abort_completion = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid params\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+	abort_completion = SESSION_MSG_INDEX(HAL_SESSION_ABORT_DONE);
+
+	rc = call_hfi_op(hdev, session_abort, (void *)inst->session);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"%s session_abort failed rc: %d\n", __func__, rc);
+		return rc;
+	}
+	rc = wait_for_completion_timeout(
+			&inst->completions[abort_completion],
+			msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
+	if (!rc) {
+		dprintk(VIDC_ERR,
+				"%s: Wait interrupted or timed out [%pK]: %d\n",
+				__func__, inst, abort_completion);
+		call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+		dprintk(VIDC_ERR,
+			"ABORT timeout can potentially crash the system\n");
+		msm_comm_print_debug_info(inst);
+
+		msm_vidc_handle_hw_error(inst->core);
+		rc = -EBUSY;
+	} else {
+		rc = 0;
+	}
+	msm_comm_session_clean(inst);
+	return rc;
+}
+
+static void handle_thermal_event(struct msm_vidc_core *core)
+{
+	int rc = 0;
+	struct msm_vidc_inst *inst;
+
+	if (!core || !core->device) {
+		dprintk(VIDC_ERR, "%s Invalid params\n", __func__);
+		return;
+	}
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list) {
+		if (!inst->session)
+			continue;
+
+		mutex_unlock(&core->lock);
+		if (inst->state >= MSM_VIDC_OPEN_DONE &&
+			inst->state < MSM_VIDC_CLOSE_DONE) {
+			dprintk(VIDC_WARN, "%s: abort inst %pK\n",
+				__func__, inst);
+			rc = msm_comm_session_abort(inst);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"%s session_abort failed rc: %d\n",
+					__func__, rc);
+				goto err_sess_abort;
+			}
+			change_inst_state(inst, MSM_VIDC_CORE_INVALID);
+			dprintk(VIDC_WARN,
+				"%s Send sys error for inst %pK\n",
+				__func__, inst);
+			msm_vidc_queue_v4l2_event(inst,
+					V4L2_EVENT_MSM_VIDC_SYS_ERROR);
+		} else {
+			msm_comm_generate_session_error(inst);
+		}
+		mutex_lock(&core->lock);
+	}
+	mutex_unlock(&core->lock);
+	return;
+
+err_sess_abort:
+	msm_comm_clean_notify_client(core);
+}
+
+void msm_comm_handle_thermal_event(void)
+{
+	struct msm_vidc_core *core;
+
+	list_for_each_entry(core, &vidc_driver->cores, list) {
+		if (!is_thermal_permissible(core)) {
+			dprintk(VIDC_WARN,
+				"Thermal level critical, stop all active sessions!\n");
+			handle_thermal_event(core);
+		}
+	}
+}
+
+int msm_comm_check_core_init(struct msm_vidc_core *core)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	struct msm_vidc_inst *inst = NULL;
+
+	mutex_lock(&core->lock);
+	if (core->state >= VIDC_CORE_INIT_DONE) {
+		dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
+				core->id, core->state);
+		goto exit;
+	}
+	dprintk(VIDC_DBG, "Waiting for SYS_INIT_DONE\n");
+	hdev = (struct hfi_device *)core->device;
+	rc = wait_for_completion_timeout(
+		&core->completions[SYS_MSG_INDEX(HAL_SYS_INIT_DONE)],
+		msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
+	if (!rc) {
+		dprintk(VIDC_ERR, "%s: Wait interrupted or timed out: %d\n",
+				__func__, SYS_MSG_INDEX(HAL_SYS_INIT_DONE));
+		call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+		dprintk(VIDC_ERR,
+			"SYS_INIT timeout can potentially crash the system\n");
+		/*
+		 * For SYS_INIT, there will not be any inst pointer.
+		 * Just grab one of the inst from instances list and
+		 * use it.
+		 */
+		inst = list_first_entry(&core->instances,
+			struct msm_vidc_inst, list);
+
+		mutex_unlock(&core->lock);
+		msm_comm_print_debug_info(inst);
+		mutex_lock(&core->lock);
+
+		msm_vidc_handle_hw_error(core);
+		rc = -EIO;
+		goto exit;
+	} else {
+		core->state = VIDC_CORE_INIT_DONE;
+		rc = 0;
+	}
+	dprintk(VIDC_DBG, "SYS_INIT_DONE!!!\n");
+exit:
+	mutex_unlock(&core->lock);
+	return rc;
+}
+
+static int msm_comm_init_core_done(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	rc = msm_comm_check_core_init(inst->core);
+	if (rc) {
+		dprintk(VIDC_ERR, "%s - failed to initialize core\n", __func__);
+		msm_comm_generate_sys_error(inst);
+		return rc;
+	}
+	change_inst_state(inst, MSM_VIDC_CORE_INIT_DONE);
+	return rc;
+}
+
+static int msm_comm_init_core(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	struct msm_vidc_core *core;
+
+	if (!inst || !inst->core || !inst->core->device)
+		return -EINVAL;
+
+	core = inst->core;
+	hdev = core->device;
+	mutex_lock(&core->lock);
+	if (core->state >= VIDC_CORE_INIT) {
+		dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
+				core->id, core->state);
+		goto core_already_inited;
+	}
+	if (!core->capabilities) {
+		core->capabilities = kzalloc(VIDC_MAX_SESSIONS *
+				sizeof(struct msm_vidc_capability), GFP_KERNEL);
+		if (!core->capabilities) {
+			dprintk(VIDC_ERR,
+				"%s: failed to allocate capabilities\n",
+				__func__);
+			rc = -ENOMEM;
+			goto fail_cap_alloc;
+		}
+	} else {
+		dprintk(VIDC_WARN,
+			"%s: capabilities memory is expected to be freed\n",
+			__func__);
+	}
+
+	rc = call_hfi_op(hdev, core_init, hdev->hfi_device_data);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to init core, id = %d\n",
+				core->id);
+		goto fail_core_init;
+	}
+	core->state = VIDC_CORE_INIT;
+	core->smmu_fault_handled = false;
+core_already_inited:
+	change_inst_state(inst, MSM_VIDC_CORE_INIT);
+	mutex_unlock(&core->lock);
+	return rc;
+
+fail_core_init:
+	kfree(core->capabilities);
+fail_cap_alloc:
+	core->capabilities = NULL;
+	core->state = VIDC_CORE_UNINIT;
+	mutex_unlock(&core->lock);
+	return rc;
+}
+
+static int msm_vidc_deinit_core(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	core = inst->core;
+	hdev = core->device;
+
+	mutex_lock(&core->lock);
+	if (core->state == VIDC_CORE_UNINIT) {
+		dprintk(VIDC_INFO, "Video core: %d is already in state: %d\n",
+				core->id, core->state);
+		goto core_already_uninited;
+	}
+	mutex_unlock(&core->lock);
+
+	msm_comm_scale_clocks_and_bus(inst);
+
+	mutex_lock(&core->lock);
+
+	if (!core->resources.never_unload_fw) {
+		cancel_delayed_work(&core->fw_unload_work);
+
+		/*
+		 * Delay unloading of firmware. This is useful
+		 * in avoiding firmware download delays in cases where we
+		 * will have a burst of back to back video playback sessions
+		 * e.g. thumbnail generation.
+		 */
+		schedule_delayed_work(&core->fw_unload_work,
+			msecs_to_jiffies(core->state == VIDC_CORE_INVALID ?
+					0 : msm_vidc_firmware_unload_delay));
+
+		dprintk(VIDC_DBG, "firmware unload delayed by %u ms\n",
+			core->state == VIDC_CORE_INVALID ?
+			0 : msm_vidc_firmware_unload_delay);
+	}
+
+core_already_uninited:
+	change_inst_state(inst, MSM_VIDC_CORE_UNINIT);
+	mutex_unlock(&core->lock);
+	return 0;
+}
+
+int msm_comm_force_cleanup(struct msm_vidc_inst *inst)
+{
+	msm_comm_kill_session(inst);
+	return msm_vidc_deinit_core(inst);
+}
+
+static int msm_comm_session_init(int flipped_state,
+	struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	int fourcc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_OPEN)) {
+		dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n",
+						inst, inst->state);
+		goto exit;
+	}
+	if (inst->session_type == MSM_VIDC_DECODER) {
+		fourcc = inst->fmts[OUTPUT_PORT].fourcc;
+	} else if (inst->session_type == MSM_VIDC_ENCODER) {
+		fourcc = inst->fmts[CAPTURE_PORT].fourcc;
+	} else {
+		dprintk(VIDC_ERR, "Invalid session\n");
+		return -EINVAL;
+	}
+
+	rc = call_hfi_op(hdev, session_init, hdev->hfi_device_data,
+			inst, get_hal_domain(inst->session_type),
+			get_hal_codec(fourcc),
+			&inst->session);
+
+	if (rc || !inst->session) {
+		dprintk(VIDC_ERR,
+			"Failed to call session init for: %pK, %pK, %d, %d\n",
+			inst->core->device, inst,
+			inst->session_type, fourcc);
+		rc = -EINVAL;
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_OPEN);
+exit:
+	return rc;
+}
+
+static void msm_vidc_print_running_insts(struct msm_vidc_core *core)
+{
+	struct msm_vidc_inst *temp;
+
+	dprintk(VIDC_ERR, "Running instances:\n");
+	dprintk(VIDC_ERR, "%4s|%4s|%4s|%4s|%4s\n",
+			"type", "w", "h", "fps", "prop");
+
+	mutex_lock(&core->lock);
+	list_for_each_entry(temp, &core->instances, list) {
+		if (temp->state >= MSM_VIDC_OPEN_DONE &&
+				temp->state < MSM_VIDC_STOP_DONE) {
+			char properties[4] = "";
+
+			if (is_thumbnail_session(temp))
+				strlcat(properties, "N", sizeof(properties));
+
+			if (msm_comm_turbo_session(temp))
+				strlcat(properties, "T", sizeof(properties));
+
+			dprintk(VIDC_ERR, "%4d|%4d|%4d|%4d|%4s\n",
+					temp->session_type,
+					max(temp->prop.width[CAPTURE_PORT],
+						temp->prop.width[OUTPUT_PORT]),
+					max(temp->prop.height[CAPTURE_PORT],
+						temp->prop.height[OUTPUT_PORT]),
+					temp->prop.fps, properties);
+		}
+	}
+	mutex_unlock(&core->lock);
+}
+
+static int msm_vidc_load_resources(int flipped_state,
+	struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	int num_mbs_per_sec = 0, max_load_adj = 0;
+	struct msm_vidc_core *core;
+	enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD |
+		LOAD_CALC_IGNORE_THUMBNAIL_LOAD |
+		LOAD_CALC_IGNORE_NON_REALTIME_LOAD;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	core = inst->core;
+	if (core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"Core is in bad state can't do load res\n");
+		return -EINVAL;
+	}
+
+	if (inst->state == MSM_VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"Instance is in invalid state can't do load res\n");
+		return -EINVAL;
+	}
+
+	num_mbs_per_sec =
+		msm_comm_get_load(core, MSM_VIDC_DECODER, quirks) +
+		msm_comm_get_load(core, MSM_VIDC_ENCODER, quirks);
+
+	max_load_adj = core->resources.max_load +
+		inst->capability.mbs_per_frame.max;
+
+	if (num_mbs_per_sec > max_load_adj) {
+		dprintk(VIDC_ERR, "HW is overloaded, needed: %d max: %d\n",
+			num_mbs_per_sec, max_load_adj);
+		msm_vidc_print_running_insts(core);
+		inst->state = MSM_VIDC_CORE_INVALID;
+		msm_comm_kill_session(inst);
+		return -EBUSY;
+	}
+
+	hdev = core->device;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_LOAD_RESOURCES)) {
+		dprintk(VIDC_INFO, "inst: %pK is already in state: %d\n",
+						inst, inst->state);
+		goto exit;
+	}
+
+	rc = call_hfi_op(hdev, session_load_res, (void *) inst->session);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to send load resources\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_LOAD_RESOURCES);
+exit:
+	return rc;
+}
+
+static int msm_vidc_start(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			inst->core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"Core is in bad state can't do start\n");
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_START)) {
+		dprintk(VIDC_INFO,
+			"inst: %pK is already in state: %d\n",
+			inst, inst->state);
+		goto exit;
+	}
+	rc = call_hfi_op(hdev, session_start, (void *) inst->session);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to send start\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_START);
+exit:
+	return rc;
+}
+
+static int msm_vidc_stop(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_STOP)) {
+		dprintk(VIDC_INFO,
+			"inst: %pK is already in state: %d\n",
+			inst, inst->state);
+		goto exit;
+	}
+	dprintk(VIDC_DBG, "Send Stop to hal\n");
+	rc = call_hfi_op(hdev, session_stop, (void *) inst->session);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to send stop\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_STOP);
+exit:
+	return rc;
+}
+
+static int msm_vidc_release_res(int flipped_state, struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_RELEASE_RESOURCES)) {
+		dprintk(VIDC_INFO,
+			"inst: %pK is already in state: %d\n",
+			inst, inst->state);
+		goto exit;
+	}
+	dprintk(VIDC_DBG,
+		"Send release res to hal\n");
+	rc = call_hfi_op(hdev, session_release_res, (void *) inst->session);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to send release resources\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_RELEASE_RESOURCES);
+exit:
+	return rc;
+}
+
+static int msm_comm_session_close(int flipped_state,
+			struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid params\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+	if (IS_ALREADY_IN_STATE(flipped_state, MSM_VIDC_CLOSE)) {
+		dprintk(VIDC_INFO,
+			"inst: %pK is already in state: %d\n",
+						inst, inst->state);
+		goto exit;
+	}
+	dprintk(VIDC_DBG,
+		"Send session close to hal\n");
+	rc = call_hfi_op(hdev, session_end, (void *) inst->session);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to send close\n");
+		goto exit;
+	}
+	change_inst_state(inst, MSM_VIDC_CLOSE);
+exit:
+	return rc;
+}
+
+int msm_comm_suspend(int core_id)
+{
+	struct hfi_device *hdev;
+	struct msm_vidc_core *core;
+	int rc = 0;
+
+	core = get_vidc_core(core_id);
+	if (!core) {
+		dprintk(VIDC_ERR,
+			"%s: Failed to find core for core_id = %d\n",
+			__func__, core_id);
+		return -EINVAL;
+	}
+
+	hdev = (struct hfi_device *)core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "%s Invalid device handle\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&core->lock);
+	if (core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"%s - fw is not in proper state, skip suspend\n",
+				__func__);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	rc = call_hfi_op(hdev, suspend, hdev->hfi_device_data);
+	if (rc)
+		dprintk(VIDC_WARN, "Failed to suspend\n");
+
+exit:
+	mutex_unlock(&core->lock);
+	return rc;
+}
+
+static int get_flipped_state(int present_state,
+	int desired_state)
+{
+	int flipped_state = present_state;
+
+	if (flipped_state < MSM_VIDC_STOP
+			&& desired_state > MSM_VIDC_STOP) {
+		flipped_state = MSM_VIDC_STOP + (MSM_VIDC_STOP - flipped_state);
+		flipped_state &= 0xFFFE;
+		flipped_state = flipped_state - 1;
+	} else if (flipped_state > MSM_VIDC_STOP
+			&& desired_state < MSM_VIDC_STOP) {
+		flipped_state = MSM_VIDC_STOP -
+			(flipped_state - MSM_VIDC_STOP + 1);
+		flipped_state &= 0xFFFE;
+		flipped_state = flipped_state - 1;
+	}
+	return flipped_state;
+}
+
+struct hal_buffer_requirements *get_buff_req_buffer(
+		struct msm_vidc_inst *inst, enum hal_buffer buffer_type)
+{
+	int i;
+
+	for (i = 0; i < HAL_BUFFER_MAX; i++) {
+		if (inst->buff_req.buffer[i].buffer_type == buffer_type)
+			return &inst->buff_req.buffer[i];
+	}
+	return NULL;
+}
+
+static int set_output_buffers(struct msm_vidc_inst *inst,
+	enum hal_buffer buffer_type)
+{
+	int rc = 0;
+	struct msm_smem *handle;
+	struct internal_buf *binfo;
+	u32 smem_flags = 0, buffer_size;
+	struct hal_buffer_requirements *output_buf, *extradata_buf;
+	int i;
+	struct hfi_device *hdev;
+	struct hal_buffer_size_minimum b;
+
+	hdev = inst->core->device;
+
+	output_buf = get_buff_req_buffer(inst, buffer_type);
+	if (!output_buf) {
+		dprintk(VIDC_DBG,
+			"This output buffer not required, buffer_type: %x\n",
+			buffer_type);
+		return 0;
+	}
+	dprintk(VIDC_DBG,
+		"output: num = %d, size = %d\n",
+		output_buf->buffer_count_actual,
+		output_buf->buffer_size);
+
+	buffer_size = output_buf->buffer_size;
+	b.buffer_type = buffer_type;
+	b.buffer_size = buffer_size;
+	rc = call_hfi_op(hdev, session_set_property,
+		inst->session, HAL_PARAM_BUFFER_SIZE_MINIMUM,
+		&b);
+
+	extradata_buf = get_buff_req_buffer(inst, HAL_BUFFER_EXTRADATA_OUTPUT);
+	if (extradata_buf) {
+		dprintk(VIDC_DBG,
+			"extradata: num = %d, size = %d\n",
+			extradata_buf->buffer_count_actual,
+			extradata_buf->buffer_size);
+		buffer_size += extradata_buf->buffer_size;
+	} else {
+		dprintk(VIDC_DBG,
+			"This extradata buffer not required, buffer_type: %x\n",
+			buffer_type);
+	}
+
+	if (inst->flags & VIDC_SECURE)
+		smem_flags |= SMEM_SECURE;
+
+	if (output_buf->buffer_size) {
+		for (i = 0; i < output_buf->buffer_count_actual;
+				i++) {
+			handle = msm_comm_smem_alloc(inst,
+					buffer_size, 1, smem_flags,
+					buffer_type, 0);
+			if (!handle) {
+				dprintk(VIDC_ERR,
+					"Failed to allocate output memory\n");
+				rc = -ENOMEM;
+				goto err_no_mem;
+			}
+			rc = msm_comm_smem_cache_operations(inst,
+					handle, SMEM_CACHE_CLEAN);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Failed to clean cache may cause undefined behavior\n");
+			}
+			binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+			if (!binfo) {
+				dprintk(VIDC_ERR, "Out of memory\n");
+				rc = -ENOMEM;
+				goto fail_kzalloc;
+			}
+
+			binfo->handle = handle;
+			binfo->buffer_type = buffer_type;
+			binfo->buffer_ownership = DRIVER;
+			dprintk(VIDC_DBG, "Output buffer address: %pa\n",
+					&handle->device_addr);
+
+			if (inst->buffer_mode_set[CAPTURE_PORT] ==
+				HAL_BUFFER_MODE_STATIC) {
+				struct vidc_buffer_addr_info buffer_info = {0};
+
+				buffer_info.buffer_size =
+					output_buf->buffer_size;
+				buffer_info.buffer_type = buffer_type;
+				buffer_info.num_buffers = 1;
+				buffer_info.align_device_addr =
+					handle->device_addr;
+				buffer_info.extradata_addr =
+					handle->device_addr +
+					output_buf->buffer_size;
+				if (extradata_buf)
+					buffer_info.extradata_size =
+						extradata_buf->buffer_size;
+				rc = call_hfi_op(hdev, session_set_buffers,
+					(void *) inst->session, &buffer_info);
+				if (rc) {
+					dprintk(VIDC_ERR,
+						"%s : session_set_buffers failed\n",
+						__func__);
+					goto fail_set_buffers;
+				}
+			}
+			mutex_lock(&inst->outputbufs.lock);
+			list_add_tail(&binfo->list, &inst->outputbufs.list);
+			mutex_unlock(&inst->outputbufs.lock);
+		}
+	}
+	return rc;
+fail_set_buffers:
+	kfree(binfo);
+fail_kzalloc:
+	msm_comm_smem_free(inst, handle);
+err_no_mem:
+	return rc;
+}
+
+static inline char *get_buffer_name(enum hal_buffer buffer_type)
+{
+	switch (buffer_type) {
+	case HAL_BUFFER_INPUT: return "input";
+	case HAL_BUFFER_OUTPUT: return "output";
+	case HAL_BUFFER_OUTPUT2: return "output_2";
+	case HAL_BUFFER_EXTRADATA_INPUT: return "input_extra";
+	case HAL_BUFFER_EXTRADATA_OUTPUT: return "output_extra";
+	case HAL_BUFFER_EXTRADATA_OUTPUT2: return "output2_extra";
+	case HAL_BUFFER_INTERNAL_SCRATCH: return "scratch";
+	case HAL_BUFFER_INTERNAL_SCRATCH_1: return "scratch_1";
+	case HAL_BUFFER_INTERNAL_SCRATCH_2: return "scratch_2";
+	case HAL_BUFFER_INTERNAL_PERSIST: return "persist";
+	case HAL_BUFFER_INTERNAL_PERSIST_1: return "persist_1";
+	case HAL_BUFFER_INTERNAL_CMD_QUEUE: return "queue";
+	default: return "????";
+	}
+}
+
+static int set_internal_buf_on_fw(struct msm_vidc_inst *inst,
+				enum hal_buffer buffer_type,
+				struct msm_smem *handle, bool reuse)
+{
+	struct vidc_buffer_addr_info buffer_info;
+	struct hfi_device *hdev;
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device || !handle) {
+		dprintk(VIDC_ERR, "%s - invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+
+	rc = msm_comm_smem_cache_operations(inst,
+					handle, SMEM_CACHE_CLEAN);
+	if (rc) {
+		dprintk(VIDC_WARN,
+			"Failed to clean cache. Undefined behavior\n");
+	}
+
+	buffer_info.buffer_size = handle->size;
+	buffer_info.buffer_type = buffer_type;
+	buffer_info.num_buffers = 1;
+	buffer_info.align_device_addr = handle->device_addr;
+	dprintk(VIDC_DBG, "%s %s buffer : %pa\n",
+				reuse ? "Reusing" : "Allocated",
+				get_buffer_name(buffer_type),
+				&buffer_info.align_device_addr);
+
+	rc = call_hfi_op(hdev, session_set_buffers,
+		(void *) inst->session, &buffer_info);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"vidc_hal_session_set_buffers failed\n");
+		return rc;
+	}
+	return 0;
+}
+
+static bool reuse_internal_buffers(struct msm_vidc_inst *inst,
+		enum hal_buffer buffer_type, struct msm_vidc_list *buf_list)
+{
+	struct internal_buf *buf;
+	int rc = 0;
+	bool reused = false;
+
+	if (!inst || !buf_list) {
+		dprintk(VIDC_ERR, "%s: invalid params\n", __func__);
+		return false;
+	}
+
+	mutex_lock(&buf_list->lock);
+	list_for_each_entry(buf, &buf_list->list, list) {
+		if (!buf->handle) {
+			reused = false;
+			break;
+		}
+
+		if (buf->buffer_type != buffer_type)
+			continue;
+
+		/*
+		 * Persist buffer size won't change with resolution. If they
+		 * are in queue means that they are already allocated and
+		 * given to HW. HW can use them without reallocation. These
+		 * buffers are not released as part of port reconfig. So
+		 * driver no need to set them again.
+		 */
+
+		if (buffer_type != HAL_BUFFER_INTERNAL_PERSIST
+			&& buffer_type != HAL_BUFFER_INTERNAL_PERSIST_1) {
+
+			rc = set_internal_buf_on_fw(inst, buffer_type,
+					buf->handle, true);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"%s: session_set_buffers failed\n",
+					__func__);
+				reused = false;
+				break;
+			}
+		}
+		reused = true;
+		dprintk(VIDC_DBG,
+			"Re-using internal buffer type : %d\n", buffer_type);
+	}
+	mutex_unlock(&buf_list->lock);
+	return reused;
+}
+
+static int allocate_and_set_internal_bufs(struct msm_vidc_inst *inst,
+			struct hal_buffer_requirements *internal_bufreq,
+			struct msm_vidc_list *buf_list)
+{
+	struct msm_smem *handle;
+	struct internal_buf *binfo;
+	u32 smem_flags = 0;
+	int rc = 0;
+	int i = 0;
+
+	if (!inst || !internal_bufreq || !buf_list)
+		return -EINVAL;
+
+	if (!internal_bufreq->buffer_size)
+		return 0;
+
+	if (inst->flags & VIDC_SECURE)
+		smem_flags |= SMEM_SECURE;
+
+	for (i = 0; i < internal_bufreq->buffer_count_actual; i++) {
+		handle = msm_comm_smem_alloc(inst, internal_bufreq->buffer_size,
+				1, smem_flags, internal_bufreq->buffer_type, 0);
+		if (!handle) {
+			dprintk(VIDC_ERR,
+				"Failed to allocate scratch memory\n");
+			rc = -ENOMEM;
+			goto err_no_mem;
+		}
+
+		binfo = kzalloc(sizeof(*binfo), GFP_KERNEL);
+		if (!binfo) {
+			dprintk(VIDC_ERR, "Out of memory\n");
+			rc = -ENOMEM;
+			goto fail_kzalloc;
+		}
+
+		binfo->handle = handle;
+		binfo->buffer_type = internal_bufreq->buffer_type;
+
+		rc = set_internal_buf_on_fw(inst, internal_bufreq->buffer_type,
+				handle, false);
+		if (rc)
+			goto fail_set_buffers;
+
+		mutex_lock(&buf_list->lock);
+		list_add_tail(&binfo->list, &buf_list->list);
+		mutex_unlock(&buf_list->lock);
+	}
+	return rc;
+
+fail_set_buffers:
+	kfree(binfo);
+fail_kzalloc:
+	msm_comm_smem_free(inst, handle);
+err_no_mem:
+	return rc;
+
+}
+
+static int set_internal_buffers(struct msm_vidc_inst *inst,
+	enum hal_buffer buffer_type, struct msm_vidc_list *buf_list)
+{
+	struct hal_buffer_requirements *internal_buf;
+
+	internal_buf = get_buff_req_buffer(inst, buffer_type);
+	if (!internal_buf) {
+		dprintk(VIDC_DBG,
+			"This internal buffer not required, buffer_type: %x\n",
+			buffer_type);
+		return 0;
+	}
+
+	dprintk(VIDC_DBG, "Buffer type %s: num = %d, size = %d\n",
+		get_buffer_name(buffer_type),
+		internal_buf->buffer_count_actual, internal_buf->buffer_size);
+
+	/*
+	 * Try reusing existing internal buffers first.
+	 * If it's not possible to reuse, allocate new buffers.
+	 */
+	if (reuse_internal_buffers(inst, buffer_type, buf_list))
+		return 0;
+
+	return allocate_and_set_internal_bufs(inst, internal_buf,
+				buf_list);
+}
+
+int msm_comm_try_state(struct msm_vidc_inst *inst, int state)
+{
+	int rc = 0;
+	int flipped_state;
+	struct msm_vidc_core *core;
+
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %pK\n", inst);
+		return -EINVAL;
+	}
+	dprintk(VIDC_DBG,
+			"Trying to move inst: %pK from: %#x to %#x\n",
+			inst, inst->state, state);
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %pK\n", inst);
+		return -EINVAL;
+	}
+	mutex_lock(&inst->sync_lock);
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR,
+				"Core is in bad state can't change the state\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+	flipped_state = get_flipped_state(inst->state, state);
+	dprintk(VIDC_DBG,
+			"flipped_state = %#x\n", flipped_state);
+	switch (flipped_state) {
+	case MSM_VIDC_CORE_UNINIT_DONE:
+	case MSM_VIDC_CORE_INIT:
+		rc = msm_comm_init_core(inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_CORE_INIT_DONE:
+		rc = msm_comm_init_core_done(inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_OPEN:
+		rc = msm_comm_session_init(flipped_state, inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_OPEN_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_OPEN_DONE,
+			HAL_SESSION_INIT_DONE);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_LOAD_RESOURCES:
+		rc = msm_vidc_load_resources(flipped_state, inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_LOAD_RESOURCES_DONE:
+	case MSM_VIDC_START:
+		rc = msm_vidc_start(flipped_state, inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_START_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_START_DONE,
+				HAL_SESSION_START_DONE);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_STOP:
+		rc = msm_vidc_stop(flipped_state, inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_STOP_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_STOP_DONE,
+				HAL_SESSION_STOP_DONE);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+		dprintk(VIDC_DBG, "Moving to Stop Done state\n");
+	case MSM_VIDC_RELEASE_RESOURCES:
+		rc = msm_vidc_release_res(flipped_state, inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_RELEASE_RESOURCES_DONE:
+		rc = wait_for_state(inst, flipped_state,
+			MSM_VIDC_RELEASE_RESOURCES_DONE,
+			HAL_SESSION_RELEASE_RESOURCE_DONE);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+		dprintk(VIDC_DBG,
+				"Moving to release resources done state\n");
+	case MSM_VIDC_CLOSE:
+		rc = msm_comm_session_close(flipped_state, inst);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+	case MSM_VIDC_CLOSE_DONE:
+		rc = wait_for_state(inst, flipped_state, MSM_VIDC_CLOSE_DONE,
+				HAL_SESSION_END_DONE);
+		if (rc || state <= get_flipped_state(inst->state, state))
+			break;
+		msm_comm_session_clean(inst);
+	case MSM_VIDC_CORE_UNINIT:
+	case MSM_VIDC_CORE_INVALID:
+		dprintk(VIDC_DBG, "Sending core uninit\n");
+		rc = msm_vidc_deinit_core(inst);
+		if (rc || state == get_flipped_state(inst->state, state))
+			break;
+	default:
+		dprintk(VIDC_ERR, "State not recognized\n");
+		rc = -EINVAL;
+		break;
+	}
+exit:
+	mutex_unlock(&inst->sync_lock);
+	if (rc)
+		dprintk(VIDC_ERR,
+				"Failed to move from state: %d to %d\n",
+				inst->state, state);
+	else
+		trace_msm_vidc_common_state_change((void *)inst,
+				inst->state, state);
+	return rc;
+}
+
+int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd)
+{
+	struct msm_vidc_inst *inst = instance;
+	struct v4l2_decoder_cmd *dec = NULL;
+	struct v4l2_encoder_cmd *enc = NULL;
+	struct msm_vidc_core *core;
+	int which_cmd = 0, flags = 0, rc = 0;
+
+	if (!inst || !inst->core || !cmd) {
+		dprintk(VIDC_ERR, "%s invalid params\n", __func__);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (inst->session_type == MSM_VIDC_ENCODER) {
+		enc = (struct v4l2_encoder_cmd *)cmd;
+		which_cmd = enc->cmd;
+		flags = enc->flags;
+	} else if (inst->session_type == MSM_VIDC_DECODER) {
+		dec = (struct v4l2_decoder_cmd *)cmd;
+		which_cmd = dec->cmd;
+		flags = dec->flags;
+	}
+
+
+	switch (which_cmd) {
+	case V4L2_QCOM_CMD_FLUSH:
+		if (core->state != VIDC_CORE_INVALID &&
+			inst->state ==  MSM_VIDC_CORE_INVALID) {
+			rc = msm_comm_kill_session(inst);
+			if (rc)
+				dprintk(VIDC_ERR,
+					"Fail to clean session: %d\n",
+					rc);
+		}
+		rc = msm_comm_flush(inst, flags);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to flush buffers: %d\n", rc);
+		}
+		break;
+	case V4L2_DEC_QCOM_CMD_RECONFIG_HINT:
+	{
+		u32 *ptr = NULL;
+		struct hal_buffer_requirements *output_buf;
+
+		rc = msm_comm_try_get_bufreqs(inst);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"Getting buffer requirements failed: %d\n",
+					rc);
+			break;
+		}
+
+		output_buf = get_buff_req_buffer(inst,
+				msm_comm_get_hal_output_buffer(inst));
+		if (output_buf) {
+			if (dec) {
+				ptr = (u32 *)dec->raw.data;
+				ptr[0] = output_buf->buffer_size;
+				ptr[1] = output_buf->buffer_count_actual;
+				dprintk(VIDC_DBG,
+					"Reconfig hint, size is %u, count is %u\n",
+					ptr[0], ptr[1]);
+			} else {
+				dprintk(VIDC_ERR, "Null decoder\n");
+			}
+		} else {
+			dprintk(VIDC_DBG,
+					"This output buffer not required, buffer_type: %x\n",
+					HAL_BUFFER_OUTPUT);
+		}
+		break;
+	}
+	default:
+		dprintk(VIDC_ERR, "Unknown Command %d\n", which_cmd);
+		rc = -ENOTSUPP;
+		break;
+	}
+	return rc;
+}
+
+static void populate_frame_data(struct vidc_frame_data *data,
+		const struct vb2_buffer *vb, struct msm_vidc_inst *inst)
+{
+	int64_t time_usec = 0;
+	int extra_idx;
+	enum v4l2_buf_type type = vb->type;
+	enum vidc_ports port = type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE ?
+		OUTPUT_PORT : CAPTURE_PORT;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	do_div(time_usec, NSEC_PER_USEC);
+
+	data->alloc_len = vb->planes[0].length;
+	data->device_addr = vb->planes[0].m.userptr;
+	data->timestamp = time_usec;
+	data->flags = 0;
+	data->clnt_data = data->device_addr;
+
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		bool pic_decoding_mode = msm_comm_g_ctrl_for_id(inst,
+				V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE);
+
+		data->buffer_type = HAL_BUFFER_INPUT;
+		data->filled_len = vb->planes[0].bytesused;
+		data->offset = vb->planes[0].data_offset;
+
+		if (vbuf->flags & V4L2_QCOM_BUF_FLAG_EOS)
+			data->flags |= HAL_BUFFERFLAG_EOS;
+
+		if (vbuf->flags & V4L2_QCOM_BUF_FLAG_CODECCONFIG)
+			data->flags |= HAL_BUFFERFLAG_CODECCONFIG;
+
+		if (vbuf->flags & V4L2_QCOM_BUF_FLAG_DECODEONLY)
+			data->flags |= HAL_BUFFERFLAG_DECODEONLY;
+
+		if (vbuf->flags & V4L2_QCOM_BUF_TIMESTAMP_INVALID)
+			data->timestamp = LLONG_MAX;
+
+		/* XXX: This is a dirty hack necessitated by the firmware,
+		 * which refuses to issue FBDs for non I-frames in Picture Type
+		 * Decoding mode, unless we pass in non-zero value in mark_data
+		 * and mark_target.
+		 */
+		data->mark_data = data->mark_target =
+			pic_decoding_mode ? 0xdeadbeef : 0;
+
+	} else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		data->buffer_type = msm_comm_get_hal_output_buffer(inst);
+	}
+
+	extra_idx = EXTRADATA_IDX(inst->fmts[port].num_planes);
+	if (extra_idx && extra_idx < VIDEO_MAX_PLANES &&
+			vb->planes[extra_idx].m.userptr) {
+		data->extradata_addr = vb->planes[extra_idx].m.userptr;
+		data->extradata_size = vb->planes[extra_idx].length;
+		data->flags |= HAL_BUFFERFLAG_EXTRADATA;
+	}
+}
+
+static unsigned int count_single_batch(struct msm_vidc_list *list,
+		enum v4l2_buf_type type)
+{
+	struct vb2_buf_entry *buf;
+	int count = 0;
+	struct vb2_v4l2_buffer *vbuf = NULL;
+
+	mutex_lock(&list->lock);
+	list_for_each_entry(buf, &list->list, list) {
+		if (buf->vb->type != type)
+			continue;
+
+		++count;
+
+		vbuf = to_vb2_v4l2_buffer(buf->vb);
+		if (!(vbuf->flags & V4L2_MSM_BUF_FLAG_DEFER))
+			goto found_batch;
+	}
+	 /* don't have a full batch */
+	count = 0;
+
+found_batch:
+	mutex_unlock(&list->lock);
+	return count;
+}
+
+static unsigned int count_buffers(struct msm_vidc_list *list,
+		enum v4l2_buf_type type)
+{
+	struct vb2_buf_entry *buf;
+	int count = 0;
+
+	mutex_lock(&list->lock);
+	list_for_each_entry(buf, &list->list, list) {
+		if (buf->vb->type != type)
+			continue;
+
+		++count;
+	}
+	mutex_unlock(&list->lock);
+
+	return count;
+}
+
+static void log_frame(struct msm_vidc_inst *inst, struct vidc_frame_data *data,
+		enum v4l2_buf_type type)
+{
+	if (type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
+		dprintk(VIDC_DBG,
+			"Sending etb (%pa) to hal: filled: %d, ts: %lld, flags = %#x\n",
+			&data->device_addr, data->filled_len,
+			data->timestamp, data->flags);
+		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_ETB);
+
+		if (msm_vidc_bitrate_clock_scaling &&
+			inst->session_type == MSM_VIDC_DECODER &&
+			!inst->dcvs_mode)
+			inst->instant_bitrate =
+				data->filled_len * 8 * inst->prop.fps;
+		else
+			inst->instant_bitrate = 0;
+	} else if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+		dprintk(VIDC_DBG,
+			"Sending ftb (%pa) to hal: size: %d, ts: %lld, flags = %#x\n",
+			&data->device_addr, data->alloc_len,
+			data->timestamp, data->flags);
+		msm_vidc_debugfs_update(inst, MSM_VIDC_DEBUGFS_EVENT_FTB);
+	}
+
+	msm_dcvs_check_and_scale_clocks(inst,
+			type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+	if (msm_vidc_bitrate_clock_scaling && !inst->dcvs_mode &&
+		type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE &&
+		inst->session_type == MSM_VIDC_DECODER)
+		if (msm_comm_scale_clocks(inst->core))
+			dprintk(VIDC_WARN,
+				"Failed to scale clocks. Performance might be impacted\n");
+
+	if (msm_comm_vote_bus(inst->core))
+		dprintk(VIDC_WARN,
+			"Failed to scale bus. Performance might be impacted\n");
+}
+
+static int request_seq_header(struct msm_vidc_inst *inst,
+		struct vidc_frame_data *data)
+{
+	struct vidc_seq_hdr seq_hdr = {
+		.seq_hdr = data->device_addr,
+		.seq_hdr_len = data->alloc_len,
+	};
+
+	dprintk(VIDC_DBG, "Requesting sequence header in %pa\n",
+			&seq_hdr.seq_hdr);
+	return call_hfi_op(inst->core->device, session_get_seq_hdr,
+			inst->session, &seq_hdr);
+}
+
+/*
+ * Attempts to queue `vb` to hardware.  If, for various reasons, the buffer
+ * cannot be queued to hardware, the buffer will be staged for commit in the
+ * pending queue.  Once the hardware reaches a good state (or if `vb` is NULL,
+ * the subsequent *_qbuf will commit the previously staged buffers to hardware.
+ */
+int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb)
+{
+	int rc = 0, capture_count, output_count;
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+	struct {
+		struct vidc_frame_data *data;
+		int count;
+	} etbs, ftbs;
+	bool defer = false, batch_mode;
+	struct vb2_buf_entry *temp, *next;
+	struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s: Invalid arguments\n", __func__);
+		return -EINVAL;
+	}
+
+	core = inst->core;
+	hdev = core->device;
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+		core->state == VIDC_CORE_INVALID ||
+		core->state == VIDC_CORE_UNINIT) {
+		dprintk(VIDC_ERR, "Core is in bad state. Can't Queue\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Stick the buffer into the pendinq, we'll pop it out later on
+	 * if we want to commit it to hardware
+	 */
+	if (vb) {
+		temp = kzalloc(sizeof(*temp), GFP_KERNEL);
+		if (!temp) {
+			dprintk(VIDC_ERR, "Out of memory\n");
+			goto err_no_mem;
+		}
+
+		temp->vb = vb;
+		mutex_lock(&inst->pendingq.lock);
+		list_add_tail(&temp->list, &inst->pendingq.list);
+		mutex_unlock(&inst->pendingq.lock);
+	}
+
+	batch_mode = msm_comm_g_ctrl_for_id(inst, V4L2_CID_VIDC_QBUF_MODE)
+		== V4L2_VIDC_QBUF_BATCHED;
+	capture_count = (batch_mode ? &count_single_batch : &count_buffers)
+		(&inst->pendingq, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+	output_count = (batch_mode ? &count_single_batch : &count_buffers)
+		(&inst->pendingq, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+
+	/*
+	 * Somewhat complicated logic to prevent queuing the buffer to hardware.
+	 * Don't queue if:
+	 * 1) Hardware isn't ready (that's simple)
+	 */
+	defer = defer ?: inst->state != MSM_VIDC_START_DONE;
+
+	/*
+	 * 2) The client explicitly tells us not to because it wants this
+	 * buffer to be batched with future frames.  The batch size (on both
+	 * capabilities) is completely determined by the client.
+	 */
+	defer = defer ?: vbuf && vbuf->flags & V4L2_MSM_BUF_FLAG_DEFER;
+
+	/* 3) If we're in batch mode, we must have full batches of both types */
+	defer = defer ?: batch_mode && (!output_count || !capture_count);
+
+	if (defer) {
+		dprintk(VIDC_DBG, "Deferring queue of %pK\n", vb);
+		return 0;
+	}
+
+	dprintk(VIDC_DBG, "%sing %d etbs and %d ftbs\n",
+			batch_mode ? "Batch" : "Process",
+			output_count, capture_count);
+
+	etbs.data = kcalloc(output_count, sizeof(*etbs.data), GFP_KERNEL);
+	ftbs.data = kcalloc(capture_count, sizeof(*ftbs.data), GFP_KERNEL);
+	/*
+	 * Note that it's perfectly normal for (e|f)tbs.data to be NULL if
+	 * we're not in batch mode (i.e. (output|capture)_count == 0)
+	 */
+	if ((!etbs.data && output_count) ||
+			(!ftbs.data && capture_count)) {
+		dprintk(VIDC_ERR, "Failed to alloc memory for batching\n");
+		kfree(etbs.data);
+		etbs.data = NULL;
+
+		kfree(ftbs.data);
+		ftbs.data = NULL;
+		goto err_no_mem;
+	}
+
+	etbs.count = ftbs.count = 0;
+
+	/*
+	 * Try to collect all pending buffers into 2 batches of ftb and etb
+	 * Note that these "batches" might be empty if we're no in batching mode
+	 * and the pendingq is empty
+	 */
+	mutex_lock(&inst->pendingq.lock);
+	list_for_each_entry_safe(temp, next, &inst->pendingq.list, list) {
+		struct vidc_frame_data *frame_data = NULL;
+
+		switch (temp->vb->type) {
+		case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+			if (ftbs.count < capture_count && ftbs.data)
+				frame_data = &ftbs.data[ftbs.count++];
+			break;
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+			if (etbs.count < output_count && etbs.data)
+				frame_data = &etbs.data[etbs.count++];
+			break;
+		default:
+			break;
+		}
+
+		if (!frame_data)
+			continue;
+
+		populate_frame_data(frame_data, temp->vb, inst);
+
+		list_del(&temp->list);
+		kfree(temp);
+	}
+	mutex_unlock(&inst->pendingq.lock);
+
+	/* Finally commit all our frame(s) to H/W */
+	if (batch_mode) {
+		int ftb_index = 0, c = 0;
+
+		for (c = 0; atomic_read(&inst->seq_hdr_reqs) > 0; ++c) {
+			rc = request_seq_header(inst, &ftbs.data[c]);
+			if (rc) {
+				dprintk(VIDC_ERR,
+						"Failed requesting sequence header: %d\n",
+						rc);
+				goto err_bad_input;
+			}
+
+			atomic_dec(&inst->seq_hdr_reqs);
+		}
+
+		ftb_index = c;
+		rc = call_hfi_op(hdev, session_process_batch, inst->session,
+				etbs.count, etbs.data,
+				ftbs.count - ftb_index, &ftbs.data[ftb_index]);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to queue batch of %d ETBs and %d FTBs\n",
+				etbs.count, ftbs.count);
+			goto err_bad_input;
+		}
+
+		for (c = ftb_index; c < ftbs.count; ++c) {
+			log_frame(inst, &ftbs.data[c],
+					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		}
+
+		for (c = 0; c < etbs.count; ++c) {
+			log_frame(inst, &etbs.data[c],
+					V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+		}
+	}
+
+	if (!batch_mode && etbs.count) {
+		int c = 0;
+
+		for (c = 0; c < etbs.count; ++c) {
+			struct vidc_frame_data *frame_data = &etbs.data[c];
+
+			rc = call_hfi_op(hdev, session_etb, inst->session,
+					frame_data);
+			if (rc) {
+				dprintk(VIDC_ERR, "Failed to issue etb: %d\n",
+						rc);
+				goto err_bad_input;
+			}
+
+			log_frame(inst, frame_data,
+					V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+		}
+	}
+
+	if (!batch_mode && ftbs.count) {
+		int c = 0;
+
+		for (c = 0; atomic_read(&inst->seq_hdr_reqs) > 0; ++c) {
+			rc = request_seq_header(inst, &ftbs.data[c]);
+			if (rc) {
+				dprintk(VIDC_ERR,
+						"Failed requesting sequence header: %d\n",
+						rc);
+				goto err_bad_input;
+			}
+
+			atomic_dec(&inst->seq_hdr_reqs);
+		}
+
+		for (; c < ftbs.count; ++c) {
+			struct vidc_frame_data *frame_data = &ftbs.data[c];
+
+			rc = call_hfi_op(hdev, session_ftb,
+					inst->session, frame_data);
+			if (rc) {
+				dprintk(VIDC_ERR, "Failed to issue ftb: %d\n",
+						rc);
+				goto err_bad_input;
+			}
+
+			log_frame(inst, frame_data,
+					V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+		}
+	}
+
+err_bad_input:
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to queue buffer\n");
+
+	kfree(etbs.data);
+	kfree(ftbs.data);
+err_no_mem:
+	return rc;
+}
+
+int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst)
+{
+	int rc = 0, i = 0;
+	union hal_get_property hprop;
+
+	rc = msm_comm_try_get_prop(inst, HAL_PARAM_GET_BUFFER_REQUIREMENTS,
+					&hprop);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed getting buffer requirements: %d", rc);
+		return rc;
+	}
+
+	dprintk(VIDC_DBG, "Buffer requirements:\n");
+	dprintk(VIDC_DBG, "%15s %8s %8s\n", "buffer type", "count", "size");
+	for (i = 0; i < HAL_BUFFER_MAX; i++) {
+		struct hal_buffer_requirements req = hprop.buf_req.buffer[i];
+
+		inst->buff_req.buffer[i] = req;
+		dprintk(VIDC_DBG, "%15s %8d %8d\n",
+				get_buffer_name(req.buffer_type),
+				req.buffer_count_actual, req.buffer_size);
+	}
+
+	dprintk(VIDC_PROF, "Input buffers: %d, Output buffers: %d\n",
+			inst->buff_req.buffer[0].buffer_count_actual,
+			inst->buff_req.buffer[1].buffer_count_actual);
+	return rc;
+}
+
+int msm_comm_try_get_prop(struct msm_vidc_inst *inst, enum hal_property ptype,
+				union hal_get_property *hprop)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+	struct getprop_buf *buf;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+	mutex_lock(&inst->sync_lock);
+	if (inst->state < MSM_VIDC_OPEN_DONE ||
+			inst->state >= MSM_VIDC_CLOSE) {
+
+		/* No need to check inst->state == MSM_VIDC_INVALID since
+		 * INVALID is > CLOSE_DONE. When core went to INVALID state,
+		 * we put all the active instances in INVALID. So > CLOSE_DONE
+		 * is enough check to have.
+		 */
+
+		dprintk(VIDC_ERR,
+			"In Wrong state to call Buf Req: Inst %pK or Core %pK\n",
+				inst, inst->core);
+		rc = -EAGAIN;
+		mutex_unlock(&inst->sync_lock);
+		goto exit;
+	}
+	mutex_unlock(&inst->sync_lock);
+
+	switch (ptype) {
+	case HAL_PARAM_PROFILE_LEVEL_CURRENT:
+	case HAL_CONFIG_VDEC_ENTROPY:
+		rc = call_hfi_op(hdev, session_get_property, inst->session,
+				ptype);
+		break;
+	case HAL_PARAM_GET_BUFFER_REQUIREMENTS:
+		rc = call_hfi_op(hdev, session_get_buf_req, inst->session);
+		break;
+	default:
+		rc = -EAGAIN;
+		break;
+	}
+
+	if (rc) {
+		dprintk(VIDC_ERR, "Can't query hardware for property: %d\n",
+				rc);
+		goto exit;
+	}
+
+	rc = wait_for_completion_timeout(&inst->completions[
+			SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO)],
+		msecs_to_jiffies(msm_vidc_hw_rsp_timeout));
+	if (!rc) {
+		dprintk(VIDC_ERR,
+			"%s: Wait interrupted or timed out [%pK]: %d\n",
+			__func__, inst,
+			SESSION_MSG_INDEX(HAL_SESSION_PROPERTY_INFO));
+		inst->state = MSM_VIDC_CORE_INVALID;
+		msm_comm_kill_session(inst);
+		call_hfi_op(hdev, flush_debug_queue, hdev->hfi_device_data);
+		dprintk(VIDC_ERR,
+			"SESS_PROP timeout can potentially crash the system\n");
+		if (inst->core->resources.debug_timeout)
+			msm_comm_print_debug_info(inst);
+
+		msm_vidc_handle_hw_error(inst->core);
+		rc = -ETIMEDOUT;
+		goto exit;
+	} else {
+		/* wait_for_completion_timeout returns jiffies before expiry */
+		rc = 0;
+	}
+
+	mutex_lock(&inst->pending_getpropq.lock);
+	if (!list_empty(&inst->pending_getpropq.list)) {
+		buf = list_first_entry(&inst->pending_getpropq.list,
+					struct getprop_buf, list);
+		*hprop = *(union hal_get_property *)buf->data;
+		kfree(buf->data);
+		list_del(&buf->list);
+		kfree(buf);
+	} else {
+		dprintk(VIDC_ERR, "%s getprop list empty\n", __func__);
+		rc = -EINVAL;
+	}
+	mutex_unlock(&inst->pending_getpropq.lock);
+exit:
+	return rc;
+}
+
+int msm_comm_release_output_buffers(struct msm_vidc_inst *inst)
+{
+	struct msm_smem *handle;
+	struct internal_buf *buf, *dummy;
+	struct vidc_buffer_addr_info buffer_info;
+	int rc = 0;
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %pK\n", inst);
+		return -EINVAL;
+	}
+	mutex_lock(&inst->outputbufs.lock);
+	if (list_empty(&inst->outputbufs.list)) {
+		dprintk(VIDC_DBG, "%s - No OUTPUT buffers allocated\n",
+			__func__);
+		mutex_unlock(&inst->outputbufs.lock);
+		return 0;
+	}
+	mutex_unlock(&inst->outputbufs.lock);
+
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %pK\n", core);
+		return -EINVAL;
+	}
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
+		return -EINVAL;
+	}
+	mutex_lock(&inst->outputbufs.lock);
+	list_for_each_entry_safe(buf, dummy, &inst->outputbufs.list, list) {
+		handle = buf->handle;
+		if (!handle) {
+			dprintk(VIDC_ERR, "%s - invalid handle\n", __func__);
+			goto exit;
+		}
+
+		buffer_info.buffer_size = handle->size;
+		buffer_info.buffer_type = buf->buffer_type;
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr = handle->device_addr;
+		if (inst->buffer_mode_set[CAPTURE_PORT] ==
+			HAL_BUFFER_MODE_STATIC &&
+			inst->state != MSM_VIDC_CORE_INVALID &&
+				core->state != VIDC_CORE_INVALID) {
+			buffer_info.response_required = false;
+			rc = call_hfi_op(hdev, session_release_buffers,
+				(void *)inst->session, &buffer_info);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Rel output buf fail:%pa, %d\n",
+					&buffer_info.align_device_addr,
+					buffer_info.buffer_size);
+			}
+		}
+
+		list_del(&buf->list);
+		msm_comm_smem_free(inst, buf->handle);
+		kfree(buf);
+	}
+
+exit:
+	mutex_unlock(&inst->outputbufs.lock);
+	return rc;
+}
+
+static enum hal_buffer scratch_buf_sufficient(struct msm_vidc_inst *inst,
+				enum hal_buffer buffer_type)
+{
+	struct hal_buffer_requirements *bufreq = NULL;
+	struct internal_buf *buf;
+	int count = 0;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
+		goto not_sufficient;
+	}
+
+	bufreq = get_buff_req_buffer(inst, buffer_type);
+	if (!bufreq)
+		goto not_sufficient;
+
+	/* Check if current scratch buffers are sufficient */
+	mutex_lock(&inst->scratchbufs.lock);
+
+	list_for_each_entry(buf, &inst->scratchbufs.list, list) {
+		if (!buf->handle) {
+			dprintk(VIDC_ERR, "%s: invalid buf handle\n", __func__);
+			mutex_unlock(&inst->scratchbufs.lock);
+			goto not_sufficient;
+		}
+		if (buf->buffer_type == buffer_type &&
+			buf->handle->size >= bufreq->buffer_size)
+			count++;
+	}
+	mutex_unlock(&inst->scratchbufs.lock);
+
+	if (count != bufreq->buffer_count_actual)
+		goto not_sufficient;
+
+	dprintk(VIDC_DBG,
+		"Existing scratch buffer is sufficient for buffer type %#x\n",
+		buffer_type);
+
+	return buffer_type;
+
+not_sufficient:
+	return HAL_BUFFER_NONE;
+}
+
+int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst,
+					bool check_for_reuse)
+{
+	struct msm_smem *handle;
+	struct internal_buf *buf, *dummy;
+	struct vidc_buffer_addr_info buffer_info;
+	int rc = 0;
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+	enum hal_buffer sufficiency = HAL_BUFFER_NONE;
+
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %pK\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %pK\n", core);
+		return -EINVAL;
+	}
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
+		return -EINVAL;
+	}
+
+	if (check_for_reuse) {
+		sufficiency |= scratch_buf_sufficient(inst,
+					HAL_BUFFER_INTERNAL_SCRATCH);
+
+		sufficiency |= scratch_buf_sufficient(inst,
+					HAL_BUFFER_INTERNAL_SCRATCH_1);
+
+		sufficiency |= scratch_buf_sufficient(inst,
+					HAL_BUFFER_INTERNAL_SCRATCH_2);
+	}
+
+	mutex_lock(&inst->scratchbufs.lock);
+	list_for_each_entry_safe(buf, dummy, &inst->scratchbufs.list, list) {
+		if (!buf->handle) {
+			dprintk(VIDC_ERR, "%s - buf->handle NULL\n", __func__);
+			rc = -EINVAL;
+			goto exit;
+		}
+
+		handle = buf->handle;
+		buffer_info.buffer_size = handle->size;
+		buffer_info.buffer_type = buf->buffer_type;
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr = handle->device_addr;
+		if (inst->state != MSM_VIDC_CORE_INVALID &&
+				core->state != VIDC_CORE_INVALID) {
+			buffer_info.response_required = true;
+			rc = call_hfi_op(hdev, session_release_buffers,
+				(void *)inst->session, &buffer_info);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Rel scrtch buf fail:%pa, %d\n",
+					&buffer_info.align_device_addr,
+					buffer_info.buffer_size);
+			}
+			mutex_unlock(&inst->scratchbufs.lock);
+			rc = wait_for_sess_signal_receipt(inst,
+				HAL_SESSION_RELEASE_BUFFER_DONE);
+			if (rc) {
+				change_inst_state(inst,
+					MSM_VIDC_CORE_INVALID);
+				msm_comm_kill_session(inst);
+			}
+			mutex_lock(&inst->scratchbufs.lock);
+		}
+
+		/*If scratch buffers can be reused, do not free the buffers*/
+		if (sufficiency & buf->buffer_type)
+			continue;
+
+		list_del(&buf->list);
+		msm_comm_smem_free(inst, buf->handle);
+		kfree(buf);
+	}
+
+exit:
+	mutex_unlock(&inst->scratchbufs.lock);
+	return rc;
+}
+
+int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst)
+{
+	struct msm_smem *handle;
+	struct list_head *ptr, *next;
+	struct internal_buf *buf;
+	struct vidc_buffer_addr_info buffer_info;
+	int rc = 0;
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %pK\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %pK\n", core);
+		return -EINVAL;
+	}
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
+		return -EINVAL;
+	}
+
+	mutex_lock(&inst->persistbufs.lock);
+	list_for_each_safe(ptr, next, &inst->persistbufs.list) {
+		buf = list_entry(ptr, struct internal_buf, list);
+		handle = buf->handle;
+		buffer_info.buffer_size = handle->size;
+		buffer_info.buffer_type = buf->buffer_type;
+		buffer_info.num_buffers = 1;
+		buffer_info.align_device_addr = handle->device_addr;
+		if (inst->state != MSM_VIDC_CORE_INVALID &&
+				core->state != VIDC_CORE_INVALID) {
+			buffer_info.response_required = true;
+			rc = call_hfi_op(hdev, session_release_buffers,
+				(void *)inst->session, &buffer_info);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Rel prst buf fail:%pa, %d\n",
+					&buffer_info.align_device_addr,
+					buffer_info.buffer_size);
+			}
+			mutex_unlock(&inst->persistbufs.lock);
+			rc = wait_for_sess_signal_receipt(inst,
+				HAL_SESSION_RELEASE_BUFFER_DONE);
+			if (rc) {
+				change_inst_state(inst, MSM_VIDC_CORE_INVALID);
+				msm_comm_kill_session(inst);
+			}
+			mutex_lock(&inst->persistbufs.lock);
+		}
+		list_del(&buf->list);
+		msm_comm_smem_free(inst, buf->handle);
+		kfree(buf);
+	}
+	mutex_unlock(&inst->persistbufs.lock);
+	return rc;
+}
+
+int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
+	enum hal_property ptype, void *pdata)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid input: %pK\n", inst);
+		return -EINVAL;
+	}
+
+	if (!inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+	hdev = inst->core->device;
+
+	mutex_lock(&inst->sync_lock);
+	if (inst->state < MSM_VIDC_OPEN_DONE || inst->state >= MSM_VIDC_CLOSE) {
+		dprintk(VIDC_ERR, "Not in proper state to set property\n");
+		rc = -EAGAIN;
+		goto exit;
+	}
+	rc = call_hfi_op(hdev, session_set_property, (void *)inst->session,
+			ptype, pdata);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to set hal property for framesize\n");
+exit:
+	mutex_unlock(&inst->sync_lock);
+	return rc;
+}
+
+int msm_comm_set_output_buffers(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (msm_comm_release_output_buffers(inst))
+		dprintk(VIDC_WARN, "Failed to release output buffers\n");
+
+	rc = set_output_buffers(inst, HAL_BUFFER_OUTPUT);
+	if (rc)
+		goto error;
+	return rc;
+error:
+	msm_comm_release_output_buffers(inst);
+	return rc;
+}
+
+int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (msm_comm_release_scratch_buffers(inst, true))
+		dprintk(VIDC_WARN, "Failed to release scratch buffers\n");
+
+	rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH,
+		&inst->scratchbufs);
+	if (rc)
+		goto error;
+
+	rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_1,
+		&inst->scratchbufs);
+	if (rc)
+		goto error;
+
+	rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_SCRATCH_2,
+		&inst->scratchbufs);
+	if (rc)
+		goto error;
+
+	return rc;
+error:
+	msm_comm_release_scratch_buffers(inst, false);
+	return rc;
+}
+
+int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST,
+		&inst->persistbufs);
+	if (rc)
+		goto error;
+
+	rc = set_internal_buffers(inst, HAL_BUFFER_INTERNAL_PERSIST_1,
+		&inst->persistbufs);
+	if (rc)
+		goto error;
+	return rc;
+error:
+	msm_comm_release_persist_buffers(inst);
+	return rc;
+}
+
+static void msm_comm_flush_in_invalid_state(struct msm_vidc_inst *inst)
+{
+	struct list_head *ptr, *next;
+	enum vidc_ports ports[] = {OUTPUT_PORT, CAPTURE_PORT};
+	int c = 0;
+
+	for (c = 0; c < ARRAY_SIZE(ports); ++c) {
+		enum vidc_ports port = ports[c];
+
+		dprintk(VIDC_DBG, "Flushing buffers of type %d in bad state\n",
+				port);
+		mutex_lock(&inst->bufq[port].lock);
+		list_for_each_safe(ptr, next, &inst->bufq[port].
+				vb2_bufq.queued_list) {
+			struct vb2_buffer *vb = container_of(ptr,
+					struct vb2_buffer, queued_entry);
+
+			vb->planes[0].bytesused = 0;
+			vb->planes[0].data_offset = 0;
+
+			vb2_buffer_done(vb, VB2_BUF_STATE_DONE);
+		}
+		mutex_unlock(&inst->bufq[port].lock);
+	}
+
+	msm_vidc_queue_v4l2_event(inst, V4L2_EVENT_MSM_VIDC_FLUSH_DONE);
+}
+
+void msm_comm_flush_dynamic_buffers(struct msm_vidc_inst *inst)
+{
+	struct buffer_info *binfo = NULL;
+
+	if (inst->buffer_mode_set[CAPTURE_PORT] != HAL_BUFFER_MODE_DYNAMIC)
+		return;
+
+	/*
+	 * dynamic buffer mode:- if flush is called during seek
+	 * driver should not queue any new buffer it has been holding.
+	 *
+	 * Each dynamic o/p buffer can have one of following ref_count:
+	 * ref_count : 0 - f/w has released reference and sent fbd back.
+	 *		  The buffer has been returned back to client.
+	 *
+	 * ref_count : 1 - f/w is holding reference. f/w may have released
+	 *                 fbd as read_only OR fbd is pending. f/w will
+	 *		  release reference before sending flush_done.
+	 *
+	 * ref_count : 2 - f/w is holding reference, f/w has released fbd as
+	 *                 read_only, which client has queued back to driver.
+	 *                 driver holds this buffer and will queue back
+	 *                 only when f/w releases the reference. During
+	 *		  flush_done, f/w will release the reference but driver
+	 *		  should not queue back the buffer to f/w.
+	 *		  Flush all buffers with ref_count 2.
+	 */
+	mutex_lock(&inst->registeredbufs.lock);
+	if (!list_empty(&inst->registeredbufs.list)) {
+		struct v4l2_event buf_event = {0};
+		u32 *ptr = NULL;
+
+		list_for_each_entry(binfo, &inst->registeredbufs.list, list) {
+			if (binfo->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+				atomic_read(&binfo->ref_count) == 2) {
+
+				atomic_dec(&binfo->ref_count);
+				buf_event.type =
+				V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER;
+				ptr = (u32 *)buf_event.u.data;
+				ptr[0] = binfo->fd[0];
+				ptr[1] = binfo->buff_off[0];
+				ptr[2] = binfo->uvaddr[0];
+				ptr[3] = (u32) binfo->timestamp.tv_sec;
+				ptr[4] = (u32) binfo->timestamp.tv_usec;
+				ptr[5] = binfo->v4l2_index;
+				dprintk(VIDC_DBG,
+					"released buffer held in driver before issuing flush: %pa fd[0]: %d\n",
+					&binfo->device_addr[0], binfo->fd[0]);
+				/*send event to client*/
+				v4l2_event_queue_fh(&inst->event_handler,
+					&buf_event);
+			}
+		}
+	}
+	mutex_unlock(&inst->registeredbufs.lock);
+}
+
+void msm_comm_flush_pending_dynamic_buffers(struct msm_vidc_inst *inst)
+{
+	struct buffer_info *binfo = NULL;
+
+	if (!inst)
+		return;
+
+	if (inst->buffer_mode_set[CAPTURE_PORT] != HAL_BUFFER_MODE_DYNAMIC)
+		return;
+
+	if (list_empty(&inst->pendingq.list) ||
+		list_empty(&inst->registeredbufs.list))
+		return;
+
+	/*
+	 * Dynamic Buffer mode - Since pendingq is not empty
+	 * no output buffers have been sent to firmware yet.
+	 * Hence remove reference to all pendingq o/p buffers
+	 * before flushing them.
+	 */
+
+	mutex_lock(&inst->registeredbufs.lock);
+	list_for_each_entry(binfo, &inst->registeredbufs.list, list) {
+		if (binfo->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+			dprintk(VIDC_DBG,
+				"%s: binfo = %pK device_addr = %pa\n",
+				__func__, binfo, &binfo->device_addr[0]);
+			buf_ref_put(inst, binfo);
+		}
+	}
+	mutex_unlock(&inst->registeredbufs.lock);
+}
+
+int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags)
+{
+	int rc =  0;
+	bool ip_flush = false;
+	bool op_flush = false;
+	struct vb2_buf_entry *temp, *next;
+	struct mutex *lock;
+	struct msm_vidc_core *core;
+	struct hfi_device *hdev;
+
+	if (!inst) {
+		dprintk(VIDC_ERR,
+				"Invalid instance pointer = %pK\n", inst);
+		return -EINVAL;
+	}
+	core = inst->core;
+	if (!core) {
+		dprintk(VIDC_ERR,
+				"Invalid core pointer = %pK\n", core);
+		return -EINVAL;
+	}
+	hdev = core->device;
+	if (!hdev) {
+		dprintk(VIDC_ERR, "Invalid device pointer = %pK\n", hdev);
+		return -EINVAL;
+	}
+
+	ip_flush = flags & V4L2_QCOM_CMD_FLUSH_OUTPUT;
+	op_flush = flags & V4L2_QCOM_CMD_FLUSH_CAPTURE;
+
+	if (ip_flush && !op_flush) {
+		dprintk(VIDC_INFO, "Input only flush not supported\n");
+		return 0;
+	}
+
+	msm_comm_flush_dynamic_buffers(inst);
+
+	if (inst->state == MSM_VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_INVALID ||
+			core->state == VIDC_CORE_UNINIT) {
+		dprintk(VIDC_ERR,
+				"Core %pK and inst %pK are in bad state\n",
+					core, inst);
+		msm_comm_flush_in_invalid_state(inst);
+		return 0;
+	}
+
+	if (inst->in_reconfig && !ip_flush && op_flush) {
+		mutex_lock(&inst->pendingq.lock);
+		if (!list_empty(&inst->pendingq.list)) {
+			/*
+			 * Execution can never reach here since port reconfig
+			 * wont happen unless pendingq is emptied out
+			 * (both pendingq and flush being secured with same
+			 * lock). Printing a message here incase this breaks.
+			 */
+			dprintk(VIDC_WARN,
+			"FLUSH BUG: Pending q not empty! It should be empty\n");
+		}
+		mutex_unlock(&inst->pendingq.lock);
+		atomic_inc(&inst->in_flush);
+		dprintk(VIDC_DBG, "Send flush Output to firmware\n");
+		rc = call_hfi_op(hdev, session_flush, inst->session,
+				HAL_FLUSH_OUTPUT);
+	} else {
+		msm_comm_flush_pending_dynamic_buffers(inst);
+		/*
+		 * If flush is called after queueing buffers but before
+		 * streamon driver should flush the pending queue
+		 */
+		mutex_lock(&inst->pendingq.lock);
+		list_for_each_entry_safe(temp, next,
+				&inst->pendingq.list, list) {
+			enum v4l2_buf_type type = temp->vb->type;
+
+			if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
+				lock = &inst->bufq[CAPTURE_PORT].lock;
+			else
+				lock = &inst->bufq[OUTPUT_PORT].lock;
+
+			temp->vb->planes[0].bytesused = 0;
+
+			mutex_lock(lock);
+			vb2_buffer_done(temp->vb, VB2_BUF_STATE_DONE);
+			msm_vidc_debugfs_update(inst,
+				type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE ?
+					MSM_VIDC_DEBUGFS_EVENT_FBD :
+					MSM_VIDC_DEBUGFS_EVENT_EBD);
+			list_del(&temp->list);
+			mutex_unlock(lock);
+
+			kfree(temp);
+		}
+		mutex_unlock(&inst->pendingq.lock);
+
+		/*Do not send flush in case of session_error */
+		if (!(inst->state == MSM_VIDC_CORE_INVALID &&
+			  core->state != VIDC_CORE_INVALID)) {
+			atomic_inc(&inst->in_flush);
+			dprintk(VIDC_DBG, "Send flush all to firmware\n");
+			rc = call_hfi_op(hdev, session_flush, inst->session,
+				HAL_FLUSH_ALL);
+		}
+	}
+
+	return rc;
+}
+
+
+enum hal_extradata_id msm_comm_get_hal_extradata_index(
+	enum v4l2_mpeg_vidc_extradata index)
+{
+	int ret = 0;
+
+	switch (index) {
+	case V4L2_MPEG_VIDC_EXTRADATA_NONE:
+		ret = HAL_EXTRADATA_NONE;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION:
+		ret = HAL_EXTRADATA_MB_QUANTIZATION;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO:
+		ret = HAL_EXTRADATA_INTERLACE_VIDEO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP:
+		ret = HAL_EXTRADATA_TIMESTAMP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING:
+		ret = HAL_EXTRADATA_S3D_FRAME_PACKING;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE:
+		ret = HAL_EXTRADATA_FRAME_RATE;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW:
+		ret = HAL_EXTRADATA_PANSCAN_WINDOW;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI:
+		ret = HAL_EXTRADATA_RECOVERY_POINT_SEI;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO:
+		ret = HAL_EXTRADATA_MULTISLICE_INFO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB:
+		ret = HAL_EXTRADATA_NUM_CONCEALED_MB;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER:
+		ret = HAL_EXTRADATA_METADATA_FILLER;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO:
+		ret = HAL_EXTRADATA_ASPECT_RATIO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP:
+		ret = HAL_EXTRADATA_INPUT_CROP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM:
+		ret = HAL_EXTRADATA_DIGITAL_ZOOM;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP:
+		ret = HAL_EXTRADATA_MPEG2_SEQDISP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA:
+		ret = HAL_EXTRADATA_STREAM_USERDATA;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP:
+		ret = HAL_EXTRADATA_FRAME_QP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO:
+		ret = HAL_EXTRADATA_FRAME_BITS_INFO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_LTR:
+		ret = HAL_EXTRADATA_LTR_INFO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI:
+		ret = HAL_EXTRADATA_METADATA_MBI;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI:
+		ret = HAL_EXTRADATA_VQZIP_SEI;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS:
+		ret = HAL_EXTRADATA_YUV_STATS;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_ROI_QP:
+		ret = HAL_EXTRADATA_ROI_QP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP:
+		ret = HAL_EXTRADATA_OUTPUT_CROP;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI:
+		ret = HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI:
+		ret = HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO:
+		ret = HAL_EXTRADATA_PQ_INFO;
+		break;
+
+	case V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY:
+		ret = HAL_EXTRADATA_VUI_DISPLAY_INFO;
+		break;
+	case V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE:
+		ret = HAL_EXTRADATA_VPX_COLORSPACE;
+		break;
+	default:
+		dprintk(VIDC_WARN, "Extradata not found: %d\n", index);
+		break;
+	}
+	return ret;
+};
+
+int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
+	enum hal_ssr_trigger_type type)
+{
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!core || !core->device) {
+		dprintk(VIDC_WARN, "Invalid parameters: %pK\n", core);
+		return -EINVAL;
+	}
+	hdev = core->device;
+	if (core->state == VIDC_CORE_INIT_DONE) {
+		/*
+		 * In current implementation user-initiated SSR triggers
+		 * a fatal error from hardware. However, there is no way
+		 * to know if fatal error is due to SSR or not. Handle
+		 * user SSR as non-fatal.
+		 */
+		mutex_lock(&core->lock);
+		core->resources.debug_timeout = false;
+		mutex_unlock(&core->lock);
+		rc = call_hfi_op(hdev, core_trigger_ssr,
+				hdev->hfi_device_data, type);
+	}
+
+	return rc;
+}
+
+static int msm_vidc_load_supported(struct msm_vidc_inst *inst)
+{
+	int num_mbs_per_sec = 0, max_load_adj = 0;
+	enum load_calc_quirks quirks = LOAD_CALC_IGNORE_TURBO_LOAD |
+		LOAD_CALC_IGNORE_THUMBNAIL_LOAD |
+		LOAD_CALC_IGNORE_NON_REALTIME_LOAD;
+
+	if (inst->state == MSM_VIDC_OPEN_DONE) {
+		max_load_adj = inst->core->resources.max_load +
+			inst->capability.mbs_per_frame.max;
+		num_mbs_per_sec = msm_comm_get_load(inst->core,
+					MSM_VIDC_DECODER, quirks);
+		num_mbs_per_sec += msm_comm_get_load(inst->core,
+					MSM_VIDC_ENCODER, quirks);
+		if (num_mbs_per_sec > max_load_adj) {
+			dprintk(VIDC_ERR,
+				"H/W is overloaded. needed: %d max: %d\n",
+				num_mbs_per_sec,
+				max_load_adj);
+			msm_vidc_print_running_insts(inst->core);
+			return -EBUSY;
+		}
+	}
+	return 0;
+}
+
+int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst)
+{
+	u32 x_min, x_max, y_min, y_max;
+	u32 input_height, input_width, output_height, output_width;
+
+	input_height = inst->prop.height[OUTPUT_PORT];
+	input_width = inst->prop.width[OUTPUT_PORT];
+	output_height = inst->prop.height[CAPTURE_PORT];
+	output_width = inst->prop.width[CAPTURE_PORT];
+
+	if (!input_height || !input_width || !output_height || !output_width) {
+		dprintk(VIDC_ERR,
+			"Invalid : Input height = %d width = %d",
+			input_height, input_width);
+		dprintk(VIDC_ERR,
+			" output height = %d width = %d\n",
+			output_height, output_width);
+		return -ENOTSUPP;
+	}
+
+	if (!inst->capability.scale_x.min ||
+		!inst->capability.scale_x.max ||
+		!inst->capability.scale_y.min ||
+		!inst->capability.scale_y.max) {
+
+		if (input_width * input_height !=
+			output_width * output_height) {
+			dprintk(VIDC_ERR,
+				"%s: scaling is not supported (%dx%d != %dx%d)\n",
+				__func__, input_width, input_height,
+				output_width, output_height);
+			return -ENOTSUPP;
+		}
+
+		dprintk(VIDC_DBG, "%s: supported WxH = %dx%d\n",
+			__func__, input_width, input_height);
+		return 0;
+	}
+
+	x_min = (1<<16)/inst->capability.scale_x.min;
+	y_min = (1<<16)/inst->capability.scale_y.min;
+	x_max = inst->capability.scale_x.max >> 16;
+	y_max = inst->capability.scale_y.max >> 16;
+
+	if (input_height > output_height) {
+		if (input_height > x_min * output_height) {
+			dprintk(VIDC_ERR,
+				"Unsupported height downscale ratio %d vs %d\n",
+				input_height/output_height, x_min);
+			return -ENOTSUPP;
+		}
+	} else {
+		if (output_height > x_max * input_height) {
+			dprintk(VIDC_ERR,
+				"Unsupported height upscale ratio %d vs %d\n",
+				input_height/output_height, x_max);
+			return -ENOTSUPP;
+		}
+	}
+	if (input_width > output_width) {
+		if (input_width > y_min * output_width) {
+			dprintk(VIDC_ERR,
+				"Unsupported width downscale ratio %d vs %d\n",
+				input_width/output_width, y_min);
+			return -ENOTSUPP;
+		}
+	} else {
+		if (output_width > y_max * input_width) {
+			dprintk(VIDC_ERR,
+				"Unsupported width upscale ratio %d vs %d\n",
+				input_width/output_width, y_max);
+			return -ENOTSUPP;
+		}
+	}
+	return 0;
+}
+
+int msm_vidc_check_session_supported(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_capability *capability;
+	int rc = 0;
+	struct hfi_device *hdev;
+	struct msm_vidc_core *core;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_WARN, "%s: Invalid parameter\n", __func__);
+		return -EINVAL;
+	}
+	capability = &inst->capability;
+	hdev = inst->core->device;
+	core = inst->core;
+	rc = msm_vidc_load_supported(inst);
+	if (rc) {
+		change_inst_state(inst, MSM_VIDC_CORE_INVALID);
+		msm_comm_kill_session(inst);
+		dprintk(VIDC_WARN,
+			"%s: Hardware is overloaded\n", __func__);
+		return rc;
+	}
+
+	if (!is_thermal_permissible(core)) {
+		dprintk(VIDC_WARN,
+			"Thermal level critical, stop all active sessions!\n");
+		return -ENOTSUPP;
+	}
+
+	if (!rc)
+		msm_dcvs_try_enable(inst);
+
+	if (!rc) {
+		if (inst->prop.width[CAPTURE_PORT] < capability->width.min ||
+			inst->prop.height[CAPTURE_PORT] <
+			capability->height.min) {
+			dprintk(VIDC_ERR,
+				"Unsupported WxH = (%u)x(%u), min supported is - (%u)x(%u)\n",
+				inst->prop.width[CAPTURE_PORT],
+				inst->prop.height[CAPTURE_PORT],
+				capability->width.min,
+				capability->height.min);
+			rc = -ENOTSUPP;
+		}
+		if (!rc && inst->prop.width[CAPTURE_PORT] >
+			capability->width.max) {
+			dprintk(VIDC_ERR,
+				"Unsupported width = %u supported max width = %u",
+				inst->prop.width[CAPTURE_PORT],
+				capability->width.max);
+				rc = -ENOTSUPP;
+		}
+
+		if (!rc && inst->prop.height[CAPTURE_PORT]
+			* inst->prop.width[CAPTURE_PORT] >
+			capability->width.max * capability->height.max) {
+			dprintk(VIDC_ERR,
+			"Unsupported WxH = (%u)x(%u), max supported is - (%u)x(%u)\n",
+			inst->prop.width[CAPTURE_PORT],
+			inst->prop.height[CAPTURE_PORT],
+			capability->width.max, capability->height.max);
+			rc = -ENOTSUPP;
+		}
+	}
+	if (rc) {
+		change_inst_state(inst, MSM_VIDC_CORE_INVALID);
+		msm_comm_kill_session(inst);
+		dprintk(VIDC_ERR,
+			"%s: Resolution unsupported\n", __func__);
+	}
+	return rc;
+}
+
+static void msm_comm_generate_session_error(struct msm_vidc_inst *inst)
+{
+	enum hal_command_response cmd = HAL_SESSION_ERROR;
+	struct msm_vidc_cb_cmd_done response = {0};
+
+	dprintk(VIDC_WARN, "msm_comm_generate_session_error\n");
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
+		return;
+	}
+
+	response.session_id = inst;
+	response.status = VIDC_ERR_FAIL;
+	handle_session_error(cmd, (void *)&response);
+}
+
+static void msm_comm_generate_sys_error(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core;
+	enum hal_command_response cmd = HAL_SYS_ERROR;
+	struct msm_vidc_cb_cmd_done response  = {0};
+
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
+		return;
+	}
+	core = inst->core;
+	response.device_id = (u32) core->id;
+	handle_sys_error(cmd, (void *) &response);
+
+}
+
+int msm_comm_kill_session(struct msm_vidc_inst *inst)
+{
+	int rc = 0;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s: invalid input parameters\n", __func__);
+		return -EINVAL;
+	} else if (!inst->session) {
+		/* There's no hfi session to kill */
+		return 0;
+	}
+
+	/*
+	 * We're internally forcibly killing the session, if fw is aware of
+	 * the session send session_abort to firmware to clean up and release
+	 * the session, else just kill the session inside the driver.
+	 */
+	if ((inst->state >= MSM_VIDC_OPEN_DONE &&
+			inst->state < MSM_VIDC_CLOSE_DONE) ||
+			inst->state == MSM_VIDC_CORE_INVALID) {
+		if (msm_comm_session_abort(inst)) {
+			msm_comm_generate_sys_error(inst);
+			return 0;
+		}
+		change_inst_state(inst, MSM_VIDC_CLOSE_DONE);
+		msm_comm_generate_session_error(inst);
+	} else {
+		dprintk(VIDC_WARN,
+				"Inactive session %pK, triggering an internal session error\n",
+				inst);
+		msm_comm_generate_session_error(inst);
+
+	}
+
+	return rc;
+}
+
+struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst,
+			size_t size, u32 align, u32 flags,
+			enum hal_buffer buffer_type, int map_kernel)
+{
+	struct msm_smem *m = NULL;
+
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst);
+		return NULL;
+	}
+	m = msm_smem_alloc(inst->mem_client, size, align,
+				flags, buffer_type, map_kernel);
+	return m;
+}
+
+void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem)
+{
+	if (!inst || !inst->core || !mem) {
+		dprintk(VIDC_ERR,
+			"%s: invalid params: %pK %pK\n", __func__, inst, mem);
+		return;
+	}
+	msm_smem_free(inst->mem_client, mem);
+}
+
+int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
+		struct msm_smem *mem, enum smem_cache_ops cache_ops)
+{
+	if (!inst || !mem) {
+		dprintk(VIDC_ERR,
+			"%s: invalid params: %pK %pK\n", __func__, inst, mem);
+		return -EINVAL;
+	}
+	return msm_smem_cache_operations(inst->mem_client, mem, cache_ops);
+}
+
+struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
+			int fd, u32 offset, enum hal_buffer buffer_type)
+{
+	struct msm_smem *m = NULL;
+
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s: invalid inst: %pK\n", __func__, inst);
+		return NULL;
+	}
+
+	if (inst->state == MSM_VIDC_CORE_INVALID) {
+		dprintk(VIDC_ERR, "Core in Invalid state, returning from %s\n",
+			__func__);
+		return NULL;
+	}
+
+	m = msm_smem_user_to_kernel(inst->mem_client,
+			fd, offset, buffer_type);
+	return m;
+}
+
+void msm_vidc_fw_unload_handler(struct work_struct *work)
+{
+	struct msm_vidc_core *core = NULL;
+	struct hfi_device *hdev = NULL;
+	int rc = 0;
+
+	core = container_of(work, struct msm_vidc_core, fw_unload_work.work);
+	if (!core || !core->device) {
+		dprintk(VIDC_ERR, "%s - invalid work or core handle\n",
+				__func__);
+		return;
+	}
+
+	hdev = core->device;
+
+	mutex_lock(&core->lock);
+	if (list_empty(&core->instances) &&
+		core->state != VIDC_CORE_UNINIT) {
+		if (core->state > VIDC_CORE_INIT) {
+			dprintk(VIDC_DBG, "Calling vidc_hal_core_release\n");
+			rc = call_hfi_op(hdev, core_release,
+					hdev->hfi_device_data);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed to release core, id = %d\n",
+					core->id);
+				mutex_unlock(&core->lock);
+				return;
+			}
+		}
+		core->state = VIDC_CORE_UNINIT;
+		kfree(core->capabilities);
+		core->capabilities = NULL;
+	}
+	mutex_unlock(&core->lock);
+}
+
+int msm_comm_set_color_format(struct msm_vidc_inst *inst,
+		enum hal_buffer buffer_type, int fourcc)
+{
+	struct hal_uncompressed_format_select hal_fmt = {0};
+	enum hal_uncompressed_format format = HAL_UNUSED_COLOR;
+	int rc = 0;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device) {
+		dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+
+	format = get_hal_uncompressed(fourcc);
+	if (format == HAL_UNUSED_COLOR) {
+		dprintk(VIDC_ERR, "Using unsupported colorformat %#x\n",
+				fourcc);
+		rc = -ENOTSUPP;
+		goto exit;
+	}
+
+	hal_fmt.buffer_type = buffer_type;
+	hal_fmt.format = format;
+
+	rc = call_hfi_op(hdev, session_set_property, inst->session,
+		HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT, &hal_fmt);
+	if (rc)
+		dprintk(VIDC_ERR,
+			"Failed to set input color format\n");
+	else
+		dprintk(VIDC_DBG, "Setting uncompressed colorformat to %#x\n",
+				format);
+
+exit:
+	return rc;
+}
+
+int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a)
+{
+	u32 property_id = 0;
+	u64 us_per_frame = 0;
+	void *pdata;
+	int rc = 0, fps = 0;
+	struct hal_frame_rate frame_rate;
+	struct hfi_device *hdev;
+
+	if (!inst || !inst->core || !inst->core->device || !a) {
+		dprintk(VIDC_ERR, "%s invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	hdev = inst->core->device;
+	property_id = HAL_CONFIG_FRAME_RATE;
+
+	if (a->parm.output.timeperframe.denominator) {
+		switch (a->type) {
+		case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
+		case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
+			us_per_frame = a->parm.output.timeperframe.numerator *
+				(u64)USEC_PER_SEC;
+			do_div(us_per_frame, a->parm.output.
+				timeperframe.denominator);
+			break;
+		default:
+			dprintk(VIDC_ERR,
+					"Scale clocks : Unknown buffer type %d\n",
+					a->type);
+			break;
+		}
+	}
+
+	if (!us_per_frame) {
+		dprintk(VIDC_ERR,
+				"Failed to scale clocks : time between frames is 0\n");
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	fps = USEC_PER_SEC;
+	do_div(fps, us_per_frame);
+
+	if (fps % 15 == 14 || fps % 24 == 23)
+		fps = fps + 1;
+	else if ((fps > 1) && (fps % 24 == 1 || fps % 15 == 1))
+		fps = fps - 1;
+
+	if (inst->prop.fps != fps) {
+		dprintk(VIDC_PROF, "reported fps changed for %pK: %d->%d\n",
+				inst, inst->prop.fps, fps);
+		inst->prop.fps = fps;
+		frame_rate.frame_rate = inst->prop.fps * BIT(16);
+		frame_rate.buffer_type = HAL_BUFFER_OUTPUT;
+		pdata = &frame_rate;
+		if (inst->session_type == MSM_VIDC_ENCODER) {
+			rc = call_hfi_op(hdev, session_set_property,
+				inst->session, property_id, pdata);
+
+			if (rc)
+				dprintk(VIDC_WARN,
+					"Failed to set frame rate %d\n", rc);
+		} else {
+			msm_dcvs_init_load(inst);
+		}
+		msm_comm_scale_clocks_and_bus(inst);
+		msm_dcvs_try_enable(inst);
+	}
+exit:
+	return rc;
+}
+
+void msm_comm_print_inst_info(struct msm_vidc_inst *inst)
+{
+	struct buffer_info *temp;
+	struct internal_buf *buf;
+	int i = 0;
+	bool is_decode = false;
+	enum vidc_ports port;
+	bool is_secure = false;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s - invalid param %pK\n",
+			__func__, inst);
+		return;
+	}
+
+	is_decode = inst->session_type == MSM_VIDC_DECODER;
+	port = is_decode ? OUTPUT_PORT : CAPTURE_PORT;
+	is_secure = inst->flags & VIDC_SECURE;
+	dprintk(VIDC_ERR,
+			"%s session, %s, Codec type: %s HxW: %d x %d fps: %d bitrate: %d bit-depth: %s\n",
+			is_decode ? "Decode" : "Encode",
+			is_secure ? "Secure" : "Non-Secure",
+			inst->fmts[port].name,
+			inst->prop.height[port], inst->prop.width[port],
+			inst->prop.fps, inst->prop.bitrate,
+			!inst->bit_depth ? "8" : "10");
+
+	dprintk(VIDC_ERR,
+			"---Buffer details for inst: %pK of type: %d---\n",
+			inst, inst->session_type);
+	mutex_lock(&inst->registeredbufs.lock);
+	dprintk(VIDC_ERR, "registered buffer list:\n");
+	list_for_each_entry(temp, &inst->registeredbufs.list, list)
+		for (i = 0; i < temp->num_planes; i++)
+			dprintk(VIDC_ERR,
+					"type: %d plane: %d addr: %pa size: %d\n",
+					temp->type, i, &temp->device_addr[i],
+					temp->size[i]);
+
+	mutex_unlock(&inst->registeredbufs.lock);
+
+	mutex_lock(&inst->scratchbufs.lock);
+	dprintk(VIDC_ERR, "scratch buffer list:\n");
+	list_for_each_entry(buf, &inst->scratchbufs.list, list)
+		dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
+				buf->buffer_type, &buf->handle->device_addr,
+				buf->handle->size);
+	mutex_unlock(&inst->scratchbufs.lock);
+
+	mutex_lock(&inst->persistbufs.lock);
+	dprintk(VIDC_ERR, "persist buffer list:\n");
+	list_for_each_entry(buf, &inst->persistbufs.list, list)
+		dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
+				buf->buffer_type, &buf->handle->device_addr,
+				buf->handle->size);
+	mutex_unlock(&inst->persistbufs.lock);
+
+	mutex_lock(&inst->outputbufs.lock);
+	dprintk(VIDC_ERR, "dpb buffer list:\n");
+	list_for_each_entry(buf, &inst->outputbufs.list, list)
+		dprintk(VIDC_ERR, "type: %d addr: %pa size: %zu\n",
+				buf->buffer_type, &buf->handle->device_addr,
+				buf->handle->size);
+	mutex_unlock(&inst->outputbufs.lock);
+}
+
+static void msm_comm_print_debug_info(struct msm_vidc_inst *inst)
+{
+	struct msm_vidc_core *core = NULL;
+	struct msm_vidc_inst *temp = NULL;
+
+	if (!inst || !inst->core) {
+		dprintk(VIDC_ERR, "%s - invalid param %pK %pK\n",
+				__func__, inst, core);
+		return;
+	}
+	core = inst->core;
+
+	dprintk(VIDC_ERR, "Venus core frequency = %lu",
+		msm_comm_get_clock_rate(core));
+	mutex_lock(&core->lock);
+	dprintk(VIDC_ERR, "Printing instance info that caused Error\n");
+	msm_comm_print_inst_info(inst);
+	dprintk(VIDC_ERR, "Printing remaining instances info\n");
+	list_for_each_entry(temp, &core->instances, list) {
+		/* inst already printed above. Hence don't repeat.*/
+		if (temp == inst)
+			continue;
+		msm_comm_print_inst_info(temp);
+	}
+	mutex_unlock(&core->lock);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_common.h b/drivers/media/platform/msm/vidc/msm_vidc_common.h
new file mode 100644
index 0000000..c042fe9
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_common.h
@@ -0,0 +1,100 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VIDC_COMMON_H_
+#define _MSM_VIDC_COMMON_H_
+#include "msm_vidc_internal.h"
+struct vb2_buf_entry {
+	struct list_head list;
+	struct vb2_buffer *vb;
+};
+
+extern const char *const mpeg_video_vidc_extradata[];
+
+enum load_calc_quirks {
+	LOAD_CALC_NO_QUIRKS = 0,
+	LOAD_CALC_IGNORE_TURBO_LOAD = 1 << 0,
+	LOAD_CALC_IGNORE_THUMBNAIL_LOAD = 1 << 1,
+	LOAD_CALC_IGNORE_NON_REALTIME_LOAD = 1 << 2,
+};
+
+struct msm_vidc_core *get_vidc_core(int core_id);
+const struct msm_vidc_format *msm_comm_get_pixel_fmt_index(
+	const struct msm_vidc_format fmt[], int size, int index, int fmt_type);
+struct msm_vidc_format *msm_comm_get_pixel_fmt_fourcc(
+	struct msm_vidc_format fmt[], int size, int fourcc, int fmt_type);
+struct buf_queue *msm_comm_get_vb2q(
+		struct msm_vidc_inst *inst, enum v4l2_buf_type type);
+int msm_comm_try_state(struct msm_vidc_inst *inst, int state);
+int msm_comm_try_get_bufreqs(struct msm_vidc_inst *inst);
+int msm_comm_try_set_prop(struct msm_vidc_inst *inst,
+	enum hal_property ptype, void *pdata);
+int msm_comm_try_get_prop(struct msm_vidc_inst *inst,
+	enum hal_property ptype, union hal_get_property *hprop);
+int msm_comm_set_scratch_buffers(struct msm_vidc_inst *inst);
+int msm_comm_set_persist_buffers(struct msm_vidc_inst *inst);
+int msm_comm_set_output_buffers(struct msm_vidc_inst *inst);
+int msm_comm_queue_output_buffers(struct msm_vidc_inst *inst);
+int msm_comm_qbuf(struct msm_vidc_inst *inst, struct vb2_buffer *vb);
+void msm_comm_scale_clocks_and_bus(struct msm_vidc_inst *inst);
+int msm_comm_scale_clocks(struct msm_vidc_core *core);
+int msm_comm_scale_clocks_load(struct msm_vidc_core *core,
+		int num_mbs_per_sec, enum load_calc_quirks quirks);
+void msm_comm_flush_dynamic_buffers(struct msm_vidc_inst *inst);
+int msm_comm_flush(struct msm_vidc_inst *inst, u32 flags);
+int msm_comm_release_scratch_buffers(struct msm_vidc_inst *inst,
+					bool check_for_reuse);
+int msm_comm_release_persist_buffers(struct msm_vidc_inst *inst);
+int msm_comm_release_output_buffers(struct msm_vidc_inst *inst);
+int msm_comm_force_cleanup(struct msm_vidc_inst *inst);
+int msm_comm_suspend(int core_id);
+enum hal_extradata_id msm_comm_get_hal_extradata_index(
+	enum v4l2_mpeg_vidc_extradata index);
+struct hal_buffer_requirements *get_buff_req_buffer(
+			struct msm_vidc_inst *inst, u32 buffer_type);
+#define IS_PRIV_CTRL(idx) (\
+		(V4L2_CTRL_ID2CLASS(idx) == V4L2_CTRL_CLASS_MPEG) && \
+		V4L2_CTRL_DRIVER_PRIV(idx))
+void msm_comm_session_clean(struct msm_vidc_inst *inst);
+int msm_comm_kill_session(struct msm_vidc_inst *inst);
+enum multi_stream msm_comm_get_stream_output_mode(struct msm_vidc_inst *inst);
+enum hal_buffer msm_comm_get_hal_output_buffer(struct msm_vidc_inst *inst);
+struct msm_smem *msm_comm_smem_alloc(struct msm_vidc_inst *inst,
+			size_t size, u32 align, u32 flags,
+			enum hal_buffer buffer_type, int map_kernel);
+void msm_comm_smem_free(struct msm_vidc_inst *inst, struct msm_smem *mem);
+int msm_comm_smem_cache_operations(struct msm_vidc_inst *inst,
+		struct msm_smem *mem, enum smem_cache_ops cache_ops);
+struct msm_smem *msm_comm_smem_user_to_kernel(struct msm_vidc_inst *inst,
+			int fd, u32 offset, enum hal_buffer buffer_type);
+enum hal_video_codec get_hal_codec(int fourcc);
+enum hal_domain get_hal_domain(int session_type);
+int msm_comm_check_core_init(struct msm_vidc_core *core);
+int msm_comm_get_inst_load(struct msm_vidc_inst *inst,
+			enum load_calc_quirks quirks);
+int msm_comm_get_load(struct msm_vidc_core *core,
+			enum session_type type, enum load_calc_quirks quirks);
+int msm_comm_set_color_format(struct msm_vidc_inst *inst,
+		enum hal_buffer buffer_type, int fourcc);
+int msm_comm_g_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl);
+int msm_comm_s_ctrl(struct msm_vidc_inst *inst, struct v4l2_control *ctrl);
+int msm_comm_g_ctrl_for_id(struct msm_vidc_inst *inst, int id);
+int msm_comm_ctrl_init(struct msm_vidc_inst *inst,
+		struct msm_vidc_ctrl *drv_ctrls, u32 num_ctrls,
+		const struct v4l2_ctrl_ops *ctrl_ops);
+int msm_comm_ctrl_deinit(struct msm_vidc_inst *inst);
+void msm_comm_cleanup_internal_buffers(struct msm_vidc_inst *inst);
+int msm_vidc_comm_s_parm(struct msm_vidc_inst *inst, struct v4l2_streamparm *a);
+bool msm_comm_turbo_session(struct msm_vidc_inst *inst);
+void msm_comm_print_inst_info(struct msm_vidc_inst *inst);
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.c b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
new file mode 100644
index 0000000..a0e0a86
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.c
@@ -0,0 +1,423 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define CREATE_TRACE_POINTS
+#include "msm_vidc_debug.h"
+#include "vidc_hfi_api.h"
+
+int msm_vidc_debug = VIDC_ERR | VIDC_WARN;
+EXPORT_SYMBOL(msm_vidc_debug);
+
+int msm_vidc_debug_out = VIDC_OUT_PRINTK;
+EXPORT_SYMBOL(msm_vidc_debug_out);
+
+int msm_vidc_fw_debug = 0x18;
+int msm_vidc_fw_debug_mode = 1;
+int msm_vidc_fw_low_power_mode = 1;
+int msm_vidc_hw_rsp_timeout = 2000;
+bool msm_vidc_fw_coverage = !true;
+bool msm_vidc_dec_dcvs_mode = true;
+bool msm_vidc_enc_dcvs_mode = true;
+bool msm_vidc_sys_idle_indicator = !true;
+int msm_vidc_firmware_unload_delay = 15000;
+bool msm_vidc_thermal_mitigation_disabled = !true;
+bool msm_vidc_bitrate_clock_scaling = true;
+bool msm_vidc_debug_timeout = !true;
+
+#define MAX_DBG_BUF_SIZE 4096
+
+struct debug_buffer {
+	char ptr[MAX_DBG_BUF_SIZE];
+	char *curr;
+	u32 filled_size;
+};
+
+static struct debug_buffer dbg_buf;
+
+#define INIT_DBG_BUF(__buf) ({ \
+	__buf.curr = __buf.ptr;\
+	__buf.filled_size = 0; \
+})
+
+#define DYNAMIC_BUF_OWNER(__binfo) ({ \
+	atomic_read(&__binfo->ref_count) == 2 ? "video driver" : "firmware";\
+})
+
+static int core_info_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static u32 write_str(struct debug_buffer *buffer, const char *fmt, ...)
+{
+	va_list args;
+	u32 size;
+
+	va_start(args, fmt);
+	size = vscnprintf(buffer->curr, MAX_DBG_BUF_SIZE - 1, fmt, args);
+	va_end(args);
+	buffer->curr += size;
+	buffer->filled_size += size;
+	return size;
+}
+
+static ssize_t core_info_read(struct file *file, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct msm_vidc_core *core = file->private_data;
+	struct hfi_device *hdev;
+	struct hal_fw_info fw_info = { {0} };
+	int i = 0, rc = 0;
+
+	if (!core || !core->device) {
+		dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core);
+		return 0;
+	}
+	hdev = core->device;
+	INIT_DBG_BUF(dbg_buf);
+	write_str(&dbg_buf, "===============================\n");
+	write_str(&dbg_buf, "CORE %d: %pK\n", core->id, core);
+	write_str(&dbg_buf, "===============================\n");
+	write_str(&dbg_buf, "Core state: %d\n", core->state);
+	rc = call_hfi_op(hdev, get_fw_info, hdev->hfi_device_data, &fw_info);
+	if (rc) {
+		dprintk(VIDC_WARN, "Failed to read FW info\n");
+		goto err_fw_info;
+	}
+
+	write_str(&dbg_buf, "FW version : %s\n", &fw_info.version);
+	write_str(&dbg_buf, "base addr: 0x%x\n", fw_info.base_addr);
+	write_str(&dbg_buf, "register_base: 0x%x\n", fw_info.register_base);
+	write_str(&dbg_buf, "register_size: %u\n", fw_info.register_size);
+	write_str(&dbg_buf, "irq: %u\n", fw_info.irq);
+
+err_fw_info:
+	for (i = SYS_MSG_START; i < SYS_MSG_END; i++) {
+		write_str(&dbg_buf, "completions[%d]: %s\n", i,
+			completion_done(&core->completions[SYS_MSG_INDEX(i)]) ?
+			"pending" : "done");
+	}
+	return simple_read_from_buffer(buf, count, ppos,
+			dbg_buf.ptr, dbg_buf.filled_size);
+}
+
+static const struct file_operations core_info_fops = {
+	.open = core_info_open,
+	.read = core_info_read,
+};
+
+static int trigger_ssr_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t trigger_ssr_write(struct file *filp, const char __user *buf,
+		size_t count, loff_t *ppos) {
+	u32 ssr_trigger_val;
+	int rc;
+	struct msm_vidc_core *core = filp->private_data;
+
+	if (!buf)
+		return -EINVAL;
+
+	rc = kstrtou32(buf, 0, &ssr_trigger_val);
+	if (rc < 0) {
+		dprintk(VIDC_WARN, "returning error err %d\n", rc);
+		rc = -EINVAL;
+	} else {
+		msm_vidc_trigger_ssr(core, ssr_trigger_val);
+		rc = count;
+	}
+	return rc;
+}
+
+static const struct file_operations ssr_fops = {
+	.open = trigger_ssr_open,
+	.write = trigger_ssr_write,
+};
+
+struct dentry *msm_vidc_debugfs_init_drv(void)
+{
+	bool ok = false;
+	struct dentry *dir = debugfs_create_dir("msm_vidc", NULL);
+
+	if (IS_ERR_OR_NULL(dir)) {
+		dir = NULL;
+		goto failed_create_dir;
+	}
+
+#define __debugfs_create(__type, __name, __value) ({                          \
+	struct dentry *f = debugfs_create_##__type(__name, 0644,	\
+		dir, __value);                                                \
+	if (IS_ERR_OR_NULL(f)) {                                              \
+		dprintk(VIDC_ERR, "Failed creating debugfs file '%pd/%s'\n",  \
+			dir, __name);                                         \
+		f = NULL;                                                     \
+	}                                                                     \
+	f;                                                                    \
+})
+
+	ok =
+	__debugfs_create(x32, "debug_level", &msm_vidc_debug) &&
+	__debugfs_create(x32, "fw_level", &msm_vidc_fw_debug) &&
+	__debugfs_create(u32, "fw_debug_mode", &msm_vidc_fw_debug_mode) &&
+	__debugfs_create(bool, "fw_coverage", &msm_vidc_fw_coverage) &&
+	__debugfs_create(bool, "dcvs_dec_mode", &msm_vidc_dec_dcvs_mode) &&
+	__debugfs_create(bool, "dcvs_enc_mode", &msm_vidc_enc_dcvs_mode) &&
+	__debugfs_create(u32, "fw_low_power_mode",
+			&msm_vidc_fw_low_power_mode) &&
+	__debugfs_create(u32, "debug_output", &msm_vidc_debug_out) &&
+	__debugfs_create(u32, "hw_rsp_timeout", &msm_vidc_hw_rsp_timeout) &&
+	__debugfs_create(bool, "sys_idle_indicator",
+			&msm_vidc_sys_idle_indicator) &&
+	__debugfs_create(u32, "firmware_unload_delay",
+			&msm_vidc_firmware_unload_delay) &&
+	__debugfs_create(bool, "disable_thermal_mitigation",
+			&msm_vidc_thermal_mitigation_disabled) &&
+	__debugfs_create(bool, "bitrate_clock_scaling",
+			&msm_vidc_bitrate_clock_scaling) &&
+	__debugfs_create(bool, "debug_timeout",
+			&msm_vidc_debug_timeout);
+
+#undef __debugfs_create
+
+	if (!ok)
+		goto failed_create_dir;
+
+	return dir;
+
+failed_create_dir:
+	if (dir)
+		debugfs_remove_recursive(vidc_driver->debugfs_root);
+
+	return NULL;
+}
+
+struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
+		struct dentry *parent)
+{
+	struct dentry *dir = NULL;
+	char debugfs_name[MAX_DEBUGFS_NAME];
+
+	if (!core) {
+		dprintk(VIDC_ERR, "Invalid params, core: %pK\n", core);
+		goto failed_create_dir;
+	}
+
+	snprintf(debugfs_name, MAX_DEBUGFS_NAME, "core%d", core->id);
+	dir = debugfs_create_dir(debugfs_name, parent);
+	if (!dir) {
+		dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n");
+		goto failed_create_dir;
+	}
+	if (!debugfs_create_file("info", 0444, dir, core, &core_info_fops)) {
+		dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+		goto failed_create_dir;
+	}
+	if (!debugfs_create_file("trigger_ssr", 0200,
+			dir, core, &ssr_fops)) {
+		dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+		goto failed_create_dir;
+	}
+failed_create_dir:
+	return dir;
+}
+
+static int inst_info_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int publish_unreleased_reference(struct msm_vidc_inst *inst)
+{
+	struct buffer_info *temp = NULL;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "%s: invalid param\n", __func__);
+		return -EINVAL;
+	}
+
+	if (inst->buffer_mode_set[CAPTURE_PORT] == HAL_BUFFER_MODE_DYNAMIC) {
+		write_str(&dbg_buf, "Pending buffer references:\n");
+
+		mutex_lock(&inst->registeredbufs.lock);
+		list_for_each_entry(temp, &inst->registeredbufs.list, list) {
+			if (temp->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE &&
+			!temp->inactive && atomic_read(&temp->ref_count)) {
+				write_str(&dbg_buf,
+				"\tpending buffer: %#lx fd[0] = %d ref_count = %d held by: %s\n",
+				temp->device_addr[0],
+				temp->fd[0],
+				atomic_read(&temp->ref_count),
+				DYNAMIC_BUF_OWNER(temp));
+			}
+		}
+		mutex_unlock(&inst->registeredbufs.lock);
+	}
+	return 0;
+}
+
+static ssize_t inst_info_read(struct file *file, char __user *buf,
+		size_t count, loff_t *ppos)
+{
+	struct msm_vidc_inst *inst = file->private_data;
+	int i, j;
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid params, inst %pK\n", inst);
+		return 0;
+	}
+	INIT_DBG_BUF(dbg_buf);
+	write_str(&dbg_buf, "===============================\n");
+	write_str(&dbg_buf, "INSTANCE: %pK (%s)\n", inst,
+		inst->session_type == MSM_VIDC_ENCODER ? "Encoder" : "Decoder");
+	write_str(&dbg_buf, "===============================\n");
+	write_str(&dbg_buf, "core: %pK\n", inst->core);
+	write_str(&dbg_buf, "height: %d\n", inst->prop.height[CAPTURE_PORT]);
+	write_str(&dbg_buf, "width: %d\n", inst->prop.width[CAPTURE_PORT]);
+	write_str(&dbg_buf, "fps: %d\n", inst->prop.fps);
+	write_str(&dbg_buf, "state: %d\n", inst->state);
+	write_str(&dbg_buf, "secure: %d\n", !!(inst->flags & VIDC_SECURE));
+	write_str(&dbg_buf, "-----------Formats-------------\n");
+	for (i = 0; i < MAX_PORT_NUM; i++) {
+		write_str(&dbg_buf, "capability: %s\n", i == OUTPUT_PORT ?
+			"Output" : "Capture");
+		write_str(&dbg_buf, "name : %s\n", inst->fmts[i].name);
+		write_str(&dbg_buf, "planes : %d\n", inst->fmts[i].num_planes);
+		write_str(
+		&dbg_buf, "type: %s\n", inst->fmts[i].type == OUTPUT_PORT ?
+		"Output" : "Capture");
+		switch (inst->buffer_mode_set[i]) {
+		case HAL_BUFFER_MODE_STATIC:
+			write_str(&dbg_buf, "buffer mode : %s\n", "static");
+			break;
+		case HAL_BUFFER_MODE_RING:
+			write_str(&dbg_buf, "buffer mode : %s\n", "ring");
+			break;
+		case HAL_BUFFER_MODE_DYNAMIC:
+			write_str(&dbg_buf, "buffer mode : %s\n", "dynamic");
+			break;
+		default:
+			write_str(&dbg_buf, "buffer mode : unsupported\n");
+		}
+
+		write_str(&dbg_buf, "count: %u\n",
+				inst->bufq[i].vb2_bufq.num_buffers);
+
+		for (j = 0; j < inst->fmts[i].num_planes; j++)
+			write_str(&dbg_buf, "size for plane %d: %u\n", j,
+			inst->bufq[i].plane_sizes[j]);
+
+		if (i < MAX_PORT_NUM - 1)
+			write_str(&dbg_buf, "\n");
+	}
+	write_str(&dbg_buf, "-------------------------------\n");
+	for (i = SESSION_MSG_START; i < SESSION_MSG_END; i++) {
+		write_str(&dbg_buf, "completions[%d]: %s\n", i,
+		completion_done(&inst->completions[SESSION_MSG_INDEX(i)]) ?
+		"pending" : "done");
+	}
+	write_str(&dbg_buf, "ETB Count: %d\n", inst->count.etb);
+	write_str(&dbg_buf, "EBD Count: %d\n", inst->count.ebd);
+	write_str(&dbg_buf, "FTB Count: %d\n", inst->count.ftb);
+	write_str(&dbg_buf, "FBD Count: %d\n", inst->count.fbd);
+
+	publish_unreleased_reference(inst);
+
+	return simple_read_from_buffer(buf, count, ppos,
+		dbg_buf.ptr, dbg_buf.filled_size);
+}
+
+static const struct file_operations inst_info_fops = {
+	.open = inst_info_open,
+	.read = inst_info_read,
+};
+
+struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
+		struct dentry *parent)
+{
+	struct dentry *dir = NULL;
+	char debugfs_name[MAX_DEBUGFS_NAME];
+
+	if (!inst) {
+		dprintk(VIDC_ERR, "Invalid params, inst: %pK\n", inst);
+		goto failed_create_dir;
+	}
+	snprintf(debugfs_name, MAX_DEBUGFS_NAME, "inst_%p", inst);
+	dir = debugfs_create_dir(debugfs_name, parent);
+	if (!dir) {
+		dprintk(VIDC_ERR, "Failed to create debugfs for msm_vidc\n");
+		goto failed_create_dir;
+	}
+	if (!debugfs_create_file("info", 0444, dir, inst, &inst_info_fops)) {
+		dprintk(VIDC_ERR, "debugfs_create_file: fail\n");
+		goto failed_create_dir;
+	}
+	inst->debug.pdata[FRAME_PROCESSING].sampling = true;
+failed_create_dir:
+	return dir;
+}
+
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+	enum msm_vidc_debugfs_event e)
+{
+	struct msm_vidc_debug *d = &inst->debug;
+	char a[64] = "Frame processing";
+
+	switch (e) {
+	case MSM_VIDC_DEBUGFS_EVENT_ETB:
+		mutex_lock(&inst->lock);
+		inst->count.etb++;
+		mutex_unlock(&inst->lock);
+		if (inst->count.ebd && inst->count.ftb > inst->count.fbd) {
+			d->pdata[FRAME_PROCESSING].name[0] = '\0';
+			tic(inst, FRAME_PROCESSING, a);
+		}
+	break;
+	case MSM_VIDC_DEBUGFS_EVENT_EBD:
+		mutex_lock(&inst->lock);
+		inst->count.ebd++;
+		mutex_unlock(&inst->lock);
+		if (inst->count.ebd && inst->count.ebd == inst->count.etb) {
+			toc(inst, FRAME_PROCESSING);
+			dprintk(VIDC_PROF, "EBD: FW needs input buffers\n");
+		}
+		if (inst->count.ftb == inst->count.fbd)
+			dprintk(VIDC_PROF, "EBD: FW needs output buffers\n");
+	break;
+	case MSM_VIDC_DEBUGFS_EVENT_FTB: {
+		inst->count.ftb++;
+		if (inst->count.ebd && inst->count.etb > inst->count.ebd) {
+			d->pdata[FRAME_PROCESSING].name[0] = '\0';
+			tic(inst, FRAME_PROCESSING, a);
+		}
+	}
+	break;
+	case MSM_VIDC_DEBUGFS_EVENT_FBD:
+		inst->debug.samples++;
+		if (inst->count.ebd && inst->count.fbd == inst->count.ftb) {
+			toc(inst, FRAME_PROCESSING);
+			dprintk(VIDC_PROF, "FBD: FW needs output buffers\n");
+		}
+		if (inst->count.etb == inst->count.ebd)
+			dprintk(VIDC_PROF, "FBD: FW needs input buffers\n");
+		break;
+	default:
+		dprintk(VIDC_ERR, "Invalid state in debugfs: %d\n", e);
+		break;
+	}
+}
+
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_debug.h b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
new file mode 100644
index 0000000..0af0220
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_debug.h
@@ -0,0 +1,199 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VIDC_DEBUG__
+#define __MSM_VIDC_DEBUG__
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include "msm_vidc_internal.h"
+#include "trace/events/msm_vidc.h"
+
+#ifndef VIDC_DBG_LABEL
+#define VIDC_DBG_LABEL "msm_vidc"
+#endif
+
+#define VIDC_DBG_TAG VIDC_DBG_LABEL ": %4s: "
+
+/* To enable messages OR these values and
+ * echo the result to debugfs file.
+ *
+ * To enable all messages set debug_level = 0x101F
+ */
+
+enum vidc_msg_prio {
+	VIDC_ERR  = 0x0001,
+	VIDC_WARN = 0x0002,
+	VIDC_INFO = 0x0004,
+	VIDC_DBG  = 0x0008,
+	VIDC_PROF = 0x0010,
+	VIDC_PKT  = 0x0020,
+	VIDC_FW   = 0x1000,
+};
+
+enum vidc_msg_out {
+	VIDC_OUT_PRINTK = 0,
+	VIDC_OUT_FTRACE,
+};
+
+enum msm_vidc_debugfs_event {
+	MSM_VIDC_DEBUGFS_EVENT_ETB,
+	MSM_VIDC_DEBUGFS_EVENT_EBD,
+	MSM_VIDC_DEBUGFS_EVENT_FTB,
+	MSM_VIDC_DEBUGFS_EVENT_FBD,
+};
+
+extern int msm_vidc_debug;
+extern int msm_vidc_debug_out;
+extern int msm_vidc_fw_debug;
+extern int msm_vidc_fw_debug_mode;
+extern int msm_vidc_fw_low_power_mode;
+extern int msm_vidc_hw_rsp_timeout;
+extern bool msm_vidc_fw_coverage;
+extern bool msm_vidc_dec_dcvs_mode;
+extern bool msm_vidc_enc_dcvs_mode;
+extern bool msm_vidc_sys_idle_indicator;
+extern int msm_vidc_firmware_unload_delay;
+extern bool msm_vidc_thermal_mitigation_disabled;
+extern bool msm_vidc_bitrate_clock_scaling;
+extern bool msm_vidc_debug_timeout;
+
+#define VIDC_MSG_PRIO2STRING(__level) ({ \
+	char *__str; \
+	\
+	switch (__level) { \
+	case VIDC_ERR: \
+		__str = "err"; \
+		break; \
+	case VIDC_WARN: \
+		__str = "warn"; \
+		break; \
+	case VIDC_INFO: \
+		__str = "info"; \
+		break; \
+	case VIDC_DBG: \
+		__str = "dbg"; \
+		break; \
+	case VIDC_PROF: \
+		__str = "prof"; \
+		break; \
+	case VIDC_PKT: \
+		__str = "pkt"; \
+		break; \
+	case VIDC_FW: \
+		__str = "fw"; \
+		break; \
+	default: \
+		__str = "????"; \
+		break; \
+	} \
+	\
+	__str; \
+	})
+
+#define dprintk(__level, __fmt, arg...)	\
+	do { \
+		if (msm_vidc_debug & __level) { \
+			if (msm_vidc_debug_out == VIDC_OUT_PRINTK) { \
+				pr_info(VIDC_DBG_TAG __fmt, \
+						VIDC_MSG_PRIO2STRING(__level), \
+						## arg); \
+			} else if (msm_vidc_debug_out == VIDC_OUT_FTRACE) { \
+				trace_printk(KERN_DEBUG VIDC_DBG_TAG __fmt, \
+						VIDC_MSG_PRIO2STRING(__level), \
+						## arg); \
+			} \
+		} \
+	} while (0)
+
+#define MSM_VIDC_ERROR(value)					\
+	do {							\
+		dprintk(VIDC_WARN, "Fatal Level = %d\n", value);\
+		BUG_ON(value);					\
+	} while (0)
+
+
+struct dentry *msm_vidc_debugfs_init_drv(void);
+struct dentry *msm_vidc_debugfs_init_core(struct msm_vidc_core *core,
+		struct dentry *parent);
+struct dentry *msm_vidc_debugfs_init_inst(struct msm_vidc_inst *inst,
+		struct dentry *parent);
+void msm_vidc_debugfs_update(struct msm_vidc_inst *inst,
+		enum msm_vidc_debugfs_event e);
+
+static inline void tic(struct msm_vidc_inst *i, enum profiling_points p,
+				 char *b)
+{
+	struct timeval __ddl_tv;
+
+	if (!i->debug.pdata[p].name[0])
+		memcpy(i->debug.pdata[p].name, b, 64);
+	if ((msm_vidc_debug & VIDC_PROF) &&
+		i->debug.pdata[p].sampling) {
+		do_gettimeofday(&__ddl_tv);
+		i->debug.pdata[p].start =
+			(__ddl_tv.tv_sec * 1000) + (__ddl_tv.tv_usec / 1000);
+			i->debug.pdata[p].sampling = false;
+	}
+}
+
+static inline void toc(struct msm_vidc_inst *i, enum profiling_points p)
+{
+	struct timeval __ddl_tv;
+
+	if ((msm_vidc_debug & VIDC_PROF) &&
+		!i->debug.pdata[p].sampling) {
+		do_gettimeofday(&__ddl_tv);
+		i->debug.pdata[p].stop = (__ddl_tv.tv_sec * 1000)
+			+ (__ddl_tv.tv_usec / 1000);
+		i->debug.pdata[p].cumulative += i->debug.pdata[p].stop -
+			i->debug.pdata[p].start;
+		i->debug.pdata[p].sampling = true;
+	}
+}
+
+static inline void show_stats(struct msm_vidc_inst *i)
+{
+	int x;
+
+	for (x = 0; x < MAX_PROFILING_POINTS; x++) {
+		if (i->debug.pdata[x].name[0] &&
+				(msm_vidc_debug & VIDC_PROF)) {
+			if (i->debug.samples) {
+				dprintk(VIDC_PROF, "%s averaged %d ms/sample\n",
+						i->debug.pdata[x].name,
+						i->debug.pdata[x].cumulative /
+						i->debug.samples);
+			}
+
+			dprintk(VIDC_PROF, "%s Samples: %d\n",
+					i->debug.pdata[x].name,
+					i->debug.samples);
+		}
+	}
+}
+
+static inline void msm_vidc_handle_hw_error(struct msm_vidc_core *core)
+{
+	bool enable_fatal;
+
+	enable_fatal = core->resources.debug_timeout;
+
+	/* Video driver can decide FATAL handling of HW errors
+	 * based on multiple factors. This condition check will
+	 * be enhanced later.
+	 */
+
+	MSM_VIDC_ERROR(enable_fatal);
+}
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_internal.h b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
new file mode 100644
index 0000000..c05a9cc
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_internal.h
@@ -0,0 +1,387 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VIDC_INTERNAL_H_
+#define _MSM_VIDC_INTERNAL_H_
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <linux/kref.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
+#include <media/videobuf2-core.h>
+#include <media/videobuf2-v4l2.h>
+#include <media/msm_vidc.h>
+#include <media/msm_media_info.h>
+
+#include "vidc_hfi_api.h"
+
+#define MSM_VIDC_DRV_NAME "msm_vidc_driver"
+#define MSM_VIDC_VERSION KERNEL_VERSION(0, 0, 1)
+#define MAX_DEBUGFS_NAME 50
+#define DEFAULT_TIMEOUT 3
+#define DEFAULT_HEIGHT 1088
+#define DEFAULT_WIDTH 1920
+#define MIN_SUPPORTED_WIDTH 32
+#define MIN_SUPPORTED_HEIGHT 32
+#define DEFAULT_FPS 15
+
+/* Maintains the number of FTB's between each FBD over a window */
+#define DCVS_FTB_WINDOW 32
+
+#define V4L2_EVENT_VIDC_BASE  10
+
+#define SYS_MSG_START HAL_SYS_INIT_DONE
+#define SYS_MSG_END HAL_SYS_ERROR
+#define SESSION_MSG_START HAL_SESSION_EVENT_CHANGE
+#define SESSION_MSG_END HAL_SESSION_ERROR
+#define SYS_MSG_INDEX(__msg) (__msg - SYS_MSG_START)
+#define SESSION_MSG_INDEX(__msg) (__msg - SESSION_MSG_START)
+
+
+#define MAX_NAME_LENGTH 64
+
+#define EXTRADATA_IDX(__num_planes) ((__num_planes) ? (__num_planes) - 1 : 0)
+
+#define NUM_MBS_PER_SEC(__height, __width, __fps) \
+	(NUM_MBS_PER_FRAME(__height, __width) * __fps)
+
+#define NUM_MBS_PER_FRAME(__height, __width) \
+	((ALIGN(__height, 16) / 16) * (ALIGN(__width, 16) / 16))
+
+enum vidc_ports {
+	OUTPUT_PORT,
+	CAPTURE_PORT,
+	MAX_PORT_NUM
+};
+
+enum vidc_core_state {
+	VIDC_CORE_UNINIT = 0,
+	VIDC_CORE_INIT,
+	VIDC_CORE_INIT_DONE,
+	VIDC_CORE_INVALID
+};
+
+/*
+ * Do not change the enum values unless
+ * you know what you are doing
+ */
+enum instance_state {
+	MSM_VIDC_CORE_UNINIT_DONE = 0x0001,
+	MSM_VIDC_CORE_INIT,
+	MSM_VIDC_CORE_INIT_DONE,
+	MSM_VIDC_OPEN,
+	MSM_VIDC_OPEN_DONE,
+	MSM_VIDC_LOAD_RESOURCES,
+	MSM_VIDC_LOAD_RESOURCES_DONE,
+	MSM_VIDC_START,
+	MSM_VIDC_START_DONE,
+	MSM_VIDC_STOP,
+	MSM_VIDC_STOP_DONE,
+	MSM_VIDC_RELEASE_RESOURCES,
+	MSM_VIDC_RELEASE_RESOURCES_DONE,
+	MSM_VIDC_CLOSE,
+	MSM_VIDC_CLOSE_DONE,
+	MSM_VIDC_CORE_UNINIT,
+	MSM_VIDC_CORE_INVALID
+};
+
+struct buf_info {
+	struct list_head list;
+	struct vb2_buffer *buf;
+};
+
+struct msm_vidc_list {
+	struct list_head list;
+	struct mutex lock;
+};
+
+static inline void INIT_MSM_VIDC_LIST(struct msm_vidc_list *mlist)
+{
+	mutex_init(&mlist->lock);
+	INIT_LIST_HEAD(&mlist->list);
+}
+
+static inline void DEINIT_MSM_VIDC_LIST(struct msm_vidc_list *mlist)
+{
+	mutex_destroy(&mlist->lock);
+}
+
+enum buffer_owner {
+	DRIVER,
+	FIRMWARE,
+	CLIENT,
+	MAX_OWNER
+};
+
+struct internal_buf {
+	struct list_head list;
+	enum hal_buffer buffer_type;
+	struct msm_smem *handle;
+	enum buffer_owner buffer_ownership;
+};
+
+struct msm_vidc_format {
+	char name[MAX_NAME_LENGTH];
+	u8 description[32];
+	u32 fourcc;
+	int num_planes;
+	int type;
+	u32 (*get_frame_size)(int plane, u32 height, u32 width);
+};
+
+struct msm_vidc_drv {
+	struct mutex lock;
+	struct list_head cores;
+	int num_cores;
+	struct dentry *debugfs_root;
+	int thermal_level;
+	u32 platform_version;
+};
+
+struct msm_video_device {
+	int type;
+	struct video_device vdev;
+};
+
+struct session_prop {
+	u32 width[MAX_PORT_NUM];
+	u32 height[MAX_PORT_NUM];
+	u32 fps;
+	u32 bitrate;
+};
+
+struct buf_queue {
+	struct vb2_queue vb2_bufq;
+	struct mutex lock;
+	unsigned int	plane_sizes[VB2_MAX_PLANES];
+};
+
+enum profiling_points {
+	SYS_INIT = 0,
+	SESSION_INIT,
+	LOAD_RESOURCES,
+	FRAME_PROCESSING,
+	FW_IDLE,
+	MAX_PROFILING_POINTS,
+};
+
+struct buf_count {
+	int etb;
+	int ftb;
+	int fbd;
+	int ebd;
+};
+
+struct dcvs_stats {
+	int num_ftb[DCVS_FTB_WINDOW];
+	bool transition_turbo;
+	int ftb_index;
+	int ftb_counter;
+	bool prev_freq_lowered;
+	bool prev_freq_increased;
+	int threshold_disp_buf_high;
+	int threshold_disp_buf_low;
+	int load;
+	int load_low;
+	int load_high;
+	int min_threshold;
+	int max_threshold;
+	int etb_counter;
+	bool is_power_save_mode;
+	unsigned int extra_buffer_count;
+	u32 supported_codecs;
+};
+
+struct profile_data {
+	int start;
+	int stop;
+	int cumulative;
+	char name[64];
+	int sampling;
+	int average;
+};
+
+struct msm_vidc_debug {
+	struct profile_data pdata[MAX_PROFILING_POINTS];
+	int profile;
+	int samples;
+};
+
+enum msm_vidc_modes {
+	VIDC_SECURE = BIT(0),
+	VIDC_TURBO = BIT(1),
+	VIDC_THUMBNAIL = BIT(2),
+	VIDC_LOW_POWER = BIT(3),
+	VIDC_REALTIME = BIT(4),
+};
+
+struct msm_vidc_core {
+	struct list_head list;
+	struct mutex lock;
+	int id;
+	struct hfi_device *device;
+	struct msm_video_device vdev[MSM_VIDC_MAX_DEVICES];
+	struct v4l2_device v4l2_dev;
+	struct list_head instances;
+	struct dentry *debugfs_root;
+	enum vidc_core_state state;
+	struct completion completions[SYS_MSG_END - SYS_MSG_START + 1];
+	enum msm_vidc_hfi_type hfi_type;
+	struct msm_vidc_platform_resources resources;
+	u32 enc_codec_supported;
+	u32 dec_codec_supported;
+	u32 codec_count;
+	struct msm_vidc_capability *capabilities;
+	struct delayed_work fw_unload_work;
+	bool smmu_fault_handled;
+};
+
+struct msm_vidc_inst {
+	struct list_head list;
+	struct mutex sync_lock, lock;
+	struct msm_vidc_core *core;
+	enum session_type session_type;
+	void *session;
+	struct session_prop prop;
+	enum instance_state state;
+	struct msm_vidc_format fmts[MAX_PORT_NUM];
+	struct buf_queue bufq[MAX_PORT_NUM];
+	struct msm_vidc_list pendingq;
+	struct msm_vidc_list scratchbufs;
+	struct msm_vidc_list persistbufs;
+	struct msm_vidc_list pending_getpropq;
+	struct msm_vidc_list outputbufs;
+	struct msm_vidc_list registeredbufs;
+	struct buffer_requirements buff_req;
+	void *mem_client;
+	struct v4l2_ctrl_handler ctrl_handler;
+	struct completion completions[SESSION_MSG_END - SESSION_MSG_START + 1];
+	struct v4l2_ctrl **cluster;
+	struct v4l2_fh event_handler;
+	struct msm_smem *extradata_handle;
+	bool in_reconfig;
+	u32 reconfig_width;
+	u32 reconfig_height;
+	struct dentry *debugfs_root;
+	void *priv;
+	struct msm_vidc_debug debug;
+	struct buf_count count;
+	struct dcvs_stats dcvs;
+	enum msm_vidc_modes flags;
+	struct msm_vidc_capability capability;
+	u32 buffer_size_limit;
+	enum buffer_mode_type buffer_mode_set[MAX_PORT_NUM];
+	atomic_t seq_hdr_reqs;
+	struct v4l2_ctrl **ctrls;
+	bool dcvs_mode;
+	enum msm_vidc_pixel_depth bit_depth;
+	struct kref kref;
+	unsigned long instant_bitrate;
+	u32 buffers_held_in_driver;
+	atomic_t in_flush;
+	u32 pic_struct;
+	u32 colour_space;
+	u32 operating_rate;
+};
+
+extern struct msm_vidc_drv *vidc_driver;
+
+struct msm_vidc_ctrl_cluster {
+	struct v4l2_ctrl **cluster;
+	struct list_head list;
+};
+
+struct msm_vidc_ctrl {
+	u32 id;
+	char name[MAX_NAME_LENGTH];
+	enum v4l2_ctrl_type type;
+	s32 minimum;
+	s32 maximum;
+	s32 default_value;
+	u32 step;
+	u32 menu_skip_mask;
+	u32 flags;
+	const char * const *qmenu;
+};
+
+void handle_cmd_response(enum hal_command_response cmd, void *data);
+int msm_vidc_trigger_ssr(struct msm_vidc_core *core,
+	enum hal_ssr_trigger_type type);
+int msm_vidc_check_session_supported(struct msm_vidc_inst *inst);
+int msm_vidc_check_scaling_supported(struct msm_vidc_inst *inst);
+void msm_vidc_queue_v4l2_event(struct msm_vidc_inst *inst, int event_type);
+
+struct buffer_info {
+	struct list_head list;
+	int type;
+	int num_planes;
+	int fd[VIDEO_MAX_PLANES];
+	int buff_off[VIDEO_MAX_PLANES];
+	int size[VIDEO_MAX_PLANES];
+	unsigned long uvaddr[VIDEO_MAX_PLANES];
+	ion_phys_addr_t device_addr[VIDEO_MAX_PLANES];
+	struct msm_smem *handle[VIDEO_MAX_PLANES];
+	enum v4l2_memory memory;
+	u32 v4l2_index;
+	bool pending_deletion;
+	atomic_t ref_count;
+	bool dequeued;
+	bool inactive;
+	bool mapped[VIDEO_MAX_PLANES];
+	int same_fd_ref[VIDEO_MAX_PLANES];
+	struct timeval timestamp;
+};
+
+struct buffer_info *device_to_uvaddr(struct msm_vidc_list *buf_list,
+				ion_phys_addr_t device_addr);
+int buf_ref_get(struct msm_vidc_inst *inst, struct buffer_info *binfo);
+int buf_ref_put(struct msm_vidc_inst *inst, struct buffer_info *binfo);
+int output_buffer_cache_invalidate(struct msm_vidc_inst *inst,
+				struct buffer_info *binfo);
+int qbuf_dynamic_buf(struct msm_vidc_inst *inst,
+			struct buffer_info *binfo);
+int unmap_and_deregister_buf(struct msm_vidc_inst *inst,
+			struct buffer_info *binfo);
+
+void msm_comm_handle_thermal_event(void);
+void *msm_smem_new_client(enum smem_type mtype,
+		void *platform_resources, enum session_type stype);
+struct msm_smem *msm_smem_alloc(void *clt, size_t size, u32 align, u32 flags,
+		enum hal_buffer buffer_type, int map_kernel);
+void msm_smem_free(void *clt, struct msm_smem *mem);
+void msm_smem_delete_client(void *clt);
+int msm_smem_cache_operations(void *clt, struct msm_smem *mem,
+		enum smem_cache_ops);
+struct msm_smem *msm_smem_user_to_kernel(void *clt, int fd, u32 offset,
+				enum hal_buffer buffer_type);
+struct context_bank_info *msm_smem_get_context_bank(void *clt,
+		bool is_secure, enum hal_buffer buffer_type);
+void msm_vidc_fw_unload_handler(struct work_struct *work);
+bool msm_smem_compare_buffers(void *clt, int fd, void *priv);
+/*
+ * XXX: normally should be in msm_vidc.h, but that's meant for public APIs,
+ * whereas this is private
+ */
+int msm_vidc_destroy(struct msm_vidc_inst *inst);
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
new file mode 100644
index 0000000..97a625b
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.c
@@ -0,0 +1,1511 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/dma-iommu.h>
+#include <linux/iommu.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include "msm_vidc_debug.h"
+#include "msm_vidc_resources.h"
+#include "msm_vidc_res_parse.h"
+#include "venus_boot.h"
+#include "soc/qcom/secure_buffer.h"
+
+enum clock_properties {
+	CLOCK_PROP_HAS_SCALING = 1 << 0,
+	CLOCK_PROP_HAS_MEM_RETENTION    = 1 << 1,
+};
+
+static inline struct device *msm_iommu_get_ctx(const char *ctx_name)
+{
+	return NULL;
+}
+
+static int msm_vidc_populate_legacy_context_bank(
+			struct msm_vidc_platform_resources *res);
+
+static size_t get_u32_array_num_elements(struct device_node *np,
+					char *name)
+{
+	int len;
+	size_t num_elements = 0;
+
+	if (!of_get_property(np, name, &len)) {
+		dprintk(VIDC_ERR, "Failed to read %s from device tree\n",
+			name);
+		goto fail_read;
+	}
+
+	num_elements = len / sizeof(u32);
+	if (num_elements <= 0) {
+		dprintk(VIDC_ERR, "%s not specified in device tree\n",
+			name);
+		goto fail_read;
+	}
+	return num_elements;
+
+fail_read:
+	return 0;
+}
+
+static inline enum imem_type read_imem_type(struct platform_device *pdev)
+{
+	bool is_compatible(char *compat)
+	{
+		return !!of_find_compatible_node(NULL, NULL, compat);
+	}
+
+	return is_compatible("qcom,msm-ocmem") ? IMEM_OCMEM :
+		is_compatible("qcom,msm-vmem") ? IMEM_VMEM :
+						IMEM_NONE;
+
+}
+
+static inline void msm_vidc_free_allowed_clocks_table(
+		struct msm_vidc_platform_resources *res)
+{
+	res->allowed_clks_tbl = NULL;
+}
+
+static inline void msm_vidc_free_cycles_per_mb_table(
+		struct msm_vidc_platform_resources *res)
+{
+	res->clock_freq_tbl.clk_prof_entries = NULL;
+}
+
+static inline void msm_vidc_free_platform_version_table(
+		struct msm_vidc_platform_resources *res)
+{
+	res->pf_ver_tbl = NULL;
+}
+
+static inline void msm_vidc_free_freq_table(
+		struct msm_vidc_platform_resources *res)
+{
+	res->load_freq_tbl = NULL;
+}
+
+static inline void msm_vidc_free_dcvs_table(
+		struct msm_vidc_platform_resources *res)
+{
+	res->dcvs_tbl = NULL;
+}
+
+static inline void msm_vidc_free_dcvs_limit(
+		struct msm_vidc_platform_resources *res)
+{
+	res->dcvs_limit = NULL;
+}
+
+static inline void msm_vidc_free_imem_ab_table(
+		struct msm_vidc_platform_resources *res)
+{
+	res->imem_ab_tbl = NULL;
+}
+
+static inline void msm_vidc_free_reg_table(
+			struct msm_vidc_platform_resources *res)
+{
+	res->reg_set.reg_tbl = NULL;
+}
+
+static inline void msm_vidc_free_qdss_addr_table(
+			struct msm_vidc_platform_resources *res)
+{
+	res->qdss_addr_set.addr_tbl = NULL;
+}
+
+static inline void msm_vidc_free_bus_vectors(
+			struct msm_vidc_platform_resources *res)
+{
+	kfree(res->bus_set.bus_tbl);
+	res->bus_set.bus_tbl = NULL;
+	res->bus_set.count = 0;
+}
+
+static inline void msm_vidc_free_buffer_usage_table(
+			struct msm_vidc_platform_resources *res)
+{
+	res->buffer_usage_set.buffer_usage_tbl = NULL;
+}
+
+static inline void msm_vidc_free_regulator_table(
+			struct msm_vidc_platform_resources *res)
+{
+	int c = 0;
+
+	for (c = 0; c < res->regulator_set.count; ++c) {
+		struct regulator_info *rinfo =
+			&res->regulator_set.regulator_tbl[c];
+
+		rinfo->name = NULL;
+	}
+
+	res->regulator_set.regulator_tbl = NULL;
+	res->regulator_set.count = 0;
+}
+
+static inline void msm_vidc_free_clock_table(
+			struct msm_vidc_platform_resources *res)
+{
+	res->clock_set.clock_tbl = NULL;
+	res->clock_set.count = 0;
+}
+
+void msm_vidc_free_platform_resources(
+			struct msm_vidc_platform_resources *res)
+{
+	msm_vidc_free_clock_table(res);
+	msm_vidc_free_regulator_table(res);
+	msm_vidc_free_freq_table(res);
+	msm_vidc_free_platform_version_table(res);
+	msm_vidc_free_dcvs_table(res);
+	msm_vidc_free_dcvs_limit(res);
+	msm_vidc_free_cycles_per_mb_table(res);
+	msm_vidc_free_allowed_clocks_table(res);
+	msm_vidc_free_reg_table(res);
+	msm_vidc_free_qdss_addr_table(res);
+	msm_vidc_free_bus_vectors(res);
+	msm_vidc_free_buffer_usage_table(res);
+}
+
+static int msm_vidc_load_reg_table(struct msm_vidc_platform_resources *res)
+{
+	struct reg_set *reg_set;
+	struct platform_device *pdev = res->pdev;
+	int i;
+	int rc = 0;
+
+	if (!of_find_property(pdev->dev.of_node, "qcom,reg-presets", NULL)) {
+		/*
+		 * qcom,reg-presets is an optional property.  It likely won't be
+		 * present if we don't have any register settings to program
+		 */
+		dprintk(VIDC_DBG, "qcom,reg-presets not found\n");
+		return 0;
+	}
+
+	reg_set = &res->reg_set;
+	reg_set->count = get_u32_array_num_elements(pdev->dev.of_node,
+			"qcom,reg-presets");
+	reg_set->count /=  sizeof(*reg_set->reg_tbl) / sizeof(u32);
+
+	if (!reg_set->count) {
+		dprintk(VIDC_DBG, "no elements in reg set\n");
+		return rc;
+	}
+
+	reg_set->reg_tbl = devm_kzalloc(&pdev->dev, reg_set->count *
+			sizeof(*(reg_set->reg_tbl)), GFP_KERNEL);
+	if (!reg_set->reg_tbl) {
+		dprintk(VIDC_ERR, "%s Failed to alloc register table\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	if (of_property_read_u32_array(pdev->dev.of_node, "qcom,reg-presets",
+		(u32 *)reg_set->reg_tbl, reg_set->count * 2)) {
+		dprintk(VIDC_ERR, "Failed to read register table\n");
+		msm_vidc_free_reg_table(res);
+		return -EINVAL;
+	}
+	for (i = 0; i < reg_set->count; i++) {
+		dprintk(VIDC_DBG,
+			"reg = %x, value = %x\n",
+			reg_set->reg_tbl[i].reg,
+			reg_set->reg_tbl[i].value
+		);
+	}
+	return rc;
+}
+static int msm_vidc_load_qdss_table(struct msm_vidc_platform_resources *res)
+{
+	struct addr_set *qdss_addr_set;
+	struct platform_device *pdev = res->pdev;
+	int i;
+	int rc = 0;
+
+	if (!of_find_property(pdev->dev.of_node, "qcom,qdss-presets", NULL)) {
+		/*
+		 * qcom,qdss-presets is an optional property. It likely won't be
+		 * present if we don't have any register settings to program
+		 */
+		dprintk(VIDC_DBG, "qcom,qdss-presets not found\n");
+		return rc;
+	}
+
+	qdss_addr_set = &res->qdss_addr_set;
+	qdss_addr_set->count = get_u32_array_num_elements(pdev->dev.of_node,
+					"qcom,qdss-presets");
+	qdss_addr_set->count /= sizeof(*qdss_addr_set->addr_tbl) / sizeof(u32);
+
+	if (!qdss_addr_set->count) {
+		dprintk(VIDC_DBG, "no elements in qdss reg set\n");
+		return rc;
+	}
+
+	qdss_addr_set->addr_tbl = devm_kzalloc(&pdev->dev,
+			qdss_addr_set->count * sizeof(*qdss_addr_set->addr_tbl),
+			GFP_KERNEL);
+	if (!qdss_addr_set->addr_tbl) {
+		dprintk(VIDC_ERR, "%s Failed to alloc register table\n",
+			__func__);
+		rc = -ENOMEM;
+		goto err_qdss_addr_tbl;
+	}
+
+	rc = of_property_read_u32_array(pdev->dev.of_node, "qcom,qdss-presets",
+		(u32 *)qdss_addr_set->addr_tbl, qdss_addr_set->count * 2);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to read qdss address table\n");
+		msm_vidc_free_qdss_addr_table(res);
+		rc = -EINVAL;
+		goto err_qdss_addr_tbl;
+	}
+
+	for (i = 0; i < qdss_addr_set->count; i++) {
+		dprintk(VIDC_DBG, "qdss addr = %x, value = %x\n",
+				qdss_addr_set->addr_tbl[i].start,
+				qdss_addr_set->addr_tbl[i].size);
+	}
+err_qdss_addr_tbl:
+	return rc;
+}
+
+static int msm_vidc_load_imem_ab_table(struct msm_vidc_platform_resources *res)
+{
+	int num_elements = 0;
+	struct platform_device *pdev = res->pdev;
+
+	if (!of_find_property(pdev->dev.of_node, "qcom,imem-ab-tbl", NULL)) {
+		/* optional property */
+		dprintk(VIDC_DBG, "qcom,imem-freq-tbl not found\n");
+		return 0;
+	}
+
+	num_elements = get_u32_array_num_elements(pdev->dev.of_node,
+			"qcom,imem-ab-tbl");
+	num_elements /= (sizeof(*res->imem_ab_tbl) / sizeof(u32));
+	if (!num_elements) {
+		dprintk(VIDC_ERR, "no elements in imem ab table\n");
+		return -EINVAL;
+	}
+
+	res->imem_ab_tbl = devm_kzalloc(&pdev->dev, num_elements *
+			sizeof(*res->imem_ab_tbl), GFP_KERNEL);
+	if (!res->imem_ab_tbl) {
+		dprintk(VIDC_ERR, "Failed to alloc imem_ab_tbl\n");
+		return -ENOMEM;
+	}
+
+	if (of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,imem-ab-tbl", (u32 *)res->imem_ab_tbl,
+		num_elements * sizeof(*res->imem_ab_tbl) / sizeof(u32))) {
+		dprintk(VIDC_ERR, "Failed to read imem_ab_tbl\n");
+		msm_vidc_free_imem_ab_table(res);
+		return -EINVAL;
+	}
+
+	res->imem_ab_tbl_size = num_elements;
+
+	return 0;
+}
+
+/**
+ * msm_vidc_load_u32_table() - load dtsi table entries
+ * @pdev: A pointer to the platform device.
+ * @of_node:      A pointer to the device node.
+ * @table_name:   A pointer to the dtsi table entry name.
+ * @struct_size:  The size of the structure which is nothing but
+ *                a single entry in the dtsi table.
+ * @table:        A pointer to the table pointer which needs to be
+ *                filled by the dtsi table entries.
+ * @num_elements: Number of elements pointer which needs to be filled
+ *                with the number of elements in the table.
+ *
+ * This is a generic implementation to load single or multiple array
+ * table from dtsi. The array elements should be of size equal to u32.
+ *
+ * Return:        Return '0' for success else appropriate error value.
+ */
+int msm_vidc_load_u32_table(struct platform_device *pdev,
+		struct device_node *of_node, char *table_name, int struct_size,
+		u32 **table, u32 *num_elements)
+{
+	int rc = 0, num_elemts = 0;
+	u32 *ptbl = NULL;
+
+	if (!of_find_property(of_node, table_name, NULL)) {
+		dprintk(VIDC_DBG, "%s not found\n", table_name);
+		return 0;
+	}
+
+	num_elemts = get_u32_array_num_elements(of_node, table_name);
+	if (!num_elemts) {
+		dprintk(VIDC_ERR, "no elements in %s\n", table_name);
+		return 0;
+	}
+	num_elemts /= struct_size / sizeof(u32);
+
+	ptbl = devm_kzalloc(&pdev->dev, num_elemts * struct_size, GFP_KERNEL);
+	if (!ptbl) {
+		dprintk(VIDC_ERR, "Failed to alloc table %s\n", table_name);
+		return -ENOMEM;
+	}
+
+	if (of_property_read_u32_array(of_node, table_name, ptbl,
+			num_elemts * struct_size / sizeof(u32))) {
+		dprintk(VIDC_ERR, "Failed to read %s\n", table_name);
+		return -EINVAL;
+	}
+
+	*table = ptbl;
+	if (num_elements)
+		*num_elements = num_elemts;
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_vidc_load_u32_table);
+
+static int msm_vidc_load_platform_version_table(
+		struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	struct platform_device *pdev = res->pdev;
+
+	if (!of_find_property(pdev->dev.of_node,
+			"qcom,platform-version", NULL)) {
+		dprintk(VIDC_DBG, "qcom,platform-version not found\n");
+		return 0;
+	}
+
+	rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node,
+			"qcom,platform-version",
+			sizeof(*res->pf_ver_tbl),
+			(u32 **)&res->pf_ver_tbl,
+			NULL);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"%s: failed to read platform version table\n",
+			__func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int msm_vidc_load_allowed_clocks_table(
+		struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	struct platform_device *pdev = res->pdev;
+
+	if (!of_find_property(pdev->dev.of_node,
+			"qcom,allowed-clock-rates", NULL)) {
+		dprintk(VIDC_DBG, "qcom,allowed-clock-rates not found\n");
+		return 0;
+	}
+
+	rc = msm_vidc_load_u32_table(pdev, pdev->dev.of_node,
+				"qcom,allowed-clock-rates",
+				sizeof(*res->allowed_clks_tbl),
+				(u32 **)&res->allowed_clks_tbl,
+				&res->allowed_clks_tbl_size);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"%s: failed to read allowed clocks table\n", __func__);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int msm_vidc_load_cycles_per_mb_table(
+		struct msm_vidc_platform_resources *res)
+{
+	int rc = 0, i = 0;
+	struct clock_freq_table *clock_freq_tbl = &res->clock_freq_tbl;
+	struct clock_profile_entry *entry = NULL;
+	struct device_node *parent_node = NULL;
+	struct device_node *child_node = NULL;
+	struct platform_device *pdev = res->pdev;
+
+	parent_node = of_find_node_by_name(pdev->dev.of_node,
+			"qcom,clock-freq-tbl");
+	if (!parent_node) {
+		dprintk(VIDC_DBG, "Node qcom,clock-freq-tbl not found.\n");
+		return 0;
+	}
+
+	clock_freq_tbl->count = 0;
+	for_each_child_of_node(parent_node, child_node)
+		clock_freq_tbl->count++;
+
+	if (!clock_freq_tbl->count) {
+		dprintk(VIDC_DBG, "No child nodes in qcom,clock-freq-tbl\n");
+		return 0;
+	}
+
+	clock_freq_tbl->clk_prof_entries = devm_kzalloc(&pdev->dev,
+		sizeof(*clock_freq_tbl->clk_prof_entries) *
+		clock_freq_tbl->count, GFP_KERNEL);
+	if (!clock_freq_tbl->clk_prof_entries) {
+		dprintk(VIDC_DBG, "no memory to allocate clk_prof_entries\n");
+		return -ENOMEM;
+	}
+
+	for_each_child_of_node(parent_node, child_node) {
+
+		if (i >= clock_freq_tbl->count) {
+			dprintk(VIDC_ERR,
+				"qcom,clock-freq-tbl: invalid child node %d, max is %d\n",
+				i, clock_freq_tbl->count);
+			break;
+		}
+
+		entry = &clock_freq_tbl->clk_prof_entries[i];
+		dprintk(VIDC_DBG, "qcom,clock-freq-tbl: profile[%d]\n", i);
+
+		if (of_find_property(child_node, "qcom,codec-mask", NULL)) {
+			rc = of_property_read_u32(child_node,
+					"qcom,codec-mask", &entry->codec_mask);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"qcom,codec-mask not found\n");
+				goto error;
+			}
+		} else {
+			entry->codec_mask = 0;
+		}
+		dprintk(VIDC_DBG, "codec_mask %#x\n", entry->codec_mask);
+
+		if (of_find_property(child_node, "qcom,cycles-per-mb", NULL)) {
+			rc = of_property_read_u32(child_node,
+					"qcom,cycles-per-mb", &entry->cycles);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"qcom,cycles-per-mb not found\n");
+				goto error;
+			}
+		} else {
+			entry->cycles = 0;
+		}
+		dprintk(VIDC_DBG, "cycles_per_mb %d\n", entry->cycles);
+
+		if (of_find_property(child_node,
+				"qcom,low-power-mode-factor", NULL)) {
+			rc = of_property_read_u32(child_node,
+					"qcom,low-power-mode-factor",
+					&entry->low_power_factor);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"qcom,low-power-mode-factor not found\n");
+				goto error;
+			}
+		} else {
+			entry->low_power_factor = 0;
+		}
+		dprintk(VIDC_DBG, "low_power_factor %d\n",
+				entry->low_power_factor);
+
+		i++;
+	}
+
+error:
+	return rc;
+}
+
+static int msm_vidc_load_freq_table(struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	int num_elements = 0;
+	struct platform_device *pdev = res->pdev;
+
+	/* A comparator to compare loads (needed later on) */
+	int cmp(const void *a, const void *b)
+	{
+		/* want to sort in reverse so flip the comparison */
+		return ((struct load_freq_table *)b)->load -
+			((struct load_freq_table *)a)->load;
+	}
+
+	if (!of_find_property(pdev->dev.of_node, "qcom,load-freq-tbl", NULL)) {
+		/*
+		 * qcom,load-freq-tbl is an optional property.  It likely won't
+		 * be present on cores that we can't clock scale on.
+		 */
+		dprintk(VIDC_DBG, "qcom,load-freq-tbl not found\n");
+		return 0;
+	}
+
+	num_elements = get_u32_array_num_elements(pdev->dev.of_node,
+			"qcom,load-freq-tbl");
+	num_elements /= sizeof(*res->load_freq_tbl) / sizeof(u32);
+	if (!num_elements) {
+		dprintk(VIDC_ERR, "no elements in frequency table\n");
+		return rc;
+	}
+
+	res->load_freq_tbl = devm_kzalloc(&pdev->dev, num_elements *
+			sizeof(*res->load_freq_tbl), GFP_KERNEL);
+	if (!res->load_freq_tbl) {
+		dprintk(VIDC_ERR,
+				"%s Failed to alloc load_freq_tbl\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	if (of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,load-freq-tbl", (u32 *)res->load_freq_tbl,
+		num_elements * sizeof(*res->load_freq_tbl) / sizeof(u32))) {
+		dprintk(VIDC_ERR, "Failed to read frequency table\n");
+		msm_vidc_free_freq_table(res);
+		return -EINVAL;
+	}
+
+	res->load_freq_tbl_size = num_elements;
+
+	/* The entries in the DT might not be sorted (for aesthetic purposes).
+	 * Given that we expect the loads in descending order for our scaling
+	 * logic to work, just sort it ourselves
+	 */
+	sort(res->load_freq_tbl, res->load_freq_tbl_size,
+			sizeof(*res->load_freq_tbl), cmp, NULL);
+	return rc;
+}
+
+static int msm_vidc_load_dcvs_table(struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	int num_elements = 0;
+	struct platform_device *pdev = res->pdev;
+
+	if (!of_find_property(pdev->dev.of_node, "qcom,dcvs-tbl", NULL)) {
+		/*
+		 * qcom,dcvs-tbl is an optional property. Incase qcom,dcvs-limit
+		 * property is present, it becomes mandatory. It likely won't
+		 * be present on targets that does not support the feature
+		 */
+		dprintk(VIDC_DBG, "qcom,dcvs-tbl not found\n");
+		return 0;
+	}
+
+	num_elements = get_u32_array_num_elements(pdev->dev.of_node,
+			"qcom,dcvs-tbl");
+	num_elements /= sizeof(*res->dcvs_tbl) / sizeof(u32);
+	if (!num_elements) {
+		dprintk(VIDC_ERR, "no elements in dcvs table\n");
+		return rc;
+	}
+
+	res->dcvs_tbl = devm_kzalloc(&pdev->dev, num_elements *
+			sizeof(*res->dcvs_tbl), GFP_KERNEL);
+	if (!res->dcvs_tbl) {
+		dprintk(VIDC_ERR,
+				"%s Failed to alloc dcvs_tbl\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	if (of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,dcvs-tbl", (u32 *)res->dcvs_tbl,
+		num_elements * sizeof(*res->dcvs_tbl) / sizeof(u32))) {
+		dprintk(VIDC_ERR, "Failed to read dcvs table\n");
+		msm_vidc_free_dcvs_table(res);
+		return -EINVAL;
+	}
+	res->dcvs_tbl_size = num_elements;
+
+	return rc;
+}
+
+static int msm_vidc_load_dcvs_limit(struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	int num_elements = 0;
+	struct platform_device *pdev = res->pdev;
+
+	if (!of_find_property(pdev->dev.of_node, "qcom,dcvs-limit", NULL)) {
+		/*
+		 * qcom,dcvs-limit is an optional property. Incase qcom,dcvs-tbl
+		 * property is present, it becomes mandatory. It likely won't
+		 * be present on targets that does not support the feature
+		 */
+		dprintk(VIDC_DBG, "qcom,dcvs-limit not found\n");
+		return 0;
+	}
+
+	num_elements = get_u32_array_num_elements(pdev->dev.of_node,
+			"qcom,dcvs-limit");
+	num_elements /= sizeof(*res->dcvs_limit) / sizeof(u32);
+	if (!num_elements) {
+		dprintk(VIDC_ERR, "no elements in dcvs limit\n");
+		res->dcvs_limit = NULL;
+		return rc;
+	}
+
+	res->dcvs_limit = devm_kzalloc(&pdev->dev, num_elements *
+			sizeof(*res->dcvs_limit), GFP_KERNEL);
+	if (!res->dcvs_limit) {
+		dprintk(VIDC_ERR,
+				"%s Failed to alloc dcvs_limit\n",
+				__func__);
+		return -ENOMEM;
+	}
+	if (of_property_read_u32_array(pdev->dev.of_node,
+		"qcom,dcvs-limit", (u32 *)res->dcvs_limit,
+		num_elements * sizeof(*res->dcvs_limit) / sizeof(u32))) {
+		dprintk(VIDC_ERR, "Failed to read dcvs limit\n");
+		msm_vidc_free_dcvs_limit(res);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+
+static int msm_vidc_populate_bus(struct device *dev,
+		struct msm_vidc_platform_resources *res)
+{
+	struct bus_set *buses = &res->bus_set;
+	const char *temp_name = NULL;
+	struct bus_info *bus = NULL, *temp_table;
+	u32 range[2];
+	int rc = 0;
+
+	temp_table = krealloc(buses->bus_tbl, sizeof(*temp_table) *
+			(buses->count + 1), GFP_KERNEL);
+	if (!temp_table) {
+		dprintk(VIDC_ERR, "%s: Failed to allocate memory", __func__);
+		rc = -ENOMEM;
+		goto err_bus;
+	}
+
+	buses->bus_tbl = temp_table;
+	bus = &buses->bus_tbl[buses->count];
+
+	rc = of_property_read_string(dev->of_node, "label", &temp_name);
+	if (rc) {
+		dprintk(VIDC_ERR, "'label' not found in node\n");
+		goto err_bus;
+	}
+	/* need a non-const version of name, hence copying it over */
+	bus->name = devm_kstrdup(dev, temp_name, GFP_KERNEL);
+	if (!bus->name) {
+		rc = -ENOMEM;
+		goto err_bus;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,bus-master",
+			&bus->master);
+	if (rc) {
+		dprintk(VIDC_ERR, "'qcom,bus-master' not found in node\n");
+		goto err_bus;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,bus-slave", &bus->slave);
+	if (rc) {
+		dprintk(VIDC_ERR, "'qcom,bus-slave' not found in node\n");
+		goto err_bus;
+	}
+
+	rc = of_property_read_string(dev->of_node, "qcom,bus-governor",
+			&bus->governor);
+	if (rc) {
+		rc = 0;
+		dprintk(VIDC_DBG,
+				"'qcom,bus-governor' not found, default to performance governor\n");
+		bus->governor = "performance";
+	}
+
+	rc = of_property_read_u32_array(dev->of_node, "qcom,bus-range-kbps",
+			range, ARRAY_SIZE(range));
+	if (rc) {
+		rc = 0;
+		dprintk(VIDC_DBG,
+				"'qcom,range' not found defaulting to <0 INT_MAX>\n");
+		range[0] = 0;
+		range[1] = INT_MAX;
+	}
+
+	bus->range[0] = range[0]; /* min */
+	bus->range[1] = range[1]; /* max */
+
+	buses->count++;
+	bus->dev = dev;
+	dprintk(VIDC_DBG, "Found bus %s [%d->%d] with governor %s\n",
+			bus->name, bus->master, bus->slave, bus->governor);
+err_bus:
+	return rc;
+}
+
+static int msm_vidc_load_buffer_usage_table(
+		struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	struct platform_device *pdev = res->pdev;
+	struct buffer_usage_set *buffer_usage_set = &res->buffer_usage_set;
+
+	if (!of_find_property(pdev->dev.of_node,
+				"qcom,buffer-type-tz-usage-table", NULL)) {
+		/*
+		 * qcom,buffer-type-tz-usage-table is an optional property.  It
+		 * likely won't be present if the core doesn't support content
+		 * protection
+		 */
+		dprintk(VIDC_DBG, "buffer-type-tz-usage-table not found\n");
+		return 0;
+	}
+
+	buffer_usage_set->count = get_u32_array_num_elements(
+		pdev->dev.of_node, "qcom,buffer-type-tz-usage-table");
+	buffer_usage_set->count /=
+		sizeof(*buffer_usage_set->buffer_usage_tbl) / sizeof(u32);
+	if (!buffer_usage_set->count) {
+		dprintk(VIDC_DBG, "no elements in buffer usage set\n");
+		return 0;
+	}
+
+	buffer_usage_set->buffer_usage_tbl = devm_kzalloc(&pdev->dev,
+			buffer_usage_set->count *
+			sizeof(*buffer_usage_set->buffer_usage_tbl),
+			GFP_KERNEL);
+	if (!buffer_usage_set->buffer_usage_tbl) {
+		dprintk(VIDC_ERR, "%s Failed to alloc buffer usage table\n",
+			__func__);
+		rc = -ENOMEM;
+		goto err_load_buf_usage;
+	}
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+		    "qcom,buffer-type-tz-usage-table",
+		(u32 *)buffer_usage_set->buffer_usage_tbl,
+		buffer_usage_set->count *
+		sizeof(*buffer_usage_set->buffer_usage_tbl) / sizeof(u32));
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to read buffer usage table\n");
+		goto err_load_buf_usage;
+	}
+
+	return 0;
+err_load_buf_usage:
+	msm_vidc_free_buffer_usage_table(res);
+	return rc;
+}
+
+static int msm_vidc_load_regulator_table(
+		struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	struct platform_device *pdev = res->pdev;
+	struct regulator_set *regulators = &res->regulator_set;
+	struct device_node *domains_parent_node = NULL;
+	struct property *domains_property = NULL;
+	int reg_count = 0;
+
+	regulators->count = 0;
+	regulators->regulator_tbl = NULL;
+
+	domains_parent_node = pdev->dev.of_node;
+	for_each_property_of_node(domains_parent_node, domains_property) {
+		const char *search_string = "-supply";
+		char *supply;
+		bool matched = false;
+
+		/* check if current property is possibly a regulator */
+		supply = strnstr(domains_property->name, search_string,
+				strlen(domains_property->name) + 1);
+		matched = supply && (*(supply + strlen(search_string)) == '\0');
+		if (!matched)
+			continue;
+
+		reg_count++;
+	}
+
+	regulators->regulator_tbl = devm_kzalloc(&pdev->dev,
+			sizeof(*regulators->regulator_tbl) *
+			reg_count, GFP_KERNEL);
+
+	if (!regulators->regulator_tbl) {
+		rc = -ENOMEM;
+		dprintk(VIDC_ERR,
+			"Failed to alloc memory for regulator table\n");
+		goto err_reg_tbl_alloc;
+	}
+
+	for_each_property_of_node(domains_parent_node, domains_property) {
+		const char *search_string = "-supply";
+		char *supply;
+		bool matched = false;
+		struct device_node *regulator_node = NULL;
+		struct regulator_info *rinfo = NULL;
+
+		/* check if current property is possibly a regulator */
+		supply = strnstr(domains_property->name, search_string,
+				strlen(domains_property->name) + 1);
+		matched = supply && (supply[strlen(search_string)] == '\0');
+		if (!matched)
+			continue;
+
+		/* make sure prop isn't being misused */
+		regulator_node = of_parse_phandle(domains_parent_node,
+				domains_property->name, 0);
+		if (IS_ERR(regulator_node)) {
+			dprintk(VIDC_WARN, "%s is not a phandle\n",
+					domains_property->name);
+			continue;
+		}
+		regulators->count++;
+
+		/* populate regulator info */
+		rinfo = &regulators->regulator_tbl[regulators->count - 1];
+		rinfo->name = devm_kzalloc(&pdev->dev,
+			(supply - domains_property->name) + 1, GFP_KERNEL);
+		if (!rinfo->name) {
+			rc = -ENOMEM;
+			dprintk(VIDC_ERR,
+					"Failed to alloc memory for regulator name\n");
+			goto err_reg_name_alloc;
+		}
+		strlcpy(rinfo->name, domains_property->name,
+			(supply - domains_property->name) + 1);
+
+		rinfo->has_hw_power_collapse = of_property_read_bool(
+			regulator_node, "qcom,support-hw-trigger");
+
+		dprintk(VIDC_DBG, "Found regulator %s: h/w collapse = %s\n",
+				rinfo->name,
+				rinfo->has_hw_power_collapse ? "yes" : "no");
+	}
+
+	if (!regulators->count)
+		dprintk(VIDC_DBG, "No regulators found");
+
+	return 0;
+
+err_reg_name_alloc:
+err_reg_tbl_alloc:
+	msm_vidc_free_regulator_table(res);
+	return rc;
+}
+
+static int msm_vidc_load_clock_table(
+		struct msm_vidc_platform_resources *res)
+{
+	int rc = 0, num_clocks = 0, c = 0;
+	struct platform_device *pdev = res->pdev;
+	int *clock_props = NULL;
+	struct clock_set *clocks = &res->clock_set;
+
+	num_clocks = of_property_count_strings(pdev->dev.of_node,
+				"clock-names");
+	if (num_clocks <= 0) {
+		dprintk(VIDC_DBG, "No clocks found\n");
+		clocks->count = 0;
+		rc = 0;
+		goto err_load_clk_table_fail;
+	}
+
+	clock_props = devm_kzalloc(&pdev->dev, num_clocks *
+			sizeof(*clock_props), GFP_KERNEL);
+	if (!clock_props) {
+		dprintk(VIDC_ERR, "No memory to read clock properties\n");
+		rc = -ENOMEM;
+		goto err_load_clk_table_fail;
+	}
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+				"qcom,clock-configs", clock_props,
+				num_clocks);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to read clock properties: %d\n", rc);
+		goto err_load_clk_prop_fail;
+	}
+
+	clocks->clock_tbl = devm_kzalloc(&pdev->dev, sizeof(*clocks->clock_tbl)
+			* num_clocks, GFP_KERNEL);
+	if (!clocks->clock_tbl) {
+		dprintk(VIDC_ERR, "Failed to allocate memory for clock tbl\n");
+		rc = -ENOMEM;
+		goto err_load_clk_prop_fail;
+	}
+
+	clocks->count = num_clocks;
+	dprintk(VIDC_DBG, "Found %d clocks\n", num_clocks);
+
+	for (c = 0; c < num_clocks; ++c) {
+		struct clock_info *vc = &res->clock_set.clock_tbl[c];
+
+		of_property_read_string_index(pdev->dev.of_node,
+				"clock-names", c, &vc->name);
+
+		if (clock_props[c] & CLOCK_PROP_HAS_SCALING) {
+			vc->has_scaling = true;
+			vc->count = res->load_freq_tbl_size;
+			vc->load_freq_tbl = res->load_freq_tbl;
+		} else {
+			vc->count = 0;
+			vc->load_freq_tbl = NULL;
+			vc->has_scaling = false;
+		}
+
+		if (clock_props[c] & CLOCK_PROP_HAS_MEM_RETENTION)
+			vc->has_mem_retention = true;
+		else
+			vc->has_mem_retention = false;
+
+		dprintk(VIDC_DBG, "Found clock %s: scale-able = %s\n", vc->name,
+			vc->count ? "yes" : "no");
+	}
+
+
+	return 0;
+
+err_load_clk_prop_fail:
+err_load_clk_table_fail:
+	return rc;
+}
+
+int read_platform_resources_from_dt(
+		struct msm_vidc_platform_resources *res)
+{
+	struct platform_device *pdev = res->pdev;
+	struct resource *kres = NULL;
+	int rc = 0;
+	uint32_t firmware_base = 0;
+
+	if (!pdev->dev.of_node) {
+		dprintk(VIDC_ERR, "DT node not found\n");
+		return -ENOENT;
+	}
+
+	INIT_LIST_HEAD(&res->context_banks);
+
+	res->firmware_base = (phys_addr_t)firmware_base;
+
+	kres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	res->register_base = kres ? kres->start : -1;
+	res->register_size = kres ? (kres->end + 1 - kres->start) : -1;
+
+	kres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	res->irq = kres ? kres->start : -1;
+
+	of_property_read_u32(pdev->dev.of_node,
+			"qcom,imem-size", &res->imem_size);
+	res->imem_type = read_imem_type(pdev);
+
+	res->sys_idle_indicator = of_property_read_bool(pdev->dev.of_node,
+			"qcom,enable-idle-indicator");
+
+	res->thermal_mitigable =
+			of_property_read_bool(pdev->dev.of_node,
+			"qcom,enable-thermal-mitigation");
+
+	rc = of_property_read_string(pdev->dev.of_node, "qcom,firmware-name",
+			&res->fw_name);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to read firmware name: %d\n", rc);
+		goto err_load_freq_table;
+	}
+	dprintk(VIDC_DBG, "Firmware filename: %s\n", res->fw_name);
+
+	rc = of_property_read_string(pdev->dev.of_node, "qcom,hfi-version",
+			&res->hfi_version);
+	if (rc)
+		dprintk(VIDC_DBG, "HFI packetization will default to legacy\n");
+
+	rc = msm_vidc_load_platform_version_table(res);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to load pf version table: %d\n", rc);
+
+	rc = msm_vidc_load_freq_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to load freq table: %d\n", rc);
+		goto err_load_freq_table;
+	}
+
+	rc = msm_vidc_load_dcvs_table(res);
+	if (rc)
+		dprintk(VIDC_WARN, "Failed to load dcvs table: %d\n", rc);
+
+	rc = msm_vidc_load_dcvs_limit(res);
+	if (rc)
+		dprintk(VIDC_WARN, "Failed to load dcvs limit: %d\n", rc);
+
+	rc = msm_vidc_load_imem_ab_table(res);
+	if (rc)
+		dprintk(VIDC_WARN, "Failed to load freq table: %d\n", rc);
+
+	rc = msm_vidc_load_qdss_table(res);
+	if (rc)
+		dprintk(VIDC_WARN, "Failed to load qdss reg table: %d\n", rc);
+
+	rc = msm_vidc_load_reg_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to load reg table: %d\n", rc);
+		goto err_load_reg_table;
+	}
+
+	rc = msm_vidc_load_buffer_usage_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to load buffer usage table: %d\n", rc);
+		goto err_load_buffer_usage_table;
+	}
+
+	rc = msm_vidc_load_regulator_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to load list of regulators %d\n", rc);
+		goto err_load_regulator_table;
+	}
+
+	rc = msm_vidc_load_clock_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to load clock table: %d\n", rc);
+		goto err_load_clock_table;
+	}
+
+	rc = msm_vidc_load_cycles_per_mb_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to load cycles per mb table: %d\n", rc);
+		goto err_load_cycles_per_mb_table;
+	}
+
+	rc = msm_vidc_load_allowed_clocks_table(res);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to load allowed clocks table: %d\n", rc);
+		goto err_load_allowed_clocks_table;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,max-hw-load",
+			&res->max_load);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to determine max load supported: %d\n", rc);
+		goto err_load_max_hw_load;
+	}
+
+	rc = msm_vidc_populate_legacy_context_bank(res);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Failed to setup context banks %d\n", rc);
+		goto err_setup_legacy_cb;
+	}
+
+	res->use_non_secure_pil = of_property_read_bool(pdev->dev.of_node,
+			"qcom,use-non-secure-pil");
+
+	if (res->use_non_secure_pil || !is_iommu_present(res)) {
+		of_property_read_u32(pdev->dev.of_node, "qcom,fw-bias",
+				&firmware_base);
+		res->firmware_base = (phys_addr_t)firmware_base;
+		dprintk(VIDC_DBG,
+				"Using fw-bias : %pa", &res->firmware_base);
+	}
+
+	res->sw_power_collapsible = of_property_read_bool(pdev->dev.of_node,
+					"qcom,sw-power-collapse");
+	dprintk(VIDC_DBG, "Power collapse supported = %s\n",
+		res->sw_power_collapsible ? "yes" : "no");
+
+	res->never_unload_fw = of_property_read_bool(pdev->dev.of_node,
+			"qcom,never-unload-fw");
+
+	res->debug_timeout = of_property_read_bool(pdev->dev.of_node,
+			"qcom,debug-timeout");
+
+	res->debug_timeout |= msm_vidc_debug_timeout;
+
+	of_property_read_u32(pdev->dev.of_node,
+			"qcom,pm-qos-latency-us", &res->pm_qos_latency_us);
+
+	res->slave_side_cp = of_property_read_bool(pdev->dev.of_node,
+					"qcom,slave-side-cp");
+	dprintk(VIDC_DBG, "Slave side cp = %s\n",
+				res->slave_side_cp ? "yes" : "no");
+
+	of_property_read_u32(pdev->dev.of_node,
+			"qcom,max-secure-instances",
+			&res->max_secure_inst_count);
+	return rc;
+
+err_setup_legacy_cb:
+err_load_max_hw_load:
+	msm_vidc_free_allowed_clocks_table(res);
+err_load_allowed_clocks_table:
+	msm_vidc_free_cycles_per_mb_table(res);
+err_load_cycles_per_mb_table:
+	msm_vidc_free_clock_table(res);
+err_load_clock_table:
+	msm_vidc_free_regulator_table(res);
+err_load_regulator_table:
+	msm_vidc_free_buffer_usage_table(res);
+err_load_buffer_usage_table:
+	msm_vidc_free_reg_table(res);
+err_load_reg_table:
+	msm_vidc_free_freq_table(res);
+err_load_freq_table:
+	return rc;
+}
+
+static int get_secure_vmid(struct context_bank_info *cb)
+{
+	if (!strcasecmp(cb->name, "venus_sec_bitstream"))
+		return VMID_CP_BITSTREAM;
+	else if (!strcasecmp(cb->name, "venus_sec_pixel"))
+		return VMID_CP_PIXEL;
+	else if (!strcasecmp(cb->name, "venus_sec_non_pixel"))
+		return VMID_CP_NON_PIXEL;
+
+	WARN(1, "No matching secure vmid for cb name: %s\n",
+		cb->name);
+	return VMID_INVAL;
+}
+
+static int msm_vidc_setup_context_bank(struct context_bank_info *cb,
+		struct device *dev)
+{
+	int rc = 0;
+	int secure_vmid = VMID_INVAL;
+	struct bus_type *bus;
+
+	if (!dev || !cb) {
+		dprintk(VIDC_ERR,
+			"%s: Invalid Input params\n", __func__);
+		return -EINVAL;
+	}
+	cb->dev = dev;
+
+	bus = cb->dev->bus;
+	if (IS_ERR_OR_NULL(bus)) {
+		dprintk(VIDC_ERR, "%s - failed to get bus type\n", __func__);
+		rc = PTR_ERR(bus) ?: -ENODEV;
+		goto remove_cb;
+	}
+
+	cb->mapping = arm_iommu_create_mapping(bus, cb->addr_range.start,
+					cb->addr_range.size);
+	if (IS_ERR_OR_NULL(cb->mapping)) {
+		dprintk(VIDC_ERR, "%s - failed to create mapping\n", __func__);
+		rc = PTR_ERR(cb->mapping) ?: -ENODEV;
+		goto remove_cb;
+	}
+
+	if (cb->is_secure) {
+		secure_vmid = get_secure_vmid(cb);
+		rc = iommu_domain_set_attr(cb->mapping->domain,
+				DOMAIN_ATTR_SECURE_VMID, &secure_vmid);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"%s - programming secure vmid failed: %s %d\n",
+					__func__, dev_name(dev), rc);
+			goto release_mapping;
+		}
+	}
+
+	rc = arm_iommu_attach_device(cb->dev, cb->mapping);
+	if (rc) {
+		dprintk(VIDC_ERR, "%s - Couldn't arm_iommu_attach_device\n",
+			__func__);
+		goto release_mapping;
+	}
+
+	dprintk(VIDC_DBG, "Attached %s and created mapping\n", dev_name(dev));
+	dprintk(VIDC_DBG,
+		"Context bank name:%s, buffer_type: %#x, is_secure: %d, address range start: %#x, size: %#x, dev: %pK, mapping: %pK",
+		cb->name, cb->buffer_type, cb->is_secure, cb->addr_range.start,
+		cb->addr_range.size, cb->dev, cb->mapping);
+
+	return rc;
+
+release_mapping:
+	arm_iommu_release_mapping(cb->mapping);
+remove_cb:
+	return rc;
+}
+
+int msm_vidc_smmu_fault_handler(struct iommu_domain *domain,
+		struct device *dev, unsigned long iova, int flags, void *token)
+{
+	struct msm_vidc_core *core = token;
+	struct msm_vidc_inst *inst;
+
+	if (!domain || !core) {
+		dprintk(VIDC_ERR, "%s - invalid param %pK %pK\n",
+			__func__, domain, core);
+		return -EINVAL;
+	}
+
+	if (core->smmu_fault_handled)
+		return -ENOSYS;
+
+	dprintk(VIDC_ERR, "%s - faulting address: %lx\n", __func__, iova);
+
+	mutex_lock(&core->lock);
+	list_for_each_entry(inst, &core->instances, list) {
+		msm_comm_print_inst_info(inst);
+	}
+	core->smmu_fault_handled = true;
+	mutex_unlock(&core->lock);
+	/*
+	 * Return -ENOSYS to elicit the default behaviour of smmu driver.
+	 * If we return -ENOSYS, then smmu driver assumes page fault handler
+	 * is not installed and prints a list of useful debug information like
+	 * FAR, SID etc. This information is not printed if we return 0.
+	 */
+	return -ENOSYS;
+}
+
+static int msm_vidc_populate_context_bank(struct device *dev,
+		struct msm_vidc_core *core)
+{
+	int rc = 0;
+	struct context_bank_info *cb = NULL;
+	struct device_node *np = NULL;
+
+	if (!dev || !core) {
+		dprintk(VIDC_ERR, "%s - invalid inputs\n", __func__);
+		return -EINVAL;
+	}
+
+	np = dev->of_node;
+	cb = devm_kzalloc(dev, sizeof(*cb), GFP_KERNEL);
+	if (!cb) {
+		dprintk(VIDC_ERR, "%s - Failed to allocate cb\n", __func__);
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&cb->list);
+	list_add_tail(&cb->list, &core->resources.context_banks);
+
+	rc = of_property_read_string(np, "label", &cb->name);
+	if (rc) {
+		dprintk(VIDC_DBG,
+			"Failed to read cb label from device tree\n");
+		rc = 0;
+	}
+
+	dprintk(VIDC_DBG, "%s: context bank has name %s\n", __func__, cb->name);
+	rc = of_property_read_u32_array(np, "virtual-addr-pool",
+			(u32 *)&cb->addr_range, 2);
+	if (rc) {
+		dprintk(VIDC_ERR,
+			"Could not read addr pool for context bank : %s %d\n",
+			cb->name, rc);
+		goto err_setup_cb;
+	}
+
+	cb->is_secure = of_property_read_bool(np, "qcom,secure-context-bank");
+	dprintk(VIDC_DBG, "context bank %s : secure = %d\n",
+			cb->name, cb->is_secure);
+
+	/* setup buffer type for each sub device*/
+	rc = of_property_read_u32(np, "buffer-types", &cb->buffer_type);
+	if (rc) {
+		dprintk(VIDC_ERR, "failed to load buffer_type info %d\n", rc);
+		rc = -ENOENT;
+		goto err_setup_cb;
+	}
+	dprintk(VIDC_DBG,
+		"context bank %s address start = %x address size = %x buffer_type = %x\n",
+		cb->name, cb->addr_range.start,
+		cb->addr_range.size, cb->buffer_type);
+
+	rc = msm_vidc_setup_context_bank(cb, dev);
+	if (rc) {
+		dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc);
+		goto err_setup_cb;
+	}
+
+	iommu_set_fault_handler(cb->mapping->domain,
+		msm_vidc_smmu_fault_handler, (void *)core);
+
+	return 0;
+
+err_setup_cb:
+	list_del(&cb->list);
+	return rc;
+}
+
+static int msm_vidc_populate_legacy_context_bank(
+			struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+	struct platform_device *pdev = NULL;
+	struct device_node *domains_parent_node = NULL;
+	struct device_node *domains_child_node = NULL;
+	struct device_node *ctx_node = NULL;
+	struct context_bank_info *cb;
+
+	if (!res || !res->pdev) {
+		dprintk(VIDC_ERR, "%s - invalid inputs\n", __func__);
+		return -EINVAL;
+	}
+	pdev = res->pdev;
+
+	domains_parent_node = of_find_node_by_name(pdev->dev.of_node,
+			"qcom,vidc-iommu-domains");
+	if (!domains_parent_node) {
+		dprintk(VIDC_DBG,
+			"%s legacy iommu domains not present\n", __func__);
+		return 0;
+	}
+
+	/* set up each context bank for legacy DT bindings*/
+	for_each_child_of_node(domains_parent_node,
+		domains_child_node) {
+		cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL);
+		if (!cb) {
+			dprintk(VIDC_ERR,
+				"%s - Failed to allocate cb\n", __func__);
+			return -ENOMEM;
+		}
+		INIT_LIST_HEAD(&cb->list);
+		list_add_tail(&cb->list, &res->context_banks);
+
+		ctx_node = of_parse_phandle(domains_child_node,
+				"qcom,vidc-domain-phandle", 0);
+		if (!ctx_node) {
+			dprintk(VIDC_ERR,
+				"%s Unable to parse pHandle\n", __func__);
+			rc = -EBADHANDLE;
+			goto err_setup_cb;
+		}
+
+		rc = of_property_read_string(ctx_node, "label", &(cb->name));
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s Could not find label\n", __func__);
+			goto err_setup_cb;
+		}
+
+		rc = of_property_read_u32_array(ctx_node,
+			"qcom,virtual-addr-pool", (u32 *)&cb->addr_range, 2);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s Could not read addr pool for group : %s (%d)\n",
+				__func__, cb->name, rc);
+			goto err_setup_cb;
+		}
+
+		cb->is_secure =
+			of_property_read_bool(ctx_node, "qcom,secure-domain");
+
+		rc = of_property_read_u32(domains_child_node,
+				"qcom,vidc-buffer-types", &cb->buffer_type);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s Could not read buffer type (%d)\n",
+				__func__, rc);
+			goto err_setup_cb;
+		}
+
+		cb->dev = msm_iommu_get_ctx(cb->name);
+		if (IS_ERR_OR_NULL(cb->dev)) {
+			dprintk(VIDC_ERR, "%s could not get device for cb %s\n",
+					__func__, cb->name);
+			rc = -ENOENT;
+			goto err_setup_cb;
+		}
+
+		rc = msm_vidc_setup_context_bank(cb, cb->dev);
+		if (rc) {
+			dprintk(VIDC_ERR, "Cannot setup context bank %d\n", rc);
+			goto err_setup_cb;
+		}
+		dprintk(VIDC_DBG,
+			"%s: context bank %s secure %d addr start = %#x addr size = %#x buffer_type = %#x\n",
+			__func__, cb->name, cb->is_secure, cb->addr_range.start,
+			cb->addr_range.size, cb->buffer_type);
+	}
+	return rc;
+
+err_setup_cb:
+	list_del(&cb->list);
+	return rc;
+}
+
+int read_context_bank_resources_from_dt(struct platform_device *pdev)
+{
+	struct msm_vidc_core *core;
+	int rc = 0;
+
+	if (!pdev) {
+		dprintk(VIDC_ERR, "Invalid platform device\n");
+		return -EINVAL;
+	} else if (!pdev->dev.parent) {
+		dprintk(VIDC_ERR, "Failed to find a parent for %s\n",
+				dev_name(&pdev->dev));
+		return -ENODEV;
+	}
+
+	core = dev_get_drvdata(pdev->dev.parent);
+	if (!core) {
+		dprintk(VIDC_ERR, "Failed to find cookie in parent device %s",
+				dev_name(pdev->dev.parent));
+		return -EINVAL;
+	}
+
+	if (of_property_read_bool(pdev->dev.of_node, "qcom,fw-context-bank")) {
+		if (core->resources.use_non_secure_pil) {
+			struct context_bank_info *cb;
+
+			cb = devm_kzalloc(&pdev->dev, sizeof(*cb), GFP_KERNEL);
+			if (!cb) {
+				dprintk(VIDC_ERR, "alloc venus cb failed\n");
+				return -ENOMEM;
+			}
+
+			cb->dev = &pdev->dev;
+			rc = venus_boot_init(&core->resources, cb);
+			if (rc) {
+				dprintk(VIDC_ERR,
+				"Failed to init non-secure PIL %d\n", rc);
+			}
+		}
+	} else {
+		rc = msm_vidc_populate_context_bank(&pdev->dev, core);
+		if (rc)
+			dprintk(VIDC_ERR, "Failed to probe context bank\n");
+		else
+			dprintk(VIDC_DBG, "Successfully probed context bank\n");
+	}
+	return rc;
+}
+
+int read_bus_resources_from_dt(struct platform_device *pdev)
+{
+	struct msm_vidc_core *core;
+
+	if (!pdev) {
+		dprintk(VIDC_ERR, "Invalid platform device\n");
+		return -EINVAL;
+	} else if (!pdev->dev.parent) {
+		dprintk(VIDC_ERR, "Failed to find a parent for %s\n",
+				dev_name(&pdev->dev));
+		return -ENODEV;
+	}
+
+	core = dev_get_drvdata(pdev->dev.parent);
+	if (!core) {
+		dprintk(VIDC_ERR, "Failed to find cookie in parent device %s",
+				dev_name(pdev->dev.parent));
+		return -EINVAL;
+	}
+
+	return msm_vidc_populate_bus(&pdev->dev, &core->resources);
+}
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
new file mode 100644
index 0000000..4ba9057
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_res_parse.h
@@ -0,0 +1,36 @@
+
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef DT_PARSE
+#define DT_PARSE
+#include <linux/of.h>
+#include "msm_vidc_resources.h"
+#include "msm_vidc_common.h"
+void msm_vidc_free_platform_resources(
+		struct msm_vidc_platform_resources *res);
+
+int read_hfi_type(struct platform_device *pdev);
+
+int read_platform_resources_from_dt(
+		struct msm_vidc_platform_resources *res);
+
+int read_context_bank_resources_from_dt(struct platform_device *pdev);
+
+int read_bus_resources_from_dt(struct platform_device *pdev);
+
+int msm_vidc_load_u32_table(struct platform_device *pdev,
+		struct device_node *of_node, char *table_name, int struct_size,
+		u32 **table, u32 *num_elements);
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/msm_vidc_resources.h b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
new file mode 100644
index 0000000..4f152fb
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/msm_vidc_resources.h
@@ -0,0 +1,203 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __MSM_VIDC_RESOURCES_H__
+#define __MSM_VIDC_RESOURCES_H__
+
+#include <linux/devfreq.h>
+#include <linux/platform_device.h>
+#include <media/msm_vidc.h>
+#define MAX_BUFFER_TYPES 32
+
+struct platform_version_table {
+	u32 version_mask;
+	u32 version_shift;
+};
+
+struct load_freq_table {
+	u32 load;
+	u32 freq;
+	u32 supported_codecs;
+};
+
+struct dcvs_table {
+	u32 load;
+	u32 load_low;
+	u32 load_high;
+	u32 supported_codecs;
+};
+
+struct dcvs_limit {
+	u32 min_mbpf;
+	u32 fps;
+};
+
+struct imem_ab_table {
+	u32 core_freq;
+	u32 imem_ab;
+};
+
+struct reg_value_pair {
+	u32 reg;
+	u32 value;
+};
+
+struct reg_set {
+	struct reg_value_pair *reg_tbl;
+	int count;
+};
+
+struct addr_range {
+	u32 start;
+	u32 size;
+};
+
+struct addr_set {
+	struct addr_range *addr_tbl;
+	int count;
+};
+
+struct context_bank_info {
+	struct list_head list;
+	const char *name;
+	u32 buffer_type;
+	bool is_secure;
+	struct addr_range addr_range;
+	struct device *dev;
+	struct dma_iommu_mapping *mapping;
+};
+
+struct buffer_usage_table {
+	u32 buffer_type;
+	u32 tz_usage;
+};
+
+struct buffer_usage_set {
+	struct buffer_usage_table *buffer_usage_tbl;
+	u32 count;
+};
+
+struct regulator_info {
+	struct regulator *regulator;
+	bool has_hw_power_collapse;
+	char *name;
+};
+
+struct regulator_set {
+	struct regulator_info *regulator_tbl;
+	u32 count;
+};
+
+struct clock_info {
+	const char *name;
+	struct clk *clk;
+	struct load_freq_table *load_freq_tbl;
+	u32 count;
+	bool has_scaling;
+	bool has_mem_retention;
+};
+
+struct clock_set {
+	struct clock_info *clock_tbl;
+	u32 count;
+};
+
+struct bus_info {
+	char *name;
+	int master;
+	int slave;
+	unsigned int range[2];
+	const char *governor;
+	struct device *dev;
+	struct devfreq_dev_profile devfreq_prof;
+	struct devfreq *devfreq;
+	struct msm_bus_client_handle *client;
+};
+
+struct bus_set {
+	struct bus_info *bus_tbl;
+	u32 count;
+};
+
+enum imem_type {
+	IMEM_NONE,
+	IMEM_OCMEM,
+	IMEM_VMEM,
+	IMEM_MAX,
+};
+
+struct allowed_clock_rates_table {
+	u32 clock_rate;
+};
+
+struct clock_profile_entry {
+	u32 codec_mask;
+	u32 cycles;
+	u32 low_power_factor;
+};
+
+struct clock_freq_table {
+	struct clock_profile_entry *clk_prof_entries;
+	u32 count;
+};
+
+struct msm_vidc_platform_resources {
+	phys_addr_t firmware_base;
+	phys_addr_t register_base;
+	uint32_t register_size;
+	uint32_t irq;
+	struct platform_version_table *pf_ver_tbl;
+	struct allowed_clock_rates_table *allowed_clks_tbl;
+	u32 allowed_clks_tbl_size;
+	struct clock_freq_table clock_freq_tbl;
+	struct load_freq_table *load_freq_tbl;
+	uint32_t load_freq_tbl_size;
+	struct dcvs_table *dcvs_tbl;
+	uint32_t dcvs_tbl_size;
+	struct dcvs_limit *dcvs_limit;
+	struct imem_ab_table *imem_ab_tbl;
+	u32 imem_ab_tbl_size;
+	struct reg_set reg_set;
+	struct addr_set qdss_addr_set;
+	struct buffer_usage_set buffer_usage_set;
+	uint32_t imem_size;
+	enum imem_type imem_type;
+	uint32_t max_load;
+	struct platform_device *pdev;
+	struct regulator_set regulator_set;
+	struct clock_set clock_set;
+	struct bus_set bus_set;
+	bool use_non_secure_pil;
+	bool sw_power_collapsible;
+	bool sys_idle_indicator;
+	bool slave_side_cp;
+	struct list_head context_banks;
+	bool thermal_mitigable;
+	const char *fw_name;
+	const char *hfi_version;
+	bool never_unload_fw;
+	bool debug_timeout;
+	uint32_t pm_qos_latency_us;
+	uint32_t max_inst_count;
+	uint32_t max_secure_inst_count;
+};
+
+static inline bool is_iommu_present(struct msm_vidc_platform_resources *res)
+{
+	return !list_empty(&res->context_banks);
+}
+
+extern uint32_t msm_vidc_pwr_collapse_delay;
+
+#endif
+
diff --git a/drivers/media/platform/msm/vidc/venus_boot.c b/drivers/media/platform/msm/vidc/venus_boot.c
new file mode 100644
index 0000000..e33a76b
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/venus_boot.c
@@ -0,0 +1,488 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define VIDC_DBG_LABEL "venus_boot"
+
+#include <asm/dma-iommu.h>
+#include <asm/page.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/subsystem_restart.h>
+#include "msm_vidc_debug.h"
+#include "vidc_hfi_io.h"
+#include "venus_boot.h"
+
+/* VENUS WRAPPER registers */
+#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v1 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x1018)
+#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v1 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x101C)
+#define VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v1 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x1020)
+#define VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v1 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x1024)
+
+#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v2 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x1020)
+#define VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v2 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x1024)
+#define VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v2 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x1028)
+#define VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v2 \
+				(VIDC_WRAPPER_BASE_OFFS + 0x102C)
+
+#define VENUS_WRAPPER_SW_RESET	(VIDC_WRAPPER_BASE_OFFS + 0x3000)
+
+/* VENUS VBIF registers */
+#define VENUS_VBIF_CLKON_FORCE_ON			BIT(0)
+
+#define VENUS_VBIF_ADDR_TRANS_EN  (VIDC_VBIF_BASE_OFFS + 0x1000)
+#define VENUS_VBIF_AT_OLD_BASE    (VIDC_VBIF_BASE_OFFS + 0x1004)
+#define VENUS_VBIF_AT_OLD_HIGH    (VIDC_VBIF_BASE_OFFS + 0x1008)
+#define VENUS_VBIF_AT_NEW_BASE    (VIDC_VBIF_BASE_OFFS + 0x1010)
+#define VENUS_VBIF_AT_NEW_HIGH    (VIDC_VBIF_BASE_OFFS + 0x1018)
+
+
+/* Poll interval in uS */
+#define POLL_INTERVAL_US				50
+
+#define VENUS_REGION_SIZE				0x00500000
+
+static struct {
+	struct msm_vidc_platform_resources *resources;
+	struct regulator *gdsc;
+	const char *reg_name;
+	void __iomem *reg_base;
+	struct device *iommu_ctx_bank_dev;
+	struct dma_iommu_mapping *mapping;
+	dma_addr_t fw_iova;
+	bool is_booted;
+	bool hw_ver_checked;
+	u32 fw_sz;
+	u32 hw_ver_major;
+	u32 hw_ver_minor;
+	void *venus_notif_hdle;
+} *venus_data = NULL;
+
+/* Get venus clocks and set rates for rate-settable clocks */
+static int venus_clock_setup(void)
+{
+	int i, rc = 0;
+	unsigned long rate;
+	struct msm_vidc_platform_resources *res = venus_data->resources;
+	struct clock_info *cl;
+
+	for (i = 0; i < res->clock_set.count; i++) {
+		cl = &res->clock_set.clock_tbl[i];
+		/* Make sure rate-settable clocks' rates are set */
+		if (!clk_get_rate(cl->clk) && cl->count) {
+			rate = clk_round_rate(cl->clk, 0);
+			rc = clk_set_rate(cl->clk, rate);
+			if (rc) {
+				dprintk(VIDC_ERR,
+						"Failed to set clock rate %lu %s: %d\n",
+						rate, cl->name, rc);
+				break;
+			}
+		}
+	}
+
+	return rc;
+}
+
+static int venus_clock_prepare_enable(void)
+{
+	int i, rc = 0;
+	struct msm_vidc_platform_resources *res = venus_data->resources;
+	struct clock_info *cl;
+
+	for (i = 0; i < res->clock_set.count; i++) {
+		cl = &res->clock_set.clock_tbl[i];
+		rc = clk_prepare_enable(cl->clk);
+		if (rc) {
+			dprintk(VIDC_ERR, "failed to enable %s\n", cl->name);
+			for (i--; i >= 0; i--) {
+				cl = &res->clock_set.clock_tbl[i];
+				clk_disable_unprepare(cl->clk);
+			}
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static void venus_clock_disable_unprepare(void)
+{
+	struct msm_vidc_platform_resources *res = venus_data->resources;
+	struct clock_info *cl;
+	int i = res->clock_set.count;
+
+	for (i--; i >= 0; i--) {
+		cl = &res->clock_set.clock_tbl[i];
+		clk_disable_unprepare(cl->clk);
+	}
+}
+
+static int venus_setup_cb(struct device *dev,
+				u32 size)
+{
+	dma_addr_t va_start = 0x0;
+	size_t va_size = size;
+
+	venus_data->mapping = arm_iommu_create_mapping(
+		dev->bus, va_start, va_size);
+	if (IS_ERR_OR_NULL(venus_data->mapping)) {
+		dprintk(VIDC_ERR, "%s: failed to create mapping for %s\n",
+		__func__, dev_name(dev));
+		return -ENODEV;
+	}
+	dprintk(VIDC_DBG,
+		"%s Attached device %pK and created mapping %pK for %s\n",
+		__func__, dev, venus_data->mapping, dev_name(dev));
+	return 0;
+}
+
+static int pil_venus_mem_setup(size_t size)
+{
+	int rc = 0;
+
+	if (!venus_data->mapping) {
+		size = round_up(size, SZ_4K);
+		rc = venus_setup_cb(venus_data->iommu_ctx_bank_dev, size);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: Failed to setup context bank for venus : %s\n",
+				__func__,
+				dev_name(venus_data->iommu_ctx_bank_dev));
+			return rc;
+		}
+		venus_data->fw_sz = size;
+	}
+
+	return rc;
+}
+
+static int pil_venus_auth_and_reset(void)
+{
+	int rc;
+
+	phys_addr_t fw_bias = venus_data->resources->firmware_base;
+	void __iomem *reg_base = venus_data->reg_base;
+	u32 ver;
+	bool iommu_present = is_iommu_present(venus_data->resources);
+	struct device *dev = venus_data->iommu_ctx_bank_dev;
+
+	if (!fw_bias) {
+		dprintk(VIDC_ERR, "FW bias is not valid\n");
+		return -EINVAL;
+	}
+	venus_data->fw_iova = (dma_addr_t)NULL;
+	/* Get Venus version number */
+	if (!venus_data->hw_ver_checked) {
+		ver = readl_relaxed(reg_base + VIDC_WRAPPER_HW_VERSION);
+		venus_data->hw_ver_minor = (ver & 0x0FFF0000) >> 16;
+		venus_data->hw_ver_major = (ver & 0xF0000000) >> 28;
+		venus_data->hw_ver_checked = 1;
+	}
+
+	if (iommu_present) {
+		u32 cpa_start_addr, cpa_end_addr, fw_start_addr, fw_end_addr;
+		/* Get the cpa and fw start/end addr based on Venus version */
+		if (venus_data->hw_ver_major == 0x1 &&
+				venus_data->hw_ver_minor <= 1) {
+			cpa_start_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v1;
+			cpa_end_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v1;
+			fw_start_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v1;
+			fw_end_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v1;
+		} else {
+			cpa_start_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_CPA_START_ADDR_v2;
+			cpa_end_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_CPA_END_ADDR_v2;
+			fw_start_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_FW_START_ADDR_v2;
+			fw_end_addr =
+				VENUS_WRAPPER_VBIF_SS_SEC_FW_END_ADDR_v2;
+		}
+
+		/* Program CPA start and end address */
+		writel_relaxed(0, reg_base + cpa_start_addr);
+		writel_relaxed(venus_data->fw_sz, reg_base + cpa_end_addr);
+
+		/* Program FW start and end address */
+		writel_relaxed(0, reg_base + fw_start_addr);
+		writel_relaxed(venus_data->fw_sz, reg_base + fw_end_addr);
+	} else {
+		rc = regulator_enable(venus_data->gdsc);
+		if (rc) {
+			dprintk(VIDC_ERR, "GDSC enable failed\n");
+			goto err;
+		}
+
+		rc = venus_clock_prepare_enable();
+		if (rc) {
+			dprintk(VIDC_ERR, "Clock prepare and enable failed\n");
+			regulator_disable(venus_data->gdsc);
+			goto err;
+		}
+
+		writel_relaxed(0, reg_base + VENUS_VBIF_AT_OLD_BASE);
+		writel_relaxed(VENUS_REGION_SIZE,
+				reg_base + VENUS_VBIF_AT_OLD_HIGH);
+		writel_relaxed(fw_bias, reg_base + VENUS_VBIF_AT_NEW_BASE);
+		writel_relaxed(fw_bias + VENUS_REGION_SIZE,
+				reg_base + VENUS_VBIF_AT_NEW_HIGH);
+		writel_relaxed(0x7F007F, reg_base + VENUS_VBIF_ADDR_TRANS_EN);
+		venus_clock_disable_unprepare();
+		regulator_disable(venus_data->gdsc);
+	}
+	/* Make sure all register writes are committed. */
+	mb();
+
+	/*
+	 * Need to wait 10 cycles of internal clocks before bringing ARM9
+	 * out of reset.
+	 */
+	udelay(1);
+
+	if (iommu_present) {
+		phys_addr_t pa = fw_bias;
+
+		rc = arm_iommu_attach_device(dev, venus_data->mapping);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"Failed to attach iommu for %s : %d\n",
+				dev_name(dev), rc);
+			goto release_mapping;
+		}
+
+		dprintk(VIDC_DBG, "Attached and created mapping for %s\n",
+				dev_name(dev));
+
+		/* Map virtual addr space 0 - fw_sz to fw phys addr space */
+		rc = iommu_map(venus_data->mapping->domain,
+			venus_data->fw_iova, pa, venus_data->fw_sz,
+			IOMMU_READ|IOMMU_WRITE|IOMMU_PRIV);
+		if (!rc) {
+			dprintk(VIDC_DBG,
+				"%s - Successfully mapped and performed test translation!\n",
+				dev_name(dev));
+		}
+
+		if (rc || (venus_data->fw_iova != 0)) {
+			dprintk(VIDC_ERR, "%s - Failed to setup IOMMU\n",
+					dev_name(dev));
+			goto err_iommu_map;
+		}
+	}
+	/* Bring Arm9 out of reset */
+	writel_relaxed(0, reg_base + VENUS_WRAPPER_SW_RESET);
+
+	venus_data->is_booted = 1;
+	return 0;
+
+err_iommu_map:
+	if (iommu_present)
+		arm_iommu_detach_device(dev);
+release_mapping:
+	if (iommu_present)
+		arm_iommu_release_mapping(venus_data->mapping);
+err:
+	return rc;
+}
+
+static int pil_venus_shutdown(void)
+{
+	void __iomem *reg_base = venus_data->reg_base;
+	u32 reg;
+	int rc;
+
+	if (!venus_data->is_booted)
+		return 0;
+
+	/* Assert the reset to ARM9 */
+	reg = readl_relaxed(reg_base + VENUS_WRAPPER_SW_RESET);
+	reg |= BIT(4);
+	writel_relaxed(reg, reg_base + VENUS_WRAPPER_SW_RESET);
+
+	/* Make sure reset is asserted before the mapping is removed */
+	mb();
+
+	if (is_iommu_present(venus_data->resources)) {
+		iommu_unmap(venus_data->mapping->domain, venus_data->fw_iova,
+			venus_data->fw_sz);
+		arm_iommu_detach_device(venus_data->iommu_ctx_bank_dev);
+	}
+	/*
+	 * Force the VBIF clk to be on to avoid AXI bridge halt ack failure
+	 * for certain Venus version.
+	 */
+	if (venus_data->hw_ver_major == 0x1 &&
+				(venus_data->hw_ver_minor == 0x2 ||
+				venus_data->hw_ver_minor == 0x3)) {
+		reg = readl_relaxed(reg_base + VIDC_VENUS_VBIF_CLK_ON);
+		reg |= VENUS_VBIF_CLKON_FORCE_ON;
+		writel_relaxed(reg, reg_base + VIDC_VENUS_VBIF_CLK_ON);
+	}
+
+	/* Halt AXI and AXI OCMEM VBIF Access */
+	reg = readl_relaxed(reg_base + VENUS_VBIF_AXI_HALT_CTRL0);
+	reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ;
+	writel_relaxed(reg, reg_base + VENUS_VBIF_AXI_HALT_CTRL0);
+
+	/* Request for AXI bus port halt */
+	rc = readl_poll_timeout(reg_base + VENUS_VBIF_AXI_HALT_CTRL1,
+			reg, reg & VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK,
+			POLL_INTERVAL_US,
+			VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US);
+	if (rc)
+		dprintk(VIDC_ERR, "Port halt timeout\n");
+
+	venus_data->is_booted = 0;
+
+	return 0;
+}
+
+static int venus_notifier_cb(struct notifier_block *this, unsigned long code,
+							void *ss_handle)
+{
+	struct notif_data *data = (struct notif_data *)ss_handle;
+	static bool venus_data_set;
+	int ret;
+
+	if (!data->no_auth)
+		return NOTIFY_DONE;
+
+	if (!venus_data_set) {
+		ret = venus_clock_setup();
+		if (ret)
+			return ret;
+
+		ret = of_property_read_string(data->pdev->dev.of_node,
+				"qcom,proxy-reg-names", &venus_data->reg_name);
+		if (ret)
+			return ret;
+
+		venus_data->gdsc = devm_regulator_get(
+				&data->pdev->dev, venus_data->reg_name);
+		if (IS_ERR(venus_data->gdsc)) {
+			dprintk(VIDC_ERR, "Failed to get Venus GDSC\n");
+			return -ENODEV;
+		}
+
+		venus_data_set = true;
+	}
+
+	if (code != SUBSYS_AFTER_POWERUP && code != SUBSYS_AFTER_SHUTDOWN)
+		return NOTIFY_DONE;
+
+	ret = regulator_enable(venus_data->gdsc);
+	if (ret) {
+		dprintk(VIDC_ERR, "GDSC enable failed\n");
+		return ret;
+	}
+
+	ret = venus_clock_prepare_enable();
+	if (ret) {
+		dprintk(VIDC_ERR, "Clock prepare and enable failed\n");
+		goto err_clks;
+	}
+
+	if (code == SUBSYS_AFTER_POWERUP) {
+		if (is_iommu_present(venus_data->resources))
+			pil_venus_mem_setup(VENUS_REGION_SIZE);
+		pil_venus_auth_and_reset();
+	} else if (code == SUBSYS_AFTER_SHUTDOWN)
+		pil_venus_shutdown();
+
+	venus_clock_disable_unprepare();
+	regulator_disable(venus_data->gdsc);
+
+	return NOTIFY_DONE;
+err_clks:
+	regulator_disable(venus_data->gdsc);
+	return ret;
+}
+
+static struct notifier_block venus_notifier = {
+	.notifier_call = venus_notifier_cb,
+};
+
+int venus_boot_init(struct msm_vidc_platform_resources *res,
+			struct context_bank_info *cb)
+{
+	int rc = 0;
+
+	if (!res || !cb) {
+		dprintk(VIDC_ERR, "Invalid platform resource handle\n");
+		return -EINVAL;
+	}
+	venus_data = kzalloc(sizeof(*venus_data), GFP_KERNEL);
+	if (!venus_data)
+		return -ENOMEM;
+
+	venus_data->resources = res;
+	venus_data->iommu_ctx_bank_dev = cb->dev;
+	if (!venus_data->iommu_ctx_bank_dev) {
+		dprintk(VIDC_ERR, "Invalid venus context bank device\n");
+		return -ENODEV;
+	}
+	venus_data->reg_base = ioremap_nocache(res->register_base,
+			(unsigned long)res->register_size);
+	if (!venus_data->reg_base) {
+		dprintk(VIDC_ERR,
+				"could not map reg addr %pa of size %d\n",
+				&res->register_base, res->register_size);
+		rc = -ENOMEM;
+		goto err_ioremap_fail;
+	}
+	venus_data->venus_notif_hdle = subsys_notif_register_notifier("venus",
+							&venus_notifier);
+	if (IS_ERR(venus_data->venus_notif_hdle)) {
+		dprintk(VIDC_ERR, "register event notification failed\n");
+		rc = PTR_ERR(venus_data->venus_notif_hdle);
+		goto err_subsys_notif;
+	}
+
+	return rc;
+
+err_subsys_notif:
+err_ioremap_fail:
+	kfree(venus_data);
+	return rc;
+}
+
+void venus_boot_deinit(void)
+{
+	venus_data->resources = NULL;
+	subsys_notif_unregister_notifier(venus_data->venus_notif_hdle,
+			&venus_notifier);
+	kfree(venus_data);
+}
diff --git a/drivers/media/platform/msm/vidc/venus_boot.h b/drivers/media/platform/msm/vidc/venus_boot.h
new file mode 100644
index 0000000..da4e772
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/venus_boot.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VENUS_BOOT_H__
+#define __VENUS_BOOT_H__
+#include "msm_vidc_resources.h"
+
+int venus_boot_init(struct msm_vidc_platform_resources *res,
+		struct context_bank_info *cb);
+void venus_boot_deinit(void);
+
+#endif /* __VENUS_BOOT_H__ */
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.c b/drivers/media/platform/msm/vidc/venus_hfi.c
new file mode 100644
index 0000000..83846d3
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/venus_hfi.c
@@ -0,0 +1,4749 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <asm/dma-iommu.h>
+#include <asm/memory.h>
+#include <linux/clk/qcom.h>
+#include <linux/coresight-stm.h>
+#include <linux/delay.h>
+#include <linux/devfreq.h>
+#include <linux/hash.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/pm_qos.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/subsystem_restart.h>
+#include "hfi_packetization.h"
+#include "msm_vidc_debug.h"
+#include "venus_hfi.h"
+#include "vidc_hfi_io.h"
+
+#define FIRMWARE_SIZE			0X00A00000
+#define REG_ADDR_OFFSET_BITMASK	0x000FFFFF
+#define QDSS_IOVA_START 0x80001000
+
+static struct hal_device_data hal_ctxt;
+
+#define TZBSP_MEM_PROTECT_VIDEO_VAR 0x8
+struct tzbsp_memprot {
+	u32 cp_start;
+	u32 cp_size;
+	u32 cp_nonpixel_start;
+	u32 cp_nonpixel_size;
+};
+
+struct tzbsp_resp {
+	int ret;
+};
+
+#define TZBSP_VIDEO_SET_STATE 0xa
+
+/* Poll interval in uS */
+#define POLL_INTERVAL_US 50
+
+enum tzbsp_video_state {
+	TZBSP_VIDEO_STATE_SUSPEND = 0,
+	TZBSP_VIDEO_STATE_RESUME = 1,
+	TZBSP_VIDEO_STATE_RESTORE_THRESHOLD = 2,
+};
+
+struct tzbsp_video_set_state_req {
+	u32 state; /* should be tzbsp_video_state enum value */
+	u32 spare; /* reserved for future, should be zero */
+};
+
+const struct msm_vidc_gov_data DEFAULT_BUS_VOTE = {
+	.data = NULL,
+	.data_count = 0,
+	.imem_size = 0,
+};
+
+const int max_packets = 1000;
+
+static void venus_hfi_pm_handler(struct work_struct *work);
+static DECLARE_DELAYED_WORK(venus_hfi_pm_work, venus_hfi_pm_handler);
+static inline int __resume(struct venus_hfi_device *device);
+static inline int __suspend(struct venus_hfi_device *device);
+static int __disable_regulators(struct venus_hfi_device *device);
+static int __enable_regulators(struct venus_hfi_device *device);
+static inline int __prepare_enable_clks(struct venus_hfi_device *device);
+static inline void __disable_unprepare_clks(struct venus_hfi_device *device);
+static int __scale_clocks_load(struct venus_hfi_device *device, int load,
+		struct vidc_clk_scale_data *data,
+		unsigned long instant_bitrate);
+static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet);
+static int __initialize_packetization(struct venus_hfi_device *device);
+static struct hal_session *__get_session(struct venus_hfi_device *device,
+		u32 session_id);
+static int __iface_cmdq_write(struct venus_hfi_device *device,
+					void *pkt);
+static int __load_fw(struct venus_hfi_device *device);
+static void __unload_fw(struct venus_hfi_device *device);
+static int __tzbsp_set_video_state(enum tzbsp_video_state state);
+
+
+/**
+ * Utility function to enforce some of our assumptions.  Spam calls to this
+ * in hotspots in code to double check some of the assumptions that we hold.
+ */
+static inline void __strict_check(struct venus_hfi_device *device)
+{
+	WARN_ON(!mutex_is_locked(&device->lock));
+}
+
+static inline void __set_state(struct venus_hfi_device *device,
+		enum venus_hfi_state state)
+{
+	device->state = state;
+}
+
+static inline bool __core_in_valid_state(struct venus_hfi_device *device)
+{
+	return device->state != VENUS_STATE_DEINIT;
+}
+
+static void __dump_packet(u8 *packet, enum vidc_msg_prio log_level)
+{
+	u32 c = 0, packet_size = *(u32 *)packet;
+	const int row_size = 32;
+	/*
+	 * row must contain enough for 0xdeadbaad * 8 to be converted into
+	 * "de ad ba ab " * 8 + '\0'
+	 */
+	char row[3 * row_size];
+
+	for (c = 0; c * row_size < packet_size; ++c) {
+		int bytes_to_read = ((c + 1) * row_size > packet_size) ?
+			packet_size % row_size : row_size;
+		hex_dump_to_buffer(packet + c * row_size, bytes_to_read,
+				row_size, 4, row, sizeof(row), false);
+		dprintk(log_level, "%s\n", row);
+	}
+}
+
+static void __sim_modify_cmd_packet(u8 *packet, struct venus_hfi_device *device)
+{
+	struct hfi_cmd_sys_session_init_packet *sys_init;
+	struct hal_session *session = NULL;
+	u8 i;
+	phys_addr_t fw_bias = 0;
+
+	if (!device || !packet) {
+		dprintk(VIDC_ERR, "Invalid Param\n");
+		return;
+	} else if (!device->hal_data->firmware_base
+			|| is_iommu_present(device->res)) {
+		return;
+	}
+
+	fw_bias = device->hal_data->firmware_base;
+	sys_init = (struct hfi_cmd_sys_session_init_packet *)packet;
+
+	session = __get_session(device, sys_init->session_id);
+	if (!session) {
+		dprintk(VIDC_DBG, "%s :Invalid session id: %x\n",
+				__func__, sys_init->session_id);
+		return;
+	}
+
+	switch (sys_init->packet_type) {
+	case HFI_CMD_SESSION_EMPTY_BUFFER:
+		if (session->is_decoder) {
+			struct hfi_cmd_session_empty_buffer_compressed_packet
+			*pkt = (struct
+			hfi_cmd_session_empty_buffer_compressed_packet
+			*) packet;
+			pkt->packet_buffer -= fw_bias;
+		} else {
+			struct
+			hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+			*pkt = (struct
+			hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+			*) packet;
+			pkt->packet_buffer -= fw_bias;
+		}
+		break;
+	case HFI_CMD_SESSION_FILL_BUFFER:
+	{
+		struct hfi_cmd_session_fill_buffer_packet *pkt =
+			(struct hfi_cmd_session_fill_buffer_packet *)packet;
+		pkt->packet_buffer -= fw_bias;
+		break;
+	}
+	case HFI_CMD_SESSION_SET_BUFFERS:
+	{
+		struct hfi_cmd_session_set_buffers_packet *pkt =
+			(struct hfi_cmd_session_set_buffers_packet *)packet;
+		if (pkt->buffer_type == HFI_BUFFER_OUTPUT ||
+			pkt->buffer_type == HFI_BUFFER_OUTPUT2) {
+			struct hfi_buffer_info *buff;
+
+			buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+			buff->buffer_addr -= fw_bias;
+			if (buff->extra_data_addr >= fw_bias)
+				buff->extra_data_addr -= fw_bias;
+		} else {
+			for (i = 0; i < pkt->num_buffers; i++)
+				pkt->rg_buffer_info[i] -= fw_bias;
+		}
+		break;
+	}
+	case HFI_CMD_SESSION_RELEASE_BUFFERS:
+	{
+		struct hfi_cmd_session_release_buffer_packet *pkt =
+			(struct hfi_cmd_session_release_buffer_packet *)packet;
+
+		if (pkt->buffer_type == HFI_BUFFER_OUTPUT ||
+			pkt->buffer_type == HFI_BUFFER_OUTPUT2) {
+			struct hfi_buffer_info *buff;
+
+			buff = (struct hfi_buffer_info *) pkt->rg_buffer_info;
+			buff->buffer_addr -= fw_bias;
+			buff->extra_data_addr -= fw_bias;
+		} else {
+			for (i = 0; i < pkt->num_buffers; i++)
+				pkt->rg_buffer_info[i] -= fw_bias;
+		}
+		break;
+	}
+	case HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER:
+	{
+		struct hfi_cmd_session_parse_sequence_header_packet *pkt =
+			(struct hfi_cmd_session_parse_sequence_header_packet *)
+		packet;
+		pkt->packet_buffer -= fw_bias;
+		break;
+	}
+	case HFI_CMD_SESSION_GET_SEQUENCE_HEADER:
+	{
+		struct hfi_cmd_session_get_sequence_header_packet *pkt =
+			(struct hfi_cmd_session_get_sequence_header_packet *)
+		packet;
+		pkt->packet_buffer -= fw_bias;
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static int __acquire_regulator(struct regulator_info *rinfo)
+{
+	int rc = 0;
+
+	if (rinfo->has_hw_power_collapse) {
+		rc = regulator_set_mode(rinfo->regulator,
+				REGULATOR_MODE_NORMAL);
+		if (rc) {
+			/*
+			 * This is somewhat fatal, but nothing we can do
+			 * about it. We can't disable the regulator w/o
+			 * getting it back under s/w control
+			 */
+			dprintk(VIDC_WARN,
+				"Failed to acquire regulator control: %s\n",
+					rinfo->name);
+		} else {
+
+			dprintk(VIDC_DBG,
+					"Acquire regulator control from HW: %s\n",
+					rinfo->name);
+
+		}
+	}
+
+	if (!regulator_is_enabled(rinfo->regulator)) {
+		dprintk(VIDC_WARN, "Regulator is not enabled %s\n",
+			rinfo->name);
+		WARN_ON(1);
+	}
+
+	return rc;
+}
+
+static int __hand_off_regulator(struct regulator_info *rinfo)
+{
+	int rc = 0;
+
+	if (rinfo->has_hw_power_collapse) {
+		rc = regulator_set_mode(rinfo->regulator,
+				REGULATOR_MODE_FAST);
+		if (rc) {
+			dprintk(VIDC_WARN,
+				"Failed to hand off regulator control: %s\n",
+					rinfo->name);
+		} else {
+			dprintk(VIDC_DBG,
+					"Hand off regulator control to HW: %s\n",
+					rinfo->name);
+		}
+	}
+
+	return rc;
+}
+
+static int __hand_off_regulators(struct venus_hfi_device *device)
+{
+	struct regulator_info *rinfo;
+	int rc = 0, c = 0;
+
+	venus_hfi_for_each_regulator(device, rinfo) {
+		rc = __hand_off_regulator(rinfo);
+		/*
+		 * If one regulator hand off failed, driver should take
+		 * the control for other regulators back.
+		 */
+		if (rc)
+			goto err_reg_handoff_failed;
+		c++;
+	}
+
+	return rc;
+err_reg_handoff_failed:
+	venus_hfi_for_each_regulator_reverse_continue(device, rinfo, c)
+		__acquire_regulator(rinfo);
+
+	return rc;
+}
+
+static int __write_queue(struct vidc_iface_q_info *qinfo, u8 *packet,
+		bool *rx_req_is_set)
+{
+	struct hfi_queue_header *queue;
+	u32 packet_size_in_words, new_write_idx;
+	u32 empty_space, read_idx;
+	u32 *write_ptr;
+
+	if (!qinfo || !packet) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	} else if (!qinfo->q_array.align_virtual_addr) {
+		dprintk(VIDC_WARN, "Queues have already been freed\n");
+		return -EINVAL;
+	}
+
+	queue = (struct hfi_queue_header *) qinfo->q_hdr;
+	if (!queue) {
+		dprintk(VIDC_ERR, "queue not present\n");
+		return -ENOENT;
+	}
+
+	if (msm_vidc_debug & VIDC_PKT) {
+		dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo);
+		__dump_packet(packet, VIDC_PKT);
+	}
+
+	packet_size_in_words = (*(u32 *)packet) >> 2;
+	if (!packet_size_in_words) {
+		dprintk(VIDC_ERR, "Zero packet size\n");
+		return -ENODATA;
+	}
+
+	read_idx = queue->qhdr_read_idx;
+
+	empty_space = (queue->qhdr_write_idx >=  read_idx) ?
+		(queue->qhdr_q_size - (queue->qhdr_write_idx -  read_idx)) :
+		(read_idx - queue->qhdr_write_idx);
+	if (empty_space <= packet_size_in_words) {
+		queue->qhdr_tx_req =  1;
+		dprintk(VIDC_ERR, "Insufficient size (%d) to write (%d)\n",
+					  empty_space, packet_size_in_words);
+		return -ENOTEMPTY;
+	}
+
+	queue->qhdr_tx_req =  0;
+
+	new_write_idx = (queue->qhdr_write_idx + packet_size_in_words);
+	write_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) +
+		(queue->qhdr_write_idx << 2));
+	if (new_write_idx < queue->qhdr_q_size) {
+		memcpy(write_ptr, packet, packet_size_in_words << 2);
+	} else {
+		new_write_idx -= queue->qhdr_q_size;
+		memcpy(write_ptr, packet, (packet_size_in_words -
+			new_write_idx) << 2);
+		memcpy((void *)qinfo->q_array.align_virtual_addr,
+			packet + ((packet_size_in_words - new_write_idx) << 2),
+			new_write_idx  << 2);
+	}
+
+	/*
+	 * Memory barrier to make sure packet is written before updating the
+	 * write index
+	 */
+	mb();
+	queue->qhdr_write_idx = new_write_idx;
+	if (rx_req_is_set)
+		*rx_req_is_set = queue->qhdr_rx_req == 1;
+	/*
+	 * Memory barrier to make sure write index is updated before an
+	 * interrupt is raised on venus.
+	 */
+	mb();
+	return 0;
+}
+
+static void __hal_sim_modify_msg_packet(u8 *packet,
+					struct venus_hfi_device *device)
+{
+	struct hfi_msg_sys_session_init_done_packet *sys_idle;
+	struct hal_session *session = NULL;
+	phys_addr_t fw_bias = 0;
+
+	if (!device || !packet) {
+		dprintk(VIDC_ERR, "Invalid Param\n");
+		return;
+	} else if (!device->hal_data->firmware_base
+			|| is_iommu_present(device->res)) {
+		return;
+	}
+
+	fw_bias = device->hal_data->firmware_base;
+	sys_idle = (struct hfi_msg_sys_session_init_done_packet *)packet;
+	session = __get_session(device, sys_idle->session_id);
+
+	if (!session) {
+		dprintk(VIDC_DBG, "%s: Invalid session id: %x\n",
+				__func__, sys_idle->session_id);
+		return;
+	}
+
+	switch (sys_idle->packet_type) {
+	case HFI_MSG_SESSION_FILL_BUFFER_DONE:
+		if (session->is_decoder) {
+			struct
+			hfi_msg_session_fbd_uncompressed_plane0_packet
+			*pkt_uc = (struct
+			hfi_msg_session_fbd_uncompressed_plane0_packet
+			*) packet;
+			pkt_uc->packet_buffer += fw_bias;
+		} else {
+			struct
+			hfi_msg_session_fill_buffer_done_compressed_packet
+			*pkt = (struct
+			hfi_msg_session_fill_buffer_done_compressed_packet
+			*) packet;
+			pkt->packet_buffer += fw_bias;
+		}
+		break;
+	case HFI_MSG_SESSION_EMPTY_BUFFER_DONE:
+	{
+		struct hfi_msg_session_empty_buffer_done_packet *pkt =
+		(struct hfi_msg_session_empty_buffer_done_packet *)packet;
+		pkt->packet_buffer += fw_bias;
+		break;
+	}
+	case HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE:
+	{
+		struct
+		hfi_msg_session_get_sequence_header_done_packet
+		*pkt =
+		(struct hfi_msg_session_get_sequence_header_done_packet *)
+		packet;
+		pkt->sequence_header += fw_bias;
+		break;
+	}
+	default:
+		break;
+	}
+}
+
+static int __read_queue(struct vidc_iface_q_info *qinfo, u8 *packet,
+		u32 *pb_tx_req_is_set)
+{
+	struct hfi_queue_header *queue;
+	u32 packet_size_in_words, new_read_idx;
+	u32 *read_ptr;
+	u32 receive_request = 0;
+		int rc = 0;
+
+	if (!qinfo || !packet || !pb_tx_req_is_set) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	} else if (!qinfo->q_array.align_virtual_addr) {
+		dprintk(VIDC_WARN, "Queues have already been freed\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * Memory barrier to make sure data is valid before
+	 *reading it
+	 */
+	mb();
+	queue = (struct hfi_queue_header *) qinfo->q_hdr;
+
+	if (!queue) {
+		dprintk(VIDC_ERR, "Queue memory is not allocated\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Do not set receive request for debug queue, if set,
+	 * Venus generates interrupt for debug messages even
+	 * when there is no response message available.
+	 * In general debug queue will not become full as it
+	 * is being emptied out for every interrupt from Venus.
+	 * Venus will anyway generates interrupt if it is full.
+	 */
+	if (queue->qhdr_type & HFI_Q_ID_CTRL_TO_HOST_MSG_Q)
+		receive_request = 1;
+
+	if (queue->qhdr_read_idx == queue->qhdr_write_idx) {
+		queue->qhdr_rx_req = receive_request;
+		*pb_tx_req_is_set = 0;
+		dprintk(VIDC_DBG,
+			"%s queue is empty, rx_req = %u, tx_req = %u, read_idx = %u\n",
+			receive_request ? "message" : "debug",
+			queue->qhdr_rx_req, queue->qhdr_tx_req,
+			queue->qhdr_read_idx);
+		return -ENODATA;
+	}
+
+	read_ptr = (u32 *)((qinfo->q_array.align_virtual_addr) +
+				(queue->qhdr_read_idx << 2));
+	packet_size_in_words = (*read_ptr) >> 2;
+	if (!packet_size_in_words) {
+		dprintk(VIDC_ERR, "Zero packet size\n");
+		return -ENODATA;
+	}
+
+	new_read_idx = queue->qhdr_read_idx + packet_size_in_words;
+	if (((packet_size_in_words << 2) <= VIDC_IFACEQ_VAR_HUGE_PKT_SIZE)
+			&& queue->qhdr_read_idx <= queue->qhdr_q_size) {
+		if (new_read_idx < queue->qhdr_q_size) {
+			memcpy(packet, read_ptr,
+					packet_size_in_words << 2);
+		} else {
+			new_read_idx -= queue->qhdr_q_size;
+			memcpy(packet, read_ptr,
+			(packet_size_in_words - new_read_idx) << 2);
+			memcpy(packet + ((packet_size_in_words -
+					new_read_idx) << 2),
+					(u8 *)qinfo->q_array.align_virtual_addr,
+					new_read_idx << 2);
+		}
+	} else {
+		dprintk(VIDC_WARN,
+			"BAD packet received, read_idx: %#x, pkt_size: %d\n",
+			queue->qhdr_read_idx, packet_size_in_words << 2);
+		dprintk(VIDC_WARN, "Dropping this packet\n");
+		new_read_idx = queue->qhdr_write_idx;
+		rc = -ENODATA;
+	}
+
+	queue->qhdr_read_idx = new_read_idx;
+
+	if (queue->qhdr_read_idx != queue->qhdr_write_idx)
+		queue->qhdr_rx_req = 0;
+	else
+		queue->qhdr_rx_req = receive_request;
+
+	*pb_tx_req_is_set = (queue->qhdr_tx_req == 1) ? 1 : 0;
+
+	if (msm_vidc_debug & VIDC_PKT) {
+		dprintk(VIDC_PKT, "%s: %pK\n", __func__, qinfo);
+		__dump_packet(packet, VIDC_PKT);
+	}
+
+	return rc;
+}
+
+static int __smem_alloc(struct venus_hfi_device *dev,
+			struct vidc_mem_addr *mem, u32 size, u32 align,
+			u32 flags, u32 usage)
+{
+	struct msm_smem *alloc = NULL;
+	int rc = 0;
+
+	if (!dev || !dev->hal_client || !mem || !size) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	dprintk(VIDC_INFO, "start to alloc size: %d, flags: %d\n", size, flags);
+	alloc = msm_smem_alloc(dev->hal_client, size, align, flags, usage, 1);
+	if (!alloc) {
+		dprintk(VIDC_ERR, "Alloc failed\n");
+		rc = -ENOMEM;
+		goto fail_smem_alloc;
+	}
+
+	dprintk(VIDC_DBG, "__smem_alloc: ptr = %pK, size = %d\n",
+			alloc->kvaddr, size);
+	rc = msm_smem_cache_operations(dev->hal_client, alloc,
+		SMEM_CACHE_CLEAN);
+	if (rc) {
+		dprintk(VIDC_WARN, "Failed to clean cache\n");
+		dprintk(VIDC_WARN, "This may result in undefined behavior\n");
+	}
+
+	mem->mem_size = alloc->size;
+	mem->mem_data = alloc;
+	mem->align_virtual_addr = alloc->kvaddr;
+	mem->align_device_addr = alloc->device_addr;
+	return rc;
+fail_smem_alloc:
+	return rc;
+}
+
+static void __smem_free(struct venus_hfi_device *dev, struct msm_smem *mem)
+{
+	if (!dev || !mem) {
+		dprintk(VIDC_ERR, "invalid param %pK %pK\n", dev, mem);
+		return;
+	}
+
+	msm_smem_free(dev->hal_client, mem);
+}
+
+static void __write_register(struct venus_hfi_device *device,
+		u32 reg, u32 value)
+{
+	u32 hwiosymaddr = reg;
+	u8 *base_addr;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return;
+	}
+
+	__strict_check(device);
+
+	if (!device->power_enabled) {
+		dprintk(VIDC_WARN,
+			"HFI Write register failed : Power is OFF\n");
+		WARN_ON(1);
+		return;
+	}
+
+	base_addr = device->hal_data->register_base;
+	dprintk(VIDC_DBG, "Base addr: %pK, written to: %#x, Value: %#x...\n",
+		base_addr, hwiosymaddr, value);
+	base_addr += hwiosymaddr;
+	writel_relaxed(value, base_addr);
+
+	/*
+	 * Memory barrier to make sure value is written into the register.
+	 */
+	wmb();
+}
+
+static int __read_register(struct venus_hfi_device *device, u32 reg)
+{
+	int rc = 0;
+	u8 *base_addr;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return -EINVAL;
+	}
+
+	__strict_check(device);
+
+	if (!device->power_enabled) {
+		dprintk(VIDC_WARN,
+			"HFI Read register failed : Power is OFF\n");
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	base_addr = device->hal_data->register_base;
+
+	rc = readl_relaxed(base_addr + reg);
+	/*
+	 * Memory barrier to make sure value is read correctly from the
+	 * register.
+	 */
+	rmb();
+	dprintk(VIDC_DBG, "Base addr: %pK, read from: %#x, value: %#x...\n",
+		base_addr, reg, rc);
+
+	return rc;
+}
+
+static void __set_registers(struct venus_hfi_device *device)
+{
+	struct reg_set *reg_set;
+	int i;
+
+	if (!device->res) {
+		dprintk(VIDC_ERR,
+			"device resources null, cannot set registers\n");
+		return;
+	}
+
+	reg_set = &device->res->reg_set;
+	for (i = 0; i < reg_set->count; i++) {
+		__write_register(device, reg_set->reg_tbl[i].reg,
+				reg_set->reg_tbl[i].value);
+	}
+}
+
+/*
+ * The existence of this function is a hack for 8996 (or certain Venus versions)
+ * to overcome a hardware bug.  Whenever the GDSCs momentarily power collapse
+ * (after calling __hand_off_regulators()), the values of the threshold
+ * registers (typically programmed by TZ) are incorrectly reset.  As a result
+ * reprogram these registers at certain agreed upon points.
+ */
+static void __set_threshold_registers(struct venus_hfi_device *device)
+{
+	u32 version = __read_register(device, VIDC_WRAPPER_HW_VERSION);
+
+	version &= ~GENMASK(15, 0);
+	if (version != (0x3 << 28 | 0x43 << 16))
+		return;
+
+	if (__tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESTORE_THRESHOLD))
+		dprintk(VIDC_ERR, "Failed to restore threshold values\n");
+}
+
+static void __iommu_detach(struct venus_hfi_device *device)
+{
+	struct context_bank_info *cb;
+
+	if (!device || !device->res) {
+		dprintk(VIDC_ERR, "Invalid parameter: %pK\n", device);
+		return;
+	}
+
+	list_for_each_entry(cb, &device->res->context_banks, list) {
+		if (cb->dev)
+			arm_iommu_detach_device(cb->dev);
+		if (cb->mapping)
+			arm_iommu_release_mapping(cb->mapping);
+	}
+}
+
+static bool __is_session_supported(unsigned long sessions_supported,
+		enum vidc_vote_data_session session_type)
+{
+	bool same_codec, same_session_type;
+	int codec_bit, session_type_bit;
+	unsigned long session = session_type;
+
+	if (!sessions_supported || !session)
+		return false;
+
+	/* ffs returns a 1 indexed, test_bit takes a 0 indexed...index */
+	codec_bit = ffs(session) - 1;
+	session_type_bit = codec_bit + 1;
+
+	same_codec = test_bit(codec_bit, &sessions_supported) ==
+		test_bit(codec_bit, &session);
+	same_session_type = test_bit(session_type_bit, &sessions_supported) ==
+		test_bit(session_type_bit, &session);
+
+	return same_codec && same_session_type;
+}
+
+bool venus_hfi_is_session_supported(unsigned long sessions_supported,
+		enum vidc_vote_data_session session_type)
+{
+	return __is_session_supported(sessions_supported, session_type);
+}
+EXPORT_SYMBOL(venus_hfi_is_session_supported);
+
+static int __devfreq_target(struct device *devfreq_dev,
+		unsigned long *freq, u32 flags)
+{
+	int rc = 0;
+	uint64_t ab = 0;
+	struct bus_info *bus = NULL, *temp = NULL;
+	struct venus_hfi_device *device = dev_get_drvdata(devfreq_dev);
+
+	venus_hfi_for_each_bus(device, temp) {
+		if (temp->dev == devfreq_dev) {
+			bus = temp;
+			break;
+		}
+	}
+
+	if (!bus) {
+		rc = -EBADHANDLE;
+		goto err_unknown_device;
+	}
+
+	/*
+	 * Clamp for all non zero frequencies. This clamp is necessary to stop
+	 * devfreq driver from spamming - Couldn't update frequency - logs, if
+	 * the scaled ab value is not part of the frequency table.
+	 */
+	if (*freq)
+		*freq = clamp_t(typeof(*freq), *freq, bus->range[0],
+				bus->range[1]);
+
+	/* we expect governors to provide values in kBps form, convert to Bps */
+	ab = *freq * 1000;
+	rc = msm_bus_scale_update_bw(bus->client, ab, 0);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed voting bus %s to ab %llu\n: %d",
+				bus->name, ab, rc);
+		goto err_unknown_device;
+	}
+
+	dprintk(VIDC_PROF, "Voting bus %s to ab %llu\n", bus->name, ab);
+
+	return 0;
+err_unknown_device:
+	return rc;
+}
+
+static int __devfreq_get_status(struct device *devfreq_dev,
+		struct devfreq_dev_status *stat)
+{
+	int rc = 0;
+	struct bus_info *bus = NULL, *temp = NULL;
+	struct venus_hfi_device *device = dev_get_drvdata(devfreq_dev);
+
+	venus_hfi_for_each_bus(device, temp) {
+		if (temp->dev == devfreq_dev) {
+			bus = temp;
+			break;
+		}
+	}
+
+	if (!bus) {
+		rc = -EBADHANDLE;
+		goto err_unknown_device;
+	}
+
+	*stat = (struct devfreq_dev_status) {
+		.private_data = &device->bus_vote,
+		/*
+		 * Put in dummy place holder values for upstream govs, our
+		 * custom gov only needs .private_data.  We should fill this in
+		 * properly if we can actually measure busy_time accurately
+		 * (which we can't at the moment)
+		 */
+		.total_time = 1,
+		.busy_time = 1,
+		.current_frequency = 0,
+	};
+
+err_unknown_device:
+	return rc;
+}
+
+static int __unvote_buses(struct venus_hfi_device *device)
+{
+	int rc = 0;
+	struct bus_info *bus = NULL;
+
+	venus_hfi_for_each_bus(device, bus) {
+		int local_rc = 0;
+		unsigned long zero = 0;
+
+		rc = devfreq_suspend_device(bus->devfreq);
+		if (rc)
+			goto err_unknown_device;
+
+		local_rc = __devfreq_target(bus->dev, &zero, 0);
+		rc = rc ?: local_rc;
+	}
+
+	if (rc)
+		dprintk(VIDC_WARN, "Failed to unvote some buses\n");
+
+err_unknown_device:
+	return rc;
+}
+
+static int __vote_buses(struct venus_hfi_device *device,
+		struct vidc_bus_vote_data *data, int num_data)
+{
+	int rc = 0;
+	struct bus_info *bus = NULL;
+	struct vidc_bus_vote_data *new_data = NULL;
+
+	if (!num_data) {
+		dprintk(VIDC_DBG, "No vote data available\n");
+		goto no_data_count;
+	} else if (!data) {
+		dprintk(VIDC_ERR, "Invalid voting data\n");
+		return -EINVAL;
+	}
+
+	new_data = kmemdup(data, num_data * sizeof(*new_data), GFP_KERNEL);
+	if (!new_data) {
+		dprintk(VIDC_ERR, "Can't alloc memory to cache bus votes\n");
+		rc = -ENOMEM;
+		goto err_no_mem;
+	}
+
+no_data_count:
+	kfree(device->bus_vote.data);
+	device->bus_vote.data = new_data;
+	device->bus_vote.data_count = num_data;
+	device->bus_vote.imem_size = device->res->imem_size;
+
+	venus_hfi_for_each_bus(device, bus) {
+		if (bus && bus->devfreq) {
+			/* NOP if already resume */
+			rc = devfreq_resume_device(bus->devfreq);
+			if (rc)
+				goto err_no_mem;
+
+			/* Kick devfreq awake incase _resume() didn't do it */
+			bus->devfreq->nb.notifier_call(
+				&bus->devfreq->nb, 0, NULL);
+		}
+	}
+
+err_no_mem:
+	return rc;
+}
+
+static int venus_hfi_vote_buses(void *dev, struct vidc_bus_vote_data *d, int n)
+{
+	int rc = 0;
+	struct venus_hfi_device *device = dev;
+
+	if (!device)
+		return -EINVAL;
+
+	mutex_lock(&device->lock);
+	rc = __vote_buses(device, d, n);
+	mutex_unlock(&device->lock);
+
+	return rc;
+
+}
+static int __core_set_resource(struct venus_hfi_device *device,
+		struct vidc_resource_hdr *resource_hdr, void *resource_value)
+{
+	struct hfi_cmd_sys_set_resource_packet *pkt;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	int rc = 0;
+
+	if (!device || !resource_hdr || !resource_value) {
+		dprintk(VIDC_ERR, "set_res: Invalid Params\n");
+		return -EINVAL;
+	}
+
+	pkt = (struct hfi_cmd_sys_set_resource_packet *) packet;
+
+	rc = call_hfi_pkt_op(device, sys_set_resource,
+			pkt, resource_hdr, resource_value);
+	if (rc) {
+		dprintk(VIDC_ERR, "set_res: failed to create packet\n");
+		goto err_create_pkt;
+	}
+
+	rc = __iface_cmdq_write(device, pkt);
+	if (rc)
+		rc = -ENOTEMPTY;
+
+err_create_pkt:
+	return rc;
+}
+
+static int __alloc_imem(struct venus_hfi_device *device, unsigned long size)
+{
+	struct imem *imem = NULL;
+	int rc = 0;
+
+	if (!device)
+		return -EINVAL;
+
+	imem = &device->resources.imem;
+	if (imem->type) {
+		dprintk(VIDC_ERR, "IMEM of type %d already allocated\n",
+				imem->type);
+		return -ENOMEM;
+	}
+
+	switch (device->res->imem_type) {
+	case IMEM_VMEM:
+	{
+		phys_addr_t vmem_buffer = 0;
+
+		rc = vmem_allocate(size, &vmem_buffer);
+		if (rc) {
+			if (rc == -ENOTSUPP) {
+				dprintk(VIDC_DBG,
+					"Target does not support vmem\n");
+				rc = 0;
+			}
+			goto imem_alloc_failed;
+		} else if (!vmem_buffer) {
+			rc = -ENOMEM;
+			goto imem_alloc_failed;
+		}
+
+		imem->vmem = vmem_buffer;
+		break;
+	}
+	case IMEM_NONE:
+		rc = 0;
+		break;
+
+	default:
+		rc = -ENOTSUPP;
+		goto imem_alloc_failed;
+	}
+
+	imem->type = device->res->imem_type;
+	dprintk(VIDC_DBG, "Allocated %ld bytes of IMEM of type %d\n", size,
+			imem->type);
+	return 0;
+imem_alloc_failed:
+	imem->type = IMEM_NONE;
+	return rc;
+}
+
+static int __free_imem(struct venus_hfi_device *device)
+{
+	struct imem *imem = NULL;
+	int rc = 0;
+
+	if (!device)
+		return -EINVAL;
+
+	imem = &device->resources.imem;
+	switch (imem->type) {
+	case IMEM_NONE:
+		/* Follow the semantics of free(NULL), which is a no-op. */
+		break;
+	case IMEM_VMEM:
+		vmem_free(imem->vmem);
+		break;
+	default:
+		rc = -ENOTSUPP;
+		goto imem_free_failed;
+	}
+
+	imem->type = IMEM_NONE;
+	return 0;
+
+imem_free_failed:
+	return rc;
+}
+
+static int __set_imem(struct venus_hfi_device *device, struct imem *imem)
+{
+	struct vidc_resource_hdr rhdr;
+	phys_addr_t addr = 0;
+	int rc = 0;
+
+	if (!device || !device->res || !imem) {
+		dprintk(VIDC_ERR, "Invalid params, core: %pK, imem: %pK\n",
+			device, imem);
+		return -EINVAL;
+	}
+
+	rhdr.resource_handle = imem; /* cookie */
+	rhdr.size = device->res->imem_size;
+	rhdr.resource_id = VIDC_RESOURCE_NONE;
+
+	switch (imem->type) {
+	case IMEM_VMEM:
+		rhdr.resource_id = VIDC_RESOURCE_VMEM;
+		addr = imem->vmem;
+		break;
+	case IMEM_NONE:
+		dprintk(VIDC_DBG, "%s Target does not support IMEM", __func__);
+		rc = 0;
+		goto imem_set_failed;
+	default:
+		dprintk(VIDC_ERR, "IMEM of type %d unsupported\n", imem->type);
+		rc = -ENOTSUPP;
+		goto imem_set_failed;
+	}
+
+	MSM_VIDC_ERROR(!addr);
+
+	rc = __core_set_resource(device, &rhdr, (void *)addr);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to set IMEM on driver\n");
+		goto imem_set_failed;
+	}
+
+	dprintk(VIDC_DBG,
+			"Managed to set IMEM buffer of type %d sized %d bytes at %pa\n",
+			rhdr.resource_id, rhdr.size, &addr);
+
+	rc = __vote_buses(device, device->bus_vote.data,
+			device->bus_vote.data_count);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Failed to vote for buses after setting imem: %d\n",
+				rc);
+	}
+
+imem_set_failed:
+	return rc;
+}
+
+static int __tzbsp_set_video_state(enum tzbsp_video_state state)
+{
+	struct tzbsp_video_set_state_req cmd = {0};
+	int tzbsp_rsp = 0;
+	int rc = 0;
+	struct scm_desc desc = {0};
+
+	desc.args[0] = cmd.state = state;
+	desc.args[1] = cmd.spare = 0;
+	desc.arginfo = SCM_ARGS(2);
+
+	if (!is_scm_armv8()) {
+		rc = scm_call(SCM_SVC_BOOT, TZBSP_VIDEO_SET_STATE, &cmd,
+				sizeof(cmd), &tzbsp_rsp, sizeof(tzbsp_rsp));
+	} else {
+		rc = scm_call2(SCM_SIP_FNID(SCM_SVC_BOOT,
+				TZBSP_VIDEO_SET_STATE), &desc);
+		tzbsp_rsp = desc.ret[0];
+	}
+
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed scm_call %d\n", rc);
+		return rc;
+	}
+
+	dprintk(VIDC_DBG, "Set state %d, resp %d\n", state, tzbsp_rsp);
+	if (tzbsp_rsp) {
+		dprintk(VIDC_ERR,
+				"Failed to set video core state to suspend: %d\n",
+				tzbsp_rsp);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int __boot_firmware(struct venus_hfi_device *device)
+{
+	int rc = 0;
+	u32 ctrl_status = 0, count = 0, max_tries = 100;
+
+	__write_register(device, VIDC_CTRL_INIT, 0x1);
+	while (!ctrl_status && count < max_tries) {
+		ctrl_status = __read_register(device, VIDC_CPU_CS_SCIACMDARG0);
+		if ((ctrl_status & 0xFE) == 0x4) {
+			dprintk(VIDC_ERR, "invalid setting for UC_REGION\n");
+			break;
+		}
+
+		usleep_range(500, 1000);
+		count++;
+	}
+
+	if (count >= max_tries) {
+		dprintk(VIDC_ERR, "Error booting up vidc firmware\n");
+		rc = -ETIME;
+	}
+	return rc;
+}
+
+static struct clock_info *__get_clock(struct venus_hfi_device *device,
+		char *name)
+{
+	struct clock_info *vc;
+
+	venus_hfi_for_each_clock(device, vc) {
+		if (!strcmp(vc->name, name))
+			return vc;
+	}
+
+	dprintk(VIDC_WARN, "%s Clock %s not found\n", __func__, name);
+
+	return NULL;
+}
+
+static unsigned long __get_clock_rate(struct clock_info *clock,
+	int num_mbs_per_sec, struct vidc_clk_scale_data *data)
+{
+	int num_rows = clock->count;
+	struct load_freq_table *table = clock->load_freq_tbl;
+	unsigned long freq = table[0].freq, max_freq = 0;
+	int i = 0, j = 0;
+	unsigned long instance_freq[VIDC_MAX_SESSIONS] = {0};
+
+	if (!data && !num_rows) {
+		freq = 0;
+		goto print_clk;
+	}
+
+	if ((!num_mbs_per_sec || !data) && num_rows) {
+		freq = table[num_rows - 1].freq;
+		goto print_clk;
+	}
+
+	for (i = 0; i < num_rows; i++) {
+		if (num_mbs_per_sec > table[i].load)
+			break;
+		for (j = 0; j < data->num_sessions; j++) {
+			bool matches = __is_session_supported(
+				table[i].supported_codecs, data->session[j]);
+
+			if (!matches)
+				continue;
+			instance_freq[j] = table[i].freq;
+		}
+	}
+	for (i = 0; i < data->num_sessions; i++)
+		max_freq = max(instance_freq[i], max_freq);
+
+	freq = max_freq ? : freq;
+print_clk:
+	dprintk(VIDC_PROF, "Required clock rate = %lu num_mbs_per_sec %d\n",
+					freq, num_mbs_per_sec);
+	return freq;
+}
+
+static unsigned long __get_clock_rate_with_bitrate(struct clock_info *clock,
+		int num_mbs_per_sec, struct vidc_clk_scale_data *data,
+		unsigned long instant_bitrate)
+{
+	int num_rows = clock->count;
+	struct load_freq_table *table = clock->load_freq_tbl;
+	unsigned long freq = table[0].freq, max_freq = 0;
+	unsigned long base_freq, supported_clk[VIDC_MAX_SESSIONS] = {0};
+	int i, j;
+
+	if (!data && !num_rows) {
+		freq = 0;
+		goto print_clk;
+	}
+	if ((!num_mbs_per_sec || !data) && num_rows) {
+		freq = table[num_rows - 1].freq;
+		goto print_clk;
+	}
+
+	/* Get clock rate based on current load only */
+	base_freq = __get_clock_rate(clock, num_mbs_per_sec, data);
+
+	/*
+	 * Supported bitrate = 40% of clock frequency
+	 * Check if the instant bitrate is supported by the base frequency.
+	 * If not, move on to the next frequency which supports the bitrate.
+	 */
+
+	for (j = 0; j < data->num_sessions; j++) {
+		unsigned long supported_bitrate = 0;
+
+		for (i = num_rows - 1; i >= 0; i--) {
+			bool matches = __is_session_supported(
+				table[i].supported_codecs, data->session[j]);
+
+			if (!matches)
+				continue;
+			freq = table[i].freq;
+
+			supported_bitrate = freq * 40/100;
+			/*
+			 * Store this frequency for each instance, we need
+			 * to select the maximum freq among all the instances.
+			 */
+			if (freq >= base_freq &&
+				supported_bitrate >= instant_bitrate) {
+				supported_clk[j] = freq;
+				break;
+			}
+		}
+
+		/*
+		 * Current bitrate is higher than max supported load.
+		 * Select max frequency to handle this load.
+		 */
+		if (i < 0)
+			supported_clk[j] = table[0].freq;
+	}
+
+	for (i = 0; i < data->num_sessions; i++)
+		max_freq = max(supported_clk[i], max_freq);
+
+	freq = max_freq ? : base_freq;
+
+	if (base_freq == freq)
+		dprintk(VIDC_DBG, "Stay at base freq: %lu bitrate = %lu\n",
+			freq, instant_bitrate);
+	else
+		dprintk(VIDC_DBG, "Move up clock freq: %lu bitrate = %lu\n",
+			freq, instant_bitrate);
+print_clk:
+	dprintk(VIDC_PROF, "Required clock rate = %lu num_mbs_per_sec %d\n",
+					freq, num_mbs_per_sec);
+	return freq;
+}
+
+static unsigned long venus_hfi_get_core_clock_rate(void *dev, bool actual_rate)
+{
+	struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
+	struct clock_info *vc;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s Invalid args: %pK\n", __func__, device);
+		return -EINVAL;
+	}
+
+	if (actual_rate) {
+		vc = __get_clock(device, "core_clk");
+		if (vc)
+			return clk_get_rate(vc->clk);
+		else
+			return 0;
+	} else {
+		return device->scaled_rate;
+	}
+}
+
+static int venus_hfi_suspend(void *dev)
+{
+	int rc = 0;
+	struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s invalid device\n", __func__);
+		return -EINVAL;
+	} else if (!device->res->sw_power_collapsible) {
+		return -ENOTSUPP;
+	}
+
+	dprintk(VIDC_DBG, "Suspending Venus\n");
+	rc = flush_delayed_work(&venus_hfi_pm_work);
+
+	return rc;
+}
+
+static int venus_hfi_flush_debug_queue(void *dev)
+{
+	int rc = 0;
+	struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s invalid device\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&device->lock);
+
+	if (device->power_enabled) {
+		dprintk(VIDC_DBG, "Venus is busy\n");
+		rc = -EBUSY;
+		goto exit;
+	}
+	__flush_debug_queue(device, NULL);
+exit:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static enum hal_default_properties venus_hfi_get_default_properties(void *dev)
+{
+	enum hal_default_properties prop = 0;
+	struct venus_hfi_device *device = (struct venus_hfi_device *) dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s invalid device\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&device->lock);
+
+	if (device->packetization_type == HFI_PACKETIZATION_3XX)
+		prop = HAL_VIDEO_DYNAMIC_BUF_MODE;
+
+	mutex_unlock(&device->lock);
+	return prop;
+}
+
+static int __halt_axi(struct venus_hfi_device *device)
+{
+	u32 reg;
+	int rc = 0;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid input: %pK\n", device);
+		return -EINVAL;
+	}
+
+	/*
+	 * Driver needs to make sure that clocks are enabled to read Venus AXI
+	 * registers. If not skip AXI HALT.
+	 */
+	if (!device->power_enabled) {
+		dprintk(VIDC_WARN,
+			"Clocks are OFF, skipping AXI HALT\n");
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	/* Halt AXI and AXI IMEM VBIF Access */
+	reg = __read_register(device, VENUS_VBIF_AXI_HALT_CTRL0);
+	reg |= VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ;
+	__write_register(device, VENUS_VBIF_AXI_HALT_CTRL0, reg);
+
+	/* Request for AXI bus port halt */
+	rc = readl_poll_timeout(device->hal_data->register_base
+			+ VENUS_VBIF_AXI_HALT_CTRL1,
+			reg, reg & VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK,
+			POLL_INTERVAL_US,
+			VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US);
+	if (rc)
+		dprintk(VIDC_WARN, "AXI bus port halt timeout\n");
+
+	return rc;
+}
+
+static int __scale_clocks_cycles_per_mb(struct venus_hfi_device *device,
+		struct vidc_clk_scale_data *data, unsigned long instant_bitrate)
+{
+	int rc = 0, i = 0, j = 0;
+	struct clock_info *cl;
+	struct clock_freq_table *clk_freq_tbl = NULL;
+	struct allowed_clock_rates_table *allowed_clks_tbl = NULL;
+	struct clock_profile_entry *entry = NULL;
+	u64 total_freq = 0, rate = 0;
+
+	clk_freq_tbl = &device->res->clock_freq_tbl;
+	allowed_clks_tbl = device->res->allowed_clks_tbl;
+
+	if (!data) {
+		dprintk(VIDC_DBG, "%s: NULL scale data\n", __func__);
+		total_freq = device->clk_freq;
+		goto get_clock_freq;
+	}
+
+	device->clk_bitrate = instant_bitrate;
+
+	for (i = 0; i < data->num_sessions; i++) {
+		/*
+		 * for each active session iterate through all possible
+		 * sessions and get matching session's cycles per mb
+		 * from dtsi and multiply with the session's load to
+		 * get the frequency required for the session.
+		 * accumulate all session's frequencies to get the
+		 * total clock frequency.
+		 */
+		for (j = 0; j < clk_freq_tbl->count; j++) {
+			bool matched = false;
+			u64 freq = 0;
+
+			entry = &clk_freq_tbl->clk_prof_entries[j];
+
+			matched = __is_session_supported(entry->codec_mask,
+					data->session[i]);
+			if (!matched)
+				continue;
+
+			freq = entry->cycles * data->load[i];
+
+			if (data->power_mode[i] == VIDC_POWER_LOW &&
+					entry->low_power_factor) {
+				/* low_power_factor is in Q16 format */
+				freq = (freq * entry->low_power_factor) >> 16;
+			}
+
+			total_freq += freq;
+
+			dprintk(VIDC_DBG,
+				"session[%d] %#x: cycles (%d), load (%d), freq (%llu), factor (%d)\n",
+				i, data->session[i], entry->cycles,
+				data->load[i], freq,
+				entry->low_power_factor);
+		}
+	}
+
+get_clock_freq:
+	/*
+	 * get required clock rate from allowed clock rates table
+	 */
+	for (i = device->res->allowed_clks_tbl_size - 1; i >= 0; i--) {
+		rate = allowed_clks_tbl[i].clock_rate;
+		if (rate >= total_freq)
+			break;
+	}
+
+	venus_hfi_for_each_clock(device, cl) {
+		if (!cl->has_scaling)
+			continue;
+
+		device->clk_freq = rate;
+		rc = clk_set_rate(cl->clk, rate);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"%s: Failed to set clock rate %llu %s: %d\n",
+				__func__, rate, cl->name, rc);
+			return rc;
+		}
+		if (!strcmp(cl->name, "core_clk"))
+			device->scaled_rate = rate;
+
+		dprintk(VIDC_DBG,
+			"scaling clock %s to %llu (required freq %llu)\n",
+			cl->name, rate, total_freq);
+	}
+
+	return rc;
+}
+
+static int __scale_clocks_load(struct venus_hfi_device *device, int load,
+		struct vidc_clk_scale_data *data, unsigned long instant_bitrate)
+{
+	struct clock_info *cl;
+
+	device->clk_bitrate = instant_bitrate;
+
+	venus_hfi_for_each_clock(device, cl) {
+		if (cl->has_scaling) {
+
+			unsigned long rate = 0;
+			int rc;
+			/*
+			 * load_fw and power_on needs to be addressed.
+			 * differently. Below check enforces the same.
+			 */
+			if (!device->clk_bitrate && !data && !load &&
+				device->clk_freq)
+				rate = device->clk_freq;
+
+			if (!rate) {
+				if (!device->clk_bitrate)
+					rate = __get_clock_rate(cl, load,
+							data);
+				else
+					rate = __get_clock_rate_with_bitrate(cl,
+							load, data,
+							instant_bitrate);
+			}
+			device->clk_freq = rate;
+			rc = clk_set_rate(cl->clk, rate);
+			if (rc) {
+				dprintk(VIDC_ERR,
+					"Failed to set clock rate %lu %s: %d\n",
+					rate, cl->name, rc);
+				return rc;
+			}
+
+			if (!strcmp(cl->name, "core_clk"))
+				device->scaled_rate = rate;
+
+			dprintk(VIDC_PROF, "Scaling clock %s to %lu\n",
+					cl->name, rate);
+		}
+	}
+
+	return 0;
+}
+
+static int __scale_clocks(struct venus_hfi_device *device,
+		int load, struct vidc_clk_scale_data *data,
+		unsigned long instant_bitrate)
+{
+	int rc = 0;
+
+	if (device->res->clock_freq_tbl.clk_prof_entries &&
+			device->res->allowed_clks_tbl)
+		rc = __scale_clocks_cycles_per_mb(device,
+				data, instant_bitrate);
+	else if (device->res->load_freq_tbl)
+		rc = __scale_clocks_load(device, load, data, instant_bitrate);
+	else
+		dprintk(VIDC_DBG, "Clock scaling is not supported\n");
+
+	return rc;
+}
+static int venus_hfi_scale_clocks(void *dev, int load,
+					struct vidc_clk_scale_data *data,
+					unsigned long instant_bitrate)
+{
+	int rc = 0;
+	struct venus_hfi_device *device = dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid args: %pK\n", device);
+		return -EINVAL;
+	}
+
+	mutex_lock(&device->lock);
+
+	if (__resume(device)) {
+		dprintk(VIDC_ERR, "Resume from power collapse failed\n");
+		rc = -ENODEV;
+		goto exit;
+	}
+
+	rc = __scale_clocks(device, load, data, instant_bitrate);
+exit:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+/* Writes into cmdq without raising an interrupt */
+static int __iface_cmdq_write_relaxed(struct venus_hfi_device *device,
+		void *pkt, bool *requires_interrupt)
+{
+	struct vidc_iface_q_info *q_info;
+	struct vidc_hal_cmd_pkt_hdr *cmd_packet;
+	int result = -E2BIG;
+
+	if (!device || !pkt) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	__strict_check(device);
+
+	if (!__core_in_valid_state(device)) {
+		dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__);
+		result = -EINVAL;
+		goto err_q_null;
+	}
+
+	cmd_packet = (struct vidc_hal_cmd_pkt_hdr *)pkt;
+	device->last_packet_type = cmd_packet->packet_type;
+
+	q_info = &device->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
+	if (!q_info) {
+		dprintk(VIDC_ERR, "cannot write to shared Q's\n");
+		goto err_q_null;
+	}
+
+	if (!q_info->q_array.align_virtual_addr) {
+		dprintk(VIDC_ERR, "cannot write to shared CMD Q's\n");
+		result = -ENODATA;
+		goto err_q_null;
+	}
+
+	__sim_modify_cmd_packet((u8 *)pkt, device);
+	if (__resume(device)) {
+		dprintk(VIDC_ERR, "%s: Power on failed\n", __func__);
+		goto err_q_write;
+	}
+
+	if (!__write_queue(q_info, (u8 *)pkt, requires_interrupt)) {
+		if (device->res->sw_power_collapsible) {
+			cancel_delayed_work(&venus_hfi_pm_work);
+			if (!queue_delayed_work(device->venus_pm_workq,
+				&venus_hfi_pm_work,
+				msecs_to_jiffies(
+				msm_vidc_pwr_collapse_delay))) {
+				dprintk(VIDC_DBG,
+				"PM work already scheduled\n");
+			}
+		}
+
+		result = 0;
+	} else {
+		dprintk(VIDC_ERR, "__iface_cmdq_write: queue full\n");
+	}
+
+err_q_write:
+err_q_null:
+	return result;
+}
+
+static int __iface_cmdq_write(struct venus_hfi_device *device, void *pkt)
+{
+	bool needs_interrupt = false;
+	int rc = __iface_cmdq_write_relaxed(device, pkt, &needs_interrupt);
+
+	if (!rc && needs_interrupt) {
+		/* Consumer of cmdq prefers that we raise an interrupt */
+		rc = 0;
+		__write_register(device, VIDC_CPU_IC_SOFTINT,
+				1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT);
+	}
+
+	return rc;
+}
+
+static int __iface_msgq_read(struct venus_hfi_device *device, void *pkt)
+{
+	u32 tx_req_is_set = 0;
+	int rc = 0;
+	struct vidc_iface_q_info *q_info;
+
+	if (!pkt) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	__strict_check(device);
+
+	if (!__core_in_valid_state(device)) {
+		dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__);
+		rc = -EINVAL;
+		goto read_error_null;
+	}
+
+	if (device->iface_queues[VIDC_IFACEQ_MSGQ_IDX].
+		q_array.align_virtual_addr == 0) {
+		dprintk(VIDC_ERR, "cannot read from shared MSG Q's\n");
+		rc = -ENODATA;
+		goto read_error_null;
+	}
+
+	q_info = &device->iface_queues[VIDC_IFACEQ_MSGQ_IDX];
+	if (!__read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) {
+		__hal_sim_modify_msg_packet((u8 *)pkt, device);
+		if (tx_req_is_set)
+			__write_register(device, VIDC_CPU_IC_SOFTINT,
+				1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT);
+		rc = 0;
+	} else
+		rc = -ENODATA;
+
+read_error_null:
+	return rc;
+}
+
+static int __iface_dbgq_read(struct venus_hfi_device *device, void *pkt)
+{
+	u32 tx_req_is_set = 0;
+	int rc = 0;
+	struct vidc_iface_q_info *q_info;
+
+	if (!pkt) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	__strict_check(device);
+
+	if (!__core_in_valid_state(device)) {
+		dprintk(VIDC_DBG, "%s - fw not in init state\n", __func__);
+		rc = -EINVAL;
+		goto dbg_error_null;
+	}
+
+	if (device->iface_queues[VIDC_IFACEQ_DBGQ_IDX].
+		q_array.align_virtual_addr == 0) {
+		dprintk(VIDC_ERR, "cannot read from shared DBG Q's\n");
+		rc = -ENODATA;
+		goto dbg_error_null;
+	}
+
+	q_info = &device->iface_queues[VIDC_IFACEQ_DBGQ_IDX];
+	if (!__read_queue(q_info, (u8 *)pkt, &tx_req_is_set)) {
+		if (tx_req_is_set)
+			__write_register(device, VIDC_CPU_IC_SOFTINT,
+				1 << VIDC_CPU_IC_SOFTINT_H2A_SHFT);
+		rc = 0;
+	} else
+		rc = -ENODATA;
+
+dbg_error_null:
+	return rc;
+}
+
+static void __set_queue_hdr_defaults(struct hfi_queue_header *q_hdr)
+{
+	q_hdr->qhdr_status = 0x1;
+	q_hdr->qhdr_type = VIDC_IFACEQ_DFLT_QHDR;
+	q_hdr->qhdr_q_size = VIDC_IFACEQ_QUEUE_SIZE / 4;
+	q_hdr->qhdr_pkt_size = 0;
+	q_hdr->qhdr_rx_wm = 0x1;
+	q_hdr->qhdr_tx_wm = 0x1;
+	q_hdr->qhdr_rx_req = 0x1;
+	q_hdr->qhdr_tx_req = 0x0;
+	q_hdr->qhdr_rx_irq_status = 0x0;
+	q_hdr->qhdr_tx_irq_status = 0x0;
+	q_hdr->qhdr_read_idx = 0x0;
+	q_hdr->qhdr_write_idx = 0x0;
+}
+
+static void __interface_queues_release(struct venus_hfi_device *device)
+{
+	int i;
+	struct hfi_mem_map_table *qdss;
+	struct hfi_mem_map *mem_map;
+	int num_entries = device->res->qdss_addr_set.count;
+	unsigned long mem_map_table_base_addr;
+	struct context_bank_info *cb;
+
+	if (device->qdss.mem_data) {
+		qdss = (struct hfi_mem_map_table *)
+			device->qdss.align_virtual_addr;
+		qdss->mem_map_num_entries = num_entries;
+		mem_map_table_base_addr =
+			device->qdss.align_device_addr +
+			sizeof(struct hfi_mem_map_table);
+		qdss->mem_map_table_base_addr =
+			(u32)mem_map_table_base_addr;
+		if ((unsigned long)qdss->mem_map_table_base_addr !=
+			mem_map_table_base_addr) {
+			dprintk(VIDC_ERR,
+				"Invalid mem_map_table_base_addr %#lx",
+				mem_map_table_base_addr);
+		}
+
+		mem_map = (struct hfi_mem_map *)(qdss + 1);
+		cb = msm_smem_get_context_bank(device->hal_client,
+					false, HAL_BUFFER_INTERNAL_CMD_QUEUE);
+
+		for (i = 0; cb && i < num_entries; i++) {
+			iommu_unmap(cb->mapping->domain,
+						mem_map[i].virtual_addr,
+						mem_map[i].size);
+		}
+
+		__smem_free(device, device->qdss.mem_data);
+	}
+
+	__smem_free(device, device->iface_q_table.mem_data);
+	__smem_free(device, device->sfr.mem_data);
+
+	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
+		device->iface_queues[i].q_hdr = NULL;
+		device->iface_queues[i].q_array.mem_data = NULL;
+		device->iface_queues[i].q_array.align_virtual_addr = NULL;
+		device->iface_queues[i].q_array.align_device_addr = 0;
+	}
+
+	device->iface_q_table.mem_data = NULL;
+	device->iface_q_table.align_virtual_addr = NULL;
+	device->iface_q_table.align_device_addr = 0;
+
+	device->qdss.mem_data = NULL;
+	device->qdss.align_virtual_addr = NULL;
+	device->qdss.align_device_addr = 0;
+
+	device->sfr.mem_data = NULL;
+	device->sfr.align_virtual_addr = NULL;
+	device->sfr.align_device_addr = 0;
+
+	device->mem_addr.mem_data = NULL;
+	device->mem_addr.align_virtual_addr = NULL;
+	device->mem_addr.align_device_addr = 0;
+
+	msm_smem_delete_client(device->hal_client);
+	device->hal_client = NULL;
+}
+
+static int __get_qdss_iommu_virtual_addr(struct venus_hfi_device *dev,
+		struct hfi_mem_map *mem_map, struct dma_iommu_mapping *mapping)
+{
+	int i;
+	int rc = 0;
+	dma_addr_t iova = QDSS_IOVA_START;
+	int num_entries = dev->res->qdss_addr_set.count;
+	struct addr_range *qdss_addr_tbl = dev->res->qdss_addr_set.addr_tbl;
+
+	if (!num_entries)
+		return -ENODATA;
+
+	for (i = 0; i < num_entries; i++) {
+		if (mapping) {
+			rc = iommu_map(mapping->domain, iova,
+					qdss_addr_tbl[i].start,
+					qdss_addr_tbl[i].size,
+					IOMMU_READ | IOMMU_WRITE);
+
+			if (rc) {
+				dprintk(VIDC_ERR,
+						"IOMMU QDSS mapping failed for addr %#x\n",
+						qdss_addr_tbl[i].start);
+				rc = -ENOMEM;
+				break;
+			}
+		} else {
+			iova =  qdss_addr_tbl[i].start;
+		}
+
+		mem_map[i].virtual_addr = (u32)iova;
+		mem_map[i].physical_addr = qdss_addr_tbl[i].start;
+		mem_map[i].size = qdss_addr_tbl[i].size;
+		mem_map[i].attr = 0x0;
+
+		iova += mem_map[i].size;
+	}
+
+	if (i < num_entries) {
+		dprintk(VIDC_ERR,
+			"QDSS mapping failed, Freeing other entries %d\n", i);
+
+		for (--i; mapping && i >= 0; i--) {
+			iommu_unmap(mapping->domain,
+				mem_map[i].virtual_addr,
+				mem_map[i].size);
+		}
+	}
+
+	return rc;
+}
+
+static void __setup_ucregion_memory_map(struct venus_hfi_device *device)
+{
+	__write_register(device, VIDC_UC_REGION_ADDR,
+			(u32)device->iface_q_table.align_device_addr);
+	__write_register(device, VIDC_UC_REGION_SIZE, SHARED_QSIZE);
+	__write_register(device, VIDC_CPU_CS_SCIACMDARG2,
+			(u32)device->iface_q_table.align_device_addr);
+	__write_register(device, VIDC_CPU_CS_SCIACMDARG1, 0x01);
+	if (device->sfr.align_device_addr)
+		__write_register(device, VIDC_SFR_ADDR,
+				(u32)device->sfr.align_device_addr);
+	if (device->qdss.align_device_addr)
+		__write_register(device, VIDC_MMAP_ADDR,
+				(u32)device->qdss.align_device_addr);
+}
+
+static int __interface_queues_init(struct venus_hfi_device *dev)
+{
+	struct hfi_queue_table_header *q_tbl_hdr;
+	struct hfi_queue_header *q_hdr;
+	u32 i;
+	int rc = 0;
+	struct hfi_mem_map_table *qdss;
+	struct hfi_mem_map *mem_map;
+	struct vidc_iface_q_info *iface_q;
+	struct hfi_sfr_struct *vsfr;
+	struct vidc_mem_addr *mem_addr;
+	int offset = 0;
+	int num_entries = dev->res->qdss_addr_set.count;
+	u32 value = 0;
+	phys_addr_t fw_bias = 0;
+	size_t q_size;
+	unsigned long mem_map_table_base_addr;
+	struct context_bank_info *cb;
+
+	q_size = SHARED_QSIZE - ALIGNED_SFR_SIZE - ALIGNED_QDSS_SIZE;
+	mem_addr = &dev->mem_addr;
+	if (!is_iommu_present(dev->res))
+		fw_bias = dev->hal_data->firmware_base;
+	rc = __smem_alloc(dev, mem_addr, q_size, 1, 0,
+			HAL_BUFFER_INTERNAL_CMD_QUEUE);
+	if (rc) {
+		dprintk(VIDC_ERR, "iface_q_table_alloc_fail\n");
+		goto fail_alloc_queue;
+	}
+
+	dev->iface_q_table.align_virtual_addr = mem_addr->align_virtual_addr;
+	dev->iface_q_table.align_device_addr = mem_addr->align_device_addr -
+					fw_bias;
+	dev->iface_q_table.mem_size = VIDC_IFACEQ_TABLE_SIZE;
+	dev->iface_q_table.mem_data = mem_addr->mem_data;
+	offset += dev->iface_q_table.mem_size;
+
+	for (i = 0; i < VIDC_IFACEQ_NUMQ; i++) {
+		iface_q = &dev->iface_queues[i];
+		iface_q->q_array.align_device_addr = mem_addr->align_device_addr
+			+ offset - fw_bias;
+		iface_q->q_array.align_virtual_addr =
+			mem_addr->align_virtual_addr + offset;
+		iface_q->q_array.mem_size = VIDC_IFACEQ_QUEUE_SIZE;
+		iface_q->q_array.mem_data = NULL;
+		offset += iface_q->q_array.mem_size;
+		iface_q->q_hdr = VIDC_IFACEQ_GET_QHDR_START_ADDR(
+				dev->iface_q_table.align_virtual_addr, i);
+		__set_queue_hdr_defaults(iface_q->q_hdr);
+	}
+
+	if ((msm_vidc_fw_debug_mode & HFI_DEBUG_MODE_QDSS) && num_entries) {
+		rc = __smem_alloc(dev, mem_addr,
+				ALIGNED_QDSS_SIZE, 1, 0,
+				HAL_BUFFER_INTERNAL_CMD_QUEUE);
+		if (rc) {
+			dprintk(VIDC_WARN,
+				"qdss_alloc_fail: QDSS messages logging will not work\n");
+			dev->qdss.align_device_addr = 0;
+		} else {
+			dev->qdss.align_device_addr =
+				mem_addr->align_device_addr - fw_bias;
+			dev->qdss.align_virtual_addr =
+				mem_addr->align_virtual_addr;
+			dev->qdss.mem_size = ALIGNED_QDSS_SIZE;
+			dev->qdss.mem_data = mem_addr->mem_data;
+		}
+	}
+
+	rc = __smem_alloc(dev, mem_addr,
+			ALIGNED_SFR_SIZE, 1, 0,
+			HAL_BUFFER_INTERNAL_CMD_QUEUE);
+	if (rc) {
+		dprintk(VIDC_WARN, "sfr_alloc_fail: SFR not will work\n");
+		dev->sfr.align_device_addr = 0;
+	} else {
+		dev->sfr.align_device_addr = mem_addr->align_device_addr -
+					fw_bias;
+		dev->sfr.align_virtual_addr = mem_addr->align_virtual_addr;
+		dev->sfr.mem_size = ALIGNED_SFR_SIZE;
+		dev->sfr.mem_data = mem_addr->mem_data;
+	}
+
+	q_tbl_hdr = (struct hfi_queue_table_header *)
+			dev->iface_q_table.align_virtual_addr;
+	q_tbl_hdr->qtbl_version = 0;
+	q_tbl_hdr->device_addr = (void *)dev;
+	strlcpy(q_tbl_hdr->name, "msm_v4l2_vidc", sizeof(q_tbl_hdr->name));
+	q_tbl_hdr->qtbl_size = VIDC_IFACEQ_TABLE_SIZE;
+	q_tbl_hdr->qtbl_qhdr0_offset = sizeof(struct hfi_queue_table_header);
+	q_tbl_hdr->qtbl_qhdr_size = sizeof(struct hfi_queue_header);
+	q_tbl_hdr->qtbl_num_q = VIDC_IFACEQ_NUMQ;
+	q_tbl_hdr->qtbl_num_active_q = VIDC_IFACEQ_NUMQ;
+
+	iface_q = &dev->iface_queues[VIDC_IFACEQ_CMDQ_IDX];
+	q_hdr = iface_q->q_hdr;
+	q_hdr->qhdr_start_addr = (u32)iface_q->q_array.align_device_addr;
+	q_hdr->qhdr_type |= HFI_Q_ID_HOST_TO_CTRL_CMD_Q;
+	if ((ion_phys_addr_t)q_hdr->qhdr_start_addr !=
+		iface_q->q_array.align_device_addr) {
+		dprintk(VIDC_ERR, "Invalid CMDQ device address (%pa)",
+			&iface_q->q_array.align_device_addr);
+	}
+
+	iface_q = &dev->iface_queues[VIDC_IFACEQ_MSGQ_IDX];
+	q_hdr = iface_q->q_hdr;
+	q_hdr->qhdr_start_addr = (u32)iface_q->q_array.align_device_addr;
+	q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_MSG_Q;
+	if ((ion_phys_addr_t)q_hdr->qhdr_start_addr !=
+		iface_q->q_array.align_device_addr) {
+		dprintk(VIDC_ERR, "Invalid MSGQ device address (%pa)",
+			&iface_q->q_array.align_device_addr);
+	}
+
+	iface_q = &dev->iface_queues[VIDC_IFACEQ_DBGQ_IDX];
+	q_hdr = iface_q->q_hdr;
+	q_hdr->qhdr_start_addr = (u32)iface_q->q_array.align_device_addr;
+	q_hdr->qhdr_type |= HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q;
+	/*
+	 * Set receive request to zero on debug queue as there is no
+	 * need of interrupt from video hardware for debug messages
+	 */
+	q_hdr->qhdr_rx_req = 0;
+	if ((ion_phys_addr_t)q_hdr->qhdr_start_addr !=
+		iface_q->q_array.align_device_addr) {
+		dprintk(VIDC_ERR, "Invalid DBGQ device address (%pa)",
+			&iface_q->q_array.align_device_addr);
+	}
+
+	value = (u32)dev->iface_q_table.align_device_addr;
+	if ((ion_phys_addr_t)value !=
+		dev->iface_q_table.align_device_addr) {
+		dprintk(VIDC_ERR,
+			"Invalid iface_q_table device address (%pa)",
+			&dev->iface_q_table.align_device_addr);
+	}
+
+	if (dev->qdss.mem_data) {
+		qdss = (struct hfi_mem_map_table *)dev->qdss.align_virtual_addr;
+		qdss->mem_map_num_entries = num_entries;
+		mem_map_table_base_addr = dev->qdss.align_device_addr +
+			sizeof(struct hfi_mem_map_table);
+		qdss->mem_map_table_base_addr =
+			(u32)mem_map_table_base_addr;
+		if ((ion_phys_addr_t)qdss->mem_map_table_base_addr !=
+				mem_map_table_base_addr) {
+			dprintk(VIDC_ERR,
+					"Invalid mem_map_table_base_addr (%#lx)",
+					mem_map_table_base_addr);
+		}
+
+		mem_map = (struct hfi_mem_map *)(qdss + 1);
+		cb = msm_smem_get_context_bank(dev->hal_client, false,
+				HAL_BUFFER_INTERNAL_CMD_QUEUE);
+
+		if (!cb) {
+			dprintk(VIDC_ERR,
+				"%s: failed to get context bank\n", __func__);
+			return -EINVAL;
+		}
+
+		rc = __get_qdss_iommu_virtual_addr(dev, mem_map, cb->mapping);
+		if (rc) {
+			dprintk(VIDC_ERR,
+				"IOMMU mapping failed, Freeing qdss memdata\n");
+			__smem_free(dev, dev->qdss.mem_data);
+			dev->qdss.mem_data = NULL;
+			dev->qdss.align_virtual_addr = NULL;
+			dev->qdss.align_device_addr = 0;
+		}
+
+		value = (u32)dev->qdss.align_device_addr;
+		if ((ion_phys_addr_t)value !=
+				dev->qdss.align_device_addr) {
+			dprintk(VIDC_ERR, "Invalid qdss device address (%pa)",
+					&dev->qdss.align_device_addr);
+		}
+	}
+
+	vsfr = (struct hfi_sfr_struct *) dev->sfr.align_virtual_addr;
+	vsfr->bufSize = ALIGNED_SFR_SIZE;
+	value = (u32)dev->sfr.align_device_addr;
+	if ((ion_phys_addr_t)value !=
+		dev->sfr.align_device_addr) {
+		dprintk(VIDC_ERR, "Invalid sfr device address (%pa)",
+			&dev->sfr.align_device_addr);
+	}
+
+	__setup_ucregion_memory_map(dev);
+	return 0;
+fail_alloc_queue:
+	return -ENOMEM;
+}
+
+static int __sys_set_debug(struct venus_hfi_device *device, u32 debug)
+{
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	int rc = 0;
+	struct hfi_cmd_sys_set_property_packet *pkt =
+		(struct hfi_cmd_sys_set_property_packet *) &packet;
+
+	rc = call_hfi_pkt_op(device, sys_debug_config, pkt, debug);
+	if (rc) {
+		dprintk(VIDC_WARN,
+			"Debug mode setting to FW failed\n");
+		return -ENOTEMPTY;
+	}
+
+	if (__iface_cmdq_write(device, pkt))
+		return -ENOTEMPTY;
+	return 0;
+}
+
+static int __sys_set_coverage(struct venus_hfi_device *device, u32 mode)
+{
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	int rc = 0;
+	struct hfi_cmd_sys_set_property_packet *pkt =
+		(struct hfi_cmd_sys_set_property_packet *) &packet;
+
+	rc = call_hfi_pkt_op(device, sys_coverage_config,
+			pkt, mode);
+	if (rc) {
+		dprintk(VIDC_WARN,
+			"Coverage mode setting to FW failed\n");
+		return -ENOTEMPTY;
+	}
+
+	if (__iface_cmdq_write(device, pkt)) {
+		dprintk(VIDC_WARN, "Failed to send coverage pkt to f/w\n");
+		return -ENOTEMPTY;
+	}
+
+	return 0;
+}
+
+static int __sys_set_idle_message(struct venus_hfi_device *device,
+	bool enable)
+{
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hfi_cmd_sys_set_property_packet *pkt =
+		(struct hfi_cmd_sys_set_property_packet *) &packet;
+
+	if (!enable) {
+		dprintk(VIDC_DBG, "sys_idle_indicator is not enabled\n");
+		return 0;
+	}
+
+	call_hfi_pkt_op(device, sys_idle_indicator, pkt, enable);
+	if (__iface_cmdq_write(device, pkt))
+		return -ENOTEMPTY;
+	return 0;
+}
+
+static int __sys_set_power_control(struct venus_hfi_device *device,
+	bool enable)
+{
+	struct regulator_info *rinfo;
+	bool supported = false;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hfi_cmd_sys_set_property_packet *pkt =
+		(struct hfi_cmd_sys_set_property_packet *) &packet;
+
+	venus_hfi_for_each_regulator(device, rinfo) {
+		if (rinfo->has_hw_power_collapse) {
+			supported = true;
+			break;
+		}
+	}
+
+	if (!supported)
+		return 0;
+
+	call_hfi_pkt_op(device, sys_power_control, pkt, enable);
+	if (__iface_cmdq_write(device, pkt))
+		return -ENOTEMPTY;
+	return 0;
+}
+
+static int venus_hfi_core_init(void *device)
+{
+	struct hfi_cmd_sys_init_packet pkt;
+	struct hfi_cmd_sys_get_property_packet version_pkt;
+	int rc = 0;
+	struct list_head *ptr, *next;
+	struct hal_session *session = NULL;
+	struct venus_hfi_device *dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid device\n");
+		return -ENODEV;
+	}
+
+	dev = device;
+	mutex_lock(&dev->lock);
+
+	rc = __load_fw(dev);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to load Venus FW\n");
+		goto err_load_fw;
+	}
+
+	__set_state(dev, VENUS_STATE_INIT);
+
+	list_for_each_safe(ptr, next, &dev->sess_head) {
+		/*
+		 * This means that session list is not empty. Kick stale
+		 * sessions out of our valid instance list, but keep the
+		 * list_head inited so that list_del (in the future, called
+		 * by session_clean()) will be valid. When client doesn't close
+		 * them, then it is a genuine leak which driver can't fix.
+		 */
+		session = list_entry(ptr, struct hal_session, list);
+		list_del_init(&session->list);
+	}
+
+	INIT_LIST_HEAD(&dev->sess_head);
+
+	if (!dev->hal_client) {
+		dev->hal_client = msm_smem_new_client(
+				SMEM_ION, dev->res, MSM_VIDC_UNKNOWN);
+		if (dev->hal_client == NULL) {
+			dprintk(VIDC_ERR, "Failed to alloc ION_Client\n");
+			rc = -ENODEV;
+			goto err_core_init;
+		}
+
+		dprintk(VIDC_DBG, "Dev_Virt: %pa, Reg_Virt: %pK\n",
+			&dev->hal_data->firmware_base,
+			dev->hal_data->register_base);
+
+		rc = __interface_queues_init(dev);
+		if (rc) {
+			dprintk(VIDC_ERR, "failed to init queues\n");
+			rc = -ENOMEM;
+			goto err_core_init;
+		}
+	} else {
+		dprintk(VIDC_ERR, "hal_client exists\n");
+		rc = -EEXIST;
+		goto err_core_init;
+	}
+
+	rc = __boot_firmware(dev);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to start core\n");
+		rc = -ENODEV;
+		goto err_core_init;
+	}
+
+	rc =  call_hfi_pkt_op(dev, sys_init, &pkt, HFI_VIDEO_ARCH_OX);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to create sys init pkt\n");
+		goto err_core_init;
+	}
+
+	if (__iface_cmdq_write(dev, &pkt)) {
+		rc = -ENOTEMPTY;
+		goto err_core_init;
+	}
+
+	rc = call_hfi_pkt_op(dev, sys_image_version, &version_pkt);
+	if (rc || __iface_cmdq_write(dev, &version_pkt))
+		dprintk(VIDC_WARN, "Failed to send image version pkt to f/w\n");
+
+	if (dev->res->pm_qos_latency_us) {
+#ifdef CONFIG_SMP
+		dev->qos.type = PM_QOS_REQ_AFFINE_IRQ;
+		dev->qos.irq = dev->hal_data->irq;
+#endif
+		pm_qos_add_request(&dev->qos, PM_QOS_CPU_DMA_LATENCY,
+				dev->res->pm_qos_latency_us);
+	}
+
+	mutex_unlock(&dev->lock);
+	return rc;
+err_core_init:
+	__set_state(dev, VENUS_STATE_DEINIT);
+	__unload_fw(dev);
+err_load_fw:
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+static int venus_hfi_core_release(void *dev)
+{
+	struct venus_hfi_device *device = dev;
+	int rc = 0;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "invalid device\n");
+		return -ENODEV;
+	}
+
+	mutex_lock(&device->lock);
+
+	if (device->res->pm_qos_latency_us &&
+		pm_qos_request_active(&device->qos))
+		pm_qos_remove_request(&device->qos);
+	__set_state(device, VENUS_STATE_DEINIT);
+	__unload_fw(device);
+
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int __get_q_size(struct venus_hfi_device *dev, unsigned int q_index)
+{
+	struct hfi_queue_header *queue;
+	struct vidc_iface_q_info *q_info;
+	u32 write_ptr, read_ptr;
+
+	if (q_index >= VIDC_IFACEQ_NUMQ) {
+		dprintk(VIDC_ERR, "Invalid q index: %d\n", q_index);
+		return -ENOENT;
+	}
+
+	q_info = &dev->iface_queues[q_index];
+	if (!q_info) {
+		dprintk(VIDC_ERR, "cannot read shared Q's\n");
+		return -ENOENT;
+	}
+
+	queue = (struct hfi_queue_header *)q_info->q_hdr;
+	if (!queue) {
+		dprintk(VIDC_ERR, "queue not present\n");
+		return -ENOENT;
+	}
+
+	write_ptr = (u32)queue->qhdr_write_idx;
+	read_ptr = (u32)queue->qhdr_read_idx;
+	return read_ptr - write_ptr;
+}
+
+static void __core_clear_interrupt(struct venus_hfi_device *device)
+{
+	u32 intr_status = 0;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s: NULL device\n", __func__);
+		return;
+	}
+
+	intr_status = __read_register(device, VIDC_WRAPPER_INTR_STATUS);
+
+	if (intr_status & VIDC_WRAPPER_INTR_STATUS_A2H_BMSK ||
+		intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK ||
+		intr_status &
+			VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK) {
+		device->intr_status |= intr_status;
+		device->reg_count++;
+		dprintk(VIDC_DBG,
+			"INTERRUPT for device: %pK: times: %d interrupt_status: %d\n",
+			device, device->reg_count, intr_status);
+	} else {
+		device->spur_count++;
+		dprintk(VIDC_INFO,
+			"SPURIOUS_INTR for device: %pK: times: %d interrupt_status: %d\n",
+			device, device->spur_count, intr_status);
+	}
+
+	__write_register(device, VIDC_CPU_CS_A2HSOFTINTCLR, 1);
+	__write_register(device, VIDC_WRAPPER_INTR_CLEAR, intr_status);
+	dprintk(VIDC_DBG, "Cleared WRAPPER/A2H interrupt\n");
+}
+
+static int venus_hfi_core_ping(void *device)
+{
+	struct hfi_cmd_sys_ping_packet pkt;
+	int rc = 0;
+	struct venus_hfi_device *dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "invalid device\n");
+		return -ENODEV;
+	}
+
+	dev = device;
+	mutex_lock(&dev->lock);
+
+	rc = call_hfi_pkt_op(dev, sys_ping, &pkt);
+	if (rc) {
+		dprintk(VIDC_ERR, "core_ping: failed to create packet\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(dev, &pkt))
+		rc = -ENOTEMPTY;
+
+err_create_pkt:
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+static int venus_hfi_core_trigger_ssr(void *device,
+		enum hal_ssr_trigger_type type)
+{
+	struct hfi_cmd_sys_test_ssr_packet pkt;
+	int rc = 0;
+	struct venus_hfi_device *dev;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "invalid device\n");
+		return -ENODEV;
+	}
+
+	dev = device;
+	mutex_lock(&dev->lock);
+
+	rc = call_hfi_pkt_op(dev, ssr_cmd, type, &pkt);
+	if (rc) {
+		dprintk(VIDC_ERR, "core_ping: failed to create packet\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(dev, &pkt))
+		rc = -ENOTEMPTY;
+
+err_create_pkt:
+	mutex_unlock(&dev->lock);
+	return rc;
+}
+
+static int venus_hfi_session_set_property(void *sess,
+					enum hal_property ptype, void *pdata)
+{
+	u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
+	struct hfi_cmd_session_set_property_packet *pkt =
+		(struct hfi_cmd_session_set_property_packet *) &packet;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session || !session->device || !pdata) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	dprintk(VIDC_INFO, "in set_prop,with prop id: %#x\n", ptype);
+
+	rc = call_hfi_pkt_op(device, session_set_property,
+			pkt, session, ptype, pdata);
+
+	if (rc == -ENOTSUPP) {
+		dprintk(VIDC_DBG,
+			"set property: unsupported prop id: %#x\n", ptype);
+		rc = 0;
+		goto err_set_prop;
+	} else if (rc) {
+		dprintk(VIDC_ERR, "set property: failed to create packet\n");
+		rc = -EINVAL;
+		goto err_set_prop;
+	}
+
+	if (__iface_cmdq_write(session->device, pkt)) {
+		rc = -ENOTEMPTY;
+		goto err_set_prop;
+	}
+
+err_set_prop:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_get_property(void *sess,
+					enum hal_property ptype)
+{
+	struct hfi_cmd_session_get_property_packet pkt = {0};
+	struct hal_session *session = sess;
+	int rc = 0;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	dprintk(VIDC_INFO, "%s: property id: %d\n", __func__, ptype);
+
+	rc = call_hfi_pkt_op(device, session_get_property,
+				&pkt, session, ptype);
+	if (rc) {
+		dprintk(VIDC_ERR, "get property profile: pkt failed\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(session->device, &pkt)) {
+		rc = -ENOTEMPTY;
+		dprintk(VIDC_ERR, "%s cmdq_write error\n", __func__);
+	}
+
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static void __set_default_sys_properties(struct venus_hfi_device *device)
+{
+	if (__sys_set_debug(device, msm_vidc_fw_debug))
+		dprintk(VIDC_WARN, "Setting fw_debug msg ON failed\n");
+	if (__sys_set_idle_message(device,
+		device->res->sys_idle_indicator || msm_vidc_sys_idle_indicator))
+		dprintk(VIDC_WARN, "Setting idle response ON failed\n");
+	if (__sys_set_power_control(device, msm_vidc_fw_low_power_mode))
+		dprintk(VIDC_WARN, "Setting h/w power collapse ON failed\n");
+}
+
+static void __session_clean(struct hal_session *session)
+{
+	dprintk(VIDC_DBG, "deleted the session: %pK\n", session);
+	list_del(&session->list);
+	/* Poison the session handle with zeros */
+	*session = (struct hal_session){ {0} };
+	kfree(session);
+}
+
+static int venus_hfi_session_clean(void *session)
+{
+	struct hal_session *sess_close;
+	struct venus_hfi_device *device;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess_close = session;
+	device = sess_close->device;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid device handle %s\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&device->lock);
+
+	__session_clean(sess_close);
+
+	mutex_unlock(&device->lock);
+	return 0;
+}
+
+static int venus_hfi_session_init(void *device, void *session_id,
+		enum hal_domain session_type, enum hal_video_codec codec_type,
+		void **new_session)
+{
+	struct hfi_cmd_sys_session_init_packet pkt;
+	struct venus_hfi_device *dev;
+	struct hal_session *s;
+
+	if (!device || !new_session) {
+		dprintk(VIDC_ERR, "%s - invalid input\n", __func__);
+		return -EINVAL;
+	}
+
+	dev = device;
+	mutex_lock(&dev->lock);
+
+	s = kzalloc(sizeof(struct hal_session), GFP_KERNEL);
+	if (!s) {
+		dprintk(VIDC_ERR, "new session fail: Out of memory\n");
+		goto err_session_init_fail;
+	}
+
+	s->session_id = session_id;
+	s->is_decoder = (session_type == HAL_VIDEO_DOMAIN_DECODER);
+	s->device = dev;
+	s->codec = codec_type;
+	s->domain = session_type;
+	dprintk(VIDC_DBG,
+		"%s: inst %pK, session %pK, codec 0x%x, domain 0x%x\n",
+		__func__, session_id, s, s->codec, s->domain);
+
+	list_add_tail(&s->list, &dev->sess_head);
+
+	__set_default_sys_properties(device);
+
+	if (call_hfi_pkt_op(dev, session_init, &pkt,
+			s, session_type, codec_type)) {
+		dprintk(VIDC_ERR, "session_init: failed to create packet\n");
+		goto err_session_init_fail;
+	}
+
+	*new_session = s;
+	if (__iface_cmdq_write(dev, &pkt))
+		goto err_session_init_fail;
+
+	mutex_unlock(&dev->lock);
+	return 0;
+
+err_session_init_fail:
+	if (s)
+		__session_clean(s);
+	*new_session = NULL;
+	mutex_unlock(&dev->lock);
+	return -EINVAL;
+}
+
+static int __send_session_cmd(struct hal_session *session, int pkt_type)
+{
+	struct vidc_hal_session_cmd_pkt pkt;
+	int rc = 0;
+	struct venus_hfi_device *device = session->device;
+
+	rc = call_hfi_pkt_op(device, session_cmd,
+			&pkt, pkt_type, session);
+	if (rc == -EPERM)
+		return 0;
+
+	if (rc) {
+		dprintk(VIDC_ERR, "send session cmd: create pkt failed\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+
+err_create_pkt:
+	return rc;
+}
+
+static int venus_hfi_session_end(void *session)
+{
+	struct hal_session *sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess = session;
+	device = sess->device;
+
+	mutex_lock(&device->lock);
+
+	if (msm_vidc_fw_coverage) {
+		if (__sys_set_coverage(sess->device, msm_vidc_fw_coverage))
+			dprintk(VIDC_WARN, "Fw_coverage msg ON failed\n");
+	}
+
+	rc = __send_session_cmd(session, HFI_CMD_SYS_SESSION_END);
+
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int venus_hfi_session_abort(void *sess)
+{
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session || !session->device) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	device = session->device;
+
+	mutex_lock(&device->lock);
+
+	__flush_debug_queue(device, NULL);
+	rc = __send_session_cmd(session, HFI_CMD_SYS_SESSION_ABORT);
+
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int venus_hfi_session_set_buffers(void *sess,
+				struct vidc_buffer_addr_info *buffer_info)
+{
+	struct hfi_cmd_session_set_buffers_packet *pkt;
+	u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
+	int rc = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device || !buffer_info) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	if (buffer_info->buffer_type == HAL_BUFFER_INPUT) {
+		/*
+		 * Hardware doesn't care about input buffers being
+		 * published beforehand
+		 */
+		rc = 0;
+		goto err_create_pkt;
+	}
+
+	pkt = (struct hfi_cmd_session_set_buffers_packet *)packet;
+
+	rc = call_hfi_pkt_op(device, session_set_buffers,
+			pkt, session, buffer_info);
+	if (rc) {
+		dprintk(VIDC_ERR, "set buffers: failed to create packet\n");
+		goto err_create_pkt;
+	}
+
+	dprintk(VIDC_INFO, "set buffers: %#x\n", buffer_info->buffer_type);
+	if (__iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_release_buffers(void *sess,
+				struct vidc_buffer_addr_info *buffer_info)
+{
+	struct hfi_cmd_session_release_buffer_packet *pkt;
+	u8 packet[VIDC_IFACEQ_VAR_LARGE_PKT_SIZE];
+	int rc = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device || !buffer_info) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	if (buffer_info->buffer_type == HAL_BUFFER_INPUT) {
+		rc = 0;
+		goto err_create_pkt;
+	}
+
+	pkt = (struct hfi_cmd_session_release_buffer_packet *) packet;
+
+	rc = call_hfi_pkt_op(device, session_release_buffers,
+			pkt, session, buffer_info);
+	if (rc) {
+		dprintk(VIDC_ERR, "release buffers: failed to create packet\n");
+		goto err_create_pkt;
+	}
+
+	dprintk(VIDC_INFO, "Release buffers: %#x\n", buffer_info->buffer_type);
+	if (__iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_load_res(void *session)
+{
+	struct hal_session *sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess = session;
+	device = sess->device;
+
+	mutex_lock(&device->lock);
+	rc = __send_session_cmd(sess, HFI_CMD_SESSION_LOAD_RESOURCES);
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int venus_hfi_session_release_res(void *session)
+{
+	struct hal_session *sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess = session;
+	device = sess->device;
+
+	mutex_lock(&device->lock);
+	rc = __send_session_cmd(sess, HFI_CMD_SESSION_RELEASE_RESOURCES);
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int venus_hfi_session_start(void *session)
+{
+	struct hal_session *sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess = session;
+	device = sess->device;
+
+	mutex_lock(&device->lock);
+	rc = __send_session_cmd(sess, HFI_CMD_SESSION_START);
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int venus_hfi_session_continue(void *session)
+{
+	struct hal_session *sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess = session;
+	device = sess->device;
+
+	mutex_lock(&device->lock);
+	rc = __send_session_cmd(sess, HFI_CMD_SESSION_CONTINUE);
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int venus_hfi_session_stop(void *session)
+{
+	struct hal_session *sess;
+	struct venus_hfi_device *device;
+	int rc = 0;
+
+	if (!session) {
+		dprintk(VIDC_ERR, "Invalid Params %s\n", __func__);
+		return -EINVAL;
+	}
+
+	sess = session;
+	device = sess->device;
+
+	mutex_lock(&device->lock);
+	rc = __send_session_cmd(sess, HFI_CMD_SESSION_STOP);
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int __session_etb(struct hal_session *session,
+		struct vidc_frame_data *input_frame, bool relaxed)
+{
+	int rc = 0;
+	struct venus_hfi_device *device = session->device;
+
+	if (session->is_decoder) {
+		struct hfi_cmd_session_empty_buffer_compressed_packet pkt;
+
+		rc = call_hfi_pkt_op(device, session_etb_decoder,
+				&pkt, session, input_frame);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"Session etb decoder: failed to create pkt\n");
+			goto err_create_pkt;
+		}
+
+		if (!relaxed)
+			rc = __iface_cmdq_write(session->device, &pkt);
+		else
+			rc = __iface_cmdq_write_relaxed(session->device,
+					&pkt, NULL);
+		if (rc)
+			goto err_create_pkt;
+	} else {
+		struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet
+			pkt;
+
+		rc = call_hfi_pkt_op(device, session_etb_encoder,
+					 &pkt, session, input_frame);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"Session etb encoder: failed to create pkt\n");
+			goto err_create_pkt;
+		}
+
+		if (!relaxed)
+			rc = __iface_cmdq_write(session->device, &pkt);
+		else
+			rc = __iface_cmdq_write_relaxed(session->device,
+					&pkt, NULL);
+		if (rc)
+			goto err_create_pkt;
+	}
+
+err_create_pkt:
+	return rc;
+}
+
+static int venus_hfi_session_etb(void *sess,
+				struct vidc_frame_data *input_frame)
+{
+	int rc = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device || !input_frame) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+	rc = __session_etb(session, input_frame, false);
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int __session_ftb(struct hal_session *session,
+		struct vidc_frame_data *output_frame, bool relaxed)
+{
+	int rc = 0;
+	struct venus_hfi_device *device = session->device;
+	struct hfi_cmd_session_fill_buffer_packet pkt;
+
+	rc = call_hfi_pkt_op(device, session_ftb,
+			&pkt, session, output_frame);
+	if (rc) {
+		dprintk(VIDC_ERR, "Session ftb: failed to create pkt\n");
+		goto err_create_pkt;
+	}
+
+	if (!relaxed)
+		rc = __iface_cmdq_write(session->device, &pkt);
+	else
+		rc = __iface_cmdq_write_relaxed(session->device,
+				&pkt, NULL);
+
+err_create_pkt:
+	return rc;
+}
+
+static int venus_hfi_session_ftb(void *sess,
+				struct vidc_frame_data *output_frame)
+{
+	int rc = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device || !output_frame) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+	rc = __session_ftb(session, output_frame, false);
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_process_batch(void *sess,
+		int num_etbs, struct vidc_frame_data etbs[],
+		int num_ftbs, struct vidc_frame_data ftbs[])
+{
+	int rc = 0, c = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+	struct hfi_cmd_session_sync_process_packet pkt;
+
+	if (!session || !session->device) {
+		dprintk(VIDC_ERR, "%s: Invalid Params\n", __func__);
+		return -EINVAL;
+	}
+
+	device = session->device;
+
+	mutex_lock(&device->lock);
+	for (c = 0; c < num_ftbs; ++c) {
+		rc = __session_ftb(session, &ftbs[c], true);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to queue batched ftb: %d\n",
+					rc);
+			goto err_etbs_and_ftbs;
+		}
+	}
+
+	for (c = 0; c < num_etbs; ++c) {
+		rc = __session_etb(session, &etbs[c], true);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to queue batched etb: %d\n",
+					rc);
+			goto err_etbs_and_ftbs;
+		}
+	}
+
+	rc = call_hfi_pkt_op(device, session_sync_process, &pkt, session);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to create sync packet\n");
+		goto err_etbs_and_ftbs;
+	}
+
+	if (__iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+
+err_etbs_and_ftbs:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_parse_seq_hdr(void *sess,
+					struct vidc_seq_hdr *seq_hdr)
+{
+	struct hfi_cmd_session_parse_sequence_header_packet *pkt;
+	int rc = 0;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device || !seq_hdr) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	pkt = (struct hfi_cmd_session_parse_sequence_header_packet *)packet;
+	rc = call_hfi_pkt_op(device, session_parse_seq_header,
+			pkt, session, seq_hdr);
+	if (rc) {
+		dprintk(VIDC_ERR,
+		"Session parse seq hdr: failed to create pkt\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_get_seq_hdr(void *sess,
+				struct vidc_seq_hdr *seq_hdr)
+{
+	struct hfi_cmd_session_get_sequence_header_packet *pkt;
+	int rc = 0;
+	u8 packet[VIDC_IFACEQ_VAR_SMALL_PKT_SIZE];
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device || !seq_hdr) {
+		dprintk(VIDC_ERR, "Invalid Params\n");
+		return -EINVAL;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	pkt = (struct hfi_cmd_session_get_sequence_header_packet *)packet;
+	rc = call_hfi_pkt_op(device, session_get_seq_hdr,
+			pkt, session, seq_hdr);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Session get seq hdr: failed to create pkt\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(session->device, pkt))
+		rc = -ENOTEMPTY;
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_get_buf_req(void *sess)
+{
+	struct hfi_cmd_session_get_property_packet pkt;
+	int rc = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device) {
+		dprintk(VIDC_ERR, "invalid session");
+		return -ENODEV;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	rc = call_hfi_pkt_op(device, session_get_buf_req,
+			&pkt, session);
+	if (rc) {
+		dprintk(VIDC_ERR,
+				"Session get buf req: failed to create pkt\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int venus_hfi_session_flush(void *sess, enum hal_flush flush_mode)
+{
+	struct hfi_cmd_session_flush_packet pkt;
+	int rc = 0;
+	struct hal_session *session = sess;
+	struct venus_hfi_device *device;
+
+	if (!session || !session->device) {
+		dprintk(VIDC_ERR, "invalid session");
+		return -ENODEV;
+	}
+
+	device = session->device;
+	mutex_lock(&device->lock);
+
+	rc = call_hfi_pkt_op(device, session_flush,
+			&pkt, session, flush_mode);
+	if (rc) {
+		dprintk(VIDC_ERR, "Session flush: failed to create pkt\n");
+		goto err_create_pkt;
+	}
+
+	if (__iface_cmdq_write(session->device, &pkt))
+		rc = -ENOTEMPTY;
+err_create_pkt:
+	mutex_unlock(&device->lock);
+	return rc;
+}
+
+static int __check_core_registered(struct hal_device_data core,
+		phys_addr_t fw_addr, u8 *reg_addr, u32 reg_size,
+		phys_addr_t irq)
+{
+	struct venus_hfi_device *device;
+	struct list_head *curr, *next;
+
+	if (core.dev_count) {
+		list_for_each_safe(curr, next, &core.dev_head) {
+			device = list_entry(curr,
+				struct venus_hfi_device, list);
+			if (device && device->hal_data->irq == irq &&
+				(CONTAINS(device->hal_data->
+						firmware_base,
+						FIRMWARE_SIZE, fw_addr) ||
+				CONTAINS(fw_addr, FIRMWARE_SIZE,
+						device->hal_data->
+						firmware_base) ||
+				CONTAINS(device->hal_data->
+						register_base,
+						reg_size, reg_addr) ||
+				CONTAINS(reg_addr, reg_size,
+						device->hal_data->
+						register_base) ||
+				OVERLAPS(device->hal_data->
+						register_base,
+						reg_size, reg_addr, reg_size) ||
+				OVERLAPS(reg_addr, reg_size,
+						device->hal_data->
+						register_base, reg_size) ||
+				OVERLAPS(device->hal_data->
+						firmware_base,
+						FIRMWARE_SIZE, fw_addr,
+						FIRMWARE_SIZE) ||
+				OVERLAPS(fw_addr, FIRMWARE_SIZE,
+						device->hal_data->
+						firmware_base,
+						FIRMWARE_SIZE))) {
+				return 0;
+			}
+
+			dprintk(VIDC_INFO, "Device not registered\n");
+			return -EINVAL;
+		}
+	} else {
+		dprintk(VIDC_INFO, "no device Registered\n");
+	}
+
+	return -EINVAL;
+}
+
+static void __process_fatal_error(
+		struct venus_hfi_device *device)
+{
+	struct msm_vidc_cb_cmd_done cmd_done = {0};
+
+	cmd_done.device_id = device->device_id;
+	device->callback(HAL_SYS_ERROR, &cmd_done);
+}
+
+static int __prepare_pc(struct venus_hfi_device *device)
+{
+	int rc = 0;
+	struct hfi_cmd_sys_pc_prep_packet pkt;
+
+	rc = call_hfi_pkt_op(device, sys_pc_prep, &pkt);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to create sys pc prep pkt\n");
+		goto err_pc_prep;
+	}
+
+	if (__iface_cmdq_write(device, &pkt))
+		rc = -ENOTEMPTY;
+	if (rc)
+		dprintk(VIDC_ERR, "Failed to prepare venus for power off");
+err_pc_prep:
+	return rc;
+}
+
+static void venus_hfi_pm_handler(struct work_struct *work)
+{
+	int rc = 0;
+	u32 wfi_status = 0, idle_status = 0, pc_ready = 0;
+	int count = 0;
+	const int max_tries = 5;
+	struct venus_hfi_device *device = list_first_entry(
+			&hal_ctxt.dev_head, struct venus_hfi_device, list);
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s: NULL device\n", __func__);
+		return;
+	}
+
+	/*
+	 * It is ok to check this variable outside the lock since
+	 * it is being updated in this context only
+	 */
+	if (device->skip_pc_count >= VIDC_MAX_PC_SKIP_COUNT) {
+		dprintk(VIDC_WARN, "Failed to PC for %d times\n",
+				device->skip_pc_count);
+		device->skip_pc_count = 0;
+		__process_fatal_error(device);
+		return;
+	}
+	mutex_lock(&device->lock);
+	if (!device->power_enabled) {
+		dprintk(VIDC_DBG, "%s: Power already disabled\n",
+				__func__);
+		goto exit;
+	}
+
+	rc = __core_in_valid_state(device);
+	if (!rc) {
+		dprintk(VIDC_WARN,
+				"Core is in bad state, Skipping power collapse\n");
+		goto skip_power_off;
+	}
+	pc_ready = __read_register(device, VIDC_CPU_CS_SCIACMDARG0) &
+		VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY;
+	if (!pc_ready) {
+		wfi_status = __read_register(device,
+				VIDC_WRAPPER_CPU_STATUS);
+		idle_status = __read_register(device,
+				VIDC_CPU_CS_SCIACMDARG0);
+		if (!(wfi_status & BIT(0)) ||
+				!(idle_status & BIT(30))) {
+			dprintk(VIDC_WARN, "Skipping PC\n");
+			goto skip_power_off;
+		}
+
+		rc = __prepare_pc(device);
+		if (rc) {
+			dprintk(VIDC_WARN, "Failed PC %d\n", rc);
+			goto skip_power_off;
+		}
+
+		while (count < max_tries) {
+			wfi_status = __read_register(device,
+					VIDC_WRAPPER_CPU_STATUS);
+			pc_ready = __read_register(device,
+					VIDC_CPU_CS_SCIACMDARG0);
+			if ((wfi_status & BIT(0)) && (pc_ready &
+				VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY))
+				break;
+			usleep_range(1000, 1500);
+			count++;
+		}
+
+		if (count == max_tries) {
+			dprintk(VIDC_ERR,
+					"Skip PC. Core is not in right state (%#x, %#x)\n",
+					wfi_status, pc_ready);
+			goto skip_power_off;
+		}
+	}
+
+	rc = __suspend(device);
+	if (rc)
+		dprintk(VIDC_ERR, "Failed venus power off\n");
+
+	/* Cancel pending delayed works if any */
+	cancel_delayed_work(&venus_hfi_pm_work);
+	device->skip_pc_count = 0;
+
+	mutex_unlock(&device->lock);
+	return;
+
+skip_power_off:
+	device->skip_pc_count++;
+	dprintk(VIDC_WARN, "Skip PC(%d, %#x, %#x, %#x)\n",
+		device->skip_pc_count, wfi_status, idle_status, pc_ready);
+	queue_delayed_work(device->venus_pm_workq,
+			&venus_hfi_pm_work,
+			msecs_to_jiffies(msm_vidc_pwr_collapse_delay));
+exit:
+	mutex_unlock(&device->lock);
+}
+
+static void __process_sys_error(struct venus_hfi_device *device)
+{
+	struct hfi_sfr_struct *vsfr = NULL;
+
+	__set_state(device, VENUS_STATE_DEINIT);
+
+	/*
+	 * Once SYS_ERROR received from HW, it is safe to halt the AXI.
+	 * With SYS_ERROR, Venus FW may have crashed and HW might be
+	 * active and causing unnecessary transactions. Hence it is
+	 * safe to stop all AXI transactions from venus sub-system.
+	 */
+	if (__halt_axi(device))
+		dprintk(VIDC_WARN, "Failed to halt AXI after SYS_ERROR\n");
+
+	vsfr = (struct hfi_sfr_struct *)device->sfr.align_virtual_addr;
+	if (vsfr) {
+		void *p = memchr(vsfr->rg_data, '\0', vsfr->bufSize);
+		/*
+		 * SFR isn't guaranteed to be NULL terminated
+		 * since SYS_ERROR indicates that Venus is in the
+		 * process of crashing.
+		 */
+		if (p == NULL)
+			vsfr->rg_data[vsfr->bufSize - 1] = '\0';
+
+		dprintk(VIDC_ERR, "SFR Message from FW: %s\n",
+				vsfr->rg_data);
+	}
+}
+
+static void __flush_debug_queue(struct venus_hfi_device *device, u8 *packet)
+{
+	bool local_packet = false;
+	enum vidc_msg_prio log_level = VIDC_FW;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "%s: Invalid params\n", __func__);
+		return;
+	}
+
+	if (!packet) {
+		packet = kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_TEMPORARY);
+		if (!packet) {
+			dprintk(VIDC_ERR, "In %s() Fail to allocate mem\n",
+				__func__);
+			return;
+		}
+
+		local_packet = true;
+
+		/*
+		 * Local packek is used when something FATAL occurred.
+		 * It is good to print these logs by default.
+		 */
+
+		log_level = VIDC_ERR;
+	}
+
+	while (!__iface_dbgq_read(device, packet)) {
+		struct hfi_msg_sys_coverage_packet *pkt =
+			(struct hfi_msg_sys_coverage_packet *) packet;
+
+		if (pkt->packet_type == HFI_MSG_SYS_COV) {
+			int stm_size = 0;
+
+			stm_size = stm_log_inv_ts(0, 0,
+				pkt->rg_msg_data, pkt->msg_size);
+			if (stm_size == 0)
+				dprintk(VIDC_ERR,
+					"In %s, stm_log returned size of 0\n",
+					__func__);
+		} else {
+			struct hfi_msg_sys_debug_packet *pkt =
+				(struct hfi_msg_sys_debug_packet *) packet;
+			dprintk(log_level, "%s", pkt->rg_msg_data);
+		}
+	}
+
+	if (local_packet)
+		kfree(packet);
+}
+
+static struct hal_session *__get_session(struct venus_hfi_device *device,
+		u32 session_id)
+{
+	struct hal_session *temp = NULL;
+
+	list_for_each_entry(temp, &device->sess_head, list) {
+		if (session_id == hash32_ptr(temp))
+			return temp;
+	}
+
+	return NULL;
+}
+
+static int __response_handler(struct venus_hfi_device *device)
+{
+	struct msm_vidc_cb_info *packets;
+	int packet_count = 0;
+	u8 *raw_packet = NULL;
+	bool requeue_pm_work = true;
+
+	if (!device || device->state != VENUS_STATE_INIT)
+		return 0;
+
+	packets = device->response_pkt;
+
+	raw_packet = device->raw_packet;
+
+	if (!raw_packet || !packets) {
+		dprintk(VIDC_ERR,
+			"%s: Invalid args : Res packet = %p, Raw packet = %p\n",
+			__func__, packets, raw_packet);
+		return 0;
+	}
+
+	if (device->intr_status & VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK) {
+		struct hfi_sfr_struct *vsfr = (struct hfi_sfr_struct *)
+			device->sfr.align_virtual_addr;
+		struct msm_vidc_cb_info info = {
+			.response_type = HAL_SYS_WATCHDOG_TIMEOUT,
+			.response.cmd = {
+				.device_id = device->device_id,
+			}
+		};
+
+		if (vsfr)
+			dprintk(VIDC_ERR, "SFR Message from FW: %s\n",
+					vsfr->rg_data);
+
+		dprintk(VIDC_ERR, "Received watchdog timeout\n");
+		packets[packet_count++] = info;
+		goto exit;
+	}
+
+	/* Bleed the msg queue dry of packets */
+	while (!__iface_msgq_read(device, raw_packet)) {
+		void **session_id = NULL;
+		struct msm_vidc_cb_info *info = &packets[packet_count++];
+		struct vidc_hal_sys_init_done sys_init_done = {0};
+		int rc = 0;
+
+		rc = hfi_process_msg_packet(device->device_id,
+			(struct vidc_hal_msg_pkt_hdr *)raw_packet, info);
+		if (rc) {
+			dprintk(VIDC_WARN,
+					"Corrupt/unknown packet found, discarding\n");
+			--packet_count;
+			continue;
+		}
+
+		/* Process the packet types that we're interested in */
+		switch (info->response_type) {
+		case HAL_SYS_ERROR:
+			__process_sys_error(device);
+			break;
+		case HAL_SYS_RELEASE_RESOURCE_DONE:
+			dprintk(VIDC_DBG, "Received SYS_RELEASE_RESOURCE\n");
+			break;
+		case HAL_SYS_INIT_DONE:
+			dprintk(VIDC_DBG, "Received SYS_INIT_DONE\n");
+			/* Video driver intentionally does not unset
+			 * IMEM on venus to simplify power collapse.
+			 */
+			if (__set_imem(device, &device->resources.imem))
+				dprintk(VIDC_WARN,
+				"Failed to set IMEM. Performance will be impacted\n");
+			sys_init_done.capabilities =
+				device->sys_init_capabilities;
+			hfi_process_sys_init_done_prop_read(
+				(struct hfi_msg_sys_init_done_packet *)
+					raw_packet, &sys_init_done);
+			info->response.cmd.data.sys_init_done = sys_init_done;
+			break;
+		case HAL_SESSION_LOAD_RESOURCE_DONE:
+			/*
+			 * Work around for H/W bug, need to re-program these
+			 * registers as part of a handshake agreement with the
+			 * firmware.  This strictly only needs to be done for
+			 * decoder secure sessions, but there's no harm in doing
+			 * so for all sessions as it's at worst a NO-OP.
+			 */
+			__set_threshold_registers(device);
+			break;
+		default:
+			break;
+		}
+
+		/* For session-related packets, validate session */
+		switch (info->response_type) {
+		case HAL_SESSION_LOAD_RESOURCE_DONE:
+		case HAL_SESSION_INIT_DONE:
+		case HAL_SESSION_END_DONE:
+		case HAL_SESSION_ABORT_DONE:
+		case HAL_SESSION_START_DONE:
+		case HAL_SESSION_STOP_DONE:
+		case HAL_SESSION_FLUSH_DONE:
+		case HAL_SESSION_SUSPEND_DONE:
+		case HAL_SESSION_RESUME_DONE:
+		case HAL_SESSION_SET_PROP_DONE:
+		case HAL_SESSION_GET_PROP_DONE:
+		case HAL_SESSION_PARSE_SEQ_HDR_DONE:
+		case HAL_SESSION_RELEASE_BUFFER_DONE:
+		case HAL_SESSION_RELEASE_RESOURCE_DONE:
+		case HAL_SESSION_PROPERTY_INFO:
+			session_id = &info->response.cmd.session_id;
+			break;
+		case HAL_SESSION_ERROR:
+		case HAL_SESSION_GET_SEQ_HDR_DONE:
+		case HAL_SESSION_ETB_DONE:
+		case HAL_SESSION_FTB_DONE:
+			session_id = &info->response.data.session_id;
+			break;
+		case HAL_SESSION_EVENT_CHANGE:
+			session_id = &info->response.event.session_id;
+			break;
+		case HAL_RESPONSE_UNUSED:
+		default:
+			session_id = NULL;
+			break;
+		}
+
+		/*
+		 * hfi_process_msg_packet provides a session_id that's a hashed
+		 * value of struct hal_session, we need to coerce the hashed
+		 * value back to pointer that we can use. Ideally, hfi_process\
+		 * _msg_packet should take care of this, but it doesn't have
+		 * required information for it
+		 */
+		if (session_id) {
+			struct hal_session *session = NULL;
+
+			WARN_ON(upper_32_bits((uintptr_t)*session_id) != 0);
+			session = __get_session(device,
+					(u32)(uintptr_t)*session_id);
+			if (!session) {
+				dprintk(VIDC_ERR,
+						"Received a packet (%#x) for an unrecognized session (%pK), discarding\n",
+						info->response_type,
+						*session_id);
+				--packet_count;
+				continue;
+			}
+
+			*session_id = session->session_id;
+		}
+
+		if (packet_count >= max_packets &&
+				__get_q_size(device, VIDC_IFACEQ_MSGQ_IDX)) {
+			dprintk(VIDC_WARN,
+					"Too many packets in message queue to handle at once, deferring read\n");
+			break;
+		}
+	}
+
+	if (requeue_pm_work && device->res->sw_power_collapsible) {
+		cancel_delayed_work(&venus_hfi_pm_work);
+		if (!queue_delayed_work(device->venus_pm_workq,
+			&venus_hfi_pm_work,
+			msecs_to_jiffies(msm_vidc_pwr_collapse_delay))) {
+			dprintk(VIDC_ERR, "PM work already scheduled\n");
+		}
+	}
+
+exit:
+	__flush_debug_queue(device, raw_packet);
+
+	return packet_count;
+}
+
+static void venus_hfi_core_work_handler(struct work_struct *work)
+{
+	struct venus_hfi_device *device = list_first_entry(
+		&hal_ctxt.dev_head, struct venus_hfi_device, list);
+	int num_responses = 0, i = 0;
+	u32 intr_status;
+
+	mutex_lock(&device->lock);
+
+	dprintk(VIDC_INFO, "Handling interrupt\n");
+
+	if (!__core_in_valid_state(device)) {
+		dprintk(VIDC_DBG, "%s - Core not in init state\n", __func__);
+		goto err_no_work;
+	}
+
+	if (!device->callback) {
+		dprintk(VIDC_ERR, "No interrupt callback function: %pK\n",
+				device);
+		goto err_no_work;
+	}
+
+	if (__resume(device)) {
+		dprintk(VIDC_ERR, "%s: Power enable failed\n", __func__);
+		goto err_no_work;
+	}
+
+	__core_clear_interrupt(device);
+	num_responses = __response_handler(device);
+
+err_no_work:
+
+	/* Keep the interrupt status before releasing device lock */
+	intr_status = device->intr_status;
+	mutex_unlock(&device->lock);
+
+	/*
+	 * Issue the callbacks outside of the locked contex to preserve
+	 * re-entrancy.
+	 */
+
+	for (i = 0; !IS_ERR_OR_NULL(device->response_pkt) &&
+		i < num_responses; ++i) {
+		struct msm_vidc_cb_info *r = &device->response_pkt[i];
+
+		device->callback(r->response_type, &r->response);
+	}
+
+	/* We need re-enable the irq which was disabled in ISR handler */
+	if (!(intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK))
+		enable_irq(device->hal_data->irq);
+
+	/*
+	 * XXX: Don't add any code beyond here.  Reacquiring locks after release
+	 * it above doesn't guarantee the atomicity that we're aiming for.
+	 */
+}
+
+static DECLARE_WORK(venus_hfi_work, venus_hfi_core_work_handler);
+
+static irqreturn_t venus_hfi_isr(int irq, void *dev)
+{
+	struct venus_hfi_device *device = dev;
+
+	dprintk(VIDC_INFO, "Received an interrupt %d\n", irq);
+	disable_irq_nosync(irq);
+	queue_work(device->vidc_workq, &venus_hfi_work);
+	return IRQ_HANDLED;
+}
+
+static int __init_regs_and_interrupts(struct venus_hfi_device *device,
+		struct msm_vidc_platform_resources *res)
+{
+	struct hal_data *hal = NULL;
+	int rc = 0;
+
+	rc = __check_core_registered(hal_ctxt, res->firmware_base,
+			(u8 *)(uintptr_t)res->register_base,
+			res->register_size, res->irq);
+	if (!rc) {
+		dprintk(VIDC_ERR, "Core present/Already added\n");
+		rc = -EEXIST;
+		goto err_core_init;
+	}
+
+	dprintk(VIDC_DBG, "HAL_DATA will be assigned now\n");
+	hal = (struct hal_data *)
+		kzalloc(sizeof(struct hal_data), GFP_KERNEL);
+	if (!hal) {
+		dprintk(VIDC_ERR, "Failed to alloc\n");
+		rc = -ENOMEM;
+		goto err_core_init;
+	}
+
+	hal->irq = res->irq;
+	hal->firmware_base = res->firmware_base;
+	hal->register_base = devm_ioremap_nocache(&res->pdev->dev,
+			res->register_base, res->register_size);
+	hal->register_size = res->register_size;
+	if (!hal->register_base) {
+		dprintk(VIDC_ERR,
+			"could not map reg addr %pa of size %d\n",
+			&res->register_base, res->register_size);
+		goto error_irq_fail;
+	}
+
+	device->hal_data = hal;
+	rc = request_irq(res->irq, venus_hfi_isr, IRQF_TRIGGER_HIGH,
+			"msm_vidc", device);
+	if (unlikely(rc)) {
+		dprintk(VIDC_ERR, "() :request_irq failed\n");
+		goto error_irq_fail;
+	}
+
+	disable_irq_nosync(res->irq);
+	dprintk(VIDC_INFO,
+		"firmware_base = %pa, register_base = %pa, register_size = %d\n",
+		&res->firmware_base, &res->register_base,
+		res->register_size);
+	return rc;
+
+error_irq_fail:
+	kfree(hal);
+err_core_init:
+	return rc;
+
+}
+
+static inline void __deinit_clocks(struct venus_hfi_device *device)
+{
+	struct clock_info *cl;
+
+	device->clk_freq = 0;
+	venus_hfi_for_each_clock_reverse(device, cl) {
+		if (cl->clk) {
+			clk_put(cl->clk);
+			cl->clk = NULL;
+		}
+	}
+}
+
+static inline int __init_clocks(struct venus_hfi_device *device)
+{
+	int rc = 0;
+	struct clock_info *cl = NULL;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return -EINVAL;
+	}
+
+	venus_hfi_for_each_clock(device, cl) {
+		int i = 0;
+
+		dprintk(VIDC_DBG, "%s: scalable? %d, count %d\n",
+				cl->name, cl->has_scaling, cl->count);
+		for (i = 0; i < cl->count; ++i) {
+			dprintk(VIDC_DBG,
+				"\tload = %d, freq = %d codecs supported %#x\n",
+				cl->load_freq_tbl[i].load,
+				cl->load_freq_tbl[i].freq,
+				cl->load_freq_tbl[i].supported_codecs);
+		}
+	}
+
+	venus_hfi_for_each_clock(device, cl) {
+		if (!cl->clk) {
+			cl->clk = clk_get(&device->res->pdev->dev, cl->name);
+			if (IS_ERR_OR_NULL(cl->clk)) {
+				dprintk(VIDC_ERR,
+					"Failed to get clock: %s\n", cl->name);
+				rc = PTR_ERR(cl->clk) ?: -EINVAL;
+				cl->clk = NULL;
+				goto err_clk_get;
+			}
+		}
+	}
+	device->clk_freq = 0;
+	return 0;
+
+err_clk_get:
+	__deinit_clocks(device);
+	return rc;
+}
+
+
+static inline void __disable_unprepare_clks(struct venus_hfi_device *device)
+{
+	struct clock_info *cl;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return;
+	}
+
+	venus_hfi_for_each_clock_reverse(device, cl) {
+		usleep_range(100, 500);
+		dprintk(VIDC_DBG, "Clock: %s disable and unprepare\n",
+				cl->name);
+		clk_disable_unprepare(cl->clk);
+	}
+}
+
+static inline int __prepare_enable_clks(struct venus_hfi_device *device)
+{
+	struct clock_info *cl = NULL, *cl_fail = NULL;
+	int rc = 0, c = 0;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return -EINVAL;
+	}
+
+	venus_hfi_for_each_clock(device, cl) {
+		/*
+		 * For the clocks we control, set the rate prior to preparing
+		 * them.  Since we don't really have a load at this point, scale
+		 * it to the lowest frequency possible
+		 */
+		if (cl->has_scaling)
+			clk_set_rate(cl->clk, clk_round_rate(cl->clk, 0));
+
+		if (cl->has_mem_retention) {
+			rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_PERIPH);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Failed set flag NORETAIN_PERIPH %s\n",
+					cl->name);
+			}
+
+			rc = clk_set_flags(cl->clk, CLKFLAG_NORETAIN_MEM);
+			if (rc) {
+				dprintk(VIDC_WARN,
+					"Failed set flag NORETAIN_MEM %s\n",
+					cl->name);
+			}
+		}
+
+		rc = clk_prepare_enable(cl->clk);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to enable clocks\n");
+			cl_fail = cl;
+			goto fail_clk_enable;
+		}
+
+		c++;
+		dprintk(VIDC_DBG, "Clock: %s prepared and enabled\n", cl->name);
+	}
+
+	__write_register(device, VIDC_WRAPPER_CLOCK_CONFIG, 0);
+	__write_register(device, VIDC_WRAPPER_CPU_CLOCK_CONFIG, 0);
+	return rc;
+
+fail_clk_enable:
+	venus_hfi_for_each_clock_reverse_continue(device, cl, c) {
+		usleep_range(100, 500);
+		dprintk(VIDC_ERR, "Clock: %s disable and unprepare\n",
+			cl->name);
+		clk_disable_unprepare(cl->clk);
+	}
+
+	return rc;
+}
+
+static void __deinit_bus(struct venus_hfi_device *device)
+{
+	struct bus_info *bus = NULL;
+
+	if (!device)
+		return;
+
+	kfree(device->bus_vote.data);
+	device->bus_vote = DEFAULT_BUS_VOTE;
+
+	venus_hfi_for_each_bus_reverse(device, bus) {
+		devfreq_remove_device(bus->devfreq);
+		bus->devfreq = NULL;
+		dev_set_drvdata(bus->dev, NULL);
+
+		msm_bus_scale_unregister(bus->client);
+		bus->client = NULL;
+	}
+}
+
+static int __init_bus(struct venus_hfi_device *device)
+{
+	struct bus_info *bus = NULL;
+	int rc = 0;
+
+	if (!device)
+		return -EINVAL;
+
+	venus_hfi_for_each_bus(device, bus) {
+		struct devfreq_dev_profile profile = {
+			.initial_freq = 0,
+			.polling_ms = INT_MAX,
+			.freq_table = NULL,
+			.max_state = 0,
+			.target = __devfreq_target,
+			.get_dev_status = __devfreq_get_status,
+			.exit = NULL,
+		};
+
+		/*
+		 * This is stupid, but there's no other easy way to ahold
+		 * of struct bus_info in venus_hfi_devfreq_*()
+		 */
+		WARN(dev_get_drvdata(bus->dev), "%s's drvdata already set\n",
+				dev_name(bus->dev));
+		dev_set_drvdata(bus->dev, device);
+
+		bus->client = msm_bus_scale_register(bus->master, bus->slave,
+				bus->name, false);
+		if (IS_ERR_OR_NULL(bus->client)) {
+			rc = PTR_ERR(bus->client) ?: -EBADHANDLE;
+			dprintk(VIDC_ERR, "Failed to register bus %s: %d\n",
+					bus->name, rc);
+			bus->client = NULL;
+			goto err_add_dev;
+		}
+
+		bus->devfreq_prof = profile;
+		bus->devfreq = devfreq_add_device(bus->dev,
+				&bus->devfreq_prof, bus->governor, NULL);
+		if (IS_ERR_OR_NULL(bus->devfreq)) {
+			rc = PTR_ERR(bus->devfreq) ?: -EBADHANDLE;
+			dprintk(VIDC_ERR,
+					"Failed to add devfreq device for bus %s and governor %s: %d\n",
+					bus->name, bus->governor, rc);
+			bus->devfreq = NULL;
+			goto err_add_dev;
+		}
+
+		/*
+		 * Devfreq starts monitoring immediately, since we are just
+		 * initializing stuff at this point, force it to suspend
+		 */
+		devfreq_suspend_device(bus->devfreq);
+	}
+
+	device->bus_vote = DEFAULT_BUS_VOTE;
+	return 0;
+
+err_add_dev:
+	__deinit_bus(device);
+	return rc;
+}
+
+static void __deinit_regulators(struct venus_hfi_device *device)
+{
+	struct regulator_info *rinfo = NULL;
+
+	venus_hfi_for_each_regulator_reverse(device, rinfo) {
+		if (rinfo->regulator) {
+			regulator_put(rinfo->regulator);
+			rinfo->regulator = NULL;
+		}
+	}
+}
+
+static int __init_regulators(struct venus_hfi_device *device)
+{
+	int rc = 0;
+	struct regulator_info *rinfo = NULL;
+
+	venus_hfi_for_each_regulator(device, rinfo) {
+		rinfo->regulator = regulator_get(&device->res->pdev->dev,
+				rinfo->name);
+		if (IS_ERR_OR_NULL(rinfo->regulator)) {
+			rc = PTR_ERR(rinfo->regulator) ?: -EBADHANDLE;
+			dprintk(VIDC_ERR, "Failed to get regulator: %s\n",
+					rinfo->name);
+			rinfo->regulator = NULL;
+			goto err_reg_get;
+		}
+	}
+
+	return 0;
+
+err_reg_get:
+	__deinit_regulators(device);
+	return rc;
+}
+
+static int __init_resources(struct venus_hfi_device *device,
+				struct msm_vidc_platform_resources *res)
+{
+	int rc = 0;
+
+	rc = __init_regulators(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to get all regulators\n");
+		return -ENODEV;
+	}
+
+	rc = __init_clocks(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to init clocks\n");
+		rc = -ENODEV;
+		goto err_init_clocks;
+	}
+
+	rc = __init_bus(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to init bus: %d\n", rc);
+		goto err_init_bus;
+	}
+
+	device->sys_init_capabilities =
+		kzalloc(sizeof(struct msm_vidc_capability)
+		* VIDC_MAX_SESSIONS, GFP_TEMPORARY);
+
+	return rc;
+
+err_init_bus:
+	__deinit_clocks(device);
+err_init_clocks:
+	__deinit_regulators(device);
+	return rc;
+}
+
+static void __deinit_resources(struct venus_hfi_device *device)
+{
+	__deinit_bus(device);
+	__deinit_clocks(device);
+	__deinit_regulators(device);
+	kfree(device->sys_init_capabilities);
+	device->sys_init_capabilities = NULL;
+}
+
+static int __protect_cp_mem(struct venus_hfi_device *device)
+{
+	struct tzbsp_memprot memprot;
+	unsigned int resp = 0;
+	int rc = 0;
+	struct context_bank_info *cb;
+	struct scm_desc desc = {0};
+
+	if (!device)
+		return -EINVAL;
+
+	memprot.cp_start = 0x0;
+	memprot.cp_size = 0x0;
+	memprot.cp_nonpixel_start = 0x0;
+	memprot.cp_nonpixel_size = 0x0;
+
+	list_for_each_entry(cb, &device->res->context_banks, list) {
+		if (!strcmp(cb->name, "venus_ns")) {
+			desc.args[1] = memprot.cp_size =
+				cb->addr_range.start;
+			dprintk(VIDC_DBG, "%s memprot.cp_size: %#x\n",
+				__func__, memprot.cp_size);
+		}
+
+		if (!strcmp(cb->name, "venus_sec_non_pixel")) {
+			desc.args[2] = memprot.cp_nonpixel_start =
+				cb->addr_range.start;
+			desc.args[3] = memprot.cp_nonpixel_size =
+				cb->addr_range.size;
+			dprintk(VIDC_DBG,
+				"%s memprot.cp_nonpixel_start: %#x size: %#x\n",
+				__func__, memprot.cp_nonpixel_start,
+				memprot.cp_nonpixel_size);
+		}
+	}
+
+	if (!is_scm_armv8()) {
+		rc = scm_call(SCM_SVC_MP, TZBSP_MEM_PROTECT_VIDEO_VAR, &memprot,
+			sizeof(memprot), &resp, sizeof(resp));
+	} else {
+		desc.arginfo = SCM_ARGS(4);
+		rc = scm_call2(SCM_SIP_FNID(SCM_SVC_MP,
+			       TZBSP_MEM_PROTECT_VIDEO_VAR), &desc);
+		resp = desc.ret[0];
+	}
+
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to protect memory(%d) response: %d\n",
+				rc, resp);
+	}
+
+	trace_venus_hfi_var_done(
+		memprot.cp_start, memprot.cp_size,
+		memprot.cp_nonpixel_start, memprot.cp_nonpixel_size);
+	return rc;
+}
+
+static int __disable_regulator(struct regulator_info *rinfo)
+{
+	int rc = 0;
+
+	dprintk(VIDC_DBG, "Disabling regulator %s\n", rinfo->name);
+
+	/*
+	 * This call is needed. Driver needs to acquire the control back
+	 * from HW in order to disable the regualtor. Else the behavior
+	 * is unknown.
+	 */
+
+	rc = __acquire_regulator(rinfo);
+	if (rc) {
+		/*
+		 * This is somewhat fatal, but nothing we can do
+		 * about it. We can't disable the regulator w/o
+		 * getting it back under s/w control
+		 */
+		dprintk(VIDC_WARN,
+			"Failed to acquire control on %s\n",
+			rinfo->name);
+
+		goto disable_regulator_failed;
+	}
+
+	rc = regulator_disable(rinfo->regulator);
+	if (rc) {
+		dprintk(VIDC_WARN,
+			"Failed to disable %s: %d\n",
+			rinfo->name, rc);
+		goto disable_regulator_failed;
+	}
+
+	return 0;
+disable_regulator_failed:
+
+	/* Bring attention to this issue */
+	WARN_ON(1);
+	return rc;
+}
+
+static int __enable_hw_power_collapse(struct venus_hfi_device *device)
+{
+	int rc = 0;
+
+	if (!msm_vidc_fw_low_power_mode) {
+		dprintk(VIDC_DBG, "Not enabling hardware power collapse\n");
+		return 0;
+	}
+
+	rc = __hand_off_regulators(device);
+	if (rc)
+		dprintk(VIDC_WARN,
+			"%s : Failed to enable HW power collapse %d\n",
+				__func__, rc);
+	return rc;
+}
+
+static int __enable_regulators(struct venus_hfi_device *device)
+{
+	int rc = 0, c = 0;
+	struct regulator_info *rinfo;
+
+	dprintk(VIDC_DBG, "Enabling regulators\n");
+
+	venus_hfi_for_each_regulator(device, rinfo) {
+		rc = regulator_enable(rinfo->regulator);
+		if (rc) {
+			dprintk(VIDC_ERR,
+					"Failed to enable %s: %d\n",
+					rinfo->name, rc);
+			goto err_reg_enable_failed;
+		}
+
+		dprintk(VIDC_DBG, "Enabled regulator %s\n",
+				rinfo->name);
+		c++;
+	}
+
+	return 0;
+
+err_reg_enable_failed:
+	venus_hfi_for_each_regulator_reverse_continue(device, rinfo, c)
+		__disable_regulator(rinfo);
+
+	return rc;
+}
+
+static int __disable_regulators(struct venus_hfi_device *device)
+{
+	struct regulator_info *rinfo;
+	int rc = 0;
+
+	dprintk(VIDC_DBG, "Disabling regulators\n");
+
+	venus_hfi_for_each_regulator_reverse(device, rinfo)
+		__disable_regulator(rinfo);
+
+	return rc;
+}
+
+static int __venus_power_on(struct venus_hfi_device *device)
+{
+	int rc = 0;
+
+	if (device->power_enabled)
+		return 0;
+
+	device->power_enabled = true;
+	/* Vote for all hardware resources */
+	rc = __vote_buses(device, device->bus_vote.data,
+			device->bus_vote.data_count);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to vote buses, err: %d\n", rc);
+		goto fail_vote_buses;
+	}
+
+	rc = __alloc_imem(device, device->res->imem_size);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to allocate IMEM\n");
+		goto fail_alloc_imem;
+	}
+
+	rc = __enable_regulators(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to enable GDSC, err = %d\n", rc);
+		goto fail_enable_gdsc;
+	}
+
+	rc = __prepare_enable_clks(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to enable clocks: %d\n", rc);
+		goto fail_enable_clks;
+	}
+
+	rc = __scale_clocks(device, 0, NULL, 0);
+	if (rc) {
+		dprintk(VIDC_WARN,
+				"Failed to scale clocks, performance might be affected\n");
+		rc = 0;
+	}
+
+	/*
+	 * Re-program all of the registers that get reset as a result of
+	 * regulator_disable() and _enable()
+	 */
+	__set_registers(device);
+
+	__write_register(device, VIDC_WRAPPER_INTR_MASK,
+			VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK);
+	device->intr_status = 0;
+	enable_irq(device->hal_data->irq);
+
+	/*
+	 * Hand off control of regulators to h/w _after_ enabling clocks.
+	 * Note that the GDSC will turn off when switching from normal
+	 * (s/w triggered) to fast (HW triggered) unless the h/w vote is
+	 * present. Since Venus isn't up yet, the GDSC will be off briefly.
+	 */
+	if (__enable_hw_power_collapse(device))
+		dprintk(VIDC_ERR, "Failed to enabled inter-frame PC\n");
+
+	return rc;
+
+fail_enable_clks:
+	__disable_regulators(device);
+fail_enable_gdsc:
+	__free_imem(device);
+fail_alloc_imem:
+	__unvote_buses(device);
+fail_vote_buses:
+	device->power_enabled = false;
+	return rc;
+}
+
+static void __venus_power_off(struct venus_hfi_device *device, bool halt_axi)
+{
+	if (!device->power_enabled)
+		return;
+
+	if (!(device->intr_status & VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK))
+		disable_irq_nosync(device->hal_data->irq);
+	device->intr_status = 0;
+
+	/* Halt the AXI to make sure there are no pending transactions.
+	 * Clocks should be unprepared after making sure axi is halted.
+	 */
+	if (halt_axi && __halt_axi(device))
+		dprintk(VIDC_WARN, "Failed to halt AXI\n");
+
+	__disable_unprepare_clks(device);
+	if (__disable_regulators(device))
+		dprintk(VIDC_WARN, "Failed to disable regulators\n");
+
+	__free_imem(device);
+
+	if (__unvote_buses(device))
+		dprintk(VIDC_WARN, "Failed to unvote for buses\n");
+	device->power_enabled = false;
+}
+
+static inline int __suspend(struct venus_hfi_device *device)
+{
+	int rc = 0;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return -EINVAL;
+	} else if (!device->power_enabled) {
+		dprintk(VIDC_DBG, "Power already disabled\n");
+		return 0;
+	}
+
+	dprintk(VIDC_PROF, "Entering power collapse\n");
+
+	if (device->res->pm_qos_latency_us &&
+		pm_qos_request_active(&device->qos))
+		pm_qos_remove_request(&device->qos);
+
+	rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
+	if (rc) {
+		dprintk(VIDC_WARN, "Failed to suspend video core %d\n", rc);
+		goto err_tzbsp_suspend;
+	}
+
+	__venus_power_off(device, true);
+	dprintk(VIDC_PROF, "Venus power collapsed\n");
+	return rc;
+
+err_tzbsp_suspend:
+	return rc;
+}
+
+static inline int __resume(struct venus_hfi_device *device)
+{
+	int rc = 0;
+
+	if (!device) {
+		dprintk(VIDC_ERR, "Invalid params: %pK\n", device);
+		return -EINVAL;
+	} else if (device->power_enabled) {
+		dprintk(VIDC_DBG, "Power is already enabled\n");
+		goto exit;
+	} else if (!__core_in_valid_state(device)) {
+		dprintk(VIDC_DBG, "venus_hfi_device in deinit state.");
+		return -EINVAL;
+	}
+
+	dprintk(VIDC_PROF, "Resuming from power collapse\n");
+	rc = __venus_power_on(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to power on venus\n");
+		goto err_venus_power_on;
+	}
+
+	/* Reboot the firmware */
+	rc = __tzbsp_set_video_state(TZBSP_VIDEO_STATE_RESUME);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to resume video core %d\n", rc);
+		goto err_set_video_state;
+	}
+
+	__setup_ucregion_memory_map(device);
+	/* Wait for boot completion */
+	rc = __boot_firmware(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to reset venus core\n");
+		goto err_reset_core;
+	}
+
+	/*
+	 * Work around for H/W bug, need to reprogram these registers once
+	 * firmware is out reset
+	 */
+	__set_threshold_registers(device);
+
+	if (device->res->pm_qos_latency_us) {
+#ifdef CONFIG_SMP
+		device->qos.type = PM_QOS_REQ_AFFINE_IRQ;
+		device->qos.irq = device->hal_data->irq;
+#endif
+		pm_qos_add_request(&device->qos, PM_QOS_CPU_DMA_LATENCY,
+				device->res->pm_qos_latency_us);
+	}
+	dprintk(VIDC_PROF, "Resumed from power collapse\n");
+exit:
+	device->skip_pc_count = 0;
+	return rc;
+err_reset_core:
+	__tzbsp_set_video_state(TZBSP_VIDEO_STATE_SUSPEND);
+err_set_video_state:
+	__venus_power_off(device, true);
+err_venus_power_on:
+	dprintk(VIDC_ERR, "Failed to resume from power collapse\n");
+	return rc;
+}
+
+static int __load_fw(struct venus_hfi_device *device)
+{
+	int rc = 0;
+
+	/* Initialize resources */
+	rc = __init_resources(device, device->res);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to init resources: %d\n", rc);
+		goto fail_init_res;
+	}
+
+	rc = __initialize_packetization(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to initialize packetization\n");
+		goto fail_init_pkt;
+	}
+	trace_msm_v4l2_vidc_fw_load_start("msm_v4l2_vidc venus_fw load start");
+
+	rc = __venus_power_on(device);
+	if (rc) {
+		dprintk(VIDC_ERR, "Failed to power on venus in in load_fw\n");
+		goto fail_venus_power_on;
+	}
+
+	if ((!device->res->use_non_secure_pil && !device->res->firmware_base)
+			|| device->res->use_non_secure_pil) {
+		if (!device->resources.fw.cookie)
+			device->resources.fw.cookie =
+				subsystem_get_with_fwname("venus",
+				device->res->fw_name);
+
+		if (IS_ERR_OR_NULL(device->resources.fw.cookie)) {
+			dprintk(VIDC_ERR, "Failed to download firmware\n");
+			device->resources.fw.cookie = NULL;
+			rc = -ENOMEM;
+			goto fail_load_fw;
+		}
+	}
+
+	if (!device->res->use_non_secure_pil && !device->res->firmware_base) {
+		rc = __protect_cp_mem(device);
+		if (rc) {
+			dprintk(VIDC_ERR, "Failed to protect memory\n");
+			goto fail_protect_mem;
+		}
+	}
+	trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end");
+	return rc;
+fail_protect_mem:
+	if (device->resources.fw.cookie)
+		subsystem_put(device->resources.fw.cookie);
+	device->resources.fw.cookie = NULL;
+fail_load_fw:
+	__venus_power_off(device, true);
+fail_venus_power_on:
+fail_init_pkt:
+	__deinit_resources(device);
+fail_init_res:
+	trace_msm_v4l2_vidc_fw_load_end("msm_v4l2_vidc venus_fw load end");
+	return rc;
+}
+
+static void __unload_fw(struct venus_hfi_device *device)
+{
+	if (!device->resources.fw.cookie)
+		return;
+
+	cancel_delayed_work(&venus_hfi_pm_work);
+	if (device->state != VENUS_STATE_DEINIT)
+		flush_workqueue(device->venus_pm_workq);
+
+	__vote_buses(device, NULL, 0);
+	subsystem_put(device->resources.fw.cookie);
+	__interface_queues_release(device);
+	__venus_power_off(device, false);
+	device->resources.fw.cookie = NULL;
+	__deinit_resources(device);
+}
+
+static int venus_hfi_get_fw_info(void *dev, struct hal_fw_info *fw_info)
+{
+	int i = 0, j = 0;
+	struct venus_hfi_device *device = dev;
+	u32 smem_block_size = 0;
+	u8 *smem_table_ptr;
+	char version[VENUS_VERSION_LENGTH];
+	const u32 smem_image_index_venus = 14 * 128;
+
+	if (!device || !fw_info) {
+		dprintk(VIDC_ERR,
+			"%s Invalid parameter: device = %pK fw_info = %pK\n",
+			__func__, device, fw_info);
+		return -EINVAL;
+	}
+
+	mutex_lock(&device->lock);
+
+	smem_table_ptr = smem_get_entry(SMEM_IMAGE_VERSION_TABLE,
+			&smem_block_size, 0, SMEM_ANY_HOST_FLAG);
+	if (smem_table_ptr &&
+			((smem_image_index_venus +
+			  VENUS_VERSION_LENGTH) <= smem_block_size))
+		memcpy(version,
+			smem_table_ptr + smem_image_index_venus,
+			VENUS_VERSION_LENGTH);
+
+	while (version[i++] != 'V' && i < VENUS_VERSION_LENGTH)
+		;
+
+	if (i == VENUS_VERSION_LENGTH - 1) {
+		dprintk(VIDC_WARN, "Venus version string is not proper\n");
+		fw_info->version[0] = '\0';
+		goto fail_version_string;
+	}
+
+	for (i--; i < VENUS_VERSION_LENGTH && j < VENUS_VERSION_LENGTH - 1; i++)
+		fw_info->version[j++] = version[i];
+	fw_info->version[j] = '\0';
+
+fail_version_string:
+	dprintk(VIDC_DBG, "F/W version retrieved : %s\n", fw_info->version);
+	fw_info->base_addr = device->hal_data->firmware_base;
+	fw_info->register_base = device->res->register_base;
+	fw_info->register_size = device->hal_data->register_size;
+	fw_info->irq = device->hal_data->irq;
+
+	mutex_unlock(&device->lock);
+	return 0;
+}
+
+static int venus_hfi_get_core_capabilities(void *dev)
+{
+	struct venus_hfi_device *device = dev;
+	int rc = 0;
+
+	if (!device)
+		return -EINVAL;
+
+	mutex_lock(&device->lock);
+
+	rc = HAL_VIDEO_ENCODER_ROTATION_CAPABILITY |
+		HAL_VIDEO_ENCODER_SCALING_CAPABILITY |
+		HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY |
+		HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY;
+
+	mutex_unlock(&device->lock);
+
+	return rc;
+}
+
+static int __initialize_packetization(struct venus_hfi_device *device)
+{
+	int rc = 0;
+	const char *hfi_version;
+
+	if (!device || !device->res) {
+		dprintk(VIDC_ERR, "%s - invalid param\n", __func__);
+		return -EINVAL;
+	}
+
+	hfi_version = device->res->hfi_version;
+
+	if (!hfi_version) {
+		device->packetization_type = HFI_PACKETIZATION_LEGACY;
+	} else if (!strcmp(hfi_version, "3xx")) {
+		device->packetization_type = HFI_PACKETIZATION_3XX;
+	} else {
+		dprintk(VIDC_ERR, "Unsupported hfi version\n");
+		return -EINVAL;
+	}
+
+	device->pkt_ops = hfi_get_pkt_ops_handle(device->packetization_type);
+	if (!device->pkt_ops) {
+		rc = -EINVAL;
+		dprintk(VIDC_ERR, "Failed to get pkt_ops handle\n");
+	}
+
+	return rc;
+}
+
+static struct venus_hfi_device *__add_device(u32 device_id,
+			struct msm_vidc_platform_resources *res,
+			hfi_cmd_response_callback callback)
+{
+	struct venus_hfi_device *hdevice = NULL;
+	int rc = 0;
+
+	if (!res || !callback) {
+		dprintk(VIDC_ERR, "Invalid Parameters\n");
+		return NULL;
+	}
+
+	dprintk(VIDC_INFO, "entered , device_id: %d\n", device_id);
+
+	hdevice = (struct venus_hfi_device *)
+			kzalloc(sizeof(struct venus_hfi_device), GFP_KERNEL);
+	if (!hdevice) {
+		dprintk(VIDC_ERR, "failed to allocate new device\n");
+		goto exit;
+	}
+
+	hdevice->response_pkt = kmalloc_array(max_packets,
+				sizeof(*hdevice->response_pkt), GFP_KERNEL);
+	if (!hdevice->response_pkt) {
+		dprintk(VIDC_ERR, "failed to allocate response_pkt\n");
+		goto err_cleanup;
+	}
+
+	hdevice->raw_packet =
+		kzalloc(VIDC_IFACEQ_VAR_HUGE_PKT_SIZE, GFP_TEMPORARY);
+	if (!hdevice->raw_packet) {
+		dprintk(VIDC_ERR, "failed to allocate raw packet\n");
+		goto err_cleanup;
+	}
+
+	rc = __init_regs_and_interrupts(hdevice, res);
+	if (rc)
+		goto err_cleanup;
+
+	hdevice->res = res;
+	hdevice->device_id = device_id;
+	hdevice->callback = callback;
+
+	hdevice->vidc_workq = create_singlethread_workqueue(
+		"msm_vidc_workerq_venus");
+	if (!hdevice->vidc_workq) {
+		dprintk(VIDC_ERR, ": create vidc workq failed\n");
+		goto err_cleanup;
+	}
+
+	hdevice->venus_pm_workq = create_singlethread_workqueue(
+			"pm_workerq_venus");
+	if (!hdevice->venus_pm_workq) {
+		dprintk(VIDC_ERR, ": create pm workq failed\n");
+		goto err_cleanup;
+	}
+
+	if (!hal_ctxt.dev_count)
+		INIT_LIST_HEAD(&hal_ctxt.dev_head);
+
+	mutex_init(&hdevice->lock);
+	INIT_LIST_HEAD(&hdevice->list);
+	INIT_LIST_HEAD(&hdevice->sess_head);
+	list_add_tail(&hdevice->list, &hal_ctxt.dev_head);
+	hal_ctxt.dev_count++;
+
+	return hdevice;
+
+err_cleanup:
+	if (hdevice->vidc_workq)
+		destroy_workqueue(hdevice->vidc_workq);
+	kfree(hdevice->response_pkt);
+	kfree(hdevice->raw_packet);
+	kfree(hdevice);
+exit:
+	return NULL;
+}
+
+static struct venus_hfi_device *__get_device(u32 device_id,
+				struct msm_vidc_platform_resources *res,
+				hfi_cmd_response_callback callback)
+{
+	if (!res || !callback) {
+		dprintk(VIDC_ERR, "Invalid params: %pK %pK\n", res, callback);
+		return NULL;
+	}
+
+	return __add_device(device_id, res, callback);
+}
+
+void venus_hfi_delete_device(void *device)
+{
+	struct venus_hfi_device *close, *tmp, *dev;
+
+	if (!device)
+		return;
+
+	dev = (struct venus_hfi_device *) device;
+
+	mutex_lock(&dev->lock);
+	__iommu_detach(dev);
+	mutex_unlock(&dev->lock);
+
+	list_for_each_entry_safe(close, tmp, &hal_ctxt.dev_head, list) {
+		if (close->hal_data->irq == dev->hal_data->irq) {
+			hal_ctxt.dev_count--;
+			list_del(&close->list);
+			mutex_destroy(&close->lock);
+			destroy_workqueue(close->vidc_workq);
+			destroy_workqueue(close->venus_pm_workq);
+			free_irq(dev->hal_data->irq, close);
+			iounmap(dev->hal_data->register_base);
+			kfree(close->hal_data);
+			kfree(close->response_pkt);
+			kfree(close->raw_packet);
+			kfree(close);
+			break;
+		}
+	}
+}
+
+static void venus_init_hfi_callbacks(struct hfi_device *hdev)
+{
+	hdev->core_init = venus_hfi_core_init;
+	hdev->core_release = venus_hfi_core_release;
+	hdev->core_ping = venus_hfi_core_ping;
+	hdev->core_trigger_ssr = venus_hfi_core_trigger_ssr;
+	hdev->session_init = venus_hfi_session_init;
+	hdev->session_end = venus_hfi_session_end;
+	hdev->session_abort = venus_hfi_session_abort;
+	hdev->session_clean = venus_hfi_session_clean;
+	hdev->session_set_buffers = venus_hfi_session_set_buffers;
+	hdev->session_release_buffers = venus_hfi_session_release_buffers;
+	hdev->session_load_res = venus_hfi_session_load_res;
+	hdev->session_release_res = venus_hfi_session_release_res;
+	hdev->session_start = venus_hfi_session_start;
+	hdev->session_continue = venus_hfi_session_continue;
+	hdev->session_stop = venus_hfi_session_stop;
+	hdev->session_etb = venus_hfi_session_etb;
+	hdev->session_ftb = venus_hfi_session_ftb;
+	hdev->session_process_batch = venus_hfi_session_process_batch;
+	hdev->session_parse_seq_hdr = venus_hfi_session_parse_seq_hdr;
+	hdev->session_get_seq_hdr = venus_hfi_session_get_seq_hdr;
+	hdev->session_get_buf_req = venus_hfi_session_get_buf_req;
+	hdev->session_flush = venus_hfi_session_flush;
+	hdev->session_set_property = venus_hfi_session_set_property;
+	hdev->session_get_property = venus_hfi_session_get_property;
+	hdev->scale_clocks = venus_hfi_scale_clocks;
+	hdev->vote_bus = venus_hfi_vote_buses;
+	hdev->get_fw_info = venus_hfi_get_fw_info;
+	hdev->get_core_capabilities = venus_hfi_get_core_capabilities;
+	hdev->suspend = venus_hfi_suspend;
+	hdev->flush_debug_queue = venus_hfi_flush_debug_queue;
+	hdev->get_core_clock_rate = venus_hfi_get_core_clock_rate;
+	hdev->get_default_properties = venus_hfi_get_default_properties;
+}
+
+int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
+		struct msm_vidc_platform_resources *res,
+		hfi_cmd_response_callback callback)
+{
+	int rc = 0;
+
+	if (!hdev || !res || !callback) {
+		dprintk(VIDC_ERR, "Invalid params: %pK %pK %pK\n",
+			hdev, res, callback);
+		rc = -EINVAL;
+		goto err_venus_hfi_init;
+	}
+
+	hdev->hfi_device_data = __get_device(device_id, res, callback);
+
+	if (IS_ERR_OR_NULL(hdev->hfi_device_data)) {
+		rc = PTR_ERR(hdev->hfi_device_data) ?: -EINVAL;
+		goto err_venus_hfi_init;
+	}
+
+	venus_init_hfi_callbacks(hdev);
+
+err_venus_hfi_init:
+	return rc;
+}
+
diff --git a/drivers/media/platform/msm/vidc/venus_hfi.h b/drivers/media/platform/msm/vidc/venus_hfi.h
new file mode 100644
index 0000000..76ede70
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/venus_hfi.h
@@ -0,0 +1,271 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __H_VENUS_HFI_H__
+#define __H_VENUS_HFI_H__
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
+#include <linux/spinlock.h>
+#include "vmem/vmem.h"
+#include "vidc_hfi_api.h"
+#include "vidc_hfi_helper.h"
+#include "vidc_hfi_api.h"
+#include "vidc_hfi.h"
+#include "msm_vidc_resources.h"
+#include "hfi_packetization.h"
+
+#define HFI_MASK_QHDR_TX_TYPE			0xFF000000
+#define HFI_MASK_QHDR_RX_TYPE			0x00FF0000
+#define HFI_MASK_QHDR_PRI_TYPE			0x0000FF00
+#define HFI_MASK_QHDR_Q_ID_TYPE			0x000000FF
+#define HFI_Q_ID_HOST_TO_CTRL_CMD_Q		0x00
+#define HFI_Q_ID_CTRL_TO_HOST_MSG_Q		0x01
+#define HFI_Q_ID_CTRL_TO_HOST_DEBUG_Q	0x02
+#define HFI_MASK_QHDR_STATUS			0x000000FF
+
+#define VIDC_MAX_UNCOMPRESSED_FMT_PLANES	3
+
+#define VIDC_IFACEQ_NUMQ					3
+#define VIDC_IFACEQ_CMDQ_IDX				0
+#define VIDC_IFACEQ_MSGQ_IDX				1
+#define VIDC_IFACEQ_DBGQ_IDX				2
+#define VIDC_IFACEQ_MAX_BUF_COUNT			50
+#define VIDC_IFACE_MAX_PARALLEL_CLNTS		16
+#define VIDC_IFACEQ_DFLT_QHDR				0x01010000
+
+#define VIDC_MAX_NAME_LENGTH 64
+#define VIDC_MAX_PC_SKIP_COUNT 10
+struct hfi_queue_table_header {
+	u32 qtbl_version;
+	u32 qtbl_size;
+	u32 qtbl_qhdr0_offset;
+	u32 qtbl_qhdr_size;
+	u32 qtbl_num_q;
+	u32 qtbl_num_active_q;
+	void *device_addr;
+	char name[256];
+};
+
+struct hfi_queue_header {
+	u32 qhdr_status;
+	u32 qhdr_start_addr;
+	u32 qhdr_type;
+	u32 qhdr_q_size;
+	u32 qhdr_pkt_size;
+	u32 qhdr_pkt_drop_cnt;
+	u32 qhdr_rx_wm;
+	u32 qhdr_tx_wm;
+	u32 qhdr_rx_req;
+	u32 qhdr_tx_req;
+	u32 qhdr_rx_irq_status;
+	u32 qhdr_tx_irq_status;
+	u32 qhdr_read_idx;
+	u32 qhdr_write_idx;
+};
+
+struct hfi_mem_map_table {
+	u32 mem_map_num_entries;
+	u32 mem_map_table_base_addr;
+};
+
+struct hfi_mem_map {
+	u32 virtual_addr;
+	u32 physical_addr;
+	u32 size;
+	u32 attr;
+};
+
+#define VIDC_IFACEQ_TABLE_SIZE (sizeof(struct hfi_queue_table_header) \
+	+ sizeof(struct hfi_queue_header) * VIDC_IFACEQ_NUMQ)
+
+#define VIDC_IFACEQ_QUEUE_SIZE	(VIDC_IFACEQ_MAX_PKT_SIZE *  \
+	VIDC_IFACEQ_MAX_BUF_COUNT * VIDC_IFACE_MAX_PARALLEL_CLNTS)
+
+#define VIDC_IFACEQ_GET_QHDR_START_ADDR(ptr, i)     \
+	(void *)((ptr + sizeof(struct hfi_queue_table_header)) + \
+		(i * sizeof(struct hfi_queue_header)))
+
+#define QDSS_SIZE 4096
+#define SFR_SIZE 4096
+
+#define QUEUE_SIZE (VIDC_IFACEQ_TABLE_SIZE + \
+	(VIDC_IFACEQ_QUEUE_SIZE * VIDC_IFACEQ_NUMQ))
+
+#define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K)
+#define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K)
+#define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K)
+#define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \
+			ALIGNED_QDSS_SIZE, SZ_1M)
+
+enum vidc_hw_reg {
+	VIDC_HWREG_CTRL_STATUS =  0x1,
+	VIDC_HWREG_QTBL_INFO =  0x2,
+	VIDC_HWREG_QTBL_ADDR =  0x3,
+	VIDC_HWREG_CTRLR_RESET =  0x4,
+	VIDC_HWREG_IFACEQ_FWRXREQ =  0x5,
+	VIDC_HWREG_IFACEQ_FWTXREQ =  0x6,
+	VIDC_HWREG_VHI_SOFTINTEN =  0x7,
+	VIDC_HWREG_VHI_SOFTINTSTATUS =  0x8,
+	VIDC_HWREG_VHI_SOFTINTCLR =  0x9,
+	VIDC_HWREG_HVI_SOFTINTEN =  0xA,
+};
+
+struct vidc_mem_addr {
+	ion_phys_addr_t align_device_addr;
+	u8 *align_virtual_addr;
+	u32 mem_size;
+	struct msm_smem *mem_data;
+};
+
+struct vidc_iface_q_info {
+	void *q_hdr;
+	struct vidc_mem_addr q_array;
+};
+
+/*
+ * These are helper macros to iterate over various lists within
+ * venus_hfi_device->res.  The intention is to cut down on a lot of boiler-plate
+ * code
+ */
+
+/* Read as "for each 'thing' in a set of 'thingies'" */
+#define venus_hfi_for_each_thing(__device, __thing, __thingy) \
+	venus_hfi_for_each_thing_continue(__device, __thing, __thingy, 0)
+
+#define venus_hfi_for_each_thing_reverse(__device, __thing, __thingy) \
+	venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \
+			(__device)->res->__thingy##_set.count - 1)
+
+/* TODO: the __from parameter technically not required since we can figure it
+ * out with some pointer magic (i.e. __thing - __thing##_tbl[0]).  If this macro
+ * sees extensive use, probably worth cleaning it up but for now omitting it
+ * since it introduces unneccessary complexity.
+ */
+#define venus_hfi_for_each_thing_continue(__device, __thing, __thingy, __from) \
+	for (__thing = &(__device)->res->\
+			__thingy##_set.__thingy##_tbl[__from]; \
+		__thing < &(__device)->res->__thingy##_set.__thingy##_tbl[0] + \
+			((__device)->res->__thingy##_set.count - __from); \
+		++__thing)
+
+#define venus_hfi_for_each_thing_reverse_continue(__device, __thing, __thingy, \
+		__from) \
+	for (__thing = &(__device)->res->\
+			__thingy##_set.__thingy##_tbl[__from]; \
+		__thing >= &(__device)->res->__thingy##_set.__thingy##_tbl[0]; \
+		--__thing)
+
+/* Regular set helpers */
+#define venus_hfi_for_each_regulator(__device, __rinfo) \
+	venus_hfi_for_each_thing(__device, __rinfo, regulator)
+
+#define venus_hfi_for_each_regulator_reverse(__device, __rinfo) \
+	venus_hfi_for_each_thing_reverse(__device, __rinfo, regulator)
+
+#define venus_hfi_for_each_regulator_reverse_continue(__device, __rinfo, \
+		__from) \
+	venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \
+			regulator, __from)
+
+/* Clock set helpers */
+#define venus_hfi_for_each_clock(__device, __cinfo) \
+	venus_hfi_for_each_thing(__device, __cinfo, clock)
+
+#define venus_hfi_for_each_clock_reverse(__device, __cinfo) \
+	venus_hfi_for_each_thing_reverse(__device, __cinfo, clock)
+
+#define venus_hfi_for_each_clock_reverse_continue(__device, __rinfo, \
+		__from) \
+	venus_hfi_for_each_thing_reverse_continue(__device, __rinfo, \
+			clock, __from)
+
+/* Bus set helpers */
+#define venus_hfi_for_each_bus(__device, __binfo) \
+	venus_hfi_for_each_thing(__device, __binfo, bus)
+#define venus_hfi_for_each_bus_reverse(__device, __binfo) \
+	venus_hfi_for_each_thing_reverse(__device, __binfo, bus)
+
+
+/* Internal data used in vidc_hal not exposed to msm_vidc*/
+struct hal_data {
+	u32 irq;
+	phys_addr_t firmware_base;
+	u8 __iomem *register_base;
+	u32 register_size;
+};
+
+struct imem {
+	enum imem_type type;
+	union {
+		phys_addr_t vmem;
+	};
+};
+
+struct venus_resources {
+	struct msm_vidc_fw fw;
+	struct imem imem;
+};
+
+enum venus_hfi_state {
+	VENUS_STATE_DEINIT = 1,
+	VENUS_STATE_INIT,
+};
+
+struct venus_hfi_device {
+	struct list_head list;
+	struct list_head sess_head;
+	u32 intr_status;
+	u32 device_id;
+	u32 clk_freq;
+	u32 last_packet_type;
+	unsigned long clk_bitrate;
+	unsigned long scaled_rate;
+	struct msm_vidc_gov_data bus_vote;
+	bool power_enabled;
+	struct mutex lock;
+	msm_vidc_callback callback;
+	struct vidc_mem_addr iface_q_table;
+	struct vidc_mem_addr qdss;
+	struct vidc_mem_addr sfr;
+	struct vidc_mem_addr mem_addr;
+	struct vidc_iface_q_info iface_queues[VIDC_IFACEQ_NUMQ];
+	struct smem_client *hal_client;
+	struct hal_data *hal_data;
+	struct workqueue_struct *vidc_workq;
+	struct workqueue_struct *venus_pm_workq;
+	int spur_count;
+	int reg_count;
+	struct venus_resources resources;
+	struct msm_vidc_platform_resources *res;
+	enum venus_hfi_state state;
+	struct hfi_packetization_ops *pkt_ops;
+	enum hfi_packetization_type packetization_type;
+	struct msm_vidc_cb_info *response_pkt;
+	u8 *raw_packet;
+	struct pm_qos_request qos;
+	unsigned int skip_pc_count;
+	struct msm_vidc_capability *sys_init_capabilities;
+};
+
+void venus_hfi_delete_device(void *device);
+
+int venus_hfi_initialize(struct hfi_device *hdev, u32 device_id,
+		struct msm_vidc_platform_resources *res,
+		hfi_cmd_response_callback callback);
+bool venus_hfi_is_session_supported(unsigned long sessions_supported,
+		enum vidc_vote_data_session session_type);
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.c b/drivers/media/platform/msm/vidc/vidc_hfi.c
new file mode 100644
index 0000000..273471a
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.c
@@ -0,0 +1,73 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include "msm_vidc_debug.h"
+#include "vidc_hfi_api.h"
+#include "venus_hfi.h"
+
+struct hfi_device *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type,
+		u32 device_id, struct msm_vidc_platform_resources *res,
+		hfi_cmd_response_callback callback)
+{
+	struct hfi_device *hdev = NULL;
+	int rc = 0;
+
+	hdev = (struct hfi_device *)
+			kzalloc(sizeof(struct hfi_device), GFP_KERNEL);
+	if (!hdev) {
+		dprintk(VIDC_ERR, "%s: failed to allocate hdev\n", __func__);
+		return NULL;
+	}
+
+	switch (hfi_type) {
+	case VIDC_HFI_VENUS:
+		rc = venus_hfi_initialize(hdev, device_id, res, callback);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Unsupported host-firmware interface\n");
+		goto err_hfi_init;
+	}
+
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			dprintk(VIDC_ERR, "%s device init failed rc = %d",
+				__func__, rc);
+		goto err_hfi_init;
+	}
+
+	return hdev;
+
+err_hfi_init:
+	kfree(hdev);
+	return ERR_PTR(rc);
+}
+
+void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type,
+			struct hfi_device *hdev)
+{
+	if (!hdev) {
+		dprintk(VIDC_ERR, "%s invalid device %pK", __func__, hdev);
+		return;
+	}
+
+	switch (hfi_type) {
+	case VIDC_HFI_VENUS:
+		venus_hfi_delete_device(hdev->hfi_device_data);
+		break;
+	default:
+		dprintk(VIDC_ERR, "Unsupported host-firmware interface\n");
+	}
+
+	kfree(hdev);
+}
+
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi.h b/drivers/media/platform/msm/vidc/vidc_hfi.h
new file mode 100644
index 0000000..6088873
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vidc_hfi.h
@@ -0,0 +1,934 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __H_VIDC_HFI_H__
+#define __H_VIDC_HFI_H__
+
+#include <media/msm_media_info.h>
+#include "vidc_hfi_helper.h"
+#include "vidc_hfi_api.h"
+
+#define HFI_EVENT_SESSION_SEQUENCE_CHANGED (HFI_OX_BASE + 0x3)
+#define HFI_EVENT_SESSION_PROPERTY_CHANGED (HFI_OX_BASE + 0x4)
+#define HFI_EVENT_SESSION_LTRUSE_FAILED (HFI_OX_BASE + 0x5)
+#define HFI_EVENT_RELEASE_BUFFER_REFERENCE (HFI_OX_BASE + 0x6)
+
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_SUFFICIENT_BUFFER_RESOURCES	\
+	(HFI_OX_BASE + 0x1)
+#define HFI_EVENT_DATA_SEQUENCE_CHANGED_INSUFFICIENT_BUFFER_RESOURCES	\
+	(HFI_OX_BASE + 0x2)
+
+#define HFI_BUFFERFLAG_EOS			0x00000001
+#define HFI_BUFFERFLAG_STARTTIME		0x00000002
+#define HFI_BUFFERFLAG_DECODEONLY		0x00000004
+#define HFI_BUFFERFLAG_DATACORRUPT		0x00000008
+#define HFI_BUFFERFLAG_ENDOFFRAME		0x00000010
+#define HFI_BUFFERFLAG_SYNCFRAME		0x00000020
+#define HFI_BUFFERFLAG_EXTRADATA		0x00000040
+#define HFI_BUFFERFLAG_CODECCONFIG		0x00000080
+#define HFI_BUFFERFLAG_TIMESTAMPINVALID		0x00000100
+#define HFI_BUFFERFLAG_READONLY			0x00000200
+#define HFI_BUFFERFLAG_ENDOFSUBFRAME		0x00000400
+#define HFI_BUFFERFLAG_EOSEQ			0x00200000
+#define HFI_BUFFER_FLAG_MBAFF			0x08000000
+#define HFI_BUFFERFLAG_VPE_YUV_601_709_CSC_CLAMP \
+						0x10000000
+#define HFI_BUFFERFLAG_DROP_FRAME               0x20000000
+#define HFI_BUFFERFLAG_TEI			0x40000000
+#define HFI_BUFFERFLAG_DISCONTINUITY		0x80000000
+
+
+#define HFI_ERR_SESSION_EMPTY_BUFFER_DONE_OUTPUT_PENDING	\
+	(HFI_OX_BASE + 0x1001)
+#define HFI_ERR_SESSION_SAME_STATE_OPERATION		\
+	(HFI_OX_BASE + 0x1002)
+#define HFI_ERR_SESSION_SYNC_FRAME_NOT_DETECTED		\
+	(HFI_OX_BASE + 0x1003)
+#define  HFI_ERR_SESSION_START_CODE_NOT_FOUND		\
+	(HFI_OX_BASE + 0x1004)
+
+#define HFI_BUFFER_INTERNAL_SCRATCH (HFI_OX_BASE + 0x1)
+#define HFI_BUFFER_EXTRADATA_INPUT (HFI_OX_BASE + 0x2)
+#define HFI_BUFFER_EXTRADATA_OUTPUT (HFI_OX_BASE + 0x3)
+#define HFI_BUFFER_EXTRADATA_OUTPUT2 (HFI_OX_BASE + 0x4)
+#define HFI_BUFFER_INTERNAL_SCRATCH_1 (HFI_OX_BASE + 0x5)
+#define HFI_BUFFER_INTERNAL_SCRATCH_2 (HFI_OX_BASE + 0x6)
+
+#define HFI_BUFFER_MODE_STATIC (HFI_OX_BASE + 0x1)
+#define HFI_BUFFER_MODE_RING (HFI_OX_BASE + 0x2)
+#define HFI_BUFFER_MODE_DYNAMIC (HFI_OX_BASE + 0x3)
+
+#define HFI_FLUSH_INPUT (HFI_OX_BASE + 0x1)
+#define HFI_FLUSH_OUTPUT (HFI_OX_BASE + 0x2)
+#define HFI_FLUSH_ALL (HFI_OX_BASE + 0x4)
+
+#define HFI_EXTRADATA_NONE					0x00000000
+#define HFI_EXTRADATA_MB_QUANTIZATION		0x00000001
+#define HFI_EXTRADATA_INTERLACE_VIDEO		0x00000002
+#define HFI_EXTRADATA_VC1_FRAMEDISP			0x00000003
+#define HFI_EXTRADATA_VC1_SEQDISP			0x00000004
+#define HFI_EXTRADATA_TIMESTAMP				0x00000005
+#define HFI_EXTRADATA_S3D_FRAME_PACKING		0x00000006
+#define HFI_EXTRADATA_FRAME_RATE			0x00000007
+#define HFI_EXTRADATA_PANSCAN_WINDOW		0x00000008
+#define HFI_EXTRADATA_RECOVERY_POINT_SEI	0x00000009
+#define HFI_EXTRADATA_MPEG2_SEQDISP		0x0000000D
+#define HFI_EXTRADATA_STREAM_USERDATA		0x0000000E
+#define HFI_EXTRADATA_FRAME_QP			0x0000000F
+#define HFI_EXTRADATA_FRAME_BITS_INFO		0x00000010
+#define HFI_EXTRADATA_VPX_COLORSPACE		0x00000014
+#define HFI_EXTRADATA_MULTISLICE_INFO		0x7F100000
+#define HFI_EXTRADATA_NUM_CONCEALED_MB		0x7F100001
+#define HFI_EXTRADATA_INDEX					0x7F100002
+#define HFI_EXTRADATA_METADATA_LTR			0x7F100004
+#define HFI_EXTRADATA_METADATA_FILLER		0x7FE00002
+
+#define HFI_INDEX_EXTRADATA_INPUT_CROP		0x0700000E
+#define HFI_INDEX_EXTRADATA_OUTPUT_CROP		0x0700000F
+#define HFI_INDEX_EXTRADATA_ASPECT_RATIO	0x7F100003
+
+struct hfi_buffer_alloc_mode {
+	u32 buffer_type;
+	u32 buffer_mode;
+};
+
+
+struct hfi_index_extradata_config {
+	int enable;
+	u32 index_extra_data_id;
+};
+
+struct hfi_extradata_header {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 type;
+	u32 data_size;
+	u8 rg_data[1];
+};
+
+#define HFI_INTERLACE_FRAME_PROGRESSIVE					0x01
+#define HFI_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST	0x02
+#define HFI_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST	0x04
+#define HFI_INTERLACE_FRAME_TOPFIELDFIRST				0x08
+#define HFI_INTERLACE_FRAME_BOTTOMFIELDFIRST			0x10
+
+#define HFI_PROPERTY_SYS_OX_START			\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x0000)
+
+#define HFI_PROPERTY_PARAM_OX_START				\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x1000)
+#define HFI_PROPERTY_PARAM_BUFFER_COUNT_ACTUAL			\
+	(HFI_PROPERTY_PARAM_OX_START + 0x001)
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO	\
+	(HFI_PROPERTY_PARAM_OX_START + 0x002)
+#define HFI_PROPERTY_PARAM_INTERLACE_FORMAT_SUPPORTED	\
+	(HFI_PROPERTY_PARAM_OX_START + 0x003)
+#define HFI_PROPERTY_PARAM_CHROMA_SITE					\
+(HFI_PROPERTY_PARAM_OX_START + 0x004)
+#define HFI_PROPERTY_PARAM_EXTRA_DATA_HEADER_CONFIG		\
+	(HFI_PROPERTY_PARAM_OX_START + 0x005)
+#define HFI_PROPERTY_PARAM_INDEX_EXTRADATA             \
+	(HFI_PROPERTY_PARAM_OX_START + 0x006)
+#define HFI_PROPERTY_PARAM_DIVX_FORMAT					\
+	(HFI_PROPERTY_PARAM_OX_START + 0x007)
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE			\
+	(HFI_PROPERTY_PARAM_OX_START + 0x008)
+#define HFI_PROPERTY_PARAM_S3D_FRAME_PACKING_EXTRADATA	\
+	(HFI_PROPERTY_PARAM_OX_START + 0x009)
+#define HFI_PROPERTY_PARAM_ERR_DETECTION_CODE_EXTRADATA \
+	(HFI_PROPERTY_PARAM_OX_START + 0x00A)
+#define HFI_PROPERTY_PARAM_BUFFER_ALLOC_MODE_SUPPORTED	\
+	(HFI_PROPERTY_PARAM_OX_START + 0x00B)
+#define HFI_PROPERTY_PARAM_BUFFER_SIZE_MINIMUM			\
+	(HFI_PROPERTY_PARAM_OX_START + 0x00C)
+#define HFI_PROPERTY_PARAM_SYNC_BASED_INTERRUPT			\
+	(HFI_PROPERTY_PARAM_OX_START + 0x00E)
+
+#define HFI_PROPERTY_CONFIG_OX_START					\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + 0x02000)
+#define HFI_PROPERTY_CONFIG_BUFFER_REQUIREMENTS			\
+	(HFI_PROPERTY_CONFIG_OX_START + 0x001)
+#define HFI_PROPERTY_CONFIG_REALTIME					\
+	(HFI_PROPERTY_CONFIG_OX_START + 0x002)
+#define HFI_PROPERTY_CONFIG_PRIORITY					\
+	(HFI_PROPERTY_CONFIG_OX_START + 0x003)
+#define HFI_PROPERTY_CONFIG_BATCH_INFO					\
+	(HFI_PROPERTY_CONFIG_OX_START + 0x004)
+
+#define HFI_PROPERTY_PARAM_VDEC_OX_START				\
+	(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x3000)
+#define HFI_PROPERTY_PARAM_VDEC_CONTINUE_DATA_TRANSFER	\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001)
+#define HFI_PROPERTY_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x002)
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_VIEW_SELECT		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x003)
+#define HFI_PROPERTY_PARAM_VDEC_PICTURE_TYPE_DECODE		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x004)
+#define HFI_PROPERTY_PARAM_VDEC_OUTPUT_ORDER			\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x005)
+#define HFI_PROPERTY_PARAM_VDEC_MB_QUANTIZATION			\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x006)
+#define HFI_PROPERTY_PARAM_VDEC_NUM_CONCEALED_MB		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x007)
+#define HFI_PROPERTY_PARAM_VDEC_H264_ENTROPY_SWITCHING	\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x008)
+#define HFI_PROPERTY_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x009)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_RATE_EXTRADATA  \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00A)
+#define HFI_PROPERTY_PARAM_VDEC_PANSCAN_WNDW_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00B)
+#define HFI_PROPERTY_PARAM_VDEC_RECOVERY_POINT_SEI_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00C)
+#define HFI_PROPERTY_PARAM_VDEC_THUMBNAIL_MODE   \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00D)
+
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_ASSEMBLY		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x00E)
+#define HFI_PROPERTY_PARAM_VDEC_VC1_FRAMEDISP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x011)
+#define HFI_PROPERTY_PARAM_VDEC_VC1_SEQDISP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x012)
+#define HFI_PROPERTY_PARAM_VDEC_TIMESTAMP_EXTRADATA			\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x013)
+#define HFI_PROPERTY_PARAM_VDEC_INTERLACE_VIDEO_EXTRADATA	\
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x014)
+#define HFI_PROPERTY_PARAM_VDEC_AVC_SESSION_SELECT \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x015)
+#define HFI_PROPERTY_PARAM_VDEC_MPEG2_SEQDISP_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x016)
+#define HFI_PROPERTY_PARAM_VDEC_STREAM_USERDATA_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x017)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_QP_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x018)
+#define HFI_PROPERTY_PARAM_VDEC_FRAME_BITS_INFO_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x019)
+#define HFI_PROPERTY_PARAM_VDEC_SCS_THRESHOLD \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01A)
+#define HFI_PROPERTY_PARAM_VUI_DISPLAY_INFO_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x01B)
+#define HFI_PROPERTY_PARAM_VDEC_VQZIP_SEI_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001C)
+#define HFI_PROPERTY_PARAM_VDEC_VPX_COLORSPACE_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001D)
+#define HFI_PROPERTY_PARAM_VDEC_MASTERING_DISPLAY_COLOUR_SEI_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001E)
+#define HFI_PROPERTY_PARAM_VDEC_CONTENT_LIGHT_LEVEL_SEI_EXTRADATA \
+	(HFI_PROPERTY_PARAM_VDEC_OX_START + 0x001F)
+
+#define HFI_PROPERTY_CONFIG_VDEC_OX_START				\
+	(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_OX_OFFSET + 0x4000)
+#define HFI_PROPERTY_CONFIG_VDEC_POST_LOOP_DEBLOCKER	\
+	(HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x001)
+#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP_REPORTING	\
+	(HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x002)
+#define HFI_PROPERTY_CONFIG_VDEC_MB_ERROR_MAP			\
+	(HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x003)
+#define HFI_PROPERTY_CONFIG_VDEC_ENTROPY \
+	(HFI_PROPERTY_CONFIG_VDEC_OX_START + 0x004)
+
+#define HFI_PROPERTY_PARAM_VENC_OX_START				\
+	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x5000)
+#define  HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_INFO       \
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x001)
+#define  HFI_PROPERTY_PARAM_VENC_H264_IDR_S3D_FRAME_PACKING_NAL \
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x002)
+#define  HFI_PROPERTY_PARAM_VENC_LTR_INFO			\
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x003)
+#define  HFI_PROPERTY_PARAM_VENC_MBI_DUMPING				\
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x005)
+#define HFI_PROPERTY_PARAM_VENC_FRAME_QP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x006)
+#define  HFI_PROPERTY_PARAM_VENC_YUVSTAT_INFO_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x007)
+#define  HFI_PROPERTY_PARAM_VENC_ROI_QP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x008)
+#define  HFI_PROPERTY_PARAM_VENC_OVERRIDE_QP_EXTRADATA		\
+	(HFI_PROPERTY_PARAM_VENC_OX_START + 0x009)
+
+#define HFI_PROPERTY_CONFIG_VENC_OX_START				\
+	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_OX_OFFSET + 0x6000)
+#define  HFI_PROPERTY_CONFIG_VENC_FRAME_QP				\
+	(HFI_PROPERTY_CONFIG_VENC_OX_START + 0x001)
+
+#define HFI_PROPERTY_PARAM_VPE_OX_START					\
+	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x7000)
+#define HFI_PROPERTY_PARAM_VPE_COLOR_SPACE_CONVERSION			\
+	(HFI_PROPERTY_PARAM_VPE_OX_START + 0x001)
+
+#define HFI_PROPERTY_CONFIG_VPE_OX_START				\
+	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_OX_OFFSET + 0x8000)
+
+struct hfi_batch_info {
+	u32 input_batch_count;
+	u32 output_batch_count;
+};
+
+struct hfi_buffer_count_actual {
+	u32 buffer_type;
+	u32 buffer_count_actual;
+};
+
+struct hfi_buffer_size_minimum {
+	u32 buffer_type;
+	u32 buffer_size;
+};
+
+struct hfi_buffer_requirements {
+	u32 buffer_type;
+	u32 buffer_size;
+	u32 buffer_region_size;
+	u32 buffer_hold_count;
+	u32 buffer_count_min;
+	u32 buffer_count_actual;
+	u32 contiguous;
+	u32 buffer_alignment;
+};
+
+#define HFI_CHROMA_SITE_0			(HFI_OX_BASE + 0x1)
+#define HFI_CHROMA_SITE_1			(HFI_OX_BASE + 0x2)
+#define HFI_CHROMA_SITE_2			(HFI_OX_BASE + 0x3)
+#define HFI_CHROMA_SITE_3			(HFI_OX_BASE + 0x4)
+#define HFI_CHROMA_SITE_4			(HFI_OX_BASE + 0x5)
+#define HFI_CHROMA_SITE_5			(HFI_OX_BASE + 0x6)
+
+struct hfi_data_payload {
+	u32 size;
+	u8 rg_data[1];
+};
+
+struct hfi_enable_picture {
+	u32 picture_type;
+};
+
+struct hfi_display_picture_buffer_count {
+	int enable;
+	u32 count;
+};
+
+struct hfi_extra_data_header_config {
+	u32 type;
+	u32 buffer_type;
+	u32 version;
+	u32 port_index;
+	u32 client_extra_data_id;
+};
+
+struct hfi_interlace_format_supported {
+	u32 buffer_type;
+	u32 format;
+};
+
+struct hfi_buffer_alloc_mode_supported {
+	u32 buffer_type;
+	u32 num_entries;
+	u32 rg_data[1];
+};
+
+struct hfi_mb_error_map {
+	u32 error_map_size;
+	u8 rg_error_map[1];
+};
+
+struct hfi_metadata_pass_through {
+	int enable;
+	u32 size;
+};
+
+struct hfi_multi_view_select {
+	u32 view_index;
+};
+
+struct hfi_hybrid_hierp {
+	u32 layers;
+};
+
+#define HFI_PRIORITY_LOW		10
+#define HFI_PRIOIRTY_MEDIUM		20
+#define HFI_PRIORITY_HIGH		30
+
+#define HFI_OUTPUT_ORDER_DISPLAY	(HFI_OX_BASE + 0x1)
+#define HFI_OUTPUT_ORDER_DECODE		(HFI_OX_BASE + 0x2)
+
+#define HFI_RATE_CONTROL_OFF		(HFI_OX_BASE + 0x1)
+#define HFI_RATE_CONTROL_VBR_VFR	(HFI_OX_BASE + 0x2)
+#define HFI_RATE_CONTROL_VBR_CFR	(HFI_OX_BASE + 0x3)
+#define HFI_RATE_CONTROL_CBR_VFR	(HFI_OX_BASE + 0x4)
+#define HFI_RATE_CONTROL_CBR_CFR	(HFI_OX_BASE + 0x5)
+#define HFI_RATE_CONTROL_MBR_CFR	(HFI_OX_BASE + 0x6)
+#define HFI_RATE_CONTROL_MBR_VFR	(HFI_OX_BASE + 0x7)
+
+
+struct hfi_uncompressed_plane_actual_constraints_info {
+	u32 buffer_type;
+	u32 num_planes;
+	struct hfi_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+#define HFI_CMD_SYS_OX_START		\
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x0000)
+#define HFI_CMD_SYS_SESSION_ABORT	(HFI_CMD_SYS_OX_START + 0x001)
+#define HFI_CMD_SYS_PING		(HFI_CMD_SYS_OX_START + 0x002)
+
+#define HFI_CMD_SESSION_OX_START	\
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_CMD_START_OFFSET + 0x1000)
+#define HFI_CMD_SESSION_LOAD_RESOURCES	(HFI_CMD_SESSION_OX_START + 0x001)
+#define HFI_CMD_SESSION_START		(HFI_CMD_SESSION_OX_START + 0x002)
+#define HFI_CMD_SESSION_STOP		(HFI_CMD_SESSION_OX_START + 0x003)
+#define HFI_CMD_SESSION_EMPTY_BUFFER	(HFI_CMD_SESSION_OX_START + 0x004)
+#define HFI_CMD_SESSION_FILL_BUFFER	(HFI_CMD_SESSION_OX_START + 0x005)
+#define HFI_CMD_SESSION_SUSPEND		(HFI_CMD_SESSION_OX_START + 0x006)
+#define HFI_CMD_SESSION_RESUME		(HFI_CMD_SESSION_OX_START + 0x007)
+#define HFI_CMD_SESSION_FLUSH		(HFI_CMD_SESSION_OX_START + 0x008)
+#define HFI_CMD_SESSION_GET_PROPERTY	(HFI_CMD_SESSION_OX_START + 0x009)
+#define HFI_CMD_SESSION_PARSE_SEQUENCE_HEADER	\
+	(HFI_CMD_SESSION_OX_START + 0x00A)
+#define HFI_CMD_SESSION_RELEASE_BUFFERS		\
+	(HFI_CMD_SESSION_OX_START + 0x00B)
+#define HFI_CMD_SESSION_RELEASE_RESOURCES	\
+	(HFI_CMD_SESSION_OX_START + 0x00C)
+#define HFI_CMD_SESSION_CONTINUE	(HFI_CMD_SESSION_OX_START + 0x00D)
+#define HFI_CMD_SESSION_SYNC		(HFI_CMD_SESSION_OX_START + 0x00E)
+
+#define HFI_MSG_SYS_OX_START			\
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x0000)
+#define HFI_MSG_SYS_PING_ACK	(HFI_MSG_SYS_OX_START + 0x2)
+#define HFI_MSG_SYS_SESSION_ABORT_DONE	(HFI_MSG_SYS_OX_START + 0x4)
+
+#define HFI_MSG_SESSION_OX_START		\
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_OX_OFFSET + HFI_MSG_START_OFFSET + 0x1000)
+#define HFI_MSG_SESSION_LOAD_RESOURCES_DONE	(HFI_MSG_SESSION_OX_START + 0x1)
+#define HFI_MSG_SESSION_START_DONE		(HFI_MSG_SESSION_OX_START + 0x2)
+#define HFI_MSG_SESSION_STOP_DONE		(HFI_MSG_SESSION_OX_START + 0x3)
+#define HFI_MSG_SESSION_SUSPEND_DONE	(HFI_MSG_SESSION_OX_START + 0x4)
+#define HFI_MSG_SESSION_RESUME_DONE		(HFI_MSG_SESSION_OX_START + 0x5)
+#define HFI_MSG_SESSION_FLUSH_DONE		(HFI_MSG_SESSION_OX_START + 0x6)
+#define HFI_MSG_SESSION_EMPTY_BUFFER_DONE	(HFI_MSG_SESSION_OX_START + 0x7)
+#define HFI_MSG_SESSION_FILL_BUFFER_DONE	(HFI_MSG_SESSION_OX_START + 0x8)
+#define HFI_MSG_SESSION_PROPERTY_INFO		(HFI_MSG_SESSION_OX_START + 0x9)
+#define HFI_MSG_SESSION_RELEASE_RESOURCES_DONE	\
+	(HFI_MSG_SESSION_OX_START + 0xA)
+#define HFI_MSG_SESSION_PARSE_SEQUENCE_HEADER_DONE		\
+	(HFI_MSG_SESSION_OX_START + 0xB)
+#define  HFI_MSG_SESSION_RELEASE_BUFFERS_DONE			\
+	(HFI_MSG_SESSION_OX_START + 0xC)
+
+#define VIDC_IFACEQ_MAX_PKT_SIZE                        1024
+#define VIDC_IFACEQ_MED_PKT_SIZE                        768
+#define VIDC_IFACEQ_MIN_PKT_SIZE                        8
+#define VIDC_IFACEQ_VAR_SMALL_PKT_SIZE          100
+#define VIDC_IFACEQ_VAR_LARGE_PKT_SIZE          512
+#define VIDC_IFACEQ_VAR_HUGE_PKT_SIZE          (1024*12)
+
+
+struct hfi_cmd_sys_session_abort_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_sys_ping_packet {
+	u32 size;
+	u32 packet_type;
+	u32 client_data;
+};
+
+struct hfi_cmd_session_load_resources_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_start_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_stop_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_empty_buffer_compressed_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 time_stamp_hi;
+	u32 time_stamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 input_tag;
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 rgData[1];
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane0_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 view_id;
+	u32 time_stamp_hi;
+	u32 time_stamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 input_tag;
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 rgData[1];
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane1_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 packet_buffer2;
+	u32 rgData[1];
+};
+
+struct hfi_cmd_session_empty_buffer_uncompressed_plane2_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 packet_buffer3;
+	u32 rgData[1];
+};
+
+struct hfi_cmd_session_fill_buffer_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 stream_id;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 output_tag;
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 rgData[1];
+};
+
+struct hfi_cmd_session_flush_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 flush_type;
+};
+
+struct hfi_cmd_session_suspend_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_resume_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_get_property_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_cmd_session_release_buffer_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 buffer_type;
+	u32 buffer_size;
+	u32 extra_data_size;
+	int response_req;
+	u32 num_buffers;
+	u32 rg_buffer_info[1];
+};
+
+struct hfi_cmd_session_release_resources_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_session_parse_sequence_header_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 header_len;
+	u32 packet_buffer;
+};
+
+struct hfi_msg_sys_session_abort_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_sys_idle_packet {
+	u32 size;
+	u32 packet_type;
+};
+
+struct hfi_msg_sys_ping_ack_packet {
+	u32 size;
+	u32 packet_type;
+	u32 client_data;
+};
+
+struct hfi_msg_sys_property_info_packet {
+	u32 size;
+	u32 packet_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_load_resources_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_start_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_stop_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_suspend_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_resume_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_flush_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 flush_type;
+};
+
+struct hfi_msg_session_empty_buffer_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 offset;
+	u32 filled_len;
+	u32 input_tag;
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 rgData[0];
+};
+
+struct hfi_msg_session_fill_buffer_done_compressed_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 time_stamp_hi;
+	u32 time_stamp_lo;
+	u32 error_type;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 input_tag;
+	u32 output_tag;
+	u32 picture_type;
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 rgData[0];
+};
+
+struct hfi_msg_session_fbd_uncompressed_plane0_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 stream_id;
+	u32 view_id;
+	u32 error_type;
+	u32 time_stamp_hi;
+	u32 time_stamp_lo;
+	u32 flags;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 frame_width;
+	u32 frame_height;
+	u32 start_x_coord;
+	u32 start_y_coord;
+	u32 input_tag;
+	u32 input_tag2;
+	u32 output_tag;
+	u32 picture_type;
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 rgData[0];
+};
+
+struct hfi_msg_session_fill_buffer_done_uncompressed_plane1_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 packet_buffer2;
+	u32 rgData[0];
+};
+
+struct hfi_msg_session_fill_buffer_done_uncompressed_plane2_packet {
+	u32 flags;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 offset;
+	u32 packet_buffer3;
+	u32 rgData[0];
+};
+
+struct hfi_msg_session_parse_sequence_header_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_property_info_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_session_release_resources_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_release_buffers_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 num_buffers;
+	u32 rg_buffer_info[1];
+};
+
+struct hfi_extradata_mb_quantization_payload {
+	u8 rg_mb_qp[1];
+};
+
+struct hfi_extradata_vc1_pswnd {
+	u32 ps_wnd_h_offset;
+	u32 ps_wnd_v_offset;
+	u32 ps_wnd_width;
+	u32 ps_wnd_height;
+};
+
+struct hfi_extradata_vc1_framedisp_payload {
+	u32 res_pic;
+	u32 ref;
+	u32 range_map_present;
+	u32 range_map_y;
+	u32 range_map_uv;
+	u32 num_pan_scan_wnds;
+	struct hfi_extradata_vc1_pswnd rg_ps_wnd[1];
+};
+
+struct hfi_extradata_vc1_seqdisp_payload {
+	u32 prog_seg_frm;
+	u32 uv_sampling_fmt;
+	u32 color_fmt_flag;
+	u32 color_primaries;
+	u32 transfer_char;
+	u32 mat_coeff;
+	u32 aspect_ratio;
+	u32 aspect_horiz;
+	u32 aspect_vert;
+};
+
+struct hfi_extradata_timestamp_payload {
+	u32 time_stamp_low;
+	u32 time_stamp_high;
+};
+
+
+struct hfi_extradata_s3d_frame_packing_payload {
+	u32 fpa_id;
+	int cancel_flag;
+	u32 fpa_type;
+	int quin_cunx_flag;
+	u32 content_interprtation_type;
+	int spatial_flipping_flag;
+	int frame0_flipped_flag;
+	int field_views_flag;
+	int current_frame_isFrame0_flag;
+	int frame0_self_contained_flag;
+	int frame1_self_contained_flag;
+	u32 frame0_graid_pos_x;
+	u32 frame0_graid_pos_y;
+	u32 frame1_graid_pos_x;
+	u32 frame1_graid_pos_y;
+	u32 fpa_reserved_byte;
+	u32 fpa_repetition_period;
+	int fpa_extension_flag;
+};
+
+struct hfi_extradata_interlace_video_payload {
+	u32 format;
+};
+
+struct hfi_extradata_num_concealed_mb_payload {
+	u32 num_mb_concealed;
+};
+
+struct hfi_extradata_sliceinfo {
+	u32 offset_in_stream;
+	u32 slice_length;
+};
+
+struct hfi_extradata_multislice_info_payload {
+	u32 num_slices;
+	struct hfi_extradata_sliceinfo rg_slice_info[1];
+};
+
+struct hfi_index_extradata_input_crop_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 left;
+	u32 top;
+	u32 width;
+	u32 height;
+};
+
+struct hfi_index_extradata_output_crop_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 left;
+	u32 top;
+	u32 display_width;
+	u32 display_height;
+	u32 width;
+	u32 height;
+};
+
+struct hfi_index_extradata_digital_zoom_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	int width;
+	int height;
+};
+
+struct hfi_index_extradata_aspect_ratio_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 aspect_width;
+	u32 aspect_height;
+};
+struct hfi_extradata_panscan_wndw_payload {
+	u32 num_window;
+	struct hfi_extradata_vc1_pswnd wnd[1];
+};
+
+struct hfi_extradata_frame_type_payload {
+	u32 frame_rate;
+};
+
+struct hfi_extradata_recovery_point_sei_payload {
+	u32 flag;
+};
+
+struct hfi_cmd_session_continue_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hal_session {
+	struct list_head list;
+	void *session_id;
+	bool is_decoder;
+	enum hal_video_codec codec;
+	enum hal_domain domain;
+	void *device;
+};
+
+struct hal_device_data {
+	struct list_head dev_head;
+	int dev_count;
+};
+
+struct msm_vidc_fw {
+	void *cookie;
+};
+
+int hfi_process_msg_packet(u32 device_id, struct vidc_hal_msg_pkt_hdr *msg_hdr,
+		struct msm_vidc_cb_info *info);
+
+enum vidc_status hfi_process_sys_init_done_prop_read(
+	struct hfi_msg_sys_init_done_packet *pkt,
+	struct vidc_hal_sys_init_done *sys_init_done);
+
+enum vidc_status hfi_process_session_init_done_prop_read(
+	struct hfi_msg_sys_session_init_done_packet *pkt,
+	struct vidc_hal_session_init_done *session_init_done);
+
+#endif
+
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_api.h b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
new file mode 100644
index 0000000..7edada9
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_api.h
@@ -0,0 +1,1545 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDC_HFI_API_H__
+#define __VIDC_HFI_API_H__
+
+#include <linux/log2.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <media/msm_vidc.h>
+#include "msm_vidc_resources.h"
+
+#define CONTAINS(__a, __sz, __t) ({\
+	int __rc = __t >= __a && \
+			__t < __a + __sz; \
+	__rc; \
+})
+
+#define OVERLAPS(__t, __tsz, __a, __asz) ({\
+	int __rc = __t <= __a && \
+			__t + __tsz >= __a + __asz; \
+	__rc; \
+})
+
+#define HAL_BUFFERFLAG_EOS              0x00000001
+#define HAL_BUFFERFLAG_STARTTIME        0x00000002
+#define HAL_BUFFERFLAG_DECODEONLY       0x00000004
+#define HAL_BUFFERFLAG_DATACORRUPT      0x00000008
+#define HAL_BUFFERFLAG_ENDOFFRAME       0x00000010
+#define HAL_BUFFERFLAG_SYNCFRAME        0x00000020
+#define HAL_BUFFERFLAG_EXTRADATA        0x00000040
+#define HAL_BUFFERFLAG_CODECCONFIG      0x00000080
+#define HAL_BUFFERFLAG_TIMESTAMPINVALID 0x00000100
+#define HAL_BUFFERFLAG_READONLY         0x00000200
+#define HAL_BUFFERFLAG_ENDOFSUBFRAME    0x00000400
+#define HAL_BUFFERFLAG_EOSEQ            0x00200000
+#define HAL_BUFFERFLAG_MBAFF            0x08000000
+#define HAL_BUFFERFLAG_YUV_601_709_CSC_CLAMP   0x10000000
+#define HAL_BUFFERFLAG_DROP_FRAME       0x20000000
+#define HAL_BUFFERFLAG_TS_DISCONTINUITY	0x40000000
+#define HAL_BUFFERFLAG_TS_ERROR		0x80000000
+
+
+
+#define HAL_DEBUG_MSG_LOW				0x00000001
+#define HAL_DEBUG_MSG_MEDIUM			0x00000002
+#define HAL_DEBUG_MSG_HIGH				0x00000004
+#define HAL_DEBUG_MSG_ERROR				0x00000008
+#define HAL_DEBUG_MSG_FATAL				0x00000010
+#define MAX_PROFILE_COUNT	16
+
+#define HAL_MAX_MATRIX_COEFFS 9
+#define HAL_MAX_BIAS_COEFFS 3
+#define HAL_MAX_LIMIT_COEFFS 6
+#define VENUS_VERSION_LENGTH 128
+
+/* 16 encoder and 16 decoder sessions */
+#define VIDC_MAX_SESSIONS               32
+
+enum vidc_status {
+	VIDC_ERR_NONE = 0x0,
+	VIDC_ERR_FAIL = 0x80000000,
+	VIDC_ERR_ALLOC_FAIL,
+	VIDC_ERR_ILLEGAL_OP,
+	VIDC_ERR_BAD_PARAM,
+	VIDC_ERR_BAD_HANDLE,
+	VIDC_ERR_NOT_SUPPORTED,
+	VIDC_ERR_BAD_STATE,
+	VIDC_ERR_MAX_CLIENTS,
+	VIDC_ERR_IFRAME_EXPECTED,
+	VIDC_ERR_HW_FATAL,
+	VIDC_ERR_BITSTREAM_ERR,
+	VIDC_ERR_INDEX_NOMORE,
+	VIDC_ERR_SEQHDR_PARSE_FAIL,
+	VIDC_ERR_INSUFFICIENT_BUFFER,
+	VIDC_ERR_BAD_POWER_STATE,
+	VIDC_ERR_NO_VALID_SESSION,
+	VIDC_ERR_TIMEOUT,
+	VIDC_ERR_CMDQFULL,
+	VIDC_ERR_START_CODE_NOT_FOUND,
+	VIDC_ERR_CLIENT_PRESENT = 0x90000001,
+	VIDC_ERR_CLIENT_FATAL,
+	VIDC_ERR_CMD_QUEUE_FULL,
+	VIDC_ERR_UNUSED = 0x10000000
+};
+
+enum hal_extradata_id {
+	HAL_EXTRADATA_NONE,
+	HAL_EXTRADATA_MB_QUANTIZATION,
+	HAL_EXTRADATA_INTERLACE_VIDEO,
+	HAL_EXTRADATA_VC1_FRAMEDISP,
+	HAL_EXTRADATA_VC1_SEQDISP,
+	HAL_EXTRADATA_TIMESTAMP,
+	HAL_EXTRADATA_S3D_FRAME_PACKING,
+	HAL_EXTRADATA_FRAME_RATE,
+	HAL_EXTRADATA_PANSCAN_WINDOW,
+	HAL_EXTRADATA_RECOVERY_POINT_SEI,
+	HAL_EXTRADATA_MULTISLICE_INFO,
+	HAL_EXTRADATA_INDEX,
+	HAL_EXTRADATA_NUM_CONCEALED_MB,
+	HAL_EXTRADATA_METADATA_FILLER,
+	HAL_EXTRADATA_ASPECT_RATIO,
+	HAL_EXTRADATA_MPEG2_SEQDISP,
+	HAL_EXTRADATA_STREAM_USERDATA,
+	HAL_EXTRADATA_FRAME_QP,
+	HAL_EXTRADATA_FRAME_BITS_INFO,
+	HAL_EXTRADATA_INPUT_CROP,
+	HAL_EXTRADATA_DIGITAL_ZOOM,
+	HAL_EXTRADATA_LTR_INFO,
+	HAL_EXTRADATA_METADATA_MBI,
+	HAL_EXTRADATA_VQZIP_SEI,
+	HAL_EXTRADATA_YUV_STATS,
+	HAL_EXTRADATA_ROI_QP,
+	HAL_EXTRADATA_OUTPUT_CROP,
+	HAL_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI,
+	HAL_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI,
+	HAL_EXTRADATA_PQ_INFO,
+	HAL_EXTRADATA_VUI_DISPLAY_INFO,
+	HAL_EXTRADATA_VPX_COLORSPACE,
+};
+
+enum hal_property {
+	HAL_CONFIG_FRAME_RATE = 0x04000001,
+	HAL_PARAM_UNCOMPRESSED_FORMAT_SELECT,
+	HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_CONSTRAINTS_INFO,
+	HAL_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO,
+	HAL_PARAM_EXTRA_DATA_HEADER_CONFIG,
+	HAL_PARAM_INDEX_EXTRADATA,
+	HAL_PARAM_FRAME_SIZE,
+	HAL_CONFIG_REALTIME,
+	HAL_PARAM_BUFFER_COUNT_ACTUAL,
+	HAL_PARAM_BUFFER_SIZE_MINIMUM,
+	HAL_PARAM_NAL_STREAM_FORMAT_SELECT,
+	HAL_PARAM_VDEC_OUTPUT_ORDER,
+	HAL_PARAM_VDEC_PICTURE_TYPE_DECODE,
+	HAL_PARAM_VDEC_OUTPUT2_KEEP_ASPECT_RATIO,
+	HAL_CONFIG_VDEC_POST_LOOP_DEBLOCKER,
+	HAL_PARAM_VDEC_MULTI_STREAM,
+	HAL_PARAM_VDEC_DISPLAY_PICTURE_BUFFER_COUNT,
+	HAL_PARAM_DIVX_FORMAT,
+	HAL_CONFIG_VDEC_MB_ERROR_MAP_REPORTING,
+	HAL_PARAM_VDEC_CONTINUE_DATA_TRANSFER,
+	HAL_CONFIG_VDEC_MB_ERROR_MAP,
+	HAL_CONFIG_VENC_REQUEST_IFRAME,
+	HAL_PARAM_VENC_MPEG4_SHORT_HEADER,
+	HAL_PARAM_VENC_MPEG4_AC_PREDICTION,
+	HAL_CONFIG_VENC_TARGET_BITRATE,
+	HAL_PARAM_PROFILE_LEVEL_CURRENT,
+	HAL_PARAM_VENC_H264_ENTROPY_CONTROL,
+	HAL_PARAM_VENC_RATE_CONTROL,
+	HAL_PARAM_VENC_MPEG4_TIME_RESOLUTION,
+	HAL_PARAM_VENC_MPEG4_HEADER_EXTENSION,
+	HAL_PARAM_VENC_H264_DEBLOCK_CONTROL,
+	HAL_PARAM_VENC_TEMPORAL_SPATIAL_TRADEOFF,
+	HAL_PARAM_VENC_SESSION_QP,
+	HAL_PARAM_VENC_SESSION_QP_RANGE,
+	HAL_CONFIG_VENC_INTRA_PERIOD,
+	HAL_CONFIG_VENC_IDR_PERIOD,
+	HAL_CONFIG_VPE_OPERATIONS,
+	HAL_PARAM_VENC_INTRA_REFRESH,
+	HAL_PARAM_VENC_MULTI_SLICE_CONTROL,
+	HAL_CONFIG_VPE_DEINTERLACE,
+	HAL_SYS_DEBUG_CONFIG,
+	HAL_CONFIG_BUFFER_REQUIREMENTS,
+	HAL_CONFIG_PRIORITY,
+	HAL_CONFIG_BATCH_INFO,
+	HAL_PARAM_METADATA_PASS_THROUGH,
+	HAL_SYS_IDLE_INDICATOR,
+	HAL_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED,
+	HAL_PARAM_INTERLACE_FORMAT_SUPPORTED,
+	HAL_PARAM_CHROMA_SITE,
+	HAL_PARAM_PROPERTIES_SUPPORTED,
+	HAL_PARAM_PROFILE_LEVEL_SUPPORTED,
+	HAL_PARAM_CAPABILITY_SUPPORTED,
+	HAL_PARAM_NAL_STREAM_FORMAT_SUPPORTED,
+	HAL_PARAM_MULTI_VIEW_FORMAT,
+	HAL_PARAM_MAX_SEQUENCE_HEADER_SIZE,
+	HAL_PARAM_CODEC_SUPPORTED,
+	HAL_PARAM_VDEC_MULTI_VIEW_SELECT,
+	HAL_PARAM_VDEC_MB_QUANTIZATION,
+	HAL_PARAM_VDEC_NUM_CONCEALED_MB,
+	HAL_PARAM_VDEC_H264_ENTROPY_SWITCHING,
+	HAL_PARAM_VENC_SLICE_DELIVERY_MODE,
+	HAL_PARAM_VENC_MPEG4_DATA_PARTITIONING,
+	HAL_CONFIG_BUFFER_COUNT_ACTUAL,
+	HAL_CONFIG_VDEC_MULTI_STREAM,
+	HAL_PARAM_VENC_MULTI_SLICE_INFO,
+	HAL_CONFIG_VENC_TIMESTAMP_SCALE,
+	HAL_PARAM_VENC_SYNC_FRAME_SEQUENCE_HEADER,
+	HAL_PARAM_VDEC_SYNC_FRAME_DECODE,
+	HAL_PARAM_VENC_H264_ENTROPY_CABAC_MODEL,
+	HAL_CONFIG_VENC_MAX_BITRATE,
+	HAL_PARAM_VENC_H264_VUI_TIMING_INFO,
+	HAL_PARAM_VENC_H264_GENERATE_AUDNAL,
+	HAL_PARAM_VENC_MAX_NUM_B_FRAMES,
+	HAL_PARAM_BUFFER_ALLOC_MODE,
+	HAL_PARAM_VDEC_FRAME_ASSEMBLY,
+	HAL_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC,
+	HAL_PARAM_VENC_PRESERVE_TEXT_QUALITY,
+	HAL_PARAM_VDEC_CONCEAL_COLOR,
+	HAL_PARAM_VDEC_SCS_THRESHOLD,
+	HAL_PARAM_GET_BUFFER_REQUIREMENTS,
+	HAL_PARAM_MVC_BUFFER_LAYOUT,
+	HAL_PARAM_VENC_LTRMODE,
+	HAL_CONFIG_VENC_MARKLTRFRAME,
+	HAL_CONFIG_VENC_USELTRFRAME,
+	HAL_CONFIG_VENC_LTRPERIOD,
+	HAL_CONFIG_VENC_HIER_P_NUM_FRAMES,
+	HAL_PARAM_VENC_HIER_P_MAX_ENH_LAYERS,
+	HAL_PARAM_VENC_DISABLE_RC_TIMESTAMP,
+	HAL_PARAM_VENC_ENABLE_INITIAL_QP,
+	HAL_PARAM_VENC_SEARCH_RANGE,
+	HAL_PARAM_VPE_COLOR_SPACE_CONVERSION,
+	HAL_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE,
+	HAL_PARAM_VENC_H264_NAL_SVC_EXT,
+	HAL_CONFIG_VENC_PERF_MODE,
+	HAL_PARAM_VENC_HIER_B_MAX_ENH_LAYERS,
+	HAL_PARAM_VDEC_NON_SECURE_OUTPUT2,
+	HAL_PARAM_VENC_HIER_P_HYBRID_MODE,
+	HAL_PARAM_VENC_MBI_STATISTICS_MODE,
+	HAL_PARAM_SYNC_BASED_INTERRUPT,
+	HAL_CONFIG_VENC_FRAME_QP,
+	HAL_CONFIG_VENC_BASELAYER_PRIORITYID,
+	HAL_PARAM_VENC_VQZIP_SEI,
+	HAL_PROPERTY_PARAM_VENC_ASPECT_RATIO,
+	HAL_CONFIG_VDEC_ENTROPY,
+	HAL_PARAM_VENC_BITRATE_TYPE,
+	HAL_PARAM_VENC_H264_PIC_ORDER_CNT,
+	HAL_PARAM_VENC_LOW_LATENCY,
+	HAL_PARAM_VENC_CONSTRAINED_INTRA_PRED,
+	HAL_CONFIG_VENC_BLUR_RESOLUTION,
+	HAL_PARAM_VENC_SESSION_QP_RANGE_PACKED,
+	HAL_PARAM_VENC_H264_TRANSFORM_8x8,
+	HAL_PARAM_VENC_VIDEO_SIGNAL_INFO,
+	HAL_PARAM_VENC_IFRAMESIZE_TYPE,
+};
+
+enum hal_domain {
+	HAL_VIDEO_DOMAIN_VPE,
+	HAL_VIDEO_DOMAIN_ENCODER,
+	HAL_VIDEO_DOMAIN_DECODER,
+	HAL_UNUSED_DOMAIN = 0x10000000,
+};
+
+enum multi_stream {
+	HAL_VIDEO_DECODER_NONE = 0x00000000,
+	HAL_VIDEO_DECODER_PRIMARY = 0x00000001,
+	HAL_VIDEO_DECODER_SECONDARY = 0x00000002,
+	HAL_VIDEO_DECODER_BOTH_OUTPUTS = 0x00000004,
+	HAL_VIDEO_UNUSED_OUTPUTS = 0x10000000,
+};
+
+enum hal_core_capabilities {
+	HAL_VIDEO_ENCODER_ROTATION_CAPABILITY = 0x00000001,
+	HAL_VIDEO_ENCODER_SCALING_CAPABILITY = 0x00000002,
+	HAL_VIDEO_ENCODER_DEINTERLACE_CAPABILITY = 0x00000004,
+	HAL_VIDEO_DECODER_MULTI_STREAM_CAPABILITY = 0x00000008,
+	HAL_VIDEO_UNUSED_CAPABILITY      = 0x10000000,
+};
+
+enum hal_default_properties {
+	HAL_VIDEO_DYNAMIC_BUF_MODE = 0x00000001,
+	HAL_VIDEO_CONTINUE_DATA_TRANSFER = 0x00000002,
+};
+
+enum hal_video_codec {
+	HAL_VIDEO_CODEC_UNKNOWN  = 0x00000000,
+	HAL_VIDEO_CODEC_MVC      = 0x00000001,
+	HAL_VIDEO_CODEC_H264     = 0x00000002,
+	HAL_VIDEO_CODEC_H263     = 0x00000004,
+	HAL_VIDEO_CODEC_MPEG1    = 0x00000008,
+	HAL_VIDEO_CODEC_MPEG2    = 0x00000010,
+	HAL_VIDEO_CODEC_MPEG4    = 0x00000020,
+	HAL_VIDEO_CODEC_DIVX_311 = 0x00000040,
+	HAL_VIDEO_CODEC_DIVX     = 0x00000080,
+	HAL_VIDEO_CODEC_VC1      = 0x00000100,
+	HAL_VIDEO_CODEC_SPARK    = 0x00000200,
+	HAL_VIDEO_CODEC_VP6      = 0x00000400,
+	HAL_VIDEO_CODEC_VP7      = 0x00000800,
+	HAL_VIDEO_CODEC_VP8      = 0x00001000,
+	HAL_VIDEO_CODEC_HEVC     = 0x00002000,
+	HAL_VIDEO_CODEC_VP9      = 0x00004000,
+	HAL_VIDEO_CODEC_HEVC_HYBRID     = 0x80000000,
+	HAL_UNUSED_CODEC = 0x10000000,
+};
+
+enum hal_h263_profile {
+	HAL_H263_PROFILE_BASELINE           = 0x00000001,
+	HAL_H263_PROFILE_H320CODING         = 0x00000002,
+	HAL_H263_PROFILE_BACKWARDCOMPATIBLE = 0x00000004,
+	HAL_H263_PROFILE_ISWV2              = 0x00000008,
+	HAL_H263_PROFILE_ISWV3              = 0x00000010,
+	HAL_H263_PROFILE_HIGHCOMPRESSION    = 0x00000020,
+	HAL_H263_PROFILE_INTERNET           = 0x00000040,
+	HAL_H263_PROFILE_INTERLACE          = 0x00000080,
+	HAL_H263_PROFILE_HIGHLATENCY        = 0x00000100,
+	HAL_UNUSED_H263_PROFILE = 0x10000000,
+};
+
+enum hal_h263_level {
+	HAL_H263_LEVEL_10 = 0x00000001,
+	HAL_H263_LEVEL_20 = 0x00000002,
+	HAL_H263_LEVEL_30 = 0x00000004,
+	HAL_H263_LEVEL_40 = 0x00000008,
+	HAL_H263_LEVEL_45 = 0x00000010,
+	HAL_H263_LEVEL_50 = 0x00000020,
+	HAL_H263_LEVEL_60 = 0x00000040,
+	HAL_H263_LEVEL_70 = 0x00000080,
+	HAL_UNUSED_H263_LEVEL = 0x10000000,
+};
+
+enum hal_mpeg2_profile {
+	HAL_MPEG2_PROFILE_SIMPLE  = 0x00000001,
+	HAL_MPEG2_PROFILE_MAIN    = 0x00000002,
+	HAL_MPEG2_PROFILE_422     = 0x00000004,
+	HAL_MPEG2_PROFILE_SNR     = 0x00000008,
+	HAL_MPEG2_PROFILE_SPATIAL = 0x00000010,
+	HAL_MPEG2_PROFILE_HIGH    = 0x00000020,
+	HAL_UNUSED_MPEG2_PROFILE = 0x10000000,
+};
+
+enum hal_mpeg2_level {
+	HAL_MPEG2_LEVEL_LL  = 0x00000001,
+	HAL_MPEG2_LEVEL_ML  = 0x00000002,
+	HAL_MPEG2_LEVEL_H14 = 0x00000004,
+	HAL_MPEG2_LEVEL_HL  = 0x00000008,
+	HAL_UNUSED_MEPG2_LEVEL = 0x10000000,
+};
+
+enum hal_mpeg4_profile {
+	HAL_MPEG4_PROFILE_SIMPLE           = 0x00000001,
+	HAL_MPEG4_PROFILE_ADVANCEDSIMPLE   = 0x00000002,
+	HAL_MPEG4_PROFILE_CORE             = 0x00000004,
+	HAL_MPEG4_PROFILE_MAIN             = 0x00000008,
+	HAL_MPEG4_PROFILE_NBIT             = 0x00000010,
+	HAL_MPEG4_PROFILE_SCALABLETEXTURE  = 0x00000020,
+	HAL_MPEG4_PROFILE_SIMPLEFACE       = 0x00000040,
+	HAL_MPEG4_PROFILE_SIMPLEFBA        = 0x00000080,
+	HAL_MPEG4_PROFILE_BASICANIMATED    = 0x00000100,
+	HAL_MPEG4_PROFILE_HYBRID           = 0x00000200,
+	HAL_MPEG4_PROFILE_ADVANCEDREALTIME = 0x00000400,
+	HAL_MPEG4_PROFILE_CORESCALABLE     = 0x00000800,
+	HAL_MPEG4_PROFILE_ADVANCEDCODING   = 0x00001000,
+	HAL_MPEG4_PROFILE_ADVANCEDCORE     = 0x00002000,
+	HAL_MPEG4_PROFILE_ADVANCEDSCALABLE = 0x00004000,
+	HAL_MPEG4_PROFILE_SIMPLESCALABLE   = 0x00008000,
+	HAL_UNUSED_MPEG4_PROFILE = 0x10000000,
+};
+
+enum hal_mpeg4_level {
+	HAL_MPEG4_LEVEL_0  = 0x00000001,
+	HAL_MPEG4_LEVEL_0b = 0x00000002,
+	HAL_MPEG4_LEVEL_1  = 0x00000004,
+	HAL_MPEG4_LEVEL_2  = 0x00000008,
+	HAL_MPEG4_LEVEL_3  = 0x00000010,
+	HAL_MPEG4_LEVEL_4  = 0x00000020,
+	HAL_MPEG4_LEVEL_4a = 0x00000040,
+	HAL_MPEG4_LEVEL_5  = 0x00000080,
+	HAL_MPEG4_LEVEL_VENDOR_START_UNUSED = 0x7F000000,
+	HAL_MPEG4_LEVEL_6  = 0x7F000001,
+	HAL_MPEG4_LEVEL_7  = 0x7F000002,
+	HAL_MPEG4_LEVEL_8  = 0x7F000003,
+	HAL_MPEG4_LEVEL_9  = 0x7F000004,
+	HAL_MPEG4_LEVEL_3b = 0x7F000005,
+	HAL_UNUSED_MPEG4_LEVEL = 0x10000000,
+};
+
+enum hal_h264_profile {
+	HAL_H264_PROFILE_BASELINE = 0x00000001,
+	HAL_H264_PROFILE_MAIN     = 0x00000002,
+	HAL_H264_PROFILE_HIGH     = 0x00000004,
+	HAL_H264_PROFILE_EXTENDED = 0x00000008,
+	HAL_H264_PROFILE_HIGH10   = 0x00000010,
+	HAL_H264_PROFILE_HIGH422  = 0x00000020,
+	HAL_H264_PROFILE_HIGH444  = 0x00000040,
+	HAL_H264_PROFILE_CONSTRAINED_BASE  = 0x00000080,
+	HAL_H264_PROFILE_CONSTRAINED_HIGH  = 0x00000100,
+	HAL_UNUSED_H264_PROFILE = 0x10000000,
+};
+
+enum hal_h264_level {
+	HAL_H264_LEVEL_1  = 0x00000001,
+	HAL_H264_LEVEL_1b = 0x00000002,
+	HAL_H264_LEVEL_11 = 0x00000004,
+	HAL_H264_LEVEL_12 = 0x00000008,
+	HAL_H264_LEVEL_13 = 0x00000010,
+	HAL_H264_LEVEL_2  = 0x00000020,
+	HAL_H264_LEVEL_21 = 0x00000040,
+	HAL_H264_LEVEL_22 = 0x00000080,
+	HAL_H264_LEVEL_3  = 0x00000100,
+	HAL_H264_LEVEL_31 = 0x00000200,
+	HAL_H264_LEVEL_32 = 0x00000400,
+	HAL_H264_LEVEL_4  = 0x00000800,
+	HAL_H264_LEVEL_41 = 0x00001000,
+	HAL_H264_LEVEL_42 = 0x00002000,
+	HAL_H264_LEVEL_5  = 0x00004000,
+	HAL_H264_LEVEL_51 = 0x00008000,
+	HAL_H264_LEVEL_52 = 0x00010000,
+	HAL_UNUSED_H264_LEVEL = 0x10000000,
+};
+
+enum hal_hevc_profile {
+	HAL_HEVC_PROFILE_MAIN           = 0x00000001,
+	HAL_HEVC_PROFILE_MAIN10         = 0x00000002,
+	HAL_HEVC_PROFILE_MAIN_STILL_PIC = 0x00000004,
+	HAL_UNUSED_HEVC_PROFILE         = 0x10000000,
+};
+
+enum hal_hevc_level {
+	HAL_HEVC_MAIN_TIER_LEVEL_1      = 0x10000001,
+	HAL_HEVC_MAIN_TIER_LEVEL_2      = 0x10000002,
+	HAL_HEVC_MAIN_TIER_LEVEL_2_1    = 0x10000004,
+	HAL_HEVC_MAIN_TIER_LEVEL_3      = 0x10000008,
+	HAL_HEVC_MAIN_TIER_LEVEL_3_1    = 0x10000010,
+	HAL_HEVC_MAIN_TIER_LEVEL_4      = 0x10000020,
+	HAL_HEVC_MAIN_TIER_LEVEL_4_1    = 0x10000040,
+	HAL_HEVC_MAIN_TIER_LEVEL_5      = 0x10000080,
+	HAL_HEVC_MAIN_TIER_LEVEL_5_1    = 0x10000100,
+	HAL_HEVC_MAIN_TIER_LEVEL_5_2    = 0x10000200,
+	HAL_HEVC_MAIN_TIER_LEVEL_6      = 0x10000400,
+	HAL_HEVC_MAIN_TIER_LEVEL_6_1    = 0x10000800,
+	HAL_HEVC_MAIN_TIER_LEVEL_6_2    = 0x10001000,
+	HAL_HEVC_HIGH_TIER_LEVEL_1      = 0x20000001,
+	HAL_HEVC_HIGH_TIER_LEVEL_2      = 0x20000002,
+	HAL_HEVC_HIGH_TIER_LEVEL_2_1    = 0x20000004,
+	HAL_HEVC_HIGH_TIER_LEVEL_3      = 0x20000008,
+	HAL_HEVC_HIGH_TIER_LEVEL_3_1    = 0x20000010,
+	HAL_HEVC_HIGH_TIER_LEVEL_4      = 0x20000020,
+	HAL_HEVC_HIGH_TIER_LEVEL_4_1    = 0x20000040,
+	HAL_HEVC_HIGH_TIER_LEVEL_5      = 0x20000080,
+	HAL_HEVC_HIGH_TIER_LEVEL_5_1    = 0x20000100,
+	HAL_HEVC_HIGH_TIER_LEVEL_5_2    = 0x20000200,
+	HAL_HEVC_HIGH_TIER_LEVEL_6      = 0x20000400,
+	HAL_HEVC_HIGH_TIER_LEVEL_6_1    = 0x20000800,
+	HAL_HEVC_HIGH_TIER_LEVEL_6_2    = 0x20001000,
+	HAL_UNUSED_HEVC_TIER_LEVEL      = 0x80000000,
+};
+
+enum hal_hevc_tier {
+	HAL_HEVC_TIER_MAIN   = 0x00000001,
+	HAL_HEVC_TIER_HIGH   = 0x00000002,
+	HAL_UNUSED_HEVC_TIER = 0x10000000,
+};
+
+enum hal_vpx_profile {
+	HAL_VPX_PROFILE_SIMPLE    = 0x00000001,
+	HAL_VPX_PROFILE_ADVANCED  = 0x00000002,
+	HAL_VPX_PROFILE_VERSION_0 = 0x00000004,
+	HAL_VPX_PROFILE_VERSION_1 = 0x00000008,
+	HAL_VPX_PROFILE_VERSION_2 = 0x00000010,
+	HAL_VPX_PROFILE_VERSION_3 = 0x00000020,
+	HAL_VPX_PROFILE_UNUSED = 0x10000000,
+};
+
+enum hal_vc1_profile {
+	HAL_VC1_PROFILE_SIMPLE   = 0x00000001,
+	HAL_VC1_PROFILE_MAIN     = 0x00000002,
+	HAL_VC1_PROFILE_ADVANCED = 0x00000004,
+	HAL_UNUSED_VC1_PROFILE = 0x10000000,
+};
+
+enum hal_vc1_level {
+	HAL_VC1_LEVEL_LOW    = 0x00000001,
+	HAL_VC1_LEVEL_MEDIUM = 0x00000002,
+	HAL_VC1_LEVEL_HIGH   = 0x00000004,
+	HAL_VC1_LEVEL_0      = 0x00000008,
+	HAL_VC1_LEVEL_1      = 0x00000010,
+	HAL_VC1_LEVEL_2      = 0x00000020,
+	HAL_VC1_LEVEL_3      = 0x00000040,
+	HAL_VC1_LEVEL_4      = 0x00000080,
+	HAL_UNUSED_VC1_LEVEL = 0x10000000,
+};
+
+enum hal_divx_format {
+	HAL_DIVX_FORMAT_4,
+	HAL_DIVX_FORMAT_5,
+	HAL_DIVX_FORMAT_6,
+	HAL_UNUSED_DIVX_FORMAT = 0x10000000,
+};
+
+enum hal_divx_profile {
+	HAL_DIVX_PROFILE_QMOBILE  = 0x00000001,
+	HAL_DIVX_PROFILE_MOBILE   = 0x00000002,
+	HAL_DIVX_PROFILE_MT       = 0x00000004,
+	HAL_DIVX_PROFILE_HT       = 0x00000008,
+	HAL_DIVX_PROFILE_HD       = 0x00000010,
+	HAL_UNUSED_DIVX_PROFILE = 0x10000000,
+};
+
+enum hal_mvc_profile {
+	HAL_MVC_PROFILE_STEREO_HIGH  = 0x00001000,
+	HAL_UNUSED_MVC_PROFILE = 0x10000000,
+};
+
+enum hal_mvc_level {
+	HAL_MVC_LEVEL_1  = 0x00000001,
+	HAL_MVC_LEVEL_1b = 0x00000002,
+	HAL_MVC_LEVEL_11 = 0x00000004,
+	HAL_MVC_LEVEL_12 = 0x00000008,
+	HAL_MVC_LEVEL_13 = 0x00000010,
+	HAL_MVC_LEVEL_2  = 0x00000020,
+	HAL_MVC_LEVEL_21 = 0x00000040,
+	HAL_MVC_LEVEL_22 = 0x00000080,
+	HAL_MVC_LEVEL_3  = 0x00000100,
+	HAL_MVC_LEVEL_31 = 0x00000200,
+	HAL_MVC_LEVEL_32 = 0x00000400,
+	HAL_MVC_LEVEL_4  = 0x00000800,
+	HAL_MVC_LEVEL_41 = 0x00001000,
+	HAL_MVC_LEVEL_42 = 0x00002000,
+	HAL_MVC_LEVEL_5  = 0x00004000,
+	HAL_MVC_LEVEL_51 = 0x00008000,
+	HAL_UNUSED_MVC_LEVEL = 0x10000000,
+};
+
+struct hal_frame_rate {
+	enum hal_buffer buffer_type;
+	u32 frame_rate;
+};
+
+enum hal_uncompressed_format {
+	HAL_COLOR_FORMAT_MONOCHROME     = 0x00000001,
+	HAL_COLOR_FORMAT_NV12           = 0x00000002,
+	HAL_COLOR_FORMAT_NV21           = 0x00000004,
+	HAL_COLOR_FORMAT_NV12_4x4TILE   = 0x00000008,
+	HAL_COLOR_FORMAT_NV21_4x4TILE   = 0x00000010,
+	HAL_COLOR_FORMAT_YUYV           = 0x00000020,
+	HAL_COLOR_FORMAT_YVYU           = 0x00000040,
+	HAL_COLOR_FORMAT_UYVY           = 0x00000080,
+	HAL_COLOR_FORMAT_VYUY           = 0x00000100,
+	HAL_COLOR_FORMAT_RGB565         = 0x00000200,
+	HAL_COLOR_FORMAT_BGR565         = 0x00000400,
+	HAL_COLOR_FORMAT_RGB888         = 0x00000800,
+	HAL_COLOR_FORMAT_BGR888         = 0x00001000,
+	HAL_COLOR_FORMAT_NV12_UBWC      = 0x00002000,
+	HAL_COLOR_FORMAT_NV12_TP10_UBWC = 0x00004000,
+	HAL_COLOR_FORMAT_RGBA8888       = 0x00008000,
+	HAL_COLOR_FORMAT_RGBA8888_UBWC  = 0x00010000,
+	HAL_UNUSED_COLOR                = 0x10000000,
+};
+
+enum hal_statistics_mode_type {
+	HAL_STATISTICS_MODE_DEFAULT	= 0x00000001,
+	HAL_STATISTICS_MODE_1		= 0x00000002,
+	HAL_STATISTICS_MODE_2		= 0x00000004,
+	HAL_STATISTICS_MODE_3		= 0x00000008,
+};
+
+enum hal_ssr_trigger_type {
+	SSR_ERR_FATAL = 1,
+	SSR_SW_DIV_BY_ZERO,
+	SSR_HW_WDOG_IRQ,
+};
+
+struct hal_uncompressed_format_select {
+	enum hal_buffer buffer_type;
+	enum hal_uncompressed_format format;
+};
+
+struct hal_uncompressed_plane_actual {
+	int actual_stride;
+	u32 actual_plane_buffer_height;
+};
+
+struct hal_uncompressed_plane_actual_info {
+	enum hal_buffer buffer_type;
+	u32 num_planes;
+	struct hal_uncompressed_plane_actual rg_plane_format[1];
+};
+
+struct hal_uncompressed_plane_constraints {
+	u32 stride_multiples;
+	u32 max_stride;
+	u32 min_plane_buffer_height_multiple;
+	u32 buffer_alignment;
+};
+
+struct hal_uncompressed_plane_actual_constraints_info {
+	enum hal_buffer buffer_type;
+	u32 num_planes;
+	struct hal_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+struct hal_extra_data_header_config {
+	u32 type;
+	enum hal_buffer buffer_type;
+	u32 version;
+	u32 port_index;
+	u32 client_extradata_id;
+};
+
+struct hal_frame_size {
+	enum hal_buffer buffer_type;
+	u32 width;
+	u32 height;
+};
+
+struct hal_enable {
+	bool enable;
+};
+
+struct hal_buffer_count_actual {
+	enum hal_buffer buffer_type;
+	u32 buffer_count_actual;
+};
+
+struct hal_buffer_size_minimum {
+	enum hal_buffer buffer_type;
+	u32 buffer_size;
+};
+
+struct hal_buffer_display_hold_count_actual {
+	enum hal_buffer buffer_type;
+	u32 hold_count;
+};
+
+enum hal_nal_stream_format {
+	HAL_NAL_FORMAT_STARTCODES         = 0x00000001,
+	HAL_NAL_FORMAT_ONE_NAL_PER_BUFFER = 0x00000002,
+	HAL_NAL_FORMAT_ONE_BYTE_LENGTH    = 0x00000004,
+	HAL_NAL_FORMAT_TWO_BYTE_LENGTH    = 0x00000008,
+	HAL_NAL_FORMAT_FOUR_BYTE_LENGTH   = 0x00000010,
+};
+
+enum hal_output_order {
+	HAL_OUTPUT_ORDER_DISPLAY,
+	HAL_OUTPUT_ORDER_DECODE,
+	HAL_UNUSED_OUTPUT = 0x10000000,
+};
+
+enum hal_picture {
+	HAL_PICTURE_I = 0x01,
+	HAL_PICTURE_P = 0x02,
+	HAL_PICTURE_B = 0x04,
+	HAL_PICTURE_IDR = 0x08,
+	HAL_PICTURE_CRA = 0x10,
+	HAL_FRAME_NOTCODED = 0x7F002000,
+	HAL_FRAME_YUV = 0x7F004000,
+	HAL_UNUSED_PICT = 0x10000000,
+};
+
+struct hal_extradata_enable {
+	u32 enable;
+	enum hal_extradata_id index;
+};
+
+struct hal_enable_picture {
+	u32 picture_type;
+};
+
+struct hal_multi_stream {
+	enum hal_buffer buffer_type;
+	u32 enable;
+	u32 width;
+	u32 height;
+};
+
+struct hal_display_picture_buffer_count {
+	u32 enable;
+	u32 count;
+};
+
+struct hal_mb_error_map {
+	u32 error_map_size;
+	u8 rg_error_map[1];
+};
+
+struct hal_request_iframe {
+	u32 enable;
+};
+
+struct hal_bitrate {
+	u32 bit_rate;
+	u32 layer_id;
+};
+
+struct hal_profile_level {
+	u32 profile;
+	u32 level;
+};
+
+struct hal_profile_level_supported {
+	u32 profile_count;
+	struct hal_profile_level profile_level[MAX_PROFILE_COUNT];
+};
+
+enum hal_h264_entropy {
+	HAL_H264_ENTROPY_CAVLC = 1,
+	HAL_H264_ENTROPY_CABAC = 2,
+	HAL_UNUSED_ENTROPY = 0x10000000,
+};
+
+enum hal_h264_cabac_model {
+	HAL_H264_CABAC_MODEL_0 = 1,
+	HAL_H264_CABAC_MODEL_1 = 2,
+	HAL_H264_CABAC_MODEL_2 = 4,
+	HAL_UNUSED_CABAC = 0x10000000,
+};
+
+struct hal_h264_entropy_control {
+	enum hal_h264_entropy entropy_mode;
+	enum hal_h264_cabac_model cabac_model;
+};
+
+enum hal_rate_control {
+	HAL_RATE_CONTROL_OFF,
+	HAL_RATE_CONTROL_VBR_VFR,
+	HAL_RATE_CONTROL_VBR_CFR,
+	HAL_RATE_CONTROL_CBR_VFR,
+	HAL_RATE_CONTROL_CBR_CFR,
+	HAL_RATE_CONTROL_MBR_CFR,
+	HAL_RATE_CONTROL_MBR_VFR,
+	HAL_UNUSED_RC = 0x10000000,
+};
+
+struct hal_mpeg4_time_resolution {
+	u32 time_increment_resolution;
+};
+
+struct hal_mpeg4_header_extension {
+	u32 header_extension;
+};
+
+enum hal_h264_db_mode {
+	HAL_H264_DB_MODE_DISABLE,
+	HAL_H264_DB_MODE_SKIP_SLICE_BOUNDARY,
+	HAL_H264_DB_MODE_ALL_BOUNDARY,
+	HAL_UNUSED_H264_DB = 0x10000000,
+};
+
+struct hal_h264_db_control {
+	enum hal_h264_db_mode mode;
+	int slice_alpha_offset;
+	int slice_beta_offset;
+};
+
+struct hal_temporal_spatial_tradeoff {
+	u32 ts_factor;
+};
+
+struct hal_quantization {
+	u32 qpi;
+	u32 qpp;
+	u32 qpb;
+	u32 layer_id;
+};
+
+struct hal_initial_quantization {
+	u32 qpi;
+	u32 qpp;
+	u32 qpb;
+	u32 init_qp_enable;
+};
+
+struct hal_quantization_range {
+	u32 min_qp;
+	u32 max_qp;
+	u32 layer_id;
+};
+
+struct hal_intra_period {
+	u32 pframes;
+	u32 bframes;
+};
+
+struct hal_idr_period {
+	u32 idr_period;
+};
+
+enum hal_rotate {
+	HAL_ROTATE_NONE,
+	HAL_ROTATE_90,
+	HAL_ROTATE_180,
+	HAL_ROTATE_270,
+	HAL_UNUSED_ROTATE = 0x10000000,
+};
+
+enum hal_flip {
+	HAL_FLIP_NONE,
+	HAL_FLIP_HORIZONTAL,
+	HAL_FLIP_VERTICAL,
+	HAL_UNUSED_FLIP = 0x10000000,
+};
+
+struct hal_operations {
+	enum hal_rotate rotate;
+	enum hal_flip flip;
+};
+
+enum hal_intra_refresh_mode {
+	HAL_INTRA_REFRESH_NONE,
+	HAL_INTRA_REFRESH_CYCLIC,
+	HAL_INTRA_REFRESH_ADAPTIVE,
+	HAL_INTRA_REFRESH_CYCLIC_ADAPTIVE,
+	HAL_INTRA_REFRESH_RANDOM,
+	HAL_UNUSED_INTRA = 0x10000000,
+};
+
+struct hal_intra_refresh {
+	enum hal_intra_refresh_mode mode;
+	u32 air_mbs;
+	u32 air_ref;
+	u32 cir_mbs;
+};
+
+enum hal_multi_slice {
+	HAL_MULTI_SLICE_OFF,
+	HAL_MULTI_SLICE_BY_MB_COUNT,
+	HAL_MULTI_SLICE_BY_BYTE_COUNT,
+	HAL_MULTI_SLICE_GOB,
+	HAL_UNUSED_SLICE = 0x10000000,
+};
+
+struct hal_multi_slice_control {
+	enum hal_multi_slice multi_slice;
+	u32 slice_size;
+};
+
+struct hal_debug_config {
+	u32 debug_config;
+};
+
+struct hal_buffer_requirements {
+	enum hal_buffer buffer_type;
+	u32 buffer_size;
+	u32 buffer_region_size;
+	u32 buffer_hold_count;
+	u32 buffer_count_min;
+	u32 buffer_count_actual;
+	u32 contiguous;
+	u32 buffer_alignment;
+};
+
+enum hal_priority {/* Priority increases with number */
+	HAL_PRIORITY_LOW = 10,
+	HAL_PRIOIRTY_MEDIUM = 20,
+	HAL_PRIORITY_HIGH = 30,
+	HAL_UNUSED_PRIORITY = 0x10000000,
+};
+
+struct hal_batch_info {
+	u32 input_batch_count;
+	u32 output_batch_count;
+};
+
+struct hal_metadata_pass_through {
+	u32 enable;
+	u32 size;
+};
+
+struct hal_uncompressed_format_supported {
+	enum hal_buffer buffer_type;
+	u32 format_entries;
+	u32 rg_format_info[1];
+};
+
+enum hal_interlace_format {
+	HAL_INTERLACE_FRAME_PROGRESSIVE                 = 0x01,
+	HAL_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST    = 0x02,
+	HAL_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04,
+	HAL_INTERLACE_FRAME_TOPFIELDFIRST               = 0x08,
+	HAL_INTERLACE_FRAME_BOTTOMFIELDFIRST            = 0x10,
+	HAL_UNUSED_INTERLACE = 0x10000000,
+};
+
+struct hal_interlace_format_supported {
+	enum hal_buffer buffer_type;
+	enum hal_interlace_format format;
+};
+
+enum hal_chroma_site {
+	HAL_CHROMA_SITE_0,
+	HAL_CHROMA_SITE_1,
+	HAL_UNUSED_CHROMA = 0x10000000,
+};
+
+struct hal_properties_supported {
+	u32 num_properties;
+	u32 rg_properties[1];
+};
+
+enum hal_capability {
+	HAL_CAPABILITY_FRAME_WIDTH = 0x1,
+	HAL_CAPABILITY_FRAME_HEIGHT,
+	HAL_CAPABILITY_MBS_PER_FRAME,
+	HAL_CAPABILITY_MBS_PER_SECOND,
+	HAL_CAPABILITY_FRAMERATE,
+	HAL_CAPABILITY_SCALE_X,
+	HAL_CAPABILITY_SCALE_Y,
+	HAL_CAPABILITY_BITRATE,
+	HAL_CAPABILITY_BFRAME,
+	HAL_CAPABILITY_PEAKBITRATE,
+	HAL_CAPABILITY_HIER_P_NUM_ENH_LAYERS,
+	HAL_CAPABILITY_ENC_LTR_COUNT,
+	HAL_CAPABILITY_SECURE_OUTPUT2_THRESHOLD,
+	HAL_CAPABILITY_HIER_B_NUM_ENH_LAYERS,
+	HAL_CAPABILITY_LCU_SIZE,
+	HAL_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS,
+	HAL_CAPABILITY_MBS_PER_SECOND_POWER_SAVE,
+	HAL_UNUSED_CAPABILITY = 0x10000000,
+};
+
+struct hal_capability_supported {
+	enum hal_capability capability_type;
+	u32 min;
+	u32 max;
+	u32 step_size;
+};
+
+struct hal_capability_supported_info {
+	u32 num_capabilities;
+	struct hal_capability_supported rg_data[1];
+};
+
+struct hal_nal_stream_format_supported {
+	u32 nal_stream_format_supported;
+};
+
+struct hal_nal_stream_format_select {
+	u32 nal_stream_format_select;
+};
+
+struct hal_multi_view_format {
+	u32 views;
+	u32 rg_view_order[1];
+};
+
+enum hal_buffer_layout_type {
+	HAL_BUFFER_LAYOUT_TOP_BOTTOM,
+	HAL_BUFFER_LAYOUT_SEQ,
+	HAL_UNUSED_BUFFER_LAYOUT = 0x10000000,
+};
+
+struct hal_mvc_buffer_layout {
+	enum hal_buffer_layout_type layout_type;
+	u32 bright_view_first;
+	u32 ngap;
+};
+
+struct hal_seq_header_info {
+	u32 nax_header_len;
+};
+
+struct hal_aspect_ratio {
+	u32 aspect_width;
+	u32 aspect_height;
+};
+
+struct hal_codec_supported {
+	u32 decoder_codec_supported;
+	u32 encoder_codec_supported;
+};
+
+struct hal_multi_view_select {
+	u32 view_index;
+};
+
+struct hal_timestamp_scale {
+	u32 time_stamp_scale;
+};
+
+
+struct hal_h264_vui_timing_info {
+	u32 enable;
+	u32 fixed_frame_rate;
+	u32 time_scale;
+};
+
+struct hal_h264_vui_bitstream_restrc {
+	u32 enable;
+};
+
+struct hal_preserve_text_quality {
+	u32 enable;
+};
+
+struct hal_vc1e_perf_cfg_type {
+	struct {
+		u32 x_subsampled;
+		u32 y_subsampled;
+	} i_frame, p_frame, b_frame;
+};
+
+struct hal_vpe_color_space_conversion {
+	u32 csc_matrix[HAL_MAX_MATRIX_COEFFS];
+	u32 csc_bias[HAL_MAX_BIAS_COEFFS];
+	u32 csc_limit[HAL_MAX_LIMIT_COEFFS];
+};
+
+struct hal_video_signal_info {
+	u32 color_space;
+	u32 transfer_chars;
+	u32 matrix_coeffs;
+	bool full_range;
+};
+
+enum hal_iframesize_type {
+	HAL_IFRAMESIZE_TYPE_DEFAULT,
+	HAL_IFRAMESIZE_TYPE_MEDIUM,
+	HAL_IFRAMESIZE_TYPE_HUGE,
+	HAL_IFRAMESIZE_TYPE_UNLIMITED,
+};
+
+enum vidc_resource_id {
+	VIDC_RESOURCE_NONE,
+	VIDC_RESOURCE_OCMEM,
+	VIDC_RESOURCE_VMEM,
+	VIDC_UNUSED_RESOURCE = 0x10000000,
+};
+
+struct vidc_resource_hdr {
+	enum vidc_resource_id resource_id;
+	void *resource_handle;
+	u32 size;
+};
+
+struct vidc_buffer_addr_info {
+	enum hal_buffer buffer_type;
+	u32 buffer_size;
+	u32 num_buffers;
+	ion_phys_addr_t align_device_addr;
+	ion_phys_addr_t extradata_addr;
+	u32 extradata_size;
+	u32 response_required;
+};
+
+/* Needs to be exactly the same as hfi_buffer_info */
+struct hal_buffer_info {
+	u32 buffer_addr;
+	u32 extra_data_addr;
+};
+
+struct vidc_frame_plane_config {
+	u32 left;
+	u32 top;
+	u32 width;
+	u32 height;
+	u32 stride;
+	u32 scan_lines;
+};
+
+struct vidc_uncompressed_frame_config {
+	struct vidc_frame_plane_config luma_plane;
+	struct vidc_frame_plane_config chroma_plane;
+};
+
+struct vidc_frame_data {
+	enum hal_buffer buffer_type;
+	ion_phys_addr_t device_addr;
+	ion_phys_addr_t extradata_addr;
+	int64_t timestamp;
+	u32 flags;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	u32 mark_target;
+	u32 mark_data;
+	u32 clnt_data;
+	u32 extradata_size;
+};
+
+struct vidc_seq_hdr {
+	ion_phys_addr_t seq_hdr;
+	u32 seq_hdr_len;
+};
+
+struct hal_fw_info {
+	char version[VENUS_VERSION_LENGTH];
+	phys_addr_t base_addr;
+	int register_base;
+	int register_size;
+	int irq;
+};
+
+enum hal_flush {
+	HAL_FLUSH_INPUT,
+	HAL_FLUSH_OUTPUT,
+	HAL_FLUSH_ALL,
+	HAL_UNUSED_FLUSH = 0x10000000,
+};
+
+enum hal_event_type {
+	HAL_EVENT_SEQ_CHANGED_SUFFICIENT_RESOURCES,
+	HAL_EVENT_SEQ_CHANGED_INSUFFICIENT_RESOURCES,
+	HAL_EVENT_RELEASE_BUFFER_REFERENCE,
+	HAL_UNUSED_SEQCHG = 0x10000000,
+};
+
+enum buffer_mode_type {
+	HAL_BUFFER_MODE_STATIC = 0x001,
+	HAL_BUFFER_MODE_RING = 0x010,
+	HAL_BUFFER_MODE_DYNAMIC = 0x100,
+};
+
+struct hal_buffer_alloc_mode {
+	enum hal_buffer buffer_type;
+	enum buffer_mode_type buffer_mode;
+};
+
+enum ltr_mode {
+	HAL_LTR_MODE_DISABLE,
+	HAL_LTR_MODE_MANUAL,
+	HAL_LTR_MODE_PERIODIC,
+};
+
+struct hal_ltr_mode {
+	enum ltr_mode mode;
+	u32 count;
+	u32 trust_mode;
+};
+
+struct hal_ltr_use {
+	u32 ref_ltr;
+	u32 use_constraint;
+	u32 frames;
+};
+
+struct hal_ltr_mark {
+	u32 mark_frame;
+};
+
+enum hal_perf_mode {
+	HAL_PERF_MODE_POWER_SAVE,
+	HAL_PERF_MODE_POWER_MAX_QUALITY,
+};
+
+struct hal_hybrid_hierp {
+	u32 layers;
+};
+
+struct hal_scs_threshold {
+	u32 threshold_value;
+};
+
+struct buffer_requirements {
+	struct hal_buffer_requirements buffer[HAL_BUFFER_MAX];
+};
+
+union hal_get_property {
+	struct hal_frame_rate frame_rate;
+	struct hal_uncompressed_format_select format_select;
+	struct hal_uncompressed_plane_actual plane_actual;
+	struct hal_uncompressed_plane_actual_info plane_actual_info;
+	struct hal_uncompressed_plane_constraints plane_constraints;
+	struct hal_uncompressed_plane_actual_constraints_info
+						plane_constraints_info;
+	struct hal_extra_data_header_config extra_data_header_config;
+	struct hal_frame_size frame_size;
+	struct hal_enable enable;
+	struct hal_buffer_count_actual buffer_count_actual;
+	struct hal_extradata_enable extradata_enable;
+	struct hal_enable_picture enable_picture;
+	struct hal_multi_stream multi_stream;
+	struct hal_display_picture_buffer_count display_picture_buffer_count;
+	struct hal_mb_error_map mb_error_map;
+	struct hal_request_iframe request_iframe;
+	struct hal_bitrate bitrate;
+	struct hal_profile_level profile_level;
+	struct hal_profile_level_supported profile_level_supported;
+	struct hal_mpeg4_time_resolution mpeg4_time_resolution;
+	struct hal_mpeg4_header_extension mpeg4_header_extension;
+	struct hal_h264_db_control h264_db_control;
+	struct hal_temporal_spatial_tradeoff temporal_spatial_tradeoff;
+	struct hal_quantization quantization;
+	struct hal_quantization_range quantization_range;
+	struct hal_intra_period intra_period;
+	struct hal_idr_period idr_period;
+	struct hal_operations operations;
+	struct hal_intra_refresh intra_refresh;
+	struct hal_multi_slice_control multi_slice_control;
+	struct hal_debug_config debug_config;
+	struct hal_batch_info batch_info;
+	struct hal_metadata_pass_through metadata_pass_through;
+	struct hal_uncompressed_format_supported uncompressed_format_supported;
+	struct hal_interlace_format_supported interlace_format_supported;
+	struct hal_properties_supported properties_supported;
+	struct hal_capability_supported capability_supported;
+	struct hal_capability_supported_info capability_supported_info;
+	struct hal_nal_stream_format_supported nal_stream_format_supported;
+	struct hal_nal_stream_format_select nal_stream_format_select;
+	struct hal_multi_view_format multi_view_format;
+	struct hal_seq_header_info seq_header_info;
+	struct hal_codec_supported codec_supported;
+	struct hal_multi_view_select multi_view_select;
+	struct hal_timestamp_scale timestamp_scale;
+	struct hal_h264_vui_timing_info h264_vui_timing_info;
+	struct hal_h264_vui_bitstream_restrc h264_vui_bitstream_restrc;
+	struct hal_preserve_text_quality preserve_text_quality;
+	struct hal_buffer_info buffer_info;
+	struct hal_buffer_alloc_mode buffer_alloc_mode;
+	struct buffer_requirements buf_req;
+	enum hal_h264_entropy h264_entropy;
+};
+
+/* HAL Response */
+#define IS_HAL_SYS_CMD(cmd) ((cmd) >= HAL_SYS_INIT_DONE && \
+		(cmd) <= HAL_SYS_ERROR)
+#define IS_HAL_SESSION_CMD(cmd) ((cmd) >= HAL_SESSION_EVENT_CHANGE && \
+		(cmd) <= HAL_SESSION_ERROR)
+enum hal_command_response {
+	/* SYSTEM COMMANDS_DONE*/
+	HAL_SYS_INIT_DONE,
+	HAL_SYS_SET_RESOURCE_DONE,
+	HAL_SYS_RELEASE_RESOURCE_DONE,
+	HAL_SYS_PING_ACK_DONE,
+	HAL_SYS_PC_PREP_DONE,
+	HAL_SYS_IDLE,
+	HAL_SYS_DEBUG,
+	HAL_SYS_WATCHDOG_TIMEOUT,
+	HAL_SYS_ERROR,
+	/* SESSION COMMANDS_DONE */
+	HAL_SESSION_EVENT_CHANGE,
+	HAL_SESSION_LOAD_RESOURCE_DONE,
+	HAL_SESSION_INIT_DONE,
+	HAL_SESSION_END_DONE,
+	HAL_SESSION_ABORT_DONE,
+	HAL_SESSION_START_DONE,
+	HAL_SESSION_STOP_DONE,
+	HAL_SESSION_ETB_DONE,
+	HAL_SESSION_FTB_DONE,
+	HAL_SESSION_FLUSH_DONE,
+	HAL_SESSION_SUSPEND_DONE,
+	HAL_SESSION_RESUME_DONE,
+	HAL_SESSION_SET_PROP_DONE,
+	HAL_SESSION_GET_PROP_DONE,
+	HAL_SESSION_PARSE_SEQ_HDR_DONE,
+	HAL_SESSION_GET_SEQ_HDR_DONE,
+	HAL_SESSION_RELEASE_BUFFER_DONE,
+	HAL_SESSION_RELEASE_RESOURCE_DONE,
+	HAL_SESSION_PROPERTY_INFO,
+	HAL_SESSION_ERROR,
+	HAL_RESPONSE_UNUSED = 0x10000000,
+};
+
+struct vidc_hal_ebd {
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags;
+	enum vidc_status status;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 offset;
+	u32 alloc_len;
+	u32 filled_len;
+	enum hal_picture picture_type;
+	ion_phys_addr_t packet_buffer;
+	ion_phys_addr_t extra_data_buffer;
+};
+
+struct vidc_hal_fbd {
+	u32 stream_id;
+	u32 view_id;
+	u32 timestamp_hi;
+	u32 timestamp_lo;
+	u32 flags1;
+	u32 mark_target;
+	u32 mark_data;
+	u32 stats;
+	u32 alloc_len1;
+	u32 filled_len1;
+	u32 offset1;
+	u32 frame_width;
+	u32 frame_height;
+	u32 start_x_coord;
+	u32 start_y_coord;
+	u32 input_tag;
+	u32 input_tag1;
+	enum hal_picture picture_type;
+	ion_phys_addr_t packet_buffer1;
+	ion_phys_addr_t extra_data_buffer;
+	u32 flags2;
+	u32 alloc_len2;
+	u32 filled_len2;
+	u32 offset2;
+	ion_phys_addr_t packet_buffer2;
+	u32 flags3;
+	u32 alloc_len3;
+	u32 filled_len3;
+	u32 offset3;
+	ion_phys_addr_t packet_buffer3;
+	enum hal_buffer buffer_type;
+};
+
+struct msm_vidc_capability {
+	enum hal_domain domain;
+	enum hal_video_codec codec;
+	struct hal_capability_supported width;
+	struct hal_capability_supported height;
+	struct hal_capability_supported mbs_per_frame;
+	struct hal_capability_supported mbs_per_sec;
+	struct hal_capability_supported frame_rate;
+	struct hal_capability_supported scale_x;
+	struct hal_capability_supported scale_y;
+	struct hal_capability_supported bitrate;
+	struct hal_capability_supported bframe;
+	struct hal_capability_supported peakbitrate;
+	struct hal_capability_supported hier_p;
+	struct hal_capability_supported ltr_count;
+	struct hal_capability_supported secure_output2_threshold;
+	struct hal_capability_supported hier_b;
+	struct hal_capability_supported lcu_size;
+	struct hal_capability_supported hier_p_hybrid;
+	struct hal_capability_supported mbs_per_sec_power_save;
+	struct hal_profile_level_supported profile_level;
+	struct hal_uncompressed_format_supported uncomp_format;
+	struct hal_interlace_format_supported HAL_format;
+	struct hal_nal_stream_format_supported nal_stream_format;
+	struct hal_intra_refresh intra_refresh;
+	enum buffer_mode_type alloc_mode_out;
+	enum buffer_mode_type alloc_mode_in;
+	u32 pixelprocess_capabilities;
+};
+
+struct vidc_hal_sys_init_done {
+	u32 dec_codec_supported;
+	u32 enc_codec_supported;
+	u32 codec_count;
+	struct msm_vidc_capability *capabilities;
+	u32 max_sessions_supported;
+};
+
+struct vidc_hal_session_init_done {
+	struct msm_vidc_capability capability;
+};
+
+struct msm_vidc_cb_cmd_done {
+	u32 device_id;
+	void *session_id;
+	enum vidc_status status;
+	u32 size;
+	union {
+		struct vidc_resource_hdr resource_hdr;
+		struct vidc_buffer_addr_info buffer_addr_info;
+		struct vidc_frame_plane_config frame_plane_config;
+		struct vidc_uncompressed_frame_config uncompressed_frame_config;
+		struct vidc_frame_data frame_data;
+		struct vidc_seq_hdr seq_hdr;
+		struct vidc_hal_ebd ebd;
+		struct vidc_hal_fbd fbd;
+		struct vidc_hal_sys_init_done sys_init_done;
+		struct vidc_hal_session_init_done session_init_done;
+		struct hal_buffer_info buffer_info;
+		union hal_get_property property;
+		enum hal_flush flush_type;
+	} data;
+};
+
+struct msm_vidc_cb_event {
+	u32 device_id;
+	void *session_id;
+	enum vidc_status status;
+	u32 height;
+	u32 width;
+	enum msm_vidc_pixel_depth bit_depth;
+	u32 hal_event_type;
+	ion_phys_addr_t packet_buffer;
+	ion_phys_addr_t extra_data_buffer;
+	u32 pic_struct;
+	u32 colour_space;
+};
+
+struct msm_vidc_cb_data_done {
+	u32 device_id;
+	void *session_id;
+	enum vidc_status status;
+	u32 size;
+	u32 clnt_data;
+	union {
+		struct vidc_hal_ebd input_done;
+		struct vidc_hal_fbd output_done;
+	};
+};
+
+struct msm_vidc_cb_info {
+	enum hal_command_response response_type;
+	union {
+		struct msm_vidc_cb_cmd_done cmd;
+		struct msm_vidc_cb_event event;
+		struct msm_vidc_cb_data_done data;
+	} response;
+};
+
+enum msm_vidc_hfi_type {
+	VIDC_HFI_VENUS,
+};
+
+enum msm_vidc_thermal_level {
+	VIDC_THERMAL_NORMAL = 0,
+	VIDC_THERMAL_LOW,
+	VIDC_THERMAL_HIGH,
+	VIDC_THERMAL_CRITICAL
+};
+
+enum vidc_vote_data_session {
+	VIDC_BUS_VOTE_DATA_SESSION_INVALID = 0,
+	/*
+	 * No declarations exist. Values generated by VIDC_VOTE_DATA_SESSION_VAL
+	 * describe the enumerations e.g.:
+	 *
+	 * enum vidc_bus_vote_data_session_type h264_decoder_session =
+	 *        VIDC_VOTE_DATA_SESSION_VAL(HAL_VIDEO_CODEC_H264,
+	 *                 HAL_VIDEO_DOMAIN_DECODER);
+	 */
+};
+
+/*
+ * Careful modifying VIDC_VOTE_DATA_SESSION_VAL().
+ *
+ * This macro assigns two bits to each codec: the lower bit denoting the codec
+ * type, and the higher bit denoting session type.
+ */
+static inline enum vidc_vote_data_session VIDC_VOTE_DATA_SESSION_VAL(
+		enum hal_video_codec c, enum hal_domain d) {
+	if (d != HAL_VIDEO_DOMAIN_ENCODER && d != HAL_VIDEO_DOMAIN_DECODER)
+		return VIDC_BUS_VOTE_DATA_SESSION_INVALID;
+
+	return (1 << ilog2(c) * 2) | ((d - 1) << (ilog2(c) * 2 + 1));
+}
+
+struct msm_vidc_gov_data {
+	struct vidc_bus_vote_data *data;
+	u32 data_count;
+	int imem_size;
+};
+
+enum msm_vidc_power_mode {
+	VIDC_POWER_NORMAL = 0,
+	VIDC_POWER_LOW,
+	VIDC_POWER_TURBO
+};
+
+struct vidc_bus_vote_data {
+	enum hal_domain domain;
+	enum hal_video_codec codec;
+	enum hal_uncompressed_format color_formats[2];
+	int num_formats; /* 1 = DPB-OPB unified; 2 = split */
+	int height, width, fps;
+	enum msm_vidc_power_mode power_mode;
+	struct imem_ab_table *imem_ab_tbl;
+	u32 imem_ab_tbl_size;
+	unsigned long core_freq;
+};
+
+struct vidc_clk_scale_data {
+	enum vidc_vote_data_session session[VIDC_MAX_SESSIONS];
+	enum msm_vidc_power_mode power_mode[VIDC_MAX_SESSIONS];
+	u32 load[VIDC_MAX_SESSIONS];
+	int num_sessions;
+};
+
+struct hal_index_extradata_input_crop_payload {
+	u32 size;
+	u32 version;
+	u32 port_index;
+	u32 left;
+	u32 top;
+	u32 width;
+	u32 height;
+};
+
+struct hal_cmd_sys_get_property_packet {
+	u32 size;
+	u32 packet_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+#define call_hfi_op(q, op, args...)			\
+	(((q) && (q)->op) ? ((q)->op(args)) : 0)
+
+struct hfi_device {
+	void *hfi_device_data;
+
+	/*Add function pointers for all the hfi functions below*/
+	int (*core_init)(void *device);
+	int (*core_release)(void *device);
+	int (*core_ping)(void *device);
+	int (*core_trigger_ssr)(void *device, enum hal_ssr_trigger_type);
+	int (*session_init)(void *device, void *session_id,
+		enum hal_domain session_type, enum hal_video_codec codec_type,
+		void **new_session);
+	int (*session_end)(void *session);
+	int (*session_abort)(void *session);
+	int (*session_set_buffers)(void *sess,
+				struct vidc_buffer_addr_info *buffer_info);
+	int (*session_release_buffers)(void *sess,
+				struct vidc_buffer_addr_info *buffer_info);
+	int (*session_load_res)(void *sess);
+	int (*session_release_res)(void *sess);
+	int (*session_start)(void *sess);
+	int (*session_continue)(void *sess);
+	int (*session_stop)(void *sess);
+	int (*session_etb)(void *sess, struct vidc_frame_data *input_frame);
+	int (*session_ftb)(void *sess, struct vidc_frame_data *output_frame);
+	int (*session_process_batch)(void *sess,
+		int num_etbs, struct vidc_frame_data etbs[],
+		int num_ftbs, struct vidc_frame_data ftbs[]);
+	int (*session_parse_seq_hdr)(void *sess,
+			struct vidc_seq_hdr *seq_hdr);
+	int (*session_get_seq_hdr)(void *sess,
+			struct vidc_seq_hdr *seq_hdr);
+	int (*session_get_buf_req)(void *sess);
+	int (*session_flush)(void *sess, enum hal_flush flush_mode);
+	int (*session_set_property)(void *sess, enum hal_property ptype,
+			void *pdata);
+	int (*session_get_property)(void *sess, enum hal_property ptype);
+	int (*scale_clocks)(void *dev, int load,
+			struct vidc_clk_scale_data *data,
+			unsigned long instant_bitrate);
+	int (*vote_bus)(void *dev, struct vidc_bus_vote_data *data,
+			int num_data);
+	int (*get_fw_info)(void *dev, struct hal_fw_info *fw_info);
+	int (*session_clean)(void *sess);
+	int (*get_core_capabilities)(void *dev);
+	int (*suspend)(void *dev);
+	int (*flush_debug_queue)(void *dev);
+	unsigned long (*get_core_clock_rate)(void *dev, bool actual_rate);
+	enum hal_default_properties (*get_default_properties)(void *dev);
+};
+
+typedef void (*hfi_cmd_response_callback) (enum hal_command_response cmd,
+			void *data);
+typedef void (*msm_vidc_callback) (u32 response, void *callback);
+
+struct hfi_device *vidc_hfi_initialize(enum msm_vidc_hfi_type hfi_type,
+		u32 device_id, struct msm_vidc_platform_resources *res,
+		hfi_cmd_response_callback callback);
+void vidc_hfi_deinitialize(enum msm_vidc_hfi_type hfi_type,
+			struct hfi_device *hdev);
+u32 vidc_get_hfi_domain(enum hal_domain hal_domain);
+u32 vidc_get_hfi_codec(enum hal_video_codec hal_codec);
+enum hal_domain vidc_get_hal_domain(u32 hfi_domain);
+enum hal_video_codec vidc_get_hal_codec(u32 hfi_codec);
+
+#endif /*__VIDC_HFI_API_H__ */
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_helper.h b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
new file mode 100644
index 0000000..9ab1a81
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_helper.h
@@ -0,0 +1,1180 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __H_VIDC_HFI_HELPER_H__
+#define __H_VIDC_HFI_HELPER_H__
+
+#define HFI_COMMON_BASE				(0)
+#define HFI_OX_BASE					(0x01000000)
+
+#define HFI_VIDEO_DOMAIN_ENCODER	(HFI_COMMON_BASE + 0x1)
+#define HFI_VIDEO_DOMAIN_DECODER	(HFI_COMMON_BASE + 0x2)
+#define HFI_VIDEO_DOMAIN_VPE		(HFI_COMMON_BASE + 0x4)
+#define HFI_VIDEO_DOMAIN_MBI		(HFI_COMMON_BASE + 0x8)
+
+#define HFI_DOMAIN_BASE_COMMON		(HFI_COMMON_BASE + 0)
+#define HFI_DOMAIN_BASE_VDEC		(HFI_COMMON_BASE + 0x01000000)
+#define HFI_DOMAIN_BASE_VENC		(HFI_COMMON_BASE + 0x02000000)
+#define HFI_DOMAIN_BASE_VPE			(HFI_COMMON_BASE + 0x03000000)
+
+#define HFI_VIDEO_ARCH_OX			(HFI_COMMON_BASE + 0x1)
+
+#define HFI_ARCH_COMMON_OFFSET		(0)
+#define HFI_ARCH_OX_OFFSET			(0x00200000)
+
+#define  HFI_CMD_START_OFFSET		(0x00010000)
+#define  HFI_MSG_START_OFFSET		(0x00020000)
+
+#define HFI_ERR_NONE						HFI_COMMON_BASE
+#define HFI_ERR_SYS_FATAL				(HFI_COMMON_BASE + 0x1)
+#define HFI_ERR_SYS_INVALID_PARAMETER		(HFI_COMMON_BASE + 0x2)
+#define HFI_ERR_SYS_VERSION_MISMATCH		(HFI_COMMON_BASE + 0x3)
+#define HFI_ERR_SYS_INSUFFICIENT_RESOURCES	(HFI_COMMON_BASE + 0x4)
+#define HFI_ERR_SYS_MAX_SESSIONS_REACHED	(HFI_COMMON_BASE + 0x5)
+#define HFI_ERR_SYS_UNSUPPORTED_CODEC		(HFI_COMMON_BASE + 0x6)
+#define HFI_ERR_SYS_SESSION_IN_USE			(HFI_COMMON_BASE + 0x7)
+#define HFI_ERR_SYS_SESSION_ID_OUT_OF_RANGE	(HFI_COMMON_BASE + 0x8)
+#define HFI_ERR_SYS_UNSUPPORTED_DOMAIN		(HFI_COMMON_BASE + 0x9)
+
+#define HFI_ERR_SESSION_FATAL			(HFI_COMMON_BASE + 0x1001)
+#define HFI_ERR_SESSION_INVALID_PARAMETER	(HFI_COMMON_BASE + 0x1002)
+#define HFI_ERR_SESSION_BAD_POINTER		(HFI_COMMON_BASE + 0x1003)
+#define HFI_ERR_SESSION_INVALID_SESSION_ID	(HFI_COMMON_BASE + 0x1004)
+#define HFI_ERR_SESSION_INVALID_STREAM_ID	(HFI_COMMON_BASE + 0x1005)
+#define HFI_ERR_SESSION_INCORRECT_STATE_OPERATION		\
+	(HFI_COMMON_BASE + 0x1006)
+#define HFI_ERR_SESSION_UNSUPPORTED_PROPERTY	(HFI_COMMON_BASE + 0x1007)
+
+#define HFI_ERR_SESSION_UNSUPPORTED_SETTING	(HFI_COMMON_BASE + 0x1008)
+
+#define HFI_ERR_SESSION_INSUFFICIENT_RESOURCES	(HFI_COMMON_BASE + 0x1009)
+
+#define HFI_ERR_SESSION_STREAM_CORRUPT_OUTPUT_STALLED	\
+	(HFI_COMMON_BASE + 0x100A)
+
+#define HFI_ERR_SESSION_STREAM_CORRUPT		(HFI_COMMON_BASE + 0x100B)
+#define HFI_ERR_SESSION_ENC_OVERFLOW		(HFI_COMMON_BASE + 0x100C)
+#define HFI_ERR_SESSION_UNSUPPORTED_STREAM	(HFI_COMMON_BASE + 0x100D)
+#define HFI_ERR_SESSION_CMDSIZE			(HFI_COMMON_BASE + 0x100E)
+#define HFI_ERR_SESSION_UNSUPPORT_CMD		(HFI_COMMON_BASE + 0x100F)
+#define HFI_ERR_SESSION_UNSUPPORT_BUFFERTYPE	(HFI_COMMON_BASE + 0x1010)
+#define HFI_ERR_SESSION_BUFFERCOUNT_TOOSMALL	(HFI_COMMON_BASE + 0x1011)
+#define HFI_ERR_SESSION_INVALID_SCALE_FACTOR	(HFI_COMMON_BASE + 0x1012)
+#define HFI_ERR_SESSION_UPSCALE_NOT_SUPPORTED	(HFI_COMMON_BASE + 0x1013)
+
+#define HFI_EVENT_SYS_ERROR				(HFI_COMMON_BASE + 0x1)
+#define HFI_EVENT_SESSION_ERROR			(HFI_COMMON_BASE + 0x2)
+
+#define HFI_VIDEO_CODEC_H264				0x00000002
+#define HFI_VIDEO_CODEC_H263				0x00000004
+#define HFI_VIDEO_CODEC_MPEG1				0x00000008
+#define HFI_VIDEO_CODEC_MPEG2				0x00000010
+#define HFI_VIDEO_CODEC_MPEG4				0x00000020
+#define HFI_VIDEO_CODEC_DIVX_311			0x00000040
+#define HFI_VIDEO_CODEC_DIVX				0x00000080
+#define HFI_VIDEO_CODEC_VC1				0x00000100
+#define HFI_VIDEO_CODEC_SPARK				0x00000200
+#define HFI_VIDEO_CODEC_VP8				0x00001000
+#define HFI_VIDEO_CODEC_HEVC				0x00002000
+#define HFI_VIDEO_CODEC_VP9				0x00004000
+#define HFI_VIDEO_CODEC_HEVC_HYBRID			0x80000000
+
+#define HFI_H264_PROFILE_BASELINE			0x00000001
+#define HFI_H264_PROFILE_MAIN				0x00000002
+#define HFI_H264_PROFILE_HIGH				0x00000004
+#define HFI_H264_PROFILE_STEREO_HIGH		0x00000008
+#define HFI_H264_PROFILE_MULTIVIEW_HIGH		0x00000010
+#define HFI_H264_PROFILE_CONSTRAINED_BASE	0x00000020
+#define HFI_H264_PROFILE_CONSTRAINED_HIGH	0x00000040
+
+#define HFI_H264_LEVEL_1					0x00000001
+#define HFI_H264_LEVEL_1b					0x00000002
+#define HFI_H264_LEVEL_11					0x00000004
+#define HFI_H264_LEVEL_12					0x00000008
+#define HFI_H264_LEVEL_13					0x00000010
+#define HFI_H264_LEVEL_2					0x00000020
+#define HFI_H264_LEVEL_21					0x00000040
+#define HFI_H264_LEVEL_22					0x00000080
+#define HFI_H264_LEVEL_3					0x00000100
+#define HFI_H264_LEVEL_31					0x00000200
+#define HFI_H264_LEVEL_32					0x00000400
+#define HFI_H264_LEVEL_4					0x00000800
+#define HFI_H264_LEVEL_41					0x00001000
+#define HFI_H264_LEVEL_42					0x00002000
+#define HFI_H264_LEVEL_5					0x00004000
+#define HFI_H264_LEVEL_51					0x00008000
+#define HFI_H264_LEVEL_52                                       0x00010000
+
+#define HFI_H263_PROFILE_BASELINE			0x00000001
+
+#define HFI_H263_LEVEL_10					0x00000001
+#define HFI_H263_LEVEL_20					0x00000002
+#define HFI_H263_LEVEL_30					0x00000004
+#define HFI_H263_LEVEL_40					0x00000008
+#define HFI_H263_LEVEL_45					0x00000010
+#define HFI_H263_LEVEL_50					0x00000020
+#define HFI_H263_LEVEL_60					0x00000040
+#define HFI_H263_LEVEL_70					0x00000080
+
+#define HFI_MPEG2_PROFILE_SIMPLE			0x00000001
+#define HFI_MPEG2_PROFILE_MAIN				0x00000002
+#define HFI_MPEG2_PROFILE_422				0x00000004
+#define HFI_MPEG2_PROFILE_SNR				0x00000008
+#define HFI_MPEG2_PROFILE_SPATIAL			0x00000010
+#define HFI_MPEG2_PROFILE_HIGH				0x00000020
+
+#define HFI_MPEG2_LEVEL_LL					0x00000001
+#define HFI_MPEG2_LEVEL_ML					0x00000002
+#define HFI_MPEG2_LEVEL_H14					0x00000004
+#define HFI_MPEG2_LEVEL_HL					0x00000008
+
+#define HFI_MPEG4_PROFILE_SIMPLE			0x00000001
+#define HFI_MPEG4_PROFILE_ADVANCEDSIMPLE	0x00000002
+
+#define HFI_MPEG4_LEVEL_0					0x00000001
+#define HFI_MPEG4_LEVEL_0b					0x00000002
+#define HFI_MPEG4_LEVEL_1					0x00000004
+#define HFI_MPEG4_LEVEL_2					0x00000008
+#define HFI_MPEG4_LEVEL_3					0x00000010
+#define HFI_MPEG4_LEVEL_4					0x00000020
+#define HFI_MPEG4_LEVEL_4a					0x00000040
+#define HFI_MPEG4_LEVEL_5					0x00000080
+#define HFI_MPEG4_LEVEL_6					0x00000100
+#define HFI_MPEG4_LEVEL_7					0x00000200
+#define HFI_MPEG4_LEVEL_8					0x00000400
+#define HFI_MPEG4_LEVEL_9					0x00000800
+#define HFI_MPEG4_LEVEL_3b					0x00001000
+
+#define HFI_VC1_PROFILE_SIMPLE				0x00000001
+#define HFI_VC1_PROFILE_MAIN				0x00000002
+#define HFI_VC1_PROFILE_ADVANCED			0x00000004
+
+#define HFI_VC1_LEVEL_LOW					0x00000001
+#define HFI_VC1_LEVEL_MEDIUM				0x00000002
+#define HFI_VC1_LEVEL_HIGH					0x00000004
+#define HFI_VC1_LEVEL_0						0x00000008
+#define HFI_VC1_LEVEL_1						0x00000010
+#define HFI_VC1_LEVEL_2						0x00000020
+#define HFI_VC1_LEVEL_3						0x00000040
+#define HFI_VC1_LEVEL_4						0x00000080
+
+#define HFI_VPX_PROFILE_SIMPLE				0x00000001
+#define HFI_VPX_PROFILE_ADVANCED			0x00000002
+#define HFI_VPX_PROFILE_VERSION_0			0x00000004
+#define HFI_VPX_PROFILE_VERSION_1			0x00000008
+#define HFI_VPX_PROFILE_VERSION_2			0x00000010
+#define HFI_VPX_PROFILE_VERSION_3			0x00000020
+
+#define HFI_DIVX_FORMAT_4				(HFI_COMMON_BASE + 0x1)
+#define HFI_DIVX_FORMAT_5				(HFI_COMMON_BASE + 0x2)
+#define HFI_DIVX_FORMAT_6				(HFI_COMMON_BASE + 0x3)
+
+#define HFI_DIVX_PROFILE_QMOBILE		0x00000001
+#define HFI_DIVX_PROFILE_MOBILE			0x00000002
+#define HFI_DIVX_PROFILE_MT				0x00000004
+#define HFI_DIVX_PROFILE_HT				0x00000008
+#define HFI_DIVX_PROFILE_HD				0x00000010
+
+#define  HFI_HEVC_PROFILE_MAIN			0x00000001
+#define  HFI_HEVC_PROFILE_MAIN10		0x00000002
+#define  HFI_HEVC_PROFILE_MAIN_STILL_PIC	0x00000004
+
+#define  HFI_HEVC_LEVEL_1	0x00000001
+#define  HFI_HEVC_LEVEL_2	0x00000002
+#define  HFI_HEVC_LEVEL_21	0x00000004
+#define  HFI_HEVC_LEVEL_3	0x00000008
+#define  HFI_HEVC_LEVEL_31	0x00000010
+#define  HFI_HEVC_LEVEL_4	0x00000020
+#define  HFI_HEVC_LEVEL_41	0x00000040
+#define  HFI_HEVC_LEVEL_5	0x00000080
+#define  HFI_HEVC_LEVEL_51	0x00000100
+#define  HFI_HEVC_LEVEL_52	0x00000200
+#define  HFI_HEVC_LEVEL_6	0x00000400
+#define  HFI_HEVC_LEVEL_61	0x00000800
+#define  HFI_HEVC_LEVEL_62	0x00001000
+
+#define HFI_HEVC_TIER_MAIN	0x1
+#define HFI_HEVC_TIER_HIGH0	0x2
+
+#define HFI_BUFFER_INPUT				(HFI_COMMON_BASE + 0x1)
+#define HFI_BUFFER_OUTPUT				(HFI_COMMON_BASE + 0x2)
+#define HFI_BUFFER_OUTPUT2				(HFI_COMMON_BASE + 0x3)
+#define HFI_BUFFER_INTERNAL_PERSIST		(HFI_COMMON_BASE + 0x4)
+#define HFI_BUFFER_INTERNAL_PERSIST_1		(HFI_COMMON_BASE + 0x5)
+
+#define  HFI_BITDEPTH_8				(HFI_COMMON_BASE + 0x0)
+#define  HFI_BITDEPTH_9				(HFI_COMMON_BASE + 0x1)
+#define  HFI_BITDEPTH_10			(HFI_COMMON_BASE + 0x2)
+
+#define HFI_VENC_PERFMODE_MAX_QUALITY	0x1
+#define HFI_VENC_PERFMODE_POWER_SAVE	0x2
+
+struct hfi_buffer_info {
+	u32 buffer_addr;
+	u32 extra_data_addr;
+};
+
+#define HFI_PROPERTY_SYS_COMMON_START		\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x0000)
+#define HFI_PROPERTY_SYS_DEBUG_CONFIG		\
+	(HFI_PROPERTY_SYS_COMMON_START + 0x001)
+#define HFI_PROPERTY_SYS_RESOURCE_OCMEM_REQUIREMENT_INFO	\
+	(HFI_PROPERTY_SYS_COMMON_START + 0x002)
+#define HFI_PROPERTY_SYS_CONFIG_VCODEC_CLKFREQ				\
+	(HFI_PROPERTY_SYS_COMMON_START + 0x003)
+#define HFI_PROPERTY_SYS_IDLE_INDICATOR         \
+	(HFI_PROPERTY_SYS_COMMON_START + 0x004)
+#define  HFI_PROPERTY_SYS_CODEC_POWER_PLANE_CTRL     \
+	(HFI_PROPERTY_SYS_COMMON_START + 0x005)
+#define  HFI_PROPERTY_SYS_IMAGE_VERSION    \
+	(HFI_PROPERTY_SYS_COMMON_START + 0x006)
+#define  HFI_PROPERTY_SYS_CONFIG_COVERAGE    \
+	(HFI_PROPERTY_SYS_COMMON_START + 0x007)
+
+#define HFI_PROPERTY_PARAM_COMMON_START	\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x1000)
+#define HFI_PROPERTY_PARAM_FRAME_SIZE		\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x001)
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_PLANE_ACTUAL_INFO	\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x002)
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SELECT		\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x003)
+#define HFI_PROPERTY_PARAM_UNCOMPRESSED_FORMAT_SUPPORTED	\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x004)
+#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_CURRENT			\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x005)
+#define HFI_PROPERTY_PARAM_PROFILE_LEVEL_SUPPORTED			\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x006)
+#define HFI_PROPERTY_PARAM_CAPABILITY_SUPPORTED				\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x007)
+#define HFI_PROPERTY_PARAM_PROPERTIES_SUPPORTED				\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x008)
+#define HFI_PROPERTY_PARAM_CODEC_SUPPORTED			\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x009)
+#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SUPPORTED		\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00A)
+#define HFI_PROPERTY_PARAM_NAL_STREAM_FORMAT_SELECT			\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00B)
+#define HFI_PROPERTY_PARAM_MULTI_VIEW_FORMAT				\
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00C)
+#define  HFI_PROPERTY_PARAM_MAX_SEQUENCE_HEADER_SIZE        \
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00D)
+#define  HFI_PROPERTY_PARAM_CODEC_MASK_SUPPORTED            \
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00E)
+#define HFI_PROPERTY_PARAM_MVC_BUFFER_LAYOUT \
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x00F)
+#define  HFI_PROPERTY_PARAM_MAX_SESSIONS_SUPPORTED	    \
+	(HFI_PROPERTY_PARAM_COMMON_START + 0x010)
+
+#define HFI_PROPERTY_CONFIG_COMMON_START				\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + 0x2000)
+#define HFI_PROPERTY_CONFIG_FRAME_RATE					\
+	(HFI_PROPERTY_CONFIG_COMMON_START + 0x001)
+
+#define HFI_PROPERTY_PARAM_VDEC_COMMON_START				\
+	(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x3000)
+#define HFI_PROPERTY_PARAM_VDEC_MULTI_STREAM				\
+	(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x001)
+#define HFI_PROPERTY_PARAM_VDEC_CONCEAL_COLOR				\
+	(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x002)
+#define HFI_PROPERTY_PARAM_VDEC_NONCP_OUTPUT2				\
+	(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x003)
+#define  HFI_PROPERTY_PARAM_VDEC_PIXEL_BITDEPTH				\
+	(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x007)
+#define  HFI_PROPERTY_PARAM_VDEC_PIC_STRUCT				\
+	(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x009)
+#define  HFI_PROPERTY_PARAM_VDEC_COLOUR_SPACE				\
+	(HFI_PROPERTY_PARAM_VDEC_COMMON_START + 0x00A)
+
+
+#define HFI_PROPERTY_CONFIG_VDEC_COMMON_START				\
+	(HFI_DOMAIN_BASE_VDEC + HFI_ARCH_COMMON_OFFSET + 0x4000)
+
+#define HFI_PROPERTY_PARAM_VENC_COMMON_START				\
+	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x5000)
+#define HFI_PROPERTY_PARAM_VENC_SLICE_DELIVERY_MODE			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x001)
+#define HFI_PROPERTY_PARAM_VENC_H264_ENTROPY_CONTROL		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x002)
+#define HFI_PROPERTY_PARAM_VENC_H264_DEBLOCK_CONTROL		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x003)
+#define HFI_PROPERTY_PARAM_VENC_RATE_CONTROL				\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x004)
+#define  HFI_PROPERTY_PARAM_VENC_H264_PICORDER_CNT_TYPE     \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x005)
+#define HFI_PROPERTY_PARAM_VENC_SESSION_QP				\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x006)
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_AC_PREDICTION			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x007)
+#define  HFI_PROPERTY_PARAM_VENC_SESSION_QP_RANGE           \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x008)
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_TIME_RESOLUTION		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x009)
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_SHORT_HEADER			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00A)
+#define HFI_PROPERTY_PARAM_VENC_MPEG4_HEADER_EXTENSION		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00B)
+#define  HFI_PROPERTY_PARAM_VENC_OPEN_GOP                   \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00C)
+#define HFI_PROPERTY_PARAM_VENC_INTRA_REFRESH				\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00D)
+#define HFI_PROPERTY_PARAM_VENC_MULTI_SLICE_CONTROL			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00E)
+#define  HFI_PROPERTY_PARAM_VENC_VBV_HRD_BUF_SIZE           \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x00F)
+#define  HFI_PROPERTY_PARAM_VENC_QUALITY_VS_SPEED           \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x010)
+#define HFI_PROPERTY_PARAM_VENC_ADVANCED				\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x012)
+#define  HFI_PROPERTY_PARAM_VENC_H264_SPS_ID                \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x014)
+#define  HFI_PROPERTY_PARAM_VENC_H264_PPS_ID               \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x015)
+#define HFI_PROPERTY_PARAM_VENC_H264_GENERATE_AUDNAL	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x016)
+#define HFI_PROPERTY_PARAM_VENC_ASPECT_RATIO			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x017)
+#define HFI_PROPERTY_PARAM_VENC_NUMREF					\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x018)
+#define HFI_PROPERTY_PARAM_VENC_MULTIREF_P				\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x019)
+#define HFI_PROPERTY_PARAM_VENC_H264_NAL_SVC_EXT		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01B)
+#define HFI_PROPERTY_PARAM_VENC_LTRMODE		\
+	 (HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01C)
+#define HFI_PROPERTY_PARAM_VENC_VIDEO_SIGNAL_INFO	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01D)
+#define HFI_PROPERTY_PARAM_VENC_H264_VUI_TIMING_INFO	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01E)
+#define HFI_PROPERTY_PARAM_VENC_VC1_PERF_CFG		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x01F)
+#define  HFI_PROPERTY_PARAM_VENC_MAX_NUM_B_FRAMES \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x020)
+#define HFI_PROPERTY_PARAM_VENC_H264_VUI_BITSTREAM_RESTRC \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x021)
+#define HFI_PROPERTY_PARAM_VENC_LOW_LATENCY_MODE	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x022)
+#define HFI_PROPERTY_PARAM_VENC_PRESERVE_TEXT_QUALITY \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x023)
+#define HFI_PROPERTY_PARAM_VENC_H264_8X8_TRANSFORM \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x025)
+#define HFI_PROPERTY_PARAM_VENC_HIER_P_MAX_NUM_ENH_LAYER	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x026)
+#define HFI_PROPERTY_PARAM_VENC_DISABLE_RC_TIMESTAMP \
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x027)
+#define HFI_PROPERTY_PARAM_VENC_INITIAL_QP	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x028)
+#define HFI_PROPERTY_PARAM_VENC_VPX_ERROR_RESILIENCE_MODE	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x029)
+#define HFI_PROPERTY_PARAM_VENC_CONSTRAINED_INTRA_PRED	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02B)
+#define HFI_PROPERTY_PARAM_VENC_HIER_B_MAX_NUM_ENH_LAYER	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02C)
+#define  HFI_PROPERTY_PARAM_VENC_HIER_P_HYBRID_MODE	\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x02F)
+#define  HFI_PROPERTY_PARAM_VENC_BITRATE_TYPE		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x031)
+#define  HFI_PROPERTY_PARAM_VENC_VQZIP_SEI_TYPE		\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x033)
+#define  HFI_PROPERTY_PARAM_VENC_IFRAMESIZE			\
+	(HFI_PROPERTY_PARAM_VENC_COMMON_START + 0x034)
+
+#define HFI_PROPERTY_CONFIG_VENC_COMMON_START				\
+	(HFI_DOMAIN_BASE_VENC + HFI_ARCH_COMMON_OFFSET + 0x6000)
+#define HFI_PROPERTY_CONFIG_VENC_TARGET_BITRATE				\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x001)
+#define HFI_PROPERTY_CONFIG_VENC_IDR_PERIOD				\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x002)
+#define HFI_PROPERTY_CONFIG_VENC_INTRA_PERIOD				\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x003)
+#define HFI_PROPERTY_CONFIG_VENC_REQUEST_SYNC_FRAME			\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x004)
+#define  HFI_PROPERTY_CONFIG_VENC_SLICE_SIZE                \
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x005)
+#define HFI_PROPERTY_CONFIG_VENC_MAX_BITRATE				\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x007)
+
+#define HFI_PROPERTY_PARAM_VPE_COMMON_START				\
+	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x7000)
+#define  HFI_PROPERTY_CONFIG_VENC_SYNC_FRAME_SEQUENCE_HEADER	\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x008)
+#define  HFI_PROPERTY_CONFIG_VENC_MARKLTRFRAME			\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x009)
+#define  HFI_PROPERTY_CONFIG_VENC_USELTRFRAME			\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00A)
+#define  HFI_PROPERTY_CONFIG_VENC_HIER_P_ENH_LAYER		\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00B)
+#define  HFI_PROPERTY_CONFIG_VENC_LTRPERIOD			\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00C)
+#define  HFI_PROPERTY_CONFIG_VENC_PERF_MODE			\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00E)
+#define HFI_PROPERTY_CONFIG_VENC_BASELAYER_PRIORITYID		\
+	(HFI_PROPERTY_CONFIG_VENC_COMMON_START + 0x00F)
+
+#define HFI_PROPERTY_CONFIG_VPE_COMMON_START				\
+	(HFI_DOMAIN_BASE_VPE + HFI_ARCH_COMMON_OFFSET + 0x8000)
+#define  HFI_PROPERTY_CONFIG_VENC_BLUR_FRAME_SIZE		\
+	(HFI_PROPERTY_CONFIG_COMMON_START + 0x010)
+#define HFI_PROPERTY_CONFIG_VPE_DEINTERLACE				\
+	(HFI_PROPERTY_CONFIG_VPE_COMMON_START + 0x001)
+#define HFI_PROPERTY_CONFIG_VPE_OPERATIONS				\
+	(HFI_PROPERTY_CONFIG_VPE_COMMON_START + 0x002)
+
+struct hfi_pic_struct {
+	u32 progressive_only;
+};
+
+struct hfi_bitrate {
+	u32 bit_rate;
+	u32 layer_id;
+};
+
+struct hfi_colour_space {
+	u32 colour_space;
+};
+
+#define HFI_CAPABILITY_FRAME_WIDTH			(HFI_COMMON_BASE + 0x1)
+#define HFI_CAPABILITY_FRAME_HEIGHT			(HFI_COMMON_BASE + 0x2)
+#define HFI_CAPABILITY_MBS_PER_FRAME			(HFI_COMMON_BASE + 0x3)
+#define HFI_CAPABILITY_MBS_PER_SECOND			(HFI_COMMON_BASE + 0x4)
+#define HFI_CAPABILITY_FRAMERATE			(HFI_COMMON_BASE + 0x5)
+#define HFI_CAPABILITY_SCALE_X				(HFI_COMMON_BASE + 0x6)
+#define HFI_CAPABILITY_SCALE_Y				(HFI_COMMON_BASE + 0x7)
+#define HFI_CAPABILITY_BITRATE				(HFI_COMMON_BASE + 0x8)
+#define HFI_CAPABILITY_BFRAME				(HFI_COMMON_BASE + 0x9)
+#define HFI_CAPABILITY_PEAKBITRATE			(HFI_COMMON_BASE + 0xa)
+#define HFI_CAPABILITY_HIER_P_NUM_ENH_LAYERS		(HFI_COMMON_BASE + 0x10)
+#define HFI_CAPABILITY_ENC_LTR_COUNT			(HFI_COMMON_BASE + 0x11)
+#define HFI_CAPABILITY_CP_OUTPUT2_THRESH		(HFI_COMMON_BASE + 0x12)
+#define HFI_CAPABILITY_HIER_B_NUM_ENH_LAYERS	(HFI_COMMON_BASE + 0x13)
+#define HFI_CAPABILITY_LCU_SIZE				(HFI_COMMON_BASE + 0x14)
+#define HFI_CAPABILITY_HIER_P_HYBRID_NUM_ENH_LAYERS	(HFI_COMMON_BASE + 0x15)
+#define HFI_CAPABILITY_MBS_PER_SECOND_POWERSAVE		(HFI_COMMON_BASE + 0x16)
+
+struct hfi_capability_supported {
+	u32 capability_type;
+	u32 min;
+	u32 max;
+	u32 step_size;
+};
+
+struct hfi_capability_supported_info {
+	u32 num_capabilities;
+	struct hfi_capability_supported rg_data[1];
+};
+
+#define HFI_DEBUG_MSG_LOW					0x00000001
+#define HFI_DEBUG_MSG_MEDIUM					0x00000002
+#define HFI_DEBUG_MSG_HIGH					0x00000004
+#define HFI_DEBUG_MSG_ERROR					0x00000008
+#define HFI_DEBUG_MSG_FATAL					0x00000010
+#define HFI_DEBUG_MSG_PERF					0x00000020
+
+#define HFI_DEBUG_MODE_QUEUE					0x00000001
+#define HFI_DEBUG_MODE_QDSS					0x00000002
+
+struct hfi_debug_config {
+	u32 debug_config;
+	u32 debug_mode;
+};
+
+struct hfi_enable {
+	u32 enable;
+};
+
+#define HFI_H264_DB_MODE_DISABLE			(HFI_COMMON_BASE + 0x1)
+#define HFI_H264_DB_MODE_SKIP_SLICE_BOUNDARY	\
+	(HFI_COMMON_BASE + 0x2)
+#define HFI_H264_DB_MODE_ALL_BOUNDARY		(HFI_COMMON_BASE + 0x3)
+
+struct hfi_h264_db_control {
+	u32 mode;
+	u32 slice_alpha_offset;
+	u32 slice_beta_offset;
+};
+
+#define HFI_H264_ENTROPY_CAVLC				(HFI_COMMON_BASE + 0x1)
+#define HFI_H264_ENTROPY_CABAC				(HFI_COMMON_BASE + 0x2)
+
+#define HFI_H264_CABAC_MODEL_0				(HFI_COMMON_BASE + 0x1)
+#define HFI_H264_CABAC_MODEL_1				(HFI_COMMON_BASE + 0x2)
+#define HFI_H264_CABAC_MODEL_2				(HFI_COMMON_BASE + 0x3)
+
+struct hfi_h264_entropy_control {
+	u32 entropy_mode;
+	u32 cabac_model;
+};
+
+struct hfi_frame_rate {
+	u32 buffer_type;
+	u32 frame_rate;
+};
+
+#define HFI_INTRA_REFRESH_NONE				(HFI_COMMON_BASE + 0x1)
+#define HFI_INTRA_REFRESH_CYCLIC			(HFI_COMMON_BASE + 0x2)
+#define HFI_INTRA_REFRESH_ADAPTIVE			(HFI_COMMON_BASE + 0x3)
+#define HFI_INTRA_REFRESH_CYCLIC_ADAPTIVE	(HFI_COMMON_BASE + 0x4)
+#define HFI_INTRA_REFRESH_RANDOM			(HFI_COMMON_BASE + 0x5)
+
+struct hfi_intra_refresh {
+	u32 mode;
+	u32 air_mbs;
+	u32 air_ref;
+	u32 cir_mbs;
+};
+
+struct hfi_3x_intra_refresh {
+	u32 mode;
+	u32 mbs;
+};
+
+struct hfi_idr_period {
+	u32 idr_period;
+};
+
+struct hfi_operations_type {
+	u32 rotation;
+	u32 flip;
+};
+
+struct hfi_max_num_b_frames {
+	u32 max_num_b_frames;
+};
+
+struct hfi_vc1e_perf_cfg_type {
+	u32 search_range_x_subsampled[3];
+	u32 search_range_y_subsampled[3];
+};
+
+struct hfi_conceal_color {
+	u32 conceal_color;
+};
+
+struct hfi_intra_period {
+	u32 pframes;
+	u32 bframes;
+};
+
+struct hfi_mpeg4_header_extension {
+	u32 header_extension;
+};
+
+struct hfi_mpeg4_time_resolution {
+	u32 time_increment_resolution;
+};
+
+struct hfi_multi_stream {
+	u32 buffer_type;
+	u32 enable;
+	u32 width;
+	u32 height;
+};
+
+struct hfi_3x_multi_stream {
+	u32 buffer_type;
+	u32 enable;
+};
+
+struct hfi_multi_view_format {
+	u32 views;
+	u32 rg_view_order[1];
+};
+
+#define HFI_MULTI_SLICE_OFF				(HFI_COMMON_BASE + 0x1)
+#define HFI_MULTI_SLICE_BY_MB_COUNT		(HFI_COMMON_BASE + 0x2)
+#define HFI_MULTI_SLICE_BY_BYTE_COUNT	(HFI_COMMON_BASE + 0x3)
+#define HFI_MULTI_SLICE_GOB				(HFI_COMMON_BASE + 0x4)
+
+struct hfi_multi_slice_control {
+	u32 multi_slice;
+	u32 slice_size;
+};
+
+#define HFI_NAL_FORMAT_STARTCODES			0x00000001
+#define HFI_NAL_FORMAT_ONE_NAL_PER_BUFFER	0x00000002
+#define HFI_NAL_FORMAT_ONE_BYTE_LENGTH		0x00000004
+#define HFI_NAL_FORMAT_TWO_BYTE_LENGTH		0x00000008
+#define HFI_NAL_FORMAT_FOUR_BYTE_LENGTH		0x00000010
+
+struct hfi_nal_stream_format_supported {
+	u32 nal_stream_format_supported;
+};
+
+struct hfi_nal_stream_format_select {
+	u32 nal_stream_format_select;
+};
+#define HFI_PICTURE_TYPE_I					0x01
+#define HFI_PICTURE_TYPE_P					0x02
+#define HFI_PICTURE_TYPE_B					0x04
+#define HFI_PICTURE_TYPE_IDR					0x08
+#define HFI_PICTURE_TYPE_CRA					0x10
+
+struct hfi_profile_level {
+	u32 profile;
+	u32 level;
+};
+
+struct hfi_profile_level_supported {
+	u32 profile_count;
+	struct hfi_profile_level rg_profile_level[1];
+};
+
+struct hfi_quality_vs_speed {
+	u32 quality_vs_speed;
+};
+
+struct hfi_quantization {
+	u32 qp_i;
+	u32 qp_p;
+	u32 qp_b;
+	u32 layer_id;
+};
+
+struct hfi_initial_quantization {
+	u32 qp_i;
+	u32 qp_p;
+	u32 qp_b;
+	u32 init_qp_enable;
+};
+
+struct hfi_quantization_range {
+	u32 min_qp;
+	u32 max_qp;
+	u32 layer_id;
+};
+
+#define HFI_LTR_MODE_DISABLE	0x0
+#define HFI_LTR_MODE_MANUAL		0x1
+#define HFI_LTR_MODE_PERIODIC	0x2
+
+struct hfi_ltr_mode {
+	u32 ltr_mode;
+	u32 ltr_count;
+	u32 trust_mode;
+};
+
+struct hfi_ltr_use {
+	u32 ref_ltr;
+	u32 use_constrnt;
+	u32 frames;
+};
+
+struct hfi_ltr_mark {
+	u32 mark_frame;
+};
+
+struct hfi_frame_size {
+	u32 buffer_type;
+	u32 width;
+	u32 height;
+};
+
+struct hfi_video_signal_metadata {
+	u32 enable;
+	u32 video_format;
+	u32 video_full_range;
+	u32 color_description;
+	u32 color_primaries;
+	u32 transfer_characteristics;
+	u32 matrix_coeffs;
+};
+
+struct hfi_h264_vui_timing_info {
+	u32 enable;
+	u32 fixed_frame_rate;
+	u32 time_scale;
+};
+
+struct hfi_bit_depth {
+	u32 buffer_type;
+	u32 bit_depth;
+};
+
+struct hfi_picture_type {
+	u32 is_sync_frame;
+	u32 picture_type;
+};
+
+/* Base Offset for UBWC color formats  */
+#define HFI_COLOR_FORMAT_UBWC_BASE        (0x8000)
+/* Base Offset for 10-bit color formats */
+#define HFI_COLOR_FORMAT_10_BIT_BASE      (0x4000)
+
+#define HFI_COLOR_FORMAT_MONOCHROME			(HFI_COMMON_BASE + 0x1)
+#define HFI_COLOR_FORMAT_NV12				(HFI_COMMON_BASE + 0x2)
+#define HFI_COLOR_FORMAT_NV21				(HFI_COMMON_BASE + 0x3)
+#define HFI_COLOR_FORMAT_NV12_4x4TILE		(HFI_COMMON_BASE + 0x4)
+#define HFI_COLOR_FORMAT_NV21_4x4TILE		(HFI_COMMON_BASE + 0x5)
+#define HFI_COLOR_FORMAT_YUYV				(HFI_COMMON_BASE + 0x6)
+#define HFI_COLOR_FORMAT_YVYU				(HFI_COMMON_BASE + 0x7)
+#define HFI_COLOR_FORMAT_UYVY				(HFI_COMMON_BASE + 0x8)
+#define HFI_COLOR_FORMAT_VYUY				(HFI_COMMON_BASE + 0x9)
+#define HFI_COLOR_FORMAT_RGB565				(HFI_COMMON_BASE + 0xA)
+#define HFI_COLOR_FORMAT_BGR565				(HFI_COMMON_BASE + 0xB)
+#define HFI_COLOR_FORMAT_RGB888				(HFI_COMMON_BASE + 0xC)
+#define HFI_COLOR_FORMAT_BGR888				(HFI_COMMON_BASE + 0xD)
+#define HFI_COLOR_FORMAT_YUV444				(HFI_COMMON_BASE + 0xE)
+#define HFI_COLOR_FORMAT_RGBA8888			(HFI_COMMON_BASE + 0x10)
+
+#define HFI_COLOR_FORMAT_YUV420_TP10					\
+		(HFI_COLOR_FORMAT_10_BIT_BASE + HFI_COLOR_FORMAT_NV12)
+
+#define HFI_COLOR_FORMAT_NV12_UBWC					\
+		(HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_NV12)
+
+#define HFI_COLOR_FORMAT_YUV420_TP10_UBWC				\
+		(HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_YUV420_TP10)
+
+#define  HFI_COLOR_FORMAT_RGBA8888_UBWC					\
+		(HFI_COLOR_FORMAT_UBWC_BASE + HFI_COLOR_FORMAT_RGBA8888)
+
+#define HFI_MAX_MATRIX_COEFFS 9
+#define HFI_MAX_BIAS_COEFFS 3
+#define HFI_MAX_LIMIT_COEFFS 6
+
+#define HFI_STATISTICS_MODE_DEFAULT 0x10
+#define HFI_STATISTICS_MODE_1 0x11
+#define HFI_STATISTICS_MODE_2 0x12
+#define HFI_STATISTICS_MODE_3 0x13
+
+struct hfi_uncompressed_format_select {
+	u32 buffer_type;
+	u32 format;
+};
+
+struct hfi_uncompressed_format_supported {
+	u32 buffer_type;
+	u32 format_entries;
+	u32 rg_format_info[1];
+};
+
+struct hfi_uncompressed_plane_actual {
+	u32 actual_stride;
+	u32 actual_plane_buffer_height;
+};
+
+struct hfi_uncompressed_plane_actual_info {
+	u32 buffer_type;
+	u32 num_planes;
+	struct hfi_uncompressed_plane_actual rg_plane_format[1];
+};
+
+struct hfi_uncompressed_plane_constraints {
+	u32 stride_multiples;
+	u32 max_stride;
+	u32 min_plane_buffer_height_multiple;
+	u32 buffer_alignment;
+};
+
+struct hfi_uncompressed_plane_info {
+	u32 format;
+	u32 num_planes;
+	struct hfi_uncompressed_plane_constraints rg_plane_format[1];
+};
+
+struct hfi_codec_supported {
+	u32 decoder_codec_supported;
+	u32 encoder_codec_supported;
+};
+
+struct hfi_properties_supported {
+	u32 num_properties;
+	u32 rg_properties[1];
+};
+
+struct hfi_max_sessions_supported {
+	u32 max_sessions;
+};
+
+struct hfi_vpe_color_space_conversion {
+	u32 csc_matrix[HFI_MAX_MATRIX_COEFFS];
+	u32 csc_bias[HFI_MAX_BIAS_COEFFS];
+	u32 csc_limit[HFI_MAX_LIMIT_COEFFS];
+};
+
+struct hfi_scs_threshold {
+	u32 threshold_value;
+};
+
+#define HFI_ROTATE_NONE					(HFI_COMMON_BASE + 0x1)
+#define HFI_ROTATE_90					(HFI_COMMON_BASE + 0x2)
+#define HFI_ROTATE_180					(HFI_COMMON_BASE + 0x3)
+#define HFI_ROTATE_270					(HFI_COMMON_BASE + 0x4)
+
+#define HFI_FLIP_NONE					(HFI_COMMON_BASE + 0x1)
+#define HFI_FLIP_HORIZONTAL				(HFI_COMMON_BASE + 0x2)
+#define HFI_FLIP_VERTICAL				(HFI_COMMON_BASE + 0x3)
+
+struct hfi_operations {
+	u32 rotate;
+	u32 flip;
+};
+
+#define HFI_RESOURCE_OCMEM 0x00000001
+
+struct hfi_resource_ocmem {
+	u32 size;
+	u32 mem;
+};
+
+struct hfi_resource_ocmem_requirement {
+	u32 session_domain;
+	u32 width;
+	u32 height;
+	u32 size;
+};
+
+struct hfi_resource_ocmem_requirement_info {
+	u32 num_entries;
+	struct hfi_resource_ocmem_requirement rg_requirements[1];
+};
+
+struct hfi_property_sys_image_version_info_type {
+	u32 string_size;
+	u8  str_image_version[1];
+};
+
+struct hfi_venc_config_advanced {
+	u8 pipe2d;
+	u8 hw_mode;
+	u8 low_delay_enforce;
+	u8 worker_vppsg_delay;
+	u32 close_gop;
+	u32 h264_constrain_intra_pred;
+	u32 h264_transform_8x8_flag;
+	u32 mpeg4_qpel_enable;
+	u32 multi_refp_en;
+	u32 qmatrix_en;
+	u8 vpp_info_packet_mode;
+	u8 ref_tile_mode;
+	u8 bitstream_flush_mode;
+	u32 vppsg_vspap_fb_sync_delay;
+	u32 rc_initial_delay;
+	u32 peak_bitrate_constraint;
+	u32 ds_display_frame_width;
+	u32 ds_display_frame_height;
+	u32 perf_tune_param_ptr;
+	u32 input_x_offset;
+	u32 input_y_offset;
+	u32 input_roi_width;
+	u32 input_roi_height;
+	u32 vsp_fifo_dma_sel;
+	u32 h264_num_ref_frames;
+};
+
+struct hfi_vbv_hrd_bufsize {
+	u32 buffer_size;
+};
+
+struct hfi_codec_mask_supported {
+	u32 codecs;
+	u32 video_domains;
+};
+
+struct hfi_seq_header_info {
+	u32 max_hader_len;
+};
+
+struct hfi_aspect_ratio {
+	u32 aspect_width;
+	u32 aspect_height;
+};
+
+#define HFI_IFRAME_SIZE_DEFAULT			(HFI_COMMON_BASE + 0x1)
+#define HFI_IFRAME_SIZE_MEDIUM			(HFI_COMMON_BASE + 0x2)
+#define HFI_IFRAME_SIZE_HIGH			(HFI_COMMON_BASE + 0x3)
+#define HFI_IFRAME_SIZE_UNLIMITED		(HFI_COMMON_BASE + 0x4)
+struct hfi_iframe_size {
+	u32 type;
+};
+
+#define HFI_MVC_BUFFER_LAYOUT_TOP_BOTTOM  (0)
+#define HFI_MVC_BUFFER_LAYOUT_SIDEBYSIDE  (1)
+#define HFI_MVC_BUFFER_LAYOUT_SEQ         (2)
+struct hfi_mvc_buffer_layout_descp_type {
+	u32    layout_type;
+	u32    bright_view_first;
+	u32    ngap;
+};
+
+
+#define HFI_CMD_SYS_COMMON_START			\
+(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET + HFI_CMD_START_OFFSET \
+	+ 0x0000)
+#define HFI_CMD_SYS_INIT		(HFI_CMD_SYS_COMMON_START + 0x001)
+#define HFI_CMD_SYS_PC_PREP		(HFI_CMD_SYS_COMMON_START + 0x002)
+#define HFI_CMD_SYS_SET_RESOURCE	(HFI_CMD_SYS_COMMON_START + 0x003)
+#define HFI_CMD_SYS_RELEASE_RESOURCE (HFI_CMD_SYS_COMMON_START + 0x004)
+#define HFI_CMD_SYS_SET_PROPERTY	(HFI_CMD_SYS_COMMON_START + 0x005)
+#define HFI_CMD_SYS_GET_PROPERTY	(HFI_CMD_SYS_COMMON_START + 0x006)
+#define HFI_CMD_SYS_SESSION_INIT	(HFI_CMD_SYS_COMMON_START + 0x007)
+#define HFI_CMD_SYS_SESSION_END		(HFI_CMD_SYS_COMMON_START + 0x008)
+#define HFI_CMD_SYS_SET_BUFFERS		(HFI_CMD_SYS_COMMON_START + 0x009)
+#define HFI_CMD_SYS_TEST_START		(HFI_CMD_SYS_COMMON_START + 0x100)
+
+#define HFI_CMD_SESSION_COMMON_START		\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET +	\
+	HFI_CMD_START_OFFSET + 0x1000)
+#define HFI_CMD_SESSION_SET_PROPERTY		\
+	(HFI_CMD_SESSION_COMMON_START + 0x001)
+#define HFI_CMD_SESSION_SET_BUFFERS			\
+	(HFI_CMD_SESSION_COMMON_START + 0x002)
+#define HFI_CMD_SESSION_GET_SEQUENCE_HEADER	\
+	(HFI_CMD_SESSION_COMMON_START + 0x003)
+
+#define HFI_MSG_SYS_COMMON_START			\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET +	\
+	HFI_MSG_START_OFFSET + 0x0000)
+#define HFI_MSG_SYS_INIT_DONE			(HFI_MSG_SYS_COMMON_START + 0x1)
+#define HFI_MSG_SYS_PC_PREP_DONE		(HFI_MSG_SYS_COMMON_START + 0x2)
+#define HFI_MSG_SYS_RELEASE_RESOURCE	(HFI_MSG_SYS_COMMON_START + 0x3)
+#define HFI_MSG_SYS_DEBUG			(HFI_MSG_SYS_COMMON_START + 0x4)
+#define HFI_MSG_SYS_SESSION_INIT_DONE	(HFI_MSG_SYS_COMMON_START + 0x6)
+#define HFI_MSG_SYS_SESSION_END_DONE	(HFI_MSG_SYS_COMMON_START + 0x7)
+#define HFI_MSG_SYS_IDLE		(HFI_MSG_SYS_COMMON_START + 0x8)
+#define HFI_MSG_SYS_COV                 (HFI_MSG_SYS_COMMON_START + 0x9)
+#define HFI_MSG_SYS_PROPERTY_INFO	(HFI_MSG_SYS_COMMON_START + 0xA)
+#define HFI_MSG_SESSION_SYNC_DONE      (HFI_MSG_SESSION_OX_START + 0xD)
+
+#define HFI_MSG_SESSION_COMMON_START		\
+	(HFI_DOMAIN_BASE_COMMON + HFI_ARCH_COMMON_OFFSET +	\
+	HFI_MSG_START_OFFSET + 0x1000)
+#define HFI_MSG_EVENT_NOTIFY	(HFI_MSG_SESSION_COMMON_START + 0x1)
+#define HFI_MSG_SESSION_GET_SEQUENCE_HEADER_DONE	\
+	(HFI_MSG_SESSION_COMMON_START + 0x2)
+
+#define HFI_CMD_SYS_TEST_SSR	(HFI_CMD_SYS_TEST_START + 0x1)
+#define HFI_TEST_SSR_SW_ERR_FATAL	0x1
+#define HFI_TEST_SSR_SW_DIV_BY_ZERO	0x2
+#define HFI_TEST_SSR_HW_WDOG_IRQ	0x3
+
+struct vidc_hal_cmd_pkt_hdr {
+	u32 size;
+	u32 packet_type;
+};
+
+struct vidc_hal_msg_pkt_hdr {
+	u32 size;
+	u32 packet;
+};
+
+struct vidc_hal_session_cmd_pkt {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_sys_init_packet {
+	u32 size;
+	u32 packet_type;
+	u32 arch_type;
+};
+
+struct hfi_cmd_sys_pc_prep_packet {
+	u32 size;
+	u32 packet_type;
+};
+
+struct hfi_cmd_sys_set_resource_packet {
+	u32 size;
+	u32 packet_type;
+	u32 resource_handle;
+	u32 resource_type;
+	u32 rg_resource_data[1];
+};
+
+struct hfi_cmd_sys_release_resource_packet {
+	u32 size;
+	u32 packet_type;
+	u32 resource_type;
+	u32 resource_handle;
+};
+
+struct hfi_cmd_sys_set_property_packet {
+	u32 size;
+	u32 packet_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_cmd_sys_get_property_packet {
+	u32 size;
+	u32 packet_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_cmd_sys_session_init_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 session_domain;
+	u32 session_codec;
+};
+
+struct hfi_cmd_sys_session_end_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+};
+
+struct hfi_cmd_sys_set_buffers_packet {
+	u32 size;
+	u32 packet_type;
+	u32 buffer_type;
+	u32 buffer_size;
+	u32 num_buffers;
+	u32 rg_buffer_addr[1];
+};
+
+struct hfi_cmd_session_set_property_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 num_properties;
+	u32 rg_property_data[0];
+};
+
+struct hfi_cmd_session_set_buffers_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 buffer_type;
+	u32 buffer_size;
+	u32 extra_data_size;
+	u32 min_buffer_size;
+	u32 num_buffers;
+	u32 rg_buffer_info[1];
+};
+
+struct hfi_cmd_session_get_sequence_header_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 buffer_len;
+	u32 packet_buffer;
+};
+
+struct hfi_cmd_session_sync_process_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 sync_id;
+	u32 rg_data[1];
+};
+
+struct hfi_msg_event_notify_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 event_id;
+	u32 event_data1;
+	u32 event_data2;
+	u32 rg_ext_event_data[1];
+};
+
+struct hfi_msg_release_buffer_ref_event_packet {
+	u32 packet_buffer;
+	u32 extra_data_buffer;
+	u32 output_tag;
+};
+
+struct hfi_msg_sys_init_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 error_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_sys_pc_prep_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 error_type;
+};
+
+struct hfi_msg_sys_release_resource_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 resource_handle;
+	u32 error_type;
+};
+
+struct hfi_msg_sys_session_init_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 num_properties;
+	u32 rg_property_data[1];
+};
+
+struct hfi_msg_sys_session_end_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+};
+
+struct hfi_msg_session_get_sequence_header_done_packet {
+	u32 size;
+	u32 packet_type;
+	u32 session_id;
+	u32 error_type;
+	u32 header_len;
+	u32 sequence_header;
+};
+
+struct hfi_msg_sys_debug_packet {
+	u32 size;
+	u32 packet_type;
+	u32 msg_type;
+	u32 msg_size;
+	u32 time_stamp_hi;
+	u32 time_stamp_lo;
+	u8 rg_msg_data[1];
+};
+
+struct hfi_msg_sys_coverage_packet {
+	u32 size;
+	u32 packet_type;
+	u32 msg_size;
+	u32 time_stamp_hi;
+	u32 time_stamp_lo;
+	u8 rg_msg_data[1];
+};
+
+enum HFI_VENUS_QTBL_STATUS {
+	HFI_VENUS_QTBL_DISABLED = 0x00,
+	HFI_VENUS_QTBL_ENABLED = 0x01,
+	HFI_VENUS_QTBL_INITIALIZING = 0x02,
+	HFI_VENUS_QTBL_DEINITIALIZING = 0x03
+};
+
+enum HFI_VENUS_CTRL_INIT_STATUS {
+	HFI_VENUS_CTRL_NOT_INIT = 0x0,
+	HFI_VENUS_CTRL_READY = 0x1,
+	HFI_VENUS_CTRL_ERROR_FATAL = 0x2
+};
+
+struct hfi_sfr_struct {
+	u32 bufSize;
+	u8 rg_data[1];
+};
+
+struct hfi_cmd_sys_test_ssr_packet {
+	u32 size;
+	u32 packet_type;
+	u32 trigger_type;
+};
+#endif
diff --git a/drivers/media/platform/msm/vidc/vidc_hfi_io.h b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
new file mode 100644
index 0000000..fc32d73
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vidc_hfi_io.h
@@ -0,0 +1,193 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __VIDC_HFI_IO_H__
+#define __VIDC_HFI_IO_H__
+
+#include <linux/io.h>
+
+#define VENUS_VCODEC_SS_CLOCK_HALT     0x0000000C
+#define VENUS_VPP_CORE_SW_RESET        0x00042004
+#define VENUS_VPP_CTRL_CTRL_RESET      0x00041008
+
+#define VIDC_VBIF_BASE_OFFS			0x00080000
+#define VIDC_VBIF_VERSION			(VIDC_VBIF_BASE_OFFS + 0x00)
+#define VIDC_VENUS_VBIF_DDR_OUT_MAX_BURST		\
+			(VIDC_VBIF_BASE_OFFS + 0xD8)
+#define VIDC_VENUS_VBIF_OCMEM_OUT_MAX_BURST		\
+			(VIDC_VBIF_BASE_OFFS + 0xDC)
+#define VIDC_VENUS_VBIF_ROUND_ROBIN_QOS_ARB		\
+			(VIDC_VBIF_BASE_OFFS + 0x124)
+
+#define VIDC_CPU_BASE_OFFS			0x000C0000
+#define VIDC_CPU_CS_BASE_OFFS		(VIDC_CPU_BASE_OFFS + 0x00012000)
+#define VIDC_CPU_IC_BASE_OFFS		(VIDC_CPU_BASE_OFFS + 0x0001F000)
+
+#define VIDC_CPU_CS_REMAP_OFFS		(VIDC_CPU_CS_BASE_OFFS + 0x00)
+#define VIDC_CPU_CS_TIMER_CONTROL	(VIDC_CPU_CS_BASE_OFFS + 0x04)
+#define VIDC_CPU_CS_A2HSOFTINTEN	(VIDC_CPU_CS_BASE_OFFS + 0x10)
+#define VIDC_CPU_CS_A2HSOFTINTENCLR	(VIDC_CPU_CS_BASE_OFFS + 0x14)
+#define VIDC_CPU_CS_A2HSOFTINT		(VIDC_CPU_CS_BASE_OFFS + 0x18)
+#define VIDC_CPU_CS_A2HSOFTINTCLR	(VIDC_CPU_CS_BASE_OFFS + 0x1C)
+#define VIDC_CPU_CS_SCIACMD			(VIDC_CPU_CS_BASE_OFFS + 0x48)
+
+/* HFI_CTRL_STATUS */
+#define VIDC_CPU_CS_SCIACMDARG0		(VIDC_CPU_CS_BASE_OFFS + 0x4C)
+#define VIDC_CPU_CS_SCIACMDARG0_BMSK	0xff
+#define VIDC_CPU_CS_SCIACMDARG0_SHFT	0x0
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_BMSK	0xfe
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_ERROR_STATUS_SHFT	0x1
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_BMSK	0x1
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_STATUS_SHFT	0x0
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_PC_READY           0x100
+#define VIDC_CPU_CS_SCIACMDARG0_HFI_CTRL_INIT_IDLE_MSG_BMSK     0x40000000
+
+/* HFI_QTBL_INFO */
+#define VIDC_CPU_CS_SCIACMDARG1		(VIDC_CPU_CS_BASE_OFFS + 0x50)
+
+/* HFI_QTBL_ADDR */
+#define VIDC_CPU_CS_SCIACMDARG2		(VIDC_CPU_CS_BASE_OFFS + 0x54)
+
+/* HFI_VERSION_INFO */
+#define VIDC_CPU_CS_SCIACMDARG3		(VIDC_CPU_CS_BASE_OFFS + 0x58)
+#define VIDC_CPU_IC_IRQSTATUS		(VIDC_CPU_IC_BASE_OFFS + 0x00)
+#define VIDC_CPU_IC_FIQSTATUS		(VIDC_CPU_IC_BASE_OFFS + 0x04)
+#define VIDC_CPU_IC_RAWINTR			(VIDC_CPU_IC_BASE_OFFS + 0x08)
+#define VIDC_CPU_IC_INTSELECT		(VIDC_CPU_IC_BASE_OFFS + 0x0C)
+#define VIDC_CPU_IC_INTENABLE		(VIDC_CPU_IC_BASE_OFFS + 0x10)
+#define VIDC_CPU_IC_INTENACLEAR		(VIDC_CPU_IC_BASE_OFFS + 0x14)
+#define VIDC_CPU_IC_SOFTINT			(VIDC_CPU_IC_BASE_OFFS + 0x18)
+#define VIDC_CPU_IC_SOFTINT_H2A_BMSK	0x8000
+#define VIDC_CPU_IC_SOFTINT_H2A_SHFT	0xF
+#define VIDC_CPU_IC_SOFTINTCLEAR	(VIDC_CPU_IC_BASE_OFFS + 0x1C)
+
+/*
+ * --------------------------------------------------------------------------
+ * MODULE: vidc_wrapper
+ * --------------------------------------------------------------------------
+ */
+#define VIDC_WRAPPER_BASE_OFFS		0x000E0000
+
+#define VIDC_WRAPPER_HW_VERSION		(VIDC_WRAPPER_BASE_OFFS + 0x00)
+#define VIDC_WRAPPER_HW_VERSION_MAJOR_VERSION_MASK  0x78000000
+#define VIDC_WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT 28
+#define VIDC_WRAPPER_HW_VERSION_MINOR_VERSION_MASK  0xFFF0000
+#define VIDC_WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT 16
+#define VIDC_WRAPPER_HW_VERSION_STEP_VERSION_MASK   0xFFFF
+#define VIDC_WRAPPER_CLOCK_CONFIG	(VIDC_WRAPPER_BASE_OFFS + 0x04)
+
+#define VIDC_WRAPPER_INTR_STATUS	(VIDC_WRAPPER_BASE_OFFS + 0x0C)
+#define VIDC_WRAPPER_INTR_STATUS_A2HWD_BMSK	0x10
+#define VIDC_WRAPPER_INTR_STATUS_A2HWD_SHFT	0x4
+#define VIDC_WRAPPER_INTR_STATUS_A2H_BMSK	0x4
+#define VIDC_WRAPPER_INTR_STATUS_A2H_SHFT	0x2
+
+#define VIDC_WRAPPER_INTR_MASK		(VIDC_WRAPPER_BASE_OFFS + 0x10)
+#define VIDC_WRAPPER_INTR_MASK_A2HWD_BMSK	0x10
+#define VIDC_WRAPPER_INTR_MASK_A2HWD_SHFT	0x4
+#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_BMSK	0x8
+#define VIDC_WRAPPER_INTR_MASK_A2HVCODEC_SHFT	0x3
+#define VIDC_WRAPPER_INTR_MASK_A2HCPU_BMSK	0x4
+#define VIDC_WRAPPER_INTR_MASK_A2HCPU_SHFT	0x2
+
+#define VIDC_WRAPPER_INTR_CLEAR		(VIDC_WRAPPER_BASE_OFFS + 0x14)
+#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_BMSK	0x10
+#define VIDC_WRAPPER_INTR_CLEAR_A2HWD_SHFT	0x4
+#define VIDC_WRAPPER_INTR_CLEAR_A2H_BMSK	0x4
+#define VIDC_WRAPPER_INTR_CLEAR_A2H_SHFT	0x2
+
+#define VIDC_WRAPPER_VBIF_XIN_SW_RESET	(VIDC_WRAPPER_BASE_OFFS + 0x18)
+#define VIDC_WRAPPER_VBIF_XIN_STATUS	(VIDC_WRAPPER_BASE_OFFS + 0x1C)
+#define VIDC_WRAPPER_CPU_CLOCK_CONFIG	(VIDC_WRAPPER_BASE_OFFS + 0x2000)
+#define VIDC_WRAPPER_VBIF_XIN_CPU_SW_RESET	\
+				(VIDC_WRAPPER_BASE_OFFS + 0x2004)
+#define VIDC_WRAPPER_AXI_HALT		(VIDC_WRAPPER_BASE_OFFS + 0x2008)
+#define VIDC_WRAPPER_AXI_HALT_STATUS	(VIDC_WRAPPER_BASE_OFFS + 0x200C)
+#define VIDC_WRAPPER_CPU_CGC_DIS	(VIDC_WRAPPER_BASE_OFFS + 0x2010)
+#define VIDC_WRAPPER_CPU_STATUS (VIDC_WRAPPER_BASE_OFFS + 0x2014)
+#define VIDC_VENUS_VBIF_CLK_ON		(VIDC_VBIF_BASE_OFFS + 0x4)
+#define VIDC_VBIF_IN_RD_LIM_CONF0       (VIDC_VBIF_BASE_OFFS + 0xB0)
+#define VIDC_VBIF_IN_RD_LIM_CONF1       (VIDC_VBIF_BASE_OFFS + 0xB4)
+#define VIDC_VBIF_IN_RD_LIM_CONF2       (VIDC_VBIF_BASE_OFFS + 0xB8)
+#define VIDC_VBIF_IN_RD_LIM_CONF3       (VIDC_VBIF_BASE_OFFS + 0xBC)
+#define VIDC_VBIF_IN_WR_LIM_CONF0       (VIDC_VBIF_BASE_OFFS + 0xC0)
+#define VIDC_VBIF_IN_WR_LIM_CONF1       (VIDC_VBIF_BASE_OFFS + 0xC4)
+#define VIDC_VBIF_IN_WR_LIM_CONF2       (VIDC_VBIF_BASE_OFFS + 0xC8)
+#define VIDC_VBIF_IN_WR_LIM_CONF3       (VIDC_VBIF_BASE_OFFS + 0xCC)
+#define VIDC_VBIF_OUT_RD_LIM_CONF0      (VIDC_VBIF_BASE_OFFS + 0xD0)
+#define VIDC_VBIF_OUT_WR_LIM_CONF0      (VIDC_VBIF_BASE_OFFS + 0xD4)
+#define VIDC_VBIF_DDR_OUT_MAX_BURST     (VIDC_VBIF_BASE_OFFS + 0xD8)
+#define VIDC_VBIF_OCMEM_OUT_MAX_BURST   (VIDC_VBIF_BASE_OFFS + 0xDC)
+#define VIDC_VBIF_DDR_ARB_CONF0         (VIDC_VBIF_BASE_OFFS + 0xF4)
+#define VIDC_VBIF_DDR_ARB_CONF1         (VIDC_VBIF_BASE_OFFS + 0xF8)
+#define VIDC_VBIF_ROUND_ROBIN_QOS_ARB   (VIDC_VBIF_BASE_OFFS + 0x124)
+#define VIDC_VBIF_OUT_AXI_AOOO_EN       (VIDC_VBIF_BASE_OFFS + 0x178)
+#define VIDC_VBIF_OUT_AXI_AOOO          (VIDC_VBIF_BASE_OFFS + 0x17C)
+#define VIDC_VBIF_ARB_CTL               (VIDC_VBIF_BASE_OFFS + 0xF0)
+#define VIDC_VBIF_OUT_AXI_AMEMTYPE_CONF0 (VIDC_VBIF_BASE_OFFS + 0x160)
+#define VIDC_VBIF_OUT_AXI_AMEMTYPE_CONF1 (VIDC_VBIF_BASE_OFFS + 0x164)
+#define VIDC_VBIF_ADDR_TRANS_EN         (VIDC_VBIF_BASE_OFFS + 0xC00)
+#define VIDC_VBIF_AT_OLD_BASE           (VIDC_VBIF_BASE_OFFS + 0xC04)
+#define VIDC_VBIF_AT_OLD_HIGH           (VIDC_VBIF_BASE_OFFS + 0xC08)
+#define VIDC_VBIF_AT_NEW_BASE           (VIDC_VBIF_BASE_OFFS + 0xC10)
+#define VIDC_VBIF_AT_NEW_HIGH           (VIDC_VBIF_BASE_OFFS + 0xC18)
+#define VENUS_VBIF_AXI_HALT_CTRL0   (VIDC_VBIF_BASE_OFFS + 0x208)
+#define VENUS_VBIF_AXI_HALT_CTRL1   (VIDC_VBIF_BASE_OFFS + 0x20C)
+
+#define VENUS_VBIF_AXI_HALT_CTRL0_HALT_REQ		BIT(0)
+#define VENUS_VBIF_AXI_HALT_CTRL1_HALT_ACK		BIT(0)
+#define VENUS_VBIF_AXI_HALT_ACK_TIMEOUT_US		500000
+
+#define VIDC_VENUS0_WRAPPER_VBIF_REQ_PRIORITY \
+	(VIDC_WRAPPER_BASE_OFFS + 0x20)
+#define VIDC_VENUS0_WRAPPER_VBIF_PRIORITY_LEVEL \
+	(VIDC_WRAPPER_BASE_OFFS + 0x24)
+
+#define VIDC_CTRL_INIT 0x000D2048
+#define VIDC_CTRL_INIT_RESERVED_BITS31_1__M 0xFFFFFFFE
+#define VIDC_CTRL_INIT_RESERVED_BITS31_1__S 1
+#define VIDC_CTRL_INIT_CTRL__M 0x00000001
+#define VIDC_CTRL_INIT_CTRL__S 0
+
+#define VIDC_CTRL_STATUS 0x000D204C
+#define VIDC_CTRL_STATUS_RESERVED_BITS31_8__M 0xFFFFFF00
+#define VIDC_CTRL_STATUS_RESERVED_BITS31_8__S 8
+#define VIDC_CTRL_ERROR_STATUS__M             0x000000FE
+#define VIDC_CTRL_ERROR_STATUS__S             1
+#define VIDC_CTRL_INIT_STATUS__M              0x00000001
+#define VIDC_CTRL_INIT_STATUS__S              0
+
+#define VIDC_QTBL_INFO 0x000D2050
+#define VIDC_QTBL_HOSTID__M 0xFF000000
+#define VIDC_QTBL_HOSTID__S 24
+#define VIDC_QTBL_INFO_RESERVED_BITS23_8__M 0x00FFFF00
+#define VIDC_QTBL_INFO_RESERVED_BITS23_8__S 8
+#define VIDC_QTBL_STATUS__M 0x000000FF
+#define VIDC_QTBL_STATUS__S 0
+
+#define VIDC_QTBL_ADDR 0x000D2054
+
+#define VIDC_VERSION_INFO 0x000D2058
+#define VIDC_VERSION_INFO_MAJOR__M  0xF0000000
+#define VIDC_VERSION_INFO_MAJOR__S  28
+#define VIDC_VERSION_INFO_MINOR__M  0x0FFFFFE0
+#define VIDC_VERSION_INFO_MINOR__S  5
+#define VIDC_VERSION_INFO_BRANCH__M 0x0000001F
+#define VIDC_VERSION_INFO_BRANCH__S 0
+
+#define VIDC_SFR_ADDR 0x000D205C
+#define VIDC_MMAP_ADDR 0x000D2060
+#define VIDC_UC_REGION_ADDR 0x000D2064
+#define VIDC_UC_REGION_SIZE 0x000D2068
+
+#endif
diff --git a/drivers/media/platform/msm/vidc/vmem/Kconfig b/drivers/media/platform/msm/vidc/vmem/Kconfig
new file mode 100644
index 0000000..99260a9
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vmem/Kconfig
@@ -0,0 +1,3 @@
+menuconfig MSM_VIDC_VMEM
+	tristate "Qualcomm Technologies, Inc. MSM VMEM driver"
+	depends on ARCH_QCOM && MSM_VIDC_V4L2
diff --git a/drivers/media/platform/msm/vidc/vmem/Makefile b/drivers/media/platform/msm/vidc/vmem/Makefile
new file mode 100644
index 0000000..a56ad95
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vmem/Makefile
@@ -0,0 +1,7 @@
+ccflags-y += -I$(srctree)/drivers/media/platform/msm/vidc/
+ccflags-y += -I$(srctree)/drivers/media/platform/msm/vidc/vmem/
+
+msm-vidc-vmem-objs := vmem.o \
+                      vmem_debugfs.o
+
+obj-$(CONFIG_MSM_VIDC_VMEM) := msm-vidc-vmem.o
diff --git a/drivers/media/platform/msm/vidc/vmem/vmem.c b/drivers/media/platform/msm/vidc/vmem/vmem.c
new file mode 100644
index 0000000..c75f02d
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vmem/vmem.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/clk/qcom.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/msm-bus.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include "vmem.h"
+#include "vmem_debugfs.h"
+
+/* Registers */
+#define OCIMEM_BASE(v)               ((uint8_t *)(v)->reg.base)
+#define OCIMEM_HW_VERSION(v)         (OCIMEM_BASE(v) + 0x00)
+#define OCIMEM_HW_PROFILE(v)         (OCIMEM_BASE(v) + 0x04)
+#define OCIMEM_GEN_CTL(v)            (OCIMEM_BASE(v) + 0x08)
+#define OCIMEM_GEN_STAT(v)           (OCIMEM_BASE(v) + 0x0C)
+#define OCIMEM_INTC_CLR(v)           (OCIMEM_BASE(v) + 0x10)
+#define OCIMEM_INTC_MASK(v)          (OCIMEM_BASE(v) + 0x14)
+#define OCIMEM_INTC_STAT(v)          (OCIMEM_BASE(v) + 0x18)
+#define OCIMEM_OSW_STATUS(v)         (OCIMEM_BASE(v) + 0x1C)
+#define OCIMEM_PSCGC_TIMERS(v)       (OCIMEM_BASE(v) + 0x34)
+#define OCIMEM_PSCGC_STAT(v)         (OCIMEM_BASE(v) + 0x38)
+#define OCIMEM_PSCGC_M0_M7_CTL(v)    (OCIMEM_BASE(v) + 0x3C)
+#define OCIMEM_ERR_ADDRESS(v)        (OCIMEM_BASE(v) + 0x60)
+#define OCIMEM_AXI_ERR_SYNDROME(v)   (OCIMEM_BASE(v) + 0x64)
+#define OCIMEM_DEBUG_CTL(v)          (OCIMEM_BASE(v) + 0x68)
+
+/*
+ * Helper macro to help out with masks and shifts for values packed into
+ * registers.
+ */
+#define DECLARE_TYPE(__type, __end, __start)                                   \
+	static const unsigned int __type##_BITS = (__end) - (__start) + 1;     \
+	static const unsigned int __type##_SHIFT = (__start);                  \
+	static const unsigned int __type##_MASK = GENMASK((__end), (__start)); \
+	static inline unsigned int __type(uint32_t val)                        \
+	{                                                                      \
+		return (val & __type##_MASK) >> __type##_SHIFT;                \
+	}                                                                      \
+	static inline uint32_t __type##_UPDATE(unsigned int val)               \
+	{                                                                      \
+		return (val << __type##_SHIFT) & __type##_MASK;                \
+	}
+
+/* Register masks */
+/* OCIMEM_PSCGC_M0_M7_CTL */
+DECLARE_TYPE(BANK0_STATE, 3, 0);
+DECLARE_TYPE(BANK1_STATE, 7, 4);
+DECLARE_TYPE(BANK2_STATE, 11, 8);
+DECLARE_TYPE(BANK3_STATE, 15, 12);
+/* OCIMEM_PSCGC_TIMERS */
+DECLARE_TYPE(TIMERS_WAKEUP, 3, 0);
+DECLARE_TYPE(TIMERS_SLEEP, 11, 8);
+/* OCIMEM_HW_VERSION */
+DECLARE_TYPE(VERSION_STEP, 15, 0);
+DECLARE_TYPE(VERSION_MINOR, 27, 16);
+DECLARE_TYPE(VERSION_MAJOR, 31, 28);
+/* OCIMEM_HW_PROFILE */
+DECLARE_TYPE(PROFILE_BANKS, 16, 12);
+/* OCIMEM_AXI_ERR_SYNDROME */
+DECLARE_TYPE(ERR_SYN_ATID, 14, 8);
+DECLARE_TYPE(ERR_SYN_AMID, 23, 16);
+DECLARE_TYPE(ERR_SYN_APID, 28, 24);
+DECLARE_TYPE(ERR_SYN_ABID, 31, 29);
+/* OCIMEM_INTC_MASK */
+DECLARE_TYPE(AXI_ERR_INT, 0, 0);
+
+/* Internal stuff */
+#define MAX_BANKS 4
+
+enum bank_state {
+	BANK_STATE_NORM_PASSTHRU = 0x000,
+	BANK_STATE_NORM_FORCE_CORE_ON = 0x002,
+	BANK_STATE_NORM_FORCE_PERIPH_ON = 0x001,
+	BANK_STATE_NORM_FORCE_ALL_ON = 0x03,
+	BANK_STATE_SLEEP_RET = 0x6,
+	BANK_STATE_SLEEP_RET_PERIPH_ON = 0x7,
+	BANK_STATE_SLEEP_NO_RET = 0x4,
+};
+
+struct vmem {
+	int irq;
+	int num_banks;
+	int bank_size;
+	struct {
+		struct resource *resource;
+		void __iomem *base;
+	} reg, mem;
+	struct regulator *vdd;
+	struct {
+		const char *name;
+		struct clk *clk;
+		bool has_mem_retention;
+	} *clocks;
+	int num_clocks;
+	struct {
+		struct msm_bus_scale_pdata *pdata;
+		uint32_t priv;
+	} bus;
+	atomic_t alloc_count;
+	struct dentry *debugfs_root;
+};
+
+static struct vmem *vmem;
+
+static inline u32 __readl(void * __iomem addr)
+{
+	u32 value = 0;
+
+	pr_debug("read %pK ", addr);
+	value = readl_relaxed(addr);
+	pr_debug("-> %08x\n", value);
+
+	return value;
+}
+
+static inline void __writel(u32 val, void * __iomem addr)
+{
+	pr_debug("write %08x -> %pK\n", val, addr);
+	writel_relaxed(val, addr);
+	/*
+	 * Commit all writes via a mem barrier, as subsequent __readl()
+	 * will depend on the state that's set via __writel().
+	 */
+	mb();
+}
+
+static inline void __wait_timer(struct vmem *v, bool wakeup)
+{
+	uint32_t ticks = 0;
+	unsigned int (*timer)(uint32_t) = wakeup ?
+		TIMERS_WAKEUP : TIMERS_SLEEP;
+
+	ticks = timer(__readl(OCIMEM_PSCGC_TIMERS(v)));
+
+	/* Sleep for `ticks` nanoseconds as per h/w spec */
+	ndelay(ticks);
+}
+
+static inline void __wait_wakeup(struct vmem *v)
+{
+	return __wait_timer(v, true);
+}
+
+static inline void __wait_sleep(struct vmem *v)
+{
+	return __wait_timer(v, false);
+}
+
+static inline int __power_on(struct vmem *v)
+{
+	int rc = 0, c = 0;
+
+	rc = msm_bus_scale_client_update_request(v->bus.priv, 1);
+	if (rc) {
+		pr_err("Failed to vote for buses (%d)\n", rc);
+		goto exit;
+	}
+	pr_debug("Voted for buses\n");
+
+	rc = regulator_enable(v->vdd);
+	if (rc) {
+		pr_err("Failed to power on gdsc (%d)", rc);
+		goto unvote_bus;
+	}
+	pr_debug("Enabled regulator vdd\n");
+
+	for (c = 0; c < v->num_clocks; ++c) {
+		if (v->clocks[c].has_mem_retention) {
+			rc = clk_set_flags(v->clocks[c].clk,
+				       CLKFLAG_NORETAIN_PERIPH);
+			if (rc) {
+				pr_warn("Failed set flag NORETAIN_PERIPH %s\n",
+					v->clocks[c].name);
+			}
+			rc = clk_set_flags(v->clocks[c].clk,
+				       CLKFLAG_NORETAIN_MEM);
+			if (rc) {
+				pr_warn("Failed set flag NORETAIN_MEM %s\n",
+					v->clocks[c].name);
+			}
+		}
+
+		rc = clk_prepare_enable(v->clocks[c].clk);
+		if (rc) {
+			pr_err("Failed to enable %s clock (%d)\n",
+					v->clocks[c].name, rc);
+			goto disable_clocks;
+		}
+
+		pr_debug("Enabled clock %s\n", v->clocks[c].name);
+	}
+
+	return 0;
+disable_clocks:
+	for (--c; c >= 0; c--)
+		clk_disable_unprepare(v->clocks[c].clk);
+	regulator_disable(v->vdd);
+unvote_bus:
+	msm_bus_scale_client_update_request(v->bus.priv, 0);
+exit:
+	return rc;
+}
+
+static inline int __power_off(struct vmem *v)
+{
+	int c = v->num_clocks;
+
+	for (c--; c >= 0; --c) {
+		clk_disable_unprepare(v->clocks[c].clk);
+		pr_debug("Disabled clock %s\n", v->clocks[c].name);
+	}
+
+	regulator_disable(v->vdd);
+	pr_debug("Disabled regulator vdd\n");
+
+	msm_bus_scale_client_update_request(v->bus.priv, 0);
+	pr_debug("Unvoted for buses\n");
+
+	return 0;
+}
+
+static inline enum bank_state __bank_get_state(struct vmem *v,
+		unsigned int bank)
+{
+	unsigned int (*func[MAX_BANKS])(uint32_t) = {
+		BANK0_STATE, BANK1_STATE, BANK2_STATE, BANK3_STATE
+	};
+
+	VMEM_ERROR(bank >= ARRAY_SIZE(func));
+	return func[bank](__readl(OCIMEM_PSCGC_M0_M7_CTL(v)));
+}
+
+static inline void __bank_set_state(struct vmem *v, unsigned int bank,
+		enum bank_state state)
+{
+	uint32_t bank_state = 0;
+	struct {
+		uint32_t (*update)(unsigned int);
+		uint32_t mask;
+	} banks[MAX_BANKS] = {
+		{BANK0_STATE_UPDATE, BANK0_STATE_MASK},
+		{BANK1_STATE_UPDATE, BANK1_STATE_MASK},
+		{BANK2_STATE_UPDATE, BANK2_STATE_MASK},
+		{BANK3_STATE_UPDATE, BANK3_STATE_MASK},
+	};
+
+	VMEM_ERROR(bank >= ARRAY_SIZE(banks));
+
+	bank_state = __readl(OCIMEM_PSCGC_M0_M7_CTL(v));
+	bank_state &= ~banks[bank].mask;
+	bank_state |= banks[bank].update(state);
+
+	__writel(bank_state, OCIMEM_PSCGC_M0_M7_CTL(v));
+}
+
+static inline void __toggle_interrupts(struct vmem *v, bool enable)
+{
+	uint32_t ints = __readl(OCIMEM_INTC_MASK(v)),
+		mask = AXI_ERR_INT_MASK,
+		update = AXI_ERR_INT_UPDATE(!enable);
+
+	ints &= ~mask;
+	ints |= update;
+
+	__writel(ints, OCIMEM_INTC_MASK(v));
+}
+
+static void __enable_interrupts(struct vmem *v)
+{
+	pr_debug("Enabling interrupts\n");
+	enable_irq(v->irq);
+	__toggle_interrupts(v, true);
+}
+
+static void __disable_interrupts(struct vmem *v)
+{
+	pr_debug("Disabling interrupts\n");
+	__toggle_interrupts(v, false);
+	disable_irq_nosync(v->irq);
+}
+
+/**
+ * vmem_allocate: - Allocates memory from VMEM.  Allocations have a few
+ * restrictions: only allocations of the entire VMEM memory are allowed, and
+ * , as a result, only single outstanding allocations are allowed.
+ *
+ * @size: amount of bytes to allocate
+ * @addr: A pointer to phys_addr_t where the physical address of the memory
+ * allocated is stored.
+ *
+ * Return: 0 in case of successful allocation (i.e. *addr != NULL). -ENOTSUPP,
+ * if platform doesn't support VMEM. -EEXIST, if there are outstanding VMEM
+ * allocations.  -ENOMEM, if platform can't support allocation of `size` bytes.
+ * -EAGAIN, if `size` does not allocate the entire VMEM region.  -EIO in case of
+ * internal errors.
+ */
+int vmem_allocate(size_t size, phys_addr_t *addr)
+{
+	int rc = 0, c = 0;
+	resource_size_t max_size = 0;
+
+	if (!vmem) {
+		pr_err("No vmem, try rebooting your device\n");
+		rc = -ENOTSUPP;
+		goto exit;
+	}
+	if (!size) {
+		pr_err("%s Invalid size %zu\n", __func__, size);
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	max_size = resource_size(vmem->mem.resource);
+
+	if (atomic_read(&vmem->alloc_count)) {
+		pr_err("Only single allocations allowed for vmem\n");
+		rc = -EEXIST;
+		goto exit;
+	} else if (size > max_size) {
+		pr_err("Out of memory, have max %pa\n", &max_size);
+		rc = -ENOMEM;
+		goto exit;
+	} else if (size != max_size) {
+		pr_err("Only support allocations of size %pa\n", &max_size);
+		rc = -EAGAIN;
+		goto exit;
+	}
+
+	rc = __power_on(vmem);
+	if (rc) {
+		pr_err("Failed power on (%d)\n", rc);
+		goto exit;
+	}
+
+	VMEM_ERROR(vmem->num_banks != DIV_ROUND_UP(size, vmem->bank_size));
+
+	/* Turn on the necessary banks */
+	for (c = 0; c < vmem->num_banks; ++c) {
+		__bank_set_state(vmem, c, BANK_STATE_NORM_FORCE_CORE_ON);
+		__wait_wakeup(vmem);
+	}
+
+	/* Enable interrupts to detect faults */
+	__enable_interrupts(vmem);
+
+	atomic_inc(&vmem->alloc_count);
+	*addr = (phys_addr_t)vmem->mem.resource->start;
+	return 0;
+exit:
+	return rc;
+}
+EXPORT_SYMBOL(vmem_allocate);
+
+/**
+ * vmem_free: - Frees the memory allocated via vmem_allocate.  Undefined
+ * behaviour if to_free is a not a pointer returned via vmem_allocate
+ */
+void vmem_free(phys_addr_t to_free)
+{
+	int c = 0;
+
+	if (!to_free || !vmem)
+		return;
+
+	VMEM_ERROR(atomic_read(&vmem->alloc_count) == 0);
+
+	for (c = 0; c < vmem->num_banks; ++c) {
+		enum bank_state curr_state = __bank_get_state(vmem, c);
+
+		if (curr_state != BANK_STATE_NORM_FORCE_CORE_ON) {
+			pr_warn("When freeing, expected bank state to be %d, was instead %d\n",
+					BANK_STATE_NORM_FORCE_CORE_ON,
+					curr_state);
+		}
+
+		__bank_set_state(vmem, c, BANK_STATE_SLEEP_NO_RET);
+	}
+
+	__disable_interrupts(vmem);
+	__power_off(vmem);
+	atomic_dec(&vmem->alloc_count);
+}
+EXPORT_SYMBOL(vmem_free);
+
+struct vmem_interrupt_cookie {
+	struct vmem *vmem;
+	struct work_struct work;
+};
+
+static void __irq_helper(struct work_struct *work)
+{
+	struct vmem_interrupt_cookie *cookie = container_of(work,
+			struct vmem_interrupt_cookie, work);
+	struct vmem *v = cookie->vmem;
+	unsigned int stat, gen_stat, pscgc_stat, err_addr_abs,
+		err_addr_rel, err_syn;
+
+	stat = __readl(OCIMEM_INTC_STAT(v));
+	gen_stat = __readl(OCIMEM_GEN_CTL(v));
+	pscgc_stat = __readl(OCIMEM_PSCGC_STAT(v));
+
+	err_addr_abs = __readl(OCIMEM_ERR_ADDRESS(v));
+	err_addr_rel = v->mem.resource->start - err_addr_abs;
+
+	err_syn = __readl(OCIMEM_AXI_ERR_SYNDROME(v));
+
+	pr_crit("Detected a fault on VMEM:\n");
+	pr_cont("\tinterrupt status: %x\n", stat);
+	pr_cont("\tgeneral status: %x\n", gen_stat);
+	pr_cont("\tmemory status: %x\n", pscgc_stat);
+	pr_cont("\tfault address: %x (absolute), %x (relative)\n",
+			err_addr_abs, err_addr_rel);
+	pr_cont("\tfault bank: %x\n", err_addr_rel / v->bank_size);
+	pr_cont("\tfault core: %u (mid), %u (pid), %u (bid)\n",
+			ERR_SYN_AMID(err_syn), ERR_SYN_APID(err_syn),
+			ERR_SYN_ABID(err_syn));
+
+	/* Clear the interrupt */
+	__writel(0, OCIMEM_INTC_CLR(v));
+
+	__enable_interrupts(v);
+}
+
+static struct vmem_interrupt_cookie interrupt_cookie;
+
+static irqreturn_t __irq_handler(int irq, void *cookie)
+{
+	struct vmem *v = cookie;
+	irqreturn_t status = __readl(OCIMEM_INTC_STAT(vmem)) ?
+		IRQ_HANDLED : IRQ_NONE;
+
+	if (status != IRQ_NONE) {
+		/* Mask further interrupts while handling this one */
+		__disable_interrupts(v);
+
+		interrupt_cookie.vmem = v;
+		INIT_WORK(&interrupt_cookie.work, __irq_helper);
+		schedule_work(&interrupt_cookie.work);
+	}
+
+	return status;
+}
+
+static inline int __init_resources(struct vmem *v,
+		struct platform_device *pdev)
+{
+	int rc = 0, c = 0;
+	int *clock_props = NULL;
+
+	v->irq = platform_get_irq(pdev, 0);
+	if (v->irq < 0) {
+		rc = v->irq;
+		pr_err("Failed to get irq (%d)\n", rc);
+		v->irq = 0;
+		goto exit;
+	}
+
+	/* Registers and memory */
+	v->reg.resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+			"reg-base");
+	if (!v->reg.resource) {
+		pr_err("Failed to find register base\n");
+		rc = -ENOENT;
+		goto exit;
+	}
+
+	v->reg.base = devm_ioremap_resource(&pdev->dev, v->reg.resource);
+	if (IS_ERR_OR_NULL(v->reg.base)) {
+		rc = PTR_ERR(v->reg.base) ?: -EIO;
+		pr_err("Failed to map register base into kernel (%d)\n", rc);
+		v->reg.base = NULL;
+		goto exit;
+	}
+
+	pr_debug("Register range: %pa -> %pa\n", &v->reg.resource->start,
+			&v->reg.resource->end);
+
+	v->mem.resource = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+			"mem-base");
+	if (!v->mem.resource) {
+		pr_err("Failed to find memory base\n");
+		rc = -ENOENT;
+		goto exit;
+	}
+
+	v->mem.base = NULL;
+	pr_debug("Memory range: %pa -> %pa\n", &v->mem.resource->start,
+			&v->mem.resource->end);
+
+	/* Buses, Clocks & Regulators*/
+	v->num_clocks = of_property_count_strings(pdev->dev.of_node,
+			"clock-names");
+	if (v->num_clocks <= 0) {
+		pr_err("Can't find any clocks\n");
+		goto exit;
+	}
+
+	v->clocks = devm_kzalloc(&pdev->dev, sizeof(*v->clocks) * v->num_clocks,
+			GFP_KERNEL);
+	if (!v->clocks) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	clock_props = devm_kzalloc(&pdev->dev,
+					v->num_clocks * sizeof(*clock_props),
+					GFP_KERNEL);
+	if (!clock_props) {
+		pr_err("Failed to allocate clock config table\n");
+		goto exit;
+	}
+
+	rc = of_property_read_u32_array(pdev->dev.of_node, "clock-config",
+			clock_props, v->num_clocks);
+	if (rc) {
+		pr_err("Failed to read clock config\n");
+		goto exit;
+	}
+
+	for (c = 0; c < v->num_clocks; ++c) {
+		const char *name = NULL;
+		struct clk *temp = NULL;
+
+		of_property_read_string_index(pdev->dev.of_node, "clock-names",
+				c, &name);
+		temp = devm_clk_get(&pdev->dev, name);
+		if (IS_ERR_OR_NULL(temp)) {
+			rc = PTR_ERR(temp) ?: -ENOENT;
+			pr_err("Failed to find %s (%d)\n", name, rc);
+			goto exit;
+		}
+
+		v->clocks[c].clk = temp;
+		v->clocks[c].name = name;
+		v->clocks[c].has_mem_retention = clock_props[c];
+	}
+
+	v->vdd = devm_regulator_get(&pdev->dev, "vdd");
+	if (IS_ERR_OR_NULL(v->vdd)) {
+		rc = PTR_ERR(v->vdd) ?: -ENOENT;
+		pr_err("Failed to find regulator (vdd) (%d)\n", rc);
+		goto exit;
+	}
+
+	v->bus.pdata = msm_bus_cl_get_pdata(pdev);
+	if (IS_ERR_OR_NULL(v->bus.pdata)) {
+		rc = PTR_ERR(v->bus.pdata) ?: -ENOENT;
+		pr_err("Failed to find bus vectors (%d)\n", rc);
+		goto exit;
+	}
+
+	v->bus.priv = msm_bus_scale_register_client(v->bus.pdata);
+	if (!v->bus.priv) {
+		rc = -EBADHANDLE;
+		pr_err("Failed to register bus client\n");
+		goto free_pdata;
+	}
+
+	/* Misc. */
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,bank-size",
+			&v->bank_size);
+	if (rc || !v->bank_size) {
+		pr_err("Failed reading (or found invalid) qcom,bank-size in %s (%d)\n",
+				of_node_full_name(pdev->dev.of_node), rc);
+		rc = -ENOENT;
+		goto free_pdata;
+	}
+
+	v->num_banks = resource_size(v->mem.resource) / v->bank_size;
+
+	pr_debug("Found configuration with %d banks with size %d\n",
+			v->num_banks, v->bank_size);
+
+	return 0;
+free_pdata:
+	msm_bus_cl_clear_pdata(v->bus.pdata);
+exit:
+	return rc;
+}
+
+static inline void __uninit_resources(struct vmem *v,
+		struct platform_device *pdev)
+{
+	int c = 0;
+
+	msm_bus_cl_clear_pdata(v->bus.pdata);
+	v->bus.pdata = NULL;
+	v->bus.priv = 0;
+
+	for (c = 0; c < v->num_clocks; ++c) {
+		v->clocks[c].clk = NULL;
+		v->clocks[c].name = NULL;
+	}
+
+	v->vdd = NULL;
+}
+
+static int vmem_probe(struct platform_device *pdev)
+{
+	uint32_t version = 0, num_banks = 0, rc = 0;
+	struct vmem *v = NULL;
+
+	if (vmem) {
+		pr_err("Only one instance of %s allowed", pdev->name);
+		return -EEXIST;
+	}
+
+	v = devm_kzalloc(&pdev->dev, sizeof(*v), GFP_KERNEL);
+	if (!v)
+		return -ENOMEM;
+
+
+	rc = __init_resources(v, pdev);
+	if (rc) {
+		pr_err("Failed to read resources\n");
+		goto exit;
+	}
+
+	/*
+	 * For now, only support up to 4 banks. It's unrealistic that VMEM has
+	 * more banks than that (even in the future).
+	 */
+	if (v->num_banks > MAX_BANKS) {
+		pr_err("Number of banks (%d) exceeds what's supported (%d)\n",
+			v->num_banks, MAX_BANKS);
+		rc = -ENOTSUPP;
+		goto exit;
+	}
+
+	/* Cross check the platform resources with what's available on chip */
+	rc = __power_on(v);
+	if (rc) {
+		pr_err("Failed to power on (%d)\n", rc);
+		goto exit;
+	}
+
+	version = __readl(OCIMEM_HW_VERSION(v));
+	pr_debug("v%d.%d.%d\n", VERSION_MAJOR(version), VERSION_MINOR(version),
+			VERSION_STEP(version));
+
+	num_banks = PROFILE_BANKS(__readl(OCIMEM_HW_PROFILE(v)));
+	pr_debug("Found %d banks on chip\n", num_banks);
+	if (v->num_banks != num_banks) {
+		pr_err("Platform configuration of %d banks differs from what's available on chip (%d)\n",
+				v->num_banks, num_banks);
+		rc = -EINVAL;
+		goto disable_clocks;
+	}
+
+	rc = devm_request_irq(&pdev->dev, v->irq, __irq_handler,
+			IRQF_TRIGGER_HIGH, "vmem", v);
+	if (rc) {
+		pr_err("Failed to setup irq (%d)\n", rc);
+		goto disable_clocks;
+	}
+
+	__disable_interrupts(v);
+
+	/* Everything good so far, set up the global context and debug hooks */
+	pr_info("Up and running with %d banks of memory from %pR\n",
+			v->num_banks, &v->mem.resource);
+	v->debugfs_root = vmem_debugfs_init(pdev);
+	platform_set_drvdata(pdev, v);
+	vmem = v;
+
+disable_clocks:
+	__power_off(v);
+exit:
+	return rc;
+}
+
+static int vmem_remove(struct platform_device *pdev)
+{
+	struct vmem *v = platform_get_drvdata(pdev);
+
+	VMEM_ERROR(v != vmem);
+
+	__uninit_resources(v, pdev);
+	vmem_debugfs_deinit(v->debugfs_root);
+	vmem = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id vmem_of_match[] = {
+	{.compatible = "qcom,msm-vmem"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, vmem_of_match);
+
+static struct platform_driver vmem_driver = {
+	.probe = vmem_probe,
+	.remove = vmem_remove,
+	.driver = {
+		.name = "msm_vidc_vmem",
+		.owner = THIS_MODULE,
+		.of_match_table = vmem_of_match,
+	},
+};
+
+static int __init vmem_init(void)
+{
+	return platform_driver_register(&vmem_driver);
+}
+
+static void __exit vmem_exit(void)
+{
+	platform_driver_unregister(&vmem_driver);
+}
+
+module_init(vmem_init);
+module_exit(vmem_exit);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/media/platform/msm/vidc/vmem/vmem.h b/drivers/media/platform/msm/vidc/vmem/vmem.h
new file mode 100644
index 0000000..0376427
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vmem/vmem.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2014, 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __VMEM_H__
+#define __VMEM_H__
+
+#define VMEM_ERROR(value)	\
+	do {			\
+		pr_info("%s : Fatal Level = %d\n", KBUILD_MODNAME, value);\
+		BUG_ON(value);  \
+	} while (0)
+
+#if (defined CONFIG_MSM_VIDC_VMEM) || (defined CONFIG_MSM_VIDC_VMEM_MODULE)
+
+int vmem_allocate(size_t size, phys_addr_t *addr);
+void vmem_free(phys_addr_t to_free);
+
+#else
+
+static inline int vmem_allocate(size_t size, phys_addr_t *addr)
+{
+	return -ENODEV;
+}
+
+static inline void vmem_free(phys_addr_t to_free)
+{
+}
+
+#endif
+
+#endif /* __VMEM_H__ */
diff --git a/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.c b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.c
new file mode 100644
index 0000000..7d2d524
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/debugfs.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include "vmem.h"
+
+struct vmem_debugfs_cookie {
+	phys_addr_t addr;
+	size_t size;
+};
+
+static int __vmem_alloc_get(void *priv, u64 *val)
+{
+	struct vmem_debugfs_cookie *cookie = priv;
+
+	*val = cookie->size;
+	return 0;
+}
+
+static int __vmem_alloc_set(void *priv, u64 val)
+{
+	struct vmem_debugfs_cookie *cookie = priv;
+	int rc = 0;
+
+	switch (val) {
+	case 0: /* free */
+		vmem_free(cookie->addr);
+		cookie->size = 0;
+		break;
+	default:
+		rc = vmem_allocate(val, &cookie->addr);
+		cookie->size = val;
+		break;
+	}
+
+	return rc;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(fops_vmem_alloc, __vmem_alloc_get,
+		__vmem_alloc_set, "%llu");
+
+struct dentry *vmem_debugfs_init(struct platform_device *pdev)
+{
+	struct vmem_debugfs_cookie *alloc_cookie = NULL;
+	struct dentry *debugfs_root = NULL;
+
+	alloc_cookie = devm_kzalloc(&pdev->dev, sizeof(*alloc_cookie),
+			GFP_KERNEL);
+	if (!alloc_cookie)
+		goto exit;
+
+	debugfs_root = debugfs_create_dir("vmem", NULL);
+	if (IS_ERR_OR_NULL(debugfs_root)) {
+		pr_warn("Failed to create '<debugfs>/vmem'\n");
+		debugfs_root = NULL;
+		goto exit;
+	}
+
+	debugfs_create_file("alloc", 0600, debugfs_root,
+			alloc_cookie, &fops_vmem_alloc);
+
+exit:
+	return debugfs_root;
+}
+
+void vmem_debugfs_deinit(struct dentry *debugfs_root)
+{
+	debugfs_remove_recursive(debugfs_root);
+}
+
diff --git a/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.h b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.h
new file mode 100644
index 0000000..8b716cc
--- /dev/null
+++ b/drivers/media/platform/msm/vidc/vmem/vmem_debugfs.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __VMEM_DEBUGFS_H__
+#define __VMEM_DEBUGFS_H__
+
+#include <linux/debugfs.h>
+
+struct dentry *vmem_debugfs_init(struct platform_device *pdev);
+void vmem_debugfs_deinit(struct dentry *debugfs_root);
+
+#endif /* __VMEM_DEBUGFS_H__ */
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index c6df644..1239e68 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -775,6 +775,17 @@
 	  Say M here if you want to include support for PM8921 chip as a module.
 	  This will build a module called "pm8921-core".
 
+config MFD_I2C_PMIC
+	tristate "QTI I2C PMIC support"
+	depends on I2C && OF
+	select IRQ_DOMAIN
+	select REGMAP_I2C
+	help
+	  This enables support for controlling Qualcomm Technologies, Inc.
+	  PMICs over I2C. The driver controls interrupts, and provides register
+	  access for all of the device's peripherals.  Some QTI PMIC chips
+	  support communication over both I2C and SPMI.
+
 config MFD_QCOM_RPM
 	tristate "Qualcomm Resource Power Manager (RPM)"
 	depends on ARCH_QCOM && OF
@@ -1607,6 +1618,80 @@
 	  in various ST Microelectronics and ST-Ericsson embedded
 	  Nomadik series.
 
+config MSM_CDC_PINCTRL
+	tristate "MSM Codec Pinctrl"
+	select MFD_CORE
+	help
+	  Enables msm codec pinctrl driver. The pinctrl driver
+	  provides support for handling WCD and WSA MSM gpios. This
+	  pinctrl driver will handle WCD and WSA gpios pinctrl states.
+	  This driver acts as interface between codec and pinctrl
+	  framework.
+
+config MSM_CDC_SUPPLY
+	tristate "MSM Codec Power Supply"
+	help
+	  Enables msm codec power supply driver. The power supply
+	  driver provides API support for handling WCD and WSA codec
+	  power supply enable or disable. This driver acts as interface
+	  between codec and regulator framework.
+
+config WCD9XXX_CODEC_UTIL
+	tristate "WCD9XXX Codec Utils"
+	select MFD_CORE
+	help
+	  WCD9XXX Util driver provides APIs for WCD drivers to reset,
+	  suspend/resume, regmap bus callback functions and read/write
+	  functions. This driver also hides the underlying bus related
+	  functionalities.
+
+config WCD9330_CODEC
+	tristate "WCD9330 Codec"
+	select SLIMBUS
+	select MFD_CORE
+	select WCD9XXX_CODEC_UTIL
+	select MSM_CDC_SUPPLY
+	select REGMAP_ALLOW_WRITE_DEBUGFS
+	help
+	  Enables the WCD9xxx codec core driver. The core driver provides
+	  read/write capability to registers which are part of the
+	  WCD9330 core and gives the ability to use the WCD9330 codec.
+	  The WCD9330 codec support either I2C/I2S or Slimbus for
+	  control and data exchnage with master processor.
+
+config WCD9335_CODEC
+	tristate "WCD9335 Codec"
+	select SLIMBUS
+	select SOUNDWIRE_WCD_CTRL
+	select MFD_CORE
+	select WCD9XXX_CODEC_UTIL
+	select MSM_CDC_SUPPLY
+	select MSM_CDC_PINCTRL
+	select REGMAP_ALLOW_WRITE_DEBUGFS
+	help
+	  Enables the WCD9xxx codec core driver. The core driver provides
+	  read/write capability to registers which are part of the
+	  WCD9335 core and gives the ability to use the WCD9335 codec.
+	  The WCD9335 codec support either I2C/I2S or Slimbus for
+	  control and data exchnage with master processor.
+
+config WCD934X_CODEC
+	tristate "WCD934X Codec"
+	depends on SLIMBUS
+	select SOUNDWIRE_WCD_CTRL
+	select MFD_CORE
+	select WCD9XXX_CODEC_UTIL
+	select MSM_CDC_SUPPLY
+	select MSM_CDC_PINCTRL
+	select REGMAP_ALLOW_WRITE_DEBUGFS
+	select PINCTRL_WCD
+	help
+	  Enables the WCD9xxx codec core driver. The core driver provides
+	  read/write capability to registers which are part of the
+	  WCD934X core and gives the ability to use the WCD934X codec.
+	  The WCD934X codec supports either I2C/I2S or Slimbus for
+	  control and data exchange with master processor.
+
 menu "Multimedia Capabilities Port drivers"
 	depends on ARCH_SA1100
 
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 9834e66..b2fe74b 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -173,6 +173,7 @@
 obj-$(CONFIG_MFD_CS5535)	+= cs5535-mfd.o
 obj-$(CONFIG_MFD_OMAP_USB_HOST)	+= omap-usb-host.o omap-usb-tll.o
 obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o ssbi.o
+obj-$(CONFIG_MFD_I2C_PMIC)	+= qcom-i2c-pmic.o
 obj-$(CONFIG_MFD_QCOM_RPM)	+= qcom_rpm.o
 obj-$(CONFIG_MFD_SPMI_PMIC)	+= qcom-spmi-pmic.o
 obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o
@@ -204,6 +205,15 @@
 obj-$(CONFIG_MFD_DLN2)		+= dln2.o
 obj-$(CONFIG_MFD_RT5033)	+= rt5033.o
 obj-$(CONFIG_MFD_SKY81452)	+= sky81452.o
+obj-$(CONFIG_MSM_CDC_PINCTRL)	+= msm-cdc-pinctrl.o
+obj-$(CONFIG_MSM_CDC_SUPPLY) += msm-cdc-supply.o
+obj-$(CONFIG_WCD9XXX_CODEC_UTIL) += wcd9xxx-utils.o
+obj-$(CONFIG_WCD9330_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\
+						wcd9330-regmap.o
+obj-$(CONFIG_WCD9335_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\
+					wcd9335-regmap.o wcd9335-tables.o
+obj-$(CONFIG_WCD934X_CODEC)	+= wcd9xxx-core.o wcd9xxx-irq.o wcd9xxx-slimslave.o\
+					wcd934x-regmap.o wcd934x-tables.o
 
 intel-soc-pmic-objs		:= intel_soc_pmic_core.o intel_soc_pmic_crc.o
 intel-soc-pmic-$(CONFIG_INTEL_PMC_IPC)	+= intel_soc_pmic_bxtwc.o
diff --git a/drivers/mfd/msm-cdc-pinctrl.c b/drivers/mfd/msm-cdc-pinctrl.c
new file mode 100644
index 0000000..3ffd202
--- /dev/null
+++ b/drivers/mfd/msm-cdc-pinctrl.c
@@ -0,0 +1,243 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+
+struct msm_cdc_pinctrl_info {
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *pinctrl_active;
+	struct pinctrl_state *pinctrl_sleep;
+	int gpio;
+	bool state;
+};
+
+static struct msm_cdc_pinctrl_info *msm_cdc_pinctrl_get_gpiodata(
+						struct device_node *np)
+{
+	struct platform_device *pdev;
+	struct msm_cdc_pinctrl_info *gpio_data;
+
+	if (!np) {
+		pr_err("%s: device node is null\n", __func__);
+		return NULL;
+	}
+
+	pdev = of_find_device_by_node(np);
+	if (!pdev) {
+		pr_err("%s: platform device not found!\n", __func__);
+		return NULL;
+	}
+
+	gpio_data = dev_get_drvdata(&pdev->dev);
+	if (!gpio_data)
+		dev_err(&pdev->dev, "%s: cannot find cdc gpio info\n",
+			__func__);
+
+	return gpio_data;
+}
+
+/*
+ * msm_cdc_get_gpio_state: select pinctrl sleep state
+ * @np: pointer to struct device_node
+ *
+ * Returns error code for failure and GPIO value on success
+ */
+int msm_cdc_get_gpio_state(struct device_node *np)
+{
+	struct msm_cdc_pinctrl_info *gpio_data;
+	int value = -EINVAL;
+
+	gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
+	if (!gpio_data)
+		return value;
+
+	if (gpio_is_valid(gpio_data->gpio))
+		value = gpio_get_value_cansleep(gpio_data->gpio);
+
+	return value;
+}
+EXPORT_SYMBOL(msm_cdc_get_gpio_state);
+
+/*
+ * msm_cdc_pinctrl_select_sleep_state: select pinctrl sleep state
+ * @np: pointer to struct device_node
+ *
+ * Returns error code for failure
+ */
+int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
+{
+	struct msm_cdc_pinctrl_info *gpio_data;
+
+	gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
+	if (!gpio_data)
+		return -EINVAL;
+
+	if (!gpio_data->pinctrl_sleep) {
+		pr_err("%s: pinctrl sleep state is null\n", __func__);
+		return -EINVAL;
+	}
+	gpio_data->state = false;
+
+	return pinctrl_select_state(gpio_data->pinctrl,
+				    gpio_data->pinctrl_sleep);
+}
+EXPORT_SYMBOL(msm_cdc_pinctrl_select_sleep_state);
+
+/*
+ * msm_cdc_pinctrl_select_active_state: select pinctrl active state
+ * @np: pointer to struct device_node
+ *
+ * Returns error code for failure
+ */
+int msm_cdc_pinctrl_select_active_state(struct device_node *np)
+{
+	struct msm_cdc_pinctrl_info *gpio_data;
+
+	gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
+	if (!gpio_data)
+		return -EINVAL;
+
+	if (!gpio_data->pinctrl_active) {
+		pr_err("%s: pinctrl active state is null\n", __func__);
+		return -EINVAL;
+	}
+	gpio_data->state = true;
+
+	return pinctrl_select_state(gpio_data->pinctrl,
+				    gpio_data->pinctrl_active);
+}
+EXPORT_SYMBOL(msm_cdc_pinctrl_select_active_state);
+
+/*
+ * msm_cdc_pinctrl_get_state: get curren pinctrl state
+ * @np: pointer to struct device_node
+ *
+ * Returns 0 for sleep state, 1 for active state
+ */
+bool msm_cdc_pinctrl_get_state(struct device_node *np)
+{
+	struct msm_cdc_pinctrl_info *gpio_data;
+
+	gpio_data = msm_cdc_pinctrl_get_gpiodata(np);
+	if (!gpio_data)
+		return -EINVAL;
+
+	return gpio_data->state;
+}
+EXPORT_SYMBOL(msm_cdc_pinctrl_get_state);
+
+static int msm_cdc_pinctrl_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm_cdc_pinctrl_info *gpio_data;
+
+	gpio_data = devm_kzalloc(&pdev->dev,
+				 sizeof(struct msm_cdc_pinctrl_info),
+				 GFP_KERNEL);
+	if (!gpio_data)
+		return -ENOMEM;
+
+	gpio_data->pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(gpio_data->pinctrl)) {
+		dev_err(&pdev->dev, "%s: Cannot get cdc gpio pinctrl:%ld\n",
+			__func__, PTR_ERR(gpio_data->pinctrl));
+		ret = PTR_ERR(gpio_data->pinctrl);
+		goto err_pctrl_get;
+	}
+
+	gpio_data->pinctrl_active = pinctrl_lookup_state(
+					gpio_data->pinctrl, "aud_active");
+	if (IS_ERR_OR_NULL(gpio_data->pinctrl_active)) {
+		dev_err(&pdev->dev, "%s: Cannot get aud_active pinctrl state:%ld\n",
+			__func__, PTR_ERR(gpio_data->pinctrl_active));
+		ret = PTR_ERR(gpio_data->pinctrl_active);
+		goto err_lookup_state;
+	}
+
+	gpio_data->pinctrl_sleep = pinctrl_lookup_state(
+					gpio_data->pinctrl, "aud_sleep");
+	if (IS_ERR_OR_NULL(gpio_data->pinctrl_sleep)) {
+		dev_err(&pdev->dev, "%s: Cannot get aud_sleep pinctrl state:%ld\n",
+			__func__, PTR_ERR(gpio_data->pinctrl_sleep));
+		ret = PTR_ERR(gpio_data->pinctrl_sleep);
+		goto err_lookup_state;
+	}
+
+	/* Set pinctrl state to aud_sleep by default */
+	ret = pinctrl_select_state(gpio_data->pinctrl,
+				   gpio_data->pinctrl_sleep);
+	if (ret)
+		dev_err(&pdev->dev, "%s: set cdc gpio sleep state fail: %d\n",
+			__func__, ret);
+
+	gpio_data->gpio = of_get_named_gpio(pdev->dev.of_node,
+					    "qcom,cdc-rst-n-gpio", 0);
+	if (gpio_is_valid(gpio_data->gpio)) {
+		ret = gpio_request(gpio_data->gpio, "MSM_CDC_RESET");
+		if (ret) {
+			dev_err(&pdev->dev, "%s: Failed to request gpio %d\n",
+				__func__, gpio_data->gpio);
+			goto err_lookup_state;
+		}
+	}
+
+	dev_set_drvdata(&pdev->dev, gpio_data);
+	return 0;
+
+err_lookup_state:
+	devm_pinctrl_put(gpio_data->pinctrl);
+err_pctrl_get:
+	devm_kfree(&pdev->dev, gpio_data);
+	return ret;
+}
+
+static int msm_cdc_pinctrl_remove(struct platform_device *pdev)
+{
+	struct msm_cdc_pinctrl_info *gpio_data;
+
+	gpio_data = dev_get_drvdata(&pdev->dev);
+
+	if (gpio_data && gpio_data->pinctrl)
+		devm_pinctrl_put(gpio_data->pinctrl);
+
+	devm_kfree(&pdev->dev, gpio_data);
+
+	return 0;
+}
+
+static const struct of_device_id msm_cdc_pinctrl_match[] = {
+	{.compatible = "qcom,msm-cdc-pinctrl"},
+	{}
+};
+
+static struct platform_driver msm_cdc_pinctrl_driver = {
+	.driver = {
+		.name = "msm-cdc-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_cdc_pinctrl_match,
+	},
+	.probe = msm_cdc_pinctrl_probe,
+	.remove = msm_cdc_pinctrl_remove,
+};
+module_platform_driver(msm_cdc_pinctrl_driver);
+
+MODULE_DESCRIPTION("MSM CODEC pin control platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/msm-cdc-supply.c b/drivers/mfd/msm-cdc-supply.c
new file mode 100644
index 0000000..9c7ebf7
--- /dev/null
+++ b/drivers/mfd/msm-cdc-supply.c
@@ -0,0 +1,456 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/mfd/msm-cdc-supply.h>
+#include <linux/regulator/consumer.h>
+
+#define CODEC_DT_MAX_PROP_SIZE 40
+
+static int msm_cdc_dt_parse_vreg_info(struct device *dev,
+				      struct cdc_regulator *cdc_vreg,
+				      const char *name, bool is_ond)
+{
+	char prop_name[CODEC_DT_MAX_PROP_SIZE];
+	struct device_node *regulator_node = NULL;
+	const __be32 *prop;
+	int len, rc;
+	u32 prop_val;
+
+	/* Parse supply name */
+	snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply", name);
+
+	regulator_node = of_parse_phandle(dev->of_node, prop_name, 0);
+	if (!regulator_node) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed",
+			__func__, prop_name, dev->of_node->full_name);
+		rc = -EINVAL;
+		goto done;
+	}
+	cdc_vreg->name = name;
+	cdc_vreg->ondemand = is_ond;
+
+	/* Parse supply - voltage */
+	snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-voltage", name);
+	prop = of_get_property(dev->of_node, prop_name, &len);
+	if (!prop || (len != (2 * sizeof(__be32)))) {
+		dev_err(dev, "%s: %s %s property\n", __func__,
+			prop ? "invalid format" : "no", prop_name);
+		rc = -EINVAL;
+		goto done;
+	} else {
+		cdc_vreg->min_uV = be32_to_cpup(&prop[0]);
+		cdc_vreg->max_uV = be32_to_cpup(&prop[1]);
+	}
+
+	/* Parse supply - current */
+	snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "qcom,%s-current", name);
+	rc = of_property_read_u32(dev->of_node, prop_name, &prop_val);
+	if (rc) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed",
+			__func__, prop_name, dev->of_node->full_name);
+		goto done;
+	}
+	cdc_vreg->optimum_uA = prop_val;
+
+	dev_info(dev, "%s: %s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n",
+		 __func__, cdc_vreg->name, cdc_vreg->min_uV, cdc_vreg->max_uV,
+		 cdc_vreg->optimum_uA, cdc_vreg->ondemand);
+
+done:
+	return rc;
+}
+
+static int msm_cdc_parse_supplies(struct device *dev,
+				  struct cdc_regulator *cdc_reg,
+				  const char *sup_list, int sup_cnt,
+				  bool is_ond)
+{
+	int idx, rc = 0;
+	const char *name = NULL;
+
+	for (idx = 0; idx < sup_cnt; idx++) {
+		rc = of_property_read_string_index(dev->of_node, sup_list, idx,
+						   &name);
+		if (rc) {
+			dev_err(dev, "%s: read string %s[%d] error (%d)\n",
+				__func__, sup_list, idx, rc);
+			goto done;
+		}
+
+		dev_dbg(dev, "%s: Found cdc supply %s as part of %s\n",
+			__func__, name, sup_list);
+
+		rc = msm_cdc_dt_parse_vreg_info(dev, &cdc_reg[idx], name,
+						is_ond);
+		if (rc) {
+			dev_err(dev, "%s: parse %s vreg info failed (%d)\n",
+				__func__, name, rc);
+			goto done;
+		}
+	}
+
+done:
+	return rc;
+}
+
+static int msm_cdc_check_supply_param(struct device *dev,
+				      struct cdc_regulator *cdc_vreg,
+				      int num_supplies)
+{
+	if (!dev) {
+		pr_err("%s: device is NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!cdc_vreg || (num_supplies <= 0)) {
+		dev_err(dev, "%s: supply check failed: vreg: %pK, num_supplies: %d\n",
+			__func__, cdc_vreg, num_supplies);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * msm_cdc_disable_static_supplies:
+ *	Disable codec static supplies
+ *
+ * @dev: pointer to codec device
+ * @supplies: pointer to regulator bulk data
+ * @cdc_vreg: pointer to platform regulator data
+ * @num_supplies: number of supplies
+ *
+ * Return error code if supply disable is failed
+ */
+int msm_cdc_disable_static_supplies(struct device *dev,
+				    struct regulator_bulk_data *supplies,
+				    struct cdc_regulator *cdc_vreg,
+				    int num_supplies)
+{
+	int rc, i;
+
+	if ((!dev) || (!supplies) || (!cdc_vreg)) {
+		pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
+				__func__);
+		return -EINVAL;
+	}
+	/* input parameter validation */
+	rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < num_supplies; i++) {
+		if (cdc_vreg[i].ondemand)
+			continue;
+
+		rc = regulator_disable(supplies[i].consumer);
+		if (rc)
+			dev_err(dev, "%s: failed to disable supply %s, err:%d\n",
+				__func__, supplies[i].supply, rc);
+		else
+			dev_dbg(dev, "%s: disabled regulator %s\n",
+				__func__, supplies[i].supply);
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_cdc_disable_static_supplies);
+
+/*
+ * msm_cdc_release_supplies:
+ *	Release codec power supplies
+ *
+ * @dev: pointer to codec device
+ * @supplies: pointer to regulator bulk data
+ * @cdc_vreg: pointer to platform regulator data
+ * @num_supplies: number of supplies
+ *
+ * Return error code if supply disable is failed
+ */
+int msm_cdc_release_supplies(struct device *dev,
+			     struct regulator_bulk_data *supplies,
+			     struct cdc_regulator *cdc_vreg,
+			     int num_supplies)
+{
+	int rc = 0;
+	int i;
+
+	if ((!dev) || (!supplies) || (!cdc_vreg)) {
+		pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
+				__func__);
+		return -EINVAL;
+	}
+	/* input parameter validation */
+	rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
+	if (rc)
+		return rc;
+
+	msm_cdc_disable_static_supplies(dev, supplies, cdc_vreg,
+					num_supplies);
+	for (i = 0; i < num_supplies; i++) {
+		if (regulator_count_voltages(supplies[i].consumer) < 0)
+			continue;
+
+		regulator_set_voltage(supplies[i].consumer, 0,
+				      cdc_vreg[i].max_uV);
+		regulator_set_load(supplies[i].consumer, 0);
+		devm_regulator_put(supplies[i].consumer);
+		supplies[i].consumer = NULL;
+	}
+	devm_kfree(dev, supplies);
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_cdc_release_supplies);
+
+/*
+ * msm_cdc_enable_static_supplies:
+ *	Enable codec static supplies
+ *
+ * @dev: pointer to codec device
+ * @supplies: pointer to regulator bulk data
+ * @cdc_vreg: pointer to platform regulator data
+ * @num_supplies: number of supplies
+ *
+ * Return error code if supply enable is failed
+ */
+int msm_cdc_enable_static_supplies(struct device *dev,
+				   struct regulator_bulk_data *supplies,
+				   struct cdc_regulator *cdc_vreg,
+				   int num_supplies)
+{
+	int rc, i;
+
+	if ((!dev) || (!supplies) || (!cdc_vreg)) {
+		pr_err("%s: either dev or supplies or cdc_vreg is NULL\n",
+				__func__);
+		return -EINVAL;
+	}
+	/* input parameter validation */
+	rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < num_supplies; i++) {
+		if (cdc_vreg[i].ondemand)
+			continue;
+
+		rc = regulator_enable(supplies[i].consumer);
+		if (rc) {
+			dev_err(dev, "%s: failed to enable supply %s, rc: %d\n",
+				__func__, supplies[i].supply, rc);
+			break;
+		}
+	}
+
+	while (rc && i--)
+		if (!cdc_vreg[i].ondemand)
+			regulator_disable(supplies[i].consumer);
+
+	return rc;
+}
+EXPORT_SYMBOL(msm_cdc_enable_static_supplies);
+
+/*
+ * msm_cdc_init_supplies:
+ *	Initialize codec static supplies with regulator get
+ *
+ * @dev: pointer to codec device
+ * @supplies: pointer to regulator bulk data
+ * @cdc_vreg: pointer to platform regulator data
+ * @num_supplies: number of supplies
+ *
+ * Return error code if supply init is failed
+ */
+int msm_cdc_init_supplies(struct device *dev,
+			  struct regulator_bulk_data **supplies,
+			  struct cdc_regulator *cdc_vreg,
+			  int num_supplies)
+{
+	struct regulator_bulk_data *vsup;
+	int rc;
+	int i;
+
+	if (!dev || !cdc_vreg) {
+		pr_err("%s: device pointer or dce_vreg is NULL\n",
+				__func__);
+		return -EINVAL;
+	}
+	/* input parameter validation */
+	rc = msm_cdc_check_supply_param(dev, cdc_vreg, num_supplies);
+	if (rc)
+		return rc;
+
+	vsup = devm_kcalloc(dev, num_supplies,
+			    sizeof(struct regulator_bulk_data),
+			    GFP_KERNEL);
+	if (!vsup)
+		return -ENOMEM;
+
+	for (i = 0; i < num_supplies; i++) {
+		if (!cdc_vreg[i].name) {
+			dev_err(dev, "%s: supply name not defined\n",
+				__func__);
+			rc = -EINVAL;
+			goto err_supply;
+		}
+		vsup[i].supply = cdc_vreg[i].name;
+	}
+
+	rc = devm_regulator_bulk_get(dev, num_supplies, vsup);
+	if (rc) {
+		dev_err(dev, "%s: failed to get supplies (%d)\n",
+			__func__, rc);
+		goto err_supply;
+	}
+
+	/* Set voltage and current on regulators */
+	for (i = 0; i < num_supplies; i++) {
+		if (regulator_count_voltages(vsup[i].consumer) < 0)
+			continue;
+
+		rc = regulator_set_voltage(vsup[i].consumer,
+					   cdc_vreg[i].min_uV,
+					   cdc_vreg[i].max_uV);
+		if (rc) {
+			dev_err(dev, "%s: set regulator voltage failed for %s, err:%d\n",
+				__func__, vsup[i].supply, rc);
+			goto err_set_supply;
+		}
+		rc = regulator_set_load(vsup[i].consumer,
+					cdc_vreg[i].optimum_uA);
+		if (rc < 0) {
+			dev_err(dev, "%s: set regulator optimum mode failed for %s, err:%d\n",
+				__func__, vsup[i].supply, rc);
+			goto err_set_supply;
+		}
+	}
+
+	*supplies = vsup;
+
+	return 0;
+
+err_set_supply:
+	for (i = 0; i < num_supplies; i++)
+		devm_regulator_put(vsup[i].consumer);
+err_supply:
+	devm_kfree(dev, vsup);
+	return rc;
+}
+EXPORT_SYMBOL(msm_cdc_init_supplies);
+
+/*
+ * msm_cdc_get_power_supplies:
+ *	Get codec power supplies from device tree.
+ *	Allocate memory to hold regulator data for
+ *	all power supplies.
+ *
+ * @dev: pointer to codec device
+ * @cdc_vreg: pointer to codec regulator
+ * @total_num_supplies: total number of supplies read from DT
+ *
+ * Return error code if supply disable is failed
+ */
+int msm_cdc_get_power_supplies(struct device *dev,
+			       struct cdc_regulator **cdc_vreg,
+			       int *total_num_supplies)
+{
+	const char *static_prop_name = "qcom,cdc-static-supplies";
+	const char *ond_prop_name = "qcom,cdc-on-demand-supplies";
+	const char *cp_prop_name = "qcom,cdc-cp-supplies";
+	int static_sup_cnt = 0;
+	int ond_sup_cnt = 0;
+	int cp_sup_cnt = 0;
+	int num_supplies = 0;
+	struct cdc_regulator *cdc_reg;
+	int rc;
+
+	if (!dev) {
+		pr_err("%s: device pointer is NULL\n", __func__);
+		return -EINVAL;
+	}
+	static_sup_cnt = of_property_count_strings(dev->of_node,
+						   static_prop_name);
+	if (static_sup_cnt < 0) {
+		dev_err(dev, "%s: Failed to get static supplies(%d)\n",
+			__func__, static_sup_cnt);
+		rc = static_sup_cnt;
+		goto err_supply_cnt;
+	}
+	ond_sup_cnt = of_property_count_strings(dev->of_node, ond_prop_name);
+	if (ond_sup_cnt < 0)
+		ond_sup_cnt = 0;
+
+	cp_sup_cnt = of_property_count_strings(dev->of_node,
+					       cp_prop_name);
+	if (cp_sup_cnt < 0)
+		cp_sup_cnt = 0;
+
+	num_supplies = static_sup_cnt + ond_sup_cnt + cp_sup_cnt;
+	if (num_supplies <= 0) {
+		dev_err(dev, "%s: supply count is 0 or negative\n", __func__);
+		rc = -EINVAL;
+		goto err_supply_cnt;
+	}
+
+	cdc_reg = devm_kcalloc(dev, num_supplies,
+			       sizeof(struct cdc_regulator),
+			       GFP_KERNEL);
+	if (!cdc_reg) {
+		rc = -ENOMEM;
+		goto err_mem_alloc;
+	}
+
+	rc = msm_cdc_parse_supplies(dev, cdc_reg, static_prop_name,
+				    static_sup_cnt, false);
+	if (rc) {
+		dev_err(dev, "%s: failed to parse static supplies(%d)\n",
+				__func__, rc);
+		goto err_sup;
+	}
+
+	rc = msm_cdc_parse_supplies(dev, &cdc_reg[static_sup_cnt],
+				    ond_prop_name, ond_sup_cnt,
+				    true);
+	if (rc) {
+		dev_err(dev, "%s: failed to parse demand supplies(%d)\n",
+				__func__, rc);
+		goto err_sup;
+	}
+
+	rc = msm_cdc_parse_supplies(dev,
+				    &cdc_reg[static_sup_cnt + ond_sup_cnt],
+				    cp_prop_name, cp_sup_cnt, true);
+	if (rc) {
+		dev_err(dev, "%s: failed to parse cp supplies(%d)\n",
+				__func__, rc);
+		goto err_sup;
+	}
+
+	*cdc_vreg = cdc_reg;
+	*total_num_supplies = num_supplies;
+
+	return 0;
+
+err_sup:
+	devm_kfree(dev, cdc_reg);
+err_supply_cnt:
+err_mem_alloc:
+	return rc;
+}
+EXPORT_SYMBOL(msm_cdc_get_power_supplies);
diff --git a/drivers/mfd/qcom-i2c-pmic.c b/drivers/mfd/qcom-i2c-pmic.c
new file mode 100644
index 0000000..590e4c1
--- /dev/null
+++ b/drivers/mfd/qcom-i2c-pmic.c
@@ -0,0 +1,681 @@
+/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "I2C PMIC: %s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define I2C_INTR_STATUS_BASE	0x0550
+#define INT_RT_STS_OFFSET	0x10
+#define INT_SET_TYPE_OFFSET	0x11
+#define INT_POL_HIGH_OFFSET	0x12
+#define INT_POL_LOW_OFFSET	0x13
+#define INT_LATCHED_CLR_OFFSET	0x14
+#define INT_EN_SET_OFFSET	0x15
+#define INT_EN_CLR_OFFSET	0x16
+#define INT_LATCHED_STS_OFFSET	0x18
+#define INT_PENDING_STS_OFFSET	0x19
+#define INT_MID_SEL_OFFSET	0x1A
+#define INT_MID_SEL_MASK	GENMASK(1, 0)
+#define INT_PRIORITY_OFFSET	0x1B
+#define INT_PRIORITY_BIT	BIT(0)
+
+enum {
+	IRQ_SET_TYPE = 0,
+	IRQ_POL_HIGH,
+	IRQ_POL_LOW,
+	IRQ_LATCHED_CLR, /* not needed but makes life easy */
+	IRQ_EN_SET,
+	IRQ_MAX_REGS,
+};
+
+struct i2c_pmic_periph {
+	void		*data;
+	u16		addr;
+	u8		cached[IRQ_MAX_REGS];
+	u8		synced[IRQ_MAX_REGS];
+	u8		wake;
+	struct mutex	lock;
+};
+
+struct i2c_pmic {
+	struct device		*dev;
+	struct regmap		*regmap;
+	struct irq_domain	*domain;
+	struct i2c_pmic_periph	*periph;
+	struct pinctrl		*pinctrl;
+	const char		*pinctrl_name;
+	int			num_periphs;
+};
+
+static void i2c_pmic_irq_bus_lock(struct irq_data *d)
+{
+	struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+	mutex_lock(&periph->lock);
+}
+
+static void i2c_pmic_sync_type_polarity(struct i2c_pmic *chip,
+			       struct i2c_pmic_periph *periph)
+{
+	int rc;
+
+	/* did any irq type change? */
+	if (periph->cached[IRQ_SET_TYPE] ^ periph->synced[IRQ_SET_TYPE]) {
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_SET_TYPE_OFFSET,
+				  periph->cached[IRQ_SET_TYPE]);
+		if (rc < 0) {
+			pr_err("Couldn't set periph 0x%04x irqs 0x%02x type rc=%d\n",
+				periph->addr, periph->cached[IRQ_SET_TYPE], rc);
+			return;
+		}
+
+		periph->synced[IRQ_SET_TYPE] = periph->cached[IRQ_SET_TYPE];
+	}
+
+	/* did any polarity high change? */
+	if (periph->cached[IRQ_POL_HIGH] ^ periph->synced[IRQ_POL_HIGH]) {
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_POL_HIGH_OFFSET,
+				  periph->cached[IRQ_POL_HIGH]);
+		if (rc < 0) {
+			pr_err("Couldn't set periph 0x%04x irqs 0x%02x polarity high rc=%d\n",
+				periph->addr, periph->cached[IRQ_POL_HIGH], rc);
+			return;
+		}
+
+		periph->synced[IRQ_POL_HIGH] = periph->cached[IRQ_POL_HIGH];
+	}
+
+	/* did any polarity low change? */
+	if (periph->cached[IRQ_POL_LOW] ^ periph->synced[IRQ_POL_LOW]) {
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_POL_LOW_OFFSET,
+				  periph->cached[IRQ_POL_LOW]);
+		if (rc < 0) {
+			pr_err("Couldn't set periph 0x%04x irqs 0x%02x polarity low rc=%d\n",
+				periph->addr, periph->cached[IRQ_POL_LOW], rc);
+			return;
+		}
+
+		periph->synced[IRQ_POL_LOW] = periph->cached[IRQ_POL_LOW];
+	}
+}
+
+static void i2c_pmic_sync_enable(struct i2c_pmic *chip,
+				 struct i2c_pmic_periph *periph)
+{
+	u8 en_set, en_clr;
+	int rc;
+
+	/* determine which irqs were enabled and which were disabled */
+	en_clr = periph->synced[IRQ_EN_SET] & ~periph->cached[IRQ_EN_SET];
+	en_set = ~periph->synced[IRQ_EN_SET] & periph->cached[IRQ_EN_SET];
+
+	/* were any irqs disabled? */
+	if (en_clr) {
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_EN_CLR_OFFSET, en_clr);
+		if (rc < 0) {
+			pr_err("Couldn't disable periph 0x%04x irqs 0x%02x rc=%d\n",
+				periph->addr, en_clr, rc);
+			return;
+		}
+	}
+
+	/* were any irqs enabled? */
+	if (en_set) {
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_EN_SET_OFFSET, en_set);
+		if (rc < 0) {
+			pr_err("Couldn't enable periph 0x%04x irqs 0x%02x rc=%d\n",
+				periph->addr, en_set, rc);
+			return;
+		}
+	}
+
+	/* irq enabled status was written to hardware */
+	periph->synced[IRQ_EN_SET] = periph->cached[IRQ_EN_SET];
+}
+
+static void i2c_pmic_irq_bus_sync_unlock(struct irq_data *d)
+{
+	struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+	struct i2c_pmic *chip = periph->data;
+
+	i2c_pmic_sync_type_polarity(chip, periph);
+	i2c_pmic_sync_enable(chip, periph);
+	mutex_unlock(&periph->lock);
+}
+
+static void i2c_pmic_irq_disable(struct irq_data *d)
+{
+	struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+	periph->cached[IRQ_EN_SET] &= ~d->hwirq & 0xFF;
+}
+
+static void i2c_pmic_irq_enable(struct irq_data *d)
+{
+	struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+	periph->cached[IRQ_EN_SET] |= d->hwirq & 0xFF;
+}
+
+static int i2c_pmic_irq_set_type(struct irq_data *d, unsigned int irq_type)
+{
+	struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+	switch (irq_type) {
+	case IRQ_TYPE_EDGE_RISING:
+		periph->cached[IRQ_SET_TYPE] |= d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_HIGH] |= d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_LOW] &= ~d->hwirq & 0xFF;
+		break;
+	case IRQ_TYPE_EDGE_FALLING:
+		periph->cached[IRQ_SET_TYPE] |= d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_HIGH] &= ~d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_LOW] |= d->hwirq & 0xFF;
+		break;
+	case IRQ_TYPE_EDGE_BOTH:
+		periph->cached[IRQ_SET_TYPE] |= d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_HIGH] |= d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_LOW] |= d->hwirq & 0xFF;
+		break;
+	case IRQ_TYPE_LEVEL_HIGH:
+		periph->cached[IRQ_SET_TYPE] &= ~d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_HIGH] |= d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_LOW] &= ~d->hwirq & 0xFF;
+		break;
+	case IRQ_TYPE_LEVEL_LOW:
+		periph->cached[IRQ_SET_TYPE] &= ~d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_HIGH] &= ~d->hwirq & 0xFF;
+		periph->cached[IRQ_POL_LOW] |= d->hwirq & 0xFF;
+		break;
+	default:
+		pr_err("irq type 0x%04x is not supported\n", irq_type);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2c_pmic_irq_set_wake(struct irq_data *d, unsigned int on)
+{
+	struct i2c_pmic_periph *periph = irq_data_get_irq_chip_data(d);
+
+	if (on)
+		periph->wake |= d->hwirq & 0xFF;
+	else
+		periph->wake &= ~d->hwirq & 0xFF;
+
+	return 0;
+}
+#else
+#define i2c_pmic_irq_set_wake NULL
+#endif
+
+static struct irq_chip i2c_pmic_irq_chip = {
+	.name			= "i2c_pmic_irq_chip",
+	.irq_bus_lock		= i2c_pmic_irq_bus_lock,
+	.irq_bus_sync_unlock	= i2c_pmic_irq_bus_sync_unlock,
+	.irq_disable		= i2c_pmic_irq_disable,
+	.irq_enable		= i2c_pmic_irq_enable,
+	.irq_set_type		= i2c_pmic_irq_set_type,
+	.irq_set_wake		= i2c_pmic_irq_set_wake,
+};
+
+static struct i2c_pmic_periph *i2c_pmic_find_periph(struct i2c_pmic *chip,
+						    irq_hw_number_t hwirq)
+{
+	int i;
+
+	for (i = 0; i < chip->num_periphs; i++)
+		if (chip->periph[i].addr == (hwirq & 0xFF00))
+			return &chip->periph[i];
+
+	pr_err_ratelimited("Couldn't find periph struct for hwirq 0x%04lx\n",
+			   hwirq);
+	return NULL;
+}
+
+static int i2c_pmic_domain_map(struct irq_domain *d, unsigned int virq,
+			irq_hw_number_t hwirq)
+{
+	struct i2c_pmic *chip = d->host_data;
+	struct i2c_pmic_periph *periph = i2c_pmic_find_periph(chip, hwirq);
+
+	if (!periph)
+		return -ENODEV;
+
+	irq_set_chip_data(virq, periph);
+	irq_set_chip_and_handler(virq, &i2c_pmic_irq_chip, handle_level_irq);
+	irq_set_nested_thread(virq, 1);
+	irq_set_noprobe(virq);
+	return 0;
+}
+
+static int i2c_pmic_domain_xlate(struct irq_domain *d,
+				 struct device_node *ctrlr, const u32 *intspec,
+				 unsigned int intsize, unsigned long *out_hwirq,
+				 unsigned int *out_type)
+{
+	if (intsize != 3)
+		return -EINVAL;
+
+	if (intspec[0] > 0xFF || intspec[1] > 0x7 ||
+					intspec[2] > IRQ_TYPE_SENSE_MASK)
+		return -EINVAL;
+
+	/*
+	 * Interrupt specifiers are triplets
+	 * <peripheral-address, irq-number, IRQ_TYPE_*>
+	 *
+	 * peripheral-address - The base address of the peripheral
+	 * irq-number	      - The zero based bit position of the peripheral's
+	 *			interrupt registers corresponding to the irq
+	 *			where the LSB is 0 and the MSB is 7
+	 * IRQ_TYPE_*	      - Please refer to linux/irq.h
+	 */
+	*out_hwirq = intspec[0] << 8 | BIT(intspec[1]);
+	*out_type = intspec[2] & IRQ_TYPE_SENSE_MASK;
+
+	return 0;
+}
+
+static const struct irq_domain_ops i2c_pmic_domain_ops = {
+	.map	= i2c_pmic_domain_map,
+	.xlate	= i2c_pmic_domain_xlate,
+};
+
+static void i2c_pmic_irq_ack_now(struct i2c_pmic *chip, u16 hwirq)
+{
+	int rc;
+
+	rc = regmap_write(chip->regmap,
+			  (hwirq & 0xFF00) | INT_LATCHED_CLR_OFFSET,
+			  hwirq & 0xFF);
+	if (rc < 0)
+		pr_err_ratelimited("Couldn't ack 0x%04x rc=%d\n", hwirq, rc);
+}
+
+static void i2c_pmic_irq_disable_now(struct i2c_pmic *chip, u16 hwirq)
+{
+	struct i2c_pmic_periph *periph = i2c_pmic_find_periph(chip, hwirq);
+	int rc;
+
+	if (!periph)
+		return;
+
+	mutex_lock(&periph->lock);
+	periph->cached[IRQ_EN_SET] &= ~hwirq & 0xFF;
+
+	rc = regmap_write(chip->regmap,
+			  (hwirq & 0xFF00) | INT_EN_CLR_OFFSET,
+			  hwirq & 0xFF);
+	if (rc < 0) {
+		pr_err_ratelimited("Couldn't disable irq 0x%04x rc=%d\n",
+				   hwirq, rc);
+		goto unlock;
+	}
+
+	periph->synced[IRQ_EN_SET] = periph->cached[IRQ_EN_SET];
+
+unlock:
+	mutex_unlock(&periph->lock);
+}
+
+static void i2c_pmic_periph_status_handler(struct i2c_pmic *chip,
+					   u16 periph_address, u8 periph_status)
+{
+	unsigned int hwirq, virq;
+	int i;
+
+	while (periph_status) {
+		i = ffs(periph_status) - 1;
+		periph_status &= ~BIT(i);
+		hwirq = periph_address | BIT(i);
+		virq = irq_find_mapping(chip->domain, hwirq);
+		if (virq == 0) {
+			pr_err_ratelimited("Couldn't find mapping; disabling 0x%04x\n",
+					   hwirq);
+			i2c_pmic_irq_disable_now(chip, hwirq);
+			continue;
+		}
+
+		handle_nested_irq(virq);
+		i2c_pmic_irq_ack_now(chip, hwirq);
+	}
+}
+
+static void i2c_pmic_summary_status_handler(struct i2c_pmic *chip,
+					    struct i2c_pmic_periph *periph,
+					    u8 summary_status)
+{
+	unsigned int periph_status;
+	int rc, i;
+
+	while (summary_status) {
+		i = ffs(summary_status) - 1;
+		summary_status &= ~BIT(i);
+
+		rc = regmap_read(chip->regmap,
+				 periph[i].addr | INT_LATCHED_STS_OFFSET,
+				 &periph_status);
+		if (rc < 0) {
+			pr_err_ratelimited("Couldn't read 0x%04x | INT_LATCHED_STS rc=%d\n",
+					   periph[i].addr, rc);
+			continue;
+		}
+
+		i2c_pmic_periph_status_handler(chip, periph[i].addr,
+					       periph_status);
+	}
+}
+
+static irqreturn_t i2c_pmic_irq_handler(int irq, void *dev_id)
+{
+	struct i2c_pmic *chip = dev_id;
+	struct i2c_pmic_periph *periph;
+	unsigned int summary_status;
+	int rc, i;
+
+	for (i = 0; i < DIV_ROUND_UP(chip->num_periphs, BITS_PER_BYTE); i++) {
+		rc = regmap_read(chip->regmap, I2C_INTR_STATUS_BASE + i,
+				&summary_status);
+		if (rc < 0) {
+			pr_err_ratelimited("Couldn't read I2C_INTR_STATUS%d rc=%d\n",
+					   i, rc);
+			continue;
+		}
+
+		if (summary_status == 0)
+			continue;
+
+		periph = &chip->periph[i * 8];
+		i2c_pmic_summary_status_handler(chip, periph, summary_status);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int i2c_pmic_parse_dt(struct i2c_pmic *chip)
+{
+	struct device_node *node = chip->dev->of_node;
+	int rc, i;
+	u32 temp;
+
+	if (!node) {
+		pr_err("missing device tree\n");
+		return -EINVAL;
+	}
+
+	chip->num_periphs = of_property_count_u32_elems(node,
+							"qcom,periph-map");
+	if (chip->num_periphs < 0) {
+		pr_err("missing qcom,periph-map property rc=%d\n",
+			chip->num_periphs);
+		return chip->num_periphs;
+	}
+
+	if (chip->num_periphs == 0) {
+		pr_err("qcom,periph-map must contain at least one address\n");
+		return -EINVAL;
+	}
+
+	chip->periph = devm_kcalloc(chip->dev, chip->num_periphs,
+				     sizeof(*chip->periph), GFP_KERNEL);
+	if (!chip->periph)
+		return -ENOMEM;
+
+	for (i = 0; i < chip->num_periphs; i++) {
+		rc = of_property_read_u32_index(node, "qcom,periph-map",
+						i, &temp);
+		if (rc < 0) {
+			pr_err("Couldn't read qcom,periph-map[%d] rc=%d\n",
+			       i, rc);
+			return rc;
+		}
+
+		chip->periph[i].addr = (u16)(temp << 8);
+		chip->periph[i].data = chip;
+		mutex_init(&chip->periph[i].lock);
+	}
+
+	of_property_read_string(node, "pinctrl-names", &chip->pinctrl_name);
+
+	return rc;
+}
+
+#define MAX_I2C_RETRIES	3
+static int i2c_pmic_read(struct regmap *map, unsigned int reg, void *val,
+			size_t val_count)
+{
+	int rc, retries = 0;
+
+	do {
+		rc = regmap_bulk_read(map, reg, val, val_count);
+	} while (rc == -ENOTCONN && retries++ < MAX_I2C_RETRIES);
+
+	if (retries > 1)
+		pr_err("i2c_pmic_read failed for %d retries, rc = %d\n",
+			retries - 1, rc);
+
+	return rc;
+}
+
+static int i2c_pmic_determine_initial_status(struct i2c_pmic *chip)
+{
+	int rc, i;
+
+	for (i = 0; i < chip->num_periphs; i++) {
+		rc = i2c_pmic_read(chip->regmap,
+				chip->periph[i].addr | INT_SET_TYPE_OFFSET,
+				chip->periph[i].cached, IRQ_MAX_REGS);
+		if (rc < 0) {
+			pr_err("Couldn't read irq data rc=%d\n", rc);
+			return rc;
+		}
+
+		memcpy(chip->periph[i].synced, chip->periph[i].cached,
+		       IRQ_MAX_REGS * sizeof(*chip->periph[i].synced));
+	}
+
+	return 0;
+}
+
+static struct regmap_config i2c_pmic_regmap_config = {
+	.reg_bits	= 16,
+	.val_bits	= 8,
+	.max_register	= 0xFFFF,
+};
+
+static int i2c_pmic_probe(struct i2c_client *client,
+			  const struct i2c_device_id *id)
+{
+	struct i2c_pmic *chip;
+	int rc = 0;
+
+	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->dev = &client->dev;
+	chip->regmap = devm_regmap_init_i2c(client, &i2c_pmic_regmap_config);
+	if (!chip->regmap)
+		return -ENODEV;
+
+	i2c_set_clientdata(client, chip);
+	if (!of_property_read_bool(chip->dev->of_node, "interrupt-controller"))
+		goto probe_children;
+
+	chip->domain = irq_domain_add_tree(client->dev.of_node,
+					   &i2c_pmic_domain_ops, chip);
+	if (!chip->domain) {
+		rc = -ENOMEM;
+		goto cleanup;
+	}
+
+	rc = i2c_pmic_parse_dt(chip);
+	if (rc < 0) {
+		pr_err("Couldn't parse device tree rc=%d\n", rc);
+		goto cleanup;
+	}
+
+	rc = i2c_pmic_determine_initial_status(chip);
+	if (rc < 0) {
+		pr_err("Couldn't determine initial status rc=%d\n", rc);
+		goto cleanup;
+	}
+
+	if (chip->pinctrl_name) {
+		chip->pinctrl = devm_pinctrl_get_select(chip->dev,
+							chip->pinctrl_name);
+		if (IS_ERR(chip->pinctrl)) {
+			pr_err("Couldn't select %s pinctrl rc=%ld\n",
+				chip->pinctrl_name, PTR_ERR(chip->pinctrl));
+			rc = PTR_ERR(chip->pinctrl);
+			goto cleanup;
+		}
+	}
+
+	rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
+				       i2c_pmic_irq_handler,
+				       IRQF_ONESHOT | IRQF_SHARED,
+				       "i2c_pmic_stat_irq", chip);
+	if (rc < 0) {
+		pr_err("Couldn't request irq %d rc=%d\n", client->irq, rc);
+		goto cleanup;
+	}
+
+	enable_irq_wake(client->irq);
+
+probe_children:
+	of_platform_populate(chip->dev->of_node, NULL, NULL, chip->dev);
+	pr_info("I2C PMIC probe successful\n");
+	return rc;
+
+cleanup:
+	if (chip->domain)
+		irq_domain_remove(chip->domain);
+	i2c_set_clientdata(client, NULL);
+	return rc;
+}
+
+static int i2c_pmic_remove(struct i2c_client *client)
+{
+	struct i2c_pmic *chip = i2c_get_clientdata(client);
+
+	of_platform_depopulate(chip->dev);
+	if (chip->domain)
+		irq_domain_remove(chip->domain);
+	i2c_set_clientdata(client, NULL);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int i2c_pmic_suspend(struct device *dev)
+{
+	struct i2c_pmic *chip = dev_get_drvdata(dev);
+	struct i2c_pmic_periph *periph;
+	int rc = 0, i;
+
+	for (i = 0; i < chip->num_periphs; i++) {
+		periph = &chip->periph[i];
+
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_EN_CLR_OFFSET, 0xFF);
+		if (rc < 0) {
+			pr_err_ratelimited("Couldn't clear 0x%04x irqs rc=%d\n",
+				periph->addr, rc);
+			continue;
+		}
+
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_EN_SET_OFFSET,
+				  periph->wake);
+		if (rc < 0)
+			pr_err_ratelimited("Couldn't enable 0x%04x wake irqs 0x%02x rc=%d\n",
+			       periph->addr, periph->wake, rc);
+	}
+
+	return rc;
+}
+
+static int i2c_pmic_resume(struct device *dev)
+{
+	struct i2c_pmic *chip = dev_get_drvdata(dev);
+	struct i2c_pmic_periph *periph;
+	int rc = 0, i;
+
+	for (i = 0; i < chip->num_periphs; i++) {
+		periph = &chip->periph[i];
+
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_EN_CLR_OFFSET, 0xFF);
+		if (rc < 0) {
+			pr_err("Couldn't clear 0x%04x irqs rc=%d\n",
+				periph->addr, rc);
+			continue;
+		}
+
+		rc = regmap_write(chip->regmap,
+				  periph->addr | INT_EN_SET_OFFSET,
+				  periph->synced[IRQ_EN_SET]);
+		if (rc < 0)
+			pr_err("Couldn't restore 0x%04x synced irqs 0x%02x rc=%d\n",
+			       periph->addr, periph->synced[IRQ_EN_SET], rc);
+	}
+
+	return rc;
+}
+#endif
+static SIMPLE_DEV_PM_OPS(i2c_pmic_pm_ops, i2c_pmic_suspend, i2c_pmic_resume);
+
+static const struct of_device_id i2c_pmic_match_table[] = {
+	{ .compatible = "qcom,i2c-pmic", },
+	{ },
+};
+
+static const struct i2c_device_id i2c_pmic_id[] = {
+	{ "i2c-pmic", 0 },
+	{ },
+};
+MODULE_DEVICE_TABLE(i2c, i2c_pmic_id);
+
+static struct i2c_driver i2c_pmic_driver = {
+	.driver		= {
+		.name		= "i2c_pmic",
+		.owner		= THIS_MODULE,
+		.pm		= &i2c_pmic_pm_ops,
+		.of_match_table	= i2c_pmic_match_table,
+	},
+	.probe		= i2c_pmic_probe,
+	.remove		= i2c_pmic_remove,
+	.id_table	= i2c_pmic_id,
+};
+
+module_i2c_driver(i2c_pmic_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("i2c:i2c_pmic");
diff --git a/drivers/mfd/wcd9330-regmap.c b/drivers/mfd/wcd9330-regmap.c
new file mode 100644
index 0000000..878ea72
--- /dev/null
+++ b/drivers/mfd/wcd9330-regmap.c
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wcd9xxx-regmap.h"
+
+static struct reg_default wcd9330_defaults[] = {
+	{ TOMTOM_A_CHIP_CTL, TOMTOM_A_CHIP_CTL__POR },
+	{ TOMTOM_A_CHIP_STATUS, TOMTOM_A_CHIP_STATUS__POR },
+	{ TOMTOM_A_CHIP_ID_BYTE_0, TOMTOM_A_CHIP_ID_BYTE_0__POR },
+	{ TOMTOM_A_CHIP_ID_BYTE_1, TOMTOM_A_CHIP_ID_BYTE_1__POR },
+	{ TOMTOM_A_CHIP_ID_BYTE_2, TOMTOM_A_CHIP_ID_BYTE_2__POR },
+	{ TOMTOM_A_CHIP_ID_BYTE_3, TOMTOM_A_CHIP_ID_BYTE_3__POR },
+	{ TOMTOM_A_CHIP_I2C_SLAVE_ID, TOMTOM_A_CHIP_I2C_SLAVE_ID__POR },
+	{ TOMTOM_A_SLAVE_ID_1, TOMTOM_A_SLAVE_ID_1__POR },
+	{ TOMTOM_A_SLAVE_ID_2, TOMTOM_A_SLAVE_ID_2__POR },
+	{ TOMTOM_A_SLAVE_ID_3, TOMTOM_A_SLAVE_ID_3__POR },
+	{ TOMTOM_A_PIN_CTL_OE0, TOMTOM_A_PIN_CTL_OE0__POR },
+	{ TOMTOM_A_PIN_CTL_OE1, TOMTOM_A_PIN_CTL_OE1__POR },
+	{ TOMTOM_A_PIN_CTL_OE2, TOMTOM_A_PIN_CTL_OE2__POR },
+	{ TOMTOM_A_PIN_CTL_DATA0, TOMTOM_A_PIN_CTL_DATA0__POR },
+	{ TOMTOM_A_PIN_CTL_DATA1, TOMTOM_A_PIN_CTL_DATA1__POR },
+	{ TOMTOM_A_PIN_CTL_DATA2, TOMTOM_A_PIN_CTL_DATA2__POR },
+	{ TOMTOM_A_HDRIVE_GENERIC, TOMTOM_A_HDRIVE_GENERIC__POR },
+	{ TOMTOM_A_HDRIVE_OVERRIDE, TOMTOM_A_HDRIVE_OVERRIDE__POR },
+	{ TOMTOM_A_ANA_CSR_WAIT_STATE, TOMTOM_A_ANA_CSR_WAIT_STATE__POR },
+	{ TOMTOM_A_PROCESS_MONITOR_CTL0, TOMTOM_A_PROCESS_MONITOR_CTL0__POR },
+	{ TOMTOM_A_PROCESS_MONITOR_CTL1, TOMTOM_A_PROCESS_MONITOR_CTL1__POR },
+	{ TOMTOM_A_PROCESS_MONITOR_CTL2, TOMTOM_A_PROCESS_MONITOR_CTL2__POR },
+	{ TOMTOM_A_PROCESS_MONITOR_CTL3, TOMTOM_A_PROCESS_MONITOR_CTL3__POR },
+	{ TOMTOM_A_QFUSE_CTL, TOMTOM_A_QFUSE_CTL__POR },
+	{ TOMTOM_A_QFUSE_STATUS, TOMTOM_A_QFUSE_STATUS__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT0, TOMTOM_A_QFUSE_DATA_OUT0__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT1, TOMTOM_A_QFUSE_DATA_OUT1__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT2, TOMTOM_A_QFUSE_DATA_OUT2__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT3, TOMTOM_A_QFUSE_DATA_OUT3__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT4, TOMTOM_A_QFUSE_DATA_OUT4__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT5, TOMTOM_A_QFUSE_DATA_OUT5__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT6, TOMTOM_A_QFUSE_DATA_OUT6__POR },
+	{ TOMTOM_A_QFUSE_DATA_OUT7, TOMTOM_A_QFUSE_DATA_OUT7__POR },
+	{ TOMTOM_A_CDC_CTL, TOMTOM_A_CDC_CTL__POR },
+	{ TOMTOM_A_LEAKAGE_CTL, TOMTOM_A_LEAKAGE_CTL__POR },
+	{ TOMTOM_A_SVASS_MEM_PTR0, TOMTOM_A_SVASS_MEM_PTR0__POR },
+	{ TOMTOM_A_SVASS_MEM_PTR1, TOMTOM_A_SVASS_MEM_PTR1__POR },
+	{ TOMTOM_A_SVASS_MEM_PTR2, TOMTOM_A_SVASS_MEM_PTR2__POR },
+	{ TOMTOM_A_SVASS_MEM_CTL, TOMTOM_A_SVASS_MEM_CTL__POR },
+	{ TOMTOM_A_SVASS_MEM_BANK, TOMTOM_A_SVASS_MEM_BANK__POR },
+	{ TOMTOM_A_DMIC_B1_CTL, TOMTOM_A_DMIC_B1_CTL__POR },
+	{ TOMTOM_A_DMIC_B2_CTL, TOMTOM_A_DMIC_B2_CTL__POR },
+	{ TOMTOM_A_SVASS_CLKRST_CTL, TOMTOM_A_SVASS_CLKRST_CTL__POR },
+	{ TOMTOM_A_SVASS_CPAR_CFG, TOMTOM_A_SVASS_CPAR_CFG__POR },
+	{ TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD,
+				TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR },
+	{ TOMTOM_A_SVASS_CPAR_WDOG_CFG, TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR },
+	{ TOMTOM_A_SVASS_CFG, TOMTOM_A_SVASS_CFG__POR },
+	{ TOMTOM_A_SVASS_SPE_CFG, TOMTOM_A_SVASS_SPE_CFG__POR },
+	{ TOMTOM_A_SVASS_STATUS, TOMTOM_A_SVASS_STATUS__POR },
+	{ TOMTOM_A_SVASS_INT_MASK, TOMTOM_A_SVASS_INT_MASK__POR },
+	{ TOMTOM_A_SVASS_INT_STATUS, TOMTOM_A_SVASS_INT_STATUS__POR },
+	{ TOMTOM_A_SVASS_INT_CLR, TOMTOM_A_SVASS_INT_CLR__POR },
+	{ TOMTOM_A_SVASS_DEBUG, TOMTOM_A_SVASS_DEBUG__POR },
+	{ TOMTOM_A_SVASS_SPE_BKUP_INT, TOMTOM_A_SVASS_SPE_BKUP_INT__POR },
+	{ TOMTOM_A_SVASS_MEM_ACC, TOMTOM_A_SVASS_MEM_ACC__POR },
+	{ TOMTOM_A_MEM_LEAKAGE_CTL, TOMTOM_A_MEM_LEAKAGE_CTL__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_TRG, TOMTOM_A_SVASS_SPE_INBOX_TRG__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_0, TOMTOM_A_SVASS_SPE_INBOX_0__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_1, TOMTOM_A_SVASS_SPE_INBOX_1__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_2, TOMTOM_A_SVASS_SPE_INBOX_2__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_3, TOMTOM_A_SVASS_SPE_INBOX_3__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_4, TOMTOM_A_SVASS_SPE_INBOX_4__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_5, TOMTOM_A_SVASS_SPE_INBOX_5__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_6, TOMTOM_A_SVASS_SPE_INBOX_6__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_7, TOMTOM_A_SVASS_SPE_INBOX_7__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_8, TOMTOM_A_SVASS_SPE_INBOX_8__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_9, TOMTOM_A_SVASS_SPE_INBOX_9__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_10, TOMTOM_A_SVASS_SPE_INBOX_10__POR },
+	{ TOMTOM_A_SVASS_SPE_INBOX_11, TOMTOM_A_SVASS_SPE_INBOX_11__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_0, TOMTOM_A_SVASS_SPE_OUTBOX_0__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_1, TOMTOM_A_SVASS_SPE_OUTBOX_1__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_2, TOMTOM_A_SVASS_SPE_OUTBOX_2__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_3, TOMTOM_A_SVASS_SPE_OUTBOX_3__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_4, TOMTOM_A_SVASS_SPE_OUTBOX_4__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_5, TOMTOM_A_SVASS_SPE_OUTBOX_5__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_6, TOMTOM_A_SVASS_SPE_OUTBOX_6__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_7, TOMTOM_A_SVASS_SPE_OUTBOX_7__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_8, TOMTOM_A_SVASS_SPE_OUTBOX_8__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_9, TOMTOM_A_SVASS_SPE_OUTBOX_9__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_10, TOMTOM_A_SVASS_SPE_OUTBOX_10__POR },
+	{ TOMTOM_A_SVASS_SPE_OUTBOX_11, TOMTOM_A_SVASS_SPE_OUTBOX_11__POR },
+	{ TOMTOM_A_INTR_MODE, TOMTOM_A_INTR_MODE__POR },
+	{ TOMTOM_A_INTR1_MASK0, TOMTOM_A_INTR1_MASK0__POR },
+	{ TOMTOM_A_INTR1_MASK1, TOMTOM_A_INTR1_MASK1__POR },
+	{ TOMTOM_A_INTR1_MASK2, TOMTOM_A_INTR1_MASK2__POR },
+	{ TOMTOM_A_INTR1_MASK3, TOMTOM_A_INTR1_MASK3__POR },
+	{ TOMTOM_A_INTR1_STATUS0, TOMTOM_A_INTR1_STATUS0__POR },
+	{ TOMTOM_A_INTR1_STATUS1, TOMTOM_A_INTR1_STATUS1__POR },
+	{ TOMTOM_A_INTR1_STATUS2, TOMTOM_A_INTR1_STATUS2__POR },
+	{ TOMTOM_A_INTR1_STATUS3, TOMTOM_A_INTR1_STATUS3__POR },
+	{ TOMTOM_A_INTR1_CLEAR0, TOMTOM_A_INTR1_CLEAR0__POR },
+	{ TOMTOM_A_INTR1_CLEAR1, TOMTOM_A_INTR1_CLEAR1__POR },
+	{ TOMTOM_A_INTR1_CLEAR2, TOMTOM_A_INTR1_CLEAR2__POR },
+	{ TOMTOM_A_INTR1_CLEAR3, TOMTOM_A_INTR1_CLEAR3__POR },
+	{ TOMTOM_A_INTR1_LEVEL0, TOMTOM_A_INTR1_LEVEL0__POR },
+	{ TOMTOM_A_INTR1_LEVEL1, TOMTOM_A_INTR1_LEVEL1__POR },
+	{ TOMTOM_A_INTR1_LEVEL2, TOMTOM_A_INTR1_LEVEL2__POR },
+	{ TOMTOM_A_INTR1_LEVEL3, TOMTOM_A_INTR1_LEVEL3__POR },
+	{ TOMTOM_A_INTR1_TEST0, TOMTOM_A_INTR1_TEST0__POR },
+	{ TOMTOM_A_INTR1_TEST1, TOMTOM_A_INTR1_TEST1__POR },
+	{ TOMTOM_A_INTR1_TEST2, TOMTOM_A_INTR1_TEST2__POR },
+	{ TOMTOM_A_INTR1_TEST3, TOMTOM_A_INTR1_TEST3__POR },
+	{ TOMTOM_A_INTR1_SET0, TOMTOM_A_INTR1_SET0__POR },
+	{ TOMTOM_A_INTR1_SET1, TOMTOM_A_INTR1_SET1__POR },
+	{ TOMTOM_A_INTR1_SET2, TOMTOM_A_INTR1_SET2__POR },
+	{ TOMTOM_A_INTR1_SET3, TOMTOM_A_INTR1_SET3__POR },
+	{ TOMTOM_A_INTR2_MASK0, TOMTOM_A_INTR2_MASK0__POR },
+	{ TOMTOM_A_INTR2_STATUS0, TOMTOM_A_INTR2_STATUS0__POR },
+	{ TOMTOM_A_INTR2_CLEAR0, TOMTOM_A_INTR2_CLEAR0__POR },
+	{ TOMTOM_A_INTR2_LEVEL0, TOMTOM_A_INTR2_LEVEL0__POR },
+	{ TOMTOM_A_INTR2_TEST0, TOMTOM_A_INTR2_TEST0__POR },
+	{ TOMTOM_A_INTR2_SET0, TOMTOM_A_INTR2_SET0__POR },
+	{ TOMTOM_A_CDC_TX_I2S_SCK_MODE, TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR },
+	{ TOMTOM_A_CDC_TX_I2S_WS_MODE, TOMTOM_A_CDC_TX_I2S_WS_MODE__POR },
+	{ TOMTOM_A_CDC_DMIC_DATA0_MODE, TOMTOM_A_CDC_DMIC_DATA0_MODE__POR },
+	{ TOMTOM_A_CDC_DMIC_CLK0_MODE, TOMTOM_A_CDC_DMIC_CLK0_MODE__POR },
+	{ TOMTOM_A_CDC_DMIC_DATA1_MODE, TOMTOM_A_CDC_DMIC_DATA1_MODE__POR },
+	{ TOMTOM_A_CDC_DMIC_CLK1_MODE, TOMTOM_A_CDC_DMIC_CLK1_MODE__POR },
+	{ TOMTOM_A_CDC_RX_I2S_SCK_MODE, TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR },
+	{ TOMTOM_A_CDC_RX_I2S_WS_MODE, TOMTOM_A_CDC_RX_I2S_WS_MODE__POR },
+	{ TOMTOM_A_CDC_DMIC_DATA2_MODE, TOMTOM_A_CDC_DMIC_DATA2_MODE__POR },
+	{ TOMTOM_A_CDC_DMIC_CLK2_MODE, TOMTOM_A_CDC_DMIC_CLK2_MODE__POR },
+	{ TOMTOM_A_CDC_INTR1_MODE, TOMTOM_A_CDC_INTR1_MODE__POR },
+	{ TOMTOM_A_CDC_SB_NRZ_SEL_MODE, TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR },
+	{ TOMTOM_A_CDC_INTR2_MODE, TOMTOM_A_CDC_INTR2_MODE__POR },
+	{ TOMTOM_A_CDC_RF_PA_ON_MODE, TOMTOM_A_CDC_RF_PA_ON_MODE__POR },
+	{ TOMTOM_A_CDC_BOOST_MODE, TOMTOM_A_CDC_BOOST_MODE__POR },
+	{ TOMTOM_A_CDC_JTCK_MODE, TOMTOM_A_CDC_JTCK_MODE__POR },
+	{ TOMTOM_A_CDC_JTDI_MODE, TOMTOM_A_CDC_JTDI_MODE__POR },
+	{ TOMTOM_A_CDC_JTMS_MODE, TOMTOM_A_CDC_JTMS_MODE__POR },
+	{ TOMTOM_A_CDC_JTDO_MODE, TOMTOM_A_CDC_JTDO_MODE__POR },
+	{ TOMTOM_A_CDC_JTRST_MODE, TOMTOM_A_CDC_JTRST_MODE__POR },
+	{ TOMTOM_A_CDC_BIST_MODE_MODE, TOMTOM_A_CDC_BIST_MODE_MODE__POR },
+	{ TOMTOM_A_CDC_MAD_MAIN_CTL_1, TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR },
+	{ TOMTOM_A_CDC_MAD_MAIN_CTL_2, TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_1, TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_2, TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_3, TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_4, TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_5, TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_6, TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_7, TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_CTL_8, TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR,
+				TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR },
+	{ TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL,
+				TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_1, TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_2, TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_3, TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_4, TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_5, TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_6, TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR },
+	{ TOMTOM_A_CDC_MAD_ULTR_CTL_7, TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_1, TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_2, TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_3, TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_4, TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_5, TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_6, TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_7, TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_CTL_8, TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR,
+				TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR },
+	{ TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL,
+				TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR },
+	{ TOMTOM_A_CDC_MAD_INP_SEL, TOMTOM_A_CDC_MAD_INP_SEL__POR },
+	{ TOMTOM_A_BIAS_REF_CTL, TOMTOM_A_BIAS_REF_CTL__POR },
+	{ TOMTOM_A_BIAS_CENTRAL_BG_CTL, TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR },
+	{ TOMTOM_A_BIAS_PRECHRG_CTL, TOMTOM_A_BIAS_PRECHRG_CTL__POR },
+	{ TOMTOM_A_BIAS_CURR_CTL_1, TOMTOM_A_BIAS_CURR_CTL_1__POR },
+	{ TOMTOM_A_BIAS_CURR_CTL_2, TOMTOM_A_BIAS_CURR_CTL_2__POR },
+	{ TOMTOM_A_BIAS_OSC_BG_CTL, TOMTOM_A_BIAS_OSC_BG_CTL__POR },
+	{ TOMTOM_A_CLK_BUFF_EN1, TOMTOM_A_CLK_BUFF_EN1__POR },
+	{ TOMTOM_A_CLK_BUFF_EN2, TOMTOM_A_CLK_BUFF_EN2__POR },
+	{ TOMTOM_A_LDO_L_MODE_1, TOMTOM_A_LDO_L_MODE_1__POR },
+	{ TOMTOM_A_LDO_L_MODE_2, TOMTOM_A_LDO_L_MODE_2__POR },
+	{ TOMTOM_A_LDO_L_CTRL_1, TOMTOM_A_LDO_L_CTRL_1__POR },
+	{ TOMTOM_A_LDO_L_CTRL_2, TOMTOM_A_LDO_L_CTRL_2__POR },
+	{ TOMTOM_A_LDO_L_CTRL_3, TOMTOM_A_LDO_L_CTRL_3__POR },
+	{ TOMTOM_A_LDO_L_CTRL_4, TOMTOM_A_LDO_L_CTRL_4__POR },
+	{ TOMTOM_A_LDO_H_MODE_1, TOMTOM_A_LDO_H_MODE_1__POR },
+	{ TOMTOM_A_LDO_H_MODE_2, TOMTOM_A_LDO_H_MODE_2__POR },
+	{ TOMTOM_A_LDO_H_LOOP_CTL, TOMTOM_A_LDO_H_LOOP_CTL__POR },
+	{ TOMTOM_A_LDO_H_COMP_1, TOMTOM_A_LDO_H_COMP_1__POR },
+	{ TOMTOM_A_LDO_H_COMP_2, TOMTOM_A_LDO_H_COMP_2__POR },
+	{ TOMTOM_A_LDO_H_BIAS_1, TOMTOM_A_LDO_H_BIAS_1__POR },
+	{ TOMTOM_A_LDO_H_BIAS_2, TOMTOM_A_LDO_H_BIAS_2__POR },
+	{ TOMTOM_A_LDO_H_BIAS_3, TOMTOM_A_LDO_H_BIAS_3__POR },
+	{ TOMTOM_A_VBAT_CLK, TOMTOM_A_VBAT_CLK__POR },
+	{ TOMTOM_A_VBAT_LOOP, TOMTOM_A_VBAT_LOOP__POR },
+	{ TOMTOM_A_VBAT_REF, TOMTOM_A_VBAT_REF__POR },
+	{ TOMTOM_A_VBAT_ADC_TEST, TOMTOM_A_VBAT_ADC_TEST__POR },
+	{ TOMTOM_A_VBAT_FE, TOMTOM_A_VBAT_FE__POR },
+	{ TOMTOM_A_VBAT_BIAS_1, TOMTOM_A_VBAT_BIAS_1__POR },
+	{ TOMTOM_A_VBAT_BIAS_2, TOMTOM_A_VBAT_BIAS_2__POR },
+	{ TOMTOM_A_VBAT_ADC_DATA_MSB, TOMTOM_A_VBAT_ADC_DATA_MSB__POR },
+	{ TOMTOM_A_VBAT_ADC_DATA_LSB, TOMTOM_A_VBAT_ADC_DATA_LSB__POR },
+	{ TOMTOM_A_FLL_NREF, TOMTOM_A_FLL_NREF__POR },
+	{ TOMTOM_A_FLL_KDCO_TUNE, TOMTOM_A_FLL_KDCO_TUNE__POR },
+	{ TOMTOM_A_FLL_LOCK_THRESH, TOMTOM_A_FLL_LOCK_THRESH__POR },
+	{ TOMTOM_A_FLL_LOCK_DET_COUNT, TOMTOM_A_FLL_LOCK_DET_COUNT__POR },
+	{ TOMTOM_A_FLL_DAC_THRESHOLD, TOMTOM_A_FLL_DAC_THRESHOLD__POR },
+	{ TOMTOM_A_FLL_TEST_DCO_FREERUN, TOMTOM_A_FLL_TEST_DCO_FREERUN__POR },
+	{ TOMTOM_A_FLL_TEST_ENABLE, TOMTOM_A_FLL_TEST_ENABLE__POR },
+	{ TOMTOM_A_MICB_CFILT_1_CTL, TOMTOM_A_MICB_CFILT_1_CTL__POR },
+	{ TOMTOM_A_MICB_CFILT_1_VAL, TOMTOM_A_MICB_CFILT_1_VAL__POR },
+	{ TOMTOM_A_MICB_CFILT_1_PRECHRG, TOMTOM_A_MICB_CFILT_1_PRECHRG__POR },
+	{ TOMTOM_A_MICB_1_CTL, TOMTOM_A_MICB_1_CTL__POR },
+	{ TOMTOM_A_MICB_1_INT_RBIAS, TOMTOM_A_MICB_1_INT_RBIAS__POR },
+	{ TOMTOM_A_MICB_1_MBHC, TOMTOM_A_MICB_1_MBHC__POR },
+	{ TOMTOM_A_MICB_CFILT_2_CTL, TOMTOM_A_MICB_CFILT_2_CTL__POR },
+	{ TOMTOM_A_MICB_CFILT_2_VAL, TOMTOM_A_MICB_CFILT_2_VAL__POR },
+	{ TOMTOM_A_MICB_CFILT_2_PRECHRG, TOMTOM_A_MICB_CFILT_2_PRECHRG__POR },
+	{ TOMTOM_A_MICB_2_CTL, TOMTOM_A_MICB_2_CTL__POR },
+	{ TOMTOM_A_MICB_2_INT_RBIAS, TOMTOM_A_MICB_2_INT_RBIAS__POR },
+	{ TOMTOM_A_MICB_2_MBHC, TOMTOM_A_MICB_2_MBHC__POR },
+	{ TOMTOM_A_MICB_CFILT_3_CTL, TOMTOM_A_MICB_CFILT_3_CTL__POR },
+	{ TOMTOM_A_MICB_CFILT_3_VAL, TOMTOM_A_MICB_CFILT_3_VAL__POR },
+	{ TOMTOM_A_MICB_CFILT_3_PRECHRG, TOMTOM_A_MICB_CFILT_3_PRECHRG__POR },
+	{ TOMTOM_A_MICB_3_CTL, TOMTOM_A_MICB_3_CTL__POR },
+	{ TOMTOM_A_MICB_3_INT_RBIAS, TOMTOM_A_MICB_3_INT_RBIAS__POR },
+	{ TOMTOM_A_MICB_3_MBHC, TOMTOM_A_MICB_3_MBHC__POR },
+	{ TOMTOM_A_MICB_4_CTL, TOMTOM_A_MICB_4_CTL__POR },
+	{ TOMTOM_A_MICB_4_INT_RBIAS, TOMTOM_A_MICB_4_INT_RBIAS__POR },
+	{ TOMTOM_A_MICB_4_MBHC, TOMTOM_A_MICB_4_MBHC__POR },
+	{ TOMTOM_A_SPKR_DRV2_EN, TOMTOM_A_SPKR_DRV2_EN__POR },
+	{ TOMTOM_A_SPKR_DRV2_GAIN, TOMTOM_A_SPKR_DRV2_GAIN__POR },
+	{ TOMTOM_A_SPKR_DRV2_DAC_CTL, TOMTOM_A_SPKR_DRV2_DAC_CTL__POR },
+	{ TOMTOM_A_SPKR_DRV2_OCP_CTL, TOMTOM_A_SPKR_DRV2_OCP_CTL__POR },
+	{ TOMTOM_A_SPKR_DRV2_CLIP_DET, TOMTOM_A_SPKR_DRV2_CLIP_DET__POR },
+	{ TOMTOM_A_SPKR_DRV2_DBG_DAC, TOMTOM_A_SPKR_DRV2_DBG_DAC__POR },
+	{ TOMTOM_A_SPKR_DRV2_DBG_PA, TOMTOM_A_SPKR_DRV2_DBG_PA__POR },
+	{ TOMTOM_A_SPKR_DRV2_DBG_PWRSTG, TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR },
+	{ TOMTOM_A_SPKR_DRV2_BIAS_LDO, TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR },
+	{ TOMTOM_A_SPKR_DRV2_BIAS_INT, TOMTOM_A_SPKR_DRV2_BIAS_INT__POR },
+	{ TOMTOM_A_SPKR_DRV2_BIAS_PA, TOMTOM_A_SPKR_DRV2_BIAS_PA__POR },
+	{ TOMTOM_A_SPKR_DRV2_STATUS_OCP, TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR },
+	{ TOMTOM_A_SPKR_DRV2_STATUS_PA, TOMTOM_A_SPKR_DRV2_STATUS_PA__POR },
+	{ TOMTOM_A_MBHC_INSERT_DETECT, TOMTOM_A_MBHC_INSERT_DETECT__POR },
+	{ TOMTOM_A_MBHC_INSERT_DET_STATUS,
+				TOMTOM_A_MBHC_INSERT_DET_STATUS__POR },
+	{ TOMTOM_A_TX_COM_BIAS, TOMTOM_A_TX_COM_BIAS__POR },
+	{ TOMTOM_A_MBHC_INSERT_DETECT2, TOMTOM_A_MBHC_INSERT_DETECT2__POR },
+	{ TOMTOM_A_MBHC_SCALING_MUX_1, TOMTOM_A_MBHC_SCALING_MUX_1__POR },
+	{ TOMTOM_A_MBHC_SCALING_MUX_2, TOMTOM_A_MBHC_SCALING_MUX_2__POR },
+	{ TOMTOM_A_MAD_ANA_CTRL, TOMTOM_A_MAD_ANA_CTRL__POR },
+	{ TOMTOM_A_TX_SUP_SWITCH_CTRL_1, TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR },
+	{ TOMTOM_A_TX_SUP_SWITCH_CTRL_2, TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR },
+	{ TOMTOM_A_TX_1_GAIN, TOMTOM_A_TX_1_GAIN__POR },
+	{ TOMTOM_A_TX_1_2_TEST_EN, TOMTOM_A_TX_1_2_TEST_EN__POR },
+	{ TOMTOM_A_TX_2_GAIN, TOMTOM_A_TX_2_GAIN__POR },
+	{ TOMTOM_A_TX_1_2_ADC_IB, TOMTOM_A_TX_1_2_ADC_IB__POR },
+	{ TOMTOM_A_TX_1_2_ATEST_REFCTRL, TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR },
+	{ TOMTOM_A_TX_1_2_TEST_CTL, TOMTOM_A_TX_1_2_TEST_CTL__POR },
+	{ TOMTOM_A_TX_1_2_TEST_BLOCK_EN, TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR },
+	{ TOMTOM_A_TX_1_2_TXFE_CLKDIV, TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR },
+	{ TOMTOM_A_TX_1_2_SAR_ERR_CH1, TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR },
+	{ TOMTOM_A_TX_1_2_SAR_ERR_CH2, TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR },
+	{ TOMTOM_A_TX_3_GAIN, TOMTOM_A_TX_3_GAIN__POR },
+	{ TOMTOM_A_TX_3_4_TEST_EN, TOMTOM_A_TX_3_4_TEST_EN__POR },
+	{ TOMTOM_A_TX_4_GAIN, TOMTOM_A_TX_4_GAIN__POR },
+	{ TOMTOM_A_TX_3_4_ADC_IB, TOMTOM_A_TX_3_4_ADC_IB__POR },
+	{ TOMTOM_A_TX_3_4_ATEST_REFCTRL, TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR },
+	{ TOMTOM_A_TX_3_4_TEST_CTL, TOMTOM_A_TX_3_4_TEST_CTL__POR },
+	{ TOMTOM_A_TX_3_4_TEST_BLOCK_EN, TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR },
+	{ TOMTOM_A_TX_3_4_TXFE_CKDIV, TOMTOM_A_TX_3_4_TXFE_CKDIV__POR },
+	{ TOMTOM_A_TX_3_4_SAR_ERR_CH3, TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR },
+	{ TOMTOM_A_TX_3_4_SAR_ERR_CH4, TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR },
+	{ TOMTOM_A_TX_5_GAIN, TOMTOM_A_TX_5_GAIN__POR },
+	{ TOMTOM_A_TX_5_6_TEST_EN, TOMTOM_A_TX_5_6_TEST_EN__POR },
+	{ TOMTOM_A_TX_6_GAIN, TOMTOM_A_TX_6_GAIN__POR },
+	{ TOMTOM_A_TX_5_6_ADC_IB, TOMTOM_A_TX_5_6_ADC_IB__POR },
+	{ TOMTOM_A_TX_5_6_ATEST_REFCTRL, TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR },
+	{ TOMTOM_A_TX_5_6_TEST_CTL, TOMTOM_A_TX_5_6_TEST_CTL__POR },
+	{ TOMTOM_A_TX_5_6_TEST_BLOCK_EN, TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR },
+	{ TOMTOM_A_TX_5_6_TXFE_CKDIV, TOMTOM_A_TX_5_6_TXFE_CKDIV__POR },
+	{ TOMTOM_A_TX_5_6_SAR_ERR_CH5, TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR },
+	{ TOMTOM_A_TX_5_6_SAR_ERR_CH6, TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR },
+	{ TOMTOM_A_TX_7_MBHC_EN, TOMTOM_A_TX_7_MBHC_EN__POR },
+	{ TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL,
+				TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR },
+	{ TOMTOM_A_TX_7_MBHC_ADC, TOMTOM_A_TX_7_MBHC_ADC__POR },
+	{ TOMTOM_A_TX_7_MBHC_TEST_CTL, TOMTOM_A_TX_7_MBHC_TEST_CTL__POR },
+	{ TOMTOM_A_TX_7_MBHC_SAR_ERR, TOMTOM_A_TX_7_MBHC_SAR_ERR__POR },
+	{ TOMTOM_A_TX_7_TXFE_CLKDIV, TOMTOM_A_TX_7_TXFE_CLKDIV__POR },
+	{ TOMTOM_A_RCO_CTRL, TOMTOM_A_RCO_CTRL__POR },
+	{ TOMTOM_A_RCO_CALIBRATION_CTRL1, TOMTOM_A_RCO_CALIBRATION_CTRL1__POR },
+	{ TOMTOM_A_RCO_CALIBRATION_CTRL2, TOMTOM_A_RCO_CALIBRATION_CTRL2__POR },
+	{ TOMTOM_A_RCO_CALIBRATION_CTRL3, TOMTOM_A_RCO_CALIBRATION_CTRL3__POR },
+	{ TOMTOM_A_RCO_TEST_CTRL, TOMTOM_A_RCO_TEST_CTRL__POR },
+	{ TOMTOM_A_RCO_CALIBRATION_RESULT1,
+				TOMTOM_A_RCO_CALIBRATION_RESULT1__POR },
+	{ TOMTOM_A_RCO_CALIBRATION_RESULT2,
+				TOMTOM_A_RCO_CALIBRATION_RESULT2__POR },
+	{ TOMTOM_A_BUCK_MODE_1, TOMTOM_A_BUCK_MODE_1__POR },
+	{ TOMTOM_A_BUCK_MODE_2, TOMTOM_A_BUCK_MODE_2__POR },
+	{ TOMTOM_A_BUCK_MODE_3, TOMTOM_A_BUCK_MODE_3__POR },
+	{ TOMTOM_A_BUCK_MODE_4, TOMTOM_A_BUCK_MODE_4__POR },
+	{ TOMTOM_A_BUCK_MODE_5, TOMTOM_A_BUCK_MODE_5__POR },
+	{ TOMTOM_A_BUCK_CTRL_VCL_1, TOMTOM_A_BUCK_CTRL_VCL_1__POR },
+	{ TOMTOM_A_BUCK_CTRL_VCL_2, TOMTOM_A_BUCK_CTRL_VCL_2__POR },
+	{ TOMTOM_A_BUCK_CTRL_VCL_3, TOMTOM_A_BUCK_CTRL_VCL_3__POR },
+	{ TOMTOM_A_BUCK_CTRL_CCL_1, TOMTOM_A_BUCK_CTRL_CCL_1__POR },
+	{ TOMTOM_A_BUCK_CTRL_CCL_2, TOMTOM_A_BUCK_CTRL_CCL_2__POR },
+	{ TOMTOM_A_BUCK_CTRL_CCL_3, TOMTOM_A_BUCK_CTRL_CCL_3__POR },
+	{ TOMTOM_A_BUCK_CTRL_CCL_4, TOMTOM_A_BUCK_CTRL_CCL_4__POR },
+	{ TOMTOM_A_BUCK_CTRL_PWM_DRVR_1, TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR },
+	{ TOMTOM_A_BUCK_CTRL_PWM_DRVR_2, TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR },
+	{ TOMTOM_A_BUCK_CTRL_PWM_DRVR_3, TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR },
+	{ TOMTOM_A_BUCK_TMUX_A_D, TOMTOM_A_BUCK_TMUX_A_D__POR },
+	{ TOMTOM_A_NCP_BUCKREF, TOMTOM_A_NCP_BUCKREF__POR },
+	{ TOMTOM_A_NCP_EN, TOMTOM_A_NCP_EN__POR },
+	{ TOMTOM_A_NCP_CLK, TOMTOM_A_NCP_CLK__POR },
+	{ TOMTOM_A_NCP_STATIC, TOMTOM_A_NCP_STATIC__POR },
+	{ TOMTOM_A_NCP_VTH_LOW, TOMTOM_A_NCP_VTH_LOW__POR },
+	{ TOMTOM_A_NCP_VTH_HIGH, TOMTOM_A_NCP_VTH_HIGH__POR },
+	{ TOMTOM_A_NCP_ATEST, TOMTOM_A_NCP_ATEST__POR },
+	{ TOMTOM_A_NCP_DTEST, TOMTOM_A_NCP_DTEST__POR },
+	{ TOMTOM_A_NCP_DLY1, TOMTOM_A_NCP_DLY1__POR },
+	{ TOMTOM_A_NCP_DLY2, TOMTOM_A_NCP_DLY2__POR },
+	{ TOMTOM_A_RX_AUX_SW_CTL, TOMTOM_A_RX_AUX_SW_CTL__POR },
+	{ TOMTOM_A_RX_PA_AUX_IN_CONN, TOMTOM_A_RX_PA_AUX_IN_CONN__POR },
+	{ TOMTOM_A_RX_COM_TIMER_DIV, TOMTOM_A_RX_COM_TIMER_DIV__POR },
+	{ TOMTOM_A_RX_COM_OCP_CTL, TOMTOM_A_RX_COM_OCP_CTL__POR },
+	{ TOMTOM_A_RX_COM_OCP_COUNT, TOMTOM_A_RX_COM_OCP_COUNT__POR },
+	{ TOMTOM_A_RX_COM_DAC_CTL, TOMTOM_A_RX_COM_DAC_CTL__POR },
+	{ TOMTOM_A_RX_COM_BIAS, TOMTOM_A_RX_COM_BIAS__POR },
+	{ TOMTOM_A_RX_HPH_AUTO_CHOP, TOMTOM_A_RX_HPH_AUTO_CHOP__POR },
+	{ TOMTOM_A_RX_HPH_CHOP_CTL, TOMTOM_A_RX_HPH_CHOP_CTL__POR },
+	{ TOMTOM_A_RX_HPH_BIAS_PA, TOMTOM_A_RX_HPH_BIAS_PA__POR },
+	{ TOMTOM_A_RX_HPH_BIAS_LDO, TOMTOM_A_RX_HPH_BIAS_LDO__POR },
+	{ TOMTOM_A_RX_HPH_BIAS_CNP, TOMTOM_A_RX_HPH_BIAS_CNP__POR },
+	{ TOMTOM_A_RX_HPH_BIAS_WG_OCP, TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR },
+	{ TOMTOM_A_RX_HPH_OCP_CTL, TOMTOM_A_RX_HPH_OCP_CTL__POR },
+	{ TOMTOM_A_RX_HPH_CNP_EN, TOMTOM_A_RX_HPH_CNP_EN__POR },
+	{ TOMTOM_A_RX_HPH_CNP_WG_CTL, TOMTOM_A_RX_HPH_CNP_WG_CTL__POR },
+	{ TOMTOM_A_RX_HPH_CNP_WG_TIME, TOMTOM_A_RX_HPH_CNP_WG_TIME__POR },
+	{ TOMTOM_A_RX_HPH_L_GAIN, TOMTOM_A_RX_HPH_L_GAIN__POR },
+	{ TOMTOM_A_RX_HPH_L_TEST, TOMTOM_A_RX_HPH_L_TEST__POR },
+	{ TOMTOM_A_RX_HPH_L_PA_CTL, TOMTOM_A_RX_HPH_L_PA_CTL__POR },
+	{ TOMTOM_A_RX_HPH_L_DAC_CTL, TOMTOM_A_RX_HPH_L_DAC_CTL__POR },
+	{ TOMTOM_A_RX_HPH_L_ATEST, TOMTOM_A_RX_HPH_L_ATEST__POR },
+	{ TOMTOM_A_RX_HPH_L_STATUS, TOMTOM_A_RX_HPH_L_STATUS__POR },
+	{ TOMTOM_A_RX_HPH_R_GAIN, TOMTOM_A_RX_HPH_R_GAIN__POR },
+	{ TOMTOM_A_RX_HPH_R_TEST, TOMTOM_A_RX_HPH_R_TEST__POR },
+	{ TOMTOM_A_RX_HPH_R_PA_CTL, TOMTOM_A_RX_HPH_R_PA_CTL__POR },
+	{ TOMTOM_A_RX_HPH_R_DAC_CTL, TOMTOM_A_RX_HPH_R_DAC_CTL__POR },
+	{ TOMTOM_A_RX_HPH_R_ATEST, TOMTOM_A_RX_HPH_R_ATEST__POR },
+	{ TOMTOM_A_RX_HPH_R_STATUS, TOMTOM_A_RX_HPH_R_STATUS__POR },
+	{ TOMTOM_A_RX_EAR_BIAS_PA, TOMTOM_A_RX_EAR_BIAS_PA__POR },
+	{ TOMTOM_A_RX_EAR_BIAS_CMBUFF, TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR },
+	{ TOMTOM_A_RX_EAR_EN, TOMTOM_A_RX_EAR_EN__POR },
+	{ TOMTOM_A_RX_EAR_GAIN, TOMTOM_A_RX_EAR_GAIN__POR },
+	{ TOMTOM_A_RX_EAR_CMBUFF, TOMTOM_A_RX_EAR_CMBUFF__POR },
+	{ TOMTOM_A_RX_EAR_ICTL, TOMTOM_A_RX_EAR_ICTL__POR },
+	{ TOMTOM_A_RX_EAR_CCOMP, TOMTOM_A_RX_EAR_CCOMP__POR },
+	{ TOMTOM_A_RX_EAR_VCM, TOMTOM_A_RX_EAR_VCM__POR },
+	{ TOMTOM_A_RX_EAR_CNP, TOMTOM_A_RX_EAR_CNP__POR },
+	{ TOMTOM_A_RX_EAR_DAC_CTL_ATEST, TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR },
+	{ TOMTOM_A_RX_EAR_STATUS, TOMTOM_A_RX_EAR_STATUS__POR },
+	{ TOMTOM_A_RX_LINE_BIAS_PA, TOMTOM_A_RX_LINE_BIAS_PA__POR },
+	{ TOMTOM_A_RX_BUCK_BIAS1, TOMTOM_A_RX_BUCK_BIAS1__POR },
+	{ TOMTOM_A_RX_BUCK_BIAS2, TOMTOM_A_RX_BUCK_BIAS2__POR },
+	{ TOMTOM_A_RX_LINE_COM, TOMTOM_A_RX_LINE_COM__POR },
+	{ TOMTOM_A_RX_LINE_CNP_EN, TOMTOM_A_RX_LINE_CNP_EN__POR },
+	{ TOMTOM_A_RX_LINE_CNP_WG_CTL, TOMTOM_A_RX_LINE_CNP_WG_CTL__POR },
+	{ TOMTOM_A_RX_LINE_CNP_WG_TIME, TOMTOM_A_RX_LINE_CNP_WG_TIME__POR },
+	{ TOMTOM_A_RX_LINE_1_GAIN, TOMTOM_A_RX_LINE_1_GAIN__POR },
+	{ TOMTOM_A_RX_LINE_1_TEST, TOMTOM_A_RX_LINE_1_TEST__POR },
+	{ TOMTOM_A_RX_LINE_1_DAC_CTL, TOMTOM_A_RX_LINE_1_DAC_CTL__POR },
+	{ TOMTOM_A_RX_LINE_1_STATUS, TOMTOM_A_RX_LINE_1_STATUS__POR },
+	{ TOMTOM_A_RX_LINE_2_GAIN, TOMTOM_A_RX_LINE_2_GAIN__POR },
+	{ TOMTOM_A_RX_LINE_2_TEST, TOMTOM_A_RX_LINE_2_TEST__POR },
+	{ TOMTOM_A_RX_LINE_2_DAC_CTL, TOMTOM_A_RX_LINE_2_DAC_CTL__POR },
+	{ TOMTOM_A_RX_LINE_2_STATUS, TOMTOM_A_RX_LINE_2_STATUS__POR },
+	{ TOMTOM_A_RX_LINE_3_GAIN, TOMTOM_A_RX_LINE_3_GAIN__POR },
+	{ TOMTOM_A_RX_LINE_3_TEST, TOMTOM_A_RX_LINE_3_TEST__POR },
+	{ TOMTOM_A_RX_LINE_3_DAC_CTL, TOMTOM_A_RX_LINE_3_DAC_CTL__POR },
+	{ TOMTOM_A_RX_LINE_3_STATUS, TOMTOM_A_RX_LINE_3_STATUS__POR },
+	{ TOMTOM_A_RX_LINE_4_GAIN, TOMTOM_A_RX_LINE_4_GAIN__POR },
+	{ TOMTOM_A_RX_LINE_4_TEST, TOMTOM_A_RX_LINE_4_TEST__POR },
+	{ TOMTOM_A_RX_LINE_4_DAC_CTL, TOMTOM_A_RX_LINE_4_DAC_CTL__POR },
+	{ TOMTOM_A_RX_LINE_4_STATUS, TOMTOM_A_RX_LINE_4_STATUS__POR },
+	{ TOMTOM_A_RX_LINE_CNP_DBG, TOMTOM_A_RX_LINE_CNP_DBG__POR },
+	{ TOMTOM_A_SPKR_DRV1_EN, TOMTOM_A_SPKR_DRV1_EN__POR },
+	{ TOMTOM_A_SPKR_DRV1_GAIN, TOMTOM_A_SPKR_DRV1_GAIN__POR },
+	{ TOMTOM_A_SPKR_DRV1_DAC_CTL, TOMTOM_A_SPKR_DRV1_DAC_CTL__POR },
+	{ TOMTOM_A_SPKR_DRV1_OCP_CTL, TOMTOM_A_SPKR_DRV1_OCP_CTL__POR },
+	{ TOMTOM_A_SPKR_DRV1_CLIP_DET, TOMTOM_A_SPKR_DRV1_CLIP_DET__POR },
+	{ TOMTOM_A_SPKR_DRV1_IEC, TOMTOM_A_SPKR_DRV1_IEC__POR },
+	{ TOMTOM_A_SPKR_DRV1_DBG_DAC, TOMTOM_A_SPKR_DRV1_DBG_DAC__POR },
+	{ TOMTOM_A_SPKR_DRV1_DBG_PA, TOMTOM_A_SPKR_DRV1_DBG_PA__POR },
+	{ TOMTOM_A_SPKR_DRV1_DBG_PWRSTG, TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR },
+	{ TOMTOM_A_SPKR_DRV1_BIAS_LDO, TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR },
+	{ TOMTOM_A_SPKR_DRV1_BIAS_INT, TOMTOM_A_SPKR_DRV1_BIAS_INT__POR },
+	{ TOMTOM_A_SPKR_DRV1_BIAS_PA, TOMTOM_A_SPKR_DRV1_BIAS_PA__POR },
+	{ TOMTOM_A_SPKR_DRV1_STATUS_OCP, TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR },
+	{ TOMTOM_A_SPKR_DRV1_STATUS_PA, TOMTOM_A_SPKR_DRV1_STATUS_PA__POR },
+	{ TOMTOM_A_SPKR1_PROT_EN, TOMTOM_A_SPKR1_PROT_EN__POR },
+	{ TOMTOM_A_SPKR1_PROT_ADC_TEST_EN,
+				TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR },
+	{ TOMTOM_A_SPKR1_PROT_ATEST, TOMTOM_A_SPKR1_PROT_ATEST__POR },
+	{ TOMTOM_A_SPKR1_PROT_LDO_CTRL, TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR },
+	{ TOMTOM_A_SPKR1_PROT_ISENSE_CTRL,
+				TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR },
+	{ TOMTOM_A_SPKR1_PROT_VSENSE_CTRL,
+				TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR },
+	{ TOMTOM_A_SPKR2_PROT_EN, TOMTOM_A_SPKR2_PROT_EN__POR },
+	{ TOMTOM_A_SPKR2_PROT_ADC_TEST_EN,
+				TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR },
+	{ TOMTOM_A_SPKR2_PROT_ATEST, TOMTOM_A_SPKR2_PROT_ATEST__POR },
+	{ TOMTOM_A_SPKR2_PROT_LDO_CTRL, TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR },
+	{ TOMTOM_A_SPKR2_PROT_ISENSE_CTRL,
+				TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR },
+	{ TOMTOM_A_SPKR2_PROT_VSENSE_CTRL,
+				TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR },
+	{ TOMTOM_A_MBHC_HPH, TOMTOM_A_MBHC_HPH__POR },
+	{ TOMTOM_A_CDC_ANC1_B1_CTL, TOMTOM_A_CDC_ANC1_B1_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_B1_CTL, TOMTOM_A_CDC_ANC2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_SHIFT, TOMTOM_A_CDC_ANC1_SHIFT__POR },
+	{ TOMTOM_A_CDC_ANC2_SHIFT, TOMTOM_A_CDC_ANC2_SHIFT__POR },
+	{ TOMTOM_A_CDC_ANC1_IIR_B1_CTL, TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_IIR_B1_CTL, TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_IIR_B2_CTL, TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_IIR_B2_CTL, TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_IIR_B3_CTL, TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_IIR_B3_CTL, TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_LPF_B1_CTL, TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_LPF_B1_CTL, TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_LPF_B2_CTL, TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_LPF_B2_CTL, TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_SPARE, TOMTOM_A_CDC_ANC1_SPARE__POR },
+	{ TOMTOM_A_CDC_ANC2_SPARE, TOMTOM_A_CDC_ANC2_SPARE__POR },
+	{ TOMTOM_A_CDC_ANC1_SMLPF_CTL, TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_SMLPF_CTL, TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_DCFLT_CTL, TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_DCFLT_CTL, TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_GAIN_CTL, TOMTOM_A_CDC_ANC1_GAIN_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_GAIN_CTL, TOMTOM_A_CDC_ANC2_GAIN_CTL__POR },
+	{ TOMTOM_A_CDC_ANC1_B2_CTL, TOMTOM_A_CDC_ANC1_B2_CTL__POR },
+	{ TOMTOM_A_CDC_ANC2_B2_CTL, TOMTOM_A_CDC_ANC2_B2_CTL__POR },
+	{ TOMTOM_A_CDC_TX1_VOL_CTL_TIMER, TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX2_VOL_CTL_TIMER, TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX3_VOL_CTL_TIMER, TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX4_VOL_CTL_TIMER, TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX5_VOL_CTL_TIMER, TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX6_VOL_CTL_TIMER, TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX7_VOL_CTL_TIMER, TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX8_VOL_CTL_TIMER, TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX9_VOL_CTL_TIMER, TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX10_VOL_CTL_TIMER,
+				TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR },
+	{ TOMTOM_A_CDC_TX1_VOL_CTL_GAIN, TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX2_VOL_CTL_GAIN, TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX3_VOL_CTL_GAIN, TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX4_VOL_CTL_GAIN, TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX5_VOL_CTL_GAIN, TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX6_VOL_CTL_GAIN, TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX7_VOL_CTL_GAIN, TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX8_VOL_CTL_GAIN, TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX9_VOL_CTL_GAIN, TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX10_VOL_CTL_GAIN, TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR },
+	{ TOMTOM_A_CDC_TX1_VOL_CTL_CFG, TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX2_VOL_CTL_CFG, TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX3_VOL_CTL_CFG, TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX4_VOL_CTL_CFG, TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX5_VOL_CTL_CFG, TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX6_VOL_CTL_CFG, TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX7_VOL_CTL_CFG, TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX8_VOL_CTL_CFG, TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX9_VOL_CTL_CFG, TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX10_VOL_CTL_CFG, TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR },
+	{ TOMTOM_A_CDC_TX1_MUX_CTL, TOMTOM_A_CDC_TX1_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX2_MUX_CTL, TOMTOM_A_CDC_TX2_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX3_MUX_CTL, TOMTOM_A_CDC_TX3_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX4_MUX_CTL, TOMTOM_A_CDC_TX4_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX5_MUX_CTL, TOMTOM_A_CDC_TX5_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX6_MUX_CTL, TOMTOM_A_CDC_TX6_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX7_MUX_CTL, TOMTOM_A_CDC_TX7_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX8_MUX_CTL, TOMTOM_A_CDC_TX8_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX9_MUX_CTL, TOMTOM_A_CDC_TX9_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX10_MUX_CTL, TOMTOM_A_CDC_TX10_MUX_CTL__POR },
+	{ TOMTOM_A_CDC_TX1_CLK_FS_CTL, TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX2_CLK_FS_CTL, TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX3_CLK_FS_CTL, TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX4_CLK_FS_CTL, TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX5_CLK_FS_CTL, TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX6_CLK_FS_CTL, TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX7_CLK_FS_CTL, TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX8_CLK_FS_CTL, TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX9_CLK_FS_CTL, TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX10_CLK_FS_CTL, TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR },
+	{ TOMTOM_A_CDC_TX1_DMIC_CTL, TOMTOM_A_CDC_TX1_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX2_DMIC_CTL, TOMTOM_A_CDC_TX2_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX3_DMIC_CTL, TOMTOM_A_CDC_TX3_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX4_DMIC_CTL, TOMTOM_A_CDC_TX4_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX5_DMIC_CTL, TOMTOM_A_CDC_TX5_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX6_DMIC_CTL, TOMTOM_A_CDC_TX6_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX7_DMIC_CTL, TOMTOM_A_CDC_TX7_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX8_DMIC_CTL, TOMTOM_A_CDC_TX8_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX9_DMIC_CTL, TOMTOM_A_CDC_TX9_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_TX10_DMIC_CTL, TOMTOM_A_CDC_TX10_DMIC_CTL__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL0, TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL1, TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL2, TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL3, TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL4, TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL5, TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL6, TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_VAL7, TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR },
+	{ TOMTOM_A_CDC_DEBUG_B1_CTL, TOMTOM_A_CDC_DEBUG_B1_CTL__POR },
+	{ TOMTOM_A_CDC_DEBUG_B2_CTL, TOMTOM_A_CDC_DEBUG_B2_CTL__POR },
+	{ TOMTOM_A_CDC_DEBUG_B3_CTL, TOMTOM_A_CDC_DEBUG_B3_CTL__POR },
+	{ TOMTOM_A_CDC_DEBUG_B4_CTL, TOMTOM_A_CDC_DEBUG_B4_CTL__POR },
+	{ TOMTOM_A_CDC_DEBUG_B5_CTL, TOMTOM_A_CDC_DEBUG_B5_CTL__POR },
+	{ TOMTOM_A_CDC_DEBUG_B6_CTL, TOMTOM_A_CDC_DEBUG_B6_CTL__POR },
+	{ TOMTOM_A_CDC_DEBUG_B7_CTL, TOMTOM_A_CDC_DEBUG_B7_CTL__POR },
+	{ TOMTOM_A_CDC_SRC1_PDA_CFG, TOMTOM_A_CDC_SRC1_PDA_CFG__POR },
+	{ TOMTOM_A_CDC_SRC2_PDA_CFG, TOMTOM_A_CDC_SRC2_PDA_CFG__POR },
+	{ TOMTOM_A_CDC_SRC1_FS_CTL, TOMTOM_A_CDC_SRC1_FS_CTL__POR },
+	{ TOMTOM_A_CDC_SRC2_FS_CTL, TOMTOM_A_CDC_SRC2_FS_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_B1_CTL, TOMTOM_A_CDC_RX1_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_B1_CTL, TOMTOM_A_CDC_RX2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_B1_CTL, TOMTOM_A_CDC_RX3_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_B1_CTL, TOMTOM_A_CDC_RX4_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_B1_CTL, TOMTOM_A_CDC_RX5_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_B1_CTL, TOMTOM_A_CDC_RX6_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_B1_CTL, TOMTOM_A_CDC_RX7_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_B2_CTL, TOMTOM_A_CDC_RX1_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_B2_CTL, TOMTOM_A_CDC_RX2_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_B2_CTL, TOMTOM_A_CDC_RX3_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_B2_CTL, TOMTOM_A_CDC_RX4_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_B2_CTL, TOMTOM_A_CDC_RX5_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_B2_CTL, TOMTOM_A_CDC_RX6_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_B2_CTL, TOMTOM_A_CDC_RX7_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_B3_CTL, TOMTOM_A_CDC_RX1_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_B3_CTL, TOMTOM_A_CDC_RX2_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_B3_CTL, TOMTOM_A_CDC_RX3_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_B3_CTL, TOMTOM_A_CDC_RX4_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_B3_CTL, TOMTOM_A_CDC_RX5_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_B3_CTL, TOMTOM_A_CDC_RX6_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_B3_CTL, TOMTOM_A_CDC_RX7_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_B4_CTL, TOMTOM_A_CDC_RX1_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_B4_CTL, TOMTOM_A_CDC_RX2_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_B4_CTL, TOMTOM_A_CDC_RX3_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_B4_CTL, TOMTOM_A_CDC_RX4_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_B4_CTL, TOMTOM_A_CDC_RX5_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_B4_CTL, TOMTOM_A_CDC_RX6_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_B4_CTL, TOMTOM_A_CDC_RX7_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_B5_CTL, TOMTOM_A_CDC_RX1_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_B5_CTL, TOMTOM_A_CDC_RX2_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_B5_CTL, TOMTOM_A_CDC_RX3_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_B5_CTL, TOMTOM_A_CDC_RX4_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_B5_CTL, TOMTOM_A_CDC_RX5_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_B5_CTL, TOMTOM_A_CDC_RX6_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_B5_CTL, TOMTOM_A_CDC_RX7_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_B6_CTL, TOMTOM_A_CDC_RX1_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_B6_CTL, TOMTOM_A_CDC_RX2_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_B6_CTL, TOMTOM_A_CDC_RX3_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_B6_CTL, TOMTOM_A_CDC_RX4_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_B6_CTL, TOMTOM_A_CDC_RX5_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_B6_CTL, TOMTOM_A_CDC_RX6_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_B6_CTL, TOMTOM_A_CDC_RX7_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_VBAT_CFG, TOMTOM_A_CDC_VBAT_CFG__POR },
+	{ TOMTOM_A_CDC_VBAT_ADC_CAL1, TOMTOM_A_CDC_VBAT_ADC_CAL1__POR },
+	{ TOMTOM_A_CDC_VBAT_ADC_CAL2, TOMTOM_A_CDC_VBAT_ADC_CAL2__POR },
+	{ TOMTOM_A_CDC_VBAT_ADC_CAL3, TOMTOM_A_CDC_VBAT_ADC_CAL3__POR },
+	{ TOMTOM_A_CDC_VBAT_PK_EST1, TOMTOM_A_CDC_VBAT_PK_EST1__POR },
+	{ TOMTOM_A_CDC_VBAT_PK_EST2, TOMTOM_A_CDC_VBAT_PK_EST2__POR },
+	{ TOMTOM_A_CDC_VBAT_PK_EST3, TOMTOM_A_CDC_VBAT_PK_EST3__POR },
+	{ TOMTOM_A_CDC_VBAT_RF_PROC1, TOMTOM_A_CDC_VBAT_RF_PROC1__POR },
+	{ TOMTOM_A_CDC_VBAT_RF_PROC2, TOMTOM_A_CDC_VBAT_RF_PROC2__POR },
+	{ TOMTOM_A_CDC_VBAT_TAC1, TOMTOM_A_CDC_VBAT_TAC1__POR },
+	{ TOMTOM_A_CDC_VBAT_TAC2, TOMTOM_A_CDC_VBAT_TAC2__POR },
+	{ TOMTOM_A_CDC_VBAT_TAC3, TOMTOM_A_CDC_VBAT_TAC3__POR },
+	{ TOMTOM_A_CDC_VBAT_TAC4, TOMTOM_A_CDC_VBAT_TAC4__POR },
+	{ TOMTOM_A_CDC_VBAT_GAIN_UPD1, TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR },
+	{ TOMTOM_A_CDC_VBAT_GAIN_UPD2, TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR },
+	{ TOMTOM_A_CDC_VBAT_GAIN_UPD3, TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR },
+	{ TOMTOM_A_CDC_VBAT_GAIN_UPD4, TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR },
+	{ TOMTOM_A_CDC_VBAT_DEBUG1, TOMTOM_A_CDC_VBAT_DEBUG1__POR },
+	{ TOMTOM_A_CDC_VBAT_GAIN_UPD_MON, TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR },
+	{ TOMTOM_A_CDC_VBAT_GAIN_MON_VAL, TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR },
+	{ TOMTOM_A_CDC_CLK_ANC_RESET_CTL, TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_RX_RESET_CTL, TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL,
+				TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL,
+				TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_RX_I2S_CTL, TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_TX_I2S_CTL, TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL,
+				TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL,
+				TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL,
+				TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_OTHR_CTL, TOMTOM_A_CDC_CLK_OTHR_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL,
+				TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_RX_B1_CTL, TOMTOM_A_CDC_CLK_RX_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_RX_B2_CTL, TOMTOM_A_CDC_CLK_RX_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_MCLK_CTL, TOMTOM_A_CDC_CLK_MCLK_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_PDM_CTL, TOMTOM_A_CDC_CLK_PDM_CTL__POR },
+	{ TOMTOM_A_CDC_CLK_SD_CTL, TOMTOM_A_CDC_CLK_SD_CTL__POR },
+	{ TOMTOM_A_CDC_CLSH_B1_CTL, TOMTOM_A_CDC_CLSH_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLSH_B2_CTL, TOMTOM_A_CDC_CLSH_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CLSH_B3_CTL, TOMTOM_A_CDC_CLSH_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS,
+				TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR },
+	{ TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD,
+				TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR },
+	{ TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD,
+				TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR },
+	{ TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD,
+				TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR },
+	{ TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD,
+				TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR },
+	{ TOMTOM_A_CDC_CLSH_K_ADDR, TOMTOM_A_CDC_CLSH_K_ADDR__POR },
+	{ TOMTOM_A_CDC_CLSH_K_DATA, TOMTOM_A_CDC_CLSH_K_DATA__POR },
+	{ TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L,
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR },
+	{ TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U,
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR },
+	{ TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L,
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR },
+	{ TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U,
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR },
+	{ TOMTOM_A_CDC_CLSH_V_PA_HD_EAR, TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR },
+	{ TOMTOM_A_CDC_CLSH_V_PA_HD_HPH, TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR },
+	{ TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR, TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR },
+	{ TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH, TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B1_CTL, TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B1_CTL, TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B2_CTL, TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B2_CTL, TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B3_CTL, TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B3_CTL, TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B4_CTL, TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B4_CTL, TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B5_CTL, TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B5_CTL, TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B6_CTL, TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B6_CTL, TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B7_CTL, TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B7_CTL, TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_B8_CTL, TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_B8_CTL, TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_CTL, TOMTOM_A_CDC_IIR1_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_CTL, TOMTOM_A_CDC_IIR2_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL,
+				TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL,
+				TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_COEF_B1_CTL, TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_COEF_B1_CTL, TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR },
+	{ TOMTOM_A_CDC_IIR1_COEF_B2_CTL, TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR },
+	{ TOMTOM_A_CDC_IIR2_COEF_B2_CTL, TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR },
+	{ TOMTOM_A_CDC_TOP_GAIN_UPDATE, TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR },
+	{ TOMTOM_A_CDC_PA_RAMP_B1_CTL, TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR },
+	{ TOMTOM_A_CDC_PA_RAMP_B2_CTL, TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR },
+	{ TOMTOM_A_CDC_PA_RAMP_B3_CTL, TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR },
+	{ TOMTOM_A_CDC_PA_RAMP_B4_CTL, TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR },
+	{ TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL,
+				TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_B1_CTL, TOMTOM_A_CDC_COMP0_B1_CTL__POR },
+	{ TOMTOM_A_CDC_COMP1_B1_CTL, TOMTOM_A_CDC_COMP1_B1_CTL__POR },
+	{ TOMTOM_A_CDC_COMP2_B1_CTL, TOMTOM_A_CDC_COMP2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_B2_CTL, TOMTOM_A_CDC_COMP0_B2_CTL__POR },
+	{ TOMTOM_A_CDC_COMP1_B2_CTL, TOMTOM_A_CDC_COMP1_B2_CTL__POR },
+	{ TOMTOM_A_CDC_COMP2_B2_CTL, TOMTOM_A_CDC_COMP2_B2_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_B3_CTL, TOMTOM_A_CDC_COMP0_B3_CTL__POR },
+	{ TOMTOM_A_CDC_COMP1_B3_CTL, TOMTOM_A_CDC_COMP1_B3_CTL__POR },
+	{ TOMTOM_A_CDC_COMP2_B3_CTL, TOMTOM_A_CDC_COMP2_B3_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_B4_CTL, TOMTOM_A_CDC_COMP0_B4_CTL__POR },
+	{ TOMTOM_A_CDC_COMP1_B4_CTL, TOMTOM_A_CDC_COMP1_B4_CTL__POR },
+	{ TOMTOM_A_CDC_COMP2_B4_CTL, TOMTOM_A_CDC_COMP2_B4_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_B5_CTL, TOMTOM_A_CDC_COMP0_B5_CTL__POR },
+	{ TOMTOM_A_CDC_COMP1_B5_CTL, TOMTOM_A_CDC_COMP1_B5_CTL__POR },
+	{ TOMTOM_A_CDC_COMP2_B5_CTL, TOMTOM_A_CDC_COMP2_B5_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_B6_CTL, TOMTOM_A_CDC_COMP0_B6_CTL__POR },
+	{ TOMTOM_A_CDC_COMP1_B6_CTL, TOMTOM_A_CDC_COMP1_B6_CTL__POR },
+	{ TOMTOM_A_CDC_COMP2_B6_CTL, TOMTOM_A_CDC_COMP2_B6_CTL__POR },
+	{ TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS,
+				TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR },
+	{ TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS,
+				TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR },
+	{ TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS,
+				TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR },
+	{ TOMTOM_A_CDC_COMP0_FS_CFG, TOMTOM_A_CDC_COMP0_FS_CFG__POR },
+	{ TOMTOM_A_CDC_COMP1_FS_CFG, TOMTOM_A_CDC_COMP1_FS_CFG__POR },
+	{ TOMTOM_A_CDC_COMP2_FS_CFG, TOMTOM_A_CDC_COMP2_FS_CFG__POR },
+	{ TOMTOM_A_CDC_CONN_RX1_B1_CTL, TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX1_B2_CTL, TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX1_B3_CTL, TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX2_B1_CTL, TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX2_B2_CTL, TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX2_B3_CTL, TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX3_B1_CTL, TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX3_B2_CTL, TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX4_B1_CTL, TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX4_B2_CTL, TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX5_B1_CTL, TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX5_B2_CTL, TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX6_B1_CTL, TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX6_B2_CTL, TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX7_B1_CTL, TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX7_B2_CTL, TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX7_B3_CTL, TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_ANC_B1_CTL, TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_ANC_B2_CTL, TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_B1_CTL, TOMTOM_A_CDC_CONN_TX_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_B2_CTL, TOMTOM_A_CDC_CONN_TX_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_B3_CTL, TOMTOM_A_CDC_CONN_TX_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_B4_CTL, TOMTOM_A_CDC_CONN_TX_B4_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ1_B1_CTL, TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ1_B2_CTL, TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ1_B3_CTL, TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ1_B4_CTL, TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ2_B1_CTL, TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ2_B2_CTL, TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ2_B3_CTL, TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_EQ2_B4_CTL, TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_SRC1_B1_CTL, TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_SRC1_B2_CTL, TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_SRC2_B1_CTL, TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_SRC2_B2_CTL, TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B10_CTL,
+				TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_TX_SB_B11_CTL,
+				TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX_SB_B1_CTL, TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_RX_SB_B2_CTL, TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_CLSH_CTL, TOMTOM_A_CDC_CONN_CLSH_CTL__POR },
+	{ TOMTOM_A_CDC_CONN_MISC, TOMTOM_A_CDC_CONN_MISC__POR },
+	{ TOMTOM_A_CDC_CONN_RX8_B1_CTL, TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL,
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL,
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS,
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK,
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR },
+	{ TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING,
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR },
+	{ TOMTOM_A_CDC_MBHC_EN_CTL, TOMTOM_A_CDC_MBHC_EN_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_FIR_B1_CFG, TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR },
+	{ TOMTOM_A_CDC_MBHC_FIR_B2_CFG, TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR },
+	{ TOMTOM_A_CDC_MBHC_TIMER_B1_CTL, TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_TIMER_B2_CTL, TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_TIMER_B3_CTL, TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_TIMER_B4_CTL, TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_TIMER_B5_CTL, TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_TIMER_B6_CTL, TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_B1_STATUS, TOMTOM_A_CDC_MBHC_B1_STATUS__POR },
+	{ TOMTOM_A_CDC_MBHC_B2_STATUS, TOMTOM_A_CDC_MBHC_B2_STATUS__POR },
+	{ TOMTOM_A_CDC_MBHC_B3_STATUS, TOMTOM_A_CDC_MBHC_B3_STATUS__POR },
+	{ TOMTOM_A_CDC_MBHC_B4_STATUS, TOMTOM_A_CDC_MBHC_B4_STATUS__POR },
+	{ TOMTOM_A_CDC_MBHC_B5_STATUS, TOMTOM_A_CDC_MBHC_B5_STATUS__POR },
+	{ TOMTOM_A_CDC_MBHC_B1_CTL, TOMTOM_A_CDC_MBHC_B1_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_B2_CTL, TOMTOM_A_CDC_MBHC_B2_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B1_CTL, TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B2_CTL, TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B3_CTL, TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B4_CTL, TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B5_CTL, TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B6_CTL, TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B7_CTL, TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B8_CTL, TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B9_CTL, TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B10_CTL, TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B11_CTL, TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_VOLT_B12_CTL, TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_CLK_CTL, TOMTOM_A_CDC_MBHC_CLK_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_INT_CTL, TOMTOM_A_CDC_MBHC_INT_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_DEBUG_CTL, TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR },
+	{ TOMTOM_A_CDC_MBHC_SPARE, TOMTOM_A_CDC_MBHC_SPARE__POR },
+	{ TOMTOM_A_CDC_RX8_B1_CTL, TOMTOM_A_CDC_RX8_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_B2_CTL, TOMTOM_A_CDC_RX8_B2_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_B3_CTL, TOMTOM_A_CDC_RX8_B3_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_B4_CTL, TOMTOM_A_CDC_RX8_B4_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_B5_CTL, TOMTOM_A_CDC_RX8_B5_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_B6_CTL, TOMTOM_A_CDC_RX8_B6_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL,
+				TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR },
+	{ TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL,
+				TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR },
+	{ TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7,
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR },
+	{ TOMTOM_A_CDC_BOOST_MODE_CTL, TOMTOM_A_CDC_BOOST_MODE_CTL__POR },
+	{ TOMTOM_A_CDC_BOOST_THRESHOLD, TOMTOM_A_CDC_BOOST_THRESHOLD__POR },
+	{ TOMTOM_A_CDC_BOOST_TAP_SEL, TOMTOM_A_CDC_BOOST_TAP_SEL__POR },
+	{ TOMTOM_A_CDC_BOOST_HOLD_TIME, TOMTOM_A_CDC_BOOST_HOLD_TIME__POR },
+	{ TOMTOM_A_CDC_BOOST_TRGR_EN, TOMTOM_A_CDC_BOOST_TRGR_EN__POR },
+};
+
+static bool wcd9330_is_readable_register(struct device *dev, unsigned int reg)
+{
+	return tomtom_reg_readable[reg];
+}
+
+static bool tomtom_is_digital_gain_register(unsigned int reg)
+{
+	bool rtn = false;
+
+	switch (reg) {
+	case TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL:
+	case TOMTOM_A_CDC_TX1_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX2_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX3_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX4_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX5_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX6_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX7_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX8_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX9_VOL_CTL_GAIN:
+	case TOMTOM_A_CDC_TX10_VOL_CTL_GAIN:
+		rtn = true;
+		break;
+	default:
+		break;
+	}
+	return rtn;
+}
+
+static bool wcd9330_is_volatile_register(struct device *dev, unsigned int reg)
+{
+
+	if ((reg >= TOMTOM_A_CDC_MBHC_EN_CTL) || (reg < 0x100))
+		return true;
+
+	/* IIR Coeff registers are not cacheable */
+	if ((reg >= TOMTOM_A_CDC_IIR1_COEF_B1_CTL) &&
+		(reg <= TOMTOM_A_CDC_IIR2_COEF_B2_CTL))
+		return true;
+
+	/* ANC filter registers are not cacheable */
+	if ((reg >= TOMTOM_A_CDC_ANC1_IIR_B1_CTL) &&
+		(reg <= TOMTOM_A_CDC_ANC1_LPF_B2_CTL))
+		return true;
+
+	if ((reg >= TOMTOM_A_CDC_ANC2_IIR_B1_CTL) &&
+		(reg <= TOMTOM_A_CDC_ANC2_LPF_B2_CTL))
+		return true;
+
+	if (((reg >= TOMTOM_A_CDC_SPKR_CLIPDET_VAL0 &&
+	    reg <= TOMTOM_A_CDC_SPKR_CLIPDET_VAL7)) ||
+	    ((reg >= TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0) &&
+	     (reg <= TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7)))
+		return true;
+
+	/* Digital gain register is not cacheable so we have to write
+	 * the setting even it is the same
+	 */
+	if (tomtom_is_digital_gain_register(reg))
+		return true;
+
+	switch (reg) {
+	case TOMTOM_A_RX_HPH_L_STATUS:
+	case TOMTOM_A_RX_HPH_R_STATUS:
+	case TOMTOM_A_MBHC_INSERT_DET_STATUS:
+	case TOMTOM_A_RX_HPH_CNP_EN:
+	case TOMTOM_A_CDC_VBAT_GAIN_MON_VAL:
+	case TOMTOM_A_CDC_MAD_MAIN_CTL_1:
+	case TOMTOM_A_CDC_MAD_AUDIO_CTL_3:
+	case TOMTOM_A_CDC_MAD_AUDIO_CTL_4:
+	case TOMTOM_A_INTR_MODE:
+	case TOMTOM_A_INTR2_MASK0:
+	case TOMTOM_A_INTR2_STATUS0:
+	case TOMTOM_A_INTR2_CLEAR0:
+	case TOMTOM_SB_PGD_PORT_TX_BASE:
+	case TOMTOM_SB_PGD_PORT_RX_BASE:
+	case TOMTOM_A_CDC_ANC1_IIR_B1_CTL:
+	case TOMTOM_A_CDC_ANC1_GAIN_CTL:
+	case TOMTOM_A_SVASS_SPE_INBOX_TRG:
+		return true;
+	default:
+		return false;
+	}
+}
+
+struct regmap_config wcd9330_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = wcd9330_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wcd9330_defaults),
+	.max_register = WCD9330_MAX_REGISTER,
+	.volatile_reg = wcd9330_is_volatile_register,
+	.readable_reg = wcd9330_is_readable_register,
+};
diff --git a/drivers/mfd/wcd9335-regmap.c b/drivers/mfd/wcd9335-regmap.c
new file mode 100644
index 0000000..1762374
--- /dev/null
+++ b/drivers/mfd/wcd9335-regmap.c
@@ -0,0 +1,1611 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wcd9xxx-regmap.h"
+
+static const struct reg_sequence wcd9335_1_x_defaults[] = {
+	{ WCD9335_CODEC_RPM_CLK_GATE,                     0x03,  0x00 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN,       0x1f,  0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0,           0x00,  0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,               0x00,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG,          0x00,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG,          0x00,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG,          0x00,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG,          0x00,  0x00 },
+	{ WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD,       0x14,  0x00 },
+	{ WCD9335_CPE_SS_SS_ERROR_INT_MASK,               0x3f,  0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL,              0x00,  0x00 },
+	{ WCD9335_BIAS_VBG_FINE_ADJ,                      0x55,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_2,                        0x6c,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_3,                        0x2d,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_8,                        0x6c,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_10,                       0x6c,  0x00 },
+	{ WCD9335_SIDO_SIDO_DRIVER_2,                     0x77,  0x00 },
+	{ WCD9335_SIDO_SIDO_DRIVER_3,                     0x77,  0x00 },
+	{ WCD9335_SIDO_SIDO_TEST_2,                       0x00,  0x00 },
+	{ WCD9335_MBHC_ZDET_ANA_CTL,                      0x00,  0x00 },
+	{ WCD9335_MBHC_FSM_DEBUG,                         0xc0,  0x00 },
+	{ WCD9335_TX_1_2_ATEST_REFCTL,                    0x08,  0x00 },
+	{ WCD9335_TX_3_4_ATEST_REFCTL,                    0x08,  0x00 },
+	{ WCD9335_TX_5_6_ATEST_REFCTL,                    0x08,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_1,                    0x67,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_4,                    0x5f,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_9,                    0x50,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_DAC_CTRL_1,                0x65,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_DAC_CTRL_4,                0x40,  0x00 },
+	{ WCD9335_RX_BIAS_HPH_PA,                         0xaa,  0x00 },
+	{ WCD9335_RX_BIAS_HPH_LOWPOWER,                   0x62,  0x00 },
+	{ WCD9335_HPH_PA_CTL2,                            0x40,  0x00 },
+	{ WCD9335_HPH_L_EN,                               0x00,  0x00 },
+	{ WCD9335_HPH_R_EN,                               0x00,  0x00 },
+	{ WCD9335_HPH_R_ATEST,                            0x50,  0x00 },
+	{ WCD9335_HPH_RDAC_LDO_CTL,                       0x00,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_CFG0,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_CFG1,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC2,                   0x00,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC3,                   0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER1_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER2_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER3_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER4_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER5_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER6_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER7_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_COMPANDER8_CTL7,                    0x0c,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_CFG1,                   0x04,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_MIX_CFG,                0x0e,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC0,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC1,                   0x00,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,               0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC0_CLK_RST_CTL_0,              0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC1_CLK_RST_CTL_0,              0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC2_CLK_RST_CTL_0,              0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC3_CLK_RST_CTL_0,              0x00,  0x00 },
+	{ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,        0x00,  0x00 },
+	{ WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,              0x00,  0x00 },
+	{ WCD9335_TEST_DEBUG_NPL_DLY_TEST_2,              0x00,  0x00 },
+};
+
+static const struct reg_sequence wcd9335_2_0_defaults[] = {
+	{ WCD9335_CODEC_RPM_CLK_GATE,                     0x07,  0x00 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN,       0x3f,  0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0,           0x01,  0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,               0x10,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG,          0x08,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG,          0x08,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG,          0x08,  0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG,          0x08,  0x00 },
+	{ WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD,       0x13,  0x00 },
+	{ WCD9335_CPE_SS_SS_ERROR_INT_MASK,               0xff,  0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL,              0x40,  0x00 },
+	{ WCD9335_BIAS_VBG_FINE_ADJ,                      0xc5,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_2,                        0x92,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_3,                        0x35,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_8,                        0x6e,  0x00 },
+	{ WCD9335_SIDO_SIDO_CCL_10,                       0x6e,  0x00 },
+	{ WCD9335_SIDO_SIDO_DRIVER_2,                     0x55,  0x00 },
+	{ WCD9335_SIDO_SIDO_DRIVER_3,                     0x55,  0x00 },
+	{ WCD9335_SIDO_SIDO_TEST_2,                       0x0f,  0x00 },
+	{ WCD9335_MBHC_ZDET_ANA_CTL,                      0x0f,  0x00 },
+	{ WCD9335_TX_1_2_ATEST_REFCTL,                    0x0a,  0x00 },
+	{ WCD9335_TX_3_4_ATEST_REFCTL,                    0x0a,  0x00 },
+	{ WCD9335_TX_5_6_ATEST_REFCTL,                    0x0a,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_1,                    0xeb,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_4,                    0x7f,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_9,                    0x64,  0x00 },
+	{ WCD9335_FLYBACK_VNEG_DAC_CTRL_1,                0xed,  0x00 },
+	{ WCD9335_RX_BIAS_HPH_PA,                         0x9a,  0x00 },
+	{ WCD9335_RX_BIAS_HPH_LOWPOWER,                   0x82,  0x00 },
+	{ WCD9335_HPH_PA_CTL2,                            0x50,  0x00 },
+	{ WCD9335_HPH_L_EN,                               0x80,  0x00 },
+	{ WCD9335_HPH_R_EN,                               0x80,  0x00 },
+	{ WCD9335_HPH_R_ATEST,                            0x54,  0x00 },
+	{ WCD9335_HPH_RDAC_LDO_CTL,                       0x33,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_CFG0,                   0x10,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_CFG1,                   0x02,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC2,                   0x01,  0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC3,                   0x3c,  0x00 },
+	{ WCD9335_CDC_COMPANDER1_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER2_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER3_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER4_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER5_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER6_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER7_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_COMPANDER8_CTL7,                    0x08,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_CFG1,                   0x44,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_MIX_CFG,                0x1e,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC0,                   0xfc,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC1,                   0x08,  0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,               0x08,  0x00 },
+	{ WCD9335_SPLINE_SRC0_CLK_RST_CTL_0,              0x20,  0x00 },
+	{ WCD9335_SPLINE_SRC1_CLK_RST_CTL_0,              0x20,  0x00 },
+	{ WCD9335_SPLINE_SRC2_CLK_RST_CTL_0,              0x20,  0x00 },
+	{ WCD9335_SPLINE_SRC3_CLK_RST_CTL_0,              0x20,  0x00 },
+	{ WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,        0x0c,  0x00 },
+	{ WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,              0x10,  0x00 },
+	{ WCD9335_TEST_DEBUG_NPL_DLY_TEST_2,              0x60,  0x00 },
+	{ WCD9335_DATA_HUB_NATIVE_FIFO_SYNC,              0x00,  0x00 },
+	{ WCD9335_DATA_HUB_NATIVE_FIFO_STATUS,            0x00,  0x00 },
+	{ WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD,            0x60,  0x00 },
+	{ WCD9335_CPE_SS_TX_PP_CFG,                       0x3C,  0x00 },
+	{ WCD9335_CPE_SS_SVA_CFG,                         0x00,  0x00 },
+	{ WCD9335_MBHC_FSM_STATUS,                        0x00,  0x00 },
+	{ WCD9335_FLYBACK_CTRL_1,                         0x45,  0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC7,                   0x25,  0x00 },
+	{ WCD9335_SPLINE_SRC0_STATUS,                     0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC1_STATUS,                     0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC2_STATUS,                     0x00,  0x00 },
+	{ WCD9335_SPLINE_SRC3_STATUS,                     0x00,  0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT,    0x00,  0x00 },
+};
+
+static const struct reg_default wcd9335_defaults[] = {
+	/* Page #0 registers */
+	{ WCD9335_PAGE0_PAGE_REGISTER,                    0x00 },
+	{ WCD9335_CODEC_RPM_CLK_BYPASS,                   0x00 },
+	{ WCD9335_CODEC_RPM_CLK_MCLK_CFG,                 0x00 },
+	{ WCD9335_CODEC_RPM_RST_CTL,                      0x00 },
+	{ WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,           0x07 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1,            0x00 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2,            0x00 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3,            0x00 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN,        0x01 },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1,     0xff },
+	{ WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2,     0xff },
+	{ WCD9335_CODEC_RPM_INT_MASK,                     0x3f },
+	{ WCD9335_CODEC_RPM_INT_STATUS,                   0x00 },
+	{ WCD9335_CODEC_RPM_INT_CLEAR,                    0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1,           0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2,           0x07 },
+	{ WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3,           0x01 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0,             0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1,             0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10,         0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11,         0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12,         0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13,         0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14,         0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15,         0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS,            0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO,    0x0d },
+	{ WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3,          0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL,      0xCC },
+	{ WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE,              0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL,           0x06 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS,        0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB,       0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB,       0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL,           0x06 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS,        0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB,       0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB,       0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL,           0x06 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS,        0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB,       0x00 },
+	{ WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,           0x0c },
+	{ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,           0x0c },
+	{ WCD9335_DATA_HUB_DATA_HUB_I2S_CLK,              0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG,          0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG,          0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG,          0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG,          0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG,       0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG,      0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG,      0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG,      0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG,      0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG,      0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG,     0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG,     0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG,     0x00 },
+	{ WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG,     0x00 },
+	{ WCD9335_INTR_CFG,                               0x00 },
+	{ WCD9335_INTR_CLR_COMMIT,                        0x00 },
+	{ WCD9335_INTR_PIN1_MASK0,                        0xff },
+	{ WCD9335_INTR_PIN1_MASK1,                        0xff },
+	{ WCD9335_INTR_PIN1_MASK2,                        0xff },
+	{ WCD9335_INTR_PIN1_MASK3,                        0xff },
+	{ WCD9335_INTR_PIN1_STATUS0,                      0x00 },
+	{ WCD9335_INTR_PIN1_STATUS1,                      0x00 },
+	{ WCD9335_INTR_PIN1_STATUS2,                      0x00 },
+	{ WCD9335_INTR_PIN1_STATUS3,                      0x00 },
+	{ WCD9335_INTR_PIN1_CLEAR0,                       0x00 },
+	{ WCD9335_INTR_PIN1_CLEAR1,                       0x00 },
+	{ WCD9335_INTR_PIN1_CLEAR2,                       0x00 },
+	{ WCD9335_INTR_PIN1_CLEAR3,                       0x00 },
+	{ WCD9335_INTR_PIN2_MASK0,                        0xff },
+	{ WCD9335_INTR_PIN2_MASK1,                        0xff },
+	{ WCD9335_INTR_PIN2_MASK2,                        0xff },
+	{ WCD9335_INTR_PIN2_MASK3,                        0xff },
+	{ WCD9335_INTR_PIN2_STATUS0,                      0x00 },
+	{ WCD9335_INTR_PIN2_STATUS1,                      0x00 },
+	{ WCD9335_INTR_PIN2_STATUS2,                      0x00 },
+	{ WCD9335_INTR_PIN2_STATUS3,                      0x00 },
+	{ WCD9335_INTR_PIN2_CLEAR0,                       0x00 },
+	{ WCD9335_INTR_PIN2_CLEAR1,                       0x00 },
+	{ WCD9335_INTR_PIN2_CLEAR2,                       0x00 },
+	{ WCD9335_INTR_PIN2_CLEAR3,                       0x00 },
+	{ WCD9335_INTR_LEVEL0,                            0x03 },
+	{ WCD9335_INTR_LEVEL1,                            0xe0 },
+	{ WCD9335_INTR_LEVEL2,                            0x10 },
+	{ WCD9335_INTR_LEVEL3,                            0x80 },
+	{ WCD9335_INTR_BYPASS0,                           0x00 },
+	{ WCD9335_INTR_BYPASS1,                           0x00 },
+	{ WCD9335_INTR_BYPASS2,                           0x00 },
+	{ WCD9335_INTR_BYPASS3,                           0x00 },
+	{ WCD9335_INTR_SET0,                              0x00 },
+	{ WCD9335_INTR_SET1,                              0x00 },
+	{ WCD9335_INTR_SET2,                              0x00 },
+	{ WCD9335_INTR_SET3,                              0x00 },
+	/* Page #1 registers */
+	{ WCD9335_PAGE1_PAGE_REGISTER,                    0x00 },
+	{ WCD9335_CPE_FLL_USER_CTL_0,                     0x71 },
+	{ WCD9335_CPE_FLL_USER_CTL_1,                     0x34 },
+	{ WCD9335_CPE_FLL_USER_CTL_2,                     0x0b },
+	{ WCD9335_CPE_FLL_USER_CTL_3,                     0x02 },
+	{ WCD9335_CPE_FLL_USER_CTL_4,                     0x04 },
+	{ WCD9335_CPE_FLL_USER_CTL_5,                     0x02 },
+	{ WCD9335_CPE_FLL_USER_CTL_6,                     0x64 },
+	{ WCD9335_CPE_FLL_USER_CTL_7,                     0x00 },
+	{ WCD9335_CPE_FLL_USER_CTL_8,                     0x94 },
+	{ WCD9335_CPE_FLL_USER_CTL_9,                     0x70 },
+	{ WCD9335_CPE_FLL_L_VAL_CTL_0,                    0x40 },
+	{ WCD9335_CPE_FLL_L_VAL_CTL_1,                    0x00 },
+	{ WCD9335_CPE_FLL_DSM_FRAC_CTL_0,                 0x00 },
+	{ WCD9335_CPE_FLL_DSM_FRAC_CTL_1,                 0xff },
+	{ WCD9335_CPE_FLL_CONFIG_CTL_0,                   0x6b },
+	{ WCD9335_CPE_FLL_CONFIG_CTL_1,                   0x05 },
+	{ WCD9335_CPE_FLL_CONFIG_CTL_2,                   0x08 },
+	{ WCD9335_CPE_FLL_CONFIG_CTL_3,                   0x00 },
+	{ WCD9335_CPE_FLL_CONFIG_CTL_4,                   0x10 },
+	{ WCD9335_CPE_FLL_TEST_CTL_0,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_1,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_2,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_3,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_4,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_5,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_6,                     0x00 },
+	{ WCD9335_CPE_FLL_TEST_CTL_7,                     0x33 },
+	{ WCD9335_CPE_FLL_FREQ_CTL_0,                     0x00 },
+	{ WCD9335_CPE_FLL_FREQ_CTL_1,                     0x00 },
+	{ WCD9335_CPE_FLL_FREQ_CTL_2,                     0x00 },
+	{ WCD9335_CPE_FLL_FREQ_CTL_3,                     0x00 },
+	{ WCD9335_CPE_FLL_SSC_CTL_0,                      0x00 },
+	{ WCD9335_CPE_FLL_SSC_CTL_1,                      0x00 },
+	{ WCD9335_CPE_FLL_SSC_CTL_2,                      0x00 },
+	{ WCD9335_CPE_FLL_SSC_CTL_3,                      0x00 },
+	{ WCD9335_CPE_FLL_FLL_MODE,                       0x20 },
+	{ WCD9335_CPE_FLL_STATUS_0,                       0x00 },
+	{ WCD9335_CPE_FLL_STATUS_1,                       0x00 },
+	{ WCD9335_CPE_FLL_STATUS_2,                       0x00 },
+	{ WCD9335_CPE_FLL_STATUS_3,                       0x00 },
+	{ WCD9335_I2S_FLL_USER_CTL_0,                     0x41 },
+	{ WCD9335_I2S_FLL_USER_CTL_1,                     0x94 },
+	{ WCD9335_I2S_FLL_USER_CTL_2,                     0x08 },
+	{ WCD9335_I2S_FLL_USER_CTL_3,                     0x02 },
+	{ WCD9335_I2S_FLL_USER_CTL_4,                     0x04 },
+	{ WCD9335_I2S_FLL_USER_CTL_5,                     0x02 },
+	{ WCD9335_I2S_FLL_USER_CTL_6,                     0x40 },
+	{ WCD9335_I2S_FLL_USER_CTL_7,                     0x00 },
+	{ WCD9335_I2S_FLL_USER_CTL_8,                     0x5f },
+	{ WCD9335_I2S_FLL_USER_CTL_9,                     0x02 },
+	{ WCD9335_I2S_FLL_L_VAL_CTL_0,                    0x40 },
+	{ WCD9335_I2S_FLL_L_VAL_CTL_1,                    0x00 },
+	{ WCD9335_I2S_FLL_DSM_FRAC_CTL_0,                 0x00 },
+	{ WCD9335_I2S_FLL_DSM_FRAC_CTL_1,                 0xff },
+	{ WCD9335_I2S_FLL_CONFIG_CTL_0,                   0x6b },
+	{ WCD9335_I2S_FLL_CONFIG_CTL_1,                   0x05 },
+	{ WCD9335_I2S_FLL_CONFIG_CTL_2,                   0x08 },
+	{ WCD9335_I2S_FLL_CONFIG_CTL_3,                   0x00 },
+	{ WCD9335_I2S_FLL_CONFIG_CTL_4,                   0x30 },
+	{ WCD9335_I2S_FLL_TEST_CTL_0,                     0x80 },
+	{ WCD9335_I2S_FLL_TEST_CTL_1,                     0x00 },
+	{ WCD9335_I2S_FLL_TEST_CTL_2,                     0x00 },
+	{ WCD9335_I2S_FLL_TEST_CTL_3,                     0x00 },
+	{ WCD9335_I2S_FLL_TEST_CTL_4,                     0x00 },
+	{ WCD9335_I2S_FLL_TEST_CTL_5,                     0x00 },
+	{ WCD9335_I2S_FLL_TEST_CTL_6,                     0x00 },
+	{ WCD9335_I2S_FLL_TEST_CTL_7,                     0xff },
+	{ WCD9335_I2S_FLL_FREQ_CTL_0,                     0x00 },
+	{ WCD9335_I2S_FLL_FREQ_CTL_1,                     0x00 },
+	{ WCD9335_I2S_FLL_FREQ_CTL_2,                     0x00 },
+	{ WCD9335_I2S_FLL_FREQ_CTL_3,                     0x00 },
+	{ WCD9335_I2S_FLL_SSC_CTL_0,                      0x00 },
+	{ WCD9335_I2S_FLL_SSC_CTL_1,                      0x00 },
+	{ WCD9335_I2S_FLL_SSC_CTL_2,                      0x00 },
+	{ WCD9335_I2S_FLL_SSC_CTL_3,                      0x00 },
+	{ WCD9335_I2S_FLL_FLL_MODE,                       0x00 },
+	{ WCD9335_I2S_FLL_STATUS_0,                       0x00 },
+	{ WCD9335_I2S_FLL_STATUS_1,                       0x00 },
+	{ WCD9335_I2S_FLL_STATUS_2,                       0x00 },
+	{ WCD9335_I2S_FLL_STATUS_3,                       0x00 },
+	{ WCD9335_SB_FLL_USER_CTL_0,                      0x41 },
+	{ WCD9335_SB_FLL_USER_CTL_1,                      0x94 },
+	{ WCD9335_SB_FLL_USER_CTL_2,                      0x08 },
+	{ WCD9335_SB_FLL_USER_CTL_3,                      0x02 },
+	{ WCD9335_SB_FLL_USER_CTL_4,                      0x04 },
+	{ WCD9335_SB_FLL_USER_CTL_5,                      0x02 },
+	{ WCD9335_SB_FLL_USER_CTL_6,                      0x40 },
+	{ WCD9335_SB_FLL_USER_CTL_7,                      0x00 },
+	{ WCD9335_SB_FLL_USER_CTL_8,                      0x5e },
+	{ WCD9335_SB_FLL_USER_CTL_9,                      0x01 },
+	{ WCD9335_SB_FLL_L_VAL_CTL_0,                     0x40 },
+	{ WCD9335_SB_FLL_L_VAL_CTL_1,                     0x00 },
+	{ WCD9335_SB_FLL_DSM_FRAC_CTL_0,                  0x00 },
+	{ WCD9335_SB_FLL_DSM_FRAC_CTL_1,                  0xff },
+	{ WCD9335_SB_FLL_CONFIG_CTL_0,                    0x6b },
+	{ WCD9335_SB_FLL_CONFIG_CTL_1,                    0x05 },
+	{ WCD9335_SB_FLL_CONFIG_CTL_2,                    0x08 },
+	{ WCD9335_SB_FLL_CONFIG_CTL_3,                    0x00 },
+	{ WCD9335_SB_FLL_CONFIG_CTL_4,                    0x10 },
+	{ WCD9335_SB_FLL_TEST_CTL_0,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_1,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_2,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_3,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_4,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_5,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_6,                      0x00 },
+	{ WCD9335_SB_FLL_TEST_CTL_7,                      0xff },
+	{ WCD9335_SB_FLL_FREQ_CTL_0,                      0x00 },
+	{ WCD9335_SB_FLL_FREQ_CTL_1,                      0x00 },
+	{ WCD9335_SB_FLL_FREQ_CTL_2,                      0x00 },
+	{ WCD9335_SB_FLL_FREQ_CTL_3,                      0x00 },
+	{ WCD9335_SB_FLL_SSC_CTL_0,                       0x00 },
+	{ WCD9335_SB_FLL_SSC_CTL_1,                       0x00 },
+	{ WCD9335_SB_FLL_SSC_CTL_2,                       0x00 },
+	{ WCD9335_SB_FLL_SSC_CTL_3,                       0x00 },
+	{ WCD9335_SB_FLL_FLL_MODE,                        0x00 },
+	{ WCD9335_SB_FLL_STATUS_0,                        0x00 },
+	{ WCD9335_SB_FLL_STATUS_1,                        0x00 },
+	{ WCD9335_SB_FLL_STATUS_2,                        0x00 },
+	{ WCD9335_SB_FLL_STATUS_3,                        0x00 },
+	/* Page #2 registers */
+	{ WCD9335_PAGE2_PAGE_REGISTER,                    0x00 },
+	{ WCD9335_CPE_SS_MEM_PTR_0,                       0x00 },
+	{ WCD9335_CPE_SS_MEM_PTR_1,                       0x00 },
+	{ WCD9335_CPE_SS_MEM_PTR_2,                       0x00 },
+	{ WCD9335_CPE_SS_MEM_CTRL,                        0x08 },
+	{ WCD9335_CPE_SS_MEM_BANK_0,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_1,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_2,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_3,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_4,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_5,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_6,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_7,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_8,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_9,                      0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_10,                     0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_11,                     0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_12,                     0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_13,                     0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_14,                     0x00 },
+	{ WCD9335_CPE_SS_MEM_BANK_15,                     0x00 },
+	{ WCD9335_CPE_SS_INBOX1_TRG,                      0x00 },
+	{ WCD9335_CPE_SS_INBOX2_TRG,                      0x00 },
+	{ WCD9335_CPE_SS_INBOX1_0,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_1,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_2,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_3,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_4,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_5,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_6,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_7,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_8,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_9,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX1_10,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX1_11,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX1_12,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX1_13,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX1_14,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX1_15,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_0,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_1,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_2,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_3,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_4,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_5,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_6,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_7,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_8,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_9,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_10,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_11,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_12,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_13,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_14,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_15,                      0x00 },
+	{ WCD9335_CPE_SS_INBOX2_0,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_1,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_2,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_3,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_4,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_5,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_6,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_7,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_8,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_9,                        0x00 },
+	{ WCD9335_CPE_SS_INBOX2_10,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX2_11,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX2_12,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX2_13,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX2_14,                       0x00 },
+	{ WCD9335_CPE_SS_INBOX2_15,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_0,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_1,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_2,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_3,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_4,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_5,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_6,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_7,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_8,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_9,                       0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_10,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_11,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_12,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_13,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_14,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_15,                      0x00 },
+	{ WCD9335_CPE_SS_OUTBOX1_ACK,                     0x00 },
+	{ WCD9335_CPE_SS_OUTBOX2_ACK,                     0x00 },
+	{ WCD9335_CPE_SS_EC_BUF_INT_PERIOD,               0x3c },
+	{ WCD9335_CPE_SS_US_BUF_INT_PERIOD,               0x60 },
+	{ WCD9335_CPE_SS_CFG,                             0x41 },
+	{ WCD9335_CPE_SS_US_EC_MUX_CFG,                   0x00 },
+	{ WCD9335_CPE_SS_MAD_CTL,                         0x00 },
+	{ WCD9335_CPE_SS_CPAR_CTL,                        0x00 },
+	{ WCD9335_CPE_SS_DMIC0_CTL,                       0x00 },
+	{ WCD9335_CPE_SS_DMIC1_CTL,                       0x00 },
+	{ WCD9335_CPE_SS_DMIC2_CTL,                       0x00 },
+	{ WCD9335_CPE_SS_DMIC_CFG,                        0x80 },
+	{ WCD9335_CPE_SS_CPAR_CFG,                        0x00 },
+	{ WCD9335_CPE_SS_WDOG_CFG,                        0x01 },
+	{ WCD9335_CPE_SS_BACKUP_INT,                      0x00 },
+	{ WCD9335_CPE_SS_STATUS,                          0x00 },
+	{ WCD9335_CPE_SS_CPE_OCD_CFG,                     0x00 },
+	{ WCD9335_CPE_SS_SS_ERROR_INT_STATUS,             0x00 },
+	{ WCD9335_CPE_SS_SS_ERROR_INT_CLEAR,              0x00 },
+	{ WCD9335_SOC_MAD_MAIN_CTL_1,                     0x00 },
+	{ WCD9335_SOC_MAD_MAIN_CTL_2,                     0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_1,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_2,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_3,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_4,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_5,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_6,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_7,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_CTL_8,                    0x00 },
+	{ WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR,              0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_1,                     0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_2,                     0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_3,                     0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_4,                     0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_5,                     0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_6,                     0x00 },
+	{ WCD9335_SOC_MAD_ULTR_CTL_7,                     0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_1,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_2,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_3,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_4,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_5,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_6,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_7,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_CTL_8,                   0x00 },
+	{ WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR,             0x00 },
+	{ WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL,             0x00 },
+	{ WCD9335_SOC_MAD_INP_SEL,                        0x00 },
+	/* Page #6 registers */
+	{ WCD9335_PAGE6_PAGE_REGISTER,                    0x00 },
+	{ WCD9335_ANA_BIAS,                               0x00 },
+	{ WCD9335_ANA_CLK_TOP,                            0x00 },
+	{ WCD9335_ANA_RCO,                                0x30 },
+	{ WCD9335_ANA_BUCK_VOUT_A,                        0xb4 },
+	{ WCD9335_ANA_BUCK_VOUT_D,                        0xb4 },
+	{ WCD9335_ANA_BUCK_CTL,                           0x00 },
+	{ WCD9335_ANA_BUCK_STATUS,                        0xe0 },
+	{ WCD9335_ANA_RX_SUPPLIES,                        0x00 },
+	{ WCD9335_ANA_HPH,                                0x00 },
+	{ WCD9335_ANA_EAR,                                0x00 },
+	{ WCD9335_ANA_LO_1_2,                             0x00 },
+	{ WCD9335_ANA_LO_3_4,                             0x00 },
+	{ WCD9335_ANA_MAD_SETUP,                          0x81 },
+	{ WCD9335_ANA_AMIC1,                              0x20 },
+	{ WCD9335_ANA_AMIC2,                              0x00 },
+	{ WCD9335_ANA_AMIC3,                              0x20 },
+	{ WCD9335_ANA_AMIC4,                              0x00 },
+	{ WCD9335_ANA_AMIC5,                              0x20 },
+	{ WCD9335_ANA_AMIC6,                              0x00 },
+	{ WCD9335_ANA_MBHC_MECH,                          0x39 },
+	{ WCD9335_ANA_MBHC_ELECT,                         0x08 },
+	{ WCD9335_ANA_MBHC_ZDET,                          0x00 },
+	{ WCD9335_ANA_MBHC_RESULT_1,                      0x00 },
+	{ WCD9335_ANA_MBHC_RESULT_2,                      0x00 },
+	{ WCD9335_ANA_MBHC_RESULT_3,                      0x00 },
+	{ WCD9335_ANA_MBHC_BTN0,                          0x00 },
+	{ WCD9335_ANA_MBHC_BTN1,                          0x10 },
+	{ WCD9335_ANA_MBHC_BTN2,                          0x20 },
+	{ WCD9335_ANA_MBHC_BTN3,                          0x30 },
+	{ WCD9335_ANA_MBHC_BTN4,                          0x40 },
+	{ WCD9335_ANA_MBHC_BTN5,                          0x50 },
+	{ WCD9335_ANA_MBHC_BTN6,                          0x60 },
+	{ WCD9335_ANA_MBHC_BTN7,                          0x70 },
+	{ WCD9335_ANA_MICB1,                              0x10 },
+	{ WCD9335_ANA_MICB2,                              0x10 },
+	{ WCD9335_ANA_MICB2_RAMP,                         0x00 },
+	{ WCD9335_ANA_MICB3,                              0x10 },
+	{ WCD9335_ANA_MICB4,                              0x10 },
+	{ WCD9335_ANA_VBADC,                              0x00 },
+	{ WCD9335_BIAS_CTL,                               0x28 },
+	{ WCD9335_CLOCK_TEST_CTL,                         0x00 },
+	{ WCD9335_RCO_CTRL_1,                             0x44 },
+	{ WCD9335_RCO_CTRL_2,                             0x44 },
+	{ WCD9335_RCO_CAL,                                0x00 },
+	{ WCD9335_RCO_CAL_1,                              0x00 },
+	{ WCD9335_RCO_CAL_2,                              0x00 },
+	{ WCD9335_RCO_TEST_CTRL,                          0x00 },
+	{ WCD9335_RCO_CAL_OUT_1,                          0x00 },
+	{ WCD9335_RCO_CAL_OUT_2,                          0x00 },
+	{ WCD9335_RCO_CAL_OUT_3,                          0x00 },
+	{ WCD9335_RCO_CAL_OUT_4,                          0x00 },
+	{ WCD9335_RCO_CAL_OUT_5,                          0x00 },
+	{ WCD9335_SIDO_SIDO_MODE_1,                       0x84 },
+	{ WCD9335_SIDO_SIDO_MODE_2,                       0xfe },
+	{ WCD9335_SIDO_SIDO_MODE_3,                       0xf6 },
+	{ WCD9335_SIDO_SIDO_MODE_4,                       0x56 },
+	{ WCD9335_SIDO_SIDO_VCL_1,                        0x00 },
+	{ WCD9335_SIDO_SIDO_VCL_2,                        0x6c },
+	{ WCD9335_SIDO_SIDO_VCL_3,                        0x44 },
+	{ WCD9335_SIDO_SIDO_CCL_1,                        0x57 },
+	{ WCD9335_SIDO_SIDO_CCL_4,                        0x61 },
+	{ WCD9335_SIDO_SIDO_CCL_5,                        0x6d },
+	{ WCD9335_SIDO_SIDO_CCL_6,                        0x60 },
+	{ WCD9335_SIDO_SIDO_CCL_7,                        0x6f },
+	{ WCD9335_SIDO_SIDO_CCL_9,                        0x6e },
+	{ WCD9335_SIDO_SIDO_FILTER_1,                     0x92 },
+	{ WCD9335_SIDO_SIDO_FILTER_2,                     0x54 },
+	{ WCD9335_SIDO_SIDO_DRIVER_1,                     0x77 },
+	{ WCD9335_SIDO_SIDO_CAL_CODE_EXT_1,               0x9c },
+	{ WCD9335_SIDO_SIDO_CAL_CODE_EXT_2,               0x82 },
+	{ WCD9335_SIDO_SIDO_CAL_CODE_OUT_1,               0x00 },
+	{ WCD9335_SIDO_SIDO_CAL_CODE_OUT_2,               0x00 },
+	{ WCD9335_SIDO_SIDO_TEST_1,                       0x00 },
+	{ WCD9335_MBHC_CTL_1,                             0x32 },
+	{ WCD9335_MBHC_CTL_2,                             0x01 },
+	{ WCD9335_MBHC_PLUG_DETECT_CTL,                   0x69 },
+	{ WCD9335_MBHC_ZDET_RAMP_CTL,                     0x00 },
+	{ WCD9335_MBHC_TEST_CTL,                          0x00 },
+	{ WCD9335_VBADC_SUBBLOCK_EN,                      0xfe },
+	{ WCD9335_VBADC_IBIAS_FE,                         0x54 },
+	{ WCD9335_VBADC_BIAS_ADC,                         0x51 },
+	{ WCD9335_VBADC_FE_CTRL,                          0x1c },
+	{ WCD9335_VBADC_ADC_REF,                          0x20 },
+	{ WCD9335_VBADC_ADC_IO,                           0x80 },
+	{ WCD9335_VBADC_ADC_SAR,                          0xff },
+	{ WCD9335_VBADC_DEBUG,                            0x00 },
+	{ WCD9335_VBADC_ADC_DOUTMSB,                      0x00 },
+	{ WCD9335_VBADC_ADC_DOUTLSB,                      0x00 },
+	{ WCD9335_LDOH_MODE,                              0x2b },
+	{ WCD9335_LDOH_BIAS,                              0x68 },
+	{ WCD9335_LDOH_STB_LOADS,                         0x00 },
+	{ WCD9335_LDOH_SLOWRAMP,                          0x50 },
+	{ WCD9335_MICB1_TEST_CTL_1,                       0x1a },
+	{ WCD9335_MICB1_TEST_CTL_2,                       0x18 },
+	{ WCD9335_MICB1_TEST_CTL_3,                       0xa4 },
+	{ WCD9335_MICB2_TEST_CTL_1,                       0x1a },
+	{ WCD9335_MICB2_TEST_CTL_2,                       0x18 },
+	{ WCD9335_MICB2_TEST_CTL_3,                       0x24 },
+	{ WCD9335_MICB3_TEST_CTL_1,                       0x1a },
+	{ WCD9335_MICB3_TEST_CTL_2,                       0x18 },
+	{ WCD9335_MICB3_TEST_CTL_3,                       0xa4 },
+	{ WCD9335_MICB4_TEST_CTL_1,                       0x1a },
+	{ WCD9335_MICB4_TEST_CTL_2,                       0x18 },
+	{ WCD9335_MICB4_TEST_CTL_3,                       0xa4 },
+	{ WCD9335_TX_COM_ADC_VCM,                         0x39 },
+	{ WCD9335_TX_COM_BIAS_ATEST,                      0xc0 },
+	{ WCD9335_TX_COM_ADC_INT1_IB,                     0x6f },
+	{ WCD9335_TX_COM_ADC_INT2_IB,                     0x4f },
+	{ WCD9335_TX_COM_TXFE_DIV_CTL,                    0x2e },
+	{ WCD9335_TX_COM_TXFE_DIV_START,                  0x00 },
+	{ WCD9335_TX_COM_TXFE_DIV_STOP_9P6M,              0xc7 },
+	{ WCD9335_TX_COM_TXFE_DIV_STOP_12P288M,           0xff },
+	{ WCD9335_TX_1_2_TEST_EN,                         0xcc },
+	{ WCD9335_TX_1_2_ADC_IB,                          0x09 },
+	{ WCD9335_TX_1_2_TEST_CTL,                        0x38 },
+	{ WCD9335_TX_1_2_TEST_BLK_EN,                     0xff },
+	{ WCD9335_TX_1_2_TXFE_CLKDIV,                     0x00 },
+	{ WCD9335_TX_1_2_SAR1_ERR,                        0x00 },
+	{ WCD9335_TX_1_2_SAR2_ERR,                        0x00 },
+	{ WCD9335_TX_3_4_TEST_EN,                         0xcc },
+	{ WCD9335_TX_3_4_ADC_IB,                          0x09 },
+	{ WCD9335_TX_3_4_TEST_CTL,                        0x38 },
+	{ WCD9335_TX_3_4_TEST_BLK_EN,                     0xff },
+	{ WCD9335_TX_3_4_TXFE_CLKDIV,                     0x00 },
+	{ WCD9335_TX_3_4_SAR1_ERR,                        0x00 },
+	{ WCD9335_TX_3_4_SAR2_ERR,                        0x00 },
+	{ WCD9335_TX_5_6_TEST_EN,                         0xcc },
+	{ WCD9335_TX_5_6_ADC_IB,                          0x09 },
+	{ WCD9335_TX_5_6_TEST_CTL,                        0x38 },
+	{ WCD9335_TX_5_6_TEST_BLK_EN,                     0xff },
+	{ WCD9335_TX_5_6_TXFE_CLKDIV,                     0x00 },
+	{ WCD9335_TX_5_6_SAR1_ERR,                        0x00 },
+	{ WCD9335_TX_5_6_SAR2_ERR,                        0x00 },
+	{ WCD9335_CLASSH_MODE_1,                          0x40 },
+	{ WCD9335_CLASSH_MODE_2,                          0x3a },
+	{ WCD9335_CLASSH_MODE_3,                          0x00 },
+	{ WCD9335_CLASSH_CTRL_VCL_1,                      0x70 },
+	{ WCD9335_CLASSH_CTRL_VCL_2,                      0xa2 },
+	{ WCD9335_CLASSH_CTRL_CCL_1,                      0x51 },
+	{ WCD9335_CLASSH_CTRL_CCL_2,                      0x80 },
+	{ WCD9335_CLASSH_CTRL_CCL_3,                      0x80 },
+	{ WCD9335_CLASSH_CTRL_CCL_4,                      0x51 },
+	{ WCD9335_CLASSH_CTRL_CCL_5,                      0x00 },
+	{ WCD9335_CLASSH_BUCK_TMUX_A_D,                   0x00 },
+	{ WCD9335_CLASSH_BUCK_SW_DRV_CNTL,                0x77 },
+	{ WCD9335_CLASSH_SPARE,                           0x00 },
+	{ WCD9335_FLYBACK_EN,                             0x4e },
+	{ WCD9335_FLYBACK_VNEG_CTRL_2,                    0x45 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_3,                    0x74 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_5,                    0x83 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_6,                    0x98 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_7,                    0xa9 },
+	{ WCD9335_FLYBACK_VNEG_CTRL_8,                    0x68 },
+	{ WCD9335_FLYBACK_VNEG_DAC_CTRL_2,                0x50 },
+	{ WCD9335_FLYBACK_VNEG_DAC_CTRL_3,                0xa6 },
+	{ WCD9335_FLYBACK_TEST_CTL,                       0x00 },
+	{ WCD9335_RX_AUX_SW_CTL,                          0x00 },
+	{ WCD9335_RX_PA_AUX_IN_CONN,                      0x00 },
+	{ WCD9335_RX_TIMER_DIV,                           0x74 },
+	{ WCD9335_RX_OCP_CTL,                             0x1f },
+	{ WCD9335_RX_OCP_COUNT,                           0x77 },
+	{ WCD9335_RX_BIAS_EAR_DAC,                        0xa0 },
+	{ WCD9335_RX_BIAS_EAR_AMP,                        0xaa },
+	{ WCD9335_RX_BIAS_HPH_LDO,                        0xa9 },
+	{ WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2,              0x8a },
+	{ WCD9335_RX_BIAS_HPH_RDAC_LDO,                   0x88 },
+	{ WCD9335_RX_BIAS_HPH_CNP1,                       0x86 },
+	{ WCD9335_RX_BIAS_DIFFLO_PA,                      0x80 },
+	{ WCD9335_RX_BIAS_DIFFLO_REF,                     0x88 },
+	{ WCD9335_RX_BIAS_DIFFLO_LDO,                     0x88 },
+	{ WCD9335_RX_BIAS_SELO_DAC_PA,                    0xa8 },
+	{ WCD9335_RX_BIAS_BUCK_RST,                       0x08 },
+	{ WCD9335_RX_BIAS_BUCK_VREF_ERRAMP,               0x44 },
+	{ WCD9335_RX_BIAS_FLYB_ERRAMP,                    0x40 },
+	{ WCD9335_RX_BIAS_FLYB_BUFF,                      0xaa },
+	{ WCD9335_RX_BIAS_FLYB_MID_RST,                   0x44 },
+	{ WCD9335_HPH_L_STATUS,                           0x04 },
+	{ WCD9335_HPH_R_STATUS,                           0x04 },
+	{ WCD9335_HPH_CNP_EN,                             0x80 },
+	{ WCD9335_HPH_CNP_WG_CTL,                         0xda },
+	{ WCD9335_HPH_CNP_WG_TIME,                        0x15 },
+	{ WCD9335_HPH_OCP_CTL,                            0x28 },
+	{ WCD9335_HPH_AUTO_CHOP,                          0x12 },
+	{ WCD9335_HPH_CHOP_CTL,                           0x83 },
+	{ WCD9335_HPH_PA_CTL1,                            0x46 },
+	{ WCD9335_HPH_L_TEST,                             0x00 },
+	{ WCD9335_HPH_L_ATEST,                            0x50 },
+	{ WCD9335_HPH_R_TEST,                             0x00 },
+	{ WCD9335_HPH_RDAC_CLK_CTL1,                      0x99 },
+	{ WCD9335_HPH_RDAC_CLK_CTL2,                      0x9b },
+	{ WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL,               0x00 },
+	{ WCD9335_HPH_REFBUFF_UHQA_CTL,                   0xa8 },
+	{ WCD9335_HPH_REFBUFF_LP_CTL,                     0x00 },
+	{ WCD9335_HPH_L_DAC_CTL,                          0x00 },
+	{ WCD9335_HPH_R_DAC_CTL,                          0x00 },
+	{ WCD9335_EAR_EN_REG,                             0x60 },
+	{ WCD9335_EAR_CMBUFF,                             0x0d },
+	{ WCD9335_EAR_ICTL,                               0x40 },
+	{ WCD9335_EAR_EN_DBG_CTL,                         0x00 },
+	{ WCD9335_EAR_CNP,                                0xe0 },
+	{ WCD9335_EAR_DAC_CTL_ATEST,                      0x00 },
+	{ WCD9335_EAR_STATUS_REG,                         0x04 },
+	{ WCD9335_EAR_OUT_SHORT,                          0x00 },
+	{ WCD9335_DIFF_LO_MISC,                           0x03 },
+	{ WCD9335_DIFF_LO_LO2_COMPANDER,                  0x00 },
+	{ WCD9335_DIFF_LO_LO1_COMPANDER,                  0x00 },
+	{ WCD9335_DIFF_LO_COMMON,                         0x40 },
+	{ WCD9335_DIFF_LO_BYPASS_EN,                      0x00 },
+	{ WCD9335_DIFF_LO_CNP,                            0x20 },
+	{ WCD9335_DIFF_LO_CORE_OUT_PROG,                  0x00 },
+	{ WCD9335_DIFF_LO_LDO_OUT_PROG,                   0x00 },
+	{ WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ,          0x9b },
+	{ WCD9335_DIFF_LO_COM_PA_FREQ,                    0xb0 },
+	{ WCD9335_DIFF_LO_RESERVED_REG,                   0x60 },
+	{ WCD9335_DIFF_LO_LO1_STATUS_1,                   0x00 },
+	{ WCD9335_DIFF_LO_LO1_STATUS_2,                   0x00 },
+	{ WCD9335_SE_LO_COM1,                             0x80 },
+	{ WCD9335_SE_LO_COM2,                             0x04 },
+	{ WCD9335_SE_LO_LO3_GAIN,                         0x20 },
+	{ WCD9335_SE_LO_LO3_CTRL,                         0x04 },
+	{ WCD9335_SE_LO_LO4_GAIN,                         0x20 },
+	{ WCD9335_SE_LO_LO4_CTRL,                         0x04 },
+	{ WCD9335_SE_LO_LO3_STATUS,                       0x00 },
+	{ WCD9335_SE_LO_LO4_STATUS,                       0x00 },
+	/* Page #10 registers */
+	{ WCD9335_PAGE10_PAGE_REGISTER,                   0x00 },
+	{ WCD9335_CDC_ANC0_CLK_RESET_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC0_MODE_1_CTL,                    0x00 },
+	{ WCD9335_CDC_ANC0_MODE_2_CTL,                    0x00 },
+	{ WCD9335_CDC_ANC0_FF_SHIFT,                      0x00 },
+	{ WCD9335_CDC_ANC0_FB_SHIFT,                      0x00 },
+	{ WCD9335_CDC_ANC0_LPF_FF_A_CTL,                  0x00 },
+	{ WCD9335_CDC_ANC0_LPF_FF_B_CTL,                  0x00 },
+	{ WCD9335_CDC_ANC0_LPF_FB_CTL,                    0x00 },
+	{ WCD9335_CDC_ANC0_SMLPF_CTL,                     0x00 },
+	{ WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL,               0x00 },
+	{ WCD9335_CDC_ANC0_IIR_ADAPT_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC0_IIR_COEFF_1_CTL,               0x00 },
+	{ WCD9335_CDC_ANC0_IIR_COEFF_2_CTL,               0x00 },
+	{ WCD9335_CDC_ANC0_FF_A_GAIN_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC0_FF_B_GAIN_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC0_FB_GAIN_CTL,                   0x00 },
+	{ WCD9335_CDC_ANC1_CLK_RESET_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC1_MODE_1_CTL,                    0x00 },
+	{ WCD9335_CDC_ANC1_MODE_2_CTL,                    0x00 },
+	{ WCD9335_CDC_ANC1_FF_SHIFT,                      0x00 },
+	{ WCD9335_CDC_ANC1_FB_SHIFT,                      0x00 },
+	{ WCD9335_CDC_ANC1_LPF_FF_A_CTL,                  0x00 },
+	{ WCD9335_CDC_ANC1_LPF_FF_B_CTL,                  0x00 },
+	{ WCD9335_CDC_ANC1_LPF_FB_CTL,                    0x00 },
+	{ WCD9335_CDC_ANC1_SMLPF_CTL,                     0x00 },
+	{ WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL,               0x00 },
+	{ WCD9335_CDC_ANC1_IIR_ADAPT_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC1_IIR_COEFF_1_CTL,               0x00 },
+	{ WCD9335_CDC_ANC1_IIR_COEFF_2_CTL,               0x00 },
+	{ WCD9335_CDC_ANC1_FF_A_GAIN_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC1_FF_B_GAIN_CTL,                 0x00 },
+	{ WCD9335_CDC_ANC1_FB_GAIN_CTL,                   0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX0_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX0_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX1_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX1_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX2_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX2_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX3_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX3_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX4_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX4_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX5_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX5_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX6_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX6_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX7_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX7_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_TX8_TX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_192_CTL,                0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_192_CFG,                0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC0,                   0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC1,                   0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC4,                   0x20 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_TX8_TX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL,             0x02 },
+	{ WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0,            0x00 },
+	{ WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL,            0x02 },
+	{ WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0,           0x00 },
+	{ WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL,            0x02 },
+	{ WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0,           0x00 },
+	{ WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL,            0x02 },
+	{ WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0,           0x00 },
+	/* Page #11 registers */
+	{ WCD9335_PAGE11_PAGE_REGISTER,                   0x00 },
+	{ WCD9335_CDC_COMPANDER1_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER1_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER1_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER1_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER1_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER1_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER1_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER2_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER2_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER2_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER2_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER2_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER2_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER2_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER3_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER3_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER3_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER3_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER3_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER3_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER3_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER4_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER4_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER4_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER4_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER4_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER4_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER4_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER5_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER5_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER5_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER5_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER5_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER5_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER5_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER6_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER6_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER6_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER6_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER6_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER6_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER6_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER7_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER7_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER7_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER7_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER7_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER7_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER7_CTL6,                    0x01 },
+	{ WCD9335_CDC_COMPANDER8_CTL0,                    0x60 },
+	{ WCD9335_CDC_COMPANDER8_CTL1,                    0xdb },
+	{ WCD9335_CDC_COMPANDER8_CTL2,                    0xff },
+	{ WCD9335_CDC_COMPANDER8_CTL3,                    0x35 },
+	{ WCD9335_CDC_COMPANDER8_CTL4,                    0xff },
+	{ WCD9335_CDC_COMPANDER8_CTL5,                    0x00 },
+	{ WCD9335_CDC_COMPANDER8_CTL6,                    0x01 },
+	{ WCD9335_CDC_RX0_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX0_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX0_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX0_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX0_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX1_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX1_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX1_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC4,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX1_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX2_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX2_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX2_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC4,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX2_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX3_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX3_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX3_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX3_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX4_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX4_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX4_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX4_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX5_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX5_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX5_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX5_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX6_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX6_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX6_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX6_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX7_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX7_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX7_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX7_RX_PATH_MIX_SEC1,               0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_CTL,                    0x04 },
+	{ WCD9335_CDC_RX8_RX_PATH_CFG0,                   0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_CFG2,                   0x8f },
+	{ WCD9335_CDC_RX8_RX_VOL_CTL,                     0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_MIX_CTL,                0x04 },
+	{ WCD9335_CDC_RX8_RX_VOL_MIX_CTL,                 0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC2,                   0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC3,                   0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC5,                   0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC6,                   0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_SEC7,                   0x00 },
+	{ WCD9335_CDC_RX8_RX_PATH_MIX_SEC1,               0x00 },
+	/* Page #12 registers */
+	{ WCD9335_PAGE12_PAGE_REGISTER,                   0x00 },
+	{ WCD9335_CDC_CLSH_CRC,                           0x00 },
+	{ WCD9335_CDC_CLSH_DLY_CTRL,                      0x03 },
+	{ WCD9335_CDC_CLSH_DECAY_CTRL,                    0x02 },
+	{ WCD9335_CDC_CLSH_HPH_V_PA,                      0x1c },
+	{ WCD9335_CDC_CLSH_EAR_V_PA,                      0x39 },
+	{ WCD9335_CDC_CLSH_HPH_V_HD,                      0x0c },
+	{ WCD9335_CDC_CLSH_EAR_V_HD,                      0x0c },
+	{ WCD9335_CDC_CLSH_K1_MSB,                        0x01 },
+	{ WCD9335_CDC_CLSH_K1_LSB,                        0x00 },
+	{ WCD9335_CDC_CLSH_K2_MSB,                        0x00 },
+	{ WCD9335_CDC_CLSH_K2_LSB,                        0x80 },
+	{ WCD9335_CDC_CLSH_IDLE_CTRL,                     0x00 },
+	{ WCD9335_CDC_CLSH_IDLE_HPH,                      0x00 },
+	{ WCD9335_CDC_CLSH_IDLE_EAR,                      0x00 },
+	{ WCD9335_CDC_CLSH_TEST0,                         0x07 },
+	{ WCD9335_CDC_CLSH_TEST1,                         0x00 },
+	{ WCD9335_CDC_CLSH_OVR_VREF,                      0x00 },
+	{ WCD9335_CDC_BOOST0_BOOST_PATH_CTL,              0x00 },
+	{ WCD9335_CDC_BOOST0_BOOST_CTL,                   0xb2 },
+	{ WCD9335_CDC_BOOST0_BOOST_CFG1,                  0x00 },
+	{ WCD9335_CDC_BOOST0_BOOST_CFG2,                  0x00 },
+	{ WCD9335_CDC_BOOST1_BOOST_PATH_CTL,              0x00 },
+	{ WCD9335_CDC_BOOST1_BOOST_CTL,                   0xb2 },
+	{ WCD9335_CDC_BOOST1_BOOST_CFG1,                  0x00 },
+	{ WCD9335_CDC_BOOST1_BOOST_CFG2,                  0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_DATA_0,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_DATA_1,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_DATA_2,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_DATA_3,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_DATA_0,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_DATA_1,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_DATA_2,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_RD_DATA_3,               0x00 },
+	{ WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG,              0x0f },
+	{ WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS,           0x03 },
+	{ WCD9335_CDC_VBAT_VBAT_PATH_CTL,                 0x00 },
+	{ WCD9335_CDC_VBAT_VBAT_CFG,                      0x0a },
+	{ WCD9335_CDC_VBAT_VBAT_ADC_CAL1,                 0x00 },
+	{ WCD9335_CDC_VBAT_VBAT_ADC_CAL2,                 0x00 },
+	{ WCD9335_CDC_VBAT_VBAT_ADC_CAL3,                 0x04 },
+	{ WCD9335_CDC_VBAT_VBAT_PK_EST1,                  0xe0 },
+	{ WCD9335_CDC_VBAT_VBAT_PK_EST2,                  0x01 },
+	{ WCD9335_CDC_VBAT_VBAT_PK_EST3,                  0x40 },
+	{ WCD9335_CDC_VBAT_VBAT_RF_PROC1,                 0x2a },
+	{ WCD9335_CDC_VBAT_VBAT_RF_PROC2,                 0x86 },
+	{ WCD9335_CDC_VBAT_VBAT_TAC1,                     0x70 },
+	{ WCD9335_CDC_VBAT_VBAT_TAC2,                     0x18 },
+	{ WCD9335_CDC_VBAT_VBAT_TAC3,                     0x18 },
+	{ WCD9335_CDC_VBAT_VBAT_TAC4,                     0x03 },
+	{ WCD9335_CDC_VBAT_VBAT_GAIN_UPD1,                0x01 },
+	{ WCD9335_CDC_VBAT_VBAT_GAIN_UPD2,                0x00 },
+	{ WCD9335_CDC_VBAT_VBAT_GAIN_UPD3,                0x64 },
+	{ WCD9335_CDC_VBAT_VBAT_GAIN_UPD4,                0x01 },
+	{ WCD9335_CDC_VBAT_VBAT_DEBUG1,                   0x00 },
+	{ WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON,             0x00 },
+	{ WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL,             0x00 },
+	{ WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,      0x04 },
+	{ WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1,     0x00 },
+	{ WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,      0x04 },
+	{ WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1,     0x00 },
+	/* Page #13 registers */
+	{ WCD9335_PAGE13_PAGE_REGISTER,                   0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1,            0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0,             0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1,             0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2,             0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3,             0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4,             0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0,       0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1,       0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_ANC_CFG0,                0x00 },
+	{ WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0,         0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0,           0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0,          0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0,          0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0,          0x00 },
+	{ WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0,          0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0x00 },
+	{ WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,              0x00 },
+	{ WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1,              0x00 },
+	{ WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2,              0x00 },
+	{ WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3,              0x00 },
+	{ WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,          0x00 },
+	{ WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL,           0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_CTL,               0x08 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0,      0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1,      0x4b },
+	{ WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB,   0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB,   0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_STATUS,            0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL,         0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB,     0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB,     0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD,  0x00 },
+	{ WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD,  0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL,         0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_CTL,              0x40 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL,   0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL,         0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_CTL,              0x40 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL,   0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL,      0x00 },
+	{ WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL,      0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG0,                       0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG1,                       0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG2,                       0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG3,                       0x18 },
+	{ WCD9335_CDC_TOP_TOP_CFG4,                       0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG5,                       0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG6,                       0x00 },
+	{ WCD9335_CDC_TOP_TOP_CFG7,                       0x00 },
+	{ WCD9335_CDC_TOP_HPHL_COMP_WR_LSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHL_COMP_WR_MSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHL_COMP_LUT,                  0x00 },
+	{ WCD9335_CDC_TOP_HPHL_COMP_RD_LSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHL_COMP_RD_MSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHR_COMP_WR_LSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHR_COMP_WR_MSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHR_COMP_LUT,                  0x00 },
+	{ WCD9335_CDC_TOP_HPHR_COMP_RD_LSB,               0x00 },
+	{ WCD9335_CDC_TOP_HPHR_COMP_RD_MSB,               0x00 },
+	{ WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFL_COMP_LUT,                 0x00 },
+	{ WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFR_COMP_LUT,                 0x00 },
+	{ WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB,              0x00 },
+	{ WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB,              0x00 },
+	/* Page #0x80 registers */
+	{ WCD9335_PAGE80_PAGE_REGISTER,                   0x00 },
+	{ WCD9335_TLMM_BIST_MODE_PINCFG,                  0x00 },
+	{ WCD9335_TLMM_RF_PA_ON_PINCFG,                   0x00 },
+	{ WCD9335_TLMM_INTR1_PINCFG,                      0x00 },
+	{ WCD9335_TLMM_INTR2_PINCFG,                      0x00 },
+	{ WCD9335_TLMM_SWR_DATA_PINCFG,                   0x00 },
+	{ WCD9335_TLMM_SWR_CLK_PINCFG,                    0x00 },
+	{ WCD9335_TLMM_SLIMBUS_DATA2_PINCFG,              0x00 },
+	{ WCD9335_TLMM_I2C_CLK_PINCFG,                    0x00 },
+	{ WCD9335_TLMM_I2C_DATA_PINCFG,                   0x00 },
+	{ WCD9335_TLMM_I2S_RX_SD0_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_I2S_RX_SD1_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_I2S_RX_SCK_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_I2S_RX_WS_PINCFG,                  0x00 },
+	{ WCD9335_TLMM_I2S_TX_SD0_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_I2S_TX_SD1_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_I2S_TX_SCK_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_I2S_TX_WS_PINCFG,                  0x00 },
+	{ WCD9335_TLMM_DMIC1_CLK_PINCFG,                  0x00 },
+	{ WCD9335_TLMM_DMIC1_DATA_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_DMIC2_CLK_PINCFG,                  0x00 },
+	{ WCD9335_TLMM_DMIC2_DATA_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_DMIC3_CLK_PINCFG,                  0x00 },
+	{ WCD9335_TLMM_DMIC3_DATA_PINCFG,                 0x00 },
+	{ WCD9335_TLMM_JTDI_PINCFG,                       0x00 },
+	{ WCD9335_TLMM_JTDO_PINCFG,                       0x00 },
+	{ WCD9335_TLMM_JTMS_PINCFG,                       0x00 },
+	{ WCD9335_TLMM_JTCK_PINCFG,                       0x00 },
+	{ WCD9335_TLMM_JTRST_PINCFG,                      0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_OE_0,                0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_OE_1,                0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_OE_2,                0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_OE_3,                0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_DATA_0,              0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_DATA_1,              0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_DATA_2,              0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_CTL_DATA_3,              0x00 },
+	{ WCD9335_TEST_DEBUG_PAD_DRVCTL,                  0x00 },
+	{ WCD9335_TEST_DEBUG_PIN_STATUS,                  0x00 },
+	{ WCD9335_TEST_DEBUG_MEM_CTRL,                    0x00 },
+	{ WCD9335_TEST_DEBUG_DEBUG_BUS_SEL,               0x00 },
+	{ WCD9335_TEST_DEBUG_DEBUG_JTAG,                  0x00 },
+	{ WCD9335_TEST_DEBUG_DEBUG_EN_1,                  0x00 },
+	{ WCD9335_TEST_DEBUG_DEBUG_EN_2,                  0x00 },
+	{ WCD9335_TEST_DEBUG_DEBUG_EN_3,                  0x00 },
+};
+
+/*
+ * wcd9335_regmap_register_patch: Update register defaults based on version
+ * @regmap: handle to wcd9xxx regmap
+ * @version: wcd9335 version
+ *
+ * Returns error code in case of failure or 0 for success
+ */
+int wcd9335_regmap_register_patch(struct regmap *regmap, int version)
+{
+	int rc;
+
+	if (!regmap) {
+		pr_err("%s: regmap struct is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (version) {
+	case TASHA_VERSION_1_0:
+	case TASHA_VERSION_1_1:
+		regcache_cache_only(regmap, true);
+		rc = regmap_multi_reg_write(regmap, wcd9335_1_x_defaults,
+					    ARRAY_SIZE(wcd9335_1_x_defaults));
+		regcache_cache_only(regmap, false);
+		break;
+	case TASHA_VERSION_2_0:
+		regcache_cache_only(regmap, true);
+		rc = regmap_multi_reg_write(regmap, wcd9335_2_0_defaults,
+					    ARRAY_SIZE(wcd9335_2_0_defaults));
+		regcache_cache_only(regmap, false);
+		break;
+	default:
+		pr_err("%s: unknown version: %d\n", __func__, version);
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9335_regmap_register_patch);
+
+static bool wcd9335_is_readable_register(struct device *dev, unsigned int reg)
+{
+	u8 pg_num, reg_offset;
+	const u8 *reg_tbl = NULL;
+
+	/*
+	 * Get the page number from MSB of codec register. If its 0x80, assign
+	 * the corresponding page index PAGE_0x80.
+	 */
+	pg_num = reg >> 0x8;
+	if (pg_num == 0x80)
+		pg_num = PAGE_0X80;
+	else if (pg_num >= 0xE)
+		return false;
+
+	reg_tbl = wcd9335_reg[pg_num];
+	reg_offset = reg & 0xFF;
+
+	if (reg_tbl)
+		return reg_tbl[reg_offset];
+	else
+		return false;
+}
+
+static bool wcd9335_is_volatile_register(struct device *dev, unsigned int reg)
+{
+	/*
+	 * registers from 0x000 to 0x0FF are volatile because
+	 * this space contains registers related to interrupt
+	 * status, mask etc
+	 */
+	if (reg < 0x100)
+		return true;
+
+	/* IIR Coeff registers are not cacheable */
+	if ((reg >= WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) &&
+	    (reg <= WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL))
+		return true;
+
+	if ((reg >= WCD9335_CDC_ANC0_IIR_COEFF_1_CTL) &&
+	    (reg <= WCD9335_CDC_ANC0_FB_GAIN_CTL))
+		return true;
+
+	if ((reg >= WCD9335_CDC_ANC1_IIR_COEFF_1_CTL) &&
+	    (reg <= WCD9335_CDC_ANC1_FB_GAIN_CTL))
+		return true;
+	/*
+	 * CPE inbox and outbox registers are volatile
+	 * since they can be updated in the codec hardware
+	 * to indicate CPE status
+	 */
+	if (reg >= WCD9335_CPE_SS_MEM_PTR_0 &&
+	    reg <= WCD9335_CPE_SS_OUTBOX2_ACK)
+		return true;
+
+	if (reg >= WCD9335_RCO_CAL_OUT_1 &&
+	    reg <= WCD9335_RCO_CAL_OUT_5)
+		return true;
+
+	switch (reg) {
+	case WCD9335_CPE_SS_INBOX1_TRG:
+	case WCD9335_CPE_SS_INBOX2_TRG:
+	case WCD9335_SWR_AHB_BRIDGE_WR_DATA_0:
+	case WCD9335_SWR_AHB_BRIDGE_WR_DATA_1:
+	case WCD9335_SWR_AHB_BRIDGE_WR_DATA_2:
+	case WCD9335_SWR_AHB_BRIDGE_WR_DATA_3:
+	case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0:
+	case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1:
+	case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2:
+	case WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3:
+	case WCD9335_SWR_AHB_BRIDGE_RD_DATA_0:
+	case WCD9335_SWR_AHB_BRIDGE_RD_DATA_1:
+	case WCD9335_SWR_AHB_BRIDGE_RD_DATA_2:
+	case WCD9335_SWR_AHB_BRIDGE_RD_DATA_3:
+	case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0:
+	case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1:
+	case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2:
+	case WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3:
+	case WCD9335_ANA_BIAS:
+	case WCD9335_ANA_CLK_TOP:
+	case WCD9335_ANA_RCO:
+	case WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL:
+	case WCD9335_ANA_MBHC_RESULT_3:
+	case WCD9335_ANA_MBHC_RESULT_2:
+	case WCD9335_ANA_MBHC_RESULT_1:
+	case WCD9335_ANA_MBHC_MECH:
+	case WCD9335_ANA_MBHC_ELECT:
+	case WCD9335_ANA_MBHC_ZDET:
+	case WCD9335_ANA_MICB2:
+	case WCD9335_CPE_SS_SS_ERROR_INT_STATUS:
+	case WCD9335_CPE_SS_SS_ERROR_INT_MASK:
+	case WCD9335_CPE_SS_SS_ERROR_INT_CLEAR:
+	case WCD9335_CPE_SS_STATUS:
+	case WCD9335_CPE_SS_BACKUP_INT:
+	case WCD9335_CPE_SS_CFG:
+	case WCD9335_SOC_MAD_MAIN_CTL_1:
+	case WCD9335_SOC_MAD_AUDIO_CTL_3:
+	case WCD9335_SOC_MAD_AUDIO_CTL_4:
+	case WCD9335_FLYBACK_EN:
+	case WCD9335_ANA_RX_SUPPLIES:
+	case WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL:
+	case WCD9335_SIDO_SIDO_CCL_2:
+	case WCD9335_SIDO_SIDO_CCL_4:
+	case WCD9335_DATA_HUB_NATIVE_FIFO_STATUS:
+	case WCD9335_MBHC_FSM_STATUS:
+	case WCD9335_SPLINE_SRC0_STATUS:
+	case WCD9335_SPLINE_SRC1_STATUS:
+	case WCD9335_SPLINE_SRC2_STATUS:
+	case WCD9335_SPLINE_SRC3_STATUS:
+	case WCD9335_SIDO_SIDO_TEST_2:
+	case WCD9335_SIDO_SIDO_CCL_8:
+	case WCD9335_BIAS_VBG_FINE_ADJ:
+	case WCD9335_VBADC_ADC_DOUTMSB:
+	case WCD9335_VBADC_ADC_DOUTLSB:
+	case WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL:
+	case WCD9335_ANA_BUCK_CTL:
+		return true;
+	default:
+		return false;
+	}
+}
+
+struct regmap_config wcd9335_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = wcd9335_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wcd9335_defaults),
+	.max_register = WCD9335_MAX_REGISTER,
+	.volatile_reg = wcd9335_is_volatile_register,
+	.readable_reg = wcd9335_is_readable_register,
+	.can_multi_write = true,
+};
diff --git a/drivers/mfd/wcd9335-tables.c b/drivers/mfd/wcd9335-tables.c
new file mode 100644
index 0000000..f5c32ef
--- /dev/null
+++ b/drivers/mfd/wcd9335-tables.c
@@ -0,0 +1,1325 @@
+/*
+ * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mfd/wcd9335/registers.h>
+
+#define WCD9335_REG(reg)  ((reg) & 0xFF)
+
+const u8 wcd9335_page0_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE0_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_CLK_BYPASS)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_CLK_GATE)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_CLK_MCLK_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_RST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_INT_MASK)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_INT_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CODEC_RPM_INT_CLEAR)] = 0,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_I2S_CLK)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_SYNC)] = 1,
+	[WCD9335_REG(WCD9335_DATA_HUB_NATIVE_FIFO_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_INTR_CFG)] = 1,
+	[WCD9335_REG(WCD9335_INTR_CLR_COMMIT)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN1_MASK0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_MASK1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_MASK2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_MASK3)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_STATUS0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_STATUS1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_STATUS2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_STATUS3)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN1_CLEAR0)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN1_CLEAR1)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN1_CLEAR2)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN1_CLEAR3)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN2_MASK0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_MASK1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_MASK2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_MASK3)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_STATUS0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_STATUS1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_STATUS2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_STATUS3)] = 1,
+	[WCD9335_REG(WCD9335_INTR_PIN2_CLEAR0)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN2_CLEAR1)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN2_CLEAR2)] = 0,
+	[WCD9335_REG(WCD9335_INTR_PIN2_CLEAR3)] = 0,
+	[WCD9335_REG(WCD9335_INTR_LEVEL0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_LEVEL1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_LEVEL2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_LEVEL3)] = 1,
+	[WCD9335_REG(WCD9335_INTR_BYPASS0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_BYPASS1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_BYPASS2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_BYPASS3)] = 1,
+	[WCD9335_REG(WCD9335_INTR_SET0)] = 1,
+	[WCD9335_REG(WCD9335_INTR_SET1)] = 1,
+	[WCD9335_REG(WCD9335_INTR_SET2)] = 1,
+	[WCD9335_REG(WCD9335_INTR_SET3)] = 1,
+};
+
+const u8 wcd9335_page1_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE1_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_8)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_USER_CTL_9)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_L_VAL_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_DSM_FRAC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_CONFIG_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_TEST_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_FREQ_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_SSC_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_FLL_MODE)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_STATUS_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_STATUS_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_STATUS_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_FLL_STATUS_3)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_8)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_USER_CTL_9)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_L_VAL_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_DSM_FRAC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_CONFIG_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_TEST_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_FREQ_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_SSC_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_FLL_MODE)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_STATUS_0)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_STATUS_1)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_STATUS_2)] = 1,
+	[WCD9335_REG(WCD9335_I2S_FLL_STATUS_3)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_8)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_USER_CTL_9)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_L_VAL_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_DSM_FRAC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_CONFIG_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_TEST_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_FREQ_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_SSC_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_FLL_MODE)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_STATUS_0)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_STATUS_1)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_STATUS_2)] = 1,
+	[WCD9335_REG(WCD9335_SB_FLL_STATUS_3)] = 1,
+};
+
+const u8 wcd9335_page2_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE2_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_PTR_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_4)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_5)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_6)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_7)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_8)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_9)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_10)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_11)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_12)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_13)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_14)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MEM_BANK_15)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_TRG)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_TRG)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_0)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_1)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_2)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_3)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_4)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_5)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_6)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_7)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_8)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_9)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_10)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_11)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_12)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_13)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_14)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX1_15)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_4)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_5)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_6)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_7)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_8)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_9)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_10)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_11)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_12)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_13)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_14)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_15)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_0)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_1)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_2)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_3)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_4)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_5)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_6)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_7)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_8)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_9)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_10)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_11)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_12)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_13)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_14)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_INBOX2_15)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_0)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_1)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_2)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_3)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_4)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_5)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_6)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_7)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_8)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_9)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_10)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_11)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_12)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_13)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_14)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_15)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX1_ACK)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_OUTBOX2_ACK)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_EC_BUF_INT_PERIOD)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_US_BUF_INT_PERIOD)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_US_EC_MUX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_MAD_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_CPAR_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_TX_PP_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_DMIC0_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_DMIC1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_DMIC2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_DMIC_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_SVA_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_CPAR_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_WDOG_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_BACKUP_INT)] = 0,
+	[WCD9335_REG(WCD9335_CPE_SS_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_CPE_OCD_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_MASK)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CPE_SS_SS_ERROR_INT_CLEAR)] = 0,
+	[WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_MAIN_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_CTL_8)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_ULTR_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_4)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_5)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_6)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_7)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_CTL_8)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL)] = 1,
+	[WCD9335_REG(WCD9335_SOC_MAD_INP_SEL)] = 1,
+};
+
+const u8 wcd9335_page6_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE6_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_ANA_BIAS)] = 1,
+	[WCD9335_REG(WCD9335_ANA_CLK_TOP)] = 1,
+	[WCD9335_REG(WCD9335_ANA_RCO)] = 1,
+	[WCD9335_REG(WCD9335_ANA_BUCK_VOUT_A)] = 1,
+	[WCD9335_REG(WCD9335_ANA_BUCK_VOUT_D)] = 1,
+	[WCD9335_REG(WCD9335_ANA_BUCK_CTL)] = 1,
+	[WCD9335_REG(WCD9335_ANA_BUCK_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_ANA_RX_SUPPLIES)] = 1,
+	[WCD9335_REG(WCD9335_ANA_HPH)] = 1,
+	[WCD9335_REG(WCD9335_ANA_EAR)] = 1,
+	[WCD9335_REG(WCD9335_ANA_LO_1_2)] = 1,
+	[WCD9335_REG(WCD9335_ANA_LO_3_4)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MAD_SETUP)] = 1,
+	[WCD9335_REG(WCD9335_ANA_AMIC1)] = 1,
+	[WCD9335_REG(WCD9335_ANA_AMIC2)] = 1,
+	[WCD9335_REG(WCD9335_ANA_AMIC3)] = 1,
+	[WCD9335_REG(WCD9335_ANA_AMIC4)] = 1,
+	[WCD9335_REG(WCD9335_ANA_AMIC5)] = 1,
+	[WCD9335_REG(WCD9335_ANA_AMIC6)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_MECH)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_ELECT)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_ZDET)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_RESULT_1)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_RESULT_2)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_RESULT_3)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN0)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN1)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN2)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN3)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN4)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN5)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN6)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MBHC_BTN7)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MICB1)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MICB2)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MICB2_RAMP)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MICB3)] = 1,
+	[WCD9335_REG(WCD9335_ANA_MICB4)] = 1,
+	[WCD9335_REG(WCD9335_ANA_VBADC)] = 1,
+	[WCD9335_REG(WCD9335_BIAS_CTL)] = 1,
+	[WCD9335_REG(WCD9335_BIAS_VBG_FINE_ADJ)] = 1,
+	[WCD9335_REG(WCD9335_CLOCK_TEST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CTRL_1)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CTRL_2)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_1)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_2)] = 1,
+	[WCD9335_REG(WCD9335_RCO_TEST_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_OUT_1)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_OUT_2)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_OUT_3)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_OUT_4)] = 1,
+	[WCD9335_REG(WCD9335_RCO_CAL_OUT_5)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_MODE_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_MODE_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_MODE_3)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_MODE_4)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_VCL_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_VCL_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_VCL_3)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_3)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_4)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_5)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_6)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_7)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_8)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_9)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CCL_10)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_FILTER_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_DRIVER_3)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_EXT_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_CAL_CODE_OUT_2)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_TEST_1)] = 1,
+	[WCD9335_REG(WCD9335_SIDO_SIDO_TEST_2)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_PLUG_DETECT_CTL)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_ZDET_ANA_CTL)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_ZDET_RAMP_CTL)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_FSM_DEBUG)] = 1,
+	[WCD9335_REG(WCD9335_MBHC_TEST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_SUBBLOCK_EN)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_IBIAS_FE)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_BIAS_ADC)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_FE_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_ADC_REF)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_ADC_IO)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_ADC_SAR)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_DEBUG)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_ADC_DOUTMSB)] = 1,
+	[WCD9335_REG(WCD9335_VBADC_ADC_DOUTLSB)] = 1,
+	[WCD9335_REG(WCD9335_LDOH_MODE)] = 1,
+	[WCD9335_REG(WCD9335_LDOH_BIAS)] = 1,
+	[WCD9335_REG(WCD9335_LDOH_STB_LOADS)] = 1,
+	[WCD9335_REG(WCD9335_LDOH_SLOWRAMP)] = 1,
+	[WCD9335_REG(WCD9335_MICB1_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_MICB1_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_MICB1_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_MICB2_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_MICB2_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_MICB2_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_MICB3_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_MICB3_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_MICB3_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_MICB4_TEST_CTL_1)] = 1,
+	[WCD9335_REG(WCD9335_MICB4_TEST_CTL_2)] = 1,
+	[WCD9335_REG(WCD9335_MICB4_TEST_CTL_3)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_ADC_VCM)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_BIAS_ATEST)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_ADC_INT1_IB)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_ADC_INT2_IB)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_CTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_START)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_9P6M)] = 1,
+	[WCD9335_REG(WCD9335_TX_COM_TXFE_DIV_STOP_12P288M)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_TEST_EN)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_ADC_IB)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_ATEST_REFCTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_TEST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_TEST_BLK_EN)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_TXFE_CLKDIV)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_SAR1_ERR)] = 1,
+	[WCD9335_REG(WCD9335_TX_1_2_SAR2_ERR)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_TEST_EN)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_ADC_IB)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_ATEST_REFCTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_TEST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_TEST_BLK_EN)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_TXFE_CLKDIV)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_SAR1_ERR)] = 1,
+	[WCD9335_REG(WCD9335_TX_3_4_SAR2_ERR)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_TEST_EN)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_ADC_IB)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_ATEST_REFCTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_TEST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_TEST_BLK_EN)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_TXFE_CLKDIV)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_SAR1_ERR)] = 1,
+	[WCD9335_REG(WCD9335_TX_5_6_SAR2_ERR)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_MODE_1)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_MODE_2)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_MODE_3)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_1)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_VCL_2)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_1)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_2)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_3)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_4)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_CTRL_CCL_5)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_BUCK_TMUX_A_D)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_BUCK_SW_DRV_CNTL)] = 1,
+	[WCD9335_REG(WCD9335_CLASSH_SPARE)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_EN)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_1)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_2)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_3)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_4)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_5)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_6)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_7)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_8)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_CTRL_9)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_1)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_2)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_3)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_VNEG_DAC_CTRL_4)] = 1,
+	[WCD9335_REG(WCD9335_FLYBACK_TEST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_RX_AUX_SW_CTL)] = 1,
+	[WCD9335_REG(WCD9335_RX_PA_AUX_IN_CONN)] = 1,
+	[WCD9335_REG(WCD9335_RX_TIMER_DIV)] = 1,
+	[WCD9335_REG(WCD9335_RX_OCP_CTL)] = 1,
+	[WCD9335_REG(WCD9335_RX_OCP_COUNT)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_EAR_DAC)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_EAR_AMP)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_HPH_LDO)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_HPH_PA)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_HPH_RDAC_LDO)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_HPH_CNP1)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_HPH_LOWPOWER)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_PA)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_REF)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_DIFFLO_LDO)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_SELO_DAC_PA)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_BUCK_RST)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_BUCK_VREF_ERRAMP)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_FLYB_ERRAMP)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_FLYB_BUFF)] = 1,
+	[WCD9335_REG(WCD9335_RX_BIAS_FLYB_MID_RST)] = 1,
+	[WCD9335_REG(WCD9335_HPH_L_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_HPH_R_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_HPH_CNP_EN)] = 1,
+	[WCD9335_REG(WCD9335_HPH_CNP_WG_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_CNP_WG_TIME)] = 1,
+	[WCD9335_REG(WCD9335_HPH_OCP_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_AUTO_CHOP)] = 1,
+	[WCD9335_REG(WCD9335_HPH_CHOP_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_PA_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_HPH_PA_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_HPH_L_EN)] = 1,
+	[WCD9335_REG(WCD9335_HPH_L_TEST)] = 1,
+	[WCD9335_REG(WCD9335_HPH_L_ATEST)] = 1,
+	[WCD9335_REG(WCD9335_HPH_R_EN)] = 1,
+	[WCD9335_REG(WCD9335_HPH_R_TEST)] = 1,
+	[WCD9335_REG(WCD9335_HPH_R_ATEST)] = 1,
+	[WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_HPH_RDAC_CLK_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_HPH_RDAC_LDO_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_REFBUFF_UHQA_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_REFBUFF_LP_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_L_DAC_CTL)] = 1,
+	[WCD9335_REG(WCD9335_HPH_R_DAC_CTL)] = 1,
+	[WCD9335_REG(WCD9335_EAR_EN_REG)] = 1,
+	[WCD9335_REG(WCD9335_EAR_CMBUFF)] = 1,
+	[WCD9335_REG(WCD9335_EAR_ICTL)] = 1,
+	[WCD9335_REG(WCD9335_EAR_EN_DBG_CTL)] = 1,
+	[WCD9335_REG(WCD9335_EAR_CNP)] = 1,
+	[WCD9335_REG(WCD9335_EAR_DAC_CTL_ATEST)] = 1,
+	[WCD9335_REG(WCD9335_EAR_STATUS_REG)] = 1,
+	[WCD9335_REG(WCD9335_EAR_OUT_SHORT)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_MISC)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_LO2_COMPANDER)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_LO1_COMPANDER)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_COMMON)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_BYPASS_EN)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_CNP)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_CORE_OUT_PROG)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_LDO_OUT_PROG)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_COM_PA_FREQ)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_RESERVED_REG)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_1)] = 1,
+	[WCD9335_REG(WCD9335_DIFF_LO_LO1_STATUS_2)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_COM1)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_COM2)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_LO3_GAIN)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_LO3_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_LO4_GAIN)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_LO4_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_LO3_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_SE_LO_LO4_STATUS)] = 1,
+};
+
+const u8 wcd9335_page10_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE10_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_CLK_RESET_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_MODE_1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_MODE_2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_FF_SHIFT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_FB_SHIFT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_A_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_LPF_FF_B_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_LPF_FB_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_SMLPF_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_IIR_ADAPT_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_IIR_COEFF_2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_FF_A_GAIN_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_FF_B_GAIN_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC0_FB_GAIN_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_CLK_RESET_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_MODE_1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_MODE_2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_FF_SHIFT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_FB_SHIFT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_A_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_LPF_FF_B_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_LPF_FB_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_SMLPF_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_IIR_ADAPT_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_IIR_COEFF_2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_FF_A_GAIN_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_FF_B_GAIN_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_ANC1_FB_GAIN_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX0_TX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX1_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX2_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX3_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX4_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX5_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX6_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX7_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_192_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX8_TX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0)] = 1,
+};
+
+const u8 wcd9335_page11_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE11_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER1_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER2_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER3_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER4_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER5_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER6_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER7_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_COMPANDER8_CTL7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX0_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX1_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX2_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX3_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX4_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX5_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX6_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX7_RX_PATH_MIX_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_VOL_MIX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_SEC7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX8_RX_PATH_MIX_SEC1)] = 1,
+};
+
+const u8 wcd9335_page12_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE12_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_CRC)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_DLY_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_DECAY_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_PA)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_PA)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_HPH_V_HD)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_EAR_V_HD)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_K1_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_K1_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_K2_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_K2_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_IDLE_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_IDLE_HPH)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_IDLE_EAR)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_TEST0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_TEST1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLSH_OVR_VREF)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST0_BOOST_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_BOOST1_BOOST_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_0)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_1)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_2)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_DATA_3)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_0)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_1)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_2)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_RD_DATA_3)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG)] = 1,
+	[WCD9335_REG(WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_CFG)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_ADC_CAL3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_PK_EST3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_RF_PROC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_TAC4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_DEBUG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON)] = 0,
+	[WCD9335_REG(WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC0_CLK_RST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC0_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC1_CLK_RST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC1_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC2_CLK_RST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC2_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC3_CLK_RST_CTL_0)] = 1,
+	[WCD9335_REG(WCD9335_SPLINE_SRC3_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] = 1,
+};
+
+const u8 wcd9335_page13_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE13_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_ANC_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] = 1,
+	[WCD9335_REG(WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG0)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG1)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG2)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG3)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG4)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG5)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG6)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_TOP_CFG7)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_WR_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_LUT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHL_COMP_RD_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_WR_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_LUT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_HPHR_COMP_RD_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_LUT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_LUT)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB)] = 1,
+	[WCD9335_REG(WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB)] = 1,
+};
+
+const u8 wcd9335_page_0x80_reg_readable[WCD9335_PAGE_SIZE] = {
+	[WCD9335_REG(WCD9335_PAGE80_PAGE_REGISTER)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_BIST_MODE_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_RF_PA_ON_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_INTR1_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_INTR2_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_SWR_DATA_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_SWR_CLK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_SLIMBUS_DATA2_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2C_CLK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2C_DATA_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_RX_SD0_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_RX_SD1_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_RX_SCK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_RX_WS_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_TX_SD0_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_TX_SD1_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_TX_SCK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_I2S_TX_WS_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_DMIC1_CLK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_DMIC1_DATA_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_DMIC2_CLK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_DMIC2_DATA_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_DMIC3_CLK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_DMIC3_DATA_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_JTDI_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_JTDO_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_JTMS_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_JTCK_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TLMM_JTRST_PINCFG)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_0)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_1)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_2)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_OE_3)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_0)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_1)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_2)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_CTL_DATA_3)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PAD_DRVCTL)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_PIN_STATUS)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_1)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_NPL_DLY_TEST_2)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_MEM_CTRL)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_BUS_SEL)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_JTAG)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_1)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_2)] = 1,
+	[WCD9335_REG(WCD9335_TEST_DEBUG_DEBUG_EN_3)] = 1,
+};
+
+const u8 *wcd9335_reg[WCD9335_NUM_PAGES] = {
+	[PAGE_0] = wcd9335_page0_reg_readable,
+	[PAGE_1] = wcd9335_page1_reg_readable,
+	[PAGE_2] = wcd9335_page2_reg_readable,
+	[PAGE_6] = wcd9335_page6_reg_readable,
+	[PAGE_10] = wcd9335_page10_reg_readable,
+	[PAGE_11] = wcd9335_page11_reg_readable,
+	[PAGE_12] = wcd9335_page12_reg_readable,
+	[PAGE_13] = wcd9335_page13_reg_readable,
+	[PAGE_0X80] = wcd9335_page_0x80_reg_readable,
+};
diff --git a/drivers/mfd/wcd934x-regmap.c b/drivers/mfd/wcd934x-regmap.c
new file mode 100644
index 0000000..fbaf05e
--- /dev/null
+++ b/drivers/mfd/wcd934x-regmap.c
@@ -0,0 +1,1945 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wcd9xxx-regmap.h"
+
+
+static const struct reg_sequence wcd934x_1_1_defaults[] = {
+	{ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,             0x01 },
+	{ WCD934X_BIAS_VBG_FINE_ADJ,                        0x75 },
+	{ WCD934X_HPH_REFBUFF_LP_CTL,                       0x0E },
+	{ WCD934X_EAR_DAC_CTL_ATEST,                        0x08 },
+	{ WCD934X_SIDO_NEW_VOUT_A_STARTUP,                  0x17 },
+	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,                0x40 },
+	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L,               0x81 },
+	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R,               0x81 },
+};
+
+static const struct reg_default wcd934x_defaults[] = {
+	{ WCD934X_PAGE0_PAGE_REGISTER,                      0x00 },
+	{ WCD934X_CODEC_RPM_CLK_BYPASS,                     0x00 },
+	{ WCD934X_CODEC_RPM_CLK_GATE,                       0x1f },
+	{ WCD934X_CODEC_RPM_CLK_MCLK_CFG,                   0x00 },
+	{ WCD934X_CODEC_RPM_CLK_MCLK2_CFG,                  0x02 },
+	{ WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL,                0x00 },
+	{ WCD934X_CODEC_RPM_RST_CTL,                        0x00 },
+	{ WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,             0x04 },
+	{ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,             0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1,             0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,             0x08 },
+	{ WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3,             0x01 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_CTL,                 0x10 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0,               0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1,               0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10,           0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11,           0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12,           0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13,           0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14,           0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15,           0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS,              0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO,      0x0d },
+	{ WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3,            0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL,        0xcc },
+	{ WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL,       0xcc },
+	{ WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE,                0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN,               0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE,               0x00 },
+	{ WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA,             0x00 },
+	{ WCD934X_DATA_HUB_RX0_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX1_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX2_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX3_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX4_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX5_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX6_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_RX7_CFG,                         0x00 },
+	{ WCD934X_DATA_HUB_SB_TX0_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX1_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX2_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX3_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX4_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX5_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX6_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX7_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX8_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX9_INP_CFG,                  0x00 },
+	{ WCD934X_DATA_HUB_SB_TX10_INP_CFG,                 0x00 },
+	{ WCD934X_DATA_HUB_SB_TX11_INP_CFG,                 0x00 },
+	{ WCD934X_DATA_HUB_SB_TX13_INP_CFG,                 0x00 },
+	{ WCD934X_DATA_HUB_SB_TX14_INP_CFG,                 0x00 },
+	{ WCD934X_DATA_HUB_SB_TX15_INP_CFG,                 0x00 },
+	{ WCD934X_DATA_HUB_I2S_TX0_CFG,                     0x00 },
+	{ WCD934X_DATA_HUB_I2S_TX1_0_CFG,                   0x00 },
+	{ WCD934X_DATA_HUB_I2S_TX1_1_CFG,                   0x00 },
+	{ WCD934X_DATA_HUB_I2S_0_CTL,                       0x0c },
+	{ WCD934X_DATA_HUB_I2S_1_CTL,                       0x0c },
+	{ WCD934X_DATA_HUB_I2S_2_CTL,                       0x0c },
+	{ WCD934X_DATA_HUB_I2S_3_CTL,                       0x0c },
+	{ WCD934X_DATA_HUB_I2S_CLKSRC_CTL,                  0x00 },
+	{ WCD934X_DATA_HUB_I2S_COMMON_CTL,                  0x00 },
+	{ WCD934X_DATA_HUB_I2S_0_TDM_CTL,                   0x00 },
+	{ WCD934X_DATA_HUB_I2S_STATUS,                      0x00 },
+	{ WCD934X_DMA_RDMA_CTL_0,                           0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_RDMA_0,                    0xff },
+	{ WCD934X_DMA_CH_0_1_CFG_RDMA_0,                    0xff },
+	{ WCD934X_DMA_RDMA_CTL_1,                           0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_RDMA_1,                    0xff },
+	{ WCD934X_DMA_CH_0_1_CFG_RDMA_1,                    0xff },
+	{ WCD934X_DMA_RDMA_CTL_2,                           0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_RDMA_2,                    0xff },
+	{ WCD934X_DMA_CH_0_1_CFG_RDMA_2,                    0xff },
+	{ WCD934X_DMA_RDMA_CTL_3,                           0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_RDMA_3,                    0xff },
+	{ WCD934X_DMA_CH_0_1_CFG_RDMA_3,                    0xff },
+	{ WCD934X_DMA_RDMA_CTL_4,                           0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_RDMA_4,                    0xff },
+	{ WCD934X_DMA_CH_0_1_CFG_RDMA_4,                    0xff },
+	{ WCD934X_DMA_RDMA4_PRT_CFG,                       0x00 },
+	{ WCD934X_DMA_RDMA_SBTX0_7_CFG,                    0x00 },
+	{ WCD934X_DMA_RDMA_SBTX8_11_CFG,                   0x00 },
+	{ WCD934X_DMA_WDMA_CTL_0,                          0x00 },
+	{ WCD934X_DMA_CH_4_5_CFG_WDMA_0,                   0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_WDMA_0,                   0x00 },
+	{ WCD934X_DMA_CH_0_1_CFG_WDMA_0,                   0x00 },
+	{ WCD934X_DMA_WDMA_CTL_1,                          0x00 },
+	{ WCD934X_DMA_CH_4_5_CFG_WDMA_1,                   0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_WDMA_1,                   0x00 },
+	{ WCD934X_DMA_CH_0_1_CFG_WDMA_1,                   0x00 },
+	{ WCD934X_DMA_WDMA_CTL_2,                          0x00 },
+	{ WCD934X_DMA_CH_4_5_CFG_WDMA_2,                   0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_WDMA_2,                   0x00 },
+	{ WCD934X_DMA_CH_0_1_CFG_WDMA_2,                   0x00 },
+	{ WCD934X_DMA_WDMA_CTL_3,                          0x00 },
+	{ WCD934X_DMA_CH_4_5_CFG_WDMA_3,                   0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_WDMA_3,                   0x00 },
+	{ WCD934X_DMA_CH_0_1_CFG_WDMA_3,                   0x00 },
+	{ WCD934X_DMA_WDMA_CTL_4,                          0x00 },
+	{ WCD934X_DMA_CH_4_5_CFG_WDMA_4,                   0x00 },
+	{ WCD934X_DMA_CH_2_3_CFG_WDMA_4,                   0x00 },
+	{ WCD934X_DMA_CH_0_1_CFG_WDMA_4,                   0x00 },
+	{ WCD934X_DMA_WDMA0_PRT_CFG,                       0x00 },
+	{ WCD934X_DMA_WDMA3_PRT_CFG,                       0x00 },
+	{ WCD934X_DMA_WDMA4_PRT0_3_CFG,                    0x00 },
+	{ WCD934X_DMA_WDMA4_PRT4_7_CFG,                    0x00 },
+	{ WCD934X_PAGE1_PAGE_REGISTER,                     0x00 },
+	{ WCD934X_CPE_FLL_USER_CTL_0,                      0x71 },
+	{ WCD934X_CPE_FLL_USER_CTL_1,                      0x34 },
+	{ WCD934X_CPE_FLL_USER_CTL_2,                      0x0b },
+	{ WCD934X_CPE_FLL_USER_CTL_3,                      0x02 },
+	{ WCD934X_CPE_FLL_USER_CTL_4,                      0x04 },
+	{ WCD934X_CPE_FLL_USER_CTL_5,                      0x02 },
+	{ WCD934X_CPE_FLL_USER_CTL_6,                      0x6e },
+	{ WCD934X_CPE_FLL_USER_CTL_7,                      0x00 },
+	{ WCD934X_CPE_FLL_USER_CTL_8,                      0x94 },
+	{ WCD934X_CPE_FLL_USER_CTL_9,                      0x50 },
+	{ WCD934X_CPE_FLL_L_VAL_CTL_0,                     0x53 },
+	{ WCD934X_CPE_FLL_L_VAL_CTL_1,                     0x00 },
+	{ WCD934X_CPE_FLL_DSM_FRAC_CTL_0,                  0x00 },
+	{ WCD934X_CPE_FLL_DSM_FRAC_CTL_1,                  0xff },
+	{ WCD934X_CPE_FLL_CONFIG_CTL_0,                    0x6b },
+	{ WCD934X_CPE_FLL_CONFIG_CTL_1,                    0x05 },
+	{ WCD934X_CPE_FLL_CONFIG_CTL_2,                    0x08 },
+	{ WCD934X_CPE_FLL_CONFIG_CTL_3,                    0x00 },
+	{ WCD934X_CPE_FLL_CONFIG_CTL_4,                    0x10 },
+	{ WCD934X_CPE_FLL_TEST_CTL_0,                      0x80 },
+	{ WCD934X_CPE_FLL_TEST_CTL_1,                      0x00 },
+	{ WCD934X_CPE_FLL_TEST_CTL_2,                      0x00 },
+	{ WCD934X_CPE_FLL_TEST_CTL_3,                      0x00 },
+	{ WCD934X_CPE_FLL_TEST_CTL_4,                      0x00 },
+	{ WCD934X_CPE_FLL_TEST_CTL_5,                      0x00 },
+	{ WCD934X_CPE_FLL_TEST_CTL_6,                      0x00 },
+	{ WCD934X_CPE_FLL_TEST_CTL_7,                      0x33 },
+	{ WCD934X_CPE_FLL_FREQ_CTL_0,                      0x00 },
+	{ WCD934X_CPE_FLL_FREQ_CTL_1,                      0x00 },
+	{ WCD934X_CPE_FLL_FREQ_CTL_2,                      0x00 },
+	{ WCD934X_CPE_FLL_FREQ_CTL_3,                      0x00 },
+	{ WCD934X_CPE_FLL_SSC_CTL_0,                       0x00 },
+	{ WCD934X_CPE_FLL_SSC_CTL_1,                       0x00 },
+	{ WCD934X_CPE_FLL_SSC_CTL_2,                       0x00 },
+	{ WCD934X_CPE_FLL_SSC_CTL_3,                       0x00 },
+	{ WCD934X_CPE_FLL_FLL_MODE,                        0x20 },
+	{ WCD934X_CPE_FLL_STATUS_0,                        0x00 },
+	{ WCD934X_CPE_FLL_STATUS_1,                        0x00 },
+	{ WCD934X_CPE_FLL_STATUS_2,                        0x00 },
+	{ WCD934X_CPE_FLL_STATUS_3,                        0x00 },
+	{ WCD934X_I2S_FLL_USER_CTL_0,                      0x41 },
+	{ WCD934X_I2S_FLL_USER_CTL_1,                      0x94 },
+	{ WCD934X_I2S_FLL_USER_CTL_2,                      0x08 },
+	{ WCD934X_I2S_FLL_USER_CTL_3,                      0x02 },
+	{ WCD934X_I2S_FLL_USER_CTL_4,                      0x04 },
+	{ WCD934X_I2S_FLL_USER_CTL_5,                      0x02 },
+	{ WCD934X_I2S_FLL_USER_CTL_6,                      0x40 },
+	{ WCD934X_I2S_FLL_USER_CTL_7,                      0x00 },
+	{ WCD934X_I2S_FLL_USER_CTL_8,                      0x5f },
+	{ WCD934X_I2S_FLL_USER_CTL_9,                      0x02 },
+	{ WCD934X_I2S_FLL_L_VAL_CTL_0,                     0x40 },
+	{ WCD934X_I2S_FLL_L_VAL_CTL_1,                     0x00 },
+	{ WCD934X_I2S_FLL_DSM_FRAC_CTL_0,                  0x00 },
+	{ WCD934X_I2S_FLL_DSM_FRAC_CTL_1,                  0xff },
+	{ WCD934X_I2S_FLL_CONFIG_CTL_0,                    0x6b },
+	{ WCD934X_I2S_FLL_CONFIG_CTL_1,                    0x05 },
+	{ WCD934X_I2S_FLL_CONFIG_CTL_2,                    0x08 },
+	{ WCD934X_I2S_FLL_CONFIG_CTL_3,                    0x00 },
+	{ WCD934X_I2S_FLL_CONFIG_CTL_4,                    0x30 },
+	{ WCD934X_I2S_FLL_TEST_CTL_0,                      0x80 },
+	{ WCD934X_I2S_FLL_TEST_CTL_1,                      0x00 },
+	{ WCD934X_I2S_FLL_TEST_CTL_2,                      0x00 },
+	{ WCD934X_I2S_FLL_TEST_CTL_3,                      0x00 },
+	{ WCD934X_I2S_FLL_TEST_CTL_4,                      0x00 },
+	{ WCD934X_I2S_FLL_TEST_CTL_5,                      0x00 },
+	{ WCD934X_I2S_FLL_TEST_CTL_6,                      0x00 },
+	{ WCD934X_I2S_FLL_TEST_CTL_7,                      0xff },
+	{ WCD934X_I2S_FLL_FREQ_CTL_0,                      0x00 },
+	{ WCD934X_I2S_FLL_FREQ_CTL_1,                      0x00 },
+	{ WCD934X_I2S_FLL_FREQ_CTL_2,                      0x00 },
+	{ WCD934X_I2S_FLL_FREQ_CTL_3,                      0x00 },
+	{ WCD934X_I2S_FLL_SSC_CTL_0,                       0x00 },
+	{ WCD934X_I2S_FLL_SSC_CTL_1,                       0x00 },
+	{ WCD934X_I2S_FLL_SSC_CTL_2,                       0x00 },
+	{ WCD934X_I2S_FLL_SSC_CTL_3,                       0x00 },
+	{ WCD934X_I2S_FLL_FLL_MODE,                        0x00 },
+	{ WCD934X_I2S_FLL_STATUS_0,                        0x00 },
+	{ WCD934X_I2S_FLL_STATUS_1,                        0x00 },
+	{ WCD934X_I2S_FLL_STATUS_2,                        0x00 },
+	{ WCD934X_I2S_FLL_STATUS_3,                        0x00 },
+	{ WCD934X_SB_FLL_USER_CTL_0,                       0x41 },
+	{ WCD934X_SB_FLL_USER_CTL_1,                       0x94 },
+	{ WCD934X_SB_FLL_USER_CTL_2,                       0x08 },
+	{ WCD934X_SB_FLL_USER_CTL_3,                       0x02 },
+	{ WCD934X_SB_FLL_USER_CTL_4,                       0x04 },
+	{ WCD934X_SB_FLL_USER_CTL_5,                       0x02 },
+	{ WCD934X_SB_FLL_USER_CTL_6,                       0x40 },
+	{ WCD934X_SB_FLL_USER_CTL_7,                       0x00 },
+	{ WCD934X_SB_FLL_USER_CTL_8,                       0x5e },
+	{ WCD934X_SB_FLL_USER_CTL_9,                       0x01 },
+	{ WCD934X_SB_FLL_L_VAL_CTL_0,                      0x40 },
+	{ WCD934X_SB_FLL_L_VAL_CTL_1,                      0x00 },
+	{ WCD934X_SB_FLL_DSM_FRAC_CTL_0,                   0x00 },
+	{ WCD934X_SB_FLL_DSM_FRAC_CTL_1,                   0xff },
+	{ WCD934X_SB_FLL_CONFIG_CTL_0,                     0x6b },
+	{ WCD934X_SB_FLL_CONFIG_CTL_1,                     0x05 },
+	{ WCD934X_SB_FLL_CONFIG_CTL_2,                     0x08 },
+	{ WCD934X_SB_FLL_CONFIG_CTL_3,                     0x00 },
+	{ WCD934X_SB_FLL_CONFIG_CTL_4,                     0x10 },
+	{ WCD934X_SB_FLL_TEST_CTL_0,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_1,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_2,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_3,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_4,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_5,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_6,                       0x00 },
+	{ WCD934X_SB_FLL_TEST_CTL_7,                       0xff },
+	{ WCD934X_SB_FLL_FREQ_CTL_0,                       0x00 },
+	{ WCD934X_SB_FLL_FREQ_CTL_1,                       0x00 },
+	{ WCD934X_SB_FLL_FREQ_CTL_2,                       0x00 },
+	{ WCD934X_SB_FLL_FREQ_CTL_3,                       0x00 },
+	{ WCD934X_SB_FLL_SSC_CTL_0,                        0x00 },
+	{ WCD934X_SB_FLL_SSC_CTL_1,                        0x00 },
+	{ WCD934X_SB_FLL_SSC_CTL_2,                        0x00 },
+	{ WCD934X_SB_FLL_SSC_CTL_3,                        0x00 },
+	{ WCD934X_SB_FLL_FLL_MODE,                         0x00 },
+	{ WCD934X_SB_FLL_STATUS_0,                         0x00 },
+	{ WCD934X_SB_FLL_STATUS_1,                         0x00 },
+	{ WCD934X_SB_FLL_STATUS_2,                         0x00 },
+	{ WCD934X_SB_FLL_STATUS_3,                         0x00 },
+	{ WCD934X_PAGE2_PAGE_REGISTER,                     0x00 },
+	{ WCD934X_CPE_SS_CPE_CTL,                          0x05 },
+	{ WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0,             0x01 },
+	{ WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1,             0x00 },
+	{ WCD934X_CPE_SS_PWR_CPEFLL_CTL,                   0x02 },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0,         0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1,         0x0f },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE,  0x00 },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0,        0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1,        0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2,        0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3,        0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4,        0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5,        0xff },
+	{ WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN,           0x07 },
+	{ WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,              0x00 },
+	{ WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL,     0x20 },
+	{ WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1,    0x00 },
+	{ WCD934X_CPE_SS_US_BUF_INT_PERIOD,                0x60 },
+	{ WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD,        0x13 },
+	{ WCD934X_CPE_SS_SVA_CFG,                          0x41 },
+	{ WCD934X_CPE_SS_US_CFG,                           0x00 },
+	{ WCD934X_CPE_SS_MAD_CTL,                          0x00 },
+	{ WCD934X_CPE_SS_CPAR_CTL,                         0x00 },
+	{ WCD934X_CPE_SS_DMIC0_CTL,                        0x00 },
+	{ WCD934X_CPE_SS_DMIC1_CTL,                        0x00 },
+	{ WCD934X_CPE_SS_DMIC2_CTL,                        0x00 },
+	{ WCD934X_CPE_SS_DMIC_CFG,                         0x80 },
+	{ WCD934X_CPE_SS_CPAR_CFG,                         0x00 },
+	{ WCD934X_CPE_SS_WDOG_CFG,                         0x01 },
+	{ WCD934X_CPE_SS_BACKUP_INT,                       0x00 },
+	{ WCD934X_CPE_SS_STATUS,                           0x00 },
+	{ WCD934X_CPE_SS_CPE_OCD_CFG,                      0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A,             0xff },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B,             0x3f },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A,             0xff },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B,             0x3f },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A,           0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B,           0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A,           0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B,           0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A,            0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B,            0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A,            0x00 },
+	{ WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B,            0x00 },
+	{ WCD934X_SOC_MAD_MAIN_CTL_1,                      0x00 },
+	{ WCD934X_SOC_MAD_MAIN_CTL_2,                      0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_1,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_2,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_3,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_4,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_5,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_6,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_7,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_CTL_8,                     0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR,               0x00 },
+	{ WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL,               0x40 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_1,                      0x00 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_2,                      0x00 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_3,                      0x00 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_4,                      0x00 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_5,                      0x00 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_6,                      0x00 },
+	{ WCD934X_SOC_MAD_ULTR_CTL_7,                      0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_1,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_2,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_3,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_4,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_5,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_6,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_7,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_CTL_8,                    0x00 },
+	{ WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR,              0x00 },
+	{ WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL,              0x00 },
+	{ WCD934X_SOC_MAD_INP_SEL,                         0x00 },
+	{ WCD934X_PAGE4_PAGE_REGISTER,                     0x00 },
+	{ WCD934X_INTR_CFG,                                0x00 },
+	{ WCD934X_INTR_CLR_COMMIT,                         0x00 },
+	{ WCD934X_INTR_PIN1_MASK0,                         0xff },
+	{ WCD934X_INTR_PIN1_MASK1,                         0xff },
+	{ WCD934X_INTR_PIN1_MASK2,                         0xff },
+	{ WCD934X_INTR_PIN1_MASK3,                         0xff },
+	{ WCD934X_INTR_PIN1_STATUS0,                       0x00 },
+	{ WCD934X_INTR_PIN1_STATUS1,                       0x00 },
+	{ WCD934X_INTR_PIN1_STATUS2,                       0x00 },
+	{ WCD934X_INTR_PIN1_STATUS3,                       0x00 },
+	{ WCD934X_INTR_PIN1_CLEAR0,                        0x00 },
+	{ WCD934X_INTR_PIN1_CLEAR1,                        0x00 },
+	{ WCD934X_INTR_PIN1_CLEAR2,                        0x00 },
+	{ WCD934X_INTR_PIN1_CLEAR3,                        0x00 },
+	{ WCD934X_INTR_PIN2_MASK3,                         0xff },
+	{ WCD934X_INTR_PIN2_STATUS3,                       0x00 },
+	{ WCD934X_INTR_PIN2_CLEAR3,                        0x00 },
+	{ WCD934X_INTR_CPESS_SUMRY_MASK2,                  0xff },
+	{ WCD934X_INTR_CPESS_SUMRY_MASK3,                  0xff },
+	{ WCD934X_INTR_CPESS_SUMRY_STATUS2,                0x00 },
+	{ WCD934X_INTR_CPESS_SUMRY_STATUS3,                0x00 },
+	{ WCD934X_INTR_CPESS_SUMRY_CLEAR2,                 0x00 },
+	{ WCD934X_INTR_CPESS_SUMRY_CLEAR3,                 0x00 },
+	{ WCD934X_INTR_LEVEL0,                             0x03 },
+	{ WCD934X_INTR_LEVEL1,                             0xe0 },
+	{ WCD934X_INTR_LEVEL2,                             0x94 },
+	{ WCD934X_INTR_LEVEL3,                             0x80 },
+	{ WCD934X_INTR_BYPASS0,                            0x00 },
+	{ WCD934X_INTR_BYPASS1,                            0x00 },
+	{ WCD934X_INTR_BYPASS2,                            0x00 },
+	{ WCD934X_INTR_BYPASS3,                            0x00 },
+	{ WCD934X_INTR_SET0,                               0x00 },
+	{ WCD934X_INTR_SET1,                               0x00 },
+	{ WCD934X_INTR_SET2,                               0x00 },
+	{ WCD934X_INTR_SET3,                               0x00 },
+	{ WCD934X_INTR_CODEC_MISC_MASK,                    0x7f },
+	{ WCD934X_INTR_CODEC_MISC_STATUS,                  0x00 },
+	{ WCD934X_INTR_CODEC_MISC_CLEAR,                   0x00 },
+	{ WCD934X_PAGE5_PAGE_REGISTER,                     0x00 },
+	{ WCD934X_SLNQ_DIG_DEVICE,                         0x49 },
+	{ WCD934X_SLNQ_DIG_REVISION,                       0x01 },
+	{ WCD934X_SLNQ_DIG_H_COMMAND,                      0x00 },
+	{ WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB,             0x00 },
+	{ WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB,             0x00 },
+	{ WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB,             0x00 },
+	{ WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB,             0x00 },
+	{ WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB,              0x00 },
+	{ WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB,              0x00 },
+	{ WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB,           0x40 },
+	{ WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB,           0x00 },
+	{ WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB,           0x40 },
+	{ WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB,           0x00 },
+	{ WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB,           0x40 },
+	{ WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB,           0x00 },
+	{ WCD934X_SLNQ_DIG_COMM_CTL,                       0x00 },
+	{ WCD934X_SLNQ_DIG_FRAME_CTRL,                     0x01 },
+	{ WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2,             0x77 },
+	{ WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4,             0x77 },
+	{ WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5,               0x70 },
+	{ WCD934X_SLNQ_DIG_SW_EVENT_RD,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SW_EVENT_CTRL,                  0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_SELECT_1,                   0x12 },
+	{ WCD934X_SLNQ_DIG_PDM_SELECT_2,                   0x34 },
+	{ WCD934X_SLNQ_DIG_PDM_SELECT_3,                   0x55 },
+	{ WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ,              0x01 },
+	{ WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL,          0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL,          0x11 },
+	{ WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB,            0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB,            0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB,            0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB,            0x00 },
+	{ WCD934X_SLNQ_DIG_RAM_CNTRL,                      0x01 },
+	{ WCD934X_SLNQ_DIG_SRAM_BANK,                      0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_0,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_4,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_5,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_6,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_7,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_8,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_9,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_A,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_B,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_C,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_D,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_E,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_F,                    0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_10,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_11,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_12,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_13,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_14,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_15,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_16,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_17,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_18,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_19,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1A,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1B,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1C,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1D,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1E,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_1F,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_20,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_21,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_22,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_23,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_24,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_25,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_26,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_27,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_28,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_29,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2A,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2B,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2C,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2D,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2E,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_2F,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_30,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_31,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_32,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_33,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_34,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_35,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_36,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_37,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_38,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_39,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3A,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3B,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3C,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3D,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3E,                   0x00 },
+	{ WCD934X_SLNQ_DIG_SRAM_BYTE_3F,                   0x00 },
+	{ WCD934X_SLNQ_DIG_TOP_CTRL1,                      0x00 },
+	{ WCD934X_SLNQ_DIG_TOP_CTRL2,                      0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_CTRL,                       0x00 },
+	{ WCD934X_SLNQ_DIG_PDM_MUTE_CTRL,                  0x20 },
+	{ WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL,                0x00 },
+	{ WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS,              0x00 },
+	{ WCD934X_SLNQ_DIG_DEC_BYPASS_FS,                  0x00 },
+	{ WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL,              0x00 },
+	{ WCD934X_SLNQ_DIG_GPOUT_ENABLE,                   0x00 },
+	{ WCD934X_SLNQ_DIG_GPOUT_VAL,                      0x00 },
+	{ WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK,             0x00 },
+	{ WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS,           0x00 },
+	{ WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR,              0x00 },
+	{ WCD934X_SLNQ_DIG_IP_TESTING,                     0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CNTRL,                0x0f },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CNT,                  0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB,              0xff },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB,              0xff },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_MASK0,                0xff },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_MASK1,                0xff },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_MASK2,                0xff },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_MASK3,                0xff },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_MASK4,                0x1f },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_STATUS0,              0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_STATUS1,              0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_STATUS2,              0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_STATUS3,              0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_STATUS4,              0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CLR0,                 0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CLR1,                 0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CLR2,                 0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CLR3,                 0x00 },
+	{ WCD934X_SLNQ_DIG_INTERRUPT_CLR4,                 0x00 },
+	{ WCD934X_ANA_PAGE_REGISTER,                       0x00 },
+	{ WCD934X_ANA_BIAS,                                0x00 },
+	{ WCD934X_ANA_RCO,                                 0x00 },
+	{ WCD934X_ANA_PAGE6_SPARE2,                        0x00 },
+	{ WCD934X_ANA_PAGE6_SPARE3,                        0x00 },
+	{ WCD934X_ANA_BUCK_CTL,                            0x00 },
+	{ WCD934X_ANA_BUCK_STATUS,                         0x00 },
+	{ WCD934X_ANA_RX_SUPPLIES,                         0x00 },
+	{ WCD934X_ANA_HPH,                                 0x0c },
+	{ WCD934X_ANA_EAR,                                 0x00 },
+	{ WCD934X_ANA_LO_1_2,                              0x3c },
+	{ WCD934X_ANA_MAD_SETUP,                           0x01 },
+	{ WCD934X_ANA_AMIC1,                               0x20 },
+	{ WCD934X_ANA_AMIC2,                               0x00 },
+	{ WCD934X_ANA_AMIC3,                               0x20 },
+	{ WCD934X_ANA_AMIC4,                               0x00 },
+	{ WCD934X_ANA_MBHC_MECH,                           0x39 },
+	{ WCD934X_ANA_MBHC_ELECT,                          0x08 },
+	{ WCD934X_ANA_MBHC_ZDET,                           0x00 },
+	{ WCD934X_ANA_MBHC_RESULT_1,                       0x00 },
+	{ WCD934X_ANA_MBHC_RESULT_2,                       0x00 },
+	{ WCD934X_ANA_MBHC_RESULT_3,                       0x00 },
+	{ WCD934X_ANA_MBHC_BTN0,                           0x00 },
+	{ WCD934X_ANA_MBHC_BTN1,                           0x10 },
+	{ WCD934X_ANA_MBHC_BTN2,                           0x20 },
+	{ WCD934X_ANA_MBHC_BTN3,                           0x30 },
+	{ WCD934X_ANA_MBHC_BTN4,                           0x40 },
+	{ WCD934X_ANA_MBHC_BTN5,                           0x50 },
+	{ WCD934X_ANA_MBHC_BTN6,                           0x60 },
+	{ WCD934X_ANA_MBHC_BTN7,                           0x70 },
+	{ WCD934X_ANA_MICB1,                               0x10 },
+	{ WCD934X_ANA_MICB2,                               0x10 },
+	{ WCD934X_ANA_MICB2_RAMP,                          0x00 },
+	{ WCD934X_ANA_MICB3,                               0x10 },
+	{ WCD934X_ANA_MICB4,                               0x10 },
+	{ WCD934X_ANA_VBADC,                               0x00 },
+	{ WCD934X_BIAS_CTL,                                0x28 },
+	{ WCD934X_BIAS_VBG_FINE_ADJ,                       0x65 },
+	{ WCD934X_RCO_CTRL_1,                              0x44 },
+	{ WCD934X_RCO_CTRL_2,                              0x48 },
+	{ WCD934X_RCO_CAL,                                 0x00 },
+	{ WCD934X_RCO_CAL_1,                               0x00 },
+	{ WCD934X_RCO_CAL_2,                               0x00 },
+	{ WCD934X_RCO_TEST_CTRL,                           0x00 },
+	{ WCD934X_RCO_CAL_OUT_1,                           0x00 },
+	{ WCD934X_RCO_CAL_OUT_2,                           0x00 },
+	{ WCD934X_RCO_CAL_OUT_3,                           0x00 },
+	{ WCD934X_RCO_CAL_OUT_4,                           0x00 },
+	{ WCD934X_RCO_CAL_OUT_5,                           0x00 },
+	{ WCD934X_SIDO_MODE_1,                             0x84 },
+	{ WCD934X_SIDO_MODE_2,                             0xfe },
+	{ WCD934X_SIDO_MODE_3,                             0xf6 },
+	{ WCD934X_SIDO_MODE_4,                             0x56 },
+	{ WCD934X_SIDO_VCL_1,                              0x00 },
+	{ WCD934X_SIDO_VCL_2,                              0x6c },
+	{ WCD934X_SIDO_VCL_3,                              0x44 },
+	{ WCD934X_SIDO_CCL_1,                              0x57 },
+	{ WCD934X_SIDO_CCL_2,                              0x92 },
+	{ WCD934X_SIDO_CCL_3,                              0x35 },
+	{ WCD934X_SIDO_CCL_4,                              0x61 },
+	{ WCD934X_SIDO_CCL_5,                              0x6d },
+	{ WCD934X_SIDO_CCL_6,                              0x60 },
+	{ WCD934X_SIDO_CCL_7,                              0x6f },
+	{ WCD934X_SIDO_CCL_8,                              0x6f },
+	{ WCD934X_SIDO_CCL_9,                              0x6e },
+	{ WCD934X_SIDO_CCL_10,                             0x26 },
+	{ WCD934X_SIDO_FILTER_1,                           0x92 },
+	{ WCD934X_SIDO_FILTER_2,                           0x54 },
+	{ WCD934X_SIDO_DRIVER_1,                           0x77 },
+	{ WCD934X_SIDO_DRIVER_2,                           0x55 },
+	{ WCD934X_SIDO_DRIVER_3,                           0x55 },
+	{ WCD934X_SIDO_CAL_CODE_EXT_1,                     0x9c },
+	{ WCD934X_SIDO_CAL_CODE_EXT_2,                     0x82 },
+	{ WCD934X_SIDO_CAL_CODE_OUT_1,                     0x00 },
+	{ WCD934X_SIDO_CAL_CODE_OUT_2,                     0x00 },
+	{ WCD934X_SIDO_TEST_1,                             0x00 },
+	{ WCD934X_SIDO_TEST_2,                             0x00 },
+	{ WCD934X_MBHC_CTL_CLK,                            0x30 },
+	{ WCD934X_MBHC_CTL_ANA,                            0x00 },
+	{ WCD934X_MBHC_CTL_SPARE_1,                        0x00 },
+	{ WCD934X_MBHC_CTL_SPARE_2,                        0x00 },
+	{ WCD934X_MBHC_CTL_BCS,                            0x00 },
+	{ WCD934X_MBHC_STATUS_SPARE_1,                     0x00 },
+	{ WCD934X_MBHC_TEST_CTL,                           0x00 },
+	{ WCD934X_VBADC_SUBBLOCK_EN,                       0xde },
+	{ WCD934X_VBADC_IBIAS_FE,                          0x58 },
+	{ WCD934X_VBADC_BIAS_ADC,                          0x51 },
+	{ WCD934X_VBADC_FE_CTRL,                           0x1c },
+	{ WCD934X_VBADC_ADC_REF,                           0x20 },
+	{ WCD934X_VBADC_ADC_IO,                            0x80 },
+	{ WCD934X_VBADC_ADC_SAR,                           0xff },
+	{ WCD934X_VBADC_DEBUG,                             0x00 },
+	{ WCD934X_LDOH_MODE,                               0x2b },
+	{ WCD934X_LDOH_BIAS,                               0x68 },
+	{ WCD934X_LDOH_STB_LOADS,                          0x00 },
+	{ WCD934X_LDOH_SLOWRAMP,                           0x50 },
+	{ WCD934X_MICB1_TEST_CTL_1,                        0x1a },
+	{ WCD934X_MICB1_TEST_CTL_2,                        0x18 },
+	{ WCD934X_MICB1_TEST_CTL_3,                        0xa4 },
+	{ WCD934X_MICB2_TEST_CTL_1,                        0x1a },
+	{ WCD934X_MICB2_TEST_CTL_2,                        0x18 },
+	{ WCD934X_MICB2_TEST_CTL_3,                        0xa4 },
+	{ WCD934X_MICB3_TEST_CTL_1,                        0x1a },
+	{ WCD934X_MICB3_TEST_CTL_2,                        0x18 },
+	{ WCD934X_MICB3_TEST_CTL_3,                        0xa4 },
+	{ WCD934X_MICB4_TEST_CTL_1,                        0x1a },
+	{ WCD934X_MICB4_TEST_CTL_2,                        0x18 },
+	{ WCD934X_MICB4_TEST_CTL_3,                        0xa4 },
+	{ WCD934X_TX_COM_ADC_VCM,                          0x39 },
+	{ WCD934X_TX_COM_BIAS_ATEST,                       0xc0 },
+	{ WCD934X_TX_COM_ADC_INT1_IB,                      0x6f },
+	{ WCD934X_TX_COM_ADC_INT2_IB,                      0x4f },
+	{ WCD934X_TX_COM_TXFE_DIV_CTL,                     0x2e },
+	{ WCD934X_TX_COM_TXFE_DIV_START,                   0x00 },
+	{ WCD934X_TX_COM_TXFE_DIV_STOP_9P6M,               0xc7 },
+	{ WCD934X_TX_COM_TXFE_DIV_STOP_12P288M,            0xff },
+	{ WCD934X_TX_1_2_TEST_EN,                          0xcc },
+	{ WCD934X_TX_1_2_ADC_IB,                           0x09 },
+	{ WCD934X_TX_1_2_ATEST_REFCTL,                     0x0a },
+	{ WCD934X_TX_1_2_TEST_CTL,                         0x38 },
+	{ WCD934X_TX_1_2_TEST_BLK_EN,                      0xff },
+	{ WCD934X_TX_1_2_TXFE_CLKDIV,                      0x00 },
+	{ WCD934X_TX_1_2_SAR1_ERR,                         0x00 },
+	{ WCD934X_TX_1_2_SAR2_ERR,                         0x00 },
+	{ WCD934X_TX_3_4_TEST_EN,                          0xcc },
+	{ WCD934X_TX_3_4_ADC_IB,                           0x09 },
+	{ WCD934X_TX_3_4_ATEST_REFCTL,                     0x0a },
+	{ WCD934X_TX_3_4_TEST_CTL,                         0x38 },
+	{ WCD934X_TX_3_4_TEST_BLK_EN,                      0xff },
+	{ WCD934X_TX_3_4_TXFE_CLKDIV,                      0x00 },
+	{ WCD934X_TX_3_4_SAR1_ERR,                         0x00 },
+	{ WCD934X_TX_3_4_SAR2_ERR,                         0x00 },
+	{ WCD934X_CLASSH_MODE_1,                           0x40 },
+	{ WCD934X_CLASSH_MODE_2,                           0x3a },
+	{ WCD934X_CLASSH_MODE_3,                           0x00 },
+	{ WCD934X_CLASSH_CTRL_VCL_1,                       0x70 },
+	{ WCD934X_CLASSH_CTRL_VCL_2,                       0x82 },
+	{ WCD934X_CLASSH_CTRL_CCL_1,                       0x31 },
+	{ WCD934X_CLASSH_CTRL_CCL_2,                       0x80 },
+	{ WCD934X_CLASSH_CTRL_CCL_3,                       0x80 },
+	{ WCD934X_CLASSH_CTRL_CCL_4,                       0x51 },
+	{ WCD934X_CLASSH_CTRL_CCL_5,                       0x00 },
+	{ WCD934X_CLASSH_BUCK_TMUX_A_D,                    0x00 },
+	{ WCD934X_CLASSH_BUCK_SW_DRV_CNTL,                 0x77 },
+	{ WCD934X_CLASSH_SPARE,                            0x00 },
+	{ WCD934X_FLYBACK_EN,                              0x4e },
+	{ WCD934X_FLYBACK_VNEG_CTRL_1,                     0x0b },
+	{ WCD934X_FLYBACK_VNEG_CTRL_2,                     0x45 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_3,                     0x74 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_4,                     0x7f },
+	{ WCD934X_FLYBACK_VNEG_CTRL_5,                     0x83 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_6,                     0x98 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_7,                     0xa9 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_8,                     0x68 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_9,                     0x64 },
+	{ WCD934X_FLYBACK_VNEGDAC_CTRL_1,                  0xed },
+	{ WCD934X_FLYBACK_VNEGDAC_CTRL_2,                  0xf0 },
+	{ WCD934X_FLYBACK_VNEGDAC_CTRL_3,                  0xa6 },
+	{ WCD934X_FLYBACK_CTRL_1,                          0x65 },
+	{ WCD934X_FLYBACK_TEST_CTL,                        0x00 },
+	{ WCD934X_RX_AUX_SW_CTL,                           0x00 },
+	{ WCD934X_RX_PA_AUX_IN_CONN,                       0x00 },
+	{ WCD934X_RX_TIMER_DIV,                            0x32 },
+	{ WCD934X_RX_OCP_CTL,                              0x1f },
+	{ WCD934X_RX_OCP_COUNT,                            0x77 },
+	{ WCD934X_RX_BIAS_EAR_DAC,                         0xa0 },
+	{ WCD934X_RX_BIAS_EAR_AMP,                         0xaa },
+	{ WCD934X_RX_BIAS_HPH_LDO,                         0xa9 },
+	{ WCD934X_RX_BIAS_HPH_PA,                          0xaa },
+	{ WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2,               0x8a },
+	{ WCD934X_RX_BIAS_HPH_RDAC_LDO,                    0x88 },
+	{ WCD934X_RX_BIAS_HPH_CNP1,                        0x82 },
+	{ WCD934X_RX_BIAS_HPH_LOWPOWER,                    0x82 },
+	{ WCD934X_RX_BIAS_DIFFLO_PA,                       0x80 },
+	{ WCD934X_RX_BIAS_DIFFLO_REF,                      0x88 },
+	{ WCD934X_RX_BIAS_DIFFLO_LDO,                      0x88 },
+	{ WCD934X_RX_BIAS_SELO_DAC_PA,                     0xa8 },
+	{ WCD934X_RX_BIAS_BUCK_RST,                        0x08 },
+	{ WCD934X_RX_BIAS_BUCK_VREF_ERRAMP,                0x44 },
+	{ WCD934X_RX_BIAS_FLYB_ERRAMP,                     0x40 },
+	{ WCD934X_RX_BIAS_FLYB_BUFF,                       0xaa },
+	{ WCD934X_RX_BIAS_FLYB_MID_RST,                    0x14 },
+	{ WCD934X_HPH_L_STATUS,                            0x04 },
+	{ WCD934X_HPH_R_STATUS,                            0x04 },
+	{ WCD934X_HPH_CNP_EN,                              0x80 },
+	{ WCD934X_HPH_CNP_WG_CTL,                          0x9a },
+	{ WCD934X_HPH_CNP_WG_TIME,                         0x14 },
+	{ WCD934X_HPH_OCP_CTL,                             0x28 },
+	{ WCD934X_HPH_AUTO_CHOP,                           0x16 },
+	{ WCD934X_HPH_CHOP_CTL,                            0x83 },
+	{ WCD934X_HPH_PA_CTL1,                             0x46 },
+	{ WCD934X_HPH_PA_CTL2,                             0x50 },
+	{ WCD934X_HPH_L_EN,                                0x80 },
+	{ WCD934X_HPH_L_TEST,                              0xe0 },
+	{ WCD934X_HPH_L_ATEST,                             0x50 },
+	{ WCD934X_HPH_R_EN,                                0x80 },
+	{ WCD934X_HPH_R_TEST,                              0xe0 },
+	{ WCD934X_HPH_R_ATEST,                             0x54 },
+	{ WCD934X_HPH_RDAC_CLK_CTL1,                       0x99 },
+	{ WCD934X_HPH_RDAC_CLK_CTL2,                       0x9b },
+	{ WCD934X_HPH_RDAC_LDO_CTL,                        0x33 },
+	{ WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL,                0x00 },
+	{ WCD934X_HPH_REFBUFF_UHQA_CTL,                    0xa8 },
+	{ WCD934X_HPH_REFBUFF_LP_CTL,                      0x0a },
+	{ WCD934X_HPH_L_DAC_CTL,                           0x00 },
+	{ WCD934X_HPH_R_DAC_CTL,                           0x00 },
+	{ WCD934X_EAR_EN_REG,                              0x60 },
+	{ WCD934X_EAR_CMBUFF,                              0x05 },
+	{ WCD934X_EAR_ICTL,                                0x40 },
+	{ WCD934X_EAR_EN_DBG_CTL,                          0x00 },
+	{ WCD934X_EAR_CNP,                                 0xe0 },
+	{ WCD934X_EAR_DAC_CTL_ATEST,                       0x00 },
+	{ WCD934X_EAR_STATUS_REG,                          0x04 },
+	{ WCD934X_EAR_EAR_MISC,                            0x28 },
+	{ WCD934X_DIFF_LO_MISC,                            0x03 },
+	{ WCD934X_DIFF_LO_LO2_COMPANDER,                   0x00 },
+	{ WCD934X_DIFF_LO_LO1_COMPANDER,                   0x00 },
+	{ WCD934X_DIFF_LO_COMMON,                          0x40 },
+	{ WCD934X_DIFF_LO_BYPASS_EN,                       0x00 },
+	{ WCD934X_DIFF_LO_CNP,                             0x20 },
+	{ WCD934X_DIFF_LO_CORE_OUT_PROG,                   0xa0 },
+	{ WCD934X_DIFF_LO_LDO_OUT_PROG,                    0x00 },
+	{ WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ,           0x8b },
+	{ WCD934X_DIFF_LO_COM_PA_FREQ,                     0xb0 },
+	{ WCD934X_DIFF_LO_RESERVED_REG,                    0x60 },
+	{ WCD934X_DIFF_LO_LO1_STATUS_1,                    0x00 },
+	{ WCD934X_DIFF_LO_LO1_STATUS_2,                    0x00 },
+	{ WCD934X_ANA_NEW_PAGE_REGISTER,                   0x00 },
+	{ WCD934X_HPH_NEW_ANA_HPH2,                        0x00 },
+	{ WCD934X_HPH_NEW_ANA_HPH3,                        0x00 },
+	{ WCD934X_SLNQ_ANA_EN,                             0x02 },
+	{ WCD934X_SLNQ_ANA_STATUS,                         0x00 },
+	{ WCD934X_SLNQ_ANA_LDO_CONFIG,                     0xea },
+	{ WCD934X_SLNQ_ANA_LDO_OCP_CONFIG,                 0x95 },
+	{ WCD934X_SLNQ_ANA_TX_LDO_CONFIG,                  0xb6 },
+	{ WCD934X_SLNQ_ANA_TX_DRV_CONFIG,                  0x26 },
+	{ WCD934X_SLNQ_ANA_RX_CONFIG_1,                    0x64 },
+	{ WCD934X_SLNQ_ANA_RX_CONFIG_2,                    0x40 },
+	{ WCD934X_SLNQ_ANA_PLL_ENABLES,                    0x00 },
+	{ WCD934X_SLNQ_ANA_PLL_PRESET,                     0x08 },
+	{ WCD934X_SLNQ_ANA_PLL_STATUS,                     0x00 },
+	{ WCD934X_CLK_SYS_PLL_ENABLES,                     0x00 },
+	{ WCD934X_CLK_SYS_PLL_PRESET,                      0x00 },
+	{ WCD934X_CLK_SYS_PLL_STATUS,                      0x00 },
+	{ WCD934X_CLK_SYS_MCLK_PRG,                        0x00 },
+	{ WCD934X_CLK_SYS_MCLK2_PRG1,                      0x00 },
+	{ WCD934X_CLK_SYS_MCLK2_PRG2,                      0x00 },
+	{ WCD934X_CLK_SYS_XO_PRG,                          0x00 },
+	{ WCD934X_CLK_SYS_XO_CAP_XTP,                      0x00 },
+	{ WCD934X_CLK_SYS_XO_CAP_XTM,                      0x00 },
+	{ WCD934X_BOOST_BST_EN_DLY,                        0x40 },
+	{ WCD934X_BOOST_CTRL_ILIM,                         0x9c },
+	{ WCD934X_BOOST_VOUT_SETTING,                      0xca },
+	{ WCD934X_SIDO_NEW_VOUT_A_STARTUP,                 0x05 },
+	{ WCD934X_SIDO_NEW_VOUT_D_STARTUP,                 0x0d },
+	{ WCD934X_SIDO_NEW_VOUT_D_FREQ1,                   0x07 },
+	{ WCD934X_SIDO_NEW_VOUT_D_FREQ2,                   0x00 },
+	{ WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL,            0x00 },
+	{ WCD934X_MBHC_NEW_CTL_1,                          0x02 },
+	{ WCD934X_MBHC_NEW_CTL_2,                          0x05 },
+	{ WCD934X_MBHC_NEW_PLUG_DETECT_CTL,                0xe9 },
+	{ WCD934X_MBHC_NEW_ZDET_ANA_CTL,                   0x0f },
+	{ WCD934X_MBHC_NEW_ZDET_RAMP_CTL,                  0x00 },
+	{ WCD934X_MBHC_NEW_FSM_STATUS,                     0x00 },
+	{ WCD934X_MBHC_NEW_ADC_RESULT,                     0x00 },
+	{ WCD934X_TX_NEW_AMIC_4_5_SEL,                     0x00 },
+	{ WCD934X_VBADC_NEW_ADC_MODE,                      0x10 },
+	{ WCD934X_VBADC_NEW_ADC_DOUTMSB,                   0x00 },
+	{ WCD934X_VBADC_NEW_ADC_DOUTLSB,                   0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,               0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL,                0xa0 },
+	{ WCD934X_HPH_NEW_INT_RDAC_VREF_CTL,               0x10 },
+	{ WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL,           0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_MISC1,                  0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC1,                    0x22 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC2,                    0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC,                0x00 },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER1,                  0xfe },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER2,                  0x02 },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER3,                  0x4e },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER4,                  0x54 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC2,               0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC3,               0x00 },
+	{ WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI,         0x62 },
+	{ WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP,            0x01 },
+	{ WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP,              0x11 },
+	{ WCD934X_SLNQ_INT_ANA_INT_LDO_TEST,               0x0d },
+	{ WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1,            0x85 },
+	{ WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2,            0xb4 },
+	{ WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST,            0x16 },
+	{ WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST,            0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_RX_TEST,                0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS,         0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1,             0x50 },
+	{ WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2,             0x04 },
+	{ WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL,               0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_RESERVED_1,             0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_RESERVED_2,             0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0,      0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1,      0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0,       0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1,       0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0,        0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1,        0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL,              0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL,              0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL,              0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0,          0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG,    0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG,           0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1,          0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG,       0x00 },
+	{ WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG,   0x00 },
+	{ WCD934X_CLK_SYS_INT_POST_DIV_REG0,               0x00 },
+	{ WCD934X_CLK_SYS_INT_POST_DIV_REG1,               0x00 },
+	{ WCD934X_CLK_SYS_INT_REF_DIV_REG0,                0x00 },
+	{ WCD934X_CLK_SYS_INT_REF_DIV_REG1,                0x00 },
+	{ WCD934X_CLK_SYS_INT_FILTER_REG0,                 0x00 },
+	{ WCD934X_CLK_SYS_INT_FILTER_REG1,                 0x00 },
+	{ WCD934X_CLK_SYS_INT_PLL_L_VAL,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_PLL_M_VAL,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_PLL_N_VAL,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_TEST_REG0,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG,             0x00 },
+	{ WCD934X_CLK_SYS_INT_VCO_PROG,                    0x00 },
+	{ WCD934X_CLK_SYS_INT_TEST_REG1,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_LDO_LOCK_CFG,                0x00 },
+	{ WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG,            0x00 },
+	{ WCD934X_CLK_SYS_INT_CLK_TEST1,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_CLK_TEST2,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_CLK_TEST3,                   0x00 },
+	{ WCD934X_CLK_SYS_INT_XO_TEST1,                    0x98 },
+	{ WCD934X_CLK_SYS_INT_XO_TEST2,                    0x00 },
+	{ WCD934X_BOOST_INT_VCOMP_HYST,                    0x02 },
+	{ WCD934X_BOOST_INT_VLOOP_FILTER,                  0xef },
+	{ WCD934X_BOOST_INT_CTRL_IDELTA,                   0xa8 },
+	{ WCD934X_BOOST_INT_CTRL_ILIM_STARTUP,             0x17 },
+	{ WCD934X_BOOST_INT_CTRL_MIN_ONTIME,               0x5f },
+	{ WCD934X_BOOST_INT_CTRL_MAX_ONTIME,               0x88 },
+	{ WCD934X_BOOST_INT_CTRL_TIMING,                   0x0a },
+	{ WCD934X_BOOST_INT_TMUX_A_D,                      0x00 },
+	{ WCD934X_BOOST_INT_SW_DRV_CNTL,                   0xf8 },
+	{ WCD934X_BOOST_INT_SPARE1,                        0x00 },
+	{ WCD934X_BOOST_INT_SPARE2,                        0x00 },
+	{ WCD934X_SIDO_NEW_INT_RAMP_STATUS,                0x00 },
+	{ WCD934X_SIDO_NEW_INT_SPARE_1,                    0x00 },
+	{ WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A,       0x64 },
+	{ WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D,       0x40 },
+	{ WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT,              0x24 },
+	{ WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL,          0x09 },
+	{ WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL,            0x7d },
+	{ WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST,          0x00 },
+	{ WCD934X_SIDO_NEW_INT_RAMP_CTL_A,                 0x14 },
+	{ WCD934X_SIDO_NEW_INT_RAMP_CTL_D,                 0x14 },
+	{ WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD,        0x33 },
+	{ WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1,     0x3f },
+	{ WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2,     0x74 },
+	{ WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3,     0x33 },
+	{ WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1,        0x1d },
+	{ WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2,        0x0a },
+	{ WCD934X_MBHC_NEW_INT_SLNQ_HPF,                   0x50 },
+	{ WCD934X_MBHC_NEW_INT_SLNQ_REF,                   0x24 },
+	{ WCD934X_MBHC_NEW_INT_SLNQ_COMP,                  0x50 },
+	{ WCD934X_MBHC_NEW_INT_SPARE_2,                    0x00 },
+	{ WCD934X_PAGE10_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_CDC_ANC0_CLK_RESET_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC0_MODE_1_CTL,                     0x00 },
+	{ WCD934X_CDC_ANC0_MODE_2_CTL,                     0x00 },
+	{ WCD934X_CDC_ANC0_FF_SHIFT,                       0x00 },
+	{ WCD934X_CDC_ANC0_FB_SHIFT,                       0x00 },
+	{ WCD934X_CDC_ANC0_LPF_FF_A_CTL,                   0x00 },
+	{ WCD934X_CDC_ANC0_LPF_FF_B_CTL,                   0x00 },
+	{ WCD934X_CDC_ANC0_LPF_FB_CTL,                     0x00 },
+	{ WCD934X_CDC_ANC0_SMLPF_CTL,                      0x00 },
+	{ WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL,                0x00 },
+	{ WCD934X_CDC_ANC0_IIR_ADAPT_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC0_IIR_COEFF_1_CTL,                0x00 },
+	{ WCD934X_CDC_ANC0_IIR_COEFF_2_CTL,                0x00 },
+	{ WCD934X_CDC_ANC0_FF_A_GAIN_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC0_FF_B_GAIN_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC0_FB_GAIN_CTL,                    0x00 },
+	{ WCD934X_CDC_ANC0_RC_COMMON_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC0_FIFO_COMMON_CTL,                0x88 },
+	{ WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC0_STATUS_FIFO,                    0x00 },
+	{ WCD934X_CDC_ANC1_CLK_RESET_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC1_MODE_1_CTL,                     0x00 },
+	{ WCD934X_CDC_ANC1_MODE_2_CTL,                     0x00 },
+	{ WCD934X_CDC_ANC1_FF_SHIFT,                       0x00 },
+	{ WCD934X_CDC_ANC1_FB_SHIFT,                       0x00 },
+	{ WCD934X_CDC_ANC1_LPF_FF_A_CTL,                   0x00 },
+	{ WCD934X_CDC_ANC1_LPF_FF_B_CTL,                   0x00 },
+	{ WCD934X_CDC_ANC1_LPF_FB_CTL,                     0x00 },
+	{ WCD934X_CDC_ANC1_SMLPF_CTL,                      0x00 },
+	{ WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL,                0x00 },
+	{ WCD934X_CDC_ANC1_IIR_ADAPT_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC1_IIR_COEFF_1_CTL,                0x00 },
+	{ WCD934X_CDC_ANC1_IIR_COEFF_2_CTL,                0x00 },
+	{ WCD934X_CDC_ANC1_FF_A_GAIN_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC1_FF_B_GAIN_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC1_FB_GAIN_CTL,                    0x00 },
+	{ WCD934X_CDC_ANC1_RC_COMMON_CTL,                  0x00 },
+	{ WCD934X_CDC_ANC1_FIFO_COMMON_CTL,                0x88 },
+	{ WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR,           0x00 },
+	{ WCD934X_CDC_ANC1_STATUS_FIFO,                    0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX0_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX0_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX0_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX0_TX_PATH_SEC7,                    0x25 },
+	{ WCD934X_CDC_TX1_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX1_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX1_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX1_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX1_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX1_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX1_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX2_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX2_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX2_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX2_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX3_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX3_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX3_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX3_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX4_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX4_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX4_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX4_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX5_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX5_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX5_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX5_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX6_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX6_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX6_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX6_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX7_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX7_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX7_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX7_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_TX8_TX_PATH_CFG0,                    0x10 },
+	{ WCD934X_CDC_TX8_TX_PATH_CFG1,                    0x03 },
+	{ WCD934X_CDC_TX8_TX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_192_CTL,                 0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_192_CFG,                 0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC0,                    0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC1,                    0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC2,                    0x01 },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC3,                    0x3c },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC4,                    0x20 },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_TX8_TX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL,              0x02 },
+	{ WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0,             0x00 },
+	{ WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL,             0x02 },
+	{ WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0,            0x00 },
+	{ WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL,             0x02 },
+	{ WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0,            0x00 },
+	{ WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL,             0x02 },
+	{ WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0,            0x00 },
+	{ WCD934X_PAGE11_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_CDC_COMPANDER1_CTL0,                     0x60 },
+	{ WCD934X_CDC_COMPANDER1_CTL1,                     0xdb },
+	{ WCD934X_CDC_COMPANDER1_CTL2,                     0xff },
+	{ WCD934X_CDC_COMPANDER1_CTL3,                     0x35 },
+	{ WCD934X_CDC_COMPANDER1_CTL4,                     0xff },
+	{ WCD934X_CDC_COMPANDER1_CTL5,                     0x00 },
+	{ WCD934X_CDC_COMPANDER1_CTL6,                     0x01 },
+	{ WCD934X_CDC_COMPANDER1_CTL7,                     0x08 },
+	{ WCD934X_CDC_COMPANDER2_CTL0,                     0x60 },
+	{ WCD934X_CDC_COMPANDER2_CTL1,                     0xdb },
+	{ WCD934X_CDC_COMPANDER2_CTL2,                     0xff },
+	{ WCD934X_CDC_COMPANDER2_CTL3,                     0x35 },
+	{ WCD934X_CDC_COMPANDER2_CTL4,                     0xff },
+	{ WCD934X_CDC_COMPANDER2_CTL5,                     0x00 },
+	{ WCD934X_CDC_COMPANDER2_CTL6,                     0x01 },
+	{ WCD934X_CDC_COMPANDER2_CTL7,                     0x08 },
+	{ WCD934X_CDC_COMPANDER3_CTL0,                     0x60 },
+	{ WCD934X_CDC_COMPANDER3_CTL1,                     0xdb },
+	{ WCD934X_CDC_COMPANDER3_CTL2,                     0xff },
+	{ WCD934X_CDC_COMPANDER3_CTL3,                     0x35 },
+	{ WCD934X_CDC_COMPANDER3_CTL4,                     0xff },
+	{ WCD934X_CDC_COMPANDER3_CTL5,                     0x00 },
+	{ WCD934X_CDC_COMPANDER3_CTL6,                     0x01 },
+	{ WCD934X_CDC_COMPANDER3_CTL7,                     0x08 },
+	{ WCD934X_CDC_COMPANDER4_CTL0,                     0x60 },
+	{ WCD934X_CDC_COMPANDER4_CTL1,                     0xdb },
+	{ WCD934X_CDC_COMPANDER4_CTL2,                     0xff },
+	{ WCD934X_CDC_COMPANDER4_CTL3,                     0x35 },
+	{ WCD934X_CDC_COMPANDER4_CTL4,                     0xff },
+	{ WCD934X_CDC_COMPANDER4_CTL5,                     0x00 },
+	{ WCD934X_CDC_COMPANDER4_CTL6,                     0x01 },
+	{ WCD934X_CDC_COMPANDER4_CTL7,                     0x08 },
+	{ WCD934X_CDC_COMPANDER7_CTL0,                     0x60 },
+	{ WCD934X_CDC_COMPANDER7_CTL1,                     0xdb },
+	{ WCD934X_CDC_COMPANDER7_CTL2,                     0xff },
+	{ WCD934X_CDC_COMPANDER7_CTL3,                     0x35 },
+	{ WCD934X_CDC_COMPANDER7_CTL4,                     0xff },
+	{ WCD934X_CDC_COMPANDER7_CTL5,                     0x00 },
+	{ WCD934X_CDC_COMPANDER7_CTL6,                     0x01 },
+	{ WCD934X_CDC_COMPANDER7_CTL7,                     0x08 },
+	{ WCD934X_CDC_COMPANDER8_CTL0,                     0x60 },
+	{ WCD934X_CDC_COMPANDER8_CTL1,                     0xdb },
+	{ WCD934X_CDC_COMPANDER8_CTL2,                     0xff },
+	{ WCD934X_CDC_COMPANDER8_CTL3,                     0x35 },
+	{ WCD934X_CDC_COMPANDER8_CTL4,                     0xff },
+	{ WCD934X_CDC_COMPANDER8_CTL5,                     0x00 },
+	{ WCD934X_CDC_COMPANDER8_CTL6,                     0x01 },
+	{ WCD934X_CDC_COMPANDER8_CTL7,                     0x08 },
+	{ WCD934X_CDC_RX0_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX0_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX0_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX0_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX0_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX0_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC0,                    0xfc },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX0_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX1_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX1_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX1_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX1_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX1_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC0,                    0xfc },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC4,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX1_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX2_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX2_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX2_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX2_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX2_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC0,                    0xfc },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC4,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX2_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX3_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX3_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX3_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX3_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX3_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC0,                    0xfc },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX3_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX4_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX4_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX4_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX4_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX4_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC0,                    0xfc },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX4_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX7_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX7_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX7_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX7_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX7_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC0,                    0x04 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX7_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_CTL,                     0x04 },
+	{ WCD934X_CDC_RX8_RX_PATH_CFG0,                    0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_CFG1,                    0x64 },
+	{ WCD934X_CDC_RX8_RX_PATH_CFG2,                    0x8f },
+	{ WCD934X_CDC_RX8_RX_VOL_CTL,                      0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_MIX_CTL,                 0x04 },
+	{ WCD934X_CDC_RX8_RX_PATH_MIX_CFG,                 0x7e },
+	{ WCD934X_CDC_RX8_RX_VOL_MIX_CTL,                  0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC0,                    0x04 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC1,                    0x08 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC2,                    0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC3,                    0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC5,                    0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC6,                    0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_SEC7,                    0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,                0x08 },
+	{ WCD934X_CDC_RX8_RX_PATH_MIX_SEC1,                0x00 },
+	{ WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL,              0x00 },
+	{ WCD934X_PAGE12_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_CDC_CLSH_CRC,                            0x00 },
+	{ WCD934X_CDC_CLSH_DLY_CTRL,                       0x03 },
+	{ WCD934X_CDC_CLSH_DECAY_CTRL,                     0x02 },
+	{ WCD934X_CDC_CLSH_HPH_V_PA,                       0x1c },
+	{ WCD934X_CDC_CLSH_EAR_V_PA,                       0x39 },
+	{ WCD934X_CDC_CLSH_HPH_V_HD,                       0x0c },
+	{ WCD934X_CDC_CLSH_EAR_V_HD,                       0x0c },
+	{ WCD934X_CDC_CLSH_K1_MSB,                         0x01 },
+	{ WCD934X_CDC_CLSH_K1_LSB,                         0x00 },
+	{ WCD934X_CDC_CLSH_K2_MSB,                         0x00 },
+	{ WCD934X_CDC_CLSH_K2_LSB,                         0x80 },
+	{ WCD934X_CDC_CLSH_IDLE_CTRL,                      0x00 },
+	{ WCD934X_CDC_CLSH_IDLE_HPH,                       0x00 },
+	{ WCD934X_CDC_CLSH_IDLE_EAR,                       0x00 },
+	{ WCD934X_CDC_CLSH_TEST0,                          0x07 },
+	{ WCD934X_CDC_CLSH_TEST1,                          0x00 },
+	{ WCD934X_CDC_CLSH_OVR_VREF,                       0x00 },
+	{ WCD934X_CDC_BOOST0_BOOST_PATH_CTL,               0x00 },
+	{ WCD934X_CDC_BOOST0_BOOST_CTL,                    0xb2 },
+	{ WCD934X_CDC_BOOST0_BOOST_CFG1,                   0x00 },
+	{ WCD934X_CDC_BOOST0_BOOST_CFG2,                   0x00 },
+	{ WCD934X_CDC_BOOST1_BOOST_PATH_CTL,               0x00 },
+	{ WCD934X_CDC_BOOST1_BOOST_CTL,                    0xb2 },
+	{ WCD934X_CDC_BOOST1_BOOST_CFG1,                   0x00 },
+	{ WCD934X_CDC_BOOST1_BOOST_CFG2,                   0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_PATH_CTL,                  0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_CFG,                       0x1a },
+	{ WCD934X_CDC_VBAT_VBAT_ADC_CAL1,                  0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_ADC_CAL2,                  0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_ADC_CAL3,                  0x04 },
+	{ WCD934X_CDC_VBAT_VBAT_PK_EST1,                   0xe0 },
+	{ WCD934X_CDC_VBAT_VBAT_PK_EST2,                   0x01 },
+	{ WCD934X_CDC_VBAT_VBAT_PK_EST3,                   0x40 },
+	{ WCD934X_CDC_VBAT_VBAT_RF_PROC1,                  0x2a },
+	{ WCD934X_CDC_VBAT_VBAT_RF_PROC2,                  0x86 },
+	{ WCD934X_CDC_VBAT_VBAT_TAC1,                      0x70 },
+	{ WCD934X_CDC_VBAT_VBAT_TAC2,                      0x18 },
+	{ WCD934X_CDC_VBAT_VBAT_TAC3,                      0x18 },
+	{ WCD934X_CDC_VBAT_VBAT_TAC4,                      0x03 },
+	{ WCD934X_CDC_VBAT_VBAT_GAIN_UPD1,                 0x01 },
+	{ WCD934X_CDC_VBAT_VBAT_GAIN_UPD2,                 0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_GAIN_UPD3,                 0x64 },
+	{ WCD934X_CDC_VBAT_VBAT_GAIN_UPD4,                 0x01 },
+	{ WCD934X_CDC_VBAT_VBAT_DEBUG1,                    0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON,              0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL,              0x00 },
+	{ WCD934X_CDC_VBAT_VBAT_BAN,                       0x0c },
+	{ WCD934X_MIXING_ASRC0_CLK_RST_CTL,                0x00 },
+	{ WCD934X_MIXING_ASRC0_CTL0,                       0x00 },
+	{ WCD934X_MIXING_ASRC0_CTL1,                       0x00 },
+	{ WCD934X_MIXING_ASRC0_FIFO_CTL,                   0xa8 },
+	{ WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC0_STATUS_FIFO,                0x00 },
+	{ WCD934X_MIXING_ASRC1_CLK_RST_CTL,                0x00 },
+	{ WCD934X_MIXING_ASRC1_CTL0,                       0x00 },
+	{ WCD934X_MIXING_ASRC1_CTL1,                       0x00 },
+	{ WCD934X_MIXING_ASRC1_FIFO_CTL,                   0xa8 },
+	{ WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC1_STATUS_FIFO,                0x00 },
+	{ WCD934X_MIXING_ASRC2_CLK_RST_CTL,                0x00 },
+	{ WCD934X_MIXING_ASRC2_CTL0,                       0x00 },
+	{ WCD934X_MIXING_ASRC2_CTL1,                       0x00 },
+	{ WCD934X_MIXING_ASRC2_FIFO_CTL,                   0xa8 },
+	{ WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC2_STATUS_FIFO,                0x00 },
+	{ WCD934X_MIXING_ASRC3_CLK_RST_CTL,                0x00 },
+	{ WCD934X_MIXING_ASRC3_CTL0,                       0x00 },
+	{ WCD934X_MIXING_ASRC3_CTL1,                       0x00 },
+	{ WCD934X_MIXING_ASRC3_FIFO_CTL,                   0xa8 },
+	{ WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB,       0x00 },
+	{ WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB,       0x00 },
+	{ WCD934X_MIXING_ASRC3_STATUS_FIFO,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_DATA_0,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_DATA_1,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_DATA_2,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_DATA_3,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_DATA_0,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_DATA_1,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_DATA_2,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_RD_DATA_3,                0x00 },
+	{ WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG,               0x0f },
+	{ WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS,            0x03 },
+	{ WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,       0x04 },
+	{ WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1,      0x00 },
+	{ WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,       0x04 },
+	{ WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1,      0x00 },
+	{ WCD934X_SIDETONE_ASRC0_CLK_RST_CTL,              0x00 },
+	{ WCD934X_SIDETONE_ASRC0_CTL0,                     0x00 },
+	{ WCD934X_SIDETONE_ASRC0_CTL1,                     0x00 },
+	{ WCD934X_SIDETONE_ASRC0_FIFO_CTL,                 0xa8 },
+	{ WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC0_STATUS_FIFO,              0x00 },
+	{ WCD934X_SIDETONE_ASRC1_CLK_RST_CTL,              0x00 },
+	{ WCD934X_SIDETONE_ASRC1_CTL0,                     0x00 },
+	{ WCD934X_SIDETONE_ASRC1_CTL1,                     0x00 },
+	{ WCD934X_SIDETONE_ASRC1_FIFO_CTL,                 0xa8 },
+	{ WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB,     0x00 },
+	{ WCD934X_SIDETONE_ASRC1_STATUS_FIFO,              0x00 },
+	{ WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL,           0x00 },
+	{ WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0,               0x01 },
+	{ WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL,           0x00 },
+	{ WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0,               0x01 },
+	{ WCD934X_EC_ASRC0_CLK_RST_CTL,                    0x00 },
+	{ WCD934X_EC_ASRC0_CTL0,                           0x00 },
+	{ WCD934X_EC_ASRC0_CTL1,                           0x00 },
+	{ WCD934X_EC_ASRC0_FIFO_CTL,                       0xa8 },
+	{ WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB,           0x00 },
+	{ WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB,           0x00 },
+	{ WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB,           0x00 },
+	{ WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB,           0x00 },
+	{ WCD934X_EC_ASRC0_STATUS_FIFO,                    0x00 },
+	{ WCD934X_EC_ASRC1_CLK_RST_CTL,                    0x00 },
+	{ WCD934X_EC_ASRC1_CTL0,                           0x00 },
+	{ WCD934X_EC_ASRC1_CTL1,                           0x00 },
+	{ WCD934X_EC_ASRC1_FIFO_CTL,                       0xa8 },
+	{ WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB,           0x00 },
+	{ WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB,           0x00 },
+	{ WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB,           0x00 },
+	{ WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB,           0x00 },
+	{ WCD934X_EC_ASRC1_STATUS_FIFO,                    0x00 },
+	{ WCD934X_PAGE13_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1,             0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0,              0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1,              0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2,              0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3,              0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4,              0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0,        0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1,        0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_ANC_CFG0,                 0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0,         0x00 },
+	{ WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0,           0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0,            0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0,           0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0,           0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0,           0x00 },
+	{ WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0,           0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2,  0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3,  0x00 },
+	{ WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0,               0x00 },
+	{ WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1,               0x00 },
+	{ WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2,               0x00 },
+	{ WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3,               0x00 },
+	{ WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL,           0x00 },
+	{ WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,         0x0c },
+	{ WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,            0x00 },
+	{ WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL,            0x00 },
+	{ WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL,     0x0f },
+	{ WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL,            0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_CTL,                0x08 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0,       0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1,       0x4b },
+	{ WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB,    0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB,    0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_STATUS,             0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL,          0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB,      0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB,      0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD,   0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD,   0x00 },
+	{ WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT,     0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL,          0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_CTL,               0x40 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL,    0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL,          0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_CTL,               0x40 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL,    0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL,       0x00 },
+	{ WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL,       0x00 },
+	{ WCD934X_CDC_TOP_TOP_CFG0,                        0x00 },
+	{ WCD934X_CDC_TOP_TOP_CFG1,                        0x00 },
+	{ WCD934X_CDC_TOP_TOP_CFG7,                        0x00 },
+	{ WCD934X_CDC_TOP_HPHL_COMP_WR_LSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHL_COMP_WR_MSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHL_COMP_LUT,                   0x00 },
+	{ WCD934X_CDC_TOP_HPHL_COMP_RD_LSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHL_COMP_RD_MSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHR_COMP_WR_LSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHR_COMP_WR_MSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHR_COMP_LUT,                   0x00 },
+	{ WCD934X_CDC_TOP_HPHR_COMP_RD_LSB,                0x00 },
+	{ WCD934X_CDC_TOP_HPHR_COMP_RD_MSB,                0x00 },
+	{ WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFL_COMP_LUT,                  0x00 },
+	{ WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFR_COMP_LUT,                  0x00 },
+	{ WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB,               0x00 },
+	{ WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB,               0x00 },
+	{ WCD934X_CDC_DSD0_PATH_CTL,                       0x00 },
+	{ WCD934X_CDC_DSD0_CFG0,                           0x00 },
+	{ WCD934X_CDC_DSD0_CFG1,                           0x00 },
+	{ WCD934X_CDC_DSD0_CFG2,                           0x42 },
+	{ WCD934X_CDC_DSD0_CFG3,                           0x00 },
+	{ WCD934X_CDC_DSD0_CFG4,                           0x02 },
+	{ WCD934X_CDC_DSD0_CFG5,                           0x00 },
+	{ WCD934X_CDC_DSD1_PATH_CTL,                       0x00 },
+	{ WCD934X_CDC_DSD1_CFG0,                           0x00 },
+	{ WCD934X_CDC_DSD1_CFG1,                           0x00 },
+	{ WCD934X_CDC_DSD1_CFG2,                           0x42 },
+	{ WCD934X_CDC_DSD1_CFG3,                           0x00 },
+	{ WCD934X_CDC_DSD1_CFG4,                           0x02 },
+	{ WCD934X_CDC_DSD1_CFG5,                           0x00 },
+	{ WCD934X_CDC_RX_IDLE_DET_PATH_CTL,                0x00 },
+	{ WCD934X_CDC_RX_IDLE_DET_CFG0,                    0x07 },
+	{ WCD934X_CDC_RX_IDLE_DET_CFG1,                    0x3c },
+	{ WCD934X_CDC_RX_IDLE_DET_CFG2,                    0x00 },
+	{ WCD934X_CDC_RX_IDLE_DET_CFG3,                    0x00 },
+	{ WCD934X_PAGE14_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL,            0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_CTL,                    0x09 },
+	{ WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL,         0x06 },
+	{ WCD934X_CDC_RATE_EST0_RE_TIMER,                  0x01 },
+	{ WCD934X_CDC_RATE_EST0_RE_BW_SW,                  0x20 },
+	{ WCD934X_CDC_RATE_EST0_RE_THRESH,                 0xa0 },
+	{ WCD934X_CDC_RATE_EST0_RE_STATUS,                 0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL,              0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2,            0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1,        0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2,        0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3,        0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4,        0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5,        0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1,         0x08 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2,         0x07 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3,         0x05 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4,         0x05 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5,         0x05 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1,       0x08 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2,       0x07 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3,       0x05 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4,       0x05 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5,       0x05 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1,          0x03 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2,          0x03 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3,          0x03 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4,          0x03 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5,          0x03 },
+	{ WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_PH_DET,                 0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_DIAG_CLR,               0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE,            0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE,        0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0,           0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8,          0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16,         0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24,         0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32,         0x00 },
+	{ WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43,         0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL,            0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_CTL,                    0x09 },
+	{ WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL,         0x06 },
+	{ WCD934X_CDC_RATE_EST1_RE_TIMER,                  0x01 },
+	{ WCD934X_CDC_RATE_EST1_RE_BW_SW,                  0x20 },
+	{ WCD934X_CDC_RATE_EST1_RE_THRESH,                 0xa0 },
+	{ WCD934X_CDC_RATE_EST1_RE_STATUS,                 0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL,              0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2,            0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1,        0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2,        0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3,        0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4,        0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5,        0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1,         0x08 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2,         0x07 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3,         0x05 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4,         0x05 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5,         0x05 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1,       0x08 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2,       0x07 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3,       0x05 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4,       0x05 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5,       0x05 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1,          0x03 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2,          0x03 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3,          0x03 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4,          0x03 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5,          0x03 },
+	{ WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_PH_DET,                 0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_DIAG_CLR,               0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE,            0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE,        0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0,           0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8,          0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16,         0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24,         0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32,         0x00 },
+	{ WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43,         0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL,            0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_CTL,                    0x09 },
+	{ WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL,         0x06 },
+	{ WCD934X_CDC_RATE_EST2_RE_TIMER,                  0x01 },
+	{ WCD934X_CDC_RATE_EST2_RE_BW_SW,                  0x20 },
+	{ WCD934X_CDC_RATE_EST2_RE_THRESH,                 0xa0 },
+	{ WCD934X_CDC_RATE_EST2_RE_STATUS,                 0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL,              0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2,            0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1,        0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2,        0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3,        0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4,        0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5,        0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1,         0x08 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2,         0x07 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3,         0x05 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4,         0x05 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5,         0x05 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1,       0x08 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2,       0x07 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3,       0x05 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4,       0x05 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5,       0x05 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1,          0x03 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2,          0x03 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3,          0x03 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4,          0x03 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5,          0x03 },
+	{ WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_PH_DET,                 0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_DIAG_CLR,               0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE,            0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE,        0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0,           0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8,          0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16,         0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24,         0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32,         0x00 },
+	{ WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43,         0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL,            0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_CTL,                    0x09 },
+	{ WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL,         0x06 },
+	{ WCD934X_CDC_RATE_EST3_RE_TIMER,                  0x01 },
+	{ WCD934X_CDC_RATE_EST3_RE_BW_SW,                  0x20 },
+	{ WCD934X_CDC_RATE_EST3_RE_THRESH,                 0xa0 },
+	{ WCD934X_CDC_RATE_EST3_RE_STATUS,                 0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL,              0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2,            0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1,        0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2,        0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3,        0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4,        0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5,        0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1,         0x08 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2,         0x07 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3,         0x05 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4,         0x05 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5,         0x05 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1,       0x08 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2,       0x07 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3,       0x05 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4,       0x05 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5,       0x05 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1,          0x03 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2,          0x03 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3,          0x03 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4,          0x03 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5,          0x03 },
+	{ WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG,              0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_PH_DET,                 0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_DIAG_CLR,               0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE,            0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE,        0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0,           0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8,          0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16,         0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24,         0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32,         0x00 },
+	{ WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43,         0x00 },
+	{ WCD934X_PAGE15_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_SPLINE_SRC0_CLK_RST_CTL_0,               0x20 },
+	{ WCD934X_SPLINE_SRC0_STATUS,                      0x00 },
+	{ WCD934X_SPLINE_SRC1_CLK_RST_CTL_0,               0x20 },
+	{ WCD934X_SPLINE_SRC1_STATUS,                      0x00 },
+	{ WCD934X_SPLINE_SRC2_CLK_RST_CTL_0,               0x20 },
+	{ WCD934X_SPLINE_SRC2_STATUS,                      0x00 },
+	{ WCD934X_SPLINE_SRC3_CLK_RST_CTL_0,               0x20 },
+	{ WCD934X_SPLINE_SRC3_STATUS,                      0x00 },
+	{ WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0,               0x11 },
+	{ WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1,               0x20 },
+	{ WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2,               0x00 },
+	{ WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3,               0x08 },
+	{ WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0,               0x11 },
+	{ WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1,               0x20 },
+	{ WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2,               0x00 },
+	{ WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3,               0x08 },
+	{ WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0,         0x00 },
+	{ WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1,         0x00 },
+	{ WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0,         0x00 },
+	{ WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL,             0x4c },
+	{ WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL,             0x4c },
+	{ WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL,             0x4c },
+	{ WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL,             0x4c },
+	{ WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR,           0x00 },
+	{ WCD934X_PAGE80_PAGE_REGISTER,                    0x00 },
+	{ WCD934X_CODEC_CPR_WR_DATA_0,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_DATA_1,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_DATA_2,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_DATA_3,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_ADDR_0,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_ADDR_1,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_ADDR_2,                     0x00 },
+	{ WCD934X_CODEC_CPR_WR_ADDR_3,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_ADDR_0,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_ADDR_1,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_ADDR_2,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_ADDR_3,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_DATA_0,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_DATA_1,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_DATA_2,                     0x00 },
+	{ WCD934X_CODEC_CPR_RD_DATA_3,                     0x00 },
+	{ WCD934X_CODEC_CPR_ACCESS_CFG,                    0x0f },
+	{ WCD934X_CODEC_CPR_ACCESS_STATUS,                 0x03 },
+	{ WCD934X_CODEC_CPR_NOM_CX_VDD,                    0xb4 },
+	{ WCD934X_CODEC_CPR_SVS_CX_VDD,                    0x5c },
+	{ WCD934X_CODEC_CPR_SVS2_CX_VDD,                   0x40 },
+	{ WCD934X_CODEC_CPR_NOM_MX_VDD,                    0xb4 },
+	{ WCD934X_CODEC_CPR_SVS_MX_VDD,                    0xb4 },
+	{ WCD934X_CODEC_CPR_SVS2_MX_VDD,                   0xa0 },
+	{ WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD,               0x28 },
+	{ WCD934X_CODEC_CPR_MAX_SVS2_STEP,                 0x08 },
+	{ WCD934X_CODEC_CPR_CTL,                           0x00 },
+	{ WCD934X_CODEC_CPR_SW_MODECHNG_STATUS,            0x00 },
+	{ WCD934X_CODEC_CPR_SW_MODECHNG_START,             0x00 },
+	{ WCD934X_CODEC_CPR_CPR_STATUS,                    0x00 },
+	{ WCD934X_PAGE128_PAGE_REGISTER,                   0x00 },
+	{ WCD934X_TLMM_BIST_MODE_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_RF_PA_ON_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_INTR1_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_INTR2_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_SWR_DATA_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_SWR_CLK_PINCFG,                     0x00 },
+	{ WCD934X_TLMM_I2S_2_SCK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_SLIMBUS_DATA1_PINCFG,               0x00 },
+	{ WCD934X_TLMM_SLIMBUS_DATA2_PINCFG,               0x00 },
+	{ WCD934X_TLMM_SLIMBUS_CLK_PINCFG,                 0x00 },
+	{ WCD934X_TLMM_I2C_CLK_PINCFG,                     0x00 },
+	{ WCD934X_TLMM_I2C_DATA_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_0_RX_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_0_TX_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_0_SCK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_I2S_0_WS_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_1_RX_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_1_TX_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_1_SCK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_I2S_1_WS_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_DMIC1_CLK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_DMIC1_DATA_PINCFG,                  0x00 },
+	{ WCD934X_TLMM_DMIC2_CLK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_DMIC2_DATA_PINCFG,                  0x00 },
+	{ WCD934X_TLMM_DMIC3_CLK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_DMIC3_DATA_PINCFG,                  0x00 },
+	{ WCD934X_TLMM_JTCK_PINCFG,                        0x00 },
+	{ WCD934X_TLMM_GPIO1_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_GPIO2_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_GPIO3_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_GPIO4_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_SPI_S_CSN_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_SPI_S_CLK_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_SPI_S_DOUT_PINCFG,                  0x00 },
+	{ WCD934X_TLMM_SPI_S_DIN_PINCFG,                   0x00 },
+	{ WCD934X_TLMM_BA_N_PINCFG,                        0x00 },
+	{ WCD934X_TLMM_GPIO0_PINCFG,                       0x00 },
+	{ WCD934X_TLMM_I2S_2_RX_PINCFG,                    0x00 },
+	{ WCD934X_TLMM_I2S_2_WS_PINCFG,                    0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_OE_0,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_OE_1,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_OE_2,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_OE_3,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_OE_4,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_DATA_0,               0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_DATA_1,               0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_DATA_2,               0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_DATA_3,               0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_CTL_DATA_4,               0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_DRVCTL_0,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_DRVCTL_1,                 0x00 },
+	{ WCD934X_TEST_DEBUG_PIN_STATUS,                   0x00 },
+	{ WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,               0x10 },
+	{ WCD934X_TEST_DEBUG_NPL_DLY_TEST_2,               0x60 },
+	{ WCD934X_TEST_DEBUG_MEM_CTRL,                     0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_BUS_SEL,                0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_JTAG,                   0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_EN_1,                   0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_EN_2,                   0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_EN_3,                   0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_EN_4,                   0x00 },
+	{ WCD934X_TEST_DEBUG_DEBUG_EN_5,                   0x00 },
+	{ WCD934X_TEST_DEBUG_ANA_DTEST_DIR,                0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0,            0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1,            0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2,            0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3,            0x00 },
+	{ WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4,            0x00 },
+	{ WCD934X_TEST_DEBUG_SYSMEM_CTRL,                  0x00 },
+	{ WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY,         0x00 },
+	{ WCD934X_TEST_DEBUG_LVAL_NOM_LOW,                 0x96 },
+	{ WCD934X_TEST_DEBUG_LVAL_NOM_HIGH,                0x00 },
+	{ WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW,            0x53 },
+	{ WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH,           0x00 },
+	{ WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR,               0x00 },
+	{ WCD934X_TEST_DEBUG_CODEC_DIAGS,                  0x00 },
+};
+
+/*
+ * wcd934x_regmap_register_patch: Update register defaults based on version
+ * @regmap: handle to wcd9xxx regmap
+ * @version: wcd934x version
+ *
+ * Returns error code in case of failure or 0 for success
+ */
+int wcd934x_regmap_register_patch(struct regmap *regmap, int revision)
+{
+	int rc = 0;
+
+	if (!regmap) {
+		pr_err("%s: regmap struct is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (revision) {
+	case TAVIL_VERSION_1_1:
+	case TAVIL_VERSION_WCD9340_1_1:
+	case TAVIL_VERSION_WCD9341_1_1:
+		regcache_cache_only(regmap, true);
+		rc = regmap_multi_reg_write(regmap, wcd934x_1_1_defaults,
+					    ARRAY_SIZE(wcd934x_1_1_defaults));
+		regcache_cache_only(regmap, false);
+		break;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd934x_regmap_register_patch);
+
+static bool wcd934x_is_readable_register(struct device *dev, unsigned int reg)
+{
+	u8 pg_num, reg_offset;
+	const u8 *reg_tbl = NULL;
+
+	/*
+	 * Get the page number from MSB of codec register. If its 0x80, assign
+	 * the corresponding page index PAGE_0x80.
+	 */
+	pg_num = reg >> 0x8;
+	if (pg_num == 0x80)
+		pg_num = WCD934X_PAGE_0X80;
+	else if (pg_num == 0x50)
+		pg_num = WCD934X_PAGE_0x50;
+	else if (pg_num > 0xF)
+		return false;
+
+	reg_tbl = wcd934x_reg[pg_num];
+	reg_offset = reg & 0xFF;
+
+	if (reg_tbl && reg_tbl[reg_offset])
+		return true;
+	else
+		return false;
+}
+
+static bool wcd934x_is_volatile_register(struct device *dev, unsigned int reg)
+{
+	u8 pg_num, reg_offset;
+	const u8 *reg_tbl = NULL;
+
+	pg_num = reg >> 0x8;
+	if (pg_num == 0x80)
+		pg_num = WCD934X_PAGE_0X80;
+	else if (pg_num == 0x50)
+		pg_num = WCD934X_PAGE_0x50;
+	else if (pg_num > 0xF)
+		return false;
+
+	reg_tbl = wcd934x_reg[pg_num];
+	reg_offset = reg & 0xFF;
+
+	if (reg_tbl && reg_tbl[reg_offset] == WCD934X_READ)
+		return true;
+
+	/* IIR Coeff registers are not cacheable */
+	if ((reg >= WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL) &&
+	    (reg <= WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL))
+		return true;
+
+	if ((reg >= WCD934X_CDC_ANC0_IIR_COEFF_1_CTL) &&
+	    (reg <= WCD934X_CDC_ANC0_FB_GAIN_CTL))
+		return true;
+
+	if ((reg >= WCD934X_CDC_ANC1_IIR_COEFF_1_CTL) &&
+	    (reg <= WCD934X_CDC_ANC1_FB_GAIN_CTL))
+		return true;
+
+	if ((reg >= WCD934X_CODEC_CPR_WR_DATA_0) &&
+	    (reg <= WCD934X_CODEC_CPR_RD_DATA_3))
+		return true;
+
+	/*
+	 * Need to mark volatile for registers that are writable but
+	 * only few bits are read-only
+	 */
+	switch (reg) {
+	case WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL:
+	case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0:
+	case WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1:
+	case WCD934X_CPE_SS_CPAR_CTL:
+	case WCD934X_CPE_SS_STATUS:
+	case WCD934X_CODEC_RPM_RST_CTL:
+	case WCD934X_SIDO_NEW_VOUT_A_STARTUP:
+	case WCD934X_SIDO_NEW_VOUT_D_STARTUP:
+	case WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL:
+	case WCD934X_ANA_MBHC_MECH:
+	case WCD934X_ANA_MBHC_ELECT:
+	case WCD934X_ANA_MBHC_ZDET:
+	case WCD934X_ANA_MICB2:
+		return true;
+	}
+
+	return false;
+}
+
+struct regmap_config wcd934x_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = wcd934x_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wcd934x_defaults),
+	.max_register = WCD934X_MAX_REGISTER,
+	.volatile_reg = wcd934x_is_volatile_register,
+	.readable_reg = wcd934x_is_readable_register,
+	.can_multi_write = true,
+};
diff --git a/drivers/mfd/wcd934x-tables.c b/drivers/mfd/wcd934x-tables.c
new file mode 100644
index 0000000..db963d0
--- /dev/null
+++ b/drivers/mfd/wcd934x-tables.c
@@ -0,0 +1,2155 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mfd/wcd934x/registers.h>
+
+#define WCD934X_REG(reg)  ((reg) & 0xFF)
+
+const u8 wcd934x_page0_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE0_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_CLK_BYPASS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_CLK_GATE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_CLK_MCLK2_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO)] =
+								WCD934X_READ,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX0_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX1_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX2_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX3_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX4_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX5_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX6_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_RX7_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX0_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX1_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX2_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX3_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX4_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX5_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX6_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX7_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX8_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX9_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX10_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX11_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX13_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX14_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_SB_TX15_INP_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_TX0_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_0_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_TX1_1_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_0_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_1_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_3_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_CLKSRC_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_COMMON_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_0_TDM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DATA_HUB_I2S_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_DMA_RDMA_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_RDMA_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_RDMA_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA4_PRT_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA_SBTX0_7_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_RDMA_SBTX8_11_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_4_5_CFG_WDMA_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_2_3_CFG_WDMA_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_CH_0_1_CFG_WDMA_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA0_PRT_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA3_PRT_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA4_PRT0_3_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DMA_WDMA4_PRT4_7_CFG)] = WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page1_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE1_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_USER_CTL_9)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_FLL_MODE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_FLL_STATUS_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_FLL_STATUS_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_FLL_STATUS_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_FLL_STATUS_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_USER_CTL_9)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_FLL_MODE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_I2S_FLL_STATUS_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_I2S_FLL_STATUS_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_I2S_FLL_STATUS_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_I2S_FLL_STATUS_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_USER_CTL_9)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_L_VAL_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_DSM_FRAC_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_CONFIG_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_TEST_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_FREQ_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_SSC_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_FLL_MODE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SB_FLL_STATUS_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SB_FLL_STATUS_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SB_FLL_STATUS_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SB_FLL_STATUS_3)] = WCD934X_READ,
+};
+
+const u8 wcd934x_page2_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE2_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_CPE_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPEFLL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_US_BUF_INT_PERIOD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SVA_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_US_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_MAD_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_CPAR_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_DMIC0_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_DMIC1_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_DMIC2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_DMIC_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_CPAR_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_WDOG_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_BACKUP_INT)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_STATUS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_CPE_OCD_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_MAIN_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_CTL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_ULTR_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_CTL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SOC_MAD_INP_SEL)] = WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page4_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE4_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CLR_COMMIT)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_MASK0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_MASK1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_MASK2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_MASK3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_STATUS0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_PIN1_STATUS1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_PIN1_STATUS2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_PIN1_STATUS3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_PIN1_CLEAR0)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_CLEAR1)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_CLEAR2)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN1_CLEAR3)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN2_MASK3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_PIN2_STATUS3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_PIN2_CLEAR3)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_MASK3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_STATUS3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR2)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CPESS_SUMRY_CLEAR3)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_INTR_LEVEL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_LEVEL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_LEVEL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_LEVEL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_BYPASS0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_BYPASS1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_BYPASS2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_BYPASS3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_SET0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_SET1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_SET2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_SET3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CODEC_MISC_MASK)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_INTR_CODEC_MISC_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_INTR_CODEC_MISC_CLEAR)] = WCD934X_WRITE,
+};
+
+const u8 wcd934x_page5_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE5_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_DEVICE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_REVISION)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_H_COMMAND)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_COMM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_FRAME_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_RD)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SW_EVENT_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SELECT_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_RAM_CNTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BANK)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_9)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_A)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_B)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_C)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_E)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_F)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_10)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_11)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_12)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_13)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_14)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_15)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_16)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_17)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_18)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_19)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1A)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1B)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1C)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1E)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_1F)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_20)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_21)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_22)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_23)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_24)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_25)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_26)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_27)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_28)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_29)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2A)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2B)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2C)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2E)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_2F)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_30)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_31)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_32)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_33)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_34)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_35)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_36)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_37)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_38)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_39)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3A)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3B)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3C)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3E)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_SRAM_BYTE_3F)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_TOP_CTRL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_PDM_MUTE_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_FS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_ENABLE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_GPOUT_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_IP_TESTING)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_MASK4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_STATUS4)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR0)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR1)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR2)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR3)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_DIG_INTERRUPT_CLR4)] = WCD934X_WRITE,
+};
+
+const u8 wcd934x_page6_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_ANA_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_BIAS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_RCO)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_PAGE6_SPARE2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_PAGE6_SPARE3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_BUCK_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_BUCK_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_ANA_RX_SUPPLIES)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_HPH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_EAR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_LO_1_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MAD_SETUP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_AMIC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_AMIC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_AMIC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_AMIC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_MECH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_ELECT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_ZDET)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_RESULT_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_ANA_MBHC_RESULT_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_ANA_MBHC_RESULT_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MBHC_BTN7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MICB1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MICB2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MICB2_RAMP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MICB3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_MICB4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_ANA_VBADC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BIAS_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BIAS_VBG_FINE_ADJ)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_CTRL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_CTRL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_CAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_CAL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_CAL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_TEST_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RCO_CAL_OUT_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_RCO_CAL_OUT_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_RCO_CAL_OUT_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_RCO_CAL_OUT_4)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_RCO_CAL_OUT_5)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDO_MODE_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_MODE_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_MODE_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_MODE_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_VCL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_VCL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_VCL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_9)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CCL_10)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_FILTER_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_FILTER_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_DRIVER_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_DRIVER_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_DRIVER_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CAL_CODE_EXT_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDO_CAL_CODE_OUT_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDO_TEST_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_TEST_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_CTL_CLK)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_CTL_ANA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_CTL_SPARE_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_CTL_SPARE_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_CTL_BCS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_STATUS_SPARE_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MBHC_TEST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_SUBBLOCK_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_IBIAS_FE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_BIAS_ADC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_FE_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_ADC_REF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_ADC_IO)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_ADC_SAR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_DEBUG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_LDOH_MODE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_LDOH_BIAS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_LDOH_STB_LOADS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_LDOH_SLOWRAMP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB1_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB1_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB1_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB2_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB2_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB2_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB3_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB3_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB3_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB4_TEST_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB4_TEST_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MICB4_TEST_CTL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_ADC_VCM)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_BIAS_ATEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_ADC_INT1_IB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_ADC_INT2_IB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_START)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_9P6M)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_COM_TXFE_DIV_STOP_12P288M)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_TEST_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_ADC_IB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_ATEST_REFCTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_TEST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_TEST_BLK_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_TXFE_CLKDIV)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_1_2_SAR1_ERR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_TX_1_2_SAR2_ERR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_TX_3_4_TEST_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_3_4_ADC_IB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_3_4_ATEST_REFCTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_3_4_TEST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_3_4_TEST_BLK_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_3_4_TXFE_CLKDIV)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TX_3_4_SAR1_ERR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_TX_3_4_SAR2_ERR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CLASSH_MODE_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_MODE_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_MODE_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_VCL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_CTRL_CCL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_BUCK_TMUX_A_D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_BUCK_SW_DRV_CNTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLASSH_SPARE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_8)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEG_CTRL_9)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_VNEGDAC_CTRL_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_CTRL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_FLYBACK_TEST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_AUX_SW_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_PA_AUX_IN_CONN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_TIMER_DIV)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_OCP_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_OCP_COUNT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_EAR_DAC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_EAR_AMP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_HPH_LDO)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_HPH_PA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_HPH_RDAC_LDO)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_HPH_CNP1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_HPH_LOWPOWER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_PA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_REF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_DIFFLO_LDO)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_SELO_DAC_PA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_BUCK_RST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_BUCK_VREF_ERRAMP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_FLYB_ERRAMP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_FLYB_BUFF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_BIAS_FLYB_MID_RST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_L_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_HPH_R_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_HPH_CNP_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_CNP_WG_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_CNP_WG_TIME)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_OCP_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_AUTO_CHOP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_CHOP_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_PA_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_PA_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_L_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_L_TEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_L_ATEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_R_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_R_TEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_R_ATEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_RDAC_CLK_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_RDAC_LDO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_REFBUFF_UHQA_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_REFBUFF_LP_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_L_DAC_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_R_DAC_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_EN_REG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_CMBUFF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_ICTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_EN_DBG_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_CNP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_DAC_CTL_ATEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EAR_STATUS_REG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EAR_EAR_MISC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_MISC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_LO2_COMPANDER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_LO1_COMPANDER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_COMMON)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_BYPASS_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_CNP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_CORE_OUT_PROG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_LDO_OUT_PROG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_COM_PA_FREQ)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_RESERVED_REG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_DIFF_LO_LO1_STATUS_2)] = WCD934X_READ,
+};
+
+const u8 wcd934x_page7_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_ANA_NEW_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_ANA_HPH3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_EN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_LDO_CONFIG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_LDO_OCP_CONFIG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_TX_LDO_CONFIG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_TX_DRV_CONFIG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_RX_CONFIG_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_PLL_ENABLES)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_PLL_PRESET)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_ANA_PLL_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CLK_SYS_PLL_ENABLES)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_PLL_PRESET)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_PLL_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CLK_SYS_MCLK_PRG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_MCLK2_PRG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_XO_PRG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_XO_CAP_XTM)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_BST_EN_DLY)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_CTRL_ILIM)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_VOUT_SETTING)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_VOUT_A_STARTUP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_STARTUP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_VOUT_D_FREQ2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_CTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_CTL_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_PLUG_DETECT_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_ZDET_ANA_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_ZDET_RAMP_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_FSM_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MBHC_NEW_ADC_RESULT)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_TX_NEW_AMIC_4_5_SEL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_NEW_ADC_MODE)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTMSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_VBADC_NEW_ADC_DOUTLSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_HD2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_VREF_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_RDAC_MISC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_PA_MISC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_HPH_TIMER4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_HPH_NEW_INT_PA_RDAC_MISC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_TEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_RESERVED_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_POST_DIV_REG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_REF_DIV_REG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_FILTER_REG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_L_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_M_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_PLL_N_VAL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_VCO_PROG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_TEST_REG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_LDO_LOCK_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_CLK_TEST3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CLK_SYS_INT_XO_TEST2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_VCOMP_HYST)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_VLOOP_FILTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_CTRL_IDELTA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_CTRL_ILIM_STARTUP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_CTRL_MIN_ONTIME)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_CTRL_MAX_ONTIME)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_CTRL_TIMING)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_TMUX_A_D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_SW_DRV_CNTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_SPARE1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_BOOST_INT_SPARE2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_SPARE_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_A)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_CTL_D)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_HPF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_REF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_INT_SLNQ_COMP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MBHC_NEW_INT_SPARE_2)] = WCD934X_READ_WRITE,
+
+};
+
+const u8 wcd934x_page10_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE10_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_CLK_RESET_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_MODE_1_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_MODE_2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_FF_SHIFT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_FB_SHIFT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_A_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_LPF_FF_B_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_LPF_FB_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_SMLPF_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_FB_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_RC_COMMON_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC0_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC1_CLK_RESET_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_MODE_1_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_MODE_2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_FF_SHIFT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_FB_SHIFT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_A_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_LPF_FF_B_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_LPF_FB_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_SMLPF_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_IIR_ADAPT_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_1_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_IIR_COEFF_2_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_FF_A_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_FF_B_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_FB_GAIN_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_RC_COMMON_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_FIFO_COMMON_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_ANC1_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX0_TX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX1_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX2_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX3_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX4_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX5_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX6_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX7_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_192_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX8_TX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0)] =
+							WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page11_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE11_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER1_CTL7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER2_CTL7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER3_CTL7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER4_CTL7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER7_CTL7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL6)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_COMPANDER8_CTL7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_VOL_MIX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC6)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_SEC7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_MIX_SEC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL)] = WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page12_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE12_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_CRC)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_DLY_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_DECAY_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_PA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_PA)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_HPH_V_HD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_EAR_V_HD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_K1_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_K1_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_K2_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_K2_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_IDLE_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_IDLE_HPH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_IDLE_EAR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_TEST0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_TEST1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLSH_OVR_VREF)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST0_BOOST_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_BOOST1_BOOST_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_ADC_CAL3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_PK_EST3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_RF_PROC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_TAC4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_DEBUG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_VBAT_VBAT_BAN)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC0_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC1_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC2_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_MIXING_ASRC3_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_DATA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_RD_DATA_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC0_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_SIDETONE_ASRC1_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC0_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC0_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC0_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC0_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC0_STATUS_FIFO)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC1_CLK_RST_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC1_CTL0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC1_CTL1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC1_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_EC_ASRC1_STATUS_FIFO)] = WCD934X_READ,
+};
+
+const u8 wcd934x_page13_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE13_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_ANC_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD)] =
+							WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_TOP_CFG7)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_WR_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_LUT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHL_COMP_RD_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_WR_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_LUT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_HPHR_COMP_RD_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_LUT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_LUT)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_DSD0_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD0_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD0_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD0_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD0_CFG3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD0_CFG4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD0_CFG5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_CFG3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_CFG4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DSD1_CFG5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_PATH_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RX_IDLE_DET_CFG3)] = WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page14_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE14_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_TIMER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_BW_SW)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_THRESH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_PH_DET)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_DIAG_CLR)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_TIMER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_BW_SW)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_THRESH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_PH_DET)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_DIAG_CLR)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_TIMER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_BW_SW)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_THRESH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_PH_DET)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_DIAG_CLR)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_TIMER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_BW_SW)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_THRESH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_PH_DET)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_DIAG_CLR)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43)] = WCD934X_READ,
+};
+
+const u8 wcd934x_page15_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE15_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SPLINE_SRC0_CLK_RST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SPLINE_SRC0_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SPLINE_SRC1_CLK_RST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SPLINE_SRC1_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SPLINE_SRC2_CLK_RST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SPLINE_SRC2_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_SPLINE_SRC3_CLK_RST_CTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_SPLINE_SRC3_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR)] =
+							WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page_0x50_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE,
+};
+
+const u8 wcd934x_page_0x80_reg_access[WCD934X_PAGE_SIZE] = {
+	[WCD934X_REG(WCD934X_PAGE80_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_DATA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_WR_ADDR_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_ADDR_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_0)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_1)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_2)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_RD_DATA_3)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_CFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_ACCESS_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_NOM_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS2_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_NOM_MX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS_MX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_MAX_SVS2_STEP)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_CTL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_CODEC_CPR_SW_MODECHNG_START)] = WCD934X_WRITE,
+	[WCD934X_REG(WCD934X_CODEC_CPR_CPR_STATUS)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_PAGE128_PAGE_REGISTER)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_BIST_MODE_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_RF_PA_ON_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_INTR1_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_INTR2_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SWR_DATA_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SWR_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_2_SCK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA1_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SLIMBUS_DATA2_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SLIMBUS_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2C_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2C_DATA_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_0_RX_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_0_TX_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_0_SCK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_0_WS_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_1_RX_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_1_TX_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_1_SCK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_1_WS_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_DMIC1_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_DMIC1_DATA_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_DMIC2_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_DMIC2_DATA_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_DMIC3_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_DMIC3_DATA_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_JTCK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_GPIO1_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_GPIO2_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_GPIO3_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_GPIO4_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SPI_S_CSN_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SPI_S_CLK_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SPI_S_DOUT_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_SPI_S_DIN_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_BA_N_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_GPIO0_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_2_RX_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TLMM_I2S_2_WS_PINCFG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_OE_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_CTL_DATA_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_0)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_DRVCTL_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PIN_STATUS)] = WCD934X_READ,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_NPL_DLY_TEST_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_MEM_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_BUS_SEL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_JTAG)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_1)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_2)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_3)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_4)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_DEBUG_EN_5)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_ANA_DTEST_DIR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_SYSMEM_CTRL)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_LOW)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_NOM_HIGH)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH)] =
+							WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR)] = WCD934X_READ_WRITE,
+	[WCD934X_REG(WCD934X_TEST_DEBUG_CODEC_DIAGS)] = WCD934X_READ,
+};
+
+const u8 * const wcd934x_reg[WCD934X_NUM_PAGES] = {
+	[WCD934X_PAGE_0] = wcd934x_page0_reg_access,
+	[WCD934X_PAGE_1] = wcd934x_page1_reg_access,
+	[WCD934X_PAGE_2] = wcd934x_page2_reg_access,
+	[WCD934X_PAGE_4] = wcd934x_page4_reg_access,
+	[WCD934X_PAGE_5] = wcd934x_page5_reg_access,
+	[WCD934X_PAGE_6] = wcd934x_page6_reg_access,
+	[WCD934X_PAGE_7] = wcd934x_page7_reg_access,
+	[WCD934X_PAGE_10] = wcd934x_page10_reg_access,
+	[WCD934X_PAGE_11] = wcd934x_page11_reg_access,
+	[WCD934X_PAGE_12] = wcd934x_page12_reg_access,
+	[WCD934X_PAGE_13] = wcd934x_page13_reg_access,
+	[WCD934X_PAGE_14] = wcd934x_page14_reg_access,
+	[WCD934X_PAGE_15] = wcd934x_page15_reg_access,
+	[WCD934X_PAGE_0x50] = wcd934x_page_0x50_reg_access,
+	[WCD934X_PAGE_0X80] = wcd934x_page_0x80_reg_access,
+};
diff --git a/drivers/mfd/wcd9xxx-core.c b/drivers/mfd/wcd9xxx-core.c
new file mode 100644
index 0000000..f0126dd
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-core.c
@@ -0,0 +1,1690 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-utils.h>
+#include <linux/mfd/msm-cdc-supply.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/debugfs.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "wcd9xxx-regmap.h"
+
+#define WCD9XXX_REGISTER_START_OFFSET 0x800
+#define WCD9XXX_SLIM_RW_MAX_TRIES 3
+#define SLIMBUS_PRESENT_TIMEOUT 100
+
+#define MAX_WCD9XXX_DEVICE	4
+#define WCD9XXX_I2C_GSBI_SLAVE_ID "3-000d"
+#define WCD9XXX_I2C_TOP_SLAVE_ADDR	0x0d
+#define WCD9XXX_ANALOG_I2C_SLAVE_ADDR	0x77
+#define WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR	0x66
+#define WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR	0x55
+#define WCD9XXX_I2C_TOP_LEVEL	0
+#define WCD9XXX_I2C_ANALOG	1
+#define WCD9XXX_I2C_DIGITAL_1	2
+#define WCD9XXX_I2C_DIGITAL_2	3
+
+/*
+ * Number of return values needs to be checked for each
+ * registration of Slimbus of I2C bus for each codec
+ */
+#define NUM_WCD9XXX_REG_RET	4
+
+#define SLIM_USR_MC_REPEAT_CHANGE_VALUE 0x0
+#define SLIM_REPEAT_WRITE_MAX_SLICE 16
+#define REG_BYTES 2
+#define VAL_BYTES 1
+#define WCD9XXX_PAGE_NUM(reg)    (((reg) >> 8) & 0xff)
+#define WCD9XXX_PAGE_SIZE 256
+
+struct wcd9xxx_i2c {
+	struct i2c_client *client;
+	struct i2c_msg xfer_msg[2];
+	struct mutex xfer_lock;
+	int mod_id;
+};
+
+static struct regmap_config wcd9xxx_base_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.can_multi_write = true,
+};
+
+static struct regmap_config wcd9xxx_i2c_base_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.can_multi_write = false,
+	.use_single_rw = true,
+};
+
+static u8 wcd9xxx_pgd_la;
+static u8 wcd9xxx_inf_la;
+
+static const int wcd9xxx_cdc_types[] = {
+	[WCD9XXX] = WCD9XXX,
+	[WCD9330] = WCD9330,
+	[WCD9335] = WCD9335,
+	[WCD934X] = WCD934X,
+};
+
+static const struct of_device_id wcd9xxx_of_match[] = {
+	{ .compatible = "qcom,tasha-i2c-pgd",
+	  .data = (void *)&wcd9xxx_cdc_types[WCD9335]},
+	{ .compatible = "qcom,wcd9xxx-i2c",
+	  .data = (void *)&wcd9xxx_cdc_types[WCD9330]},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, wcd9xxx_of_match);
+
+static int wcd9xxx_slim_device_up(struct slim_device *sldev);
+static int wcd9xxx_slim_device_down(struct slim_device *sldev);
+
+struct wcd9xxx_i2c wcd9xxx_modules[MAX_WCD9XXX_DEVICE];
+
+static int wcd9xxx_slim_multi_reg_write(struct wcd9xxx *wcd9xxx,
+					const void *data, size_t count)
+{
+	unsigned int reg;
+	struct device *dev;
+	u8 val[WCD9XXX_PAGE_SIZE];
+	int ret = 0;
+	int i = 0;
+	int n = 0;
+	unsigned int page_num;
+	size_t num_regs = (count / (REG_BYTES + VAL_BYTES));
+	struct wcd9xxx_reg_val *bulk_reg;
+	u8 *buf;
+
+	dev = wcd9xxx->dev;
+	if (!data) {
+		dev_err(dev, "%s: data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (num_regs == 0)
+		return -EINVAL;
+
+	bulk_reg = kzalloc(num_regs * (sizeof(struct wcd9xxx_reg_val)),
+			   GFP_KERNEL);
+	if (!bulk_reg)
+		return -ENOMEM;
+
+	buf = (u8 *)data;
+	reg = *(u16 *)buf;
+	page_num = WCD9XXX_PAGE_NUM(reg);
+	for (i = 0, n = 0; n < num_regs; i++, n++) {
+		reg = *(u16 *)buf;
+		if (page_num != WCD9XXX_PAGE_NUM(reg)) {
+			ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg,
+						      i, false);
+			page_num = WCD9XXX_PAGE_NUM(reg);
+			i = 0;
+		}
+		buf += REG_BYTES;
+		val[i] = *buf;
+		buf += VAL_BYTES;
+		bulk_reg[i].reg = reg;
+		bulk_reg[i].buf = &val[i];
+		bulk_reg[i].bytes = 1;
+	}
+	ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg,
+				      i, false);
+	if (ret)
+		dev_err(dev, "%s: error writing bulk regs\n",
+			__func__);
+
+	kfree(bulk_reg);
+	return ret;
+}
+
+/*
+ * wcd9xxx_interface_reg_read: Read slim interface registers
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ * @reg: register adderss
+ *
+ * Returns register value in success and negative error code in case of failure
+ */
+int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg)
+{
+	u8 val;
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx->read_dev(wcd9xxx, reg, 1, (void *)&val,
+				true);
+	if (ret < 0)
+		dev_err(wcd9xxx->dev, "%s: Codec read 0x%x failed\n",
+			__func__, reg);
+	else
+		dev_dbg(wcd9xxx->dev, "%s: Read 0x%02x from 0x%x\n",
+			__func__, val, reg);
+
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	if (ret < 0)
+		return ret;
+	else
+		return val;
+}
+EXPORT_SYMBOL(wcd9xxx_interface_reg_read);
+
+/*
+ * wcd9xxx_interface_reg_write: Write slim interface registers
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ * @reg: register adderss
+ * @val: value of the register to be written
+ *
+ * Returns 0 for success and negative error code in case of failure
+ */
+int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		     u8 val)
+{
+	int ret;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	ret = wcd9xxx->write_dev(wcd9xxx, reg, 1, (void *)&val, true);
+	dev_dbg(wcd9xxx->dev, "%s: Write %02x to 0x%x ret(%d)\n",
+		__func__, val, reg, ret);
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_interface_reg_write);
+
+static int wcd9xxx_slim_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg,
+				int bytes, void *dest, bool interface)
+{
+	int ret;
+	struct slim_ele_access msg;
+	int slim_read_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+
+	msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	if (!wcd9xxx->dev_up) {
+		dev_dbg_ratelimited(
+			wcd9xxx->dev, "%s: No read allowed. dev_up = %d\n",
+			__func__, wcd9xxx->dev_up);
+		return 0;
+	}
+
+	while (1) {
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_request_val_element(interface ?
+			       wcd9xxx->slim_slave : wcd9xxx->slim,
+			       &msg, dest, bytes);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+		if (likely(ret == 0) || (--slim_read_tries == 0))
+			break;
+		usleep_range(5000, 5100);
+	}
+
+	if (ret)
+		dev_err(wcd9xxx->dev, "%s: Error, Codec read failed (%d)\n",
+			__func__, ret);
+
+	return ret;
+}
+
+/*
+ * Interface specifies whether the write is to the interface or general
+ * registers.
+ */
+static int wcd9xxx_slim_write_device(struct wcd9xxx *wcd9xxx,
+		unsigned short reg, int bytes, void *src, bool interface)
+{
+	int ret;
+	struct slim_ele_access msg;
+	int slim_write_tries = WCD9XXX_SLIM_RW_MAX_TRIES;
+
+	msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	msg.num_bytes = bytes;
+	msg.comp = NULL;
+
+	if (!wcd9xxx->dev_up) {
+		dev_dbg_ratelimited(
+			wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n",
+			__func__, wcd9xxx->dev_up);
+		return 0;
+	}
+
+	while (1) {
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_change_val_element(interface ?
+			      wcd9xxx->slim_slave : wcd9xxx->slim,
+			      &msg, src, bytes);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+		if (likely(ret == 0) || (--slim_write_tries == 0))
+			break;
+		usleep_range(5000, 5100);
+	}
+
+	if (ret)
+		pr_err("%s: Error, Codec write failed (%d)\n", __func__, ret);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_get_allowed_slice(struct wcd9xxx *wcd9xxx,
+					  int bytes)
+{
+	int allowed_sz = bytes;
+
+	if (likely(bytes == SLIM_REPEAT_WRITE_MAX_SLICE))
+		allowed_sz = 16;
+	else if (bytes >= 12)
+		allowed_sz = 12;
+	else if (bytes >= 8)
+		allowed_sz = 8;
+	else if (bytes >= 6)
+		allowed_sz = 6;
+	else if (bytes >= 4)
+		allowed_sz = 4;
+	else
+		allowed_sz = bytes;
+
+	return allowed_sz;
+}
+
+/*
+ * wcd9xxx_slim_write_repeat: Write the same register with multiple values
+ * @wcd9xxx: handle to wcd core
+ * @reg: register to be written
+ * @bytes: number of bytes to be written to reg
+ * @src: buffer with data content to be written to reg
+ * This API will write reg with bytes from src in a single slimbus
+ * transaction. All values from 1 to 16 are supported by this API.
+ */
+int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			      int bytes, void *src)
+{
+	int ret = 0, bytes_to_write = bytes, bytes_allowed;
+	struct slim_ele_access slim_msg;
+
+	mutex_lock(&wcd9xxx->io_lock);
+	if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X) {
+		ret = wcd9xxx_page_write(wcd9xxx, &reg);
+		if (ret)
+			goto done;
+	}
+
+	slim_msg.start_offset = WCD9XXX_REGISTER_START_OFFSET + reg;
+	slim_msg.comp = NULL;
+
+	if (unlikely(bytes > SLIM_REPEAT_WRITE_MAX_SLICE)) {
+		dev_err(wcd9xxx->dev, "%s: size %d not supported\n",
+			__func__, bytes);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!wcd9xxx->dev_up) {
+		dev_dbg_ratelimited(
+			wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n",
+			__func__, wcd9xxx->dev_up);
+		ret = 0;
+		goto done;
+	}
+
+	while (bytes_to_write > 0) {
+		bytes_allowed = wcd9xxx_slim_get_allowed_slice(wcd9xxx,
+				       bytes_to_write);
+
+		slim_msg.num_bytes = bytes_allowed;
+		mutex_lock(&wcd9xxx->xfer_lock);
+		ret = slim_user_msg(wcd9xxx->slim, wcd9xxx->slim->laddr,
+				    SLIM_MSG_MT_DEST_REFERRED_USER,
+				    SLIM_USR_MC_REPEAT_CHANGE_VALUE,
+				    &slim_msg, src, bytes_allowed);
+		mutex_unlock(&wcd9xxx->xfer_lock);
+
+		if (ret) {
+			dev_err(wcd9xxx->dev, "%s: failed, ret = %d\n",
+				__func__, ret);
+			break;
+		}
+
+		bytes_to_write = bytes_to_write - bytes_allowed;
+		src = ((u8 *)src) + bytes_allowed;
+	}
+
+done:
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_slim_write_repeat);
+
+/*
+ * wcd9xxx_slim_reserve_bw: API to reserve the slimbus bandwidth
+ * @wcd9xxx: Handle to the wcd9xxx core
+ * @bw_ops: value of the bandwidth that is requested
+ * @commit: Flag to indicate if bandwidth change is to be committed
+ *	    right away
+ */
+int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx,
+		u32 bw_ops, bool commit)
+{
+	if (!wcd9xxx || !wcd9xxx->slim) {
+		pr_err("%s: Invalid handle to %s\n",
+			__func__,
+			(!wcd9xxx) ? "wcd9xxx" : "slim_device");
+		return -EINVAL;
+	}
+
+	return slim_reservemsg_bw(wcd9xxx->slim, bw_ops, commit);
+}
+EXPORT_SYMBOL(wcd9xxx_slim_reserve_bw);
+
+/*
+ * wcd9xxx_slim_bulk_write: API to write multiple registers with one descriptor
+ * @wcd9xxx: Handle to the wcd9xxx core
+ * @wcd9xxx_reg_val: structure holding register and values to be written
+ * @size: Indicates number of messages to be written with one descriptor
+ * @is_interface: Indicates whether the register is for slim interface or for
+ *	       general registers.
+ * @return: returns 0 if success or error information to the caller in case
+ *	    of failure.
+ */
+int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx,
+			    struct wcd9xxx_reg_val *bulk_reg,
+			    unsigned int size, bool is_interface)
+{
+	int ret, i;
+	struct slim_val_inf *msgs;
+	unsigned short reg;
+
+	if (!bulk_reg || !size || !wcd9xxx) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!wcd9xxx->dev_up) {
+		dev_dbg_ratelimited(
+			wcd9xxx->dev, "%s: No write allowed. dev_up = %d\n",
+			__func__, wcd9xxx->dev_up);
+		return 0;
+	}
+
+	msgs = kzalloc(size * (sizeof(struct slim_val_inf)), GFP_KERNEL);
+	if (!msgs) {
+		ret = -ENOMEM;
+		goto mem_fail;
+	}
+
+	mutex_lock(&wcd9xxx->io_lock);
+	reg = bulk_reg->reg;
+	for (i = 0; i < size; i++) {
+		msgs[i].start_offset = WCD9XXX_REGISTER_START_OFFSET +
+					(bulk_reg->reg & 0xFF);
+		msgs[i].num_bytes = bulk_reg->bytes;
+		msgs[i].wbuf = bulk_reg->buf;
+		bulk_reg++;
+	}
+	ret = wcd9xxx_page_write(wcd9xxx, &reg);
+	if (ret) {
+		pr_err("%s: Page write error for reg: 0x%x\n",
+			__func__, reg);
+		goto err;
+	}
+
+	ret = slim_bulk_msg_write(is_interface ?
+				  wcd9xxx->slim_slave : wcd9xxx->slim,
+				  SLIM_MSG_MT_CORE,
+				  SLIM_MSG_MC_CHANGE_VALUE, msgs, size,
+				  NULL, NULL);
+	if (ret)
+		pr_err("%s: Error, Codec bulk write failed (%d)\n",
+			__func__, ret);
+	/* 100 usec sleep is needed as per HW requirement */
+	usleep_range(100, 110);
+err:
+	mutex_unlock(&wcd9xxx->io_lock);
+	kfree(msgs);
+mem_fail:
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_slim_bulk_write);
+
+static int wcd9xxx_num_irq_regs(const struct wcd9xxx *wcd9xxx)
+{
+	return (wcd9xxx->codec_type->num_irqs / 8) +
+		((wcd9xxx->codec_type->num_irqs % 8) ? 1 : 0);
+}
+
+static int wcd9xxx_regmap_init_cache(struct wcd9xxx *wcd9xxx)
+{
+	struct regmap_config *regmap_config;
+	int rc;
+
+	regmap_config = wcd9xxx_get_regmap_config(wcd9xxx->type);
+	if (!regmap_config) {
+		dev_err(wcd9xxx->dev, "regmap config is not defined\n");
+		return -EINVAL;
+	}
+
+	rc = regmap_reinit_cache(wcd9xxx->regmap, regmap_config);
+	if (rc != 0) {
+		dev_err(wcd9xxx->dev, "%s:Failed to reinit register cache: %d\n",
+			__func__, rc);
+	}
+
+	return rc;
+}
+
+static int wcd9xxx_device_init(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0, i;
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+	regmap_patch_fptr regmap_apply_patch = NULL;
+
+	mutex_init(&wcd9xxx->io_lock);
+	mutex_init(&wcd9xxx->xfer_lock);
+
+	ret = wcd9xxx_bringup(wcd9xxx->dev);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto err_bring_up;
+	}
+
+	wcd9xxx->codec_type = devm_kzalloc(wcd9xxx->dev,
+			sizeof(struct wcd9xxx_codec_type), GFP_KERNEL);
+	if (!wcd9xxx->codec_type) {
+		ret = -ENOMEM;
+		goto err_bring_up;
+	}
+	ret = wcd9xxx_get_codec_info(wcd9xxx->dev);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto fail_cdc_fill;
+	}
+	wcd9xxx->version = wcd9xxx->codec_type->version;
+	if (!wcd9xxx->codec_type->dev || !wcd9xxx->codec_type->size)
+		goto fail_cdc_fill;
+
+	core_res->parent = wcd9xxx;
+	core_res->dev = wcd9xxx->dev;
+	core_res->intr_table = wcd9xxx->codec_type->intr_tbl;
+	core_res->intr_table_size = wcd9xxx->codec_type->intr_tbl_size;
+
+	for (i = 0; i < WCD9XXX_INTR_REG_MAX; i++)
+		wcd9xxx->core_res.intr_reg[i] =
+			wcd9xxx->codec_type->intr_reg[i];
+
+	wcd9xxx_core_res_init(&wcd9xxx->core_res,
+			      wcd9xxx->codec_type->num_irqs,
+			      wcd9xxx_num_irq_regs(wcd9xxx),
+			      wcd9xxx->regmap);
+
+	if (wcd9xxx_core_irq_init(&wcd9xxx->core_res))
+		goto err;
+
+	ret = wcd9xxx_regmap_init_cache(wcd9xxx);
+	if (ret)
+		goto err_irq;
+
+	regmap_apply_patch = wcd9xxx_get_regmap_reg_patch(
+			wcd9xxx->type);
+	if (regmap_apply_patch) {
+		ret = regmap_apply_patch(wcd9xxx->regmap,
+				wcd9xxx->version);
+		if (ret)
+			dev_err(wcd9xxx->dev,
+					"Failed to register patch: %d\n", ret);
+	}
+
+	ret = mfd_add_devices(wcd9xxx->dev, -1, wcd9xxx->codec_type->dev,
+			      wcd9xxx->codec_type->size, NULL, 0, NULL);
+	if (ret != 0) {
+		dev_err(wcd9xxx->dev, "Failed to add children: %d\n", ret);
+		goto err_irq;
+	}
+
+	ret = device_init_wakeup(wcd9xxx->dev, true);
+	if (ret) {
+		dev_err(wcd9xxx->dev, "Device wakeup init failed: %d\n", ret);
+		goto err_irq;
+	}
+
+	return ret;
+err_irq:
+	wcd9xxx_irq_exit(&wcd9xxx->core_res);
+fail_cdc_fill:
+	devm_kfree(wcd9xxx->dev, wcd9xxx->codec_type);
+	wcd9xxx->codec_type = NULL;
+err:
+	wcd9xxx_bringdown(wcd9xxx->dev);
+	wcd9xxx_core_res_deinit(&wcd9xxx->core_res);
+err_bring_up:
+	mutex_destroy(&wcd9xxx->io_lock);
+	mutex_destroy(&wcd9xxx->xfer_lock);
+	return ret;
+}
+
+static void wcd9xxx_device_exit(struct wcd9xxx *wcd9xxx)
+{
+	device_init_wakeup(wcd9xxx->dev, false);
+	wcd9xxx_irq_exit(&wcd9xxx->core_res);
+	wcd9xxx_bringdown(wcd9xxx->dev);
+	wcd9xxx_reset_low(wcd9xxx->dev);
+	wcd9xxx_core_res_deinit(&wcd9xxx->core_res);
+	mutex_destroy(&wcd9xxx->io_lock);
+	mutex_destroy(&wcd9xxx->xfer_lock);
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		slim_remove_device(wcd9xxx->slim_slave);
+}
+
+
+#ifdef CONFIG_DEBUG_FS
+struct wcd9xxx *debugCodec;
+
+static struct dentry *debugfs_wcd9xxx_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_power_state;
+static struct dentry *debugfs_reg_dump;
+
+static unsigned char read_data;
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (kstrtoul(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+		} else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t wcd9xxx_slimslave_reg_show(char __user *ubuf, size_t count,
+					  loff_t *ppos)
+{
+	int i, reg_val, len;
+	ssize_t total = 0;
+	char tmp_buf[25]; /* each line is 12 bytes but 25 for margin of error */
+
+	for (i = (int) *ppos / 12; i <= SLIM_MAX_REG_ADDR; i++) {
+		reg_val = wcd9xxx_interface_reg_read(debugCodec, i);
+		len = snprintf(tmp_buf, sizeof(tmp_buf),
+			"0x%.3x: 0x%.2x\n", i, reg_val);
+
+		if ((total + len) >= count - 1)
+			break;
+		if (copy_to_user((ubuf + total), tmp_buf, len)) {
+			pr_err("%s: fail to copy reg dump\n", __func__);
+			total = -EFAULT;
+			goto copy_err;
+		}
+		*ppos += len;
+		total += len;
+	}
+
+copy_err:
+	return total;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[8];
+	char *access_str = file->private_data;
+	ssize_t ret_cnt;
+
+	if (*ppos < 0 || !count)
+		return -EINVAL;
+
+	if (!strcmp(access_str, "slimslave_peek")) {
+		snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+		ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf,
+					       strnlen(lbuf, 7));
+	} else if (!strcmp(access_str, "slimslave_reg_dump")) {
+		ret_cnt = wcd9xxx_slimslave_reg_show(ubuf, count, ppos);
+	} else {
+		pr_err("%s: %s not permitted to read\n", __func__, access_str);
+		ret_cnt = -EPERM;
+	}
+
+	return ret_cnt;
+}
+
+static void wcd9xxx_set_reset_pin_state(struct wcd9xxx *wcd9xxx,
+					struct wcd9xxx_pdata *pdata,
+					bool active)
+{
+	if (wcd9xxx->wcd_rst_np) {
+		if (active)
+			msm_cdc_pinctrl_select_active_state(
+						wcd9xxx->wcd_rst_np);
+		else
+			msm_cdc_pinctrl_select_sleep_state(
+						wcd9xxx->wcd_rst_np);
+
+		return;
+	} else if (gpio_is_valid(wcd9xxx->reset_gpio)) {
+		gpio_direction_output(wcd9xxx->reset_gpio,
+				      (active == true ? 1 : 0));
+	}
+}
+
+static int codec_debug_process_cdc_power(char *lbuf)
+{
+	long int param;
+	int rc;
+	struct wcd9xxx_pdata *pdata;
+
+	if (wcd9xxx_get_intf_type() != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		pr_err("%s: CODEC is not in SLIMBUS mode\n", __func__);
+		rc = -EPERM;
+		goto error_intf;
+	}
+
+	rc = get_parameters(lbuf, &param, 1);
+
+	if (likely(!rc)) {
+		pdata = debugCodec->slim->dev.platform_data;
+		if (param == 0) {
+			wcd9xxx_slim_device_down(debugCodec->slim);
+			msm_cdc_disable_static_supplies(debugCodec->dev,
+							debugCodec->supplies,
+							pdata->regulator,
+							pdata->num_supplies);
+			wcd9xxx_set_reset_pin_state(debugCodec, pdata, false);
+		} else if (param == 1) {
+			msm_cdc_enable_static_supplies(debugCodec->dev,
+						       debugCodec->supplies,
+						       pdata->regulator,
+						       pdata->num_supplies);
+			usleep_range(1000, 2000);
+			wcd9xxx_set_reset_pin_state(debugCodec, pdata, false);
+			usleep_range(1000, 2000);
+			wcd9xxx_set_reset_pin_state(debugCodec, pdata, true);
+			usleep_range(1000, 2000);
+			wcd9xxx_slim_device_up(debugCodec->slim);
+		} else {
+			pr_err("%s: invalid command %ld\n", __func__, param);
+		}
+	}
+
+error_intf:
+	return rc;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *access_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	long int param[5];
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(access_str, "slimslave_poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= 0x3FF) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			wcd9xxx_interface_reg_write(debugCodec, param[0],
+				param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "slimslave_peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= 0x3FF) && (rc == 0))
+			read_data = wcd9xxx_interface_reg_read(debugCodec,
+				param[0]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "power_state")) {
+		rc = codec_debug_process_cdc_power(lbuf);
+	}
+
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read
+};
+#endif
+
+static struct wcd9xxx_i2c *wcd9xxx_i2c_get_device_info(struct wcd9xxx *wcd9xxx,
+						u16 reg)
+{
+	u16 mask = 0x0f00;
+	int value = 0;
+	struct wcd9xxx_i2c *wcd9xxx_i2c = NULL;
+
+	if (wcd9xxx->type == WCD9335) {
+		wcd9xxx_i2c = &wcd9xxx_modules[0];
+	} else {
+		value = ((reg & mask) >> 8) & 0x000f;
+		switch (value) {
+		case 0:
+			wcd9xxx_i2c = &wcd9xxx_modules[0];
+			break;
+		case 1:
+			wcd9xxx_i2c = &wcd9xxx_modules[1];
+			break;
+		case 2:
+			wcd9xxx_i2c = &wcd9xxx_modules[2];
+			break;
+		case 3:
+			wcd9xxx_i2c = &wcd9xxx_modules[3];
+			break;
+
+		default:
+			break;
+		}
+	}
+	return wcd9xxx_i2c;
+}
+
+static int wcd9xxx_i2c_write_device(struct wcd9xxx *wcd9xxx, u16 reg, u8 *value,
+				u32 bytes)
+{
+
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	u8 data[bytes + 1];
+	struct wcd9xxx_i2c *wcd9xxx_i2c;
+
+	wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg);
+	if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	reg_addr = (u8)reg;
+	msg = &wcd9xxx_i2c->xfer_msg[0];
+	msg->addr = wcd9xxx_i2c->client->addr;
+	msg->len = bytes + 1;
+	msg->flags = 0;
+	data[0] = reg;
+	data[1] = *value;
+	msg->buf = data;
+	ret = i2c_transfer(wcd9xxx_i2c->client->adapter,
+			   wcd9xxx_i2c->xfer_msg, 1);
+	/* Try again if the write fails */
+	if (ret != 1) {
+		ret = i2c_transfer(wcd9xxx_i2c->client->adapter,
+						wcd9xxx_i2c->xfer_msg, 1);
+		if (ret != 1) {
+			pr_err("failed to write the device\n");
+			return ret;
+		}
+	}
+	pr_debug("write success register = %x val = %x\n", reg, data[1]);
+	return 0;
+}
+
+
+static int wcd9xxx_i2c_read_device(struct wcd9xxx *wcd9xxx, unsigned short reg,
+				  int bytes, unsigned char *dest)
+{
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	struct wcd9xxx_i2c *wcd9xxx_i2c;
+	u8 i = 0;
+
+	wcd9xxx_i2c = wcd9xxx_i2c_get_device_info(wcd9xxx, reg);
+	if (wcd9xxx_i2c == NULL || wcd9xxx_i2c->client == NULL) {
+		pr_err("failed to get device info\n");
+		return -ENODEV;
+	}
+	for (i = 0; i < bytes; i++) {
+		reg_addr = (u8)reg++;
+		msg = &wcd9xxx_i2c->xfer_msg[0];
+		msg->addr = wcd9xxx_i2c->client->addr;
+		msg->len = 1;
+		msg->flags = 0;
+		msg->buf = &reg_addr;
+
+		msg = &wcd9xxx_i2c->xfer_msg[1];
+		msg->addr = wcd9xxx_i2c->client->addr;
+		msg->len = 1;
+		msg->flags = I2C_M_RD;
+		msg->buf = dest++;
+		ret = i2c_transfer(wcd9xxx_i2c->client->adapter,
+				wcd9xxx_i2c->xfer_msg, 2);
+
+		/* Try again if read fails first time */
+		if (ret != 2) {
+			ret = i2c_transfer(wcd9xxx_i2c->client->adapter,
+					   wcd9xxx_i2c->xfer_msg, 2);
+			if (ret != 2) {
+				pr_err("failed to read wcd9xxx register\n");
+				return ret;
+			}
+		}
+	}
+	return 0;
+}
+
+int wcd9xxx_i2c_read(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *dest, bool interface_reg)
+{
+	return wcd9xxx_i2c_read_device(wcd9xxx, reg, bytes, dest);
+}
+
+int wcd9xxx_i2c_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			 int bytes, void *src, bool interface_reg)
+{
+	return wcd9xxx_i2c_write_device(wcd9xxx, reg, src, bytes);
+}
+
+static int wcd9xxx_i2c_get_client_index(struct i2c_client *client,
+					int *wcd9xx_index)
+{
+	int ret = 0;
+
+	switch (client->addr) {
+	case WCD9XXX_I2C_TOP_SLAVE_ADDR:
+		*wcd9xx_index = WCD9XXX_I2C_TOP_LEVEL;
+	break;
+	case WCD9XXX_ANALOG_I2C_SLAVE_ADDR:
+		*wcd9xx_index = WCD9XXX_I2C_ANALOG;
+	break;
+	case WCD9XXX_DIGITAL1_I2C_SLAVE_ADDR:
+		*wcd9xx_index = WCD9XXX_I2C_DIGITAL_1;
+	break;
+	case WCD9XXX_DIGITAL2_I2C_SLAVE_ADDR:
+		*wcd9xx_index = WCD9XXX_I2C_DIGITAL_2;
+	break;
+	default:
+		ret = -EINVAL;
+	break;
+	}
+	return ret;
+}
+
+static int wcd9xxx_i2c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	struct wcd9xxx *wcd9xxx = NULL;
+	struct wcd9xxx_pdata *pdata = NULL;
+	int val = 0;
+	int ret = 0;
+	int wcd9xx_index = 0;
+	struct device *dev;
+	int intf_type;
+	const struct of_device_id *of_id;
+
+	intf_type = wcd9xxx_get_intf_type();
+
+	pr_debug("%s: interface status %d\n", __func__, intf_type);
+	if (intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		dev_dbg(&client->dev, "%s:Codec is detected in slimbus mode\n",
+			__func__);
+		return -ENODEV;
+	} else if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+		ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index);
+		if (ret != 0)
+			dev_err(&client->dev, "%s: I2C set codec I2C\n"
+				"client failed\n", __func__);
+		else {
+			dev_err(&client->dev, "%s:probe for other slaves\n"
+				"devices of codec I2C slave Addr = %x\n",
+				__func__, client->addr);
+			wcd9xxx_modules[wcd9xx_index].client = client;
+		}
+		return ret;
+	} else if (intf_type == WCD9XXX_INTERFACE_TYPE_PROBING) {
+		dev = &client->dev;
+		if (client->dev.of_node) {
+			dev_dbg(&client->dev, "%s:Platform data\n"
+				"from device tree\n", __func__);
+			pdata = wcd9xxx_populate_dt_data(&client->dev);
+			if (!pdata) {
+				dev_err(&client->dev,
+					"%s: Fail to obtain pdata from device tree\n",
+					__func__);
+				ret = -EINVAL;
+				goto fail;
+			}
+			client->dev.platform_data = pdata;
+		} else {
+			dev_dbg(&client->dev, "%s:Platform data from\n"
+				"board file\n", __func__);
+			pdata = client->dev.platform_data;
+		}
+		wcd9xxx = devm_kzalloc(&client->dev, sizeof(struct wcd9xxx),
+				       GFP_KERNEL);
+		if (!wcd9xxx) {
+			ret = -ENOMEM;
+			goto fail;
+		}
+
+		if (!pdata) {
+			dev_dbg(&client->dev, "no platform data?\n");
+			ret = -EINVAL;
+			goto fail;
+		}
+		wcd9xxx->type = WCD9XXX;
+		if (client->dev.of_node) {
+			of_id = of_match_device(wcd9xxx_of_match, &client->dev);
+			if (of_id) {
+				wcd9xxx->type = *((int *)of_id->data);
+				dev_info(&client->dev, "%s: codec type is %d\n",
+					 __func__, wcd9xxx->type);
+			}
+		} else {
+			dev_info(&client->dev, "%s: dev.of_node is NULL, default to WCD9XXX\n",
+				 __func__);
+			wcd9xxx->type = WCD9XXX;
+		}
+		wcd9xxx->regmap = wcd9xxx_regmap_init(&client->dev,
+				&wcd9xxx_i2c_base_regmap_config);
+		if (IS_ERR(wcd9xxx->regmap)) {
+			ret = PTR_ERR(wcd9xxx->regmap);
+			dev_err(&client->dev, "%s: Failed to allocate register map: %d\n",
+					__func__, ret);
+			goto err_codec;
+		}
+		wcd9xxx->reset_gpio = pdata->reset_gpio;
+		wcd9xxx->wcd_rst_np = pdata->wcd_rst_np;
+
+		if (!wcd9xxx->wcd_rst_np) {
+			pdata->use_pinctrl = false;
+			dev_err(&client->dev, "%s: pinctrl not used for rst_n\n",
+				__func__);
+			goto err_codec;
+		}
+
+		if (i2c_check_functionality(client->adapter,
+					    I2C_FUNC_I2C) == 0) {
+			dev_dbg(&client->dev, "can't talk I2C?\n");
+			ret = -EIO;
+			goto fail;
+		}
+		dev_set_drvdata(&client->dev, wcd9xxx);
+		wcd9xxx->dev = &client->dev;
+		wcd9xxx->dev_up = true;
+		if (client->dev.of_node)
+			wcd9xxx->mclk_rate = pdata->mclk_rate;
+
+		wcd9xxx->num_of_supplies = pdata->num_supplies;
+		ret = msm_cdc_init_supplies(wcd9xxx->dev, &wcd9xxx->supplies,
+					    pdata->regulator,
+					    pdata->num_supplies);
+		if (!wcd9xxx->supplies) {
+			dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n",
+				__func__);
+			goto err_codec;
+		}
+		ret = msm_cdc_enable_static_supplies(wcd9xxx->dev,
+						     wcd9xxx->supplies,
+						     pdata->regulator,
+						     pdata->num_supplies);
+		if (ret) {
+			dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n",
+				__func__);
+			goto err_codec;
+		}
+		/* For WCD9335, it takes about 600us for the Vout_A and
+		 * Vout_D to be ready after BUCK_SIDO is powered up\
+		 * SYS_RST_N shouldn't be pulled high during this time
+		 */
+		if (wcd9xxx->type == WCD9335)
+			usleep_range(600, 650);
+		else
+			usleep_range(5, 10);
+
+		ret = wcd9xxx_reset(wcd9xxx->dev);
+		if (ret) {
+			pr_err("%s: Resetting Codec failed\n", __func__);
+			goto err_supplies;
+		}
+
+		ret = wcd9xxx_i2c_get_client_index(client, &wcd9xx_index);
+		if (ret != 0) {
+			pr_err("%s:Set codec I2C client failed\n", __func__);
+			goto err_supplies;
+		}
+
+		wcd9xxx_modules[wcd9xx_index].client = client;
+		wcd9xxx->read_dev = wcd9xxx_i2c_read;
+		wcd9xxx->write_dev = wcd9xxx_i2c_write;
+		if (!wcd9xxx->dev->of_node)
+			wcd9xxx_assign_irq(&wcd9xxx->core_res,
+					pdata->irq, pdata->irq_base);
+
+		ret = wcd9xxx_device_init(wcd9xxx);
+		if (ret) {
+			pr_err("%s: error, initializing device failed (%d)\n",
+			       __func__, ret);
+			goto err_device_init;
+		}
+
+		ret = wcd9xxx_i2c_read(wcd9xxx, WCD9XXX_A_CHIP_STATUS, 1,
+				       &val, 0);
+		if (ret < 0)
+			pr_err("%s: failed to read the wcd9xxx status (%d)\n",
+			       __func__, ret);
+		if (val != wcd9xxx->codec_type->i2c_chip_status)
+			pr_err("%s: unknown chip status 0x%x\n", __func__, val);
+
+		wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_I2C);
+
+		return ret;
+	}
+
+	pr_err("%s: I2C probe in wrong state\n", __func__);
+
+
+err_device_init:
+	wcd9xxx_reset_low(wcd9xxx->dev);
+err_supplies:
+	msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies,
+				 pdata->regulator,
+				 pdata->num_supplies);
+	pdata->regulator = NULL;
+	pdata->num_supplies = 0;
+err_codec:
+	devm_kfree(&client->dev, wcd9xxx);
+	dev_set_drvdata(&client->dev, NULL);
+fail:
+	return ret;
+}
+
+static int wcd9xxx_i2c_remove(struct i2c_client *client)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = client->dev.platform_data;
+
+	wcd9xxx = dev_get_drvdata(&client->dev);
+	msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies,
+				 pdata->regulator,
+				 pdata->num_supplies);
+	wcd9xxx_device_exit(wcd9xxx);
+	dev_set_drvdata(&client->dev, NULL);
+	return 0;
+}
+
+static int wcd9xxx_dt_parse_slim_interface_dev_info(struct device *dev,
+						struct slim_device *slim_ifd)
+{
+	int ret = 0;
+	struct property *prop;
+
+	ret = of_property_read_string(dev->of_node, "qcom,cdc-slim-ifd",
+				      &slim_ifd->name);
+	if (ret) {
+		dev_err(dev, "Looking up %s property in node %s failed",
+			"qcom,cdc-slim-ifd-dev", dev->of_node->full_name);
+		return -ENODEV;
+	}
+	prop = of_find_property(dev->of_node,
+			"qcom,cdc-slim-ifd-elemental-addr", NULL);
+	if (!prop) {
+		dev_err(dev, "Looking up %s property in node %s failed",
+			"qcom,cdc-slim-ifd-elemental-addr",
+			dev->of_node->full_name);
+		return -ENODEV;
+	} else if (prop->length != 6) {
+		dev_err(dev, "invalid codec slim ifd addr. addr length = %d\n",
+			      prop->length);
+		return -ENODEV;
+	}
+	memcpy(slim_ifd->e_addr, prop->value, 6);
+
+	return 0;
+}
+
+static int wcd9xxx_slim_get_laddr(struct slim_device *sb,
+				  const u8 *e_addr, u8 e_len, u8 *laddr)
+{
+	int ret;
+	const unsigned long timeout = jiffies +
+				      msecs_to_jiffies(SLIMBUS_PRESENT_TIMEOUT);
+
+	do {
+		ret = slim_get_logical_addr(sb, e_addr, e_len, laddr);
+		if (!ret)
+			break;
+		/* Give SLIMBUS time to report present and be ready. */
+		usleep_range(1000, 1100);
+		pr_debug_ratelimited("%s: retyring get logical addr\n",
+				     __func__);
+	} while time_before(jiffies, timeout);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_probe(struct slim_device *slim)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata;
+	int ret = 0;
+	int intf_type;
+
+	intf_type = wcd9xxx_get_intf_type();
+
+	if (!slim) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	if (intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+		dev_dbg(&slim->dev, "%s:Codec is detected in I2C mode\n",
+			__func__);
+		return -ENODEV;
+	}
+	if (slim->dev.of_node) {
+		dev_info(&slim->dev, "Platform data from device tree\n");
+		pdata = wcd9xxx_populate_dt_data(&slim->dev);
+		if (!pdata) {
+			dev_err(&slim->dev,
+				"%s: Fail to obtain pdata from device tree\n",
+				__func__);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		ret = wcd9xxx_dt_parse_slim_interface_dev_info(&slim->dev,
+				&pdata->slimbus_slave_device);
+		if (ret) {
+			dev_err(&slim->dev, "Error, parsing slim interface\n");
+			devm_kfree(&slim->dev, pdata);
+			ret = -EINVAL;
+			goto err;
+		}
+		slim->dev.platform_data = pdata;
+
+	} else {
+		dev_info(&slim->dev, "Platform data from board file\n");
+		pdata = slim->dev.platform_data;
+	}
+
+	if (!pdata) {
+		dev_err(&slim->dev, "Error, no platform data\n");
+		ret = -EINVAL;
+		goto err;
+	}
+
+	wcd9xxx = devm_kzalloc(&slim->dev, sizeof(struct wcd9xxx),
+				GFP_KERNEL);
+	if (!wcd9xxx) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	if (!slim->ctrl) {
+		dev_err(&slim->dev, "%s: Error, no SLIMBUS control data\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_codec;
+	}
+	wcd9xxx->type = slim_get_device_id(slim)->driver_data;
+	dev_info(&slim->dev, "%s: probing for wcd type: %d, name: %s\n",
+		 __func__, wcd9xxx->type, slim_get_device_id(slim)->name);
+
+	/* wcd9xxx members init */
+	wcd9xxx->multi_reg_write = wcd9xxx_slim_multi_reg_write;
+	wcd9xxx->slim = slim;
+	slim_set_clientdata(slim, wcd9xxx);
+	wcd9xxx->reset_gpio = pdata->reset_gpio;
+	wcd9xxx->dev = &slim->dev;
+	wcd9xxx->mclk_rate = pdata->mclk_rate;
+	wcd9xxx->dev_up = true;
+	wcd9xxx->wcd_rst_np = pdata->wcd_rst_np;
+
+	wcd9xxx->regmap = wcd9xxx_regmap_init(&slim->dev,
+					      &wcd9xxx_base_regmap_config);
+	if (IS_ERR(wcd9xxx->regmap)) {
+		ret = PTR_ERR(wcd9xxx->regmap);
+		dev_err(&slim->dev, "%s: Failed to allocate register map: %d\n",
+			__func__, ret);
+		goto err_codec;
+	}
+
+	if (!wcd9xxx->wcd_rst_np) {
+		pdata->use_pinctrl = false;
+		dev_err(&slim->dev, "%s: pinctrl not used for rst_n\n",
+			__func__);
+		goto err_codec;
+	}
+
+	wcd9xxx->num_of_supplies = pdata->num_supplies;
+	ret = msm_cdc_init_supplies(&slim->dev, &wcd9xxx->supplies,
+				    pdata->regulator,
+				    pdata->num_supplies);
+	if (!wcd9xxx->supplies) {
+		dev_err(wcd9xxx->dev, "%s: Cannot init wcd supplies\n",
+			__func__);
+		goto err_codec;
+	}
+	ret = msm_cdc_enable_static_supplies(wcd9xxx->dev,
+					     wcd9xxx->supplies,
+					     pdata->regulator,
+					     pdata->num_supplies);
+	if (ret) {
+		dev_err(wcd9xxx->dev, "%s: wcd static supply enable failed!\n",
+			__func__);
+		goto err_codec;
+	}
+
+	/*
+	 * For WCD9335, it takes about 600us for the Vout_A and
+	 * Vout_D to be ready after BUCK_SIDO is powered up.
+	 * SYS_RST_N shouldn't be pulled high during this time
+	 */
+	if (wcd9xxx->type == WCD9335 || wcd9xxx->type == WCD934X)
+		usleep_range(600, 650);
+	else
+		usleep_range(5, 10);
+
+	ret = wcd9xxx_reset(&slim->dev);
+	if (ret) {
+		dev_err(&slim->dev, "%s: Resetting Codec failed\n", __func__);
+		goto err_supplies;
+	}
+
+	ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim, wcd9xxx->slim->e_addr,
+				     ARRAY_SIZE(wcd9xxx->slim->e_addr),
+				     &wcd9xxx->slim->laddr);
+	if (ret) {
+		dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n",
+		       __func__, wcd9xxx->slim->name, ret);
+		goto err_reset;
+	}
+	wcd9xxx->read_dev = wcd9xxx_slim_read_device;
+	wcd9xxx->write_dev = wcd9xxx_slim_write_device;
+	wcd9xxx_pgd_la = wcd9xxx->slim->laddr;
+	wcd9xxx->slim_slave = &pdata->slimbus_slave_device;
+	if (!wcd9xxx->dev->of_node)
+		wcd9xxx_assign_irq(&wcd9xxx->core_res,
+					pdata->irq, pdata->irq_base);
+
+	ret = slim_add_device(slim->ctrl, wcd9xxx->slim_slave);
+	if (ret) {
+		dev_err(&slim->dev, "%s: error, adding SLIMBUS device failed\n",
+			__func__);
+		goto err_reset;
+	}
+
+	ret = wcd9xxx_slim_get_laddr(wcd9xxx->slim_slave,
+				     wcd9xxx->slim_slave->e_addr,
+				     ARRAY_SIZE(wcd9xxx->slim_slave->e_addr),
+				     &wcd9xxx->slim_slave->laddr);
+	if (ret) {
+		dev_err(&slim->dev, "%s: failed to get slimbus %s logical address: %d\n",
+		       __func__, wcd9xxx->slim->name, ret);
+		goto err_slim_add;
+	}
+	wcd9xxx_inf_la = wcd9xxx->slim_slave->laddr;
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_SLIMBUS);
+
+	ret = wcd9xxx_device_init(wcd9xxx);
+	if (ret) {
+		dev_err(&slim->dev, "%s: error, initializing device failed (%d)\n",
+			__func__, ret);
+		goto err_slim_add;
+	}
+#ifdef CONFIG_DEBUG_FS
+	debugCodec = wcd9xxx;
+
+	debugfs_wcd9xxx_dent = debugfs_create_dir
+		("wcd9xxx_core", 0);
+	if (!IS_ERR(debugfs_wcd9xxx_dent)) {
+		debugfs_peek = debugfs_create_file("slimslave_peek",
+		S_IFREG | 0444, debugfs_wcd9xxx_dent,
+		(void *) "slimslave_peek", &codec_debug_ops);
+
+		debugfs_poke = debugfs_create_file("slimslave_poke",
+		S_IFREG | 0444, debugfs_wcd9xxx_dent,
+		(void *) "slimslave_poke", &codec_debug_ops);
+
+		debugfs_power_state = debugfs_create_file("power_state",
+		S_IFREG | 0444, debugfs_wcd9xxx_dent,
+		(void *) "power_state", &codec_debug_ops);
+
+		debugfs_reg_dump = debugfs_create_file("slimslave_reg_dump",
+		S_IFREG | 0444, debugfs_wcd9xxx_dent,
+		(void *) "slimslave_reg_dump", &codec_debug_ops);
+	}
+#endif
+
+	return ret;
+
+err_slim_add:
+	slim_remove_device(wcd9xxx->slim_slave);
+err_reset:
+	wcd9xxx_reset_low(wcd9xxx->dev);
+err_supplies:
+	msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies,
+				 pdata->regulator,
+				 pdata->num_supplies);
+err_codec:
+	slim_set_clientdata(slim, NULL);
+err:
+	return ret;
+}
+static int wcd9xxx_slim_remove(struct slim_device *pdev)
+{
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_pdata *pdata = pdev->dev.platform_data;
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove_recursive(debugfs_wcd9xxx_dent);
+#endif
+	wcd9xxx = slim_get_devicedata(pdev);
+	wcd9xxx_deinit_slimslave(wcd9xxx);
+	slim_remove_device(wcd9xxx->slim_slave);
+	msm_cdc_release_supplies(wcd9xxx->dev, wcd9xxx->supplies,
+				 pdata->regulator,
+				 pdata->num_supplies);
+	wcd9xxx_device_exit(wcd9xxx);
+	slim_set_clientdata(pdev, NULL);
+	return 0;
+}
+
+static int wcd9xxx_device_up(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+	struct wcd9xxx_core_resource *wcd9xxx_res = &wcd9xxx->core_res;
+
+	dev_info(wcd9xxx->dev, "%s: codec bring up\n", __func__);
+	wcd9xxx_bringup(wcd9xxx->dev);
+	ret = wcd9xxx_irq_init(wcd9xxx_res);
+	if (ret) {
+		pr_err("%s: wcd9xx_irq_init failed : %d\n", __func__, ret);
+	} else {
+		if (wcd9xxx->post_reset)
+			ret = wcd9xxx->post_reset(wcd9xxx);
+	}
+	return ret;
+}
+
+static int wcd9xxx_slim_device_reset(struct slim_device *sldev)
+{
+	int ret;
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+
+	if (!wcd9xxx) {
+		pr_err("%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_info(wcd9xxx->dev, "%s: device reset, dev_up = %d\n",
+		__func__, wcd9xxx->dev_up);
+	if (wcd9xxx->dev_up)
+		return 0;
+
+	ret = wcd9xxx_reset(wcd9xxx->dev);
+	if (ret)
+		dev_err(wcd9xxx->dev, "%s: Resetting Codec failed\n", __func__);
+
+	return ret;
+}
+
+static int wcd9xxx_slim_device_up(struct slim_device *sldev)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+
+	if (!wcd9xxx) {
+		pr_err("%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_info(wcd9xxx->dev, "%s: slim device up, dev_up = %d\n",
+		__func__, wcd9xxx->dev_up);
+	if (wcd9xxx->dev_up)
+		return 0;
+
+	wcd9xxx->dev_up = true;
+	return wcd9xxx_device_up(wcd9xxx);
+}
+
+static int wcd9xxx_slim_device_down(struct slim_device *sldev)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+
+	if (!wcd9xxx) {
+		pr_err("%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_info(wcd9xxx->dev, "%s: device down, dev_up = %d\n",
+		__func__, wcd9xxx->dev_up);
+	if (!wcd9xxx->dev_up)
+		return 0;
+
+	wcd9xxx->dev_up = false;
+	wcd9xxx_irq_exit(&wcd9xxx->core_res);
+	if (wcd9xxx->dev_down)
+		wcd9xxx->dev_down(wcd9xxx);
+	wcd9xxx_reset_low(wcd9xxx->dev);
+	return 0;
+}
+
+static int wcd9xxx_slim_resume(struct slim_device *sldev)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+
+	return wcd9xxx_core_res_resume(&wcd9xxx->core_res);
+}
+
+static int wcd9xxx_i2c_resume(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev);
+
+	if (wcd9xxx)
+		return wcd9xxx_core_res_resume(&wcd9xxx->core_res);
+	else
+		return 0;
+}
+
+static int wcd9xxx_slim_suspend(struct slim_device *sldev, pm_message_t pmesg)
+{
+	struct wcd9xxx *wcd9xxx = slim_get_devicedata(sldev);
+
+	return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg);
+}
+
+static int wcd9xxx_i2c_suspend(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev);
+	pm_message_t pmesg = {0};
+
+	if (wcd9xxx)
+		return wcd9xxx_core_res_suspend(&wcd9xxx->core_res, pmesg);
+	else
+		return 0;
+}
+
+static const struct slim_device_id wcd_slim_device_id[] = {
+	{"sitar-slim", 0},
+	{"sitar1p1-slim", 0},
+	{"tabla-slim", 0},
+	{"tabla2x-slim", 0},
+	{"taiko-slim-pgd", 0},
+	{"tapan-slim-pgd", 0},
+	{"tomtom-slim-pgd", WCD9330},
+	{"tasha-slim-pgd", WCD9335},
+	{"tavil-slim-pgd", WCD934X},
+	{}
+};
+
+static struct slim_driver wcd_slim_driver = {
+	.driver = {
+		.name = "wcd-slim",
+		.owner = THIS_MODULE,
+	},
+	.probe = wcd9xxx_slim_probe,
+	.remove = wcd9xxx_slim_remove,
+	.id_table = wcd_slim_device_id,
+	.resume = wcd9xxx_slim_resume,
+	.suspend = wcd9xxx_slim_suspend,
+	.device_up = wcd9xxx_slim_device_up,
+	.reset_device = wcd9xxx_slim_device_reset,
+	.device_down = wcd9xxx_slim_device_down,
+};
+
+static struct i2c_device_id wcd9xxx_id_table[] = {
+	{"wcd9xxx-i2c", WCD9XXX_I2C_TOP_LEVEL},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_ANALOG},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_1},
+	{"wcd9xxx-i2c", WCD9XXX_I2C_DIGITAL_2},
+	{}
+};
+
+static struct i2c_device_id tasha_id_table[] = {
+	{"tasha-i2c-pgd", WCD9XXX_I2C_TOP_LEVEL},
+	{}
+};
+
+static struct i2c_device_id tabla_id_table[] = {
+	{"tabla top level", WCD9XXX_I2C_TOP_LEVEL},
+	{"tabla analog", WCD9XXX_I2C_ANALOG},
+	{"tabla digital1", WCD9XXX_I2C_DIGITAL_1},
+	{"tabla digital2", WCD9XXX_I2C_DIGITAL_2},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, tabla_id_table);
+
+static const struct dev_pm_ops wcd9xxx_i2c_pm_ops = {
+	.suspend = wcd9xxx_i2c_suspend,
+	.resume	= wcd9xxx_i2c_resume,
+};
+
+static struct i2c_driver tabla_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "tabla-i2c-core",
+		.pm		=	&wcd9xxx_i2c_pm_ops,
+	},
+	.id_table               =       tabla_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       wcd9xxx_i2c_remove,
+};
+
+static struct i2c_driver wcd9xxx_i2c_driver = {
+	.driver                 = {
+		.owner          =       THIS_MODULE,
+		.name           =       "wcd9xxx-i2c-core",
+		.pm		=	&wcd9xxx_i2c_pm_ops,
+	},
+	.id_table               =       wcd9xxx_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       wcd9xxx_i2c_remove,
+};
+
+static struct i2c_driver wcd9335_i2c_driver = {
+	.driver	                = {
+		.owner	        =       THIS_MODULE,
+		.name           =       "tasha-i2c-core",
+		.pm		=	&wcd9xxx_i2c_pm_ops,
+	},
+	.id_table               =       tasha_id_table,
+	.probe                  =       wcd9xxx_i2c_probe,
+	.remove                 =       wcd9xxx_i2c_remove,
+};
+
+static int __init wcd9xxx_init(void)
+{
+	int ret[NUM_WCD9XXX_REG_RET] = {0};
+	int i = 0;
+
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING);
+
+	ret[0] = i2c_add_driver(&tabla_i2c_driver);
+	if (ret[0])
+		pr_err("%s: Failed to add the tabla2x I2C driver: %d\n",
+			__func__, ret[0]);
+
+	ret[1] = i2c_add_driver(&wcd9xxx_i2c_driver);
+	if (ret[1])
+		pr_err("%s: Failed to add the wcd9xxx I2C driver: %d\n",
+			__func__, ret[1]);
+
+	ret[2] = i2c_add_driver(&wcd9335_i2c_driver);
+	if (ret[2])
+		pr_err("%s: Failed to add the wcd9335 I2C driver: %d\n",
+			__func__, ret[2]);
+
+	ret[3] = slim_driver_register(&wcd_slim_driver);
+	if (ret[3])
+		pr_err("%s: Failed to register wcd SB driver: %d\n",
+			__func__, ret[3]);
+
+	for (i = 0; i < NUM_WCD9XXX_REG_RET; i++) {
+		if (ret[i])
+			return ret[i];
+	}
+
+	return 0;
+}
+module_init(wcd9xxx_init);
+
+static void __exit wcd9xxx_exit(void)
+{
+	wcd9xxx_set_intf_type(WCD9XXX_INTERFACE_TYPE_PROBING);
+
+	i2c_del_driver(&tabla_i2c_driver);
+	i2c_del_driver(&wcd9xxx_i2c_driver);
+	i2c_del_driver(&wcd9335_i2c_driver);
+	slim_driver_unregister(&wcd_slim_driver);
+}
+module_exit(wcd9xxx_exit);
+
+MODULE_DESCRIPTION("Codec core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/wcd9xxx-irq.c b/drivers/mfd/wcd9xxx-irq.c
new file mode 100644
index 0000000..1a50f37
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-irq.c
@@ -0,0 +1,785 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/mfd/core.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/delay.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <soc/qcom/pm.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+
+#define BYTE_BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr)			((nr) / BITS_PER_BYTE)
+
+#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 100
+
+#ifndef NO_IRQ
+#define NO_IRQ	(-1)
+#endif
+
+#ifdef CONFIG_OF
+struct wcd9xxx_irq_drv_data {
+	struct irq_domain *domain;
+	int irq;
+};
+#endif
+
+static int virq_to_phyirq(
+	struct wcd9xxx_core_resource *wcd9xxx_res, int virq);
+static int phyirq_to_virq(
+	struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
+static unsigned int wcd9xxx_irq_get_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res);
+static void wcd9xxx_irq_put_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res);
+static int wcd9xxx_map_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
+
+static void wcd9xxx_irq_lock(struct irq_data *data)
+{
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	mutex_lock(&wcd9xxx_res->irq_lock);
+}
+
+static void wcd9xxx_irq_sync_unlock(struct irq_data *data)
+{
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	int i;
+
+	if ((ARRAY_SIZE(wcd9xxx_res->irq_masks_cur) >
+	     WCD9XXX_MAX_IRQ_REGS) ||
+	     (ARRAY_SIZE(wcd9xxx_res->irq_masks_cache) >
+	      WCD9XXX_MAX_IRQ_REGS)) {
+		pr_err("%s: Array Size out of bound\n", __func__);
+		return;
+	}
+	if (!wcd9xxx_res->wcd_core_regmap) {
+		pr_err("%s: Codec core regmap not defined\n",
+			__func__);
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(wcd9xxx_res->irq_masks_cur); i++) {
+		/* If there's been a change in the mask write it back
+		 * to the hardware.
+		 */
+		if (wcd9xxx_res->irq_masks_cur[i] !=
+					wcd9xxx_res->irq_masks_cache[i]) {
+
+			wcd9xxx_res->irq_masks_cache[i] =
+					wcd9xxx_res->irq_masks_cur[i];
+			regmap_write(wcd9xxx_res->wcd_core_regmap,
+			wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i,
+			wcd9xxx_res->irq_masks_cur[i]);
+		}
+	}
+
+	mutex_unlock(&wcd9xxx_res->irq_lock);
+}
+
+static void wcd9xxx_irq_enable(struct irq_data *data)
+{
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
+	int byte = BIT_BYTE(wcd9xxx_irq);
+	int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur);
+
+	if ((byte < size) && (byte >= 0)) {
+		wcd9xxx_res->irq_masks_cur[byte] &=
+			~(BYTE_BIT_MASK(wcd9xxx_irq));
+	} else {
+		pr_err("%s: Array size is %d but index is %d: Out of range\n",
+			__func__, size, byte);
+	}
+}
+
+static void wcd9xxx_irq_disable(struct irq_data *data)
+{
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+	int wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
+	int byte = BIT_BYTE(wcd9xxx_irq);
+	int size = ARRAY_SIZE(wcd9xxx_res->irq_masks_cur);
+
+	if ((byte < size) && (byte >= 0)) {
+		wcd9xxx_res->irq_masks_cur[byte]
+			|= BYTE_BIT_MASK(wcd9xxx_irq);
+	} else {
+		pr_err("%s: Array size is %d but index is %d: Out of range\n",
+			__func__, size, byte);
+	}
+}
+
+static void wcd9xxx_irq_ack(struct irq_data *data)
+{
+	int wcd9xxx_irq = 0;
+	struct wcd9xxx_core_resource *wcd9xxx_res =
+			irq_data_get_irq_chip_data(data);
+
+	if (wcd9xxx_res == NULL) {
+		pr_err("%s: wcd9xxx_res is NULL\n", __func__);
+		return;
+	}
+	wcd9xxx_irq = virq_to_phyirq(wcd9xxx_res, data->irq);
+	pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n",
+				__func__, wcd9xxx_irq);
+}
+
+static void wcd9xxx_irq_mask(struct irq_data *d)
+{
+	/* do nothing but required as linux calls irq_mask without NULL check */
+}
+
+static struct irq_chip wcd9xxx_irq_chip = {
+	.name = "wcd9xxx",
+	.irq_bus_lock = wcd9xxx_irq_lock,
+	.irq_bus_sync_unlock = wcd9xxx_irq_sync_unlock,
+	.irq_disable = wcd9xxx_irq_disable,
+	.irq_enable = wcd9xxx_irq_enable,
+	.irq_mask = wcd9xxx_irq_mask,
+	.irq_ack = wcd9xxx_irq_ack,
+};
+
+bool wcd9xxx_lock_sleep(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	enum wcd9xxx_pm_state os;
+
+	/*
+	 * wcd9xxx_{lock/unlock}_sleep will be called by wcd9xxx_irq_thread
+	 * and its subroutines only motly.
+	 * but btn0_lpress_fn is not wcd9xxx_irq_thread's subroutine and
+	 * It can race with wcd9xxx_irq_thread.
+	 * So need to embrace wlock_holders with mutex.
+	 *
+	 * If system didn't resume, we can simply return false so codec driver's
+	 * IRQ handler can return without handling IRQ.
+	 * As interrupt line is still active, codec will have another IRQ to
+	 * retry shortly.
+	 */
+	mutex_lock(&wcd9xxx_res->pm_lock);
+	if (wcd9xxx_res->wlock_holders++ == 0) {
+		pr_debug("%s: holding wake lock\n", __func__);
+		pm_qos_update_request(&wcd9xxx_res->pm_qos_req,
+				      msm_cpuidle_get_deep_idle_latency());
+		pm_stay_awake(wcd9xxx_res->dev);
+	}
+	mutex_unlock(&wcd9xxx_res->pm_lock);
+
+	if (!wait_event_timeout(wcd9xxx_res->pm_wq,
+				((os =  wcd9xxx_pm_cmpxchg(wcd9xxx_res,
+						  WCD9XXX_PM_SLEEPABLE,
+						  WCD9XXX_PM_AWAKE)) ==
+							WCD9XXX_PM_SLEEPABLE ||
+					(os == WCD9XXX_PM_AWAKE)),
+				msecs_to_jiffies(
+					WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
+		pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
+			__func__,
+			WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, wcd9xxx_res->pm_state,
+			wcd9xxx_res->wlock_holders);
+		wcd9xxx_unlock_sleep(wcd9xxx_res);
+		return false;
+	}
+	wake_up_all(&wcd9xxx_res->pm_wq);
+	return true;
+}
+EXPORT_SYMBOL(wcd9xxx_lock_sleep);
+
+void wcd9xxx_unlock_sleep(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	mutex_lock(&wcd9xxx_res->pm_lock);
+	if (--wcd9xxx_res->wlock_holders == 0) {
+		pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
+			 __func__, wcd9xxx_res->pm_state, WCD9XXX_PM_SLEEPABLE);
+		/*
+		 * if wcd9xxx_lock_sleep failed, pm_state would be still
+		 * WCD9XXX_PM_ASLEEP, don't overwrite
+		 */
+		if (likely(wcd9xxx_res->pm_state == WCD9XXX_PM_AWAKE))
+			wcd9xxx_res->pm_state = WCD9XXX_PM_SLEEPABLE;
+		pm_qos_update_request(&wcd9xxx_res->pm_qos_req,
+				PM_QOS_DEFAULT_VALUE);
+		pm_relax(wcd9xxx_res->dev);
+	}
+	mutex_unlock(&wcd9xxx_res->pm_lock);
+	wake_up_all(&wcd9xxx_res->pm_wq);
+}
+EXPORT_SYMBOL(wcd9xxx_unlock_sleep);
+
+void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	mutex_lock(&wcd9xxx_res->nested_irq_lock);
+}
+
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	mutex_unlock(&wcd9xxx_res->nested_irq_lock);
+}
+
+
+static void wcd9xxx_irq_dispatch(struct wcd9xxx_core_resource *wcd9xxx_res,
+			struct intr_data *irqdata)
+{
+	int irqbit = irqdata->intr_num;
+
+	if (!wcd9xxx_res->wcd_core_regmap) {
+		pr_err("%s: codec core regmap not defined\n",
+			__func__);
+		return;
+	}
+
+	if (irqdata->clear_first) {
+		wcd9xxx_nested_irq_lock(wcd9xxx_res);
+		regmap_write(wcd9xxx_res->wcd_core_regmap,
+			wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] +
+					      BIT_BYTE(irqbit),
+			BYTE_BIT_MASK(irqbit));
+
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			regmap_write(wcd9xxx_res->wcd_core_regmap,
+				wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],
+				0x02);
+		handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit));
+		wcd9xxx_nested_irq_unlock(wcd9xxx_res);
+	} else {
+		wcd9xxx_nested_irq_lock(wcd9xxx_res);
+		handle_nested_irq(phyirq_to_virq(wcd9xxx_res, irqbit));
+		regmap_write(wcd9xxx_res->wcd_core_regmap,
+			wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE] +
+					      BIT_BYTE(irqbit),
+			BYTE_BIT_MASK(irqbit));
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			regmap_write(wcd9xxx_res->wcd_core_regmap,
+				wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],
+				0x02);
+
+		wcd9xxx_nested_irq_unlock(wcd9xxx_res);
+	}
+}
+
+static irqreturn_t wcd9xxx_irq_thread(int irq, void *data)
+{
+	int ret;
+	int i;
+	struct intr_data irqdata;
+	char linebuf[128];
+	static DEFINE_RATELIMIT_STATE(ratelimit, 5 * HZ, 1);
+	struct wcd9xxx_core_resource *wcd9xxx_res = data;
+	int num_irq_regs = wcd9xxx_res->num_irq_regs;
+	u8 status[num_irq_regs], status1[num_irq_regs];
+
+	if (unlikely(wcd9xxx_lock_sleep(wcd9xxx_res) == false)) {
+		dev_err(wcd9xxx_res->dev, "Failed to hold suspend\n");
+		return IRQ_NONE;
+	}
+
+	if (!wcd9xxx_res->wcd_core_regmap) {
+		dev_err(wcd9xxx_res->dev,
+			"%s: Codec core regmap not supplied\n",
+			   __func__);
+		goto err_disable_irq;
+	}
+
+	ret = regmap_bulk_read(wcd9xxx_res->wcd_core_regmap,
+		wcd9xxx_res->intr_reg[WCD9XXX_INTR_STATUS_BASE],
+		status, num_irq_regs);
+
+	if (ret < 0) {
+		dev_err(wcd9xxx_res->dev,
+				"Failed to read interrupt status: %d\n", ret);
+		goto err_disable_irq;
+	}
+
+	/* Apply masking */
+	for (i = 0; i < num_irq_regs; i++)
+		status[i] &= ~wcd9xxx_res->irq_masks_cur[i];
+
+	memcpy(status1, status, sizeof(status1));
+
+	/* Find out which interrupt was triggered and call that interrupt's
+	 * handler function
+	 *
+	 * Since codec has only one hardware irq line which is shared by
+	 * codec's different internal interrupts, so it's possible master irq
+	 * handler dispatches multiple nested irq handlers after breaking
+	 * order.  Dispatch interrupts in the order that is maintained by
+	 * the interrupt table.
+	 */
+	for (i = 0; i < wcd9xxx_res->intr_table_size; i++) {
+		irqdata = wcd9xxx_res->intr_table[i];
+		if (status[BIT_BYTE(irqdata.intr_num)] &
+			BYTE_BIT_MASK(irqdata.intr_num)) {
+			wcd9xxx_irq_dispatch(wcd9xxx_res, &irqdata);
+			status1[BIT_BYTE(irqdata.intr_num)] &=
+					~BYTE_BIT_MASK(irqdata.intr_num);
+		}
+	}
+
+	/*
+	 * As a failsafe if unhandled irq is found, clear it to prevent
+	 * interrupt storm.
+	 * Note that we can say there was an unhandled irq only when no irq
+	 * handled by nested irq handler since Taiko supports qdsp as irqs'
+	 * destination for few irqs.  Therefore driver shouldn't clear pending
+	 * irqs when few handled while few others not.
+	 */
+	if (unlikely(!memcmp(status, status1, sizeof(status)))) {
+		if (__ratelimit(&ratelimit)) {
+			pr_warn("%s: Unhandled irq found\n", __func__);
+			hex_dump_to_buffer(status, sizeof(status), 16, 1,
+					   linebuf, sizeof(linebuf), false);
+			pr_warn("%s: status0 : %s\n", __func__, linebuf);
+			hex_dump_to_buffer(status1, sizeof(status1), 16, 1,
+					   linebuf, sizeof(linebuf), false);
+			pr_warn("%s: status1 : %s\n", __func__, linebuf);
+		}
+
+		memset(status, 0xff, num_irq_regs);
+
+		ret = regmap_bulk_write(wcd9xxx_res->wcd_core_regmap,
+			wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLEAR_BASE],
+			status, num_irq_regs);
+		if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+			regmap_write(wcd9xxx_res->wcd_core_regmap,
+				wcd9xxx_res->intr_reg[WCD9XXX_INTR_CLR_COMMIT],
+				0x02);
+	}
+	wcd9xxx_unlock_sleep(wcd9xxx_res);
+
+	return IRQ_HANDLED;
+
+err_disable_irq:
+		dev_err(wcd9xxx_res->dev,
+				"Disable irq %d\n", wcd9xxx_res->irq);
+
+		disable_irq_wake(wcd9xxx_res->irq);
+		disable_irq_nosync(wcd9xxx_res->irq);
+		wcd9xxx_unlock_sleep(wcd9xxx_res);
+		return IRQ_NONE;
+}
+
+void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq, void *data)
+{
+	free_irq(phyirq_to_virq(wcd9xxx_res, irq), data);
+}
+
+void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
+{
+	if (wcd9xxx_res->irq)
+		enable_irq(phyirq_to_virq(wcd9xxx_res, irq));
+}
+
+void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
+{
+	if (wcd9xxx_res->irq)
+		disable_irq_nosync(phyirq_to_virq(wcd9xxx_res, irq));
+}
+
+void wcd9xxx_disable_irq_sync(
+			struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
+{
+	if (wcd9xxx_res->irq)
+		disable_irq(phyirq_to_virq(wcd9xxx_res, irq));
+}
+
+static int wcd9xxx_irq_setup_downstream_irq(
+			struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	int irq, virq, ret;
+
+	pr_debug("%s: enter\n", __func__);
+
+	for (irq = 0; irq < wcd9xxx_res->num_irqs; irq++) {
+		/* Map OF irq */
+		virq = wcd9xxx_map_irq(wcd9xxx_res, irq);
+		pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+		if (virq == NO_IRQ) {
+			pr_err("%s, No interrupt specifier for irq %d\n",
+			       __func__, irq);
+			return NO_IRQ;
+		}
+
+		ret = irq_set_chip_data(virq, wcd9xxx_res);
+		if (ret) {
+			pr_err("%s: Failed to configure irq %d (%d)\n",
+			       __func__, irq, ret);
+			return ret;
+		}
+
+		if (wcd9xxx_res->irq_level_high[irq])
+			irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+						 handle_level_irq);
+		else
+			irq_set_chip_and_handler(virq, &wcd9xxx_irq_chip,
+						 handle_edge_irq);
+
+		irq_set_nested_thread(virq, 1);
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
+int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	int i, ret;
+	u8 irq_level[wcd9xxx_res->num_irq_regs];
+	struct irq_domain *domain;
+	struct device_node *pnode;
+
+	mutex_init(&wcd9xxx_res->irq_lock);
+	mutex_init(&wcd9xxx_res->nested_irq_lock);
+
+	pnode = of_irq_find_parent(wcd9xxx_res->dev->of_node);
+	if (unlikely(!pnode))
+		return -EINVAL;
+
+	domain = irq_find_host(pnode);
+	if (unlikely(!domain))
+		return -EINVAL;
+
+	wcd9xxx_res->domain = domain;
+
+	wcd9xxx_res->irq = wcd9xxx_irq_get_upstream_irq(wcd9xxx_res);
+	if (!wcd9xxx_res->irq) {
+		pr_warn("%s: irq driver is not yet initialized\n", __func__);
+		mutex_destroy(&wcd9xxx_res->irq_lock);
+		mutex_destroy(&wcd9xxx_res->nested_irq_lock);
+		return -EPROBE_DEFER;
+	}
+	pr_debug("%s: probed irq %d\n", __func__, wcd9xxx_res->irq);
+
+	/* Setup downstream IRQs */
+	ret = wcd9xxx_irq_setup_downstream_irq(wcd9xxx_res);
+	if (ret) {
+		pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
+		mutex_destroy(&wcd9xxx_res->irq_lock);
+		mutex_destroy(&wcd9xxx_res->nested_irq_lock);
+		return ret;
+	}
+
+	/* All other wcd9xxx interrupts are edge triggered */
+	wcd9xxx_res->irq_level_high[0] = true;
+
+	/* mask all the interrupts */
+	memset(irq_level, 0, wcd9xxx_res->num_irq_regs);
+	for (i = 0; i < wcd9xxx_res->num_irqs; i++) {
+		wcd9xxx_res->irq_masks_cur[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		wcd9xxx_res->irq_masks_cache[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+		irq_level[BIT_BYTE(i)] |=
+		    wcd9xxx_res->irq_level_high[i] << (i % BITS_PER_BYTE);
+	}
+
+	if (!wcd9xxx_res->wcd_core_regmap) {
+		dev_err(wcd9xxx_res->dev,
+			"%s: Codec core regmap not defined\n",
+			   __func__);
+		ret = -EINVAL;
+		goto fail_irq_init;
+	}
+
+	for (i = 0; i < wcd9xxx_res->num_irq_regs; i++) {
+		/* Initialize interrupt mask and level registers */
+		regmap_write(wcd9xxx_res->wcd_core_regmap,
+			wcd9xxx_res->intr_reg[WCD9XXX_INTR_LEVEL_BASE] + i,
+					irq_level[i]);
+		regmap_write(wcd9xxx_res->wcd_core_regmap,
+			wcd9xxx_res->intr_reg[WCD9XXX_INTR_MASK_BASE] + i,
+			wcd9xxx_res->irq_masks_cur[i]);
+	}
+
+	ret = request_threaded_irq(wcd9xxx_res->irq, NULL, wcd9xxx_irq_thread,
+				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				   "wcd9xxx", wcd9xxx_res);
+	if (ret != 0)
+		dev_err(wcd9xxx_res->dev, "Failed to request IRQ %d: %d\n",
+			wcd9xxx_res->irq, ret);
+	else {
+		ret = enable_irq_wake(wcd9xxx_res->irq);
+		if (ret)
+			dev_err(wcd9xxx_res->dev,
+				"Failed to set wake interrupt on IRQ %d: %d\n",
+				wcd9xxx_res->irq, ret);
+		if (ret)
+			free_irq(wcd9xxx_res->irq, wcd9xxx_res);
+	}
+
+	if (ret)
+		goto fail_irq_init;
+
+	return ret;
+
+fail_irq_init:
+	dev_err(wcd9xxx_res->dev,
+			"%s: Failed to init wcd9xxx irq\n", __func__);
+	wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
+	mutex_destroy(&wcd9xxx_res->irq_lock);
+	mutex_destroy(&wcd9xxx_res->nested_irq_lock);
+	return ret;
+}
+
+int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq, irq_handler_t handler,
+			const char *name, void *data)
+{
+	int virq;
+
+	virq = phyirq_to_virq(wcd9xxx_res, irq);
+
+	return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+				    name, data);
+}
+
+void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	dev_dbg(wcd9xxx_res->dev, "%s: Cleaning up irq %d\n", __func__,
+		wcd9xxx_res->irq);
+
+	if (wcd9xxx_res->irq) {
+		disable_irq_wake(wcd9xxx_res->irq);
+		free_irq(wcd9xxx_res->irq, wcd9xxx_res);
+		wcd9xxx_res->irq = 0;
+		wcd9xxx_irq_put_upstream_irq(wcd9xxx_res);
+	}
+	mutex_destroy(&wcd9xxx_res->irq_lock);
+	mutex_destroy(&wcd9xxx_res->nested_irq_lock);
+}
+
+#ifndef CONFIG_OF
+static int phyirq_to_virq(
+	struct wcd9xxx_core_resource *wcd9xxx_res,
+	int offset)
+{
+	return wcd9xxx_res->irq_base + offset;
+}
+
+static int virq_to_phyirq(
+	struct wcd9xxx_core_resource *wcd9xxx_res,
+	int virq)
+{
+	return virq - wcd9xxx_res->irq_base;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	return wcd9xxx_res->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	/* Do nothing */
+}
+
+static int wcd9xxx_map_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res, int irq)
+{
+	return phyirq_to_virq(wcd9xxx_core_res, irq);
+}
+#else
+static struct wcd9xxx_irq_drv_data *
+wcd9xxx_irq_add_domain(struct device_node *node,
+			       struct device_node *parent)
+{
+	struct wcd9xxx_irq_drv_data *data = NULL;
+
+	pr_debug("%s: node %s, node parent %s\n", __func__,
+		 node->name, node->parent->name);
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return NULL;
+
+	/*
+	 * wcd9xxx_intc interrupt controller supports N to N irq mapping with
+	 * single cell binding with irq numbers(offsets) only.
+	 * Use irq_domain_simple_ops that has irq_domain_simple_map and
+	 * irq_domain_xlate_onetwocell.
+	 */
+	data->domain = irq_domain_add_linear(node, WCD9XXX_MAX_NUM_IRQS,
+					     &irq_domain_simple_ops, data);
+	if (!data->domain) {
+		kfree(data);
+		return NULL;
+	}
+
+	return data;
+}
+
+static struct wcd9xxx_irq_drv_data *
+wcd9xxx_get_irq_drv_d(const struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	struct irq_domain *domain;
+
+	domain = wcd9xxx_res->domain;
+
+	if (domain)
+		return domain->host_data;
+	else
+		return NULL;
+}
+
+static int phyirq_to_virq(struct wcd9xxx_core_resource *wcd9xxx_res, int offset)
+{
+	struct wcd9xxx_irq_drv_data *data;
+
+	data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
+	if (!data) {
+		pr_warn("%s: not registered to interrupt controller\n",
+			__func__);
+		return -EINVAL;
+	}
+	return irq_linear_revmap(data->domain, offset);
+}
+
+static int virq_to_phyirq(struct wcd9xxx_core_resource *wcd9xxx_res, int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	if (unlikely(!irq_data)) {
+		pr_err("%s: irq_data is NULL", __func__);
+		return -EINVAL;
+	}
+	return irq_data->hwirq;
+}
+
+static unsigned int wcd9xxx_irq_get_upstream_irq(
+				struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	struct wcd9xxx_irq_drv_data *data;
+
+	data = wcd9xxx_get_irq_drv_d(wcd9xxx_res);
+	if (!data) {
+		pr_err("%s: interrupt controller is not registered\n",
+			__func__);
+		return 0;
+	}
+
+	/* Make sure data is updated before return. */
+	rmb();
+	return data->irq;
+}
+
+static void wcd9xxx_irq_put_upstream_irq(
+			struct wcd9xxx_core_resource *wcd9xxx_res)
+{
+	wcd9xxx_res->domain = NULL;
+}
+
+static int wcd9xxx_map_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq)
+{
+	return of_irq_to_resource(wcd9xxx_res->dev->of_node, irq, NULL);
+}
+
+static int wcd9xxx_irq_probe(struct platform_device *pdev)
+{
+	int irq;
+	struct wcd9xxx_irq_drv_data *data;
+	struct device_node *node = pdev->dev.of_node;
+	int ret = -EINVAL;
+
+	irq = of_get_named_gpio(node, "qcom,gpio-connect", 0);
+	if (!gpio_is_valid(irq)) {
+		dev_err(&pdev->dev, "TLMM connect gpio not found\n");
+		return -EPROBE_DEFER;
+	}
+	irq = gpio_to_irq(irq);
+	if (irq < 0) {
+		dev_err(&pdev->dev, "Unable to configure irq\n");
+		return irq;
+	}
+	dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+	data = wcd9xxx_irq_add_domain(node, node->parent);
+	if (!data) {
+		pr_err("%s: irq_add_domain failed\n", __func__);
+		return -EINVAL;
+	}
+	data->irq = irq;
+
+	/* Make sure irq is saved before return. */
+	wmb();
+	ret = 0;
+
+	return ret;
+}
+
+static int wcd9xxx_irq_remove(struct platform_device *pdev)
+{
+	struct irq_domain *domain;
+	struct wcd9xxx_irq_drv_data *data;
+
+	domain = irq_find_host(pdev->dev.of_node);
+	if (unlikely(!domain)) {
+		pr_err("%s: domain is NULL", __func__);
+		return -EINVAL;
+	}
+	data = (struct wcd9xxx_irq_drv_data *)domain->host_data;
+	data->irq = 0;
+
+	/* Make sure irq variable is updated in data, before irq removal. */
+	wmb();
+	irq_domain_remove(data->domain);
+	kfree(data);
+	domain->host_data = NULL;
+
+	return 0;
+}
+
+static const struct of_device_id of_match[] = {
+	{ .compatible = "qcom,wcd9xxx-irq" },
+	{ }
+};
+
+static struct platform_driver wcd9xxx_irq_driver = {
+	.probe = wcd9xxx_irq_probe,
+	.remove = wcd9xxx_irq_remove,
+	.driver = {
+		.name = "wcd9xxx_intc",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(of_match),
+	},
+};
+
+static int wcd9xxx_irq_drv_init(void)
+{
+	return platform_driver_register(&wcd9xxx_irq_driver);
+}
+subsys_initcall(wcd9xxx_irq_drv_init);
+
+static void wcd9xxx_irq_drv_exit(void)
+{
+	platform_driver_unregister(&wcd9xxx_irq_driver);
+}
+module_exit(wcd9xxx_irq_drv_exit);
+#endif /* CONFIG_OF */
diff --git a/drivers/mfd/wcd9xxx-regmap.h b/drivers/mfd/wcd9xxx-regmap.h
new file mode 100644
index 0000000..6db8fc5
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-regmap.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD9XXX_REGMAP_
+#define _WCD9XXX_REGMAP_
+
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+
+typedef int (*regmap_patch_fptr)(struct regmap *, int);
+
+#ifdef CONFIG_WCD934X_CODEC
+extern struct regmap_config wcd934x_regmap_config;
+extern int wcd934x_regmap_register_patch(struct regmap *regmap,
+					 int version);
+#endif
+
+#ifdef CONFIG_WCD9335_CODEC
+extern struct regmap_config wcd9335_regmap_config;
+extern int wcd9335_regmap_register_patch(struct regmap *regmap,
+					 int version);
+#endif
+
+#ifdef CONFIG_WCD9330_CODEC
+extern struct regmap_config wcd9330_regmap_config;
+#endif
+
+static inline struct regmap_config *wcd9xxx_get_regmap_config(int type)
+{
+	struct regmap_config *regmap_config;
+
+	switch (type) {
+#ifdef CONFIG_WCD934X_CODEC
+	case WCD934X:
+		regmap_config = &wcd934x_regmap_config;
+		break;
+#endif
+#ifdef CONFIG_WCD9335_CODEC
+	case WCD9335:
+		regmap_config = &wcd9335_regmap_config;
+		break;
+#endif
+#ifdef CONFIG_WCD9330_CODEC
+	case WCD9330:
+		regmap_config = &wcd9330_regmap_config;
+		break;
+#endif
+	default:
+		regmap_config = NULL;
+		break;
+	};
+
+	return regmap_config;
+}
+
+static inline regmap_patch_fptr wcd9xxx_get_regmap_reg_patch(int type)
+{
+	regmap_patch_fptr apply_patch;
+
+	switch (type) {
+#ifdef CONFIG_WCD9335_CODEC
+	case WCD9335:
+		apply_patch = wcd9335_regmap_register_patch;
+		break;
+#endif
+#ifdef CONFIG_WCD934X_CODEC
+	case WCD934X:
+		apply_patch = wcd934x_regmap_register_patch;
+		break;
+#endif
+	default:
+		apply_patch = NULL;
+		break;
+	}
+
+	return apply_patch;
+}
+
+#endif
diff --git a/drivers/mfd/wcd9xxx-slimslave.c b/drivers/mfd/wcd9xxx-slimslave.c
new file mode 100644
index 0000000..d3a926c
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-slimslave.c
@@ -0,0 +1,564 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+struct wcd9xxx_slim_sch {
+	u16 rx_port_ch_reg_base;
+	u16 port_tx_cfg_reg_base;
+	u16 port_rx_cfg_reg_base;
+};
+
+static struct wcd9xxx_slim_sch sh_ch;
+
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+				    u8 wcd9xxx_pgd_la, u32 cnt,
+				    struct wcd9xxx_ch *channels, u32 path);
+
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+				      u32 cnt, struct wcd9xxx_ch *channels);
+
+static int wcd9xxx_configure_ports(struct wcd9xxx *wcd9xxx)
+{
+	if (wcd9xxx->codec_type->slim_slave_type ==
+	    WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0) {
+		sh_ch.rx_port_ch_reg_base = 0x180;
+		sh_ch.port_rx_cfg_reg_base = 0x040;
+		sh_ch.port_tx_cfg_reg_base = 0x040;
+	} else {
+		sh_ch.rx_port_ch_reg_base =
+			0x180 - (TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS * 4);
+		sh_ch.port_rx_cfg_reg_base =
+			0x040 - TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS;
+		sh_ch.port_tx_cfg_reg_base = 0x050;
+	}
+
+	return 0;
+}
+
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx, u8 wcd9xxx_pgd_la,
+			   unsigned int tx_num, unsigned int *tx_slot,
+			   unsigned int rx_num, unsigned int *rx_slot)
+{
+	int ret = 0;
+	int i;
+
+	ret = wcd9xxx_configure_ports(wcd9xxx);
+	if (ret) {
+		pr_err("%s: Failed to configure register address offset\n",
+		       __func__);
+		goto err;
+	}
+
+	if (wcd9xxx->rx_chs) {
+		wcd9xxx->num_rx_port = rx_num;
+		for (i = 0; i < rx_num; i++) {
+			wcd9xxx->rx_chs[i].ch_num = rx_slot[i];
+			INIT_LIST_HEAD(&wcd9xxx->rx_chs[i].list);
+		}
+		ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+						wcd9xxx->num_rx_port,
+						wcd9xxx->rx_chs,
+						SLIM_SINK);
+		if (ret) {
+			pr_err("%s: Failed to alloc %d rx slimbus channels\n",
+				__func__, wcd9xxx->num_rx_port);
+			kfree(wcd9xxx->rx_chs);
+			wcd9xxx->rx_chs = NULL;
+			wcd9xxx->num_rx_port = 0;
+		}
+	} else {
+		pr_err("Not able to allocate memory for %d slimbus rx ports\n",
+			wcd9xxx->num_rx_port);
+	}
+
+	if (wcd9xxx->tx_chs) {
+		wcd9xxx->num_tx_port = tx_num;
+		for (i = 0; i < tx_num; i++) {
+			wcd9xxx->tx_chs[i].ch_num = tx_slot[i];
+			INIT_LIST_HEAD(&wcd9xxx->tx_chs[i].list);
+		}
+		ret = wcd9xxx_alloc_slim_sh_ch(wcd9xxx, wcd9xxx_pgd_la,
+						wcd9xxx->num_tx_port,
+						wcd9xxx->tx_chs,
+						SLIM_SRC);
+		if (ret) {
+			pr_err("%s: Failed to alloc %d tx slimbus channels\n",
+				__func__, wcd9xxx->num_tx_port);
+			kfree(wcd9xxx->tx_chs);
+			wcd9xxx->tx_chs = NULL;
+			wcd9xxx->num_tx_port = 0;
+		}
+	} else {
+		pr_err("Not able to allocate memory for %d slimbus tx ports\n",
+			wcd9xxx->num_tx_port);
+	}
+	return 0;
+err:
+	return ret;
+}
+
+int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx)
+{
+	if (wcd9xxx->num_rx_port) {
+		wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+					wcd9xxx->num_rx_port,
+					wcd9xxx->rx_chs);
+		wcd9xxx->num_rx_port = 0;
+	}
+	if (wcd9xxx->num_tx_port) {
+		wcd9xxx_dealloc_slim_sh_ch(wcd9xxx->slim,
+					wcd9xxx->num_tx_port,
+					wcd9xxx->tx_chs);
+		wcd9xxx->num_tx_port = 0;
+	}
+	return 0;
+}
+
+
+static int wcd9xxx_alloc_slim_sh_ch(struct wcd9xxx *wcd9xxx,
+				    u8 wcd9xxx_pgd_la, u32 cnt,
+				    struct wcd9xxx_ch *channels, u32 path)
+{
+	int ret = 0;
+	u32 ch_idx;
+
+	/* The slimbus channel allocation seem take longer time
+	 * so do the allocation up front to avoid delay in start of
+	 * playback
+	 */
+	pr_debug("%s: pgd_la[%d]\n", __func__, wcd9xxx_pgd_la);
+	for (ch_idx = 0; ch_idx < cnt; ch_idx++) {
+		ret = slim_get_slaveport(wcd9xxx_pgd_la,
+					channels[ch_idx].port,
+					&channels[ch_idx].sph, path);
+		pr_debug("%s: pgd_la[%d] channels[%d].port[%d]\n"
+			"channels[%d].sph[%d] path[%d]\n",
+			__func__, wcd9xxx_pgd_la, ch_idx,
+			channels[ch_idx].port,
+			ch_idx, channels[ch_idx].sph, path);
+		if (ret < 0) {
+			pr_err("%s: slave port failure id[%d] ret[%d]\n",
+				__func__, channels[ch_idx].ch_num, ret);
+			goto err;
+		}
+
+		ret = slim_query_ch(wcd9xxx->slim,
+				    channels[ch_idx].ch_num,
+				    &channels[ch_idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_query_ch failed ch-num[%d] ret[%d]\n",
+				__func__, channels[ch_idx].ch_num, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static int wcd9xxx_dealloc_slim_sh_ch(struct slim_device *slim,
+			u32 cnt, struct wcd9xxx_ch *channels)
+{
+	int idx = 0;
+	int ret = 0;
+	/* slim_dealloc_ch */
+	for (idx = 0; idx < cnt; idx++) {
+		ret = slim_dealloc_ch(slim, channels[idx].ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_dealloc_ch fail ret[%d] ch_h[%d]\n",
+				__func__, ret, channels[idx].ch_h);
+		}
+	}
+	return ret;
+}
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph)
+{
+	u8 ch_cnt = 0;
+	u16 ch_h[SLIM_MAX_RX_PORTS] = {0};
+	u8  payload = 0;
+	u16 codec_port = 0;
+	int ret;
+	struct slim_ch prop;
+	struct wcd9xxx_ch *rx;
+	int size = ARRAY_SIZE(ch_h);
+
+	/* Configure slave interface device */
+
+	list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+		payload |= 1 << rx->shift;
+		if (ch_cnt < size) {
+			ch_h[ch_cnt] = rx->ch_h;
+			ch_cnt++;
+			pr_debug("list ch->ch_h %d ch->sph %d\n",
+				 rx->ch_h, rx->sph);
+		} else {
+			pr_err("%s: allocated channel number %u is out of max rangae %d\n",
+			       __func__, ch_cnt,
+			       size);
+			ret = EINVAL;
+			goto err;
+		}
+	}
+	pr_debug("%s: ch_cnt[%d] rate=%d WATER_MARK_VAL %d\n",
+		 __func__, ch_cnt, rate, WATER_MARK_VAL);
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	if ((rate == 44100) || (rate == 88200) || (rate == 176400) ||
+	    (rate == 352800)) {
+		prop.baser = SLIM_RATE_11025HZ;
+		prop.ratem = (rate/11025);
+	} else {
+		prop.baser = SLIM_RATE_4000HZ;
+		prop.ratem = (rate/4000);
+	}
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.sampleszbits = bit_width;
+
+	pr_debug("Before slim_define_ch:\n"
+		 "ch_cnt %d,ch_h[0] %d ch_h[1] %d, grph %d\n",
+		 ch_cnt, ch_h[0], ch_h[1], *grph);
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+			     true, grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+		       __func__, ret);
+		goto err;
+	}
+
+	list_for_each_entry(rx, wcd9xxx_ch_list, list) {
+		codec_port = rx->port;
+		pr_debug("%s: codec_port %d rx 0x%p, payload %d\n"
+			 "sh_ch.rx_port_ch_reg_base0 0x%x\n"
+			 "sh_ch.port_rx_cfg_reg_base 0x%x\n",
+			 __func__, codec_port, rx, payload,
+			 sh_ch.rx_port_ch_reg_base,
+			sh_ch.port_rx_cfg_reg_base);
+
+		/* look for the valid port range and chose the
+		 * payload accordingly
+		 */
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+				sh_ch.rx_port_ch_reg_base, codec_port),
+				payload);
+
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_RX_PORT_MULTI_CHANNEL_0(
+				sh_ch.rx_port_ch_reg_base, codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(
+				sh_ch.port_rx_cfg_reg_base, codec_port),
+				WATER_MARK_VAL);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+				__func__, codec_port, ret);
+		}
+
+		ret = slim_connect_sink(wcd9xxx->slim, &rx->sph, 1, rx->ch_h);
+		if (ret < 0) {
+			pr_err("%s: slim_connect_sink failed ret[%d]\n",
+				__func__, ret);
+			goto err_close_slim_sch;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+			      true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+			__func__, ret);
+		goto err_close_slim_sch;
+	}
+	return 0;
+
+err_close_slim_sch:
+	/*  release all acquired handles */
+	wcd9xxx_close_slim_sch_rx(wcd9xxx, wcd9xxx_ch_list, *grph);
+err:
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_rx);
+
+/* Enable slimbus slave device for RX path */
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph)
+{
+	u16 ch_cnt = 0;
+	u16 payload = 0;
+	u16 ch_h[SLIM_MAX_TX_PORTS] = {0};
+	u16 codec_port;
+	int ret = 0;
+	struct wcd9xxx_ch *tx;
+	int size = ARRAY_SIZE(ch_h);
+
+	struct slim_ch prop;
+
+	list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+		payload |= 1 << tx->shift;
+		if (ch_cnt < size) {
+			ch_h[ch_cnt] = tx->ch_h;
+			ch_cnt++;
+		} else {
+			pr_err("%s: allocated channel number %u is out of max rangae %d\n",
+			       __func__, ch_cnt,
+			       size);
+			ret = EINVAL;
+			goto err;
+		}
+	}
+
+	/* slim_define_ch api */
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (rate/4000);
+	prop.sampleszbits = bit_width;
+	ret = slim_define_ch(wcd9xxx->slim, &prop, ch_h, ch_cnt,
+			     true, grph);
+	if (ret < 0) {
+		pr_err("%s: slim_define_ch failed ret[%d]\n",
+		       __func__, ret);
+		goto err;
+	}
+
+	pr_debug("%s: ch_cnt[%d] rate[%d] bitwidth[%u]\n", __func__, ch_cnt,
+		 rate, bit_width);
+	list_for_each_entry(tx, wcd9xxx_ch_list, list) {
+		codec_port = tx->port;
+		pr_debug("%s: codec_port %d tx 0x%p, payload 0x%x\n",
+			 __func__, codec_port, tx, payload);
+		/* write to interface device */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+				payload & 0x00FF);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_0(codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* ports 8,9 */
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+				(payload & 0xFF00)>>8);
+		if (ret < 0) {
+			pr_err("%s:Intf-dev fail reg[%d] payload[%d] ret[%d]\n",
+				__func__,
+				SB_PGD_TX_PORT_MULTI_CHANNEL_1(codec_port),
+				payload, ret);
+			goto err;
+		}
+		/* configure the slave port for water mark and enable*/
+		ret = wcd9xxx_interface_reg_write(wcd9xxx,
+				SB_PGD_PORT_CFG_BYTE_ADDR(
+				sh_ch.port_tx_cfg_reg_base, codec_port),
+				WATER_MARK_VAL);
+		if (ret < 0) {
+			pr_err("%s:watermark set failure for port[%d] ret[%d]",
+				__func__, codec_port, ret);
+		}
+
+		ret = slim_connect_src(wcd9xxx->slim, tx->sph, tx->ch_h);
+
+		if (ret < 0) {
+			pr_err("%s: slim_connect_src failed ret[%d]\n",
+			       __func__, ret);
+			goto err;
+		}
+	}
+	/* slim_control_ch */
+	ret = slim_control_ch(wcd9xxx->slim, *grph, SLIM_CH_ACTIVATE,
+			      true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+			__func__, ret);
+		goto err;
+	}
+	return 0;
+err:
+	/* release all acquired handles */
+	wcd9xxx_close_slim_sch_tx(wcd9xxx, wcd9xxx_ch_list, *grph);
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_cfg_slim_sch_tx);
+
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph)
+{
+	u32 sph[SLIM_MAX_RX_PORTS] = {0};
+	int ch_cnt = 0;
+	int ret = 0;
+	struct wcd9xxx_ch *rx;
+
+	list_for_each_entry(rx, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = rx->sph;
+
+	pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n", __func__, ch_cnt,
+		sph[0], sph[1]);
+
+	/* slim_control_ch (REMOVE) */
+	pr_debug("%s before slim_control_ch grph %d\n", __func__, grph);
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n", __func__, ret);
+		goto err;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_close_slim_sch_rx);
+
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list,
+			      u16 grph)
+{
+	u32 sph[SLIM_MAX_TX_PORTS] = {0};
+	int ret = 0;
+	int ch_cnt = 0;
+	struct wcd9xxx_ch *tx;
+
+	pr_debug("%s\n", __func__);
+	list_for_each_entry(tx, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = tx->sph;
+
+	pr_debug("%s ch_cht %d, sph[0] %d sph[1] %d\n",
+		__func__, ch_cnt, sph[0], sph[1]);
+	/* slim_control_ch (REMOVE) */
+	ret = slim_control_ch(wcd9xxx->slim, grph, SLIM_CH_REMOVE, true);
+	if (ret < 0) {
+		pr_err("%s: slim_control_ch failed ret[%d]\n",
+			__func__, ret);
+		goto err;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_close_slim_sch_tx);
+
+int wcd9xxx_get_slave_port(unsigned int ch_num)
+{
+	int ret = 0;
+
+	ret = (ch_num - BASE_CH_NUM);
+	pr_debug("%s: ch_num[%d] slave port[%d]\n", __func__, ch_num, ret);
+	if (ret < 0) {
+		pr_err("%s: Error:- Invalid slave port found = %d\n",
+			__func__, ret);
+		return -EINVAL;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_get_slave_port);
+
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list, u16 grph)
+{
+	u32 sph[SLIM_MAX_TX_PORTS + SLIM_MAX_RX_PORTS] = {0};
+	int ch_cnt = 0;
+	int ret = 0;
+	struct wcd9xxx_ch *slim_ch;
+
+	list_for_each_entry(slim_ch, wcd9xxx_ch_list, list)
+		sph[ch_cnt++] = slim_ch->sph;
+
+	/* slim_disconnect_port */
+	ret = slim_disconnect_ports(wcd9xxx->slim, sph, ch_cnt);
+	if (ret < 0) {
+		pr_err("%s: slim_disconnect_ports failed ret[%d]\n",
+			__func__, ret);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_disconnect_port);
+
+/* This function is called with mutex acquired */
+int wcd9xxx_rx_vport_validation(u32 port_id,
+				struct list_head *codec_dai_list)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+
+	pr_debug("%s: port_id %u\n", __func__, port_id);
+
+	list_for_each_entry(ch,
+		codec_dai_list, list) {
+		pr_debug("%s: ch->port %u\n", __func__, ch->port);
+		if (ch->port == port_id) {
+			ret = -EINVAL;
+			break;
+		}
+	}
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_rx_vport_validation);
+
+
+/* This function is called with mutex acquired */
+int wcd9xxx_tx_vport_validation(u32 table, u32 port_id,
+				struct wcd9xxx_codec_dai_data *codec_dai,
+				u32 num_codec_dais)
+{
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+	u32 index;
+	unsigned long vtable = table;
+	u32 size = sizeof(table) * BITS_PER_BYTE;
+
+	pr_debug("%s: vtable 0x%lx port_id %u size %d\n", __func__,
+		 vtable, port_id, size);
+	for_each_set_bit(index, &vtable, size) {
+		if (index < num_codec_dais) {
+			list_for_each_entry(ch,
+					&codec_dai[index].wcd9xxx_ch_list,
+					list) {
+				pr_debug("%s: index %u ch->port %u vtable 0x%lx\n",
+						__func__, index, ch->port,
+						vtable);
+				if (ch->port == port_id) {
+					pr_err("%s: TX%u is used by AIF%u_CAP Mixer\n",
+							__func__, port_id + 1,
+							(index + 1)/2);
+					ret = -EINVAL;
+					break;
+				}
+			}
+		} else {
+			pr_err("%s: Invalid index %d of codec dai",
+					__func__, index);
+			ret = -EINVAL;
+		}
+		if (ret)
+			break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_tx_vport_validation);
diff --git a/drivers/mfd/wcd9xxx-utils.c b/drivers/mfd/wcd9xxx-utils.c
new file mode 100644
index 0000000..6062fb8
--- /dev/null
+++ b/drivers/mfd/wcd9xxx-utils.c
@@ -0,0 +1,1198 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/msm-cdc-supply.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-utils.h>
+
+#define REG_BYTES 2
+#define VAL_BYTES 1
+/*
+ * Page Register Address that APP Proc uses to
+ * access WCD9335 Codec registers is identified
+ * as 0x00
+ */
+#define PAGE_REG_ADDR 0x00
+
+static enum wcd9xxx_intf_status wcd9xxx_intf = -1;
+
+static struct mfd_cell tavil_devs[] = {
+	{
+		.name = "qcom-wcd-pinctrl",
+		.of_compatible = "qcom,wcd-pinctrl",
+	},
+	{
+		.name = "tavil_codec",
+	},
+};
+
+static struct mfd_cell tasha_devs[] = {
+	{
+		.name = "tasha_codec",
+	},
+};
+
+static struct mfd_cell tomtom_devs[] = {
+	{
+		.name = "tomtom_codec",
+	},
+};
+
+static int wcd9xxx_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",
+			__func__, name, dev->of_node->full_name);
+
+	return rc;
+}
+
+static void wcd9xxx_dt_parse_micbias_info(struct device *dev,
+					  struct wcd9xxx_micbias_setting *mb)
+{
+	u32 prop_val;
+	int rc;
+
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias-ldoh-v", NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						  "qcom,cdc-micbias-ldoh-v",
+						  &prop_val);
+		if (!rc)
+			mb->ldoh_v  =  (u8)prop_val;
+	}
+
+	/* MB1 */
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias-cfilt1-mv",
+			     NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						  "qcom,cdc-micbias-cfilt1-mv",
+						   &prop_val);
+		if (!rc)
+			mb->cfilt1_mv = prop_val;
+
+		rc = wcd9xxx_read_of_property_u32(dev,
+						"qcom,cdc-micbias1-cfilt-sel",
+						&prop_val);
+		if (!rc)
+			mb->bias1_cfilt_sel = (u8)prop_val;
+
+	} else if (of_find_property(dev->of_node, "qcom,cdc-micbias1-mv",
+				    NULL)) {
+		rc = wcd9xxx_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-micbias-cfilt2-mv",
+			     NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						  "qcom,cdc-micbias-cfilt2-mv",
+						   &prop_val);
+		if (!rc)
+			mb->cfilt2_mv = prop_val;
+
+		rc = wcd9xxx_read_of_property_u32(dev,
+						"qcom,cdc-micbias2-cfilt-sel",
+						&prop_val);
+		if (!rc)
+			mb->bias2_cfilt_sel = (u8)prop_val;
+
+	} else if (of_find_property(dev->of_node, "qcom,cdc-micbias2-mv",
+				    NULL)) {
+		rc = wcd9xxx_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-micbias-cfilt3-mv",
+			     NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						  "qcom,cdc-micbias-cfilt3-mv",
+						   &prop_val);
+		if (!rc)
+			mb->cfilt3_mv = prop_val;
+
+		rc = wcd9xxx_read_of_property_u32(dev,
+						"qcom,cdc-micbias3-cfilt-sel",
+						&prop_val);
+		if (!rc)
+			mb->bias3_cfilt_sel = (u8)prop_val;
+
+	} else if (of_find_property(dev->of_node, "qcom,cdc-micbias3-mv",
+				    NULL)) {
+		rc = wcd9xxx_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__);
+	}
+
+	/* MB4 */
+	if (of_find_property(dev->of_node, "qcom,cdc-micbias4-cfilt-sel",
+			     NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						"qcom,cdc-micbias4-cfilt-sel",
+						&prop_val);
+		if (!rc)
+			mb->bias4_cfilt_sel = (u8)prop_val;
+
+	} else if (of_find_property(dev->of_node, "qcom,cdc-micbias4-mv",
+				    NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						  "qcom,cdc-micbias4-mv",
+						  &prop_val);
+		if (!rc)
+			mb->micb4_mv = prop_val;
+	} else {
+		dev_info(dev, "%s: Micbias4 DT property not found\n",
+			__func__);
+	}
+
+	mb->bias1_cap_mode =
+	   (of_property_read_bool(dev->of_node, "qcom,cdc-micbias1-ext-cap") ?
+	 MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+	mb->bias2_cap_mode =
+	   (of_property_read_bool(dev->of_node, "qcom,cdc-micbias2-ext-cap") ?
+	    MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+	mb->bias3_cap_mode =
+	   (of_property_read_bool(dev->of_node, "qcom,cdc-micbias3-ext-cap") ?
+	    MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+	mb->bias4_cap_mode =
+	   (of_property_read_bool(dev->of_node, "qcom,cdc-micbias4-ext-cap") ?
+	    MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+
+	mb->bias2_is_headset_only =
+		of_property_read_bool(dev->of_node,
+				      "qcom,cdc-micbias2-headset-only");
+
+	/* Print micbias info */
+	dev_dbg(dev, "%s: ldoh_v  %u cfilt1_mv %u cfilt2_mv %u cfilt3_mv %u",
+		__func__, (u32)mb->ldoh_v, (u32)mb->cfilt1_mv,
+		(u32)mb->cfilt2_mv, (u32)mb->cfilt3_mv);
+
+	dev_dbg(dev, "%s: micb1_mv %u micb2_mv %u micb3_mv %u micb4_mv %u",
+		__func__, mb->micb1_mv, mb->micb2_mv,
+		mb->micb3_mv, mb->micb4_mv);
+
+	dev_dbg(dev, "%s: bias1_cfilt_sel %u bias2_cfilt_sel %u\n",
+		__func__, (u32)mb->bias1_cfilt_sel, (u32)mb->bias2_cfilt_sel);
+
+	dev_dbg(dev, "%s: bias3_cfilt_sel %u bias4_cfilt_sel %u\n",
+		__func__, (u32)mb->bias3_cfilt_sel, (u32)mb->bias4_cfilt_sel);
+
+	dev_dbg(dev, "%s: bias1_ext_cap %d bias2_ext_cap %d\n",
+		__func__, mb->bias1_cap_mode, mb->bias2_cap_mode);
+
+	dev_dbg(dev, "%s: bias3_ext_cap %d bias4_ext_cap %d\n",
+		__func__, mb->bias3_cap_mode, mb->bias4_cap_mode);
+
+	dev_dbg(dev, "%s: bias2_is_headset_only %d\n",
+		__func__, mb->bias2_is_headset_only);
+}
+
+/*
+ * wcd9xxx_validate_dmic_sample_rate:
+ *	Given the dmic_sample_rate and mclk rate, validate the
+ *	dmic_sample_rate. If dmic rate is found to be invalid,
+ *	assign the dmic rate as undefined, so individual codec
+ *	drivers can use their own defaults
+ * @dev: the device for which the dmic is to be configured
+ * @dmic_sample_rate: The input dmic_sample_rate
+ * @mclk_rate: The input codec mclk rate
+ * @dmic_rate_type: String to indicate the type of dmic sample
+ *		    rate, used for debug/error logging.
+ */
+static u32 wcd9xxx_validate_dmic_sample_rate(struct device *dev,
+		u32 dmic_sample_rate, u32 mclk_rate,
+		const char *dmic_rate_type)
+{
+	u32 div_factor;
+
+	if (dmic_sample_rate == WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED ||
+	    mclk_rate % dmic_sample_rate != 0)
+		goto undefined_rate;
+
+	div_factor = mclk_rate / dmic_sample_rate;
+
+	switch (div_factor) {
+	case 2:
+	case 3:
+	case 4:
+	case 8:
+	case 16:
+		/* Valid dmic DIV factors */
+		dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n",
+			__func__, div_factor, mclk_rate);
+		break;
+	case 6:
+		/*
+		 * DIV 6 is valid for both 9.6MHz and 12.288MHz
+		 * MCLK on Tavil. Older codecs support DIV6 only
+		 * for 12.288MHz MCLK.
+		 */
+		if ((mclk_rate == WCD9XXX_MCLK_CLK_9P6HZ) &&
+		    (of_device_is_compatible(dev->of_node,
+					     "qcom,tavil-slim-pgd")))
+			dev_dbg(dev, "%s: DMIC_DIV = %u, mclk_rate = %u\n",
+				__func__, div_factor, mclk_rate);
+		else if (mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ)
+			goto undefined_rate;
+		break;
+	default:
+		/* Any other DIV factor is invalid */
+		goto undefined_rate;
+	}
+
+	return dmic_sample_rate;
+
+undefined_rate:
+	dev_info(dev, "%s: Invalid %s = %d, for mclk %d\n",
+		 __func__, dmic_rate_type, dmic_sample_rate, mclk_rate);
+	dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
+
+	return dmic_sample_rate;
+}
+
+/*
+ * wcd9xxx_populate_dt_data:
+ *	Parse device tree properties for the given codec device
+ *
+ * @dev: pointer to codec device
+ *
+ * Returns pointer to the platform data resulting from parsing
+ * device tree.
+ */
+struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev)
+{
+	struct wcd9xxx_pdata *pdata;
+	u32 dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
+	u32 mad_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
+	u32 ecpp_dmic_sample_rate = WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED;
+	u32 dmic_clk_drive = WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED;
+	u32 prop_val;
+	int rc = 0;
+
+	if (!dev || !dev->of_node)
+		return NULL;
+
+	pdata = devm_kzalloc(dev, sizeof(struct wcd9xxx_pdata),
+			     GFP_KERNEL);
+	if (!pdata)
+		return NULL;
+
+	/* 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__);
+		goto err_power_sup;
+	}
+
+	/* Parse micbias info */
+	wcd9xxx_dt_parse_micbias_info(dev, &pdata->micbias);
+
+	pdata->wcd_rst_np = of_parse_phandle(dev->of_node,
+					     "qcom,wcd-rst-gpio-node", 0);
+	if (!pdata->wcd_rst_np) {
+		dev_err(dev, "%s: Looking up %s property in node %s failed\n",
+			__func__, "qcom,wcd-rst-gpio-node",
+			dev->of_node->full_name);
+		goto err_parse_dt_prop;
+	}
+
+	if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mclk-clk-rate",
+					   &prop_val)))
+		pdata->mclk_rate = prop_val;
+
+	if (pdata->mclk_rate != WCD9XXX_MCLK_CLK_9P6HZ &&
+	    pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ) {
+		dev_err(dev, "%s: Invalid mclk_rate = %u\n", __func__,
+			pdata->mclk_rate);
+		goto err_parse_dt_prop;
+	}
+
+	if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-dmic-sample-rate",
+					   &prop_val)))
+		dmic_sample_rate = prop_val;
+
+	pdata->dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev,
+							dmic_sample_rate,
+							pdata->mclk_rate,
+							"audio_dmic_rate");
+	if (!(wcd9xxx_read_of_property_u32(dev, "qcom,cdc-mad-dmic-rate",
+					   &prop_val)))
+		mad_dmic_sample_rate = prop_val;
+
+	pdata->mad_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev,
+							mad_dmic_sample_rate,
+							pdata->mclk_rate,
+							"mad_dmic_rate");
+
+	if (of_find_property(dev->of_node, "qcom,cdc-ecpp-dmic-rate", NULL)) {
+		rc = wcd9xxx_read_of_property_u32(dev,
+						  "qcom,cdc-ecpp-dmic-rate",
+						  &prop_val);
+		if (!rc)
+			ecpp_dmic_sample_rate = prop_val;
+	}
+
+	pdata->ecpp_dmic_sample_rate = wcd9xxx_validate_dmic_sample_rate(dev,
+							ecpp_dmic_sample_rate,
+							pdata->mclk_rate,
+							"ecpp_dmic_rate");
+
+	if (!(of_property_read_u32(dev->of_node,
+				   "qcom,cdc-dmic-clk-drv-strength",
+				   &prop_val))) {
+		dmic_clk_drive = prop_val;
+
+		if (dmic_clk_drive != 2 && dmic_clk_drive != 4 &&
+		    dmic_clk_drive != 8 && dmic_clk_drive != 16)
+			dev_err(dev, "Invalid cdc-dmic-clk-drv-strength %d\n",
+				dmic_clk_drive);
+	}
+
+	pdata->dmic_clk_drv = dmic_clk_drive;
+
+	return pdata;
+
+err_parse_dt_prop:
+	devm_kfree(dev, pdata->regulator);
+	pdata->regulator = NULL;
+	pdata->num_supplies = 0;
+err_power_sup:
+	devm_kfree(dev, pdata);
+	return NULL;
+}
+EXPORT_SYMBOL(wcd9xxx_populate_dt_data);
+
+static bool is_wcd9xxx_reg_power_down(struct wcd9xxx *wcd9xxx, u16 rreg)
+{
+	bool ret = false;
+	int i;
+	struct wcd9xxx_power_region *wcd9xxx_pwr;
+
+	if (!wcd9xxx)
+		return ret;
+
+	for (i = 0; i < WCD9XXX_MAX_PWR_REGIONS; i++) {
+		wcd9xxx_pwr = wcd9xxx->wcd9xxx_pwr[i];
+		if (!wcd9xxx_pwr)
+			continue;
+		if (((wcd9xxx_pwr->pwr_collapse_reg_min == 0) &&
+		     (wcd9xxx_pwr->pwr_collapse_reg_max == 0)) ||
+		    (wcd9xxx_pwr->power_state ==
+		     WCD_REGION_POWER_COLLAPSE_REMOVE))
+			ret = false;
+		else if (((wcd9xxx_pwr->power_state ==
+			   WCD_REGION_POWER_DOWN) ||
+			  (wcd9xxx_pwr->power_state ==
+			   WCD_REGION_POWER_COLLAPSE_BEGIN)) &&
+			 (rreg >= wcd9xxx_pwr->pwr_collapse_reg_min) &&
+			 (rreg <= wcd9xxx_pwr->pwr_collapse_reg_max))
+			ret = true;
+	}
+	return ret;
+}
+
+/*
+ * wcd9xxx_page_write:
+ *	Retrieve page number from register and
+ *	write that page number to the page address.
+ *	Called under io_lock acquisition.
+ *
+ * @wcd9xxx: pointer to wcd9xxx
+ * @reg: Register address from which page number is retrieved
+ *
+ * Returns 0 for success and negative error code for failure.
+ */
+int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg)
+{
+	int ret = 0;
+	unsigned short c_reg, reg_addr;
+	u8 pg_num, prev_pg_num;
+
+	if (wcd9xxx->type != WCD9335 && wcd9xxx->type != WCD934X)
+		return ret;
+
+	c_reg = *reg;
+	pg_num = c_reg >> 8;
+	reg_addr = c_reg & 0xff;
+	if (wcd9xxx->prev_pg_valid) {
+		prev_pg_num = wcd9xxx->prev_pg;
+		if (prev_pg_num != pg_num) {
+			ret = wcd9xxx->write_dev(
+					wcd9xxx, PAGE_REG_ADDR, 1,
+					(void *) &pg_num, false);
+			if (ret < 0)
+				pr_err("page write error, pg_num: 0x%x\n",
+					pg_num);
+			else {
+				wcd9xxx->prev_pg = pg_num;
+				dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n",
+					__func__, pg_num);
+			}
+		}
+	} else {
+		ret = wcd9xxx->write_dev(
+				wcd9xxx, PAGE_REG_ADDR, 1, (void *) &pg_num,
+				false);
+		if (ret < 0)
+			pr_err("page write error, pg_num: 0x%x\n", pg_num);
+		else {
+			wcd9xxx->prev_pg = pg_num;
+			wcd9xxx->prev_pg_valid = true;
+			dev_dbg(wcd9xxx->dev, "%s: Page 0x%x Write to 0x00\n",
+				__func__, pg_num);
+		}
+	}
+	*reg = reg_addr;
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_page_write);
+
+static int regmap_bus_read(void *context, const void *reg, size_t reg_size,
+			   void *val, size_t val_size)
+{
+	struct device *dev = context;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev);
+	unsigned short c_reg, rreg;
+	int ret, i;
+
+	if (!wcd9xxx) {
+		dev_err(dev, "%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (!reg || !val) {
+		dev_err(dev, "%s: reg or val is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (reg_size != REG_BYTES) {
+		dev_err(dev, "%s: register size %zd bytes, not supported\n",
+			__func__, reg_size);
+		return -EINVAL;
+	}
+
+	mutex_lock(&wcd9xxx->io_lock);
+	c_reg = *(u16 *)reg;
+	rreg = c_reg;
+
+	if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) {
+		ret = 0;
+		for (i = 0; i < val_size; i++)
+			((u8 *)val)[i] = 0;
+		goto err;
+	}
+	ret = wcd9xxx_page_write(wcd9xxx, &c_reg);
+	if (ret)
+		goto err;
+	ret = wcd9xxx->read_dev(wcd9xxx, c_reg, val_size, val, false);
+	if (ret < 0)
+		dev_err(dev, "%s: Codec read failed (%d), reg: 0x%x, size:%zd\n",
+			__func__, ret, rreg, val_size);
+	else {
+		for (i = 0; i < val_size; i++)
+			dev_dbg(dev, "%s: Read 0x%02x from 0x%x\n",
+				__func__, ((u8 *)val)[i], rreg + i);
+	}
+err:
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return ret;
+}
+
+static int regmap_bus_gather_write(void *context,
+				   const void *reg, size_t reg_size,
+				   const void *val, size_t val_size)
+{
+	struct device *dev = context;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev);
+	unsigned short c_reg, rreg;
+	int ret, i;
+
+	if (!wcd9xxx) {
+		dev_err(dev, "%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (!reg || !val) {
+		dev_err(dev, "%s: reg or val is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (reg_size != REG_BYTES) {
+		dev_err(dev, "%s: register size %zd bytes, not supported\n",
+			__func__, reg_size);
+		return -EINVAL;
+	}
+	mutex_lock(&wcd9xxx->io_lock);
+	c_reg = *(u16 *)reg;
+	rreg = c_reg;
+
+	if (is_wcd9xxx_reg_power_down(wcd9xxx, rreg)) {
+		ret = 0;
+		goto err;
+	}
+	ret = wcd9xxx_page_write(wcd9xxx, &c_reg);
+	if (ret)
+		goto err;
+
+	for (i = 0; i < val_size; i++)
+		dev_dbg(dev, "Write %02x to 0x%x\n", ((u8 *)val)[i],
+			rreg + i);
+
+	ret = wcd9xxx->write_dev(wcd9xxx, c_reg, val_size, (void *) val,
+				 false);
+	if (ret < 0)
+		dev_err(dev, "%s: Codec write failed (%d), reg:0x%x, size:%zd\n",
+			__func__, ret, rreg, val_size);
+
+err:
+	mutex_unlock(&wcd9xxx->io_lock);
+	return ret;
+}
+
+static int regmap_bus_write(void *context, const void *data, size_t count)
+{
+	struct device *dev = context;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(dev);
+
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	WARN_ON(count < REG_BYTES);
+
+	if (count > (REG_BYTES + VAL_BYTES)) {
+		if (wcd9xxx->multi_reg_write)
+			return wcd9xxx->multi_reg_write(wcd9xxx,
+							data, count);
+	} else
+		return regmap_bus_gather_write(context, data, REG_BYTES,
+					       data + REG_BYTES,
+					       count - REG_BYTES);
+
+	dev_err(dev, "%s: bus multi reg write failure\n", __func__);
+
+	return -EINVAL;
+}
+
+static struct regmap_bus regmap_bus_config = {
+	.write = regmap_bus_write,
+	.gather_write = regmap_bus_gather_write,
+	.read = regmap_bus_read,
+	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
+};
+
+/*
+ * wcd9xxx_regmap_init:
+ *	Initialize wcd9xxx register map
+ *
+ * @dev: pointer to wcd device
+ * @config: pointer to register map config
+ *
+ * Returns pointer to regmap structure for success
+ * or NULL in case of failure.
+ */
+struct regmap *wcd9xxx_regmap_init(struct device *dev,
+				   const struct regmap_config *config)
+{
+	return devm_regmap_init(dev, &regmap_bus_config, dev, config);
+}
+EXPORT_SYMBOL(wcd9xxx_regmap_init);
+
+/*
+ * wcd9xxx_reset:
+ *	Reset wcd9xxx codec
+ *
+ * @dev: pointer to wcd device
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_reset(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx;
+	int rc;
+	int value;
+
+	if (!dev)
+		return -ENODEV;
+
+	wcd9xxx = dev_get_drvdata(dev);
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	if (!wcd9xxx->wcd_rst_np) {
+		dev_err(dev, "%s: reset gpio device node not specified\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	value = msm_cdc_get_gpio_state(wcd9xxx->wcd_rst_np);
+	if (value > 0) {
+		wcd9xxx->avoid_cdc_rstlow = 1;
+		return 0;
+	}
+
+	rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np);
+	if (rc) {
+		dev_err(dev, "%s: wcd sleep state request fail!\n",
+			__func__);
+		return rc;
+	}
+
+	/* 20ms sleep required after pulling the reset gpio to LOW */
+	msleep(20);
+
+	rc = msm_cdc_pinctrl_select_active_state(wcd9xxx->wcd_rst_np);
+	if (rc) {
+		dev_err(dev, "%s: wcd active state request fail!\n",
+			__func__);
+		return rc;
+	}
+	msleep(20);
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_reset);
+
+/*
+ * wcd9xxx_reset_low:
+ *	Pull the wcd9xxx codec reset_n to low
+ *
+ * @dev: pointer to wcd device
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_reset_low(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx;
+	int rc;
+
+	if (!dev)
+		return -ENODEV;
+
+	wcd9xxx = dev_get_drvdata(dev);
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	if (!wcd9xxx->wcd_rst_np) {
+		dev_err(dev, "%s: reset gpio device node not specified\n",
+			__func__);
+		return -EINVAL;
+	}
+	if (wcd9xxx->avoid_cdc_rstlow) {
+		wcd9xxx->avoid_cdc_rstlow = 0;
+		dev_dbg(dev, "%s: avoid pull down of reset GPIO\n", __func__);
+		return 0;
+	}
+
+	rc = msm_cdc_pinctrl_select_sleep_state(wcd9xxx->wcd_rst_np);
+	if (rc)
+		dev_err(dev, "%s: wcd sleep state request fail!\n",
+			__func__);
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_reset_low);
+
+/*
+ * wcd9xxx_bringup:
+ *	Toggle reset analog and digital cores of wcd9xxx codec
+ *
+ * @dev: pointer to wcd device
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_bringup(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx;
+	int rc;
+	codec_bringup_fn cdc_bup_fn;
+
+	if (!dev)
+		return -ENODEV;
+
+	wcd9xxx = dev_get_drvdata(dev);
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	cdc_bup_fn = wcd9xxx_bringup_fn(wcd9xxx->type);
+	if (!cdc_bup_fn) {
+		dev_err(dev, "%s: Codec bringup fn NULL!\n",
+			__func__);
+		return -EINVAL;
+	}
+	rc = cdc_bup_fn(wcd9xxx);
+	if (rc)
+		dev_err(dev, "%s: Codec bringup error, rc: %d\n",
+			__func__, rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_bringup);
+
+/*
+ * wcd9xxx_bringup:
+ *	Set analog and digital cores of wcd9xxx codec in reset state
+ *
+ * @dev: pointer to wcd device
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_bringdown(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx;
+	int rc;
+	codec_bringdown_fn cdc_bdown_fn;
+
+	if (!dev)
+		return -ENODEV;
+
+	wcd9xxx = dev_get_drvdata(dev);
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	cdc_bdown_fn = wcd9xxx_bringdown_fn(wcd9xxx->type);
+	if (!cdc_bdown_fn) {
+		dev_err(dev, "%s: Codec bring down fn NULL!\n",
+			__func__);
+		return -EINVAL;
+	}
+	rc = cdc_bdown_fn(wcd9xxx);
+	if (rc)
+		dev_err(dev, "%s: Codec bring down error, rc: %d\n",
+			__func__, rc);
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_bringdown);
+
+/*
+ * wcd9xxx_get_codec_info:
+ *	Fill codec specific information like interrupts, version
+ *
+ * @dev: pointer to wcd device
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_get_codec_info(struct device *dev)
+{
+	struct wcd9xxx *wcd9xxx;
+	int rc;
+	codec_type_fn cdc_type_fn;
+	struct wcd9xxx_codec_type *cinfo;
+
+	if (!dev)
+		return -ENODEV;
+
+	wcd9xxx = dev_get_drvdata(dev);
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	cdc_type_fn = wcd9xxx_get_codec_info_fn(wcd9xxx->type);
+	if (!cdc_type_fn) {
+		dev_err(dev, "%s: Codec fill type fn NULL!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	cinfo = wcd9xxx->codec_type;
+	if (!cinfo)
+		return -EINVAL;
+
+	rc = cdc_type_fn(wcd9xxx, cinfo);
+	if (rc) {
+		dev_err(dev, "%s: Codec type fill failed, rc:%d\n",
+			__func__, rc);
+		return rc;
+
+	}
+
+	switch (wcd9xxx->type) {
+	case WCD934X:
+		cinfo->dev = tavil_devs;
+		cinfo->size = ARRAY_SIZE(tavil_devs);
+		break;
+	case WCD9335:
+		cinfo->dev = tasha_devs;
+		cinfo->size = ARRAY_SIZE(tasha_devs);
+		break;
+	case WCD9330:
+		cinfo->dev = tomtom_devs;
+		cinfo->size = ARRAY_SIZE(tomtom_devs);
+		break;
+	default:
+		cinfo->dev = NULL;
+		cinfo->size = 0;
+		break;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_get_codec_info);
+
+/*
+ * wcd9xxx_core_irq_init:
+ *	Initialize wcd9xxx codec irq instance
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_core_irq_init(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res)
+{
+	int ret = 0;
+
+	if (!wcd9xxx_core_res)
+		return -EINVAL;
+
+	if (wcd9xxx_core_res->irq != 1) {
+		ret = wcd9xxx_irq_init(wcd9xxx_core_res);
+		if (ret)
+			pr_err("IRQ initialization failed\n");
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_core_irq_init);
+
+/*
+ * wcd9xxx_assign_irq:
+ *	Assign irq and irq_base to wcd9xxx core resource
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ * @irq: irq number
+ * @irq_base: base irq number
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_assign_irq(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	unsigned int irq,
+	unsigned int irq_base)
+{
+	if (!wcd9xxx_core_res)
+		return -EINVAL;
+
+	wcd9xxx_core_res->irq = irq;
+	wcd9xxx_core_res->irq_base = irq_base;
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9xxx_assign_irq);
+
+/*
+ * wcd9xxx_core_res_init:
+ *	Initialize wcd core resource instance
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ * @num_irqs: number of irqs for wcd9xxx core
+ * @num_irq_regs: number of irq registers
+ * @wcd_regmap: pointer to the wcd register map
+ *
+ * Returns 0 for success or negative error code in case of failure
+ */
+int wcd9xxx_core_res_init(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	int num_irqs, int num_irq_regs, struct regmap *wcd_regmap)
+{
+	if (!wcd9xxx_core_res || !wcd_regmap)
+		return -EINVAL;
+
+	mutex_init(&wcd9xxx_core_res->pm_lock);
+	wcd9xxx_core_res->wlock_holders = 0;
+	wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
+	init_waitqueue_head(&wcd9xxx_core_res->pm_wq);
+	pm_qos_add_request(&wcd9xxx_core_res->pm_qos_req,
+				PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+
+	wcd9xxx_core_res->num_irqs = num_irqs;
+	wcd9xxx_core_res->num_irq_regs = num_irq_regs;
+	wcd9xxx_core_res->wcd_core_regmap = wcd_regmap;
+
+	pr_info("%s: num_irqs = %d, num_irq_regs = %d\n",
+			__func__, wcd9xxx_core_res->num_irqs,
+			wcd9xxx_core_res->num_irq_regs);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_init);
+
+/*
+ * wcd9xxx_core_res_deinit:
+ *	Deinit wcd core resource instance
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ */
+void wcd9xxx_core_res_deinit(struct wcd9xxx_core_resource *wcd9xxx_core_res)
+{
+	if (!wcd9xxx_core_res)
+		return;
+
+	pm_qos_remove_request(&wcd9xxx_core_res->pm_qos_req);
+	mutex_destroy(&wcd9xxx_core_res->pm_lock);
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_deinit);
+
+/*
+ * wcd9xxx_pm_cmpxchg:
+ *	Check old state and exchange with pm new state
+ *	if old state matches with current state
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ * @o: pm old state
+ * @n: pm new state
+ *
+ * Returns old state
+ */
+enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
+		struct wcd9xxx_core_resource *wcd9xxx_core_res,
+		enum wcd9xxx_pm_state o,
+		enum wcd9xxx_pm_state n)
+{
+	enum wcd9xxx_pm_state old;
+
+	if (!wcd9xxx_core_res)
+		return o;
+
+	mutex_lock(&wcd9xxx_core_res->pm_lock);
+	old = wcd9xxx_core_res->pm_state;
+	if (old == o)
+		wcd9xxx_core_res->pm_state = n;
+	mutex_unlock(&wcd9xxx_core_res->pm_lock);
+
+	return old;
+}
+EXPORT_SYMBOL(wcd9xxx_pm_cmpxchg);
+
+/*
+ * wcd9xxx_core_res_suspend:
+ *	Suspend callback function for wcd9xxx core
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ * @pm_message_t: pm message
+ *
+ * Returns 0 for success or negative error code for failure/busy
+ */
+int wcd9xxx_core_res_suspend(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	pm_message_t pmesg)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	/*
+	 * pm_qos_update_request() can be called after this suspend chain call
+	 * started. thus suspend can be called while lock is being held
+	 */
+	mutex_lock(&wcd9xxx_core_res->pm_lock);
+	if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_SLEEPABLE) {
+		pr_debug("%s: suspending system, state %d, wlock %d\n",
+			 __func__, wcd9xxx_core_res->pm_state,
+			 wcd9xxx_core_res->wlock_holders);
+		wcd9xxx_core_res->pm_state = WCD9XXX_PM_ASLEEP;
+	} else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_AWAKE) {
+		/*
+		 * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+		 * then set to WCD9XXX_PM_ASLEEP
+		 */
+		pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+			 __func__, wcd9xxx_core_res->pm_state,
+			 wcd9xxx_core_res->wlock_holders);
+		mutex_unlock(&wcd9xxx_core_res->pm_lock);
+		if (!(wait_event_timeout(wcd9xxx_core_res->pm_wq,
+					 wcd9xxx_pm_cmpxchg(wcd9xxx_core_res,
+						  WCD9XXX_PM_SLEEPABLE,
+						  WCD9XXX_PM_ASLEEP) ==
+							WCD9XXX_PM_SLEEPABLE,
+					 HZ))) {
+			pr_debug("%s: suspend failed state %d, wlock %d\n",
+				 __func__, wcd9xxx_core_res->pm_state,
+				 wcd9xxx_core_res->wlock_holders);
+			ret = -EBUSY;
+		} else {
+			pr_debug("%s: done, state %d, wlock %d\n", __func__,
+				 wcd9xxx_core_res->pm_state,
+				 wcd9xxx_core_res->wlock_holders);
+		}
+		mutex_lock(&wcd9xxx_core_res->pm_lock);
+	} else if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_warn("%s: system is already suspended, state %d, wlock %dn",
+			__func__, wcd9xxx_core_res->pm_state,
+			wcd9xxx_core_res->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx_core_res->pm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_suspend);
+
+/*
+ * wcd9xxx_core_res_resume:
+ *	Resume callback function for wcd9xxx core
+ *
+ * @wcd9xxx_core_res: pointer to wcd core resource
+ *
+ * Returns 0 for success or negative error code for failure/busy
+ */
+int wcd9xxx_core_res_resume(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	mutex_lock(&wcd9xxx_core_res->pm_lock);
+	if (wcd9xxx_core_res->pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+				wcd9xxx_core_res->pm_state,
+				wcd9xxx_core_res->wlock_holders);
+		wcd9xxx_core_res->pm_state = WCD9XXX_PM_SLEEPABLE;
+	} else {
+		pr_warn("%s: system is already awake, state %d wlock %d\n",
+				__func__, wcd9xxx_core_res->pm_state,
+				wcd9xxx_core_res->wlock_holders);
+	}
+	mutex_unlock(&wcd9xxx_core_res->pm_lock);
+	wake_up_all(&wcd9xxx_core_res->pm_wq);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_core_res_resume);
+
+/*
+ * wcd9xxx_get_intf_type:
+ *	Get interface type of wcd9xxx core
+ *
+ * Returns interface type
+ */
+enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void)
+{
+	return wcd9xxx_intf;
+}
+EXPORT_SYMBOL(wcd9xxx_get_intf_type);
+
+/*
+ * wcd9xxx_set_intf_type:
+ *	Set interface type of wcd9xxx core
+ *
+ */
+void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status intf_status)
+{
+	wcd9xxx_intf = intf_status;
+}
+EXPORT_SYMBOL(wcd9xxx_set_intf_type);
+
+/*
+ * wcd9xxx_set_power_state: set power state for the region
+ * @wcd9xxx: handle to wcd core
+ * @state: power state to be set
+ * @region: region index
+ *
+ * Returns error code in case of failure or 0 for success
+ */
+int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx,
+			    enum codec_power_states state,
+			    enum wcd_power_regions region)
+{
+	if (!wcd9xxx) {
+		pr_err("%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) {
+		dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n",
+			__func__, region);
+		return -EINVAL;
+	}
+	if (!wcd9xxx->wcd9xxx_pwr[region]) {
+		dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n",
+			__func__, region);
+		return -EINVAL;
+	}
+	mutex_lock(&wcd9xxx->io_lock);
+	wcd9xxx->wcd9xxx_pwr[region]->power_state = state;
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9xxx_set_power_state);
+
+/*
+ * wcd9xxx_get_current_power_state: Get power state of the region
+ * @wcd9xxx: handle to wcd core
+ * @region: region index
+ *
+ * Returns current power state of the region or error code for failure
+ */
+int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx,
+				    enum wcd_power_regions region)
+{
+	int state;
+
+	if (!wcd9xxx) {
+		pr_err("%s: wcd9xxx is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((region < 0) || (region >= WCD9XXX_MAX_PWR_REGIONS)) {
+		dev_err(wcd9xxx->dev, "%s: region index %d out of bounds\n",
+			__func__, region);
+		return -EINVAL;
+	}
+	if (!wcd9xxx->wcd9xxx_pwr[region]) {
+		dev_err(wcd9xxx->dev, "%s: memory not created for region: %d\n",
+			__func__, region);
+		return -EINVAL;
+	}
+
+	mutex_lock(&wcd9xxx->io_lock);
+	state = wcd9xxx->wcd9xxx_pwr[region]->power_state;
+	mutex_unlock(&wcd9xxx->io_lock);
+
+	return state;
+}
+EXPORT_SYMBOL(wcd9xxx_get_current_power_state);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 407cf29..f513ed4 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -786,6 +786,7 @@
 source "drivers/misc/altera-stapl/Kconfig"
 source "drivers/misc/mei/Kconfig"
 source "drivers/misc/vmw_vmci/Kconfig"
+source "drivers/misc/qcom/Kconfig"
 source "drivers/misc/mic/Kconfig"
 source "drivers/misc/genwqe/Kconfig"
 source "drivers/misc/echo/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 6c2fde7..7ea17ec 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,6 +54,7 @@
 obj-$(CONFIG_CXL_BASE)		+= cxl/
 obj-$(CONFIG_PANEL)             += panel.o
 obj-$(CONFIG_UID_CPUTIME) += uid_cputime.o
+obj-y				+= qcom/
 obj-$(CONFIG_MEMORY_STATE_TIME)	+= memory_state_time.o
 
 lkdtm-$(CONFIG_LKDTM)		+= lkdtm_core.o
diff --git a/drivers/misc/qcom/Kconfig b/drivers/misc/qcom/Kconfig
new file mode 100644
index 0000000..9c73960
--- /dev/null
+++ b/drivers/misc/qcom/Kconfig
@@ -0,0 +1,20 @@
+config MSM_QDSP6V2_CODECS
+	bool "Audio QDSP6V2 APR support"
+	depends on MSM_SMD
+	select SND_SOC_QDSP6V2
+	help
+	  Enable Audio codecs with APR IPC protocol support between
+	  application processor and QDSP6 for B-family. APR is
+	  used by audio driver to configure QDSP6's
+	  ASM, ADM and AFE.
+
+config MSM_ULTRASOUND
+	bool "QDSP6V2 HW Ultrasound support"
+	select SND_SOC_QDSP6V2
+	help
+	  Enable HW Ultrasound support in QDSP6V2.
+	  QDSP6V2 can support HW encoder & decoder and
+	  ultrasound processing. It will enable
+	  ultrasound data paths between
+	  HW and services, calculating input events
+	  upon the ultrasound data.
diff --git a/drivers/misc/qcom/Makefile b/drivers/misc/qcom/Makefile
new file mode 100644
index 0000000..120bddd
--- /dev/null
+++ b/drivers/misc/qcom/Makefile
@@ -0,0 +1 @@
+obj-y		+= qdsp6v2/
diff --git a/drivers/misc/qcom/qdsp6v2/Makefile b/drivers/misc/qcom/qdsp6v2/Makefile
new file mode 100644
index 0000000..90a123a
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/Makefile
@@ -0,0 +1,6 @@
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += aac_in.o qcelp_in.o evrc_in.o amrnb_in.o g711mlaw_in.o g711alaw_in.o audio_utils.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_wma.o audio_wmapro.o audio_aac.o audio_multi_aac.o audio_alac.o audio_ape.o audio_utils_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += q6audio_v2.o q6audio_v2_aio.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS) += audio_g711mlaw.o audio_g711alaw.o
+obj-$(CONFIG_MSM_QDSP6V2_CODECS)  += audio_mp3.o audio_amrnb.o audio_amrwb.o audio_amrwbplus.o audio_evrc.o audio_qcelp.o amrwb_in.o audio_hwacc_effects.o
+obj-$(CONFIG_MSM_ULTRASOUND) += ultrasound/
diff --git a/drivers/misc/qcom/qdsp6v2/aac_in.c b/drivers/misc/qcom/qdsp6v2/aac_in.c
new file mode 100644
index 0000000..b74aa59
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/aac_in.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_aac.h>
+#include <linux/compat.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 5 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((1536+sizeof(struct meta_out_dsp)) * 5))
+
+#define AAC_FORMAT_ADTS 65535
+
+static long aac_in_ioctl_shared(struct file *file, unsigned int cmd, void *arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_aac_enc_config *enc_cfg;
+		struct msm_audio_aac_config *aac_config;
+		uint32_t aac_mode = AAC_ENC_MODE_AAC_LC;
+
+		enc_cfg = audio->enc_cfg;
+		aac_config = audio->codec_cfg;
+		/* ENCODE CFG (after new set of API's are published )bharath*/
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			pr_info("%s:AUDIO_START already over\n", __func__);
+			rc = 0;
+			break;
+		}
+
+		if (audio->opened) {
+			rc = audio_in_buf_alloc(audio);
+			if (rc < 0) {
+				pr_err("%s:session id %d: buffer allocation failed\n",
+					 __func__, audio->ac->session);
+				break;
+			}
+		} else {
+			if (audio->feedback == NON_TUNNEL_MODE) {
+				pr_debug("%s: starting in non_tunnel mode",
+					__func__);
+				rc = q6asm_open_read_write(audio->ac,
+					FORMAT_MPEG4_AAC, FORMAT_LINEAR_PCM);
+				if (rc < 0) {
+					pr_err("%s:open read write failed\n",
+						__func__);
+					break;
+				}
+			}
+			if (audio->feedback == TUNNEL_MODE) {
+				pr_debug("%s: starting in tunnel mode",
+					__func__);
+				rc = q6asm_open_read(audio->ac,
+							FORMAT_MPEG4_AAC);
+
+				if (rc < 0) {
+					pr_err("%s:open read failed\n",
+							__func__);
+					break;
+				}
+			}
+			audio->stopped = 0;
+		}
+
+		pr_debug("%s:sbr_ps_flag = %d, sbr_flag = %d\n", __func__,
+			aac_config->sbr_ps_on_flag, aac_config->sbr_on_flag);
+		if (aac_config->sbr_ps_on_flag)
+			aac_mode = AAC_ENC_MODE_EAAC_P;
+		else if (aac_config->sbr_on_flag)
+			aac_mode = AAC_ENC_MODE_AAC_P;
+		else
+			aac_mode = AAC_ENC_MODE_AAC_LC;
+
+		rc = q6asm_enc_cfg_blk_aac(audio->ac,
+					audio->buf_cfg.frames_per_buf,
+					enc_cfg->sample_rate,
+					enc_cfg->channels,
+					enc_cfg->bit_rate,
+					aac_mode,
+					enc_cfg->stream_format);
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd media format block failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+						audio->pcm_cfg.sample_rate,
+						audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed\n",
+				__func__, audio->ac->session);
+				break;
+			}
+		}
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac);
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:session id %d: Rxed AUDIO_STOP\n", __func__,
+				audio->ac->session);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_GET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config *cfg;
+		struct msm_audio_aac_enc_config *enc_cfg;
+
+		cfg = (struct msm_audio_aac_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer for %s\n",
+			__func__, "AUDIO_GET_AAC_CONFIG");
+			rc = -EINVAL;
+			break;
+		}
+		memset(cfg, 0, sizeof(*cfg));
+		enc_cfg = audio->enc_cfg;
+		if (enc_cfg->channels == CH_MODE_MONO)
+			cfg->channels = 1;
+		else
+			cfg->channels = 2;
+
+		cfg->sample_rate = enc_cfg->sample_rate;
+		cfg->bit_rate = enc_cfg->bit_rate;
+		switch (enc_cfg->stream_format) {
+		case 0x00:
+			cfg->stream_format = AUDIO_AAC_FORMAT_ADTS;
+			break;
+		case 0x01:
+			cfg->stream_format = AUDIO_AAC_FORMAT_LOAS;
+			break;
+		case 0x02:
+			cfg->stream_format = AUDIO_AAC_FORMAT_ADIF;
+			break;
+		default:
+		case 0x03:
+			cfg->stream_format = AUDIO_AAC_FORMAT_RAW;
+		}
+		pr_debug("%s:session id %d: Get-aac-cfg: format=%d sr=%d bitrate=%d\n",
+			__func__, audio->ac->session,
+			cfg->stream_format, cfg->sample_rate, cfg->bit_rate);
+		break;
+	}
+	case AUDIO_SET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config *cfg;
+		struct msm_audio_aac_enc_config *enc_cfg;
+		uint32_t min_bitrate, max_bitrate;
+
+		cfg = (struct msm_audio_aac_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer for %s\n",
+			"AUDIO_SET_AAC_ENC_CONFIG", __func__);
+			rc = -EINVAL;
+			break;
+		}
+		enc_cfg = audio->enc_cfg;
+		pr_debug("%s:session id %d: Set-aac-cfg: stream=%d\n", __func__,
+			audio->ac->session, cfg->stream_format);
+
+		switch (cfg->stream_format) {
+		case AUDIO_AAC_FORMAT_ADTS:
+			enc_cfg->stream_format = 0x00;
+			break;
+		case AUDIO_AAC_FORMAT_LOAS:
+			enc_cfg->stream_format = 0x01;
+			break;
+		case AUDIO_AAC_FORMAT_ADIF:
+			enc_cfg->stream_format = 0x02;
+			break;
+		case AUDIO_AAC_FORMAT_RAW:
+			enc_cfg->stream_format = 0x03;
+			break;
+		default:
+			pr_err("%s:session id %d: unsupported AAC format %d\n",
+				__func__, audio->ac->session,
+				cfg->stream_format);
+			rc = -EINVAL;
+			break;
+		}
+
+		if (cfg->channels == 1) {
+			cfg->channels = CH_MODE_MONO;
+		} else if (cfg->channels == 2) {
+			cfg->channels = CH_MODE_STEREO;
+		} else {
+			rc = -EINVAL;
+			break;
+		}
+
+		min_bitrate = ((cfg->sample_rate)*(cfg->channels))/2;
+		/* This calculation should be based on AAC mode. But we cannot
+		 * get AAC mode in this setconfig. min_bitrate's logical max
+		 * value is 24000. So if min_bitrate is higher than 24000,
+		 * choose 24000.
+		 */
+		if (min_bitrate > 24000)
+			min_bitrate = 24000;
+		max_bitrate = 6*(cfg->sample_rate)*(cfg->channels);
+		if (max_bitrate > 192000)
+			max_bitrate = 192000;
+		if ((cfg->bit_rate < min_bitrate) ||
+			(cfg->bit_rate > max_bitrate)) {
+			pr_err("%s: bitrate permissible: max=%d, min=%d\n",
+				__func__, max_bitrate, min_bitrate);
+			pr_err("%s: ERROR in setting bitrate = %d\n",
+				__func__, cfg->bit_rate);
+			rc = -EINVAL;
+			break;
+		}
+		enc_cfg->sample_rate = cfg->sample_rate;
+		enc_cfg->channels = cfg->channels;
+		enc_cfg->bit_rate = cfg->bit_rate;
+		pr_debug("%s:session id %d: Set-aac-cfg:SR= 0x%x ch=0x%x bitrate=0x%x, format(adts/raw) = %d\n",
+			__func__, audio->ac->session, enc_cfg->sample_rate,
+			enc_cfg->channels, enc_cfg->bit_rate,
+			enc_cfg->stream_format);
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG: {
+		struct msm_audio_aac_config *aac_cfg;
+		struct msm_audio_aac_config *audio_aac_cfg;
+		struct msm_audio_aac_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		audio_aac_cfg = audio->codec_cfg;
+		aac_cfg = (struct msm_audio_aac_config *)arg;
+
+		if (aac_cfg == NULL) {
+			pr_err("%s: NULL config pointer %s\n",
+				__func__, "AUDIO_SET_AAC_CONFIG");
+			rc = -EINVAL;
+			break;
+		}
+		pr_debug("%s:session id %d: AUDIO_SET_AAC_CONFIG: sbr_flag = %d sbr_ps_flag = %d\n",
+			 __func__, audio->ac->session, aac_cfg->sbr_on_flag,
+			 aac_cfg->sbr_ps_on_flag);
+		audio_aac_cfg->sbr_on_flag = aac_cfg->sbr_on_flag;
+		audio_aac_cfg->sbr_ps_on_flag = aac_cfg->sbr_ps_on_flag;
+		if ((audio_aac_cfg->sbr_on_flag == 1) ||
+			 (audio_aac_cfg->sbr_ps_on_flag == 1)) {
+			if (enc_cfg->sample_rate < 24000) {
+				pr_err("%s: ERROR in setting samplerate = %d\n",
+					__func__, enc_cfg->sample_rate);
+				rc = -EINVAL;
+				break;
+			}
+		}
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long aac_in_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = aac_in_ioctl_shared(file, cmd, NULL);
+		break;
+	}
+	case AUDIO_GET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config cfg;
+
+		rc = aac_in_ioctl_shared(file, cmd, &cfg);
+		if (rc) {
+			pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG failed. rc=%d\n",
+				__func__, rc);
+			break;
+		}
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_ENC_CONFIG: {
+		struct msm_audio_aac_enc_config cfg;
+
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_ENC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = aac_in_ioctl_shared(file, cmd, &cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
+				__func__, rc);
+		break;
+	}
+	case AUDIO_GET_AAC_CONFIG: {
+		if (copy_to_user((void *)arg, &audio->codec_cfg,
+				 sizeof(struct msm_audio_aac_config))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG: {
+		struct msm_audio_aac_config aac_cfg;
+
+		if (copy_from_user(&aac_cfg, (void *)arg,
+				 sizeof(struct msm_audio_aac_config))) {
+			pr_err("%s: copy_to_user for AUDIO_SET_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = aac_in_ioctl_shared(file, cmd, &aac_cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc=%d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd=%d\n", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_aac_enc_config32 {
+	u32 channels;
+	u32 sample_rate;
+	u32 bit_rate;
+	u32 stream_format;
+};
+
+struct msm_audio_aac_config32 {
+	s16 format;
+	u16 audio_object;
+	u16 ep_config;       /* 0 ~ 3 useful only obj = ERLC */
+	u16 aac_section_data_resilience_flag;
+	u16 aac_scalefactor_data_resilience_flag;
+	u16 aac_spectral_data_resilience_flag;
+	u16 sbr_on_flag;
+	u16 sbr_ps_on_flag;
+	u16 dual_mono_mode;
+	u16 channel_configuration;
+	u16 sample_rate;
+};
+
+enum {
+	AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
+	AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32),
+	AUDIO_SET_AAC_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config32),
+	AUDIO_GET_AAC_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config32)
+};
+
+static long aac_in_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = aac_in_ioctl_shared(file, cmd, NULL);
+		break;
+	}
+	case AUDIO_GET_AAC_ENC_CONFIG_32: {
+		struct msm_audio_aac_enc_config cfg;
+		struct msm_audio_aac_enc_config32 cfg_32;
+
+		memset(&cfg_32, 0, sizeof(cfg_32));
+
+		cmd = AUDIO_GET_AAC_ENC_CONFIG;
+		rc = aac_in_ioctl_shared(file, cmd, &cfg);
+		if (rc) {
+			pr_err("%s:AUDIO_GET_AAC_ENC_CONFIG_32 failed. Rc= %d\n",
+				__func__, rc);
+			break;
+		}
+		cfg_32.channels = cfg.channels;
+		cfg_32.sample_rate = cfg.sample_rate;
+		cfg_32.bit_rate = cfg.bit_rate;
+		cfg_32.stream_format = cfg.stream_format;
+		if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_ENC_CONFIG_32: {
+		struct msm_audio_aac_enc_config cfg;
+		struct msm_audio_aac_enc_config32 cfg_32;
+
+		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_GET_AAC_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.channels = cfg_32.channels;
+		cfg.sample_rate = cfg_32.sample_rate;
+		cfg.bit_rate = cfg_32.bit_rate;
+		cfg.stream_format = cfg_32.stream_format;
+		/* The command should be converted from 32 bit to normal
+		 * before the shared ioctl is called as shared ioctl
+		 * can process only normal commands
+		 */
+		cmd = AUDIO_SET_AAC_ENC_CONFIG;
+		rc = aac_in_ioctl_shared(file, cmd, &cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG_32 failed. rc=%d\n",
+				__func__, rc);
+		break;
+	}
+	case AUDIO_GET_AAC_CONFIG_32: {
+		struct msm_audio_aac_config *aac_config;
+		struct msm_audio_aac_config32 aac_config_32;
+
+		aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+		aac_config_32.format = aac_config->format;
+		aac_config_32.audio_object = aac_config->audio_object;
+		aac_config_32.ep_config = aac_config->ep_config;
+		aac_config_32.aac_section_data_resilience_flag =
+			aac_config->aac_section_data_resilience_flag;
+		aac_config_32.aac_scalefactor_data_resilience_flag =
+			aac_config->aac_scalefactor_data_resilience_flag;
+		aac_config_32.aac_spectral_data_resilience_flag =
+			aac_config->aac_spectral_data_resilience_flag;
+		aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
+		aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
+		aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
+		aac_config_32.channel_configuration =
+				aac_config->channel_configuration;
+		aac_config_32.sample_rate = aac_config->sample_rate;
+
+		if (copy_to_user((void *)arg, &aac_config_32,
+				 sizeof(aac_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG_32: {
+		struct msm_audio_aac_config aac_cfg;
+		struct msm_audio_aac_config32 aac_cfg_32;
+
+		if (copy_from_user(&aac_cfg_32, (void *)arg,
+					sizeof(aac_cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		aac_cfg.format = aac_cfg_32.format;
+		aac_cfg.audio_object = aac_cfg_32.audio_object;
+		aac_cfg.ep_config = aac_cfg_32.ep_config;
+		aac_cfg.aac_section_data_resilience_flag =
+			aac_cfg_32.aac_section_data_resilience_flag;
+		aac_cfg.aac_scalefactor_data_resilience_flag =
+			aac_cfg_32.aac_scalefactor_data_resilience_flag;
+		aac_cfg.aac_spectral_data_resilience_flag =
+			aac_cfg_32.aac_spectral_data_resilience_flag;
+		aac_cfg.sbr_on_flag = aac_cfg_32.sbr_on_flag;
+		aac_cfg.sbr_ps_on_flag = aac_cfg_32.sbr_ps_on_flag;
+		aac_cfg.dual_mono_mode = aac_cfg_32.dual_mono_mode;
+		aac_cfg.channel_configuration =
+				aac_cfg_32.channel_configuration;
+		aac_cfg.sample_rate = aac_cfg_32.sample_rate;
+
+		cmd = AUDIO_SET_AAC_CONFIG;
+		rc = aac_in_ioctl_shared(file, cmd, &aac_cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d\n", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+#else
+#define aac_in_compat_ioctl NULL
+#endif
+
+static int aac_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_aac_enc_config *enc_cfg;
+	struct msm_audio_aac_config *aac_config;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_aac_enc_config),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+				GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	aac_config = audio->codec_cfg;
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 1536;
+	audio->max_frames_per_buf = 5;
+	enc_cfg->sample_rate = 8000;
+	enc_cfg->channels = 1;
+	enc_cfg->bit_rate = 16000;
+	enc_cfg->stream_format = 0x00;/* 0:ADTS, 3:RAW */
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf   = 0x01;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	audio->pcm_cfg.buffer_size  = PCM_BUF_SIZE;
+	aac_config->format = AUDIO_AAC_FORMAT_ADTS;
+	aac_config->audio_object = AUDIO_AAC_OBJECT_LC;
+	aac_config->sbr_on_flag = 0;
+	aac_config->sbr_ps_on_flag = 0;
+	aac_config->channel_configuration = 1;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+							(void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+			__func__);
+		kfree(audio->enc_cfg);
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	/* open aac encoder in tunnel mode */
+	audio->buf_cfg.frames_per_buf = 0x01;
+
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_MPEG4_AAC,
+						FORMAT_LINEAR_PCM);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->buf_cfg.meta_info_enable = 0x01;
+		pr_info("%s:session id %d: NT mode encoder success\n", __func__,
+				audio->ac->session);
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_MPEG4_AAC);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: Tunnel Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__,
+				audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->buf_cfg.meta_info_enable = 0x00;
+		pr_info("%s:session id %d: T mode encoder success\n", __func__,
+			audio->ac->session);
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+	audio->opened = 1;
+	audio->reset_event = false;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = aac_in_compat_ioctl;
+	audio->enc_ioctl = aac_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= aac_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl	= audio_in_ioctl,
+	.compat_ioctl	= audio_in_compat_ioctl
+};
+
+struct miscdevice audio_aac_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_aac_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init aac_in_init(void)
+{
+	return misc_register(&audio_aac_in_misc);
+}
+device_initcall(aac_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/amrnb_in.c b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
new file mode 100644
index 0000000..bdafde0
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/amrnb_in.c
@@ -0,0 +1,403 @@
+/* Copyright (c) 2010-2012, 2014, 2016-2017 The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_amrnb.h>
+#include <linux/compat.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((32+sizeof(struct meta_out_dsp)) * 10))
+
+static long amrnb_in_ioctl_shared(struct file *file,
+				unsigned int cmd, void *arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			pr_info("%s:AUDIO_START already over\n", __func__);
+			rc = 0;
+			break;
+		}
+		rc = audio_in_buf_alloc(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: buffer allocation failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+
+		rc = q6asm_enc_cfg_blk_amrnb(audio->ac,
+			audio->buf_cfg.frames_per_buf,
+			enc_cfg->band_mode,
+			enc_cfg->dtx_enable);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd amrnb media format block failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+				audio->pcm_cfg.sample_rate,
+				audio->pcm_cfg.channel_count);
+
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed\n",
+				__func__, audio->ac->session);
+				break;
+			}
+		}
+		pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+				__func__, audio->ac->session,
+				audio->enabled);
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac); /* Push buffer to DSP */
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:AUDIO_STOP\n", __func__);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+		struct msm_audio_amrnb_enc_config_v2 *cfg;
+		struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+
+		cfg = (struct msm_audio_amrnb_enc_config_v2 *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer for %s\n",
+					__func__,
+					"AUDIO_SET_AMRNB_ENC_CONFIG_V2");
+			rc = -EINVAL;
+			break;
+		}
+
+		enc_cfg = audio->enc_cfg;
+		if (cfg->band_mode > 8 ||
+			 cfg->band_mode < 1) {
+			pr_err("%s:session id %d: invalid band mode\n",
+				__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		/* AMR NB encoder accepts values between 0-7
+		 * while openmax provides value between 1-8
+		 * as per spec
+		 */
+		enc_cfg->band_mode = (cfg->band_mode - 1);
+		enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0);
+		enc_cfg->frame_format = 0;
+		pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
+				__func__, audio->ac->session,
+				enc_cfg->band_mode, enc_cfg->dtx_enable);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long amrnb_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc =  amrnb_in_ioctl_shared(file, cmd, NULL);
+		break;
+	}
+	case AUDIO_GET_AMRNB_ENC_CONFIG_V2: {
+		if (copy_to_user((void *)arg, audio->enc_cfg,
+			sizeof(struct msm_audio_amrnb_enc_config_v2))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_AMRNB_ENC_CONFIG_V2: {
+		struct msm_audio_amrnb_enc_config_v2 cfg;
+
+		if (copy_from_user(&cfg, (void *) arg,
+				sizeof(cfg))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = amrnb_in_ioctl_shared(file, cmd, &cfg);
+		if (rc)
+			pr_err("%s: AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed. rc=%d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd=%d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_amrnb_enc_config_v2_32 {
+	u32 band_mode;
+	u32 dtx_enable;
+	u32 frame_format;
+};
+
+enum {
+	AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+2),
+		struct msm_audio_amrnb_enc_config_v2_32),
+	AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+3),
+		struct msm_audio_amrnb_enc_config_v2_32)
+};
+
+static long amrnb_in_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc =  amrnb_in_ioctl_shared(file, cmd, NULL);
+		break;
+	}
+	case AUDIO_GET_AMRNB_ENC_CONFIG_V2_32: {
+		struct msm_audio_amrnb_enc_config_v2 *amrnb_config;
+		struct msm_audio_amrnb_enc_config_v2_32 amrnb_config_32;
+
+		memset(&amrnb_config_32, 0, sizeof(amrnb_config_32));
+
+		amrnb_config =
+		(struct msm_audio_amrnb_enc_config_v2 *)audio->enc_cfg;
+		amrnb_config_32.band_mode = amrnb_config->band_mode;
+		amrnb_config_32.dtx_enable = amrnb_config->dtx_enable;
+		amrnb_config_32.frame_format = amrnb_config->frame_format;
+
+		if (copy_to_user((void *)arg, &amrnb_config_32,
+			sizeof(amrnb_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AMRNB_ENC_CONFIG_V2_32 failed",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_AMRNB_ENC_CONFIG_V2_32: {
+		struct msm_audio_amrnb_enc_config_v2_32 cfg_32;
+
+		if (copy_from_user(&cfg_32, (void *) arg,
+				sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AMRNB_ENC_CONFIG_V2_32 failed\n",
+					__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cmd = AUDIO_SET_AMRNB_ENC_CONFIG_V2;
+		rc = amrnb_in_ioctl_shared(file, cmd, &cfg_32);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AMRNB_ENC_CONFIG_V2 failed rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+#else
+#define amrnb_in_compat_ioctl NULL
+#endif
+
+static int amrnb_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_amrnb_enc_config_v2 *enc_cfg;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrnb_enc_config_v2),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 32;
+	audio->max_frames_per_buf = 10;
+	audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	enc_cfg->band_mode = 7;
+	enc_cfg->dtx_enable = 0;
+	audio->pcm_cfg.channel_count = 1;
+	audio->pcm_cfg.sample_rate = 8000;
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf = 0x01;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+				(void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+			__func__);
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open amrnb encoder in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_AMRNB,
+					FORMAT_LINEAR_PCM);
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: NT mode encoder success\n",
+				__func__, audio->ac->session);
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_AMRNB);
+		if (rc < 0) {
+			pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__, audio->ac->session,
+				rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: T mode encoder success\n",
+				__func__, audio->ac->session);
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	audio->opened = 1;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = amrnb_in_compat_ioctl;
+	audio->enc_ioctl = amrnb_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= amrnb_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl	= audio_in_ioctl,
+	.compat_ioctl   = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_amrnb_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_amrnb_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init amrnb_in_init(void)
+{
+	return misc_register(&audio_amrnb_in_misc);
+}
+
+device_initcall(amrnb_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/amrwb_in.c b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
new file mode 100644
index 0000000..98c1911
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/amrwb_in.c
@@ -0,0 +1,400 @@
+/* Copyright (c) 2011-2012, 2014, 2016-2017 The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/msm_audio_amrwb.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/compat.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((61+sizeof(struct meta_out_dsp)) * 10))
+
+static long amrwb_in_ioctl_shared(struct file *file,
+				  unsigned int cmd, void *arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_amrwb_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			pr_info("%s:AUDIO_START already over\n", __func__);
+			rc = 0;
+			break;
+		}
+		rc = audio_in_buf_alloc(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: buffer allocation failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+
+		rc = q6asm_enc_cfg_blk_amrwb(audio->ac,
+			audio->buf_cfg.frames_per_buf,
+			enc_cfg->band_mode,
+			enc_cfg->dtx_enable);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd amrwb media format block failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+				audio->pcm_cfg.sample_rate,
+				audio->pcm_cfg.channel_count);
+
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed\n",
+				__func__, audio->ac->session);
+				break;
+			}
+		}
+		pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+				__func__, audio->ac->session,
+				audio->enabled);
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac); /* Push buffer to DSP */
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:AUDIO_STOP\n", __func__);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AMRWB_ENC_CONFIG: {
+		struct msm_audio_amrwb_enc_config *cfg;
+		struct msm_audio_amrwb_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		cfg = (struct msm_audio_amrwb_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer for %s\n",
+					__func__, "AUDIO_SET_AMRWB_ENC_CONFIG");
+			rc = -EINVAL;
+			break;
+		}
+
+		if (cfg->band_mode > 8) {
+			pr_err("%s:session id %d: invalid band mode\n",
+				__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		/* ToDo: AMR WB encoder accepts values between 0-8
+		 * while openmax provides value between 9-17
+		 * as per spec
+		 */
+		enc_cfg->band_mode = cfg->band_mode;
+		enc_cfg->dtx_enable = (cfg->dtx_enable ? 1 : 0);
+		/* Currently DSP does not support different frameformat */
+		enc_cfg->frame_format = 0;
+		pr_debug("%s:session id %d: band_mode = 0x%x dtx_enable=0x%x\n",
+				__func__, audio->ac->session,
+				enc_cfg->band_mode, enc_cfg->dtx_enable);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long amrwb_in_ioctl(struct file *file,
+				  unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = amrwb_in_ioctl_shared(file, cmd, NULL);
+		break;
+	}
+	case AUDIO_GET_AMRWB_ENC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->enc_cfg,
+				sizeof(struct msm_audio_amrwb_enc_config)))
+			pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_SET_AMRWB_ENC_CONFIG: {
+		struct msm_audio_amrwb_enc_config cfg;
+
+		if (copy_from_user(&cfg, (void *) arg,
+				sizeof(cfg))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = amrwb_in_ioctl_shared(file, cmd, &cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_amrwb_enc_config_32 {
+	u32 band_mode;
+	u32 dtx_enable;
+	u32 frame_format;
+};
+
+enum {
+	AUDIO_GET_AMRWB_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+0),
+		struct msm_audio_amrwb_enc_config_32),
+	AUDIO_SET_AMRWB_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+1),
+		struct msm_audio_amrwb_enc_config_32)
+};
+
+static long amrwb_in_compat_ioctl(struct file *file,
+				  unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = amrwb_in_ioctl_shared(file, cmd, NULL);
+		break;
+	}
+	case AUDIO_GET_AMRWB_ENC_CONFIG_32: {
+		struct msm_audio_amrwb_enc_config *amrwb_config;
+		struct msm_audio_amrwb_enc_config_32 amrwb_config_32;
+
+		memset(&amrwb_config_32, 0, sizeof(amrwb_config_32));
+
+		amrwb_config =
+		(struct msm_audio_amrwb_enc_config *)audio->enc_cfg;
+		amrwb_config_32.band_mode = amrwb_config->band_mode;
+		amrwb_config_32.dtx_enable = amrwb_config->dtx_enable;
+		amrwb_config_32.frame_format = amrwb_config->frame_format;
+
+		if (copy_to_user((void *)arg, &amrwb_config_32,
+			sizeof(struct msm_audio_amrwb_enc_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AMRWB_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_AMRWB_ENC_CONFIG_32: {
+		struct msm_audio_amrwb_enc_config cfg_32;
+
+		if (copy_from_user(&cfg_32, (void *) arg,
+				sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AMRWB_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cmd = AUDIO_SET_AMRWB_ENC_CONFIG;
+		rc = amrwb_in_ioctl_shared(file, cmd, &cfg_32);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_ENC_CONFIG failed. rc=%d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+#else
+#define amrwb_in_compat_ioctl NULL
+#endif
+
+static int amrwb_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_amrwb_enc_config *enc_cfg;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_amrwb_enc_config),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 32;
+	audio->max_frames_per_buf = 10;
+	audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	enc_cfg->band_mode = 8;
+	enc_cfg->dtx_enable = 0;
+	audio->pcm_cfg.channel_count = 1;
+	audio->pcm_cfg.sample_rate = 16000;
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf = 0x01;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+				(void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s:audio[%pK]: Could not allocate memory for audio client\n",
+			__func__, audio);
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open amrwb encoder in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_AMRWB,
+					FORMAT_LINEAR_PCM);
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: NT mode encoder success\n",
+				__func__, audio->ac->session);
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_AMRWB);
+		if (rc < 0) {
+			pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__, audio->ac->session,
+				rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: T mode encoder success\n",
+				__func__, audio->ac->session);
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	audio->opened = 1;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = amrwb_in_compat_ioctl;
+	audio->enc_ioctl = amrwb_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= amrwb_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl	= audio_in_ioctl,
+	.compat_ioctl   = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_amrwb_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_amrwb_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init amrwb_in_init(void)
+{
+	return misc_register(&audio_amrwb_in_misc);
+}
+
+device_initcall(amrwb_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_aac.c b/drivers/misc/qcom/qdsp6v2/audio_aac.c
new file mode 100644
index 0000000..0f371fd
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_aac.c
@@ -0,0 +1,474 @@
+/* aac audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_audio_aac.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
+#define PCM_BUFSZ_MIN_AAC	((8*1024) + sizeof(struct dec_meta_out))
+
+static struct miscdevice audio_aac_misc;
+static struct ws_mgr audio_aac_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_aac_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+				void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_aac_cfg aac_cfg;
+		struct msm_audio_aac_config *aac_config;
+		uint32_t sbr_ps = 0x00;
+
+		pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+							audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac, 0, 0);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		/* turn on both sbr and ps */
+		rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
+		if (rc < 0)
+			pr_err("sbr-ps enable failed\n");
+		aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+		if (aac_config->sbr_ps_on_flag)
+			aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+		else if (aac_config->sbr_on_flag)
+			aac_cfg.aot = AAC_ENC_MODE_AAC_P;
+		else
+			aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
+
+		switch (aac_config->format) {
+		case AUDIO_AAC_FORMAT_ADTS:
+			aac_cfg.format = 0x00;
+			break;
+		case AUDIO_AAC_FORMAT_LOAS:
+			aac_cfg.format = 0x01;
+			break;
+		case AUDIO_AAC_FORMAT_ADIF:
+			aac_cfg.format = 0x02;
+			break;
+		default:
+		case AUDIO_AAC_FORMAT_RAW:
+			aac_cfg.format = 0x03;
+		}
+		aac_cfg.ep_config = aac_config->ep_config;
+		aac_cfg.section_data_resilience =
+			aac_config->aac_section_data_resilience_flag;
+		aac_cfg.scalefactor_data_resilience =
+			aac_config->aac_scalefactor_data_resilience_flag;
+		aac_cfg.spectral_data_resilience =
+			aac_config->aac_spectral_data_resilience_flag;
+		aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+		if (audio->feedback == TUNNEL_MODE) {
+			aac_cfg.sample_rate = aac_config->sample_rate;
+			aac_cfg.ch_cfg = aac_config->channel_configuration;
+		} else {
+			aac_cfg.sample_rate =  audio->pcm_cfg.sample_rate;
+			aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+		}
+
+		pr_debug("%s:format=%x aot=%d  ch=%d sr=%d\n",
+			__func__, aac_cfg.format,
+			aac_cfg.aot, aac_cfg.ch_cfg,
+			aac_cfg.sample_rate);
+
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_aac(audio->ac, &aac_cfg);
+		if (rc < 0) {
+			pr_err("cmd media format block failed\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			rc = enable_volume_ramp(audio);
+			if (rc < 0) {
+				pr_err("%s: Failed to enable volume ramp\n",
+					__func__);
+			}
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+						audio->ac->session,
+						audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG: {
+		struct msm_audio_aac_config *aac_config;
+		uint16_t sce_left = 1, sce_right = 2;
+
+		pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+		aac_config = (struct msm_audio_aac_config *)arg;
+		if (aac_config == NULL) {
+			pr_err("%s: Invalid config pointer\n", __func__);
+			rc = -EINVAL;
+			break;
+		}
+		memcpy(audio->codec_cfg, aac_config,
+				sizeof(struct msm_audio_aac_config));
+		/* PL_PR is 0 only need to check PL_SR */
+		if (aac_config->dual_mono_mode >
+		    AUDIO_AAC_DUAL_MONO_PL_SR) {
+			pr_err("%s:Invalid dual_mono mode =%d\n", __func__,
+			aac_config->dual_mono_mode);
+		} else {
+			/* convert the data from user into sce_left
+			 * and sce_right based on the definitions
+			 */
+			pr_debug("%s: modify dual_mono mode =%d\n", __func__,
+				 aac_config->dual_mono_mode);
+			switch (aac_config->dual_mono_mode) {
+			case AUDIO_AAC_DUAL_MONO_PL_PR:
+				sce_left = 1;
+				sce_right = 1;
+				break;
+			case AUDIO_AAC_DUAL_MONO_SL_SR:
+				sce_left = 2;
+				sce_right = 2;
+				break;
+			case AUDIO_AAC_DUAL_MONO_SL_PR:
+				sce_left = 2;
+				sce_right = 1;
+				break;
+			case AUDIO_AAC_DUAL_MONO_PL_SR:
+			default:
+				sce_left = 1;
+				sce_right = 2;
+				break;
+			}
+			rc = q6asm_cfg_dual_mono_aac(audio->ac,
+						sce_left, sce_right);
+			if (rc < 0)
+				pr_err("%s:asm cmd dualmono failed rc=%d\n",
+					 __func__, rc);
+		}
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_AAC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_aac_config))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG: {
+		struct msm_audio_aac_config aac_config;
+
+		pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+		if (copy_from_user(&aac_config, (void *)arg,
+			sizeof(aac_config))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = audio_ioctl_shared(file, cmd, &aac_config);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+						__func__, rc);
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
+				__func__, audio, rc);
+	}
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_aac_config32 {
+	s16 format;
+	u16 audio_object;
+	u16 ep_config;	/* 0 ~ 3 useful only obj = ERLC */
+	u16 aac_section_data_resilience_flag;
+	u16 aac_scalefactor_data_resilience_flag;
+	u16 aac_spectral_data_resilience_flag;
+	u16 sbr_on_flag;
+	u16 sbr_ps_on_flag;
+	u16 dual_mono_mode;
+	u16 channel_configuration;
+	u16 sample_rate;
+};
+
+enum {
+	AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
+	AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_AAC_CONFIG_32: {
+		struct msm_audio_aac_config *aac_config;
+		struct msm_audio_aac_config32 aac_config_32;
+
+		aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+		aac_config_32.format = aac_config->format;
+		aac_config_32.audio_object = aac_config->audio_object;
+		aac_config_32.ep_config = aac_config->ep_config;
+		aac_config_32.aac_section_data_resilience_flag =
+			aac_config->aac_section_data_resilience_flag;
+		aac_config_32.aac_scalefactor_data_resilience_flag =
+			 aac_config->aac_scalefactor_data_resilience_flag;
+		aac_config_32.aac_spectral_data_resilience_flag =
+			aac_config->aac_spectral_data_resilience_flag;
+		aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
+		aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
+		aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
+		aac_config_32.channel_configuration =
+					aac_config->channel_configuration;
+		aac_config_32.sample_rate = aac_config->sample_rate;
+
+		if (copy_to_user((void *)arg, &aac_config_32,
+			sizeof(aac_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG_32: {
+		struct msm_audio_aac_config aac_config;
+		struct msm_audio_aac_config32 aac_config_32;
+
+		pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+		if (copy_from_user(&aac_config_32, (void *)arg,
+			sizeof(aac_config_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		aac_config.format = aac_config_32.format;
+		aac_config.audio_object = aac_config_32.audio_object;
+		aac_config.ep_config = aac_config_32.ep_config;
+		aac_config.aac_section_data_resilience_flag =
+			aac_config_32.aac_section_data_resilience_flag;
+		aac_config.aac_scalefactor_data_resilience_flag =
+			aac_config_32.aac_scalefactor_data_resilience_flag;
+		aac_config.aac_spectral_data_resilience_flag =
+			aac_config_32.aac_spectral_data_resilience_flag;
+		aac_config.sbr_on_flag = aac_config_32.sbr_on_flag;
+		aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag;
+		aac_config.dual_mono_mode = aac_config_32.dual_mono_mode;
+		aac_config.channel_configuration =
+				aac_config_32.channel_configuration;
+		aac_config.sample_rate = aac_config_32.sample_rate;
+
+		cmd = AUDIO_SET_AAC_CONFIG;
+		rc = audio_ioctl_shared(file, cmd, &aac_config);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("%s[%pK]:Failed in utils_ioctl: %d\n",
+				__func__, audio, rc);
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+	struct msm_audio_aac_config *aac_config = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_aac_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+					GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	aac_config = audio->codec_cfg;
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AAC;
+	audio->miscdevice = &audio_aac_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_aac_ws_mgr;
+	aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_MPEG4_AAC);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open AAC decoder, expected frames is always 1
+		 * audio->buf_cfg.frames_per_buf = 0x01;
+		 */
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_AAC);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_aac_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_aac_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:aacdec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_aac_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_aac",
+	.fops = &audio_aac_fops,
+};
+
+static int __init audio_aac_init(void)
+{
+	int ret = misc_register(&audio_aac_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_aac_misc.this_device, true);
+	audio_aac_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_aac_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_aac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_alac.c b/drivers/misc/qcom/qdsp6v2/audio_alac.c
new file mode 100644
index 0000000..d0b86c6
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_alac.c
@@ -0,0 +1,435 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_alac.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_alac_misc;
+static struct ws_mgr audio_alac_ws_mgr;
+
+static const struct file_operations audio_alac_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+
+static struct dentry *config_debugfs_create_file(const char *name, void *data)
+{
+	return debugfs_create_file(name, S_IFREG | 0444,
+				NULL, (void *)data, &audio_alac_debug_fops);
+}
+
+static int alac_channel_map(u8 *channel_mapping, uint32_t channels);
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_alac_cfg alac_cfg;
+		struct msm_audio_alac_config *alac_config;
+		u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+		memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+		if (alac_channel_map(channel_mapping,
+			audio->pcm_cfg.channel_count)) {
+			pr_err("%s: setting channel map failed %d\n",
+					__func__, audio->pcm_cfg.channel_count);
+		}
+
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count,
+					16, /*bits per sample*/
+					false, false, channel_mapping);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
+		alac_cfg.frame_length = alac_config->frameLength;
+		alac_cfg.compatible_version = alac_config->compatVersion;
+		alac_cfg.bit_depth = alac_config->bitDepth;
+		alac_cfg.pb = alac_config->pb;
+		alac_cfg.mb = alac_config->mb;
+		alac_cfg.kb = alac_config->kb;
+		alac_cfg.num_channels = alac_config->channelCount;
+		alac_cfg.max_run = alac_config->maxRun;
+		alac_cfg.max_frame_bytes = alac_config->maxSize;
+		alac_cfg.avg_bit_rate = alac_config->averageBitRate;
+		alac_cfg.sample_rate = alac_config->sampleRate;
+		alac_cfg.channel_layout_tag = alac_config->channelLayout;
+		pr_debug("%s: frame_length %d compatible_version %d bit_depth %d pb %d mb %d kb %d num_channels %d max_run %d max_frame_bytes %d avg_bit_rate %d sample_rate %d channel_layout_tag %d\n",
+				__func__, alac_config->frameLength,
+				alac_config->compatVersion,
+				alac_config->bitDepth, alac_config->pb,
+				alac_config->mb, alac_config->kb,
+				alac_config->channelCount, alac_config->maxRun,
+				alac_config->maxSize,
+				alac_config->averageBitRate,
+				alac_config->sampleRate,
+				alac_config->channelLayout);
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_alac(audio->ac, &alac_cfg,
+							audio->ac->stream_id);
+		if (rc < 0) {
+			pr_err("cmd media format block failed\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_ALAC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_alac_config))) {
+			pr_err("%s:copy_to_user for AUDIO_GET_ALAC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_ALAC_CONFIG: {
+		if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_alac_config))) {
+			pr_err("%s:copy_from_user for AUDIO_SET_ALAC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	default: {
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_alac_config_32 {
+	u32 frameLength;
+	u8 compatVersion;
+	u8 bitDepth;
+	u8 pb;
+	u8 mb;
+	u8 kb;
+	u8 channelCount;
+	u16 maxRun;
+	u32 maxSize;
+	u32 averageBitRate;
+	u32 sampleRate;
+	u32 channelLayout;
+};
+
+enum {
+	AUDIO_GET_ALAC_CONFIG_32 =  _IOR(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config_32),
+	AUDIO_SET_ALAC_CONFIG_32 =  _IOW(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_ALAC_CONFIG_32: {
+		struct msm_audio_alac_config *alac_config;
+		struct msm_audio_alac_config_32 alac_config_32;
+
+		memset(&alac_config_32, 0, sizeof(alac_config_32));
+
+		alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
+		alac_config_32.frameLength = alac_config->frameLength;
+		alac_config_32.compatVersion =
+				alac_config->compatVersion;
+		alac_config_32.bitDepth = alac_config->bitDepth;
+		alac_config_32.pb = alac_config->pb;
+		alac_config_32.mb = alac_config->mb;
+		alac_config_32.kb = alac_config->kb;
+		alac_config_32.channelCount = alac_config->channelCount;
+		alac_config_32.maxRun = alac_config->maxRun;
+		alac_config_32.maxSize = alac_config->maxSize;
+		alac_config_32.averageBitRate = alac_config->averageBitRate;
+		alac_config_32.sampleRate = alac_config->sampleRate;
+		alac_config_32.channelLayout = alac_config->channelLayout;
+
+		if (copy_to_user((void *)arg, &alac_config_32,
+			sizeof(alac_config_32))) {
+			pr_err("%s: copy_to_user for GET_ALAC_CONFIG_32 failed\n",
+				 __func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_ALAC_CONFIG_32: {
+		struct msm_audio_alac_config *alac_config;
+		struct msm_audio_alac_config_32 alac_config_32;
+
+		if (copy_from_user(&alac_config_32, (void *)arg,
+			sizeof(alac_config_32))) {
+			pr_err("%s: copy_from_user for SET_ALAC_CONFIG_32 failed\n"
+				, __func__);
+			rc = -EFAULT;
+			break;
+		}
+		alac_config = (struct msm_audio_alac_config *)audio->codec_cfg;
+		alac_config->frameLength = alac_config_32.frameLength;
+		alac_config->compatVersion =
+				alac_config_32.compatVersion;
+		alac_config->bitDepth = alac_config_32.bitDepth;
+		alac_config->pb = alac_config_32.pb;
+		alac_config->mb = alac_config_32.mb;
+		alac_config->kb = alac_config_32.kb;
+		alac_config->channelCount = alac_config_32.channelCount;
+		alac_config->maxRun = alac_config_32.maxRun;
+		alac_config->maxSize = alac_config_32.maxSize;
+		alac_config->averageBitRate = alac_config_32.averageBitRate;
+		alac_config->sampleRate = alac_config_32.sampleRate;
+		alac_config->channelLayout = alac_config_32.channelLayout;
+
+		break;
+	}
+	default: {
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_alac_" + 5];
+
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+	if (!audio)
+		return -ENOMEM;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_alac_config),
+					GFP_KERNEL);
+	if (!audio->codec_cfg) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_alac_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_alac_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_ALAC);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open ALAC decoder, expected frames is always 1*/
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_ALAC);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+	snprintf(name, sizeof(name), "msm_alac_%04x", audio->ac->session);
+	audio->dentry = config_debugfs_create_file(name, (void *)audio);
+
+	if (IS_ERR_OR_NULL(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+	pr_debug("%s:alacdec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static int alac_channel_map(u8 *channel_mapping, uint32_t channels)
+{
+	u8 *lchannel_mapping;
+
+	lchannel_mapping = channel_mapping;
+	pr_debug("%s:  channels passed: %d\n", __func__, channels);
+	if (channels == 1)  {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+	} else if (channels == 2) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+	} else if (channels == 3) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+	} else if (channels == 4) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_CS;
+	} else if (channels == 5) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+	} else if (channels == 6) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+		lchannel_mapping[5] = PCM_CHANNEL_LFE;
+	} else if (channels == 7) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+		lchannel_mapping[5] = PCM_CHANNEL_CS;
+		lchannel_mapping[6] = PCM_CHANNEL_LFE;
+	} else if (channels == 8) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FLC;
+		lchannel_mapping[2] = PCM_CHANNEL_FRC;
+		lchannel_mapping[3] = PCM_CHANNEL_FL;
+		lchannel_mapping[4] = PCM_CHANNEL_FR;
+		lchannel_mapping[5] = PCM_CHANNEL_LS;
+		lchannel_mapping[6] = PCM_CHANNEL_RS;
+		lchannel_mapping[7] = PCM_CHANNEL_LFE;
+	} else {
+		pr_err("%s: ERROR.unsupported num_ch = %u\n",
+				__func__, channels);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct file_operations audio_alac_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_alac_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_alac",
+	.fops = &audio_alac_fops,
+};
+
+static int __init audio_alac_init(void)
+{
+	int ret = misc_register(&audio_alac_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_alac_misc.this_device, true);
+	audio_alac_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_alac_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_alac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrnb.c b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
new file mode 100644
index 0000000..950098b
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrnb.c
@@ -0,0 +1,226 @@
+/* amrnb audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_amrnb_misc;
+static struct ws_mgr audio_amrnb_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrnb_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+			       unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+				audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("%s: pcm output block config failed rc=%d\n",
+					__func__, rc);
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s: Audio Start procedure failed rc=%d\n",
+				__func__, rc);
+			break;
+		}
+		pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_amrnb_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_amrnb_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_amrnb_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_AMRNB);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_AMRNB);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_amrnb_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_amrnb_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:amrnb decoder open success, session_id = %d\n", __func__,
+				audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_amrnb_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl,
+};
+
+static struct miscdevice audio_amrnb_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_amrnb",
+	.fops = &audio_amrnb_fops,
+};
+
+static int __init audio_amrnb_init(void)
+{
+	int ret = misc_register(&audio_amrnb_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_amrnb_misc.this_device, true);
+	audio_amrnb_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_amrnb_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_amrnb_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwb.c b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
new file mode 100644
index 0000000..cb5db0d
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwb.c
@@ -0,0 +1,231 @@
+/* amrwb audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/compat.h>
+#include <linux/types.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_amrwb_misc;
+static struct ws_mgr audio_amrwb_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwb_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+						audio->ac->session,
+						audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+			       unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+				audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("%s: pcm output block config failed rc=%d\n",
+					__func__, rc);
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s: Audio Start procedure failed rc=%d\n",
+				__func__, rc);
+			break;
+		}
+		pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+				audio->ac->session,
+				audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling compat ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_amrwb_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_amrwb_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_amrwb_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_AMRWB);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_AMRWB);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_amrwb_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_amrwb_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s: AMRWB dec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_amrwb_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl,
+};
+
+static struct miscdevice audio_amrwb_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_amrwb",
+	.fops = &audio_amrwb_fops,
+};
+
+static int __init audio_amrwb_init(void)
+{
+	int ret = misc_register(&audio_amrwb_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_amrwb_misc.this_device, true);
+	audio_amrwb_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_amrwb_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_amrwb_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
new file mode 100644
index 0000000..2132591
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_amrwbplus.c
@@ -0,0 +1,397 @@
+/* amr-wbplus audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/msm_audio_amrwbplus.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_amrwbplus_misc;
+static struct ws_mgr audio_amrwbplus_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_amrwbplus_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+	if (audio != NULL) {
+		char name[sizeof("msm_amrwbplus_") + 5];
+
+		snprintf(name, sizeof(name), "msm_amrwbplus_%04x",
+			audio->ac->session);
+		audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+						NULL, (void *)audio,
+						&audio_amrwbplus_debug_fops);
+		if (IS_ERR(audio->dentry))
+			pr_debug("debugfs_create_file failed\n");
+	}
+}
+#else
+static void config_debug_fs(struct q6audio_aio *audio)
+{
+}
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+					void *arg)
+{
+	struct asm_amrwbplus_cfg q6_amrwbplus_cfg;
+	struct msm_audio_amrwbplus_config_v2 *amrwbplus_drv_config;
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_err("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+			audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+			audio->pcm_cfg.sample_rate,
+			audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		amrwbplus_drv_config =
+		(struct msm_audio_amrwbplus_config_v2 *)audio->codec_cfg;
+
+		q6_amrwbplus_cfg.size_bytes     =
+			amrwbplus_drv_config->size_bytes;
+		q6_amrwbplus_cfg.version        =
+			amrwbplus_drv_config->version;
+		q6_amrwbplus_cfg.num_channels   =
+			amrwbplus_drv_config->num_channels;
+		q6_amrwbplus_cfg.amr_band_mode  =
+			amrwbplus_drv_config->amr_band_mode;
+		q6_amrwbplus_cfg.amr_dtx_mode   =
+			amrwbplus_drv_config->amr_dtx_mode;
+		q6_amrwbplus_cfg.amr_frame_fmt  =
+			amrwbplus_drv_config->amr_frame_fmt;
+		q6_amrwbplus_cfg.amr_lsf_idx    =
+			amrwbplus_drv_config->amr_lsf_idx;
+
+		rc = q6asm_media_format_block_amrwbplus(audio->ac,
+							&q6_amrwbplus_cfg);
+		if (rc < 0) {
+			pr_err("q6asm_media_format_block_amrwb+ failed...\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("%s:AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+			audio->ac->session,
+			audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+			break;
+		}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_AMRWBPLUS_CONFIG_V2: {
+		if ((audio) && (arg) && (audio->codec_cfg)) {
+			if (copy_to_user((void *)arg, audio->codec_cfg,
+				sizeof(struct msm_audio_amrwbplus_config_v2))) {
+				rc = -EFAULT;
+				pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2 failed\n",
+					__func__);
+				break;
+			}
+			} else {
+				pr_err("%s: wb+ config v2 invalid parameters\n"
+					, __func__);
+				rc = -EFAULT;
+				break;
+			}
+		break;
+	}
+	case AUDIO_SET_AMRWBPLUS_CONFIG_V2: {
+		if ((audio) && (arg) && (audio->codec_cfg)) {
+			if (copy_from_user(audio->codec_cfg, (void *)arg,
+				sizeof(struct msm_audio_amrwbplus_config_v2))) {
+				rc = -EFAULT;
+				pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2 failed\n",
+					__func__);
+				break;
+			}
+			} else {
+				pr_err("%s: wb+ config invalid parameters\n",
+					__func__);
+				rc = -EFAULT;
+				break;
+			}
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+		break;
+	}
+	}
+	return rc;
+}
+#ifdef CONFIG_COMPAT
+struct msm_audio_amrwbplus_config_v2_32 {
+	u32 size_bytes;
+	u32 version;
+	u32 num_channels;
+	u32 amr_band_mode;
+	u32 amr_dtx_mode;
+	u32 amr_frame_fmt;
+	u32 amr_lsf_idx;
+};
+
+enum {
+	AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 = _IOR(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+2),
+		struct msm_audio_amrwbplus_config_v2_32),
+	AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 = _IOW(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+3),
+		struct msm_audio_amrwbplus_config_v2_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+					unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_AMRWBPLUS_CONFIG_V2_32: {
+		if (audio && arg && (audio->codec_cfg)) {
+			struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
+			struct msm_audio_amrwbplus_config_v2_32
+						amrwbplus_config_32;
+
+			memset(&amrwbplus_config_32, 0,
+					sizeof(amrwbplus_config_32));
+
+			amrwbplus_config =
+				(struct msm_audio_amrwbplus_config_v2 *)
+				audio->codec_cfg;
+			amrwbplus_config_32.size_bytes =
+					amrwbplus_config->size_bytes;
+			amrwbplus_config_32.version =
+					amrwbplus_config->version;
+			amrwbplus_config_32.num_channels =
+					amrwbplus_config->num_channels;
+			amrwbplus_config_32.amr_band_mode =
+					amrwbplus_config->amr_band_mode;
+			amrwbplus_config_32.amr_dtx_mode =
+					amrwbplus_config->amr_dtx_mode;
+			amrwbplus_config_32.amr_frame_fmt =
+					amrwbplus_config->amr_frame_fmt;
+			amrwbplus_config_32.amr_lsf_idx =
+					amrwbplus_config->amr_lsf_idx;
+
+			if (copy_to_user((void *)arg, &amrwbplus_config_32,
+				sizeof(amrwbplus_config_32))) {
+				rc = -EFAULT;
+				pr_err("%s: copy_to_user for AUDIO_GET_AMRWBPLUS_CONFIG_V2_32 failed\n"
+					, __func__);
+			}
+		} else {
+			pr_err("%s: wb+ Get config v2 invalid parameters\n"
+				, __func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_AMRWBPLUS_CONFIG_V2_32: {
+		if ((audio) && (arg) && (audio->codec_cfg)) {
+			struct msm_audio_amrwbplus_config_v2 *amrwbplus_config;
+			struct msm_audio_amrwbplus_config_v2_32
+							amrwbplus_config_32;
+
+			if (copy_from_user(&amrwbplus_config_32, (void *)arg,
+			sizeof(struct msm_audio_amrwbplus_config_v2_32))) {
+				rc = -EFAULT;
+				pr_err("%s: copy_from_user for AUDIO_SET_AMRWBPLUS_CONFIG_V2_32 failed\n"
+					, __func__);
+				break;
+			}
+			amrwbplus_config =
+			 (struct msm_audio_amrwbplus_config_v2 *)
+						audio->codec_cfg;
+			amrwbplus_config->size_bytes =
+					amrwbplus_config_32.size_bytes;
+			amrwbplus_config->version =
+					amrwbplus_config_32.version;
+			amrwbplus_config->num_channels =
+					amrwbplus_config_32.num_channels;
+			amrwbplus_config->amr_band_mode =
+					amrwbplus_config_32.amr_band_mode;
+			amrwbplus_config->amr_dtx_mode =
+					amrwbplus_config_32.amr_dtx_mode;
+			amrwbplus_config->amr_frame_fmt =
+					amrwbplus_config_32.amr_frame_fmt;
+			amrwbplus_config->amr_lsf_idx =
+					amrwbplus_config_32.amr_lsf_idx;
+		} else {
+			pr_err("%s: wb+ config invalid parameters\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->codec_cfg =
+	kzalloc(sizeof(struct msm_audio_amrwbplus_config_v2), GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_amrwbplus_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_amrwbplus_ws_mgr;
+
+	audio->ac =
+	q6asm_audio_client_alloc((app_cb) q6_audio_cb, (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					FORMAT_AMR_WB_PLUS);
+		if (rc < 0) {
+			pr_err("amrwbplus NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_AMR_WB_PLUS);
+		if (rc < 0) {
+			pr_err("wb+ T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("audio_amrwbplus Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+	config_debug_fs(audio);
+	pr_debug("%s: AMRWBPLUS dec success mode[%d]session[%d]\n", __func__,
+		audio->feedback,
+		audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_amrwbplus_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_amrwbplus_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_amrwbplus",
+	.fops = &audio_amrwbplus_fops,
+};
+
+static int __init audio_amrwbplus_init(void)
+{
+	int ret = misc_register(&audio_amrwbplus_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_amrwbplus_misc.this_device, true);
+	audio_amrwbplus_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_amrwbplus_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_amrwbplus_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_ape.c b/drivers/misc/qcom/qdsp6v2/audio_ape.c
new file mode 100644
index 0000000..d7dc064
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_ape.c
@@ -0,0 +1,359 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_ape.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_ape_misc;
+static struct ws_mgr audio_ape_ws_mgr;
+
+static const struct file_operations audio_ape_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+static struct dentry *config_debugfs_create_file(const char *name, void *data)
+{
+	return debugfs_create_file(name, S_IFREG | 0444,
+			NULL, (void *)data, &audio_ape_debug_fops);
+}
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_ape_cfg ape_cfg;
+		struct msm_audio_ape_config *ape_config;
+
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
+		ape_cfg.compatible_version = ape_config->compatibleVersion;
+		ape_cfg.compression_level = ape_config->compressionLevel;
+		ape_cfg.format_flags = ape_config->formatFlags;
+		ape_cfg.blocks_per_frame = ape_config->blocksPerFrame;
+		ape_cfg.final_frame_blocks = ape_config->finalFrameBlocks;
+		ape_cfg.total_frames = ape_config->totalFrames;
+		ape_cfg.bits_per_sample = ape_config->bitsPerSample;
+		ape_cfg.num_channels = ape_config->numChannels;
+		ape_cfg.sample_rate = ape_config->sampleRate;
+		ape_cfg.seek_table_present = ape_config->seekTablePresent;
+		pr_debug("%s: compatibleVersion %d compressionLevel %d formatFlags %d blocksPerFrame %d finalFrameBlocks %d totalFrames %d bitsPerSample %d numChannels %d sampleRate %d seekTablePresent %d\n",
+				__func__, ape_config->compatibleVersion,
+				ape_config->compressionLevel,
+				ape_config->formatFlags,
+				ape_config->blocksPerFrame,
+				ape_config->finalFrameBlocks,
+				ape_config->totalFrames,
+				ape_config->bitsPerSample,
+				ape_config->numChannels,
+				ape_config->sampleRate,
+				ape_config->seekTablePresent);
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_ape(audio->ac, &ape_cfg,
+							audio->ac->stream_id);
+		if (rc < 0) {
+			pr_err("cmd media format block failed\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_APE_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_ape_config))) {
+			pr_err("%s:copy_to_user for AUDIO_GET_APE_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_APE_CONFIG: {
+		if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_ape_config))) {
+			pr_err("%s:copy_from_user for AUDIO_SET_APE_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_ape_config_32 {
+	u16 compatibleVersion;
+	u16 compressionLevel;
+	u32 formatFlags;
+	u32 blocksPerFrame;
+	u32 finalFrameBlocks;
+	u32 totalFrames;
+	u16 bitsPerSample;
+	u16 numChannels;
+	u32 sampleRate;
+	u32 seekTablePresent;
+
+};
+
+enum {
+	AUDIO_GET_APE_CONFIG_32 =  _IOR(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config_32),
+	AUDIO_SET_APE_CONFIG_32 =  _IOW(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_APE_CONFIG_32: {
+		struct msm_audio_ape_config *ape_config;
+		struct msm_audio_ape_config_32 ape_config_32;
+
+		memset(&ape_config_32, 0, sizeof(ape_config_32));
+
+		ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
+		ape_config_32.compatibleVersion = ape_config->compatibleVersion;
+		ape_config_32.compressionLevel =
+				ape_config->compressionLevel;
+		ape_config_32.formatFlags = ape_config->formatFlags;
+		ape_config_32.blocksPerFrame = ape_config->blocksPerFrame;
+		ape_config_32.finalFrameBlocks = ape_config->finalFrameBlocks;
+		ape_config_32.totalFrames = ape_config->totalFrames;
+		ape_config_32.bitsPerSample = ape_config->bitsPerSample;
+		ape_config_32.numChannels = ape_config->numChannels;
+		ape_config_32.sampleRate = ape_config->sampleRate;
+		ape_config_32.seekTablePresent = ape_config->seekTablePresent;
+
+		if (copy_to_user((void *)arg, &ape_config_32,
+			sizeof(ape_config_32))) {
+			pr_err("%s: copy_to_user for GET_APE_CONFIG_32 failed\n",
+				 __func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_APE_CONFIG_32: {
+		struct msm_audio_ape_config *ape_config;
+		struct msm_audio_ape_config_32 ape_config_32;
+
+		if (copy_from_user(&ape_config_32, (void *)arg,
+			sizeof(ape_config_32))) {
+			pr_err("%s: copy_from_user for SET_APE_CONFIG_32 failed\n"
+				, __func__);
+			rc = -EFAULT;
+			break;
+		}
+		ape_config = (struct msm_audio_ape_config *)audio->codec_cfg;
+		ape_config->compatibleVersion = ape_config_32.compatibleVersion;
+		ape_config->compressionLevel =
+				ape_config_32.compressionLevel;
+		ape_config->formatFlags = ape_config_32.formatFlags;
+		ape_config->blocksPerFrame = ape_config_32.blocksPerFrame;
+		ape_config->finalFrameBlocks = ape_config_32.finalFrameBlocks;
+		ape_config->totalFrames = ape_config_32.totalFrames;
+		ape_config->bitsPerSample = ape_config_32.bitsPerSample;
+		ape_config->numChannels = ape_config_32.numChannels;
+		ape_config->sampleRate = ape_config_32.sampleRate;
+		ape_config->seekTablePresent = ape_config_32.seekTablePresent;
+
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_ape_" + 5];
+
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+	if (!audio)
+		return -ENOMEM;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_ape_config),
+					GFP_KERNEL);
+	if (!audio->codec_cfg) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_ape_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_ape_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_APE);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open APE decoder, expected frames is always 1*/
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_APE);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+	snprintf(name, sizeof(name), "msm_ape_%04x", audio->ac->session);
+	audio->dentry = config_debugfs_create_file(name, (void *)audio);
+
+	if (IS_ERR_OR_NULL(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+	pr_debug("%s:apedec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_ape_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_ape_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_ape",
+	.fops = &audio_ape_fops,
+};
+
+static int __init audio_ape_init(void)
+{
+	int ret = misc_register(&audio_ape_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_ape_misc.this_device, true);
+	audio_ape_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_ape_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_ape_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_evrc.c b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
new file mode 100644
index 0000000..8776231
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_evrc.c
@@ -0,0 +1,184 @@
+/* evrc audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_evrc_misc;
+static struct ws_mgr audio_evrc_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_evrc_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+						audio->ac->session,
+						audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_evrc_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_evrc_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_evrc_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_EVRC);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_EVRC);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_evrc_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_evrc_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_evrc_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_evrc_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_evrc",
+	.fops = &audio_evrc_fops,
+};
+
+static int __init audio_evrc_init(void)
+{
+	int ret = misc_register(&audio_evrc_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_evrc_misc.this_device, true);
+	audio_evrc_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_evrc_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_evrc_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c b/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c
new file mode 100644
index 0000000..24f87e4
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_g711alaw.c
@@ -0,0 +1,396 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_g711_dec.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_g711alaw_misc;
+static struct ws_mgr audio_g711_ws_mgr;
+
+static const struct file_operations audio_g711_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+
+static struct dentry *config_debugfs_create_file(const char *name, void *data)
+{
+	return debugfs_create_file(name, S_IFREG | 0444,
+				NULL, (void *)data, &audio_g711_debug_fops);
+}
+
+static int g711_channel_map(u8 *channel_mapping, uint32_t channels);
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_g711_dec_cfg g711_dec_cfg;
+		struct msm_audio_g711_dec_config *g711_dec_config;
+		u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+		memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+		memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg));
+
+		if (g711_channel_map(channel_mapping,
+			audio->pcm_cfg.channel_count)) {
+			pr_err("%s: setting channel map failed %d\n",
+					__func__, audio->pcm_cfg.channel_count);
+		}
+
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count,
+					16, /*bits per sample*/
+					false, false, channel_mapping);
+			if (rc < 0) {
+				pr_err("%s: pcm output block config failed rc=%d\n",
+						 __func__, rc);
+				break;
+			}
+		}
+		g711_dec_config =
+			(struct msm_audio_g711_dec_config *)audio->codec_cfg;
+		g711_dec_cfg.sample_rate = g711_dec_config->sample_rate;
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg,
+							audio->ac->stream_id);
+		if (rc < 0) {
+			pr_err("%s: cmd media format block failed rc=%d\n",
+				__func__, rc);
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s: Audio Start procedure failed rc=%d\n",
+						__func__, rc);
+			break;
+		}
+		pr_debug("%s: AUDIO_START success enable[%d]\n",
+					 __func__, audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_G711_DEC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_g711_dec_config))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_DEC_CONFIG: {
+		if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_g711_dec_config))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default: {
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n",
+				__func__, rc, cmd);
+		break;
+	}
+	}
+	return  rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_g711_dec_config_32 {
+	u32 sample_rate;
+};
+
+enum {
+	AUDIO_SET_G711_DEC_CONFIG_32 =  _IOW(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32),
+	AUDIO_GET_G711_DEC_CONFIG_32 =  _IOR(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_G711_DEC_CONFIG_32: {
+		struct msm_audio_g711_dec_config *g711_dec_config;
+		struct msm_audio_g711_dec_config_32 g711_dec_config_32;
+
+		memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
+
+		g711_dec_config =
+			(struct msm_audio_g711_dec_config *)audio->codec_cfg;
+		g711_dec_config_32.sample_rate = g711_dec_config->sample_rate;
+
+		if (copy_to_user((void *)arg, &g711_dec_config_32,
+			sizeof(g711_dec_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG_32 failed\n",
+				 __func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_DEC_CONFIG_32: {
+		struct msm_audio_g711_dec_config *g711_dec_config;
+		struct msm_audio_g711_dec_config_32 g711_dec_config_32;
+
+		memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
+
+		if (copy_from_user(&g711_dec_config_32, (void *)arg,
+			sizeof(g711_dec_config_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+
+		g711_dec_config =
+			(struct msm_audio_g711_dec_config *)audio->codec_cfg;
+		g711_dec_config->sample_rate = g711_dec_config_32.sample_rate;
+
+		break;
+	}
+	default: {
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n",
+				__func__, rc, cmd);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_g711_" + 5];
+
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (!audio)
+		return -ENOMEM;
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config),
+					GFP_KERNEL);
+	if (!audio->codec_cfg) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_g711alaw_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_g711_ws_mgr;
+
+	init_waitqueue_head(&audio->event_wait);
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+					 __func__);
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */ /*foramt:G711_ALAW*/
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_G711_ALAW_FS);
+		if (rc < 0) {
+			pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc);
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open G711 decoder, expected frames is always 1*/
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_G711_ALAW_FS);
+		if (rc < 0) {
+			pr_err("%s: T mode Open failed rc=%d\n", __func__, rc);
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("%s: %d mode is not supported mode\n",
+				__func__, file->f_mode);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session);
+	audio->dentry = config_debugfs_create_file(name, (void *)audio);
+
+	if (IS_ERR_OR_NULL(audio->dentry))
+		pr_debug("%s: debugfs_create_file failed\n", __func__);
+	pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static int g711_channel_map(u8 *channel_mapping, uint32_t channels)
+{
+	u8 *lchannel_mapping;
+
+	lchannel_mapping = channel_mapping;
+	pr_debug("%s: channels passed: %d\n", __func__, channels);
+	if (channels == 1)  {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+	} else if (channels == 2) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+	} else if (channels == 3) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+	} else if (channels == 4) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_CS;
+	} else if (channels == 5) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+	} else if (channels == 6) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+		lchannel_mapping[5] = PCM_CHANNEL_LFE;
+	} else if (channels == 7) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+		lchannel_mapping[5] = PCM_CHANNEL_CS;
+		lchannel_mapping[6] = PCM_CHANNEL_LFE;
+	} else if (channels == 8) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FLC;
+		lchannel_mapping[2] = PCM_CHANNEL_FRC;
+		lchannel_mapping[3] = PCM_CHANNEL_FL;
+		lchannel_mapping[4] = PCM_CHANNEL_FR;
+		lchannel_mapping[5] = PCM_CHANNEL_LS;
+		lchannel_mapping[6] = PCM_CHANNEL_RS;
+		lchannel_mapping[7] = PCM_CHANNEL_LFE;
+	} else {
+		pr_err("%s: ERROR.unsupported num_ch = %u\n",
+				__func__, channels);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct file_operations audio_g711_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.compat_ioctl = audio_compat_ioctl,
+	.fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_g711alaw_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_g711alaw",
+	.fops = &audio_g711_fops,
+};
+
+static int __init audio_g711alaw_init(void)
+{
+	int ret = misc_register(&audio_g711alaw_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_g711alaw_misc.this_device, true);
+	audio_g711_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_g711_ws_mgr.ws_lock);
+
+	return ret;
+}
+static void __exit audio_g711alaw_exit(void)
+{
+	misc_deregister(&audio_g711alaw_misc);
+	mutex_destroy(&audio_g711_ws_mgr.ws_lock);
+}
+
+device_initcall(audio_g711alaw_init);
+__exitcall(audio_g711alaw_exit);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c b/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c
new file mode 100644
index 0000000..10d3680
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_g711mlaw.c
@@ -0,0 +1,396 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_g711_dec.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_g711mlaw_misc;
+static struct ws_mgr audio_g711_ws_mgr;
+
+static const struct file_operations audio_g711_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+
+static struct dentry *config_debugfs_create_file(const char *name, void *data)
+{
+	return debugfs_create_file(name, S_IFREG | 0444,
+				NULL, (void *)data, &audio_g711_debug_fops);
+}
+
+static int g711_channel_map(u8 *channel_mapping, uint32_t channels);
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_g711_dec_cfg g711_dec_cfg;
+		struct msm_audio_g711_dec_config *g711_dec_config;
+		u8 channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+
+		memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+		memset(&g711_dec_cfg, 0, sizeof(g711_dec_cfg));
+
+		if (g711_channel_map(channel_mapping,
+			audio->pcm_cfg.channel_count)) {
+			pr_err("%s: setting channel map failed %d\n",
+					__func__, audio->pcm_cfg.channel_count);
+		}
+
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count,
+					16, /*bits per sample*/
+					false, false, channel_mapping);
+			if (rc < 0) {
+				pr_err("%s: pcm output block config failed rc=%d\n",
+						 __func__, rc);
+				break;
+			}
+		}
+		g711_dec_config =
+			(struct msm_audio_g711_dec_config *)audio->codec_cfg;
+		g711_dec_cfg.sample_rate = g711_dec_config->sample_rate;
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_g711(audio->ac, &g711_dec_cfg,
+							audio->ac->stream_id);
+		if (rc < 0) {
+			pr_err("%s: cmd media format block failed rc=%d\n",
+				__func__, rc);
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s: Audio Start procedure failed rc=%d\n",
+						__func__, rc);
+			break;
+		}
+		pr_debug("%s: AUDIO_START success enable[%d]\n",
+						__func__, audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_G711_DEC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_g711_dec_config))) {
+			pr_err("%s: AUDIO_GET_G711_DEC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_DEC_CONFIG: {
+		if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_g711_dec_config))) {
+			pr_err("%s: AUDIO_SET_G711_DEC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default: {
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("%s: Failed in audio_aio_ioctl: %d cmd=%d\n",
+				__func__, rc, cmd);
+		break;
+	}
+	}
+	return  rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_g711_dec_config_32 {
+	u32 sample_rate;
+};
+
+enum {
+	AUDIO_SET_G711_DEC_CONFIG_32 =  _IOW(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config_32),
+	AUDIO_GET_G711_DEC_CONFIG_32 =  _IOR(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_G711_DEC_CONFIG_32: {
+		struct msm_audio_g711_dec_config *g711_dec_config;
+		struct msm_audio_g711_dec_config_32 g711_dec_config_32;
+
+		memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
+
+		g711_dec_config =
+			(struct msm_audio_g711_dec_config *)audio->codec_cfg;
+		g711_dec_config_32.sample_rate = g711_dec_config->sample_rate;
+
+		if (copy_to_user((void *)arg, &g711_dec_config_32,
+			sizeof(g711_dec_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_G711_DEC_CONFIG failed\n",
+				 __func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_DEC_CONFIG_32: {
+		struct msm_audio_g711_dec_config *g711_dec_config;
+		struct msm_audio_g711_dec_config_32 g711_dec_config_32;
+
+		memset(&g711_dec_config_32, 0, sizeof(g711_dec_config_32));
+
+		if (copy_from_user(&g711_dec_config_32, (void *)arg,
+			sizeof(g711_dec_config_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_G711_DEC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		g711_dec_config =
+			(struct msm_audio_g711_dec_config *)audio->codec_cfg;
+		g711_dec_config->sample_rate = g711_dec_config_32.sample_rate;
+
+		break;
+	}
+	default: {
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("%s: Failed in audio_aio_compat_ioctl: %d cmd=%d\n",
+				__func__, rc, cmd);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_g711_" + 5];
+
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (!audio)
+		return -ENOMEM;
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_g711_dec_config),
+					GFP_KERNEL);
+	if (!audio->codec_cfg) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_g711mlaw_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_g711_ws_mgr;
+
+	init_waitqueue_head(&audio->event_wait);
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+					__func__);
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */ /*foramt:G711_ALAW*/
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_G711_MLAW_FS);
+		if (rc < 0) {
+			pr_err("%s: NT mode Open failed rc=%d\n", __func__, rc);
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open G711 decoder, expected frames is always 1*/
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_G711_MLAW_FS);
+		if (rc < 0) {
+			pr_err("%s: T mode Open failed rc=%d\n", __func__, rc);
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("%s: %d mode is not supported\n", __func__,
+					file->f_mode);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	snprintf(name, sizeof(name), "msm_g711_%04x", audio->ac->session);
+	audio->dentry = config_debugfs_create_file(name, (void *)audio);
+
+	if (IS_ERR_OR_NULL(audio->dentry))
+		pr_debug("%s: debugfs_create_file failed\n", __func__);
+	pr_debug("%s: g711dec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static int g711_channel_map(u8 *channel_mapping, uint32_t channels)
+{
+	u8 *lchannel_mapping;
+
+	lchannel_mapping = channel_mapping;
+	pr_debug("%s: channels passed: %d\n", __func__, channels);
+	if (channels == 1)  {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+	} else if (channels == 2) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+	} else if (channels == 3) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+	} else if (channels == 4) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_CS;
+	} else if (channels == 5) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+	} else if (channels == 6) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+		lchannel_mapping[5] = PCM_CHANNEL_LFE;
+	} else if (channels == 7) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FL;
+		lchannel_mapping[2] = PCM_CHANNEL_FR;
+		lchannel_mapping[3] = PCM_CHANNEL_LS;
+		lchannel_mapping[4] = PCM_CHANNEL_RS;
+		lchannel_mapping[5] = PCM_CHANNEL_CS;
+		lchannel_mapping[6] = PCM_CHANNEL_LFE;
+	} else if (channels == 8) {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+		lchannel_mapping[1] = PCM_CHANNEL_FLC;
+		lchannel_mapping[2] = PCM_CHANNEL_FRC;
+		lchannel_mapping[3] = PCM_CHANNEL_FL;
+		lchannel_mapping[4] = PCM_CHANNEL_FR;
+		lchannel_mapping[5] = PCM_CHANNEL_LS;
+		lchannel_mapping[6] = PCM_CHANNEL_RS;
+		lchannel_mapping[7] = PCM_CHANNEL_LFE;
+	} else {
+		pr_err("%s: ERROR.unsupported num_ch = %u\n",
+				__func__, channels);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static const struct file_operations audio_g711_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.compat_ioctl = audio_compat_ioctl,
+	.fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_g711mlaw_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_g711mlaw",
+	.fops = &audio_g711_fops,
+};
+
+static int __init audio_g711mlaw_init(void)
+{
+	int ret = misc_register(&audio_g711mlaw_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_g711mlaw_misc.this_device, true);
+	audio_g711_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_g711_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+static void __exit audio_g711mlaw_exit(void)
+{
+	misc_deregister(&audio_g711mlaw_misc);
+	mutex_destroy(&audio_g711_ws_mgr.ws_lock);
+}
+
+device_initcall(audio_g711mlaw_init);
+__exitcall(audio_g711mlaw_exit);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
new file mode 100644
index 0000000..c1d792b
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_hwacc_effects.c
@@ -0,0 +1,787 @@
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_audio.h>
+#include <linux/compat.h>
+#include "q6audio_common.h"
+#include "audio_utils_aio.h"
+#include <sound/msm-audio-effects-q6-v2.h>
+#include <sound/msm-dts-eagle.h>
+
+#define MAX_CHANNELS_SUPPORTED		8
+#define WAIT_TIMEDOUT_DURATION_SECS	1
+
+struct q6audio_effects {
+	wait_queue_head_t		read_wait;
+	wait_queue_head_t		write_wait;
+
+	struct audio_client             *ac;
+	struct msm_hwacc_effects_config  config;
+
+	atomic_t			in_count;
+	atomic_t			out_count;
+
+	int				opened;
+	int				started;
+	int				buf_alloc;
+	struct msm_nt_eff_all_config audio_effects;
+};
+
+static void audio_effects_init_pp(struct audio_client *ac)
+{
+	int ret = 0;
+	struct asm_softvolume_params softvol = {
+		.period = SOFT_VOLUME_PERIOD,
+		.step = SOFT_VOLUME_STEP,
+		.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+	};
+
+	if (!ac) {
+		pr_err("%s: audio client null to init pp\n", __func__);
+		return;
+	}
+	switch (ac->topology) {
+	case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+
+		ret = q6asm_set_softvolume_v2(ac, &softvol,
+					      SOFT_VOLUME_INSTANCE_1);
+		if (ret < 0)
+			pr_err("%s: Send SoftVolume1 Param failed ret=%d\n",
+				__func__, ret);
+		ret = q6asm_set_softvolume_v2(ac, &softvol,
+					      SOFT_VOLUME_INSTANCE_2);
+		if (ret < 0)
+			pr_err("%s: Send SoftVolume2 Param failed ret=%d\n",
+				 __func__, ret);
+
+		msm_dts_eagle_init_master_module(ac);
+
+		break;
+	default:
+		ret = q6asm_set_softvolume_v2(ac, &softvol,
+					      SOFT_VOLUME_INSTANCE_1);
+		if (ret < 0)
+			pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+				__func__, ret);
+		break;
+	}
+}
+
+static void audio_effects_deinit_pp(struct audio_client *ac)
+{
+	if (!ac) {
+		pr_err("%s: audio client null to deinit pp\n", __func__);
+		return;
+	}
+	switch (ac->topology) {
+	case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+		msm_dts_eagle_deinit_master_module(ac);
+		break;
+	default:
+		break;
+	}
+}
+
+static void audio_effects_event_handler(uint32_t opcode, uint32_t token,
+				 uint32_t *payload,  void *priv)
+{
+	struct q6audio_effects *effects;
+
+	if (!payload || !priv) {
+		pr_err("%s: invalid data to handle events, payload: %pK, priv: %pK\n",
+			__func__, payload, priv);
+		return;
+	}
+
+	effects = (struct q6audio_effects *)priv;
+	switch (opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2: {
+		atomic_inc(&effects->out_count);
+		wake_up(&effects->write_wait);
+		break;
+	}
+	case ASM_DATA_EVENT_READ_DONE_V2: {
+		atomic_inc(&effects->in_count);
+		wake_up(&effects->read_wait);
+		break;
+	}
+	case APR_BASIC_RSP_RESULT: {
+		pr_debug("%s: APR_BASIC_RSP_RESULT Cmd[0x%x] Status[0x%x]\n",
+			 __func__, payload[0], payload[1]);
+		switch (payload[0]) {
+		case ASM_SESSION_CMD_RUN_V2:
+			pr_debug("ASM_SESSION_CMD_RUN_V2\n");
+			break;
+		default:
+			pr_debug("%s: Payload = [0x%x] stat[0x%x]\n",
+				 __func__, payload[0], payload[1]);
+			break;
+		}
+		break;
+	}
+	default:
+		pr_debug("%s: Unhandled Event 0x%x token = 0x%x\n",
+			 __func__, opcode, token);
+		break;
+	}
+}
+
+static int audio_effects_shared_ioctl(struct file *file, unsigned int cmd,
+				      unsigned long arg)
+{
+	struct q6audio_effects *effects = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s: AUDIO_START\n", __func__);
+
+		rc = q6asm_open_read_write_v2(effects->ac,
+					FORMAT_LINEAR_PCM,
+					FORMAT_MULTI_CHANNEL_LINEAR_PCM,
+					effects->config.meta_mode_enabled,
+					effects->config.output.bits_per_sample,
+					true /*overwrite topology*/,
+					ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER);
+		if (rc < 0) {
+			pr_err("%s: Open failed for hw accelerated effects:rc=%d\n",
+				__func__, rc);
+			rc = -EINVAL;
+			goto ioctl_fail;
+		}
+		effects->opened = 1;
+
+		pr_debug("%s: dec buf size: %d, num_buf: %d, enc buf size: %d, num_buf: %d\n",
+			 __func__, effects->config.output.buf_size,
+			 effects->config.output.num_buf,
+			 effects->config.input.buf_size,
+			 effects->config.input.num_buf);
+		rc = q6asm_audio_client_buf_alloc_contiguous(IN, effects->ac,
+					effects->config.output.buf_size,
+					effects->config.output.num_buf);
+		if (rc < 0) {
+			pr_err("%s: Write buffer Allocation failed rc = %d\n",
+				__func__, rc);
+			rc = -ENOMEM;
+			goto ioctl_fail;
+		}
+		atomic_set(&effects->in_count, effects->config.input.num_buf);
+		rc = q6asm_audio_client_buf_alloc_contiguous(OUT, effects->ac,
+					effects->config.input.buf_size,
+					effects->config.input.num_buf);
+		if (rc < 0) {
+			pr_err("%s: Read buffer Allocation failed rc = %d\n",
+				__func__, rc);
+			rc = -ENOMEM;
+			goto readbuf_fail;
+		}
+		atomic_set(&effects->out_count, effects->config.output.num_buf);
+		effects->buf_alloc = 1;
+
+		pr_debug("%s: enc: sample_rate: %d, num_channels: %d\n",
+			 __func__, effects->config.input.sample_rate,
+			effects->config.input.num_channels);
+		rc = q6asm_enc_cfg_blk_pcm(effects->ac,
+					   effects->config.input.sample_rate,
+					   effects->config.input.num_channels);
+		if (rc < 0) {
+			pr_err("%s: pcm read block config failed\n", __func__);
+			rc = -EINVAL;
+			goto cfg_fail;
+		}
+		pr_debug("%s: dec: sample_rate: %d, num_channels: %d, bit_width: %d\n",
+			 __func__, effects->config.output.sample_rate,
+			effects->config.output.num_channels,
+			effects->config.output.bits_per_sample);
+		rc = q6asm_media_format_block_pcm_format_support(
+				effects->ac, effects->config.output.sample_rate,
+				effects->config.output.num_channels,
+				effects->config.output.bits_per_sample);
+		if (rc < 0) {
+			pr_err("%s: pcm write format block config failed\n",
+				__func__);
+			rc = -EINVAL;
+			goto cfg_fail;
+		}
+
+		audio_effects_init_pp(effects->ac);
+
+		rc = q6asm_run(effects->ac, 0x00, 0x00, 0x00);
+		if (!rc)
+			effects->started = 1;
+		else {
+			effects->started = 0;
+			pr_err("%s: ASM run state failed\n", __func__);
+		}
+		break;
+	}
+	case AUDIO_EFFECTS_WRITE: {
+		char *bufptr = NULL;
+		uint32_t idx = 0;
+		uint32_t size = 0;
+
+		if (!effects->started) {
+			rc = -EFAULT;
+			goto ioctl_fail;
+		}
+
+		rc = wait_event_timeout(effects->write_wait,
+					atomic_read(&effects->out_count),
+					WAIT_TIMEDOUT_DURATION_SECS * HZ);
+		if (!rc) {
+			pr_err("%s: write wait_event_timeout\n", __func__);
+			rc = -EFAULT;
+			goto ioctl_fail;
+		}
+		if (!atomic_read(&effects->out_count)) {
+			pr_err("%s: pcm stopped out_count 0\n", __func__);
+			rc = -EFAULT;
+			goto ioctl_fail;
+		}
+
+		bufptr = q6asm_is_cpu_buf_avail(IN, effects->ac, &size, &idx);
+		if (bufptr) {
+			if ((effects->config.buf_cfg.output_len > size) ||
+				copy_from_user(bufptr, (void *)arg,
+					effects->config.buf_cfg.output_len)) {
+				rc = -EFAULT;
+				goto ioctl_fail;
+			}
+			rc = q6asm_write(effects->ac,
+					 effects->config.buf_cfg.output_len,
+					 0, 0, NO_TIMESTAMP);
+			if (rc < 0) {
+				rc = -EFAULT;
+				goto ioctl_fail;
+			}
+			atomic_dec(&effects->out_count);
+		} else {
+			pr_err("%s: AUDIO_EFFECTS_WRITE: Buffer dropped\n",
+				__func__);
+		}
+		break;
+	}
+	case AUDIO_EFFECTS_READ: {
+		char *bufptr = NULL;
+		uint32_t idx = 0;
+		uint32_t size = 0;
+
+		if (!effects->started) {
+			rc = -EFAULT;
+			goto ioctl_fail;
+		}
+
+		atomic_set(&effects->in_count, 0);
+
+		q6asm_read_v2(effects->ac, effects->config.buf_cfg.input_len);
+		/* Read might fail initially, don't error out */
+		if (rc < 0)
+			pr_err("%s: read failed\n", __func__);
+
+		rc = wait_event_timeout(effects->read_wait,
+					atomic_read(&effects->in_count),
+					WAIT_TIMEDOUT_DURATION_SECS * HZ);
+		if (!rc) {
+			pr_err("%s: read wait_event_timeout\n", __func__);
+			rc = -EFAULT;
+			goto ioctl_fail;
+		}
+		if (!atomic_read(&effects->in_count)) {
+			pr_err("%s: pcm stopped in_count 0\n", __func__);
+			rc = -EFAULT;
+			goto ioctl_fail;
+		}
+
+		bufptr = q6asm_is_cpu_buf_avail(OUT, effects->ac, &size, &idx);
+		if (bufptr) {
+			if (!((void *)arg)) {
+				rc = -EFAULT;
+				goto ioctl_fail;
+			}
+			if ((effects->config.buf_cfg.input_len > size) ||
+				copy_to_user((void *)arg, bufptr,
+					  effects->config.buf_cfg.input_len)) {
+				rc = -EFAULT;
+				goto ioctl_fail;
+			}
+		}
+		break;
+	}
+	default:
+		pr_err("%s: Invalid effects config module\n", __func__);
+		rc = -EINVAL;
+		break;
+	}
+ioctl_fail:
+	return rc;
+readbuf_fail:
+	q6asm_audio_client_buf_free_contiguous(IN,
+					effects->ac);
+	return rc;
+cfg_fail:
+	q6asm_audio_client_buf_free_contiguous(IN,
+					effects->ac);
+	q6asm_audio_client_buf_free_contiguous(OUT,
+					effects->ac);
+	effects->buf_alloc = 0;
+	return rc;
+}
+
+static long audio_effects_set_pp_param(struct q6audio_effects *effects,
+				long *values)
+{
+	int rc = 0;
+	int effects_module = values[0];
+
+	switch (effects_module) {
+	case VIRTUALIZER_MODULE:
+		pr_debug("%s: VIRTUALIZER_MODULE\n", __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology))
+			msm_audio_effects_virtualizer_handler(
+				effects->ac,
+				&(effects->audio_effects.virtualizer),
+				(long *)&values[1]);
+		break;
+	case REVERB_MODULE:
+		pr_debug("%s: REVERB_MODULE\n", __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology))
+			msm_audio_effects_reverb_handler(effects->ac,
+				 &(effects->audio_effects.reverb),
+				 (long *)&values[1]);
+		break;
+	case BASS_BOOST_MODULE:
+		pr_debug("%s: BASS_BOOST_MODULE\n", __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology))
+			msm_audio_effects_bass_boost_handler(
+				effects->ac,
+				&(effects->audio_effects.bass_boost),
+				(long *)&values[1]);
+		break;
+	case PBE_MODULE:
+		pr_debug("%s: PBE_MODULE\n", __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology))
+			msm_audio_effects_pbe_handler(
+				effects->ac,
+				&(effects->audio_effects.pbe),
+				(long *)&values[1]);
+		break;
+	case EQ_MODULE:
+		pr_debug("%s: EQ_MODULE\n", __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology))
+			msm_audio_effects_popless_eq_handler(
+				effects->ac,
+				&(effects->audio_effects.equalizer),
+				(long *)&values[1]);
+		break;
+	case SOFT_VOLUME_MODULE:
+		pr_debug("%s: SA PLUS VOLUME_MODULE\n", __func__);
+		msm_audio_effects_volume_handler_v2(effects->ac,
+				&(effects->audio_effects.saplus_vol),
+				(long *)&values[1], SOFT_VOLUME_INSTANCE_1);
+		break;
+	case SOFT_VOLUME2_MODULE:
+		pr_debug("%s: TOPOLOGY SWITCH VOLUME MODULE\n",
+			 __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology))
+			msm_audio_effects_volume_handler_v2(effects->ac,
+			      &(effects->audio_effects.topo_switch_vol),
+			      (long *)&values[1], SOFT_VOLUME_INSTANCE_2);
+		break;
+	case DTS_EAGLE_MODULE_ENABLE:
+		pr_debug("%s: DTS_EAGLE_MODULE_ENABLE\n", __func__);
+		if (msm_audio_effects_is_effmodule_supp_in_top(
+			effects_module, effects->ac->topology)) {
+			/*
+			 * HPX->OFF: first disable HPX and then
+			 * enable SA+
+			 * HPX->ON: first disable SA+ and then
+			 * enable HPX
+			 */
+			bool hpx_state = (bool)values[1];
+
+			if (hpx_state)
+				msm_audio_effects_enable_extn(effects->ac,
+					&(effects->audio_effects),
+					false);
+			msm_dts_eagle_enable_asm(effects->ac,
+				hpx_state,
+				AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
+			msm_dts_eagle_enable_asm(effects->ac,
+				hpx_state,
+				AUDPROC_MODULE_ID_DTS_HPX_POSTMIX);
+			if (!hpx_state)
+				msm_audio_effects_enable_extn(effects->ac,
+					&(effects->audio_effects),
+					true);
+		}
+		break;
+	default:
+		pr_err("%s: Invalid effects config module\n", __func__);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long audio_effects_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct q6audio_effects *effects = file->private_data;
+	int rc = 0;
+	long argvalues[MAX_PP_PARAMS_SZ] = {0};
+
+	switch (cmd) {
+	case AUDIO_SET_EFFECTS_CONFIG: {
+		pr_debug("%s: AUDIO_SET_EFFECTS_CONFIG\n", __func__);
+		memset(&effects->config, 0, sizeof(effects->config));
+		if (copy_from_user(&effects->config, (void *)arg,
+				   sizeof(effects->config))) {
+			pr_err("%s: copy from user for AUDIO_SET_EFFECTS_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
+			 __func__, effects->config.output.buf_size,
+			 effects->config.output.num_buf,
+			 effects->config.output.sample_rate,
+			 effects->config.output.num_channels);
+		pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channel: %d\n",
+			 __func__, effects->config.input.buf_size,
+			 effects->config.input.num_buf,
+			 effects->config.input.sample_rate,
+			 effects->config.input.num_channels);
+		break;
+	}
+	case AUDIO_EFFECTS_SET_BUF_LEN: {
+		if (copy_from_user(&effects->config.buf_cfg, (void *)arg,
+				   sizeof(effects->config.buf_cfg))) {
+			pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		pr_debug("%s: write buf len: %d, read buf len: %d\n",
+			 __func__, effects->config.buf_cfg.output_len,
+			 effects->config.buf_cfg.input_len);
+		break;
+	}
+	case AUDIO_EFFECTS_GET_BUF_AVAIL: {
+		struct msm_hwacc_buf_avail buf_avail;
+
+		buf_avail.input_num_avail = atomic_read(&effects->in_count);
+		buf_avail.output_num_avail = atomic_read(&effects->out_count);
+		pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
+			 __func__, buf_avail.output_num_avail,
+			 buf_avail.input_num_avail);
+		if (copy_to_user((void *)arg, &buf_avail,
+				   sizeof(buf_avail))) {
+			pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_EFFECTS_SET_PP_PARAMS: {
+		if (copy_from_user(argvalues, (void *)arg,
+				   MAX_PP_PARAMS_SZ*sizeof(long))) {
+			pr_err("%s: copy from user for pp params failed\n",
+				__func__);
+			return -EFAULT;
+		}
+		rc = audio_effects_set_pp_param(effects, argvalues);
+		break;
+	}
+	default:
+		pr_debug("%s: Calling shared ioctl\n", __func__);
+		rc = audio_effects_shared_ioctl(file, cmd, arg);
+		break;
+	}
+	if (rc)
+		pr_err("%s: cmd 0x%x failed\n", __func__, cmd);
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_hwacc_data_config32 {
+	__u32 buf_size;
+	__u32 num_buf;
+	__u32 num_channels;
+	__u8 channel_map[MAX_CHANNELS_SUPPORTED];
+	__u32 sample_rate;
+	__u32 bits_per_sample;
+};
+
+struct msm_hwacc_buf_cfg32 {
+	__u32 input_len;
+	__u32 output_len;
+};
+
+struct msm_hwacc_buf_avail32 {
+	__u32 input_num_avail;
+	__u32 output_num_avail;
+};
+
+struct msm_hwacc_effects_config32 {
+	struct msm_hwacc_data_config32 input;
+	struct msm_hwacc_data_config32 output;
+	struct msm_hwacc_buf_cfg32 buf_cfg;
+	__u32 meta_mode_enabled;
+	__u32 overwrite_topology;
+	__s32 topology;
+};
+
+enum {
+	AUDIO_SET_EFFECTS_CONFIG32 = _IOW(AUDIO_IOCTL_MAGIC, 99,
+					  struct msm_hwacc_effects_config32),
+	AUDIO_EFFECTS_SET_BUF_LEN32 = _IOW(AUDIO_IOCTL_MAGIC, 100,
+					   struct msm_hwacc_buf_cfg32),
+	AUDIO_EFFECTS_GET_BUF_AVAIL32 = _IOW(AUDIO_IOCTL_MAGIC, 101,
+					     struct msm_hwacc_buf_avail32),
+	AUDIO_EFFECTS_WRITE32 = _IOW(AUDIO_IOCTL_MAGIC, 102, compat_uptr_t),
+	AUDIO_EFFECTS_READ32 = _IOWR(AUDIO_IOCTL_MAGIC, 103, compat_uptr_t),
+	AUDIO_EFFECTS_SET_PP_PARAMS32 = _IOW(AUDIO_IOCTL_MAGIC, 104,
+					   compat_uptr_t),
+	AUDIO_START32 = _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int),
+};
+
+static long audio_effects_compat_ioctl(struct file *file, unsigned int cmd,
+					unsigned long arg)
+{
+	struct q6audio_effects *effects = file->private_data;
+	int rc = 0, i;
+
+	switch (cmd) {
+	case AUDIO_SET_EFFECTS_CONFIG32: {
+		struct msm_hwacc_effects_config32 config32;
+		struct msm_hwacc_effects_config *config = &effects->config;
+
+		memset(&effects->config, 0, sizeof(effects->config));
+		if (copy_from_user(&config32, (void *)arg,
+				   sizeof(config32))) {
+			pr_err("%s: copy to user for AUDIO_SET_EFFECTS_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		config->input.buf_size = config32.input.buf_size;
+		config->input.num_buf = config32.input.num_buf;
+		config->input.num_channels = config32.input.num_channels;
+		config->input.sample_rate = config32.input.sample_rate;
+		config->input.bits_per_sample = config32.input.bits_per_sample;
+		config->input.buf_size = config32.input.buf_size;
+		for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
+			config->input.channel_map[i] =
+						config32.input.channel_map[i];
+		config->output.buf_size = config32.output.buf_size;
+		config->output.num_buf = config32.output.num_buf;
+		config->output.num_channels = config32.output.num_channels;
+		config->output.sample_rate = config32.output.sample_rate;
+		config->output.bits_per_sample =
+					 config32.output.bits_per_sample;
+		config->output.buf_size = config32.output.buf_size;
+		for (i = 0; i < MAX_CHANNELS_SUPPORTED; i++)
+			config->output.channel_map[i] =
+						config32.output.channel_map[i];
+		config->buf_cfg.input_len = config32.buf_cfg.input_len;
+		config->buf_cfg.output_len = config32.buf_cfg.output_len;
+		config->meta_mode_enabled = config32.meta_mode_enabled;
+		config->overwrite_topology = config32.overwrite_topology;
+		config->topology = config32.topology;
+		pr_debug("%s: write buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
+			 __func__, effects->config.output.buf_size,
+			 effects->config.output.num_buf,
+			 effects->config.output.sample_rate,
+			 effects->config.output.num_channels);
+		pr_debug("%s: read buf_size: %d, num_buf: %d, sample_rate: %d, channels: %d\n",
+			 __func__, effects->config.input.buf_size,
+			 effects->config.input.num_buf,
+			 effects->config.input.sample_rate,
+			 effects->config.input.num_channels);
+		break;
+	}
+	case AUDIO_EFFECTS_SET_BUF_LEN32: {
+		struct msm_hwacc_buf_cfg32 buf_cfg32;
+		struct msm_hwacc_effects_config *config = &effects->config;
+
+		if (copy_from_user(&buf_cfg32, (void *)arg,
+				   sizeof(buf_cfg32))) {
+			pr_err("%s: copy from user for AUDIO_EFFECTS_SET_BUF_LEN failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		config->buf_cfg.input_len = buf_cfg32.input_len;
+		config->buf_cfg.output_len = buf_cfg32.output_len;
+		pr_debug("%s: write buf len: %d, read buf len: %d\n",
+			 __func__, effects->config.buf_cfg.output_len,
+			 effects->config.buf_cfg.input_len);
+		break;
+	}
+	case AUDIO_EFFECTS_GET_BUF_AVAIL32: {
+		struct msm_hwacc_buf_avail32 buf_avail;
+
+		memset(&buf_avail, 0, sizeof(buf_avail));
+
+		buf_avail.input_num_avail = atomic_read(&effects->in_count);
+		buf_avail.output_num_avail = atomic_read(&effects->out_count);
+		pr_debug("%s: write buf avail: %d, read buf avail: %d\n",
+			 __func__, buf_avail.output_num_avail,
+			 buf_avail.input_num_avail);
+		if (copy_to_user((void *)arg, &buf_avail,
+				   sizeof(buf_avail))) {
+			pr_err("%s: copy to user for AUDIO_EFFECTS_GET_NUM_BUF_AVAIL failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_EFFECTS_SET_PP_PARAMS32: {
+		long argvalues[MAX_PP_PARAMS_SZ] = {0};
+		int argvalues32[MAX_PP_PARAMS_SZ] = {0};
+
+		if (copy_from_user(argvalues32, (void *)arg,
+				   MAX_PP_PARAMS_SZ*sizeof(int))) {
+			pr_err("%s: copy from user failed for pp params\n",
+				__func__);
+			return -EFAULT;
+		}
+		for (i = 0; i < MAX_PP_PARAMS_SZ; i++)
+			argvalues[i] = argvalues32[i];
+
+		rc = audio_effects_set_pp_param(effects, argvalues);
+		break;
+	}
+	case AUDIO_START32: {
+		rc = audio_effects_shared_ioctl(file, AUDIO_START, arg);
+		break;
+	}
+	case AUDIO_EFFECTS_WRITE32: {
+		rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_WRITE, arg);
+		break;
+	}
+	case AUDIO_EFFECTS_READ32: {
+		rc = audio_effects_shared_ioctl(file, AUDIO_EFFECTS_READ, arg);
+		break;
+	}
+	default:
+		pr_debug("%s: unhandled ioctl\n", __func__);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+#endif
+
+static int audio_effects_release(struct inode *inode, struct file *file)
+{
+	struct q6audio_effects *effects = file->private_data;
+	int rc = 0;
+
+	if (!effects) {
+		pr_err("%s: effect is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (effects->opened) {
+		rc = wait_event_timeout(effects->write_wait,
+					atomic_read(&effects->out_count),
+					WAIT_TIMEDOUT_DURATION_SECS * HZ);
+		if (!rc)
+			pr_err("%s: write wait_event_timeout failed\n",
+				__func__);
+		rc = wait_event_timeout(effects->read_wait,
+					atomic_read(&effects->in_count),
+					WAIT_TIMEDOUT_DURATION_SECS * HZ);
+		if (!rc)
+			pr_err("%s: read wait_event_timeout failed\n",
+				__func__);
+		rc = q6asm_cmd(effects->ac, CMD_CLOSE);
+		if (rc < 0)
+			pr_err("%s[%pK]:Failed to close the session rc=%d\n",
+				__func__, effects, rc);
+		effects->opened = 0;
+		effects->started = 0;
+
+		audio_effects_deinit_pp(effects->ac);
+	}
+
+	if (effects->buf_alloc) {
+		q6asm_audio_client_buf_free_contiguous(IN, effects->ac);
+		q6asm_audio_client_buf_free_contiguous(OUT, effects->ac);
+	}
+	q6asm_audio_client_free(effects->ac);
+
+	kfree(effects);
+
+	pr_debug("%s: close session success\n", __func__);
+	return rc;
+}
+
+static int audio_effects_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_effects *effects;
+	int rc = 0;
+
+	effects = kzalloc(sizeof(struct q6audio_effects), GFP_KERNEL);
+	if (!effects)
+		return -ENOMEM;
+
+	effects->ac = q6asm_audio_client_alloc(
+					(app_cb)audio_effects_event_handler,
+					(void *)effects);
+	if (!effects->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+			__func__);
+		kfree(effects);
+		return -ENOMEM;
+	}
+
+	init_waitqueue_head(&effects->read_wait);
+	init_waitqueue_head(&effects->write_wait);
+
+	effects->opened = 0;
+	effects->started = 0;
+	effects->buf_alloc = 0;
+	file->private_data = effects;
+	pr_debug("%s: open session success\n", __func__);
+	return rc;
+}
+
+static const struct file_operations audio_effects_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_effects_open,
+	.release = audio_effects_release,
+	.unlocked_ioctl = audio_effects_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = audio_effects_compat_ioctl,
+#endif
+};
+
+struct miscdevice audio_effects_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_hweffects",
+	.fops = &audio_effects_fops,
+};
+
+static int __init audio_effects_init(void)
+{
+	return misc_register(&audio_effects_misc);
+}
+
+device_initcall(audio_effects_init);
+MODULE_DESCRIPTION("Audio hardware accelerated effects driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/qcom/qdsp6v2/audio_mp3.c b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
new file mode 100644
index 0000000..0b10c7a
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_mp3.c
@@ -0,0 +1,188 @@
+/* mp3 audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_mp3_misc;
+static struct ws_mgr audio_mp3_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_mp3_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			rc = enable_volume_ramp(audio);
+			if (rc < 0) {
+				pr_err("%s: Failed to enable volume ramp\n",
+					__func__);
+			}
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+						audio->ac->session,
+						audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_mp3_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_mp3_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_mp3_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_MP3);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open MP3 decoder, expected frames is always 1
+		 * audio->buf_cfg.frames_per_buf = 0x01;
+		 */
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_MP3);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_mp3_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_mp3_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:mp3dec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_mp3_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_mp3_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_mp3",
+	.fops = &audio_mp3_fops,
+};
+
+static int __init audio_mp3_init(void)
+{
+	int ret = misc_register(&audio_mp3_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_mp3_misc.this_device, true);
+	audio_mp3_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_mp3_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_mp3_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
new file mode 100644
index 0000000..01c3dc5a
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_multi_aac.c
@@ -0,0 +1,523 @@
+/* aac audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/msm_audio_aac.h>
+#include <linux/compat.h>
+#include <soc/qcom/socinfo.h>
+#include "audio_utils_aio.h"
+
+#define AUDIO_AAC_DUAL_MONO_INVALID -1
+
+
+/* Default number of pre-allocated event packets */
+#define PCM_BUFSZ_MIN_AACM	((8*1024) + sizeof(struct dec_meta_out))
+static struct miscdevice audio_multiaac_misc;
+static struct ws_mgr audio_multiaac_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_aac_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_aac_cfg aac_cfg;
+		struct msm_audio_aac_config *aac_config;
+		uint32_t sbr_ps = 0x00;
+
+		aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+		if (audio->feedback == TUNNEL_MODE) {
+			aac_cfg.sample_rate = aac_config->sample_rate;
+			aac_cfg.ch_cfg = aac_config->channel_configuration;
+		} else {
+			aac_cfg.sample_rate =  audio->pcm_cfg.sample_rate;
+			aac_cfg.ch_cfg = audio->pcm_cfg.channel_count;
+		}
+		pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+						audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm_native(audio->ac,
+				aac_cfg.sample_rate,
+				aac_cfg.ch_cfg);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		/* turn on both sbr and ps */
+		rc = q6asm_enable_sbrps(audio->ac, sbr_ps);
+		if (rc < 0)
+			pr_err("sbr-ps enable failed\n");
+		if (aac_config->sbr_ps_on_flag)
+			aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+		else if (aac_config->sbr_on_flag)
+			aac_cfg.aot = AAC_ENC_MODE_AAC_P;
+		else
+			aac_cfg.aot = AAC_ENC_MODE_AAC_LC;
+
+		switch (aac_config->format) {
+		case AUDIO_AAC_FORMAT_ADTS:
+			aac_cfg.format = 0x00;
+			break;
+		case AUDIO_AAC_FORMAT_LOAS:
+			aac_cfg.format = 0x01;
+			break;
+		case AUDIO_AAC_FORMAT_ADIF:
+			aac_cfg.format = 0x02;
+			break;
+		default:
+		case AUDIO_AAC_FORMAT_RAW:
+			aac_cfg.format = 0x03;
+		}
+		aac_cfg.ep_config = aac_config->ep_config;
+		aac_cfg.section_data_resilience =
+			aac_config->aac_section_data_resilience_flag;
+		aac_cfg.scalefactor_data_resilience =
+			aac_config->aac_scalefactor_data_resilience_flag;
+		aac_cfg.spectral_data_resilience =
+			aac_config->aac_spectral_data_resilience_flag;
+
+		pr_debug("%s:format=%x aot=%d  ch=%d sr=%d\n",
+			__func__, aac_cfg.format,
+			aac_cfg.aot, aac_cfg.ch_cfg,
+			aac_cfg.sample_rate);
+
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_multi_aac(audio->ac, &aac_cfg);
+		if (rc < 0) {
+			pr_err("cmd media format block failed\n");
+			break;
+		}
+		rc = q6asm_set_encdec_chan_map(audio->ac, 2);
+		if (rc < 0) {
+			pr_err("%s: cmd set encdec_chan_map failed\n",
+				__func__);
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_info("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+						audio->ac->session,
+						audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG: {
+		struct msm_audio_aac_config *aac_config;
+		uint16_t sce_left = 1, sce_right = 2;
+
+		if (arg == NULL) {
+			pr_err("%s: NULL config pointer\n", __func__);
+			rc = -EINVAL;
+			break;
+		}
+		memcpy(audio->codec_cfg, arg,
+				sizeof(struct msm_audio_aac_config));
+		aac_config = audio->codec_cfg;
+		if (aac_config->dual_mono_mode >
+		    AUDIO_AAC_DUAL_MONO_PL_SR) {
+			pr_err("%s:AUDIO_SET_AAC_CONFIG: Invalid dual_mono mode =%d\n",
+				 __func__, aac_config->dual_mono_mode);
+		} else {
+			/* convert the data from user into sce_left
+			 * and sce_right based on the definitions
+			 */
+			pr_debug("%s: AUDIO_SET_AAC_CONFIG: modify dual_mono mode =%d\n",
+				 __func__, aac_config->dual_mono_mode);
+			switch (aac_config->dual_mono_mode) {
+			case AUDIO_AAC_DUAL_MONO_PL_PR:
+				sce_left = 1;
+				sce_right = 1;
+				break;
+			case AUDIO_AAC_DUAL_MONO_SL_SR:
+				sce_left = 2;
+				sce_right = 2;
+				break;
+			case AUDIO_AAC_DUAL_MONO_SL_PR:
+				sce_left = 2;
+				sce_right = 1;
+				break;
+			case AUDIO_AAC_DUAL_MONO_PL_SR:
+			default:
+				sce_left = 1;
+				sce_right = 2;
+				break;
+			}
+			rc = q6asm_cfg_dual_mono_aac(audio->ac,
+						sce_left, sce_right);
+			if (rc < 0)
+				pr_err("%s: asm cmd dualmono failed rc=%d\n",
+							 __func__, rc);
+		}			break;
+		break;
+	}
+	case AUDIO_SET_AAC_MIX_CONFIG:	{
+		u32 *mix_coeff = (u32 *)arg;
+
+		if (!arg) {
+			pr_err("%s: Invalid param for %s\n",
+				__func__, "AUDIO_SET_AAC_MIX_CONFIG");
+			rc = -EINVAL;
+			break;
+		}
+		pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
+		pr_debug("%s, value of coeff = %d",
+					__func__, *mix_coeff);
+		q6asm_cfg_aac_sel_mix_coef(audio->ac, *mix_coeff);
+		if (rc < 0)
+			pr_err("%s asm aac_sel_mix_coef failed rc=%d\n",
+				 __func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_AAC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_aac_config))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG failed\n"
+				, __func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG: {
+		struct msm_audio_aac_config aac_config;
+
+		if (copy_from_user(&aac_config, (void *)arg,
+			sizeof(aac_config))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_CONFIG failed\n"
+				, __func__);
+			rc = -EFAULT;
+		}
+		rc = audio_ioctl_shared(file, cmd, &aac_config);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	case AUDIO_SET_AAC_MIX_CONFIG:	{
+		u32 mix_config;
+
+		pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG", __func__);
+		if (copy_from_user(&mix_config, (void *)arg,
+			sizeof(u32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = audio_ioctl_shared(file, cmd, &mix_config);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+			__func__, rc);
+		break;
+	}
+	default: {
+		pr_debug("Calling utils ioctl\n");
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_aac_config32 {
+	s16 format;
+	u16 audio_object;
+	u16 ep_config;  /* 0 ~ 3 useful only obj = ERLC */
+	u16 aac_section_data_resilience_flag;
+	u16 aac_scalefactor_data_resilience_flag;
+	u16 aac_spectral_data_resilience_flag;
+	u16 sbr_on_flag;
+	u16 sbr_ps_on_flag;
+	u16 dual_mono_mode;
+	u16 channel_configuration;
+	u16 sample_rate;
+};
+
+enum {
+	AUDIO_SET_AAC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config32),
+	AUDIO_GET_AAC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+		(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config32),
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+							unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_AAC_CONFIG_32: {
+		struct msm_audio_aac_config *aac_config;
+		struct msm_audio_aac_config32 aac_config_32;
+
+		memset(&aac_config_32, 0, sizeof(aac_config_32));
+
+		aac_config = (struct msm_audio_aac_config *)audio->codec_cfg;
+		aac_config_32.format = aac_config->format;
+		aac_config_32.audio_object = aac_config->audio_object;
+		aac_config_32.ep_config = aac_config->ep_config;
+		aac_config_32.aac_section_data_resilience_flag =
+			aac_config->aac_section_data_resilience_flag;
+		aac_config_32.aac_scalefactor_data_resilience_flag =
+			aac_config->aac_scalefactor_data_resilience_flag;
+		aac_config_32.aac_spectral_data_resilience_flag =
+			aac_config->aac_spectral_data_resilience_flag;
+		aac_config_32.sbr_on_flag = aac_config->sbr_on_flag;
+		aac_config_32.sbr_ps_on_flag = aac_config->sbr_ps_on_flag;
+		aac_config_32.dual_mono_mode = aac_config->dual_mono_mode;
+		aac_config_32.channel_configuration =
+			aac_config->channel_configuration;
+		aac_config_32.sample_rate = aac_config->sample_rate;
+
+		if (copy_to_user((void *)arg, &aac_config_32,
+			sizeof(aac_config_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_AAC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_AAC_CONFIG_32: {
+		struct msm_audio_aac_config aac_config;
+		struct msm_audio_aac_config32 aac_config_32;
+
+		pr_debug("%s: AUDIO_SET_AAC_CONFIG\n", __func__);
+		if (copy_from_user(&aac_config_32, (void *)arg,
+			sizeof(aac_config_32))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_SET_AAC_CONFIG_32 failed",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		aac_config.format = aac_config_32.format;
+		aac_config.audio_object = aac_config_32.audio_object;
+		aac_config.ep_config = aac_config_32.ep_config;
+		aac_config.aac_section_data_resilience_flag =
+			aac_config_32.aac_section_data_resilience_flag;
+		aac_config.aac_scalefactor_data_resilience_flag =
+			aac_config_32.aac_scalefactor_data_resilience_flag;
+		aac_config.aac_spectral_data_resilience_flag =
+			aac_config_32.aac_spectral_data_resilience_flag;
+		aac_config.sbr_on_flag = aac_config_32.sbr_on_flag;
+		aac_config.sbr_ps_on_flag = aac_config_32.sbr_ps_on_flag;
+		aac_config.dual_mono_mode = aac_config_32.dual_mono_mode;
+		aac_config.channel_configuration =
+				aac_config_32.channel_configuration;
+		aac_config.sample_rate = aac_config_32.sample_rate;
+
+		cmd = AUDIO_SET_AAC_CONFIG;
+		rc = audio_ioctl_shared(file, cmd, &aac_config);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	case AUDIO_SET_AAC_MIX_CONFIG: {
+		u32 mix_config;
+
+		pr_debug("%s, AUDIO_SET_AAC_MIX_CONFIG\n", __func__);
+		if (copy_from_user(&mix_config, (void *)arg,
+			sizeof(u32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_AAC_MIX_CONFIG failed\n"
+				, __func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = audio_ioctl_shared(file, cmd, &mix_config);
+		if (rc)
+			pr_err("%s:AUDIO_SET_AAC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default: {
+		pr_debug("Calling utils ioctl\n");
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+	struct msm_audio_aac_config *aac_config = NULL;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_multi_aac_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_aac_config),
+					GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	aac_config = audio->codec_cfg;
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN_AACM;
+	audio->miscdevice = &audio_multiaac_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_multiaac_ws_mgr;
+	aac_config->dual_mono_mode = AUDIO_AAC_DUAL_MONO_INVALID;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_MPEG4_MULTI_AAC);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open AAC decoder, expected frames is always 1
+		 * audio->buf_cfg.frames_per_buf = 0x01;
+		 */
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_MPEG4_MULTI_AAC);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_multi_aac_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_aac_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:AAC 5.1 Decoder OPEN success mode[%d]session[%d]\n",
+		__func__, audio->feedback, audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_aac_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_multiaac_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_multi_aac",
+	.fops = &audio_aac_fops,
+};
+
+static int __init audio_aac_init(void)
+{
+	int ret = misc_register(&audio_multiaac_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_multiaac_misc.this_device, true);
+	audio_multiaac_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_multiaac_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_aac_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_qcelp.c b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
new file mode 100644
index 0000000..8f2511c
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_qcelp.c
@@ -0,0 +1,191 @@
+/* qcelp(v13k) audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "audio_utils_aio.h"
+
+#define FRAME_SIZE_DEC_QCELP  ((32) + sizeof(struct dec_meta_in))
+
+static struct miscdevice audio_qcelp_misc;
+static struct ws_mgr audio_qcelp_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_qcelp_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("%s: AUDIO_START sessionid[%d]enable[%d]\n", __func__,
+						audio->ac->session,
+						audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+	}
+	return rc;
+}
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_qcelp_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE_DEC_QCELP;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	audio->pcm_cfg.sample_rate = 8000;
+	audio->pcm_cfg.channel_count = 1;
+	audio->miscdevice = &audio_qcelp_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_qcelp_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_V13K);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_V13K);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_qcelp_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_qcelp_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:dec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_qcelp_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+};
+
+static struct miscdevice audio_qcelp_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_qcelp",
+	.fops = &audio_qcelp_fops,
+};
+
+static int __init audio_qcelp_init(void)
+{
+	int ret = misc_register(&audio_qcelp_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_qcelp_misc.this_device, true);
+	audio_qcelp_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_qcelp_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_qcelp_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.c b/drivers/misc/qcom/qdsp6v2/audio_utils.c
new file mode 100644
index 0000000..15ee9f5
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.c
@@ -0,0 +1,954 @@
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/*
+ * Define maximum buffer size. Below values are chosen considering the higher
+ * values used among all native drivers.
+ */
+#define MAX_FRAME_SIZE	1536
+#define MAX_FRAMES	5
+#define META_SIZE	(sizeof(struct meta_out_dsp))
+#define MAX_BUFFER_SIZE	(1 + ((MAX_FRAME_SIZE + META_SIZE) * MAX_FRAMES))
+
+static int audio_in_pause(struct q6audio_in  *audio)
+{
+	int rc;
+
+	rc = q6asm_cmd(audio->ac, CMD_PAUSE);
+	if (rc < 0)
+		pr_err("%s:session id %d: pause cmd failed rc=%d\n", __func__,
+				audio->ac->session, rc);
+
+	return rc;
+}
+
+static int audio_in_flush(struct q6audio_in  *audio)
+{
+	int rc;
+
+	pr_debug("%s:session id %d: flush\n", __func__, audio->ac->session);
+	/* Flush if session running */
+	if (audio->enabled) {
+		/* Implicitly issue a pause to the encoder before flushing */
+		rc = audio_in_pause(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: pause cmd failed rc=%d\n",
+				 __func__, audio->ac->session, rc);
+			return rc;
+		}
+
+		rc = q6asm_cmd(audio->ac, CMD_FLUSH);
+		if (rc < 0) {
+			pr_err("%s:session id %d: flush cmd failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			return rc;
+		}
+		/* 2nd arg: 0 -> run immediately
+		 * 3rd arg: 0 -> msw_ts,
+		 * 4th arg: 0 ->lsw_ts
+		 */
+		q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+		pr_debug("Rerun the session\n");
+	}
+	audio->rflush = 1;
+	audio->wflush = 1;
+	memset(audio->out_frame_info, 0, sizeof(audio->out_frame_info));
+	wake_up(&audio->read_wait);
+	/* get read_lock to ensure no more waiting read thread */
+	mutex_lock(&audio->read_lock);
+	audio->rflush = 0;
+	mutex_unlock(&audio->read_lock);
+	wake_up(&audio->write_wait);
+	/* get write_lock to ensure no more waiting write thread */
+	mutex_lock(&audio->write_lock);
+	audio->wflush = 0;
+	mutex_unlock(&audio->write_lock);
+	pr_debug("%s:session id %d: in_bytes %d\n", __func__,
+			audio->ac->session, atomic_read(&audio->in_bytes));
+	pr_debug("%s:session id %d: in_samples %d\n", __func__,
+			audio->ac->session, atomic_read(&audio->in_samples));
+	atomic_set(&audio->in_bytes, 0);
+	atomic_set(&audio->in_samples, 0);
+	atomic_set(&audio->out_count, 0);
+	return 0;
+}
+
+/* must be called with audio->lock held */
+int audio_in_enable(struct q6audio_in  *audio)
+{
+	if (audio->enabled)
+		return 0;
+
+	/* 2nd arg: 0 -> run immediately
+	 * 3rd arg: 0 -> msw_ts,
+	 * 4th arg: 0 ->lsw_ts
+	 */
+	return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+}
+
+/* must be called with audio->lock held */
+int audio_in_disable(struct q6audio_in  *audio)
+{
+	int rc = 0;
+
+	if (!audio->stopped) {
+		audio->enabled = 0;
+		audio->opened = 0;
+		pr_debug("%s:session id %d: inbytes[%d] insamples[%d]\n",
+				__func__, audio->ac->session,
+				atomic_read(&audio->in_bytes),
+				atomic_read(&audio->in_samples));
+
+		rc = q6asm_cmd(audio->ac, CMD_CLOSE);
+		if (rc < 0)
+			pr_err("%s:session id %d: Failed to close the session rc=%d\n",
+				__func__, audio->ac->session,
+				rc);
+		audio->stopped = 1;
+		memset(audio->out_frame_info, 0,
+				sizeof(audio->out_frame_info));
+		wake_up(&audio->read_wait);
+		wake_up(&audio->write_wait);
+	}
+	pr_debug("%s:session id %d: enabled[%d]\n", __func__,
+			audio->ac->session, audio->enabled);
+	return rc;
+}
+
+int audio_in_buf_alloc(struct q6audio_in *audio)
+{
+	int rc = 0;
+
+	switch (audio->buf_alloc) {
+	case NO_BUF_ALLOC:
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_audio_client_buf_alloc(IN,
+				audio->ac,
+				ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+				audio->pcm_cfg.buffer_count);
+			if (rc < 0) {
+				pr_err("%s:session id %d: Buffer Alloc failed\n",
+						__func__,
+						audio->ac->session);
+				rc = -ENOMEM;
+				break;
+			}
+			audio->buf_alloc |= BUF_ALLOC_IN;
+		}
+		rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+				ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+				audio->str_cfg.buffer_count);
+		if (rc < 0) {
+			pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			rc = -ENOMEM;
+			break;
+		}
+		audio->buf_alloc |= BUF_ALLOC_OUT;
+		break;
+	case BUF_ALLOC_IN:
+		rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+				ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+				audio->str_cfg.buffer_count);
+		if (rc < 0) {
+			pr_err("%s:session id %d: Buffer Alloc failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			rc = -ENOMEM;
+			break;
+		}
+		audio->buf_alloc |= BUF_ALLOC_OUT;
+		break;
+	case BUF_ALLOC_OUT:
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
+				ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+				audio->pcm_cfg.buffer_count);
+			if (rc < 0) {
+				pr_err("%s:session id %d: Buffer Alloc failed\n",
+					__func__,
+					audio->ac->session);
+				rc = -ENOMEM;
+				break;
+			}
+			audio->buf_alloc |= BUF_ALLOC_IN;
+		}
+		break;
+	default:
+		pr_debug("%s:session id %d: buf[%d]\n", __func__,
+					audio->ac->session, audio->buf_alloc);
+	}
+
+	return rc;
+}
+
+int audio_in_set_config(struct file *file,
+		struct msm_audio_config *cfg)
+{
+	int rc = 0;
+	struct q6audio_in  *audio = file->private_data;
+
+	if (audio->feedback != NON_TUNNEL_MODE) {
+		pr_err("%s:session id %d: Not sufficient permission to change the record mode\n",
+			__func__, audio->ac->session);
+		rc = -EACCES;
+		goto ret;
+	}
+	if ((cfg->buffer_count > PCM_BUF_COUNT) ||
+		(cfg->buffer_count == 1))
+		cfg->buffer_count = PCM_BUF_COUNT;
+
+	audio->pcm_cfg.buffer_count = cfg->buffer_count;
+	audio->pcm_cfg.buffer_size  = cfg->buffer_size;
+	audio->pcm_cfg.channel_count = cfg->channel_count;
+	audio->pcm_cfg.sample_rate = cfg->sample_rate;
+	if (audio->opened && audio->feedback == NON_TUNNEL_MODE) {
+		rc = q6asm_audio_client_buf_alloc(IN, audio->ac,
+			ALIGN_BUF_SIZE(audio->pcm_cfg.buffer_size),
+			audio->pcm_cfg.buffer_count);
+		if (rc < 0) {
+			pr_err("%s:session id %d: Buffer Alloc failed\n",
+				__func__, audio->ac->session);
+			rc = -ENOMEM;
+			goto ret;
+		}
+	}
+	audio->buf_alloc |= BUF_ALLOC_IN;
+	rc = 0;
+	pr_debug("%s:session id %d: AUDIO_SET_CONFIG %d %d\n", __func__,
+			audio->ac->session, audio->pcm_cfg.buffer_count,
+			audio->pcm_cfg.buffer_size);
+ret:
+	return rc;
+}
+/* ------------------- device --------------------- */
+static long audio_in_ioctl_shared(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_FLUSH: {
+		/* Make sure we're stopped and we wake any threads
+		 * that might be blocked holding the read_lock.
+		 * While audio->stopped read threads will always
+		 * exit immediately.
+		 */
+		rc = audio_in_flush(audio);
+		if (rc < 0)
+			pr_err("%s:session id %d: Flush Fail rc=%d\n",
+				__func__, audio->ac->session, rc);
+		else { /* Register back the flushed read buffer with DSP */
+			int cnt = 0;
+
+			while (cnt++ < audio->str_cfg.buffer_count)
+				q6asm_read(audio->ac); /* Push buffer to DSP */
+			pr_debug("register the read buffer\n");
+		}
+		break;
+	}
+	case AUDIO_PAUSE: {
+		pr_debug("%s:session id %d: AUDIO_PAUSE\n", __func__,
+					audio->ac->session);
+		if (audio->enabled)
+			audio_in_pause(audio);
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		if (copy_to_user((void *) arg, &audio->ac->session,
+			sizeof(u16))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+long audio_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	if (cmd == AUDIO_GET_STATS) {
+		struct msm_audio_stats stats;
+
+		memset(&stats, 0, sizeof(stats));
+		stats.byte_count = atomic_read(&audio->in_bytes);
+		stats.sample_count = atomic_read(&audio->in_samples);
+		if (copy_to_user((void *) arg, &stats, sizeof(stats)))
+			return -EFAULT;
+		return rc;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_FLUSH:
+	case AUDIO_PAUSE:
+	case AUDIO_GET_SESSION_ID:
+		rc = audio_in_ioctl_shared(file, cmd, arg);
+		break;
+	case AUDIO_GET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = audio->str_cfg.buffer_size;
+		cfg.buffer_count = audio->str_cfg.buffer_count;
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg)))
+			rc = -EFAULT;
+		pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
+				__func__, audio->ac->session, cfg.buffer_size,
+				cfg.buffer_count);
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n"
+				, __func__);
+			rc = -EFAULT;
+			break;
+		}
+		/* Minimum single frame size,
+		 * but with in maximum frames number
+		 */
+		if ((cfg.buffer_size < (audio->min_frame_size +
+			sizeof(struct meta_out_dsp))) ||
+			(cfg.buffer_count < FRAME_NUM)) {
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg.buffer_size > MAX_BUFFER_SIZE) {
+			rc = -EINVAL;
+			break;
+		}
+		audio->str_cfg.buffer_size = cfg.buffer_size;
+		audio->str_cfg.buffer_count = cfg.buffer_count;
+		if (audio->opened) {
+			rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+				ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+				audio->str_cfg.buffer_count);
+			if (rc < 0) {
+				pr_err("%s: session id %d: Buffer Alloc failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+				rc = -ENOMEM;
+				break;
+			}
+		}
+		audio->buf_alloc |= BUF_ALLOC_OUT;
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
+				__func__, audio->ac->session,
+				audio->str_cfg.buffer_size,
+				audio->str_cfg.buffer_count);
+		break;
+	}
+	case AUDIO_SET_BUF_CFG: {
+		struct msm_audio_buf_cfg  cfg;
+
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			rc = -EFAULT;
+			break;
+		}
+		if ((audio->feedback == NON_TUNNEL_MODE) &&
+			!cfg.meta_info_enable) {
+			rc = -EFAULT;
+			break;
+		}
+
+		/* Restrict the num of frames per buf to coincide with
+		 * default buf size
+		 */
+		if (cfg.frames_per_buf > audio->max_frames_per_buf) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+		audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
+		pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
+				__func__,
+				audio->ac->session, cfg.meta_info_enable,
+				cfg.frames_per_buf);
+		break;
+	}
+	case AUDIO_GET_BUF_CFG: {
+		pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+			__func__,
+			audio->ac->session, audio->buf_cfg.meta_info_enable,
+			audio->buf_cfg.frames_per_buf);
+
+		if (copy_to_user((void *)arg, &audio->buf_cfg,
+					sizeof(struct msm_audio_buf_cfg)))
+			rc = -EFAULT;
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		if (copy_to_user((void *)arg, &audio->pcm_cfg,
+					sizeof(struct msm_audio_config)))
+			rc = -EFAULT;
+		break;
+
+	}
+	case AUDIO_SET_CONFIG: {
+		struct msm_audio_config cfg;
+
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = audio_in_set_config(file, &cfg);
+		break;
+	}
+	default:
+		/* call codec specific ioctl */
+		rc = audio->enc_ioctl(file, cmd, arg);
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_stats32 {
+	u32 byte_count;
+	u32 sample_count;
+	u32 unused[2];
+};
+
+struct msm_audio_stream_config32 {
+	u32 buffer_size;
+	u32 buffer_count;
+};
+
+struct msm_audio_config32 {
+	u32 buffer_size;
+	u32 buffer_count;
+	u32 channel_count;
+	u32 sample_rate;
+	u32 type;
+	u32 meta_field;
+	u32 bits;
+	u32 unused[3];
+};
+
+struct msm_audio_buf_cfg32 {
+	u32 meta_info_enable;
+	u32 frames_per_buf;
+};
+
+enum {
+	AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
+			struct msm_audio_config32),
+	AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
+			struct msm_audio_config32),
+	AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
+			struct msm_audio_stats32),
+	AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
+			struct msm_audio_stream_config32),
+	AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
+			struct msm_audio_stream_config32),
+	AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
+			struct msm_audio_buf_cfg32),
+	AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
+			struct msm_audio_buf_cfg32),
+};
+
+long audio_in_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	if (cmd == AUDIO_GET_STATS_32) {
+		struct msm_audio_stats32 stats_32;
+
+		memset(&stats_32, 0, sizeof(stats_32));
+		stats_32.byte_count = atomic_read(&audio->in_bytes);
+		stats_32.sample_count = atomic_read(&audio->in_samples);
+		if (copy_to_user((void *) arg, &stats_32, sizeof(stats_32))) {
+			pr_err("%s: copy_to_user failed for AUDIO_GET_STATS_32\n",
+				__func__);
+			return -EFAULT;
+		}
+		return rc;
+	}
+
+	mutex_lock(&audio->lock);
+	switch (cmd) {
+	case AUDIO_FLUSH:
+	case AUDIO_PAUSE:
+	case AUDIO_GET_SESSION_ID:
+		rc = audio_in_ioctl_shared(file, cmd, arg);
+		break;
+	case AUDIO_GET_STREAM_CONFIG_32: {
+		struct msm_audio_stream_config32 cfg_32;
+
+		memset(&cfg_32, 0, sizeof(cfg_32));
+		cfg_32.buffer_size = audio->str_cfg.buffer_size;
+		cfg_32.buffer_count = audio->str_cfg.buffer_count;
+		if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
+			pr_err("%s: Copy to user failed\n", __func__);
+			rc = -EFAULT;
+		}
+		pr_debug("%s:session id %d: AUDIO_GET_STREAM_CONFIG %d %d\n",
+				__func__, audio->ac->session,
+				cfg_32.buffer_size,
+				cfg_32.buffer_count);
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG_32: {
+		struct msm_audio_stream_config32 cfg_32;
+		struct msm_audio_stream_config cfg;
+
+		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.buffer_size = cfg_32.buffer_size;
+		cfg.buffer_count = cfg_32.buffer_count;
+		/* Minimum single frame size,
+		 * but with in maximum frames number
+		 */
+		if ((cfg.buffer_size < (audio->min_frame_size +
+			sizeof(struct meta_out_dsp))) ||
+			(cfg.buffer_count < FRAME_NUM)) {
+			rc = -EINVAL;
+			break;
+		}
+		audio->str_cfg.buffer_size = cfg.buffer_size;
+		audio->str_cfg.buffer_count = cfg.buffer_count;
+		if (audio->opened) {
+			rc = q6asm_audio_client_buf_alloc(OUT, audio->ac,
+				ALIGN_BUF_SIZE(audio->str_cfg.buffer_size),
+				audio->str_cfg.buffer_count);
+			if (rc < 0) {
+				pr_err("%s: session id %d:\n",
+					__func__, audio->ac->session);
+				pr_err("Buffer Alloc failed rc=%d\n", rc);
+				rc = -ENOMEM;
+				break;
+			}
+		}
+		audio->buf_alloc |= BUF_ALLOC_OUT;
+		pr_debug("%s:session id %d: AUDIO_SET_STREAM_CONFIG %d %d\n",
+				__func__, audio->ac->session,
+				audio->str_cfg.buffer_size,
+				audio->str_cfg.buffer_count);
+		break;
+	}
+	case AUDIO_SET_BUF_CFG_32: {
+		struct msm_audio_buf_cfg32 cfg_32;
+		struct msm_audio_buf_cfg cfg;
+
+		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_BUG_CFG_32 failed",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.meta_info_enable = cfg_32.meta_info_enable;
+		cfg.frames_per_buf = cfg_32.frames_per_buf;
+
+		if ((audio->feedback == NON_TUNNEL_MODE) &&
+			!cfg.meta_info_enable) {
+			rc = -EFAULT;
+			break;
+		}
+
+		/* Restrict the num of frames per buf to coincide with
+		 * default buf size
+		 */
+		if (cfg.frames_per_buf > audio->max_frames_per_buf) {
+			rc = -EFAULT;
+			break;
+		}
+		audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+		audio->buf_cfg.frames_per_buf = cfg.frames_per_buf;
+		pr_debug("%s:session id %d: Set-buf-cfg: meta[%d] framesperbuf[%d]\n",
+			__func__, audio->ac->session, cfg.meta_info_enable,
+			cfg.frames_per_buf);
+		break;
+	}
+	case AUDIO_GET_BUF_CFG_32: {
+		struct msm_audio_buf_cfg32 cfg_32;
+
+		pr_debug("%s:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+			__func__,
+			audio->ac->session, audio->buf_cfg.meta_info_enable,
+			audio->buf_cfg.frames_per_buf);
+		cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
+		cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
+
+		if (copy_to_user((void *)arg, &cfg_32,
+			sizeof(struct msm_audio_buf_cfg32))) {
+			pr_err("%s: Copy to user failed\n", __func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_GET_CONFIG_32: {
+		struct msm_audio_config32 cfg_32;
+
+		memset(&cfg_32, 0, sizeof(cfg_32));
+		cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
+		cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
+		cfg_32.channel_count = audio->pcm_cfg.channel_count;
+		cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
+		cfg_32.type = audio->pcm_cfg.type;
+		cfg_32.meta_field = audio->pcm_cfg.meta_field;
+		cfg_32.bits = audio->pcm_cfg.bits;
+
+		if (copy_to_user((void *)arg, &cfg_32,
+					sizeof(struct msm_audio_config32))) {
+			pr_err("%s: Copy to user failed\n", __func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_CONFIG_32: {
+		struct msm_audio_config32 cfg_32;
+		struct msm_audio_config cfg;
+
+		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.buffer_size = cfg_32.buffer_size;
+		cfg.buffer_count = cfg_32.buffer_count;
+		cfg.channel_count = cfg_32.channel_count;
+		cfg.sample_rate = cfg_32.sample_rate;
+		cfg.type = cfg_32.type;
+		cfg.meta_field = cfg_32.meta_field;
+		cfg.bits = cfg_32.bits;
+		rc = audio_in_set_config(file, &cfg);
+		break;
+	}
+	default:
+		  /* call codec specific ioctl */
+		  rc = audio->enc_compat_ioctl(file, cmd, arg);
+	}
+	mutex_unlock(&audio->lock);
+	return rc;
+}
+#endif
+
+ssize_t audio_in_read(struct file *file,
+				char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct q6audio_in  *audio = file->private_data;
+	const char __user *start = buf;
+	unsigned char *data;
+	uint32_t offset = 0;
+	uint32_t size = 0;
+	int rc = 0;
+	uint32_t idx;
+	struct meta_out_dsp meta;
+	uint32_t bytes_to_copy = 0;
+	uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
+		(sizeof(unsigned char) +
+		(sizeof(struct meta_out_dsp)*(audio->buf_cfg.frames_per_buf)));
+
+	memset(&meta, 0, sizeof(meta));
+	pr_debug("%s:session id %d: read - %zd\n", __func__, audio->ac->session,
+			count);
+	if (audio->reset_event)
+		return -ENETRESET;
+
+	if (!audio->enabled)
+		return -EFAULT;
+	mutex_lock(&audio->read_lock);
+	while (count > 0) {
+		rc = wait_event_interruptible(
+			audio->read_wait,
+			((atomic_read(&audio->out_count) > 0) ||
+			(audio->stopped) ||
+			 audio->rflush || audio->eos_rsp ||
+			audio->event_abort));
+
+		if (audio->event_abort) {
+			rc = -EIO;
+			break;
+		}
+
+
+		if (rc < 0)
+			break;
+
+		if ((audio->stopped && !(atomic_read(&audio->out_count))) ||
+			audio->rflush) {
+			pr_debug("%s:session id %d: driver in stop state or flush,No more buf to read",
+				__func__,
+				audio->ac->session);
+			rc = 0;/* End of File */
+			break;
+		}
+		if (!(atomic_read(&audio->out_count)) &&
+			(audio->eos_rsp == 1) &&
+			(count >= (sizeof(unsigned char) +
+				sizeof(struct meta_out_dsp)))) {
+			unsigned char num_of_frames;
+
+			pr_info("%s:session id %d: eos %d at output\n",
+				__func__, audio->ac->session, audio->eos_rsp);
+			if (buf != start)
+				break;
+			num_of_frames = 0xFF;
+			if (copy_to_user(buf, &num_of_frames,
+					sizeof(unsigned char))) {
+				rc = -EFAULT;
+				break;
+			}
+			buf += sizeof(unsigned char);
+			meta.frame_size = 0xFFFF;
+			meta.encoded_pcm_samples = 0xFFFF;
+			meta.msw_ts = 0x00;
+			meta.lsw_ts = 0x00;
+			meta.nflags = AUD_EOS_SET;
+			audio->eos_rsp = 0;
+			if (copy_to_user(buf, &meta, sizeof(meta))) {
+				rc = -EFAULT;
+				break;
+			}
+			buf += sizeof(meta);
+			break;
+		}
+		data = (unsigned char *)q6asm_is_cpu_buf_avail(OUT, audio->ac,
+						&size, &idx);
+		if ((count >= (size + mfield_size)) && data) {
+			if (audio->buf_cfg.meta_info_enable) {
+				if (copy_to_user(buf,
+					&audio->out_frame_info[idx][0],
+					sizeof(unsigned char))) {
+					rc = -EFAULT;
+					break;
+				}
+				bytes_to_copy =
+					(size + audio->out_frame_info[idx][1]);
+				/* Number of frames information copied */
+				buf += sizeof(unsigned char);
+				count -= sizeof(unsigned char);
+			} else {
+				offset = audio->out_frame_info[idx][1];
+				bytes_to_copy = size;
+			}
+
+			pr_debug("%s:session id %d: offset=%d nr of frames= %d\n",
+					__func__, audio->ac->session,
+					audio->out_frame_info[idx][1],
+					audio->out_frame_info[idx][0]);
+
+			if (copy_to_user(buf, &data[offset], bytes_to_copy)) {
+				rc = -EFAULT;
+				break;
+			}
+			count -= bytes_to_copy;
+			buf += bytes_to_copy;
+		} else {
+			pr_err("%s:session id %d: short read data[%pK] bytesavail[%d]bytesrequest[%zd]\n",
+				__func__,
+				audio->ac->session,
+				data, size, count);
+		}
+		atomic_dec(&audio->out_count);
+		q6asm_read(audio->ac);
+		break;
+	}
+	mutex_unlock(&audio->read_lock);
+
+	pr_debug("%s:session id %d: read: %zd bytes\n", __func__,
+			audio->ac->session, (buf-start));
+	if (buf > start)
+		return buf - start;
+	return rc;
+}
+
+static int extract_meta_info(char *buf, unsigned long *msw_ts,
+		unsigned long *lsw_ts, unsigned int *flags)
+{
+	struct meta_in *meta = (struct meta_in *)buf;
+	*msw_ts = meta->ntimestamp.highpart;
+	*lsw_ts = meta->ntimestamp.lowpart;
+	*flags = meta->nflags;
+	return 0;
+}
+
+ssize_t audio_in_write(struct file *file,
+		const char __user *buf,
+		size_t count, loff_t *pos)
+{
+	struct q6audio_in *audio = file->private_data;
+	const char __user *start = buf;
+	size_t xfer = 0;
+	char *cpy_ptr;
+	int rc = 0;
+	unsigned char *data;
+	uint32_t size = 0;
+	uint32_t idx = 0;
+	uint32_t nflags = 0;
+	unsigned long msw_ts = 0;
+	unsigned long lsw_ts = 0;
+	uint32_t mfield_size = (audio->buf_cfg.meta_info_enable == 0) ? 0 :
+			sizeof(struct meta_in);
+
+	pr_debug("%s:session id %d: to write[%zd]\n", __func__,
+			audio->ac->session, count);
+	if (audio->reset_event)
+		return -ENETRESET;
+
+	if (!audio->enabled)
+		return -EFAULT;
+	mutex_lock(&audio->write_lock);
+
+	while (count > 0) {
+		rc = wait_event_interruptible(audio->write_wait,
+				     ((atomic_read(&audio->in_count) > 0) ||
+				      (audio->stopped) ||
+				      (audio->wflush) || (audio->event_abort)));
+
+		if (audio->event_abort) {
+			rc = -EIO;
+			break;
+		}
+
+		if (rc < 0)
+			break;
+		if (audio->stopped || audio->wflush) {
+			pr_debug("%s: session id %d: stop or flush\n", __func__,
+					audio->ac->session);
+			rc = -EBUSY;
+			break;
+		}
+		/* if no PCM data, might have only eos buffer
+		 * such case do not hold cpu buffer
+		 */
+		if ((buf == start) && (count == mfield_size)) {
+			char eos_buf[sizeof(struct meta_in)];
+			/* Processing beginning of user buffer */
+			if (copy_from_user(eos_buf, buf, mfield_size)) {
+				rc = -EFAULT;
+				break;
+			}
+			/* Check if EOS flag is set and buffer has
+			 * contains just meta field
+			 */
+			extract_meta_info(eos_buf, &msw_ts, &lsw_ts,
+						&nflags);
+			buf += mfield_size;
+			/* send the EOS and return */
+			pr_debug("%s:session id %d: send EOS 0x%8x\n",
+				__func__,
+				audio->ac->session, nflags);
+			break;
+		}
+		data = (unsigned char *)q6asm_is_cpu_buf_avail(IN, audio->ac,
+						&size, &idx);
+		if (!data) {
+			pr_debug("%s:session id %d: No buf available\n",
+				__func__, audio->ac->session);
+			continue;
+		}
+		cpy_ptr = data;
+		if (audio->buf_cfg.meta_info_enable) {
+			if (buf == start) {
+				/* Processing beginning of user buffer */
+				if (copy_from_user(cpy_ptr, buf, mfield_size)) {
+					rc = -EFAULT;
+					break;
+				}
+				/* Check if EOS flag is set and buffer has
+				 * contains just meta field
+				 */
+				extract_meta_info(cpy_ptr, &msw_ts, &lsw_ts,
+						&nflags);
+				buf += mfield_size;
+				count -= mfield_size;
+			} else {
+				pr_debug("%s:session id %d: continuous buffer\n",
+						__func__, audio->ac->session);
+			}
+		}
+		xfer = (count > (audio->pcm_cfg.buffer_size)) ?
+				(audio->pcm_cfg.buffer_size) : count;
+
+		if (copy_from_user(cpy_ptr, buf, xfer)) {
+			rc = -EFAULT;
+			break;
+		}
+		rc = q6asm_write(audio->ac, xfer, msw_ts, lsw_ts, 0x00);
+		if (rc < 0) {
+			rc = -EFAULT;
+			break;
+		}
+		atomic_dec(&audio->in_count);
+		count -= xfer;
+		buf += xfer;
+	}
+	mutex_unlock(&audio->write_lock);
+	pr_debug("%s:session id %d: eos_condition 0x%x buf[0x%pK] start[0x%pK]\n",
+				__func__, audio->ac->session,
+				nflags, buf, start);
+	if (nflags & AUD_EOS_SET) {
+		rc = q6asm_cmd(audio->ac, CMD_EOS);
+		pr_info("%s:session id %d: eos %d at input\n", __func__,
+				audio->ac->session, audio->eos_rsp);
+	}
+	pr_debug("%s:session id %d: Written %zd Avail Buf[%d]", __func__,
+			audio->ac->session, (buf - start - mfield_size),
+			atomic_read(&audio->in_count));
+	if (!rc) {
+		if (buf > start)
+			return buf - start;
+	}
+	return rc;
+}
+
+int audio_in_release(struct inode *inode, struct file *file)
+{
+	struct q6audio_in  *audio = file->private_data;
+
+	pr_info("%s: session id %d\n", __func__, audio->ac->session);
+	mutex_lock(&audio->lock);
+	audio_in_disable(audio);
+	q6asm_audio_client_free(audio->ac);
+	mutex_unlock(&audio->lock);
+	kfree(audio->enc_cfg);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return 0;
+}
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils.h b/drivers/misc/qcom/qdsp6v2/audio_utils.h
new file mode 100644
index 0000000..f5517d8
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils.h
@@ -0,0 +1,114 @@
+/* Copyright (c) 2010-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/msm_audio.h>
+#include <linux/compat.h>
+#include "q6audio_common.h"
+
+#define FRAME_NUM	(8)
+
+#define PCM_BUF_COUNT		(2)
+
+#define AUD_EOS_SET  0x01
+#define TUNNEL_MODE     0x0000
+#define NON_TUNNEL_MODE 0x0001
+
+#define NO_BUF_ALLOC	0x00
+#define BUF_ALLOC_IN    0x01
+#define BUF_ALLOC_OUT   0x02
+#define BUF_ALLOC_INOUT 0x03
+#define ALIGN_BUF_SIZE(size) ((size + 4095) & (~4095))
+
+struct timestamp {
+	u32 lowpart;
+	u32 highpart;
+} __packed;
+
+struct meta_in {
+	unsigned short offset;
+	struct timestamp ntimestamp;
+	unsigned int nflags;
+} __packed;
+
+struct meta_out_dsp {
+	u32 offset_to_frame;
+	u32 frame_size;
+	u32 encoded_pcm_samples;
+	u32 msw_ts;
+	u32 lsw_ts;
+	u32 nflags;
+} __packed;
+
+struct meta_out {
+	unsigned char num_of_frames;
+	struct meta_out_dsp meta_out_dsp[];
+} __packed;
+
+struct q6audio_in {
+	spinlock_t			dsp_lock;
+	atomic_t			in_bytes;
+	atomic_t			in_samples;
+
+	struct mutex			lock;
+	struct mutex			read_lock;
+	struct mutex			write_lock;
+	wait_queue_head_t		read_wait;
+	wait_queue_head_t		write_wait;
+
+	struct audio_client             *ac;
+	struct msm_audio_stream_config  str_cfg;
+	void				*enc_cfg;
+	struct msm_audio_buf_cfg        buf_cfg;
+	struct msm_audio_config		pcm_cfg;
+	void				*codec_cfg;
+
+	/* number of buffers available to read/write */
+	atomic_t			in_count;
+	atomic_t			out_count;
+
+	/* first idx: num of frames per buf, second idx: offset to frame */
+	uint32_t			out_frame_info[FRAME_NUM][2];
+	int				eos_rsp;
+	int				opened;
+	int				enabled;
+	int				stopped;
+	int				event_abort;
+	int				feedback; /* Flag indicates whether used
+						   * in Non Tunnel mode
+						   */
+	int				rflush;
+	int				wflush;
+	int				buf_alloc;
+	uint16_t			min_frame_size;
+	uint16_t			max_frames_per_buf;
+	bool				reset_event;
+	long (*enc_ioctl)(struct file *, unsigned int, unsigned long);
+	long (*enc_compat_ioctl)(struct file *, unsigned int, unsigned long);
+};
+
+int audio_in_enable(struct q6audio_in  *audio);
+int audio_in_disable(struct q6audio_in  *audio);
+int audio_in_buf_alloc(struct q6audio_in *audio);
+long audio_in_ioctl(struct file *file,
+		unsigned int cmd, unsigned long arg);
+#ifdef CONFIG_COMPAT
+long audio_in_compat_ioctl(struct file *file,
+		unsigned int cmd, unsigned long arg);
+#else
+#define audio_in_compat_ioctl NULL
+#endif
+ssize_t audio_in_read(struct file *file, char __user *buf,
+		size_t count, loff_t *pos);
+ssize_t audio_in_write(struct file *file, const char __user *buf,
+		size_t count, loff_t *pos);
+int audio_in_release(struct inode *inode, struct file *file);
+int audio_in_set_config(struct file *file, struct msm_audio_config *cfg);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
new file mode 100644
index 0000000..73746ee
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.c
@@ -0,0 +1,2132 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include <linux/debugfs.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+#ifdef CONFIG_USE_DEV_CTRL_VOLUME
+#include <linux/qdsp6v2/audio_dev_ctl.h>
+#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+#ifdef CONFIG_DEBUG_FS
+int audio_aio_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	const int debug_bufmax = 4096;
+	static char buffer[4096];
+	int n = 0;
+	struct q6audio_aio *audio = file->private_data;
+
+	mutex_lock(&audio->lock);
+	n = scnprintf(buffer, debug_bufmax, "opened %d\n", audio->opened);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"enabled %d\n", audio->enabled);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"stopped %d\n", audio->stopped);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"feedback %d\n", audio->feedback);
+	mutex_unlock(&audio->lock);
+	/* Following variables are only useful for debugging when
+	 * when playback halts unexpectedly. Thus, no mutual exclusion
+	 * enforced
+	 */
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"wflush %d\n", audio->wflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"rflush %d\n", audio->rflush);
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"inqueue empty %d\n", list_empty(&audio->in_queue));
+	n += scnprintf(buffer + n, debug_bufmax - n,
+			"outqueue empty %d\n", list_empty(&audio->out_queue));
+	buffer[n] = 0;
+	return simple_read_from_buffer(buf, count, ppos, buffer, n);
+}
+#endif
+
+static long audio_aio_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg);
+#ifdef CONFIG_COMPAT
+static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg);
+#else
+#define audio_aio_compat_ioctl NULL
+#endif
+int insert_eos_buf(struct q6audio_aio *audio,
+		struct audio_aio_buffer_node *buf_node)
+{
+	struct dec_meta_out *eos_buf = buf_node->kvaddr;
+
+	pr_debug("%s[%pK]:insert_eos_buf\n", __func__, audio);
+	eos_buf->num_of_frames = 0xFFFFFFFF;
+	eos_buf->meta_out_dsp[0].offset_to_frame = 0x0;
+	eos_buf->meta_out_dsp[0].nflags = AUDIO_DEC_EOS_SET;
+	return sizeof(struct dec_meta_out) +
+		sizeof(eos_buf->meta_out_dsp[0]);
+}
+
+/* Routine which updates read buffers of driver/dsp,
+ * for flush operation as DSP output might not have proper
+ * value set
+ */
+static int insert_meta_data_flush(struct q6audio_aio *audio,
+	struct audio_aio_buffer_node *buf_node)
+{
+	struct dec_meta_out *meta_data = buf_node->kvaddr;
+
+	meta_data->num_of_frames = 0x0;
+	meta_data->meta_out_dsp[0].offset_to_frame = 0x0;
+	meta_data->meta_out_dsp[0].nflags = 0x0;
+	return sizeof(struct dec_meta_out) +
+		sizeof(meta_data->meta_out_dsp[0]);
+}
+
+static int audio_aio_ion_lookup_vaddr(struct q6audio_aio *audio, void *addr,
+					unsigned long len,
+					struct audio_aio_ion_region **region)
+{
+	struct audio_aio_ion_region *region_elt;
+
+	int match_count = 0;
+
+	*region = NULL;
+
+	/* returns physical address or zero */
+	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
+		if (addr >= region_elt->vaddr &&
+			addr < region_elt->vaddr + region_elt->len &&
+			addr + len <= region_elt->vaddr + region_elt->len) {
+			/* offset since we could pass vaddr inside a registered
+			 * ion buffer
+			 */
+
+			match_count++;
+			if (!*region)
+				*region = region_elt;
+		}
+	}
+
+	if (match_count > 1) {
+		pr_err("%s[%pK]:multiple hits for vaddr %pK, len %ld\n",
+			__func__, audio, addr, len);
+		list_for_each_entry(region_elt, &audio->ion_region_queue,
+					list) {
+			if (addr >= region_elt->vaddr &&
+			addr < region_elt->vaddr + region_elt->len &&
+			addr + len <= region_elt->vaddr + region_elt->len)
+				pr_err("\t%s[%pK]:%pK, %ld --> %pK\n",
+					__func__, audio,
+					region_elt->vaddr,
+					region_elt->len,
+					&region_elt->paddr);
+		}
+	}
+
+	return *region ? 0 : -1;
+}
+
+static phys_addr_t audio_aio_ion_fixup(struct q6audio_aio *audio, void *addr,
+				unsigned long len, int ref_up, void **kvaddr)
+{
+	struct audio_aio_ion_region *region;
+	phys_addr_t paddr;
+	int ret;
+
+	ret = audio_aio_ion_lookup_vaddr(audio, addr, len, &region);
+	if (ret) {
+		pr_err("%s[%pK]:lookup (%pK, %ld) failed\n",
+				__func__, audio, addr, len);
+		return 0;
+	}
+	if (ref_up)
+		region->ref_cnt++;
+	else
+		region->ref_cnt--;
+	pr_debug("%s[%pK]:found region %pK ref_cnt %d\n",
+			__func__, audio, region, region->ref_cnt);
+	paddr = region->paddr + (addr - region->vaddr);
+	/* provide kernel virtual address for accessing meta information */
+	if (kvaddr)
+		*kvaddr = (void *) (region->kvaddr + (addr - region->vaddr));
+	return paddr;
+}
+
+static int audio_aio_pause(struct q6audio_aio  *audio)
+{
+	int rc = -EINVAL;
+
+	pr_debug("%s[%pK], enabled = %d\n", __func__, audio,
+			audio->enabled);
+	if (audio->enabled) {
+		rc = q6asm_cmd(audio->ac, CMD_PAUSE);
+		if (rc < 0)
+			pr_err("%s[%pK]: pause cmd failed rc=%d\n",
+				__func__, audio, rc);
+
+		if (rc == 0) {
+			/* Send suspend only if pause was successful */
+			rc = q6asm_cmd(audio->ac, CMD_SUSPEND);
+			if (rc < 0)
+				pr_err("%s[%pK]: suspend cmd failed rc=%d\n",
+					__func__, audio, rc);
+		} else
+			pr_err("%s[%pK]: not sending suspend since pause failed\n",
+				__func__, audio);
+
+	} else
+		pr_err("%s[%pK]: Driver not enabled\n", __func__, audio);
+	return rc;
+}
+
+static int audio_aio_flush(struct q6audio_aio  *audio)
+{
+	int rc = 0;
+
+	if (audio->enabled) {
+		/* Implicitly issue a pause to the decoder before flushing if
+		 * it is not in pause state
+		 */
+		if (!(audio->drv_status & ADRV_STATUS_PAUSE)) {
+			rc = audio_aio_pause(audio);
+			if (rc < 0)
+				pr_err("%s[%pK}: pause cmd failed rc=%d\n",
+					__func__, audio,
+					rc);
+			else
+				audio->drv_status |= ADRV_STATUS_PAUSE;
+		}
+		rc = q6asm_cmd(audio->ac, CMD_FLUSH);
+		if (rc < 0)
+			pr_err("%s[%pK]: flush cmd failed rc=%d\n",
+				__func__, audio, rc);
+		/* Not in stop state, reenable the stream */
+		if (audio->stopped == 0) {
+			rc = audio_aio_enable(audio);
+			if (rc)
+				pr_err("%s[%pK]:audio re-enable failed\n",
+					__func__, audio);
+			else {
+				audio->enabled = 1;
+				if (audio->drv_status & ADRV_STATUS_PAUSE)
+					audio->drv_status &= ~ADRV_STATUS_PAUSE;
+			}
+		}
+	}
+	pr_debug("%s[%pK]:in_bytes %d\n",
+			__func__, audio, atomic_read(&audio->in_bytes));
+	pr_debug("%s[%pK]:in_samples %d\n",
+			__func__, audio, atomic_read(&audio->in_samples));
+	atomic_set(&audio->in_bytes, 0);
+	atomic_set(&audio->in_samples, 0);
+	return rc;
+}
+
+static int audio_aio_outport_flush(struct q6audio_aio *audio)
+{
+	int rc;
+
+	rc = q6asm_cmd(audio->ac, CMD_OUT_FLUSH);
+	if (rc < 0)
+		pr_err("%s[%pK}: output port flush cmd failed rc=%d\n",
+			__func__, audio, rc);
+	return rc;
+}
+
+/* Write buffer to DSP / Handle Ack from DSP */
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+				uint32_t *payload)
+{
+	unsigned long flags;
+	union msm_audio_event_payload event_payload;
+	struct audio_aio_buffer_node *used_buf;
+
+	/* No active flush in progress */
+	if (audio->wflush)
+		return;
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	if (list_empty(&audio->out_queue)) {
+		pr_warn("%s: ignore unexpected event from dsp\n", __func__);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		return;
+	}
+	used_buf = list_first_entry(&audio->out_queue,
+					struct audio_aio_buffer_node, list);
+	if (token == used_buf->token) {
+		list_del(&used_buf->list);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		pr_debug("%s[%pK]:consumed buffer\n", __func__, audio);
+		event_payload.aio_buf = used_buf->buf;
+		audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+					event_payload);
+		kfree(used_buf);
+		if (list_empty(&audio->out_queue) &&
+			(audio->drv_status & ADRV_STATUS_FSYNC)) {
+			pr_debug("%s[%pK]: list is empty, reached EOS in Tunnel\n",
+				 __func__, audio);
+			wake_up(&audio->write_wait);
+		}
+	} else {
+		pr_err("%s[%pK]:expected=%x ret=%x\n",
+			__func__, audio, used_buf->token, token);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	}
+}
+
+/* ------------------- device --------------------- */
+void audio_aio_async_out_flush(struct q6audio_aio *audio)
+{
+	struct audio_aio_buffer_node *buf_node;
+	struct list_head *ptr, *next;
+	union msm_audio_event_payload payload;
+	unsigned long flags;
+
+	pr_debug("%s[%pK}\n", __func__, audio);
+	/* EOS followed by flush, EOS response not guranteed, free EOS i/p
+	 * buffer
+	 */
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+
+	if (audio->eos_flag && (audio->eos_write_payload.aio_buf.buf_addr)) {
+		pr_debug("%s[%pK]: EOS followed by flush received,acknowledge eos i/p buffer immediately\n",
+			__func__, audio);
+		audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE,
+				audio->eos_write_payload);
+		memset(&audio->eos_write_payload, 0,
+			sizeof(union msm_audio_event_payload));
+	}
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	list_for_each_safe(ptr, next, &audio->out_queue) {
+		buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
+		list_del(&buf_node->list);
+		payload.aio_buf = buf_node->buf;
+		audio_aio_post_event(audio, AUDIO_EVENT_WRITE_DONE, payload);
+		kfree(buf_node);
+		pr_debug("%s[%pK]: Propagate WRITE_DONE during flush\n",
+				__func__, audio);
+	}
+}
+
+void audio_aio_async_in_flush(struct q6audio_aio *audio)
+{
+	struct audio_aio_buffer_node *buf_node;
+	struct list_head *ptr, *next;
+	union msm_audio_event_payload payload;
+
+	pr_debug("%s[%pK]\n", __func__, audio);
+	list_for_each_safe(ptr, next, &audio->in_queue) {
+		buf_node = list_entry(ptr, struct audio_aio_buffer_node, list);
+		list_del(&buf_node->list);
+		/* Forcefull send o/p eos buffer after flush, if no eos response
+		 * received by dsp even after sending eos command
+		 */
+		if ((audio->eos_rsp != 1) && audio->eos_flag) {
+			pr_debug("%s[%pK]: send eos on o/p buffer during flush\n",
+				 __func__, audio);
+			payload.aio_buf = buf_node->buf;
+			payload.aio_buf.data_len =
+					insert_eos_buf(audio, buf_node);
+			audio->eos_flag = 0;
+		} else {
+			payload.aio_buf = buf_node->buf;
+			payload.aio_buf.data_len =
+					insert_meta_data_flush(audio, buf_node);
+		}
+		audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE, payload);
+		kfree(buf_node);
+		pr_debug("%s[%pK]: Propagate READ_DONE during flush\n",
+				__func__, audio);
+	}
+}
+
+int audio_aio_enable(struct q6audio_aio  *audio)
+{
+	/* 2nd arg: 0 -> run immediately
+	 * 3rd arg: 0 -> msw_ts,
+	 * 4th arg: 0 ->lsw_ts
+	 */
+	return q6asm_run(audio->ac, 0x00, 0x00, 0x00);
+}
+
+int audio_aio_disable(struct q6audio_aio *audio)
+{
+	int rc = 0;
+
+	if (audio->opened) {
+		audio->enabled = 0;
+		audio->opened = 0;
+		pr_debug("%s[%pK]: inbytes[%d] insamples[%d]\n", __func__,
+			audio, atomic_read(&audio->in_bytes),
+			atomic_read(&audio->in_samples));
+		/* Close the session */
+		rc = q6asm_cmd(audio->ac, CMD_CLOSE);
+		if (rc < 0)
+			pr_err("%s[%pK]:Failed to close the session rc=%d\n",
+				__func__, audio, rc);
+		audio->stopped = 1;
+		wake_up(&audio->write_wait);
+		wake_up(&audio->cmd_wait);
+	}
+	pr_debug("%s[%pK]:enabled[%d]\n", __func__, audio, audio->enabled);
+	return rc;
+}
+
+void audio_aio_reset_ion_region(struct q6audio_aio *audio)
+{
+	struct audio_aio_ion_region *region;
+	struct list_head *ptr, *next;
+
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audio_aio_ion_region, list);
+		list_del(&region->list);
+		msm_audio_ion_free_legacy(audio->client, region->handle);
+		kfree(region);
+	}
+}
+
+void audio_aio_reset_event_queue(struct q6audio_aio *audio)
+{
+	unsigned long flags;
+	struct audio_aio_event *drv_evt;
+	struct list_head *ptr, *next;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	list_for_each_safe(ptr, next, &audio->event_queue) {
+		drv_evt = list_first_entry(&audio->event_queue,
+				   struct audio_aio_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		drv_evt = list_first_entry(&audio->free_event_queue,
+				   struct audio_aio_event, list);
+		list_del(&drv_evt->list);
+		kfree(drv_evt);
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+}
+
+static void audio_aio_unmap_ion_region(struct q6audio_aio *audio)
+{
+	struct audio_aio_ion_region *region;
+	struct list_head *ptr, *next;
+	int rc = -EINVAL;
+
+	pr_debug("%s[%pK]:\n", __func__, audio);
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audio_aio_ion_region, list);
+		if (region != NULL) {
+			pr_debug("%s[%pK]: phy_address = 0x%pK\n",
+				__func__, audio, &region->paddr);
+			rc = q6asm_memory_unmap(audio->ac,
+						region->paddr, IN);
+			if (rc < 0)
+				pr_err("%s[%pK]: memory unmap failed\n",
+					__func__, audio);
+		}
+	}
+}
+
+#ifdef CONFIG_USE_DEV_CTRL_VOLUME
+
+static void audio_aio_listner(u32 evt_id, union auddev_evt_data *evt_payload,
+			void *private_data)
+{
+	struct q6audio_aio *audio = (struct q6audio_aio *) private_data;
+	int rc  = 0;
+
+	switch (evt_id) {
+	case AUDDEV_EVT_STREAM_VOL_CHG:
+		audio->volume = evt_payload->session_vol;
+		pr_debug("%s[%pK]: AUDDEV_EVT_STREAM_VOL_CHG, stream vol %d, enabled = %d\n",
+			__func__, audio, audio->volume, audio->enabled);
+		if (audio->enabled == 1) {
+			if (audio->ac) {
+				rc = q6asm_set_volume(audio->ac, audio->volume);
+				if (rc < 0) {
+					pr_err("%s[%pK]: Send Volume command failed rc=%d\n",
+						__func__, audio, rc);
+				}
+			}
+		}
+		break;
+	default:
+		pr_err("%s[%pK]:ERROR:wrong event\n", __func__, audio);
+		break;
+	}
+}
+
+int register_volume_listener(struct q6audio_aio *audio)
+{
+	int rc  = 0;
+
+	audio->device_events = AUDDEV_EVT_STREAM_VOL_CHG;
+	audio->drv_status &= ~ADRV_STATUS_PAUSE;
+
+	rc = auddev_register_evt_listner(audio->device_events,
+					AUDDEV_CLNT_DEC,
+					audio->ac->session,
+					audio_aio_listner,
+					(void *)audio);
+	if (rc < 0) {
+		pr_err("%s[%pK]: Event listener failed\n", __func__, audio);
+		rc = -EACCES;
+	}
+	return rc;
+}
+void unregister_volume_listener(struct q6audio_aio *audio)
+{
+	auddev_unregister_evt_listner(AUDDEV_CLNT_DEC, audio->ac->session);
+}
+
+int enable_volume_ramp(struct q6audio_aio *audio)
+{
+	int rc = 0;
+	struct asm_softpause_params softpause;
+	struct asm_softvolume_params softvol;
+
+	if (audio->ac == NULL)
+		return -EINVAL;
+	pr_debug("%s[%pK]\n", __func__, audio);
+	softpause.enable = SOFT_PAUSE_ENABLE;
+	softpause.period = SOFT_PAUSE_PERIOD;
+	softpause.step = SOFT_PAUSE_STEP;
+	softpause.rampingcurve = SOFT_PAUSE_CURVE_LINEAR;
+
+	softvol.period = SOFT_VOLUME_PERIOD;
+	softvol.step = SOFT_VOLUME_STEP;
+	softvol.rampingcurve = SOFT_VOLUME_CURVE_LINEAR;
+
+	if (softpause.rampingcurve == SOFT_PAUSE_CURVE_LINEAR)
+		softpause.step = SOFT_PAUSE_STEP_LINEAR;
+	if (softvol.rampingcurve == SOFT_VOLUME_CURVE_LINEAR)
+		softvol.step = SOFT_VOLUME_STEP_LINEAR;
+	rc = q6asm_set_volume(audio->ac, audio->volume);
+	if (rc < 0) {
+		pr_err("%s: Send Volume command failed rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+	rc = q6asm_set_softpause(audio->ac, &softpause);
+	if (rc < 0) {
+		pr_err("%s: Send SoftPause Param failed rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+	rc = q6asm_set_softvolume(audio->ac, &softvol);
+	if (rc < 0) {
+		pr_err("%s: Send SoftVolume Param failed rc=%d\n",
+		__func__, rc);
+		return rc;
+	}
+	/* disable mute by default */
+	rc = q6asm_set_mute(audio->ac, 0);
+	if (rc < 0) {
+		pr_err("%s: Send mute command failed rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+	return rc;
+}
+
+#else /*CONFIG_USE_DEV_CTRL_VOLUME*/
+int register_volume_listener(struct q6audio_aio *audio)
+{
+	return 0;/* do nothing */
+}
+void unregister_volume_listener(struct q6audio_aio *audio)
+{
+	return;/* do nothing */
+}
+int enable_volume_ramp(struct q6audio_aio *audio)
+{
+	return 0; /* do nothing */
+}
+#endif /*CONFIG_USE_DEV_CTRL_VOLUME*/
+
+int audio_aio_release(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = file->private_data;
+
+	pr_debug("%s[%pK]\n", __func__, audio);
+	mutex_lock(&audio->lock);
+	mutex_lock(&audio->read_lock);
+	mutex_lock(&audio->write_lock);
+	audio->wflush = 1;
+	if (audio->wakelock_voted &&
+		(audio->audio_ws_mgr != NULL) &&
+		(audio->miscdevice != NULL)) {
+		audio->wakelock_voted = false;
+		mutex_lock(&audio->audio_ws_mgr->ws_lock);
+		if ((audio->audio_ws_mgr->ref_cnt > 0) &&
+				(--audio->audio_ws_mgr->ref_cnt == 0)) {
+			pm_relax(audio->miscdevice->this_device);
+		}
+		mutex_unlock(&audio->audio_ws_mgr->ws_lock);
+	}
+	if (audio->enabled)
+		audio_aio_flush(audio);
+	audio->wflush = 0;
+	audio->drv_ops.out_flush(audio);
+	audio->drv_ops.in_flush(audio);
+	audio_aio_disable(audio);
+	audio_aio_unmap_ion_region(audio);
+	audio_aio_reset_ion_region(audio);
+	msm_audio_ion_client_destroy(audio->client);
+	audio->event_abort = 1;
+	wake_up(&audio->event_wait);
+	audio_aio_reset_event_queue(audio);
+	q6asm_audio_client_free(audio->ac);
+	mutex_unlock(&audio->write_lock);
+	mutex_unlock(&audio->read_lock);
+	mutex_unlock(&audio->lock);
+	mutex_destroy(&audio->lock);
+	mutex_destroy(&audio->read_lock);
+	mutex_destroy(&audio->write_lock);
+	mutex_destroy(&audio->get_event_lock);
+	unregister_volume_listener(audio);
+
+#ifdef CONFIG_DEBUG_FS
+	debugfs_remove(audio->dentry);
+#endif
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return 0;
+}
+
+int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync)
+{
+	int rc = 0;
+	struct q6audio_aio *audio = file->private_data;
+
+	if (!audio->enabled || audio->feedback)
+		return -EINVAL;
+
+	/* Blocking client sends more data */
+	mutex_lock(&audio->lock);
+	audio->drv_status |= ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	pr_debug("%s[%pK]:\n", __func__, audio);
+
+	audio->eos_rsp = 0;
+
+	pr_debug("%s[%pK]Wait for write done from DSP\n", __func__, audio);
+	rc = wait_event_interruptible(audio->write_wait,
+					(list_empty(&audio->out_queue)) ||
+					audio->wflush || audio->stopped);
+
+	if (audio->stopped || audio->wflush) {
+		pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n"
+			, __func__, audio);
+		audio->wflush = 0;
+		rc = -EBUSY;
+	}
+
+	if (rc < 0) {
+		pr_err("%s[%pK]: wait event for list_empty failed, rc = %d\n",
+			__func__, audio, rc);
+		goto done;
+	}
+
+	rc = q6asm_cmd(audio->ac, CMD_EOS);
+	pr_debug("%s[%pK]: EOS cmd sent to DSP\n", __func__, audio);
+
+	if (rc < 0)
+		pr_err("%s[%pK]: q6asm_cmd failed, rc = %d",
+			__func__, audio, rc);
+
+	pr_debug("%s[%pK]: wait for RENDERED_EOS from DSP\n"
+		, __func__, audio);
+	rc = wait_event_interruptible(audio->write_wait,
+					(audio->eos_rsp || audio->wflush ||
+					audio->stopped));
+
+	if (rc < 0) {
+		pr_err("%s[%pK]: wait event for eos_rsp failed, rc = %d\n",
+			__func__, audio, rc);
+		goto done;
+	}
+
+	if (audio->stopped || audio->wflush) {
+		audio->wflush = 0;
+		pr_debug("%s[%pK]: Audio Flushed or Stopped,this is not EOS\n"
+			, __func__, audio);
+		rc = -EBUSY;
+	}
+
+	if (audio->eos_rsp == 1)
+		pr_debug("%s[%pK]: EOS\n", __func__, audio);
+
+
+done:
+	mutex_lock(&audio->lock);
+	audio->drv_status &= ~ADRV_STATUS_FSYNC;
+	mutex_unlock(&audio->lock);
+
+	return rc;
+}
+
+static int audio_aio_events_pending(struct q6audio_aio *audio)
+{
+	unsigned long flags;
+	int empty;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	empty = !list_empty(&audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	return empty || audio->event_abort || audio->reset_event;
+}
+
+static long audio_aio_process_event_req_common(struct q6audio_aio *audio,
+					struct msm_audio_event *usr_evt)
+{
+	long rc;
+	struct audio_aio_event *drv_evt = NULL;
+	int timeout;
+	unsigned long flags;
+
+	timeout = usr_evt->timeout_ms;
+
+	if (timeout > 0) {
+		rc = wait_event_interruptible_timeout(audio->event_wait,
+						audio_aio_events_pending
+						(audio),
+						msecs_to_jiffies
+						(timeout));
+		if (rc == 0)
+			return -ETIMEDOUT;
+	} else {
+		rc = wait_event_interruptible(audio->event_wait,
+		audio_aio_events_pending(audio));
+	}
+	if (rc < 0)
+		return rc;
+
+	if (audio->reset_event) {
+		audio->reset_event = false;
+		pr_err("In SSR, post ENETRESET err\n");
+		return -ENETRESET;
+	}
+
+	if (audio->event_abort) {
+		audio->event_abort = 0;
+		return -ENODEV;
+	}
+
+	rc = 0;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+	if (!list_empty(&audio->event_queue)) {
+		drv_evt = list_first_entry(&audio->event_queue,
+		   struct audio_aio_event, list);
+		list_del(&drv_evt->list);
+	}
+	if (drv_evt) {
+		usr_evt->event_type = drv_evt->event_type;
+		usr_evt->event_payload = drv_evt->payload;
+		list_add_tail(&drv_evt->list, &audio->free_event_queue);
+	} else {
+		pr_err("%s[%pK]:Unexpected path\n", __func__, audio);
+		spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+		return -EPERM;
+	}
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+
+	if (drv_evt->event_type == AUDIO_EVENT_WRITE_DONE) {
+		pr_debug("%s[%pK]:posted AUDIO_EVENT_WRITE_DONE to user\n",
+			__func__, audio);
+		mutex_lock(&audio->write_lock);
+		audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+		drv_evt->payload.aio_buf.buf_len, 0, 0);
+		mutex_unlock(&audio->write_lock);
+	} else if (drv_evt->event_type == AUDIO_EVENT_READ_DONE) {
+		pr_debug("%s[%pK]:posted AUDIO_EVENT_READ_DONE to user\n",
+			__func__, audio);
+		mutex_lock(&audio->read_lock);
+		audio_aio_ion_fixup(audio, drv_evt->payload.aio_buf.buf_addr,
+		drv_evt->payload.aio_buf.buf_len, 0, 0);
+		mutex_unlock(&audio->read_lock);
+	}
+
+	/* Some read buffer might be held up in DSP,release all
+	 * Once EOS indicated
+	 */
+	if (audio->eos_rsp && !list_empty(&audio->in_queue)) {
+		pr_debug("%s[%pK]:Send flush command to release read buffers held up in DSP\n",
+			__func__, audio);
+		mutex_lock(&audio->lock);
+		audio_aio_flush(audio);
+		mutex_unlock(&audio->lock);
+	}
+
+	return rc;
+}
+
+static long audio_aio_process_event_req(struct q6audio_aio *audio,
+					void __user *arg)
+{
+	long rc;
+	struct msm_audio_event usr_evt;
+
+	if (copy_from_user(&usr_evt, arg, sizeof(struct msm_audio_event))) {
+		pr_err("%s: copy_from_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	rc = audio_aio_process_event_req_common(audio, &usr_evt);
+
+	if (copy_to_user(arg, &usr_evt, sizeof(usr_evt))) {
+		pr_err("%s: copy_to_user failed\n", __func__);
+		rc = -EFAULT;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+
+struct msm_audio_aio_buf32 {
+	compat_uptr_t buf_addr;
+	u32 buf_len;
+	u32 data_len;
+	compat_uptr_t private_data;
+	u16 mfield_sz; /*only useful for data has meta field */
+};
+
+struct msm_audio_bitstream_info32 {
+	u32 codec_type;
+	u32 chan_info;
+	u32 sample_rate;
+	u32 bit_stream_info;
+	u32 bit_rate;
+	u32 unused[3];
+};
+
+struct msm_audio_bitstream_error_info32 {
+	u32 dec_id;
+	u32 err_msg_indicator;
+	u32 err_type;
+};
+
+union msm_audio_event_payload32 {
+	struct msm_audio_aio_buf32 aio_buf;
+	struct msm_audio_bitstream_info32 stream_info;
+	struct msm_audio_bitstream_error_info32 error_info;
+	s32 reserved;
+};
+
+struct msm_audio_event32 {
+	s32 event_type;
+	s32 timeout_ms;
+	union msm_audio_event_payload32 event_payload;
+};
+
+static long audio_aio_process_event_req_compat(struct q6audio_aio *audio,
+					void __user *arg)
+{
+	long rc;
+	struct msm_audio_event32 usr_evt_32;
+	struct msm_audio_event usr_evt;
+
+	if (copy_from_user(&usr_evt_32, arg,
+				sizeof(struct msm_audio_event32))) {
+		pr_err("%s: copy_from_user failed\n", __func__);
+		return -EFAULT;
+	}
+	usr_evt.timeout_ms = usr_evt_32.timeout_ms;
+
+	rc = audio_aio_process_event_req_common(audio, &usr_evt);
+
+	usr_evt_32.event_type = usr_evt.event_type;
+	switch (usr_evt_32.event_type) {
+	case AUDIO_EVENT_SUSPEND:
+	case AUDIO_EVENT_RESUME:
+	case AUDIO_EVENT_WRITE_DONE:
+	case AUDIO_EVENT_READ_DONE:
+		usr_evt_32.event_payload.aio_buf.buf_addr =
+			ptr_to_compat(usr_evt.event_payload.aio_buf.buf_addr);
+		usr_evt_32.event_payload.aio_buf.buf_len =
+			usr_evt.event_payload.aio_buf.buf_len;
+		usr_evt_32.event_payload.aio_buf.data_len =
+			usr_evt.event_payload.aio_buf.data_len;
+		usr_evt_32.event_payload.aio_buf.private_data =
+		ptr_to_compat(usr_evt.event_payload.aio_buf.private_data);
+		usr_evt_32.event_payload.aio_buf.mfield_sz =
+			usr_evt.event_payload.aio_buf.mfield_sz;
+		break;
+	case AUDIO_EVENT_STREAM_INFO:
+		usr_evt_32.event_payload.stream_info.codec_type =
+			usr_evt.event_payload.stream_info.codec_type;
+		usr_evt_32.event_payload.stream_info.chan_info =
+			usr_evt.event_payload.stream_info.chan_info;
+		usr_evt_32.event_payload.stream_info.sample_rate =
+			usr_evt.event_payload.stream_info.sample_rate;
+		usr_evt_32.event_payload.stream_info.bit_stream_info =
+			usr_evt.event_payload.stream_info.bit_stream_info;
+		usr_evt_32.event_payload.stream_info.bit_rate =
+			usr_evt.event_payload.stream_info.bit_rate;
+		break;
+	case AUDIO_EVENT_BITSTREAM_ERROR_INFO:
+		usr_evt_32.event_payload.error_info.dec_id =
+			usr_evt.event_payload.error_info.dec_id;
+		usr_evt_32.event_payload.error_info.err_msg_indicator =
+			usr_evt.event_payload.error_info.err_msg_indicator;
+		usr_evt_32.event_payload.error_info.err_type =
+			usr_evt.event_payload.error_info.err_type;
+		break;
+	default:
+		pr_debug("%s: unknown audio event type = %d rc = %ld",
+			 __func__, usr_evt_32.event_type, rc);
+		return rc;
+	}
+	if (copy_to_user(arg, &usr_evt_32, sizeof(usr_evt_32))) {
+		pr_err("%s: copy_to_user failed\n", __func__);
+		rc = -EFAULT;
+	}
+	return rc;
+}
+#endif
+
+static int audio_aio_ion_check(struct q6audio_aio *audio,
+				void *vaddr, unsigned long len)
+{
+	struct audio_aio_ion_region *region_elt;
+	struct audio_aio_ion_region t = {.vaddr = vaddr, .len = len };
+
+	list_for_each_entry(region_elt, &audio->ion_region_queue, list) {
+		if (CONTAINS(region_elt, &t) || CONTAINS(&t, region_elt) ||
+			OVERLAPS(region_elt, &t)) {
+			pr_err("%s[%pK]:region (vaddr %pK len %ld) clashes with registered region (vaddr %pK paddr %pK len %ld)\n",
+				__func__, audio, vaddr, len,
+				region_elt->vaddr,
+				&region_elt->paddr, region_elt->len);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int audio_aio_ion_add(struct q6audio_aio *audio,
+				struct msm_audio_ion_info *info)
+{
+	ion_phys_addr_t paddr = 0;
+	size_t len = 0;
+	struct audio_aio_ion_region *region;
+	int rc = -EINVAL;
+	struct ion_handle *handle = NULL;
+	unsigned long ionflag;
+	void *kvaddr = NULL;
+
+	pr_debug("%s[%pK]:\n", __func__, audio);
+	region = kmalloc(sizeof(*region), GFP_KERNEL);
+
+	if (!region) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	rc = msm_audio_ion_import_legacy("Audio_Dec_Client", audio->client,
+				&handle, info->fd, &ionflag,
+				0, &paddr, &len, &kvaddr);
+	if (rc) {
+		pr_err("%s: msm audio ion alloc failed\n", __func__);
+		goto import_error;
+	}
+
+	rc = audio_aio_ion_check(audio, info->vaddr, len);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_ion_check failed\n", __func__);
+		goto ion_error;
+	}
+
+	region->handle = handle;
+	region->vaddr = info->vaddr;
+	region->fd = info->fd;
+	region->paddr = paddr;
+	region->kvaddr = kvaddr;
+	region->len = len;
+	region->ref_cnt = 0;
+	pr_debug("%s[%pK]:add region paddr %pK vaddr %pK, len %lu kvaddr %pK\n",
+		__func__, audio,
+		&region->paddr, region->vaddr, region->len,
+		region->kvaddr);
+	list_add_tail(&region->list, &audio->ion_region_queue);
+	rc = q6asm_memory_map(audio->ac,  paddr, IN, len, 1);
+	if (rc < 0) {
+		pr_err("%s[%pK]: memory map failed\n", __func__, audio);
+		goto mmap_error;
+	} else {
+		goto end;
+	}
+mmap_error:
+	list_del(&region->list);
+ion_error:
+	msm_audio_ion_free_legacy(audio->client, handle);
+import_error:
+	kfree(region);
+end:
+	return rc;
+}
+
+static int audio_aio_ion_remove(struct q6audio_aio *audio,
+				struct msm_audio_ion_info *info)
+{
+	struct audio_aio_ion_region *region;
+	struct list_head *ptr, *next;
+	int rc = -EINVAL;
+
+	pr_debug("%s[%pK]:info fd %d vaddr %pK\n",
+		__func__, audio, info->fd, info->vaddr);
+
+	list_for_each_safe(ptr, next, &audio->ion_region_queue) {
+		region = list_entry(ptr, struct audio_aio_ion_region, list);
+
+		if ((region->fd == info->fd) &&
+			(region->vaddr == info->vaddr)) {
+			if (region->ref_cnt) {
+				pr_debug("%s[%pK]:region %pK in use ref_cnt %d\n",
+					__func__, audio, region,
+					region->ref_cnt);
+				break;
+			}
+			pr_debug("%s[%pK]:remove region fd %d vaddr %pK\n",
+				__func__, audio, info->fd, info->vaddr);
+			rc = q6asm_memory_unmap(audio->ac,
+						region->paddr, IN);
+			if (rc < 0)
+				pr_err("%s[%pK]: memory unmap failed\n",
+					__func__, audio);
+
+			list_del(&region->list);
+			msm_audio_ion_free_legacy(audio->client,
+						 region->handle);
+			kfree(region);
+			rc = 0;
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static int audio_aio_async_write(struct q6audio_aio *audio,
+				struct audio_aio_buffer_node *buf_node)
+{
+	int rc;
+	struct audio_client *ac;
+	struct audio_aio_write_param param;
+
+	if (!audio || !buf_node) {
+		pr_err("%s NULL pointer audio=[0x%pK], buf_node=[0x%pK]\n",
+			__func__, audio, buf_node);
+		return -EINVAL;
+	}
+	pr_debug("%s[%pK]: Send write buff %pK phy %pK len %d meta_enable = %d\n",
+		__func__, audio, buf_node, &buf_node->paddr,
+		buf_node->buf.data_len,
+		audio->buf_cfg.meta_info_enable);
+	pr_debug("%s[%pK]: flags = 0x%x\n", __func__, audio,
+		buf_node->meta_info.meta_in.nflags);
+
+	ac = audio->ac;
+	/* Offset with  appropriate meta */
+	if (audio->feedback) {
+		/* Non Tunnel mode */
+		param.paddr = buf_node->paddr + sizeof(struct dec_meta_in);
+		param.len = buf_node->buf.data_len - sizeof(struct dec_meta_in);
+	} else {
+		/* Tunnel mode */
+		param.paddr = buf_node->paddr;
+		param.len = buf_node->buf.data_len;
+	}
+	param.msw_ts = buf_node->meta_info.meta_in.ntimestamp.highpart;
+	param.lsw_ts = buf_node->meta_info.meta_in.ntimestamp.lowpart;
+	param.flags  = buf_node->meta_info.meta_in.nflags;
+	/* If no meta_info enaled, indicate no time stamp valid */
+	if (!audio->buf_cfg.meta_info_enable)
+		param.flags = 0xFF00;
+
+	if (buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOF_SET)
+		param.flags |= AUDIO_DEC_EOF_SET;
+
+	param.uid = ac->session;
+	/* Read command will populate session id as token */
+	buf_node->token = ac->session;
+	rc = q6asm_async_write(ac, &param);
+	if (rc < 0)
+		pr_err("%s[%pK]:failed\n", __func__, audio);
+	return rc;
+}
+
+void audio_aio_post_event(struct q6audio_aio *audio, int type,
+			union msm_audio_event_payload payload)
+{
+	struct audio_aio_event *e_node = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&audio->event_queue_lock, flags);
+
+	if (!list_empty(&audio->free_event_queue)) {
+		e_node = list_first_entry(&audio->free_event_queue,
+					struct audio_aio_event, list);
+		list_del(&e_node->list);
+	} else {
+		e_node = kmalloc(sizeof(struct audio_aio_event), GFP_ATOMIC);
+		if (!e_node) {
+			spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+			return;
+		}
+	}
+
+	e_node->event_type = type;
+	e_node->payload = payload;
+
+	list_add_tail(&e_node->list, &audio->event_queue);
+	spin_unlock_irqrestore(&audio->event_queue_lock, flags);
+	wake_up(&audio->event_wait);
+}
+
+static int audio_aio_async_read(struct q6audio_aio *audio,
+				struct audio_aio_buffer_node *buf_node)
+{
+	struct audio_client *ac;
+	struct audio_aio_read_param param;
+	int rc;
+
+	pr_debug("%s[%pK]: Send read buff %pK phy %pK len %d\n",
+		__func__, audio, buf_node,
+		&buf_node->paddr, buf_node->buf.buf_len);
+	ac = audio->ac;
+	/* Provide address so driver can append nr frames information */
+	param.paddr = buf_node->paddr +
+		sizeof(struct dec_meta_out);
+	param.len = buf_node->buf.buf_len -
+		sizeof(struct dec_meta_out);
+	param.uid = ac->session;
+	/* Write command will populate session_id as token */
+	buf_node->token = ac->session;
+	rc = q6asm_async_read(ac, &param);
+	if (rc < 0)
+		pr_err("%s[%pK]:failed\n", __func__, audio);
+	return rc;
+}
+
+static int audio_aio_buf_add_shared(struct q6audio_aio *audio, u32 dir,
+				struct audio_aio_buffer_node *buf_node)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	pr_debug("%s[%pK]:node %pK dir %x buf_addr %pK buf_len %d data_len %d\n",
+		 __func__, audio, buf_node, dir, buf_node->buf.buf_addr,
+		buf_node->buf.buf_len, buf_node->buf.data_len);
+	buf_node->paddr = audio_aio_ion_fixup(audio, buf_node->buf.buf_addr,
+						buf_node->buf.buf_len, 1,
+						&buf_node->kvaddr);
+	if (dir) {
+		/* write */
+		if (!buf_node->paddr ||
+			(buf_node->paddr & 0x1) ||
+			(!audio->feedback && !buf_node->buf.data_len)) {
+			kfree(buf_node);
+			return -EINVAL;
+		}
+		extract_meta_out_info(audio, buf_node, 1);
+		/* Not a EOS buffer */
+		if (!(buf_node->meta_info.meta_in.nflags & AUDIO_DEC_EOS_SET)) {
+			spin_lock_irqsave(&audio->dsp_lock, flags);
+			ret = audio_aio_async_write(audio, buf_node);
+			/* EOS buffer handled in driver */
+			list_add_tail(&buf_node->list, &audio->out_queue);
+			spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		} else if (buf_node->meta_info.meta_in.nflags
+				   & AUDIO_DEC_EOS_SET) {
+			if (!audio->wflush) {
+				pr_debug("%s[%pK]:Send EOS cmd at i/p\n",
+					__func__, audio);
+				/* Driver will forcefully post writedone event
+				 * once eos ack recived from DSP
+				 */
+				audio->eos_write_payload.aio_buf =
+					buf_node->buf;
+				audio->eos_flag = 1;
+				audio->eos_rsp = 0;
+				q6asm_cmd(audio->ac, CMD_EOS);
+				kfree(buf_node);
+			} else { /* Flush in progress, send back i/p
+				  * EOS buffer as is
+				  */
+				union msm_audio_event_payload event_payload;
+
+				event_payload.aio_buf = buf_node->buf;
+				audio_aio_post_event(audio,
+						AUDIO_EVENT_WRITE_DONE,
+						event_payload);
+				kfree(buf_node);
+			}
+		}
+	} else {
+		/* read */
+		if (!buf_node->paddr ||
+			(buf_node->paddr & 0x1) ||
+			(buf_node->buf.buf_len < PCM_BUFSZ_MIN)) {
+			kfree(buf_node);
+			return -EINVAL;
+		}
+		/* No EOS reached */
+		if (!audio->eos_rsp) {
+			spin_lock_irqsave(&audio->dsp_lock, flags);
+			ret = audio_aio_async_read(audio, buf_node);
+			/* EOS buffer handled in driver */
+			list_add_tail(&buf_node->list, &audio->in_queue);
+			spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		}
+		/* EOS reached at input side fake all upcoming read buffer to
+		 * indicate the same
+		 */
+		else {
+			union msm_audio_event_payload event_payload;
+
+			event_payload.aio_buf = buf_node->buf;
+			event_payload.aio_buf.data_len =
+				insert_eos_buf(audio, buf_node);
+			pr_debug("%s[%pK]: propagate READ_DONE as EOS done\n",
+				__func__, audio);
+			audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
+					event_payload);
+			kfree(buf_node);
+		}
+	}
+	return ret;
+}
+#ifdef CONFIG_COMPAT
+static int audio_aio_buf_add_compat(struct q6audio_aio *audio, u32 dir,
+				void __user *arg)
+{
+	struct audio_aio_buffer_node *buf_node;
+	struct msm_audio_aio_buf32 aio_buf_32;
+
+	buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL);
+
+	if (!buf_node)
+		return -ENOMEM;
+
+	if (copy_from_user(&aio_buf_32, arg, sizeof(aio_buf_32))) {
+		kfree(buf_node);
+		pr_err("%s: copy_from_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	buf_node->buf.buf_addr = compat_ptr(aio_buf_32.buf_addr);
+	buf_node->buf.buf_len = aio_buf_32.buf_len;
+	buf_node->buf.data_len = aio_buf_32.data_len;
+	buf_node->buf.private_data = compat_ptr(aio_buf_32.private_data);
+	buf_node->buf.mfield_sz = aio_buf_32.mfield_sz;
+
+	return audio_aio_buf_add_shared(audio, dir, buf_node);
+}
+#endif
+
+static int audio_aio_buf_add(struct q6audio_aio *audio, u32 dir,
+				void __user *arg)
+{
+	struct audio_aio_buffer_node *buf_node;
+
+	buf_node = kzalloc(sizeof(*buf_node), GFP_KERNEL);
+
+	if (!buf_node)
+		return -ENOMEM;
+
+	if (copy_from_user(&buf_node->buf, arg, sizeof(buf_node->buf))) {
+		kfree(buf_node);
+		pr_err("%s: copy_from_user failed\n", __func__);
+		return -EFAULT;
+	}
+
+	return audio_aio_buf_add_shared(audio, dir, buf_node);
+}
+
+void audio_aio_ioport_reset(struct q6audio_aio *audio)
+{
+	if (audio->drv_status & ADRV_STATUS_AIO_INTF) {
+		/* If fsync is in progress, make sure
+		 * return value of fsync indicates
+		 * abort due to flush
+		 */
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			pr_debug("%s[%pK]:fsync in progress\n",
+				 __func__, audio);
+			audio->drv_ops.out_flush(audio);
+		} else
+			audio->drv_ops.out_flush(audio);
+		if (audio->feedback == NON_TUNNEL_MODE)
+			audio->drv_ops.in_flush(audio);
+	}
+}
+
+int audio_aio_open(struct q6audio_aio *audio, struct file *file)
+{
+	int rc = 0;
+	int i;
+	struct audio_aio_event *e_node = NULL;
+	struct list_head *ptr, *next;
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	audio->pcm_cfg.sample_rate = 48000;
+	audio->pcm_cfg.channel_count = 2;
+
+	/* Only AIO interface */
+	if (file->f_flags & O_NONBLOCK) {
+		pr_debug("%s[%pK]:set to aio interface\n", __func__, audio);
+		audio->drv_status |= ADRV_STATUS_AIO_INTF;
+		audio->drv_ops.out_flush = audio_aio_async_out_flush;
+		audio->drv_ops.in_flush = audio_aio_async_in_flush;
+		q6asm_set_io_mode(audio->ac, ASYNC_IO_MODE);
+	} else {
+		pr_err("%s[%pK]:SIO interface not supported\n",
+			__func__, audio);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	/* Initialize all locks of audio instance */
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	mutex_init(&audio->get_event_lock);
+	spin_lock_init(&audio->dsp_lock);
+	spin_lock_init(&audio->event_queue_lock);
+	init_waitqueue_head(&audio->cmd_wait);
+	init_waitqueue_head(&audio->write_wait);
+	init_waitqueue_head(&audio->event_wait);
+	INIT_LIST_HEAD(&audio->out_queue);
+	INIT_LIST_HEAD(&audio->in_queue);
+	INIT_LIST_HEAD(&audio->ion_region_queue);
+	INIT_LIST_HEAD(&audio->free_event_queue);
+	INIT_LIST_HEAD(&audio->event_queue);
+
+	audio->drv_ops.out_flush(audio);
+	audio->opened = 1;
+	audio->reset_event = false;
+	file->private_data = audio;
+	audio->codec_ioctl = audio_aio_ioctl;
+	audio->codec_compat_ioctl = audio_aio_compat_ioctl;
+	for (i = 0; i < AUDIO_EVENT_NUM; i++) {
+		e_node = kmalloc(sizeof(struct audio_aio_event), GFP_KERNEL);
+		if (e_node)
+			list_add_tail(&e_node->list, &audio->free_event_queue);
+		else {
+			rc = -ENOMEM;
+			goto cleanup;
+		}
+	}
+	audio->client = msm_audio_ion_client_create("Audio_Dec_Client");
+	if (IS_ERR_OR_NULL(audio->client)) {
+		pr_err("Unable to create ION client\n");
+		rc = -ENOMEM;
+		goto cleanup;
+	}
+	pr_debug("Ion client create in audio_aio_open %pK", audio->client);
+
+	rc = register_volume_listener(audio);
+	if (rc < 0)
+		goto ion_cleanup;
+
+	return 0;
+ion_cleanup:
+	msm_audio_ion_client_destroy(audio->client);
+	audio->client = NULL;
+cleanup:
+	list_for_each_safe(ptr, next, &audio->free_event_queue) {
+		e_node = list_first_entry(&audio->free_event_queue,
+				   struct audio_aio_event, list);
+		list_del(&e_node->list);
+		kfree(e_node);
+	}
+fail:
+	return rc;
+}
+
+static long audio_aio_shared_ioctl(struct file *file, unsigned int cmd,
+			unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_ABORT_GET_EVENT: {
+		audio->event_abort = 1;
+		wake_up(&audio->event_wait);
+		break;
+	}
+	case AUDIO_OUTPORT_FLUSH: {
+		pr_debug("%s[%pK]:AUDIO_OUTPORT_FLUSH\n", __func__, audio);
+		mutex_lock(&audio->read_lock);
+		rc = audio_aio_outport_flush(audio);
+		if (rc < 0) {
+			pr_err("%s[%pK]: AUDIO_OUTPORT_FLUSH failed\n",
+				__func__, audio);
+			rc = -EINTR;
+		}
+		mutex_unlock(&audio->read_lock);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s[%pK]: AUDIO_STOP session_id[%d]\n", __func__,
+				audio, audio->ac->session);
+		mutex_lock(&audio->lock);
+		audio->stopped = 1;
+		rc = audio_aio_flush(audio);
+		if (rc < 0) {
+			pr_err("%s[%pK]:Audio Stop procedure failed rc=%d\n",
+				__func__, audio, rc);
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		audio->enabled = 0;
+		audio->drv_status &= ~ADRV_STATUS_PAUSE;
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			pr_debug("%s[%pK] Waking up the audio_aio_fsync\n",
+					__func__, audio);
+			wake_up(&audio->write_wait);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_PAUSE: {
+		pr_debug("%s[%pK]:AUDIO_PAUSE %ld\n", __func__, audio, arg);
+		mutex_lock(&audio->lock);
+		if (arg == 1) {
+			rc = audio_aio_pause(audio);
+			if (rc < 0) {
+				pr_err("%s[%pK]: pause FAILED rc=%d\n",
+					__func__, audio, rc);
+				mutex_unlock(&audio->lock);
+				break;
+			}
+			audio->drv_status |= ADRV_STATUS_PAUSE;
+		} else if (arg == 0) {
+			if (audio->drv_status & ADRV_STATUS_PAUSE) {
+				rc = audio_aio_enable(audio);
+				if (rc)
+					pr_err("%s[%pK]: audio enable failed\n",
+					__func__, audio);
+				else {
+					audio->drv_status &= ~ADRV_STATUS_PAUSE;
+					audio->enabled = 1;
+				}
+			}
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_FLUSH: {
+		pr_debug("%s[%pK]: AUDIO_FLUSH sessionid[%d]\n", __func__,
+			audio, audio->ac->session);
+		mutex_lock(&audio->lock);
+		audio->rflush = 1;
+		audio->wflush = 1;
+		if (audio->drv_status & ADRV_STATUS_FSYNC) {
+			pr_debug("%s[%pK] Waking up the audio_aio_fsync\n",
+				__func__, audio);
+			wake_up(&audio->write_wait);
+		}
+		/* Flush DSP */
+		rc = audio_aio_flush(audio);
+		/* Flush input / Output buffer in software*/
+		audio_aio_ioport_reset(audio);
+		if (rc < 0) {
+			pr_err("%s[%pK]:AUDIO_FLUSH interrupted\n",
+				__func__, audio);
+			rc = -EINTR;
+		} else {
+			audio->rflush = 0;
+			if (audio->drv_status & ADRV_STATUS_FSYNC)
+				wake_up(&audio->write_wait);
+			else
+				audio->wflush = 0;
+
+		}
+		audio->eos_flag = 0;
+		audio->eos_rsp = 0;
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_GET_SESSION_ID: {
+		mutex_lock(&audio->lock);
+		if (copy_to_user((void *)arg, &audio->ac->session,
+			sizeof(u16))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_SESSION_ID failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_PM_AWAKE: {
+		if ((audio->audio_ws_mgr ==  NULL) ||
+				(audio->miscdevice == NULL)) {
+			pr_err("%s[%pK]: invalid ws_mgr or miscdevice",
+					__func__, audio);
+			rc = -EACCES;
+			break;
+		}
+		pr_debug("%s[%pK]:AUDIO_PM_AWAKE\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (!audio->wakelock_voted) {
+			audio->wakelock_voted = true;
+			mutex_lock(&audio->audio_ws_mgr->ws_lock);
+			if (audio->audio_ws_mgr->ref_cnt++ == 0)
+				pm_stay_awake(audio->miscdevice->this_device);
+			mutex_unlock(&audio->audio_ws_mgr->ws_lock);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_PM_RELAX: {
+		if ((audio->audio_ws_mgr ==  NULL) ||
+				(audio->miscdevice == NULL)) {
+			pr_err("%s[%pK]: invalid ws_mgr or miscdevice",
+					__func__, audio);
+			rc = -EACCES;
+			break;
+		}
+		pr_debug("%s[%pK]:AUDIO_PM_RELAX\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (audio->wakelock_voted) {
+			audio->wakelock_voted = false;
+			mutex_lock(&audio->audio_ws_mgr->ws_lock);
+			if ((audio->audio_ws_mgr->ref_cnt > 0) &&
+					(--audio->audio_ws_mgr->ref_cnt == 0)) {
+				pm_relax(audio->miscdevice->this_device);
+			}
+			mutex_unlock(&audio->audio_ws_mgr->ws_lock);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc =  -EINVAL;
+	}
+	return rc;
+
+
+}
+
+static long audio_aio_ioctl(struct file *file, unsigned int cmd,
+				unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_ABORT_GET_EVENT:
+	case AUDIO_OUTPORT_FLUSH:
+	case AUDIO_STOP:
+	case AUDIO_PAUSE:
+	case AUDIO_FLUSH:
+	case AUDIO_GET_SESSION_ID:
+	case AUDIO_PM_AWAKE:
+	case AUDIO_PM_RELAX:
+		rc = audio_aio_shared_ioctl(file, cmd, arg);
+		break;
+	case AUDIO_GET_STATS: {
+		struct msm_audio_stats stats;
+		uint64_t timestamp;
+
+		memset(&stats, 0, sizeof(struct msm_audio_stats));
+		stats.byte_count = atomic_read(&audio->in_bytes);
+		stats.sample_count = atomic_read(&audio->in_samples);
+		rc = q6asm_get_session_time(audio->ac, &timestamp);
+		if (rc >= 0)
+			memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
+		else
+			pr_debug("Error while getting timestamp\n");
+		if (copy_to_user((void *)arg, &stats, sizeof(stats))) {
+			pr_err("%s: copy_frm_user for AUDIO_GET_STATS failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_GET_EVENT: {
+		pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio);
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audio_aio_process_event_req(audio,
+						(void __user *)arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		break;
+	}
+	case AUDIO_ASYNC_WRITE: {
+		mutex_lock(&audio->write_lock);
+		if (audio->drv_status & ADRV_STATUS_FSYNC)
+			rc = -EBUSY;
+		else {
+			if (audio->enabled)
+				rc = audio_aio_buf_add(audio, 1,
+						(void __user *)arg);
+			else
+				rc = -EPERM;
+		}
+		mutex_unlock(&audio->write_lock);
+		break;
+	}
+	case AUDIO_ASYNC_READ: {
+		mutex_lock(&audio->read_lock);
+		if (audio->feedback)
+			rc = audio_aio_buf_add(audio, 0,
+					(void __user *)arg);
+		else
+			rc = -EPERM;
+		mutex_unlock(&audio->read_lock);
+		break;
+	}
+
+	case AUDIO_GET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+
+		mutex_lock(&audio->lock);
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = audio->str_cfg.buffer_size;
+		cfg.buffer_count = audio->str_cfg.buffer_count;
+		pr_debug("%s[%pK]:GET STREAM CFG %d %d\n",
+			__func__, audio, cfg.buffer_size, cfg.buffer_count);
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_STREAM_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG: {
+		struct msm_audio_stream_config cfg;
+
+		pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_SET_STREAM_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		audio->str_cfg.buffer_size = FRAME_SIZE;
+		audio->str_cfg.buffer_count = FRAME_NUM;
+		rc = 0;
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_GET_CONFIG: {
+		struct msm_audio_config cfg;
+
+		mutex_lock(&audio->lock);
+		if (copy_to_user((void *)arg, &audio->pcm_cfg, sizeof(cfg))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_SET_CONFIG: {
+		struct msm_audio_config config;
+
+		pr_err("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&config, (void *)arg, sizeof(config))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_SET_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		if (audio->feedback != NON_TUNNEL_MODE) {
+			pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n",
+				 __func__, audio);
+			rc = -EACCES;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		if ((config.buffer_count > PCM_BUF_COUNT) ||
+			(config.buffer_count == 1))
+			config.buffer_count = PCM_BUF_COUNT;
+
+		if (config.buffer_size < PCM_BUFSZ_MIN)
+			config.buffer_size = PCM_BUFSZ_MIN;
+
+		audio->pcm_cfg.buffer_count = config.buffer_count;
+		audio->pcm_cfg.buffer_size = config.buffer_size;
+		audio->pcm_cfg.channel_count = config.channel_count;
+		audio->pcm_cfg.sample_rate = config.sample_rate;
+		rc = 0;
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_SET_BUF_CFG: {
+		struct msm_audio_buf_cfg  cfg;
+
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&cfg, (void *)arg, sizeof(cfg))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_GET_BUF CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		if ((audio->feedback == NON_TUNNEL_MODE) &&
+			!cfg.meta_info_enable) {
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+
+		audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+		pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]",
+				__func__, audio,
+				audio->ac->session, cfg.meta_info_enable);
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_GET_BUF_CFG: {
+		pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+			 __func__, audio,
+			audio->ac->session, audio->buf_cfg.meta_info_enable,
+			audio->buf_cfg.frames_per_buf);
+
+		mutex_lock(&audio->lock);
+		if (copy_to_user((void *)arg, &audio->buf_cfg,
+			sizeof(struct msm_audio_buf_cfg))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_BUF_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_REGISTER_ION: {
+		struct msm_audio_ion_info info;
+
+		pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&info, (void *)arg, sizeof(info))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_REGISTER_ION failed\n",
+				__func__);
+			rc = -EFAULT;
+		} else {
+			mutex_lock(&audio->read_lock);
+			mutex_lock(&audio->write_lock);
+			rc = audio_aio_ion_add(audio, &info);
+			mutex_unlock(&audio->write_lock);
+			mutex_unlock(&audio->read_lock);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_DEREGISTER_ION: {
+		struct msm_audio_ion_info info;
+
+		mutex_lock(&audio->lock);
+		pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+		if (copy_from_user(&info, (void *)arg, sizeof(info))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_DEREGISTER_ION failed\n",
+				__func__);
+			rc = -EFAULT;
+		} else {
+			mutex_lock(&audio->read_lock);
+			mutex_lock(&audio->write_lock);
+			rc = audio_aio_ion_remove(audio, &info);
+			mutex_unlock(&audio->write_lock);
+			mutex_unlock(&audio->read_lock);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc =  -EINVAL;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_stream_config32 {
+	u32 buffer_size;
+	u32 buffer_count;
+};
+
+struct msm_audio_stats32 {
+	u32 byte_count;
+	u32 sample_count;
+	u32 unused[2];
+};
+
+struct msm_audio_config32 {
+	u32 buffer_size;
+	u32 buffer_count;
+	u32 channel_count;
+	u32 sample_rate;
+	u32 type;
+	u32 meta_field;
+	u32 bits;
+	u32 unused[3];
+};
+
+struct msm_audio_buf_cfg32 {
+	u32 meta_info_enable;
+	u32 frames_per_buf;
+};
+
+struct msm_audio_ion_info32 {
+	int fd;
+	compat_uptr_t vaddr;
+};
+
+enum {
+	AUDIO_GET_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 3,
+			struct msm_audio_config32),
+	AUDIO_SET_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 4,
+			struct msm_audio_config32),
+	AUDIO_GET_STATS_32 = _IOR(AUDIO_IOCTL_MAGIC, 5,
+			struct msm_audio_stats32),
+	AUDIO_GET_EVENT_32 = _IOR(AUDIO_IOCTL_MAGIC, 13,
+			struct msm_audio_event32),
+	AUDIO_ASYNC_WRITE_32 = _IOW(AUDIO_IOCTL_MAGIC, 17,
+			struct msm_audio_aio_buf32),
+	AUDIO_ASYNC_READ_32 = _IOW(AUDIO_IOCTL_MAGIC, 18,
+			struct msm_audio_aio_buf32),
+	AUDIO_SET_STREAM_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC, 80,
+			struct msm_audio_stream_config32),
+	AUDIO_GET_STREAM_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC, 81,
+			struct msm_audio_stream_config32),
+	AUDIO_GET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 93,
+			struct msm_audio_buf_cfg32),
+	AUDIO_SET_BUF_CFG_32 = _IOW(AUDIO_IOCTL_MAGIC, 94,
+			struct msm_audio_buf_cfg32),
+	AUDIO_REGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 97,
+			struct msm_audio_ion_info32),
+	AUDIO_DEREGISTER_ION_32 = _IOW(AUDIO_IOCTL_MAGIC, 98,
+			struct msm_audio_ion_info32),
+};
+
+static long audio_aio_compat_ioctl(struct file *file, unsigned int cmd,
+			unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_ABORT_GET_EVENT:
+	case AUDIO_OUTPORT_FLUSH:
+	case AUDIO_STOP:
+	case AUDIO_PAUSE:
+	case AUDIO_FLUSH:
+	case AUDIO_GET_SESSION_ID:
+	case AUDIO_PM_AWAKE:
+	case AUDIO_PM_RELAX:
+		rc = audio_aio_shared_ioctl(file, cmd, arg);
+		break;
+	case AUDIO_GET_STATS_32: {
+		struct msm_audio_stats32 stats;
+		uint64_t timestamp;
+
+		memset(&stats, 0, sizeof(struct msm_audio_stats32));
+		stats.byte_count = atomic_read(&audio->in_bytes);
+		stats.sample_count = atomic_read(&audio->in_samples);
+		rc = q6asm_get_session_time(audio->ac, &timestamp);
+		if (rc >= 0)
+			memcpy(&stats.unused[0], &timestamp, sizeof(timestamp));
+		else
+			pr_debug("Error while getting timestamp\n");
+		if (copy_to_user((void *)arg, &stats, sizeof(stats))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_STATS_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_GET_EVENT_32: {
+		pr_debug("%s[%pK]:AUDIO_GET_EVENT\n", __func__, audio);
+		if (mutex_trylock(&audio->get_event_lock)) {
+			rc = audio_aio_process_event_req_compat(audio,
+						(void __user *)arg);
+			mutex_unlock(&audio->get_event_lock);
+		} else
+			rc = -EBUSY;
+		break;
+	}
+	case AUDIO_ASYNC_WRITE_32: {
+		mutex_lock(&audio->write_lock);
+		if (audio->drv_status & ADRV_STATUS_FSYNC)
+			rc = -EBUSY;
+		else {
+			if (audio->enabled)
+				rc = audio_aio_buf_add_compat(audio, 1,
+						(void __user *)arg);
+			else
+				rc = -EPERM;
+		}
+		mutex_unlock(&audio->write_lock);
+		break;
+	}
+	case AUDIO_ASYNC_READ_32: {
+		mutex_lock(&audio->read_lock);
+		if (audio->feedback)
+			rc = audio_aio_buf_add_compat(audio, 0,
+					(void __user *)arg);
+		else
+			rc = -EPERM;
+		mutex_unlock(&audio->read_lock);
+		break;
+	}
+
+	case AUDIO_GET_STREAM_CONFIG_32: {
+		struct msm_audio_stream_config32 cfg;
+
+		mutex_lock(&audio->lock);
+		memset(&cfg, 0, sizeof(cfg));
+		cfg.buffer_size = audio->str_cfg.buffer_size;
+		cfg.buffer_count = audio->str_cfg.buffer_count;
+		pr_debug("%s[%pK]:GET STREAM CFG %d %d\n",
+			__func__, audio, cfg.buffer_size, cfg.buffer_count);
+		if (copy_to_user((void *)arg, &cfg, sizeof(cfg))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_STREAM_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_SET_STREAM_CONFIG_32: {
+		struct msm_audio_stream_config32 cfg_32;
+		struct msm_audio_stream_config cfg;
+
+		pr_debug("%s[%pK]:SET STREAM CONFIG\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_STREAM_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		cfg.buffer_size = cfg_32.buffer_size;
+		cfg.buffer_count = cfg_32.buffer_count;
+
+		audio->str_cfg.buffer_size = FRAME_SIZE;
+		audio->str_cfg.buffer_count = FRAME_NUM;
+		rc = 0;
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_GET_CONFIG_32: {
+		struct msm_audio_config32 cfg_32;
+
+		mutex_lock(&audio->lock);
+		memset(&cfg_32, 0, sizeof(cfg_32));
+		cfg_32.buffer_size = audio->pcm_cfg.buffer_size;
+		cfg_32.buffer_count = audio->pcm_cfg.buffer_count;
+		cfg_32.channel_count = audio->pcm_cfg.channel_count;
+		cfg_32.sample_rate = audio->pcm_cfg.sample_rate;
+		cfg_32.type = audio->pcm_cfg.type;
+		cfg_32.meta_field = audio->pcm_cfg.meta_field;
+		cfg_32.bits = audio->pcm_cfg.bits;
+
+		if (copy_to_user((void *)arg, &cfg_32, sizeof(cfg_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_SET_CONFIG_32: {
+		struct msm_audio_config config;
+		struct msm_audio_config32 config_32;
+
+		mutex_lock(&audio->lock);
+
+		if (audio->feedback != NON_TUNNEL_MODE) {
+			pr_err("%s[%pK]:Not sufficient permission to change the playback mode\n",
+				 __func__, audio);
+			rc = -EACCES;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		pr_err("%s[%pK]:AUDIO_SET_CONFIG\n", __func__, audio);
+		if (copy_from_user(&config_32, (void *)arg,
+					sizeof(config_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		config.buffer_size = config_32.buffer_size;
+		config.buffer_count = config_32.buffer_count;
+		config.channel_count = config_32.channel_count;
+		config.sample_rate = config_32.sample_rate;
+		config.type = config_32.type;
+		config.meta_field = config_32.meta_field;
+		config.bits = config_32.bits;
+
+		if ((config.buffer_count > PCM_BUF_COUNT) ||
+			(config.buffer_count == 1))
+			config.buffer_count = PCM_BUF_COUNT;
+
+		if (config.buffer_size < PCM_BUFSZ_MIN)
+			config.buffer_size = PCM_BUFSZ_MIN;
+
+		audio->pcm_cfg.buffer_count = config.buffer_count;
+		audio->pcm_cfg.buffer_size = config.buffer_size;
+		audio->pcm_cfg.channel_count = config.channel_count;
+		audio->pcm_cfg.sample_rate = config.sample_rate;
+		rc = 0;
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_SET_BUF_CFG_32: {
+		struct msm_audio_buf_cfg cfg;
+		struct msm_audio_buf_cfg32 cfg_32;
+
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&cfg_32, (void *)arg, sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+		cfg.meta_info_enable = cfg_32.meta_info_enable;
+		cfg.frames_per_buf = cfg_32.frames_per_buf;
+
+		if ((audio->feedback == NON_TUNNEL_MODE) &&
+			!cfg.meta_info_enable) {
+			rc = -EFAULT;
+			mutex_unlock(&audio->lock);
+			break;
+		}
+
+		audio->buf_cfg.meta_info_enable = cfg.meta_info_enable;
+		pr_debug("%s[%pK]:session id %d: Set-buf-cfg: meta[%d]",
+				__func__, audio,
+				audio->ac->session, cfg.meta_info_enable);
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_GET_BUF_CFG_32: {
+		struct msm_audio_buf_cfg32 cfg_32;
+
+		pr_debug("%s[%pK]:session id %d: Get-buf-cfg: meta[%d] framesperbuf[%d]\n",
+			 __func__, audio,
+			audio->ac->session, audio->buf_cfg.meta_info_enable,
+			audio->buf_cfg.frames_per_buf);
+		mutex_lock(&audio->lock);
+		memset(&cfg_32, 0, sizeof(cfg_32));
+		cfg_32.meta_info_enable = audio->buf_cfg.meta_info_enable;
+		cfg_32.frames_per_buf = audio->buf_cfg.frames_per_buf;
+		if (copy_to_user((void *)arg, &cfg_32,
+			sizeof(struct msm_audio_buf_cfg32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_BUF_CFG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_REGISTER_ION_32: {
+		struct msm_audio_ion_info32 info_32;
+		struct msm_audio_ion_info info;
+
+		pr_debug("%s[%pK]:AUDIO_REGISTER_ION\n", __func__, audio);
+		mutex_lock(&audio->lock);
+		if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
+			pr_err("%s: copy_from_user for AUDIO_REGISTER_ION_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		} else {
+			info.fd = info_32.fd;
+			info.vaddr = compat_ptr(info_32.vaddr);
+			mutex_lock(&audio->read_lock);
+			mutex_lock(&audio->write_lock);
+			rc = audio_aio_ion_add(audio, &info);
+			mutex_unlock(&audio->write_lock);
+			mutex_unlock(&audio->read_lock);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	case AUDIO_DEREGISTER_ION_32: {
+		struct msm_audio_ion_info32 info_32;
+		struct msm_audio_ion_info info;
+
+		mutex_lock(&audio->lock);
+		pr_debug("%s[%pK]:AUDIO_DEREGISTER_ION\n", __func__, audio);
+		if (copy_from_user(&info_32, (void *)arg, sizeof(info_32))) {
+			pr_err("%s: copy_from_user for	AUDIO_DEREGISTER_ION_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		} else {
+			info.fd = info_32.fd;
+			info.vaddr = compat_ptr(info_32.vaddr);
+			mutex_lock(&audio->read_lock);
+			mutex_lock(&audio->write_lock);
+			rc = audio_aio_ion_remove(audio, &info);
+			mutex_unlock(&audio->write_lock);
+			mutex_unlock(&audio->read_lock);
+		}
+		mutex_unlock(&audio->lock);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc =  -EINVAL;
+	}
+	return rc;
+}
+#endif
diff --git a/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
new file mode 100644
index 0000000..82374f9
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_utils_aio.h
@@ -0,0 +1,232 @@
+/* Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/msm_audio.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/msm_ion.h>
+#include <asm/ioctls.h>
+#include <linux/atomic.h>
+#include "q6audio_common.h"
+
+#define TUNNEL_MODE     0x0000
+#define NON_TUNNEL_MODE 0x0001
+
+#define ADRV_STATUS_AIO_INTF 0x00000001 /* AIO interface */
+#define ADRV_STATUS_FSYNC 0x00000008
+#define ADRV_STATUS_PAUSE 0x00000010
+#define AUDIO_DEC_EOS_SET  0x00000001
+#define AUDIO_DEC_EOF_SET  0x00000010
+#define AUDIO_EVENT_NUM		10
+
+#define __CONTAINS(r, v, l) ({                                  \
+	typeof(r) __r = r;                                      \
+	typeof(v) __v = v;                                      \
+	typeof(v) __e = __v + l;                                \
+	int res = ((__v >= __r->vaddr) &&                       \
+		(__e <= __r->vaddr + __r->len));                \
+	res;                                                    \
+})
+
+#define CONTAINS(r1, r2) ({                                     \
+	typeof(r2) __r2 = r2;                                   \
+	__CONTAINS(r1, __r2->vaddr, __r2->len);                 \
+})
+
+#define IN_RANGE(r, v) ({                                       \
+	typeof(r) __r = r;                                      \
+	typeof(v) __vv = v;                                     \
+	int res = ((__vv >= __r->vaddr) &&                      \
+		(__vv < (__r->vaddr + __r->len)));              \
+	res;                                                    \
+})
+
+#define OVERLAPS(r1, r2) ({                                     \
+	typeof(r1) __r1 = r1;                                   \
+	typeof(r2) __r2 = r2;                                   \
+	typeof(__r2->vaddr) __v = __r2->vaddr;                  \
+	typeof(__v) __e = __v + __r2->len - 1;                  \
+	int res = (IN_RANGE(__r1, __v) || IN_RANGE(__r1, __e)); \
+	res;                                                    \
+})
+
+struct timestamp {
+	u32 lowpart;
+	u32 highpart;
+} __packed;
+
+struct meta_out_dsp {
+	u32 offset_to_frame;
+	u32 frame_size;
+	u32 encoded_pcm_samples;
+	u32 msw_ts;
+	u32 lsw_ts;
+	u32 nflags;
+} __packed;
+
+struct dec_meta_in {
+	unsigned char reserved[18];
+	unsigned short offset;
+	struct timestamp ntimestamp;
+	unsigned int nflags;
+} __packed;
+
+struct dec_meta_out {
+	unsigned int reserved[7];
+	unsigned int num_of_frames;
+	struct meta_out_dsp meta_out_dsp[];
+} __packed;
+
+/* General meta field to store meta info locally */
+union  meta_data {
+	struct dec_meta_out meta_out;
+	struct dec_meta_in meta_in;
+} __packed;
+
+/* per device wakeup source manager */
+struct ws_mgr {
+	struct mutex       ws_lock;
+	uint32_t           ref_cnt;
+};
+
+#define PCM_BUF_COUNT           (2)
+/* Buffer with meta */
+#define PCM_BUFSZ_MIN           ((4*1024) + sizeof(struct dec_meta_out))
+
+/* FRAME_NUM must be a power of two */
+#define FRAME_NUM               (2)
+#define FRAME_SIZE	((4*1536) + sizeof(struct dec_meta_in))
+
+struct audio_aio_ion_region {
+	struct list_head list;
+	struct ion_handle *handle;
+	int fd;
+	void *vaddr;
+	phys_addr_t paddr;
+	void *kvaddr;
+	unsigned long len;
+	unsigned int ref_cnt;
+};
+
+struct audio_aio_event {
+	struct list_head list;
+	int event_type;
+	union msm_audio_event_payload payload;
+};
+
+struct audio_aio_buffer_node {
+	struct list_head list;
+	struct msm_audio_aio_buf buf;
+	unsigned long paddr;
+	uint32_t token;
+	void            *kvaddr;
+	union meta_data meta_info;
+};
+
+struct q6audio_aio;
+struct audio_aio_drv_operations {
+	void (*out_flush)(struct q6audio_aio *);
+	void (*in_flush)(struct q6audio_aio *);
+};
+
+struct q6audio_aio {
+	atomic_t in_bytes;
+	atomic_t in_samples;
+
+	struct msm_audio_stream_config str_cfg;
+	struct msm_audio_buf_cfg        buf_cfg;
+	struct msm_audio_config pcm_cfg;
+	void *codec_cfg;
+
+	struct audio_client *ac;
+
+	struct mutex lock;
+	struct mutex read_lock;
+	struct mutex write_lock;
+	struct mutex get_event_lock;
+	wait_queue_head_t cmd_wait;
+	wait_queue_head_t write_wait;
+	wait_queue_head_t event_wait;
+	spinlock_t dsp_lock;
+	spinlock_t event_queue_lock;
+
+	struct miscdevice *miscdevice;
+	uint32_t wakelock_voted;
+	struct ws_mgr *audio_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *dentry;
+#endif
+	struct list_head out_queue;     /* queue to retain output buffers */
+	struct list_head in_queue;      /* queue to retain input buffers */
+	struct list_head free_event_queue;
+	struct list_head event_queue;
+	struct list_head ion_region_queue;     /* protected by lock */
+	struct ion_client *client;
+	struct audio_aio_drv_operations drv_ops;
+	union msm_audio_event_payload eos_write_payload;
+	uint32_t device_events;
+	uint16_t volume;
+	uint32_t drv_status;
+	int event_abort;
+	int eos_rsp;
+	int eos_flag;
+	int opened;
+	int enabled;
+	int stopped;
+	int feedback;
+	int rflush;             /* Read  flush */
+	int wflush;             /* Write flush */
+	bool reset_event;
+	long (*codec_ioctl)(struct file *, unsigned int, unsigned long);
+	long (*codec_compat_ioctl)(struct file *, unsigned int, unsigned long);
+};
+
+void audio_aio_async_write_ack(struct q6audio_aio *audio, uint32_t token,
+				uint32_t *payload);
+
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+			uint32_t *payload);
+
+int insert_eos_buf(struct q6audio_aio *audio,
+		struct audio_aio_buffer_node *buf_node);
+
+void extract_meta_out_info(struct q6audio_aio *audio,
+		struct audio_aio_buffer_node *buf_node, int dir);
+
+int audio_aio_open(struct q6audio_aio *audio, struct file *file);
+int audio_aio_enable(struct q6audio_aio  *audio);
+void audio_aio_post_event(struct q6audio_aio *audio, int type,
+		union msm_audio_event_payload payload);
+int audio_aio_release(struct inode *inode, struct file *file);
+int audio_aio_fsync(struct file *file, loff_t start, loff_t end, int datasync);
+void audio_aio_async_out_flush(struct q6audio_aio *audio);
+void audio_aio_async_in_flush(struct q6audio_aio *audio);
+void audio_aio_ioport_reset(struct q6audio_aio *audio);
+int enable_volume_ramp(struct q6audio_aio *audio);
+#ifdef CONFIG_DEBUG_FS
+int audio_aio_debug_open(struct inode *inode, struct file *file);
+ssize_t audio_aio_debug_read(struct file *file, char __user *buf,
+			size_t count, loff_t *ppos);
+#endif
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wma.c b/drivers/misc/qcom/qdsp6v2/audio_wma.c
new file mode 100644
index 0000000..e35334a
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_wma.c
@@ -0,0 +1,345 @@
+/* wma audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_wma.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_wma_misc;
+static struct ws_mgr audio_wma_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_wma_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_wma_cfg wma_cfg;
+		struct msm_audio_wma_config_v2 *wma_config;
+
+		pr_debug("%s[%pK]: AUDIO_START session_id[%d]\n", __func__,
+						audio, audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+		wma_cfg.format_tag = wma_config->format_tag;
+		wma_cfg.ch_cfg = wma_config->numchannels;
+		wma_cfg.sample_rate =  wma_config->samplingrate;
+		wma_cfg.avg_bytes_per_sec = wma_config->avgbytespersecond;
+		wma_cfg.block_align = wma_config->block_align;
+		wma_cfg.valid_bits_per_sample =
+				wma_config->validbitspersample;
+		wma_cfg.ch_mask =  wma_config->channelmask;
+		wma_cfg.encode_opt = wma_config->encodeopt;
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_wma(audio->ac, &wma_cfg,
+				audio->ac->stream_id);
+		if (rc < 0) {
+			pr_err("cmd media format block failed\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_WMA_CONFIG_V2: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			sizeof(struct msm_audio_wma_config_v2))) {
+			pr_err("%s:copy_to_user for AUDIO_SET_WMA_CONFIG_V2 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_WMA_CONFIG_V2: {
+		if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_wma_config_v2))) {
+			pr_err("%s:copy_from_user for AUDIO_SET_WMA_CONFIG_V2 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_wma_config_v2_32 {
+	u16 format_tag;
+	u16 numchannels;
+	u32 samplingrate;
+	u32 avgbytespersecond;
+	u16 block_align;
+	u16 validbitspersample;
+	u32 channelmask;
+	u16 encodeopt;
+};
+
+enum {
+	AUDIO_GET_WMA_CONFIG_V2_32 =  _IOR(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2_32),
+	AUDIO_SET_WMA_CONFIG_V2_32 =  _IOW(AUDIO_IOCTL_MAGIC,
+	(AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2_32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+						unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	case AUDIO_GET_WMA_CONFIG_V2_32: {
+		struct msm_audio_wma_config_v2 *wma_config;
+		struct msm_audio_wma_config_v2_32 wma_config_32;
+
+		memset(&wma_config_32, 0, sizeof(wma_config_32));
+
+		wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+		wma_config_32.format_tag = wma_config->format_tag;
+		wma_config_32.numchannels = wma_config->numchannels;
+		wma_config_32.samplingrate = wma_config->samplingrate;
+		wma_config_32.avgbytespersecond = wma_config->avgbytespersecond;
+		wma_config_32.block_align = wma_config->block_align;
+		wma_config_32.validbitspersample =
+					wma_config->validbitspersample;
+		wma_config_32.channelmask = wma_config->channelmask;
+		wma_config_32.encodeopt = wma_config->encodeopt;
+		if (copy_to_user((void *)arg, &wma_config_32,
+			sizeof(wma_config_32))) {
+			pr_err("%s: copy_to_user for GET_WMA_CONFIG_V2_32 failed\n",
+				 __func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_WMA_CONFIG_V2_32: {
+		struct msm_audio_wma_config_v2 *wma_config;
+		struct msm_audio_wma_config_v2_32 wma_config_32;
+
+		if (copy_from_user(&wma_config_32, (void *)arg,
+			sizeof(wma_config_32))) {
+			pr_err("%s: copy_from_user for SET_WMA_CONFIG_V2_32 failed\n"
+				, __func__);
+			rc = -EFAULT;
+			break;
+		}
+		wma_config = (struct msm_audio_wma_config_v2 *)audio->codec_cfg;
+		wma_config->format_tag = wma_config_32.format_tag;
+		wma_config->numchannels = wma_config_32.numchannels;
+		wma_config->samplingrate = wma_config_32.samplingrate;
+		wma_config->avgbytespersecond = wma_config_32.avgbytespersecond;
+		wma_config->block_align = wma_config_32.block_align;
+		wma_config->validbitspersample =
+				wma_config_32.validbitspersample;
+		wma_config->channelmask = wma_config_32.channelmask;
+		wma_config->encodeopt = wma_config_32.encodeopt;
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_wma_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wma_config_v2),
+					GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_wma_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_wma_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_WMA_V9);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open WMA decoder, expected frames is always 1*/
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_WMA_V9);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_wma_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_wma_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:wmadec success mode[%d]session[%d]\n", __func__,
+						audio->feedback,
+						audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_wma_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_wma_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_wma",
+	.fops = &audio_wma_fops,
+};
+
+static int __init audio_wma_init(void)
+{
+	int ret = misc_register(&audio_wma_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_wma_misc.this_device, true);
+	audio_wma_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_wma_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_wma_init);
diff --git a/drivers/misc/qcom/qdsp6v2/audio_wmapro.c b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
new file mode 100644
index 0000000..3cb9db1
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/audio_wmapro.c
@@ -0,0 +1,418 @@
+/* wmapro audio output device
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2009-2017, The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/msm_audio_wmapro.h>
+#include <linux/compat.h>
+#include "audio_utils_aio.h"
+
+static struct miscdevice audio_wmapro_misc;
+static struct ws_mgr audio_wmapro_ws_mgr;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations audio_wmapro_debug_fops = {
+	.read = audio_aio_debug_read,
+	.open = audio_aio_debug_open,
+};
+#endif
+
+static long audio_ioctl_shared(struct file *file, unsigned int cmd,
+						void *arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct asm_wmapro_cfg wmapro_cfg;
+		struct msm_audio_wmapro_config *wmapro_config;
+
+		pr_debug("%s: AUDIO_START session_id[%d]\n", __func__,
+						audio->ac->session);
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			/* Configure PCM output block */
+			rc = q6asm_enc_cfg_blk_pcm_v2(audio->ac,
+					audio->pcm_cfg.sample_rate,
+					audio->pcm_cfg.channel_count,
+					16, /* bits per sample */
+					true, /* use default channel map */
+					true, /* use back channel map flavor */
+					NULL);
+			if (rc < 0) {
+				pr_err("pcm output block config failed\n");
+				break;
+			}
+		}
+		wmapro_config = (struct msm_audio_wmapro_config *)
+				audio->codec_cfg;
+		if ((wmapro_config->formattag == 0x162) ||
+		(wmapro_config->formattag == 0x163) ||
+		(wmapro_config->formattag == 0x166) ||
+		(wmapro_config->formattag == 0x167)) {
+			wmapro_cfg.format_tag = wmapro_config->formattag;
+		} else {
+			pr_err("%s:AUDIO_START failed: formattag = %d\n",
+				__func__, wmapro_config->formattag);
+			rc = -EINVAL;
+			break;
+		}
+		if (wmapro_config->numchannels > 0) {
+			wmapro_cfg.ch_cfg = wmapro_config->numchannels;
+		} else {
+			pr_err("%s:AUDIO_START failed: channels = %d\n",
+				__func__, wmapro_config->numchannels);
+			rc = -EINVAL;
+			break;
+		}
+		if (wmapro_config->samplingrate > 0) {
+			wmapro_cfg.sample_rate = wmapro_config->samplingrate;
+		} else {
+			pr_err("%s:AUDIO_START failed: sample_rate = %d\n",
+				__func__, wmapro_config->samplingrate);
+			rc = -EINVAL;
+			break;
+		}
+		wmapro_cfg.avg_bytes_per_sec =
+				wmapro_config->avgbytespersecond;
+		if ((wmapro_config->asfpacketlength <= 13376) ||
+		(wmapro_config->asfpacketlength > 0)) {
+			wmapro_cfg.block_align =
+				wmapro_config->asfpacketlength;
+		} else {
+			pr_err("%s:AUDIO_START failed: block_align = %d\n",
+				__func__, wmapro_config->asfpacketlength);
+			rc = -EINVAL;
+			break;
+		}
+		if ((wmapro_config->validbitspersample == 16) ||
+			(wmapro_config->validbitspersample == 24)) {
+			wmapro_cfg.valid_bits_per_sample =
+				wmapro_config->validbitspersample;
+		} else {
+			pr_err("%s:AUDIO_START failed: bitspersample = %d\n",
+				__func__, wmapro_config->validbitspersample);
+			rc = -EINVAL;
+			break;
+		}
+		wmapro_cfg.ch_mask = wmapro_config->channelmask;
+		wmapro_cfg.encode_opt = wmapro_config->encodeopt;
+		wmapro_cfg.adv_encode_opt =
+				wmapro_config->advancedencodeopt;
+		wmapro_cfg.adv_encode_opt2 =
+				wmapro_config->advancedencodeopt2;
+		/* Configure Media format block */
+		rc = q6asm_media_format_block_wmapro(audio->ac, &wmapro_cfg,
+				audio->ac->stream_id);
+		if (rc < 0) {
+			pr_err("cmd media format block failed\n");
+			break;
+		}
+		rc = audio_aio_enable(audio);
+		audio->eos_rsp = 0;
+		audio->eos_flag = 0;
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("Audio Start procedure failed rc=%d\n", rc);
+			break;
+		}
+		pr_debug("AUDIO_START success enable[%d]\n", audio->enabled);
+		if (audio->stopped == 1)
+			audio->stopped = 0;
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd %d\n", __func__, cmd);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static long audio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_GET_WMAPRO_CONFIG: {
+		if (copy_to_user((void *)arg, audio->codec_cfg,
+			 sizeof(struct msm_audio_wmapro_config))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_WMAPRO_CONFIG: {
+		if (copy_from_user(audio->codec_cfg, (void *)arg,
+			sizeof(struct msm_audio_wmapro_config))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_WMAPRO_CONFIG_V2 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		break;
+	}
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+
+struct msm_audio_wmapro_config32 {
+	u16 armdatareqthr;
+	u8  validbitspersample;
+	u8  numchannels;
+	u16 formattag;
+	u32 samplingrate;
+	u32 avgbytespersecond;
+	u16 asfpacketlength;
+	u32 channelmask;
+	u16 encodeopt;
+	u16 advancedencodeopt;
+	u32 advancedencodeopt2;
+};
+
+enum {
+	AUDIO_GET_WMAPRO_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config32),
+	AUDIO_SET_WMAPRO_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config32)
+};
+
+static long audio_compat_ioctl(struct file *file, unsigned int cmd,
+					unsigned long arg)
+{
+	struct q6audio_aio *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_GET_WMAPRO_CONFIG_32: {
+		struct msm_audio_wmapro_config *wmapro_config;
+		struct msm_audio_wmapro_config32 wmapro_config_32;
+
+		memset(&wmapro_config_32, 0, sizeof(wmapro_config_32));
+
+		wmapro_config =
+			(struct msm_audio_wmapro_config *)audio->codec_cfg;
+		wmapro_config_32.armdatareqthr = wmapro_config->armdatareqthr;
+		wmapro_config_32.validbitspersample =
+					wmapro_config->validbitspersample;
+		wmapro_config_32.numchannels = wmapro_config->numchannels;
+		wmapro_config_32.formattag = wmapro_config->formattag;
+		wmapro_config_32.samplingrate = wmapro_config->samplingrate;
+		wmapro_config_32.avgbytespersecond =
+					wmapro_config->avgbytespersecond;
+		wmapro_config_32.asfpacketlength =
+					wmapro_config->asfpacketlength;
+		wmapro_config_32.channelmask = wmapro_config->channelmask;
+		wmapro_config_32.encodeopt = wmapro_config->encodeopt;
+		wmapro_config_32.advancedencodeopt =
+					wmapro_config->advancedencodeopt;
+		wmapro_config_32.advancedencodeopt2 =
+					wmapro_config->advancedencodeopt2;
+
+		if (copy_to_user((void *)arg, &wmapro_config_32,
+			 sizeof(struct msm_audio_wmapro_config32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_WMAPRO_CONFIG_V2_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_WMAPRO_CONFIG_32: {
+		struct msm_audio_wmapro_config *wmapro_config;
+		struct msm_audio_wmapro_config32 wmapro_config_32;
+
+		if (copy_from_user(&wmapro_config_32, (void *)arg,
+			sizeof(struct msm_audio_wmapro_config32))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_SET_WMAPRO_CONFG_V2_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		wmapro_config =
+			(struct msm_audio_wmapro_config *)audio->codec_cfg;
+		wmapro_config->armdatareqthr = wmapro_config_32.armdatareqthr;
+		wmapro_config->validbitspersample =
+					wmapro_config_32.validbitspersample;
+		wmapro_config->numchannels = wmapro_config_32.numchannels;
+		wmapro_config->formattag = wmapro_config_32.formattag;
+		wmapro_config->samplingrate = wmapro_config_32.samplingrate;
+		wmapro_config->avgbytespersecond =
+					wmapro_config_32.avgbytespersecond;
+		wmapro_config->asfpacketlength =
+					wmapro_config_32.asfpacketlength;
+		wmapro_config->channelmask = wmapro_config_32.channelmask;
+		wmapro_config->encodeopt = wmapro_config_32.encodeopt;
+		wmapro_config->advancedencodeopt =
+					wmapro_config_32.advancedencodeopt;
+		wmapro_config->advancedencodeopt2 =
+					wmapro_config_32.advancedencodeopt2;
+		break;
+	}
+	case AUDIO_START: {
+		rc = audio_ioctl_shared(file, cmd, (void *)arg);
+		break;
+	}
+	default: {
+		pr_debug("%s[%pK]: Calling utils ioctl\n", __func__, audio);
+		rc = audio->codec_compat_ioctl(file, cmd, arg);
+		if (rc)
+			pr_err("Failed in utils_ioctl: %d\n", rc);
+		break;
+	}
+	}
+	return rc;
+}
+#else
+#define audio_compat_ioctl NULL
+#endif
+
+static int audio_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_aio *audio = NULL;
+	int rc = 0;
+
+#ifdef CONFIG_DEBUG_FS
+	/* 4 bytes represents decoder number, 1 byte for terminate string */
+	char name[sizeof "msm_wmapro_" + 5];
+#endif
+	audio = kzalloc(sizeof(struct q6audio_aio), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	audio->codec_cfg = kzalloc(sizeof(struct msm_audio_wmapro_config),
+					GFP_KERNEL);
+	if (audio->codec_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+
+	audio->pcm_cfg.buffer_size = PCM_BUFSZ_MIN;
+	audio->miscdevice = &audio_wmapro_misc;
+	audio->wakelock_voted = false;
+	audio->audio_ws_mgr = &audio_wmapro_ws_mgr;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb) q6_audio_cb,
+					     (void *)audio);
+
+	if (!audio->ac) {
+		pr_err("Could not allocate memory for audio client\n");
+		kfree(audio->codec_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	rc = audio_aio_open(audio, file);
+	if (rc < 0) {
+		pr_err("%s: audio_aio_open rc=%d\n",
+			__func__, rc);
+		goto fail;
+	}
+	/* open in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) && (file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_read_write(audio->ac, FORMAT_LINEAR_PCM,
+					   FORMAT_WMA_V10PRO);
+		if (rc < 0) {
+			pr_err("NT mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = NON_TUNNEL_MODE;
+		/* open WMA decoder, expected frames is always 1*/
+		audio->buf_cfg.frames_per_buf = 0x01;
+		audio->buf_cfg.meta_info_enable = 0x01;
+	} else if ((file->f_mode & FMODE_WRITE) &&
+			!(file->f_mode & FMODE_READ)) {
+		rc = q6asm_open_write(audio->ac, FORMAT_WMA_V10PRO);
+		if (rc < 0) {
+			pr_err("T mode Open failed rc=%d\n", rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		audio->feedback = TUNNEL_MODE;
+		audio->buf_cfg.meta_info_enable = 0x00;
+	} else {
+		pr_err("Not supported mode\n");
+		rc = -EACCES;
+		goto fail;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	snprintf(name, sizeof(name), "msm_wmapro_%04x", audio->ac->session);
+	audio->dentry = debugfs_create_file(name, S_IFREG | 0444,
+					    NULL, (void *)audio,
+					    &audio_wmapro_debug_fops);
+
+	if (IS_ERR(audio->dentry))
+		pr_debug("debugfs_create_file failed\n");
+#endif
+	pr_info("%s:wmapro decoder open success, session_id = %d\n", __func__,
+				audio->ac->session);
+	return rc;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->codec_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_wmapro_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_open,
+	.release = audio_aio_release,
+	.unlocked_ioctl = audio_ioctl,
+	.fsync = audio_aio_fsync,
+	.compat_ioctl = audio_compat_ioctl
+};
+
+static struct miscdevice audio_wmapro_misc = {
+	.minor = MISC_DYNAMIC_MINOR,
+	.name = "msm_wmapro",
+	.fops = &audio_wmapro_fops,
+};
+
+static int __init audio_wmapro_init(void)
+{
+	int ret = misc_register(&audio_wmapro_misc);
+
+	if (ret == 0)
+		device_init_wakeup(audio_wmapro_misc.this_device, true);
+	audio_wmapro_ws_mgr.ref_cnt = 0;
+	mutex_init(&audio_wmapro_ws_mgr.ws_lock);
+
+	return ret;
+}
+
+device_initcall(audio_wmapro_init);
diff --git a/drivers/misc/qcom/qdsp6v2/evrc_in.c b/drivers/misc/qcom/qdsp6v2/evrc_in.c
new file mode 100644
index 0000000..e30271d
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/evrc_in.c
@@ -0,0 +1,410 @@
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((23+sizeof(struct meta_out_dsp)) * 10))
+
+static long evrc_in_ioctl_shared(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_evrc_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			pr_info("%s:AUDIO_START already over\n", __func__);
+			rc = 0;
+			break;
+		}
+		rc = audio_in_buf_alloc(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: buffer allocation failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+
+		/* rate_modulation_cmd set to zero
+		 * currently not configurable from user space
+		 */
+		rc = q6asm_enc_cfg_blk_evrc(audio->ac,
+			audio->buf_cfg.frames_per_buf,
+			enc_cfg->min_bit_rate,
+			enc_cfg->max_bit_rate, 0);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd evrc media format block failed\n",
+					__func__, audio->ac->session);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+				audio->pcm_cfg.sample_rate,
+				audio->pcm_cfg.channel_count);
+
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed\n",
+					__func__, audio->ac->session);
+				break;
+			}
+		}
+		pr_debug("%s:session id %d: AUDIO_START enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac); /* Push buffer to DSP */
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+				audio->ac->session);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_EVRC_ENC_CONFIG: {
+		struct msm_audio_evrc_enc_config *cfg;
+		struct msm_audio_evrc_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		cfg = (struct msm_audio_evrc_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer for %s\n",
+					__func__, "AUDIO_SET_EVRC_ENC_CONFIG");
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg->min_bit_rate > 4 ||
+			 cfg->min_bit_rate < 1 ||
+			 (cfg->min_bit_rate == 2)) {
+			pr_err("%s:session id %d: invalid min bitrate\n",
+					__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg->max_bit_rate > 4 ||
+			 cfg->max_bit_rate < 1 ||
+			 (cfg->max_bit_rate == 2)) {
+			pr_err("%s:session id %d: invalid max bitrate\n",
+				__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		enc_cfg->min_bit_rate = cfg->min_bit_rate;
+		enc_cfg->max_bit_rate = cfg->max_bit_rate;
+		pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+			__func__,
+			audio->ac->session, enc_cfg->min_bit_rate,
+			enc_cfg->max_bit_rate);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long evrc_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = evrc_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_EVRC_ENC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->enc_cfg,
+			sizeof(struct msm_audio_evrc_enc_config))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_EVRC_ENC_CONFIG: {
+		struct msm_audio_evrc_enc_config cfg;
+
+		if (copy_from_user(&cfg, (void *) arg,
+				sizeof(struct msm_audio_evrc_enc_config))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_evrc_enc_config32 {
+	u32 cdma_rate;
+	u32 min_bit_rate;
+	u32 max_bit_rate;
+};
+
+enum {
+	AUDIO_SET_EVRC_ENC_CONFIG_32 =  _IOW(AUDIO_IOCTL_MAGIC,
+		2, struct msm_audio_evrc_enc_config32),
+	AUDIO_GET_EVRC_ENC_CONFIG_32 =  _IOR(AUDIO_IOCTL_MAGIC,
+		3, struct msm_audio_evrc_enc_config32)
+};
+
+static long evrc_in_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = evrc_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_EVRC_ENC_CONFIG_32: {
+		struct msm_audio_evrc_enc_config32 cfg_32;
+		struct msm_audio_evrc_enc_config *enc_cfg;
+
+		memset(&cfg_32, 0, sizeof(cfg_32));
+
+		enc_cfg = audio->enc_cfg;
+		cfg_32.cdma_rate = enc_cfg->cdma_rate;
+		cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
+		cfg_32.max_bit_rate = enc_cfg->max_bit_rate;
+
+		if (copy_to_user((void *)arg, &cfg_32,
+			sizeof(cfg_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_EVRC_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_EVRC_ENC_CONFIG_32: {
+		struct msm_audio_evrc_enc_config cfg;
+		struct msm_audio_evrc_enc_config32 cfg_32;
+
+		if (copy_from_user(&cfg_32, (void *) arg,
+				sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_EVRC_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.cdma_rate = cfg_32.cdma_rate;
+		cfg.min_bit_rate = cfg_32.min_bit_rate;
+		cfg.max_bit_rate = cfg_32.max_bit_rate;
+		cmd = AUDIO_SET_EVRC_ENC_CONFIG;
+		rc = evrc_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_EVRC_ENC_CONFIG failed. rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+#else
+#define evrc_in_compat_ioctl NULL
+#endif
+
+static int evrc_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_evrc_enc_config *enc_cfg;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_evrc_enc_config),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 23;
+	audio->max_frames_per_buf = 10;
+	audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	enc_cfg->min_bit_rate = 4;
+	enc_cfg->max_bit_rate = 4;
+	audio->pcm_cfg.channel_count = 1;
+	audio->pcm_cfg.sample_rate = 8000;
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf = 0x01;
+	audio->event_abort = 0;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+				(void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+				__func__);
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open evrc encoder in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_EVRC,
+					FORMAT_LINEAR_PCM);
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: NT mode encoder success\n",
+				__func__, audio->ac->session);
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_EVRC);
+		if (rc < 0) {
+			pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__,
+				audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: T mode encoder success\n", __func__,
+				audio->ac->session);
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	audio->opened = 1;
+	audio->reset_event = false;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = evrc_in_compat_ioctl;
+	audio->enc_ioctl = evrc_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= evrc_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl	= audio_in_ioctl,
+	.compat_ioctl   = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_evrc_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_evrc_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init evrc_in_init(void)
+{
+	return misc_register(&audio_evrc_in_misc);
+}
+
+device_initcall(evrc_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/g711alaw_in.c b/drivers/misc/qcom/qdsp6v2/g711alaw_in.c
new file mode 100644
index 0000000..bc8c0a3
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/g711alaw_in.c
@@ -0,0 +1,382 @@
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_g711.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((320+sizeof(struct meta_out_dsp)) * 10))
+static long g711_in_ioctl_shared(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_g711_enc_config *enc_cfg;
+
+		enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			rc = 0;
+			break;
+		}
+		rc = audio_in_buf_alloc(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: buffer allocation failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate);
+		rc = q6asm_enc_cfg_blk_g711(audio->ac,
+			audio->buf_cfg.frames_per_buf,
+			enc_cfg->sample_rate);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+				audio->pcm_cfg.sample_rate,
+				audio->pcm_cfg.channel_count);
+
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+				break;
+			}
+		}
+		pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
+				audio->ac->session, audio->enabled);
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac); /* Push buffer to DSP */
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+				audio->ac->session);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session,
+					rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_ENC_CONFIG: {
+		struct msm_audio_g711_enc_config *cfg;
+		struct msm_audio_g711_enc_config *enc_cfg;
+
+		enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
+
+		cfg = (struct msm_audio_g711_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer\n", __func__);
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg->sample_rate != 8000 &&
+			 cfg->sample_rate != 16000) {
+			pr_err("%s:session id %d: invalid sample rate\n",
+					__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		enc_cfg->sample_rate = cfg->sample_rate;
+		pr_debug("%s:session id %d: sample_rate= 0x%x",
+			__func__,
+			audio->ac->session, enc_cfg->sample_rate);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -ENOIOCTLCMD;
+	}
+	return rc;
+}
+
+static long g711_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = g711_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_G711_ENC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->enc_cfg,
+			sizeof(struct msm_audio_g711_enc_config))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_ENC_CONFIG: {
+		struct msm_audio_g711_enc_config cfg;
+
+		if (copy_from_user(&cfg, (void *) arg,
+				sizeof(cfg))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -ENOIOCTLCMD;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_g711_enc_config32 {
+	uint32_t sample_rate;
+};
+
+enum {
+	AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32),
+	AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32)
+};
+
+static long g711_in_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = g711_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_G711_ENC_CONFIG_32: {
+		struct msm_audio_g711_enc_config32 cfg_32;
+		struct msm_audio_g711_enc_config32 *enc_cfg;
+
+		enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg;
+		cfg_32.sample_rate = enc_cfg->sample_rate;
+		if (copy_to_user((void *)arg, &cfg_32,
+			sizeof(cfg_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_ENC_CONFIG_32: {
+		struct msm_audio_g711_enc_config32 cfg_32;
+		struct msm_audio_g711_enc_config32 cfg;
+
+		if (copy_from_user(&cfg_32, (void *) arg,
+				sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.sample_rate = cfg_32.sample_rate;
+		cmd = AUDIO_SET_G711_ENC_CONFIG;
+		rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -ENOIOCTLCMD;
+	}
+	return rc;
+}
+#else
+#define g711_in_compat_ioctl NULL
+#endif
+
+static int g711_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_g711_enc_config *enc_cfg;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/*
+	 * Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 320;
+	audio->max_frames_per_buf = 10;
+	audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	enc_cfg->sample_rate = 8000;
+	audio->pcm_cfg.channel_count = 1;
+	audio->pcm_cfg.sample_rate = 8000;
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf = 0x01;
+	audio->event_abort = 0;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+				(void *)audio);
+
+	if (!audio->ac) {
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open g711 encoder in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_G711_ALAW_FS,
+					FORMAT_LINEAR_PCM);
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_G711_ALAW_FS);
+		if (rc < 0) {
+			pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	audio->opened = 1;
+	audio->reset_event = false;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = g711_in_compat_ioctl;
+	audio->enc_ioctl = g711_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= g711_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl	= audio_in_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl   = audio_in_compat_ioctl,
+#endif
+};
+
+struct miscdevice audio_g711alaw_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_g711alaw_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init g711alaw_in_init(void)
+{
+	return misc_register(&audio_g711alaw_in_misc);
+}
+
+device_initcall(g711alaw_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c b/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c
new file mode 100644
index 0000000..b92c449
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/g711mlaw_in.c
@@ -0,0 +1,385 @@
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_g711.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+#ifdef CONFIG_COMPAT
+#undef PROC_ADD
+#endif
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((320+sizeof(struct meta_out_dsp)) * 10))
+static long g711_in_ioctl_shared(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_g711_enc_config *enc_cfg;
+
+		enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			rc = 0;
+			break;
+		}
+		rc = audio_in_buf_alloc(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: buffer allocation failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			break;
+		}
+		pr_debug("%s: sample rate %d", __func__, enc_cfg->sample_rate);
+		rc = q6asm_enc_cfg_blk_g711(audio->ac,
+			audio->buf_cfg.frames_per_buf,
+			enc_cfg->sample_rate);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd g711 media format block failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+				audio->pcm_cfg.sample_rate,
+				audio->pcm_cfg.channel_count);
+
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+				break;
+			}
+		}
+		pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
+				audio->ac->session, audio->enabled);
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac); /* Push buffer to DSP */
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+				audio->ac->session);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session,
+					rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_ENC_CONFIG: {
+		struct msm_audio_g711_enc_config *cfg;
+		struct msm_audio_g711_enc_config *enc_cfg;
+
+		enc_cfg = (struct msm_audio_g711_enc_config *)audio->enc_cfg;
+
+		cfg = (struct msm_audio_g711_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer\n", __func__);
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg->sample_rate != 8000 &&
+			 cfg->sample_rate != 16000) {
+			pr_err("%s:session id %d: invalid sample rate\n",
+					__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		enc_cfg->sample_rate = cfg->sample_rate;
+		pr_debug("%s:session id %d: sample_rate= 0x%x",
+			__func__,
+			audio->ac->session, enc_cfg->sample_rate);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -ENOIOCTLCMD;
+	}
+	return rc;
+}
+
+static long g711_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = g711_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_G711_ENC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->enc_cfg,
+			sizeof(struct msm_audio_g711_enc_config))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_g711_ENC_CONFIG failed",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_ENC_CONFIG: {
+		struct msm_audio_g711_enc_config cfg;
+
+		if (copy_from_user(&cfg, (void *) arg,
+				sizeof(cfg))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_GET_G711_ENC_CONFIG failed",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_GET_G711_ENC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -ENOIOCTLCMD;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_g711_enc_config32 {
+	uint32_t sample_rate;
+};
+
+enum {
+	AUDIO_SET_G711_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config32),
+	AUDIO_GET_G711_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config32)
+};
+
+static long g711_in_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = g711_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_G711_ENC_CONFIG_32: {
+		struct msm_audio_g711_enc_config32 cfg_32;
+		struct msm_audio_g711_enc_config32 *enc_cfg;
+
+		enc_cfg = (struct msm_audio_g711_enc_config32 *)audio->enc_cfg;
+		cfg_32.sample_rate = enc_cfg->sample_rate;
+		if (copy_to_user((void *)arg, &cfg_32,
+			sizeof(cfg_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_G711_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_G711_ENC_CONFIG_32: {
+		struct msm_audio_g711_enc_config32 cfg_32;
+		struct msm_audio_g711_enc_config32 cfg;
+
+		if (copy_from_user(&cfg_32, (void *) arg,
+				sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_G711_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.sample_rate = cfg_32.sample_rate;
+		cmd = AUDIO_SET_G711_ENC_CONFIG;
+		rc = g711_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_G711_ENC_CONFIG failed. rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -ENOIOCTLCMD;
+	}
+	return rc;
+}
+#else
+#define g711_in_compat_ioctl NULL
+#endif
+
+static int g711_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_g711_enc_config *enc_cfg;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_g711_enc_config),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/*
+	 * Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 320;
+	audio->max_frames_per_buf = 10;
+	audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	enc_cfg->sample_rate = 8000;
+	audio->pcm_cfg.channel_count = 1;
+	audio->pcm_cfg.sample_rate = 8000;
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf = 0x01;
+	audio->event_abort = 0;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+				(void *)audio);
+
+	if (!audio->ac) {
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open g711 encoder in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_G711_MLAW_FS,
+					FORMAT_LINEAR_PCM);
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_G711_MLAW_FS);
+		if (rc < 0) {
+			pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	audio->opened = 1;
+	audio->reset_event = false;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = g711_in_compat_ioctl;
+	audio->enc_ioctl = g711_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= g711_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl = audio_in_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl   = audio_in_compat_ioctl,
+#endif
+};
+
+struct miscdevice audio_g711mlaw_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_g711mlaw_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init g711mlaw_in_init(void)
+{
+	return misc_register(&audio_g711mlaw_in_misc);
+}
+
+device_initcall(g711mlaw_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_common.h b/drivers/misc/qcom/qdsp6v2/q6audio_common.h
new file mode 100644
index 0000000..49b88b7
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_common.h
@@ -0,0 +1,37 @@
+/* Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+
+/* For Decoders */
+#ifndef __Q6_AUDIO_COMMON_H__
+#define __Q6_AUDIO_COMMON_H__
+
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+		uint32_t *payload, void *priv);
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+			uint32_t *payload,  void *audio);
+
+
+/* For Encoders */
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+		uint32_t *payload, void *priv);
+
+void  audio_in_get_dsp_frames(void *audio,
+		uint32_t token,	uint32_t *payload);
+
+#endif /*__Q6_AUDIO_COMMON_H__*/
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
new file mode 100644
index 0000000..6c05165
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2.c
@@ -0,0 +1,107 @@
+/* Copyright (c) 2012-2013, 2015-2017, The Linux Foundation. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+void q6asm_in_cb(uint32_t opcode, uint32_t token,
+		uint32_t *payload, void *priv)
+{
+	struct q6audio_in *audio = (struct q6audio_in *)priv;
+	unsigned long flags;
+
+	pr_debug("%s:session id %d: opcode[0x%x]\n", __func__,
+			audio->ac->session, opcode);
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	switch (opcode) {
+	case ASM_DATA_EVENT_READ_DONE_V2:
+		audio_in_get_dsp_frames(audio, token, payload);
+		break;
+	case ASM_DATA_EVENT_WRITE_DONE_V2:
+		atomic_inc(&audio->in_count);
+		wake_up(&audio->write_wait);
+		break;
+	case ASM_DATA_EVENT_RENDERED_EOS:
+		audio->eos_rsp = 1;
+		wake_up(&audio->read_wait);
+		break;
+	case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
+		break;
+	case ASM_SESSION_EVENTX_OVERFLOW:
+		pr_err("%s:session id %d: ASM_SESSION_EVENT_TX_OVERFLOW\n",
+			__func__, audio->ac->session);
+		break;
+	case RESET_EVENTS:
+		pr_debug("%s:received RESET EVENTS\n", __func__);
+		audio->enabled = 0;
+		audio->stopped = 1;
+		audio->event_abort = 1;
+		audio->reset_event = true;
+		wake_up(&audio->read_wait);
+		wake_up(&audio->write_wait);
+		break;
+	default:
+		pr_debug("%s:session id %d: Ignore opcode[0x%x]\n", __func__,
+			audio->ac->session, opcode);
+		break;
+	}
+	spin_unlock_irqrestore(&audio->dsp_lock, flags);
+}
+
+void  audio_in_get_dsp_frames(void *priv,
+	uint32_t token,	uint32_t *payload)
+{
+	struct q6audio_in *audio = (struct q6audio_in *)priv;
+	uint32_t index;
+
+	index = q6asm_get_buf_index_from_token(token);
+	pr_debug("%s:session id %d: index=%d nr frames=%d offset[%d]\n",
+			__func__, audio->ac->session, token, payload[9],
+			payload[5]);
+	pr_debug("%s:session id %d: timemsw=%d lsw=%d\n", __func__,
+			audio->ac->session, payload[7], payload[6]);
+	pr_debug("%s:session id %d: uflags=0x%8x uid=0x%8x\n", __func__,
+			audio->ac->session, payload[8], payload[10]);
+	pr_debug("%s:session id %d: enc_framesotal_size=0x%8x\n", __func__,
+			audio->ac->session, payload[4]);
+
+	/* Ensure the index is within max array size: FRAME_NUM */
+	if (index >= FRAME_NUM) {
+		pr_err("%s: Invalid index %d\n",
+			__func__, index);
+		return;
+	}
+
+	audio->out_frame_info[index][0] = payload[9];
+	audio->out_frame_info[index][1] = payload[5];
+
+	/* statistics of read */
+	atomic_add(payload[4], &audio->in_bytes);
+	atomic_add(payload[9], &audio->in_samples);
+
+	if (atomic_read(&audio->out_count) <= audio->str_cfg.buffer_count) {
+		atomic_inc(&audio->out_count);
+		wake_up(&audio->read_wait);
+	}
+}
diff --git a/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
new file mode 100644
index 0000000..9f76458
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/q6audio_v2_aio.c
@@ -0,0 +1,223 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/atomic.h>
+#include <asm/ioctls.h>
+#include "audio_utils_aio.h"
+
+void q6_audio_cb(uint32_t opcode, uint32_t token,
+		uint32_t *payload, void *priv)
+{
+	struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+
+	pr_debug("%s:opcode = %x token = 0x%x\n", __func__, opcode, token);
+	switch (opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2:
+	case ASM_DATA_EVENT_READ_DONE_V2:
+	case ASM_DATA_EVENT_RENDERED_EOS:
+	case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+	case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+	case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+	case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+	case RESET_EVENTS:
+		audio_aio_cb(opcode, token, payload, audio);
+		break;
+	default:
+		pr_debug("%s:Unhandled event = 0x%8x\n", __func__, opcode);
+		break;
+	}
+}
+
+void audio_aio_cb(uint32_t opcode, uint32_t token,
+		uint32_t *payload,  void *priv/*struct q6audio_aio *audio*/)
+{
+	struct q6audio_aio *audio = (struct q6audio_aio *)priv;
+	union msm_audio_event_payload e_payload;
+
+	switch (opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2:
+		pr_debug("%s[%pK]:ASM_DATA_EVENT_WRITE_DONE token = 0x%x\n",
+			__func__, audio, token);
+		audio_aio_async_write_ack(audio, token, payload);
+		break;
+	case ASM_DATA_EVENT_READ_DONE_V2:
+		pr_debug("%s[%pK]:ASM_DATA_EVENT_READ_DONE token = 0x%x\n",
+			__func__, audio, token);
+		audio_aio_async_read_ack(audio, token, payload);
+		break;
+	case ASM_DATA_EVENT_RENDERED_EOS:
+		/* EOS Handle */
+		pr_debug("%s[%pK]:ASM_DATA_CMDRSP_EOS\n", __func__, audio);
+		if (audio->feedback) { /* Non-Tunnel mode */
+			audio->eos_rsp = 1;
+			/* propagate input EOS i/p buffer,
+			 * after receiving DSP acknowledgment
+			 */
+			if (audio->eos_flag &&
+				(audio->eos_write_payload.aio_buf.buf_addr)) {
+				audio_aio_post_event(audio,
+						AUDIO_EVENT_WRITE_DONE,
+						audio->eos_write_payload);
+				memset(&audio->eos_write_payload, 0,
+					sizeof(union msm_audio_event_payload));
+				audio->eos_flag = 0;
+			}
+		} else { /* Tunnel mode */
+			audio->eos_rsp = 1;
+			wake_up(&audio->write_wait);
+			wake_up(&audio->cmd_wait);
+		}
+		break;
+	case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+	case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+		pr_debug("%s[%pK]:payload0[%x] payloa1d[%x]opcode= 0x%x\n",
+			__func__, audio, payload[0], payload[1], opcode);
+		break;
+	case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+	case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+		pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0]-sr = %d, payload[1]-chl = %d, payload[2] = %d, payload[3] = %d\n",
+					 __func__, audio, payload[0],
+					 payload[1], payload[2], payload[3]);
+
+		pr_debug("%s[%pK]: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, sr(prev) = %d, chl(prev) = %d,",
+		__func__, audio, audio->pcm_cfg.sample_rate,
+		audio->pcm_cfg.channel_count);
+
+		audio->pcm_cfg.sample_rate = payload[0];
+		audio->pcm_cfg.channel_count = payload[1] & 0xFFFF;
+		e_payload.stream_info.chan_info = audio->pcm_cfg.channel_count;
+		e_payload.stream_info.sample_rate = audio->pcm_cfg.sample_rate;
+		audio_aio_post_event(audio, AUDIO_EVENT_STREAM_INFO, e_payload);
+		break;
+	case RESET_EVENTS:
+		pr_err("%s: Received opcode:0x%x\n", __func__, opcode);
+		audio->stopped = 1;
+		audio->reset_event = true;
+		wake_up(&audio->event_wait);
+		break;
+	default:
+		break;
+	}
+}
+
+void extract_meta_out_info(struct q6audio_aio *audio,
+		struct audio_aio_buffer_node *buf_node, int dir)
+{
+	struct dec_meta_out *meta_data = buf_node->kvaddr;
+	uint32_t temp;
+
+	if (dir) { /* input buffer - Write */
+		if (audio->buf_cfg.meta_info_enable)
+			memcpy(&buf_node->meta_info.meta_in,
+			(char *)buf_node->kvaddr, sizeof(struct dec_meta_in));
+		else
+			memset(&buf_node->meta_info.meta_in,
+			0, sizeof(struct dec_meta_in));
+		pr_debug("%s[%pK]:i/p: msw_ts %d lsw_ts %d nflags 0x%8x\n",
+			__func__, audio,
+			buf_node->meta_info.meta_in.ntimestamp.highpart,
+			buf_node->meta_info.meta_in.ntimestamp.lowpart,
+			buf_node->meta_info.meta_in.nflags);
+	} else { /* output buffer - Read */
+		memcpy((char *)buf_node->kvaddr,
+			&buf_node->meta_info.meta_out,
+			sizeof(struct dec_meta_out));
+		meta_data->meta_out_dsp[0].nflags = 0x00000000;
+		temp = meta_data->meta_out_dsp[0].msw_ts;
+		meta_data->meta_out_dsp[0].msw_ts =
+				meta_data->meta_out_dsp[0].lsw_ts;
+		meta_data->meta_out_dsp[0].lsw_ts = temp;
+
+		pr_debug("%s[%pK]:o/p: msw_ts %d lsw_ts %d nflags 0x%8x, num_frames = %d\n",
+		__func__, audio,
+		((struct dec_meta_out *)buf_node->kvaddr)->
+			meta_out_dsp[0].msw_ts,
+		((struct dec_meta_out *)buf_node->kvaddr)->
+			meta_out_dsp[0].lsw_ts,
+		((struct dec_meta_out *)buf_node->kvaddr)->
+			meta_out_dsp[0].nflags,
+		((struct dec_meta_out *)buf_node->kvaddr)->num_of_frames);
+	}
+}
+
+/* Read buffer from DSP / Handle Ack from DSP */
+void audio_aio_async_read_ack(struct q6audio_aio *audio, uint32_t token,
+			uint32_t *payload)
+{
+	unsigned long flags;
+	union msm_audio_event_payload event_payload;
+	struct audio_aio_buffer_node *filled_buf;
+
+	pr_debug("%s\n", __func__);
+
+	/* No active flush in progress */
+	if (audio->rflush)
+		return;
+
+	/* Statistics of read */
+	atomic_add(payload[4], &audio->in_bytes);
+	atomic_add(payload[9], &audio->in_samples);
+
+	spin_lock_irqsave(&audio->dsp_lock, flags);
+	if (list_empty(&audio->in_queue)) {
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		pr_warn("%s unexpected ack from dsp\n", __func__);
+		return;
+	}
+	filled_buf = list_first_entry(&audio->in_queue,
+					struct audio_aio_buffer_node, list);
+
+	pr_debug("%s token: 0x[%x], filled_buf->token: 0x[%x]",
+				 __func__, token, filled_buf->token);
+	if (token == (filled_buf->token)) {
+		list_del(&filled_buf->list);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+		event_payload.aio_buf = filled_buf->buf;
+		/* Read done Buffer due to flush/normal condition
+		 * after EOS event, so append EOS buffer
+		 */
+		if (audio->eos_rsp == 0x1) {
+			event_payload.aio_buf.data_len =
+			insert_eos_buf(audio, filled_buf);
+			/* Reset flag back to indicate eos intimated */
+			audio->eos_rsp = 0;
+		} else {
+			filled_buf->meta_info.meta_out.num_of_frames
+							 = payload[9];
+			event_payload.aio_buf.data_len = payload[4]
+				 + payload[5] + sizeof(struct dec_meta_out);
+			pr_debug("%s[%pK]:nr of frames 0x%8x len=%d\n",
+				__func__, audio,
+				filled_buf->meta_info.meta_out.num_of_frames,
+				event_payload.aio_buf.data_len);
+			extract_meta_out_info(audio, filled_buf, 0);
+			audio->eos_rsp = 0;
+		}
+		pr_debug("%s, posting read done to the app here\n", __func__);
+		audio_aio_post_event(audio, AUDIO_EVENT_READ_DONE,
+					event_payload);
+		kfree(filled_buf);
+	} else {
+		pr_err("%s[%pK]:expected=%x ret=%x\n",
+			__func__, audio, filled_buf->token, token);
+		spin_unlock_irqrestore(&audio->dsp_lock, flags);
+	}
+}
diff --git a/drivers/misc/qcom/qdsp6v2/qcelp_in.c b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
new file mode 100644
index 0000000..da5520f
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/qcelp_in.c
@@ -0,0 +1,410 @@
+/* Copyright (c) 2010-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+#include <linux/msm_audio_qcp.h>
+#include <linux/atomic.h>
+#include <linux/compat.h>
+#include <asm/ioctls.h>
+#include "audio_utils.h"
+
+/* Buffer with meta*/
+#define PCM_BUF_SIZE		(4096 + sizeof(struct meta_in))
+
+/* Maximum 10 frames in buffer with meta */
+#define FRAME_SIZE		(1 + ((35+sizeof(struct meta_out_dsp)) * 10))
+
+static long qcelp_in_ioctl_shared(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+	int cnt = 0;
+
+	switch (cmd) {
+	case AUDIO_START: {
+		struct msm_audio_qcelp_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		pr_debug("%s:session id %d: default buf alloc[%d]\n", __func__,
+				audio->ac->session, audio->buf_alloc);
+		if (audio->enabled == 1) {
+			pr_info("%s:AUDIO_START already over\n", __func__);
+			rc = 0;
+			break;
+		}
+		rc = audio_in_buf_alloc(audio);
+		if (rc < 0) {
+			pr_err("%s:session id %d: buffer allocation failed\n",
+				__func__, audio->ac->session);
+			break;
+		}
+
+		/* reduced_rate_level, rate_modulation_cmd set to zero
+		 * currently not configurable from user space
+		 */
+		rc = q6asm_enc_cfg_blk_qcelp(audio->ac,
+			audio->buf_cfg.frames_per_buf,
+			enc_cfg->min_bit_rate,
+			enc_cfg->max_bit_rate, 0, 0);
+
+		if (rc < 0) {
+			pr_err("%s:session id %d: cmd qcelp media format block failed\n",
+					__func__, audio->ac->session);
+			break;
+		}
+		if (audio->feedback == NON_TUNNEL_MODE) {
+			rc = q6asm_media_format_block_pcm(audio->ac,
+				audio->pcm_cfg.sample_rate,
+				audio->pcm_cfg.channel_count);
+
+			if (rc < 0) {
+				pr_err("%s:session id %d: media format block failed\n",
+					__func__, audio->ac->session);
+				break;
+			}
+		}
+		pr_debug("%s:session id %d: AUDIO_START enable[%d]\n", __func__,
+				audio->ac->session, audio->enabled);
+		rc = audio_in_enable(audio);
+		if (!rc) {
+			audio->enabled = 1;
+		} else {
+			audio->enabled = 0;
+			pr_err("%s:session id %d: Audio Start procedure failed rc=%d\n",
+					__func__, audio->ac->session, rc);
+			break;
+		}
+		while (cnt++ < audio->str_cfg.buffer_count)
+			q6asm_read(audio->ac); /* Push buffer to DSP */
+		rc = 0;
+		pr_debug("%s:session id %d: AUDIO_START success enable[%d]\n",
+				__func__, audio->ac->session, audio->enabled);
+		break;
+	}
+	case AUDIO_STOP: {
+		pr_debug("%s:session id %d: AUDIO_STOP\n", __func__,
+				audio->ac->session);
+		rc = audio_in_disable(audio);
+		if (rc  < 0) {
+			pr_err("%s:session id %d: Audio Stop procedure failed rc=%d\n",
+				__func__, audio->ac->session,
+					rc);
+			break;
+		}
+		break;
+	}
+	case AUDIO_SET_QCELP_ENC_CONFIG: {
+		struct msm_audio_qcelp_enc_config *cfg;
+		struct msm_audio_qcelp_enc_config *enc_cfg;
+
+		enc_cfg = audio->enc_cfg;
+		cfg = (struct msm_audio_qcelp_enc_config *)arg;
+		if (cfg == NULL) {
+			pr_err("%s: NULL config pointer\n", __func__);
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg->min_bit_rate > 4 ||
+			 cfg->min_bit_rate < 1) {
+			pr_err("%s:session id %d: invalid min bitrate\n",
+					__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		if (cfg->max_bit_rate > 4 ||
+			 cfg->max_bit_rate < 1) {
+			pr_err("%s:session id %d: invalid max bitrate\n",
+					__func__, audio->ac->session);
+			rc = -EINVAL;
+			break;
+		}
+		enc_cfg->cdma_rate = cfg->cdma_rate;
+		enc_cfg->min_bit_rate = cfg->min_bit_rate;
+		enc_cfg->max_bit_rate = cfg->max_bit_rate;
+		pr_debug("%s:session id %d: min_bit_rate= 0x%x max_bit_rate=0x%x\n",
+			__func__,
+			audio->ac->session, enc_cfg->min_bit_rate,
+			enc_cfg->max_bit_rate);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+static long qcelp_in_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = qcelp_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_QCELP_ENC_CONFIG: {
+		if (copy_to_user((void *)arg, audio->enc_cfg,
+			sizeof(struct msm_audio_qcelp_enc_config))) {
+			pr_err(
+				"%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG failed",
+				__func__);
+			rc = -EFAULT;
+		}
+		break;
+	}
+	case AUDIO_SET_QCELP_ENC_CONFIG: {
+		struct msm_audio_qcelp_enc_config cfg;
+
+		if (copy_from_user(&cfg, (void *) arg,
+				sizeof(cfg))) {
+			pr_err(
+				"%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG failed",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. Rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+
+#ifdef CONFIG_COMPAT
+struct msm_audio_qcelp_enc_config32 {
+	u32 cdma_rate;
+	u32 min_bit_rate;
+	u32 max_bit_rate;
+};
+
+enum {
+	AUDIO_SET_QCELP_ENC_CONFIG_32 = _IOW(AUDIO_IOCTL_MAGIC,
+		0, struct msm_audio_qcelp_enc_config32),
+	AUDIO_GET_QCELP_ENC_CONFIG_32 = _IOR(AUDIO_IOCTL_MAGIC,
+		1, struct msm_audio_qcelp_enc_config32)
+};
+
+static long qcelp_in_compat_ioctl(struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct q6audio_in  *audio = file->private_data;
+	int rc = 0;
+
+	switch (cmd) {
+	case AUDIO_START:
+	case AUDIO_STOP: {
+		rc = qcelp_in_ioctl_shared(file, cmd, arg);
+		break;
+	}
+	case AUDIO_GET_QCELP_ENC_CONFIG_32: {
+		struct msm_audio_qcelp_enc_config32 cfg_32;
+		struct msm_audio_qcelp_enc_config *enc_cfg;
+
+		memset(&cfg_32, 0, sizeof(cfg_32));
+
+		enc_cfg = (struct msm_audio_qcelp_enc_config *)audio->enc_cfg;
+		cfg_32.cdma_rate = enc_cfg->cdma_rate;
+		cfg_32.min_bit_rate = enc_cfg->min_bit_rate;
+		cfg_32.max_bit_rate = enc_cfg->max_bit_rate;
+		if (copy_to_user((void *)arg, &cfg_32,
+			sizeof(cfg_32))) {
+			pr_err("%s: copy_to_user for AUDIO_GET_QCELP_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+}
+		break;
+	}
+	case AUDIO_SET_QCELP_ENC_CONFIG_32: {
+		struct msm_audio_qcelp_enc_config32 cfg_32;
+		struct msm_audio_qcelp_enc_config cfg;
+
+		if (copy_from_user(&cfg_32, (void *) arg,
+				sizeof(cfg_32))) {
+			pr_err("%s: copy_from_user for AUDIO_SET_QCELP_ENC_CONFIG_32 failed\n",
+				__func__);
+			rc = -EFAULT;
+			break;
+		}
+		cfg.cdma_rate = cfg_32.cdma_rate;
+		cfg.min_bit_rate = cfg_32.min_bit_rate;
+		cfg.max_bit_rate = cfg_32.max_bit_rate;
+		cmd = AUDIO_SET_QCELP_ENC_CONFIG;
+		rc = qcelp_in_ioctl_shared(file, cmd, (unsigned long)&cfg);
+		if (rc)
+			pr_err("%s:AUDIO_SET_QCELP_ENC_CONFIG failed. rc= %d\n",
+				__func__, rc);
+		break;
+	}
+	default:
+		pr_err("%s: Unknown ioctl cmd = %d", __func__, cmd);
+		rc = -EINVAL;
+	}
+	return rc;
+}
+#else
+#define qcelp_in_compat_ioctl NULL
+#endif
+
+static int qcelp_in_open(struct inode *inode, struct file *file)
+{
+	struct q6audio_in *audio = NULL;
+	struct msm_audio_qcelp_enc_config *enc_cfg;
+	int rc = 0;
+
+	audio = kzalloc(sizeof(struct q6audio_in), GFP_KERNEL);
+
+	if (audio == NULL)
+		return -ENOMEM;
+
+	/* Allocate memory for encoder config param */
+	audio->enc_cfg = kzalloc(sizeof(struct msm_audio_qcelp_enc_config),
+				GFP_KERNEL);
+	if (audio->enc_cfg == NULL) {
+		kfree(audio);
+		return -ENOMEM;
+	}
+	enc_cfg = audio->enc_cfg;
+
+	mutex_init(&audio->lock);
+	mutex_init(&audio->read_lock);
+	mutex_init(&audio->write_lock);
+	spin_lock_init(&audio->dsp_lock);
+	init_waitqueue_head(&audio->read_wait);
+	init_waitqueue_head(&audio->write_wait);
+
+	/* Settings will be re-config at AUDIO_SET_CONFIG,
+	 * but at least we need to have initial config
+	 */
+	audio->str_cfg.buffer_size = FRAME_SIZE;
+	audio->str_cfg.buffer_count = FRAME_NUM;
+	audio->min_frame_size = 35;
+	audio->max_frames_per_buf = 10;
+	audio->pcm_cfg.buffer_size = PCM_BUF_SIZE;
+	audio->pcm_cfg.buffer_count = PCM_BUF_COUNT;
+	enc_cfg->min_bit_rate = 4;
+	enc_cfg->max_bit_rate = 4;
+	audio->pcm_cfg.channel_count = 1;
+	audio->pcm_cfg.sample_rate = 8000;
+	audio->buf_cfg.meta_info_enable = 0x01;
+	audio->buf_cfg.frames_per_buf = 0x01;
+	audio->event_abort = 0;
+
+	audio->ac = q6asm_audio_client_alloc((app_cb)q6asm_in_cb,
+				(void *)audio);
+
+	if (!audio->ac) {
+		pr_err("%s: Could not allocate memory for audio client\n",
+				__func__);
+		kfree(audio->enc_cfg);
+		kfree(audio);
+		return -ENOMEM;
+	}
+
+	/* open qcelp encoder in T/NT mode */
+	if ((file->f_mode & FMODE_WRITE) &&
+		(file->f_mode & FMODE_READ)) {
+		audio->feedback = NON_TUNNEL_MODE;
+		rc = q6asm_open_read_write(audio->ac, FORMAT_V13K,
+					FORMAT_LINEAR_PCM);
+		if (rc < 0) {
+			pr_err("%s:session id %d: NT mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: NT mode encoder success\n", __func__,
+				audio->ac->session);
+	} else if (!(file->f_mode & FMODE_WRITE) &&
+				(file->f_mode & FMODE_READ)) {
+		audio->feedback = TUNNEL_MODE;
+		rc = q6asm_open_read(audio->ac, FORMAT_V13K);
+		if (rc < 0) {
+			pr_err("%s:session id %d: T mode Open failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		/* register for tx overflow (valid for tunnel mode only) */
+		rc = q6asm_reg_tx_overflow(audio->ac, 0x01);
+		if (rc < 0) {
+			pr_err("%s:session id %d: TX Overflow registration failed rc=%d\n",
+				__func__, audio->ac->session, rc);
+			rc = -ENODEV;
+			goto fail;
+		}
+		pr_info("%s:session id %d: T mode encoder success\n", __func__,
+				audio->ac->session);
+	} else {
+		pr_err("%s:session id %d: Unexpected mode\n", __func__,
+				audio->ac->session);
+		rc = -EACCES;
+		goto fail;
+	}
+
+	audio->opened = 1;
+	audio->reset_event = false;
+	atomic_set(&audio->in_count, PCM_BUF_COUNT);
+	atomic_set(&audio->out_count, 0x00);
+	audio->enc_compat_ioctl = qcelp_in_compat_ioctl;
+	audio->enc_ioctl = qcelp_in_ioctl;
+	file->private_data = audio;
+
+	pr_info("%s:session id %d: success\n", __func__, audio->ac->session);
+	return 0;
+fail:
+	q6asm_audio_client_free(audio->ac);
+	kfree(audio->enc_cfg);
+	kfree(audio);
+	return rc;
+}
+
+static const struct file_operations audio_in_fops = {
+	.owner		= THIS_MODULE,
+	.open		= qcelp_in_open,
+	.release	= audio_in_release,
+	.read		= audio_in_read,
+	.write		= audio_in_write,
+	.unlocked_ioctl	= audio_in_ioctl,
+	.compat_ioctl   = audio_in_compat_ioctl
+};
+
+struct miscdevice audio_qcelp_in_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_qcelp_in",
+	.fops	= &audio_in_fops,
+};
+
+static int __init qcelp_in_init(void)
+{
+	return misc_register(&audio_qcelp_in_misc);
+}
+
+device_initcall(qcelp_in_init);
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile
new file mode 100644
index 0000000..41f614a
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/Makefile
@@ -0,0 +1,2 @@
+ccflags-y := -I$(src)/..
+obj-$(CONFIG_MSM_ULTRASOUND) += usf.o usfcdev.o q6usm.o
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
new file mode 100644
index 0000000..f20f335
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.c
@@ -0,0 +1,1467 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/qdsp6v2/apr_us.h>
+#include "q6usm.h"
+
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL 3
+
+#define MEM_4K_OFFSET 4095
+#define MEM_4K_MASK 0xfffff000
+
+#define USM_SESSION_MAX 0x02 /* aDSP:USM limit */
+
+#define READDONE_IDX_STATUS     0
+
+#define WRITEDONE_IDX_STATUS    0
+
+/* Standard timeout in the asynchronous ops */
+#define Q6USM_TIMEOUT_JIFFIES	(1*HZ) /* 1 sec */
+
+static DEFINE_MUTEX(session_lock);
+
+static struct us_client *session[USM_SESSION_MAX];
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv);
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv);
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+			  uint32_t pkt_size, bool cmd_flg);
+
+struct usm_mmap {
+	atomic_t ref_cnt;
+	atomic_t cmd_state;
+	wait_queue_head_t cmd_wait;
+	void *apr;
+	int mem_handle;
+};
+
+static struct usm_mmap this_mmap;
+
+static void q6usm_add_mmaphdr(struct apr_hdr *hdr,
+			      uint32_t pkt_size, bool cmd_flg, u32 token)
+{
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr->src_port = 0;
+	hdr->dest_port = 0;
+	if (cmd_flg) {
+		hdr->token = token;
+		atomic_set(&this_mmap.cmd_state, 1);
+	}
+	hdr->pkt_size  = pkt_size;
+}
+
+static int q6usm_memory_map(phys_addr_t buf_add, int dir, uint32_t bufsz,
+		uint32_t bufcnt, uint32_t session, uint32_t *mem_handle)
+{
+	struct usm_cmd_memory_map_region mem_region_map;
+	int rc = 0;
+
+	if (this_mmap.apr == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6usm_add_mmaphdr(&mem_region_map.hdr,
+			  sizeof(struct usm_cmd_memory_map_region), true,
+			  ((session << 8) | dir));
+
+	mem_region_map.hdr.opcode = USM_CMD_SHARED_MEM_MAP_REGION;
+	mem_region_map.mempool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+
+	mem_region_map.num_regions = 1;
+	mem_region_map.flags = 0;
+
+	mem_region_map.shm_addr_lsw = lower_32_bits(buf_add);
+	mem_region_map.shm_addr_msw =
+			msm_audio_populate_upper_32_bits(buf_add);
+	mem_region_map.mem_size_bytes = bufsz * bufcnt;
+
+	rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_region_map);
+	if (rc < 0) {
+		pr_err("%s: mem_map op[0x%x]rc[%d]\n",
+		       __func__, mem_region_map.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(this_mmap.cmd_wait,
+				(atomic_read(&this_mmap.cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout. waited for memory_map\n", __func__);
+	} else {
+		*mem_handle = this_mmap.mem_handle;
+		rc = 0;
+	}
+fail_cmd:
+	return rc;
+}
+
+int q6usm_memory_unmap(phys_addr_t buf_add, int dir, uint32_t session,
+			uint32_t mem_handle)
+{
+	struct usm_cmd_memory_unmap_region mem_unmap;
+	int rc = 0;
+
+	if (this_mmap.apr == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6usm_add_mmaphdr(&mem_unmap.hdr,
+			  sizeof(struct usm_cmd_memory_unmap_region), true,
+			  ((session << 8) | dir));
+	mem_unmap.hdr.opcode = USM_CMD_SHARED_MEM_UNMAP_REGION;
+	mem_unmap.mem_map_handle = mem_handle;
+
+	rc = apr_send_pkt(this_mmap.apr, (uint32_t *) &mem_unmap);
+	if (rc < 0) {
+		pr_err("%s: mem_unmap op[0x%x] rc[%d]\n",
+		       __func__, mem_unmap.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(this_mmap.cmd_wait,
+				(atomic_read(&this_mmap.cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout. waited for memory_unmap\n", __func__);
+	} else
+		rc = 0;
+fail_cmd:
+	return rc;
+}
+
+static int q6usm_session_alloc(struct us_client *usc)
+{
+	int ind = 0;
+
+	mutex_lock(&session_lock);
+	for (ind = 0; ind < USM_SESSION_MAX; ++ind) {
+		if (!session[ind]) {
+			session[ind] = usc;
+			mutex_unlock(&session_lock);
+			++ind; /* session id: 0 reserved */
+			pr_debug("%s: session[%d] was allocated\n",
+				  __func__, ind);
+			return ind;
+		}
+	}
+	mutex_unlock(&session_lock);
+	return -ENOMEM;
+}
+
+static void q6usm_session_free(struct us_client *usc)
+{
+	/* Session index was incremented during allocation */
+	uint16_t ind = (uint16_t)usc->session - 1;
+
+	pr_debug("%s: to free session[%d]\n", __func__, ind);
+	if (ind < USM_SESSION_MAX) {
+		mutex_lock(&session_lock);
+		session[ind] = NULL;
+		mutex_unlock(&session_lock);
+	}
+}
+
+static int q6usm_us_client_buf_free(unsigned int dir,
+			     struct us_client *usc)
+{
+	struct us_port_data *port;
+	int rc = 0;
+
+	if ((usc == NULL) ||
+	    ((dir != IN) && (dir != OUT)))
+		return -EINVAL;
+
+	mutex_lock(&usc->cmd_lock);
+	port = &usc->port[dir];
+	if (port == NULL) {
+		mutex_unlock(&usc->cmd_lock);
+		return -EINVAL;
+	}
+
+	if (port->data == NULL) {
+		mutex_unlock(&usc->cmd_lock);
+		return 0;
+	}
+
+	rc = q6usm_memory_unmap(port->phys, dir, usc->session,
+				*((uint32_t *)port->ext));
+	pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__,
+		 (void *)port->data, (u64)port->phys, (void *)&port->phys);
+
+	msm_audio_ion_free(port->client, port->handle);
+
+	port->data = NULL;
+	port->phys = 0;
+	port->buf_size = 0;
+	port->buf_cnt = 0;
+	port->client = NULL;
+	port->handle = NULL;
+
+	mutex_unlock(&usc->cmd_lock);
+	return rc;
+}
+
+int q6usm_us_param_buf_free(unsigned int dir,
+			struct us_client *usc)
+{
+	struct us_port_data *port;
+	int rc = 0;
+
+	if ((usc == NULL) ||
+		((dir != IN) && (dir != OUT)))
+		return -EINVAL;
+
+	mutex_lock(&usc->cmd_lock);
+	port = &usc->port[dir];
+	if (port == NULL) {
+		mutex_unlock(&usc->cmd_lock);
+		return -EINVAL;
+	}
+
+	if (port->param_buf == NULL) {
+		mutex_unlock(&usc->cmd_lock);
+		return 0;
+	}
+
+	rc = q6usm_memory_unmap(port->param_phys, dir, usc->session,
+				*((uint32_t *)port->param_buf_mem_handle));
+	pr_debug("%s: data[%pK]phys[%llx][%pK]\n", __func__,
+		 (void *)port->param_buf, (u64)port->param_phys,
+		 (void *)&port->param_phys);
+
+	msm_audio_ion_free(port->param_client, port->param_handle);
+
+	port->param_buf = NULL;
+	port->param_phys = 0;
+	port->param_buf_size = 0;
+	port->param_client = NULL;
+	port->param_handle = NULL;
+
+	mutex_unlock(&usc->cmd_lock);
+	return rc;
+}
+
+void q6usm_us_client_free(struct us_client *usc)
+{
+	int loopcnt = 0;
+	struct us_port_data *port;
+	uint32_t *p_mem_handle = NULL;
+
+	if ((usc == NULL) ||
+	    !(usc->session))
+		return;
+
+	for (loopcnt = 0; loopcnt <= OUT; ++loopcnt) {
+		port = &usc->port[loopcnt];
+		if (port->data == NULL)
+			continue;
+		pr_debug("%s: loopcnt = %d\n", __func__, loopcnt);
+		q6usm_us_client_buf_free(loopcnt, usc);
+		q6usm_us_param_buf_free(loopcnt, usc);
+	}
+	q6usm_session_free(usc);
+	apr_deregister(usc->apr);
+
+	pr_debug("%s: APR De-Register\n", __func__);
+
+	if (atomic_read(&this_mmap.ref_cnt) <= 0) {
+		pr_err("%s: APR Common Port Already Closed\n", __func__);
+		goto done;
+	}
+
+	atomic_dec(&this_mmap.ref_cnt);
+	if (atomic_read(&this_mmap.ref_cnt) == 0) {
+		apr_deregister(this_mmap.apr);
+		pr_debug("%s: APR De-Register common port\n", __func__);
+	}
+
+done:
+	p_mem_handle = (uint32_t *)usc->port[IN].ext;
+	kfree(p_mem_handle);
+	kfree(usc);
+	pr_debug("%s:\n", __func__);
+}
+
+struct us_client *q6usm_us_client_alloc(
+	void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
+	void *priv)
+{
+	struct us_client *usc;
+	uint32_t *p_mem_handle = NULL;
+	int n;
+	int lcnt = 0;
+
+	usc = kzalloc(sizeof(struct us_client), GFP_KERNEL);
+	if (usc == NULL)
+		return NULL;
+
+	p_mem_handle = kzalloc(sizeof(uint32_t) * 4, GFP_KERNEL);
+	if (p_mem_handle == NULL) {
+		kfree(usc);
+		return NULL;
+	}
+
+	n = q6usm_session_alloc(usc);
+	if (n <= 0)
+		goto fail_session;
+	usc->session = n;
+	usc->cb = cb;
+	usc->priv = priv;
+	usc->apr = apr_register("ADSP", "USM",
+				(apr_fn)q6usm_callback,
+				((usc->session) << 8 | 0x0001),
+				usc);
+
+	if (usc->apr == NULL) {
+		pr_err("%s: Registration with APR failed\n", __func__);
+		goto fail;
+	}
+	pr_debug("%s: Registering the common port with APR\n", __func__);
+	if (atomic_read(&this_mmap.ref_cnt) == 0) {
+		this_mmap.apr = apr_register("ADSP", "USM",
+					     (apr_fn)q6usm_mmapcallback,
+					     0x0FFFFFFFF, &this_mmap);
+		if (this_mmap.apr == NULL) {
+			pr_err("%s: USM port registration failed\n",
+			       __func__);
+			goto fail;
+		}
+	}
+
+	atomic_inc(&this_mmap.ref_cnt);
+	init_waitqueue_head(&usc->cmd_wait);
+	mutex_init(&usc->cmd_lock);
+	for (lcnt = 0; lcnt <= OUT; ++lcnt) {
+		mutex_init(&usc->port[lcnt].lock);
+		spin_lock_init(&usc->port[lcnt].dsp_lock);
+		usc->port[lcnt].ext = (void *)p_mem_handle++;
+		usc->port[lcnt].param_buf_mem_handle = (void *)p_mem_handle++;
+		pr_err("%s: usc->port[%d].ext=%pK;\n",
+		       __func__, lcnt, usc->port[lcnt].ext);
+	}
+	atomic_set(&usc->cmd_state, 0);
+
+	return usc;
+fail:
+	kfree(p_mem_handle);
+	q6usm_us_client_free(usc);
+	return NULL;
+fail_session:
+	kfree(p_mem_handle);
+	kfree(usc);
+	return NULL;
+}
+
+int q6usm_us_client_buf_alloc(unsigned int dir,
+			      struct us_client *usc,
+			      unsigned int bufsz,
+			      unsigned int bufcnt)
+{
+	int rc = 0;
+	struct us_port_data *port = NULL;
+	unsigned int size = bufsz*bufcnt;
+	size_t len;
+
+	if ((usc == NULL) ||
+	    ((dir != IN) && (dir != OUT)) || (size == 0) ||
+	    (usc->session <= 0 || usc->session > USM_SESSION_MAX)) {
+		pr_err("%s: wrong parameters: size=%d; bufcnt=%d\n",
+		       __func__, size, bufcnt);
+		return -EINVAL;
+	}
+
+	mutex_lock(&usc->cmd_lock);
+
+	port = &usc->port[dir];
+
+	/* The size to allocate should be multiple of 4K bytes */
+	size = PAGE_ALIGN(size);
+
+	rc = msm_audio_ion_alloc("ultrasound_client",
+		&port->client, &port->handle,
+		size, &port->phys,
+		&len, &port->data);
+
+	if (rc) {
+		pr_err("%s: US ION allocation failed, rc = %d\n",
+			__func__, rc);
+		mutex_unlock(&usc->cmd_lock);
+		return -ENOMEM;
+	}
+
+	port->buf_cnt = bufcnt;
+	port->buf_size = bufsz;
+	pr_debug("%s: data[%pK]; phys[%llx]; [%pK]\n", __func__,
+		 (void *)port->data,
+		 (u64)port->phys,
+		 (void *)&port->phys);
+
+	rc = q6usm_memory_map(port->phys, dir, size, 1, usc->session,
+				(uint32_t *)port->ext);
+	if (rc < 0) {
+		pr_err("%s: CMD Memory_map failed\n", __func__);
+		mutex_unlock(&usc->cmd_lock);
+		q6usm_us_client_buf_free(dir, usc);
+		q6usm_us_param_buf_free(dir, usc);
+	} else {
+		mutex_unlock(&usc->cmd_lock);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+int q6usm_us_param_buf_alloc(unsigned int dir,
+			struct us_client *usc,
+			unsigned int bufsz)
+{
+	int rc = 0;
+	struct us_port_data *port = NULL;
+	unsigned int size = bufsz;
+	size_t len;
+
+	if ((usc == NULL) ||
+		((dir != IN) && (dir != OUT)) ||
+		(usc->session <= 0 || usc->session > USM_SESSION_MAX)) {
+		pr_err("%s: wrong parameters: direction=%d, bufsz=%d\n",
+			__func__, dir, bufsz);
+		return -EINVAL;
+	}
+
+	mutex_lock(&usc->cmd_lock);
+
+	port = &usc->port[dir];
+
+	if (bufsz == 0) {
+		pr_debug("%s: bufsz=0, get/set param commands are forbidden\n",
+			__func__);
+		port->param_buf = NULL;
+		mutex_unlock(&usc->cmd_lock);
+		return rc;
+	}
+
+	/* The size to allocate should be multiple of 4K bytes */
+	size = PAGE_ALIGN(size);
+
+	rc = msm_audio_ion_alloc("ultrasound_client",
+		&port->param_client, &port->param_handle,
+		size, &port->param_phys,
+		&len, &port->param_buf);
+
+	if (rc) {
+		pr_err("%s: US ION allocation failed, rc = %d\n",
+			__func__, rc);
+		mutex_unlock(&usc->cmd_lock);
+		return -ENOMEM;
+	}
+
+	port->param_buf_size = bufsz;
+	pr_debug("%s: param_buf[%pK]; param_phys[%llx]; [%pK]\n", __func__,
+		 (void *)port->param_buf,
+		 (u64)port->param_phys,
+		 (void *)&port->param_phys);
+
+	rc = q6usm_memory_map(port->param_phys, (IN | OUT), size, 1,
+			usc->session, (uint32_t *)port->param_buf_mem_handle);
+	if (rc < 0) {
+		pr_err("%s: CMD Memory_map failed\n", __func__);
+		mutex_unlock(&usc->cmd_lock);
+		q6usm_us_client_buf_free(dir, usc);
+		q6usm_us_param_buf_free(dir, usc);
+	} else {
+		mutex_unlock(&usc->cmd_lock);
+		rc = 0;
+	}
+
+	return rc;
+}
+
+static int32_t q6usm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+	uint32_t token;
+	uint32_t *payload = data->payload;
+
+	pr_debug("%s: ptr0[0x%x]; ptr1[0x%x]; opcode[0x%x]\n",
+		 __func__, payload[0], payload[1], data->opcode);
+	pr_debug("%s: token[0x%x]; payload_size[%d]; src[%d]; dest[%d];\n",
+		 __func__, data->token, data->payload_size,
+		 data->src_port, data->dest_port);
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		/* status field check */
+		if (payload[1]) {
+			pr_err("%s: wrong response[%d] on cmd [%d]\n",
+			       __func__, payload[1], payload[0]);
+		} else {
+			token = data->token;
+			switch (payload[0]) {
+			case USM_CMD_SHARED_MEM_UNMAP_REGION:
+				if (atomic_read(&this_mmap.cmd_state)) {
+					atomic_set(&this_mmap.cmd_state, 0);
+					wake_up(&this_mmap.cmd_wait);
+				}
+				/* fallthrough */
+			case USM_CMD_SHARED_MEM_MAP_REGION:
+				/* For MEM_MAP, additional answer is waited, */
+				/* therfore, no wake-up here */
+				pr_debug("%s: cmd[0x%x]; result[0x%x]\n",
+					 __func__, payload[0], payload[1]);
+				break;
+			default:
+				pr_debug("%s: wrong command[0x%x]\n",
+					 __func__, payload[0]);
+				break;
+			}
+		}
+	} else {
+		if (data->opcode == USM_CMDRSP_SHARED_MEM_MAP_REGION) {
+			this_mmap.mem_handle = payload[0];
+			pr_debug("%s: memory map handle = 0x%x",
+				__func__, payload[0]);
+			if (atomic_read(&this_mmap.cmd_state)) {
+				atomic_set(&this_mmap.cmd_state, 0);
+				wake_up(&this_mmap.cmd_wait);
+			}
+		}
+	}
+	return 0;
+}
+
+
+static int32_t q6usm_callback(struct apr_client_data *data, void *priv)
+{
+	struct us_client *usc = (struct us_client *)priv;
+	unsigned long dsp_flags;
+	uint32_t *payload = data->payload;
+	uint32_t token = data->token;
+	uint32_t opcode = Q6USM_EVENT_UNDEF;
+
+	if (usc == NULL) {
+		pr_err("%s: client info is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		/* status field check */
+		if (payload[1]) {
+			pr_err("%s: wrong response[%d] on cmd [%d]\n",
+			       __func__, payload[1], payload[0]);
+			if (usc->cb)
+				usc->cb(data->opcode, token,
+					(uint32_t *)data->payload, usc->priv);
+		} else {
+			switch (payload[0]) {
+			case USM_SESSION_CMD_RUN:
+			case USM_STREAM_CMD_CLOSE:
+				if (token != usc->session) {
+					pr_err("%s: wrong token[%d]",
+					       __func__, token);
+					break;
+				}
+			case USM_STREAM_CMD_OPEN_READ:
+			case USM_STREAM_CMD_OPEN_WRITE:
+			case USM_STREAM_CMD_SET_ENC_PARAM:
+			case USM_DATA_CMD_MEDIA_FORMAT_UPDATE:
+			case USM_SESSION_CMD_SIGNAL_DETECT_MODE:
+			case USM_STREAM_CMD_SET_PARAM:
+			case USM_STREAM_CMD_GET_PARAM:
+				if (atomic_read(&usc->cmd_state)) {
+					atomic_set(&usc->cmd_state, 0);
+					wake_up(&usc->cmd_wait);
+				}
+				if (usc->cb)
+					usc->cb(data->opcode, token,
+						(uint32_t *)data->payload,
+						usc->priv);
+				break;
+			default:
+				break;
+			}
+		}
+		return 0;
+	}
+
+	switch (data->opcode) {
+	case RESET_EVENTS: {
+		pr_err("%s: Reset event is received: %d %d\n",
+				__func__,
+				data->reset_event,
+				data->reset_proc);
+
+		opcode = RESET_EVENTS;
+
+		apr_reset(this_mmap.apr);
+		this_mmap.apr = NULL;
+
+		apr_reset(usc->apr);
+		usc->apr = NULL;
+
+		break;
+	}
+
+
+	case USM_DATA_EVENT_READ_DONE: {
+		struct us_port_data *port = &usc->port[OUT];
+
+		opcode = Q6USM_EVENT_READ_DONE;
+		spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+		if (payload[READDONE_IDX_STATUS]) {
+			pr_err("%s: wrong READDONE[%d]; token[%d]\n",
+			       __func__,
+			       payload[READDONE_IDX_STATUS],
+			       token);
+			token = USM_WRONG_TOKEN;
+			spin_unlock_irqrestore(&port->dsp_lock,
+					       dsp_flags);
+			break;
+		}
+
+		if (port->expected_token != token) {
+			u32 cpu_buf = port->cpu_buf;
+
+			pr_err("%s: expected[%d] != token[%d]\n",
+				__func__, port->expected_token, token);
+			pr_debug("%s: dsp_buf=%d; cpu_buf=%d;\n",
+				__func__,   port->dsp_buf, cpu_buf);
+
+			token = USM_WRONG_TOKEN;
+			/* To prevent data handle continiue */
+			port->expected_token = USM_WRONG_TOKEN;
+			spin_unlock_irqrestore(&port->dsp_lock,
+					       dsp_flags);
+			break;
+		} /* port->expected_token != data->token */
+
+		port->expected_token = token + 1;
+		if (port->expected_token == port->buf_cnt)
+			port->expected_token = 0;
+
+		/* gap support */
+		if (port->expected_token != port->cpu_buf) {
+			port->dsp_buf = port->expected_token;
+			token = port->dsp_buf; /* for callback */
+		} else
+			port->dsp_buf = token;
+
+		spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+		break;
+	} /* case USM_DATA_EVENT_READ_DONE */
+
+	case USM_DATA_EVENT_WRITE_DONE: {
+		struct us_port_data *port = &usc->port[IN];
+
+		opcode = Q6USM_EVENT_WRITE_DONE;
+		if (payload[WRITEDONE_IDX_STATUS]) {
+			pr_err("%s: wrong WRITEDONE_IDX_STATUS[%d]\n",
+			       __func__,
+			       payload[WRITEDONE_IDX_STATUS]);
+			break;
+		}
+
+		spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+		port->dsp_buf = token + 1;
+		if (port->dsp_buf == port->buf_cnt)
+			port->dsp_buf = 0;
+		spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+		break;
+	} /* case USM_DATA_EVENT_WRITE_DONE */
+
+	case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT: {
+		pr_debug("%s: US detect result: result=%d",
+			 __func__,
+			 payload[0]);
+		opcode = Q6USM_EVENT_SIGNAL_DETECT_RESULT;
+
+		break;
+	} /* case USM_SESSION_EVENT_SIGNAL_DETECT_RESULT */
+
+	default:
+		return 0;
+
+	} /* switch */
+
+	if (usc->cb)
+		usc->cb(opcode, token,
+			data->payload, usc->priv);
+
+	return 0;
+}
+
+uint32_t q6usm_get_virtual_address(int dir,
+				   struct us_client *usc,
+				   struct vm_area_struct *vms)
+{
+	uint32_t ret = 0xffffffff;
+
+	if (vms && (usc != NULL) && ((dir == IN) || (dir == OUT))) {
+		struct us_port_data *port = &usc->port[dir];
+		int size = PAGE_ALIGN(port->buf_size * port->buf_cnt);
+		struct audio_buffer ab;
+
+		ab.phys = port->phys;
+		ab.data = port->data;
+		ab.used = 1;
+		ab.size = size;
+		ab.actual_size = size;
+		ab.handle = port->handle;
+		ab.client = port->client;
+
+		ret = msm_audio_ion_mmap(&ab, vms);
+
+	}
+	return ret;
+}
+
+static void q6usm_add_hdr(struct us_client *usc, struct apr_hdr *hdr,
+			  uint32_t pkt_size, bool cmd_flg)
+{
+	mutex_lock(&usc->cmd_lock);
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				       APR_HDR_LEN(sizeof(struct apr_hdr)),
+				       APR_PKT_VER);
+	hdr->src_svc = ((struct apr_svc *)usc->apr)->id;
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->dest_svc = APR_SVC_USM;
+	hdr->dest_domain = APR_DOMAIN_ADSP;
+	hdr->src_port = (usc->session << 8) | 0x0001;
+	hdr->dest_port = (usc->session << 8) | 0x0001;
+	if (cmd_flg) {
+		hdr->token = usc->session;
+		atomic_set(&usc->cmd_state, 1);
+	}
+	hdr->pkt_size  = pkt_size;
+	mutex_unlock(&usc->cmd_lock);
+}
+
+static uint32_t q6usm_ext2int_format(uint32_t ext_format)
+{
+	uint32_t int_format = INVALID_FORMAT;
+
+	switch (ext_format) {
+	case FORMAT_USPS_EPOS:
+		int_format = US_POINT_EPOS_FORMAT_V2;
+		break;
+	case FORMAT_USRAW:
+		int_format = US_RAW_FORMAT_V2;
+		break;
+	case FORMAT_USPROX:
+		int_format = US_PROX_FORMAT_V4;
+		break;
+	case FORMAT_USGES_SYNC:
+		int_format = US_GES_SYNC_FORMAT;
+		break;
+	case FORMAT_USRAW_SYNC:
+		int_format = US_RAW_SYNC_FORMAT;
+		break;
+	default:
+		pr_err("%s: Invalid format[%d]\n", __func__, ext_format);
+		break;
+	}
+
+	return int_format;
+}
+
+int q6usm_open_read(struct us_client *usc,
+		    uint32_t format)
+{
+	uint32_t int_format = INVALID_FORMAT;
+	int rc = 0x00;
+	struct usm_stream_cmd_open_read open;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: client or its apr is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: session[%d]", __func__, usc->session);
+
+	q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+	open.hdr.opcode = USM_STREAM_CMD_OPEN_READ;
+	open.src_endpoint = 0; /* AFE */
+	open.pre_proc_top = 0; /* No preprocessing required */
+
+	int_format = q6usm_ext2int_format(format);
+	if (int_format == INVALID_FORMAT)
+		return -EINVAL;
+
+	open.uMode = STREAM_PRIORITY_NORMAL;
+	open.format = int_format;
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n",
+		       __func__, open.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout, waited for OPEN_READ rc[%d]\n",
+		       __func__, rc);
+		goto fail_cmd;
+	} else
+		rc = 0;
+fail_cmd:
+	return rc;
+}
+
+
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+	uint32_t int_format = INVALID_FORMAT;
+	struct usm_stream_cmd_encdec_cfg_blk  enc_cfg_obj;
+	struct usm_stream_cmd_encdec_cfg_blk  *enc_cfg = &enc_cfg_obj;
+	int rc = 0;
+	uint32_t total_cfg_size =
+		sizeof(struct usm_stream_cmd_encdec_cfg_blk);
+	uint32_t round_params_size = 0;
+	uint8_t  is_allocated = 0;
+
+
+	if ((usc == NULL) || (us_cfg == NULL)) {
+		pr_err("%s: wrong input", __func__);
+		return -EINVAL;
+	}
+
+	int_format = q6usm_ext2int_format(us_cfg->format_id);
+	if (int_format == INVALID_FORMAT) {
+		pr_err("%s: wrong input format[%d]",
+		       __func__, us_cfg->format_id);
+		return -EINVAL;
+	}
+
+	/* Transparent configuration data is after enc_cfg */
+	/* Integer number of u32s is required */
+	round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+	if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+		/* Dynamic allocated encdec_cfg_blk is required */
+		/* static part use */
+		round_params_size -= USM_MAX_CFG_DATA_SIZE;
+		total_cfg_size += round_params_size;
+		enc_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+		if (enc_cfg == NULL) {
+			pr_err("%s: enc_cfg[%d] allocation failed\n",
+			       __func__, total_cfg_size);
+			return -ENOMEM;
+		}
+		is_allocated = 1;
+	} else
+		round_params_size = 0;
+
+	q6usm_add_hdr(usc, &enc_cfg->hdr, total_cfg_size, true);
+
+	enc_cfg->hdr.opcode = USM_STREAM_CMD_SET_ENC_PARAM;
+	enc_cfg->param_id = USM_PARAM_ID_ENCDEC_ENC_CFG_BLK;
+	enc_cfg->param_size = sizeof(struct usm_encode_cfg_blk)+
+				round_params_size;
+	enc_cfg->enc_blk.frames_per_buf = 1;
+	enc_cfg->enc_blk.format_id = int_format;
+	enc_cfg->enc_blk.cfg_size = sizeof(struct usm_cfg_common)+
+				    USM_MAX_CFG_DATA_SIZE +
+				    round_params_size;
+	memcpy(&(enc_cfg->enc_blk.cfg_common), &(us_cfg->cfg_common),
+	       sizeof(struct usm_cfg_common));
+
+	/* Transparent data copy */
+	memcpy(enc_cfg->enc_blk.transp_data, us_cfg->params,
+	       us_cfg->params_size);
+	pr_debug("%s: cfg_size[%d], params_size[%d]\n",
+		__func__,
+		enc_cfg->enc_blk.cfg_size,
+		us_cfg->params_size);
+	pr_debug("%s: params[%d,%d,%d,%d, %d,%d,%d,%d]\n",
+		__func__,
+		enc_cfg->enc_blk.transp_data[0],
+		enc_cfg->enc_blk.transp_data[1],
+		enc_cfg->enc_blk.transp_data[2],
+		enc_cfg->enc_blk.transp_data[3],
+		enc_cfg->enc_blk.transp_data[4],
+		enc_cfg->enc_blk.transp_data[5],
+		enc_cfg->enc_blk.transp_data[6],
+		enc_cfg->enc_blk.transp_data[7]
+	       );
+	pr_debug("%s: srate:%d, ch=%d, bps= %d;\n",
+		__func__, enc_cfg->enc_blk.cfg_common.sample_rate,
+		enc_cfg->enc_blk.cfg_common.ch_cfg,
+		enc_cfg->enc_blk.cfg_common.bits_per_sample);
+	pr_debug("dmap:[0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x]; dev_id=0x%x\n",
+		enc_cfg->enc_blk.cfg_common.data_map[0],
+		enc_cfg->enc_blk.cfg_common.data_map[1],
+		enc_cfg->enc_blk.cfg_common.data_map[2],
+		enc_cfg->enc_blk.cfg_common.data_map[3],
+		enc_cfg->enc_blk.cfg_common.data_map[4],
+		enc_cfg->enc_blk.cfg_common.data_map[5],
+		enc_cfg->enc_blk.cfg_common.data_map[6],
+		enc_cfg->enc_blk.cfg_common.data_map[7],
+		enc_cfg->enc_blk.cfg_common.dev_id);
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) enc_cfg);
+	if (rc < 0) {
+		pr_err("%s:Comamnd open failed\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout opcode[0x%x]\n",
+		       __func__, enc_cfg->hdr.opcode);
+	} else
+		rc = 0;
+
+fail_cmd:
+	if (is_allocated == 1)
+		kfree(enc_cfg);
+
+	return rc;
+}
+
+int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg)
+{
+
+	uint32_t int_format = INVALID_FORMAT;
+	struct usm_stream_media_format_update dec_cfg_obj;
+	struct usm_stream_media_format_update *dec_cfg = &dec_cfg_obj;
+
+	int rc = 0;
+	uint32_t total_cfg_size = sizeof(struct usm_stream_media_format_update);
+	uint32_t round_params_size = 0;
+	uint8_t  is_allocated = 0;
+
+
+	if ((usc == NULL) || (us_cfg == NULL)) {
+		pr_err("%s: wrong input", __func__);
+		return -EINVAL;
+	}
+
+	int_format = q6usm_ext2int_format(us_cfg->format_id);
+	if (int_format == INVALID_FORMAT) {
+		pr_err("%s: wrong input format[%d]",
+		       __func__, us_cfg->format_id);
+		return -EINVAL;
+	}
+
+	/* Transparent configuration data is after enc_cfg */
+	/* Integer number of u32s is required */
+	round_params_size = ((us_cfg->params_size + 3)/4) * 4;
+	if (round_params_size > USM_MAX_CFG_DATA_SIZE) {
+		/* Dynamic allocated encdec_cfg_blk is required */
+		/* static part use */
+		round_params_size -= USM_MAX_CFG_DATA_SIZE;
+		total_cfg_size += round_params_size;
+		dec_cfg = kzalloc(total_cfg_size, GFP_KERNEL);
+		if (dec_cfg == NULL) {
+			pr_err("%s:dec_cfg[%d] allocation failed\n",
+			       __func__, total_cfg_size);
+			return -ENOMEM;
+		}
+		is_allocated = 1;
+	} else { /* static transp_data is enough */
+		round_params_size = 0;
+	}
+
+	q6usm_add_hdr(usc, &dec_cfg->hdr, total_cfg_size, true);
+
+	dec_cfg->hdr.opcode = USM_DATA_CMD_MEDIA_FORMAT_UPDATE;
+	dec_cfg->format_id = int_format;
+	dec_cfg->cfg_size = sizeof(struct usm_cfg_common) +
+			    USM_MAX_CFG_DATA_SIZE +
+			    round_params_size;
+	memcpy(&(dec_cfg->cfg_common), &(us_cfg->cfg_common),
+	       sizeof(struct usm_cfg_common));
+	/* Transparent data copy */
+	memcpy(dec_cfg->transp_data, us_cfg->params, us_cfg->params_size);
+	pr_debug("%s: cfg_size[%d], params_size[%d]; parambytes[%d,%d,%d,%d]\n",
+		__func__,
+		dec_cfg->cfg_size,
+		us_cfg->params_size,
+		dec_cfg->transp_data[0],
+		dec_cfg->transp_data[1],
+		dec_cfg->transp_data[2],
+		dec_cfg->transp_data[3]
+	       );
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) dec_cfg);
+	if (rc < 0) {
+		pr_err("%s:Comamnd open failed\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout opcode[0x%x]\n",
+		       __func__, dec_cfg->hdr.opcode);
+	} else
+		rc = 0;
+
+fail_cmd:
+	if (is_allocated == 1)
+		kfree(dec_cfg);
+
+	return rc;
+}
+
+int q6usm_open_write(struct us_client *usc,
+		     uint32_t format)
+{
+	int rc = 0;
+	uint32_t int_format = INVALID_FORMAT;
+	struct usm_stream_cmd_open_write open;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: session[%d]", __func__, usc->session);
+
+	q6usm_add_hdr(usc, &open.hdr, sizeof(open), true);
+	open.hdr.opcode = USM_STREAM_CMD_OPEN_WRITE;
+
+	int_format = q6usm_ext2int_format(format);
+	if (int_format == INVALID_FORMAT) {
+		pr_err("%s: wrong format[%d]", __func__, format);
+		return -EINVAL;
+	}
+
+	open.format = int_format;
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s:open failed op[0x%x]rc[%d]\n",
+		       __func__, open.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s:timeout. waited for OPEN_WRITR rc[%d]\n",
+		       __func__, rc);
+		goto fail_cmd;
+	} else
+		rc = 0;
+
+fail_cmd:
+	return rc;
+}
+
+int q6usm_run(struct us_client *usc, uint32_t flags,
+	      uint32_t msw_ts, uint32_t lsw_ts)
+{
+	struct usm_stream_cmd_run run;
+	int rc = 0;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	q6usm_add_hdr(usc, &run.hdr, sizeof(run), true);
+
+	run.hdr.opcode = USM_SESSION_CMD_RUN;
+	run.flags    = flags;
+	run.msw_ts   = msw_ts;
+	run.lsw_ts   = lsw_ts;
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) &run);
+	if (rc < 0) {
+		pr_err("%s: Commmand run failed[%d]\n", __func__, rc);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: timeout. waited for run success rc[%d]\n",
+		       __func__, rc);
+	} else
+		rc = 0;
+
+fail_cmd:
+	return rc;
+}
+
+
+
+int q6usm_read(struct us_client *usc, uint32_t read_ind)
+{
+	struct usm_stream_cmd_read read;
+	struct us_port_data *port = NULL;
+	int rc = 0;
+	u32 read_counter = 0;
+	u32 loop_ind = 0;
+	u64 buf_addr = 0;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	port = &usc->port[OUT];
+
+	if (read_ind > port->buf_cnt) {
+		pr_err("%s: wrong read_ind[%d]\n",
+		       __func__, read_ind);
+		return -EINVAL;
+	}
+	if (read_ind == port->cpu_buf) {
+		pr_err("%s: no free region\n", __func__);
+		return 0;
+	}
+
+	if (read_ind > port->cpu_buf) { /* 1 range */
+		read_counter = read_ind - port->cpu_buf;
+	} else { /* 2 ranges */
+		read_counter = (port->buf_cnt - port->cpu_buf) + read_ind;
+	}
+
+	q6usm_add_hdr(usc, &read.hdr, sizeof(read), false);
+
+	read.hdr.opcode = USM_DATA_CMD_READ;
+	read.buf_size = port->buf_size;
+	buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf);
+	read.buf_addr_lsw = lower_32_bits(buf_addr);
+	read.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr);
+	read.mem_map_handle = *((uint32_t *)(port->ext));
+
+	for (loop_ind = 0; loop_ind < read_counter; ++loop_ind) {
+		u32 temp_cpu_buf = port->cpu_buf;
+
+		buf_addr = (u64)(port->phys) +
+				port->buf_size * (port->cpu_buf);
+		read.buf_addr_lsw = lower_32_bits(buf_addr);
+		read.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr);
+		read.seq_id = port->cpu_buf;
+		read.hdr.token = port->cpu_buf;
+		read.counter = 1;
+
+		++(port->cpu_buf);
+		if (port->cpu_buf == port->buf_cnt)
+			port->cpu_buf = 0;
+
+		rc = apr_send_pkt(usc->apr, (uint32_t *) &read);
+
+		if (rc < 0) {
+			port->cpu_buf = temp_cpu_buf;
+
+			pr_err("%s:read op[0x%x]rc[%d]\n",
+			       __func__, read.hdr.opcode, rc);
+			break;
+		}
+
+		rc = 0;
+	} /* bufs loop */
+
+	return rc;
+}
+
+int q6usm_write(struct us_client *usc, uint32_t write_ind)
+{
+	int rc = 0;
+	struct usm_stream_cmd_write cmd_write;
+	struct us_port_data *port = NULL;
+	u32 current_dsp_buf = 0;
+	u64 buf_addr = 0;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	port = &usc->port[IN];
+
+	current_dsp_buf = port->dsp_buf;
+	/* free region, caused by new dsp_buf report from DSP, */
+	/* can be only extended */
+	if (port->cpu_buf >= current_dsp_buf) {
+		/* 2 -part free region, including empty buffer */
+		if ((write_ind <= port->cpu_buf)  &&
+		    (write_ind > current_dsp_buf)) {
+			pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+			       __func__, write_ind,
+			       current_dsp_buf, port->cpu_buf);
+			return -EINVAL;
+		}
+	} else {
+		/* 1 -part free region */
+		if ((write_ind <= port->cpu_buf)  ||
+		    (write_ind > current_dsp_buf)) {
+			pr_err("%s: wrong w_ind[%d]; d_buf=%d; c_buf=%d\n",
+			       __func__, write_ind,
+			       current_dsp_buf, port->cpu_buf);
+			return -EINVAL;
+		}
+	}
+
+	q6usm_add_hdr(usc, &cmd_write.hdr, sizeof(cmd_write), false);
+
+	cmd_write.hdr.opcode = USM_DATA_CMD_WRITE;
+	cmd_write.buf_size = port->buf_size;
+	buf_addr = (u64)(port->phys) + port->buf_size * (port->cpu_buf);
+	cmd_write.buf_addr_lsw = lower_32_bits(buf_addr);
+	cmd_write.buf_addr_msw = msm_audio_populate_upper_32_bits(buf_addr);
+	cmd_write.mem_map_handle = *((uint32_t *)(port->ext));
+	cmd_write.res0 = 0;
+	cmd_write.res1 = 0;
+	cmd_write.res2 = 0;
+
+	while (port->cpu_buf != write_ind) {
+		u32 temp_cpu_buf = port->cpu_buf;
+
+		buf_addr = (u64)(port->phys) +
+				port->buf_size * (port->cpu_buf);
+		cmd_write.buf_addr_lsw = lower_32_bits(buf_addr);
+		cmd_write.buf_addr_msw =
+				msm_audio_populate_upper_32_bits(buf_addr);
+		cmd_write.seq_id = port->cpu_buf;
+		cmd_write.hdr.token = port->cpu_buf;
+
+		++(port->cpu_buf);
+		if (port->cpu_buf == port->buf_cnt)
+			port->cpu_buf = 0;
+
+		rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_write);
+
+		if (rc < 0) {
+			port->cpu_buf = temp_cpu_buf;
+			pr_err("%s:write op[0x%x];rc[%d];cpu_buf[%d]\n",
+			       __func__, cmd_write.hdr.opcode,
+			       rc, port->cpu_buf);
+			break;
+		}
+
+		rc = 0;
+	}
+
+	return rc;
+}
+
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region)
+{
+	struct us_port_data *port = NULL;
+	u32 cpu_buf = 0;
+
+	if ((usc == NULL) || !free_region) {
+		pr_err("%s: input data wrong\n", __func__);
+		return false;
+	}
+	port = &usc->port[IN];
+	cpu_buf = port->cpu_buf + 1;
+	if (cpu_buf == port->buf_cnt)
+		cpu_buf = 0;
+
+	*free_region = port->dsp_buf;
+
+	return cpu_buf == *free_region;
+}
+
+int q6usm_cmd(struct us_client *usc, int cmd)
+{
+	struct apr_hdr hdr;
+	int rc = 0;
+	atomic_t *state;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	q6usm_add_hdr(usc, &hdr, sizeof(hdr), true);
+	switch (cmd) {
+	case CMD_CLOSE:
+		hdr.opcode = USM_STREAM_CMD_CLOSE;
+		state = &usc->cmd_state;
+		break;
+
+	default:
+		pr_err("%s:Invalid format[%d]\n", __func__, cmd);
+		goto fail_cmd;
+	}
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) &hdr);
+	if (rc < 0) {
+		pr_err("%s: Command 0x%x failed\n", __func__, hdr.opcode);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(usc->cmd_wait, (atomic_read(state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s:timeout. waited for response opcode[0x%x]\n",
+		       __func__, hdr.opcode);
+	} else
+		rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int q6usm_set_us_detection(struct us_client *usc,
+			   struct usm_session_cmd_detect_info *detect_info,
+			   uint16_t detect_info_size)
+{
+	int rc = 0;
+
+	if ((usc == NULL) ||
+	    (detect_info_size == 0) ||
+	    (detect_info == NULL)) {
+		pr_err("%s: wrong input: usc=0x%pK, inf_size=%d; info=0x%pK",
+		       __func__,
+		       usc,
+		       detect_info_size,
+		       detect_info);
+		return -EINVAL;
+	}
+
+	q6usm_add_hdr(usc, &detect_info->hdr, detect_info_size, true);
+
+	detect_info->hdr.opcode = USM_SESSION_CMD_SIGNAL_DETECT_MODE;
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *)detect_info);
+	if (rc < 0) {
+		pr_err("%s:Comamnd signal detect failed\n", __func__);
+		return -EINVAL;
+	}
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: CMD_SIGNAL_DETECT_MODE: timeout=%d\n",
+		       __func__, Q6USM_TIMEOUT_JIFFIES);
+	} else
+		rc = 0;
+
+	return rc;
+}
+
+int q6usm_set_us_stream_param(int dir, struct us_client *usc,
+		uint32_t module_id, uint32_t param_id, uint32_t buf_size)
+{
+	int rc = 0;
+	struct usm_stream_cmd_set_param cmd_set_param;
+	struct us_port_data *port = NULL;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	port = &usc->port[dir];
+
+	q6usm_add_hdr(usc, &cmd_set_param.hdr, sizeof(cmd_set_param), true);
+
+	cmd_set_param.hdr.opcode = USM_STREAM_CMD_SET_PARAM;
+	cmd_set_param.buf_size = buf_size;
+	cmd_set_param.buf_addr_msw =
+			msm_audio_populate_upper_32_bits(port->param_phys);
+	cmd_set_param.buf_addr_lsw = lower_32_bits(port->param_phys);
+	cmd_set_param.mem_map_handle =
+			*((uint32_t *)(port->param_buf_mem_handle));
+	cmd_set_param.module_id = module_id;
+	cmd_set_param.param_id = param_id;
+	cmd_set_param.hdr.token = 0;
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_set_param);
+
+	if (rc < 0) {
+		pr_err("%s:write op[0x%x];rc[%d]\n",
+			__func__, cmd_set_param.hdr.opcode, rc);
+	}
+
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: CMD_SET_PARAM: timeout=%d\n",
+			__func__, Q6USM_TIMEOUT_JIFFIES);
+	} else
+		rc = 0;
+
+	return rc;
+}
+
+int q6usm_get_us_stream_param(int dir, struct us_client *usc,
+		uint32_t module_id, uint32_t param_id, uint32_t buf_size)
+{
+	int rc = 0;
+	struct usm_stream_cmd_get_param cmd_get_param;
+	struct us_port_data *port = NULL;
+
+	if ((usc == NULL) || (usc->apr == NULL)) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	port = &usc->port[dir];
+
+	q6usm_add_hdr(usc, &cmd_get_param.hdr, sizeof(cmd_get_param), true);
+
+	cmd_get_param.hdr.opcode = USM_STREAM_CMD_GET_PARAM;
+	cmd_get_param.buf_size = buf_size;
+	cmd_get_param.buf_addr_msw =
+			msm_audio_populate_upper_32_bits(port->param_phys);
+	cmd_get_param.buf_addr_lsw = lower_32_bits(port->param_phys);
+	cmd_get_param.mem_map_handle =
+			*((uint32_t *)(port->param_buf_mem_handle));
+	cmd_get_param.module_id = module_id;
+	cmd_get_param.param_id = param_id;
+	cmd_get_param.hdr.token = 0;
+
+	rc = apr_send_pkt(usc->apr, (uint32_t *) &cmd_get_param);
+
+	if (rc < 0) {
+		pr_err("%s:write op[0x%x];rc[%d]\n",
+			__func__, cmd_get_param.hdr.opcode, rc);
+	}
+
+	rc = wait_event_timeout(usc->cmd_wait,
+				(atomic_read(&usc->cmd_state) == 0),
+				Q6USM_TIMEOUT_JIFFIES);
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s: CMD_GET_PARAM: timeout=%d\n",
+			__func__, Q6USM_TIMEOUT_JIFFIES);
+	} else
+		rc = 0;
+
+	return rc;
+}
+
+static int __init q6usm_init(void)
+{
+	pr_debug("%s\n", __func__);
+	init_waitqueue_head(&this_mmap.cmd_wait);
+	memset(session, 0, sizeof(session));
+	return 0;
+}
+
+device_initcall(q6usm_init);
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h
new file mode 100644
index 0000000..d45d165
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/q6usm.h
@@ -0,0 +1,130 @@
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __Q6_USM_H__
+#define __Q6_USM_H__
+
+#include <linux/qdsp6v2/apr_us.h>
+
+#define Q6USM_EVENT_UNDEF                0
+#define Q6USM_EVENT_READ_DONE            1
+#define Q6USM_EVENT_WRITE_DONE           2
+#define Q6USM_EVENT_SIGNAL_DETECT_RESULT 3
+
+/* cyclic buffer with 1 gap support */
+#define USM_MIN_BUF_CNT 3
+
+#define FORMAT_USPS_EPOS	0x00000000
+#define FORMAT_USRAW		0x00000001
+#define FORMAT_USPROX		0x00000002
+#define FORMAT_USGES_SYNC	0x00000003
+#define FORMAT_USRAW_SYNC	0x00000004
+#define INVALID_FORMAT		0xffffffff
+
+#define IN			0x000
+#define OUT			0x001
+
+#define USM_WRONG_TOKEN		0xffffffff
+#define USM_UNDEF_TOKEN		0xfffffffe
+
+#define CMD_CLOSE		0x0004
+
+/* bit 0:1 represents priority of stream */
+#define STREAM_PRIORITY_NORMAL	0x0000
+#define STREAM_PRIORITY_LOW	0x0001
+#define STREAM_PRIORITY_HIGH	0x0002
+
+/* bit 4 represents META enable of encoded data buffer */
+#define BUFFER_META_ENABLE	0x0010
+
+struct us_port_data {
+	dma_addr_t	phys;
+	/* cyclic region of buffers with 1 gap */
+	void		*data;
+	/* number of buffers in the region */
+	uint32_t	buf_cnt;
+	/* size of buffer */
+	size_t		buf_size;
+	/* write index */
+	uint32_t	dsp_buf;
+	/* read index */
+	uint32_t	cpu_buf;
+	/* expected token from dsp */
+	uint32_t	expected_token;
+	/* read or write locks */
+	struct mutex	lock;
+	spinlock_t	dsp_lock;
+	/* ION memory handle */
+	struct      ion_handle *handle;
+	/* ION memory client */
+	struct      ion_client *client;
+	/* extended parameters, related to q6 variants */
+	void		*ext;
+	/* physical address of parameter buffer */
+	dma_addr_t	param_phys;
+	/* buffer which stores the parameter data */
+	void		*param_buf;
+	/* size of parameter buffer */
+	uint32_t	param_buf_size;
+	/* parameter buffer memory handle */
+	void		*param_buf_mem_handle;
+	/* ION memory handle for parameter buffer */
+	struct      ion_handle *param_handle;
+	/* ION memory client for parameter buffer */
+	struct      ion_client *param_client;
+};
+
+struct us_client {
+	int			session;
+	/* idx:1 out port, 0: in port*/
+	struct us_port_data	port[2];
+
+	struct apr_svc		*apr;
+	struct mutex		cmd_lock;
+
+	atomic_t		cmd_state;
+	atomic_t		eos_state;
+	wait_queue_head_t	cmd_wait;
+
+	void (*cb)(uint32_t, uint32_t, uint32_t *, void *);
+	void			*priv;
+};
+
+int q6usm_run(struct us_client *usc, uint32_t flags,
+	      uint32_t msw_ts, uint32_t lsw_ts);
+int q6usm_cmd(struct us_client *usc, int cmd);
+int q6usm_us_client_buf_alloc(unsigned int dir, struct us_client *usc,
+			      unsigned int bufsz, unsigned int bufcnt);
+int q6usm_us_param_buf_alloc(unsigned int dir, struct us_client *usc,
+			      unsigned int bufsz);
+int q6usm_enc_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg);
+int q6usm_dec_cfg_blk(struct us_client *usc, struct us_encdec_cfg *us_cfg);
+int q6usm_read(struct us_client *usc, uint32_t read_ind);
+struct us_client *q6usm_us_client_alloc(
+	void (*cb)(uint32_t, uint32_t, uint32_t *, void *),
+	void *priv);
+int q6usm_open_read(struct us_client *usc, uint32_t format);
+void q6usm_us_client_free(struct us_client *usc);
+uint32_t q6usm_get_virtual_address(int dir, struct us_client *usc,
+				   struct vm_area_struct *vms);
+int q6usm_open_write(struct us_client *usc,  uint32_t format);
+int q6usm_write(struct us_client *usc, uint32_t write_ind);
+bool q6usm_is_write_buf_full(struct us_client *usc, uint32_t *free_region);
+int q6usm_set_us_detection(struct us_client *usc,
+			   struct usm_session_cmd_detect_info *detect_info,
+			   uint16_t detect_info_size);
+int q6usm_set_us_stream_param(int dir, struct us_client *usc,
+		uint32_t module_id, uint32_t param_id, uint32_t buf_size);
+int q6usm_get_us_stream_param(int dir, struct us_client *usc,
+		uint32_t module_id, uint32_t param_id, uint32_t buf_size);
+
+#endif /* __Q6_USM_H__ */
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
new file mode 100644
index 0000000..c964dcb
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usf.c
@@ -0,0 +1,2468 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/compat.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/time.h>
+#include <linux/kmemleak.h>
+#include <linux/wakelock.h>
+#include <linux/mutex.h>
+#include <sound/apr_audio.h>
+#include <linux/qdsp6v2/usf.h>
+#include "q6usm.h"
+#include "usfcdev.h"
+
+/* The driver version*/
+#define DRV_VERSION "1.7.1"
+#define USF_VERSION_ID 0x0171
+
+/* Standard timeout in the asynchronous ops */
+#define USF_TIMEOUT_JIFFIES (1*HZ) /* 1 sec */
+
+/* Undefined USF device */
+#define USF_UNDEF_DEV_ID 0xffff
+
+/* TX memory mapping flag */
+#define USF_VM_READ 1
+/* RX memory mapping flag */
+#define USF_VM_WRITE 2
+
+/* Number of events, copied from the user space to kernel one */
+#define USF_EVENTS_PORTION_SIZE 20
+
+/* Indexes in range definitions */
+#define MIN_IND 0
+#define MAX_IND 1
+
+/* The coordinates indexes */
+#define X_IND 0
+#define Y_IND 1
+#define Z_IND 2
+
+/* Shared memory limits */
+/* max_buf_size = (port_size(65535*2) * port_num(8) * group_size(3) */
+#define USF_MAX_BUF_SIZE 3145680
+#define USF_MAX_BUF_NUM  32
+
+/* max size for buffer set from user space */
+#define USF_MAX_USER_BUF_SIZE 100000
+
+/* Place for opreation result, received from QDSP6 */
+#define APR_RESULT_IND 1
+
+/* Place for US detection result, received from QDSP6 */
+#define APR_US_DETECT_RESULT_IND 0
+
+#define BITS_IN_BYTE 8
+
+/* Time to stay awake after tx read event (e.g., proximity) */
+#define STAY_AWAKE_AFTER_READ_MSECS 3000
+
+/* The driver states */
+enum usf_state_type {
+	USF_IDLE_STATE,
+	USF_OPENED_STATE,
+	USF_CONFIGURED_STATE,
+	USF_WORK_STATE,
+	USF_ADSP_RESTART_STATE,
+	USF_ERROR_STATE
+};
+
+/* The US detection status upon FW/HW based US detection results */
+enum usf_us_detect_type {
+	USF_US_DETECT_UNDEF,
+	USF_US_DETECT_YES,
+	USF_US_DETECT_NO
+};
+
+struct usf_xx_type {
+	/* Name of the client - event calculator */
+	char client_name[USF_MAX_CLIENT_NAME_SIZE];
+	/* The driver state in TX or RX direction */
+	enum usf_state_type usf_state;
+	/* wait for q6 events mechanism */
+	wait_queue_head_t wait;
+	/* IF with q6usm info */
+	struct us_client *usc;
+	/* Q6:USM' Encoder/decoder configuration */
+	struct us_encdec_cfg encdec_cfg;
+	/* Shared buffer (with Q6:USM) size */
+	uint32_t buffer_size;
+	/* Number of the shared buffers (with Q6:USM) */
+	uint32_t buffer_count;
+	/* Shared memory (Cyclic buffer with 1 gap) control */
+	uint32_t new_region;
+	uint32_t prev_region;
+	/* Q6:USM's events handler */
+	void (*cb)(uint32_t, uint32_t, uint32_t *, void *);
+	/* US detection result */
+	enum usf_us_detect_type us_detect_type;
+	/* User's update info isn't acceptable */
+	u8 user_upd_info_na;
+};
+
+struct usf_type {
+	/* TX device component configuration & control */
+	struct usf_xx_type usf_tx;
+	/* RX device component configuration & control */
+	struct usf_xx_type usf_rx;
+	/* Index into the opened device container */
+	/* To prevent mutual usage of the same device */
+	uint16_t dev_ind;
+	/* Event types, supported by device */
+	uint16_t event_types;
+	/*  The input devices are "input" module registered clients */
+	struct input_dev *input_ifs[USF_MAX_EVENT_IND];
+	/* Bitmap of types of events, conflicting to USF's ones */
+	uint16_t conflicting_event_types;
+	/* Bitmap of types of events from devs, conflicting with USF */
+	uint16_t conflicting_event_filters;
+	/* The requested buttons bitmap */
+	uint16_t req_buttons_bitmap;
+	/* Mutex for exclusive operations (all public APIs) */
+	struct mutex mutex;
+};
+
+struct usf_input_dev_type {
+	/* Input event type, supported by the input device */
+	uint16_t event_type;
+	/* Input device name */
+	const char *input_dev_name;
+	/* Input device registration function */
+	int (*prepare_dev)(uint16_t, struct usf_type *,
+			    struct us_input_info_type *,
+			   const char *);
+	/* Input event notification function */
+	void (*notify_event)(struct usf_type *,
+			     uint16_t,
+			     struct usf_event_type *
+			     );
+};
+
+
+/* The MAX number of the supported devices */
+#define MAX_DEVS_NUMBER	1
+
+/*
+ * code for a special button that is used to show/hide a
+ * hovering cursor in the input framework. Must be in
+ * sync with the button code definition in the framework
+ * (EventHub.h)
+ */
+#define BTN_USF_HOVERING_CURSOR         0x230
+
+/* Supported buttons container */
+static const int s_button_map[] = {
+	BTN_STYLUS,
+	BTN_STYLUS2,
+	BTN_TOOL_PEN,
+	BTN_TOOL_RUBBER,
+	BTN_TOOL_FINGER,
+	BTN_USF_HOVERING_CURSOR
+};
+
+/* The opened devices container */
+static int s_opened_devs[MAX_DEVS_NUMBER];
+
+static struct wakeup_source usf_wakeup_source;
+
+#define USF_NAME_PREFIX "usf_"
+#define USF_NAME_PREFIX_SIZE 4
+
+
+static struct input_dev *allocate_dev(uint16_t ind, const char *name)
+{
+	struct input_dev *in_dev = input_allocate_device();
+
+	if (in_dev == NULL) {
+		pr_err("%s: input_allocate_device() failed\n", __func__);
+	} else {
+		/* Common part configuration */
+		in_dev->name = name;
+		in_dev->phys = NULL;
+		in_dev->id.bustype = BUS_HOST;
+		in_dev->id.vendor  = 0x0001;
+		in_dev->id.product = 0x0001;
+		in_dev->id.version = USF_VERSION_ID;
+	}
+	return in_dev;
+}
+
+static int prepare_tsc_input_device(uint16_t ind,
+				struct usf_type *usf_info,
+				struct us_input_info_type *input_info,
+				const char *name)
+{
+	int i = 0;
+
+	int num_buttons = min(ARRAY_SIZE(s_button_map),
+		sizeof(input_info->req_buttons_bitmap) *
+		BITS_IN_BYTE);
+	uint16_t max_buttons_bitmap = ((1 << ARRAY_SIZE(s_button_map)) - 1);
+
+	struct input_dev *in_dev = allocate_dev(ind, name);
+
+	if (in_dev == NULL)
+		return -ENOMEM;
+
+	if (input_info->req_buttons_bitmap > max_buttons_bitmap) {
+		pr_err("%s: Requested buttons[%d] exceeds max buttons available[%d]\n",
+		__func__,
+		input_info->req_buttons_bitmap,
+		max_buttons_bitmap);
+		input_free_device(in_dev);
+		return -EINVAL;
+	}
+
+	usf_info->input_ifs[ind] = in_dev;
+	usf_info->req_buttons_bitmap =
+		input_info->req_buttons_bitmap;
+	in_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+	in_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
+
+	for (i = 0; i < num_buttons; i++)
+		if (input_info->req_buttons_bitmap & (1 << i))
+			in_dev->keybit[BIT_WORD(s_button_map[i])] |=
+			BIT_MASK(s_button_map[i]);
+
+	input_set_abs_params(in_dev, ABS_X,
+			     input_info->tsc_x_dim[MIN_IND],
+			     input_info->tsc_x_dim[MAX_IND],
+			     0, 0);
+	input_set_abs_params(in_dev, ABS_Y,
+			     input_info->tsc_y_dim[MIN_IND],
+			     input_info->tsc_y_dim[MAX_IND],
+			     0, 0);
+	input_set_abs_params(in_dev, ABS_DISTANCE,
+			     input_info->tsc_z_dim[MIN_IND],
+			     input_info->tsc_z_dim[MAX_IND],
+			     0, 0);
+
+	input_set_abs_params(in_dev, ABS_PRESSURE,
+			     input_info->tsc_pressure[MIN_IND],
+			     input_info->tsc_pressure[MAX_IND],
+			     0, 0);
+
+	input_set_abs_params(in_dev, ABS_TILT_X,
+			     input_info->tsc_x_tilt[MIN_IND],
+			     input_info->tsc_x_tilt[MAX_IND],
+			     0, 0);
+	input_set_abs_params(in_dev, ABS_TILT_Y,
+			     input_info->tsc_y_tilt[MIN_IND],
+			     input_info->tsc_y_tilt[MAX_IND],
+			     0, 0);
+
+	return 0;
+}
+
+static int prepare_mouse_input_device(uint16_t ind, struct usf_type *usf_info,
+			struct us_input_info_type *input_info,
+			const char *name)
+{
+	struct input_dev *in_dev = allocate_dev(ind, name);
+
+	if (in_dev == NULL)
+		return -ENOMEM;
+
+	usf_info->input_ifs[ind] = in_dev;
+	in_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
+
+	in_dev->keybit[BIT_WORD(BTN_MOUSE)] = BIT_MASK(BTN_LEFT) |
+						BIT_MASK(BTN_RIGHT) |
+						BIT_MASK(BTN_MIDDLE);
+	in_dev->relbit[0] =  BIT_MASK(REL_X) |
+				BIT_MASK(REL_Y) |
+				BIT_MASK(REL_Z);
+
+	return 0;
+}
+
+static int prepare_keyboard_input_device(
+					uint16_t ind,
+					struct usf_type *usf_info,
+					struct us_input_info_type *input_info,
+					const char *name)
+{
+	struct input_dev *in_dev = allocate_dev(ind, name);
+
+	if (in_dev == NULL)
+		return -ENOMEM;
+
+	usf_info->input_ifs[ind] = in_dev;
+	in_dev->evbit[0] |= BIT_MASK(EV_KEY);
+	/* All keys are permitted */
+	memset(in_dev->keybit, 0xff, sizeof(in_dev->keybit));
+
+	return 0;
+}
+
+static void notify_tsc_event(struct usf_type *usf_info,
+			     uint16_t if_ind,
+			     struct usf_event_type *event)
+
+{
+	int i = 0;
+	int num_buttons = min(ARRAY_SIZE(s_button_map),
+		sizeof(usf_info->req_buttons_bitmap) *
+		BITS_IN_BYTE);
+
+	struct input_dev *input_if = usf_info->input_ifs[if_ind];
+	struct point_event_type *pe = &(event->event_data.point_event);
+
+	input_report_abs(input_if, ABS_X, pe->coordinates[X_IND]);
+	input_report_abs(input_if, ABS_Y, pe->coordinates[Y_IND]);
+	input_report_abs(input_if, ABS_DISTANCE, pe->coordinates[Z_IND]);
+
+	input_report_abs(input_if, ABS_TILT_X, pe->inclinations[X_IND]);
+	input_report_abs(input_if, ABS_TILT_Y, pe->inclinations[Y_IND]);
+
+	input_report_abs(input_if, ABS_PRESSURE, pe->pressure);
+	input_report_key(input_if, BTN_TOUCH, !!(pe->pressure));
+
+	for (i = 0; i < num_buttons; i++) {
+		uint16_t mask = (1 << i),
+		btn_state = !!(pe->buttons_state_bitmap & mask);
+		if (usf_info->req_buttons_bitmap & mask)
+			input_report_key(input_if, s_button_map[i], btn_state);
+	}
+
+	input_sync(input_if);
+
+	pr_debug("%s: TSC event: xyz[%d;%d;%d], incl[%d;%d], pressure[%d], buttons[%d]\n",
+		 __func__,
+		 pe->coordinates[X_IND],
+		 pe->coordinates[Y_IND],
+		 pe->coordinates[Z_IND],
+		 pe->inclinations[X_IND],
+		 pe->inclinations[Y_IND],
+		 pe->pressure,
+		 pe->buttons_state_bitmap);
+}
+
+static void notify_mouse_event(struct usf_type *usf_info,
+			       uint16_t if_ind,
+			       struct usf_event_type *event)
+{
+	struct input_dev *input_if = usf_info->input_ifs[if_ind];
+	struct mouse_event_type *me = &(event->event_data.mouse_event);
+
+	input_report_rel(input_if, REL_X, me->rels[X_IND]);
+	input_report_rel(input_if, REL_Y, me->rels[Y_IND]);
+	input_report_rel(input_if, REL_Z, me->rels[Z_IND]);
+
+	input_report_key(input_if, BTN_LEFT,
+			 me->buttons_states & USF_BUTTON_LEFT_MASK);
+	input_report_key(input_if, BTN_MIDDLE,
+			 me->buttons_states & USF_BUTTON_MIDDLE_MASK);
+	input_report_key(input_if, BTN_RIGHT,
+			 me->buttons_states & USF_BUTTON_RIGHT_MASK);
+
+	input_sync(input_if);
+
+	pr_debug("%s: mouse event: dx[%d], dy[%d], buttons_states[%d]\n",
+		 __func__, me->rels[X_IND],
+		 me->rels[Y_IND], me->buttons_states);
+}
+
+static void notify_key_event(struct usf_type *usf_info,
+			     uint16_t if_ind,
+			     struct usf_event_type *event)
+{
+	struct input_dev *input_if = usf_info->input_ifs[if_ind];
+	struct key_event_type *ke = &(event->event_data.key_event);
+
+	input_report_key(input_if, ke->key, ke->key_state);
+	input_sync(input_if);
+	pr_debug("%s: key event: key[%d], state[%d]\n",
+		 __func__,
+		 ke->key,
+		 ke->key_state);
+
+}
+
+static struct usf_input_dev_type s_usf_input_devs[] = {
+	{USF_TSC_EVENT, "usf_tsc",
+		prepare_tsc_input_device, notify_tsc_event},
+	{USF_TSC_PTR_EVENT, "usf_tsc_ptr",
+		prepare_tsc_input_device, notify_tsc_event},
+	{USF_MOUSE_EVENT, "usf_mouse",
+		prepare_mouse_input_device, notify_mouse_event},
+	{USF_KEYBOARD_EVENT, "usf_kb",
+		prepare_keyboard_input_device, notify_key_event},
+	{USF_TSC_EXT_EVENT, "usf_tsc_ext",
+		prepare_tsc_input_device, notify_tsc_event},
+};
+
+static void usf_rx_cb(uint32_t opcode, uint32_t token,
+		      uint32_t *payload, void *priv)
+{
+	struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv;
+
+	if (usf_xx == NULL) {
+		pr_err("%s: the private data is NULL\n", __func__);
+		return;
+	}
+
+	switch (opcode) {
+	case Q6USM_EVENT_WRITE_DONE:
+		wake_up(&usf_xx->wait);
+		break;
+
+	case RESET_EVENTS:
+		pr_err("%s: received RESET_EVENTS\n", __func__);
+		usf_xx->usf_state = USF_ADSP_RESTART_STATE;
+		wake_up(&usf_xx->wait);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void usf_tx_cb(uint32_t opcode, uint32_t token,
+		      uint32_t *payload, void *priv)
+{
+	struct usf_xx_type *usf_xx = (struct usf_xx_type *) priv;
+
+	if (usf_xx == NULL) {
+		pr_err("%s: the private data is NULL\n", __func__);
+		return;
+	}
+
+	switch (opcode) {
+	case Q6USM_EVENT_READ_DONE:
+		pr_debug("%s: acquiring %d msec wake lock\n", __func__,
+				STAY_AWAKE_AFTER_READ_MSECS);
+		__pm_wakeup_event(&usf_wakeup_source,
+				  STAY_AWAKE_AFTER_READ_MSECS);
+		if (token == USM_WRONG_TOKEN)
+			usf_xx->usf_state = USF_ERROR_STATE;
+		usf_xx->new_region = token;
+		wake_up(&usf_xx->wait);
+		break;
+
+	case Q6USM_EVENT_SIGNAL_DETECT_RESULT:
+		usf_xx->us_detect_type = (payload[APR_US_DETECT_RESULT_IND]) ?
+					USF_US_DETECT_YES :
+					USF_US_DETECT_NO;
+
+		wake_up(&usf_xx->wait);
+		break;
+
+	case APR_BASIC_RSP_RESULT:
+		if (payload[APR_RESULT_IND]) {
+			usf_xx->usf_state = USF_ERROR_STATE;
+			usf_xx->new_region = USM_WRONG_TOKEN;
+			wake_up(&usf_xx->wait);
+		}
+		break;
+
+	case RESET_EVENTS:
+		pr_err("%s: received RESET_EVENTS\n", __func__);
+		usf_xx->usf_state = USF_ADSP_RESTART_STATE;
+		wake_up(&usf_xx->wait);
+		break;
+
+	default:
+		break;
+	}
+}
+
+static void release_xx(struct usf_xx_type *usf_xx)
+{
+	if (usf_xx != NULL) {
+		if (usf_xx->usc) {
+			q6usm_us_client_free(usf_xx->usc);
+			usf_xx->usc = NULL;
+		}
+
+		if (usf_xx->encdec_cfg.params != NULL) {
+			kfree(usf_xx->encdec_cfg.params);
+			usf_xx->encdec_cfg.params = NULL;
+		}
+	}
+}
+
+static void usf_disable(struct usf_xx_type *usf_xx)
+{
+	if (usf_xx != NULL) {
+		if ((usf_xx->usf_state != USF_IDLE_STATE) &&
+		    (usf_xx->usf_state != USF_OPENED_STATE)) {
+			(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+			usf_xx->usf_state = USF_OPENED_STATE;
+			wake_up(&usf_xx->wait);
+		}
+		release_xx(usf_xx);
+	}
+}
+
+static int config_xx(struct usf_xx_type *usf_xx, struct us_xx_info_type *config)
+{
+	int rc = 0;
+	uint16_t data_map_size = 0;
+	uint16_t min_map_size = 0;
+
+	if ((usf_xx == NULL) ||
+	    (config == NULL))
+		return -EINVAL;
+
+	if ((config->buf_size == 0) ||
+	    (config->buf_size > USF_MAX_BUF_SIZE) ||
+	    (config->buf_num == 0) ||
+	    (config->buf_num > USF_MAX_BUF_NUM)) {
+		pr_err("%s: wrong params: buf_size=%d; buf_num=%d\n",
+		       __func__, config->buf_size, config->buf_num);
+		return -EINVAL;
+	}
+
+	data_map_size = sizeof(usf_xx->encdec_cfg.cfg_common.data_map);
+	min_map_size = min(data_map_size, config->port_cnt);
+
+	if (config->client_name != NULL) {
+		if (strncpy_from_user(usf_xx->client_name,
+				      (char __user *)config->client_name,
+				      sizeof(usf_xx->client_name) - 1) < 0) {
+			pr_err("%s: get client name failed\n", __func__);
+			return -EINVAL;
+		}
+	}
+
+	pr_debug("%s: name=%s; buf_size:%d; dev_id:0x%x; sample_rate:%d\n",
+		__func__, usf_xx->client_name, config->buf_size,
+		config->dev_id, config->sample_rate);
+
+	pr_debug("%s: buf_num:%d; format:%d; port_cnt:%d; data_size=%d\n",
+		__func__, config->buf_num, config->stream_format,
+		config->port_cnt, config->params_data_size);
+
+	pr_debug("%s: id[0]=%d, id[1]=%d, id[2]=%d, id[3]=%d, id[4]=%d,\n",
+		__func__,
+		config->port_id[0],
+		config->port_id[1],
+		config->port_id[2],
+		config->port_id[3],
+		config->port_id[4]);
+
+	pr_debug("id[5]=%d, id[6]=%d, id[7]=%d\n",
+		config->port_id[5],
+		config->port_id[6],
+		config->port_id[7]);
+
+	/* q6usm allocation & configuration */
+	usf_xx->buffer_size = config->buf_size;
+	usf_xx->buffer_count = config->buf_num;
+	usf_xx->encdec_cfg.cfg_common.bits_per_sample =
+				config->bits_per_sample;
+	usf_xx->encdec_cfg.cfg_common.sample_rate = config->sample_rate;
+	/* AFE port e.g. AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX */
+	usf_xx->encdec_cfg.cfg_common.dev_id = config->dev_id;
+
+	usf_xx->encdec_cfg.cfg_common.ch_cfg = config->port_cnt;
+	memcpy((void *)&usf_xx->encdec_cfg.cfg_common.data_map,
+	       (void *)config->port_id,
+	       min_map_size);
+
+	usf_xx->encdec_cfg.format_id = config->stream_format;
+	usf_xx->encdec_cfg.params_size = config->params_data_size;
+	usf_xx->user_upd_info_na = 1; /* it's used in US_GET_TX_UPDATE */
+
+	if (config->params_data_size > 0) { /* transparent data copy */
+		usf_xx->encdec_cfg.params = kzalloc(config->params_data_size,
+						    GFP_KERNEL);
+		/* False memory leak here - pointer in packed struct
+		 * is undetected by kmemleak tool
+		 */
+		kmemleak_ignore(usf_xx->encdec_cfg.params);
+		if (usf_xx->encdec_cfg.params == NULL) {
+			pr_err("%s: params memory alloc[%d] failure\n",
+				__func__,
+				config->params_data_size);
+			return -ENOMEM;
+		}
+		rc = copy_from_user(usf_xx->encdec_cfg.params,
+				    (uint8_t __user *)config->params_data,
+				    config->params_data_size);
+		if (rc) {
+			pr_err("%s: transparent data copy failure\n",
+			       __func__);
+			kfree(usf_xx->encdec_cfg.params);
+			usf_xx->encdec_cfg.params = NULL;
+			return -EFAULT;
+		}
+		pr_debug("%s: params_size[%d]; params[%d,%d,%d,%d, %d]\n",
+			 __func__,
+			 config->params_data_size,
+			 usf_xx->encdec_cfg.params[0],
+			 usf_xx->encdec_cfg.params[1],
+			 usf_xx->encdec_cfg.params[2],
+			 usf_xx->encdec_cfg.params[3],
+			 usf_xx->encdec_cfg.params[4]
+			);
+	}
+
+	usf_xx->usc = q6usm_us_client_alloc(usf_xx->cb, (void *)usf_xx);
+	if (!usf_xx->usc) {
+		pr_err("%s: Could not allocate q6usm client\n", __func__);
+		rc = -EFAULT;
+	}
+
+	return rc;
+}
+
+static bool usf_match(uint16_t event_type_ind, struct input_dev *dev)
+{
+	bool rc = false;
+
+	rc = (event_type_ind < MAX_EVENT_TYPE_NUM) &&
+		((dev->name == NULL) ||
+		strcmp(dev->name, USF_NAME_PREFIX));
+	pr_debug("%s: name=[%s]; rc=%d\n",
+		 __func__, dev->name, rc);
+
+	return rc;
+}
+
+static bool usf_register_conflicting_events(uint16_t event_types)
+{
+	bool rc = true;
+	uint16_t ind = 0;
+	uint16_t mask = 1;
+
+	for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) {
+		if (event_types & mask) {
+			rc = usfcdev_register(ind, usf_match);
+			if (!rc)
+				break;
+		}
+		mask = mask << 1;
+	}
+
+	return rc;
+}
+
+static void usf_unregister_conflicting_events(uint16_t event_types)
+{
+	uint16_t ind = 0;
+	uint16_t mask = 1;
+
+	for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) {
+		if (event_types & mask)
+			usfcdev_unregister(ind);
+		mask = mask << 1;
+	}
+}
+
+static void usf_set_event_filters(struct usf_type *usf, uint16_t event_filters)
+{
+	uint16_t ind = 0;
+	uint16_t mask = 1;
+
+	if (usf->conflicting_event_filters != event_filters) {
+		for (ind = 0; ind < MAX_EVENT_TYPE_NUM; ++ind) {
+			if (usf->conflicting_event_types & mask)
+				usfcdev_set_filter(ind, event_filters&mask);
+			mask = mask << 1;
+		}
+		usf->conflicting_event_filters = event_filters;
+	}
+}
+
+static int register_input_device(struct usf_type *usf_info,
+				 struct us_input_info_type *input_info)
+{
+	int rc = 0;
+	bool ret = true;
+	uint16_t ind = 0;
+
+	if ((usf_info == NULL) ||
+	    (input_info == NULL) ||
+	    !(input_info->event_types & USF_ALL_EVENTS)) {
+		pr_err("%s: wrong input parameter(s)\n", __func__);
+		return -EINVAL;
+	}
+
+	for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) {
+		if (usf_info->input_ifs[ind] != NULL) {
+			pr_err("%s: input_if[%d] is already allocated\n",
+				__func__, ind);
+			return -EFAULT;
+		}
+		if ((input_info->event_types &
+			s_usf_input_devs[ind].event_type) &&
+		     s_usf_input_devs[ind].prepare_dev) {
+			rc = (*s_usf_input_devs[ind].prepare_dev)(
+				ind,
+				usf_info,
+				input_info,
+				s_usf_input_devs[ind].input_dev_name);
+			if (rc)
+				return rc;
+
+			rc = input_register_device(usf_info->input_ifs[ind]);
+			if (rc) {
+				pr_err("%s: input_reg_dev() failed; rc=%d\n",
+					__func__, rc);
+				input_free_device(usf_info->input_ifs[ind]);
+				usf_info->input_ifs[ind] = NULL;
+			} else {
+				usf_info->event_types |=
+					s_usf_input_devs[ind].event_type;
+				pr_debug("%s: input device[%s] was registered\n",
+					__func__,
+					s_usf_input_devs[ind].input_dev_name);
+			}
+		} /* supported event */
+	} /* event types loop */
+
+	ret = usf_register_conflicting_events(
+			input_info->conflicting_event_types);
+	if (ret)
+		usf_info->conflicting_event_types =
+			input_info->conflicting_event_types;
+
+	return 0;
+}
+
+
+static void handle_input_event(struct usf_type *usf_info,
+			       uint16_t event_counter,
+			       struct usf_event_type __user *event)
+{
+	uint16_t ind = 0;
+	uint16_t events_num = 0;
+	struct usf_event_type usf_events[USF_EVENTS_PORTION_SIZE];
+	int rc = 0;
+
+	if ((usf_info == NULL) ||
+	    (event == NULL) || (!event_counter)) {
+		return;
+	}
+
+	while (event_counter > 0) {
+		if (event_counter > USF_EVENTS_PORTION_SIZE) {
+			events_num = USF_EVENTS_PORTION_SIZE;
+			event_counter -= USF_EVENTS_PORTION_SIZE;
+		} else {
+			events_num = event_counter;
+			event_counter = 0;
+		}
+		rc = copy_from_user(usf_events,
+				(struct usf_event_type __user *)event,
+				events_num * sizeof(struct usf_event_type));
+		if (rc) {
+			pr_err("%s: copy upd_rx_info from user; rc=%d\n",
+				__func__, rc);
+			return;
+		}
+		for (ind = 0; ind < events_num; ++ind) {
+			struct usf_event_type *p_event = &usf_events[ind];
+			uint16_t if_ind = p_event->event_type_ind;
+
+			if ((if_ind >= USF_MAX_EVENT_IND) ||
+			    (usf_info->input_ifs[if_ind] == NULL))
+				continue; /* event isn't supported */
+
+			if (s_usf_input_devs[if_ind].notify_event)
+				(*s_usf_input_devs[if_ind].notify_event)(
+								usf_info,
+								if_ind,
+								p_event);
+		} /* loop in the portion */
+	} /* all events loop */
+}
+
+static int usf_start_tx(struct usf_xx_type *usf_xx)
+{
+	int rc = q6usm_run(usf_xx->usc, 0, 0, 0);
+
+	pr_debug("%s: tx: q6usm_run; rc=%d\n", __func__, rc);
+	if (!rc) {
+		if (usf_xx->buffer_count >= USM_MIN_BUF_CNT) {
+			/* supply all buffers */
+			rc = q6usm_read(usf_xx->usc,
+					usf_xx->buffer_count);
+			pr_debug("%s: q6usm_read[%d]\n",
+				 __func__, rc);
+
+			if (rc)
+				pr_err("%s: buf read failed",
+				       __func__);
+			else
+				usf_xx->usf_state =
+					USF_WORK_STATE;
+		} else
+			usf_xx->usf_state =
+				USF_WORK_STATE;
+	}
+
+	return rc;
+} /* usf_start_tx */
+
+static int usf_start_rx(struct usf_xx_type *usf_xx)
+{
+	int rc = q6usm_run(usf_xx->usc, 0, 0, 0);
+
+	pr_debug("%s: rx: q6usm_run; rc=%d\n",
+		 __func__, rc);
+	if (!rc)
+		usf_xx->usf_state = USF_WORK_STATE;
+
+	return rc;
+} /* usf_start_rx */
+
+static int __usf_set_us_detection(struct usf_type *usf,
+				  struct us_detect_info_type *detect_info)
+{
+	uint32_t timeout = 0;
+	struct usm_session_cmd_detect_info *p_allocated_memory = NULL;
+	struct usm_session_cmd_detect_info usm_detect_info;
+	struct usm_session_cmd_detect_info *p_usm_detect_info =
+						&usm_detect_info;
+	uint32_t detect_info_size = sizeof(struct usm_session_cmd_detect_info);
+	struct usf_xx_type *usf_xx =  &usf->usf_tx;
+	int rc = 0;
+
+	if (detect_info->us_detector != US_DETECT_FW) {
+		pr_err("%s: unsupported detector: %d\n",
+			__func__, detect_info->us_detector);
+		return -EINVAL;
+	}
+
+	if ((detect_info->params_data_size != 0) &&
+	    (detect_info->params_data != NULL)) {
+		uint8_t *p_data = NULL;
+
+		detect_info_size += detect_info->params_data_size;
+		 p_allocated_memory = kzalloc(detect_info_size, GFP_KERNEL);
+		if (p_allocated_memory == NULL) {
+			pr_err("%s: detect_info[%d] allocation failed\n",
+			       __func__, detect_info_size);
+			return -ENOMEM;
+		}
+		p_usm_detect_info = p_allocated_memory;
+		p_data = (uint8_t *)p_usm_detect_info +
+			sizeof(struct usm_session_cmd_detect_info);
+
+		rc = copy_from_user(p_data,
+			(uint8_t __user *)(detect_info->params_data),
+			detect_info->params_data_size);
+		if (rc) {
+			pr_err("%s: copy params from user; rc=%d\n",
+				__func__, rc);
+			kfree(p_allocated_memory);
+			return -EFAULT;
+		}
+		p_usm_detect_info->algorithm_cfg_size =
+				detect_info->params_data_size;
+	} else
+		usm_detect_info.algorithm_cfg_size = 0;
+
+	p_usm_detect_info->detect_mode = detect_info->us_detect_mode;
+	p_usm_detect_info->skip_interval = detect_info->skip_time;
+
+	usf_xx->us_detect_type = USF_US_DETECT_UNDEF;
+
+	rc = q6usm_set_us_detection(usf_xx->usc,
+				    p_usm_detect_info,
+				    detect_info_size);
+	if (rc || (detect_info->detect_timeout == USF_NO_WAIT_TIMEOUT)) {
+		kfree(p_allocated_memory);
+		return rc;
+	}
+
+	/* Get US detection result */
+	if (detect_info->detect_timeout == USF_INFINITIVE_TIMEOUT) {
+		rc = wait_event_interruptible(usf_xx->wait,
+						(usf_xx->us_detect_type !=
+						USF_US_DETECT_UNDEF) ||
+						(usf_xx->usf_state ==
+						USF_ADSP_RESTART_STATE));
+	} else {
+		if (detect_info->detect_timeout == USF_DEFAULT_TIMEOUT)
+			timeout = USF_TIMEOUT_JIFFIES;
+		else
+			timeout = detect_info->detect_timeout * HZ;
+	}
+	rc = wait_event_interruptible_timeout(usf_xx->wait,
+					(usf_xx->us_detect_type !=
+					USF_US_DETECT_UNDEF) ||
+					(usf_xx->usf_state ==
+					USF_ADSP_RESTART_STATE), timeout);
+
+	/* In the case of aDSP restart, "no US" is assumed */
+	if (usf_xx->usf_state == USF_ADSP_RESTART_STATE)
+		rc = -EFAULT;
+
+	/* In the case of timeout, "no US" is assumed */
+	if (rc < 0)
+		pr_err("%s: Getting US detection failed rc[%d]\n",
+		       __func__, rc);
+	else {
+		usf->usf_rx.us_detect_type = usf->usf_tx.us_detect_type;
+		detect_info->is_us =
+			(usf_xx->us_detect_type == USF_US_DETECT_YES);
+	}
+
+	kfree(p_allocated_memory);
+
+	return rc;
+} /* __usf_set_us_detection */
+
+static int usf_set_us_detection(struct usf_type *usf, unsigned long arg)
+{
+	struct us_detect_info_type detect_info;
+
+	int rc = copy_from_user(&detect_info,
+				(struct us_detect_info_type __user *) arg,
+				sizeof(detect_info));
+
+	if (rc) {
+		pr_err("%s: copy detect_info from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	if (detect_info.params_data_size > USF_MAX_USER_BUF_SIZE) {
+		pr_err("%s: user buffer size exceeds maximum\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	rc = __usf_set_us_detection(usf, &detect_info);
+	if (rc < 0) {
+		pr_err("%s: set us detection failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = copy_to_user((void __user *)arg,
+			  &detect_info,
+			  sizeof(detect_info));
+	if (rc) {
+		pr_err("%s: copy detect_info to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_set_us_detection */
+
+static int __usf_set_tx_info(struct usf_type *usf,
+			     struct us_tx_info_type *config_tx)
+{
+	struct usf_xx_type *usf_xx =  &usf->usf_tx;
+	int rc = 0;
+
+	usf_xx->new_region = USM_UNDEF_TOKEN;
+	usf_xx->prev_region = USM_UNDEF_TOKEN;
+	usf_xx->cb = usf_tx_cb;
+
+	init_waitqueue_head(&usf_xx->wait);
+
+	if (config_tx->us_xx_info.client_name != NULL) {
+		int res = strncpy_from_user(
+			usf_xx->client_name,
+			(char __user *)(config_tx->us_xx_info.client_name),
+			sizeof(usf_xx->client_name)-1);
+		if (res < 0) {
+			pr_err("%s: get client name failed\n",
+			       __func__);
+			return -EINVAL;
+		}
+	}
+
+	rc = config_xx(usf_xx, &(config_tx->us_xx_info));
+	if (rc)
+		return rc;
+
+	rc = q6usm_open_read(usf_xx->usc,
+			     usf_xx->encdec_cfg.format_id);
+	if (rc)
+		return rc;
+
+	rc = q6usm_us_client_buf_alloc(OUT, usf_xx->usc,
+				       usf_xx->buffer_size,
+				       usf_xx->buffer_count);
+	if (rc) {
+		(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+		return rc;
+	}
+
+	rc = q6usm_us_param_buf_alloc(OUT, usf_xx->usc,
+			config_tx->us_xx_info.max_get_set_param_buf_size);
+	if (rc) {
+		(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+		return rc;
+	}
+
+	rc = q6usm_enc_cfg_blk(usf_xx->usc,
+			       &usf_xx->encdec_cfg);
+	if (!rc &&
+	     (config_tx->input_info.event_types != USF_NO_EVENT)) {
+		rc = register_input_device(usf,
+					   &(config_tx->input_info));
+	}
+
+	if (rc)
+		(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+	else
+		usf_xx->usf_state = USF_CONFIGURED_STATE;
+
+	return rc;
+} /* __usf_set_tx_info */
+
+static int usf_set_tx_info(struct usf_type *usf, unsigned long arg)
+{
+	struct us_tx_info_type config_tx;
+
+	int rc = copy_from_user(&config_tx,
+			    (struct us_tx_info_type __user *) arg,
+			    sizeof(config_tx));
+
+	if (rc) {
+		pr_err("%s: copy config_tx from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	if (config_tx.us_xx_info.params_data_size > USF_MAX_USER_BUF_SIZE) {
+		pr_err("%s: user buffer size exceeds maximum\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	return __usf_set_tx_info(usf, &config_tx);
+} /* usf_set_tx_info */
+
+static int __usf_set_rx_info(struct usf_type *usf,
+			     struct us_rx_info_type *config_rx)
+{
+	struct usf_xx_type *usf_xx =  &usf->usf_rx;
+	int rc = 0;
+
+	usf_xx->new_region = USM_UNDEF_TOKEN;
+	usf_xx->prev_region = USM_UNDEF_TOKEN;
+
+	usf_xx->cb = usf_rx_cb;
+
+	rc = config_xx(usf_xx, &(config_rx->us_xx_info));
+	if (rc)
+		return rc;
+
+	rc = q6usm_open_write(usf_xx->usc,
+			      usf_xx->encdec_cfg.format_id);
+	if (rc)
+		return rc;
+
+	rc = q6usm_us_client_buf_alloc(
+				IN,
+				usf_xx->usc,
+				usf_xx->buffer_size,
+				usf_xx->buffer_count);
+	if (rc) {
+		(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+		return rc;
+	}
+
+	rc = q6usm_us_param_buf_alloc(IN, usf_xx->usc,
+			config_rx->us_xx_info.max_get_set_param_buf_size);
+	if (rc) {
+		(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+		return rc;
+	}
+
+	rc = q6usm_dec_cfg_blk(usf_xx->usc,
+			       &usf_xx->encdec_cfg);
+	if (rc)
+		(void)q6usm_cmd(usf_xx->usc, CMD_CLOSE);
+	else {
+		init_waitqueue_head(&usf_xx->wait);
+		usf_xx->usf_state = USF_CONFIGURED_STATE;
+	}
+
+	return rc;
+} /* __usf_set_rx_info */
+
+static int usf_set_rx_info(struct usf_type *usf, unsigned long arg)
+{
+	struct us_rx_info_type config_rx;
+
+	int rc = copy_from_user(&config_rx,
+				(struct us_rx_info_type __user *) arg,
+				sizeof(config_rx));
+
+	if (rc) {
+		pr_err("%s: copy config_rx from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	if (config_rx.us_xx_info.params_data_size > USF_MAX_USER_BUF_SIZE) {
+		pr_err("%s: user buffer size exceeds maximum\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	return __usf_set_rx_info(usf, &config_rx);
+} /* usf_set_rx_info */
+
+static int __usf_get_tx_update(struct usf_type *usf,
+			struct us_tx_update_info_type *upd_tx_info)
+{
+	unsigned long prev_jiffies = 0;
+	uint32_t timeout = 0;
+	struct usf_xx_type *usf_xx =  &usf->usf_tx;
+	int rc = 0;
+
+	if (!usf_xx->user_upd_info_na) {
+		usf_set_event_filters(usf, upd_tx_info->event_filters);
+		handle_input_event(usf,
+				   upd_tx_info->event_counter,
+				   upd_tx_info->event);
+
+		/* Release available regions */
+		rc = q6usm_read(usf_xx->usc,
+				upd_tx_info->free_region);
+		if (rc)
+			return rc;
+	} else
+		usf_xx->user_upd_info_na = 0;
+
+	/* Get data ready regions */
+	if (upd_tx_info->timeout == USF_INFINITIVE_TIMEOUT) {
+		rc = wait_event_interruptible(usf_xx->wait,
+			   (usf_xx->prev_region !=
+			    usf_xx->new_region) ||
+			   (usf_xx->usf_state !=
+			    USF_WORK_STATE));
+	} else {
+		if (upd_tx_info->timeout == USF_NO_WAIT_TIMEOUT)
+			rc = (usf_xx->prev_region != usf_xx->new_region);
+		else {
+			prev_jiffies = jiffies;
+			if (upd_tx_info->timeout == USF_DEFAULT_TIMEOUT) {
+				timeout = USF_TIMEOUT_JIFFIES;
+				rc = wait_event_timeout(
+						usf_xx->wait,
+						(usf_xx->prev_region !=
+						 usf_xx->new_region) ||
+						(usf_xx->usf_state !=
+						 USF_WORK_STATE),
+						timeout);
+			} else {
+				timeout = upd_tx_info->timeout * HZ;
+				rc = wait_event_interruptible_timeout(
+						usf_xx->wait,
+						(usf_xx->prev_region !=
+						 usf_xx->new_region) ||
+						(usf_xx->usf_state !=
+						 USF_WORK_STATE),
+						timeout);
+			}
+		}
+		if (!rc) {
+			pr_debug("%s: timeout. prev_j=%lu; j=%lu\n",
+				__func__, prev_jiffies, jiffies);
+			pr_debug("%s: timeout. prev=%d; new=%d\n",
+				__func__, usf_xx->prev_region,
+				usf_xx->new_region);
+			pr_debug("%s: timeout. free_region=%d;\n",
+				__func__, upd_tx_info->free_region);
+			if (usf_xx->prev_region ==
+			    usf_xx->new_region) {
+				pr_err("%s:read data: timeout\n",
+				       __func__);
+				return -ETIME;
+			}
+		}
+	}
+
+	if ((usf_xx->usf_state != USF_WORK_STATE) ||
+	    (rc == -ERESTARTSYS)) {
+		pr_err("%s: Get ready region failure; state[%d]; rc[%d]\n",
+		       __func__, usf_xx->usf_state, rc);
+		return -EINTR;
+	}
+
+	upd_tx_info->ready_region = usf_xx->new_region;
+	usf_xx->prev_region = upd_tx_info->ready_region;
+
+	if (upd_tx_info->ready_region == USM_WRONG_TOKEN) {
+		pr_err("%s: TX path corrupted; prev=%d\n",
+		       __func__, usf_xx->prev_region);
+		return -EIO;
+	}
+
+	return rc;
+} /* __usf_get_tx_update */
+
+static int usf_get_tx_update(struct usf_type *usf, unsigned long arg)
+{
+	struct us_tx_update_info_type upd_tx_info;
+
+	int rc = copy_from_user(&upd_tx_info,
+				(struct us_tx_update_info_type __user *) arg,
+				sizeof(upd_tx_info));
+
+	if (rc < 0) {
+		pr_err("%s: copy upd_tx_info from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	rc = __usf_get_tx_update(usf, &upd_tx_info);
+	if (rc < 0) {
+		pr_err("%s: get tx update failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = copy_to_user((void __user *)arg,
+			  &upd_tx_info,
+			  sizeof(upd_tx_info));
+	if (rc) {
+		pr_err("%s: copy upd_tx_info to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_get_tx_update */
+
+static int __usf_set_rx_update(struct usf_xx_type *usf_xx,
+			       struct us_rx_update_info_type *upd_rx_info)
+{
+	int rc = 0;
+
+	/* Send available data regions */
+	if (upd_rx_info->ready_region !=
+	    usf_xx->buffer_count) {
+		rc = q6usm_write(
+			usf_xx->usc,
+			upd_rx_info->ready_region);
+		if (rc)
+			return rc;
+	}
+
+	/* Get free regions */
+	rc = wait_event_timeout(
+		usf_xx->wait,
+		!q6usm_is_write_buf_full(
+			usf_xx->usc,
+			&(upd_rx_info->free_region)) ||
+		(usf_xx->usf_state == USF_IDLE_STATE),
+		USF_TIMEOUT_JIFFIES);
+
+	if (!rc) {
+		rc = -ETIME;
+		pr_err("%s:timeout. wait for write buf not full\n",
+		       __func__);
+	} else {
+		if (usf_xx->usf_state !=
+		    USF_WORK_STATE) {
+			pr_err("%s: RX: state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			rc = -EINTR;
+		}
+	}
+
+	return rc;
+} /* __usf_set_rx_update */
+
+static int usf_set_rx_update(struct usf_xx_type *usf_xx, unsigned long arg)
+{
+	struct us_rx_update_info_type upd_rx_info;
+
+	int rc = copy_from_user(&upd_rx_info,
+				(struct us_rx_update_info_type __user *) arg,
+				sizeof(upd_rx_info));
+
+	if (rc) {
+		pr_err("%s: copy upd_rx_info from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	rc = __usf_set_rx_update(usf_xx, &upd_rx_info);
+	if (rc < 0) {
+		pr_err("%s: set rx update failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = copy_to_user((void __user *)arg,
+			&upd_rx_info,
+			sizeof(upd_rx_info));
+	if (rc) {
+		pr_err("%s: copy rx_info to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_set_rx_update */
+
+static void usf_release_input(struct usf_type *usf)
+{
+	uint16_t ind = 0;
+
+	usf_unregister_conflicting_events(
+					usf->conflicting_event_types);
+	usf->conflicting_event_types = 0;
+	for (ind = 0; ind < USF_MAX_EVENT_IND; ++ind) {
+		if (usf->input_ifs[ind] == NULL)
+			continue;
+		input_unregister_device(usf->input_ifs[ind]);
+		usf->input_ifs[ind] = NULL;
+		pr_debug("%s input_unregister_device[%s]\n",
+			 __func__,
+			 s_usf_input_devs[ind].input_dev_name);
+	}
+} /* usf_release_input */
+
+static int usf_stop_tx(struct usf_type *usf)
+{
+	struct usf_xx_type *usf_xx =  &usf->usf_tx;
+
+	usf_release_input(usf);
+	usf_disable(usf_xx);
+
+	return 0;
+} /* usf_stop_tx */
+
+static int __usf_get_version(struct us_version_info_type *version_info)
+{
+	int rc = 0;
+
+	if (version_info->buf_size < sizeof(DRV_VERSION)) {
+		pr_err("%s: buf_size (%d) < version string size (%zu)\n",
+			__func__, version_info->buf_size, sizeof(DRV_VERSION));
+		return -EINVAL;
+	}
+
+	rc = copy_to_user((void __user *)(version_info->pbuf),
+			  DRV_VERSION,
+			  sizeof(DRV_VERSION));
+	if (rc) {
+		pr_err("%s: copy to version_info.pbuf; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* __usf_get_version */
+
+static int usf_get_version(unsigned long arg)
+{
+	struct us_version_info_type version_info;
+
+	int rc = copy_from_user(&version_info,
+				(struct us_version_info_type __user *) arg,
+				sizeof(version_info));
+
+	if (rc) {
+		pr_err("%s: copy version_info from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	rc = __usf_get_version(&version_info);
+	if (rc < 0) {
+		pr_err("%s: get version failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = copy_to_user((void __user *)arg,
+			  &version_info,
+			  sizeof(version_info));
+	if (rc) {
+		pr_err("%s: copy version_info to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_get_version */
+
+static int __usf_set_stream_param(struct usf_xx_type *usf_xx,
+				struct us_stream_param_type *set_stream_param,
+				int dir)
+{
+	struct us_client *usc = usf_xx->usc;
+	struct us_port_data *port;
+	int rc = 0;
+
+	if (usc == NULL) {
+		pr_err("%s: usc is null\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	port = &usc->port[dir];
+	if (port == NULL) {
+		pr_err("%s: port is null\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	if (port->param_buf == NULL) {
+		pr_err("%s: parameter buffer is null\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	if (set_stream_param->buf_size > port->param_buf_size) {
+		pr_err("%s: buf_size (%d) > maximum buf size (%d)\n",
+			__func__, set_stream_param->buf_size,
+			port->param_buf_size);
+		return -EINVAL;
+	}
+
+	if (set_stream_param->buf_size == 0) {
+		pr_err("%s: buf_size is 0\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = copy_from_user(port->param_buf,
+			(uint8_t __user *) set_stream_param->pbuf,
+			set_stream_param->buf_size);
+	if (rc) {
+		pr_err("%s: copy param buf from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	rc = q6usm_set_us_stream_param(dir, usc, set_stream_param->module_id,
+					set_stream_param->param_id,
+					set_stream_param->buf_size);
+	if (rc) {
+		pr_err("%s: q6usm_set_us_stream_param failed; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	return rc;
+}
+
+static int usf_set_stream_param(struct usf_xx_type *usf_xx,
+				unsigned long arg, int dir)
+{
+	struct us_stream_param_type set_stream_param;
+	int rc = 0;
+
+	rc = copy_from_user(&set_stream_param,
+			(struct us_stream_param_type __user *) arg,
+			sizeof(set_stream_param));
+
+	if (rc) {
+		pr_err("%s: copy set_stream_param from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	return __usf_set_stream_param(usf_xx, &set_stream_param, dir);
+} /* usf_set_stream_param */
+
+static int __usf_get_stream_param(struct usf_xx_type *usf_xx,
+				struct us_stream_param_type *get_stream_param,
+				int dir)
+{
+	struct us_client *usc = usf_xx->usc;
+	struct us_port_data *port;
+	int rc = 0;
+
+	if (usc == NULL) {
+		pr_err("%s: us_client is null\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	port = &usc->port[dir];
+
+	if (port->param_buf == NULL) {
+		pr_err("%s: parameter buffer is null\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	if (get_stream_param->buf_size > port->param_buf_size) {
+		pr_err("%s: buf_size (%d) > maximum buf size (%d)\n",
+			__func__, get_stream_param->buf_size,
+			port->param_buf_size);
+		return -EINVAL;
+	}
+
+	if (get_stream_param->buf_size == 0) {
+		pr_err("%s: buf_size is 0\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = q6usm_get_us_stream_param(dir, usc, get_stream_param->module_id,
+					get_stream_param->param_id,
+					get_stream_param->buf_size);
+	if (rc) {
+		pr_err("%s: q6usm_get_us_stream_param failed; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	rc = copy_to_user((uint8_t __user *) get_stream_param->pbuf,
+			port->param_buf,
+			get_stream_param->buf_size);
+	if (rc) {
+		pr_err("%s: copy param buf to user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	return rc;
+}
+
+static int usf_get_stream_param(struct usf_xx_type *usf_xx,
+				unsigned long arg, int dir)
+{
+	struct us_stream_param_type get_stream_param;
+	int rc = 0;
+
+	rc = copy_from_user(&get_stream_param,
+			(struct us_stream_param_type __user *) arg,
+			sizeof(get_stream_param));
+
+	if (rc) {
+		pr_err("%s: copy get_stream_param from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	return __usf_get_stream_param(usf_xx, &get_stream_param, dir);
+} /* usf_get_stream_param */
+
+static long __usf_ioctl(struct usf_type *usf,
+		unsigned int cmd,
+		unsigned long arg)
+{
+
+	int rc = 0;
+	struct usf_xx_type *usf_xx = NULL;
+
+	switch (cmd) {
+	case US_START_TX: {
+		usf_xx = &usf->usf_tx;
+		if (usf_xx->usf_state == USF_CONFIGURED_STATE)
+			rc = usf_start_tx(usf_xx);
+		else {
+			pr_err("%s: start_tx: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			return -EBADFD;
+		}
+		break;
+	}
+
+	case US_START_RX: {
+		usf_xx = &usf->usf_rx;
+		if (usf_xx->usf_state == USF_CONFIGURED_STATE)
+			rc = usf_start_rx(usf_xx);
+		else {
+			pr_err("%s: start_rx: wrong state[%d]\n",
+				__func__,
+				usf_xx->usf_state);
+			return -EBADFD;
+		}
+		break;
+	}
+
+	case US_SET_TX_INFO: {
+		usf_xx = &usf->usf_tx;
+		if (usf_xx->usf_state == USF_OPENED_STATE)
+			rc = usf_set_tx_info(usf, arg);
+		else {
+			pr_err("%s: set_tx_info: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			return -EBADFD;
+		}
+
+		break;
+	} /* US_SET_TX_INFO */
+
+	case US_SET_RX_INFO: {
+		usf_xx = &usf->usf_rx;
+		if (usf_xx->usf_state == USF_OPENED_STATE)
+			rc = usf_set_rx_info(usf, arg);
+		else {
+			pr_err("%s: set_rx_info: wrong state[%d]\n",
+				__func__,
+				usf_xx->usf_state);
+			return -EBADFD;
+		}
+
+		break;
+	} /* US_SET_RX_INFO */
+
+	case US_GET_TX_UPDATE: {
+		struct usf_xx_type *usf_xx = &usf->usf_tx;
+
+		if (usf_xx->usf_state == USF_WORK_STATE)
+			rc = usf_get_tx_update(usf, arg);
+		else {
+			pr_err("%s: get_tx_update: wrong state[%d]\n", __func__,
+			       usf_xx->usf_state);
+			rc = -EBADFD;
+		}
+		break;
+	} /* US_GET_TX_UPDATE */
+
+	case US_SET_RX_UPDATE: {
+		struct usf_xx_type *usf_xx = &usf->usf_rx;
+
+		if (usf_xx->usf_state == USF_WORK_STATE)
+			rc = usf_set_rx_update(usf_xx, arg);
+		else {
+			pr_err("%s: set_rx_update: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			rc = -EBADFD;
+		}
+		break;
+	} /* US_SET_RX_UPDATE */
+
+	case US_STOP_TX: {
+		usf_xx = &usf->usf_tx;
+		if ((usf_xx->usf_state == USF_WORK_STATE)
+			|| (usf_xx->usf_state == USF_ADSP_RESTART_STATE))
+			rc = usf_stop_tx(usf);
+		else {
+			pr_err("%s: stop_tx: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			return -EBADFD;
+		}
+		break;
+	} /* US_STOP_TX */
+
+	case US_STOP_RX: {
+		usf_xx = &usf->usf_rx;
+		if ((usf_xx->usf_state == USF_WORK_STATE)
+			|| (usf_xx->usf_state == USF_ADSP_RESTART_STATE))
+			usf_disable(usf_xx);
+		else {
+			pr_err("%s: stop_rx: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			return -EBADFD;
+		}
+		break;
+	} /* US_STOP_RX */
+
+	case US_SET_DETECTION: {
+		struct usf_xx_type *usf_xx = &usf->usf_tx;
+
+		if (usf_xx->usf_state == USF_WORK_STATE)
+			rc = usf_set_us_detection(usf, arg);
+		else {
+			pr_err("%s: set us detection: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			rc = -EBADFD;
+		}
+		break;
+	} /* US_SET_DETECTION */
+
+	case US_GET_VERSION: {
+		rc = usf_get_version(arg);
+		break;
+	} /* US_GET_VERSION */
+
+	case US_SET_TX_STREAM_PARAM: {
+		rc = usf_set_stream_param(&usf->usf_tx, arg, OUT);
+		break;
+	} /* US_SET_TX_STREAM_PARAM */
+
+	case US_GET_TX_STREAM_PARAM: {
+		rc = usf_get_stream_param(&usf->usf_tx, arg, OUT);
+		break;
+	} /* US_GET_TX_STREAM_PARAM */
+
+	case US_SET_RX_STREAM_PARAM: {
+		rc = usf_set_stream_param(&usf->usf_rx, arg, IN);
+		break;
+	} /* US_SET_RX_STREAM_PARAM */
+
+	case US_GET_RX_STREAM_PARAM: {
+		rc = usf_get_stream_param(&usf->usf_rx, arg, IN);
+		break;
+	} /* US_GET_RX_STREAM_PARAM */
+
+	default:
+		pr_err("%s: unsupported IOCTL command [%d]\n",
+		       __func__,
+		       cmd);
+		rc = -ENOTTY;
+		break;
+	}
+
+	if (rc &&
+	    ((cmd == US_SET_TX_INFO) ||
+	     (cmd == US_SET_RX_INFO)))
+		release_xx(usf_xx);
+
+	return rc;
+} /* __usf_ioctl */
+
+static long usf_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct usf_type *usf = file->private_data;
+	int rc = 0;
+
+	mutex_lock(&usf->mutex);
+	rc = __usf_ioctl(usf, cmd, arg);
+	mutex_unlock(&usf->mutex);
+
+	return rc;
+} /* usf_ioctl */
+
+#ifdef CONFIG_COMPAT
+
+#define US_SET_TX_INFO32   _IOW(USF_IOCTL_MAGIC, 0, \
+				struct us_tx_info_type32)
+#define US_GET_TX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 2, \
+				struct us_tx_update_info_type32)
+#define US_SET_RX_INFO32   _IOW(USF_IOCTL_MAGIC, 3, \
+				struct us_rx_info_type32)
+#define US_SET_RX_UPDATE32 _IOWR(USF_IOCTL_MAGIC, 4, \
+				struct us_rx_update_info_type32)
+#define US_SET_DETECTION32 _IOWR(USF_IOCTL_MAGIC, 8, \
+				struct us_detect_info_type32)
+#define US_GET_VERSION32  _IOWR(USF_IOCTL_MAGIC, 9, \
+				struct us_version_info_type32)
+#define US_SET_TX_STREAM_PARAM32   _IOW(USF_IOCTL_MAGIC, 10, \
+				struct us_stream_param_type32)
+#define US_GET_TX_STREAM_PARAM32  _IOWR(USF_IOCTL_MAGIC, 11, \
+				struct us_stream_param_type32)
+#define US_SET_RX_STREAM_PARAM32   _IOW(USF_IOCTL_MAGIC, 12, \
+				struct us_stream_param_type32)
+#define US_GET_RX_STREAM_PARAM32  _IOWR(USF_IOCTL_MAGIC, 13, \
+				struct us_stream_param_type32)
+
+/* Info structure common for TX and RX */
+struct us_xx_info_type32 {
+/* Input:  general info */
+/* Name of the client - event calculator, ptr to char */
+	const compat_uptr_t client_name;
+/* Selected device identification, accepted in the kernel's CAD */
+	uint32_t dev_id;
+/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */
+	uint32_t stream_format;
+/* Required sample rate in Hz */
+	uint32_t sample_rate;
+/* Size of a buffer (bytes) for US data transfer between the module and USF */
+	uint32_t buf_size;
+/* Number of the buffers for the US data transfer */
+	uint16_t buf_num;
+/* Number of the microphones (TX) or speakers(RX) */
+	uint16_t port_cnt;
+/* Microphones(TX) or speakers(RX) indexes in their enumeration */
+	uint8_t  port_id[USF_MAX_PORT_NUM];
+/* Bits per sample 16 or 32 */
+	uint16_t bits_per_sample;
+/* Input:  Transparent info for encoder in the LPASS */
+/* Parameters data size in bytes */
+	uint16_t params_data_size;
+/* Pointer to the parameters, ptr to uint8_t */
+	compat_uptr_t params_data;
+/* Max size of buffer for get and set parameter */
+	uint32_t max_get_set_param_buf_size;
+};
+
+struct us_tx_info_type32 {
+/* Common info. This struct includes ptr and therefore the 32 version */
+	struct us_xx_info_type32 us_xx_info;
+/* Info specific for TX. This struct doesn't include long or ptr
+ * and therefore no 32 version
+ */
+	struct us_input_info_type input_info;
+};
+
+struct us_tx_update_info_type32 {
+/* Input  general: */
+/* Number of calculated events */
+	uint16_t event_counter;
+/* Calculated events or NULL, ptr to struct usf_event_type */
+	compat_uptr_t event;
+/* Pointer (read index) to the end of available region */
+/* in the shared US data memory */
+	uint32_t free_region;
+/* Time (sec) to wait for data or special values: */
+/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */
+	uint32_t timeout;
+/* Events (from conflicting devs) to be disabled/enabled */
+	uint16_t event_filters;
+
+/* Input  transparent data: */
+/* Parameters size */
+	uint16_t params_data_size;
+/* Pointer to the parameters, ptr to uint8_t */
+	compat_uptr_t params_data;
+/* Output parameters: */
+/* Pointer (write index) to the end of ready US data region */
+/* in the shared memory */
+	uint32_t ready_region;
+};
+
+struct us_rx_info_type32 {
+	/* Common info */
+	struct us_xx_info_type32 us_xx_info;
+	/* Info specific for RX*/
+};
+
+struct us_rx_update_info_type32 {
+/* Input  general: */
+/* Pointer (write index) to the end of ready US data region */
+/* in the shared memory */
+	uint32_t ready_region;
+/* Input  transparent data: */
+/* Parameters size */
+	uint16_t params_data_size;
+/* pPointer to the parameters, ptr to uint8_t */
+	compat_uptr_t params_data;
+/* Output parameters: */
+/* Pointer (read index) to the end of available region */
+/* in the shared US data memory */
+	uint32_t free_region;
+};
+
+struct us_detect_info_type32 {
+/* US detection place (HW|FW) */
+/* NA in the Active and OFF states */
+	enum us_detect_place_enum us_detector;
+/* US detection mode */
+	enum us_detect_mode_enum  us_detect_mode;
+/* US data dropped during this time (msec) */
+	uint32_t skip_time;
+/* Transparent data size */
+	uint16_t params_data_size;
+/* Pointer to the transparent data, ptr to uint8_t */
+	compat_uptr_t params_data;
+/* Time (sec) to wait for US presence event */
+	uint32_t detect_timeout;
+/* Out parameter: US presence */
+	bool is_us;
+};
+
+struct us_version_info_type32 {
+/* Size of memory for the version string */
+	uint16_t buf_size;
+/* Pointer to the memory for the version string, ptr to char */
+	compat_uptr_t pbuf;
+};
+
+struct us_stream_param_type32 {
+/* Id of module */
+	uint32_t module_id;
+/* Id of parameter */
+	uint32_t param_id;
+/* Size of memory of the parameter buffer */
+	uint32_t buf_size;
+/* Pointer to the memory of the parameter buffer */
+	compat_uptr_t pbuf;
+};
+
+static void usf_compat_xx_info_type(struct us_xx_info_type32 *us_xx_info32,
+				   struct us_xx_info_type *us_xx_info)
+{
+	int i = 0;
+
+	us_xx_info->client_name = compat_ptr(us_xx_info32->client_name);
+	us_xx_info->dev_id = us_xx_info32->dev_id;
+	us_xx_info->stream_format = us_xx_info32->stream_format;
+	us_xx_info->sample_rate = us_xx_info32->sample_rate;
+	us_xx_info->buf_size = us_xx_info32->buf_size;
+	us_xx_info->buf_num = us_xx_info32->buf_num;
+	us_xx_info->port_cnt = us_xx_info32->port_cnt;
+	for (i = 0; i < USF_MAX_PORT_NUM; i++)
+		us_xx_info->port_id[i] = us_xx_info32->port_id[i];
+	us_xx_info->bits_per_sample = us_xx_info32->bits_per_sample;
+	us_xx_info->params_data_size = us_xx_info32->params_data_size;
+	us_xx_info->params_data = compat_ptr(us_xx_info32->params_data);
+	us_xx_info->max_get_set_param_buf_size =
+			    us_xx_info32->max_get_set_param_buf_size;
+}
+
+static int usf_set_tx_info32(struct usf_type *usf, unsigned long arg)
+{
+	struct us_tx_info_type32 config_tx32;
+	struct us_tx_info_type config_tx;
+
+	int rc = copy_from_user(&config_tx32,
+			    (struct us_tx_info_type32 __user *) arg,
+			    sizeof(config_tx32));
+
+	if (rc) {
+		pr_err("%s: copy config_tx from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+	memset(&config_tx, 0, sizeof(config_tx));
+	usf_compat_xx_info_type(&(config_tx32.us_xx_info),
+				&(config_tx.us_xx_info));
+	config_tx.input_info = config_tx32.input_info;
+
+	return __usf_set_tx_info(usf, &config_tx);
+} /* usf_set_tx_info 32*/
+
+static int usf_set_rx_info32(struct usf_type *usf, unsigned long arg)
+{
+	struct us_rx_info_type32 config_rx32;
+	struct us_rx_info_type config_rx;
+
+	int rc = copy_from_user(&config_rx32,
+				(struct us_rx_info_type32 __user *) arg,
+				sizeof(config_rx32));
+
+	if (rc) {
+		pr_err("%s: copy config_rx from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+	memset(&config_rx, 0, sizeof(config_rx));
+	usf_compat_xx_info_type(&(config_rx32.us_xx_info),
+				&(config_rx.us_xx_info));
+
+	return __usf_set_rx_info(usf, &config_rx);
+} /* usf_set_rx_info32 */
+
+static int usf_get_tx_update32(struct usf_type *usf, unsigned long arg)
+{
+	struct us_tx_update_info_type32 upd_tx_info32;
+	struct us_tx_update_info_type upd_tx_info;
+
+	int rc = copy_from_user(&upd_tx_info32,
+				(struct us_tx_update_info_type32 __user *) arg,
+				sizeof(upd_tx_info32));
+
+	if (rc) {
+		pr_err("%s: copy upd_tx_info32 from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	memset(&upd_tx_info, 0, sizeof(upd_tx_info));
+	upd_tx_info.event_counter = upd_tx_info32.event_counter;
+	upd_tx_info.event = compat_ptr(upd_tx_info32.event);
+	upd_tx_info.free_region = upd_tx_info32.free_region;
+	upd_tx_info.timeout = upd_tx_info32.timeout;
+	upd_tx_info.event_filters = upd_tx_info32.event_filters;
+	upd_tx_info.params_data_size = upd_tx_info32.params_data_size;
+	upd_tx_info.params_data = compat_ptr(upd_tx_info32.params_data);
+	upd_tx_info.ready_region = upd_tx_info32.ready_region;
+
+	rc = __usf_get_tx_update(usf, &upd_tx_info);
+	if (rc < 0) {
+		pr_err("%s: get tx update failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	/* Update only the fields that were changed */
+	upd_tx_info32.ready_region = upd_tx_info.ready_region;
+
+	rc = copy_to_user((void __user *)arg, &upd_tx_info32,
+			  sizeof(upd_tx_info32));
+	if (rc) {
+		pr_err("%s: copy upd_tx_info32 to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_get_tx_update */
+
+static int usf_set_rx_update32(struct usf_xx_type *usf_xx, unsigned long arg)
+{
+	struct us_rx_update_info_type32 upd_rx_info32;
+	struct us_rx_update_info_type upd_rx_info;
+
+	int rc = copy_from_user(&upd_rx_info32,
+				(struct us_rx_update_info_type32 __user *) arg,
+				sizeof(upd_rx_info32));
+
+	if (rc) {
+		pr_err("%s: copy upd_rx_info32 from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	memset(&upd_rx_info, 0, sizeof(upd_rx_info));
+	upd_rx_info.ready_region = upd_rx_info32.ready_region;
+	upd_rx_info.params_data_size = upd_rx_info32.params_data_size;
+	upd_rx_info.params_data = compat_ptr(upd_rx_info32.params_data);
+	upd_rx_info.free_region = upd_rx_info32.free_region;
+
+	rc = __usf_set_rx_update(usf_xx, &upd_rx_info);
+	if (rc < 0) {
+		pr_err("%s: set rx update failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	/* Update only the fields that were changed */
+	upd_rx_info32.free_region = upd_rx_info.free_region;
+
+	rc = copy_to_user((void __user *)arg,
+			&upd_rx_info32,
+			sizeof(upd_rx_info32));
+	if (rc) {
+		pr_err("%s: copy rx_info32 to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_set_rx_update32 */
+
+static int usf_set_us_detection32(struct usf_type *usf, unsigned long arg)
+{
+	struct us_detect_info_type32 detect_info32;
+	struct us_detect_info_type detect_info;
+
+	int rc = copy_from_user(&detect_info32,
+				(struct us_detect_info_type32 __user *) arg,
+				sizeof(detect_info32));
+
+	if (rc) {
+		pr_err("%s: copy detect_info32 from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	if (detect_info32.params_data_size > USF_MAX_USER_BUF_SIZE) {
+		pr_err("%s: user buffer size exceeds maximum\n",
+			__func__);
+		return -EFAULT;
+	}
+
+	memset(&detect_info, 0, sizeof(detect_info));
+	detect_info.us_detector = detect_info32.us_detector;
+	detect_info.us_detect_mode = detect_info32.us_detect_mode;
+	detect_info.skip_time = detect_info32.skip_time;
+	detect_info.params_data_size = detect_info32.params_data_size;
+	detect_info.params_data = compat_ptr(detect_info32.params_data);
+	detect_info.detect_timeout = detect_info32.detect_timeout;
+	detect_info.is_us = detect_info32.is_us;
+
+	rc = __usf_set_us_detection(usf, &detect_info);
+	if (rc < 0) {
+		pr_err("%s: set us detection failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	/* Update only the fields that were changed */
+	detect_info32.is_us = detect_info.is_us;
+
+	rc = copy_to_user((void __user *)arg,
+			  &detect_info32,
+			  sizeof(detect_info32));
+	if (rc) {
+		pr_err("%s: copy detect_info32 to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_set_us_detection32 */
+
+static int usf_get_version32(unsigned long arg)
+{
+	struct us_version_info_type32 version_info32;
+	struct us_version_info_type version_info;
+
+	int rc = copy_from_user(&version_info32,
+				(struct us_version_info_type32 __user *) arg,
+				sizeof(version_info32));
+
+	if (rc) {
+		pr_err("%s: copy version_info32 from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	memset(&version_info, 0, sizeof(version_info));
+	version_info.buf_size = version_info32.buf_size;
+	version_info.pbuf = compat_ptr(version_info32.pbuf);
+
+	rc = __usf_get_version(&version_info);
+	if (rc < 0) {
+		pr_err("%s: get version failed; rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	/* None of the fields were changed */
+
+	rc = copy_to_user((void __user *)arg,
+			  &version_info32,
+			  sizeof(version_info32));
+	if (rc) {
+		pr_err("%s: copy version_info32 to user; rc=%d\n",
+			__func__, rc);
+		rc = -EFAULT;
+	}
+
+	return rc;
+} /* usf_get_version32 */
+
+static int usf_set_stream_param32(struct usf_xx_type *usf_xx,
+				unsigned long arg, int dir)
+{
+	struct us_stream_param_type32 set_stream_param32;
+	struct us_stream_param_type set_stream_param;
+	int rc = 0;
+
+	rc = copy_from_user(&set_stream_param32,
+			(struct us_stream_param_type32 __user *) arg,
+			sizeof(set_stream_param32));
+
+	if (rc) {
+		pr_err("%s: copy set_stream_param from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	memset(&set_stream_param, 0, sizeof(set_stream_param));
+	set_stream_param.module_id = set_stream_param32.module_id;
+	set_stream_param.param_id = set_stream_param32.param_id;
+	set_stream_param.buf_size = set_stream_param32.buf_size;
+	set_stream_param.pbuf = compat_ptr(set_stream_param32.pbuf);
+
+	return __usf_set_stream_param(usf_xx, &set_stream_param, dir);
+} /* usf_set_stream_param32 */
+
+static int usf_get_stream_param32(struct usf_xx_type *usf_xx,
+				unsigned long arg, int dir)
+{
+	struct us_stream_param_type32 get_stream_param32;
+	struct us_stream_param_type get_stream_param;
+	int rc = 0;
+
+	rc = copy_from_user(&get_stream_param32,
+			(struct us_stream_param_type32 __user *) arg,
+			sizeof(get_stream_param32));
+
+	if (rc) {
+		pr_err("%s: copy get_stream_param from user; rc=%d\n",
+			__func__, rc);
+		return -EFAULT;
+	}
+
+	memset(&get_stream_param, 0, sizeof(get_stream_param));
+	get_stream_param.module_id = get_stream_param32.module_id;
+	get_stream_param.param_id = get_stream_param32.param_id;
+	get_stream_param.buf_size = get_stream_param32.buf_size;
+	get_stream_param.pbuf = compat_ptr(get_stream_param32.pbuf);
+
+	return __usf_get_stream_param(usf_xx, &get_stream_param, dir);
+} /* usf_get_stream_param32 */
+
+static long __usf_compat_ioctl(struct usf_type *usf,
+			     unsigned int cmd,
+			     unsigned long arg)
+{
+	int rc = 0;
+	struct usf_xx_type *usf_xx = NULL;
+
+	switch (cmd) {
+	case US_START_TX:
+	case US_START_RX:
+	case US_STOP_TX:
+	case US_STOP_RX: {
+		return __usf_ioctl(usf, cmd, arg);
+	}
+
+	case US_SET_TX_INFO32: {
+		usf_xx = &usf->usf_tx;
+		if (usf_xx->usf_state == USF_OPENED_STATE)
+			rc = usf_set_tx_info32(usf, arg);
+		else {
+			pr_err("%s: set_tx_info32: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			return -EBADFD;
+		}
+
+		break;
+	} /* US_SET_TX_INFO32 */
+
+	case US_SET_RX_INFO32: {
+		usf_xx = &usf->usf_rx;
+		if (usf_xx->usf_state == USF_OPENED_STATE)
+			rc = usf_set_rx_info32(usf, arg);
+		else {
+			pr_err("%s: set_rx_info32: wrong state[%d]\n",
+				__func__,
+				usf_xx->usf_state);
+			return -EBADFD;
+		}
+
+		break;
+	} /* US_SET_RX_INFO32 */
+
+	case US_GET_TX_UPDATE32: {
+		struct usf_xx_type *usf_xx = &usf->usf_tx;
+
+		if (usf_xx->usf_state == USF_WORK_STATE)
+			rc = usf_get_tx_update32(usf, arg);
+		else {
+			pr_err("%s: get_tx_update32: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			rc = -EBADFD;
+		}
+		break;
+	} /* US_GET_TX_UPDATE32 */
+
+	case US_SET_RX_UPDATE32: {
+		struct usf_xx_type *usf_xx = &usf->usf_rx;
+
+		if (usf_xx->usf_state == USF_WORK_STATE)
+			rc = usf_set_rx_update32(usf_xx, arg);
+		else {
+			pr_err("%s: set_rx_update: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			rc = -EBADFD;
+		}
+		break;
+	} /* US_SET_RX_UPDATE32 */
+
+	case US_SET_DETECTION32: {
+		struct usf_xx_type *usf_xx = &usf->usf_tx;
+
+		if (usf_xx->usf_state == USF_WORK_STATE)
+			rc = usf_set_us_detection32(usf, arg);
+		else {
+			pr_err("%s: set us detection: wrong state[%d]\n",
+			       __func__,
+			       usf_xx->usf_state);
+			rc = -EBADFD;
+		}
+		break;
+	} /* US_SET_DETECTION32 */
+
+	case US_GET_VERSION32: {
+		rc = usf_get_version32(arg);
+		break;
+	} /* US_GET_VERSION32 */
+
+	case US_SET_TX_STREAM_PARAM32: {
+		rc = usf_set_stream_param32(&usf->usf_tx, arg, OUT);
+		break;
+	} /* US_SET_TX_STREAM_PARAM32 */
+
+	case US_GET_TX_STREAM_PARAM32: {
+		rc = usf_get_stream_param32(&usf->usf_tx, arg, OUT);
+		break;
+	} /* US_GET_TX_STREAM_PARAM32 */
+
+	case US_SET_RX_STREAM_PARAM32: {
+		rc = usf_set_stream_param32(&usf->usf_rx, arg, IN);
+		break;
+	} /* US_SET_RX_STREAM_PARAM32 */
+
+	case US_GET_RX_STREAM_PARAM32: {
+		rc = usf_get_stream_param32(&usf->usf_rx, arg, IN);
+		break;
+	} /* US_GET_RX_STREAM_PARAM32 */
+
+	default:
+		pr_err("%s: unsupported IOCTL command [%d]\n",
+		       __func__,
+		       cmd);
+		rc = -ENOTTY;
+		break;
+	}
+
+	if (rc &&
+	    ((cmd == US_SET_TX_INFO) ||
+	     (cmd == US_SET_RX_INFO)))
+		release_xx(usf_xx);
+
+	return rc;
+} /* __usf_compat_ioctl */
+
+static long usf_compat_ioctl(struct file *file,
+			     unsigned int cmd,
+			     unsigned long arg)
+{
+	struct usf_type *usf = file->private_data;
+	int rc = 0;
+
+	mutex_lock(&usf->mutex);
+	rc = __usf_compat_ioctl(usf, cmd, arg);
+	mutex_unlock(&usf->mutex);
+
+	return rc;
+} /* usf_compat_ioctl */
+#endif /* CONFIG_COMPAT */
+
+static int usf_mmap(struct file *file, struct vm_area_struct *vms)
+{
+	struct usf_type *usf = file->private_data;
+	int dir = OUT;
+	struct usf_xx_type *usf_xx = &usf->usf_tx;
+	int rc = 0;
+
+	mutex_lock(&usf->mutex);
+	if (vms->vm_flags & USF_VM_WRITE) { /* RX buf mapping */
+		dir = IN;
+		usf_xx = &usf->usf_rx;
+	}
+	rc = q6usm_get_virtual_address(dir, usf_xx->usc, vms);
+	mutex_unlock(&usf->mutex);
+
+	return rc;
+}
+
+static uint16_t add_opened_dev(int minor)
+{
+	uint16_t ind = 0;
+
+	for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) {
+		if (minor == s_opened_devs[ind]) {
+			pr_err("%s: device %d is already opened\n",
+			       __func__, minor);
+			return USF_UNDEF_DEV_ID;
+		}
+
+		if (s_opened_devs[ind] == 0) {
+			s_opened_devs[ind] = minor;
+			pr_debug("%s: device %d is added; ind=%d\n",
+				__func__, minor, ind);
+			return ind;
+		}
+	}
+
+	pr_err("%s: there is no place for device %d\n",
+	       __func__, minor);
+	return USF_UNDEF_DEV_ID;
+}
+
+static int usf_open(struct inode *inode, struct file *file)
+{
+	struct usf_type *usf =  NULL;
+	uint16_t dev_ind = 0;
+	int minor = MINOR(inode->i_rdev);
+
+	dev_ind = add_opened_dev(minor);
+	if (dev_ind == USF_UNDEF_DEV_ID)
+		return -EBUSY;
+
+	usf = kzalloc(sizeof(struct usf_type), GFP_KERNEL);
+	if (usf == NULL)
+		return -ENOMEM;
+
+	wakeup_source_init(&usf_wakeup_source, "usf");
+
+	file->private_data = usf;
+	usf->dev_ind = dev_ind;
+
+	usf->usf_tx.usf_state = USF_OPENED_STATE;
+	usf->usf_rx.usf_state = USF_OPENED_STATE;
+
+	usf->usf_tx.us_detect_type = USF_US_DETECT_UNDEF;
+	usf->usf_rx.us_detect_type = USF_US_DETECT_UNDEF;
+
+	mutex_init(&usf->mutex);
+
+	pr_debug("%s:usf in open\n", __func__);
+	return 0;
+}
+
+static int usf_release(struct inode *inode, struct file *file)
+{
+	struct usf_type *usf = file->private_data;
+
+	pr_debug("%s: release entry\n", __func__);
+
+	mutex_lock(&usf->mutex);
+	usf_release_input(usf);
+
+	usf_disable(&usf->usf_tx);
+	usf_disable(&usf->usf_rx);
+
+	s_opened_devs[usf->dev_ind] = 0;
+
+	wakeup_source_trash(&usf_wakeup_source);
+	mutex_unlock(&usf->mutex);
+	mutex_destroy(&usf->mutex);
+	kfree(usf);
+	pr_debug("%s: release exit\n", __func__);
+	return 0;
+}
+
+extern long usf_compat_ioctl(struct file *file,
+			     unsigned int cmd,
+			     unsigned long arg);
+
+static const struct file_operations usf_fops = {
+	.owner                  = THIS_MODULE,
+	.open                   = usf_open,
+	.release                = usf_release,
+	.unlocked_ioctl = usf_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = usf_compat_ioctl,
+#endif /* CONFIG_COMPAT */
+	.mmap                   = usf_mmap,
+};
+
+static struct miscdevice usf_misc[MAX_DEVS_NUMBER] = {
+	{
+		.minor  = MISC_DYNAMIC_MINOR,
+		.name   = "usf1",
+		.fops   = &usf_fops,
+	},
+};
+
+static int __init usf_init(void)
+{
+	int rc = 0;
+	uint16_t ind = 0;
+
+	pr_debug("%s: USF SW version %s.\n", __func__, DRV_VERSION);
+	pr_debug("%s: Max %d devs registration\n", __func__, MAX_DEVS_NUMBER);
+
+	for (ind = 0; ind < MAX_DEVS_NUMBER; ++ind) {
+		rc = misc_register(&usf_misc[ind]);
+		if (rc) {
+			pr_err("%s: misc_register() failed ind=%d; rc = %d\n",
+			       __func__, ind, rc);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+device_initcall(usf_init);
+
+MODULE_DESCRIPTION("Ultrasound framework driver");
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
new file mode 100644
index 0000000..6b8aae0
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.c
@@ -0,0 +1,422 @@
+/* Copyright (c) 2012-2013, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input/mt.h>
+#include <linux/syscalls.h>
+#include "usfcdev.h"
+
+#define UNDEF_ID    0xffffffff
+#define SLOT_CMD_ID 0
+#define MAX_RETRIES 10
+
+enum usdev_event_status {
+	USFCDEV_EVENT_ENABLED,
+	USFCDEV_EVENT_DISABLING,
+	USFCDEV_EVENT_DISABLED,
+};
+
+struct usfcdev_event {
+	bool (*match_cb)(uint16_t, struct input_dev *dev);
+	bool registered_event;
+	bool interleaved;
+	enum usdev_event_status event_status;
+};
+static struct usfcdev_event s_usfcdev_events[MAX_EVENT_TYPE_NUM];
+
+struct usfcdev_input_command {
+	unsigned int type;
+	unsigned int code;
+	unsigned int value;
+};
+
+static long  s_usf_pid;
+
+static bool usfcdev_filter(struct input_handle *handle,
+			 unsigned int type, unsigned int code, int value);
+static bool usfcdev_match(struct input_handler *handler,
+				struct input_dev *dev);
+static int usfcdev_connect(struct input_handler *handler,
+				struct input_dev *dev,
+				const struct input_device_id *id);
+static void usfcdev_disconnect(struct input_handle *handle);
+
+static const struct input_device_id usfc_tsc_ids[] = {
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+			INPUT_DEVICE_ID_MATCH_KEYBIT |
+			INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) },
+		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+		/* assumption: ABS_X & ABS_Y are in the same long */
+		.absbit = { [BIT_WORD(ABS_X)] = BIT_MASK(ABS_X) |
+						BIT_MASK(ABS_Y) },
+	},
+	{
+		.flags = INPUT_DEVICE_ID_MATCH_EVBIT |
+			INPUT_DEVICE_ID_MATCH_KEYBIT |
+			INPUT_DEVICE_ID_MATCH_ABSBIT,
+		.evbit = { BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY) },
+		.keybit = { [BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH) },
+		/* assumption: MT_.._X & MT_.._Y are in the same long */
+		.absbit = { [BIT_WORD(ABS_MT_POSITION_X)] =
+			BIT_MASK(ABS_MT_POSITION_X) |
+			BIT_MASK(ABS_MT_POSITION_Y) },
+	},
+	{ } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(input, usfc_tsc_ids);
+
+static struct input_handler s_usfc_handlers[MAX_EVENT_TYPE_NUM] = {
+	{ /* TSC handler */
+		.filter         = usfcdev_filter,
+		.match          = usfcdev_match,
+		.connect        = usfcdev_connect,
+		.disconnect     = usfcdev_disconnect,
+		/* .minor can be used as index in the container, */
+		/*  because .fops isn't supported */
+		.minor          = TSC_EVENT_TYPE_IND,
+		.name           = "usfc_tsc_handler",
+		.id_table       = usfc_tsc_ids,
+	},
+};
+
+/*
+ * For each event type, there are a number conflicting devices (handles)
+ * The first registered device (primary) is real TSC device; it's mandatory
+ * Optionally, later registered devices are simulated ones.
+ * They are dynamically managed
+ * The primary device's handles are stored in the below static array
+ */
+static struct input_handle s_usfc_primary_handles[MAX_EVENT_TYPE_NUM] = {
+	{ /* TSC handle */
+		.handler	= &s_usfc_handlers[TSC_EVENT_TYPE_IND],
+		.name		= "usfc_tsc_handle",
+	},
+};
+
+static struct usfcdev_input_command initial_clear_cmds[] = {
+	{EV_ABS, ABS_PRESSURE,               0},
+	{EV_KEY, BTN_TOUCH,                  0},
+};
+
+static struct usfcdev_input_command slot_clear_cmds[] = {
+	{EV_ABS, ABS_MT_SLOT,               0},
+	{EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
+};
+
+static struct usfcdev_input_command no_filter_cmds[] = {
+	{EV_ABS, ABS_MT_SLOT,               0},
+	{EV_ABS, ABS_MT_TRACKING_ID, UNDEF_ID},
+	{EV_SYN, SYN_REPORT,                0},
+};
+
+static bool usfcdev_match(struct input_handler *handler, struct input_dev *dev)
+{
+	bool rc = false;
+	int ind = handler->minor;
+
+	pr_debug("%s: name=[%s]; ind=%d\n", __func__, dev->name, ind);
+
+	if (s_usfcdev_events[ind].registered_event &&
+		s_usfcdev_events[ind].match_cb) {
+		rc = (*s_usfcdev_events[ind].match_cb)((uint16_t)ind, dev);
+		pr_debug("%s: [%s]; rc=%d\n", __func__, dev->name, rc);
+	}
+	return rc;
+}
+
+static int usfcdev_connect(struct input_handler *handler, struct input_dev *dev,
+				const struct input_device_id *id)
+{
+	int ret = 0;
+	uint16_t ind = handler->minor;
+	struct input_handle *usfc_handle = NULL;
+
+	if (s_usfc_primary_handles[ind].dev == NULL) {
+		pr_debug("%s: primary device; ind=%d\n",
+			__func__,
+			ind);
+		usfc_handle = &s_usfc_primary_handles[ind];
+	} else {
+		pr_debug("%s: secondary device; ind=%d\n",
+			__func__,
+			ind);
+		usfc_handle = kzalloc(sizeof(struct input_handle),
+					GFP_KERNEL);
+		if (!usfc_handle)
+			return -ENOMEM;
+
+		usfc_handle->handler = &s_usfc_handlers[ind];
+		usfc_handle->name = s_usfc_primary_handles[ind].name;
+	}
+	usfc_handle->dev = dev;
+	ret = input_register_handle(usfc_handle);
+	pr_debug("%s: name=[%s]; ind=%d; dev=0x%pK\n",
+		 __func__,
+		dev->name,
+		ind,
+		usfc_handle->dev);
+	if (ret)
+		pr_err("%s: input_register_handle[%d] failed: ret=%d\n",
+			__func__,
+			ind,
+			ret);
+	else {
+		ret = input_open_device(usfc_handle);
+		if (ret) {
+			pr_err("%s: input_open_device[%d] failed: ret=%d\n",
+				__func__,
+				ind,
+				ret);
+			input_unregister_handle(usfc_handle);
+		} else
+			pr_debug("%s: device[%d] is opened\n",
+				__func__,
+				ind);
+	}
+
+	return ret;
+}
+
+static void usfcdev_disconnect(struct input_handle *handle)
+{
+	int ind = handle->handler->minor;
+
+	input_close_device(handle);
+	input_unregister_handle(handle);
+	pr_debug("%s: handle[%d], name=[%s] is disconnected\n",
+		__func__,
+		ind,
+		handle->dev->name);
+	if (s_usfc_primary_handles[ind].dev == handle->dev)
+		s_usfc_primary_handles[ind].dev = NULL;
+	else
+		kfree(handle);
+}
+
+static bool usfcdev_filter(struct input_handle *handle,
+			unsigned int type, unsigned int code, int value)
+{
+	uint16_t i = 0;
+	uint16_t ind = (uint16_t)handle->handler->minor;
+	bool rc = (s_usfcdev_events[ind].event_status != USFCDEV_EVENT_ENABLED);
+
+	if (s_usf_pid == sys_getpid()) {
+		/* Pass events from usfcdev driver */
+		rc = false;
+		pr_debug("%s: event_type=%d; type=%d; code=%d; val=%d",
+			__func__,
+			ind,
+			type,
+			code,
+			value);
+	} else if (s_usfcdev_events[ind].event_status ==
+						USFCDEV_EVENT_DISABLING) {
+		uint32_t u_value = value;
+
+		s_usfcdev_events[ind].interleaved = true;
+		/* Pass events for freeing slots from TSC driver */
+		for (i = 0; i < ARRAY_SIZE(no_filter_cmds); ++i) {
+			if ((no_filter_cmds[i].type == type) &&
+			    (no_filter_cmds[i].code == code) &&
+			    (no_filter_cmds[i].value <= u_value)) {
+				rc = false;
+				pr_debug("%s: no_filter_cmds[%d]; %d",
+					__func__,
+					i,
+					no_filter_cmds[i].value);
+				break;
+			}
+		}
+	}
+
+	return rc;
+}
+
+bool usfcdev_register(
+	uint16_t event_type_ind,
+	bool (*match_cb)(uint16_t, struct input_dev *dev))
+{
+	int ret = 0;
+	bool rc = false;
+
+	if ((event_type_ind >= MAX_EVENT_TYPE_NUM) || !match_cb) {
+		pr_err("%s: wrong input: event_type_ind=%d; match_cb=0x%pK\n",
+			__func__,
+			event_type_ind,
+			match_cb);
+		return false;
+	}
+
+	if (s_usfcdev_events[event_type_ind].registered_event) {
+		pr_info("%s: handler[%d] was already registered\n",
+			__func__,
+			event_type_ind);
+		return true;
+	}
+
+	s_usfcdev_events[event_type_ind].registered_event = true;
+	s_usfcdev_events[event_type_ind].match_cb = match_cb;
+	s_usfcdev_events[event_type_ind].event_status = USFCDEV_EVENT_ENABLED;
+	ret = input_register_handler(&s_usfc_handlers[event_type_ind]);
+	if (!ret) {
+		rc = true;
+		pr_debug("%s: handler[%d] was registered\n",
+			__func__,
+			event_type_ind);
+	} else {
+		s_usfcdev_events[event_type_ind].registered_event = false;
+		s_usfcdev_events[event_type_ind].match_cb = NULL;
+		pr_err("%s: handler[%d] registration failed: ret=%d\n",
+			__func__,
+			event_type_ind,
+			ret);
+	}
+
+	return rc;
+}
+
+void usfcdev_unregister(uint16_t event_type_ind)
+{
+	if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+		pr_err("%s: wrong input: event_type_ind=%d\n",
+			__func__,
+			event_type_ind);
+		return;
+	}
+	if (s_usfcdev_events[event_type_ind].registered_event) {
+		input_unregister_handler(&s_usfc_handlers[event_type_ind]);
+		pr_debug("%s: handler[%d] was unregistered\n",
+			__func__,
+			event_type_ind);
+		s_usfcdev_events[event_type_ind].registered_event = false;
+		s_usfcdev_events[event_type_ind].match_cb = NULL;
+		s_usfcdev_events[event_type_ind].event_status =
+							USFCDEV_EVENT_ENABLED;
+
+	}
+}
+
+static inline void usfcdev_send_cmd(
+	struct input_dev *dev,
+	struct usfcdev_input_command cmd)
+{
+	input_event(dev, cmd.type, cmd.code, cmd.value);
+}
+
+static void usfcdev_clean_dev(uint16_t event_type_ind)
+{
+	struct input_dev *dev = NULL;
+	int i;
+	int j;
+	int retries = 0;
+
+	if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+		pr_err("%s: wrong input: event_type_ind=%d\n",
+			__func__,
+			event_type_ind);
+		return;
+	}
+	/* Only primary device must exist */
+	dev = s_usfc_primary_handles[event_type_ind].dev;
+	if (dev == NULL) {
+		pr_err("%s: NULL primary device\n",
+		__func__);
+		return;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(initial_clear_cmds); i++)
+		usfcdev_send_cmd(dev, initial_clear_cmds[i]);
+	input_sync(dev);
+
+	/* Send commands to free all slots */
+	for (i = 0; i < dev->mt->num_slots; i++) {
+		s_usfcdev_events[event_type_ind].interleaved = false;
+		if (input_mt_get_value(&dev->mt->slots[i],
+					ABS_MT_TRACKING_ID) < 0) {
+			pr_debug("%s: skipping slot %d",
+				__func__, i);
+			continue;
+		}
+		slot_clear_cmds[SLOT_CMD_ID].value = i;
+		for (j = 0; j < ARRAY_SIZE(slot_clear_cmds); j++)
+			usfcdev_send_cmd(dev, slot_clear_cmds[j]);
+
+		if (s_usfcdev_events[event_type_ind].interleaved) {
+			pr_debug("%s: interleaved(%d): slot(%d)",
+				__func__, i, dev->mt->slot);
+			if (retries++ < MAX_RETRIES) {
+				--i;
+				continue;
+			}
+			pr_warn("%s: index(%d) reached max retires",
+				__func__, i);
+		}
+
+		retries = 0;
+		input_sync(dev);
+	}
+}
+
+bool usfcdev_set_filter(uint16_t event_type_ind, bool filter)
+{
+	bool rc = true;
+
+	if (event_type_ind >= MAX_EVENT_TYPE_NUM) {
+		pr_err("%s: wrong input: event_type_ind=%d\n",
+			__func__,
+			event_type_ind);
+		return false;
+	}
+
+	if (s_usfcdev_events[event_type_ind].registered_event) {
+
+		pr_debug("%s: event_type[%d]; filter=%d\n",
+			__func__,
+			event_type_ind,
+			filter
+			);
+		if (filter) {
+			s_usfcdev_events[event_type_ind].event_status =
+						USFCDEV_EVENT_DISABLING;
+			s_usf_pid = sys_getpid();
+			usfcdev_clean_dev(event_type_ind);
+			s_usfcdev_events[event_type_ind].event_status =
+						USFCDEV_EVENT_DISABLED;
+		} else
+			s_usfcdev_events[event_type_ind].event_status =
+						USFCDEV_EVENT_ENABLED;
+	} else {
+		pr_err("%s: event_type[%d] isn't registered\n",
+			__func__,
+			event_type_ind);
+		rc = false;
+	}
+
+	return rc;
+}
+
+static int __init usfcdev_init(void)
+{
+	return 0;
+}
+
+device_initcall(usfcdev_init);
+
+MODULE_DESCRIPTION("Handle of events from devices, conflicting with USF");
diff --git a/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h
new file mode 100644
index 0000000..03b62c5
--- /dev/null
+++ b/drivers/misc/qcom/qdsp6v2/ultrasound/usfcdev.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __USFCDEV_H__
+#define __USFCDEV_H__
+
+#include <linux/input.h>
+
+/* TSC event type index in the containers of the handlers & handles */
+#define TSC_EVENT_TYPE_IND 0
+/* Number of supported event types to be filtered */
+#define MAX_EVENT_TYPE_NUM 1
+
+bool usfcdev_register(
+	uint16_t event_type_ind,
+	bool (*match_cb)(uint16_t, struct input_dev *dev));
+void usfcdev_unregister(uint16_t event_type_ind);
+bool usfcdev_set_filter(uint16_t event_type_ind, bool filter);
+#endif /* __USFCDEV_H__ */
diff --git a/drivers/net/ethernet/msm/ecm_ipa.c b/drivers/net/ethernet/msm/ecm_ipa.c
index ebc424b..b769cca 100644
--- a/drivers/net/ethernet/msm/ecm_ipa.c
+++ b/drivers/net/ethernet/msm/ecm_ipa.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -9,7 +9,6 @@
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  */
-
 #include <linux/debugfs.h>
 #include <linux/errno.h>
 #include <linux/etherdevice.h>
@@ -105,10 +104,6 @@ enum ecm_ipa_operation {
  * struct ecm_ipa_dev - main driver context parameters
  * @net: network interface struct implemented by this driver
  * @directory: debugfs directory for various debuging switches
- * @tx_enable: flag that enable/disable Tx path to continue to IPA
- * @rx_enable: flag that enable/disable Rx path to continue to IPA
- * @rm_enable: flag that enable/disable Resource manager request prior to Tx
- * @dma_enable: flag that allow on-the-fly DMA mode for IPA
  * @eth_ipv4_hdr_hdl: saved handle for ipv4 header-insertion table
  * @eth_ipv6_hdr_hdl: saved handle for ipv6 header-insertion table
  * @usb_to_ipa_hdl: save handle for IPA pipe operations
@@ -128,10 +123,6 @@ enum ecm_ipa_operation {
  */
 struct ecm_ipa_dev {
 	struct net_device *net;
-	u32 tx_enable;
-	u32 rx_enable;
-	u32  rm_enable;
-	bool dma_enable;
 	struct dentry *directory;
 	u32 eth_ipv4_hdr_hdl;
 	u32 eth_ipv6_hdr_hdl;
@@ -167,26 +158,11 @@ static void ecm_ipa_rm_notify
 static struct net_device_stats *ecm_ipa_get_stats(struct net_device *net);
 static int ecm_ipa_create_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
 static void ecm_ipa_destroy_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx);
-static bool rx_filter(struct sk_buff *skb);
-static bool tx_filter(struct sk_buff *skb);
-static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx);
 static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx);
 static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx);
 static netdev_tx_t ecm_ipa_start_xmit
 	(struct sk_buff *skb, struct net_device *net);
-static int ecm_ipa_debugfs_stall_open
-	(struct inode *inode, struct file *file);
-static ssize_t ecm_ipa_debugfs_stall_write
-	(struct file *file, const char __user *buf, size_t count,
-		loff_t *ppos);
 static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file);
-static ssize_t ecm_ipa_debugfs_enable_write_dma
-	(struct file *file, const char __user *buf, size_t count,
-		loff_t *ppos);
-static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file);
-static ssize_t ecm_ipa_debugfs_enable_write
-	(struct file *file, const char __user *buf, size_t count,
-		loff_t *ppos);
 static ssize_t ecm_ipa_debugfs_enable_read
 	(struct file *file, char __user *ubuf, size_t count, loff_t *ppos);
 static ssize_t ecm_ipa_debugfs_atomic_read
@@ -194,8 +170,6 @@ static ssize_t ecm_ipa_debugfs_atomic_read
 static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx);
 static void ecm_ipa_debugfs_destroy(struct ecm_ipa_dev *ecm_ipa_ctx);
 static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl);
-static int ecm_ipa_ep_registers_dma_cfg
-	(u32 usb_to_ipa_hdl, enum ipa_client_type prod_client);
 static int ecm_ipa_set_device_ethernet_addr
 	(u8 *dev_ethaddr, u8 device_ethaddr[]);
 static enum ecm_ipa_state ecm_ipa_next_state
@@ -213,22 +187,11 @@ static const struct net_device_ops ecm_ipa_netdev_ops = {
 	.ndo_get_stats = ecm_ipa_get_stats,
 };
 
-const struct file_operations ecm_ipa_debugfs_dma_ops = {
-	.open = ecm_ipa_debugfs_dma_open,
-	.read = ecm_ipa_debugfs_enable_read,
-	.write = ecm_ipa_debugfs_enable_write_dma,
-};
-
 const struct file_operations ecm_ipa_debugfs_atomic_ops = {
 	.open = ecm_ipa_debugfs_atomic_open,
 	.read = ecm_ipa_debugfs_atomic_read,
 };
 
-const struct file_operations ecm_ipa_debugfs_stall_ops = {
-	.open = ecm_ipa_debugfs_stall_open,
-	.write = ecm_ipa_debugfs_stall_write,
-};
-
 static void ecm_ipa_msg_free_cb(void *buff, u32 len, u32 type)
 {
 	kfree(buff);
@@ -294,9 +257,6 @@ int ecm_ipa_init(struct ecm_ipa_params *params)
 	ECM_IPA_DEBUG("ecm_ipa_ctx (private) = %p\n", ecm_ipa_ctx);
 
 	ecm_ipa_ctx->net = net;
-	ecm_ipa_ctx->tx_enable = true;
-	ecm_ipa_ctx->rx_enable = true;
-	ecm_ipa_ctx->rm_enable = true;
 	ecm_ipa_ctx->outstanding_high = DEFAULT_OUTSTANDING_HIGH;
 	ecm_ipa_ctx->outstanding_low = DEFAULT_OUTSTANDING_LOW;
 	atomic_set(&ecm_ipa_ctx->outstanding_pkts, 0);
@@ -619,12 +579,6 @@ static netdev_tx_t ecm_ipa_start_xmit
 		return NETDEV_TX_BUSY;
 	}
 
-	if (unlikely(tx_filter(skb))) {
-		dev_kfree_skb_any(skb);
-		ECM_IPA_DEBUG("packet got filtered out on Tx path\n");
-		status = NETDEV_TX_OK;
-		goto out;
-	}
 	ret = resource_request(ecm_ipa_ctx);
 	if (ret) {
 		ECM_IPA_DEBUG("Waiting to resource\n");
@@ -698,11 +652,6 @@ static void ecm_ipa_packet_receive_notify
 
 	skb->dev = ecm_ipa_ctx->net;
 	skb->protocol = eth_type_trans(skb, ecm_ipa_ctx->net);
-	if (rx_filter(skb)) {
-		ECM_IPA_DEBUG("packet got filtered out on Rx path\n");
-		dev_kfree_skb_any(skb);
-		return;
-	}
 
 	result = netif_rx(skb);
 	if (result)
@@ -1174,44 +1123,15 @@ static void ecm_ipa_destroy_rm_resource(struct ecm_ipa_dev *ecm_ipa_ctx)
 	ECM_IPA_LOG_EXIT();
 }
 
-static bool rx_filter(struct sk_buff *skb)
-{
-	struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev);
-
-	return !ecm_ipa_ctx->rx_enable;
-}
-
-static bool tx_filter(struct sk_buff *skb)
-{
-	struct ecm_ipa_dev *ecm_ipa_ctx = netdev_priv(skb->dev);
-
-	return !ecm_ipa_ctx->tx_enable;
-}
-
-static bool rm_enabled(struct ecm_ipa_dev *ecm_ipa_ctx)
-{
-	return ecm_ipa_ctx->rm_enable;
-}
-
 static int resource_request(struct ecm_ipa_dev *ecm_ipa_ctx)
 {
-	int result = 0;
-
-	if (!rm_enabled(ecm_ipa_ctx))
-		goto out;
-	result = ipa_rm_inactivity_timer_request_resource(
-			IPA_RM_RESOURCE_STD_ECM_PROD);
-out:
-	return result;
+	return ipa_rm_inactivity_timer_request_resource(
+		IPA_RM_RESOURCE_STD_ECM_PROD);
 }
 
 static void resource_release(struct ecm_ipa_dev *ecm_ipa_ctx)
 {
-	if (!rm_enabled(ecm_ipa_ctx))
-		goto out;
 	ipa_rm_inactivity_timer_release_resource(IPA_RM_RESOURCE_STD_ECM_PROD);
-out:
-	return;
 }
 
 /**
@@ -1289,45 +1209,6 @@ static void ecm_ipa_tx_timeout(struct net_device *net)
 	net->stats.tx_errors++;
 }
 
-static int ecm_ipa_debugfs_stall_open
-	(struct inode *inode, struct file *file)
-{
-	ECM_IPA_LOG_ENTRY();
-
-	ECM_IPA_LOG_EXIT();
-
-	return 0;
-}
-
-static ssize_t ecm_ipa_debugfs_stall_write
-	(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
-	u32 cmdq_cfg_mmio_phy = 0xFD4E3038;
-	void *cmdq_cfg_mmio_virt;
-	int result;
-	bool val = 0;
-
-	ECM_IPA_LOG_ENTRY();
-
-	file->private_data = &val;
-	result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos);
-
-	cmdq_cfg_mmio_virt = ioremap(cmdq_cfg_mmio_phy, sizeof(u32));
-	if (!cmdq_cfg_mmio_virt) {
-		ECM_IPA_ERROR
-			("fail on mmio for cmdq_cfg_mmio_phy=0x%x",
-			cmdq_cfg_mmio_phy);
-		return result;
-	}
-
-	iowrite32(val, cmdq_cfg_mmio_virt);
-	ECM_IPA_DEBUG("Value %d was written to cfgq", val);
-
-	ECM_IPA_LOG_EXIT();
-
-	return result;
-}
-
 static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file)
 {
 	struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private;
@@ -1338,84 +1219,6 @@ static int ecm_ipa_debugfs_atomic_open(struct inode *inode, struct file *file)
 	return 0;
 }
 
-static ssize_t ecm_ipa_debugfs_enable_write_dma
-	(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
-	struct ecm_ipa_dev *ecm_ipa_ctx = file->private_data;
-	int result;
-
-	ECM_IPA_LOG_ENTRY();
-	file->private_data = &ecm_ipa_ctx->dma_enable;
-	result = ecm_ipa_debugfs_enable_write(file, buf, count, ppos);
-	if (ecm_ipa_ctx->dma_enable)
-		ecm_ipa_ep_registers_dma_cfg
-			(ecm_ipa_ctx->usb_to_ipa_hdl,
-			ecm_ipa_ctx->ipa_to_usb_client);
-	else
-		ecm_ipa_ep_registers_cfg
-			(ecm_ipa_ctx->usb_to_ipa_hdl,
-			ecm_ipa_ctx->usb_to_ipa_hdl);
-	ECM_IPA_LOG_EXIT();
-	return result;
-}
-
-static int ecm_ipa_debugfs_dma_open(struct inode *inode, struct file *file)
-{
-	struct ecm_ipa_dev *ecm_ipa_ctx = inode->i_private;
-
-	ECM_IPA_LOG_ENTRY();
-	file->private_data = ecm_ipa_ctx;
-	ECM_IPA_LOG_EXIT();
-	return 0;
-}
-
-static ssize_t ecm_ipa_debugfs_enable_write
-	(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
-{
-	unsigned long missing;
-	char input;
-	bool *enable = file->private_data;
-
-	if (count != sizeof(input) + 1) {
-		ECM_IPA_ERROR("wrong input length(%zd)\n", count);
-		return -EINVAL;
-	}
-	if (!buf) {
-		ECM_IPA_ERROR("Bad argument\n");
-		return -EINVAL;
-	}
-	missing = copy_from_user(&input, buf, 1);
-	if (missing)
-		return -EFAULT;
-	ECM_IPA_DEBUG("input received %c\n", input);
-	*enable = input - '0';
-	ECM_IPA_DEBUG("value was set to %d\n", *enable);
-	return count;
-}
-
-static ssize_t ecm_ipa_debugfs_enable_read
-	(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
-{
-	int nbytes;
-	int size = 0;
-	int ret;
-	loff_t pos;
-	u8 enable_str[sizeof(char) * 3] = {0};
-	bool *enable = file->private_data;
-
-	pos = *ppos;
-	nbytes = scnprintf(enable_str, sizeof(enable_str), "%d\n", *enable);
-	ret = simple_read_from_buffer(ubuf, count, ppos, enable_str, nbytes);
-	if (ret < 0) {
-		ECM_IPA_ERROR("simple_read_from_buffer problem\n");
-		return ret;
-	}
-	size += ret;
-	count -= nbytes;
-	*ppos = pos + size;
-	return size;
-}
-
 static ssize_t ecm_ipa_debugfs_atomic_read
 	(struct file *file, char __user *ubuf, size_t count, loff_t *ppos)
 {
@@ -1433,7 +1236,6 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx)
 {
 	const mode_t flags_read_write = 0666;
 	const mode_t flags_read_only = 0444;
-	const mode_t flags_write_only = 0222;
 	struct dentry *file;
 
 	ECM_IPA_LOG_ENTRY();
@@ -1446,27 +1248,6 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx)
 		ECM_IPA_ERROR("could not create debugfs directory entry\n");
 		goto fail_directory;
 	}
-	file = debugfs_create_bool
-		("tx_enable", flags_read_write,
-		ecm_ipa_ctx->directory, &ecm_ipa_ctx->tx_enable);
-	if (!file) {
-		ECM_IPA_ERROR("could not create debugfs tx file\n");
-		goto fail_file;
-	}
-	file = debugfs_create_bool
-		("rx_enable", flags_read_write,
-		ecm_ipa_ctx->directory, &ecm_ipa_ctx->rx_enable);
-	if (!file) {
-		ECM_IPA_ERROR("could not create debugfs rx file\n");
-		goto fail_file;
-	}
-	file = debugfs_create_bool
-		("rm_enable", flags_read_write,
-		ecm_ipa_ctx->directory, &ecm_ipa_ctx->rm_enable);
-	if (!file) {
-		ECM_IPA_ERROR("could not create debugfs rm file\n");
-		goto fail_file;
-	}
 	file = debugfs_create_u8
 		("outstanding_high", flags_read_write,
 		ecm_ipa_ctx->directory, &ecm_ipa_ctx->outstanding_high);
@@ -1482,14 +1263,6 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx)
 		goto fail_file;
 	}
 	file = debugfs_create_file
-		("dma_enable", flags_read_write,
-		ecm_ipa_ctx->directory,
-		ecm_ipa_ctx, &ecm_ipa_debugfs_dma_ops);
-	if (!file) {
-		ECM_IPA_ERROR("could not create debugfs dma file\n");
-		goto fail_file;
-	}
-	file = debugfs_create_file
 		("outstanding", flags_read_only,
 		ecm_ipa_ctx->directory,
 		ecm_ipa_ctx, &ecm_ipa_debugfs_atomic_ops);
@@ -1498,15 +1271,7 @@ static int ecm_ipa_debugfs_init(struct ecm_ipa_dev *ecm_ipa_ctx)
 		goto fail_file;
 	}
 
-	file = debugfs_create_file
-		("stall_ipa_rx_proc", flags_write_only,
-		ecm_ipa_ctx->directory,
-		ecm_ipa_ctx, &ecm_ipa_debugfs_stall_ops);
-	if (!file) {
-		ECM_IPA_ERROR("could not create stall_ipa_rx_proc file\n");
-		goto fail_file;
-	}
-
+	ECM_IPA_DEBUG("debugfs entries were created\n");
 	ECM_IPA_LOG_EXIT();
 
 	return 0;
@@ -1571,46 +1336,6 @@ static int ecm_ipa_ep_registers_cfg(u32 usb_to_ipa_hdl, u32 ipa_to_usb_hdl)
 }
 
 /**
- * ecm_ipa_ep_registers_dma_cfg() - configure the USB endpoints for ECM
- *	DMA
- * @usb_to_ipa_hdl: handle received from ipa_connect
- *
- * This function will override the previous configuration
- * which is needed for cores that does not support blocks logic
- * Note that client handles are the actual pipe index
- */
-static int ecm_ipa_ep_registers_dma_cfg(u32 usb_to_ipa_hdl,
-					enum ipa_client_type prod_client)
-{
-	int result = 0;
-	struct ipa_ep_cfg_mode cfg_mode;
-	u32 apps_to_ipa_hdl = 2;
-
-	ECM_IPA_LOG_ENTRY();
-
-	memset(&cfg_mode, 0, sizeof(cfg_mode));
-	cfg_mode.mode = IPA_DMA;
-	cfg_mode.dst = prod_client;
-	result = ipa_cfg_ep_mode(apps_to_ipa_hdl, &cfg_mode);
-	if (result) {
-		ECM_IPA_ERROR("failed to configure Apps to IPA\n");
-		goto out;
-	}
-	memset(&cfg_mode, 0, sizeof(cfg_mode));
-	cfg_mode.mode = IPA_DMA;
-	cfg_mode.dst = IPA_CLIENT_A5_LAN_WAN_CONS;
-	result = ipa_cfg_ep_mode(usb_to_ipa_hdl, &cfg_mode);
-	if (result) {
-		ECM_IPA_ERROR("failed to configure USB to IPA\n");
-		goto out;
-	}
-	ECM_IPA_DEBUG("end-point registers successfully configured\n");
-out:
-	ECM_IPA_LOG_EXIT();
-	return result;
-}
-
-/**
  * ecm_ipa_set_device_ethernet_addr() - set device etherenet address
  * @dev_ethaddr: device etherenet address
  *
diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
index ba7b034b..7810bad 100644
--- a/drivers/of/Kconfig
+++ b/drivers/of/Kconfig
@@ -99,6 +99,12 @@
 config OF_RESOLVE
 	bool
 
+config OF_SLIMBUS
+	def_tristate SLIMBUS
+	depends on SLIMBUS
+	help
+	  OpenFirmware SLIMBUS accessors
+
 config OF_OVERLAY
 	bool "Device Tree overlays"
 	select OF_DYNAMIC
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index d7efd9d..4b8dabe 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_OF_RESERVED_MEM) += of_reserved_mem.o
 obj-$(CONFIG_OF_RESOLVE)  += resolver.o
 obj-$(CONFIG_OF_OVERLAY) += overlay.o
+obj-$(CONFIG_OF_SLIMBUS)        += of_slimbus.o
 obj-$(CONFIG_OF_NUMA) += of_numa.o
 
 obj-$(CONFIG_OF_UNITTEST) += unittest-data/
diff --git a/drivers/of/address.c b/drivers/of/address.c
index 02b2903..3723f57 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -676,6 +676,23 @@ const __be32 *of_get_address(struct device_node *dev, int index, u64 *size,
 }
 EXPORT_SYMBOL(of_get_address);
 
+const __be32 *of_get_address_by_name(struct device_node *dev, const char *name,
+		u64 *size, unsigned int *flags)
+{
+	int index;
+
+	if (!name)
+		return NULL;
+
+	/* Try to read "reg-names" property and get the index by name */
+	index = of_property_match_string(dev, "reg-names", name);
+	if (index < 0)
+		return NULL;
+
+	return of_get_address(dev, index, size, flags);
+}
+EXPORT_SYMBOL(of_get_address_by_name);
+
 static int __of_address_to_resource(struct device_node *dev,
 		const __be32 *addrp, u64 size, unsigned int flags,
 		const char *name, struct resource *r)
@@ -769,6 +786,22 @@ void __iomem *of_iomap(struct device_node *np, int index)
 }
 EXPORT_SYMBOL(of_iomap);
 
+void __iomem *of_iomap_by_name(struct device_node *np, const char *name)
+{
+	int index;
+
+	if (!name)
+		return NULL;
+
+	/* Try to read "reg-names" property and get the index by name */
+	index = of_property_match_string(np, "reg-names", name);
+	if (index < 0)
+		return NULL;
+
+	return of_iomap(np, index);
+}
+EXPORT_SYMBOL(of_iomap_by_name);
+
 /*
  * of_io_request_and_map - Requests a resource and maps the memory mapped IO
  *			   for a given device_node
diff --git a/drivers/of/of_slimbus.c b/drivers/of/of_slimbus.c
new file mode 100644
index 0000000..2b3d240
--- /dev/null
+++ b/drivers/of/of_slimbus.c
@@ -0,0 +1,89 @@
+/* Copyright (c) 2012, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* OF helpers for SLIMbus */
+#include <linux/slimbus/slimbus.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_slimbus.h>
+
+int of_register_slim_devices(struct slim_controller *ctrl)
+{
+	struct device_node *node;
+	struct slim_boardinfo *binfo = NULL;
+	struct slim_boardinfo *temp;
+	int n = 0;
+	int ret = 0;
+
+	if (!ctrl->dev.of_node)
+		return -EINVAL;
+
+	for_each_child_of_node(ctrl->dev.of_node, node) {
+		struct property *prop;
+		struct slim_device *slim;
+		char *name;
+
+		prop = of_find_property(node, "elemental-addr", NULL);
+		if (!prop || prop->length != 6) {
+			dev_err(&ctrl->dev, "of_slim: invalid E-addr");
+			continue;
+		}
+		name = kzalloc(SLIMBUS_NAME_SIZE, GFP_KERNEL);
+		if (!name) {
+			ret = -ENOMEM;
+			goto of_slim_err;
+		}
+		if (of_modalias_node(node, name, SLIMBUS_NAME_SIZE) < 0) {
+			dev_err(&ctrl->dev, "of_slim: modalias failure on %s\n",
+				node->full_name);
+			kfree(name);
+			continue;
+		}
+		slim = kzalloc(sizeof(struct slim_device), GFP_KERNEL);
+		if (!slim) {
+			ret = -ENOMEM;
+			kfree(name);
+			goto of_slim_err;
+		}
+		memcpy(slim->e_addr, prop->value, 6);
+
+		temp = krealloc(binfo, (n + 1) * sizeof(struct slim_boardinfo),
+					GFP_KERNEL);
+		if (!temp) {
+			dev_err(&ctrl->dev, "out of memory");
+			kfree(name);
+			kfree(slim);
+			ret = -ENOMEM;
+			goto of_slim_err;
+		}
+		binfo = temp;
+
+		slim->dev.of_node = of_node_get(node);
+		slim->name = (const char *)name;
+		binfo[n].bus_num = ctrl->nr;
+		binfo[n].slim_slave = slim;
+		n++;
+	}
+	ret = slim_register_board_info(binfo, n);
+	if (!ret)
+		goto of_slim_ret;
+of_slim_err:
+	while (n-- > 0) {
+		kfree(binfo[n].slim_slave->name);
+		kfree(binfo[n].slim_slave);
+	}
+of_slim_ret:
+	kfree(binfo);
+	return ret;
+}
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 084cb49..9afa97e 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -7,6 +7,7 @@
 obj-$(CONFIG_PCI_MVEBU) += pci-mvebu.o
 obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.o
 obj-$(CONFIG_PCI_TEGRA) += pci-tegra.o
+obj-$(CONFIG_PCI_MSM)	+= pci-msm.o
 obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
 obj-$(CONFIG_PCIE_RCAR) += pcie-rcar.o
 obj-$(CONFIG_PCI_HOST_COMMON) += pci-host-common.o
diff --git a/drivers/pci/host/pci-msm.c b/drivers/pci/host/pci-msm.c
new file mode 100644
index 0000000..fdbbe41
--- /dev/null
+++ b/drivers/pci/host/pci-msm.c
@@ -0,0 +1,7098 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * MSM PCIe controller driver.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/of_pci.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/rpm-smd-regulator.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/of_gpio.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/reset.h>
+#include <linux/msm-bus.h>
+#include <linux/msm-bus-board.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/msi.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/pm_wakeup.h>
+#include <linux/compiler.h>
+#include <soc/qcom/scm.h>
+#include <linux/ipc_logging.h>
+#include <linux/msm_pcie.h>
+
+#ifdef CONFIG_ARCH_MDMCALIFORNIUM
+#define PCIE_VENDOR_ID_RCP		0x17cb
+#define PCIE_DEVICE_ID_RCP		0x0302
+
+#define PCIE20_L1SUB_CONTROL1		0x158
+#define PCIE20_PARF_DBI_BASE_ADDR	0x350
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE	0x358
+
+#define TX_BASE 0x200
+#define RX_BASE 0x400
+#define PCS_BASE 0x800
+#define PCS_MISC_BASE 0x600
+
+#elif defined(CONFIG_ARCH_MSM8998)
+#define PCIE_VENDOR_ID_RCP		0x17cb
+#define PCIE_DEVICE_ID_RCP		0x0105
+
+#define PCIE20_L1SUB_CONTROL1		0x1E4
+#define PCIE20_PARF_DBI_BASE_ADDR       0x350
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE 0x358
+
+#define TX_BASE 0
+#define RX_BASE 0
+#define PCS_BASE 0x800
+#define PCS_MISC_BASE 0
+
+#else
+#define PCIE_VENDOR_ID_RCP		0x17cb
+#define PCIE_DEVICE_ID_RCP		0x0104
+
+#define PCIE20_L1SUB_CONTROL1		0x158
+#define PCIE20_PARF_DBI_BASE_ADDR	0x168
+#define PCIE20_PARF_SLV_ADDR_SPACE_SIZE	0x16C
+
+#define TX_BASE 0x1000
+#define RX_BASE 0x1200
+#define PCS_BASE 0x1400
+#define PCS_MISC_BASE 0
+#endif
+
+#define TX(n, m) (TX_BASE + n * m * 0x1000)
+#define RX(n, m) (RX_BASE + n * m * 0x1000)
+#define PCS_PORT(n, m) (PCS_BASE + n * m * 0x1000)
+#define PCS_MISC_PORT(n, m) (PCS_MISC_BASE + n * m * 0x1000)
+
+#define QSERDES_COM_BG_TIMER			0x00C
+#define QSERDES_COM_SSC_EN_CENTER		0x010
+#define QSERDES_COM_SSC_ADJ_PER1		0x014
+#define QSERDES_COM_SSC_ADJ_PER2		0x018
+#define QSERDES_COM_SSC_PER1			0x01C
+#define QSERDES_COM_SSC_PER2			0x020
+#define QSERDES_COM_SSC_STEP_SIZE1		0x024
+#define QSERDES_COM_SSC_STEP_SIZE2		0x028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		0x034
+#define QSERDES_COM_CLK_ENABLE1			0x038
+#define QSERDES_COM_SYS_CLK_CTRL		0x03C
+#define QSERDES_COM_SYSCLK_BUF_ENABLE		0x040
+#define QSERDES_COM_PLL_IVCO			0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0		0x04C
+#define QSERDES_COM_LOCK_CMP2_MODE0		0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0		0x054
+#define QSERDES_COM_BG_TRIM			0x070
+#define QSERDES_COM_CLK_EP_DIV			0x074
+#define QSERDES_COM_CP_CTRL_MODE0		0x078
+#define QSERDES_COM_PLL_RCTRL_MODE0		0x084
+#define QSERDES_COM_PLL_CCTRL_MODE0		0x090
+#define QSERDES_COM_SYSCLK_EN_SEL		0x0AC
+#define QSERDES_COM_RESETSM_CNTRL		0x0B4
+#define QSERDES_COM_RESTRIM_CTRL		0x0BC
+#define QSERDES_COM_RESCODE_DIV_NUM		0x0C4
+#define QSERDES_COM_LOCK_CMP_EN			0x0C8
+#define QSERDES_COM_DEC_START_MODE0		0x0D0
+#define QSERDES_COM_DIV_FRAC_START1_MODE0	0x0DC
+#define QSERDES_COM_DIV_FRAC_START2_MODE0	0x0E0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0	0x0E4
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0	0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0	0x10C
+#define QSERDES_COM_VCO_TUNE_CTRL		0x124
+#define QSERDES_COM_VCO_TUNE_MAP		0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0		0x12C
+#define QSERDES_COM_VCO_TUNE2_MODE0		0x130
+#define QSERDES_COM_VCO_TUNE_TIMER1		0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2		0x148
+#define QSERDES_COM_BG_CTRL			0x170
+#define QSERDES_COM_CLK_SELECT			0x174
+#define QSERDES_COM_HSCLK_SEL			0x178
+#define QSERDES_COM_CORECLK_DIV			0x184
+#define QSERDES_COM_CORE_CLK_EN			0x18C
+#define QSERDES_COM_C_READY_STATUS		0x190
+#define QSERDES_COM_CMN_CONFIG			0x194
+#define QSERDES_COM_SVS_MODE_CLK_SEL		0x19C
+#define QSERDES_COM_DEBUG_BUS0			0x1A0
+#define QSERDES_COM_DEBUG_BUS1			0x1A4
+#define QSERDES_COM_DEBUG_BUS2			0x1A8
+#define QSERDES_COM_DEBUG_BUS3			0x1AC
+#define QSERDES_COM_DEBUG_BUS_SEL		0x1B0
+
+#define QSERDES_TX_N_RES_CODE_LANE_OFFSET(n, m)		(TX(n, m) + 0x4C)
+#define QSERDES_TX_N_DEBUG_BUS_SEL(n, m)		(TX(n, m) + 0x64)
+#define QSERDES_TX_N_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN(n, m) (TX(n, m) + 0x68)
+#define QSERDES_TX_N_LANE_MODE(n, m)			(TX(n, m) + 0x94)
+#define QSERDES_TX_N_RCV_DETECT_LVL_2(n, m)		(TX(n, m) + 0xAC)
+
+#define QSERDES_RX_N_UCDR_SO_GAIN_HALF(n, m)		(RX(n, m) + 0x010)
+#define QSERDES_RX_N_UCDR_SO_GAIN(n, m)			(RX(n, m) + 0x01C)
+#define QSERDES_RX_N_UCDR_SO_SATURATION_AND_ENABLE(n, m) (RX(n, m) + 0x048)
+#define QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL2(n, m)	(RX(n, m) + 0x0D8)
+#define QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL3(n, m)	(RX(n, m) + 0x0DC)
+#define QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL4(n, m)	(RX(n, m) + 0x0E0)
+#define QSERDES_RX_N_SIGDET_ENABLES(n, m)		(RX(n, m) + 0x110)
+#define QSERDES_RX_N_SIGDET_DEGLITCH_CNTRL(n, m)	(RX(n, m) + 0x11C)
+#define QSERDES_RX_N_SIGDET_LVL(n, m)			(RX(n, m) + 0x118)
+#define QSERDES_RX_N_RX_BAND(n, m)			(RX(n, m) + 0x120)
+
+#define PCIE_MISC_N_DEBUG_BUS_BYTE0_INDEX(n, m)	(PCS_MISC_PORT(n, m) + 0x00)
+#define PCIE_MISC_N_DEBUG_BUS_BYTE1_INDEX(n, m)	(PCS_MISC_PORT(n, m) + 0x04)
+#define PCIE_MISC_N_DEBUG_BUS_BYTE2_INDEX(n, m)	(PCS_MISC_PORT(n, m) + 0x08)
+#define PCIE_MISC_N_DEBUG_BUS_BYTE3_INDEX(n, m)	(PCS_MISC_PORT(n, m) + 0x0C)
+#define PCIE_MISC_N_DEBUG_BUS_0_STATUS(n, m)	(PCS_MISC_PORT(n, m) + 0x14)
+#define PCIE_MISC_N_DEBUG_BUS_1_STATUS(n, m)	(PCS_MISC_PORT(n, m) + 0x18)
+#define PCIE_MISC_N_DEBUG_BUS_2_STATUS(n, m)	(PCS_MISC_PORT(n, m) + 0x1C)
+#define PCIE_MISC_N_DEBUG_BUS_3_STATUS(n, m)	(PCS_MISC_PORT(n, m) + 0x20)
+
+#define PCIE_N_SW_RESET(n, m)			(PCS_PORT(n, m) + 0x00)
+#define PCIE_N_POWER_DOWN_CONTROL(n, m)		(PCS_PORT(n, m) + 0x04)
+#define PCIE_N_START_CONTROL(n, m)		(PCS_PORT(n, m) + 0x08)
+#define PCIE_N_TXDEEMPH_M6DB_V0(n, m)		(PCS_PORT(n, m) + 0x24)
+#define PCIE_N_TXDEEMPH_M3P5DB_V0(n, m)		(PCS_PORT(n, m) + 0x28)
+#define PCIE_N_ENDPOINT_REFCLK_DRIVE(n, m)	(PCS_PORT(n, m) + 0x54)
+#define PCIE_N_RX_IDLE_DTCT_CNTRL(n, m)		(PCS_PORT(n, m) + 0x58)
+#define PCIE_N_POWER_STATE_CONFIG1(n, m)	(PCS_PORT(n, m) + 0x60)
+#define PCIE_N_POWER_STATE_CONFIG4(n, m)	(PCS_PORT(n, m) + 0x6C)
+#define PCIE_N_PWRUP_RESET_DLY_TIME_AUXCLK(n, m)	(PCS_PORT(n, m) + 0xA0)
+#define PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK(n, m)	(PCS_PORT(n, m) + 0xA4)
+#define PCIE_N_PLL_LOCK_CHK_DLY_TIME(n, m)	(PCS_PORT(n, m) + 0xA8)
+#define PCIE_N_TEST_CONTROL4(n, m)		(PCS_PORT(n, m) + 0x11C)
+#define PCIE_N_TEST_CONTROL5(n, m)		(PCS_PORT(n, m) + 0x120)
+#define PCIE_N_TEST_CONTROL6(n, m)		(PCS_PORT(n, m) + 0x124)
+#define PCIE_N_TEST_CONTROL7(n, m)		(PCS_PORT(n, m) + 0x128)
+#define PCIE_N_PCS_STATUS(n, m)			(PCS_PORT(n, m) + 0x174)
+#define PCIE_N_DEBUG_BUS_0_STATUS(n, m)		(PCS_PORT(n, m) + 0x198)
+#define PCIE_N_DEBUG_BUS_1_STATUS(n, m)		(PCS_PORT(n, m) + 0x19C)
+#define PCIE_N_DEBUG_BUS_2_STATUS(n, m)		(PCS_PORT(n, m) + 0x1A0)
+#define PCIE_N_DEBUG_BUS_3_STATUS(n, m)		(PCS_PORT(n, m) + 0x1A4)
+#define PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK_MSB(n, m)	(PCS_PORT(n, m) + 0x1A8)
+#define PCIE_N_OSC_DTCT_ACTIONS(n, m)			(PCS_PORT(n, m) + 0x1AC)
+#define PCIE_N_SIGDET_CNTRL(n, m)			(PCS_PORT(n, m) + 0x1B0)
+#define PCIE_N_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB(n, m)	(PCS_PORT(n, m) + 0x1DC)
+#define PCIE_N_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB(n, m)	(PCS_PORT(n, m) + 0x1E0)
+
+#define PCIE_COM_SW_RESET		0x400
+#define PCIE_COM_POWER_DOWN_CONTROL	0x404
+#define PCIE_COM_START_CONTROL		0x408
+#define PCIE_COM_DEBUG_BUS_BYTE0_INDEX	0x438
+#define PCIE_COM_DEBUG_BUS_BYTE1_INDEX	0x43C
+#define PCIE_COM_DEBUG_BUS_BYTE2_INDEX	0x440
+#define PCIE_COM_DEBUG_BUS_BYTE3_INDEX	0x444
+#define PCIE_COM_PCS_READY_STATUS	0x448
+#define PCIE_COM_DEBUG_BUS_0_STATUS	0x45C
+#define PCIE_COM_DEBUG_BUS_1_STATUS	0x460
+#define PCIE_COM_DEBUG_BUS_2_STATUS	0x464
+#define PCIE_COM_DEBUG_BUS_3_STATUS	0x468
+
+#define PCIE20_PARF_SYS_CTRL	     0x00
+#define PCIE20_PARF_PM_STTS		0x24
+#define PCIE20_PARF_PCS_DEEMPH	   0x34
+#define PCIE20_PARF_PCS_SWING	    0x38
+#define PCIE20_PARF_PHY_CTRL	     0x40
+#define PCIE20_PARF_PHY_REFCLK	   0x4C
+#define PCIE20_PARF_CONFIG_BITS	  0x50
+#define PCIE20_PARF_TEST_BUS		0xE4
+#define PCIE20_PARF_MHI_CLOCK_RESET_CTRL	0x174
+#define PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT   0x1A8
+#define PCIE20_PARF_LTSSM              0x1B0
+#define PCIE20_PARF_INT_ALL_STATUS	0x224
+#define PCIE20_PARF_INT_ALL_CLEAR	0x228
+#define PCIE20_PARF_INT_ALL_MASK	0x22C
+#define PCIE20_PARF_SID_OFFSET		0x234
+#define PCIE20_PARF_BDF_TRANSLATE_CFG	0x24C
+#define PCIE20_PARF_BDF_TRANSLATE_N	0x250
+
+#define PCIE20_ELBI_VERSION		0x00
+#define PCIE20_ELBI_SYS_CTRL	     0x04
+#define PCIE20_ELBI_SYS_STTS		 0x08
+
+#define PCIE20_CAP			   0x70
+#define PCIE20_CAP_DEVCTRLSTATUS	(PCIE20_CAP + 0x08)
+#define PCIE20_CAP_LINKCTRLSTATUS	(PCIE20_CAP + 0x10)
+
+#define PCIE20_COMMAND_STATUS	    0x04
+#define PCIE20_HEADER_TYPE		0x0C
+#define PCIE20_BUSNUMBERS		  0x18
+#define PCIE20_MEMORY_BASE_LIMIT	 0x20
+#define PCIE20_BRIDGE_CTRL		0x3C
+#define PCIE20_DEVICE_CONTROL_STATUS	0x78
+#define PCIE20_DEVICE_CONTROL2_STATUS2 0x98
+
+#define PCIE20_AUX_CLK_FREQ_REG		0xB40
+#define PCIE20_ACK_F_ASPM_CTRL_REG     0x70C
+#define PCIE20_ACK_N_FTS		   0xff00
+
+#define PCIE20_PLR_IATU_VIEWPORT	 0x900
+#define PCIE20_PLR_IATU_CTRL1	    0x904
+#define PCIE20_PLR_IATU_CTRL2	    0x908
+#define PCIE20_PLR_IATU_LBAR	     0x90C
+#define PCIE20_PLR_IATU_UBAR	     0x910
+#define PCIE20_PLR_IATU_LAR		0x914
+#define PCIE20_PLR_IATU_LTAR	     0x918
+#define PCIE20_PLR_IATU_UTAR	     0x91c
+
+#define PCIE20_CTRL1_TYPE_CFG0		0x04
+#define PCIE20_CTRL1_TYPE_CFG1		0x05
+
+#define PCIE20_CAP_ID			0x10
+#define L1SUB_CAP_ID			0x1E
+
+#define PCIE_CAP_PTR_OFFSET		0x34
+#define PCIE_EXT_CAP_OFFSET		0x100
+
+#define PCIE20_AER_UNCORR_ERR_STATUS_REG	0x104
+#define PCIE20_AER_CORR_ERR_STATUS_REG		0x110
+#define PCIE20_AER_ROOT_ERR_STATUS_REG		0x130
+#define PCIE20_AER_ERR_SRC_ID_REG		0x134
+
+#define RD 0
+#define WR 1
+#define MSM_PCIE_ERROR -1
+
+#define PERST_PROPAGATION_DELAY_US_MIN	  1000
+#define PERST_PROPAGATION_DELAY_US_MAX	  1005
+#define REFCLK_STABILIZATION_DELAY_US_MIN     1000
+#define REFCLK_STABILIZATION_DELAY_US_MAX     1005
+#define LINK_UP_TIMEOUT_US_MIN		    5000
+#define LINK_UP_TIMEOUT_US_MAX		    5100
+#define LINK_UP_CHECK_MAX_COUNT		   20
+#define PHY_STABILIZATION_DELAY_US_MIN	  995
+#define PHY_STABILIZATION_DELAY_US_MAX	  1005
+#define POWER_DOWN_DELAY_US_MIN		10
+#define POWER_DOWN_DELAY_US_MAX		11
+#define LINKDOWN_INIT_WAITING_US_MIN    995
+#define LINKDOWN_INIT_WAITING_US_MAX    1005
+#define LINKDOWN_WAITING_US_MIN	   4900
+#define LINKDOWN_WAITING_US_MAX	   5100
+#define LINKDOWN_WAITING_COUNT	    200
+
+#define PHY_READY_TIMEOUT_COUNT		   10
+#define XMLH_LINK_UP				  0x400
+#define MAX_LINK_RETRIES 5
+#define MAX_BUS_NUM 3
+#define MAX_PROP_SIZE 32
+#define MAX_RC_NAME_LEN 15
+#define MSM_PCIE_MAX_VREG 4
+#define MSM_PCIE_MAX_CLK 9
+#define MSM_PCIE_MAX_PIPE_CLK 1
+#define MAX_RC_NUM 3
+#define MAX_DEVICE_NUM 20
+#define MAX_SHORT_BDF_NUM 16
+#define PCIE_TLP_RD_SIZE 0x5
+#define PCIE_MSI_NR_IRQS 256
+#define MSM_PCIE_MAX_MSI 32
+#define MAX_MSG_LEN 80
+#define PCIE_LOG_PAGES (50)
+#define PCIE_CONF_SPACE_DW			1024
+#define PCIE_CLEAR				0xDEADBEEF
+#define PCIE_LINK_DOWN				0xFFFFFFFF
+
+#define MSM_PCIE_MAX_RESET 4
+#define MSM_PCIE_MAX_PIPE_RESET 1
+
+#define MSM_PCIE_MSI_PHY 0xa0000000
+#define PCIE20_MSI_CTRL_ADDR		(0x820)
+#define PCIE20_MSI_CTRL_UPPER_ADDR	(0x824)
+#define PCIE20_MSI_CTRL_INTR_EN	   (0x828)
+#define PCIE20_MSI_CTRL_INTR_MASK	 (0x82C)
+#define PCIE20_MSI_CTRL_INTR_STATUS     (0x830)
+#define PCIE20_MSI_CTRL_MAX 8
+
+/* PM control options */
+#define PM_IRQ			 0x1
+#define PM_CLK			 0x2
+#define PM_GPIO			0x4
+#define PM_VREG			0x8
+#define PM_PIPE_CLK		  0x10
+#define PM_ALL (PM_IRQ | PM_CLK | PM_GPIO | PM_VREG | PM_PIPE_CLK)
+
+#ifdef CONFIG_PHYS_ADDR_T_64BIT
+#define PCIE_UPPER_ADDR(addr) ((u32)((addr) >> 32))
+#else
+#define PCIE_UPPER_ADDR(addr) (0x0)
+#endif
+#define PCIE_LOWER_ADDR(addr) ((u32)((addr) & 0xffffffff))
+
+/* Config Space Offsets */
+#define BDF_OFFSET(bus, devfn) \
+	((bus << 24) | (devfn << 16))
+
+#define PCIE_GEN_DBG(x...) do { \
+	if (msm_pcie_debug_mask) \
+		pr_alert(x); \
+	} while (0)
+
+#define PCIE_DBG(dev, fmt, arg...) do {			 \
+	if ((dev) && (dev)->ipc_log_long)   \
+		ipc_log_string((dev)->ipc_log_long, \
+			"DBG1:%s: " fmt, __func__, arg); \
+	if ((dev) && (dev)->ipc_log)   \
+		ipc_log_string((dev)->ipc_log, "%s: " fmt, __func__, arg); \
+	if (msm_pcie_debug_mask)   \
+		pr_alert("%s: " fmt, __func__, arg);		  \
+	} while (0)
+
+#define PCIE_DBG2(dev, fmt, arg...) do {			 \
+	if ((dev) && (dev)->ipc_log)   \
+		ipc_log_string((dev)->ipc_log, "DBG2:%s: " fmt, __func__, arg);\
+	if (msm_pcie_debug_mask)   \
+		pr_alert("%s: " fmt, __func__, arg);              \
+	} while (0)
+
+#define PCIE_DBG3(dev, fmt, arg...) do {			 \
+	if ((dev) && (dev)->ipc_log)   \
+		ipc_log_string((dev)->ipc_log, "DBG3:%s: " fmt, __func__, arg);\
+	if (msm_pcie_debug_mask)   \
+		pr_alert("%s: " fmt, __func__, arg);              \
+	} while (0)
+
+#define PCIE_DUMP(dev, fmt, arg...) do {			\
+	if ((dev) && (dev)->ipc_log_dump) \
+		ipc_log_string((dev)->ipc_log_dump, \
+			"DUMP:%s: " fmt, __func__, arg); \
+	} while (0)
+
+#define PCIE_DBG_FS(dev, fmt, arg...) do {			\
+	if ((dev) && (dev)->ipc_log_dump) \
+		ipc_log_string((dev)->ipc_log_dump, \
+			"DBG_FS:%s: " fmt, __func__, arg); \
+	pr_alert("%s: " fmt, __func__, arg); \
+	} while (0)
+
+#define PCIE_INFO(dev, fmt, arg...) do {			 \
+	if ((dev) && (dev)->ipc_log_long)   \
+		ipc_log_string((dev)->ipc_log_long, \
+			"INFO:%s: " fmt, __func__, arg); \
+	if ((dev) && (dev)->ipc_log)   \
+		ipc_log_string((dev)->ipc_log, "%s: " fmt, __func__, arg); \
+	pr_info("%s: " fmt, __func__, arg);  \
+	} while (0)
+
+#define PCIE_ERR(dev, fmt, arg...) do {			 \
+	if ((dev) && (dev)->ipc_log_long)   \
+		ipc_log_string((dev)->ipc_log_long, \
+			"ERR:%s: " fmt, __func__, arg); \
+	if ((dev) && (dev)->ipc_log)   \
+		ipc_log_string((dev)->ipc_log, "%s: " fmt, __func__, arg); \
+	pr_err("%s: " fmt, __func__, arg);  \
+	} while (0)
+
+
+enum msm_pcie_res {
+	MSM_PCIE_RES_PARF,
+	MSM_PCIE_RES_PHY,
+	MSM_PCIE_RES_DM_CORE,
+	MSM_PCIE_RES_ELBI,
+	MSM_PCIE_RES_CONF,
+	MSM_PCIE_RES_IO,
+	MSM_PCIE_RES_BARS,
+	MSM_PCIE_RES_TCSR,
+	MSM_PCIE_MAX_RES,
+};
+
+enum msm_pcie_irq {
+	MSM_PCIE_INT_MSI,
+	MSM_PCIE_INT_A,
+	MSM_PCIE_INT_B,
+	MSM_PCIE_INT_C,
+	MSM_PCIE_INT_D,
+	MSM_PCIE_INT_PLS_PME,
+	MSM_PCIE_INT_PME_LEGACY,
+	MSM_PCIE_INT_PLS_ERR,
+	MSM_PCIE_INT_AER_LEGACY,
+	MSM_PCIE_INT_LINK_UP,
+	MSM_PCIE_INT_LINK_DOWN,
+	MSM_PCIE_INT_BRIDGE_FLUSH_N,
+	MSM_PCIE_INT_GLOBAL_INT,
+	MSM_PCIE_MAX_IRQ,
+};
+
+enum msm_pcie_irq_event {
+	MSM_PCIE_INT_EVT_LINK_DOWN = 1,
+	MSM_PCIE_INT_EVT_BME,
+	MSM_PCIE_INT_EVT_PM_TURNOFF,
+	MSM_PCIE_INT_EVT_DEBUG,
+	MSM_PCIE_INT_EVT_LTR,
+	MSM_PCIE_INT_EVT_MHI_Q6,
+	MSM_PCIE_INT_EVT_MHI_A7,
+	MSM_PCIE_INT_EVT_DSTATE_CHANGE,
+	MSM_PCIE_INT_EVT_L1SUB_TIMEOUT,
+	MSM_PCIE_INT_EVT_MMIO_WRITE,
+	MSM_PCIE_INT_EVT_CFG_WRITE,
+	MSM_PCIE_INT_EVT_BRIDGE_FLUSH_N,
+	MSM_PCIE_INT_EVT_LINK_UP,
+	MSM_PCIE_INT_EVT_AER_LEGACY,
+	MSM_PCIE_INT_EVT_AER_ERR,
+	MSM_PCIE_INT_EVT_PME_LEGACY,
+	MSM_PCIE_INT_EVT_PLS_PME,
+	MSM_PCIE_INT_EVT_INTD,
+	MSM_PCIE_INT_EVT_INTC,
+	MSM_PCIE_INT_EVT_INTB,
+	MSM_PCIE_INT_EVT_INTA,
+	MSM_PCIE_INT_EVT_EDMA,
+	MSM_PCIE_INT_EVT_MSI_0,
+	MSM_PCIE_INT_EVT_MSI_1,
+	MSM_PCIE_INT_EVT_MSI_2,
+	MSM_PCIE_INT_EVT_MSI_3,
+	MSM_PCIE_INT_EVT_MSI_4,
+	MSM_PCIE_INT_EVT_MSI_5,
+	MSM_PCIE_INT_EVT_MSI_6,
+	MSM_PCIE_INT_EVT_MSI_7,
+	MSM_PCIE_INT_EVT_MAX = 30,
+};
+
+enum msm_pcie_gpio {
+	MSM_PCIE_GPIO_PERST,
+	MSM_PCIE_GPIO_WAKE,
+	MSM_PCIE_GPIO_EP,
+	MSM_PCIE_MAX_GPIO
+};
+
+enum msm_pcie_link_status {
+	MSM_PCIE_LINK_DEINIT,
+	MSM_PCIE_LINK_ENABLED,
+	MSM_PCIE_LINK_DISABLED
+};
+
+/* gpio info structure */
+struct msm_pcie_gpio_info_t {
+	char	*name;
+	uint32_t   num;
+	bool	 out;
+	uint32_t   on;
+	uint32_t   init;
+	bool	required;
+};
+
+/* voltage regulator info structrue */
+struct msm_pcie_vreg_info_t {
+	struct regulator  *hdl;
+	char		  *name;
+	uint32_t	     max_v;
+	uint32_t	     min_v;
+	uint32_t	     opt_mode;
+	bool		   required;
+};
+
+/* reset info structure */
+struct msm_pcie_reset_info_t {
+	struct reset_control *hdl;
+	char *name;
+	bool required;
+};
+
+/* clock info structure */
+struct msm_pcie_clk_info_t {
+	struct clk  *hdl;
+	char	  *name;
+	u32	   freq;
+	bool	config_mem;
+	bool	  required;
+};
+
+/* resource info structure */
+struct msm_pcie_res_info_t {
+	char		*name;
+	struct resource *resource;
+	void __iomem    *base;
+};
+
+/* irq info structrue */
+struct msm_pcie_irq_info_t {
+	char		  *name;
+	uint32_t	    num;
+};
+
+/* phy info structure */
+struct msm_pcie_phy_info_t {
+	u32	offset;
+	u32	val;
+	u32	delay;
+};
+
+/* PCIe device info structure */
+struct msm_pcie_device_info {
+	u32			bdf;
+	struct pci_dev		*dev;
+	short			short_bdf;
+	u32			sid;
+	int			domain;
+	void __iomem		*conf_base;
+	unsigned long		phy_address;
+	u32			dev_ctrlstts_offset;
+	struct msm_pcie_register_event *event_reg;
+	bool			registered;
+};
+
+/* msm pcie device structure */
+struct msm_pcie_dev_t {
+	struct platform_device	 *pdev;
+	struct pci_dev *dev;
+	struct regulator *gdsc;
+	struct regulator *gdsc_smmu;
+	struct msm_pcie_vreg_info_t  vreg[MSM_PCIE_MAX_VREG];
+	struct msm_pcie_gpio_info_t  gpio[MSM_PCIE_MAX_GPIO];
+	struct msm_pcie_clk_info_t   clk[MSM_PCIE_MAX_CLK];
+	struct msm_pcie_clk_info_t   pipeclk[MSM_PCIE_MAX_PIPE_CLK];
+	struct msm_pcie_res_info_t   res[MSM_PCIE_MAX_RES];
+	struct msm_pcie_irq_info_t   irq[MSM_PCIE_MAX_IRQ];
+	struct msm_pcie_irq_info_t	msi[MSM_PCIE_MAX_MSI];
+	struct msm_pcie_reset_info_t reset[MSM_PCIE_MAX_RESET];
+	struct msm_pcie_reset_info_t pipe_reset[MSM_PCIE_MAX_PIPE_RESET];
+
+	void __iomem		     *parf;
+	void __iomem		     *phy;
+	void __iomem		     *elbi;
+	void __iomem		     *dm_core;
+	void __iomem		     *conf;
+	void __iomem		     *bars;
+	void __iomem		     *tcsr;
+
+	uint32_t			    axi_bar_start;
+	uint32_t			    axi_bar_end;
+
+	struct resource		   *dev_mem_res;
+	struct resource		   *dev_io_res;
+
+	uint32_t			    wake_n;
+	uint32_t			    vreg_n;
+	uint32_t			    gpio_n;
+	uint32_t			    parf_deemph;
+	uint32_t			    parf_swing;
+
+	bool				 cfg_access;
+	spinlock_t			 cfg_lock;
+	unsigned long		    irqsave_flags;
+	struct mutex			enumerate_lock;
+	struct mutex		     setup_lock;
+
+	struct irq_domain		*irq_domain;
+	DECLARE_BITMAP(msi_irq_in_use, PCIE_MSI_NR_IRQS);
+	uint32_t			   msi_gicm_addr;
+	uint32_t			   msi_gicm_base;
+	bool				 use_msi;
+
+	enum msm_pcie_link_status    link_status;
+	bool				 user_suspend;
+	bool                         disable_pc;
+	struct pci_saved_state	     *saved_state;
+
+	struct wakeup_source	     ws;
+	struct msm_bus_scale_pdata   *bus_scale_table;
+	uint32_t			   bus_client;
+
+	bool				l0s_supported;
+	bool				l1_supported;
+	bool				 l1ss_supported;
+	bool				common_clk_en;
+	bool				clk_power_manage_en;
+	bool				 aux_clk_sync;
+	bool				aer_enable;
+	bool				smmu_exist;
+	uint32_t			smmu_sid_base;
+	uint32_t			   n_fts;
+	bool				 ext_ref_clk;
+	bool				common_phy;
+	uint32_t			   ep_latency;
+	uint32_t			wr_halt_size;
+	uint32_t			cpl_timeout;
+	uint32_t			current_bdf;
+	short				current_short_bdf;
+	uint32_t			perst_delay_us_min;
+	uint32_t			perst_delay_us_max;
+	uint32_t			tlp_rd_size;
+	bool				linkdown_panic;
+	bool				 ep_wakeirq;
+
+	uint32_t			   rc_idx;
+	uint32_t			phy_ver;
+	bool				drv_ready;
+	bool				 enumerated;
+	struct work_struct	     handle_wake_work;
+	struct mutex		     recovery_lock;
+	spinlock_t                   linkdown_lock;
+	spinlock_t                   wakeup_lock;
+	spinlock_t			global_irq_lock;
+	spinlock_t			aer_lock;
+	ulong				linkdown_counter;
+	ulong				link_turned_on_counter;
+	ulong				link_turned_off_counter;
+	ulong				rc_corr_counter;
+	ulong				rc_non_fatal_counter;
+	ulong				rc_fatal_counter;
+	ulong				ep_corr_counter;
+	ulong				ep_non_fatal_counter;
+	ulong				ep_fatal_counter;
+	bool				 suspending;
+	ulong				wake_counter;
+	u32				num_active_ep;
+	u32				num_ep;
+	bool				pending_ep_reg;
+	u32				phy_len;
+	u32				port_phy_len;
+	struct msm_pcie_phy_info_t	*phy_sequence;
+	struct msm_pcie_phy_info_t	*port_phy_sequence;
+	u32		ep_shadow[MAX_DEVICE_NUM][PCIE_CONF_SPACE_DW];
+	u32				  rc_shadow[PCIE_CONF_SPACE_DW];
+	bool				 shadow_en;
+	bool				bridge_found;
+	struct msm_pcie_register_event *event_reg;
+	unsigned int			scm_dev_id;
+	bool				 power_on;
+	void				 *ipc_log;
+	void				*ipc_log_long;
+	void				*ipc_log_dump;
+	bool				use_19p2mhz_aux_clk;
+	bool				use_pinctrl;
+	struct pinctrl			*pinctrl;
+	struct pinctrl_state		*pins_default;
+	struct pinctrl_state		*pins_sleep;
+	struct msm_pcie_device_info   pcidev_table[MAX_DEVICE_NUM];
+};
+
+
+/* debug mask sys interface */
+static int msm_pcie_debug_mask;
+module_param_named(debug_mask, msm_pcie_debug_mask,
+			    int, 0644);
+
+/* debugfs values */
+static u32 rc_sel;
+static u32 base_sel;
+static u32 wr_offset;
+static u32 wr_mask;
+static u32 wr_value;
+static ulong corr_counter_limit = 5;
+
+/* counter to keep track if common PHY needs to be configured */
+static u32 num_rc_on;
+
+/* global lock for PCIe common PHY */
+static struct mutex com_phy_lock;
+
+/* Table to track info of PCIe devices */
+static struct msm_pcie_device_info
+	msm_pcie_dev_tbl[MAX_RC_NUM * MAX_DEVICE_NUM];
+
+/* PCIe driver state */
+struct pcie_drv_sta {
+	u32 rc_num;
+	struct mutex drv_lock;
+} pcie_drv;
+
+/* msm pcie device data */
+static struct msm_pcie_dev_t msm_pcie_dev[MAX_RC_NUM];
+
+/* regulators */
+static struct msm_pcie_vreg_info_t msm_pcie_vreg_info[MSM_PCIE_MAX_VREG] = {
+	{NULL, "vreg-3.3", 0, 0, 0, false},
+	{NULL, "vreg-1.8", 1800000, 1800000, 14000, true},
+	{NULL, "vreg-0.9", 1000000, 1000000, 40000, true},
+	{NULL, "vreg-cx", 0, 0, 0, false}
+};
+
+/* GPIOs */
+static struct msm_pcie_gpio_info_t msm_pcie_gpio_info[MSM_PCIE_MAX_GPIO] = {
+	{"perst-gpio",		0, 1, 0, 0, 1},
+	{"wake-gpio",		0, 0, 0, 0, 0},
+	{"qcom,ep-gpio",	0, 1, 1, 0, 0}
+};
+
+/* resets */
+static struct msm_pcie_reset_info_t
+msm_pcie_reset_info[MAX_RC_NUM][MSM_PCIE_MAX_RESET] = {
+	{
+		{NULL, "pcie_phy_reset", false},
+		{NULL, "pcie_phy_com_reset", false},
+		{NULL, "pcie_phy_nocsr_com_phy_reset", false},
+		{NULL, "pcie_0_phy_reset", false}
+	},
+	{
+		{NULL, "pcie_phy_reset", false},
+		{NULL, "pcie_phy_com_reset", false},
+		{NULL, "pcie_phy_nocsr_com_phy_reset", false},
+		{NULL, "pcie_1_phy_reset", false}
+	},
+	{
+		{NULL, "pcie_phy_reset", false},
+		{NULL, "pcie_phy_com_reset", false},
+		{NULL, "pcie_phy_nocsr_com_phy_reset", false},
+		{NULL, "pcie_2_phy_reset", false}
+	}
+};
+
+/* pipe reset  */
+static struct msm_pcie_reset_info_t
+msm_pcie_pipe_reset_info[MAX_RC_NUM][MSM_PCIE_MAX_PIPE_RESET] = {
+	{
+		{NULL, "pcie_0_phy_pipe_reset", false}
+	},
+	{
+		{NULL, "pcie_1_phy_pipe_reset", false}
+	},
+	{
+		{NULL, "pcie_2_phy_pipe_reset", false}
+	}
+};
+
+/* clocks */
+static struct msm_pcie_clk_info_t
+	msm_pcie_clk_info[MAX_RC_NUM][MSM_PCIE_MAX_CLK] = {
+	{
+	{NULL, "pcie_0_ref_clk_src", 0, false, false},
+	{NULL, "pcie_0_aux_clk", 1010000, false, true},
+	{NULL, "pcie_0_cfg_ahb_clk", 0, false, true},
+	{NULL, "pcie_0_mstr_axi_clk", 0, true, true},
+	{NULL, "pcie_0_slv_axi_clk", 0, true, true},
+	{NULL, "pcie_0_ldo", 0, false, true},
+	{NULL, "pcie_0_smmu_clk", 0, false, false},
+	{NULL, "pcie_phy_cfg_ahb_clk", 0, false, false},
+	{NULL, "pcie_phy_aux_clk", 0, false, false}
+	},
+	{
+	{NULL, "pcie_1_ref_clk_src", 0, false, false},
+	{NULL, "pcie_1_aux_clk", 1010000, false, true},
+	{NULL, "pcie_1_cfg_ahb_clk", 0, false, true},
+	{NULL, "pcie_1_mstr_axi_clk", 0, true, true},
+	{NULL, "pcie_1_slv_axi_clk", 0, true,  true},
+	{NULL, "pcie_1_ldo", 0, false, true},
+	{NULL, "pcie_1_smmu_clk", 0, false, false},
+	{NULL, "pcie_phy_cfg_ahb_clk", 0, false, false},
+	{NULL, "pcie_phy_aux_clk", 0, false, false}
+	},
+	{
+	{NULL, "pcie_2_ref_clk_src", 0, false, false},
+	{NULL, "pcie_2_aux_clk", 1010000, false, true},
+	{NULL, "pcie_2_cfg_ahb_clk", 0, false, true},
+	{NULL, "pcie_2_mstr_axi_clk", 0, true, true},
+	{NULL, "pcie_2_slv_axi_clk", 0, true, true},
+	{NULL, "pcie_2_ldo", 0, false, true},
+	{NULL, "pcie_2_smmu_clk", 0, false, false},
+	{NULL, "pcie_phy_cfg_ahb_clk", 0, false, false},
+	{NULL, "pcie_phy_aux_clk", 0, false, false}
+	}
+};
+
+/* Pipe Clocks */
+static struct msm_pcie_clk_info_t
+	msm_pcie_pipe_clk_info[MAX_RC_NUM][MSM_PCIE_MAX_PIPE_CLK] = {
+	{
+	{NULL, "pcie_0_pipe_clk", 125000000, true, true},
+	},
+	{
+	{NULL, "pcie_1_pipe_clk", 125000000, true, true},
+	},
+	{
+	{NULL, "pcie_2_pipe_clk", 125000000, true, true},
+	}
+};
+
+/* resources */
+static const struct msm_pcie_res_info_t msm_pcie_res_info[MSM_PCIE_MAX_RES] = {
+	{"parf",	0, 0},
+	{"phy",     0, 0},
+	{"dm_core",	0, 0},
+	{"elbi",	0, 0},
+	{"conf",	0, 0},
+	{"io",		0, 0},
+	{"bars",	0, 0},
+	{"tcsr",	0, 0}
+};
+
+/* irqs */
+static const struct msm_pcie_irq_info_t msm_pcie_irq_info[MSM_PCIE_MAX_IRQ] = {
+	{"int_msi",	0},
+	{"int_a",	0},
+	{"int_b",	0},
+	{"int_c",	0},
+	{"int_d",	0},
+	{"int_pls_pme",		0},
+	{"int_pme_legacy",	0},
+	{"int_pls_err",		0},
+	{"int_aer_legacy",	0},
+	{"int_pls_link_up",	0},
+	{"int_pls_link_down",	0},
+	{"int_bridge_flush_n",	0},
+	{"int_global_int",	0}
+};
+
+/* MSIs */
+static const struct msm_pcie_irq_info_t msm_pcie_msi_info[MSM_PCIE_MAX_MSI] = {
+	{"msi_0", 0}, {"msi_1", 0}, {"msi_2", 0}, {"msi_3", 0},
+	{"msi_4", 0}, {"msi_5", 0}, {"msi_6", 0}, {"msi_7", 0},
+	{"msi_8", 0}, {"msi_9", 0}, {"msi_10", 0}, {"msi_11", 0},
+	{"msi_12", 0}, {"msi_13", 0}, {"msi_14", 0}, {"msi_15", 0},
+	{"msi_16", 0}, {"msi_17", 0}, {"msi_18", 0}, {"msi_19", 0},
+	{"msi_20", 0}, {"msi_21", 0}, {"msi_22", 0}, {"msi_23", 0},
+	{"msi_24", 0}, {"msi_25", 0}, {"msi_26", 0}, {"msi_27", 0},
+	{"msi_28", 0}, {"msi_29", 0}, {"msi_30", 0}, {"msi_31", 0}
+};
+
+#ifdef CONFIG_ARM
+#define PCIE_BUS_PRIV_DATA(bus) \
+	(((struct pci_sys_data *)bus->sysdata)->private_data)
+
+static struct pci_sys_data msm_pcie_sys_data[MAX_RC_NUM];
+
+static inline void *msm_pcie_setup_sys_data(struct msm_pcie_dev_t *dev)
+{
+	msm_pcie_sys_data[dev->rc_idx].domain = dev->rc_idx;
+	msm_pcie_sys_data[dev->rc_idx].private_data = dev;
+
+	return &msm_pcie_sys_data[dev->rc_idx];
+}
+
+static inline void msm_pcie_fixup_irqs(struct msm_pcie_dev_t *dev)
+{
+	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
+}
+#else
+#define PCIE_BUS_PRIV_DATA(bus) \
+	(struct msm_pcie_dev_t *)(bus->sysdata)
+
+static inline void *msm_pcie_setup_sys_data(struct msm_pcie_dev_t *dev)
+{
+	return dev;
+}
+
+static inline void msm_pcie_fixup_irqs(struct msm_pcie_dev_t *dev)
+{
+}
+#endif
+
+static inline void msm_pcie_write_reg(void *base, u32 offset, u32 value)
+{
+	writel_relaxed(value, base + offset);
+	/* ensure that changes propagated to the hardware */
+	wmb();
+}
+
+static inline void msm_pcie_write_reg_field(void *base, u32 offset,
+	const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = readl_relaxed(base + offset);
+
+	tmp &= ~mask; /* clear written bits */
+	val = tmp | (val << shift);
+	writel_relaxed(val, base + offset);
+	/* ensure that changes propagated to the hardware */
+	wmb();
+}
+
+static inline void msm_pcie_config_clock_mem(struct msm_pcie_dev_t *dev,
+	struct msm_pcie_clk_info_t *info)
+{
+	int ret;
+
+	ret = clk_set_flags(info->hdl, CLKFLAG_NORETAIN_MEM);
+	if (ret)
+		PCIE_ERR(dev,
+			"PCIe: RC%d can't configure core memory for clk %s: %d.\n",
+			dev->rc_idx, info->name, ret);
+	else
+		PCIE_DBG2(dev,
+			"PCIe: RC%d configured core memory for clk %s.\n",
+			dev->rc_idx, info->name);
+
+	ret = clk_set_flags(info->hdl, CLKFLAG_NORETAIN_PERIPH);
+	if (ret)
+		PCIE_ERR(dev,
+			"PCIe: RC%d can't configure peripheral memory for clk %s: %d.\n",
+			dev->rc_idx, info->name, ret);
+	else
+		PCIE_DBG2(dev,
+			"PCIe: RC%d configured peripheral memory for clk %s.\n",
+			dev->rc_idx, info->name);
+}
+
+#if defined(CONFIG_ARCH_FSM9010)
+#define PCIE20_PARF_PHY_STTS         0x3c
+#define PCIE2_PHY_RESET_CTRL         0x44
+#define PCIE20_PARF_PHY_REFCLK_CTRL2 0xa0
+#define PCIE20_PARF_PHY_REFCLK_CTRL3 0xa4
+#define PCIE20_PARF_PCS_SWING_CTRL1  0x88
+#define PCIE20_PARF_PCS_SWING_CTRL2  0x8c
+#define PCIE20_PARF_PCS_DEEMPH1      0x74
+#define PCIE20_PARF_PCS_DEEMPH2      0x78
+#define PCIE20_PARF_PCS_DEEMPH3      0x7c
+#define PCIE20_PARF_CONFIGBITS       0x84
+#define PCIE20_PARF_PHY_CTRL3        0x94
+#define PCIE20_PARF_PCS_CTRL         0x80
+
+#define TX_AMP_VAL                   127
+#define PHY_RX0_EQ_GEN1_VAL          0
+#define PHY_RX0_EQ_GEN2_VAL          4
+#define TX_DEEMPH_GEN1_VAL           24
+#define TX_DEEMPH_GEN2_3_5DB_VAL     24
+#define TX_DEEMPH_GEN2_6DB_VAL       34
+#define PHY_TX0_TERM_OFFST_VAL       0
+
+static inline void pcie_phy_dump(struct msm_pcie_dev_t *dev)
+{
+}
+
+static inline void pcie20_phy_reset(struct msm_pcie_dev_t *dev, uint32_t assert)
+{
+	msm_pcie_write_reg_field(dev->phy, PCIE2_PHY_RESET_CTRL,
+					 BIT(0), (assert) ? 1 : 0);
+}
+
+static void pcie_phy_init(struct msm_pcie_dev_t *dev)
+{
+	PCIE_DBG(dev, "RC%d: Initializing 28LP SNS phy - 100MHz\n",
+		dev->rc_idx);
+
+	/* De-assert Phy SW Reset */
+	pcie20_phy_reset(dev, 1);
+
+	/* Program SSP ENABLE */
+	if (readl_relaxed(dev->phy + PCIE20_PARF_PHY_REFCLK_CTRL2) & BIT(0))
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PHY_REFCLK_CTRL2,
+								 BIT(0), 0);
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PHY_REFCLK_CTRL3) &
+								 BIT(0)) == 0)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PHY_REFCLK_CTRL3,
+								 BIT(0), 1);
+	/* Program Tx Amplitude */
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PCS_SWING_CTRL1) &
+		(BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0))) !=
+				TX_AMP_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PCS_SWING_CTRL1,
+			BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
+				TX_AMP_VAL);
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PCS_SWING_CTRL2) &
+		(BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0))) !=
+				TX_AMP_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PCS_SWING_CTRL2,
+			BIT(6)|BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
+				TX_AMP_VAL);
+	/* Program De-Emphasis */
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PCS_DEEMPH1) &
+			(BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0))) !=
+				TX_DEEMPH_GEN2_6DB_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PCS_DEEMPH1,
+			BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
+				TX_DEEMPH_GEN2_6DB_VAL);
+
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PCS_DEEMPH2) &
+			(BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0))) !=
+				TX_DEEMPH_GEN2_3_5DB_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PCS_DEEMPH2,
+			BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
+				TX_DEEMPH_GEN2_3_5DB_VAL);
+
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PCS_DEEMPH3) &
+			(BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0))) !=
+				TX_DEEMPH_GEN1_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PCS_DEEMPH3,
+			BIT(5)|BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
+				TX_DEEMPH_GEN1_VAL);
+
+	/* Program Rx_Eq */
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_CONFIGBITS) &
+			(BIT(2)|BIT(1)|BIT(0))) != PHY_RX0_EQ_GEN1_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_CONFIGBITS,
+				 BIT(2)|BIT(1)|BIT(0), PHY_RX0_EQ_GEN1_VAL);
+
+	/* Program Tx0_term_offset */
+	if ((readl_relaxed(dev->phy + PCIE20_PARF_PHY_CTRL3) &
+			(BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0))) !=
+				PHY_TX0_TERM_OFFST_VAL)
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PHY_CTRL3,
+			 BIT(4)|BIT(3)|BIT(2)|BIT(1)|BIT(0),
+				PHY_TX0_TERM_OFFST_VAL);
+
+	/* Program REF_CLK source */
+	msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PHY_REFCLK_CTRL2, BIT(1),
+		(dev->ext_ref_clk) ? 1 : 0);
+	/* disable Tx2Rx Loopback */
+	if (readl_relaxed(dev->phy + PCIE20_PARF_PCS_CTRL) & BIT(1))
+		msm_pcie_write_reg_field(dev->phy, PCIE20_PARF_PCS_CTRL,
+								 BIT(1), 0);
+	/* De-assert Phy SW Reset */
+	pcie20_phy_reset(dev, 0);
+}
+
+static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
+{
+
+	/* read PCIE20_PARF_PHY_STTS twice */
+	readl_relaxed(dev->phy + PCIE20_PARF_PHY_STTS);
+	if (readl_relaxed(dev->phy + PCIE20_PARF_PHY_STTS) & BIT(0))
+		return false;
+	else
+		return true;
+}
+#else
+static void pcie_phy_dump_test_cntrl(struct msm_pcie_dev_t *dev,
+					u32 cntrl4_val, u32 cntrl5_val,
+					u32 cntrl6_val, u32 cntrl7_val)
+{
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_TEST_CONTROL4(dev->rc_idx, dev->common_phy), cntrl4_val);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_TEST_CONTROL5(dev->rc_idx, dev->common_phy), cntrl5_val);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_TEST_CONTROL6(dev->rc_idx, dev->common_phy), cntrl6_val);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_TEST_CONTROL7(dev->rc_idx, dev->common_phy), cntrl7_val);
+
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_TEST_CONTROL4: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_TEST_CONTROL4(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_TEST_CONTROL5: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_TEST_CONTROL5(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_TEST_CONTROL6: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_TEST_CONTROL6(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_TEST_CONTROL7: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_TEST_CONTROL7(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_DEBUG_BUS_0_STATUS: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_DEBUG_BUS_0_STATUS(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_DEBUG_BUS_1_STATUS: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_DEBUG_BUS_1_STATUS(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_DEBUG_BUS_2_STATUS: 0x%x\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_DEBUG_BUS_2_STATUS(dev->rc_idx,
+				dev->common_phy)));
+	PCIE_DUMP(dev,
+		"PCIe: RC%d PCIE_N_DEBUG_BUS_3_STATUS: 0x%x\n\n", dev->rc_idx,
+		readl_relaxed(dev->phy +
+			PCIE_N_DEBUG_BUS_3_STATUS(dev->rc_idx,
+				dev->common_phy)));
+}
+
+static void pcie_phy_dump(struct msm_pcie_dev_t *dev)
+{
+	int i, size;
+	u32 write_val;
+
+	if (dev->phy_ver >= 0x20) {
+		PCIE_DUMP(dev, "PCIe: RC%d PHY dump is not supported\n",
+			dev->rc_idx);
+		return;
+	}
+
+	PCIE_DUMP(dev, "PCIe: RC%d PHY testbus\n", dev->rc_idx);
+
+	pcie_phy_dump_test_cntrl(dev, 0x18, 0x19, 0x1A, 0x1B);
+	pcie_phy_dump_test_cntrl(dev, 0x1C, 0x1D, 0x1E, 0x1F);
+	pcie_phy_dump_test_cntrl(dev, 0x20, 0x21, 0x22, 0x23);
+
+	for (i = 0; i < 3; i++) {
+		write_val = 0x1 + i;
+		msm_pcie_write_reg(dev->phy,
+			QSERDES_TX_N_DEBUG_BUS_SEL(dev->rc_idx,
+				dev->common_phy), write_val);
+		PCIE_DUMP(dev,
+			"PCIe: RC%d QSERDES_TX_N_DEBUG_BUS_SEL: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				QSERDES_TX_N_DEBUG_BUS_SEL(dev->rc_idx,
+					dev->common_phy)));
+
+		pcie_phy_dump_test_cntrl(dev, 0x30, 0x31, 0x32, 0x33);
+	}
+
+	pcie_phy_dump_test_cntrl(dev, 0, 0, 0, 0);
+
+	if (dev->phy_ver >= 0x10 && dev->phy_ver < 0x20) {
+		pcie_phy_dump_test_cntrl(dev, 0x01, 0x02, 0x03, 0x0A);
+		pcie_phy_dump_test_cntrl(dev, 0x0E, 0x0F, 0x12, 0x13);
+		pcie_phy_dump_test_cntrl(dev, 0, 0, 0, 0);
+
+		for (i = 0; i < 8; i += 4) {
+			write_val = 0x1 + i;
+			msm_pcie_write_reg(dev->phy,
+				PCIE_MISC_N_DEBUG_BUS_BYTE0_INDEX(dev->rc_idx,
+					dev->common_phy), write_val);
+			msm_pcie_write_reg(dev->phy,
+				PCIE_MISC_N_DEBUG_BUS_BYTE1_INDEX(dev->rc_idx,
+					dev->common_phy), write_val + 1);
+			msm_pcie_write_reg(dev->phy,
+				PCIE_MISC_N_DEBUG_BUS_BYTE2_INDEX(dev->rc_idx,
+					dev->common_phy), write_val + 2);
+			msm_pcie_write_reg(dev->phy,
+				PCIE_MISC_N_DEBUG_BUS_BYTE3_INDEX(dev->rc_idx,
+					dev->common_phy), write_val + 3);
+
+			PCIE_DUMP(dev,
+				"PCIe: RC%d to PCIE_MISC_N_DEBUG_BUS_BYTE0_INDEX: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_BYTE0_INDEX(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d to PCIE_MISC_N_DEBUG_BUS_BYTE1_INDEX: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_BYTE1_INDEX(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d to PCIE_MISC_N_DEBUG_BUS_BYTE2_INDEX: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_BYTE2_INDEX(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d to PCIE_MISC_N_DEBUG_BUS_BYTE3_INDEX: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_BYTE3_INDEX(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d PCIE_MISC_N_DEBUG_BUS_0_STATUS: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_0_STATUS(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d PCIE_MISC_N_DEBUG_BUS_1_STATUS: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_1_STATUS(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d PCIE_MISC_N_DEBUG_BUS_2_STATUS: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_2_STATUS(
+						dev->rc_idx, dev->common_phy)));
+			PCIE_DUMP(dev,
+				"PCIe: RC%d PCIE_MISC_N_DEBUG_BUS_3_STATUS: 0x%x\n",
+				dev->rc_idx,
+				readl_relaxed(dev->phy +
+					PCIE_MISC_N_DEBUG_BUS_3_STATUS(
+						dev->rc_idx, dev->common_phy)));
+		}
+
+		msm_pcie_write_reg(dev->phy,
+			PCIE_MISC_N_DEBUG_BUS_BYTE0_INDEX(
+				dev->rc_idx, dev->common_phy), 0);
+		msm_pcie_write_reg(dev->phy,
+			PCIE_MISC_N_DEBUG_BUS_BYTE1_INDEX(
+				dev->rc_idx, dev->common_phy), 0);
+		msm_pcie_write_reg(dev->phy,
+			PCIE_MISC_N_DEBUG_BUS_BYTE2_INDEX(
+				dev->rc_idx, dev->common_phy), 0);
+		msm_pcie_write_reg(dev->phy,
+			PCIE_MISC_N_DEBUG_BUS_BYTE3_INDEX(
+				dev->rc_idx, dev->common_phy), 0);
+	}
+
+	for (i = 0; i < 2; i++) {
+		write_val = 0x2 + i;
+
+		msm_pcie_write_reg(dev->phy, QSERDES_COM_DEBUG_BUS_SEL,
+			write_val);
+
+		PCIE_DUMP(dev,
+			"PCIe: RC%d to QSERDES_COM_DEBUG_BUS_SEL: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS_SEL));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d QSERDES_COM_DEBUG_BUS0: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS0));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d QSERDES_COM_DEBUG_BUS1: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS1));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d QSERDES_COM_DEBUG_BUS2: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS2));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d QSERDES_COM_DEBUG_BUS3: 0x%x\n\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy + QSERDES_COM_DEBUG_BUS3));
+	}
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DEBUG_BUS_SEL, 0);
+
+	if (dev->common_phy) {
+		msm_pcie_write_reg(dev->phy, PCIE_COM_DEBUG_BUS_BYTE0_INDEX,
+			0x01);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_DEBUG_BUS_BYTE1_INDEX,
+			0x02);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_DEBUG_BUS_BYTE2_INDEX,
+			0x03);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_DEBUG_BUS_BYTE3_INDEX,
+			0x04);
+
+		PCIE_DUMP(dev,
+			"PCIe: RC%d to PCIE_COM_DEBUG_BUS_BYTE0_INDEX: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_BYTE0_INDEX));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d to PCIE_COM_DEBUG_BUS_BYTE1_INDEX: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_BYTE1_INDEX));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d to PCIE_COM_DEBUG_BUS_BYTE2_INDEX: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_BYTE2_INDEX));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d to PCIE_COM_DEBUG_BUS_BYTE3_INDEX: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_BYTE3_INDEX));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d PCIE_COM_DEBUG_BUS_0_STATUS: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_0_STATUS));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d PCIE_COM_DEBUG_BUS_1_STATUS: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_1_STATUS));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d PCIE_COM_DEBUG_BUS_2_STATUS: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_2_STATUS));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d PCIE_COM_DEBUG_BUS_3_STATUS: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_3_STATUS));
+
+		msm_pcie_write_reg(dev->phy, PCIE_COM_DEBUG_BUS_BYTE0_INDEX,
+			0x05);
+
+		PCIE_DUMP(dev,
+			"PCIe: RC%d to PCIE_COM_DEBUG_BUS_BYTE0_INDEX: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_BYTE0_INDEX));
+		PCIE_DUMP(dev,
+			"PCIe: RC%d PCIE_COM_DEBUG_BUS_0_STATUS: 0x%x\n\n",
+			dev->rc_idx,
+			readl_relaxed(dev->phy +
+				PCIE_COM_DEBUG_BUS_0_STATUS));
+	}
+
+	size = resource_size(dev->res[MSM_PCIE_RES_PHY].resource);
+	for (i = 0; i < size; i += 32) {
+		PCIE_DUMP(dev,
+			"PCIe PHY of RC%d: 0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+			dev->rc_idx, i,
+			readl_relaxed(dev->phy + i),
+			readl_relaxed(dev->phy + (i + 4)),
+			readl_relaxed(dev->phy + (i + 8)),
+			readl_relaxed(dev->phy + (i + 12)),
+			readl_relaxed(dev->phy + (i + 16)),
+			readl_relaxed(dev->phy + (i + 20)),
+			readl_relaxed(dev->phy + (i + 24)),
+			readl_relaxed(dev->phy + (i + 28)));
+	}
+}
+
+#ifdef CONFIG_ARCH_MDMCALIFORNIUM
+static void pcie_phy_init(struct msm_pcie_dev_t *dev)
+{
+	u8 common_phy;
+
+	PCIE_DBG(dev,
+		"RC%d: Initializing MDM 14nm QMP phy - 19.2MHz with Common Mode Clock (SSC ON)\n",
+		dev->rc_idx);
+
+	if (dev->common_phy)
+		common_phy = 1;
+	else
+		common_phy = 0;
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_SW_RESET(dev->rc_idx, common_phy),
+		0x01);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx, common_phy),
+		0x03);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x18);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_ENABLE1, 0x10);
+
+	msm_pcie_write_reg(dev->phy,
+			QSERDES_TX_N_LANE_MODE(dev->rc_idx, common_phy), 0x06);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP_EN, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER1, 0xFF);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER2, 0x1F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_BG_TRIM, 0x0F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_PLL_IVCO, 0x0F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_HSCLK_SEL, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CORE_CLK_EN, 0x20);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CORECLK_DIV, 0x0A);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_BG_TIMER, 0x09);
+
+	if (dev->tcsr) {
+		PCIE_DBG(dev, "RC%d: TCSR PHY clock scheme is 0x%x\n",
+			dev->rc_idx, readl_relaxed(dev->tcsr));
+
+		if (readl_relaxed(dev->tcsr) & (BIT(1) | BIT(0)))
+			msm_pcie_write_reg(dev->phy,
+					QSERDES_COM_SYSCLK_EN_SEL, 0x0A);
+		else
+			msm_pcie_write_reg(dev->phy,
+					QSERDES_COM_SYSCLK_EN_SEL, 0x04);
+	}
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DEC_START_MODE0, 0x82);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP3_MODE0, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x0D);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x04);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_SELECT, 0x33);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CP_CTRL_MODE0, 0x0B);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CCTRL_MODE0, 0x28);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_EN_CENTER, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_PER1, 0x31);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_PER2, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_ADJ_PER1, 0x02);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_ADJ_PER2, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_STEP_SIZE1, 0x2f);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_STEP_SIZE2, 0x19);
+
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_TX_N_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN(dev->rc_idx,
+		common_phy), 0x45);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CMN_CONFIG, 0x06);
+
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_TX_N_RES_CODE_LANE_OFFSET(dev->rc_idx, common_phy),
+		0x02);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_TX_N_RCV_DETECT_LVL_2(dev->rc_idx, common_phy),
+		0x12);
+
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_SIGDET_ENABLES(dev->rc_idx, common_phy),
+		0x1C);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_SIGDET_DEGLITCH_CNTRL(dev->rc_idx, common_phy),
+		0x14);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL2(dev->rc_idx, common_phy),
+		0x01);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL3(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL4(dev->rc_idx, common_phy),
+		0xDB);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_UCDR_SO_SATURATION_AND_ENABLE(dev->rc_idx,
+		common_phy),
+		0x4B);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_UCDR_SO_GAIN(dev->rc_idx, common_phy),
+		0x04);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_UCDR_SO_GAIN_HALF(dev->rc_idx, common_phy),
+		0x04);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_EP_DIV, 0x19);
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_ENDPOINT_REFCLK_DRIVE(dev->rc_idx, common_phy),
+		0x04);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_OSC_DTCT_ACTIONS(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_PWRUP_RESET_DLY_TIME_AUXCLK(dev->rc_idx, common_phy),
+		0x40);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB(dev->rc_idx, common_phy),
+		0x40);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK_MSB(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK(dev->rc_idx, common_phy),
+		0x40);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_PLL_LOCK_CHK_DLY_TIME(dev->rc_idx, common_phy),
+		0x73);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_SIGDET_LVL(dev->rc_idx, common_phy),
+		0x99);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_TXDEEMPH_M6DB_V0(dev->rc_idx, common_phy),
+		0x15);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_TXDEEMPH_M3P5DB_V0(dev->rc_idx, common_phy),
+		0x0E);
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_SIGDET_CNTRL(dev->rc_idx, common_phy),
+		0x07);
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_SW_RESET(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_START_CONTROL(dev->rc_idx, common_phy),
+		0x03);
+}
+
+static void pcie_pcs_port_phy_init(struct msm_pcie_dev_t *dev)
+{
+}
+
+static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
+{
+	if (readl_relaxed(dev->phy +
+		PCIE_N_PCS_STATUS(dev->rc_idx, dev->common_phy)) & BIT(6))
+		return false;
+	else
+		return true;
+}
+#else
+static void pcie_phy_init(struct msm_pcie_dev_t *dev)
+{
+	int i;
+	struct msm_pcie_phy_info_t *phy_seq;
+
+	PCIE_DBG(dev,
+		"RC%d: Initializing 14nm QMP phy - 19.2MHz with Common Mode Clock (SSC ON)\n",
+		dev->rc_idx);
+
+	if (dev->phy_sequence) {
+		i =  dev->phy_len;
+		phy_seq = dev->phy_sequence;
+		while (i--) {
+			msm_pcie_write_reg(dev->phy,
+				phy_seq->offset,
+				phy_seq->val);
+			if (phy_seq->delay)
+				usleep_range(phy_seq->delay,
+					phy_seq->delay + 1);
+			phy_seq++;
+		}
+		return;
+	}
+
+	if (dev->common_phy)
+		msm_pcie_write_reg(dev->phy, PCIE_COM_POWER_DOWN_CONTROL, 0x01);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1C);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_ENABLE1, 0x10);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_SELECT, 0x33);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CMN_CONFIG, 0x06);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP_EN, 0x42);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_MAP, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER1, 0xFF);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_VCO_TUNE_TIMER2, 0x1F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_HSCLK_SEL, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SVS_MODE_CLK_SEL, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CORE_CLK_EN, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CORECLK_DIV, 0x0A);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_BG_TIMER, 0x09);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DEC_START_MODE0, 0x82);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP3_MODE0, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP2_MODE0, 0x1A);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_LOCK_CMP1_MODE0, 0x0A);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_SELECT, 0x33);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SYS_CLK_CTRL, 0x02);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_BUF_ENABLE, 0x1F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SYSCLK_EN_SEL, 0x04);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CP_CTRL_MODE0, 0x0B);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_PLL_RCTRL_MODE0, 0x16);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_PLL_CCTRL_MODE0, 0x28);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_EN_CENTER, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_PER1, 0x31);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_PER2, 0x01);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_ADJ_PER1, 0x02);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_ADJ_PER2, 0x00);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_STEP_SIZE1, 0x2f);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_SSC_STEP_SIZE2, 0x19);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_RESCODE_DIV_NUM, 0x15);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_BG_TRIM, 0x0F);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_PLL_IVCO, 0x0F);
+
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_EP_DIV, 0x19);
+	msm_pcie_write_reg(dev->phy, QSERDES_COM_CLK_ENABLE1, 0x10);
+
+	if (dev->phy_ver == 0x3) {
+		msm_pcie_write_reg(dev->phy, QSERDES_COM_HSCLK_SEL, 0x00);
+		msm_pcie_write_reg(dev->phy, QSERDES_COM_RESCODE_DIV_NUM, 0x40);
+	}
+
+	if (dev->common_phy) {
+		msm_pcie_write_reg(dev->phy, PCIE_COM_SW_RESET, 0x00);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_START_CONTROL, 0x03);
+	}
+}
+
+static void pcie_pcs_port_phy_init(struct msm_pcie_dev_t *dev)
+{
+	int i;
+	struct msm_pcie_phy_info_t *phy_seq;
+	u8 common_phy;
+
+	if (dev->phy_ver >= 0x20)
+		return;
+
+	PCIE_DBG(dev, "RC%d: Initializing PCIe PHY Port\n", dev->rc_idx);
+
+	if (dev->common_phy)
+		common_phy = 1;
+	else
+		common_phy = 0;
+
+	if (dev->port_phy_sequence) {
+		i =  dev->port_phy_len;
+		phy_seq = dev->port_phy_sequence;
+		while (i--) {
+			msm_pcie_write_reg(dev->phy,
+				phy_seq->offset,
+				phy_seq->val);
+			if (phy_seq->delay)
+				usleep_range(phy_seq->delay,
+					phy_seq->delay + 1);
+			phy_seq++;
+		}
+		return;
+	}
+
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_TX_N_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN(dev->rc_idx,
+		common_phy), 0x45);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_TX_N_LANE_MODE(dev->rc_idx, common_phy),
+		0x06);
+
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_SIGDET_ENABLES(dev->rc_idx, common_phy),
+		0x1C);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_SIGDET_LVL(dev->rc_idx, common_phy),
+		0x17);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL2(dev->rc_idx, common_phy),
+		0x01);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL3(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL4(dev->rc_idx, common_phy),
+		0xDB);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_RX_BAND(dev->rc_idx, common_phy),
+		0x18);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_UCDR_SO_GAIN(dev->rc_idx, common_phy),
+		0x04);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_UCDR_SO_GAIN_HALF(dev->rc_idx, common_phy),
+		0x04);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_RX_IDLE_DTCT_CNTRL(dev->rc_idx, common_phy),
+		0x4C);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_PWRUP_RESET_DLY_TIME_AUXCLK(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK(dev->rc_idx, common_phy),
+		0x01);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_PLL_LOCK_CHK_DLY_TIME(dev->rc_idx, common_phy),
+		0x05);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_UCDR_SO_SATURATION_AND_ENABLE(dev->rc_idx,
+		common_phy), 0x4B);
+	msm_pcie_write_reg(dev->phy,
+		QSERDES_RX_N_SIGDET_DEGLITCH_CNTRL(dev->rc_idx, common_phy),
+		0x14);
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_ENDPOINT_REFCLK_DRIVE(dev->rc_idx, common_phy),
+		0x05);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx, common_phy),
+		0x02);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_STATE_CONFIG4(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_STATE_CONFIG1(dev->rc_idx, common_phy),
+		0xA3);
+
+	if (dev->phy_ver == 0x3) {
+		msm_pcie_write_reg(dev->phy,
+			QSERDES_RX_N_SIGDET_LVL(dev->rc_idx, common_phy),
+			0x19);
+
+		msm_pcie_write_reg(dev->phy,
+			PCIE_N_TXDEEMPH_M3P5DB_V0(dev->rc_idx, common_phy),
+			0x0E);
+	}
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx, common_phy),
+		0x03);
+	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_SW_RESET(dev->rc_idx, common_phy),
+		0x00);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_START_CONTROL(dev->rc_idx, common_phy),
+		0x0A);
+}
+
+static bool pcie_phy_is_ready(struct msm_pcie_dev_t *dev)
+{
+	if (dev->phy_ver >= 0x20) {
+		if (readl_relaxed(dev->phy +
+			PCIE_N_PCS_STATUS(dev->rc_idx, dev->common_phy)) &
+					BIT(6))
+			return false;
+		else
+			return true;
+	}
+
+	if (!(readl_relaxed(dev->phy + PCIE_COM_PCS_READY_STATUS) & 0x1))
+		return false;
+	else
+		return true;
+}
+#endif
+#endif
+
+static int msm_pcie_restore_sec_config(struct msm_pcie_dev_t *dev)
+{
+	int ret, scm_ret;
+
+	if (!dev) {
+		pr_err("PCIe: the input pcie dev is NULL.\n");
+		return -ENODEV;
+	}
+
+	ret = scm_restore_sec_cfg(dev->scm_dev_id, 0, &scm_ret);
+	if (ret || scm_ret) {
+		PCIE_ERR(dev,
+			"PCIe: RC%d failed(%d) to restore sec config, scm_ret=%d\n",
+			dev->rc_idx, ret, scm_ret);
+		return ret ? ret : -EINVAL;
+	}
+
+	return 0;
+}
+
+static inline int msm_pcie_check_align(struct msm_pcie_dev_t *dev,
+						u32 offset)
+{
+	if (offset % 4) {
+		PCIE_ERR(dev,
+			"PCIe: RC%d: offset 0x%x is not correctly aligned\n",
+			dev->rc_idx, offset);
+		return MSM_PCIE_ERROR;
+	}
+
+	return 0;
+}
+
+static bool msm_pcie_confirm_linkup(struct msm_pcie_dev_t *dev,
+						bool check_sw_stts,
+						bool check_ep,
+						void __iomem *ep_conf)
+{
+	u32 val;
+
+	if (check_sw_stts && (dev->link_status != MSM_PCIE_LINK_ENABLED)) {
+		PCIE_DBG(dev, "PCIe: The link of RC %d is not enabled.\n",
+			dev->rc_idx);
+		return false;
+	}
+
+	if (!(readl_relaxed(dev->dm_core + 0x80) & BIT(29))) {
+		PCIE_DBG(dev, "PCIe: The link of RC %d is not up.\n",
+			dev->rc_idx);
+		return false;
+	}
+
+	val = readl_relaxed(dev->dm_core);
+	PCIE_DBG(dev, "PCIe: device ID and vender ID of RC %d are 0x%x.\n",
+		dev->rc_idx, val);
+	if (val == PCIE_LINK_DOWN) {
+		PCIE_ERR(dev,
+			"PCIe: The link of RC %d is not really up; device ID and vender ID of RC %d are 0x%x.\n",
+			dev->rc_idx, dev->rc_idx, val);
+		return false;
+	}
+
+	if (check_ep) {
+		val = readl_relaxed(ep_conf);
+		PCIE_DBG(dev,
+			"PCIe: device ID and vender ID of EP of RC %d are 0x%x.\n",
+			dev->rc_idx, val);
+		if (val == PCIE_LINK_DOWN) {
+			PCIE_ERR(dev,
+				"PCIe: The link of RC %d is not really up; device ID and vender ID of EP of RC %d are 0x%x.\n",
+				dev->rc_idx, dev->rc_idx, val);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static void msm_pcie_cfg_recover(struct msm_pcie_dev_t *dev, bool rc)
+{
+	int i, j;
+	u32 val = 0;
+	u32 *shadow;
+	void *cfg = dev->conf;
+
+	for (i = 0; i < MAX_DEVICE_NUM; i++) {
+		if (!rc && !dev->pcidev_table[i].bdf)
+			break;
+		if (rc) {
+			cfg = dev->dm_core;
+			shadow = dev->rc_shadow;
+		} else {
+			if (!msm_pcie_confirm_linkup(dev, false, true,
+				dev->pcidev_table[i].conf_base))
+				continue;
+
+			shadow = dev->ep_shadow[i];
+			PCIE_DBG(dev,
+				"PCIe Device: %02x:%02x.%01x\n",
+				dev->pcidev_table[i].bdf >> 24,
+				dev->pcidev_table[i].bdf >> 19 & 0x1f,
+				dev->pcidev_table[i].bdf >> 16 & 0x07);
+		}
+		for (j = PCIE_CONF_SPACE_DW - 1; j >= 0; j--) {
+			val = shadow[j];
+			if (val != PCIE_CLEAR) {
+				PCIE_DBG3(dev,
+					"PCIe: before recovery:cfg 0x%x:0x%x\n",
+					j * 4, readl_relaxed(cfg + j * 4));
+				PCIE_DBG3(dev,
+					"PCIe: shadow_dw[%d]:cfg 0x%x:0x%x\n",
+					j, j * 4, val);
+				writel_relaxed(val, cfg + j * 4);
+				/* ensure changes propagated to the hardware */
+				wmb();
+				PCIE_DBG3(dev,
+					"PCIe: after recovery:cfg 0x%x:0x%x\n\n",
+					j * 4, readl_relaxed(cfg + j * 4));
+			}
+		}
+		if (rc)
+			break;
+
+		pci_save_state(dev->pcidev_table[i].dev);
+		cfg += SZ_4K;
+	}
+}
+
+static void msm_pcie_write_mask(void __iomem *addr,
+				uint32_t clear_mask, uint32_t set_mask)
+{
+	uint32_t val;
+
+	val = (readl_relaxed(addr) & ~clear_mask) | set_mask;
+	writel_relaxed(val, addr);
+	wmb();  /* ensure data is written to hardware register */
+}
+
+static void pcie_parf_dump(struct msm_pcie_dev_t *dev)
+{
+	int i, size;
+	u32 original;
+
+	PCIE_DUMP(dev, "PCIe: RC%d PARF testbus\n", dev->rc_idx);
+
+	original = readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL);
+	for (i = 1; i <= 0x1A; i++) {
+		msm_pcie_write_mask(dev->parf + PCIE20_PARF_SYS_CTRL,
+				0xFF0000, i << 16);
+		PCIE_DUMP(dev,
+			"RC%d: PARF_SYS_CTRL: 0%08x PARF_TEST_BUS: 0%08x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->parf + PCIE20_PARF_SYS_CTRL),
+			readl_relaxed(dev->parf + PCIE20_PARF_TEST_BUS));
+	}
+	writel_relaxed(original, dev->parf + PCIE20_PARF_SYS_CTRL);
+
+	PCIE_DUMP(dev, "PCIe: RC%d PARF register dump\n", dev->rc_idx);
+
+	size = resource_size(dev->res[MSM_PCIE_RES_PARF].resource);
+	for (i = 0; i < size; i += 32) {
+		PCIE_DUMP(dev,
+			"RC%d: 0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+			dev->rc_idx, i,
+			readl_relaxed(dev->parf + i),
+			readl_relaxed(dev->parf + (i + 4)),
+			readl_relaxed(dev->parf + (i + 8)),
+			readl_relaxed(dev->parf + (i + 12)),
+			readl_relaxed(dev->parf + (i + 16)),
+			readl_relaxed(dev->parf + (i + 20)),
+			readl_relaxed(dev->parf + (i + 24)),
+			readl_relaxed(dev->parf + (i + 28)));
+	}
+}
+
+static void msm_pcie_show_status(struct msm_pcie_dev_t *dev)
+{
+	PCIE_DBG_FS(dev, "PCIe: RC%d is %s enumerated\n",
+		dev->rc_idx, dev->enumerated ? "" : "not");
+	PCIE_DBG_FS(dev, "PCIe: link is %s\n",
+		(dev->link_status == MSM_PCIE_LINK_ENABLED)
+		? "enabled" : "disabled");
+	PCIE_DBG_FS(dev, "cfg_access is %s allowed\n",
+		dev->cfg_access ? "" : "not");
+	PCIE_DBG_FS(dev, "use_msi is %d\n",
+		dev->use_msi);
+	PCIE_DBG_FS(dev, "use_pinctrl is %d\n",
+		dev->use_pinctrl);
+	PCIE_DBG_FS(dev, "use_19p2mhz_aux_clk is %d\n",
+		dev->use_19p2mhz_aux_clk);
+	PCIE_DBG_FS(dev, "user_suspend is %d\n",
+		dev->user_suspend);
+	PCIE_DBG_FS(dev, "num_ep: %d\n",
+		dev->num_ep);
+	PCIE_DBG_FS(dev, "num_active_ep: %d\n",
+		dev->num_active_ep);
+	PCIE_DBG_FS(dev, "pending_ep_reg: %s\n",
+		dev->pending_ep_reg ? "true" : "false");
+	PCIE_DBG_FS(dev, "phy_len is %d",
+		dev->phy_len);
+	PCIE_DBG_FS(dev, "port_phy_len is %d",
+		dev->port_phy_len);
+	PCIE_DBG_FS(dev, "disable_pc is %d",
+		dev->disable_pc);
+	PCIE_DBG_FS(dev, "l0s_supported is %s supported\n",
+		dev->l0s_supported ? "" : "not");
+	PCIE_DBG_FS(dev, "l1_supported is %s supported\n",
+		dev->l1_supported ? "" : "not");
+	PCIE_DBG_FS(dev, "l1ss_supported is %s supported\n",
+		dev->l1ss_supported ? "" : "not");
+	PCIE_DBG_FS(dev, "common_clk_en is %d\n",
+		dev->common_clk_en);
+	PCIE_DBG_FS(dev, "clk_power_manage_en is %d\n",
+		dev->clk_power_manage_en);
+	PCIE_DBG_FS(dev, "aux_clk_sync is %d\n",
+		dev->aux_clk_sync);
+	PCIE_DBG_FS(dev, "AER is %s enable\n",
+		dev->aer_enable ? "" : "not");
+	PCIE_DBG_FS(dev, "ext_ref_clk is %d\n",
+		dev->ext_ref_clk);
+	PCIE_DBG_FS(dev, "ep_wakeirq is %d\n",
+		dev->ep_wakeirq);
+	PCIE_DBG_FS(dev, "phy_ver is %d\n",
+		dev->phy_ver);
+	PCIE_DBG_FS(dev, "drv_ready is %d\n",
+		dev->drv_ready);
+	PCIE_DBG_FS(dev, "linkdown_panic is %d\n",
+		dev->linkdown_panic);
+	PCIE_DBG_FS(dev, "the link is %s suspending\n",
+		dev->suspending ? "" : "not");
+	PCIE_DBG_FS(dev, "shadow is %s enabled\n",
+		dev->shadow_en ? "" : "not");
+	PCIE_DBG_FS(dev, "the power of RC is %s on\n",
+		dev->power_on ? "" : "not");
+	PCIE_DBG_FS(dev, "msi_gicm_addr: 0x%x\n",
+		dev->msi_gicm_addr);
+	PCIE_DBG_FS(dev, "msi_gicm_base: 0x%x\n",
+		dev->msi_gicm_base);
+	PCIE_DBG_FS(dev, "bus_client: %d\n",
+		dev->bus_client);
+	PCIE_DBG_FS(dev, "current short bdf: %d\n",
+		dev->current_short_bdf);
+	PCIE_DBG_FS(dev, "smmu does %s exist\n",
+		dev->smmu_exist ? "" : "not");
+	PCIE_DBG_FS(dev, "smmu_sid_base: 0x%x\n",
+		dev->smmu_sid_base);
+	PCIE_DBG_FS(dev, "n_fts: %d\n",
+		dev->n_fts);
+	PCIE_DBG_FS(dev, "common_phy: %d\n",
+		dev->common_phy);
+	PCIE_DBG_FS(dev, "ep_latency: %dms\n",
+		dev->ep_latency);
+	PCIE_DBG_FS(dev, "wr_halt_size: 0x%x\n",
+		dev->wr_halt_size);
+	PCIE_DBG_FS(dev, "cpl_timeout: 0x%x\n",
+		dev->cpl_timeout);
+	PCIE_DBG_FS(dev, "current_bdf: 0x%x\n",
+		dev->current_bdf);
+	PCIE_DBG_FS(dev, "perst_delay_us_min: %dus\n",
+		dev->perst_delay_us_min);
+	PCIE_DBG_FS(dev, "perst_delay_us_max: %dus\n",
+		dev->perst_delay_us_max);
+	PCIE_DBG_FS(dev, "tlp_rd_size: 0x%x\n",
+		dev->tlp_rd_size);
+	PCIE_DBG_FS(dev, "rc_corr_counter: %lu\n",
+		dev->rc_corr_counter);
+	PCIE_DBG_FS(dev, "rc_non_fatal_counter: %lu\n",
+		dev->rc_non_fatal_counter);
+	PCIE_DBG_FS(dev, "rc_fatal_counter: %lu\n",
+		dev->rc_fatal_counter);
+	PCIE_DBG_FS(dev, "ep_corr_counter: %lu\n",
+		dev->ep_corr_counter);
+	PCIE_DBG_FS(dev, "ep_non_fatal_counter: %lu\n",
+		dev->ep_non_fatal_counter);
+	PCIE_DBG_FS(dev, "ep_fatal_counter: %lu\n",
+		dev->ep_fatal_counter);
+	PCIE_DBG_FS(dev, "linkdown_counter: %lu\n",
+		dev->linkdown_counter);
+	PCIE_DBG_FS(dev, "wake_counter: %lu\n",
+		dev->wake_counter);
+	PCIE_DBG_FS(dev, "link_turned_on_counter: %lu\n",
+		dev->link_turned_on_counter);
+	PCIE_DBG_FS(dev, "link_turned_off_counter: %lu\n",
+		dev->link_turned_off_counter);
+}
+
+static void msm_pcie_shadow_dump(struct msm_pcie_dev_t *dev, bool rc)
+{
+	int i, j;
+	u32 val = 0;
+	u32 *shadow;
+
+	for (i = 0; i < MAX_DEVICE_NUM; i++) {
+		if (!rc && !dev->pcidev_table[i].bdf)
+			break;
+		if (rc) {
+			shadow = dev->rc_shadow;
+		} else {
+			shadow = dev->ep_shadow[i];
+			PCIE_DBG_FS(dev, "PCIe Device: %02x:%02x.%01x\n",
+				dev->pcidev_table[i].bdf >> 24,
+				dev->pcidev_table[i].bdf >> 19 & 0x1f,
+				dev->pcidev_table[i].bdf >> 16 & 0x07);
+		}
+		for (j = 0; j < PCIE_CONF_SPACE_DW; j++) {
+			val = shadow[j];
+			if (val != PCIE_CLEAR) {
+				PCIE_DBG_FS(dev,
+					"PCIe: shadow_dw[%d]:cfg 0x%x:0x%x\n",
+					j, j * 4, val);
+			}
+		}
+		if (rc)
+			break;
+	}
+}
+
+static void msm_pcie_sel_debug_testcase(struct msm_pcie_dev_t *dev,
+					u32 testcase)
+{
+	int ret, i;
+	u32 base_sel_size = 0;
+	u32 val = 0;
+	u32 current_offset = 0;
+	u32 ep_l1sub_ctrl1_offset = 0;
+	u32 ep_l1sub_cap_reg1_offset = 0;
+	u32 ep_link_ctrlstts_offset = 0;
+	u32 ep_dev_ctrl2stts2_offset = 0;
+
+	if (testcase >= 5 && testcase <= 10) {
+		current_offset =
+			readl_relaxed(dev->conf + PCIE_CAP_PTR_OFFSET) & 0xff;
+
+		while (current_offset) {
+			val = readl_relaxed(dev->conf + current_offset);
+			if ((val & 0xff) == PCIE20_CAP_ID) {
+				ep_link_ctrlstts_offset = current_offset +
+								0x10;
+				ep_dev_ctrl2stts2_offset = current_offset +
+								0x28;
+				break;
+			}
+			current_offset = (val >> 8) & 0xff;
+		}
+
+		if (!ep_link_ctrlstts_offset)
+			PCIE_DBG(dev,
+				"RC%d endpoint does not support PCIe capability registers\n",
+				dev->rc_idx);
+		else
+			PCIE_DBG(dev,
+				"RC%d: ep_link_ctrlstts_offset: 0x%x\n",
+				dev->rc_idx, ep_link_ctrlstts_offset);
+	}
+
+	switch (testcase) {
+	case 0: /* output status */
+		PCIE_DBG_FS(dev, "\n\nPCIe: Status for RC%d:\n",
+			dev->rc_idx);
+		msm_pcie_show_status(dev);
+		break;
+	case 1: /* disable link */
+		PCIE_DBG_FS(dev,
+			"\n\nPCIe: RC%d: disable link\n\n", dev->rc_idx);
+		ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, 0,
+			dev->dev, NULL,
+			MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+		if (ret)
+			PCIE_DBG_FS(dev, "PCIe:%s:failed to disable link\n",
+				__func__);
+		else
+			PCIE_DBG_FS(dev, "PCIe:%s:disabled link\n",
+				__func__);
+		break;
+	case 2: /* enable link and recover config space for RC and EP */
+		PCIE_DBG_FS(dev,
+			"\n\nPCIe: RC%d: enable link and recover config space\n\n",
+			dev->rc_idx);
+		ret = msm_pcie_pm_control(MSM_PCIE_RESUME, 0,
+			dev->dev, NULL,
+			MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+		if (ret)
+			PCIE_DBG_FS(dev, "PCIe:%s:failed to enable link\n",
+				__func__);
+		else {
+			PCIE_DBG_FS(dev, "PCIe:%s:enabled link\n", __func__);
+			msm_pcie_recover_config(dev->dev);
+		}
+		break;
+	case 3: /*
+		 * disable and enable link, recover config space for
+		 * RC and EP
+		 */
+		PCIE_DBG_FS(dev,
+			"\n\nPCIe: RC%d: disable and enable link then recover config space\n\n",
+			dev->rc_idx);
+		ret = msm_pcie_pm_control(MSM_PCIE_SUSPEND, 0,
+			dev->dev, NULL,
+			MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+		if (ret)
+			PCIE_DBG_FS(dev, "PCIe:%s:failed to disable link\n",
+				__func__);
+		else
+			PCIE_DBG_FS(dev, "PCIe:%s:disabled link\n", __func__);
+		ret = msm_pcie_pm_control(MSM_PCIE_RESUME, 0,
+			dev->dev, NULL,
+			MSM_PCIE_CONFIG_NO_CFG_RESTORE);
+		if (ret)
+			PCIE_DBG_FS(dev, "PCIe:%s:failed to enable link\n",
+				__func__);
+		else {
+			PCIE_DBG_FS(dev, "PCIe:%s:enabled link\n", __func__);
+			msm_pcie_recover_config(dev->dev);
+		}
+		break;
+	case 4: /* dump shadow registers for RC and EP */
+		PCIE_DBG_FS(dev,
+			"\n\nPCIe: RC%d: dumping RC shadow registers\n",
+			dev->rc_idx);
+		msm_pcie_shadow_dump(dev, true);
+
+		PCIE_DBG_FS(dev,
+			"\n\nPCIe: RC%d: dumping EP shadow registers\n",
+			dev->rc_idx);
+		msm_pcie_shadow_dump(dev, false);
+		break;
+	case 5: /* disable L0s */
+		PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: disable L0s\n\n",
+			dev->rc_idx);
+		msm_pcie_write_mask(dev->dm_core +
+				PCIE20_CAP_LINKCTRLSTATUS,
+				BIT(0), 0);
+		msm_pcie_write_mask(dev->conf +
+				ep_link_ctrlstts_offset,
+				BIT(0), 0);
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+					readl_relaxed(dev->dm_core +
+					PCIE20_CAP_LINKCTRLSTATUS);
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+					readl_relaxed(dev->conf +
+					ep_link_ctrlstts_offset);
+		}
+		PCIE_DBG_FS(dev, "PCIe: RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG_FS(dev, "PCIe: EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_link_ctrlstts_offset));
+		break;
+	case 6: /* enable L0s */
+		PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: enable L0s\n\n",
+			dev->rc_idx);
+		msm_pcie_write_mask(dev->dm_core +
+				PCIE20_CAP_LINKCTRLSTATUS,
+				0, BIT(0));
+		msm_pcie_write_mask(dev->conf +
+				ep_link_ctrlstts_offset,
+				0, BIT(0));
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+					readl_relaxed(dev->dm_core +
+					PCIE20_CAP_LINKCTRLSTATUS);
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+					readl_relaxed(dev->conf +
+					ep_link_ctrlstts_offset);
+		}
+		PCIE_DBG_FS(dev, "PCIe: RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG_FS(dev, "PCIe: EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_link_ctrlstts_offset));
+		break;
+	case 7: /* disable L1 */
+		PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: disable L1\n\n",
+			dev->rc_idx);
+		msm_pcie_write_mask(dev->dm_core +
+				PCIE20_CAP_LINKCTRLSTATUS,
+				BIT(1), 0);
+		msm_pcie_write_mask(dev->conf +
+				ep_link_ctrlstts_offset,
+				BIT(1), 0);
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+					readl_relaxed(dev->dm_core +
+					PCIE20_CAP_LINKCTRLSTATUS);
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+					readl_relaxed(dev->conf +
+					ep_link_ctrlstts_offset);
+		}
+		PCIE_DBG_FS(dev, "PCIe: RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG_FS(dev, "PCIe: EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_link_ctrlstts_offset));
+		break;
+	case 8: /* enable L1 */
+		PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: enable L1\n\n",
+			dev->rc_idx);
+		msm_pcie_write_mask(dev->dm_core +
+				PCIE20_CAP_LINKCTRLSTATUS,
+				0, BIT(1));
+		msm_pcie_write_mask(dev->conf +
+				ep_link_ctrlstts_offset,
+				0, BIT(1));
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+					readl_relaxed(dev->dm_core +
+					PCIE20_CAP_LINKCTRLSTATUS);
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+					readl_relaxed(dev->conf +
+					ep_link_ctrlstts_offset);
+		}
+		PCIE_DBG_FS(dev, "PCIe: RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG_FS(dev, "PCIe: EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_link_ctrlstts_offset));
+		break;
+	case 9: /* disable L1ss */
+		PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: disable L1ss\n\n",
+			dev->rc_idx);
+		current_offset = PCIE_EXT_CAP_OFFSET;
+		while (current_offset) {
+			val = readl_relaxed(dev->conf + current_offset);
+			if ((val & 0xffff) == L1SUB_CAP_ID) {
+				ep_l1sub_ctrl1_offset =
+						current_offset + 0x8;
+				break;
+			}
+			current_offset = val >> 20;
+		}
+		if (!ep_l1sub_ctrl1_offset) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d endpoint does not support l1ss registers\n",
+				dev->rc_idx);
+			break;
+		}
+
+		PCIE_DBG_FS(dev, "PCIe: RC%d: ep_l1sub_ctrl1_offset: 0x%x\n",
+				dev->rc_idx, ep_l1sub_ctrl1_offset);
+
+		msm_pcie_write_reg_field(dev->dm_core,
+					PCIE20_L1SUB_CONTROL1,
+					0xf, 0);
+		msm_pcie_write_mask(dev->dm_core +
+					PCIE20_DEVICE_CONTROL2_STATUS2,
+					BIT(10), 0);
+		msm_pcie_write_reg_field(dev->conf,
+					ep_l1sub_ctrl1_offset,
+					0xf, 0);
+		msm_pcie_write_mask(dev->conf +
+					ep_dev_ctrl2stts2_offset,
+					BIT(10), 0);
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_L1SUB_CONTROL1 / 4] =
+				readl_relaxed(dev->dm_core +
+				PCIE20_L1SUB_CONTROL1);
+			dev->rc_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] =
+				readl_relaxed(dev->dm_core +
+				PCIE20_DEVICE_CONTROL2_STATUS2);
+			dev->ep_shadow[0][ep_l1sub_ctrl1_offset / 4] =
+				readl_relaxed(dev->conf +
+				ep_l1sub_ctrl1_offset);
+			dev->ep_shadow[0][ep_dev_ctrl2stts2_offset / 4] =
+				readl_relaxed(dev->conf +
+				ep_dev_ctrl2stts2_offset);
+		}
+		PCIE_DBG_FS(dev, "PCIe: RC's L1SUB_CONTROL1:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_L1SUB_CONTROL1));
+		PCIE_DBG_FS(dev, "PCIe: RC's DEVICE_CONTROL2_STATUS2:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_DEVICE_CONTROL2_STATUS2));
+		PCIE_DBG_FS(dev, "PCIe: EP's L1SUB_CONTROL1:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_l1sub_ctrl1_offset));
+		PCIE_DBG_FS(dev, "PCIe: EP's DEVICE_CONTROL2_STATUS2:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_dev_ctrl2stts2_offset));
+		break;
+	case 10: /* enable L1ss */
+		PCIE_DBG_FS(dev, "\n\nPCIe: RC%d: enable L1ss\n\n",
+			dev->rc_idx);
+		current_offset = PCIE_EXT_CAP_OFFSET;
+		while (current_offset) {
+			val = readl_relaxed(dev->conf + current_offset);
+			if ((val & 0xffff) == L1SUB_CAP_ID) {
+				ep_l1sub_cap_reg1_offset =
+						current_offset + 0x4;
+				ep_l1sub_ctrl1_offset =
+						current_offset + 0x8;
+				break;
+			}
+			current_offset = val >> 20;
+		}
+		if (!ep_l1sub_ctrl1_offset) {
+			PCIE_DBG_FS(dev,
+				"PCIe: RC%d endpoint does not support l1ss registers\n",
+				dev->rc_idx);
+			break;
+		}
+
+		val = readl_relaxed(dev->conf +
+				ep_l1sub_cap_reg1_offset);
+
+		PCIE_DBG_FS(dev, "PCIe: EP's L1SUB_CAPABILITY_REG_1: 0x%x\n",
+			val);
+		PCIE_DBG_FS(dev, "PCIe: RC%d: ep_l1sub_ctrl1_offset: 0x%x\n",
+			dev->rc_idx, ep_l1sub_ctrl1_offset);
+
+		val &= 0xf;
+
+		msm_pcie_write_reg_field(dev->dm_core,
+					PCIE20_L1SUB_CONTROL1,
+					0xf, val);
+		msm_pcie_write_mask(dev->dm_core +
+					PCIE20_DEVICE_CONTROL2_STATUS2,
+					0, BIT(10));
+		msm_pcie_write_reg_field(dev->conf,
+					ep_l1sub_ctrl1_offset,
+					0xf, val);
+		msm_pcie_write_mask(dev->conf +
+					ep_dev_ctrl2stts2_offset,
+					0, BIT(10));
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_L1SUB_CONTROL1 / 4] =
+				readl_relaxed(dev->dm_core +
+					PCIE20_L1SUB_CONTROL1);
+			dev->rc_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] =
+				readl_relaxed(dev->dm_core +
+				PCIE20_DEVICE_CONTROL2_STATUS2);
+			dev->ep_shadow[0][ep_l1sub_ctrl1_offset / 4] =
+				readl_relaxed(dev->conf +
+				ep_l1sub_ctrl1_offset);
+			dev->ep_shadow[0][ep_dev_ctrl2stts2_offset / 4] =
+				readl_relaxed(dev->conf +
+				ep_dev_ctrl2stts2_offset);
+		}
+		PCIE_DBG_FS(dev, "PCIe: RC's L1SUB_CONTROL1:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_L1SUB_CONTROL1));
+		PCIE_DBG_FS(dev, "PCIe: RC's DEVICE_CONTROL2_STATUS2:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_DEVICE_CONTROL2_STATUS2));
+		PCIE_DBG_FS(dev, "PCIe: EP's L1SUB_CONTROL1:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_l1sub_ctrl1_offset));
+		PCIE_DBG_FS(dev, "PCIe: EP's DEVICE_CONTROL2_STATUS2:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_dev_ctrl2stts2_offset));
+		break;
+	case 11: /* enumerate PCIe  */
+		PCIE_DBG_FS(dev, "\n\nPCIe: attempting to enumerate RC%d\n\n",
+			dev->rc_idx);
+		if (dev->enumerated)
+			PCIE_DBG_FS(dev, "PCIe: RC%d is already enumerated\n",
+				dev->rc_idx);
+		else {
+			if (!msm_pcie_enumerate(dev->rc_idx))
+				PCIE_DBG_FS(dev,
+					"PCIe: RC%d is successfully enumerated\n",
+					dev->rc_idx);
+			else
+				PCIE_DBG_FS(dev,
+					"PCIe: RC%d enumeration failed\n",
+					dev->rc_idx);
+		}
+		break;
+	case 12: /* write a value to a register */
+		PCIE_DBG_FS(dev,
+			"\n\nPCIe: RC%d: writing a value to a register\n\n",
+			dev->rc_idx);
+
+		if (!base_sel) {
+			PCIE_DBG_FS(dev, "Invalid base_sel: 0x%x\n", base_sel);
+			break;
+		}
+
+		PCIE_DBG_FS(dev,
+			"base: %s: 0x%p\nwr_offset: 0x%x\nwr_mask: 0x%x\nwr_value: 0x%x\n",
+			dev->res[base_sel - 1].name,
+			dev->res[base_sel - 1].base,
+			wr_offset, wr_mask, wr_value);
+
+		msm_pcie_write_reg_field(dev->res[base_sel - 1].base,
+			wr_offset, wr_mask, wr_value);
+
+		break;
+	case 13: /* dump all registers of base_sel */
+		if (!base_sel) {
+			PCIE_DBG_FS(dev, "Invalid base_sel: 0x%x\n", base_sel);
+			break;
+		} else if (base_sel - 1 == MSM_PCIE_RES_PARF) {
+			pcie_parf_dump(dev);
+			break;
+		} else if (base_sel - 1 == MSM_PCIE_RES_PHY) {
+			pcie_phy_dump(dev);
+			break;
+		} else if (base_sel - 1 == MSM_PCIE_RES_CONF) {
+			base_sel_size = 0x1000;
+		} else {
+			base_sel_size = resource_size(
+				dev->res[base_sel - 1].resource);
+		}
+
+		PCIE_DBG_FS(dev, "\n\nPCIe: Dumping %s Registers for RC%d\n\n",
+			dev->res[base_sel - 1].name, dev->rc_idx);
+
+		for (i = 0; i < base_sel_size; i += 32) {
+			PCIE_DBG_FS(dev,
+			"0x%04x %08x %08x %08x %08x %08x %08x %08x %08x\n",
+			i, readl_relaxed(dev->res[base_sel - 1].base + i),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 4)),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 8)),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 12)),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 16)),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 20)),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 24)),
+			readl_relaxed(dev->res[base_sel - 1].base + (i + 28)));
+		}
+		break;
+	default:
+		PCIE_DBG_FS(dev, "Invalid testcase: %d.\n", testcase);
+		break;
+	}
+}
+
+int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
+			u32 offset, u32 mask, u32 value)
+{
+	int ret = 0;
+	struct msm_pcie_dev_t *pdev = NULL;
+
+	if (!dev) {
+		pr_err("PCIe: the input pci dev is NULL.\n");
+		return -ENODEV;
+	}
+
+	if (option == 12 || option == 13) {
+		if (!base || base > 5) {
+			PCIE_DBG_FS(pdev, "Invalid base_sel: 0x%x\n", base);
+			PCIE_DBG_FS(pdev,
+				"PCIe: base_sel is still 0x%x\n", base_sel);
+			return -EINVAL;
+		}
+
+		base_sel = base;
+		PCIE_DBG_FS(pdev, "PCIe: base_sel is now 0x%x\n", base_sel);
+
+		if (option == 12) {
+			wr_offset = offset;
+			wr_mask = mask;
+			wr_value = value;
+
+			PCIE_DBG_FS(pdev,
+				"PCIe: wr_offset is now 0x%x\n", wr_offset);
+			PCIE_DBG_FS(pdev,
+				"PCIe: wr_mask is now 0x%x\n", wr_mask);
+			PCIE_DBG_FS(pdev,
+				"PCIe: wr_value is now 0x%x\n", wr_value);
+		}
+	}
+
+	pdev = PCIE_BUS_PRIV_DATA(dev->bus);
+	rc_sel = 1 << pdev->rc_idx;
+
+	msm_pcie_sel_debug_testcase(pdev, option);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_debug_info);
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *dent_msm_pcie;
+static struct dentry *dfile_rc_sel;
+static struct dentry *dfile_case;
+static struct dentry *dfile_base_sel;
+static struct dentry *dfile_linkdown_panic;
+static struct dentry *dfile_wr_offset;
+static struct dentry *dfile_wr_mask;
+static struct dentry *dfile_wr_value;
+static struct dentry *dfile_ep_wakeirq;
+static struct dentry *dfile_aer_enable;
+static struct dentry *dfile_corr_counter_limit;
+
+static u32 rc_sel_max;
+
+static ssize_t msm_pcie_cmd_debug(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	unsigned int testcase = 0;
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		testcase = (testcase * 10) + (str[i] - '0');
+
+	if (!rc_sel)
+		rc_sel = 1;
+
+	pr_alert("PCIe: TEST: %d\n", testcase);
+
+	for (i = 0; i < MAX_RC_NUM; i++) {
+		if (!((rc_sel >> i) & 0x1))
+			continue;
+		msm_pcie_sel_debug_testcase(&msm_pcie_dev[i], testcase);
+	}
+
+	return count;
+}
+
+const struct file_operations msm_pcie_cmd_debug_ops = {
+	.write = msm_pcie_cmd_debug,
+};
+
+static ssize_t msm_pcie_set_rc_sel(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	int i;
+	u32 new_rc_sel = 0;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		new_rc_sel = (new_rc_sel * 10) + (str[i] - '0');
+
+	if ((!new_rc_sel) || (new_rc_sel > rc_sel_max)) {
+		pr_alert("PCIe: invalid value for rc_sel: 0x%x\n", new_rc_sel);
+		pr_alert("PCIe: rc_sel is still 0x%x\n", rc_sel ? rc_sel : 0x1);
+	} else {
+		rc_sel = new_rc_sel;
+		pr_alert("PCIe: rc_sel is now: 0x%x\n", rc_sel);
+	}
+
+	pr_alert("PCIe: the following RC(s) will be tested:\n");
+	for (i = 0; i < MAX_RC_NUM; i++) {
+		if (!rc_sel) {
+			pr_alert("RC %d\n", i);
+			break;
+		} else if (rc_sel & (1 << i)) {
+			pr_alert("RC %d\n", i);
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations msm_pcie_rc_sel_ops = {
+	.write = msm_pcie_set_rc_sel,
+};
+
+static ssize_t msm_pcie_set_base_sel(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	int i;
+	u32 new_base_sel = 0;
+	char *base_sel_name;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		new_base_sel = (new_base_sel * 10) + (str[i] - '0');
+
+	if (!new_base_sel || new_base_sel > 5) {
+		pr_alert("PCIe: invalid value for base_sel: 0x%x\n",
+			new_base_sel);
+		pr_alert("PCIe: base_sel is still 0x%x\n", base_sel);
+	} else {
+		base_sel = new_base_sel;
+		pr_alert("PCIe: base_sel is now 0x%x\n", base_sel);
+	}
+
+	switch (base_sel) {
+	case 1:
+		base_sel_name = "PARF";
+		break;
+	case 2:
+		base_sel_name = "PHY";
+		break;
+	case 3:
+		base_sel_name = "RC CONFIG SPACE";
+		break;
+	case 4:
+		base_sel_name = "ELBI";
+		break;
+	case 5:
+		base_sel_name = "EP CONFIG SPACE";
+		break;
+	default:
+		base_sel_name = "INVALID";
+		break;
+	}
+
+	pr_alert("%s\n", base_sel_name);
+
+	return count;
+}
+
+const struct file_operations msm_pcie_base_sel_ops = {
+	.write = msm_pcie_set_base_sel,
+};
+
+static ssize_t msm_pcie_set_linkdown_panic(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	u32 new_linkdown_panic = 0;
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		new_linkdown_panic = (new_linkdown_panic * 10) + (str[i] - '0');
+
+	if (new_linkdown_panic <= 1) {
+		for (i = 0; i < MAX_RC_NUM; i++) {
+			if (!rc_sel) {
+				msm_pcie_dev[0].linkdown_panic =
+					new_linkdown_panic;
+				PCIE_DBG_FS(&msm_pcie_dev[0],
+					"PCIe: RC0: linkdown_panic is now %d\n",
+					msm_pcie_dev[0].linkdown_panic);
+				break;
+			} else if (rc_sel & (1 << i)) {
+				msm_pcie_dev[i].linkdown_panic =
+					new_linkdown_panic;
+				PCIE_DBG_FS(&msm_pcie_dev[i],
+					"PCIe: RC%d: linkdown_panic is now %d\n",
+					i, msm_pcie_dev[i].linkdown_panic);
+			}
+		}
+	} else {
+		pr_err("PCIe: Invalid input for linkdown_panic: %d. Please enter 0 or 1.\n",
+			new_linkdown_panic);
+	}
+
+	return count;
+}
+
+const struct file_operations msm_pcie_linkdown_panic_ops = {
+	.write = msm_pcie_set_linkdown_panic,
+};
+
+static ssize_t msm_pcie_set_wr_offset(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	wr_offset = 0;
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		wr_offset = (wr_offset * 10) + (str[i] - '0');
+
+	pr_alert("PCIe: wr_offset is now 0x%x\n", wr_offset);
+
+	return count;
+}
+
+const struct file_operations msm_pcie_wr_offset_ops = {
+	.write = msm_pcie_set_wr_offset,
+};
+
+static ssize_t msm_pcie_set_wr_mask(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	wr_mask = 0;
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		wr_mask = (wr_mask * 10) + (str[i] - '0');
+
+	pr_alert("PCIe: wr_mask is now 0x%x\n", wr_mask);
+
+	return count;
+}
+
+const struct file_operations msm_pcie_wr_mask_ops = {
+	.write = msm_pcie_set_wr_mask,
+};
+static ssize_t msm_pcie_set_wr_value(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	wr_value = 0;
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		wr_value = (wr_value * 10) + (str[i] - '0');
+
+	pr_alert("PCIe: wr_value is now 0x%x\n", wr_value);
+
+	return count;
+}
+
+const struct file_operations msm_pcie_wr_value_ops = {
+	.write = msm_pcie_set_wr_value,
+};
+
+static ssize_t msm_pcie_set_ep_wakeirq(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	u32 new_ep_wakeirq = 0;
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		new_ep_wakeirq = (new_ep_wakeirq * 10) + (str[i] - '0');
+
+	if (new_ep_wakeirq <= 1) {
+		for (i = 0; i < MAX_RC_NUM; i++) {
+			if (!rc_sel) {
+				msm_pcie_dev[0].ep_wakeirq = new_ep_wakeirq;
+				PCIE_DBG_FS(&msm_pcie_dev[0],
+					"PCIe: RC0: ep_wakeirq is now %d\n",
+					msm_pcie_dev[0].ep_wakeirq);
+				break;
+			} else if (rc_sel & (1 << i)) {
+				msm_pcie_dev[i].ep_wakeirq = new_ep_wakeirq;
+				PCIE_DBG_FS(&msm_pcie_dev[i],
+					"PCIe: RC%d: ep_wakeirq is now %d\n",
+					i, msm_pcie_dev[i].ep_wakeirq);
+			}
+		}
+	} else {
+		pr_err("PCIe: Invalid input for ep_wakeirq: %d. Please enter 0 or 1.\n",
+			new_ep_wakeirq);
+	}
+
+	return count;
+}
+
+const struct file_operations msm_pcie_ep_wakeirq_ops = {
+	.write = msm_pcie_set_ep_wakeirq,
+};
+
+static ssize_t msm_pcie_set_aer_enable(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	u32 new_aer_enable = 0;
+	u32 temp_rc_sel;
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		new_aer_enable = (new_aer_enable * 10) + (str[i] - '0');
+
+	if (new_aer_enable > 1) {
+		pr_err(
+			"PCIe: Invalid input for aer_enable: %d. Please enter 0 or 1.\n",
+			new_aer_enable);
+		return count;
+	}
+
+	if (rc_sel)
+		temp_rc_sel = rc_sel;
+	else
+		temp_rc_sel = 0x1;
+
+	for (i = 0; i < MAX_RC_NUM; i++) {
+		if (temp_rc_sel & (1 << i)) {
+			msm_pcie_dev[i].aer_enable = new_aer_enable;
+			PCIE_DBG_FS(&msm_pcie_dev[i],
+				"PCIe: RC%d: aer_enable is now %d\n",
+				i, msm_pcie_dev[i].aer_enable);
+
+			msm_pcie_write_mask(msm_pcie_dev[i].dm_core +
+					PCIE20_BRIDGE_CTRL,
+					new_aer_enable ? 0 : BIT(16),
+					new_aer_enable ? BIT(16) : 0);
+
+			PCIE_DBG_FS(&msm_pcie_dev[i],
+				"RC%d: PCIE20_BRIDGE_CTRL: 0x%x\n", i,
+				readl_relaxed(msm_pcie_dev[i].dm_core +
+					PCIE20_BRIDGE_CTRL));
+		}
+	}
+
+	return count;
+}
+
+const struct file_operations msm_pcie_aer_enable_ops = {
+	.write = msm_pcie_set_aer_enable,
+};
+
+static ssize_t msm_pcie_set_corr_counter_limit(struct file *file,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	unsigned long ret;
+	char str[MAX_MSG_LEN];
+	int i;
+
+	memset(str, 0, sizeof(str));
+	ret = copy_from_user(str, buf, sizeof(str));
+	if (ret)
+		return -EFAULT;
+
+	corr_counter_limit = 0;
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		corr_counter_limit = (corr_counter_limit * 10) + (str[i] - '0');
+
+	pr_info("PCIe: corr_counter_limit is now %lu\n", corr_counter_limit);
+
+	return count;
+}
+
+const struct file_operations msm_pcie_corr_counter_limit_ops = {
+	.write = msm_pcie_set_corr_counter_limit,
+};
+
+static void msm_pcie_debugfs_init(void)
+{
+	rc_sel_max = (0x1 << MAX_RC_NUM) - 1;
+	wr_mask = 0xffffffff;
+
+	dent_msm_pcie = debugfs_create_dir("pci-msm", 0);
+	if (IS_ERR(dent_msm_pcie)) {
+		pr_err("PCIe: fail to create the folder for debug_fs.\n");
+		return;
+	}
+
+	dfile_rc_sel = debugfs_create_file("rc_sel", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_rc_sel_ops);
+	if (!dfile_rc_sel || IS_ERR(dfile_rc_sel)) {
+		pr_err("PCIe: fail to create the file for debug_fs rc_sel.\n");
+		goto rc_sel_error;
+	}
+
+	dfile_case = debugfs_create_file("case", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_cmd_debug_ops);
+	if (!dfile_case || IS_ERR(dfile_case)) {
+		pr_err("PCIe: fail to create the file for debug_fs case.\n");
+		goto case_error;
+	}
+
+	dfile_base_sel = debugfs_create_file("base_sel", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_base_sel_ops);
+	if (!dfile_base_sel || IS_ERR(dfile_base_sel)) {
+		pr_err("PCIe: fail to create the file for debug_fs base_sel.\n");
+		goto base_sel_error;
+	}
+
+	dfile_linkdown_panic = debugfs_create_file("linkdown_panic", 0644,
+					dent_msm_pcie, 0,
+					&msm_pcie_linkdown_panic_ops);
+	if (!dfile_linkdown_panic || IS_ERR(dfile_linkdown_panic)) {
+		pr_err("PCIe: fail to create the file for debug_fs linkdown_panic.\n");
+		goto linkdown_panic_error;
+	}
+
+	dfile_wr_offset = debugfs_create_file("wr_offset", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_wr_offset_ops);
+	if (!dfile_wr_offset || IS_ERR(dfile_wr_offset)) {
+		pr_err("PCIe: fail to create the file for debug_fs wr_offset.\n");
+		goto wr_offset_error;
+	}
+
+	dfile_wr_mask = debugfs_create_file("wr_mask", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_wr_mask_ops);
+	if (!dfile_wr_mask || IS_ERR(dfile_wr_mask)) {
+		pr_err("PCIe: fail to create the file for debug_fs wr_mask.\n");
+		goto wr_mask_error;
+	}
+
+	dfile_wr_value = debugfs_create_file("wr_value", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_wr_value_ops);
+	if (!dfile_wr_value || IS_ERR(dfile_wr_value)) {
+		pr_err("PCIe: fail to create the file for debug_fs wr_value.\n");
+		goto wr_value_error;
+	}
+
+	dfile_ep_wakeirq = debugfs_create_file("ep_wakeirq", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_ep_wakeirq_ops);
+	if (!dfile_ep_wakeirq || IS_ERR(dfile_ep_wakeirq)) {
+		pr_err("PCIe: fail to create the file for debug_fs ep_wakeirq.\n");
+		goto ep_wakeirq_error;
+	}
+
+	dfile_aer_enable = debugfs_create_file("aer_enable", 0664,
+					dent_msm_pcie, 0,
+					&msm_pcie_aer_enable_ops);
+	if (!dfile_aer_enable || IS_ERR(dfile_aer_enable)) {
+		pr_err("PCIe: fail to create the file for debug_fs aer_enable.\n");
+		goto aer_enable_error;
+	}
+
+	dfile_corr_counter_limit = debugfs_create_file("corr_counter_limit",
+					0664, dent_msm_pcie, 0,
+					&msm_pcie_corr_counter_limit_ops);
+	if (!dfile_corr_counter_limit || IS_ERR(dfile_corr_counter_limit)) {
+		pr_err("PCIe: fail to create the file for debug_fs corr_counter_limit.\n");
+		goto corr_counter_limit_error;
+	}
+	return;
+
+corr_counter_limit_error:
+	debugfs_remove(dfile_aer_enable);
+aer_enable_error:
+	debugfs_remove(dfile_ep_wakeirq);
+ep_wakeirq_error:
+	debugfs_remove(dfile_wr_value);
+wr_value_error:
+	debugfs_remove(dfile_wr_mask);
+wr_mask_error:
+	debugfs_remove(dfile_wr_offset);
+wr_offset_error:
+	debugfs_remove(dfile_linkdown_panic);
+linkdown_panic_error:
+	debugfs_remove(dfile_base_sel);
+base_sel_error:
+	debugfs_remove(dfile_case);
+case_error:
+	debugfs_remove(dfile_rc_sel);
+rc_sel_error:
+	debugfs_remove(dent_msm_pcie);
+}
+
+static void msm_pcie_debugfs_exit(void)
+{
+	debugfs_remove(dfile_rc_sel);
+	debugfs_remove(dfile_case);
+	debugfs_remove(dfile_base_sel);
+	debugfs_remove(dfile_linkdown_panic);
+	debugfs_remove(dfile_wr_offset);
+	debugfs_remove(dfile_wr_mask);
+	debugfs_remove(dfile_wr_value);
+	debugfs_remove(dfile_ep_wakeirq);
+	debugfs_remove(dfile_aer_enable);
+	debugfs_remove(dfile_corr_counter_limit);
+}
+#else
+static void msm_pcie_debugfs_init(void)
+{
+}
+
+static void msm_pcie_debugfs_exit(void)
+{
+}
+#endif
+
+static inline int msm_pcie_is_link_up(struct msm_pcie_dev_t *dev)
+{
+	return readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS) & BIT(29);
+}
+
+/**
+ * msm_pcie_iatu_config - configure outbound address translation region
+ * @dev: root commpex
+ * @nr: region number
+ * @type: target transaction type, see PCIE20_CTRL1_TYPE_xxx
+ * @host_addr: - region start address on host
+ * @host_end: - region end address (low 32 bit) on host,
+ *	upper 32 bits are same as for @host_addr
+ * @target_addr: - region start address on target
+ */
+static void msm_pcie_iatu_config(struct msm_pcie_dev_t *dev, int nr, u8 type,
+				unsigned long host_addr, u32 host_end,
+				unsigned long target_addr)
+{
+	void __iomem *pcie20 = dev->dm_core;
+
+	if (dev->shadow_en) {
+		dev->rc_shadow[PCIE20_PLR_IATU_VIEWPORT / 4] =
+			nr;
+		dev->rc_shadow[PCIE20_PLR_IATU_CTRL1 / 4] =
+			type;
+		dev->rc_shadow[PCIE20_PLR_IATU_LBAR / 4] =
+			lower_32_bits(host_addr);
+		dev->rc_shadow[PCIE20_PLR_IATU_UBAR / 4] =
+			upper_32_bits(host_addr);
+		dev->rc_shadow[PCIE20_PLR_IATU_LAR / 4] =
+			host_end;
+		dev->rc_shadow[PCIE20_PLR_IATU_LTAR / 4] =
+			lower_32_bits(target_addr);
+		dev->rc_shadow[PCIE20_PLR_IATU_UTAR / 4] =
+			upper_32_bits(target_addr);
+		dev->rc_shadow[PCIE20_PLR_IATU_CTRL2 / 4] =
+			BIT(31);
+	}
+
+	/* select region */
+	writel_relaxed(nr, pcie20 + PCIE20_PLR_IATU_VIEWPORT);
+	/* ensure that hardware locks it */
+	wmb();
+
+	/* switch off region before changing it */
+	writel_relaxed(0, pcie20 + PCIE20_PLR_IATU_CTRL2);
+	/* and wait till it propagates to the hardware */
+	wmb();
+
+	writel_relaxed(type, pcie20 + PCIE20_PLR_IATU_CTRL1);
+	writel_relaxed(lower_32_bits(host_addr),
+		       pcie20 + PCIE20_PLR_IATU_LBAR);
+	writel_relaxed(upper_32_bits(host_addr),
+		       pcie20 + PCIE20_PLR_IATU_UBAR);
+	writel_relaxed(host_end, pcie20 + PCIE20_PLR_IATU_LAR);
+	writel_relaxed(lower_32_bits(target_addr),
+		       pcie20 + PCIE20_PLR_IATU_LTAR);
+	writel_relaxed(upper_32_bits(target_addr),
+		       pcie20 + PCIE20_PLR_IATU_UTAR);
+	/* ensure that changes propagated to the hardware */
+	wmb();
+	writel_relaxed(BIT(31), pcie20 + PCIE20_PLR_IATU_CTRL2);
+
+	/* ensure that changes propagated to the hardware */
+	wmb();
+
+	if (dev->enumerated) {
+		PCIE_DBG2(dev, "IATU for Endpoint %02x:%02x.%01x\n",
+			dev->pcidev_table[nr].bdf >> 24,
+			dev->pcidev_table[nr].bdf >> 19 & 0x1f,
+			dev->pcidev_table[nr].bdf >> 16 & 0x07);
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_VIEWPORT:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_VIEWPORT));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_CTRL1:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL1));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_LBAR:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LBAR));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_UBAR:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UBAR));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_LAR:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LAR));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_LTAR:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_LTAR));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_UTAR:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_UTAR));
+		PCIE_DBG2(dev, "PCIE20_PLR_IATU_CTRL2:0x%x\n\n",
+			readl_relaxed(dev->dm_core + PCIE20_PLR_IATU_CTRL2));
+	}
+}
+
+/**
+ * msm_pcie_cfg_bdf - configure for config access
+ * @dev: root commpex
+ * @bus: PCI bus number
+ * @devfn: PCI dev and function number
+ *
+ * Remap if required region 0 for config access of proper type
+ * (CFG0 for bus 1, CFG1 for other buses)
+ * Cache current device bdf for speed-up
+ */
+static void msm_pcie_cfg_bdf(struct msm_pcie_dev_t *dev, u8 bus, u8 devfn)
+{
+	struct resource *axi_conf = dev->res[MSM_PCIE_RES_CONF].resource;
+	u32 bdf  = BDF_OFFSET(bus, devfn);
+	u8 type = bus == 1 ? PCIE20_CTRL1_TYPE_CFG0 : PCIE20_CTRL1_TYPE_CFG1;
+
+	if (dev->current_bdf == bdf)
+		return;
+
+	msm_pcie_iatu_config(dev, 0, type,
+			axi_conf->start,
+			axi_conf->start + SZ_4K - 1,
+			bdf);
+
+	dev->current_bdf = bdf;
+}
+
+static inline void msm_pcie_save_shadow(struct msm_pcie_dev_t *dev,
+					u32 word_offset, u32 wr_val,
+					u32 bdf, bool rc)
+{
+	int i, j;
+	u32 max_dev = MAX_RC_NUM * MAX_DEVICE_NUM;
+
+	if (rc) {
+		dev->rc_shadow[word_offset / 4] = wr_val;
+	} else {
+		for (i = 0; i < MAX_DEVICE_NUM; i++) {
+			if (!dev->pcidev_table[i].bdf) {
+				for (j = 0; j < max_dev; j++)
+					if (!msm_pcie_dev_tbl[j].bdf) {
+						msm_pcie_dev_tbl[j].bdf = bdf;
+						break;
+					}
+				dev->pcidev_table[i].bdf = bdf;
+				if ((!dev->bridge_found) && (i > 0))
+					dev->bridge_found = true;
+			}
+			if (dev->pcidev_table[i].bdf == bdf) {
+				dev->ep_shadow[i][word_offset / 4] = wr_val;
+				break;
+			}
+		}
+	}
+}
+
+static inline int msm_pcie_oper_conf(struct pci_bus *bus, u32 devfn, int oper,
+				     int where, int size, u32 *val)
+{
+	uint32_t word_offset, byte_offset, mask;
+	uint32_t rd_val, wr_val;
+	struct msm_pcie_dev_t *dev;
+	void __iomem *config_base;
+	bool rc = false;
+	u32 rc_idx;
+	int rv = 0;
+	u32 bdf = BDF_OFFSET(bus->number, devfn);
+	int i;
+
+	dev = PCIE_BUS_PRIV_DATA(bus);
+
+	if (!dev) {
+		pr_err("PCIe: No device found for this bus.\n");
+		*val = ~0;
+		rv = PCIBIOS_DEVICE_NOT_FOUND;
+		goto out;
+	}
+
+	rc_idx = dev->rc_idx;
+	rc = (bus->number == 0);
+
+	spin_lock_irqsave(&dev->cfg_lock, dev->irqsave_flags);
+
+	if (!dev->cfg_access) {
+		PCIE_DBG3(dev,
+			"Access denied for RC%d %d:0x%02x + 0x%04x[%d]\n",
+			rc_idx, bus->number, devfn, where, size);
+		*val = ~0;
+		rv = PCIBIOS_DEVICE_NOT_FOUND;
+		goto unlock;
+	}
+
+	if (rc && (devfn != 0)) {
+		PCIE_DBG3(dev, "RC%d invalid %s - bus %d devfn %d\n", rc_idx,
+			 (oper == RD) ? "rd" : "wr", bus->number, devfn);
+		*val = ~0;
+		rv = PCIBIOS_DEVICE_NOT_FOUND;
+		goto unlock;
+	}
+
+	if (dev->link_status != MSM_PCIE_LINK_ENABLED) {
+		PCIE_DBG3(dev,
+			"Access to RC%d %d:0x%02x + 0x%04x[%d] is denied because link is down\n",
+			rc_idx, bus->number, devfn, where, size);
+		*val = ~0;
+		rv = PCIBIOS_DEVICE_NOT_FOUND;
+		goto unlock;
+	}
+
+	/* check if the link is up for endpoint */
+	if (!rc && !msm_pcie_is_link_up(dev)) {
+		PCIE_ERR(dev,
+			"PCIe: RC%d %s fail, link down - bus %d devfn %d\n",
+				rc_idx, (oper == RD) ? "rd" : "wr",
+				bus->number, devfn);
+			*val = ~0;
+			rv = PCIBIOS_DEVICE_NOT_FOUND;
+			goto unlock;
+	}
+
+	if (!rc && !dev->enumerated)
+		msm_pcie_cfg_bdf(dev, bus->number, devfn);
+
+	word_offset = where & ~0x3;
+	byte_offset = where & 0x3;
+	mask = (~0 >> (8 * (4 - size))) << (8 * byte_offset);
+
+	if (rc || !dev->enumerated) {
+		config_base = rc ? dev->dm_core : dev->conf;
+	} else {
+		for (i = 0; i < MAX_DEVICE_NUM; i++) {
+			if (dev->pcidev_table[i].bdf == bdf) {
+				config_base = dev->pcidev_table[i].conf_base;
+				break;
+			}
+		}
+		if (i == MAX_DEVICE_NUM) {
+			*val = ~0;
+			rv = PCIBIOS_DEVICE_NOT_FOUND;
+			goto unlock;
+		}
+	}
+
+	rd_val = readl_relaxed(config_base + word_offset);
+
+	if (oper == RD) {
+		*val = ((rd_val & mask) >> (8 * byte_offset));
+		PCIE_DBG3(dev,
+			"RC%d %d:0x%02x + 0x%04x[%d] -> 0x%08x; rd 0x%08x\n",
+			rc_idx, bus->number, devfn, where, size, *val, rd_val);
+	} else {
+		wr_val = (rd_val & ~mask) |
+				((*val << (8 * byte_offset)) & mask);
+
+		if ((bus->number == 0) && (where == 0x3c))
+			wr_val = wr_val | (3 << 16);
+
+		writel_relaxed(wr_val, config_base + word_offset);
+		wmb(); /* ensure config data is written to hardware register */
+
+		if (rd_val == PCIE_LINK_DOWN)
+			PCIE_ERR(dev,
+				"Read of RC%d %d:0x%02x + 0x%04x[%d] is all FFs\n",
+				rc_idx, bus->number, devfn, where, size);
+		else if (dev->shadow_en)
+			msm_pcie_save_shadow(dev, word_offset, wr_val, bdf, rc);
+
+		PCIE_DBG3(dev,
+			"RC%d %d:0x%02x + 0x%04x[%d] <- 0x%08x; rd 0x%08x val 0x%08x\n",
+			rc_idx, bus->number, devfn, where, size,
+			wr_val, rd_val, *val);
+	}
+
+unlock:
+	spin_unlock_irqrestore(&dev->cfg_lock, dev->irqsave_flags);
+out:
+	return rv;
+}
+
+static int msm_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
+			    int size, u32 *val)
+{
+	int ret = msm_pcie_oper_conf(bus, devfn, RD, where, size, val);
+
+	if ((bus->number == 0) && (where == PCI_CLASS_REVISION)) {
+		*val = (*val & 0xff) | (PCI_CLASS_BRIDGE_PCI << 16);
+		PCIE_GEN_DBG("change class for RC:0x%x\n", *val);
+	}
+
+	return ret;
+}
+
+static int msm_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
+			    int where, int size, u32 val)
+{
+	return msm_pcie_oper_conf(bus, devfn, WR, where, size, &val);
+}
+
+static struct pci_ops msm_pcie_ops = {
+	.read = msm_pcie_rd_conf,
+	.write = msm_pcie_wr_conf,
+};
+
+static int msm_pcie_gpio_init(struct msm_pcie_dev_t *dev)
+{
+	int rc = 0, i;
+	struct msm_pcie_gpio_info_t *info;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	for (i = 0; i < dev->gpio_n; i++) {
+		info = &dev->gpio[i];
+
+		if (!info->num)
+			continue;
+
+		rc = gpio_request(info->num, info->name);
+		if (rc) {
+			PCIE_ERR(dev, "PCIe: RC%d can't get gpio %s; %d\n",
+				dev->rc_idx, info->name, rc);
+			break;
+		}
+
+		if (info->out)
+			rc = gpio_direction_output(info->num, info->init);
+		else
+			rc = gpio_direction_input(info->num);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d can't set direction for GPIO %s:%d\n",
+				dev->rc_idx, info->name, rc);
+			gpio_free(info->num);
+			break;
+		}
+	}
+
+	if (rc)
+		while (i--)
+			gpio_free(dev->gpio[i].num);
+
+	return rc;
+}
+
+static void msm_pcie_gpio_deinit(struct msm_pcie_dev_t *dev)
+{
+	int i;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	for (i = 0; i < dev->gpio_n; i++)
+		gpio_free(dev->gpio[i].num);
+}
+
+int msm_pcie_vreg_init(struct msm_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct regulator *vreg;
+	struct msm_pcie_vreg_info_t *info;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	for (i = 0; i < MSM_PCIE_MAX_VREG; i++) {
+		info = &dev->vreg[i];
+		vreg = info->hdl;
+
+		if (!vreg)
+			continue;
+
+		PCIE_DBG2(dev, "RC%d Vreg %s is being enabled\n",
+			dev->rc_idx, info->name);
+		if (info->max_v) {
+			rc = regulator_set_voltage(vreg,
+						   info->min_v, info->max_v);
+			if (rc) {
+				PCIE_ERR(dev,
+					"PCIe: RC%d can't set voltage for %s: %d\n",
+					dev->rc_idx, info->name, rc);
+				break;
+			}
+		}
+
+		if (info->opt_mode) {
+			rc = regulator_set_load(vreg, info->opt_mode);
+			if (rc < 0) {
+				PCIE_ERR(dev,
+					"PCIe: RC%d can't set mode for %s: %d\n",
+					dev->rc_idx, info->name, rc);
+				break;
+			}
+		}
+
+		rc = regulator_enable(vreg);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d can't enable regulator %s: %d\n",
+				dev->rc_idx, info->name, rc);
+			break;
+		}
+	}
+
+	if (rc)
+		while (i--) {
+			struct regulator *hdl = dev->vreg[i].hdl;
+
+			if (hdl) {
+				regulator_disable(hdl);
+				if (!strcmp(dev->vreg[i].name, "vreg-cx")) {
+					PCIE_DBG(dev,
+						"RC%d: Removing %s vote.\n",
+						dev->rc_idx,
+						dev->vreg[i].name);
+					regulator_set_voltage(hdl,
+						RPM_REGULATOR_CORNER_NONE,
+						INT_MAX);
+				}
+			}
+
+		}
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+
+	return rc;
+}
+
+static void msm_pcie_vreg_deinit(struct msm_pcie_dev_t *dev)
+{
+	int i;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	for (i = MSM_PCIE_MAX_VREG - 1; i >= 0; i--) {
+		if (dev->vreg[i].hdl) {
+			PCIE_DBG(dev, "Vreg %s is being disabled\n",
+				dev->vreg[i].name);
+			regulator_disable(dev->vreg[i].hdl);
+
+			if (!strcmp(dev->vreg[i].name, "vreg-cx")) {
+				PCIE_DBG(dev,
+					"RC%d: Removing %s vote.\n",
+					dev->rc_idx,
+					dev->vreg[i].name);
+				regulator_set_voltage(dev->vreg[i].hdl,
+					RPM_REGULATOR_CORNER_NONE,
+					INT_MAX);
+			}
+		}
+	}
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+}
+
+static int msm_pcie_clk_init(struct msm_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct msm_pcie_clk_info_t *info;
+	struct msm_pcie_reset_info_t *reset_info;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	rc = regulator_enable(dev->gdsc);
+
+	if (rc) {
+		PCIE_ERR(dev, "PCIe: fail to enable GDSC for RC%d (%s)\n",
+			dev->rc_idx, dev->pdev->name);
+		return rc;
+	}
+
+	if (dev->gdsc_smmu) {
+		rc = regulator_enable(dev->gdsc_smmu);
+
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: fail to enable SMMU GDSC for RC%d (%s)\n",
+				dev->rc_idx, dev->pdev->name);
+			return rc;
+		}
+	}
+
+	PCIE_DBG(dev, "PCIe: requesting bus vote for RC%d\n", dev->rc_idx);
+	if (dev->bus_client) {
+		rc = msm_bus_scale_client_update_request(dev->bus_client, 1);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: fail to set bus bandwidth for RC%d:%d.\n",
+				dev->rc_idx, rc);
+			return rc;
+		}
+
+		PCIE_DBG2(dev,
+			"PCIe: set bus bandwidth for RC%d.\n",
+			dev->rc_idx);
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_CLK; i++) {
+		info = &dev->clk[i];
+
+		if (!info->hdl)
+			continue;
+
+		if (info->config_mem)
+			msm_pcie_config_clock_mem(dev, info);
+
+		if (info->freq) {
+			rc = clk_set_rate(info->hdl, info->freq);
+			if (rc) {
+				PCIE_ERR(dev,
+					"PCIe: RC%d can't set rate for clk %s: %d.\n",
+					dev->rc_idx, info->name, rc);
+				break;
+			}
+
+			PCIE_DBG2(dev,
+				"PCIe: RC%d set rate for clk %s.\n",
+				dev->rc_idx, info->name);
+		}
+
+		rc = clk_prepare_enable(info->hdl);
+
+		if (rc)
+			PCIE_ERR(dev, "PCIe: RC%d failed to enable clk %s\n",
+				dev->rc_idx, info->name);
+		else
+			PCIE_DBG2(dev, "enable clk %s for RC%d.\n",
+				info->name, dev->rc_idx);
+	}
+
+	if (rc) {
+		PCIE_DBG(dev, "RC%d disable clocks for error handling.\n",
+			dev->rc_idx);
+		while (i--) {
+			struct clk *hdl = dev->clk[i].hdl;
+
+			if (hdl)
+				clk_disable_unprepare(hdl);
+		}
+
+		if (dev->gdsc_smmu)
+			regulator_disable(dev->gdsc_smmu);
+
+		regulator_disable(dev->gdsc);
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_RESET; i++) {
+		reset_info = &dev->reset[i];
+		if (reset_info->hdl) {
+			rc = reset_control_deassert(reset_info->hdl);
+			if (rc)
+				PCIE_ERR(dev,
+					"PCIe: RC%d failed to deassert reset for %s.\n",
+					dev->rc_idx, reset_info->name);
+			else
+				PCIE_DBG2(dev,
+					"PCIe: RC%d successfully deasserted reset for %s.\n",
+					dev->rc_idx, reset_info->name);
+		}
+	}
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+
+	return rc;
+}
+
+static void msm_pcie_clk_deinit(struct msm_pcie_dev_t *dev)
+{
+	int i;
+	int rc;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	for (i = 0; i < MSM_PCIE_MAX_CLK; i++)
+		if (dev->clk[i].hdl)
+			clk_disable_unprepare(dev->clk[i].hdl);
+
+	if (dev->bus_client) {
+		PCIE_DBG(dev, "PCIe: removing bus vote for RC%d\n",
+			dev->rc_idx);
+
+		rc = msm_bus_scale_client_update_request(dev->bus_client, 0);
+		if (rc)
+			PCIE_ERR(dev,
+				"PCIe: fail to relinquish bus bandwidth for RC%d:%d.\n",
+				dev->rc_idx, rc);
+		else
+			PCIE_DBG(dev,
+				"PCIe: relinquish bus bandwidth for RC%d.\n",
+				dev->rc_idx);
+	}
+
+	if (dev->gdsc_smmu)
+		regulator_disable(dev->gdsc_smmu);
+
+	regulator_disable(dev->gdsc);
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+}
+
+static int msm_pcie_pipe_clk_init(struct msm_pcie_dev_t *dev)
+{
+	int i, rc = 0;
+	struct msm_pcie_clk_info_t *info;
+	struct msm_pcie_reset_info_t *pipe_reset_info;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	for (i = 0; i < MSM_PCIE_MAX_PIPE_CLK; i++) {
+		info = &dev->pipeclk[i];
+
+		if (!info->hdl)
+			continue;
+
+
+		if (info->config_mem)
+			msm_pcie_config_clock_mem(dev, info);
+
+		if (info->freq) {
+			rc = clk_set_rate(info->hdl, info->freq);
+			if (rc) {
+				PCIE_ERR(dev,
+					"PCIe: RC%d can't set rate for clk %s: %d.\n",
+					dev->rc_idx, info->name, rc);
+				break;
+			}
+
+			PCIE_DBG2(dev,
+				"PCIe: RC%d set rate for clk %s: %d.\n",
+				dev->rc_idx, info->name, rc);
+		}
+
+		rc = clk_prepare_enable(info->hdl);
+
+		if (rc)
+			PCIE_ERR(dev, "PCIe: RC%d failed to enable clk %s.\n",
+				dev->rc_idx, info->name);
+		else
+			PCIE_DBG2(dev, "RC%d enabled pipe clk %s.\n",
+				dev->rc_idx, info->name);
+	}
+
+	if (rc) {
+		PCIE_DBG(dev, "RC%d disable pipe clocks for error handling.\n",
+			dev->rc_idx);
+		while (i--)
+			if (dev->pipeclk[i].hdl)
+				clk_disable_unprepare(dev->pipeclk[i].hdl);
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_PIPE_RESET; i++) {
+		pipe_reset_info = &dev->pipe_reset[i];
+		if (pipe_reset_info->hdl) {
+			rc = reset_control_deassert(
+					pipe_reset_info->hdl);
+			if (rc)
+				PCIE_ERR(dev,
+					"PCIe: RC%d failed to deassert pipe reset for %s.\n",
+					dev->rc_idx, pipe_reset_info->name);
+			else
+				PCIE_DBG2(dev,
+					"PCIe: RC%d successfully deasserted pipe reset for %s.\n",
+					dev->rc_idx, pipe_reset_info->name);
+		}
+	}
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+
+	return rc;
+}
+
+static void msm_pcie_pipe_clk_deinit(struct msm_pcie_dev_t *dev)
+{
+	int i;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	for (i = 0; i < MSM_PCIE_MAX_PIPE_CLK; i++)
+		if (dev->pipeclk[i].hdl)
+			clk_disable_unprepare(
+				dev->pipeclk[i].hdl);
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+}
+
+static void msm_pcie_iatu_config_all_ep(struct msm_pcie_dev_t *dev)
+{
+	int i;
+	u8 type;
+	struct msm_pcie_device_info *dev_table = dev->pcidev_table;
+
+	for (i = 0; i < MAX_DEVICE_NUM; i++) {
+		if (!dev_table[i].bdf)
+			break;
+
+		type = dev_table[i].bdf >> 24 == 0x1 ?
+			PCIE20_CTRL1_TYPE_CFG0 : PCIE20_CTRL1_TYPE_CFG1;
+
+		msm_pcie_iatu_config(dev, i, type, dev_table[i].phy_address,
+			dev_table[i].phy_address + SZ_4K - 1,
+			dev_table[i].bdf);
+	}
+}
+
+static void msm_pcie_config_controller(struct msm_pcie_dev_t *dev)
+{
+	int i;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	/*
+	 * program and enable address translation region 0 (device config
+	 * address space); region type config;
+	 * axi config address range to device config address range
+	 */
+	if (dev->enumerated) {
+		msm_pcie_iatu_config_all_ep(dev);
+	} else {
+		dev->current_bdf = 0; /* to force IATU re-config */
+		msm_pcie_cfg_bdf(dev, 1, 0);
+	}
+
+	/* configure N_FTS */
+	PCIE_DBG2(dev, "Original PCIE20_ACK_F_ASPM_CTRL_REG:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG));
+	if (!dev->n_fts)
+		msm_pcie_write_mask(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG,
+					0, BIT(15));
+	else
+		msm_pcie_write_mask(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG,
+					PCIE20_ACK_N_FTS,
+					dev->n_fts << 8);
+
+	if (dev->shadow_en)
+		dev->rc_shadow[PCIE20_ACK_F_ASPM_CTRL_REG / 4] =
+			readl_relaxed(dev->dm_core +
+			PCIE20_ACK_F_ASPM_CTRL_REG);
+
+	PCIE_DBG2(dev, "Updated PCIE20_ACK_F_ASPM_CTRL_REG:0x%x\n",
+		readl_relaxed(dev->dm_core + PCIE20_ACK_F_ASPM_CTRL_REG));
+
+	/* configure AUX clock frequency register for PCIe core */
+	if (dev->use_19p2mhz_aux_clk)
+		msm_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x14);
+	else
+		msm_pcie_write_reg(dev->dm_core, PCIE20_AUX_CLK_FREQ_REG, 0x01);
+
+	/* configure the completion timeout value for PCIe core */
+	if (dev->cpl_timeout && dev->bridge_found)
+		msm_pcie_write_reg_field(dev->dm_core,
+					PCIE20_DEVICE_CONTROL2_STATUS2,
+					0xf, dev->cpl_timeout);
+
+	/* Enable AER on RC */
+	if (dev->aer_enable) {
+		msm_pcie_write_mask(dev->dm_core + PCIE20_BRIDGE_CTRL, 0,
+						BIT(16)|BIT(17));
+		msm_pcie_write_mask(dev->dm_core +  PCIE20_CAP_DEVCTRLSTATUS, 0,
+						BIT(3)|BIT(2)|BIT(1)|BIT(0));
+
+		PCIE_DBG(dev, "RC's PCIE20_CAP_DEVCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_CAP_DEVCTRLSTATUS));
+	}
+
+	/* configure SMMU registers */
+	if (dev->smmu_exist) {
+		msm_pcie_write_reg(dev->parf,
+			PCIE20_PARF_BDF_TRANSLATE_CFG, 0);
+		msm_pcie_write_reg(dev->parf,
+			PCIE20_PARF_SID_OFFSET, 0);
+
+		if (dev->enumerated) {
+			for (i = 0; i < MAX_DEVICE_NUM; i++) {
+				if (dev->pcidev_table[i].dev &&
+					dev->pcidev_table[i].short_bdf) {
+					msm_pcie_write_reg(dev->parf,
+						PCIE20_PARF_BDF_TRANSLATE_N +
+						dev->pcidev_table[i].short_bdf
+						* 4,
+						dev->pcidev_table[i].bdf >> 16);
+				}
+			}
+		}
+	}
+}
+
+static void msm_pcie_config_link_state(struct msm_pcie_dev_t *dev)
+{
+	u32 val;
+	u32 current_offset;
+	u32 ep_l1sub_ctrl1_offset = 0;
+	u32 ep_l1sub_cap_reg1_offset = 0;
+	u32 ep_link_cap_offset = 0;
+	u32 ep_link_ctrlstts_offset = 0;
+	u32 ep_dev_ctrl2stts2_offset = 0;
+
+	/* Enable the AUX Clock and the Core Clk to be synchronous for L1SS*/
+	if (!dev->aux_clk_sync && dev->l1ss_supported)
+		msm_pcie_write_mask(dev->parf +
+				PCIE20_PARF_SYS_CTRL, BIT(3), 0);
+
+	current_offset = readl_relaxed(dev->conf + PCIE_CAP_PTR_OFFSET) & 0xff;
+
+	while (current_offset) {
+		if (msm_pcie_check_align(dev, current_offset))
+			return;
+
+		val = readl_relaxed(dev->conf + current_offset);
+		if ((val & 0xff) == PCIE20_CAP_ID) {
+			ep_link_cap_offset = current_offset + 0x0c;
+			ep_link_ctrlstts_offset = current_offset + 0x10;
+			ep_dev_ctrl2stts2_offset = current_offset + 0x28;
+			break;
+		}
+		current_offset = (val >> 8) & 0xff;
+	}
+
+	if (!ep_link_cap_offset) {
+		PCIE_DBG(dev,
+			"RC%d endpoint does not support PCIe capability registers\n",
+			dev->rc_idx);
+		return;
+	}
+
+	PCIE_DBG(dev,
+		"RC%d: ep_link_cap_offset: 0x%x\n",
+		dev->rc_idx, ep_link_cap_offset);
+
+	if (dev->common_clk_en) {
+		msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
+					0, BIT(6));
+
+		msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
+					0, BIT(6));
+
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+				readl_relaxed(dev->dm_core +
+					PCIE20_CAP_LINKCTRLSTATUS);
+
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+				readl_relaxed(dev->conf +
+					ep_link_ctrlstts_offset);
+		}
+
+		PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
+	}
+
+	if (dev->clk_power_manage_en) {
+		val = readl_relaxed(dev->conf + ep_link_cap_offset);
+		if (val & BIT(18)) {
+			msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
+						0, BIT(8));
+
+			if (dev->shadow_en)
+				dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+					readl_relaxed(dev->conf +
+						ep_link_ctrlstts_offset);
+
+			PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
+				readl_relaxed(dev->conf +
+					ep_link_ctrlstts_offset));
+		}
+	}
+
+	if (dev->l0s_supported) {
+		msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
+					0, BIT(0));
+		msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
+					0, BIT(0));
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+						readl_relaxed(dev->dm_core +
+						PCIE20_CAP_LINKCTRLSTATUS);
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+						readl_relaxed(dev->conf +
+						ep_link_ctrlstts_offset);
+		}
+		PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
+	}
+
+	if (dev->l1_supported) {
+		msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_LINKCTRLSTATUS,
+					0, BIT(1));
+		msm_pcie_write_mask(dev->conf + ep_link_ctrlstts_offset,
+					0, BIT(1));
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_CAP_LINKCTRLSTATUS / 4] =
+						readl_relaxed(dev->dm_core +
+						PCIE20_CAP_LINKCTRLSTATUS);
+			dev->ep_shadow[0][ep_link_ctrlstts_offset / 4] =
+						readl_relaxed(dev->conf +
+						ep_link_ctrlstts_offset);
+		}
+		PCIE_DBG2(dev, "RC's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_CAP_LINKCTRLSTATUS));
+		PCIE_DBG2(dev, "EP's CAP_LINKCTRLSTATUS:0x%x\n",
+			readl_relaxed(dev->conf + ep_link_ctrlstts_offset));
+	}
+
+	if (dev->l1ss_supported) {
+		current_offset = PCIE_EXT_CAP_OFFSET;
+		while (current_offset) {
+			if (msm_pcie_check_align(dev, current_offset))
+				return;
+
+			val = readl_relaxed(dev->conf + current_offset);
+			if ((val & 0xffff) == L1SUB_CAP_ID) {
+				ep_l1sub_cap_reg1_offset = current_offset + 0x4;
+				ep_l1sub_ctrl1_offset = current_offset + 0x8;
+				break;
+			}
+			current_offset = val >> 20;
+		}
+		if (!ep_l1sub_ctrl1_offset) {
+			PCIE_DBG(dev,
+				"RC%d endpoint does not support l1ss registers\n",
+				dev->rc_idx);
+			return;
+		}
+
+		val = readl_relaxed(dev->conf + ep_l1sub_cap_reg1_offset);
+
+		PCIE_DBG2(dev, "EP's L1SUB_CAPABILITY_REG_1: 0x%x\n", val);
+		PCIE_DBG2(dev, "RC%d: ep_l1sub_ctrl1_offset: 0x%x\n",
+				dev->rc_idx, ep_l1sub_ctrl1_offset);
+
+		val &= 0xf;
+
+		msm_pcie_write_reg_field(dev->dm_core, PCIE20_L1SUB_CONTROL1,
+					0xf, val);
+		msm_pcie_write_mask(dev->dm_core +
+					PCIE20_DEVICE_CONTROL2_STATUS2,
+					0, BIT(10));
+		msm_pcie_write_reg_field(dev->conf, ep_l1sub_ctrl1_offset,
+					0xf, val);
+		msm_pcie_write_mask(dev->conf + ep_dev_ctrl2stts2_offset,
+					0, BIT(10));
+		if (dev->shadow_en) {
+			dev->rc_shadow[PCIE20_L1SUB_CONTROL1 / 4] =
+					readl_relaxed(dev->dm_core +
+					PCIE20_L1SUB_CONTROL1);
+			dev->rc_shadow[PCIE20_DEVICE_CONTROL2_STATUS2 / 4] =
+					readl_relaxed(dev->dm_core +
+					PCIE20_DEVICE_CONTROL2_STATUS2);
+			dev->ep_shadow[0][ep_l1sub_ctrl1_offset / 4] =
+					readl_relaxed(dev->conf +
+					ep_l1sub_ctrl1_offset);
+			dev->ep_shadow[0][ep_dev_ctrl2stts2_offset / 4] =
+					readl_relaxed(dev->conf +
+					ep_dev_ctrl2stts2_offset);
+		}
+		PCIE_DBG2(dev, "RC's L1SUB_CONTROL1:0x%x\n",
+			readl_relaxed(dev->dm_core + PCIE20_L1SUB_CONTROL1));
+		PCIE_DBG2(dev, "RC's DEVICE_CONTROL2_STATUS2:0x%x\n",
+			readl_relaxed(dev->dm_core +
+			PCIE20_DEVICE_CONTROL2_STATUS2));
+		PCIE_DBG2(dev, "EP's L1SUB_CONTROL1:0x%x\n",
+			readl_relaxed(dev->conf + ep_l1sub_ctrl1_offset));
+		PCIE_DBG2(dev, "EP's DEVICE_CONTROL2_STATUS2:0x%x\n",
+			readl_relaxed(dev->conf +
+			ep_dev_ctrl2stts2_offset));
+	}
+}
+
+void msm_pcie_config_msi_controller(struct msm_pcie_dev_t *dev)
+{
+	int i;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	/* program MSI controller and enable all interrupts */
+	writel_relaxed(MSM_PCIE_MSI_PHY, dev->dm_core + PCIE20_MSI_CTRL_ADDR);
+	writel_relaxed(0, dev->dm_core + PCIE20_MSI_CTRL_UPPER_ADDR);
+
+	for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++)
+		writel_relaxed(~0, dev->dm_core +
+			       PCIE20_MSI_CTRL_INTR_EN + (i * 12));
+
+	/* ensure that hardware is configured before proceeding */
+	wmb();
+}
+
+static int msm_pcie_get_resources(struct msm_pcie_dev_t *dev,
+					struct platform_device *pdev)
+{
+	int i, len, cnt, ret = 0, size = 0;
+	struct msm_pcie_vreg_info_t *vreg_info;
+	struct msm_pcie_gpio_info_t *gpio_info;
+	struct msm_pcie_clk_info_t  *clk_info;
+	struct resource *res;
+	struct msm_pcie_res_info_t *res_info;
+	struct msm_pcie_irq_info_t *irq_info;
+	struct msm_pcie_irq_info_t *msi_info;
+	struct msm_pcie_reset_info_t *reset_info;
+	struct msm_pcie_reset_info_t *pipe_reset_info;
+	char prop_name[MAX_PROP_SIZE];
+	const __be32 *prop;
+	u32 *clkfreq = NULL;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	cnt = of_property_count_strings((&pdev->dev)->of_node,
+			"clock-names");
+	if (cnt > 0) {
+		clkfreq = kzalloc((MSM_PCIE_MAX_CLK + MSM_PCIE_MAX_PIPE_CLK) *
+					sizeof(*clkfreq), GFP_KERNEL);
+		if (!clkfreq) {
+			PCIE_ERR(dev, "PCIe: memory alloc failed for RC%d\n",
+					dev->rc_idx);
+			return -ENOMEM;
+		}
+		ret = of_property_read_u32_array(
+			(&pdev->dev)->of_node,
+			"max-clock-frequency-hz", clkfreq, cnt);
+		if (ret) {
+			PCIE_ERR(dev,
+				"PCIe: invalid max-clock-frequency-hz property for RC%d:%d\n",
+				dev->rc_idx, ret);
+			goto out;
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_VREG; i++) {
+		vreg_info = &dev->vreg[i];
+		vreg_info->hdl =
+				devm_regulator_get(&pdev->dev, vreg_info->name);
+
+		if (PTR_ERR(vreg_info->hdl) == -EPROBE_DEFER) {
+			PCIE_DBG(dev, "EPROBE_DEFER for VReg:%s\n",
+				vreg_info->name);
+			ret = PTR_ERR(vreg_info->hdl);
+			goto out;
+		}
+
+		if (IS_ERR(vreg_info->hdl)) {
+			if (vreg_info->required) {
+				PCIE_DBG(dev, "Vreg %s doesn't exist\n",
+					vreg_info->name);
+				ret = PTR_ERR(vreg_info->hdl);
+				goto out;
+			} else {
+				PCIE_DBG(dev,
+					"Optional Vreg %s doesn't exist\n",
+					vreg_info->name);
+				vreg_info->hdl = NULL;
+			}
+		} else {
+			dev->vreg_n++;
+			snprintf(prop_name, MAX_PROP_SIZE,
+				"qcom,%s-voltage-level", vreg_info->name);
+			prop = of_get_property((&pdev->dev)->of_node,
+						prop_name, &len);
+			if (!prop || (len != (3 * sizeof(__be32)))) {
+				PCIE_DBG(dev, "%s %s property\n",
+					prop ? "invalid format" :
+					"no", prop_name);
+			} else {
+				vreg_info->max_v = be32_to_cpup(&prop[0]);
+				vreg_info->min_v = be32_to_cpup(&prop[1]);
+				vreg_info->opt_mode =
+					be32_to_cpup(&prop[2]);
+			}
+		}
+	}
+
+	dev->gdsc = devm_regulator_get(&pdev->dev, "gdsc-vdd");
+
+	if (IS_ERR(dev->gdsc)) {
+		PCIE_ERR(dev, "PCIe: RC%d Failed to get %s GDSC:%ld\n",
+			dev->rc_idx, dev->pdev->name, PTR_ERR(dev->gdsc));
+		if (PTR_ERR(dev->gdsc) == -EPROBE_DEFER)
+			PCIE_DBG(dev, "PCIe: EPROBE_DEFER for %s GDSC\n",
+					dev->pdev->name);
+		ret = PTR_ERR(dev->gdsc);
+		goto out;
+	}
+
+	dev->gdsc_smmu = devm_regulator_get(&pdev->dev, "gdsc-smmu");
+
+	if (IS_ERR(dev->gdsc_smmu)) {
+		PCIE_DBG(dev, "PCIe: RC%d SMMU GDSC does not exist",
+			dev->rc_idx);
+		dev->gdsc_smmu = NULL;
+	}
+
+	dev->gpio_n = 0;
+	for (i = 0; i < MSM_PCIE_MAX_GPIO; i++) {
+		gpio_info = &dev->gpio[i];
+		ret = of_get_named_gpio((&pdev->dev)->of_node,
+					gpio_info->name, 0);
+		if (ret >= 0) {
+			gpio_info->num = ret;
+			dev->gpio_n++;
+			PCIE_DBG(dev, "GPIO num for %s is %d\n",
+				gpio_info->name, gpio_info->num);
+		} else {
+			if (gpio_info->required) {
+				PCIE_ERR(dev,
+					"Could not get required GPIO %s\n",
+					gpio_info->name);
+				goto out;
+			} else {
+				PCIE_DBG(dev,
+					"Could not get optional GPIO %s\n",
+					gpio_info->name);
+			}
+		}
+		ret = 0;
+	}
+
+	of_get_property(pdev->dev.of_node, "qcom,phy-sequence", &size);
+	if (size) {
+		dev->phy_sequence = (struct msm_pcie_phy_info_t *)
+			devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+
+		if (dev->phy_sequence) {
+			dev->phy_len =
+				size / sizeof(*dev->phy_sequence);
+
+			of_property_read_u32_array(pdev->dev.of_node,
+				"qcom,phy-sequence",
+				(unsigned int *)dev->phy_sequence,
+				size / sizeof(dev->phy_sequence->offset));
+		} else {
+			PCIE_ERR(dev,
+				"RC%d: Could not allocate memory for phy init sequence.\n",
+				dev->rc_idx);
+			ret = -ENOMEM;
+			goto out;
+		}
+	} else {
+		PCIE_DBG(dev, "RC%d: phy sequence is not present in DT\n",
+			dev->rc_idx);
+	}
+
+	of_get_property(pdev->dev.of_node, "qcom,port-phy-sequence", &size);
+	if (size) {
+		dev->port_phy_sequence = (struct msm_pcie_phy_info_t *)
+			devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+
+		if (dev->port_phy_sequence) {
+			dev->port_phy_len =
+				size / sizeof(*dev->port_phy_sequence);
+
+			of_property_read_u32_array(pdev->dev.of_node,
+				"qcom,port-phy-sequence",
+				(unsigned int *)dev->port_phy_sequence,
+				size / sizeof(dev->port_phy_sequence->offset));
+		} else {
+			PCIE_ERR(dev,
+				"RC%d: Could not allocate memory for port phy init sequence.\n",
+				dev->rc_idx);
+			ret = -ENOMEM;
+			goto out;
+		}
+	} else {
+		PCIE_DBG(dev, "RC%d: port phy sequence is not present in DT\n",
+			dev->rc_idx);
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_CLK; i++) {
+		clk_info = &dev->clk[i];
+
+		clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name);
+
+		if (IS_ERR(clk_info->hdl)) {
+			if (clk_info->required) {
+				PCIE_DBG(dev, "Clock %s isn't available:%ld\n",
+				clk_info->name, PTR_ERR(clk_info->hdl));
+				ret = PTR_ERR(clk_info->hdl);
+				goto out;
+			} else {
+				PCIE_DBG(dev, "Ignoring Clock %s\n",
+					clk_info->name);
+				clk_info->hdl = NULL;
+			}
+		} else {
+			if (clkfreq != NULL) {
+				clk_info->freq = clkfreq[i +
+					MSM_PCIE_MAX_PIPE_CLK];
+				PCIE_DBG(dev, "Freq of Clock %s is:%d\n",
+					clk_info->name, clk_info->freq);
+			}
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_PIPE_CLK; i++) {
+		clk_info = &dev->pipeclk[i];
+
+		clk_info->hdl = devm_clk_get(&pdev->dev, clk_info->name);
+
+		if (IS_ERR(clk_info->hdl)) {
+			if (clk_info->required) {
+				PCIE_DBG(dev, "Clock %s isn't available:%ld\n",
+				clk_info->name, PTR_ERR(clk_info->hdl));
+				ret = PTR_ERR(clk_info->hdl);
+				goto out;
+			} else {
+				PCIE_DBG(dev, "Ignoring Clock %s\n",
+					clk_info->name);
+				clk_info->hdl = NULL;
+			}
+		} else {
+			if (clkfreq != NULL) {
+				clk_info->freq = clkfreq[i];
+				PCIE_DBG(dev, "Freq of Clock %s is:%d\n",
+					clk_info->name, clk_info->freq);
+			}
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_RESET; i++) {
+		reset_info = &dev->reset[i];
+
+		reset_info->hdl = devm_reset_control_get(&pdev->dev,
+						reset_info->name);
+
+		if (IS_ERR(reset_info->hdl)) {
+			if (reset_info->required) {
+				PCIE_DBG(dev,
+					"Reset %s isn't available:%ld\n",
+					reset_info->name,
+					PTR_ERR(reset_info->hdl));
+
+				ret = PTR_ERR(reset_info->hdl);
+				reset_info->hdl = NULL;
+				goto out;
+			} else {
+				PCIE_DBG(dev, "Ignoring Reset %s\n",
+					reset_info->name);
+				reset_info->hdl = NULL;
+			}
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_PIPE_RESET; i++) {
+		pipe_reset_info = &dev->pipe_reset[i];
+
+		pipe_reset_info->hdl = devm_reset_control_get(&pdev->dev,
+						pipe_reset_info->name);
+
+		if (IS_ERR(pipe_reset_info->hdl)) {
+			if (pipe_reset_info->required) {
+				PCIE_DBG(dev,
+					"Pipe Reset %s isn't available:%ld\n",
+					pipe_reset_info->name,
+					PTR_ERR(pipe_reset_info->hdl));
+
+				ret = PTR_ERR(pipe_reset_info->hdl);
+				pipe_reset_info->hdl = NULL;
+				goto out;
+			} else {
+				PCIE_DBG(dev, "Ignoring Pipe Reset %s\n",
+					pipe_reset_info->name);
+				pipe_reset_info->hdl = NULL;
+			}
+		}
+	}
+
+	dev->bus_scale_table = msm_bus_cl_get_pdata(pdev);
+	if (!dev->bus_scale_table) {
+		PCIE_DBG(dev, "PCIe: No bus scale table for RC%d (%s)\n",
+			dev->rc_idx, dev->pdev->name);
+		dev->bus_client = 0;
+	} else {
+		dev->bus_client =
+			msm_bus_scale_register_client(dev->bus_scale_table);
+		if (!dev->bus_client) {
+			PCIE_ERR(dev,
+				"PCIe: Failed to register bus client for RC%d (%s)\n",
+				dev->rc_idx, dev->pdev->name);
+			msm_bus_cl_clear_pdata(dev->bus_scale_table);
+			ret = -ENODEV;
+			goto out;
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_RES; i++) {
+		res_info = &dev->res[i];
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							   res_info->name);
+
+		if (!res) {
+			PCIE_ERR(dev, "PCIe: RC%d can't get %s resource.\n",
+				dev->rc_idx, res_info->name);
+		} else {
+			PCIE_DBG(dev, "start addr for %s is %pa.\n",
+				res_info->name,	&res->start);
+
+			res_info->base = devm_ioremap(&pdev->dev,
+						res->start, resource_size(res));
+			if (!res_info->base) {
+				PCIE_ERR(dev, "PCIe: RC%d can't remap %s.\n",
+					dev->rc_idx, res_info->name);
+				ret = -ENOMEM;
+				goto out;
+			} else {
+				res_info->resource = res;
+			}
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_IRQ; i++) {
+		irq_info = &dev->irq[i];
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							   irq_info->name);
+
+		if (!res) {
+			PCIE_DBG(dev, "PCIe: RC%d can't find IRQ # for %s.\n",
+				dev->rc_idx, irq_info->name);
+		} else {
+			irq_info->num = res->start;
+			PCIE_DBG(dev, "IRQ # for %s is %d.\n", irq_info->name,
+					irq_info->num);
+		}
+	}
+
+	for (i = 0; i < MSM_PCIE_MAX_MSI; i++) {
+		msi_info = &dev->msi[i];
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							   msi_info->name);
+
+		if (!res) {
+			PCIE_DBG(dev, "PCIe: RC%d can't find IRQ # for %s.\n",
+				dev->rc_idx, msi_info->name);
+		} else {
+			msi_info->num = res->start;
+			PCIE_DBG(dev, "IRQ # for %s is %d.\n", msi_info->name,
+					msi_info->num);
+		}
+	}
+
+	/* All allocations succeeded */
+
+	if (dev->gpio[MSM_PCIE_GPIO_WAKE].num)
+		dev->wake_n = gpio_to_irq(dev->gpio[MSM_PCIE_GPIO_WAKE].num);
+	else
+		dev->wake_n = 0;
+
+	dev->parf = dev->res[MSM_PCIE_RES_PARF].base;
+	dev->phy = dev->res[MSM_PCIE_RES_PHY].base;
+	dev->elbi = dev->res[MSM_PCIE_RES_ELBI].base;
+	dev->dm_core = dev->res[MSM_PCIE_RES_DM_CORE].base;
+	dev->conf = dev->res[MSM_PCIE_RES_CONF].base;
+	dev->bars = dev->res[MSM_PCIE_RES_BARS].base;
+	dev->tcsr = dev->res[MSM_PCIE_RES_TCSR].base;
+	dev->dev_mem_res = dev->res[MSM_PCIE_RES_BARS].resource;
+	dev->dev_io_res = dev->res[MSM_PCIE_RES_IO].resource;
+	dev->dev_io_res->flags = IORESOURCE_IO;
+
+out:
+	kfree(clkfreq);
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+
+	return ret;
+}
+
+static void msm_pcie_release_resources(struct msm_pcie_dev_t *dev)
+{
+	dev->parf = NULL;
+	dev->elbi = NULL;
+	dev->dm_core = NULL;
+	dev->conf = NULL;
+	dev->bars = NULL;
+	dev->tcsr = NULL;
+	dev->dev_mem_res = NULL;
+	dev->dev_io_res = NULL;
+}
+
+int msm_pcie_enable(struct msm_pcie_dev_t *dev, u32 options)
+{
+	int ret = 0;
+	uint32_t val;
+	long int retries = 0;
+	int link_check_count = 0;
+
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	mutex_lock(&dev->setup_lock);
+
+	if (dev->link_status == MSM_PCIE_LINK_ENABLED) {
+		PCIE_ERR(dev, "PCIe: the link of RC%d is already enabled\n",
+			dev->rc_idx);
+		goto out;
+	}
+
+	/* assert PCIe reset link to keep EP in reset */
+
+	PCIE_INFO(dev, "PCIe: Assert the reset of endpoint of RC%d.\n",
+		dev->rc_idx);
+	gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+				dev->gpio[MSM_PCIE_GPIO_PERST].on);
+	usleep_range(PERST_PROPAGATION_DELAY_US_MIN,
+				 PERST_PROPAGATION_DELAY_US_MAX);
+
+	/* enable power */
+
+	if (options & PM_VREG) {
+		ret = msm_pcie_vreg_init(dev);
+		if (ret)
+			goto out;
+	}
+
+	/* enable clocks */
+	if (options & PM_CLK) {
+		ret = msm_pcie_clk_init(dev);
+		/* ensure that changes propagated to the hardware */
+		wmb();
+		if (ret)
+			goto clk_fail;
+	}
+
+	if (dev->scm_dev_id) {
+		PCIE_DBG(dev, "RC%d: restoring sec config\n", dev->rc_idx);
+		msm_pcie_restore_sec_config(dev);
+	}
+
+	/* enable PCIe clocks and resets */
+	msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, BIT(0), 0);
+
+	/* change DBI base address */
+	writel_relaxed(0, dev->parf + PCIE20_PARF_DBI_BASE_ADDR);
+
+	writel_relaxed(0x365E, dev->parf + PCIE20_PARF_SYS_CTRL);
+
+	msm_pcie_write_mask(dev->parf + PCIE20_PARF_MHI_CLOCK_RESET_CTRL,
+				0, BIT(4));
+
+	/* enable selected IRQ */
+	if (dev->irq[MSM_PCIE_INT_GLOBAL_INT].num) {
+		msm_pcie_write_reg(dev->parf, PCIE20_PARF_INT_ALL_MASK, 0);
+
+		msm_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_MASK, 0,
+					BIT(MSM_PCIE_INT_EVT_LINK_DOWN) |
+					BIT(MSM_PCIE_INT_EVT_AER_LEGACY) |
+					BIT(MSM_PCIE_INT_EVT_AER_ERR) |
+					BIT(MSM_PCIE_INT_EVT_MSI_0) |
+					BIT(MSM_PCIE_INT_EVT_MSI_1) |
+					BIT(MSM_PCIE_INT_EVT_MSI_2) |
+					BIT(MSM_PCIE_INT_EVT_MSI_3) |
+					BIT(MSM_PCIE_INT_EVT_MSI_4) |
+					BIT(MSM_PCIE_INT_EVT_MSI_5) |
+					BIT(MSM_PCIE_INT_EVT_MSI_6) |
+					BIT(MSM_PCIE_INT_EVT_MSI_7));
+
+		PCIE_DBG(dev, "PCIe: RC%d: PCIE20_PARF_INT_ALL_MASK: 0x%x\n",
+			dev->rc_idx,
+			readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK));
+	}
+
+	if (dev->dev_mem_res->end - dev->dev_mem_res->start > SZ_16M)
+		writel_relaxed(SZ_32M, dev->parf +
+			PCIE20_PARF_SLV_ADDR_SPACE_SIZE);
+	else if (dev->dev_mem_res->end - dev->dev_mem_res->start > SZ_8M)
+		writel_relaxed(SZ_16M, dev->parf +
+			PCIE20_PARF_SLV_ADDR_SPACE_SIZE);
+	else
+		writel_relaxed(SZ_8M, dev->parf +
+			PCIE20_PARF_SLV_ADDR_SPACE_SIZE);
+
+	if (dev->use_msi) {
+		PCIE_DBG(dev, "RC%d: enable WR halt.\n", dev->rc_idx);
+		val = dev->wr_halt_size ? dev->wr_halt_size :
+			readl_relaxed(dev->parf +
+				PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT);
+
+		msm_pcie_write_reg(dev->parf,
+			PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT,
+			BIT(31) | val);
+
+		PCIE_DBG(dev,
+			"RC%d: PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT: 0x%x.\n",
+			dev->rc_idx,
+			readl_relaxed(dev->parf +
+				PCIE20_PARF_AXI_MSTR_WR_ADDR_HALT));
+	}
+
+	mutex_lock(&com_phy_lock);
+	/* init PCIe PHY */
+	if (!num_rc_on)
+		pcie_phy_init(dev);
+
+	num_rc_on++;
+	mutex_unlock(&com_phy_lock);
+
+	if (options & PM_PIPE_CLK) {
+		usleep_range(PHY_STABILIZATION_DELAY_US_MIN,
+					 PHY_STABILIZATION_DELAY_US_MAX);
+		/* Enable the pipe clock */
+		ret = msm_pcie_pipe_clk_init(dev);
+		/* ensure that changes propagated to the hardware */
+		wmb();
+		if (ret)
+			goto link_fail;
+	}
+
+	PCIE_DBG(dev, "RC%d: waiting for phy ready...\n", dev->rc_idx);
+
+	do {
+		if (pcie_phy_is_ready(dev))
+			break;
+		retries++;
+		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
+					 REFCLK_STABILIZATION_DELAY_US_MAX);
+	} while (retries < PHY_READY_TIMEOUT_COUNT);
+
+	PCIE_DBG(dev, "RC%d: number of PHY retries:%ld.\n",
+		dev->rc_idx, retries);
+
+	if (pcie_phy_is_ready(dev))
+		PCIE_INFO(dev, "PCIe RC%d PHY is ready!\n", dev->rc_idx);
+	else {
+		PCIE_ERR(dev, "PCIe PHY RC%d failed to come up!\n",
+			dev->rc_idx);
+		ret = -ENODEV;
+		pcie_phy_dump(dev);
+		goto link_fail;
+	}
+
+	pcie_pcs_port_phy_init(dev);
+
+	if (dev->ep_latency)
+		usleep_range(dev->ep_latency * 1000, dev->ep_latency * 1000);
+
+	if (dev->gpio[MSM_PCIE_GPIO_EP].num)
+		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_EP].num,
+				dev->gpio[MSM_PCIE_GPIO_EP].on);
+
+	/* de-assert PCIe reset link to bring EP out of reset */
+
+	PCIE_INFO(dev, "PCIe: Release the reset of endpoint of RC%d.\n",
+		dev->rc_idx);
+	gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+				1 - dev->gpio[MSM_PCIE_GPIO_PERST].on);
+	usleep_range(dev->perst_delay_us_min, dev->perst_delay_us_max);
+
+	/* set max tlp read size */
+	msm_pcie_write_reg_field(dev->dm_core, PCIE20_DEVICE_CONTROL_STATUS,
+				0x7000, dev->tlp_rd_size);
+
+	/* enable link training */
+	msm_pcie_write_mask(dev->parf + PCIE20_PARF_LTSSM, 0, BIT(8));
+
+	PCIE_DBG(dev, "%s", "check if link is up\n");
+
+	/* Wait for up to 100ms for the link to come up */
+	do {
+		usleep_range(LINK_UP_TIMEOUT_US_MIN, LINK_UP_TIMEOUT_US_MAX);
+		val =  readl_relaxed(dev->elbi + PCIE20_ELBI_SYS_STTS);
+	} while ((!(val & XMLH_LINK_UP) ||
+		!msm_pcie_confirm_linkup(dev, false, false, NULL))
+		&& (link_check_count++ < LINK_UP_CHECK_MAX_COUNT));
+
+	if ((val & XMLH_LINK_UP) &&
+		msm_pcie_confirm_linkup(dev, false, false, NULL)) {
+		PCIE_DBG(dev, "Link is up after %d checkings\n",
+			link_check_count);
+		PCIE_INFO(dev, "PCIe RC%d link initialized\n", dev->rc_idx);
+	} else {
+		PCIE_INFO(dev, "PCIe: Assert the reset of endpoint of RC%d.\n",
+			dev->rc_idx);
+		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+			dev->gpio[MSM_PCIE_GPIO_PERST].on);
+		PCIE_ERR(dev, "PCIe RC%d link initialization failed\n",
+			dev->rc_idx);
+		ret = -1;
+		goto link_fail;
+	}
+
+	msm_pcie_config_controller(dev);
+
+	if (!dev->msi_gicm_addr)
+		msm_pcie_config_msi_controller(dev);
+
+	msm_pcie_config_link_state(dev);
+
+	dev->link_status = MSM_PCIE_LINK_ENABLED;
+	dev->power_on = true;
+	dev->suspending = false;
+	dev->link_turned_on_counter++;
+
+	goto out;
+
+link_fail:
+	if (dev->gpio[MSM_PCIE_GPIO_EP].num)
+		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_EP].num,
+				1 - dev->gpio[MSM_PCIE_GPIO_EP].on);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_SW_RESET(dev->rc_idx, dev->common_phy), 0x1);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx, dev->common_phy), 0);
+
+	mutex_lock(&com_phy_lock);
+	num_rc_on--;
+	if (!num_rc_on && dev->common_phy) {
+		PCIE_DBG(dev, "PCIe: RC%d is powering down the common phy\n",
+			dev->rc_idx);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_SW_RESET, 0x1);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_POWER_DOWN_CONTROL, 0);
+	}
+	mutex_unlock(&com_phy_lock);
+
+	msm_pcie_pipe_clk_deinit(dev);
+	msm_pcie_clk_deinit(dev);
+clk_fail:
+	msm_pcie_vreg_deinit(dev);
+out:
+	mutex_unlock(&dev->setup_lock);
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+
+	return ret;
+}
+
+void msm_pcie_disable(struct msm_pcie_dev_t *dev, u32 options)
+{
+	PCIE_DBG(dev, "RC%d: entry\n", dev->rc_idx);
+
+	mutex_lock(&dev->setup_lock);
+
+	if (!dev->power_on) {
+		PCIE_DBG(dev,
+			"PCIe: the link of RC%d is already power down.\n",
+			dev->rc_idx);
+		mutex_unlock(&dev->setup_lock);
+		return;
+	}
+
+	dev->link_status = MSM_PCIE_LINK_DISABLED;
+	dev->power_on = false;
+	dev->link_turned_off_counter++;
+
+	PCIE_INFO(dev, "PCIe: Assert the reset of endpoint of RC%d.\n",
+		dev->rc_idx);
+
+	gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+				dev->gpio[MSM_PCIE_GPIO_PERST].on);
+
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_SW_RESET(dev->rc_idx, dev->common_phy), 0x1);
+	msm_pcie_write_reg(dev->phy,
+		PCIE_N_POWER_DOWN_CONTROL(dev->rc_idx, dev->common_phy), 0);
+
+	mutex_lock(&com_phy_lock);
+	num_rc_on--;
+	if (!num_rc_on && dev->common_phy) {
+		PCIE_DBG(dev, "PCIe: RC%d is powering down the common phy\n",
+			dev->rc_idx);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_SW_RESET, 0x1);
+		msm_pcie_write_reg(dev->phy, PCIE_COM_POWER_DOWN_CONTROL, 0);
+	}
+	mutex_unlock(&com_phy_lock);
+
+	if (options & PM_CLK) {
+		msm_pcie_write_mask(dev->parf + PCIE20_PARF_PHY_CTRL, 0,
+					BIT(0));
+		msm_pcie_clk_deinit(dev);
+	}
+
+	if (options & PM_VREG)
+		msm_pcie_vreg_deinit(dev);
+
+	if (options & PM_PIPE_CLK)
+		msm_pcie_pipe_clk_deinit(dev);
+
+	if (dev->gpio[MSM_PCIE_GPIO_EP].num)
+		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_EP].num,
+				1 - dev->gpio[MSM_PCIE_GPIO_EP].on);
+
+	mutex_unlock(&dev->setup_lock);
+
+	PCIE_DBG(dev, "RC%d: exit\n", dev->rc_idx);
+}
+
+static void msm_pcie_config_ep_aer(struct msm_pcie_dev_t *dev,
+				struct msm_pcie_device_info *ep_dev_info)
+{
+	u32 val;
+	void __iomem *ep_base = ep_dev_info->conf_base;
+	u32 current_offset = readl_relaxed(ep_base + PCIE_CAP_PTR_OFFSET) &
+						0xff;
+
+	while (current_offset) {
+		if (msm_pcie_check_align(dev, current_offset))
+			return;
+
+		val = readl_relaxed(ep_base + current_offset);
+		if ((val & 0xff) == PCIE20_CAP_ID) {
+			ep_dev_info->dev_ctrlstts_offset =
+				current_offset + 0x8;
+			break;
+		}
+		current_offset = (val >> 8) & 0xff;
+	}
+
+	if (!ep_dev_info->dev_ctrlstts_offset) {
+		PCIE_DBG(dev,
+			"RC%d endpoint does not support PCIe cap registers\n",
+			dev->rc_idx);
+		return;
+	}
+
+	PCIE_DBG2(dev, "RC%d: EP dev_ctrlstts_offset: 0x%x\n",
+		dev->rc_idx, ep_dev_info->dev_ctrlstts_offset);
+
+	/* Enable AER on EP */
+	msm_pcie_write_mask(ep_base + ep_dev_info->dev_ctrlstts_offset, 0,
+				BIT(3)|BIT(2)|BIT(1)|BIT(0));
+
+	PCIE_DBG(dev, "EP's PCIE20_CAP_DEVCTRLSTATUS:0x%x\n",
+		readl_relaxed(ep_base + ep_dev_info->dev_ctrlstts_offset));
+}
+
+static int msm_pcie_config_device_table(struct device *dev, void *pdev)
+{
+	struct pci_dev *pcidev = to_pci_dev(dev);
+	struct msm_pcie_dev_t *pcie_dev = (struct msm_pcie_dev_t *) pdev;
+	struct msm_pcie_device_info *dev_table_t = pcie_dev->pcidev_table;
+	struct resource *axi_conf = pcie_dev->res[MSM_PCIE_RES_CONF].resource;
+	int ret = 0;
+	u32 rc_idx = pcie_dev->rc_idx;
+	u32 i, index;
+	u32 bdf = 0;
+	u8 type;
+	u32 h_type;
+	u32 bme;
+
+	if (!pcidev) {
+		PCIE_ERR(pcie_dev,
+			"PCIe: Did not find PCI device in list for RC%d.\n",
+			pcie_dev->rc_idx);
+		return -ENODEV;
+	}
+
+	PCIE_DBG(pcie_dev,
+		"PCI device found: vendor-id:0x%x device-id:0x%x\n",
+		pcidev->vendor, pcidev->device);
+
+	if (!pcidev->bus->number)
+		return ret;
+
+	bdf = BDF_OFFSET(pcidev->bus->number, pcidev->devfn);
+	type = pcidev->bus->number == 1 ?
+		PCIE20_CTRL1_TYPE_CFG0 : PCIE20_CTRL1_TYPE_CFG1;
+
+	for (i = 0; i < (MAX_RC_NUM * MAX_DEVICE_NUM); i++) {
+		if (msm_pcie_dev_tbl[i].bdf == bdf &&
+			!msm_pcie_dev_tbl[i].dev) {
+			for (index = 0; index < MAX_DEVICE_NUM; index++) {
+				if (dev_table_t[index].bdf == bdf) {
+					msm_pcie_dev_tbl[i].dev = pcidev;
+					msm_pcie_dev_tbl[i].domain = rc_idx;
+					msm_pcie_dev_tbl[i].conf_base =
+						pcie_dev->conf + index * SZ_4K;
+					msm_pcie_dev_tbl[i].phy_address =
+						axi_conf->start + index * SZ_4K;
+
+					dev_table_t[index].dev = pcidev;
+					dev_table_t[index].domain = rc_idx;
+					dev_table_t[index].conf_base =
+						pcie_dev->conf + index * SZ_4K;
+					dev_table_t[index].phy_address =
+						axi_conf->start + index * SZ_4K;
+
+					msm_pcie_iatu_config(pcie_dev, index,
+						type,
+						dev_table_t[index].phy_address,
+						dev_table_t[index].phy_address
+						+ SZ_4K - 1,
+						bdf);
+
+					h_type = readl_relaxed(
+						dev_table_t[index].conf_base +
+						PCIE20_HEADER_TYPE);
+
+					bme = readl_relaxed(
+						dev_table_t[index].conf_base +
+						PCIE20_COMMAND_STATUS);
+
+					if (h_type & (1 << 16)) {
+						pci_write_config_dword(pcidev,
+							PCIE20_COMMAND_STATUS,
+							bme | 0x06);
+					} else {
+						pcie_dev->num_ep++;
+						dev_table_t[index].registered =
+							false;
+					}
+
+					if (pcie_dev->num_ep > 1)
+						pcie_dev->pending_ep_reg = true;
+
+					msm_pcie_config_ep_aer(pcie_dev,
+						&dev_table_t[index]);
+
+					break;
+				}
+			}
+			if (index == MAX_DEVICE_NUM) {
+				PCIE_ERR(pcie_dev,
+					"RC%d PCI device table is full.\n",
+					rc_idx);
+				ret = index;
+			} else {
+				break;
+			}
+		} else if (msm_pcie_dev_tbl[i].bdf == bdf &&
+			pcidev == msm_pcie_dev_tbl[i].dev) {
+			break;
+		}
+	}
+	if (i == MAX_RC_NUM * MAX_DEVICE_NUM) {
+		PCIE_ERR(pcie_dev,
+			"Global PCI device table is full: %d elements.\n",
+			i);
+		PCIE_ERR(pcie_dev,
+			"Bus number is 0x%x\nDevice number is 0x%x\n",
+			pcidev->bus->number, pcidev->devfn);
+		ret = i;
+	}
+	return ret;
+}
+
+int msm_pcie_configure_sid(struct device *dev, u32 *sid, int *domain)
+{
+	struct pci_dev *pcidev;
+	struct msm_pcie_dev_t *pcie_dev;
+	struct pci_bus *bus;
+	int i;
+	u32 bdf;
+
+	if (!dev) {
+		pr_err("%s: PCIe: endpoint device passed in is NULL\n",
+			__func__);
+		return MSM_PCIE_ERROR;
+	}
+
+	pcidev = to_pci_dev(dev);
+	if (!pcidev) {
+		pr_err("%s: PCIe: PCI device of endpoint is NULL\n",
+			__func__);
+		return MSM_PCIE_ERROR;
+	}
+
+	bus = pcidev->bus;
+	if (!bus) {
+		pr_err("%s: PCIe: Bus of PCI device is NULL\n",
+			__func__);
+		return MSM_PCIE_ERROR;
+	}
+
+	while (!pci_is_root_bus(bus))
+		bus = bus->parent;
+
+	pcie_dev = (struct msm_pcie_dev_t *)(bus->sysdata);
+	if (!pcie_dev) {
+		pr_err("%s: PCIe: Could not get PCIe structure\n",
+			__func__);
+		return MSM_PCIE_ERROR;
+	}
+
+	if (!pcie_dev->smmu_exist) {
+		PCIE_DBG(pcie_dev,
+			"PCIe: RC:%d: smmu does not exist\n",
+			pcie_dev->rc_idx);
+		return MSM_PCIE_ERROR;
+	}
+
+	PCIE_DBG(pcie_dev, "PCIe: RC%d: device address is: %p\n",
+		pcie_dev->rc_idx, dev);
+	PCIE_DBG(pcie_dev, "PCIe: RC%d: PCI device address is: %p\n",
+		pcie_dev->rc_idx, pcidev);
+
+	*domain = pcie_dev->rc_idx;
+
+	if (pcie_dev->current_short_bdf < (MAX_SHORT_BDF_NUM - 1)) {
+		pcie_dev->current_short_bdf++;
+	} else {
+		PCIE_ERR(pcie_dev,
+			"PCIe: RC%d: No more short BDF left\n",
+			pcie_dev->rc_idx);
+		return MSM_PCIE_ERROR;
+	}
+
+	bdf = BDF_OFFSET(pcidev->bus->number, pcidev->devfn);
+
+	for (i = 0; i < MAX_DEVICE_NUM; i++) {
+		if (pcie_dev->pcidev_table[i].bdf == bdf) {
+			*sid = pcie_dev->smmu_sid_base +
+				((pcie_dev->rc_idx << 4) |
+				pcie_dev->current_short_bdf);
+
+			msm_pcie_write_reg(pcie_dev->parf,
+				PCIE20_PARF_BDF_TRANSLATE_N +
+				pcie_dev->current_short_bdf * 4,
+				bdf >> 16);
+
+			pcie_dev->pcidev_table[i].sid = *sid;
+			pcie_dev->pcidev_table[i].short_bdf =
+				pcie_dev->current_short_bdf;
+			break;
+		}
+	}
+
+	if (i == MAX_DEVICE_NUM) {
+		pcie_dev->current_short_bdf--;
+		PCIE_ERR(pcie_dev,
+			"PCIe: RC%d could not find BDF:%d\n",
+			pcie_dev->rc_idx, bdf);
+		return MSM_PCIE_ERROR;
+	}
+
+	PCIE_DBG(pcie_dev,
+		"PCIe: RC%d: Device: %02x:%02x.%01x received SID %d\n",
+		pcie_dev->rc_idx,
+		bdf >> 24,
+		bdf >> 19 & 0x1f,
+		bdf >> 16 & 0x07,
+		*sid);
+
+	return 0;
+}
+EXPORT_SYMBOL(msm_pcie_configure_sid);
+
+int msm_pcie_enumerate(u32 rc_idx)
+{
+	int ret = 0, bus_ret = 0, scan_ret = 0;
+	struct msm_pcie_dev_t *dev = &msm_pcie_dev[rc_idx];
+
+	mutex_lock(&dev->enumerate_lock);
+
+	PCIE_DBG(dev, "Enumerate RC%d\n", rc_idx);
+
+	if (!dev->drv_ready) {
+		PCIE_DBG(dev, "RC%d has not been successfully probed yet\n",
+			rc_idx);
+		ret = -EPROBE_DEFER;
+		goto out;
+	}
+
+	if (!dev->enumerated) {
+		ret = msm_pcie_enable(dev, PM_ALL);
+
+		/* kick start ARM PCI configuration framework */
+		if (!ret) {
+			struct pci_dev *pcidev = NULL;
+			bool found = false;
+			struct pci_bus *bus;
+			resource_size_t iobase = 0;
+			u32 ids = readl_relaxed(msm_pcie_dev[rc_idx].dm_core);
+			u32 vendor_id = ids & 0xffff;
+			u32 device_id = (ids & 0xffff0000) >> 16;
+			LIST_HEAD(res);
+
+			PCIE_DBG(dev, "vendor-id:0x%x device_id:0x%x\n",
+					vendor_id, device_id);
+
+			ret = of_pci_get_host_bridge_resources(
+						dev->pdev->dev.of_node,
+						0, 0xff, &res, &iobase);
+			if (ret) {
+				PCIE_ERR(dev,
+					"PCIe: failed to get host bridge resources for RC%d: %d\n",
+					dev->rc_idx, ret);
+				goto out;
+			}
+
+			bus = pci_create_root_bus(&dev->pdev->dev, 0,
+						&msm_pcie_ops,
+						msm_pcie_setup_sys_data(dev),
+						&res);
+			if (!bus) {
+				PCIE_ERR(dev,
+					"PCIe: failed to create root bus for RC%d\n",
+					dev->rc_idx);
+				ret = -ENOMEM;
+				goto out;
+			}
+
+			scan_ret = pci_scan_child_bus(bus);
+			PCIE_DBG(dev,
+				"PCIe: RC%d: The max subordinate bus number discovered is %d\n",
+				dev->rc_idx, ret);
+
+			msm_pcie_fixup_irqs(dev);
+			pci_assign_unassigned_bus_resources(bus);
+			pci_bus_add_devices(bus);
+
+			dev->enumerated = true;
+
+			msm_pcie_write_mask(dev->dm_core +
+				PCIE20_COMMAND_STATUS, 0, BIT(2)|BIT(1));
+
+			if (dev->cpl_timeout && dev->bridge_found)
+				msm_pcie_write_reg_field(dev->dm_core,
+					PCIE20_DEVICE_CONTROL2_STATUS2,
+					0xf, dev->cpl_timeout);
+
+			if (dev->shadow_en) {
+				u32 val = readl_relaxed(dev->dm_core +
+						PCIE20_COMMAND_STATUS);
+				PCIE_DBG(dev, "PCIE20_COMMAND_STATUS:0x%x\n",
+					val);
+				dev->rc_shadow[PCIE20_COMMAND_STATUS / 4] = val;
+			}
+
+			do {
+				pcidev = pci_get_device(vendor_id,
+					device_id, pcidev);
+				if (pcidev && (&msm_pcie_dev[rc_idx] ==
+					(struct msm_pcie_dev_t *)
+					PCIE_BUS_PRIV_DATA(pcidev->bus))) {
+					msm_pcie_dev[rc_idx].dev = pcidev;
+					found = true;
+					PCIE_DBG(&msm_pcie_dev[rc_idx],
+						"PCI device is found for RC%d\n",
+						rc_idx);
+				}
+			} while (!found && pcidev);
+
+			if (!pcidev) {
+				PCIE_ERR(dev,
+					"PCIe: Did not find PCI device for RC%d.\n",
+					dev->rc_idx);
+				ret = -ENODEV;
+				goto out;
+			}
+
+			bus_ret = bus_for_each_dev(&pci_bus_type, NULL, dev,
+					&msm_pcie_config_device_table);
+
+			if (bus_ret) {
+				PCIE_ERR(dev,
+					"PCIe: Failed to set up device table for RC%d\n",
+					dev->rc_idx);
+				ret = -ENODEV;
+				goto out;
+			}
+		} else {
+			PCIE_ERR(dev, "PCIe: failed to enable RC%d.\n",
+				dev->rc_idx);
+		}
+	} else {
+		PCIE_ERR(dev, "PCIe: RC%d has already been enumerated.\n",
+			dev->rc_idx);
+	}
+
+out:
+	mutex_unlock(&dev->enumerate_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_enumerate);
+
+static void msm_pcie_notify_client(struct msm_pcie_dev_t *dev,
+					enum msm_pcie_event event)
+{
+	if (dev->event_reg && dev->event_reg->callback &&
+		(dev->event_reg->events & event)) {
+		struct msm_pcie_notify *notify = &dev->event_reg->notify;
+
+		notify->event = event;
+		notify->user = dev->event_reg->user;
+		PCIE_DBG(dev, "PCIe: callback RC%d for event %d\n",
+			dev->rc_idx, event);
+		dev->event_reg->callback(notify);
+
+		if ((dev->event_reg->options & MSM_PCIE_CONFIG_NO_RECOVERY) &&
+				(event == MSM_PCIE_EVENT_LINKDOWN)) {
+			dev->user_suspend = true;
+			PCIE_DBG(dev,
+				"PCIe: Client of RC%d will recover the link later.\n",
+				dev->rc_idx);
+			return;
+		}
+	} else {
+		PCIE_DBG2(dev,
+			"PCIe: Client of RC%d does not have registration for event %d\n",
+			dev->rc_idx, event);
+	}
+}
+
+static void handle_wake_func(struct work_struct *work)
+{
+	int i, ret;
+	struct msm_pcie_dev_t *dev = container_of(work, struct msm_pcie_dev_t,
+					handle_wake_work);
+
+	PCIE_DBG(dev, "PCIe: Wake work for RC%d\n", dev->rc_idx);
+
+	mutex_lock(&dev->recovery_lock);
+
+	if (!dev->enumerated) {
+		PCIE_DBG(dev,
+			"PCIe: Start enumeration for RC%d upon the wake from endpoint.\n",
+			dev->rc_idx);
+
+		ret = msm_pcie_enumerate(dev->rc_idx);
+		if (ret) {
+			PCIE_ERR(dev,
+				"PCIe: failed to enable RC%d upon wake request from the device.\n",
+				dev->rc_idx);
+			goto out;
+		}
+
+		if (dev->num_ep > 1) {
+			for (i = 0; i < MAX_DEVICE_NUM; i++) {
+				dev->event_reg = dev->pcidev_table[i].event_reg;
+
+				if ((dev->link_status == MSM_PCIE_LINK_ENABLED)
+					&& dev->event_reg &&
+					dev->event_reg->callback &&
+					(dev->event_reg->events &
+					MSM_PCIE_EVENT_LINKUP)) {
+					struct msm_pcie_notify *notify =
+						&dev->event_reg->notify;
+					notify->event = MSM_PCIE_EVENT_LINKUP;
+					notify->user = dev->event_reg->user;
+					PCIE_DBG(dev,
+						"PCIe: Linkup callback for RC%d after enumeration is successful in wake IRQ handling\n",
+						dev->rc_idx);
+					dev->event_reg->callback(notify);
+				}
+			}
+		} else {
+			if ((dev->link_status == MSM_PCIE_LINK_ENABLED) &&
+				dev->event_reg && dev->event_reg->callback &&
+				(dev->event_reg->events &
+				MSM_PCIE_EVENT_LINKUP)) {
+				struct msm_pcie_notify *notify =
+						&dev->event_reg->notify;
+				notify->event = MSM_PCIE_EVENT_LINKUP;
+				notify->user = dev->event_reg->user;
+				PCIE_DBG(dev,
+					"PCIe: Linkup callback for RC%d after enumeration is successful in wake IRQ handling\n",
+					dev->rc_idx);
+				dev->event_reg->callback(notify);
+			} else {
+				PCIE_DBG(dev,
+					"PCIe: Client of RC%d does not have registration for linkup event.\n",
+					dev->rc_idx);
+			}
+		}
+		goto out;
+	} else {
+		PCIE_ERR(dev,
+			"PCIe: The enumeration for RC%d has already been done.\n",
+			dev->rc_idx);
+		goto out;
+	}
+
+out:
+	mutex_unlock(&dev->recovery_lock);
+}
+
+static irqreturn_t handle_aer_irq(int irq, void *data)
+{
+	struct msm_pcie_dev_t *dev = data;
+
+	int corr_val = 0, uncorr_val = 0, rc_err_status = 0;
+	int ep_corr_val = 0, ep_uncorr_val = 0;
+	int rc_dev_ctrlstts = 0, ep_dev_ctrlstts = 0;
+	u32 ep_dev_ctrlstts_offset = 0;
+	int i, j, ep_src_bdf = 0;
+	void __iomem *ep_base = NULL;
+	unsigned long irqsave_flags;
+
+	PCIE_DBG2(dev,
+		"AER Interrupt handler fired for RC%d irq %d\nrc_corr_counter: %lu\nrc_non_fatal_counter: %lu\nrc_fatal_counter: %lu\nep_corr_counter: %lu\nep_non_fatal_counter: %lu\nep_fatal_counter: %lu\n",
+		dev->rc_idx, irq, dev->rc_corr_counter,
+		dev->rc_non_fatal_counter, dev->rc_fatal_counter,
+		dev->ep_corr_counter, dev->ep_non_fatal_counter,
+		dev->ep_fatal_counter);
+
+	spin_lock_irqsave(&dev->aer_lock, irqsave_flags);
+
+	if (dev->suspending) {
+		PCIE_DBG2(dev,
+			"PCIe: RC%d is currently suspending.\n",
+			dev->rc_idx);
+		spin_unlock_irqrestore(&dev->aer_lock, irqsave_flags);
+		return IRQ_HANDLED;
+	}
+
+	uncorr_val = readl_relaxed(dev->dm_core +
+				PCIE20_AER_UNCORR_ERR_STATUS_REG);
+	corr_val = readl_relaxed(dev->dm_core +
+				PCIE20_AER_CORR_ERR_STATUS_REG);
+	rc_err_status = readl_relaxed(dev->dm_core +
+				PCIE20_AER_ROOT_ERR_STATUS_REG);
+	rc_dev_ctrlstts = readl_relaxed(dev->dm_core +
+				PCIE20_CAP_DEVCTRLSTATUS);
+
+	if (uncorr_val)
+		PCIE_DBG(dev, "RC's PCIE20_AER_UNCORR_ERR_STATUS_REG:0x%x\n",
+				uncorr_val);
+	if (corr_val && (dev->rc_corr_counter < corr_counter_limit))
+		PCIE_DBG(dev, "RC's PCIE20_AER_CORR_ERR_STATUS_REG:0x%x\n",
+				corr_val);
+
+	if ((rc_dev_ctrlstts >> 18) & 0x1)
+		dev->rc_fatal_counter++;
+	if ((rc_dev_ctrlstts >> 17) & 0x1)
+		dev->rc_non_fatal_counter++;
+	if ((rc_dev_ctrlstts >> 16) & 0x1)
+		dev->rc_corr_counter++;
+
+	msm_pcie_write_mask(dev->dm_core + PCIE20_CAP_DEVCTRLSTATUS, 0,
+				BIT(18)|BIT(17)|BIT(16));
+
+	if (dev->link_status == MSM_PCIE_LINK_DISABLED) {
+		PCIE_DBG2(dev, "RC%d link is down\n", dev->rc_idx);
+		goto out;
+	}
+
+	for (i = 0; i < 2; i++) {
+		if (i)
+			ep_src_bdf = readl_relaxed(dev->dm_core +
+				PCIE20_AER_ERR_SRC_ID_REG) & ~0xffff;
+		else
+			ep_src_bdf = (readl_relaxed(dev->dm_core +
+				PCIE20_AER_ERR_SRC_ID_REG) & 0xffff) << 16;
+
+		if (!ep_src_bdf)
+			continue;
+
+		for (j = 0; j < MAX_DEVICE_NUM; j++) {
+			if (ep_src_bdf == dev->pcidev_table[j].bdf) {
+				PCIE_DBG2(dev,
+					"PCIe: %s Error from Endpoint: %02x:%02x.%01x\n",
+					i ? "Uncorrectable" : "Correctable",
+					dev->pcidev_table[j].bdf >> 24,
+					dev->pcidev_table[j].bdf >> 19 & 0x1f,
+					dev->pcidev_table[j].bdf >> 16 & 0x07);
+				ep_base = dev->pcidev_table[j].conf_base;
+				ep_dev_ctrlstts_offset = dev->
+					pcidev_table[j].dev_ctrlstts_offset;
+				break;
+			}
+		}
+
+		if (!ep_base) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d no endpoint found for reported error\n",
+				dev->rc_idx);
+			goto out;
+		}
+
+		ep_uncorr_val = readl_relaxed(ep_base +
+					PCIE20_AER_UNCORR_ERR_STATUS_REG);
+		ep_corr_val = readl_relaxed(ep_base +
+					PCIE20_AER_CORR_ERR_STATUS_REG);
+		ep_dev_ctrlstts = readl_relaxed(ep_base +
+					ep_dev_ctrlstts_offset);
+
+		if (ep_uncorr_val)
+			PCIE_DBG(dev,
+				"EP's PCIE20_AER_UNCORR_ERR_STATUS_REG:0x%x\n",
+				ep_uncorr_val);
+		if (ep_corr_val && (dev->ep_corr_counter < corr_counter_limit))
+			PCIE_DBG(dev,
+				"EP's PCIE20_AER_CORR_ERR_STATUS_REG:0x%x\n",
+				ep_corr_val);
+
+		if ((ep_dev_ctrlstts >> 18) & 0x1)
+			dev->ep_fatal_counter++;
+		if ((ep_dev_ctrlstts >> 17) & 0x1)
+			dev->ep_non_fatal_counter++;
+		if ((ep_dev_ctrlstts >> 16) & 0x1)
+			dev->ep_corr_counter++;
+
+		msm_pcie_write_mask(ep_base + ep_dev_ctrlstts_offset, 0,
+					BIT(18)|BIT(17)|BIT(16));
+
+		msm_pcie_write_reg_field(ep_base,
+				PCIE20_AER_UNCORR_ERR_STATUS_REG,
+				0x3fff031, 0x3fff031);
+		msm_pcie_write_reg_field(ep_base,
+				PCIE20_AER_CORR_ERR_STATUS_REG,
+				0xf1c1, 0xf1c1);
+	}
+out:
+	if (((dev->rc_corr_counter < corr_counter_limit) &&
+		(dev->ep_corr_counter < corr_counter_limit)) ||
+		uncorr_val || ep_uncorr_val)
+		PCIE_DBG(dev, "RC's PCIE20_AER_ROOT_ERR_STATUS_REG:0x%x\n",
+				rc_err_status);
+	msm_pcie_write_reg_field(dev->dm_core,
+			PCIE20_AER_UNCORR_ERR_STATUS_REG,
+			0x3fff031, 0x3fff031);
+	msm_pcie_write_reg_field(dev->dm_core,
+			PCIE20_AER_CORR_ERR_STATUS_REG,
+			0xf1c1, 0xf1c1);
+	msm_pcie_write_reg_field(dev->dm_core,
+			PCIE20_AER_ROOT_ERR_STATUS_REG,
+			0x7f, 0x7f);
+
+	spin_unlock_irqrestore(&dev->aer_lock, irqsave_flags);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_wake_irq(int irq, void *data)
+{
+	struct msm_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+	int i;
+
+	spin_lock_irqsave(&dev->wakeup_lock, irqsave_flags);
+
+	dev->wake_counter++;
+	PCIE_DBG(dev, "PCIe: No. %ld wake IRQ for RC%d\n",
+			dev->wake_counter, dev->rc_idx);
+
+	PCIE_DBG2(dev, "PCIe WAKE is asserted by Endpoint of RC%d\n",
+		dev->rc_idx);
+
+	if (!dev->enumerated) {
+		PCIE_DBG(dev, "Start enumeating RC%d\n", dev->rc_idx);
+		if (dev->ep_wakeirq)
+			schedule_work(&dev->handle_wake_work);
+		else
+			PCIE_DBG(dev,
+				"wake irq is received but ep_wakeirq is not supported for RC%d.\n",
+				dev->rc_idx);
+	} else {
+		PCIE_DBG2(dev, "Wake up RC%d\n", dev->rc_idx);
+		__pm_stay_awake(&dev->ws);
+		__pm_relax(&dev->ws);
+
+		if (dev->num_ep > 1) {
+			for (i = 0; i < MAX_DEVICE_NUM; i++) {
+				dev->event_reg =
+					dev->pcidev_table[i].event_reg;
+				msm_pcie_notify_client(dev,
+					MSM_PCIE_EVENT_WAKEUP);
+			}
+		} else {
+			msm_pcie_notify_client(dev, MSM_PCIE_EVENT_WAKEUP);
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->wakeup_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_linkdown_irq(int irq, void *data)
+{
+	struct msm_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+	int i;
+
+	spin_lock_irqsave(&dev->linkdown_lock, irqsave_flags);
+
+	dev->linkdown_counter++;
+
+	PCIE_DBG(dev,
+		"PCIe: No. %ld linkdown IRQ for RC%d.\n",
+		dev->linkdown_counter, dev->rc_idx);
+
+	if (!dev->enumerated || dev->link_status != MSM_PCIE_LINK_ENABLED) {
+		PCIE_DBG(dev,
+			"PCIe:Linkdown IRQ for RC%d when the link is not enabled\n",
+			dev->rc_idx);
+	} else if (dev->suspending) {
+		PCIE_DBG(dev,
+			"PCIe:the link of RC%d is suspending.\n",
+			dev->rc_idx);
+	} else {
+		dev->link_status = MSM_PCIE_LINK_DISABLED;
+		dev->shadow_en = false;
+
+		if (dev->linkdown_panic)
+			panic("User has chosen to panic on linkdown\n");
+
+		/* assert PERST */
+		gpio_set_value(dev->gpio[MSM_PCIE_GPIO_PERST].num,
+				dev->gpio[MSM_PCIE_GPIO_PERST].on);
+		PCIE_ERR(dev, "PCIe link is down for RC%d\n", dev->rc_idx);
+
+		if (dev->num_ep > 1) {
+			for (i = 0; i < MAX_DEVICE_NUM; i++) {
+				dev->event_reg =
+					dev->pcidev_table[i].event_reg;
+				msm_pcie_notify_client(dev,
+					MSM_PCIE_EVENT_LINKDOWN);
+			}
+		} else {
+			msm_pcie_notify_client(dev, MSM_PCIE_EVENT_LINKDOWN);
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->linkdown_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_msi_irq(int irq, void *data)
+{
+	int i, j;
+	unsigned long val;
+	struct msm_pcie_dev_t *dev = data;
+	void __iomem *ctrl_status;
+
+	PCIE_DUMP(dev, "irq: %d\n", irq);
+
+	/*
+	 * check for set bits, clear it by setting that bit
+	 * and trigger corresponding irq
+	 */
+	for (i = 0; i < PCIE20_MSI_CTRL_MAX; i++) {
+		ctrl_status = dev->dm_core +
+				PCIE20_MSI_CTRL_INTR_STATUS + (i * 12);
+
+		val = readl_relaxed(ctrl_status);
+		while (val) {
+			j = find_first_bit(&val, 32);
+			writel_relaxed(BIT(j), ctrl_status);
+			/* ensure that interrupt is cleared (acked) */
+			wmb();
+			generic_handle_irq(
+			   irq_find_mapping(dev->irq_domain, (j + (32*i)))
+			   );
+			val = readl_relaxed(ctrl_status);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t handle_global_irq(int irq, void *data)
+{
+	int i;
+	struct msm_pcie_dev_t *dev = data;
+	unsigned long irqsave_flags;
+	u32 status = 0;
+
+	spin_lock_irqsave(&dev->global_irq_lock, irqsave_flags);
+
+	status = readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_STATUS) &
+			readl_relaxed(dev->parf + PCIE20_PARF_INT_ALL_MASK);
+
+	msm_pcie_write_mask(dev->parf + PCIE20_PARF_INT_ALL_CLEAR, 0, status);
+
+	PCIE_DBG2(dev, "RC%d: Global IRQ %d received: 0x%x\n",
+		dev->rc_idx, irq, status);
+
+	for (i = 0; i <= MSM_PCIE_INT_EVT_MAX; i++) {
+		if (status & BIT(i)) {
+			switch (i) {
+			case MSM_PCIE_INT_EVT_LINK_DOWN:
+				PCIE_DBG(dev,
+					"PCIe: RC%d: handle linkdown event.\n",
+					dev->rc_idx);
+				handle_linkdown_irq(irq, data);
+				break;
+			case MSM_PCIE_INT_EVT_AER_LEGACY:
+				PCIE_DBG(dev,
+					"PCIe: RC%d: AER legacy event.\n",
+					dev->rc_idx);
+				handle_aer_irq(irq, data);
+				break;
+			case MSM_PCIE_INT_EVT_AER_ERR:
+				PCIE_DBG(dev,
+					"PCIe: RC%d: AER event.\n",
+					dev->rc_idx);
+				handle_aer_irq(irq, data);
+				break;
+			default:
+				PCIE_ERR(dev,
+					"PCIe: RC%d: Unexpected event %d is caught!\n",
+					dev->rc_idx, i);
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&dev->global_irq_lock, irqsave_flags);
+
+	return IRQ_HANDLED;
+}
+
+void msm_pcie_destroy_irq(unsigned int irq, struct msm_pcie_dev_t *pcie_dev)
+{
+	int pos, i;
+	struct msm_pcie_dev_t *dev;
+
+	if (pcie_dev)
+		dev = pcie_dev;
+	else
+		dev = irq_get_chip_data(irq);
+
+	if (!dev) {
+		pr_err("PCIe: device is null. IRQ:%d\n", irq);
+		return;
+	}
+
+	if (dev->msi_gicm_addr) {
+		PCIE_DBG(dev, "destroy QGIC based irq %d\n", irq);
+
+		for (i = 0; i < MSM_PCIE_MAX_MSI; i++)
+			if (irq == dev->msi[i].num)
+				break;
+		if (i == MSM_PCIE_MAX_MSI) {
+			PCIE_ERR(dev,
+				"Could not find irq: %d in RC%d MSI table\n",
+				irq, dev->rc_idx);
+			return;
+		}
+
+		pos = i;
+	} else {
+		PCIE_DBG(dev, "destroy default MSI irq %d\n", irq);
+		pos = irq - irq_find_mapping(dev->irq_domain, 0);
+	}
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	PCIE_DBG(dev, "Before clear_bit pos:%d msi_irq_in_use:%ld\n",
+		pos, *dev->msi_irq_in_use);
+	clear_bit(pos, dev->msi_irq_in_use);
+	PCIE_DBG(dev, "After clear_bit pos:%d msi_irq_in_use:%ld\n",
+		pos, *dev->msi_irq_in_use);
+}
+
+/* hookup to linux pci msi framework */
+void arch_teardown_msi_irq(unsigned int irq)
+{
+	PCIE_GEN_DBG("irq %d deallocated\n", irq);
+	msm_pcie_destroy_irq(irq, NULL);
+}
+
+void arch_teardown_msi_irqs(struct pci_dev *dev)
+{
+	struct msi_desc *entry;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC:%d EP: vendor_id:0x%x device_id:0x%x\n",
+		pcie_dev->rc_idx, dev->vendor, dev->device);
+
+	pcie_dev->use_msi = false;
+
+	list_for_each_entry(entry, &dev->dev.msi_list, list) {
+		int i, nvec;
+
+		if (entry->irq == 0)
+			continue;
+		nvec = 1 << entry->msi_attrib.multiple;
+		for (i = 0; i < nvec; i++)
+			msm_pcie_destroy_irq(entry->irq + i, pcie_dev);
+	}
+}
+
+static void msm_pcie_msi_nop(struct irq_data *d)
+{
+}
+
+static struct irq_chip pcie_msi_chip = {
+	.name = "msm-pcie-msi",
+	.irq_ack = msm_pcie_msi_nop,
+	.irq_enable = unmask_msi_irq,
+	.irq_disable = mask_msi_irq,
+	.irq_mask = mask_msi_irq,
+	.irq_unmask = unmask_msi_irq,
+};
+
+static int msm_pcie_create_irq(struct msm_pcie_dev_t *dev)
+{
+	int irq, pos;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+again:
+	pos = find_first_zero_bit(dev->msi_irq_in_use, PCIE_MSI_NR_IRQS);
+
+	if (pos >= PCIE_MSI_NR_IRQS)
+		return -ENOSPC;
+
+	PCIE_DBG(dev, "pos:%d msi_irq_in_use:%ld\n", pos, *dev->msi_irq_in_use);
+
+	if (test_and_set_bit(pos, dev->msi_irq_in_use))
+		goto again;
+	else
+		PCIE_DBG(dev, "test_and_set_bit is successful pos=%d\n", pos);
+
+	irq = irq_create_mapping(dev->irq_domain, pos);
+	if (!irq)
+		return -EINVAL;
+
+	return irq;
+}
+
+static int arch_setup_msi_irq_default(struct pci_dev *pdev,
+		struct msi_desc *desc, int nvec)
+{
+	int irq;
+	struct msi_msg msg;
+	struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev->bus);
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	irq = msm_pcie_create_irq(dev);
+
+	PCIE_DBG(dev, "IRQ %d is allocated.\n", irq);
+
+	if (irq < 0)
+		return irq;
+
+	PCIE_DBG(dev, "irq %d allocated\n", irq);
+
+	irq_set_msi_desc(irq, desc);
+
+	/* write msi vector and data */
+	msg.address_hi = 0;
+	msg.address_lo = MSM_PCIE_MSI_PHY;
+	msg.data = irq - irq_find_mapping(dev->irq_domain, 0);
+	write_msi_msg(irq, &msg);
+
+	return 0;
+}
+
+static int msm_pcie_create_irq_qgic(struct msm_pcie_dev_t *dev)
+{
+	int irq, pos;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+again:
+	pos = find_first_zero_bit(dev->msi_irq_in_use, PCIE_MSI_NR_IRQS);
+
+	if (pos >= PCIE_MSI_NR_IRQS)
+		return -ENOSPC;
+
+	PCIE_DBG(dev, "pos:%d msi_irq_in_use:%ld\n", pos, *dev->msi_irq_in_use);
+
+	if (test_and_set_bit(pos, dev->msi_irq_in_use))
+		goto again;
+	else
+		PCIE_DBG(dev, "test_and_set_bit is successful pos=%d\n", pos);
+
+	if (pos >= MSM_PCIE_MAX_MSI) {
+		PCIE_ERR(dev,
+			"PCIe: RC%d: pos %d is not less than %d\n",
+			dev->rc_idx, pos, MSM_PCIE_MAX_MSI);
+		return MSM_PCIE_ERROR;
+	}
+
+	irq = dev->msi[pos].num;
+	if (!irq) {
+		PCIE_ERR(dev, "PCIe: RC%d failed to create QGIC MSI IRQ.\n",
+			dev->rc_idx);
+		return -EINVAL;
+	}
+
+	return irq;
+}
+
+static int arch_setup_msi_irq_qgic(struct pci_dev *pdev,
+		struct msi_desc *desc, int nvec)
+{
+	int irq, index, firstirq = 0;
+	struct msi_msg msg;
+	struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev->bus);
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	for (index = 0; index < nvec; index++) {
+		irq = msm_pcie_create_irq_qgic(dev);
+		PCIE_DBG(dev, "irq %d is allocated\n", irq);
+
+		if (irq < 0)
+			return irq;
+
+		if (index == 0)
+			firstirq = irq;
+
+		irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
+	}
+
+	/* write msi vector and data */
+	irq_set_msi_desc(firstirq, desc);
+	msg.address_hi = 0;
+	msg.address_lo = dev->msi_gicm_addr;
+	msg.data = dev->msi_gicm_base + (firstirq - dev->msi[0].num);
+	write_msi_msg(firstirq, &msg);
+
+	return 0;
+}
+
+int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc)
+{
+	struct msm_pcie_dev_t *dev = PCIE_BUS_PRIV_DATA(pdev->bus);
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	if (dev->msi_gicm_addr)
+		return arch_setup_msi_irq_qgic(pdev, desc, 1);
+	else
+		return arch_setup_msi_irq_default(pdev, desc, 1);
+}
+
+static int msm_pcie_get_msi_multiple(int nvec)
+{
+	int msi_multiple = 0;
+
+	while (nvec) {
+		nvec = nvec >> 1;
+		msi_multiple++;
+	}
+	PCIE_GEN_DBG("log2 number of MSI multiple:%d\n",
+		msi_multiple - 1);
+
+	return msi_multiple - 1;
+}
+
+int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+	struct msi_desc *entry;
+	int ret;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
+
+	if (type != PCI_CAP_ID_MSI || nvec > 32)
+		return -ENOSPC;
+
+	PCIE_DBG(pcie_dev, "nvec = %d\n", nvec);
+
+	list_for_each_entry(entry, &dev->dev.msi_list, list) {
+		entry->msi_attrib.multiple =
+				msm_pcie_get_msi_multiple(nvec);
+
+		if (pcie_dev->msi_gicm_addr)
+			ret = arch_setup_msi_irq_qgic(dev, entry, nvec);
+		else
+			ret = arch_setup_msi_irq_default(dev, entry, nvec);
+
+		PCIE_DBG(pcie_dev, "ret from msi_irq: %d\n", ret);
+
+		if (ret < 0)
+			return ret;
+		if (ret > 0)
+			return -ENOSPC;
+	}
+
+	pcie_dev->use_msi = true;
+
+	return 0;
+}
+
+static int msm_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
+	   irq_hw_number_t hwirq)
+{
+	irq_set_chip_and_handler (irq, &pcie_msi_chip, handle_simple_irq);
+	irq_set_chip_data(irq, domain->host_data);
+	return 0;
+}
+
+static const struct irq_domain_ops msm_pcie_msi_ops = {
+	.map = msm_pcie_msi_map,
+};
+
+int32_t msm_pcie_irq_init(struct msm_pcie_dev_t *dev)
+{
+	int rc;
+	int msi_start =  0;
+	struct device *pdev = &dev->pdev->dev;
+
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	if (dev->rc_idx)
+		wakeup_source_init(&dev->ws, "RC1 pcie_wakeup_source");
+	else
+		wakeup_source_init(&dev->ws, "RC0 pcie_wakeup_source");
+
+	/* register handler for linkdown interrupt */
+	if (dev->irq[MSM_PCIE_INT_LINK_DOWN].num) {
+		rc = devm_request_irq(pdev,
+			dev->irq[MSM_PCIE_INT_LINK_DOWN].num,
+			handle_linkdown_irq,
+			IRQF_TRIGGER_RISING,
+			dev->irq[MSM_PCIE_INT_LINK_DOWN].name,
+			dev);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: Unable to request linkdown interrupt:%d\n",
+				dev->irq[MSM_PCIE_INT_LINK_DOWN].num);
+			return rc;
+		}
+	}
+
+	/* register handler for physical MSI interrupt line */
+	if (dev->irq[MSM_PCIE_INT_MSI].num) {
+		rc = devm_request_irq(pdev,
+			dev->irq[MSM_PCIE_INT_MSI].num,
+			handle_msi_irq,
+			IRQF_TRIGGER_RISING,
+			dev->irq[MSM_PCIE_INT_MSI].name,
+			dev);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to request MSI interrupt\n",
+				dev->rc_idx);
+			return rc;
+		}
+	}
+
+	/* register handler for AER interrupt */
+	if (dev->irq[MSM_PCIE_INT_PLS_ERR].num) {
+		rc = devm_request_irq(pdev,
+				dev->irq[MSM_PCIE_INT_PLS_ERR].num,
+				handle_aer_irq,
+				IRQF_TRIGGER_RISING,
+				dev->irq[MSM_PCIE_INT_PLS_ERR].name,
+				dev);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to request aer pls_err interrupt: %d\n",
+				dev->rc_idx,
+				dev->irq[MSM_PCIE_INT_PLS_ERR].num);
+			return rc;
+		}
+	}
+
+	/* register handler for AER legacy interrupt */
+	if (dev->irq[MSM_PCIE_INT_AER_LEGACY].num) {
+		rc = devm_request_irq(pdev,
+				dev->irq[MSM_PCIE_INT_AER_LEGACY].num,
+				handle_aer_irq,
+				IRQF_TRIGGER_RISING,
+				dev->irq[MSM_PCIE_INT_AER_LEGACY].name,
+				dev);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to request aer aer_legacy interrupt: %d\n",
+				dev->rc_idx,
+				dev->irq[MSM_PCIE_INT_AER_LEGACY].num);
+			return rc;
+		}
+	}
+
+	if (dev->irq[MSM_PCIE_INT_GLOBAL_INT].num) {
+		rc = devm_request_irq(pdev,
+				dev->irq[MSM_PCIE_INT_GLOBAL_INT].num,
+				handle_global_irq,
+				IRQF_TRIGGER_RISING,
+				dev->irq[MSM_PCIE_INT_GLOBAL_INT].name,
+				dev);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to request global_int interrupt: %d\n",
+				dev->rc_idx,
+				dev->irq[MSM_PCIE_INT_GLOBAL_INT].num);
+			return rc;
+		}
+	}
+
+	/* register handler for PCIE_WAKE_N interrupt line */
+	if (dev->wake_n) {
+		rc = devm_request_irq(pdev,
+				dev->wake_n, handle_wake_irq,
+				IRQF_TRIGGER_FALLING, "msm_pcie_wake", dev);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to request wake interrupt\n",
+				dev->rc_idx);
+			return rc;
+		}
+
+		INIT_WORK(&dev->handle_wake_work, handle_wake_func);
+
+		rc = enable_irq_wake(dev->wake_n);
+		if (rc) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to enable wake interrupt\n",
+				dev->rc_idx);
+			return rc;
+		}
+	}
+
+	/* Create a virtual domain of interrupts */
+	if (!dev->msi_gicm_addr) {
+		dev->irq_domain = irq_domain_add_linear(dev->pdev->dev.of_node,
+			PCIE_MSI_NR_IRQS, &msm_pcie_msi_ops, dev);
+
+		if (!dev->irq_domain) {
+			PCIE_ERR(dev,
+				"PCIe: RC%d: Unable to initialize irq domain\n",
+				dev->rc_idx);
+
+			if (dev->wake_n)
+				disable_irq(dev->wake_n);
+
+			return PTR_ERR(dev->irq_domain);
+		}
+
+		msi_start = irq_create_mapping(dev->irq_domain, 0);
+	}
+
+	return 0;
+}
+
+void msm_pcie_irq_deinit(struct msm_pcie_dev_t *dev)
+{
+	PCIE_DBG(dev, "RC%d\n", dev->rc_idx);
+
+	wakeup_source_trash(&dev->ws);
+
+	if (dev->wake_n)
+		disable_irq(dev->wake_n);
+}
+
+
+static int msm_pcie_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	int rc_idx = -1;
+	int i, j;
+
+	PCIE_GEN_DBG("%s\n", __func__);
+
+	mutex_lock(&pcie_drv.drv_lock);
+
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"cell-index", &rc_idx);
+	if (ret) {
+		PCIE_GEN_DBG("Did not find RC index.\n");
+		goto out;
+	} else {
+		if (rc_idx >= MAX_RC_NUM) {
+			pr_err(
+				"PCIe: Invalid RC Index %d (max supported = %d)\n",
+				rc_idx, MAX_RC_NUM);
+			goto out;
+		}
+		pcie_drv.rc_num++;
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "PCIe: RC index is %d.\n",
+			rc_idx);
+	}
+
+	msm_pcie_dev[rc_idx].l0s_supported =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,l0s-supported");
+	PCIE_DBG(&msm_pcie_dev[rc_idx], "L0s is %s supported.\n",
+		msm_pcie_dev[rc_idx].l0s_supported ? "" : "not");
+	msm_pcie_dev[rc_idx].l1_supported =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,l1-supported");
+	PCIE_DBG(&msm_pcie_dev[rc_idx], "L1 is %s supported.\n",
+		msm_pcie_dev[rc_idx].l1_supported ? "" : "not");
+	msm_pcie_dev[rc_idx].l1ss_supported =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,l1ss-supported");
+	PCIE_DBG(&msm_pcie_dev[rc_idx], "L1ss is %s supported.\n",
+		msm_pcie_dev[rc_idx].l1ss_supported ? "" : "not");
+	msm_pcie_dev[rc_idx].common_clk_en =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,common-clk-en");
+	PCIE_DBG(&msm_pcie_dev[rc_idx], "Common clock is %s enabled.\n",
+		msm_pcie_dev[rc_idx].common_clk_en ? "" : "not");
+	msm_pcie_dev[rc_idx].clk_power_manage_en =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,clk-power-manage-en");
+	PCIE_DBG(&msm_pcie_dev[rc_idx],
+		"Clock power management is %s enabled.\n",
+		msm_pcie_dev[rc_idx].clk_power_manage_en ? "" : "not");
+	msm_pcie_dev[rc_idx].aux_clk_sync =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,aux-clk-sync");
+	PCIE_DBG(&msm_pcie_dev[rc_idx],
+		"AUX clock is %s synchronous to Core clock.\n",
+		msm_pcie_dev[rc_idx].aux_clk_sync ? "" : "not");
+
+	msm_pcie_dev[rc_idx].use_19p2mhz_aux_clk =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,use-19p2mhz-aux-clk");
+	PCIE_DBG(&msm_pcie_dev[rc_idx],
+		"AUX clock frequency is %s 19.2MHz.\n",
+		msm_pcie_dev[rc_idx].use_19p2mhz_aux_clk ? "" : "not");
+
+	msm_pcie_dev[rc_idx].smmu_exist =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,smmu-exist");
+	PCIE_DBG(&msm_pcie_dev[rc_idx],
+		"SMMU does %s exist.\n",
+		msm_pcie_dev[rc_idx].smmu_exist ? "" : "not");
+
+	msm_pcie_dev[rc_idx].smmu_sid_base = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node, "qcom,smmu-sid-base",
+				&msm_pcie_dev[rc_idx].smmu_sid_base);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d SMMU sid base not found\n",
+			msm_pcie_dev[rc_idx].rc_idx);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: qcom,smmu-sid-base: 0x%x.\n",
+			msm_pcie_dev[rc_idx].rc_idx,
+			msm_pcie_dev[rc_idx].smmu_sid_base);
+
+	msm_pcie_dev[rc_idx].ep_wakeirq =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,ep-wakeirq");
+	PCIE_DBG(&msm_pcie_dev[rc_idx],
+		"PCIe: EP of RC%d does %s assert wake when it is up.\n",
+		rc_idx, msm_pcie_dev[rc_idx].ep_wakeirq ? "" : "not");
+
+	msm_pcie_dev[rc_idx].phy_ver = 1;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,pcie-phy-ver",
+				&msm_pcie_dev[rc_idx].phy_ver);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: pcie-phy-ver does not exist.\n",
+			msm_pcie_dev[rc_idx].rc_idx);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: pcie-phy-ver: %d.\n",
+			msm_pcie_dev[rc_idx].rc_idx,
+			msm_pcie_dev[rc_idx].phy_ver);
+
+	msm_pcie_dev[rc_idx].n_fts = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,n-fts",
+				&msm_pcie_dev[rc_idx].n_fts);
+
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"n-fts does not exist. ret=%d\n", ret);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "n-fts: 0x%x.\n",
+				msm_pcie_dev[rc_idx].n_fts);
+
+	msm_pcie_dev[rc_idx].common_phy =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,common-phy");
+	PCIE_DBG(&msm_pcie_dev[rc_idx],
+		"PCIe: RC%d: Common PHY does %s exist.\n",
+		rc_idx, msm_pcie_dev[rc_idx].common_phy ? "" : "not");
+
+	msm_pcie_dev[rc_idx].ext_ref_clk =
+		of_property_read_bool((&pdev->dev)->of_node,
+				"qcom,ext-ref-clk");
+	PCIE_DBG(&msm_pcie_dev[rc_idx], "ref clk is %s.\n",
+		msm_pcie_dev[rc_idx].ext_ref_clk ? "external" : "internal");
+
+	msm_pcie_dev[rc_idx].ep_latency = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,ep-latency",
+				&msm_pcie_dev[rc_idx].ep_latency);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: ep-latency does not exist.\n",
+			rc_idx);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: ep-latency: 0x%x.\n",
+			rc_idx, msm_pcie_dev[rc_idx].ep_latency);
+
+	msm_pcie_dev[rc_idx].wr_halt_size = 0;
+	ret = of_property_read_u32(pdev->dev.of_node,
+				"qcom,wr-halt-size",
+				&msm_pcie_dev[rc_idx].wr_halt_size);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: wr-halt-size not specified in dt. Use default value.\n",
+			rc_idx);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: wr-halt-size: 0x%x.\n",
+			rc_idx, msm_pcie_dev[rc_idx].wr_halt_size);
+
+	msm_pcie_dev[rc_idx].cpl_timeout = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,cpl-timeout",
+				&msm_pcie_dev[rc_idx].cpl_timeout);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: Using default cpl-timeout.\n",
+			rc_idx);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: cpl-timeout: 0x%x.\n",
+			rc_idx, msm_pcie_dev[rc_idx].cpl_timeout);
+
+	msm_pcie_dev[rc_idx].perst_delay_us_min =
+		PERST_PROPAGATION_DELAY_US_MIN;
+	ret = of_property_read_u32(pdev->dev.of_node,
+				"qcom,perst-delay-us-min",
+				&msm_pcie_dev[rc_idx].perst_delay_us_min);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: perst-delay-us-min does not exist. Use default value %dus.\n",
+			rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_min);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: perst-delay-us-min: %dus.\n",
+			rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_min);
+
+	msm_pcie_dev[rc_idx].perst_delay_us_max =
+		PERST_PROPAGATION_DELAY_US_MAX;
+	ret = of_property_read_u32(pdev->dev.of_node,
+				"qcom,perst-delay-us-max",
+				&msm_pcie_dev[rc_idx].perst_delay_us_max);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: perst-delay-us-max does not exist. Use default value %dus.\n",
+			rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_max);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: perst-delay-us-max: %dus.\n",
+			rc_idx, msm_pcie_dev[rc_idx].perst_delay_us_max);
+
+	msm_pcie_dev[rc_idx].tlp_rd_size = PCIE_TLP_RD_SIZE;
+	ret = of_property_read_u32(pdev->dev.of_node,
+				"qcom,tlp-rd-size",
+				&msm_pcie_dev[rc_idx].tlp_rd_size);
+	if (ret)
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"RC%d: tlp-rd-size does not exist. tlp-rd-size: 0x%x.\n",
+			rc_idx, msm_pcie_dev[rc_idx].tlp_rd_size);
+	else
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "RC%d: tlp-rd-size: 0x%x.\n",
+			rc_idx, msm_pcie_dev[rc_idx].tlp_rd_size);
+
+	msm_pcie_dev[rc_idx].msi_gicm_addr = 0;
+	msm_pcie_dev[rc_idx].msi_gicm_base = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,msi-gicm-addr",
+				&msm_pcie_dev[rc_idx].msi_gicm_addr);
+
+	if (ret) {
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "%s",
+			"msi-gicm-addr does not exist.\n");
+	} else {
+		PCIE_DBG(&msm_pcie_dev[rc_idx], "msi-gicm-addr: 0x%x.\n",
+				msm_pcie_dev[rc_idx].msi_gicm_addr);
+
+		ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,msi-gicm-base",
+				&msm_pcie_dev[rc_idx].msi_gicm_base);
+
+		if (ret) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: msi-gicm-base does not exist.\n",
+				rc_idx);
+			goto decrease_rc_num;
+		} else {
+			PCIE_DBG(&msm_pcie_dev[rc_idx], "msi-gicm-base: 0x%x\n",
+					msm_pcie_dev[rc_idx].msi_gicm_base);
+		}
+	}
+
+	msm_pcie_dev[rc_idx].scm_dev_id = 0;
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,scm-dev-id",
+				&msm_pcie_dev[rc_idx].scm_dev_id);
+
+	msm_pcie_dev[rc_idx].rc_idx = rc_idx;
+	msm_pcie_dev[rc_idx].pdev = pdev;
+	msm_pcie_dev[rc_idx].vreg_n = 0;
+	msm_pcie_dev[rc_idx].gpio_n = 0;
+	msm_pcie_dev[rc_idx].parf_deemph = 0;
+	msm_pcie_dev[rc_idx].parf_swing = 0;
+	msm_pcie_dev[rc_idx].link_status = MSM_PCIE_LINK_DEINIT;
+	msm_pcie_dev[rc_idx].user_suspend = false;
+	msm_pcie_dev[rc_idx].disable_pc = false;
+	msm_pcie_dev[rc_idx].saved_state = NULL;
+	msm_pcie_dev[rc_idx].enumerated = false;
+	msm_pcie_dev[rc_idx].num_active_ep = 0;
+	msm_pcie_dev[rc_idx].num_ep = 0;
+	msm_pcie_dev[rc_idx].pending_ep_reg = false;
+	msm_pcie_dev[rc_idx].phy_len = 0;
+	msm_pcie_dev[rc_idx].port_phy_len = 0;
+	msm_pcie_dev[rc_idx].phy_sequence = NULL;
+	msm_pcie_dev[rc_idx].port_phy_sequence = NULL;
+	msm_pcie_dev[rc_idx].event_reg = NULL;
+	msm_pcie_dev[rc_idx].linkdown_counter = 0;
+	msm_pcie_dev[rc_idx].link_turned_on_counter = 0;
+	msm_pcie_dev[rc_idx].link_turned_off_counter = 0;
+	msm_pcie_dev[rc_idx].rc_corr_counter = 0;
+	msm_pcie_dev[rc_idx].rc_non_fatal_counter = 0;
+	msm_pcie_dev[rc_idx].rc_fatal_counter = 0;
+	msm_pcie_dev[rc_idx].ep_corr_counter = 0;
+	msm_pcie_dev[rc_idx].ep_non_fatal_counter = 0;
+	msm_pcie_dev[rc_idx].ep_fatal_counter = 0;
+	msm_pcie_dev[rc_idx].suspending = false;
+	msm_pcie_dev[rc_idx].wake_counter = 0;
+	msm_pcie_dev[rc_idx].aer_enable = true;
+	msm_pcie_dev[rc_idx].power_on = false;
+	msm_pcie_dev[rc_idx].current_short_bdf = 0;
+	msm_pcie_dev[rc_idx].use_msi = false;
+	msm_pcie_dev[rc_idx].use_pinctrl = false;
+	msm_pcie_dev[rc_idx].linkdown_panic = false;
+	msm_pcie_dev[rc_idx].bridge_found = false;
+	memcpy(msm_pcie_dev[rc_idx].vreg, msm_pcie_vreg_info,
+				sizeof(msm_pcie_vreg_info));
+	memcpy(msm_pcie_dev[rc_idx].gpio, msm_pcie_gpio_info,
+				sizeof(msm_pcie_gpio_info));
+	memcpy(msm_pcie_dev[rc_idx].clk, msm_pcie_clk_info[rc_idx],
+				sizeof(msm_pcie_clk_info[rc_idx]));
+	memcpy(msm_pcie_dev[rc_idx].pipeclk, msm_pcie_pipe_clk_info[rc_idx],
+				sizeof(msm_pcie_pipe_clk_info[rc_idx]));
+	memcpy(msm_pcie_dev[rc_idx].res, msm_pcie_res_info,
+				sizeof(msm_pcie_res_info));
+	memcpy(msm_pcie_dev[rc_idx].irq, msm_pcie_irq_info,
+				sizeof(msm_pcie_irq_info));
+	memcpy(msm_pcie_dev[rc_idx].msi, msm_pcie_msi_info,
+				sizeof(msm_pcie_msi_info));
+	memcpy(msm_pcie_dev[rc_idx].reset, msm_pcie_reset_info[rc_idx],
+				sizeof(msm_pcie_reset_info[rc_idx]));
+	memcpy(msm_pcie_dev[rc_idx].pipe_reset,
+			msm_pcie_pipe_reset_info[rc_idx],
+			sizeof(msm_pcie_pipe_reset_info[rc_idx]));
+	msm_pcie_dev[rc_idx].shadow_en = true;
+	for (i = 0; i < PCIE_CONF_SPACE_DW; i++)
+		msm_pcie_dev[rc_idx].rc_shadow[i] = PCIE_CLEAR;
+	for (i = 0; i < MAX_DEVICE_NUM; i++)
+		for (j = 0; j < PCIE_CONF_SPACE_DW; j++)
+			msm_pcie_dev[rc_idx].ep_shadow[i][j] = PCIE_CLEAR;
+	for (i = 0; i < MAX_DEVICE_NUM; i++) {
+		msm_pcie_dev[rc_idx].pcidev_table[i].bdf = 0;
+		msm_pcie_dev[rc_idx].pcidev_table[i].dev = NULL;
+		msm_pcie_dev[rc_idx].pcidev_table[i].short_bdf = 0;
+		msm_pcie_dev[rc_idx].pcidev_table[i].sid = 0;
+		msm_pcie_dev[rc_idx].pcidev_table[i].domain = rc_idx;
+		msm_pcie_dev[rc_idx].pcidev_table[i].conf_base = 0;
+		msm_pcie_dev[rc_idx].pcidev_table[i].phy_address = 0;
+		msm_pcie_dev[rc_idx].pcidev_table[i].dev_ctrlstts_offset = 0;
+		msm_pcie_dev[rc_idx].pcidev_table[i].event_reg = NULL;
+		msm_pcie_dev[rc_idx].pcidev_table[i].registered = true;
+	}
+
+	ret = msm_pcie_get_resources(&msm_pcie_dev[rc_idx],
+				msm_pcie_dev[rc_idx].pdev);
+
+	if (ret)
+		goto decrease_rc_num;
+
+	msm_pcie_dev[rc_idx].pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(msm_pcie_dev[rc_idx].pinctrl))
+		PCIE_ERR(&msm_pcie_dev[rc_idx],
+			"PCIe: RC%d failed to get pinctrl\n",
+			rc_idx);
+	else
+		msm_pcie_dev[rc_idx].use_pinctrl = true;
+
+	if (msm_pcie_dev[rc_idx].use_pinctrl) {
+		msm_pcie_dev[rc_idx].pins_default =
+			pinctrl_lookup_state(msm_pcie_dev[rc_idx].pinctrl,
+						"default");
+		if (IS_ERR(msm_pcie_dev[rc_idx].pins_default)) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d could not get pinctrl default state\n",
+				rc_idx);
+			msm_pcie_dev[rc_idx].pins_default = NULL;
+		}
+
+		msm_pcie_dev[rc_idx].pins_sleep =
+			pinctrl_lookup_state(msm_pcie_dev[rc_idx].pinctrl,
+						"sleep");
+		if (IS_ERR(msm_pcie_dev[rc_idx].pins_sleep)) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d could not get pinctrl sleep state\n",
+				rc_idx);
+			msm_pcie_dev[rc_idx].pins_sleep = NULL;
+		}
+	}
+
+	ret = msm_pcie_gpio_init(&msm_pcie_dev[rc_idx]);
+	if (ret) {
+		msm_pcie_release_resources(&msm_pcie_dev[rc_idx]);
+		goto decrease_rc_num;
+	}
+
+	ret = msm_pcie_irq_init(&msm_pcie_dev[rc_idx]);
+	if (ret) {
+		msm_pcie_release_resources(&msm_pcie_dev[rc_idx]);
+		msm_pcie_gpio_deinit(&msm_pcie_dev[rc_idx]);
+		goto decrease_rc_num;
+	}
+
+	msm_pcie_dev[rc_idx].drv_ready = true;
+
+	if (msm_pcie_dev[rc_idx].ep_wakeirq) {
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"PCIe: RC%d will be enumerated upon WAKE signal from Endpoint.\n",
+			rc_idx);
+		mutex_unlock(&pcie_drv.drv_lock);
+		return 0;
+	}
+
+	ret = msm_pcie_enumerate(rc_idx);
+
+	if (ret)
+		PCIE_ERR(&msm_pcie_dev[rc_idx],
+			"PCIe: RC%d is not enabled during bootup; it will be enumerated upon client request.\n",
+			rc_idx);
+	else
+		PCIE_ERR(&msm_pcie_dev[rc_idx], "RC%d is enabled in bootup\n",
+			rc_idx);
+
+	PCIE_DBG(&msm_pcie_dev[rc_idx], "PCIE probed %s\n",
+		dev_name(&(pdev->dev)));
+
+	mutex_unlock(&pcie_drv.drv_lock);
+	return 0;
+
+decrease_rc_num:
+	pcie_drv.rc_num--;
+out:
+	if (rc_idx < 0 || rc_idx >= MAX_RC_NUM)
+		pr_err("PCIe: Invalid RC index %d. Driver probe failed\n",
+		rc_idx);
+	else
+		PCIE_ERR(&msm_pcie_dev[rc_idx],
+			"PCIe: Driver probe failed for RC%d:%d\n",
+			rc_idx, ret);
+
+	mutex_unlock(&pcie_drv.drv_lock);
+
+	return ret;
+}
+
+static int msm_pcie_remove(struct platform_device *pdev)
+{
+	int ret = 0;
+	int rc_idx;
+
+	PCIE_GEN_DBG("PCIe:%s.\n", __func__);
+
+	mutex_lock(&pcie_drv.drv_lock);
+
+	ret = of_property_read_u32((&pdev->dev)->of_node,
+				"cell-index", &rc_idx);
+	if (ret) {
+		pr_err("%s: Did not find RC index.\n", __func__);
+		goto out;
+	} else {
+		pcie_drv.rc_num--;
+		PCIE_GEN_DBG("%s: RC index is 0x%x.", __func__, rc_idx);
+	}
+
+	msm_pcie_irq_deinit(&msm_pcie_dev[rc_idx]);
+	msm_pcie_vreg_deinit(&msm_pcie_dev[rc_idx]);
+	msm_pcie_clk_deinit(&msm_pcie_dev[rc_idx]);
+	msm_pcie_gpio_deinit(&msm_pcie_dev[rc_idx]);
+	msm_pcie_release_resources(&msm_pcie_dev[rc_idx]);
+
+out:
+	mutex_unlock(&pcie_drv.drv_lock);
+
+	return ret;
+}
+
+static const struct of_device_id msm_pcie_match[] = {
+	{	.compatible = "qcom,pci-msm",
+	},
+	{}
+};
+
+static struct platform_driver msm_pcie_driver = {
+	.probe	= msm_pcie_probe,
+	.remove	= msm_pcie_remove,
+	.driver	= {
+		.name		= "pci-msm",
+		.owner		= THIS_MODULE,
+		.of_match_table	= msm_pcie_match,
+	},
+};
+
+int __init pcie_init(void)
+{
+	int ret = 0, i;
+	char rc_name[MAX_RC_NAME_LEN];
+
+	pr_alert("pcie:%s.\n", __func__);
+
+	pcie_drv.rc_num = 0;
+	mutex_init(&pcie_drv.drv_lock);
+	mutex_init(&com_phy_lock);
+
+	for (i = 0; i < MAX_RC_NUM; i++) {
+		snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-short", i);
+		msm_pcie_dev[i].ipc_log =
+			ipc_log_context_create(PCIE_LOG_PAGES, rc_name, 0);
+		if (msm_pcie_dev[i].ipc_log == NULL)
+			pr_err("%s: unable to create IPC log context for %s\n",
+				__func__, rc_name);
+		else
+			PCIE_DBG(&msm_pcie_dev[i],
+				"PCIe IPC logging is enable for RC%d\n",
+				i);
+		snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-long", i);
+		msm_pcie_dev[i].ipc_log_long =
+			ipc_log_context_create(PCIE_LOG_PAGES, rc_name, 0);
+		if (msm_pcie_dev[i].ipc_log_long == NULL)
+			pr_err("%s: unable to create IPC log context for %s\n",
+				__func__, rc_name);
+		else
+			PCIE_DBG(&msm_pcie_dev[i],
+				"PCIe IPC logging %s is enable for RC%d\n",
+				rc_name, i);
+		snprintf(rc_name, MAX_RC_NAME_LEN, "pcie%d-dump", i);
+		msm_pcie_dev[i].ipc_log_dump =
+			ipc_log_context_create(PCIE_LOG_PAGES, rc_name, 0);
+		if (msm_pcie_dev[i].ipc_log_dump == NULL)
+			pr_err("%s: unable to create IPC log context for %s\n",
+				__func__, rc_name);
+		else
+			PCIE_DBG(&msm_pcie_dev[i],
+				"PCIe IPC logging %s is enable for RC%d\n",
+				rc_name, i);
+		spin_lock_init(&msm_pcie_dev[i].cfg_lock);
+		msm_pcie_dev[i].cfg_access = true;
+		mutex_init(&msm_pcie_dev[i].enumerate_lock);
+		mutex_init(&msm_pcie_dev[i].setup_lock);
+		mutex_init(&msm_pcie_dev[i].recovery_lock);
+		spin_lock_init(&msm_pcie_dev[i].linkdown_lock);
+		spin_lock_init(&msm_pcie_dev[i].wakeup_lock);
+		spin_lock_init(&msm_pcie_dev[i].global_irq_lock);
+		spin_lock_init(&msm_pcie_dev[i].aer_lock);
+		msm_pcie_dev[i].drv_ready = false;
+	}
+	for (i = 0; i < MAX_RC_NUM * MAX_DEVICE_NUM; i++) {
+		msm_pcie_dev_tbl[i].bdf = 0;
+		msm_pcie_dev_tbl[i].dev = NULL;
+		msm_pcie_dev_tbl[i].short_bdf = 0;
+		msm_pcie_dev_tbl[i].sid = 0;
+		msm_pcie_dev_tbl[i].domain = -1;
+		msm_pcie_dev_tbl[i].conf_base = 0;
+		msm_pcie_dev_tbl[i].phy_address = 0;
+		msm_pcie_dev_tbl[i].dev_ctrlstts_offset = 0;
+		msm_pcie_dev_tbl[i].event_reg = NULL;
+		msm_pcie_dev_tbl[i].registered = true;
+	}
+
+	msm_pcie_debugfs_init();
+
+	ret = platform_driver_register(&msm_pcie_driver);
+
+	return ret;
+}
+
+static void __exit pcie_exit(void)
+{
+	PCIE_GEN_DBG("pcie:%s.\n", __func__);
+
+	platform_driver_unregister(&msm_pcie_driver);
+
+	msm_pcie_debugfs_exit();
+}
+
+subsys_initcall_sync(pcie_init);
+module_exit(pcie_exit);
+
+
+/* RC do not represent the right class; set it to PCI_CLASS_BRIDGE_PCI */
+static void msm_pcie_fixup_early(struct pci_dev *dev)
+{
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "hdr_type %d\n", dev->hdr_type);
+	if (dev->hdr_type == 1)
+		dev->class = (dev->class & 0xff) | (PCI_CLASS_BRIDGE_PCI << 8);
+}
+DECLARE_PCI_FIXUP_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+			msm_pcie_fixup_early);
+
+/* Suspend the PCIe link */
+static int msm_pcie_pm_suspend(struct pci_dev *dev,
+			void *user, void *data, u32 options)
+{
+	int ret = 0;
+	u32 val = 0;
+	int ret_l23;
+	unsigned long irqsave_flags;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC%d: entry\n", pcie_dev->rc_idx);
+
+	spin_lock_irqsave(&pcie_dev->aer_lock, irqsave_flags);
+	pcie_dev->suspending = true;
+	spin_unlock_irqrestore(&pcie_dev->aer_lock, irqsave_flags);
+
+	if (!pcie_dev->power_on) {
+		PCIE_DBG(pcie_dev,
+			"PCIe: power of RC%d has been turned off.\n",
+			pcie_dev->rc_idx);
+		return ret;
+	}
+
+	if (dev && !(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)
+		&& msm_pcie_confirm_linkup(pcie_dev, true, true,
+			pcie_dev->conf)) {
+		ret = pci_save_state(dev);
+		pcie_dev->saved_state =	pci_store_saved_state(dev);
+	}
+	if (ret) {
+		PCIE_ERR(pcie_dev, "PCIe: fail to save state of RC%d:%d.\n",
+			pcie_dev->rc_idx, ret);
+		pcie_dev->suspending = false;
+		return ret;
+	}
+
+	spin_lock_irqsave(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+	pcie_dev->cfg_access = false;
+	spin_unlock_irqrestore(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+
+	msm_pcie_write_mask(pcie_dev->elbi + PCIE20_ELBI_SYS_CTRL, 0,
+				BIT(4));
+
+	PCIE_DBG(pcie_dev, "RC%d: PME_TURNOFF_MSG is sent out\n",
+		pcie_dev->rc_idx);
+
+	ret_l23 = readl_poll_timeout((pcie_dev->parf
+		+ PCIE20_PARF_PM_STTS), val, (val & BIT(5)), 10000, 100000);
+
+	/* check L23_Ready */
+	PCIE_DBG(pcie_dev, "RC%d: PCIE20_PARF_PM_STTS is 0x%x.\n",
+		pcie_dev->rc_idx,
+		readl_relaxed(pcie_dev->parf + PCIE20_PARF_PM_STTS));
+	if (!ret_l23)
+		PCIE_DBG(pcie_dev, "RC%d: PM_Enter_L23 is received\n",
+			pcie_dev->rc_idx);
+	else
+		PCIE_DBG(pcie_dev, "RC%d: PM_Enter_L23 is NOT received\n",
+			pcie_dev->rc_idx);
+
+		msm_pcie_disable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG);
+
+	if (pcie_dev->use_pinctrl && pcie_dev->pins_sleep)
+		pinctrl_select_state(pcie_dev->pinctrl,
+					pcie_dev->pins_sleep);
+
+	PCIE_DBG(pcie_dev, "RC%d: exit\n", pcie_dev->rc_idx);
+
+	return ret;
+}
+
+static void msm_pcie_fixup_suspend(struct pci_dev *dev)
+{
+	int ret;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
+
+	if (pcie_dev->link_status != MSM_PCIE_LINK_ENABLED)
+		return;
+
+	spin_lock_irqsave(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+	if (pcie_dev->disable_pc) {
+		PCIE_DBG(pcie_dev,
+			"RC%d: Skip suspend because of user request\n",
+			pcie_dev->rc_idx);
+		spin_unlock_irqrestore(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+		return;
+	}
+	spin_unlock_irqrestore(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+
+	mutex_lock(&pcie_dev->recovery_lock);
+
+	ret = msm_pcie_pm_suspend(dev, NULL, NULL, 0);
+	if (ret)
+		PCIE_ERR(pcie_dev, "PCIe: RC%d got failure in suspend:%d.\n",
+			pcie_dev->rc_idx, ret);
+
+	mutex_unlock(&pcie_dev->recovery_lock);
+}
+DECLARE_PCI_FIXUP_SUSPEND(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+			  msm_pcie_fixup_suspend);
+
+/* Resume the PCIe link */
+static int msm_pcie_pm_resume(struct pci_dev *dev,
+			void *user, void *data, u32 options)
+{
+	int ret;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC%d: entry\n", pcie_dev->rc_idx);
+
+	if (pcie_dev->use_pinctrl && pcie_dev->pins_default)
+		pinctrl_select_state(pcie_dev->pinctrl,
+					pcie_dev->pins_default);
+
+	spin_lock_irqsave(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+	pcie_dev->cfg_access = true;
+	spin_unlock_irqrestore(&pcie_dev->cfg_lock,
+				pcie_dev->irqsave_flags);
+
+	ret = msm_pcie_enable(pcie_dev, PM_PIPE_CLK | PM_CLK | PM_VREG);
+	if (ret) {
+		PCIE_ERR(pcie_dev,
+			"PCIe: RC%d fail to enable PCIe link in resume.\n",
+			pcie_dev->rc_idx);
+		return ret;
+	}
+
+	pcie_dev->suspending = false;
+	PCIE_DBG(pcie_dev,
+		"dev->bus->number = %d dev->bus->primary = %d\n",
+		 dev->bus->number, dev->bus->primary);
+
+	if (!(options & MSM_PCIE_CONFIG_NO_CFG_RESTORE)) {
+		PCIE_DBG(pcie_dev,
+			"RC%d: entry of PCI framework restore state\n",
+			pcie_dev->rc_idx);
+
+		pci_load_and_free_saved_state(dev,
+				&pcie_dev->saved_state);
+		pci_restore_state(dev);
+
+		PCIE_DBG(pcie_dev,
+			"RC%d: exit of PCI framework restore state\n",
+			pcie_dev->rc_idx);
+	}
+
+	if (pcie_dev->bridge_found) {
+		PCIE_DBG(pcie_dev,
+			"RC%d: entry of PCIe recover config\n",
+			pcie_dev->rc_idx);
+
+		msm_pcie_recover_config(dev);
+
+		PCIE_DBG(pcie_dev,
+			"RC%d: exit of PCIe recover config\n",
+			pcie_dev->rc_idx);
+	}
+
+	PCIE_DBG(pcie_dev, "RC%d: exit\n", pcie_dev->rc_idx);
+
+	return ret;
+}
+
+void msm_pcie_fixup_resume(struct pci_dev *dev)
+{
+	int ret;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
+
+	if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
+		pcie_dev->user_suspend)
+		return;
+
+	mutex_lock(&pcie_dev->recovery_lock);
+	ret = msm_pcie_pm_resume(dev, NULL, NULL, 0);
+	if (ret)
+		PCIE_ERR(pcie_dev,
+			"PCIe: RC%d got failure in fixup resume:%d.\n",
+			pcie_dev->rc_idx, ret);
+
+	mutex_unlock(&pcie_dev->recovery_lock);
+}
+DECLARE_PCI_FIXUP_RESUME(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+				 msm_pcie_fixup_resume);
+
+void msm_pcie_fixup_resume_early(struct pci_dev *dev)
+{
+	int ret;
+	struct msm_pcie_dev_t *pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+
+	PCIE_DBG(pcie_dev, "RC%d\n", pcie_dev->rc_idx);
+
+	if ((pcie_dev->link_status != MSM_PCIE_LINK_DISABLED) ||
+		pcie_dev->user_suspend)
+		return;
+
+	mutex_lock(&pcie_dev->recovery_lock);
+	ret = msm_pcie_pm_resume(dev, NULL, NULL, 0);
+	if (ret)
+		PCIE_ERR(pcie_dev, "PCIe: RC%d got failure in resume:%d.\n",
+			pcie_dev->rc_idx, ret);
+
+	mutex_unlock(&pcie_dev->recovery_lock);
+}
+DECLARE_PCI_FIXUP_RESUME_EARLY(PCIE_VENDOR_ID_RCP, PCIE_DEVICE_ID_RCP,
+				 msm_pcie_fixup_resume_early);
+
+int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
+			void *data, u32 options)
+{
+	int i, ret = 0;
+	struct pci_dev *dev;
+	u32 rc_idx = 0;
+	struct msm_pcie_dev_t *pcie_dev;
+
+	PCIE_GEN_DBG("PCIe: pm_opt:%d;busnr:%d;options:%d\n",
+		pm_opt, busnr, options);
+
+
+	if (!user) {
+		pr_err("PCIe: endpoint device is NULL\n");
+		ret = -ENODEV;
+		goto out;
+	}
+
+	pcie_dev = PCIE_BUS_PRIV_DATA(((struct pci_dev *)user)->bus);
+
+	if (pcie_dev) {
+		rc_idx = pcie_dev->rc_idx;
+		PCIE_DBG(pcie_dev,
+			"PCIe: RC%d: pm_opt:%d;busnr:%d;options:%d\n",
+			rc_idx, pm_opt, busnr, options);
+	} else {
+		pr_err(
+			"PCIe: did not find RC for pci endpoint device.\n"
+			);
+		ret = -ENODEV;
+		goto out;
+	}
+
+	for (i = 0; i < MAX_DEVICE_NUM; i++) {
+		if (!busnr)
+			break;
+		if (user == pcie_dev->pcidev_table[i].dev) {
+			if (busnr == pcie_dev->pcidev_table[i].bdf >> 24)
+				break;
+
+			PCIE_ERR(pcie_dev,
+				"PCIe: RC%d: bus number %d does not match with the expected value %d\n",
+				pcie_dev->rc_idx, busnr,
+				pcie_dev->pcidev_table[i].bdf >> 24);
+			ret = MSM_PCIE_ERROR;
+			goto out;
+		}
+	}
+
+	if (i == MAX_DEVICE_NUM) {
+		PCIE_ERR(pcie_dev,
+			"PCIe: RC%d: endpoint device was not found in device table",
+			pcie_dev->rc_idx);
+		ret = MSM_PCIE_ERROR;
+		goto out;
+	}
+
+	dev = msm_pcie_dev[rc_idx].dev;
+
+	if (!msm_pcie_dev[rc_idx].drv_ready) {
+		PCIE_ERR(&msm_pcie_dev[rc_idx],
+			"RC%d has not been successfully probed yet\n",
+			rc_idx);
+		return -EPROBE_DEFER;
+	}
+
+	switch (pm_opt) {
+	case MSM_PCIE_SUSPEND:
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"User of RC%d requests to suspend the link\n", rc_idx);
+		if (msm_pcie_dev[rc_idx].link_status != MSM_PCIE_LINK_ENABLED)
+			PCIE_DBG(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: requested to suspend when link is not enabled:%d.\n",
+				rc_idx, msm_pcie_dev[rc_idx].link_status);
+
+		if (!msm_pcie_dev[rc_idx].power_on) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: requested to suspend when link is powered down:%d.\n",
+				rc_idx, msm_pcie_dev[rc_idx].link_status);
+			break;
+		}
+
+		if (msm_pcie_dev[rc_idx].pending_ep_reg) {
+			PCIE_DBG(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: request to suspend the link is rejected\n",
+				rc_idx);
+			break;
+		}
+
+		if (pcie_dev->num_active_ep) {
+			PCIE_DBG(pcie_dev,
+				"RC%d: an EP requested to suspend the link, but other EPs are still active: %d\n",
+				pcie_dev->rc_idx, pcie_dev->num_active_ep);
+			return ret;
+		}
+
+		msm_pcie_dev[rc_idx].user_suspend = true;
+
+		mutex_lock(&msm_pcie_dev[rc_idx].recovery_lock);
+
+		ret = msm_pcie_pm_suspend(dev, user, data, options);
+		if (ret) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: user failed to suspend the link.\n",
+				rc_idx);
+			msm_pcie_dev[rc_idx].user_suspend = false;
+		}
+
+		mutex_unlock(&msm_pcie_dev[rc_idx].recovery_lock);
+		break;
+	case MSM_PCIE_RESUME:
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"User of RC%d requests to resume the link\n", rc_idx);
+		if (msm_pcie_dev[rc_idx].link_status !=
+					MSM_PCIE_LINK_DISABLED) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: requested to resume when link is not disabled:%d. Number of active EP(s): %d\n",
+				rc_idx, msm_pcie_dev[rc_idx].link_status,
+				msm_pcie_dev[rc_idx].num_active_ep);
+			break;
+		}
+
+		mutex_lock(&msm_pcie_dev[rc_idx].recovery_lock);
+		ret = msm_pcie_pm_resume(dev, user, data, options);
+		if (ret) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: user failed to resume the link.\n",
+				rc_idx);
+		} else {
+			PCIE_DBG(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d: user succeeded to resume the link.\n",
+				rc_idx);
+
+			msm_pcie_dev[rc_idx].user_suspend = false;
+		}
+
+		mutex_unlock(&msm_pcie_dev[rc_idx].recovery_lock);
+
+		break;
+	case MSM_PCIE_DISABLE_PC:
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"User of RC%d requests to keep the link always alive.\n",
+			rc_idx);
+		spin_lock_irqsave(&msm_pcie_dev[rc_idx].cfg_lock,
+				msm_pcie_dev[rc_idx].irqsave_flags);
+		if (msm_pcie_dev[rc_idx].suspending) {
+			PCIE_ERR(&msm_pcie_dev[rc_idx],
+				"PCIe: RC%d Link has been suspended before request\n",
+				rc_idx);
+			ret = MSM_PCIE_ERROR;
+		} else {
+			msm_pcie_dev[rc_idx].disable_pc = true;
+		}
+		spin_unlock_irqrestore(&msm_pcie_dev[rc_idx].cfg_lock,
+				msm_pcie_dev[rc_idx].irqsave_flags);
+		break;
+	case MSM_PCIE_ENABLE_PC:
+		PCIE_DBG(&msm_pcie_dev[rc_idx],
+			"User of RC%d cancels the request of alive link.\n",
+			rc_idx);
+		spin_lock_irqsave(&msm_pcie_dev[rc_idx].cfg_lock,
+				msm_pcie_dev[rc_idx].irqsave_flags);
+		msm_pcie_dev[rc_idx].disable_pc = false;
+		spin_unlock_irqrestore(&msm_pcie_dev[rc_idx].cfg_lock,
+				msm_pcie_dev[rc_idx].irqsave_flags);
+		break;
+	default:
+		PCIE_ERR(&msm_pcie_dev[rc_idx],
+			"PCIe: RC%d: unsupported pm operation:%d.\n",
+			rc_idx, pm_opt);
+		ret = -ENODEV;
+		goto out;
+	}
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_pm_control);
+
+int msm_pcie_register_event(struct msm_pcie_register_event *reg)
+{
+	int i, ret = 0;
+	struct msm_pcie_dev_t *pcie_dev;
+
+	if (!reg) {
+		pr_err("PCIe: Event registration is NULL\n");
+		return -ENODEV;
+	}
+
+	if (!reg->user) {
+		pr_err("PCIe: User of event registration is NULL\n");
+		return -ENODEV;
+	}
+
+	pcie_dev = PCIE_BUS_PRIV_DATA(((struct pci_dev *)reg->user)->bus);
+
+	if (!pcie_dev) {
+		PCIE_ERR(pcie_dev, "%s",
+			"PCIe: did not find RC for pci endpoint device.\n");
+		return -ENODEV;
+	}
+
+	if (pcie_dev->num_ep > 1) {
+		for (i = 0; i < MAX_DEVICE_NUM; i++) {
+			if (reg->user ==
+				pcie_dev->pcidev_table[i].dev) {
+				pcie_dev->event_reg =
+					pcie_dev->pcidev_table[i].event_reg;
+
+				if (!pcie_dev->event_reg) {
+					pcie_dev->pcidev_table[i].registered =
+						true;
+
+					pcie_dev->num_active_ep++;
+					PCIE_DBG(pcie_dev,
+						"PCIe: RC%d: number of active EP(s): %d.\n",
+						pcie_dev->rc_idx,
+						pcie_dev->num_active_ep);
+				}
+
+				pcie_dev->event_reg = reg;
+				pcie_dev->pcidev_table[i].event_reg = reg;
+				PCIE_DBG(pcie_dev,
+					"Event 0x%x is registered for RC %d\n",
+					reg->events,
+					pcie_dev->rc_idx);
+
+				break;
+			}
+		}
+
+		if (pcie_dev->pending_ep_reg) {
+			for (i = 0; i < MAX_DEVICE_NUM; i++)
+				if (!pcie_dev->pcidev_table[i].registered)
+					break;
+
+			if (i == MAX_DEVICE_NUM)
+				pcie_dev->pending_ep_reg = false;
+		}
+	} else {
+		pcie_dev->event_reg = reg;
+		PCIE_DBG(pcie_dev,
+			"Event 0x%x is registered for RC %d\n", reg->events,
+			pcie_dev->rc_idx);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_register_event);
+
+int msm_pcie_deregister_event(struct msm_pcie_register_event *reg)
+{
+	int i, ret = 0;
+	struct msm_pcie_dev_t *pcie_dev;
+
+	if (!reg) {
+		pr_err("PCIe: Event deregistration is NULL\n");
+		return -ENODEV;
+	}
+
+	if (!reg->user) {
+		pr_err("PCIe: User of event deregistration is NULL\n");
+		return -ENODEV;
+	}
+
+	pcie_dev = PCIE_BUS_PRIV_DATA(((struct pci_dev *)reg->user)->bus);
+
+	if (!pcie_dev) {
+		PCIE_ERR(pcie_dev, "%s",
+			"PCIe: did not find RC for pci endpoint device.\n");
+		return -ENODEV;
+	}
+
+	if (pcie_dev->num_ep > 1) {
+		for (i = 0; i < MAX_DEVICE_NUM; i++) {
+			if (reg->user == pcie_dev->pcidev_table[i].dev) {
+				if (pcie_dev->pcidev_table[i].event_reg) {
+					pcie_dev->num_active_ep--;
+					PCIE_DBG(pcie_dev,
+						"PCIe: RC%d: number of active EP(s) left: %d.\n",
+						pcie_dev->rc_idx,
+						pcie_dev->num_active_ep);
+				}
+
+				pcie_dev->event_reg = NULL;
+				pcie_dev->pcidev_table[i].event_reg = NULL;
+				PCIE_DBG(pcie_dev,
+					"Event is deregistered for RC %d\n",
+					pcie_dev->rc_idx);
+
+				break;
+			}
+		}
+	} else {
+		pcie_dev->event_reg = NULL;
+		PCIE_DBG(pcie_dev, "Event is deregistered for RC %d\n",
+				pcie_dev->rc_idx);
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_deregister_event);
+
+int msm_pcie_recover_config(struct pci_dev *dev)
+{
+	int ret = 0;
+	struct msm_pcie_dev_t *pcie_dev;
+
+	if (dev) {
+		pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+		PCIE_DBG(pcie_dev,
+			"Recovery for the link of RC%d\n", pcie_dev->rc_idx);
+	} else {
+		pr_err("PCIe: the input pci dev is NULL.\n");
+		return -ENODEV;
+	}
+
+	if (msm_pcie_confirm_linkup(pcie_dev, true, true, pcie_dev->conf)) {
+		PCIE_DBG(pcie_dev,
+			"Recover config space of RC%d and its EP\n",
+			pcie_dev->rc_idx);
+		pcie_dev->shadow_en = false;
+		PCIE_DBG(pcie_dev, "Recover RC%d\n", pcie_dev->rc_idx);
+		msm_pcie_cfg_recover(pcie_dev, true);
+		PCIE_DBG(pcie_dev, "Recover EP of RC%d\n", pcie_dev->rc_idx);
+		msm_pcie_cfg_recover(pcie_dev, false);
+		PCIE_DBG(pcie_dev,
+			"Refreshing the saved config space in PCI framework for RC%d and its EP\n",
+			pcie_dev->rc_idx);
+		pci_save_state(pcie_dev->dev);
+		pci_save_state(dev);
+		pcie_dev->shadow_en = true;
+		PCIE_DBG(pcie_dev, "Turn on shadow for RC%d\n",
+			pcie_dev->rc_idx);
+	} else {
+		PCIE_ERR(pcie_dev,
+			"PCIe: the link of RC%d is not up yet; can't recover config space.\n",
+			pcie_dev->rc_idx);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_recover_config);
+
+int msm_pcie_shadow_control(struct pci_dev *dev, bool enable)
+{
+	int ret = 0;
+	struct msm_pcie_dev_t *pcie_dev;
+
+	if (dev) {
+		pcie_dev = PCIE_BUS_PRIV_DATA(dev->bus);
+		PCIE_DBG(pcie_dev,
+			"User requests to %s shadow\n",
+			enable ? "enable" : "disable");
+	} else {
+		pr_err("PCIe: the input pci dev is NULL.\n");
+		return -ENODEV;
+	}
+
+	PCIE_DBG(pcie_dev,
+		"The shadowing of RC%d is %s enabled currently.\n",
+		pcie_dev->rc_idx, pcie_dev->shadow_en ? "" : "not");
+
+	pcie_dev->shadow_en = enable;
+
+	PCIE_DBG(pcie_dev,
+		"Shadowing of RC%d is turned %s upon user's request.\n",
+		pcie_dev->rc_idx, enable ? "on" : "off");
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcie_shadow_control);
diff --git a/drivers/pinctrl/qcom/Kconfig b/drivers/pinctrl/qcom/Kconfig
index 9ddaff9..1058e5e 100644
--- a/drivers/pinctrl/qcom/Kconfig
+++ b/drivers/pinctrl/qcom/Kconfig
@@ -88,6 +88,15 @@
 	  Qualcomm Technologies Inc TLMM block found on the Qualcomm
 	  Technologies Inc MSMSKUNK platform.
 
+config PINCTRL_SDMBAT
+	tristate "Qualcomm Technologies Inc SDMBAT pin controller driver"
+	depends on GPIOLIB && OF
+	select PINCTRL_MSM
+	help
+	  This is the pinctrl, pinmux, pinconf and gpiolib driver for the
+	  Qualcomm Technologies Inc TLMM block found on the Qualcomm
+	  Technologies Inc SDMBAT platform.
+
 
 config PINCTRL_MSM8996
 	tristate "Qualcomm MSM8996 pin controller driver"
diff --git a/drivers/pinctrl/qcom/Makefile b/drivers/pinctrl/qcom/Makefile
index 6e14ef9..fd52c43 100644
--- a/drivers/pinctrl/qcom/Makefile
+++ b/drivers/pinctrl/qcom/Makefile
@@ -16,3 +16,4 @@
 obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-gpio.o
 obj-$(CONFIG_PINCTRL_QCOM_SSBI_PMIC) += pinctrl-ssbi-mpp.o
 obj-$(CONFIG_PINCTRL_MSMSKUNK) += pinctrl-msmskunk.o
+obj-$(CONFIG_PINCTRL_SDMBAT) += pinctrl-sdmbat.o
diff --git a/drivers/pinctrl/qcom/pinctrl-sdmbat.c b/drivers/pinctrl/qcom/pinctrl-sdmbat.c
new file mode 100644
index 0000000..3e4fdda
--- /dev/null
+++ b/drivers/pinctrl/qcom/pinctrl-sdmbat.c
@@ -0,0 +1,2283 @@
+/*
+ * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/pinctrl.h>
+
+#include "pinctrl-msm.h"
+
+#define FUNCTION(fname)					\
+	[msm_mux_##fname] = {				\
+		.name = #fname,				\
+		.groups = fname##_groups,		\
+		.ngroups = ARRAY_SIZE(fname##_groups),	\
+	}
+
+#define NORTH	0x00500000
+#define SOUTH	0x00900000
+#define WEST	0x00100000
+#define REG_SIZE 0x1000
+
+#define PINGROUP(id, base, f1, f2, f3, f4, f5, f6, f7, f8, f9)	\
+	{						\
+		.name = "gpio" #id,			\
+		.pins = gpio##id##_pins,		\
+		.npins = (unsigned int)ARRAY_SIZE(gpio##id##_pins),	\
+		.funcs = (int[]){			\
+			msm_mux_gpio, /* gpio mode */	\
+			msm_mux_##f1,			\
+			msm_mux_##f2,			\
+			msm_mux_##f3,			\
+			msm_mux_##f4,			\
+			msm_mux_##f5,			\
+			msm_mux_##f6,			\
+			msm_mux_##f7,			\
+			msm_mux_##f8,			\
+			msm_mux_##f9			\
+		},					\
+		.nfuncs = 10,				\
+		.ctl_reg = base + REG_SIZE * id,		\
+		.io_reg = base + 0x4 + REG_SIZE * id,		\
+		.intr_cfg_reg = base + 0x8 + REG_SIZE * id,	\
+		.intr_status_reg = base + 0xc + REG_SIZE * id,	\
+		.intr_target_reg = base + 0x8 + REG_SIZE * id,	\
+		.mux_bit = 2,			\
+		.pull_bit = 0,			\
+		.drv_bit = 6,			\
+		.oe_bit = 9,			\
+		.in_bit = 0,			\
+		.out_bit = 1,			\
+		.intr_enable_bit = 0,		\
+		.intr_status_bit = 0,		\
+		.intr_target_bit = 5,		\
+		.intr_target_kpss_val = 3,	\
+		.intr_raw_status_bit = 4,	\
+		.intr_polarity_bit = 1,		\
+		.intr_detection_bit = 2,	\
+		.intr_detection_width = 2,	\
+	}
+
+#define SDC_QDSD_PINGROUP(pg_name, ctl, pull, drv)	\
+	{						\
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = ctl,				\
+		.io_reg = 0,				\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = pull,			\
+		.drv_bit = drv,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = -1,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+
+#define UFS_RESET(pg_name, offset)				\
+	{						\
+		.name = #pg_name,			\
+		.pins = pg_name##_pins,			\
+		.npins = (unsigned int)ARRAY_SIZE(pg_name##_pins),	\
+		.ctl_reg = offset,			\
+		.io_reg = offset + 0x4,			\
+		.intr_cfg_reg = 0,			\
+		.intr_status_reg = 0,			\
+		.intr_target_reg = 0,			\
+		.mux_bit = -1,				\
+		.pull_bit = 3,				\
+		.drv_bit = 0,				\
+		.oe_bit = -1,				\
+		.in_bit = -1,				\
+		.out_bit = 0,				\
+		.intr_enable_bit = -1,			\
+		.intr_status_bit = -1,			\
+		.intr_target_bit = -1,			\
+		.intr_raw_status_bit = -1,		\
+		.intr_polarity_bit = -1,		\
+		.intr_detection_bit = -1,		\
+		.intr_detection_width = -1,		\
+	}
+static const struct pinctrl_pin_desc sdmbat_pins[] = {
+	PINCTRL_PIN(0, "GPIO_0"),
+	PINCTRL_PIN(1, "GPIO_1"),
+	PINCTRL_PIN(2, "GPIO_2"),
+	PINCTRL_PIN(3, "GPIO_3"),
+	PINCTRL_PIN(4, "GPIO_4"),
+	PINCTRL_PIN(5, "GPIO_5"),
+	PINCTRL_PIN(6, "GPIO_6"),
+	PINCTRL_PIN(7, "GPIO_7"),
+	PINCTRL_PIN(8, "GPIO_8"),
+	PINCTRL_PIN(9, "GPIO_9"),
+	PINCTRL_PIN(10, "GPIO_10"),
+	PINCTRL_PIN(11, "GPIO_11"),
+	PINCTRL_PIN(12, "GPIO_12"),
+	PINCTRL_PIN(13, "GPIO_13"),
+	PINCTRL_PIN(14, "GPIO_14"),
+	PINCTRL_PIN(15, "GPIO_15"),
+	PINCTRL_PIN(16, "GPIO_16"),
+	PINCTRL_PIN(17, "GPIO_17"),
+	PINCTRL_PIN(18, "GPIO_18"),
+	PINCTRL_PIN(19, "GPIO_19"),
+	PINCTRL_PIN(20, "GPIO_20"),
+	PINCTRL_PIN(21, "GPIO_21"),
+	PINCTRL_PIN(22, "GPIO_22"),
+	PINCTRL_PIN(23, "GPIO_23"),
+	PINCTRL_PIN(24, "GPIO_24"),
+	PINCTRL_PIN(25, "GPIO_25"),
+	PINCTRL_PIN(26, "GPIO_26"),
+	PINCTRL_PIN(27, "GPIO_27"),
+	PINCTRL_PIN(28, "GPIO_28"),
+	PINCTRL_PIN(29, "GPIO_29"),
+	PINCTRL_PIN(30, "GPIO_30"),
+	PINCTRL_PIN(31, "GPIO_31"),
+	PINCTRL_PIN(32, "GPIO_32"),
+	PINCTRL_PIN(33, "GPIO_33"),
+	PINCTRL_PIN(34, "GPIO_34"),
+	PINCTRL_PIN(35, "GPIO_35"),
+	PINCTRL_PIN(36, "GPIO_36"),
+	PINCTRL_PIN(37, "GPIO_37"),
+	PINCTRL_PIN(38, "GPIO_38"),
+	PINCTRL_PIN(39, "GPIO_39"),
+	PINCTRL_PIN(40, "GPIO_40"),
+	PINCTRL_PIN(41, "GPIO_41"),
+	PINCTRL_PIN(42, "GPIO_42"),
+	PINCTRL_PIN(43, "GPIO_43"),
+	PINCTRL_PIN(44, "GPIO_44"),
+	PINCTRL_PIN(45, "GPIO_45"),
+	PINCTRL_PIN(46, "GPIO_46"),
+	PINCTRL_PIN(47, "GPIO_47"),
+	PINCTRL_PIN(48, "GPIO_48"),
+	PINCTRL_PIN(49, "GPIO_49"),
+	PINCTRL_PIN(50, "GPIO_50"),
+	PINCTRL_PIN(51, "GPIO_51"),
+	PINCTRL_PIN(52, "GPIO_52"),
+	PINCTRL_PIN(53, "GPIO_53"),
+	PINCTRL_PIN(54, "GPIO_54"),
+	PINCTRL_PIN(55, "GPIO_55"),
+	PINCTRL_PIN(56, "GPIO_56"),
+	PINCTRL_PIN(57, "GPIO_57"),
+	PINCTRL_PIN(65, "GPIO_65"),
+	PINCTRL_PIN(66, "GPIO_66"),
+	PINCTRL_PIN(67, "GPIO_67"),
+	PINCTRL_PIN(68, "GPIO_68"),
+	PINCTRL_PIN(75, "GPIO_75"),
+	PINCTRL_PIN(76, "GPIO_76"),
+	PINCTRL_PIN(77, "GPIO_77"),
+	PINCTRL_PIN(78, "GPIO_78"),
+	PINCTRL_PIN(79, "GPIO_79"),
+	PINCTRL_PIN(80, "GPIO_80"),
+	PINCTRL_PIN(81, "GPIO_81"),
+	PINCTRL_PIN(82, "GPIO_82"),
+	PINCTRL_PIN(83, "GPIO_83"),
+	PINCTRL_PIN(84, "GPIO_84"),
+	PINCTRL_PIN(85, "GPIO_85"),
+	PINCTRL_PIN(86, "GPIO_86"),
+	PINCTRL_PIN(87, "GPIO_87"),
+	PINCTRL_PIN(88, "GPIO_88"),
+	PINCTRL_PIN(89, "GPIO_89"),
+	PINCTRL_PIN(90, "GPIO_90"),
+	PINCTRL_PIN(91, "GPIO_91"),
+	PINCTRL_PIN(92, "GPIO_92"),
+	PINCTRL_PIN(93, "GPIO_93"),
+	PINCTRL_PIN(94, "GPIO_94"),
+	PINCTRL_PIN(95, "GPIO_95"),
+	PINCTRL_PIN(96, "GPIO_96"),
+	PINCTRL_PIN(97, "GPIO_97"),
+	PINCTRL_PIN(98, "GPIO_98"),
+	PINCTRL_PIN(99, "GPIO_99"),
+	PINCTRL_PIN(100, "GPIO_100"),
+	PINCTRL_PIN(101, "GPIO_101"),
+	PINCTRL_PIN(102, "GPIO_102"),
+	PINCTRL_PIN(103, "GPIO_103"),
+	PINCTRL_PIN(105, "GPIO_105"),
+	PINCTRL_PIN(106, "GPIO_106"),
+	PINCTRL_PIN(107, "GPIO_107"),
+	PINCTRL_PIN(108, "GPIO_108"),
+	PINCTRL_PIN(109, "GPIO_109"),
+	PINCTRL_PIN(110, "GPIO_110"),
+	PINCTRL_PIN(111, "GPIO_111"),
+	PINCTRL_PIN(112, "GPIO_112"),
+	PINCTRL_PIN(113, "GPIO_113"),
+	PINCTRL_PIN(114, "GPIO_114"),
+	PINCTRL_PIN(115, "GPIO_115"),
+	PINCTRL_PIN(116, "GPIO_116"),
+	PINCTRL_PIN(117, "GPIO_117"),
+	PINCTRL_PIN(118, "GPIO_118"),
+	PINCTRL_PIN(119, "GPIO_119"),
+	PINCTRL_PIN(120, "GPIO_120"),
+	PINCTRL_PIN(121, "GPIO_121"),
+	PINCTRL_PIN(122, "GPIO_122"),
+	PINCTRL_PIN(123, "GPIO_123"),
+	PINCTRL_PIN(124, "GPIO_124"),
+	PINCTRL_PIN(125, "GPIO_125"),
+	PINCTRL_PIN(126, "GPIO_126"),
+	PINCTRL_PIN(127, "GPIO_127"),
+	PINCTRL_PIN(128, "GPIO_128"),
+	PINCTRL_PIN(129, "GPIO_129"),
+	PINCTRL_PIN(130, "GPIO_130"),
+	PINCTRL_PIN(131, "GPIO_131"),
+	PINCTRL_PIN(132, "GPIO_132"),
+	PINCTRL_PIN(133, "GPIO_133"),
+	PINCTRL_PIN(134, "GPIO_134"),
+	PINCTRL_PIN(135, "GPIO_135"),
+	PINCTRL_PIN(136, "GPIO_136"),
+	PINCTRL_PIN(137, "GPIO_137"),
+	PINCTRL_PIN(138, "GPIO_138"),
+	PINCTRL_PIN(139, "GPIO_139"),
+	PINCTRL_PIN(140, "GPIO_140"),
+	PINCTRL_PIN(141, "GPIO_141"),
+	PINCTRL_PIN(142, "GPIO_142"),
+	PINCTRL_PIN(143, "GPIO_143"),
+	PINCTRL_PIN(144, "GPIO_144"),
+	PINCTRL_PIN(145, "GPIO_145"),
+	PINCTRL_PIN(146, "GPIO_146"),
+	PINCTRL_PIN(147, "GPIO_147"),
+	PINCTRL_PIN(148, "GPIO_148"),
+	PINCTRL_PIN(149, "GPIO_149"),
+	PINCTRL_PIN(150, "SDC2_CLK"),
+	PINCTRL_PIN(151, "SDC2_CMD"),
+	PINCTRL_PIN(152, "SDC2_DATA"),
+};
+
+#define DECLARE_MSM_GPIO_PINS(pin) \
+	static const unsigned int gpio##pin##_pins[] = { pin }
+DECLARE_MSM_GPIO_PINS(0);
+DECLARE_MSM_GPIO_PINS(1);
+DECLARE_MSM_GPIO_PINS(2);
+DECLARE_MSM_GPIO_PINS(3);
+DECLARE_MSM_GPIO_PINS(4);
+DECLARE_MSM_GPIO_PINS(5);
+DECLARE_MSM_GPIO_PINS(6);
+DECLARE_MSM_GPIO_PINS(7);
+DECLARE_MSM_GPIO_PINS(8);
+DECLARE_MSM_GPIO_PINS(9);
+DECLARE_MSM_GPIO_PINS(10);
+DECLARE_MSM_GPIO_PINS(11);
+DECLARE_MSM_GPIO_PINS(12);
+DECLARE_MSM_GPIO_PINS(13);
+DECLARE_MSM_GPIO_PINS(14);
+DECLARE_MSM_GPIO_PINS(15);
+DECLARE_MSM_GPIO_PINS(16);
+DECLARE_MSM_GPIO_PINS(17);
+DECLARE_MSM_GPIO_PINS(18);
+DECLARE_MSM_GPIO_PINS(19);
+DECLARE_MSM_GPIO_PINS(20);
+DECLARE_MSM_GPIO_PINS(21);
+DECLARE_MSM_GPIO_PINS(22);
+DECLARE_MSM_GPIO_PINS(23);
+DECLARE_MSM_GPIO_PINS(24);
+DECLARE_MSM_GPIO_PINS(25);
+DECLARE_MSM_GPIO_PINS(26);
+DECLARE_MSM_GPIO_PINS(27);
+DECLARE_MSM_GPIO_PINS(28);
+DECLARE_MSM_GPIO_PINS(29);
+DECLARE_MSM_GPIO_PINS(30);
+DECLARE_MSM_GPIO_PINS(31);
+DECLARE_MSM_GPIO_PINS(32);
+DECLARE_MSM_GPIO_PINS(33);
+DECLARE_MSM_GPIO_PINS(34);
+DECLARE_MSM_GPIO_PINS(35);
+DECLARE_MSM_GPIO_PINS(36);
+DECLARE_MSM_GPIO_PINS(37);
+DECLARE_MSM_GPIO_PINS(38);
+DECLARE_MSM_GPIO_PINS(39);
+DECLARE_MSM_GPIO_PINS(40);
+DECLARE_MSM_GPIO_PINS(41);
+DECLARE_MSM_GPIO_PINS(42);
+DECLARE_MSM_GPIO_PINS(43);
+DECLARE_MSM_GPIO_PINS(44);
+DECLARE_MSM_GPIO_PINS(45);
+DECLARE_MSM_GPIO_PINS(46);
+DECLARE_MSM_GPIO_PINS(47);
+DECLARE_MSM_GPIO_PINS(48);
+DECLARE_MSM_GPIO_PINS(49);
+DECLARE_MSM_GPIO_PINS(50);
+DECLARE_MSM_GPIO_PINS(51);
+DECLARE_MSM_GPIO_PINS(52);
+DECLARE_MSM_GPIO_PINS(53);
+DECLARE_MSM_GPIO_PINS(54);
+DECLARE_MSM_GPIO_PINS(55);
+DECLARE_MSM_GPIO_PINS(56);
+DECLARE_MSM_GPIO_PINS(57);
+DECLARE_MSM_GPIO_PINS(65);
+DECLARE_MSM_GPIO_PINS(66);
+DECLARE_MSM_GPIO_PINS(67);
+DECLARE_MSM_GPIO_PINS(68);
+DECLARE_MSM_GPIO_PINS(75);
+DECLARE_MSM_GPIO_PINS(76);
+DECLARE_MSM_GPIO_PINS(77);
+DECLARE_MSM_GPIO_PINS(78);
+DECLARE_MSM_GPIO_PINS(79);
+DECLARE_MSM_GPIO_PINS(80);
+DECLARE_MSM_GPIO_PINS(81);
+DECLARE_MSM_GPIO_PINS(82);
+DECLARE_MSM_GPIO_PINS(83);
+DECLARE_MSM_GPIO_PINS(84);
+DECLARE_MSM_GPIO_PINS(85);
+DECLARE_MSM_GPIO_PINS(86);
+DECLARE_MSM_GPIO_PINS(87);
+DECLARE_MSM_GPIO_PINS(88);
+DECLARE_MSM_GPIO_PINS(89);
+DECLARE_MSM_GPIO_PINS(90);
+DECLARE_MSM_GPIO_PINS(91);
+DECLARE_MSM_GPIO_PINS(92);
+DECLARE_MSM_GPIO_PINS(93);
+DECLARE_MSM_GPIO_PINS(94);
+DECLARE_MSM_GPIO_PINS(95);
+DECLARE_MSM_GPIO_PINS(96);
+DECLARE_MSM_GPIO_PINS(97);
+DECLARE_MSM_GPIO_PINS(98);
+DECLARE_MSM_GPIO_PINS(99);
+DECLARE_MSM_GPIO_PINS(100);
+DECLARE_MSM_GPIO_PINS(101);
+DECLARE_MSM_GPIO_PINS(102);
+DECLARE_MSM_GPIO_PINS(103);
+DECLARE_MSM_GPIO_PINS(105);
+DECLARE_MSM_GPIO_PINS(106);
+DECLARE_MSM_GPIO_PINS(107);
+DECLARE_MSM_GPIO_PINS(108);
+DECLARE_MSM_GPIO_PINS(109);
+DECLARE_MSM_GPIO_PINS(110);
+DECLARE_MSM_GPIO_PINS(111);
+DECLARE_MSM_GPIO_PINS(112);
+DECLARE_MSM_GPIO_PINS(113);
+DECLARE_MSM_GPIO_PINS(114);
+DECLARE_MSM_GPIO_PINS(115);
+DECLARE_MSM_GPIO_PINS(116);
+DECLARE_MSM_GPIO_PINS(117);
+DECLARE_MSM_GPIO_PINS(118);
+DECLARE_MSM_GPIO_PINS(119);
+DECLARE_MSM_GPIO_PINS(120);
+DECLARE_MSM_GPIO_PINS(121);
+DECLARE_MSM_GPIO_PINS(122);
+DECLARE_MSM_GPIO_PINS(123);
+DECLARE_MSM_GPIO_PINS(124);
+DECLARE_MSM_GPIO_PINS(125);
+DECLARE_MSM_GPIO_PINS(126);
+DECLARE_MSM_GPIO_PINS(127);
+DECLARE_MSM_GPIO_PINS(128);
+DECLARE_MSM_GPIO_PINS(129);
+DECLARE_MSM_GPIO_PINS(130);
+DECLARE_MSM_GPIO_PINS(131);
+DECLARE_MSM_GPIO_PINS(132);
+DECLARE_MSM_GPIO_PINS(133);
+DECLARE_MSM_GPIO_PINS(134);
+DECLARE_MSM_GPIO_PINS(135);
+DECLARE_MSM_GPIO_PINS(136);
+DECLARE_MSM_GPIO_PINS(137);
+DECLARE_MSM_GPIO_PINS(138);
+DECLARE_MSM_GPIO_PINS(139);
+DECLARE_MSM_GPIO_PINS(140);
+DECLARE_MSM_GPIO_PINS(141);
+DECLARE_MSM_GPIO_PINS(142);
+DECLARE_MSM_GPIO_PINS(143);
+DECLARE_MSM_GPIO_PINS(144);
+DECLARE_MSM_GPIO_PINS(145);
+DECLARE_MSM_GPIO_PINS(146);
+DECLARE_MSM_GPIO_PINS(147);
+DECLARE_MSM_GPIO_PINS(148);
+DECLARE_MSM_GPIO_PINS(149);
+
+static const unsigned int sdc2_clk_pins[] = { 150 };
+static const unsigned int sdc2_cmd_pins[] = { 151 };
+static const unsigned int sdc2_data_pins[] = { 152 };
+
+enum sdmbat_functions {
+	msm_mux_qup0,
+	msm_mux_gpio,
+	msm_mux_reserved0,
+	msm_mux_reserved1,
+	msm_mux_reserved2,
+	msm_mux_reserved3,
+	msm_mux_qup9,
+	msm_mux_qdss_cti,
+	msm_mux_reserved4,
+	msm_mux_reserved5,
+	msm_mux_ddr_pxi0,
+	msm_mux_reserved6,
+	msm_mux_ddr_bist,
+	msm_mux_atest_tsens2,
+	msm_mux_vsense_trigger,
+	msm_mux_atest_usb1,
+	msm_mux_reserved7,
+	msm_mux_qup_l4,
+	msm_mux_GP_PDM1,
+	msm_mux_reserved8,
+	msm_mux_qup_l5,
+	msm_mux_reserved9,
+	msm_mux_mdp_vsync,
+	msm_mux_qup_l6,
+	msm_mux_wlan2_adc1,
+	msm_mux_atest_usb11,
+	msm_mux_ddr_pxi2,
+	msm_mux_reserved10,
+	msm_mux_edp_lcd,
+	msm_mux_dbg_out,
+	msm_mux_wlan2_adc0,
+	msm_mux_atest_usb10,
+	msm_mux_reserved11,
+	msm_mux_m_voc,
+	msm_mux_tsif1_sync,
+	msm_mux_ddr_pxi3,
+	msm_mux_reserved12,
+	msm_mux_cam_mclk,
+	msm_mux_pll_bypassnl,
+	msm_mux_qdss_gpio0,
+	msm_mux_reserved13,
+	msm_mux_pll_reset,
+	msm_mux_qdss_gpio1,
+	msm_mux_reserved14,
+	msm_mux_qdss_gpio2,
+	msm_mux_reserved15,
+	msm_mux_qdss_gpio3,
+	msm_mux_reserved16,
+	msm_mux_cci_i2c,
+	msm_mux_qup1,
+	msm_mux_qdss_gpio4,
+	msm_mux_reserved17,
+	msm_mux_cci_timer1,
+	msm_mux_gcc_gp3,
+	msm_mux_qdss_gpio,
+	msm_mux_reserved22,
+	msm_mux_cci_timer2,
+	msm_mux_qdss_gpio9,
+	msm_mux_reserved23,
+	msm_mux_cci_timer3,
+	msm_mux_cci_async,
+	msm_mux_qdss_gpio10,
+	msm_mux_reserved24,
+	msm_mux_cci_timer4,
+	msm_mux_qdss_gpio11,
+	msm_mux_reserved25,
+	msm_mux_qdss_gpio5,
+	msm_mux_reserved18,
+	msm_mux_qdss_gpio6,
+	msm_mux_reserved19,
+	msm_mux_qdss_gpio7,
+	msm_mux_reserved20,
+	msm_mux_cci_timer0,
+	msm_mux_gcc_gp2,
+	msm_mux_qdss_gpio8,
+	msm_mux_reserved21,
+	msm_mux_qdss_gpio12,
+	msm_mux_JITTER_BIST,
+	msm_mux_reserved26,
+	msm_mux_qup2,
+	msm_mux_qdss_gpio13,
+	msm_mux_PLL_BIST,
+	msm_mux_reserved27,
+	msm_mux_qdss_gpio14,
+	msm_mux_AGERA_PLL,
+	msm_mux_reserved28,
+	msm_mux_phase_flag1,
+	msm_mux_qdss_gpio15,
+	msm_mux_atest_tsens,
+	msm_mux_reserved29,
+	msm_mux_phase_flag2,
+	msm_mux_reserved30,
+	msm_mux_qup11,
+	msm_mux_qup14,
+	msm_mux_reserved31,
+	msm_mux_reserved32,
+	msm_mux_reserved33,
+	msm_mux_reserved34,
+	msm_mux_pci_e0,
+	msm_mux_QUP_L4,
+	msm_mux_reserved35,
+	msm_mux_QUP_L5,
+	msm_mux_reserved36,
+	msm_mux_QUP_L6,
+	msm_mux_reserved37,
+	msm_mux_usb_phy,
+	msm_mux_reserved38,
+	msm_mux_lpass_slimbus,
+	msm_mux_reserved39,
+	msm_mux_sd_write,
+	msm_mux_tsif1_error,
+	msm_mux_reserved40,
+	msm_mux_qup3,
+	msm_mux_reserved41,
+	msm_mux_reserved42,
+	msm_mux_reserved43,
+	msm_mux_reserved44,
+	msm_mux_bt_reset,
+	msm_mux_qup6,
+	msm_mux_reserved45,
+	msm_mux_reserved46,
+	msm_mux_reserved47,
+	msm_mux_reserved124,
+	msm_mux_reserved125,
+	msm_mux_reserved126,
+	msm_mux_reserved127,
+	msm_mux_reserved128,
+	msm_mux_reserved129,
+	msm_mux_qlink_request,
+	msm_mux_reserved130,
+	msm_mux_qlink_enable,
+	msm_mux_reserved131,
+	msm_mux_reserved132,
+	msm_mux_reserved133,
+	msm_mux_reserved134,
+	msm_mux_pa_indicator,
+	msm_mux_reserved135,
+	msm_mux_reserved136,
+	msm_mux_phase_flag26,
+	msm_mux_reserved137,
+	msm_mux_phase_flag27,
+	msm_mux_reserved138,
+	msm_mux_phase_flag28,
+	msm_mux_reserved139,
+	msm_mux_phase_flag6,
+	msm_mux_reserved140,
+	msm_mux_phase_flag29,
+	msm_mux_reserved141,
+	msm_mux_phase_flag30,
+	msm_mux_reserved142,
+	msm_mux_phase_flag31,
+	msm_mux_reserved143,
+	msm_mux_mss_lte,
+	msm_mux_reserved144,
+	msm_mux_reserved145,
+	msm_mux_reserved146,
+	msm_mux_reserved147,
+	msm_mux_reserved148,
+	msm_mux_reserved149,
+	msm_mux_reserved48,
+	msm_mux_qup12,
+	msm_mux_reserved49,
+	msm_mux_reserved50,
+	msm_mux_reserved51,
+	msm_mux_phase_flag16,
+	msm_mux_reserved52,
+	msm_mux_qup10,
+	msm_mux_phase_flag11,
+	msm_mux_reserved53,
+	msm_mux_GP_PDM0,
+	msm_mux_phase_flag12,
+	msm_mux_wlan1_adc1,
+	msm_mux_atest_usb13,
+	msm_mux_ddr_pxi1,
+	msm_mux_reserved54,
+	msm_mux_phase_flag13,
+	msm_mux_wlan1_adc0,
+	msm_mux_atest_usb12,
+	msm_mux_reserved55,
+	msm_mux_phase_flag17,
+	msm_mux_reserved56,
+	msm_mux_qua_mi2s,
+	msm_mux_gcc_gp1,
+	msm_mux_phase_flag18,
+	msm_mux_reserved57,
+	msm_mux_pri_mi2s,
+	msm_mux_qup8,
+	msm_mux_wsa_clk,
+	msm_mux_reserved65,
+	msm_mux_pri_mi2s_ws,
+	msm_mux_wsa_data,
+	msm_mux_reserved66,
+	msm_mux_wsa_en,
+	msm_mux_atest_usb2,
+	msm_mux_reserved67,
+	msm_mux_atest_usb23,
+	msm_mux_reserved68,
+	msm_mux_ter_mi2s,
+	msm_mux_phase_flag8,
+	msm_mux_atest_usb22,
+	msm_mux_reserved75,
+	msm_mux_phase_flag9,
+	msm_mux_atest_usb21,
+	msm_mux_reserved76,
+	msm_mux_phase_flag4,
+	msm_mux_atest_usb20,
+	msm_mux_reserved77,
+	msm_mux_ssc_irq,
+	msm_mux_reserved78,
+	msm_mux_sec_mi2s,
+	msm_mux_GP_PDM2,
+	msm_mux_reserved79,
+	msm_mux_reserved80,
+	msm_mux_qup15,
+	msm_mux_reserved81,
+	msm_mux_reserved82,
+	msm_mux_reserved83,
+	msm_mux_reserved84,
+	msm_mux_qup5,
+	msm_mux_reserved85,
+	msm_mux_copy_gp,
+	msm_mux_reserved86,
+	msm_mux_reserved87,
+	msm_mux_reserved88,
+	msm_mux_tsif1_clk,
+	msm_mux_qup4,
+	msm_mux_tgu_ch3,
+	msm_mux_phase_flag10,
+	msm_mux_reserved89,
+	msm_mux_tsif1_en,
+	msm_mux_mdp_vsync0,
+	msm_mux_mdp_vsync1,
+	msm_mux_mdp_vsync2,
+	msm_mux_mdp_vsync3,
+	msm_mux_tgu_ch0,
+	msm_mux_phase_flag0,
+	msm_mux_reserved90,
+	msm_mux_tsif1_data,
+	msm_mux_sdc4_cmd,
+	msm_mux_tgu_ch1,
+	msm_mux_reserved91,
+	msm_mux_tsif2_error,
+	msm_mux_sdc43,
+	msm_mux_vfr_1,
+	msm_mux_tgu_ch2,
+	msm_mux_reserved92,
+	msm_mux_tsif2_clk,
+	msm_mux_sdc4_clk,
+	msm_mux_qup7,
+	msm_mux_reserved93,
+	msm_mux_tsif2_en,
+	msm_mux_sdc42,
+	msm_mux_reserved94,
+	msm_mux_tsif2_data,
+	msm_mux_sdc41,
+	msm_mux_reserved95,
+	msm_mux_tsif2_sync,
+	msm_mux_sdc40,
+	msm_mux_phase_flag3,
+	msm_mux_reserved96,
+	msm_mux_ldo_en,
+	msm_mux_reserved97,
+	msm_mux_ldo_update,
+	msm_mux_reserved98,
+	msm_mux_phase_flag14,
+	msm_mux_prng_rosc,
+	msm_mux_reserved99,
+	msm_mux_phase_flag15,
+	msm_mux_reserved100,
+	msm_mux_phase_flag5,
+	msm_mux_reserved101,
+	msm_mux_pci_e1,
+	msm_mux_reserved102,
+	msm_mux_COPY_PHASE,
+	msm_mux_reserved103,
+	msm_mux_uim2_data,
+	msm_mux_qup13,
+	msm_mux_reserved105,
+	msm_mux_uim2_clk,
+	msm_mux_reserved106,
+	msm_mux_uim2_reset,
+	msm_mux_reserved107,
+	msm_mux_uim2_present,
+	msm_mux_reserved108,
+	msm_mux_uim1_data,
+	msm_mux_reserved109,
+	msm_mux_uim1_clk,
+	msm_mux_reserved110,
+	msm_mux_uim1_reset,
+	msm_mux_reserved111,
+	msm_mux_uim1_present,
+	msm_mux_reserved112,
+	msm_mux_uim_batt,
+	msm_mux_edp_hot,
+	msm_mux_reserved113,
+	msm_mux_NAV_PPS,
+	msm_mux_GPS_TX,
+	msm_mux_reserved114,
+	msm_mux_reserved115,
+	msm_mux_reserved116,
+	msm_mux_atest_char,
+	msm_mux_reserved117,
+	msm_mux_adsp_ext,
+	msm_mux_atest_char3,
+	msm_mux_reserved118,
+	msm_mux_atest_char2,
+	msm_mux_reserved119,
+	msm_mux_atest_char1,
+	msm_mux_reserved120,
+	msm_mux_atest_char0,
+	msm_mux_reserved121,
+	msm_mux_reserved122,
+	msm_mux_reserved123,
+	msm_mux_NA,
+};
+
+static const char * const qup0_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3",
+};
+static const char * const gpio_groups[] = {
+	"gpio0", "gpio1", "gpio2", "gpio3", "gpio4", "gpio5", "gpio6", "gpio7",
+	"gpio8", "gpio9", "gpio10", "gpio11", "gpio12", "gpio13", "gpio14",
+	"gpio15", "gpio16", "gpio17", "gpio18", "gpio19", "gpio20", "gpio21",
+	"gpio22", "gpio23", "gpio24", "gpio25", "gpio26", "gpio27", "gpio28",
+	"gpio29", "gpio30", "gpio31", "gpio32", "gpio33", "gpio34", "gpio35",
+	"gpio36", "gpio37", "gpio38", "gpio39", "gpio40", "gpio41", "gpio42",
+	"gpio43", "gpio44", "gpio46", "gpio47", "gpio48", "gpio49", "gpio50",
+	"gpio51", "gpio52", "gpio53", "gpio54", "gpio55", "gpio56", "gpio57",
+	"gpio65", "gpio66", "gpio75", "gpio76", "gpio77", "gpio81", "gpio82",
+	"gpio83", "gpio84", "gpio85", "gpio86", "gpio87", "gpio88", "gpio89",
+	"gpio90", "gpio91", "gpio92", "gpio93", "gpio94", "gpio95", "gpio96",
+	"gpio97", "gpio98", "gpio99", "gpio100", "gpio101", "gpio102",
+	"gpio103", "gpio105", "gpio106", "gpio107", "gpio108", "gpio109",
+	"gpio110", "gpio111", "gpio112", "gpio113", "gpio114", "gpio115",
+	"gpio116", "gpio126", "gpio127", "gpio128", "gpio129", "gpio130",
+	"gpio131", "gpio132", "gpio133", "gpio134", "gpio135", "gpio136",
+	"gpio137", "gpio138", "gpio139", "gpio140", "gpio141", "gpio142",
+	"gpio143", "gpio144", "gpio145", "gpio146", "gpio147", "gpio148",
+	"gpio149",
+};
+static const char * const reserved0_groups[] = {
+	"gpio0",
+};
+static const char * const reserved1_groups[] = {
+	"gpio1",
+};
+static const char * const reserved2_groups[] = {
+	"gpio2",
+};
+static const char * const reserved3_groups[] = {
+	"gpio3",
+};
+static const char * const qup9_groups[] = {
+	"gpio4", "gpio5", "gpio6", "gpio7",
+};
+static const char * const qdss_cti_groups[] = {
+	"gpio4", "gpio5", "gpio51", "gpio52", "gpio90", "gpio91",
+};
+static const char * const reserved4_groups[] = {
+	"gpio4",
+};
+static const char * const reserved5_groups[] = {
+	"gpio5",
+};
+static const char * const ddr_pxi0_groups[] = {
+	"gpio6", "gpio7",
+};
+static const char * const reserved6_groups[] = {
+	"gpio6",
+};
+static const char * const ddr_bist_groups[] = {
+	"gpio7", "gpio8", "gpio9", "gpio10",
+};
+static const char * const atest_tsens2_groups[] = {
+	"gpio7",
+};
+static const char * const vsense_trigger_groups[] = {
+	"gpio7",
+};
+static const char * const atest_usb1_groups[] = {
+	"gpio7",
+};
+static const char * const reserved7_groups[] = {
+	"gpio7",
+};
+static const char * const qup_l4_groups[] = {
+	"gpio8", "gpio105", "gpio123",
+};
+static const char * const GP_PDM1_groups[] = {
+	"gpio8", "gpio66",
+};
+static const char * const reserved8_groups[] = {
+	"gpio8",
+};
+static const char * const qup_l5_groups[] = {
+	"gpio9", "gpio106", "gpio124",
+};
+static const char * const reserved9_groups[] = {
+	"gpio9",
+};
+static const char * const mdp_vsync_groups[] = {
+	"gpio10", "gpio11", "gpio12", "gpio97", "gpio98",
+};
+static const char * const qup_l6_groups[] = {
+	"gpio10", "gpio107", "gpio125",
+};
+static const char * const wlan2_adc1_groups[] = {
+	"gpio10",
+};
+static const char * const atest_usb11_groups[] = {
+	"gpio10",
+};
+static const char * const ddr_pxi2_groups[] = {
+	"gpio10", "gpio11",
+};
+static const char * const reserved10_groups[] = {
+	"gpio10",
+};
+static const char * const edp_lcd_groups[] = {
+	"gpio11",
+};
+static const char * const dbg_out_groups[] = {
+	"gpio11",
+};
+static const char * const wlan2_adc0_groups[] = {
+	"gpio11",
+};
+static const char * const atest_usb10_groups[] = {
+	"gpio11",
+};
+static const char * const reserved11_groups[] = {
+	"gpio11",
+};
+static const char * const m_voc_groups[] = {
+	"gpio12",
+};
+static const char * const tsif1_sync_groups[] = {
+	"gpio12",
+};
+static const char * const ddr_pxi3_groups[] = {
+	"gpio12", "gpio13",
+};
+static const char * const reserved12_groups[] = {
+	"gpio12",
+};
+static const char * const cam_mclk_groups[] = {
+	"gpio13", "gpio14", "gpio15", "gpio16",
+};
+static const char * const pll_bypassnl_groups[] = {
+	"gpio13",
+};
+static const char * const qdss_gpio0_groups[] = {
+	"gpio13", "gpio117",
+};
+static const char * const reserved13_groups[] = {
+	"gpio13",
+};
+static const char * const pll_reset_groups[] = {
+	"gpio14",
+};
+static const char * const qdss_gpio1_groups[] = {
+	"gpio14", "gpio118",
+};
+static const char * const reserved14_groups[] = {
+	"gpio14",
+};
+static const char * const qdss_gpio2_groups[] = {
+	"gpio15", "gpio119",
+};
+static const char * const reserved15_groups[] = {
+	"gpio15",
+};
+static const char * const qdss_gpio3_groups[] = {
+	"gpio16", "gpio120",
+};
+static const char * const reserved16_groups[] = {
+	"gpio16",
+};
+static const char * const cci_i2c_groups[] = {
+	"gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qup1_groups[] = {
+	"gpio17", "gpio18", "gpio19", "gpio20",
+};
+static const char * const qdss_gpio4_groups[] = {
+	"gpio17", "gpio121",
+};
+static const char * const reserved17_groups[] = {
+	"gpio17",
+};
+static const char * const cci_timer1_groups[] = {
+	"gpio22",
+};
+static const char * const gcc_gp3_groups[] = {
+	"gpio22",
+};
+static const char * const qdss_gpio_groups[] = {
+	"gpio22", "gpio30", "gpio123", "gpio124",
+};
+static const char * const reserved22_groups[] = {
+	"gpio22",
+};
+static const char * const cci_timer2_groups[] = {
+	"gpio23",
+};
+static const char * const qdss_gpio9_groups[] = {
+	"gpio23", "gpio76",
+};
+static const char * const reserved23_groups[] = {
+	"gpio23",
+};
+static const char * const cci_timer3_groups[] = {
+	"gpio24",
+};
+static const char * const cci_async_groups[] = {
+	"gpio24", "gpio25", "gpio26",
+};
+static const char * const qdss_gpio10_groups[] = {
+	"gpio24", "gpio77",
+};
+static const char * const reserved24_groups[] = {
+	"gpio24",
+};
+static const char * const cci_timer4_groups[] = {
+	"gpio25",
+};
+static const char * const qdss_gpio11_groups[] = {
+	"gpio25", "gpio79",
+};
+static const char * const reserved25_groups[] = {
+	"gpio25",
+};
+static const char * const qdss_gpio5_groups[] = {
+	"gpio18", "gpio122",
+};
+static const char * const reserved18_groups[] = {
+	"gpio18",
+};
+static const char * const qdss_gpio6_groups[] = {
+	"gpio19", "gpio41",
+};
+static const char * const reserved19_groups[] = {
+	"gpio19",
+};
+static const char * const qdss_gpio7_groups[] = {
+	"gpio20", "gpio42",
+};
+static const char * const reserved20_groups[] = {
+	"gpio20",
+};
+static const char * const cci_timer0_groups[] = {
+	"gpio21",
+};
+static const char * const gcc_gp2_groups[] = {
+	"gpio21",
+};
+static const char * const qdss_gpio8_groups[] = {
+	"gpio21", "gpio75",
+};
+static const char * const reserved21_groups[] = {
+	"gpio21",
+};
+static const char * const qdss_gpio12_groups[] = {
+	"gpio26", "gpio80",
+};
+static const char * const JITTER_BIST_groups[] = {
+	"gpio26", "gpio35",
+};
+static const char * const reserved26_groups[] = {
+	"gpio26",
+};
+static const char * const qup2_groups[] = {
+	"gpio27", "gpio28", "gpio29", "gpio30",
+};
+static const char * const qdss_gpio13_groups[] = {
+	"gpio27", "gpio93",
+};
+static const char * const PLL_BIST_groups[] = {
+	"gpio27", "gpio36",
+};
+static const char * const reserved27_groups[] = {
+	"gpio27",
+};
+static const char * const qdss_gpio14_groups[] = {
+	"gpio28", "gpio43",
+};
+static const char * const AGERA_PLL_groups[] = {
+	"gpio28", "gpio37",
+};
+static const char * const reserved28_groups[] = {
+	"gpio28",
+};
+static const char * const phase_flag1_groups[] = {
+	"gpio29",
+};
+static const char * const qdss_gpio15_groups[] = {
+	"gpio29", "gpio44",
+};
+static const char * const atest_tsens_groups[] = {
+	"gpio29",
+};
+static const char * const reserved29_groups[] = {
+	"gpio29",
+};
+static const char * const phase_flag2_groups[] = {
+	"gpio30",
+};
+static const char * const reserved30_groups[] = {
+	"gpio30",
+};
+static const char * const qup11_groups[] = {
+	"gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const qup14_groups[] = {
+	"gpio31", "gpio32", "gpio33", "gpio34",
+};
+static const char * const reserved31_groups[] = {
+	"gpio31",
+};
+static const char * const reserved32_groups[] = {
+	"gpio32",
+};
+static const char * const reserved33_groups[] = {
+	"gpio33",
+};
+static const char * const reserved34_groups[] = {
+	"gpio34",
+};
+static const char * const pci_e0_groups[] = {
+	"gpio35", "gpio36",
+};
+static const char * const QUP_L4_groups[] = {
+	"gpio35", "gpio75",
+};
+static const char * const reserved35_groups[] = {
+	"gpio35",
+};
+static const char * const QUP_L5_groups[] = {
+	"gpio36", "gpio76",
+};
+static const char * const reserved36_groups[] = {
+	"gpio36",
+};
+static const char * const QUP_L6_groups[] = {
+	"gpio37", "gpio77",
+};
+static const char * const reserved37_groups[] = {
+	"gpio37",
+};
+static const char * const usb_phy_groups[] = {
+	"gpio38",
+};
+static const char * const reserved38_groups[] = {
+	"gpio38",
+};
+static const char * const lpass_slimbus_groups[] = {
+	"gpio39",
+};
+static const char * const reserved39_groups[] = {
+	"gpio39",
+};
+static const char * const sd_write_groups[] = {
+	"gpio40",
+};
+static const char * const tsif1_error_groups[] = {
+	"gpio40",
+};
+static const char * const reserved40_groups[] = {
+	"gpio40",
+};
+static const char * const qup3_groups[] = {
+	"gpio41", "gpio42", "gpio43", "gpio44",
+};
+static const char * const reserved41_groups[] = {
+	"gpio41",
+};
+static const char * const reserved42_groups[] = {
+	"gpio42",
+};
+static const char * const reserved43_groups[] = {
+	"gpio43",
+};
+static const char * const reserved44_groups[] = {
+	"gpio44",
+};
+static const char * const bt_reset_groups[] = {
+	"gpio45",
+};
+static const char * const qup6_groups[] = {
+	"gpio45", "gpio46", "gpio47", "gpio48",
+};
+static const char * const reserved45_groups[] = {
+	"gpio45",
+};
+static const char * const reserved46_groups[] = {
+	"gpio46",
+};
+static const char * const reserved47_groups[] = {
+	"gpio47",
+};
+static const char * const reserved124_groups[] = {
+	"gpio124",
+};
+static const char * const reserved125_groups[] = {
+	"gpio125",
+};
+static const char * const reserved126_groups[] = {
+	"gpio126",
+};
+static const char * const reserved127_groups[] = {
+	"gpio127",
+};
+static const char * const reserved128_groups[] = {
+	"gpio128",
+};
+static const char * const reserved129_groups[] = {
+	"gpio129",
+};
+static const char * const qlink_request_groups[] = {
+	"gpio130",
+};
+static const char * const reserved130_groups[] = {
+	"gpio130",
+};
+static const char * const qlink_enable_groups[] = {
+	"gpio131",
+};
+static const char * const reserved131_groups[] = {
+	"gpio131",
+};
+static const char * const reserved132_groups[] = {
+	"gpio132",
+};
+static const char * const reserved133_groups[] = {
+	"gpio133",
+};
+static const char * const reserved134_groups[] = {
+	"gpio134",
+};
+static const char * const pa_indicator_groups[] = {
+	"gpio135",
+};
+static const char * const reserved135_groups[] = {
+	"gpio135",
+};
+static const char * const reserved136_groups[] = {
+	"gpio136",
+};
+static const char * const phase_flag26_groups[] = {
+	"gpio137",
+};
+static const char * const reserved137_groups[] = {
+	"gpio137",
+};
+static const char * const phase_flag27_groups[] = {
+	"gpio138",
+};
+static const char * const reserved138_groups[] = {
+	"gpio138",
+};
+static const char * const phase_flag28_groups[] = {
+	"gpio139",
+};
+static const char * const reserved139_groups[] = {
+	"gpio139",
+};
+static const char * const phase_flag6_groups[] = {
+	"gpio140",
+};
+static const char * const reserved140_groups[] = {
+	"gpio140",
+};
+static const char * const phase_flag29_groups[] = {
+	"gpio141",
+};
+static const char * const reserved141_groups[] = {
+	"gpio141",
+};
+static const char * const phase_flag30_groups[] = {
+	"gpio142",
+};
+static const char * const reserved142_groups[] = {
+	"gpio142",
+};
+static const char * const phase_flag31_groups[] = {
+	"gpio143",
+};
+static const char * const reserved143_groups[] = {
+	"gpio143",
+};
+static const char * const mss_lte_groups[] = {
+	"gpio144", "gpio145",
+};
+static const char * const reserved144_groups[] = {
+	"gpio144",
+};
+static const char * const reserved145_groups[] = {
+	"gpio145",
+};
+static const char * const reserved146_groups[] = {
+	"gpio146",
+};
+static const char * const reserved147_groups[] = {
+	"gpio147",
+};
+static const char * const reserved148_groups[] = {
+	"gpio148",
+};
+static const char * const reserved149_groups[] = {
+	"gpio149", "gpio149",
+};
+static const char * const reserved48_groups[] = {
+	"gpio48",
+};
+static const char * const qup12_groups[] = {
+	"gpio49", "gpio50", "gpio51", "gpio52",
+};
+static const char * const reserved49_groups[] = {
+	"gpio49",
+};
+static const char * const reserved50_groups[] = {
+	"gpio50",
+};
+static const char * const reserved51_groups[] = {
+	"gpio51",
+};
+static const char * const phase_flag16_groups[] = {
+	"gpio52",
+};
+static const char * const reserved52_groups[] = {
+	"gpio52",
+};
+static const char * const qup10_groups[] = {
+	"gpio53", "gpio54", "gpio55", "gpio56",
+};
+static const char * const phase_flag11_groups[] = {
+	"gpio53",
+};
+static const char * const reserved53_groups[] = {
+	"gpio53",
+};
+static const char * const GP_PDM0_groups[] = {
+	"gpio54", "gpio95",
+};
+static const char * const phase_flag12_groups[] = {
+	"gpio54",
+};
+static const char * const wlan1_adc1_groups[] = {
+	"gpio54",
+};
+static const char * const atest_usb13_groups[] = {
+	"gpio54",
+};
+static const char * const ddr_pxi1_groups[] = {
+	"gpio54", "gpio55",
+};
+static const char * const reserved54_groups[] = {
+	"gpio54",
+};
+static const char * const phase_flag13_groups[] = {
+	"gpio55",
+};
+static const char * const wlan1_adc0_groups[] = {
+	"gpio55",
+};
+static const char * const atest_usb12_groups[] = {
+	"gpio55",
+};
+static const char * const reserved55_groups[] = {
+	"gpio55",
+};
+static const char * const phase_flag17_groups[] = {
+	"gpio56",
+};
+static const char * const reserved56_groups[] = {
+	"gpio56",
+};
+static const char * const qua_mi2s_groups[] = {
+	"gpio57",
+};
+static const char * const gcc_gp1_groups[] = {
+	"gpio57", "gpio78",
+};
+static const char * const phase_flag18_groups[] = {
+	"gpio57",
+};
+static const char * const reserved57_groups[] = {
+	"gpio57",
+};
+static const char * const pri_mi2s_groups[] = {
+	"gpio65", "gpio67", "gpio68",
+};
+static const char * const qup8_groups[] = {
+	"gpio65", "gpio66", "gpio67", "gpio68",
+};
+static const char * const wsa_clk_groups[] = {
+	"gpio65",
+};
+static const char * const reserved65_groups[] = {
+	"gpio65",
+};
+static const char * const pri_mi2s_ws_groups[] = {
+	"gpio66",
+};
+static const char * const wsa_data_groups[] = {
+	"gpio66",
+};
+static const char * const reserved66_groups[] = {
+	"gpio66",
+};
+static const char * const wsa_en_groups[] = {
+	"gpio67", "gpio68",
+};
+static const char * const atest_usb2_groups[] = {
+	"gpio67",
+};
+static const char * const reserved67_groups[] = {
+	"gpio67",
+};
+static const char * const atest_usb23_groups[] = {
+	"gpio68",
+};
+static const char * const reserved68_groups[] = {
+	"gpio68",
+};
+static const char * const ter_mi2s_groups[] = {
+	"gpio75", "gpio76", "gpio77", "gpio78",
+};
+static const char * const phase_flag8_groups[] = {
+	"gpio75",
+};
+static const char * const atest_usb22_groups[] = {
+	"gpio75",
+};
+static const char * const reserved75_groups[] = {
+	"gpio75",
+};
+static const char * const phase_flag9_groups[] = {
+	"gpio76",
+};
+static const char * const atest_usb21_groups[] = {
+	"gpio76",
+};
+static const char * const reserved76_groups[] = {
+	"gpio76",
+};
+static const char * const phase_flag4_groups[] = {
+	"gpio77",
+};
+static const char * const atest_usb20_groups[] = {
+	"gpio77",
+};
+static const char * const reserved77_groups[] = {
+	"gpio77",
+};
+static const char * const ssc_irq_groups[] = {
+	"gpio78", "gpio79", "gpio80", "gpio117", "gpio118", "gpio119",
+	"gpio120", "gpio121", "gpio122", "gpio123", "gpio124", "gpio125",
+};
+static const char * const reserved78_groups[] = {
+	"gpio78",
+};
+static const char * const sec_mi2s_groups[] = {
+	"gpio79", "gpio80", "gpio81", "gpio82", "gpio83",
+};
+static const char * const GP_PDM2_groups[] = {
+	"gpio79",
+};
+static const char * const reserved79_groups[] = {
+	"gpio79",
+};
+static const char * const reserved80_groups[] = {
+	"gpio80",
+};
+static const char * const qup15_groups[] = {
+	"gpio81", "gpio82", "gpio83", "gpio84",
+};
+static const char * const reserved81_groups[] = {
+	"gpio81",
+};
+static const char * const reserved82_groups[] = {
+	"gpio82",
+};
+static const char * const reserved83_groups[] = {
+	"gpio83",
+};
+static const char * const reserved84_groups[] = {
+	"gpio84",
+};
+static const char * const qup5_groups[] = {
+	"gpio85", "gpio86", "gpio87", "gpio88",
+};
+static const char * const reserved85_groups[] = {
+	"gpio85",
+};
+static const char * const copy_gp_groups[] = {
+	"gpio86",
+};
+static const char * const reserved86_groups[] = {
+	"gpio86",
+};
+static const char * const reserved87_groups[] = {
+	"gpio87",
+};
+static const char * const reserved88_groups[] = {
+	"gpio88",
+};
+static const char * const tsif1_clk_groups[] = {
+	"gpio89",
+};
+static const char * const qup4_groups[] = {
+	"gpio89", "gpio90", "gpio91", "gpio92",
+};
+static const char * const tgu_ch3_groups[] = {
+	"gpio89",
+};
+static const char * const phase_flag10_groups[] = {
+	"gpio89",
+};
+static const char * const reserved89_groups[] = {
+	"gpio89",
+};
+static const char * const tsif1_en_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync0_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync1_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync2_groups[] = {
+	"gpio90",
+};
+static const char * const mdp_vsync3_groups[] = {
+	"gpio90",
+};
+static const char * const tgu_ch0_groups[] = {
+	"gpio90",
+};
+static const char * const phase_flag0_groups[] = {
+	"gpio90",
+};
+static const char * const reserved90_groups[] = {
+	"gpio90",
+};
+static const char * const tsif1_data_groups[] = {
+	"gpio91",
+};
+static const char * const sdc4_cmd_groups[] = {
+	"gpio91",
+};
+static const char * const tgu_ch1_groups[] = {
+	"gpio91",
+};
+static const char * const reserved91_groups[] = {
+	"gpio91",
+};
+static const char * const tsif2_error_groups[] = {
+	"gpio92",
+};
+static const char * const sdc43_groups[] = {
+	"gpio92",
+};
+static const char * const vfr_1_groups[] = {
+	"gpio92",
+};
+static const char * const tgu_ch2_groups[] = {
+	"gpio92",
+};
+static const char * const reserved92_groups[] = {
+	"gpio92",
+};
+static const char * const tsif2_clk_groups[] = {
+	"gpio93",
+};
+static const char * const sdc4_clk_groups[] = {
+	"gpio93",
+};
+static const char * const qup7_groups[] = {
+	"gpio93", "gpio94", "gpio95", "gpio96",
+};
+static const char * const reserved93_groups[] = {
+	"gpio93",
+};
+static const char * const tsif2_en_groups[] = {
+	"gpio94",
+};
+static const char * const sdc42_groups[] = {
+	"gpio94",
+};
+static const char * const reserved94_groups[] = {
+	"gpio94",
+};
+static const char * const tsif2_data_groups[] = {
+	"gpio95",
+};
+static const char * const sdc41_groups[] = {
+	"gpio95",
+};
+static const char * const reserved95_groups[] = {
+	"gpio95",
+};
+static const char * const tsif2_sync_groups[] = {
+	"gpio96",
+};
+static const char * const sdc40_groups[] = {
+	"gpio96",
+};
+static const char * const phase_flag3_groups[] = {
+	"gpio96",
+};
+static const char * const reserved96_groups[] = {
+	"gpio96",
+};
+static const char * const ldo_en_groups[] = {
+	"gpio97",
+};
+static const char * const reserved97_groups[] = {
+	"gpio97",
+};
+static const char * const ldo_update_groups[] = {
+	"gpio98",
+};
+static const char * const reserved98_groups[] = {
+	"gpio98",
+};
+static const char * const phase_flag14_groups[] = {
+	"gpio99",
+};
+static const char * const prng_rosc_groups[] = {
+	"gpio99", "gpio102",
+};
+static const char * const reserved99_groups[] = {
+	"gpio99",
+};
+static const char * const phase_flag15_groups[] = {
+	"gpio100",
+};
+static const char * const reserved100_groups[] = {
+	"gpio100",
+};
+static const char * const phase_flag5_groups[] = {
+	"gpio101",
+};
+static const char * const reserved101_groups[] = {
+	"gpio101",
+};
+static const char * const pci_e1_groups[] = {
+	"gpio102", "gpio103",
+};
+static const char * const reserved102_groups[] = {
+	"gpio102",
+};
+static const char * const COPY_PHASE_groups[] = {
+	"gpio103",
+};
+static const char * const reserved103_groups[] = {
+	"gpio103",
+};
+static const char * const uim2_data_groups[] = {
+	"gpio105",
+};
+static const char * const qup13_groups[] = {
+	"gpio105", "gpio106", "gpio107", "gpio108",
+};
+static const char * const reserved105_groups[] = {
+	"gpio105",
+};
+static const char * const uim2_clk_groups[] = {
+	"gpio106",
+};
+static const char * const reserved106_groups[] = {
+	"gpio106",
+};
+static const char * const uim2_reset_groups[] = {
+	"gpio107",
+};
+static const char * const reserved107_groups[] = {
+	"gpio107",
+};
+static const char * const uim2_present_groups[] = {
+	"gpio108",
+};
+static const char * const reserved108_groups[] = {
+	"gpio108",
+};
+static const char * const uim1_data_groups[] = {
+	"gpio109",
+};
+static const char * const reserved109_groups[] = {
+	"gpio109",
+};
+static const char * const uim1_clk_groups[] = {
+	"gpio110",
+};
+static const char * const reserved110_groups[] = {
+	"gpio110",
+};
+static const char * const uim1_reset_groups[] = {
+	"gpio111",
+};
+static const char * const reserved111_groups[] = {
+	"gpio111",
+};
+static const char * const uim1_present_groups[] = {
+	"gpio112",
+};
+static const char * const reserved112_groups[] = {
+	"gpio112",
+};
+static const char * const uim_batt_groups[] = {
+	"gpio113",
+};
+static const char * const edp_hot_groups[] = {
+	"gpio113",
+};
+static const char * const reserved113_groups[] = {
+	"gpio113",
+};
+static const char * const NAV_PPS_groups[] = {
+	"gpio114", "gpio114", "gpio115", "gpio115", "gpio128", "gpio128",
+	"gpio129", "gpio129", "gpio143", "gpio143",
+};
+static const char * const GPS_TX_groups[] = {
+	"gpio114", "gpio115", "gpio128", "gpio129", "gpio143", "gpio145",
+};
+static const char * const reserved114_groups[] = {
+	"gpio114",
+};
+static const char * const reserved115_groups[] = {
+	"gpio115",
+};
+static const char * const reserved116_groups[] = {
+	"gpio116",
+};
+static const char * const atest_char_groups[] = {
+	"gpio117",
+};
+static const char * const reserved117_groups[] = {
+	"gpio117",
+};
+static const char * const adsp_ext_groups[] = {
+	"gpio118",
+};
+static const char * const atest_char3_groups[] = {
+	"gpio118",
+};
+static const char * const reserved118_groups[] = {
+	"gpio118",
+};
+static const char * const atest_char2_groups[] = {
+	"gpio119",
+};
+static const char * const reserved119_groups[] = {
+	"gpio119",
+};
+static const char * const atest_char1_groups[] = {
+	"gpio120",
+};
+static const char * const reserved120_groups[] = {
+	"gpio120",
+};
+static const char * const atest_char0_groups[] = {
+	"gpio121",
+};
+static const char * const reserved121_groups[] = {
+	"gpio121",
+};
+static const char * const reserved122_groups[] = {
+	"gpio122",
+};
+static const char * const reserved123_groups[] = {
+	"gpio123",
+};
+
+static const struct msm_function sdmbat_functions[] = {
+	FUNCTION(qup0),
+	FUNCTION(gpio),
+	FUNCTION(reserved0),
+	FUNCTION(reserved1),
+	FUNCTION(reserved2),
+	FUNCTION(reserved3),
+	FUNCTION(qup9),
+	FUNCTION(qdss_cti),
+	FUNCTION(reserved4),
+	FUNCTION(reserved5),
+	FUNCTION(ddr_pxi0),
+	FUNCTION(reserved6),
+	FUNCTION(ddr_bist),
+	FUNCTION(atest_tsens2),
+	FUNCTION(vsense_trigger),
+	FUNCTION(atest_usb1),
+	FUNCTION(reserved7),
+	FUNCTION(qup_l4),
+	FUNCTION(GP_PDM1),
+	FUNCTION(reserved8),
+	FUNCTION(qup_l5),
+	FUNCTION(reserved9),
+	FUNCTION(mdp_vsync),
+	FUNCTION(qup_l6),
+	FUNCTION(wlan2_adc1),
+	FUNCTION(atest_usb11),
+	FUNCTION(ddr_pxi2),
+	FUNCTION(reserved10),
+	FUNCTION(edp_lcd),
+	FUNCTION(dbg_out),
+	FUNCTION(wlan2_adc0),
+	FUNCTION(atest_usb10),
+	FUNCTION(reserved11),
+	FUNCTION(m_voc),
+	FUNCTION(tsif1_sync),
+	FUNCTION(ddr_pxi3),
+	FUNCTION(reserved12),
+	FUNCTION(cam_mclk),
+	FUNCTION(pll_bypassnl),
+	FUNCTION(qdss_gpio0),
+	FUNCTION(reserved13),
+	FUNCTION(pll_reset),
+	FUNCTION(qdss_gpio1),
+	FUNCTION(reserved14),
+	FUNCTION(qdss_gpio2),
+	FUNCTION(reserved15),
+	FUNCTION(qdss_gpio3),
+	FUNCTION(reserved16),
+	FUNCTION(cci_i2c),
+	FUNCTION(qup1),
+	FUNCTION(qdss_gpio4),
+	FUNCTION(reserved17),
+	FUNCTION(cci_timer1),
+	FUNCTION(gcc_gp3),
+	FUNCTION(qdss_gpio),
+	FUNCTION(reserved22),
+	FUNCTION(cci_timer2),
+	FUNCTION(qdss_gpio9),
+	FUNCTION(reserved23),
+	FUNCTION(cci_timer3),
+	FUNCTION(cci_async),
+	FUNCTION(qdss_gpio10),
+	FUNCTION(reserved24),
+	FUNCTION(cci_timer4),
+	FUNCTION(qdss_gpio11),
+	FUNCTION(reserved25),
+	FUNCTION(qdss_gpio5),
+	FUNCTION(reserved18),
+	FUNCTION(qdss_gpio6),
+	FUNCTION(reserved19),
+	FUNCTION(qdss_gpio7),
+	FUNCTION(reserved20),
+	FUNCTION(cci_timer0),
+	FUNCTION(gcc_gp2),
+	FUNCTION(qdss_gpio8),
+	FUNCTION(reserved21),
+	FUNCTION(qdss_gpio12),
+	FUNCTION(JITTER_BIST),
+	FUNCTION(reserved26),
+	FUNCTION(qup2),
+	FUNCTION(qdss_gpio13),
+	FUNCTION(PLL_BIST),
+	FUNCTION(reserved27),
+	FUNCTION(qdss_gpio14),
+	FUNCTION(AGERA_PLL),
+	FUNCTION(reserved28),
+	FUNCTION(phase_flag1),
+	FUNCTION(qdss_gpio15),
+	FUNCTION(atest_tsens),
+	FUNCTION(reserved29),
+	FUNCTION(phase_flag2),
+	FUNCTION(reserved30),
+	FUNCTION(qup11),
+	FUNCTION(qup14),
+	FUNCTION(reserved31),
+	FUNCTION(reserved32),
+	FUNCTION(reserved33),
+	FUNCTION(reserved34),
+	FUNCTION(pci_e0),
+	FUNCTION(QUP_L4),
+	FUNCTION(reserved35),
+	FUNCTION(QUP_L5),
+	FUNCTION(reserved36),
+	FUNCTION(QUP_L6),
+	FUNCTION(reserved37),
+	FUNCTION(usb_phy),
+	FUNCTION(reserved38),
+	FUNCTION(lpass_slimbus),
+	FUNCTION(reserved39),
+	FUNCTION(sd_write),
+	FUNCTION(tsif1_error),
+	FUNCTION(reserved40),
+	FUNCTION(qup3),
+	FUNCTION(reserved41),
+	FUNCTION(reserved42),
+	FUNCTION(reserved43),
+	FUNCTION(reserved44),
+	FUNCTION(bt_reset),
+	FUNCTION(qup6),
+	FUNCTION(reserved45),
+	FUNCTION(reserved46),
+	FUNCTION(reserved47),
+	FUNCTION(reserved124),
+	FUNCTION(reserved125),
+	FUNCTION(reserved126),
+	FUNCTION(reserved127),
+	FUNCTION(reserved128),
+	FUNCTION(reserved129),
+	FUNCTION(qlink_request),
+	FUNCTION(reserved130),
+	FUNCTION(qlink_enable),
+	FUNCTION(reserved131),
+	FUNCTION(reserved132),
+	FUNCTION(reserved133),
+	FUNCTION(reserved134),
+	FUNCTION(pa_indicator),
+	FUNCTION(reserved135),
+	FUNCTION(reserved136),
+	FUNCTION(phase_flag26),
+	FUNCTION(reserved137),
+	FUNCTION(phase_flag27),
+	FUNCTION(reserved138),
+	FUNCTION(phase_flag28),
+	FUNCTION(reserved139),
+	FUNCTION(phase_flag6),
+	FUNCTION(reserved140),
+	FUNCTION(phase_flag29),
+	FUNCTION(reserved141),
+	FUNCTION(phase_flag30),
+	FUNCTION(reserved142),
+	FUNCTION(phase_flag31),
+	FUNCTION(reserved143),
+	FUNCTION(mss_lte),
+	FUNCTION(reserved144),
+	FUNCTION(reserved145),
+	FUNCTION(reserved146),
+	FUNCTION(reserved147),
+	FUNCTION(reserved148),
+	FUNCTION(reserved149),
+	FUNCTION(reserved48),
+	FUNCTION(qup12),
+	FUNCTION(reserved49),
+	FUNCTION(reserved50),
+	FUNCTION(reserved51),
+	FUNCTION(phase_flag16),
+	FUNCTION(reserved52),
+	FUNCTION(qup10),
+	FUNCTION(phase_flag11),
+	FUNCTION(reserved53),
+	FUNCTION(GP_PDM0),
+	FUNCTION(phase_flag12),
+	FUNCTION(wlan1_adc1),
+	FUNCTION(atest_usb13),
+	FUNCTION(ddr_pxi1),
+	FUNCTION(reserved54),
+	FUNCTION(phase_flag13),
+	FUNCTION(wlan1_adc0),
+	FUNCTION(atest_usb12),
+	FUNCTION(reserved55),
+	FUNCTION(phase_flag17),
+	FUNCTION(reserved56),
+	FUNCTION(qua_mi2s),
+	FUNCTION(gcc_gp1),
+	FUNCTION(phase_flag18),
+	FUNCTION(reserved57),
+	FUNCTION(pri_mi2s),
+	FUNCTION(qup8),
+	FUNCTION(wsa_clk),
+	FUNCTION(reserved65),
+	FUNCTION(pri_mi2s_ws),
+	FUNCTION(wsa_data),
+	FUNCTION(reserved66),
+	FUNCTION(wsa_en),
+	FUNCTION(atest_usb2),
+	FUNCTION(reserved67),
+	FUNCTION(atest_usb23),
+	FUNCTION(reserved68),
+	FUNCTION(ter_mi2s),
+	FUNCTION(phase_flag8),
+	FUNCTION(atest_usb22),
+	FUNCTION(reserved75),
+	FUNCTION(phase_flag9),
+	FUNCTION(atest_usb21),
+	FUNCTION(reserved76),
+	FUNCTION(phase_flag4),
+	FUNCTION(atest_usb20),
+	FUNCTION(reserved77),
+	FUNCTION(ssc_irq),
+	FUNCTION(reserved78),
+	FUNCTION(sec_mi2s),
+	FUNCTION(GP_PDM2),
+	FUNCTION(reserved79),
+	FUNCTION(reserved80),
+	FUNCTION(qup15),
+	FUNCTION(reserved81),
+	FUNCTION(reserved82),
+	FUNCTION(reserved83),
+	FUNCTION(reserved84),
+	FUNCTION(qup5),
+	FUNCTION(reserved85),
+	FUNCTION(copy_gp),
+	FUNCTION(reserved86),
+	FUNCTION(reserved87),
+	FUNCTION(reserved88),
+	FUNCTION(tsif1_clk),
+	FUNCTION(qup4),
+	FUNCTION(tgu_ch3),
+	FUNCTION(phase_flag10),
+	FUNCTION(reserved89),
+	FUNCTION(tsif1_en),
+	FUNCTION(mdp_vsync0),
+	FUNCTION(mdp_vsync1),
+	FUNCTION(mdp_vsync2),
+	FUNCTION(mdp_vsync3),
+	FUNCTION(tgu_ch0),
+	FUNCTION(phase_flag0),
+	FUNCTION(reserved90),
+	FUNCTION(tsif1_data),
+	FUNCTION(sdc4_cmd),
+	FUNCTION(tgu_ch1),
+	FUNCTION(reserved91),
+	FUNCTION(tsif2_error),
+	FUNCTION(sdc43),
+	FUNCTION(vfr_1),
+	FUNCTION(tgu_ch2),
+	FUNCTION(reserved92),
+	FUNCTION(tsif2_clk),
+	FUNCTION(sdc4_clk),
+	FUNCTION(qup7),
+	FUNCTION(reserved93),
+	FUNCTION(tsif2_en),
+	FUNCTION(sdc42),
+	FUNCTION(reserved94),
+	FUNCTION(tsif2_data),
+	FUNCTION(sdc41),
+	FUNCTION(reserved95),
+	FUNCTION(tsif2_sync),
+	FUNCTION(sdc40),
+	FUNCTION(phase_flag3),
+	FUNCTION(reserved96),
+	FUNCTION(ldo_en),
+	FUNCTION(reserved97),
+	FUNCTION(ldo_update),
+	FUNCTION(reserved98),
+	FUNCTION(phase_flag14),
+	FUNCTION(prng_rosc),
+	FUNCTION(reserved99),
+	FUNCTION(phase_flag15),
+	FUNCTION(reserved100),
+	FUNCTION(phase_flag5),
+	FUNCTION(reserved101),
+	FUNCTION(pci_e1),
+	FUNCTION(reserved102),
+	FUNCTION(COPY_PHASE),
+	FUNCTION(reserved103),
+	FUNCTION(uim2_data),
+	FUNCTION(qup13),
+	FUNCTION(reserved105),
+	FUNCTION(uim2_clk),
+	FUNCTION(reserved106),
+	FUNCTION(uim2_reset),
+	FUNCTION(reserved107),
+	FUNCTION(uim2_present),
+	FUNCTION(reserved108),
+	FUNCTION(uim1_data),
+	FUNCTION(reserved109),
+	FUNCTION(uim1_clk),
+	FUNCTION(reserved110),
+	FUNCTION(uim1_reset),
+	FUNCTION(reserved111),
+	FUNCTION(uim1_present),
+	FUNCTION(reserved112),
+	FUNCTION(uim_batt),
+	FUNCTION(edp_hot),
+	FUNCTION(reserved113),
+	FUNCTION(NAV_PPS),
+	FUNCTION(GPS_TX),
+	FUNCTION(reserved114),
+	FUNCTION(reserved115),
+	FUNCTION(reserved116),
+	FUNCTION(atest_char),
+	FUNCTION(reserved117),
+	FUNCTION(adsp_ext),
+	FUNCTION(atest_char3),
+	FUNCTION(reserved118),
+	FUNCTION(atest_char2),
+	FUNCTION(reserved119),
+	FUNCTION(atest_char1),
+	FUNCTION(reserved120),
+	FUNCTION(atest_char0),
+	FUNCTION(reserved121),
+	FUNCTION(reserved122),
+	FUNCTION(reserved123),
+};
+
+static const struct msm_pingroup sdmbat_groups[] = {
+	PINGROUP(0, SOUTH, qup0, NA, reserved0, NA, NA, NA, NA, NA, NA),
+	PINGROUP(1, SOUTH, qup0, NA, reserved1, NA, NA, NA, NA, NA, NA),
+	PINGROUP(2, SOUTH, qup0, NA, reserved2, NA, NA, NA, NA, NA, NA),
+	PINGROUP(3, SOUTH, qup0, NA, reserved3, NA, NA, NA, NA, NA, NA),
+	PINGROUP(4, NORTH, qup9, qdss_cti, reserved4, NA, NA, NA, NA, NA, NA),
+	PINGROUP(5, NORTH, qup9, qdss_cti, reserved5, NA, NA, NA, NA, NA, NA),
+	PINGROUP(6, NORTH, qup9, NA, ddr_pxi0, reserved6, NA, NA, NA, NA, NA),
+	PINGROUP(7, NORTH, qup9, ddr_bist, NA, atest_tsens2, vsense_trigger,
+		 atest_usb1, ddr_pxi0, reserved7, NA),
+	PINGROUP(8, WEST, qup_l4, GP_PDM1, ddr_bist, NA, reserved8, NA, NA, NA,
+		 NA),
+	PINGROUP(9, WEST, qup_l5, ddr_bist, reserved9, NA, NA, NA, NA, NA, NA),
+	PINGROUP(10, NORTH, mdp_vsync, qup_l6, ddr_bist, wlan2_adc1,
+		 atest_usb11, ddr_pxi2, reserved10, NA, NA),
+	PINGROUP(11, NORTH, mdp_vsync, edp_lcd, dbg_out, wlan2_adc0,
+		 atest_usb10, ddr_pxi2, reserved11, NA, NA),
+	PINGROUP(12, SOUTH, mdp_vsync, m_voc, tsif1_sync, ddr_pxi3, reserved12,
+		 NA, NA, NA, NA),
+	PINGROUP(13, WEST, cam_mclk, pll_bypassnl, qdss_gpio0, ddr_pxi3,
+		 reserved13, NA, NA, NA, NA),
+	PINGROUP(14, WEST, cam_mclk, pll_reset, qdss_gpio1, reserved14, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(15, WEST, cam_mclk, qdss_gpio2, reserved15, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(16, WEST, cam_mclk, qdss_gpio3, reserved16, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(17, WEST, cci_i2c, qup1, qdss_gpio4, reserved17, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(18, WEST, cci_i2c, qup1, NA, qdss_gpio5, reserved18, NA, NA,
+		 NA, NA),
+	PINGROUP(19, WEST, cci_i2c, qup1, NA, qdss_gpio6, reserved19, NA, NA,
+		 NA, NA),
+	PINGROUP(20, WEST, cci_i2c, qup1, NA, qdss_gpio7, reserved20, NA, NA,
+		 NA, NA),
+	PINGROUP(21, WEST, cci_timer0, gcc_gp2, qdss_gpio8, NA, reserved21, NA,
+		 NA, NA, NA),
+	PINGROUP(22, WEST, cci_timer1, gcc_gp3, qdss_gpio, NA, reserved22, NA,
+		 NA, NA, NA),
+	PINGROUP(23, WEST, cci_timer2, qdss_gpio9, NA, reserved23, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(24, WEST, cci_timer3, cci_async, qdss_gpio10, reserved24, NA,
+		 NA, NA, NA, NA),
+	PINGROUP(25, WEST, cci_timer4, cci_async, qdss_gpio11, NA, reserved25,
+		 NA, NA, NA, NA),
+	PINGROUP(26, WEST, cci_async, qdss_gpio12, JITTER_BIST, NA, reserved26,
+		 NA, NA, NA, NA),
+	PINGROUP(27, WEST, qup2, qdss_gpio13, PLL_BIST, NA, reserved27, NA, NA,
+		 NA, NA),
+	PINGROUP(28, WEST, qup2, qdss_gpio14, AGERA_PLL, NA, reserved28, NA,
+		 NA, NA, NA),
+	PINGROUP(29, WEST, qup2, NA, phase_flag1, qdss_gpio15, atest_tsens,
+		 reserved29, NA, NA, NA),
+	PINGROUP(30, WEST, qup2, phase_flag2, qdss_gpio, reserved30, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(31, WEST, qup11, qup14, reserved31, NA, NA, NA, NA, NA, NA),
+	PINGROUP(32, WEST, qup11, qup14, NA, reserved32, NA, NA, NA, NA, NA),
+	PINGROUP(33, WEST, qup11, qup14, NA, reserved33, NA, NA, NA, NA, NA),
+	PINGROUP(34, WEST, qup11, qup14, NA, reserved34, NA, NA, NA, NA, NA),
+	PINGROUP(35, NORTH, pci_e0, QUP_L4, JITTER_BIST, NA, reserved35, NA,
+		 NA, NA, NA),
+	PINGROUP(36, NORTH, pci_e0, QUP_L5, PLL_BIST, NA, reserved36, NA, NA,
+		 NA, NA),
+	PINGROUP(37, NORTH, QUP_L6, AGERA_PLL, NA, reserved37, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(38, NORTH, usb_phy, NA, reserved38, NA, NA, NA, NA, NA, NA),
+	PINGROUP(39, NORTH, lpass_slimbus, NA, reserved39, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(40, NORTH, sd_write, tsif1_error, NA, reserved40, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(41, SOUTH, qup3, NA, qdss_gpio6, reserved41, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(42, SOUTH, qup3, NA, qdss_gpio7, reserved42, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(43, SOUTH, qup3, NA, qdss_gpio14, reserved43, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(44, SOUTH, qup3, NA, qdss_gpio15, reserved44, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(45, SOUTH, qup6, NA, reserved45, NA, NA, NA, NA, NA, NA),
+	PINGROUP(46, SOUTH, qup6, NA, reserved46, NA, NA, NA, NA, NA, NA),
+	PINGROUP(47, SOUTH, qup6, reserved47, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(48, SOUTH, qup6, reserved48, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(49, NORTH, qup12, reserved49, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(50, NORTH, qup12, reserved50, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(51, NORTH, qup12, qdss_cti, reserved51, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(52, NORTH, qup12, phase_flag16, qdss_cti, reserved52, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(53, NORTH, qup10, phase_flag11, reserved53, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(54, NORTH, qup10, GP_PDM0, phase_flag12, NA, wlan1_adc1,
+		 atest_usb13, ddr_pxi1, reserved54, NA),
+	PINGROUP(55, NORTH, qup10, phase_flag13, NA, wlan1_adc0, atest_usb12,
+		 ddr_pxi1, reserved55, NA, NA),
+	PINGROUP(56, NORTH, qup10, phase_flag17, reserved56, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(57, NORTH, qua_mi2s, gcc_gp1, phase_flag18, reserved57, NA,
+		 NA, NA, NA, NA),
+	PINGROUP(65, NORTH, pri_mi2s, qup8, wsa_clk, NA, reserved65, NA, NA,
+		 NA, NA),
+	PINGROUP(66, NORTH, pri_mi2s_ws, qup8, wsa_data, GP_PDM1, NA,
+		 reserved66, NA, NA, NA),
+	PINGROUP(67, NORTH, pri_mi2s, qup8, NA, atest_usb2, reserved67, NA, NA,
+		 NA, NA),
+	PINGROUP(68, NORTH, pri_mi2s, qup8, NA, atest_usb23, reserved68, NA,
+		 NA, NA, NA),
+	PINGROUP(75, NORTH, ter_mi2s, phase_flag8, qdss_gpio8, atest_usb22,
+		 QUP_L4, reserved75, NA, NA, NA),
+	PINGROUP(76, NORTH, ter_mi2s, phase_flag9, qdss_gpio9, atest_usb21,
+		 QUP_L5, reserved76, NA, NA, NA),
+	PINGROUP(77, NORTH, ter_mi2s, phase_flag4, qdss_gpio10, atest_usb20,
+		 QUP_L6, reserved77, NA, NA, NA),
+	PINGROUP(78, NORTH, ter_mi2s, gcc_gp1, reserved78, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(79, NORTH, sec_mi2s, GP_PDM2, NA, qdss_gpio11, reserved79, NA,
+		 NA, NA, NA),
+	PINGROUP(80, NORTH, sec_mi2s, NA, qdss_gpio12, reserved80, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(81, NORTH, sec_mi2s, qup15, NA, reserved81, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(82, NORTH, sec_mi2s, qup15, NA, reserved82, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(83, NORTH, sec_mi2s, qup15, NA, reserved83, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(84, NORTH, qup15, NA, reserved84, NA, NA, NA, NA, NA, NA),
+	PINGROUP(85, SOUTH, qup5, NA, reserved85, NA, NA, NA, NA, NA, NA),
+	PINGROUP(86, SOUTH, qup5, copy_gp, NA, reserved86, NA, NA, NA, NA, NA),
+	PINGROUP(87, SOUTH, qup5, NA, reserved87, NA, NA, NA, NA, NA, NA),
+	PINGROUP(88, SOUTH, qup5, NA, reserved88, NA, NA, NA, NA, NA, NA),
+	PINGROUP(89, SOUTH, tsif1_clk, qup4, tgu_ch3, phase_flag10, reserved89,
+		 NA, NA, NA, NA),
+	PINGROUP(90, SOUTH, tsif1_en, mdp_vsync0, qup4, mdp_vsync1, mdp_vsync2,
+		 mdp_vsync3, tgu_ch0, phase_flag0, qdss_cti),
+	PINGROUP(91, SOUTH, tsif1_data, sdc4_cmd, qup4, tgu_ch1, NA, qdss_cti,
+		 reserved91, NA, NA),
+	PINGROUP(92, SOUTH, tsif2_error, sdc43, qup4, vfr_1, tgu_ch2, NA,
+		 reserved92, NA, NA),
+	PINGROUP(93, SOUTH, tsif2_clk, sdc4_clk, qup7, NA, qdss_gpio13,
+		 reserved93, NA, NA, NA),
+	PINGROUP(94, SOUTH, tsif2_en, sdc42, qup7, NA, reserved94, NA, NA, NA,
+		 NA),
+	PINGROUP(95, SOUTH, tsif2_data, sdc41, qup7, GP_PDM0, NA, reserved95,
+		 NA, NA, NA),
+	PINGROUP(96, SOUTH, tsif2_sync, sdc40, qup7, phase_flag3, reserved96,
+		 NA, NA, NA, NA),
+	PINGROUP(97, WEST, NA, NA, mdp_vsync, ldo_en, reserved97, NA, NA, NA,
+		 NA),
+	PINGROUP(98, WEST, NA, mdp_vsync, ldo_update, reserved98, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(99, NORTH, phase_flag14, prng_rosc, reserved99, NA, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(100, WEST, phase_flag15, reserved100, NA, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(101, WEST, NA, phase_flag5, reserved101, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(102, WEST, pci_e1, prng_rosc, reserved102, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(103, WEST, pci_e1, COPY_PHASE, reserved103, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(105, NORTH, uim2_data, qup13, qup_l4, NA, reserved105, NA, NA,
+		 NA, NA),
+	PINGROUP(106, NORTH, uim2_clk, qup13, qup_l5, NA, reserved106, NA, NA,
+		 NA, NA),
+	PINGROUP(107, NORTH, uim2_reset, qup13, qup_l6, reserved107, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(108, NORTH, uim2_present, qup13, reserved108, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(109, NORTH, uim1_data, reserved109, NA, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(110, NORTH, uim1_clk, reserved110, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(111, NORTH, uim1_reset, reserved111, NA, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(112, NORTH, uim1_present, reserved112, NA, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(113, NORTH, uim_batt, edp_hot, reserved113, NA, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(114, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, reserved114, NA,
+		 NA, NA),
+	PINGROUP(115, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, NA, reserved115, NA,
+		 NA, NA),
+	PINGROUP(116, SOUTH, NA, reserved116, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(117, NORTH, NA, qdss_gpio0, atest_char, reserved117, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(118, NORTH, adsp_ext, NA, qdss_gpio1, atest_char3,
+		 reserved118, NA, NA, NA, NA),
+	PINGROUP(119, NORTH, NA, qdss_gpio2, atest_char2, reserved119, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(120, NORTH, NA, qdss_gpio3, atest_char1, reserved120, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(121, NORTH, NA, qdss_gpio4, atest_char0, reserved121, NA, NA,
+		 NA, NA, NA),
+	PINGROUP(122, NORTH, NA, qdss_gpio5, reserved122, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(123, NORTH, qup_l4, NA, qdss_gpio, reserved123, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(124, NORTH, qup_l5, NA, qdss_gpio, reserved124, NA, NA, NA,
+		 NA, NA),
+	PINGROUP(125, NORTH, qup_l6, NA, reserved125, NA, NA, NA, NA, NA, NA),
+	PINGROUP(126, NORTH, NA, reserved126, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(127, WEST, NA, NA, reserved127, NA, NA, NA, NA, NA, NA),
+	PINGROUP(128, WEST, NAV_PPS, NAV_PPS, GPS_TX, NA, reserved128, NA, NA,
+		 NA, NA),
+	PINGROUP(129, WEST, NAV_PPS, NAV_PPS, GPS_TX, NA, reserved129, NA, NA,
+		 NA, NA),
+	PINGROUP(130, WEST, qlink_request, NA, reserved130, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(131, WEST, qlink_enable, NA, reserved131, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(132, WEST, NA, NA, reserved132, NA, NA, NA, NA, NA, NA),
+	PINGROUP(133, NORTH, NA, reserved133, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(134, NORTH, NA, reserved134, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(135, WEST, NA, pa_indicator, NA, reserved135, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(136, WEST, NA, NA, reserved136, NA, NA, NA, NA, NA, NA),
+	PINGROUP(137, WEST, NA, NA, phase_flag26, reserved137, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(138, WEST, NA, NA, phase_flag27, reserved138, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(139, WEST, NA, phase_flag28, reserved139, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(140, WEST, NA, NA, phase_flag6, reserved140, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(141, WEST, NA, phase_flag29, reserved141, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(142, WEST, NA, phase_flag30, reserved142, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(143, WEST, NA, NAV_PPS, NAV_PPS, GPS_TX, phase_flag31,
+		 reserved143, NA, NA, NA),
+	PINGROUP(144, SOUTH, mss_lte, reserved144, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(145, SOUTH, mss_lte, GPS_TX, reserved145, NA, NA, NA, NA, NA,
+		 NA),
+	PINGROUP(146, WEST, NA, NA, reserved146, NA, NA, NA, NA, NA, NA),
+	PINGROUP(147, WEST, NA, NA, reserved147, NA, NA, NA, NA, NA, NA),
+	PINGROUP(148, WEST, NA, reserved148, NA, NA, NA, NA, NA, NA, NA),
+	PINGROUP(149, WEST, NA, reserved149, NA, NA, NA, NA, NA, NA, NA),
+	SDC_QDSD_PINGROUP(sdc2_clk, 0x59a000, 14, 6),
+	SDC_QDSD_PINGROUP(sdc2_cmd, 0x59a000, 11, 3),
+	SDC_QDSD_PINGROUP(sdc2_data, 0x59a000, 9, 0),
+};
+
+static const struct msm_pinctrl_soc_data sdmbat_pinctrl = {
+	.pins = sdmbat_pins,
+	.npins = ARRAY_SIZE(sdmbat_pins),
+	.functions = sdmbat_functions,
+	.nfunctions = ARRAY_SIZE(sdmbat_functions),
+	.groups = sdmbat_groups,
+	.ngroups = ARRAY_SIZE(sdmbat_groups),
+	.ngpios = 136,
+};
+
+static int sdmbat_pinctrl_probe(struct platform_device *pdev)
+{
+	return msm_pinctrl_probe(pdev, &sdmbat_pinctrl);
+}
+
+static const struct of_device_id sdmbat_pinctrl_of_match[] = {
+	{ .compatible = "qcom,sdmbat-pinctrl", },
+	{ },
+};
+
+static struct platform_driver sdmbat_pinctrl_driver = {
+	.driver = {
+		.name = "sdmbat-pinctrl",
+		.owner = THIS_MODULE,
+		.of_match_table = sdmbat_pinctrl_of_match,
+	},
+	.probe = sdmbat_pinctrl_probe,
+	.remove = msm_pinctrl_remove,
+};
+
+static int __init sdmbat_pinctrl_init(void)
+{
+	return platform_driver_register(&sdmbat_pinctrl_driver);
+}
+arch_initcall(sdmbat_pinctrl_init);
+
+static void __exit sdmbat_pinctrl_exit(void)
+{
+	platform_driver_unregister(&sdmbat_pinctrl_driver);
+}
+module_exit(sdmbat_pinctrl_exit);
+
+MODULE_DESCRIPTION("QTI sdmbat pinctrl driver");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, sdmbat_pinctrl_of_match);
diff --git a/drivers/platform/msm/Kconfig b/drivers/platform/msm/Kconfig
index ac9545e..7018007 100644
--- a/drivers/platform/msm/Kconfig
+++ b/drivers/platform/msm/Kconfig
@@ -65,4 +65,52 @@
 	  into suites according to the sub-unit of the IPA being tested.
 	  The user interface to run and control the tests is debugfs file
 	  system.
+
+config SPS
+	bool "SPS support"
+	select GENERIC_ALLOCATOR
+	help
+	  The SPS (Smart Peripheral Switch) is a DMA engine.
+	  It can move data in the following modes:
+		1. Peripheral-to-Peripheral.
+		2. Peripheral-to-Memory.
+		3. Memory-to-Memory.
+
+config SPS_SUPPORT_BAMDMA
+	bool "SPS support BAM DMA"
+	depends on SPS
+	default n
+	help
+	  The BAM-DMA is used for Memory-to-Memory transfers.
+	  The main use cases is RPC between processors.
+	  The BAM-DMA hardware has 2 registers sets:
+		1. A BAM HW like all the peripherals.
+		2. A DMA channel configuration (i.e. channel priority).
+
+config SPS_SUPPORT_NDP_BAM
+	bool "SPS support NDP BAM"
+	depends on SPS
+	default n
+	help
+	  No-Data-Path BAM is used to improve BAM performance.
+
+config QPNP_COINCELL
+	tristate "QPNP coincell charger support"
+	depends on SPMI
+	help
+	  This driver supports the QPNP coincell peripheral found inside of
+	  Qualcomm Technologies, Inc. QPNP PMIC devices.  The coincell charger
+	  provides a means to charge a coincell battery or backup capacitor
+	  which is used to maintain PMIC register state when the main battery is
+	  removed from the mobile device.
+
+config QPNP_REVID
+	tristate "QPNP Revision ID Peripheral"
+	depends on SPMI
+	help
+	  Say 'y' here to include support for the Qualcomm Technologies, Inc.
+	  QPNP REVID peripheral. REVID prints out the PMIC type and revision
+	  numbers in the kernel log along with the PMIC option status. The PMIC
+	  type is mapped to a QTI chip part number and logged as well.
+
 endmenu
diff --git a/drivers/platform/msm/Makefile b/drivers/platform/msm/Makefile
index 1f9e11b..85c4673 100644
--- a/drivers/platform/msm/Makefile
+++ b/drivers/platform/msm/Makefile
@@ -4,3 +4,8 @@
 obj-$(CONFIG_GSI) += gsi/
 obj-$(CONFIG_IPA) += ipa/
 obj-$(CONFIG_IPA3) += ipa/
+obj-$(CONFIG_SPS) += sps/
+
+obj-$(CONFIG_QPNP_COINCELL) += qpnp-coincell.o
+obj-$(CONFIG_QPNP_REVID) += qpnp-revid.o
+
diff --git a/drivers/platform/msm/gsi/gsi.c b/drivers/platform/msm/gsi/gsi.c
index f32d3d9..58c1704 100644
--- a/drivers/platform/msm/gsi/gsi.c
+++ b/drivers/platform/msm/gsi/gsi.c
@@ -1591,14 +1591,20 @@ int gsi_alloc_channel(struct gsi_chan_props *props, unsigned long dev_hdl,
 		return -GSI_STATUS_INVALID_PARAMS;
 	}
 
-	if (props->evt_ring_hdl != ~0 &&
-		atomic_read(&gsi_ctx->evtr[props->evt_ring_hdl].chan_ref_cnt) &&
-		gsi_ctx->evtr[props->evt_ring_hdl].props.exclusive) {
-		GSIERR("evt ring=%lu already in exclusive use chan_hdl=%p\n",
-				props->evt_ring_hdl, chan_hdl);
-		return -GSI_STATUS_UNSUPPORTED_OP;
-	}
+	if (props->evt_ring_hdl != ~0) {
+		if (props->evt_ring_hdl >= GSI_EVT_RING_MAX) {
+			GSIERR("invalid evt ring=%lu\n", props->evt_ring_hdl);
+			return -GSI_STATUS_INVALID_PARAMS;
+		}
 
+		if (atomic_read(
+			&gsi_ctx->evtr[props->evt_ring_hdl].chan_ref_cnt) &&
+			gsi_ctx->evtr[props->evt_ring_hdl].props.exclusive) {
+			GSIERR("evt ring=%lu exclusively used by chan_hdl=%p\n",
+				props->evt_ring_hdl, chan_hdl);
+			return -GSI_STATUS_UNSUPPORTED_OP;
+		}
+	}
 
 	ctx = &gsi_ctx->chan[props->ch_id];
 	if (ctx->allocated) {
@@ -2727,6 +2733,16 @@ int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size)
 }
 EXPORT_SYMBOL(gsi_enable_fw);
 
+void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset,
+		unsigned long *size)
+{
+	if (base_offset)
+		*base_offset = GSI_GSI_INST_RAM_BASE_OFFS;
+	if (size)
+		*size = GSI_GSI_INST_RAM_SIZE;
+}
+EXPORT_SYMBOL(gsi_get_inst_ram_offset_and_size);
+
 static int msm_gsi_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
diff --git a/drivers/platform/msm/gsi/gsi_reg.h b/drivers/platform/msm/gsi/gsi_reg.h
index fa1e848..1acaf74 100644
--- a/drivers/platform/msm/gsi/gsi_reg.h
+++ b/drivers/platform/msm/gsi/gsi_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1838,5 +1838,7 @@
 #define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_BMSK 0xffffffff
 #define GSI_INTER_EE_n_SRC_EV_CH_IRQ_CLR_EV_CH_BIT_MAP_SHFT 0x0
 
+#define GSI_GSI_INST_RAM_BASE_OFFS	0x4000
+#define GSI_GSI_INST_RAM_SIZE		0x4000
 
 #endif /* __GSI_REG_H__ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c
index d5daadc..d68e15d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c
@@ -4015,7 +4015,7 @@ static int ipa3_trigger_fw_loading_mdms(void)
 
 	IPADBG("FWs are available for loading\n");
 
-	result = ipa3_load_fws(fw);
+	result = ipa3_load_fws(fw, ipa3_res.transport_mem_base);
 	if (result) {
 		IPAERR("IPA FWs loading has failed\n");
 		release_firmware(fw);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
index 77261f3..9fe93b2 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_client.c
@@ -1473,7 +1473,7 @@ static int ipa3_is_xdci_channel_empty(struct ipa3_ep_context *ep,
 	return 0;
 }
 
-static int ipa3_enable_force_clear(u32 request_id, bool throttle_source,
+int ipa3_enable_force_clear(u32 request_id, bool throttle_source,
 	u32 source_pipe_bitmask)
 {
 	struct ipa_enable_force_clear_datapath_req_msg_v01 req;
@@ -1496,7 +1496,7 @@ static int ipa3_enable_force_clear(u32 request_id, bool throttle_source,
 	return 0;
 }
 
-static int ipa3_disable_force_clear(u32 request_id)
+int ipa3_disable_force_clear(u32 request_id)
 {
 	struct ipa_disable_force_clear_datapath_req_msg_v01 req;
 	int result;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
index ee6f43a..4f6bf55 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_i.h
@@ -1923,6 +1923,9 @@ int ipa3_alloc_rule_id(struct idr *rule_ids);
 int ipa3_id_alloc(void *ptr);
 void *ipa3_id_find(u32 id);
 void ipa3_id_remove(u32 id);
+int ipa3_enable_force_clear(u32 request_id, bool throttle_source,
+	u32 source_pipe_bitmask);
+int ipa3_disable_force_clear(u32 request_id);
 
 int ipa3_set_required_perf_profile(enum ipa_voltage_level floor_voltage,
 				  u32 bandwidth_mbps);
@@ -2028,7 +2031,7 @@ int ipa3_uc_panic_notifier(struct notifier_block *this,
 	unsigned long event, void *ptr);
 void ipa3_inc_acquire_wakelock(void);
 void ipa3_dec_release_wakelock(void);
-int ipa3_load_fws(const struct firmware *firmware);
+int ipa3_load_fws(const struct firmware *firmware, phys_addr_t gsi_mem_base);
 int ipa3_register_ipa_ready_cb(void (*ipa_ready_cb)(void *), void *user_data);
 const char *ipa_hw_error_str(enum ipa3_hw_errors err_type);
 int ipa_gsi_ch20_wa(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
index b49fb32..032062d 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_qmi_service.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -37,6 +37,8 @@
 #define QMI_SEND_STATS_REQ_TIMEOUT_MS 5000
 #define QMI_SEND_REQ_TIMEOUT_MS 60000
 
+#define QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS 1000
+
 static struct qmi_handle *ipa3_svc_handle;
 static void ipa3_a5_svc_recv_msg(struct work_struct *work);
 static DECLARE_DELAYED_WORK(work_recv_msg, ipa3_a5_svc_recv_msg);
@@ -660,7 +662,8 @@ int ipa3_qmi_enable_force_clear_datapath_send(
 			&req_desc,
 			req,
 			sizeof(*req),
-			&resp_desc, &resp, sizeof(resp), 0);
+			&resp_desc, &resp, sizeof(resp),
+			QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS);
 	if (rc < 0) {
 		IPAWANERR("send req failed %d\n", rc);
 		return rc;
@@ -705,7 +708,8 @@ int ipa3_qmi_disable_force_clear_datapath_send(
 			&req_desc,
 			req,
 			sizeof(*req),
-			&resp_desc, &resp, sizeof(resp), 0);
+			&resp_desc, &resp, sizeof(resp),
+			QMI_IPA_FORCE_CLEAR_DATAPATH_TIMEOUT_MS);
 	if (rc < 0) {
 		IPAWANERR("send req failed %d\n", rc);
 		return rc;
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
index f2db3f3..480bacb 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_uc_wdi.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1342,6 +1342,33 @@ int ipa3_enable_wdi_pipe(u32 clnt_hdl)
 	return result;
 }
 
+static int ipa3_wait_for_prod_empty(void)
+{
+	int i;
+	u32 rx_door_bell_value;
+
+	for (i = 0; i < IPA_UC_FINISH_MAX; i++) {
+		rx_door_bell_value = ipahal_read_reg_mn(
+			IPA_UC_MAILBOX_m_n,
+			IPA_HW_WDI_RX_MBOX_START_INDEX / 32,
+			IPA_HW_WDI_RX_MBOX_START_INDEX % 32);
+		IPADBG("(%d)rx_DB(%u)rp(%u),comp_wp(%u)\n",
+			i,
+			rx_door_bell_value,
+			*ipa3_ctx->uc_ctx.rdy_ring_rp_va,
+			*ipa3_ctx->uc_ctx.rdy_comp_ring_wp_va);
+		if (*ipa3_ctx->uc_ctx.rdy_ring_rp_va !=
+			*ipa3_ctx->uc_ctx.rdy_comp_ring_wp_va) {
+			usleep_range(IPA_UC_WAIT_MIN_SLEEP,
+				IPA_UC_WAII_MAX_SLEEP);
+		} else {
+			return 0;
+		}
+	}
+
+	return -ETIME;
+}
+
 /**
  * ipa3_disable_wdi_pipe() - WDI client disable
  * @clnt_hdl:	[in] opaque client handle assigned by IPA to client
@@ -1357,7 +1384,9 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
 	union IpaHwWdiCommonChCmdData_t disable;
 	struct ipa_ep_cfg_ctrl ep_cfg_ctrl;
 	u32 prod_hdl;
-	int i;
+
+	u32 source_pipe_bitmask = 0;
+	bool disable_force_clear = false;
 
 	if (clnt_hdl >= ipa3_ctx->ipa_num_pipes ||
 	    ipa3_ctx->ep[clnt_hdl].valid == 0) {
@@ -1369,28 +1398,6 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
 	if (result)
 		return result;
 
-	/* checking rdy_ring_rp_pa matches the rdy_comp_ring_wp_pa on WDI2.0 */
-	if (ipa3_ctx->ipa_wdi2) {
-		for (i = 0; i < IPA_UC_FINISH_MAX; i++) {
-			IPADBG("(%d) rp_value(%u), comp_wp_value(%u)\n",
-					i,
-					*ipa3_ctx->uc_ctx.rdy_ring_rp_va,
-					*ipa3_ctx->uc_ctx.rdy_comp_ring_wp_va);
-			if (*ipa3_ctx->uc_ctx.rdy_ring_rp_va !=
-				*ipa3_ctx->uc_ctx.rdy_comp_ring_wp_va) {
-				usleep_range(IPA_UC_WAIT_MIN_SLEEP,
-					IPA_UC_WAII_MAX_SLEEP);
-			} else {
-				break;
-			}
-		}
-		/* In case ipa_uc still haven't processed all
-		 * pending descriptors, we have to assert
-		 */
-		if (i == IPA_UC_FINISH_MAX)
-			WARN_ON(1);
-	}
-
 	IPADBG("ep=%d\n", clnt_hdl);
 
 	ep = &ipa3_ctx->ep[clnt_hdl];
@@ -1415,6 +1422,9 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
 	 * holb on IPA Producer pipe
 	 */
 	if (IPA_CLIENT_IS_PROD(ep->client)) {
+		IPADBG("Stopping PROD channel - hdl=%d clnt=%d\n",
+			clnt_hdl, ep->client);
+		/* remove delay on wlan-prod pipe*/
 		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
 		ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
 
@@ -1431,10 +1441,44 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
 		}
 		usleep_range(IPA_UC_POLL_SLEEP_USEC * IPA_UC_POLL_SLEEP_USEC,
 			IPA_UC_POLL_SLEEP_USEC * IPA_UC_POLL_SLEEP_USEC);
+
+		/*
+		 * checking rdy_ring_rp_pa matches the
+		 * rdy_comp_ring_wp_pa on WDI2.0
+		 */
+		if (ipa3_ctx->ipa_wdi2) {
+			result = ipa3_wait_for_prod_empty();
+			if (result) {
+				IPADBG("prod not empty\n");
+				/*
+				 * In case ipa_uc still haven't processed all
+				 * pending descriptors, ask modem to drain
+				 */
+				source_pipe_bitmask = 1 <<
+					ipa3_get_ep_mapping(ep->client);
+				result = ipa3_enable_force_clear(clnt_hdl,
+					false, source_pipe_bitmask);
+				if (result) {
+					IPAERR("failed to force clear %d\n",
+						result);
+				} else {
+					disable_force_clear = true;
+				}
+
+				/*
+				 * In case ipa_uc still haven't processed all
+				 * pending descriptors, we have to assert
+				 */
+				result = ipa3_wait_for_prod_empty();
+				if (result) {
+					IPAERR("prod still not empty\n");
+					ipa_assert();
+				}
+			}
+		}
 	}
 
 	disable.params.ipa_pipe_number = clnt_hdl;
-
 	result = ipa3_uc_send_cmd(disable.raw32b,
 		IPA_CPU_2_HW_CMD_WDI_CH_DISABLE,
 		IPA_HW_2_CPU_WDI_CMD_STATUS_SUCCESS,
@@ -1450,11 +1494,14 @@ int ipa3_disable_wdi_pipe(u32 clnt_hdl)
 		memset(&ep_cfg_ctrl, 0, sizeof(struct ipa_ep_cfg_ctrl));
 		ep_cfg_ctrl.ipa_ep_delay = true;
 		ipa3_cfg_ep_ctrl(clnt_hdl, &ep_cfg_ctrl);
+		if (disable_force_clear)
+			ipa3_disable_force_clear(clnt_hdl);
 	}
 	IPA_ACTIVE_CLIENTS_DEC_EP(ipa3_get_client_mapping(clnt_hdl));
 	ep->uc_offload_state &= ~IPA_WDI_ENABLED;
 	IPADBG("client (ep: %d) disabled\n", clnt_hdl);
 
+
 uc_timeout:
 	return result;
 }
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
index ad18013f..d478a06 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipa_utils.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -3968,75 +3968,164 @@ int ipa3_stop_gsi_channel(u32 clnt_hdl)
 	return res;
 }
 
+static int ipa3_load_single_fw(const struct firmware *firmware,
+	const struct elf32_phdr *phdr)
+{
+	uint32_t *fw_mem_base;
+	int index;
+	const uint32_t *elf_data_ptr;
+
+	if (phdr->p_offset > firmware->size) {
+		IPAERR("Invalid ELF: offset=%u is beyond elf_size=%zu\n",
+			phdr->p_offset, firmware->size);
+		return -EINVAL;
+	}
+	if ((firmware->size - phdr->p_offset) < phdr->p_filesz) {
+		IPAERR("Invalid ELF: offset=%u filesz=%u elf_size=%zu\n",
+			phdr->p_offset, phdr->p_filesz, firmware->size);
+		return -EINVAL;
+	}
+
+	if (phdr->p_memsz % sizeof(uint32_t)) {
+		IPAERR("FW mem size %u doesn't align to 32bit\n",
+			phdr->p_memsz);
+		return -EFAULT;
+	}
+
+	if (phdr->p_filesz > phdr->p_memsz) {
+		IPAERR("FW image too big src_size=%u dst_size=%u\n",
+			phdr->p_filesz, phdr->p_memsz);
+		return -EFAULT;
+	}
+
+	fw_mem_base = ioremap(phdr->p_vaddr, phdr->p_memsz);
+	if (!fw_mem_base) {
+		IPAERR("Failed to map 0x%x for the size of %u\n",
+			phdr->p_vaddr, phdr->p_memsz);
+		return -ENOMEM;
+	}
+
+	/* Set the entire region to 0s */
+	memset(fw_mem_base, 0, phdr->p_memsz);
+
+	elf_data_ptr = (uint32_t *)(firmware->data + phdr->p_offset);
+
+	/* Write the FW */
+	for (index = 0; index < phdr->p_filesz/sizeof(uint32_t); index++) {
+		writel_relaxed(*elf_data_ptr, &fw_mem_base[index]);
+		elf_data_ptr++;
+	}
+
+	iounmap(fw_mem_base);
+
+	return 0;
+}
+
 /**
  * ipa3_load_fws() - Load the IPAv3 FWs into IPA&GSI SRAM.
  *
  * @firmware: Structure which contains the FW data from the user space.
+ * @gsi_mem_base: GSI base address
  *
  * Return value: 0 on success, negative otherwise
  *
  */
-int ipa3_load_fws(const struct firmware *firmware)
+int ipa3_load_fws(const struct firmware *firmware, phys_addr_t gsi_mem_base)
 {
 	const struct elf32_hdr *ehdr;
 	const struct elf32_phdr *phdr;
-	const uint8_t *elf_phdr_ptr;
-	uint32_t *elf_data_ptr;
-	int phdr_idx, index;
-	uint32_t *fw_mem_base;
+	unsigned long gsi_iram_ofst;
+	unsigned long gsi_iram_size;
+	phys_addr_t ipa_reg_mem_base;
+	u32 ipa_reg_ofst;
+	int rc;
+
+	if (!gsi_mem_base) {
+		IPAERR("Invalid GSI base address\n");
+		return -EINVAL;
+	}
+
+	ipa_assert_on(!firmware);
+	/* One program header per FW image: GSI, DPS and HPS */
+	if (firmware->size < (sizeof(*ehdr) + 3 * sizeof(*phdr))) {
+		IPAERR("Missing ELF and Program headers firmware size=%zu\n",
+			firmware->size);
+		return -EINVAL;
+	}
 
 	ehdr = (struct elf32_hdr *) firmware->data;
-
-	elf_phdr_ptr = firmware->data + sizeof(*ehdr);
-
-	for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
-		/*
-		 * The ELF program header will contain the starting
-		 * address to which the firmware needs to copied.
-		 */
-		phdr = (struct elf32_phdr *)elf_phdr_ptr;
-
-		/*
-		 * p_vaddr will contain the starting address to which the
-		 * FW needs to be loaded.
-		 * p_memsz will contain the size of the IRAM.
-		 * p_filesz will contain the size of the FW image.
-		 */
-		fw_mem_base = ioremap(phdr->p_vaddr, phdr->p_memsz);
-		if (!fw_mem_base) {
-			IPAERR("Failed to map 0x%x for the size of %u\n",
-				phdr->p_vaddr, phdr->p_memsz);
-				return -ENOMEM;
-		}
-
-		/* Set the entire region to 0s */
-		memset(fw_mem_base, 0, phdr->p_memsz);
-
-		/*
-		 * p_offset will contain and absolute offset from the beginning
-		 * of the ELF file.
-		 */
-		elf_data_ptr = (uint32_t *)
-				((uint8_t *)firmware->data + phdr->p_offset);
-
-		if (phdr->p_memsz % sizeof(uint32_t)) {
-			IPAERR("FW size %u doesn't align to 32bit\n",
-				phdr->p_memsz);
-			return -EFAULT;
-		}
-
-		/* Write the FW */
-		for (index = 0; index < phdr->p_filesz/sizeof(uint32_t);
-			index++) {
-			writel_relaxed(*elf_data_ptr, &fw_mem_base[index]);
-			elf_data_ptr++;
-		}
-
-		iounmap(fw_mem_base);
-
-		elf_phdr_ptr = elf_phdr_ptr + sizeof(*phdr);
+	ipa_assert_on(!ehdr);
+	if (ehdr->e_phnum != 3) {
+		IPAERR("Unexpected number of ELF program headers\n");
+		return -EINVAL;
 	}
-	IPADBG("IPA FWs (GSI FW, HPS and DPS) were loaded\n");
+	phdr = (struct elf32_phdr *)(firmware->data + sizeof(*ehdr));
+
+	/*
+	 * Each ELF program header represents a FW image and contains:
+	 *  p_vaddr : The starting address to which the FW needs to loaded.
+	 *  p_memsz : The size of the IRAM (where the image loaded)
+	 *  p_filesz: The size of the FW image embedded inside the ELF
+	 *  p_offset: Absolute offset to the image from the head of the ELF
+	 */
+
+	/* Load GSI FW image */
+	gsi_get_inst_ram_offset_and_size(&gsi_iram_ofst, &gsi_iram_size);
+	if (phdr->p_vaddr != (gsi_mem_base + gsi_iram_ofst)) {
+		IPAERR(
+			"Invalid GSI FW img load addr vaddr=0x%x gsi_mem_base=%pa gsi_iram_ofst=0x%lx\n"
+			, phdr->p_vaddr, &gsi_mem_base, gsi_iram_ofst);
+		return -EINVAL;
+	}
+	if (phdr->p_memsz > gsi_iram_size) {
+		IPAERR("Invalid GSI FW img size memsz=%d gsi_iram_size=%lu\n",
+			phdr->p_memsz, gsi_iram_size);
+		return -EINVAL;
+	}
+	rc = ipa3_load_single_fw(firmware, phdr);
+	if (rc)
+		return rc;
+
+	phdr++;
+	ipa_reg_mem_base = ipa3_ctx->ipa_wrapper_base + ipahal_get_reg_base();
+
+	/* Load IPA DPS FW image */
+	ipa_reg_ofst = ipahal_get_reg_ofst(IPA_DPS_SEQUENCER_FIRST);
+	if (phdr->p_vaddr != (ipa_reg_mem_base + ipa_reg_ofst)) {
+		IPAERR(
+			"Invalid IPA DPS img load addr vaddr=0x%x ipa_reg_mem_base=%pa ipa_reg_ofst=%u\n"
+			, phdr->p_vaddr, &ipa_reg_mem_base, ipa_reg_ofst);
+		return -EINVAL;
+	}
+	if (phdr->p_memsz > ipahal_get_dps_img_mem_size()) {
+		IPAERR("Invalid IPA DPS img size memsz=%d dps_mem_size=%u\n",
+			phdr->p_memsz, ipahal_get_dps_img_mem_size());
+		return -EINVAL;
+	}
+	rc = ipa3_load_single_fw(firmware, phdr);
+	if (rc)
+		return rc;
+
+	phdr++;
+
+	/* Load IPA HPS FW image */
+	ipa_reg_ofst = ipahal_get_reg_ofst(IPA_HPS_SEQUENCER_FIRST);
+	if (phdr->p_vaddr != (ipa_reg_mem_base + ipa_reg_ofst)) {
+		IPAERR(
+			"Invalid IPA HPS img load addr vaddr=0x%x ipa_reg_mem_base=%pa ipa_reg_ofst=%u\n"
+			, phdr->p_vaddr, &ipa_reg_mem_base, ipa_reg_ofst);
+		return -EINVAL;
+	}
+	if (phdr->p_memsz > ipahal_get_hps_img_mem_size()) {
+		IPAERR("Invalid IPA HPS img size memsz=%d dps_mem_size=%u\n",
+			phdr->p_memsz, ipahal_get_hps_img_mem_size());
+		return -EINVAL;
+	}
+	rc = ipa3_load_single_fw(firmware, phdr);
+	if (rc)
+		return rc;
+
+	IPADBG("IPA FWs (GSI FW, DPS and HPS) loaded successfully\n");
 	return 0;
 }
 
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
index e39874e..fa9c6c8 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1257,6 +1257,21 @@ int ipahal_get_proc_ctx_needed_len(enum ipa_hdr_proc_type type)
 	return res;
 }
 
+/*
+ * Get IPA Data Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_dps_img_mem_size(void)
+{
+	return IPA_HW_DPS_IMG_MEM_SIZE_V3_0;
+}
+
+/*
+ * Get IPA Header Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_hps_img_mem_size(void)
+{
+	return IPA_HW_HPS_IMG_MEM_SIZE_V3_0;
+}
 
 int ipahal_init(enum ipa_hw_type ipa_hw_type, void __iomem *base,
 	struct device *ipa_pdev)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
index 6549775..154045f 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -634,6 +634,16 @@ int ipahal_cp_proc_ctx_to_hw_buff(enum ipa_hdr_proc_type type,
  */
 int ipahal_get_proc_ctx_needed_len(enum ipa_hdr_proc_type type);
 
+/*
+ * Get IPA Data Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_dps_img_mem_size(void);
+
+/*
+ * Get IPA Header Processing Star image memory size at IPA SRAM
+ */
+u32 ipahal_get_hps_img_mem_size(void);
+
 int ipahal_init(enum ipa_hw_type ipa_hw_type, void __iomem *base,
 	struct device *ipa_pdev);
 void ipahal_destroy(void);
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
index 4c4b666..d6a496e 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_i.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -546,4 +546,8 @@ struct ipa_hw_hdr_proc_ctx_add_hdr_cmd_seq {
 	struct ipa_hw_hdr_proc_ctx_tlv end;
 };
 
+/* IPA HW DPS/HPS image memory sizes */
+#define IPA_HW_DPS_IMG_MEM_SIZE_V3_0 128
+#define IPA_HW_HPS_IMG_MEM_SIZE_V3_0 320
+
 #endif /* _IPAHAL_I_H_ */
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
index e297dea..30fd737 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1137,6 +1137,12 @@ static struct ipahal_reg_obj ipahal_reg_objs[IPA_HW_MAX][IPA_REG_MAX] = {
 	[IPA_HW_v3_0][IPA_QSB_MAX_READS] = {
 		ipareg_construct_qsb_max_reads, ipareg_parse_dummy,
 		0x00000078, 0},
+	[IPA_HW_v3_0][IPA_DPS_SEQUENCER_FIRST] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0001e000, 0},
+	[IPA_HW_v3_0][IPA_HPS_SEQUENCER_FIRST] = {
+		ipareg_construct_dummy, ipareg_parse_dummy,
+		0x0001e080, 0},
 
 
 	/* IPAv3.1 */
@@ -1308,6 +1314,38 @@ u32 ipahal_read_reg_n(enum ipahal_reg_name reg, u32 n)
 }
 
 /*
+ * ipahal_read_reg_mn() - Get mn parameterized reg value
+ */
+u32 ipahal_read_reg_mn(enum ipahal_reg_name reg, u32 m, u32 n)
+{
+	u32 offset;
+
+	if (reg >= IPA_REG_MAX) {
+		IPAHAL_ERR("Invalid register reg=%u\n", reg);
+		return -EFAULT;
+	}
+
+	IPAHAL_DBG_LOW("read %s m=%u n=%u\n",
+		ipahal_reg_name_str(reg), m, n);
+	offset = ipahal_reg_objs[ipahal_ctx->hw_type][reg].offset;
+	if (offset == -1) {
+		IPAHAL_ERR("Read access to obsolete reg=%s\n",
+			ipahal_reg_name_str(reg));
+		WARN_ON_ONCE(1);
+		return -EFAULT;
+	}
+	/*
+	 * Currently there is one register with m and n parameters
+	 *	IPA_UC_MAILBOX_m_n. The m value of it is 0x80.
+	 * If more such registers will be added in the future,
+	 *	we can move the m parameter to the table above.
+	 */
+	offset += 0x80 * m;
+	offset += ipahal_reg_objs[ipahal_ctx->hw_type][reg].n_ofst * n;
+	return ioread32(ipahal_ctx->base + offset);
+}
+
+/*
  * ipahal_write_reg_mn() - Write to m/n parameterized reg a raw value
  */
 void ipahal_write_reg_mn(enum ipahal_reg_name reg, u32 m, u32 n, u32 val)
diff --git a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
index 98894c3..8b6ed39 100644
--- a/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
+++ b/drivers/platform/msm/ipa/ipa_v3/ipahal/ipahal_reg.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -84,6 +84,8 @@ enum ipahal_reg_name {
 	IPA_QSB_MAX_READS,
 	IPA_TX_CFG,
 	IPA_IDLE_INDICATION_CFG,
+	IPA_DPS_SEQUENCER_FIRST,
+	IPA_HPS_SEQUENCER_FIRST,
 	IPA_REG_MAX,
 };
 
@@ -352,6 +354,11 @@ const char *ipahal_reg_name_str(enum ipahal_reg_name reg_name);
 u32 ipahal_read_reg_n(enum ipahal_reg_name reg, u32 n);
 
 /*
+ * ipahal_read_reg_mn() - Get mn parameterized reg value
+ */
+u32 ipahal_read_reg_mn(enum ipahal_reg_name reg, u32 m, u32 n);
+
+/*
  * ipahal_write_reg_mn() - Write to m/n parameterized reg a raw value
  */
 void ipahal_write_reg_mn(enum ipahal_reg_name reg, u32 m, u32 n, u32 val);
diff --git a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
index e653bcd..77c1d10 100644
--- a/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
+++ b/drivers/platform/msm/ipa/ipa_v3/rmnet_ipa.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1132,8 +1132,9 @@ static int ipa3_wwan_xmit(struct sk_buff *skb, struct net_device *dev)
 	dev->stats.tx_bytes += skb->len;
 	ret = NETDEV_TX_OK;
 out:
-	ipa_rm_inactivity_timer_release_resource(
-		IPA_RM_RESOURCE_WWAN_0_PROD);
+	if (atomic_read(&wwan_ptr->outstanding_pkts) == 0)
+		ipa_rm_inactivity_timer_release_resource(
+			IPA_RM_RESOURCE_WWAN_0_PROD);
 	return ret;
 }
 
@@ -1184,10 +1185,12 @@ static void apps_ipa_tx_complete_notify(void *priv,
 				wwan_ptr->outstanding_low);
 		netif_wake_queue(wwan_ptr->net);
 	}
+
+	if (atomic_read(&wwan_ptr->outstanding_pkts) == 0)
+		ipa_rm_inactivity_timer_release_resource(
+			IPA_RM_RESOURCE_WWAN_0_PROD);
 	__netif_tx_unlock_bh(netdev_get_tx_queue(dev, 0));
 	dev_kfree_skb_any(skb);
-	ipa_rm_inactivity_timer_release_resource(
-		IPA_RM_RESOURCE_WWAN_0_PROD);
 }
 
 /**
diff --git a/drivers/platform/msm/qpnp-coincell.c b/drivers/platform/msm/qpnp-coincell.c
new file mode 100644
index 0000000..b427f43
--- /dev/null
+++ b/drivers/platform/msm/qpnp-coincell.c
@@ -0,0 +1,283 @@
+/* Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+
+#define QPNP_COINCELL_DRIVER_NAME "qcom,qpnp-coincell"
+
+struct qpnp_coincell {
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	u16			base_addr;
+};
+
+#define QPNP_COINCELL_REG_TYPE		0x04
+#define QPNP_COINCELL_REG_SUBTYPE	0x05
+#define QPNP_COINCELL_REG_RSET		0x44
+#define QPNP_COINCELL_REG_VSET		0x45
+#define QPNP_COINCELL_REG_ENABLE	0x46
+
+#define QPNP_COINCELL_TYPE		0x02
+#define QPNP_COINCELL_SUBTYPE		0x20
+#define QPNP_COINCELL_ENABLE		0x80
+#define QPNP_COINCELL_DISABLE		0x00
+
+static const int qpnp_rset_map[] = {2100, 1700, 1200, 800};
+static const int qpnp_vset_map[] = {2500, 3200, 3100, 3000};
+
+static int qpnp_coincell_set_resistance(struct qpnp_coincell *chip, int rset)
+{
+	int i, rc;
+	u8 reg;
+
+	for (i = 0; i < ARRAY_SIZE(qpnp_rset_map); i++)
+		if (rset == qpnp_rset_map[i])
+			break;
+
+	if (i >= ARRAY_SIZE(qpnp_rset_map)) {
+		pr_err("invalid rset=%d value\n", rset);
+		return -EINVAL;
+	}
+
+	reg = i;
+	rc = regmap_write(chip->regmap,
+			  chip->base_addr + QPNP_COINCELL_REG_RSET, reg);
+	if (rc)
+		dev_err(&chip->pdev->dev,
+			"%s: could not write to RSET register, rc=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static int qpnp_coincell_set_voltage(struct qpnp_coincell *chip, int vset)
+{
+	int i, rc;
+	u8 reg;
+
+	for (i = 0; i < ARRAY_SIZE(qpnp_vset_map); i++)
+		if (vset == qpnp_vset_map[i])
+			break;
+
+	if (i >= ARRAY_SIZE(qpnp_vset_map)) {
+		pr_err("invalid vset=%d value\n", vset);
+		return -EINVAL;
+	}
+
+	reg = i;
+	rc = regmap_write(chip->regmap,
+			  chip->base_addr + QPNP_COINCELL_REG_VSET, reg);
+	if (rc)
+		dev_err(&chip->pdev->dev,
+			"%s: could not write to VSET register, rc=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static int qpnp_coincell_set_charge(struct qpnp_coincell *chip, bool enabled)
+{
+	int rc;
+	u8 reg;
+
+	reg = enabled ? QPNP_COINCELL_ENABLE : QPNP_COINCELL_DISABLE;
+	rc = regmap_write(chip->regmap,
+			  chip->base_addr + QPNP_COINCELL_REG_ENABLE, reg);
+	if (rc)
+		dev_err(&chip->pdev->dev,
+			"%s: could not write to ENABLE register, rc=%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static void qpnp_coincell_charger_show_state(struct qpnp_coincell *chip)
+{
+	int rc, rset, vset, temp;
+	bool enabled;
+	u8 reg[QPNP_COINCELL_REG_ENABLE - QPNP_COINCELL_REG_RSET + 1];
+
+	rc = regmap_bulk_read(chip->regmap,
+			      chip->base_addr + QPNP_COINCELL_REG_RSET, reg,
+			      ARRAY_SIZE(reg));
+	if (rc) {
+		dev_err(&chip->pdev->dev,
+			"%s: could not read RSET register, rc=%d\n",
+			__func__, rc);
+		return;
+	}
+
+	temp = reg[QPNP_COINCELL_REG_RSET - QPNP_COINCELL_REG_RSET];
+	if (temp >= ARRAY_SIZE(qpnp_rset_map)) {
+		dev_err(&chip->pdev->dev,
+			"unknown RSET=0x%02X register value\n",
+			temp);
+		return;
+	}
+	rset = qpnp_rset_map[temp];
+
+	temp = reg[QPNP_COINCELL_REG_VSET - QPNP_COINCELL_REG_RSET];
+	if (temp >= ARRAY_SIZE(qpnp_vset_map)) {
+		dev_err(&chip->pdev->dev,
+			"unknown VSET=0x%02X register value\n",
+			temp);
+		return;
+	}
+	vset = qpnp_vset_map[temp];
+
+	temp = reg[QPNP_COINCELL_REG_ENABLE - QPNP_COINCELL_REG_RSET];
+	enabled = temp & QPNP_COINCELL_ENABLE;
+
+	pr_info("enabled=%c, voltage=%d mV, resistance=%d ohm\n",
+		(enabled ? 'Y' : 'N'), vset, rset);
+}
+
+static int qpnp_coincell_check_type(struct qpnp_coincell *chip)
+{
+	int rc;
+	u8 type[2];
+
+	rc = regmap_bulk_read(chip->regmap,
+			      chip->base_addr + QPNP_COINCELL_REG_TYPE, type,
+			      2);
+	if (rc) {
+		dev_err(&chip->pdev->dev,
+			"%s: could not read type register, rc=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	if (type[0] != QPNP_COINCELL_TYPE || type[1] != QPNP_COINCELL_SUBTYPE) {
+		dev_err(&chip->pdev->dev,
+			"%s: invalid type=0x%02X or subtype=0x%02X register value\n",
+			__func__, type[0], type[1]);
+		return -ENODEV;
+	}
+
+	return rc;
+}
+
+static int qpnp_coincell_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node;
+	struct qpnp_coincell *chip;
+	unsigned int base;
+	u32 temp;
+	int rc = 0;
+
+	if (!node) {
+		dev_err(&pdev->dev, "%s: device node missing\n", __func__);
+		return -ENODEV;
+	}
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!chip->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+	chip->pdev = pdev;
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+	chip->base_addr = base;
+
+	rc = qpnp_coincell_check_type(chip);
+	if (rc)
+		return rc;
+
+	rc = of_property_read_u32(node, "qcom,rset-ohms", &temp);
+	if (!rc) {
+		rc = qpnp_coincell_set_resistance(chip, temp);
+		if (rc)
+			return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,vset-millivolts", &temp);
+	if (!rc) {
+		rc = qpnp_coincell_set_voltage(chip, temp);
+		if (rc)
+			return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,charge-enable", &temp);
+	if (!rc) {
+		rc = qpnp_coincell_set_charge(chip, temp);
+		if (rc)
+			return rc;
+	}
+
+	qpnp_coincell_charger_show_state(chip);
+
+	return 0;
+}
+
+static int qpnp_coincell_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id qpnp_coincell_match_table[] = {
+	{ .compatible = QPNP_COINCELL_DRIVER_NAME, },
+	{}
+};
+
+static const struct platform_device_id qpnp_coincell_id[] = {
+	{ QPNP_COINCELL_DRIVER_NAME, 0 },
+	{}
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_coincell_id);
+
+static struct platform_driver qpnp_coincell_driver = {
+	.driver	= {
+		.name		= QPNP_COINCELL_DRIVER_NAME,
+		.of_match_table	= qpnp_coincell_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= qpnp_coincell_probe,
+	.remove		= qpnp_coincell_remove,
+	.id_table	= qpnp_coincell_id,
+};
+
+static int __init qpnp_coincell_init(void)
+{
+	return platform_driver_register(&qpnp_coincell_driver);
+}
+
+static void __exit qpnp_coincell_exit(void)
+{
+	platform_driver_unregister(&qpnp_coincell_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC coincell charger driver");
+MODULE_LICENSE("GPL v2");
+
+module_init(qpnp_coincell_init);
+module_exit(qpnp_coincell_exit);
diff --git a/drivers/platform/msm/qpnp-revid.c b/drivers/platform/msm/qpnp-revid.c
new file mode 100644
index 0000000..9ea0b40
--- /dev/null
+++ b/drivers/platform/msm/qpnp-revid.c
@@ -0,0 +1,270 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/err.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <linux/of.h>
+
+#define REVID_REVISION1	0x0
+#define REVID_REVISION2	0x1
+#define REVID_REVISION3	0x2
+#define REVID_REVISION4	0x3
+#define REVID_TYPE	0x4
+#define REVID_SUBTYPE	0x5
+#define REVID_STATUS1	0x8
+#define REVID_SPARE_0	0x60
+#define REVID_FAB_ID	0xf2
+
+#define QPNP_REVID_DEV_NAME "qcom,qpnp-revid"
+
+static const char *const pmic_names[] = {
+	[0] =	"Unknown PMIC",
+	[PM8941_SUBTYPE] = "PM8941",
+	[PM8841_SUBTYPE] = "PM8841",
+	[PM8019_SUBTYPE] = "PM8019",
+	[PM8226_SUBTYPE] = "PM8226",
+	[PM8110_SUBTYPE] = "PM8110",
+	[PMA8084_SUBTYPE] = "PMA8084",
+	[PMI8962_SUBTYPE] = "PMI8962",
+	[PMD9635_SUBTYPE] = "PMD9635",
+	[PM8994_SUBTYPE] = "PM8994",
+	[PMI8994_SUBTYPE] = "PMI8994",
+	[PM8916_SUBTYPE] = "PM8916",
+	[PM8004_SUBTYPE] = "PM8004",
+	[PM8909_SUBTYPE] = "PM8909",
+	[PM2433_SUBTYPE] = "PM2433",
+	[PMD9655_SUBTYPE] = "PMD9655",
+	[PM8950_SUBTYPE] = "PM8950",
+	[PMI8950_SUBTYPE] = "PMI8950",
+	[PMK8001_SUBTYPE] = "PMK8001",
+	[PMI8996_SUBTYPE] = "PMI8996",
+	[PM8998_SUBTYPE] = "PM8998",
+	[PMI8998_SUBTYPE] = "PMI8998",
+	[PM8005_SUBTYPE] = "PM8005",
+	[PM8937_SUBTYPE] = "PM8937",
+	[PM660L_SUBTYPE] = "PM660L",
+	[PM660_SUBTYPE] = "PM660",
+	[PMI8937_SUBTYPE] = "PMI8937",
+};
+
+struct revid_chip {
+	struct list_head	link;
+	struct device_node	*dev_node;
+	struct pmic_revid_data	data;
+};
+
+static LIST_HEAD(revid_chips);
+static DEFINE_MUTEX(revid_chips_lock);
+
+static const struct of_device_id qpnp_revid_match_table[] = {
+	{ .compatible = QPNP_REVID_DEV_NAME },
+	{}
+};
+
+static u8 qpnp_read_byte(struct regmap *regmap, u16 addr)
+{
+	int rc;
+	int val;
+
+	rc = regmap_read(regmap, addr, &val);
+	if (rc) {
+		pr_err("read failed rc=%d\n", rc);
+		return 0;
+	}
+	return (u8)val;
+}
+
+/**
+ * get_revid_data - Return the revision information of PMIC
+ * @dev_node: Pointer to the revid peripheral of the PMIC for which
+ *		revision information is seeked
+ *
+ * CONTEXT: Should be called in non atomic context
+ *
+ * RETURNS: pointer to struct pmic_revid_data filled with the information
+ *		about the PMIC revision
+ */
+struct pmic_revid_data *get_revid_data(struct device_node *dev_node)
+{
+	struct revid_chip *revid_chip;
+
+	if (!dev_node)
+		return ERR_PTR(-EINVAL);
+
+	mutex_lock(&revid_chips_lock);
+	list_for_each_entry(revid_chip, &revid_chips, link) {
+		if (dev_node == revid_chip->dev_node) {
+			mutex_unlock(&revid_chips_lock);
+			return &revid_chip->data;
+		}
+	}
+	mutex_unlock(&revid_chips_lock);
+	return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL(get_revid_data);
+
+#define PM8941_PERIPHERAL_SUBTYPE	0x01
+#define PM8226_PERIPHERAL_SUBTYPE	0x04
+#define PMD9655_PERIPHERAL_SUBTYPE	0x0F
+#define PMI8950_PERIPHERAL_SUBTYPE	0x11
+#define PMI8937_PERIPHERAL_SUBTYPE	0x37
+static size_t build_pmic_string(char *buf, size_t n, int sid,
+		u8 subtype, u8 rev1, u8 rev2, u8 rev3, u8 rev4)
+{
+	size_t pos = 0;
+	/*
+	 * In early versions of PM8941 and PM8226, the major revision number
+	 * started incrementing from 0 (eg 0 = v1.0, 1 = v2.0).
+	 * Increment the major revision number here if the chip is an early
+	 * version of PM8941 or PM8226.
+	 */
+	if (((int)subtype == PM8941_PERIPHERAL_SUBTYPE
+			|| (int)subtype == PM8226_PERIPHERAL_SUBTYPE)
+			&& rev4 < 0x02)
+		rev4++;
+
+	pos += snprintf(buf + pos, n - pos, "PMIC@SID%d", sid);
+	if (subtype >= ARRAY_SIZE(pmic_names) || subtype == 0)
+		pos += snprintf(buf + pos, n - pos, ": %s (subtype: 0x%02X)",
+				pmic_names[0], subtype);
+	else
+		pos += snprintf(buf + pos, n - pos, ": %s",
+				pmic_names[subtype]);
+	pos += snprintf(buf + pos, n - pos, " v%d.%d", rev4, rev3);
+	if (rev2 || rev1)
+		pos += snprintf(buf + pos, n - pos, ".%d", rev2);
+	if (rev1)
+		pos += snprintf(buf + pos, n - pos, ".%d", rev1);
+	return pos;
+}
+
+#define PMIC_PERIPHERAL_TYPE		0x51
+#define PMIC_STRING_MAXLENGTH		80
+static int qpnp_revid_probe(struct platform_device *pdev)
+{
+	u8 rev1, rev2, rev3, rev4, pmic_type, pmic_subtype, pmic_status;
+	u8 option1, option2, option3, option4, spare0, fab_id;
+	unsigned int base;
+	int rc;
+	char pmic_string[PMIC_STRING_MAXLENGTH] = {'\0'};
+	struct revid_chip *revid_chip;
+	struct regmap *regmap;
+
+	regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+	pmic_type = qpnp_read_byte(regmap, base + REVID_TYPE);
+	if (pmic_type != PMIC_PERIPHERAL_TYPE) {
+		pr_err("Invalid REVID peripheral type: %02X\n", pmic_type);
+		return -EINVAL;
+	}
+
+	rev1 = qpnp_read_byte(regmap, base + REVID_REVISION1);
+	rev2 = qpnp_read_byte(regmap, base + REVID_REVISION2);
+	rev3 = qpnp_read_byte(regmap, base + REVID_REVISION3);
+	rev4 = qpnp_read_byte(regmap, base + REVID_REVISION4);
+
+	pmic_subtype = qpnp_read_byte(regmap, base + REVID_SUBTYPE);
+	if (pmic_subtype != PMD9655_PERIPHERAL_SUBTYPE)
+		pmic_status = qpnp_read_byte(regmap, base + REVID_STATUS1);
+	else
+		pmic_status = 0;
+
+	/* special case for PMI8937 */
+	if (pmic_subtype == PMI8950_PERIPHERAL_SUBTYPE) {
+		/* read spare register */
+		spare0 = qpnp_read_byte(regmap, base + REVID_SPARE_0);
+		if (spare0)
+			pmic_subtype = PMI8937_PERIPHERAL_SUBTYPE;
+	}
+
+	if (of_property_read_bool(pdev->dev.of_node, "qcom,fab-id-valid"))
+		fab_id = qpnp_read_byte(regmap, base + REVID_FAB_ID);
+	else
+		fab_id = -EINVAL;
+
+	revid_chip = devm_kzalloc(&pdev->dev, sizeof(struct revid_chip),
+						GFP_KERNEL);
+	if (!revid_chip)
+		return -ENOMEM;
+
+	revid_chip->dev_node = pdev->dev.of_node;
+	revid_chip->data.rev1 = rev1;
+	revid_chip->data.rev2 = rev2;
+	revid_chip->data.rev3 = rev3;
+	revid_chip->data.rev4 = rev4;
+	revid_chip->data.pmic_subtype = pmic_subtype;
+	revid_chip->data.pmic_type = pmic_type;
+	revid_chip->data.fab_id = fab_id;
+
+	if (pmic_subtype < ARRAY_SIZE(pmic_names))
+		revid_chip->data.pmic_name = pmic_names[pmic_subtype];
+	else
+		revid_chip->data.pmic_name = pmic_names[0];
+
+	mutex_lock(&revid_chips_lock);
+	list_add(&revid_chip->link, &revid_chips);
+	mutex_unlock(&revid_chips_lock);
+
+	option1 = pmic_status & 0x3;
+	option2 = (pmic_status >> 2) & 0x3;
+	option3 = (pmic_status >> 4) & 0x3;
+	option4 = (pmic_status >> 6) & 0x3;
+
+	build_pmic_string(pmic_string, PMIC_STRING_MAXLENGTH,
+			  to_spmi_device(pdev->dev.parent)->usid,
+			pmic_subtype, rev1, rev2, rev3, rev4);
+	pr_info("%s options: %d, %d, %d, %d\n",
+			pmic_string, option1, option2, option3, option4);
+	return 0;
+}
+
+static struct platform_driver qpnp_revid_driver = {
+	.probe	= qpnp_revid_probe,
+	.driver	= {
+		.name		= QPNP_REVID_DEV_NAME,
+		.owner		= THIS_MODULE,
+		.of_match_table	= qpnp_revid_match_table,
+	},
+};
+
+static int __init qpnp_revid_init(void)
+{
+	return platform_driver_register(&qpnp_revid_driver);
+}
+
+static void __exit qpnp_revid_exit(void)
+{
+	return platform_driver_unregister(&qpnp_revid_driver);
+}
+
+subsys_initcall(qpnp_revid_init);
+module_exit(qpnp_revid_exit);
+
+MODULE_DESCRIPTION("QPNP REVID DRIVER");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_REVID_DEV_NAME);
diff --git a/drivers/platform/msm/sps/Makefile b/drivers/platform/msm/sps/Makefile
new file mode 100644
index 0000000..f19e162
--- /dev/null
+++ b/drivers/platform/msm/sps/Makefile
@@ -0,0 +1,2 @@
+obj-y += bam.o sps_bam.o sps.o sps_dma.o sps_map.o sps_mem.o sps_rm.o
+
diff --git a/drivers/platform/msm/sps/bam.c b/drivers/platform/msm/sps/bam.c
new file mode 100644
index 0000000..8d8af1b
--- /dev/null
+++ b/drivers/platform/msm/sps/bam.c
@@ -0,0 +1,2346 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Bus-Access-Manager (BAM) Hardware manager. */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/io.h>		/* ioread32() */
+#include <linux/bitops.h>	/* find_first_bit() */
+#include <linux/errno.h>	/* ENODEV */
+#include <linux/memory.h>
+
+#include "bam.h"
+#include "sps_bam.h"
+
+/**
+ *  Valid BAM Hardware version.
+ *
+ */
+#define BAM_MIN_VERSION 2
+#define BAM_MAX_VERSION 0x2f
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+
+/* Maximum number of execution environment */
+#define BAM_MAX_EES 8
+
+/**
+ *  BAM Hardware registers bitmask.
+ *  format: <register>_<field>
+ *
+ */
+/* CTRL */
+#define BAM_MESS_ONLY_CANCEL_WB               0x100000
+#define CACHE_MISS_ERR_RESP_EN                 0x80000
+#define LOCAL_CLK_GATING                       0x60000
+#define IBC_DISABLE                            0x10000
+#define BAM_CACHED_DESC_STORE                   0x8000
+#define BAM_DESC_CACHE_SEL                      0x6000
+#define BAM_EN_ACCUM                              0x10
+#define BAM_EN                                     0x2
+#define BAM_SW_RST                                 0x1
+
+/* REVISION */
+#define BAM_INACTIV_TMR_BASE                0xff000000
+#define BAM_CMD_DESC_EN                       0x800000
+#define BAM_DESC_CACHE_DEPTH                  0x600000
+#define BAM_NUM_INACTIV_TMRS                  0x100000
+#define BAM_INACTIV_TMRS_EXST                  0x80000
+#define BAM_HIGH_FREQUENCY_BAM                 0x40000
+#define BAM_HAS_NO_BYPASS                      0x20000
+#define BAM_SECURED                            0x10000
+#define BAM_USE_VMIDMT                          0x8000
+#define BAM_AXI_ACTIVE                          0x4000
+#define BAM_CE_BUFFER_SIZE                      0x3000
+#define BAM_NUM_EES                              0xf00
+#define BAM_REVISION                              0xff
+
+/* SW_REVISION */
+#define BAM_MAJOR                           0xf0000000
+#define BAM_MINOR                            0xfff0000
+#define BAM_STEP                                0xffff
+
+/* NUM_PIPES */
+#define BAM_NON_PIPE_GRP                    0xff000000
+#define BAM_PERIPH_NON_PIPE_GRP               0xff0000
+#define BAM_DATA_ADDR_BUS_WIDTH                 0xC000
+#define BAM_NUM_PIPES                             0xff
+
+/* TIMER */
+#define BAM_TIMER                               0xffff
+
+/* TIMER_CTRL */
+#define TIMER_RST                           0x80000000
+#define TIMER_RUN                           0x40000000
+#define TIMER_MODE                          0x20000000
+#define TIMER_TRSHLD                            0xffff
+
+/* DESC_CNT_TRSHLD */
+#define BAM_DESC_CNT_TRSHLD                     0xffff
+
+/* IRQ_SRCS */
+#define BAM_IRQ                         0x80000000
+#define P_IRQ                           0x7fffffff
+
+/* IRQ_STTS */
+#define IRQ_STTS_BAM_TIMER_IRQ                         0x10
+#define IRQ_STTS_BAM_EMPTY_IRQ                          0x8
+#define IRQ_STTS_BAM_ERROR_IRQ                          0x4
+#define IRQ_STTS_BAM_HRESP_ERR_IRQ                      0x2
+
+/* IRQ_CLR */
+#define IRQ_CLR_BAM_TIMER_IRQ                          0x10
+#define IRQ_CLR_BAM_EMPTY_CLR                           0x8
+#define IRQ_CLR_BAM_ERROR_CLR                           0x4
+#define IRQ_CLR_BAM_HRESP_ERR_CLR                       0x2
+
+/* IRQ_EN */
+#define IRQ_EN_BAM_TIMER_IRQ                           0x10
+#define IRQ_EN_BAM_EMPTY_EN                             0x8
+#define IRQ_EN_BAM_ERROR_EN                             0x4
+#define IRQ_EN_BAM_HRESP_ERR_EN                         0x2
+
+/* AHB_MASTER_ERR_CTRLS */
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HVMID         0x7c0000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_DIRECT_MODE    0x20000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HCID           0x1f000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HPROT            0xf00
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HBURST            0xe0
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HSIZE             0x18
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HWRITE             0x4
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HTRANS             0x3
+
+/* TRUST_REG  */
+#define LOCK_EE_CTRL                            0x2000
+#define BAM_VMID                                0x1f00
+#define BAM_RST_BLOCK                             0x80
+#define BAM_EE                                     0x7
+
+/* TEST_BUS_SEL */
+#define BAM_SW_EVENTS_ZERO                    0x200000
+#define BAM_SW_EVENTS_SEL                     0x180000
+#define BAM_DATA_ERASE                         0x40000
+#define BAM_DATA_FLUSH                         0x20000
+#define BAM_CLK_ALWAYS_ON                      0x10000
+#define BAM_TESTBUS_SEL                           0x7f
+
+/* CNFG_BITS */
+#define CNFG_BITS_AOS_OVERFLOW_PRVNT		 0x80000000
+#define CNFG_BITS_MULTIPLE_EVENTS_DESC_AVAIL_EN  0x40000000
+#define CNFG_BITS_MULTIPLE_EVENTS_SIZE_EN        0x20000000
+#define CNFG_BITS_BAM_ZLT_W_CD_SUPPORT           0x10000000
+#define CNFG_BITS_BAM_CD_ENABLE                   0x8000000
+#define CNFG_BITS_BAM_AU_ACCUMED                  0x4000000
+#define CNFG_BITS_BAM_PSM_P_HD_DATA               0x2000000
+#define CNFG_BITS_BAM_REG_P_EN                    0x1000000
+#define CNFG_BITS_BAM_WB_DSC_AVL_P_RST             0x800000
+#define CNFG_BITS_BAM_WB_RETR_SVPNT                0x400000
+#define CNFG_BITS_BAM_WB_CSW_ACK_IDL               0x200000
+#define CNFG_BITS_BAM_WB_BLK_CSW                   0x100000
+#define CNFG_BITS_BAM_WB_P_RES                      0x80000
+#define CNFG_BITS_BAM_SI_P_RES                      0x40000
+#define CNFG_BITS_BAM_AU_P_RES                      0x20000
+#define CNFG_BITS_BAM_PSM_P_RES                     0x10000
+#define CNFG_BITS_BAM_PSM_CSW_REQ                    0x8000
+#define CNFG_BITS_BAM_SB_CLK_REQ                     0x4000
+#define CNFG_BITS_BAM_IBC_DISABLE                    0x2000
+#define CNFG_BITS_BAM_NO_EXT_P_RST                   0x1000
+#define CNFG_BITS_BAM_FULL_PIPE                       0x800
+#define CNFG_BITS_BAM_PIPE_CNFG                         0x4
+
+/* PIPE_ATTR_EEn*/
+#define BAM_ENABLED                              0x80000000
+#define P_ATTR                                   0x7fffffff
+
+/* P_ctrln */
+#define P_LOCK_GROUP                          0x1f0000
+#define P_WRITE_NWD                              0x800
+#define P_PREFETCH_LIMIT                         0x600
+#define P_AUTO_EOB_SEL                           0x180
+#define P_AUTO_EOB                                0x40
+#define P_SYS_MODE                                0x20
+#define P_SYS_STRM                                0x10
+#define P_DIRECTION                                0x8
+#define P_EN                                       0x2
+
+/* P_RSTn */
+#define P_RST_P_SW_RST                             0x1
+
+/* P_HALTn */
+#define P_HALT_P_PIPE_EMPTY			   0x8
+#define P_HALT_P_LAST_DESC_ZLT                     0x4
+#define P_HALT_P_PROD_HALTED                       0x2
+#define P_HALT_P_HALT                              0x1
+
+/* P_TRUST_REGn */
+#define BAM_P_VMID                              0x1f00
+#define BAM_P_SUP_GROUP                           0xf8
+#define BAM_P_EE                                   0x7
+
+/* P_IRQ_STTSn */
+#define P_IRQ_STTS_P_HRESP_ERR_IRQ                0x80
+#define P_IRQ_STTS_P_PIPE_RST_ERR_IRQ             0x40
+#define P_IRQ_STTS_P_TRNSFR_END_IRQ               0x20
+#define P_IRQ_STTS_P_ERR_IRQ                      0x10
+#define P_IRQ_STTS_P_OUT_OF_DESC_IRQ               0x8
+#define P_IRQ_STTS_P_WAKE_IRQ                      0x4
+#define P_IRQ_STTS_P_TIMER_IRQ                     0x2
+#define P_IRQ_STTS_P_PRCSD_DESC_IRQ                0x1
+
+/* P_IRQ_CLRn */
+#define P_IRQ_CLR_P_HRESP_ERR_CLR                 0x80
+#define P_IRQ_CLR_P_PIPE_RST_ERR_CLR              0x40
+#define P_IRQ_CLR_P_TRNSFR_END_CLR                0x20
+#define P_IRQ_CLR_P_ERR_CLR                       0x10
+#define P_IRQ_CLR_P_OUT_OF_DESC_CLR                0x8
+#define P_IRQ_CLR_P_WAKE_CLR                       0x4
+#define P_IRQ_CLR_P_TIMER_CLR                      0x2
+#define P_IRQ_CLR_P_PRCSD_DESC_CLR                 0x1
+
+/* P_IRQ_ENn */
+#define P_IRQ_EN_P_HRESP_ERR_EN                   0x80
+#define P_IRQ_EN_P_PIPE_RST_ERR_EN                0x40
+#define P_IRQ_EN_P_TRNSFR_END_EN                  0x20
+#define P_IRQ_EN_P_ERR_EN                         0x10
+#define P_IRQ_EN_P_OUT_OF_DESC_EN                  0x8
+#define P_IRQ_EN_P_WAKE_EN                         0x4
+#define P_IRQ_EN_P_TIMER_EN                        0x2
+#define P_IRQ_EN_P_PRCSD_DESC_EN                   0x1
+
+/* P_TIMERn */
+#define P_TIMER_P_TIMER                         0xffff
+
+/* P_TIMER_ctrln */
+#define P_TIMER_RST                         0x80000000
+#define P_TIMER_RUN                         0x40000000
+#define P_TIMER_MODE                        0x20000000
+#define P_TIMER_TRSHLD                          0xffff
+
+/* P_PRDCR_SDBNDn */
+#define P_PRDCR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_PRDCR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_PRDCR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_PRDCR_SDBNDn_BAM_P_BYTES_FREE         0xffff
+
+/* P_CNSMR_SDBNDn */
+#define P_CNSMR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK       0x800000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE       0x400000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R     0x200000
+#define P_CNSMR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_CNSMR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL        0xffff
+
+/* P_EVNT_regn */
+#define P_BYTES_CONSUMED                    0xffff0000
+#define P_DESC_FIFO_PEER_OFST                   0xffff
+
+/* P_SW_ofstsn */
+#define SW_OFST_IN_DESC                     0xffff0000
+#define SW_DESC_OFST                            0xffff
+
+/* P_EVNT_GEN_TRSHLDn */
+#define P_EVNT_GEN_TRSHLD_P_TRSHLD              0xffff
+
+/* P_FIFO_sizesn */
+#define P_DATA_FIFO_SIZE                    0xffff0000
+#define P_DESC_FIFO_SIZE                        0xffff
+
+#define P_RETR_CNTXT_RETR_DESC_OFST            0xffff0000
+#define P_RETR_CNTXT_RETR_OFST_IN_DESC             0xffff
+#define P_SI_CNTXT_SI_DESC_OFST                    0xffff
+#define P_DF_CNTXT_WB_ACCUMULATED              0xffff0000
+#define P_DF_CNTXT_DF_DESC_OFST                    0xffff
+#define P_AU_PSM_CNTXT_1_AU_PSM_ACCUMED        0xffff0000
+#define P_AU_PSM_CNTXT_1_AU_ACKED                  0xffff
+#define P_PSM_CNTXT_2_PSM_DESC_VALID           0x80000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ             0x40000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ_DONE        0x20000000
+#define P_PSM_CNTXT_2_PSM_GENERAL_BITS         0x1e000000
+#define P_PSM_CNTXT_2_PSM_CONS_STATE            0x1c00000
+#define P_PSM_CNTXT_2_PSM_PROD_SYS_STATE         0x380000
+#define P_PSM_CNTXT_2_PSM_PROD_B2B_STATE          0x70000
+#define P_PSM_CNTXT_2_PSM_DESC_SIZE                0xffff
+#define P_PSM_CNTXT_4_PSM_DESC_OFST            0xffff0000
+#define P_PSM_CNTXT_4_PSM_SAVED_ACCUMED_SIZE       0xffff
+#define P_PSM_CNTXT_5_PSM_BLOCK_BYTE_CNT       0xffff0000
+#define P_PSM_CNTXT_5_PSM_OFST_IN_DESC             0xffff
+
+#else
+
+/* Maximum number of execution environment */
+#define BAM_MAX_EES 4
+
+/**
+ *  BAM Hardware registers bitmask.
+ *  format: <register>_<field>
+ *
+ */
+/* CTRL */
+#define IBC_DISABLE                            0x10000
+#define BAM_CACHED_DESC_STORE                   0x8000
+#define BAM_DESC_CACHE_SEL                      0x6000
+/* BAM_PERIPH_IRQ_SIC_SEL is an obsolete field; This bit is reserved now */
+#define BAM_PERIPH_IRQ_SIC_SEL                  0x1000
+#define BAM_EN_ACCUM                              0x10
+#define BAM_EN                                     0x2
+#define BAM_SW_RST                                 0x1
+
+/* REVISION */
+#define BAM_INACTIV_TMR_BASE                0xff000000
+#define BAM_INACTIV_TMRS_EXST                  0x80000
+#define BAM_HIGH_FREQUENCY_BAM                 0x40000
+#define BAM_HAS_NO_BYPASS                      0x20000
+#define BAM_SECURED                            0x10000
+#define BAM_NUM_EES                              0xf00
+#define BAM_REVISION                              0xff
+
+/* NUM_PIPES */
+#define BAM_NON_PIPE_GRP                    0xff000000
+#define BAM_PERIPH_NON_PIPE_GRP               0xff0000
+#define BAM_DATA_ADDR_BUS_WIDTH                 0xC000
+#define BAM_NUM_PIPES                             0xff
+
+/* DESC_CNT_TRSHLD */
+#define BAM_DESC_CNT_TRSHLD                     0xffff
+
+/* IRQ_SRCS */
+#define BAM_IRQ                         0x80000000
+#define P_IRQ                           0x7fffffff
+
+#define IRQ_STTS_BAM_EMPTY_IRQ                          0x8
+#define IRQ_STTS_BAM_ERROR_IRQ                          0x4
+#define IRQ_STTS_BAM_HRESP_ERR_IRQ                      0x2
+#define IRQ_CLR_BAM_EMPTY_CLR                           0x8
+#define IRQ_CLR_BAM_ERROR_CLR                           0x4
+#define IRQ_CLR_BAM_HRESP_ERR_CLR                       0x2
+#define IRQ_EN_BAM_EMPTY_EN                             0x8
+#define IRQ_EN_BAM_ERROR_EN                             0x4
+#define IRQ_EN_BAM_HRESP_ERR_EN                         0x2
+#define IRQ_SIC_SEL_BAM_IRQ_SIC_SEL              0x80000000
+#define IRQ_SIC_SEL_P_IRQ_SIC_SEL                0x7fffffff
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HVMID         0x7c0000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_DIRECT_MODE    0x20000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HCID           0x1f000
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HPROT            0xf00
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HBURST            0xe0
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HSIZE             0x18
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HWRITE             0x4
+#define AHB_MASTER_ERR_CTRLS_BAM_ERR_HTRANS             0x3
+#define CNFG_BITS_BAM_AU_ACCUMED                  0x4000000
+#define CNFG_BITS_BAM_PSM_P_HD_DATA               0x2000000
+#define CNFG_BITS_BAM_REG_P_EN                    0x1000000
+#define CNFG_BITS_BAM_WB_DSC_AVL_P_RST             0x800000
+#define CNFG_BITS_BAM_WB_RETR_SVPNT                0x400000
+#define CNFG_BITS_BAM_WB_CSW_ACK_IDL               0x200000
+#define CNFG_BITS_BAM_WB_BLK_CSW                   0x100000
+#define CNFG_BITS_BAM_WB_P_RES                      0x80000
+#define CNFG_BITS_BAM_SI_P_RES                      0x40000
+#define CNFG_BITS_BAM_AU_P_RES                      0x20000
+#define CNFG_BITS_BAM_PSM_P_RES                     0x10000
+#define CNFG_BITS_BAM_PSM_CSW_REQ                    0x8000
+#define CNFG_BITS_BAM_SB_CLK_REQ                     0x4000
+#define CNFG_BITS_BAM_IBC_DISABLE                    0x2000
+#define CNFG_BITS_BAM_NO_EXT_P_RST                   0x1000
+#define CNFG_BITS_BAM_FULL_PIPE                       0x800
+#define CNFG_BITS_BAM_PIPE_CNFG                         0x4
+
+/* TEST_BUS_SEL */
+#define BAM_DATA_ERASE                         0x40000
+#define BAM_DATA_FLUSH                         0x20000
+#define BAM_CLK_ALWAYS_ON                      0x10000
+#define BAM_TESTBUS_SEL                           0x7f
+
+/* TRUST_REG  */
+#define BAM_VMID                                0x1f00
+#define BAM_RST_BLOCK                             0x80
+#define BAM_EE                                     0x3
+
+/* P_TRUST_REGn */
+#define BAM_P_VMID                              0x1f00
+#define BAM_P_EE                                   0x3
+
+/* P_PRDCR_SDBNDn */
+#define P_PRDCR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_PRDCR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_PRDCR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_PRDCR_SDBNDn_BAM_P_BYTES_FREE         0xffff
+/* P_CNSMR_SDBNDn */
+#define P_CNSMR_SDBNDn_BAM_P_SB_UPDATED      0x1000000
+#define P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK       0x800000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE       0x400000
+#define P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R     0x200000
+#define P_CNSMR_SDBNDn_BAM_P_TOGGLE           0x100000
+#define P_CNSMR_SDBNDn_BAM_P_CTRL              0xf0000
+#define P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL        0xffff
+
+/* P_ctrln */
+#define P_PREFETCH_LIMIT                         0x600
+#define P_AUTO_EOB_SEL                           0x180
+#define P_AUTO_EOB                                0x40
+#define P_SYS_MODE                             0x20
+#define P_SYS_STRM                             0x10
+#define P_DIRECTION                             0x8
+#define P_EN                                    0x2
+
+#define P_RST_P_SW_RST                                 0x1
+
+#define P_HALT_P_PROD_HALTED                           0x2
+#define P_HALT_P_HALT                                  0x1
+
+#define P_IRQ_STTS_P_TRNSFR_END_IRQ                   0x20
+#define P_IRQ_STTS_P_ERR_IRQ                          0x10
+#define P_IRQ_STTS_P_OUT_OF_DESC_IRQ                   0x8
+#define P_IRQ_STTS_P_WAKE_IRQ                          0x4
+#define P_IRQ_STTS_P_TIMER_IRQ                         0x2
+#define P_IRQ_STTS_P_PRCSD_DESC_IRQ                    0x1
+
+#define P_IRQ_CLR_P_TRNSFR_END_CLR                    0x20
+#define P_IRQ_CLR_P_ERR_CLR                           0x10
+#define P_IRQ_CLR_P_OUT_OF_DESC_CLR                    0x8
+#define P_IRQ_CLR_P_WAKE_CLR                           0x4
+#define P_IRQ_CLR_P_TIMER_CLR                          0x2
+#define P_IRQ_CLR_P_PRCSD_DESC_CLR                     0x1
+
+#define P_IRQ_EN_P_TRNSFR_END_EN                      0x20
+#define P_IRQ_EN_P_ERR_EN                             0x10
+#define P_IRQ_EN_P_OUT_OF_DESC_EN                      0x8
+#define P_IRQ_EN_P_WAKE_EN                             0x4
+#define P_IRQ_EN_P_TIMER_EN                            0x2
+#define P_IRQ_EN_P_PRCSD_DESC_EN                       0x1
+
+#define P_TIMER_P_TIMER                             0xffff
+
+/* P_TIMER_ctrln */
+#define P_TIMER_RST                0x80000000
+#define P_TIMER_RUN                0x40000000
+#define P_TIMER_MODE               0x20000000
+#define P_TIMER_TRSHLD                 0xffff
+
+/* P_EVNT_regn */
+#define P_BYTES_CONSUMED             0xffff0000
+#define P_DESC_FIFO_PEER_OFST            0xffff
+
+/* P_SW_ofstsn */
+#define SW_OFST_IN_DESC              0xffff0000
+#define SW_DESC_OFST                     0xffff
+
+#define P_EVNT_GEN_TRSHLD_P_TRSHLD                  0xffff
+
+/* P_FIFO_sizesn */
+#define P_DATA_FIFO_SIZE           0xffff0000
+#define P_DESC_FIFO_SIZE               0xffff
+
+#define P_RETR_CNTXT_RETR_DESC_OFST            0xffff0000
+#define P_RETR_CNTXT_RETR_OFST_IN_DESC             0xffff
+#define P_SI_CNTXT_SI_DESC_OFST                    0xffff
+#define P_AU_PSM_CNTXT_1_AU_PSM_ACCUMED        0xffff0000
+#define P_AU_PSM_CNTXT_1_AU_ACKED                  0xffff
+#define P_PSM_CNTXT_2_PSM_DESC_VALID           0x80000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ             0x40000000
+#define P_PSM_CNTXT_2_PSM_DESC_IRQ_DONE        0x20000000
+#define P_PSM_CNTXT_2_PSM_GENERAL_BITS         0x1e000000
+#define P_PSM_CNTXT_2_PSM_CONS_STATE            0x1c00000
+#define P_PSM_CNTXT_2_PSM_PROD_SYS_STATE         0x380000
+#define P_PSM_CNTXT_2_PSM_PROD_B2B_STATE          0x70000
+#define P_PSM_CNTXT_2_PSM_DESC_SIZE                0xffff
+#define P_PSM_CNTXT_4_PSM_DESC_OFST            0xffff0000
+#define P_PSM_CNTXT_4_PSM_SAVED_ACCUMED_SIZE       0xffff
+#define P_PSM_CNTXT_5_PSM_BLOCK_BYTE_CNT       0xffff0000
+#define P_PSM_CNTXT_5_PSM_OFST_IN_DESC             0xffff
+#endif
+
+#define BAM_ERROR   (-1)
+
+enum bam_regs {
+	CTRL,
+	REVISION,
+	SW_REVISION,
+	NUM_PIPES,
+	TIMER,
+	TIMER_CTRL,
+	DESC_CNT_TRSHLD,
+	IRQ_SRCS,
+	IRQ_SRCS_MSK,
+	IRQ_SRCS_UNMASKED,
+	IRQ_STTS,
+	IRQ_CLR,
+	IRQ_EN,
+	IRQ_SIC_SEL,
+	AHB_MASTER_ERR_CTRLS,
+	AHB_MASTER_ERR_ADDR,
+	AHB_MASTER_ERR_ADDR_MSB,
+	AHB_MASTER_ERR_DATA,
+	IRQ_DEST,
+	PERIPH_IRQ_DEST,
+	TRUST_REG,
+	TEST_BUS_SEL,
+	TEST_BUS_REG,
+	CNFG_BITS,
+	IRQ_SRCS_EE,
+	IRQ_SRCS_MSK_EE,
+	IRQ_SRCS_UNMASKED_EE,
+	PIPE_ATTR_EE,
+	P_CTRL,
+	P_RST,
+	P_HALT,
+	P_IRQ_STTS,
+	P_IRQ_CLR,
+	P_IRQ_EN,
+	P_TIMER,
+	P_TIMER_CTRL,
+	P_PRDCR_SDBND,
+	P_CNSMR_SDBND,
+	P_EVNT_DEST_ADDR,
+	P_EVNT_DEST_ADDR_MSB,
+	P_EVNT_REG,
+	P_SW_OFSTS,
+	P_DATA_FIFO_ADDR,
+	P_DATA_FIFO_ADDR_MSB,
+	P_DESC_FIFO_ADDR,
+	P_DESC_FIFO_ADDR_MSB,
+	P_EVNT_GEN_TRSHLD,
+	P_FIFO_SIZES,
+	P_IRQ_DEST_ADDR,
+	P_RETR_CNTXT,
+	P_SI_CNTXT,
+	P_DF_CNTXT,
+	P_AU_PSM_CNTXT_1,
+	P_PSM_CNTXT_2,
+	P_PSM_CNTXT_3,
+	P_PSM_CNTXT_3_MSB,
+	P_PSM_CNTXT_4,
+	P_PSM_CNTXT_5,
+	P_TRUST_REG,
+	BAM_MAX_REGS,
+};
+
+static u32 bam_regmap[][BAM_MAX_REGS] = {
+	{ /* LEGACY BAM*/
+			[CTRL] = 0xf80,
+			[REVISION] = 0xf84,
+			[NUM_PIPES] = 0xfbc,
+			[DESC_CNT_TRSHLD] = 0xf88,
+			[IRQ_SRCS] = 0xf8c,
+			[IRQ_SRCS_MSK] = 0xf90,
+			[IRQ_SRCS_UNMASKED] = 0xfb0,
+			[IRQ_STTS] = 0xf94,
+			[IRQ_CLR] = 0xf98,
+			[IRQ_EN] = 0xf9c,
+			[IRQ_SIC_SEL] = 0xfa0,
+			[AHB_MASTER_ERR_CTRLS] = 0xfa4,
+			[AHB_MASTER_ERR_ADDR] = 0xfa8,
+			[AHB_MASTER_ERR_DATA] = 0xfac,
+			[IRQ_DEST] = 0xfb4,
+			[PERIPH_IRQ_DEST] = 0xfb8,
+			[TRUST_REG] = 0xff0,
+			[TEST_BUS_SEL] = 0xff4,
+			[TEST_BUS_REG] = 0xff8,
+			[CNFG_BITS] = 0xffc,
+			[IRQ_SRCS_EE] = 0x1800,
+			[IRQ_SRCS_MSK_EE] = 0x1804,
+			[IRQ_SRCS_UNMASKED_EE] = 0x1808,
+			[P_CTRL] = 0x0,
+			[P_RST] = 0x4,
+			[P_HALT] = 0x8,
+			[P_IRQ_STTS] = 0x10,
+			[P_IRQ_CLR] = 0x14,
+			[P_IRQ_EN] = 0x18,
+			[P_TIMER] = 0x1c,
+			[P_TIMER_CTRL] = 0x20,
+			[P_PRDCR_SDBND] = 0x24,
+			[P_CNSMR_SDBND] = 0x28,
+			[P_EVNT_DEST_ADDR] = 0x102c,
+			[P_EVNT_REG] = 0x1018,
+			[P_SW_OFSTS] = 0x1000,
+			[P_DATA_FIFO_ADDR] = 0x1024,
+			[P_DESC_FIFO_ADDR] = 0x101c,
+			[P_EVNT_GEN_TRSHLD] = 0x1028,
+			[P_FIFO_SIZES] = 0x1020,
+			[P_IRQ_DEST_ADDR] = 0x103c,
+			[P_RETR_CNTXT] = 0x1034,
+			[P_SI_CNTXT] = 0x1038,
+			[P_AU_PSM_CNTXT_1] = 0x1004,
+			[P_PSM_CNTXT_2] = 0x1008,
+			[P_PSM_CNTXT_3] = 0x100c,
+			[P_PSM_CNTXT_4] = 0x1010,
+			[P_PSM_CNTXT_5] = 0x1014,
+			[P_TRUST_REG] = 0x30,
+	},
+	{ /* NDP BAM */
+			[CTRL] = 0x0,
+			[REVISION] = 0x4,
+			[SW_REVISION] = 0x80,
+			[NUM_PIPES] = 0x3c,
+			[TIMER] = 0x40,
+			[TIMER_CTRL] = 0x44,
+			[DESC_CNT_TRSHLD] = 0x8,
+			[IRQ_SRCS] = 0xc,
+			[IRQ_SRCS_MSK] = 0x10,
+			[IRQ_SRCS_UNMASKED] = 0x30,
+			[IRQ_STTS] = 0x14,
+			[IRQ_CLR] = 0x18,
+			[IRQ_EN] = 0x1c,
+			[AHB_MASTER_ERR_CTRLS] = 0x24,
+			[AHB_MASTER_ERR_ADDR] = 0x28,
+			[AHB_MASTER_ERR_ADDR_MSB] = 0x104,
+			[AHB_MASTER_ERR_DATA] = 0x2c,
+			[TRUST_REG] = 0x70,
+			[TEST_BUS_SEL] = 0x74,
+			[TEST_BUS_REG] = 0x78,
+			[CNFG_BITS] = 0x7c,
+			[IRQ_SRCS_EE] = 0x800,
+			[IRQ_SRCS_MSK_EE] = 0x804,
+			[IRQ_SRCS_UNMASKED_EE] = 0x808,
+			[PIPE_ATTR_EE] = 0x80c,
+			[P_CTRL] = 0x1000,
+			[P_RST] = 0x1004,
+			[P_HALT] = 0x1008,
+			[P_IRQ_STTS] = 0x1010,
+			[P_IRQ_CLR] = 0x1014,
+			[P_IRQ_EN] = 0x1018,
+			[P_TIMER] = 0x101c,
+			[P_TIMER_CTRL] = 0x1020,
+			[P_PRDCR_SDBND] = 0x1024,
+			[P_CNSMR_SDBND] = 0x1028,
+			[P_EVNT_DEST_ADDR] = 0x182c,
+			[P_EVNT_DEST_ADDR_MSB] = 0x1934,
+			[P_EVNT_REG] = 0x1818,
+			[P_SW_OFSTS] = 0x1800,
+			[P_DATA_FIFO_ADDR] = 0x1824,
+			[P_DATA_FIFO_ADDR_MSB] = 0x1924,
+			[P_DESC_FIFO_ADDR] = 0x181c,
+			[P_DESC_FIFO_ADDR_MSB] = 0x1914,
+			[P_EVNT_GEN_TRSHLD] = 0x1828,
+			[P_FIFO_SIZES] = 0x1820,
+			[P_RETR_CNTXT] = 0x1834,
+			[P_SI_CNTXT] = 0x1838,
+			[P_DF_CNTXT] = 0x1830,
+			[P_AU_PSM_CNTXT_1] = 0x1804,
+			[P_PSM_CNTXT_2] = 0x1808,
+			[P_PSM_CNTXT_3] = 0x180c,
+			[P_PSM_CNTXT_3_MSB] = 0x1904,
+			[P_PSM_CNTXT_4] = 0x1810,
+			[P_PSM_CNTXT_5] = 0x1814,
+			[P_TRUST_REG] = 0x1030,
+	},
+	{ /* 4K OFFSETs*/
+			[CTRL] = 0x0,
+			[REVISION] = 0x1000,
+			[SW_REVISION] = 0x1004,
+			[NUM_PIPES] = 0x1008,
+			[TIMER] = 0x40,
+			[TIMER_CTRL] = 0x44,
+			[DESC_CNT_TRSHLD] = 0x8,
+			[IRQ_SRCS] = 0x3010,
+			[IRQ_SRCS_MSK] = 0x3014,
+			[IRQ_SRCS_UNMASKED] = 0x3018,
+			[IRQ_STTS] = 0x14,
+			[IRQ_CLR] = 0x18,
+			[IRQ_EN] = 0x1c,
+			[AHB_MASTER_ERR_CTRLS] = 0x1024,
+			[AHB_MASTER_ERR_ADDR] = 0x1028,
+			[AHB_MASTER_ERR_ADDR_MSB] = 0x1104,
+			[AHB_MASTER_ERR_DATA] = 0x102c,
+			[TRUST_REG] = 0x2000,
+			[TEST_BUS_SEL] = 0x1010,
+			[TEST_BUS_REG] = 0x1014,
+			[CNFG_BITS] = 0x7c,
+			[IRQ_SRCS_EE] = 0x3000,
+			[IRQ_SRCS_MSK_EE] = 0x3004,
+			[IRQ_SRCS_UNMASKED_EE] = 0x3008,
+			[PIPE_ATTR_EE] = 0x300c,
+			[P_CTRL] = 0x13000,
+			[P_RST] = 0x13004,
+			[P_HALT] = 0x13008,
+			[P_IRQ_STTS] = 0x13010,
+			[P_IRQ_CLR] = 0x13014,
+			[P_IRQ_EN] = 0x13018,
+			[P_TIMER] = 0x1301c,
+			[P_TIMER_CTRL] = 0x13020,
+			[P_PRDCR_SDBND] = 0x13024,
+			[P_CNSMR_SDBND] = 0x13028,
+			[P_EVNT_DEST_ADDR] = 0x1382c,
+			[P_EVNT_DEST_ADDR_MSB] = 0x13934,
+			[P_EVNT_REG] = 0x13818,
+			[P_SW_OFSTS] = 0x13800,
+			[P_DATA_FIFO_ADDR] = 0x13824,
+			[P_DATA_FIFO_ADDR_MSB] = 0x13924,
+			[P_DESC_FIFO_ADDR] = 0x1381c,
+			[P_DESC_FIFO_ADDR_MSB] = 0x13914,
+			[P_EVNT_GEN_TRSHLD] = 0x13828,
+			[P_FIFO_SIZES] = 0x13820,
+			[P_RETR_CNTXT] = 0x13834,
+			[P_SI_CNTXT] = 0x13838,
+			[P_DF_CNTXT] = 0x13830,
+			[P_AU_PSM_CNTXT_1] = 0x13804,
+			[P_PSM_CNTXT_2] = 0x13808,
+			[P_PSM_CNTXT_3] = 0x1380c,
+			[P_PSM_CNTXT_3_MSB] = 0x13904,
+			[P_PSM_CNTXT_4] = 0x13810,
+			[P_PSM_CNTXT_5] = 0x13814,
+			[P_TRUST_REG] = 0x2020,
+	},
+};
+
+/* AHB buffer error control */
+enum bam_nonsecure_reset {
+	BAM_NONSECURE_RESET_ENABLE  = 0,
+	BAM_NONSECURE_RESET_DISABLE = 1,
+};
+
+static inline u32 bam_get_register_offset(void *base, enum bam_regs reg,
+								u32 param)
+{
+	u32 index = BAM_ERROR, offset = 0;
+	u32 *ptr_reg = bam_regmap[bam_type];
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+
+	if (reg >= CTRL && reg < IRQ_SRCS_EE)
+		index = 0;
+	if (reg >= IRQ_SRCS_EE && reg < P_CTRL)
+		index = (bam_type == SPS_BAM_NDP_4K) ? 0x1000 : 0x80;
+	if (reg >= P_CTRL && reg < P_TRUST_REG) {
+		if (bam_type == SPS_BAM_LEGACY) {
+			if (reg >= P_EVNT_DEST_ADDR)
+				index = 0x40;
+			else
+				index = 0x80;
+		} else
+			index = 0x1000;
+	} else if (reg == P_TRUST_REG) {
+		if (bam_type == SPS_BAM_LEGACY)
+			index = 0x80;
+		else
+			index = (bam_type == SPS_BAM_NDP_4K) ? 0x4 : 0x1000;
+	}
+	if (index < 0) {
+		SPS_ERR(dev, "%s:Failed to find register offset index\n",
+			__func__);
+		return index;
+	}
+
+	offset = *(ptr_reg + reg) + (index * param);
+	return offset;
+}
+
+
+/**
+ *
+ * Read register with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ *
+ * @return u32
+ */
+static inline u32 bam_read_reg(void *base, enum bam_regs reg, u32 param)
+{
+	u32 val, offset = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+	offset = bam_get_register_offset(base, reg, param);
+	if (offset < 0) {
+		SPS_ERR(dev, "%s:Failed to get the register offset\n",
+			__func__);
+		return offset;
+	}
+	val = ioread32(dev->base + offset);
+	SPS_DBG(dev, "sps:bam 0x%p(va) offset 0x%x reg 0x%x r_val 0x%x.\n",
+			dev->base, offset, reg, val);
+	return val;
+}
+
+/**
+ * Read register masked field with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ *
+ * @return u32
+ */
+static inline u32 bam_read_reg_field(void *base, enum bam_regs reg, u32 param,
+								const u32 mask)
+{
+	u32 val, shift, offset = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+	shift = find_first_bit((void *)&mask, 32);
+	offset = bam_get_register_offset(base, reg, param);
+	if (offset < 0) {
+		SPS_ERR(dev, "%s:Failed to get the register offset\n",
+			__func__);
+		return offset;
+	}
+	val = ioread32(dev->base + offset);
+	val &= mask;		/* clear other bits */
+	val >>= shift;
+	SPS_DBG(dev, "sps:bam 0x%p(va) read reg 0x%x mask 0x%x r_val 0x%x.\n",
+			dev->base, offset, mask, val);
+	return val;
+}
+
+/**
+ *
+ * Write register with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @val - value to write.
+ *
+ */
+static inline void bam_write_reg(void *base, enum bam_regs reg,
+						u32 param, u32 val)
+{
+	u32 offset = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	offset = bam_get_register_offset(base, reg, param);
+	if (offset < 0) {
+		SPS_ERR(dev, "%s:Failed to get the register offset\n",
+			__func__);
+		return;
+	}
+	iowrite32(val, dev->base + offset);
+	SPS_DBG(dev, "sps:bam 0x%p(va) write reg 0x%x w_val 0x%x.\n",
+			dev->base, offset, val);
+}
+
+/**
+ * Write register masked field with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ * @val - value to write.
+ *
+ */
+static inline void bam_write_reg_field(void *base, enum bam_regs reg,
+					u32 param, const u32 mask, u32 val)
+{
+	u32 tmp, shift, offset = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	shift = find_first_bit((void *)&mask, 32);
+	offset = bam_get_register_offset(base, reg, param);
+	if (offset < 0) {
+		SPS_ERR(dev, "%s:Failed to get the register offset\n",
+			__func__);
+		return;
+	}
+	tmp = ioread32(dev->base + offset);
+
+	tmp &= ~mask;		/* clear written bits */
+	val = tmp | (val << shift);
+	iowrite32(val, dev->base + offset);
+	SPS_DBG(dev, "sps:bam 0x%p(va) write reg 0x%x w_val 0x%x.\n",
+			dev->base, offset, val);
+}
+
+/**
+ * Initialize a BAM device
+ *
+ */
+int bam_init(void *base, u32 ee,
+		u16 summing_threshold,
+		u32 irq_mask, u32 *version,
+		u32 *num_pipes, u32 options)
+{
+	u32 cfg_bits;
+	u32 ver = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).ee=%d.", __func__,
+			BAM_ID(dev), dev->base, ee);
+
+	ver = bam_read_reg_field(base, REVISION, 0, BAM_REVISION);
+
+	if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) {
+		SPS_ERR(dev, "sps:bam 0x%p(va) Invalid BAM REVISION 0x%x.\n",
+				dev->base, ver);
+		return -ENODEV;
+	}
+
+	SPS_DBG(dev, "sps:REVISION of BAM 0x%p is 0x%x.\n",
+				dev->base, ver);
+
+	if (summing_threshold == 0) {
+		summing_threshold = 4;
+		SPS_ERR(dev,
+			"sps:bam 0x%p(va) summing_threshold is zero,use default 4.\n",
+			dev->base);
+	}
+
+	if (options & SPS_BAM_NO_EXT_P_RST)
+		cfg_bits = 0xffffffff & ~(3 << 11);
+	else
+		cfg_bits = 0xffffffff & ~(1 << 11);
+
+	bam_write_reg_field(base, CTRL, 0, BAM_SW_RST, 1);
+	/* No delay needed */
+	bam_write_reg_field(base, CTRL, 0, BAM_SW_RST, 0);
+
+	bam_write_reg_field(base, CTRL, 0, BAM_EN, 1);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg_field(base, CTRL, 0, CACHE_MISS_ERR_RESP_EN, 0);
+
+	if (options & SPS_BAM_NO_LOCAL_CLK_GATING)
+		bam_write_reg_field(base, CTRL, 0, LOCAL_CLK_GATING, 0);
+	else
+		bam_write_reg_field(base, CTRL, 0, LOCAL_CLK_GATING, 1);
+
+	if (enhd_pipe) {
+		if (options & SPS_BAM_CANCEL_WB)
+			bam_write_reg_field(base, CTRL, 0,
+					BAM_MESS_ONLY_CANCEL_WB, 1);
+		else
+			bam_write_reg_field(base, CTRL, 0,
+					BAM_MESS_ONLY_CANCEL_WB, 0);
+	}
+#endif
+	bam_write_reg(base, DESC_CNT_TRSHLD, 0, summing_threshold);
+
+	bam_write_reg(base, CNFG_BITS, 0, cfg_bits);
+
+	/*
+	 *  Enable Global BAM Interrupt - for error reasons ,
+	 *  filter with mask.
+	 *  Note: Pipes interrupts are disabled until BAM_P_IRQ_enn is set
+	 */
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, BAM_IRQ, 1);
+
+	bam_write_reg(base, IRQ_EN, 0, irq_mask);
+
+	*num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES);
+
+	*version = ver;
+
+	return 0;
+}
+
+/**
+ * Set BAM global execution environment
+ *
+ * @base - BAM virtual base address
+ *
+ * @ee - BAM execution environment index
+ *
+ * @vmid - virtual master identifier
+ *
+ * @reset - enable/disable BAM global software reset
+ */
+static void bam_set_ee(void *base, u32 ee, u32 vmid,
+			enum bam_nonsecure_reset reset)
+{
+	bam_write_reg_field(base, TRUST_REG, 0, BAM_EE, ee);
+	bam_write_reg_field(base, TRUST_REG, 0, BAM_VMID, vmid);
+	bam_write_reg_field(base, TRUST_REG, 0, BAM_RST_BLOCK, reset);
+}
+
+/**
+ * Set the pipe execution environment
+ *
+ * @base - BAM virtual base address
+ *
+ * @pipe - pipe index
+ *
+ * @ee - BAM execution environment index
+ *
+ * @vmid - virtual master identifier
+ */
+static void bam_pipe_set_ee(void *base, u32 pipe, u32 ee, u32 vmid)
+{
+	bam_write_reg_field(base, P_TRUST_REG, pipe, BAM_P_EE, ee);
+	bam_write_reg_field(base, P_TRUST_REG, pipe, BAM_P_VMID, vmid);
+}
+
+/**
+ * Initialize BAM device security execution environment
+ */
+int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask)
+{
+	u32 version;
+	u32 num_pipes;
+	u32 mask;
+	u32 pipe;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).", __func__,
+			BAM_ID(dev), dev->base);
+
+	/*
+	 * Discover the hardware version number and the number of pipes
+	 * supported by this BAM
+	 */
+	version = bam_read_reg_field(base, REVISION, 0, BAM_REVISION);
+	num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES);
+	if (version < 3 || version > 0x1F) {
+		SPS_ERR(dev,
+			"sps:bam 0x%p(va) security is not supported for this BAM version 0x%x.\n",
+			dev->base, version);
+		return -ENODEV;
+	}
+
+	if (num_pipes > BAM_MAX_PIPES) {
+		SPS_ERR(dev,
+		"sps:bam 0x%p(va) the number of pipes is more than the maximum number allowed.\n",
+			dev->base);
+		return -ENODEV;
+	}
+
+	for (pipe = 0, mask = 1; pipe < num_pipes; pipe++, mask <<= 1)
+		if ((mask & pipe_mask) != 0)
+			bam_pipe_set_ee(base, pipe, ee, vmid);
+
+	/* If MSbit is set, assign top-level interrupt to this EE */
+	mask = 1UL << 31;
+	if ((mask & pipe_mask) != 0)
+		bam_set_ee(base, ee, vmid, BAM_NONSECURE_RESET_ENABLE);
+
+	return 0;
+}
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+static inline u32 bam_get_pipe_attr(void *base, u32 ee, bool global)
+{
+	u32 val;
+
+	if (global)
+		val = bam_read_reg_field(base, PIPE_ATTR_EE, ee, BAM_ENABLED);
+	else
+		val = bam_read_reg_field(base, PIPE_ATTR_EE, ee, P_ATTR);
+
+	return val;
+}
+#else
+static inline u32 bam_get_pipe_attr(void *base, u32 ee, bool global)
+{
+	return 0;
+}
+#endif
+
+/**
+ * Verify that a BAM device is enabled and gathers the hardware
+ * configuration.
+ *
+ */
+int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes)
+{
+	u32 ver = 0;
+	u32 enabled = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).",
+			__func__, BAM_ID(dev), dev->base);
+
+	if (!enhd_pipe)
+		enabled = bam_read_reg_field(base, CTRL, 0, BAM_EN);
+	else
+		enabled = bam_get_pipe_attr(base, ee, true);
+
+	if (!enabled) {
+		SPS_ERR(dev, "sps:%s:bam 0x%p(va) is not enabled.\n",
+				__func__, dev->base);
+		return -ENODEV;
+	}
+
+	ver = bam_read_reg(base, REVISION, 0) & BAM_REVISION;
+
+	/*
+	 *  Discover the hardware version number and the number of pipes
+	 *  supported by this BAM
+	 */
+	*num_pipes = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES);
+	*version = ver;
+
+	/* Check BAM version */
+	if ((ver < BAM_MIN_VERSION) || (ver > BAM_MAX_VERSION)) {
+		SPS_ERR(dev, "sps:%s:bam 0x%p(va) Invalid BAM version 0x%x.\n",
+				__func__, dev->base, ver);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * Disable a BAM device
+ *
+ */
+void bam_exit(void *base, u32 ee)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG3(dev, "sps:%s:bam=%pa 0x%p(va).ee=%d.",
+			__func__, BAM_ID(dev), dev->base, ee);
+
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, BAM_IRQ, 0);
+
+	bam_write_reg(base, IRQ_EN, 0, 0);
+
+	/* Disable the BAM */
+	bam_write_reg_field(base, CTRL, 0, BAM_EN, 0);
+}
+
+/**
+ * Output BAM register content
+ * including the TEST_BUS register content under
+ * different TEST_BUS_SEL values.
+ */
+void bam_output_register_content(void *base, u32 ee)
+{
+	u32 num_pipes;
+	u32 i;
+	u32 pipe_attr = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+
+	print_bam_test_bus_reg(base, 0);
+
+	print_bam_selected_reg(base, BAM_MAX_EES);
+
+	num_pipes = bam_read_reg_field(base, NUM_PIPES, 0,
+					BAM_NUM_PIPES);
+	SPS_INFO(dev, "sps:bam %pa 0x%p(va) has %d pipes.",
+			BAM_ID(dev), dev->base, num_pipes);
+
+	pipe_attr = enhd_pipe ?
+		bam_get_pipe_attr(base, ee, false) : 0x0;
+
+	if (!enhd_pipe || !pipe_attr)
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(base, i);
+	else {
+		for (i = 0; i < num_pipes; i++) {
+			if (pipe_attr & (1UL << i))
+				print_bam_pipe_selected_reg(base, i);
+		}
+	}
+}
+
+/**
+ * Get BAM IRQ source and clear global IRQ status
+ */
+u32 bam_check_irq_source(void *base, u32 ee, u32 mask,
+				enum sps_callback_case *cb_case)
+{
+	u32 source = 0, clr = 0;
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+	source = bam_read_reg(base, IRQ_SRCS_EE, ee);
+	clr = source & (1UL << 31);
+
+	if (clr) {
+		u32 status = 0;
+
+		status = bam_read_reg(base, IRQ_STTS, 0);
+
+		if (status & IRQ_STTS_BAM_ERROR_IRQ) {
+			SPS_ERR(dev,
+				"sps:bam %pa 0x%p(va);bam irq status=0x%x.\nsps: BAM_ERROR_IRQ\n",
+				BAM_ID(dev), dev->base, status);
+			bam_output_register_content(base, ee);
+			*cb_case = SPS_CALLBACK_BAM_ERROR_IRQ;
+		} else if (status & IRQ_STTS_BAM_HRESP_ERR_IRQ) {
+			SPS_ERR(dev,
+				"sps:bam %pa 0x%p(va);bam irq status=0x%x.\nsps: BAM_HRESP_ERR_IRQ\n",
+				BAM_ID(dev), dev->base, status);
+			bam_output_register_content(base, ee);
+			*cb_case = SPS_CALLBACK_BAM_HRESP_ERR_IRQ;
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		} else if (status & IRQ_STTS_BAM_TIMER_IRQ) {
+			SPS_DBG1(dev,
+				"sps:bam 0x%p(va);receive BAM_TIMER_IRQ\n",
+					dev->base);
+			*cb_case = SPS_CALLBACK_BAM_TIMER_IRQ;
+#endif
+		} else
+			SPS_INFO(dev,
+				"sps:bam %pa 0x%p(va);bam irq status=0x%x.\n",
+				BAM_ID(dev), dev->base, status);
+
+		bam_write_reg(base, IRQ_CLR, 0, status);
+	}
+
+	source &= (mask|(1UL << 31));
+	return source;
+}
+
+/*
+ * Reset a BAM pipe
+ */
+void bam_pipe_reset(void *base, u32 pipe)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.",
+			__func__, BAM_ID(dev), dev->base, pipe);
+
+	bam_write_reg(base, P_RST, pipe, 1);
+	wmb(); /* ensure pipe is reset */
+	bam_write_reg(base, P_RST, pipe, 0);
+	wmb(); /* ensure pipe reset is de-asserted*/
+}
+
+/*
+ * Disable a BAM pipe
+ */
+void bam_disable_pipe(void *base, u32 pipe)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG2(dev, "sps:%s:bam=0x%p(va).pipe=%d.", __func__, base, pipe);
+	bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0);
+	wmb(); /* ensure pipe is disabled */
+}
+
+/*
+ * Check if the last desc is ZLT
+ */
+bool bam_pipe_check_zlt(void *base, u32 pipe)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return false;
+	}
+
+	if (bam_read_reg_field(base, P_HALT, pipe, P_HALT_P_LAST_DESC_ZLT)) {
+		SPS_DBG(dev,
+			"sps:%s:bam=0x%p(va).pipe=%d: the last desc is ZLT.",
+			__func__, base, pipe);
+		return true;
+	}
+
+	SPS_DBG(dev,
+		"sps:%s:bam=0x%p(va).pipe=%d: the last desc is not ZLT.",
+		__func__, base, pipe);
+	return false;
+}
+
+/*
+ * Check if desc FIFO is empty
+ */
+bool bam_pipe_check_pipe_empty(void *base, u32 pipe)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return false;
+	}
+
+	if (bam_read_reg_field(base, P_HALT, pipe, P_HALT_P_PIPE_EMPTY)) {
+		SPS_DBG(dev,
+			"sps:%s:bam=0x%p(va).pipe=%d: desc FIFO is empty.",
+			__func__, base, pipe);
+		return true;
+	}
+
+	SPS_DBG(dev,
+		"sps:%s:bam=0x%p(va).pipe=%d: desc FIFO is not empty.",
+		__func__, base, pipe);
+	return false;
+}
+
+/**
+ * Initialize a BAM pipe
+ */
+int bam_pipe_init(void *base, u32 pipe,	struct bam_pipe_parameters *param,
+					u32 ee)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return SPS_ERROR;
+	}
+	SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.",
+			__func__, BAM_ID(dev), dev->base, pipe);
+
+	/* Reset the BAM pipe */
+	bam_write_reg(base, P_RST, pipe, 1);
+	/* No delay needed */
+	bam_write_reg(base, P_RST, pipe, 0);
+
+	/* Enable the Pipe Interrupt at the BAM level */
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, (1 << pipe), 1);
+
+	bam_write_reg(base, P_IRQ_EN, pipe, param->pipe_irq_mask);
+
+	bam_write_reg_field(base, P_CTRL, pipe, P_DIRECTION, param->dir);
+	bam_write_reg_field(base, P_CTRL, pipe, P_SYS_MODE, param->mode);
+
+	bam_write_reg(base, P_EVNT_GEN_TRSHLD, pipe, param->event_threshold);
+
+	bam_write_reg(base, P_DESC_FIFO_ADDR, pipe,
+			SPS_GET_LOWER_ADDR(param->desc_base));
+	bam_write_reg_field(base, P_FIFO_SIZES, pipe, P_DESC_FIFO_SIZE,
+			    param->desc_size);
+
+	bam_write_reg_field(base, P_CTRL, pipe, P_SYS_STRM,
+			    param->stream_mode);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	if (SPS_LPAE && SPS_GET_UPPER_ADDR(param->desc_base))
+		bam_write_reg(base, P_DESC_FIFO_ADDR_MSB, pipe,
+				SPS_GET_UPPER_ADDR(param->desc_base));
+
+	bam_write_reg_field(base, P_CTRL, pipe, P_LOCK_GROUP,
+				param->lock_group);
+
+	SPS_DBG(dev, "sps:bam=0x%p(va).pipe=%d.lock_group=%d.\n",
+			dev->base, pipe, param->lock_group);
+#endif
+
+	if (param->mode == BAM_PIPE_MODE_BAM2BAM) {
+		u32 peer_dest_addr = param->peer_phys_addr +
+				      bam_get_register_offset(base, P_EVNT_REG,
+						      param->peer_pipe);
+
+		bam_write_reg(base, P_DATA_FIFO_ADDR, pipe,
+			      SPS_GET_LOWER_ADDR(param->data_base));
+		bam_write_reg_field(base, P_FIFO_SIZES, pipe,
+				    P_DATA_FIFO_SIZE, param->data_size);
+
+		bam_write_reg(base, P_EVNT_DEST_ADDR, pipe, peer_dest_addr);
+
+		SPS_DBG2(dev,
+			"sps:bam=0x%p(va).pipe=%d.peer_bam=0x%x.peer_pipe=%d.\n",
+			dev->base, pipe,
+			(u32) param->peer_phys_addr,
+			param->peer_pipe);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		if (SPS_LPAE && SPS_GET_UPPER_ADDR(param->data_base)) {
+			bam_write_reg(base, P_EVNT_DEST_ADDR_MSB, pipe, 0x0);
+			bam_write_reg(base, P_DATA_FIFO_ADDR_MSB, pipe,
+				      SPS_GET_UPPER_ADDR(param->data_base));
+		}
+
+		bam_write_reg_field(base, P_CTRL, pipe, P_WRITE_NWD,
+					param->write_nwd);
+
+		SPS_DBG(dev, "sps:%s WRITE_NWD bit for this bam2bam pipe.",
+			param->write_nwd ? "Set" : "Do not set");
+#endif
+	}
+
+	/* Pipe Enable - at last */
+	bam_write_reg_field(base, P_CTRL, pipe, P_EN, 1);
+
+	return 0;
+}
+
+/**
+ * Reset the BAM pipe
+ *
+ */
+void bam_pipe_exit(void *base, u32 pipe, u32 ee)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.",
+			__func__, BAM_ID(dev), dev->base, pipe);
+
+	bam_write_reg(base, P_IRQ_EN, pipe, 0);
+
+	/* Disable the Pipe Interrupt at the BAM level */
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, (1 << pipe), 0);
+
+	/* Pipe Disable */
+	bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0);
+}
+
+/**
+ * Enable a BAM pipe
+ *
+ */
+void bam_pipe_enable(void *base, u32 pipe)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.",
+			__func__, BAM_ID(dev), dev->base, pipe);
+
+	if (bam_read_reg_field(base, P_CTRL, pipe, P_EN))
+		SPS_DBG2(dev, "sps:bam=0x%p(va).pipe=%d is already enabled.\n",
+				dev->base, pipe);
+	else
+		bam_write_reg_field(base, P_CTRL, pipe, P_EN, 1);
+}
+
+/**
+ * Diasble a BAM pipe
+ *
+ */
+void bam_pipe_disable(void *base, u32 pipe)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG2(dev, "sps:%s:bam=%pa 0x%p(va).pipe=%d.",
+			__func__, BAM_ID(dev), dev->base, pipe);
+
+	bam_write_reg_field(base, P_CTRL, pipe, P_EN, 0);
+}
+
+/**
+ * Check if a BAM pipe is enabled.
+ *
+ */
+int bam_pipe_is_enabled(void *base, u32 pipe)
+{
+	return bam_read_reg_field(base, P_CTRL, pipe, P_EN);
+}
+
+/**
+ * Configure interrupt for a BAM pipe
+ *
+ */
+void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 ee)
+{
+	struct sps_bam *dev = to_sps_bam_dev(base);
+
+	if ((dev == NULL) || (&dev->base != base)) {
+		SPS_ERR(sps, "%s:Failed to get dev for base addr 0x%p\n",
+				__func__, base);
+		return;
+	}
+	SPS_DBG2(dev,
+		"sps:%s:bam=%pa 0x%p(va).pipe=%d; irq_en:%d; src_mask:0x%x; ee:%d.\n",
+			__func__, BAM_ID(dev), dev->base, pipe,
+			irq_en, src_mask, ee);
+	if (src_mask & BAM_PIPE_IRQ_RST_ERROR) {
+		if (enhd_pipe)
+			bam_write_reg_field(base, IRQ_EN, 0,
+					IRQ_EN_BAM_ERROR_EN, 0);
+		else {
+			src_mask &= ~BAM_PIPE_IRQ_RST_ERROR;
+			SPS_DBG2(dev,
+				"sps:%s:SPS_O_RST_ERROR is not supported\n",
+				__func__);
+		}
+	}
+	if (src_mask & BAM_PIPE_IRQ_HRESP_ERROR) {
+		if (enhd_pipe)
+			bam_write_reg_field(base, IRQ_EN, 0,
+					IRQ_EN_BAM_HRESP_ERR_EN, 0);
+		else {
+			src_mask &= ~BAM_PIPE_IRQ_HRESP_ERROR;
+			SPS_DBG2(dev,
+				"sps:%s:SPS_O_HRESP_ERROR is not supported\n",
+				__func__);
+		}
+	}
+
+	bam_write_reg(base, P_IRQ_EN, pipe, src_mask);
+	bam_write_reg_field(base, IRQ_SRCS_MSK_EE, ee, (1 << pipe), irq_en);
+}
+
+/**
+ * Configure a BAM pipe for satellite MTI use
+ *
+ */
+void bam_pipe_satellite_mti(void *base, u32 pipe, u32 irq_gen_addr, u32 ee)
+{
+	bam_write_reg(base, P_IRQ_EN, pipe, 0);
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg(base, P_IRQ_DEST_ADDR, pipe, irq_gen_addr);
+	bam_write_reg_field(base, IRQ_SIC_SEL, 0, (1 << pipe), 1);
+#endif
+	bam_write_reg_field(base, IRQ_SRCS_MSK, 0, (1 << pipe), 1);
+}
+
+/**
+ * Configure MTI for a BAM pipe
+ *
+ */
+void bam_pipe_set_mti(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 irq_gen_addr)
+{
+	/*
+	 * MTI use is only supported on BAMs when global config is controlled
+	 * by a remote processor.
+	 * Consequently, the global configuration register to enable SIC (MTI)
+	 * support cannot be accessed.
+	 * The remote processor must be relied upon to enable the SIC and the
+	 * interrupt. Since the remote processor enable both SIC and interrupt,
+	 * the interrupt enable mask must be set to zero for polling mode.
+	 */
+#ifndef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_write_reg(base, P_IRQ_DEST_ADDR, pipe, irq_gen_addr);
+#endif
+	if (!irq_en)
+		src_mask = 0;
+
+	bam_write_reg(base, P_IRQ_EN, pipe, src_mask);
+}
+
+/**
+ * Get and Clear BAM pipe IRQ status
+ *
+ */
+u32 bam_pipe_get_and_clear_irq_status(void *base, u32 pipe)
+{
+	u32 status = 0;
+
+	status = bam_read_reg(base, P_IRQ_STTS, pipe);
+	bam_write_reg(base, P_IRQ_CLR, pipe, status);
+
+	return status;
+}
+
+/**
+ * Set write offset for a BAM pipe
+ *
+ */
+void bam_pipe_set_desc_write_offset(void *base, u32 pipe, u32 next_write)
+{
+	/*
+	 * It is not necessary to perform a read-modify-write masking to write
+	 * the P_DESC_FIFO_PEER_OFST value, since the other field in the
+	 * register (P_BYTES_CONSUMED) is read-only.
+	 */
+	bam_write_reg_field(base, P_EVNT_REG, pipe, P_DESC_FIFO_PEER_OFST,
+			    next_write);
+}
+
+/**
+ * Get write offset for a BAM pipe
+ *
+ */
+u32 bam_pipe_get_desc_write_offset(void *base, u32 pipe)
+{
+	return bam_read_reg_field(base, P_EVNT_REG, pipe,
+				  P_DESC_FIFO_PEER_OFST);
+}
+
+/**
+ * Get read offset for a BAM pipe
+ *
+ */
+u32 bam_pipe_get_desc_read_offset(void *base, u32 pipe)
+{
+	return bam_read_reg_field(base, P_SW_OFSTS, pipe, SW_DESC_OFST);
+}
+
+/**
+ * Configure inactivity timer count for a BAM pipe
+ *
+ */
+void bam_pipe_timer_config(void *base, u32 pipe, enum bam_pipe_timer_mode mode,
+			 u32 timeout_count)
+{
+	u32 for_all_pipes = 0;
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	for_all_pipes = bam_read_reg_field(base, REVISION, 0,
+						BAM_NUM_INACTIV_TMRS);
+#endif
+
+	if (for_all_pipes) {
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_MODE, mode);
+		bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_TRSHLD,
+				    timeout_count);
+#endif
+	} else {
+		bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_MODE,
+					mode);
+		bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_TRSHLD,
+				    timeout_count);
+	}
+}
+
+/**
+ * Reset inactivity timer for a BAM pipe
+ *
+ */
+void bam_pipe_timer_reset(void *base, u32 pipe)
+{
+	u32 for_all_pipes = 0;
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	for_all_pipes = bam_read_reg_field(base, REVISION, 0,
+						BAM_NUM_INACTIV_TMRS);
+#endif
+
+	if (for_all_pipes) {
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+		/* reset */
+		bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_RST, 0);
+		/* active */
+		bam_write_reg_field(base, TIMER_CTRL, 0, TIMER_RST, 1);
+#endif
+	} else {
+		/* reset */
+		bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_RST, 0);
+		/* active */
+		bam_write_reg_field(base, P_TIMER_CTRL, pipe, P_TIMER_RST, 1);
+	}
+}
+
+/**
+ * Get inactivity timer count for a BAM pipe
+ *
+ */
+u32 bam_pipe_timer_get_count(void *base, u32 pipe)
+{
+	return bam_read_reg(base, P_TIMER, pipe);
+}
+
+/* halt and un-halt a pipe */
+void bam_pipe_halt(void *base, u32 pipe, bool halt)
+{
+	if (halt)
+		bam_write_reg_field(base, P_HALT, pipe, P_HALT_P_HALT, 1);
+	else
+		bam_write_reg_field(base, P_HALT, pipe, P_HALT_P_HALT, 0);
+}
+
+/* output the content of BAM-level registers */
+void print_bam_reg(void *virt_addr)
+{
+	int i, n, index = 0;
+	u32 *bam = (u32 *) virt_addr;
+	u32 ctrl;
+	u32 ver;
+	u32 pipes;
+	u32 offset = 0;
+
+	if (bam == NULL)
+		return;
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	if (bam_type == SPS_BAM_NDP_4K) {
+		ctrl = bam[0x0 / 4];
+		ver = bam[0x1000 / 4];
+		pipes = bam[0x1008 / 4];
+	} else {
+		ctrl = bam[0x0 / 4];
+		ver = bam[0x4 / 4];
+		pipes = bam[0x3c / 4];
+	}
+#else
+	ctrl = bam[0xf80 / 4];
+	ver = bam[0xf84 / 4];
+	pipes = bam[0xfbc / 4];
+#endif
+
+	SPS_DUMP("%s",
+		"\nsps:<bam-begin> --- Content of BAM-level registers---\n");
+
+	SPS_DUMP("BAM_CTRL: 0x%x.\n", ctrl);
+	SPS_DUMP("BAM_REVISION: 0x%x.\n", ver);
+	SPS_DUMP("NUM_PIPES: 0x%x.\n", pipes);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	if (bam_type == SPS_BAM_NDP_4K)
+		offset = 0x301c;
+	else
+		offset = 0x80;
+	for (i = 0x0; i < offset; i += 0x10)
+
+#else
+	for (i = 0xf80; i < 0x1000; i += 0x10)
+#endif
+		SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	if (bam_type == SPS_BAM_NDP_4K) {
+		offset = 0x3000;
+		index = 0x1000;
+	} else {
+		offset = 0x800;
+		index = 0x80;
+	}
+	for (i = offset, n = 0; n++ < 8; i += index)
+#else
+	for (i = 0x1800, n = 0; n++ < 4; i += 0x80)
+#endif
+		SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+	SPS_DUMP("%s",
+		"\nsps:<bam-begin> --- Content of BAM-level registers ---\n");
+}
+
+/* output the content of BAM pipe registers */
+void print_bam_pipe_reg(void *virt_addr, u32 pipe_index)
+{
+	int i;
+	u32 *bam = (u32 *) virt_addr;
+	u32 pipe = pipe_index;
+	u32 offset = 0;
+
+	if (bam == NULL)
+		return;
+
+	SPS_DUMP("\nsps:<pipe-begin> --- Content of Pipe %d registers ---\n",
+			pipe);
+
+	SPS_DUMP("%s", "-- Pipe Management Registers --\n");
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	if (bam_type == SPS_BAM_NDP_4K)
+		offset = 0x13000;
+	else
+		offset = 0x1000;
+	for (i = offset + 0x1000 * pipe; i < offset + 0x1000 * pipe + 0x80;
+	    i += 0x10)
+#else
+	for (i = 0x0000 + 0x80 * pipe; i < 0x0000 + 0x80 * (pipe + 1);
+	    i += 0x10)
+#endif
+		SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+	SPS_DUMP("%s",
+		"-- Pipe Configuration and Internal State Registers --\n");
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	if (bam_type == SPS_BAM_NDP_4K)
+		offset = 0x13800;
+	else
+		offset = 0x1800;
+	for (i = offset + 0x1000 * pipe; i < offset + 0x1000 * pipe + 0x40;
+		i += 0x10)
+#else
+	for (i = 0x1000 + 0x40 * pipe; i < 0x1000 + 0x40 * (pipe + 1);
+	    i += 0x10)
+#endif
+		SPS_DUMP("bam addr 0x%x: 0x%x,0x%x,0x%x,0x%x.\n", i,
+			bam[i / 4], bam[(i / 4) + 1],
+			bam[(i / 4) + 2], bam[(i / 4) + 3]);
+
+	SPS_DUMP("\nsps:<pipe-end> --- Content of Pipe %d registers ---\n",
+			pipe);
+}
+
+/* output the content of selected BAM-level registers */
+void print_bam_selected_reg(void *virt_addr, u32 ee)
+{
+	void *base = virt_addr;
+
+	u32 bam_ctrl;
+	u32 bam_revision;
+	u32 bam_rev_num;
+	u32 bam_rev_ee_num;
+
+	u32 bam_num_pipes;
+	u32 bam_pipe_num;
+	u32 bam_data_addr_bus_width;
+
+	u32 bam_desc_cnt_trshld;
+	u32 bam_desc_cnt_trd_val;
+
+	u32 bam_irq_en;
+	u32 bam_irq_stts;
+
+	u32 bam_irq_src_ee = 0;
+	u32 bam_irq_msk_ee = 0;
+	u32 bam_irq_unmsk_ee = 0;
+	u32 bam_pipe_attr_ee = 0;
+
+	u32 bam_ahb_err_ctrl;
+	u32 bam_ahb_err_addr;
+	u32 bam_ahb_err_data;
+	u32 bam_cnfg_bits;
+
+	u32 bam_sw_rev = 0;
+	u32 bam_timer = 0;
+	u32 bam_timer_ctrl = 0;
+	u32 bam_ahb_err_addr_msb = 0;
+
+	if (base == NULL)
+		return;
+
+	bam_ctrl = bam_read_reg(base, CTRL, 0);
+	bam_revision = bam_read_reg(base, REVISION, 0);
+	bam_rev_num = bam_read_reg_field(base, REVISION, 0, BAM_REVISION);
+	bam_rev_ee_num = bam_read_reg_field(base, REVISION, 0, BAM_NUM_EES);
+
+	bam_num_pipes = bam_read_reg(base, NUM_PIPES, 0);
+	bam_pipe_num = bam_read_reg_field(base, NUM_PIPES, 0, BAM_NUM_PIPES);
+	bam_data_addr_bus_width = bam_read_reg_field(base, NUM_PIPES, 0,
+					BAM_DATA_ADDR_BUS_WIDTH);
+
+	bam_desc_cnt_trshld = bam_read_reg(base, DESC_CNT_TRSHLD, 0);
+	bam_desc_cnt_trd_val = bam_read_reg_field(base, DESC_CNT_TRSHLD, 0,
+					BAM_DESC_CNT_TRSHLD);
+
+	bam_irq_en = bam_read_reg(base, IRQ_EN, 0);
+	bam_irq_stts = bam_read_reg(base, IRQ_STTS, 0);
+
+	if (ee < BAM_MAX_EES) {
+		bam_irq_src_ee = bam_read_reg(base, IRQ_SRCS_EE, ee);
+		bam_irq_msk_ee = bam_read_reg(base, IRQ_SRCS_MSK_EE, ee);
+		bam_irq_unmsk_ee = bam_read_reg(base, IRQ_SRCS_UNMASKED_EE, ee);
+	}
+
+	bam_ahb_err_ctrl = bam_read_reg(base, AHB_MASTER_ERR_CTRLS, 0);
+	bam_ahb_err_addr = bam_read_reg(base, AHB_MASTER_ERR_ADDR, 0);
+	bam_ahb_err_data = bam_read_reg(base, AHB_MASTER_ERR_DATA, 0);
+	bam_cnfg_bits = bam_read_reg(base, CNFG_BITS, 0);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	bam_sw_rev = bam_read_reg(base, SW_REVISION, 0);
+	bam_timer = bam_read_reg(base, TIMER, 0);
+	bam_timer_ctrl = bam_read_reg(base, TIMER_CTRL, 0);
+	bam_ahb_err_addr_msb = SPS_LPAE ?
+		bam_read_reg(base, AHB_MASTER_ERR_ADDR_MSB, 0) : 0;
+	if (ee < BAM_MAX_EES)
+		bam_pipe_attr_ee = enhd_pipe ?
+			bam_read_reg(base, PIPE_ATTR_EE, ee) : 0x0;
+#endif
+
+
+	SPS_DUMP("%s", "\nsps:<bam-begin> --- BAM-level registers ---\n\n");
+
+	SPS_DUMP("BAM_CTRL: 0x%x\n", bam_ctrl);
+	SPS_DUMP("BAM_REVISION: 0x%x\n", bam_revision);
+	SPS_DUMP("    REVISION: 0x%x\n", bam_rev_num);
+	SPS_DUMP("    NUM_EES: %d\n", bam_rev_ee_num);
+	SPS_DUMP("BAM_SW_REVISION: 0x%x\n", bam_sw_rev);
+	SPS_DUMP("BAM_NUM_PIPES: %d\n", bam_num_pipes);
+	SPS_DUMP("BAM_DATA_ADDR_BUS_WIDTH: %d\n",
+			((bam_data_addr_bus_width == 0x0) ? 32 : 36));
+	SPS_DUMP("    NUM_PIPES: %d\n", bam_pipe_num);
+	SPS_DUMP("BAM_DESC_CNT_TRSHLD: 0x%x\n", bam_desc_cnt_trshld);
+	SPS_DUMP("    DESC_CNT_TRSHLD: 0x%x (%d)\n", bam_desc_cnt_trd_val,
+			bam_desc_cnt_trd_val);
+
+	SPS_DUMP("BAM_IRQ_EN: 0x%x\n", bam_irq_en);
+	SPS_DUMP("BAM_IRQ_STTS: 0x%x\n", bam_irq_stts);
+
+	if (ee < BAM_MAX_EES) {
+		SPS_DUMP("BAM_IRQ_SRCS_EE(%d): 0x%x\n", ee, bam_irq_src_ee);
+		SPS_DUMP("BAM_IRQ_SRCS_MSK_EE(%d): 0x%x\n", ee, bam_irq_msk_ee);
+		SPS_DUMP("BAM_IRQ_SRCS_UNMASKED_EE(%d): 0x%x\n", ee,
+				bam_irq_unmsk_ee);
+		SPS_DUMP("BAM_PIPE_ATTR_EE(%d): 0x%x\n", ee, bam_pipe_attr_ee);
+	}
+
+	SPS_DUMP("BAM_AHB_MASTER_ERR_CTRLS: 0x%x\n", bam_ahb_err_ctrl);
+	SPS_DUMP("BAM_AHB_MASTER_ERR_ADDR: 0x%x\n", bam_ahb_err_addr);
+	SPS_DUMP("BAM_AHB_MASTER_ERR_ADDR_MSB: 0x%x\n", bam_ahb_err_addr_msb);
+	SPS_DUMP("BAM_AHB_MASTER_ERR_DATA: 0x%x\n", bam_ahb_err_data);
+
+	SPS_DUMP("BAM_CNFG_BITS: 0x%x\n", bam_cnfg_bits);
+	SPS_DUMP("BAM_TIMER: 0x%x\n", bam_timer);
+	SPS_DUMP("BAM_TIMER_CTRL: 0x%x\n", bam_timer_ctrl);
+
+	SPS_DUMP("%s", "\nsps:<bam-end> --- BAM-level registers ---\n\n");
+}
+
+/* output the content of selected BAM pipe registers */
+void print_bam_pipe_selected_reg(void *virt_addr, u32 pipe_index)
+{
+	void *base = virt_addr;
+	u32 pipe = pipe_index;
+
+	u32 p_ctrl;
+	u32 p_sys_mode;
+	u32 p_direction;
+	u32 p_lock_group = 0;
+
+	u32 p_irq_en;
+	u32 p_irq_stts;
+	u32 p_irq_stts_eot;
+	u32 p_irq_stts_int;
+
+	u32 p_prd_sdbd;
+	u32 p_bytes_free;
+	u32 p_prd_ctrl;
+	u32 p_prd_toggle;
+	u32 p_prd_sb_updated;
+
+	u32 p_con_sdbd;
+	u32 p_bytes_avail;
+	u32 p_con_ctrl;
+	u32 p_con_toggle;
+	u32 p_con_ack_toggle;
+	u32 p_con_ack_toggle_r;
+	u32 p_con_wait_4_ack;
+	u32 p_con_sb_updated;
+
+	u32 p_sw_offset;
+	u32 p_read_pointer;
+	u32 p_evnt_reg;
+	u32 p_write_pointer;
+
+	u32 p_evnt_dest;
+	u32 p_evnt_dest_msb = 0;
+	u32 p_desc_fifo_addr;
+	u32 p_desc_fifo_addr_msb = 0;
+	u32 p_desc_fifo_size;
+	u32 p_data_fifo_addr;
+	u32 p_data_fifo_addr_msb = 0;
+	u32 p_data_fifo_size;
+	u32 p_fifo_sizes;
+
+	u32 p_evnt_trd;
+	u32 p_evnt_trd_val;
+
+	u32 p_retr_ct;
+	u32 p_retr_offset;
+	u32 p_si_ct;
+	u32 p_si_offset;
+	u32 p_df_ct = 0;
+	u32 p_df_offset = 0;
+	u32 p_au_ct1;
+	u32 p_psm_ct2;
+	u32 p_psm_ct3;
+	u32 p_psm_ct3_msb = 0;
+	u32 p_psm_ct4;
+	u32 p_psm_ct5;
+
+	u32 p_timer;
+	u32 p_timer_ctrl;
+
+	if (base == NULL)
+		return;
+
+	p_ctrl = bam_read_reg(base, P_CTRL, pipe);
+	p_sys_mode = bam_read_reg_field(base, P_CTRL, pipe, P_SYS_MODE);
+	p_direction = bam_read_reg_field(base, P_CTRL, pipe, P_DIRECTION);
+
+	p_irq_en = bam_read_reg(base, P_IRQ_EN, pipe);
+	p_irq_stts = bam_read_reg(base, P_IRQ_STTS, pipe);
+	p_irq_stts_eot = bam_read_reg_field(base, P_IRQ_STTS, pipe,
+					P_IRQ_STTS_P_TRNSFR_END_IRQ);
+	p_irq_stts_int = bam_read_reg_field(base, P_IRQ_STTS, pipe,
+					P_IRQ_STTS_P_PRCSD_DESC_IRQ);
+
+	p_prd_sdbd = bam_read_reg(base, P_PRDCR_SDBND, pipe);
+	p_bytes_free = bam_read_reg_field(base, P_PRDCR_SDBND, pipe,
+					P_PRDCR_SDBNDn_BAM_P_BYTES_FREE);
+	p_prd_ctrl = bam_read_reg_field(base, P_PRDCR_SDBND, pipe,
+					P_PRDCR_SDBNDn_BAM_P_CTRL);
+	p_prd_toggle = bam_read_reg_field(base, P_PRDCR_SDBND, pipe,
+					P_PRDCR_SDBNDn_BAM_P_TOGGLE);
+	p_prd_sb_updated = bam_read_reg_field(base, P_PRDCR_SDBND, pipe,
+					P_PRDCR_SDBNDn_BAM_P_SB_UPDATED);
+	p_con_sdbd = bam_read_reg(base, P_CNSMR_SDBND, pipe);
+	p_bytes_avail = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_BYTES_AVAIL);
+	p_con_ctrl = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_CTRL);
+	p_con_toggle = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_TOGGLE);
+	p_con_ack_toggle = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE);
+	p_con_ack_toggle_r = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_ACK_TOGGLE_R);
+	p_con_wait_4_ack = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_WAIT_4_ACK);
+	p_con_sb_updated = bam_read_reg_field(base, P_CNSMR_SDBND, pipe,
+					P_CNSMR_SDBNDn_BAM_P_SB_UPDATED);
+
+	p_sw_offset = bam_read_reg(base, P_SW_OFSTS, pipe);
+	p_read_pointer = bam_read_reg_field(base, P_SW_OFSTS, pipe,
+						SW_DESC_OFST);
+	p_evnt_reg = bam_read_reg(base, P_EVNT_REG, pipe);
+	p_write_pointer = bam_read_reg_field(base, P_EVNT_REG, pipe,
+						P_DESC_FIFO_PEER_OFST);
+
+	p_evnt_dest = bam_read_reg(base, P_EVNT_DEST_ADDR, pipe);
+	p_desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR, pipe);
+	p_desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe,
+						P_DESC_FIFO_SIZE);
+	p_data_fifo_addr = bam_read_reg(base, P_DATA_FIFO_ADDR, pipe);
+	p_data_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe,
+						P_DATA_FIFO_SIZE);
+	p_fifo_sizes = bam_read_reg(base, P_FIFO_SIZES, pipe);
+
+	p_evnt_trd = bam_read_reg(base, P_EVNT_GEN_TRSHLD, pipe);
+	p_evnt_trd_val = bam_read_reg_field(base, P_EVNT_GEN_TRSHLD, pipe,
+					P_EVNT_GEN_TRSHLD_P_TRSHLD);
+
+	p_retr_ct = bam_read_reg(base, P_RETR_CNTXT, pipe);
+	p_retr_offset = bam_read_reg_field(base, P_RETR_CNTXT, pipe,
+					P_RETR_CNTXT_RETR_DESC_OFST);
+	p_si_ct = bam_read_reg(base, P_SI_CNTXT, pipe);
+	p_si_offset = bam_read_reg_field(base, P_SI_CNTXT, pipe,
+					P_SI_CNTXT_SI_DESC_OFST);
+	p_au_ct1 = bam_read_reg(base, P_AU_PSM_CNTXT_1, pipe);
+	p_psm_ct2 = bam_read_reg(base, P_PSM_CNTXT_2, pipe);
+	p_psm_ct3 = bam_read_reg(base, P_PSM_CNTXT_3, pipe);
+	p_psm_ct4 = bam_read_reg(base, P_PSM_CNTXT_4, pipe);
+	p_psm_ct5 = bam_read_reg(base, P_PSM_CNTXT_5, pipe);
+
+	p_timer = bam_read_reg(base, P_TIMER, pipe);
+	p_timer_ctrl = bam_read_reg(base, P_TIMER_CTRL, pipe);
+
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+	p_evnt_dest_msb = SPS_LPAE ?
+		bam_read_reg(base, P_EVNT_DEST_ADDR_MSB, pipe) : 0;
+
+	p_desc_fifo_addr_msb = SPS_LPAE ?
+		bam_read_reg(base, P_DESC_FIFO_ADDR_MSB, pipe) : 0;
+	p_data_fifo_addr_msb = SPS_LPAE ?
+		bam_read_reg(base, P_DATA_FIFO_ADDR_MSB, pipe) : 0;
+
+	p_psm_ct3_msb = SPS_LPAE ? bam_read_reg(base, P_PSM_CNTXT_3, pipe) : 0;
+	p_lock_group = bam_read_reg_field(base, P_CTRL, pipe, P_LOCK_GROUP);
+	p_df_ct = bam_read_reg(base, P_DF_CNTXT, pipe);
+	p_df_offset = bam_read_reg_field(base, P_DF_CNTXT, pipe,
+					P_DF_CNTXT_DF_DESC_OFST);
+#endif
+
+	SPS_DUMP("\nsps:<pipe-begin> --- Registers of Pipe %d ---\n\n", pipe);
+
+	SPS_DUMP("BAM_P_CTRL: 0x%x\n", p_ctrl);
+	SPS_DUMP("    SYS_MODE: %d\n", p_sys_mode);
+	if (p_direction)
+		SPS_DUMP("    DIRECTION:%d->Producer\n", p_direction);
+	else
+		SPS_DUMP("    DIRECTION:%d->Consumer\n", p_direction);
+	SPS_DUMP("    LOCK_GROUP: 0x%x (%d)\n", p_lock_group, p_lock_group);
+
+	SPS_DUMP("BAM_P_IRQ_EN: 0x%x\n", p_irq_en);
+	SPS_DUMP("BAM_P_IRQ_STTS: 0x%x\n", p_irq_stts);
+	SPS_DUMP("    TRNSFR_END_IRQ(EOT): 0x%x\n", p_irq_stts_eot);
+	SPS_DUMP("    PRCSD_DESC_IRQ(INT): 0x%x\n", p_irq_stts_int);
+
+	SPS_DUMP("BAM_P_PRDCR_SDBND: 0x%x\n", p_prd_sdbd);
+	SPS_DUMP("    BYTES_FREE: 0x%x (%d)\n", p_bytes_free, p_bytes_free);
+	SPS_DUMP("    CTRL: 0x%x\n", p_prd_ctrl);
+	SPS_DUMP("    TOGGLE: %d\n", p_prd_toggle);
+	SPS_DUMP("    SB_UPDATED: %d\n", p_prd_sb_updated);
+	SPS_DUMP("BAM_P_CNSMR_SDBND: 0x%x\n", p_con_sdbd);
+	SPS_DUMP("    WAIT_4_ACK: %d\n", p_con_wait_4_ack);
+	SPS_DUMP("    BYTES_AVAIL: 0x%x (%d)\n", p_bytes_avail, p_bytes_avail);
+	SPS_DUMP("    CTRL: 0x%x\n", p_con_ctrl);
+	SPS_DUMP("    TOGGLE: %d\n", p_con_toggle);
+	SPS_DUMP("    ACK_TOGGLE: %d\n", p_con_ack_toggle);
+	SPS_DUMP("    ACK_TOGGLE_R: %d\n", p_con_ack_toggle_r);
+	SPS_DUMP("    SB_UPDATED: %d\n", p_con_sb_updated);
+
+	SPS_DUMP("BAM_P_SW_DESC_OFST: 0x%x\n", p_sw_offset);
+	SPS_DUMP("    SW_DESC_OFST: 0x%x\n", p_read_pointer);
+	SPS_DUMP("BAM_P_EVNT_REG: 0x%x\n", p_evnt_reg);
+	SPS_DUMP("    DESC_FIFO_PEER_OFST: 0x%x\n", p_write_pointer);
+
+	SPS_DUMP("BAM_P_RETR_CNTXT: 0x%x\n", p_retr_ct);
+	SPS_DUMP("    RETR_OFFSET: 0x%x\n", p_retr_offset);
+	SPS_DUMP("BAM_P_SI_CNTXT: 0x%x\n", p_si_ct);
+	SPS_DUMP("    SI_OFFSET: 0x%x\n", p_si_offset);
+	SPS_DUMP("BAM_P_DF_CNTXT: 0x%x\n", p_df_ct);
+	SPS_DUMP("    DF_OFFSET: 0x%x\n", p_df_offset);
+
+	SPS_DUMP("BAM_P_DESC_FIFO_ADDR: 0x%x\n", p_desc_fifo_addr);
+	SPS_DUMP("BAM_P_DESC_FIFO_ADDR_MSB: 0x%x\n", p_desc_fifo_addr_msb);
+	SPS_DUMP("BAM_P_DATA_FIFO_ADDR: 0x%x\n", p_data_fifo_addr);
+	SPS_DUMP("BAM_P_DATA_FIFO_ADDR_MSB: 0x%x\n", p_data_fifo_addr_msb);
+	SPS_DUMP("BAM_P_FIFO_SIZES: 0x%x\n", p_fifo_sizes);
+	SPS_DUMP("    DESC_FIFO_SIZE: 0x%x (%d)\n", p_desc_fifo_size,
+							p_desc_fifo_size);
+	SPS_DUMP("    DATA_FIFO_SIZE: 0x%x (%d)\n", p_data_fifo_size,
+							p_data_fifo_size);
+
+	SPS_DUMP("BAM_P_EVNT_DEST_ADDR: 0x%x\n", p_evnt_dest);
+	SPS_DUMP("BAM_P_EVNT_DEST_ADDR_MSB: 0x%x\n", p_evnt_dest_msb);
+	SPS_DUMP("BAM_P_EVNT_GEN_TRSHLD: 0x%x\n", p_evnt_trd);
+	SPS_DUMP("    EVNT_GEN_TRSHLD: 0x%x (%d)\n", p_evnt_trd_val,
+							p_evnt_trd_val);
+
+	SPS_DUMP("BAM_P_AU_PSM_CNTXT_1: 0x%x\n", p_au_ct1);
+	SPS_DUMP("BAM_P_PSM_CNTXT_2: 0x%x\n", p_psm_ct2);
+	SPS_DUMP("BAM_P_PSM_CNTXT_3: 0x%x\n", p_psm_ct3);
+	SPS_DUMP("BAM_P_PSM_CNTXT_3_MSB: 0x%x\n", p_psm_ct3_msb);
+	SPS_DUMP("BAM_P_PSM_CNTXT_4: 0x%x\n", p_psm_ct4);
+	SPS_DUMP("BAM_P_PSM_CNTXT_5: 0x%x\n", p_psm_ct5);
+	SPS_DUMP("BAM_P_TIMER: 0x%x\n", p_timer);
+	SPS_DUMP("BAM_P_TIMER_CTRL: 0x%x\n", p_timer_ctrl);
+
+	SPS_DUMP("\nsps:<pipe-end> --- Registers of Pipe %d ---\n\n", pipe);
+}
+
+/* output descriptor FIFO of a pipe */
+void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index, u32 option)
+{
+	void *base = virt_addr;
+	u32 pipe = pipe_index;
+	u32 desc_fifo_addr;
+	u32 desc_fifo_size;
+	u32 *desc_fifo;
+	int i;
+	char desc_info[MAX_MSG_LEN];
+
+	if (base == NULL)
+		return;
+
+	desc_fifo_addr = bam_read_reg(base, P_DESC_FIFO_ADDR, pipe);
+	desc_fifo_size = bam_read_reg_field(base, P_FIFO_SIZES, pipe,
+						P_DESC_FIFO_SIZE);
+
+	if (desc_fifo_addr == 0) {
+		SPS_ERR(sps, "sps:%s:desc FIFO address of Pipe %d is NULL.\n",
+			__func__, pipe);
+		return;
+	} else if (desc_fifo_size == 0) {
+		SPS_ERR(sps, "sps:%s:desc FIFO size of Pipe %d is 0.\n",
+			__func__, pipe);
+		return;
+	}
+
+	SPS_DUMP("\nsps:<desc-begin> --- descriptor FIFO of Pipe %d -----\n\n",
+			pipe);
+
+	SPS_DUMP("BAM_P_DESC_FIFO_ADDR: 0x%x\n"
+		"BAM_P_DESC_FIFO_SIZE: 0x%x (%d)\n\n",
+		desc_fifo_addr, desc_fifo_size, desc_fifo_size);
+
+	desc_fifo = (u32 *) phys_to_virt(desc_fifo_addr);
+
+	if (option == 100) {
+		SPS_DUMP("%s",
+			"----- start of data blocks -----\n");
+		for (i = 0; i < desc_fifo_size; i += 8) {
+			u32 *data_block_vir;
+			u32 data_block_phy = desc_fifo[i / 4];
+
+			if (data_block_phy) {
+				data_block_vir =
+					(u32 *) phys_to_virt(data_block_phy);
+
+				SPS_DUMP("desc addr:0x%x; data addr:0x%x:\n",
+					desc_fifo_addr + i, data_block_phy);
+				SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n",
+					data_block_vir[0], data_block_vir[1],
+					data_block_vir[2], data_block_vir[3]);
+				SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n",
+					data_block_vir[4], data_block_vir[5],
+					data_block_vir[6], data_block_vir[7]);
+				SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n",
+					data_block_vir[8], data_block_vir[9],
+					data_block_vir[10], data_block_vir[11]);
+				SPS_DUMP("0x%x, 0x%x, 0x%x, 0x%x\n\n",
+					data_block_vir[12], data_block_vir[13],
+					data_block_vir[14], data_block_vir[15]);
+			}
+		}
+		SPS_DUMP("%s",
+			"----- end of data blocks -----\n");
+	} else if (option) {
+		u32 size = option * 128;
+		u32 current_desc = bam_pipe_get_desc_read_offset(base,
+								pipe_index);
+		u32 begin = 0;
+		u32 end = desc_fifo_size;
+
+		if (current_desc > size / 2)
+			begin = current_desc - size / 2;
+
+		if (desc_fifo_size > current_desc + size / 2)
+			end = current_desc + size / 2;
+
+		SPS_DUMP("%s",
+			"------------ begin of partial FIFO ------------\n\n");
+
+		SPS_DUMP("%s",
+			"desc addr; desc content; desc flags\n");
+		for (i = begin; i < end; i += 0x8) {
+			u32 offset;
+			u32 flags = desc_fifo[(i / 4) + 1] >> 16;
+
+			memset(desc_info, 0, sizeof(desc_info));
+			offset = scnprintf(desc_info, 40, "0x%x: 0x%x, 0x%x: ",
+				desc_fifo_addr + i,
+				desc_fifo[i / 4], desc_fifo[(i / 4) + 1]);
+
+			if (flags & SPS_IOVEC_FLAG_INT)
+				offset += scnprintf(desc_info + offset, 5,
+							"INT ");
+			if (flags & SPS_IOVEC_FLAG_EOT)
+				offset += scnprintf(desc_info + offset, 5,
+							"EOT ");
+			if (flags & SPS_IOVEC_FLAG_EOB)
+				offset += scnprintf(desc_info + offset, 5,
+							"EOB ");
+			if (flags & SPS_IOVEC_FLAG_NWD)
+				offset += scnprintf(desc_info + offset, 5,
+							"NWD ");
+			if (flags & SPS_IOVEC_FLAG_CMD)
+				offset += scnprintf(desc_info + offset, 5,
+							"CMD ");
+			if (flags & SPS_IOVEC_FLAG_LOCK)
+				offset += scnprintf(desc_info + offset, 5,
+							"LCK ");
+			if (flags & SPS_IOVEC_FLAG_UNLOCK)
+				offset += scnprintf(desc_info + offset, 5,
+							"UNL ");
+			if (flags & SPS_IOVEC_FLAG_IMME)
+				offset += scnprintf(desc_info + offset, 5,
+							"IMM ");
+
+			SPS_DUMP("%s\n", desc_info);
+		}
+
+		SPS_DUMP("%s",
+			"\n------------  end of partial FIFO  ------------\n");
+	} else {
+		SPS_DUMP("%s",
+			"---------------- begin of FIFO ----------------\n\n");
+
+		for (i = 0; i < desc_fifo_size; i += 0x10)
+			SPS_DUMP("addr 0x%x: 0x%x, 0x%x, 0x%x, 0x%x.\n",
+				desc_fifo_addr + i,
+				desc_fifo[i / 4], desc_fifo[(i / 4) + 1],
+				desc_fifo[(i / 4) + 2], desc_fifo[(i / 4) + 3]);
+
+		SPS_DUMP("%s",
+			"\n----------------  end of FIFO  ----------------\n");
+	}
+
+	SPS_DUMP("\nsps:<desc-end> --- descriptor FIFO of Pipe %d -----\n\n",
+			pipe);
+}
+
+/* output BAM_TEST_BUS_REG with specified TEST_BUS_SEL */
+void print_bam_test_bus_reg(void *base, u32 tb_sel)
+{
+	u32 i;
+	u32 test_bus_selection[] = {0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
+			0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
+			0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+			0x20, 0x21, 0x22, 0x23,
+			0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
+	u32 size = sizeof(test_bus_selection) / sizeof(u32);
+
+	if (base == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is NULL.\n", __func__);
+		return;
+	}
+
+	if (tb_sel) {
+		SPS_DUMP("\nsps:Specified TEST_BUS_SEL value: 0x%x\n", tb_sel);
+		bam_write_reg_field(base, TEST_BUS_SEL, 0, BAM_TESTBUS_SEL,
+					tb_sel);
+		SPS_DUMP("sps:BAM_TEST_BUS_REG:0x%x for TEST_BUS_SEL:0x%x\n\n",
+			bam_read_reg(base, TEST_BUS_REG, 0),
+			bam_read_reg_field(base, TEST_BUS_SEL, 0,
+						BAM_TESTBUS_SEL));
+	}
+
+	SPS_DUMP("%s", "\nsps:<testbus-begin> --- BAM TEST_BUS dump -----\n\n");
+
+	/* output other selections */
+	for (i = 0; i < size; i++) {
+		bam_write_reg_field(base, TEST_BUS_SEL, 0, BAM_TESTBUS_SEL,
+					test_bus_selection[i]);
+
+		SPS_DUMP("sps:TEST_BUS_REG:0x%x\t  TEST_BUS_SEL:0x%x\n",
+			bam_read_reg(base, TEST_BUS_REG, 0),
+			bam_read_reg_field(base, TEST_BUS_SEL, 0,
+					BAM_TESTBUS_SEL));
+	}
+
+	SPS_DUMP("%s", "\nsps:<testbus-end> --- BAM TEST_BUS dump -----\n\n");
+}
diff --git a/drivers/platform/msm/sps/bam.h b/drivers/platform/msm/sps/bam.h
new file mode 100644
index 0000000..1d8aab0
--- /dev/null
+++ b/drivers/platform/msm/sps/bam.h
@@ -0,0 +1,447 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Bus-Access-Manager (BAM) Hardware manager functions API. */
+
+#ifndef _BAM_H_
+#define _BAM_H_
+
+#include <linux/types.h>	/* u32 */
+#include <linux/io.h>		/* ioread32() */
+#include <linux/bitops.h>	/* find_first_bit() */
+#include "spsi.h"
+
+/* Pipe mode */
+enum bam_pipe_mode {
+	BAM_PIPE_MODE_BAM2BAM = 0,	/* BAM to BAM */
+	BAM_PIPE_MODE_SYSTEM = 1,	/* BAM to/from System Memory */
+};
+
+/* Pipe direction */
+enum bam_pipe_dir {
+	/* The Pipe Reads data from data-fifo or system-memory */
+	BAM_PIPE_CONSUMER = 0,
+	/* The Pipe Writes data to data-fifo or system-memory */
+	BAM_PIPE_PRODUCER = 1,
+};
+
+/* Stream mode Type */
+enum bam_stream_mode {
+	BAM_STREAM_MODE_DISABLE = 0,
+	BAM_STREAM_MODE_ENABLE = 1,
+};
+
+/* NWD written Type */
+enum bam_write_nwd {
+	BAM_WRITE_NWD_DISABLE = 0,
+	BAM_WRITE_NWD_ENABLE = 1,
+};
+
+
+/* Enable Type */
+enum bam_enable {
+	BAM_DISABLE = 0,
+	BAM_ENABLE = 1,
+};
+
+/* Pipe timer mode */
+enum bam_pipe_timer_mode {
+	BAM_PIPE_TIMER_ONESHOT = 0,
+	BAM_PIPE_TIMER_PERIODIC = 1,
+};
+
+struct transfer_descriptor {
+	u32 addr;	/* Buffer physical address */
+	u32 size:16;	/* Buffer size in bytes */
+	u32 flags:16;	/* Flag bitmask (see SPS_IOVEC_FLAG_ #defines) */
+}  __packed;
+
+/* BAM pipe initialization parameters */
+struct bam_pipe_parameters {
+	u16 event_threshold;
+	u32 pipe_irq_mask;
+	enum bam_pipe_dir dir;
+	enum bam_pipe_mode mode;
+	enum bam_write_nwd write_nwd;
+	phys_addr_t desc_base;	/* Physical address of descriptor FIFO */
+	u32 desc_size;	/* Size (bytes) of descriptor FIFO */
+	u32 lock_group;	/* The lock group this pipe belongs to */
+	enum bam_stream_mode stream_mode;
+	u32 ee;		/* BAM execution environment index */
+
+	/* The following are only valid if mode is BAM2BAM */
+	u32 peer_phys_addr;
+	u32 peer_pipe;
+	phys_addr_t data_base;	/* Physical address of data FIFO */
+	u32 data_size;	/* Size (bytes) of data FIFO */
+};
+
+/**
+ * Initialize a BAM device
+ *
+ * This function initializes a BAM device.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @summing_threshold - summing threshold (global for all pipes)
+ *
+ * @irq_mask - error interrupts mask
+ *
+ * @version - return BAM hardware version
+ *
+ * @num_pipes - return number of pipes
+ *
+ * @options - BAM configuration options
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_init(void *base,
+		u32 ee,
+		u16 summing_threshold,
+		u32 irq_mask, u32 *version,
+		u32 *num_pipes, u32 options);
+
+/**
+ * Initialize BAM device security execution environment
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @vmid - virtual master identifier
+ *
+ * @pipe_mask - bit mask of pipes to assign to EE
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_security_init(void *base, u32 ee, u32 vmid, u32 pipe_mask);
+
+/**
+ * Check a BAM device
+ *
+ * This function verifies that a BAM device is enabled and gathers
+ *    the hardware configuration.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @version - return BAM hardware version
+ *
+ * @ee - BAM execution environment index
+ *
+ * @num_pipes - return number of pipes
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_check(void *base, u32 *version, u32 ee, u32 *num_pipes);
+
+/**
+ * Disable a BAM device
+ *
+ * This function disables a BAM device.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_exit(void *base, u32 ee);
+
+/**
+ * This function prints BAM register content
+ * including TEST_BUS and PIPE register content.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ */
+void bam_output_register_content(void *base, u32 ee);
+
+
+/**
+ * Get BAM IRQ source and clear global IRQ status
+ *
+ * This function gets BAM IRQ source.
+ * Clear global IRQ status if it is non-zero.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @mask - active pipes mask.
+ *
+ * @case - callback case.
+ *
+ * @return IRQ status
+ *
+ */
+u32 bam_check_irq_source(void *base, u32 ee, u32 mask,
+				enum sps_callback_case *cb_case);
+
+
+/**
+ * Initialize a BAM pipe
+ *
+ * This function initializes a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @param - bam pipe parameters.
+ *
+ * @ee - BAM execution environment index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int bam_pipe_init(void *base, u32 pipe, struct bam_pipe_parameters *param,
+					u32 ee);
+
+/**
+ * Reset the BAM pipe
+ *
+ * This function resets the BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_pipe_exit(void *base, u32 pipe, u32 ee);
+
+/**
+ * Enable a BAM pipe
+ *
+ * This function enables a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ */
+void bam_pipe_enable(void *base, u32 pipe);
+
+/**
+ * Disable a BAM pipe
+ *
+ * This function disables a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ */
+void bam_pipe_disable(void *base, u32 pipe);
+
+/**
+ * Get a BAM pipe enable state
+ *
+ * This function determines if a BAM pipe is enabled.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return true if enabled, false if disabled
+ *
+ */
+int bam_pipe_is_enabled(void *base, u32 pipe);
+
+/**
+ * Configure interrupt for a BAM pipe
+ *
+ * This function configures the interrupt for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @irq_en - enable or disable interrupt
+ *
+ * @src_mask - interrupt source mask, set regardless of whether
+ *    interrupt is disabled
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_pipe_set_irq(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 ee);
+
+/**
+ * Configure a BAM pipe for satellite MTI use
+ *
+ * This function configures a BAM pipe for satellite MTI use.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @irq_gen_addr - physical address written to generate MTI
+ *
+ * @ee - BAM execution environment index
+ *
+ */
+void bam_pipe_satellite_mti(void *base, u32 pipe, u32 irq_gen_addr, u32 ee);
+
+/**
+ * Configure MTI for a BAM pipe
+ *
+ * This function configures the interrupt for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @irq_en - enable or disable interrupt
+ *
+ * @src_mask - interrupt source mask, set regardless of whether
+ *    interrupt is disabled
+ *
+ * @irq_gen_addr - physical address written to generate MTI
+ *
+ */
+void bam_pipe_set_mti(void *base, u32 pipe, enum bam_enable irq_en,
+		      u32 src_mask, u32 irq_gen_addr);
+
+/**
+ * Get and Clear BAM pipe IRQ status
+ *
+ * This function gets and clears BAM pipe IRQ status.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return IRQ status
+ *
+ */
+u32 bam_pipe_get_and_clear_irq_status(void *base, u32 pipe);
+
+/**
+ * Set write offset for a BAM pipe
+ *
+ * This function sets the write offset for a BAM pipe.  This is
+ *    the offset that is maintained by software in system mode.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @next_write - descriptor FIFO write offset
+ *
+ */
+void bam_pipe_set_desc_write_offset(void *base, u32 pipe, u32 next_write);
+
+/**
+ * Get write offset for a BAM pipe
+ *
+ * This function gets the write offset for a BAM pipe.  This is
+ *    the offset that is maintained by the pipe's peer pipe or by software.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return descriptor FIFO write offset
+ *
+ */
+u32 bam_pipe_get_desc_write_offset(void *base, u32 pipe);
+
+/**
+ * Get read offset for a BAM pipe
+ *
+ * This function gets the read offset for a BAM pipe.  This is
+ *    the offset that is maintained by the pipe in system mode.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return descriptor FIFO read offset
+ *
+ */
+u32 bam_pipe_get_desc_read_offset(void *base, u32 pipe);
+
+/**
+ * Configure inactivity timer count for a BAM pipe
+ *
+ * This function configures the inactivity timer count for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @mode - timer operating mode
+ *
+ * @timeout_count - timeout count
+ *
+ */
+void bam_pipe_timer_config(void *base, u32 pipe,
+			   enum bam_pipe_timer_mode mode,
+			   u32 timeout_count);
+
+/**
+ * Reset inactivity timer for a BAM pipe
+ *
+ * This function resets the inactivity timer count for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ */
+void bam_pipe_timer_reset(void *base, u32 pipe);
+
+/**
+ * Get inactivity timer count for a BAM pipe
+ *
+ * This function gets the inactivity timer count for a BAM pipe.
+ *
+ * @base - BAM virtual base address.
+ *
+ * @pipe - pipe index
+ *
+ * @return inactivity timer count
+ *
+ */
+u32 bam_pipe_timer_get_count(void *base, u32 pipe);
+
+/*
+ * bam_pipe_check_zlt - Check if the last desc is ZLT.
+ * @base:	BAM virtual address
+ * @pipe:	pipe index
+ *
+ * This function checks if the last desc in the desc FIFO is a ZLT desc.
+ *
+ * @return true if the last desc in the desc FIFO is a ZLT desc. Otherwise
+ *  return false.
+ */
+bool bam_pipe_check_zlt(void *base, u32 pipe);
+
+/*
+ * bam_pipe_check_pipe_empty - Check if desc FIFO is empty.
+ * @base:	BAM virtual address
+ * @pipe:	pipe index
+ *
+ * This function checks if the desc FIFO of this pipe is empty.
+ *
+ * @return true if desc FIFO is empty. Otherwise return false.
+ */
+bool bam_pipe_check_pipe_empty(void *base, u32 pipe);
+#endif				/* _BAM_H_ */
diff --git a/drivers/platform/msm/sps/sps.c b/drivers/platform/msm/sps/sps.c
new file mode 100644
index 0000000..907c94e8
--- /dev/null
+++ b/drivers/platform/msm/sps/sps.c
@@ -0,0 +1,3064 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Smart-Peripheral-Switch (SPS) Module. */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/module.h>	/* module_init() */
+#include <linux/slab.h>		/* kzalloc() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/device.h>	/* device */
+#include <linux/fs.h>		/* alloc_chrdev_region() */
+#include <linux/list.h>		/* list_head */
+#include <linux/memory.h>	/* memset */
+#include <linux/io.h>		/* ioremap() */
+#include <linux/clk.h>		/* clk_enable() */
+#include <linux/platform_device.h>	/* platform_get_resource_byname() */
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+#include "sps_bam.h"
+#include "spsi.h"
+#include "sps_core.h"
+
+#define SPS_DRV_NAME "msm_sps"	/* must match the platform_device name */
+
+/**
+ *  SPS driver state
+ */
+struct sps_drv *sps;
+
+u32 d_type;
+bool enhd_pipe;
+bool imem;
+enum sps_bam_type bam_type;
+enum sps_bam_type bam_types[] = {SPS_BAM_LEGACY, SPS_BAM_NDP, SPS_BAM_NDP_4K};
+
+static void sps_device_de_init(void);
+
+#ifdef CONFIG_DEBUG_FS
+u8 debugfs_record_enabled;
+u8 logging_option;
+u8 debug_level_option;
+u8 print_limit_option;
+u8 reg_dump_option;
+u32 testbus_sel;
+u32 bam_pipe_sel;
+u32 desc_option;
+/*
+ * Specifies range of log level from level 0 to level 3 to have fine-granularity
+ * for logging to serve all BAM use cases.
+ */
+u32 log_level_sel;
+
+static char *debugfs_buf;
+static u32 debugfs_buf_size;
+static u32 debugfs_buf_used;
+static int wraparound;
+
+struct dentry *dent;
+struct dentry *dfile_info;
+struct dentry *dfile_logging_option;
+struct dentry *dfile_debug_level_option;
+struct dentry *dfile_print_limit_option;
+struct dentry *dfile_reg_dump_option;
+struct dentry *dfile_testbus_sel;
+struct dentry *dfile_bam_pipe_sel;
+struct dentry *dfile_desc_option;
+struct dentry *dfile_bam_addr;
+struct dentry *dfile_log_level_sel;
+
+static struct sps_bam *phy2bam(phys_addr_t phys_addr);
+
+/* record debug info for debugfs */
+void sps_debugfs_record(const char *msg)
+{
+	if (debugfs_record_enabled) {
+		if (debugfs_buf_used + MAX_MSG_LEN >= debugfs_buf_size) {
+			debugfs_buf_used = 0;
+			wraparound = true;
+		}
+		debugfs_buf_used += scnprintf(debugfs_buf + debugfs_buf_used,
+				debugfs_buf_size - debugfs_buf_used, msg);
+
+		if (wraparound)
+			scnprintf(debugfs_buf + debugfs_buf_used,
+					debugfs_buf_size - debugfs_buf_used,
+					"\n**** end line of sps log ****\n\n");
+	}
+}
+
+/* read the recorded debug info to userspace */
+static ssize_t sps_read_info(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	int ret = 0;
+	int size;
+
+	if (debugfs_record_enabled) {
+		if (wraparound)
+			size = debugfs_buf_size - MAX_MSG_LEN;
+		else
+			size = debugfs_buf_used;
+
+		ret = simple_read_from_buffer(ubuf, count, ppos,
+				debugfs_buf, size);
+	}
+
+	return ret;
+}
+
+/*
+ * set the buffer size (in KB) for debug info
+ */
+static ssize_t sps_set_info(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	char str[MAX_MSG_LEN];
+	int i;
+	u32 buf_size_kb = 0;
+	u32 new_buf_size;
+	u32 size = sizeof(str) < count ? sizeof(str) : count;
+
+	memset(str, 0, sizeof(str));
+	missing = copy_from_user(str, buf, size);
+	if (missing)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		buf_size_kb = (buf_size_kb * 10) + (str[i] - '0');
+
+	pr_info("sps:debugfs: input buffer size is %dKB\n", buf_size_kb);
+
+	if ((logging_option == 0) || (logging_option == 2)) {
+		pr_info("sps:debugfs: need to first turn on recording.\n");
+		return -EFAULT;
+	}
+
+	if (buf_size_kb < 1) {
+		pr_info("sps:debugfs:buffer size should be no less than 1KB\n");
+		return -EFAULT;
+	}
+
+	if (buf_size_kb > (INT_MAX/SZ_1K)) {
+		pr_err("sps:debugfs: buffer size is too large\n");
+		return -EFAULT;
+	}
+
+	new_buf_size = buf_size_kb * SZ_1K;
+
+	if (debugfs_record_enabled) {
+		if (debugfs_buf_size == new_buf_size) {
+			/* need do nothing */
+			pr_info(
+				"sps:debugfs: input buffer size is the same as before.\n"
+				);
+			return count;
+		}
+		/* release the current buffer */
+		debugfs_record_enabled = false;
+		debugfs_buf_used = 0;
+		wraparound = false;
+		kfree(debugfs_buf);
+		debugfs_buf = NULL;
+	}
+
+	/* allocate new buffer */
+	debugfs_buf_size = new_buf_size;
+
+	debugfs_buf = kzalloc(debugfs_buf_size,	GFP_KERNEL);
+	if (!debugfs_buf) {
+		debugfs_buf_size = 0;
+		pr_err("sps:fail to allocate memory for debug_fs.\n");
+		return -ENOMEM;
+	}
+
+	debugfs_buf_used = 0;
+	wraparound = false;
+	debugfs_record_enabled = true;
+
+	return count;
+}
+
+const struct file_operations sps_info_ops = {
+	.read = sps_read_info,
+	.write = sps_set_info,
+};
+
+/* return the current logging option to userspace */
+static ssize_t sps_read_logging_option(struct file *file, char __user *ubuf,
+		size_t count, loff_t *ppos)
+{
+	char value[MAX_MSG_LEN];
+	int nbytes;
+
+	nbytes = snprintf(value, MAX_MSG_LEN, "%d\n", logging_option);
+
+	return simple_read_from_buffer(ubuf, count, ppos, value, nbytes);
+}
+
+/*
+ * set the logging option
+ */
+static ssize_t sps_set_logging_option(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	char str[MAX_MSG_LEN];
+	int i;
+	u8 option = 0;
+	u32 size = sizeof(str) < count ? sizeof(str) : count;
+
+	memset(str, 0, sizeof(str));
+	missing = copy_from_user(str, buf, size);
+	if (missing)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		option = (option * 10) + (str[i] - '0');
+
+	pr_info("sps:debugfs: try to change logging option to %d\n", option);
+
+	if (option > 3) {
+		pr_err("sps:debugfs: invalid logging option:%d\n", option);
+		return count;
+	}
+
+	if (((option == 0) || (option == 2)) &&
+		((logging_option == 1) || (logging_option == 3))) {
+		debugfs_record_enabled = false;
+		kfree(debugfs_buf);
+		debugfs_buf = NULL;
+		debugfs_buf_used = 0;
+		debugfs_buf_size = 0;
+		wraparound = false;
+	}
+
+	logging_option = option;
+
+	return count;
+}
+
+const struct file_operations sps_logging_option_ops = {
+	.read = sps_read_logging_option,
+	.write = sps_set_logging_option,
+};
+
+/*
+ * input the bam physical address
+ */
+static ssize_t sps_set_bam_addr(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	unsigned long missing;
+	char str[MAX_MSG_LEN];
+	u32 i;
+	u32 bam_addr = 0;
+	struct sps_bam *bam;
+	u32 num_pipes = 0;
+	void *vir_addr;
+	u32 size = sizeof(str) < count ? sizeof(str) : count;
+
+	memset(str, 0, sizeof(str));
+	missing = copy_from_user(str, buf, size);
+	if (missing)
+		return -EFAULT;
+
+	for (i = 0; i < sizeof(str) && (str[i] >= '0') && (str[i] <= '9'); ++i)
+		bam_addr = (bam_addr * 10) + (str[i] - '0');
+
+	pr_info("sps:debugfs:input BAM physical address:0x%x\n", bam_addr);
+
+	bam = phy2bam(bam_addr);
+
+	if (bam == NULL) {
+		pr_err("sps:debugfs:BAM 0x%x is not registered.", bam_addr);
+		return count;
+	}
+	vir_addr = &bam->base;
+	num_pipes = bam->props.num_pipes;
+	if (log_level_sel <= SPS_IPC_MAX_LOGLEVEL)
+		bam->ipc_loglevel = log_level_sel;
+
+	switch (reg_dump_option) {
+	case 1: /* output all registers of this BAM */
+		print_bam_reg(bam->base);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_reg(bam->base, i);
+		break;
+	case 2: /* output BAM-level registers */
+		print_bam_reg(bam->base);
+		break;
+	case 3: /* output selected BAM-level registers */
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		break;
+	case 4: /* output selected registers of all pipes */
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 5: /* output selected registers of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 6: /* output selected registers of typical pipes */
+		print_bam_pipe_selected_reg(vir_addr, 4);
+		print_bam_pipe_selected_reg(vir_addr, 5);
+		break;
+	case 7: /* output desc FIFO of all pipes */
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		break;
+	case 8: /* output desc FIFO of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		break;
+	case 9: /* output desc FIFO of typical pipes */
+		print_bam_pipe_desc_fifo(vir_addr, 4, 0);
+		print_bam_pipe_desc_fifo(vir_addr, 5, 0);
+		break;
+	case 10: /* output selected registers and desc FIFO of all pipes */
+		for (i = 0; i < num_pipes; i++) {
+			print_bam_pipe_selected_reg(vir_addr, i);
+			print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		}
+		break;
+	case 11: /* output selected registers and desc FIFO of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+			}
+		break;
+	case 12: /* output selected registers and desc FIFO of typical pipes */
+		print_bam_pipe_selected_reg(vir_addr, 4);
+		print_bam_pipe_desc_fifo(vir_addr, 4, 0);
+		print_bam_pipe_selected_reg(vir_addr, 5);
+		print_bam_pipe_desc_fifo(vir_addr, 5, 0);
+		break;
+	case 13: /* output BAM_TEST_BUS_REG */
+		if (testbus_sel)
+			print_bam_test_bus_reg(vir_addr, testbus_sel);
+		else {
+			pr_info("sps:output TEST_BUS_REG for all TEST_BUS_SEL");
+			print_bam_test_bus_reg(vir_addr, testbus_sel);
+		}
+		break;
+	case 14: /* output partial desc FIFO of selected pipes */
+		if (desc_option == 0)
+			desc_option = 1;
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i,
+							desc_option);
+		break;
+	case 15: /* output partial data blocks of descriptors */
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 100);
+		break;
+	case 16: /* output all registers of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_reg(bam->base, i);
+		break;
+	case 91: /*
+		  * output testbus register, BAM global regisers
+		  * and registers of all pipes
+		  */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 92: /*
+		  * output testbus register, BAM global regisers
+		  * and registers of selected pipes
+		  */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 93: /*
+		  * output registers and partial desc FIFOs
+		  * of selected pipes: format 1
+		  */
+		if (desc_option == 0)
+			desc_option = 1;
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i,
+							desc_option);
+		break;
+	case 94: /*
+		  * output registers and partial desc FIFOs
+		  * of selected pipes: format 2
+		  */
+		if (desc_option == 0)
+			desc_option = 1;
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i,
+							desc_option);
+			}
+		break;
+	case 95: /*
+		  * output registers and desc FIFOs
+		  * of selected pipes: format 1
+		  */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		break;
+	case 96: /*
+		  * output registers and desc FIFOs
+		  * of selected pipes: format 2
+		  */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+			}
+		break;
+	case 97: /*
+		  * output registers, desc FIFOs and partial data blocks
+		  * of selected pipes: format 1
+		  */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 100);
+		break;
+	case 98: /*
+		  * output registers, desc FIFOs and partial data blocks
+		  * of selected pipes: format 2
+		  */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (bam_pipe_sel & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+				print_bam_pipe_desc_fifo(vir_addr, i, 100);
+			}
+		break;
+	case 99: /* output all registers, desc FIFOs and partial data blocks */
+		print_bam_test_bus_reg(vir_addr, testbus_sel);
+		print_bam_reg(bam->base);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_reg(bam->base, i);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i, 100);
+		break;
+	default:
+		pr_info("sps:no dump option is chosen yet.");
+	}
+
+	return count;
+}
+
+const struct file_operations sps_bam_addr_ops = {
+	.write = sps_set_bam_addr,
+};
+
+static void sps_debugfs_init(void)
+{
+	debugfs_record_enabled = false;
+	logging_option = 0;
+	debug_level_option = 0;
+	print_limit_option = 0;
+	reg_dump_option = 0;
+	testbus_sel = 0;
+	bam_pipe_sel = 0;
+	desc_option = 0;
+	debugfs_buf_size = 0;
+	debugfs_buf_used = 0;
+	wraparound = false;
+	log_level_sel = SPS_IPC_MAX_LOGLEVEL + 1;
+
+	dent = debugfs_create_dir("sps", 0);
+	if (IS_ERR(dent)) {
+		pr_err("sps:fail to create the folder for debug_fs.\n");
+		return;
+	}
+
+	dfile_info = debugfs_create_file("info", 0664, dent, 0,
+			&sps_info_ops);
+	if (!dfile_info || IS_ERR(dfile_info)) {
+		pr_err("sps:fail to create the file for debug_fs info.\n");
+		goto info_err;
+	}
+
+	dfile_logging_option = debugfs_create_file("logging_option", 0664,
+			dent, 0, &sps_logging_option_ops);
+	if (!dfile_logging_option || IS_ERR(dfile_logging_option)) {
+		pr_err("sps:fail to create debug_fs for logging_option.\n");
+		goto logging_option_err;
+	}
+
+	dfile_debug_level_option = debugfs_create_u8("debug_level_option",
+					0664, dent, &debug_level_option);
+	if (!dfile_debug_level_option || IS_ERR(dfile_debug_level_option)) {
+		pr_err("sps:fail to create debug_fs for debug_level_option.\n");
+		goto debug_level_option_err;
+	}
+
+	dfile_print_limit_option = debugfs_create_u8("print_limit_option",
+					0664, dent, &print_limit_option);
+	if (!dfile_print_limit_option || IS_ERR(dfile_print_limit_option)) {
+		pr_err("sps:fail to create debug_fs for print_limit_option.\n");
+		goto print_limit_option_err;
+	}
+
+	dfile_reg_dump_option = debugfs_create_u8("reg_dump_option", 0664,
+						dent, &reg_dump_option);
+	if (!dfile_reg_dump_option || IS_ERR(dfile_reg_dump_option)) {
+		pr_err("sps:fail to create debug_fs for reg_dump_option.\n");
+		goto reg_dump_option_err;
+	}
+
+	dfile_testbus_sel = debugfs_create_u32("testbus_sel", 0664,
+						dent, &testbus_sel);
+	if (!dfile_testbus_sel || IS_ERR(dfile_testbus_sel)) {
+		pr_err("sps:fail to create debug_fs file for testbus_sel.\n");
+		goto testbus_sel_err;
+	}
+
+	dfile_bam_pipe_sel = debugfs_create_u32("bam_pipe_sel", 0664,
+						dent, &bam_pipe_sel);
+	if (!dfile_bam_pipe_sel || IS_ERR(dfile_bam_pipe_sel)) {
+		pr_err("sps:fail to create debug_fs file for bam_pipe_sel.\n");
+		goto bam_pipe_sel_err;
+	}
+
+	dfile_desc_option = debugfs_create_u32("desc_option", 0664,
+						dent, &desc_option);
+	if (!dfile_desc_option || IS_ERR(dfile_desc_option)) {
+		pr_err("sps:fail to create debug_fs file for desc_option.\n");
+		goto desc_option_err;
+	}
+
+	dfile_bam_addr = debugfs_create_file("bam_addr", 0664,
+			dent, 0, &sps_bam_addr_ops);
+	if (!dfile_bam_addr || IS_ERR(dfile_bam_addr)) {
+		pr_err("sps:fail to create the file for debug_fs bam_addr.\n");
+		goto bam_addr_err;
+	}
+
+	dfile_log_level_sel = debugfs_create_u32("log_level_sel", 0664,
+						dent, &log_level_sel);
+	if (!dfile_log_level_sel || IS_ERR(dfile_log_level_sel)) {
+		pr_err("sps:fail to create debug_fs file for log_level_sel.\n");
+		goto bam_log_level_err;
+	}
+
+	return;
+
+bam_log_level_err:
+	debugfs_remove(dfile_bam_addr);
+bam_addr_err:
+	debugfs_remove(dfile_desc_option);
+desc_option_err:
+	debugfs_remove(dfile_bam_pipe_sel);
+bam_pipe_sel_err:
+	debugfs_remove(dfile_testbus_sel);
+testbus_sel_err:
+	debugfs_remove(dfile_reg_dump_option);
+reg_dump_option_err:
+	debugfs_remove(dfile_print_limit_option);
+print_limit_option_err:
+	debugfs_remove(dfile_debug_level_option);
+debug_level_option_err:
+	debugfs_remove(dfile_logging_option);
+logging_option_err:
+	debugfs_remove(dfile_info);
+info_err:
+	debugfs_remove(dent);
+}
+
+static void sps_debugfs_exit(void)
+{
+	debugfs_remove(dfile_info);
+	debugfs_remove(dfile_logging_option);
+	debugfs_remove(dfile_debug_level_option);
+	debugfs_remove(dfile_print_limit_option);
+	debugfs_remove(dfile_reg_dump_option);
+	debugfs_remove(dfile_testbus_sel);
+	debugfs_remove(dfile_bam_pipe_sel);
+	debugfs_remove(dfile_desc_option);
+	debugfs_remove(dfile_bam_addr);
+	debugfs_remove(dent);
+	debugfs_remove(dfile_log_level_sel);
+	kfree(debugfs_buf);
+	debugfs_buf = NULL;
+}
+#endif
+
+/* Get the debug info of BAM registers and descriptor FIFOs */
+int sps_get_bam_debug_info(unsigned long dev, u32 option, u32 para,
+		u32 tb_sel, u32 desc_sel)
+{
+	int res = 0;
+	struct sps_bam *bam;
+	u32 i;
+	u32 num_pipes = 0;
+	void *vir_addr;
+
+	if (dev == 0) {
+		SPS_ERR(sps,
+			"sps:%s:device handle should not be 0.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL || !sps->is_ready) {
+		SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	mutex_lock(&sps->lock);
+	/* Search for the target BAM device */
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		pr_err("sps:Can't find any BAM with handle 0x%lx.", dev);
+		mutex_unlock(&sps->lock);
+		return SPS_ERROR;
+	}
+	mutex_unlock(&sps->lock);
+
+	vir_addr = &bam->base;
+	num_pipes = bam->props.num_pipes;
+
+	SPS_DUMP("sps:<bam-addr> dump BAM:%pa.\n", &bam->props.phys_addr);
+
+	switch (option) {
+	case 1: /* output all registers of this BAM */
+		print_bam_reg(bam->base);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_reg(bam->base, i);
+		break;
+	case 2: /* output BAM-level registers */
+		print_bam_reg(bam->base);
+		break;
+	case 3: /* output selected BAM-level registers */
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		break;
+	case 4: /* output selected registers of all pipes */
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 5: /* output selected registers of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 6: /* output selected registers of typical pipes */
+		print_bam_pipe_selected_reg(vir_addr, 4);
+		print_bam_pipe_selected_reg(vir_addr, 5);
+		break;
+	case 7: /* output desc FIFO of all pipes */
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		break;
+	case 8: /* output desc FIFO of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		break;
+	case 9: /* output desc FIFO of typical pipes */
+		print_bam_pipe_desc_fifo(vir_addr, 4, 0);
+		print_bam_pipe_desc_fifo(vir_addr, 5, 0);
+		break;
+	case 10: /* output selected registers and desc FIFO of all pipes */
+		for (i = 0; i < num_pipes; i++) {
+			print_bam_pipe_selected_reg(vir_addr, i);
+			print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		}
+		break;
+	case 11: /* output selected registers and desc FIFO of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+			}
+		break;
+	case 12: /* output selected registers and desc FIFO of typical pipes */
+		print_bam_pipe_selected_reg(vir_addr, 4);
+		print_bam_pipe_desc_fifo(vir_addr, 4, 0);
+		print_bam_pipe_selected_reg(vir_addr, 5);
+		print_bam_pipe_desc_fifo(vir_addr, 5, 0);
+		break;
+	case 13: /* output BAM_TEST_BUS_REG */
+		if (tb_sel)
+			print_bam_test_bus_reg(vir_addr, tb_sel);
+		else
+			pr_info("sps:TEST_BUS_SEL should NOT be zero.");
+		break;
+	case 14: /* output partial desc FIFO of selected pipes */
+		if (desc_sel == 0)
+			desc_sel = 1;
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i,
+							desc_sel);
+		break;
+	case 15: /* output partial data blocks of descriptors */
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 100);
+		break;
+	case 16: /* output all registers of selected pipes */
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_reg(bam->base, i);
+		break;
+	case 91: /*
+		  * output testbus register, BAM global regisers
+		  * and registers of all pipes
+		  */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 92: /*
+		  * output testbus register, BAM global regisers
+		  * and registers of selected pipes
+		  */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		break;
+	case 93: /*
+		  * output registers and partial desc FIFOs
+		  * of selected pipes: format 1
+		  */
+		if (desc_sel == 0)
+			desc_sel = 1;
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i,
+							desc_sel);
+		break;
+	case 94: /*
+		  * output registers and partial desc FIFOs
+		  * of selected pipes: format 2
+		  */
+		if (desc_sel == 0)
+			desc_sel = 1;
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i,
+							desc_sel);
+			}
+		break;
+	case 95: /*
+		  * output registers and desc FIFOs
+		  * of selected pipes: format 1
+		  */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		break;
+	case 96: /*
+		  * output registers and desc FIFOs
+		  * of selected pipes: format 2
+		  */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+			}
+		break;
+	case 97: /*
+		  * output registers, desc FIFOs and partial data blocks
+		  * of selected pipes: format 1
+		  */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i))
+				print_bam_pipe_desc_fifo(vir_addr, i, 100);
+		break;
+	case 98: /*
+		  * output registers, desc FIFOs and partial data blocks
+		  * of selected pipes: format 2
+		  */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			if (para & (1UL << i)) {
+				print_bam_pipe_selected_reg(vir_addr, i);
+				print_bam_pipe_desc_fifo(vir_addr, i, 0);
+				print_bam_pipe_desc_fifo(vir_addr, i, 100);
+			}
+		break;
+	case 99: /* output all registers, desc FIFOs and partial data blocks */
+		print_bam_test_bus_reg(vir_addr, tb_sel);
+		print_bam_reg(bam->base);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_reg(bam->base, i);
+		print_bam_selected_reg(vir_addr, bam->props.ee);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_selected_reg(vir_addr, i);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i, 0);
+		for (i = 0; i < num_pipes; i++)
+			print_bam_pipe_desc_fifo(vir_addr, i, 100);
+		break;
+	default:
+		pr_info("sps:no option is chosen yet.");
+	}
+
+	return res;
+}
+EXPORT_SYMBOL(sps_get_bam_debug_info);
+
+/**
+ * Initialize SPS device
+ *
+ * This function initializes the SPS device.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_device_init(void)
+{
+	int result;
+	int success;
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	struct sps_bam_props bamdma_props = {0};
+#endif
+
+	SPS_DBG3(sps, "sps:%s.", __func__);
+
+	success = false;
+
+	result = sps_mem_init(sps->pipemem_phys_base, sps->pipemem_size);
+	if (result) {
+		SPS_ERR(sps, "sps:%s:SPS memory init failed", __func__);
+		goto exit_err;
+	}
+
+	INIT_LIST_HEAD(&sps->bams_q);
+	mutex_init(&sps->lock);
+
+	if (sps_rm_init(&sps->connection_ctrl, sps->options)) {
+		SPS_ERR(sps, "sps:%s:Fail to init SPS resource manager",
+				__func__);
+		goto exit_err;
+	}
+
+	result = sps_bam_driver_init(sps->options);
+	if (result) {
+		SPS_ERR(sps, "sps:%s:SPS BAM driver init failed", __func__);
+		goto exit_err;
+	}
+
+	/* Initialize the BAM DMA device */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	bamdma_props.phys_addr = sps->bamdma_bam_phys_base;
+	bamdma_props.virt_addr = ioremap(sps->bamdma_bam_phys_base,
+					 sps->bamdma_bam_size);
+
+	if (!bamdma_props.virt_addr) {
+		SPS_ERR(sps, "sps:%s:Fail to IO map BAM-DMA BAM registers.\n",
+				__func__);
+		goto exit_err;
+	}
+
+	SPS_DBG3(sps, "sps:bamdma_bam.phys=%pa.virt=0x%p.",
+		&bamdma_props.phys_addr,
+		bamdma_props.virt_addr);
+
+	bamdma_props.periph_phys_addr =	sps->bamdma_dma_phys_base;
+	bamdma_props.periph_virt_size = sps->bamdma_dma_size;
+	bamdma_props.periph_virt_addr = ioremap(sps->bamdma_dma_phys_base,
+						sps->bamdma_dma_size);
+
+	if (!bamdma_props.periph_virt_addr) {
+		SPS_ERR(sps, "sps:%s:Fail to IO map BAM-DMA peripheral reg.\n",
+				__func__);
+		goto exit_err;
+	}
+
+	SPS_DBG3(sps, "sps:bamdma_dma.phys=%pa.virt=0x%p.",
+		&bamdma_props.periph_phys_addr,
+		bamdma_props.periph_virt_addr);
+
+	bamdma_props.irq = sps->bamdma_irq;
+
+	bamdma_props.event_threshold = 0x10;	/* Pipe event threshold */
+	bamdma_props.summing_threshold = 0x10;	/* BAM event threshold */
+
+	bamdma_props.options = SPS_BAM_OPT_BAMDMA;
+	bamdma_props.restricted_pipes =	sps->bamdma_restricted_pipes;
+
+	result = sps_dma_init(&bamdma_props);
+	if (result) {
+		SPS_ERR(sps, "sps:%s:SPS BAM DMA driver init failed", __func__);
+		goto exit_err;
+	}
+#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
+
+	result = sps_map_init(NULL, sps->options);
+	if (result) {
+		SPS_ERR(sps,
+			"sps:%s:SPS connection mapping init failed", __func__);
+		goto exit_err;
+	}
+
+	success = true;
+exit_err:
+	if (!success) {
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		sps_device_de_init();
+#endif
+		return SPS_ERROR;
+	}
+
+	return 0;
+}
+
+/**
+ * De-initialize SPS device
+ *
+ * This function de-initializes the SPS device.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static void sps_device_de_init(void)
+{
+	SPS_DBG3(sps, "sps:%s.", __func__);
+
+	if (sps != NULL) {
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		sps_dma_de_init();
+#endif
+		/* Are there any remaining BAM registrations? */
+		if (!list_empty(&sps->bams_q))
+			SPS_ERR(sps,
+				"sps:%s:BAMs are still registered", __func__);
+
+		sps_map_de_init();
+
+		kfree(sps);
+	}
+
+	sps_mem_de_init();
+}
+
+/**
+ * Initialize client state context
+ *
+ * This function initializes a client state context struct.
+ *
+ * @client - Pointer to client state context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_client_init(struct sps_pipe *client)
+{
+	SPS_DBG(sps, "sps:%s.", __func__);
+
+	if (client == NULL)
+		return -EINVAL;
+
+	/*
+	 * NOTE: Cannot store any state within the SPS driver because
+	 * the driver init function may not have been called yet.
+	 */
+	memset(client, 0, sizeof(*client));
+	sps_rm_config_init(&client->connect);
+
+	client->client_state = SPS_STATE_DISCONNECT;
+	client->bam = NULL;
+
+	return 0;
+}
+
+/**
+ * De-initialize client state context
+ *
+ * This function de-initializes a client state context struct.
+ *
+ * @client - Pointer to client state context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_client_de_init(struct sps_pipe *client)
+{
+	SPS_DBG(sps, "sps:%s.", __func__);
+
+	if (client->client_state != SPS_STATE_DISCONNECT) {
+		SPS_ERR(sps, "sps:De-init client in connected state: 0x%x",
+				   client->client_state);
+		return SPS_ERROR;
+	}
+
+	client->bam = NULL;
+	client->map = NULL;
+	memset(&client->connect, 0, sizeof(client->connect));
+
+	return 0;
+}
+
+/**
+ * Find the BAM device from the physical address
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified physical address.
+ *
+ * @phys_addr - physical address of the BAM
+ *
+ * @return - pointer to the BAM device struct, or NULL on error
+ *
+ */
+static struct sps_bam *phy2bam(phys_addr_t phys_addr)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG2(sps, "sps:%s.", __func__);
+
+	list_for_each_entry(bam, &sps->bams_q, list) {
+		if (bam->props.phys_addr == phys_addr)
+			return bam;
+	}
+
+	return NULL;
+}
+
+/**
+ * Find the handle of a BAM device based on the physical address
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified physical address, and returns its handle.
+ *
+ * @phys_addr - physical address of the BAM
+ *
+ * @h - device handle of the BAM
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_phy2h(phys_addr_t phys_addr, unsigned long *handle)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG2(sps, "sps:%s.", __func__);
+
+	if (sps == NULL || !sps->is_ready) {
+		SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	if (handle == NULL) {
+		SPS_ERR(sps, "sps:%s:handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	list_for_each_entry(bam, &sps->bams_q, list) {
+		if (bam->props.phys_addr == phys_addr) {
+			*handle = (uintptr_t) bam;
+			return 0;
+		}
+	}
+
+	SPS_ERR(sps,
+		"sps: BAM device %pa is not registered yet.\n", &phys_addr);
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL(sps_phy2h);
+
+/**
+ * Setup desc/data FIFO for bam-to-bam connection
+ *
+ * @mem_buffer - Pointer to struct for allocated memory properties.
+ *
+ * @addr - address of FIFO
+ *
+ * @size - FIFO size
+ *
+ * @use_offset - use address offset instead of absolute address
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_setup_bam2bam_fifo(struct sps_mem_buffer *mem_buffer,
+		  u32 addr, u32 size, int use_offset)
+{
+	SPS_DBG1(sps, "sps:%s.", __func__);
+
+	if ((mem_buffer == NULL) || (size == 0)) {
+		SPS_ERR(sps, "sps:%s:invalid buffer address or size.",
+				__func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL || !sps->is_ready) {
+		SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	if (use_offset) {
+		if ((addr + size) <= sps->pipemem_size)
+			mem_buffer->phys_base = sps->pipemem_phys_base + addr;
+		else {
+			SPS_ERR(sps,
+				"sps:%s:requested mem is out of pipe mem range.\n",
+				__func__);
+			return SPS_ERROR;
+		}
+	} else {
+		if (addr >= sps->pipemem_phys_base &&
+			(addr + size) <= (sps->pipemem_phys_base
+						+ sps->pipemem_size))
+			mem_buffer->phys_base = addr;
+		else {
+			SPS_ERR(sps,
+				"sps:%s:requested mem is out of pipe mem range.\n",
+				__func__);
+			return SPS_ERROR;
+		}
+	}
+
+	mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base);
+	mem_buffer->size = size;
+
+	memset(mem_buffer->base, 0, mem_buffer->size);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_setup_bam2bam_fifo);
+
+/**
+ * Find the BAM device from the handle
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified device handle.
+ *
+ * @h - device handle of the BAM
+ *
+ * @return - pointer to the BAM device struct, or NULL on error
+ *
+ */
+struct sps_bam *sps_h2bam(unsigned long h)
+{
+	struct sps_bam *bam;
+
+	SPS_DBG1(sps, "sps:%s: BAM handle:0x%lx.", __func__, h);
+
+	if (h == SPS_DEV_HANDLE_MEM || h == SPS_DEV_HANDLE_INVALID)
+		return NULL;
+
+	list_for_each_entry(bam, &sps->bams_q, list) {
+		if ((uintptr_t) bam == h)
+			return bam;
+	}
+
+	SPS_ERR(sps, "sps:Can't find BAM device for handle 0x%lx.", h);
+
+	return NULL;
+}
+
+/**
+ * Lock BAM device
+ *
+ * This function obtains the BAM spinlock on the client's connection.
+ *
+ * @pipe - pointer to client pipe state
+ *
+ * @return pointer to BAM device struct, or NULL on error
+ *
+ */
+static struct sps_bam *sps_bam_lock(struct sps_pipe *pipe)
+{
+	struct sps_bam *bam;
+	u32 pipe_index;
+
+	bam = pipe->bam;
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:Connection is not in connected state.",
+				__func__);
+		return NULL;
+	}
+
+	spin_lock_irqsave(&bam->connection_lock, bam->irqsave_flags);
+
+	/* Verify client owns this pipe */
+	pipe_index = pipe->pipe_index;
+	if (pipe_index >= bam->props.num_pipes ||
+	    pipe != bam->pipes[pipe_index]) {
+		SPS_ERR(bam,
+			"sps:Client not owner of BAM %pa pipe: %d (max %d)",
+			&bam->props.phys_addr, pipe_index,
+			bam->props.num_pipes);
+		spin_unlock_irqrestore(&bam->connection_lock,
+						bam->irqsave_flags);
+		return NULL;
+	}
+
+	return bam;
+}
+
+/**
+ * Unlock BAM device
+ *
+ * This function releases the BAM spinlock on the client's connection.
+ *
+ * @bam - pointer to BAM device struct
+ *
+ */
+static inline void sps_bam_unlock(struct sps_bam *bam)
+{
+	spin_unlock_irqrestore(&bam->connection_lock, bam->irqsave_flags);
+}
+
+/**
+ * Connect an SPS connection end point
+ *
+ */
+int sps_connect(struct sps_pipe *h, struct sps_connect *connect)
+{
+	struct sps_pipe *pipe = h;
+	unsigned long dev;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (connect == NULL) {
+		SPS_ERR(sps, "sps:%s:connection is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL)
+		return -ENODEV;
+
+	if (!sps->is_ready) {
+		SPS_ERR(sps, "sps:%s:sps driver is not ready.\n", __func__);
+		return -EAGAIN;
+	}
+
+	if ((connect->lock_group != SPSRM_CLEAR)
+		&& (connect->lock_group > BAM_MAX_P_LOCK_GROUP_NUM)) {
+		SPS_ERR(sps,
+			"sps:%s:The value of pipe lock group is invalid.\n",
+			__func__);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&sps->lock);
+	/*
+	 * Must lock the BAM device at the top level function, so must
+	 * determine which BAM is the target for the connection
+	 */
+	if (connect->mode == SPS_MODE_SRC)
+		dev = connect->source;
+	else
+		dev = connect->destination;
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:Invalid BAM device handle: 0x%lx", dev);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	mutex_lock(&bam->lock);
+	SPS_DBG2(bam, "sps:sps_connect: bam %pa src 0x%lx dest 0x%lx mode %s",
+			BAM_ID(bam),
+			connect->source,
+			connect->destination,
+			connect->mode == SPS_MODE_SRC ? "SRC" : "DEST");
+
+	/* Allocate resources for the specified connection */
+	pipe->connect = *connect;
+	result = sps_rm_state_change(pipe, SPS_STATE_ALLOCATE);
+	if (result) {
+		mutex_unlock(&bam->lock);
+		goto exit_err;
+	}
+
+	/* Configure the connection */
+	result = sps_rm_state_change(pipe, SPS_STATE_CONNECT);
+	mutex_unlock(&bam->lock);
+	if (result) {
+		sps_disconnect(h);
+		goto exit_err;
+	}
+
+exit_err:
+	mutex_unlock(&sps->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_connect);
+
+/**
+ * Disconnect an SPS connection end point
+ *
+ * This function disconnects an SPS connection end point.
+ * The SPS hardware associated with that end point will be disabled.
+ * For a connection involving system memory (SPS_DEV_HANDLE_MEM), all
+ * connection resources are deallocated.  For a peripheral-to-peripheral
+ * connection, the resources associated with the connection will not be
+ * deallocated until both end points are closed.
+ *
+ * The client must call sps_connect() for the handle before calling
+ * this function.
+ *
+ * @h - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_disconnect(struct sps_pipe *h)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_pipe *check;
+	struct sps_bam *bam;
+	int result;
+
+	if (pipe == NULL) {
+		SPS_ERR(sps, "sps:%s:Invalid pipe.", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = pipe->bam;
+	if (bam == NULL) {
+		SPS_ERR(sps,
+			"sps:%s:BAM device of this pipe is NULL.", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG2(bam,
+		"sps:sps_disconnect: bam %pa src 0x%lx dest 0x%lx mode %s",
+		BAM_ID(bam),
+		pipe->connect.source,
+		pipe->connect.destination,
+		pipe->connect.mode == SPS_MODE_SRC ? "SRC" : "DEST");
+
+	result = SPS_ERROR;
+	/* Cross-check client with map table */
+	if (pipe->connect.mode == SPS_MODE_SRC)
+		check = pipe->map->client_src;
+	else
+		check = pipe->map->client_dest;
+
+	if (check != pipe) {
+		SPS_ERR(sps, "sps:%s:Client context is corrupt", __func__);
+		goto exit_err;
+	}
+
+	/* Disconnect the BAM pipe */
+	mutex_lock(&bam->lock);
+	result = sps_rm_state_change(pipe, SPS_STATE_DISCONNECT);
+	mutex_unlock(&bam->lock);
+	if (result)
+		goto exit_err;
+
+	sps_rm_config_init(&pipe->connect);
+	result = 0;
+
+exit_err:
+
+	return result;
+}
+EXPORT_SYMBOL(sps_disconnect);
+
+/**
+ * Register an event object for an SPS connection end point
+ *
+ */
+int sps_register_event(struct sps_pipe *h, struct sps_register_event *reg)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (reg == NULL) {
+		SPS_ERR(sps, "sps:%s:registered event is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL)
+		return -ENODEV;
+
+	if (!sps->is_ready) {
+		SPS_ERR(sps, "sps:%s:sps driver not ready.\n", __func__);
+		return -EAGAIN;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG2(bam, "sps:%s; events:%d.\n", __func__, reg->options);
+
+	result = sps_bam_pipe_reg_event(bam, pipe->pipe_index, reg);
+	sps_bam_unlock(bam);
+	if (result)
+		SPS_ERR(bam,
+			"sps:Fail to register event for BAM %pa pipe %d",
+			&pipe->bam->props.phys_addr, pipe->pipe_index);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_register_event);
+
+/**
+ * Enable an SPS connection end point
+ *
+ */
+int sps_flow_on(struct sps_pipe *h)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result = 0;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG2(bam, "sps:%s.\n", __func__);
+
+	bam_pipe_halt(&bam->base, pipe->pipe_index, false);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_flow_on);
+
+/**
+ * Disable an SPS connection end point
+ *
+ */
+int sps_flow_off(struct sps_pipe *h, enum sps_flow_off mode)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result = 0;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG2(bam, "sps:%s.\n", __func__);
+
+	bam_pipe_halt(&bam->base, pipe->pipe_index, true);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_flow_off);
+
+/**
+ * Check if the flags on a descriptor/iovec are valid
+ *
+ * @flags - flags on a descriptor/iovec
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_check_iovec_flags(u32 flags)
+{
+	if ((flags & SPS_IOVEC_FLAG_NWD) &&
+		!(flags & (SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_CMD))) {
+		SPS_ERR(sps,
+			"sps:%s:NWD is only valid with EOT or CMD.\n",
+			__func__);
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_EOT) &&
+		(flags & SPS_IOVEC_FLAG_CMD)) {
+		SPS_ERR(sps,
+			"sps:%s:EOT and CMD are not allowed to coexist.\n",
+			__func__);
+		return SPS_ERROR;
+	} else if (!(flags & SPS_IOVEC_FLAG_CMD) &&
+		(flags & (SPS_IOVEC_FLAG_LOCK | SPS_IOVEC_FLAG_UNLOCK))) {
+		static char err_msg[] =
+		"pipe lock/unlock flags are only valid with Command Descriptor";
+		SPS_ERR(sps, "sps:%s.\n", err_msg);
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_LOCK) &&
+		(flags & SPS_IOVEC_FLAG_UNLOCK)) {
+		static char err_msg[] =
+		"Can't lock and unlock a pipe by the same Command Descriptor";
+		SPS_ERR(sps, "sps:%s.\n", err_msg);
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
+		(flags & SPS_IOVEC_FLAG_CMD)) {
+		SPS_ERR(sps,
+			"sps:%s:Immediate and CMD are not allowed to coexist.\n",
+			__func__);
+		return SPS_ERROR;
+	} else if ((flags & SPS_IOVEC_FLAG_IMME) &&
+		(flags & SPS_IOVEC_FLAG_NWD)) {
+		SPS_ERR(sps,
+			"sps:%s:Immediate and NWD are not allowed to coexist.\n",
+			__func__);
+		return SPS_ERROR;
+	}
+
+	return 0;
+}
+
+/**
+ * Perform a DMA transfer on an SPS connection end point
+ *
+ */
+int sps_transfer(struct sps_pipe *h, struct sps_transfer *transfer)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+	struct sps_iovec *iovec;
+	int i;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (transfer == NULL) {
+		SPS_ERR(sps, "sps:%s:transfer is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (transfer->iovec == NULL) {
+		SPS_ERR(sps, "sps:%s:iovec list is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (transfer->iovec_count == 0) {
+		SPS_ERR(sps, "sps:%s:iovec list is empty.\n", __func__);
+		return SPS_ERROR;
+	} else if (transfer->iovec_phys == 0) {
+		SPS_ERR(sps,
+			"sps:%s:iovec list address is invalid.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	/* Verify content of IOVECs */
+	iovec = transfer->iovec;
+	for (i = 0; i < transfer->iovec_count; i++) {
+		u32 flags = iovec->flags;
+
+		if (iovec->size > SPS_IOVEC_MAX_SIZE) {
+			SPS_ERR(sps,
+				"sps:%s:iovec size is invalid.\n", __func__);
+			return SPS_ERROR;
+		}
+
+		if (sps_check_iovec_flags(flags))
+			return SPS_ERROR;
+
+		iovec++;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG(bam, "sps:%s.\n", __func__);
+
+	result = sps_bam_pipe_transfer(bam, pipe->pipe_index, transfer);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_transfer);
+
+/**
+ * Perform a single DMA transfer on an SPS connection end point
+ *
+ */
+int sps_transfer_one(struct sps_pipe *h, phys_addr_t addr, u32 size,
+		     void *user, u32 flags)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps_check_iovec_flags(flags))
+		return SPS_ERROR;
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG(bam, "sps:%s.\n", __func__);
+
+	result = sps_bam_pipe_transfer_one(bam, pipe->pipe_index,
+				SPS_GET_LOWER_ADDR(addr), size, user,
+				DESC_FLAG_WORD(flags, addr));
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_transfer_one);
+
+/**
+ * Read event queue for an SPS connection end point
+ *
+ */
+int sps_get_event(struct sps_pipe *h, struct sps_event_notify *notify)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (notify == NULL) {
+		SPS_ERR(sps, "sps:%s:event_notify is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG1(bam, "sps:%s.\n", __func__);
+
+	result = sps_bam_pipe_get_event(bam, pipe->pipe_index, notify);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_event);
+
+/**
+ * Determine whether an SPS connection end point FIFO is empty
+ *
+ */
+int sps_is_pipe_empty(struct sps_pipe *h, u32 *empty)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (empty == NULL) {
+		SPS_ERR(sps, "sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG1(bam, "sps:%s.\n", __func__);
+
+	result = sps_bam_pipe_is_empty(bam, pipe->pipe_index, empty);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_is_pipe_empty);
+
+/**
+ * Get number of free transfer entries for an SPS connection end point
+ *
+ */
+int sps_get_free_count(struct sps_pipe *h, u32 *count)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (count == NULL) {
+		SPS_ERR(sps, "sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG(bam, "sps:%s.\n", __func__);
+
+	result = sps_bam_get_free_count(bam, pipe->pipe_index, count);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_free_count);
+
+/**
+ * Reset an SPS BAM device
+ *
+ */
+int sps_device_reset(unsigned long dev)
+{
+	struct sps_bam *bam;
+	int result;
+
+	if (dev == 0) {
+		SPS_ERR(sps,
+			"sps:%s:device handle should not be 0.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL || !sps->is_ready) {
+		SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	mutex_lock(&sps->lock);
+	/* Search for the target BAM device */
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:Invalid BAM device handle: 0x%lx", dev);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	SPS_DBG3(bam, "sps:%s.\n", __func__);
+
+	mutex_lock(&bam->lock);
+	result = sps_bam_reset(bam);
+	mutex_unlock(&bam->lock);
+	if (result) {
+		SPS_ERR(sps, "sps:Fail to reset BAM device: 0x%lx", dev);
+		goto exit_err;
+	}
+
+exit_err:
+	mutex_unlock(&sps->lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_device_reset);
+
+/**
+ * Get the configuration parameters for an SPS connection end point
+ *
+ */
+int sps_get_config(struct sps_pipe *h, struct sps_connect *config)
+{
+	struct sps_pipe *pipe = h;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (config == NULL) {
+		SPS_ERR(sps, "sps:%s:config pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (pipe->bam == NULL)
+		SPS_DBG(sps, "sps:%s.\n", __func__);
+	else
+		SPS_DBG(pipe->bam,
+			"sps:%s; BAM: %pa; pipe index:%d; options:0x%x.\n",
+			__func__, BAM_ID(pipe->bam), pipe->pipe_index,
+			pipe->connect.options);
+
+	/* Copy current client connection state */
+	*config = pipe->connect;
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_get_config);
+
+/**
+ * Set the configuration parameters for an SPS connection end point
+ *
+ */
+int sps_set_config(struct sps_pipe *h, struct sps_connect *config)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (config == NULL) {
+		SPS_ERR(sps, "sps:%s:config pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d, config-options:0x%x.\n",
+		__func__, BAM_ID(bam), pipe->pipe_index, config->options);
+
+	result = sps_bam_pipe_set_params(bam, pipe->pipe_index,
+					 config->options);
+	if (result == 0)
+		pipe->connect.options = config->options;
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_set_config);
+
+/**
+ * Set ownership of an SPS connection end point
+ *
+ */
+int sps_set_owner(struct sps_pipe *h, enum sps_owner owner,
+		  struct sps_satellite *connect)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (connect == NULL) {
+		SPS_ERR(sps, "sps:%s:connection is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (owner != SPS_OWNER_REMOTE) {
+		SPS_ERR(sps, "sps:Unsupported ownership state: %d", owner);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe->pipe_index);
+
+	result = sps_bam_set_satellite(bam, pipe->pipe_index);
+	if (result)
+		goto exit_err;
+
+	/* Return satellite connect info */
+	if (connect == NULL)
+		goto exit_err;
+
+	if (pipe->connect.mode == SPS_MODE_SRC) {
+		connect->dev = pipe->map->src.bam_phys;
+		connect->pipe_index = pipe->map->src.pipe_index;
+	} else {
+		connect->dev = pipe->map->dest.bam_phys;
+		connect->pipe_index = pipe->map->dest.pipe_index;
+	}
+	connect->config = SPS_CONFIG_SATELLITE;
+	connect->options = (enum sps_option) 0;
+
+exit_err:
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_set_owner);
+
+/**
+ * Allocate memory from the SPS Pipe-Memory.
+ *
+ */
+int sps_alloc_mem(struct sps_pipe *h, enum sps_mem mem,
+		  struct sps_mem_buffer *mem_buffer)
+{
+	if (sps == NULL)
+		return -ENODEV;
+
+	if (!sps->is_ready) {
+		SPS_ERR(sps, "sps:%s:sps driver is not ready.", __func__);
+		return -EAGAIN;
+	}
+
+	if (mem_buffer == NULL || mem_buffer->size == 0) {
+		SPS_ERR(sps, "sps:%s:invalid memory buffer address or size",
+				__func__);
+		return SPS_ERROR;
+	}
+
+	if (h == NULL)
+		SPS_DBG2(sps,
+			"sps:%s:allocate pipe memory before setup pipe",
+			__func__);
+	else
+		SPS_DBG2(sps,
+			"sps:allocate pipe memory for pipe %d", h->pipe_index);
+
+	mem_buffer->phys_base = sps_mem_alloc_io(mem_buffer->size);
+	if (mem_buffer->phys_base == SPS_ADDR_INVALID) {
+		SPS_ERR(sps, "sps:%s:invalid address of allocated memory",
+				__func__);
+		return SPS_ERROR;
+	}
+
+	mem_buffer->base = spsi_get_mem_ptr(mem_buffer->phys_base);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_alloc_mem);
+
+/**
+ * Free memory from the SPS Pipe-Memory.
+ *
+ */
+int sps_free_mem(struct sps_pipe *h, struct sps_mem_buffer *mem_buffer)
+{
+	SPS_DBG(sps, "sps:%s.", __func__);
+
+	if (mem_buffer == NULL || mem_buffer->phys_base == SPS_ADDR_INVALID) {
+		SPS_ERR(sps, "sps:%s:invalid memory to free", __func__);
+		return SPS_ERROR;
+	}
+
+	if (h == NULL)
+		SPS_DBG2(sps, "sps:%s:free pipe memory.", __func__);
+	else
+		SPS_DBG2(sps,
+			"sps:free pipe memory for pipe %d.", h->pipe_index);
+
+	sps_mem_free_io(mem_buffer->phys_base, mem_buffer->size);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_free_mem);
+
+/**
+ * Get the number of unused descriptors in the descriptor FIFO
+ * of a pipe
+ *
+ */
+int sps_get_unused_desc_num(struct sps_pipe *h, u32 *desc_num)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (desc_num == NULL) {
+		SPS_ERR(sps, "sps:%s:result pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL)
+		return SPS_ERROR;
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe->pipe_index);
+
+	result = sps_bam_pipe_get_unused_desc_num(bam, pipe->pipe_index,
+						desc_num);
+
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_unused_desc_num);
+
+/**
+ * Vote for or relinquish BAM DMA clock
+ *
+ */
+int sps_ctrl_bam_dma_clk(bool clk_on)
+{
+	int ret;
+
+	if (sps == NULL || !sps->is_ready) {
+		SPS_DBG3(sps, "sps:%s:sps driver is not ready.\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	if (clk_on == true) {
+		SPS_DBG1(sps, "%s", "sps:vote for bam dma clk.\n");
+		ret = clk_prepare_enable(sps->bamdma_clk);
+		if (ret) {
+			SPS_ERR(sps,
+				"sps:fail to enable bamdma_clk:ret=%d\n", ret);
+			return ret;
+		}
+	} else {
+		SPS_DBG1(sps, "%s", "sps:relinquish bam dma clk.\n");
+		clk_disable_unprepare(sps->bamdma_clk);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_ctrl_bam_dma_clk);
+
+/**
+ * Register a BAM device
+ *
+ */
+int sps_register_bam_device(const struct sps_bam_props *bam_props,
+				unsigned long *dev_handle)
+{
+	struct sps_bam *bam = NULL;
+	void *virt_addr = NULL;
+	char bam_name[MAX_MSG_LEN];
+	u32 manage;
+	int ok;
+	int result;
+
+	if (bam_props == NULL) {
+		SPS_ERR(sps, "sps:%s:bam_props is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (dev_handle == NULL) {
+		SPS_ERR(sps, "sps:%s:device handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (sps == NULL) {
+		pr_err("sps:%s:sps driver is not ready.\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	SPS_DBG3(sps, "sps:%s: Client requests to register BAM %pa.\n",
+		__func__, &bam_props->phys_addr);
+
+	/* BAM-DMA is registered internally during power-up */
+	if ((!sps->is_ready) && !(bam_props->options & SPS_BAM_OPT_BAMDMA)) {
+		SPS_ERR(sps, "sps:%s:sps driver not ready.\n", __func__);
+		return -EAGAIN;
+	}
+
+	/* Check BAM parameters */
+	manage = bam_props->manage & SPS_BAM_MGR_ACCESS_MASK;
+	if (manage != SPS_BAM_MGR_NONE) {
+		if (bam_props->virt_addr == NULL && bam_props->virt_size == 0) {
+			SPS_ERR(sps, "sps:Invalid properties for BAM: %pa",
+					   &bam_props->phys_addr);
+			return SPS_ERROR;
+		}
+	}
+	if ((bam_props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
+		/* BAM global is configured by local processor */
+		if (bam_props->summing_threshold == 0) {
+			SPS_ERR(sps,
+				"sps:Invalid device ctrl properties for BAM: %pa",
+				&bam_props->phys_addr);
+			return SPS_ERROR;
+		}
+	}
+	manage = bam_props->manage &
+		  (SPS_BAM_MGR_PIPE_NO_CONFIG | SPS_BAM_MGR_PIPE_NO_CTRL);
+
+	/* In case of error */
+	*dev_handle = SPS_DEV_HANDLE_INVALID;
+	result = SPS_ERROR;
+
+	mutex_lock(&sps->lock);
+	/* Is this BAM already registered? */
+	bam = phy2bam(bam_props->phys_addr);
+	if (bam != NULL) {
+		mutex_unlock(&sps->lock);
+		SPS_ERR(sps, "sps:BAM is already registered: %pa",
+				&bam->props.phys_addr);
+		result = -EEXIST;
+		bam = NULL;   /* Avoid error clean-up kfree(bam) */
+		goto exit_err;
+	}
+
+	/* Perform virtual mapping if required */
+	if ((bam_props->manage & SPS_BAM_MGR_ACCESS_MASK) !=
+	    SPS_BAM_MGR_NONE && bam_props->virt_addr == NULL) {
+		/* Map the memory region */
+		virt_addr = ioremap(bam_props->phys_addr, bam_props->virt_size);
+		if (virt_addr == NULL) {
+			SPS_ERR(sps,
+				"sps:Unable to map BAM IO mem:%pa size:0x%x",
+				&bam_props->phys_addr, bam_props->virt_size);
+			goto exit_err;
+		}
+	}
+
+	bam = kzalloc(sizeof(*bam), GFP_KERNEL);
+	if (bam == NULL) {
+		SPS_ERR(sps,
+			"sps:Unable to allocate BAM device state: size is %zu",
+			sizeof(*bam));
+		goto exit_err;
+	}
+	memset(bam, 0, sizeof(*bam));
+
+	mutex_init(&bam->lock);
+	mutex_lock(&bam->lock);
+
+	/* Copy configuration to BAM device descriptor */
+	bam->props = *bam_props;
+	if (virt_addr != NULL)
+		bam->props.virt_addr = virt_addr;
+
+	snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_0",
+					&bam->props.phys_addr);
+	bam->ipc_log0 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							bam_name, 0);
+	if (!bam->ipc_log0)
+		SPS_ERR(sps, "%s : unable to create IPC Logging 0 for bam %pa",
+					__func__, &bam->props.phys_addr);
+
+	snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_1",
+					&bam->props.phys_addr);
+	bam->ipc_log1 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							bam_name, 0);
+	if (!bam->ipc_log1)
+		SPS_ERR(sps, "%s : unable to create IPC Logging 1 for bam %pa",
+					__func__, &bam->props.phys_addr);
+
+	snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_2",
+					&bam->props.phys_addr);
+	bam->ipc_log2 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							bam_name, 0);
+	if (!bam->ipc_log2)
+		SPS_ERR(sps, "%s : unable to create IPC Logging 2 for bam %pa",
+					__func__, &bam->props.phys_addr);
+
+	snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_3",
+					&bam->props.phys_addr);
+	bam->ipc_log3 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							bam_name, 0);
+	if (!bam->ipc_log3)
+		SPS_ERR(sps, "%s : unable to create IPC Logging 3 for bam %pa",
+					__func__, &bam->props.phys_addr);
+
+	snprintf(bam_name, sizeof(bam_name), "sps_bam_%pa_4",
+					&bam->props.phys_addr);
+	bam->ipc_log4 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							bam_name, 0);
+	if (!bam->ipc_log4)
+		SPS_ERR(sps, "%s : unable to create IPC Logging 4 for bam %pa",
+					__func__, &bam->props.phys_addr);
+
+	if (bam_props->ipc_loglevel)
+		bam->ipc_loglevel = bam_props->ipc_loglevel;
+	else
+		bam->ipc_loglevel = SPS_IPC_DEFAULT_LOGLEVEL;
+
+	ok = sps_bam_device_init(bam);
+	mutex_unlock(&bam->lock);
+	if (ok) {
+		SPS_ERR(bam, "sps:Fail to init BAM device: phys %pa",
+			&bam->props.phys_addr);
+		goto exit_err;
+	}
+
+	/* Add BAM to the list */
+	list_add_tail(&bam->list, &sps->bams_q);
+	*dev_handle = (uintptr_t) bam;
+
+	result = 0;
+exit_err:
+	mutex_unlock(&sps->lock);
+
+	if (result) {
+		if (bam != NULL) {
+			if (virt_addr != NULL)
+				iounmap(bam->props.virt_addr);
+			kfree(bam);
+		}
+
+		return result;
+	}
+
+	/* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+		if (sps_dma_device_init((uintptr_t) bam)) {
+			bam->props.options &= ~SPS_BAM_OPT_BAMDMA;
+			sps_deregister_bam_device((uintptr_t) bam);
+			SPS_ERR(bam, "sps:Fail to init BAM-DMA BAM: phys %pa",
+				&bam->props.phys_addr);
+			return SPS_ERROR;
+		}
+	}
+#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
+
+	SPS_INFO(bam, "sps:BAM %pa is registered.", &bam->props.phys_addr);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_register_bam_device);
+
+/**
+ * Deregister a BAM device
+ *
+ */
+int sps_deregister_bam_device(unsigned long dev_handle)
+{
+	struct sps_bam *bam;
+	int n;
+
+	if (dev_handle == 0) {
+		SPS_ERR(sps, "sps:%s:device handle should not be 0.\n",
+				__func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev_handle);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:did not find a BAM for this handle",
+				__func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG3(sps, "sps:%s: SPS deregister BAM: phys %pa.",
+		__func__, &bam->props.phys_addr);
+
+	if (bam->props.options & SPS_BAM_HOLD_MEM) {
+		for (n = 0; n < BAM_MAX_PIPES; n++)
+			if (bam->desc_cache_pointers[n] != NULL)
+				kfree(bam->desc_cache_pointers[n]);
+	}
+
+	/* If this BAM is attached to a BAM-DMA, init the BAM-DMA device */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+		mutex_lock(&bam->lock);
+		(void)sps_dma_device_de_init((uintptr_t) bam);
+		bam->props.options &= ~SPS_BAM_OPT_BAMDMA;
+		mutex_unlock(&bam->lock);
+	}
+#endif
+
+	/* Remove the BAM from the registration list */
+	mutex_lock(&sps->lock);
+	list_del(&bam->list);
+	mutex_unlock(&sps->lock);
+
+	/* De-init the BAM and free resources */
+	mutex_lock(&bam->lock);
+	sps_bam_device_de_init(bam);
+	mutex_unlock(&bam->lock);
+	ipc_log_context_destroy(bam->ipc_log1);
+	ipc_log_context_destroy(bam->ipc_log2);
+	if (bam->props.virt_size)
+		(void)iounmap(bam->props.virt_addr);
+
+	kfree(bam);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_deregister_bam_device);
+
+/**
+ * Get processed I/O vector (completed transfers)
+ *
+ */
+int sps_get_iovec(struct sps_pipe *h, struct sps_iovec *iovec)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (iovec == NULL) {
+		SPS_ERR(sps, "sps:%s:iovec pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe->pipe_index);
+
+	/* Get an iovec from the BAM pipe descriptor FIFO */
+	result = sps_bam_pipe_get_iovec(bam, pipe->pipe_index, iovec);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_get_iovec);
+
+/**
+ * Perform timer control
+ *
+ */
+int sps_timer_ctrl(struct sps_pipe *h,
+			struct sps_timer_ctrl *timer_ctrl,
+			struct sps_timer_result *timer_result)
+{
+	struct sps_pipe *pipe = h;
+	struct sps_bam *bam;
+	int result;
+
+	if (h == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (timer_ctrl == NULL) {
+		SPS_ERR(sps, "sps:%s:timer_ctrl pointer is NULL.\n", __func__);
+		return SPS_ERROR;
+	} else if (timer_result == NULL) {
+		SPS_DBG(sps, "sps:%s:no result to return.\n", __func__);
+	}
+
+	bam = sps_bam_lock(pipe);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG2(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe->pipe_index);
+
+	/* Perform the BAM pipe timer control operation */
+	result = sps_bam_pipe_timer_ctrl(bam, pipe->pipe_index, timer_ctrl,
+					 timer_result);
+	sps_bam_unlock(bam);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_timer_ctrl);
+
+/*
+ * Reset a BAM pipe
+ */
+int sps_pipe_reset(unsigned long dev, u32 pipe)
+{
+	struct sps_bam *bam;
+
+	if (!dev) {
+		SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (pipe >= BAM_MAX_PIPES) {
+		SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG2(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe);
+
+	bam_pipe_reset(&bam->base, pipe);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_pipe_reset);
+
+/*
+ * Disable a BAM pipe
+ */
+int sps_pipe_disable(unsigned long dev, u32 pipe)
+{
+	struct sps_bam *bam;
+
+	if (!dev) {
+		SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (pipe >= BAM_MAX_PIPES) {
+		SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe);
+
+	bam_disable_pipe(&bam->base, pipe);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_pipe_disable);
+
+/*
+ * Check pending descriptors in the descriptor FIFO
+ * of a pipe
+ */
+int sps_pipe_pending_desc(unsigned long dev, u32 pipe, bool *pending)
+{
+
+	struct sps_bam *bam;
+
+	if (!dev) {
+		SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (pipe >= BAM_MAX_PIPES) {
+		SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (!pending) {
+		SPS_ERR(sps, "sps:%s:input flag is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe);
+
+	*pending = sps_bam_pipe_pending_desc(bam, pipe);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_pipe_pending_desc);
+
+/*
+ * Process any pending IRQ of a BAM
+ */
+int sps_bam_process_irq(unsigned long dev)
+{
+	struct sps_bam *bam;
+	int ret = 0;
+
+	if (!dev) {
+		SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG1(bam, "sps:%s; BAM: %pa.\n", __func__, BAM_ID(bam));
+
+	ret = sps_bam_check_irq(bam);
+
+	return ret;
+}
+EXPORT_SYMBOL(sps_bam_process_irq);
+
+/*
+ * Get address info of a BAM
+ */
+int sps_get_bam_addr(unsigned long dev, phys_addr_t *base,
+				u32 *size)
+{
+	struct sps_bam *bam;
+
+	if (!dev) {
+		SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	*base = bam->props.phys_addr;
+	*size = bam->props.virt_size;
+
+	SPS_DBG2(bam, "sps:%s; BAM: %pa; base:%pa; size:%d.\n",
+		__func__, BAM_ID(bam), base, *size);
+
+	return 0;
+}
+EXPORT_SYMBOL(sps_get_bam_addr);
+
+/*
+ * Inject a ZLT with EOT for a BAM pipe
+ */
+int sps_pipe_inject_zlt(unsigned long dev, u32 pipe_index)
+{
+	struct sps_bam *bam;
+	int rc;
+
+	if (!dev) {
+		SPS_ERR(sps, "sps:%s:BAM handle is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	if (pipe_index >= BAM_MAX_PIPES) {
+		SPS_ERR(sps, "sps:%s:pipe index is invalid.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	bam = sps_h2bam(dev);
+	if (bam == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM is not found by handle.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	SPS_DBG(bam, "sps:%s; BAM: %pa; pipe index:%d.\n",
+		__func__, BAM_ID(bam), pipe_index);
+
+	rc = sps_bam_pipe_inject_zlt(bam, pipe_index);
+	if (rc)
+		SPS_ERR(bam, "sps:%s:failed to inject a ZLT.\n", __func__);
+
+	return rc;
+}
+EXPORT_SYMBOL(sps_pipe_inject_zlt);
+
+/**
+ * Allocate client state context
+ *
+ */
+struct sps_pipe *sps_alloc_endpoint(void)
+{
+	struct sps_pipe *ctx = NULL;
+
+	SPS_DBG(sps, "sps:%s.", __func__);
+
+	ctx = kzalloc(sizeof(struct sps_pipe), GFP_KERNEL);
+	if (ctx == NULL) {
+		SPS_ERR(sps, "sps:%s:Fail to allocate pipe context.",
+				__func__);
+		return NULL;
+	}
+
+	sps_client_init(ctx);
+
+	return ctx;
+}
+EXPORT_SYMBOL(sps_alloc_endpoint);
+
+/**
+ * Free client state context
+ *
+ */
+int sps_free_endpoint(struct sps_pipe *ctx)
+{
+	int res;
+
+	SPS_DBG(sps, "sps:%s.", __func__);
+
+	if (ctx == NULL) {
+		SPS_ERR(sps, "sps:%s:pipe is NULL.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	res = sps_client_de_init(ctx);
+
+	if (res == 0)
+		kfree(ctx);
+
+	return res;
+}
+EXPORT_SYMBOL(sps_free_endpoint);
+
+/**
+ * Platform Driver.
+ */
+static int get_platform_data(struct platform_device *pdev)
+{
+	struct resource *resource;
+	struct msm_sps_platform_data *pdata;
+
+	SPS_DBG3(sps, "sps:%s.", __func__);
+
+	pdata = pdev->dev.platform_data;
+
+	if (pdata == NULL) {
+		SPS_ERR(sps, "sps:%s:inavlid platform data.\n", __func__);
+		sps->bamdma_restricted_pipes = 0;
+		return -EINVAL;
+	}
+	sps->bamdma_restricted_pipes = pdata->bamdma_restricted_pipes;
+	SPS_DBG3(sps, "sps:bamdma_restricted_pipes=0x%x.\n",
+			sps->bamdma_restricted_pipes);
+
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "pipe_mem");
+	if (resource) {
+		sps->pipemem_phys_base = resource->start;
+		sps->pipemem_size = resource_size(resource);
+		SPS_DBG3(sps, "sps:pipemem.base=%pa,size=0x%x.\n",
+			&sps->pipemem_phys_base,
+			sps->pipemem_size);
+	}
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "bamdma_bam");
+	if (resource) {
+		sps->bamdma_bam_phys_base = resource->start;
+		sps->bamdma_bam_size = resource_size(resource);
+		SPS_DBG(sps, "sps:bamdma_bam.base=%pa,size=0x%x.",
+			&sps->bamdma_bam_phys_base,
+			sps->bamdma_bam_size);
+	}
+
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						 "bamdma_dma");
+	if (resource) {
+		sps->bamdma_dma_phys_base = resource->start;
+		sps->bamdma_dma_size = resource_size(resource);
+		SPS_DBG(sps, "sps:bamdma_dma.base=%pa,size=0x%x.",
+			&sps->bamdma_dma_phys_base,
+			sps->bamdma_dma_size);
+	}
+
+	resource  = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						 "bamdma_irq");
+	if (resource) {
+		sps->bamdma_irq = resource->start;
+		SPS_DBG(sps, "sps:bamdma_irq=%d.", sps->bamdma_irq);
+	}
+#endif
+
+	return 0;
+}
+
+/**
+ * Read data from device tree
+ */
+static int get_device_tree_data(struct platform_device *pdev)
+{
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	struct resource *resource;
+
+	SPS_DBG(sps, "sps:%s.", __func__);
+
+	if (of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,bam-dma-res-pipes",
+				&sps->bamdma_restricted_pipes))
+		SPS_DBG(sps,
+			"sps:%s:No restricted bamdma pipes on this target.\n",
+			__func__);
+	else
+		SPS_DBG(sps, "sps:bamdma_restricted_pipes=0x%x.",
+			sps->bamdma_restricted_pipes);
+
+	resource  = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (resource) {
+		sps->bamdma_bam_phys_base = resource->start;
+		sps->bamdma_bam_size = resource_size(resource);
+		SPS_DBG(sps, "sps:bamdma_bam.base=%pa,size=0x%x.",
+			&sps->bamdma_bam_phys_base,
+			sps->bamdma_bam_size);
+	} else {
+		SPS_ERR(sps, "sps:%s:BAM DMA BAM mem unavailable.", __func__);
+		return -ENODEV;
+	}
+
+	resource  = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (resource) {
+		sps->bamdma_dma_phys_base = resource->start;
+		sps->bamdma_dma_size = resource_size(resource);
+		SPS_DBG(sps, "sps:bamdma_dma.base=%pa,size=0x%x.",
+			&sps->bamdma_dma_phys_base,
+			sps->bamdma_dma_size);
+	} else {
+		SPS_ERR(sps, "sps:%s:BAM DMA mem unavailable.", __func__);
+		return -ENODEV;
+	}
+
+	resource = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (resource) {
+		imem = true;
+		sps->pipemem_phys_base = resource->start;
+		sps->pipemem_size = resource_size(resource);
+		SPS_DBG(sps, "sps:pipemem.base=%pa,size=0x%x.",
+			&sps->pipemem_phys_base,
+			sps->pipemem_size);
+	} else {
+		imem = false;
+		SPS_DBG(sps, "sps:%s:No pipe memory on this target.\n",
+				__func__);
+	}
+
+	resource  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (resource) {
+		sps->bamdma_irq = resource->start;
+		SPS_DBG(sps, "sps:bamdma_irq=%d.", sps->bamdma_irq);
+	} else {
+		SPS_ERR(sps, "sps:%s:BAM DMA IRQ unavailable.", __func__);
+		return -ENODEV;
+	}
+#endif
+
+	if (of_property_read_u32((&pdev->dev)->of_node,
+				"qcom,device-type",
+				&d_type)) {
+		d_type = 3;
+		SPS_DBG3(sps, "sps:default device type %d.\n", d_type);
+	} else
+		SPS_DBG3(sps, "sps:device type is %d.", d_type);
+
+	enhd_pipe = of_property_read_bool((&pdev->dev)->of_node,
+			"qcom,pipe-attr-ee");
+	SPS_DBG3(sps, "sps:PIPE_ATTR_EE is %s supported.\n",
+			(enhd_pipe ? "" : "not"));
+
+	return 0;
+}
+
+static const struct of_device_id msm_sps_match[] = {
+	{	.compatible = "qcom,msm_sps",
+		.data = &bam_types[SPS_BAM_NDP]
+	},
+	{	.compatible = "qcom,msm_sps_4k",
+		.data = &bam_types[SPS_BAM_NDP_4K]
+	},
+	{}
+};
+
+static int msm_sps_probe(struct platform_device *pdev)
+{
+	int ret = -ENODEV;
+
+	SPS_DBG3(sps, "sps:%s.", __func__);
+
+	if (pdev->dev.of_node) {
+		const struct of_device_id *match;
+
+		if (get_device_tree_data(pdev)) {
+			SPS_ERR(sps,
+				"sps:%s:Fail to get data from device tree.",
+				__func__);
+			return -ENODEV;
+		}
+		SPS_DBG(sps, "%s", "sps:get data from device tree.");
+
+		match = of_match_device(msm_sps_match, &pdev->dev);
+		if (match) {
+			bam_type = *((enum sps_bam_type *)(match->data));
+			SPS_DBG3(sps, "sps:BAM type is:%d\n", bam_type);
+		} else {
+			bam_type = SPS_BAM_NDP;
+			SPS_DBG3(sps, "sps:use default BAM type:%d\n",
+				bam_type);
+		}
+	} else {
+		d_type = 0;
+		if (get_platform_data(pdev)) {
+			SPS_ERR(sps, "sps:%s:Fail to get platform data.",
+				__func__);
+			return -ENODEV;
+		}
+		SPS_DBG(sps, "%s", "sps:get platform data.");
+		bam_type = SPS_BAM_LEGACY;
+	}
+
+	/* Create Device */
+	sps->dev_class = class_create(THIS_MODULE, SPS_DRV_NAME);
+
+	ret = alloc_chrdev_region(&sps->dev_num, 0, 1, SPS_DRV_NAME);
+	if (ret) {
+		SPS_ERR(sps, "sps:%s:alloc_chrdev_region err.", __func__);
+		goto alloc_chrdev_region_err;
+	}
+
+	sps->dev = device_create(sps->dev_class, NULL, sps->dev_num, sps,
+				SPS_DRV_NAME);
+	if (IS_ERR(sps->dev)) {
+		SPS_ERR(sps, "sps:%s:device_create err.", __func__);
+		goto device_create_err;
+	}
+
+	if (pdev->dev.of_node)
+		sps->dev->of_node = pdev->dev.of_node;
+
+	if (!d_type) {
+		sps->pmem_clk = clk_get(sps->dev, "mem_clk");
+		if (IS_ERR(sps->pmem_clk)) {
+			if (PTR_ERR(sps->pmem_clk) == -EPROBE_DEFER)
+				ret = -EPROBE_DEFER;
+			else
+				SPS_ERR(sps, "sps:%s:fail to get pmem_clk.",
+					__func__);
+			goto pmem_clk_err;
+		} else {
+			ret = clk_prepare_enable(sps->pmem_clk);
+			if (ret) {
+				SPS_ERR(sps,
+					"sps:%s:failed to enable pmem_clk.",
+					__func__);
+				goto pmem_clk_en_err;
+			}
+		}
+	}
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	sps->dfab_clk = clk_get(sps->dev, "dfab_clk");
+	if (IS_ERR(sps->dfab_clk)) {
+		if (PTR_ERR(sps->dfab_clk) == -EPROBE_DEFER)
+			ret = -EPROBE_DEFER;
+		else
+			SPS_ERR(sps, "sps:%s:fail to get dfab_clk.", __func__);
+		goto dfab_clk_err;
+	} else {
+		ret = clk_set_rate(sps->dfab_clk, 64000000);
+		if (ret) {
+			SPS_ERR(sps, "sps:%s:failed to set dfab_clk rate.",
+				__func__);
+			clk_put(sps->dfab_clk);
+			goto dfab_clk_err;
+		}
+	}
+
+	sps->bamdma_clk = clk_get(sps->dev, "dma_bam_pclk");
+	if (IS_ERR(sps->bamdma_clk)) {
+		if (PTR_ERR(sps->bamdma_clk) == -EPROBE_DEFER)
+			ret = -EPROBE_DEFER;
+		else
+			SPS_ERR(sps, "sps:%s:fail to get bamdma_clk.",
+				__func__);
+		clk_put(sps->dfab_clk);
+		goto dfab_clk_err;
+	} else {
+		ret = clk_prepare_enable(sps->bamdma_clk);
+		if (ret) {
+			SPS_ERR(sps, "sps:failed to enable bamdma_clk. ret=%d",
+									ret);
+			clk_put(sps->bamdma_clk);
+			clk_put(sps->dfab_clk);
+			goto dfab_clk_err;
+		}
+	}
+
+	ret = clk_prepare_enable(sps->dfab_clk);
+	if (ret) {
+		SPS_ERR(sps, "sps:failed to enable dfab_clk. ret=%d", ret);
+		clk_disable_unprepare(sps->bamdma_clk);
+		clk_put(sps->bamdma_clk);
+		clk_put(sps->dfab_clk);
+		goto dfab_clk_err;
+	}
+#endif
+	ret = sps_device_init();
+	if (ret) {
+		SPS_ERR(sps, "sps:%s:sps_device_init err.", __func__);
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		clk_disable_unprepare(sps->dfab_clk);
+		clk_disable_unprepare(sps->bamdma_clk);
+		clk_put(sps->bamdma_clk);
+		clk_put(sps->dfab_clk);
+#endif
+		goto dfab_clk_err;
+	}
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+	clk_disable_unprepare(sps->dfab_clk);
+	clk_disable_unprepare(sps->bamdma_clk);
+#endif
+	sps->is_ready = true;
+
+	SPS_INFO(sps, "%s", "sps:sps is ready.\n");
+
+	return 0;
+dfab_clk_err:
+	if (!d_type)
+		clk_disable_unprepare(sps->pmem_clk);
+pmem_clk_en_err:
+	if (!d_type)
+		clk_put(sps->pmem_clk);
+pmem_clk_err:
+	device_destroy(sps->dev_class, sps->dev_num);
+device_create_err:
+	unregister_chrdev_region(sps->dev_num, 1);
+alloc_chrdev_region_err:
+	class_destroy(sps->dev_class);
+
+	return ret;
+}
+
+static int msm_sps_remove(struct platform_device *pdev)
+{
+	SPS_DBG3(sps, "sps:%s.\n", __func__);
+
+	device_destroy(sps->dev_class, sps->dev_num);
+	unregister_chrdev_region(sps->dev_num, 1);
+	class_destroy(sps->dev_class);
+	sps_device_de_init();
+
+	clk_put(sps->dfab_clk);
+	if (!d_type)
+		clk_put(sps->pmem_clk);
+	clk_put(sps->bamdma_clk);
+
+	return 0;
+}
+
+static struct platform_driver msm_sps_driver = {
+	.probe          = msm_sps_probe,
+	.driver		= {
+		.name	= SPS_DRV_NAME,
+		.owner	= THIS_MODULE,
+		.of_match_table = msm_sps_match,
+	},
+	.remove		= msm_sps_remove,
+};
+
+/**
+ * Module Init.
+ */
+static int __init sps_init(void)
+{
+	int ret;
+
+#ifdef CONFIG_DEBUG_FS
+	sps_debugfs_init();
+#endif
+
+	pr_debug("sps:%s.", __func__);
+
+	/* Allocate the SPS driver state struct */
+	sps = kzalloc(sizeof(*sps), GFP_KERNEL);
+	if (sps == NULL)
+		return -ENOMEM;
+
+	sps->ipc_log0 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							"sps_ipc_log0", 0);
+	if (!sps->ipc_log0)
+		pr_err("Failed to create IPC log0\n");
+	sps->ipc_log1 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							"sps_ipc_log1", 0);
+	if (!sps->ipc_log1)
+		pr_err("Failed to create IPC log1\n");
+	sps->ipc_log2 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							"sps_ipc_log2", 0);
+	if (!sps->ipc_log2)
+		pr_err("Failed to create IPC log2\n");
+	sps->ipc_log3 = ipc_log_context_create(SPS_IPC_LOGPAGES,
+							"sps_ipc_log3", 0);
+	if (!sps->ipc_log3)
+		pr_err("Failed to create IPC log3\n");
+	sps->ipc_log4 = ipc_log_context_create(SPS_IPC_LOGPAGES *
+				SPS_IPC_REG_DUMP_FACTOR, "sps_ipc_log4", 0);
+	if (!sps->ipc_log4)
+		pr_err("Failed to create IPC log4\n");
+
+	ret = platform_driver_register(&msm_sps_driver);
+
+	return ret;
+}
+
+/**
+ * Module Exit.
+ */
+static void __exit sps_exit(void)
+{
+	pr_debug("sps:%s.", __func__);
+
+	platform_driver_unregister(&msm_sps_driver);
+
+	if (sps != NULL) {
+		kfree(sps);
+		sps = NULL;
+	}
+
+#ifdef CONFIG_DEBUG_FS
+	sps_debugfs_exit();
+#endif
+}
+
+arch_initcall(sps_init);
+module_exit(sps_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Smart Peripheral Switch (SPS)");
diff --git a/drivers/platform/msm/sps/sps_bam.c b/drivers/platform/msm/sps/sps_bam.c
new file mode 100644
index 0000000..be4a2cc
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_bam.c
@@ -0,0 +1,2504 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+#include <linux/slab.h>		/* kzalloc() */
+#include <linux/interrupt.h>	/* request_irq() */
+#include <linux/memory.h>	/* memset */
+#include <linux/vmalloc.h>
+
+#include "sps_bam.h"
+#include "bam.h"
+#include "spsi.h"
+
+/* All BAM global IRQ sources */
+#define BAM_IRQ_ALL (BAM_DEV_IRQ_HRESP_ERROR | BAM_DEV_IRQ_ERROR |   \
+	BAM_DEV_IRQ_TIMER)
+
+/* BAM device state flags */
+#define BAM_STATE_INIT     (1UL << 1)
+#define BAM_STATE_IRQ      (1UL << 2)
+#define BAM_STATE_ENABLED  (1UL << 3)
+#define BAM_STATE_BAM2BAM  (1UL << 4)
+#define BAM_STATE_MTI      (1UL << 5)
+#define BAM_STATE_REMOTE   (1UL << 6)
+
+/* Mask for valid hardware descriptor flags */
+#define BAM_IOVEC_FLAG_MASK   \
+	(SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT | SPS_IOVEC_FLAG_EOB |   \
+	SPS_IOVEC_FLAG_NWD | SPS_IOVEC_FLAG_CMD | SPS_IOVEC_FLAG_LOCK |   \
+	SPS_IOVEC_FLAG_UNLOCK | SPS_IOVEC_FLAG_IMME)
+
+/* Mask for invalid BAM-to-BAM pipe options */
+#define BAM2BAM_O_INVALID   \
+	(SPS_O_DESC_DONE | \
+	 SPS_O_EOT | \
+	 SPS_O_POLL | \
+	 SPS_O_NO_Q | \
+	 SPS_O_ACK_TRANSFERS)
+
+/**
+ * Pipe/client pointer value indicating pipe is allocated, but no client has
+ * been assigned
+ */
+#define BAM_PIPE_UNASSIGNED   ((struct sps_pipe *)((~0x0ul) - 0x88888888))
+
+/* Check whether pipe has been assigned */
+#define BAM_PIPE_IS_ASSIGNED(p)  \
+	(((p) != NULL) && ((p) != BAM_PIPE_UNASSIGNED))
+
+/* Is MTI use supported for a specific BAM version? */
+#define BAM_VERSION_MTI_SUPPORT(ver)   (ver <= 2)
+
+/* Event option<->event translation table entry */
+struct sps_bam_opt_event_table {
+	enum sps_event event_id;
+	enum sps_option option;
+	enum bam_pipe_irq pipe_irq;
+};
+
+static const struct sps_bam_opt_event_table opt_event_table[] = {
+	{SPS_EVENT_EOT, SPS_O_EOT, BAM_PIPE_IRQ_EOT},
+	{SPS_EVENT_DESC_DONE, SPS_O_DESC_DONE, BAM_PIPE_IRQ_DESC_INT},
+	{SPS_EVENT_WAKEUP, SPS_O_WAKEUP, BAM_PIPE_IRQ_WAKE},
+	{SPS_EVENT_INACTIVE, SPS_O_INACTIVE, BAM_PIPE_IRQ_TIMER},
+	{SPS_EVENT_OUT_OF_DESC, SPS_O_OUT_OF_DESC,
+		BAM_PIPE_IRQ_OUT_OF_DESC},
+	{SPS_EVENT_ERROR, SPS_O_ERROR, BAM_PIPE_IRQ_ERROR},
+	{SPS_EVENT_RST_ERROR, SPS_O_RST_ERROR, BAM_PIPE_IRQ_RST_ERROR},
+	{SPS_EVENT_HRESP_ERROR, SPS_O_HRESP_ERROR, BAM_PIPE_IRQ_HRESP_ERROR}
+};
+
+/* Pipe event source handler */
+static void pipe_handler(struct sps_bam *dev,
+			struct sps_pipe *pipe);
+
+/**
+ * Pipe transfer event (EOT, DESC_DONE) source handler.
+ * This function is called by pipe_handler() and other functions to process the
+ * descriptor FIFO.
+ */
+static void pipe_handler_eot(struct sps_bam *dev,
+			   struct sps_pipe *pipe);
+
+/**
+ * BAM driver initialization
+ */
+int sps_bam_driver_init(u32 options)
+{
+	int n;
+
+	/*
+	 * Check that SPS_O_ and BAM_PIPE_IRQ_ values are identical.
+	 * This is required so that the raw pipe IRQ status can be passed
+	 * to the client in the SPS_EVENT_IRQ.
+	 */
+	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
+		if ((u32)opt_event_table[n].option !=
+			(u32)opt_event_table[n].pipe_irq) {
+			SPS_ERR(sps, "sps:SPS_O 0x%x != HAL IRQ 0x%x\n",
+				opt_event_table[n].option,
+				opt_event_table[n].pipe_irq);
+			return SPS_ERROR;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * Check BAM interrupt
+ */
+int sps_bam_check_irq(struct sps_bam *dev)
+{
+	struct sps_pipe *pipe;
+	u32 source;
+	unsigned long flags = 0;
+	int ret = 0;
+
+	SPS_DBG1(dev, "sps:%s:bam=%pa.\n", __func__, BAM_ID(dev));
+
+	spin_lock_irqsave(&dev->isr_lock, flags);
+
+polling:
+	/* Get BAM interrupt source(s) */
+	if ((dev->state & BAM_STATE_MTI) == 0) {
+		u32 mask = dev->pipe_active_mask;
+		enum sps_callback_case cb_case;
+
+		source = bam_check_irq_source(&dev->base, dev->props.ee,
+						mask, &cb_case);
+
+		SPS_DBG1(dev, "sps:bam=%pa;source=0x%x;mask=0x%x.\n",
+				BAM_ID(dev), source, mask);
+
+		if ((source == 0) &&
+			(dev->props.options & SPS_BAM_RES_CONFIRM)) {
+			SPS_DBG2(dev,
+				"sps: BAM %pa has no source (source = 0x%x).\n",
+				BAM_ID(dev), source);
+
+			spin_unlock_irqrestore(&dev->isr_lock, flags);
+			return SPS_ERROR;
+		}
+
+		if ((source & (1UL << 31)) && (dev->props.callback)) {
+			SPS_DBG1(dev, "sps:bam=%pa;callback for case %d.\n",
+				BAM_ID(dev), cb_case);
+			dev->props.callback(cb_case, dev->props.user);
+		}
+
+		/* Mask any non-local source */
+		source &= dev->pipe_active_mask;
+	} else {
+		/* If MTIs are used, must poll each active pipe */
+		source = dev->pipe_active_mask;
+
+		SPS_DBG1(dev, "sps:MTI:bam=%pa;source=0x%x.\n",
+				BAM_ID(dev), source);
+	}
+
+	/* Process active pipe sources */
+	pipe = list_first_entry(&dev->pipes_q, struct sps_pipe, list);
+
+	list_for_each_entry(pipe, &dev->pipes_q, list) {
+		/* Check this pipe's bit in the source mask */
+		if (BAM_PIPE_IS_ASSIGNED(pipe)
+				&& (!pipe->disconnecting)
+				&& (source & pipe->pipe_index_mask)) {
+			/* This pipe has an interrupt pending */
+			pipe_handler(dev, pipe);
+			source &= ~pipe->pipe_index_mask;
+		}
+		if (source == 0)
+			break;
+	}
+
+	/* Process any inactive pipe sources */
+	if (source) {
+		SPS_ERR(dev, "sps:IRQ from BAM %pa inactive pipe(s) 0x%x\n",
+			BAM_ID(dev), source);
+		dev->irq_from_disabled_pipe++;
+	}
+
+	if (dev->props.options & SPS_BAM_RES_CONFIRM) {
+		u32 mask = dev->pipe_active_mask;
+		enum sps_callback_case cb_case;
+
+		source = bam_check_irq_source(&dev->base, dev->props.ee,
+						mask, &cb_case);
+
+		SPS_DBG1(dev,
+			"sps:check if there is any new IRQ coming:bam=%pa;source=0x%x;mask=0x%x.\n",
+				BAM_ID(dev), source, mask);
+
+		if ((source & (1UL << 31)) && (dev->props.callback)) {
+			SPS_DBG1(dev, "sps:bam=%pa;callback for case %d.\n",
+				BAM_ID(dev), cb_case);
+			dev->props.callback(cb_case, dev->props.user);
+		}
+
+		if (source)
+			goto polling;
+	}
+
+	spin_unlock_irqrestore(&dev->isr_lock, flags);
+
+	return ret;
+}
+
+/**
+ * BAM interrupt service routine
+ *
+ * This function is the BAM interrupt service routine.
+ *
+ * @ctxt - pointer to ISR's registered argument
+ *
+ * @return void
+ */
+static irqreturn_t bam_isr(int irq, void *ctxt)
+{
+	struct sps_bam *dev = ctxt;
+
+	SPS_DBG1(dev, "sps:bam_isr: bam:%pa; IRQ #:%d.\n",
+		BAM_ID(dev), irq);
+
+	if (dev->props.options & SPS_BAM_RES_CONFIRM) {
+		if (dev->props.callback) {
+			bool ready = false;
+
+			dev->props.callback(SPS_CALLBACK_BAM_RES_REQ, &ready);
+			if (ready) {
+				SPS_DBG1(dev,
+					"sps:bam_isr: handle IRQ for bam:%pa IRQ #:%d.\n",
+					BAM_ID(dev), irq);
+				if (sps_bam_check_irq(dev))
+					SPS_DBG2(dev,
+						"sps:bam_isr: callback bam:%pa IRQ #:%d to poll the pipes.\n",
+						BAM_ID(dev), irq);
+				dev->props.callback(SPS_CALLBACK_BAM_RES_REL,
+							&ready);
+			} else {
+				SPS_DBG1(dev,
+					"sps:bam_isr: BAM is not ready and thus skip IRQ for bam:%pa IRQ #:%d.\n",
+					BAM_ID(dev), irq);
+			}
+		} else {
+			SPS_ERR(dev,
+				"sps:Client of BAM %pa requires confirmation but does not register callback\n",
+				BAM_ID(dev));
+		}
+	} else {
+		sps_bam_check_irq(dev);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * BAM device enable
+ */
+int sps_bam_enable(struct sps_bam *dev)
+{
+	u32 num_pipes;
+	u32 irq_mask;
+	int result;
+	int rc;
+	int MTIenabled;
+
+	/* Is this BAM enabled? */
+	if ((dev->state & BAM_STATE_ENABLED))
+		return 0;	/* Yes, so no work to do */
+
+	/* Is there any access to this BAM? */
+	if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) {
+		SPS_ERR(dev, "sps:No local access to BAM %pa\n", BAM_ID(dev));
+		return SPS_ERROR;
+	}
+
+	/* Set interrupt handling */
+	if ((dev->props.options & SPS_BAM_OPT_IRQ_DISABLED) != 0 ||
+	    dev->props.irq == SPS_IRQ_INVALID) {
+		/* Disable the BAM interrupt */
+		irq_mask = 0;
+		dev->state &= ~BAM_STATE_IRQ;
+	} else {
+		/* Register BAM ISR */
+		if (dev->props.irq > 0) {
+			if (dev->props.options & SPS_BAM_RES_CONFIRM) {
+				result = request_irq(dev->props.irq,
+					(irq_handler_t) bam_isr,
+					IRQF_TRIGGER_RISING, "sps", dev);
+				SPS_DBG3(dev,
+					"sps:BAM %pa uses edge for IRQ# %d\n",
+					BAM_ID(dev), dev->props.irq);
+			} else {
+				result = request_irq(dev->props.irq,
+					(irq_handler_t) bam_isr,
+					IRQF_TRIGGER_HIGH, "sps", dev);
+				SPS_DBG3(dev,
+					"sps:BAM %pa uses level for IRQ# %d\n",
+					BAM_ID(dev), dev->props.irq);
+			}
+		} else {
+			SPS_DBG3(dev,
+				"sps:BAM %pa does not have an valid IRQ# %d\n",
+				BAM_ID(dev), dev->props.irq);
+		}
+
+		if (result) {
+			SPS_ERR(dev, "sps:Failed to enable BAM %pa IRQ %d\n",
+				BAM_ID(dev), dev->props.irq);
+			return SPS_ERROR;
+		}
+
+		/* Enable the BAM interrupt */
+		irq_mask = BAM_IRQ_ALL;
+		dev->state |= BAM_STATE_IRQ;
+
+		/* Register BAM IRQ for apps wakeup */
+		if (dev->props.options & SPS_BAM_OPT_IRQ_WAKEUP) {
+			result = enable_irq_wake(dev->props.irq);
+
+			if (result) {
+				SPS_ERR(dev,
+					"sps:Fail to enable wakeup irq for BAM %pa IRQ %d\n",
+					BAM_ID(dev), dev->props.irq);
+				return SPS_ERROR;
+			}
+			SPS_DBG3(dev,
+				"sps:Enable wakeup irq for BAM %pa IRQ %d\n",
+				BAM_ID(dev), dev->props.irq);
+		}
+	}
+
+	/* Is global BAM control managed by the local processor? */
+	num_pipes = 0;
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0)
+		/* Yes, so initialize the BAM device */
+		rc = bam_init(&dev->base,
+				  dev->props.ee,
+				  (u16) dev->props.summing_threshold,
+				  irq_mask,
+				  &dev->version, &num_pipes,
+				  dev->props.options);
+	else
+		/* No, so just verify that it is enabled */
+		rc = bam_check(&dev->base, &dev->version,
+				dev->props.ee, &num_pipes);
+
+	if (rc) {
+		SPS_ERR(dev, "sps:Fail to init BAM %pa IRQ %d\n",
+			BAM_ID(dev), dev->props.irq);
+		return SPS_ERROR;
+	}
+
+	/* Check if this BAM supports MTIs (Message Triggered Interrupts) or
+	 * multiple EEs (Execution Environments).
+	 * MTI and EE support are mutually exclusive.
+	 */
+	MTIenabled = BAM_VERSION_MTI_SUPPORT(dev->version);
+
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
+			(dev->props.manage & SPS_BAM_MGR_MULTI_EE) != 0 &&
+			dev->props.ee == 0 && MTIenabled) {
+		/*
+		 * BAM global is owned by remote processor and local processor
+		 * must use MTI. Thus, force EE index to a non-zero value to
+		 * insure that EE zero globals can't be modified.
+		 */
+		SPS_ERR(dev,
+			"sps:%s:EE for satellite BAM must be set to non-zero.\n",
+			__func__);
+		return SPS_ERROR;
+	}
+
+	/*
+	 * Enable MTI use (message triggered interrupt)
+	 * if local processor does not control the global BAM config
+	 * and this BAM supports MTIs.
+	 */
+	if ((dev->state & BAM_STATE_IRQ) != 0 &&
+		(dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) != 0 &&
+		MTIenabled) {
+		if (dev->props.irq_gen_addr == 0 ||
+		    dev->props.irq_gen_addr == SPS_ADDR_INVALID) {
+			SPS_ERR(dev,
+				"sps:MTI destination address not specified for BAM %pa\n",
+				BAM_ID(dev));
+			return SPS_ERROR;
+		}
+		dev->state |= BAM_STATE_MTI;
+	}
+
+	if (num_pipes) {
+		dev->props.num_pipes = num_pipes;
+		SPS_DBG3(dev,
+			"sps:BAM %pa number of pipes reported by hw: %d\n",
+				 BAM_ID(dev), dev->props.num_pipes);
+	}
+
+	/* Check EE index */
+	if (!MTIenabled && dev->props.ee >= SPS_BAM_NUM_EES) {
+		SPS_ERR(dev, "sps:Invalid EE BAM %pa: %d\n", BAM_ID(dev),
+				dev->props.ee);
+		return SPS_ERROR;
+	}
+
+	/*
+	 * Process EE configuration parameters,
+	 * if specified in the properties
+	 */
+	if (!MTIenabled && dev->props.sec_config == SPS_BAM_SEC_DO_CONFIG) {
+		struct sps_bam_sec_config_props *p_sec =
+						dev->props.p_sec_config_props;
+		if (p_sec == NULL) {
+			SPS_ERR(dev,
+				"sps:EE config table is not specified for BAM %pa\n",
+				BAM_ID(dev));
+			return SPS_ERROR;
+		}
+
+		/*
+		 * Set restricted pipes based on the pipes assigned to local EE
+		 */
+		dev->props.restricted_pipes =
+					~p_sec->ees[dev->props.ee].pipe_mask;
+
+		/*
+		 * If local processor manages the BAM, perform the EE
+		 * configuration
+		 */
+		if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
+			u32 ee;
+			u32 pipe_mask;
+			int n, i;
+
+			/*
+			 * Verify that there are no overlapping pipe
+			 * assignments
+			 */
+			for (n = 0; n < SPS_BAM_NUM_EES - 1; n++) {
+				for (i = n + 1; i < SPS_BAM_NUM_EES; i++) {
+					if ((p_sec->ees[n].pipe_mask &
+						p_sec->ees[i].pipe_mask) != 0) {
+						SPS_ERR(dev,
+							"sps:Overlapping pipe assignments for BAM %pa: EEs %d and %d\n",
+							BAM_ID(dev), n, i);
+						return SPS_ERROR;
+					}
+				}
+			}
+
+			for (ee = 0; ee < SPS_BAM_NUM_EES; ee++) {
+				/*
+				 * MSbit specifies EE for the global (top-level)
+				 * BAM interrupt
+				 */
+				pipe_mask = p_sec->ees[ee].pipe_mask;
+				if (ee == dev->props.ee)
+					pipe_mask |= (1UL << 31);
+				else
+					pipe_mask &= ~(1UL << 31);
+
+				bam_security_init(&dev->base, ee,
+						p_sec->ees[ee].vmid, pipe_mask);
+			}
+		}
+	}
+
+	/*
+	 * If local processor manages the BAM and the BAM supports MTIs
+	 * but does not support multiple EEs, set all restricted pipes
+	 * to MTI mode.
+	 */
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0
+			&& MTIenabled) {
+		u32 pipe_index;
+		u32 pipe_mask;
+
+		for (pipe_index = 0, pipe_mask = 1;
+		    pipe_index < dev->props.num_pipes;
+		    pipe_index++, pipe_mask <<= 1) {
+			if ((pipe_mask & dev->props.restricted_pipes) == 0)
+				continue;	/* This is a local pipe */
+
+			/*
+			 * Enable MTI with destination address of zero
+			 * (and source mask zero). Pipe is in reset,
+			 * so no interrupt will be generated.
+			 */
+			bam_pipe_satellite_mti(&dev->base, pipe_index, 0,
+						       dev->props.ee);
+		}
+	}
+
+	dev->state |= BAM_STATE_ENABLED;
+
+	if (!dev->props.constrained_logging ||
+		(dev->props.constrained_logging && dev->props.logging_number)) {
+		if (dev->props.logging_number > 0)
+			dev->props.logging_number--;
+		SPS_INFO(dev,
+			"sps:BAM %pa (va:0x%p) enabled: ver:0x%x, number of pipes:%d\n",
+			BAM_ID(dev), dev->base, dev->version,
+			dev->props.num_pipes);
+	} else
+		SPS_DBG3(dev,
+			"sps:BAM %pa (va:0x%p) enabled: ver:0x%x, number of pipes:%d\n",
+			BAM_ID(dev), dev->base, dev->version,
+			dev->props.num_pipes);
+
+	return 0;
+}
+
+/**
+ * BAM device disable
+ *
+ */
+int sps_bam_disable(struct sps_bam *dev)
+{
+	if ((dev->state & BAM_STATE_ENABLED) == 0)
+		return 0;
+
+	/* Is there any access to this BAM? */
+	if ((dev->props.manage & SPS_BAM_MGR_ACCESS_MASK) == SPS_BAM_MGR_NONE) {
+		SPS_ERR(dev, "sps:No local access to BAM %pa\n", BAM_ID(dev));
+		return SPS_ERROR;
+	}
+
+	/* Is this BAM controlled by the local processor? */
+	if ((dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) {
+		/* No, so just mark it disabled */
+		dev->state &= ~BAM_STATE_ENABLED;
+		if ((dev->state & BAM_STATE_IRQ) && (dev->props.irq > 0)) {
+			free_irq(dev->props.irq, dev);
+			dev->state &= ~BAM_STATE_IRQ;
+		}
+		return 0;
+	}
+
+	/* Disable BAM (interrupts) */
+	if ((dev->state & BAM_STATE_IRQ)) {
+		bam_exit(&dev->base, dev->props.ee);
+
+		/* Deregister BAM ISR */
+		if ((dev->state & BAM_STATE_IRQ))
+			if (dev->props.irq > 0)
+				free_irq(dev->props.irq, dev);
+		dev->state &= ~BAM_STATE_IRQ;
+	}
+
+	dev->state &= ~BAM_STATE_ENABLED;
+
+	SPS_DBG3(dev, "sps:BAM %pa disabled\n", BAM_ID(dev));
+
+	return 0;
+}
+
+/**
+ * BAM device initialization
+ */
+int sps_bam_device_init(struct sps_bam *dev)
+{
+	if (dev->props.virt_addr == NULL) {
+		SPS_ERR(dev, "sps:%s:NULL BAM virtual address\n", __func__);
+		return SPS_ERROR;
+	}
+	dev->base = (void *) dev->props.virt_addr;
+
+	if (dev->props.num_pipes == 0) {
+		/* Assume max number of pipes until BAM registers can be read */
+		dev->props.num_pipes = BAM_MAX_PIPES;
+		SPS_DBG3(dev, "sps:BAM %pa: assuming max number of pipes: %d\n",
+			BAM_ID(dev), dev->props.num_pipes);
+	}
+
+	/* Init BAM state data */
+	dev->state = 0;
+	dev->pipe_active_mask = 0;
+	dev->pipe_remote_mask = 0;
+	INIT_LIST_HEAD(&dev->pipes_q);
+
+	spin_lock_init(&dev->isr_lock);
+
+	spin_lock_init(&dev->connection_lock);
+
+	if ((dev->props.options & SPS_BAM_OPT_ENABLE_AT_BOOT))
+		if (sps_bam_enable(dev)) {
+			SPS_ERR(dev, "sps:%s:Fail to enable bam device\n",
+					__func__);
+			return SPS_ERROR;
+		}
+
+	SPS_DBG3(dev, "sps:BAM device: phys %pa IRQ %d\n",
+			BAM_ID(dev), dev->props.irq);
+
+	return 0;
+}
+
+/**
+ * BAM device de-initialization
+ *
+ */
+int sps_bam_device_de_init(struct sps_bam *dev)
+{
+	int result;
+
+	SPS_DBG3(dev, "sps:BAM device DEINIT: phys %pa IRQ %d\n",
+		BAM_ID(dev), dev->props.irq);
+
+	result = sps_bam_disable(dev);
+
+	return result;
+}
+
+/**
+ * BAM device reset
+ *
+ */
+int sps_bam_reset(struct sps_bam *dev)
+{
+	struct sps_pipe *pipe;
+	u32 pipe_index;
+	int result;
+
+	SPS_DBG3(dev, "sps:BAM device RESET: phys %pa IRQ %d\n",
+		BAM_ID(dev), dev->props.irq);
+
+	/* If BAM is enabled, then disable */
+	result = 0;
+	if ((dev->state & BAM_STATE_ENABLED)) {
+		/* Verify that no pipes are currently allocated */
+		for (pipe_index = 0; pipe_index < dev->props.num_pipes;
+		      pipe_index++) {
+			pipe = dev->pipes[pipe_index];
+			if (BAM_PIPE_IS_ASSIGNED(pipe)) {
+				SPS_ERR(dev,
+					"sps:BAM device %pa RESET failed: pipe %d in use\n",
+					BAM_ID(dev), pipe_index);
+				result = SPS_ERROR;
+				break;
+			}
+		}
+
+		if (result == 0)
+			result = sps_bam_disable(dev);
+	}
+
+	/* BAM will be reset as part of the enable process */
+	if (result == 0)
+		result = sps_bam_enable(dev);
+
+	return result;
+}
+
+/**
+ * Clear the BAM pipe state struct
+ *
+ * This function clears the BAM pipe state struct.
+ *
+ * @pipe - pointer to client pipe struct
+ *
+ */
+static void pipe_clear(struct sps_pipe *pipe)
+{
+	INIT_LIST_HEAD(&pipe->list);
+
+	pipe->state = 0;
+	pipe->pipe_index = SPS_BAM_PIPE_INVALID;
+	pipe->pipe_index_mask = 0;
+	pipe->irq_mask = 0;
+	pipe->mode = -1;
+	pipe->num_descs = 0;
+	pipe->desc_size = 0;
+	pipe->disconnecting = false;
+	pipe->late_eot = false;
+	memset(&pipe->sys, 0, sizeof(pipe->sys));
+	INIT_LIST_HEAD(&pipe->sys.events_q);
+}
+
+/**
+ * Allocate a BAM pipe
+ *
+ */
+u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index)
+{
+	u32 pipe_mask;
+
+	if (pipe_index == SPS_BAM_PIPE_INVALID) {
+		/* Allocate a pipe from the BAM */
+		if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_ALLOC)) {
+			SPS_ERR(dev,
+				"sps:Restricted from allocating pipes on BAM %pa\n",
+				BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+		for (pipe_index = 0, pipe_mask = 1;
+		    pipe_index < dev->props.num_pipes;
+		    pipe_index++, pipe_mask <<= 1) {
+			if ((pipe_mask & dev->props.restricted_pipes))
+				continue;	/* This is a restricted pipe */
+
+			if (dev->pipes[pipe_index] == NULL)
+				break;	/* Found an available pipe */
+		}
+		if (pipe_index >= dev->props.num_pipes) {
+			SPS_ERR(dev, "sps:Fail to allocate pipe on BAM %pa\n",
+				BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+	} else {
+		/* Check that client-specified pipe is available */
+		if (pipe_index >= dev->props.num_pipes) {
+			SPS_ERR(dev,
+				"sps:Invalid pipe %d for allocate on BAM %pa\n",
+				pipe_index, BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+		if ((dev->props.restricted_pipes & (1UL << pipe_index))) {
+			SPS_ERR(dev, "sps:BAM %pa pipe %d is not local\n",
+				BAM_ID(dev), pipe_index);
+			return SPS_BAM_PIPE_INVALID;
+		}
+		if (dev->pipes[pipe_index] != NULL) {
+			SPS_ERR(dev,
+				"sps:Pipe %d already allocated on BAM %pa\n",
+				pipe_index, BAM_ID(dev));
+			return SPS_BAM_PIPE_INVALID;
+		}
+	}
+
+	/* Mark pipe as allocated */
+	dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED;
+
+	return pipe_index;
+}
+
+/**
+ * Free a BAM pipe
+ *
+ */
+void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe;
+
+	if (pipe_index >= dev->props.num_pipes) {
+		SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev),
+				pipe_index);
+		return;
+	}
+
+	/* Get the client pipe struct and mark the pipe free */
+	pipe = dev->pipes[pipe_index];
+	dev->pipes[pipe_index] = NULL;
+
+	/* Is the pipe currently allocated? */
+	if (pipe == NULL) {
+		SPS_ERR(dev,
+			"sps:Attempt to free unallocated pipe %d on BAM %pa\n",
+			pipe_index, BAM_ID(dev));
+		return;
+	}
+
+	if (pipe == BAM_PIPE_UNASSIGNED)
+		return;		/* Never assigned, so no work to do */
+
+	/* Return pending items to appropriate pools */
+	if (!list_empty(&pipe->sys.events_q)) {
+		struct sps_q_event *sps_event;
+
+		SPS_ERR(dev,
+			"sps:Disconnect BAM %pa pipe %d with events pending\n",
+			BAM_ID(dev), pipe_index);
+
+		sps_event = list_entry((&pipe->sys.events_q)->next,
+				typeof(*sps_event), list);
+
+		while (&sps_event->list != (&pipe->sys.events_q)) {
+			struct sps_q_event *sps_event_delete = sps_event;
+
+			list_del(&sps_event->list);
+			sps_event = list_entry(sps_event->list.next,
+					typeof(*sps_event), list);
+			kfree(sps_event_delete);
+		}
+	}
+
+	/* Clear the BAM pipe state struct */
+	pipe_clear(pipe);
+}
+
+/**
+ * Establish BAM pipe connection
+ *
+ */
+int sps_bam_pipe_connect(struct sps_pipe *bam_pipe,
+			 const struct sps_bam_connect_param *params)
+{
+	struct bam_pipe_parameters hw_params;
+	struct sps_bam *dev;
+	const struct sps_connection *map = bam_pipe->map;
+	const struct sps_conn_end_pt *map_pipe;
+	const struct sps_conn_end_pt *other_pipe;
+	void *desc_buf = NULL;
+	u32 pipe_index;
+	int result;
+
+	/* Clear the client pipe state and hw init struct */
+	pipe_clear(bam_pipe);
+	memset(&hw_params, 0, sizeof(hw_params));
+
+	/* Initialize the BAM state struct */
+	bam_pipe->mode = params->mode;
+
+	/* Set pipe streaming mode */
+	if ((params->options & SPS_O_STREAMING) == 0)
+		hw_params.stream_mode = BAM_STREAM_MODE_DISABLE;
+	else
+		hw_params.stream_mode = BAM_STREAM_MODE_ENABLE;
+
+	/* Determine which end point to connect */
+	if (bam_pipe->mode == SPS_MODE_SRC) {
+		map_pipe = &map->src;
+		other_pipe = &map->dest;
+		hw_params.dir = BAM_PIPE_PRODUCER;
+	} else {
+		map_pipe = &map->dest;
+		other_pipe = &map->src;
+		hw_params.dir = BAM_PIPE_CONSUMER;
+	}
+
+	/* Process map parameters */
+	dev = map_pipe->bam;
+	pipe_index = map_pipe->pipe_index;
+
+	SPS_DBG2(dev,
+		"sps:BAM %pa; pipe %d; mode:%d; options:0x%x.\n",
+		BAM_ID(dev), pipe_index, params->mode, params->options);
+
+	if (pipe_index >= dev->props.num_pipes) {
+		SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev),
+				pipe_index);
+		return SPS_ERROR;
+	}
+	hw_params.event_threshold = (u16) map_pipe->event_threshold;
+	hw_params.ee = dev->props.ee;
+	hw_params.lock_group = map_pipe->lock_group;
+
+	/* Verify that control of this pipe is allowed */
+	if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CTRL) ||
+	    (dev->props.restricted_pipes & (1UL << pipe_index))) {
+		SPS_ERR(dev, "sps:BAM %pa pipe %d is not local\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Control without configuration permission is not supported yet */
+	if ((dev->props.manage & SPS_BAM_MGR_PIPE_NO_CONFIG)) {
+		SPS_ERR(dev,
+			"sps:BAM %pa pipe %d remote config is not supported\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Determine operational mode */
+	if (other_pipe->bam != NULL) {
+		unsigned long iova;
+		struct sps_bam *peer_bam = (struct sps_bam *)(other_pipe->bam);
+		/* BAM-to-BAM mode */
+		bam_pipe->state |= BAM_STATE_BAM2BAM;
+		hw_params.mode = BAM_PIPE_MODE_BAM2BAM;
+
+		if (dev->props.options & SPS_BAM_SMMU_EN) {
+			if (bam_pipe->mode == SPS_MODE_SRC)
+				iova = bam_pipe->connect.dest_iova;
+			else
+				iova = bam_pipe->connect.source_iova;
+			SPS_DBG2(dev,
+				"sps:BAM %pa pipe %d uses IOVA 0x%lx.\n",
+				 BAM_ID(dev), pipe_index, iova);
+			hw_params.peer_phys_addr = (u32)iova;
+		} else {
+			hw_params.peer_phys_addr = peer_bam->props.phys_addr;
+		}
+
+		hw_params.peer_pipe = other_pipe->pipe_index;
+
+		/* Verify FIFO buffers are allocated for BAM-to-BAM pipes */
+		if (map->desc.phys_base == SPS_ADDR_INVALID ||
+		    map->data.phys_base == SPS_ADDR_INVALID ||
+		    map->desc.size == 0 || map->data.size == 0) {
+			SPS_ERR(dev,
+				"sps:FIFO buffers are not allocated for BAM %pa pipe %d.\n",
+				BAM_ID(dev), pipe_index);
+			return SPS_ERROR;
+		}
+
+		if (dev->props.options & SPS_BAM_SMMU_EN) {
+			hw_params.data_base =
+				(phys_addr_t)bam_pipe->connect.data.iova;
+			SPS_DBG2(dev,
+				"sps:BAM %pa pipe %d uses IOVA 0x%lx for data FIFO.\n",
+				 BAM_ID(dev), pipe_index,
+				 bam_pipe->connect.data.iova);
+		} else {
+			hw_params.data_base = map->data.phys_base;
+		}
+
+		hw_params.data_size = map->data.size;
+
+		/* Clear the data FIFO for debug */
+		if (map->data.base != NULL && bam_pipe->mode == SPS_MODE_SRC)
+			memset_io(map->data.base, 0, hw_params.data_size);
+
+		/* set NWD bit for BAM2BAM producer pipe */
+		if (bam_pipe->mode == SPS_MODE_SRC) {
+			if ((params->options & SPS_O_WRITE_NWD) == 0)
+				hw_params.write_nwd = BAM_WRITE_NWD_DISABLE;
+			else
+				hw_params.write_nwd = BAM_WRITE_NWD_ENABLE;
+		}
+	} else {
+		/* System mode */
+		hw_params.mode = BAM_PIPE_MODE_SYSTEM;
+		bam_pipe->sys.desc_buf = map->desc.base;
+		bam_pipe->sys.desc_offset = 0;
+		bam_pipe->sys.acked_offset = 0;
+	}
+
+	/* Initialize the client pipe state */
+	bam_pipe->pipe_index = pipe_index;
+	bam_pipe->pipe_index_mask = 1UL << pipe_index;
+
+	/* Get virtual address for descriptor FIFO */
+	if (map->desc.phys_base != SPS_ADDR_INVALID) {
+		if (map->desc.size < (2 * sizeof(struct sps_iovec))) {
+			SPS_ERR(dev,
+				"sps:Invalid descriptor FIFO size for BAM %pa pipe %d: %d\n",
+				BAM_ID(dev), pipe_index, map->desc.size);
+			return SPS_ERROR;
+		}
+		desc_buf = map->desc.base;
+
+		/*
+		 * Note that descriptor base and size will be left zero from
+		 * the memset() above if the physical address was invalid.
+		 * This allows a satellite driver to set the FIFO as
+		 * local memory	for system mode.
+		 */
+
+		if (dev->props.options & SPS_BAM_SMMU_EN) {
+			hw_params.desc_base =
+				(phys_addr_t)bam_pipe->connect.desc.iova;
+			SPS_DBG2(dev,
+				"sps:BAM %pa pipe %d uses IOVA 0x%lx for desc FIFO.\n",
+				 BAM_ID(dev), pipe_index,
+				 bam_pipe->connect.desc.iova);
+		} else {
+			hw_params.desc_base = map->desc.phys_base;
+		}
+
+		hw_params.desc_size = map->desc.size;
+	}
+
+	/* Configure the descriptor FIFO for both operational modes */
+	if (desc_buf != NULL)
+		if (bam_pipe->mode == SPS_MODE_SRC ||
+		    hw_params.mode == BAM_PIPE_MODE_SYSTEM)
+			memset_io(desc_buf, 0, hw_params.desc_size);
+
+	bam_pipe->desc_size = hw_params.desc_size;
+	bam_pipe->num_descs = bam_pipe->desc_size / sizeof(struct sps_iovec);
+
+	result = SPS_ERROR;
+	/* Insure that the BAM is enabled */
+	if ((dev->state & BAM_STATE_ENABLED) == 0)
+		if (sps_bam_enable(dev))
+			goto exit_init_err;
+
+	/* Check pipe allocation */
+	if (dev->pipes[pipe_index] != BAM_PIPE_UNASSIGNED) {
+		SPS_ERR(dev, "sps:Invalid pipe %d on BAM %pa for connect\n",
+			pipe_index, BAM_ID(dev));
+		return SPS_ERROR;
+	}
+
+	if (bam_pipe_is_enabled(&dev->base, pipe_index)) {
+		if (params->options & SPS_O_NO_DISABLE)
+			SPS_DBG2(dev,
+				"sps:BAM %pa pipe %d is already enabled.\n",
+				BAM_ID(dev), pipe_index);
+		else {
+			SPS_ERR(dev, "sps:BAM %pa pipe %d sharing violation\n",
+				BAM_ID(dev), pipe_index);
+			return SPS_ERROR;
+		}
+	}
+
+	if (bam_pipe_init(&dev->base, pipe_index, &hw_params, dev->props.ee)) {
+		SPS_ERR(dev, "sps:BAM %pa pipe %d init error\n",
+			BAM_ID(dev), pipe_index);
+		goto exit_err;
+	}
+
+	/* Assign pipe to client */
+	dev->pipes[pipe_index] = bam_pipe;
+
+	/* Process configuration parameters */
+	if (params->options != 0 ||
+	    (bam_pipe->state & BAM_STATE_BAM2BAM) == 0) {
+		/* Process init-time only parameters */
+		u32 irq_gen_addr;
+
+		/* Set interrupt mode */
+		irq_gen_addr = SPS_ADDR_INVALID;
+		if ((params->options & SPS_O_IRQ_MTI))
+			/* Client has directly specified the MTI address */
+			irq_gen_addr = params->irq_gen_addr;
+		else if ((dev->state & BAM_STATE_MTI))
+			/* This BAM has MTI use enabled */
+			irq_gen_addr = dev->props.irq_gen_addr;
+
+		if (irq_gen_addr != SPS_ADDR_INVALID) {
+			/*
+			 * No checks - assume BAM is already setup for
+			 * MTI generation,
+			 * or the pipe will be set to satellite control.
+			 */
+			bam_pipe->state |= BAM_STATE_MTI;
+			bam_pipe->irq_gen_addr = irq_gen_addr;
+		}
+
+		/* Process runtime parameters */
+		if (sps_bam_pipe_set_params(dev, pipe_index,
+					  params->options)) {
+			dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED;
+			goto exit_err;
+		}
+	}
+
+	/* Indicate initialization is complete */
+	dev->pipes[pipe_index] = bam_pipe;
+	dev->pipe_active_mask |= 1UL << pipe_index;
+	list_add_tail(&bam_pipe->list, &dev->pipes_q);
+
+	SPS_DBG2(dev,
+		"sps:BAM %pa; pipe %d; pipe_index_mask:0x%x; pipe_active_mask:0x%x.\n",
+		BAM_ID(dev), pipe_index,
+		bam_pipe->pipe_index_mask, dev->pipe_active_mask);
+
+	bam_pipe->state |= BAM_STATE_INIT;
+	result = 0;
+exit_err:
+	if (result) {
+		if (params->options & SPS_O_NO_DISABLE)
+			SPS_DBG2(dev, "sps:BAM %pa pipe %d connection exits\n",
+				BAM_ID(dev), pipe_index);
+		else
+			bam_pipe_exit(&dev->base, pipe_index, dev->props.ee);
+	}
+exit_init_err:
+	if (result) {
+		/* Clear the client pipe state */
+		pipe_clear(bam_pipe);
+	}
+
+	return result;
+}
+
+/**
+ * Disconnect a BAM pipe connection
+ *
+ */
+int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe;
+	int result;
+	unsigned long flags;
+
+	if (pipe_index >= dev->props.num_pipes) {
+		SPS_ERR(dev, "sps:Invalid BAM %pa pipe: %d\n", BAM_ID(dev),
+				pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Deallocate and reset the BAM pipe */
+	pipe = dev->pipes[pipe_index];
+	if (BAM_PIPE_IS_ASSIGNED(pipe)) {
+		if ((dev->pipe_active_mask & (1UL << pipe_index))) {
+			spin_lock_irqsave(&dev->isr_lock, flags);
+			list_del(&pipe->list);
+			dev->pipe_active_mask &= ~(1UL << pipe_index);
+			spin_unlock_irqrestore(&dev->isr_lock, flags);
+		}
+		dev->pipe_remote_mask &= ~(1UL << pipe_index);
+		if (pipe->connect.options & SPS_O_NO_DISABLE)
+			SPS_DBG2(dev, "sps:BAM %pa pipe %d exits.\n",
+				BAM_ID(dev), pipe_index);
+		else
+			bam_pipe_exit(&dev->base, pipe_index, dev->props.ee);
+		if (pipe->sys.desc_cache != NULL) {
+			u32 size = pipe->num_descs * sizeof(void *);
+
+			if (pipe->desc_size + size <= PAGE_SIZE) {
+				if (dev->props.options & SPS_BAM_HOLD_MEM)
+					memset(pipe->sys.desc_cache, 0,
+						pipe->desc_size + size);
+				else
+					kfree(pipe->sys.desc_cache);
+			} else {
+				vfree(pipe->sys.desc_cache);
+			}
+			pipe->sys.desc_cache = NULL;
+		}
+		dev->pipes[pipe_index] = BAM_PIPE_UNASSIGNED;
+		pipe_clear(pipe);
+		result = 0;
+	} else {
+		result = SPS_ERROR;
+	}
+
+	if (result)
+		SPS_ERR(dev, "sps:BAM %pa pipe %d already disconnected\n",
+			BAM_ID(dev), pipe_index);
+
+	return result;
+}
+
+/**
+ * Set BAM pipe interrupt enable state
+ *
+ * This function sets the interrupt enable state for a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @poll - true if SPS_O_POLL is set, false otherwise
+ *
+ */
+static void pipe_set_irq(struct sps_bam *dev, u32 pipe_index,
+				 u32 poll)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	enum bam_enable irq_enable;
+
+	SPS_DBG2(dev,
+		"sps:BAM:%pa; pipe %d; poll:%d, irq_mask:0x%x; pipe state:0x%x; dev state:0x%x.\n",
+		BAM_ID(dev), pipe_index, poll, pipe->irq_mask,
+		pipe->state, dev->state);
+
+	if (poll == 0 && pipe->irq_mask != 0 &&
+	    (dev->state & BAM_STATE_IRQ)) {
+		if ((pipe->state & BAM_STATE_BAM2BAM) != 0 &&
+		    (pipe->state & BAM_STATE_IRQ) == 0) {
+			/*
+			 * If enabling the interrupt for a BAM-to-BAM pipe,
+			 * clear the existing interrupt status
+			 */
+			(void)bam_pipe_get_and_clear_irq_status(&dev->base,
+							   pipe_index);
+		}
+		pipe->state |= BAM_STATE_IRQ;
+		irq_enable = BAM_ENABLE;
+		pipe->polled = false;
+	} else {
+		pipe->state &= ~BAM_STATE_IRQ;
+		irq_enable = BAM_DISABLE;
+		pipe->polled = true;
+		if (poll == 0 && pipe->irq_mask)
+			SPS_DBG2(dev,
+				"sps:BAM %pa pipe %d forced to use polling\n",
+				 BAM_ID(dev), pipe_index);
+	}
+	if ((pipe->state & BAM_STATE_MTI) == 0)
+		bam_pipe_set_irq(&dev->base, pipe_index, irq_enable,
+					 pipe->irq_mask, dev->props.ee);
+	else
+		bam_pipe_set_mti(&dev->base, pipe_index, irq_enable,
+					 pipe->irq_mask, pipe->irq_gen_addr);
+
+}
+
+/**
+ * Set BAM pipe parameters
+ *
+ */
+int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	u32 mask;
+	int wake_up_is_one_shot;
+	int no_queue;
+	int ack_xfers;
+	u32 size;
+	int n;
+
+	SPS_DBG2(dev, "sps:BAM %pa pipe %d opt 0x%x\n",
+		BAM_ID(dev), pipe_index, options);
+
+	/* Capture some options */
+	wake_up_is_one_shot = ((options & SPS_O_WAKEUP_IS_ONESHOT));
+	no_queue = ((options & SPS_O_NO_Q));
+	ack_xfers = ((options & SPS_O_ACK_TRANSFERS));
+
+	pipe->hybrid = options & SPS_O_HYBRID;
+	pipe->late_eot = options & SPS_O_LATE_EOT;
+
+	/* Create interrupt source mask */
+	mask = 0;
+	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
+		/* Is client registering for this event? */
+		if ((options & opt_event_table[n].option) == 0)
+			continue;	/* No */
+
+		mask |= opt_event_table[n].pipe_irq;
+	}
+
+#ifdef SPS_BAM_STATISTICS
+	/* Is an illegal mode change specified? */
+	if (pipe->sys.desc_wr_count > 0 &&
+	    (no_queue != pipe->sys.no_queue
+	     || ack_xfers != pipe->sys.ack_xfers)) {
+		SPS_ERR(dev,
+			"sps:Queue/ack mode change after transfer: BAM %pa pipe %d opt 0x%x\n",
+			BAM_ID(dev), pipe_index, options);
+		return SPS_ERROR;
+	}
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Is client setting invalid options for a BAM-to-BAM connection? */
+	if ((pipe->state & BAM_STATE_BAM2BAM) &&
+	    (options & BAM2BAM_O_INVALID)) {
+		SPS_ERR(dev,
+			"sps:Invalid option for BAM-to-BAM: BAM %pa pipe %d opt 0x%x\n",
+			BAM_ID(dev), pipe_index, options);
+		return SPS_ERROR;
+	}
+
+	/* Allocate descriptor FIFO cache if NO_Q option is disabled */
+	if (!no_queue && pipe->sys.desc_cache == NULL && pipe->num_descs > 0
+	    && (pipe->state & BAM_STATE_BAM2BAM) == 0) {
+		/* Allocate both descriptor cache and user pointer array */
+		size = pipe->num_descs * sizeof(void *);
+
+		if (pipe->desc_size + size <= PAGE_SIZE) {
+			if ((dev->props.options &
+						SPS_BAM_HOLD_MEM)) {
+				if (dev->desc_cache_pointers[pipe_index]) {
+					pipe->sys.desc_cache =
+						dev->desc_cache_pointers
+							[pipe_index];
+				} else {
+					pipe->sys.desc_cache =
+						kzalloc(pipe->desc_size + size,
+								GFP_KERNEL);
+					dev->desc_cache_pointers[pipe_index] =
+							pipe->sys.desc_cache;
+				}
+			} else {
+				pipe->sys.desc_cache =
+						kzalloc(pipe->desc_size + size,
+							GFP_KERNEL);
+			}
+			if (pipe->sys.desc_cache == NULL) {
+				SPS_ERR(dev,
+					"sps:No memory for pipe%d of BAM %pa\n",
+						pipe_index, BAM_ID(dev));
+				return -ENOMEM;
+			}
+		} else {
+			pipe->sys.desc_cache =
+				vmalloc(pipe->desc_size + size);
+
+			if (pipe->sys.desc_cache == NULL) {
+				SPS_ERR(dev,
+					"sps:No memory for pipe %d of BAM %pa\n",
+					pipe_index, BAM_ID(dev));
+				return -ENOMEM;
+			}
+
+			memset(pipe->sys.desc_cache, 0, pipe->desc_size + size);
+		}
+
+		if (pipe->sys.desc_cache == NULL) {
+			/*** MUST BE LAST POINT OF FAILURE (see below) *****/
+			SPS_ERR(dev,
+				"sps:Desc cache error: BAM %pa pipe %d: %d\n",
+				BAM_ID(dev), pipe_index,
+				pipe->desc_size + size);
+			return SPS_ERROR;
+		}
+		pipe->sys.user_ptrs = (void **)(pipe->sys.desc_cache +
+						 pipe->desc_size);
+		pipe->sys.cache_offset = pipe->sys.acked_offset;
+	}
+
+	/*
+	 * No failures beyond this point. Note that malloc() is last point of
+	 * failure, so no free() handling is needed.
+	 */
+
+	/* Enable/disable the pipe's interrupt sources */
+	pipe->irq_mask = mask;
+	pipe_set_irq(dev, pipe_index, (options & SPS_O_POLL));
+
+	/* Store software feature enables */
+	pipe->wake_up_is_one_shot = wake_up_is_one_shot;
+	pipe->sys.no_queue = no_queue;
+	pipe->sys.ack_xfers = ack_xfers;
+
+	return 0;
+}
+
+/**
+ * Enable a BAM pipe
+ *
+ */
+int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	/* Enable the BAM pipe */
+	bam_pipe_enable(&dev->base, pipe_index);
+	pipe->state |= BAM_STATE_ENABLED;
+
+	return 0;
+}
+
+/**
+ * Disable a BAM pipe
+ *
+ */
+int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	/* Disable the BAM pipe */
+	if (pipe->connect.options & SPS_O_NO_DISABLE)
+		SPS_DBG2(dev, "sps:BAM %pa pipe %d enters disable state\n",
+			BAM_ID(dev), pipe_index);
+	else
+		bam_pipe_disable(&dev->base, pipe_index);
+
+	pipe->state &= ~BAM_STATE_ENABLED;
+
+	return 0;
+}
+
+/**
+ * Register an event for a BAM pipe
+ *
+ */
+int sps_bam_pipe_reg_event(struct sps_bam *dev,
+			   u32 pipe_index,
+			   struct sps_register_event *reg)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_bam_event_reg *event_reg;
+	int n;
+
+	if (pipe->sys.no_queue && reg->xfer_done != NULL &&
+	    reg->mode != SPS_TRIGGER_CALLBACK) {
+		SPS_ERR(dev,
+			"sps:Only callback events support for NO_Q: BAM %pa pipe %d mode %d\n",
+			BAM_ID(dev), pipe_index, reg->mode);
+		return SPS_ERROR;
+	}
+
+	for (n = 0; n < ARRAY_SIZE(opt_event_table); n++) {
+		int index;
+
+		/* Is client registering for this event? */
+		if ((reg->options & opt_event_table[n].option) == 0)
+			continue;	/* No */
+
+		index = SPS_EVENT_INDEX(opt_event_table[n].event_id);
+		if (index < 0)
+			SPS_ERR(dev,
+				"sps:Negative event index: BAM %pa pipe %d mode %d\n",
+				BAM_ID(dev), pipe_index, reg->mode);
+		else {
+			event_reg = &pipe->sys.event_regs[index];
+			event_reg->xfer_done = reg->xfer_done;
+			event_reg->callback = reg->callback;
+			event_reg->mode = reg->mode;
+			event_reg->user = reg->user;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Submit a transfer of a single buffer to a BAM pipe
+ *
+ */
+int sps_bam_pipe_transfer_one(struct sps_bam *dev,
+				    u32 pipe_index, u32 addr, u32 size,
+				    void *user, u32 flags)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_iovec *desc;
+	struct sps_iovec iovec;
+	u32 next_write;
+	static int show_recom;
+
+	SPS_DBG(dev, "sps:BAM %pa pipe %d addr 0x%x size 0x%x flags 0x%x\n",
+			BAM_ID(dev), pipe_index, addr, size, flags);
+
+	/* Is this a BAM-to-BAM or satellite connection? */
+	if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) {
+		SPS_ERR(dev, "sps:Transfer on BAM-to-BAM: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/*
+	 * Client identifier (user pointer) is not supported for
+	 * SPS_O_NO_Q option.
+	 */
+	if (pipe->sys.no_queue && user != NULL) {
+		SPS_ERR(dev, "sps:User pointer arg non-NULL: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Determine if descriptor can be queued */
+	next_write = pipe->sys.desc_offset + sizeof(struct sps_iovec);
+	if (next_write >= pipe->desc_size)
+		next_write = 0;
+
+	if (next_write == pipe->sys.acked_offset) {
+		/*
+		 * If pipe is polled and client is not ACK'ing descriptors,
+		 * perform polling operation so that any outstanding ACKs
+		 * can occur.
+		 */
+		if (!pipe->sys.ack_xfers && pipe->polled) {
+			pipe_handler_eot(dev, pipe);
+			if (next_write == pipe->sys.acked_offset) {
+				if (!show_recom) {
+					show_recom = true;
+					SPS_ERR(dev,
+						"sps:Client of BAM %pa pipe %d is recommended to have flow control\n",
+						BAM_ID(dev), pipe_index);
+				}
+
+				SPS_DBG1(dev,
+					"sps:Descriptor FIFO is full for BAM %pa pipe %d after pipe_handler_eot\n",
+					BAM_ID(dev), pipe_index);
+				return SPS_ERROR;
+			}
+		} else {
+			if (!show_recom) {
+				show_recom = true;
+				SPS_ERR(dev,
+					"sps:Client of BAM %pa pipe %d is recommended to have flow control.\n",
+					BAM_ID(dev), pipe_index);
+			}
+
+			SPS_DBG1(dev,
+				"sps:Descriptor FIFO is full for BAM %pa pipe %d\n",
+				BAM_ID(dev), pipe_index);
+			return SPS_ERROR;
+		}
+	}
+
+	/* Create descriptor */
+	if (!pipe->sys.no_queue)
+		desc = (struct sps_iovec *) (pipe->sys.desc_cache +
+					      pipe->sys.desc_offset);
+	else
+		desc = &iovec;
+
+	desc->addr = addr;
+	desc->size = size;
+
+	if ((flags & SPS_IOVEC_FLAG_DEFAULT) == 0) {
+		desc->flags = (flags & BAM_IOVEC_FLAG_MASK)
+				| DESC_UPPER_ADDR(flags);
+	} else {
+		if (pipe->mode == SPS_MODE_SRC)
+			desc->flags = SPS_IOVEC_FLAG_INT
+					| DESC_UPPER_ADDR(flags);
+		else
+			desc->flags = (SPS_IOVEC_FLAG_INT | SPS_IOVEC_FLAG_EOT)
+					| DESC_UPPER_ADDR(flags);
+	}
+
+#ifdef SPS_BAM_STATISTICS
+	if ((flags & SPS_IOVEC_FLAG_INT))
+		pipe->sys.int_flags++;
+	if ((flags & SPS_IOVEC_FLAG_EOT))
+		pipe->sys.eot_flags++;
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Update hardware descriptor FIFO - should result in burst */
+	*((struct sps_iovec *) (pipe->sys.desc_buf + pipe->sys.desc_offset))
+	= *desc;
+
+	/* Record user pointer value */
+	if (!pipe->sys.no_queue) {
+		u32 index = pipe->sys.desc_offset / sizeof(struct sps_iovec);
+
+		pipe->sys.user_ptrs[index] = user;
+#ifdef SPS_BAM_STATISTICS
+		if (user != NULL)
+			pipe->sys.user_ptrs_count++;
+#endif /* SPS_BAM_STATISTICS */
+	}
+
+	/* Update descriptor ACK offset */
+	pipe->sys.desc_offset = next_write;
+
+#ifdef SPS_BAM_STATISTICS
+	/* Update statistics */
+	pipe->sys.desc_wr_count++;
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Notify pipe */
+	if ((flags & SPS_IOVEC_FLAG_NO_SUBMIT) == 0) {
+		wmb(); /* Memory Barrier */
+		bam_pipe_set_desc_write_offset(&dev->base, pipe_index,
+					       next_write);
+	}
+
+	if (dev->ipc_loglevel == 0)
+		SPS_DBG(dev,
+			"sps:%s: BAM phy addr:%pa; pipe %d; write pointer to tell HW: 0x%x; write pointer read from HW: 0x%x\n",
+			__func__, BAM_ID(dev), pipe_index, next_write,
+			bam_pipe_get_desc_write_offset(&dev->base, pipe_index));
+
+	return 0;
+}
+
+/**
+ * Submit a transfer to a BAM pipe
+ *
+ */
+int sps_bam_pipe_transfer(struct sps_bam *dev,
+			 u32 pipe_index, struct sps_transfer *transfer)
+{
+	struct sps_iovec *iovec;
+	u32 count;
+	u32 flags;
+	void *user;
+	int n;
+	int result;
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	if (transfer->iovec_count == 0) {
+		SPS_ERR(dev, "sps:iovec count zero: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	if (!pipe->sys.ack_xfers && pipe->polled) {
+		sps_bam_pipe_get_unused_desc_num(dev, pipe_index,
+					&count);
+		count = pipe->desc_size / sizeof(struct sps_iovec) - count - 1;
+	} else
+		sps_bam_get_free_count(dev, pipe_index, &count);
+
+	if (count < transfer->iovec_count) {
+		SPS_ERR(dev,
+			"sps:Insufficient free desc: BAM %pa pipe %d: %d\n",
+			BAM_ID(dev), pipe_index, count);
+		return SPS_ERROR;
+	}
+
+	user = NULL;		/* NULL for all except last descriptor */
+	for (n = (int)transfer->iovec_count - 1, iovec = transfer->iovec;
+	    n >= 0; n--, iovec++) {
+		if (n > 0) {
+			/* This is *not* the last descriptor */
+			flags = iovec->flags | SPS_IOVEC_FLAG_NO_SUBMIT;
+		} else {
+			/* This *is* the last descriptor */
+			flags = iovec->flags;
+			user = transfer->user;
+		}
+		result = sps_bam_pipe_transfer_one(dev, pipe_index,
+						 iovec->addr,
+						 iovec->size, user,
+						 flags);
+		if (result)
+			return SPS_ERROR;
+	}
+
+	return 0;
+}
+
+int sps_bam_pipe_inject_zlt(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_iovec *desc;
+	u32 read_p, write_p, next_write;
+
+	if (pipe->state & BAM_STATE_BAM2BAM)
+		SPS_DBG2(dev, "sps: BAM-to-BAM pipe: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+	else
+		SPS_DBG2(dev, "sps: BAM-to-System pipe: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+
+	if (!(pipe->state & BAM_STATE_ENABLED)) {
+		SPS_ERR(dev,
+			"sps: BAM %pa pipe %d is not enabled.\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	read_p = bam_pipe_get_desc_read_offset(&dev->base, pipe_index);
+	write_p = bam_pipe_get_desc_write_offset(&dev->base, pipe_index);
+
+	SPS_DBG2(dev,
+		"sps: BAM %pa pipe %d: read pointer:0x%x; write pointer:0x%x.\n",
+		BAM_ID(dev), pipe_index, read_p, write_p);
+
+	if (read_p == write_p) {
+		SPS_ERR(dev,
+			"sps: BAM %pa pipe %d: read pointer 0x%x is already equal to write pointer.\n",
+			BAM_ID(dev), pipe_index, read_p);
+		return SPS_ERROR;
+	}
+
+	next_write = write_p + sizeof(struct sps_iovec);
+	if (next_write >= pipe->desc_size) {
+		SPS_DBG2(dev,
+			"sps: BAM %pa pipe %d: next write is 0x%x: wrap around.\n",
+			BAM_ID(dev), pipe_index, next_write);
+		next_write = 0;
+	}
+
+	desc = (struct sps_iovec *) (pipe->connect.desc.base + write_p);
+	desc->addr = 0;
+	desc->size = 0;
+	desc->flags = SPS_IOVEC_FLAG_EOT;
+
+	bam_pipe_set_desc_write_offset(&dev->base, pipe_index,
+					       next_write);
+	wmb(); /* update write pointer in HW */
+	SPS_DBG2(dev,
+		"sps: BAM %pa pipe %d: write pointer to tell HW: 0x%x; write pointer read from HW: 0x%x\n",
+		BAM_ID(dev), pipe_index, next_write,
+		bam_pipe_get_desc_write_offset(&dev->base, pipe_index));
+
+	return 0;
+}
+
+/**
+ * Allocate an event tracking struct
+ *
+ * This function allocates an event tracking struct.
+ *
+ * @pipe - pointer to pipe state
+ *
+ * @event_reg - pointer to event registration
+ *
+ * @return - pointer to event notification struct, or NULL
+ *
+ */
+static struct sps_q_event *alloc_event(struct sps_pipe *pipe,
+					struct sps_bam_event_reg *event_reg)
+{
+	struct sps_q_event *event;
+
+	/* A callback event object is registered, so trigger with payload */
+	event = &pipe->sys.event;
+	memset(event, 0, sizeof(*event));
+
+	return event;
+}
+
+/**
+ * Trigger an event notification
+ *
+ * This function triggers an event notification.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ * @event_reg - pointer to event registration
+ *
+ * @sps_event - pointer to event struct
+ *
+ */
+static void trigger_event(struct sps_bam *dev,
+			  struct sps_pipe *pipe,
+			  struct sps_bam_event_reg *event_reg,
+			  struct sps_q_event *sps_event)
+{
+	if (sps_event == NULL) {
+		SPS_DBG1(dev, "%s", "sps:trigger_event.sps_event is NULL.\n");
+		return;
+	}
+
+	if (event_reg->xfer_done) {
+		complete(event_reg->xfer_done);
+		SPS_DBG(dev, "sps:trigger_event.done=%d.\n",
+			event_reg->xfer_done->done);
+	}
+
+	if (event_reg->callback) {
+		SPS_DBG(dev, "%s", "sps:trigger_event.using callback.\n");
+		event_reg->callback(&sps_event->notify);
+	}
+
+}
+
+/**
+ * Handle a BAM pipe's generic interrupt sources
+ *
+ * This function creates the event notification for a BAM pipe's
+ *    generic interrupt sources.  The caller of this function must lock the BAM
+ *    device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ * @event_id - event identifier enum
+ *
+ */
+static void pipe_handler_generic(struct sps_bam *dev,
+			       struct sps_pipe *pipe,
+			       enum sps_event event_id)
+{
+	struct sps_bam_event_reg *event_reg;
+	struct sps_q_event *sps_event;
+	int index;
+
+	index = SPS_EVENT_INDEX(event_id);
+	if (index < 0 || index >= SPS_EVENT_INDEX(SPS_EVENT_MAX))
+		return;
+
+	event_reg = &pipe->sys.event_regs[index];
+	sps_event = alloc_event(pipe, event_reg);
+	if (sps_event != NULL) {
+		sps_event->notify.event_id = event_id;
+		sps_event->notify.user = event_reg->user;
+		trigger_event(dev, pipe, event_reg, sps_event);
+	}
+}
+
+/**
+ * Handle a BAM pipe's WAKEUP interrupt sources
+ *
+ * This function creates the event notification for a BAM pipe's
+ *    WAKEUP interrupt source.  The caller of this function must lock the BAM
+ *    device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ */
+static void pipe_handler_wakeup(struct sps_bam *dev, struct sps_pipe *pipe)
+{
+	struct sps_bam_event_reg *event_reg;
+	struct sps_q_event *event;
+	u32 pipe_index = pipe->pipe_index;
+
+	if (pipe->wake_up_is_one_shot) {
+		SPS_DBG2(dev,
+			"sps:BAM:%pa pipe %d wake_up_is_one_shot; irq_mask:0x%x.\n",
+			BAM_ID(dev), pipe_index, pipe->irq_mask);
+		/* Disable the pipe WAKEUP interrupt source */
+		pipe->irq_mask &= ~BAM_PIPE_IRQ_WAKE;
+		pipe_set_irq(dev, pipe_index, pipe->polled);
+	}
+
+	event_reg = &pipe->sys.event_regs[SPS_EVENT_INDEX(SPS_EVENT_WAKEUP)];
+	event = alloc_event(pipe, event_reg);
+	if (event != NULL) {
+		event->notify.event_id = SPS_EVENT_WAKEUP;
+		event->notify.user = event_reg->user;
+		trigger_event(dev, pipe, event_reg, event);
+	}
+}
+
+/**
+ * Handle a BAM pipe's EOT/INT interrupt sources
+ *
+ * This function creates the event notification for a BAM pipe's EOT interrupt
+ *    source.  The caller of this function must lock the BAM device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe - pointer to pipe state
+ *
+ */
+static void pipe_handler_eot(struct sps_bam *dev, struct sps_pipe *pipe)
+{
+	struct sps_bam_event_reg *event_reg;
+	struct sps_q_event *event;
+	struct sps_iovec *desc;
+	struct sps_iovec *cache;
+	void **user;
+	u32 *update_offset;
+	u32 pipe_index = pipe->pipe_index;
+	u32 offset;
+	u32 end_offset;
+	enum sps_event event_id;
+	u32 flags;
+	u32 enabled;
+	int producer = (pipe->mode == SPS_MODE_SRC);
+
+	if (pipe->sys.handler_eot) {
+		/*
+		 * This can happen if the pipe is configured for polling
+		 * (IRQ disabled) and callback event generation.
+		 * The client may perform a get_iovec() inside the callback.
+		 */
+		SPS_DBG(dev,
+			"sps:%s; still handling EOT for pipe %d.\n",
+			__func__, pipe->pipe_index);
+		return;
+	}
+
+	pipe->sys.handler_eot = true;
+
+	/* Get offset of last descriptor completed by the pipe */
+	end_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index);
+
+	if (dev->ipc_loglevel == 0)
+		SPS_DBG(dev,
+			"sps:%s; pipe index:%d; read pointer:0x%x; write pointer:0x%x; sys.acked_offset:0x%x.\n",
+			__func__, pipe->pipe_index, end_offset,
+			bam_pipe_get_desc_write_offset(&dev->base, pipe_index),
+			pipe->sys.acked_offset);
+
+	if (producer && pipe->late_eot) {
+		struct sps_iovec *desc_end;
+
+		if (end_offset == 0)
+			desc_end = (struct sps_iovec *)(pipe->sys.desc_buf
+				+ pipe->desc_size - sizeof(struct sps_iovec));
+		else
+			desc_end = (struct sps_iovec *)	(pipe->sys.desc_buf
+				+ end_offset - sizeof(struct sps_iovec));
+
+		if (!(desc_end->flags & SPS_IOVEC_FLAG_EOT)) {
+			if (end_offset == 0)
+				end_offset = pipe->desc_size
+					- sizeof(struct sps_iovec);
+			else
+				end_offset -= sizeof(struct sps_iovec);
+		}
+	}
+
+	/* If no queue, then do not generate any events */
+	if (pipe->sys.no_queue) {
+		if (!pipe->sys.ack_xfers) {
+			/* Client is not ACK'ing transfers, so do it now */
+			pipe->sys.acked_offset = end_offset;
+		}
+		pipe->sys.handler_eot = false;
+		SPS_DBG(dev,
+			"sps:%s; pipe %d has no queue.\n",
+			__func__, pipe->pipe_index);
+		return;
+	}
+
+	/*
+	 * Get offset of last descriptor processed by software,
+	 * and update to the last descriptor completed by the pipe
+	 */
+	if (!pipe->sys.ack_xfers) {
+		update_offset = &pipe->sys.acked_offset;
+		offset = *update_offset;
+	} else {
+		update_offset = &pipe->sys.cache_offset;
+		offset = *update_offset;
+	}
+
+	/* Are there any completed descriptors to process? */
+	if (offset == end_offset) {
+		pipe->sys.handler_eot = false;
+		SPS_DBG(dev,
+			"sps:%s; there is no completed desc to process for pipe %d.\n",
+			__func__, pipe->pipe_index);
+		return;
+	}
+
+	/* Determine enabled events */
+	enabled = 0;
+	if ((pipe->irq_mask & SPS_O_EOT))
+		enabled |= SPS_IOVEC_FLAG_EOT;
+
+	if ((pipe->irq_mask & SPS_O_DESC_DONE))
+		enabled |= SPS_IOVEC_FLAG_INT;
+
+	/*
+	 * For producer pipe, update the cached descriptor byte count and flags.
+	 * For consumer pipe, the BAM does not update the descriptors, so just
+	 * use the cached copies.
+	 */
+	if (producer) {
+		/*
+		 * Do copies in a tight loop to increase chance of
+		 * multi-descriptor burst accesses on the bus
+		 */
+		struct sps_iovec *desc_end;
+
+		/* Set starting point for copy */
+		desc = (struct sps_iovec *) (pipe->sys.desc_buf + offset);
+		cache =	(struct sps_iovec *) (pipe->sys.desc_cache + offset);
+
+		/* Fetch all completed descriptors to end of FIFO (wrap) */
+		if (end_offset < offset) {
+			desc_end = (struct sps_iovec *)
+				   (pipe->sys.desc_buf + pipe->desc_size);
+			while (desc < desc_end)
+				*cache++ = *desc++;
+
+			desc = (void *)pipe->sys.desc_buf;
+			cache = (void *)pipe->sys.desc_cache;
+		}
+
+		/* Fetch all remaining completed descriptors (no wrap) */
+		desc_end = (struct sps_iovec *)	(pipe->sys.desc_buf +
+						 end_offset);
+		while (desc < desc_end)
+			*cache++ = *desc++;
+	}
+
+	/* Process all completed descriptors */
+	cache = (struct sps_iovec *) (pipe->sys.desc_cache + offset);
+	user = &pipe->sys.user_ptrs[offset / sizeof(struct sps_iovec)];
+	for (;;) {
+		SPS_DBG(dev,
+			"sps:%s; pipe index:%d; iovec addr:0x%x; size:0x%x; flags:0x%x; enabled:0x%x; *user is %s NULL.\n",
+			__func__, pipe->pipe_index, cache->addr,
+			cache->size, cache->flags, enabled,
+			(*user == NULL) ? "" : "not");
+
+		/*
+		 * Increment offset to next descriptor and update pipe offset
+		 * so a client callback can fetch the I/O vector.
+		 */
+		offset += sizeof(struct sps_iovec);
+		if (offset >= pipe->desc_size)
+			/* Roll to start of descriptor FIFO */
+			offset = 0;
+
+		*update_offset = offset;
+#ifdef SPS_BAM_STATISTICS
+		pipe->sys.desc_rd_count++;
+#endif /* SPS_BAM_STATISTICS */
+
+		/* Did client request notification for this descriptor? */
+		flags = cache->flags & enabled;
+		if (*user != NULL || flags) {
+			int index;
+
+			if ((flags & SPS_IOVEC_FLAG_EOT))
+				event_id = SPS_EVENT_EOT;
+			else
+				event_id = SPS_EVENT_DESC_DONE;
+
+			index = SPS_EVENT_INDEX(event_id);
+			event_reg = &pipe->sys.event_regs[index];
+			event = alloc_event(pipe, event_reg);
+			if (event != NULL) {
+				/*
+				 * Store the descriptor and user pointer
+				 * in the notification
+				 */
+				event->notify.data.transfer.iovec = *cache;
+				event->notify.data.transfer.user = *user;
+
+				event->notify.event_id = event_id;
+				event->notify.user = event_reg->user;
+				trigger_event(dev, pipe, event_reg, event);
+			} else {
+				SPS_ERR(dev,
+					"sps: %s: pipe %d: event is NULL.\n",
+					__func__, pipe->pipe_index);
+			}
+#ifdef SPS_BAM_STATISTICS
+			if (*user != NULL)
+				pipe->sys.user_found++;
+#endif /* SPS_BAM_STATISTICS */
+		}
+
+		/* Increment to next descriptor */
+		if (offset == end_offset)
+			break;	/* No more descriptors */
+
+		if (offset) {
+			cache++;
+			user++;
+		} else {
+			cache = (void *)pipe->sys.desc_cache;
+			user = pipe->sys.user_ptrs;
+		}
+	}
+
+	pipe->sys.handler_eot = false;
+}
+
+/**
+ * Handle a BAM pipe's interrupt sources
+ *
+ * This function handles a BAM pipe's interrupt sources.
+ *    The caller of this function must lock the BAM device's mutex.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return void
+ *
+ */
+static void pipe_handler(struct sps_bam *dev, struct sps_pipe *pipe)
+{
+	u32 pipe_index;
+	u32 status;
+	enum sps_event event_id;
+
+	/* Get interrupt sources and ack all */
+	pipe_index = pipe->pipe_index;
+	status = bam_pipe_get_and_clear_irq_status(&dev->base, pipe_index);
+
+	SPS_DBG(dev, "sps:pipe_handler.bam %pa.pipe %d.status=0x%x.\n",
+			BAM_ID(dev), pipe_index, status);
+
+	/* Check for enabled interrupt sources */
+	status &= pipe->irq_mask;
+	if (status == 0)
+		/* No enabled interrupt sources are active */
+		return;
+
+	/*
+	 * Process the interrupt sources in order of frequency of occurrance.
+	 * Check for early exit opportunities.
+	 */
+
+	if ((status & (SPS_O_EOT | SPS_O_DESC_DONE)) &&
+	    (pipe->state & BAM_STATE_BAM2BAM) == 0) {
+		pipe_handler_eot(dev, pipe);
+		if (pipe->sys.no_queue) {
+			/*
+			 * EOT handler will not generate any event if there
+			 * is no queue,
+			 * so generate "empty" (no descriptor) event
+			 */
+			if ((status & SPS_O_EOT))
+				event_id = SPS_EVENT_EOT;
+			else
+				event_id = SPS_EVENT_DESC_DONE;
+
+			pipe_handler_generic(dev, pipe, event_id);
+		}
+		status &= ~(SPS_O_EOT | SPS_O_DESC_DONE);
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_WAKEUP)) {
+		pipe_handler_wakeup(dev, pipe);
+		status &= ~SPS_O_WAKEUP;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_INACTIVE)) {
+		pipe_handler_generic(dev, pipe, SPS_EVENT_INACTIVE);
+		status &= ~SPS_O_INACTIVE;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_OUT_OF_DESC)) {
+		pipe_handler_generic(dev, pipe,
+					     SPS_EVENT_OUT_OF_DESC);
+		status &= ~SPS_O_OUT_OF_DESC;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_RST_ERROR) && enhd_pipe) {
+		SPS_ERR(dev, "sps:bam %pa ;pipe 0x%x irq status=0x%x.\n"
+				"sps: BAM_PIPE_IRQ_RST_ERROR\n",
+				BAM_ID(dev), pipe_index, status);
+		bam_output_register_content(&dev->base, dev->props.ee);
+		pipe_handler_generic(dev, pipe,
+					     SPS_EVENT_RST_ERROR);
+		status &= ~SPS_O_RST_ERROR;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_O_HRESP_ERROR) && enhd_pipe) {
+		SPS_ERR(dev, "sps:bam %pa ;pipe 0x%x irq status=0x%x.\n"
+				"sps: BAM_PIPE_IRQ_HRESP_ERROR\n",
+				BAM_ID(dev), pipe_index, status);
+		bam_output_register_content(&dev->base, dev->props.ee);
+		pipe_handler_generic(dev, pipe,
+					     SPS_EVENT_HRESP_ERROR);
+		status &= ~SPS_O_HRESP_ERROR;
+		if (status == 0)
+			return;
+	}
+
+	if ((status & SPS_EVENT_ERROR))
+		pipe_handler_generic(dev, pipe, SPS_EVENT_ERROR);
+}
+
+/**
+ * Get a BAM pipe event
+ *
+ */
+int sps_bam_pipe_get_event(struct sps_bam *dev,
+			   u32 pipe_index, struct sps_event_notify *notify)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_q_event *event_queue;
+
+	if (pipe->sys.no_queue) {
+		SPS_ERR(dev,
+			"sps:Invalid connection for event: BAM %pa pipe %d context 0x%p\n",
+			BAM_ID(dev), pipe_index, pipe);
+		notify->event_id = SPS_EVENT_INVALID;
+		return SPS_ERROR;
+	}
+
+	/* If pipe is polled, perform polling operation */
+	if (pipe->polled && (pipe->state & BAM_STATE_BAM2BAM) == 0)
+		pipe_handler_eot(dev, pipe);
+
+	/* Pull an event off the synchronous event queue */
+	if (list_empty(&pipe->sys.events_q)) {
+		event_queue = NULL;
+		SPS_DBG(dev, "sps:events_q of bam %pa is empty.\n",
+							BAM_ID(dev));
+	} else {
+		SPS_DBG(dev, "sps:events_q of bam %pa is not empty.\n",
+			BAM_ID(dev));
+		event_queue =
+		list_first_entry(&pipe->sys.events_q, struct sps_q_event,
+				 list);
+		list_del(&event_queue->list);
+	}
+
+	/* Update client's event buffer */
+	if (event_queue == NULL) {
+		/* No event queued, so set client's event to "invalid" */
+		notify->event_id = SPS_EVENT_INVALID;
+	} else {
+		/*
+		 * Copy event into client's buffer and return the event
+		 * to the pool
+		 */
+		*notify = event_queue->notify;
+		kfree(event_queue);
+#ifdef SPS_BAM_STATISTICS
+		pipe->sys.get_events++;
+#endif /* SPS_BAM_STATISTICS */
+	}
+
+	return 0;
+}
+
+/**
+ * Get processed I/O vector
+ */
+int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_iovec *iovec)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	struct sps_iovec *desc;
+	u32 read_offset;
+
+	/* Is this a valid pipe configured for get_iovec use? */
+	if (!pipe->sys.ack_xfers ||
+	    (pipe->state & BAM_STATE_BAM2BAM) != 0 ||
+	    (pipe->state & BAM_STATE_REMOTE)) {
+		return SPS_ERROR;
+	}
+
+	/* If pipe is polled and queue is enabled, perform polling operation */
+	if ((pipe->polled || pipe->hybrid) && !pipe->sys.no_queue) {
+		SPS_DBG(dev,
+			"sps:%s; BAM: %pa; pipe index:%d; polled is %d; hybrid is %d.\n",
+			__func__, BAM_ID(dev), pipe_index,
+			pipe->polled, pipe->hybrid);
+		pipe_handler_eot(dev, pipe);
+	}
+
+	/* Is there a completed descriptor? */
+	if (pipe->sys.no_queue)
+		read_offset =
+		bam_pipe_get_desc_read_offset(&dev->base, pipe_index);
+	else
+		read_offset = pipe->sys.cache_offset;
+
+	if (read_offset == pipe->sys.acked_offset) {
+		/* No, so clear the iovec to indicate FIFO is empty */
+		memset(iovec, 0, sizeof(*iovec));
+		SPS_DBG(dev,
+			"sps:%s; BAM: %pa; pipe index:%d; no iovec to process.\n",
+			__func__, BAM_ID(dev), pipe_index);
+		return 0;
+	}
+
+	/* Fetch next descriptor */
+	desc = (struct sps_iovec *) (pipe->sys.desc_buf +
+				     pipe->sys.acked_offset);
+	*iovec = *desc;
+#ifdef SPS_BAM_STATISTICS
+	pipe->sys.get_iovecs++;
+#endif /* SPS_BAM_STATISTICS */
+
+	/* Update read/ACK offset */
+	pipe->sys.acked_offset += sizeof(struct sps_iovec);
+	if (pipe->sys.acked_offset >= pipe->desc_size)
+		pipe->sys.acked_offset = 0;
+
+	SPS_DBG(dev,
+		"sps:%s; pipe index:%d; iovec addr:0x%x; size:0x%x; flags:0x%x; acked_offset:0x%x.\n",
+		__func__, pipe->pipe_index, desc->addr,
+		desc->size, desc->flags, pipe->sys.acked_offset);
+
+	return 0;
+}
+
+/**
+ * Determine whether a BAM pipe descriptor FIFO is empty
+ *
+ */
+int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index,
+				u32 *empty)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	u32 end_offset;
+	u32 acked_offset;
+
+	/* Is this a satellite connection? */
+	if ((pipe->state & BAM_STATE_REMOTE)) {
+		SPS_ERR(dev, "sps:Is empty on remote: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Get offset of last descriptor completed by the pipe */
+	end_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index);
+
+	if ((pipe->state & BAM_STATE_BAM2BAM) == 0)
+		/* System mode */
+		acked_offset = pipe->sys.acked_offset;
+	else
+		/* BAM-to-BAM */
+		acked_offset = bam_pipe_get_desc_write_offset(&dev->base,
+							  pipe_index);
+
+
+	/* Determine descriptor FIFO state */
+	if (end_offset == acked_offset) {
+		*empty = true;
+	} else {
+		if ((pipe->state & BAM_STATE_BAM2BAM) == 0) {
+			*empty = false;
+			SPS_DBG1(dev,
+				"sps:%s; pipe index:%d; this sys2bam pipe is NOT empty.\n",
+				__func__, pipe->pipe_index);
+			return 0;
+		}
+		if (bam_pipe_check_zlt(&dev->base, pipe_index)) {
+			bool p_idc;
+			u32 next_write;
+
+			p_idc = bam_pipe_check_pipe_empty(&dev->base,
+								pipe_index);
+
+			next_write = acked_offset + sizeof(struct sps_iovec);
+			if (next_write >= pipe->desc_size)
+				next_write = 0;
+
+			if (next_write == end_offset) {
+				*empty = true;
+				if (!p_idc)
+					SPS_DBG3(dev,
+						"sps:BAM %pa pipe %d pipe empty checking for ZLT.\n",
+						BAM_ID(dev), pipe_index);
+			} else {
+				*empty = false;
+			}
+		} else {
+			*empty = false;
+		}
+	}
+
+	SPS_DBG1(dev,
+		"sps:%s; pipe index:%d; this pipe is %s empty.\n",
+		__func__, pipe->pipe_index, *empty ? "" : "NOT");
+
+	return 0;
+}
+
+/**
+ * Get number of free slots in a BAM pipe descriptor FIFO
+ *
+ */
+int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index,
+				 u32 *count)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+	u32 next_write;
+	u32 free;
+
+	/* Is this a BAM-to-BAM or satellite connection? */
+	if ((pipe->state & (BAM_STATE_BAM2BAM | BAM_STATE_REMOTE))) {
+		SPS_ERR(dev,
+			"sps:Free count on BAM-to-BAM or remote: BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+		*count = 0;
+		return SPS_ERROR;
+	}
+
+	/* Determine descriptor FIFO state */
+	next_write = pipe->sys.desc_offset + sizeof(struct sps_iovec);
+	if (next_write >= pipe->desc_size)
+		next_write = 0;
+
+	if (pipe->sys.acked_offset >= next_write)
+		free = pipe->sys.acked_offset - next_write;
+	else
+		free = pipe->desc_size - next_write + pipe->sys.acked_offset;
+
+	free /= sizeof(struct sps_iovec);
+	*count = free;
+
+	return 0;
+}
+
+/**
+ * Set BAM pipe to satellite ownership
+ *
+ */
+int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index)
+{
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	/*
+	 * Switch to satellite control is only supported on processor
+	 * that controls the BAM global config on multi-EE BAMs
+	 */
+	if ((dev->props.manage & SPS_BAM_MGR_MULTI_EE) == 0 ||
+	    (dev->props.manage & SPS_BAM_MGR_DEVICE_REMOTE)) {
+		SPS_ERR(dev,
+			"sps:Cannot grant satellite control to BAM %pa pipe %d\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Is this pipe locally controlled? */
+	if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) {
+		SPS_ERR(dev, "sps:BAM %pa pipe %d not local and active\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Disable local interrupts for this pipe */
+	if (!pipe->polled)
+		bam_pipe_set_irq(&dev->base, pipe_index, BAM_DISABLE,
+					 pipe->irq_mask, dev->props.ee);
+
+	if (BAM_VERSION_MTI_SUPPORT(dev->version)) {
+		/*
+		 * Set pipe to MTI interrupt mode.
+		 * Must be performed after IRQ disable,
+		 * because it is necessary to re-enable the IRQ to enable
+		 * MTI generation.
+		 * Set both pipe IRQ mask and MTI dest address to zero.
+		 */
+		if ((pipe->state & BAM_STATE_MTI) == 0 || pipe->polled) {
+			bam_pipe_satellite_mti(&dev->base, pipe_index, 0,
+						       dev->props.ee);
+			pipe->state |= BAM_STATE_MTI;
+		}
+	}
+
+	/* Indicate satellite control */
+	list_del(&pipe->list);
+	dev->pipe_active_mask &= ~(1UL << pipe_index);
+	dev->pipe_remote_mask |= pipe->pipe_index_mask;
+	pipe->state |= BAM_STATE_REMOTE;
+
+	return 0;
+}
+
+/**
+ * Perform BAM pipe timer control
+ *
+ */
+int sps_bam_pipe_timer_ctrl(struct sps_bam *dev,
+			    u32 pipe_index,
+			    struct sps_timer_ctrl *timer_ctrl,
+			    struct sps_timer_result *timer_result)
+{
+	enum bam_pipe_timer_mode mode;
+	int result = 0;
+
+	/* Is this pipe locally controlled? */
+	if ((dev->pipe_active_mask & (1UL << pipe_index)) == 0) {
+		SPS_ERR(dev, "sps:BAM %pa pipe %d not local and active\n",
+			BAM_ID(dev), pipe_index);
+		return SPS_ERROR;
+	}
+
+	/* Perform the timer operation */
+	switch (timer_ctrl->op) {
+	case SPS_TIMER_OP_CONFIG:
+		mode = (timer_ctrl->mode == SPS_TIMER_MODE_ONESHOT) ?
+			BAM_PIPE_TIMER_ONESHOT :
+			BAM_PIPE_TIMER_PERIODIC;
+		bam_pipe_timer_config(&dev->base, pipe_index, mode,
+				    timer_ctrl->timeout_msec * 8);
+		break;
+	case SPS_TIMER_OP_RESET:
+		bam_pipe_timer_reset(&dev->base, pipe_index);
+		break;
+	case SPS_TIMER_OP_READ:
+		break;
+	default:
+		result = SPS_ERROR;
+		break;
+	}
+
+	/* Provide the current timer value */
+	if (timer_result != NULL)
+		timer_result->current_timer =
+			bam_pipe_timer_get_count(&dev->base, pipe_index);
+
+	return result;
+}
+
+/**
+ * Get the number of unused descriptors in the descriptor FIFO
+ * of a pipe
+ */
+int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index,
+					u32 *desc_num)
+{
+	u32 sw_offset, peer_offset, fifo_size;
+	u32 desc_size = sizeof(struct sps_iovec);
+	struct sps_pipe *pipe = dev->pipes[pipe_index];
+
+	if (pipe == NULL)
+		return SPS_ERROR;
+
+	fifo_size = pipe->desc_size;
+
+	sw_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index);
+	if ((dev->props.options & SPS_BAM_CACHED_WP) &&
+		!(pipe->state & BAM_STATE_BAM2BAM)) {
+		peer_offset = pipe->sys.desc_offset;
+		SPS_DBG(dev,
+			"sps:BAM %pa pipe %d: peer offset in cache:0x%x\n",
+			BAM_ID(dev), pipe_index, peer_offset);
+	} else {
+		peer_offset = bam_pipe_get_desc_write_offset(&dev->base,
+				pipe_index);
+	}
+
+	if (sw_offset <= peer_offset)
+		*desc_num = (peer_offset - sw_offset) / desc_size;
+	else
+		*desc_num = (peer_offset + fifo_size - sw_offset) / desc_size;
+
+	return 0;
+}
+
+/*
+ * Check if a pipe of a BAM has any pending descriptor
+ */
+bool sps_bam_pipe_pending_desc(struct sps_bam *dev, u32 pipe_index)
+{
+	u32 sw_offset, peer_offset;
+
+	sw_offset = bam_pipe_get_desc_read_offset(&dev->base, pipe_index);
+	peer_offset = bam_pipe_get_desc_write_offset(&dev->base, pipe_index);
+
+	if (sw_offset == peer_offset)
+		return false;
+	else
+		return true;
+}
diff --git a/drivers/platform/msm/sps/sps_bam.h b/drivers/platform/msm/sps/sps_bam.h
new file mode 100644
index 0000000..468c492
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_bam.h
@@ -0,0 +1,616 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Function and data structure declarations for SPS BAM handling.
+ */
+
+
+#ifndef _SPSBAM_H_
+#define _SPSBAM_H_
+
+#include <linux/types.h>
+#include <linux/completion.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
+#include "spsi.h"
+
+#define BAM_HANDLE_INVALID         0
+
+#define to_sps_bam_dev(x) \
+	container_of((x), struct sps_bam, base)
+
+enum bam_irq {
+	BAM_DEV_IRQ_RDY_TO_SLEEP = 0x00000001,
+	BAM_DEV_IRQ_HRESP_ERROR = 0x00000002,
+	BAM_DEV_IRQ_ERROR = 0x00000004,
+	BAM_DEV_IRQ_TIMER = 0x00000010,
+};
+
+/* Pipe interrupt mask */
+enum bam_pipe_irq {
+	/* BAM finishes descriptor which has INT bit selected */
+	BAM_PIPE_IRQ_DESC_INT = 0x00000001,
+	/* Inactivity timer Expires */
+	BAM_PIPE_IRQ_TIMER = 0x00000002,
+	/* Wakeup peripheral (i.e. USB) */
+	BAM_PIPE_IRQ_WAKE = 0x00000004,
+	/* Producer - no free space for adding a descriptor */
+	/* Consumer - no descriptors for processing */
+	BAM_PIPE_IRQ_OUT_OF_DESC = 0x00000008,
+	/* Pipe Error interrupt */
+	BAM_PIPE_IRQ_ERROR = 0x00000010,
+	/* End-Of-Transfer */
+	BAM_PIPE_IRQ_EOT = 0x00000020,
+	/* Pipe RESET unsuccessful */
+	BAM_PIPE_IRQ_RST_ERROR = 0x00000040,
+	/* Errorneous Hresponse by AHB MASTER */
+	BAM_PIPE_IRQ_HRESP_ERROR = 0x00000080,
+};
+
+/* Halt Type */
+enum bam_halt {
+	BAM_HALT_OFF = 0,
+	BAM_HALT_ON = 1,
+};
+
+/* Threshold values of the DMA channels */
+enum bam_dma_thresh_dma {
+	BAM_DMA_THRESH_512 = 0x3,
+	BAM_DMA_THRESH_256 = 0x2,
+	BAM_DMA_THRESH_128 = 0x1,
+	BAM_DMA_THRESH_64 = 0x0,
+};
+
+/* Weight values of the DMA channels */
+enum bam_dma_weight_dma {
+	BAM_DMA_WEIGHT_HIGH = 7,
+	BAM_DMA_WEIGHT_MED = 3,
+	BAM_DMA_WEIGHT_LOW = 1,
+	BAM_DMA_WEIGHT_DEFAULT = BAM_DMA_WEIGHT_LOW,
+	BAM_DMA_WEIGHT_DISABLE = 0,
+};
+
+
+/* Invalid pipe index value */
+#define SPS_BAM_PIPE_INVALID  ((u32)(-1))
+
+/* Parameters for sps_bam_pipe_connect() */
+struct sps_bam_connect_param {
+	/* which end point must be initialized */
+	enum sps_mode mode;
+
+	/* OR'd connection end point options (see SPS_O defines) */
+	u32 options;
+
+	/* SETPEND/MTI interrupt generation parameters */
+	u32 irq_gen_addr;
+	u32 irq_gen_data;
+
+};
+
+/* Event registration struct */
+struct sps_bam_event_reg {
+	/* Client's event object handle */
+	struct completion *xfer_done;
+	void (*callback)(struct sps_event_notify *notify);
+
+	/* Event trigger mode */
+	enum sps_trigger mode;
+
+	/* User pointer that will be provided in event payload data */
+	void *user;
+
+};
+
+/* Descriptor FIFO cache entry */
+struct sps_bam_desc_cache {
+	struct sps_iovec iovec;
+	void *user; /* User pointer registered with this transfer */
+};
+
+/* Forward declaration */
+struct sps_bam;
+
+/* System mode control */
+struct sps_bam_sys_mode {
+	/* Descriptor FIFO control */
+	u8 *desc_buf; /* Descriptor FIFO for BAM pipe */
+	u32 desc_offset; /* Next new descriptor to be written to hardware */
+	u32 acked_offset; /* Next descriptor to be retired by software */
+
+	/* Descriptor cache control (!no_queue only) */
+	u8 *desc_cache; /* Software cache of descriptor FIFO contents */
+	u32 cache_offset; /* Next descriptor to be cached (ack_xfers only) */
+
+	/* User pointers associated with cached descriptors */
+	void **user_ptrs;
+
+	/* Event handling */
+	struct sps_bam_event_reg event_regs[SPS_EVENT_INDEX(SPS_EVENT_MAX)];
+	struct list_head events_q;
+
+	struct sps_q_event event;	/* Temp storage for event creation */
+	int no_queue;	/* Whether events are queued */
+	int ack_xfers;	/* Whether client must ACK all descriptors */
+	int handler_eot; /* Whether EOT handling is in progress (debug) */
+
+	/* Statistics */
+#ifdef SPS_BAM_STATISTICS
+	u32 desc_wr_count;
+	u32 desc_rd_count;
+	u32 user_ptrs_count;
+	u32 user_found;
+	u32 int_flags;
+	u32 eot_flags;
+	u32 callback_events;
+	u32 wait_events;
+	u32 queued_events;
+	u32 get_events;
+	u32 get_iovecs;
+#endif /* SPS_BAM_STATISTICS */
+};
+
+/* BAM pipe descriptor */
+struct sps_pipe {
+	struct list_head list;
+
+	/* Client state */
+	u32 client_state;
+	struct sps_bam *bam;
+	struct sps_connect connect;
+	const struct sps_connection *map;
+
+	/* Pipe parameters */
+	u32 state;
+	u32 pipe_index;
+	u32 pipe_index_mask;
+	u32 irq_mask;
+	int polled;
+	int hybrid;
+	bool late_eot;
+	u32 irq_gen_addr;
+	enum sps_mode mode;
+	u32 num_descs; /* Size (number of elements) of descriptor FIFO */
+	u32 desc_size; /* Size (bytes) of descriptor FIFO */
+	int wake_up_is_one_shot; /* Whether WAKEUP event is a one-shot or not */
+
+	/* System mode control */
+	struct sps_bam_sys_mode sys;
+
+	bool disconnecting;
+};
+
+/* BAM device descriptor */
+struct sps_bam {
+	struct list_head list;
+
+	/* BAM device properties, including connection defaults */
+	struct sps_bam_props props;
+
+	/* BAM device state */
+	u32 state;
+	struct mutex lock;
+	void *base; /* BAM virtual base address */
+	u32 version;
+	spinlock_t isr_lock;
+	spinlock_t connection_lock;
+	unsigned long irqsave_flags;
+
+	/* Pipe state */
+	u32 pipe_active_mask;
+	u32 pipe_remote_mask;
+	struct sps_pipe *pipes[BAM_MAX_PIPES];
+	struct list_head pipes_q;
+
+	/* Statistics */
+	u32 irq_from_disabled_pipe;
+	u32 event_trigger_failures;
+
+	void *ipc_log0;
+	void *ipc_log1;
+	void *ipc_log2;
+	void *ipc_log3;
+	void *ipc_log4;
+
+	u32 ipc_loglevel;
+
+	/* Desc cache pointers */
+	u8 *desc_cache_pointers[BAM_MAX_PIPES];
+};
+
+/**
+ * BAM driver initialization
+ *
+ * This function initializes the BAM driver.
+ *
+ * @options - driver options bitflags (see SPS_OPT_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_driver_init(u32 options);
+
+/**
+ * BAM device initialization
+ *
+ * This function initializes a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_device_init(struct sps_bam *dev);
+
+/**
+ * BAM device de-initialization
+ *
+ * This function de-initializes a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_device_de_init(struct sps_bam *dev);
+
+/**
+ * BAM device reset
+ *
+ * This Function resets a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_reset(struct sps_bam *dev);
+
+/**
+ * BAM device enable
+ *
+ * This function enables a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_enable(struct sps_bam *dev);
+
+/**
+ * BAM device disable
+ *
+ * This Function disables a BAM device.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_disable(struct sps_bam *dev);
+
+/**
+ * Allocate a BAM pipe
+ *
+ * This function allocates a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - client-specified pipe index, or SPS_BAM_PIPE_INVALID if
+ *    any available pipe is acceptable
+ *
+ * @return - allocated pipe index, or SPS_BAM_PIPE_INVALID on error
+ *
+ */
+u32 sps_bam_pipe_alloc(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Free a BAM pipe
+ *
+ * This function frees a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ */
+void sps_bam_pipe_free(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Establish BAM pipe connection
+ *
+ * This function establishes a connection for a BAM pipe (end point).
+ *
+ * @client - pointer to client pipe state struct
+ *
+ * @params - connection parameters
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_connect(struct sps_pipe *client,
+			const struct sps_bam_connect_param *params);
+
+/**
+ * Disconnect a BAM pipe connection
+ *
+ * This function disconnects a connection for a BAM pipe (end point).
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_disconnect(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Set BAM pipe parameters
+ *
+ * This function sets parameters for a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @options - bitflag options (see SPS_O_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_set_params(struct sps_bam *dev, u32 pipe_index, u32 options);
+
+/**
+ * Enable a BAM pipe
+ *
+ * This function enables a BAM pipe.  Note that this function
+ *    is separate from the pipe connect function to allow proper
+ *    sequencing of consumer enable followed by producer enable.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_enable(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Disable a BAM pipe
+ *
+ * This function disables a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_disable(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Register an event for a BAM pipe
+ *
+ * This function registers an event for a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @reg - pointer to event registration struct
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_reg_event(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_register_event *reg);
+
+/**
+ * Submit a transfer of a single buffer to a BAM pipe
+ *
+ * This function submits a transfer of a single buffer to a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @addr - physical address of buffer to transfer
+ *
+ * @size - number of bytes to transfer
+ *
+ * @user - user pointer to register for event
+ *
+ * @flags - descriptor flags (see SPS_IOVEC_FLAG defines)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_transfer_one(struct sps_bam *dev, u32 pipe_index, u32 addr,
+			      u32 size, void *user, u32 flags);
+
+/**
+ * Submit a transfer to a BAM pipe
+ *
+ * This function submits a transfer to a BAM pipe.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @transfer - pointer to transfer struct
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_transfer(struct sps_bam *dev, u32 pipe_index,
+			 struct sps_transfer *transfer);
+
+/**
+ * Get a BAM pipe event
+ *
+ * This function polls for a BAM pipe event.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @notify - pointer to event notification struct
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_get_event(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_event_notify *notify);
+
+/**
+ * Get processed I/O vector
+ *
+ * This function fetches the next processed I/O vector.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @iovec - Pointer to I/O vector struct (output).
+ *   This struct will be zeroed if there are no more processed I/O vectors.
+ *
+ * @return 0 on success, negative value on error
+ */
+int sps_bam_pipe_get_iovec(struct sps_bam *dev, u32 pipe_index,
+			   struct sps_iovec *iovec);
+
+/**
+ * Determine whether a BAM pipe descriptor FIFO is empty
+ *
+ * This function returns the empty state of a BAM pipe descriptor FIFO.
+ *
+ * The pipe mutex must be locked before calling this function.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @empty - pointer to client's empty status word (boolean)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_is_empty(struct sps_bam *dev, u32 pipe_index, u32 *empty);
+
+/**
+ * Get number of free slots in a BAM pipe descriptor FIFO
+ *
+ * This function returns the number of free slots in a BAM pipe descriptor FIFO.
+ *
+ * The pipe mutex must be locked before calling this function.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @count - pointer to count status
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_get_free_count(struct sps_bam *dev, u32 pipe_index, u32 *count);
+
+/**
+ * Set BAM pipe to satellite ownership
+ *
+ * This function sets the BAM pipe to satellite ownership.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_set_satellite(struct sps_bam *dev, u32 pipe_index);
+
+/**
+ * Perform BAM pipe timer control
+ *
+ * This function performs BAM pipe timer control operations.
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @timer_ctrl - Pointer to timer control specification
+ *
+ * @timer_result - Pointer to buffer for timer operation result.
+ *    This argument can be NULL if no result is expected for the operation.
+ *    If non-NULL, the current timer value will always provided.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_bam_pipe_timer_ctrl(struct sps_bam *dev, u32 pipe_index,
+			    struct sps_timer_ctrl *timer_ctrl,
+			    struct sps_timer_result *timer_result);
+
+
+/**
+ * Get the number of unused descriptors in the descriptor FIFO
+ * of a pipe
+ *
+ * @dev - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @desc_num - number of unused descriptors
+ *
+ */
+int sps_bam_pipe_get_unused_desc_num(struct sps_bam *dev, u32 pipe_index,
+					u32 *desc_num);
+
+/*
+ * sps_bam_check_irq - check IRQ of a BAM device.
+ * @dev - pointer to BAM device descriptor
+ *
+ * This function checks any pending interrupt of a BAM device.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int sps_bam_check_irq(struct sps_bam *dev);
+
+/*
+ * sps_bam_pipe_pending_desc - checking pending descriptor.
+ * @dev:	BAM device handle
+ * @pipe_index:	pipe index
+ *
+ * This function checks if a pipe of a BAM has any pending descriptor.
+ *
+ * @return true if there is any desc pending
+ */
+bool sps_bam_pipe_pending_desc(struct sps_bam *dev, u32 pipe_index);
+
+/*
+ * sps_bam_pipe_inject_zlt - inject a ZLT with EOT.
+ * @dev:	BAM device handle
+ * @pipe_index:	pipe index
+ *
+ * This function injects a ZLT with EOT for a pipe of a BAM.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int sps_bam_pipe_inject_zlt(struct sps_bam *dev, u32 pipe_index);
+#endif	/* _SPSBAM_H_ */
diff --git a/drivers/platform/msm/sps/sps_core.h b/drivers/platform/msm/sps/sps_core.h
new file mode 100644
index 0000000..592fef8
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_core.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2011, 2013, 2015-2016, The Linux Foundation. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Function and data structure declarations.
+ */
+
+#ifndef _SPS_CORE_H_
+#define _SPS_CORE_H_
+
+#include <linux/types.h>	/* u32 */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+
+#include "spsi.h"
+#include "sps_bam.h"
+
+/* Connection state definitions */
+#define SPS_STATE_DEF(x)   ('S' | ('P' << 8) | ('S' << 16) | ((x) << 24))
+#define IS_SPS_STATE_OK(x) \
+	(((x)->client_state & 0x00ffffff) == SPS_STATE_DEF(0))
+
+/* Configuration indicating satellite connection */
+#define SPS_CONFIG_SATELLITE  0x11111111
+
+/* Client connection state */
+#define SPS_STATE_DISCONNECT  0
+#define SPS_STATE_ALLOCATE    SPS_STATE_DEF(1)
+#define SPS_STATE_CONNECT     SPS_STATE_DEF(2)
+#define SPS_STATE_ENABLE      SPS_STATE_DEF(3)
+#define SPS_STATE_DISABLE     SPS_STATE_DEF(4)
+
+
+/**
+ * Find the BAM device from the handle
+ *
+ * This function finds a BAM device in the BAM registration list that
+ * matches the specified device handle.
+ *
+ * @h - device handle of the BAM
+ *
+ * @return - pointer to the BAM device struct, or NULL on error
+ *
+ */
+struct sps_bam *sps_h2bam(unsigned long h);
+
+/**
+ * Initialize resource manager module
+ *
+ * This function initializes the resource manager module.
+ *
+ * @rm - pointer to resource manager struct
+ *
+ * @options - driver options bitflags (see SPS_OPT_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_rm_init(struct sps_rm *rm, u32 options);
+
+/**
+ * De-initialize resource manager module
+ *
+ * This function de-initializes the resource manager module.
+ *
+ */
+void sps_rm_de_init(void);
+
+/**
+ * Initialize client state context
+ *
+ * This function initializes a client state context struct.
+ *
+ * @connect - pointer to client connection state struct
+ *
+ */
+void sps_rm_config_init(struct sps_connect *connect);
+
+/**
+ * Process connection state change
+ *
+ * This function processes a connection state change.
+ *
+ * @pipe - pointer to pipe context
+ *
+ * @state - new state for connection
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_rm_state_change(struct sps_pipe *pipe, u32 state);
+
+#endif				/* _SPS_CORE_H_ */
diff --git a/drivers/platform/msm/sps/sps_dma.c b/drivers/platform/msm/sps/sps_dma.c
new file mode 100644
index 0000000..abdcabc
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_dma.c
@@ -0,0 +1,925 @@
+/* Copyright (c) 2011-2013, 2015, 2017, The Linux Foundation. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* BAM-DMA Manager. */
+
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+
+#include <linux/export.h>
+#include <linux/memory.h>	/* memset */
+
+#include "spsi.h"
+#include "bam.h"
+#include "sps_bam.h"		/* bam_dma_thresh_dma */
+#include "sps_core.h"		/* sps_h2bam() */
+
+/**
+ * registers
+ */
+
+#define DMA_ENBL			(0x00000000)
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+#define DMA_REVISION			(0x00000004)
+#define DMA_CONFIG			(0x00000008)
+#define DMA_CHNL_CONFIG(n)		(0x00001000 + 4096 * (n))
+#else
+#define DMA_CHNL_CONFIG(n)		(0x00000004 + 4 * (n))
+#define DMA_CONFIG			(0x00000040)
+#endif
+
+/**
+ * masks
+ */
+
+/* DMA_CHNL_confign */
+#ifdef CONFIG_SPS_SUPPORT_NDP_BAM
+#define DMA_CHNL_PRODUCER_PIPE_ENABLED	0x40000
+#define DMA_CHNL_CONSUMER_PIPE_ENABLED	0x20000
+#endif
+#define DMA_CHNL_HALT_DONE		0x10000
+#define DMA_CHNL_HALT			0x1000
+#define DMA_CHNL_ENABLE                 0x100
+#define DMA_CHNL_ACT_THRESH             0x30
+#define DMA_CHNL_WEIGHT                 0x7
+
+/* DMA_CONFIG */
+#define TESTBUS_SELECT                  0x3
+
+/**
+ *
+ * Write register with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @val - value to write.
+ *
+ */
+static inline void dma_write_reg(void *base, u32 offset, u32 val)
+{
+	iowrite32(val, base + offset);
+	SPS_DBG(sps, "sps:bamdma: write reg 0x%x w_val 0x%x.", offset, val);
+}
+
+/**
+ * Write register masked field with debug info.
+ *
+ * @base - bam base virtual address.
+ * @offset - register offset.
+ * @mask - register bitmask.
+ * @val - value to write.
+ *
+ */
+static inline void dma_write_reg_field(void *base, u32 offset,
+				       const u32 mask, u32 val)
+{
+	u32 shift = find_first_bit((void *)&mask, 32);
+	u32 tmp = ioread32(base + offset);
+
+	tmp &= ~mask;		/* clear written bits */
+	val = tmp | (val << shift);
+	iowrite32(val, base + offset);
+	SPS_DBG(sps, "sps:bamdma: write reg 0x%x w_val 0x%x.", offset, val);
+}
+
+/* Round max number of pipes to nearest multiple of 2 */
+#define DMA_MAX_PIPES         ((BAM_MAX_PIPES / 2) * 2)
+
+/* Maximum number of BAM-DMAs supported */
+#define MAX_BAM_DMA_DEVICES   1
+
+/* Maximum number of BAMs that will be registered */
+#define MAX_BAM_DMA_BAMS      1
+
+/* Pipe enable check values */
+#define DMA_PIPES_STATE_DIFF     0
+#define DMA_PIPES_BOTH_DISABLED  1
+#define DMA_PIPES_BOTH_ENABLED   2
+
+/* Even pipe is tx/dest/input/write, odd pipe is rx/src/output/read */
+#define DMA_PIPE_IS_DEST(p)   (((p) & 1) == 0)
+#define DMA_PIPE_IS_SRC(p)    (((p) & 1) != 0)
+
+/* BAM DMA pipe state */
+enum bamdma_pipe_state {
+	PIPE_INACTIVE = 0,
+	PIPE_ACTIVE
+};
+
+/* BAM DMA channel state */
+enum bamdma_chan_state {
+	DMA_CHAN_STATE_FREE = 0,
+	DMA_CHAN_STATE_ALLOC_EXT,	/* Client allocation */
+	DMA_CHAN_STATE_ALLOC_INT	/* Internal (resource mgr) allocation */
+};
+
+struct bamdma_chan {
+	/* Allocation state */
+	enum bamdma_chan_state state;
+
+	/* BAM DMA channel configuration parameters */
+	u32 threshold;
+	enum sps_dma_priority priority;
+
+	/* HWIO channel configuration parameters */
+	enum bam_dma_thresh_dma thresh;
+	enum bam_dma_weight_dma weight;
+
+};
+
+/* BAM DMA device state */
+struct bamdma_device {
+	/* BAM-DMA device state */
+	int enabled;
+	int local;
+
+	/* BAM device state */
+	struct sps_bam *bam;
+
+	/* BAM handle, for deregistration */
+	unsigned long h;
+
+	/* BAM DMA device virtual mapping */
+	void *virt_addr;
+	int virtual_mapped;
+	phys_addr_t phys_addr;
+	void *hwio;
+
+	/* BAM DMA pipe/channel state */
+	u32 num_pipes;
+	enum bamdma_pipe_state pipes[DMA_MAX_PIPES];
+	struct bamdma_chan chans[DMA_MAX_PIPES / 2];
+
+};
+
+/* BAM-DMA devices */
+static struct bamdma_device bam_dma_dev[MAX_BAM_DMA_DEVICES];
+static struct mutex bam_dma_lock;
+
+/*
+ * The BAM DMA module registers all BAMs in the BSP properties, but only
+ * uses the first BAM-DMA device for allocations.  References to the others
+ * are stored in the following data array.
+ */
+static int num_bams;
+static unsigned long bam_handles[MAX_BAM_DMA_BAMS];
+
+/**
+ * Find BAM-DMA device
+ *
+ * This function finds the BAM-DMA device associated with the BAM handle.
+ *
+ * @h - BAM handle
+ *
+ * @return - pointer to BAM-DMA device, or NULL on error
+ *
+ */
+static struct bamdma_device *sps_dma_find_device(unsigned long h)
+{
+	return &bam_dma_dev[0];
+}
+
+/**
+ * BAM DMA device enable
+ *
+ * This function enables a BAM DMA device and the associated BAM.
+ *
+ * @dev - pointer to BAM DMA device context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_dma_device_enable(struct bamdma_device *dev)
+{
+	if (dev->enabled)
+		return 0;
+
+	/*
+	 *  If the BAM-DMA device is locally controlled then enable BAM-DMA
+	 *  device
+	 */
+	if (dev->local)
+		dma_write_reg(dev->virt_addr, DMA_ENBL, 1);
+
+	/* Enable BAM device */
+	if (sps_bam_enable(dev->bam)) {
+		SPS_ERR(sps, "sps:Failed to enable BAM DMA's BAM: %pa",
+			&dev->phys_addr);
+		return SPS_ERROR;
+	}
+
+	dev->enabled = true;
+
+	return 0;
+}
+
+/**
+ * BAM DMA device enable
+ *
+ * This function initializes a BAM DMA device.
+ *
+ * @dev - pointer to BAM DMA device context
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_dma_device_disable(struct bamdma_device *dev)
+{
+	u32 pipe_index;
+
+	if (!dev->enabled)
+		return 0;
+
+	/* Do not disable if channels active */
+	for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) {
+		if (dev->pipes[pipe_index] != PIPE_INACTIVE)
+			break;
+	}
+
+	if (pipe_index < dev->num_pipes) {
+		SPS_ERR(sps,
+			"sps:Fail to disable BAM-DMA %pa:channels are active",
+			&dev->phys_addr);
+		return SPS_ERROR;
+	}
+
+	dev->enabled = false;
+
+	/* Disable BAM device */
+	if (sps_bam_disable(dev->bam)) {
+		SPS_ERR(sps,
+			"sps:Fail to disable BAM-DMA BAM:%pa", &dev->phys_addr);
+		return SPS_ERROR;
+	}
+
+	/* Is the BAM-DMA device locally controlled? */
+	if (dev->local)
+		/* Disable BAM-DMA device */
+		dma_write_reg(dev->virt_addr, DMA_ENBL, 0);
+
+	return 0;
+}
+
+/**
+ * Initialize BAM DMA device
+ *
+ */
+int sps_dma_device_init(unsigned long h)
+{
+	struct bamdma_device *dev;
+	struct sps_bam_props *props;
+	int result = SPS_ERROR;
+
+	mutex_lock(&bam_dma_lock);
+
+	/* Find a free BAM-DMA device slot */
+	dev = NULL;
+	if (bam_dma_dev[0].bam != NULL) {
+		SPS_ERR(sps,
+			"sps:%s:BAM-DMA BAM device is already initialized.",
+			__func__);
+		goto exit_err;
+	} else {
+		dev = &bam_dma_dev[0];
+	}
+
+	/* Record BAM */
+	memset(dev, 0, sizeof(*dev));
+	dev->h = h;
+	dev->bam = sps_h2bam(h);
+
+	if (dev->bam == NULL) {
+		SPS_ERR(sps,
+			"sps:%s:BAM-DMA BAM device is not found from the handle.",
+			__func__);
+		goto exit_err;
+	}
+
+	/* Map the BAM DMA device into virtual space, if necessary */
+	props = &dev->bam->props;
+	dev->phys_addr = props->periph_phys_addr;
+	if (props->periph_virt_addr != NULL) {
+		dev->virt_addr = props->periph_virt_addr;
+		dev->virtual_mapped = false;
+	} else {
+		if (props->periph_virt_size == 0) {
+			SPS_ERR(sps,
+				"sps:Unable to map BAM DMA IO memory: %pa %x",
+				&dev->phys_addr, props->periph_virt_size);
+			goto exit_err;
+		}
+
+		dev->virt_addr = ioremap(dev->phys_addr,
+					  props->periph_virt_size);
+		if (dev->virt_addr == NULL) {
+			SPS_ERR(sps,
+				"sps:Unable to map BAM DMA IO memory: %pa %x",
+				&dev->phys_addr, props->periph_virt_size);
+			goto exit_err;
+		}
+		dev->virtual_mapped = true;
+	}
+	dev->hwio = (void *) dev->virt_addr;
+
+	/* Is the BAM-DMA device locally controlled? */
+	if ((props->manage & SPS_BAM_MGR_DEVICE_REMOTE) == 0) {
+		SPS_DBG3(sps, "sps:BAM-DMA is controlled locally: %pa",
+			&dev->phys_addr);
+		dev->local = true;
+	} else {
+		SPS_DBG3(sps, "sps:BAM-DMA is controlled remotely: %pa",
+			&dev->phys_addr);
+		dev->local = false;
+	}
+
+	/*
+	 * Enable the BAM DMA and determine the number of pipes/channels.
+	 * Leave the BAM-DMA enabled, since it is always a shared device.
+	 */
+	if (sps_dma_device_enable(dev))
+		goto exit_err;
+
+	dev->num_pipes = dev->bam->props.num_pipes;
+
+	result = 0;
+exit_err:
+	if (result) {
+		if (dev != NULL) {
+			if (dev->virtual_mapped)
+				iounmap(dev->virt_addr);
+
+			dev->bam = NULL;
+		}
+	}
+
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * De-initialize BAM DMA device
+ *
+ */
+int sps_dma_device_de_init(unsigned long h)
+{
+	struct bamdma_device *dev;
+	u32 pipe_index;
+	u32 chan;
+	int result = 0;
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device(h);
+	if (dev == NULL) {
+		SPS_ERR(sps, "sps:BAM-DMA: not registered: %lx", h);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Check for channel leaks */
+	for (chan = 0; chan < dev->num_pipes / 2; chan++) {
+		if (dev->chans[chan].state != DMA_CHAN_STATE_FREE) {
+			SPS_ERR(sps, "sps:BAM-DMA: channel not free: %d", chan);
+			result = SPS_ERROR;
+			dev->chans[chan].state = DMA_CHAN_STATE_FREE;
+		}
+	}
+	for (pipe_index = 0; pipe_index < dev->num_pipes; pipe_index++) {
+		if (dev->pipes[pipe_index] != PIPE_INACTIVE) {
+			SPS_ERR(sps, "sps:BAM-DMA: pipe not inactive: %d",
+					pipe_index);
+			result = SPS_ERROR;
+			dev->pipes[pipe_index] = PIPE_INACTIVE;
+		}
+	}
+
+	/* Disable BAM and BAM-DMA */
+	if (sps_dma_device_disable(dev))
+		result = SPS_ERROR;
+
+	dev->h = BAM_HANDLE_INVALID;
+	dev->bam = NULL;
+	if (dev->virtual_mapped)
+		iounmap(dev->virt_addr);
+
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Initialize BAM DMA module
+ *
+ */
+int sps_dma_init(const struct sps_bam_props *bam_props)
+{
+	struct sps_bam_props props;
+	const struct sps_bam_props *bam_reg;
+	unsigned long h;
+
+	/* Init local data */
+	memset(&bam_dma_dev, 0, sizeof(bam_dma_dev));
+	num_bams = 0;
+	memset(bam_handles, 0, sizeof(bam_handles));
+
+	/* Create a mutex to control access to the BAM-DMA devices */
+	mutex_init(&bam_dma_lock);
+
+	/* Are there any BAM DMA devices? */
+	if (bam_props == NULL)
+		return 0;
+
+	/*
+	 * Registers all BAMs in the BSP properties, but only uses the first
+	 * BAM-DMA device for allocations.
+	 */
+	if (bam_props->phys_addr) {
+		/* Force multi-EE option for all BAM-DMAs */
+		bam_reg = bam_props;
+		if ((bam_props->options & SPS_BAM_OPT_BAMDMA) &&
+		    (bam_props->manage & SPS_BAM_MGR_MULTI_EE) == 0) {
+			SPS_DBG(sps,
+				"sps:Setting multi-EE options for BAM-DMA: %pa",
+				&bam_props->phys_addr);
+			props = *bam_props;
+			props.manage |= SPS_BAM_MGR_MULTI_EE;
+			bam_reg = &props;
+		}
+
+		/* Register the BAM */
+		if (sps_register_bam_device(bam_reg, &h)) {
+			SPS_ERR(sps,
+				"sps:Fail to register BAM-DMA BAM device: phys %pa",
+				&bam_props->phys_addr);
+			return SPS_ERROR;
+		}
+
+		/* Record the BAM so that it may be deregistered later */
+		if (num_bams < MAX_BAM_DMA_BAMS) {
+			bam_handles[num_bams] = h;
+			num_bams++;
+		} else {
+			SPS_ERR(sps, "sps:BAM-DMA: BAM limit exceeded: %d",
+					num_bams);
+			return SPS_ERROR;
+		}
+	} else {
+		SPS_ERR(sps,
+			"sps:%s:BAM-DMA phys_addr is zero.",
+			__func__);
+		return SPS_ERROR;
+	}
+
+
+	return 0;
+}
+
+/**
+ * De-initialize BAM DMA module
+ *
+ */
+void sps_dma_de_init(void)
+{
+	int n;
+
+	/* De-initialize the BAM devices */
+	for (n = 0; n < num_bams; n++)
+		sps_deregister_bam_device(bam_handles[n]);
+
+	/* Clear local data */
+	memset(&bam_dma_dev, 0, sizeof(bam_dma_dev));
+	num_bams = 0;
+	memset(bam_handles, 0, sizeof(bam_handles));
+}
+
+/**
+ * Allocate a BAM DMA channel
+ *
+ */
+int sps_alloc_dma_chan(const struct sps_alloc_dma_chan *alloc,
+		       struct sps_dma_chan *chan_info)
+{
+	struct bamdma_device *dev;
+	struct bamdma_chan *chan;
+	u32 pipe_index;
+	enum bam_dma_thresh_dma thresh = (enum bam_dma_thresh_dma) 0;
+	enum bam_dma_weight_dma weight = (enum bam_dma_weight_dma) 0;
+	int result = SPS_ERROR;
+
+	if (alloc == NULL || chan_info == NULL) {
+		SPS_ERR(sps,
+			"sps:%s:invalid parameters", __func__);
+		return SPS_ERROR;
+	}
+
+	/* Translate threshold and priority to hwio values */
+	if (alloc->threshold != SPS_DMA_THRESHOLD_DEFAULT) {
+		if (alloc->threshold >= 512)
+			thresh = BAM_DMA_THRESH_512;
+		else if (alloc->threshold >= 256)
+			thresh = BAM_DMA_THRESH_256;
+		else if (alloc->threshold >= 128)
+			thresh = BAM_DMA_THRESH_128;
+		else
+			thresh = BAM_DMA_THRESH_64;
+	}
+
+	weight = alloc->priority;
+
+	if ((u32)alloc->priority > (u32)BAM_DMA_WEIGHT_HIGH) {
+		SPS_ERR(sps, "sps:BAM-DMA: invalid priority: %x",
+						alloc->priority);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device(alloc->dev);
+	if (dev == NULL) {
+		SPS_ERR(sps, "sps:BAM-DMA: invalid BAM handle: %lx",
+							alloc->dev);
+		goto exit_err;
+	}
+
+	/* Search for a free set of pipes */
+	for (pipe_index = 0, chan = dev->chans;
+	      pipe_index < dev->num_pipes; pipe_index += 2, chan++) {
+		if (chan->state == DMA_CHAN_STATE_FREE) {
+			/* Just check pipes for safety */
+			if (dev->pipes[pipe_index] != PIPE_INACTIVE ||
+			    dev->pipes[pipe_index + 1] != PIPE_INACTIVE) {
+				SPS_ERR(sps,
+					"sps:BAM-DMA: channel %d state error:%d %d",
+					pipe_index / 2, dev->pipes[pipe_index],
+				 dev->pipes[pipe_index + 1]);
+				goto exit_err;
+			}
+			break; /* Found free pipe */
+		}
+	}
+
+	if (pipe_index >= dev->num_pipes) {
+		SPS_ERR(sps, "sps:BAM-DMA: no free channel. num_pipes = %d",
+			dev->num_pipes);
+		goto exit_err;
+	}
+
+	chan->state = DMA_CHAN_STATE_ALLOC_EXT;
+
+	/* Store config values for use when pipes are activated */
+	chan = &dev->chans[pipe_index / 2];
+	chan->threshold = alloc->threshold;
+	chan->thresh = thresh;
+	chan->priority = alloc->priority;
+	chan->weight = weight;
+
+	SPS_DBG3(sps, "sps:sps_alloc_dma_chan. pipe %d.\n", pipe_index);
+
+	/* Report allocated pipes to client */
+	chan_info->dev = dev->h;
+	/* Dest/input/write pipex */
+	chan_info->dest_pipe_index = pipe_index;
+	/* Source/output/read pipe */
+	chan_info->src_pipe_index = pipe_index + 1;
+
+	result = 0;
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_alloc_dma_chan);
+
+/**
+ * Free a BAM DMA channel
+ *
+ */
+int sps_free_dma_chan(struct sps_dma_chan *chan)
+{
+	struct bamdma_device *dev;
+	u32 pipe_index;
+	int result = 0;
+
+	if (chan == NULL) {
+		SPS_ERR(sps,
+			"sps:%s:chan is NULL", __func__);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device(chan->dev);
+	if (dev == NULL) {
+		SPS_ERR(sps, "sps:BAM-DMA: invalid BAM handle: %lx", chan->dev);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Verify the pipe indices */
+	pipe_index = chan->dest_pipe_index;
+	if (pipe_index >= dev->num_pipes || ((pipe_index & 1)) ||
+	    (pipe_index + 1) != chan->src_pipe_index) {
+		SPS_ERR(sps,
+			"sps:sps_free_dma_chan. Invalid pipe indices. num_pipes=%d.dest=%d.src=%d.",
+			dev->num_pipes,
+			chan->dest_pipe_index,
+			chan->src_pipe_index);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Are both pipes inactive? */
+	if (dev->chans[pipe_index / 2].state != DMA_CHAN_STATE_ALLOC_EXT ||
+	    dev->pipes[pipe_index] != PIPE_INACTIVE ||
+	    dev->pipes[pipe_index + 1] != PIPE_INACTIVE) {
+		SPS_ERR(sps,
+			"sps:BAM-DMA: attempt to free active chan %d: %d %d",
+			pipe_index / 2, dev->pipes[pipe_index],
+			dev->pipes[pipe_index + 1]);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	/* Free the channel */
+	dev->chans[pipe_index / 2].state = DMA_CHAN_STATE_FREE;
+
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+EXPORT_SYMBOL(sps_free_dma_chan);
+
+/**
+ * Activate a BAM DMA pipe
+ *
+ * This function activates a BAM DMA pipe.
+ *
+ * @dev - pointer to BAM-DMA device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static u32 sps_dma_check_pipes(struct bamdma_device *dev, u32 pipe_index)
+{
+	u32 pipe_in;
+	u32 pipe_out;
+	int enabled_in;
+	int enabled_out;
+	u32 check;
+
+	pipe_in = pipe_index & ~1;
+	pipe_out = pipe_in + 1;
+	enabled_in = bam_pipe_is_enabled(&dev->bam->base, pipe_in);
+	enabled_out = bam_pipe_is_enabled(&dev->bam->base, pipe_out);
+
+	if (!enabled_in && !enabled_out)
+		check = DMA_PIPES_BOTH_DISABLED;
+	else if (enabled_in && enabled_out)
+		check = DMA_PIPES_BOTH_ENABLED;
+	else
+		check = DMA_PIPES_STATE_DIFF;
+
+	return check;
+}
+
+/**
+ * Allocate a BAM DMA pipe
+ *
+ */
+int sps_dma_pipe_alloc(void *bam_arg, u32 pipe_index, enum sps_mode dir)
+{
+	struct sps_bam *bam = bam_arg;
+	struct bamdma_device *dev;
+	struct bamdma_chan *chan;
+	u32 channel;
+	int result = SPS_ERROR;
+
+	if (bam == NULL) {
+		SPS_ERR(sps, "%s", "sps:BAM context is NULL");
+		return SPS_ERROR;
+	}
+
+	/* Check pipe direction */
+	if ((DMA_PIPE_IS_DEST(pipe_index) && dir != SPS_MODE_DEST) ||
+	    (DMA_PIPE_IS_SRC(pipe_index) && dir != SPS_MODE_SRC)) {
+		SPS_ERR(sps, "sps:BAM-DMA: wrong direction for BAM %pa pipe %d",
+			&bam->props.phys_addr, pipe_index);
+		return SPS_ERROR;
+	}
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device((unsigned long) bam);
+	if (dev == NULL) {
+		SPS_ERR(sps, "sps:BAM-DMA: invalid BAM: %pa",
+			&bam->props.phys_addr);
+		goto exit_err;
+	}
+	if (pipe_index >= dev->num_pipes) {
+		SPS_ERR(sps, "sps:BAM-DMA: BAM %pa invalid pipe: %d",
+			&bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+	if (dev->pipes[pipe_index] != PIPE_INACTIVE) {
+		SPS_ERR(sps, "sps:BAM-DMA: BAM %pa pipe %d already active",
+			&bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+
+	/* Mark pipe active */
+	dev->pipes[pipe_index] = PIPE_ACTIVE;
+
+	/* If channel is not allocated, make an internal allocation */
+	channel = pipe_index / 2;
+	chan = &dev->chans[channel];
+	if (chan->state != DMA_CHAN_STATE_ALLOC_EXT &&
+	    chan->state != DMA_CHAN_STATE_ALLOC_INT) {
+		chan->state = DMA_CHAN_STATE_ALLOC_INT;
+	}
+
+	result = 0;
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Enable a BAM DMA pipe
+ *
+ */
+int sps_dma_pipe_enable(void *bam_arg, u32 pipe_index)
+{
+	struct sps_bam *bam = bam_arg;
+	struct bamdma_device *dev;
+	struct bamdma_chan *chan;
+	u32 channel;
+	int result = SPS_ERROR;
+
+	SPS_DBG3(sps, "sps:sps_dma_pipe_enable.pipe %d", pipe_index);
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device((unsigned long) bam);
+	if (dev == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM-DMA: invalid BAM", __func__);
+		goto exit_err;
+	}
+	if (pipe_index >= dev->num_pipes) {
+		SPS_ERR(sps, "sps:BAM-DMA: BAM %pa invalid pipe: %d",
+			&bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+	if (dev->pipes[pipe_index] != PIPE_ACTIVE) {
+		SPS_ERR(sps, "sps:BAM-DMA: BAM %pa pipe %d not active",
+			&bam->props.phys_addr, pipe_index);
+		goto exit_err;
+	}
+
+      /*
+       * The channel must be enabled when the dest/input/write pipe
+       * is enabled
+       */
+	if (DMA_PIPE_IS_DEST(pipe_index)) {
+		/* Configure and enable the channel */
+		channel = pipe_index / 2;
+		chan = &dev->chans[channel];
+
+		if (chan->threshold != SPS_DMA_THRESHOLD_DEFAULT)
+			dma_write_reg_field(dev->virt_addr,
+					    DMA_CHNL_CONFIG(channel),
+					    DMA_CHNL_ACT_THRESH,
+					    chan->thresh);
+
+		if (chan->priority != SPS_DMA_PRI_DEFAULT)
+			dma_write_reg_field(dev->virt_addr,
+					    DMA_CHNL_CONFIG(channel),
+					    DMA_CHNL_WEIGHT,
+					    chan->weight);
+
+		dma_write_reg_field(dev->virt_addr,
+				    DMA_CHNL_CONFIG(channel),
+				    DMA_CHNL_ENABLE, 1);
+	}
+
+	result = 0;
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Deactivate a BAM DMA pipe
+ *
+ * This function deactivates a BAM DMA pipe.
+ *
+ * @dev - pointer to BAM-DMA device descriptor
+ *
+ * @bam - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_dma_deactivate_pipe_atomic(struct bamdma_device *dev,
+					  struct sps_bam *bam,
+					  u32 pipe_index)
+{
+	u32 channel;
+
+	if (dev->bam != bam)
+		return SPS_ERROR;
+	if (pipe_index >= dev->num_pipes)
+		return SPS_ERROR;
+	if (dev->pipes[pipe_index] != PIPE_ACTIVE)
+		return SPS_ERROR;	/* Pipe is not active */
+
+	SPS_DBG3(sps, "sps:BAM-DMA: deactivate pipe %d", pipe_index);
+
+	/* Mark pipe inactive */
+	dev->pipes[pipe_index] = PIPE_INACTIVE;
+
+	/*
+	 * Channel must be reset when either pipe is disabled, so just always
+	 * reset regardless of other pipe's state
+	 */
+	channel = pipe_index / 2;
+	dma_write_reg_field(dev->virt_addr, DMA_CHNL_CONFIG(channel),
+			    DMA_CHNL_ENABLE, 0);
+
+	/* If the peer pipe is also inactive, reset the channel */
+	if (sps_dma_check_pipes(dev, pipe_index) == DMA_PIPES_BOTH_DISABLED) {
+		/* Free channel if allocated internally */
+		if (dev->chans[channel].state == DMA_CHAN_STATE_ALLOC_INT)
+			dev->chans[channel].state = DMA_CHAN_STATE_FREE;
+	}
+
+	return 0;
+}
+
+/**
+ * Free a BAM DMA pipe
+ *
+ */
+int sps_dma_pipe_free(void *bam_arg, u32 pipe_index)
+{
+	struct bamdma_device *dev;
+	struct sps_bam *bam = bam_arg;
+	int result;
+
+	mutex_lock(&bam_dma_lock);
+
+	dev = sps_dma_find_device((unsigned long) bam);
+	if (dev == NULL) {
+		SPS_ERR(sps, "sps:%s:BAM-DMA: invalid BAM", __func__);
+		result = SPS_ERROR;
+		goto exit_err;
+	}
+
+	result = sps_dma_deactivate_pipe_atomic(dev, bam, pipe_index);
+
+exit_err:
+	mutex_unlock(&bam_dma_lock);
+
+	return result;
+}
+
+/**
+ * Get the BAM handle for BAM-DMA.
+ *
+ * The BAM handle should be use as source/destination in the sps_connect().
+ *
+ * @return bam handle on success, zero on error
+ */
+unsigned long sps_dma_get_bam_handle(void)
+{
+	return (unsigned long)bam_dma_dev[0].bam;
+}
+EXPORT_SYMBOL(sps_dma_get_bam_handle);
+
+/**
+ * Free the BAM handle for BAM-DMA.
+ *
+ */
+void sps_dma_free_bam_handle(unsigned long h)
+{
+}
+EXPORT_SYMBOL(sps_dma_free_bam_handle);
+
+#endif /* CONFIG_SPS_SUPPORT_BAMDMA */
diff --git a/drivers/platform/msm/sps/sps_map.c b/drivers/platform/msm/sps/sps_map.c
new file mode 100644
index 0000000..901d9cc
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_map.c
@@ -0,0 +1,140 @@
+/* Copyright (c) 2011-2013, 2015, 2017. The Linux Foundation. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Connection mapping table management for SPS device driver.
+ */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/memory.h>	/* memset */
+
+#include "spsi.h"
+
+/* Module state */
+struct sps_map_state {
+	const struct sps_map *maps;
+	u32 num_maps;
+	u32 options;
+};
+
+static struct sps_map_state sps_maps;
+
+/**
+ * Initialize connection mapping module
+ *
+ */
+int sps_map_init(const struct sps_map *map_props, u32 options)
+{
+	const struct sps_map *maps;
+
+	/* Are there any connection mappings? */
+	memset(&sps_maps, 0, sizeof(sps_maps));
+	if (map_props == NULL)
+		return 0;
+
+	/* Init the module state */
+	sps_maps.maps = map_props;
+	sps_maps.options = options;
+	for (maps = sps_maps.maps;; maps++, sps_maps.num_maps++)
+		if (maps->src.periph_class == SPS_CLASS_INVALID &&
+		    maps->src.periph_phy_addr == SPS_ADDR_INVALID)
+			break;
+
+	SPS_DBG(sps, "sps: %d mappings", sps_maps.num_maps);
+
+	return 0;
+}
+
+/**
+ * De-initialize connection mapping module
+ *
+ */
+void sps_map_de_init(void)
+{
+	memset(&sps_maps, 0, sizeof(sps_maps));
+}
+
+/**
+ * Find matching connection mapping
+ *
+ */
+int sps_map_find(struct sps_connect *connect)
+{
+	const struct sps_map *map;
+	u32 i;
+	void *desc;
+	void *data;
+
+	/* Are there any connection mappings? */
+	if (sps_maps.num_maps == 0)
+		return SPS_ERROR;
+
+	/* Search the mapping table for a match to the specified connection */
+	for (i = sps_maps.num_maps, map = sps_maps.maps;
+	    i > 0; i--, map++)
+		if (map->src.periph_class == (u32) connect->source &&
+		    map->dest.periph_class == (u32) connect->destination
+		    && map->config == (u32) connect->config)
+			break;
+
+	if (i == 0)
+		return SPS_ERROR;
+
+	/*
+	 * Before modifying client parameter struct, perform all
+	 * operations that might fail
+	 */
+	desc = spsi_get_mem_ptr(map->desc_base);
+	if (desc == NULL) {
+		SPS_ERR(sps,
+			"sps:Cannot get virt addr for I/O buffer: %pa\n",
+			&map->desc_base);
+		return SPS_ERROR;
+	}
+
+	if (map->data_size > 0 && map->data_base != SPS_ADDR_INVALID) {
+		data = spsi_get_mem_ptr(map->data_base);
+		if (data == NULL) {
+			SPS_ERR(sps,
+				"sps:Can't get virt addr for I/O buffer: %pa",
+				&map->data_base);
+			return SPS_ERROR;
+		}
+	} else {
+		data = NULL;
+	}
+
+	/* Copy mapping values to client parameter struct */
+	if (connect->source != SPS_DEV_HANDLE_MEM)
+		connect->src_pipe_index = map->src.pipe_index;
+
+	if (connect->destination != SPS_DEV_HANDLE_MEM)
+		connect->dest_pipe_index = map->dest.pipe_index;
+
+	if (connect->mode == SPS_MODE_SRC)
+		connect->event_thresh = map->src.event_thresh;
+	else
+		connect->event_thresh = map->dest.event_thresh;
+
+	connect->desc.size = map->desc_size;
+	connect->desc.phys_base = map->desc_base;
+	connect->desc.base = desc;
+	if (map->data_size > 0 && map->data_base != SPS_ADDR_INVALID) {
+		connect->data.size = map->data_size;
+		connect->data.phys_base = map->data_base;
+		connect->data.base = data;
+	}
+
+	return 0;
+}
diff --git a/drivers/platform/msm/sps/sps_map.h b/drivers/platform/msm/sps/sps_map.h
new file mode 100644
index 0000000..2c9f8e3
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_map.h
@@ -0,0 +1,46 @@
+/* Copyright (c) 2011,2013, 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* SPS driver mapping table data declarations. */
+
+
+#ifndef _SPS_MAP_H_
+#define _SPS_MAP_H_
+
+#include <linux/types.h>	/* u32 */
+
+/* End point parameters */
+struct sps_map_end_point {
+	u32 periph_class;	/* Peripheral device enumeration class */
+	phys_addr_t periph_phy_addr;	/* Peripheral base address */
+	u32 pipe_index;		/* Pipe index */
+	u32 event_thresh;	/* Pipe event threshold */
+};
+
+/* Mapping connection descriptor */
+struct sps_map {
+	/* Source end point parameters */
+	struct sps_map_end_point src;
+
+	/* Destination end point parameters */
+	struct sps_map_end_point dest;
+
+	/* Resource parameters */
+	u32 config;	 /* Configuration (stream) identifier */
+	phys_addr_t desc_base;	 /* Physical address of descriptor FIFO */
+	u32 desc_size;	 /* Size (bytes) of descriptor FIFO */
+	phys_addr_t data_base;	 /* Physical address of data FIFO */
+	u32 data_size;	 /* Size (bytes) of data FIFO */
+
+};
+
+#endif /* _SPS_MAP_H_ */
diff --git a/drivers/platform/msm/sps/sps_mem.c b/drivers/platform/msm/sps/sps_mem.c
new file mode 100644
index 0000000..16556bd
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_mem.c
@@ -0,0 +1,172 @@
+/* Copyright (c) 2011-2013, 2015, 2017, The Linux Foundation. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Pipe-Memory allocation/free management.
+ */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/io.h>		/* ioremap() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+#include <linux/genalloc.h>	/* gen_pool_alloc() */
+#include <linux/errno.h>	/* ENOMEM */
+
+#include "sps_bam.h"
+#include "spsi.h"
+
+static phys_addr_t iomem_phys;
+static void *iomem_virt;
+static u32 iomem_size;
+static u32 iomem_offset;
+static struct gen_pool *pool;
+static u32 nid = 0xaa;
+
+/* Debug */
+static u32 total_alloc;
+static u32 total_free;
+
+/**
+ * Translate physical to virtual address
+ *
+ */
+void *spsi_get_mem_ptr(phys_addr_t phys_addr)
+{
+	void *virt = NULL;
+
+	if ((phys_addr >= iomem_phys) &&
+	    (phys_addr < (iomem_phys + iomem_size))) {
+		virt = (u8 *) iomem_virt + (phys_addr - iomem_phys);
+	} else {
+		virt = phys_to_virt(phys_addr);
+		SPS_ERR(sps, "sps:spsi_get_mem_ptr.invalid phys addr=0x%pa.",
+			&phys_addr);
+	}
+	return virt;
+}
+
+/**
+ * Allocate I/O (pipe) memory
+ *
+ */
+phys_addr_t sps_mem_alloc_io(u32 bytes)
+{
+	phys_addr_t phys_addr = SPS_ADDR_INVALID;
+	unsigned long virt_addr = 0;
+
+	virt_addr = gen_pool_alloc(pool, bytes);
+	if (virt_addr) {
+		iomem_offset = virt_addr - (uintptr_t) iomem_virt;
+		phys_addr = iomem_phys + iomem_offset;
+		total_alloc += bytes;
+	} else {
+		SPS_ERR(sps, "sps:gen_pool_alloc %d bytes fail.", bytes);
+		return SPS_ADDR_INVALID;
+	}
+
+	SPS_DBG3(sps, "sps:sps_mem_alloc_io.phys=%pa.virt=0x%lx.size=0x%x.",
+		&phys_addr, virt_addr, bytes);
+
+	return phys_addr;
+}
+
+/**
+ * Free I/O memory
+ *
+ */
+void sps_mem_free_io(phys_addr_t phys_addr, u32 bytes)
+{
+	unsigned long virt_addr = 0;
+
+	iomem_offset = phys_addr - iomem_phys;
+	virt_addr = (uintptr_t) iomem_virt + iomem_offset;
+
+	SPS_DBG3(sps, "sps:sps_mem_free_io.phys=%pa.virt=0x%lx.size=0x%x.",
+		&phys_addr, virt_addr, bytes);
+
+	gen_pool_free(pool, virt_addr, bytes);
+	total_free += bytes;
+}
+
+/**
+ * Initialize driver memory module
+ *
+ */
+int sps_mem_init(phys_addr_t pipemem_phys_base, u32 pipemem_size)
+{
+	int res;
+
+	/* 2^8=128. The desc-fifo and data-fifo minimal allocation. */
+	int min_alloc_order = 8;
+
+	if ((d_type == 0) || (d_type == 2) || imem) {
+		iomem_phys = pipemem_phys_base;
+		iomem_size = pipemem_size;
+
+		if (iomem_phys == 0) {
+			SPS_ERR(sps, "sps:%s:Invalid Pipe-Mem address",
+				__func__);
+			return SPS_ERROR;
+		}
+		iomem_virt = ioremap(iomem_phys, iomem_size);
+		if (!iomem_virt) {
+			SPS_ERR(sps,
+				"sps:%s:Failed to IO map pipe memory.\n",
+					__func__);
+			return -ENOMEM;
+		}
+
+		iomem_offset = 0;
+		SPS_DBG(sps,
+			"sps:sps_mem_init.iomem_phys=%pa,iomem_virt=0x%p.",
+			&iomem_phys, iomem_virt);
+	}
+
+	pool = gen_pool_create(min_alloc_order, nid);
+
+	if (!pool) {
+		SPS_ERR(sps, "sps:%s:Failed to create a new memory pool.\n",
+								__func__);
+		return -ENOMEM;
+	}
+
+	if ((d_type == 0) || (d_type == 2) || imem) {
+		res = gen_pool_add(pool, (uintptr_t)iomem_virt,
+				iomem_size, nid);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+/**
+ * De-initialize driver memory module
+ *
+ */
+int sps_mem_de_init(void)
+{
+	if (iomem_virt != NULL) {
+		gen_pool_destroy(pool);
+		pool = NULL;
+		iounmap(iomem_virt);
+		iomem_virt = NULL;
+	}
+
+	if (total_alloc == total_free)
+		return 0;
+
+	SPS_ERR(sps, "sps:%s:some memory not free", __func__);
+	return SPS_ERROR;
+}
diff --git a/drivers/platform/msm/sps/sps_rm.c b/drivers/platform/msm/sps/sps_rm.c
new file mode 100644
index 0000000..602a256
--- /dev/null
+++ b/drivers/platform/msm/sps/sps_rm.c
@@ -0,0 +1,854 @@
+/* Copyright (c) 2011-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Resource management for the SPS device driver. */
+
+#include <linux/types.h>	/* u32 */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/mutex.h>	/* mutex */
+#include <linux/list.h>		/* list_head */
+#include <linux/slab.h>		/* kzalloc() */
+#include <linux/memory.h>	/* memset */
+#include <linux/interrupt.h>
+
+#include "spsi.h"
+#include "sps_core.h"
+
+/* Max BAM FIFO sizes */
+#define SPSRM_MAX_DESC_FIFO_SIZE    0xffff
+#define SPSRM_MAX_DATA_FIFO_SIZE    0xffff
+
+/* Connection control struct pointer */
+static struct sps_rm *sps_rm;
+
+/**
+ * Initialize resource manager module
+ */
+int sps_rm_init(struct sps_rm *rm, u32 options)
+{
+	/* Set the resource manager state struct pointer */
+	sps_rm = rm;
+
+	/* Initialize the state struct */
+	INIT_LIST_HEAD(&sps_rm->connections_q);
+	mutex_init(&sps_rm->lock);
+
+	return 0;
+}
+
+/**
+ * Initialize client state context
+ *
+ */
+void sps_rm_config_init(struct sps_connect *connect)
+{
+	memset(connect, SPSRM_CLEAR, sizeof(*connect));
+}
+
+/**
+ * Remove reference to connection mapping
+ *
+ * This function removes a reference from a connection mapping struct.
+ *
+ * @map - pointer to connection mapping struct
+ *
+ */
+static void sps_rm_remove_ref(struct sps_connection *map)
+{
+	/* Free this connection */
+	map->refs--;
+	if (map->refs <= 0) {
+		if (map->client_src != NULL || map->client_dest != NULL)
+			SPS_ERR(sps,
+				"sps:%s:Failed to allocate connection struct",
+				__func__);
+
+		list_del(&map->list);
+		kfree(map);
+	}
+}
+
+/**
+ * Compare map to connect parameters
+ *
+ * This function compares client connect parameters to an allocated
+ * connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return - true if match, false otherwise
+ *
+ */
+static int sps_rm_map_match(const struct sps_connect *cfg,
+			    const struct sps_connection *map)
+{
+	if (cfg->source != map->src.dev ||
+	    cfg->destination != map->dest.dev)
+		return false;
+
+	if (cfg->src_pipe_index != SPSRM_CLEAR &&
+	    cfg->src_pipe_index != map->src.pipe_index)
+		return false;
+
+	if (cfg->dest_pipe_index != SPSRM_CLEAR &&
+	    cfg->dest_pipe_index != map->dest.pipe_index)
+		return false;
+
+	if (cfg->config != map->config)
+		return false;
+
+	if (cfg->desc.size != SPSRM_CLEAR) {
+		if (cfg->desc.size != map->desc.size)
+			return false;
+
+		if (cfg->desc.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
+		    cfg->desc.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
+		    (cfg->desc.phys_base != map->desc.phys_base ||
+		     cfg->desc.base != map->desc.base)) {
+			return false;
+		}
+	}
+
+	if (cfg->data.size != SPSRM_CLEAR) {
+		if (cfg->data.size != map->data.size)
+			return false;
+
+		if (cfg->data.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
+		    cfg->data.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
+		    (cfg->data.phys_base != map->data.phys_base ||
+		     cfg->data.base != map->data.base))
+			return false;
+	}
+
+	return true;
+}
+
+/**
+ * Find unconnected mapping
+ *
+ * This function finds an allocated a connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return - pointer to allocated connection mapping, or NULL if not found
+ *
+ */
+static struct sps_connection *find_unconnected(struct sps_pipe *pipe)
+{
+	struct sps_connect *cfg = &pipe->connect;
+	struct sps_connection *map;
+
+	/* Has this connection already been allocated? */
+	list_for_each_entry(map, &sps_rm->connections_q, list) {
+		if (sps_rm_map_match(cfg, map))
+			if ((cfg->mode == SPS_MODE_SRC
+			     && map->client_src == NULL)
+			    || (cfg->mode != SPS_MODE_SRC
+				&& map->client_dest == NULL))
+				return map;	/* Found */
+	}
+
+	return NULL;		/* Not Found */
+}
+
+/**
+ * Assign connection to client
+ *
+ * This function assigns a connection to a client.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @map - connection mapping
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_assign(struct sps_pipe *pipe,
+			 struct sps_connection *map)
+{
+	struct sps_connect *cfg = &pipe->connect;
+
+	/* Check ownership and BAM */
+	if ((cfg->mode == SPS_MODE_SRC && map->client_src != NULL) ||
+	    (cfg->mode != SPS_MODE_SRC && map->client_dest != NULL)) {
+		SPS_ERR(sps,
+			"sps:%s:The end point is already connected.\n",
+			__func__);
+		return SPS_ERROR;
+	}
+
+	/* Check whether this end point is a BAM (not memory) */
+	if ((cfg->mode == SPS_MODE_SRC && map->src.bam == NULL) ||
+	    (cfg->mode != SPS_MODE_SRC && map->dest.bam == NULL)) {
+		SPS_ERR(sps, "sps:%s:The end point is empty.\n", __func__);
+		return SPS_ERROR;
+	}
+
+	/* Record the connection assignment */
+	if (cfg->mode == SPS_MODE_SRC) {
+		map->client_src = pipe;
+		pipe->bam = map->src.bam;
+		pipe->pipe_index = map->src.pipe_index;
+		if (pipe->connect.event_thresh != SPSRM_CLEAR)
+			map->src.event_threshold = pipe->connect.event_thresh;
+		if (pipe->connect.lock_group != SPSRM_CLEAR)
+			map->src.lock_group = pipe->connect.lock_group;
+	} else {
+		map->client_dest = pipe;
+		pipe->bam = map->dest.bam;
+		pipe->pipe_index = map->dest.pipe_index;
+		if (pipe->connect.event_thresh != SPSRM_CLEAR)
+			map->dest.event_threshold =
+			pipe->connect.event_thresh;
+		if (pipe->connect.lock_group != SPSRM_CLEAR)
+			map->dest.lock_group = pipe->connect.lock_group;
+	}
+	pipe->map = map;
+
+	SPS_DBG(pipe->bam, "sps:sps_rm_assign.bam %pa.pipe_index=%d\n",
+			BAM_ID(pipe->bam), pipe->pipe_index);
+
+	/* Copy parameters to client connect state */
+	pipe->connect.src_pipe_index = map->src.pipe_index;
+	pipe->connect.dest_pipe_index = map->dest.pipe_index;
+	pipe->connect.desc = map->desc;
+	pipe->connect.data = map->data;
+
+	pipe->client_state = SPS_STATE_ALLOCATE;
+
+	return 0;
+}
+
+/**
+ * Free connection mapping resources
+ *
+ * This function frees a connection mapping resources.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ */
+static void sps_rm_free_map_rsrc(struct sps_connection *map)
+{
+	struct sps_bam *bam;
+
+	if (map->client_src != NULL || map->client_dest != NULL)
+		return;
+
+	if (map->alloc_src_pipe != SPS_BAM_PIPE_INVALID) {
+		bam = map->src.bam;
+		sps_bam_pipe_free(bam, map->src.pipe_index);
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA))
+			/* Deallocate and free the BAM-DMA channel */
+			sps_dma_pipe_free(bam, map->src.pipe_index);
+#endif
+		map->alloc_src_pipe = SPS_BAM_PIPE_INVALID;
+		map->src.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+	if (map->alloc_dest_pipe != SPS_BAM_PIPE_INVALID) {
+		bam = map->dest.bam;
+		sps_bam_pipe_free(bam, map->dest.pipe_index);
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			/* Deallocate the BAM-DMA channel */
+			sps_dma_pipe_free(bam, map->dest.pipe_index);
+		}
+#endif
+		map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID;
+		map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+	if (map->alloc_desc_base != SPS_ADDR_INVALID) {
+		sps_mem_free_io(map->alloc_desc_base, map->desc.size);
+
+		map->alloc_desc_base = SPS_ADDR_INVALID;
+		map->desc.phys_base = SPS_ADDR_INVALID;
+	}
+	if (map->alloc_data_base != SPS_ADDR_INVALID) {
+		sps_mem_free_io(map->alloc_data_base, map->data.size);
+
+		map->alloc_data_base = SPS_ADDR_INVALID;
+		map->data.phys_base = SPS_ADDR_INVALID;
+	}
+}
+
+/**
+ * Init connection mapping from client connect
+ *
+ * This function initializes a connection mapping from the client's
+ * connect parameters.
+ *
+ * @map - connection mapping struct
+ *
+ * @cfg - client connect parameters
+ *
+ * @return - pointer to allocated connection mapping, or NULL on error
+ *
+ */
+static void sps_rm_init_map(struct sps_connection *map,
+			    const struct sps_connect *cfg)
+{
+	/* Clear the connection mapping struct */
+	memset(map, 0, sizeof(*map));
+	map->desc.phys_base = SPS_ADDR_INVALID;
+	map->data.phys_base = SPS_ADDR_INVALID;
+	map->alloc_desc_base = SPS_ADDR_INVALID;
+	map->alloc_data_base = SPS_ADDR_INVALID;
+	map->alloc_src_pipe = SPS_BAM_PIPE_INVALID;
+	map->alloc_dest_pipe = SPS_BAM_PIPE_INVALID;
+
+	/* Copy client required parameters */
+	map->src.dev = cfg->source;
+	map->dest.dev = cfg->destination;
+	map->desc.size = cfg->desc.size;
+	map->data.size = cfg->data.size;
+	map->config = cfg->config;
+
+	/* Did client specify descriptor FIFO? */
+	if (map->desc.size != SPSRM_CLEAR &&
+	    cfg->desc.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
+	    cfg->desc.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR))
+		map->desc = cfg->desc;
+
+	/* Did client specify data FIFO? */
+	if (map->data.size != SPSRM_CLEAR &&
+	    cfg->data.phys_base != (SPSRM_CLEAR|SPSRM_ADDR_CLR) &&
+	    cfg->data.base != (void *)(SPSRM_CLEAR|SPSRM_ADDR_CLR))
+		map->data = cfg->data;
+
+	/* Did client specify source pipe? */
+	if (cfg->src_pipe_index != SPSRM_CLEAR)
+		map->src.pipe_index = cfg->src_pipe_index;
+	else
+		map->src.pipe_index = SPS_BAM_PIPE_INVALID;
+
+
+	/* Did client specify destination pipe? */
+	if (cfg->dest_pipe_index != SPSRM_CLEAR)
+		map->dest.pipe_index = cfg->dest_pipe_index;
+	else
+		map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
+}
+
+/**
+ * Create a new connection mapping
+ *
+ * This function creates a new connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return - pointer to allocated connection mapping, or NULL on error
+ *
+ */
+static struct sps_connection *sps_rm_create(struct sps_pipe *pipe)
+{
+	struct sps_connection *map;
+	struct sps_bam *bam;
+	u32 desc_size;
+	u32 data_size;
+	enum sps_mode dir;
+	int success = false;
+
+	/* Allocate new connection */
+	map = kzalloc(sizeof(*map), GFP_KERNEL);
+	if (map == NULL) {
+		SPS_ERR(sps,
+			"sps:%s:Failed to allocate connection struct",
+			__func__);
+		return NULL;
+	}
+
+	/* Initialize connection struct */
+	sps_rm_init_map(map, &pipe->connect);
+	dir = pipe->connect.mode;
+
+	/* Use a do/while() loop to avoid a "goto" */
+	success = false;
+	/* Get BAMs */
+	map->src.bam = sps_h2bam(map->src.dev);
+	if (map->src.bam == NULL) {
+		if (map->src.dev != SPS_DEV_HANDLE_MEM) {
+			SPS_ERR(sps, "sps:Invalid BAM handle: %pa",
+							&map->src.dev);
+			goto exit_err;
+		}
+		map->src.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+	map->dest.bam = sps_h2bam(map->dest.dev);
+	if (map->dest.bam == NULL) {
+		if (map->dest.dev != SPS_DEV_HANDLE_MEM) {
+			SPS_ERR(sps, "sps:Invalid BAM handle: %pa",
+							&map->dest.dev);
+			goto exit_err;
+		}
+		map->dest.pipe_index = SPS_BAM_PIPE_INVALID;
+	}
+
+	/* Check the BAM device for the pipe */
+	if ((dir == SPS_MODE_SRC && map->src.bam == NULL) ||
+	    (dir != SPS_MODE_SRC && map->dest.bam == NULL)) {
+		SPS_ERR(sps, "sps:Invalid BAM endpt: dir %d src %pa dest %pa",
+			dir, &map->src.dev, &map->dest.dev);
+		goto exit_err;
+	}
+
+	/* Allocate pipes and copy BAM parameters */
+	if (map->src.bam != NULL) {
+		/* Allocate the pipe */
+		bam = map->src.bam;
+		map->alloc_src_pipe = sps_bam_pipe_alloc(bam,
+							map->src.pipe_index);
+		if (map->alloc_src_pipe == SPS_BAM_PIPE_INVALID)
+			goto exit_err;
+		map->src.pipe_index = map->alloc_src_pipe;
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			int rc;
+			/* Allocate the BAM-DMA channel */
+			rc = sps_dma_pipe_alloc(bam, map->src.pipe_index,
+						 SPS_MODE_SRC);
+			if (rc) {
+				SPS_ERR(bam,
+					"sps:Failed to alloc BAM-DMA pipe: %d",
+					map->src.pipe_index);
+				goto exit_err;
+			}
+		}
+#endif
+		map->src.bam_phys = bam->props.phys_addr;
+		map->src.event_threshold = bam->props.event_threshold;
+	}
+	if (map->dest.bam != NULL) {
+		/* Allocate the pipe */
+		bam = map->dest.bam;
+		map->alloc_dest_pipe = sps_bam_pipe_alloc(bam,
+							 map->dest.pipe_index);
+		if (map->alloc_dest_pipe == SPS_BAM_PIPE_INVALID)
+			goto exit_err;
+
+		map->dest.pipe_index = map->alloc_dest_pipe;
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			int rc;
+			/* Allocate the BAM-DMA channel */
+			rc = sps_dma_pipe_alloc(bam, map->dest.pipe_index,
+					       SPS_MODE_DEST);
+			if (rc) {
+				SPS_ERR(bam,
+					"sps:Failed to alloc BAM-DMA pipe: %d",
+					map->dest.pipe_index);
+				goto exit_err;
+			}
+		}
+#endif
+		map->dest.bam_phys = bam->props.phys_addr;
+		map->dest.event_threshold =
+		bam->props.event_threshold;
+	}
+
+	/* Get default FIFO sizes */
+	desc_size = 0;
+	data_size = 0;
+	if (map->src.bam != NULL) {
+		bam = map->src.bam;
+		desc_size = bam->props.desc_size;
+		data_size = bam->props.data_size;
+	}
+	if (map->dest.bam != NULL) {
+		bam = map->dest.bam;
+		if (bam->props.desc_size > desc_size)
+			desc_size = bam->props.desc_size;
+		if (bam->props.data_size > data_size)
+			data_size = bam->props.data_size;
+	}
+
+	/* Set FIFO sizes */
+	if (map->desc.size == SPSRM_CLEAR)
+		map->desc.size = desc_size;
+	if (map->src.bam != NULL && map->dest.bam != NULL) {
+		/* BAM-to-BAM requires data FIFO */
+		if (map->data.size == SPSRM_CLEAR)
+			map->data.size = data_size;
+	} else {
+		map->data.size = 0;
+	}
+	if (map->desc.size > SPSRM_MAX_DESC_FIFO_SIZE) {
+		SPS_ERR(sps, "sps:Invalid desc FIFO size: 0x%x",
+						map->desc.size);
+		goto exit_err;
+	}
+	if (map->src.bam != NULL && map->dest.bam != NULL &&
+	    map->data.size > SPSRM_MAX_DATA_FIFO_SIZE) {
+		SPS_ERR(sps, "sps:Invalid data FIFO size: 0x%x",
+						map->data.size);
+		goto exit_err;
+	}
+
+	/* Allocate descriptor FIFO if necessary */
+	if (map->desc.size && map->desc.phys_base == SPS_ADDR_INVALID) {
+		map->alloc_desc_base = sps_mem_alloc_io(map->desc.size);
+		if (map->alloc_desc_base == SPS_ADDR_INVALID) {
+			SPS_ERR(sps, "sps:I/O memory allocation failure:0x%x",
+				map->desc.size);
+			goto exit_err;
+		}
+		map->desc.phys_base = map->alloc_desc_base;
+		map->desc.base = spsi_get_mem_ptr(map->desc.phys_base);
+		if (map->desc.base == NULL) {
+			SPS_ERR(sps,
+				"sps:Cannot get virt addr for I/O buffer:%pa",
+				&map->desc.phys_base);
+			goto exit_err;
+		}
+	}
+
+	/* Allocate data FIFO if necessary */
+	if (map->data.size && map->data.phys_base == SPS_ADDR_INVALID) {
+		map->alloc_data_base = sps_mem_alloc_io(map->data.size);
+		if (map->alloc_data_base == SPS_ADDR_INVALID) {
+			SPS_ERR(sps, "sps:I/O memory allocation failure:0x%x",
+				map->data.size);
+			goto exit_err;
+		}
+		map->data.phys_base = map->alloc_data_base;
+		map->data.base = spsi_get_mem_ptr(map->data.phys_base);
+		if (map->data.base == NULL) {
+			SPS_ERR(sps,
+				"sps:Cannot get virt addr for I/O buffer:%pa",
+				&map->data.phys_base);
+			goto exit_err;
+		}
+	}
+
+	/* Attempt to assign this connection to the client */
+	if (sps_rm_assign(pipe, map)) {
+		SPS_ERR(sps,
+		"sps:%s:failed to assign a connection to the client.\n",
+			__func__);
+		goto exit_err;
+	}
+
+	/* Initialization was successful */
+	success = true;
+exit_err:
+
+	/* If initialization failed, free resources */
+	if (!success) {
+		sps_rm_free_map_rsrc(map);
+		kfree(map);
+		return NULL;
+	}
+
+	return map;
+}
+
+/**
+ * Free connection mapping
+ *
+ * This function frees a connection mapping.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_free(struct sps_pipe *pipe)
+{
+	struct sps_connection *map = (void *)pipe->map;
+	struct sps_connect *cfg = &pipe->connect;
+
+	mutex_lock(&sps_rm->lock);
+
+	/* Free this connection */
+	if (cfg->mode == SPS_MODE_SRC)
+		map->client_src = NULL;
+	else
+		map->client_dest = NULL;
+
+	pipe->map = NULL;
+	pipe->client_state = SPS_STATE_DISCONNECT;
+	sps_rm_free_map_rsrc(map);
+
+	sps_rm_remove_ref(map);
+
+	mutex_unlock(&sps_rm->lock);
+
+	return 0;
+}
+
+/**
+ * Allocate an SPS connection end point
+ *
+ * This function allocates resources and initializes a BAM connection.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_alloc(struct sps_pipe *pipe)
+{
+	struct sps_connection *map;
+	int result = SPS_ERROR;
+
+	if (pipe->connect.sps_reserved != SPSRM_CLEAR) {
+		/*
+		 * Client did not call sps_get_config()	to init
+		 * struct sps_connect, so only use legacy members.
+		 */
+		unsigned long source = pipe->connect.source;
+		unsigned long destination = pipe->connect.destination;
+		enum sps_mode mode = pipe->connect.mode;
+		u32 config = pipe->connect.config;
+
+		memset(&pipe->connect, SPSRM_CLEAR,
+			      sizeof(pipe->connect));
+		pipe->connect.source = source;
+		pipe->connect.destination = destination;
+		pipe->connect.mode = mode;
+		pipe->connect.config = config;
+	}
+	if (pipe->connect.config == SPSRM_CLEAR)
+		pipe->connect.config = SPS_CONFIG_DEFAULT;
+
+	/*
+	 *  If configuration is not default, then client is specifying a
+	 * connection mapping.  Find a matching mapping, or fail.
+	 * If a match is found, the client's Connect struct will be updated
+	 * with all the mapping's values.
+	 */
+	if (pipe->connect.config != SPS_CONFIG_DEFAULT) {
+		if (sps_map_find(&pipe->connect)) {
+			SPS_ERR(sps,
+				"sps:%s:Failed to find connection mapping",
+								__func__);
+			return SPS_ERROR;
+		}
+	}
+
+	mutex_lock(&sps_rm->lock);
+	/* Check client state */
+	if (IS_SPS_STATE_OK(pipe)) {
+		SPS_ERR(sps,
+			"sps:%s:Client connection already allocated",
+							__func__);
+		goto exit_err;
+	}
+
+	/* Are the connection resources already allocated? */
+	map = find_unconnected(pipe);
+	if (map != NULL) {
+		/* Attempt to assign this connection to the client */
+		if (sps_rm_assign(pipe, map))
+			/* Assignment failed, so must allocate new */
+			map = NULL;
+	}
+
+	/* Allocate a new connection if necessary */
+	if (map == NULL) {
+		map = sps_rm_create(pipe);
+		if (map == NULL) {
+			SPS_ERR(sps,
+				"sps:%s:Failed to allocate connection",
+							__func__);
+			goto exit_err;
+		}
+		list_add_tail(&map->list, &sps_rm->connections_q);
+	}
+
+	/* Add the connection to the allocated queue */
+	map->refs++;
+
+	/* Initialization was successful */
+	result = 0;
+exit_err:
+	mutex_unlock(&sps_rm->lock);
+
+	if (result)
+		return SPS_ERROR;
+
+	return 0;
+}
+
+/**
+ * Disconnect an SPS connection end point
+ *
+ * This function frees resources and de-initializes a BAM connection.
+ *
+ * @pipe - client context for SPS connection end point
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+static int sps_rm_disconnect(struct sps_pipe *pipe)
+{
+	sps_rm_free(pipe);
+	return 0;
+}
+
+/**
+ * Process connection state change
+ *
+ * This function processes a connection state change.
+ *
+ * @pipe - pointer to client context
+ *
+ * @state - new state for connection
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_rm_state_change(struct sps_pipe *pipe, u32 state)
+{
+	int auto_enable = false;
+	int result;
+
+	/* Allocate the pipe */
+	if (pipe->client_state == SPS_STATE_DISCONNECT &&
+	    state == SPS_STATE_ALLOCATE) {
+		if (sps_rm_alloc(pipe)) {
+			SPS_ERR(pipe->bam,
+				"sps:Fail to allocate resource for BAM 0x%p pipe %d.\n",
+					pipe->bam, pipe->pipe_index);
+			return SPS_ERROR;
+		}
+	}
+
+	/* Configure the pipe */
+	if (pipe->client_state == SPS_STATE_ALLOCATE &&
+	    state == SPS_STATE_CONNECT) {
+		/* Connect the BAM pipe */
+		struct sps_bam_connect_param params;
+
+		memset(&params, 0, sizeof(params));
+		params.mode = pipe->connect.mode;
+		if (pipe->connect.options != SPSRM_CLEAR) {
+			params.options = pipe->connect.options;
+			params.irq_gen_addr = pipe->connect.irq_gen_addr;
+			params.irq_gen_data = pipe->connect.irq_gen_data;
+		}
+		result = sps_bam_pipe_connect(pipe, &params);
+		if (result) {
+			SPS_ERR(pipe->bam,
+				"sps:Failed to connect BAM 0x%p pipe %d",
+					pipe->bam, pipe->pipe_index);
+			return SPS_ERROR;
+		}
+		pipe->client_state = SPS_STATE_CONNECT;
+
+		/* Set auto-enable for system-mode connections */
+		if (pipe->connect.source == SPS_DEV_HANDLE_MEM ||
+		    pipe->connect.destination == SPS_DEV_HANDLE_MEM) {
+			if (pipe->map->desc.size != 0 &&
+			    pipe->map->desc.phys_base != SPS_ADDR_INVALID)
+				auto_enable = true;
+		}
+	}
+
+	/* Enable the pipe data flow */
+	if (pipe->client_state == SPS_STATE_CONNECT &&
+	    !(state == SPS_STATE_DISABLE
+	      || state == SPS_STATE_DISCONNECT)
+	    && (state == SPS_STATE_ENABLE || auto_enable
+		|| (pipe->connect.options & SPS_O_AUTO_ENABLE))) {
+		result = sps_bam_pipe_enable(pipe->bam, pipe->pipe_index);
+		if (result) {
+			SPS_ERR(pipe->bam,
+				"sps:Failed to set BAM %pa pipe %d flow on",
+				&pipe->bam->props.phys_addr,
+				pipe->pipe_index);
+			return SPS_ERROR;
+		}
+
+		/* Is this a BAM-DMA pipe? */
+#ifdef CONFIG_SPS_SUPPORT_BAMDMA
+		if ((pipe->bam->props.options & SPS_BAM_OPT_BAMDMA)) {
+			/* Activate the BAM-DMA channel */
+			result = sps_dma_pipe_enable(pipe->bam,
+						     pipe->pipe_index);
+			if (result) {
+				SPS_ERR(pipe->bam,
+					"sps:Failed to activate BAM-DMA pipe: %d",
+					pipe->pipe_index);
+				return SPS_ERROR;
+			}
+		}
+#endif
+		pipe->client_state = SPS_STATE_ENABLE;
+	}
+
+	/* Disable the pipe data flow */
+	if (pipe->client_state == SPS_STATE_ENABLE &&
+	    (state == SPS_STATE_DISABLE	|| state == SPS_STATE_DISCONNECT)) {
+		result = sps_bam_pipe_disable(pipe->bam, pipe->pipe_index);
+		if (result) {
+			SPS_ERR(pipe->bam,
+				"sps:Failed to set BAM %pa pipe %d flow off",
+				&pipe->bam->props.phys_addr,
+				pipe->pipe_index);
+			return SPS_ERROR;
+		}
+		pipe->client_state = SPS_STATE_CONNECT;
+	}
+
+	/* Disconnect the BAM pipe */
+	if (pipe->client_state == SPS_STATE_CONNECT &&
+	    state == SPS_STATE_DISCONNECT) {
+		struct sps_connection *map;
+		struct sps_bam *bam = pipe->bam;
+		unsigned long flags;
+		u32 pipe_index;
+
+		if (pipe->connect.mode == SPS_MODE_SRC)
+			pipe_index = pipe->map->src.pipe_index;
+		else
+			pipe_index = pipe->map->dest.pipe_index;
+
+		if (bam->props.irq > 0)
+			synchronize_irq(bam->props.irq);
+
+		spin_lock_irqsave(&bam->isr_lock, flags);
+		pipe->disconnecting = true;
+		spin_unlock_irqrestore(&bam->isr_lock, flags);
+		result = sps_bam_pipe_disconnect(pipe->bam, pipe_index);
+		if (result) {
+			SPS_ERR(pipe->bam,
+				"sps:Failed to disconnect BAM %pa pipe %d",
+				&pipe->bam->props.phys_addr,
+				pipe->pipe_index);
+			return SPS_ERROR;
+		}
+
+		/* Clear map state */
+		map = (void *)pipe->map;
+		if (pipe->connect.mode == SPS_MODE_SRC)
+			map->client_src = NULL;
+		else if (pipe->connect.mode == SPS_MODE_DEST)
+			map->client_dest = NULL;
+
+		sps_rm_disconnect(pipe);
+
+		/* Clear the client state */
+		pipe->map = NULL;
+		pipe->bam = NULL;
+		pipe->client_state = SPS_STATE_DISCONNECT;
+	}
+
+	return 0;
+}
diff --git a/drivers/platform/msm/sps/spsi.h b/drivers/platform/msm/sps/spsi.h
new file mode 100644
index 0000000..2e57f7d
--- /dev/null
+++ b/drivers/platform/msm/sps/spsi.h
@@ -0,0 +1,547 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/**
+ * Smart-Peripheral-Switch (SPS) internal API.
+ */
+
+#ifndef _SPSI_H_
+#define _SPSI_H_
+
+#include <linux/types.h>	/* u32 */
+#include <linux/list.h>		/* list_head */
+#include <linux/kernel.h>	/* pr_info() */
+#include <linux/compiler.h>
+#include <linux/ratelimit.h>
+#include <linux/ipc_logging.h>
+
+#include <linux/msm-sps.h>
+
+#include "sps_map.h"
+
+#if defined(CONFIG_PHYS_ADDR_T_64BIT) || defined(CONFIG_ARM_LPAE)
+#define SPS_LPAE (true)
+#else
+#define SPS_LPAE (false)
+#endif
+
+#define BAM_MAX_PIPES              31
+#define BAM_MAX_P_LOCK_GROUP_NUM   31
+
+/* Adjust for offset of struct sps_q_event */
+#define SPS_EVENT_INDEX(e)    ((e) - 1)
+#define SPS_ERROR -1
+
+/* BAM identifier used in log messages */
+#define BAM_ID(dev)       (&(dev)->props.phys_addr)
+
+/* "Clear" value for the connection parameter struct */
+#define SPSRM_CLEAR     0xccccccccUL
+#define SPSRM_ADDR_CLR \
+	((sizeof(int) == sizeof(long)) ? 0 : (SPSRM_CLEAR << 32))
+
+#define MAX_MSG_LEN 80
+#define SPS_IPC_LOGPAGES 10
+#define SPS_IPC_REG_DUMP_FACTOR 3
+#define SPS_IPC_DEFAULT_LOGLEVEL 3
+#define SPS_IPC_MAX_LOGLEVEL 4
+
+/* Connection mapping control struct */
+struct sps_rm {
+	struct list_head connections_q;
+	struct mutex lock;
+};
+
+/* SPS driver state struct */
+struct sps_drv {
+	struct class *dev_class;
+	dev_t dev_num;
+	struct device *dev;
+	struct clk *pmem_clk;
+	struct clk *bamdma_clk;
+	struct clk *dfab_clk;
+
+	int is_ready;
+
+	/* Platform data */
+	phys_addr_t pipemem_phys_base;
+	u32 pipemem_size;
+	phys_addr_t bamdma_bam_phys_base;
+	u32 bamdma_bam_size;
+	phys_addr_t bamdma_dma_phys_base;
+	u32 bamdma_dma_size;
+	u32 bamdma_irq;
+	u32 bamdma_restricted_pipes;
+
+	/* Driver options bitflags (see SPS_OPT_*) */
+	u32 options;
+
+	/* Mutex to protect BAM and connection queues */
+	struct mutex lock;
+
+	/* BAM devices */
+	struct list_head bams_q;
+
+	char *hal_bam_version;
+
+	/* Connection control state */
+	struct sps_rm connection_ctrl;
+
+	void *ipc_log0;
+	void *ipc_log1;
+	void *ipc_log2;
+	void *ipc_log3;
+	void *ipc_log4;
+
+	u32 ipc_loglevel;
+};
+
+extern struct sps_drv *sps;
+extern u32 d_type;
+extern bool enhd_pipe;
+extern bool imem;
+extern enum sps_bam_type bam_type;
+
+#ifdef CONFIG_DEBUG_FS
+extern u8 debugfs_record_enabled;
+extern u8 logging_option;
+extern u8 debug_level_option;
+extern u8 print_limit_option;
+
+#define SPS_IPC(idx, dev, msg, args...) do { \
+		if (dev) { \
+			if ((idx == 0) && (dev)->ipc_log0) \
+				ipc_log_string((dev)->ipc_log0, \
+					"%s: " msg, __func__, args); \
+			else if ((idx == 1) && (dev)->ipc_log1) \
+				ipc_log_string((dev)->ipc_log1, \
+					"%s: " msg, __func__, args); \
+			else if ((idx == 2) && (dev)->ipc_log2) \
+				ipc_log_string((dev)->ipc_log2, \
+					"%s: " msg, __func__, args); \
+			else if ((idx == 3) && (dev)->ipc_log3) \
+				ipc_log_string((dev)->ipc_log3, \
+					"%s: " msg, __func__, args); \
+			else if ((idx == 4) && (dev)->ipc_log4) \
+				ipc_log_string((dev)->ipc_log4, \
+					"%s: " msg, __func__, args); \
+			else \
+				pr_debug("sps: no such IPC logging index!\n"); \
+		} \
+	} while (0)
+#define SPS_DUMP(msg, args...) do {					\
+		SPS_IPC(4, sps, msg, args); \
+		if (sps) { \
+			if (sps->ipc_log4 == NULL) \
+				pr_info(msg, ##args);	\
+		} \
+	} while (0)
+#define SPS_DEBUGFS(msg, args...) do {					\
+		char buf[MAX_MSG_LEN];		\
+		snprintf(buf, MAX_MSG_LEN, msg"\n", ##args);	\
+		sps_debugfs_record(buf);	\
+	} while (0)
+#define SPS_ERR(dev, msg, args...) do {					\
+		if (logging_option != 1) {	\
+			if (unlikely(print_limit_option > 2))	\
+				pr_err_ratelimited(msg, ##args);	\
+			else	\
+				pr_err(msg, ##args);	\
+		}	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+		SPS_IPC(3, dev, msg, args); \
+	} while (0)
+#define SPS_INFO(dev, msg, args...) do {				\
+		if (logging_option != 1) {	\
+			if (unlikely(print_limit_option > 1))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		}	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+		SPS_IPC(3, dev, msg, args); \
+	} while (0)
+#define SPS_DBG(dev, msg, args...) do {					\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 3))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+		if (dev) { \
+			if ((dev)->ipc_loglevel <= 0)	\
+				SPS_IPC(0, dev, msg, args); \
+		}	\
+	} while (0)
+#define SPS_DBG1(dev, msg, args...) do {				\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 2))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+		if (dev) { \
+			if ((dev)->ipc_loglevel <= 1)	\
+				SPS_IPC(1, dev, msg, args);	\
+		}	\
+	} while (0)
+#define SPS_DBG2(dev, msg, args...) do {				\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 1))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+		if (dev) { \
+			if ((dev)->ipc_loglevel <= 2)	\
+				SPS_IPC(2, dev, msg, args); \
+		}	\
+	} while (0)
+#define SPS_DBG3(dev, msg, args...) do {				\
+		if ((unlikely(logging_option > 1))	\
+			&& (unlikely(debug_level_option > 0))) {\
+			if (unlikely(print_limit_option > 0))	\
+				pr_info_ratelimited(msg, ##args);	\
+			else	\
+				pr_info(msg, ##args);	\
+		} else	\
+			pr_debug(msg, ##args);	\
+		if (unlikely(debugfs_record_enabled))	\
+			SPS_DEBUGFS(msg, ##args);	\
+		if (dev) { \
+			if ((dev)->ipc_loglevel <= 3)	\
+				SPS_IPC(3, dev, msg, args); \
+		}	\
+	} while (0)
+#else
+#define	SPS_DBG3(x...)		pr_debug(x)
+#define	SPS_DBG2(x...)		pr_debug(x)
+#define	SPS_DBG1(x...)		pr_debug(x)
+#define	SPS_DBG(x...)		pr_debug(x)
+#define	SPS_INFO(x...)		pr_info(x)
+#define	SPS_ERR(x...)		pr_err(x)
+#define	SPS_DUMP(x...)		pr_info(x)
+#endif
+
+/* End point parameters */
+struct sps_conn_end_pt {
+	unsigned long dev;		/* Device handle of BAM */
+	phys_addr_t bam_phys;		/* Physical address of BAM. */
+	u32 pipe_index;		/* Pipe index */
+	u32 event_threshold;	/* Pipe event threshold */
+	u32 lock_group;	/* The lock group this pipe belongs to */
+	void *bam;
+};
+
+/* Connection bookkeeping descriptor struct */
+struct sps_connection {
+	struct list_head list;
+
+	/* Source end point parameters */
+	struct sps_conn_end_pt src;
+
+	/* Destination end point parameters */
+	struct sps_conn_end_pt dest;
+
+	/* Resource parameters */
+	struct sps_mem_buffer desc;	/* Descriptor FIFO */
+	struct sps_mem_buffer data;	/* Data FIFO (BAM-to-BAM mode only) */
+	u32 config;		/* Client specified connection configuration */
+
+	/* Connection state */
+	void *client_src;
+	void *client_dest;
+	int refs;		/* Reference counter */
+
+	/* Dynamically allocated resources, if required */
+	u32 alloc_src_pipe;	/* Source pipe index */
+	u32 alloc_dest_pipe;	/* Destination pipe index */
+	/* Physical address of descriptor FIFO */
+	phys_addr_t alloc_desc_base;
+	phys_addr_t alloc_data_base;	/* Physical address of data FIFO */
+};
+
+/* Event bookkeeping descriptor struct */
+struct sps_q_event {
+	struct list_head list;
+	/* Event payload data */
+	struct sps_event_notify notify;
+};
+
+/* Memory heap statistics */
+struct sps_mem_stats {
+	u32 base_addr;
+	u32 size;
+	u32 blocks_used;
+	u32 bytes_used;
+	u32 max_bytes_used;
+};
+
+enum sps_bam_type {
+	SPS_BAM_LEGACY,
+	SPS_BAM_NDP,
+	SPS_BAM_NDP_4K
+};
+
+#ifdef CONFIG_DEBUG_FS
+/* record debug info for debugfs */
+void sps_debugfs_record(const char *msg);
+#endif
+
+/* output the content of BAM-level registers */
+void print_bam_reg(void *virt_addr);
+
+/* output the content of BAM pipe registers */
+void print_bam_pipe_reg(void *virt_addr, u32 pipe_index);
+
+/* output the content of selected BAM-level registers */
+void print_bam_selected_reg(void *virt_addr, u32 pipe_index);
+
+/* output the content of selected BAM pipe registers */
+void print_bam_pipe_selected_reg(void *virt_addr, u32 pipe_index);
+
+/* output descriptor FIFO of a pipe */
+void print_bam_pipe_desc_fifo(void *virt_addr, u32 pipe_index, u32 option);
+
+/* output BAM_TEST_BUS_REG */
+void print_bam_test_bus_reg(void *base, u32 tb_sel);
+
+/* halt and un-halt a pipe */
+void bam_pipe_halt(void *base, u32 pipe, bool halt);
+
+/**
+ * Translate physical to virtual address
+ *
+ * This Function translates physical to virtual address.
+ *
+ * @phys_addr - physical address to translate
+ *
+ * @return virtual memory pointer
+ *
+ */
+void *spsi_get_mem_ptr(phys_addr_t phys_addr);
+
+/**
+ * Allocate I/O (pipe) memory
+ *
+ * This function allocates target I/O (pipe) memory.
+ *
+ * @bytes - number of bytes to allocate
+ *
+ * @return physical address of allocated memory, or SPS_ADDR_INVALID on error
+ */
+phys_addr_t sps_mem_alloc_io(u32 bytes);
+
+/**
+ * Free I/O (pipe) memory
+ *
+ * This function frees target I/O (pipe) memory.
+ *
+ * @phys_addr - physical address of memory to free
+ *
+ * @bytes - number of bytes to free.
+ */
+void sps_mem_free_io(phys_addr_t phys_addr, u32 bytes);
+
+/**
+ * Find matching connection mapping
+ *
+ * This function searches for a connection mapping that matches the
+ * parameters supplied by the client.  If a match is found, the client's
+ * parameter struct is updated with the values specified in the mapping.
+ *
+ * @connect - pointer to client connection parameters
+ *
+ * @return 0 if match is found, negative value otherwise
+ *
+ */
+int sps_map_find(struct sps_connect *connect);
+
+/**
+ * Allocate a BAM DMA pipe
+ *
+ * This function allocates a BAM DMA pipe, and is intended to be called
+ * internally from the BAM resource manager.  Allocation implies that
+ * the pipe has been referenced by a client Connect() and is in use.
+ *
+ * BAM DMA is permissive with activations, and allows a pipe to be allocated
+ * with or without a client-initiated allocation.  This allows the client to
+ * specify exactly which pipe should be used directly through the Connect() API.
+ * sps_dma_alloc_chan() does not allow the client to specify the pipes/channel.
+ *
+ * @bam - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @dir - pipe direction
+ *
+ * @return 0 on success, negative value on error
+ */
+int sps_dma_pipe_alloc(void *bam, u32 pipe_index, enum sps_mode dir);
+
+/**
+ * Enable a BAM DMA pipe
+ *
+ * This function enables the channel associated with a BAM DMA pipe, and
+ * is intended to be called internally from the BAM resource manager.
+ * Enable must occur *after* the pipe has been enabled so that proper
+ * sequencing between pipe and DMA channel enables can be enforced.
+ *
+ * @bam - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_pipe_enable(void *bam, u32 pipe_index);
+
+/**
+ * Free a BAM DMA pipe
+ *
+ * This function disables and frees a BAM DMA pipe, and is intended to be
+ * called internally from the BAM resource manager.  This must occur *after*
+ * the pipe has been disabled/reset so that proper sequencing between pipe and
+ * DMA channel resets can be enforced.
+ *
+ * @bam_arg - pointer to BAM device descriptor
+ *
+ * @pipe_index - pipe index
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_pipe_free(void *bam, u32 pipe_index);
+
+/**
+ * Initialize driver memory module
+ *
+ * This function initializes the driver memory module.
+ *
+ * @pipemem_phys_base - Pipe-Memory physical base.
+ *
+ * @pipemem_size - Pipe-Memory size.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_mem_init(phys_addr_t pipemem_phys_base, u32 pipemem_size);
+
+/**
+ * De-initialize driver memory module
+ *
+ * This function de-initializes the driver memory module.
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_mem_de_init(void);
+
+/**
+ * Initialize BAM DMA module
+ *
+ * This function initializes the BAM DMA module.
+ *
+ * @bam_props - pointer to BAM DMA devices BSP configuration properties
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_init(const struct sps_bam_props *bam_props);
+
+/**
+ * De-initialize BAM DMA module
+ *
+ * This function de-initializes the SPS BAM DMA module.
+ *
+ */
+void sps_dma_de_init(void);
+
+/**
+ * Initialize BAM DMA device
+ *
+ * This function initializes a BAM DMA device.
+ *
+ * @h - BAM handle
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_device_init(unsigned long h);
+
+/**
+ * De-initialize BAM DMA device
+ *
+ * This function de-initializes a BAM DMA device.
+ *
+ * @h - BAM handle
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+int sps_dma_device_de_init(unsigned long h);
+
+/**
+ * Initialize connection mapping module
+ *
+ * This function initializes the SPS connection mapping module.
+ *
+ * @map_props - pointer to connection mapping BSP configuration properties
+ *
+ * @options - driver options bitflags (see SPS_OPT_*)
+ *
+ * @return 0 on success, negative value on error
+ *
+ */
+
+int sps_map_init(const struct sps_map *map_props, u32 options);
+
+/**
+ * De-initialize connection mapping module
+ *
+ * This function de-initializes the SPS connection mapping module.
+ *
+ */
+void sps_map_de_init(void);
+
+/*
+ * bam_pipe_reset - reset a BAM pipe.
+ * @base:	BAM virtual address
+ * @pipe:	pipe index
+ *
+ * This function resets a BAM pipe.
+ */
+void bam_pipe_reset(void *base, u32 pipe);
+
+/*
+ * bam_disable_pipe - disable a BAM pipe.
+ * @base:	BAM virtual address
+ * @pipe:	pipe index
+ *
+ * This function disables a BAM pipe.
+ */
+void bam_disable_pipe(void *base, u32 pipe);
+#endif	/* _SPSI_H_ */
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 63454b5..b5d3f08 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,3 +1,4 @@
 source "drivers/power/avs/Kconfig"
 source "drivers/power/reset/Kconfig"
 source "drivers/power/supply/Kconfig"
+source "drivers/power/qcom/Kconfig"
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index ff35c71..b0cafa9 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -1,3 +1,4 @@
 obj-$(CONFIG_POWER_AVS)		+= avs/
 obj-$(CONFIG_POWER_RESET)	+= reset/
 obj-$(CONFIG_POWER_SUPPLY)	+= supply/
+obj-$(CONFIG_ARCH_QCOM)		+= qcom/
diff --git a/drivers/power/qcom/Kconfig b/drivers/power/qcom/Kconfig
new file mode 100644
index 0000000..8625aa8
--- /dev/null
+++ b/drivers/power/qcom/Kconfig
@@ -0,0 +1,11 @@
+menu "Miscellaneous Qualcomm Technologies, Inc. power support"
+
+config MSM_APM
+	bool "Qualcomm Technologies, Inc. platform specific APM driver"
+	help
+	  Platform specific driver to manage the power source of
+	  memory arrays. Interfaces with regulator drivers to ensure
+	  SRAM Vmin requirements are met across different performance
+	  levels.
+
+endmenu
diff --git a/drivers/power/qcom/Makefile b/drivers/power/qcom/Makefile
new file mode 100644
index 0000000..818179c
--- /dev/null
+++ b/drivers/power/qcom/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MSM_APM)		+= apm.o
diff --git a/drivers/power/qcom/apm.c b/drivers/power/qcom/apm.c
new file mode 100644
index 0000000..3dfb439
--- /dev/null
+++ b/drivers/power/qcom/apm.c
@@ -0,0 +1,1062 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/arm-smccc.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/power/qcom/apm.h>
+#include <soc/qcom/scm.h>
+
+/*
+ *        VDD_APCC
+ * =============================================================
+ *       |      VDD_MX                  |                    |
+ *       |    ==========================|=============       |
+ *    ___|___   ___|___    ___|___   ___|___    ___|___   ___|___
+ *   |       | |       |  |       | |       |  |       | |       |
+ *   | APCC  | | MX HS |  | MX HS | | APCC  |  | MX HS | | APCC  |
+ *   |  HS   | |       |  |       | |  HS   |  |       | |  HS   |
+ *   |_______| |_______|  |_______| |_______|  |_______| |_______|
+ *       |_________|          |_________|         |__________|
+ *            |                    |                    |
+ *      ______|_____         ______|_____        _______|_____
+ *     |            |       |            |      |             |
+ *     |            |       |            |      |             |
+ *     |  CPU MEM   |       |   L2 MEM   |      |    L3 MEM   |
+ *     |   Arrays   |       |   Arrays   |      |    Arrays   |
+ *     |            |       |            |      |             |
+ *     |____________|       |____________|      |_____________|
+ *
+ */
+
+/* Register value definitions */
+#define APCS_GFMUXA_SEL_VAL            0x13
+#define APCS_GFMUXA_DESEL_VAL          0x03
+#define MSM_APM_MX_MODE_VAL            0x00
+#define MSM_APM_APCC_MODE_VAL          0x10
+#define MSM_APM_MX_DONE_VAL            0x00
+#define MSM_APM_APCC_DONE_VAL          0x03
+#define MSM_APM_OVERRIDE_SEL_VAL       0xb0
+#define MSM_APM_SEC_CLK_SEL_VAL        0x30
+#define SPM_EVENT_SET_VAL              0x01
+#define SPM_EVENT_CLEAR_VAL            0x00
+
+/* Register bit mask definitions */
+#define MSM_APM_CTL_STS_MASK            0x0f
+
+/* Register offset definitions */
+#define APCC_APM_MODE              0x00000098
+#define APCC_APM_CTL_STS           0x000000a8
+#define APCS_SPARE                 0x00000068
+#define APCS_VERSION               0x00000fd0
+
+#define HMSS_VERSION_1P2           0x10020000
+
+#define MSM_APM_SWITCH_TIMEOUT_US  10
+#define SPM_WAKEUP_DELAY_US        2
+#define SPM_EVENT_NUM              6
+
+#define MSM_APM_DRIVER_NAME        "qcom,msm-apm"
+
+enum {
+	CLOCK_ASSERT_ENABLE,
+	CLOCK_ASSERT_DISABLE,
+	CLOCK_ASSERT_TOGGLE,
+};
+
+enum {
+	MSM8996_ID,
+	MSM8996PRO_ID,
+	MSM8953_ID,
+};
+
+struct msm_apm_ctrl_dev {
+	struct list_head	list;
+	struct device		*dev;
+	enum msm_apm_supply	supply;
+	spinlock_t		lock;
+	void __iomem		*reg_base;
+	void __iomem		*apcs_csr_base;
+	void __iomem		**apcs_spm_events_addr;
+	void __iomem		*apc0_pll_ctl_addr;
+	void __iomem		*apc1_pll_ctl_addr;
+	bool			clk_src_override;
+	u32			version;
+	struct dentry		*debugfs;
+	u32			msm_id;
+};
+
+#if defined(CONFIG_DEBUG_FS)
+static struct dentry *apm_debugfs_base;
+#endif
+
+static DEFINE_MUTEX(apm_ctrl_list_mutex);
+static LIST_HEAD(apm_ctrl_list);
+
+static unsigned long __invoke_psci_fn_smc(unsigned long a, unsigned long b,
+					  unsigned long c, unsigned long d)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(a, b, c, d, 0, 0, 0, 0, &res);
+
+	return res.a0;
+}
+
+/*
+ * Get the resources associated with the APM controller from device tree
+ * and remap all I/O addresses that are relevant to this HW revision.
+ */
+static int msm_apm_ctrl_devm_ioremap(struct platform_device *pdev,
+				     struct msm_apm_ctrl_dev *ctrl)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	static const char *res_name[SPM_EVENT_NUM] = {
+		"apc0-l2-spm",
+		"apc1-l2-spm",
+		"apc0-cpu0-spm",
+		"apc0-cpu1-spm",
+		"apc1-cpu0-spm",
+		"apc1-cpu1-spm"
+	};
+	int i, ret = 0;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb");
+	if (!res) {
+		dev_err(dev, "Missing PM APCC Global register physical address");
+		return -EINVAL;
+	}
+	ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!ctrl->reg_base) {
+		dev_err(dev, "Failed to map PM APCC Global registers\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs-csr");
+	if (!res) {
+		dev_err(dev, "Missing APCS CSR physical base address");
+		return -EINVAL;
+	}
+	ctrl->apcs_csr_base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!ctrl->apcs_csr_base) {
+		dev_err(dev, "Failed to map APCS CSR registers\n");
+		return -ENOMEM;
+	}
+
+	ctrl->clk_src_override = of_property_read_bool(dev->of_node,
+					       "qcom,clock-source-override");
+
+	if (ctrl->clk_src_override)
+		dev_info(dev, "overriding clock sources across APM switch\n");
+
+	ctrl->version = readl_relaxed(ctrl->apcs_csr_base + APCS_VERSION);
+
+	if (ctrl->version >= HMSS_VERSION_1P2)
+		return ret;
+
+	ctrl->apcs_spm_events_addr = devm_kzalloc(&pdev->dev,
+						  SPM_EVENT_NUM
+						  * sizeof(void __iomem *),
+						  GFP_KERNEL);
+	if (!ctrl->apcs_spm_events_addr) {
+		dev_err(dev, "Failed to allocate memory for APCS SPM event registers\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < SPM_EVENT_NUM; i++) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						   res_name[i]);
+		if (!res) {
+			dev_err(dev, "Missing address for %s\n", res_name[i]);
+			ret = -EINVAL;
+			goto free_events;
+		}
+
+		ctrl->apcs_spm_events_addr[i] = devm_ioremap(dev, res->start,
+						resource_size(res));
+		if (!ctrl->apcs_spm_events_addr[i]) {
+			dev_err(dev, "Failed to map %s\n", res_name[i]);
+			ret = -ENOMEM;
+			goto free_events;
+		}
+
+		dev_dbg(dev, "%s event phys: %pa virt:0x%p\n", res_name[i],
+			&res->start, ctrl->apcs_spm_events_addr[i]);
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   "apc0-pll-ctl");
+	if (!res) {
+		dev_err(dev, "Missing APC0 PLL CTL physical address\n");
+		ret = -EINVAL;
+		goto free_events;
+	}
+
+	ctrl->apc0_pll_ctl_addr = devm_ioremap(dev,
+					   res->start,
+					   resource_size(res));
+	if (!ctrl->apc0_pll_ctl_addr) {
+		dev_err(dev, "Failed to map APC0 PLL CTL register\n");
+		ret = -ENOMEM;
+		goto free_events;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   "apc1-pll-ctl");
+	if (!res) {
+		dev_err(dev, "Missing APC1 PLL CTL physical address\n");
+		ret = -EINVAL;
+		goto free_events;
+	}
+
+	ctrl->apc1_pll_ctl_addr = devm_ioremap(dev,
+					   res->start,
+					   resource_size(res));
+	if (!ctrl->apc1_pll_ctl_addr) {
+		dev_err(dev, "Failed to map APC1 PLL CTL register\n");
+		ret = -ENOMEM;
+		goto free_events;
+	}
+
+	return ret;
+
+free_events:
+	devm_kfree(dev, ctrl->apcs_spm_events_addr);
+	return ret;
+}
+
+/* MSM8953 register offset definition */
+#define MSM8953_APM_DLY_CNTR		0x2ac
+
+/* Register field shift definitions */
+#define APM_CTL_SEL_SWITCH_DLY_SHIFT	0
+#define APM_CTL_RESUME_CLK_DLY_SHIFT	8
+#define APM_CTL_HALT_CLK_DLY_SHIFT	16
+#define APM_CTL_POST_HALT_DLY_SHIFT	24
+
+/* Register field mask definitions */
+#define APM_CTL_SEL_SWITCH_DLY_MASK	GENMASK(7, 0)
+#define APM_CTL_RESUME_CLK_DLY_MASK	GENMASK(15, 8)
+#define APM_CTL_HALT_CLK_DLY_MASK	GENMASK(23, 16)
+#define APM_CTL_POST_HALT_DLY_MASK	GENMASK(31, 24)
+
+/*
+ * Get the resources associated with the MSM8953 APM controller from
+ * device tree, remap all I/O addresses, and program the initial
+ * register configuration required for the MSM8953 APM controller device.
+ */
+static int msm8953_apm_ctrl_init(struct platform_device *pdev,
+				 struct msm_apm_ctrl_dev *ctrl)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	u32 delay_counter, val = 0, regval = 0;
+	int rc = 0;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb");
+	if (!res) {
+		dev_err(dev, "Missing PM APCC Global register physical address\n");
+		return -ENODEV;
+	}
+	ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!ctrl->reg_base) {
+		dev_err(dev, "Failed to map PM APCC Global registers\n");
+		return -ENOMEM;
+	}
+
+	/*
+	 * Initial APM register configuration required before starting
+	 * APM HW controller.
+	 */
+	regval = readl_relaxed(ctrl->reg_base + MSM8953_APM_DLY_CNTR);
+	val = regval;
+
+	if (of_find_property(dev->of_node, "qcom,apm-post-halt-delay", NULL)) {
+		rc = of_property_read_u32(dev->of_node,
+				"qcom,apm-post-halt-delay", &delay_counter);
+		if (rc < 0) {
+			dev_err(dev, "apm-post-halt-delay read failed, rc = %d",
+				rc);
+			return rc;
+		}
+
+		val &= ~APM_CTL_POST_HALT_DLY_MASK;
+		val |= (delay_counter << APM_CTL_POST_HALT_DLY_SHIFT)
+			& APM_CTL_POST_HALT_DLY_MASK;
+	}
+
+	if (of_find_property(dev->of_node, "qcom,apm-halt-clk-delay", NULL)) {
+		rc = of_property_read_u32(dev->of_node,
+				"qcom,apm-halt-clk-delay", &delay_counter);
+		if (rc < 0) {
+			dev_err(dev, "apm-halt-clk-delay read failed, rc = %d",
+				rc);
+			return rc;
+		}
+
+		val &= ~APM_CTL_HALT_CLK_DLY_MASK;
+		val |= (delay_counter << APM_CTL_HALT_CLK_DLY_SHIFT)
+			& APM_CTL_HALT_CLK_DLY_MASK;
+	}
+
+	if (of_find_property(dev->of_node, "qcom,apm-resume-clk-delay", NULL)) {
+		rc = of_property_read_u32(dev->of_node,
+				"qcom,apm-resume-clk-delay", &delay_counter);
+		if (rc < 0) {
+			dev_err(dev, "apm-resume-clk-delay read failed, rc = %d",
+				rc);
+			return rc;
+		}
+
+		val &= ~APM_CTL_RESUME_CLK_DLY_MASK;
+		val |= (delay_counter << APM_CTL_RESUME_CLK_DLY_SHIFT)
+			& APM_CTL_RESUME_CLK_DLY_MASK;
+	}
+
+	if (of_find_property(dev->of_node, "qcom,apm-sel-switch-delay", NULL)) {
+		rc = of_property_read_u32(dev->of_node,
+				"qcom,apm-sel-switch-delay", &delay_counter);
+		if (rc < 0) {
+			dev_err(dev, "apm-sel-switch-delay read failed, rc = %d",
+				rc);
+			return rc;
+		}
+
+		val &= ~APM_CTL_SEL_SWITCH_DLY_MASK;
+		val |= (delay_counter << APM_CTL_SEL_SWITCH_DLY_SHIFT)
+			& APM_CTL_SEL_SWITCH_DLY_MASK;
+	}
+
+	if (val != regval) {
+		writel_relaxed(val, ctrl->reg_base + MSM8953_APM_DLY_CNTR);
+		/* make sure write completes before return */
+		mb();
+	}
+
+	return rc;
+}
+
+static int msm_apm_secure_clock_source_override(
+			struct msm_apm_ctrl_dev *ctrl_dev, bool enable)
+{
+	int ret;
+
+	if (ctrl_dev->clk_src_override) {
+		ret = __invoke_psci_fn_smc(0xC4000020, 3, enable ?
+					   CLOCK_ASSERT_ENABLE :
+					   CLOCK_ASSERT_DISABLE, 0);
+		if (ret)
+			dev_err(ctrl_dev->dev, "PSCI request to switch to %s clock source failed\n",
+				enable ? "GPLL0" : "original");
+	}
+
+	return 0;
+}
+
+static int msm8996_apm_wait_for_switch(struct msm_apm_ctrl_dev *ctrl_dev,
+					u32 done_val)
+{
+	int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+	u32 regval;
+
+	while (timeout > 0) {
+		regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS);
+		if ((regval & MSM_APM_CTL_STS_MASK) == done_val)
+			break;
+
+		udelay(1);
+		timeout--;
+	}
+
+	if (timeout == 0) {
+		dev_err(ctrl_dev->dev, "%s switch timed out. APCC_APM_CTL_STS=0x%x\n",
+			done_val == MSM_APM_MX_DONE_VAL
+				? "APCC to MX" : "MX to APCC",
+			regval);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	unsigned long flags;
+	int i, ret;
+
+	mutex_lock(&scm_lmh_lock);
+	spin_lock_irqsave(&ctrl_dev->lock, flags);
+
+	ret = msm_apm_secure_clock_source_override(ctrl_dev, true);
+	if (ret)
+		goto done;
+
+	/* Perform revision-specific programming steps */
+	if (ctrl_dev->version < HMSS_VERSION_1P2) {
+		/* Clear SPM events */
+		for (i = 0; i < SPM_EVENT_NUM; i++)
+			writel_relaxed(SPM_EVENT_CLEAR_VAL,
+				       ctrl_dev->apcs_spm_events_addr[i]);
+
+		udelay(SPM_WAKEUP_DELAY_US);
+
+		/* Switch APC/CBF to GPLL0 clock */
+		writel_relaxed(APCS_GFMUXA_SEL_VAL,
+			       ctrl_dev->apcs_csr_base + APCS_SPARE);
+		ndelay(200);
+		writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL,
+			       ctrl_dev->apc0_pll_ctl_addr);
+		ndelay(200);
+		writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL,
+			       ctrl_dev->apc1_pll_ctl_addr);
+
+		/* Ensure writes complete before proceeding */
+		mb();
+	}
+
+	/* Switch arrays to MX supply and wait for its completion */
+	writel_relaxed(MSM_APM_MX_MODE_VAL, ctrl_dev->reg_base +
+		       APCC_APM_MODE);
+
+	/* Ensure write above completes before delaying */
+	mb();
+
+	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_MX_DONE_VAL);
+
+	/* Perform revision-specific programming steps */
+	if (ctrl_dev->version < HMSS_VERSION_1P2) {
+		/* Switch APC/CBF clocks to original source */
+		writel_relaxed(APCS_GFMUXA_DESEL_VAL,
+			       ctrl_dev->apcs_csr_base + APCS_SPARE);
+		ndelay(200);
+		writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL,
+			       ctrl_dev->apc0_pll_ctl_addr);
+		ndelay(200);
+		writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL,
+			       ctrl_dev->apc1_pll_ctl_addr);
+
+		/* Complete clock source switch before SPM event sequence */
+		mb();
+
+		/* Set SPM events */
+		for (i = 0; i < SPM_EVENT_NUM; i++)
+			writel_relaxed(SPM_EVENT_SET_VAL,
+				       ctrl_dev->apcs_spm_events_addr[i]);
+	}
+
+	/*
+	 * Ensure that HMSS v1.0/v1.1 register writes are completed before
+	 * bailing out in the case of a switching time out.
+	 */
+	if (ret)
+		goto done;
+
+	ret = msm_apm_secure_clock_source_override(ctrl_dev, false);
+	if (ret)
+		goto done;
+
+	ctrl_dev->supply = MSM_APM_SUPPLY_MX;
+	dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n");
+
+done:
+	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
+	mutex_unlock(&scm_lmh_lock);
+
+	return ret;
+}
+
+static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	unsigned long flags;
+	int i, ret;
+
+	mutex_lock(&scm_lmh_lock);
+	spin_lock_irqsave(&ctrl_dev->lock, flags);
+
+	ret = msm_apm_secure_clock_source_override(ctrl_dev, true);
+	if (ret)
+		goto done;
+
+	/* Perform revision-specific programming steps */
+	if (ctrl_dev->version < HMSS_VERSION_1P2) {
+		/* Clear SPM events */
+		for (i = 0; i < SPM_EVENT_NUM; i++)
+			writel_relaxed(SPM_EVENT_CLEAR_VAL,
+				       ctrl_dev->apcs_spm_events_addr[i]);
+
+		udelay(SPM_WAKEUP_DELAY_US);
+
+		/* Switch APC/CBF to GPLL0 clock */
+		writel_relaxed(APCS_GFMUXA_SEL_VAL,
+			       ctrl_dev->apcs_csr_base + APCS_SPARE);
+		ndelay(200);
+		writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL,
+			       ctrl_dev->apc0_pll_ctl_addr);
+		ndelay(200);
+		writel_relaxed(MSM_APM_OVERRIDE_SEL_VAL,
+			       ctrl_dev->apc1_pll_ctl_addr);
+
+		/* Ensure previous writes complete before proceeding */
+		mb();
+	}
+
+	/* Switch arrays to APCC supply and wait for its completion */
+	writel_relaxed(MSM_APM_APCC_MODE_VAL, ctrl_dev->reg_base +
+		       APCC_APM_MODE);
+
+	/* Ensure write above completes before delaying */
+	mb();
+
+	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_APCC_DONE_VAL);
+
+	/* Perform revision-specific programming steps */
+	if (ctrl_dev->version < HMSS_VERSION_1P2) {
+		/* Set SPM events */
+		for (i = 0; i < SPM_EVENT_NUM; i++)
+			writel_relaxed(SPM_EVENT_SET_VAL,
+				       ctrl_dev->apcs_spm_events_addr[i]);
+
+		/* Complete SPM event sequence before clock source switch */
+		mb();
+
+		/* Switch APC/CBF clocks to original source */
+		writel_relaxed(APCS_GFMUXA_DESEL_VAL,
+			       ctrl_dev->apcs_csr_base + APCS_SPARE);
+		ndelay(200);
+		writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL,
+			       ctrl_dev->apc0_pll_ctl_addr);
+		ndelay(200);
+		writel_relaxed(MSM_APM_SEC_CLK_SEL_VAL,
+			       ctrl_dev->apc1_pll_ctl_addr);
+	}
+
+	/*
+	 * Ensure that HMSS v1.0/v1.1 register writes are completed before
+	 * bailing out in the case of a switching time out.
+	 */
+	if (ret)
+		goto done;
+
+	ret = msm_apm_secure_clock_source_override(ctrl_dev, false);
+	if (ret)
+		goto done;
+
+	ctrl_dev->supply = MSM_APM_SUPPLY_APCC;
+	dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n");
+
+done:
+	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
+	mutex_unlock(&scm_lmh_lock);
+
+	return ret;
+}
+
+static int msm8996pro_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&ctrl_dev->lock, flags);
+
+	/* Switch arrays to MX supply and wait for its completion */
+	writel_relaxed(MSM_APM_MX_MODE_VAL, ctrl_dev->reg_base +
+		       APCC_APM_MODE);
+
+	/* Ensure write above completes before delaying */
+	mb();
+
+	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_MX_DONE_VAL);
+	if (ret)
+		goto done;
+
+	ctrl_dev->supply = MSM_APM_SUPPLY_MX;
+	dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n");
+
+done:
+	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
+
+	return ret;
+}
+
+static int msm8996pro_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&ctrl_dev->lock, flags);
+
+	/* Switch arrays to APCC supply and wait for its completion */
+	writel_relaxed(MSM_APM_APCC_MODE_VAL, ctrl_dev->reg_base +
+		       APCC_APM_MODE);
+
+	/* Ensure write above completes before delaying */
+	mb();
+
+	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_APCC_DONE_VAL);
+	if (ret)
+		goto done;
+
+	ctrl_dev->supply = MSM_APM_SUPPLY_APCC;
+	dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n");
+
+done:
+	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
+
+	return ret;
+}
+
+/* MSM8953 register value definitions */
+#define MSM8953_APM_MX_MODE_VAL            0x00
+#define MSM8953_APM_APCC_MODE_VAL          0x02
+#define MSM8953_APM_MX_DONE_VAL            0x00
+#define MSM8953_APM_APCC_DONE_VAL          0x03
+
+/* MSM8953 register offset definitions */
+#define MSM8953_APCC_APM_MODE              0x000002a8
+#define MSM8953_APCC_APM_CTL_STS           0x000002b0
+
+static int msm8953_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+	u32 regval;
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctrl_dev->lock, flags);
+
+	/* Switch arrays to MX supply and wait for its completion */
+	writel_relaxed(MSM8953_APM_MX_MODE_VAL, ctrl_dev->reg_base +
+		       MSM8953_APCC_APM_MODE);
+
+	/* Ensure write above completes before delaying */
+	mb();
+
+	while (timeout > 0) {
+		regval = readl_relaxed(ctrl_dev->reg_base +
+					MSM8953_APCC_APM_CTL_STS);
+		if ((regval & MSM_APM_CTL_STS_MASK) ==
+				MSM8953_APM_MX_DONE_VAL)
+			break;
+
+		udelay(1);
+		timeout--;
+	}
+
+	if (timeout == 0) {
+		ret = -ETIMEDOUT;
+		dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n",
+			regval);
+	} else {
+		ctrl_dev->supply = MSM_APM_SUPPLY_MX;
+		dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n");
+	}
+
+	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
+
+	return ret;
+}
+
+static int msm8953_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	int timeout = MSM_APM_SWITCH_TIMEOUT_US;
+	u32 regval;
+	int ret = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctrl_dev->lock, flags);
+
+	/* Switch arrays to APCC supply and wait for its completion */
+	writel_relaxed(MSM8953_APM_APCC_MODE_VAL, ctrl_dev->reg_base +
+		       MSM8953_APCC_APM_MODE);
+
+	/* Ensure write above completes before delaying */
+	mb();
+
+	while (timeout > 0) {
+		regval = readl_relaxed(ctrl_dev->reg_base +
+					MSM8953_APCC_APM_CTL_STS);
+		if ((regval & MSM_APM_CTL_STS_MASK) ==
+				MSM8953_APM_APCC_DONE_VAL)
+			break;
+
+		udelay(1);
+		timeout--;
+	}
+
+	if (timeout == 0) {
+		ret = -ETIMEDOUT;
+		dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n",
+			regval);
+	} else {
+		ctrl_dev->supply = MSM_APM_SUPPLY_APCC;
+		dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n");
+	}
+
+	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
+
+	return ret;
+}
+
+static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	int ret = 0;
+
+	switch (ctrl_dev->msm_id) {
+	case MSM8996_ID:
+		ret = msm8996_apm_switch_to_mx(ctrl_dev);
+		break;
+	case MSM8996PRO_ID:
+		ret = msm8996pro_apm_switch_to_mx(ctrl_dev);
+		break;
+	case MSM8953_ID:
+		ret = msm8953_apm_switch_to_mx(ctrl_dev);
+		break;
+	}
+
+	return ret;
+}
+
+static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	int ret = 0;
+
+	switch (ctrl_dev->msm_id) {
+	case MSM8996_ID:
+		ret = msm8996_apm_switch_to_apcc(ctrl_dev);
+		break;
+	case MSM8996PRO_ID:
+		ret = msm8996pro_apm_switch_to_apcc(ctrl_dev);
+		break;
+	case MSM8953_ID:
+		ret = msm8953_apm_switch_to_apcc(ctrl_dev);
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * msm_apm_get_supply() - Returns the supply that is currently
+ *			powering the memory arrays
+ * @ctrl_dev:                   Pointer to an MSM APM controller device
+ *
+ * Returns the supply currently selected by the APM.
+ */
+int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	return ctrl_dev->supply;
+}
+EXPORT_SYMBOL(msm_apm_get_supply);
+
+/**
+ * msm_apm_set_supply() - Perform the necessary steps to switch the voltage
+ *                        source of the memory arrays to a given supply
+ * @ctrl_dev:                   Pointer to an MSM APM controller device
+ * @supply:                     Power rail to use as supply for the memory
+ *                              arrays
+ *
+ * Returns 0 on success, -ETIMEDOUT on APM switch timeout, or -EPERM if
+ * the supply is not supported.
+ */
+int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev,
+		       enum msm_apm_supply supply)
+{
+	int ret;
+
+	switch (supply) {
+	case MSM_APM_SUPPLY_APCC:
+		ret = msm_apm_switch_to_apcc(ctrl_dev);
+		break;
+	case MSM_APM_SUPPLY_MX:
+		ret = msm_apm_switch_to_mx(ctrl_dev);
+		break;
+	default:
+		ret = -EPERM;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(msm_apm_set_supply);
+
+/**
+ * msm_apm_ctrl_dev_get() - get a handle to the MSM APM controller linked to
+ *                          the device in device tree
+ * @dev:                    Pointer to the device
+ *
+ * The device must specify "qcom,apm-ctrl" property in its device tree
+ * node which points to an MSM APM controller device node.
+ *
+ * Returns an MSM APM controller handle if successful or ERR_PTR on any error.
+ * If the APM controller device hasn't probed yet, ERR_PTR(-EPROBE_DEFER) is
+ * returned.
+ */
+struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev)
+{
+	struct msm_apm_ctrl_dev *ctrl_dev = NULL;
+	struct msm_apm_ctrl_dev *dev_found = ERR_PTR(-EPROBE_DEFER);
+	struct device_node *ctrl_node;
+
+	if (!dev || !dev->of_node) {
+		pr_err("Invalid device node\n");
+		return ERR_PTR(-EINVAL);
+	}
+
+	ctrl_node = of_parse_phandle(dev->of_node, "qcom,apm-ctrl", 0);
+	if (!ctrl_node) {
+		pr_err("Could not find qcom,apm-ctrl property in %s\n",
+		       dev->of_node->full_name);
+		return ERR_PTR(-ENXIO);
+	}
+
+	mutex_lock(&apm_ctrl_list_mutex);
+	list_for_each_entry(ctrl_dev, &apm_ctrl_list, list) {
+		if (ctrl_dev->dev && ctrl_dev->dev->of_node == ctrl_node) {
+			dev_found = ctrl_dev;
+			break;
+		}
+	}
+	mutex_unlock(&apm_ctrl_list_mutex);
+
+	of_node_put(ctrl_node);
+	return dev_found;
+}
+EXPORT_SYMBOL(msm_apm_ctrl_dev_get);
+
+#if defined(CONFIG_DEBUG_FS)
+
+static int apm_supply_dbg_open(struct inode *inode, struct file *filep)
+{
+	filep->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t apm_supply_dbg_read(struct file *filep, char __user *ubuf,
+				   size_t count, loff_t *ppos)
+{
+	struct msm_apm_ctrl_dev *ctrl_dev = filep->private_data;
+	char buf[10];
+	int len;
+
+	if (!ctrl_dev) {
+		pr_err("invalid apm ctrl handle\n");
+		return -ENODEV;
+	}
+
+	if (ctrl_dev->supply == MSM_APM_SUPPLY_APCC)
+		len = snprintf(buf, sizeof(buf), "APCC\n");
+	else if (ctrl_dev->supply == MSM_APM_SUPPLY_MX)
+		len = snprintf(buf, sizeof(buf), "MX\n");
+	else
+		len = snprintf(buf, sizeof(buf), "ERR\n");
+
+	return simple_read_from_buffer(ubuf, count, ppos, buf, len);
+}
+
+static const struct file_operations apm_supply_fops = {
+	.open = apm_supply_dbg_open,
+	.read = apm_supply_dbg_read,
+};
+
+static void apm_debugfs_base_init(void)
+{
+	apm_debugfs_base = debugfs_create_dir("msm-apm", NULL);
+
+	if (IS_ERR_OR_NULL(apm_debugfs_base))
+		pr_err("msm-apm debugfs base directory creation failed\n");
+}
+
+static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	struct dentry *temp;
+
+	if (IS_ERR_OR_NULL(apm_debugfs_base)) {
+		pr_err("Base directory missing, cannot create apm debugfs nodes\n");
+		return;
+	}
+
+	ctrl_dev->debugfs = debugfs_create_dir(dev_name(ctrl_dev->dev),
+					       apm_debugfs_base);
+	if (IS_ERR_OR_NULL(ctrl_dev->debugfs)) {
+		pr_err("%s debugfs directory creation failed\n",
+		       dev_name(ctrl_dev->dev));
+		return;
+	}
+
+	temp = debugfs_create_file("supply", 0444, ctrl_dev->debugfs,
+				   ctrl_dev, &apm_supply_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		pr_err("supply mode creation failed\n");
+		return;
+	}
+}
+
+static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev)
+{
+	if (!IS_ERR_OR_NULL(ctrl_dev->debugfs))
+		debugfs_remove_recursive(ctrl_dev->debugfs);
+}
+
+static void apm_debugfs_base_remove(void)
+{
+	debugfs_remove_recursive(apm_debugfs_base);
+}
+#else
+
+static void apm_debugfs_base_init(void)
+{}
+
+static void apm_debugfs_init(struct msm_apm_ctrl_dev *ctrl_dev)
+{}
+
+static void apm_debugfs_deinit(struct msm_apm_ctrl_dev *ctrl_dev)
+{}
+
+static void apm_debugfs_base_remove(void)
+{}
+
+#endif
+
+static const struct of_device_id msm_apm_match_table[] = {
+	{
+		.compatible = "qcom,msm-apm",
+		.data = (void *)(uintptr_t)MSM8996_ID,
+	},
+	{
+		.compatible = "qcom,msm8996pro-apm",
+		.data = (void *)(uintptr_t)MSM8996PRO_ID,
+	},
+	{
+		.compatible = "qcom,msm8953-apm",
+		.data = (void *)(uintptr_t)MSM8953_ID,
+	},
+	{}
+};
+
+static int msm_apm_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct msm_apm_ctrl_dev *ctrl;
+	const struct of_device_id *match;
+	int ret = 0;
+
+	dev_dbg(dev, "probing MSM Array Power Mux driver\n");
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -ENODEV;
+	}
+
+	match = of_match_device(msm_apm_match_table, dev);
+	if (!match)
+		return -ENODEV;
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&ctrl->list);
+	spin_lock_init(&ctrl->lock);
+	ctrl->dev = dev;
+	ctrl->msm_id = (uintptr_t)match->data;
+	platform_set_drvdata(pdev, ctrl);
+
+	switch (ctrl->msm_id) {
+	case MSM8996_ID:
+	case MSM8996PRO_ID:
+		ret = msm_apm_ctrl_devm_ioremap(pdev, ctrl);
+		if (ret) {
+			dev_err(dev, "Failed to add APM controller device\n");
+			return ret;
+		}
+		break;
+	case MSM8953_ID:
+		ret = msm8953_apm_ctrl_init(pdev, ctrl);
+		if (ret) {
+			dev_err(dev, "Failed to initialize APM controller device: ret=%d\n",
+				ret);
+			return ret;
+		}
+		break;
+	default:
+		dev_err(dev, "unable to add APM controller device for msm_id:%d\n",
+			ctrl->msm_id);
+		return -ENODEV;
+	}
+
+	apm_debugfs_init(ctrl);
+	mutex_lock(&apm_ctrl_list_mutex);
+	list_add_tail(&ctrl->list, &apm_ctrl_list);
+	mutex_unlock(&apm_ctrl_list_mutex);
+
+	dev_dbg(dev, "MSM Array Power Mux driver probe successful");
+
+	return ret;
+}
+
+static int msm_apm_remove(struct platform_device *pdev)
+{
+	struct msm_apm_ctrl_dev *ctrl_dev;
+
+	ctrl_dev = platform_get_drvdata(pdev);
+	if (ctrl_dev) {
+		mutex_lock(&apm_ctrl_list_mutex);
+		list_del(&ctrl_dev->list);
+		mutex_unlock(&apm_ctrl_list_mutex);
+		apm_debugfs_deinit(ctrl_dev);
+	}
+
+	return 0;
+}
+
+static struct platform_driver msm_apm_driver = {
+	.driver		= {
+		.name		= MSM_APM_DRIVER_NAME,
+		.of_match_table	= msm_apm_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= msm_apm_probe,
+	.remove		= msm_apm_remove,
+};
+
+static int __init msm_apm_init(void)
+{
+	apm_debugfs_base_init();
+	return platform_driver_register(&msm_apm_driver);
+}
+
+static void __exit msm_apm_exit(void)
+{
+	platform_driver_unregister(&msm_apm_driver);
+	apm_debugfs_base_remove();
+}
+
+arch_initcall(msm_apm_init);
+module_exit(msm_apm_exit);
+
+MODULE_DESCRIPTION("MSM Array Power Mux driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c
index bae9ef2..efcc4e8 100644
--- a/drivers/power/reset/msm-poweroff.c
+++ b/drivers/power/reset/msm-poweroff.c
@@ -287,6 +287,8 @@ static void msm_restart_prepare(const char *cmd)
 		}
 	}
 
+	flush_cache_all();
+
 	/*outer_flush_all is not supported by 64bit kernel*/
 #ifndef CONFIG_ARM64
 	outer_flush_all();
diff --git a/drivers/power/supply/power_supply_sysfs.c b/drivers/power/supply/power_supply_sysfs.c
index 7a22d8c..505ba01 100644
--- a/drivers/power/supply/power_supply_sysfs.c
+++ b/drivers/power/supply/power_supply_sysfs.c
@@ -69,6 +69,17 @@ static ssize_t power_supply_show_property(struct device *dev,
 	static char *scope_text[] = {
 		"Unknown", "System", "Device"
 	};
+	static const char * const typec_text[] = {
+		"Nothing attached", "Sink attached", "Powered cable w/ sink",
+		"Debug Accessory", "Audio Adapter", "Powered cable w/o sink",
+		"Source attached (default current)",
+		"Source attached (medium current)",
+		"Source attached (high current)",
+		"Non compliant",
+	};
+	static const char * const typec_pr_text[] = {
+		"none", "dual power role", "sink", "source"
+	};
 	ssize_t ret = 0;
 	struct power_supply *psy = dev_get_drvdata(dev);
 	const ptrdiff_t off = attr - power_supply_attrs;
@@ -104,6 +115,10 @@ static ssize_t power_supply_show_property(struct device *dev,
 		return sprintf(buf, "%s\n", type_text[value.intval]);
 	else if (off == POWER_SUPPLY_PROP_SCOPE)
 		return sprintf(buf, "%s\n", scope_text[value.intval]);
+	else if (off == POWER_SUPPLY_PROP_TYPEC_MODE)
+		return sprintf(buf, "%s\n", typec_text[value.intval]);
+	else if (off == POWER_SUPPLY_PROP_TYPEC_POWER_ROLE)
+		return sprintf(buf, "%s\n", typec_pr_text[value.intval]);
 	else if (off >= POWER_SUPPLY_PROP_MODEL_NAME)
 		return sprintf(buf, "%s\n", value.strval);
 
@@ -205,7 +220,18 @@ static struct device_attribute power_supply_attrs[] = {
 	POWER_SUPPLY_ATTR(usb_hc),
 	POWER_SUPPLY_ATTR(usb_otg),
 	POWER_SUPPLY_ATTR(charge_enabled),
+	POWER_SUPPLY_ATTR(typec_mode),
+	POWER_SUPPLY_ATTR(typec_cc_orientation),
+	POWER_SUPPLY_ATTR(typec_power_role),
+	POWER_SUPPLY_ATTR(pd_allowed),
+	POWER_SUPPLY_ATTR(pd_active),
+	POWER_SUPPLY_ATTR(pd_in_hard_reset),
+	POWER_SUPPLY_ATTR(pd_current_max),
+	POWER_SUPPLY_ATTR(pd_usb_suspend_supported),
+	POWER_SUPPLY_ATTR(pe_start),
 	POWER_SUPPLY_ATTR(set_ship_mode),
+	POWER_SUPPLY_ATTR(boost_current),
+	POWER_SUPPLY_ATTR(force_tlim),
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_ATTR(charge_counter_ext),
 	/* Properties of type `const char *' */
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index bf01288..e7d13ae 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -329,6 +329,16 @@
 	  To compile this driver as a module, choose M here: the module
 	  will be called pwm-pxa.
 
+config PWM_QPNP
+	tristate "Qualcomm Technologies, Inc. QPNP LPG/PWM support"
+	depends on SPMI
+	help
+	  This driver supports PWM/LPG devices in Qualcomm Technologies, Inc.
+	  PMIC chips which comply with QPNP.  QPNP is an SPMI based PMIC
+	  implementation.  These devices support Pulse Width Modulation output
+	  with user generated patterns. They share a lookup table with size of
+	  64 entries.
+
 config PWM_RCAR
 	tristate "Renesas R-Car PWM support"
 	depends on ARCH_RENESAS || COMPILE_TEST
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index 1194c54..24c1baf 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -31,6 +31,7 @@
 obj-$(CONFIG_PWM_PCA9685)	+= pwm-pca9685.o
 obj-$(CONFIG_PWM_PUV3)		+= pwm-puv3.o
 obj-$(CONFIG_PWM_PXA)		+= pwm-pxa.o
+obj-$(CONFIG_PWM_QPNP)		+= pwm-qpnp.o
 obj-$(CONFIG_PWM_RCAR)		+= pwm-rcar.o
 obj-$(CONFIG_PWM_RENESAS_TPU)	+= pwm-renesas-tpu.o
 obj-$(CONFIG_PWM_ROCKCHIP)	+= pwm-rockchip.o
diff --git a/drivers/pwm/pwm-qpnp.c b/drivers/pwm/pwm-qpnp.c
new file mode 100644
index 0000000..86f08d2
--- /dev/null
+++ b/drivers/pwm/pwm-qpnp.c
@@ -0,0 +1,2218 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm Technologies, Inc. QPNP Pulse Width Modulation (PWM) driver
+ *
+ * The HW module is also called LPG (Light Pattern Generator).
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/radix-tree.h>
+#include <linux/qpnp/pwm.h>
+
+#define QPNP_LPG_DRIVER_NAME	"qcom,qpnp-pwm"
+#define QPNP_LPG_CHANNEL_BASE	"qpnp-lpg-channel-base"
+#define QPNP_LPG_LUT_BASE	"qpnp-lpg-lut-base"
+
+#define QPNP_PWM_MODE_ONLY_SUB_TYPE	0x0B
+#define QPNP_LPG_CHAN_SUB_TYPE		0x2
+#define QPNP_LPG_S_CHAN_SUB_TYPE	0x11
+
+/* LPG Control for LPG_PATTERN_CONFIG */
+#define QPNP_RAMP_DIRECTION_SHIFT	4
+#define QPNP_RAMP_DIRECTION_MASK	0x10
+#define QPNP_PATTERN_REPEAT_SHIFT	3
+#define QPNP_PATTERN_REPEAT_MASK	0x08
+#define QPNP_RAMP_TOGGLE_SHIFT		2
+#define QPNP_RAMP_TOGGLE_MASK		0x04
+#define QPNP_EN_PAUSE_HI_SHIFT		1
+#define QPNP_EN_PAUSE_HI_MASK		0x02
+#define QPNP_EN_PAUSE_LO_MASK		0x01
+
+/* LPG Control for LPG_PWM_SIZE_CLK */
+#define QPNP_PWM_SIZE_SHIFT_SUB_TYPE		2
+#define QPNP_PWM_SIZE_MASK_SUB_TYPE		0x4
+#define QPNP_PWM_FREQ_CLK_SELECT_MASK_SUB_TYPE	0x03
+#define QPNP_PWM_SIZE_9_BIT_SUB_TYPE		0x01
+
+#define QPNP_SET_PWM_CLK_SUB_TYPE(val, clk, pwm_size) \
+do { \
+	val = (clk + 1) & QPNP_PWM_FREQ_CLK_SELECT_MASK_SUB_TYPE; \
+	val |= (((pwm_size > 6 ? QPNP_PWM_SIZE_9_BIT_SUB_TYPE : 0) << \
+		QPNP_PWM_SIZE_SHIFT_SUB_TYPE) & QPNP_PWM_SIZE_MASK_SUB_TYPE); \
+} while (0)
+
+#define QPNP_GET_PWM_SIZE_SUB_TYPE(reg) ((reg & QPNP_PWM_SIZE_MASK_SUB_TYPE) \
+				>> QPNP_PWM_SIZE_SHIFT_SUB_TYPE)
+
+#define QPNP_PWM_SIZE_SHIFT			4
+#define QPNP_PWM_SIZE_MASK			0x30
+#define QPNP_PWM_FREQ_CLK_SELECT_MASK		0x03
+#define QPNP_MIN_PWM_BIT_SIZE		6
+#define QPNP_MAX_PWM_BIT_SIZE		9
+#define QPNP_PWM_SIZES_SUPPORTED	10
+
+#define QPNP_SET_PWM_CLK(val, clk, pwm_size) \
+do { \
+	val = (clk + 1) & QPNP_PWM_FREQ_CLK_SELECT_MASK; \
+	val |= (((pwm_size - QPNP_MIN_PWM_BIT_SIZE) << \
+		QPNP_PWM_SIZE_SHIFT) & QPNP_PWM_SIZE_MASK); \
+} while (0)
+
+#define QPNP_GET_PWM_SIZE(reg) ((reg & QPNP_PWM_SIZE_MASK) \
+				>> QPNP_PWM_SIZE_SHIFT)
+
+/* LPG Control for LPG_PWM_FREQ_PREDIV_CLK */
+#define QPNP_PWM_FREQ_PRE_DIVIDE_SHIFT		5
+#define QPNP_PWM_FREQ_PRE_DIVIDE_MASK		0x60
+#define QPNP_PWM_FREQ_EXP_MASK			0x07
+
+#define QPNP_SET_PWM_FREQ_PREDIV(val, pre_div, pre_div_exp) \
+do { \
+	val = (pre_div << QPNP_PWM_FREQ_PRE_DIVIDE_SHIFT) & \
+				QPNP_PWM_FREQ_PRE_DIVIDE_MASK;	\
+	val |= (pre_div_exp & QPNP_PWM_FREQ_EXP_MASK);	\
+} while (0)
+
+/* LPG Control for LPG_PWM_TYPE_CONFIG */
+#define QPNP_EN_GLITCH_REMOVAL_SHIFT		5
+#define QPNP_EN_GLITCH_REMOVAL_MASK		0x20
+#define QPNP_EN_FULL_SCALE_SHIFT		3
+#define QPNP_EN_FULL_SCALE_MASK			0x08
+#define QPNP_EN_PHASE_STAGGER_SHIFT		2
+#define QPNP_EN_PHASE_STAGGER_MASK		0x04
+#define QPNP_PHASE_STAGGER_MASK			0x03
+
+/* LPG Control for PWM_VALUE_LSB */
+#define QPNP_PWM_VALUE_LSB_MASK			0xFF
+
+/* LPG Control for PWM_VALUE_MSB */
+#define QPNP_PWM_VALUE_MSB_SHIFT		8
+#define QPNP_PWM_VALUE_MSB_MASK			0x01
+
+/* LPG Control for ENABLE_CONTROL */
+#define QPNP_EN_PWM_HIGH_SHIFT			7
+#define QPNP_EN_PWM_HIGH_MASK			0x80
+#define QPNP_EN_PWM_LO_SHIFT			6
+#define QPNP_EN_PWM_LO_MASK			0x40
+#define QPNP_EN_PWM_OUTPUT_SHIFT		5
+#define QPNP_EN_PWM_OUTPUT_MASK			0x20
+#define QPNP_PWM_SRC_SELECT_SHIFT		2
+#define QPNP_PWM_SRC_SELECT_MASK		0x04
+#define QPNP_PWM_EN_RAMP_GEN_SHIFT		1
+#define QPNP_PWM_EN_RAMP_GEN_MASK		0x02
+
+/* LPG Control for PWM_SYNC */
+#define QPNP_PWM_SYNC_VALUE			0x01
+#define QPNP_PWM_SYNC_MASK			0x01
+
+/* LPG Control for RAMP_CONTROL */
+#define QPNP_RAMP_START_MASK			0x01
+
+#define QPNP_ENABLE_LUT_V0(value) (value |= QPNP_RAMP_START_MASK)
+#define QPNP_DISABLE_LUT_V0(value) (value &= ~QPNP_RAMP_START_MASK)
+#define QPNP_ENABLE_LUT_V1(value, id) (value |= BIT(id))
+
+/* LPG Control for RAMP_STEP_DURATION_LSB */
+#define QPNP_RAMP_STEP_DURATION_LSB_MASK	0xFF
+
+/* LPG Control for RAMP_STEP_DURATION_MSB */
+#define QPNP_RAMP_STEP_DURATION_MSB_SHIFT	8
+#define QPNP_RAMP_STEP_DURATION_MSB_MASK	0x01
+
+#define QPNP_PWM_1KHZ				1024
+#define QPNP_GET_RAMP_STEP_DURATION(ramp_time_ms) \
+		((ramp_time_ms * QPNP_PWM_1KHZ) / 1000)
+
+/* LPG Control for PAUSE_HI_MULTIPLIER_LSB */
+#define QPNP_PAUSE_HI_MULTIPLIER_LSB_MASK	0xFF
+
+/* LPG Control for PAUSE_HI_MULTIPLIER_MSB */
+#define QPNP_PAUSE_HI_MULTIPLIER_MSB_SHIFT	8
+#define QPNP_PAUSE_HI_MULTIPLIER_MSB_MASK	0x1F
+
+/* LPG Control for PAUSE_LO_MULTIPLIER_LSB */
+#define QPNP_PAUSE_LO_MULTIPLIER_LSB_MASK	0xFF
+
+/* LPG Control for PAUSE_LO_MULTIPLIER_MSB */
+#define QPNP_PAUSE_LO_MULTIPLIER_MSB_SHIFT	8
+#define QPNP_PAUSE_LO_MULTIPLIER_MSB_MASK	0x1F
+
+/* LPG Control for HI_INDEX */
+#define QPNP_HI_INDEX_MASK			0x3F
+
+/* LPG Control for LO_INDEX */
+#define QPNP_LO_INDEX_MASK			0x3F
+
+/* LPG DTEST */
+#define QPNP_LPG_DTEST_LINE_MAX			4
+#define QPNP_LPG_DTEST_OUTPUT_MAX		5
+#define QPNP_LPG_DTEST_OUTPUT_MASK		0x07
+
+/* PWM DTEST */
+#define QPNP_PWM_DTEST_LINE_MAX			2
+#define QPNP_PWM_DTEST_OUTPUT_MAX		2
+#define QPNP_PWM_DTEST_OUTPUT_MASK		0x03
+
+#define NUM_CLOCKS				3
+#define QPNP_PWM_M_MAX				7
+#define NSEC_1024HZ	(NSEC_PER_SEC / 1024)
+#define NSEC_32768HZ	(NSEC_PER_SEC / 32768)
+#define NSEC_19P2MHZ	(NSEC_PER_SEC / 19200000)
+
+#define NUM_LPG_PRE_DIVIDE	4
+
+#define PRE_DIVIDE_1		1
+#define PRE_DIVIDE_3		3
+#define PRE_DIVIDE_5		5
+#define PRE_DIVIDE_6		6
+
+#define SPMI_LPG_REG_BASE_OFFSET	0x40
+#define SPMI_LPG_REVISION2_OFFSET	0x1
+#define SPMI_LPG_REV1_RAMP_CONTROL_OFFSET	0x86
+#define SPMI_LPG_SUB_TYPE_OFFSET	0x5
+#define SPMI_LPG_PWM_SYNC		0x7
+#define SPMI_LPG_REG_ADDR(b, n)	(b + SPMI_LPG_REG_BASE_OFFSET + (n))
+#define SPMI_MAX_BUF_LEN	8
+
+#define QPNP_PWM_LUT_NOT_SUPPORTED	0x1
+
+/* Supported PWM sizes */
+#define QPNP_PWM_SIZE_6_BIT		6
+#define QPNP_PWM_SIZE_7_BIT		7
+#define QPNP_PWM_SIZE_8_BIT		8
+#define QPNP_PWM_SIZE_9_BIT		9
+
+#define QPNP_PWM_SIZE_6_9_BIT		0x9
+#define QPNP_PWM_SIZE_7_8_BIT		0x6
+#define QPNP_PWM_SIZE_6_7_9_BIT		0xB
+
+/*
+ * Registers that don't need to be cached are defined below from an offset
+ * of SPMI_LPG_REG_BASE_OFFSET.
+ */
+#define QPNP_LPG_SEC_ACCESS		0x90
+#define QPNP_LPG_DTEST			0xA2
+
+/* Supported time levels */
+enum time_level {
+	LVL_NSEC,
+	LVL_USEC,
+};
+
+/* LPG revisions */
+enum qpnp_lpg_revision {
+	QPNP_LPG_REVISION_0 = 0x0,
+	QPNP_LPG_REVISION_1 = 0x1,
+};
+
+/* LPG LUT MODE STATE */
+enum qpnp_lut_state {
+	QPNP_LUT_ENABLE = 0x0,
+	QPNP_LUT_DISABLE = 0x1,
+};
+
+/* PWM MODE STATE */
+enum qpnp_pwm_state {
+	QPNP_PWM_ENABLE = 0x0,
+	QPNP_PWM_DISABLE = 0x1,
+};
+
+/* SPMI LPG registers */
+enum qpnp_lpg_registers_list {
+	QPNP_LPG_PATTERN_CONFIG,
+	QPNP_LPG_PWM_SIZE_CLK,
+	QPNP_LPG_PWM_FREQ_PREDIV_CLK,
+	QPNP_LPG_PWM_TYPE_CONFIG,
+	QPNP_PWM_VALUE_LSB,
+	QPNP_PWM_VALUE_MSB,
+	QPNP_ENABLE_CONTROL,
+	QPNP_RAMP_CONTROL,
+	QPNP_RAMP_STEP_DURATION_LSB = QPNP_RAMP_CONTROL + 9,
+	QPNP_RAMP_STEP_DURATION_MSB,
+	QPNP_PAUSE_HI_MULTIPLIER_LSB,
+	QPNP_PAUSE_HI_MULTIPLIER_MSB,
+	QPNP_PAUSE_LO_MULTIPLIER_LSB,
+	QPNP_PAUSE_LO_MULTIPLIER_MSB,
+	QPNP_HI_INDEX,
+	QPNP_LO_INDEX,
+	QPNP_TOTAL_LPG_SPMI_REGISTERS
+};
+
+/*
+ * Formula from HSID,
+ * pause_time (hi/lo) = (pause_cnt- 1)*(ramp_ms)
+ * OR,
+ * pause_cnt = (pause_time / ramp_ms) + 1
+ */
+#define QPNP_SET_PAUSE_CNT(to_pause_cnt, from_pause, ramp_ms) \
+	(to_pause_cnt = (from_pause / (ramp_ms ? ramp_ms : 1)) + 1)
+
+
+static unsigned int pt_t[NUM_LPG_PRE_DIVIDE][NUM_CLOCKS] = {
+	{	PRE_DIVIDE_1 * NSEC_1024HZ,
+		PRE_DIVIDE_1 * NSEC_32768HZ,
+		PRE_DIVIDE_1 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_3 * NSEC_1024HZ,
+		PRE_DIVIDE_3 * NSEC_32768HZ,
+		PRE_DIVIDE_3 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_5 * NSEC_1024HZ,
+		PRE_DIVIDE_5 * NSEC_32768HZ,
+		PRE_DIVIDE_5 * NSEC_19P2MHZ,
+	},
+	{	PRE_DIVIDE_6 * NSEC_1024HZ,
+		PRE_DIVIDE_6 * NSEC_32768HZ,
+		PRE_DIVIDE_6 * NSEC_19P2MHZ,
+	},
+};
+
+struct qpnp_lut_config {
+	u8	*duty_pct_list;
+	int	list_len;
+	int	ramp_index;
+	int	lo_index;
+	int	hi_index;
+	int	lut_pause_hi_cnt;
+	int	lut_pause_lo_cnt;
+	int	ramp_step_ms;
+	bool	ramp_direction;
+	bool	pattern_repeat;
+	bool	ramp_toggle;
+	bool	enable_pause_hi;
+	bool	enable_pause_lo;
+};
+
+struct qpnp_lpg_config {
+	struct qpnp_lut_config	lut_config;
+	u16			base_addr;
+	u16			lut_base_addr;
+	u16			lut_size;
+};
+
+struct _qpnp_pwm_config {
+	int				pwm_value;
+	int				pwm_period;	/* in microseconds */
+	int				pwm_duty;	/* in microseconds */
+	struct pwm_period_config	period;
+	int				supported_sizes;
+	int				force_pwm_size;
+};
+
+/* Public facing structure */
+struct qpnp_pwm_chip {
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	struct pwm_chip         chip;
+	bool			enabled;
+	struct _qpnp_pwm_config	pwm_config;
+	struct	qpnp_lpg_config	lpg_config;
+	spinlock_t		lpg_lock;
+	enum qpnp_lpg_revision	revision;
+	u8			sub_type;
+	u32			flags;
+	u8	qpnp_lpg_registers[QPNP_TOTAL_LPG_SPMI_REGISTERS];
+	int			channel_id;
+	const char		*channel_owner;
+	u32			dtest_line;
+	u32			dtest_output;
+	bool			in_test_mode;
+};
+
+/* Internal functions */
+static inline struct qpnp_pwm_chip *qpnp_pwm_from_pwm_dev(
+					struct pwm_device *pwm)
+{
+	return container_of(pwm->chip, struct qpnp_pwm_chip, chip);
+}
+
+static inline struct qpnp_pwm_chip *qpnp_pwm_from_pwm_chip(
+					struct pwm_chip *chip)
+{
+	return container_of(chip, struct qpnp_pwm_chip, chip);
+}
+
+static inline void qpnp_set_pattern_config(u8 *val,
+			struct qpnp_lut_config *lut_config)
+{
+	*val = lut_config->enable_pause_lo & QPNP_EN_PAUSE_LO_MASK;
+	*val |= (lut_config->enable_pause_hi << QPNP_EN_PAUSE_HI_SHIFT) &
+						QPNP_EN_PAUSE_HI_MASK;
+	*val |= (lut_config->ramp_toggle << QPNP_RAMP_TOGGLE_SHIFT) &
+						QPNP_RAMP_TOGGLE_MASK;
+	*val |= (lut_config->pattern_repeat << QPNP_PATTERN_REPEAT_SHIFT) &
+						QPNP_PATTERN_REPEAT_MASK;
+	*val |= (lut_config->ramp_direction << QPNP_RAMP_DIRECTION_SHIFT) &
+						QPNP_RAMP_DIRECTION_MASK;
+}
+
+static inline void qpnp_set_pwm_type_config(u8 *val, bool glitch,
+			bool full_scale, bool en_phase, bool phase)
+{
+	*val = phase;
+	*val |= (en_phase << QPNP_EN_PHASE_STAGGER_SHIFT) &
+				QPNP_EN_PHASE_STAGGER_MASK;
+	*val |= (full_scale << QPNP_EN_FULL_SCALE_SHIFT) &
+				QPNP_EN_FULL_SCALE_MASK;
+	*val |= (glitch << QPNP_EN_GLITCH_REMOVAL_SHIFT) &
+				QPNP_EN_GLITCH_REMOVAL_MASK;
+}
+
+static int qpnp_set_control(struct qpnp_pwm_chip *chip, bool pwm_hi,
+		bool pwm_lo, bool pwm_out, bool pwm_src, bool ramp_gen)
+{
+	int value;
+
+	value = (ramp_gen << QPNP_PWM_EN_RAMP_GEN_SHIFT) |
+		(pwm_src << QPNP_PWM_SRC_SELECT_SHIFT) |
+		(pwm_lo << QPNP_EN_PWM_LO_SHIFT) |
+		(pwm_hi << QPNP_EN_PWM_HIGH_SHIFT);
+	if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)
+		value |= (pwm_out << QPNP_EN_PWM_OUTPUT_SHIFT);
+	return value;
+}
+
+#define QPNP_ENABLE_LUT_CONTROL(chip) \
+	qpnp_set_control((chip), 0, 0, 0, 0, 1)
+#define QPNP_ENABLE_PWM_CONTROL(chip) \
+	qpnp_set_control((chip), 0, 0, 0, 1, 0)
+#define QPNP_ENABLE_PWM_MODE(chip) \
+	qpnp_set_control((chip), 1, 1, 1, 1, 0)
+#define QPNP_ENABLE_PWM_MODE_GPLED_CHANNEL(chip) \
+	qpnp_set_control((chip), 1, 1, 1, 1, 1)
+#define QPNP_ENABLE_LPG_MODE(chip) \
+	qpnp_set_control((chip), 1, 1, 1, 0, 1)
+#define QPNP_DISABLE_PWM_MODE(chip) \
+	qpnp_set_control((chip), 0, 0, 0, 1, 0)
+#define QPNP_DISABLE_LPG_MODE(chip) \
+	qpnp_set_control((chip), 0, 0, 0, 0, 1)
+#define QPNP_IS_PWM_CONFIG_SELECTED(val) (val & QPNP_PWM_SRC_SELECT_MASK)
+
+#define QPNP_ENABLE_PWM_MODE_ONLY_SUB_TYPE			0x80
+#define QPNP_DISABLE_PWM_MODE_ONLY_SUB_TYPE			0x0
+#define QPNP_PWM_MODE_ONLY_ENABLE_DISABLE_MASK_SUB_TYPE	0x80
+
+static inline void qpnp_convert_to_lut_flags(int *flags,
+				struct qpnp_lut_config *l_config)
+{
+	*flags = ((l_config->ramp_direction ? PM_PWM_LUT_RAMP_UP : 0) |
+		(l_config->pattern_repeat ? PM_PWM_LUT_LOOP : 0)|
+		(l_config->ramp_toggle ? PM_PWM_LUT_REVERSE : 0) |
+		(l_config->enable_pause_hi ? PM_PWM_LUT_PAUSE_HI_EN : 0) |
+		(l_config->enable_pause_lo ? PM_PWM_LUT_PAUSE_LO_EN : 0));
+}
+
+static inline void qpnp_set_lut_params(struct lut_params *l_params,
+		struct qpnp_lut_config *l_config, int s_idx, int size)
+{
+	l_params->start_idx = s_idx;
+	l_params->idx_len = size;
+	l_params->lut_pause_hi = l_config->lut_pause_hi_cnt;
+	l_params->lut_pause_lo = l_config->lut_pause_lo_cnt;
+	l_params->ramp_step_ms = l_config->ramp_step_ms;
+	qpnp_convert_to_lut_flags(&l_params->flags, l_config);
+}
+
+static void qpnp_lpg_save(u8 *u8p, u8 mask, u8 val)
+{
+	*u8p &= ~mask;
+	*u8p |= val & mask;
+}
+
+static int qpnp_lpg_save_and_write(u8 value, u8 mask, u8 *reg, u16 addr,
+				u16 size, struct qpnp_pwm_chip *chip)
+{
+	qpnp_lpg_save(reg, mask, value);
+
+	return regmap_bulk_write(chip->regmap, addr, reg, size);
+}
+
+/*
+ * PWM Frequency = Clock Frequency / (N * T)
+ *	or
+ * PWM Period = Clock Period * (N * T)
+ *	where
+ * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
+ * T = Pre-divide * 2^m, where m = 0..7 (exponent)
+ *
+ * This is the formula to figure out m for the best pre-divide and clock:
+ * (PWM Period / N) = (Pre-divide * Clock Period) * 2^m
+ */
+static void qpnp_lpg_calc_period(enum time_level tm_lvl,
+				unsigned int period_value,
+				struct qpnp_pwm_chip *chip)
+{
+	int		n, m, clk, div;
+	int		best_m, best_div, best_clk;
+	unsigned int	last_err, cur_err, min_err;
+	unsigned int	tmp_p, period_n;
+	int             supported_sizes = chip->pwm_config.supported_sizes;
+	int		force_pwm_size = chip->pwm_config.force_pwm_size;
+	struct pwm_period_config *period = &chip->pwm_config.period;
+
+	/* PWM Period / N */
+	if (supported_sizes == QPNP_PWM_SIZE_7_8_BIT)
+		n = 7;
+	else
+		n = 6;
+
+	if (tm_lvl == LVL_USEC) {
+		if (period_value < ((unsigned int)(-1) / NSEC_PER_USEC)) {
+			period_n = (period_value * NSEC_PER_USEC) >> n;
+		} else {
+			if (supported_sizes == QPNP_PWM_SIZE_7_8_BIT)
+				n = 8;
+			else
+				n = 9;
+			period_n = (period_value >> n) * NSEC_PER_USEC;
+		}
+	} else {
+		period_n = period_value >> n;
+	}
+
+	if (force_pwm_size != 0) {
+		if (n < force_pwm_size)
+			period_n = period_n >> (force_pwm_size - n);
+		else
+			period_n = period_n << (n - force_pwm_size);
+		n = force_pwm_size;
+		pr_info("LPG channel '%d' pwm size is forced to=%d\n",
+					chip->channel_id, n);
+	}
+
+	min_err = last_err = (unsigned int)(-1);
+	best_m = 0;
+	best_clk = 0;
+	best_div = 0;
+	for (clk = 0; clk < NUM_CLOCKS; clk++) {
+		for (div = 0; div < NUM_LPG_PRE_DIVIDE; div++) {
+			/* period_n = (PWM Period / N) */
+			/* tmp_p = (Pre-divide * Clock Period) * 2^m */
+			tmp_p = pt_t[div][clk];
+			for (m = 0; m <= QPNP_PWM_M_MAX; m++) {
+				if (period_n > tmp_p)
+					cur_err = period_n - tmp_p;
+				else
+					cur_err = tmp_p - period_n;
+
+				if (cur_err < min_err) {
+					min_err = cur_err;
+					best_m = m;
+					best_clk = clk;
+					best_div = div;
+				}
+
+				if (m && cur_err > last_err)
+					/* Break for bigger cur_err */
+					break;
+
+				last_err = cur_err;
+				tmp_p <<= 1;
+			}
+		}
+	}
+
+	/* Adapt to optimal pwm size, the higher the resolution the better */
+	if (!force_pwm_size) {
+		if (supported_sizes == QPNP_PWM_SIZE_7_8_BIT) {
+			if (n == 7 && best_m >= 1) {
+				n += 1;
+				best_m -= 1;
+			}
+		} else if (n == 6) {
+			if (best_m >= 3) {
+				n += 3;
+				best_m -= 3;
+			} else if (best_m >= 1 && (
+				chip->sub_type != QPNP_PWM_MODE_ONLY_SUB_TYPE &&
+				chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)) {
+				n += 1;
+				best_m -= 1;
+			}
+		}
+	}
+
+	period->pwm_size = n;
+	period->clk = best_clk;
+	period->pre_div = best_div;
+	period->pre_div_exp = best_m;
+}
+
+static void qpnp_lpg_calc_pwm_value(struct _qpnp_pwm_config *pwm_config,
+				      unsigned int period_value,
+				      unsigned int duty_value)
+{
+	unsigned int max_pwm_value, tmp;
+
+	/* Figure out pwm_value with overflow handling */
+	tmp = 1 << (sizeof(tmp) * 8 - pwm_config->period.pwm_size);
+	if (duty_value < tmp) {
+		tmp = duty_value << pwm_config->period.pwm_size;
+		pwm_config->pwm_value = tmp / period_value;
+	} else {
+		tmp = period_value >> pwm_config->period.pwm_size;
+		pwm_config->pwm_value = duty_value / tmp;
+	}
+	max_pwm_value = (1 << pwm_config->period.pwm_size) - 1;
+	if (pwm_config->pwm_value > max_pwm_value)
+		pwm_config->pwm_value = max_pwm_value;
+	pr_debug("pwm_value: %d\n", pwm_config->pwm_value);
+}
+
+static int qpnp_lpg_change_table(struct qpnp_pwm_chip *chip,
+					int duty_pct[], int raw_value)
+{
+	unsigned int		pwm_value, max_pwm_value;
+	struct qpnp_lut_config	*lut = &chip->lpg_config.lut_config;
+	int			i, pwm_size, rc = 0;
+	int			burst_size = SPMI_MAX_BUF_LEN;
+	int			list_len = lut->list_len << 1;
+	int			offset = (lut->lo_index << 1) - 2;
+
+	pwm_size = QPNP_GET_PWM_SIZE(
+			chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) +
+				QPNP_MIN_PWM_BIT_SIZE;
+
+	max_pwm_value = (1 << pwm_size) - 1;
+
+	if (unlikely(lut->list_len != (lut->hi_index - lut->lo_index + 1))) {
+		pr_err("LUT internal Data structure corruption detected\n");
+		pr_err("LUT list size: %d\n", lut->list_len);
+		pr_err("However, index size is: %d\n",
+				(lut->hi_index - lut->lo_index + 1));
+		return -EINVAL;
+	}
+
+	for (i = 0; i < lut->list_len; i++) {
+		if (raw_value)
+			pwm_value = duty_pct[i];
+		else
+			pwm_value = (duty_pct[i] << pwm_size) / 100;
+
+		if (pwm_value > max_pwm_value)
+			pwm_value = max_pwm_value;
+
+		if (chip->pwm_config.supported_sizes == QPNP_PWM_SIZE_7_8_BIT) {
+			lut->duty_pct_list[i] = pwm_value;
+		} else {
+			lut->duty_pct_list[i*2] = pwm_value;
+			lut->duty_pct_list[(i*2)+1] = (pwm_value >>
+			 QPNP_PWM_VALUE_MSB_SHIFT) & QPNP_PWM_VALUE_MSB_MASK;
+		}
+	}
+
+	/*
+	 * For the Keypad Backlight Lookup Table (KPDBL_LUT),
+	 * offset is lo_index.
+	 */
+	if (chip->pwm_config.supported_sizes == QPNP_PWM_SIZE_7_8_BIT)
+		offset = lut->lo_index;
+
+	/* Write with max allowable burst mode, each entry is of two bytes */
+	for (i = 0; i < list_len; i += burst_size) {
+		if (i + burst_size >= list_len)
+			burst_size = list_len - i;
+		rc = regmap_bulk_write(chip->regmap,
+			       chip->lpg_config.lut_base_addr + offset + i,
+			       lut->duty_pct_list + i,
+			       burst_size);
+	}
+
+	return rc;
+}
+
+static void qpnp_lpg_save_period(struct qpnp_pwm_chip *chip)
+{
+	u8 mask, val;
+	struct _qpnp_pwm_config	*pwm_config = &chip->pwm_config;
+
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {
+		QPNP_SET_PWM_CLK_SUB_TYPE(val, pwm_config->period.clk,
+				pwm_config->period.pwm_size);
+		mask = QPNP_PWM_SIZE_MASK_SUB_TYPE |
+				QPNP_PWM_FREQ_CLK_SELECT_MASK_SUB_TYPE;
+	} else {
+		QPNP_SET_PWM_CLK(val, pwm_config->period.clk,
+				pwm_config->period.pwm_size);
+		mask = QPNP_PWM_SIZE_MASK | QPNP_PWM_FREQ_CLK_SELECT_MASK;
+	}
+
+	qpnp_lpg_save(&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK],
+							mask, val);
+
+	QPNP_SET_PWM_FREQ_PREDIV(val, pwm_config->period.pre_div,
+					pwm_config->period.pre_div_exp);
+
+	mask = QPNP_PWM_FREQ_PRE_DIVIDE_MASK | QPNP_PWM_FREQ_EXP_MASK;
+
+	qpnp_lpg_save(&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK],
+								mask, val);
+}
+
+static int qpnp_lpg_save_pwm_value(struct qpnp_pwm_chip *chip)
+{
+	unsigned int		max_pwm_value;
+	int			pwm_size;
+	u8			mask, value;
+	struct _qpnp_pwm_config	*pwm_config = &chip->pwm_config;
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	int rc;
+
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE)
+		pwm_size = QPNP_GET_PWM_SIZE_SUB_TYPE(
+			chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) ?
+				QPNP_MAX_PWM_BIT_SIZE : QPNP_MIN_PWM_BIT_SIZE;
+	else
+		pwm_size = QPNP_GET_PWM_SIZE(
+			chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]) +
+				QPNP_MIN_PWM_BIT_SIZE;
+
+	max_pwm_value = (1 << pwm_size) - 1;
+
+	if (pwm_config->pwm_value > max_pwm_value)
+		pwm_config->pwm_value = max_pwm_value;
+
+	value = pwm_config->pwm_value;
+	mask = QPNP_PWM_VALUE_LSB_MASK;
+
+	pr_debug("pwm_lsb value:%d\n", value & mask);
+	rc = qpnp_lpg_save_and_write(value, mask,
+			&chip->qpnp_lpg_registers[QPNP_PWM_VALUE_LSB],
+			SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+			QPNP_PWM_VALUE_LSB), 1, chip);
+	if (rc)
+		return rc;
+
+	value = (pwm_config->pwm_value >> QPNP_PWM_VALUE_MSB_SHIFT) &
+					QPNP_PWM_VALUE_MSB_MASK;
+
+	mask = QPNP_PWM_VALUE_MSB_MASK;
+
+	pr_debug("pwm_msb value:%d\n", value);
+	rc = qpnp_lpg_save_and_write(value, mask,
+			&chip->qpnp_lpg_registers[QPNP_PWM_VALUE_MSB],
+			SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+			QPNP_PWM_VALUE_MSB), 1, chip);
+	if (rc)
+		return rc;
+
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE ||
+		chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) {
+		value = QPNP_PWM_SYNC_VALUE & QPNP_PWM_SYNC_MASK;
+		rc = regmap_write(chip->regmap,
+					SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+						SPMI_LPG_PWM_SYNC),
+					value);
+	}
+
+	return rc;
+}
+
+static int qpnp_lpg_configure_pattern(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	struct qpnp_lut_config	*lut_config = &lpg_config->lut_config;
+	u8			value, mask;
+
+	qpnp_set_pattern_config(&value, lut_config);
+
+	mask = QPNP_RAMP_DIRECTION_MASK | QPNP_PATTERN_REPEAT_MASK |
+			QPNP_RAMP_TOGGLE_MASK | QPNP_EN_PAUSE_HI_MASK |
+			QPNP_EN_PAUSE_LO_MASK;
+
+	return qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_LPG_PATTERN_CONFIG],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PATTERN_CONFIG), 1, chip);
+}
+
+static int qpnp_lpg_glitch_removal(struct qpnp_pwm_chip *chip, bool enable)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	u8 value, mask;
+
+	qpnp_set_pwm_type_config(&value, enable ? 1 : 0, 0, 0, 0);
+
+	mask = QPNP_EN_GLITCH_REMOVAL_MASK | QPNP_EN_FULL_SCALE_MASK |
+			QPNP_EN_PHASE_STAGGER_MASK | QPNP_PHASE_STAGGER_MASK;
+
+	pr_debug("pwm_type_config: %d\n", value);
+	return qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_LPG_PWM_TYPE_CONFIG],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PWM_TYPE_CONFIG), 1, chip);
+}
+
+static int qpnp_lpg_configure_pwm(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	int rc;
+
+	pr_debug("pwm_size_clk: %d\n",
+		chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]);
+	rc = regmap_write(chip->regmap,
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PWM_SIZE_CLK),
+		*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]);
+
+	if (rc)
+		return rc;
+
+	pr_debug("pwm_freq_prediv_clk: %d\n",
+		chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK]);
+	rc = regmap_write(chip->regmap,
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PWM_FREQ_PREDIV_CLK),
+		*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK]);
+	if (rc)
+		return rc;
+
+	/* Disable glitch removal when LPG/PWM is configured */
+	rc = qpnp_lpg_glitch_removal(chip, false);
+	if (rc) {
+		pr_err("Error in disabling glitch control, rc=%d\n", rc);
+		return rc;
+	}
+	return rc;
+}
+
+static int qpnp_configure_pwm_control(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	u8			value, mask;
+
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE)
+		return 0;
+
+	value = QPNP_ENABLE_PWM_CONTROL(chip);
+
+	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+		QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK;
+	if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)
+		mask |= QPNP_EN_PWM_OUTPUT_MASK;
+
+	return qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_ENABLE_CONTROL), 1, chip);
+
+}
+
+static int qpnp_configure_lpg_control(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	u8			value, mask;
+
+	value = QPNP_ENABLE_LUT_CONTROL(chip);
+
+	mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+		QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK;
+	if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)
+		mask |= QPNP_EN_PWM_OUTPUT_MASK;
+
+	return qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_ENABLE_CONTROL), 1, chip);
+
+}
+
+static int qpnp_lpg_configure_ramp_step_duration(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	struct qpnp_lut_config	lut_config = lpg_config->lut_config;
+	int			rc, value;
+	u8			val, mask;
+
+	value = QPNP_GET_RAMP_STEP_DURATION(lut_config.ramp_step_ms);
+	val = value & QPNP_RAMP_STEP_DURATION_LSB_MASK;
+	mask = QPNP_RAMP_STEP_DURATION_LSB_MASK;
+
+	rc = qpnp_lpg_save_and_write(val, mask,
+		&chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_LSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_RAMP_STEP_DURATION_LSB), 1, chip);
+	if (rc)
+		return rc;
+
+	val = (value >> QPNP_RAMP_STEP_DURATION_MSB_SHIFT) &
+				QPNP_RAMP_STEP_DURATION_MSB_MASK;
+
+	mask = QPNP_RAMP_STEP_DURATION_MSB_MASK;
+
+	return qpnp_lpg_save_and_write(val, mask,
+		&chip->qpnp_lpg_registers[QPNP_RAMP_STEP_DURATION_MSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_RAMP_STEP_DURATION_MSB), 1, chip);
+}
+
+static int qpnp_lpg_configure_pause(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	struct qpnp_lut_config	lut_config = lpg_config->lut_config;
+	u8			value, mask;
+	int			rc = 0;
+
+	if (lut_config.enable_pause_hi) {
+		value = lut_config.lut_pause_hi_cnt;
+		mask = QPNP_PAUSE_HI_MULTIPLIER_LSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
+		if (rc)
+			return rc;
+
+		value = (lut_config.lut_pause_hi_cnt >>
+			QPNP_PAUSE_HI_MULTIPLIER_MSB_SHIFT) &
+					QPNP_PAUSE_HI_MULTIPLIER_MSB_MASK;
+
+		mask = QPNP_PAUSE_HI_MULTIPLIER_MSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
+	} else {
+		value = 0;
+		mask = QPNP_PAUSE_HI_MULTIPLIER_LSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_LSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_LSB), 1, chip);
+		if (rc)
+			return rc;
+
+		mask = QPNP_PAUSE_HI_MULTIPLIER_MSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_HI_MULTIPLIER_MSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_HI_MULTIPLIER_MSB), 1, chip);
+		if (rc)
+			return rc;
+
+	}
+
+	if (lut_config.enable_pause_lo) {
+		value = lut_config.lut_pause_lo_cnt;
+		mask = QPNP_PAUSE_LO_MULTIPLIER_LSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
+		if (rc)
+			return rc;
+
+		value = (lut_config.lut_pause_lo_cnt >>
+				QPNP_PAUSE_LO_MULTIPLIER_MSB_SHIFT) &
+					QPNP_PAUSE_LO_MULTIPLIER_MSB_MASK;
+
+		mask = QPNP_PAUSE_LO_MULTIPLIER_MSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
+	} else {
+		value = 0;
+		mask = QPNP_PAUSE_LO_MULTIPLIER_LSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_LSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_LSB), 1, chip);
+		if (rc)
+			return rc;
+
+		mask = QPNP_PAUSE_LO_MULTIPLIER_MSB_MASK;
+
+		rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_PAUSE_LO_MULTIPLIER_MSB],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_PAUSE_LO_MULTIPLIER_MSB), 1, chip);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int qpnp_lpg_configure_index(struct qpnp_pwm_chip *chip)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	struct qpnp_lut_config	lut_config = lpg_config->lut_config;
+	u8			value, mask;
+	int			rc = 0;
+
+	value = lut_config.hi_index;
+	mask = QPNP_HI_INDEX_MASK;
+
+	rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_HI_INDEX],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_HI_INDEX), 1, chip);
+	if (rc)
+		return rc;
+
+	value = lut_config.lo_index;
+	mask = QPNP_LO_INDEX_MASK;
+
+	rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_LO_INDEX],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LO_INDEX), 1, chip);
+
+	return rc;
+}
+
+static int qpnp_lpg_change_lut(struct qpnp_pwm_chip *chip)
+{
+	int	rc;
+
+	rc = qpnp_lpg_configure_pattern(chip);
+	if (rc) {
+		pr_err("Failed to configure LUT pattern");
+		return rc;
+	}
+	rc = qpnp_lpg_configure_pwm(chip);
+	if (rc) {
+		pr_err("Failed to configure LUT pattern");
+		return rc;
+	}
+	rc = qpnp_configure_lpg_control(chip);
+	if (rc) {
+		pr_err("Failed to configure pause registers");
+		return rc;
+	}
+	rc = qpnp_lpg_configure_ramp_step_duration(chip);
+	if (rc) {
+		pr_err("Failed to configure duty time");
+		return rc;
+	}
+	rc = qpnp_lpg_configure_pause(chip);
+	if (rc) {
+		pr_err("Failed to configure pause registers");
+		return rc;
+	}
+	rc = qpnp_lpg_configure_index(chip);
+	if (rc) {
+		pr_err("Failed to configure index registers");
+		return rc;
+	}
+	return rc;
+}
+
+static int qpnp_dtest_config(struct qpnp_pwm_chip *chip, bool enable)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	u8			value;
+	u8			mask;
+	u16			addr;
+	int			rc = 0;
+
+	value = 0xA5;
+
+	addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr, QPNP_LPG_SEC_ACCESS);
+
+	rc = regmap_write(chip->regmap, addr, value);
+
+	if (rc) {
+		pr_err("Couldn't set the access for test mode\n");
+		return rc;
+	}
+
+	addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+			QPNP_LPG_DTEST + chip->dtest_line - 1);
+
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE)
+		mask = QPNP_PWM_DTEST_OUTPUT_MASK;
+	else
+		mask = QPNP_LPG_DTEST_OUTPUT_MASK;
+
+	if (enable)
+		value = chip->dtest_output & mask;
+	else
+		value = 0;
+
+	pr_debug("Setting TEST mode for channel %d addr:%x value: %x\n",
+		chip->channel_id, addr, value);
+
+	rc = regmap_write(chip->regmap, addr, value);
+
+	return rc;
+}
+
+static int qpnp_lpg_configure_lut_state(struct qpnp_pwm_chip *chip,
+				enum qpnp_lut_state state)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	u8			value1, value2, mask1, mask2;
+	u8			*reg1, *reg2;
+	u16			addr, addr1;
+	int			rc;
+	bool			test_enable;
+
+	value1 = chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	reg1 = &chip->qpnp_lpg_registers[QPNP_RAMP_CONTROL];
+	reg2 = &chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL];
+	mask2 = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+		 QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK;
+	if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)
+		mask2 |= QPNP_EN_PWM_OUTPUT_MASK;
+
+	if (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE
+		&& chip->revision == QPNP_LPG_REVISION_0) {
+		if (state == QPNP_LUT_ENABLE) {
+			QPNP_ENABLE_LUT_V0(value1);
+			value2 = QPNP_ENABLE_LPG_MODE(chip);
+		} else {
+			QPNP_DISABLE_LUT_V0(value1);
+			value2 = QPNP_DISABLE_LPG_MODE(chip);
+		}
+		mask1 = QPNP_RAMP_START_MASK;
+		addr1 = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+					QPNP_RAMP_CONTROL);
+	} else if ((chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE
+			&& chip->revision == QPNP_LPG_REVISION_1)
+			|| chip->sub_type == QPNP_LPG_S_CHAN_SUB_TYPE) {
+		if (state == QPNP_LUT_ENABLE) {
+			QPNP_ENABLE_LUT_V1(value1,
+					lpg_config->lut_config.ramp_index);
+			value2 = QPNP_ENABLE_LPG_MODE(chip);
+		} else {
+			value2 = QPNP_DISABLE_LPG_MODE(chip);
+		}
+		mask1 = value1;
+		addr1 = lpg_config->lut_base_addr +
+			SPMI_LPG_REV1_RAMP_CONTROL_OFFSET;
+	} else {
+		pr_err("Unsupported LPG subtype 0x%02x, revision 0x%02x\n",
+			chip->sub_type, chip->revision);
+		return -EINVAL;
+	}
+
+	addr = SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+				QPNP_ENABLE_CONTROL);
+
+	if (chip->in_test_mode) {
+		test_enable = (state == QPNP_LUT_ENABLE) ? 1 : 0;
+		rc = qpnp_dtest_config(chip, test_enable);
+		if (rc)
+			pr_err("Failed to configure TEST mode\n");
+	}
+
+	rc = qpnp_lpg_save_and_write(value2, mask2, reg2,
+					addr, 1, chip);
+	if (rc)
+		return rc;
+
+	if (state == QPNP_LUT_ENABLE
+		|| (chip->sub_type == QPNP_LPG_CHAN_SUB_TYPE
+		&& chip->revision == QPNP_LPG_REVISION_0))
+		rc = qpnp_lpg_save_and_write(value1, mask1, reg1,
+					addr1, 1, chip);
+	return rc;
+}
+
+static inline int qpnp_enable_pwm_mode(struct qpnp_pwm_chip *chip)
+{
+	if (chip->pwm_config.supported_sizes == QPNP_PWM_SIZE_7_8_BIT)
+		return QPNP_ENABLE_PWM_MODE_GPLED_CHANNEL(chip);
+	return QPNP_ENABLE_PWM_MODE(chip);
+}
+
+static int qpnp_lpg_configure_pwm_state(struct qpnp_pwm_chip *chip,
+					enum qpnp_pwm_state state)
+{
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	u8			value, mask;
+	int			rc;
+	bool			test_enable;
+
+	if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {
+		if (state == QPNP_PWM_ENABLE)
+			value = QPNP_ENABLE_PWM_MODE_ONLY_SUB_TYPE;
+		else
+			value = QPNP_DISABLE_PWM_MODE_ONLY_SUB_TYPE;
+
+		mask = QPNP_PWM_MODE_ONLY_ENABLE_DISABLE_MASK_SUB_TYPE;
+	} else {
+		if (state == QPNP_PWM_ENABLE)
+			value = qpnp_enable_pwm_mode(chip);
+		else
+			value = QPNP_DISABLE_PWM_MODE(chip);
+
+		mask = QPNP_EN_PWM_HIGH_MASK | QPNP_EN_PWM_LO_MASK |
+			QPNP_PWM_SRC_SELECT_MASK | QPNP_PWM_EN_RAMP_GEN_MASK;
+		if (chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE)
+			mask |= QPNP_EN_PWM_OUTPUT_MASK;
+	}
+
+	if (chip->in_test_mode) {
+		test_enable = (state == QPNP_PWM_ENABLE) ? 1 : 0;
+		rc = qpnp_dtest_config(chip, test_enable);
+		if (rc)
+			pr_err("Failed to configure TEST mode\n");
+	}
+
+	pr_debug("pwm_enable_control: %d\n", value);
+	rc = qpnp_lpg_save_and_write(value, mask,
+		&chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL],
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_ENABLE_CONTROL), 1, chip);
+	if (rc)
+		goto out;
+
+	/*
+	 * Due to LPG hardware bug, in the PWM mode, having enabled PWM,
+	 * We have to write PWM values one more time.
+	 */
+	if (state == QPNP_PWM_ENABLE)
+		return qpnp_lpg_save_pwm_value(chip);
+
+out:
+	return rc;
+}
+
+static int _pwm_config(struct qpnp_pwm_chip *chip,
+				enum time_level tm_lvl,
+				int duty_value, int period_value)
+{
+	int rc;
+	struct _qpnp_pwm_config *pwm_config = &chip->pwm_config;
+	struct pwm_period_config *period = &pwm_config->period;
+
+	pwm_config->pwm_duty = (tm_lvl == LVL_USEC) ? duty_value :
+			duty_value / NSEC_PER_USEC;
+	qpnp_lpg_calc_pwm_value(pwm_config, period_value, duty_value);
+	rc = qpnp_lpg_save_pwm_value(chip);
+	if (rc)
+		goto out;
+	rc = qpnp_lpg_configure_pwm(chip);
+	if (rc)
+		goto out;
+	rc = qpnp_configure_pwm_control(chip);
+	if (rc)
+		goto out;
+
+	if (!rc && chip->enabled) {
+		rc = qpnp_lpg_configure_pwm_state(chip, QPNP_PWM_ENABLE);
+		if (rc) {
+			pr_err("Error in configuring pwm state, rc=%d\n", rc);
+			return rc;
+		}
+
+		/* Enable the glitch removal after PWM is enabled */
+		rc = qpnp_lpg_glitch_removal(chip, true);
+		if (rc) {
+			pr_err("Error in enabling glitch control, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	pr_debug("duty/period=%u/%u %s: pwm_value=%d (of %d)\n",
+		 (unsigned int)duty_value, (unsigned int)period_value,
+		 (tm_lvl == LVL_USEC) ? "usec" : "nsec",
+		 pwm_config->pwm_value, 1 << period->pwm_size);
+
+out:
+	return rc;
+}
+
+static int _pwm_lut_config(struct qpnp_pwm_chip *chip, int period_us,
+		int duty_pct[], struct lut_params lut_params)
+{
+	struct qpnp_lpg_config		*lpg_config;
+	struct qpnp_lut_config		*lut_config;
+	struct pwm_period_config	*period;
+	struct _qpnp_pwm_config		*pwm_config;
+	int				start_idx = lut_params.start_idx;
+	int				len = lut_params.idx_len;
+	int				flags = lut_params.flags;
+	int				raw_lut, ramp_step_ms;
+	int				rc = 0;
+
+	pwm_config = &chip->pwm_config;
+	lpg_config = &chip->lpg_config;
+	lut_config = &lpg_config->lut_config;
+	period = &pwm_config->period;
+
+	if (flags & PM_PWM_LUT_NO_TABLE)
+		goto after_table_write;
+
+	raw_lut = 0;
+	if (flags & PM_PWM_LUT_USE_RAW_VALUE)
+		raw_lut = 1;
+
+	lut_config->list_len = len;
+	lut_config->lo_index = start_idx + 1;
+	lut_config->hi_index = start_idx + len;
+
+	rc = qpnp_lpg_change_table(chip, duty_pct, raw_lut);
+	if (rc) {
+		pr_err("qpnp_lpg_change_table: rc=%d\n", rc);
+		return -EINVAL;
+	}
+
+after_table_write:
+	ramp_step_ms = lut_params.ramp_step_ms;
+
+	if (ramp_step_ms > PM_PWM_LUT_RAMP_STEP_TIME_MAX)
+		ramp_step_ms = PM_PWM_LUT_RAMP_STEP_TIME_MAX;
+
+	QPNP_SET_PAUSE_CNT(lut_config->lut_pause_lo_cnt,
+			lut_params.lut_pause_lo, ramp_step_ms);
+	if (lut_config->lut_pause_lo_cnt > PM_PWM_MAX_PAUSE_CNT)
+		lut_config->lut_pause_lo_cnt = PM_PWM_MAX_PAUSE_CNT;
+
+	QPNP_SET_PAUSE_CNT(lut_config->lut_pause_hi_cnt,
+			lut_params.lut_pause_hi, ramp_step_ms);
+	if (lut_config->lut_pause_hi_cnt > PM_PWM_MAX_PAUSE_CNT)
+		lut_config->lut_pause_hi_cnt = PM_PWM_MAX_PAUSE_CNT;
+
+	lut_config->ramp_step_ms = ramp_step_ms;
+
+	lut_config->ramp_direction  = !!(flags & PM_PWM_LUT_RAMP_UP);
+	lut_config->pattern_repeat  = !!(flags & PM_PWM_LUT_LOOP);
+	lut_config->ramp_toggle	    = !!(flags & PM_PWM_LUT_REVERSE);
+	lut_config->enable_pause_hi = !!(flags & PM_PWM_LUT_PAUSE_HI_EN);
+	lut_config->enable_pause_lo = !!(flags & PM_PWM_LUT_PAUSE_LO_EN);
+
+	rc = qpnp_lpg_change_lut(chip);
+
+	if (!rc && chip->enabled)
+		rc = qpnp_lpg_configure_lut_state(chip, QPNP_LUT_ENABLE);
+
+	return rc;
+}
+
+static int _pwm_enable(struct qpnp_pwm_chip *chip)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	if (QPNP_IS_PWM_CONFIG_SELECTED(
+		chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||
+			chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED) {
+		rc = qpnp_lpg_configure_pwm_state(chip, QPNP_PWM_ENABLE);
+	} else if (!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED)) {
+		rc = qpnp_lpg_configure_lut_state(chip, QPNP_LUT_ENABLE);
+	}
+
+	if (!rc)
+		chip->enabled = true;
+
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
+	return rc;
+}
+
+/* APIs */
+/**
+ * qpnp_pwm_free - free a PWM device
+ * @pwm_chip: the PWM chip
+ * @pwm: the PWM device
+ */
+static void qpnp_pwm_free(struct pwm_chip *pwm_chip,
+		struct pwm_device *pwm)
+{
+	struct qpnp_pwm_chip	*chip = qpnp_pwm_from_pwm_chip(pwm_chip);
+	unsigned long		flags;
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	qpnp_lpg_configure_pwm_state(chip, QPNP_PWM_DISABLE);
+	if (!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED))
+		qpnp_lpg_configure_lut_state(chip, QPNP_LUT_DISABLE);
+
+	chip->enabled = false;
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+}
+
+/**
+ * qpnp_pwm_config - change a PWM device configuration
+ * @pwm: the PWM device
+ * @period_ns: period in nanoseconds
+ * @duty_ns: duty cycle in nanoseconds
+ */
+static int qpnp_pwm_config(struct pwm_chip *pwm_chip,
+	struct pwm_device *pwm, int duty_ns, int period_ns)
+{
+	int rc;
+	unsigned long flags;
+	struct qpnp_pwm_chip *chip = qpnp_pwm_from_pwm_chip(pwm_chip);
+	int prev_period_us = chip->pwm_config.pwm_period;
+
+	if ((unsigned int)period_ns < PM_PWM_PERIOD_MIN * NSEC_PER_USEC) {
+		pr_err("Invalid pwm handle or parameters\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	if (prev_period_us > INT_MAX / NSEC_PER_USEC ||
+			prev_period_us * NSEC_PER_USEC != period_ns) {
+		qpnp_lpg_calc_period(LVL_NSEC, period_ns, chip);
+		qpnp_lpg_save_period(chip);
+		pwm->state.period = period_ns;
+		chip->pwm_config.pwm_period = period_ns / NSEC_PER_USEC;
+	}
+
+	rc = _pwm_config(chip, LVL_NSEC, duty_ns, period_ns);
+
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to configure PWM mode\n");
+
+	return rc;
+}
+
+/**
+ * qpnp_pwm_enable - start a PWM output toggling
+ * @pwm_chip: the PWM chip
+ * @pwm: the PWM device
+ */
+static int qpnp_pwm_enable(struct pwm_chip *pwm_chip,
+		struct pwm_device *pwm)
+{
+	int rc;
+	struct qpnp_pwm_chip *chip = qpnp_pwm_from_pwm_chip(pwm_chip);
+
+	rc = _pwm_enable(chip);
+	if (rc)
+		pr_err("Failed to enable PWM channel: %d\n", chip->channel_id);
+
+	return rc;
+}
+
+/**
+ * qpnp_pwm_disable - stop a PWM output toggling
+ * @pwm_chip: the PWM chip
+ * @pwm: the PWM device
+ */
+static void qpnp_pwm_disable(struct pwm_chip *pwm_chip,
+		struct pwm_device *pwm)
+{
+
+	struct qpnp_pwm_chip	*chip = qpnp_pwm_from_pwm_chip(pwm_chip);
+	unsigned long		flags;
+	int rc = 0;
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	if (QPNP_IS_PWM_CONFIG_SELECTED(
+		chip->qpnp_lpg_registers[QPNP_ENABLE_CONTROL]) ||
+			chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED)
+		rc = qpnp_lpg_configure_pwm_state(chip,
+					QPNP_PWM_DISABLE);
+	else if (!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED))
+		rc = qpnp_lpg_configure_lut_state(chip,
+					QPNP_LUT_DISABLE);
+
+	if (!rc)
+		chip->enabled = false;
+
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to disable PWM channel: %d\n",
+					chip->channel_id);
+}
+
+static int _pwm_change_mode(struct qpnp_pwm_chip *chip, enum pm_pwm_mode mode)
+{
+	int rc;
+
+	if (mode)
+		rc = qpnp_configure_lpg_control(chip);
+	else
+		rc = qpnp_configure_pwm_control(chip);
+
+	if (rc)
+		pr_err("Failed to change the mode\n");
+	return rc;
+}
+
+/**
+ * pwm_change_mode - Change the PWM mode configuration
+ * @pwm: the PWM device
+ * @mode: Mode selection value
+ */
+int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
+{
+	int rc;
+	unsigned long flags;
+	struct qpnp_pwm_chip *chip;
+
+	if (pwm == NULL || IS_ERR(pwm) || pwm->chip == NULL) {
+		pr_err("Invalid pwm handle or no pwm_chip\n");
+		return -EINVAL;
+	}
+
+	if (mode < PM_PWM_MODE_PWM || mode > PM_PWM_MODE_LPG) {
+		pr_err("Invalid mode value\n");
+		return -EINVAL;
+	}
+
+	chip = qpnp_pwm_from_pwm_dev(pwm);
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+	rc = _pwm_change_mode(chip, mode);
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
+	return rc;
+}
+EXPORT_SYMBOL(pwm_change_mode);
+
+/**
+ * pwm_config_period - change PWM period
+ *
+ * @pwm: the PWM device
+ * @pwm_p: period in struct qpnp_lpg_period
+ */
+int pwm_config_period(struct pwm_device *pwm,
+			     struct pwm_period_config *period)
+{
+	struct _qpnp_pwm_config	*pwm_config;
+	struct qpnp_lpg_config	*lpg_config;
+	struct qpnp_pwm_chip	*chip;
+	unsigned long		flags;
+	int			rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm) || period == NULL)
+		return -EINVAL;
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	chip = qpnp_pwm_from_pwm_dev(pwm);
+	pwm_config = &chip->pwm_config;
+	lpg_config = &chip->lpg_config;
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	pwm_config->period.pwm_size = period->pwm_size;
+	pwm_config->period.clk = period->clk;
+	pwm_config->period.pre_div = period->pre_div;
+	pwm_config->period.pre_div_exp = period->pre_div_exp;
+
+	qpnp_lpg_save_period(chip);
+
+	rc = regmap_write(chip->regmap,
+			SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+			QPNP_LPG_PWM_SIZE_CLK),
+			*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_SIZE_CLK]);
+
+	if (rc) {
+		pr_err("Write failed: QPNP_LPG_PWM_SIZE_CLK register, rc: %d\n",
+									rc);
+		goto out_unlock;
+	}
+
+	rc = regmap_write(chip->regmap,
+		SPMI_LPG_REG_ADDR(lpg_config->base_addr,
+		QPNP_LPG_PWM_FREQ_PREDIV_CLK),
+		*&chip->qpnp_lpg_registers[QPNP_LPG_PWM_FREQ_PREDIV_CLK]);
+	if (rc) {
+		pr_err("Failed to write to QPNP_LPG_PWM_FREQ_PREDIV_CLK\n");
+		pr_err("register, rc = %d\n", rc);
+	}
+
+out_unlock:
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+	return rc;
+}
+EXPORT_SYMBOL(pwm_config_period);
+
+/**
+ * pwm_config_pwm_value - change a PWM device configuration
+ * @pwm: the PWM device
+ * @pwm_value: the duty cycle in raw PWM value (< 2^pwm_size)
+ */
+int pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
+{
+	struct qpnp_lpg_config	*lpg_config;
+	struct _qpnp_pwm_config	*pwm_config;
+	struct qpnp_pwm_chip	*chip;
+	unsigned long		flags;
+	int			rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm)) {
+		pr_err("Invalid parameter passed\n");
+		return -EINVAL;
+	}
+
+	if (pwm->chip == NULL) {
+		pr_err("Invalid device handle\n");
+		return -ENODEV;
+	}
+
+	chip = qpnp_pwm_from_pwm_dev(pwm);
+	lpg_config = &chip->lpg_config;
+	pwm_config = &chip->pwm_config;
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	if (pwm_config->pwm_value == pwm_value)
+		goto out_unlock;
+
+	pwm_config->pwm_value = pwm_value;
+
+	rc = qpnp_lpg_save_pwm_value(chip);
+
+	if (rc)
+		pr_err("Could not update PWM value for channel %d rc=%d\n",
+						chip->channel_id, rc);
+
+out_unlock:
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+	return rc;
+}
+EXPORT_SYMBOL(pwm_config_pwm_value);
+
+/**
+ * pwm_config_us - change a PWM device configuration
+ * @pwm: the PWM device
+ * @period_us: period in microseconds
+ * @duty_us: duty cycle in microseconds
+ */
+int pwm_config_us(struct pwm_device *pwm, int duty_us, int period_us)
+{
+	int rc;
+	unsigned long flags;
+	struct qpnp_pwm_chip *chip;
+
+	if (pwm == NULL || IS_ERR(pwm) ||
+		duty_us > period_us ||
+		(unsigned int)period_us > PM_PWM_PERIOD_MAX ||
+		(unsigned int)period_us < PM_PWM_PERIOD_MIN) {
+		pr_err("Invalid pwm handle or parameters\n");
+		return -EINVAL;
+	}
+
+	chip = qpnp_pwm_from_pwm_dev(pwm);
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	if (chip->pwm_config.pwm_period != period_us) {
+		qpnp_lpg_calc_period(LVL_USEC, period_us, chip);
+		qpnp_lpg_save_period(chip);
+		chip->pwm_config.pwm_period = period_us;
+		if ((unsigned int)period_us >
+		    (unsigned int)(-1) / NSEC_PER_USEC)
+			pwm->state.period = 0;
+		else
+			pwm->state.period
+				= (unsigned int)period_us * NSEC_PER_USEC;
+	}
+
+	rc = _pwm_config(chip, LVL_USEC, duty_us, period_us);
+
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to configure PWM mode\n");
+
+	return rc;
+}
+EXPORT_SYMBOL(pwm_config_us);
+
+/**
+ * pwm_lut_config - change LPG LUT device configuration
+ * @pwm: the PWM device
+ * @period_us: period in micro second
+ * @duty_pct: array of duty cycles in percent, like 20, 50.
+ * @lut_params: Lookup table parameters
+ */
+int pwm_lut_config(struct pwm_device *pwm, int period_us,
+		int duty_pct[], struct lut_params lut_params)
+{
+	unsigned long flags;
+	struct qpnp_pwm_chip *chip;
+	int rc = 0;
+
+	if (pwm == NULL || IS_ERR(pwm) || !lut_params.idx_len) {
+		pr_err("Invalid pwm handle or idx_len=0\n");
+		return -EINVAL;
+	}
+
+	if (pwm->chip == NULL)
+		return -ENODEV;
+
+	if (duty_pct == NULL && !(lut_params.flags & PM_PWM_LUT_NO_TABLE)) {
+		pr_err("Invalid duty_pct with flag\n");
+		return -EINVAL;
+	}
+
+	chip = qpnp_pwm_from_pwm_dev(pwm);
+
+	if (chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED) {
+		pr_err("LUT mode isn't supported\n");
+		return -EINVAL;
+	}
+
+	if ((lut_params.start_idx + lut_params.idx_len) >
+				chip->lpg_config.lut_size) {
+		pr_err("Exceed LUT limit\n");
+		return -EINVAL;
+	}
+
+	if ((unsigned int)period_us > PM_PWM_PERIOD_MAX ||
+	    (unsigned int)period_us < PM_PWM_PERIOD_MIN) {
+		pr_err("Period out of range\n");
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&chip->lpg_lock, flags);
+
+	if (chip->pwm_config.pwm_period != period_us) {
+		qpnp_lpg_calc_period(LVL_USEC, period_us, chip);
+		qpnp_lpg_save_period(chip);
+		chip->pwm_config.pwm_period = period_us;
+	}
+
+	rc = _pwm_lut_config(chip, period_us, duty_pct, lut_params);
+
+	spin_unlock_irqrestore(&chip->lpg_lock, flags);
+
+	if (rc)
+		pr_err("Failed to configure LUT\n");
+
+	return rc;
+}
+EXPORT_SYMBOL(pwm_lut_config);
+
+static int qpnp_parse_pwm_dt_config(struct device_node *of_pwm_node,
+		struct device_node *of_parent, struct qpnp_pwm_chip *chip)
+{
+	int rc, period;
+
+	rc = of_property_read_u32(of_parent, "qcom,period", (u32 *)&period);
+	if (rc) {
+		pr_err("node is missing PWM Period prop");
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_pwm_node, "qcom,duty",
+				&chip->pwm_config.pwm_duty);
+	if (rc) {
+		pr_err("node is missing PWM Duty prop");
+		return rc;
+	}
+
+	if (period < chip->pwm_config.pwm_duty || period > PM_PWM_PERIOD_MAX ||
+		period < PM_PWM_PERIOD_MIN) {
+		pr_err("Invalid pwm period(%d) or duty(%d)\n", period,
+			chip->pwm_config.pwm_duty);
+		return -EINVAL;
+	}
+
+	qpnp_lpg_calc_period(LVL_USEC, period, chip);
+	qpnp_lpg_save_period(chip);
+	chip->pwm_config.pwm_period = period;
+
+	rc = _pwm_config(chip, LVL_USEC, chip->pwm_config.pwm_duty, period);
+
+	return rc;
+}
+
+static int qpnp_parse_lpg_dt_config(struct device_node *of_lpg_node,
+		struct device_node *of_parent, struct qpnp_pwm_chip *chip)
+{
+	int rc, period, list_size, start_idx, *duty_pct_list;
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	struct qpnp_lut_config	*lut_config = &lpg_config->lut_config;
+	struct lut_params	lut_params;
+
+	rc = of_property_read_u32(of_parent, "qcom,period", &period);
+	if (rc) {
+		pr_err("node is missing PWM Period prop\n");
+		return rc;
+	}
+
+	if (!of_get_property(of_lpg_node, "qcom,duty-percents", &list_size)) {
+		pr_err("node is missing duty-pct list\n");
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_lpg_node, "cell-index", &start_idx);
+	if (rc) {
+		pr_err("Missing start index\n");
+		return rc;
+	}
+
+	list_size /= sizeof(u32);
+
+	if (list_size + start_idx > lpg_config->lut_size) {
+		pr_err("duty pct list size overflows\n");
+		return -EINVAL;
+	}
+
+	duty_pct_list = kcalloc(list_size, sizeof(*duty_pct_list), GFP_KERNEL);
+	if (!duty_pct_list)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_lpg_node, "qcom,duty-percents",
+						duty_pct_list, list_size);
+	if (rc) {
+		pr_err("invalid or missing property: qcom,duty-pcts-list\n");
+		goto out;
+	}
+
+	/* Read optional properties */
+	rc = of_property_read_u32(of_lpg_node, "qcom,ramp-step-duration",
+				  &lut_config->ramp_step_ms);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-pause-hi",
+				  &lut_config->lut_pause_hi_cnt);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-pause-lo",
+				  &lut_config->lut_pause_lo_cnt);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-ramp-direction",
+				  (u32 *)&lut_config->ramp_direction);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-pattern-repeat",
+				  (u32 *)&lut_config->pattern_repeat);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-ramp-toggle",
+				  (u32 *)&lut_config->ramp_toggle);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-enable-pause-hi",
+				  (u32 *)&lut_config->enable_pause_hi);
+	if (rc && rc != -EINVAL)
+		goto out;
+
+	rc = of_property_read_u32(of_lpg_node, "qcom,lpg-lut-enable-pause-lo",
+				  (u32 *)&lut_config->enable_pause_lo);
+	if (rc && rc != -EINVAL)
+		goto out;
+	rc = 0;
+
+	qpnp_set_lut_params(&lut_params, lut_config, start_idx, list_size);
+
+	_pwm_lut_config(chip, period, duty_pct_list, lut_params);
+
+out:
+	kfree(duty_pct_list);
+	return rc;
+}
+
+static int qpnp_lpg_get_rev_subtype(struct qpnp_pwm_chip *chip)
+{
+	int rc;
+	uint val;
+
+	rc = regmap_read(chip->regmap,
+			 chip->lpg_config.base_addr + SPMI_LPG_SUB_TYPE_OFFSET,
+			 &val);
+
+	if (rc) {
+		pr_err("Couldn't read subtype rc: %d\n", rc);
+		goto out;
+	}
+	 chip->sub_type = (u8)val;
+
+	rc = regmap_read(chip->regmap,
+			 chip->lpg_config.base_addr + SPMI_LPG_REVISION2_OFFSET,
+			 &val);
+
+	if (rc) {
+		pr_err("Couldn't read revision2 rc: %d\n", rc);
+		goto out;
+	}
+	chip->revision = (u8)val;
+
+	if (chip->revision < QPNP_LPG_REVISION_0 ||
+		chip->revision > QPNP_LPG_REVISION_1) {
+		pr_err("Unknown LPG revision detected, rev:%d\n",
+						chip->revision);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (chip->sub_type != QPNP_PWM_MODE_ONLY_SUB_TYPE
+		&& chip->sub_type != QPNP_LPG_CHAN_SUB_TYPE
+		&& chip->sub_type != QPNP_LPG_S_CHAN_SUB_TYPE) {
+		pr_err("Unknown LPG/PWM subtype detected, subtype:%d\n",
+						chip->sub_type);
+		rc = -EINVAL;
+	}
+out:
+	pr_debug("LPG rev 0x%02x subtype 0x%02x rc: %d\n", chip->revision,
+		chip->sub_type, rc);
+	return rc;
+}
+
+/* Fill in lpg device elements based on values found in device tree. */
+static int qpnp_parse_dt_config(struct platform_device *pdev,
+					struct qpnp_pwm_chip *chip)
+{
+	int			rc, enable, lut_entry_size, list_size, i;
+	const char		*label;
+	const __be32		*prop;
+	u32			size;
+	struct device_node	*node;
+	int found_pwm_subnode = 0;
+	int found_lpg_subnode = 0;
+	struct device_node	*of_node = pdev->dev.of_node;
+	struct qpnp_lpg_config	*lpg_config = &chip->lpg_config;
+	struct qpnp_lut_config	*lut_config = &lpg_config->lut_config;
+	struct _qpnp_pwm_config	*pwm_config = &chip->pwm_config;
+	int			force_pwm_size = 0;
+	int			pwm_size_list[QPNP_PWM_SIZES_SUPPORTED];
+
+	rc = of_property_read_u32(of_node, "qcom,channel-id",
+				&chip->channel_id);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: node is missing LPG channel id\n",
+								__func__);
+		return -EINVAL;
+	}
+
+	if (!of_get_property(of_node, "qcom,supported-sizes", &list_size)) {
+		pr_err("Missing qcom,supported-size list\n");
+		return -EINVAL;
+	}
+
+	list_size /= sizeof(u32);
+	if (list_size > QPNP_PWM_SIZES_SUPPORTED) {
+		pr_err(" qcom,supported-size list is too big\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32_array(of_node, "qcom,supported-sizes",
+			pwm_size_list, list_size);
+
+	if (rc) {
+		pr_err("Invalid qcom,supported-size property\n");
+		return rc;
+	}
+
+	for (i = 0; i < list_size; i++) {
+		pwm_config->supported_sizes |=
+			(1 << (pwm_size_list[i] - QPNP_MIN_PWM_BIT_SIZE));
+	}
+
+	if (!(pwm_config->supported_sizes == QPNP_PWM_SIZE_6_9_BIT ||
+		pwm_config->supported_sizes == QPNP_PWM_SIZE_7_8_BIT ||
+		pwm_config->supported_sizes == QPNP_PWM_SIZE_6_7_9_BIT)) {
+		pr_err("PWM sizes list qcom,supported-size is not proper\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * For cetrain LPG channels PWM size can be forced. So that
+	 * for every requested pwm period closest pwm frequency is
+	 * selected in qpnp_lpg_calc_period() for the forced pwm size.
+	 */
+	rc = of_property_read_u32(of_node, "qcom,force-pwm-size",
+				&force_pwm_size);
+	if (pwm_config->supported_sizes == QPNP_PWM_SIZE_7_8_BIT) {
+		if (!(force_pwm_size == QPNP_PWM_SIZE_7_BIT ||
+				force_pwm_size == QPNP_PWM_SIZE_8_BIT))
+			force_pwm_size = 0;
+	} else if (chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) {
+		if (!(force_pwm_size == QPNP_PWM_SIZE_6_BIT ||
+				force_pwm_size == QPNP_PWM_SIZE_9_BIT))
+			force_pwm_size = 0;
+	} else if (pwm_config->supported_sizes == QPNP_PWM_SIZE_6_7_9_BIT) {
+		if (!(force_pwm_size == QPNP_PWM_SIZE_6_BIT ||
+				force_pwm_size == QPNP_PWM_SIZE_7_BIT ||
+				force_pwm_size == QPNP_PWM_SIZE_9_BIT))
+			force_pwm_size = 0;
+	}
+
+	pwm_config->force_pwm_size = force_pwm_size;
+
+
+	prop = of_get_address_by_name(pdev->dev.of_node, QPNP_LPG_CHANNEL_BASE,
+			0, 0);
+	if (!prop) {
+		dev_err(&pdev->dev, "Couldnt find channel's base addr rc %d\n",
+				rc);
+		return rc;
+	}
+	lpg_config->base_addr = be32_to_cpu(*prop);
+
+	rc = qpnp_lpg_get_rev_subtype(chip);
+	if (rc)
+		return rc;
+
+	prop = of_get_address_by_name(pdev->dev.of_node, QPNP_LPG_LUT_BASE,
+			0, 0);
+	if (!prop) {
+		chip->flags |= QPNP_PWM_LUT_NOT_SUPPORTED;
+	} else {
+		lpg_config->lut_base_addr = be32_to_cpu(*prop);
+		rc = of_property_read_u32(of_node, "qcom,lpg-lut-size", &size);
+		if (rc < 0) {
+			dev_err(&pdev->dev, "Error reading qcom,lpg-lut-size, rc=%d\n",
+					rc);
+			return rc;
+		}
+
+		/*
+		 * Each entry of LUT is of 2 bytes for generic LUT and of 1 byte
+		 * for KPDBL/GLED LUT.
+		 */
+		lpg_config->lut_size = size >> 1;
+		lut_entry_size = sizeof(u16);
+
+		if (pwm_config->supported_sizes == QPNP_PWM_SIZE_7_8_BIT) {
+			lpg_config->lut_size = size;
+			lut_entry_size = sizeof(u8);
+		}
+
+		lut_config->duty_pct_list = kcalloc(lpg_config->lut_size,
+					lut_entry_size, GFP_KERNEL);
+		if (!lut_config->duty_pct_list)
+			return -ENOMEM;
+
+		rc = of_property_read_u32(of_node, "qcom,ramp-index",
+						&lut_config->ramp_index);
+		if (rc) {
+			pr_err("Missing LPG qcom,ramp-index property\n");
+			kfree(lut_config->duty_pct_list);
+			return rc;
+		}
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,dtest-line",
+		&chip->dtest_line);
+	if (rc) {
+		chip->in_test_mode = 0;
+	} else {
+		rc = of_property_read_u32(of_node, "qcom,dtest-output",
+			&chip->dtest_output);
+		if (rc) {
+			pr_err("Missing DTEST output configuration\n");
+			return rc;
+		}
+		chip->in_test_mode = 1;
+	}
+
+	if (chip->in_test_mode) {
+		if ((chip->sub_type == QPNP_PWM_MODE_ONLY_SUB_TYPE) &&
+			(chip->dtest_line > QPNP_PWM_DTEST_LINE_MAX ||
+			chip->dtest_output > QPNP_PWM_DTEST_OUTPUT_MAX)) {
+			pr_err("DTEST line/output values are improper for PWM channel %d\n",
+				chip->channel_id);
+			return -EINVAL;
+		} else if (chip->dtest_line > QPNP_LPG_DTEST_LINE_MAX ||
+			chip->dtest_output > QPNP_LPG_DTEST_OUTPUT_MAX) {
+			pr_err("DTEST line/output values are improper for LPG channel %d\n",
+				chip->channel_id);
+			return -EINVAL;
+		}
+	}
+
+	for_each_child_of_node(of_node, node) {
+		rc = of_property_read_string(node, "label", &label);
+		if (rc) {
+			dev_err(&pdev->dev, "%s: Missing label property\n",
+								__func__);
+			goto out;
+		}
+		if (!strcmp(label, "pwm")) {
+			rc = qpnp_parse_pwm_dt_config(node, of_node, chip);
+			if (rc)
+				goto out;
+			found_pwm_subnode = 1;
+		} else if (!strcmp(label, "lpg") &&
+				!(chip->flags & QPNP_PWM_LUT_NOT_SUPPORTED)) {
+			rc = qpnp_parse_lpg_dt_config(node, of_node, chip);
+			if (rc)
+				goto out;
+			found_lpg_subnode = 1;
+		} else {
+			dev_err(&pdev->dev,
+				"%s: Invalid value for lable prop",
+								__func__);
+		}
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,mode-select", &enable);
+	if (rc)
+		goto read_opt_props;
+
+	if ((enable == PM_PWM_MODE_PWM && found_pwm_subnode == 0) ||
+		(enable == PM_PWM_MODE_LPG && found_lpg_subnode == 0)) {
+		dev_err(&pdev->dev, "%s: Invalid mode select\n", __func__);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	_pwm_change_mode(chip, enable);
+	_pwm_enable(chip);
+
+read_opt_props:
+	/* Initialize optional config parameters from DT if provided */
+	of_property_read_string(node, "qcom,channel-owner",
+				&chip->channel_owner);
+
+	return 0;
+
+out:
+	kfree(lut_config->duty_pct_list);
+	return rc;
+}
+
+static struct pwm_ops qpnp_pwm_ops = {
+	.enable = qpnp_pwm_enable,
+	.disable = qpnp_pwm_disable,
+	.config = qpnp_pwm_config,
+	.free = qpnp_pwm_free,
+	.owner = THIS_MODULE,
+};
+
+static int qpnp_pwm_probe(struct platform_device *pdev)
+{
+	struct qpnp_pwm_chip	*pwm_chip;
+	int			rc;
+
+	pwm_chip = kzalloc(sizeof(*pwm_chip), GFP_KERNEL);
+	if (pwm_chip == NULL)
+		return -ENOMEM;
+
+	pwm_chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pwm_chip->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	spin_lock_init(&pwm_chip->lpg_lock);
+
+	pwm_chip->pdev = pdev;
+	dev_set_drvdata(&pdev->dev, pwm_chip);
+
+	rc = qpnp_parse_dt_config(pdev, pwm_chip);
+
+	if (rc) {
+		pr_err("Failed parsing DT parameters, rc=%d\n", rc);
+		goto failed_config;
+	}
+
+	pwm_chip->chip.dev = &pdev->dev;
+	pwm_chip->chip.ops = &qpnp_pwm_ops;
+	pwm_chip->chip.base = -1;
+	pwm_chip->chip.npwm = 1;
+
+	rc = pwmchip_add(&pwm_chip->chip);
+	if (rc < 0) {
+		pr_err("pwmchip_add() failed: %d\n", rc);
+		goto failed_insert;
+	}
+
+	if (pwm_chip->channel_owner)
+		pwm_chip->chip.pwms[0].label = pwm_chip->channel_owner;
+
+	pr_debug("PWM device channel:%d probed successfully\n",
+		pwm_chip->channel_id);
+	return 0;
+
+failed_insert:
+	kfree(pwm_chip->lpg_config.lut_config.duty_pct_list);
+failed_config:
+	dev_set_drvdata(&pdev->dev, NULL);
+	kfree(pwm_chip);
+	return rc;
+}
+
+static int qpnp_pwm_remove(struct platform_device *pdev)
+{
+	struct qpnp_pwm_chip *pwm_chip;
+	struct qpnp_lpg_config *lpg_config;
+
+	pwm_chip = dev_get_drvdata(&pdev->dev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (pwm_chip) {
+		lpg_config = &pwm_chip->lpg_config;
+		pwmchip_remove(&pwm_chip->chip);
+		kfree(lpg_config->lut_config.duty_pct_list);
+		kfree(pwm_chip);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = QPNP_LPG_DRIVER_NAME, },
+	{}
+};
+
+static const struct platform_device_id qpnp_lpg_id[] = {
+	{ QPNP_LPG_DRIVER_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_lpg_id);
+
+static struct platform_driver qpnp_lpg_driver = {
+	.driver		= {
+		.name		= QPNP_LPG_DRIVER_NAME,
+		.of_match_table	= spmi_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= qpnp_pwm_probe,
+	.remove		= qpnp_pwm_remove,
+	.id_table	= qpnp_lpg_id,
+};
+
+/**
+ * qpnp_lpg_init() - register spmi driver for qpnp-lpg
+ */
+int __init qpnp_lpg_init(void)
+{
+	return platform_driver_register(&qpnp_lpg_driver);
+}
+
+static void __exit qpnp_lpg_exit(void)
+{
+	platform_driver_unregister(&qpnp_lpg_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC LPG driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" QPNP_LPG_DRIVER_NAME);
+
+subsys_initcall(qpnp_lpg_init);
+module_exit(qpnp_lpg_exit);
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f4ce22d..534a1d7 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -888,6 +888,145 @@
 	  This driver provides support for the voltage regulators on the
 	  WM8994 CODEC.
 
+config REGULATOR_CPR3
+	bool "CPR3 regulator core support"
+	help
+	  This driver supports Core Power Reduction (CPR) version 3 controllers
+	  which are used by some Qualcomm Technologies, Inc. (QTI) SoCs to
+	  manage important voltage regulators.  CPR3 controllers are capable of
+	  monitoring several ring oscillator sensing loops simultaneously.  The
+	  CPR3 controller informs software when the silicon conditions require
+	  the supply voltage to be increased or decreased.  On certain supply
+	  rails, the CPR3 controller is able to propagate the voltage increase
+	  or decrease requests all the way to the PMIC without software
+	  involvement.
+
+config REGULATOR_CPR3_HMSS
+	bool "CPR3 regulator for HMSS"
+	depends on OF
+	select REGULATOR_CPR3
+	help
+	  This driver supports Qualcomm Technologies, Inc. HMSS application
+	  processor specific features including memory array power mux (APM)
+	  switching, two CPR3 threads which monitor the two HMSS clusters that
+	  are both powered by a shared supply, and hardware closed-loop auto
+	  voltage stepping.  This driver reads both initial voltage and CPR
+	  target quotient values out of hardware fuses.
+
+config REGULATOR_CPR3_MMSS
+	bool "RBCPR3 regulator for MMSS"
+	depends on OF
+	select REGULATOR_CPR3
+	help
+	  This driver supports Qualcomm Technologies, Inc. MMSS graphics
+	  processor specific features.  The MMSS CPR3 controller only uses one
+	  thread to monitor the MMSS voltage requirements.  This driver reads
+	  initial voltage values out of hardware fuses and CPR target quotient
+	  values out of device tree.
+
+config REGULATOR_CPR4_APSS
+	bool "CPR4 regulator for APSS"
+	depends on OF
+	select REGULATOR_CPR3
+	help
+	  This driver supports Qualcomm Technologies, Inc. APSS application
+	  processor specific features including memory array power mux (APM)
+	  switching, one CPR4 thread which monitor the two APSS clusters that
+	  are both powered by a shared supply, hardware closed-loop auto
+	  voltage stepping, voltage adjustments based on online core count,
+	  voltage adjustments based on temperature readings, and voltage
+	  adjustments for performance boost mode. This driver reads both initial
+	  voltage and CPR target quotient values out of hardware fuses.
+
+config REGULATOR_CPR4_MMSS_LDO
+	bool "RBCPR3 regulator for MMSS LDO"
+	depends on OF
+	select REGULATOR_CPR3
+	help
+	  This driver supports Qualcomm Technologies, Inc. MMSS graphics
+	  processor specific features.  The MMSS CPR3 controller only uses one
+	  thread to monitor the MMSS LDO voltage requirements. This driver reads
+	  initial voltage values out of hardware fuses and CPR target quotient
+	  values out of device tree.
+
+config REGULATOR_CPRH_KBSS
+	bool "CPRH regulator for KBSS"
+	depends on OF
+	select REGULATOR_CPR3
+	help
+	  This driver supports Qualcomm Technologies, Inc. KBSS application
+	  processor specific features including CPR hardening (CPRh) and two
+	  CPRh controllers which monitor the two KBSS clusters each powered by
+	  independent voltage supplies. This driver reads both initial voltage
+	  and CPR target quotient values out of hardware fuses.
+
+config REGULATOR_KRYO
+	bool "Kryo regulator driver"
+	depends on OF
+	help
+	  Some MSM designs have CPUs that can be directly powered from a common
+	  voltage rail via a Block Head Switch (BHS) or an LDO whose output
+	  voltage can be configured for use when certain power constraints are
+	  met.  Say yes to support management of LDO and BHS modes for the
+	  clusters in the CPU subsystem.
+
+config REGULATOR_MEM_ACC
+	tristate "QTI Memory accelerator regulator driver"
+	help
+	  Say y here to enable the memory accelerator driver for Qualcomm
+	  Technologies, Inc. (QTI) chips. The accelerator controls delays
+	  applied for memory accesses.  This driver configures the power-mode
+	  (corner) for the memory accelerator.
+
+config REGULATOR_MSM_GFX_LDO
+	tristate "MSM GFX LDO Regulator"
+	depends on OF
+	help
+	  This driver supports the MSM GFX (Graphics) LDO regulator. The
+	  GFU core is either powered by an internal MSM LDO or by BHS.
+	  Typically the lower voltage corners are powered by LDO and
+	  the higher ones by BHS. This driver allows for configuration of
+	  the rail between the LDO/BHS as well as the LDO voltage.
+
+config REGULATOR_QPNP_LABIBB
+	tristate "Qualcomm Technologies, Inc. QPNP LAB/IBB regulator support"
+	depends on SPMI
+	help
+	  This driver supports voltage regulators in Qualcomm Technologies, Inc.
+	  PMIC chips which comply with QPNP LAB/IBB regulators. QPNP LAB and IBB
+	  are SPMI based PMIC implementations. LAB regulator can be used as a
+	  regular positive boost regulator. IBB can be used as a regular
+	  negative boost regulator. LAB/IBB regulators can also be used
+	  together for LCD or AMOLED.
+
+config REGULATOR_QPNP_LCDB
+	tristate "Qualcomm Technologies, Inc. QPNP LCDB support"
+	depends on SPMI
+	help
+	  Supports the LCDB module in the Qualcomm Technologies, Inc.
+	  QPNP PMICs. Exposes regulators to control the positive and
+	  negative voltage bias for the LCD display panel. It also
+	  allows configurability for the various bias-voltage parameters.
+
+config REGULATOR_QPNP_OLEDB
+	tristate "Qualcomm Technologies, Inc. QPNP OLEDB regulator support"
+	depends on SPMI
+	help
+	  This driver supports the OLEDB (AVDD bias) signal for AMOLED panel in
+	  Qualcomm Technologies, Inc. QPNP PMICs. It exposes the OLED voltage
+	  configuration via the regulator framework. The configurable range of
+	  this bias is 5 V to 8.1 V.
+
+config REGULATOR_QPNP
+	tristate "Qualcomm Technologies, Inc. QPNP regulator support"
+	depends on SPMI
+	help
+	  This driver supports voltage regulators in Qualcomm Technologies, Inc.
+	  PMIC chips which comply with QPNP.  QPNP is a SPMI based PMIC
+	  implementation.  These chips provide several different varieties of
+	  LDO and switching regulators.  They also provide voltage switches and
+	  boost regulators.
+
 config REGULATOR_RPMH
 	tristate "Qualcomm Technologies, Inc. RPMh regulator driver"
 	depends on OF
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2e466b1..bf30b8d 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -114,6 +114,19 @@
 obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
 obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
 
+obj-$(CONFIG_REGULATOR_CPR3) += cpr3-regulator.o cpr3-util.o
+obj-$(CONFIG_REGULATOR_CPR3_HMSS) += cpr3-hmss-regulator.o
+obj-$(CONFIG_REGULATOR_CPR3_MMSS) += cpr3-mmss-regulator.o
+obj-$(CONFIG_REGULATOR_CPR4_APSS) += cpr4-apss-regulator.o
+obj-$(CONFIG_REGULATOR_CPR4_MMSS_LDO) += cpr4-mmss-ldo-regulator.o
+obj-$(CONFIG_REGULATOR_CPRH_KBSS) += cprh-kbss-regulator.o
+obj-$(CONFIG_REGULATOR_KRYO) += kryo-regulator.o
+obj-$(CONFIG_REGULATOR_MEM_ACC) += mem-acc-regulator.o
+obj-$(CONFIG_REGULATOR_MSM_GFX_LDO) += msm_gfx_ldo.o
+obj-$(CONFIG_REGULATOR_QPNP_LABIBB) += qpnp-labibb-regulator.o
+obj-$(CONFIG_REGULATOR_QPNP_LCDB) += qpnp-lcdb-regulator.o
+obj-$(CONFIG_REGULATOR_QPNP_OLEDB) += qpnp-oledb-regulator.o
+obj-$(CONFIG_REGULATOR_QPNP) += qpnp-regulator.o
 obj-$(CONFIG_REGULATOR_RPMH) += rpmh-regulator.o
 obj-$(CONFIG_REGULATOR_STUB) += stub-regulator.o
 
diff --git a/drivers/regulator/cpr3-hmss-regulator.c b/drivers/regulator/cpr3-hmss-regulator.c
new file mode 100644
index 0000000..77e8bf4b
--- /dev/null
+++ b/drivers/regulator/cpr3-hmss-regulator.c
@@ -0,0 +1,1829 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/msm-ldo-regulator.h>
+
+#include "cpr3-regulator.h"
+
+#define MSM8996_HMSS_FUSE_CORNERS	5
+
+/**
+ * struct cpr3_msm8996_hmss_fuses - HMSS specific fuse data for MSM8996
+ * @ro_sel:		Ring oscillator select fuse parameter value for each
+ *			fuse corner
+ * @init_voltage:	Initial (i.e. open-loop) voltage fuse parameter value
+ *			for each fuse corner (raw, not converted to a voltage)
+ * @target_quot:	CPR target quotient fuse parameter value for each fuse
+ *			corner
+ * @quot_offset:	CPR target quotient offset fuse parameter value for each
+ *			fuse corner (raw, not unpacked) used for target quotient
+ *			interpolation
+ * @speed_bin:		Application processor speed bin fuse parameter value for
+ *			the given chip
+ * @cbf_voltage_offset:	Voltage margin offset for the CBF regulator used on
+ *			MSM8996-Pro chips.
+ * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
+ * @redundant_fusing:	Redundant fusing select fuse parameter value
+ * @limitation:		CPR limitation select fuse parameter value
+ * @partial_binning:	Chip partial binning fuse parameter value which defines
+ *			limitations found on a given chip
+ * @vdd_mx_ret_fuse:	Defines the logic retention voltage of VDD_MX
+ * @vdd_apcc_ret_fuse:	Defines the logic retention voltage of VDD_APCC
+ * @aging_init_quot_diff:	Initial quotient difference between CPR aging
+ *			min and max sensors measured at time of manufacturing
+ *
+ * This struct holds the values for all of the fuses read from memory.  The
+ * values for ro_sel, init_voltage, target_quot, and quot_offset come from
+ * either the primary or redundant fuse locations depending upon the value of
+ * redundant_fusing.
+ */
+struct cpr3_msm8996_hmss_fuses {
+	u64	ro_sel[MSM8996_HMSS_FUSE_CORNERS];
+	u64	init_voltage[MSM8996_HMSS_FUSE_CORNERS];
+	u64	target_quot[MSM8996_HMSS_FUSE_CORNERS];
+	u64	quot_offset[MSM8996_HMSS_FUSE_CORNERS];
+	u64	cbf_voltage_offset[MSM8996_HMSS_FUSE_CORNERS];
+	u64	speed_bin;
+	u64	cpr_fusing_rev;
+	u64	redundant_fusing;
+	u64	limitation;
+	u64	partial_binning;
+	u64	vdd_mx_ret_fuse;
+	u64	vdd_apcc_ret_fuse;
+	u64	aging_init_quot_diff;
+};
+
+/*
+ * Fuse combos 0 -  7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
+ * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ */
+#define CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT	16
+
+/*
+ * Constants which define the name of each fuse corner.  Note that no actual
+ * fuses are defined for LowSVS.  However, a mapping from corner to LowSVS
+ * is required in order to perform target quotient interpolation properly.
+ */
+enum cpr3_msm8996_hmss_fuse_corner {
+	CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS	= 0,
+	CPR3_MSM8996_HMSS_FUSE_CORNER_LOWSVS	= 1,
+	CPR3_MSM8996_HMSS_FUSE_CORNER_SVS	= 2,
+	CPR3_MSM8996_HMSS_FUSE_CORNER_NOM	= 3,
+	CPR3_MSM8996_HMSS_FUSE_CORNER_TURBO	= 4,
+};
+
+static const char * const cpr3_msm8996_hmss_fuse_corner_name[] = {
+	[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS]	= "MinSVS",
+	[CPR3_MSM8996_HMSS_FUSE_CORNER_LOWSVS]	= "LowSVS",
+	[CPR3_MSM8996_HMSS_FUSE_CORNER_SVS]	= "SVS",
+	[CPR3_MSM8996_HMSS_FUSE_CORNER_NOM]	= "NOM",
+	[CPR3_MSM8996_HMSS_FUSE_CORNER_TURBO]	= "TURBO",
+};
+
+/* CPR3 hardware thread IDs */
+#define MSM8996_HMSS_POWER_CLUSTER_THREAD_ID		0
+#define MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID	1
+
+/*
+ * MSM8996 HMSS fuse parameter locations:
+ *
+ * Structs are organized with the following dimensions:
+ *	Outer:  0 or 1 for power or performance cluster
+ *	Middle: 0 to 3 for fuse corners from lowest to highest corner
+ *	Inner:  large enough to hold the longest set of parameter segments which
+ *		fully defines a fuse parameter, +1 (for NULL termination).
+ *		Each segment corresponds to a contiguous group of bits from a
+ *		single fuse row.  These segments are concatentated together in
+ *		order to form the full fuse parameter value.  The segments for
+ *		a given parameter may correspond to different fuse rows.
+ *
+ * Note that there are only physically 4 sets of fuse parameters which
+ * correspond to the MinSVS, SVS, NOM, and TURBO fuse corners.  However, the SVS
+ * quotient offset fuse is used to define the target quotient for the LowSVS
+ * fuse corner.  In order to utilize LowSVS, it must be treated as if it were a
+ * real fully defined fuse corner.  Thus, LowSVS fuse parameter locations are
+ * specified.  These locations duplicate the SVS values in order to simplify
+ * interpolation logic.
+ */
+static const struct cpr3_fuse_param
+msm8996_hmss_ro_sel_param[2][MSM8996_HMSS_FUSE_CORNERS][2] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{66, 38, 41}, {} },
+		{{66, 38, 41}, {} },
+		{{66, 38, 41}, {} },
+		{{66, 34, 37}, {} },
+		{{66, 30, 33}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{64, 54, 57}, {} },
+		{{64, 54, 57}, {} },
+		{{64, 54, 57}, {} },
+		{{64, 50, 53}, {} },
+		{{64, 46, 49}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_init_voltage_param[2][MSM8996_HMSS_FUSE_CORNERS][3] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{67,  0,  5}, {} },
+		{{66, 58, 63}, {} },
+		{{66, 58, 63}, {} },
+		{{66, 52, 57}, {} },
+		{{66, 46, 51}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{65, 16, 21}, {} },
+		{{65, 10, 15}, {} },
+		{{65, 10, 15}, {} },
+		{{65,  4,  9}, {} },
+		{{64, 62, 63}, {65,  0,  3}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_target_quot_param[2][MSM8996_HMSS_FUSE_CORNERS][3] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{67, 42, 53}, {} },
+		{{67, 30, 41}, {} },
+		{{67, 30, 41}, {} },
+		{{67, 18, 29}, {} },
+		{{67,  6, 17}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{65, 58, 63}, {66,  0,  5}, {} },
+		{{65, 46, 57}, {} },
+		{{65, 46, 57}, {} },
+		{{65, 34, 45}, {} },
+		{{65, 22, 33}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_quot_offset_param[2][MSM8996_HMSS_FUSE_CORNERS][3] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{} },
+		{{} },
+		{{68,  6, 13}, {} },
+		{{67, 62, 63}, {68, 0, 5}, {} },
+		{{67, 54, 61}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{} },
+		{{} },
+		{{66, 22, 29}, {} },
+		{{66, 14, 21}, {} },
+		{{66,  6, 13}, {} },
+	},
+};
+
+/*
+ * This fuse is used to define if the redundant set of fuses should be used for
+ * any particular feature.  CPR is one such feature.  The redundant CPR fuses
+ * should be used if this fuse parameter has a value of 1.
+ */
+static const struct cpr3_fuse_param msm8996_redundant_fusing_param[] = {
+	{73, 61, 63},
+	{},
+};
+#define MSM8996_CPR_REDUNDANT_FUSING 1
+
+static const struct cpr3_fuse_param
+msm8996_hmss_redun_ro_sel_param[2][MSM8996_HMSS_FUSE_CORNERS][2] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{76, 36, 39}, {} },
+		{{76, 32, 35}, {} },
+		{{76, 32, 35}, {} },
+		{{76, 28, 31}, {} },
+		{{76, 24, 27}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{74, 52, 55}, {} },
+		{{74, 48, 51}, {} },
+		{{74, 48, 51}, {} },
+		{{74, 44, 47}, {} },
+		{{74, 40, 43}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_redun_init_voltage_param[2][MSM8996_HMSS_FUSE_CORNERS][3] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{76, 58, 63}, {} },
+		{{76, 52, 57}, {} },
+		{{76, 52, 57}, {} },
+		{{76, 46, 51}, {} },
+		{{76, 40, 45}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{75, 10, 15}, {} },
+		{{75,  4,  9}, {} },
+		{{75,  4,  9}, {} },
+		{{74, 62, 63}, {75,  0,  3}, {} },
+		{{74, 56, 61}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_redun_target_quot_param[2][MSM8996_HMSS_FUSE_CORNERS][2] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{77, 36, 47}, {} },
+		{{77, 24, 35}, {} },
+		{{77, 24, 35}, {} },
+		{{77, 12, 23}, {} },
+		{{77,  0, 11}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{75, 52, 63}, {} },
+		{{75, 40, 51}, {} },
+		{{75, 40, 51}, {} },
+		{{75, 28, 39}, {} },
+		{{75, 16, 27}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_redun_quot_offset_param[2][MSM8996_HMSS_FUSE_CORNERS][2] = {
+	[MSM8996_HMSS_POWER_CLUSTER_THREAD_ID] = {
+		{{} },
+		{{} },
+		{{68, 11, 18}, {} },
+		{{77, 56, 63}, {} },
+		{{77, 48, 55}, {} },
+	},
+	[MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID] = {
+		{{} },
+		{{} },
+		{{76, 16, 23}, {} },
+		{{76,  8, 15}, {} },
+		{{76,  0,  7}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param msm8996_cpr_fusing_rev_param[] = {
+	{39, 51, 53},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8996_hmss_speed_bin_param[] = {
+	{38, 29, 31},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8996_cpr_limitation_param[] = {
+	{41, 31, 32},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8996_vdd_mx_ret_param[] = {
+	{41, 2, 4},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8996_vdd_apcc_ret_param[] = {
+	{41, 52, 54},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8996_cpr_partial_binning_param[] = {
+	{39, 55, 59},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8996_hmss_aging_init_quot_diff_param[] = {
+	{68, 14, 19},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8996pro_hmss_voltage_offset_param[MSM8996_HMSS_FUSE_CORNERS][4] = {
+	{{68, 50, 52}, {41, 63, 63}, {} },
+	{{62, 30, 31}, {62, 63, 63}, {66, 45, 45}, {} },
+	{{61, 35, 36}, {61, 62, 63}, {} },
+	{{61, 26, 26}, {61, 32, 34}, {} },
+	{{61, 22, 25}, {} },
+};
+
+#define MSM8996PRO_SOC_ID			4
+
+/*
+ * Some initial msm8996 parts cannot be used in a meaningful way by software.
+ * Other parts can only be used when operating with CPR disabled (i.e. at the
+ * fused open-loop voltage) when no voltage interpolation is applied.  A fuse
+ * parameter is provided so that software can properly handle these limitations.
+ */
+enum msm8996_cpr_limitation {
+	MSM8996_CPR_LIMITATION_NONE = 0,
+	MSM8996_CPR_LIMITATION_UNSUPPORTED = 2,
+	MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION = 3,
+};
+
+/*
+ * Some initial msm8996 parts cannot be operated at low voltages.  A fuse
+ * parameter is provided so that software can properly handle these limitations.
+ */
+enum msm8996_cpr_partial_binning {
+	MSM8996_CPR_PARTIAL_BINNING_SVS = 11,
+	MSM8996_CPR_PARTIAL_BINNING_NOM = 12,
+};
+
+/* Additional MSM8996 specific data: */
+
+/* Open loop voltage fuse reference voltages in microvolts for MSM8996 v1/v2 */
+static const int msm8996_v1_v2_hmss_fuse_ref_volt[MSM8996_HMSS_FUSE_CORNERS] = {
+	605000,
+	745000, /* Place holder entry for LowSVS */
+	745000,
+	905000,
+	1015000,
+};
+
+/* Open loop voltage fuse reference voltages in microvolts for MSM8996 v3 */
+static const int msm8996_v3_hmss_fuse_ref_volt[MSM8996_HMSS_FUSE_CORNERS] = {
+	605000,
+	745000, /* Place holder entry for LowSVS */
+	745000,
+	905000,
+	1140000,
+};
+
+/*
+ * Open loop voltage fuse reference voltages in microvolts for MSM8996 v3 with
+ * speed_bin == 1 and cpr_fusing_rev >= 5.
+ */
+static const int msm8996_v3_speed_bin1_rev5_hmss_fuse_ref_volt[
+						MSM8996_HMSS_FUSE_CORNERS] = {
+	605000,
+	745000, /* Place holder entry for LowSVS */
+	745000,
+	905000,
+	1040000,
+};
+
+/* Defines mapping from retention fuse values to voltages in microvolts */
+static const int msm8996_vdd_apcc_fuse_ret_volt[] = {
+	600000, 550000, 500000, 450000, 400000, 350000, 300000, 600000,
+};
+
+static const int msm8996_vdd_mx_fuse_ret_volt[] = {
+	700000, 650000, 580000, 550000, 490000, 490000, 490000, 490000,
+};
+
+#define MSM8996_HMSS_FUSE_STEP_VOLT		10000
+#define MSM8996_HMSS_VOLTAGE_FUSE_SIZE		6
+#define MSM8996PRO_HMSS_CBF_FUSE_STEP_VOLT	10000
+#define MSM8996PRO_HMSS_CBF_VOLTAGE_FUSE_SIZE	4
+#define MSM8996_HMSS_QUOT_OFFSET_SCALE		5
+#define MSM8996_HMSS_AGING_INIT_QUOT_DIFF_SCALE	2
+#define MSM8996_HMSS_AGING_INIT_QUOT_DIFF_SIZE	6
+
+#define MSM8996_HMSS_CPR_SENSOR_COUNT		25
+#define MSM8996_HMSS_THREAD0_SENSOR_MIN		0
+#define MSM8996_HMSS_THREAD0_SENSOR_MAX		14
+#define MSM8996_HMSS_THREAD1_SENSOR_MIN		15
+#define MSM8996_HMSS_THREAD1_SENSOR_MAX		24
+
+#define MSM8996_HMSS_CPR_CLOCK_RATE		19200000
+
+#define MSM8996_HMSS_AGING_SENSOR_ID		11
+#define MSM8996_HMSS_AGING_BYPASS_MASK0		(GENMASK(7, 0) & ~BIT(3))
+
+/**
+ * cpr3_msm8996_hmss_use_voltage_offset_fuse() - return if this part utilizes
+ *		voltage offset fuses or not
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: true if this part utilizes voltage offset fuses, else false
+ */
+static inline bool cpr3_msm8996_hmss_use_voltage_offset_fuse(
+					struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+
+	return vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID
+	       && fuse->cpr_fusing_rev >= 1
+	       && of_property_read_bool(vreg->of_node, "qcom,is-cbf-regulator");
+}
+
+/**
+ * cpr3_msm8996_hmss_read_fuse_data() - load HMSS specific fuse parameter values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function allocates a cpr3_msm8996_hmss_fuses struct, fills it with
+ * values read out of hardware fuses, and finally copies common fuse values
+ * into the CPR3 regulator struct.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_hmss_read_fuse_data(struct cpr3_regulator *vreg)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	struct cpr3_msm8996_hmss_fuses *fuse;
+	bool redundant;
+	int i, id, rc;
+
+	fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL);
+	if (!fuse)
+		return -ENOMEM;
+
+	rc = cpr3_read_fuse_param(base, msm8996_hmss_speed_bin_param,
+				&fuse->speed_bin);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc);
+		return rc;
+	}
+	cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin);
+
+	rc = cpr3_read_fuse_param(base, msm8996_cpr_fusing_rev_param,
+				&fuse->cpr_fusing_rev);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
+
+	rc = cpr3_read_fuse_param(base, msm8996_redundant_fusing_param,
+				&fuse->redundant_fusing);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read redundant fusing config fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	redundant = (fuse->redundant_fusing == MSM8996_CPR_REDUNDANT_FUSING);
+	cpr3_info(vreg, "using redundant fuses = %c\n",
+		redundant ? 'Y' : 'N');
+
+	rc = cpr3_read_fuse_param(base, msm8996_cpr_limitation_param,
+				&fuse->limitation);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR limitation fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR limitation = %s\n",
+		fuse->limitation == MSM8996_CPR_LIMITATION_UNSUPPORTED
+		? "unsupported chip" : fuse->limitation
+			  == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION
+		? "CPR disabled and no interpolation" : "none");
+
+	rc = cpr3_read_fuse_param(base, msm8996_cpr_partial_binning_param,
+				&fuse->partial_binning);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read partial binning fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR partial binning limitation = %s\n",
+		fuse->partial_binning == MSM8996_CPR_PARTIAL_BINNING_SVS
+			? "SVS min voltage"
+		: fuse->partial_binning == MSM8996_CPR_PARTIAL_BINNING_NOM
+			? "NOM min voltage"
+		: "none");
+
+	rc = cpr3_read_fuse_param(base, msm8996_vdd_mx_ret_param,
+				&fuse->vdd_mx_ret_fuse);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read VDD_MX retention fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_read_fuse_param(base, msm8996_vdd_apcc_ret_param,
+				&fuse->vdd_apcc_ret_fuse);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read VDD_APCC retention fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_info(vreg, "Retention voltage fuses: VDD_MX = %llu, VDD_APCC = %llu\n",
+		  fuse->vdd_mx_ret_fuse, fuse->vdd_apcc_ret_fuse);
+
+	rc = cpr3_read_fuse_param(base, msm8996_hmss_aging_init_quot_diff_param,
+				&fuse->aging_init_quot_diff);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	id = vreg->thread->thread_id;
+
+	for (i = 0; i < MSM8996_HMSS_FUSE_CORNERS; i++) {
+		rc = cpr3_read_fuse_param(base,
+			redundant
+			    ? msm8996_hmss_redun_init_voltage_param[id][i]
+			    : msm8996_hmss_init_voltage_param[id][i],
+			&fuse->init_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+			redundant
+			    ? msm8996_hmss_redun_target_quot_param[id][i]
+			    : msm8996_hmss_target_quot_param[id][i],
+			&fuse->target_quot[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+			redundant
+			    ? msm8996_hmss_redun_ro_sel_param[id][i]
+			    : msm8996_hmss_ro_sel_param[id][i],
+			&fuse->ro_sel[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+			redundant
+			    ? msm8996_hmss_redun_quot_offset_param[id][i]
+			    : msm8996_hmss_quot_offset_param[id][i],
+			&fuse->quot_offset[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
+	if (vreg->fuse_combo >= CPR3_MSM8996_HMSS_FUSE_COMBO_COUNT) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
+			vreg->fuse_combo);
+		return -EINVAL;
+	}
+
+	vreg->speed_bin_fuse	= fuse->speed_bin;
+	vreg->cpr_rev_fuse	= fuse->cpr_fusing_rev;
+	vreg->fuse_corner_count	= MSM8996_HMSS_FUSE_CORNERS;
+	vreg->platform_fuses	= fuse;
+
+	if (cpr3_msm8996_hmss_use_voltage_offset_fuse(vreg)) {
+		for (i = 0; i < MSM8996_HMSS_FUSE_CORNERS; i++) {
+			rc = cpr3_read_fuse_param(base,
+				msm8996pro_hmss_voltage_offset_param[i],
+				&fuse->cbf_voltage_offset[i]);
+			if (rc) {
+				cpr3_err(vreg, "Unable to read fuse-corner %d CBF voltage offset fuse, rc=%d\n",
+					i, rc);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_apply_fused_voltage_offset() - adjust the fused voltages for each
+ *		fuse corner according to voltage offset fuse values
+ * @vreg:		Pointer to the CPR3 regulator
+ * @fuse_volt:		Pointer to an array of the fused voltage values; must
+ *			have length equal to vreg->fuse_corner_count
+ *
+ * Voltage values in fuse_volt are modified in place.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_apply_fused_voltage_offset(struct cpr3_regulator *vreg,
+		int *fuse_volt)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+	int i;
+
+	if (!cpr3_msm8996_hmss_use_voltage_offset_fuse(vreg))
+		return 0;
+
+	for (i = 0; i < vreg->fuse_corner_count; i++)
+		fuse_volt[i] += cpr3_convert_open_loop_voltage_fuse(
+					0,
+					MSM8996PRO_HMSS_CBF_FUSE_STEP_VOLT,
+					fuse->cbf_voltage_offset[i],
+					MSM8996PRO_HMSS_CBF_VOLTAGE_FUSE_SIZE);
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_parse_corner_data() - parse HMSS corner data from device tree
+ *		properties of the CPR3 regulator's device node
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_parse_corner_data(struct cpr3_regulator *vreg)
+{
+	int rc;
+
+	rc = cpr3_parse_common_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "error reading corner data, rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_msm8996_hmss_calculate_open_loop_voltages() - calculate the open-loop
+ *		voltage for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If open-loop voltage interpolation is allowed in both device tree and in
+ * hardware fuses, then this function calculates the open-loop voltage for a
+ * given corner using linear interpolation.  This interpolation is performed
+ * using the processor frequencies of the lower and higher Fmax corners along
+ * with their fused open-loop voltages.
+ *
+ * If open-loop voltage interpolation is not allowed, then this function uses
+ * the Fmax fused open-loop voltage for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_hmss_calculate_open_loop_voltages(
+			struct cpr3_regulator *vreg)
+{
+	struct device_node *node = vreg->of_node;
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+	int rc = 0;
+	bool allow_interpolation;
+	u64 freq_low, volt_low, freq_high, volt_high;
+	int i, j, soc_revision;
+	const int *ref_volt;
+	int *fuse_volt;
+	int *fmax_corner;
+
+	fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
+				GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+				GFP_KERNEL);
+	if (!fuse_volt || !fmax_corner) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	soc_revision = vreg->thread->ctrl->soc_revision;
+	if (soc_revision == 1 || soc_revision == 2)
+		ref_volt = msm8996_v1_v2_hmss_fuse_ref_volt;
+	else if (soc_revision == 3 && fuse->speed_bin == 1
+				   && fuse->cpr_fusing_rev >= 5)
+		ref_volt = msm8996_v3_speed_bin1_rev5_hmss_fuse_ref_volt;
+	else
+		ref_volt = msm8996_v3_hmss_fuse_ref_volt;
+
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(
+			ref_volt[i],
+			MSM8996_HMSS_FUSE_STEP_VOLT, fuse->init_voltage[i],
+			MSM8996_HMSS_VOLTAGE_FUSE_SIZE);
+
+		/* Log fused open-loop voltage values for debugging purposes. */
+		if (i != CPR3_MSM8996_HMSS_FUSE_CORNER_LOWSVS)
+			cpr3_info(vreg, "fused %6s: open-loop=%7d uV\n",
+				cpr3_msm8996_hmss_fuse_corner_name[i],
+				fuse_volt[i]);
+	}
+
+	if (cpr3_msm8996_hmss_use_voltage_offset_fuse(vreg)) {
+		rc = cpr3_hmss_apply_fused_voltage_offset(vreg, fuse_volt);
+		if (rc) {
+			cpr3_err(vreg, "could not apply CBF voltage offsets, rc=%d\n",
+				rc);
+			goto done;
+		}
+
+		for (i = 0; i < vreg->fuse_corner_count; i++)
+			cpr3_info(vreg, "fused %6s: CBF offset open-loop=%7d uV\n",
+					cpr3_msm8996_hmss_fuse_corner_name[i],
+					fuse_volt[i]);
+	}
+
+	rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt);
+	if (rc) {
+		cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	allow_interpolation = of_property_read_bool(node,
+				"qcom,allow-voltage-interpolation");
+
+	/*
+	 * No LowSVS open-loop voltage fuse exists.  Instead, intermediate
+	 * voltages are interpolated between MinSVS and SVS.  Set the LowSVS
+	 * voltage to be equal to the adjusted SVS voltage in order to avoid
+	 * triggering an incorrect condition violation in the following loop.
+	 */
+	fuse_volt[CPR3_MSM8996_HMSS_FUSE_CORNER_LOWSVS]
+		= fuse_volt[CPR3_MSM8996_HMSS_FUSE_CORNER_SVS];
+
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		if (fuse_volt[i] < fuse_volt[i - 1]) {
+			cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n",
+				i, fuse_volt[i], i - 1, fuse_volt[i - 1],
+				i, fuse_volt[i - 1]);
+			fuse_volt[i] = fuse_volt[i - 1];
+		}
+	}
+
+	if (fuse->limitation == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION)
+		allow_interpolation = false;
+
+	if (!allow_interpolation) {
+		/* Use fused open-loop voltage for lower frequencies. */
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].open_loop_volt
+				= fuse_volt[vreg->corner[i].cpr_fuse_corner];
+		goto done;
+	}
+
+	for (i = 0; i < vreg->fuse_corner_count; i++)
+		fmax_corner[i] = vreg->fuse_corner_map[i];
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	for (i = 0; i <= fmax_corner[0]; i++)
+		vreg->corner[i].open_loop_volt = fuse_volt[0];
+
+	/*
+	 * Interpolation is not possible for corners mapped above the highest
+	 * fuse corner so use the fuse corner value directly.
+	 */
+	j = vreg->fuse_corner_count - 1;
+	for (i = fmax_corner[j] + 1; i < vreg->corner_count; i++)
+		vreg->corner[i].open_loop_volt = fuse_volt[j];
+
+	/*
+	 * Corner LowSVS should be skipped for voltage interpolation
+	 * since no fuse exists for it.  Instead, the lowest interpolation
+	 * should be between MinSVS and SVS.
+	 */
+	for (i = CPR3_MSM8996_HMSS_FUSE_CORNER_LOWSVS;
+	     i < vreg->fuse_corner_count - 1; i++) {
+		fmax_corner[i] = fmax_corner[i + 1];
+		fuse_volt[i] = fuse_volt[i + 1];
+	}
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count - 1; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		volt_low = fuse_volt[i - 1];
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+		volt_high = fuse_volt[i];
+
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].open_loop_volt = cpr3_interpolate(
+				freq_low, volt_low, freq_high, volt_high,
+				vreg->corner[j].proc_freq);
+	}
+
+done:
+	if (rc == 0) {
+		cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n");
+		for (i = 0; i < vreg->corner_count; i++)
+			cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i,
+				vreg->corner[i].open_loop_volt);
+
+		rc = cpr3_adjust_open_loop_voltages(vreg);
+		if (rc)
+			cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
+				rc);
+	}
+
+	kfree(fuse_volt);
+	kfree(fmax_corner);
+	return rc;
+}
+
+/**
+ * cpr3_msm8996_hmss_set_no_interpolation_quotients() - use the fused target
+ *		quotient values for lower frequencies.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @volt_adjust:	Pointer to array of per-corner closed-loop adjustment
+ *			voltages
+ * @volt_adjust_fuse:	Pointer to array of per-fuse-corner closed-loop
+ *			adjustment voltages
+ * @ro_scale:		Pointer to array of per-fuse-corner RO scaling factor
+ *			values with units of QUOT/V
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_hmss_set_no_interpolation_quotients(
+			struct cpr3_regulator *vreg, int *volt_adjust,
+			int *volt_adjust_fuse, int *ro_scale)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+	u32 quot, ro;
+	int quot_adjust;
+	int i, fuse_corner;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		fuse_corner = vreg->corner[i].cpr_fuse_corner;
+		quot = fuse->target_quot[fuse_corner];
+		quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
+				volt_adjust_fuse[fuse_corner] + volt_adjust[i]);
+		ro = fuse->ro_sel[fuse_corner];
+		vreg->corner[i].target_quot[ro] = quot + quot_adjust;
+		if (quot_adjust)
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n",
+				i, ro, quot, vreg->corner[i].target_quot[ro],
+				volt_adjust_fuse[fuse_corner] + volt_adjust[i]);
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_msm8996_hmss_calculate_target_quotients() - calculate the CPR target
+ *		quotient for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If target quotient interpolation is allowed in both device tree and in
+ * hardware fuses, then this function calculates the target quotient for a
+ * given corner using linear interpolation.  This interpolation is performed
+ * using the processor frequencies of the lower and higher Fmax corners along
+ * with the fused target quotient and quotient offset of the higher Fmax corner.
+ *
+ * If target quotient interpolation is not allowed, then this function uses
+ * the Fmax fused target quotient for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_hmss_calculate_target_quotients(
+			struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+	int rc;
+	bool allow_interpolation;
+	u64 freq_low, freq_high, prev_quot;
+	u64 *quot_low;
+	u64 *quot_high;
+	u32 quot, ro;
+	int i, j, fuse_corner, quot_adjust;
+	int *fmax_corner;
+	int *volt_adjust, *volt_adjust_fuse, *ro_scale;
+
+	/* Log fused quotient values for debugging purposes. */
+	cpr3_info(vreg, "fused MinSVS: quot[%2llu]=%4llu\n",
+		fuse->ro_sel[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS],
+		fuse->target_quot[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS]);
+	for (i = CPR3_MSM8996_HMSS_FUSE_CORNER_SVS;
+	     i <= CPR3_MSM8996_HMSS_FUSE_CORNER_TURBO; i++)
+		cpr3_info(vreg, "fused %6s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n",
+			cpr3_msm8996_hmss_fuse_corner_name[i],
+			fuse->ro_sel[i], fuse->target_quot[i], fuse->ro_sel[i],
+			fuse->quot_offset[i] * MSM8996_HMSS_QUOT_OFFSET_SCALE);
+
+	allow_interpolation = of_property_read_bool(vreg->of_node,
+					"qcom,allow-quotient-interpolation");
+
+	if (fuse->limitation == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION)
+		allow_interpolation = false;
+
+	volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust),
+					GFP_KERNEL);
+	volt_adjust_fuse = kcalloc(vreg->fuse_corner_count,
+					sizeof(*volt_adjust_fuse), GFP_KERNEL);
+	ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale),
+					GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+					GFP_KERNEL);
+	quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low),
+					GFP_KERNEL);
+	quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high),
+					GFP_KERNEL);
+	if (!volt_adjust || !volt_adjust_fuse || !ro_scale ||
+	    !fmax_corner || !quot_low || !quot_high) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0],
+				volt_adjust, volt_adjust_fuse, ro_scale);
+	if (rc) {
+		cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	rc = cpr3_hmss_apply_fused_voltage_offset(vreg, volt_adjust_fuse);
+	if (rc) {
+		cpr3_err(vreg, "could not apply CBF voltage offsets, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	if (!allow_interpolation) {
+		/* Use fused target quotients for lower frequencies. */
+		return cpr3_msm8996_hmss_set_no_interpolation_quotients(vreg,
+				volt_adjust, volt_adjust_fuse, ro_scale);
+	}
+
+	for (i = 0; i < vreg->fuse_corner_count; i++)
+		fmax_corner[i] = vreg->fuse_corner_map[i];
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	i = CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS;
+	quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]);
+	quot = fuse->target_quot[i] + quot_adjust;
+	quot_high[i] = quot;
+	ro = fuse->ro_sel[i];
+	if (quot_adjust)
+		cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n",
+			i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]);
+	for (i = 0; i <= fmax_corner[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS]; i++)
+		vreg->corner[i].target_quot[ro] = quot;
+
+	/*
+	 * Interpolation is not possible for corners mapped above the highest
+	 * fuse corner so use the fuse corner value directly.
+	 */
+	j = vreg->fuse_corner_count - 1;
+	quot_adjust = cpr3_quot_adjustment(ro_scale[j], volt_adjust_fuse[j]);
+	quot = fuse->target_quot[j] + quot_adjust;
+	ro = fuse->ro_sel[j];
+	for (i = fmax_corner[j] + 1; i < vreg->corner_count; i++)
+		vreg->corner[i].target_quot[ro] = quot;
+
+	/*
+	 * The LowSVS target quotient is defined as:
+	 *	(SVS target quotient) - (the unpacked SVS quotient offset)
+	 * MinSVS, LowSVS, and SVS fuse corners all share the same RO so it is
+	 * possible to interpolate between their target quotient values.
+	 */
+	i = CPR3_MSM8996_HMSS_FUSE_CORNER_LOWSVS;
+	quot_high[i] = fuse->target_quot[CPR3_MSM8996_HMSS_FUSE_CORNER_SVS]
+			- fuse->quot_offset[CPR3_MSM8996_HMSS_FUSE_CORNER_SVS]
+				* MSM8996_HMSS_QUOT_OFFSET_SCALE;
+	quot_low[i] = fuse->target_quot[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS];
+	if (quot_high[i] < quot_low[i]) {
+		cpr3_info(vreg, "quot_lowsvs=%llu < quot_minsvs=%llu; overriding: quot_lowsvs=%llu\n",
+			quot_high[i], quot_low[i], quot_low[i]);
+		quot_high[i] = quot_low[i];
+	}
+	if (fuse->ro_sel[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS]
+	    != fuse->ro_sel[CPR3_MSM8996_HMSS_FUSE_CORNER_SVS]) {
+		cpr3_info(vreg, "MinSVS RO=%llu != SVS RO=%llu; disabling LowSVS interpolation\n",
+			fuse->ro_sel[CPR3_MSM8996_HMSS_FUSE_CORNER_MINSVS],
+			fuse->ro_sel[CPR3_MSM8996_HMSS_FUSE_CORNER_SVS]);
+		quot_low[i] = quot_high[i];
+	}
+
+	for (i = CPR3_MSM8996_HMSS_FUSE_CORNER_SVS;
+	     i < vreg->fuse_corner_count; i++) {
+		quot_high[i] = fuse->target_quot[i];
+		if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
+			quot_low[i] = quot_high[i - 1];
+		else
+			quot_low[i] = quot_high[i]
+					- fuse->quot_offset[i]
+					  * MSM8996_HMSS_QUOT_OFFSET_SCALE;
+		if (quot_high[i] < quot_low[i]) {
+			cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n",
+				i, quot_high[i], i, quot_low[i],
+				i, quot_low[i]);
+			quot_high[i] = quot_low[i];
+		}
+	}
+
+	/* Perform per-fuse-corner target quotient adjustment */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		quot_adjust = cpr3_quot_adjustment(ro_scale[i],
+						   volt_adjust_fuse[i]);
+		if (quot_adjust) {
+			prev_quot = quot_high[i];
+			quot_high[i] += quot_adjust;
+			cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n",
+				i, fuse->ro_sel[i], prev_quot, quot_high[i],
+				volt_adjust_fuse[i]);
+		}
+
+		if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
+			quot_low[i] = quot_high[i - 1];
+		else
+			quot_low[i] += cpr3_quot_adjustment(ro_scale[i],
+						    volt_adjust_fuse[i - 1]);
+
+		if (quot_high[i] < quot_low[i]) {
+			cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n",
+				i, quot_high[i], i, quot_low[i],
+				i, quot_low[i]);
+			quot_high[i] = quot_low[i];
+		}
+	}
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+
+		ro = fuse->ro_sel[i];
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].target_quot[ro] = cpr3_interpolate(
+				freq_low, quot_low[i], freq_high, quot_high[i],
+				vreg->corner[j].proc_freq);
+	}
+
+	/* Perform per-corner target quotient adjustment */
+	for (i = 0; i < vreg->corner_count; i++) {
+		fuse_corner = vreg->corner[i].cpr_fuse_corner;
+		ro = fuse->ro_sel[fuse_corner];
+		quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
+						   volt_adjust[i]);
+		if (quot_adjust) {
+			prev_quot = vreg->corner[i].target_quot[ro];
+			vreg->corner[i].target_quot[ro] += quot_adjust;
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n",
+				i, ro, prev_quot,
+				vreg->corner[i].target_quot[ro],
+				volt_adjust[i]);
+		}
+	}
+
+	/* Ensure that target quotients increase monotonically */
+	for (i = 1; i < vreg->corner_count; i++) {
+		ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner];
+		if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro
+		    && vreg->corner[i].target_quot[ro]
+				< vreg->corner[i - 1].target_quot[ro]) {
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n",
+				i, ro, vreg->corner[i].target_quot[ro],
+				i - 1, ro, vreg->corner[i - 1].target_quot[ro],
+				i, ro, vreg->corner[i - 1].target_quot[ro]);
+			vreg->corner[i].target_quot[ro]
+				= vreg->corner[i - 1].target_quot[ro];
+		}
+	}
+
+done:
+	kfree(volt_adjust);
+	kfree(volt_adjust_fuse);
+	kfree(ro_scale);
+	kfree(fmax_corner);
+	kfree(quot_low);
+	kfree(quot_high);
+	return rc;
+}
+
+/**
+ * cpr3_msm8996_partial_binning_override() - override the voltage and quotient
+ *		settings for low corners based upon the value of the partial
+ *		binning fuse
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Some parts are not able to operate at low voltages.  The partial binning
+ * fuse specifies if a given part has such limitations.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_partial_binning_override(struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+	int i, fuse_corner, fmax_corner;
+
+	if (fuse->partial_binning == MSM8996_CPR_PARTIAL_BINNING_SVS)
+		fuse_corner = CPR3_MSM8996_HMSS_FUSE_CORNER_SVS;
+	else if (fuse->partial_binning == MSM8996_CPR_PARTIAL_BINNING_NOM)
+		fuse_corner = CPR3_MSM8996_HMSS_FUSE_CORNER_NOM;
+	else
+		return 0;
+
+	cpr3_info(vreg, "overriding voltages and quotients for all corners below %s Fmax\n",
+		cpr3_msm8996_hmss_fuse_corner_name[fuse_corner]);
+
+	fmax_corner = -1;
+	for (i = vreg->corner_count - 1; i >= 0; i--) {
+		if (vreg->corner[i].cpr_fuse_corner == fuse_corner) {
+			fmax_corner = i;
+			break;
+		}
+	}
+	if (fmax_corner < 0) {
+		cpr3_err(vreg, "could not find %s Fmax corner\n",
+			cpr3_msm8996_hmss_fuse_corner_name[fuse_corner]);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < fmax_corner; i++)
+		vreg->corner[i] = vreg->corner[fmax_corner];
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_print_settings() - print out HMSS CPR configuration settings into
+ *		the kernel log for debugging purposes
+ * @vreg:		Pointer to the CPR3 regulator
+ */
+static void cpr3_hmss_print_settings(struct cpr3_regulator *vreg)
+{
+	struct cpr3_corner *corner;
+	int i;
+
+	cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n");
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n",
+			i, corner->proc_freq, corner->cpr_fuse_corner,
+			corner->floor_volt, corner->open_loop_volt,
+			corner->ceiling_volt);
+	}
+
+	if (vreg->thread->ctrl->apm)
+		cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n",
+			vreg->thread->ctrl->apm_threshold_volt,
+			vreg->thread->ctrl->apm_adj_volt);
+}
+
+/**
+ * cpr3_hmss_init_thread() - perform steps necessary to initialize the
+ *		configuration data for a CPR3 thread
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_init_thread(struct cpr3_thread *thread)
+{
+	int rc;
+
+	rc = cpr3_parse_common_thread_data(thread);
+	if (rc) {
+		cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n",
+			thread->thread_id, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+#define MAX_VREG_NAME_SIZE 25
+/**
+ * cpr3_hmss_kvreg_init() - initialize HMSS Kryo Regulator data for a CPR3
+ *		regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function loads Kryo Regulator data from device tree if it is present
+ * and requests a handle to the appropriate Kryo regulator device. In addition,
+ * it initializes Kryo Regulator data originating from hardware fuses, such as
+ * the LDO retention voltage, and requests the Kryo retention regulator to
+ * be configured to that value.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_kvreg_init(struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = vreg->platform_fuses;
+	struct device_node *node = vreg->of_node;
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int id = vreg->thread->thread_id;
+	char kvreg_name_buf[MAX_VREG_NAME_SIZE];
+	int rc;
+
+	scnprintf(kvreg_name_buf, MAX_VREG_NAME_SIZE,
+		"vdd-thread%d-ldo-supply", id);
+
+	if (!of_find_property(ctrl->dev->of_node, kvreg_name_buf, NULL))
+		return 0;
+	else if (!of_find_property(node, "qcom,ldo-min-headroom-voltage", NULL))
+		return 0;
+
+	scnprintf(kvreg_name_buf, MAX_VREG_NAME_SIZE, "vdd-thread%d-ldo", id);
+
+	vreg->ldo_regulator = devm_regulator_get(ctrl->dev, kvreg_name_buf);
+	if (IS_ERR(vreg->ldo_regulator)) {
+		rc = PTR_ERR(vreg->ldo_regulator);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to request %s regulator, rc=%d\n",
+				 kvreg_name_buf, rc);
+		return rc;
+	}
+
+	vreg->ldo_regulator_bypass = BHS_MODE;
+
+	scnprintf(kvreg_name_buf, MAX_VREG_NAME_SIZE, "vdd-thread%d-ldo-ret",
+		  id);
+
+	vreg->ldo_ret_regulator = devm_regulator_get(ctrl->dev, kvreg_name_buf);
+	if (IS_ERR(vreg->ldo_ret_regulator)) {
+		rc = PTR_ERR(vreg->ldo_ret_regulator);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to request %s regulator, rc=%d\n",
+				 kvreg_name_buf, rc);
+		return rc;
+	}
+
+	if (!ctrl->system_supply_max_volt) {
+		cpr3_err(ctrl, "system-supply max voltage must be specified\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(node, "qcom,ldo-min-headroom-voltage",
+				&vreg->ldo_min_headroom_volt);
+	if (rc) {
+		cpr3_err(vreg, "error reading qcom,ldo-min-headroom-voltage, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,ldo-max-headroom-voltage",
+				  &vreg->ldo_max_headroom_volt);
+	if (rc) {
+		cpr3_err(vreg, "error reading qcom,ldo-max-headroom-voltage, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,ldo-max-voltage",
+				&vreg->ldo_max_volt);
+	if (rc) {
+		cpr3_err(vreg, "error reading qcom,ldo-max-voltage, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	/* Determine the CPU retention voltage based on fused data */
+	vreg->ldo_ret_volt =
+		max(msm8996_vdd_apcc_fuse_ret_volt[fuse->vdd_apcc_ret_fuse],
+		    msm8996_vdd_mx_fuse_ret_volt[fuse->vdd_mx_ret_fuse]);
+
+	rc = regulator_set_voltage(vreg->ldo_ret_regulator, vreg->ldo_ret_volt,
+				   INT_MAX);
+	if (rc < 0) {
+		cpr3_err(vreg, "regulator_set_voltage(ldo_ret) == %d failed, rc=%d\n",
+			 vreg->ldo_ret_volt, rc);
+		return rc;
+	}
+
+	/* optional properties, do not error out if missing */
+	of_property_read_u32(node, "qcom,ldo-adjust-voltage",
+			     &vreg->ldo_adjust_volt);
+
+	vreg->ldo_mode_allowed = !of_property_read_bool(node,
+							"qcom,ldo-disable");
+
+	cpr3_info(vreg, "LDO min headroom=%d uV, LDO max headroom=%d uV, LDO adj=%d uV, LDO mode=%s, LDO retention=%d uV\n",
+		  vreg->ldo_min_headroom_volt,
+		  vreg->ldo_max_headroom_volt,
+		  vreg->ldo_adjust_volt,
+		  vreg->ldo_mode_allowed ? "allowed" : "disallowed",
+		  vreg->ldo_ret_volt);
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_mem_acc_init() - initialize mem-acc regulator data for
+ *		a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function loads mem-acc data from device tree to enable
+ * the control of mem-acc settings based upon the CPR3 regulator
+ * output voltage.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_mem_acc_init(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int id = vreg->thread->thread_id;
+	char mem_acc_vreg_name_buf[MAX_VREG_NAME_SIZE];
+	int rc;
+
+	scnprintf(mem_acc_vreg_name_buf, MAX_VREG_NAME_SIZE,
+		  "mem-acc-thread%d-supply", id);
+
+	if (!of_find_property(ctrl->dev->of_node, mem_acc_vreg_name_buf,
+			      NULL)) {
+		cpr3_debug(vreg, "not using memory accelerator regulator\n");
+		return 0;
+	} else if (!of_property_read_bool(vreg->of_node, "qcom,uses-mem-acc")) {
+		return 0;
+	}
+
+	scnprintf(mem_acc_vreg_name_buf, MAX_VREG_NAME_SIZE,
+		  "mem-acc-thread%d", id);
+
+	vreg->mem_acc_regulator = devm_regulator_get(ctrl->dev,
+						     mem_acc_vreg_name_buf);
+	if (IS_ERR(vreg->mem_acc_regulator)) {
+		rc = PTR_ERR(vreg->mem_acc_regulator);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to request %s regulator, rc=%d\n",
+				 mem_acc_vreg_name_buf, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_init_regulator() - perform all steps necessary to initialize the
+ *		configuration data for a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_init_regulator(struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse;
+	int rc;
+
+	rc = cpr3_msm8996_hmss_read_fuse_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cpr3_hmss_kvreg_init(vreg);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to initialize Kryo Regulator settings, rc=%d\n",
+				 rc);
+		return rc;
+	}
+
+	rc = cpr3_hmss_mem_acc_init(vreg);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to initialize mem-acc regulator settings, rc=%d\n",
+				 rc);
+		return rc;
+	}
+
+	fuse = vreg->platform_fuses;
+	if (fuse->limitation == MSM8996_CPR_LIMITATION_UNSUPPORTED) {
+		cpr3_err(vreg, "this chip requires an unsupported voltage\n");
+		return -EPERM;
+	} else if (fuse->limitation
+			== MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION) {
+		vreg->thread->ctrl->cpr_allowed_hw = false;
+	}
+
+	rc = of_property_read_u32(vreg->of_node, "qcom,cpr-pd-bypass-mask",
+				&vreg->pd_bypass_mask);
+	if (rc) {
+		cpr3_err(vreg, "error reading qcom,cpr-pd-bypass-mask, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_hmss_parse_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (of_find_property(vreg->of_node, "qcom,cpr-dynamic-floor-corner",
+				NULL)) {
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,cpr-dynamic-floor-corner",
+			1, &vreg->dynamic_floor_corner);
+		if (rc) {
+			cpr3_err(vreg, "error reading qcom,cpr-dynamic-floor-corner, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		if (vreg->dynamic_floor_corner <= 0) {
+			vreg->uses_dynamic_floor = false;
+		} else if (vreg->dynamic_floor_corner < CPR3_CORNER_OFFSET
+			   || vreg->dynamic_floor_corner
+				> vreg->corner_count - 1 + CPR3_CORNER_OFFSET) {
+			cpr3_err(vreg, "dynamic floor corner=%d not in range [%d, %d]\n",
+				vreg->dynamic_floor_corner, CPR3_CORNER_OFFSET,
+				vreg->corner_count - 1 + CPR3_CORNER_OFFSET);
+			return -EINVAL;
+		}
+
+		vreg->dynamic_floor_corner -= CPR3_CORNER_OFFSET;
+		vreg->uses_dynamic_floor = true;
+	}
+
+	rc = cpr3_msm8996_hmss_calculate_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_limit_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_open_loop_voltage_as_ceiling(vreg);
+
+	rc = cpr3_limit_floor_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cpr3_msm8996_hmss_calculate_target_quotients(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_msm8996_partial_binning_override(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to override voltages and quotients based on partial binning fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_hmss_print_settings(vreg);
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_init_aging() - perform HMSS CPR3 controller specific
+ *		aging initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_init_aging(struct cpr3_controller *ctrl)
+{
+	struct cpr3_msm8996_hmss_fuses *fuse = NULL;
+	struct cpr3_regulator *vreg;
+	u32 aging_ro_scale;
+	int i, j, rc;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			if (ctrl->thread[i].vreg[j].aging_allowed) {
+				ctrl->aging_required = true;
+				vreg = &ctrl->thread[i].vreg[j];
+				fuse = vreg->platform_fuses;
+				break;
+			}
+		}
+	}
+
+	if (!ctrl->aging_required || !fuse || !vreg)
+		return 0;
+
+	rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
+					1, &aging_ro_scale);
+	if (rc)
+		return rc;
+
+	if (aging_ro_scale == 0) {
+		cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
+			aging_ro_scale);
+		return -EINVAL;
+	}
+
+	ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
+	ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
+
+	ctrl->aging_sensor_count = 1;
+	ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
+	if (!ctrl->aging_sensor)
+		return -ENOMEM;
+
+	ctrl->aging_sensor->sensor_id = MSM8996_HMSS_AGING_SENSOR_ID;
+	ctrl->aging_sensor->bypass_mask[0] = MSM8996_HMSS_AGING_BYPASS_MASK0;
+	ctrl->aging_sensor->ro_scale = aging_ro_scale;
+
+	ctrl->aging_sensor->init_quot_diff
+		= cpr3_convert_open_loop_voltage_fuse(0,
+			MSM8996_HMSS_AGING_INIT_QUOT_DIFF_SCALE,
+			fuse->aging_init_quot_diff,
+			MSM8996_HMSS_AGING_INIT_QUOT_DIFF_SIZE);
+
+	cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
+		ctrl->aging_sensor->sensor_id,
+		ctrl->aging_sensor->init_quot_diff,
+		ctrl->aging_sensor->ro_scale);
+
+	return 0;
+}
+
+/**
+ * cpr3_hmss_init_controller() - perform HMSS CPR3 controller specific
+ *		initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_hmss_init_controller(struct cpr3_controller *ctrl)
+{
+	int i, rc;
+
+	rc = cpr3_parse_common_ctrl_data(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	ctrl->vdd_limit_regulator = devm_regulator_get(ctrl->dev, "vdd-limit");
+	if (IS_ERR(ctrl->vdd_limit_regulator)) {
+		rc = PTR_ERR(ctrl->vdd_limit_regulator);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to request vdd-supply regulator, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+			"qcom,cpr-up-down-delay-time",
+			&ctrl->up_down_delay_time);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,cpr-up-down-delay-time, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	/* No error check since this is an optional property. */
+	of_property_read_u32(ctrl->dev->of_node,
+			     "qcom,system-supply-max-voltage",
+			     &ctrl->system_supply_max_volt);
+
+	/* No error check since this is an optional property. */
+	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-clock-throttling",
+			&ctrl->proc_clock_throttle);
+
+	rc = cpr3_apm_init(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to initialize APM settings, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	ctrl->sensor_count = MSM8996_HMSS_CPR_SENSOR_COUNT;
+
+	ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count,
+		sizeof(*ctrl->sensor_owner), GFP_KERNEL);
+	if (!ctrl->sensor_owner)
+		return -ENOMEM;
+
+	/* Specify sensor ownership */
+	for (i = MSM8996_HMSS_THREAD0_SENSOR_MIN;
+	     i <= MSM8996_HMSS_THREAD0_SENSOR_MAX; i++)
+		ctrl->sensor_owner[i] = 0;
+	for (i = MSM8996_HMSS_THREAD1_SENSOR_MIN;
+	     i <= MSM8996_HMSS_THREAD1_SENSOR_MAX; i++)
+		ctrl->sensor_owner[i] = 1;
+
+	ctrl->cpr_clock_rate = MSM8996_HMSS_CPR_CLOCK_RATE;
+	ctrl->ctrl_type = CPR_CTRL_TYPE_CPR3;
+	ctrl->supports_hw_closed_loop = true;
+	ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node,
+						"qcom,cpr-hw-closed-loop");
+
+	if (ctrl->mem_acc_regulator) {
+		rc = of_property_read_u32(ctrl->dev->of_node,
+					  "qcom,mem-acc-supply-threshold-voltage",
+					  &ctrl->mem_acc_threshold_volt);
+		if (rc) {
+			cpr3_err(ctrl, "error reading property qcom,mem-acc-supply-threshold-voltage, rc=%d\n",
+				 rc);
+			return rc;
+		}
+
+		ctrl->mem_acc_threshold_volt =
+			CPR3_ROUND(ctrl->mem_acc_threshold_volt,
+				   ctrl->step_volt);
+
+		rc = of_property_read_u32_array(ctrl->dev->of_node,
+			"qcom,mem-acc-supply-corner-map",
+			&ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER],
+			CPR3_MEM_ACC_CORNERS);
+		if (rc) {
+			cpr3_err(ctrl, "error reading qcom,mem-acc-supply-corner-map, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int cpr3_hmss_regulator_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_suspend(ctrl);
+}
+
+static int cpr3_hmss_regulator_resume(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_resume(ctrl);
+}
+
+/* Data corresponds to the SoC revision */
+static const struct of_device_id cpr_regulator_match_table[] = {
+	{
+		.compatible = "qcom,cpr3-msm8996-v1-hmss-regulator",
+		.data = (void *)(uintptr_t)1
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996-v2-hmss-regulator",
+		.data = (void *)(uintptr_t)2
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996-v3-hmss-regulator",
+		.data = (void *)(uintptr_t)3
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996-hmss-regulator",
+		.data = (void *)(uintptr_t)3
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996pro-hmss-regulator",
+		.data = (void *)(uintptr_t)MSM8996PRO_SOC_ID,
+	},
+	{}
+};
+
+static int cpr3_hmss_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cpr3_controller *ctrl;
+	struct cpr3_regulator *vreg;
+	int i, j, rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	/* Set to false later if anything precludes CPR operation. */
+	ctrl->cpr_allowed_hw = true;
+
+	rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name",
+					&ctrl->name);
+	if (rc) {
+		cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	match = of_match_node(cpr_regulator_match_table, dev->of_node);
+	if (match)
+		ctrl->soc_revision = (uintptr_t)match->data;
+	else
+		cpr3_err(ctrl, "could not find compatible string match\n");
+
+	rc = cpr3_map_fuse_base(ctrl, pdev);
+	if (rc) {
+		cpr3_err(ctrl, "could not map fuse base address\n");
+		return rc;
+	}
+
+	rc = cpr3_allocate_threads(ctrl, MSM8996_HMSS_POWER_CLUSTER_THREAD_ID,
+		MSM8996_HMSS_PERFORMANCE_CLUSTER_THREAD_ID);
+	if (rc) {
+		cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->thread_count < 1) {
+		cpr3_err(ctrl, "thread nodes are missing\n");
+		return -EINVAL;
+	}
+
+	rc = cpr3_hmss_init_controller(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		rc = cpr3_hmss_init_thread(&ctrl->thread[i]);
+		if (rc) {
+			cpr3_err(ctrl, "thread %u initialization failed, rc=%d\n",
+				ctrl->thread[i].thread_id, rc);
+			return rc;
+		}
+
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+
+			rc = cpr3_hmss_init_regulator(vreg);
+			if (rc) {
+				cpr3_err(vreg, "regulator initialization failed, rc=%d\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	rc = cpr3_hmss_init_aging(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	platform_set_drvdata(pdev, ctrl);
+
+	return cpr3_regulator_register(pdev, ctrl);
+}
+
+static int cpr3_hmss_regulator_remove(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_unregister(ctrl);
+}
+
+static struct platform_driver cpr3_hmss_regulator_driver = {
+	.driver		= {
+		.name		= "qcom,cpr3-hmss-regulator",
+		.of_match_table	= cpr_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= cpr3_hmss_regulator_probe,
+	.remove		= cpr3_hmss_regulator_remove,
+	.suspend	= cpr3_hmss_regulator_suspend,
+	.resume		= cpr3_hmss_regulator_resume,
+};
+
+static int cpr_regulator_init(void)
+{
+	return platform_driver_register(&cpr3_hmss_regulator_driver);
+}
+
+static void cpr_regulator_exit(void)
+{
+	platform_driver_unregister(&cpr3_hmss_regulator_driver);
+}
+
+MODULE_DESCRIPTION("CPR3 HMSS regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(cpr_regulator_init);
+module_exit(cpr_regulator_exit);
diff --git a/drivers/regulator/cpr3-mmss-regulator.c b/drivers/regulator/cpr3-mmss-regulator.c
new file mode 100644
index 0000000..41032dd
--- /dev/null
+++ b/drivers/regulator/cpr3-mmss-regulator.c
@@ -0,0 +1,1251 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include "cpr3-regulator.h"
+
+#define MSM8996_MMSS_FUSE_CORNERS	4
+
+/**
+ * struct cpr3_msm8996_mmss_fuses - MMSS specific fuse data for MSM8996
+ * @init_voltage:	Initial (i.e. open-loop) voltage fuse parameter value
+ *			for each fuse corner (raw, not converted to a voltage)
+ * @offset_voltage:	The closed-loop voltage margin adjustment fuse parameter
+ *			value for each fuse corner (raw, not converted to a
+ *			voltage)
+ * @speed_bin:		Graphics processor speed bin fuse parameter value for
+ *			the given chip
+ * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
+ * @limitation:		CPR limitation select fuse parameter value
+ * @aging_init_quot_diff:	Initial quotient difference between CPR aging
+ *			min and max sensors measured at time of manufacturing
+ * @force_highest_corner:	Flag indicating that all corners must operate
+ *			at the voltage of the highest corner.  This is
+ *			applicable to MSM8998 only.
+ *
+ * This struct holds the values for all of the fuses read from memory.
+ */
+struct cpr3_msm8996_mmss_fuses {
+	u64	init_voltage[MSM8996_MMSS_FUSE_CORNERS];
+	u64	offset_voltage[MSM8996_MMSS_FUSE_CORNERS];
+	u64	speed_bin;
+	u64	cpr_fusing_rev;
+	u64	limitation;
+	u64	aging_init_quot_diff;
+	u64	force_highest_corner;
+};
+
+/* Fuse combos 0 -  7 map to CPR fusing revision 0 - 7 */
+#define CPR3_MSM8996_MMSS_FUSE_COMBO_COUNT	8
+
+/*
+ * Fuse combos 0 -  7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
+ * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ */
+#define CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT	16
+
+/* Fuse combos 0 -  7 map to CPR fusing revision 0 - 7 */
+#define CPR3_MSM8998_MMSS_FUSE_COMBO_COUNT	8
+
+/*
+ * MSM8996 MMSS fuse parameter locations:
+ *
+ * Structs are organized with the following dimensions:
+ *	Outer: 0 to 3 for fuse corners from lowest to highest corner
+ *	Inner: large enough to hold the longest set of parameter segments which
+ *		fully defines a fuse parameter, +1 (for NULL termination).
+ *		Each segment corresponds to a contiguous group of bits from a
+ *		single fuse row.  These segments are concatentated together in
+ *		order to form the full fuse parameter value.  The segments for
+ *		a given parameter may correspond to different fuse rows.
+ */
+static const struct cpr3_fuse_param
+msm8996_mmss_init_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = {
+	{{63, 55, 59}, {} },
+	{{63, 50, 54}, {} },
+	{{63, 45, 49}, {} },
+	{{63, 40, 44}, {} },
+};
+
+static const struct cpr3_fuse_param msm8996_cpr_fusing_rev_param[] = {
+	{39, 48, 50},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8996_cpr_limitation_param[] = {
+	{41, 31, 32},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8996_mmss_aging_init_quot_diff_param[] = {
+	{68, 26, 31},
+	{},
+};
+
+/* Offset voltages are defined for SVS and Turbo fuse corners only */
+static const struct cpr3_fuse_param
+msm8996_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = {
+	{{} },
+	{{66, 42, 44}, {} },
+	{{} },
+	{{64, 58, 61}, {} },
+};
+
+static const struct cpr3_fuse_param msm8996pro_mmss_speed_bin_param[] = {
+	{39, 60, 61},
+	{},
+};
+
+/* MSM8998 MMSS fuse parameter locations: */
+static const struct cpr3_fuse_param
+msm8998_mmss_init_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = {
+	{{65, 39, 43}, {} },
+	{{65, 34, 38}, {} },
+	{{65, 29, 33}, {} },
+	{{65, 24, 28}, {} },
+};
+
+static const struct cpr3_fuse_param msm8998_cpr_fusing_rev_param[] = {
+	{39, 48, 50},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8998_cpr_limitation_param[] = {
+	{41, 46, 47},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8998_mmss_aging_init_quot_diff_param[] = {
+	{65, 60, 63},
+	{66, 0, 3},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8998_mmss_offset_voltage_param[MSM8996_MMSS_FUSE_CORNERS][2] = {
+	{{65, 56, 59}, {} },
+	{{65, 52, 55}, {} },
+	{{65, 48, 51}, {} },
+	{{65, 44, 47}, {} },
+};
+
+static const struct cpr3_fuse_param
+msm8998_cpr_force_highest_corner_param[] = {
+	{100, 45, 45},
+	{},
+};
+
+#define MSM8996PRO_SOC_ID			4
+#define MSM8998_V1_SOC_ID			5
+#define MSM8998_V2_SOC_ID			6
+
+/*
+ * Some initial msm8996 parts cannot be used in a meaningful way by software.
+ * Other parts can only be used when operating with CPR disabled (i.e. at the
+ * fused open-loop voltage) when no voltage interpolation is applied.  A fuse
+ * parameter is provided so that software can properly handle these limitations.
+ */
+enum msm8996_cpr_limitation {
+	MSM8996_CPR_LIMITATION_NONE = 0,
+	MSM8996_CPR_LIMITATION_UNSUPPORTED = 2,
+	MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION = 3,
+};
+
+/* Additional MSM8996 specific data: */
+
+/* Open loop voltage fuse reference voltages in microvolts */
+static const int msm8996_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+	670000,
+	745000,
+	905000,
+	1015000,
+};
+
+static const int msm8996pro_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+	670000,
+	745000,
+	905000,
+	1065000,
+};
+
+static const int msm8998_v1_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+	528000,
+	656000,
+	812000,
+	932000,
+};
+
+static const int
+msm8998_v1_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+	632000,
+	768000,
+	896000,
+	1032000,
+};
+
+static const int msm8998_v2_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+	516000,
+	628000,
+	752000,
+	924000,
+};
+
+static const int
+msm8998_v2_rev0_mmss_fuse_ref_volt[MSM8996_MMSS_FUSE_CORNERS] = {
+	616000,
+	740000,
+	828000,
+	1024000,
+};
+
+#define MSM8996_MMSS_FUSE_STEP_VOLT		10000
+#define MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT	10000
+#define MSM8996_MMSS_VOLTAGE_FUSE_SIZE		5
+#define MSM8996_MMSS_MIN_VOLTAGE_FUSE_VAL	0x1F
+#define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE	2
+#define MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE	6
+
+#define MSM8996_MMSS_CPR_SENSOR_COUNT		35
+
+#define MSM8996_MMSS_CPR_CLOCK_RATE		19200000
+
+#define MSM8996_MMSS_AGING_SENSOR_ID		29
+#define MSM8996_MMSS_AGING_BYPASS_MASK0		(GENMASK(23, 0))
+
+#define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SCALE	1
+#define MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SIZE	8
+
+#define MSM8998_MMSS_CPR_SENSOR_COUNT			35
+
+#define MSM8998_MMSS_AGING_SENSOR_ID			29
+#define MSM8998_MMSS_AGING_BYPASS_MASK0		(GENMASK(23, 0))
+
+#define MSM8998_MMSS_MAX_TEMP_POINTS			3
+#define MSM8998_MMSS_TEMP_SENSOR_ID_START		12
+#define MSM8998_MMSS_TEMP_SENSOR_ID_END		13
+
+/*
+ * Some initial msm8998 parts cannot be operated at low voltages.  The
+ * open-loop voltage fuses are reused to identify these parts so that software
+ * can properly handle the limitation.  0xF means that the next higher fuse
+ * corner should be used.  0xE means that the next higher fuse corner which
+ * does not have a voltage limitation should be used.
+ */
+enum msm8998_cpr_partial_binning {
+	MSM8998_CPR_PARTIAL_BINNING_NEXT_CORNER = 0xF,
+	MSM8998_CPR_PARTIAL_BINNING_SAFE_CORNER = 0xE,
+};
+
+/*
+ * The partial binning open-loop voltage fuse values only apply to the lowest
+ * two fuse corners (0 and 1, i.e. MinSVS and SVS).
+ */
+#define MSM8998_CPR_PARTIAL_BINNING_MAX_FUSE_CORNER	1
+
+static inline bool cpr3_ctrl_is_msm8998(const struct cpr3_controller *ctrl)
+{
+	return ctrl->soc_revision == MSM8998_V1_SOC_ID ||
+		ctrl->soc_revision == MSM8998_V2_SOC_ID;
+}
+
+/**
+ * cpr3_msm8996_mmss_read_fuse_data() - load MMSS specific fuse parameter values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function allocates a cpr3_msm8996_mmss_fuses struct, fills it with
+ * values read out of hardware fuses, and finally copies common fuse values
+ * into the regulator struct.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_mmss_read_fuse_data(struct cpr3_regulator *vreg)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	struct cpr3_msm8996_mmss_fuses *fuse;
+	int i, rc, combo_max;
+
+	fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL);
+	if (!fuse)
+		return -ENOMEM;
+
+	if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) {
+		rc = cpr3_read_fuse_param(base, msm8996pro_mmss_speed_bin_param,
+					&fuse->speed_bin);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n",
+				rc);
+			return rc;
+		}
+		cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin);
+	}
+
+	rc = cpr3_read_fuse_param(base,
+			cpr3_ctrl_is_msm8998(vreg->thread->ctrl)
+				? msm8998_cpr_fusing_rev_param
+				: msm8996_cpr_fusing_rev_param,
+			&fuse->cpr_fusing_rev);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
+
+	rc = cpr3_read_fuse_param(base,
+			cpr3_ctrl_is_msm8998(vreg->thread->ctrl)
+				? msm8998_cpr_limitation_param
+				: msm8996_cpr_limitation_param,
+			&fuse->limitation);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR limitation fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR limitation = %s\n",
+		fuse->limitation == MSM8996_CPR_LIMITATION_UNSUPPORTED
+		? "unsupported chip" : fuse->limitation
+			  == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION
+		? "CPR disabled and no interpolation" : "none");
+
+	rc = cpr3_read_fuse_param(base,
+			cpr3_ctrl_is_msm8998(vreg->thread->ctrl)
+				? msm8998_mmss_aging_init_quot_diff_param
+				: msm8996_mmss_aging_init_quot_diff_param,
+			&fuse->aging_init_quot_diff);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	for (i = 0; i < MSM8996_MMSS_FUSE_CORNERS; i++) {
+		rc = cpr3_read_fuse_param(base,
+			cpr3_ctrl_is_msm8998(vreg->thread->ctrl)
+				? msm8998_mmss_init_voltage_param[i]
+				: msm8996_mmss_init_voltage_param[i],
+			&fuse->init_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+			cpr3_ctrl_is_msm8998(vreg->thread->ctrl)
+				? msm8998_mmss_offset_voltage_param[i]
+				: msm8996_mmss_offset_voltage_param[i],
+			&fuse->offset_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	if (cpr3_ctrl_is_msm8998(vreg->thread->ctrl)) {
+		rc = cpr3_read_fuse_param(base,
+			msm8998_cpr_force_highest_corner_param,
+			&fuse->force_highest_corner);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read CPR force highest corner fuse, rc=%d\n",
+				rc);
+			return rc;
+		}
+		if (fuse->force_highest_corner)
+			cpr3_info(vreg, "Fusing requires all operation at the highest corner\n");
+	}
+
+	if (cpr3_ctrl_is_msm8998(vreg->thread->ctrl)) {
+		combo_max = CPR3_MSM8998_MMSS_FUSE_COMBO_COUNT;
+		vreg->fuse_combo = fuse->cpr_fusing_rev;
+	} else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID) {
+		combo_max = CPR3_MSM8996PRO_MMSS_FUSE_COMBO_COUNT;
+		vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
+	} else {
+		combo_max = CPR3_MSM8996_MMSS_FUSE_COMBO_COUNT;
+		vreg->fuse_combo = fuse->cpr_fusing_rev;
+	}
+
+	if (vreg->fuse_combo >= combo_max) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found, not in range 0 - %d\n",
+			vreg->fuse_combo, combo_max - 1);
+		return -EINVAL;
+	}
+
+	vreg->speed_bin_fuse	= fuse->speed_bin;
+	vreg->cpr_rev_fuse	= fuse->cpr_fusing_rev;
+	vreg->fuse_corner_count	= MSM8996_MMSS_FUSE_CORNERS;
+	vreg->platform_fuses	= fuse;
+
+	return 0;
+}
+
+/**
+ * cpr3_mmss_parse_corner_data() - parse MMSS corner data from device tree
+ *		properties of the regulator's device node
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_mmss_parse_corner_data(struct cpr3_regulator *vreg)
+{
+	int i, rc;
+	u32 *temp;
+
+	rc = cpr3_parse_common_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "error reading corner data, rc=%d\n", rc);
+		return rc;
+	}
+
+	temp = kcalloc(vreg->corner_count * CPR3_RO_COUNT, sizeof(*temp),
+			GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-target-quotients",
+			CPR3_RO_COUNT, temp);
+	if (rc) {
+		cpr3_err(vreg, "could not load target quotients, rc=%d\n", rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++)
+		memcpy(vreg->corner[i].target_quot, &temp[i * CPR3_RO_COUNT],
+			sizeof(*temp) * CPR3_RO_COUNT);
+
+done:
+	kfree(temp);
+	return rc;
+}
+
+/**
+ * cpr3_msm8996_mmss_adjust_target_quotients() - adjust the target quotients
+ *		for each corner according to device tree values and fuse values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_mmss_adjust_target_quotients(
+			struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses;
+	const struct cpr3_fuse_param (*offset_param)[2];
+	int *volt_offset;
+	int i, fuse_len, rc = 0;
+
+	volt_offset = kcalloc(vreg->fuse_corner_count, sizeof(*volt_offset),
+				GFP_KERNEL);
+	if (!volt_offset)
+		return -ENOMEM;
+
+	offset_param = cpr3_ctrl_is_msm8998(vreg->thread->ctrl)
+			? msm8998_mmss_offset_voltage_param
+			: msm8996_mmss_offset_voltage_param;
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		fuse_len = offset_param[i][0].bit_end + 1
+			   - offset_param[i][0].bit_start;
+		volt_offset[i] = cpr3_convert_open_loop_voltage_fuse(
+			0, MSM8996_MMSS_OFFSET_FUSE_STEP_VOLT,
+			fuse->offset_voltage[i], fuse_len);
+		if (volt_offset[i])
+			cpr3_info(vreg, "fuse_corner[%d] offset=%7d uV\n",
+				i, volt_offset[i]);
+	}
+
+	rc = cpr3_adjust_target_quotients(vreg, volt_offset);
+	if (rc)
+		cpr3_err(vreg, "adjust target quotients failed, rc=%d\n", rc);
+
+	kfree(volt_offset);
+	return rc;
+}
+
+/**
+ * cpr3_msm8996_mmss_calculate_open_loop_voltages() - calculate the open-loop
+ *		voltage for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If open-loop voltage interpolation is allowed in both device tree and in
+ * hardware fuses, then this function calculates the open-loop voltage for a
+ * given corner using linear interpolation.  This interpolation is performed
+ * using the processor frequencies of the lower and higher Fmax corners along
+ * with their fused open-loop voltages.
+ *
+ * If open-loop voltage interpolation is not allowed, then this function uses
+ * the Fmax fused open-loop voltage for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8996_mmss_calculate_open_loop_voltages(
+			struct cpr3_regulator *vreg)
+{
+	struct device_node *node = vreg->of_node;
+	struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses;
+	bool is_msm8998 = cpr3_ctrl_is_msm8998(vreg->thread->ctrl);
+	int rc = 0;
+	bool allow_interpolation;
+	u64 freq_low, volt_low, freq_high, volt_high, volt_init;
+	int i, j;
+	const int *ref_volt;
+	int *fuse_volt;
+	int *fmax_corner;
+
+	fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
+				GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+				GFP_KERNEL);
+	if (!fuse_volt || !fmax_corner) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	if (vreg->thread->ctrl->soc_revision == MSM8998_V2_SOC_ID
+	    && fuse->cpr_fusing_rev == 0)
+		ref_volt = msm8998_v2_rev0_mmss_fuse_ref_volt;
+	else if (vreg->thread->ctrl->soc_revision == MSM8998_V2_SOC_ID)
+		ref_volt = msm8998_v2_mmss_fuse_ref_volt;
+	else if (vreg->thread->ctrl->soc_revision == MSM8998_V1_SOC_ID
+	    && fuse->cpr_fusing_rev == 0)
+		ref_volt = msm8998_v1_rev0_mmss_fuse_ref_volt;
+	else if (vreg->thread->ctrl->soc_revision == MSM8998_V1_SOC_ID)
+		ref_volt = msm8998_v1_mmss_fuse_ref_volt;
+	else if (vreg->thread->ctrl->soc_revision == MSM8996PRO_SOC_ID)
+		ref_volt = msm8996pro_mmss_fuse_ref_volt;
+	else
+		ref_volt = msm8996_mmss_fuse_ref_volt;
+
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		volt_init = fuse->init_voltage[i];
+		/*
+		 * Handle partial binning on MSM8998 where the initial voltage
+		 * fuse is reused as a flag for partial binning needs.  Set the
+		 * open-loop voltage to the minimum possible value so that it
+		 * does not result in higher fuse corners getting forced to
+		 * higher open-loop voltages after monotonicity enforcement.
+		 */
+		if (is_msm8998 &&
+		    (volt_init == MSM8998_CPR_PARTIAL_BINNING_NEXT_CORNER ||
+		     volt_init == MSM8998_CPR_PARTIAL_BINNING_SAFE_CORNER) &&
+		    i <= MSM8998_CPR_PARTIAL_BINNING_MAX_FUSE_CORNER)
+			volt_init = MSM8996_MMSS_MIN_VOLTAGE_FUSE_VAL;
+
+		fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(ref_volt[i],
+			MSM8996_MMSS_FUSE_STEP_VOLT, volt_init,
+			MSM8996_MMSS_VOLTAGE_FUSE_SIZE);
+		cpr3_info(vreg, "fuse_corner[%d] open-loop=%7d uV\n",
+			i, fuse_volt[i]);
+	}
+
+	rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt);
+	if (rc) {
+		cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	allow_interpolation = of_property_read_bool(node,
+				"qcom,allow-voltage-interpolation");
+
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		if (fuse_volt[i] < fuse_volt[i - 1]) {
+			cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n",
+				i, fuse_volt[i], i - 1, fuse_volt[i - 1],
+				i, fuse_volt[i - 1]);
+			fuse_volt[i] = fuse_volt[i - 1];
+		}
+	}
+
+	if (fuse->limitation == MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION)
+		allow_interpolation = false;
+
+	if (!allow_interpolation) {
+		/* Use fused open-loop voltage for lower frequencies. */
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].open_loop_volt
+				= fuse_volt[vreg->corner[i].cpr_fuse_corner];
+		goto done;
+	}
+
+	/* Determine highest corner mapped to each fuse corner */
+	j = vreg->fuse_corner_count - 1;
+	for (i = vreg->corner_count - 1; i >= 0; i--) {
+		if (vreg->corner[i].cpr_fuse_corner == j) {
+			fmax_corner[j] = i;
+			j--;
+		}
+	}
+	if (j >= 0) {
+		cpr3_err(vreg, "invalid fuse corner mapping\n");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	for (i = 0; i <= fmax_corner[0]; i++)
+		vreg->corner[i].open_loop_volt = fuse_volt[0];
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		volt_low = fuse_volt[i - 1];
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+		volt_high = fuse_volt[i];
+
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].open_loop_volt = cpr3_interpolate(
+				freq_low, volt_low, freq_high, volt_high,
+				vreg->corner[j].proc_freq);
+	}
+
+done:
+	if (rc == 0) {
+		cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n");
+		for (i = 0; i < vreg->corner_count; i++)
+			cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i,
+				vreg->corner[i].open_loop_volt);
+
+		rc = cpr3_adjust_open_loop_voltages(vreg);
+		if (rc)
+			cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
+				rc);
+	}
+
+	kfree(fuse_volt);
+	kfree(fmax_corner);
+	return rc;
+}
+
+/**
+ * cpr3_msm8998_partial_binning_override() - override the voltage and quotient
+ *		settings for low corners based upon the special partial binning
+ *		open-loop voltage fuse values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Some parts are not able to operate at low voltages.  The partial binning
+ * open-loop voltage fuse values specify if a given part has such limitations.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_msm8998_partial_binning_override(struct cpr3_regulator *vreg)
+{
+	struct cpr3_msm8996_mmss_fuses *fuse = vreg->platform_fuses;
+	u64 next = MSM8998_CPR_PARTIAL_BINNING_NEXT_CORNER;
+	u64 safe = MSM8998_CPR_PARTIAL_BINNING_SAFE_CORNER;
+	u32 proc_freq;
+	struct cpr3_corner *corner;
+	struct cpr3_corner *safe_corner;
+	int i, j, low, high, safe_fuse_corner, max_fuse_corner;
+
+	if (!cpr3_ctrl_is_msm8998(vreg->thread->ctrl))
+		return 0;
+
+	/* Handle the force highest corner fuse. */
+	if (fuse->force_highest_corner) {
+		cpr3_info(vreg, "overriding CPR parameters for corners 0 to %d with quotients and voltages of corner %d\n",
+			vreg->corner_count - 2, vreg->corner_count - 1);
+		corner = &vreg->corner[vreg->corner_count - 1];
+		for (i = 0; i < vreg->corner_count - 1; i++) {
+			proc_freq = vreg->corner[i].proc_freq;
+			vreg->corner[i] = *corner;
+			vreg->corner[i].proc_freq = proc_freq;
+		}
+
+		/*
+		 * Return since the potential partial binning fuse values are
+		 * superceded by the force highest corner fuse value.
+		 */
+		return 0;
+	}
+
+	/*
+	 * Allow up to the max corner which can be fused with partial
+	 * binning values.
+	 */
+	max_fuse_corner = min(MSM8998_CPR_PARTIAL_BINNING_MAX_FUSE_CORNER,
+				vreg->fuse_corner_count - 2);
+
+	for (i = 0; i <= max_fuse_corner; i++) {
+		/* Determine which higher corners to override with (if any). */
+		if (fuse->init_voltage[i] != next
+		    && fuse->init_voltage[i] != safe)
+			continue;
+
+		for (j = i + 1; j <= max_fuse_corner; j++)
+			if (fuse->init_voltage[j] != next
+			    && fuse->init_voltage[j] != safe)
+				break;
+		safe_fuse_corner = j;
+
+		j = fuse->init_voltage[i] == next ? i + 1 : safe_fuse_corner;
+
+		low = i > 0 ? vreg->fuse_corner_map[i] : 0;
+		high = vreg->fuse_corner_map[i + 1] - 1;
+
+		cpr3_info(vreg, "overriding CPR parameters for corners %d to %d with quotients of corner %d and voltages of corner %d\n",
+			low, high, vreg->fuse_corner_map[j],
+			vreg->fuse_corner_map[safe_fuse_corner]);
+
+		corner = &vreg->corner[vreg->fuse_corner_map[j]];
+		safe_corner
+		       = &vreg->corner[vreg->fuse_corner_map[safe_fuse_corner]];
+
+		for (j = low; j <= high; j++) {
+			proc_freq = vreg->corner[j].proc_freq;
+			vreg->corner[j] = *corner;
+			vreg->corner[j].proc_freq = proc_freq;
+
+			vreg->corner[j].floor_volt
+				= safe_corner->floor_volt;
+			vreg->corner[j].ceiling_volt
+				= safe_corner->ceiling_volt;
+			vreg->corner[j].open_loop_volt
+				= safe_corner->open_loop_volt;
+			vreg->corner[j].abs_ceiling_volt
+				= safe_corner->abs_ceiling_volt;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_mmss_print_settings() - print out MMSS CPR configuration settings into
+ *		the kernel log for debugging purposes
+ * @vreg:		Pointer to the CPR3 regulator
+ */
+static void cpr3_mmss_print_settings(struct cpr3_regulator *vreg)
+{
+	struct cpr3_corner *corner;
+	int i;
+
+	cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n");
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n",
+			i, corner->proc_freq, corner->cpr_fuse_corner,
+			corner->floor_volt, corner->open_loop_volt,
+			corner->ceiling_volt);
+	}
+}
+
+/**
+ * cpr3_mmss_init_aging() - perform MMSS CPR3 controller specific
+ *		aging initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_mmss_init_aging(struct cpr3_controller *ctrl)
+{
+	struct cpr3_msm8996_mmss_fuses *fuse;
+	struct cpr3_regulator *vreg;
+	u32 aging_ro_scale;
+	int rc;
+
+	vreg = &ctrl->thread[0].vreg[0];
+
+	ctrl->aging_required = vreg->aging_allowed;
+	fuse = vreg->platform_fuses;
+
+	if (!ctrl->aging_required || !fuse)
+		return 0;
+
+	rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
+			1, &aging_ro_scale);
+	if (rc)
+		return rc;
+
+	if (aging_ro_scale == 0) {
+		cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
+			aging_ro_scale);
+		return -EINVAL;
+	}
+
+	ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
+	ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
+
+	ctrl->aging_sensor_count = 1;
+	ctrl->aging_sensor = kzalloc(sizeof(*ctrl->aging_sensor), GFP_KERNEL);
+	if (!ctrl->aging_sensor)
+		return -ENOMEM;
+
+	ctrl->aging_sensor->ro_scale = aging_ro_scale;
+
+	if (cpr3_ctrl_is_msm8998(ctrl)) {
+		ctrl->aging_sensor->sensor_id = MSM8998_MMSS_AGING_SENSOR_ID;
+		ctrl->aging_sensor->bypass_mask[0]
+					= MSM8998_MMSS_AGING_BYPASS_MASK0;
+		ctrl->aging_sensor->init_quot_diff
+			= cpr3_convert_open_loop_voltage_fuse(0,
+				MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SCALE,
+				fuse->aging_init_quot_diff,
+				MSM8998_MMSS_AGING_INIT_QUOT_DIFF_SIZE);
+	} else {
+		ctrl->aging_sensor->sensor_id = MSM8996_MMSS_AGING_SENSOR_ID;
+		ctrl->aging_sensor->bypass_mask[0]
+					= MSM8996_MMSS_AGING_BYPASS_MASK0;
+		ctrl->aging_sensor->init_quot_diff
+			= cpr3_convert_open_loop_voltage_fuse(0,
+				MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SCALE,
+				fuse->aging_init_quot_diff,
+				MSM8996_MMSS_AGING_INIT_QUOT_DIFF_SIZE);
+	}
+
+	cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
+		ctrl->aging_sensor->sensor_id,
+		ctrl->aging_sensor->init_quot_diff,
+		ctrl->aging_sensor->ro_scale);
+
+	return 0;
+}
+
+/**
+ * cpr3_mmss_init_thread() - perform all steps necessary to initialize the
+ *		configuration data for a CPR3 thread
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_mmss_init_thread(struct cpr3_thread *thread)
+{
+	struct cpr3_regulator *vreg = &thread->vreg[0];
+	struct cpr3_msm8996_mmss_fuses *fuse;
+	int rc;
+
+	rc = cpr3_parse_common_thread_data(thread);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR thread data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_msm8996_mmss_read_fuse_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc);
+		return rc;
+	}
+
+	fuse = vreg->platform_fuses;
+	if (fuse->limitation == MSM8996_CPR_LIMITATION_UNSUPPORTED) {
+		cpr3_err(vreg, "this chip requires an unsupported voltage\n");
+		return -EPERM;
+	} else if (fuse->limitation
+			== MSM8996_CPR_LIMITATION_NO_CPR_OR_INTERPOLATION) {
+		thread->ctrl->cpr_allowed_hw = false;
+	}
+
+	rc = cpr3_mmss_parse_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_msm8996_mmss_adjust_target_quotients(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to adjust target quotients, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_msm8996_mmss_calculate_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_limit_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_open_loop_voltage_as_ceiling(vreg);
+
+	rc = cpr3_limit_floor_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (cpr3_ctrl_is_msm8998(thread->ctrl)) {
+		rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false);
+		if (rc) {
+			cpr3_err(vreg, "unable to parse temperature based voltage adjustments, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	rc = cpr3_msm8998_partial_binning_override(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to override CPR parameters based on partial binning fuse values, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_mmss_print_settings(vreg);
+
+	return 0;
+}
+
+/**
+ * cpr4_mmss_parse_temp_adj_properties() - parse temperature based
+ *		adjustment properties from device tree
+ * @ctrl:	Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_mmss_parse_temp_adj_properties(struct cpr3_controller *ctrl)
+{
+	struct device_node *of_node = ctrl->dev->of_node;
+	int rc, len, temp_point_count;
+
+	if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len))
+		return 0;
+
+	temp_point_count = len / sizeof(u32);
+	if (temp_point_count <= 0
+	    || temp_point_count > MSM8998_MMSS_MAX_TEMP_POINTS) {
+		cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n",
+			 temp_point_count, MSM8998_MMSS_MAX_TEMP_POINTS);
+		return -EINVAL;
+	}
+
+	ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count,
+					sizeof(*ctrl->temp_points), GFP_KERNEL);
+	if (!ctrl->temp_points)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map",
+					ctrl->temp_points, temp_point_count);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	/*
+	 * If t1, t2, and t3 are the temperature points, then the temperature
+	 * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).
+	 */
+	ctrl->temp_band_count = temp_point_count + 1;
+
+	rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band",
+				  &ctrl->initial_temp_band);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->initial_temp_band >= ctrl->temp_band_count) {
+		cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n",
+			ctrl->initial_temp_band, ctrl->temp_band_count - 1);
+		return -EINVAL;
+	}
+
+	ctrl->temp_sensor_id_start = MSM8998_MMSS_TEMP_SENSOR_ID_START;
+	ctrl->temp_sensor_id_end = MSM8998_MMSS_TEMP_SENSOR_ID_END;
+	ctrl->allow_temp_adj = true;
+
+	return rc;
+}
+
+/**
+ * cpr3_mmss_init_controller() - perform MMSS CPR3 controller specific
+ *		initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_mmss_init_controller(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	rc = cpr3_parse_common_ctrl_data(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	if (cpr3_ctrl_is_msm8998(ctrl)) {
+		rc = cpr4_mmss_parse_temp_adj_properties(ctrl);
+		if (rc)
+			return rc;
+	}
+
+	ctrl->sensor_count = cpr3_ctrl_is_msm8998(ctrl)
+				? MSM8998_MMSS_CPR_SENSOR_COUNT
+				: MSM8996_MMSS_CPR_SENSOR_COUNT;
+
+	/*
+	 * MMSS only has one thread (0) so the zeroed array does not need
+	 * further modification.
+	 */
+	ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count,
+				sizeof(*ctrl->sensor_owner), GFP_KERNEL);
+	if (!ctrl->sensor_owner)
+		return -ENOMEM;
+
+	ctrl->cpr_clock_rate = MSM8996_MMSS_CPR_CLOCK_RATE;
+	ctrl->ctrl_type = cpr3_ctrl_is_msm8998(ctrl)
+				? CPR_CTRL_TYPE_CPR4 : CPR_CTRL_TYPE_CPR3;
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		/*
+		 * Use fixed step quotient if specified otherwise use dynamic
+		 * calculated per RO step quotient
+		 */
+		of_property_read_u32(ctrl->dev->of_node,
+				     "qcom,cpr-step-quot-fixed",
+				     &ctrl->step_quot_fixed);
+		ctrl->use_dynamic_step_quot = !ctrl->step_quot_fixed;
+	}
+
+	ctrl->iface_clk = devm_clk_get(ctrl->dev, "iface_clk");
+	if (IS_ERR(ctrl->iface_clk)) {
+		rc = PTR_ERR(ctrl->iface_clk);
+		if (cpr3_ctrl_is_msm8998(ctrl)) {
+			/* iface_clk is optional for msm8998 */
+			ctrl->iface_clk = NULL;
+		} else if (rc == -EPROBE_DEFER) {
+			return rc;
+		} else {
+			cpr3_err(ctrl, "unable to request interface clock, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	ctrl->bus_clk = devm_clk_get(ctrl->dev, "bus_clk");
+	if (IS_ERR(ctrl->bus_clk)) {
+		rc = PTR_ERR(ctrl->bus_clk);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable request bus clock, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int cpr3_mmss_regulator_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_suspend(ctrl);
+}
+
+static int cpr3_mmss_regulator_resume(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_resume(ctrl);
+}
+
+/* Data corresponds to the SoC revision */
+static const struct of_device_id cpr_regulator_match_table[] = {
+	{
+		.compatible = "qcom,cpr3-msm8996-v1-mmss-regulator",
+		.data = (void *)(uintptr_t)1,
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996-v2-mmss-regulator",
+		.data = (void *)(uintptr_t)2,
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996-v3-mmss-regulator",
+		.data = (void *)(uintptr_t)3,
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996-mmss-regulator",
+		.data = (void *)(uintptr_t)3,
+	},
+	{
+		.compatible = "qcom,cpr3-msm8996pro-mmss-regulator",
+		.data = (void *)(uintptr_t)MSM8996PRO_SOC_ID,
+	},
+	{
+		.compatible = "qcom,cpr4-msm8998-v1-mmss-regulator",
+		.data = (void *)(uintptr_t)MSM8998_V1_SOC_ID,
+	},
+	{
+		.compatible = "qcom,cpr4-msm8998-v2-mmss-regulator",
+		.data = (void *)(uintptr_t)MSM8998_V2_SOC_ID,
+	},
+	{
+		.compatible = "qcom,cpr4-msm8998-mmss-regulator",
+		.data = (void *)(uintptr_t)MSM8998_V2_SOC_ID,
+	},
+	{}
+};
+
+static int cpr3_mmss_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cpr3_controller *ctrl;
+	int rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	/* Set to false later if anything precludes CPR operation. */
+	ctrl->cpr_allowed_hw = true;
+
+	rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name",
+					&ctrl->name);
+	if (rc) {
+		cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	match = of_match_node(cpr_regulator_match_table, dev->of_node);
+	if (match)
+		ctrl->soc_revision = (uintptr_t)match->data;
+	else
+		cpr3_err(ctrl, "could not find compatible string match\n");
+
+	rc = cpr3_map_fuse_base(ctrl, pdev);
+	if (rc) {
+		cpr3_err(ctrl, "could not map fuse base address\n");
+		return rc;
+	}
+
+	rc = cpr3_allocate_threads(ctrl, 0, 0);
+	if (rc) {
+		cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->thread_count != 1) {
+		cpr3_err(ctrl, "expected 1 thread but found %d\n",
+			ctrl->thread_count);
+		return -EINVAL;
+	} else if (ctrl->thread[0].vreg_count != 1) {
+		cpr3_err(ctrl, "expected 1 regulator but found %d\n",
+			ctrl->thread[0].vreg_count);
+		return -EINVAL;
+	}
+
+	rc = cpr3_mmss_init_controller(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = cpr3_mmss_init_thread(&ctrl->thread[0]);
+	if (rc) {
+		cpr3_err(&ctrl->thread[0].vreg[0], "thread initialization failed, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_mem_acc_init(&ctrl->thread[0].vreg[0]);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize mem-acc configuration, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	rc = cpr3_mmss_init_aging(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	platform_set_drvdata(pdev, ctrl);
+
+	return cpr3_regulator_register(pdev, ctrl);
+}
+
+static int cpr3_mmss_regulator_remove(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_unregister(ctrl);
+}
+
+static struct platform_driver cpr3_mmss_regulator_driver = {
+	.driver		= {
+		.name		= "qcom,cpr3-mmss-regulator",
+		.of_match_table	= cpr_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= cpr3_mmss_regulator_probe,
+	.remove		= cpr3_mmss_regulator_remove,
+	.suspend	= cpr3_mmss_regulator_suspend,
+	.resume		= cpr3_mmss_regulator_resume,
+};
+
+static int cpr_regulator_init(void)
+{
+	return platform_driver_register(&cpr3_mmss_regulator_driver);
+}
+
+static void cpr_regulator_exit(void)
+{
+	platform_driver_unregister(&cpr3_mmss_regulator_driver);
+}
+
+MODULE_DESCRIPTION("CPR3 MMSS regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(cpr_regulator_init);
+module_exit(cpr_regulator_exit);
diff --git a/drivers/regulator/cpr3-regulator.c b/drivers/regulator/cpr3-regulator.c
new file mode 100644
index 0000000..c51ed182b
--- /dev/null
+++ b/drivers/regulator/cpr3-regulator.c
@@ -0,0 +1,6492 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/ktime.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/msm-ldo-regulator.h>
+
+#include <soc/qcom/spm.h>
+
+#include "cpr3-regulator.h"
+
+#define CPR3_REGULATOR_CORNER_INVALID	(-1)
+#define CPR3_RO_MASK			GENMASK(CPR3_RO_COUNT - 1, 0)
+
+/* CPR3 registers */
+#define CPR3_REG_CPR_CTL			0x4
+#define CPR3_CPR_CTL_LOOP_EN_MASK		BIT(0)
+#define CPR3_CPR_CTL_LOOP_ENABLE		BIT(0)
+#define CPR3_CPR_CTL_LOOP_DISABLE		0
+#define CPR3_CPR_CTL_IDLE_CLOCKS_MASK		GENMASK(5, 1)
+#define CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT		1
+#define CPR3_CPR_CTL_COUNT_MODE_MASK		GENMASK(7, 6)
+#define CPR3_CPR_CTL_COUNT_MODE_SHIFT		6
+#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN	0
+#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MAX	1
+#define CPR3_CPR_CTL_COUNT_MODE_STAGGERED	2
+#define CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE	3
+#define CPR3_CPR_CTL_COUNT_REPEAT_MASK		GENMASK(31, 9)
+#define CPR3_CPR_CTL_COUNT_REPEAT_SHIFT		9
+
+#define CPR3_REG_CPR_STATUS			0x8
+#define CPR3_CPR_STATUS_BUSY_MASK		BIT(0)
+#define CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK	BIT(1)
+
+/*
+ * This register is not present on controllers that support HW closed-loop
+ * except CPR4 APSS controller.
+ */
+#define CPR3_REG_CPR_TIMER_AUTO_CONT		0xC
+
+#define CPR3_REG_CPR_STEP_QUOT			0x14
+#define CPR3_CPR_STEP_QUOT_MIN_MASK		GENMASK(5, 0)
+#define CPR3_CPR_STEP_QUOT_MIN_SHIFT		0
+#define CPR3_CPR_STEP_QUOT_MAX_MASK		GENMASK(11, 6)
+#define CPR3_CPR_STEP_QUOT_MAX_SHIFT		6
+
+#define CPR3_REG_GCNT(ro)			(0xA0 + 0x4 * (ro))
+
+#define CPR3_REG_SENSOR_BYPASS_WRITE(sensor)	(0xE0 + 0x4 * ((sensor) / 32))
+#define CPR3_REG_SENSOR_BYPASS_WRITE_BANK(bank)	(0xE0 + 0x4 * (bank))
+
+#define CPR3_REG_SENSOR_MASK_WRITE(sensor)	(0x120 + 0x4 * ((sensor) / 32))
+#define CPR3_REG_SENSOR_MASK_WRITE_BANK(bank)	(0x120 + 0x4 * (bank))
+#define CPR3_REG_SENSOR_MASK_READ(sensor)	(0x140 + 0x4 * ((sensor) / 32))
+
+#define CPR3_REG_SENSOR_OWNER(sensor)	(0x200 + 0x4 * (sensor))
+
+#define CPR3_REG_CONT_CMD		0x800
+#define CPR3_CONT_CMD_ACK		0x1
+#define CPR3_CONT_CMD_NACK		0x0
+
+#define CPR3_REG_THRESH(thread)		(0x808 + 0x440 * (thread))
+#define CPR3_THRESH_CONS_DOWN_MASK	GENMASK(3, 0)
+#define CPR3_THRESH_CONS_DOWN_SHIFT	0
+#define CPR3_THRESH_CONS_UP_MASK	GENMASK(7, 4)
+#define CPR3_THRESH_CONS_UP_SHIFT	4
+#define CPR3_THRESH_DOWN_THRESH_MASK	GENMASK(12, 8)
+#define CPR3_THRESH_DOWN_THRESH_SHIFT	8
+#define CPR3_THRESH_UP_THRESH_MASK	GENMASK(17, 13)
+#define CPR3_THRESH_UP_THRESH_SHIFT	13
+
+#define CPR3_REG_RO_MASK(thread)	(0x80C + 0x440 * (thread))
+
+#define CPR3_REG_RESULT0(thread)	(0x810 + 0x440 * (thread))
+#define CPR3_RESULT0_BUSY_MASK		BIT(0)
+#define CPR3_RESULT0_STEP_DN_MASK	BIT(1)
+#define CPR3_RESULT0_STEP_UP_MASK	BIT(2)
+#define CPR3_RESULT0_ERROR_STEPS_MASK	GENMASK(7, 3)
+#define CPR3_RESULT0_ERROR_STEPS_SHIFT	3
+#define CPR3_RESULT0_ERROR_MASK		GENMASK(19, 8)
+#define CPR3_RESULT0_ERROR_SHIFT	8
+#define CPR3_RESULT0_NEGATIVE_MASK	BIT(20)
+
+#define CPR3_REG_RESULT1(thread)	(0x814 + 0x440 * (thread))
+#define CPR3_RESULT1_QUOT_MIN_MASK	GENMASK(11, 0)
+#define CPR3_RESULT1_QUOT_MIN_SHIFT	0
+#define CPR3_RESULT1_QUOT_MAX_MASK	GENMASK(23, 12)
+#define CPR3_RESULT1_QUOT_MAX_SHIFT	12
+#define CPR3_RESULT1_RO_MIN_MASK	GENMASK(27, 24)
+#define CPR3_RESULT1_RO_MIN_SHIFT	24
+#define CPR3_RESULT1_RO_MAX_MASK	GENMASK(31, 28)
+#define CPR3_RESULT1_RO_MAX_SHIFT	28
+
+#define CPR3_REG_RESULT2(thread)		(0x818 + 0x440 * (thread))
+#define CPR3_RESULT2_STEP_QUOT_MIN_MASK		GENMASK(5, 0)
+#define CPR3_RESULT2_STEP_QUOT_MIN_SHIFT	0
+#define CPR3_RESULT2_STEP_QUOT_MAX_MASK		GENMASK(11, 6)
+#define CPR3_RESULT2_STEP_QUOT_MAX_SHIFT	6
+#define CPR3_RESULT2_SENSOR_MIN_MASK		GENMASK(23, 16)
+#define CPR3_RESULT2_SENSOR_MIN_SHIFT		16
+#define CPR3_RESULT2_SENSOR_MAX_MASK		GENMASK(31, 24)
+#define CPR3_RESULT2_SENSOR_MAX_SHIFT		24
+
+#define CPR3_REG_IRQ_EN			0x81C
+#define CPR3_REG_IRQ_CLEAR		0x820
+#define CPR3_REG_IRQ_STATUS		0x824
+#define CPR3_IRQ_UP			BIT(3)
+#define CPR3_IRQ_MID			BIT(2)
+#define CPR3_IRQ_DOWN			BIT(1)
+
+#define CPR3_REG_TARGET_QUOT(thread, ro) \
+					(0x840 + 0x440 * (thread) + 0x4 * (ro))
+
+/* Registers found only on controllers that support HW closed-loop. */
+#define CPR3_REG_PD_THROTTLE		0xE8
+#define CPR3_PD_THROTTLE_DISABLE	0x0
+
+#define CPR3_REG_HW_CLOSED_LOOP		0x3000
+#define CPR3_HW_CLOSED_LOOP_ENABLE	0x0
+#define CPR3_HW_CLOSED_LOOP_DISABLE	0x1
+
+#define CPR3_REG_CPR_TIMER_MID_CONT	0x3004
+#define CPR3_REG_CPR_TIMER_UP_DN_CONT	0x3008
+
+#define CPR3_REG_LAST_MEASUREMENT		0x7F8
+#define CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT	0
+#define CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT	4
+#define CPR3_LAST_MEASUREMENT_THREAD_DN(thread) \
+		(BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_DN_SHIFT)
+#define CPR3_LAST_MEASUREMENT_THREAD_UP(thread) \
+		(BIT(thread) << CPR3_LAST_MEASUREMENT_THREAD_UP_SHIFT)
+#define CPR3_LAST_MEASUREMENT_AGGR_DN		BIT(8)
+#define CPR3_LAST_MEASUREMENT_AGGR_MID		BIT(9)
+#define CPR3_LAST_MEASUREMENT_AGGR_UP		BIT(10)
+#define CPR3_LAST_MEASUREMENT_VALID		BIT(11)
+#define CPR3_LAST_MEASUREMENT_SAW_ERROR		BIT(12)
+#define CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK	GENMASK(23, 16)
+#define CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT	16
+
+/* CPR4 controller specific registers and bit definitions */
+#define CPR4_REG_CPR_TIMER_CLAMP			0x10
+#define CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN	BIT(27)
+
+#define CPR4_REG_MISC				0x700
+#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK	GENMASK(23, 20)
+#define CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT	20
+#define CPR4_MISC_TEMP_SENSOR_ID_START_MASK	GENMASK(27, 24)
+#define CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT	24
+#define CPR4_MISC_TEMP_SENSOR_ID_END_MASK	GENMASK(31, 28)
+#define CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT	28
+
+#define CPR4_REG_SAW_ERROR_STEP_LIMIT		0x7A4
+#define CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK	GENMASK(4, 0)
+#define CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT	0
+#define CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK	GENMASK(9, 5)
+#define CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT	5
+
+#define CPR4_REG_MARGIN_TEMP_CORE_TIMERS			0x7A8
+#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK	GENMASK(28, 18)
+#define CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT	18
+
+#define CPR4_REG_MARGIN_TEMP_CORE(core)		(0x7AC + 0x4 * (core))
+#define CPR4_MARGIN_TEMP_CORE_ADJ_MASK		GENMASK(7, 0)
+#define CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT		8
+
+#define CPR4_REG_MARGIN_TEMP_POINT0N1		0x7F0
+#define CPR4_MARGIN_TEMP_POINT0_MASK		GENMASK(11, 0)
+#define CPR4_MARGIN_TEMP_POINT0_SHIFT		0
+#define CPR4_MARGIN_TEMP_POINT1_MASK		GENMASK(23, 12)
+#define CPR4_MARGIN_TEMP_POINT1_SHIFT		12
+#define CPR4_REG_MARGIN_TEMP_POINT2		0x7F4
+#define CPR4_MARGIN_TEMP_POINT2_MASK		GENMASK(11, 0)
+#define CPR4_MARGIN_TEMP_POINT2_SHIFT		0
+
+#define CPR4_REG_MARGIN_ADJ_CTL					0x7F8
+#define CPR4_MARGIN_ADJ_CTL_BOOST_EN				BIT(0)
+#define CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN				BIT(1)
+#define CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN				BIT(2)
+#define CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN		BIT(3)
+#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK		BIT(4)
+#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE		BIT(4)
+#define CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE		0
+#define CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN			BIT(7)
+#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN			BIT(8)
+#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK			GENMASK(16, 12)
+#define CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT		12
+#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK		GENMASK(21, 19)
+#define CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT		19
+#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK			GENMASK(25, 22)
+#define CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT			22
+#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK	GENMASK(31, 26)
+#define CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT	26
+
+#define CPR4_REG_CPR_MASK_THREAD(thread)	(0x80C + 0x440 * (thread))
+#define CPR4_CPR_MASK_THREAD_DISABLE_THREAD		BIT(31)
+#define CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK	GENMASK(15, 0)
+
+/* CPRh controller specific registers and bit definitions */
+#define CPRH_REG_CORNER(corner)	(0x3A00 + 0x4 * (corner))
+#define CPRH_CORNER_INIT_VOLTAGE_MASK		GENMASK(7, 0)
+#define CPRH_CORNER_INIT_VOLTAGE_SHIFT		0
+#define CPRH_CORNER_FLOOR_VOLTAGE_MASK		GENMASK(15, 8)
+#define CPRH_CORNER_FLOOR_VOLTAGE_SHIFT		8
+#define CPRH_CORNER_QUOT_DELTA_MASK		GENMASK(24, 16)
+#define CPRH_CORNER_QUOT_DELTA_SHIFT		16
+#define CPRH_CORNER_RO_SEL_MASK			GENMASK(28, 25)
+#define CPRH_CORNER_RO_SEL_SHIFT		25
+#define CPRH_CORNER_CPR_CL_DISABLE	BIT(29)
+#define CPRH_CORNER_CORE_TEMP_MARGIN_DISABLE	BIT(30)
+#define CPRH_CORNER_LAST_KNOWN_VOLTAGE_ENABLE	BIT(31)
+#define CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE	255
+#define CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE	255
+#define CPRH_CORNER_QUOT_DELTA_MAX_VALUE	511
+
+#define CPRH_REG_CTL				0x3AA0
+#define CPRH_CTL_OSM_ENABLED			BIT(0)
+#define CPRH_CTL_BASE_VOLTAGE_MASK		GENMASK(10, 1)
+#define CPRH_CTL_BASE_VOLTAGE_SHIFT		1
+#define CPRH_CTL_INIT_MODE_MASK		GENMASK(16, 11)
+#define CPRH_CTL_INIT_MODE_SHIFT		11
+#define CPRH_CTL_MODE_SWITCH_DELAY_MASK		GENMASK(24, 17)
+#define CPRH_CTL_MODE_SWITCH_DELAY_SHIFT	17
+#define CPRH_CTL_VOLTAGE_MULTIPLIER_MASK	GENMASK(28, 25)
+#define CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT	25
+#define CPRH_CTL_LAST_KNOWN_VOLTAGE_MARGIN_MASK		GENMASK(31, 29)
+#define CPRH_CTL_LAST_KNOWN_VOLTAGE_MARGIN_SHIFT	29
+
+#define CPRH_REG_STATUS			0x3AA4
+#define CPRH_STATUS_CORNER			GENMASK(5, 0)
+#define CPRH_STATUS_CORNER_LAST_VOLT_MASK	GENMASK(17, 6)
+#define CPRH_STATUS_CORNER_LAST_VOLT_SHIFT	6
+
+#define CPRH_REG_CORNER_BAND	0x3AA8
+#define CPRH_CORNER_BAND_MASK		GENMASK(5, 0)
+#define CPRH_CORNER_BAND_SHIFT		6
+#define CPRH_CORNER_BAND_MAX_COUNT		4
+
+#define CPRH_MARGIN_TEMP_CORE_VBAND(core, vband) \
+	((vband) == 0 ? CPR4_REG_MARGIN_TEMP_CORE(core) \
+			: 0x3AB0 + 0x40 * ((vband) - 1) + 0x4 * (core))
+
+/*
+ * The amount of time to wait for the CPR controller to become idle when
+ * performing an aging measurement.
+ */
+#define CPR3_AGING_MEASUREMENT_TIMEOUT_NS	5000000
+
+/*
+ * The number of individual aging measurements to perform which are then
+ * averaged together in order to determine the final aging adjustment value.
+ */
+#define CPR3_AGING_MEASUREMENT_ITERATIONS	16
+
+/*
+ * Aging measurements for the aged and unaged ring oscillators take place a few
+ * microseconds apart.  If the vdd-supply voltage fluctuates between the two
+ * measurements, then the difference between them will be incorrect.  The
+ * difference could end up too high or too low.  This constant defines the
+ * number of lowest and highest measurements to ignore when averaging.
+ */
+#define CPR3_AGING_MEASUREMENT_FILTER		3
+
+/*
+ * The number of times to attempt the full aging measurement sequence before
+ * declaring a measurement failure.
+ */
+#define CPR3_AGING_RETRY_COUNT			5
+
+/*
+ * The maximum time to wait in microseconds for a CPR register write to
+ * complete.
+ */
+#define CPR3_REGISTER_WRITE_DELAY_US		200
+
+/*
+ * The number of times the CPRh controller multiplies the mode switch
+ * delay before utilizing it.
+ */
+#define CPRH_MODE_SWITCH_DELAY_FACTOR 4
+
+/*
+ * The number of times the CPRh controller multiplies the delta quotient
+ * steps before utilizing it.
+ */
+#define CPRH_DELTA_QUOT_STEP_FACTOR 4
+
+static DEFINE_MUTEX(cpr3_controller_list_mutex);
+static LIST_HEAD(cpr3_controller_list);
+static struct dentry *cpr3_debugfs_base;
+
+/**
+ * cpr3_read() - read four bytes from the memory address specified
+ * @ctrl:		Pointer to the CPR3 controller
+ * @offset:		Offset in bytes from the CPR3 controller's base address
+ *
+ * Return: memory address value
+ */
+static inline u32 cpr3_read(struct cpr3_controller *ctrl, u32 offset)
+{
+	if (!ctrl->cpr_enabled) {
+		cpr3_err(ctrl, "CPR register reads are not possible when CPR clocks are disabled\n");
+		return 0;
+	}
+
+	return readl_relaxed(ctrl->cpr_ctrl_base + offset);
+}
+
+/**
+ * cpr3_write() - write four bytes to the memory address specified
+ * @ctrl:		Pointer to the CPR3 controller
+ * @offset:		Offset in bytes from the CPR3 controller's base address
+ * @value:		Value to write to the memory address
+ *
+ * Return: none
+ */
+static inline void cpr3_write(struct cpr3_controller *ctrl, u32 offset,
+				u32 value)
+{
+	if (!ctrl->cpr_enabled) {
+		cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n");
+		return;
+	}
+
+	writel_relaxed(value, ctrl->cpr_ctrl_base + offset);
+}
+
+/**
+ * cpr3_masked_write() - perform a read-modify-write sequence so that only
+ *		masked bits are modified
+ * @ctrl:		Pointer to the CPR3 controller
+ * @offset:		Offset in bytes from the CPR3 controller's base address
+ * @mask:		Mask identifying the bits that should be modified
+ * @value:		Value to write to the memory address
+ *
+ * Return: none
+ */
+static inline void cpr3_masked_write(struct cpr3_controller *ctrl, u32 offset,
+				u32 mask, u32 value)
+{
+	u32 reg_val, orig_val;
+
+	if (!ctrl->cpr_enabled) {
+		cpr3_err(ctrl, "CPR register writes are not possible when CPR clocks are disabled\n");
+		return;
+	}
+
+	reg_val = orig_val = readl_relaxed(ctrl->cpr_ctrl_base + offset);
+	reg_val &= ~mask;
+	reg_val |= value & mask;
+
+	if (reg_val != orig_val)
+		writel_relaxed(reg_val, ctrl->cpr_ctrl_base + offset);
+}
+
+/**
+ * cpr3_ctrl_loop_enable() - enable the CPR sensing loop for a given controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: none
+ */
+static inline void cpr3_ctrl_loop_enable(struct cpr3_controller *ctrl)
+{
+	if (ctrl->cpr_enabled && !(ctrl->aggr_corner.sdelta
+		&& ctrl->aggr_corner.sdelta->allow_boost))
+		cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
+			CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_ENABLE);
+}
+
+/**
+ * cpr3_ctrl_loop_disable() - disable the CPR sensing loop for a given
+ *		controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: none
+ */
+static inline void cpr3_ctrl_loop_disable(struct cpr3_controller *ctrl)
+{
+	if (ctrl->cpr_enabled)
+		cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
+			CPR3_CPR_CTL_LOOP_EN_MASK, CPR3_CPR_CTL_LOOP_DISABLE);
+}
+
+/**
+ * cpr3_clock_enable() - prepare and enable all clocks used by this CPR3
+ *		controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_clock_enable(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	rc = clk_prepare_enable(ctrl->bus_clk);
+	if (rc) {
+		cpr3_err(ctrl, "failed to enable bus clock, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = clk_prepare_enable(ctrl->iface_clk);
+	if (rc) {
+		cpr3_err(ctrl, "failed to enable interface clock, rc=%d\n", rc);
+		clk_disable_unprepare(ctrl->bus_clk);
+		return rc;
+	}
+
+	rc = clk_prepare_enable(ctrl->core_clk);
+	if (rc) {
+		cpr3_err(ctrl, "failed to enable core clock, rc=%d\n", rc);
+		clk_disable_unprepare(ctrl->iface_clk);
+		clk_disable_unprepare(ctrl->bus_clk);
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_clock_disable() - disable and unprepare all clocks used by this CPR3
+ *		controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: none
+ */
+static void cpr3_clock_disable(struct cpr3_controller *ctrl)
+{
+	clk_disable_unprepare(ctrl->core_clk);
+	clk_disable_unprepare(ctrl->iface_clk);
+	clk_disable_unprepare(ctrl->bus_clk);
+}
+
+/**
+ * cpr3_ctrl_clear_cpr4_config() - clear the CPR4 register configuration
+ *		programmed for current aggregated corner of a given controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static inline int cpr3_ctrl_clear_cpr4_config(struct cpr3_controller *ctrl)
+{
+	struct cpr4_sdelta *aggr_sdelta = ctrl->aggr_corner.sdelta;
+	bool cpr_enabled = ctrl->cpr_enabled;
+	int i, rc = 0;
+
+	if (!aggr_sdelta || !(aggr_sdelta->allow_core_count_adj
+		|| aggr_sdelta->allow_temp_adj || aggr_sdelta->allow_boost))
+		/* cpr4 features are not enabled */
+		return 0;
+
+	/* Ensure that CPR clocks are enabled before writing to registers. */
+	if (!cpr_enabled) {
+		rc = cpr3_clock_enable(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
+			return rc;
+		}
+		ctrl->cpr_enabled = true;
+	}
+
+	/*
+	 * Clear feature enable configuration made for current
+	 * aggregated corner.
+	 */
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+		CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
+		| CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_BOOST_EN
+		| CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK, 0);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MISC,
+			CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
+			0 << CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);
+
+	for (i = 0; i <= aggr_sdelta->max_core_count; i++) {
+		/* Clear voltage margin adjustments programmed in TEMP_COREi */
+		cpr3_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE(i), 0);
+	}
+
+	/* Turn off CPR clocks if they were off before this function call. */
+	if (!cpr_enabled) {
+		cpr3_clock_disable(ctrl);
+		ctrl->cpr_enabled = false;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_closed_loop_enable() - enable logical CPR closed-loop operation
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_closed_loop_enable(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	if (!ctrl->cpr_allowed_hw || !ctrl->cpr_allowed_sw) {
+		cpr3_err(ctrl, "cannot enable closed-loop CPR operation because it is disallowed\n");
+		return -EPERM;
+	} else if (ctrl->cpr_enabled) {
+		/* Already enabled */
+		return 0;
+	} else if (ctrl->cpr_suspended) {
+		/*
+		 * CPR must remain disabled as the system is entering suspend.
+		 */
+		return 0;
+	}
+
+	rc = cpr3_clock_enable(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "unable to enable CPR clocks, rc=%d\n", rc);
+		return rc;
+	}
+
+	ctrl->cpr_enabled = true;
+	cpr3_debug(ctrl, "CPR closed-loop operation enabled\n");
+
+	return 0;
+}
+
+/**
+ * cpr3_closed_loop_disable() - disable logical CPR closed-loop operation
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static inline int cpr3_closed_loop_disable(struct cpr3_controller *ctrl)
+{
+	if (!ctrl->cpr_enabled) {
+		/* Already disabled */
+		return 0;
+	}
+
+	cpr3_clock_disable(ctrl);
+	ctrl->cpr_enabled = false;
+	cpr3_debug(ctrl, "CPR closed-loop operation disabled\n");
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_get_gcnt() - returns the GCNT register value corresponding
+ *		to the clock rate and sensor time of the CPR3 controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: GCNT value
+ */
+static u32 cpr3_regulator_get_gcnt(struct cpr3_controller *ctrl)
+{
+	u64 temp;
+	unsigned int remainder;
+	u32 gcnt;
+
+	temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->sensor_time;
+	remainder = do_div(temp, 1000000000);
+	if (remainder)
+		temp++;
+	/*
+	 * GCNT == 0 corresponds to a single ref clock measurement interval so
+	 * offset GCNT values by 1.
+	 */
+	gcnt = temp - 1;
+
+	return gcnt;
+}
+
+/**
+ * cpr3_regulator_init_thread() - performs hardware initialization of CPR
+ *		thread registers
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_thread(struct cpr3_thread *thread)
+{
+	u32 reg;
+
+	reg = (thread->consecutive_up << CPR3_THRESH_CONS_UP_SHIFT)
+		& CPR3_THRESH_CONS_UP_MASK;
+	reg |= (thread->consecutive_down << CPR3_THRESH_CONS_DOWN_SHIFT)
+		& CPR3_THRESH_CONS_DOWN_MASK;
+	reg |= (thread->up_threshold << CPR3_THRESH_UP_THRESH_SHIFT)
+		& CPR3_THRESH_UP_THRESH_MASK;
+	reg |= (thread->down_threshold << CPR3_THRESH_DOWN_THRESH_SHIFT)
+		& CPR3_THRESH_DOWN_THRESH_MASK;
+
+	cpr3_write(thread->ctrl, CPR3_REG_THRESH(thread->thread_id), reg);
+
+	/*
+	 * Mask all RO's initially so that unused thread doesn't contribute
+	 * to closed-loop voltage.
+	 */
+	cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id),
+		CPR3_RO_MASK);
+
+	return 0;
+}
+
+/**
+ * cpr4_regulator_init_temp_points() - performs hardware initialization of CPR4
+ *		registers to track tsen temperature data and also specify the
+ *		temperature band range values to apply different voltage margins
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_regulator_init_temp_points(struct cpr3_controller *ctrl)
+{
+	if (!ctrl->allow_temp_adj)
+		return 0;
+
+	cpr3_masked_write(ctrl, CPR4_REG_MISC,
+				CPR4_MISC_TEMP_SENSOR_ID_START_MASK,
+				ctrl->temp_sensor_id_start
+				<< CPR4_MISC_TEMP_SENSOR_ID_START_SHIFT);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MISC,
+				CPR4_MISC_TEMP_SENSOR_ID_END_MASK,
+				ctrl->temp_sensor_id_end
+				<< CPR4_MISC_TEMP_SENSOR_ID_END_SHIFT);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT2,
+		CPR4_MARGIN_TEMP_POINT2_MASK,
+		(ctrl->temp_band_count == 4 ? ctrl->temp_points[2] : 0x7FF)
+		<< CPR4_MARGIN_TEMP_POINT2_SHIFT);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1,
+		CPR4_MARGIN_TEMP_POINT1_MASK,
+		(ctrl->temp_band_count >= 3 ? ctrl->temp_points[1] : 0x7FF)
+		<< CPR4_MARGIN_TEMP_POINT1_SHIFT);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_POINT0N1,
+		CPR4_MARGIN_TEMP_POINT0_MASK,
+		(ctrl->temp_band_count >= 2 ? ctrl->temp_points[0] : 0x7FF)
+		<< CPR4_MARGIN_TEMP_POINT0_SHIFT);
+	return 0;
+}
+
+/**
+ * cpr3_regulator_init_cpr4() - performs hardware initialization at the
+ *		controller and thread level required for CPR4 operation.
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ * This function allocates sdelta structures and sdelta tables for aggregated
+ * corners of the controller and its threads.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_cpr4(struct cpr3_controller *ctrl)
+{
+	struct cpr3_thread *thread;
+	struct cpr3_regulator *vreg;
+	struct cpr4_sdelta *sdelta;
+	int i, j, ctrl_max_core_count, thread_max_core_count, rc = 0;
+	bool ctrl_valid_sdelta, thread_valid_sdelta;
+	u32 pmic_step_size = 1;
+	int thread_id = 0;
+	u64 temp;
+
+	if (ctrl->supports_hw_closed_loop) {
+		if (ctrl->saw_use_unit_mV)
+			pmic_step_size = ctrl->step_volt / 1000;
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+				  CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK,
+				  (pmic_step_size
+				  << CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT));
+
+		cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
+				  CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK,
+				  (ctrl->down_error_step_limit
+					<< CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT));
+
+		cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
+				  CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK,
+				  (ctrl->up_error_step_limit
+					<< CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT));
+
+		/*
+		 * Enable thread aggregation regardless of which threads are
+		 * enabled or disabled.
+		 */
+		cpr3_masked_write(ctrl, CPR4_REG_CPR_TIMER_CLAMP,
+				  CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN,
+				  CPR4_CPR_TIMER_CLAMP_THREAD_AGGREGATION_EN);
+
+		switch (ctrl->thread_count) {
+		case 0:
+			/* Disable both threads */
+			cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(0),
+				CPR4_CPR_MASK_THREAD_DISABLE_THREAD
+				    | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK,
+				CPR4_CPR_MASK_THREAD_DISABLE_THREAD
+				    | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK);
+
+			cpr3_masked_write(ctrl, CPR4_REG_CPR_MASK_THREAD(1),
+				CPR4_CPR_MASK_THREAD_DISABLE_THREAD
+				    | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK,
+				CPR4_CPR_MASK_THREAD_DISABLE_THREAD
+				    | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK);
+			break;
+		case 1:
+			/* Disable unused thread */
+			thread_id = ctrl->thread[0].thread_id ? 0 : 1;
+			cpr3_masked_write(ctrl,
+				CPR4_REG_CPR_MASK_THREAD(thread_id),
+				CPR4_CPR_MASK_THREAD_DISABLE_THREAD
+				    | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK,
+				CPR4_CPR_MASK_THREAD_DISABLE_THREAD
+				    | CPR4_CPR_MASK_THREAD_RO_MASK4THREAD_MASK);
+			break;
+		}
+	}
+
+	if (!ctrl->allow_core_count_adj && !ctrl->allow_temp_adj
+		&& !ctrl->allow_boost) {
+		/*
+		 * Skip below configuration as none of the features
+		 * are enabled.
+		 */
+		return rc;
+	}
+
+	if (ctrl->supports_hw_closed_loop)
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+				  CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN,
+				  CPR4_MARGIN_ADJ_CTL_TIMER_SETTLE_VOLTAGE_EN);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK,
+			ctrl->step_quot_fixed
+			<< CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN,
+			(ctrl->use_dynamic_step_quot
+			? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0));
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_MASK,
+			ctrl->initial_temp_band
+			<< CPR4_MARGIN_ADJ_CTL_INITIAL_TEMP_BAND_SHIFT);
+
+	rc = cpr4_regulator_init_temp_points(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "initialize temp points failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (ctrl->voltage_settling_time) {
+		/*
+		 * Configure the settling timer used to account for
+		 * one VDD supply step.
+		 */
+		temp = (u64)ctrl->cpr_clock_rate
+				* (u64)ctrl->voltage_settling_time;
+		do_div(temp, 1000000000);
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS,
+			CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK,
+			temp
+		    << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT);
+	}
+
+	/*
+	 * Allocate memory for cpr4_sdelta structure and sdelta table for
+	 * controller aggregated corner by finding the maximum core count
+	 * used by any cpr3 regulators.
+	 */
+	ctrl_max_core_count = 1;
+	ctrl_valid_sdelta = false;
+	for (i = 0; i < ctrl->thread_count; i++) {
+		thread = &ctrl->thread[i];
+
+		/*
+		 * Allocate memory for cpr4_sdelta structure and sdelta table
+		 * for thread aggregated corner by finding the maximum core
+		 * count used by any cpr3 regulators of the thread.
+		 */
+		thread_max_core_count = 1;
+		thread_valid_sdelta = false;
+		for (j = 0; j < thread->vreg_count; j++) {
+			vreg = &thread->vreg[j];
+			thread_max_core_count = max(thread_max_core_count,
+							vreg->max_core_count);
+			thread_valid_sdelta |= (vreg->allow_core_count_adj
+							| vreg->allow_temp_adj
+							| vreg->allow_boost);
+		}
+		if (thread_valid_sdelta) {
+			sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta),
+					GFP_KERNEL);
+			if (!sdelta)
+				return -ENOMEM;
+
+			sdelta->table = devm_kcalloc(ctrl->dev,
+						thread_max_core_count
+						* ctrl->temp_band_count,
+						sizeof(*sdelta->table),
+						GFP_KERNEL);
+			if (!sdelta->table)
+				return -ENOMEM;
+
+			sdelta->boost_table = devm_kcalloc(ctrl->dev,
+						ctrl->temp_band_count,
+						sizeof(*sdelta->boost_table),
+						GFP_KERNEL);
+			if (!sdelta->boost_table)
+				return -ENOMEM;
+
+			thread->aggr_corner.sdelta = sdelta;
+		}
+
+		ctrl_valid_sdelta |= thread_valid_sdelta;
+		ctrl_max_core_count = max(ctrl_max_core_count,
+						thread_max_core_count);
+	}
+
+	if (ctrl_valid_sdelta) {
+		sdelta = devm_kzalloc(ctrl->dev, sizeof(*sdelta), GFP_KERNEL);
+		if (!sdelta)
+			return -ENOMEM;
+
+		sdelta->table = devm_kcalloc(ctrl->dev, ctrl_max_core_count
+					* ctrl->temp_band_count,
+					sizeof(*sdelta->table), GFP_KERNEL);
+		if (!sdelta->table)
+			return -ENOMEM;
+
+		sdelta->boost_table = devm_kcalloc(ctrl->dev,
+					ctrl->temp_band_count,
+					sizeof(*sdelta->boost_table),
+					GFP_KERNEL);
+		if (!sdelta->boost_table)
+			return -ENOMEM;
+
+		ctrl->aggr_corner.sdelta = sdelta;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_write_temp_core_margin() - programs hardware SDELTA registers with
+ *		the voltage margin adjustments that need to be applied for
+ *		different online core-count and temperature bands.
+ * @ctrl:		Pointer to the CPR3 controller
+ * @addr:		SDELTA register address
+ * @temp_core_adj:	Array of voltage margin values for different temperature
+ *			bands.
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ *
+ * Return: none
+ */
+static void cpr3_write_temp_core_margin(struct cpr3_controller *ctrl,
+				 int addr, int *temp_core_adj)
+{
+	int i, margin_steps;
+	u32 reg = 0;
+
+	for (i = 0; i < ctrl->temp_band_count; i++) {
+		margin_steps = max(min(temp_core_adj[i], 127), -128);
+		reg |= (margin_steps & CPR4_MARGIN_TEMP_CORE_ADJ_MASK) <<
+			(i * CPR4_MARGIN_TEMP_CORE_ADJ_SHIFT);
+	}
+
+	cpr3_write(ctrl, addr, reg);
+	cpr3_debug(ctrl, "sdelta offset=0x%08x, val=0x%08x\n", addr, reg);
+}
+
+/**
+ * cpr3_controller_program_sdelta() - programs hardware SDELTA registers with
+ *		the voltage margin adjustments that need to be applied at
+ *		different online core-count and temperature bands. Also,
+ *		programs hardware register configuration for per-online-core
+ *		and per-temperature based adjustments.
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_controller_program_sdelta(struct cpr3_controller *ctrl)
+{
+	struct cpr3_corner *corner = &ctrl->aggr_corner;
+	struct cpr4_sdelta *sdelta = corner->sdelta;
+	int i, index, max_core_count, rc = 0;
+	bool cpr_enabled = ctrl->cpr_enabled;
+
+	if (!sdelta)
+		/* cpr4_sdelta not defined for current aggregated corner */
+		return 0;
+
+	if (ctrl->supports_hw_closed_loop && ctrl->cpr_enabled) {
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+			(ctrl->use_hw_closed_loop && !sdelta->allow_boost)
+			? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0);
+	}
+
+	if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj
+		&& !sdelta->allow_boost) {
+		/*
+		 * Per-online-core, per-temperature and voltage boost
+		 * adjustments are disabled for this aggregation corner.
+		 */
+		return 0;
+	}
+
+	/* Ensure that CPR clocks are enabled before writing to registers. */
+	if (!cpr_enabled) {
+		rc = cpr3_clock_enable(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
+			return rc;
+		}
+		ctrl->cpr_enabled = true;
+	}
+
+	max_core_count = sdelta->max_core_count;
+
+	if (sdelta->allow_core_count_adj || sdelta->allow_temp_adj) {
+		if (sdelta->allow_core_count_adj) {
+			/* Program TEMP_CORE0 to same margins as TEMP_CORE1 */
+			cpr3_write_temp_core_margin(ctrl,
+				CPR4_REG_MARGIN_TEMP_CORE(0),
+				&sdelta->table[0]);
+		}
+
+		for (i = 0; i < max_core_count; i++) {
+			index = i * sdelta->temp_band_count;
+			/*
+			 * Program TEMP_COREi with voltage margin adjustments
+			 * that need to be applied when the number of cores
+			 * becomes i.
+			 */
+			cpr3_write_temp_core_margin(ctrl,
+				CPR4_REG_MARGIN_TEMP_CORE(
+						sdelta->allow_core_count_adj
+						? i + 1 : max_core_count),
+						&sdelta->table[index]);
+		}
+	}
+
+	if (sdelta->allow_boost) {
+		/* Program only boost_num_cores row of SDELTA */
+		cpr3_write_temp_core_margin(ctrl,
+			CPR4_REG_MARGIN_TEMP_CORE(sdelta->boost_num_cores),
+					&sdelta->boost_table[0]);
+	}
+
+	if (!sdelta->allow_core_count_adj && !sdelta->allow_boost) {
+		cpr3_masked_write(ctrl, CPR4_REG_MISC,
+			CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
+			max_core_count
+			<< CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);
+	}
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+		CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
+		| CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_BOOST_EN,
+		max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
+		| ((sdelta->allow_core_count_adj || sdelta->allow_boost)
+			? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
+		| ((sdelta->allow_temp_adj && ctrl->supports_hw_closed_loop)
+			? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
+		| (((ctrl->use_hw_closed_loop && !sdelta->allow_boost)
+		    || !ctrl->supports_hw_closed_loop)
+			? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0)
+		| (sdelta->allow_boost
+			?  CPR4_MARGIN_ADJ_CTL_BOOST_EN : 0));
+
+	/*
+	 * Ensure that all previous CPR register writes have completed before
+	 * continuing.
+	 */
+	mb();
+
+	/* Turn off CPR clocks if they were off before this function call. */
+	if (!cpr_enabled) {
+		cpr3_clock_disable(ctrl);
+		ctrl->cpr_enabled = false;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_set_base_target_quot() - configure the target quotient
+ *		for each RO of the CPR3 regulator for CPRh operation.
+ *		In particular, the quotient of the RO selected for operation
+ *		should correspond to the lowest target quotient across the
+ *		corners supported by the single regulator of the CPR3 thread.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @base_quots:		Pointer to the base quotient array. The array must be
+ *			of size CPR3_RO_COUNT and it is populated with the
+ *			base quotient per-RO.
+ *
+ * Return: none
+ */
+static void cpr3_regulator_set_base_target_quot(struct cpr3_regulator *vreg,
+						u32 *base_quots)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int i, j, ro_mask = CPR3_RO_MASK;
+	u32 min_quot;
+
+	for (i = 0; i < vreg->corner_count; i++)
+		ro_mask &= vreg->corner[i].ro_mask;
+
+	/* Unmask the ROs selected for active use. */
+	cpr3_write(ctrl, CPR3_REG_RO_MASK(vreg->thread->thread_id),
+		   ro_mask);
+
+	for (i = 0; i < CPR3_RO_COUNT; i++) {
+		for (j = 0, min_quot = INT_MAX; j < vreg->corner_count; j++)
+			if (vreg->corner[j].target_quot[i])
+				min_quot = min(min_quot,
+				       vreg->corner[j].target_quot[i]);
+
+		if (min_quot == INT_MAX)
+			min_quot = 0;
+
+		cpr3_write(ctrl,
+			   CPR3_REG_TARGET_QUOT(vreg->thread->thread_id, i),
+			   min_quot);
+
+		base_quots[i] = min_quot;
+	}
+}
+
+/**
+ * cpr3_regulator_init_cprh_corners() - configure the per-corner CPRh registers
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function programs the controller registers which contain all information
+ * necessary to resolve the closed-loop voltage per-corner at runtime such as
+ * open-loop and floor voltages, target quotient delta, and RO select value.
+ * These registers also provide a means to disable closed-loop operation, core
+ * and temperature adjustments.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_cprh_corners(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr3_corner *corner;
+	u32 reg, delta_quot_steps, ro_sel;
+	u32 *base_quots;
+	int open_loop_volt_steps, floor_volt_steps, i, j, rc = 0;
+
+	base_quots = kcalloc(CPR3_RO_COUNT, sizeof(*base_quots),
+			     GFP_KERNEL);
+	if (!base_quots)
+		return -ENOMEM;
+
+	cpr3_regulator_set_base_target_quot(vreg, base_quots);
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		for (j = 0, ro_sel = INT_MAX; j < CPR3_RO_COUNT; j++) {
+			if (corner->target_quot[j]) {
+				ro_sel = j;
+				break;
+			}
+		}
+
+		if (ro_sel == INT_MAX) {
+			if (!corner->proc_freq) {
+				/*
+				 * Corner is not used as active DCVS set point
+				 * select RO 0 arbitrarily.
+				 */
+				ro_sel = 0;
+			} else {
+				cpr3_err(vreg, "corner=%d has invalid RO select value\n",
+					 i);
+				rc = -EINVAL;
+				goto free_base_quots;
+			}
+		}
+
+		open_loop_volt_steps = DIV_ROUND_UP(corner->open_loop_volt -
+						    ctrl->base_volt,
+						    ctrl->step_volt);
+		floor_volt_steps = DIV_ROUND_UP(corner->floor_volt -
+						ctrl->base_volt,
+						ctrl->step_volt);
+		delta_quot_steps = corner->proc_freq ?
+			DIV_ROUND_UP(corner->target_quot[ro_sel] -
+				     base_quots[ro_sel],
+				     CPRH_DELTA_QUOT_STEP_FACTOR) :
+			0;
+
+		if (open_loop_volt_steps > CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE ||
+		    floor_volt_steps > CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE ||
+		    delta_quot_steps > CPRH_CORNER_QUOT_DELTA_MAX_VALUE) {
+			cpr3_err(ctrl, "invalid CPRh corner configuration: open_loop_volt_steps=%d (%d max.), floor_volt_steps=%d (%d max), delta_quot_steps=%d (%d max)\n",
+				 open_loop_volt_steps,
+				 CPRH_CORNER_INIT_VOLTAGE_MAX_VALUE,
+				 floor_volt_steps,
+				 CPRH_CORNER_FLOOR_VOLTAGE_MAX_VALUE,
+				 delta_quot_steps,
+				 CPRH_CORNER_QUOT_DELTA_MAX_VALUE);
+			rc = -EINVAL;
+			goto free_base_quots;
+		}
+
+		reg = (open_loop_volt_steps << CPRH_CORNER_INIT_VOLTAGE_SHIFT)
+			& CPRH_CORNER_INIT_VOLTAGE_MASK;
+		reg |= (floor_volt_steps << CPRH_CORNER_FLOOR_VOLTAGE_SHIFT)
+			& CPRH_CORNER_FLOOR_VOLTAGE_MASK;
+		reg |= (delta_quot_steps << CPRH_CORNER_QUOT_DELTA_SHIFT)
+			& CPRH_CORNER_QUOT_DELTA_MASK;
+		reg |= (ro_sel << CPRH_CORNER_RO_SEL_SHIFT)
+			& CPRH_CORNER_RO_SEL_MASK;
+
+		if (corner->use_open_loop)
+			reg |= CPRH_CORNER_CPR_CL_DISABLE;
+
+		cpr3_debug(ctrl, "corner=%d open_loop_volt_steps=%d, floor_volt_steps=%d, delta_quot_steps=%d, base_volt=%d, step_volt=%d, base_quot=%d\n",
+			   i, open_loop_volt_steps, floor_volt_steps,
+			   delta_quot_steps, ctrl->base_volt,
+			   ctrl->step_volt, base_quots[ro_sel]);
+		cpr3_write(ctrl, CPRH_REG_CORNER(i), reg);
+	}
+
+free_base_quots:
+	kfree(base_quots);
+	return rc;
+}
+
+/**
+ * cprh_controller_program_sdelta() - programs hardware SDELTA registers with
+ *		the margins that need to be applied at different online
+ *		core-count and temperature bands for each corner band. Also,
+ *		programs hardware register configuration for core-count and
+ *		temp-based adjustments
+ *
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ *
+ * Return: none
+ */
+static void cprh_controller_program_sdelta(
+		struct cpr3_controller *ctrl)
+{
+	struct cpr3_regulator *vreg = &ctrl->thread[0].vreg[0];
+	struct cprh_corner_band *corner_band;
+	struct cpr4_sdelta *sdelta;
+	int i, j, index;
+	u32 reg = 0;
+
+	if (!vreg->allow_core_count_adj && !vreg->allow_temp_adj)
+		return;
+
+	cpr4_regulator_init_temp_points(ctrl);
+
+	for (i = 0; i < CPRH_CORNER_BAND_MAX_COUNT; i++) {
+		reg |= (i < vreg->corner_band_count ?
+			vreg->corner_band[i].corner
+			& CPRH_CORNER_BAND_MASK :
+			vreg->corner_count + 1)
+			<< (i * CPRH_CORNER_BAND_SHIFT);
+	}
+
+	cpr3_write(ctrl, CPRH_REG_CORNER_BAND, reg);
+
+	for (i = 0; i < vreg->corner_band_count; i++) {
+		corner_band = &vreg->corner_band[i];
+		sdelta = corner_band->sdelta;
+
+		if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) {
+			/*
+			 * Per-online-core and per-temperature margin
+			 * adjustments are disabled for this corner band.
+			 */
+			continue;
+		}
+
+		if (vreg->allow_core_count_adj)
+			cpr3_write_temp_core_margin(ctrl,
+				    CPRH_MARGIN_TEMP_CORE_VBAND(0, i),
+				    &sdelta->table[0]);
+
+		for (j = 0; j < sdelta->max_core_count; j++) {
+			index = j * sdelta->temp_band_count;
+
+			cpr3_write_temp_core_margin(ctrl,
+				    CPRH_MARGIN_TEMP_CORE_VBAND(
+				    sdelta->allow_core_count_adj
+				    ? j + 1 : vreg->max_core_count, i),
+				    &sdelta->table[index]);
+		}
+	}
+
+	if (!vreg->allow_core_count_adj) {
+		cpr3_masked_write(ctrl, CPR4_REG_MISC,
+			CPR4_MISC_MARGIN_TABLE_ROW_SELECT_MASK,
+			vreg->max_core_count
+			<< CPR4_MISC_MARGIN_TABLE_ROW_SELECT_SHIFT);
+	}
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+		CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_MASK
+		| CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN
+		| CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+		vreg->max_core_count << CPR4_MARGIN_ADJ_CTL_MAX_NUM_CORES_SHIFT
+		| ((vreg->allow_core_count_adj)
+		   ? CPR4_MARGIN_ADJ_CTL_CORE_ADJ_EN : 0)
+		| (vreg->allow_temp_adj ? CPR4_MARGIN_ADJ_CTL_TEMP_ADJ_EN : 0)
+		| ((ctrl->use_hw_closed_loop)
+		? CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_EN : 0)
+		| (ctrl->use_hw_closed_loop
+		? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE : 0));
+
+	/* Ensure that previous CPR register writes complete */
+	mb();
+}
+
+static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl);
+
+/**
+ * cpr3_regulator_init_cprh() - performs hardware initialization at the
+ *		controller and thread level required for CPRh operation.
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * CPR interface/bus clocks must be enabled before calling this function.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_cprh(struct cpr3_controller *ctrl)
+{
+	u32 reg, pmic_step_size = 1;
+	u64 temp;
+	int rc;
+
+	/* Single thread, single regulator supported */
+	if (ctrl->thread_count != 1) {
+		cpr3_err(ctrl, "expected 1 thread but found %d\n",
+			ctrl->thread_count);
+		return -EINVAL;
+	} else if (ctrl->thread[0].vreg_count != 1) {
+		cpr3_err(ctrl, "expected 1 regulator but found %d\n",
+			ctrl->thread[0].vreg_count);
+		return -EINVAL;
+	}
+
+	rc = cprh_regulator_aging_adjust(ctrl);
+	if (rc && rc != -ETIMEDOUT) {
+		/*
+		 * Don't fail initialization if the CPR aging measurement
+		 * timed out due to sensors not being available.
+		 */
+		cpr3_err(ctrl, "CPR aging adjustment failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	cprh_controller_program_sdelta(ctrl);
+
+	rc = cpr3_regulator_init_cprh_corners(&ctrl->thread[0].vreg[0]);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize CPRh corner registers\n");
+		return rc;
+	}
+
+	if (ctrl->saw_use_unit_mV)
+		pmic_step_size = ctrl->step_volt / 1000;
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+				CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_MASK,
+				(pmic_step_size
+				<< CPR4_MARGIN_ADJ_CTL_PMIC_STEP_SIZE_SHIFT));
+
+	cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
+				CPR4_SAW_ERROR_STEP_LIMIT_DN_MASK,
+				(ctrl->down_error_step_limit
+				<< CPR4_SAW_ERROR_STEP_LIMIT_DN_SHIFT));
+
+	cpr3_masked_write(ctrl, CPR4_REG_SAW_ERROR_STEP_LIMIT,
+				CPR4_SAW_ERROR_STEP_LIMIT_UP_MASK,
+				(ctrl->up_error_step_limit
+				<< CPR4_SAW_ERROR_STEP_LIMIT_UP_SHIFT));
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_MASK,
+			ctrl->step_quot_fixed
+			<< CPR4_MARGIN_ADJ_CTL_KV_MARGIN_ADJ_STEP_QUOT_SHIFT);
+
+	cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN,
+			(ctrl->use_dynamic_step_quot
+			? CPR4_MARGIN_ADJ_CTL_PER_RO_KV_MARGIN_EN : 0));
+
+	if (ctrl->voltage_settling_time) {
+		/*
+		 * Configure the settling timer used to account for
+		 * one VDD supply step.
+		 */
+		temp = (u64)ctrl->cpr_clock_rate
+				* (u64)ctrl->voltage_settling_time;
+		do_div(temp, 1000000000);
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_TEMP_CORE_TIMERS,
+			CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_MASK,
+			temp
+		  << CPR4_MARGIN_TEMP_CORE_TIMERS_SETTLE_VOLTAGE_COUNT_SHIFT);
+	}
+
+	if (ctrl->corner_switch_delay_time) {
+		/*
+		 * Configure the settling timer used to delay
+		 * following SAW requests
+		 */
+		temp = (u64)ctrl->cpr_clock_rate
+			* (u64)ctrl->corner_switch_delay_time;
+		do_div(temp, 1000000000);
+		do_div(temp, CPRH_MODE_SWITCH_DELAY_FACTOR);
+		cpr3_masked_write(ctrl, CPRH_REG_CTL,
+				  CPRH_CTL_MODE_SWITCH_DELAY_MASK,
+				  temp << CPRH_CTL_MODE_SWITCH_DELAY_SHIFT);
+	}
+
+	/*
+	 * Program base voltage and voltage multiplier values which
+	 * are used for floor and initial voltage calculations by the
+	 * CPRh controller.
+	 */
+	reg = (DIV_ROUND_UP(ctrl->base_volt, ctrl->step_volt)
+	       << CPRH_CTL_BASE_VOLTAGE_SHIFT)
+		& CPRH_CTL_BASE_VOLTAGE_MASK;
+	reg |= (DIV_ROUND_UP(ctrl->step_volt, 1000)
+		<< CPRH_CTL_VOLTAGE_MULTIPLIER_SHIFT)
+		& CPRH_CTL_VOLTAGE_MULTIPLIER_MASK;
+	/* Enable OSM block interface with CPR */
+	reg |= CPRH_CTL_OSM_ENABLED;
+	cpr3_masked_write(ctrl, CPRH_REG_CTL, CPRH_CTL_BASE_VOLTAGE_MASK
+			  | CPRH_CTL_VOLTAGE_MULTIPLIER_MASK
+			  | CPRH_CTL_OSM_ENABLED, reg);
+
+	/* Enable loop_en */
+	cpr3_ctrl_loop_enable(ctrl);
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_init_ctrl() - performs hardware initialization of CPR
+ *		controller registers
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_ctrl(struct cpr3_controller *ctrl)
+{
+	int i, j, k, m, rc;
+	u32 ro_used = 0;
+	u32 gcnt, cont_dly, up_down_dly, val;
+	u64 temp;
+	char *mode;
+
+	if (ctrl->core_clk) {
+		rc = clk_set_rate(ctrl->core_clk, ctrl->cpr_clock_rate);
+		if (rc) {
+			cpr3_err(ctrl, "clk_set_rate(core_clk, %u) failed, rc=%d\n",
+				ctrl->cpr_clock_rate, rc);
+			return rc;
+		}
+	}
+
+	rc = cpr3_clock_enable(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
+		return rc;
+	}
+	ctrl->cpr_enabled = true;
+
+	/* Find all RO's used by any corner of any regulator. */
+	for (i = 0; i < ctrl->thread_count; i++)
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++)
+			for (k = 0; k < ctrl->thread[i].vreg[j].corner_count;
+			     k++)
+				for (m = 0; m < CPR3_RO_COUNT; m++)
+					if (ctrl->thread[i].vreg[j].corner[k].
+					    target_quot[m])
+						ro_used |= BIT(m);
+
+	/* Configure the GCNT of the RO's that will be used */
+	gcnt = cpr3_regulator_get_gcnt(ctrl);
+	for (i = 0; i < CPR3_RO_COUNT; i++)
+		if (ro_used & BIT(i))
+			cpr3_write(ctrl, CPR3_REG_GCNT(i), gcnt);
+
+	/* Configure the loop delay time */
+	temp = (u64)ctrl->cpr_clock_rate * (u64)ctrl->loop_time;
+	do_div(temp, 1000000000);
+	cont_dly = temp;
+	if (ctrl->supports_hw_closed_loop
+		&& ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly);
+	else
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, cont_dly);
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		temp = (u64)ctrl->cpr_clock_rate *
+				(u64)ctrl->up_down_delay_time;
+		do_div(temp, 1000000000);
+		up_down_dly = temp;
+		if (ctrl->supports_hw_closed_loop)
+			cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT,
+				up_down_dly);
+		cpr3_debug(ctrl, "up_down_dly=%u, up_down_delay_time=%u ns\n",
+			up_down_dly, ctrl->up_down_delay_time);
+	}
+
+	cpr3_debug(ctrl, "cpr_clock_rate=%u HZ, sensor_time=%u ns, loop_time=%u ns, gcnt=%u, cont_dly=%u\n",
+		ctrl->cpr_clock_rate, ctrl->sensor_time, ctrl->loop_time,
+		gcnt, cont_dly);
+
+	/* Configure CPR sensor operation */
+	val = (ctrl->idle_clocks << CPR3_CPR_CTL_IDLE_CLOCKS_SHIFT)
+		& CPR3_CPR_CTL_IDLE_CLOCKS_MASK;
+	val |= (ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT)
+		& CPR3_CPR_CTL_COUNT_MODE_MASK;
+	val |= (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT)
+		& CPR3_CPR_CTL_COUNT_REPEAT_MASK;
+	cpr3_write(ctrl, CPR3_REG_CPR_CTL, val);
+
+	cpr3_debug(ctrl, "idle_clocks=%u, count_mode=%u, count_repeat=%u; CPR_CTL=0x%08X\n",
+		ctrl->idle_clocks, ctrl->count_mode, ctrl->count_repeat, val);
+
+	/* Configure CPR default step quotients */
+	val = (ctrl->step_quot_init_min << CPR3_CPR_STEP_QUOT_MIN_SHIFT)
+		& CPR3_CPR_STEP_QUOT_MIN_MASK;
+	val |= (ctrl->step_quot_init_max << CPR3_CPR_STEP_QUOT_MAX_SHIFT)
+		& CPR3_CPR_STEP_QUOT_MAX_MASK;
+	cpr3_write(ctrl, CPR3_REG_CPR_STEP_QUOT, val);
+
+	cpr3_debug(ctrl, "step_quot_min=%u, step_quot_max=%u; STEP_QUOT=0x%08X\n",
+		ctrl->step_quot_init_min, ctrl->step_quot_init_max, val);
+
+	/* Configure the CPR sensor ownership */
+	for (i = 0; i < ctrl->sensor_count; i++)
+		cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(i),
+			   ctrl->sensor_owner[i]);
+
+	/* Configure per-thread registers */
+	for (i = 0; i < ctrl->thread_count; i++) {
+		rc = cpr3_regulator_init_thread(&ctrl->thread[i]);
+		if (rc) {
+			cpr3_err(ctrl, "CPR thread register initialization failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	if (ctrl->supports_hw_closed_loop) {
+		if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+		    ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+			cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+				CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+				ctrl->use_hw_closed_loop
+				? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
+				: CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
+		} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+			cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
+				ctrl->use_hw_closed_loop
+				? CPR3_HW_CLOSED_LOOP_ENABLE
+				: CPR3_HW_CLOSED_LOOP_DISABLE);
+
+			cpr3_debug(ctrl, "PD_THROTTLE=0x%08X\n",
+				ctrl->proc_clock_throttle);
+		}
+
+		if ((ctrl->use_hw_closed_loop ||
+		     ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) &&
+		    ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+			rc = regulator_enable(ctrl->vdd_limit_regulator);
+			if (rc) {
+				cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n",
+					rc);
+				return rc;
+			}
+
+			if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+				rc = msm_spm_avs_enable_irq(0,
+							   MSM_SPM_AVS_IRQ_MAX);
+				if (rc) {
+					cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n",
+						rc);
+					return rc;
+				}
+			}
+		}
+	}
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc = cpr3_regulator_init_cpr4(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "CPR4-specific controller initialization failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+		rc = cpr3_regulator_init_cprh(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "CPRh-specific controller initialization failed, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	/* Ensure that all register writes complete before disabling clocks. */
+	wmb();
+
+	/* Keep CPR clocks on for CPRh full HW closed-loop operation */
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		cpr3_clock_disable(ctrl);
+		ctrl->cpr_enabled = false;
+	}
+
+	if (!ctrl->cpr_allowed_sw || !ctrl->cpr_allowed_hw)
+		mode = "open-loop";
+	else if (ctrl->supports_hw_closed_loop &&
+		 ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
+		mode = ctrl->use_hw_closed_loop
+			? "HW closed-loop" : "SW closed-loop";
+	else if (ctrl->supports_hw_closed_loop &&
+		 ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
+		mode = ctrl->use_hw_closed_loop
+			? "full HW closed-loop" : "open-loop";
+	else
+		mode = "closed-loop";
+
+	cpr3_info(ctrl, "Default CPR mode = %s", mode);
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_set_target_quot() - configure the target quotient for each
+ *		RO of the CPR3 thread and set the RO mask
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: none
+ */
+static void cpr3_regulator_set_target_quot(struct cpr3_thread *thread)
+{
+	u32 new_quot, last_quot;
+	int i;
+
+	if (thread->aggr_corner.ro_mask == CPR3_RO_MASK
+	    && thread->last_closed_loop_aggr_corner.ro_mask == CPR3_RO_MASK) {
+		/* Avoid writing target quotients since all RO's are masked. */
+		return;
+	} else if (thread->aggr_corner.ro_mask == CPR3_RO_MASK) {
+		cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id),
+			CPR3_RO_MASK);
+		thread->last_closed_loop_aggr_corner.ro_mask = CPR3_RO_MASK;
+		/*
+		 * Only the RO_MASK register needs to be written since all
+		 * RO's are masked.
+		 */
+		return;
+	} else if (thread->aggr_corner.ro_mask
+			!= thread->last_closed_loop_aggr_corner.ro_mask) {
+		cpr3_write(thread->ctrl, CPR3_REG_RO_MASK(thread->thread_id),
+			thread->aggr_corner.ro_mask);
+	}
+
+	for (i = 0; i < CPR3_RO_COUNT; i++) {
+		new_quot = thread->aggr_corner.target_quot[i];
+		last_quot = thread->last_closed_loop_aggr_corner.target_quot[i];
+		if (new_quot != last_quot)
+			cpr3_write(thread->ctrl,
+				CPR3_REG_TARGET_QUOT(thread->thread_id, i),
+				new_quot);
+	}
+
+	thread->last_closed_loop_aggr_corner = thread->aggr_corner;
+}
+
+/**
+ * cpr3_update_vreg_closed_loop_volt() - update the last known settled
+ *		closed loop voltage for a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ * @vdd_volt:		Last known settled voltage in microvolts for the
+ *			VDD supply
+ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register
+ *
+ * Return: none
+ */
+static void cpr3_update_vreg_closed_loop_volt(struct cpr3_regulator *vreg,
+				int vdd_volt, u32 reg_last_measurement)
+{
+	bool step_dn, step_up, aggr_step_up, aggr_step_dn, aggr_step_mid;
+	bool valid, pd_valid, saw_error;
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr3_corner *corner;
+	u32 id;
+
+	if (vreg->last_closed_loop_corner == CPR3_REGULATOR_CORNER_INVALID)
+		return;
+
+	corner = &vreg->corner[vreg->last_closed_loop_corner];
+
+	if (vreg->thread->last_closed_loop_aggr_corner.ro_mask
+	    == CPR3_RO_MASK  || !vreg->aggregated) {
+		return;
+	} else if (!ctrl->cpr_enabled || !ctrl->last_corner_was_closed_loop) {
+		return;
+	} else if (ctrl->thread_count == 1
+		 && vdd_volt >= corner->floor_volt
+		 && vdd_volt <= corner->ceiling_volt) {
+		corner->last_volt = vdd_volt;
+		cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n",
+			   vreg->last_closed_loop_corner, corner->last_volt,
+			   vreg->last_closed_loop_corner,
+			   corner->ceiling_volt,
+			   vreg->last_closed_loop_corner,
+			   corner->floor_volt);
+		return;
+	} else if (!ctrl->supports_hw_closed_loop) {
+		return;
+	} else if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPR3) {
+		corner->last_volt = vdd_volt;
+		cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d\n",
+			   vreg->last_closed_loop_corner, corner->last_volt,
+			   vreg->last_closed_loop_corner,
+			   corner->ceiling_volt,
+			   vreg->last_closed_loop_corner,
+			   corner->floor_volt);
+		return;
+	}
+
+	/* CPR clocks are on and HW closed loop is supported */
+	valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID);
+	if (!valid) {
+		cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X valid bit not set\n",
+			   reg_last_measurement);
+		return;
+	}
+
+	id = vreg->thread->thread_id;
+
+	step_dn
+	       = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_DN(id));
+	step_up
+	       = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_THREAD_UP(id));
+	aggr_step_dn = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_DN);
+	aggr_step_mid
+		= !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_MID);
+	aggr_step_up = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_AGGR_UP);
+	saw_error = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_SAW_ERROR);
+	pd_valid
+	     = !((((reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK)
+		       >> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT)
+		      & vreg->pd_bypass_mask) == vreg->pd_bypass_mask);
+
+	if (!pd_valid) {
+		cpr3_debug(vreg, "CPR_LAST_VALID_MEASUREMENT=0x%X, all power domains bypassed\n",
+			   reg_last_measurement);
+		return;
+	} else if (step_dn && step_up) {
+		cpr3_err(vreg, "both up and down status bits set, CPR_LAST_VALID_MEASUREMENT=0x%X\n",
+			 reg_last_measurement);
+		return;
+	} else if (aggr_step_dn && step_dn && vdd_volt < corner->last_volt
+		   && vdd_volt >= corner->floor_volt) {
+		corner->last_volt = vdd_volt;
+	} else if (aggr_step_up && step_up && vdd_volt > corner->last_volt
+		   && vdd_volt <= corner->ceiling_volt) {
+		corner->last_volt = vdd_volt;
+	} else if (aggr_step_mid
+		   && vdd_volt >= corner->floor_volt
+		   && vdd_volt <= corner->ceiling_volt) {
+		corner->last_volt = vdd_volt;
+	} else if (saw_error && (vdd_volt == corner->ceiling_volt
+				 || vdd_volt == corner->floor_volt)) {
+		corner->last_volt = vdd_volt;
+	} else {
+		cpr3_debug(vreg, "last_volt not updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, vdd_volt=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n",
+			   vreg->last_closed_loop_corner, corner->last_volt,
+			   vreg->last_closed_loop_corner,
+			   corner->ceiling_volt,
+			   vreg->last_closed_loop_corner, corner->floor_volt,
+			   vdd_volt, reg_last_measurement);
+		return;
+	}
+
+	cpr3_debug(vreg, "last_volt updated: last_volt[%d]=%d, ceiling_volt[%d]=%d, floor_volt[%d]=%d, CPR_LAST_VALID_MEASUREMENT=0x%X\n",
+		   vreg->last_closed_loop_corner, corner->last_volt,
+		   vreg->last_closed_loop_corner, corner->ceiling_volt,
+		   vreg->last_closed_loop_corner, corner->floor_volt,
+		   reg_last_measurement);
+}
+
+/**
+ * cpr3_regulator_config_ldo_retention() - configure per-regulator LDO retention
+ *		mode
+ * @vreg:		Pointer to the CPR3 regulator to configure
+ * @ref_volt:		Reference voltage used to determine if LDO retention
+ *			mode can be allowed. It corresponds either to the
+ *			aggregated floor voltage or the next VDD supply setpoint
+ *
+ * This function determines if a CPR3 regulator's configuration satisfies safe
+ * operating voltages for LDO retention and uses the regulator_allow_bypass()
+ * interface on the LDO retention regulator to enable or disable such feature
+ * accordingly.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_ldo_retention(struct cpr3_regulator *vreg,
+					int ref_volt)
+{
+	struct regulator *ldo_ret_reg = vreg->ldo_ret_regulator;
+	int retention_volt, rc;
+	enum msm_ldo_supply_mode mode;
+
+	if (!ldo_ret_reg) {
+		/* LDO retention regulator is not defined */
+		return 0;
+	}
+
+	retention_volt = regulator_get_voltage(ldo_ret_reg);
+	if (retention_volt < 0) {
+		cpr3_err(vreg, "regulator_get_voltage(ldo_ret) failed, rc=%d\n",
+			 retention_volt);
+		return retention_volt;
+
+	}
+
+	mode = ref_volt >= retention_volt + vreg->ldo_min_headroom_volt
+		? LDO_MODE : BHS_MODE;
+
+	rc = regulator_allow_bypass(ldo_ret_reg, mode);
+	if (rc)
+		cpr3_err(vreg, "regulator_allow_bypass(ldo_ret) == %s failed, rc=%d\n",
+			 mode ? "true" : "false", rc);
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_config_kryo_ldo_mem_acc() - configure the mem-acc regulator
+ *		corner based upon a future Kryo LDO regulator voltage setpoint
+ * @vreg:		Pointer to the CPR3 regulator
+ * @new_volt:		New voltage in microvolts that the LDO regulator needs
+ *			to end up at
+ *
+ * This function determines if a new LDO regulator set point will result
+ * in crossing the voltage threshold that requires reconfiguration of
+ * the mem-acc regulator associated with a CPR3 regulator and if so, performs
+ * the correct sequence to select the correct mem-acc corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_kryo_ldo_mem_acc(struct cpr3_regulator *vreg,
+					     int new_volt)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct regulator *ldo_reg = vreg->ldo_regulator;
+	struct regulator *mem_acc_reg = vreg->mem_acc_regulator;
+	int mem_acc_volt = ctrl->mem_acc_threshold_volt;
+	int last_volt, safe_volt, mem_acc_corn, rc;
+	enum msm_apm_supply apm_mode;
+
+	if (!mem_acc_reg || !mem_acc_volt || !ldo_reg)
+		return 0;
+
+	apm_mode = msm_apm_get_supply(ctrl->apm);
+	if (apm_mode < 0) {
+		cpr3_err(ctrl, "APM get supply failed, rc=%d\n",
+			 apm_mode);
+		return apm_mode;
+	}
+
+	last_volt = regulator_get_voltage(ldo_reg);
+	if (last_volt < 0) {
+		cpr3_err(vreg, "regulator_get_voltage(ldo) failed, rc=%d\n",
+			 last_volt);
+		return last_volt;
+	}
+
+	if (((last_volt < mem_acc_volt && mem_acc_volt <= new_volt)
+	     || (last_volt >= mem_acc_volt && mem_acc_volt > new_volt))) {
+
+		if (apm_mode == ctrl->apm_high_supply)
+			safe_volt = min(vreg->ldo_max_volt, mem_acc_volt);
+		else
+			safe_volt = min(max(ctrl->system_supply_max_volt -
+					    vreg->ldo_max_headroom_volt,
+					    mem_acc_volt), vreg->ldo_max_volt);
+
+		rc = regulator_set_voltage(ldo_reg, safe_volt,
+					   max(new_volt, last_volt));
+		if (rc) {
+			cpr3_err(ctrl, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
+				 mem_acc_volt, rc);
+			return rc;
+		}
+
+		mem_acc_corn = new_volt < mem_acc_volt ?
+			ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] :
+			ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER];
+
+		rc = regulator_set_voltage(mem_acc_reg, mem_acc_corn,
+					   mem_acc_corn);
+		if (rc) {
+			cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
+				 0, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_kryo_bhs_prepare() - configure the Kryo LDO regulator
+ *		associated with a CPR3 regulator in preparation for BHS
+ *		mode switch.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @vdd_volt:		Last known settled voltage in microvolts for the VDD
+ *			supply
+ * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
+ *			the VDD supply
+ *
+ * This function performs the necessary steps prior to switching a Kryo LDO
+ * regulator to BHS mode (LDO bypassed mode).
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_kryo_bhs_prepare(struct cpr3_regulator *vreg,
+			       int vdd_volt, int vdd_ceiling_volt)
+{
+	struct regulator *ldo_reg = vreg->ldo_regulator;
+	int bhs_volt, rc;
+
+	bhs_volt = vdd_volt - vreg->ldo_min_headroom_volt;
+	if (bhs_volt > vreg->ldo_max_volt) {
+		cpr3_debug(vreg, "limited to LDO output of %d uV when switching to BHS mode\n",
+			   vreg->ldo_max_volt);
+		bhs_volt = vreg->ldo_max_volt;
+	}
+
+	rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg, bhs_volt);
+	if (rc) {
+		cpr3_err(vreg, "failed to configure mem-acc settings\n");
+		return rc;
+	}
+
+	rc = regulator_set_voltage(ldo_reg, bhs_volt, min(vdd_ceiling_volt,
+							  vreg->ldo_max_volt));
+	if (rc) {
+		cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
+			 bhs_volt, rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_set_bhs_mode() - configure the LDO regulator associated with
+ *		a CPR3 regulator to BHS mode
+ * @vreg:		Pointer to the CPR3 regulator
+ * @vdd_volt:		Last known settled voltage in microvolts for the VDD
+ *			supply
+ * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
+ *			the VDD supply
+ *
+ * This function performs the necessary steps to switch an LDO regulator
+ * to BHS mode (LDO bypassed mode).
+ */
+static int cpr3_regulator_set_bhs_mode(struct cpr3_regulator *vreg,
+			       int vdd_volt, int vdd_ceiling_volt)
+{
+	struct regulator *ldo_reg = vreg->ldo_regulator;
+	int rc;
+
+	if (vreg->ldo_type == CPR3_LDO_KRYO) {
+		rc = cpr3_regulator_kryo_bhs_prepare(vreg, vdd_volt,
+				vdd_ceiling_volt);
+		if (rc) {
+			cpr3_err(vreg, "cpr3 regulator bhs mode prepare failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	rc = regulator_allow_bypass(ldo_reg, BHS_MODE);
+	if (rc) {
+		cpr3_err(vreg, "regulator_allow_bypass(bhs) == %s failed, rc=%d\n",
+			 BHS_MODE ? "true" : "false", rc);
+		return rc;
+	}
+	vreg->ldo_regulator_bypass = BHS_MODE;
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_ldo_apm_prepare() - configure LDO regulators associated
+ *		with each CPR3 regulator of a CPR3 controller in preparation
+ *		for an APM switch.
+ * @ctrl:		Pointer to the CPR3 controller
+ * @new_volt:		New voltage in microvolts that the VDD supply
+ *			needs to end up at
+ * @last_volt:		Last known voltage in microvolts for the VDD supply
+ * @aggr_corner:	Pointer to the CPR3 corner which corresponds to the max
+ *			corner aggregated from all CPR3 threads managed by the
+ *			CPR3 controller
+ *
+ * This function ensures LDO regulator hardware requirements are met before
+ * an APM switch is requested. The function must be called as the last step
+ * before switching the APM mode.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_ldo_apm_prepare(struct cpr3_controller *ctrl,
+				int new_volt, int last_volt,
+				struct cpr3_corner *aggr_corner)
+{
+	struct cpr3_regulator *vreg;
+	struct cpr3_corner *current_corner;
+	enum msm_apm_supply apm_mode;
+	int i, j, safe_volt, max_volt, ldo_volt, ref_volt, rc;
+
+	apm_mode = msm_apm_get_supply(ctrl->apm);
+	if (apm_mode < 0) {
+		cpr3_err(ctrl, "APM get supply failed, rc=%d\n", apm_mode);
+		return apm_mode;
+	}
+
+	if (apm_mode == ctrl->apm_low_supply ||
+	    new_volt >= ctrl->apm_threshold_volt)
+		return 0;
+
+	/*
+	 * Guarantee LDO maximum headroom is not violated when the APM is
+	 * switched to the system-supply source.
+	 */
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+
+			if (!vreg->vreg_enabled || vreg->current_corner
+			    == CPR3_REGULATOR_CORNER_INVALID)
+				continue;
+
+			if (!vreg->ldo_regulator || !vreg->ldo_mode_allowed ||
+			    vreg->ldo_regulator_bypass == BHS_MODE)
+				continue;
+
+			/*
+			 * If the new VDD configuration does not satisfy
+			 * requirements for LDO usage, switch the regulator
+			 * to BHS mode. By doing so, the LDO maximum headroom
+			 * does not need to be enforced.
+			 */
+			current_corner = &vreg->corner[vreg->current_corner];
+			ldo_volt = current_corner->open_loop_volt
+				- vreg->ldo_adjust_volt;
+			ref_volt = ctrl->use_hw_closed_loop ?
+				aggr_corner->floor_volt :
+				new_volt;
+
+			if (ref_volt < ldo_volt + vreg->ldo_min_headroom_volt
+			    || ldo_volt < ctrl->system_supply_max_volt -
+			    vreg->ldo_max_headroom_volt ||
+			    ldo_volt > vreg->ldo_max_volt) {
+				rc = cpr3_regulator_set_bhs_mode(vreg,
+					 last_volt, aggr_corner->ceiling_volt);
+				if (rc)
+					return rc;
+				/*
+				 * Do not enforce LDO maximum headroom since the
+				 * regulator is now configured to BHS mode.
+				 */
+				continue;
+			}
+
+			safe_volt = min(max(ldo_volt,
+					    ctrl->system_supply_max_volt
+					    - vreg->ldo_max_headroom_volt),
+					vreg->ldo_max_volt);
+			max_volt = min(ctrl->system_supply_max_volt,
+				       vreg->ldo_max_volt);
+
+			rc = regulator_set_voltage(vreg->ldo_regulator,
+						   safe_volt, max_volt);
+			if (rc) {
+				cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
+					 safe_volt, rc);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_config_vreg_kryo_ldo() - configure the voltage and bypass
+ *		state for the Kryo LDO regulator associated with a single CPR3
+ *		regulator.
+ *
+ * @vreg:		Pointer to the CPR3 regulator
+ * @vdd_floor_volt:	Last known aggregated floor voltage in microvolts for
+ *			the VDD supply
+ * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
+ *			the VDD supply
+ * @ref_volt:		Reference voltage in microvolts corresponds either to
+ *			the aggregated floor voltage or the next VDD supply
+ *			setpoint.
+ * @last_volt:		Last known voltage in microvolts for the VDD supply
+ *
+ * This function performs all relevant LDO or BHS configurations if a Kryo LDO
+ * regulator is specified.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_vreg_kryo_ldo(struct cpr3_regulator *vreg,
+			  int vdd_floor_volt, int vdd_ceiling_volt,
+			  int ref_volt, int last_volt)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct regulator *ldo_reg = vreg->ldo_regulator;
+	struct cpr3_corner *current_corner;
+	enum msm_apm_supply apm_mode;
+	int rc, ldo_volt, final_ldo_volt, bhs_volt, max_volt, safe_volt;
+
+	current_corner = &vreg->corner[vreg->current_corner];
+	ldo_volt = current_corner->open_loop_volt
+		- vreg->ldo_adjust_volt;
+	bhs_volt = last_volt - vreg->ldo_min_headroom_volt;
+	max_volt = min(vdd_ceiling_volt, vreg->ldo_max_volt);
+
+	if (ref_volt >= ldo_volt + vreg->ldo_min_headroom_volt &&
+	    ldo_volt >= ctrl->system_supply_max_volt -
+	    vreg->ldo_max_headroom_volt &&
+	    bhs_volt >= ctrl->system_supply_max_volt -
+	    vreg->ldo_max_headroom_volt &&
+	    ldo_volt <= vreg->ldo_max_volt) {
+		/* LDO minimum and maximum headrooms satisfied */
+		apm_mode = msm_apm_get_supply(ctrl->apm);
+		if (apm_mode < 0) {
+			cpr3_err(ctrl, "APM get supply failed, rc=%d\n",
+				 apm_mode);
+			return apm_mode;
+		}
+
+		if (vreg->ldo_regulator_bypass == BHS_MODE) {
+			/*
+			 * BHS to LDO transition. Configure LDO output
+			 * to min(max LDO output, VDD - LDO headroom)
+			 * voltage if APM is on high supply source or
+			 * min(max(system-supply ceiling - LDO max headroom,
+			 * VDD - LDO headroom), max LDO output) if
+			 * APM is on low supply source, then switch
+			 * regulator mode.
+			 */
+			if (apm_mode == ctrl->apm_high_supply)
+				safe_volt = min(vreg->ldo_max_volt, bhs_volt);
+			else
+				safe_volt =
+					min(max(ctrl->system_supply_max_volt -
+						vreg->ldo_max_headroom_volt,
+						bhs_volt),
+					    vreg->ldo_max_volt);
+
+			rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg,
+							       safe_volt);
+			if (rc) {
+				cpr3_err(vreg, "failed to configure mem-acc settings\n");
+				return rc;
+			}
+
+			rc = regulator_set_voltage(ldo_reg, safe_volt,
+						   max_volt);
+			if (rc) {
+				cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
+					 safe_volt, rc);
+				return rc;
+			}
+
+			rc = regulator_allow_bypass(ldo_reg, LDO_MODE);
+			if (rc) {
+				cpr3_err(vreg, "regulator_allow_bypass(ldo) == %s failed, rc=%d\n",
+					 LDO_MODE ? "true" : "false", rc);
+				return rc;
+			}
+			vreg->ldo_regulator_bypass = LDO_MODE;
+		}
+
+		/* Configure final LDO output voltage */
+		if (apm_mode == ctrl->apm_high_supply)
+			final_ldo_volt = max(ldo_volt,
+					     vdd_ceiling_volt -
+					     vreg->ldo_max_headroom_volt);
+		else
+			final_ldo_volt = ldo_volt;
+
+		rc = cpr3_regulator_config_kryo_ldo_mem_acc(vreg,
+						       final_ldo_volt);
+		if (rc) {
+			cpr3_err(vreg, "failed to configure mem-acc settings\n");
+			return rc;
+		}
+
+		rc = regulator_set_voltage(ldo_reg, final_ldo_volt, max_volt);
+		if (rc) {
+			cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
+				 final_ldo_volt, rc);
+			return rc;
+		}
+	} else {
+		if (vreg->ldo_regulator_bypass == LDO_MODE) {
+			/* LDO to BHS transition */
+			rc = cpr3_regulator_set_bhs_mode(vreg, last_volt,
+							 vdd_ceiling_volt);
+			if (rc)
+				return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_config_vreg_ldo300() - configure the voltage and bypass state
+ *		for the LDO300 regulator associated with a single CPR3
+ *		regulator.
+ *
+ * @vreg:		Pointer to the CPR3 regulator
+ * @new_volt:		New voltage in microvolts that VDD supply needs to
+ *			end up at
+ * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
+ *			the VDD supply
+ *
+ * This function performs all relevant LDO or BHS configurations for an LDO300
+ * type regulator.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_vreg_ldo300(struct cpr3_regulator *vreg,
+		int new_volt, int vdd_ceiling_volt)
+{
+	struct regulator *ldo_reg = vreg->ldo_regulator;
+	struct cpr3_corner *corner;
+	bool mode;
+	int rc = 0;
+
+	corner = &vreg->corner[vreg->current_corner];
+	mode = corner->ldo_mode_allowed ? LDO_MODE : BHS_MODE;
+
+	if (mode == LDO_MODE) {
+		rc = regulator_set_voltage(ldo_reg, new_volt, vdd_ceiling_volt);
+		if (rc) {
+			cpr3_err(vreg, "regulator_set_voltage(ldo) == %d failed, rc=%d\n",
+				 new_volt, rc);
+			return rc;
+		}
+	}
+
+	if (vreg->ldo_regulator_bypass != mode) {
+		rc = regulator_allow_bypass(ldo_reg, mode);
+		if (rc) {
+			cpr3_err(vreg, "regulator_allow_bypass(%s) is failed, rc=%d\n",
+				 mode == LDO_MODE ? "ldo" : "bhs", rc);
+			return rc;
+		}
+		vreg->ldo_regulator_bypass = mode;
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_config_vreg_ldo() - configure the voltage and bypass state for
+ *		the LDO regulator associated with a single CPR3 regulator.
+ *
+ * @vreg:		Pointer to the CPR3 regulator
+ * @vdd_floor_volt:	Last known aggregated floor voltage in microvolts for
+ *			the VDD supply
+ * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
+ *			the VDD supply
+ * @new_volt:		New voltage in microvolts that VDD supply needs to
+ *			end up at
+ * @last_volt:		Last known voltage in microvolts for the VDD supply
+ *
+ * This function identifies the type of LDO regulator associated with a CPR3
+ * regulator and invokes the LDO specific configuration functions.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_vreg_ldo(struct cpr3_regulator *vreg,
+			  int vdd_floor_volt, int vdd_ceiling_volt,
+			  int new_volt, int last_volt)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int ref_volt, rc;
+
+	ref_volt = ctrl->use_hw_closed_loop ? vdd_floor_volt :
+		new_volt;
+
+	rc = cpr3_regulator_config_ldo_retention(vreg, ref_volt);
+	if (rc)
+		return rc;
+
+	if (!vreg->vreg_enabled ||
+		vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID)
+		return 0;
+
+	switch (vreg->ldo_type) {
+	case CPR3_LDO_KRYO:
+		rc = cpr3_regulator_config_vreg_kryo_ldo(vreg, vdd_floor_volt,
+				vdd_ceiling_volt, ref_volt, last_volt);
+		if (rc)
+			cpr3_err(vreg, "kryo ldo regulator config failed, rc=%d\n",
+				rc);
+		break;
+	case CPR3_LDO300:
+		rc = cpr3_regulator_config_vreg_ldo300(vreg, new_volt,
+				vdd_ceiling_volt);
+		if (rc)
+			cpr3_err(vreg, "ldo300 regulator config failed, rc=%d\n",
+				rc);
+		break;
+	default:
+		cpr3_err(vreg, "invalid ldo regulator type = %d\n",
+				vreg->ldo_type);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_config_ldo() - configure the voltage and bypass state for the
+ *		LDO regulator associated with each CPR3 regulator of a CPR3
+ *		controller
+ * @ctrl:		Pointer to the CPR3 controller
+ * @vdd_floor_volt:	Last known aggregated floor voltage in microvolts for
+ *			the VDD supply
+ * @vdd_ceiling_volt:	Last known aggregated ceiling voltage in microvolts for
+ *			the VDD supply
+ * @new_volt:		New voltage in microvolts that VDD supply needs to
+ *			end up at
+ * @last_volt:		Last known voltage in microvolts for the VDD supply
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_ldo(struct cpr3_controller *ctrl,
+				int vdd_floor_volt, int vdd_ceiling_volt,
+				int new_volt, int last_volt)
+{
+	struct cpr3_regulator *vreg;
+	int i, j, rc;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+
+			if (!vreg->ldo_regulator || !vreg->ldo_mode_allowed)
+				continue;
+
+			rc = cpr3_regulator_config_vreg_ldo(vreg,
+					vdd_floor_volt, vdd_ceiling_volt,
+					new_volt, last_volt);
+			if (rc)
+				return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_mem_acc_bhs_used() - determines if mem-acc regulators powered
+ *		through a BHS are associated with the CPR3 controller or any of
+ *		the CPR3 regulators it controls.
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * This function determines if the CPR3 controller or any of its CPR3 regulators
+ * need to manage mem-acc regulators that are currently powered through a BHS
+ * and whose corner selection is based upon a particular voltage threshold.
+ *
+ * Return: true or false
+ */
+static bool cpr3_regulator_mem_acc_bhs_used(struct cpr3_controller *ctrl)
+{
+	struct cpr3_regulator *vreg;
+	int i, j;
+
+	if (!ctrl->mem_acc_threshold_volt)
+		return false;
+
+	if (ctrl->mem_acc_regulator)
+		return true;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+
+			if (vreg->mem_acc_regulator &&
+			    (!vreg->ldo_regulator ||
+			     vreg->ldo_regulator_bypass
+			     == BHS_MODE))
+				return true;
+		}
+	}
+
+	return false;
+}
+
+/**
+ * cpr3_regulator_config_bhs_mem_acc() - configure the mem-acc regulator
+ *		settings for hardware blocks currently powered through the BHS.
+ * @ctrl:		Pointer to the CPR3 controller
+ * @new_volt:		New voltage in microvolts that VDD supply needs to
+ *			end up at
+ * @last_volt:		Pointer to the last known voltage in microvolts for the
+ *			VDD supply
+ * @aggr_corner:	Pointer to the CPR3 corner which corresponds to the max
+ *			corner aggregated from all CPR3 threads managed by the
+ *			CPR3 controller
+ *
+ * This function programs the mem-acc regulator corners for CPR3 regulators
+ * whose LDO regulators are in bypassed state. The function also handles
+ * CPR3 controllers which utilize mem-acc regulators that operate independently
+ * from the LDO hardware and that must be programmed when the VDD supply
+ * crosses a particular voltage threshold.
+ *
+ * Return: 0 on success, errno on failure. If the VDD supply voltage is
+ * modified, last_volt is updated to reflect the new voltage setpoint.
+ */
+static int cpr3_regulator_config_bhs_mem_acc(struct cpr3_controller *ctrl,
+				     int new_volt, int *last_volt,
+				     struct cpr3_corner *aggr_corner)
+{
+	struct cpr3_regulator *vreg;
+	int i, j, rc, mem_acc_corn, safe_volt;
+	int mem_acc_volt = ctrl->mem_acc_threshold_volt;
+	int ref_volt;
+
+	if (!cpr3_regulator_mem_acc_bhs_used(ctrl))
+		return 0;
+
+	ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt :
+		new_volt;
+
+	if (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) ||
+	     (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))) {
+		if (ref_volt < *last_volt)
+			safe_volt = max(mem_acc_volt, aggr_corner->last_volt);
+		else
+			safe_volt = max(mem_acc_volt, *last_volt);
+
+		rc = regulator_set_voltage(ctrl->vdd_regulator, safe_volt,
+					   new_volt < *last_volt ?
+					   ctrl->aggr_corner.ceiling_volt :
+					   new_volt);
+		if (rc) {
+			cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
+				 safe_volt, rc);
+			return rc;
+		}
+
+		*last_volt = safe_volt;
+
+		mem_acc_corn = ref_volt < mem_acc_volt ?
+			ctrl->mem_acc_corner_map[CPR3_MEM_ACC_LOW_CORNER] :
+			ctrl->mem_acc_corner_map[CPR3_MEM_ACC_HIGH_CORNER];
+
+		if (ctrl->mem_acc_regulator) {
+			rc = regulator_set_voltage(ctrl->mem_acc_regulator,
+						   mem_acc_corn, mem_acc_corn);
+			if (rc) {
+				cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
+					 mem_acc_corn, rc);
+				return rc;
+			}
+		}
+
+		for (i = 0; i < ctrl->thread_count; i++) {
+			for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+				vreg = &ctrl->thread[i].vreg[j];
+
+				if (!vreg->mem_acc_regulator ||
+				    (vreg->ldo_regulator &&
+				     vreg->ldo_regulator_bypass
+				     == LDO_MODE))
+					continue;
+
+				rc = regulator_set_voltage(
+					vreg->mem_acc_regulator, mem_acc_corn,
+					mem_acc_corn);
+				if (rc) {
+					cpr3_err(vreg, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
+						 mem_acc_corn, rc);
+					return rc;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_switch_apm_mode() - switch the mode of the APM controller
+ *		associated with a given CPR3 controller
+ * @ctrl:		Pointer to the CPR3 controller
+ * @new_volt:		New voltage in microvolts that VDD supply needs to
+ *			end up at
+ * @last_volt:		Pointer to the last known voltage in microvolts for the
+ *			VDD supply
+ * @aggr_corner:	Pointer to the CPR3 corner which corresponds to the max
+ *			corner aggregated from all CPR3 threads managed by the
+ *			CPR3 controller
+ *
+ * This function requests a switch of the APM mode while guaranteeing
+ * any LDO regulator hardware requirements are satisfied. The function must
+ * be called once it is known a new VDD supply setpoint crosses the APM
+ * voltage threshold.
+ *
+ * Return: 0 on success, errno on failure. If the VDD supply voltage is
+ * modified, last_volt is updated to reflect the new voltage setpoint.
+ */
+static int cpr3_regulator_switch_apm_mode(struct cpr3_controller *ctrl,
+					  int new_volt, int *last_volt,
+					  struct cpr3_corner *aggr_corner)
+{
+	struct regulator *vdd = ctrl->vdd_regulator;
+	int apm_volt = ctrl->apm_threshold_volt;
+	int orig_last_volt = *last_volt;
+	int rc;
+
+	rc = regulator_set_voltage(vdd, apm_volt, apm_volt);
+	if (rc) {
+		cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
+			 apm_volt, rc);
+		return rc;
+	}
+
+	*last_volt = apm_volt;
+
+	rc = cpr3_regulator_ldo_apm_prepare(ctrl, new_volt, *last_volt,
+					    aggr_corner);
+	if (rc) {
+		cpr3_err(ctrl, "unable to prepare LDO state for APM switch, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	rc = msm_apm_set_supply(ctrl->apm, new_volt >= apm_volt
+				? ctrl->apm_high_supply : ctrl->apm_low_supply);
+	if (rc) {
+		cpr3_err(ctrl, "APM switch failed, rc=%d\n", rc);
+		/* Roll back the voltage. */
+		regulator_set_voltage(vdd, orig_last_volt, INT_MAX);
+		*last_volt = orig_last_volt;
+		return rc;
+	}
+	return 0;
+}
+
+/**
+ * cpr3_regulator_config_voltage_crossings() - configure APM and mem-acc
+ *		settings depending upon a new VDD supply setpoint
+ *
+ * @ctrl:		Pointer to the CPR3 controller
+ * @new_volt:		New voltage in microvolts that VDD supply needs to
+ *			end up at
+ * @last_volt:		Pointer to the last known voltage in microvolts for the
+ *			VDD supply
+ * @aggr_corner:	Pointer to the CPR3 corner which corresponds to the max
+ *			corner aggregated from all CPR3 threads managed by the
+ *			CPR3 controller
+ *
+ * This function handles the APM and mem-acc regulator reconfiguration if
+ * the new VDD supply voltage will result in crossing their respective voltage
+ * thresholds.
+ *
+ * Return: 0 on success, errno on failure. If the VDD supply voltage is
+ * modified, last_volt is updated to reflect the new voltage setpoint.
+ */
+static int cpr3_regulator_config_voltage_crossings(struct cpr3_controller *ctrl,
+				   int new_volt, int *last_volt,
+				   struct cpr3_corner *aggr_corner)
+{
+	bool apm_crossing = false, mem_acc_crossing = false;
+	bool mem_acc_bhs_used;
+	int apm_volt = ctrl->apm_threshold_volt;
+	int mem_acc_volt = ctrl->mem_acc_threshold_volt;
+	int ref_volt, rc;
+
+	if (ctrl->apm && apm_volt > 0
+	    && ((*last_volt < apm_volt && apm_volt <= new_volt)
+		|| (*last_volt >= apm_volt && apm_volt > new_volt)))
+		apm_crossing = true;
+
+	mem_acc_bhs_used = cpr3_regulator_mem_acc_bhs_used(ctrl);
+
+	ref_volt = ctrl->use_hw_closed_loop ? aggr_corner->floor_volt :
+		new_volt;
+
+	if (mem_acc_bhs_used &&
+	    (((*last_volt < mem_acc_volt && mem_acc_volt <= ref_volt) ||
+	      (*last_volt >= mem_acc_volt && mem_acc_volt > ref_volt))))
+		mem_acc_crossing = true;
+
+	if (apm_crossing && mem_acc_crossing) {
+		if ((new_volt < *last_volt && apm_volt >= mem_acc_volt) ||
+		    (new_volt >= *last_volt && apm_volt < mem_acc_volt)) {
+			rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt,
+							    last_volt,
+							    aggr_corner);
+			if (rc) {
+				cpr3_err(ctrl, "unable to switch APM mode\n");
+				return rc;
+			}
+
+			rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt,
+						       last_volt, aggr_corner);
+			if (rc) {
+				cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n");
+				return rc;
+			}
+		} else {
+			rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt,
+						       last_volt, aggr_corner);
+			if (rc) {
+				cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n");
+				return rc;
+			}
+
+			rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt,
+							    last_volt,
+							    aggr_corner);
+			if (rc) {
+				cpr3_err(ctrl, "unable to switch APM mode\n");
+				return rc;
+			}
+		}
+	} else if (apm_crossing) {
+		rc = cpr3_regulator_switch_apm_mode(ctrl, new_volt, last_volt,
+						    aggr_corner);
+		if (rc) {
+			cpr3_err(ctrl, "unable to switch APM mode\n");
+			return rc;
+		}
+	} else if (mem_acc_crossing) {
+		rc = cpr3_regulator_config_bhs_mem_acc(ctrl, new_volt,
+						       last_volt, aggr_corner);
+		if (rc) {
+			cpr3_err(ctrl, "unable to configure BHS mem-acc settings\n");
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_config_mem_acc() - configure the corner of the mem-acc
+ *			regulator associated with the CPR3 controller
+ * @ctrl:		Pointer to the CPR3 controller
+ * @aggr_corner:	Pointer to the CPR3 corner which corresponds to the max
+ *			corner aggregated from all CPR3 threads managed by the
+ *			CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_config_mem_acc(struct cpr3_controller *ctrl,
+					 struct cpr3_corner *aggr_corner)
+{
+	int rc;
+
+	if (ctrl->mem_acc_regulator && aggr_corner->mem_acc_volt) {
+		rc = regulator_set_voltage(ctrl->mem_acc_regulator,
+					   aggr_corner->mem_acc_volt,
+					   aggr_corner->mem_acc_volt);
+		if (rc) {
+			cpr3_err(ctrl, "regulator_set_voltage(mem_acc) == %d failed, rc=%d\n",
+				 aggr_corner->mem_acc_volt, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_scale_vdd_voltage() - scale the CPR controlled VDD supply
+ *		voltage to the new level while satisfying any other hardware
+ *		requirements
+ * @ctrl:		Pointer to the CPR3 controller
+ * @new_volt:		New voltage in microvolts that VDD supply needs to end
+ *			up at
+ * @last_volt:		Last known voltage in microvolts for the VDD supply
+ * @aggr_corner:	Pointer to the CPR3 corner which corresponds to the max
+ *			corner aggregated from all CPR3 threads managed by the
+ *			CPR3 controller
+ *
+ * This function scales the CPR controlled VDD supply voltage from its
+ * current level to the new voltage that is specified.  If the supply is
+ * configured to use the APM and the APM threshold is crossed as a result of
+ * the voltage scaling, then this function also stops at the APM threshold,
+ * switches the APM source, and finally sets the final new voltage.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_scale_vdd_voltage(struct cpr3_controller *ctrl,
+				int new_volt, int last_volt,
+				struct cpr3_corner *aggr_corner)
+{
+	struct regulator *vdd = ctrl->vdd_regulator;
+	int rc;
+
+	if (new_volt < last_volt) {
+		if (ctrl->support_ldo300_vreg) {
+			rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner);
+			if (rc)
+				return rc;
+		}
+
+		/* Decreasing VDD voltage */
+		rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
+					       ctrl->aggr_corner.ceiling_volt,
+					       new_volt, last_volt);
+		if (rc) {
+			cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
+				 rc);
+			return rc;
+		}
+
+		if (!ctrl->support_ldo300_vreg) {
+			rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner);
+			if (rc)
+				return rc;
+		}
+	} else {
+		/* Increasing VDD voltage */
+		if (ctrl->system_regulator) {
+			rc = regulator_set_voltage(ctrl->system_regulator,
+				aggr_corner->system_volt, INT_MAX);
+			if (rc) {
+				cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n",
+					aggr_corner->system_volt, rc);
+				return rc;
+			}
+		}
+	}
+
+	rc = cpr3_regulator_config_voltage_crossings(ctrl, new_volt, &last_volt,
+						     aggr_corner);
+	if (rc) {
+		cpr3_err(ctrl, "unable to handle voltage threshold crossing configurations, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	/*
+	 * Subtract a small amount from the min_uV parameter so that the
+	 * set voltage request is not dropped by the framework due to being
+	 * duplicate.  This is needed in order to switch from hardware
+	 * closed-loop to open-loop successfully.
+	 */
+	rc = regulator_set_voltage(vdd, new_volt - (ctrl->cpr_enabled ? 0 : 1),
+				   aggr_corner->ceiling_volt);
+	if (rc) {
+		cpr3_err(ctrl, "regulator_set_voltage(vdd) == %d failed, rc=%d\n",
+			new_volt, rc);
+		return rc;
+	}
+
+	if (new_volt == last_volt && ctrl->supports_hw_closed_loop
+	    && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		/*
+		 * CPR4 features enforce voltage reprogramming when the last
+		 * set voltage and new set voltage are same. This way, we can
+		 * ensure that SAW PMIC STATUS register is updated with newly
+		 * programmed voltage.
+		 */
+		rc = regulator_sync_voltage(vdd);
+		if (rc) {
+			cpr3_err(ctrl, "regulator_sync_voltage(vdd) == %d failed, rc=%d\n",
+				new_volt, rc);
+			return rc;
+		}
+	}
+
+	if (new_volt >= last_volt) {
+		/* Increasing VDD voltage */
+		rc = cpr3_regulator_config_ldo(ctrl, aggr_corner->floor_volt,
+					       aggr_corner->ceiling_volt,
+					       new_volt, new_volt);
+		if (rc) {
+			cpr3_err(ctrl, "unable to configure LDO state, rc=%d\n",
+				 rc);
+			return rc;
+		}
+
+		rc = cpr3_regulator_config_mem_acc(ctrl, aggr_corner);
+		if (rc)
+			return rc;
+	} else {
+		/* Decreasing VDD voltage */
+		if (ctrl->system_regulator) {
+			rc = regulator_set_voltage(ctrl->system_regulator,
+				aggr_corner->system_volt, INT_MAX);
+			if (rc) {
+				cpr3_err(ctrl, "regulator_set_voltage(system) == %d failed, rc=%d\n",
+					aggr_corner->system_volt, rc);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_get_dynamic_floor_volt() - returns the current dynamic floor
+ *		voltage based upon static configurations and the state of all
+ *		power domains during the last CPR measurement
+ * @ctrl:		Pointer to the CPR3 controller
+ * @reg_last_measurement: Value read from the LAST_MEASUREMENT register
+ *
+ * When using HW closed-loop, the dynamic floor voltage is always returned
+ * regardless of the current state of the power domains.
+ *
+ * Return: dynamic floor voltage in microvolts or 0 if dynamic floor is not
+ *         currently required
+ */
+static int cpr3_regulator_get_dynamic_floor_volt(struct cpr3_controller *ctrl,
+		u32 reg_last_measurement)
+{
+	int dynamic_floor_volt = 0;
+	struct cpr3_regulator *vreg;
+	bool valid, pd_valid;
+	u32 bypass_bits;
+	int i, j;
+
+	if (!ctrl->supports_hw_closed_loop)
+		return 0;
+
+	if (likely(!ctrl->use_hw_closed_loop)) {
+		valid = !!(reg_last_measurement & CPR3_LAST_MEASUREMENT_VALID);
+		bypass_bits
+		 = (reg_last_measurement & CPR3_LAST_MEASUREMENT_PD_BYPASS_MASK)
+			>> CPR3_LAST_MEASUREMENT_PD_BYPASS_SHIFT;
+	} else {
+		/*
+		 * Ensure that the dynamic floor voltage is always used for
+		 * HW closed-loop since the conditions below cannot be evaluated
+		 * after each CPR measurement.
+		 */
+		valid = false;
+		bypass_bits = 0;
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+
+			if (!vreg->uses_dynamic_floor)
+				continue;
+
+			pd_valid = !((bypass_bits & vreg->pd_bypass_mask)
+					== vreg->pd_bypass_mask);
+
+			if (!valid || !pd_valid)
+				dynamic_floor_volt = max(dynamic_floor_volt,
+					vreg->corner[
+					 vreg->dynamic_floor_corner].last_volt);
+		}
+	}
+
+	return dynamic_floor_volt;
+}
+
+/**
+ * cpr3_regulator_max_sdelta_diff() - returns the maximum voltage difference in
+ *		microvolts that can result from different operating conditions
+ *		for the specified sdelta struct
+ * @sdelta:		Pointer to the sdelta structure
+ * @step_volt:		Step size in microvolts between available set
+ *			points of the VDD supply.
+ *
+ * Return: voltage difference between the highest and lowest adjustments if
+ *	sdelta and sdelta->table are valid, else 0.
+ */
+static int cpr3_regulator_max_sdelta_diff(const struct cpr4_sdelta *sdelta,
+				int step_volt)
+{
+	int i, j, index, sdelta_min = INT_MAX, sdelta_max = INT_MIN;
+
+	if (!sdelta || !sdelta->table)
+		return 0;
+
+	for (i = 0; i < sdelta->max_core_count; i++) {
+		for (j = 0; j < sdelta->temp_band_count; j++) {
+			index = i * sdelta->temp_band_count + j;
+			sdelta_min = min(sdelta_min, sdelta->table[index]);
+			sdelta_max = max(sdelta_max, sdelta->table[index]);
+		}
+	}
+
+	return (sdelta_max - sdelta_min) * step_volt;
+}
+
+/**
+ * cpr3_regulator_aggregate_sdelta() - check open-loop voltages of current
+ *		aggregated corner and current corner of a given regulator
+ *		and adjust the sdelta strucuture data of aggregate corner.
+ * @aggr_corner:	Pointer to accumulated aggregated corner which
+ *			is both an input and an output
+ * @corner:		Pointer to the corner to be aggregated with
+ *			aggr_corner
+ * @step_volt:		Step size in microvolts between available set
+ *			points of the VDD supply.
+ *
+ * Return: none
+ */
+static void cpr3_regulator_aggregate_sdelta(
+				struct cpr3_corner *aggr_corner,
+				const struct cpr3_corner *corner, int step_volt)
+{
+	struct cpr4_sdelta *aggr_sdelta, *sdelta;
+	int aggr_core_count, core_count, temp_band_count;
+	u32 aggr_index, index;
+	int i, j, sdelta_size, cap_steps, adjust_sdelta;
+
+	aggr_sdelta = aggr_corner->sdelta;
+	sdelta = corner->sdelta;
+
+	if (aggr_corner->open_loop_volt < corner->open_loop_volt) {
+		/*
+		 * Found the new dominant regulator as its open-loop requirement
+		 * is higher than previous dominant regulator. Calculate cap
+		 * voltage to limit the SDELTA values to make sure the runtime
+		 * (Core-count/temp) adjustments do not violate other
+		 * regulators' voltage requirements. Use cpr4_sdelta values of
+		 * new dominant regulator.
+		 */
+		aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt,
+						(corner->open_loop_volt -
+						aggr_corner->open_loop_volt));
+
+		/* Clear old data in the sdelta table */
+		sdelta_size = aggr_sdelta->max_core_count
+					* aggr_sdelta->temp_band_count;
+
+		if (aggr_sdelta->allow_core_count_adj
+			|| aggr_sdelta->allow_temp_adj)
+			memset(aggr_sdelta->table, 0, sdelta_size
+					* sizeof(*aggr_sdelta->table));
+
+		if (sdelta->allow_temp_adj || sdelta->allow_core_count_adj) {
+			/* Copy new data in sdelta table */
+			sdelta_size = sdelta->max_core_count
+						* sdelta->temp_band_count;
+			if (sdelta->table)
+				memcpy(aggr_sdelta->table, sdelta->table,
+					sdelta_size * sizeof(*sdelta->table));
+		}
+
+		if (sdelta->allow_boost) {
+			memcpy(aggr_sdelta->boost_table, sdelta->boost_table,
+				sdelta->temp_band_count
+				* sizeof(*sdelta->boost_table));
+			aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
+		} else if (aggr_sdelta->allow_boost) {
+			for (i = 0; i < aggr_sdelta->temp_band_count; i++) {
+				adjust_sdelta = (corner->open_loop_volt
+						- aggr_corner->open_loop_volt)
+						/ step_volt;
+				aggr_sdelta->boost_table[i] += adjust_sdelta;
+				aggr_sdelta->boost_table[i]
+					= min(aggr_sdelta->boost_table[i], 0);
+			}
+		}
+
+		aggr_corner->open_loop_volt = corner->open_loop_volt;
+		aggr_sdelta->allow_temp_adj = sdelta->allow_temp_adj;
+		aggr_sdelta->allow_core_count_adj
+					= sdelta->allow_core_count_adj;
+		aggr_sdelta->max_core_count = sdelta->max_core_count;
+		aggr_sdelta->temp_band_count = sdelta->temp_band_count;
+	} else if (aggr_corner->open_loop_volt > corner->open_loop_volt) {
+		/*
+		 * Adjust the cap voltage if the open-loop requirement of new
+		 * regulator is the next highest.
+		 */
+		aggr_sdelta->cap_volt = min(aggr_sdelta->cap_volt,
+						(aggr_corner->open_loop_volt
+						- corner->open_loop_volt));
+
+		if (sdelta->allow_boost) {
+			for (i = 0; i < aggr_sdelta->temp_band_count; i++) {
+				adjust_sdelta = (aggr_corner->open_loop_volt
+						- corner->open_loop_volt)
+						/ step_volt;
+				aggr_sdelta->boost_table[i] =
+					sdelta->boost_table[i] + adjust_sdelta;
+				aggr_sdelta->boost_table[i]
+					= min(aggr_sdelta->boost_table[i], 0);
+			}
+			aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
+		}
+	} else {
+		/*
+		 * Found another dominant regulator with same open-loop
+		 * requirement. Make cap voltage to '0'. Disable core-count
+		 * adjustments as we couldn't support for both regulators.
+		 * Keep enable temp based adjustments if enabled for both
+		 * regulators and choose mininum margin adjustment values
+		 * between them.
+		 */
+		aggr_sdelta->cap_volt = 0;
+		aggr_sdelta->allow_core_count_adj = false;
+
+		if (aggr_sdelta->allow_temp_adj
+					&& sdelta->allow_temp_adj) {
+			aggr_core_count = aggr_sdelta->max_core_count - 1;
+			core_count = sdelta->max_core_count - 1;
+			temp_band_count = sdelta->temp_band_count;
+			for (j = 0; j < temp_band_count; j++) {
+				aggr_index = aggr_core_count * temp_band_count
+						+ j;
+				index = core_count * temp_band_count + j;
+				aggr_sdelta->table[aggr_index] =
+					min(aggr_sdelta->table[aggr_index],
+						sdelta->table[index]);
+			}
+		} else {
+			aggr_sdelta->allow_temp_adj = false;
+		}
+
+		if (sdelta->allow_boost) {
+			memcpy(aggr_sdelta->boost_table, sdelta->boost_table,
+				sdelta->temp_band_count
+				* sizeof(*sdelta->boost_table));
+			aggr_sdelta->boost_num_cores = sdelta->boost_num_cores;
+		}
+	}
+
+	/* Keep non-dominant clients boost enable state */
+	aggr_sdelta->allow_boost |= sdelta->allow_boost;
+	if (aggr_sdelta->allow_boost)
+		aggr_sdelta->allow_core_count_adj = false;
+
+	if (aggr_sdelta->cap_volt && !(aggr_sdelta->cap_volt == INT_MAX)) {
+		core_count = aggr_sdelta->max_core_count;
+		temp_band_count = aggr_sdelta->temp_band_count;
+		/*
+		 * Convert cap voltage from uV to PMIC steps and use to limit
+		 * sdelta margin adjustments.
+		 */
+		cap_steps = aggr_sdelta->cap_volt / step_volt;
+		for (i = 0; i < core_count; i++)
+			for (j = 0; j < temp_band_count; j++) {
+				index = i * temp_band_count + j;
+				aggr_sdelta->table[index] =
+						min(aggr_sdelta->table[index],
+							cap_steps);
+		}
+	}
+}
+
+/**
+ * cpr3_regulator_aggregate_corners() - aggregate two corners together
+ * @aggr_corner:		Pointer to accumulated aggregated corner which
+ *				is both an input and an output
+ * @corner:			Pointer to the corner to be aggregated with
+ *				aggr_corner
+ * @aggr_quot:			Flag indicating that target quotients should be
+ *				aggregated as well.
+ * @step_volt:			Step size in microvolts between available set
+ *				points of the VDD supply.
+ *
+ * Return: none
+ */
+static void cpr3_regulator_aggregate_corners(struct cpr3_corner *aggr_corner,
+			const struct cpr3_corner *corner, bool aggr_quot,
+			int step_volt)
+{
+	int i;
+
+	aggr_corner->ceiling_volt
+		= max(aggr_corner->ceiling_volt, corner->ceiling_volt);
+	aggr_corner->floor_volt
+		= max(aggr_corner->floor_volt, corner->floor_volt);
+	aggr_corner->last_volt
+		= max(aggr_corner->last_volt, corner->last_volt);
+	aggr_corner->system_volt
+		= max(aggr_corner->system_volt, corner->system_volt);
+	aggr_corner->mem_acc_volt
+		= max(aggr_corner->mem_acc_volt, corner->mem_acc_volt);
+	aggr_corner->irq_en |= corner->irq_en;
+	aggr_corner->use_open_loop |= corner->use_open_loop;
+	aggr_corner->ldo_mode_allowed |= corner->ldo_mode_allowed;
+
+	if (aggr_quot) {
+		aggr_corner->ro_mask &= corner->ro_mask;
+
+		for (i = 0; i < CPR3_RO_COUNT; i++)
+			aggr_corner->target_quot[i]
+				= max(aggr_corner->target_quot[i],
+				      corner->target_quot[i]);
+	}
+
+	if (aggr_corner->sdelta && corner->sdelta
+		&& (aggr_corner->sdelta->table
+		|| aggr_corner->sdelta->boost_table)) {
+		cpr3_regulator_aggregate_sdelta(aggr_corner, corner, step_volt);
+	} else {
+		aggr_corner->open_loop_volt
+			= max(aggr_corner->open_loop_volt,
+				corner->open_loop_volt);
+	}
+}
+
+/**
+ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller
+ *		to reflect the corners used by all CPR3 regulators as well as
+ *		the CPR operating mode
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * This function aggregates the CPR parameters for all CPR3 regulators
+ * associated with the VDD supply.  Upon success, it sets the aggregated last
+ * known good voltage.
+ *
+ * The VDD supply voltage will not be physically configured unless this
+ * condition is met by at least one of the regulators of the controller:
+ * regulator->vreg_enabled == true &&
+ * regulator->current_corner != CPR3_REGULATOR_CORNER_INVALID
+ *
+ * CPR registers for the controller and each thread are updated as long as
+ * ctrl->cpr_enabled == true.
+ *
+ * Note, CPR3 controller lock must be held by the caller.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int _cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
+{
+	struct cpr3_corner aggr_corner = {};
+	struct cpr3_thread *thread;
+	struct cpr3_regulator *vreg;
+	struct cpr4_sdelta *sdelta;
+	bool valid = false;
+	bool thread_valid;
+	int i, j, rc, new_volt, vdd_volt, dynamic_floor_volt, last_corner_volt;
+	u32 reg_last_measurement = 0, sdelta_size;
+	int *sdelta_table, *boost_table;
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	cpr3_ctrl_loop_disable(ctrl);
+
+	vdd_volt = regulator_get_voltage(ctrl->vdd_regulator);
+	if (vdd_volt < 0) {
+		cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n",
+			 vdd_volt);
+		return vdd_volt;
+	}
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		/*
+		 * Save aggregated corner open-loop voltage which was programmed
+		 * during last corner switch which is used when programming new
+		 * aggregated corner open-loop voltage.
+		 */
+		last_corner_volt = ctrl->aggr_corner.open_loop_volt;
+	}
+
+	if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop &&
+		ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
+		reg_last_measurement
+			= cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT);
+
+	aggr_corner.sdelta = ctrl->aggr_corner.sdelta;
+	if (aggr_corner.sdelta) {
+		sdelta = aggr_corner.sdelta;
+		sdelta_table = sdelta->table;
+		if (sdelta_table) {
+			sdelta_size = sdelta->max_core_count *
+					sdelta->temp_band_count;
+			memset(sdelta_table, 0, sdelta_size
+					* sizeof(*sdelta_table));
+		}
+
+		boost_table = sdelta->boost_table;
+		if (boost_table)
+			memset(boost_table, 0, sdelta->temp_band_count
+					* sizeof(*boost_table));
+
+		memset(sdelta, 0, sizeof(*sdelta));
+		sdelta->table = sdelta_table;
+		sdelta->cap_volt = INT_MAX;
+		sdelta->boost_table = boost_table;
+	}
+
+	/* Aggregate the requests of all threads */
+	for (i = 0; i < ctrl->thread_count; i++) {
+		thread = &ctrl->thread[i];
+		thread_valid = false;
+
+		sdelta = thread->aggr_corner.sdelta;
+		if (sdelta) {
+			sdelta_table = sdelta->table;
+			if (sdelta_table) {
+				sdelta_size = sdelta->max_core_count *
+						sdelta->temp_band_count;
+				memset(sdelta_table, 0, sdelta_size
+						* sizeof(*sdelta_table));
+			}
+
+			boost_table = sdelta->boost_table;
+			if (boost_table)
+				memset(boost_table, 0, sdelta->temp_band_count
+						* sizeof(*boost_table));
+
+			memset(sdelta, 0, sizeof(*sdelta));
+			sdelta->table = sdelta_table;
+			sdelta->cap_volt = INT_MAX;
+			sdelta->boost_table = boost_table;
+		}
+
+		memset(&thread->aggr_corner, 0, sizeof(thread->aggr_corner));
+		thread->aggr_corner.sdelta = sdelta;
+		thread->aggr_corner.ro_mask = CPR3_RO_MASK;
+
+		for (j = 0; j < thread->vreg_count; j++) {
+			vreg = &thread->vreg[j];
+
+			if (ctrl->cpr_enabled && ctrl->use_hw_closed_loop)
+				cpr3_update_vreg_closed_loop_volt(vreg,
+						vdd_volt, reg_last_measurement);
+
+			if (!vreg->vreg_enabled
+			    || vreg->current_corner
+					    == CPR3_REGULATOR_CORNER_INVALID) {
+				/* Cannot participate in aggregation. */
+				vreg->aggregated = false;
+				continue;
+			} else {
+				vreg->aggregated = true;
+				thread_valid = true;
+			}
+
+			cpr3_regulator_aggregate_corners(&thread->aggr_corner,
+					&vreg->corner[vreg->current_corner],
+					true, ctrl->step_volt);
+		}
+
+		valid |= thread_valid;
+
+		if (thread_valid)
+			cpr3_regulator_aggregate_corners(&aggr_corner,
+					&thread->aggr_corner,
+					false, ctrl->step_volt);
+	}
+
+	if (valid && ctrl->cpr_allowed_hw && ctrl->cpr_allowed_sw) {
+		rc = cpr3_closed_loop_enable(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc);
+			return rc;
+		}
+	} else {
+		rc = cpr3_closed_loop_disable(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* No threads are enabled with a valid corner so exit. */
+	if (!valid)
+		return 0;
+
+	/*
+	 * When using CPR hardware closed-loop, the voltage may vary anywhere
+	 * between the floor and ceiling voltage without software notification.
+	 * Therefore, it is required that the floor to ceiling range for the
+	 * aggregated corner not intersect the APM threshold voltage.  Adjust
+	 * the floor to ceiling range if this requirement is violated.
+	 *
+	 * The following algorithm is applied in the case that
+	 * floor < threshold <= ceiling:
+	 *	if open_loop >= threshold - adj, then floor = threshold
+	 *	else ceiling = threshold - step
+	 * where adj = an adjustment factor to ensure sufficient voltage margin
+	 * and step = VDD output step size
+	 *
+	 * The open-loop and last known voltages are also bounded by the new
+	 * floor or ceiling value as needed.
+	 */
+	if (ctrl->use_hw_closed_loop
+	    && aggr_corner.ceiling_volt >= ctrl->apm_threshold_volt
+	    && aggr_corner.floor_volt < ctrl->apm_threshold_volt) {
+
+		if (aggr_corner.open_loop_volt
+		    >= ctrl->apm_threshold_volt - ctrl->apm_adj_volt)
+			aggr_corner.floor_volt = ctrl->apm_threshold_volt;
+		else
+			aggr_corner.ceiling_volt
+				= ctrl->apm_threshold_volt - ctrl->step_volt;
+
+		aggr_corner.last_volt
+		    = max(aggr_corner.last_volt, aggr_corner.floor_volt);
+		aggr_corner.last_volt
+		    = min(aggr_corner.last_volt, aggr_corner.ceiling_volt);
+		aggr_corner.open_loop_volt
+		    = max(aggr_corner.open_loop_volt, aggr_corner.floor_volt);
+		aggr_corner.open_loop_volt
+		    = min(aggr_corner.open_loop_volt, aggr_corner.ceiling_volt);
+	}
+
+	if (ctrl->use_hw_closed_loop
+	    && aggr_corner.ceiling_volt >= ctrl->mem_acc_threshold_volt
+	    && aggr_corner.floor_volt < ctrl->mem_acc_threshold_volt) {
+		aggr_corner.floor_volt = ctrl->mem_acc_threshold_volt;
+		aggr_corner.last_volt = max(aggr_corner.last_volt,
+					     aggr_corner.floor_volt);
+		aggr_corner.open_loop_volt = max(aggr_corner.open_loop_volt,
+						  aggr_corner.floor_volt);
+	}
+
+	if (ctrl->use_hw_closed_loop) {
+		dynamic_floor_volt
+			= cpr3_regulator_get_dynamic_floor_volt(ctrl,
+							reg_last_measurement);
+		if (aggr_corner.floor_volt < dynamic_floor_volt) {
+			aggr_corner.floor_volt = dynamic_floor_volt;
+			aggr_corner.last_volt = max(aggr_corner.last_volt,
+							aggr_corner.floor_volt);
+			aggr_corner.open_loop_volt
+				= max(aggr_corner.open_loop_volt,
+					aggr_corner.floor_volt);
+			aggr_corner.ceiling_volt = max(aggr_corner.ceiling_volt,
+							aggr_corner.floor_volt);
+		}
+	}
+
+	if (ctrl->cpr_enabled && ctrl->last_corner_was_closed_loop) {
+		/*
+		 * Always program open-loop voltage for CPR4 controllers which
+		 * support hardware closed-loop.  Storing the last closed loop
+		 * voltage in corner structure can still help with debugging.
+		 */
+		if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
+			new_volt = aggr_corner.last_volt;
+		else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4
+			 && ctrl->supports_hw_closed_loop)
+			new_volt = aggr_corner.open_loop_volt;
+		else
+			new_volt = min(aggr_corner.last_volt +
+			      cpr3_regulator_max_sdelta_diff(aggr_corner.sdelta,
+							     ctrl->step_volt),
+				       aggr_corner.ceiling_volt);
+	} else {
+		new_volt = aggr_corner.open_loop_volt;
+		aggr_corner.last_volt = aggr_corner.open_loop_volt;
+	}
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4
+	    && ctrl->supports_hw_closed_loop) {
+		/*
+		 * Store last aggregated corner open-loop voltage in vdd_volt
+		 * which is used when programming current aggregated corner
+		 * required voltage.
+		 */
+		vdd_volt = last_corner_volt;
+	}
+
+	cpr3_debug(ctrl, "setting new voltage=%d uV\n", new_volt);
+	rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt,
+					      vdd_volt, &aggr_corner);
+	if (rc) {
+		cpr3_err(ctrl, "vdd voltage scaling failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Only update registers if CPR is enabled. */
+	if (ctrl->cpr_enabled) {
+		if (ctrl->use_hw_closed_loop) {
+			/* Hardware closed-loop */
+
+			/* Set ceiling and floor limits in hardware */
+			rc = regulator_set_voltage(ctrl->vdd_limit_regulator,
+				aggr_corner.floor_volt,
+				aggr_corner.ceiling_volt);
+			if (rc) {
+				cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n",
+					rc);
+				return rc;
+			}
+		} else {
+			/* Software closed-loop */
+
+			/*
+			 * Disable UP or DOWN interrupts when at ceiling or
+			 * floor respectively.
+			 */
+			if (new_volt == aggr_corner.floor_volt)
+				aggr_corner.irq_en &= ~CPR3_IRQ_DOWN;
+			if (new_volt == aggr_corner.ceiling_volt)
+				aggr_corner.irq_en &= ~CPR3_IRQ_UP;
+
+			cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR,
+				CPR3_IRQ_UP | CPR3_IRQ_DOWN);
+			cpr3_write(ctrl, CPR3_REG_IRQ_EN, aggr_corner.irq_en);
+		}
+
+		for (i = 0; i < ctrl->thread_count; i++) {
+			cpr3_regulator_set_target_quot(&ctrl->thread[i]);
+
+			for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+				vreg = &ctrl->thread[i].vreg[j];
+
+				if (vreg->vreg_enabled)
+					vreg->last_closed_loop_corner
+						= vreg->current_corner;
+			}
+		}
+
+		if (ctrl->proc_clock_throttle) {
+			if (aggr_corner.ceiling_volt > aggr_corner.floor_volt
+			    && (ctrl->use_hw_closed_loop
+					|| new_volt < aggr_corner.ceiling_volt))
+				cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
+						ctrl->proc_clock_throttle);
+			else
+				cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
+						CPR3_PD_THROTTLE_DISABLE);
+		}
+
+		/*
+		 * Ensure that all CPR register writes complete before
+		 * re-enabling CPR loop operation.
+		 */
+		wmb();
+	} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4
+		   && ctrl->vdd_limit_regulator) {
+		/* Set ceiling and floor limits in hardware */
+		rc = regulator_set_voltage(ctrl->vdd_limit_regulator,
+			aggr_corner.floor_volt,
+			aggr_corner.ceiling_volt);
+		if (rc) {
+			cpr3_err(ctrl, "could not configure HW closed-loop voltage limits, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	ctrl->aggr_corner = aggr_corner;
+
+	if (ctrl->allow_core_count_adj || ctrl->allow_temp_adj
+		|| ctrl->allow_boost) {
+		rc = cpr3_controller_program_sdelta(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "failed to program sdelta, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/*
+	 * Only enable the CPR controller if it is possible to set more than
+	 * one vdd-supply voltage.
+	 */
+	if (aggr_corner.ceiling_volt > aggr_corner.floor_volt &&
+			!aggr_corner.use_open_loop)
+		cpr3_ctrl_loop_enable(ctrl);
+
+	ctrl->last_corner_was_closed_loop = ctrl->cpr_enabled;
+	cpr3_debug(ctrl, "CPR configuration updated\n");
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_wait_for_idle() - wait for the CPR controller to no longer be
+ *		busy
+ * @ctrl:		Pointer to the CPR3 controller
+ * @max_wait_ns:	Max wait time in nanoseconds
+ *
+ * Return: 0 on success or -ETIMEDOUT if the controller was still busy after
+ *	   the maximum delay time
+ */
+static int cpr3_regulator_wait_for_idle(struct cpr3_controller *ctrl,
+					s64 max_wait_ns)
+{
+	ktime_t start, end;
+	s64 time_ns;
+	u32 reg;
+
+	/*
+	 * Ensure that all previous CPR register writes have completed before
+	 * checking the status register.
+	 */
+	mb();
+
+	start = ktime_get();
+	do {
+		end = ktime_get();
+		time_ns = ktime_to_ns(ktime_sub(end, start));
+		if (time_ns > max_wait_ns) {
+			cpr3_err(ctrl, "CPR controller still busy after %lld us\n",
+				div_s64(time_ns, 1000));
+			return -ETIMEDOUT;
+		}
+		usleep_range(50, 100);
+		reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS);
+	} while (reg & CPR3_CPR_STATUS_BUSY_MASK);
+
+	return 0;
+}
+
+/**
+ * cmp_int() - int comparison function to be passed into the sort() function
+ *		which leads to ascending sorting
+ * @a:			First int value
+ * @b:			Second int value
+ *
+ * Return: >0 if a > b, 0 if a == b, <0 if a < b
+ */
+static int cmp_int(const void *a, const void *b)
+{
+	return *(int *)a - *(int *)b;
+}
+
+/**
+ * cpr3_regulator_measure_aging() - measure the quotient difference for the
+ *		specified CPR aging sensor
+ * @ctrl:		Pointer to the CPR3 controller
+ * @aging_sensor:	Aging sensor to measure
+ *
+ * Note that vdd-supply must be configured to the aging reference voltage before
+ * calling this function.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_measure_aging(struct cpr3_controller *ctrl,
+				struct cpr3_aging_sensor_info *aging_sensor)
+{
+	u32 mask, reg, result, quot_min, quot_max, sel_min, sel_max;
+	u32 quot_min_scaled, quot_max_scaled;
+	u32 gcnt, gcnt_ref, gcnt0_restore, gcnt1_restore, irq_restore;
+	u32 ro_mask_restore, cont_dly_restore, up_down_dly_restore = 0;
+	int quot_delta, quot_delta_scaled, quot_delta_scaled_sum;
+	int *quot_delta_results;
+	int rc, rc2, i, aging_measurement_count, filtered_count;
+	bool is_aging_measurement;
+
+	quot_delta_results = kcalloc(CPR3_AGING_MEASUREMENT_ITERATIONS,
+			sizeof(*quot_delta_results), GFP_KERNEL);
+	if (!quot_delta_results)
+		return -ENOMEM;
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc);
+			kfree(quot_delta_results);
+			return rc;
+		}
+	}
+
+	cpr3_ctrl_loop_disable(ctrl);
+
+	/* Enable up, down, and mid CPR interrupts */
+	irq_restore = cpr3_read(ctrl, CPR3_REG_IRQ_EN);
+	cpr3_write(ctrl, CPR3_REG_IRQ_EN,
+			CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID);
+
+	/* Ensure that the aging sensor is assigned to CPR thread 0 */
+	cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id), 0);
+
+	/* Switch from HW to SW closed-loop if necessary */
+	if (ctrl->supports_hw_closed_loop) {
+		if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+		    ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+			cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+				CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+				CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
+		} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+			cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
+				CPR3_HW_CLOSED_LOOP_DISABLE);
+		}
+	}
+
+	/* Configure the GCNT for RO0 and RO1 that are used for aging */
+	gcnt0_restore = cpr3_read(ctrl, CPR3_REG_GCNT(0));
+	gcnt1_restore = cpr3_read(ctrl, CPR3_REG_GCNT(1));
+	gcnt_ref = cpr3_regulator_get_gcnt(ctrl);
+	gcnt = gcnt_ref * 3 / 2;
+	cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt);
+	cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt);
+
+	/* Unmask all RO's */
+	ro_mask_restore = cpr3_read(ctrl, CPR3_REG_RO_MASK(0));
+	cpr3_write(ctrl, CPR3_REG_RO_MASK(0), 0);
+
+	/*
+	 * Mask all sensors except for the one to measure and bypass all
+	 * sensors in collapsible domains.
+	 */
+	for (i = 0; i <= ctrl->sensor_count / 32; i++) {
+		mask = GENMASK(min(31, ctrl->sensor_count - i * 32), 0);
+		if (aging_sensor->sensor_id / 32 >= i
+		    && aging_sensor->sensor_id / 32 < (i + 1))
+			mask &= ~BIT(aging_sensor->sensor_id % 32);
+		cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), mask);
+		cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i),
+				aging_sensor->bypass_mask[i]);
+	}
+
+	/* Set CPR loop delays to 0 us */
+	if (ctrl->supports_hw_closed_loop
+		&& ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		cont_dly_restore = cpr3_read(ctrl, CPR3_REG_CPR_TIMER_MID_CONT);
+		up_down_dly_restore = cpr3_read(ctrl,
+						CPR3_REG_CPR_TIMER_UP_DN_CONT);
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, 0);
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT, 0);
+	} else {
+		cont_dly_restore = cpr3_read(ctrl,
+						CPR3_REG_CPR_TIMER_AUTO_CONT);
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT, 0);
+	}
+
+	/* Set count mode to all-at-once min with no repeat */
+	cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
+		CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK,
+		CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_MIN
+			<< CPR3_CPR_CTL_COUNT_MODE_SHIFT);
+
+	cpr3_ctrl_loop_enable(ctrl);
+
+	rc = cpr3_regulator_wait_for_idle(ctrl,
+					CPR3_AGING_MEASUREMENT_TIMEOUT_NS);
+	if (rc)
+		goto cleanup;
+
+	/* Set count mode to all-at-once aging */
+	cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL, CPR3_CPR_CTL_COUNT_MODE_MASK,
+			CPR3_CPR_CTL_COUNT_MODE_ALL_AT_ONCE_AGE
+				<< CPR3_CPR_CTL_COUNT_MODE_SHIFT);
+
+	aging_measurement_count = 0;
+	for (i = 0; i < CPR3_AGING_MEASUREMENT_ITERATIONS; i++) {
+		/* Send CONT_NACK */
+		cpr3_write(ctrl, CPR3_REG_CONT_CMD, CPR3_CONT_CMD_NACK);
+
+		rc = cpr3_regulator_wait_for_idle(ctrl,
+					CPR3_AGING_MEASUREMENT_TIMEOUT_NS);
+		if (rc)
+			goto cleanup;
+
+		/* Check for PAGE_IS_AGE flag in status register */
+		reg = cpr3_read(ctrl, CPR3_REG_CPR_STATUS);
+		is_aging_measurement
+			= reg & CPR3_CPR_STATUS_AGING_MEASUREMENT_MASK;
+
+		/* Read CPR measurement results */
+		result = cpr3_read(ctrl, CPR3_REG_RESULT1(0));
+		quot_min = (result & CPR3_RESULT1_QUOT_MIN_MASK)
+				>> CPR3_RESULT1_QUOT_MIN_SHIFT;
+		quot_max = (result & CPR3_RESULT1_QUOT_MAX_MASK)
+				>> CPR3_RESULT1_QUOT_MAX_SHIFT;
+		sel_min = (result & CPR3_RESULT1_RO_MIN_MASK)
+				>> CPR3_RESULT1_RO_MIN_SHIFT;
+		sel_max = (result & CPR3_RESULT1_RO_MAX_MASK)
+				>> CPR3_RESULT1_RO_MAX_SHIFT;
+
+		/*
+		 * Scale the quotients so that they are equivalent to the fused
+		 * values.  This accounts for the difference in measurement
+		 * interval times.
+		 */
+		quot_min_scaled = quot_min * (gcnt_ref + 1) / (gcnt + 1);
+		quot_max_scaled = quot_max * (gcnt_ref + 1) / (gcnt + 1);
+
+		if (sel_max == 1) {
+			quot_delta = quot_max - quot_min;
+			quot_delta_scaled = quot_max_scaled - quot_min_scaled;
+		} else {
+			quot_delta = quot_min - quot_max;
+			quot_delta_scaled = quot_min_scaled - quot_max_scaled;
+		}
+
+		if (is_aging_measurement)
+			quot_delta_results[aging_measurement_count++]
+				= quot_delta_scaled;
+
+		cpr3_debug(ctrl, "aging results: page_is_age=%u, sel_min=%u, sel_max=%u, quot_min=%u, quot_max=%u, quot_delta=%d, quot_min_scaled=%u, quot_max_scaled=%u, quot_delta_scaled=%d\n",
+			is_aging_measurement, sel_min, sel_max, quot_min,
+			quot_max, quot_delta, quot_min_scaled, quot_max_scaled,
+			quot_delta_scaled);
+	}
+
+	filtered_count
+		= aging_measurement_count - CPR3_AGING_MEASUREMENT_FILTER * 2;
+	if (filtered_count > 0) {
+		sort(quot_delta_results, aging_measurement_count,
+			sizeof(*quot_delta_results), cmp_int, NULL);
+
+		quot_delta_scaled_sum = 0;
+		for (i = 0; i < filtered_count; i++)
+			quot_delta_scaled_sum
+				+= quot_delta_results[i
+					+ CPR3_AGING_MEASUREMENT_FILTER];
+
+		aging_sensor->measured_quot_diff
+			= quot_delta_scaled_sum / filtered_count;
+		cpr3_info(ctrl, "average quotient delta=%d (count=%d)\n",
+			aging_sensor->measured_quot_diff,
+			filtered_count);
+	} else {
+		cpr3_err(ctrl, "%d aging measurements completed after %d iterations\n",
+			aging_measurement_count,
+			CPR3_AGING_MEASUREMENT_ITERATIONS);
+		rc = -EBUSY;
+	}
+
+cleanup:
+	kfree(quot_delta_results);
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc2 = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc2) {
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc2);
+			rc = rc2;
+		}
+	}
+
+	cpr3_ctrl_loop_disable(ctrl);
+
+	cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_restore);
+
+	cpr3_write(ctrl, CPR3_REG_RO_MASK(0), ro_mask_restore);
+
+	cpr3_write(ctrl, CPR3_REG_GCNT(0), gcnt0_restore);
+	cpr3_write(ctrl, CPR3_REG_GCNT(1), gcnt1_restore);
+
+	if (ctrl->supports_hw_closed_loop
+		&& ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_MID_CONT, cont_dly_restore);
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_UP_DN_CONT,
+				up_down_dly_restore);
+	} else {
+		cpr3_write(ctrl, CPR3_REG_CPR_TIMER_AUTO_CONT,
+				cont_dly_restore);
+	}
+
+	for (i = 0; i <= ctrl->sensor_count / 32; i++) {
+		cpr3_write(ctrl, CPR3_REG_SENSOR_MASK_WRITE_BANK(i), 0);
+		cpr3_write(ctrl, CPR3_REG_SENSOR_BYPASS_WRITE_BANK(i), 0);
+	}
+
+	cpr3_masked_write(ctrl, CPR3_REG_CPR_CTL,
+		CPR3_CPR_CTL_COUNT_MODE_MASK | CPR3_CPR_CTL_COUNT_REPEAT_MASK,
+		(ctrl->count_mode << CPR3_CPR_CTL_COUNT_MODE_SHIFT)
+		| (ctrl->count_repeat << CPR3_CPR_CTL_COUNT_REPEAT_SHIFT));
+
+	cpr3_write(ctrl, CPR3_REG_SENSOR_OWNER(aging_sensor->sensor_id),
+			ctrl->sensor_owner[aging_sensor->sensor_id]);
+
+	cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR,
+			CPR3_IRQ_UP | CPR3_IRQ_DOWN | CPR3_IRQ_MID);
+
+	if (ctrl->supports_hw_closed_loop) {
+		if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4 ||
+		    ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+			cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+				CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+				ctrl->use_hw_closed_loop
+				? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
+				: CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
+		} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+			cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
+				ctrl->use_hw_closed_loop
+				? CPR3_HW_CLOSED_LOOP_ENABLE
+				: CPR3_HW_CLOSED_LOOP_DISABLE);
+		}
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_readjust_volt_and_quot() - readjust the target quotients as
+ *		well as the floor, ceiling, and open-loop voltages for the
+ *		regulator by removing the old adjustment and adding the new one
+ * @vreg:		Pointer to the CPR3 regulator
+ * @old_adjust_volt:	Old aging adjustment voltage in microvolts
+ * @new_adjust_volt:	New aging adjustment voltage in microvolts
+ *
+ * Also reset the cached closed loop voltage (last_volt) to equal the open-loop
+ * voltage for each corner.
+ *
+ * Return: None
+ */
+static void cpr3_regulator_readjust_volt_and_quot(struct cpr3_regulator *vreg,
+		int old_adjust_volt, int new_adjust_volt)
+{
+	unsigned long long temp;
+	int i, j, old_volt, new_volt, rounded_volt;
+
+	if (!vreg->aging_allowed)
+		return;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		temp = (unsigned long long)old_adjust_volt
+			* (unsigned long long)vreg->corner[i].aging_derate;
+		do_div(temp, 1000);
+		old_volt = temp;
+
+		temp = (unsigned long long)new_adjust_volt
+			* (unsigned long long)vreg->corner[i].aging_derate;
+		do_div(temp, 1000);
+		new_volt = temp;
+
+		old_volt = min(vreg->aging_max_adjust_volt, old_volt);
+		new_volt = min(vreg->aging_max_adjust_volt, new_volt);
+
+		for (j = 0; j < CPR3_RO_COUNT; j++) {
+			if (vreg->corner[i].target_quot[j] != 0) {
+				vreg->corner[i].target_quot[j]
+					+= cpr3_quot_adjustment(
+						vreg->corner[i].ro_scale[j],
+						new_volt)
+					   - cpr3_quot_adjustment(
+						vreg->corner[i].ro_scale[j],
+						old_volt);
+			}
+		}
+
+		rounded_volt = CPR3_ROUND(new_volt,
+					vreg->thread->ctrl->step_volt);
+
+		if (!vreg->aging_allow_open_loop_adj)
+			rounded_volt = 0;
+
+		vreg->corner[i].ceiling_volt
+			= vreg->corner[i].unaged_ceiling_volt + rounded_volt;
+		vreg->corner[i].ceiling_volt = min(vreg->corner[i].ceiling_volt,
+					      vreg->corner[i].abs_ceiling_volt);
+		vreg->corner[i].floor_volt
+			= vreg->corner[i].unaged_floor_volt + rounded_volt;
+		vreg->corner[i].floor_volt = min(vreg->corner[i].floor_volt,
+						vreg->corner[i].ceiling_volt);
+		vreg->corner[i].open_loop_volt
+			= vreg->corner[i].unaged_open_loop_volt + rounded_volt;
+		vreg->corner[i].open_loop_volt
+			= min(vreg->corner[i].open_loop_volt,
+				vreg->corner[i].ceiling_volt);
+
+		vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt;
+
+		cpr3_debug(vreg, "corner %d: applying %d uV closed-loop and %d uV open-loop voltage margin adjustment\n",
+			i, new_volt, rounded_volt);
+	}
+}
+
+/**
+ * cpr3_regulator_set_aging_ref_adjustment() - adjust target quotients for the
+ *		regulators managed by this CPR controller to account for aging
+ * @ctrl:		Pointer to the CPR3 controller
+ * @ref_adjust_volt:	New aging reference adjustment voltage in microvolts to
+ *			apply to all regulators managed by this CPR controller
+ *
+ * The existing aging adjustment as defined by ctrl->aging_ref_adjust_volt is
+ * first removed and then the adjustment is applied.  Lastly, the value of
+ * ctrl->aging_ref_adjust_volt is updated to ref_adjust_volt.
+ */
+static void cpr3_regulator_set_aging_ref_adjustment(
+		struct cpr3_controller *ctrl, int ref_adjust_volt)
+{
+	struct cpr3_regulator *vreg;
+	int i, j;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+			cpr3_regulator_readjust_volt_and_quot(vreg,
+				ctrl->aging_ref_adjust_volt, ref_adjust_volt);
+			if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
+				cprh_adjust_voltages_for_apm(vreg);
+		}
+	}
+
+	ctrl->aging_ref_adjust_volt = ref_adjust_volt;
+}
+
+/**
+ * cpr3_regulator_aging_adjust() - adjust the target quotients for regulators
+ *		based on the output of CPR aging sensors
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_aging_adjust(struct cpr3_controller *ctrl)
+{
+	struct cpr3_regulator *vreg;
+	struct cpr3_corner restore_aging_corner;
+	struct cpr3_corner *corner;
+	int *restore_current_corner;
+	bool *restore_vreg_enabled;
+	int i, j, id, rc, rc2, vreg_count, aging_volt, max_aging_volt = 0;
+	u32 reg;
+
+	if (!ctrl->aging_required || !ctrl->cpr_enabled
+	    || ctrl->aggr_corner.ceiling_volt == 0
+	    || ctrl->aggr_corner.ceiling_volt > ctrl->aging_ref_volt)
+		return 0;
+
+	for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			vreg = &ctrl->thread[i].vreg[j];
+			vreg_count++;
+
+			if (vreg->aging_allowed && vreg->vreg_enabled
+			    && vreg->current_corner > vreg->aging_corner)
+				return 0;
+		}
+	}
+
+	/* Verify that none of the aging sensors are currently masked. */
+	for (i = 0; i < ctrl->aging_sensor_count; i++) {
+		id = ctrl->aging_sensor[i].sensor_id;
+		reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id));
+		if (reg & BIT(id % 32))
+			return 0;
+	}
+
+	/*
+	 * Verify that the aging possible register (if specified) has an
+	 * acceptable value.
+	 */
+	if (ctrl->aging_possible_reg) {
+		reg = readl_relaxed(ctrl->aging_possible_reg);
+		reg &= ctrl->aging_possible_mask;
+		if (reg != ctrl->aging_possible_val)
+			return 0;
+	}
+
+	restore_current_corner = kcalloc(vreg_count,
+				sizeof(*restore_current_corner), GFP_KERNEL);
+	restore_vreg_enabled = kcalloc(vreg_count,
+				sizeof(*restore_vreg_enabled), GFP_KERNEL);
+	if (!restore_current_corner || !restore_vreg_enabled) {
+		kfree(restore_current_corner);
+		kfree(restore_vreg_enabled);
+		return -ENOMEM;
+	}
+
+	/* Force all regulators to the aging corner */
+	for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) {
+			vreg = &ctrl->thread[i].vreg[j];
+
+			restore_current_corner[vreg_count]
+				= vreg->current_corner;
+			restore_vreg_enabled[vreg_count]
+				= vreg->vreg_enabled;
+
+			vreg->current_corner = vreg->aging_corner;
+			vreg->vreg_enabled = true;
+		}
+	}
+
+	/* Force one of the regulators to require the aging reference voltage */
+	vreg = &ctrl->thread[0].vreg[0];
+	corner = &vreg->corner[vreg->current_corner];
+	restore_aging_corner = *corner;
+	corner->ceiling_volt = ctrl->aging_ref_volt;
+	corner->floor_volt = ctrl->aging_ref_volt;
+	corner->open_loop_volt = ctrl->aging_ref_volt;
+	corner->last_volt = ctrl->aging_ref_volt;
+
+	/* Skip last_volt caching */
+	ctrl->last_corner_was_closed_loop = false;
+
+	/* Set the vdd supply voltage to the aging reference voltage */
+	rc = _cpr3_regulator_update_ctrl_state(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "unable to force vdd-supply to the aging reference voltage=%d uV, rc=%d\n",
+			ctrl->aging_ref_volt, rc);
+		goto cleanup;
+	}
+
+	if (ctrl->aging_vdd_mode) {
+		rc = regulator_set_mode(ctrl->vdd_regulator,
+					ctrl->aging_vdd_mode);
+		if (rc) {
+			cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+				ctrl->aging_vdd_mode, rc);
+			goto cleanup;
+		}
+	}
+
+	/* Perform aging measurement on all aging sensors */
+	for (i = 0; i < ctrl->aging_sensor_count; i++) {
+		for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) {
+			rc = cpr3_regulator_measure_aging(ctrl,
+					&ctrl->aging_sensor[i]);
+			if (!rc)
+				break;
+		}
+
+		if (!rc) {
+			aging_volt =
+				cpr3_voltage_adjustment(
+					ctrl->aging_sensor[i].ro_scale,
+					ctrl->aging_sensor[i].measured_quot_diff
+					- ctrl->aging_sensor[i].init_quot_diff);
+			max_aging_volt = max(max_aging_volt, aging_volt);
+		} else {
+			cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
+				j, rc);
+			ctrl->aging_failed = true;
+			ctrl->aging_required = false;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	vreg = &ctrl->thread[0].vreg[0];
+	vreg->corner[vreg->current_corner] = restore_aging_corner;
+
+	for (i = 0, vreg_count = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++, vreg_count++) {
+			vreg = &ctrl->thread[i].vreg[j];
+			vreg->current_corner
+				= restore_current_corner[vreg_count];
+			vreg->vreg_enabled = restore_vreg_enabled[vreg_count];
+		}
+	}
+
+	kfree(restore_current_corner);
+	kfree(restore_vreg_enabled);
+
+	/* Adjust the CPR target quotients according to the aging measurement */
+	if (!rc) {
+		cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt);
+
+		cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n",
+			ctrl->aging_ref_adjust_volt);
+		ctrl->aging_succeeded = true;
+		ctrl->aging_required = false;
+	}
+
+	if (ctrl->aging_complete_vdd_mode) {
+		rc = regulator_set_mode(ctrl->vdd_regulator,
+					ctrl->aging_complete_vdd_mode);
+		if (rc)
+			cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+				ctrl->aging_complete_vdd_mode, rc);
+	}
+
+	/* Skip last_volt caching */
+	ctrl->last_corner_was_closed_loop = false;
+
+	/*
+	 * Restore vdd-supply to the voltage before the aging measurement and
+	 * restore the CPR3 controller hardware state.
+	 */
+	rc2 = _cpr3_regulator_update_ctrl_state(ctrl);
+
+	/* Stop last_volt caching on for the next request */
+	ctrl->last_corner_was_closed_loop = false;
+
+	return rc ? rc : rc2;
+}
+
+/**
+ * cprh_regulator_aging_adjust() - adjust the target quotients and open-loop
+ *		voltages for CPRh regulators based on the output of CPR aging
+ *		sensors
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_regulator_aging_adjust(struct cpr3_controller *ctrl)
+{
+	int i, j, id, rc, rc2, aging_volt, init_volt;
+	int max_aging_volt = 0;
+	u32 reg;
+
+	if (!ctrl->aging_required || !ctrl->cpr_enabled)
+		return 0;
+
+	if (!ctrl->vdd_regulator) {
+		cpr3_err(ctrl, "vdd-supply regulator missing\n");
+		return -ENODEV;
+	}
+
+	init_volt = regulator_get_voltage(ctrl->vdd_regulator);
+	if (init_volt < 0) {
+		cpr3_err(ctrl, "could not get vdd-supply voltage, rc=%d\n",
+			init_volt);
+		return init_volt;
+	}
+
+	if (init_volt > ctrl->aging_ref_volt) {
+		cpr3_info(ctrl, "unable to perform CPR aging measurement as vdd=%d uV > aging voltage=%d uV\n",
+			init_volt, ctrl->aging_ref_volt);
+		return 0;
+	}
+
+	/* Verify that none of the aging sensors are currently masked. */
+	for (i = 0; i < ctrl->aging_sensor_count; i++) {
+		id = ctrl->aging_sensor[i].sensor_id;
+		reg = cpr3_read(ctrl, CPR3_REG_SENSOR_MASK_READ(id));
+		if (reg & BIT(id % 32)) {
+			cpr3_info(ctrl, "unable to perform CPR aging measurement as CPR sensor %d is masked\n",
+				id);
+			return 0;
+		}
+	}
+
+	rc = regulator_set_voltage(ctrl->vdd_regulator, ctrl->aging_ref_volt,
+				INT_MAX);
+	if (rc) {
+		cpr3_err(ctrl, "unable to set vdd-supply to aging voltage=%d uV, rc=%d\n",
+			ctrl->aging_ref_volt, rc);
+		return rc;
+	}
+
+	if (ctrl->aging_vdd_mode) {
+		rc = regulator_set_mode(ctrl->vdd_regulator,
+					ctrl->aging_vdd_mode);
+		if (rc) {
+			cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+				ctrl->aging_vdd_mode, rc);
+			goto cleanup;
+		}
+	}
+
+	/* Perform aging measurement on all aging sensors */
+	for (i = 0; i < ctrl->aging_sensor_count; i++) {
+		for (j = 0; j < CPR3_AGING_RETRY_COUNT; j++) {
+			rc = cpr3_regulator_measure_aging(ctrl,
+					&ctrl->aging_sensor[i]);
+			if (!rc)
+				break;
+		}
+
+		if (!rc) {
+			aging_volt =
+				cpr3_voltage_adjustment(
+					ctrl->aging_sensor[i].ro_scale,
+					ctrl->aging_sensor[i].measured_quot_diff
+					- ctrl->aging_sensor[i].init_quot_diff);
+			max_aging_volt = max(max_aging_volt, aging_volt);
+		} else {
+			cpr3_err(ctrl, "CPR aging measurement failed after %d tries, rc=%d\n",
+				j, rc);
+			ctrl->aging_failed = true;
+			ctrl->aging_required = false;
+			goto cleanup;
+		}
+	}
+
+cleanup:
+	/* Adjust the CPR target quotients according to the aging measurement */
+	if (!rc) {
+		cpr3_regulator_set_aging_ref_adjustment(ctrl, max_aging_volt);
+
+		cpr3_info(ctrl, "aging measurement successful; aging reference adjustment voltage=%d uV\n",
+			ctrl->aging_ref_adjust_volt);
+		ctrl->aging_succeeded = true;
+		ctrl->aging_required = false;
+	}
+
+	rc2 = regulator_set_voltage(ctrl->vdd_regulator, init_volt, INT_MAX);
+	if (rc2) {
+		cpr3_err(ctrl, "unable to reset vdd-supply to initial voltage=%d uV, rc=%d\n",
+			init_volt, rc2);
+		return rc2;
+	}
+
+	if (ctrl->aging_complete_vdd_mode) {
+		rc2 = regulator_set_mode(ctrl->vdd_regulator,
+					ctrl->aging_complete_vdd_mode);
+		if (rc2)  {
+			cpr3_err(ctrl, "unable to configure vdd-supply for mode=%u, rc=%d\n",
+				ctrl->aging_complete_vdd_mode, rc2);
+			return rc2;
+		}
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_update_ctrl_state() - update the state of the CPR controller
+ *		to reflect the corners used by all CPR3 regulators as well as
+ *		the CPR operating mode and perform aging adjustments if needed
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Note, CPR3 controller lock must be held by the caller.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_update_ctrl_state(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	rc = _cpr3_regulator_update_ctrl_state(ctrl);
+	if (rc)
+		return rc;
+
+	return cpr3_regulator_aging_adjust(ctrl);
+}
+
+/**
+ * cpr3_regulator_set_voltage() - set the voltage corner for the CPR3 regulator
+ *			associated with the regulator device
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ * @corner:		New voltage corner to set (offset by CPR3_CORNER_OFFSET)
+ * @corner_max:		Maximum voltage corner allowed (offset by
+ *			CPR3_CORNER_OFFSET)
+ * @selector:		Pointer which is filled with the selector value for the
+ *			corner
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.  The VDD voltage will not be
+ * physically configured until both this function and cpr3_regulator_enable()
+ * are called.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_set_voltage(struct regulator_dev *rdev,
+		int corner, int corner_max, unsigned int *selector)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int rc = 0;
+	int last_corner;
+
+	corner -= CPR3_CORNER_OFFSET;
+	corner_max -= CPR3_CORNER_OFFSET;
+	*selector = corner;
+
+	mutex_lock(&ctrl->lock);
+
+	if (!vreg->vreg_enabled) {
+		vreg->current_corner = corner;
+		cpr3_debug(vreg, "stored corner=%d\n", corner);
+		goto done;
+	} else if (vreg->current_corner == corner) {
+		goto done;
+	}
+
+	last_corner = vreg->current_corner;
+	vreg->current_corner = corner;
+
+	rc = cpr3_regulator_update_ctrl_state(ctrl);
+	if (rc) {
+		cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc);
+		vreg->current_corner = last_corner;
+	}
+
+	cpr3_debug(vreg, "set corner=%d\n", corner);
+done:
+	mutex_unlock(&ctrl->lock);
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_get_voltage() - get the voltage corner for the CPR3 regulator
+ *			associated with the regulator device
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.
+ *
+ * Return: voltage corner value offset by CPR3_CORNER_OFFSET
+ */
+static int cpr3_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+
+	if (vreg->current_corner == CPR3_REGULATOR_CORNER_INVALID)
+		return CPR3_CORNER_OFFSET;
+	else
+		return vreg->current_corner + CPR3_CORNER_OFFSET;
+}
+
+/**
+ * cpr3_regulator_list_voltage() - return the voltage corner mapped to the
+ *			specified selector
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ * @selector:		Regulator selector
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.
+ *
+ * Return: voltage corner value offset by CPR3_CORNER_OFFSET
+ */
+static int cpr3_regulator_list_voltage(struct regulator_dev *rdev,
+		unsigned int selector)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+
+	if (selector < vreg->corner_count)
+		return selector + CPR3_CORNER_OFFSET;
+	else
+		return 0;
+}
+
+/**
+ * cpr3_regulator_list_corner_voltage() - return the ceiling voltage mapped to
+ *			the specified voltage corner
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ * @corner:		Voltage corner
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.
+ *
+ * Return: voltage value in microvolts or -EINVAL if the corner is out of range
+ */
+static int cpr3_regulator_list_corner_voltage(struct regulator_dev *rdev,
+		int corner)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+
+	corner -= CPR3_CORNER_OFFSET;
+
+	if (corner >= 0 && corner < vreg->corner_count)
+		return vreg->corner[corner].ceiling_volt;
+	else
+		return -EINVAL;
+}
+
+/**
+ * cpr3_regulator_is_enabled() - return the enable state of the CPR3 regulator
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.
+ *
+ * Return: true if regulator is enabled, false if regulator is disabled
+ */
+static int cpr3_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->vreg_enabled;
+}
+
+/**
+ * cpr3_regulator_enable() - enable the CPR3 regulator
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_enable(struct regulator_dev *rdev)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int rc = 0;
+
+	if (vreg->vreg_enabled == true)
+		return 0;
+
+	mutex_lock(&ctrl->lock);
+
+	if (ctrl->system_regulator) {
+		rc = regulator_enable(ctrl->system_regulator);
+		if (rc) {
+			cpr3_err(ctrl, "regulator_enable(system) failed, rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	rc = regulator_enable(ctrl->vdd_regulator);
+	if (rc) {
+		cpr3_err(vreg, "regulator_enable(vdd) failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	if (vreg->ldo_regulator) {
+		rc = regulator_enable(vreg->ldo_regulator);
+		if (rc) {
+			cpr3_err(vreg, "regulator_enable(ldo) failed, rc=%d\n",
+				 rc);
+			goto done;
+		}
+	}
+
+	vreg->vreg_enabled = true;
+	rc = cpr3_regulator_update_ctrl_state(ctrl);
+	if (rc) {
+		cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc);
+		regulator_disable(ctrl->vdd_regulator);
+		vreg->vreg_enabled = false;
+		goto done;
+	}
+
+	cpr3_debug(vreg, "Enabled\n");
+done:
+	mutex_unlock(&ctrl->lock);
+
+	return rc;
+}
+
+/**
+ * cpr3_regulator_disable() - disable the CPR3 regulator
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_disable(struct regulator_dev *rdev)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	int rc, rc2;
+
+	if (vreg->vreg_enabled == false)
+		return 0;
+
+	mutex_lock(&ctrl->lock);
+
+	if (vreg->ldo_regulator && vreg->ldo_regulator_bypass == LDO_MODE) {
+		rc = regulator_get_voltage(ctrl->vdd_regulator);
+		if (rc < 0) {
+			cpr3_err(vreg, "regulator_get_voltage(vdd) failed, rc=%d\n",
+				 rc);
+			goto done;
+		}
+
+		/* Switch back to BHS for safe operation */
+		rc = cpr3_regulator_set_bhs_mode(vreg, rc,
+				       ctrl->aggr_corner.ceiling_volt);
+		if (rc) {
+			cpr3_err(vreg, "unable to switch to BHS mode, rc=%d\n",
+				 rc);
+			goto done;
+		}
+	}
+
+	if (vreg->ldo_regulator) {
+		rc = regulator_disable(vreg->ldo_regulator);
+		if (rc) {
+			cpr3_err(vreg, "regulator_disable(ldo) failed, rc=%d\n",
+				 rc);
+			goto done;
+		}
+	}
+	rc = regulator_disable(ctrl->vdd_regulator);
+	if (rc) {
+		cpr3_err(vreg, "regulator_disable(vdd) failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	vreg->vreg_enabled = false;
+	rc = cpr3_regulator_update_ctrl_state(ctrl);
+	if (rc) {
+		cpr3_err(vreg, "could not update CPR state, rc=%d\n", rc);
+		rc2 = regulator_enable(ctrl->vdd_regulator);
+		vreg->vreg_enabled = true;
+		goto done;
+	}
+
+	if (ctrl->system_regulator) {
+		rc = regulator_disable(ctrl->system_regulator);
+		if (rc) {
+			cpr3_err(ctrl, "regulator_disable(system) failed, rc=%d\n",
+				rc);
+			goto done;
+		}
+		if (ctrl->support_ldo300_vreg) {
+			rc = regulator_set_voltage(ctrl->system_regulator, 0,
+						INT_MAX);
+			if (rc)
+				cpr3_err(ctrl, "failed to set voltage on system rc=%d\n",
+					rc);
+			goto done;
+		}
+	}
+
+	cpr3_debug(vreg, "Disabled\n");
+done:
+	mutex_unlock(&ctrl->lock);
+
+	return rc;
+}
+
+static struct regulator_ops cpr3_regulator_ops = {
+	.enable			= cpr3_regulator_enable,
+	.disable		= cpr3_regulator_disable,
+	.is_enabled		= cpr3_regulator_is_enabled,
+	.set_voltage		= cpr3_regulator_set_voltage,
+	.get_voltage		= cpr3_regulator_get_voltage,
+	.list_voltage		= cpr3_regulator_list_voltage,
+	.list_corner_voltage	= cpr3_regulator_list_corner_voltage,
+};
+
+/**
+ * cprh_regulator_get_voltage() - get the voltage corner for the CPR3 regulator
+ *			associated with the regulator device
+ * @rdev:		Regulator device pointer for the cpr3-regulator
+ *
+ * This function is passed as a callback function into the regulator ops that
+ * are registered for each cpr3-regulator device of a CPRh controller. The
+ * corner is read directly from CPRh hardware register.
+ *
+ * Return: voltage corner value offset by CPR3_CORNER_OFFSET
+ */
+static int cprh_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct cpr3_regulator *vreg = rdev_get_drvdata(rdev);
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	bool cpr_enabled;
+	u32 reg, rc;
+
+	mutex_lock(&ctrl->lock);
+
+	cpr_enabled = ctrl->cpr_enabled;
+	if (!cpr_enabled) {
+		rc = cpr3_clock_enable(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
+			mutex_unlock(&ctrl->lock);
+			return CPR3_REGULATOR_CORNER_INVALID;
+		}
+		ctrl->cpr_enabled = true;
+	}
+
+	reg = cpr3_read(vreg->thread->ctrl, CPRH_REG_STATUS);
+
+	if (!cpr_enabled) {
+		cpr3_clock_disable(ctrl);
+		ctrl->cpr_enabled = false;
+	}
+
+	mutex_unlock(&ctrl->lock);
+
+	return (reg & CPRH_STATUS_CORNER)
+		+ CPR3_CORNER_OFFSET;
+}
+
+static struct regulator_ops cprh_regulator_ops = {
+	.get_voltage		= cprh_regulator_get_voltage,
+	.list_corner_voltage	= cpr3_regulator_list_corner_voltage,
+};
+
+/**
+ * cpr3_print_result() - print CPR measurement results to the kernel log for
+ *		debugging purposes
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: None
+ */
+static void cpr3_print_result(struct cpr3_thread *thread)
+{
+	struct cpr3_controller *ctrl = thread->ctrl;
+	u32 result[3], busy, step_dn, step_up, error_steps, error, negative;
+	u32 quot_min, quot_max, ro_min, ro_max, step_quot_min, step_quot_max;
+	u32 sensor_min, sensor_max;
+	char *sign;
+
+	result[0] = cpr3_read(ctrl, CPR3_REG_RESULT0(thread->thread_id));
+	result[1] = cpr3_read(ctrl, CPR3_REG_RESULT1(thread->thread_id));
+	result[2] = cpr3_read(ctrl, CPR3_REG_RESULT2(thread->thread_id));
+
+	busy = !!(result[0] & CPR3_RESULT0_BUSY_MASK);
+	step_dn = !!(result[0] & CPR3_RESULT0_STEP_DN_MASK);
+	step_up = !!(result[0] & CPR3_RESULT0_STEP_UP_MASK);
+	error_steps = (result[0] & CPR3_RESULT0_ERROR_STEPS_MASK)
+			>> CPR3_RESULT0_ERROR_STEPS_SHIFT;
+	error = (result[0] & CPR3_RESULT0_ERROR_MASK)
+			>> CPR3_RESULT0_ERROR_SHIFT;
+	negative = !!(result[0] & CPR3_RESULT0_NEGATIVE_MASK);
+
+	quot_min = (result[1] & CPR3_RESULT1_QUOT_MIN_MASK)
+			>> CPR3_RESULT1_QUOT_MIN_SHIFT;
+	quot_max = (result[1] & CPR3_RESULT1_QUOT_MAX_MASK)
+			>> CPR3_RESULT1_QUOT_MAX_SHIFT;
+	ro_min = (result[1] & CPR3_RESULT1_RO_MIN_MASK)
+			>> CPR3_RESULT1_RO_MIN_SHIFT;
+	ro_max = (result[1] & CPR3_RESULT1_RO_MAX_MASK)
+			>> CPR3_RESULT1_RO_MAX_SHIFT;
+
+	step_quot_min = (result[2] & CPR3_RESULT2_STEP_QUOT_MIN_MASK)
+			>> CPR3_RESULT2_STEP_QUOT_MIN_SHIFT;
+	step_quot_max = (result[2] & CPR3_RESULT2_STEP_QUOT_MAX_MASK)
+			>> CPR3_RESULT2_STEP_QUOT_MAX_SHIFT;
+	sensor_min = (result[2] & CPR3_RESULT2_SENSOR_MIN_MASK)
+			>> CPR3_RESULT2_SENSOR_MIN_SHIFT;
+	sensor_max = (result[2] & CPR3_RESULT2_SENSOR_MAX_MASK)
+			>> CPR3_RESULT2_SENSOR_MAX_SHIFT;
+
+	sign = negative ? "-" : "";
+	cpr3_debug(ctrl, "thread %u: busy=%u, step_dn=%u, step_up=%u, error_steps=%s%u, error=%s%u\n",
+		thread->thread_id, busy, step_dn, step_up, sign, error_steps,
+		sign, error);
+	cpr3_debug(ctrl, "thread %u: quot_min=%u, quot_max=%u, ro_min=%u, ro_max=%u\n",
+		thread->thread_id, quot_min, quot_max, ro_min, ro_max);
+	cpr3_debug(ctrl, "thread %u: step_quot_min=%u, step_quot_max=%u, sensor_min=%u, sensor_max=%u\n",
+		thread->thread_id, step_quot_min, step_quot_max, sensor_min,
+		sensor_max);
+}
+
+/**
+ * cpr3_thread_busy() - returns if the specified CPR3 thread is busy taking
+ *		a measurement
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: CPR3 busy status
+ */
+static bool cpr3_thread_busy(struct cpr3_thread *thread)
+{
+	u32 result;
+
+	result = cpr3_read(thread->ctrl, CPR3_REG_RESULT0(thread->thread_id));
+
+	return !!(result & CPR3_RESULT0_BUSY_MASK);
+}
+
+/**
+ * cpr3_irq_handler() - CPR interrupt handler callback function used for
+ *		software closed-loop operation
+ * @irq:		CPR interrupt number
+ * @data:		Private data corresponding to the CPR3 controller
+ *			pointer
+ *
+ * This function increases or decreases the vdd supply voltage based upon the
+ * CPR controller recommendation.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t cpr3_irq_handler(int irq, void *data)
+{
+	struct cpr3_controller *ctrl = data;
+	struct cpr3_corner *aggr = &ctrl->aggr_corner;
+	u32 cont = CPR3_CONT_CMD_NACK;
+	u32 reg_last_measurement = 0;
+	struct cpr3_regulator *vreg;
+	struct cpr3_corner *corner;
+	unsigned long flags;
+	int i, j, new_volt, last_volt, dynamic_floor_volt, rc;
+	u32 irq_en, status, cpr_status, ctl;
+	bool up, down;
+
+	mutex_lock(&ctrl->lock);
+
+	if (!ctrl->cpr_enabled) {
+		cpr3_debug(ctrl, "CPR interrupt received but CPR is disabled\n");
+		mutex_unlock(&ctrl->lock);
+		return IRQ_HANDLED;
+	} else if (ctrl->use_hw_closed_loop) {
+		cpr3_debug(ctrl, "CPR interrupt received but CPR is using HW closed-loop\n");
+		goto done;
+	}
+
+	/*
+	 * CPR IRQ status checking and CPR controller disabling must happen
+	 * atomically and without invening delay in order to avoid an interrupt
+	 * storm caused by the handler racing with the CPR controller.
+	 */
+	local_irq_save(flags);
+	preempt_disable();
+
+	status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS);
+	up = status & CPR3_IRQ_UP;
+	down = status & CPR3_IRQ_DOWN;
+
+	if (!up && !down) {
+		/*
+		 * Toggle the CPR controller off and then back on since the
+		 * hardware and software states are out of sync.  This condition
+		 * occurs after an aging measurement completes as the CPR IRQ
+		 * physically triggers during the aging measurement but the
+		 * handler is stuck waiting on the mutex lock.
+		 */
+		cpr3_ctrl_loop_disable(ctrl);
+
+		local_irq_restore(flags);
+		preempt_enable();
+
+		/* Wait for the loop disable write to complete */
+		mb();
+
+		/* Wait for BUSY=1 and LOOP_EN=0 in CPR controller registers. */
+		for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) {
+			cpr_status = cpr3_read(ctrl, CPR3_REG_CPR_STATUS);
+			ctl = cpr3_read(ctrl, CPR3_REG_CPR_CTL);
+			if (cpr_status & CPR3_CPR_STATUS_BUSY_MASK
+			    && (ctl & CPR3_CPR_CTL_LOOP_EN_MASK)
+					== CPR3_CPR_CTL_LOOP_DISABLE)
+				break;
+			udelay(10);
+		}
+		if (i == CPR3_REGISTER_WRITE_DELAY_US / 10)
+			cpr3_debug(ctrl, "CPR controller not disabled after %d us\n",
+				CPR3_REGISTER_WRITE_DELAY_US);
+
+		/* Clear interrupt status */
+		cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR,
+			CPR3_IRQ_UP | CPR3_IRQ_DOWN);
+
+		/* Wait for the interrupt clearing write to complete */
+		mb();
+
+		/* Wait for IRQ_STATUS register to be cleared. */
+		for (i = 0; i < CPR3_REGISTER_WRITE_DELAY_US / 10; i++) {
+			status = cpr3_read(ctrl, CPR3_REG_IRQ_STATUS);
+			if (!(status & (CPR3_IRQ_UP | CPR3_IRQ_DOWN)))
+				break;
+			udelay(10);
+		}
+		if (i == CPR3_REGISTER_WRITE_DELAY_US / 10)
+			cpr3_debug(ctrl, "CPR interrupts not cleared after %d us\n",
+				CPR3_REGISTER_WRITE_DELAY_US);
+
+		cpr3_ctrl_loop_enable(ctrl);
+
+		cpr3_debug(ctrl, "CPR interrupt received but no up or down status bit is set\n");
+
+		mutex_unlock(&ctrl->lock);
+		return IRQ_HANDLED;
+	} else if (up && down) {
+		cpr3_debug(ctrl, "both up and down status bits set\n");
+		/* The up flag takes precedence over the down flag. */
+		down = false;
+	}
+
+	if (ctrl->supports_hw_closed_loop)
+		reg_last_measurement
+			= cpr3_read(ctrl, CPR3_REG_LAST_MEASUREMENT);
+	dynamic_floor_volt = cpr3_regulator_get_dynamic_floor_volt(ctrl,
+							reg_last_measurement);
+
+	local_irq_restore(flags);
+	preempt_enable();
+
+	irq_en = aggr->irq_en;
+	last_volt = aggr->last_volt;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		if (cpr3_thread_busy(&ctrl->thread[i])) {
+			cpr3_debug(ctrl, "CPR thread %u busy when it should be waiting for SW cont\n",
+				ctrl->thread[i].thread_id);
+			goto done;
+		}
+	}
+
+	new_volt = up ? last_volt + ctrl->step_volt
+		      : last_volt - ctrl->step_volt;
+
+	/* Re-enable UP/DOWN interrupt when its opposite is received. */
+	irq_en |= up ? CPR3_IRQ_DOWN : CPR3_IRQ_UP;
+
+	if (new_volt > aggr->ceiling_volt) {
+		new_volt = aggr->ceiling_volt;
+		irq_en &= ~CPR3_IRQ_UP;
+		cpr3_debug(ctrl, "limiting to ceiling=%d uV\n",
+			aggr->ceiling_volt);
+	} else if (new_volt < aggr->floor_volt) {
+		new_volt = aggr->floor_volt;
+		irq_en &= ~CPR3_IRQ_DOWN;
+		cpr3_debug(ctrl, "limiting to floor=%d uV\n", aggr->floor_volt);
+	}
+
+	if (down && new_volt < dynamic_floor_volt) {
+		/*
+		 * The vdd-supply voltage should not be decreased below the
+		 * dynamic floor voltage.  However, it is not necessary (and
+		 * counter productive) to force the voltage up to this level
+		 * if it happened to be below it since the closed-loop voltage
+		 * must have gotten there in a safe manner while the power
+		 * domains for the CPR3 regulator imposing the dynamic floor
+		 * were not bypassed.
+		 */
+		new_volt = last_volt;
+		irq_en &= ~CPR3_IRQ_DOWN;
+		cpr3_debug(ctrl, "limiting to dynamic floor=%d uV\n",
+			dynamic_floor_volt);
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++)
+		cpr3_print_result(&ctrl->thread[i]);
+
+	cpr3_debug(ctrl, "%s: new_volt=%d uV, last_volt=%d uV\n",
+		up ? "UP" : "DN", new_volt, last_volt);
+
+	if (ctrl->proc_clock_throttle && last_volt == aggr->ceiling_volt
+	    && new_volt < last_volt)
+		cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
+				ctrl->proc_clock_throttle);
+
+	if (new_volt != last_volt) {
+		rc = cpr3_regulator_scale_vdd_voltage(ctrl, new_volt,
+						      last_volt,
+						      aggr);
+		if (rc) {
+			cpr3_err(ctrl, "scale_vdd() failed to set vdd=%d uV, rc=%d\n",
+				 new_volt, rc);
+			goto done;
+		}
+		cont = CPR3_CONT_CMD_ACK;
+
+		/*
+		 * Update the closed-loop voltage for all regulators managed
+		 * by this CPR controller.
+		 */
+		for (i = 0; i < ctrl->thread_count; i++) {
+			for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+				vreg = &ctrl->thread[i].vreg[j];
+				cpr3_update_vreg_closed_loop_volt(vreg,
+					new_volt, reg_last_measurement);
+			}
+		}
+	}
+
+	if (ctrl->proc_clock_throttle && new_volt == aggr->ceiling_volt)
+		cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
+				CPR3_PD_THROTTLE_DISABLE);
+
+	corner = &ctrl->thread[0].vreg[0].corner[
+			ctrl->thread[0].vreg[0].current_corner];
+
+	if (irq_en != aggr->irq_en) {
+		aggr->irq_en = irq_en;
+		cpr3_write(ctrl, CPR3_REG_IRQ_EN, irq_en);
+	}
+
+	aggr->last_volt = new_volt;
+
+done:
+	/* Clear interrupt status */
+	cpr3_write(ctrl, CPR3_REG_IRQ_CLEAR, CPR3_IRQ_UP | CPR3_IRQ_DOWN);
+
+	/* ACK or NACK the CPR controller */
+	cpr3_write(ctrl, CPR3_REG_CONT_CMD, cont);
+
+	mutex_unlock(&ctrl->lock);
+	return IRQ_HANDLED;
+}
+
+/**
+ * cpr3_ceiling_irq_handler() - CPR ceiling reached interrupt handler callback
+ *		function used for hardware closed-loop operation
+ * @irq:		CPR ceiling interrupt number
+ * @data:		Private data corresponding to the CPR3 controller
+ *			pointer
+ *
+ * This function disables processor clock throttling and closed-loop operation
+ * when the ceiling voltage is reached.
+ *
+ * Return: IRQ_HANDLED
+ */
+static irqreturn_t cpr3_ceiling_irq_handler(int irq, void *data)
+{
+	struct cpr3_controller *ctrl = data;
+	int rc, volt;
+
+	mutex_lock(&ctrl->lock);
+
+	if (!ctrl->cpr_enabled) {
+		cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is disabled\n");
+		goto done;
+	} else if (!ctrl->use_hw_closed_loop) {
+		cpr3_debug(ctrl, "CPR ceiling interrupt received but CPR is using SW closed-loop\n");
+		goto done;
+	}
+
+	volt = regulator_get_voltage(ctrl->vdd_regulator);
+	if (volt < 0) {
+		cpr3_err(ctrl, "could not get vdd voltage, rc=%d\n", volt);
+		goto done;
+	} else if (volt != ctrl->aggr_corner.ceiling_volt) {
+		cpr3_debug(ctrl, "CPR ceiling interrupt received but vdd voltage: %d uV != ceiling voltage: %d uV\n",
+			volt, ctrl->aggr_corner.ceiling_volt);
+		goto done;
+	}
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		/*
+		 * Since the ceiling voltage has been reached, disable processor
+		 * clock throttling as well as CPR closed-loop operation.
+		 */
+		cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
+				CPR3_PD_THROTTLE_DISABLE);
+		cpr3_ctrl_loop_disable(ctrl);
+		cpr3_debug(ctrl, "CPR closed-loop and throttling disabled\n");
+	}
+
+done:
+	rc = msm_spm_avs_clear_irq(0, MSM_SPM_AVS_IRQ_MAX);
+	if (rc)
+		cpr3_err(ctrl, "could not clear max IRQ, rc=%d\n", rc);
+
+	mutex_unlock(&ctrl->lock);
+	return IRQ_HANDLED;
+}
+
+/**
+ * cpr3_regulator_vreg_register() - register a regulator device for a CPR3
+ *		regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function initializes all regulator framework related structures and then
+ * calls regulator_register() for the CPR3 regulator.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_vreg_register(struct cpr3_regulator *vreg)
+{
+	struct regulator_config config = {};
+	struct regulator_desc *rdesc;
+	struct regulator_init_data *init_data;
+	int rc;
+
+	init_data = of_get_regulator_init_data(vreg->thread->ctrl->dev,
+						vreg->of_node, &vreg->rdesc);
+	if (!init_data) {
+		cpr3_err(vreg, "regulator init data is missing\n");
+		return -EINVAL;
+	}
+
+	init_data->constraints.input_uV = init_data->constraints.max_uV;
+	rdesc			= &vreg->rdesc;
+	if (vreg->thread->ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+		/* CPRh regulators are treated as always-on regulators */
+		rdesc->ops = &cprh_regulator_ops;
+	} else {
+		init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS;
+		rdesc->ops = &cpr3_regulator_ops;
+	}
+
+	rdesc->n_voltages	= vreg->corner_count;
+	rdesc->name		= init_data->constraints.name;
+	rdesc->owner		= THIS_MODULE;
+	rdesc->type		= REGULATOR_VOLTAGE;
+
+	config.dev		= vreg->thread->ctrl->dev;
+	config.driver_data	= vreg;
+	config.init_data	= init_data;
+	config.of_node		= vreg->of_node;
+
+	vreg->rdev = regulator_register(rdesc, &config);
+	if (IS_ERR(vreg->rdev)) {
+		rc = PTR_ERR(vreg->rdev);
+		cpr3_err(vreg, "regulator_register failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int debugfs_int_set(void *data, u64 val)
+{
+	*(int *)data = val;
+	return 0;
+}
+
+static int debugfs_int_get(void *data, u64 *val)
+{
+	*val = *(int *)data;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_int, debugfs_int_get, debugfs_int_set, "%lld\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_int_ro, debugfs_int_get, NULL, "%lld\n");
+DEFINE_SIMPLE_ATTRIBUTE(fops_int_wo, NULL, debugfs_int_set, "%lld\n");
+
+/**
+ * debugfs_create_int - create a debugfs file that is used to read and write a
+ *		signed int value
+ * @name:		Pointer to a string containing the name of the file to
+ *			create
+ * @mode:		The permissions that the file should have
+ * @parent:		Pointer to the parent dentry for this file.  This should
+ *			be a directory dentry if set.  If this parameter is
+ *			%NULL, then the file will be created in the root of the
+ *			debugfs filesystem.
+ * @value:		Pointer to the variable that the file should read to and
+ *			write from
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the variable @value.  If the @mode variable is so
+ * set, it can be read from, and written to.
+ *
+ * This function will return a pointer to a dentry if it succeeds.  This
+ * pointer must be passed to the debugfs_remove() function when the file is
+ * to be removed.  If an error occurs, %NULL will be returned.
+ */
+static struct dentry *debugfs_create_int(const char *name, umode_t mode,
+				struct dentry *parent, int *value)
+{
+	/* if there are no write bits set, make read only */
+	if (!(mode & 0222))
+		return debugfs_create_file(name, mode, parent, value,
+					   &fops_int_ro);
+	/* if there are no read bits set, make write only */
+	if (!(mode & 0444))
+		return debugfs_create_file(name, mode, parent, value,
+					   &fops_int_wo);
+
+	return debugfs_create_file(name, mode, parent, value, &fops_int);
+}
+
+static int debugfs_bool_get(void *data, u64 *val)
+{
+	*val = *(bool *)data;
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(fops_bool_ro, debugfs_bool_get, NULL, "%lld\n");
+
+/**
+ * cpr3_debug_ldo_mode_allowed_set() - debugfs callback used to change the
+ *		value of the CPR3 regulator ldo_mode_allowed flag
+ * @data:		Pointer to private data which is equal to the CPR3
+ *			regulator pointer
+ * @val:		New value for ldo_mode_allowed
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_ldo_mode_allowed_set(void *data, u64 val)
+{
+	struct cpr3_regulator *vreg = data;
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	bool allow = !!val;
+	int rc, vdd_volt;
+
+	mutex_lock(&ctrl->lock);
+
+	if (vreg->ldo_mode_allowed == allow)
+		goto done;
+
+	vreg->ldo_mode_allowed = allow;
+
+	if (!allow && vreg->ldo_regulator_bypass == LDO_MODE) {
+		vdd_volt = regulator_get_voltage(ctrl->vdd_regulator);
+		if (vdd_volt < 0) {
+			cpr3_err(vreg, "regulator_get_voltage(vdd) failed, rc=%d\n",
+				 vdd_volt);
+			goto done;
+		}
+
+		/* Switch back to BHS */
+		rc = cpr3_regulator_set_bhs_mode(vreg, vdd_volt,
+				       ctrl->aggr_corner.ceiling_volt);
+		if (rc) {
+			cpr3_err(vreg, "unable to switch to BHS mode, rc=%d\n",
+				 rc);
+			goto done;
+		}
+	} else {
+		rc = cpr3_regulator_update_ctrl_state(ctrl);
+		if (rc) {
+			cpr3_err(vreg, "could not change LDO mode=%s, rc=%d\n",
+				allow ? "allowed" : "disallowed", rc);
+			goto done;
+		}
+	}
+
+	cpr3_debug(vreg, "LDO mode=%s\n", allow ? "allowed" : "disallowed");
+
+done:
+	mutex_unlock(&ctrl->lock);
+	return 0;
+}
+
+/**
+ * cpr3_debug_ldo_mode_allowed_get() - debugfs callback used to retrieve the
+ *		value of the CPR3 regulator ldo_mode_allowed flag
+ * @data:		Pointer to private data which is equal to the CPR3
+ *			regulator pointer
+ * @val:		Output parameter written with a value of the
+ *			ldo_mode_allowed flag
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_ldo_mode_allowed_get(void *data, u64 *val)
+{
+	struct cpr3_regulator *vreg = data;
+
+	*val = vreg->ldo_mode_allowed;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_ldo_mode_allowed_fops,
+			cpr3_debug_ldo_mode_allowed_get,
+			cpr3_debug_ldo_mode_allowed_set,
+			"%llu\n");
+
+/**
+ * cpr3_debug_ldo_mode_get() - debugfs callback used to retrieve the state of
+ *		the CPR3 regulator's LDO
+ * @data:		Pointer to private data which is equal to the CPR3
+ *			regulator pointer
+ * @val:		Output parameter written with a value of 1 if using
+ *			LDO mode or 0 if the LDO is bypassed
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_ldo_mode_get(void *data, u64 *val)
+{
+	struct cpr3_regulator *vreg = data;
+
+	*val = (vreg->ldo_regulator_bypass == LDO_MODE);
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_ldo_mode_fops, cpr3_debug_ldo_mode_get,
+			NULL, "%llu\n");
+
+/**
+ * struct cpr3_debug_corner_info - data structure used by the
+ *		cpr3_debugfs_create_corner_int function
+ * @vreg:		Pointer to the CPR3 regulator
+ * @index:		Pointer to the corner array index
+ * @member_offset:	Offset in bytes from the beginning of struct cpr3_corner
+ *			to the beginning of the value to be read from
+ * @corner:		Pointer to the CPR3 corner array
+ */
+struct cpr3_debug_corner_info {
+	struct cpr3_regulator	*vreg;
+	int			*index;
+	size_t			member_offset;
+	struct cpr3_corner	*corner;
+};
+
+static int cpr3_debug_corner_int_get(void *data, u64 *val)
+{
+	struct cpr3_debug_corner_info *info = data;
+	struct cpr3_controller *ctrl = info->vreg->thread->ctrl;
+	int i;
+
+	mutex_lock(&ctrl->lock);
+
+	i = *info->index;
+	if (i < 0)
+		i = 0;
+
+	*val = *(int *)((char *)&info->vreg->corner[i] + info->member_offset);
+
+	mutex_unlock(&ctrl->lock);
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_int_fops, cpr3_debug_corner_int_get,
+			NULL, "%lld\n");
+
+/**
+ * cpr3_debugfs_create_corner_int - create a debugfs file that is used to read
+ *		a signed int value out of a CPR3 regulator's corner array
+ * @vreg:		Pointer to the CPR3 regulator
+ * @name:		Pointer to a string containing the name of the file to
+ *			create
+ * @mode:		The permissions that the file should have
+ * @parent:		Pointer to the parent dentry for this file.  This should
+ *			be a directory dentry if set.  If this parameter is
+ *			%NULL, then the file will be created in the root of the
+ *			debugfs filesystem.
+ * @index:		Pointer to the corner array index
+ * @member_offset:	Offset in bytes from the beginning of struct cpr3_corner
+ *			to the beginning of the value to be read from
+ *
+ * This function creates a file in debugfs with the given name that
+ * contains the value of the int type variable vreg->corner[index].member
+ * where member_offset == offsetof(struct cpr3_corner, member).
+ */
+static struct dentry *cpr3_debugfs_create_corner_int(
+		struct cpr3_regulator *vreg, const char *name, umode_t mode,
+		struct dentry *parent, int *index, size_t member_offset)
+{
+	struct cpr3_debug_corner_info *info;
+
+	info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+
+	info->vreg = vreg;
+	info->index = index;
+	info->member_offset = member_offset;
+
+	return debugfs_create_file(name, mode, parent, info,
+				   &cpr3_debug_corner_int_fops);
+}
+
+static int cpr3_debug_quot_open(struct inode *inode, struct file *file)
+{
+	struct cpr3_debug_corner_info *info = inode->i_private;
+	struct cpr3_thread *thread = info->vreg->thread;
+	int size, i, pos;
+	u32 *quot;
+	char *buf;
+
+	/*
+	 * Max size:
+	 *  - 10 digits + ' ' or '\n' = 11 bytes per number
+	 *  - terminating '\0'
+	 */
+	size = CPR3_RO_COUNT * 11;
+	buf = kzalloc(size + 1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	file->private_data = buf;
+
+	mutex_lock(&thread->ctrl->lock);
+
+	quot = info->corner[*info->index].target_quot;
+
+	for (i = 0, pos = 0; i < CPR3_RO_COUNT; i++)
+		pos += scnprintf(buf + pos, size - pos, "%u%c",
+			quot[i], i < CPR3_RO_COUNT - 1 ? ' ' : '\n');
+
+	mutex_unlock(&thread->ctrl->lock);
+
+	return nonseekable_open(inode, file);
+}
+
+static ssize_t cpr3_debug_quot_read(struct file *file, char __user *buf,
+		size_t len, loff_t *ppos)
+{
+	return simple_read_from_buffer(buf, len, ppos, file->private_data,
+					strlen(file->private_data));
+}
+
+static int cpr3_debug_quot_release(struct inode *inode, struct file *file)
+{
+	kfree(file->private_data);
+
+	return 0;
+}
+
+static const struct file_operations cpr3_debug_quot_fops = {
+	.owner	 = THIS_MODULE,
+	.open	 = cpr3_debug_quot_open,
+	.release = cpr3_debug_quot_release,
+	.read	 = cpr3_debug_quot_read,
+	.llseek  = no_llseek,
+};
+
+/**
+ * cpr3_regulator_debugfs_corner_add() - add debugfs files to expose
+ *		configuration data for the CPR corner
+ * @vreg:		Pointer to the CPR3 regulator
+ * @corner_dir:		Pointer to the parent corner dentry for the new files
+ * @index:		Pointer to the corner array index
+ *
+ * Return: none
+ */
+static void cpr3_regulator_debugfs_corner_add(struct cpr3_regulator *vreg,
+		struct dentry *corner_dir, int *index)
+{
+	struct cpr3_debug_corner_info *info;
+	struct dentry *temp;
+
+	temp = cpr3_debugfs_create_corner_int(vreg, "floor_volt", 0444,
+		corner_dir, index, offsetof(struct cpr3_corner, floor_volt));
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "floor_volt debugfs file creation failed\n");
+		return;
+	}
+
+	temp = cpr3_debugfs_create_corner_int(vreg, "ceiling_volt", 0444,
+		corner_dir, index, offsetof(struct cpr3_corner, ceiling_volt));
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "ceiling_volt debugfs file creation failed\n");
+		return;
+	}
+
+	temp = cpr3_debugfs_create_corner_int(vreg, "open_loop_volt", 0444,
+		corner_dir, index,
+		offsetof(struct cpr3_corner, open_loop_volt));
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "open_loop_volt debugfs file creation failed\n");
+		return;
+	}
+
+	temp = cpr3_debugfs_create_corner_int(vreg, "last_volt", 0444,
+		corner_dir, index, offsetof(struct cpr3_corner, last_volt));
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "last_volt debugfs file creation failed\n");
+		return;
+	}
+
+	info = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return;
+
+	info->vreg = vreg;
+	info->index = index;
+	info->corner = vreg->corner;
+
+	temp = debugfs_create_file("target_quots", 0444, corner_dir, info,
+				&cpr3_debug_quot_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "target_quots debugfs file creation failed\n");
+		return;
+	}
+}
+
+/**
+ * cpr3_debug_corner_index_set() - debugfs callback used to change the
+ *		value of the CPR3 regulator debug_corner index
+ * @data:		Pointer to private data which is equal to the CPR3
+ *			regulator pointer
+ * @val:		New value for debug_corner
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_corner_index_set(void *data, u64 val)
+{
+	struct cpr3_regulator *vreg = data;
+
+	if (val < CPR3_CORNER_OFFSET || val > vreg->corner_count) {
+		cpr3_err(vreg, "invalid corner index %llu; allowed values: %d-%d\n",
+			val, CPR3_CORNER_OFFSET, vreg->corner_count);
+		return -EINVAL;
+	}
+
+	mutex_lock(&vreg->thread->ctrl->lock);
+	vreg->debug_corner = val - CPR3_CORNER_OFFSET;
+	mutex_unlock(&vreg->thread->ctrl->lock);
+
+	return 0;
+}
+
+/**
+ * cpr3_debug_corner_index_get() - debugfs callback used to retrieve
+ *		the value of the CPR3 regulator debug_corner index
+ * @data:		Pointer to private data which is equal to the CPR3
+ *			regulator pointer
+ * @val:		Output parameter written with the value of
+ *			debug_corner
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_corner_index_get(void *data, u64 *val)
+{
+	struct cpr3_regulator *vreg = data;
+
+	*val = vreg->debug_corner + CPR3_CORNER_OFFSET;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_corner_index_fops,
+			cpr3_debug_corner_index_get,
+			cpr3_debug_corner_index_set,
+			"%llu\n");
+
+/**
+ * cpr3_debug_current_corner_index_get() - debugfs callback used to retrieve
+ *		the value of the CPR3 regulator current_corner index
+ * @data:		Pointer to private data which is equal to the CPR3
+ *			regulator pointer
+ * @val:		Output parameter written with the value of
+ *			current_corner
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_current_corner_index_get(void *data, u64 *val)
+{
+	struct cpr3_regulator *vreg = data;
+
+	*val = vreg->current_corner + CPR3_CORNER_OFFSET;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_current_corner_index_fops,
+			cpr3_debug_current_corner_index_get,
+			NULL, "%llu\n");
+
+/**
+ * cpr3_regulator_debugfs_vreg_add() - add debugfs files to expose configuration
+ *		data for the CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ * @thread_dir		CPR3 thread debugfs directory handle
+ *
+ * Return: none
+ */
+static void cpr3_regulator_debugfs_vreg_add(struct cpr3_regulator *vreg,
+				struct dentry *thread_dir)
+{
+	struct dentry *temp, *corner_dir, *vreg_dir;
+
+	vreg_dir = debugfs_create_dir(vreg->name, thread_dir);
+	if (IS_ERR_OR_NULL(vreg_dir)) {
+		cpr3_err(vreg, "%s debugfs directory creation failed\n",
+			vreg->name);
+		return;
+	}
+
+	temp = debugfs_create_int("speed_bin_fuse", 0444, vreg_dir,
+				  &vreg->speed_bin_fuse);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "speed_bin_fuse debugfs file creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_int("cpr_rev_fuse", 0444, vreg_dir,
+				  &vreg->cpr_rev_fuse);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "cpr_rev_fuse debugfs file creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_int("fuse_combo", 0444, vreg_dir,
+				  &vreg->fuse_combo);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "fuse_combo debugfs file creation failed\n");
+		return;
+	}
+
+	if (vreg->ldo_regulator) {
+		temp = debugfs_create_file("ldo_mode", 0444, vreg_dir, vreg,
+					&cpr3_debug_ldo_mode_fops);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(vreg, "ldo_mode debugfs file creation failed\n");
+			return;
+		}
+
+		temp = debugfs_create_file("ldo_mode_allowed",
+				0644, vreg_dir, vreg,
+				&cpr3_debug_ldo_mode_allowed_fops);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(vreg, "ldo_mode_allowed debugfs file creation failed\n");
+			return;
+		}
+	}
+
+	temp = debugfs_create_int("corner_count", 0444, vreg_dir,
+				  &vreg->corner_count);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "corner_count debugfs file creation failed\n");
+		return;
+	}
+
+	corner_dir = debugfs_create_dir("corner", vreg_dir);
+	if (IS_ERR_OR_NULL(corner_dir)) {
+		cpr3_err(vreg, "corner debugfs directory creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_file("index", 0644, corner_dir, vreg,
+				&cpr3_debug_corner_index_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "index debugfs file creation failed\n");
+		return;
+	}
+
+	cpr3_regulator_debugfs_corner_add(vreg, corner_dir,
+					&vreg->debug_corner);
+
+	corner_dir = debugfs_create_dir("current_corner", vreg_dir);
+	if (IS_ERR_OR_NULL(corner_dir)) {
+		cpr3_err(vreg, "current_corner debugfs directory creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_file("index", 0444, corner_dir, vreg,
+				&cpr3_debug_current_corner_index_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(vreg, "index debugfs file creation failed\n");
+		return;
+	}
+
+	cpr3_regulator_debugfs_corner_add(vreg, corner_dir,
+					  &vreg->current_corner);
+}
+
+/**
+ * cpr3_regulator_debugfs_thread_add() - add debugfs files to expose
+ *		configuration data for the CPR thread
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: none
+ */
+static void cpr3_regulator_debugfs_thread_add(struct cpr3_thread *thread)
+{
+	struct cpr3_controller *ctrl = thread->ctrl;
+	struct dentry *aggr_dir, *temp, *thread_dir;
+	struct cpr3_debug_corner_info *info;
+	char buf[20];
+	int *index;
+	int i;
+
+	scnprintf(buf, sizeof(buf), "thread%u", thread->thread_id);
+	thread_dir = debugfs_create_dir(buf, thread->ctrl->debugfs);
+	if (IS_ERR_OR_NULL(thread_dir)) {
+		cpr3_err(ctrl, "thread %u %s debugfs directory creation failed\n",
+			thread->thread_id, buf);
+		return;
+	}
+
+	aggr_dir = debugfs_create_dir("max_aggregated_params", thread_dir);
+	if (IS_ERR_OR_NULL(aggr_dir)) {
+		cpr3_err(ctrl, "thread %u max_aggregated_params debugfs directory creation failed\n",
+			thread->thread_id);
+		return;
+	}
+
+	temp = debugfs_create_int("floor_volt", 0444, aggr_dir,
+				  &thread->aggr_corner.floor_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "thread %u aggr floor_volt debugfs file creation failed\n",
+			thread->thread_id);
+		return;
+	}
+
+	temp = debugfs_create_int("ceiling_volt", 0444, aggr_dir,
+				  &thread->aggr_corner.ceiling_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "thread %u aggr ceiling_volt debugfs file creation failed\n",
+			thread->thread_id);
+		return;
+	}
+
+	temp = debugfs_create_int("open_loop_volt", 0444, aggr_dir,
+				  &thread->aggr_corner.open_loop_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "thread %u aggr open_loop_volt debugfs file creation failed\n",
+			thread->thread_id);
+		return;
+	}
+
+	temp = debugfs_create_int("last_volt", 0444, aggr_dir,
+				  &thread->aggr_corner.last_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "thread %u aggr last_volt debugfs file creation failed\n",
+			thread->thread_id);
+		return;
+	}
+
+	info = devm_kzalloc(thread->ctrl->dev, sizeof(*info), GFP_KERNEL);
+	index = devm_kzalloc(thread->ctrl->dev, sizeof(*index), GFP_KERNEL);
+	if (!info || !index)
+		return;
+	*index = 0;
+	info->vreg = &thread->vreg[0];
+	info->index = index;
+	info->corner = &thread->aggr_corner;
+
+	temp = debugfs_create_file("target_quots", 0444, aggr_dir, info,
+				&cpr3_debug_quot_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "thread %u target_quots debugfs file creation failed\n",
+			thread->thread_id);
+		return;
+	}
+
+	for (i = 0; i < thread->vreg_count; i++)
+		cpr3_regulator_debugfs_vreg_add(&thread->vreg[i], thread_dir);
+}
+
+/**
+ * cpr3_debug_closed_loop_enable_set() - debugfs callback used to change the
+ *		value of the CPR controller cpr_allowed_sw flag which enables or
+ *		disables closed-loop operation
+ * @data:		Pointer to private data which is equal to the CPR
+ *			controller pointer
+ * @val:		New value for cpr_allowed_sw
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_closed_loop_enable_set(void *data, u64 val)
+{
+	struct cpr3_controller *ctrl = data;
+	bool enable = !!val;
+	int rc;
+
+	mutex_lock(&ctrl->lock);
+
+	if (ctrl->cpr_allowed_sw == enable)
+		goto done;
+
+	if (enable && !ctrl->cpr_allowed_hw) {
+		cpr3_err(ctrl, "CPR closed-loop operation is not allowed\n");
+		goto done;
+	}
+
+	ctrl->cpr_allowed_sw = enable;
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+			ctrl->cpr_allowed_sw && ctrl->use_hw_closed_loop
+			? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
+			: CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
+	} else {
+		rc = cpr3_regulator_update_ctrl_state(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "could not change CPR enable state=%u, rc=%d\n",
+				 enable, rc);
+			goto done;
+		}
+
+		if (ctrl->proc_clock_throttle && !ctrl->cpr_enabled) {
+			rc = cpr3_clock_enable(ctrl);
+			if (rc) {
+				cpr3_err(ctrl, "clock enable failed, rc=%d\n",
+					 rc);
+				goto done;
+			}
+			ctrl->cpr_enabled = true;
+
+			cpr3_write(ctrl, CPR3_REG_PD_THROTTLE,
+				   CPR3_PD_THROTTLE_DISABLE);
+
+			cpr3_clock_disable(ctrl);
+			ctrl->cpr_enabled = false;
+		}
+	}
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		cpr3_debug(ctrl, "closed-loop=%s\n", enable ?
+			   "enabled" : "disabled");
+	} else {
+		cpr3_debug(ctrl, "closed-loop=%s\n", enable &&
+			   ctrl->use_hw_closed_loop ? "enabled" : "disabled");
+	}
+done:
+	mutex_unlock(&ctrl->lock);
+	return 0;
+}
+
+/**
+ * cpr3_debug_closed_loop_enable_get() - debugfs callback used to retrieve
+ *		the value of the CPR controller cpr_allowed_sw flag which
+ *		indicates if closed-loop operation is enabled
+ * @data:		Pointer to private data which is equal to the CPR
+ *			controller pointer
+ * @val:		Output parameter written with the value of
+ *			cpr_allowed_sw
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_closed_loop_enable_get(void *data, u64 *val)
+{
+	struct cpr3_controller *ctrl = data;
+
+	*val = ctrl->cpr_allowed_sw;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_closed_loop_enable_fops,
+			cpr3_debug_closed_loop_enable_get,
+			cpr3_debug_closed_loop_enable_set,
+			"%llu\n");
+
+/**
+ * cpr3_debug_hw_closed_loop_enable_set() - debugfs callback used to change the
+ *		value of the CPR controller use_hw_closed_loop flag which
+ *		switches between software closed-loop and hardware closed-loop
+ *		operation for CPR3 and CPR4 controllers and between open-loop
+ *		and full hardware closed-loop operation for CPRh controllers.
+ * @data:		Pointer to private data which is equal to the CPR
+ *			controller pointer
+ * @val:		New value for use_hw_closed_loop
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_hw_closed_loop_enable_set(void *data, u64 val)
+{
+	struct cpr3_controller *ctrl = data;
+	bool use_hw_closed_loop = !!val;
+	struct cpr3_regulator *vreg;
+	bool cpr_enabled;
+	int i, j, k, rc;
+
+	mutex_lock(&ctrl->lock);
+
+	if (ctrl->use_hw_closed_loop == use_hw_closed_loop)
+		goto done;
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
+		cpr3_ctrl_loop_disable(ctrl);
+
+	ctrl->use_hw_closed_loop = use_hw_closed_loop;
+
+	cpr_enabled = ctrl->cpr_enabled;
+
+	/* Ensure that CPR clocks are enabled before writing to registers. */
+	if (!cpr_enabled) {
+		rc = cpr3_clock_enable(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "clock enable failed, rc=%d\n", rc);
+			goto done;
+		}
+		ctrl->cpr_enabled = true;
+	}
+
+	if (ctrl->use_hw_closed_loop && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH)
+		cpr3_write(ctrl, CPR3_REG_IRQ_EN, 0);
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+			ctrl->use_hw_closed_loop
+			? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
+			: CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
+	} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+		cpr3_masked_write(ctrl, CPR4_REG_MARGIN_ADJ_CTL,
+			CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_EN_MASK,
+			ctrl->cpr_allowed_sw && ctrl->use_hw_closed_loop
+			? CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_ENABLE
+			: CPR4_MARGIN_ADJ_CTL_HW_CLOSED_LOOP_DISABLE);
+	} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		cpr3_write(ctrl, CPR3_REG_HW_CLOSED_LOOP,
+			ctrl->use_hw_closed_loop
+			? CPR3_HW_CLOSED_LOOP_ENABLE
+			: CPR3_HW_CLOSED_LOOP_DISABLE);
+	}
+
+	/* Turn off CPR clocks if they were off before this function call. */
+	if (!cpr_enabled) {
+		cpr3_clock_disable(ctrl);
+		ctrl->cpr_enabled = false;
+	}
+
+	if (ctrl->use_hw_closed_loop && ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		rc = regulator_enable(ctrl->vdd_limit_regulator);
+		if (rc) {
+			cpr3_err(ctrl, "CPR limit regulator enable failed, rc=%d\n",
+				rc);
+			goto done;
+		}
+
+		rc = msm_spm_avs_enable_irq(0, MSM_SPM_AVS_IRQ_MAX);
+		if (rc) {
+			cpr3_err(ctrl, "could not enable max IRQ, rc=%d\n", rc);
+			goto done;
+		}
+	} else if (!ctrl->use_hw_closed_loop
+			&& ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		rc = regulator_disable(ctrl->vdd_limit_regulator);
+		if (rc) {
+			cpr3_err(ctrl, "CPR limit regulator disable failed, rc=%d\n",
+				rc);
+			goto done;
+		}
+
+		rc = msm_spm_avs_disable_irq(0, MSM_SPM_AVS_IRQ_MAX);
+		if (rc) {
+			cpr3_err(ctrl, "could not disable max IRQ, rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		/*
+		 * Due to APM and mem-acc floor restriction constraints,
+		 * the closed-loop voltage may be different when using
+		 * software closed-loop vs hardware closed-loop.  Therefore,
+		 * reset the cached closed-loop voltage for all corners to the
+		 * corresponding open-loop voltage when switching between
+		 * SW and HW closed-loop mode.
+		 */
+		for (i = 0; i < ctrl->thread_count; i++) {
+			for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+				vreg = &ctrl->thread[i].vreg[j];
+				for (k = 0; k < vreg->corner_count; k++)
+					vreg->corner[k].last_volt
+					= vreg->corner[k].open_loop_volt;
+			}
+		}
+
+		/* Skip last_volt caching */
+		ctrl->last_corner_was_closed_loop = false;
+
+		rc = cpr3_regulator_update_ctrl_state(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "could not change CPR HW closed-loop enable state=%u, rc=%d\n",
+				 use_hw_closed_loop, rc);
+			goto done;
+		}
+
+		cpr3_debug(ctrl, "CPR mode=%s\n",
+			   use_hw_closed_loop ?
+			   "HW closed-loop" : "SW closed-loop");
+	} else {
+		cpr3_debug(ctrl, "CPR mode=%s\n",
+			   ctrl->cpr_allowed_sw && use_hw_closed_loop ?
+			   "full HW closed-loop" : "open-loop");
+	}
+done:
+	mutex_unlock(&ctrl->lock);
+	return 0;
+}
+
+/**
+ * cpr3_debug_hw_closed_loop_enable_get() - debugfs callback used to retrieve
+ *		the value of the CPR controller use_hw_closed_loop flag which
+ *		indicates if hardware closed-loop operation is being used in
+ *		place of software closed-loop operation
+ * @data:		Pointer to private data which is equal to the CPR
+ *			controller pointer
+ * @val:		Output parameter written with the value of
+ *			use_hw_closed_loop
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_hw_closed_loop_enable_get(void *data, u64 *val)
+{
+	struct cpr3_controller *ctrl = data;
+
+	*val = ctrl->use_hw_closed_loop;
+
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_hw_closed_loop_enable_fops,
+			cpr3_debug_hw_closed_loop_enable_get,
+			cpr3_debug_hw_closed_loop_enable_set,
+			"%llu\n");
+
+/**
+ * cpr3_debug_trigger_aging_measurement_set() - debugfs callback used to trigger
+ *		another CPR measurement
+ * @data:		Pointer to private data which is equal to the CPR
+ *			controller pointer
+ * @val:		Unused
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_debug_trigger_aging_measurement_set(void *data, u64 val)
+{
+	struct cpr3_controller *ctrl = data;
+	int rc;
+
+	mutex_lock(&ctrl->lock);
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	cpr3_ctrl_loop_disable(ctrl);
+
+	cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX);
+	ctrl->aging_required = true;
+	ctrl->aging_succeeded = false;
+	ctrl->aging_failed = false;
+
+	rc = cpr3_regulator_update_ctrl_state(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "could not update the CPR controller state, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+done:
+	mutex_unlock(&ctrl->lock);
+	return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(cpr3_debug_trigger_aging_measurement_fops,
+			NULL,
+			cpr3_debug_trigger_aging_measurement_set,
+			"%llu\n");
+
+/**
+ * cpr3_regulator_debugfs_ctrl_add() - add debugfs files to expose configuration
+ *		data for the CPR controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: none
+ */
+static void cpr3_regulator_debugfs_ctrl_add(struct cpr3_controller *ctrl)
+{
+	struct dentry *temp, *aggr_dir;
+	int i;
+
+	/* Add cpr3-regulator base directory if it isn't present already. */
+	if (cpr3_debugfs_base == NULL) {
+		cpr3_debugfs_base = debugfs_create_dir("cpr3-regulator", NULL);
+		if (IS_ERR_OR_NULL(cpr3_debugfs_base)) {
+			cpr3_err(ctrl, "cpr3-regulator debugfs base directory creation failed\n");
+			cpr3_debugfs_base = NULL;
+			return;
+		}
+	}
+
+	ctrl->debugfs = debugfs_create_dir(ctrl->name, cpr3_debugfs_base);
+	if (IS_ERR_OR_NULL(ctrl->debugfs)) {
+		cpr3_err(ctrl, "cpr3-regulator controller debugfs directory creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_file("cpr_closed_loop_enable", 0644,
+					ctrl->debugfs, ctrl,
+					&cpr3_debug_closed_loop_enable_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "cpr_closed_loop_enable debugfs file creation failed\n");
+		return;
+	}
+
+	if (ctrl->supports_hw_closed_loop) {
+		temp = debugfs_create_file("use_hw_closed_loop", 0644,
+					ctrl->debugfs, ctrl,
+					&cpr3_debug_hw_closed_loop_enable_fops);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(ctrl, "use_hw_closed_loop debugfs file creation failed\n");
+			return;
+		}
+	}
+
+	temp = debugfs_create_int("thread_count", 0444, ctrl->debugfs,
+				  &ctrl->thread_count);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "thread_count debugfs file creation failed\n");
+		return;
+	}
+
+	if (ctrl->apm) {
+		temp = debugfs_create_int("apm_threshold_volt", 0444,
+				ctrl->debugfs, &ctrl->apm_threshold_volt);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(ctrl, "apm_threshold_volt debugfs file creation failed\n");
+			return;
+		}
+	}
+
+	if (ctrl->aging_required || ctrl->aging_succeeded
+	    || ctrl->aging_failed) {
+		temp = debugfs_create_int("aging_adj_volt", 0444,
+				ctrl->debugfs, &ctrl->aging_ref_adjust_volt);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(ctrl, "aging_adj_volt debugfs file creation failed\n");
+			return;
+		}
+
+		temp = debugfs_create_file("aging_succeeded", 0444,
+			ctrl->debugfs, &ctrl->aging_succeeded, &fops_bool_ro);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(ctrl, "aging_succeeded debugfs file creation failed\n");
+			return;
+		}
+
+		temp = debugfs_create_file("aging_failed", 0444,
+			ctrl->debugfs, &ctrl->aging_failed, &fops_bool_ro);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(ctrl, "aging_failed debugfs file creation failed\n");
+			return;
+		}
+
+		temp = debugfs_create_file("aging_trigger", 0200,
+			ctrl->debugfs, ctrl,
+			&cpr3_debug_trigger_aging_measurement_fops);
+		if (IS_ERR_OR_NULL(temp)) {
+			cpr3_err(ctrl, "aging_trigger debugfs file creation failed\n");
+			return;
+		}
+	}
+
+	aggr_dir = debugfs_create_dir("max_aggregated_voltages", ctrl->debugfs);
+	if (IS_ERR_OR_NULL(aggr_dir)) {
+		cpr3_err(ctrl, "max_aggregated_voltages debugfs directory creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_int("floor_volt", 0444, aggr_dir,
+				  &ctrl->aggr_corner.floor_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "aggr floor_volt debugfs file creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_int("ceiling_volt", 0444, aggr_dir,
+				  &ctrl->aggr_corner.ceiling_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "aggr ceiling_volt debugfs file creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_int("open_loop_volt", 0444, aggr_dir,
+				  &ctrl->aggr_corner.open_loop_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "aggr open_loop_volt debugfs file creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_int("last_volt", 0444, aggr_dir,
+				  &ctrl->aggr_corner.last_volt);
+	if (IS_ERR_OR_NULL(temp)) {
+		cpr3_err(ctrl, "aggr last_volt debugfs file creation failed\n");
+		return;
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++)
+		cpr3_regulator_debugfs_thread_add(&ctrl->thread[i]);
+}
+
+/**
+ * cpr3_regulator_debugfs_ctrl_remove() - remove debugfs files for the CPR
+ *		controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Note, this function must be called after the controller has been removed from
+ * cpr3_controller_list and while the cpr3_controller_list_mutex lock is held.
+ *
+ * Return: none
+ */
+static void cpr3_regulator_debugfs_ctrl_remove(struct cpr3_controller *ctrl)
+{
+	if (list_empty(&cpr3_controller_list)) {
+		debugfs_remove_recursive(cpr3_debugfs_base);
+		cpr3_debugfs_base = NULL;
+	} else {
+		debugfs_remove_recursive(ctrl->debugfs);
+	}
+}
+
+/**
+ * cpr3_regulator_init_ctrl_data() - performs initialization of CPR controller
+ *					elements
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_ctrl_data(struct cpr3_controller *ctrl)
+{
+	/* Read the initial vdd voltage from hardware. */
+	ctrl->aggr_corner.last_volt
+		= regulator_get_voltage(ctrl->vdd_regulator);
+	if (ctrl->aggr_corner.last_volt < 0) {
+		cpr3_err(ctrl, "regulator_get_voltage(vdd) failed, rc=%d\n",
+				ctrl->aggr_corner.last_volt);
+		return ctrl->aggr_corner.last_volt;
+	}
+	ctrl->aggr_corner.open_loop_volt = ctrl->aggr_corner.last_volt;
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_init_vreg_data() - performs initialization of common CPR3
+ *		regulator elements and validate aging configurations
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_init_vreg_data(struct cpr3_regulator *vreg)
+{
+	int i, j;
+	bool init_aging;
+
+	vreg->current_corner = CPR3_REGULATOR_CORNER_INVALID;
+	vreg->last_closed_loop_corner = CPR3_REGULATOR_CORNER_INVALID;
+
+	init_aging = vreg->aging_allowed && vreg->thread->ctrl->aging_required;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		vreg->corner[i].last_volt = vreg->corner[i].open_loop_volt;
+		vreg->corner[i].irq_en = CPR3_IRQ_UP | CPR3_IRQ_DOWN;
+
+		vreg->corner[i].ro_mask = 0;
+		for (j = 0; j < CPR3_RO_COUNT; j++) {
+			if (vreg->corner[i].target_quot[j] == 0)
+				vreg->corner[i].ro_mask |= BIT(j);
+		}
+
+		if (init_aging) {
+			vreg->corner[i].unaged_floor_volt
+				= vreg->corner[i].floor_volt;
+			vreg->corner[i].unaged_ceiling_volt
+				= vreg->corner[i].ceiling_volt;
+			vreg->corner[i].unaged_open_loop_volt
+				= vreg->corner[i].open_loop_volt;
+		}
+
+		if (vreg->aging_allowed) {
+			if (vreg->corner[i].unaged_floor_volt <= 0) {
+				cpr3_err(vreg, "invalid unaged_floor_volt[%d] = %d\n",
+					i, vreg->corner[i].unaged_floor_volt);
+				return -EINVAL;
+			}
+			if (vreg->corner[i].unaged_ceiling_volt <= 0) {
+				cpr3_err(vreg, "invalid unaged_ceiling_volt[%d] = %d\n",
+					i, vreg->corner[i].unaged_ceiling_volt);
+				return -EINVAL;
+			}
+			if (vreg->corner[i].unaged_open_loop_volt <= 0) {
+				cpr3_err(vreg, "invalid unaged_open_loop_volt[%d] = %d\n",
+				      i, vreg->corner[i].unaged_open_loop_volt);
+				return -EINVAL;
+			}
+		}
+	}
+
+	if (vreg->aging_allowed && vreg->corner[vreg->aging_corner].ceiling_volt
+	    > vreg->thread->ctrl->aging_ref_volt) {
+		cpr3_err(vreg, "aging corner %d ceiling voltage = %d > aging ref voltage = %d uV\n",
+			vreg->aging_corner,
+			vreg->corner[vreg->aging_corner].ceiling_volt,
+			vreg->thread->ctrl->aging_ref_volt);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_regulator_suspend() - perform common required CPR3 power down steps
+ *		before the system enters suspend
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_regulator_suspend(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	mutex_lock(&ctrl->lock);
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4) {
+		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc);
+			mutex_unlock(&ctrl->lock);
+			return rc;
+		}
+	}
+
+	cpr3_ctrl_loop_disable(ctrl);
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		rc = cpr3_closed_loop_disable(ctrl);
+		if (rc)
+			cpr3_err(ctrl, "could not disable CPR, rc=%d\n", rc);
+
+		ctrl->cpr_suspended = true;
+	}
+
+	mutex_unlock(&ctrl->lock);
+	return 0;
+}
+
+/**
+ * cpr3_regulator_resume() - perform common required CPR3 power up steps after
+ *		the system resumes from suspend
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_regulator_resume(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	mutex_lock(&ctrl->lock);
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		ctrl->cpr_suspended = false;
+		rc = cpr3_regulator_update_ctrl_state(ctrl);
+		if (rc)
+			cpr3_err(ctrl, "could not enable CPR, rc=%d\n", rc);
+	} else {
+		cpr3_ctrl_loop_enable(ctrl);
+	}
+
+	mutex_unlock(&ctrl->lock);
+	return 0;
+}
+
+/**
+ * cpr3_regulator_cpu_hotplug_callback() - reset CPR IRQ affinity when a CPU is
+ *		brought online via hotplug
+ * @nb:			Pointer to the notifier block
+ * @action:		hotplug action
+ * @hcpu:		long value corresponding to the CPU number
+ *
+ * Return: NOTIFY_OK
+ */
+static int cpr3_regulator_cpu_hotplug_callback(struct notifier_block *nb,
+					    unsigned long action, void *hcpu)
+{
+	struct cpr3_controller *ctrl = container_of(nb, struct cpr3_controller,
+					cpu_hotplug_notifier);
+	int cpu = (long)hcpu;
+
+	action &= ~CPU_TASKS_FROZEN;
+
+	if (action == CPU_ONLINE
+	    && cpumask_test_cpu(cpu, &ctrl->irq_affinity_mask))
+		irq_set_affinity(ctrl->irq, &ctrl->irq_affinity_mask);
+
+	return NOTIFY_OK;
+}
+
+/**
+ * cpr3_regulator_validate_controller() - verify the data passed in via the
+ *		cpr3_controller data structure
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_regulator_validate_controller(struct cpr3_controller *ctrl)
+{
+	struct cpr3_thread *thread;
+	struct cpr3_regulator *vreg;
+	int i, j, allow_boost_vreg_count = 0;
+
+	if (!ctrl->vdd_regulator && ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		cpr3_err(ctrl, "vdd regulator missing\n");
+		return -EINVAL;
+	} else if (ctrl->sensor_count <= 0
+		   || ctrl->sensor_count > CPR3_MAX_SENSOR_COUNT) {
+		cpr3_err(ctrl, "invalid CPR sensor count=%d\n",
+			ctrl->sensor_count);
+		return -EINVAL;
+	} else if (!ctrl->sensor_owner) {
+		cpr3_err(ctrl, "CPR sensor ownership table missing\n");
+		return -EINVAL;
+	}
+
+	if (ctrl->aging_required) {
+		for (i = 0; i < ctrl->aging_sensor_count; i++) {
+			if (ctrl->aging_sensor[i].sensor_id
+			    >= ctrl->sensor_count) {
+				cpr3_err(ctrl, "aging_sensor[%d] id=%u is not in the value range 0-%d",
+					i, ctrl->aging_sensor[i].sensor_id,
+					ctrl->sensor_count - 1);
+				return -EINVAL;
+			}
+		}
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		thread = &ctrl->thread[i];
+		for (j = 0; j < thread->vreg_count; j++) {
+			vreg = &thread->vreg[j];
+			if (vreg->allow_boost)
+				allow_boost_vreg_count++;
+		}
+	}
+
+	if (allow_boost_vreg_count > 1) {
+		/*
+		 * Boost feature is not allowed to be used for more
+		 * than one CPR3 regulator of a CPR3 controller.
+		 */
+		cpr3_err(ctrl, "Boost feature is enabled for more than one regulator\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_panic_callback() - panic notification callback function. This function
+ *		is invoked when a kernel panic occurs.
+ * @nfb:	Notifier block pointer of CPR3 controller
+ * @event:	Value passed unmodified to notifier function
+ * @data:	Pointer passed unmodified to notifier function
+ *
+ * Return: NOTIFY_OK
+ */
+static int cpr3_panic_callback(struct notifier_block *nfb,
+			unsigned long event, void *data)
+{
+	struct cpr3_controller *ctrl = container_of(nfb,
+				struct cpr3_controller, panic_notifier);
+	struct cpr3_panic_regs_info *regs_info = ctrl->panic_regs_info;
+	struct cpr3_reg_info *reg;
+	int i = 0;
+
+	for (i = 0; i < regs_info->reg_count; i++) {
+		reg = &(regs_info->regs[i]);
+		reg->value = readl_relaxed(reg->virt_addr);
+		pr_err("%s[0x%08x] = 0x%08x\n", reg->name, reg->addr,
+			reg->value);
+	}
+	/*
+	 * Barrier to ensure that the information has been updated in the
+	 * structure.
+	 */
+	mb();
+
+	return NOTIFY_OK;
+}
+
+/**
+ * cpr3_regulator_register() - register the regulators for a CPR3 controller and
+ *		perform CPR hardware initialization
+ * @pdev:		Platform device pointer for the CPR3 controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_regulator_register(struct platform_device *pdev,
+			struct cpr3_controller *ctrl)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	int i, j, rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "%s: Device tree node is missing\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!ctrl || !ctrl->name) {
+		dev_err(dev, "%s: CPR controller data is missing\n", __func__);
+		return -EINVAL;
+	}
+
+	rc = cpr3_regulator_validate_controller(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "controller validation failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	mutex_init(&ctrl->lock);
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "cpr_ctrl");
+	if (!res || !res->start) {
+		cpr3_err(ctrl, "CPR controller address is missing\n");
+		return -ENXIO;
+	}
+	ctrl->cpr_ctrl_base = devm_ioremap(dev, res->start, resource_size(res));
+
+	if (ctrl->aging_possible_mask) {
+		/*
+		 * Aging possible register address is required if an aging
+		 * possible mask has been specified.
+		 */
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"aging_allowed");
+		if (!res || !res->start) {
+			cpr3_err(ctrl, "CPR aging allowed address is missing\n");
+			return -ENXIO;
+		}
+		ctrl->aging_possible_reg = devm_ioremap(dev, res->start,
+							resource_size(res));
+	}
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		ctrl->irq = platform_get_irq_byname(pdev, "cpr");
+		if (ctrl->irq < 0) {
+			cpr3_err(ctrl, "missing CPR interrupt\n");
+			return ctrl->irq;
+		}
+	}
+
+	if (ctrl->supports_hw_closed_loop) {
+		rc = msm_spm_probe_done();
+		if (rc) {
+			if (rc != -EPROBE_DEFER)
+				cpr3_err(ctrl, "spm unavailable, rc=%d\n", rc);
+			return rc;
+		}
+
+		if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+			ctrl->ceiling_irq = platform_get_irq_byname(pdev,
+						"ceiling");
+			if (ctrl->ceiling_irq < 0) {
+				cpr3_err(ctrl, "missing ceiling interrupt\n");
+				return ctrl->ceiling_irq;
+			}
+		}
+	}
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		rc = cpr3_regulator_init_ctrl_data(ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "CPR controller data initialization failed, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			rc = cpr3_regulator_init_vreg_data(
+						&ctrl->thread[i].vreg[j]);
+			if (rc)
+				return rc;
+			cpr3_print_quots(&ctrl->thread[i].vreg[j]);
+		}
+	}
+
+	/*
+	 * Add the maximum possible aging voltage margin until it is possible
+	 * to perform an aging measurement.
+	 */
+	if (ctrl->aging_required)
+		cpr3_regulator_set_aging_ref_adjustment(ctrl, INT_MAX);
+
+	rc = cpr3_regulator_init_ctrl(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "CPR controller initialization failed, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	/* Register regulator devices for all threads. */
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			rc = cpr3_regulator_vreg_register(
+					&ctrl->thread[i].vreg[j]);
+			if (rc) {
+				cpr3_err(&ctrl->thread[i].vreg[j], "failed to register regulator, rc=%d\n",
+					rc);
+				goto free_regulators;
+			}
+		}
+	}
+
+	if (ctrl->ctrl_type != CPR_CTRL_TYPE_CPRH) {
+		rc = devm_request_threaded_irq(dev, ctrl->irq, NULL,
+					       cpr3_irq_handler,
+					       IRQF_ONESHOT |
+					       IRQF_TRIGGER_RISING,
+					       "cpr3", ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "could not request IRQ %d, rc=%d\n",
+				 ctrl->irq, rc);
+			goto free_regulators;
+		}
+	}
+
+	if (ctrl->supports_hw_closed_loop &&
+	    ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3) {
+		rc = devm_request_threaded_irq(dev, ctrl->ceiling_irq, NULL,
+			cpr3_ceiling_irq_handler,
+			IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+			"cpr3_ceiling", ctrl);
+		if (rc) {
+			cpr3_err(ctrl, "could not request ceiling IRQ %d, rc=%d\n",
+				ctrl->ceiling_irq, rc);
+			goto free_regulators;
+		}
+	}
+
+	if (ctrl->irq && !cpumask_empty(&ctrl->irq_affinity_mask)) {
+		irq_set_affinity(ctrl->irq, &ctrl->irq_affinity_mask);
+
+		ctrl->cpu_hotplug_notifier.notifier_call
+			= cpr3_regulator_cpu_hotplug_callback;
+		register_hotcpu_notifier(&ctrl->cpu_hotplug_notifier);
+	}
+
+	mutex_lock(&cpr3_controller_list_mutex);
+	cpr3_regulator_debugfs_ctrl_add(ctrl);
+	list_add(&ctrl->list, &cpr3_controller_list);
+	mutex_unlock(&cpr3_controller_list_mutex);
+
+	if (ctrl->panic_regs_info) {
+		/* Register panic notification call back */
+		ctrl->panic_notifier.notifier_call = cpr3_panic_callback;
+		atomic_notifier_chain_register(&panic_notifier_list,
+			&ctrl->panic_notifier);
+	}
+
+	return 0;
+
+free_regulators:
+	for (i = 0; i < ctrl->thread_count; i++)
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++)
+			if (!IS_ERR_OR_NULL(ctrl->thread[i].vreg[j].rdev))
+				regulator_unregister(
+					ctrl->thread[i].vreg[j].rdev);
+	return rc;
+}
+
+/**
+ * cpr3_regulator_unregister() - unregister the regulators for a CPR3 controller
+ *		and perform CPR hardware shutdown
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_regulator_unregister(struct cpr3_controller *ctrl)
+{
+	int i, j, rc = 0;
+
+	mutex_lock(&cpr3_controller_list_mutex);
+	list_del(&ctrl->list);
+	cpr3_regulator_debugfs_ctrl_remove(ctrl);
+	mutex_unlock(&cpr3_controller_list_mutex);
+
+	if (ctrl->irq && !cpumask_empty(&ctrl->irq_affinity_mask))
+		unregister_hotcpu_notifier(&ctrl->cpu_hotplug_notifier);
+
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR4)
+		rc = cpr3_ctrl_clear_cpr4_config(ctrl);
+		if (rc)
+			cpr3_err(ctrl, "failed to clear CPR4 configuration,rc=%d\n",
+				rc);
+
+	cpr3_ctrl_loop_disable(ctrl);
+
+	cpr3_closed_loop_disable(ctrl);
+
+	if (ctrl->vdd_limit_regulator) {
+		regulator_disable(ctrl->vdd_limit_regulator);
+
+		if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPR3)
+			msm_spm_avs_disable_irq(0, MSM_SPM_AVS_IRQ_MAX);
+	}
+
+	for (i = 0; i < ctrl->thread_count; i++)
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++)
+			regulator_unregister(ctrl->thread[i].vreg[j].rdev);
+
+	if (ctrl->panic_notifier.notifier_call)
+		atomic_notifier_chain_unregister(&panic_notifier_list,
+			&ctrl->panic_notifier);
+
+	return 0;
+}
diff --git a/drivers/regulator/cpr3-regulator.h b/drivers/regulator/cpr3-regulator.h
new file mode 100644
index 0000000..7dae23c
--- /dev/null
+++ b/drivers/regulator/cpr3-regulator.h
@@ -0,0 +1,1095 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __REGULATOR_CPR3_REGULATOR_H__
+#define __REGULATOR_CPR3_REGULATOR_H__
+
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/power/qcom/apm.h>
+#include <linux/regulator/driver.h>
+
+struct cpr3_controller;
+struct cpr3_thread;
+
+/**
+ * struct cpr3_fuse_param - defines one contiguous segment of a fuse parameter
+ *			    that is contained within a given row.
+ * @row:	Fuse row number
+ * @bit_start:	The first bit within the row of the fuse parameter segment
+ * @bit_end:	The last bit within the row of the fuse parameter segment
+ *
+ * Each fuse row is 64 bits in length.  bit_start and bit_end may take values
+ * from 0 to 63.  bit_start must be less than or equal to bit_end.
+ */
+struct cpr3_fuse_param {
+	unsigned int		row;
+	unsigned int		bit_start;
+	unsigned int		bit_end;
+};
+
+/* Each CPR3 sensor has 16 ring oscillators */
+#define CPR3_RO_COUNT		16
+
+/* The maximum number of sensors that can be present on a single CPR loop. */
+#define CPR3_MAX_SENSOR_COUNT	256
+
+/* This constant is used when allocating array printing buffers. */
+#define MAX_CHARS_PER_INT	10
+
+/**
+ * struct cpr4_sdelta - CPR4 controller specific data structure for the sdelta
+ *			adjustment table which is used to adjust the VDD supply
+ *			voltage automatically based upon the temperature and/or
+ *			the number of online CPU cores.
+ * @allow_core_count_adj: Core count adjustments are allowed.
+ * @allow_temp_adj:	Temperature based adjustments are allowed.
+ * @max_core_count:	Maximum number of cores considered for core count
+ *			adjustment logic.
+ * @temp_band_count:	Number of temperature bands considered for temperature
+ *			based adjustment logic.
+ * @cap_volt:		CAP in uV to apply to SDELTA margins with multiple
+ *			cpr3-regulators defined for single controller.
+ * @table:		SDELTA table with per-online-core and temperature based
+ *			adjustments of size (max_core_count * temp_band_count)
+ *			Outer: core count
+ *			Inner: temperature band
+ *			Each element has units of VDD supply steps. Positive
+ *			values correspond to a reduction in voltage and negative
+ *			value correspond to an increase (this follows the SDELTA
+ *			register semantics).
+ * @allow_boost:	Voltage boost allowed.
+ * @boost_num_cores:	The number of online cores at which the boost voltage
+ *			adjustments will be applied
+ * @boost_table:	SDELTA table with boost voltage adjustments of size
+ *			temp_band_count. Each element has units of VDD supply
+ *			steps. Positive values correspond to a reduction in
+ *			voltage and negative value correspond to an increase
+ *			(this follows the SDELTA register semantics).
+ */
+struct cpr4_sdelta {
+	bool	allow_core_count_adj;
+	bool	allow_temp_adj;
+	int	max_core_count;
+	int	temp_band_count;
+	int	cap_volt;
+	int	*table;
+	bool	allow_boost;
+	int	boost_num_cores;
+	int	*boost_table;
+};
+
+/**
+ * struct cpr3_corner - CPR3 virtual voltage corner data structure
+ * @floor_volt:		CPR closed-loop floor voltage in microvolts
+ * @ceiling_volt:	CPR closed-loop ceiling voltage in microvolts
+ * @open_loop_volt:	CPR open-loop voltage (i.e. initial voltage) in
+ *			microvolts
+ * @last_volt:		Last known settled CPR closed-loop voltage which is used
+ *			when switching to a new corner
+ * @abs_ceiling_volt:	The absolute CPR closed-loop ceiling voltage in
+ *			microvolts.  This is used to limit the ceiling_volt
+ *			value when it is increased as a result of aging
+ *			adjustment.
+ * @unaged_floor_volt:	The CPR closed-loop floor voltage in microvolts before
+ *			any aging adjustment is performed
+ * @unaged_ceiling_volt: The CPR closed-loop ceiling voltage in microvolts
+ *			before any aging adjustment is performed
+ * @unaged_open_loop_volt: The CPR open-loop voltage (i.e. initial voltage) in
+ *			microvolts before any aging adjusment is performed
+ * @system_volt:	The system-supply voltage in microvolts or corners or
+ *			levels
+ * @mem_acc_volt:	The mem-acc-supply voltage in corners
+ * @proc_freq:		Processor frequency in Hertz. For CPR rev. 3 and 4
+ *			conrollers, this field is only used by platform specific
+ *			CPR3 driver for interpolation. For CPRh-compliant
+ *			controllers, this frequency is also utilized by the
+ *			clock driver to determine the corner to CPU clock
+ *			frequency mappings.
+ * @cpr_fuse_corner:	Fused corner index associated with this virtual corner
+ *			(only used by platform specific CPR3 driver for
+ *			mapping purposes)
+ * @target_quot:	Array of target quotient values to use for each ring
+ *			oscillator (RO) for this corner.  A value of 0 should be
+ *			specified as the target quotient for each RO that is
+ *			unused by this corner.
+ * @ro_scale:		Array of CPR ring oscillator (RO) scaling factors.  The
+ *			scaling factor for each RO is defined from RO0 to RO15
+ *			with units of QUOT/V.  A value of 0 may be specified for
+ *			an RO that is unused.
+ * @ro_mask:		Bitmap where each of the 16 LSBs indicate if the
+ *			corresponding ROs should be masked for this corner
+ * @irq_en:		Bitmap of the CPR interrupts to enable for this corner
+ * @aging_derate:	The amount to derate the aging voltage adjustment
+ *			determined for the reference corner in units of uV/mV.
+ *			E.g. a value of 900 would imply that the adjustment for
+ *			this corner should be 90% (900/1000) of that for the
+ *			reference corner.
+ * @use_open_loop:	Boolean indicating that open-loop (i.e CPR disabled) as
+ *			opposed to closed-loop operation must be used for this
+ *			corner on CPRh controllers.
+ * @ldo_mode_allowed:	Boolean which indicates if LDO mode is allowed for this
+ *			corner. This field is applicable for CPR4 controllers
+ *			that manage LDO300 supply regulator.
+ * @sdelta:		The CPR4 controller specific data for this corner. This
+ *			field is applicable for CPR4 controllers.
+ *
+ * The value of last_volt is initialized inside of the cpr3_regulator_register()
+ * call with the open_loop_volt value.  It can later be updated to the settled
+ * VDD supply voltage.  The values for unaged_floor_volt, unaged_ceiling_volt,
+ * and unaged_open_loop_volt are initialized inside of cpr3_regulator_register()
+ * if ctrl->aging_required == true.  These three values must be pre-initialized
+ * if cpr3_regulator_register() is called with ctrl->aging_required == false and
+ * ctrl->aging_succeeded == true.
+ *
+ * The values of ro_mask and irq_en are initialized inside of the
+ * cpr3_regulator_register() call.
+ */
+struct cpr3_corner {
+	int			floor_volt;
+	int			ceiling_volt;
+	int			open_loop_volt;
+	int			last_volt;
+	int			abs_ceiling_volt;
+	int			unaged_floor_volt;
+	int			unaged_ceiling_volt;
+	int			unaged_open_loop_volt;
+	int			system_volt;
+	int			mem_acc_volt;
+	u32			proc_freq;
+	int			cpr_fuse_corner;
+	u32			target_quot[CPR3_RO_COUNT];
+	u32			ro_scale[CPR3_RO_COUNT];
+	u32			ro_mask;
+	u32			irq_en;
+	int			aging_derate;
+	bool			use_open_loop;
+	bool			ldo_mode_allowed;
+	struct cpr4_sdelta	*sdelta;
+};
+
+/**
+ * struct cprh_corner_band - CPRh controller specific data structure which
+ *			encapsulates the range of corners and the SDELTA
+ *			adjustment table to be applied to the corners within
+ *			the min and max bounds of the corner band.
+ * @corner:		Corner number which defines the corner band boundary
+ * @sdelta:		The SDELTA adjustment table which contains core-count
+ *			and temp based margin adjustments that are applicable
+ *			to the corner band.
+ */
+struct cprh_corner_band {
+	int			corner;
+	struct cpr4_sdelta	*sdelta;
+};
+
+/**
+ * enum cpr3_ldo_type - Constants which define the LDO supply regulator
+ *	types used to manage the subsystem component rail voltage.
+ * %CPR3_LDO_KRYO:	Kryo LDO regulator used to sub-regulate the HMSS
+ *			per-cluster voltage.
+ * %CPR3_LDO300:	LDO regulator used to sub-regulate the GFX voltage.
+ */
+enum cpr3_ldo_type {
+	CPR3_LDO_KRYO	= 0,
+	CPR3_LDO300	= 1,
+};
+
+/**
+ * struct cpr3_regulator - CPR3 logical regulator instance associated with a
+ *			given CPR3 hardware thread
+ * @of_node:		Device node associated with the device tree child node
+ *			of this CPR3 regulator
+ * @thread:		Pointer to the CPR3 thread which manages this CPR3
+ *			regulator
+ * @name:		Unique name for this CPR3 regulator which is filled
+ *			using the device tree regulator-name property
+ * @rdesc:		Regulator description for this CPR3 regulator
+ * @rdev:		Regulator device pointer for the regulator registered
+ *			for this CPR3 regulator
+ * @mem_acc_regulator:	Pointer to the optional mem-acc supply regulator used
+ *			to manage memory circuitry settings based upon CPR3
+ *			regulator output voltage.
+ * @ldo_regulator:	Pointer to the LDO supply regulator used to manage
+ *			per-cluster LDO voltage and bypass state
+ * @ldo_regulator_bypass: Cached copy of the LDO regulator bypass state
+ * @ldo_ret_regulator:	Pointer to the LDO retention supply regulator used to
+ *			manage LDO retention bypass state
+ * @corner:		Array of all corners supported by this CPR3 regulator
+ * @corner_count:	The number of elements in the corner array
+ * @corner_band:	Array of all corner bands supported by CPRh compatible
+ *			controllers
+ * @corner_band_count:	The number of elements in the corner band array
+ * @platform_fuses:	Pointer to platform specific CPR fuse data (only used by
+ *			platform specific CPR3 driver)
+ * @speed_bin_fuse:	Value read from the speed bin fuse parameter
+ * @speed_bins_supported: The number of speed bins supported by the device tree
+ *			configuration for this CPR3 regulator
+ * @cpr_rev_fuse:	Value read from the CPR fusing revision fuse parameter
+ * @fuse_combo:		Platform specific enum value identifying the specific
+ *			combination of fuse values found on a given chip
+ * @fuse_combos_supported: The number of fuse combinations supported by the
+ *			device tree configuration for this CPR3 regulator
+ * @fuse_corner_count:	Number of corners defined by fuse parameters
+ * @fuse_corner_map:	Array of length fuse_corner_count which specifies the
+ *			highest corner associated with each fuse corner.  Note
+ *			that each element must correspond to a valid corner
+ *			and that element values must be strictly increasing.
+ *			Also, it is acceptable for the lowest fuse corner to map
+ *			to a corner other than the lowest.  Likewise, it is
+ *			acceptable for the highest fuse corner to map to a
+ *			corner other than the highest.
+ * @fuse_combo_corner_sum: The sum of the corner counts across all fuse combos
+ * @fuse_combo_offset:	The device tree property array offset for the selected
+ *			fuse combo
+ * @speed_bin_corner_sum: The sum of the corner counts across all speed bins
+ *			This may be specified as 0 if per speed bin parsing
+ *			support is not required.
+ * @speed_bin_offset:	The device tree property array offset for the selected
+ *			speed bin
+ * @fuse_combo_corner_band_sum: The sum of the corner band counts across all
+ *			fuse combos
+ * @fuse_combo_corner_band_offset: The device tree property array offset for
+ *			the corner band count corresponding to the selected
+ *			fuse combo
+ * @speed_bin_corner_band_sum: The sum of the corner band counts across all
+ *			speed bins. This may be specified as 0 if per speed bin
+ *			parsing support is not required
+ * @speed_bin_corner_band_offset: The device tree property array offset for the
+ *			corner band count corresponding to the selected speed
+ *			bin
+ * @pd_bypass_mask:	Bit mask of power domains associated with this CPR3
+ *			regulator
+ * @dynamic_floor_corner: Index identifying the voltage corner for the CPR3
+ *			regulator whose last_volt value should be used as the
+ *			global CPR floor voltage if all of the power domains
+ *			associated with this CPR3 regulator are bypassed
+ * @uses_dynamic_floor: Boolean flag indicating that dynamic_floor_corner should
+ *			be utilized for the CPR3 regulator
+ * @current_corner:	Index identifying the currently selected voltage corner
+ *			for the CPR3 regulator or less than 0 if no corner has
+ *			been requested
+ * @last_closed_loop_corner: Index identifying the last voltage corner for the
+ *			CPR3 regulator which was configured when operating in
+ *			CPR closed-loop mode or less than 0 if no corner has
+ *			been requested.  CPR registers are only written to when
+ *			using closed-loop mode.
+ * @aggregated:		Boolean flag indicating that this CPR3 regulator
+ *			participated in the last aggregation event
+ * @debug_corner:	Index identifying voltage corner used for displaying
+ *			corner configuration values in debugfs
+ * @ldo_type:		LDO regulator type.
+ * @ldo_min_headroom_volt: Minimum voltage difference in microvolts required
+ *			between the VDD supply voltage and the LDO output in
+ *			order for the LDO operate
+ * @ldo_max_headroom_volt: Maximum voltage difference in microvolts between
+ *			the input and output of the active LDO hardware to
+ *			maintain optimum operability.
+ * @ldo_adjust_volt:	Voltage in microvolts used to offset margin assigned
+ *			to IR drop between PMIC and CPU
+ * @ldo_ret_volt:	The lowest supported CPU retention voltage in
+ *			microvolts. This voltage may vary part-to-part based
+ *			upon the value of hardware fuses.
+ * @ldo_max_volt:	The maximum physically supported LDO voltage in
+ *			microvolts
+ * @ldo_mode_allowed:	Boolean which indicates if LDO mode is allowed for this
+ *			CPR3 regulator
+ * @vreg_enabled:	Boolean defining the enable state of the CPR3
+ *			regulator's regulator within the regulator framework.
+ * @aging_allowed:	Boolean defining if CPR aging adjustments are allowed
+ *			for this CPR3 regulator given the fuse combo of the
+ *			device
+ * @aging_allow_open_loop_adj: Boolean defining if the open-loop voltage of each
+ *			corner of this regulator should be adjusted as a result
+ *			of an aging measurement.  This flag can be set to false
+ *			when the open-loop voltage adjustments have been
+ *			specified such that they include the maximum possible
+ *			aging adjustment.  This flag is only used if
+ *			aging_allowed == true.
+ * @aging_corner:	The corner that should be configured for this regulator
+ *			when an aging measurement is performed.
+ * @aging_max_adjust_volt: The maximum aging voltage margin in microvolts that
+ *			may be added to the target quotients of this regulator.
+ *			A value of 0 may be specified if this regulator does not
+ *			require any aging adjustment.
+ * @allow_core_count_adj: Core count adjustments are allowed for this regulator.
+ * @allow_temp_adj:	Temperature based adjustments are allowed for this
+ *			regulator.
+ * @max_core_count:	Maximum number of cores considered for core count
+ *			adjustment logic.
+ * @allow_boost:	Voltage boost allowed for this regulator.
+ *
+ * This structure contains both configuration and runtime state data.  The
+ * elements current_corner, last_closed_loop_corner, aggregated, debug_corner,
+ * ldo_mode_allowed, and vreg_enabled are state variables.
+ */
+struct cpr3_regulator {
+	struct device_node	*of_node;
+	struct cpr3_thread	*thread;
+	const char		*name;
+	struct regulator_desc	rdesc;
+	struct regulator_dev	*rdev;
+	struct regulator	*mem_acc_regulator;
+	struct regulator	*ldo_regulator;
+	bool			ldo_regulator_bypass;
+	struct regulator	*ldo_ret_regulator;
+	struct cpr3_corner	*corner;
+	int			corner_count;
+	struct cprh_corner_band *corner_band;
+	u32			corner_band_count;
+
+	void			*platform_fuses;
+	int			speed_bin_fuse;
+	int			speed_bins_supported;
+	int			cpr_rev_fuse;
+	int			fuse_combo;
+	int			fuse_combos_supported;
+	int			fuse_corner_count;
+	int			*fuse_corner_map;
+	int			fuse_combo_corner_sum;
+	int			fuse_combo_offset;
+	int			speed_bin_corner_sum;
+	int			speed_bin_offset;
+	int			fuse_combo_corner_band_sum;
+	int			fuse_combo_corner_band_offset;
+	int			speed_bin_corner_band_sum;
+	int			speed_bin_corner_band_offset;
+	u32			pd_bypass_mask;
+	int			dynamic_floor_corner;
+	bool			uses_dynamic_floor;
+
+	int			current_corner;
+	int			last_closed_loop_corner;
+	bool			aggregated;
+	int			debug_corner;
+	enum cpr3_ldo_type	ldo_type;
+	int			ldo_min_headroom_volt;
+	int			ldo_max_headroom_volt;
+	int			ldo_adjust_volt;
+	int			ldo_ret_volt;
+	int			ldo_max_volt;
+	bool			ldo_mode_allowed;
+	bool			vreg_enabled;
+
+	bool			aging_allowed;
+	bool			aging_allow_open_loop_adj;
+	int			aging_corner;
+	int			aging_max_adjust_volt;
+
+	bool			allow_core_count_adj;
+	bool			allow_temp_adj;
+	int			max_core_count;
+	bool			allow_boost;
+};
+
+/**
+ * struct cpr3_thread - CPR3 hardware thread data structure
+ * @thread_id:		Hardware thread ID
+ * @of_node:		Device node associated with the device tree child node
+ *			of this CPR3 thread
+ * @ctrl:		Pointer to the CPR3 controller which manages this thread
+ * @vreg:		Array of CPR3 regulators handled by the CPR3 thread
+ * @vreg_count:		Number of elements in the vreg array
+ * @aggr_corner:	CPR corner containing the in process aggregated voltage
+ *			and target quotient configurations which will be applied
+ * @last_closed_loop_aggr_corner: CPR corner containing the most recent
+ *			configurations which were written into hardware
+ *			registers when operating in closed loop mode (i.e. with
+ *			CPR enabled)
+ * @consecutive_up:	The number of consecutive CPR step up events needed to
+ *			to trigger an up interrupt
+ * @consecutive_down:	The number of consecutive CPR step down events needed to
+ *			to trigger a down interrupt
+ * @up_threshold:	The number CPR error steps required to generate an up
+ *			event
+ * @down_threshold:	The number CPR error steps required to generate a down
+ *			event
+ *
+ * This structure contains both configuration and runtime state data.  The
+ * elements aggr_corner and last_closed_loop_aggr_corner are state variables.
+ */
+struct cpr3_thread {
+	u32			thread_id;
+	struct device_node	*of_node;
+	struct cpr3_controller	*ctrl;
+	struct cpr3_regulator	*vreg;
+	int			vreg_count;
+	struct cpr3_corner	aggr_corner;
+	struct cpr3_corner	last_closed_loop_aggr_corner;
+
+	u32			consecutive_up;
+	u32			consecutive_down;
+	u32			up_threshold;
+	u32			down_threshold;
+};
+
+/* Per CPR controller data */
+/**
+ * enum cpr3_mem_acc_corners - Constants which define the number of mem-acc
+ *		regulator corners available in the mem-acc corner map array.
+ * %CPR3_MEM_ACC_LOW_CORNER:	Index in mem-acc corner map array mapping to the
+ *				mem-acc regulator corner
+ *				to be used for low voltage vdd supply
+ * %CPR3_MEM_ACC_HIGH_CORNER:	Index in mem-acc corner map array mapping to the
+ *				mem-acc regulator corner to be used for high
+ *				voltage vdd supply
+ * %CPR3_MEM_ACC_CORNERS:	Number of elements in the mem-acc corner map
+ *				array
+ */
+enum cpr3_mem_acc_corners {
+	CPR3_MEM_ACC_LOW_CORNER		= 0,
+	CPR3_MEM_ACC_HIGH_CORNER	= 1,
+	CPR3_MEM_ACC_CORNERS		= 2,
+};
+
+/**
+ * enum cpr3_count_mode - CPR3 controller count mode which defines the
+ *		method that CPR sensor data is acquired
+ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MIN:	Capture all CPR sensor readings
+ *					simultaneously and report the minimum
+ *					value seen in successive measurements
+ * %CPR3_COUNT_MODE_ALL_AT_ONCE_MAX:	Capture all CPR sensor readings
+ *					simultaneously and report the maximum
+ *					value seen in successive measurements
+ * %CPR3_COUNT_MODE_STAGGERED:		Read one sensor at a time in a
+ *					sequential fashion
+ * %CPR3_COUNT_MODE_ALL_AT_ONCE_AGE:	Capture all CPR aging sensor readings
+ *					simultaneously.
+ */
+enum cpr3_count_mode {
+	CPR3_COUNT_MODE_ALL_AT_ONCE_MIN	= 0,
+	CPR3_COUNT_MODE_ALL_AT_ONCE_MAX	= 1,
+	CPR3_COUNT_MODE_STAGGERED	= 2,
+	CPR3_COUNT_MODE_ALL_AT_ONCE_AGE	= 3,
+};
+
+/**
+ * enum cpr_controller_type - supported CPR controller hardware types
+ * %CPR_CTRL_TYPE_CPR3:	HW has CPR3 controller
+ * %CPR_CTRL_TYPE_CPR4:	HW has CPR4 controller
+ * %CPR_CTRL_TYPE_CPRH:	HW has CPRh controller
+ */
+enum cpr_controller_type {
+	CPR_CTRL_TYPE_CPR3,
+	CPR_CTRL_TYPE_CPR4,
+	CPR_CTRL_TYPE_CPRH,
+};
+
+/**
+ * struct cpr3_aging_sensor_info - CPR3 aging sensor information
+ * @sensor_id		The index of the CPR3 sensor to be used in the aging
+ *			measurement.
+ * @ro_scale		The CPR ring oscillator (RO) scaling factor for the
+ *			aging sensor with units of QUOT/V.
+ * @init_quot_diff:	The fused quotient difference between aged and un-aged
+ *			paths that was measured at manufacturing time.
+ * @measured_quot_diff: The quotient difference measured at runtime.
+ * @bypass_mask:	Bit mask of the CPR sensors that must be bypassed during
+ *			the aging measurement for this sensor
+ *
+ * This structure contains both configuration and runtime state data.  The
+ * element measured_quot_diff is a state variable.
+ */
+struct cpr3_aging_sensor_info {
+	u32			sensor_id;
+	u32			ro_scale;
+	int			init_quot_diff;
+	int			measured_quot_diff;
+	u32			bypass_mask[CPR3_MAX_SENSOR_COUNT / 32];
+};
+
+/**
+ * struct cpr3_reg_info - Register information data structure
+ * @name:	Register name
+ * @addr:	Register physical address
+ * @value:	Register content
+ * @virt_addr:	Register virtual address
+ *
+ * This data structure is used to dump some critical register contents
+ * when the device crashes due to a kernel panic.
+ */
+struct cpr3_reg_info {
+	const char	*name;
+	u32		addr;
+	u32		value;
+	void __iomem	*virt_addr;
+};
+
+/**
+ * struct cpr3_panic_regs_info - Data structure to dump critical register
+ *		contents.
+ * @reg_count:		Number of elements in the regs array
+ * @regs:		Array of critical registers information
+ *
+ * This data structure is used to dump critical register contents when
+ * the device crashes due to a kernel panic.
+ */
+struct cpr3_panic_regs_info {
+	int			reg_count;
+	struct cpr3_reg_info	*regs;
+};
+
+/**
+ * struct cpr3_controller - CPR3 controller data structure
+ * @dev:		Device pointer for the CPR3 controller device
+ * @name:		Unique name for the CPR3 controller
+ * @ctrl_id:		Controller ID corresponding to the VDD supply number
+ *			that this CPR3 controller manages.
+ * @cpr_ctrl_base:	Virtual address of the CPR3 controller base register
+ * @fuse_base:		Virtual address of fuse row 0
+ * @aging_possible_reg:	Virtual address of an optional platform-specific
+ *			register that must be ready to determine if it is
+ *			possible to perform an aging measurement.
+ * @list:		list head used in a global cpr3-regulator list so that
+ *			cpr3-regulator structs can be found easily in RAM dumps
+ * @thread:		Array of CPR3 threads managed by the CPR3 controller
+ * @thread_count:	Number of elements in the thread array
+ * @sensor_owner:	Array of thread IDs indicating which thread owns a given
+ *			CPR sensor
+ * @sensor_count:	The number of CPR sensors found on the CPR loop managed
+ *			by this CPR controller.  Must be equal to the number of
+ *			elements in the sensor_owner array
+ * @soc_revision:	Revision number of the SoC.  This may be unused by
+ *			platforms that do not have different behavior for
+ *			different SoC revisions.
+ * @lock:		Mutex lock used to ensure mutual exclusion between
+ *			all of the threads associated with the controller
+ * @vdd_regulator:	Pointer to the VDD supply regulator which this CPR3
+ *			controller manages
+ * @system_regulator:	Pointer to the optional system-supply regulator upon
+ *			which the VDD supply regulator depends.
+ * @mem_acc_regulator:	Pointer to the optional mem-acc supply regulator used
+ *			to manage memory circuitry settings based upon the
+ *			VDD supply output voltage.
+ * @vdd_limit_regulator: Pointer to the VDD supply limit regulator which is used
+ *			for hardware closed-loop in order specify ceiling and
+ *			floor voltage limits (platform specific)
+ * @system_supply_max_volt: Voltage in microvolts which corresponds to the
+ *			absolute ceiling voltage of the system-supply
+ * @mem_acc_threshold_volt: mem-acc threshold voltage in microvolts
+ * @mem_acc_corner_map: mem-acc regulator corners mapping to low and high
+ *			voltage mem-acc settings for the memories powered by
+ *			this CPR3 controller and its associated CPR3 regulators
+ * @mem_acc_crossover_volt: Voltage in microvolts corresponding to the voltage
+ *			that the VDD supply must be set to while a MEM ACC
+ *			switch is in progress. This element must be initialized
+ *			for CPRh controllers when a MEM ACC threshold voltage is
+ *			defined.
+ * @core_clk:		Pointer to the CPR3 controller core clock
+ * @iface_clk:		Pointer to the CPR3 interface clock (platform specific)
+ * @bus_clk:		Pointer to the CPR3 bus clock (platform specific)
+ * @irq:		CPR interrupt number
+ * @irq_affinity_mask:	The cpumask for the CPUs which the CPR interrupt should
+ *			have affinity for
+ * @cpu_hotplug_notifier: CPU hotplug notifier used to reset IRQ affinity when a
+ *			CPU is brought back online
+ * @ceiling_irq:	Interrupt number for the interrupt that is triggered
+ *			when hardware closed-loop attempts to exceed the ceiling
+ *			voltage
+ * @apm:		Handle to the array power mux (APM)
+ * @apm_threshold_volt:	Voltage in microvolts which defines the threshold
+ *			voltage to determine the APM supply selection for
+ *			each corner
+ * @apm_crossover_volt:	Voltage in microvolts corresponding to the voltage that
+ *			the VDD supply must be set to while an APM switch is in
+ *			progress. This element must be initialized for CPRh
+ *			controllers when an APM threshold voltage is defined
+ * @apm_adj_volt:	Minimum difference between APM threshold voltage and
+ *			open-loop voltage which allows the APM threshold voltage
+ *			to be used as a ceiling
+ * @apm_high_supply:	APM supply to configure if VDD voltage is greater than
+ *			or equal to the APM threshold voltage
+ * @apm_low_supply:	APM supply to configure if the VDD voltage is less than
+ *			the APM threshold voltage
+ * @base_volt:		Minimum voltage in microvolts supported by the VDD
+ *			supply managed by this CPR controller
+ * @corner_switch_delay_time: The delay time in nanoseconds used by the CPR
+ *			controller to wait for voltage settling before
+ *			acknowledging the OSM block after corner changes
+ * @cpr_clock_rate:	CPR reference clock frequency in Hz.
+ * @sensor_time:	The time in nanoseconds that each sensor takes to
+ *			perform a measurement.
+ * @loop_time:		The time in nanoseconds between consecutive CPR
+ *			measurements.
+ * @up_down_delay_time: The time to delay in nanoseconds between consecutive CPR
+ *			measurements when the last measurement recommended
+ *			increasing or decreasing the vdd-supply voltage.
+ *			(platform specific)
+ * @idle_clocks:	Number of CPR reference clock ticks that the CPR
+ *			controller waits in transitional states.
+ * @step_quot_init_min:	The default minimum CPR step quotient value.  The step
+ *			quotient is the number of additional ring oscillator
+ *			ticks observed when increasing one step in vdd-supply
+ *			output voltage.
+ * @step_quot_init_max:	The default maximum CPR step quotient value.
+ * @step_volt:		Step size in microvolts between available set points
+ *			of the VDD supply
+ * @down_error_step_limit: CPR4 hardware closed-loop down error step limit which
+ *			defines the maximum number of VDD supply regulator steps
+ *			that the voltage may be reduced as the result of a
+ *			single CPR measurement.
+ * @up_error_step_limit: CPR4 hardware closed-loop up error step limit which
+ *			defines the maximum number of VDD supply regulator steps
+ *			that the voltage may be increased as the result of a
+ *			single CPR measurement.
+ * @count_mode:		CPR controller count mode
+ * @count_repeat:	Number of times to perform consecutive sensor
+ *			measurements when using all-at-once count modes.
+ * @proc_clock_throttle: Defines the processor clock frequency throttling
+ *			register value to use.  This can be used to reduce the
+ *			clock frequency when a power domain exits a low power
+ *			mode until CPR settles at a new voltage.
+ *			(platform specific)
+ * @cpr_allowed_hw:	Boolean which indicates if closed-loop CPR operation is
+ *			permitted for a given chip based upon hardware fuse
+ *			values
+ * @cpr_allowed_sw:	Boolean which indicates if closed-loop CPR operation is
+ *			permitted based upon software policies
+ * @supports_hw_closed_loop: Boolean which indicates if this CPR3/4 controller
+ *			physically supports hardware closed-loop CPR operation
+ * @use_hw_closed_loop:	Boolean which indicates that this controller will be
+ *			using hardware closed-loop operation in place of
+ *			software closed-loop operation.
+ * @ctrl_type:		CPR controller type
+ * @saw_use_unit_mV:	Boolean which indicates the unit used in SAW PVC
+ *			interface is mV.
+ * @aggr_corner:	CPR corner containing the most recently aggregated
+ *			voltage configurations which are being used currently
+ * @cpr_enabled:	Boolean which indicates that the CPR controller is
+ *			enabled and operating in closed-loop mode.  CPR clocks
+ *			have been prepared and enabled whenever this flag is
+ *			true.
+ * @last_corner_was_closed_loop: Boolean indicating if the last known corners
+ *			were updated during closed loop operation.
+ * @cpr_suspended:	Boolean which indicates that CPR has been temporarily
+ *			disabled while enterring system suspend.
+ * @debugfs:		Pointer to the debugfs directory of this CPR3 controller
+ * @aging_ref_volt:	Reference voltage in microvolts to configure when
+ *			performing CPR aging measurements.
+ * @aging_vdd_mode:	vdd-supply regulator mode to configure before performing
+ *			a CPR aging measurement.  It should be one of
+ *			REGULATOR_MODE_*.
+ * @aging_complete_vdd_mode: vdd-supply regulator mode to configure after
+ *			performing a CPR aging measurement.  It should be one of
+ *			REGULATOR_MODE_*.
+ * @aging_ref_adjust_volt: The reference aging voltage margin in microvolts that
+ *			should be added to the target quotients of the
+ *			regulators managed by this controller after derating.
+ * @aging_required:	Flag which indicates that a CPR aging measurement still
+ *			needs to be performed for this CPR3 controller.
+ * @aging_succeeded:	Flag which indicates that a CPR aging measurement has
+ *			completed successfully.
+ * @aging_failed:	Flag which indicates that a CPR aging measurement has
+ *			failed to complete successfully.
+ * @aging_sensor:	Array of CPR3 aging sensors which are used to perform
+ *			aging measurements at a runtime.
+ * @aging_sensor_count:	Number of elements in the aging_sensor array
+ * @aging_possible_mask: Optional bitmask used to mask off the
+ *			aging_possible_reg register.
+ * @aging_possible_val:	Optional value that the masked aging_possible_reg
+ *			register must have in order for a CPR aging measurement
+ *			to be possible.
+ * @step_quot_fixed:	Fixed step quotient value used for target quotient
+ *			adjustment if use_dynamic_step_quot is not set.
+ *			This parameter is only relevant for CPR4 controllers
+ *			when using the per-online-core or per-temperature
+ *			adjustments.
+ * @initial_temp_band:	Temperature band used for calculation of base-line
+ *			target quotients (fused).
+ * @use_dynamic_step_quot: Boolean value which indicates that margin adjustment
+ *			of target quotient will be based on the step quotient
+ *			calculated dynamically in hardware for each RO.
+ * @allow_core_count_adj: Core count adjustments are allowed for this controller
+ * @allow_temp_adj:	Temperature based adjustments are allowed for
+ *			this controller
+ * @allow_boost:	Voltage boost allowed for this controller.
+ * @temp_band_count:	Number of temperature bands used for temperature based
+ *			adjustment logic
+ * @temp_points:	Array of temperature points in decidegrees Celsius used
+ *			to specify the ranges for selected temperature bands.
+ *			The array must have (temp_band_count - 1) elements
+ *			allocated.
+ * @temp_sensor_id_start: Start ID of temperature sensors used for temperature
+ *			based adjustments.
+ * @temp_sensor_id_end:	End ID of temperature sensors used for temperature
+ *			based adjustments.
+ * @voltage_settling_time: The time in nanoseconds that it takes for the
+ *			VDD supply voltage to settle after being increased or
+ *			decreased by step_volt microvolts which is used when
+ *			SDELTA voltage margin adjustments are applied.
+ * @panic_regs_info:	Array of panic registers information which provides the
+ *			list of registers to dump when the device crashes.
+ * @panic_notifier:	Notifier block registered to global panic notifier list.
+ * @support_ldo300_vreg: Boolean value which indicates that this CPR controller
+ *			manages an underlying LDO regulator of type LDO300.
+ *
+ * This structure contains both configuration and runtime state data.  The
+ * elements cpr_allowed_sw, use_hw_closed_loop, aggr_corner, cpr_enabled,
+ * last_corner_was_closed_loop, cpr_suspended, aging_ref_adjust_volt,
+ * aging_required, aging_succeeded, and aging_failed are state variables.
+ *
+ * The apm* elements do not need to be initialized if the VDD supply managed by
+ * the CPR3 controller does not utilize an APM.
+ *
+ * The elements step_quot_fixed, initial_temp_band, allow_core_count_adj,
+ * allow_temp_adj and temp* need to be initialized for CPR4 controllers which
+ * are using per-online-core or per-temperature adjustments.
+ */
+struct cpr3_controller {
+	struct device		*dev;
+	const char		*name;
+	int			ctrl_id;
+	void __iomem		*cpr_ctrl_base;
+	void __iomem		*fuse_base;
+	void __iomem		*aging_possible_reg;
+	struct list_head	list;
+	struct cpr3_thread	*thread;
+	int			thread_count;
+	u8			*sensor_owner;
+	int			sensor_count;
+	int			soc_revision;
+	struct mutex		lock;
+	struct regulator	*vdd_regulator;
+	struct regulator	*system_regulator;
+	struct regulator	*mem_acc_regulator;
+	struct regulator	*vdd_limit_regulator;
+	int			system_supply_max_volt;
+	int			mem_acc_threshold_volt;
+	int			mem_acc_corner_map[CPR3_MEM_ACC_CORNERS];
+	int			mem_acc_crossover_volt;
+	struct clk		*core_clk;
+	struct clk		*iface_clk;
+	struct clk		*bus_clk;
+	int			irq;
+	struct cpumask		irq_affinity_mask;
+	struct notifier_block	cpu_hotplug_notifier;
+	int			ceiling_irq;
+	struct msm_apm_ctrl_dev *apm;
+	int			apm_threshold_volt;
+	int			apm_crossover_volt;
+	int			apm_adj_volt;
+	enum msm_apm_supply	apm_high_supply;
+	enum msm_apm_supply	apm_low_supply;
+	int			base_volt;
+	u32			corner_switch_delay_time;
+	u32			cpr_clock_rate;
+	u32			sensor_time;
+	u32			loop_time;
+	u32			up_down_delay_time;
+	u32			idle_clocks;
+	u32			step_quot_init_min;
+	u32			step_quot_init_max;
+	int			step_volt;
+	u32			down_error_step_limit;
+	u32			up_error_step_limit;
+	enum cpr3_count_mode	count_mode;
+	u32			count_repeat;
+	u32			proc_clock_throttle;
+	bool			cpr_allowed_hw;
+	bool			cpr_allowed_sw;
+	bool			supports_hw_closed_loop;
+	bool			use_hw_closed_loop;
+	enum cpr_controller_type ctrl_type;
+	bool			saw_use_unit_mV;
+	struct cpr3_corner	aggr_corner;
+	bool			cpr_enabled;
+	bool			last_corner_was_closed_loop;
+	bool			cpr_suspended;
+	struct dentry		*debugfs;
+
+	int			aging_ref_volt;
+	unsigned int		aging_vdd_mode;
+	unsigned int		aging_complete_vdd_mode;
+	int			aging_ref_adjust_volt;
+	bool			aging_required;
+	bool			aging_succeeded;
+	bool			aging_failed;
+	struct cpr3_aging_sensor_info *aging_sensor;
+	int			aging_sensor_count;
+	u32			aging_possible_mask;
+	u32			aging_possible_val;
+
+	u32			step_quot_fixed;
+	u32			initial_temp_band;
+	bool			use_dynamic_step_quot;
+	bool			allow_core_count_adj;
+	bool			allow_temp_adj;
+	bool			allow_boost;
+	int			temp_band_count;
+	int			*temp_points;
+	u32			temp_sensor_id_start;
+	u32			temp_sensor_id_end;
+	u32			voltage_settling_time;
+	struct cpr3_panic_regs_info *panic_regs_info;
+	struct notifier_block	panic_notifier;
+	bool			support_ldo300_vreg;
+};
+
+/* Used for rounding voltages to the closest physically available set point. */
+#define CPR3_ROUND(n, d) (DIV_ROUND_UP(n, d) * (d))
+
+#define cpr3_err(cpr3_thread, message, ...) \
+	pr_err("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__)
+#define cpr3_info(cpr3_thread, message, ...) \
+	pr_info("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__)
+#define cpr3_debug(cpr3_thread, message, ...) \
+	pr_debug("%s: " message, (cpr3_thread)->name, ##__VA_ARGS__)
+
+/*
+ * Offset subtracted from voltage corner values passed in from the regulator
+ * framework in order to get internal voltage corner values.  This is needed
+ * since the regulator framework treats 0 as an error value at regulator
+ * registration time.
+ */
+#define CPR3_CORNER_OFFSET	1
+
+#ifdef CONFIG_REGULATOR_CPR3
+
+int cpr3_regulator_register(struct platform_device *pdev,
+			struct cpr3_controller *ctrl);
+int cpr3_regulator_unregister(struct cpr3_controller *ctrl);
+int cpr3_regulator_suspend(struct cpr3_controller *ctrl);
+int cpr3_regulator_resume(struct cpr3_controller *ctrl);
+
+int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id,
+			u32 max_thread_id);
+int cpr3_map_fuse_base(struct cpr3_controller *ctrl,
+			struct platform_device *pdev);
+int cpr3_read_fuse_param(void __iomem *fuse_base_addr,
+			const struct cpr3_fuse_param *param, u64 *param_value);
+int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse,
+			int fuse_len);
+u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x);
+int cpr3_parse_array_property(struct cpr3_regulator *vreg,
+			const char *prop_name, int tuple_size, u32 *out);
+int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg,
+			const char *prop_name, int tuple_size, u32 *out);
+int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg,
+			const char *prop_name, int tuple_size, u32 *out);
+int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg);
+int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname,
+			u32 *out_value, u32 value_min, u32 value_max);
+int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname,
+			u32 *out_value, u32 value_min, u32 value_max);
+int cpr3_parse_common_thread_data(struct cpr3_thread *thread);
+int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl);
+int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg);
+void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg);
+int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg);
+void cpr3_print_quots(struct cpr3_regulator *vreg);
+int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg,
+			int *fuse_volt);
+int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg);
+int cpr3_quot_adjustment(int ro_scale, int volt_adjust);
+int cpr3_voltage_adjustment(int ro_scale, int quot_adjust);
+int cpr3_parse_closed_loop_voltage_adjustments(struct cpr3_regulator *vreg,
+			u64 *ro_sel, int *volt_adjust,
+			int *volt_adjust_fuse, int *ro_scale);
+int cpr4_parse_core_count_temp_voltage_adj(struct cpr3_regulator *vreg,
+			bool use_corner_band);
+int cpr3_apm_init(struct cpr3_controller *ctrl);
+int cpr3_mem_acc_init(struct cpr3_regulator *vreg);
+void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg);
+void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg);
+int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg,
+			int *fuse_volt_adjust);
+
+#else
+
+static inline int cpr3_regulator_register(struct platform_device *pdev,
+			struct cpr3_controller *ctrl)
+{
+	return -ENXIO;
+}
+
+static inline int cpr3_regulator_unregister(struct cpr3_controller *ctrl)
+{
+	return -ENXIO;
+}
+
+static inline int cpr3_regulator_suspend(struct cpr3_controller *ctrl)
+{
+	return -ENXIO;
+}
+
+static inline int cpr3_regulator_resume(struct cpr3_controller *ctrl)
+{
+	return -ENXIO;
+}
+
+static inline int cpr3_get_thread_name(struct cpr3_thread *thread,
+			struct device_node *thread_node)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_allocate_threads(struct cpr3_controller *ctrl,
+			u32 min_thread_id, u32 max_thread_id)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_map_fuse_base(struct cpr3_controller *ctrl,
+			struct platform_device *pdev)
+{
+	return -ENXIO;
+}
+
+static inline int cpr3_read_fuse_param(void __iomem *fuse_base_addr,
+			const struct cpr3_fuse_param *param, u64 *param_value)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_convert_open_loop_voltage_fuse(int ref_volt,
+			int step_volt, u32 fuse, int fuse_len)
+{
+	return -EPERM;
+}
+
+static inline u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x)
+{
+	return 0;
+}
+
+static inline int cpr3_parse_array_property(struct cpr3_regulator *vreg,
+			const char *prop_name, int tuple_size, u32 *out)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg,
+			const char *prop_name, int tuple_size, u32 *out)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_corner_band_array_property(
+			struct cpr3_regulator *vreg, const char *prop_name,
+			int tuple_size, u32 *out)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_thread_u32(struct cpr3_thread *thread,
+			const char *propname, u32 *out_value, u32 value_min,
+			u32 value_max)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl,
+			const char *propname, u32 *out_value, u32 value_min,
+			u32 value_max)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_common_thread_data(struct cpr3_thread *thread)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg)
+{
+	return -EPERM;
+}
+
+static inline void cpr3_open_loop_voltage_as_ceiling(
+			struct cpr3_regulator *vreg)
+{
+}
+
+static inline int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg)
+{
+	return -EPERM;
+}
+
+static inline void cpr3_print_quots(struct cpr3_regulator *vreg)
+{
+}
+
+static inline int cpr3_adjust_fused_open_loop_voltages(
+			struct cpr3_regulator *vreg, int *fuse_volt)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg)
+{
+	return -EPERM;
+}
+
+static inline int cpr3_quot_adjustment(int ro_scale, int volt_adjust)
+{
+	return 0;
+}
+
+static inline int cpr3_voltage_adjustment(int ro_scale, int quot_adjust)
+{
+	return 0;
+}
+
+static inline int cpr3_parse_closed_loop_voltage_adjustments(
+			struct cpr3_regulator *vreg, u64 *ro_sel,
+			int *volt_adjust, int *volt_adjust_fuse, int *ro_scale)
+{
+	return 0;
+}
+
+static inline int cpr4_parse_core_count_temp_voltage_adj(
+			struct cpr3_regulator *vreg, bool use_corner_band)
+{
+	return 0;
+}
+
+static inline int cpr3_apm_init(struct cpr3_controller *ctrl)
+{
+	return 0;
+}
+
+static inline int cpr3_mem_acc_init(struct cpr3_regulator *vreg)
+{
+	return 0;
+}
+
+static inline void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
+{
+}
+
+static inline void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg)
+{
+}
+
+static inline int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg,
+			int *fuse_volt_adjust)
+{
+	return 0;
+}
+
+#endif /* CONFIG_REGULATOR_CPR3 */
+
+#endif /* __REGULATOR_CPR_REGULATOR_H__ */
diff --git a/drivers/regulator/cpr3-util.c b/drivers/regulator/cpr3-util.c
new file mode 100644
index 0000000..0a1a1c5
--- /dev/null
+++ b/drivers/regulator/cpr3-util.c
@@ -0,0 +1,2405 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * This file contains utility functions to be used by platform specific CPR3
+ * regulator drivers.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/cpumask.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cpr3-regulator.h"
+
+#define BYTES_PER_FUSE_ROW		8
+#define MAX_FUSE_ROW_BIT		63
+
+#define CPR3_CONSECUTIVE_UP_DOWN_MIN	0
+#define CPR3_CONSECUTIVE_UP_DOWN_MAX	15
+#define CPR3_UP_DOWN_THRESHOLD_MIN	0
+#define CPR3_UP_DOWN_THRESHOLD_MAX	31
+#define CPR3_STEP_QUOT_MIN		0
+#define CPR3_STEP_QUOT_MAX		63
+#define CPR3_IDLE_CLOCKS_MIN		0
+#define CPR3_IDLE_CLOCKS_MAX		31
+
+/* This constant has units of uV/mV so 1000 corresponds to 100%. */
+#define CPR3_AGING_DERATE_UNITY		1000
+
+/**
+ * cpr3_allocate_regulators() - allocate and initialize CPR3 regulators for a
+ *		given thread based upon device tree data
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * This function allocates the thread->vreg array based upon the number of
+ * device tree regulator subnodes.  It also initializes generic elements of each
+ * regulator struct such as name, of_node, and thread.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_allocate_regulators(struct cpr3_thread *thread)
+{
+	struct device_node *node;
+	int i, rc;
+
+	thread->vreg_count = 0;
+
+	for_each_available_child_of_node(thread->of_node, node) {
+		thread->vreg_count++;
+	}
+
+	thread->vreg = devm_kcalloc(thread->ctrl->dev, thread->vreg_count,
+			sizeof(*thread->vreg), GFP_KERNEL);
+	if (!thread->vreg)
+		return -ENOMEM;
+
+	i = 0;
+	for_each_available_child_of_node(thread->of_node, node) {
+		thread->vreg[i].of_node = node;
+		thread->vreg[i].thread = thread;
+
+		rc = of_property_read_string(node, "regulator-name",
+						&thread->vreg[i].name);
+		if (rc) {
+			dev_err(thread->ctrl->dev, "could not find regulator name, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		i++;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_allocate_threads() - allocate and initialize CPR3 threads for a given
+ *			     controller based upon device tree data
+ * @ctrl:		Pointer to the CPR3 controller
+ * @min_thread_id:	Minimum allowed hardware thread ID for this controller
+ * @max_thread_id:	Maximum allowed hardware thread ID for this controller
+ *
+ * This function allocates the ctrl->thread array based upon the number of
+ * device tree thread subnodes.  It also initializes generic elements of each
+ * thread struct such as thread_id, of_node, ctrl, and vreg array.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_allocate_threads(struct cpr3_controller *ctrl, u32 min_thread_id,
+			u32 max_thread_id)
+{
+	struct device *dev = ctrl->dev;
+	struct device_node *thread_node;
+	int i, j, rc;
+
+	ctrl->thread_count = 0;
+
+	for_each_available_child_of_node(dev->of_node, thread_node) {
+		ctrl->thread_count++;
+	}
+
+	ctrl->thread = devm_kcalloc(dev, ctrl->thread_count,
+			sizeof(*ctrl->thread), GFP_KERNEL);
+	if (!ctrl->thread)
+		return -ENOMEM;
+
+	i = 0;
+	for_each_available_child_of_node(dev->of_node, thread_node) {
+		ctrl->thread[i].of_node = thread_node;
+		ctrl->thread[i].ctrl = ctrl;
+
+		rc = of_property_read_u32(thread_node, "qcom,cpr-thread-id",
+					  &ctrl->thread[i].thread_id);
+		if (rc) {
+			dev_err(dev, "could not read DT property qcom,cpr-thread-id, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		if (ctrl->thread[i].thread_id < min_thread_id ||
+				ctrl->thread[i].thread_id > max_thread_id) {
+			dev_err(dev, "invalid thread id = %u; not within [%u, %u]\n",
+				ctrl->thread[i].thread_id, min_thread_id,
+				max_thread_id);
+			return -EINVAL;
+		}
+
+		/* Verify that the thread ID is unique for all child nodes. */
+		for (j = 0; j < i; j++) {
+			if (ctrl->thread[j].thread_id
+					== ctrl->thread[i].thread_id) {
+				dev_err(dev, "duplicate thread id = %u found\n",
+					ctrl->thread[i].thread_id);
+				return -EINVAL;
+			}
+		}
+
+		rc = cpr3_allocate_regulators(&ctrl->thread[i]);
+		if (rc)
+			return rc;
+
+		i++;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_map_fuse_base() - ioremap the base address of the fuse region
+ * @ctrl:	Pointer to the CPR3 controller
+ * @pdev:	Platform device pointer for the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_map_fuse_base(struct cpr3_controller *ctrl,
+			struct platform_device *pdev)
+{
+	struct resource *res;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "fuse_base");
+	if (!res || !res->start) {
+		dev_err(&pdev->dev, "fuse base address is missing\n");
+		return -ENXIO;
+	}
+
+	ctrl->fuse_base = devm_ioremap(&pdev->dev, res->start,
+						resource_size(res));
+
+	return 0;
+}
+
+/**
+ * cpr3_read_fuse_param() - reads a CPR3 fuse parameter out of eFuses
+ * @fuse_base_addr:	Virtual memory address of the eFuse base address
+ * @param:		Null terminated array of fuse param segments to read
+ *			from
+ * @param_value:	Output with value read from the eFuses
+ *
+ * This function reads from each of the parameter segments listed in the param
+ * array and concatenates their values together.  Reading stops when an element
+ * is reached which has all 0 struct values.  The total number of bits specified
+ * for the fuse parameter across all segments must be less than or equal to 64.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_read_fuse_param(void __iomem *fuse_base_addr,
+		const struct cpr3_fuse_param *param, u64 *param_value)
+{
+	u64 fuse_val, val;
+	int bits;
+	int bits_total = 0;
+
+	*param_value = 0;
+
+	while (param->row || param->bit_start || param->bit_end) {
+		if (param->bit_start > param->bit_end
+		    || param->bit_end > MAX_FUSE_ROW_BIT) {
+			pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n",
+				param->row, param->bit_start, param->bit_end);
+			return -EINVAL;
+		}
+
+		bits = param->bit_end - param->bit_start + 1;
+		if (bits_total + bits > 64) {
+			pr_err("Invalid fuse parameter segments; total bits = %d\n",
+				bits_total + bits);
+			return -EINVAL;
+		}
+
+		fuse_val = readq_relaxed(fuse_base_addr
+					 + param->row * BYTES_PER_FUSE_ROW);
+		val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1);
+		*param_value |= val << bits_total;
+		bits_total += bits;
+
+		param++;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_convert_open_loop_voltage_fuse() - converts an open loop voltage fuse
+ *		value into an absolute voltage with units of microvolts
+ * @ref_volt:		Reference voltage in microvolts
+ * @step_volt:		The step size in microvolts of the fuse LSB
+ * @fuse:		Open loop voltage fuse value
+ * @fuse_len:		The bit length of the fuse value
+ *
+ * The MSB of the fuse parameter corresponds to a sign bit.  If it is set, then
+ * the lower bits correspond to the number of steps to go down from the
+ * reference voltage.  If it is not set, then the lower bits correspond to the
+ * number of steps to go up from the reference voltage.
+ */
+int cpr3_convert_open_loop_voltage_fuse(int ref_volt, int step_volt, u32 fuse,
+					int fuse_len)
+{
+	int sign, steps;
+
+	sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1;
+	steps = fuse & ((1 << (fuse_len - 1)) - 1);
+
+	return ref_volt + sign * steps * step_volt;
+}
+
+/**
+ * cpr3_interpolate() - performs linear interpolation
+ * @x1		Lower known x value
+ * @y1		Lower known y value
+ * @x2		Upper known x value
+ * @y2		Upper known y value
+ * @x		Intermediate x value
+ *
+ * Returns y where (x, y) falls on the line between (x1, y1) and (x2, y2).
+ * It is required that x1 < x2, y1 <= y2, and x1 <= x <= x2.  If these
+ * conditions are not met, then y2 will be returned.
+ */
+u64 cpr3_interpolate(u64 x1, u64 y1, u64 x2, u64 y2, u64 x)
+{
+	u64 temp;
+
+	if (x1 >= x2 || y1 > y2 || x1 > x || x > x2)
+		return y2;
+
+	temp = (x2 - x) * (y2 - y1);
+	do_div(temp, (u32)(x2 - x1));
+
+	return y2 - temp;
+}
+
+/**
+ * cpr3_parse_array_property() - fill an array from a portion of the values
+ *		specified for a device tree property
+ * @vreg:		Pointer to the CPR3 regulator
+ * @prop_name:		The name of the device tree property to read from
+ * @tuple_size:		The number of elements in each tuple
+ * @out:		Output data array which must be of size tuple_size
+ *
+ * cpr3_parse_common_corner_data() must be called for vreg before this function
+ * is called so that fuse combo and speed bin size elements are initialized.
+ *
+ * Three formats are supported for the device tree property:
+ * 1. Length == tuple_size
+ *	(reading begins at index 0)
+ * 2. Length == tuple_size * vreg->fuse_combos_supported
+ *	(reading begins at index tuple_size * vreg->fuse_combo)
+ * 3. Length == tuple_size * vreg->speed_bins_supported
+ *	(reading begins at index tuple_size * vreg->speed_bin_fuse)
+ *
+ * All other property lengths are treated as errors.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_array_property(struct cpr3_regulator *vreg,
+		const char *prop_name, int tuple_size, u32 *out)
+{
+	struct device_node *node = vreg->of_node;
+	int len = 0;
+	int i, offset, rc;
+
+	if (!of_find_property(node, prop_name, &len)) {
+		cpr3_err(vreg, "property %s is missing\n", prop_name);
+		return -EINVAL;
+	}
+
+	if (len == tuple_size * sizeof(u32)) {
+		offset = 0;
+	} else if (len == tuple_size * vreg->fuse_combos_supported
+				     * sizeof(u32)) {
+		offset = tuple_size * vreg->fuse_combo;
+	} else if (vreg->speed_bins_supported > 0 &&
+		 len == tuple_size * vreg->speed_bins_supported * sizeof(u32)) {
+		offset = tuple_size * vreg->speed_bin_fuse;
+	} else {
+		if (vreg->speed_bins_supported > 0)
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n",
+				prop_name, len,
+				tuple_size * sizeof(u32),
+				tuple_size * vreg->speed_bins_supported
+					   * sizeof(u32),
+				tuple_size * vreg->fuse_combos_supported
+					   * sizeof(u32));
+		else
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n",
+				prop_name, len,
+				tuple_size * sizeof(u32),
+				tuple_size * vreg->fuse_combos_supported
+					   * sizeof(u32));
+		return -EINVAL;
+	}
+
+	for (i = 0; i < tuple_size; i++) {
+		rc = of_property_read_u32_index(node, prop_name, offset + i,
+						&out[i]);
+		if (rc) {
+			cpr3_err(vreg, "error reading property %s, rc=%d\n",
+				prop_name, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_parse_corner_array_property() - fill a per-corner array from a portion
+ *		of the values specified for a device tree property
+ * @vreg:		Pointer to the CPR3 regulator
+ * @prop_name:		The name of the device tree property to read from
+ * @tuple_size:		The number of elements in each per-corner tuple
+ * @out:		Output data array which must be of size:
+ *			tuple_size * vreg->corner_count
+ *
+ * cpr3_parse_common_corner_data() must be called for vreg before this function
+ * is called so that fuse combo and speed bin size elements are initialized.
+ *
+ * Three formats are supported for the device tree property:
+ * 1. Length == tuple_size * vreg->corner_count
+ *	(reading begins at index 0)
+ * 2. Length == tuple_size * vreg->fuse_combo_corner_sum
+ *	(reading begins at index tuple_size * vreg->fuse_combo_offset)
+ * 3. Length == tuple_size * vreg->speed_bin_corner_sum
+ *	(reading begins at index tuple_size * vreg->speed_bin_offset)
+ *
+ * All other property lengths are treated as errors.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_corner_array_property(struct cpr3_regulator *vreg,
+		const char *prop_name, int tuple_size, u32 *out)
+{
+	struct device_node *node = vreg->of_node;
+	int len = 0;
+	int i, offset, rc;
+
+	if (!of_find_property(node, prop_name, &len)) {
+		cpr3_err(vreg, "property %s is missing\n", prop_name);
+		return -EINVAL;
+	}
+
+	if (len == tuple_size * vreg->corner_count * sizeof(u32)) {
+		offset = 0;
+	} else if (len == tuple_size * vreg->fuse_combo_corner_sum
+				     * sizeof(u32)) {
+		offset = tuple_size * vreg->fuse_combo_offset;
+	} else if (vreg->speed_bin_corner_sum > 0 &&
+		 len == tuple_size * vreg->speed_bin_corner_sum * sizeof(u32)) {
+		offset = tuple_size * vreg->speed_bin_offset;
+	} else {
+		if (vreg->speed_bin_corner_sum > 0)
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n",
+				prop_name, len,
+				tuple_size * vreg->corner_count * sizeof(u32),
+				tuple_size * vreg->speed_bin_corner_sum
+					   * sizeof(u32),
+				tuple_size * vreg->fuse_combo_corner_sum
+					   * sizeof(u32));
+		else
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n",
+				prop_name, len,
+				tuple_size * vreg->corner_count * sizeof(u32),
+				tuple_size * vreg->fuse_combo_corner_sum
+					   * sizeof(u32));
+		return -EINVAL;
+	}
+
+	for (i = 0; i < tuple_size * vreg->corner_count; i++) {
+		rc = of_property_read_u32_index(node, prop_name, offset + i,
+						&out[i]);
+		if (rc) {
+			cpr3_err(vreg, "error reading property %s, rc=%d\n",
+				prop_name, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_parse_corner_band_array_property() - fill a per-corner band array
+ *		from a portion of the values specified for a device tree
+ *		property
+ * @vreg:		Pointer to the CPR3 regulator
+ * @prop_name:		The name of the device tree property to read from
+ * @tuple_size:		The number of elements in each per-corner band tuple
+ * @out:		Output data array which must be of size:
+ *			tuple_size * vreg->corner_band_count
+ *
+ * cpr3_parse_common_corner_data() must be called for vreg before this function
+ * is called so that fuse combo and speed bin size elements are initialized.
+ * In addition, corner band fuse combo and speed bin sum and offset elements
+ * must be initialized prior to executing this function.
+ *
+ * Three formats are supported for the device tree property:
+ * 1. Length == tuple_size * vreg->corner_band_count
+ *	(reading begins at index 0)
+ * 2. Length == tuple_size * vreg->fuse_combo_corner_band_sum
+ *	(reading begins at index tuple_size *
+ *		vreg->fuse_combo_corner_band_offset)
+ * 3. Length == tuple_size * vreg->speed_bin_corner_band_sum
+ *	(reading begins at index tuple_size *
+ *		vreg->speed_bin_corner_band_offset)
+ *
+ * All other property lengths are treated as errors.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_corner_band_array_property(struct cpr3_regulator *vreg,
+		const char *prop_name, int tuple_size, u32 *out)
+{
+	struct device_node *node = vreg->of_node;
+	int len = 0;
+	int i, offset, rc;
+
+	if (!of_find_property(node, prop_name, &len)) {
+		cpr3_err(vreg, "property %s is missing\n", prop_name);
+		return -EINVAL;
+	}
+
+	if (len == tuple_size * vreg->corner_band_count * sizeof(u32)) {
+		offset = 0;
+	} else if (len == tuple_size * vreg->fuse_combo_corner_band_sum
+				     * sizeof(u32)) {
+		offset = tuple_size * vreg->fuse_combo_corner_band_offset;
+	} else if (vreg->speed_bin_corner_band_sum > 0 &&
+		 len == tuple_size * vreg->speed_bin_corner_band_sum *
+		   sizeof(u32)) {
+		offset = tuple_size * vreg->speed_bin_corner_band_offset;
+	} else {
+		if (vreg->speed_bin_corner_band_sum > 0)
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu, %zu, or %zu\n",
+				prop_name, len,
+				tuple_size * vreg->corner_band_count *
+				 sizeof(u32),
+				tuple_size * vreg->speed_bin_corner_band_sum
+					   * sizeof(u32),
+				tuple_size * vreg->fuse_combo_corner_band_sum
+					   * sizeof(u32));
+		else
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n",
+				prop_name, len,
+				tuple_size * vreg->corner_band_count *
+				 sizeof(u32),
+				tuple_size * vreg->fuse_combo_corner_band_sum
+					   * sizeof(u32));
+		return -EINVAL;
+	}
+
+	for (i = 0; i < tuple_size * vreg->corner_band_count; i++) {
+		rc = of_property_read_u32_index(node, prop_name, offset + i,
+						&out[i]);
+		if (rc) {
+			cpr3_err(vreg, "error reading property %s, rc=%d\n",
+				prop_name, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_parse_common_corner_data() - parse common CPR3 properties relating to
+ *		the corners supported by a CPR3 regulator from device tree
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function reads, validates, and utilizes the following device tree
+ * properties: qcom,cpr-fuse-corners, qcom,cpr-fuse-combos, qcom,cpr-speed-bins,
+ * qcom,cpr-speed-bin-corners, qcom,cpr-corners, qcom,cpr-voltage-ceiling,
+ * qcom,cpr-voltage-floor, qcom,corner-frequencies,
+ * and qcom,cpr-corner-fmax-map.
+ *
+ * It initializes these CPR3 regulator elements: corner, corner_count,
+ * fuse_combos_supported, fuse_corner_map, and speed_bins_supported.  It
+ * initializes these elements for each corner: ceiling_volt, floor_volt,
+ * proc_freq, and cpr_fuse_corner.
+ *
+ * It requires that the following CPR3 regulator elements be initialized before
+ * being called: fuse_corner_count, fuse_combo, and speed_bin_fuse.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_common_corner_data(struct cpr3_regulator *vreg)
+{
+	struct device_node *node = vreg->of_node;
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	u32 max_fuse_combos, fuse_corners, aging_allowed = 0;
+	u32 max_speed_bins = 0;
+	u32 *combo_corners;
+	u32 *speed_bin_corners;
+	u32 *temp;
+	int i, j, rc;
+
+	rc = of_property_read_u32(node, "qcom,cpr-fuse-corners", &fuse_corners);
+	if (rc) {
+		cpr3_err(vreg, "error reading property qcom,cpr-fuse-corners, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (vreg->fuse_corner_count != fuse_corners) {
+		cpr3_err(vreg, "device tree config supports %d fuse corners but the hardware has %d fuse corners\n",
+			fuse_corners, vreg->fuse_corner_count);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(node, "qcom,cpr-fuse-combos",
+				&max_fuse_combos);
+	if (rc) {
+		cpr3_err(vreg, "error reading property qcom,cpr-fuse-combos, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	/*
+	 * Sanity check against arbitrarily large value to avoid excessive
+	 * memory allocation.
+	 */
+	if (max_fuse_combos > 100 || max_fuse_combos == 0) {
+		cpr3_err(vreg, "qcom,cpr-fuse-combos is invalid: %u\n",
+			max_fuse_combos);
+		return -EINVAL;
+	}
+
+	if (vreg->fuse_combo >= max_fuse_combos) {
+		cpr3_err(vreg, "device tree config supports fuse combos 0-%u but the hardware has combo %d\n",
+			max_fuse_combos - 1, vreg->fuse_combo);
+		BUG_ON(1);
+		return -EINVAL;
+	}
+
+	vreg->fuse_combos_supported = max_fuse_combos;
+
+	of_property_read_u32(node, "qcom,cpr-speed-bins", &max_speed_bins);
+
+	/*
+	 * Sanity check against arbitrarily large value to avoid excessive
+	 * memory allocation.
+	 */
+	if (max_speed_bins > 100) {
+		cpr3_err(vreg, "qcom,cpr-speed-bins is invalid: %u\n",
+			max_speed_bins);
+		return -EINVAL;
+	}
+
+	if (max_speed_bins && vreg->speed_bin_fuse >= max_speed_bins) {
+		cpr3_err(vreg, "device tree config supports speed bins 0-%u but the hardware has speed bin %d\n",
+			max_speed_bins - 1, vreg->speed_bin_fuse);
+		BUG();
+		return -EINVAL;
+	}
+
+	vreg->speed_bins_supported = max_speed_bins;
+
+	combo_corners = kcalloc(vreg->fuse_combos_supported,
+				sizeof(*combo_corners), GFP_KERNEL);
+	if (!combo_corners)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(node, "qcom,cpr-corners", combo_corners,
+					vreg->fuse_combos_supported);
+	if (rc == -EOVERFLOW) {
+		/* Single value case */
+		rc = of_property_read_u32(node, "qcom,cpr-corners",
+					combo_corners);
+		for (i = 1; i < vreg->fuse_combos_supported; i++)
+			combo_corners[i] = combo_corners[0];
+	}
+	if (rc) {
+		cpr3_err(vreg, "error reading property qcom,cpr-corners, rc=%d\n",
+			rc);
+		kfree(combo_corners);
+		return rc;
+	}
+
+	vreg->fuse_combo_offset = 0;
+	vreg->fuse_combo_corner_sum = 0;
+	for (i = 0; i < vreg->fuse_combos_supported; i++) {
+		vreg->fuse_combo_corner_sum += combo_corners[i];
+		if (i < vreg->fuse_combo)
+			vreg->fuse_combo_offset += combo_corners[i];
+	}
+
+	vreg->corner_count = combo_corners[vreg->fuse_combo];
+
+	kfree(combo_corners);
+
+	vreg->speed_bin_offset = 0;
+	vreg->speed_bin_corner_sum = 0;
+	if (vreg->speed_bins_supported > 0) {
+		speed_bin_corners = kcalloc(vreg->speed_bins_supported,
+					sizeof(*speed_bin_corners), GFP_KERNEL);
+		if (!speed_bin_corners)
+			return -ENOMEM;
+
+		rc = of_property_read_u32_array(node,
+				"qcom,cpr-speed-bin-corners", speed_bin_corners,
+				vreg->speed_bins_supported);
+		if (rc) {
+			cpr3_err(vreg, "error reading property qcom,cpr-speed-bin-corners, rc=%d\n",
+				rc);
+			kfree(speed_bin_corners);
+			return rc;
+		}
+
+		for (i = 0; i < vreg->speed_bins_supported; i++) {
+			vreg->speed_bin_corner_sum += speed_bin_corners[i];
+			if (i < vreg->speed_bin_fuse)
+				vreg->speed_bin_offset += speed_bin_corners[i];
+		}
+
+		if (speed_bin_corners[vreg->speed_bin_fuse]
+		    != vreg->corner_count) {
+			cpr3_err(vreg, "qcom,cpr-corners and qcom,cpr-speed-bin-corners conflict on number of corners: %d vs %u\n",
+				vreg->corner_count,
+				speed_bin_corners[vreg->speed_bin_fuse]);
+			kfree(speed_bin_corners);
+			return -EINVAL;
+		}
+
+		kfree(speed_bin_corners);
+	}
+
+	/*
+	 * For CPRh compliant controllers two additional corners are
+	 * allocated to correspond to the APM crossover voltage and the MEM ACC
+	 * crossover voltage.
+	 */
+	vreg->corner = devm_kcalloc(ctrl->dev, ctrl->ctrl_type ==
+				    CPR_CTRL_TYPE_CPRH ?
+				    vreg->corner_count + 2 :
+				    vreg->corner_count,
+				    sizeof(*vreg->corner), GFP_KERNEL);
+	temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL);
+	if (!vreg->corner || !temp)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-ceiling",
+			1, temp);
+	if (rc)
+		goto free_temp;
+	for (i = 0; i < vreg->corner_count; i++) {
+		vreg->corner[i].ceiling_volt
+			= CPR3_ROUND(temp[i], ctrl->step_volt);
+		vreg->corner[i].abs_ceiling_volt = vreg->corner[i].ceiling_volt;
+	}
+
+	rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-voltage-floor",
+			1, temp);
+	if (rc)
+		goto free_temp;
+	for (i = 0; i < vreg->corner_count; i++)
+		vreg->corner[i].floor_volt
+			= CPR3_ROUND(temp[i], ctrl->step_volt);
+
+	/* Validate ceiling and floor values */
+	for (i = 0; i < vreg->corner_count; i++) {
+		if (vreg->corner[i].floor_volt
+		    > vreg->corner[i].ceiling_volt) {
+			cpr3_err(vreg, "CPR floor[%d]=%d > ceiling[%d]=%d uV\n",
+				i, vreg->corner[i].floor_volt,
+				i, vreg->corner[i].ceiling_volt);
+			rc = -EINVAL;
+			goto free_temp;
+		}
+	}
+
+	/* Load optional system-supply voltages */
+	if (of_find_property(vreg->of_node, "qcom,system-voltage", NULL)) {
+		rc = cpr3_parse_corner_array_property(vreg,
+			"qcom,system-voltage", 1, temp);
+		if (rc)
+			goto free_temp;
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].system_volt = temp[i];
+	}
+
+	rc = cpr3_parse_corner_array_property(vreg, "qcom,corner-frequencies",
+			1, temp);
+	if (rc)
+		goto free_temp;
+	for (i = 0; i < vreg->corner_count; i++)
+		vreg->corner[i].proc_freq = temp[i];
+
+	/* Validate frequencies */
+	for (i = 1; i < vreg->corner_count; i++) {
+		if (vreg->corner[i].proc_freq
+		    < vreg->corner[i - 1].proc_freq) {
+			cpr3_err(vreg, "invalid frequency: freq[%d]=%u < freq[%d]=%u\n",
+				i, vreg->corner[i].proc_freq, i - 1,
+				vreg->corner[i - 1].proc_freq);
+			rc = -EINVAL;
+			goto free_temp;
+		}
+	}
+
+	vreg->fuse_corner_map = devm_kcalloc(ctrl->dev, vreg->fuse_corner_count,
+				    sizeof(*vreg->fuse_corner_map), GFP_KERNEL);
+	if (!vreg->fuse_corner_map) {
+		rc = -ENOMEM;
+		goto free_temp;
+	}
+
+	rc = cpr3_parse_array_property(vreg, "qcom,cpr-corner-fmax-map",
+		vreg->fuse_corner_count, temp);
+	if (rc)
+		goto free_temp;
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		vreg->fuse_corner_map[i] = temp[i] - CPR3_CORNER_OFFSET;
+		if (temp[i] < CPR3_CORNER_OFFSET
+		    || temp[i] > vreg->corner_count + CPR3_CORNER_OFFSET) {
+			cpr3_err(vreg, "invalid corner value specified in qcom,cpr-corner-fmax-map: %u\n",
+				temp[i]);
+			rc = -EINVAL;
+			goto free_temp;
+		} else if (i > 0 && temp[i - 1] >= temp[i]) {
+			cpr3_err(vreg, "invalid corner %u less than or equal to previous corner %u\n",
+				temp[i], temp[i - 1]);
+			rc = -EINVAL;
+			goto free_temp;
+		}
+	}
+	if (temp[vreg->fuse_corner_count - 1] != vreg->corner_count)
+		cpr3_debug(vreg, "Note: highest Fmax corner %u in qcom,cpr-corner-fmax-map does not match highest supported corner %d\n",
+			temp[vreg->fuse_corner_count - 1],
+			vreg->corner_count);
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		for (j = 0; j < vreg->fuse_corner_count; j++) {
+			if (i + CPR3_CORNER_OFFSET <= temp[j]) {
+				vreg->corner[i].cpr_fuse_corner = j;
+				break;
+			}
+		}
+		if (j == vreg->fuse_corner_count) {
+			/*
+			 * Handle the case where the highest fuse corner maps
+			 * to a corner below the highest corner.
+			 */
+			vreg->corner[i].cpr_fuse_corner
+				= vreg->fuse_corner_count - 1;
+		}
+	}
+
+	if (of_find_property(vreg->of_node,
+				"qcom,allow-aging-voltage-adjustment", NULL)) {
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,allow-aging-voltage-adjustment",
+			1, &aging_allowed);
+		if (rc)
+			goto free_temp;
+
+		vreg->aging_allowed = aging_allowed;
+	}
+
+	if (of_find_property(vreg->of_node,
+		       "qcom,allow-aging-open-loop-voltage-adjustment", NULL)) {
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,allow-aging-open-loop-voltage-adjustment",
+			1, &aging_allowed);
+		if (rc)
+			goto free_temp;
+
+		vreg->aging_allow_open_loop_adj = aging_allowed;
+	}
+
+	if (vreg->aging_allowed) {
+		if (ctrl->aging_ref_volt <= 0) {
+			cpr3_err(ctrl, "qcom,cpr-aging-ref-voltage must be specified\n");
+			rc = -EINVAL;
+			goto free_temp;
+		}
+
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,cpr-aging-max-voltage-adjustment",
+			1, &vreg->aging_max_adjust_volt);
+		if (rc)
+			goto free_temp;
+
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,cpr-aging-ref-corner", 1, &vreg->aging_corner);
+		if (rc) {
+			goto free_temp;
+		} else if (vreg->aging_corner < CPR3_CORNER_OFFSET
+			   || vreg->aging_corner > vreg->corner_count - 1
+							+ CPR3_CORNER_OFFSET) {
+			cpr3_err(vreg, "aging reference corner=%d not in range [%d, %d]\n",
+				vreg->aging_corner, CPR3_CORNER_OFFSET,
+				vreg->corner_count - 1 + CPR3_CORNER_OFFSET);
+			rc = -EINVAL;
+			goto free_temp;
+		}
+		vreg->aging_corner -= CPR3_CORNER_OFFSET;
+
+		if (of_find_property(vreg->of_node, "qcom,cpr-aging-derate",
+					NULL)) {
+			rc = cpr3_parse_corner_array_property(vreg,
+				"qcom,cpr-aging-derate", 1, temp);
+			if (rc)
+				goto free_temp;
+
+			for (i = 0; i < vreg->corner_count; i++)
+				vreg->corner[i].aging_derate = temp[i];
+		} else {
+			for (i = 0; i < vreg->corner_count; i++)
+				vreg->corner[i].aging_derate
+					= CPR3_AGING_DERATE_UNITY;
+		}
+	}
+
+free_temp:
+	kfree(temp);
+	return rc;
+}
+
+/**
+ * cpr3_parse_thread_u32() - parse the specified property from the CPR3 thread's
+ *		device tree node and verify that it is within the allowed limits
+ * @thread:		Pointer to the CPR3 thread
+ * @propname:		The name of the device tree property to read
+ * @out_value:		The output pointer to fill with the value read
+ * @value_min:		The minimum allowed property value
+ * @value_max:		The maximum allowed property value
+ *
+ * This function prints a verbose error message if the property is missing or
+ * has a value which is not within the specified range.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_thread_u32(struct cpr3_thread *thread, const char *propname,
+		       u32 *out_value, u32 value_min, u32 value_max)
+{
+	int rc;
+
+	rc = of_property_read_u32(thread->of_node, propname, out_value);
+	if (rc) {
+		cpr3_err(thread->ctrl, "thread %u error reading property %s, rc=%d\n",
+			thread->thread_id, propname, rc);
+		return rc;
+	}
+
+	if (*out_value < value_min || *out_value > value_max) {
+		cpr3_err(thread->ctrl, "thread %u %s=%u is invalid; allowed range: [%u, %u]\n",
+			thread->thread_id, propname, *out_value, value_min,
+			value_max);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_parse_ctrl_u32() - parse the specified property from the CPR3
+ *		controller's device tree node and verify that it is within the
+ *		allowed limits
+ * @ctrl:		Pointer to the CPR3 controller
+ * @propname:		The name of the device tree property to read
+ * @out_value:		The output pointer to fill with the value read
+ * @value_min:		The minimum allowed property value
+ * @value_max:		The maximum allowed property value
+ *
+ * This function prints a verbose error message if the property is missing or
+ * has a value which is not within the specified range.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_ctrl_u32(struct cpr3_controller *ctrl, const char *propname,
+		       u32 *out_value, u32 value_min, u32 value_max)
+{
+	int rc;
+
+	rc = of_property_read_u32(ctrl->dev->of_node, propname, out_value);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property %s, rc=%d\n",
+			propname, rc);
+		return rc;
+	}
+
+	if (*out_value < value_min || *out_value > value_max) {
+		cpr3_err(ctrl, "%s=%u is invalid; allowed range: [%u, %u]\n",
+			propname, *out_value, value_min, value_max);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_parse_common_thread_data() - parse common CPR3 thread properties from
+ *		device tree
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_common_thread_data(struct cpr3_thread *thread)
+{
+	int rc;
+
+	rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-up",
+			&thread->consecutive_up, CPR3_CONSECUTIVE_UP_DOWN_MIN,
+			CPR3_CONSECUTIVE_UP_DOWN_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_thread_u32(thread, "qcom,cpr-consecutive-down",
+			&thread->consecutive_down, CPR3_CONSECUTIVE_UP_DOWN_MIN,
+			CPR3_CONSECUTIVE_UP_DOWN_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_thread_u32(thread, "qcom,cpr-up-threshold",
+			&thread->up_threshold, CPR3_UP_DOWN_THRESHOLD_MIN,
+			CPR3_UP_DOWN_THRESHOLD_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_thread_u32(thread, "qcom,cpr-down-threshold",
+			&thread->down_threshold, CPR3_UP_DOWN_THRESHOLD_MIN,
+			CPR3_UP_DOWN_THRESHOLD_MAX);
+	if (rc)
+		return rc;
+
+	return rc;
+}
+
+/**
+ * cpr3_parse_irq_affinity() - parse CPR IRQ affinity information
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_parse_irq_affinity(struct cpr3_controller *ctrl)
+{
+	struct device_node *cpu_node;
+	int i, cpu;
+	int len = 0;
+
+	if (!of_find_property(ctrl->dev->of_node, "qcom,cpr-interrupt-affinity",
+				&len)) {
+		/* No IRQ affinity required */
+		return 0;
+	}
+
+	len /= sizeof(u32);
+
+	for (i = 0; i < len; i++) {
+		cpu_node = of_parse_phandle(ctrl->dev->of_node,
+					    "qcom,cpr-interrupt-affinity", i);
+		if (!cpu_node) {
+			cpr3_err(ctrl, "could not find CPU node %d\n", i);
+			return -EINVAL;
+		}
+
+		for_each_possible_cpu(cpu) {
+			if (of_get_cpu_node(cpu, NULL) == cpu_node) {
+				cpumask_set_cpu(cpu, &ctrl->irq_affinity_mask);
+				break;
+			}
+		}
+		of_node_put(cpu_node);
+	}
+
+	return 0;
+}
+
+static int cpr3_panic_notifier_init(struct cpr3_controller *ctrl)
+{
+	struct device_node *node = ctrl->dev->of_node;
+	struct cpr3_panic_regs_info *panic_regs_info;
+	struct cpr3_reg_info *regs;
+	int i, reg_count, len, rc = 0;
+
+	if (!of_find_property(node, "qcom,cpr-panic-reg-addr-list", &len)) {
+		/* panic register address list not specified */
+		return rc;
+	}
+
+	reg_count = len / sizeof(u32);
+	if (!reg_count) {
+		cpr3_err(ctrl, "qcom,cpr-panic-reg-addr-list has invalid len = %d\n",
+			len);
+		return -EINVAL;
+	}
+
+	if (!of_find_property(node, "qcom,cpr-panic-reg-name-list", NULL)) {
+		cpr3_err(ctrl, "property qcom,cpr-panic-reg-name-list not specified\n");
+		return -EINVAL;
+	}
+
+	len = of_property_count_strings(node, "qcom,cpr-panic-reg-name-list");
+	if (reg_count != len) {
+		cpr3_err(ctrl, "qcom,cpr-panic-reg-name-list should have %d strings\n",
+			reg_count);
+		return -EINVAL;
+	}
+
+	panic_regs_info = devm_kzalloc(ctrl->dev, sizeof(*panic_regs_info),
+					GFP_KERNEL);
+	if (!panic_regs_info)
+		return -ENOMEM;
+
+	regs = devm_kcalloc(ctrl->dev, reg_count, sizeof(*regs), GFP_KERNEL);
+	if (!regs)
+		return -ENOMEM;
+
+	for (i = 0; i < reg_count; i++) {
+		rc = of_property_read_string_index(node,
+				"qcom,cpr-panic-reg-name-list", i,
+				&(regs[i].name));
+		if (rc) {
+			cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-name-list, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		rc = of_property_read_u32_index(node,
+				"qcom,cpr-panic-reg-addr-list", i,
+				&(regs[i].addr));
+		if (rc) {
+			cpr3_err(ctrl, "error reading property qcom,cpr-panic-reg-addr-list, rc=%d\n",
+				rc);
+			return rc;
+		}
+		regs[i].virt_addr = devm_ioremap(ctrl->dev, regs[i].addr, 0x4);
+		if (!regs[i].virt_addr) {
+			pr_err("Unable to map panic register addr 0x%08x\n",
+				regs[i].addr);
+			return -EINVAL;
+		}
+		regs[i].value = 0xFFFFFFFF;
+	}
+
+	panic_regs_info->reg_count = reg_count;
+	panic_regs_info->regs = regs;
+	ctrl->panic_regs_info = panic_regs_info;
+
+	return rc;
+}
+
+/**
+ * cpr3_parse_common_ctrl_data() - parse common CPR3 controller properties from
+ *		device tree
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_common_ctrl_data(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-sensor-time",
+			&ctrl->sensor_time, 0, UINT_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-loop-time",
+			&ctrl->loop_time, 0, UINT_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-idle-cycles",
+			&ctrl->idle_clocks, CPR3_IDLE_CLOCKS_MIN,
+			CPR3_IDLE_CLOCKS_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-min",
+			&ctrl->step_quot_init_min, CPR3_STEP_QUOT_MIN,
+			CPR3_STEP_QUOT_MAX);
+	if (rc)
+		return rc;
+
+	rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-step-quot-init-max",
+			&ctrl->step_quot_init_max, CPR3_STEP_QUOT_MIN,
+			CPR3_STEP_QUOT_MAX);
+	if (rc)
+		return rc;
+
+	rc = of_property_read_u32(ctrl->dev->of_node, "qcom,voltage-step",
+				&ctrl->step_volt);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,voltage-step, rc=%d\n",
+			rc);
+		return rc;
+	}
+	if (ctrl->step_volt <= 0) {
+		cpr3_err(ctrl, "qcom,voltage-step=%d is invalid\n",
+			ctrl->step_volt);
+		return -EINVAL;
+	}
+
+	rc = cpr3_parse_ctrl_u32(ctrl, "qcom,cpr-count-mode",
+			&ctrl->count_mode, CPR3_COUNT_MODE_ALL_AT_ONCE_MIN,
+			CPR3_COUNT_MODE_STAGGERED);
+	if (rc)
+		return rc;
+
+	/* Count repeat is optional */
+	ctrl->count_repeat = 0;
+	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-count-repeat",
+			&ctrl->count_repeat);
+
+	ctrl->cpr_allowed_sw = of_property_read_bool(ctrl->dev->of_node,
+			"qcom,cpr-enable");
+
+	rc = cpr3_parse_irq_affinity(ctrl);
+	if (rc)
+		return rc;
+
+	/* Aging reference voltage is optional */
+	ctrl->aging_ref_volt = 0;
+	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-aging-ref-voltage",
+			&ctrl->aging_ref_volt);
+
+	/* Aging possible bitmask is optional */
+	ctrl->aging_possible_mask = 0;
+	of_property_read_u32(ctrl->dev->of_node,
+			"qcom,cpr-aging-allowed-reg-mask",
+			&ctrl->aging_possible_mask);
+
+	if (ctrl->aging_possible_mask) {
+		/*
+		 * Aging possible register value required if bitmask is
+		 * specified
+		 */
+		rc = cpr3_parse_ctrl_u32(ctrl,
+				"qcom,cpr-aging-allowed-reg-value",
+				&ctrl->aging_possible_val, 0, UINT_MAX);
+		if (rc)
+			return rc;
+	}
+
+	if (of_find_property(ctrl->dev->of_node, "clock-names", NULL)) {
+		ctrl->core_clk = devm_clk_get(ctrl->dev, "core_clk");
+		if (IS_ERR(ctrl->core_clk)) {
+			rc = PTR_ERR(ctrl->core_clk);
+			if (rc != -EPROBE_DEFER)
+				cpr3_err(ctrl, "unable request core clock, rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	rc = cpr3_panic_notifier_init(ctrl);
+	if (rc)
+		return rc;
+
+	if (of_find_property(ctrl->dev->of_node, "vdd-supply", NULL)) {
+		ctrl->vdd_regulator = devm_regulator_get(ctrl->dev, "vdd");
+		if (IS_ERR(ctrl->vdd_regulator)) {
+			rc = PTR_ERR(ctrl->vdd_regulator);
+			if (rc != -EPROBE_DEFER)
+				cpr3_err(ctrl, "unable to request vdd regulator, rc=%d\n",
+					 rc);
+			return rc;
+		}
+	} else if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH) {
+		/* vdd-supply is optional for CPRh controllers. */
+		ctrl->vdd_regulator = NULL;
+	} else {
+		cpr3_err(ctrl, "vdd supply is not defined\n");
+		return -ENODEV;
+	}
+
+	/*
+	 * Regulator device handles are not necessary for CPRh controllers
+	 * since communication with the regulators is completely managed
+	 * in hardware.
+	 */
+	if (ctrl->ctrl_type == CPR_CTRL_TYPE_CPRH)
+		return rc;
+
+	ctrl->system_regulator = devm_regulator_get_optional(ctrl->dev,
+								"system");
+	if (IS_ERR(ctrl->system_regulator)) {
+		rc = PTR_ERR(ctrl->system_regulator);
+		if (rc != -EPROBE_DEFER) {
+			rc = 0;
+			ctrl->system_regulator = NULL;
+		} else {
+			return rc;
+		}
+	}
+
+	ctrl->mem_acc_regulator = devm_regulator_get_optional(ctrl->dev,
+							      "mem-acc");
+	if (IS_ERR(ctrl->mem_acc_regulator)) {
+		rc = PTR_ERR(ctrl->mem_acc_regulator);
+		if (rc != -EPROBE_DEFER) {
+			rc = 0;
+			ctrl->mem_acc_regulator = NULL;
+		} else {
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_limit_open_loop_voltages() - modify the open-loop voltage of each corner
+ *				so that it fits within the floor to ceiling
+ *				voltage range of the corner
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function clips the open-loop voltage for each corner so that it is
+ * limited to the floor to ceiling range.  It also rounds each open-loop voltage
+ * so that it corresponds to a set point available to the underlying regulator.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_limit_open_loop_voltages(struct cpr3_regulator *vreg)
+{
+	int i, volt;
+
+	cpr3_debug(vreg, "open-loop voltages after trimming and rounding:\n");
+	for (i = 0; i < vreg->corner_count; i++) {
+		volt = CPR3_ROUND(vreg->corner[i].open_loop_volt,
+					vreg->thread->ctrl->step_volt);
+		if (volt < vreg->corner[i].floor_volt)
+			volt = vreg->corner[i].floor_volt;
+		else if (volt > vreg->corner[i].ceiling_volt)
+			volt = vreg->corner[i].ceiling_volt;
+		vreg->corner[i].open_loop_volt = volt;
+		cpr3_debug(vreg, "corner[%2d]: open-loop=%d uV\n", i, volt);
+	}
+
+	return 0;
+}
+
+/**
+ * cpr3_open_loop_voltage_as_ceiling() - configures the ceiling voltage for each
+ *		corner to equal the open-loop voltage if the relevant device
+ *		tree property is found for the CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function assumes that the the open-loop voltage for each corner has
+ * already been rounded to the nearest allowed set point and that it falls
+ * within the floor to ceiling range.
+ *
+ * Return: none
+ */
+void cpr3_open_loop_voltage_as_ceiling(struct cpr3_regulator *vreg)
+{
+	int i;
+
+	if (!of_property_read_bool(vreg->of_node,
+				"qcom,cpr-scaled-open-loop-voltage-as-ceiling"))
+		return;
+
+	for (i = 0; i < vreg->corner_count; i++)
+		vreg->corner[i].ceiling_volt
+			= vreg->corner[i].open_loop_volt;
+}
+
+/**
+ * cpr3_limit_floor_voltages() - raise the floor voltage of each corner so that
+ *		the optional maximum floor to ceiling voltage range specified in
+ *		device tree is satisfied
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function also ensures that the open-loop voltage for each corner falls
+ * within the final floor to ceiling voltage range and that floor voltages
+ * increase monotonically.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_limit_floor_voltages(struct cpr3_regulator *vreg)
+{
+	char *prop = "qcom,cpr-floor-to-ceiling-max-range";
+	int i, floor_new;
+	u32 *floor_range;
+	int rc = 0;
+
+	if (!of_find_property(vreg->of_node, prop, NULL))
+		goto enforce_monotonicity;
+
+	floor_range = kcalloc(vreg->corner_count, sizeof(*floor_range),
+				GFP_KERNEL);
+	if (!floor_range)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, prop, 1, floor_range);
+	if (rc)
+		goto free_floor_adjust;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		if ((s32)floor_range[i] >= 0) {
+			floor_new = CPR3_ROUND(vreg->corner[i].ceiling_volt
+							- floor_range[i],
+						vreg->thread->ctrl->step_volt);
+
+			vreg->corner[i].floor_volt = max(floor_new,
+						vreg->corner[i].floor_volt);
+			if (vreg->corner[i].open_loop_volt
+			    < vreg->corner[i].floor_volt)
+				vreg->corner[i].open_loop_volt
+					= vreg->corner[i].floor_volt;
+		}
+	}
+
+free_floor_adjust:
+	kfree(floor_range);
+
+enforce_monotonicity:
+	/* Ensure that floor voltages increase monotonically. */
+	for (i = 1; i < vreg->corner_count; i++) {
+		if (vreg->corner[i].floor_volt
+		    < vreg->corner[i - 1].floor_volt) {
+			cpr3_debug(vreg, "corner %d floor voltage=%d uV < corner %d voltage=%d uV; overriding: corner %d voltage=%d\n",
+				i, vreg->corner[i].floor_volt,
+				i - 1, vreg->corner[i - 1].floor_volt,
+				i, vreg->corner[i - 1].floor_volt);
+			vreg->corner[i].floor_volt
+				= vreg->corner[i - 1].floor_volt;
+
+			if (vreg->corner[i].open_loop_volt
+			    < vreg->corner[i].floor_volt)
+				vreg->corner[i].open_loop_volt
+					= vreg->corner[i].floor_volt;
+			if (vreg->corner[i].ceiling_volt
+			    < vreg->corner[i].floor_volt)
+				vreg->corner[i].ceiling_volt
+					= vreg->corner[i].floor_volt;
+		}
+	}
+
+	return rc;
+}
+
+/**
+ * cpr3_print_quots() - print CPR target quotients into the kernel log for
+ *		debugging purposes
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: none
+ */
+void cpr3_print_quots(struct cpr3_regulator *vreg)
+{
+	int i, j, pos;
+	size_t buflen;
+	char *buf;
+
+	buflen = sizeof(*buf) * CPR3_RO_COUNT * (MAX_CHARS_PER_INT + 2);
+	buf = kzalloc(buflen, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		for (j = 0, pos = 0; j < CPR3_RO_COUNT; j++)
+			pos += scnprintf(buf + pos, buflen - pos, " %u",
+				vreg->corner[i].target_quot[j]);
+		cpr3_debug(vreg, "target quots[%2d]:%s\n", i, buf);
+	}
+
+	kfree(buf);
+}
+
+/**
+ * cpr3_adjust_fused_open_loop_voltages() - adjust the fused open-loop voltages
+ *		for each fuse corner according to device tree values
+ * @vreg:		Pointer to the CPR3 regulator
+ * @fuse_volt:		Pointer to an array of the fused open-loop voltage
+ *			values
+ *
+ * Voltage values in fuse_volt are modified in place.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_adjust_fused_open_loop_voltages(struct cpr3_regulator *vreg,
+		int *fuse_volt)
+{
+	int i, rc, prev_volt;
+	int *volt_adjust;
+
+	if (!of_find_property(vreg->of_node,
+			"qcom,cpr-open-loop-voltage-fuse-adjustment", NULL)) {
+		/* No adjustment required. */
+		return 0;
+	}
+
+	volt_adjust = kcalloc(vreg->fuse_corner_count, sizeof(*volt_adjust),
+				GFP_KERNEL);
+	if (!volt_adjust)
+		return -ENOMEM;
+
+	rc = cpr3_parse_array_property(vreg,
+		"qcom,cpr-open-loop-voltage-fuse-adjustment",
+		vreg->fuse_corner_count, volt_adjust);
+	if (rc) {
+		cpr3_err(vreg, "could not load open-loop fused voltage adjustments, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		if (volt_adjust[i]) {
+			prev_volt = fuse_volt[i];
+			fuse_volt[i] += volt_adjust[i];
+			cpr3_debug(vreg, "adjusted fuse corner %d open-loop voltage: %d --> %d uV\n",
+				i, prev_volt, fuse_volt[i]);
+		}
+	}
+
+done:
+	kfree(volt_adjust);
+	return rc;
+}
+
+/**
+ * cpr3_adjust_open_loop_voltages() - adjust the open-loop voltages for each
+ *		corner according to device tree values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_adjust_open_loop_voltages(struct cpr3_regulator *vreg)
+{
+	int i, rc, prev_volt, min_volt;
+	int *volt_adjust, *volt_diff;
+
+	if (!of_find_property(vreg->of_node,
+			"qcom,cpr-open-loop-voltage-adjustment", NULL)) {
+		/* No adjustment required. */
+		return 0;
+	}
+
+	volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust),
+				GFP_KERNEL);
+	volt_diff = kcalloc(vreg->corner_count, sizeof(*volt_diff), GFP_KERNEL);
+	if (!volt_adjust || !volt_diff) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = cpr3_parse_corner_array_property(vreg,
+		"qcom,cpr-open-loop-voltage-adjustment", 1, volt_adjust);
+	if (rc) {
+		cpr3_err(vreg, "could not load open-loop voltage adjustments, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		if (volt_adjust[i]) {
+			prev_volt = vreg->corner[i].open_loop_volt;
+			vreg->corner[i].open_loop_volt += volt_adjust[i];
+			cpr3_debug(vreg, "adjusted corner %d open-loop voltage: %d --> %d uV\n",
+				i, prev_volt, vreg->corner[i].open_loop_volt);
+		}
+	}
+
+	if (of_find_property(vreg->of_node,
+			"qcom,cpr-open-loop-voltage-min-diff", NULL)) {
+		rc = cpr3_parse_corner_array_property(vreg,
+			"qcom,cpr-open-loop-voltage-min-diff", 1, volt_diff);
+		if (rc) {
+			cpr3_err(vreg, "could not load minimum open-loop voltage differences, rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	/*
+	 * Ensure that open-loop voltages increase monotonically with respect
+	 * to configurable minimum allowed differences.
+	 */
+	for (i = 1; i < vreg->corner_count; i++) {
+		min_volt = vreg->corner[i - 1].open_loop_volt + volt_diff[i];
+		if (vreg->corner[i].open_loop_volt < min_volt) {
+			cpr3_debug(vreg, "adjusted corner %d open-loop voltage=%d uV < corner %d voltage=%d uV + min diff=%d uV; overriding: corner %d voltage=%d\n",
+				i, vreg->corner[i].open_loop_volt,
+				i - 1, vreg->corner[i - 1].open_loop_volt,
+				volt_diff[i], i, min_volt);
+			vreg->corner[i].open_loop_volt = min_volt;
+		}
+	}
+
+done:
+	kfree(volt_diff);
+	kfree(volt_adjust);
+	return rc;
+}
+
+/**
+ * cpr3_quot_adjustment() - returns the quotient adjustment value resulting from
+ *		the specified voltage adjustment and RO scaling factor
+ * @ro_scale:		The CPR ring oscillator (RO) scaling factor with units
+ *			of QUOT/V
+ * @volt_adjust:	The amount to adjust the voltage by in units of
+ *			microvolts.  This value may be positive or negative.
+ */
+int cpr3_quot_adjustment(int ro_scale, int volt_adjust)
+{
+	unsigned long long temp;
+	int quot_adjust;
+	int sign = 1;
+
+	if (ro_scale < 0) {
+		sign = -sign;
+		ro_scale = -ro_scale;
+	}
+
+	if (volt_adjust < 0) {
+		sign = -sign;
+		volt_adjust = -volt_adjust;
+	}
+
+	temp = (unsigned long long)ro_scale * (unsigned long long)volt_adjust;
+	do_div(temp, 1000000);
+
+	quot_adjust = temp;
+	quot_adjust *= sign;
+
+	return quot_adjust;
+}
+
+/**
+ * cpr3_voltage_adjustment() - returns the voltage adjustment value resulting
+ *		from the specified quotient adjustment and RO scaling factor
+ * @ro_scale:		The CPR ring oscillator (RO) scaling factor with units
+ *			of QUOT/V
+ * @quot_adjust:	The amount to adjust the quotient by in units of
+ *			QUOT.  This value may be positive or negative.
+ */
+int cpr3_voltage_adjustment(int ro_scale, int quot_adjust)
+{
+	unsigned long long temp;
+	int volt_adjust;
+	int sign = 1;
+
+	if (ro_scale < 0) {
+		sign = -sign;
+		ro_scale = -ro_scale;
+	}
+
+	if (quot_adjust < 0) {
+		sign = -sign;
+		quot_adjust = -quot_adjust;
+	}
+
+	if (ro_scale == 0)
+		return 0;
+
+	temp = (unsigned long long)quot_adjust * 1000000;
+	do_div(temp, ro_scale);
+
+	volt_adjust = temp;
+	volt_adjust *= sign;
+
+	return volt_adjust;
+}
+
+/**
+ * cpr3_parse_closed_loop_voltage_adjustments() - load per-fuse-corner and
+ *		per-corner closed-loop adjustment values from device tree
+ * @vreg:		Pointer to the CPR3 regulator
+ * @ro_sel:		Array of ring oscillator values selected for each
+ *			fuse corner
+ * @volt_adjust:	Pointer to array which will be filled with the
+ *			per-corner closed-loop adjustment voltages
+ * @volt_adjust_fuse:	Pointer to array which will be filled with the
+ *			per-fuse-corner closed-loop adjustment voltages
+ * @ro_scale:		Pointer to array which will be filled with the
+ *			per-fuse-corner RO scaling factor values with units of
+ *			QUOT/V
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_parse_closed_loop_voltage_adjustments(
+			struct cpr3_regulator *vreg, u64 *ro_sel,
+			int *volt_adjust, int *volt_adjust_fuse, int *ro_scale)
+{
+	int i, rc;
+	u32 *ro_all_scale;
+
+	if (!of_find_property(vreg->of_node,
+			"qcom,cpr-closed-loop-voltage-adjustment", NULL)
+	    && !of_find_property(vreg->of_node,
+			"qcom,cpr-closed-loop-voltage-fuse-adjustment", NULL)
+	    && !vreg->aging_allowed) {
+		/* No adjustment required. */
+		return 0;
+	} else if (!of_find_property(vreg->of_node,
+			"qcom,cpr-ro-scaling-factor", NULL)) {
+		cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n");
+		return -EINVAL;
+	}
+
+	ro_all_scale = kcalloc(vreg->fuse_corner_count * CPR3_RO_COUNT,
+				sizeof(*ro_all_scale), GFP_KERNEL);
+	if (!ro_all_scale)
+		return -ENOMEM;
+
+	rc = cpr3_parse_array_property(vreg, "qcom,cpr-ro-scaling-factor",
+		vreg->fuse_corner_count * CPR3_RO_COUNT, ro_all_scale);
+	if (rc) {
+		cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->fuse_corner_count; i++)
+		ro_scale[i] = ro_all_scale[i * CPR3_RO_COUNT + ro_sel[i]];
+
+	for (i = 0; i < vreg->corner_count; i++)
+		memcpy(vreg->corner[i].ro_scale,
+		 &ro_all_scale[vreg->corner[i].cpr_fuse_corner * CPR3_RO_COUNT],
+		 sizeof(*ro_all_scale) * CPR3_RO_COUNT);
+
+	if (of_find_property(vreg->of_node,
+			"qcom,cpr-closed-loop-voltage-fuse-adjustment", NULL)) {
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,cpr-closed-loop-voltage-fuse-adjustment",
+			vreg->fuse_corner_count, volt_adjust_fuse);
+		if (rc) {
+			cpr3_err(vreg, "could not load closed-loop fused voltage adjustments, rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	if (of_find_property(vreg->of_node,
+			"qcom,cpr-closed-loop-voltage-adjustment", NULL)) {
+		rc = cpr3_parse_corner_array_property(vreg,
+			"qcom,cpr-closed-loop-voltage-adjustment",
+			1, volt_adjust);
+		if (rc) {
+			cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+done:
+	kfree(ro_all_scale);
+	return rc;
+}
+
+/**
+ * cpr3_apm_init() - initialize APM data for a CPR3 controller
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * This function loads memory array power mux (APM) data from device tree
+ * if it is present and requests a handle to the appropriate APM controller
+ * device.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_apm_init(struct cpr3_controller *ctrl)
+{
+	struct device_node *node = ctrl->dev->of_node;
+	int rc;
+
+	if (!of_find_property(node, "qcom,apm-ctrl", NULL)) {
+		/* No APM used */
+		return 0;
+	}
+
+	ctrl->apm = msm_apm_ctrl_dev_get(ctrl->dev);
+	if (IS_ERR(ctrl->apm)) {
+		rc = PTR_ERR(ctrl->apm);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "APM get failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(node, "qcom,apm-threshold-voltage",
+				&ctrl->apm_threshold_volt);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,apm-threshold-voltage, rc=%d\n",
+			rc);
+		return rc;
+	}
+	ctrl->apm_threshold_volt
+		= CPR3_ROUND(ctrl->apm_threshold_volt, ctrl->step_volt);
+
+	/* No error check since this is an optional property. */
+	of_property_read_u32(node, "qcom,apm-hysteresis-voltage",
+				&ctrl->apm_adj_volt);
+	ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
+
+	ctrl->apm_high_supply = MSM_APM_SUPPLY_APCC;
+	ctrl->apm_low_supply = MSM_APM_SUPPLY_MX;
+
+	return 0;
+}
+
+/**
+ * cpr3_mem_acc_init() - initialize mem-acc regulator data for
+ *		a CPR3 regulator
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_mem_acc_init(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	u32 *temp;
+	int i, rc;
+
+	if (!ctrl->mem_acc_regulator) {
+		cpr3_info(ctrl, "not using memory accelerator regulator\n");
+		return 0;
+	}
+
+	temp = kcalloc(vreg->corner_count, sizeof(*temp), GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, "qcom,mem-acc-voltage",
+					      1, temp);
+	if (rc) {
+		cpr3_err(ctrl, "could not load mem-acc corners, rc=%d\n", rc);
+	} else {
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].mem_acc_volt = temp[i];
+	}
+
+	kfree(temp);
+	return rc;
+}
+
+/**
+ * cpr4_load_core_and_temp_adj() - parse amount of voltage adjustment for
+ *		per-online-core and per-temperature voltage adjustment for a
+ *		given corner or corner band from device tree.
+ * @vreg:	Pointer to the CPR3 regulator
+ * @num:	Corner number or corner band number
+ * @use_corner_band:	Boolean indicating if the CPR3 regulator supports
+ *			adjustments per corner band
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_load_core_and_temp_adj(struct cpr3_regulator *vreg,
+					int num, bool use_corner_band)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr4_sdelta *sdelta;
+	int sdelta_size, i, j, pos, rc = 0;
+	char str[75];
+	size_t buflen;
+	char *buf;
+
+	sdelta = use_corner_band ? vreg->corner_band[num].sdelta :
+		vreg->corner[num].sdelta;
+
+	if (!sdelta->allow_core_count_adj && !sdelta->allow_temp_adj) {
+		/* corner doesn't need sdelta table */
+		sdelta->max_core_count = 0;
+		sdelta->temp_band_count = 0;
+		return rc;
+	}
+
+	sdelta_size = sdelta->max_core_count * sdelta->temp_band_count;
+	snprintf(str, sizeof(str), use_corner_band ?
+	 "corner_band=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n"
+	 : "corner=%d core_config_count=%d temp_band_count=%d sdelta_size=%d\n",
+		 num, sdelta->max_core_count,
+		 sdelta->temp_band_count, sdelta_size);
+
+	cpr3_debug(vreg, "%s", str);
+
+	sdelta->table = devm_kcalloc(ctrl->dev, sdelta_size,
+				sizeof(*sdelta->table), GFP_KERNEL);
+	if (!sdelta->table)
+		return -ENOMEM;
+
+	snprintf(str, sizeof(str), use_corner_band ?
+		 "qcom,cpr-corner-band%d-temp-core-voltage-adjustment" :
+		 "qcom,cpr-corner%d-temp-core-voltage-adjustment",
+		 num + CPR3_CORNER_OFFSET);
+
+	rc = cpr3_parse_array_property(vreg, str, sdelta_size,
+				sdelta->table);
+	if (rc) {
+		cpr3_err(vreg, "could not load %s, rc=%d\n", str, rc);
+		return rc;
+	}
+
+	/*
+	 * Convert sdelta margins from uV to PMIC steps and apply negation to
+	 * follow the SDELTA register semantics.
+	 */
+	for (i = 0; i < sdelta_size; i++)
+		sdelta->table[i] = -(sdelta->table[i] / ctrl->step_volt);
+
+	buflen = sizeof(*buf) * sdelta_size * (MAX_CHARS_PER_INT + 2);
+	buf = kzalloc(buflen, GFP_KERNEL);
+	if (!buf)
+		return rc;
+
+	for (i = 0; i < sdelta->max_core_count; i++) {
+		for (j = 0, pos = 0; j < sdelta->temp_band_count; j++)
+			pos += scnprintf(buf + pos, buflen - pos, " %u",
+			 sdelta->table[i * sdelta->temp_band_count + j]);
+		cpr3_debug(vreg, "sdelta[%d]:%s\n", i, buf);
+	}
+
+	kfree(buf);
+	return rc;
+}
+
+/**
+ * cpr4_parse_core_count_temp_voltage_adj() - parse configuration data for
+ *		per-online-core and per-temperature voltage adjustment for
+ *		a CPR3 regulator from device tree.
+ * @vreg:	Pointer to the CPR3 regulator
+ * @use_corner_band:	Boolean indicating if the CPR3 regulator supports
+ *			adjustments per corner band
+ *
+ * This function supports parsing of per-online-core and per-temperature
+ * adjustments per corner or per corner band. CPR controllers which support
+ * corner bands apply the same adjustments to all corners within a corner band.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr4_parse_core_count_temp_voltage_adj(
+			struct cpr3_regulator *vreg, bool use_corner_band)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct device_node *node = vreg->of_node;
+	struct cpr3_corner *corner;
+	struct cpr4_sdelta *sdelta;
+	int i, sdelta_table_count, rc = 0;
+	int *allow_core_count_adj = NULL, *allow_temp_adj = NULL;
+	char prop_str[75];
+
+	if (of_find_property(node, use_corner_band ?
+			     "qcom,corner-band-allow-temp-adjustment"
+			     : "qcom,corner-allow-temp-adjustment", NULL)) {
+		if (!ctrl->allow_temp_adj) {
+			cpr3_err(ctrl, "Temperature adjustment configurations missing\n");
+			return -EINVAL;
+		}
+
+		vreg->allow_temp_adj = true;
+	}
+
+	if (of_find_property(node, use_corner_band ?
+			     "qcom,corner-band-allow-core-count-adjustment"
+			     : "qcom,corner-allow-core-count-adjustment",
+			     NULL)) {
+		rc = of_property_read_u32(node, "qcom,max-core-count",
+				&vreg->max_core_count);
+		if (rc) {
+			cpr3_err(vreg, "error reading qcom,max-core-count, rc=%d\n",
+				rc);
+			return -EINVAL;
+		}
+
+		vreg->allow_core_count_adj = true;
+		ctrl->allow_core_count_adj = true;
+	}
+
+	if (!vreg->allow_temp_adj && !vreg->allow_core_count_adj) {
+		/*
+		 * Both per-online-core and temperature based adjustments are
+		 * disabled for this regulator.
+		 */
+		return 0;
+	} else if (!vreg->allow_core_count_adj) {
+		/*
+		 * Only per-temperature voltage adjusments are allowed.
+		 * Keep max core count value as 1 to allocate SDELTA.
+		 */
+		vreg->max_core_count = 1;
+	}
+
+	if (vreg->allow_core_count_adj) {
+		allow_core_count_adj = kcalloc(vreg->corner_count,
+					sizeof(*allow_core_count_adj),
+					GFP_KERNEL);
+		if (!allow_core_count_adj)
+			return -ENOMEM;
+
+		snprintf(prop_str, sizeof(prop_str), use_corner_band ?
+			 "qcom,corner-band-allow-core-count-adjustment" :
+			 "qcom,corner-allow-core-count-adjustment");
+
+		rc = use_corner_band ?
+			cpr3_parse_corner_band_array_property(vreg, prop_str,
+					      1, allow_core_count_adj) :
+			cpr3_parse_corner_array_property(vreg, prop_str,
+						 1, allow_core_count_adj);
+		if (rc) {
+			cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str,
+				 rc);
+			goto done;
+		}
+	}
+
+	if (vreg->allow_temp_adj) {
+		allow_temp_adj = kcalloc(vreg->corner_count,
+					sizeof(*allow_temp_adj), GFP_KERNEL);
+		if (!allow_temp_adj) {
+			rc = -ENOMEM;
+			goto done;
+		}
+
+		snprintf(prop_str, sizeof(prop_str), use_corner_band ?
+			 "qcom,corner-band-allow-temp-adjustment" :
+			 "qcom,corner-allow-temp-adjustment");
+
+		rc = use_corner_band ?
+			cpr3_parse_corner_band_array_property(vreg, prop_str,
+						      1, allow_temp_adj) :
+			cpr3_parse_corner_array_property(vreg, prop_str,
+						 1, allow_temp_adj);
+		if (rc) {
+			cpr3_err(vreg, "error reading %s, rc=%d\n", prop_str,
+				 rc);
+			goto done;
+		}
+	}
+
+	sdelta_table_count = use_corner_band ? vreg->corner_band_count :
+		vreg->corner_count;
+
+	for (i = 0; i < sdelta_table_count; i++) {
+		sdelta = devm_kzalloc(ctrl->dev, sizeof(*corner->sdelta),
+				      GFP_KERNEL);
+		if (!sdelta) {
+			rc = -ENOMEM;
+			goto done;
+		}
+
+		if (allow_core_count_adj)
+			sdelta->allow_core_count_adj = allow_core_count_adj[i];
+		if (allow_temp_adj)
+			sdelta->allow_temp_adj = allow_temp_adj[i];
+		sdelta->max_core_count = vreg->max_core_count;
+		sdelta->temp_band_count = ctrl->temp_band_count;
+
+		if (use_corner_band)
+			vreg->corner_band[i].sdelta = sdelta;
+		else
+			vreg->corner[i].sdelta = sdelta;
+
+		rc = cpr4_load_core_and_temp_adj(vreg, i, use_corner_band);
+		if (rc) {
+			cpr3_err(vreg, "corner/band %d core and temp adjustment loading failed, rc=%d\n",
+				 i, rc);
+			goto done;
+		}
+	}
+
+done:
+	kfree(allow_core_count_adj);
+	kfree(allow_temp_adj);
+
+	return rc;
+}
+
+/**
+ * cprh_adjust_voltages_for_apm() - adjust per-corner floor and ceiling voltages
+ *		so that they do not overlap the APM threshold voltage.
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * The memory array power mux (APM) must be configured for a specific supply
+ * based upon where the VDD voltage lies with respect to the APM threshold
+ * voltage.  When using CPR hardware closed-loop, the voltage may vary anywhere
+ * between the floor and ceiling voltage without software notification.
+ * Therefore, it is required that the floor to ceiling range for every corner
+ * not intersect the APM threshold voltage.  This function adjusts the floor to
+ * ceiling range for each corner which violates this requirement.
+ *
+ * The following algorithm is applied:
+ *	if floor < threshold <= ceiling:
+ *		if open_loop >= threshold, then floor = threshold - adj
+ *		else ceiling = threshold - step
+ * where:
+ *	adj = APM hysteresis voltage established to minimize the number of
+ *	      corners with artificially increased floor voltages
+ *	step = voltage in microvolts of a single step of the VDD supply
+ *
+ * The open-loop voltage is also bounded by the new floor or ceiling value as
+ * needed.
+ *
+ * Return: none
+ */
+void cprh_adjust_voltages_for_apm(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr3_corner *corner;
+	int i, adj, threshold, prev_ceiling, prev_floor, prev_open_loop;
+
+	if (!ctrl->apm_threshold_volt) {
+		/* APM not being used. */
+		return;
+	}
+
+	ctrl->apm_threshold_volt = CPR3_ROUND(ctrl->apm_threshold_volt,
+						ctrl->step_volt);
+	ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
+
+	threshold = ctrl->apm_threshold_volt;
+	adj = ctrl->apm_adj_volt;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+
+		if (threshold <= corner->floor_volt
+		    || threshold > corner->ceiling_volt)
+			continue;
+
+		prev_floor = corner->floor_volt;
+		prev_ceiling = corner->ceiling_volt;
+		prev_open_loop = corner->open_loop_volt;
+
+		if (corner->open_loop_volt >= threshold) {
+			corner->floor_volt = max(corner->floor_volt,
+						 threshold - adj);
+			if (corner->open_loop_volt < corner->floor_volt)
+				corner->open_loop_volt = corner->floor_volt;
+		} else {
+			corner->ceiling_volt = threshold - ctrl->step_volt;
+		}
+
+		if (corner->floor_volt != prev_floor
+		    || corner->ceiling_volt != prev_ceiling
+		    || corner->open_loop_volt != prev_open_loop)
+			cpr3_debug(vreg, "APM threshold=%d, APM adj=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
+				threshold, adj, i, prev_floor, prev_ceiling,
+				prev_open_loop, corner->floor_volt,
+				corner->ceiling_volt, corner->open_loop_volt);
+	}
+}
+
+/**
+ * cprh_adjust_voltages_for_mem_acc() - adjust per-corner floor and ceiling
+ *		voltages so that they do not intersect the MEM ACC threshold
+ *		voltage
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * The following algorithm is applied:
+ *	if floor < threshold <= ceiling:
+ *		if open_loop >= threshold, then floor = threshold
+ *		else ceiling = threshold - step
+ * where:
+ *	step = voltage in microvolts of a single step of the VDD supply
+ *
+ * The open-loop voltage is also bounded by the new floor or ceiling value as
+ * needed.
+ *
+ * Return: none
+ */
+void cprh_adjust_voltages_for_mem_acc(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr3_corner *corner;
+	int i, threshold, prev_ceiling, prev_floor, prev_open_loop;
+
+	if (!ctrl->mem_acc_threshold_volt) {
+		/* MEM ACC not being used. */
+		return;
+	}
+
+	ctrl->mem_acc_threshold_volt = CPR3_ROUND(ctrl->mem_acc_threshold_volt,
+						ctrl->step_volt);
+
+	threshold = ctrl->mem_acc_threshold_volt;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+
+		if (threshold <= corner->floor_volt
+		    || threshold > corner->ceiling_volt)
+			continue;
+
+		prev_floor = corner->floor_volt;
+		prev_ceiling = corner->ceiling_volt;
+		prev_open_loop = corner->open_loop_volt;
+
+		if (corner->open_loop_volt >= threshold) {
+			corner->floor_volt = max(corner->floor_volt, threshold);
+			if (corner->open_loop_volt < corner->floor_volt)
+				corner->open_loop_volt = corner->floor_volt;
+		} else {
+			corner->ceiling_volt = threshold - ctrl->step_volt;
+		}
+
+		if (corner->floor_volt != prev_floor
+		    || corner->ceiling_volt != prev_ceiling
+		    || corner->open_loop_volt != prev_open_loop)
+			cpr3_debug(vreg, "MEM ACC threshold=%d changed corner %d voltages; prev: floor=%d, ceiling=%d, open-loop=%d; new: floor=%d, ceiling=%d, open-loop=%d\n",
+				threshold, i, prev_floor, prev_ceiling,
+				prev_open_loop, corner->floor_volt,
+				corner->ceiling_volt, corner->open_loop_volt);
+	}
+}
+
+/**
+ * cpr3_apply_closed_loop_offset_voltages() - modify the closed-loop voltage
+ *		adjustments by the amounts that are needed for this
+ *		fuse combo
+ * @vreg:		Pointer to the CPR3 regulator
+ * @volt_adjust:	Array of closed-loop voltage adjustment values of length
+ *			vreg->corner_count which is further adjusted based upon
+ *			offset voltage fuse values.
+ * @fuse_volt_adjust:	Fused closed-loop voltage adjustment values of length
+ *			vreg->fuse_corner_count.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr3_apply_closed_loop_offset_voltages(struct cpr3_regulator *vreg,
+			int *volt_adjust, int *fuse_volt_adjust)
+{
+	u32 *corner_map;
+	int rc = 0, i;
+
+	if (!of_find_property(vreg->of_node,
+		"qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL)) {
+		/* No closed-loop offset required. */
+		return 0;
+	}
+
+	corner_map = kcalloc(vreg->corner_count, sizeof(*corner_map),
+				GFP_KERNEL);
+	if (!corner_map)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg,
+		"qcom,cpr-fused-closed-loop-voltage-adjustment-map",
+		1, corner_map);
+	if (rc)
+		goto done;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		if (corner_map[i] == 0) {
+			continue;
+		} else if (corner_map[i] > vreg->fuse_corner_count) {
+			cpr3_err(vreg, "corner %d mapped to invalid fuse corner: %u\n",
+				i, corner_map[i]);
+			rc = -EINVAL;
+			goto done;
+		}
+
+		volt_adjust[i] += fuse_volt_adjust[corner_map[i] - 1];
+	}
+
+done:
+	kfree(corner_map);
+	return rc;
+}
+
+/**
+ * cpr3_enforce_inc_quotient_monotonicity() - Ensure that target quotients
+ *		increase monotonically from lower to higher corners
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static void cpr3_enforce_inc_quotient_monotonicity(struct cpr3_regulator *vreg)
+{
+	int i, j;
+
+	for (i = 1; i < vreg->corner_count; i++) {
+		for (j = 0; j < CPR3_RO_COUNT; j++) {
+			if (vreg->corner[i].target_quot[j]
+			    && vreg->corner[i].target_quot[j]
+					< vreg->corner[i - 1].target_quot[j]) {
+				cpr3_debug(vreg, "corner %d RO%u target quot=%u < corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n",
+					i, j,
+					vreg->corner[i].target_quot[j],
+					i - 1, j,
+					vreg->corner[i - 1].target_quot[j],
+					i, j,
+					vreg->corner[i - 1].target_quot[j]);
+				vreg->corner[i].target_quot[j]
+					= vreg->corner[i - 1].target_quot[j];
+			}
+		}
+	}
+}
+
+/**
+ * cpr3_enforce_dec_quotient_monotonicity() - Ensure that target quotients
+ *		decrease monotonically from higher to lower corners
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static void cpr3_enforce_dec_quotient_monotonicity(struct cpr3_regulator *vreg)
+{
+	int i, j;
+
+	for (i = vreg->corner_count - 2; i >= 0; i--) {
+		for (j = 0; j < CPR3_RO_COUNT; j++) {
+			if (vreg->corner[i + 1].target_quot[j]
+			    && vreg->corner[i].target_quot[j]
+					> vreg->corner[i + 1].target_quot[j]) {
+				cpr3_debug(vreg, "corner %d RO%u target quot=%u > corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n",
+					i, j,
+					vreg->corner[i].target_quot[j],
+					i + 1, j,
+					vreg->corner[i + 1].target_quot[j],
+					i, j,
+					vreg->corner[i + 1].target_quot[j]);
+				vreg->corner[i].target_quot[j]
+					= vreg->corner[i + 1].target_quot[j];
+			}
+		}
+	}
+}
+
+/**
+ * _cpr3_adjust_target_quotients() - adjust the target quotients for each
+ *		corner of the regulator according to input adjustment and
+ *		scaling arrays
+ * @vreg:		Pointer to the CPR3 regulator
+ * @volt_adjust:	Pointer to an array of closed-loop voltage adjustments
+ *			with units of microvolts.  The array must have
+ *			vreg->corner_count number of elements.
+ * @ro_scale:		Pointer to a flattened 2D array of RO scaling factors.
+ *			The array must have an inner dimension of CPR3_RO_COUNT
+ *			and an outer dimension of vreg->corner_count
+ * @label:		Null terminated string providing a label for the type
+ *			of adjustment.
+ *
+ * Return: true if any corners received a positive voltage adjustment (> 0),
+ *	   else false
+ */
+static bool _cpr3_adjust_target_quotients(struct cpr3_regulator *vreg,
+		const int *volt_adjust, const int *ro_scale, const char *label)
+{
+	int i, j, quot_adjust;
+	bool is_increasing = false;
+	u32 prev_quot;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		for (j = 0; j < CPR3_RO_COUNT; j++) {
+			if (vreg->corner[i].target_quot[j]) {
+				quot_adjust = cpr3_quot_adjustment(
+					ro_scale[i * CPR3_RO_COUNT + j],
+					volt_adjust[i]);
+				if (quot_adjust) {
+					prev_quot = vreg->corner[i].
+							target_quot[j];
+					vreg->corner[i].target_quot[j]
+						+= quot_adjust;
+					cpr3_debug(vreg, "adjusted corner %d RO%d target quot %s: %u --> %u (%d uV)\n",
+						i, j, label, prev_quot,
+						vreg->corner[i].target_quot[j],
+						volt_adjust[i]);
+				}
+			}
+		}
+		if (volt_adjust[i] > 0)
+			is_increasing = true;
+	}
+
+	return is_increasing;
+}
+
+/**
+ * cpr3_adjust_target_quotients() - adjust the target quotients for each
+ *			corner according to device tree values and fuse values
+ * @vreg:		Pointer to the CPR3 regulator
+ * @fuse_volt_adjust:	Fused closed-loop voltage adjustment values of length
+ *			vreg->fuse_corner_count. This parameter could be null
+ *			pointer when no fused adjustments are needed.
+ *
+ * Return: 0 on success, errno on failure
+ */
+int cpr3_adjust_target_quotients(struct cpr3_regulator *vreg,
+			int *fuse_volt_adjust)
+{
+	int i, rc;
+	int *volt_adjust, *ro_scale;
+	bool explicit_adjustment, fused_adjustment, is_increasing;
+
+	explicit_adjustment = of_find_property(vreg->of_node,
+		"qcom,cpr-closed-loop-voltage-adjustment", NULL);
+	fused_adjustment = of_find_property(vreg->of_node,
+		"qcom,cpr-fused-closed-loop-voltage-adjustment-map", NULL);
+
+	if (!explicit_adjustment && !fused_adjustment && !vreg->aging_allowed) {
+		/* No adjustment required. */
+		return 0;
+	} else if (!of_find_property(vreg->of_node,
+			"qcom,cpr-ro-scaling-factor", NULL)) {
+		cpr3_err(vreg, "qcom,cpr-ro-scaling-factor is required for closed-loop voltage adjustment, but is missing\n");
+		return -EINVAL;
+	}
+
+	volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust),
+				GFP_KERNEL);
+	ro_scale = kcalloc(vreg->corner_count * CPR3_RO_COUNT,
+				sizeof(*ro_scale), GFP_KERNEL);
+	if (!volt_adjust || !ro_scale) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = cpr3_parse_corner_array_property(vreg,
+			"qcom,cpr-ro-scaling-factor", CPR3_RO_COUNT, ro_scale);
+	if (rc) {
+		cpr3_err(vreg, "could not load RO scaling factors, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++)
+		memcpy(vreg->corner[i].ro_scale, &ro_scale[i * CPR3_RO_COUNT],
+			sizeof(*ro_scale) * CPR3_RO_COUNT);
+
+	if (explicit_adjustment) {
+		rc = cpr3_parse_corner_array_property(vreg,
+			"qcom,cpr-closed-loop-voltage-adjustment",
+			1, volt_adjust);
+		if (rc) {
+			cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n",
+				rc);
+			goto done;
+		}
+
+		_cpr3_adjust_target_quotients(vreg, volt_adjust, ro_scale,
+			"from DT");
+		cpr3_enforce_inc_quotient_monotonicity(vreg);
+	}
+
+	if (fused_adjustment && fuse_volt_adjust) {
+		memset(volt_adjust, 0,
+			sizeof(*volt_adjust) * vreg->corner_count);
+
+		rc = cpr3_apply_closed_loop_offset_voltages(vreg, volt_adjust,
+				fuse_volt_adjust);
+		if (rc) {
+			cpr3_err(vreg, "could not apply fused closed-loop voltage reductions, rc=%d\n",
+				rc);
+			goto done;
+		}
+
+		is_increasing = _cpr3_adjust_target_quotients(vreg, volt_adjust,
+					ro_scale, "from fuse");
+		if (is_increasing)
+			cpr3_enforce_inc_quotient_monotonicity(vreg);
+		else
+			cpr3_enforce_dec_quotient_monotonicity(vreg);
+	}
+
+done:
+	kfree(volt_adjust);
+	kfree(ro_scale);
+	return rc;
+}
diff --git a/drivers/regulator/cpr4-apss-regulator.c b/drivers/regulator/cpr4-apss-regulator.c
new file mode 100644
index 0000000..cfc09ba
--- /dev/null
+++ b/drivers/regulator/cpr4-apss-regulator.c
@@ -0,0 +1,1436 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include "cpr3-regulator.h"
+
+#define MSM8953_APSS_FUSE_CORNERS	4
+
+/**
+ * struct cpr4_msm8953_apss_fuses - APSS specific fuse data for MSM8953
+ * @ro_sel:		Ring oscillator select fuse parameter value for each
+ *			fuse corner
+ * @init_voltage:	Initial (i.e. open-loop) voltage fuse parameter value
+ *			for each fuse corner (raw, not converted to a voltage)
+ * @target_quot:	CPR target quotient fuse parameter value for each fuse
+ *			corner
+ * @quot_offset:	CPR target quotient offset fuse parameter value for each
+ *			fuse corner (raw, not unpacked) used for target quotient
+ *			interpolation
+ * @speed_bin:		Application processor speed bin fuse parameter value for
+ *			the given chip
+ * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
+ * @boost_cfg:		CPR boost configuration fuse parameter value
+ * @boost_voltage:	CPR boost voltage fuse parameter value (raw, not
+ *			converted to a voltage)
+ *
+ * This struct holds the values for all of the fuses read from memory.
+ */
+struct cpr4_msm8953_apss_fuses {
+	u64	ro_sel[MSM8953_APSS_FUSE_CORNERS];
+	u64	init_voltage[MSM8953_APSS_FUSE_CORNERS];
+	u64	target_quot[MSM8953_APSS_FUSE_CORNERS];
+	u64	quot_offset[MSM8953_APSS_FUSE_CORNERS];
+	u64	speed_bin;
+	u64	cpr_fusing_rev;
+	u64	boost_cfg;
+	u64	boost_voltage;
+	u64	misc;
+};
+
+/*
+ * fuse combo = fusing revision + 8 * (speed bin)
+ * where: fusing revision = 0 - 7 and speed bin = 0 - 7
+ */
+#define CPR4_MSM8953_APSS_FUSE_COMBO_COUNT	64
+
+/*
+ * Constants which define the name of each fuse corner.
+ */
+enum cpr4_msm8953_apss_fuse_corner {
+	CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS	= 0,
+	CPR4_MSM8953_APSS_FUSE_CORNER_SVS		= 1,
+	CPR4_MSM8953_APSS_FUSE_CORNER_NOM		= 2,
+	CPR4_MSM8953_APSS_FUSE_CORNER_TURBO_L1	= 3,
+};
+
+static const char * const cpr4_msm8953_apss_fuse_corner_name[] = {
+	[CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS]	= "LowSVS",
+	[CPR4_MSM8953_APSS_FUSE_CORNER_SVS]		= "SVS",
+	[CPR4_MSM8953_APSS_FUSE_CORNER_NOM]		= "NOM",
+	[CPR4_MSM8953_APSS_FUSE_CORNER_TURBO_L1]	= "TURBO_L1",
+};
+
+/*
+ * MSM8953 APSS fuse parameter locations:
+ *
+ * Structs are organized with the following dimensions:
+ *	Outer: 0 to 3 for fuse corners from lowest to highest corner
+ *	Inner: large enough to hold the longest set of parameter segments which
+ *		fully defines a fuse parameter, +1 (for NULL termination).
+ *		Each segment corresponds to a contiguous group of bits from a
+ *		single fuse row.  These segments are concatentated together in
+ *		order to form the full fuse parameter value.  The segments for
+ *		a given parameter may correspond to different fuse rows.
+ */
+static const struct cpr3_fuse_param
+msm8953_apss_ro_sel_param[MSM8953_APSS_FUSE_CORNERS][2] = {
+	{{73, 12, 15}, {} },
+	{{73,  8, 11}, {} },
+	{{73,  4,  7}, {} },
+	{{73,  0,  3}, {} },
+};
+
+static const struct cpr3_fuse_param
+msm8953_apss_init_voltage_param[MSM8953_APSS_FUSE_CORNERS][2] = {
+	{{71, 24, 29}, {} },
+	{{71, 18, 23}, {} },
+	{{71, 12, 17}, {} },
+	{{71,  6, 11}, {} },
+};
+
+static const struct cpr3_fuse_param
+msm8953_apss_target_quot_param[MSM8953_APSS_FUSE_CORNERS][2] = {
+	{{72, 44, 55}, {} },
+	{{72, 32, 43}, {} },
+	{{72, 20, 31}, {} },
+	{{72,  8, 19}, {} },
+};
+
+static const struct cpr3_fuse_param
+msm8953_apss_quot_offset_param[MSM8953_APSS_FUSE_CORNERS][2] = {
+	{{} },
+	{{71, 46, 52}, {} },
+	{{71, 39, 45}, {} },
+	{{71, 32, 38}, {} },
+};
+
+static const struct cpr3_fuse_param msm8953_cpr_fusing_rev_param[] = {
+	{71, 53, 55},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8953_apss_speed_bin_param[] = {
+	{36, 40, 42},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8953_cpr_boost_fuse_cfg_param[] = {
+	{36, 43, 45},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8953_apss_boost_fuse_volt_param[] = {
+	{71, 0, 5},
+	{},
+};
+
+static const struct cpr3_fuse_param msm8953_misc_fuse_volt_adj_param[] = {
+	{36, 54, 54},
+	{},
+};
+
+/*
+ * The number of possible values for misc fuse is
+ * 2^(#bits defined for misc fuse)
+ */
+#define MSM8953_MISC_FUSE_VAL_COUNT		BIT(1)
+
+/*
+ * Open loop voltage fuse reference voltages in microvolts for MSM8953
+ */
+static const int msm8953_apss_fuse_ref_volt
+	[MSM8953_APSS_FUSE_CORNERS] = {
+	645000,
+	720000,
+	865000,
+	1065000,
+};
+
+#define MSM8953_APSS_FUSE_STEP_VOLT		10000
+#define MSM8953_APSS_VOLTAGE_FUSE_SIZE	6
+#define MSM8953_APSS_QUOT_OFFSET_SCALE	5
+
+#define MSM8953_APSS_CPR_SENSOR_COUNT	13
+
+#define MSM8953_APSS_CPR_CLOCK_RATE		19200000
+
+#define MSM8953_APSS_MAX_TEMP_POINTS	3
+#define MSM8953_APSS_TEMP_SENSOR_ID_START	4
+#define MSM8953_APSS_TEMP_SENSOR_ID_END	13
+/*
+ * Boost voltage fuse reference and ceiling voltages in microvolts for
+ * MSM8953.
+ */
+#define MSM8953_APSS_BOOST_FUSE_REF_VOLT	1140000
+#define MSM8953_APSS_BOOST_CEILING_VOLT	1140000
+#define MSM8953_APSS_BOOST_FLOOR_VOLT	900000
+#define MAX_BOOST_CONFIG_FUSE_VALUE		8
+
+#define MSM8953_APSS_CPR_SDELTA_CORE_COUNT	15
+
+/*
+ * Array of integer values mapped to each of the boost config fuse values to
+ * indicate boost enable/disable status.
+ */
+static bool boost_fuse[MAX_BOOST_CONFIG_FUSE_VALUE] = {0, 1, 1, 1, 1, 1, 1, 1};
+
+/**
+ * cpr4_msm8953_apss_read_fuse_data() - load APSS specific fuse parameter values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function allocates a cpr4_msm8953_apss_fuses struct, fills it with
+ * values read out of hardware fuses, and finally copies common fuse values
+ * into the CPR3 regulator struct.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_msm8953_apss_read_fuse_data(struct cpr3_regulator *vreg)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	struct cpr4_msm8953_apss_fuses *fuse;
+	int i, rc;
+
+	fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL);
+	if (!fuse)
+		return -ENOMEM;
+
+	rc = cpr3_read_fuse_param(base, msm8953_apss_speed_bin_param,
+				&fuse->speed_bin);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc);
+		return rc;
+	}
+	cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin);
+
+	rc = cpr3_read_fuse_param(base, msm8953_cpr_fusing_rev_param,
+				&fuse->cpr_fusing_rev);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
+
+	rc = cpr3_read_fuse_param(base, msm8953_misc_fuse_volt_adj_param,
+				&fuse->misc);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read misc voltage adjustment fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR misc fuse value = %llu\n", fuse->misc);
+	if (fuse->misc >= MSM8953_MISC_FUSE_VAL_COUNT) {
+		cpr3_err(vreg, "CPR misc fuse value = %llu, should be < %lu\n",
+			fuse->misc, MSM8953_MISC_FUSE_VAL_COUNT);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < MSM8953_APSS_FUSE_CORNERS; i++) {
+		rc = cpr3_read_fuse_param(base,
+				msm8953_apss_init_voltage_param[i],
+				&fuse->init_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				msm8953_apss_target_quot_param[i],
+				&fuse->target_quot[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				msm8953_apss_ro_sel_param[i],
+				&fuse->ro_sel[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				msm8953_apss_quot_offset_param[i],
+				&fuse->quot_offset[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	rc = cpr3_read_fuse_param(base, msm8953_cpr_boost_fuse_cfg_param,
+				&fuse->boost_cfg);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR boost config fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "Voltage boost fuse config = %llu boost = %s\n",
+			fuse->boost_cfg, boost_fuse[fuse->boost_cfg]
+			? "enable" : "disable");
+
+	rc = cpr3_read_fuse_param(base,
+				msm8953_apss_boost_fuse_volt_param,
+				&fuse->boost_voltage);
+	if (rc) {
+		cpr3_err(vreg, "failed to read boost fuse voltage, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
+	if (vreg->fuse_combo >= CPR4_MSM8953_APSS_FUSE_COMBO_COUNT) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
+			vreg->fuse_combo);
+		return -EINVAL;
+	}
+
+	vreg->speed_bin_fuse	= fuse->speed_bin;
+	vreg->cpr_rev_fuse	= fuse->cpr_fusing_rev;
+	vreg->fuse_corner_count	= MSM8953_APSS_FUSE_CORNERS;
+	vreg->platform_fuses	= fuse;
+
+	return 0;
+}
+
+/**
+ * cpr4_apss_parse_corner_data() - parse APSS corner data from device tree
+ *		properties of the CPR3 regulator's device node
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_parse_corner_data(struct cpr3_regulator *vreg)
+{
+	int rc;
+
+	rc = cpr3_parse_common_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "error reading corner data, rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+/**
+ * cpr4_apss_parse_misc_fuse_voltage_adjustments() - fill an array from a
+ *		portion of the voltage adjustments specified based on
+ *		miscellaneous fuse bits.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @volt_adjust:	Voltage adjustment output data array which must be
+ *			of size vreg->corner_count
+ *
+ * cpr3_parse_common_corner_data() must be called for vreg before this function
+ * is called so that speed bin size elements are initialized.
+ *
+ * Two formats are supported for the device tree property:
+ * 1. Length == tuple_list_size * vreg->corner_count
+ *	(reading begins at index 0)
+ * 2. Length == tuple_list_size * vreg->speed_bin_corner_sum
+ *	(reading begins at index tuple_list_size * vreg->speed_bin_offset)
+ *
+ * Here, tuple_list_size is the number of possible values for misc fuse.
+ * All other property lengths are treated as errors.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_parse_misc_fuse_voltage_adjustments(
+	struct cpr3_regulator *vreg, u32 *volt_adjust)
+{
+	struct device_node *node = vreg->of_node;
+	struct cpr4_msm8953_apss_fuses *fuse = vreg->platform_fuses;
+	int tuple_list_size = MSM8953_MISC_FUSE_VAL_COUNT;
+	int i, offset, rc, len = 0;
+	const char *prop_name = "qcom,cpr-misc-fuse-voltage-adjustment";
+
+	if (!of_find_property(node, prop_name, &len)) {
+		cpr3_err(vreg, "property %s is missing\n", prop_name);
+		return -EINVAL;
+	}
+
+	if (len == tuple_list_size * vreg->corner_count * sizeof(u32)) {
+		offset = 0;
+	} else if (vreg->speed_bin_corner_sum > 0 &&
+			len == tuple_list_size * vreg->speed_bin_corner_sum
+			* sizeof(u32)) {
+		offset = tuple_list_size * vreg->speed_bin_offset
+			+ fuse->misc * vreg->corner_count;
+	} else {
+		if (vreg->speed_bin_corner_sum > 0)
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu or %zu\n",
+				prop_name, len,
+				tuple_list_size * vreg->corner_count
+					* sizeof(u32),
+				tuple_list_size * vreg->speed_bin_corner_sum
+					* sizeof(u32));
+		else
+			cpr3_err(vreg, "property %s has invalid length=%d, should be %zu\n",
+				prop_name, len,
+				tuple_list_size * vreg->corner_count
+				* sizeof(u32));
+		return -EINVAL;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		rc = of_property_read_u32_index(node, prop_name, offset + i,
+						&volt_adjust[i]);
+		if (rc) {
+			cpr3_err(vreg, "error reading property %s, rc=%d\n",
+				prop_name, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * cpr4_msm8953_apss_calculate_open_loop_voltages() - calculate the open-loop
+ *		voltage for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If open-loop voltage interpolation is allowed in device tree, then
+ * this function calculates the open-loop voltage for a given corner using
+ * linear interpolation.  This interpolation is performed using the processor
+ * frequencies of the lower and higher Fmax corners along with their fused
+ * open-loop voltages.
+ *
+ * If open-loop voltage interpolation is not allowed, then this function uses
+ * the Fmax fused open-loop voltage for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_msm8953_apss_calculate_open_loop_voltages(
+			struct cpr3_regulator *vreg)
+{
+	struct device_node *node = vreg->of_node;
+	struct cpr4_msm8953_apss_fuses *fuse = vreg->platform_fuses;
+	int i, j, rc = 0;
+	bool allow_interpolation;
+	u64 freq_low, volt_low, freq_high, volt_high;
+	int *fuse_volt, *misc_adj_volt;
+	int *fmax_corner;
+
+	fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
+				GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+				GFP_KERNEL);
+	if (!fuse_volt || !fmax_corner) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(
+			msm8953_apss_fuse_ref_volt[i],
+			MSM8953_APSS_FUSE_STEP_VOLT, fuse->init_voltage[i],
+			MSM8953_APSS_VOLTAGE_FUSE_SIZE);
+
+		/* Log fused open-loop voltage values for debugging purposes. */
+		cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n",
+			  cpr4_msm8953_apss_fuse_corner_name[i],
+			  fuse_volt[i]);
+	}
+
+	rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt);
+	if (rc) {
+		cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	allow_interpolation = of_property_read_bool(node,
+				"qcom,allow-voltage-interpolation");
+
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		if (fuse_volt[i] < fuse_volt[i - 1]) {
+			cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n",
+				i, fuse_volt[i], i - 1, fuse_volt[i - 1],
+				i, fuse_volt[i - 1]);
+			fuse_volt[i] = fuse_volt[i - 1];
+		}
+	}
+
+	if (!allow_interpolation) {
+		/* Use fused open-loop voltage for lower frequencies. */
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].open_loop_volt
+				= fuse_volt[vreg->corner[i].cpr_fuse_corner];
+		goto done;
+	}
+
+	/* Determine highest corner mapped to each fuse corner */
+	j = vreg->fuse_corner_count - 1;
+	for (i = vreg->corner_count - 1; i >= 0; i--) {
+		if (vreg->corner[i].cpr_fuse_corner == j) {
+			fmax_corner[j] = i;
+			j--;
+		}
+	}
+	if (j >= 0) {
+		cpr3_err(vreg, "invalid fuse corner mapping\n");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	for (i = 0; i <= fmax_corner[0]; i++)
+		vreg->corner[i].open_loop_volt = fuse_volt[0];
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		volt_low = fuse_volt[i - 1];
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+		volt_high = fuse_volt[i];
+
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].open_loop_volt = cpr3_interpolate(
+				freq_low, volt_low, freq_high, volt_high,
+				vreg->corner[j].proc_freq);
+	}
+
+done:
+	if (rc == 0) {
+		cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n");
+		for (i = 0; i < vreg->corner_count; i++)
+			cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i,
+				vreg->corner[i].open_loop_volt);
+
+		rc = cpr3_adjust_open_loop_voltages(vreg);
+		if (rc)
+			cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
+				rc);
+
+		if (of_find_property(node,
+			"qcom,cpr-misc-fuse-voltage-adjustment",
+			NULL)) {
+			misc_adj_volt = kcalloc(vreg->corner_count,
+					sizeof(*misc_adj_volt), GFP_KERNEL);
+			if (!misc_adj_volt) {
+				rc = -ENOMEM;
+				goto _exit;
+			}
+
+			rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg,
+				misc_adj_volt);
+			if (rc) {
+				cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n",
+					rc);
+				kfree(misc_adj_volt);
+				goto _exit;
+			}
+
+			for (i = 0; i < vreg->corner_count; i++)
+				vreg->corner[i].open_loop_volt
+						+= misc_adj_volt[i];
+			kfree(misc_adj_volt);
+		}
+	}
+
+_exit:
+	kfree(fuse_volt);
+	kfree(fmax_corner);
+	return rc;
+}
+
+/**
+ * cpr4_msm8953_apss_set_no_interpolation_quotients() - use the fused target
+ *		quotient values for lower frequencies.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @volt_adjust:	Pointer to array of per-corner closed-loop adjustment
+ *			voltages
+ * @volt_adjust_fuse:	Pointer to array of per-fuse-corner closed-loop
+ *			adjustment voltages
+ * @ro_scale:		Pointer to array of per-fuse-corner RO scaling factor
+ *			values with units of QUOT/V
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_msm8953_apss_set_no_interpolation_quotients(
+			struct cpr3_regulator *vreg, int *volt_adjust,
+			int *volt_adjust_fuse, int *ro_scale)
+{
+	struct cpr4_msm8953_apss_fuses *fuse = vreg->platform_fuses;
+	u32 quot, ro;
+	int quot_adjust;
+	int i, fuse_corner;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		fuse_corner = vreg->corner[i].cpr_fuse_corner;
+		quot = fuse->target_quot[fuse_corner];
+		quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
+					   volt_adjust_fuse[fuse_corner] +
+					   volt_adjust[i]);
+		ro = fuse->ro_sel[fuse_corner];
+		vreg->corner[i].target_quot[ro] = quot + quot_adjust;
+		cpr3_debug(vreg, "corner=%d RO=%u target quot=%u\n",
+			  i, ro, quot);
+
+		if (quot_adjust)
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n",
+				  i, ro, quot, vreg->corner[i].target_quot[ro],
+				  volt_adjust_fuse[fuse_corner] +
+				  volt_adjust[i]);
+	}
+
+	return 0;
+}
+
+/**
+ * cpr4_msm8953_apss_calculate_target_quotients() - calculate the CPR target
+ *		quotient for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If target quotient interpolation is allowed in device tree, then this
+ * function calculates the target quotient for a given corner using linear
+ * interpolation.  This interpolation is performed using the processor
+ * frequencies of the lower and higher Fmax corners along with the fused
+ * target quotient and quotient offset of the higher Fmax corner.
+ *
+ * If target quotient interpolation is not allowed, then this function uses
+ * the Fmax fused target quotient for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_msm8953_apss_calculate_target_quotients(
+			struct cpr3_regulator *vreg)
+{
+	struct cpr4_msm8953_apss_fuses *fuse = vreg->platform_fuses;
+	int rc;
+	bool allow_interpolation;
+	u64 freq_low, freq_high, prev_quot;
+	u64 *quot_low;
+	u64 *quot_high;
+	u32 quot, ro;
+	int i, j, fuse_corner, quot_adjust;
+	int *fmax_corner;
+	int *volt_adjust, *volt_adjust_fuse, *ro_scale;
+	int *voltage_adj_misc;
+
+	/* Log fused quotient values for debugging purposes. */
+	cpr3_info(vreg, "fused   LowSVS: quot[%2llu]=%4llu\n",
+		fuse->ro_sel[CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS],
+		fuse->target_quot[CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS]);
+	for (i = CPR4_MSM8953_APSS_FUSE_CORNER_SVS;
+		i <= CPR4_MSM8953_APSS_FUSE_CORNER_TURBO_L1; i++)
+		cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n",
+			cpr4_msm8953_apss_fuse_corner_name[i],
+			fuse->ro_sel[i], fuse->target_quot[i],
+			fuse->ro_sel[i], fuse->quot_offset[i] *
+			MSM8953_APSS_QUOT_OFFSET_SCALE);
+
+	allow_interpolation = of_property_read_bool(vreg->of_node,
+					"qcom,allow-quotient-interpolation");
+
+	volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust),
+					GFP_KERNEL);
+	volt_adjust_fuse = kcalloc(vreg->fuse_corner_count,
+					sizeof(*volt_adjust_fuse), GFP_KERNEL);
+	ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale),
+					GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+					GFP_KERNEL);
+	quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low),
+					GFP_KERNEL);
+	quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high),
+					GFP_KERNEL);
+	if (!volt_adjust || !volt_adjust_fuse || !ro_scale ||
+	    !fmax_corner || !quot_low || !quot_high) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0],
+				volt_adjust, volt_adjust_fuse, ro_scale);
+	if (rc) {
+		cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	if (of_find_property(vreg->of_node,
+		"qcom,cpr-misc-fuse-voltage-adjustment", NULL)) {
+		voltage_adj_misc = kcalloc(vreg->corner_count,
+				sizeof(*voltage_adj_misc), GFP_KERNEL);
+		if (!voltage_adj_misc) {
+			rc = -ENOMEM;
+			goto done;
+		}
+
+		rc = cpr4_apss_parse_misc_fuse_voltage_adjustments(vreg,
+			voltage_adj_misc);
+		if (rc) {
+			cpr3_err(vreg, "qcom,cpr-misc-fuse-voltage-adjustment reading failed, rc=%d\n",
+				rc);
+			kfree(voltage_adj_misc);
+			goto done;
+		}
+
+		for (i = 0; i < vreg->corner_count; i++)
+			volt_adjust[i] += voltage_adj_misc[i];
+
+		kfree(voltage_adj_misc);
+	}
+
+	if (!allow_interpolation) {
+		/* Use fused target quotients for lower frequencies. */
+		return cpr4_msm8953_apss_set_no_interpolation_quotients(
+				vreg, volt_adjust, volt_adjust_fuse, ro_scale);
+	}
+
+	/* Determine highest corner mapped to each fuse corner */
+	j = vreg->fuse_corner_count - 1;
+	for (i = vreg->corner_count - 1; i >= 0; i--) {
+		if (vreg->corner[i].cpr_fuse_corner == j) {
+			fmax_corner[j] = i;
+			j--;
+		}
+	}
+	if (j >= 0) {
+		cpr3_err(vreg, "invalid fuse corner mapping\n");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	i = CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS;
+	quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]);
+	quot = fuse->target_quot[i] + quot_adjust;
+	quot_high[i] = quot_low[i] = quot;
+	ro = fuse->ro_sel[i];
+	if (quot_adjust)
+		cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n",
+			i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]);
+
+	for (i = 0; i <= fmax_corner[CPR4_MSM8953_APSS_FUSE_CORNER_LOWSVS];
+		i++)
+		vreg->corner[i].target_quot[ro] = quot;
+
+	for (i = CPR4_MSM8953_APSS_FUSE_CORNER_SVS;
+	     i < vreg->fuse_corner_count; i++) {
+		quot_high[i] = fuse->target_quot[i];
+		if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
+			quot_low[i] = quot_high[i - 1];
+		else
+			quot_low[i] = quot_high[i]
+					- fuse->quot_offset[i]
+					  * MSM8953_APSS_QUOT_OFFSET_SCALE;
+		if (quot_high[i] < quot_low[i]) {
+			cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n",
+				i, quot_high[i], i, quot_low[i],
+				i, quot_low[i]);
+			quot_high[i] = quot_low[i];
+		}
+	}
+
+	/* Perform per-fuse-corner target quotient adjustment */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		quot_adjust = cpr3_quot_adjustment(ro_scale[i],
+						   volt_adjust_fuse[i]);
+		if (quot_adjust) {
+			prev_quot = quot_high[i];
+			quot_high[i] += quot_adjust;
+			cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n",
+				i, fuse->ro_sel[i], prev_quot, quot_high[i],
+				volt_adjust_fuse[i]);
+		}
+
+		if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
+			quot_low[i] = quot_high[i - 1];
+		else
+			quot_low[i] += cpr3_quot_adjustment(ro_scale[i],
+						    volt_adjust_fuse[i - 1]);
+
+		if (quot_high[i] < quot_low[i]) {
+			cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n",
+				i, quot_high[i], i, quot_low[i],
+				i, quot_low[i]);
+			quot_high[i] = quot_low[i];
+		}
+	}
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+
+		ro = fuse->ro_sel[i];
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].target_quot[ro] = cpr3_interpolate(
+				freq_low, quot_low[i], freq_high, quot_high[i],
+				vreg->corner[j].proc_freq);
+	}
+
+	/* Perform per-corner target quotient adjustment */
+	for (i = 0; i < vreg->corner_count; i++) {
+		fuse_corner = vreg->corner[i].cpr_fuse_corner;
+		ro = fuse->ro_sel[fuse_corner];
+		quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
+						   volt_adjust[i]);
+		if (quot_adjust) {
+			prev_quot = vreg->corner[i].target_quot[ro];
+			vreg->corner[i].target_quot[ro] += quot_adjust;
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n",
+				i, ro, prev_quot,
+				vreg->corner[i].target_quot[ro],
+				volt_adjust[i]);
+		}
+	}
+
+	/* Ensure that target quotients increase monotonically */
+	for (i = 1; i < vreg->corner_count; i++) {
+		ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner];
+		if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro
+		    && vreg->corner[i].target_quot[ro]
+				< vreg->corner[i - 1].target_quot[ro]) {
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n",
+				i, ro, vreg->corner[i].target_quot[ro],
+				i - 1, ro, vreg->corner[i - 1].target_quot[ro],
+				i, ro, vreg->corner[i - 1].target_quot[ro]);
+			vreg->corner[i].target_quot[ro]
+				= vreg->corner[i - 1].target_quot[ro];
+		}
+	}
+
+done:
+	kfree(volt_adjust);
+	kfree(volt_adjust_fuse);
+	kfree(ro_scale);
+	kfree(fmax_corner);
+	kfree(quot_low);
+	kfree(quot_high);
+	return rc;
+}
+
+/**
+ * cpr4_apss_print_settings() - print out APSS CPR configuration settings into
+ *		the kernel log for debugging purposes
+ * @vreg:		Pointer to the CPR3 regulator
+ */
+static void cpr4_apss_print_settings(struct cpr3_regulator *vreg)
+{
+	struct cpr3_corner *corner;
+	int i;
+
+	cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n");
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n",
+			i, corner->proc_freq, corner->cpr_fuse_corner,
+			corner->floor_volt, corner->open_loop_volt,
+			corner->ceiling_volt);
+	}
+
+	if (vreg->thread->ctrl->apm)
+		cpr3_debug(vreg, "APM threshold = %d uV, APM adjust = %d uV\n",
+			vreg->thread->ctrl->apm_threshold_volt,
+			vreg->thread->ctrl->apm_adj_volt);
+}
+
+/**
+ * cpr4_apss_init_thread() - perform steps necessary to initialize the
+ *		configuration data for a CPR3 thread
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_init_thread(struct cpr3_thread *thread)
+{
+	int rc;
+
+	rc = cpr3_parse_common_thread_data(thread);
+	if (rc) {
+		cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n",
+			thread->thread_id, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * cpr4_apss_parse_temp_adj_properties() - parse temperature based
+ *		adjustment properties from device tree.
+ * @ctrl:	Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_parse_temp_adj_properties(struct cpr3_controller *ctrl)
+{
+	struct device_node *of_node = ctrl->dev->of_node;
+	int rc, i, len, temp_point_count;
+
+	if (!of_find_property(of_node, "qcom,cpr-temp-point-map", &len)) {
+		/*
+		 * Temperature based adjustments are not defined. Single
+		 * temperature band is still valid for per-online-core
+		 * adjustments.
+		 */
+		ctrl->temp_band_count = 1;
+		return 0;
+	}
+
+	temp_point_count = len / sizeof(u32);
+	if (temp_point_count <= 0
+		|| temp_point_count > MSM8953_APSS_MAX_TEMP_POINTS) {
+		cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n",
+			 temp_point_count, MSM8953_APSS_MAX_TEMP_POINTS);
+		return -EINVAL;
+	}
+
+	ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count,
+					sizeof(*ctrl->temp_points), GFP_KERNEL);
+	if (!ctrl->temp_points)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,cpr-temp-point-map",
+					ctrl->temp_points, temp_point_count);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	for (i = 0; i < temp_point_count; i++)
+		cpr3_debug(ctrl, "Temperature Point %d=%d\n", i,
+				   ctrl->temp_points[i]);
+
+	/*
+	 * If t1, t2, and t3 are the temperature points, then the temperature
+	 * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).
+	 */
+	ctrl->temp_band_count = temp_point_count + 1;
+	cpr3_debug(ctrl, "Number of temp bands =%d\n", ctrl->temp_band_count);
+
+	rc = of_property_read_u32(of_node, "qcom,cpr-initial-temp-band",
+				  &ctrl->initial_temp_band);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->initial_temp_band >= ctrl->temp_band_count) {
+		cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n",
+			ctrl->initial_temp_band, ctrl->temp_band_count - 1);
+		return -EINVAL;
+	}
+
+	ctrl->temp_sensor_id_start = MSM8953_APSS_TEMP_SENSOR_ID_START;
+	ctrl->temp_sensor_id_end = MSM8953_APSS_TEMP_SENSOR_ID_END;
+	ctrl->allow_temp_adj = true;
+	return rc;
+}
+
+/**
+ * cpr4_apss_parse_boost_properties() - parse configuration data for boost
+ *		voltage adjustment for CPR3 regulator from device tree.
+ * @vreg:	Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_parse_boost_properties(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr4_msm8953_apss_fuses *fuse = vreg->platform_fuses;
+	struct cpr3_corner *corner;
+	int i, boost_voltage, final_boost_volt, rc = 0;
+	int *boost_table = NULL, *boost_temp_adj = NULL;
+	int boost_voltage_adjust = 0, boost_num_cores = 0;
+	u32 boost_allowed = 0;
+
+	if (!boost_fuse[fuse->boost_cfg])
+		/* Voltage boost is disabled in fuse */
+		return 0;
+
+	if (of_find_property(vreg->of_node, "qcom,allow-boost", NULL)) {
+		rc = cpr3_parse_array_property(vreg, "qcom,allow-boost", 1,
+				&boost_allowed);
+		if (rc)
+			return rc;
+	}
+
+	if (!boost_allowed) {
+		/* Voltage boost is not enabled for this regulator */
+		return 0;
+	}
+
+	boost_voltage = cpr3_convert_open_loop_voltage_fuse(
+				MSM8953_APSS_BOOST_FUSE_REF_VOLT,
+				MSM8953_APSS_FUSE_STEP_VOLT,
+				fuse->boost_voltage,
+				MSM8953_APSS_VOLTAGE_FUSE_SIZE);
+
+	/* Log boost voltage value for debugging purposes. */
+	cpr3_info(vreg, "Boost open-loop=%7d uV\n", boost_voltage);
+
+	if (of_find_property(vreg->of_node,
+			"qcom,cpr-boost-voltage-fuse-adjustment", NULL)) {
+		rc = cpr3_parse_array_property(vreg,
+			"qcom,cpr-boost-voltage-fuse-adjustment",
+			1, &boost_voltage_adjust);
+		if (rc) {
+			cpr3_err(vreg, "qcom,cpr-boost-voltage-fuse-adjustment reading failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		boost_voltage += boost_voltage_adjust;
+		/* Log boost voltage value for debugging purposes. */
+		cpr3_info(vreg, "Adjusted boost open-loop=%7d uV\n",
+			boost_voltage);
+	}
+
+	/* Limit boost voltage value between ceiling and floor voltage limits */
+	boost_voltage = min(boost_voltage, MSM8953_APSS_BOOST_CEILING_VOLT);
+	boost_voltage = max(boost_voltage, MSM8953_APSS_BOOST_FLOOR_VOLT);
+
+	/*
+	 * The boost feature can only be used for the highest voltage corner.
+	 * Also, keep core-count adjustments disabled when the boost feature
+	 * is enabled.
+	 */
+	corner = &vreg->corner[vreg->corner_count - 1];
+	if (!corner->sdelta) {
+		/*
+		 * If core-count/temp adjustments are not defined, the cpr4
+		 * sdelta for this corner will not be allocated. Allocate it
+		 * here for boost configuration.
+		 */
+		corner->sdelta = devm_kzalloc(ctrl->dev,
+					sizeof(*corner->sdelta), GFP_KERNEL);
+		if (!corner->sdelta)
+			return -ENOMEM;
+	}
+	corner->sdelta->temp_band_count = ctrl->temp_band_count;
+
+	rc = of_property_read_u32(vreg->of_node, "qcom,cpr-num-boost-cores",
+				&boost_num_cores);
+	if (rc) {
+		cpr3_err(vreg, "qcom,cpr-num-boost-cores reading failed, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (boost_num_cores <= 0
+		|| boost_num_cores > MSM8953_APSS_CPR_SDELTA_CORE_COUNT) {
+		cpr3_err(vreg, "Invalid boost number of cores = %d\n",
+			boost_num_cores);
+		return -EINVAL;
+	}
+	corner->sdelta->boost_num_cores = boost_num_cores;
+
+	boost_table = devm_kcalloc(ctrl->dev, corner->sdelta->temp_band_count,
+					sizeof(*boost_table), GFP_KERNEL);
+	if (!boost_table)
+		return -ENOMEM;
+
+	if (of_find_property(vreg->of_node,
+				"qcom,cpr-boost-temp-adjustment", NULL)) {
+		boost_temp_adj = kcalloc(corner->sdelta->temp_band_count,
+					sizeof(*boost_temp_adj), GFP_KERNEL);
+		if (!boost_temp_adj)
+			return -ENOMEM;
+
+		rc = cpr3_parse_array_property(vreg,
+				"qcom,cpr-boost-temp-adjustment",
+				corner->sdelta->temp_band_count,
+				boost_temp_adj);
+		if (rc) {
+			cpr3_err(vreg, "qcom,cpr-boost-temp-adjustment reading failed, rc=%d\n",
+				rc);
+			goto done;
+		}
+	}
+
+	for (i = 0; i < corner->sdelta->temp_band_count; i++) {
+		/* Apply static adjustments to boost voltage */
+		final_boost_volt = boost_voltage + (boost_temp_adj == NULL
+						? 0 : boost_temp_adj[i]);
+		/*
+		 * Limit final adjusted boost voltage value between ceiling
+		 * and floor voltage limits
+		 */
+		final_boost_volt = min(final_boost_volt,
+					MSM8953_APSS_BOOST_CEILING_VOLT);
+		final_boost_volt = max(final_boost_volt,
+					MSM8953_APSS_BOOST_FLOOR_VOLT);
+
+		boost_table[i] = (corner->open_loop_volt - final_boost_volt)
+					/ ctrl->step_volt;
+		cpr3_debug(vreg, "Adjusted boost voltage margin for temp band %d = %d steps\n",
+			i, boost_table[i]);
+	}
+
+	corner->ceiling_volt = MSM8953_APSS_BOOST_CEILING_VOLT;
+	corner->sdelta->boost_table = boost_table;
+	corner->sdelta->allow_boost = true;
+	corner->sdelta->allow_core_count_adj = false;
+	vreg->allow_boost = true;
+	ctrl->allow_boost = true;
+done:
+	kfree(boost_temp_adj);
+	return rc;
+}
+
+/**
+ * cpr4_apss_init_regulator() - perform all steps necessary to initialize the
+ *		configuration data for a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_init_regulator(struct cpr3_regulator *vreg)
+{
+	struct cpr4_msm8953_apss_fuses *fuse;
+	int rc;
+
+	rc = cpr4_msm8953_apss_read_fuse_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc);
+		return rc;
+	}
+
+	fuse = vreg->platform_fuses;
+
+	rc = cpr4_apss_parse_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_mem_acc_init(vreg);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to initialize mem-acc regulator settings, rc=%d\n",
+				 rc);
+		return rc;
+	}
+
+	rc = cpr4_msm8953_apss_calculate_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_limit_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_open_loop_voltage_as_ceiling(vreg);
+
+	rc = cpr3_limit_floor_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cpr4_msm8953_apss_calculate_target_quotients(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr4_parse_core_count_temp_voltage_adj(vreg, false);
+	if (rc) {
+		cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	if (vreg->allow_core_count_adj && (vreg->max_core_count <= 0
+				   || vreg->max_core_count >
+				   MSM8953_APSS_CPR_SDELTA_CORE_COUNT)) {
+		cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n",
+			 vreg->max_core_count);
+		return -EINVAL;
+	}
+
+	rc = cpr4_apss_parse_boost_properties(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to parse boost adjustments, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	cpr4_apss_print_settings(vreg);
+
+	return rc;
+}
+
+/**
+ * cpr4_apss_init_controller() - perform APSS CPR4 controller specific
+ *		initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_apss_init_controller(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	rc = cpr3_parse_common_ctrl_data(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,cpr-down-error-step-limit",
+				  &ctrl->down_error_step_limit);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-down-error-step-limit, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,cpr-up-error-step-limit",
+				  &ctrl->up_error_step_limit);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-up-error-step-limit, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	/*
+	 * Use fixed step quotient if specified otherwise use dynamic
+	 * calculated per RO step quotient
+	 */
+	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed",
+			&ctrl->step_quot_fixed);
+	ctrl->use_dynamic_step_quot = ctrl->step_quot_fixed ? false : true;
+
+	ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node,
+					"qcom,cpr-saw-use-unit-mV");
+
+	of_property_read_u32(ctrl->dev->of_node,
+			"qcom,cpr-voltage-settling-time",
+			&ctrl->voltage_settling_time);
+
+	ctrl->vdd_limit_regulator = devm_regulator_get(ctrl->dev, "vdd-limit");
+	if (IS_ERR(ctrl->vdd_limit_regulator)) {
+		rc = PTR_ERR(ctrl->vdd_limit_regulator);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to request vdd-limit regulator, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = cpr3_apm_init(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to initialize APM settings, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = cpr4_apss_parse_temp_adj_properties(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "unable to parse temperature adjustment properties, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	ctrl->sensor_count = MSM8953_APSS_CPR_SENSOR_COUNT;
+
+	/*
+	 * APSS only has one thread (0) per controller so the zeroed
+	 * array does not need further modification.
+	 */
+	ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count,
+		sizeof(*ctrl->sensor_owner), GFP_KERNEL);
+	if (!ctrl->sensor_owner)
+		return -ENOMEM;
+
+	ctrl->cpr_clock_rate = MSM8953_APSS_CPR_CLOCK_RATE;
+	ctrl->ctrl_type = CPR_CTRL_TYPE_CPR4;
+	ctrl->supports_hw_closed_loop = true;
+	ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node,
+						"qcom,cpr-hw-closed-loop");
+	return 0;
+}
+
+static int cpr4_apss_regulator_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_suspend(ctrl);
+}
+
+static int cpr4_apss_regulator_resume(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_resume(ctrl);
+}
+
+static int cpr4_apss_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cpr3_controller *ctrl;
+	int i, rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	/* Set to false later if anything precludes CPR operation. */
+	ctrl->cpr_allowed_hw = true;
+
+	rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name",
+					&ctrl->name);
+	if (rc) {
+		cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_map_fuse_base(ctrl, pdev);
+	if (rc) {
+		cpr3_err(ctrl, "could not map fuse base address\n");
+		return rc;
+	}
+
+	rc = cpr3_allocate_threads(ctrl, 0, 0);
+	if (rc) {
+		cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->thread_count != 1) {
+		cpr3_err(ctrl, "expected 1 thread but found %d\n",
+			ctrl->thread_count);
+		return -EINVAL;
+	}
+
+	rc = cpr4_apss_init_controller(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = cpr4_apss_init_thread(&ctrl->thread[0]);
+	if (rc) {
+		cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < ctrl->thread[0].vreg_count; i++) {
+		rc = cpr4_apss_init_regulator(&ctrl->thread[0].vreg[i]);
+		if (rc) {
+			cpr3_err(&ctrl->thread[0].vreg[i], "regulator initialization failed, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	platform_set_drvdata(pdev, ctrl);
+
+	return cpr3_regulator_register(pdev, ctrl);
+}
+
+static int cpr4_apss_regulator_remove(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_unregister(ctrl);
+}
+
+static const struct of_device_id cpr4_regulator_match_table[] = {
+	{ .compatible = "qcom,cpr4-msm8953-apss-regulator", },
+	{}
+};
+
+static struct platform_driver cpr4_apss_regulator_driver = {
+	.driver		= {
+		.name		= "qcom,cpr4-apss-regulator",
+		.of_match_table	= cpr4_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= cpr4_apss_regulator_probe,
+	.remove		= cpr4_apss_regulator_remove,
+	.suspend	= cpr4_apss_regulator_suspend,
+	.resume		= cpr4_apss_regulator_resume,
+};
+
+static int cpr4_regulator_init(void)
+{
+	return platform_driver_register(&cpr4_apss_regulator_driver);
+}
+
+static void cpr4_regulator_exit(void)
+{
+	platform_driver_unregister(&cpr4_apss_regulator_driver);
+}
+
+MODULE_DESCRIPTION("CPR4 APSS regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(cpr4_regulator_init);
+module_exit(cpr4_regulator_exit);
diff --git a/drivers/regulator/cpr4-mmss-ldo-regulator.c b/drivers/regulator/cpr4-mmss-ldo-regulator.c
new file mode 100644
index 0000000..c4aef84
--- /dev/null
+++ b/drivers/regulator/cpr4-mmss-ldo-regulator.c
@@ -0,0 +1,769 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/msm-ldo-regulator.h>
+
+#include "cpr3-regulator.h"
+
+#define SDM660_MMSS_FUSE_CORNERS	6
+
+/**
+ * struct cpr4_sdm660_mmss_fuses - MMSS specific fuse data for SDM660
+ * @init_voltage:	Initial (i.e. open-loop) voltage fuse parameter value
+ *			for each fuse corner (raw, not converted to a voltage)
+ * @offset_voltage:	The closed-loop voltage margin adjustment fuse parameter
+ *			value for each fuse corner (raw, not converted to a
+ *			voltage)
+ * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
+ * @ldo_enable:		The ldo enable fuse parameter for each fuse corner
+ *			indicates that VDD_GFX can be configured to LDO mode in
+ *			the corresponding fuse corner.
+ * @ldo_cpr_cl_enable:	A fuse parameter indicates that GFX CPR can be
+ *			configured to operate in closed-loop mode when VDD_GFX
+ *			is configured for LDO sub-regulated mode.
+ *
+ * This struct holds the values for all of the fuses read from memory.
+ */
+struct cpr4_sdm660_mmss_fuses {
+	u64	init_voltage[SDM660_MMSS_FUSE_CORNERS];
+	u64	offset_voltage[SDM660_MMSS_FUSE_CORNERS];
+	u64	cpr_fusing_rev;
+	u64	ldo_enable[SDM660_MMSS_FUSE_CORNERS];
+	u64	ldo_cpr_cl_enable;
+};
+
+/* Fuse combos 0 -  7 map to CPR fusing revision 0 - 7 */
+#define CPR4_SDM660_MMSS_FUSE_COMBO_COUNT	8
+
+/*
+ * SDM660 MMSS fuse parameter locations:
+ *
+ * Structs are organized with the following dimensions:
+ *	Outer: 0 to 3 for fuse corners from lowest to highest corner
+ *	Inner: large enough to hold the longest set of parameter segments which
+ *		fully defines a fuse parameter, +1 (for NULL termination).
+ *		Each segment corresponds to a contiguous group of bits from a
+ *		single fuse row.  These segments are concatentated together in
+ *		order to form the full fuse parameter value.  The segments for
+ *		a given parameter may correspond to different fuse rows.
+ */
+static const struct cpr3_fuse_param
+sdm660_mmss_init_voltage_param[SDM660_MMSS_FUSE_CORNERS][2] = {
+	{{65, 39, 43}, {} },
+	{{65, 39, 43}, {} },
+	{{65, 34, 38}, {} },
+	{{65, 34, 38}, {} },
+	{{65, 29, 33}, {} },
+	{{65, 24, 28}, {} },
+};
+
+static const struct cpr3_fuse_param sdm660_cpr_fusing_rev_param[] = {
+	{71, 34, 36},
+	{},
+};
+
+static const struct cpr3_fuse_param
+sdm660_mmss_offset_voltage_param[SDM660_MMSS_FUSE_CORNERS][2] = {
+	{{} },
+	{{} },
+	{{} },
+	{{65, 52, 55}, {} },
+	{{65, 48, 51}, {} },
+	{{65, 44, 47}, {} },
+};
+
+static const struct cpr3_fuse_param
+sdm660_mmss_ldo_enable_param[SDM660_MMSS_FUSE_CORNERS][2] = {
+	{{73, 62, 62}, {} },
+	{{73, 61, 61}, {} },
+	{{73, 60, 60}, {} },
+	{{73, 59, 59}, {} },
+	{{73, 58, 58}, {} },
+	{{73, 57, 57}, {} },
+};
+
+static const struct cpr3_fuse_param sdm660_ldo_cpr_cl_enable_param[] = {
+	{71, 38, 38},
+	{},
+};
+
+/* Additional SDM660 specific data: */
+
+/* Open loop voltage fuse reference voltages in microvolts */
+static const int sdm660_mmss_fuse_ref_volt[SDM660_MMSS_FUSE_CORNERS] = {
+	585000,
+	645000,
+	725000,
+	790000,
+	870000,
+	925000,
+};
+
+#define SDM660_MMSS_FUSE_STEP_VOLT		10000
+#define SDM660_MMSS_OFFSET_FUSE_STEP_VOLT	10000
+#define SDM660_MMSS_VOLTAGE_FUSE_SIZE	5
+
+#define SDM660_MMSS_CPR_SENSOR_COUNT		11
+
+#define SDM660_MMSS_CPR_CLOCK_RATE		19200000
+
+/**
+ * cpr4_sdm660_mmss_read_fuse_data() - load MMSS specific fuse parameter
+ *		values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function allocates a cpr4_sdm660_mmss_fuses struct, fills it with
+ * values read out of hardware fuses, and finally copies common fuse values
+ * into the regulator struct.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_sdm660_mmss_read_fuse_data(struct cpr3_regulator *vreg)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	struct cpr4_sdm660_mmss_fuses *fuse;
+	int i, rc;
+
+	fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL);
+	if (!fuse)
+		return -ENOMEM;
+
+	rc = cpr3_read_fuse_param(base, sdm660_cpr_fusing_rev_param,
+			&fuse->cpr_fusing_rev);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
+
+	rc = cpr3_read_fuse_param(base, sdm660_ldo_cpr_cl_enable_param,
+			&fuse->ldo_cpr_cl_enable);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read ldo cpr closed-loop enable fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	for (i = 0; i < SDM660_MMSS_FUSE_CORNERS; i++) {
+		rc = cpr3_read_fuse_param(base,
+			sdm660_mmss_init_voltage_param[i],
+			&fuse->init_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+			sdm660_mmss_offset_voltage_param[i],
+			&fuse->offset_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d offset voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+			sdm660_mmss_ldo_enable_param[i],
+			&fuse->ldo_enable[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d ldo enable fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	vreg->fuse_combo = fuse->cpr_fusing_rev;
+	if (vreg->fuse_combo >= CPR4_SDM660_MMSS_FUSE_COMBO_COUNT) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found, not in range 0 - %d\n",
+			vreg->fuse_combo,
+			CPR4_SDM660_MMSS_FUSE_COMBO_COUNT - 1);
+		return -EINVAL;
+	}
+
+	vreg->cpr_rev_fuse	= fuse->cpr_fusing_rev;
+	vreg->fuse_corner_count	= SDM660_MMSS_FUSE_CORNERS;
+	vreg->platform_fuses	= fuse;
+
+	return 0;
+}
+
+/**
+ * cpr3_sdm660_mmss_calculate_open_loop_voltages() - calculate the open-loop
+ *		voltage for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_sdm660_mmss_calculate_open_loop_voltages(
+			struct cpr3_regulator *vreg)
+{
+	struct cpr4_sdm660_mmss_fuses *fuse = vreg->platform_fuses;
+	int i, rc = 0;
+	const int *ref_volt;
+	int *fuse_volt;
+
+	fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
+				GFP_KERNEL);
+	if (!fuse_volt)
+		return -ENOMEM;
+
+	ref_volt = sdm660_mmss_fuse_ref_volt;
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(ref_volt[i],
+			SDM660_MMSS_FUSE_STEP_VOLT, fuse->init_voltage[i],
+			SDM660_MMSS_VOLTAGE_FUSE_SIZE);
+		cpr3_info(vreg, "fuse_corner[%d] open-loop=%7d uV\n",
+			i, fuse_volt[i]);
+	}
+
+	rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt);
+	if (rc) {
+		cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		if (fuse_volt[i] < fuse_volt[i - 1]) {
+			cpr3_debug(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n",
+				i, fuse_volt[i], i - 1, fuse_volt[i - 1],
+				i, fuse_volt[i - 1]);
+			fuse_volt[i] = fuse_volt[i - 1];
+		}
+	}
+
+	for (i = 0; i < vreg->corner_count; i++)
+		vreg->corner[i].open_loop_volt
+			= fuse_volt[vreg->corner[i].cpr_fuse_corner];
+
+	cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n");
+	for (i = 0; i < vreg->corner_count; i++)
+		cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i,
+			vreg->corner[i].open_loop_volt);
+
+	rc = cpr3_adjust_open_loop_voltages(vreg);
+	if (rc)
+		cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
+			rc);
+
+done:
+	kfree(fuse_volt);
+	return rc;
+}
+
+/**
+ * cpr4_mmss_parse_ldo_mode_data() - Parse the LDO mode enable state for each
+ *		corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function considers 2 sets of data: one set from device node and other
+ * set from fuses and applies set intersection to decide the final LDO mode
+ * enable state of each corner. If the device node configuration is not
+ * specified, then the function applies LDO mode disable for all corners.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_mmss_parse_ldo_mode_data(struct cpr3_regulator *vreg)
+{
+	struct cpr4_sdm660_mmss_fuses *fuse = vreg->platform_fuses;
+	int i, rc = 0;
+	u32 *ldo_allowed;
+	char *prop_str = "qcom,cpr-corner-allow-ldo-mode";
+
+	if (!of_find_property(vreg->of_node, prop_str, NULL)) {
+		cpr3_debug(vreg, "%s property is missing. LDO mode is disabled for all corners\n",
+			prop_str);
+		return 0;
+	}
+
+	ldo_allowed = kcalloc(vreg->corner_count, sizeof(*ldo_allowed),
+			GFP_KERNEL);
+	if (!ldo_allowed)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, prop_str, 1, ldo_allowed);
+	if (rc) {
+		cpr3_err(vreg, "%s read failed, rc=%d\n", prop_str, rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++)
+		vreg->corner[i].ldo_mode_allowed
+			= (ldo_allowed[i] && fuse->ldo_enable[i]);
+
+done:
+	kfree(ldo_allowed);
+	return rc;
+}
+
+/**
+ * cpr4_mmss_parse_corner_operating_mode() - Parse the CPR closed-loop operation
+ *		enable state for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function ensures that closed-loop operation is enabled only for LDO
+ * mode allowed corners.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_mmss_parse_corner_operating_mode(struct cpr3_regulator *vreg)
+{
+	struct cpr4_sdm660_mmss_fuses *fuse = vreg->platform_fuses;
+	int i, rc = 0;
+	u32 *use_closed_loop;
+	char *prop_str = "qcom,cpr-corner-allow-closed-loop";
+
+	if (!of_find_property(vreg->of_node, prop_str, NULL)) {
+		cpr3_debug(vreg, "%s property is missing. Use open-loop for all corners\n",
+			prop_str);
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].use_open_loop = true;
+
+		return 0;
+	}
+
+	use_closed_loop = kcalloc(vreg->corner_count, sizeof(*use_closed_loop),
+				GFP_KERNEL);
+	if (!use_closed_loop)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, prop_str, 1,
+			use_closed_loop);
+	if (rc) {
+		cpr3_err(vreg, "%s read failed, rc=%d\n", prop_str, rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++)
+		vreg->corner[i].use_open_loop
+			= !(fuse->ldo_cpr_cl_enable && use_closed_loop[i]
+				&& vreg->corner[i].ldo_mode_allowed);
+
+done:
+	kfree(use_closed_loop);
+	return rc;
+}
+
+/**
+ * cpr4_mmss_parse_corner_data() - parse MMSS corner data from device tree
+ *		properties of the regulator's device node
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_mmss_parse_corner_data(struct cpr3_regulator *vreg)
+{
+	int i, rc;
+	u32 *temp;
+
+	rc = cpr3_parse_common_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "error reading corner data, rc=%d\n", rc);
+		return rc;
+	}
+
+	temp = kcalloc(vreg->corner_count * CPR3_RO_COUNT, sizeof(*temp),
+			GFP_KERNEL);
+	if (!temp)
+		return -ENOMEM;
+
+	rc = cpr3_parse_corner_array_property(vreg, "qcom,cpr-target-quotients",
+			CPR3_RO_COUNT, temp);
+	if (rc) {
+		cpr3_err(vreg, "could not load target quotients, rc=%d\n", rc);
+		goto done;
+	}
+
+	for (i = 0; i < vreg->corner_count; i++)
+		memcpy(vreg->corner[i].target_quot, &temp[i * CPR3_RO_COUNT],
+			sizeof(*temp) * CPR3_RO_COUNT);
+
+done:
+	kfree(temp);
+	return rc;
+}
+
+/**
+ * cpr4_sdm660_mmss_adjust_target_quotients() - adjust the target quotients for
+ *		each corner according to device tree values and fuse values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_sdm660_mmss_adjust_target_quotients(struct cpr3_regulator *vreg)
+{
+	struct cpr4_sdm660_mmss_fuses *fuse = vreg->platform_fuses;
+	const struct cpr3_fuse_param (*offset_param)[2];
+	int *volt_offset;
+	int i, fuse_len, rc = 0;
+
+	volt_offset = kcalloc(vreg->fuse_corner_count, sizeof(*volt_offset),
+				GFP_KERNEL);
+	if (!volt_offset)
+		return -ENOMEM;
+
+	offset_param = sdm660_mmss_offset_voltage_param;
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		fuse_len = offset_param[i][0].bit_end + 1
+			   - offset_param[i][0].bit_start;
+		volt_offset[i] = cpr3_convert_open_loop_voltage_fuse(
+			0, SDM660_MMSS_OFFSET_FUSE_STEP_VOLT,
+			fuse->offset_voltage[i], fuse_len);
+		if (volt_offset[i])
+			cpr3_info(vreg, "fuse_corner[%d] offset=%7d uV\n",
+				i, volt_offset[i]);
+	}
+
+	rc = cpr3_adjust_target_quotients(vreg, volt_offset);
+	if (rc)
+		cpr3_err(vreg, "adjust target quotients failed, rc=%d\n", rc);
+
+	kfree(volt_offset);
+	return rc;
+}
+
+/**
+ * cpr4_mmss_print_settings() - print out MMSS CPR configuration settings into
+ *		the kernel log for debugging purposes
+ * @vreg:		Pointer to the CPR3 regulator
+ */
+static void cpr4_mmss_print_settings(struct cpr3_regulator *vreg)
+{
+	struct cpr3_corner *corner;
+	int i;
+
+	cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n");
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n",
+			i, corner->proc_freq, corner->cpr_fuse_corner,
+			corner->floor_volt, corner->open_loop_volt,
+			corner->ceiling_volt);
+	}
+}
+
+/**
+ * cpr4_mmss_init_thread() - perform all steps necessary to initialize the
+ *		configuration data for a CPR3 thread
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_mmss_init_thread(struct cpr3_thread *thread)
+{
+	struct cpr3_controller *ctrl = thread->ctrl;
+	struct cpr3_regulator *vreg = &thread->vreg[0];
+	int rc;
+
+	rc = cpr3_parse_common_thread_data(thread);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR thread data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (!of_find_property(ctrl->dev->of_node, "vdd-thread0-ldo-supply",
+		NULL)) {
+		cpr3_err(vreg, "ldo supply regulator is not specified\n");
+		return -EINVAL;
+	}
+
+	vreg->ldo_regulator = devm_regulator_get(ctrl->dev, "vdd-thread0-ldo");
+	if (IS_ERR(vreg->ldo_regulator)) {
+		rc = PTR_ERR(vreg->ldo_regulator);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(vreg, "unable to request vdd-thread0-ldo regulator, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	vreg->ldo_mode_allowed = !of_property_read_bool(vreg->of_node,
+							"qcom,ldo-disable");
+	vreg->ldo_regulator_bypass = BHS_MODE;
+	vreg->ldo_type = CPR3_LDO300;
+
+	rc = cpr4_sdm660_mmss_read_fuse_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cpr4_mmss_parse_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr4_sdm660_mmss_adjust_target_quotients(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to adjust target quotients, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr4_sdm660_mmss_calculate_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_limit_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr3_open_loop_voltage_as_ceiling(vreg);
+
+	rc = cpr3_limit_floor_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cpr4_mmss_parse_ldo_mode_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to parse ldo mode data, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cpr4_mmss_parse_corner_operating_mode(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to parse closed-loop operating mode data, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cpr4_mmss_print_settings(vreg);
+
+	return 0;
+}
+
+/**
+ * cpr4_mmss_init_controller() - perform MMSS CPR4 controller specific
+ *		initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cpr4_mmss_init_controller(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	rc = cpr3_parse_common_ctrl_data(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	ctrl->sensor_count = SDM660_MMSS_CPR_SENSOR_COUNT;
+
+	/*
+	 * MMSS only has one thread (0) so the zeroed array does not need
+	 * further modification.
+	 */
+	ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count,
+				sizeof(*ctrl->sensor_owner), GFP_KERNEL);
+	if (!ctrl->sensor_owner)
+		return -ENOMEM;
+
+	ctrl->cpr_clock_rate = SDM660_MMSS_CPR_CLOCK_RATE;
+	ctrl->ctrl_type = CPR_CTRL_TYPE_CPR4;
+	ctrl->support_ldo300_vreg = true;
+
+	/*
+	 * Use fixed step quotient if specified otherwise use dynamic
+	 * calculated per RO step quotient
+	 */
+	of_property_read_u32(ctrl->dev->of_node,
+			     "qcom,cpr-step-quot-fixed",
+			     &ctrl->step_quot_fixed);
+	ctrl->use_dynamic_step_quot = !ctrl->step_quot_fixed;
+
+	/* iface_clk is optional for sdm660 */
+	ctrl->iface_clk = NULL;
+	ctrl->bus_clk = devm_clk_get(ctrl->dev, "bus_clk");
+	if (IS_ERR(ctrl->bus_clk)) {
+		rc = PTR_ERR(ctrl->bus_clk);
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable request bus clock, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int cpr4_mmss_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct cpr3_controller *ctrl;
+	int rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	/* Set to false later if anything precludes CPR operation. */
+	ctrl->cpr_allowed_hw = true;
+
+	rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name",
+					&ctrl->name);
+	if (rc) {
+		cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_map_fuse_base(ctrl, pdev);
+	if (rc) {
+		cpr3_err(ctrl, "could not map fuse base address\n");
+		return rc;
+	}
+
+	rc = cpr3_allocate_threads(ctrl, 0, 0);
+	if (rc) {
+		cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->thread_count != 1) {
+		cpr3_err(ctrl, "expected 1 thread but found %d\n",
+			ctrl->thread_count);
+		return -EINVAL;
+	} else if (ctrl->thread[0].vreg_count != 1) {
+		cpr3_err(ctrl, "expected 1 regulator but found %d\n",
+			ctrl->thread[0].vreg_count);
+		return -EINVAL;
+	}
+
+	rc = cpr4_mmss_init_controller(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = cpr4_mmss_init_thread(&ctrl->thread[0]);
+	if (rc) {
+		cpr3_err(&ctrl->thread[0].vreg[0], "thread initialization failed, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_mem_acc_init(&ctrl->thread[0].vreg[0]);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize mem-acc configuration, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	platform_set_drvdata(pdev, ctrl);
+
+	return cpr3_regulator_register(pdev, ctrl);
+}
+
+static int cpr4_mmss_regulator_remove(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_unregister(ctrl);
+}
+
+static int cpr4_mmss_regulator_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_suspend(ctrl);
+}
+
+static int cpr4_mmss_regulator_resume(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_resume(ctrl);
+}
+
+/* Data corresponds to the SoC revision */
+static const struct of_device_id cpr4_mmss_regulator_match_table[] = {
+	{
+		.compatible = "qcom,cpr4-sdm660-mmss-ldo-regulator",
+		.data = (void *)NULL,
+	},
+	{ },
+};
+
+static struct platform_driver cpr4_mmss_regulator_driver = {
+	.driver		= {
+		.name		= "qcom,cpr4-mmss-ldo-regulator",
+		.of_match_table	= cpr4_mmss_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= cpr4_mmss_regulator_probe,
+	.remove		= cpr4_mmss_regulator_remove,
+	.suspend	= cpr4_mmss_regulator_suspend,
+	.resume		= cpr4_mmss_regulator_resume,
+};
+
+static int cpr_regulator_init(void)
+{
+	return platform_driver_register(&cpr4_mmss_regulator_driver);
+}
+
+static void cpr_regulator_exit(void)
+{
+	platform_driver_unregister(&cpr4_mmss_regulator_driver);
+}
+
+MODULE_DESCRIPTION("CPR4 MMSS LDO regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(cpr_regulator_init);
+module_exit(cpr_regulator_exit);
diff --git a/drivers/regulator/cprh-kbss-regulator.c b/drivers/regulator/cprh-kbss-regulator.c
new file mode 100644
index 0000000..f21800e
--- /dev/null
+++ b/drivers/regulator/cprh-kbss-regulator.c
@@ -0,0 +1,2156 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_opp.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include "cpr3-regulator.h"
+
+#define MSM8998_KBSS_FUSE_CORNERS	4
+#define SDM660_KBSS_FUSE_CORNERS	5
+
+/**
+ * struct cprh_kbss_fuses - KBSS specific fuse data
+ * @ro_sel:		Ring oscillator select fuse parameter value for each
+ *			fuse corner
+ * @init_voltage:	Initial (i.e. open-loop) voltage fuse parameter value
+ *			for each fuse corner (raw, not converted to a voltage)
+ * @target_quot:	CPR target quotient fuse parameter value for each fuse
+ *			corner
+ * @quot_offset:	CPR target quotient offset fuse parameter value for each
+ *			fuse corner (raw, not unpacked) used for target quotient
+ *			interpolation
+ * @speed_bin:		Application processor speed bin fuse parameter value for
+ *			the given chip
+ * @cpr_fusing_rev:	CPR fusing revision fuse parameter value
+ * @force_highest_corner:	Flag indicating that all corners must operate
+ *			at the voltage of the highest corner.  This is
+ *			applicable to MSM8998 only.
+ * @aging_init_quot_diff:	Initial quotient difference between CPR aging
+ *			min and max sensors measured at time of manufacturing
+ *
+ * This struct holds the values for all of the fuses read from memory.
+ */
+struct cprh_kbss_fuses {
+	u64	*ro_sel;
+	u64	*init_voltage;
+	u64	*target_quot;
+	u64	*quot_offset;
+	u64	speed_bin;
+	u64	cpr_fusing_rev;
+	u64	force_highest_corner;
+	u64	aging_init_quot_diff;
+};
+
+/*
+ * Fuse combos 0 - 7 map to CPR fusing revision 0 - 7 with speed bin fuse = 0.
+ * Fuse combos 8 - 15 map to CPR fusing revision 0 - 7 with speed bin fuse = 1.
+ * Fuse combos 16 - 23 map to CPR fusing revision 0 - 7 with speed bin fuse = 2.
+ * Fuse combos 24 - 31 map to CPR fusing revision 0 - 7 with speed bin fuse = 3.
+ */
+#define CPRH_MSM8998_KBSS_FUSE_COMBO_COUNT	32
+#define CPRH_SDM660_KBSS_FUSE_COMBO_COUNT	16
+
+/*
+ * Constants which define the name of each fuse corner.
+ */
+enum cprh_msm8998_kbss_fuse_corner {
+	CPRH_MSM8998_KBSS_FUSE_CORNER_LOWSVS		= 0,
+	CPRH_MSM8998_KBSS_FUSE_CORNER_SVS		= 1,
+	CPRH_MSM8998_KBSS_FUSE_CORNER_NOM		= 2,
+	CPRH_MSM8998_KBSS_FUSE_CORNER_TURBO_L1	= 3,
+};
+
+static const char * const cprh_msm8998_kbss_fuse_corner_name[] = {
+	[CPRH_MSM8998_KBSS_FUSE_CORNER_LOWSVS]	= "LowSVS",
+	[CPRH_MSM8998_KBSS_FUSE_CORNER_SVS]		= "SVS",
+	[CPRH_MSM8998_KBSS_FUSE_CORNER_NOM]		= "NOM",
+	[CPRH_MSM8998_KBSS_FUSE_CORNER_TURBO_L1]	= "TURBO_L1",
+};
+
+enum cprh_sdm660_power_kbss_fuse_corner {
+	CPRH_SDM660_POWER_KBSS_FUSE_CORNER_LOWSVS	= 0,
+	CPRH_SDM660_POWER_KBSS_FUSE_CORNER_SVS		= 1,
+	CPRH_SDM660_POWER_KBSS_FUSE_CORNER_SVSPLUS	= 2,
+	CPRH_SDM660_POWER_KBSS_FUSE_CORNER_NOM		= 3,
+	CPRH_SDM660_POWER_KBSS_FUSE_CORNER_TURBO_L1	= 4,
+};
+
+static const char * const cprh_sdm660_power_kbss_fuse_corner_name[] = {
+	[CPRH_SDM660_POWER_KBSS_FUSE_CORNER_LOWSVS]	= "LowSVS",
+	[CPRH_SDM660_POWER_KBSS_FUSE_CORNER_SVS]	= "SVS",
+	[CPRH_SDM660_POWER_KBSS_FUSE_CORNER_SVSPLUS]	= "SVSPLUS",
+	[CPRH_SDM660_POWER_KBSS_FUSE_CORNER_NOM]	= "NOM",
+	[CPRH_SDM660_POWER_KBSS_FUSE_CORNER_TURBO_L1]	= "TURBO_L1",
+};
+
+enum cprh_sdm660_perf_kbss_fuse_corner {
+	CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVS		= 0,
+	CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVSPLUS	= 1,
+	CPRH_SDM660_PERF_KBSS_FUSE_CORNER_NOM		= 2,
+	CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO		= 3,
+	CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2	= 4,
+};
+
+static const char * const cprh_sdm660_perf_kbss_fuse_corner_name[] = {
+	[CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVS]		= "SVS",
+	[CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVSPLUS]	= "SVSPLUS",
+	[CPRH_SDM660_PERF_KBSS_FUSE_CORNER_NOM]		= "NOM",
+	[CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO]	= "TURBO",
+	[CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2]	= "TURBO_L2",
+};
+
+/* KBSS cluster IDs */
+#define CPRH_KBSS_POWER_CLUSTER_ID 0
+#define CPRH_KBSS_PERFORMANCE_CLUSTER_ID 1
+
+/* KBSS controller IDs */
+#define CPRH_KBSS_MIN_CONTROLLER_ID 0
+#define CPRH_KBSS_MAX_CONTROLLER_ID 1
+
+/*
+ * MSM8998 KBSS fuse parameter locations:
+ *
+ * Structs are organized with the following dimensions:
+ *	Outer:  0 or 1 for power or performance cluster
+ *	Middle: 0 to 3 for fuse corners from lowest to highest corner
+ *	Inner:  large enough to hold the longest set of parameter segments which
+ *		fully defines a fuse parameter, +1 (for NULL termination).
+ *		Each segment corresponds to a contiguous group of bits from a
+ *		single fuse row.  These segments are concatentated together in
+ *		order to form the full fuse parameter value.  The segments for
+ *		a given parameter may correspond to different fuse rows.
+ *
+ */
+static const struct cpr3_fuse_param
+msm8998_kbss_ro_sel_param[2][MSM8998_KBSS_FUSE_CORNERS][2] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{67, 12, 15}, {} },
+		{{67,  8, 11}, {} },
+		{{67,  4,  7}, {} },
+		{{67,  0,  3}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{69, 26, 29}, {} },
+		{{69, 22, 25}, {} },
+		{{69, 18, 21}, {} },
+		{{69, 14, 17}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+sdm660_kbss_ro_sel_param[2][SDM660_KBSS_FUSE_CORNERS][3] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{67, 12, 15}, {} },
+		{{67,  8, 11}, {} },
+		{{65, 56, 59}, {} },
+		{{67,  4,  7}, {} },
+		{{67,  0,  3}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{68, 61, 63}, {69,  0,  0} },
+		{{69,  1,  4}, {} },
+		{{68, 57, 60}, {} },
+		{{68, 53, 56}, {} },
+		{{66, 14, 17}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8998_kbss_init_voltage_param[2][MSM8998_KBSS_FUSE_CORNERS][2] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{67, 34, 39}, {} },
+		{{67, 28, 33}, {} },
+		{{67, 22, 27}, {} },
+		{{67, 16, 21}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{69, 48, 53}, {} },
+		{{69, 42, 47}, {} },
+		{{69, 36, 41}, {} },
+		{{69, 30, 35}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+sdm660_kbss_init_voltage_param[2][SDM660_KBSS_FUSE_CORNERS][2] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{67, 34, 39}, {} },
+		{{67, 28, 33}, {} },
+		{{71,  3,  8}, {} },
+		{{67, 22, 27}, {} },
+		{{67, 16, 21}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{69, 17, 22}, {} },
+		{{69, 23, 28}, {} },
+		{{69, 11, 16}, {} },
+		{{69,  5, 10}, {} },
+		{{70, 42, 47}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8998_kbss_target_quot_param[2][MSM8998_KBSS_FUSE_CORNERS][3] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{68, 18, 29}, {} },
+		{{68,  6, 17}, {} },
+		{{67, 58, 63}, {68,  0,  5} },
+		{{67, 46, 57}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{70, 32, 43}, {} },
+		{{70, 20, 31}, {} },
+		{{70,  8, 19}, {} },
+		{{69, 60, 63}, {70,  0,  7}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+sdm660_kbss_target_quot_param[2][SDM660_KBSS_FUSE_CORNERS][3] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{68, 12, 23}, {} },
+		{{68,  0, 11}, {} },
+		{{71,  9, 20}, {} },
+		{{67, 52, 63}, {} },
+		{{67, 40, 51}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{69, 53, 63}, {70,  0,  0}, {} },
+		{{70,  1, 12}, {} },
+		{{69, 41, 52}, {} },
+		{{69, 29, 40}, {} },
+		{{70, 48, 59}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+msm8998_kbss_quot_offset_param[2][MSM8998_KBSS_FUSE_CORNERS][3] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{} },
+		{{68, 63, 63}, {69, 0, 5}, {} },
+		{{68, 56, 62}, {} },
+		{{68, 49, 55}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{} },
+		{{71, 13, 15}, {71, 21, 24}, {} },
+		{{71,  6, 12}, {} },
+		{{70, 63, 63}, {71,  0,  5}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param
+sdm660_kbss_quot_offset_param[2][SDM660_KBSS_FUSE_CORNERS][3] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{{} },
+		{{68, 38, 44}, {} },
+		{{71, 21, 27}, {} },
+		{{68, 31, 37}, {} },
+		{{68, 24, 30}, {} },
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{{} },
+		{{70, 27, 33}, {} },
+		{{70, 20, 26}, {} },
+		{{70, 13, 19}, {} },
+		{{70, 60, 63}, {71,  0,  2}, {} },
+	},
+};
+
+static const struct cpr3_fuse_param msm8998_cpr_fusing_rev_param[] = {
+	{39, 51, 53},
+	{},
+};
+
+static const struct cpr3_fuse_param sdm660_cpr_fusing_rev_param[] = {
+	{71, 28, 30},
+	{},
+};
+
+static const struct cpr3_fuse_param kbss_speed_bin_param[] = {
+	{38, 29, 31},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8998_cpr_force_highest_corner_param[] = {
+	{100, 45, 45},
+	{},
+};
+
+static const struct cpr3_fuse_param
+msm8998_kbss_aging_init_quot_diff_param[2][2] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{69, 6, 13},
+		{},
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{71, 25, 32},
+		{},
+	},
+};
+
+static const struct cpr3_fuse_param
+sdm660_kbss_aging_init_quot_diff_param[2][2] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		{68, 45, 52},
+		{},
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		{70, 34, 41},
+		{},
+	},
+};
+
+/*
+ * Open loop voltage fuse reference voltages in microvolts for MSM8998 v1
+ */
+static const int
+msm8998_v1_kbss_fuse_ref_volt[MSM8998_KBSS_FUSE_CORNERS] = {
+	696000,
+	768000,
+	896000,
+	1112000,
+};
+
+/*
+ * Open loop voltage fuse reference voltages in microvolts for MSM8998 v2
+ */
+static const int
+msm8998_v2_kbss_fuse_ref_volt[2][MSM8998_KBSS_FUSE_CORNERS] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		688000,
+		756000,
+		828000,
+		1056000,
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		756000,
+		756000,
+		828000,
+		1056000,
+	},
+};
+
+/*
+ * Open loop voltage fuse reference voltages in microvolts for SDM660
+ */
+static const int
+sdm660_kbss_fuse_ref_volt[2][SDM660_KBSS_FUSE_CORNERS] = {
+	[CPRH_KBSS_POWER_CLUSTER_ID] = {
+		644000,
+		724000,
+		788000,
+		868000,
+		1068000,
+	},
+	[CPRH_KBSS_PERFORMANCE_CLUSTER_ID] = {
+		724000,
+		788000,
+		868000,
+		988000,
+		1068000,
+	},
+};
+
+#define CPRH_KBSS_FUSE_STEP_VOLT		10000
+#define CPRH_KBSS_VOLTAGE_FUSE_SIZE		6
+#define CPRH_KBSS_QUOT_OFFSET_SCALE		5
+#define CPRH_KBSS_AGING_INIT_QUOT_DIFF_SIZE	8
+#define CPRH_KBSS_AGING_INIT_QUOT_DIFF_SCALE	1
+
+#define CPRH_KBSS_CPR_CLOCK_RATE		19200000
+
+#define CPRH_KBSS_MAX_CORNER_BAND_COUNT		4
+#define CPRH_KBSS_MAX_CORNER_COUNT		40
+
+#define CPRH_KBSS_CPR_SDELTA_CORE_COUNT		4
+
+#define CPRH_KBSS_MAX_TEMP_POINTS		3
+
+/*
+ * msm8998 configuration
+ */
+#define MSM8998_KBSS_POWER_CPR_SENSOR_COUNT		6
+#define MSM8998_KBSS_PERFORMANCE_CPR_SENSOR_COUNT	9
+
+#define MSM8998_KBSS_POWER_TEMP_SENSOR_ID_START		1
+#define MSM8998_KBSS_POWER_TEMP_SENSOR_ID_END		5
+#define MSM8998_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START	6
+#define MSM8998_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END	10
+
+#define MSM8998_KBSS_POWER_AGING_SENSOR_ID		0
+#define MSM8998_KBSS_POWER_AGING_BYPASS_MASK0		0
+
+#define MSM8998_KBSS_PERFORMANCE_AGING_SENSOR_ID	0
+#define MSM8998_KBSS_PERFORMANCE_AGING_BYPASS_MASK0	0
+
+/*
+ * sdm660 configuration
+ */
+#define SDM660_KBSS_POWER_CPR_SENSOR_COUNT		6
+#define SDM660_KBSS_PERFORMANCE_CPR_SENSOR_COUNT	9
+
+#define SDM660_KBSS_POWER_TEMP_SENSOR_ID_START		10
+#define SDM660_KBSS_POWER_TEMP_SENSOR_ID_END		11
+#define SDM660_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START	4
+#define SDM660_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END	9
+
+#define SDM660_KBSS_POWER_AGING_SENSOR_ID		0
+#define SDM660_KBSS_POWER_AGING_BYPASS_MASK0		0
+
+#define SDM660_KBSS_PERFORMANCE_AGING_SENSOR_ID		0
+#define SDM660_KBSS_PERFORMANCE_AGING_BYPASS_MASK0	0
+
+/*
+ * SOC IDs
+ */
+enum soc_id {
+	MSM8998_V1_SOC_ID = 1,
+	MSM8998_V2_SOC_ID = 2,
+	SDM660_SOC_ID     = 3,
+};
+
+/**
+ * cprh_msm8998_kbss_read_fuse_data() - load msm8998 KBSS specific fuse
+ *		parameter values
+ * @vreg:		Pointer to the CPR3 regulator
+ * @fuse:		KBSS specific fuse data
+ *
+ * This function fills cprh_kbss_fuses struct with values read out of hardware
+ * fuses.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_msm8998_kbss_read_fuse_data(struct cpr3_regulator *vreg,
+		struct cprh_kbss_fuses *fuse)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	int i, id, rc;
+
+	rc = cpr3_read_fuse_param(base, msm8998_cpr_fusing_rev_param,
+				&fuse->cpr_fusing_rev);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
+
+	id = vreg->thread->ctrl->ctrl_id;
+	for (i = 0; i < MSM8998_KBSS_FUSE_CORNERS; i++) {
+		rc = cpr3_read_fuse_param(base,
+				msm8998_kbss_init_voltage_param[id][i],
+				&fuse->init_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				msm8998_kbss_target_quot_param[id][i],
+				&fuse->target_quot[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				msm8998_kbss_ro_sel_param[id][i],
+				&fuse->ro_sel[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				msm8998_kbss_quot_offset_param[id][i],
+				&fuse->quot_offset[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+	}
+
+	rc = cpr3_read_fuse_param(base,
+				msm8998_kbss_aging_init_quot_diff_param[id],
+				&fuse->aging_init_quot_diff);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_read_fuse_param(base,
+			  msm8998_cpr_force_highest_corner_param,
+			  &fuse->force_highest_corner);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR force highest corner fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (fuse->force_highest_corner)
+		cpr3_info(vreg, "Fusing requires all operation at the highest corner\n");
+
+	vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
+	if (vreg->fuse_combo >= CPRH_MSM8998_KBSS_FUSE_COMBO_COUNT) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
+			vreg->fuse_combo);
+		return -EINVAL;
+	}
+
+	return rc;
+};
+
+/**
+ * cprh_sdm660_kbss_read_fuse_data() - load SDM660 KBSS specific fuse parameter
+ *		values
+ * @vreg:		Pointer to the CPR3 regulator
+ * @fuse:		KBSS specific fuse data
+ *
+ * This function fills cprh_kbss_fuses struct with values read out of hardware
+ * fuses.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_sdm660_kbss_read_fuse_data(struct cpr3_regulator *vreg,
+		struct cprh_kbss_fuses *fuse)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	int i, id, rc;
+
+	rc = cpr3_read_fuse_param(base, sdm660_cpr_fusing_rev_param,
+				&fuse->cpr_fusing_rev);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read CPR fusing revision fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+	cpr3_info(vreg, "CPR fusing revision = %llu\n", fuse->cpr_fusing_rev);
+
+	id = vreg->thread->ctrl->ctrl_id;
+	for (i = 0; i < SDM660_KBSS_FUSE_CORNERS; i++) {
+		rc = cpr3_read_fuse_param(base,
+				sdm660_kbss_init_voltage_param[id][i],
+				&fuse->init_voltage[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d initial voltage fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				sdm660_kbss_target_quot_param[id][i],
+				&fuse->target_quot[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d target quotient fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				sdm660_kbss_ro_sel_param[id][i],
+				&fuse->ro_sel[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d RO select fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+
+		rc = cpr3_read_fuse_param(base,
+				sdm660_kbss_quot_offset_param[id][i],
+				&fuse->quot_offset[i]);
+		if (rc) {
+			cpr3_err(vreg, "Unable to read fuse-corner %d quotient offset fuse, rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	rc = cpr3_read_fuse_param(base,
+				sdm660_kbss_aging_init_quot_diff_param[id],
+				&fuse->aging_init_quot_diff);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read aging initial quotient difference fuse, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	vreg->fuse_combo = fuse->cpr_fusing_rev + 8 * fuse->speed_bin;
+	if (vreg->fuse_combo >= CPRH_SDM660_KBSS_FUSE_COMBO_COUNT) {
+		cpr3_err(vreg, "invalid CPR fuse combo = %d found\n",
+			vreg->fuse_combo);
+		return -EINVAL;
+	}
+
+	return rc;
+};
+
+/**
+ * cprh_kbss_read_fuse_data() - load KBSS specific fuse parameter values
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * This function allocates a cprh_kbss_fuses struct, fills it with values
+ * read out of hardware fuses, and finally copies common fuse values
+ * into the CPR3 regulator struct.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_read_fuse_data(struct cpr3_regulator *vreg)
+{
+	void __iomem *base = vreg->thread->ctrl->fuse_base;
+	struct cprh_kbss_fuses *fuse;
+	int rc, fuse_corners;
+	enum soc_id soc_revision;
+
+	fuse = devm_kzalloc(vreg->thread->ctrl->dev, sizeof(*fuse), GFP_KERNEL);
+	if (!fuse)
+		return -ENOMEM;
+
+	soc_revision = vreg->thread->ctrl->soc_revision;
+	switch (soc_revision) {
+	case SDM660_SOC_ID:
+		fuse_corners = SDM660_KBSS_FUSE_CORNERS;
+		break;
+	case MSM8998_V1_SOC_ID:
+	case MSM8998_V2_SOC_ID:
+		fuse_corners = MSM8998_KBSS_FUSE_CORNERS;
+		break;
+	default:
+		cpr3_err(vreg, "unsupported soc id = %d\n", soc_revision);
+		return -EINVAL;
+	}
+
+	fuse->ro_sel = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
+			sizeof(*fuse->ro_sel), GFP_KERNEL);
+	fuse->init_voltage = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
+			sizeof(*fuse->init_voltage), GFP_KERNEL);
+	fuse->target_quot = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
+			sizeof(*fuse->target_quot), GFP_KERNEL);
+	fuse->quot_offset = devm_kcalloc(vreg->thread->ctrl->dev, fuse_corners,
+			sizeof(*fuse->quot_offset), GFP_KERNEL);
+
+	if (!fuse->ro_sel || !fuse->init_voltage || !fuse->target_quot
+			|| !fuse->quot_offset)
+		return -ENOMEM;
+
+	rc = cpr3_read_fuse_param(base, kbss_speed_bin_param, &fuse->speed_bin);
+	if (rc) {
+		cpr3_err(vreg, "Unable to read speed bin fuse, rc=%d\n", rc);
+		return rc;
+	}
+	cpr3_info(vreg, "speed bin = %llu\n", fuse->speed_bin);
+
+	switch (soc_revision) {
+	case SDM660_SOC_ID:
+		rc = cprh_sdm660_kbss_read_fuse_data(vreg, fuse);
+		if (rc) {
+			cpr3_err(vreg, "sdm660 kbss fuse data read failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+		break;
+	case MSM8998_V1_SOC_ID:
+	case MSM8998_V2_SOC_ID:
+		rc = cprh_msm8998_kbss_read_fuse_data(vreg, fuse);
+		if (rc) {
+			cpr3_err(vreg, "msm8998 kbss fuse data read failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+		break;
+	default:
+		cpr3_err(vreg, "unsupported soc id = %d\n", soc_revision);
+		return -EINVAL;
+	}
+
+	vreg->speed_bin_fuse	= fuse->speed_bin;
+	vreg->cpr_rev_fuse	= fuse->cpr_fusing_rev;
+	vreg->fuse_corner_count	= fuse_corners;
+	vreg->platform_fuses	= fuse;
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_parse_corner_data() - parse KBSS corner data from device tree
+ *		properties of the CPR3 regulator's device node
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_parse_corner_data(struct cpr3_regulator *vreg)
+{
+	int rc;
+
+	rc = cpr3_parse_common_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "error reading corner data, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * A total of CPRH_KBSS_MAX_CORNER_COUNT - 1 corners
+	 * may be specified in device tree as an additional corner
+	 * must be allocated to correspond to the APM crossover voltage.
+	 */
+	if (vreg->corner_count > CPRH_KBSS_MAX_CORNER_COUNT - 1) {
+		cpr3_err(vreg, "corner count %d exceeds supported maximum %d\n",
+		 vreg->corner_count, CPRH_KBSS_MAX_CORNER_COUNT - 1);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+/**
+ * cprh_kbss_calculate_open_loop_voltages() - calculate the open-loop
+ *		voltage for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If open-loop voltage interpolation is allowed in device tree, then this
+ * function calculates the open-loop voltage for a given corner using linear
+ * interpolation.  This interpolation is performed using the processor
+ * frequencies of the lower and higher Fmax corners along with their fused
+ * open-loop voltages.
+ *
+ * If open-loop voltage interpolation is not allowed, then this function uses
+ * the Fmax fused open-loop voltage for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_calculate_open_loop_voltages(struct cpr3_regulator *vreg)
+{
+	struct device_node *node = vreg->of_node;
+	struct cprh_kbss_fuses *fuse = vreg->platform_fuses;
+	int i, j, id, rc = 0;
+	bool allow_interpolation;
+	u64 freq_low, volt_low, freq_high, volt_high;
+	const int *ref_volt;
+	int *fuse_volt;
+	int *fmax_corner;
+	const char * const *corner_name;
+	enum soc_id soc_revision;
+
+	fuse_volt = kcalloc(vreg->fuse_corner_count, sizeof(*fuse_volt),
+				GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+				GFP_KERNEL);
+	if (!fuse_volt || !fmax_corner) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	id = vreg->thread->ctrl->ctrl_id;
+	soc_revision = vreg->thread->ctrl->soc_revision;
+
+	switch (soc_revision) {
+	case SDM660_SOC_ID:
+		ref_volt = sdm660_kbss_fuse_ref_volt[id];
+		if (id == CPRH_KBSS_POWER_CLUSTER_ID)
+			corner_name = cprh_sdm660_power_kbss_fuse_corner_name;
+		else
+			corner_name = cprh_sdm660_perf_kbss_fuse_corner_name;
+		break;
+	case MSM8998_V1_SOC_ID:
+		ref_volt = msm8998_v1_kbss_fuse_ref_volt;
+		corner_name = cprh_msm8998_kbss_fuse_corner_name;
+		break;
+	case MSM8998_V2_SOC_ID:
+		ref_volt = msm8998_v2_kbss_fuse_ref_volt[id];
+		corner_name = cprh_msm8998_kbss_fuse_corner_name;
+		break;
+	default:
+		cpr3_err(vreg, "unsupported soc id = %d\n", soc_revision);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	for (i = 0; i < vreg->fuse_corner_count; i++) {
+		fuse_volt[i] = cpr3_convert_open_loop_voltage_fuse(ref_volt[i],
+			CPRH_KBSS_FUSE_STEP_VOLT, fuse->init_voltage[i],
+			CPRH_KBSS_VOLTAGE_FUSE_SIZE);
+
+		/* Log fused open-loop voltage values for debugging purposes. */
+		cpr3_info(vreg, "fused %8s: open-loop=%7d uV\n", corner_name[i],
+			  fuse_volt[i]);
+	}
+
+	rc = cpr3_adjust_fused_open_loop_voltages(vreg, fuse_volt);
+	if (rc) {
+		cpr3_err(vreg, "fused open-loop voltage adjustment failed, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	allow_interpolation = of_property_read_bool(node,
+				"qcom,allow-voltage-interpolation");
+
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		if (fuse_volt[i] < fuse_volt[i - 1]) {
+			cpr3_info(vreg, "fuse corner %d voltage=%d uV < fuse corner %d voltage=%d uV; overriding: fuse corner %d voltage=%d\n",
+				i, fuse_volt[i], i - 1, fuse_volt[i - 1],
+				i, fuse_volt[i - 1]);
+			fuse_volt[i] = fuse_volt[i - 1];
+		}
+	}
+
+	if (!allow_interpolation) {
+		/* Use fused open-loop voltage for lower frequencies. */
+		for (i = 0; i < vreg->corner_count; i++)
+			vreg->corner[i].open_loop_volt
+				= fuse_volt[vreg->corner[i].cpr_fuse_corner];
+		goto done;
+	}
+
+	/* Determine highest corner mapped to each fuse corner */
+	j = vreg->fuse_corner_count - 1;
+	for (i = vreg->corner_count - 1; i >= 0; i--) {
+		if (vreg->corner[i].cpr_fuse_corner == j) {
+			fmax_corner[j] = i;
+			j--;
+		}
+	}
+	if (j >= 0) {
+		cpr3_err(vreg, "invalid fuse corner mapping\n");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	for (i = 0; i <= fmax_corner[0]; i++)
+		vreg->corner[i].open_loop_volt = fuse_volt[0];
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		volt_low = fuse_volt[i - 1];
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+		volt_high = fuse_volt[i];
+
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].open_loop_volt = cpr3_interpolate(
+				freq_low, volt_low, freq_high, volt_high,
+				vreg->corner[j].proc_freq);
+	}
+
+done:
+	if (rc == 0) {
+		cpr3_debug(vreg, "unadjusted per-corner open-loop voltages:\n");
+		for (i = 0; i < vreg->corner_count; i++)
+			cpr3_debug(vreg, "open-loop[%2d] = %d uV\n", i,
+				vreg->corner[i].open_loop_volt);
+
+		rc = cpr3_adjust_open_loop_voltages(vreg);
+		if (rc)
+			cpr3_err(vreg, "open-loop voltage adjustment failed, rc=%d\n",
+				rc);
+	}
+
+	kfree(fuse_volt);
+	kfree(fmax_corner);
+	return rc;
+}
+
+/**
+ * cprh_msm8998_partial_binning_override() - override the voltage and quotient
+ *		settings for low corners based upon special partial binning
+ *		fuse values
+ *
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Some parts are not able to operate at low voltages.  The force highest
+ * corner fuse specifies if a given part must operate with voltages
+ * corresponding to the highest corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_msm8998_partial_binning_override(struct cpr3_regulator *vreg)
+{
+	struct cprh_kbss_fuses *fuse = vreg->platform_fuses;
+	struct cpr3_corner *corner;
+	struct cpr4_sdelta *sdelta;
+	int i;
+	u32 proc_freq;
+
+	if (fuse->force_highest_corner) {
+		cpr3_info(vreg, "overriding CPR parameters for corners 0 to %d with quotients and voltages of corner %d\n",
+			  vreg->corner_count - 2, vreg->corner_count - 1);
+		corner = &vreg->corner[vreg->corner_count - 1];
+		for (i = 0; i < vreg->corner_count - 1; i++) {
+			proc_freq = vreg->corner[i].proc_freq;
+			sdelta = vreg->corner[i].sdelta;
+			if (sdelta) {
+				if (sdelta->table)
+					devm_kfree(vreg->thread->ctrl->dev,
+						   sdelta->table);
+				if (sdelta->boost_table)
+					devm_kfree(vreg->thread->ctrl->dev,
+						   sdelta->boost_table);
+				devm_kfree(vreg->thread->ctrl->dev,
+					   sdelta);
+			}
+			vreg->corner[i] = *corner;
+			vreg->corner[i].proc_freq = proc_freq;
+		}
+
+		return 0;
+	}
+
+	return 0;
+};
+
+/**
+ * cprh_kbss_parse_core_count_temp_adj_properties() - load device tree
+ *		properties associated with per-corner-band and temperature
+ *		voltage adjustments.
+ * @vreg:	Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_parse_core_count_temp_adj_properties(
+		struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct device_node *node = vreg->of_node;
+	u32 *temp, *combo_corner_bands, *speed_bin_corner_bands;
+	int rc, i, len, temp_point_count;
+
+	vreg->allow_core_count_adj = of_find_property(node,
+					"qcom,corner-band-allow-core-count-adjustment",
+					NULL);
+	vreg->allow_temp_adj = of_find_property(node,
+					"qcom,corner-band-allow-temp-adjustment",
+					NULL);
+
+	if (!vreg->allow_core_count_adj && !vreg->allow_temp_adj)
+		return 0;
+
+	combo_corner_bands = kcalloc(vreg->fuse_combos_supported,
+				     sizeof(*combo_corner_bands),
+				     GFP_KERNEL);
+	if (!combo_corner_bands)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(node, "qcom,cpr-corner-bands",
+					combo_corner_bands,
+					vreg->fuse_combos_supported);
+	if (rc == -EOVERFLOW) {
+		/* Single value case */
+		rc = of_property_read_u32(node, "qcom,cpr-corner-bands",
+					  combo_corner_bands);
+		for (i = 1; i < vreg->fuse_combos_supported; i++)
+			combo_corner_bands[i] = combo_corner_bands[0];
+	}
+	if (rc) {
+		cpr3_err(vreg, "error reading property qcom,cpr-corner-bands, rc=%d\n",
+			rc);
+		kfree(combo_corner_bands);
+		return rc;
+	}
+
+	vreg->fuse_combo_corner_band_offset = 0;
+	vreg->fuse_combo_corner_band_sum = 0;
+	for (i = 0; i < vreg->fuse_combos_supported; i++) {
+		vreg->fuse_combo_corner_band_sum += combo_corner_bands[i];
+		if (i < vreg->fuse_combo)
+			vreg->fuse_combo_corner_band_offset +=
+				combo_corner_bands[i];
+	}
+
+	vreg->corner_band_count = combo_corner_bands[vreg->fuse_combo];
+
+	kfree(combo_corner_bands);
+
+	if (vreg->corner_band_count <= 0 ||
+	    vreg->corner_band_count > CPRH_KBSS_MAX_CORNER_BAND_COUNT ||
+	    vreg->corner_band_count > vreg->corner_count) {
+		cpr3_err(vreg, "invalid corner band count %d > %d (max) for %d corners\n",
+			 vreg->corner_band_count,
+			 CPRH_KBSS_MAX_CORNER_BAND_COUNT,
+			 vreg->corner_count);
+		return -EINVAL;
+	}
+
+	vreg->speed_bin_corner_band_offset = 0;
+	vreg->speed_bin_corner_band_sum = 0;
+	if (vreg->speed_bins_supported > 0) {
+		speed_bin_corner_bands = kcalloc(vreg->speed_bins_supported,
+					sizeof(*speed_bin_corner_bands),
+					GFP_KERNEL);
+		if (!speed_bin_corner_bands)
+			return -ENOMEM;
+
+		rc = of_property_read_u32_array(node,
+						"qcom,cpr-speed-bin-corner-bands",
+						speed_bin_corner_bands,
+						vreg->speed_bins_supported);
+		if (rc) {
+			cpr3_err(vreg, "error reading property qcom,cpr-speed-bin-corner-bands, rc=%d\n",
+				 rc);
+			kfree(speed_bin_corner_bands);
+			return rc;
+		}
+
+		for (i = 0; i < vreg->speed_bins_supported; i++) {
+			vreg->speed_bin_corner_band_sum +=
+				speed_bin_corner_bands[i];
+			if (i < vreg->speed_bin_fuse)
+				vreg->speed_bin_corner_band_offset +=
+					speed_bin_corner_bands[i];
+		}
+
+		if (speed_bin_corner_bands[vreg->speed_bin_fuse]
+		    != vreg->corner_band_count) {
+			cpr3_err(vreg, "qcom,cpr-corner-bands and qcom,cpr-speed-bin-corner-bands conflict on number of corners bands: %d vs %u\n",
+				vreg->corner_band_count,
+				speed_bin_corner_bands[vreg->speed_bin_fuse]);
+			kfree(speed_bin_corner_bands);
+			return -EINVAL;
+		}
+
+		kfree(speed_bin_corner_bands);
+	}
+
+	vreg->corner_band = devm_kcalloc(ctrl->dev,
+					 vreg->corner_band_count,
+					 sizeof(*vreg->corner_band),
+					 GFP_KERNEL);
+
+	temp = kcalloc(vreg->corner_band_count, sizeof(*temp), GFP_KERNEL);
+
+	if (!vreg->corner_band || !temp) {
+		rc = -ENOMEM;
+		goto free_temp;
+	}
+
+	rc = cpr3_parse_corner_band_array_property(vreg,
+						   "qcom,cpr-corner-band-map",
+						   1, temp);
+	if (rc) {
+		cpr3_err(vreg, "could not load corner band map, rc=%d\n",
+			 rc);
+		goto free_temp;
+	}
+
+	for (i = 1; i < vreg->corner_band_count; i++) {
+		if (temp[i - 1] > temp[i]) {
+			cpr3_err(vreg, "invalid corner band mapping: band %d corner %d, band %d corner %d\n",
+				 i - 1, temp[i - 1],
+				 i, temp[i]);
+			rc = -EINVAL;
+			goto free_temp;
+		}
+	}
+
+	for (i = 0; i < vreg->corner_band_count; i++)
+		vreg->corner_band[i].corner = temp[i] - CPR3_CORNER_OFFSET;
+
+	if (!of_find_property(ctrl->dev->of_node,
+			      "qcom,cpr-temp-point-map", &len)) {
+		/*
+		 * Temperature based adjustments are not defined. Single
+		 * temperature band is still valid for per-online-core
+		 * adjustments.
+		 */
+		ctrl->temp_band_count = 1;
+		rc = 0;
+		goto free_temp;
+	}
+
+	if (!vreg->allow_temp_adj) {
+		rc = 0;
+		goto free_temp;
+	}
+
+	temp_point_count = len / sizeof(u32);
+	if (temp_point_count <= 0 || temp_point_count >
+	    CPRH_KBSS_MAX_TEMP_POINTS) {
+		cpr3_err(ctrl, "invalid number of temperature points %d > %d (max)\n",
+			 temp_point_count, CPRH_KBSS_MAX_TEMP_POINTS);
+		rc = -EINVAL;
+		goto free_temp;
+	}
+
+	ctrl->temp_points = devm_kcalloc(ctrl->dev, temp_point_count,
+					sizeof(*ctrl->temp_points), GFP_KERNEL);
+	if (!ctrl->temp_points) {
+		rc = -ENOMEM;
+		goto free_temp;
+	}
+	rc = of_property_read_u32_array(ctrl->dev->of_node,
+					"qcom,cpr-temp-point-map",
+					ctrl->temp_points, temp_point_count);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,cpr-temp-point-map, rc=%d\n",
+			 rc);
+		goto free_temp;
+	}
+
+	for (i = 0; i < temp_point_count; i++)
+		cpr3_debug(ctrl, "Temperature Point %d=%d\n", i,
+				   ctrl->temp_points[i]);
+
+	/*
+	 * If t1, t2, and t3 are the temperature points, then the temperature
+	 * bands are: (-inf, t1], (t1, t2], (t2, t3], and (t3, inf).
+	 */
+	ctrl->temp_band_count = temp_point_count + 1;
+	cpr3_debug(ctrl, "Number of temp bands=%d\n",
+		   ctrl->temp_band_count);
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,cpr-initial-temp-band",
+				  &ctrl->initial_temp_band);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-initial-temp-band, rc=%d\n",
+			rc);
+		goto free_temp;
+	}
+
+	if (ctrl->initial_temp_band >= ctrl->temp_band_count) {
+		cpr3_err(ctrl, "Initial temperature band value %d should be in range [0 - %d]\n",
+			ctrl->initial_temp_band, ctrl->temp_band_count - 1);
+		rc = -EINVAL;
+		goto free_temp;
+	}
+
+	switch (ctrl->soc_revision) {
+	case SDM660_SOC_ID:
+		ctrl->temp_sensor_id_start = ctrl->ctrl_id ==
+			CPRH_KBSS_POWER_CLUSTER_ID
+			? SDM660_KBSS_POWER_TEMP_SENSOR_ID_START :
+			SDM660_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START;
+		ctrl->temp_sensor_id_end = ctrl->ctrl_id ==
+			CPRH_KBSS_POWER_CLUSTER_ID
+			? SDM660_KBSS_POWER_TEMP_SENSOR_ID_END :
+			SDM660_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END;
+		break;
+	case MSM8998_V1_SOC_ID:
+	case MSM8998_V2_SOC_ID:
+		ctrl->temp_sensor_id_start = ctrl->ctrl_id ==
+			CPRH_KBSS_POWER_CLUSTER_ID
+			? MSM8998_KBSS_POWER_TEMP_SENSOR_ID_START :
+			MSM8998_KBSS_PERFORMANCE_TEMP_SENSOR_ID_START;
+		ctrl->temp_sensor_id_end = ctrl->ctrl_id ==
+			CPRH_KBSS_POWER_CLUSTER_ID
+			? MSM8998_KBSS_POWER_TEMP_SENSOR_ID_END :
+			MSM8998_KBSS_PERFORMANCE_TEMP_SENSOR_ID_END;
+		break;
+	default:
+		cpr3_err(ctrl, "unsupported soc id = %d\n", ctrl->soc_revision);
+		rc = -EINVAL;
+		goto free_temp;
+	}
+	ctrl->allow_temp_adj = true;
+
+free_temp:
+	kfree(temp);
+
+	return rc;
+}
+
+/**
+ * cprh_kbss_apm_crossover_as_corner() - introduce a corner whose floor,
+ *		open-loop, and ceiling voltages correspond to the APM
+ *		crossover voltage.
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * The APM corner is utilized as a crossover corner by OSM and CPRh
+ * hardware to set the VDD supply voltage during the APM switch
+ * routine.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_apm_crossover_as_corner(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr3_corner *corner;
+
+	if (!ctrl->apm_crossover_volt) {
+		/* APM voltage crossover corner not required. */
+		return 0;
+	}
+
+	corner = &vreg->corner[vreg->corner_count];
+	/*
+	 * 0 MHz indicates this corner is not to be
+	 * used as active DCVS set point.
+	 */
+	corner->proc_freq = 0;
+	corner->floor_volt = ctrl->apm_crossover_volt;
+	corner->ceiling_volt = ctrl->apm_crossover_volt;
+	corner->open_loop_volt = ctrl->apm_crossover_volt;
+	corner->abs_ceiling_volt = ctrl->apm_crossover_volt;
+	corner->use_open_loop = true;
+	vreg->corner_count++;
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_mem_acc_crossover_as_corner() - introduce a corner whose floor,
+ *		open-loop, and ceiling voltages correspond to the MEM ACC
+ *		crossover voltage.
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * The MEM ACC corner is utilized as a crossover corner by OSM and CPRh
+ * hardware to set the VDD supply voltage during the MEM ACC switch
+ * routine.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_mem_acc_crossover_as_corner(struct cpr3_regulator *vreg)
+{
+	struct cpr3_controller *ctrl = vreg->thread->ctrl;
+	struct cpr3_corner *corner;
+
+	if (!ctrl->mem_acc_crossover_volt) {
+		/* MEM ACC voltage crossover corner not required. */
+		return 0;
+	}
+
+	corner = &vreg->corner[vreg->corner_count];
+	/*
+	 * 0 MHz indicates this corner is not to be
+	 * used as active DCVS set point.
+	 */
+	corner->proc_freq = 0;
+	corner->floor_volt = ctrl->mem_acc_crossover_volt;
+	corner->ceiling_volt = ctrl->mem_acc_crossover_volt;
+	corner->open_loop_volt = ctrl->mem_acc_crossover_volt;
+	corner->abs_ceiling_volt = ctrl->mem_acc_crossover_volt;
+	corner->use_open_loop = true;
+	vreg->corner_count++;
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_set_no_interpolation_quotients() - use the fused target quotient
+ *		values for lower frequencies.
+ * @vreg:		Pointer to the CPR3 regulator
+ * @volt_adjust:	Pointer to array of per-corner closed-loop adjustment
+ *			voltages
+ * @volt_adjust_fuse:	Pointer to array of per-fuse-corner closed-loop
+ *			adjustment voltages
+ * @ro_scale:		Pointer to array of per-fuse-corner RO scaling factor
+ *			values with units of QUOT/V
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_set_no_interpolation_quotients(struct cpr3_regulator *vreg,
+			int *volt_adjust, int *volt_adjust_fuse, int *ro_scale)
+{
+	struct cprh_kbss_fuses *fuse = vreg->platform_fuses;
+	u32 quot, ro;
+	int quot_adjust;
+	int i, fuse_corner;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		fuse_corner = vreg->corner[i].cpr_fuse_corner;
+		quot = fuse->target_quot[fuse_corner];
+		quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
+					   volt_adjust_fuse[fuse_corner] +
+					   volt_adjust[i]);
+		ro = fuse->ro_sel[fuse_corner];
+		vreg->corner[i].target_quot[ro] = quot + quot_adjust;
+		cpr3_debug(vreg, "corner=%d RO=%u target quot=%u\n",
+			  i, ro, quot);
+
+		if (quot_adjust)
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %u --> %u (%d uV)\n",
+				  i, ro, quot, vreg->corner[i].target_quot[ro],
+				  volt_adjust_fuse[fuse_corner] +
+				  volt_adjust[i]);
+	}
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_calculate_target_quotients() - calculate the CPR target
+ *		quotient for each corner of a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * If target quotient interpolation is allowed in device tree, then this
+ * function calculates the target quotient for a given corner using linear
+ * interpolation.  This interpolation is performed using the processor
+ * frequencies of the lower and higher Fmax corners along with the fused
+ * target quotient and quotient offset of the higher Fmax corner.
+ *
+ * If target quotient interpolation is not allowed, then this function uses
+ * the Fmax fused target quotient for all of the corners associated with a
+ * given fuse corner.
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_calculate_target_quotients(struct cpr3_regulator *vreg)
+{
+	struct cprh_kbss_fuses *fuse = vreg->platform_fuses;
+	int rc;
+	bool allow_interpolation;
+	u64 freq_low, freq_high, prev_quot;
+	u64 *quot_low;
+	u64 *quot_high;
+	u32 quot, ro;
+	int i, j, fuse_corner, quot_adjust;
+	int *fmax_corner;
+	int *volt_adjust, *volt_adjust_fuse, *ro_scale;
+	int lowest_fuse_corner, highest_fuse_corner;
+	const char * const *corner_name;
+
+	switch (vreg->thread->ctrl->soc_revision) {
+	case SDM660_SOC_ID:
+		if (vreg->thread->ctrl->ctrl_id == CPRH_KBSS_POWER_CLUSTER_ID) {
+			corner_name = cprh_sdm660_power_kbss_fuse_corner_name;
+			lowest_fuse_corner =
+				CPRH_SDM660_POWER_KBSS_FUSE_CORNER_LOWSVS;
+			highest_fuse_corner =
+				CPRH_SDM660_POWER_KBSS_FUSE_CORNER_TURBO_L1;
+		} else {
+			corner_name = cprh_sdm660_perf_kbss_fuse_corner_name;
+			lowest_fuse_corner =
+				CPRH_SDM660_PERF_KBSS_FUSE_CORNER_SVS;
+			highest_fuse_corner =
+				CPRH_SDM660_PERF_KBSS_FUSE_CORNER_TURBO_L2;
+		}
+		break;
+	case MSM8998_V1_SOC_ID:
+	case MSM8998_V2_SOC_ID:
+		corner_name = cprh_msm8998_kbss_fuse_corner_name;
+		lowest_fuse_corner =
+			CPRH_MSM8998_KBSS_FUSE_CORNER_LOWSVS;
+		highest_fuse_corner =
+			CPRH_MSM8998_KBSS_FUSE_CORNER_TURBO_L1;
+		break;
+	default:
+		cpr3_err(vreg, "unsupported soc id = %d\n",
+				vreg->thread->ctrl->soc_revision);
+		return -EINVAL;
+	}
+
+	/* Log fused quotient values for debugging purposes. */
+	cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu\n",
+		corner_name[lowest_fuse_corner],
+		fuse->ro_sel[lowest_fuse_corner],
+		fuse->target_quot[lowest_fuse_corner]);
+	for (i = lowest_fuse_corner + 1; i <= highest_fuse_corner; i++)
+		cpr3_info(vreg, "fused %8s: quot[%2llu]=%4llu, quot_offset[%2llu]=%4llu\n",
+			corner_name[i], fuse->ro_sel[i], fuse->target_quot[i],
+			fuse->ro_sel[i], fuse->quot_offset[i] *
+			CPRH_KBSS_QUOT_OFFSET_SCALE);
+
+	allow_interpolation = of_property_read_bool(vreg->of_node,
+					"qcom,allow-quotient-interpolation");
+
+	volt_adjust = kcalloc(vreg->corner_count, sizeof(*volt_adjust),
+					GFP_KERNEL);
+	volt_adjust_fuse = kcalloc(vreg->fuse_corner_count,
+					sizeof(*volt_adjust_fuse), GFP_KERNEL);
+	ro_scale = kcalloc(vreg->fuse_corner_count, sizeof(*ro_scale),
+					GFP_KERNEL);
+	fmax_corner = kcalloc(vreg->fuse_corner_count, sizeof(*fmax_corner),
+					GFP_KERNEL);
+	quot_low = kcalloc(vreg->fuse_corner_count, sizeof(*quot_low),
+					GFP_KERNEL);
+	quot_high = kcalloc(vreg->fuse_corner_count, sizeof(*quot_high),
+					GFP_KERNEL);
+	if (!volt_adjust || !volt_adjust_fuse || !ro_scale ||
+	    !fmax_corner || !quot_low || !quot_high) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	rc = cpr3_parse_closed_loop_voltage_adjustments(vreg, &fuse->ro_sel[0],
+				volt_adjust, volt_adjust_fuse, ro_scale);
+	if (rc) {
+		cpr3_err(vreg, "could not load closed-loop voltage adjustments, rc=%d\n",
+			rc);
+		goto done;
+	}
+
+	if (!allow_interpolation) {
+		/* Use fused target quotients for lower frequencies. */
+		return cprh_kbss_set_no_interpolation_quotients(vreg,
+				volt_adjust, volt_adjust_fuse, ro_scale);
+	}
+
+	/* Determine highest corner mapped to each fuse corner */
+	j = vreg->fuse_corner_count - 1;
+	for (i = vreg->corner_count - 1; i >= 0; i--) {
+		if (vreg->corner[i].cpr_fuse_corner == j) {
+			fmax_corner[j] = i;
+			j--;
+		}
+	}
+	if (j >= 0) {
+		cpr3_err(vreg, "invalid fuse corner mapping\n");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	/*
+	 * Interpolation is not possible for corners mapped to the lowest fuse
+	 * corner so use the fuse corner value directly.
+	 */
+	i = lowest_fuse_corner;
+	quot_adjust = cpr3_quot_adjustment(ro_scale[i], volt_adjust_fuse[i]);
+	quot = fuse->target_quot[i] + quot_adjust;
+	quot_high[i] = quot_low[i] = quot;
+	ro = fuse->ro_sel[i];
+	if (quot_adjust)
+		cpr3_debug(vreg, "adjusted fuse corner %d RO%u target quot: %llu --> %u (%d uV)\n",
+			i, ro, fuse->target_quot[i], quot, volt_adjust_fuse[i]);
+
+	for (i = 0; i <= fmax_corner[lowest_fuse_corner]; i++)
+		vreg->corner[i].target_quot[ro] = quot;
+
+	for (i = lowest_fuse_corner + 1; i < vreg->fuse_corner_count; i++) {
+		quot_high[i] = fuse->target_quot[i];
+		if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
+			quot_low[i] = quot_high[i - 1];
+		else
+			quot_low[i] = quot_high[i]
+					- fuse->quot_offset[i]
+					  * CPRH_KBSS_QUOT_OFFSET_SCALE;
+		if (quot_high[i] < quot_low[i]) {
+			cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu; overriding: quot_high[%d]=%llu\n",
+				i, quot_high[i], i, quot_low[i],
+				i, quot_low[i]);
+			quot_high[i] = quot_low[i];
+		}
+	}
+
+	/* Perform per-fuse-corner target quotient adjustment */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		quot_adjust = cpr3_quot_adjustment(ro_scale[i],
+						   volt_adjust_fuse[i]);
+		if (quot_adjust) {
+			prev_quot = quot_high[i];
+			quot_high[i] += quot_adjust;
+			cpr3_debug(vreg, "adjusted fuse corner %d RO%llu target quot: %llu --> %llu (%d uV)\n",
+				i, fuse->ro_sel[i], prev_quot, quot_high[i],
+				volt_adjust_fuse[i]);
+		}
+
+		if (fuse->ro_sel[i] == fuse->ro_sel[i - 1])
+			quot_low[i] = quot_high[i - 1];
+		else
+			quot_low[i] += cpr3_quot_adjustment(ro_scale[i],
+						    volt_adjust_fuse[i - 1]);
+
+		if (quot_high[i] < quot_low[i]) {
+			cpr3_debug(vreg, "quot_high[%d]=%llu < quot_low[%d]=%llu after adjustment; overriding: quot_high[%d]=%llu\n",
+				i, quot_high[i], i, quot_low[i],
+				i, quot_low[i]);
+			quot_high[i] = quot_low[i];
+		}
+	}
+
+	/* Interpolate voltages for the higher fuse corners. */
+	for (i = 1; i < vreg->fuse_corner_count; i++) {
+		freq_low = vreg->corner[fmax_corner[i - 1]].proc_freq;
+		freq_high = vreg->corner[fmax_corner[i]].proc_freq;
+
+		ro = fuse->ro_sel[i];
+		for (j = fmax_corner[i - 1] + 1; j <= fmax_corner[i]; j++)
+			vreg->corner[j].target_quot[ro] = cpr3_interpolate(
+				freq_low, quot_low[i], freq_high, quot_high[i],
+				vreg->corner[j].proc_freq);
+	}
+
+	/* Perform per-corner target quotient adjustment */
+	for (i = 0; i < vreg->corner_count; i++) {
+		fuse_corner = vreg->corner[i].cpr_fuse_corner;
+		ro = fuse->ro_sel[fuse_corner];
+		quot_adjust = cpr3_quot_adjustment(ro_scale[fuse_corner],
+						   volt_adjust[i]);
+		if (quot_adjust) {
+			prev_quot = vreg->corner[i].target_quot[ro];
+			vreg->corner[i].target_quot[ro] += quot_adjust;
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot: %llu --> %u (%d uV)\n",
+				i, ro, prev_quot,
+				vreg->corner[i].target_quot[ro],
+				volt_adjust[i]);
+		}
+	}
+
+	/* Ensure that target quotients increase monotonically */
+	for (i = 1; i < vreg->corner_count; i++) {
+		ro = fuse->ro_sel[vreg->corner[i].cpr_fuse_corner];
+		if (fuse->ro_sel[vreg->corner[i - 1].cpr_fuse_corner] == ro
+		    && vreg->corner[i].target_quot[ro]
+				< vreg->corner[i - 1].target_quot[ro]) {
+			cpr3_debug(vreg, "adjusted corner %d RO%u target quot=%u < adjusted corner %d RO%u target quot=%u; overriding: corner %d RO%u target quot=%u\n",
+				i, ro, vreg->corner[i].target_quot[ro],
+				i - 1, ro, vreg->corner[i - 1].target_quot[ro],
+				i, ro, vreg->corner[i - 1].target_quot[ro]);
+			vreg->corner[i].target_quot[ro]
+				= vreg->corner[i - 1].target_quot[ro];
+		}
+	}
+
+done:
+	kfree(volt_adjust);
+	kfree(volt_adjust_fuse);
+	kfree(ro_scale);
+	kfree(fmax_corner);
+	kfree(quot_low);
+	kfree(quot_high);
+	return rc;
+}
+
+/**
+ * cprh_kbss_print_settings() - print out KBSS CPR configuration settings into
+ *		the kernel log for debugging purposes
+ * @vreg:		Pointer to the CPR3 regulator
+ */
+static void cprh_kbss_print_settings(struct cpr3_regulator *vreg)
+{
+	struct cpr3_corner *corner;
+	int i;
+
+	cpr3_debug(vreg, "Corner: Frequency (Hz), Fuse Corner, Floor (uV), Open-Loop (uV), Ceiling (uV)\n");
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		cpr3_debug(vreg, "%3d: %10u, %2d, %7d, %7d, %7d\n",
+			i, corner->proc_freq, corner->cpr_fuse_corner,
+			corner->floor_volt, corner->open_loop_volt,
+			corner->ceiling_volt);
+	}
+}
+
+/**
+ * cprh_kbss_init_thread() - perform steps necessary to initialize the
+ *		configuration data for a CPR3 thread
+ * @thread:		Pointer to the CPR3 thread
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_init_thread(struct cpr3_thread *thread)
+{
+	int rc;
+
+	rc = cpr3_parse_common_thread_data(thread);
+	if (rc) {
+		cpr3_err(thread->ctrl, "thread %u unable to read CPR thread data from device tree, rc=%d\n",
+			thread->thread_id, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_init_regulator() - perform all steps necessary to initialize the
+ *		configuration data for a CPR3 regulator
+ * @vreg:		Pointer to the CPR3 regulator
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_init_regulator(struct cpr3_regulator *vreg)
+{
+	struct cprh_kbss_fuses *fuse;
+	int rc;
+
+	rc = cprh_kbss_read_fuse_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR fuse data, rc=%d\n", rc);
+		return rc;
+	}
+
+	fuse = vreg->platform_fuses;
+
+	rc = cprh_kbss_parse_corner_data(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to read CPR corner data from device tree, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_calculate_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cpr3_limit_open_loop_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit open-loop voltages, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cprh_adjust_voltages_for_apm(vreg);
+	cprh_adjust_voltages_for_mem_acc(vreg);
+
+	cpr3_open_loop_voltage_as_ceiling(vreg);
+
+	rc = cpr3_limit_floor_voltages(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to limit floor voltages, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_calculate_target_quotients(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to calculate target quotients, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_parse_core_count_temp_adj_properties(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to parse core count and temperature adjustment properties, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	rc = cpr4_parse_core_count_temp_voltage_adj(vreg, true);
+	if (rc) {
+		cpr3_err(vreg, "unable to parse temperature and core count voltage adjustments, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	if (vreg->allow_core_count_adj && (vreg->max_core_count <= 0
+				   || vreg->max_core_count >
+				   CPRH_KBSS_CPR_SDELTA_CORE_COUNT)) {
+		cpr3_err(vreg, "qcom,max-core-count has invalid value = %d\n",
+			 vreg->max_core_count);
+		return -EINVAL;
+	}
+
+	rc = cprh_msm8998_partial_binning_override(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to override CPR parameters based on partial binning fuse values, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_apm_crossover_as_corner(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to introduce APM voltage crossover corner, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_mem_acc_crossover_as_corner(vreg);
+	if (rc) {
+		cpr3_err(vreg, "unable to introduce MEM ACC voltage crossover corner, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	cprh_kbss_print_settings(vreg);
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_init_aging() - perform KBSS CPRh controller specific aging
+ *		initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_init_aging(struct cpr3_controller *ctrl)
+{
+	struct cprh_kbss_fuses *fuse = NULL;
+	struct cpr3_regulator *vreg;
+	u32 aging_ro_scale;
+	int i, j, rc = 0;
+
+	for (i = 0; i < ctrl->thread_count; i++) {
+		for (j = 0; j < ctrl->thread[i].vreg_count; j++) {
+			if (ctrl->thread[i].vreg[j].aging_allowed) {
+				ctrl->aging_required = true;
+				vreg = &ctrl->thread[i].vreg[j];
+				fuse = vreg->platform_fuses;
+				break;
+			}
+		}
+	}
+
+	if (!ctrl->aging_required || !fuse || !vreg)
+		return 0;
+
+	rc = cpr3_parse_array_property(vreg, "qcom,cpr-aging-ro-scaling-factor",
+					1, &aging_ro_scale);
+	if (rc)
+		return rc;
+
+	if (aging_ro_scale == 0) {
+		cpr3_err(ctrl, "aging RO scaling factor is invalid: %u\n",
+			aging_ro_scale);
+		return -EINVAL;
+	}
+
+	ctrl->aging_vdd_mode = REGULATOR_MODE_NORMAL;
+	ctrl->aging_complete_vdd_mode = REGULATOR_MODE_IDLE;
+
+	ctrl->aging_sensor_count = 1;
+	ctrl->aging_sensor = devm_kzalloc(ctrl->dev,
+					sizeof(*ctrl->aging_sensor),
+					GFP_KERNEL);
+	if (!ctrl->aging_sensor)
+		return -ENOMEM;
+
+	switch (ctrl->soc_revision) {
+	case SDM660_SOC_ID:
+		if (ctrl->ctrl_id == CPRH_KBSS_POWER_CLUSTER_ID) {
+			ctrl->aging_sensor->sensor_id
+				= SDM660_KBSS_POWER_AGING_SENSOR_ID;
+			ctrl->aging_sensor->bypass_mask[0]
+				= SDM660_KBSS_POWER_AGING_BYPASS_MASK0;
+		} else  {
+			ctrl->aging_sensor->sensor_id
+				= SDM660_KBSS_PERFORMANCE_AGING_SENSOR_ID;
+			ctrl->aging_sensor->bypass_mask[0]
+				= SDM660_KBSS_PERFORMANCE_AGING_BYPASS_MASK0;
+		}
+		break;
+	case MSM8998_V1_SOC_ID:
+	case MSM8998_V2_SOC_ID:
+		if (ctrl->ctrl_id == CPRH_KBSS_POWER_CLUSTER_ID) {
+			ctrl->aging_sensor->sensor_id
+				= MSM8998_KBSS_POWER_AGING_SENSOR_ID;
+			ctrl->aging_sensor->bypass_mask[0]
+				= MSM8998_KBSS_POWER_AGING_BYPASS_MASK0;
+		} else  {
+			ctrl->aging_sensor->sensor_id
+				= MSM8998_KBSS_PERFORMANCE_AGING_SENSOR_ID;
+			ctrl->aging_sensor->bypass_mask[0]
+				= MSM8998_KBSS_PERFORMANCE_AGING_BYPASS_MASK0;
+		}
+		break;
+	default:
+		cpr3_err(ctrl, "unsupported soc id = %d\n", ctrl->soc_revision);
+		return -EINVAL;
+	}
+	ctrl->aging_sensor->ro_scale = aging_ro_scale;
+
+	ctrl->aging_sensor->init_quot_diff
+		= cpr3_convert_open_loop_voltage_fuse(0,
+			CPRH_KBSS_AGING_INIT_QUOT_DIFF_SCALE,
+			fuse->aging_init_quot_diff,
+			CPRH_KBSS_AGING_INIT_QUOT_DIFF_SIZE);
+
+	cpr3_debug(ctrl, "sensor %u aging init quotient diff = %d, aging RO scale = %u QUOT/V\n",
+		ctrl->aging_sensor->sensor_id,
+		ctrl->aging_sensor->init_quot_diff,
+		ctrl->aging_sensor->ro_scale);
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_init_controller() - perform KBSS CPRh controller specific
+ *		initializations
+ * @ctrl:		Pointer to the CPR3 controller
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_init_controller(struct cpr3_controller *ctrl)
+{
+	int rc;
+
+	ctrl->ctrl_type = CPR_CTRL_TYPE_CPRH;
+	rc = cpr3_parse_common_ctrl_data(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "unable to parse common controller data, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-controller-id",
+				  &ctrl->ctrl_id);
+	if (rc) {
+		cpr3_err(ctrl, "could not read DT property qcom,cpr-controller-id, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->ctrl_id < CPRH_KBSS_MIN_CONTROLLER_ID ||
+	    ctrl->ctrl_id > CPRH_KBSS_MAX_CONTROLLER_ID) {
+		cpr3_err(ctrl, "invalid qcom,cpr-controller-id specified\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,cpr-down-error-step-limit",
+				  &ctrl->down_error_step_limit);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-down-error-step-limit, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,cpr-up-error-step-limit",
+				  &ctrl->up_error_step_limit);
+	if (rc) {
+		cpr3_err(ctrl, "error reading qcom,cpr-up-error-step-limit, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,voltage-base",
+				  &ctrl->base_volt);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,voltage-base, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+			"qcom,cpr-up-down-delay-time",
+			&ctrl->up_down_delay_time);
+	if (rc) {
+		cpr3_err(ctrl, "error reading property qcom,cpr-up-down-delay-time, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,apm-threshold-voltage",
+				  &ctrl->apm_threshold_volt);
+	if (rc) {
+		cpr3_debug(ctrl, "qcom,apm-threshold-voltage not specified\n");
+	} else {
+		rc = of_property_read_u32(ctrl->dev->of_node,
+					  "qcom,apm-crossover-voltage",
+					  &ctrl->apm_crossover_volt);
+		if (rc) {
+			cpr3_err(ctrl, "error reading property qcom,apm-crossover-voltage, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	of_property_read_u32(ctrl->dev->of_node, "qcom,apm-hysteresis-voltage",
+				&ctrl->apm_adj_volt);
+	ctrl->apm_adj_volt = CPR3_ROUND(ctrl->apm_adj_volt, ctrl->step_volt);
+
+	ctrl->saw_use_unit_mV = of_property_read_bool(ctrl->dev->of_node,
+					"qcom,cpr-saw-use-unit-mV");
+
+	rc = of_property_read_u32(ctrl->dev->of_node,
+				  "qcom,mem-acc-threshold-voltage",
+				  &ctrl->mem_acc_threshold_volt);
+	if (!rc) {
+		ctrl->mem_acc_threshold_volt
+		    = CPR3_ROUND(ctrl->mem_acc_threshold_volt, ctrl->step_volt);
+
+		rc = of_property_read_u32(ctrl->dev->of_node,
+					  "qcom,mem-acc-crossover-voltage",
+					  &ctrl->mem_acc_crossover_volt);
+		if (rc) {
+			cpr3_err(ctrl, "error reading property qcom,mem-acc-crossover-voltage, rc=%d\n",
+				 rc);
+			return rc;
+		}
+		ctrl->mem_acc_crossover_volt
+		    = CPR3_ROUND(ctrl->mem_acc_crossover_volt, ctrl->step_volt);
+	}
+
+	/*
+	 * Use fixed step quotient if specified otherwise use dynamically
+	 * calculated per RO step quotient
+	 */
+	of_property_read_u32(ctrl->dev->of_node, "qcom,cpr-step-quot-fixed",
+			&ctrl->step_quot_fixed);
+	ctrl->use_dynamic_step_quot = !ctrl->step_quot_fixed;
+
+	of_property_read_u32(ctrl->dev->of_node,
+			"qcom,cpr-voltage-settling-time",
+			&ctrl->voltage_settling_time);
+
+	of_property_read_u32(ctrl->dev->of_node,
+			     "qcom,cpr-corner-switch-delay-time",
+			     &ctrl->corner_switch_delay_time);
+
+	switch (ctrl->soc_revision) {
+	case SDM660_SOC_ID:
+		if (ctrl->ctrl_id == CPRH_KBSS_POWER_CLUSTER_ID)
+			ctrl->sensor_count =
+				SDM660_KBSS_POWER_CPR_SENSOR_COUNT;
+		else
+			ctrl->sensor_count =
+				SDM660_KBSS_PERFORMANCE_CPR_SENSOR_COUNT;
+		break;
+	case MSM8998_V1_SOC_ID:
+	case MSM8998_V2_SOC_ID:
+		if (ctrl->ctrl_id == CPRH_KBSS_POWER_CLUSTER_ID)
+			ctrl->sensor_count =
+				MSM8998_KBSS_POWER_CPR_SENSOR_COUNT;
+		else
+			ctrl->sensor_count =
+				MSM8998_KBSS_PERFORMANCE_CPR_SENSOR_COUNT;
+		break;
+	default:
+		cpr3_err(ctrl, "unsupported soc id = %d\n", ctrl->soc_revision);
+		return -EINVAL;
+	}
+
+	/*
+	 * KBSS only has one thread (0) per controller so the zeroed
+	 * array does not need further modification.
+	 */
+	ctrl->sensor_owner = devm_kcalloc(ctrl->dev, ctrl->sensor_count,
+		sizeof(*ctrl->sensor_owner), GFP_KERNEL);
+	if (!ctrl->sensor_owner)
+		return -ENOMEM;
+
+	ctrl->cpr_clock_rate = CPRH_KBSS_CPR_CLOCK_RATE;
+	ctrl->supports_hw_closed_loop = true;
+	ctrl->use_hw_closed_loop = of_property_read_bool(ctrl->dev->of_node,
+						 "qcom,cpr-hw-closed-loop");
+
+	return 0;
+}
+
+/**
+ * cprh_kbss_populate_opp_table() - populate an Operating Performance Point
+ *		table with the frequencies associated with each corner.
+ *		This table may be used to resolve corner to frequency to
+ *		open-loop voltage mappings.
+ * @pdev:		Pointer to the platform device
+ *
+ * Return: 0 on success, errno on failure
+ */
+static int cprh_kbss_populate_opp_table(struct cpr3_controller *ctrl)
+{
+	struct device *dev = ctrl->dev;
+	struct cpr3_regulator *vreg = &ctrl->thread[0].vreg[0];
+	struct cpr3_corner *corner;
+	int rc, i;
+
+	for (i = 0; i < vreg->corner_count; i++) {
+		corner = &vreg->corner[i];
+		if (!corner->proc_freq) {
+			/*
+			 * 0 MHz indicates this corner is not to be
+			 * used as active DCVS set point. Don't add it
+			 * to the OPP table.
+			 */
+			continue;
+		}
+		rc = dev_pm_opp_add(dev, corner->proc_freq, i + 1);
+		if (rc) {
+			cpr3_err(ctrl, "could not add OPP for corner %d with frequency %u MHz, rc=%d\n",
+				 i + 1, corner->proc_freq, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int cprh_kbss_regulator_suspend(struct platform_device *pdev,
+				pm_message_t state)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_suspend(ctrl);
+}
+
+static int cprh_kbss_regulator_resume(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_resume(ctrl);
+}
+
+/* Data corresponds to the SoC revision */
+static const struct of_device_id cprh_regulator_match_table[] = {
+	{
+		.compatible =  "qcom,cprh-msm8998-v1-kbss-regulator",
+		.data = (void *)(uintptr_t)MSM8998_V1_SOC_ID,
+	},
+	{
+		.compatible = "qcom,cprh-msm8998-v2-kbss-regulator",
+		.data = (void *)(uintptr_t)MSM8998_V2_SOC_ID,
+	},
+	{
+		.compatible = "qcom,cprh-msm8998-kbss-regulator",
+		.data = (void *)(uintptr_t)MSM8998_V2_SOC_ID,
+	},
+	{
+		.compatible = "qcom,cprh-sdm660-kbss-regulator",
+		.data = (void *)(uintptr_t)SDM660_SOC_ID,
+	},
+	{}
+};
+
+static int cprh_kbss_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct cpr3_controller *ctrl;
+	int rc;
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
+	if (!ctrl)
+		return -ENOMEM;
+
+	ctrl->dev = dev;
+	ctrl->cpr_allowed_hw = true;
+
+	rc = of_property_read_string(dev->of_node, "qcom,cpr-ctrl-name",
+					&ctrl->name);
+	if (rc) {
+		cpr3_err(ctrl, "unable to read qcom,cpr-ctrl-name, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	match = of_match_node(cprh_regulator_match_table, dev->of_node);
+	if (match)
+		ctrl->soc_revision = (uintptr_t)match->data;
+	else
+		cpr3_err(ctrl, "could not find compatible string match\n");
+
+	rc = cpr3_map_fuse_base(ctrl, pdev);
+	if (rc) {
+		cpr3_err(ctrl, "could not map fuse base address\n");
+		return rc;
+	}
+
+	rc = cpr3_allocate_threads(ctrl, 0, 0);
+	if (rc) {
+		cpr3_err(ctrl, "failed to allocate CPR thread array, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	if (ctrl->thread_count != 1) {
+		cpr3_err(ctrl, "expected 1 thread but found %d\n",
+			ctrl->thread_count);
+		return -EINVAL;
+	} else if (ctrl->thread[0].vreg_count != 1) {
+		cpr3_err(ctrl, "expected 1 regulator but found %d\n",
+			ctrl->thread[0].vreg_count);
+		return -EINVAL;
+	}
+
+	rc = cprh_kbss_init_controller(ctrl);
+	if (rc) {
+		if (rc != -EPROBE_DEFER)
+			cpr3_err(ctrl, "failed to initialize CPR controller parameters, rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_init_thread(&ctrl->thread[0]);
+	if (rc) {
+		cpr3_err(ctrl, "thread initialization failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_init_regulator(&ctrl->thread[0].vreg[0]);
+	if (rc) {
+		cpr3_err(&ctrl->thread[0].vreg[0], "regulator initialization failed, rc=%d\n",
+			 rc);
+		return rc;
+	}
+
+	rc = cprh_kbss_init_aging(ctrl);
+	if (rc) {
+		cpr3_err(ctrl, "failed to initialize aging configurations, rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	platform_set_drvdata(pdev, ctrl);
+
+	rc = cprh_kbss_populate_opp_table(ctrl);
+	if (rc)
+		panic("cprh-kbss-regulator OPP table initialization failed\n");
+
+	return cpr3_regulator_register(pdev, ctrl);
+}
+
+static int cprh_kbss_regulator_remove(struct platform_device *pdev)
+{
+	struct cpr3_controller *ctrl = platform_get_drvdata(pdev);
+
+	return cpr3_regulator_unregister(ctrl);
+}
+
+static struct platform_driver cprh_kbss_regulator_driver = {
+	.driver		= {
+		.name		= "qcom,cprh-kbss-regulator",
+		.of_match_table	= cprh_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= cprh_kbss_regulator_probe,
+	.remove		= cprh_kbss_regulator_remove,
+	.suspend	= cprh_kbss_regulator_suspend,
+	.resume		= cprh_kbss_regulator_resume,
+};
+
+static int cpr_regulator_init(void)
+{
+	return platform_driver_register(&cprh_kbss_regulator_driver);
+}
+
+static void cpr_regulator_exit(void)
+{
+	platform_driver_unregister(&cprh_kbss_regulator_driver);
+}
+
+MODULE_DESCRIPTION("CPRh KBSS regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(cpr_regulator_init);
+module_exit(cpr_regulator_exit);
diff --git a/drivers/regulator/kryo-regulator.c b/drivers/regulator/kryo-regulator.c
new file mode 100644
index 0000000..fd853e7
--- /dev/null
+++ b/drivers/regulator/kryo-regulator.c
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/cpu_pm.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/msm-ldo-regulator.h>
+
+#include <soc/qcom/spm.h>
+
+#define KRYO_REGULATOR_DRIVER_NAME	"kryo-regulator"
+
+#define kvreg_err(kvreg, message, ...) \
+	pr_err("%s: " message, (kvreg)->name, ##__VA_ARGS__)
+#define kvreg_info(kvreg, message, ...) \
+	pr_info("%s: " message, (kvreg)->name, ##__VA_ARGS__)
+#define kvreg_debug(kvreg, message, ...) \
+	pr_debug("%s: " message, (kvreg)->name, ##__VA_ARGS__)
+
+/* CPUSS power domain register offsets */
+#define APCC_PWR_CTL_OVERRIDE		0x38
+#define APCC_PGS_RET_STATUS		0xe0
+
+/* APCS CSR register offsets */
+#define APCS_VERSION		0xfd0
+
+/* Cluster power domain register offsets */
+#define APC_LDO_VREF_SET	0x08
+#define APC_RET_VREF_SET	0x10
+#define APC_PWR_GATE_MODE	0x18
+#define APC_PWR_GATE_DLY	0x28
+#define APC_LDO_CFG		0x40
+#define APC_APM_CFG		0x50
+#define APC_PGSCTL_STS		0x60
+
+/* Register bit mask definitions*/
+#define PWR_GATE_SWITCH_MODE_MASK	GENMASK(0, 0)
+#define VREF_MASK			GENMASK(6, 0)
+#define APM_CFG_MASK			GENMASK(7, 0)
+#define FSM_CUR_STATE_MASK		GENMASK(5, 4)
+#define APC_PWR_GATE_DLY_MASK		GENMASK(11, 0)
+#define APCC_PGS_MASK(cluster)		(0x7 << (0x3 * (cluster)))
+
+/* Register bit definitions */
+#define VREF_BIT_POS		0
+
+/* Maximum delay to wait before declaring a Power Gate Switch timed out */
+#define PWR_GATE_SWITCH_TIMEOUT_US	5
+
+#define PWR_GATE_SWITCH_MODE_LDO	0
+#define PWR_GATE_SWITCH_MODE_BHS	1
+#define MSM8996_CPUSS_VER_1P1	0x10010000
+
+#define LDO_N_VOLTAGES		0x80
+#define AFFINITY_LEVEL_M3	2
+#define SHARED_CPU_REG_NUM	0
+#define VDD_SUPPLY_STEP_UV	5000
+#define VDD_SUPPLY_MIN_UV	80000
+
+struct kryo_regulator {
+	struct list_head		link;
+	spinlock_t			slock;
+	struct regulator_desc		desc;
+	struct regulator_dev		*rdev;
+	struct regulator_dev		*retention_rdev;
+	struct regulator_desc		retention_desc;
+	const char			*name;
+	enum msm_ldo_supply_mode	mode;
+	enum msm_ldo_supply_mode	retention_mode;
+	enum msm_ldo_supply_mode	pre_lpm_state_mode;
+	void __iomem			*reg_base;
+	void __iomem			*pm_apcc_base;
+	struct dentry			*debugfs;
+	struct notifier_block		cpu_pm_notifier;
+	unsigned long			lpm_enter_count;
+	unsigned long			lpm_exit_count;
+	int				volt;
+	int				retention_volt;
+	int				headroom_volt;
+	int				pre_lpm_state_volt;
+	int				vref_func_step_volt;
+	int				vref_func_min_volt;
+	int				vref_func_max_volt;
+	int				vref_ret_step_volt;
+	int				vref_ret_min_volt;
+	int				vref_ret_max_volt;
+	int				cluster_num;
+	u32				ldo_config_init;
+	u32				apm_config_init;
+	u32				version;
+	bool				vreg_en;
+};
+
+static struct dentry *kryo_debugfs_base;
+static DEFINE_MUTEX(kryo_regulator_list_mutex);
+static LIST_HEAD(kryo_regulator_list);
+
+static bool is_between(int left, int right, int value)
+{
+	if (left >= right && left >= value && value >= right)
+		return true;
+	if (left <= right && left <= value && value <= right)
+		return true;
+
+	return false;
+}
+
+static void kryo_masked_write(struct kryo_regulator *kvreg,
+			      int reg, u32 mask, u32 val)
+{
+	u32 reg_val;
+
+	reg_val = readl_relaxed(kvreg->reg_base + reg);
+	reg_val &= ~mask;
+	reg_val |= (val & mask);
+
+	writel_relaxed(reg_val, kvreg->reg_base + reg);
+
+	/* Ensure write above completes */
+	mb();
+}
+
+static inline void kryo_pm_apcc_masked_write(struct kryo_regulator *kvreg,
+			       int reg, u32 mask, u32 val)
+{
+	u32 reg_val, orig_val;
+
+	reg_val = orig_val = readl_relaxed(kvreg->pm_apcc_base + reg);
+	reg_val &= ~mask;
+	reg_val |= (val & mask);
+
+	if (reg_val != orig_val) {
+		writel_relaxed(reg_val, kvreg->pm_apcc_base + reg);
+
+		/* Ensure write above completes */
+		mb();
+	}
+}
+
+static inline int kryo_decode_retention_volt(struct kryo_regulator *kvreg,
+					     int reg)
+{
+	return kvreg->vref_ret_min_volt + reg * kvreg->vref_ret_step_volt;
+}
+
+static inline int kryo_encode_retention_volt(struct kryo_regulator *kvreg,
+					     int volt)
+{
+	int encoded_volt = DIV_ROUND_UP(volt - kvreg->vref_ret_min_volt,
+				      kvreg->vref_ret_step_volt);
+
+	if (encoded_volt >= LDO_N_VOLTAGES || encoded_volt < 0)
+		return -EINVAL;
+	else
+		return encoded_volt;
+}
+
+static inline int kryo_decode_functional_volt(struct kryo_regulator *kvreg,
+					      int reg)
+{
+	return kvreg->vref_func_min_volt + reg * kvreg->vref_func_step_volt;
+}
+
+static inline int kryo_encode_functional_volt(struct kryo_regulator *kvreg,
+					      int volt)
+{
+	int encoded_volt = DIV_ROUND_UP(volt - kvreg->vref_func_min_volt,
+				      kvreg->vref_func_step_volt);
+
+	if (encoded_volt >= LDO_N_VOLTAGES || encoded_volt < 0)
+		return -EINVAL;
+	else
+		return encoded_volt;
+}
+
+/* Locks must be held by the caller */
+static int kryo_set_retention_volt(struct kryo_regulator *kvreg, int volt)
+{
+	int reg_val;
+
+	reg_val = kryo_encode_retention_volt(kvreg, volt);
+	if (reg_val < 0) {
+		kvreg_err(kvreg, "unsupported LDO retention voltage, rc=%d\n",
+			  reg_val);
+		return reg_val;
+	}
+
+	kryo_masked_write(kvreg, APC_RET_VREF_SET, VREF_MASK,
+			   reg_val << VREF_BIT_POS);
+
+	kvreg->retention_volt = kryo_decode_retention_volt(kvreg, reg_val);
+	kvreg_debug(kvreg, "Set LDO retention voltage=%d uV (0x%x)\n",
+		    kvreg->retention_volt, reg_val);
+
+	return 0;
+}
+
+/* Locks must be held by the caller */
+static int kryo_set_ldo_volt(struct kryo_regulator *kvreg, int volt)
+{
+	int reg_val;
+
+	/*
+	 * Assume the consumer ensures the requested voltage satisfies the
+	 * headroom and adjustment voltage requirements. The value may be
+	 * rounded up if necessary, to match the LDO resolution. Configure it.
+	 */
+	reg_val = kryo_encode_functional_volt(kvreg, volt);
+	if (reg_val < 0) {
+		kvreg_err(kvreg, "unsupported LDO functional voltage, rc=%d\n",
+			  reg_val);
+		return reg_val;
+	}
+
+	kryo_masked_write(kvreg, APC_LDO_VREF_SET, VREF_MASK,
+			   reg_val << VREF_BIT_POS);
+
+	kvreg->volt = kryo_decode_functional_volt(kvreg, reg_val);
+	kvreg_debug(kvreg, "Set LDO voltage=%d uV (0x%x)\n",
+		    kvreg->volt, reg_val);
+
+	return 0;
+}
+
+/* Locks must be held by the caller */
+static int kryo_configure_mode(struct kryo_regulator *kvreg,
+				enum msm_ldo_supply_mode mode)
+{
+	u32 reg;
+	int timeout = PWR_GATE_SWITCH_TIMEOUT_US;
+
+	/* Configure LDO or BHS mode */
+	kryo_masked_write(kvreg, APC_PWR_GATE_MODE, PWR_GATE_SWITCH_MODE_MASK,
+			  mode == LDO_MODE ? PWR_GATE_SWITCH_MODE_LDO
+			  : PWR_GATE_SWITCH_MODE_BHS);
+
+	/* Complete register write before reading HW status register */
+	mb();
+
+	/* Delay to allow Power Gate Switch FSM to reach idle state */
+	while (timeout > 0) {
+		reg = readl_relaxed(kvreg->reg_base + APC_PGSCTL_STS);
+		if (!(reg & FSM_CUR_STATE_MASK))
+			break;
+
+		udelay(1);
+		timeout--;
+	}
+
+	if (timeout == 0) {
+		kvreg_err(kvreg, "PGS switch to %s failed. APC_PGSCTL_STS=0x%x\n",
+			  mode == LDO_MODE ? "LDO" : "BHS", reg);
+		return -ETIMEDOUT;
+	}
+
+	kvreg->mode = mode;
+	kvreg_debug(kvreg, "using %s mode\n", mode == LDO_MODE ? "LDO" : "BHS");
+
+	return 0;
+}
+
+static int kryo_regulator_enable(struct regulator_dev *rdev)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+	int rc;
+	unsigned long flags;
+
+	if (kvreg->vreg_en == true)
+		return 0;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+	rc = kryo_set_ldo_volt(kvreg, kvreg->volt);
+	if (rc) {
+		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);
+		goto done;
+	}
+
+	kvreg->vreg_en = true;
+	kvreg_debug(kvreg, "enabled\n");
+
+done:
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return rc;
+}
+
+static int kryo_regulator_disable(struct regulator_dev *rdev)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+	int rc;
+	unsigned long flags;
+
+	if (kvreg->vreg_en == false)
+		return 0;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+	kvreg->vreg_en = false;
+	kvreg_debug(kvreg, "disabled\n");
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return rc;
+}
+
+static int kryo_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	return kvreg->vreg_en;
+}
+
+static int kryo_regulator_set_voltage(struct regulator_dev *rdev,
+			int min_volt, int max_volt, unsigned int *selector)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+
+	if (!kvreg->vreg_en) {
+		kvreg->volt = min_volt;
+		spin_unlock_irqrestore(&kvreg->slock, flags);
+		return 0;
+	}
+
+	rc = kryo_set_ldo_volt(kvreg, min_volt);
+	if (rc)
+		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);
+
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return rc;
+}
+
+static int kryo_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	return kvreg->volt;
+}
+
+static int kryo_regulator_set_bypass(struct regulator_dev *rdev,
+				  bool enable)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+
+	/*
+	 * LDO Vref voltage must be programmed before switching
+	 * modes to ensure stable operation.
+	 */
+	rc = kryo_set_ldo_volt(kvreg, kvreg->volt);
+	if (rc)
+		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);
+
+	rc = kryo_configure_mode(kvreg, enable);
+	if (rc)
+		kvreg_err(kvreg, "could not configure to %s mode\n",
+			  enable == LDO_MODE ? "LDO" : "BHS");
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return rc;
+}
+
+static int kryo_regulator_get_bypass(struct regulator_dev *rdev,
+				  bool *enable)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	*enable = kvreg->mode;
+
+	return 0;
+}
+
+static int kryo_regulator_list_voltage(struct regulator_dev *rdev,
+				       unsigned int selector)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	if (selector < kvreg->desc.n_voltages)
+		return kryo_decode_functional_volt(kvreg, selector);
+	else
+		return 0;
+}
+
+static int kryo_regulator_retention_set_voltage(struct regulator_dev *rdev,
+			int min_volt, int max_volt, unsigned int *selector)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+	int rc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+	rc = kryo_set_retention_volt(kvreg, min_volt);
+	if (rc)
+		kvreg_err(kvreg, "set voltage failed, rc=%d\n", rc);
+
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return rc;
+}
+
+static int kryo_regulator_retention_get_voltage(struct regulator_dev *rdev)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	return kvreg->retention_volt;
+}
+
+static int kryo_regulator_retention_set_bypass(struct regulator_dev *rdev,
+				  bool enable)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+	int timeout = PWR_GATE_SWITCH_TIMEOUT_US;
+	int rc = 0;
+	u32 reg_val;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+
+	kryo_pm_apcc_masked_write(kvreg,
+				  APCC_PWR_CTL_OVERRIDE,
+				  APCC_PGS_MASK(kvreg->cluster_num),
+				  enable ?
+				  0 : APCC_PGS_MASK(kvreg->cluster_num));
+
+	/* Ensure write above completes before proceeding */
+	mb();
+
+	if (enable == BHS_MODE && kvreg->version < MSM8996_CPUSS_VER_1P1) {
+		/* No status register, delay worst case */
+		udelay(PWR_GATE_SWITCH_TIMEOUT_US);
+	} else if (enable == BHS_MODE) {
+		while (timeout > 0) {
+			reg_val = readl_relaxed(kvreg->pm_apcc_base
+						+ APCC_PGS_RET_STATUS);
+			if (!(reg_val & APCC_PGS_MASK(kvreg->cluster_num)))
+				break;
+
+			udelay(1);
+			timeout--;
+		}
+
+		if (timeout == 0) {
+			kvreg_err(kvreg, "PGS switch timed out. APCC_PGS_RET_STATUS=0x%x\n",
+				  reg_val);
+			rc = -ETIMEDOUT;
+			goto done;
+		}
+	}
+
+	/* Bypassed LDO retention operation == disallow LDO retention */
+	kvreg_debug(kvreg, "%s LDO retention\n",
+		    enable ? "enabled" : "disabled");
+	kvreg->retention_mode = enable == LDO_MODE ? LDO_MODE
+		: BHS_MODE;
+
+done:
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return rc;
+}
+
+static int kryo_regulator_retention_get_bypass(struct regulator_dev *rdev,
+				  bool *enable)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	*enable = kvreg->retention_mode;
+
+	return 0;
+}
+
+static int kryo_regulator_retention_list_voltage(struct regulator_dev *rdev,
+				       unsigned int selector)
+{
+	struct kryo_regulator *kvreg = rdev_get_drvdata(rdev);
+
+	if (selector < kvreg->retention_desc.n_voltages)
+		return kryo_decode_retention_volt(kvreg, selector);
+	else
+		return 0;
+}
+
+static struct regulator_ops kryo_regulator_ops = {
+	.enable			= kryo_regulator_enable,
+	.disable		= kryo_regulator_disable,
+	.is_enabled		= kryo_regulator_is_enabled,
+	.set_voltage		= kryo_regulator_set_voltage,
+	.get_voltage		= kryo_regulator_get_voltage,
+	.set_bypass		= kryo_regulator_set_bypass,
+	.get_bypass		= kryo_regulator_get_bypass,
+	.list_voltage		= kryo_regulator_list_voltage,
+};
+
+static struct regulator_ops kryo_regulator_retention_ops = {
+	.set_voltage		= kryo_regulator_retention_set_voltage,
+	.get_voltage		= kryo_regulator_retention_get_voltage,
+	.set_bypass		= kryo_regulator_retention_set_bypass,
+	.get_bypass		= kryo_regulator_retention_get_bypass,
+	.list_voltage		= kryo_regulator_retention_list_voltage,
+};
+
+static void kryo_ldo_voltage_init(struct kryo_regulator *kvreg)
+{
+	kryo_set_retention_volt(kvreg, kvreg->retention_volt);
+	kryo_set_ldo_volt(kvreg, kvreg->volt);
+}
+
+#define APC_PWR_GATE_DLY_INIT		0x00000101
+static int kryo_hw_init(struct kryo_regulator *kvreg)
+{
+	/* Set up VREF_LDO and VREF_RET */
+	kryo_ldo_voltage_init(kvreg);
+
+	/* Program LDO and APM configuration registers */
+	writel_relaxed(kvreg->ldo_config_init, kvreg->reg_base + APC_LDO_CFG);
+
+	kryo_masked_write(kvreg, APC_APM_CFG, APM_CFG_MASK,
+			  kvreg->apm_config_init);
+
+	/* Configure power gate sequencer delay */
+	kryo_masked_write(kvreg, APC_PWR_GATE_DLY, APC_PWR_GATE_DLY_MASK,
+			   APC_PWR_GATE_DLY_INIT);
+
+	/* Allow LDO retention mode only when it's safe to do so */
+	kryo_pm_apcc_masked_write(kvreg,
+				  APCC_PWR_CTL_OVERRIDE,
+				  APCC_PGS_MASK(kvreg->cluster_num),
+				  APCC_PGS_MASK(kvreg->cluster_num));
+
+	/* Complete the above writes before other accesses */
+	mb();
+
+	return 0;
+}
+
+static ssize_t kryo_dbg_mode_read(struct file *file, char __user *buff,
+				   size_t count, loff_t *ppos)
+{
+	struct kryo_regulator *kvreg = file->private_data;
+	char buf[10];
+	int len = 0;
+	u32 reg_val;
+	unsigned long flags;
+
+	if (!kvreg)
+		return -ENODEV;
+
+	/* Confirm HW state matches Kryo regulator device state */
+	spin_lock_irqsave(&kvreg->slock, flags);
+	reg_val = readl_relaxed(kvreg->reg_base + APC_PWR_GATE_MODE);
+	if (((reg_val & PWR_GATE_SWITCH_MODE_MASK) == PWR_GATE_SWITCH_MODE_LDO
+	     && kvreg->mode != LDO_MODE) ||
+	    ((reg_val & PWR_GATE_SWITCH_MODE_MASK) == PWR_GATE_SWITCH_MODE_BHS
+	     && kvreg->mode != BHS_MODE)) {
+		kvreg_err(kvreg, "HW state disagrees on PWR gate mode! reg=0x%x\n",
+			  reg_val);
+		len = snprintf(buf, sizeof(buf), "ERR\n");
+	} else {
+		len = snprintf(buf, sizeof(buf), "%s\n",
+			       kvreg->mode == LDO_MODE ?
+			       "LDO" : "BHS");
+	}
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return simple_read_from_buffer(buff, count, ppos, buf, len);
+}
+
+static int kryo_dbg_base_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static const struct file_operations kryo_dbg_mode_fops = {
+	.open = kryo_dbg_base_open,
+	.read = kryo_dbg_mode_read,
+};
+
+static void kryo_debugfs_init(struct kryo_regulator *kvreg)
+{
+	struct dentry *temp;
+
+	if (IS_ERR_OR_NULL(kryo_debugfs_base)) {
+		if (PTR_ERR(kryo_debugfs_base) != -ENODEV)
+			kvreg_err(kvreg, "Base directory missing, cannot create debugfs nodes rc=%ld\n",
+				  PTR_ERR(kryo_debugfs_base));
+		return;
+	}
+
+	kvreg->debugfs = debugfs_create_dir(kvreg->name, kryo_debugfs_base);
+
+	if (IS_ERR_OR_NULL(kvreg->debugfs)) {
+		kvreg_err(kvreg, "debugfs directory creation failed rc=%ld\n",
+			  PTR_ERR(kvreg->debugfs));
+		return;
+	}
+
+	temp = debugfs_create_file("mode", 0444, kvreg->debugfs,
+				   kvreg, &kryo_dbg_mode_fops);
+
+	if (IS_ERR_OR_NULL(temp)) {
+		kvreg_err(kvreg, "mode node creation failed rc=%ld\n",
+			PTR_ERR(temp));
+		return;
+	}
+}
+
+static void kryo_debugfs_deinit(struct kryo_regulator *kvreg)
+{
+	debugfs_remove_recursive(kvreg->debugfs);
+}
+
+static void kryo_debugfs_base_init(void)
+{
+	kryo_debugfs_base = debugfs_create_dir(KRYO_REGULATOR_DRIVER_NAME,
+						NULL);
+	if (IS_ERR_OR_NULL(kryo_debugfs_base)) {
+		if (PTR_ERR(kryo_debugfs_base) != -ENODEV)
+			pr_err("%s debugfs base directory creation failed rc=%ld\n",
+			       KRYO_REGULATOR_DRIVER_NAME,
+			       PTR_ERR(kryo_debugfs_base));
+	}
+}
+
+static void kryo_debugfs_base_remove(void)
+{
+	debugfs_remove_recursive(kryo_debugfs_base);
+}
+
+static int kryo_regulator_init_data(struct platform_device *pdev,
+				    struct kryo_regulator *kvreg)
+{
+	int rc = 0;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *temp;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apc");
+	if (!res) {
+		dev_err(dev, "PM APC register address missing\n");
+		return -EINVAL;
+	}
+
+	kvreg->reg_base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!kvreg->reg_base) {
+		dev_err(dev, "failed to map PM APC registers\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc");
+	if (!res) {
+		dev_err(dev, "PM APCC register address missing\n");
+		return -EINVAL;
+	}
+
+	kvreg->pm_apcc_base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!kvreg->pm_apcc_base) {
+		dev_err(dev, "failed to map PM APCC registers\n");
+		return -ENOMEM;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "apcs-csr");
+	if (!res) {
+		dev_err(dev, "missing APCS CSR physical base address");
+		return -EINVAL;
+	}
+
+	temp = ioremap(res->start, resource_size(res));
+	if (!temp) {
+		dev_err(dev, "failed to map APCS CSR registers\n");
+		return -ENOMEM;
+	}
+
+	kvreg->version = readl_relaxed(temp + APCS_VERSION);
+	iounmap(temp);
+
+	rc = of_property_read_u32(dev->of_node,
+				  "qcom,vref-functional-step-voltage",
+				  &kvreg->vref_func_step_volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,vref-functional-step-voltage missing rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(dev->of_node,
+				  "qcom,vref-functional-min-voltage",
+				  &kvreg->vref_func_min_volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,vref-functional-min-voltage missing rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	kvreg->vref_func_max_volt = kryo_decode_functional_volt(kvreg,
+							LDO_N_VOLTAGES - 1);
+
+	rc = of_property_read_u32(dev->of_node,
+				  "qcom,vref-retention-step-voltage",
+				  &kvreg->vref_ret_step_volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,vref-retention-step-voltage missing rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(dev->of_node,
+				  "qcom,vref-retention-min-voltage",
+				  &kvreg->vref_ret_min_volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,vref-retention-min-voltage missing rc=%d\n",
+			rc);
+		return rc;
+	}
+
+	kvreg->vref_ret_max_volt = kryo_decode_retention_volt(kvreg,
+						      LDO_N_VOLTAGES - 1);
+
+	rc = of_property_read_u32(dev->of_node, "qcom,ldo-default-voltage",
+				  &kvreg->volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,ldo-default-voltage missing rc=%d\n", rc);
+		return rc;
+	}
+	if (!is_between(kvreg->vref_func_min_volt,
+			kvreg->vref_func_max_volt,
+			kvreg->volt)) {
+		dev_err(dev, "qcom,ldo-default-voltage=%d uV outside allowed range\n",
+				kvreg->volt);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,retention-voltage",
+				  &kvreg->retention_volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,retention-voltage missing rc=%d\n", rc);
+		return rc;
+	}
+	if (!is_between(kvreg->vref_ret_min_volt,
+			kvreg->vref_ret_max_volt,
+			kvreg->retention_volt)) {
+		dev_err(dev, "qcom,retention-voltage=%d uV outside allowed range\n",
+			kvreg->retention_volt);
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,ldo-headroom-voltage",
+				  &kvreg->headroom_volt);
+	if (rc < 0) {
+		dev_err(dev, "qcom,ldo-headroom-voltage missing rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,ldo-config-init",
+				  &kvreg->ldo_config_init);
+	if (rc < 0) {
+		dev_err(dev, "qcom,ldo-config-init missing rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,apm-config-init",
+				  &kvreg->apm_config_init);
+	if (rc < 0) {
+		dev_err(dev, "qcom,apm-config-init missing rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(dev->of_node, "qcom,cluster-num",
+				  &kvreg->cluster_num);
+	if (rc < 0) {
+		dev_err(dev, "qcom,cluster-num missing rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int kryo_regulator_retention_init(struct kryo_regulator *kvreg,
+				   struct platform_device *pdev,
+				   struct device_node *ret_node)
+{
+	struct device *dev = &pdev->dev;
+	struct regulator_init_data *init_data;
+	struct regulator_config reg_config = {};
+	int rc = 0;
+
+	init_data = of_get_regulator_init_data(dev, ret_node,
+			&kvreg->retention_desc);
+	if (!init_data) {
+		kvreg_err(kvreg, "regulator init data is missing\n");
+		return -EINVAL;
+	}
+
+	if (!init_data->constraints.name) {
+		kvreg_err(kvreg, "regulator name is missing from constraints\n");
+		return -EINVAL;
+	}
+
+	init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_BYPASS
+		| REGULATOR_CHANGE_VOLTAGE;
+	init_data->constraints.input_uV = init_data->constraints.max_uV;
+
+	kvreg->retention_desc.name		= init_data->constraints.name;
+	kvreg->retention_desc.n_voltages	= LDO_N_VOLTAGES;
+	kvreg->retention_desc.ops		= &kryo_regulator_retention_ops;
+	kvreg->retention_desc.type		= REGULATOR_VOLTAGE;
+	kvreg->retention_desc.owner		= THIS_MODULE;
+
+	reg_config.dev = dev;
+	reg_config.init_data = init_data;
+	reg_config.driver_data = kvreg;
+	reg_config.of_node = ret_node;
+	kvreg->retention_rdev = regulator_register(&kvreg->retention_desc,
+						   &reg_config);
+	if (IS_ERR(kvreg->retention_rdev)) {
+		rc = PTR_ERR(kvreg->retention_rdev);
+		kvreg_err(kvreg, "regulator_register failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int kryo_regulator_lpm_prepare(struct kryo_regulator *kvreg)
+{
+	int vdd_volt_uv, bhs_volt, vdd_vlvl = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+
+	kvreg->pre_lpm_state_mode = kvreg->mode;
+	kvreg->pre_lpm_state_volt = kvreg->volt;
+
+	if (kvreg->mode == LDO_MODE) {
+		if (!vdd_vlvl) {
+			vdd_vlvl = msm_spm_get_vdd(SHARED_CPU_REG_NUM);
+			if (vdd_vlvl < 0) {
+				kvreg_err(kvreg, "could not get vdd supply voltage level, rc=%d\n",
+					  vdd_vlvl);
+				spin_unlock_irqrestore(&kvreg->slock, flags);
+				return NOTIFY_BAD;
+			}
+
+			vdd_volt_uv = vdd_vlvl * VDD_SUPPLY_STEP_UV
+				+ VDD_SUPPLY_MIN_UV;
+		}
+		kvreg_debug(kvreg, "switching to BHS mode, vdd_apcc=%d uV, current LDO Vref=%d, LPM enter count=%lx\n",
+			    vdd_volt_uv, kvreg->volt, kvreg->lpm_enter_count);
+
+		/*
+		 * Program vdd supply minus LDO headroom as voltage.
+		 * Cap this value to the maximum physically supported
+		 * LDO voltage, if necessary.
+		 */
+		bhs_volt = vdd_volt_uv - kvreg->headroom_volt;
+		if (bhs_volt > kvreg->vref_func_max_volt) {
+			kvreg_debug(kvreg, "limited to LDO output of %d uV when switching to BHS mode\n",
+				    kvreg->vref_func_max_volt);
+			bhs_volt = kvreg->vref_func_max_volt;
+		}
+
+		kryo_set_ldo_volt(kvreg, bhs_volt);
+
+		/* Switch Power Gate Mode */
+		kryo_configure_mode(kvreg, BHS_MODE);
+	}
+
+	kvreg->lpm_enter_count++;
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	return NOTIFY_OK;
+}
+
+static int kryo_regulator_lpm_resume(struct kryo_regulator *kvreg)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&kvreg->slock, flags);
+
+	if (kvreg->mode == BHS_MODE &&
+	    kvreg->pre_lpm_state_mode == LDO_MODE) {
+		kvreg_debug(kvreg, "switching to LDO mode, cached LDO Vref=%d, LPM exit count=%lx\n",
+			    kvreg->pre_lpm_state_volt, kvreg->lpm_exit_count);
+
+		/*
+		 * Cached voltage value corresponds to vdd supply minus
+		 * LDO headroom, reprogram it.
+		 */
+		kryo_set_ldo_volt(kvreg, kvreg->volt);
+
+		/* Switch Power Gate Mode */
+		kryo_configure_mode(kvreg, LDO_MODE);
+
+		/* Request final LDO output voltage */
+		kryo_set_ldo_volt(kvreg, kvreg->pre_lpm_state_volt);
+	}
+
+	kvreg->lpm_exit_count++;
+	spin_unlock_irqrestore(&kvreg->slock, flags);
+
+	if (kvreg->lpm_exit_count != kvreg->lpm_enter_count) {
+		kvreg_err(kvreg, "LPM entry/exit counter mismatch, this is not expected: enter=%lx exit=%lx\n",
+			  kvreg->lpm_enter_count, kvreg->lpm_exit_count);
+		BUG_ON(1);
+	}
+
+	return NOTIFY_OK;
+}
+
+static int kryo_regulator_cpu_pm_callback(struct notifier_block *self,
+					 unsigned long cmd, void *v)
+{
+	struct kryo_regulator *kvreg = container_of(self, struct kryo_regulator,
+						    cpu_pm_notifier);
+	unsigned long aff_level = (unsigned long) v;
+	int rc = NOTIFY_OK;
+
+	switch (cmd) {
+	case CPU_CLUSTER_PM_ENTER:
+		if (aff_level == AFFINITY_LEVEL_M3)
+			rc = kryo_regulator_lpm_prepare(kvreg);
+		break;
+	case CPU_CLUSTER_PM_EXIT:
+		if (aff_level == AFFINITY_LEVEL_M3)
+			rc = kryo_regulator_lpm_resume(kvreg);
+		break;
+	}
+
+	return rc;
+}
+
+static int kryo_regulator_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct kryo_regulator *kvreg;
+	struct regulator_config reg_config = {};
+	struct regulator_init_data *init_data = pdev->dev.platform_data;
+	struct device_node *child;
+	int rc = 0;
+
+	if (!dev->of_node) {
+		dev_err(dev, "Device tree node is missing\n");
+		return -ENODEV;
+	}
+
+	init_data = of_get_regulator_init_data(dev, dev->of_node, NULL);
+
+	if (!init_data) {
+		dev_err(dev, "regulator init data is missing\n");
+		return -EINVAL;
+	}
+
+	if (!init_data->constraints.name) {
+		dev_err(dev, "regulator name is missing from constraints\n");
+		return -EINVAL;
+	}
+
+	init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE
+		| REGULATOR_CHANGE_BYPASS | REGULATOR_CHANGE_STATUS;
+	init_data->constraints.input_uV = init_data->constraints.max_uV;
+
+	kvreg = devm_kzalloc(dev, sizeof(*kvreg), GFP_KERNEL);
+	if (!kvreg)
+		return -ENOMEM;
+
+	rc = kryo_regulator_init_data(pdev, kvreg);
+	if (rc) {
+		dev_err(dev, "could not parse and ioremap all device tree properties\n");
+		return rc;
+	}
+
+	spin_lock_init(&kvreg->slock);
+	kvreg->name		= init_data->constraints.name;
+	kvreg->desc.name	= kvreg->name;
+	kvreg->desc.n_voltages	= LDO_N_VOLTAGES;
+	kvreg->desc.ops		= &kryo_regulator_ops;
+	kvreg->desc.type	= REGULATOR_VOLTAGE;
+	kvreg->desc.owner	= THIS_MODULE;
+	kvreg->mode		= BHS_MODE;
+
+	for_each_available_child_of_node(dev->of_node, child) {
+		kryo_regulator_retention_init(kvreg, pdev, child);
+		if (rc) {
+			dev_err(dev, "could not initialize retention regulator, rc=%d\n",
+				rc);
+			return rc;
+		}
+		break;
+	}
+
+	/* CPUSS PM Register Initialization */
+	rc = kryo_hw_init(kvreg);
+	if (rc) {
+		dev_err(dev, "unable to perform CPUSS PM initialization sequence\n");
+		return rc;
+	}
+
+	reg_config.dev = dev;
+	reg_config.init_data = init_data;
+	reg_config.driver_data = kvreg;
+	reg_config.of_node = dev->of_node;
+	kvreg->rdev = regulator_register(&kvreg->desc, &reg_config);
+	if (IS_ERR(kvreg->rdev)) {
+		rc = PTR_ERR(kvreg->rdev);
+		kvreg_err(kvreg, "regulator_register failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	platform_set_drvdata(pdev, kvreg);
+	kryo_debugfs_init(kvreg);
+
+	mutex_lock(&kryo_regulator_list_mutex);
+	list_add_tail(&kvreg->link, &kryo_regulator_list);
+	mutex_unlock(&kryo_regulator_list_mutex);
+
+	kvreg->cpu_pm_notifier.notifier_call = kryo_regulator_cpu_pm_callback;
+	cpu_pm_register_notifier(&kvreg->cpu_pm_notifier);
+	kvreg_debug(kvreg, "registered cpu pm notifier\n");
+
+	kvreg_info(kvreg, "default LDO functional volt=%d uV, LDO retention volt=%d uV, Vref func=%d + %d*(val), cluster-num=%d\n",
+		   kvreg->volt, kvreg->retention_volt,
+		   kvreg->vref_func_min_volt,
+		   kvreg->vref_func_step_volt,
+		   kvreg->cluster_num);
+
+	return rc;
+}
+
+static int kryo_regulator_remove(struct platform_device *pdev)
+{
+	struct kryo_regulator *kvreg = platform_get_drvdata(pdev);
+
+	mutex_lock(&kryo_regulator_list_mutex);
+	list_del(&kvreg->link);
+	mutex_unlock(&kryo_regulator_list_mutex);
+
+	cpu_pm_unregister_notifier(&kvreg->cpu_pm_notifier);
+	regulator_unregister(kvreg->rdev);
+	platform_set_drvdata(pdev, NULL);
+	kryo_debugfs_deinit(kvreg);
+
+	return 0;
+}
+
+static const struct of_device_id kryo_regulator_match_table[] = {
+	{ .compatible = "qcom,kryo-regulator", },
+	{}
+};
+
+static struct platform_driver kryo_regulator_driver = {
+	.probe	= kryo_regulator_probe,
+	.remove	= kryo_regulator_remove,
+	.driver	= {
+		.name		= KRYO_REGULATOR_DRIVER_NAME,
+		.of_match_table	= kryo_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+};
+
+static int __init kryo_regulator_init(void)
+{
+	kryo_debugfs_base_init();
+	return platform_driver_register(&kryo_regulator_driver);
+}
+
+static void __exit kryo_regulator_exit(void)
+{
+	platform_driver_unregister(&kryo_regulator_driver);
+	kryo_debugfs_base_remove();
+}
+
+MODULE_DESCRIPTION("Kryo regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(kryo_regulator_init);
+module_exit(kryo_regulator_exit);
diff --git a/drivers/regulator/mem-acc-regulator.c b/drivers/regulator/mem-acc-regulator.c
new file mode 100644
index 0000000..4c03dec
--- /dev/null
+++ b/drivers/regulator/mem-acc-regulator.c
@@ -0,0 +1,1387 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#define pr_fmt(fmt)	"ACC: %s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/string.h>
+#include <soc/qcom/scm.h>
+
+#define MEM_ACC_DEFAULT_SEL_SIZE	2
+
+#define BYTES_PER_FUSE_ROW		8
+
+/* mem-acc config flags */
+
+enum {
+	MEM_ACC_USE_CORNER_ACC_MAP	= BIT(0),
+	MEM_ACC_USE_ADDR_VAL_MAP	= BIT(1),
+};
+
+#define FUSE_MAP_NO_MATCH		(-1)
+#define FUSE_PARAM_MATCH_ANY		(-1)
+#define PARAM_MATCH_ANY			(-1)
+
+enum {
+	MEMORY_L1,
+	MEMORY_L2,
+	MEMORY_MAX,
+};
+
+#define MEM_ACC_TYPE_MAX		6
+
+/**
+ * struct acc_reg_value - Acc register configuration structure
+ * @addr_index:	An index in to phys_reg_addr_list and remap_reg_addr_list
+ *		to get the ACC register physical address and remapped address.
+ * @reg_val:	Value to program in to the register mapped by addr_index.
+ */
+struct acc_reg_value {
+	u32		addr_index;
+	u32		reg_val;
+};
+
+struct corner_acc_reg_config {
+	struct acc_reg_value	*reg_config_list;
+	int			max_reg_config_len;
+};
+
+struct mem_acc_regulator {
+	struct device		*dev;
+	struct regulator_desc	rdesc;
+	struct regulator_dev	*rdev;
+
+	int			corner;
+	bool			mem_acc_supported[MEMORY_MAX];
+	bool			mem_acc_custom_supported[MEMORY_MAX];
+
+	u32			*acc_sel_mask[MEMORY_MAX];
+	u32			*acc_sel_bit_pos[MEMORY_MAX];
+	u32			acc_sel_bit_size[MEMORY_MAX];
+	u32			num_acc_sel[MEMORY_MAX];
+	u32			*acc_en_bit_pos;
+	u32			num_acc_en;
+	u32			*corner_acc_map;
+	u32			num_corners;
+	u32			override_fuse_value;
+	int			override_map_match;
+	int			override_map_count;
+
+
+	void __iomem		*acc_sel_base[MEMORY_MAX];
+	void __iomem		*acc_en_base;
+	phys_addr_t		acc_sel_addr[MEMORY_MAX];
+	phys_addr_t		acc_en_addr;
+	u32			flags;
+
+	void __iomem		*acc_custom_addr[MEMORY_MAX];
+	u32			*acc_custom_data[MEMORY_MAX];
+
+	phys_addr_t		mem_acc_type_addr[MEM_ACC_TYPE_MAX];
+	u32			*mem_acc_type_data;
+
+	/* eFuse parameters */
+	phys_addr_t		efuse_addr;
+	void __iomem		*efuse_base;
+
+	u32			num_acc_reg;
+	u32			*phys_reg_addr_list;
+	void __iomem		**remap_reg_addr_list;
+	struct corner_acc_reg_config	*corner_acc_reg_config;
+};
+
+static DEFINE_MUTEX(mem_acc_memory_mutex);
+
+static u64 mem_acc_read_efuse_row(struct mem_acc_regulator *mem_acc_vreg,
+					u32 row_num, bool use_tz_api)
+{
+	int rc;
+	u64 efuse_bits;
+	struct scm_desc desc = {0};
+	struct mem_acc_read_req {
+		u32 row_address;
+		int addr_type;
+	} req;
+
+	struct mem_acc_read_rsp {
+		u32 row_data[2];
+		u32 status;
+	} rsp;
+
+	if (!use_tz_api) {
+		efuse_bits = readq_relaxed(mem_acc_vreg->efuse_base
+			+ row_num * BYTES_PER_FUSE_ROW);
+		return efuse_bits;
+	}
+
+	desc.args[0] = req.row_address = mem_acc_vreg->efuse_addr +
+					row_num * BYTES_PER_FUSE_ROW;
+	desc.args[1] = req.addr_type = 0;
+	desc.arginfo = SCM_ARGS(2);
+	efuse_bits = 0;
+
+	if (!is_scm_armv8()) {
+		rc = scm_call(SCM_SVC_FUSE, SCM_FUSE_READ,
+			&req, sizeof(req), &rsp, sizeof(rsp));
+	} else {
+		rc = scm_call2(SCM_SIP_FNID(SCM_SVC_FUSE, SCM_FUSE_READ),
+				&desc);
+		rsp.row_data[0] = desc.ret[0];
+		rsp.row_data[1] = desc.ret[1];
+		rsp.status = desc.ret[2];
+	}
+
+	if (rc) {
+		pr_err("read row %d failed, err code = %d", row_num, rc);
+	} else {
+		efuse_bits = ((u64)(rsp.row_data[1]) << 32) +
+				(u64)rsp.row_data[0];
+	}
+
+	return efuse_bits;
+}
+
+static inline u32 apc_to_acc_corner(struct mem_acc_regulator *mem_acc_vreg,
+								int corner)
+{
+	/*
+	 * corner_acc_map maps the corner from index 0 and  APC corner value
+	 * starts from the value 1
+	 */
+	return mem_acc_vreg->corner_acc_map[corner - 1];
+}
+
+static void __update_acc_sel(struct mem_acc_regulator *mem_acc_vreg,
+						int corner, int mem_type)
+{
+	u32 acc_data, acc_data_old, i, bit, acc_corner;
+
+	acc_data = readl_relaxed(mem_acc_vreg->acc_sel_base[mem_type]);
+	acc_data_old = acc_data;
+	for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
+		bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
+		acc_data &= ~mem_acc_vreg->acc_sel_mask[mem_type][i];
+		acc_corner = apc_to_acc_corner(mem_acc_vreg, corner);
+		acc_data |= (acc_corner << bit) &
+			mem_acc_vreg->acc_sel_mask[mem_type][i];
+	}
+	pr_debug("corner=%d old_acc_sel=0x%02x new_acc_sel=0x%02x mem_type=%d\n",
+			corner, acc_data_old, acc_data, mem_type);
+	writel_relaxed(acc_data, mem_acc_vreg->acc_sel_base[mem_type]);
+}
+
+static void __update_acc_type(struct mem_acc_regulator *mem_acc_vreg,
+				int corner)
+{
+	int i, rc;
+
+	for (i = 0; i < MEM_ACC_TYPE_MAX; i++) {
+		if (mem_acc_vreg->mem_acc_type_addr[i]) {
+			rc = scm_io_write(mem_acc_vreg->mem_acc_type_addr[i],
+				mem_acc_vreg->mem_acc_type_data[corner - 1 + i *
+				mem_acc_vreg->num_corners]);
+			if (rc)
+				pr_err("scm_io_write: %pa failure rc:%d\n",
+					&(mem_acc_vreg->mem_acc_type_addr[i]),
+					rc);
+		}
+	}
+}
+
+static void __update_acc_custom(struct mem_acc_regulator *mem_acc_vreg,
+						int corner, int mem_type)
+{
+	writel_relaxed(
+		mem_acc_vreg->acc_custom_data[mem_type][corner-1],
+		mem_acc_vreg->acc_custom_addr[mem_type]);
+	pr_debug("corner=%d mem_type=%d custom_data=0x%2x\n", corner,
+		mem_type, mem_acc_vreg->acc_custom_data[mem_type][corner-1]);
+}
+
+static void update_acc_sel(struct mem_acc_regulator *mem_acc_vreg, int corner)
+{
+	int i;
+
+	for (i = 0; i < MEMORY_MAX; i++) {
+		if (mem_acc_vreg->mem_acc_supported[i])
+			__update_acc_sel(mem_acc_vreg, corner, i);
+		if (mem_acc_vreg->mem_acc_custom_supported[i])
+			__update_acc_custom(mem_acc_vreg, corner, i);
+	}
+
+	if (mem_acc_vreg->mem_acc_type_data)
+		__update_acc_type(mem_acc_vreg, corner);
+}
+
+static void update_acc_reg(struct mem_acc_regulator *mem_acc_vreg, int corner)
+{
+	struct corner_acc_reg_config *corner_acc_reg_config;
+	struct acc_reg_value *reg_config_list;
+	int i, index;
+	u32 addr_index, reg_val;
+
+	corner_acc_reg_config =
+		&mem_acc_vreg->corner_acc_reg_config[mem_acc_vreg->corner];
+	reg_config_list = corner_acc_reg_config->reg_config_list;
+	for (i = 0; i < corner_acc_reg_config->max_reg_config_len; i++) {
+		/*
+		 * Use (corner - 1) in the below equation as
+		 * the reg_config_list[] stores the values starting from
+		 * index '0' where as the minimum corner value allowed
+		 * in regulator framework is '1'.
+		 */
+		index = (corner - 1) * corner_acc_reg_config->max_reg_config_len
+			+ i;
+		addr_index = reg_config_list[index].addr_index;
+		reg_val = reg_config_list[index].reg_val;
+
+		if (addr_index == PARAM_MATCH_ANY)
+			break;
+
+		writel_relaxed(reg_val,
+				mem_acc_vreg->remap_reg_addr_list[addr_index]);
+		/* make sure write complete */
+		mb();
+
+		pr_debug("corner=%d register:0x%x value:0x%x\n", corner,
+			mem_acc_vreg->phys_reg_addr_list[addr_index], reg_val);
+	}
+}
+
+static int mem_acc_regulator_set_voltage(struct regulator_dev *rdev,
+		int corner, int corner_max, unsigned int *selector)
+{
+	struct mem_acc_regulator *mem_acc_vreg = rdev_get_drvdata(rdev);
+	int i;
+
+	if (corner > mem_acc_vreg->num_corners) {
+		pr_err("Invalid corner=%d requested\n", corner);
+		return -EINVAL;
+	}
+
+	pr_debug("old corner=%d, new corner=%d\n",
+			mem_acc_vreg->corner, corner);
+
+	if (corner == mem_acc_vreg->corner)
+		return 0;
+
+	/* go up or down one level at a time */
+	mutex_lock(&mem_acc_memory_mutex);
+
+	if (mem_acc_vreg->flags & MEM_ACC_USE_ADDR_VAL_MAP) {
+		update_acc_reg(mem_acc_vreg, corner);
+	} else if (mem_acc_vreg->flags & MEM_ACC_USE_CORNER_ACC_MAP) {
+		if (corner > mem_acc_vreg->corner) {
+			for (i = mem_acc_vreg->corner + 1; i <= corner; i++) {
+				pr_debug("UP: to corner %d\n", i);
+				update_acc_sel(mem_acc_vreg, i);
+			}
+		} else {
+			for (i = mem_acc_vreg->corner - 1; i >= corner; i--) {
+				pr_debug("DOWN: to corner %d\n", i);
+				update_acc_sel(mem_acc_vreg, i);
+			}
+		}
+	}
+
+	mutex_unlock(&mem_acc_memory_mutex);
+
+	pr_debug("new voltage corner set %d\n", corner);
+
+	mem_acc_vreg->corner = corner;
+
+	return 0;
+}
+
+static int mem_acc_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct mem_acc_regulator *mem_acc_vreg = rdev_get_drvdata(rdev);
+
+	return mem_acc_vreg->corner;
+}
+
+static struct regulator_ops mem_acc_corner_ops = {
+	.set_voltage		= mem_acc_regulator_set_voltage,
+	.get_voltage		= mem_acc_regulator_get_voltage,
+};
+
+static int __mem_acc_sel_init(struct mem_acc_regulator *mem_acc_vreg,
+							int mem_type)
+{
+	int i;
+	u32 bit, mask;
+
+	mem_acc_vreg->acc_sel_mask[mem_type] = devm_kzalloc(mem_acc_vreg->dev,
+		mem_acc_vreg->num_acc_sel[mem_type] * sizeof(u32), GFP_KERNEL);
+	if (!mem_acc_vreg->acc_sel_mask[mem_type])
+		return -ENOMEM;
+
+	for (i = 0; i < mem_acc_vreg->num_acc_sel[mem_type]; i++) {
+		bit = mem_acc_vreg->acc_sel_bit_pos[mem_type][i];
+		mask = BIT(mem_acc_vreg->acc_sel_bit_size[mem_type]) - 1;
+		mem_acc_vreg->acc_sel_mask[mem_type][i] = mask << bit;
+	}
+
+	return 0;
+}
+
+static int mem_acc_sel_init(struct mem_acc_regulator *mem_acc_vreg)
+{
+	int i, rc;
+
+	for (i = 0; i < MEMORY_MAX; i++) {
+		if (mem_acc_vreg->mem_acc_supported[i]) {
+			rc = __mem_acc_sel_init(mem_acc_vreg, i);
+			if (rc) {
+				pr_err("Unable to initialize mem_type=%d rc=%d\n",
+					i, rc);
+				return rc;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void mem_acc_en_init(struct mem_acc_regulator *mem_acc_vreg)
+{
+	int i, bit;
+	u32 acc_data;
+
+	acc_data = readl_relaxed(mem_acc_vreg->acc_en_base);
+	pr_debug("init: acc_en_register=%x\n", acc_data);
+	for (i = 0; i < mem_acc_vreg->num_acc_en; i++) {
+		bit = mem_acc_vreg->acc_en_bit_pos[i];
+		acc_data |= BIT(bit);
+	}
+	pr_debug("final: acc_en_register=%x\n", acc_data);
+	writel_relaxed(acc_data, mem_acc_vreg->acc_en_base);
+}
+
+static int populate_acc_data(struct mem_acc_regulator *mem_acc_vreg,
+			const char *prop_name, u32 **value, u32 *len)
+{
+	int rc;
+
+	if (!of_get_property(mem_acc_vreg->dev->of_node, prop_name, len)) {
+		pr_err("Unable to find %s property\n", prop_name);
+		return -EINVAL;
+	}
+	*len /= sizeof(u32);
+	if (!(*len)) {
+		pr_err("Incorrect entries in %s\n", prop_name);
+		return -EINVAL;
+	}
+
+	*value = devm_kzalloc(mem_acc_vreg->dev, (*len) * sizeof(u32),
+							GFP_KERNEL);
+	if (!(*value)) {
+		pr_err("Unable to allocate memory for %s\n", prop_name);
+		return -ENOMEM;
+	}
+
+	pr_debug("Found %s, data-length = %d\n", prop_name, *len);
+
+	rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
+					prop_name, *value, *len);
+	if (rc) {
+		pr_err("Unable to populate %s rc=%d\n", prop_name, rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int mem_acc_sel_setup(struct mem_acc_regulator *mem_acc_vreg,
+			struct resource *res, int mem_type)
+{
+	int len, rc;
+	char *mem_select_str;
+	char *mem_select_size_str;
+
+	mem_acc_vreg->acc_sel_addr[mem_type] = res->start;
+	len = res->end - res->start + 1;
+	pr_debug("'acc_sel_addr' = %pa mem_type=%d (len=%d)\n",
+					&res->start, mem_type, len);
+
+	mem_acc_vreg->acc_sel_base[mem_type] = devm_ioremap(mem_acc_vreg->dev,
+			mem_acc_vreg->acc_sel_addr[mem_type], len);
+	if (!mem_acc_vreg->acc_sel_base[mem_type]) {
+		pr_err("Unable to map 'acc_sel_addr' %pa for mem_type=%d\n",
+			&mem_acc_vreg->acc_sel_addr[mem_type], mem_type);
+		return -EINVAL;
+	}
+
+	switch (mem_type) {
+	case MEMORY_L1:
+		mem_select_str = "qcom,acc-sel-l1-bit-pos";
+		mem_select_size_str = "qcom,acc-sel-l1-bit-size";
+		break;
+	case MEMORY_L2:
+		mem_select_str = "qcom,acc-sel-l2-bit-pos";
+		mem_select_size_str = "qcom,acc-sel-l2-bit-size";
+		break;
+	default:
+		pr_err("Invalid memory type: %d\n", mem_type);
+		return -EINVAL;
+	}
+
+	mem_acc_vreg->acc_sel_bit_size[mem_type] = MEM_ACC_DEFAULT_SEL_SIZE;
+	of_property_read_u32(mem_acc_vreg->dev->of_node, mem_select_size_str,
+			&mem_acc_vreg->acc_sel_bit_size[mem_type]);
+
+	rc = populate_acc_data(mem_acc_vreg, mem_select_str,
+			&mem_acc_vreg->acc_sel_bit_pos[mem_type],
+			&mem_acc_vreg->num_acc_sel[mem_type]);
+	if (rc)
+		pr_err("Unable to populate '%s' rc=%d\n", mem_select_str, rc);
+
+	return rc;
+}
+
+static int mem_acc_efuse_init(struct platform_device *pdev,
+				 struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct resource *res;
+	int len;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr");
+	if (!res || !res->start) {
+		mem_acc_vreg->efuse_base = NULL;
+		pr_debug("'efuse_addr' resource missing or not used.\n");
+		return 0;
+	}
+
+	mem_acc_vreg->efuse_addr = res->start;
+	len = res->end - res->start + 1;
+
+	pr_info("efuse_addr = %pa (len=0x%x)\n", &res->start, len);
+
+	mem_acc_vreg->efuse_base = devm_ioremap(&pdev->dev,
+						mem_acc_vreg->efuse_addr, len);
+	if (!mem_acc_vreg->efuse_base) {
+		pr_err("Unable to map efuse_addr %pa\n",
+				&mem_acc_vreg->efuse_addr);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int mem_acc_custom_data_init(struct platform_device *pdev,
+				 struct mem_acc_regulator *mem_acc_vreg,
+				 int mem_type)
+{
+	struct resource *res;
+	char *custom_apc_addr_str, *custom_apc_data_str;
+	int len, rc = 0;
+
+	switch (mem_type) {
+	case MEMORY_L1:
+		custom_apc_addr_str = "acc-l1-custom";
+		custom_apc_data_str = "qcom,l1-acc-custom-data";
+		break;
+	case MEMORY_L2:
+		custom_apc_addr_str = "acc-l2-custom";
+		custom_apc_data_str = "qcom,l2-acc-custom-data";
+		break;
+	default:
+		pr_err("Invalid memory type: %d\n", mem_type);
+		return -EINVAL;
+	}
+
+	if (!of_find_property(mem_acc_vreg->dev->of_node,
+				custom_apc_data_str, NULL)) {
+		pr_debug("%s custom_data not specified\n", custom_apc_data_str);
+		return 0;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						custom_apc_addr_str);
+	if (!res || !res->start) {
+		pr_debug("%s resource missing\n", custom_apc_addr_str);
+		return -EINVAL;
+	}
+
+	len = res->end - res->start + 1;
+	mem_acc_vreg->acc_custom_addr[mem_type] =
+		devm_ioremap(mem_acc_vreg->dev, res->start, len);
+	if (!mem_acc_vreg->acc_custom_addr[mem_type]) {
+		pr_err("Unable to map %s %pa\n",
+			custom_apc_addr_str, &res->start);
+		return -EINVAL;
+	}
+
+	rc = populate_acc_data(mem_acc_vreg, custom_apc_data_str,
+				&mem_acc_vreg->acc_custom_data[mem_type], &len);
+	if (rc) {
+		pr_err("Unable to find %s rc=%d\n", custom_apc_data_str, rc);
+		return rc;
+	}
+
+	if (mem_acc_vreg->num_corners != len) {
+		pr_err("Custom data is not present for all the corners\n");
+		return -EINVAL;
+	}
+
+	mem_acc_vreg->mem_acc_custom_supported[mem_type] = true;
+
+	return 0;
+}
+
+static int override_mem_acc_custom_data(struct platform_device *pdev,
+				 struct mem_acc_regulator *mem_acc_vreg,
+				 int mem_type)
+{
+	char *custom_apc_data_str;
+	int len, rc = 0, i;
+	int tuple_count, tuple_match;
+	u32 index = 0, value = 0;
+
+	switch (mem_type) {
+	case MEMORY_L1:
+		custom_apc_data_str = "qcom,override-l1-acc-custom-data";
+		break;
+	case MEMORY_L2:
+		custom_apc_data_str = "qcom,override-l2-acc-custom-data";
+		break;
+	default:
+		pr_err("Invalid memory type: %d\n", mem_type);
+		return -EINVAL;
+	}
+
+	if (!of_find_property(mem_acc_vreg->dev->of_node,
+				custom_apc_data_str, &len)) {
+		pr_debug("%s not specified\n", custom_apc_data_str);
+		return 0;
+	}
+
+	if (mem_acc_vreg->override_map_count) {
+		if (mem_acc_vreg->override_map_match == FUSE_MAP_NO_MATCH)
+			return 0;
+		tuple_count = mem_acc_vreg->override_map_count;
+		tuple_match = mem_acc_vreg->override_map_match;
+	} else {
+		tuple_count = 1;
+		tuple_match = 0;
+	}
+
+	if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) {
+		pr_err("%s length=%d is invalid\n", custom_apc_data_str, len);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < mem_acc_vreg->num_corners; i++) {
+		index = (tuple_match * mem_acc_vreg->num_corners) + i;
+		rc = of_property_read_u32_index(mem_acc_vreg->dev->of_node,
+					custom_apc_data_str, index, &value);
+		if (rc) {
+			pr_err("Unable read %s index %u, rc=%d\n",
+					custom_apc_data_str, index, rc);
+			return rc;
+		}
+		mem_acc_vreg->acc_custom_data[mem_type][i] = value;
+	}
+
+	return 0;
+}
+
+static int mem_acc_override_corner_map(struct mem_acc_regulator *mem_acc_vreg)
+{
+	int len = 0, i, rc;
+	int tuple_count, tuple_match;
+	u32 index = 0, value = 0;
+	char *prop_str = "qcom,override-corner-acc-map";
+
+	if (!of_find_property(mem_acc_vreg->dev->of_node, prop_str, &len))
+		return 0;
+
+	if (mem_acc_vreg->override_map_count) {
+		if (mem_acc_vreg->override_map_match ==	FUSE_MAP_NO_MATCH)
+			return 0;
+		tuple_count = mem_acc_vreg->override_map_count;
+		tuple_match = mem_acc_vreg->override_map_match;
+	} else {
+		tuple_count = 1;
+		tuple_match = 0;
+	}
+
+	if (len != mem_acc_vreg->num_corners * tuple_count * sizeof(u32)) {
+		pr_err("%s length=%d is invalid\n", prop_str, len);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < mem_acc_vreg->num_corners; i++) {
+		index = (tuple_match * mem_acc_vreg->num_corners) + i;
+		rc = of_property_read_u32_index(mem_acc_vreg->dev->of_node,
+						prop_str, index, &value);
+		if (rc) {
+			pr_err("Unable read %s index %u, rc=%d\n",
+						prop_str, index, rc);
+			return rc;
+		}
+		mem_acc_vreg->corner_acc_map[i] = value;
+	}
+
+	return 0;
+
+}
+
+static int mem_acc_find_override_map_match(struct platform_device *pdev,
+				 struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = pdev->dev.of_node;
+	int i, rc, tuple_size;
+	int len = 0;
+	u32 *tmp;
+	char *prop_str = "qcom,override-fuse-version-map";
+
+	/* Specify default no match case. */
+	mem_acc_vreg->override_map_match = FUSE_MAP_NO_MATCH;
+	mem_acc_vreg->override_map_count = 0;
+
+	if (!of_find_property(of_node, prop_str, &len)) {
+		/* No mapping present. */
+		return 0;
+	}
+
+	tuple_size = 1;
+	mem_acc_vreg->override_map_count = len / (sizeof(u32) * tuple_size);
+
+	if (len == 0 || len % (sizeof(u32) * tuple_size)) {
+		pr_err("%s length=%d is invalid\n", prop_str, len);
+		return -EINVAL;
+	}
+
+	tmp = kzalloc(len, GFP_KERNEL);
+	if (!tmp)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, prop_str, tmp,
+			mem_acc_vreg->override_map_count * tuple_size);
+	if (rc) {
+		pr_err("could not read %s rc=%d\n", prop_str, rc);
+		goto done;
+	}
+
+	for (i = 0; i < mem_acc_vreg->override_map_count; i++) {
+		if (tmp[i * tuple_size] != mem_acc_vreg->override_fuse_value
+		    && tmp[i * tuple_size] != FUSE_PARAM_MATCH_ANY) {
+			continue;
+		} else {
+			mem_acc_vreg->override_map_match = i;
+			break;
+		}
+	}
+
+	if (mem_acc_vreg->override_map_match != FUSE_MAP_NO_MATCH)
+		pr_debug("%s tuple match found: %d\n", prop_str,
+				mem_acc_vreg->override_map_match);
+	else
+		pr_err("%s tuple match not found\n", prop_str);
+
+done:
+	kfree(tmp);
+	return rc;
+}
+
+#define MAX_CHARS_PER_INT	20
+
+static int mem_acc_reg_addr_val_dump(struct mem_acc_regulator *mem_acc_vreg,
+			struct corner_acc_reg_config *corner_acc_reg_config,
+			u32 corner)
+{
+	int i, k, index, pos = 0;
+	u32 addr_index;
+	size_t buflen;
+	char *buf;
+	struct acc_reg_value *reg_config_list =
+					corner_acc_reg_config->reg_config_list;
+	int max_reg_config_len = corner_acc_reg_config->max_reg_config_len;
+	int num_corners = mem_acc_vreg->num_corners;
+
+	/*
+	 * Log register and value mapping since they are useful for
+	 * baseline MEM ACC logging.
+	 */
+	buflen = max_reg_config_len * (MAX_CHARS_PER_INT + 6) * sizeof(*buf);
+	buf = kzalloc(buflen, GFP_KERNEL);
+	if (buf == NULL) {
+		pr_err("Could not allocate memory for acc register and value logging\n");
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < num_corners; i++) {
+		if (corner == i + 1)
+			continue;
+
+		pr_debug("Corner: %d --> %d:\n", corner, i + 1);
+		pos = 0;
+		for (k = 0; k < max_reg_config_len; k++) {
+			index = i * max_reg_config_len + k;
+			addr_index = reg_config_list[index].addr_index;
+			if (addr_index == PARAM_MATCH_ANY)
+				break;
+
+			pos += scnprintf(buf + pos, buflen - pos,
+				"<0x%x 0x%x> ",
+				mem_acc_vreg->phys_reg_addr_list[addr_index],
+				reg_config_list[index].reg_val);
+		}
+		buf[pos] = '\0';
+		pr_debug("%s\n", buf);
+	}
+
+	kfree(buf);
+	return 0;
+}
+
+static int mem_acc_get_reg_addr_val(struct device_node *of_node,
+		const char *prop_str, struct acc_reg_value *reg_config_list,
+		int list_offset, int list_size, u32 max_reg_index)
+{
+
+	int i, index, rc  = 0;
+
+	for (i = 0; i < list_size / 2; i++) {
+		index = (list_offset * list_size) + i * 2;
+		rc = of_property_read_u32_index(of_node, prop_str, index,
+					&reg_config_list[i].addr_index);
+		rc |= of_property_read_u32_index(of_node, prop_str, index + 1,
+					&reg_config_list[i].reg_val);
+		if (rc) {
+			pr_err("could not read %s at tuple %u: rc=%d\n",
+				prop_str, index, rc);
+			return rc;
+		}
+
+		if (reg_config_list[i].addr_index == PARAM_MATCH_ANY)
+			continue;
+
+		if ((!reg_config_list[i].addr_index) ||
+			reg_config_list[i].addr_index > max_reg_index) {
+			pr_err("Invalid register index %u in %s at tuple %u\n",
+				reg_config_list[i].addr_index, prop_str, index);
+			return -EINVAL;
+		}
+	}
+
+	return rc;
+}
+
+static int mem_acc_init_reg_config(struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	int i, size, len = 0, rc = 0;
+	u32 addr_index, reg_val, index;
+	char *prop_str = "qcom,acc-init-reg-config";
+
+	if (!of_find_property(of_node, prop_str, &len)) {
+		/* Initial acc register configuration not specified */
+		return rc;
+	}
+
+	size = len / sizeof(u32);
+	if ((!size) || (size % 2)) {
+		pr_err("%s specified with invalid length: %d\n",
+			prop_str, size);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size / 2; i++) {
+		index = i * 2;
+		rc = of_property_read_u32_index(of_node, prop_str, index,
+						&addr_index);
+		rc |= of_property_read_u32_index(of_node, prop_str, index + 1,
+						&reg_val);
+		if (rc) {
+			pr_err("could not read %s at tuple %u: rc=%d\n",
+				prop_str, index, rc);
+			return rc;
+		}
+
+		if ((!addr_index) || addr_index > mem_acc_vreg->num_acc_reg) {
+			pr_err("Invalid register index %u in %s at tuple %u\n",
+				addr_index, prop_str, index);
+			return -EINVAL;
+		}
+
+		writel_relaxed(reg_val,
+				mem_acc_vreg->remap_reg_addr_list[addr_index]);
+		/* make sure write complete */
+		mb();
+
+		pr_debug("acc initial config: register:0x%x value:0x%x\n",
+			mem_acc_vreg->phys_reg_addr_list[addr_index], reg_val);
+	}
+
+	return rc;
+}
+
+static int mem_acc_get_reg_addr(struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	void __iomem **remap_reg_addr_list;
+	u32 *phys_reg_addr_list;
+	int i, num_acc_reg, len = 0, rc = 0;
+
+	if (!of_find_property(of_node, "qcom,acc-reg-addr-list", &len)) {
+		/* acc register address list not specified */
+		return rc;
+	}
+
+	num_acc_reg = len / sizeof(u32);
+	if (!num_acc_reg) {
+		pr_err("qcom,acc-reg-addr-list has invalid len = %d\n", len);
+		return -EINVAL;
+	}
+
+	phys_reg_addr_list = devm_kcalloc(mem_acc_vreg->dev, num_acc_reg + 1,
+				sizeof(*phys_reg_addr_list), GFP_KERNEL);
+	if (!phys_reg_addr_list)
+		return -ENOMEM;
+
+	remap_reg_addr_list = devm_kcalloc(mem_acc_vreg->dev, num_acc_reg + 1,
+				sizeof(*remap_reg_addr_list), GFP_KERNEL);
+	if (!remap_reg_addr_list)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,acc-reg-addr-list",
+					&phys_reg_addr_list[1], num_acc_reg);
+	if (rc) {
+		pr_err("Read- qcom,acc-reg-addr-list failed: rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 1; i <= num_acc_reg; i++) {
+		remap_reg_addr_list[i] = devm_ioremap(mem_acc_vreg->dev,
+						phys_reg_addr_list[i], 0x4);
+		if (!remap_reg_addr_list[i]) {
+			pr_err("Unable to map register address 0x%x\n",
+					phys_reg_addr_list[i]);
+			return -EINVAL;
+		}
+	}
+
+	mem_acc_vreg->num_acc_reg = num_acc_reg;
+	mem_acc_vreg->phys_reg_addr_list = phys_reg_addr_list;
+	mem_acc_vreg->remap_reg_addr_list = remap_reg_addr_list;
+
+	return rc;
+}
+
+static int mem_acc_reg_config_init(struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	struct acc_reg_value *reg_config_list;
+	int len, size, rc, i, num_corners;
+	struct property *prop;
+	char prop_str[30];
+	struct corner_acc_reg_config *corner_acc_reg_config;
+
+	rc = of_property_read_u32(of_node, "qcom,num-acc-corners",
+				&num_corners);
+	if (rc) {
+		pr_err("could not read qcom,num-acc-corners: rc=%d\n", rc);
+		return rc;
+	}
+
+	mem_acc_vreg->num_corners = num_corners;
+
+	rc = of_property_read_u32(of_node, "qcom,boot-acc-corner",
+				&mem_acc_vreg->corner);
+	if (rc) {
+		pr_err("could not read qcom,boot-acc-corner: rc=%d\n", rc);
+		return rc;
+	}
+	pr_debug("boot acc corner = %d\n", mem_acc_vreg->corner);
+
+	corner_acc_reg_config = devm_kcalloc(mem_acc_vreg->dev, num_corners + 1,
+						sizeof(*corner_acc_reg_config),
+						GFP_KERNEL);
+	if (!corner_acc_reg_config)
+		return -ENOMEM;
+
+	for (i = 1; i <= num_corners; i++) {
+		snprintf(prop_str, sizeof(prop_str),
+				"qcom,corner%d-reg-config", i);
+		prop = of_find_property(of_node, prop_str, &len);
+		size = len / sizeof(u32);
+		if ((!prop) || (!size) || size < (num_corners * 2)) {
+			pr_err("%s property is missed or invalid length: len=%d\n",
+				prop_str, len);
+			return -EINVAL;
+		}
+
+		reg_config_list = devm_kcalloc(mem_acc_vreg->dev, size / 2,
+					sizeof(*reg_config_list), GFP_KERNEL);
+		if (!reg_config_list)
+			return -ENOMEM;
+
+		rc = mem_acc_get_reg_addr_val(of_node, prop_str,
+						reg_config_list, 0, size,
+						mem_acc_vreg->num_acc_reg);
+		if (rc) {
+			pr_err("Failed to read %s property: rc=%d\n",
+				prop_str, rc);
+			return rc;
+		}
+
+		corner_acc_reg_config[i].max_reg_config_len =
+						size / (num_corners * 2);
+		corner_acc_reg_config[i].reg_config_list = reg_config_list;
+
+		rc = mem_acc_reg_addr_val_dump(mem_acc_vreg,
+						&corner_acc_reg_config[i], i);
+		if (rc) {
+			pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	mem_acc_vreg->corner_acc_reg_config = corner_acc_reg_config;
+	mem_acc_vreg->flags |= MEM_ACC_USE_ADDR_VAL_MAP;
+	return rc;
+}
+
+static int mem_acc_override_reg_addr_val_init(
+			struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = mem_acc_vreg->dev->of_node;
+	struct corner_acc_reg_config *corner_acc_reg_config;
+	struct acc_reg_value *override_reg_config_list;
+	int i, tuple_count, tuple_match, len = 0, rc = 0;
+	u32 list_size, override_max_reg_config_len;
+	char prop_str[40];
+	struct property *prop;
+	int num_corners = mem_acc_vreg->num_corners;
+
+	if (!mem_acc_vreg->corner_acc_reg_config)
+		return 0;
+
+	if (mem_acc_vreg->override_map_count) {
+		if (mem_acc_vreg->override_map_match ==	FUSE_MAP_NO_MATCH)
+			return 0;
+		tuple_count = mem_acc_vreg->override_map_count;
+		tuple_match = mem_acc_vreg->override_map_match;
+	} else {
+		tuple_count = 1;
+		tuple_match = 0;
+	}
+
+	corner_acc_reg_config = mem_acc_vreg->corner_acc_reg_config;
+	for (i = 1; i <= num_corners; i++) {
+		snprintf(prop_str, sizeof(prop_str),
+				"qcom,override-corner%d-addr-val-map", i);
+		prop = of_find_property(of_node, prop_str, &len);
+		list_size = len / (tuple_count * sizeof(u32));
+		if (!prop) {
+			pr_debug("%s property not specified\n", prop_str);
+			continue;
+		}
+
+		if ((!list_size) || list_size < (num_corners * 2)) {
+			pr_err("qcom,override-corner%d-addr-val-map property is missed or invalid length: len=%d\n",
+			i, len);
+			return -EINVAL;
+		}
+
+		override_max_reg_config_len = list_size / (num_corners * 2);
+		override_reg_config_list =
+				corner_acc_reg_config[i].reg_config_list;
+
+		if (corner_acc_reg_config[i].max_reg_config_len
+					!= override_max_reg_config_len) {
+			/* Free already allocate memory */
+			devm_kfree(mem_acc_vreg->dev, override_reg_config_list);
+
+			/* Allocated memory for new requirement */
+			override_reg_config_list =
+				devm_kcalloc(mem_acc_vreg->dev,
+				override_max_reg_config_len * num_corners,
+				sizeof(*override_reg_config_list), GFP_KERNEL);
+			if (!override_reg_config_list)
+				return -ENOMEM;
+
+			corner_acc_reg_config[i].max_reg_config_len =
+						override_max_reg_config_len;
+			corner_acc_reg_config[i].reg_config_list =
+						override_reg_config_list;
+		}
+
+		rc = mem_acc_get_reg_addr_val(of_node, prop_str,
+					override_reg_config_list, tuple_match,
+					list_size, mem_acc_vreg->num_acc_reg);
+		if (rc) {
+			pr_err("Failed to read %s property: rc=%d\n",
+				prop_str, rc);
+			return rc;
+		}
+
+		rc = mem_acc_reg_addr_val_dump(mem_acc_vreg,
+						&corner_acc_reg_config[i], i);
+		if (rc) {
+			pr_err("could not dump acc address-value dump for corner=%d: rc=%d\n",
+				i, rc);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+#define MEM_TYPE_STRING_LEN	20
+static int mem_acc_init(struct platform_device *pdev,
+		struct mem_acc_regulator *mem_acc_vreg)
+{
+	struct device_node *of_node = pdev->dev.of_node;
+	struct resource *res;
+	int len, rc, i, j;
+	u32 fuse_sel[4];
+	u64 fuse_bits;
+	bool acc_type_present = false;
+	char tmps[MEM_TYPE_STRING_LEN];
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-en");
+	if (!res || !res->start) {
+		pr_debug("'acc-en' resource missing or not used.\n");
+	} else {
+		mem_acc_vreg->acc_en_addr = res->start;
+		len = res->end - res->start + 1;
+		pr_debug("'acc_en_addr' = %pa (len=0x%x)\n", &res->start, len);
+
+		mem_acc_vreg->acc_en_base = devm_ioremap(mem_acc_vreg->dev,
+				mem_acc_vreg->acc_en_addr, len);
+		if (!mem_acc_vreg->acc_en_base) {
+			pr_err("Unable to map 'acc_en_addr' %pa\n",
+					&mem_acc_vreg->acc_en_addr);
+			return -EINVAL;
+		}
+
+		rc = populate_acc_data(mem_acc_vreg, "qcom,acc-en-bit-pos",
+				&mem_acc_vreg->acc_en_bit_pos,
+				&mem_acc_vreg->num_acc_en);
+		if (rc) {
+			pr_err("Unable to populate 'qcom,acc-en-bit-pos' rc=%d\n",
+					rc);
+			return rc;
+		}
+	}
+
+	rc = mem_acc_efuse_init(pdev, mem_acc_vreg);
+	if (rc) {
+		pr_err("Wrong eFuse address specified: rc=%d\n", rc);
+		return rc;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l1");
+	if (!res || !res->start) {
+		pr_debug("'acc-sel-l1' resource missing or not used.\n");
+	} else {
+		rc = mem_acc_sel_setup(mem_acc_vreg, res, MEMORY_L1);
+		if (rc) {
+			pr_err("Unable to setup mem-acc for mem_type=%d rc=%d\n",
+					MEMORY_L1, rc);
+			return rc;
+		}
+		mem_acc_vreg->mem_acc_supported[MEMORY_L1] = true;
+	}
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "acc-sel-l2");
+	if (!res || !res->start) {
+		pr_debug("'acc-sel-l2' resource missing or not used.\n");
+	} else {
+		rc = mem_acc_sel_setup(mem_acc_vreg, res, MEMORY_L2);
+		if (rc) {
+			pr_err("Unable to setup mem-acc for mem_type=%d rc=%d\n",
+					MEMORY_L2, rc);
+			return rc;
+		}
+		mem_acc_vreg->mem_acc_supported[MEMORY_L2] = true;
+	}
+
+	for (i = 0; i < MEM_ACC_TYPE_MAX; i++) {
+		snprintf(tmps, MEM_TYPE_STRING_LEN, "mem-acc-type%d", i + 1);
+		res = platform_get_resource_byname(pdev, IORESOURCE_MEM, tmps);
+
+		if (!res || !res->start) {
+			pr_debug("'%s' resource missing or not used.\n", tmps);
+		} else {
+			mem_acc_vreg->mem_acc_type_addr[i] = res->start;
+			acc_type_present = true;
+		}
+	}
+
+	rc = mem_acc_get_reg_addr(mem_acc_vreg);
+	if (rc) {
+		pr_err("Unable to get acc register addresses: rc=%d\n", rc);
+		return rc;
+	}
+
+	if (mem_acc_vreg->phys_reg_addr_list) {
+		rc = mem_acc_reg_config_init(mem_acc_vreg);
+		if (rc) {
+			pr_err("acc register address-value map failed: rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	if (of_find_property(of_node, "qcom,corner-acc-map", NULL)) {
+		rc = populate_acc_data(mem_acc_vreg, "qcom,corner-acc-map",
+			&mem_acc_vreg->corner_acc_map,
+			&mem_acc_vreg->num_corners);
+
+		/* Check if at least one valid mem-acc config. is specified */
+		for (i = 0; i < MEMORY_MAX; i++) {
+			if (mem_acc_vreg->mem_acc_supported[i])
+				break;
+		}
+		if (i == MEMORY_MAX && !acc_type_present) {
+			pr_err("No mem-acc configuration specified\n");
+			return -EINVAL;
+		}
+
+		mem_acc_vreg->flags |= MEM_ACC_USE_CORNER_ACC_MAP;
+	}
+
+	if ((mem_acc_vreg->flags & MEM_ACC_USE_CORNER_ACC_MAP) &&
+		(mem_acc_vreg->flags & MEM_ACC_USE_ADDR_VAL_MAP)) {
+		pr_err("Invalid configuration, both qcom,corner-acc-map and qcom,cornerX-addr-val-map specified\n");
+		return -EINVAL;
+	}
+
+	pr_debug("num_corners = %d\n", mem_acc_vreg->num_corners);
+
+	if (mem_acc_vreg->num_acc_en)
+		mem_acc_en_init(mem_acc_vreg);
+
+	if (mem_acc_vreg->phys_reg_addr_list) {
+		rc = mem_acc_init_reg_config(mem_acc_vreg);
+		if (rc) {
+			pr_err("acc initial register configuration failed: rc=%d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	rc = mem_acc_sel_init(mem_acc_vreg);
+	if (rc) {
+		pr_err("Unable to initialize mem_acc_sel reg rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < MEMORY_MAX; i++) {
+		rc = mem_acc_custom_data_init(pdev, mem_acc_vreg, i);
+		if (rc) {
+			pr_err("Unable to initialize custom data for mem_type=%d rc=%d\n",
+					i, rc);
+			return rc;
+		}
+	}
+
+	if (of_find_property(mem_acc_vreg->dev->of_node,
+				"qcom,override-acc-fuse-sel", NULL)) {
+		rc = of_property_read_u32_array(mem_acc_vreg->dev->of_node,
+			"qcom,override-acc-fuse-sel", fuse_sel, 4);
+		if (rc < 0) {
+			pr_err("Read failed - qcom,override-acc-fuse-sel rc=%d\n",
+					rc);
+			return rc;
+		}
+
+		fuse_bits = mem_acc_read_efuse_row(mem_acc_vreg, fuse_sel[0],
+								fuse_sel[3]);
+		/*
+		 * fuse_sel[1] = LSB position in row (shift)
+		 * fuse_sel[2] = num of bits (mask)
+		 */
+		mem_acc_vreg->override_fuse_value = (fuse_bits >> fuse_sel[1]) &
+						((1 << fuse_sel[2]) - 1);
+
+		rc = mem_acc_find_override_map_match(pdev, mem_acc_vreg);
+		if (rc) {
+			pr_err("Unable to find fuse map match rc=%d\n", rc);
+			return rc;
+		}
+
+		pr_debug("override_fuse_val=%d override_map_match=%d\n",
+					mem_acc_vreg->override_fuse_value,
+					mem_acc_vreg->override_map_match);
+
+		rc = mem_acc_override_corner_map(mem_acc_vreg);
+		if (rc) {
+			pr_err("Unable to override corner map rc=%d\n", rc);
+			return rc;
+		}
+
+		rc = mem_acc_override_reg_addr_val_init(mem_acc_vreg);
+		if (rc) {
+			pr_err("Unable to override reg_config_list init rc=%d\n",
+				rc);
+			return rc;
+		}
+
+		for (i = 0; i < MEMORY_MAX; i++) {
+			rc = override_mem_acc_custom_data(pdev,
+							mem_acc_vreg, i);
+			if (rc) {
+				pr_err("Unable to override custom data for mem_type=%d rc=%d\n",
+					i, rc);
+				return rc;
+			}
+		}
+	}
+
+	if (acc_type_present) {
+		mem_acc_vreg->mem_acc_type_data = devm_kzalloc(
+			mem_acc_vreg->dev, mem_acc_vreg->num_corners *
+			MEM_ACC_TYPE_MAX * sizeof(u32), GFP_KERNEL);
+
+		if (!mem_acc_vreg->mem_acc_type_data) {
+			pr_err("Unable to allocate memory for mem_acc_type\n");
+			return -ENOMEM;
+		}
+
+		for (i = 0; i < MEM_ACC_TYPE_MAX; i++) {
+			if (mem_acc_vreg->mem_acc_type_addr[i]) {
+				snprintf(tmps, MEM_TYPE_STRING_LEN,
+					"qcom,mem-acc-type%d", i + 1);
+
+				j = i * mem_acc_vreg->num_corners;
+				rc = of_property_read_u32_array(
+					mem_acc_vreg->dev->of_node,
+					tmps,
+					&mem_acc_vreg->mem_acc_type_data[j],
+					mem_acc_vreg->num_corners);
+				if (rc) {
+					pr_err("Unable to get property %s rc=%d\n",
+						tmps, rc);
+					return rc;
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int mem_acc_regulator_probe(struct platform_device *pdev)
+{
+	struct regulator_config reg_config = {};
+	struct mem_acc_regulator *mem_acc_vreg;
+	struct regulator_desc *rdesc;
+	struct regulator_init_data *init_data;
+	int rc;
+
+	if (!pdev->dev.of_node) {
+		pr_err("Device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node,
+					NULL);
+	if (!init_data) {
+		pr_err("regulator init data is missing\n");
+		return -EINVAL;
+	}
+
+	init_data->constraints.input_uV = init_data->constraints.max_uV;
+	init_data->constraints.valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE;
+
+	mem_acc_vreg = devm_kzalloc(&pdev->dev, sizeof(*mem_acc_vreg),
+			GFP_KERNEL);
+	if (!mem_acc_vreg)
+		return -ENOMEM;
+
+	mem_acc_vreg->dev = &pdev->dev;
+
+	rc = mem_acc_init(pdev, mem_acc_vreg);
+	if (rc) {
+		pr_err("Unable to initialize mem_acc configuration rc=%d\n",
+				rc);
+		return rc;
+	}
+
+	rdesc			= &mem_acc_vreg->rdesc;
+	rdesc->owner		= THIS_MODULE;
+	rdesc->type		= REGULATOR_VOLTAGE;
+	rdesc->ops		= &mem_acc_corner_ops;
+	rdesc->name		= init_data->constraints.name;
+
+	reg_config.dev = &pdev->dev;
+	reg_config.init_data = init_data;
+	reg_config.driver_data = mem_acc_vreg;
+	reg_config.of_node = pdev->dev.of_node;
+	mem_acc_vreg->rdev = regulator_register(rdesc, &reg_config);
+	if (IS_ERR(mem_acc_vreg->rdev)) {
+		rc = PTR_ERR(mem_acc_vreg->rdev);
+		if (rc != -EPROBE_DEFER)
+			pr_err("regulator_register failed: rc=%d\n", rc);
+		return rc;
+	}
+
+	platform_set_drvdata(pdev, mem_acc_vreg);
+
+	return 0;
+}
+
+static int mem_acc_regulator_remove(struct platform_device *pdev)
+{
+	struct mem_acc_regulator *mem_acc_vreg = platform_get_drvdata(pdev);
+
+	regulator_unregister(mem_acc_vreg->rdev);
+
+	return 0;
+}
+
+static const struct of_device_id mem_acc_regulator_match_table[] = {
+	{ .compatible = "qcom,mem-acc-regulator", },
+	{}
+};
+
+static struct platform_driver mem_acc_regulator_driver = {
+	.probe		= mem_acc_regulator_probe,
+	.remove		= mem_acc_regulator_remove,
+	.driver		= {
+		.name		= "qcom,mem-acc-regulator",
+		.of_match_table = mem_acc_regulator_match_table,
+		.owner		= THIS_MODULE,
+	},
+};
+
+int __init mem_acc_regulator_init(void)
+{
+	return platform_driver_register(&mem_acc_regulator_driver);
+}
+postcore_initcall(mem_acc_regulator_init);
+
+static void __exit mem_acc_regulator_exit(void)
+{
+	platform_driver_unregister(&mem_acc_regulator_driver);
+}
+module_exit(mem_acc_regulator_exit);
+
+MODULE_DESCRIPTION("MEM-ACC-SEL regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/msm_gfx_ldo.c b/drivers/regulator/msm_gfx_ldo.c
new file mode 100644
index 0000000..2800607
--- /dev/null
+++ b/drivers/regulator/msm_gfx_ldo.c
@@ -0,0 +1,1652 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "GFX_LDO: %s: " fmt, __func__
+
+#include <linux/bitops.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/regulator/msm-ldo-regulator.h>
+
+#define LDO_ATEST_REG			0x0
+#define LDO_CFG0_REG			0x4
+#define LDO_CFG1_REG			0x8
+#define LDO_CFG2_REG			0xC
+#define LDO_LD_DATA_REG			0x10
+
+#define LDO_VREF_TEST_CFG		0x14
+#define ENABLE_LDO_STATUS_BIT		(BIT(8) | BIT(12))
+#define LDO_AUTOBYPASS_BIT		BIT(20)
+
+#define LDO_VREF_SET_REG		0x18
+#define UPDATE_VREF_BIT			BIT(31)
+#define SEL_RST_BIT			BIT(16)
+#define VREF_VAL_MASK			GENMASK(6, 0)
+
+#define PWRSWITCH_CTRL_REG		0x1C
+#define LDO_CLAMP_IO_BIT		BIT(31)
+#define CPR_BYPASS_IN_LDO_MODE_BIT	BIT(30)
+#define EN_LDOAP_CTRL_CPR_BIT		BIT(29)
+#define CX_CPR_BYPASS_IN_LDO_MODE_BIT	BIT(10)
+#define PWR_SRC_SEL_BIT			BIT(9)
+#define ACK_SW_OVR_BIT			BIT(8)
+#define LDO_PREON_SW_OVR_BIT		BIT(7)
+#define LDO_BYPASS_BIT			BIT(6)
+#define LDO_PDN_BIT			BIT(5)
+#define LDO_UNDER_SW_CTRL_BIT		BIT(4)
+#define BHS_EN_REST_BIT			BIT(2)
+#define BHS_EN_FEW_BIT			BIT(1)
+#define BHS_UNDER_SW_CTL		BIT(0)
+
+#define LDO_STATUS1_REG			0x24
+
+#define PWRSWITCH_STATUS_REG		0x28
+#define LDO_VREF_SETTLED_BIT		BIT(4)
+#define LDO_READY_BIT			BIT(2)
+#define BHS_EN_REST_ACK_BIT		BIT(1)
+
+#define REF_CURRENT_X1_REG		0x2C
+#define REF_CURRENT_X2_REG		0x30
+#define ADC_CTL_REG			0x34
+
+#define MIN_LDO_VOLTAGE			375000
+#define MAX_LDO_VOLTAGE			980000
+#define LDO_STEP_VOLATGE		5000
+
+#define MAX_LDO_REGS			11
+
+#define BYTES_PER_FUSE_ROW		8
+#define MAX_FUSE_ROW_BIT		63
+#define MIN_CORNER_OFFSET		1
+
+#define GFX_LDO_FUSE_STEP_VOLT		10000
+#define GFX_LDO_FUSE_SIZE		5
+
+enum direction {
+	NO_CHANGE,
+	UP,
+	DOWN,
+};
+
+enum voltage_handling {
+	VOLTAGE,
+	CORNER,
+};
+
+struct fuse_param {
+	unsigned int		row;
+	unsigned int		bit_start;
+	unsigned int		bit_end;
+};
+
+struct ldo_config {
+	u32 offset;
+	u32 value;
+};
+
+struct msm_gfx_ldo {
+	struct device		*dev;
+	struct regulator_desc	rdesc;
+	struct regulator_dev	*rdev;
+	struct regulator	*vdd_cx;
+	struct regulator	*mem_acc_vreg;
+	struct dentry		*debugfs;
+
+	u32			num_corners;
+	u32			num_ldo_corners;
+	u32			*open_loop_volt;
+	u32			*ceiling_volt;
+	u32			*floor_volt;
+	u32			*ldo_corner_en_map;
+	u32			*vdd_cx_corner_map;
+	u32			*mem_acc_corner_map;
+	const int		*ref_volt;
+	const struct fuse_param	*ldo_enable_param;
+	const struct fuse_param	**init_volt_param;
+	bool			ldo_fuse_enable;
+	bool			ldo_mode_disable;
+	struct ldo_config	*ldo_init_config;
+
+	void __iomem		*efuse_base;
+	phys_addr_t		efuse_addr;
+	void __iomem		*ldo_base;
+	phys_addr_t		ldo_addr;
+
+	bool			vreg_enabled;
+	enum msm_ldo_supply_mode mode;
+	u32			corner;
+	int			ldo_voltage_uv;
+	struct mutex		ldo_mutex;
+	enum voltage_handling	ops_type;
+};
+
+#define MSM8953_LDO_FUSE_CORNERS		3
+#define LDO_MAX_OFFSET				0xFFFF
+static struct ldo_config msm8953_ldo_config[] = {
+	{LDO_ATEST_REG,		0x00000203},
+	{LDO_CFG0_REG,		0x05008600},
+	{LDO_CFG1_REG,		       0x0},
+	{LDO_CFG2_REG,		0x0000C3FC},
+	{LDO_VREF_TEST_CFG,	0x004B1102},
+	{LDO_MAX_OFFSET,	LDO_MAX_OFFSET},
+};
+
+static struct ldo_config sdm660_ldo_config[] = {
+	{LDO_ATEST_REG,		0x00000080},
+	{LDO_CFG0_REG,		0x0100A600},
+	{LDO_CFG1_REG,		0x000000A0},
+	{LDO_CFG2_REG,		0x0000C3FE},
+	{LDO_LD_DATA_REG,	0x00000000},
+	{LDO_VREF_TEST_CFG,	0x00401100},
+	{REF_CURRENT_X1_REG,	0x00000230},
+	{REF_CURRENT_X2_REG,	0x00000048},
+	{ADC_CTL_REG,		0x00000000},
+	{LDO_MAX_OFFSET,	LDO_MAX_OFFSET},
+};
+
+static struct fuse_param msm8953_ldo_enable_param[] = {
+	{65, 11, 11},
+	{},
+};
+
+static const struct fuse_param
+msm8953_init_voltage_param[MSM8953_LDO_FUSE_CORNERS][2] = {
+		{ {73, 42, 46}, {} },
+		{ {73, 37, 41}, {} },
+		{ {73, 32, 36}, {} },
+};
+
+static const int msm8953_fuse_ref_volt[MSM8953_LDO_FUSE_CORNERS] = {
+	580000,
+	650000,
+	720000,
+};
+
+enum {
+	MSM8953_SOC_ID,
+	SDM660_SOC_ID,
+};
+
+static int convert_open_loop_voltage_fuse(int ref_volt, int step_volt,
+						u32 fuse, int fuse_len)
+{
+	int sign, steps;
+
+	sign = (fuse & (1 << (fuse_len - 1))) ? -1 : 1;
+	steps = fuse & ((1 << (fuse_len - 1)) - 1);
+
+	return ref_volt + sign * steps * step_volt;
+}
+
+static int read_fuse_param(void __iomem *fuse_base_addr,
+		const struct fuse_param *param, u64 *param_value)
+{
+	u64 fuse_val, val;
+	int bits;
+	int bits_total = 0;
+
+	*param_value = 0;
+
+	while (param->row || param->bit_start || param->bit_end) {
+		if (param->bit_start > param->bit_end
+		    || param->bit_end > MAX_FUSE_ROW_BIT) {
+			pr_err("Invalid fuse parameter segment: row=%u, start=%u, end=%u\n",
+				param->row, param->bit_start, param->bit_end);
+			return -EINVAL;
+		}
+
+		bits = param->bit_end - param->bit_start + 1;
+		if (bits_total + bits > 64) {
+			pr_err("Invalid fuse parameter segments; total bits = %d\n",
+				bits_total + bits);
+			return -EINVAL;
+		}
+
+		fuse_val = readq_relaxed(fuse_base_addr
+					 + param->row * BYTES_PER_FUSE_ROW);
+		val = (fuse_val >> param->bit_start) & ((1ULL << bits) - 1);
+		*param_value |= val << bits_total;
+		bits_total += bits;
+
+		param++;
+	}
+
+	return 0;
+}
+
+static enum msm_ldo_supply_mode get_operating_mode(struct msm_gfx_ldo *ldo_vreg,
+								int corner)
+{
+	if (!ldo_vreg->ldo_mode_disable && ldo_vreg->ldo_fuse_enable
+			&& ldo_vreg->ldo_corner_en_map[corner])
+		return LDO_MODE;
+
+	return BHS_MODE;
+}
+
+static char *register_str[] = {
+	"LDO_ATEST",
+	"LDO_CFG0",
+	"LDO_CFG1",
+	"LDO_CFG2",
+	"LDO_LD_DATA",
+	"LDO_VREF_TEST_CFG",
+	"LDO_VREF_SET",
+	"PWRSWITCH_CTL",
+	"LDO_STATUS0",
+	"LDO_STATUS1",
+	"PWRSWITCH_STATUS",
+};
+
+static void dump_registers(struct msm_gfx_ldo *ldo_vreg, char *func)
+{
+	u32 reg[MAX_LDO_REGS];
+	int i;
+
+	for (i = 0; i < MAX_LDO_REGS; i++) {
+		reg[i] = 0;
+		reg[i] = readl_relaxed(ldo_vreg->ldo_base + (i * 4));
+		pr_debug("%s -- %s = 0x%x\n", func, register_str[i], reg[i]);
+	}
+}
+
+#define GET_VREF(a) DIV_ROUND_UP(a - MIN_LDO_VOLTAGE, LDO_STEP_VOLATGE)
+
+static void configure_ldo_voltage(struct msm_gfx_ldo *ldo_vreg, int new_uv)
+{
+	int val = 0;
+	u32 reg = 0;
+
+	val = GET_VREF(new_uv);
+	reg = readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* set the new voltage */
+	reg &= ~VREF_VAL_MASK;
+	reg |= val & VREF_VAL_MASK;
+	writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* Initiate VREF update */
+	reg |= UPDATE_VREF_BIT;
+	writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* complete the writes */
+	mb();
+
+	reg &= ~UPDATE_VREF_BIT;
+	writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	ldo_vreg->ldo_voltage_uv = new_uv;
+
+	/* complete the write sequence */
+	mb();
+}
+
+static int ldo_update_voltage(struct msm_gfx_ldo *ldo_vreg, int new_uv)
+{
+	int timeout = 50;
+	u32 reg = 0;
+
+	configure_ldo_voltage(ldo_vreg, new_uv);
+
+	while (--timeout) {
+		reg = readl_relaxed(ldo_vreg->ldo_base +
+					PWRSWITCH_STATUS_REG);
+		if (reg & (LDO_VREF_SETTLED_BIT | LDO_READY_BIT))
+			break;
+
+		udelay(10);
+	}
+	if (!timeout) {
+		pr_err("LDO_VREF_SETTLED not set PWRSWITCH_STATUS = 0x%x\n",
+									reg);
+		return -EBUSY;
+	}
+
+	pr_debug("LDO voltage set to=%d uV VREF_REG=%x\n",
+			ldo_vreg->ldo_voltage_uv,
+			readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG));
+
+	return 0;
+}
+
+static int enable_ldo_mode(struct msm_gfx_ldo *ldo_vreg, int new_uv)
+{
+	u32 ctl = 0;
+
+	/* set the ldo-vref */
+	configure_ldo_voltage(ldo_vreg, new_uv);
+
+	/* configure the LDO for power-up */
+	ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Move BHS under SW control */
+	ctl |= BHS_UNDER_SW_CTL;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Set LDO under gdsc control */
+	ctl &= ~LDO_UNDER_SW_CTRL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* enable hw_pre-on to gdsc */
+	ctl |= LDO_PREON_SW_OVR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* remove LDO bypass */
+	ctl &= ~LDO_BYPASS_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* set power-source as LDO */
+	ctl |= PWR_SRC_SEL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* clear fake-sw ack to gdsc */
+	ctl &= ~ACK_SW_OVR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* put CPR in bypass mode */
+	if (ldo_vreg->ops_type == CORNER) {
+		ctl |= CPR_BYPASS_IN_LDO_MODE_BIT;
+		writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	}
+
+	/* complete all writes */
+	mb();
+
+	dump_registers(ldo_vreg, "enable_ldo_mode");
+
+	return 0;
+}
+
+static int enable_bhs_mode(struct msm_gfx_ldo *ldo_vreg)
+{
+	u32 ctl = 0;
+
+	ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Put LDO under SW control */
+	ctl |= LDO_UNDER_SW_CTRL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* set power-source as BHS */
+	ctl &= ~PWR_SRC_SEL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	if (ldo_vreg->ops_type == CORNER) {
+		/* clear GFX CPR in by-pass mode */
+		ctl &= ~CPR_BYPASS_IN_LDO_MODE_BIT;
+		writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	}
+
+	/* Enable the BHS control signals to gdsc */
+	ctl &= ~BHS_EN_FEW_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	ctl &= ~BHS_EN_REST_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Put BHS under GDSC control */
+	ctl &= ~BHS_UNDER_SW_CTL;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	dump_registers(ldo_vreg, "enable_bhs_mode");
+
+	return 0;
+}
+
+static int msm_gfx_ldo_corner_enable(struct regulator_dev *rdev)
+{
+	struct msm_gfx_ldo *ldo_vreg  = rdev_get_drvdata(rdev);
+	int rc = 0, new_uv;
+	enum msm_ldo_supply_mode enable_mode;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	pr_debug("regulator_enable requested. corner=%d\n",
+				ldo_vreg->corner + MIN_CORNER_OFFSET);
+
+	if (ldo_vreg->vdd_cx) {
+		rc = regulator_set_voltage(ldo_vreg->vdd_cx,
+			ldo_vreg->vdd_cx_corner_map[ldo_vreg->corner],
+			INT_MAX);
+		if (rc) {
+			pr_err("Unable to set CX for corner %d rc=%d\n",
+				ldo_vreg->corner + MIN_CORNER_OFFSET, rc);
+			goto fail;
+		}
+
+		rc = regulator_enable(ldo_vreg->vdd_cx);
+		if (rc) {
+			pr_err("regulator_enable: vdd_cx: failed rc=%d\n", rc);
+			goto fail;
+		}
+	}
+
+	enable_mode = get_operating_mode(ldo_vreg, ldo_vreg->corner);
+	if (enable_mode == LDO_MODE) {
+		new_uv = ldo_vreg->open_loop_volt[ldo_vreg->corner];
+		rc = enable_ldo_mode(ldo_vreg, new_uv);
+		pr_debug("LDO voltage configured =%d uV corner=%d\n",
+			ldo_vreg->ldo_voltage_uv,
+			ldo_vreg->corner + MIN_CORNER_OFFSET);
+	} else {
+		rc = enable_bhs_mode(ldo_vreg);
+	}
+
+	if (rc) {
+		pr_err("Failed to enable regulator in %s mode rc=%d\n",
+			(enable_mode == LDO_MODE) ? "LDO" : "BHS", rc);
+		goto disable_cx;
+	}
+
+	pr_debug("regulator_enable complete. mode=%s, corner=%d\n",
+			(enable_mode == LDO_MODE) ? "LDO" : "BHS",
+			ldo_vreg->corner + MIN_CORNER_OFFSET);
+
+	ldo_vreg->mode = enable_mode;
+	ldo_vreg->vreg_enabled = true;
+
+disable_cx:
+	if (rc && ldo_vreg->vdd_cx) {
+		rc = regulator_disable(ldo_vreg->vdd_cx);
+		if (rc)
+			pr_err("regulator_enable: vdd_cx: failed rc=%d\n", rc);
+	}
+fail:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int msm_gfx_ldo_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct msm_gfx_ldo *ldo_vreg  = rdev_get_drvdata(rdev);
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	if (ldo_vreg->vdd_cx) {
+		rc = regulator_disable(ldo_vreg->vdd_cx);
+		if (rc) {
+			pr_err("regulator_disable: vdd_cx: failed rc=%d\n", rc);
+			goto done;
+		}
+		rc = regulator_set_voltage(ldo_vreg->vdd_cx, 0, INT_MAX);
+		if (rc)
+			pr_err("failed to set voltage on CX rc=%d\n", rc);
+	}
+
+	/* No additional configuration for LDO/BHS - taken care by gsdc */
+	ldo_vreg->vreg_enabled = false;
+
+	pr_debug("regulator_disabled complete\n");
+done:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int switch_mode_to_ldo(struct msm_gfx_ldo *ldo_vreg, int new_uv)
+{
+	u32 ctl = 0, status = 0, timeout = 50;
+
+	ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	if (ldo_vreg->ops_type == CORNER) {
+		/* enable CPR bypass mode for LDO */
+		ctl |= CPR_BYPASS_IN_LDO_MODE_BIT;
+		writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	}
+
+	/* fake ack to GDSC */
+	ctl |= ACK_SW_OVR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* set power-source as LDO */
+	ctl |= PWR_SRC_SEL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Make sure BHS continues to power the rail */
+	ctl |= BHS_EN_FEW_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	ctl |= BHS_EN_REST_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* move BHS to SW control */
+	ctl |= BHS_UNDER_SW_CTL;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* set LDO under SW control */
+	ctl |= LDO_UNDER_SW_CTRL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* bypass LDO */
+	ctl |= LDO_BYPASS_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* power-on LDO */
+	ctl &= ~LDO_PDN_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* set the new LDO voltage */
+	ldo_update_voltage(ldo_vreg, new_uv);
+
+	pr_debug("LDO voltage =%d uV\n", ldo_vreg->ldo_voltage_uv);
+
+	/* make sure that the configuration is complete */
+	mb();
+
+	/* power down BHS */
+	ctl &= ~BHS_EN_FEW_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	ctl &= ~BHS_EN_REST_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* make sure that the configuration is complete */
+	mb();
+
+	/* wait for BHS to turn-off */
+	while (--timeout) {
+		status = readl_relaxed(ldo_vreg->ldo_base +
+					PWRSWITCH_STATUS_REG);
+		if (!(status & BHS_EN_REST_ACK_BIT))
+			break;
+
+		udelay(10);
+	}
+
+	if (!timeout)
+		pr_err("BHS_EN_RESET_ACK not clear PWRSWITCH_STATUS = 0x%x\n",
+								status);
+
+	/* remove LDO bypass */
+	ctl &= ~LDO_BYPASS_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* expose LDO to gdsc */
+	ctl &= ~ACK_SW_OVR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	ctl &= ~LDO_UNDER_SW_CTRL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	dump_registers(ldo_vreg, "switch_mode_to_ldo");
+
+	return 0;
+}
+
+static int switch_mode_to_bhs(struct msm_gfx_ldo *ldo_vreg)
+{
+	u32 ctl = 0, status = 0, timeout = 50;
+
+	ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* fake ack to gdsc */
+	ctl |= ACK_SW_OVR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* select BHS as power source */
+	ctl &= ~PWR_SRC_SEL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* LDO stays ON */
+	ctl &= ~LDO_PDN_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Move LDO to SW control */
+	ctl |= LDO_UNDER_SW_CTRL_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Power-up BHS */
+	ctl |= BHS_EN_FEW_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	ctl |= BHS_EN_REST_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* make sure that the configuration is complete */
+	mb();
+
+	/* wait for BHS to power-up */
+	while (--timeout) {
+		status = readl_relaxed(ldo_vreg->ldo_base +
+					PWRSWITCH_STATUS_REG);
+		if (status & BHS_EN_REST_ACK_BIT)
+			break;
+
+		udelay(10);
+	}
+	if (!timeout)
+		pr_err("BHS_EN_RESET_ACK not set PWRSWITCH_STATUS = 0x%x\n",
+								status);
+
+	/* bypass LDO */
+	ctl |= LDO_BYPASS_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* pull-down LDO */
+	ctl |= LDO_PDN_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	/* Expose BHS to gdsc */
+	ctl &= ~ACK_SW_OVR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	ctl &= ~BHS_UNDER_SW_CTL;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	if (ldo_vreg->ops_type == CORNER) {
+		/* Enable CPR in BHS mode */
+		ctl &= ~CPR_BYPASS_IN_LDO_MODE_BIT;
+		writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	}
+
+	/* make sure that all configuration is complete */
+	mb();
+
+	dump_registers(ldo_vreg, "switch_mode_to_bhs");
+
+	return 0;
+}
+
+static int msm_gfx_ldo_set_corner(struct regulator_dev *rdev,
+		int corner, int corner_max, unsigned int *selector)
+{
+	struct msm_gfx_ldo *ldo_vreg  = rdev_get_drvdata(rdev);
+	int rc = 0, mem_acc_corner, new_uv;
+	enum msm_ldo_supply_mode new_mode;
+	enum direction dir = NO_CHANGE;
+
+	corner -= MIN_CORNER_OFFSET;
+	corner_max -= MIN_CORNER_OFFSET;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	if (corner == ldo_vreg->corner)
+		goto done;
+
+	pr_debug("set-voltage requested: old_mode=%s old_corner=%d new_corner=%d vreg_enabled=%d\n",
+				ldo_vreg->mode == BHS_MODE ? "BHS" : "LDO",
+				ldo_vreg->corner + MIN_CORNER_OFFSET,
+				corner + MIN_CORNER_OFFSET,
+				ldo_vreg->vreg_enabled);
+
+	if (corner > ldo_vreg->corner)
+		dir = UP;
+	else if (corner < ldo_vreg->corner)
+		dir = DOWN;
+
+	if (ldo_vreg->mem_acc_vreg && dir == DOWN) {
+		mem_acc_corner = ldo_vreg->mem_acc_corner_map[corner];
+		rc = regulator_set_voltage(ldo_vreg->mem_acc_vreg,
+				mem_acc_corner, mem_acc_corner);
+	}
+
+	if (!ldo_vreg->vreg_enabled) {
+		ldo_vreg->corner = corner;
+		goto done;
+	}
+
+	if (ldo_vreg->vdd_cx) {
+		rc = regulator_set_voltage(ldo_vreg->vdd_cx,
+			ldo_vreg->vdd_cx_corner_map[corner],
+			INT_MAX);
+		if (rc) {
+			pr_err("Unable to set CX for corner %d rc=%d\n",
+					corner + MIN_CORNER_OFFSET, rc);
+			goto done;
+		}
+	}
+
+	new_mode = get_operating_mode(ldo_vreg, corner);
+
+	if (new_mode == BHS_MODE) {
+		if (ldo_vreg->mode == LDO_MODE) {
+			rc = switch_mode_to_bhs(ldo_vreg);
+			if (rc)
+				pr_err("Switch to BHS corner=%d failed rc=%d\n",
+						corner + MIN_CORNER_OFFSET, rc);
+		}
+	} else { /* new mode - LDO */
+		new_uv = ldo_vreg->open_loop_volt[ldo_vreg->corner];
+
+		if (ldo_vreg->mode == BHS_MODE) {
+			rc = switch_mode_to_ldo(ldo_vreg, new_uv);
+			if (rc)
+				pr_err("Switch to LDO failed corner=%d rc=%d\n",
+						corner + MIN_CORNER_OFFSET, rc);
+		} else {
+			rc = ldo_update_voltage(ldo_vreg, new_uv);
+			if (rc)
+				pr_err("Update voltage failed corner=%d rc=%d\n",
+						corner + MIN_CORNER_OFFSET, rc);
+		}
+	}
+
+	if (!rc) {
+		pr_debug("set-voltage complete. old_mode=%s new_mode=%s old_corner=%d new_corner=%d\n",
+				ldo_vreg->mode == BHS_MODE ? "BHS" : "LDO",
+				new_mode == BHS_MODE ? "BHS" : "LDO",
+				ldo_vreg->corner + MIN_CORNER_OFFSET,
+				corner + MIN_CORNER_OFFSET);
+
+		ldo_vreg->mode = new_mode;
+		ldo_vreg->corner = corner;
+	}
+
+done:
+	if (!rc && ldo_vreg->mem_acc_vreg && dir == UP) {
+		mem_acc_corner = ldo_vreg->mem_acc_corner_map[corner];
+		rc = regulator_set_voltage(ldo_vreg->mem_acc_vreg,
+				mem_acc_corner, mem_acc_corner);
+	}
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int msm_gfx_ldo_get_corner(struct regulator_dev *rdev)
+{
+	struct msm_gfx_ldo *ldo_vreg  = rdev_get_drvdata(rdev);
+
+	return ldo_vreg->corner + MIN_CORNER_OFFSET;
+}
+
+static int msm_gfx_ldo_is_enabled(struct regulator_dev *rdev)
+{
+	struct msm_gfx_ldo *ldo_vreg  = rdev_get_drvdata(rdev);
+
+	return ldo_vreg->vreg_enabled;
+}
+
+static struct regulator_ops msm_gfx_ldo_corner_ops = {
+	.enable		= msm_gfx_ldo_corner_enable,
+	.disable	= msm_gfx_ldo_disable,
+	.is_enabled	= msm_gfx_ldo_is_enabled,
+	.set_voltage	= msm_gfx_ldo_set_corner,
+	.get_voltage	= msm_gfx_ldo_get_corner,
+};
+
+static int msm_gfx_ldo_get_bypass(struct regulator_dev *rdev,
+				  bool *enable)
+{
+	struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
+
+	*enable = ldo_vreg->mode;
+
+	return 0;
+}
+
+static int msm_gfx_ldo_set_bypass(struct regulator_dev *rdev,
+				  bool mode)
+{
+	struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	if (ldo_vreg->mode == mode || !ldo_vreg->vreg_enabled)
+		goto done;
+
+	if (mode == LDO_MODE)
+		rc = switch_mode_to_ldo(ldo_vreg, ldo_vreg->ldo_voltage_uv);
+	else
+		rc = switch_mode_to_bhs(ldo_vreg);
+
+	if (rc) {
+		pr_err("Failed to configure regulator in %s mode rc=%d\n",
+			(mode == LDO_MODE) ? "LDO" : "BHS", rc);
+		goto done;
+	}
+
+	pr_debug("regulator_set_bypass complete. mode=%s, voltage = %d uV\n",
+			(mode == LDO_MODE) ? "LDO" : "BHS",
+			(mode == LDO_MODE) ? ldo_vreg->ldo_voltage_uv : 0);
+
+	ldo_vreg->mode = mode;
+
+done:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int msm_gfx_ldo_voltage_enable(struct regulator_dev *rdev)
+{
+	struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	enum msm_ldo_supply_mode enable_mode;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	pr_debug("regulator_enable requested. voltage=%d\n",
+		ldo_vreg->ldo_voltage_uv);
+
+	enable_mode = ldo_vreg->mode;
+
+	if (enable_mode == LDO_MODE)
+		rc = enable_ldo_mode(ldo_vreg, ldo_vreg->ldo_voltage_uv);
+	else
+		rc = enable_bhs_mode(ldo_vreg);
+
+	if (rc) {
+		pr_err("Failed to enable regulator in %s mode rc=%d\n",
+			(enable_mode == LDO_MODE) ? "LDO" : "BHS", rc);
+		goto fail;
+	}
+
+	pr_debug("regulator_enable complete. mode=%s, voltage = %d uV\n",
+		(enable_mode == LDO_MODE) ? "LDO" : "BHS",
+		(enable_mode == LDO_MODE) ? ldo_vreg->ldo_voltage_uv : 0);
+
+	ldo_vreg->vreg_enabled = true;
+
+fail:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int msm_gfx_ldo_set_voltage(struct regulator_dev *rdev,
+		int new_uv, int max_uv, unsigned int *selector)
+{
+	struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	if (new_uv == ldo_vreg->ldo_voltage_uv)
+		goto done;
+
+	if (!ldo_vreg->vreg_enabled || ldo_vreg->mode != LDO_MODE) {
+		ldo_vreg->ldo_voltage_uv = new_uv;
+		goto done;
+	}
+
+	/* update LDO voltage */
+	rc = ldo_update_voltage(ldo_vreg, new_uv);
+	if (rc)
+		pr_err("Update voltage failed for [%d, %d], rc=%d\n",
+			new_uv, max_uv, rc);
+done:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int msm_gfx_ldo_get_voltage(struct regulator_dev *rdev)
+{
+	struct msm_gfx_ldo *ldo_vreg = rdev_get_drvdata(rdev);
+
+	return ldo_vreg->ldo_voltage_uv;
+}
+
+static struct regulator_ops msm_gfx_ldo_voltage_ops = {
+	.enable		= msm_gfx_ldo_voltage_enable,
+	.disable	= msm_gfx_ldo_disable,
+	.is_enabled	= msm_gfx_ldo_is_enabled,
+	.set_voltage	= msm_gfx_ldo_set_voltage,
+	.get_voltage	= msm_gfx_ldo_get_voltage,
+	.set_bypass	= msm_gfx_ldo_set_bypass,
+	.get_bypass	= msm_gfx_ldo_get_bypass,
+};
+
+static int msm_gfx_ldo_adjust_init_voltage(struct msm_gfx_ldo *ldo_vreg)
+{
+	int rc, len, size, i;
+	u32 *volt_adjust;
+	struct device_node *of_node = ldo_vreg->dev->of_node;
+	char *prop_name = "qcom,ldo-init-voltage-adjustment";
+
+	if (!of_find_property(of_node, prop_name, &len)) {
+		/* No initial voltage adjustment needed. */
+		return 0;
+	}
+
+	size = len / sizeof(u32);
+	if (size != ldo_vreg->num_ldo_corners) {
+		pr_err("%s length=%d is invalid: required:%d\n",
+				prop_name, size, ldo_vreg->num_ldo_corners);
+		return -EINVAL;
+	}
+
+	volt_adjust = devm_kcalloc(ldo_vreg->dev, size, sizeof(*volt_adjust),
+								GFP_KERNEL);
+	if (!volt_adjust)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, prop_name, volt_adjust, size);
+	if (rc) {
+		pr_err("failed to read %s property rc=%d\n", prop_name, rc);
+		return rc;
+	}
+
+	for (i = 0; i < ldo_vreg->num_corners; i++) {
+		if (volt_adjust[i]) {
+			ldo_vreg->open_loop_volt[i] += volt_adjust[i];
+			pr_info("adjusted the open-loop voltage[%d] %d -> %d\n",
+				i + MIN_CORNER_OFFSET,
+				ldo_vreg->open_loop_volt[i] - volt_adjust[i],
+				ldo_vreg->open_loop_volt[i]);
+		}
+	}
+
+	return 0;
+}
+
+static int msm_gfx_ldo_voltage_init(struct msm_gfx_ldo *ldo_vreg)
+{
+	struct device_node *of_node = ldo_vreg->dev->of_node;
+	int i, rc, len;
+	u64 efuse_bits;
+
+	len = ldo_vreg->num_ldo_corners;
+
+	ldo_vreg->open_loop_volt = devm_kcalloc(ldo_vreg->dev,
+			len, sizeof(*ldo_vreg->open_loop_volt),
+			GFP_KERNEL);
+	ldo_vreg->ceiling_volt = devm_kcalloc(ldo_vreg->dev,
+			len, sizeof(*ldo_vreg->ceiling_volt),
+			GFP_KERNEL);
+	ldo_vreg->floor_volt = devm_kcalloc(ldo_vreg->dev,
+			len, sizeof(*ldo_vreg->floor_volt),
+			GFP_KERNEL);
+
+	if (!ldo_vreg->open_loop_volt || !ldo_vreg->ceiling_volt
+					|| !ldo_vreg->floor_volt)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,ldo-voltage-ceiling",
+						ldo_vreg->ceiling_volt, len);
+	if (rc) {
+		pr_err("Unable to read qcom,ldo-voltage-ceiling rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32_array(of_node, "qcom,ldo-voltage-floor",
+						ldo_vreg->floor_volt, len);
+	if (rc) {
+		pr_err("Unable to read qcom,ldo-voltage-floor rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < ldo_vreg->num_ldo_corners; i++) {
+		rc = read_fuse_param(ldo_vreg->efuse_base,
+				ldo_vreg->init_volt_param[i],
+				&efuse_bits);
+		if (rc) {
+			pr_err("Unable to read init-voltage rc=%d\n", rc);
+			return rc;
+		}
+		ldo_vreg->open_loop_volt[i] = convert_open_loop_voltage_fuse(
+					ldo_vreg->ref_volt[i],
+					GFX_LDO_FUSE_STEP_VOLT,
+					efuse_bits,
+					GFX_LDO_FUSE_SIZE);
+		pr_info("LDO corner %d: target-volt = %d uV\n",
+			i + MIN_CORNER_OFFSET, ldo_vreg->open_loop_volt[i]);
+	}
+
+	rc = msm_gfx_ldo_adjust_init_voltage(ldo_vreg);
+	if (rc) {
+		pr_err("Unable to adjust init voltages rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < ldo_vreg->num_ldo_corners; i++) {
+		if (ldo_vreg->open_loop_volt[i] > ldo_vreg->ceiling_volt[i]) {
+			pr_info("Warning: initial voltage[%d] %d above ceiling %d\n",
+				i + MIN_CORNER_OFFSET,
+				ldo_vreg->open_loop_volt[i],
+				ldo_vreg->ceiling_volt[i]);
+			ldo_vreg->open_loop_volt[i] = ldo_vreg->ceiling_volt[i];
+		} else if (ldo_vreg->open_loop_volt[i] <
+				ldo_vreg->floor_volt[i]) {
+			pr_info("Warning: initial voltage[%d] %d below floor %d\n",
+				i + MIN_CORNER_OFFSET,
+				ldo_vreg->open_loop_volt[i],
+				ldo_vreg->floor_volt[i]);
+			ldo_vreg->open_loop_volt[i] = ldo_vreg->floor_volt[i];
+		}
+	}
+
+	efuse_bits = 0;
+	rc = read_fuse_param(ldo_vreg->efuse_base, ldo_vreg->ldo_enable_param,
+								&efuse_bits);
+	if (rc) {
+		pr_err("Unable to read ldo_enable_param rc=%d\n", rc);
+		return rc;
+	}
+	ldo_vreg->ldo_fuse_enable = !!efuse_bits;
+	pr_info("LDO-mode fuse %s by default\n", ldo_vreg->ldo_fuse_enable ?
+					"enabled" : "disabled");
+
+	return rc;
+}
+
+static int msm_gfx_ldo_efuse_init(struct platform_device *pdev,
+				struct msm_gfx_ldo *ldo_vreg)
+{
+	struct resource *res;
+	u32 len;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse_addr");
+	if (!res || !res->start) {
+		pr_err("efuse_addr missing: res=%p\n", res);
+		return -EINVAL;
+	}
+
+	ldo_vreg->efuse_addr = res->start;
+	len = res->end - res->start + 1;
+
+	ldo_vreg->efuse_base = devm_ioremap(&pdev->dev,
+				ldo_vreg->efuse_addr, len);
+	if (!ldo_vreg->efuse_base) {
+		pr_err("Unable to map efuse_addr %pa\n",
+				&ldo_vreg->efuse_addr);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int msm_gfx_ldo_mem_acc_init(struct msm_gfx_ldo *ldo_vreg)
+{
+	int rc;
+	u32 len, size;
+	struct device_node *of_node = ldo_vreg->dev->of_node;
+
+	if (of_find_property(ldo_vreg->dev->of_node, "mem-acc-supply", NULL)) {
+		ldo_vreg->mem_acc_vreg = devm_regulator_get(ldo_vreg->dev,
+							"mem-acc");
+		if (IS_ERR_OR_NULL(ldo_vreg->mem_acc_vreg)) {
+			rc = PTR_RET(ldo_vreg->mem_acc_vreg);
+			if (rc != -EPROBE_DEFER)
+				pr_err("devm_regulator_get: mem-acc: rc=%d\n",
+					rc);
+			return rc;
+		}
+	} else {
+		pr_debug("mem-acc-supply not specified\n");
+		return 0;
+	}
+
+	if (!of_find_property(of_node, "qcom,mem-acc-corner-map", &len)) {
+		pr_err("qcom,mem-acc-corner-map missing");
+		return -EINVAL;
+	}
+
+	size = len / sizeof(u32);
+	if (size != ldo_vreg->num_corners) {
+		pr_err("qcom,mem-acc-corner-map length=%d is invalid: required:%u\n",
+						size, ldo_vreg->num_corners);
+		return -EINVAL;
+	}
+
+	ldo_vreg->mem_acc_corner_map = devm_kcalloc(ldo_vreg->dev, size,
+			sizeof(*ldo_vreg->mem_acc_corner_map), GFP_KERNEL);
+	if (!ldo_vreg->mem_acc_corner_map)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,mem-acc-corner-map",
+					ldo_vreg->mem_acc_corner_map, size);
+	if (rc)
+		pr_err("Unable to read qcom,mem-acc-corner-map rc=%d\n", rc);
+
+	return rc;
+}
+
+static int msm_gfx_ldo_init(struct platform_device *pdev,
+				struct msm_gfx_ldo *ldo_vreg)
+{
+	struct resource *res;
+	u32 len, ctl;
+	int i = 0;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ldo_addr");
+	if (!res || !res->start) {
+		pr_err("ldo_addr missing: res=%p\n", res);
+		return -EINVAL;
+	}
+
+	ldo_vreg->ldo_addr = res->start;
+	len = res->end - res->start + 1;
+
+	ldo_vreg->ldo_base = devm_ioremap(ldo_vreg->dev,
+					ldo_vreg->ldo_addr, len);
+	if (!ldo_vreg->ldo_base) {
+		pr_err("Unable to map efuse_addr %pa\n",
+				&ldo_vreg->ldo_addr);
+		return -EINVAL;
+	}
+
+	/* HW initialization */
+
+	/* clear clamp_io, enable CPR in auto-bypass*/
+	ctl = readl_relaxed(ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+	ctl &= ~LDO_CLAMP_IO_BIT;
+	ctl |= EN_LDOAP_CTRL_CPR_BIT;
+	writel_relaxed(ctl, ldo_vreg->ldo_base + PWRSWITCH_CTRL_REG);
+
+	i = 0;
+	while (ldo_vreg->ldo_init_config &&
+			ldo_vreg->ldo_init_config[i].offset != LDO_MAX_OFFSET) {
+		writel_relaxed(ldo_vreg->ldo_init_config[i].value,
+				ldo_vreg->ldo_base +
+				ldo_vreg->ldo_init_config[i].offset);
+		i++;
+	}
+	/* complete the writes */
+	mb();
+
+	return 0;
+}
+
+static int ldo_parse_cx_parameters(struct msm_gfx_ldo *ldo_vreg)
+{
+	struct device_node *of_node = ldo_vreg->dev->of_node;
+	int rc, len, size;
+
+	if (of_find_property(of_node, "vdd-cx-supply", NULL)) {
+		ldo_vreg->vdd_cx = devm_regulator_get(ldo_vreg->dev, "vdd-cx");
+		if (IS_ERR_OR_NULL(ldo_vreg->vdd_cx)) {
+			rc = PTR_RET(ldo_vreg->vdd_cx);
+			if (rc != -EPROBE_DEFER)
+				pr_err("devm_regulator_get: vdd-cx: rc=%d\n",
+					rc);
+			return rc;
+		}
+	} else {
+		pr_debug("vdd-cx-supply not specified\n");
+		return 0;
+	}
+
+	if (!of_find_property(of_node, "qcom,vdd-cx-corner-map", &len)) {
+		pr_err("qcom,vdd-cx-corner-map missing");
+		return -EINVAL;
+	}
+
+	size = len / sizeof(u32);
+	if (size != ldo_vreg->num_corners) {
+		pr_err("qcom,vdd-cx-corner-map length=%d is invalid: required:%u\n",
+						size, ldo_vreg->num_corners);
+		return -EINVAL;
+	}
+
+	ldo_vreg->vdd_cx_corner_map = devm_kcalloc(ldo_vreg->dev, size,
+			sizeof(*ldo_vreg->vdd_cx_corner_map), GFP_KERNEL);
+	if (!ldo_vreg->vdd_cx_corner_map)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,vdd-cx-corner-map",
+					ldo_vreg->vdd_cx_corner_map, size);
+	if (rc)
+		pr_err("Unable to read qcom,vdd-cx-corner-map rc=%d\n", rc);
+
+
+	return rc;
+}
+
+static int msm_gfx_ldo_parse_dt(struct msm_gfx_ldo *ldo_vreg)
+{
+	struct device_node *of_node = ldo_vreg->dev->of_node;
+	int rc, size, len;
+
+	rc = of_property_read_u32(of_node, "qcom,num-corners",
+					&ldo_vreg->num_corners);
+	if (rc < 0) {
+		pr_err("Unable to read qcom,num-corners rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,num-ldo-corners",
+					&ldo_vreg->num_ldo_corners);
+	if (rc) {
+		pr_err("Unable to read qcom,num-ldo-corners rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,init-corner",
+					&ldo_vreg->corner);
+	if (rc) {
+		pr_err("Unable to read qcom,init-corner rc=%d\n", rc);
+		return rc;
+	}
+
+	if (!of_find_property(of_node, "qcom,ldo-enable-corner-map", &len)) {
+		pr_err("qcom,ldo-enable-corner-map missing\n");
+		return -EINVAL;
+	}
+
+	size = len / sizeof(u32);
+	if (size != ldo_vreg->num_corners) {
+		pr_err("qcom,ldo-enable-corner-map length=%d is invalid: required:%u\n",
+					size, ldo_vreg->num_corners);
+		return -EINVAL;
+	}
+
+	ldo_vreg->ldo_corner_en_map = devm_kcalloc(ldo_vreg->dev, size,
+			sizeof(*ldo_vreg->ldo_corner_en_map), GFP_KERNEL);
+	if (!ldo_vreg->ldo_corner_en_map)
+		return -ENOMEM;
+
+	rc = of_property_read_u32_array(of_node, "qcom,ldo-enable-corner-map",
+					ldo_vreg->ldo_corner_en_map, size);
+	if (rc) {
+		pr_err("Unable to read qcom,ldo-enable-corner-map rc=%d\n",
+									rc);
+		return rc;
+	}
+
+	rc = ldo_parse_cx_parameters(ldo_vreg);
+	if (rc) {
+		pr_err("Unable to parse CX parameters rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int msm_gfx_ldo_target_init(struct msm_gfx_ldo *ldo_vreg)
+{
+	int i;
+
+	/* MSM8953 */
+	ldo_vreg->init_volt_param = devm_kzalloc(ldo_vreg->dev,
+			(MSM8953_LDO_FUSE_CORNERS *
+			sizeof(struct fuse_param *)), GFP_KERNEL);
+	if (!ldo_vreg->init_volt_param)
+		return -ENOMEM;
+
+	for (i = 0; i < MSM8953_LDO_FUSE_CORNERS; i++)
+		ldo_vreg->init_volt_param[i] =
+				msm8953_init_voltage_param[i];
+
+	ldo_vreg->ref_volt = msm8953_fuse_ref_volt;
+	ldo_vreg->ldo_enable_param = msm8953_ldo_enable_param;
+
+	return 0;
+}
+
+static int debugfs_ldo_mode_disable_set(void *data, u64 val)
+{
+	struct msm_gfx_ldo *ldo_vreg = data;
+
+	ldo_vreg->ldo_mode_disable = !!val;
+
+	pr_debug("LDO-mode %s\n", ldo_vreg->ldo_mode_disable ?
+					"disabled" : "enabled");
+
+	return 0;
+}
+
+static int debugfs_ldo_mode_disable_get(void *data, u64 *val)
+{
+	struct msm_gfx_ldo *ldo_vreg = data;
+
+	*val = ldo_vreg->ldo_mode_disable;
+
+	return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ldo_mode_disable_fops, debugfs_ldo_mode_disable_get,
+				debugfs_ldo_mode_disable_set, "%llu\n");
+
+static int debugfs_ldo_set_voltage(void *data, u64 val)
+{
+	struct msm_gfx_ldo *ldo_vreg = data;
+	int rc = 0, timeout = 50;
+	u32 reg = 0, voltage = 0;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	if (ldo_vreg->mode == BHS_MODE || !ldo_vreg->vreg_enabled ||
+		val > MAX_LDO_VOLTAGE || val < MIN_LDO_VOLTAGE) {
+		rc = -EINVAL;
+		goto done;
+	}
+	voltage = GET_VREF((u32)val);
+
+	reg = readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* set the new voltage */
+	reg &= ~VREF_VAL_MASK;
+	reg |= voltage & VREF_VAL_MASK;
+	writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* Initiate VREF update */
+	reg |= UPDATE_VREF_BIT;
+	writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* complete the writes */
+	mb();
+
+	reg &= ~UPDATE_VREF_BIT;
+	writel_relaxed(reg, ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+
+	/* complete the writes */
+	mb();
+
+	while (--timeout) {
+		reg = readl_relaxed(ldo_vreg->ldo_base +
+					PWRSWITCH_STATUS_REG);
+		if (reg & (LDO_VREF_SETTLED_BIT | LDO_READY_BIT))
+			break;
+
+		udelay(10);
+	}
+	if (!timeout) {
+		pr_err("LDO_VREF_SETTLED not set PWRSWITCH_STATUS = 0x%x\n",
+								reg);
+		rc = -EBUSY;
+	} else {
+		ldo_vreg->ldo_voltage_uv = val;
+		pr_debug("LDO voltage set to %d uV\n",
+				ldo_vreg->ldo_voltage_uv);
+	}
+done:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+
+static int debugfs_ldo_get_voltage(void *data, u64 *val)
+{
+	struct msm_gfx_ldo *ldo_vreg = data;
+	int rc = 0;
+	u32 reg;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	if (ldo_vreg->mode == BHS_MODE || !ldo_vreg->vreg_enabled) {
+		rc = -EINVAL;
+		goto done;
+	}
+
+	reg = readl_relaxed(ldo_vreg->ldo_base + LDO_VREF_SET_REG);
+	reg &= VREF_VAL_MASK;
+
+	*val = (reg * LDO_STEP_VOLATGE) + MIN_LDO_VOLTAGE;
+done:
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+	return rc;
+}
+DEFINE_SIMPLE_ATTRIBUTE(ldo_voltage_fops, debugfs_ldo_get_voltage,
+				debugfs_ldo_set_voltage, "%llu\n");
+
+static int msm_gfx_ldo_debug_info_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+
+	return 0;
+}
+
+static ssize_t msm_gfx_ldo_debug_info_read(struct file *file, char __user *buff,
+				size_t count, loff_t *ppos)
+{
+	struct msm_gfx_ldo *ldo_vreg = file->private_data;
+	char *debugfs_buf;
+	ssize_t len, ret = 0;
+	u32 i = 0, reg[MAX_LDO_REGS];
+
+	debugfs_buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!debugfs_buf)
+		return -ENOMEM;
+
+	mutex_lock(&ldo_vreg->ldo_mutex);
+
+	len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret,
+		"Regulator_enable = %d Regulator mode = %s Corner = %d LDO-voltage = %d uV\n",
+		ldo_vreg->vreg_enabled,
+		ldo_vreg->mode == BHS_MODE ? "BHS" : "LDO",
+		ldo_vreg->corner + MIN_CORNER_OFFSET,
+		ldo_vreg->ldo_voltage_uv);
+	ret += len;
+
+	for (i = 0; i < MAX_LDO_REGS; i++) {
+		reg[i] = 0;
+		reg[i] = readl_relaxed(ldo_vreg->ldo_base + (i * 4));
+		len = snprintf(debugfs_buf + ret, PAGE_SIZE - ret,
+				"%s = 0x%x\n",  register_str[i], reg[i]);
+		ret += len;
+	}
+
+	mutex_unlock(&ldo_vreg->ldo_mutex);
+
+	ret = simple_read_from_buffer(buff, count, ppos, debugfs_buf, ret);
+	kfree(debugfs_buf);
+
+	return ret;
+}
+
+static const struct file_operations msm_gfx_ldo_debug_info_fops = {
+	.open = msm_gfx_ldo_debug_info_open,
+	.read = msm_gfx_ldo_debug_info_read,
+};
+
+static void msm_gfx_ldo_debugfs_init(struct msm_gfx_ldo *ldo_vreg)
+{
+	struct dentry *temp;
+
+	ldo_vreg->debugfs = debugfs_create_dir("msm_gfx_ldo", NULL);
+	if (!ldo_vreg->debugfs) {
+		pr_err("Couldn't create debug dir\n");
+		return;
+	}
+
+	temp = debugfs_create_file("debug_info", 0444, ldo_vreg->debugfs,
+					ldo_vreg, &msm_gfx_ldo_debug_info_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		pr_err("debug_info node creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_file("ldo_voltage", 0644, ldo_vreg->debugfs,
+					ldo_vreg, &ldo_voltage_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		pr_err("ldo_voltage node creation failed\n");
+		return;
+	}
+
+	temp = debugfs_create_file("ldo_mode_disable", 0644, ldo_vreg->debugfs,
+					ldo_vreg, &ldo_mode_disable_fops);
+	if (IS_ERR_OR_NULL(temp)) {
+		pr_err("ldo_mode_disable node creation failed\n");
+		return;
+	}
+}
+
+static void msm_gfx_ldo_debugfs_remove(struct msm_gfx_ldo *ldo_vreg)
+{
+	debugfs_remove_recursive(ldo_vreg->debugfs);
+}
+
+static int msm_gfx_ldo_corner_config_init(struct msm_gfx_ldo *ldo_vreg,
+		struct platform_device *pdev)
+{
+	int rc;
+
+	rc = msm_gfx_ldo_target_init(ldo_vreg);
+	if (rc) {
+		pr_err("Unable to initialize target specific data rc=%d", rc);
+		return rc;
+	}
+
+	rc = msm_gfx_ldo_parse_dt(ldo_vreg);
+	if (rc) {
+		pr_err("Unable to pasrse dt rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = msm_gfx_ldo_efuse_init(pdev, ldo_vreg);
+	if (rc) {
+		pr_err("efuse_init failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = msm_gfx_ldo_voltage_init(ldo_vreg);
+	if (rc) {
+		pr_err("ldo_voltage_init failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = msm_gfx_ldo_mem_acc_init(ldo_vreg);
+	if (rc) {
+		pr_err("Unable to initialize mem_acc rc=%d\n", rc);
+		return rc;
+	}
+
+	return rc;
+};
+
+/* Data corresponds to the SoC revision */
+static const struct of_device_id msm_gfx_ldo_match_table[] = {
+	{
+		.compatible = "qcom,msm8953-gfx-ldo",
+		.data = (void *)(uintptr_t)MSM8953_SOC_ID,
+	},
+	{
+		.compatible = "qcom,sdm660-gfx-ldo",
+		.data = (void *)(uintptr_t)SDM660_SOC_ID,
+	},
+	{}
+};
+
+static int msm_gfx_ldo_probe(struct platform_device *pdev)
+{
+	struct msm_gfx_ldo *ldo_vreg;
+	struct regulator_config reg_config = {};
+	struct regulator_desc *rdesc;
+	struct regulator_init_data *init_data = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	int soc_id, rc;
+
+	match = of_match_device(msm_gfx_ldo_match_table, dev);
+	if (!match)
+		return -ENODEV;
+
+	ldo_vreg = devm_kzalloc(dev, sizeof(*ldo_vreg), GFP_KERNEL);
+	if (!ldo_vreg)
+		return -ENOMEM;
+
+	init_data = of_get_regulator_init_data(dev, dev->of_node, NULL);
+	if (!init_data) {
+		pr_err("regulator init data is missing\n");
+		return -EINVAL;
+	}
+
+	init_data->constraints.input_uV = init_data->constraints.max_uV;
+	init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_STATUS;
+
+	ldo_vreg->rdesc.name = init_data->constraints.name;
+	if (ldo_vreg->rdesc.name == NULL) {
+		dev_err(dev, "regulator-name missing\n");
+		return -EINVAL;
+	}
+
+	soc_id = (uintptr_t)match->data;
+	ldo_vreg->dev = &pdev->dev;
+	mutex_init(&ldo_vreg->ldo_mutex);
+	platform_set_drvdata(pdev, ldo_vreg);
+
+	switch (soc_id) {
+	case MSM8953_SOC_ID:
+		ldo_vreg->ldo_init_config = msm8953_ldo_config;
+		ldo_vreg->ops_type = CORNER;
+		rc = msm_gfx_ldo_corner_config_init(ldo_vreg, pdev);
+		if (rc) {
+			pr_err("ldo corner handling initialization failed, rc=%d\n",
+				rc);
+			return rc;
+		}
+		break;
+	case SDM660_SOC_ID:
+		ldo_vreg->ldo_init_config = sdm660_ldo_config;
+		ldo_vreg->ops_type = VOLTAGE;
+		init_data->constraints.valid_ops_mask
+			|= REGULATOR_CHANGE_BYPASS;
+		break;
+	default:
+		pr_err("invalid SOC ID = %d\n", soc_id);
+		return -EINVAL;
+	}
+
+	/* HW initialization */
+	rc = msm_gfx_ldo_init(pdev, ldo_vreg);
+	if (rc) {
+		pr_err("ldo_init failed rc=%d\n", rc);
+		return rc;
+	}
+
+	rdesc			= &ldo_vreg->rdesc;
+	rdesc->owner		= THIS_MODULE;
+	rdesc->type		= REGULATOR_VOLTAGE;
+
+	if (ldo_vreg->ops_type == CORNER)
+		rdesc->ops = &msm_gfx_ldo_corner_ops;
+	else
+		rdesc->ops = &msm_gfx_ldo_voltage_ops;
+
+	reg_config.dev = &pdev->dev;
+	reg_config.init_data = init_data;
+	reg_config.driver_data = ldo_vreg;
+	reg_config.of_node = pdev->dev.of_node;
+	ldo_vreg->rdev = regulator_register(rdesc, &reg_config);
+	if (IS_ERR(ldo_vreg->rdev)) {
+		rc = PTR_ERR(ldo_vreg->rdev);
+		pr_err("regulator_register failed: rc=%d\n", rc);
+		return rc;
+	}
+
+	msm_gfx_ldo_debugfs_init(ldo_vreg);
+
+	return 0;
+}
+
+static int msm_gfx_ldo_remove(struct platform_device *pdev)
+{
+	struct msm_gfx_ldo *ldo_vreg = platform_get_drvdata(pdev);
+
+	regulator_unregister(ldo_vreg->rdev);
+
+	msm_gfx_ldo_debugfs_remove(ldo_vreg);
+
+	return 0;
+}
+
+static struct platform_driver msm_gfx_ldo_driver = {
+	.driver		= {
+		.name		= "qcom,msm-gfx-ldo",
+		.of_match_table = msm_gfx_ldo_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= msm_gfx_ldo_probe,
+	.remove		= msm_gfx_ldo_remove,
+};
+
+static int msm_gfx_ldo_platform_init(void)
+{
+	return platform_driver_register(&msm_gfx_ldo_driver);
+}
+arch_initcall(msm_gfx_ldo_platform_init);
+
+static void msm_gfx_ldo_platform_exit(void)
+{
+	platform_driver_unregister(&msm_gfx_ldo_driver);
+}
+module_exit(msm_gfx_ldo_platform_exit);
+
+MODULE_DESCRIPTION("MSM GFX LDO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qpnp-labibb-regulator.c b/drivers/regulator/qpnp-labibb-regulator.c
new file mode 100644
index 0000000..cf8f000
--- /dev/null
+++ b/drivers/regulator/qpnp-labibb-regulator.c
@@ -0,0 +1,3877 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"%s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/string.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/qpnp/qpnp-revid.h>
+
+#define QPNP_LABIBB_REGULATOR_DRIVER_NAME	"qcom,qpnp-labibb-regulator"
+
+#define REG_REVISION_2			0x01
+#define REG_PERPH_TYPE			0x04
+
+#define QPNP_LAB_TYPE			0x24
+#define QPNP_IBB_TYPE			0x20
+
+/* Common register value for LAB/IBB */
+#define REG_LAB_IBB_LCD_MODE		0x0
+#define REG_LAB_IBB_AMOLED_MODE		BIT(7)
+#define REG_LAB_IBB_SEC_ACCESS		0xD0
+#define REG_LAB_IBB_SEC_UNLOCK_CODE	0xA5
+
+/* LAB register offset definitions */
+#define REG_LAB_STATUS1			0x08
+#define REG_LAB_SWIRE_PGM_CTL		0x40
+#define REG_LAB_VOLTAGE			0x41
+#define REG_LAB_RING_SUPPRESSION_CTL	0x42
+#define REG_LAB_LCD_AMOLED_SEL		0x44
+#define REG_LAB_MODULE_RDY		0x45
+#define REG_LAB_ENABLE_CTL		0x46
+#define REG_LAB_PD_CTL			0x47
+#define REG_LAB_CLK_DIV			0x48
+#define REG_LAB_IBB_EN_RDY		0x49
+#define REG_LAB_CURRENT_LIMIT		0x4B
+#define REG_LAB_CURRENT_SENSE		0x4C
+#define REG_LAB_PS_CTL			0x50
+#define REG_LAB_RDSON_MNGMNT		0x53
+#define REG_LAB_PRECHARGE_CTL		0x5E
+#define REG_LAB_SOFT_START_CTL		0x5F
+#define REG_LAB_SPARE_CTL		0x60
+#define REG_LAB_PFM_CTL			0x62
+
+/* LAB registers for PM660A */
+#define REG_LAB_VOUT_DEFAULT		0x44
+#define REG_LAB_SW_HIGH_PSRR_CTL	0x70
+#define REG_LAB_LDO_PD_CTL		0x78
+#define REG_LAB_VPH_ENVELOP_CTL		0x7E
+
+/* LAB register bits definitions */
+
+/* REG_LAB_STATUS1 */
+#define LAB_STATUS1_VREG_OK_MASK	BIT(7)
+#define LAB_STATUS1_VREG_OK		BIT(7)
+
+/* REG_LAB_SWIRE_PGM_CTL */
+#define LAB_EN_SWIRE_PGM_VOUT		BIT(7)
+#define LAB_EN_SWIRE_PGM_PD		BIT(6)
+
+/* REG_LAB_VOLTAGE */
+#define LAB_VOLTAGE_OVERRIDE_EN		BIT(7)
+#define LAB_VOLTAGE_SET_MASK		GENMASK(3, 0)
+
+/* REG_LAB_RING_SUPPRESSION_CTL */
+#define LAB_RING_SUPPRESSION_CTL_EN	BIT(7)
+
+/* REG_LAB_MODULE_RDY */
+#define LAB_MODULE_RDY_EN		BIT(7)
+
+/* REG_LAB_ENABLE_CTL */
+#define LAB_ENABLE_CTL_EN		BIT(7)
+
+/* REG_LAB_PD_CTL */
+#define LAB_PD_CTL_STRONG_PULL		BIT(0)
+#define LAB_PD_CTL_STRENGTH_MASK	BIT(0)
+#define LAB_PD_CTL_DISABLE_PD		BIT(1)
+#define LAB_PD_CTL_EN_MASK		BIT(1)
+
+/* REG_LAB_IBB_EN_RDY */
+#define LAB_IBB_EN_RDY_EN		BIT(7)
+
+/* REG_LAB_CURRENT_LIMIT */
+#define LAB_CURRENT_LIMIT_MASK		GENMASK(2, 0)
+#define LAB_CURRENT_LIMIT_EN_BIT	BIT(7)
+#define LAB_OVERRIDE_CURRENT_MAX_BIT	BIT(3)
+
+/* REG_LAB_CURRENT_SENSE */
+#define LAB_CURRENT_SENSE_GAIN_MASK	GENMASK(1, 0)
+
+/* REG_LAB_PS_CTL */
+#define LAB_PS_THRESH_MASK		GENMASK(1, 0)
+#define LAB_PS_CTL_EN			BIT(7)
+
+/* REG_LAB_RDSON_MNGMNT */
+#define LAB_RDSON_MNGMNT_NFET_SLEW_EN	BIT(5)
+#define LAB_RDSON_MNGMNT_PFET_SLEW_EN	BIT(4)
+#define LAB_RDSON_MNGMNT_NFET_MASK	GENMASK(3, 2)
+#define LAB_RDSON_MNGMNT_NFET_SHIFT	2
+#define LAB_RDSON_MNGMNT_PFET_MASK	GENMASK(1, 0)
+#define LAB_RDSON_NFET_SW_SIZE_QUARTER	0x0
+#define LAB_RDSON_PFET_SW_SIZE_QUARTER	0x0
+
+/* REG_LAB_PRECHARGE_CTL */
+#define LAB_FAST_PRECHARGE_CTL_EN	BIT(2)
+#define LAB_MAX_PRECHARGE_TIME_MASK	GENMASK(1, 0)
+
+/* REG_LAB_SOFT_START_CTL */
+#define LAB_SOFT_START_CTL_MASK		GENMASK(1, 0)
+
+/* REG_LAB_SPARE_CTL */
+#define LAB_SPARE_TOUCH_WAKE_BIT	BIT(3)
+#define LAB_SPARE_DISABLE_SCP_BIT	BIT(0)
+
+/* REG_LAB_PFM_CTL */
+#define LAB_PFM_EN_BIT			BIT(7)
+
+/* REG_LAB_SW_HIGH_PSRR_CTL */
+#define LAB_EN_SW_HIGH_PSRR_MODE	BIT(7)
+#define LAB_SW_HIGH_PSRR_REQ		BIT(0)
+
+/* REG_LAB_VPH_ENVELOP_CTL */
+#define LAB_VREF_HIGH_PSRR_SEL_MASK	GENMASK(7, 6)
+#define LAB_SEL_HW_HIGH_PSRR_SRC_MASK	GENMASK(1, 0)
+#define LAB_SEL_HW_HIGH_PSRR_SRC_SHIFT	6
+
+/* IBB register offset definitions */
+#define REG_IBB_REVISION4		0x03
+#define REG_IBB_STATUS1			0x08
+#define REG_IBB_VOLTAGE			0x41
+#define REG_IBB_RING_SUPPRESSION_CTL	0x42
+#define REG_IBB_LCD_AMOLED_SEL		0x44
+#define REG_IBB_MODULE_RDY		0x45
+#define REG_IBB_ENABLE_CTL		0x46
+#define REG_IBB_PD_CTL			0x47
+#define REG_IBB_CLK_DIV			0x48
+#define REG_IBB_CURRENT_LIMIT		0x4B
+#define REG_IBB_PS_CTL			0x50
+#define REG_IBB_RDSON_MNGMNT		0x53
+#define REG_IBB_NONOVERLAP_TIME_1	0x56
+#define REG_IBB_NONOVERLAP_TIME_2	0x57
+#define REG_IBB_PWRUP_PWRDN_CTL_1	0x58
+#define REG_IBB_PWRUP_PWRDN_CTL_2	0x59
+#define REG_IBB_SOFT_START_CTL		0x5F
+#define REG_IBB_SWIRE_CTL		0x5A
+#define REG_IBB_OUTPUT_SLEW_CTL		0x5D
+#define REG_IBB_SPARE_CTL		0x60
+#define REG_IBB_NLIMIT_DAC		0x61
+
+/* IBB registers for PM660A */
+#define REG_IBB_DEFAULT_VOLTAGE		0x40
+#define REG_IBB_FLOAT_CTL		0x43
+#define REG_IBB_VREG_OK_CTL		0x55
+#define REG_IBB_VOUT_MIN_MAGNITUDE	0x5C
+#define REG_IBB_PFM_CTL			0x62
+#define REG_IBB_SMART_PS_CTL		0x65
+#define REG_IBB_ADAPT_DEAD_TIME		0x67
+
+/* IBB register bits definition */
+
+/* REG_IBB_STATUS1 */
+#define IBB_STATUS1_VREG_OK_MASK	BIT(7)
+#define IBB_STATUS1_VREG_OK		BIT(7)
+
+/* REG_IBB_VOLTAGE */
+#define IBB_VOLTAGE_OVERRIDE_EN		BIT(7)
+#define IBB_VOLTAGE_SET_MASK		GENMASK(5, 0)
+
+/* REG_IBB_CLK_DIV */
+#define IBB_CLK_DIV_OVERRIDE_EN		BIT(7)
+#define IBB_CLK_DIV_MASK		GENMASK(3, 0)
+
+/* REG_IBB_RING_SUPPRESSION_CTL */
+#define IBB_RING_SUPPRESSION_CTL_EN	BIT(7)
+
+/* REG_IBB_FLOAT_CTL */
+#define IBB_FLOAT_EN			BIT(0)
+#define IBB_SMART_FLOAT_EN		BIT(7)
+
+/* REG_IBB_MIN_MAGNITUDE */
+#define IBB_MIN_VOLTAGE_0P8_V		BIT(3)
+
+/* REG_IBB_MODULE_RDY */
+#define IBB_MODULE_RDY_EN		BIT(7)
+
+/* REG_IBB_ENABLE_CTL */
+#define IBB_ENABLE_CTL_MASK		(BIT(7) | BIT(6))
+#define IBB_ENABLE_CTL_SWIRE_RDY	BIT(6)
+#define IBB_ENABLE_CTL_MODULE_EN	BIT(7)
+
+/* REG_IBB_PD_CTL */
+#define IBB_PD_CTL_HALF_STRENGTH	BIT(0)
+#define IBB_PD_CTL_STRENGTH_MASK	BIT(0)
+#define IBB_PD_CTL_EN			BIT(7)
+#define IBB_SWIRE_PD_UPD		BIT(1)
+#define IBB_PD_CTL_EN_MASK		BIT(7)
+
+/* REG_IBB_CURRENT_LIMIT */
+#define IBB_CURRENT_LIMIT_MASK		GENMASK(4, 0)
+#define IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT	5
+#define IBB_CURRENT_LIMIT_DEBOUNCE_MASK	GENMASK(6, 5)
+#define IBB_CURRENT_LIMIT_EN		BIT(7)
+#define IBB_ILIMIT_COUNT_CYC8		0
+#define IBB_CURRENT_MAX_500MA		0xA
+
+/* REG_IBB_PS_CTL */
+#define IBB_PS_CTL_EN			0x85
+
+/* REG_IBB_SMART_PS_CTL */
+#define IBB_SMART_PS_CTL_EN			BIT(7)
+#define IBB_NUM_SWIRE_PULSE_WAIT		0x5
+
+/* REG_IBB_OUTPUT_SLEW_CTL */
+#define IBB_SLEW_CTL_EN				BIT(7)
+#define IBB_SLEW_RATE_SPEED_FAST_EN		BIT(6)
+#define IBB_SLEW_RATE_TRANS_TIME_FAST_SHIFT	3
+#define IBB_SLEW_RATE_TRANS_TIME_FAST_MASK	GENMASK(5, 3)
+#define IBB_SLEW_RATE_TRANS_TIME_SLOW_MASK	GENMASK(2, 0)
+
+/* REG_IBB_VREG_OK_CTL */
+#define IBB_VREG_OK_EN_OVERLOAD_BLANK		BIT(7)
+#define IBB_VREG_OK_OVERLOAD_DEB_SHIFT		5
+#define IBB_VREG_OK_OVERLOAD_DEB_MASK		GENMASK(6, 5)
+
+/* REG_IBB_RDSON_MNGMNT */
+#define IBB_NFET_SLEW_EN		BIT(7)
+#define IBB_PFET_SLEW_EN		BIT(6)
+#define IBB_OVERRIDE_NFET_SW_SIZE	BIT(5)
+#define IBB_OVERRIDE_PFET_SW_SIZE	BIT(2)
+#define IBB_NFET_SW_SIZE_MASK		GENMASK(3, 2)
+#define IBB_PFET_SW_SIZE_MASK		GENMASK(1, 0)
+
+/* REG_IBB_NONOVERLAP_TIME_1 */
+#define IBB_OVERRIDE_NONOVERLAP		BIT(6)
+#define IBB_NONOVERLAP_NFET_MASK	GENMASK(2, 0)
+#define IBB_NFET_GATE_DELAY_2		0x3
+
+/* REG_IBB_NONOVERLAP_TIME_2 */
+#define IBB_N2P_MUX_SEL		BIT(0)
+
+/* REG_IBB_SOFT_START_CTL */
+#define IBB_SOFT_START_CHARGING_RESISTOR_16K	0x3
+
+/* REG_IBB_SPARE_CTL */
+#define IBB_BYPASS_PWRDN_DLY2_BIT	BIT(5)
+#define IBB_POFF_CTL_MASK		BIT(4)
+#define IBB_FASTER_PFET_OFF		BIT(4)
+#define IBB_FAST_STARTUP		BIT(3)
+
+/* REG_IBB_SWIRE_CTL */
+#define IBB_SWIRE_VOUT_UPD_EN		BIT(6)
+#define IBB_OUTPUT_VOLTAGE_AT_ONE_PULSE_MASK	GENMASK(5, 0)
+#define MAX_OUTPUT_EDGE_VOLTAGE_MV	6300
+#define MAX_OUTPUT_PULSE_VOLTAGE_MV	7700
+#define MIN_OUTPUT_PULSE_VOLTAGE_MV	1400
+#define OUTPUT_VOLTAGE_STEP_MV		100
+
+/* REG_IBB_NLIMIT_DAC */
+#define IBB_DEFAULT_NLIMIT_DAC		0x5
+
+/* REG_IBB_PFM_CTL */
+#define IBB_PFM_ENABLE			BIT(7)
+#define IBB_PFM_PEAK_CURRENT_BIT_SHIFT	1
+#define IBB_PFM_PEAK_CURRENT_MASK	GENMASK(3, 1)
+#define IBB_PFM_HYSTERESIS_BIT_SHIFT	4
+#define IBB_PFM_HYSTERESIS_MASK		GENMASK(5, 4)
+
+/* REG_IBB_PWRUP_PWRDN_CTL_1 */
+#define IBB_PWRUP_PWRDN_CTL_1_DLY1_BITS	2
+#define IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK	GENMASK(5, 4)
+#define IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT	4
+#define IBB_PWRUP_PWRDN_CTL_1_EN_DLY2	BIT(3)
+#define IBB_PWRUP_PWRDN_CTL_1_DLY2_MASK	GENMASK(1, 0)
+#define IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK	BIT(7)
+#define IBB_PWRUP_PWRDN_CTL_1_EN_DLY1	BIT(6)
+#define PWRUP_PWRDN_CTL_1_DISCHARGE_EN	BIT(2)
+
+/* REG_IBB_PWRUP_PWRDN_CTL_2 */
+#define IBB_DIS_DLY_MASK		GENMASK(1, 0)
+#define IBB_WAIT_MBG_OK			BIT(2)
+
+/* Constants */
+#define SWIRE_DEFAULT_2ND_CMD_DLY_MS		20
+#define SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS	200
+#define IBB_HW_DEFAULT_SLEW_RATE		12000
+
+/**
+ * enum qpnp_labibb_mode - working mode of LAB/IBB regulators
+ * %QPNP_LABIBB_LCD_MODE:		configure LAB and IBB regulators
+ * together to provide power supply for LCD
+ * %QPNP_LABIBB_AMOLED_MODE:		configure LAB and IBB regulators
+ * together to provide power supply for AMOLED
+ * %QPNP_LABIBB_MAX_MODE		max number of configureable modes
+ * supported by qpnp_labibb_regulator
+ */
+enum qpnp_labibb_mode {
+	QPNP_LABIBB_LCD_MODE,
+	QPNP_LABIBB_AMOLED_MODE,
+	QPNP_LABIBB_MAX_MODE,
+};
+
+/**
+ * IBB_SW_CONTROL_EN: Specifies IBB is enabled through software.
+ * IBB_SW_CONTROL_DIS: Specifies IBB is disabled through software.
+ * IBB_HW_CONTROL: Specifies IBB is controlled through SWIRE (hardware).
+ */
+enum ibb_mode {
+	IBB_SW_CONTROL_EN,
+	IBB_SW_CONTROL_DIS,
+	IBB_HW_CONTROL,
+	IBB_HW_SW_CONTROL,
+};
+
+static const int ibb_dischg_res_table[] = {
+	300,
+	64,
+	32,
+	16,
+};
+
+static const int ibb_pwrup_dly_table[] = {
+	1000,
+	2000,
+	4000,
+	8000,
+};
+
+static const int ibb_pwrdn_dly_table[] = {
+	1000,
+	2000,
+	4000,
+	8000,
+};
+
+static const int lab_clk_div_table[] = {
+	3200,
+	2740,
+	2400,
+	2130,
+	1920,
+	1750,
+	1600,
+	1480,
+	1370,
+	1280,
+	1200,
+	1130,
+	1070,
+	1010,
+	960,
+	910,
+};
+
+static const int ibb_clk_div_table[] = {
+	3200,
+	2740,
+	2400,
+	2130,
+	1920,
+	1750,
+	1600,
+	1480,
+	1370,
+	1280,
+	1200,
+	1130,
+	1070,
+	1010,
+	960,
+	910,
+};
+
+static const int lab_current_limit_table[] = {
+	200,
+	400,
+	600,
+	800,
+	1000,
+	1200,
+	1400,
+	1600,
+};
+
+static const char * const lab_current_sense_table[] = {
+	"0.5x",
+	"1x",
+	"1.5x",
+	"2x"
+};
+
+static const int ibb_current_limit_table[] = {
+	0,
+	50,
+	100,
+	150,
+	200,
+	250,
+	300,
+	350,
+	400,
+	450,
+	500,
+	550,
+	600,
+	650,
+	700,
+	750,
+	800,
+	850,
+	900,
+	950,
+	1000,
+	1050,
+	1100,
+	1150,
+	1200,
+	1250,
+	1300,
+	1350,
+	1400,
+	1450,
+	1500,
+	1550,
+};
+
+static const int ibb_output_slew_ctl_table[] = {
+	100,
+	200,
+	500,
+	1000,
+	2000,
+	10000,
+	12000,
+	15000
+};
+
+static const int ibb_debounce_table[] = {
+	8,
+	16,
+	32,
+	64,
+};
+
+static const int ibb_overload_debounce_table[] = {
+	1,
+	2,
+	4,
+	8
+};
+
+static const int ibb_vreg_ok_deb_table[] = {
+	4,
+	8,
+	16,
+	32
+};
+
+static const int lab_ps_thresh_table_v1[] = {
+	20,
+	30,
+	40,
+	50,
+};
+
+static const int lab_ps_thresh_table_v2[] = {
+	50,
+	60,
+	70,
+	80,
+};
+
+static const int lab_soft_start_table[] = {
+	200,
+	400,
+	600,
+	800,
+};
+
+static const int lab_rdson_nfet_table[] = {
+	25,
+	50,
+	75,
+	100,
+};
+
+static const int lab_rdson_pfet_table[] = {
+	25,
+	50,
+	75,
+	100,
+};
+
+static const int lab_max_precharge_table[] = {
+	200,
+	300,
+	400,
+	500,
+};
+
+static const int ibb_pfm_peak_curr_table[] = {
+	150,
+	200,
+	250,
+	300,
+	350,
+	400,
+	450,
+	500
+};
+
+static const int ibb_pfm_hysteresis_table[] = {
+	0,
+	25,
+	50,
+	0
+};
+
+static const int lab_vref_high_psrr_table[] = {
+	350,
+	400,
+	450,
+	500
+};
+
+struct lab_regulator {
+	struct regulator_desc		rdesc;
+	struct regulator_dev		*rdev;
+	struct mutex			lab_mutex;
+
+	int				lab_vreg_ok_irq;
+	int				curr_volt;
+	int				min_volt;
+
+	int				step_size;
+	int				slew_rate;
+	int				soft_start;
+
+	int				vreg_enabled;
+};
+
+struct ibb_regulator {
+	struct regulator_desc		rdesc;
+	struct regulator_dev		*rdev;
+	struct mutex			ibb_mutex;
+
+	int				curr_volt;
+	int				min_volt;
+
+	int				step_size;
+	int				slew_rate;
+	int				soft_start;
+
+	u32				pwrup_dly;
+	u32				pwrdn_dly;
+
+	int				vreg_enabled;
+	int				num_swire_trans;
+};
+
+struct qpnp_labibb {
+	struct device			*dev;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct pmic_revid_data		*pmic_rev_id;
+	u16				lab_base;
+	u16				ibb_base;
+	u8				lab_dig_major;
+	u8				ibb_dig_major;
+	struct lab_regulator		lab_vreg;
+	struct ibb_regulator		ibb_vreg;
+	const struct ibb_ver_ops	*ibb_ver_ops;
+	const struct lab_ver_ops	*lab_ver_ops;
+	struct mutex			bus_mutex;
+	enum qpnp_labibb_mode		mode;
+	bool				standalone;
+	bool				ttw_en;
+	bool				in_ttw_mode;
+	bool				ibb_settings_saved;
+	bool				swire_control;
+	bool				pbs_control;
+	bool				ttw_force_lab_on;
+	bool				skip_2nd_swire_cmd;
+	bool				pfm_enable;
+	u32				swire_2nd_cmd_delay;
+	u32				swire_ibb_ps_enable_delay;
+};
+
+struct ibb_ver_ops {
+	int (*set_default_voltage)(struct qpnp_labibb *labibb,
+			bool use_default);
+	int (*set_voltage)(struct qpnp_labibb *labibb, int min_uV, int max_uV);
+	int (*sel_mode)(struct qpnp_labibb *labibb, bool is_ibb);
+	int (*get_mode)(struct qpnp_labibb *labibb);
+	int (*set_clk_div)(struct qpnp_labibb *labibb, u8 val);
+	int (*smart_ps_config)(struct qpnp_labibb *labibb, bool enable,
+				int num_swire_trans, int neg_curr_limit);
+	int (*soft_start_ctl)(struct qpnp_labibb *labibb,
+				 struct device_node *of_node);
+	int (*voltage_at_one_pulse)(struct qpnp_labibb *labibb, u32 volt);
+};
+
+struct lab_ver_ops {
+	const char *ver_str;
+	int (*set_default_voltage)(struct qpnp_labibb *labibb,
+					bool default_pres);
+	int (*ps_ctl)(struct qpnp_labibb *labibb,
+				u32 thresh, bool enable);
+};
+
+enum ibb_settings_index {
+	IBB_PD_CTL = 0,
+	IBB_CURRENT_LIMIT,
+	IBB_RDSON_MNGMNT,
+	IBB_PWRUP_PWRDN_CTL_1,
+	IBB_PWRUP_PWRDN_CTL_2,
+	IBB_NLIMIT_DAC,
+	IBB_PS_CTL,
+	IBB_SOFT_START_CTL,
+	IBB_SETTINGS_MAX,
+};
+
+enum lab_settings_index {
+	LAB_SOFT_START_CTL = 0,
+	LAB_PS_CTL,
+	LAB_RDSON_MNGMNT,
+	LAB_SETTINGS_MAX,
+};
+
+struct settings {
+	u16	address;
+	u8	value;
+	bool	sec_access;
+};
+
+#define SETTING(_id, _sec_access)		\
+	[_id] = {				\
+		.address = REG_##_id,		\
+		.sec_access = _sec_access,	\
+	}
+
+static struct settings ibb_settings[IBB_SETTINGS_MAX] = {
+	SETTING(IBB_PD_CTL, false),
+	SETTING(IBB_CURRENT_LIMIT, true),
+	SETTING(IBB_RDSON_MNGMNT, false),
+	SETTING(IBB_PWRUP_PWRDN_CTL_1, true),
+	SETTING(IBB_PWRUP_PWRDN_CTL_2, true),
+	SETTING(IBB_NLIMIT_DAC, false),
+	SETTING(IBB_PS_CTL, false),
+	SETTING(IBB_SOFT_START_CTL, false),
+};
+
+static struct settings lab_settings[LAB_SETTINGS_MAX] = {
+	SETTING(LAB_SOFT_START_CTL, false),
+	SETTING(LAB_PS_CTL, false),
+	SETTING(LAB_RDSON_MNGMNT, false),
+};
+
+static int
+qpnp_labibb_read(struct qpnp_labibb *labibb, u16 address,
+			u8 *val, int count)
+{
+	int rc = 0;
+	struct platform_device *pdev = labibb->pdev;
+
+	mutex_lock(&(labibb->bus_mutex));
+	rc = regmap_bulk_read(labibb->regmap, address, val, count);
+	if (rc < 0)
+		pr_err("SPMI read failed address=0x%02x sid=0x%02x rc=%d\n",
+			address, to_spmi_device(pdev->dev.parent)->usid, rc);
+
+	mutex_unlock(&(labibb->bus_mutex));
+	return rc;
+}
+
+static int
+qpnp_labibb_write(struct qpnp_labibb *labibb, u16 address,
+			u8 *val, int count)
+{
+	int rc = 0;
+	struct platform_device *pdev = labibb->pdev;
+
+	mutex_lock(&(labibb->bus_mutex));
+	if (address == 0) {
+		pr_err("address cannot be zero address=0x%02x sid=0x%02x rc=%d\n",
+			address, to_spmi_device(pdev->dev.parent)->usid, rc);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	rc = regmap_bulk_write(labibb->regmap, address, val, count);
+	if (rc < 0)
+		pr_err("write failed address=0x%02x sid=0x%02x rc=%d\n",
+			address, to_spmi_device(pdev->dev.parent)->usid, rc);
+
+error:
+	mutex_unlock(&(labibb->bus_mutex));
+	return rc;
+}
+
+static int
+qpnp_labibb_masked_write(struct qpnp_labibb *labibb, u16 address,
+						u8 mask, u8 val)
+{
+	int rc = 0;
+	struct platform_device *pdev = labibb->pdev;
+
+	mutex_lock(&(labibb->bus_mutex));
+	if (address == 0) {
+		pr_err("address cannot be zero address=0x%02x sid=0x%02x\n",
+			address, to_spmi_device(pdev->dev.parent)->usid);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	rc = regmap_update_bits(labibb->regmap, address, mask, val);
+	if (rc < 0)
+		pr_err("spmi write failed: addr=%03X, rc=%d\n", address, rc);
+
+error:
+	mutex_unlock(&(labibb->bus_mutex));
+	return rc;
+}
+
+static int qpnp_labibb_sec_write(struct qpnp_labibb *labibb, u16 base,
+					u8 offset, u8 val)
+{
+	int rc = 0;
+	u8 sec_val = REG_LAB_IBB_SEC_UNLOCK_CODE;
+	struct platform_device *pdev = labibb->pdev;
+
+	mutex_lock(&(labibb->bus_mutex));
+	if (base == 0) {
+		pr_err("base cannot be zero base=0x%02x sid=0x%02x\n",
+			base, to_spmi_device(pdev->dev.parent)->usid);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	rc = regmap_write(labibb->regmap, base + REG_LAB_IBB_SEC_ACCESS,
+				sec_val);
+	if (rc < 0) {
+		pr_err("register %x failed rc = %d\n",
+			base + REG_LAB_IBB_SEC_ACCESS, rc);
+		goto error;
+	}
+
+	rc = regmap_write(labibb->regmap, base + offset, val);
+	if (rc < 0)
+		pr_err("failed: addr=%03X, rc=%d\n",
+			base + offset, rc);
+
+error:
+	mutex_unlock(&(labibb->bus_mutex));
+	return rc;
+}
+
+static int qpnp_labibb_sec_masked_write(struct qpnp_labibb *labibb, u16 base,
+					u8 offset, u8 mask, u8 val)
+{
+	int rc = 0;
+	u8 sec_val = REG_LAB_IBB_SEC_UNLOCK_CODE;
+	struct platform_device *pdev = labibb->pdev;
+
+	mutex_lock(&(labibb->bus_mutex));
+	if (base == 0) {
+		pr_err("base cannot be zero base=0x%02x sid=0x%02x\n",
+			base, to_spmi_device(pdev->dev.parent)->usid);
+		rc = -EINVAL;
+		goto error;
+	}
+
+	rc = regmap_write(labibb->regmap, base + REG_LAB_IBB_SEC_ACCESS,
+				sec_val);
+	if (rc < 0) {
+		pr_err("register %x failed rc = %d\n",
+			base + REG_LAB_IBB_SEC_ACCESS, rc);
+		goto error;
+	}
+
+	rc = regmap_update_bits(labibb->regmap, base + offset, mask, val);
+	if (rc < 0)
+		pr_err("spmi write failed: addr=%03X, rc=%d\n", base, rc);
+
+error:
+	mutex_unlock(&(labibb->bus_mutex));
+	return rc;
+}
+
+static int qpnp_ibb_smart_ps_config_v1(struct qpnp_labibb *labibb, bool enable,
+					int num_swire_trans, int neg_curr_limit)
+{
+	return 0;
+}
+
+static int qpnp_ibb_smart_ps_config_v2(struct qpnp_labibb *labibb, bool enable,
+					int num_swire_trans, int neg_curr_limit)
+{
+	u8 val;
+	int rc = 0;
+
+	if (enable) {
+		val = IBB_NUM_SWIRE_PULSE_WAIT;
+		rc = qpnp_labibb_write(labibb,
+			labibb->ibb_base + REG_IBB_PS_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("write register %x failed rc = %d\n",
+						REG_IBB_PS_CTL, rc);
+			return rc;
+		}
+	}
+
+	val = enable ? IBB_SMART_PS_CTL_EN : IBB_NUM_SWIRE_PULSE_WAIT;
+	if (num_swire_trans)
+		val |= num_swire_trans;
+	else
+		val |= IBB_NUM_SWIRE_PULSE_WAIT;
+
+	rc = qpnp_labibb_write(labibb,
+		labibb->ibb_base + REG_IBB_SMART_PS_CTL, &val, 1);
+	if (rc < 0) {
+		pr_err("write register %x failed rc = %d\n",
+					REG_IBB_SMART_PS_CTL, rc);
+		return rc;
+	}
+
+	val = enable ? (neg_curr_limit ? neg_curr_limit :
+		IBB_DEFAULT_NLIMIT_DAC) : IBB_DEFAULT_NLIMIT_DAC;
+
+	rc = qpnp_labibb_write(labibb,
+		labibb->ibb_base + REG_IBB_NLIMIT_DAC, &val, 1);
+	if (rc < 0)
+		pr_err("write register %x failed rc = %d\n",
+					REG_IBB_NLIMIT_DAC, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_sel_mode_v1(struct qpnp_labibb *labibb, bool is_ibb)
+{
+	int rc = 0;
+	u8 val;
+	u16 base;
+
+	val = (labibb->mode == QPNP_LABIBB_LCD_MODE) ? REG_LAB_IBB_LCD_MODE :
+				 REG_LAB_IBB_AMOLED_MODE;
+
+	base = is_ibb ? labibb->ibb_base : labibb->lab_base;
+
+	rc = qpnp_labibb_sec_write(labibb, base, REG_LAB_LCD_AMOLED_SEL,
+					val);
+	if (rc < 0)
+		pr_err("register %x failed rc = %d\n",
+			REG_LAB_LCD_AMOLED_SEL, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_sel_mode_v2(struct qpnp_labibb *labibb, bool is_ibb)
+{
+	return 0;
+}
+
+static int qpnp_ibb_get_mode_v1(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val;
+
+	rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_LCD_AMOLED_SEL,
+				&val, 1);
+	if (rc < 0)
+		return rc;
+
+	if (val == REG_LAB_IBB_AMOLED_MODE)
+		labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+	else
+		labibb->mode = QPNP_LABIBB_LCD_MODE;
+
+	return 0;
+}
+
+static int qpnp_ibb_get_mode_v2(struct qpnp_labibb *labibb)
+{
+	labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+
+	return 0;
+}
+
+static int qpnp_ibb_set_clk_div_v1(struct qpnp_labibb *labibb, u8 val)
+{
+	int rc = 0;
+
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_CLK_DIV,
+				&val, 1);
+
+	return rc;
+}
+
+static int qpnp_ibb_set_clk_div_v2(struct qpnp_labibb *labibb, u8 val)
+{
+	int rc = 0;
+
+	val |= IBB_CLK_DIV_OVERRIDE_EN;
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+				REG_IBB_CLK_DIV, IBB_CLK_DIV_MASK |
+				IBB_CLK_DIV_OVERRIDE_EN, val);
+
+	return rc;
+}
+
+static int qpnp_ibb_soft_start_ctl_v1(struct qpnp_labibb *labibb,
+					struct device_node *of_node)
+{
+	int rc = 0;
+	u8 val;
+	u32 tmp;
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-soft-start",
+					&(labibb->ibb_vreg.soft_start));
+	if (rc < 0) {
+		pr_err("qcom,qpnp-ibb-soft-start is missing, rc = %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-discharge-resistor",
+			&tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(ibb_dischg_res_table); val++) {
+			if (ibb_dischg_res_table[val] == tmp)
+				break;
+		}
+
+		if (val == ARRAY_SIZE(ibb_dischg_res_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-discharge-resistor\n");
+			return -EINVAL;
+		}
+
+		rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+				REG_IBB_SOFT_START_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_IBB_SOFT_START_CTL,	rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_ibb_soft_start_ctl_v2(struct qpnp_labibb *labibb,
+			 struct device_node *of_node)
+{
+	return 0;
+}
+
+static int qpnp_ibb_vreg_ok_ctl(struct qpnp_labibb *labibb,
+			struct device_node *of_node)
+{
+	u8 val = 0;
+	int rc = 0, i = 0;
+	u32 tmp;
+
+	if (labibb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE)
+		return rc;
+
+	val |= IBB_VREG_OK_EN_OVERLOAD_BLANK;
+
+	rc = of_property_read_u32(of_node,
+				"qcom,qpnp-ibb-overload-debounce", &tmp);
+	if (rc < 0) {
+		pr_err("failed to read qcom,qpnp-ibb-overload-debounce rc=%d\n",
+								rc);
+		return rc;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ibb_overload_debounce_table); i++)
+		if (ibb_overload_debounce_table[i] == tmp)
+			break;
+
+	if (i == ARRAY_SIZE(ibb_overload_debounce_table)) {
+		pr_err("Invalid value in qcom,qpnp-ibb-overload-debounce\n");
+		return -EINVAL;
+	}
+	val |= i << IBB_VREG_OK_OVERLOAD_DEB_SHIFT;
+
+	rc = of_property_read_u32(of_node,
+				"qcom,qpnp-ibb-vreg-ok-debounce", &tmp);
+	if (rc < 0) {
+		pr_err("failed to read qcom,qpnp-ibb-vreg-ok-debounce rc=%d\n",
+								rc);
+		return rc;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ibb_vreg_ok_deb_table); i++)
+		if (ibb_vreg_ok_deb_table[i] == tmp)
+			break;
+
+	if (i == ARRAY_SIZE(ibb_vreg_ok_deb_table)) {
+		pr_err("Invalid value in qcom,qpnp-ibb-vreg-ok-debounce\n");
+		return -EINVAL;
+	}
+	val |= i;
+
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+				REG_IBB_VREG_OK_CTL,
+				&val, 1);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n",
+		 REG_IBB_VREG_OK_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_ibb_set_default_voltage_v1(struct qpnp_labibb *labibb,
+						 bool use_default)
+{
+	u8 val;
+	int rc = 0;
+
+	if (!use_default) {
+		if (labibb->ibb_vreg.curr_volt < labibb->ibb_vreg.min_volt) {
+			pr_err("qcom,qpnp-ibb-init-voltage %d is less than the the minimum voltage %d",
+			 labibb->ibb_vreg.curr_volt, labibb->ibb_vreg.min_volt);
+				return -EINVAL;
+		}
+
+		val = DIV_ROUND_UP(labibb->ibb_vreg.curr_volt -
+				labibb->ibb_vreg.min_volt,
+				labibb->ibb_vreg.step_size);
+		if (val > IBB_VOLTAGE_SET_MASK) {
+			pr_err("qcom,qpnp-lab-init-voltage %d is larger than the max supported voltage %ld",
+				labibb->ibb_vreg.curr_volt,
+				labibb->ibb_vreg.min_volt +
+				labibb->ibb_vreg.step_size *
+				IBB_VOLTAGE_SET_MASK);
+			return -EINVAL;
+		}
+
+		labibb->ibb_vreg.curr_volt = val * labibb->ibb_vreg.step_size +
+				labibb->ibb_vreg.min_volt;
+		val |= IBB_VOLTAGE_OVERRIDE_EN;
+	} else {
+		val = 0;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+			REG_IBB_VOLTAGE, IBB_VOLTAGE_SET_MASK |
+			IBB_VOLTAGE_OVERRIDE_EN, val);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n", REG_IBB_VOLTAGE,
+			rc);
+
+	return rc;
+}
+
+static int qpnp_ibb_set_default_voltage_v2(struct qpnp_labibb *labibb,
+						bool use_default)
+{
+	int rc = 0;
+	u8 val;
+
+	val = DIV_ROUND_UP(labibb->ibb_vreg.curr_volt,
+			labibb->ibb_vreg.step_size);
+	if (val > IBB_VOLTAGE_SET_MASK) {
+		pr_err("Invalid qcom,qpnp-ibb-init-voltage property %d",
+			labibb->ibb_vreg.curr_volt);
+		return -EINVAL;
+	}
+
+	labibb->ibb_vreg.curr_volt = val * labibb->ibb_vreg.step_size;
+
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+				REG_IBB_DEFAULT_VOLTAGE, &val, 1);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n",
+			 REG_IBB_DEFAULT_VOLTAGE, rc);
+
+	return rc;
+}
+
+static int qpnp_ibb_set_voltage_v1(struct qpnp_labibb *labibb,
+				 int min_uV, int max_uV)
+{
+	int rc, new_uV;
+	u8 val;
+
+	if (min_uV < labibb->ibb_vreg.min_volt) {
+		pr_err("min_uV %d is less than min_volt %d", min_uV,
+			labibb->ibb_vreg.min_volt);
+		return -EINVAL;
+	}
+
+	val = DIV_ROUND_UP(min_uV - labibb->ibb_vreg.min_volt,
+				labibb->ibb_vreg.step_size);
+	new_uV = val * labibb->ibb_vreg.step_size + labibb->ibb_vreg.min_volt;
+
+	if (new_uV > max_uV) {
+		pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV,
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+				REG_IBB_VOLTAGE,
+				IBB_VOLTAGE_SET_MASK |
+				IBB_VOLTAGE_OVERRIDE_EN,
+				val | IBB_VOLTAGE_OVERRIDE_EN);
+
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n", REG_IBB_VOLTAGE,
+			rc);
+		return rc;
+	}
+
+	if (new_uV > labibb->ibb_vreg.curr_volt) {
+		val = DIV_ROUND_UP(new_uV - labibb->ibb_vreg.curr_volt,
+				labibb->ibb_vreg.step_size);
+		udelay(val * labibb->ibb_vreg.slew_rate);
+	}
+	labibb->ibb_vreg.curr_volt = new_uV;
+
+	return 0;
+}
+
+static int qpnp_ibb_set_voltage_v2(struct qpnp_labibb *labibb,
+				int min_uV, int max_uV)
+{
+	int rc, new_uV;
+	u8 val;
+
+	val = DIV_ROUND_UP(min_uV, labibb->ibb_vreg.step_size);
+	new_uV = val * labibb->ibb_vreg.step_size;
+
+	if (new_uV > max_uV) {
+		pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV,
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+				REG_IBB_VOLTAGE, &val, 1);
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n", REG_IBB_VOLTAGE,
+			rc);
+		return rc;
+	}
+
+	if (new_uV > labibb->ibb_vreg.curr_volt) {
+		val = DIV_ROUND_UP(new_uV - labibb->ibb_vreg.curr_volt,
+				labibb->ibb_vreg.step_size);
+		udelay(val * labibb->ibb_vreg.slew_rate);
+	}
+	labibb->ibb_vreg.curr_volt = new_uV;
+
+	return 0;
+}
+
+static int qpnp_ibb_output_voltage_at_one_pulse_v1(struct qpnp_labibb *labibb,
+						u32 volt)
+{
+	int rc = 0;
+	u8 val;
+
+	/*
+	 * Set the output voltage 100mV lower as the IBB HW module
+	 * counts one pulse less in SWIRE mode.
+	 */
+	val = DIV_ROUND_UP((volt - MIN_OUTPUT_PULSE_VOLTAGE_MV),
+				OUTPUT_VOLTAGE_STEP_MV) - 1;
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+			REG_IBB_SWIRE_CTL,
+			IBB_OUTPUT_VOLTAGE_AT_ONE_PULSE_MASK,
+			val);
+	if (rc < 0)
+		pr_err("write register %x failed rc = %d\n",
+			REG_IBB_SWIRE_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_ibb_output_voltage_at_one_pulse_v2(struct qpnp_labibb *labibb,
+						u32 volt)
+{
+	int rc = 0;
+	u8 val;
+
+	val = DIV_ROUND_UP(volt, OUTPUT_VOLTAGE_STEP_MV);
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+			REG_IBB_SWIRE_CTL,
+			IBB_OUTPUT_VOLTAGE_AT_ONE_PULSE_MASK,
+			val);
+	if (rc < 0)
+		pr_err("qpnp_labiibb_write register %x failed rc = %d\n",
+			REG_IBB_SWIRE_CTL, rc);
+
+	return rc;
+}
+
+static const struct ibb_ver_ops ibb_ops_v1 = {
+	.set_default_voltage	= qpnp_ibb_set_default_voltage_v1,
+	.set_voltage		= qpnp_ibb_set_voltage_v1,
+	.sel_mode		= qpnp_labibb_sel_mode_v1,
+	.get_mode		= qpnp_ibb_get_mode_v1,
+	.set_clk_div		= qpnp_ibb_set_clk_div_v1,
+	.smart_ps_config	= qpnp_ibb_smart_ps_config_v1,
+	.soft_start_ctl		= qpnp_ibb_soft_start_ctl_v1,
+	.voltage_at_one_pulse	= qpnp_ibb_output_voltage_at_one_pulse_v1,
+};
+
+static const struct ibb_ver_ops ibb_ops_v2 = {
+	.set_default_voltage	= qpnp_ibb_set_default_voltage_v2,
+	.set_voltage		= qpnp_ibb_set_voltage_v2,
+	.sel_mode		= qpnp_labibb_sel_mode_v2,
+	.get_mode		= qpnp_ibb_get_mode_v2,
+	.set_clk_div		= qpnp_ibb_set_clk_div_v2,
+	.smart_ps_config	= qpnp_ibb_smart_ps_config_v2,
+	.soft_start_ctl		= qpnp_ibb_soft_start_ctl_v2,
+	.voltage_at_one_pulse	= qpnp_ibb_output_voltage_at_one_pulse_v2,
+};
+
+static int qpnp_lab_set_default_voltage_v1(struct qpnp_labibb *labibb,
+						 bool default_pres)
+{
+	u8 val;
+	int rc = 0;
+
+	if (!default_pres) {
+		if (labibb->lab_vreg.curr_volt < labibb->lab_vreg.min_volt) {
+			pr_err("qcom,qpnp-lab-init-voltage %d is less than the the minimum voltage %d",
+				labibb->lab_vreg.curr_volt,
+				labibb->lab_vreg.min_volt);
+			return -EINVAL;
+		}
+
+		val = DIV_ROUND_UP(labibb->lab_vreg.curr_volt -
+				labibb->lab_vreg.min_volt,
+				labibb->lab_vreg.step_size);
+		if (val > LAB_VOLTAGE_SET_MASK) {
+			pr_err("qcom,qpnp-lab-init-voltage %d is larger than the max supported voltage %ld",
+				labibb->lab_vreg.curr_volt,
+				labibb->lab_vreg.min_volt +
+				labibb->lab_vreg.step_size *
+				LAB_VOLTAGE_SET_MASK);
+			return -EINVAL;
+		}
+
+		labibb->lab_vreg.curr_volt = val * labibb->lab_vreg.step_size +
+				labibb->lab_vreg.min_volt;
+		val |= LAB_VOLTAGE_OVERRIDE_EN;
+
+	} else {
+		val = 0;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_VOLTAGE, LAB_VOLTAGE_SET_MASK |
+				LAB_VOLTAGE_OVERRIDE_EN, val);
+
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n", REG_LAB_VOLTAGE,
+			rc);
+
+	return rc;
+}
+
+static int qpnp_lab_set_default_voltage_v2(struct qpnp_labibb *labibb,
+						 bool default_pres)
+{
+	int rc = 0;
+	u8 val;
+
+	val = DIV_ROUND_UP((labibb->lab_vreg.curr_volt
+		 - labibb->lab_vreg.min_volt), labibb->lab_vreg.step_size);
+
+	rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_VOUT_DEFAULT, &val, 1);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n",
+			 REG_LAB_VOUT_DEFAULT, rc);
+
+	return rc;
+}
+
+static int qpnp_lab_ps_ctl_v1(struct qpnp_labibb *labibb,
+					u32 thresh, bool enable)
+{
+	int rc = 0;
+	u8 val;
+
+	if (enable) {
+		for (val = 0; val < ARRAY_SIZE(lab_ps_thresh_table_v1); val++)
+			if (lab_ps_thresh_table_v1[val] == thresh)
+				break;
+
+		if (val == ARRAY_SIZE(lab_ps_thresh_table_v1)) {
+			pr_err("Invalid value in qcom,qpnp-lab-ps-threshold\n");
+			return -EINVAL;
+		}
+
+		val |= LAB_PS_CTL_EN;
+	} else {
+		val = 0;
+	}
+
+	rc = qpnp_labibb_write(labibb, labibb->lab_base +
+			 REG_LAB_PS_CTL, &val, 1);
+
+	if (rc < 0)
+		pr_err("write register %x failed rc = %d\n",
+				REG_LAB_PS_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_lab_ps_ctl_v2(struct qpnp_labibb *labibb,
+				u32 thresh, bool enable)
+{
+	int rc = 0;
+	u8 val;
+
+	if (enable) {
+		for (val = 0; val < ARRAY_SIZE(lab_ps_thresh_table_v2); val++)
+			if (lab_ps_thresh_table_v2[val] == thresh)
+				break;
+
+		if (val == ARRAY_SIZE(lab_ps_thresh_table_v2)) {
+			pr_err("Invalid value in qcom,qpnp-lab-ps-threshold\n");
+			return -EINVAL;
+		}
+
+		val |= LAB_PS_CTL_EN;
+	} else {
+		val = 0;
+	}
+
+	rc = qpnp_labibb_write(labibb, labibb->lab_base +
+			 REG_LAB_PS_CTL, &val, 1);
+
+	if (rc < 0)
+		pr_err("write register %x failed rc = %d\n",
+				REG_LAB_PS_CTL, rc);
+
+	return rc;
+}
+
+static const struct lab_ver_ops lab_ops_v1 = {
+	.set_default_voltage	= qpnp_lab_set_default_voltage_v1,
+	.ps_ctl			= qpnp_lab_ps_ctl_v1,
+};
+
+static const struct lab_ver_ops lab_ops_v2 = {
+	.set_default_voltage	= qpnp_lab_set_default_voltage_v2,
+	.ps_ctl			= qpnp_lab_ps_ctl_v2,
+};
+
+static int qpnp_labibb_get_matching_idx(const char *val)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(lab_current_sense_table); i++)
+		if (!strcmp(lab_current_sense_table[i], val))
+			return i;
+
+	return -EINVAL;
+}
+
+static int qpnp_ibb_set_mode(struct qpnp_labibb *labibb, enum ibb_mode mode)
+{
+	int rc;
+	u8 val;
+
+	if (mode == IBB_SW_CONTROL_EN)
+		val = IBB_ENABLE_CTL_MODULE_EN;
+	else if (mode == IBB_HW_CONTROL)
+		val = IBB_ENABLE_CTL_SWIRE_RDY;
+	else if (mode == IBB_HW_SW_CONTROL)
+		val = IBB_ENABLE_CTL_MODULE_EN | IBB_ENABLE_CTL_SWIRE_RDY;
+	else if (mode == IBB_SW_CONTROL_DIS)
+		val = 0;
+	else
+		return -EINVAL;
+
+	rc = qpnp_labibb_masked_write(labibb,
+		labibb->ibb_base + REG_IBB_ENABLE_CTL,
+		IBB_ENABLE_CTL_MASK, val);
+	if (rc < 0)
+		pr_err("Unable to configure IBB_ENABLE_CTL rc=%d\n", rc);
+
+	return rc;
+}
+
+static int qpnp_ibb_ps_config(struct qpnp_labibb *labibb, bool enable)
+{
+	u8 val;
+	int rc;
+
+	val = enable ? IBB_PS_CTL_EN : IBB_NUM_SWIRE_PULSE_WAIT;
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PS_CTL,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("write register %x failed rc = %d\n",
+					REG_IBB_PS_CTL, rc);
+		return rc;
+	}
+
+	val = enable ? 0 : IBB_DEFAULT_NLIMIT_DAC;
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_NLIMIT_DAC,
+							&val, 1);
+	if (rc < 0)
+		pr_err("write register %x failed rc = %d\n",
+					REG_IBB_NLIMIT_DAC, rc);
+	return rc;
+}
+
+static int qpnp_lab_dt_init(struct qpnp_labibb *labibb,
+				struct device_node *of_node)
+{
+	int rc = 0;
+	u8 i, val, mask;
+	u32 tmp;
+
+	/*
+	 * Do not configure LCD_AMOLED_SEL for pmi8998 as it will be done by
+	 * GPIO selector.
+	 */
+	if (labibb->pmic_rev_id->pmic_subtype != PMI8998_SUBTYPE) {
+		rc = labibb->ibb_ver_ops->sel_mode(labibb, 0);
+		if (rc < 0)
+			return rc;
+	}
+
+	val = 0;
+	if (of_property_read_bool(of_node, "qcom,qpnp-lab-full-pull-down"))
+		val |= LAB_PD_CTL_STRONG_PULL;
+
+	if (!of_property_read_bool(of_node, "qcom,qpnp-lab-pull-down-enable"))
+		val |= LAB_PD_CTL_DISABLE_PD;
+
+	mask = LAB_PD_CTL_EN_MASK | LAB_PD_CTL_STRENGTH_MASK;
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base + REG_LAB_PD_CTL,
+					mask, val);
+
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_PD_CTL, rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-lab-switching-clock-frequency", &tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(lab_clk_div_table); val++)
+			if (lab_clk_div_table[val] == tmp)
+				break;
+
+		if (val == ARRAY_SIZE(lab_clk_div_table)) {
+			pr_err("Invalid value in qpnp-lab-switching-clock-frequency\n");
+			return -EINVAL;
+		}
+
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_CLK_DIV, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_CLK_DIV, rc);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(of_node,
+		"qcom,qpnp-lab-limit-max-current-enable")) {
+		val = LAB_CURRENT_LIMIT_EN_BIT;
+
+		rc = of_property_read_u32(of_node,
+			"qcom,qpnp-lab-limit-maximum-current", &tmp);
+
+		if (rc < 0) {
+			pr_err("get qcom,qpnp-lab-limit-maximum-current failed rc = %d\n",
+				rc);
+			return rc;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(lab_current_limit_table); i++)
+			if (lab_current_limit_table[i] == tmp)
+				break;
+
+		if (i == ARRAY_SIZE(lab_current_limit_table)) {
+			pr_err("Invalid value in qcom,qpnp-lab-limit-maximum-current\n");
+			return -EINVAL;
+		}
+
+		val |= i;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_CURRENT_LIMIT, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+					REG_LAB_CURRENT_LIMIT, rc);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(of_node,
+		"qcom,qpnp-lab-ring-suppression-enable")) {
+		val = LAB_RING_SUPPRESSION_CTL_EN;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_RING_SUPPRESSION_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_RING_SUPPRESSION_CTL, rc);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(of_node, "qcom,qpnp-lab-ps-enable")) {
+
+		rc = of_property_read_u32(of_node,
+				 "qcom,qpnp-lab-ps-threshold", &tmp);
+
+		if (rc < 0) {
+			pr_err("get qcom,qpnp-lab-ps-threshold failed rc = %d\n",
+				rc);
+			return rc;
+		}
+		rc = labibb->lab_ver_ops->ps_ctl(labibb, tmp, true);
+		if (rc < 0)
+			return rc;
+	} else {
+		rc = labibb->lab_ver_ops->ps_ctl(labibb, tmp, false);
+		if (rc < 0)
+			return rc;
+	}
+
+	val = 0;
+	mask = 0;
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-pfet-size", &tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(lab_rdson_pfet_table); val++)
+			if (tmp == lab_rdson_pfet_table[val])
+				break;
+
+		if (val == ARRAY_SIZE(lab_rdson_pfet_table)) {
+			pr_err("Invalid value in qcom,qpnp-lab-pfet-size\n");
+			return -EINVAL;
+		}
+		val |= LAB_RDSON_MNGMNT_PFET_SLEW_EN;
+		mask |= LAB_RDSON_MNGMNT_PFET_MASK |
+				LAB_RDSON_MNGMNT_PFET_SLEW_EN;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-nfet-size",
+				 &tmp);
+	if (!rc) {
+		for (i = 0; i < ARRAY_SIZE(lab_rdson_nfet_table); i++)
+			if (tmp == lab_rdson_nfet_table[i])
+				break;
+
+		if (i == ARRAY_SIZE(lab_rdson_nfet_table)) {
+			pr_err("Invalid value in qcom,qpnp-lab-nfet-size\n");
+			return -EINVAL;
+		}
+
+		val |= i << LAB_RDSON_MNGMNT_NFET_SHIFT;
+		val |= LAB_RDSON_MNGMNT_NFET_SLEW_EN;
+		mask |= LAB_RDSON_MNGMNT_NFET_MASK |
+				LAB_RDSON_MNGMNT_NFET_SLEW_EN;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_RDSON_MNGMNT, mask, val);
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n",
+			REG_LAB_RDSON_MNGMNT, rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-init-voltage",
+				&(labibb->lab_vreg.curr_volt));
+	if (rc < 0) {
+		pr_err("get qcom,qpnp-lab-init-voltage failed, rc = %d\n",
+				 rc);
+		return rc;
+	}
+
+	if (of_property_read_bool(of_node,
+			"qcom,qpnp-lab-use-default-voltage"))
+		rc = labibb->lab_ver_ops->set_default_voltage(labibb, true);
+	else
+		rc = labibb->lab_ver_ops->set_default_voltage(labibb, false);
+
+	if (rc < 0)
+		return rc;
+
+	if (of_property_read_bool(of_node,
+		"qcom,qpnp-lab-enable-sw-high-psrr")) {
+		val = LAB_EN_SW_HIGH_PSRR_MODE;
+
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_SW_HIGH_PSRR_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_SW_HIGH_PSRR_CTL, rc);
+			return rc;
+		}
+	}
+
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-lab-ldo-pulldown-enable", (u32 *)&val);
+	if (!rc) {
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+			REG_LAB_LDO_PD_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_LDO_PD_CTL, rc);
+			return rc;
+		}
+	}
+
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-lab-high-psrr-src-select", &tmp);
+	if (!rc) {
+		val = tmp;
+
+		rc = of_property_read_u32(of_node,
+			"qcom,qpnp-lab-vref-high-psrr-select", &tmp);
+		if (rc < 0) {
+			pr_err("get qcom,qpnp-lab-vref-high-psrr-select failed rc = %d\n",
+				rc);
+			return rc;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(lab_vref_high_psrr_table); i++)
+			if (lab_vref_high_psrr_table[i] == tmp)
+				break;
+
+		if (i == ARRAY_SIZE(lab_vref_high_psrr_table)) {
+			pr_err("Invalid value in qpnp-lab-vref-high-psrr-selct\n");
+			return -EINVAL;
+		}
+		val |= (i << LAB_SEL_HW_HIGH_PSRR_SRC_SHIFT);
+
+		rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_VPH_ENVELOP_CTL,
+				LAB_VREF_HIGH_PSRR_SEL_MASK |
+				LAB_SEL_HW_HIGH_PSRR_SRC_MASK,
+				val);
+
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_VPH_ENVELOP_CTL, rc);
+			return rc;
+		}
+	}
+
+	if (labibb->swire_control) {
+		rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL);
+		if (rc < 0) {
+			pr_err("Unable to set SWIRE_RDY rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+#define LAB_CURRENT_MAX_1600MA	0x7
+#define LAB_CURRENT_MAX_400MA	0x1
+static int qpnp_lab_pfm_disable(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val, mask;
+
+	mutex_lock(&(labibb->lab_vreg.lab_mutex));
+	if (!labibb->pfm_enable) {
+		pr_debug("PFM already disabled\n");
+		goto out;
+	}
+
+	val = 0;
+	mask = LAB_PFM_EN_BIT;
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_PFM_CTL, mask, val);
+	if (rc < 0) {
+		pr_err("Write register %x failed rc = %d\n",
+			REG_LAB_PFM_CTL, rc);
+		goto out;
+	}
+
+	val = LAB_CURRENT_MAX_1600MA;
+	mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK;
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_CURRENT_LIMIT, mask, val);
+	if (rc < 0) {
+		pr_err("Write register %x failed rc = %d\n",
+			REG_LAB_CURRENT_LIMIT, rc);
+		goto out;
+	}
+
+	labibb->pfm_enable = false;
+out:
+	mutex_unlock(&(labibb->lab_vreg.lab_mutex));
+	return rc;
+}
+
+static int qpnp_lab_pfm_enable(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val, mask;
+
+	mutex_lock(&(labibb->lab_vreg.lab_mutex));
+	if (labibb->pfm_enable) {
+		pr_debug("PFM already enabled\n");
+		goto out;
+	}
+
+	/* Wait for ~100uS */
+	usleep_range(100, 105);
+
+	val = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_MAX_400MA;
+	mask = LAB_OVERRIDE_CURRENT_MAX_BIT | LAB_CURRENT_LIMIT_MASK;
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_CURRENT_LIMIT, mask, val);
+	if (rc < 0) {
+		pr_err("Write register %x failed rc = %d\n",
+			REG_LAB_CURRENT_LIMIT, rc);
+		goto out;
+	}
+
+	/* Wait for ~100uS */
+	usleep_range(100, 105);
+
+	val = LAB_PFM_EN_BIT;
+	mask = LAB_PFM_EN_BIT;
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_PFM_CTL, mask, val);
+	if (rc < 0) {
+		pr_err("Write register %x failed rc = %d\n",
+			REG_LAB_PFM_CTL, rc);
+		goto out;
+	}
+
+	labibb->pfm_enable = true;
+out:
+	mutex_unlock(&(labibb->lab_vreg.lab_mutex));
+	return rc;
+}
+
+static int qpnp_labibb_restore_settings(struct qpnp_labibb *labibb)
+{
+	int rc, i;
+
+	for (i = 0; i < ARRAY_SIZE(ibb_settings); i++) {
+		if (ibb_settings[i].sec_access)
+			rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
+					ibb_settings[i].address,
+					ibb_settings[i].value);
+		else
+			rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+					ibb_settings[i].address,
+					&ibb_settings[i].value, 1);
+
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				ibb_settings[i].address, rc);
+			return rc;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lab_settings); i++) {
+		if (lab_settings[i].sec_access)
+			rc = qpnp_labibb_sec_write(labibb, labibb->lab_base,
+					lab_settings[i].address,
+					lab_settings[i].value);
+		else
+			rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					lab_settings[i].address,
+					&lab_settings[i].value, 1);
+
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				lab_settings[i].address, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_labibb_save_settings(struct qpnp_labibb *labibb)
+{
+	int rc, i;
+
+	for (i = 0; i < ARRAY_SIZE(ibb_settings); i++) {
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+			 ibb_settings[i].address, &ibb_settings[i].value, 1);
+		if (rc < 0) {
+			pr_err("read register %x failed rc = %d\n",
+				ibb_settings[i].address, rc);
+			return rc;
+		}
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lab_settings); i++) {
+		rc = qpnp_labibb_read(labibb, labibb->lab_base +
+			lab_settings[i].address, &lab_settings[i].value, 1);
+		if (rc < 0) {
+			pr_err("read register %x failed rc = %d\n",
+				lab_settings[i].address, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_labibb_ttw_enter_ibb_common(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val;
+
+	val = 0;
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_PD_CTL,
+				&val, 1);
+	if (rc < 0) {
+		pr_err("read register %x failed rc = %d\n",
+			REG_IBB_PD_CTL, rc);
+		return rc;
+	}
+
+	val = 0;
+	rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
+				REG_IBB_PWRUP_PWRDN_CTL_1, val);
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n",
+			REG_IBB_PWRUP_PWRDN_CTL_1, rc);
+		return rc;
+	}
+
+	val = IBB_WAIT_MBG_OK;
+	rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base,
+				REG_IBB_PWRUP_PWRDN_CTL_2,
+				IBB_DIS_DLY_MASK | IBB_WAIT_MBG_OK, val);
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n",
+			REG_IBB_PWRUP_PWRDN_CTL_2, rc);
+		return rc;
+	}
+
+	val = IBB_NFET_SLEW_EN | IBB_PFET_SLEW_EN | IBB_OVERRIDE_NFET_SW_SIZE |
+		IBB_OVERRIDE_PFET_SW_SIZE;
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+				REG_IBB_RDSON_MNGMNT, 0xFF, val);
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n",
+			REG_IBB_RDSON_MNGMNT, rc);
+		return rc;
+	}
+
+	val = IBB_CURRENT_LIMIT_EN | IBB_CURRENT_MAX_500MA |
+		(IBB_ILIMIT_COUNT_CYC8 << IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT);
+	rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
+				REG_IBB_CURRENT_LIMIT, val);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n",
+			REG_IBB_CURRENT_LIMIT, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_ttw_enter_ibb_pmi8996(struct qpnp_labibb *labibb)
+{
+	int rc;
+	u8 val;
+
+	val = IBB_BYPASS_PWRDN_DLY2_BIT | IBB_FAST_STARTUP;
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL,
+				&val, 1);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n",
+			REG_IBB_SPARE_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_ttw_enter_ibb_pmi8950(struct qpnp_labibb *labibb)
+{
+	int rc;
+	u8 val;
+
+	rc = qpnp_ibb_ps_config(labibb, true);
+	if (rc < 0) {
+		pr_err("Failed to enable ibb_ps_config rc=%d\n", rc);
+		return rc;
+	}
+
+	val = IBB_SOFT_START_CHARGING_RESISTOR_16K;
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+				REG_IBB_SOFT_START_CTL, &val, 1);
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n",
+			REG_IBB_SOFT_START_CTL, rc);
+		return rc;
+	}
+
+	val = IBB_MODULE_RDY_EN;
+	rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_IBB_MODULE_RDY, &val, 1);
+	if (rc < 0)
+		pr_err("write to register %x failed rc = %d\n",
+				REG_IBB_MODULE_RDY, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_regulator_ttw_mode_enter(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val;
+
+	/* Save the IBB settings before they get modified for TTW mode */
+	if (!labibb->ibb_settings_saved) {
+		rc = qpnp_labibb_save_settings(labibb);
+		if (rc) {
+			pr_err("Error in storing IBB setttings, rc=%d\n", rc);
+			return rc;
+		}
+		labibb->ibb_settings_saved = true;
+	}
+
+	if (labibb->ttw_force_lab_on) {
+		val = LAB_MODULE_RDY_EN;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_MODULE_RDY, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_MODULE_RDY, rc);
+			return rc;
+		}
+
+		/* Prevents LAB being turned off by IBB */
+		val = LAB_ENABLE_CTL_EN;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_ENABLE_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_ENABLE_CTL, rc);
+			return rc;
+		}
+
+		val = LAB_RDSON_MNGMNT_NFET_SLEW_EN |
+			LAB_RDSON_MNGMNT_PFET_SLEW_EN |
+			LAB_RDSON_NFET_SW_SIZE_QUARTER |
+			LAB_RDSON_PFET_SW_SIZE_QUARTER;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_RDSON_MNGMNT, &val, 1);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				REG_LAB_RDSON_MNGMNT, rc);
+			return rc;
+		}
+
+		rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_PS_CTL, LAB_PS_CTL_EN, LAB_PS_CTL_EN);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_PS_CTL, rc);
+			return rc;
+		}
+	} else {
+		val = LAB_PD_CTL_DISABLE_PD;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_PD_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_PD_CTL, rc);
+			return rc;
+		}
+
+		val = LAB_SPARE_DISABLE_SCP_BIT;
+		if (labibb->pmic_rev_id->pmic_subtype != PMI8950_SUBTYPE)
+			val |= LAB_SPARE_TOUCH_WAKE_BIT;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_SPARE_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_SPARE_CTL, rc);
+			return rc;
+		}
+
+		val = 0;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_SOFT_START_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_SOFT_START_CTL, rc);
+			return rc;
+		}
+	}
+
+	rc = qpnp_labibb_ttw_enter_ibb_common(labibb);
+	if (rc) {
+		pr_err("Failed to apply TTW ibb common settings rc=%d\n", rc);
+		return rc;
+	}
+
+	switch (labibb->pmic_rev_id->pmic_subtype) {
+	case PMI8996_SUBTYPE:
+		rc = qpnp_labibb_ttw_enter_ibb_pmi8996(labibb);
+		break;
+	case PMI8950_SUBTYPE:
+		rc = qpnp_labibb_ttw_enter_ibb_pmi8950(labibb);
+		break;
+	}
+	if (rc < 0) {
+		pr_err("Failed to configure TTW-enter for IBB rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL);
+	if (rc < 0) {
+		pr_err("Unable to set SWIRE_RDY rc = %d\n", rc);
+		return rc;
+	}
+	labibb->in_ttw_mode = true;
+	return 0;
+}
+
+static int qpnp_labibb_ttw_exit_ibb_common(struct qpnp_labibb *labibb)
+{
+	int rc;
+	u8 val;
+
+	val = IBB_FASTER_PFET_OFF;
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base + REG_IBB_SPARE_CTL,
+			&val, 1);
+	if (rc < 0)
+		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+			REG_IBB_SPARE_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_regulator_ttw_mode_exit(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val;
+
+	if (!labibb->ibb_settings_saved) {
+		pr_err("IBB settings are not saved!\n");
+		return -EINVAL;
+	}
+
+	/* Restore the IBB settings back to switch back to normal mode */
+	rc = qpnp_labibb_restore_settings(labibb);
+	if (rc < 0) {
+		pr_err("Error in restoring IBB setttings, rc=%d\n", rc);
+		return rc;
+	}
+
+	if (labibb->ttw_force_lab_on) {
+		val = 0;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_ENABLE_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_ENABLE_CTL, rc);
+			return rc;
+		}
+	} else {
+		val = LAB_PD_CTL_STRONG_PULL;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_PD_CTL,	&val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+						REG_LAB_PD_CTL, rc);
+			return rc;
+		}
+
+		val = 0;
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+					REG_LAB_SPARE_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+					REG_LAB_SPARE_CTL, rc);
+			return rc;
+		}
+	}
+
+	switch (labibb->pmic_rev_id->pmic_subtype) {
+	case PMI8996_SUBTYPE:
+	case PMI8994_SUBTYPE:
+	case PMI8950_SUBTYPE:
+		rc = qpnp_labibb_ttw_exit_ibb_common(labibb);
+		break;
+	}
+	if (rc < 0) {
+		pr_err("Failed to configure TTW-exit for IBB rc=%d\n", rc);
+		return rc;
+	}
+
+	labibb->in_ttw_mode = false;
+	return rc;
+}
+
+static int qpnp_labibb_regulator_enable(struct qpnp_labibb *labibb)
+{
+	int rc;
+	u8 val;
+	int dly;
+	int retries;
+	bool enabled = false;
+
+	if (labibb->ttw_en && !labibb->ibb_vreg.vreg_enabled &&
+		labibb->in_ttw_mode) {
+		rc = qpnp_labibb_regulator_ttw_mode_exit(labibb);
+		if (rc) {
+			pr_err("Error in exiting TTW mode rc = %d\n", rc);
+			return rc;
+		}
+	}
+
+	rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+	if (rc) {
+		pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+		return rc;
+	}
+
+	/* total delay time */
+	dly = labibb->lab_vreg.soft_start + labibb->ibb_vreg.soft_start
+				+ labibb->ibb_vreg.pwrup_dly;
+	usleep_range(dly, dly + 100);
+
+	/* after this delay, lab should be enabled */
+	rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_STATUS1,
+			&val, 1);
+	if (rc < 0) {
+		pr_err("read register %x failed rc = %d\n",
+			REG_LAB_STATUS1, rc);
+		goto err_out;
+	}
+
+	pr_debug("soft=%d %d up=%d dly=%d\n",
+		labibb->lab_vreg.soft_start, labibb->ibb_vreg.soft_start,
+				labibb->ibb_vreg.pwrup_dly, dly);
+
+	if (!(val & LAB_STATUS1_VREG_OK)) {
+		pr_err("failed for LAB %x\n", val);
+		goto err_out;
+	}
+
+	/* poll IBB_STATUS to make sure ibb had been enabled */
+	dly = labibb->ibb_vreg.soft_start + labibb->ibb_vreg.pwrup_dly;
+	retries = 10;
+	while (retries--) {
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+					REG_IBB_STATUS1, &val, 1);
+		if (rc < 0) {
+			pr_err("read register %x failed rc = %d\n",
+				REG_IBB_STATUS1, rc);
+			goto err_out;
+		}
+
+		if (val & IBB_STATUS1_VREG_OK) {
+			enabled = true;
+			break;
+		}
+		usleep_range(dly, dly + 100);
+	}
+
+	if (!enabled) {
+		pr_err("failed for IBB %x\n", val);
+		goto err_out;
+	}
+
+	labibb->lab_vreg.vreg_enabled = 1;
+	labibb->ibb_vreg.vreg_enabled = 1;
+
+	return 0;
+err_out:
+	rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_DIS);
+	if (rc < 0) {
+		pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+		return rc;
+	}
+	return -EINVAL;
+}
+
+static int qpnp_labibb_regulator_disable(struct qpnp_labibb *labibb)
+{
+	int rc;
+	u8 val;
+	int dly;
+	int retries;
+	bool disabled = false;
+
+	/*
+	 * When TTW mode is enabled and LABIBB regulators are disabled, it is
+	 * recommended not to disable IBB through IBB_ENABLE_CTL when switching
+	 * to SWIRE control on entering TTW mode. Hence, just enter TTW mode
+	 * and mark the regulators disabled. When we exit TTW mode, normal
+	 * mode settings will be restored anyways and regulators will be
+	 * enabled as before.
+	 */
+	if (labibb->ttw_en && !labibb->in_ttw_mode) {
+		rc = qpnp_labibb_regulator_ttw_mode_enter(labibb);
+		if (rc < 0) {
+			pr_err("Error in entering TTW mode rc = %d\n", rc);
+			return rc;
+		}
+		labibb->lab_vreg.vreg_enabled = 0;
+		labibb->ibb_vreg.vreg_enabled = 0;
+		return 0;
+	}
+
+	rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_DIS);
+	if (rc < 0) {
+		pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+		return rc;
+	}
+
+	/* poll IBB_STATUS to make sure ibb had been disabled */
+	dly = labibb->ibb_vreg.pwrdn_dly;
+	retries = 2;
+	while (retries--) {
+		usleep_range(dly, dly + 100);
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+				REG_IBB_STATUS1, &val, 1);
+		if (rc < 0) {
+			pr_err("read register %x failed rc = %d\n",
+				REG_IBB_STATUS1, rc);
+			return rc;
+		}
+
+		if (!(val & IBB_STATUS1_VREG_OK)) {
+			disabled = true;
+			break;
+		}
+	}
+
+	if (!disabled) {
+		pr_err("failed for IBB %x\n", val);
+		return -EINVAL;
+	}
+
+	if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
+		labibb->mode == QPNP_LABIBB_LCD_MODE) {
+		rc = qpnp_lab_pfm_disable(labibb);
+		if (rc < 0) {
+			pr_err("Error in disabling PFM, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	labibb->lab_vreg.vreg_enabled = 0;
+	labibb->ibb_vreg.vreg_enabled = 0;
+
+	return 0;
+}
+
+static int qpnp_lab_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc;
+	u8 val;
+
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->skip_2nd_swire_cmd) {
+		rc = qpnp_ibb_ps_config(labibb, false);
+		if (rc < 0) {
+			pr_err("Failed to disable IBB PS rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if (!labibb->lab_vreg.vreg_enabled && !labibb->swire_control) {
+
+		if (!labibb->standalone)
+			return qpnp_labibb_regulator_enable(labibb);
+
+		val = LAB_ENABLE_CTL_EN;
+		rc = qpnp_labibb_write(labibb,
+			labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n",
+				REG_LAB_ENABLE_CTL, rc);
+			return rc;
+		}
+
+		udelay(labibb->lab_vreg.soft_start);
+
+		rc = qpnp_labibb_read(labibb, labibb->lab_base +
+					REG_LAB_STATUS1, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_lab_regulator_enable read register %x failed rc = %d\n",
+				REG_LAB_STATUS1, rc);
+			return rc;
+		}
+
+		if ((val & LAB_STATUS1_VREG_OK_MASK) != LAB_STATUS1_VREG_OK) {
+			pr_err("qpnp_lab_regulator_enable failed\n");
+			return -EINVAL;
+		}
+
+		labibb->lab_vreg.vreg_enabled = 1;
+	}
+
+	return 0;
+}
+
+static int qpnp_lab_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc;
+	u8 val;
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->lab_vreg.vreg_enabled && !labibb->swire_control) {
+
+		if (!labibb->standalone)
+			return qpnp_labibb_regulator_disable(labibb);
+
+		val = 0;
+		rc = qpnp_labibb_write(labibb,
+			labibb->lab_base + REG_LAB_ENABLE_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_lab_regulator_enable write register %x failed rc = %d\n",
+				REG_LAB_ENABLE_CTL, rc);
+			return rc;
+		}
+
+		labibb->lab_vreg.vreg_enabled = 0;
+	}
+	return 0;
+}
+
+static int qpnp_lab_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->swire_control)
+		return 0;
+
+	return labibb->lab_vreg.vreg_enabled;
+}
+
+static int qpnp_lab_regulator_set_voltage(struct regulator_dev *rdev,
+				int min_uV, int max_uV, unsigned int *selector)
+{
+	int rc, new_uV;
+	u8 val;
+	struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
+
+	if (labibb->swire_control)
+		return 0;
+
+	if (min_uV < labibb->lab_vreg.min_volt) {
+		pr_err("min_uV %d is less than min_volt %d", min_uV,
+			labibb->lab_vreg.min_volt);
+		return -EINVAL;
+	}
+
+	val = DIV_ROUND_UP(min_uV - labibb->lab_vreg.min_volt,
+				labibb->lab_vreg.step_size);
+	new_uV = val * labibb->lab_vreg.step_size + labibb->lab_vreg.min_volt;
+
+	if (new_uV > max_uV) {
+		pr_err("unable to set voltage %d (min:%d max:%d)\n", new_uV,
+			min_uV, max_uV);
+		return -EINVAL;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_VOLTAGE,
+				LAB_VOLTAGE_SET_MASK |
+				LAB_VOLTAGE_OVERRIDE_EN,
+				val | LAB_VOLTAGE_OVERRIDE_EN);
+
+	if (rc < 0) {
+		pr_err("write to register %x failed rc = %d\n", REG_LAB_VOLTAGE,
+			rc);
+		return rc;
+	}
+
+	if (new_uV > labibb->lab_vreg.curr_volt) {
+		val = DIV_ROUND_UP(new_uV - labibb->lab_vreg.curr_volt,
+				labibb->lab_vreg.step_size);
+		udelay(val * labibb->lab_vreg.slew_rate);
+	}
+	labibb->lab_vreg.curr_volt = new_uV;
+
+	return 0;
+}
+
+static int qpnp_skip_swire_command(struct qpnp_labibb *labibb)
+{
+	int rc = 0, retry = 50, dly;
+	u8 reg;
+
+	do {
+		/* poll for ibb vreg_ok */
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+					REG_IBB_STATUS1, &reg, 1);
+		if (rc < 0) {
+			pr_err("Failed to read ibb_status1 reg rc=%d\n", rc);
+			return rc;
+		}
+		if ((reg & IBB_STATUS1_VREG_OK_MASK) == IBB_STATUS1_VREG_OK)
+			break;
+
+		/* poll delay */
+		usleep_range(500, 600);
+
+	} while (--retry);
+
+	if (!retry) {
+		pr_err("ibb vreg_ok failed to turn-on\n");
+		return -EBUSY;
+	}
+
+	/* move to SW control */
+	rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+	if (rc < 0) {
+		pr_err("Failed switch to IBB_SW_CONTROL rc=%d\n", rc);
+		return rc;
+	}
+
+	/* delay to skip the second swire command */
+	dly = labibb->swire_2nd_cmd_delay * 1000;
+	while (dly / 20000) {
+		usleep_range(20000, 20010);
+		dly -= 20000;
+	}
+	if (dly)
+		usleep_range(dly, dly + 10);
+
+	rc = qpnp_ibb_set_mode(labibb, IBB_HW_SW_CONTROL);
+	if (rc < 0) {
+		pr_err("Failed switch to IBB_HW_SW_CONTROL rc=%d\n", rc);
+		return rc;
+	}
+
+	/* delay for SPMI to SWIRE transition */
+	usleep_range(1000, 1100);
+
+	/* Move back to SWIRE control */
+	rc = qpnp_ibb_set_mode(labibb, IBB_HW_CONTROL);
+	if (rc < 0)
+		pr_err("Failed switch to IBB_HW_CONTROL rc=%d\n", rc);
+
+	/* delay before enabling the PS mode */
+	msleep(labibb->swire_ibb_ps_enable_delay);
+	rc = qpnp_ibb_ps_config(labibb, true);
+	if (rc < 0)
+		pr_err("Unable to enable IBB PS rc=%d\n", rc);
+
+	return rc;
+}
+
+static irqreturn_t lab_vreg_ok_handler(int irq, void *_labibb)
+{
+	struct qpnp_labibb *labibb = _labibb;
+	int rc;
+
+	if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2) {
+		rc = qpnp_skip_swire_command(labibb);
+		if (rc < 0)
+			pr_err("Failed in 'qpnp_skip_swire_command' rc=%d\n",
+				rc);
+	} else if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
+		labibb->mode == QPNP_LABIBB_LCD_MODE) {
+		rc = qpnp_lab_pfm_enable(labibb);
+		if (rc < 0)
+			pr_err("Failed to config PFM, rc=%d\n", rc);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int qpnp_lab_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->swire_control)
+		return 0;
+
+	return labibb->lab_vreg.curr_volt;
+}
+
+static bool is_lab_vreg_ok_irq_available(struct qpnp_labibb *labibb)
+{
+	/*
+	 * LAB VREG_OK interrupt is used only to skip 2nd SWIRE command in
+	 * dig_major < 2 targets. For pmi8998, it is used to enable PFM in
+	 * LCD mode.
+	 */
+	if (labibb->skip_2nd_swire_cmd && labibb->lab_dig_major < 2)
+		return true;
+
+	if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE &&
+		labibb->mode == QPNP_LABIBB_LCD_MODE)
+		return true;
+
+	return false;
+}
+
+static struct regulator_ops qpnp_lab_ops = {
+	.enable			= qpnp_lab_regulator_enable,
+	.disable		= qpnp_lab_regulator_disable,
+	.is_enabled		= qpnp_lab_regulator_is_enabled,
+	.set_voltage		= qpnp_lab_regulator_set_voltage,
+	.get_voltage		= qpnp_lab_regulator_get_voltage,
+};
+
+static int register_qpnp_lab_regulator(struct qpnp_labibb *labibb,
+					struct device_node *of_node)
+{
+	int rc = 0;
+	struct regulator_init_data *init_data;
+	struct regulator_desc *rdesc = &labibb->lab_vreg.rdesc;
+	struct regulator_config cfg = {};
+	u8 val, mask;
+	const char *current_sense_str;
+	bool config_current_sense = false;
+	u32 tmp;
+
+	if (!of_node) {
+		dev_err(labibb->dev, "qpnp lab regulator device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	init_data = of_get_regulator_init_data(labibb->dev, of_node, rdesc);
+	if (!init_data) {
+		pr_err("unable to get regulator init data for qpnp lab regulator\n");
+		return -ENOMEM;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-min-voltage",
+					&(labibb->lab_vreg.min_volt));
+	if (rc < 0) {
+		pr_err("qcom,qpnp-lab-min-voltage is missing, rc = %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-step-size",
+					&(labibb->lab_vreg.step_size));
+	if (rc < 0) {
+		pr_err("qcom,qpnp-lab-step-size is missing, rc = %d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-slew-rate",
+					&(labibb->lab_vreg.slew_rate));
+	if (rc < 0) {
+		pr_err("qcom,qpnp-lab-slew-rate is missing, rc = %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-lab-soft-start",
+					&(labibb->lab_vreg.soft_start));
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(lab_soft_start_table); val++)
+			if (lab_soft_start_table[val] ==
+					labibb->lab_vreg.soft_start)
+				break;
+
+		if (val == ARRAY_SIZE(lab_soft_start_table))
+			val = ARRAY_SIZE(lab_soft_start_table) - 1;
+
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+				REG_LAB_SOFT_START_CTL, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_SOFT_START_CTL, rc);
+			return rc;
+		}
+
+		labibb->lab_vreg.soft_start = lab_soft_start_table
+				[val & LAB_SOFT_START_CTL_MASK];
+	}
+
+	val = 0;
+	mask = 0;
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-lab-max-precharge-time", &tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(lab_max_precharge_table); val++)
+			if (lab_max_precharge_table[val] == tmp)
+				break;
+
+		if (val == ARRAY_SIZE(lab_max_precharge_table)) {
+			pr_err("Invalid value in qcom,qpnp-lab-max-precharge-time\n");
+			return -EINVAL;
+		}
+
+		mask = LAB_MAX_PRECHARGE_TIME_MASK;
+	}
+
+	if (of_property_read_bool(of_node,
+			"qcom,qpnp-lab-max-precharge-enable")) {
+		val |= LAB_FAST_PRECHARGE_CTL_EN;
+		mask |= LAB_FAST_PRECHARGE_CTL_EN;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_PRECHARGE_CTL, mask, val);
+	if (rc < 0) {
+		pr_err("qpnp_lab_dt_init write register %x failed rc = %d\n",
+			REG_LAB_PRECHARGE_CTL, rc);
+		return rc;
+	}
+
+	if (labibb->mode == QPNP_LABIBB_AMOLED_MODE &&
+		labibb->pmic_rev_id->pmic_subtype != PM660L_SUBTYPE) {
+		/*
+		 * default to 1.5 times current gain if
+		 * user doesn't specify the current-sense
+		 * dt parameter
+		 */
+		current_sense_str = "1.5x";
+		val = qpnp_labibb_get_matching_idx(current_sense_str);
+		config_current_sense = true;
+	}
+
+	if (of_find_property(of_node,
+		"qcom,qpnp-lab-current-sense", NULL)) {
+		config_current_sense = true;
+		rc = of_property_read_string(of_node,
+			"qcom,qpnp-lab-current-sense",
+			&current_sense_str);
+		if (!rc) {
+			val = qpnp_labibb_get_matching_idx(
+					current_sense_str);
+		} else {
+			pr_err("qcom,qpnp-lab-current-sense configured incorrectly rc = %d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	if (config_current_sense) {
+		rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+			REG_LAB_CURRENT_SENSE,
+			LAB_CURRENT_SENSE_GAIN_MASK,
+			val);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+				REG_LAB_CURRENT_SENSE, rc);
+			return rc;
+		}
+	}
+
+	val = (labibb->standalone) ? 0 : LAB_IBB_EN_RDY_EN;
+	rc = qpnp_labibb_sec_write(labibb, labibb->lab_base,
+				REG_LAB_IBB_EN_RDY, val);
+
+	if (rc < 0) {
+		pr_err("qpnp_lab_sec_write register %x failed rc = %d\n",
+			REG_LAB_IBB_EN_RDY, rc);
+		return rc;
+	}
+
+	rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_ENABLE_CTL,
+				&val, 1);
+	if (rc < 0) {
+		pr_err("qpnp_labibb_read register %x failed rc = %d\n",
+			REG_IBB_ENABLE_CTL, rc);
+		return rc;
+	}
+
+	if (!(val & (IBB_ENABLE_CTL_SWIRE_RDY | IBB_ENABLE_CTL_MODULE_EN))) {
+		/* SWIRE_RDY and IBB_MODULE_EN not enabled */
+		rc = qpnp_lab_dt_init(labibb, of_node);
+		if (rc < 0) {
+			pr_err("qpnp-lab: wrong DT parameter specified: rc = %d\n",
+				rc);
+			return rc;
+		}
+	} else {
+		rc = labibb->ibb_ver_ops->get_mode(labibb);
+
+		rc = qpnp_labibb_read(labibb, labibb->lab_base +
+					REG_LAB_VOLTAGE, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_lab_read read register %x failed rc = %d\n",
+				REG_LAB_VOLTAGE, rc);
+			return rc;
+		}
+
+		labibb->lab_vreg.curr_volt =
+					(val &
+					LAB_VOLTAGE_SET_MASK) *
+					labibb->lab_vreg.step_size +
+					labibb->lab_vreg.min_volt;
+		if (labibb->mode == QPNP_LABIBB_LCD_MODE) {
+			rc = of_property_read_u32(of_node,
+				"qcom,qpnp-lab-init-lcd-voltage",
+				&(labibb->lab_vreg.curr_volt));
+			if (rc < 0) {
+				pr_err("get qcom,qpnp-lab-init-lcd-voltage failed, rc = %d\n",
+					rc);
+				return rc;
+			}
+		} else if (!(val & LAB_VOLTAGE_OVERRIDE_EN)) {
+			rc = of_property_read_u32(of_node,
+				"qcom,qpnp-lab-init-amoled-voltage",
+				&(labibb->lab_vreg.curr_volt));
+			if (rc < 0) {
+				pr_err("get qcom,qpnp-lab-init-amoled-voltage failed, rc = %d\n",
+					rc);
+				return rc;
+			}
+		}
+
+		labibb->lab_vreg.vreg_enabled = 1;
+	}
+
+	if (is_lab_vreg_ok_irq_available(labibb)) {
+		rc = devm_request_threaded_irq(labibb->dev,
+				labibb->lab_vreg.lab_vreg_ok_irq, NULL,
+				lab_vreg_ok_handler,
+				IRQF_ONESHOT | IRQF_TRIGGER_RISING,
+				"lab-vreg-ok", labibb);
+		if (rc) {
+			pr_err("Failed to register 'lab-vreg-ok' irq rc=%d\n",
+						rc);
+			return rc;
+		}
+	}
+
+	rc = qpnp_labibb_read(labibb, labibb->lab_base + REG_LAB_MODULE_RDY,
+				&val, 1);
+	if (rc < 0) {
+		pr_err("qpnp_lab_read read register %x failed rc = %d\n",
+			REG_LAB_MODULE_RDY, rc);
+		return rc;
+	}
+
+	if (!(val & LAB_MODULE_RDY_EN)) {
+		val = LAB_MODULE_RDY_EN;
+
+		rc = qpnp_labibb_write(labibb, labibb->lab_base +
+			REG_LAB_MODULE_RDY, &val, 1);
+
+		if (rc < 0) {
+			pr_err("qpnp_lab_dt_init write register %x failed rc = %d\n",
+				REG_LAB_MODULE_RDY, rc);
+			return rc;
+		}
+	}
+
+	if (init_data->constraints.name) {
+		rdesc->owner		= THIS_MODULE;
+		rdesc->type		= REGULATOR_VOLTAGE;
+		rdesc->ops		= &qpnp_lab_ops;
+		rdesc->name		= init_data->constraints.name;
+
+		cfg.dev = labibb->dev;
+		cfg.init_data = init_data;
+		cfg.driver_data = labibb;
+		cfg.of_node = of_node;
+
+		if (of_get_property(labibb->dev->of_node, "parent-supply",
+				NULL))
+			init_data->supply_regulator = "parent";
+
+		init_data->constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_VOLTAGE |
+					REGULATOR_CHANGE_STATUS;
+
+		labibb->lab_vreg.rdev = regulator_register(rdesc, &cfg);
+		if (IS_ERR(labibb->lab_vreg.rdev)) {
+			rc = PTR_ERR(labibb->lab_vreg.rdev);
+			labibb->lab_vreg.rdev = NULL;
+			pr_err("unable to get regulator init data for qpnp lab regulator, rc = %d\n",
+				rc);
+
+			return rc;
+		}
+	} else {
+		dev_err(labibb->dev, "qpnp lab regulator name missing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_ibb_pfm_mode_enable(struct qpnp_labibb *labibb,
+			struct device_node *of_node)
+{
+	int rc = 0;
+	u32 i, tmp = 0;
+	u8 val = IBB_PFM_ENABLE;
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-pfm-peak-curr",
+				&tmp);
+	if (rc < 0) {
+		pr_err("qcom,qpnp-ibb-pfm-peak-curr is missing, rc = %d\n",
+			rc);
+		return rc;
+	}
+	for (i = 0; i < ARRAY_SIZE(ibb_pfm_peak_curr_table); i++)
+		if (ibb_pfm_peak_curr_table[i] == tmp)
+			break;
+
+	if (i == ARRAY_SIZE(ibb_pfm_peak_curr_table)) {
+		pr_err("Invalid value in qcom,qpnp-ibb-pfm-peak-curr\n");
+		return -EINVAL;
+	}
+
+	val |= (i << IBB_PFM_PEAK_CURRENT_BIT_SHIFT);
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-pfm-hysteresis",
+				&tmp);
+	if (rc < 0) {
+		pr_err("qcom,qpnp-ibb-pfm-hysteresis is missing, rc = %d\n",
+			rc);
+		return rc;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ibb_pfm_hysteresis_table); i++)
+		if (ibb_pfm_hysteresis_table[i] == tmp)
+			break;
+
+	if (i == ARRAY_SIZE(ibb_pfm_hysteresis_table)) {
+		pr_err("Invalid value in qcom,qpnp-ibb-pfm-hysteresis\n");
+		return -EINVAL;
+	}
+
+	val |= (i << IBB_PFM_HYSTERESIS_BIT_SHIFT);
+
+	rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+				REG_IBB_PFM_CTL, &val, 1);
+	if (rc < 0)
+		pr_err("qpnp_ibb_pfm_ctl write register %x failed rc = %d\n",
+					REG_IBB_PFM_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_labibb_pbs_mode_enable(struct qpnp_labibb *labibb,
+			struct device_node *of_node)
+{
+	int rc = 0;
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+				REG_IBB_SWIRE_CTL,
+				IBB_SWIRE_VOUT_UPD_EN, 0);
+	if (rc < 0) {
+		pr_err("qpnp_ibb_swire_ctl write register %x failed rc = %d\n",
+					REG_IBB_SWIRE_CTL, rc);
+		return rc;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+				REG_IBB_PD_CTL, IBB_SWIRE_PD_UPD, 0);
+	if (rc < 0) {
+		pr_err("qpnp_ibb_pd_ctl write register %x failed rc = %d\n",
+					REG_IBB_PD_CTL, rc);
+		return rc;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->lab_base +
+				REG_LAB_SWIRE_PGM_CTL, LAB_EN_SWIRE_PGM_VOUT |
+				LAB_EN_SWIRE_PGM_PD, 0);
+	if (rc < 0)
+		pr_err("qpnp_lab_swire_pgm_ctl write register %x failed rc = %d\n",
+					REG_LAB_SWIRE_PGM_CTL, rc);
+
+	return rc;
+}
+
+static int qpnp_ibb_slew_rate_config(struct qpnp_labibb *labibb,
+			struct device_node *of_node)
+{
+	int rc = 0;
+	u32 i, tmp = 0;
+	u8 val = 0, mask = 0;
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-fast-slew-rate",
+						&tmp);
+	if (!rc) {
+		for (i = 0; i < ARRAY_SIZE(ibb_output_slew_ctl_table); i++)
+			if (ibb_output_slew_ctl_table[i] == tmp)
+				break;
+
+		if (i == ARRAY_SIZE(ibb_output_slew_ctl_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-fast-slew-rate\n");
+			return -EINVAL;
+		}
+
+		labibb->ibb_vreg.slew_rate = tmp;
+		val |= (i << IBB_SLEW_RATE_TRANS_TIME_FAST_SHIFT) |
+				IBB_SLEW_RATE_SPEED_FAST_EN | IBB_SLEW_CTL_EN;
+
+		mask = IBB_SLEW_RATE_SPEED_FAST_EN |
+			IBB_SLEW_RATE_TRANS_TIME_FAST_MASK | IBB_SLEW_CTL_EN;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-slow-slew-rate",
+				&tmp);
+	if (!rc) {
+		for (i = 0; i < ARRAY_SIZE(ibb_output_slew_ctl_table); i++)
+			if (ibb_output_slew_ctl_table[i] == tmp)
+				break;
+
+		if (i == ARRAY_SIZE(ibb_output_slew_ctl_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-slow-slew-rate\n");
+			return -EINVAL;
+		}
+
+		labibb->ibb_vreg.slew_rate = tmp;
+		val |= (i | IBB_SLEW_CTL_EN);
+
+		mask |= IBB_SLEW_RATE_SPEED_FAST_EN |
+			IBB_SLEW_RATE_TRANS_TIME_SLOW_MASK | IBB_SLEW_CTL_EN;
+	}
+
+	rc = qpnp_labibb_masked_write(labibb, labibb->ibb_base +
+				REG_IBB_OUTPUT_SLEW_CTL,
+				mask, val);
+	if (rc < 0)
+		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+			REG_IBB_OUTPUT_SLEW_CTL, rc);
+
+	return rc;
+}
+
+static bool qpnp_ibb_poff_ctl_required(struct qpnp_labibb *labibb)
+{
+	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE)
+		return false;
+
+	return true;
+}
+
+static int qpnp_ibb_dt_init(struct qpnp_labibb *labibb,
+				struct device_node *of_node)
+{
+	int rc = 0;
+	u32 i, tmp = 0;
+	u8 val, mask;
+
+	/*
+	 * Do not configure LCD_AMOLED_SEL for pmi8998 as it will be done by
+	 * GPIO selector. Override the labibb->mode with what was configured
+	 * by the bootloader.
+	 */
+	if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE) {
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+				REG_IBB_LCD_AMOLED_SEL, &val, 1);
+		if (rc) {
+			pr_err("qpnp_labibb_read register %x failed rc = %d\n",
+						REG_IBB_LCD_AMOLED_SEL, rc);
+			return rc;
+		}
+		if (val == REG_LAB_IBB_AMOLED_MODE)
+			labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+		else
+			labibb->mode = QPNP_LABIBB_LCD_MODE;
+	} else {
+		rc = labibb->ibb_ver_ops->sel_mode(labibb, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
+				REG_IBB_LCD_AMOLED_SEL, rc);
+			return rc;
+		}
+	}
+
+	val = 0;
+	mask = 0;
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-ibb-lab-pwrdn-delay", &tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(ibb_pwrdn_dly_table); val++)
+			if (ibb_pwrdn_dly_table[val] == tmp)
+				break;
+
+		if (val == ARRAY_SIZE(ibb_pwrdn_dly_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrdn-delay\n");
+			return -EINVAL;
+		}
+
+		labibb->ibb_vreg.pwrdn_dly = tmp;
+		val |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
+		mask |= IBB_PWRUP_PWRDN_CTL_1_EN_DLY2;
+	}
+
+	rc = of_property_read_u32(of_node,
+			"qcom,qpnp-ibb-lab-pwrup-delay", &tmp);
+	if (!rc) {
+		for (i = 0; i < ARRAY_SIZE(ibb_pwrup_dly_table); i++)
+			if (ibb_pwrup_dly_table[i] == tmp)
+				break;
+
+		if (i == ARRAY_SIZE(ibb_pwrup_dly_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-lab-pwrup-delay\n");
+			return -EINVAL;
+		}
+
+		labibb->ibb_vreg.pwrup_dly = tmp;
+
+		val |= (i << IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT);
+		val |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 |
+			IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK);
+		mask |= (IBB_PWRUP_PWRDN_CTL_1_EN_DLY1 |
+			IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK |
+			IBB_PWRUP_PWRDN_CTL_1_LAB_VREG_OK);
+	}
+
+	if (of_property_read_bool(of_node,
+				"qcom,qpnp-ibb-en-discharge")) {
+		val |= PWRUP_PWRDN_CTL_1_DISCHARGE_EN;
+		mask |= PWRUP_PWRDN_CTL_1_DISCHARGE_EN;
+	}
+
+	rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base,
+				REG_IBB_PWRUP_PWRDN_CTL_1, mask, val);
+	if (rc < 0) {
+		pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
+			REG_IBB_PWRUP_PWRDN_CTL_1, rc);
+		return rc;
+	}
+
+	if (of_property_read_bool(of_node, "qcom,qpnp-ibb-slew-rate-config")) {
+
+		rc = qpnp_ibb_slew_rate_config(labibb, of_node);
+		if (rc < 0)
+			return rc;
+	}
+
+	val = 0;
+	if (!of_property_read_bool(of_node, "qcom,qpnp-ibb-full-pull-down"))
+		val = IBB_PD_CTL_HALF_STRENGTH;
+
+	if (of_property_read_bool(of_node, "qcom,qpnp-ibb-pull-down-enable"))
+		val |= IBB_PD_CTL_EN;
+
+	mask = IBB_PD_CTL_STRENGTH_MASK | IBB_PD_CTL_EN;
+	rc = qpnp_labibb_masked_write(labibb,
+			labibb->ibb_base + REG_IBB_PD_CTL, mask, val);
+
+	if (rc < 0) {
+		pr_err("qpnp_lab_dt_init write register %x failed rc = %d\n",
+				REG_IBB_PD_CTL, rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-ibb-switching-clock-frequency", &tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(ibb_clk_div_table); val++)
+			if (ibb_clk_div_table[val] == tmp)
+				break;
+
+		if (val == ARRAY_SIZE(ibb_clk_div_table)) {
+			pr_err("Invalid value in qpnp-ibb-switching-clock-frequency\n");
+			return -EINVAL;
+		}
+		rc = labibb->ibb_ver_ops->set_clk_div(labibb, val);
+		if (rc < 0) {
+			pr_err("qpnp_ibb_dt_init write register %x failed rc = %d\n",
+				REG_IBB_CLK_DIV, rc);
+			return rc;
+		}
+	}
+
+	val = 0;
+	mask = 0;
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-ibb-limit-maximum-current", &tmp);
+	if (!rc) {
+		for (val = 0; val < ARRAY_SIZE(ibb_current_limit_table); val++)
+			if (ibb_current_limit_table[val] == tmp)
+				break;
+
+		if (val == ARRAY_SIZE(ibb_current_limit_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-limit-maximum-current\n");
+			return -EINVAL;
+		}
+
+		mask = IBB_CURRENT_LIMIT_MASK;
+	}
+
+	rc = of_property_read_u32(of_node,
+		"qcom,qpnp-ibb-debounce-cycle", &tmp);
+	if (!rc) {
+		for (i = 0; i < ARRAY_SIZE(ibb_debounce_table); i++)
+			if (ibb_debounce_table[i] == tmp)
+				break;
+
+		if (i == ARRAY_SIZE(ibb_debounce_table)) {
+			pr_err("Invalid value in qcom,qpnp-ibb-debounce-cycle\n");
+			return -EINVAL;
+		}
+
+		val |= (i << IBB_CURRENT_LIMIT_DEBOUNCE_SHIFT);
+		mask |= IBB_CURRENT_LIMIT_DEBOUNCE_MASK;
+	}
+
+	if (of_property_read_bool(of_node,
+		"qcom,qpnp-ibb-limit-max-current-enable")) {
+		val |= IBB_CURRENT_LIMIT_EN;
+		mask |= IBB_CURRENT_LIMIT_EN;
+	}
+
+	rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base,
+				REG_IBB_CURRENT_LIMIT, mask, val);
+	if (rc < 0) {
+		pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
+				REG_IBB_CURRENT_LIMIT, rc);
+		return rc;
+	}
+
+	if (of_property_read_bool(of_node,
+		"qcom,qpnp-ibb-ring-suppression-enable")) {
+		val = IBB_RING_SUPPRESSION_CTL_EN;
+		rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+					REG_IBB_RING_SUPPRESSION_CTL,
+					&val,
+					1);
+		if (rc < 0) {
+			pr_err("qpnp_ibb_dt_init write register %x failed rc = %d\n",
+				REG_IBB_RING_SUPPRESSION_CTL, rc);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(of_node, "qcom,qpnp-ibb-ps-enable")) {
+		rc = qpnp_ibb_ps_config(labibb, true);
+		if (rc < 0) {
+			pr_err("qpnp_ibb_dt_init PS enable failed rc=%d\n", rc);
+			return rc;
+		}
+	} else {
+		rc = qpnp_ibb_ps_config(labibb, false);
+		if (rc < 0) {
+			pr_err("qpnp_ibb_dt_init PS disable failed rc=%d\n",
+									rc);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(of_node,
+				 "qcom,qpnp-ibb-smart-ps-enable")){
+		of_property_read_u32(of_node, "qcom,qpnp-ibb-num-swire-trans",
+					&labibb->ibb_vreg.num_swire_trans);
+
+		of_property_read_u32(of_node,
+				"qcom,qpnp-ibb-neg-curr-limit", &tmp);
+
+		rc = labibb->ibb_ver_ops->smart_ps_config(labibb, true,
+					labibb->ibb_vreg.num_swire_trans, tmp);
+		if (rc < 0) {
+			pr_err("qpnp_ibb_dt_init smart PS enable failed rc=%d\n",
+					 rc);
+			return rc;
+		}
+
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-init-voltage",
+					&(labibb->ibb_vreg.curr_volt));
+	if (rc < 0) {
+		pr_err("get qcom,qpnp-ibb-init-voltage failed, rc = %d\n", rc);
+		return rc;
+	}
+
+	if (of_property_read_bool(of_node,
+			"qcom,qpnp-ibb-use-default-voltage"))
+		rc = labibb->ibb_ver_ops->set_default_voltage(labibb, true);
+	else
+		rc = labibb->ibb_ver_ops->set_default_voltage(labibb, false);
+
+	if (rc < 0)
+		return rc;
+
+	if (of_property_read_bool(of_node, "qcom,qpnp-ibb-overload-blank")) {
+		rc = qpnp_ibb_vreg_ok_ctl(labibb, of_node);
+		if (rc < 0)
+			return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_ibb_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc, delay, retries = 10;
+	u8 val;
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (!labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
+
+		if (!labibb->standalone)
+			return qpnp_labibb_regulator_enable(labibb);
+
+		rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_EN);
+		if (rc < 0) {
+			pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+			return rc;
+		}
+
+		delay = labibb->ibb_vreg.soft_start;
+		while (retries--) {
+			/* Wait for a small period before reading IBB_STATUS1 */
+			usleep_range(delay, delay + 100);
+
+			rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+					REG_IBB_STATUS1, &val, 1);
+			if (rc < 0) {
+				pr_err("qpnp_ibb_regulator_enable read register %x failed rc = %d\n",
+					REG_IBB_STATUS1, rc);
+				return rc;
+			}
+
+			if (val & IBB_STATUS1_VREG_OK)
+				break;
+		}
+
+		if (!(val & IBB_STATUS1_VREG_OK)) {
+			pr_err("qpnp_ibb_regulator_enable failed\n");
+			return -EINVAL;
+		}
+
+		labibb->ibb_vreg.vreg_enabled = 1;
+	}
+	return 0;
+}
+
+static int qpnp_ibb_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc;
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->ibb_vreg.vreg_enabled && !labibb->swire_control) {
+
+		if (!labibb->standalone)
+			return qpnp_labibb_regulator_disable(labibb);
+
+		rc = qpnp_ibb_set_mode(labibb, IBB_SW_CONTROL_DIS);
+		if (rc < 0) {
+			pr_err("Unable to set IBB_MODULE_EN rc = %d\n", rc);
+			return rc;
+		}
+
+		labibb->ibb_vreg.vreg_enabled = 0;
+	}
+	return 0;
+}
+
+static int qpnp_ibb_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->swire_control)
+		return 0;
+
+	return labibb->ibb_vreg.vreg_enabled;
+}
+
+static int qpnp_ibb_regulator_set_voltage(struct regulator_dev *rdev,
+				int min_uV, int max_uV, unsigned int *selector)
+{
+	int rc = 0;
+
+	struct qpnp_labibb *labibb = rdev_get_drvdata(rdev);
+
+	if (labibb->swire_control)
+		return 0;
+
+	rc = labibb->ibb_ver_ops->set_voltage(labibb, min_uV, max_uV);
+	return rc;
+}
+
+
+static int qpnp_ibb_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_labibb *labibb  = rdev_get_drvdata(rdev);
+
+	if (labibb->swire_control)
+		return 0;
+
+	return labibb->ibb_vreg.curr_volt;
+}
+
+static struct regulator_ops qpnp_ibb_ops = {
+	.enable			= qpnp_ibb_regulator_enable,
+	.disable		= qpnp_ibb_regulator_disable,
+	.is_enabled		= qpnp_ibb_regulator_is_enabled,
+	.set_voltage		= qpnp_ibb_regulator_set_voltage,
+	.get_voltage		= qpnp_ibb_regulator_get_voltage,
+};
+
+static int register_qpnp_ibb_regulator(struct qpnp_labibb *labibb,
+					struct device_node *of_node)
+{
+	int rc = 0;
+	struct regulator_init_data *init_data;
+	struct regulator_desc *rdesc = &labibb->ibb_vreg.rdesc;
+	struct regulator_config cfg = {};
+	u8 val, ibb_enable_ctl, index;
+	u32 tmp;
+
+	if (!of_node) {
+		dev_err(labibb->dev, "qpnp ibb regulator device tree node is missing\n");
+		return -EINVAL;
+	}
+
+	init_data = of_get_regulator_init_data(labibb->dev, of_node, rdesc);
+	if (!init_data) {
+		pr_err("unable to get regulator init data for qpnp ibb regulator\n");
+		return -ENOMEM;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-min-voltage",
+					&(labibb->ibb_vreg.min_volt));
+	if (rc < 0) {
+		pr_err("qcom,qpnp-ibb-min-voltage is missing, rc = %d\n",
+			rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-step-size",
+					&(labibb->ibb_vreg.step_size));
+	if (rc < 0) {
+		pr_err("qcom,qpnp-ibb-step-size is missing, rc = %d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(of_node, "qcom,qpnp-ibb-slew-rate",
+					&(labibb->ibb_vreg.slew_rate));
+	if (rc < 0)
+		labibb->ibb_vreg.slew_rate = IBB_HW_DEFAULT_SLEW_RATE;
+
+	rc = labibb->ibb_ver_ops->soft_start_ctl(labibb, of_node);
+	if (rc < 0) {
+		pr_err("qpnp_labibb_write register %x failed rc = %d\n",
+			REG_IBB_SOFT_START_CTL, rc);
+		return rc;
+	}
+
+	if (of_find_property(of_node, "qcom,output-voltage-one-pulse", NULL)) {
+		if (!labibb->swire_control) {
+			pr_err("output-voltage-one-pulse valid for SWIRE only\n");
+			return -EINVAL;
+		}
+		rc = of_property_read_u32(of_node,
+				"qcom,output-voltage-one-pulse", &tmp);
+		if (rc < 0) {
+			pr_err("failed to read qcom,output-voltage-one-pulse rc=%d\n",
+									rc);
+			return rc;
+		}
+		if (tmp > MAX_OUTPUT_PULSE_VOLTAGE_MV ||
+				tmp < MIN_OUTPUT_PULSE_VOLTAGE_MV) {
+			pr_err("Invalid one-pulse voltage range %d\n", tmp);
+			return -EINVAL;
+		}
+		rc = labibb->ibb_ver_ops->voltage_at_one_pulse(labibb, tmp);
+		if (rc < 0)
+			return rc;
+	}
+
+	rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_ENABLE_CTL,
+				&ibb_enable_ctl, 1);
+	if (rc < 0) {
+		pr_err("qpnp_ibb_read register %x failed rc = %d\n",
+			REG_IBB_ENABLE_CTL, rc);
+		return rc;
+	}
+
+	/*
+	 * For pmi8998, override swire_control with what was configured
+	 * before by the bootloader.
+	 */
+	if (labibb->pmic_rev_id->pmic_subtype == PMI8998_SUBTYPE)
+		labibb->swire_control = ibb_enable_ctl &
+						IBB_ENABLE_CTL_SWIRE_RDY;
+
+	if (ibb_enable_ctl &
+		(IBB_ENABLE_CTL_SWIRE_RDY | IBB_ENABLE_CTL_MODULE_EN)) {
+
+		rc = labibb->ibb_ver_ops->get_mode(labibb);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_read register %x failed rc = %d\n",
+				REG_IBB_LCD_AMOLED_SEL, rc);
+			return rc;
+		}
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+					REG_IBB_VOLTAGE, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_read read register %x failed rc = %d\n",
+				REG_IBB_VOLTAGE, rc);
+			return rc;
+		}
+
+		labibb->ibb_vreg.curr_volt =
+			(val & IBB_VOLTAGE_SET_MASK) *
+			labibb->ibb_vreg.step_size +
+			labibb->ibb_vreg.min_volt;
+
+		if (labibb->mode == QPNP_LABIBB_LCD_MODE) {
+			rc = of_property_read_u32(of_node,
+				"qcom,qpnp-ibb-init-lcd-voltage",
+				&(labibb->ibb_vreg.curr_volt));
+			if (rc < 0) {
+				pr_err("get qcom,qpnp-ibb-init-lcd-voltage failed, rc = %d\n",
+					rc);
+				return rc;
+			}
+		} else if (!(val & IBB_VOLTAGE_OVERRIDE_EN)) {
+			rc = of_property_read_u32(of_node,
+				"qcom,qpnp-ibb-init-amoled-voltage",
+				&(labibb->ibb_vreg.curr_volt));
+			if (rc < 0) {
+				pr_err("get qcom,qpnp-ibb-init-amoled-voltage failed, rc = %d\n",
+					rc);
+				return rc;
+			}
+
+		}
+
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+				REG_IBB_PWRUP_PWRDN_CTL_1, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_config_init read register %x failed rc = %d\n",
+				REG_IBB_PWRUP_PWRDN_CTL_1, rc);
+			return rc;
+		}
+
+		index = (val & IBB_PWRUP_PWRDN_CTL_1_DLY1_MASK) >>
+				IBB_PWRUP_PWRDN_CTL_1_DLY1_SHIFT;
+		labibb->ibb_vreg.pwrup_dly = ibb_pwrup_dly_table[index];
+		index = val & IBB_PWRUP_PWRDN_CTL_1_DLY2_MASK;
+		labibb->ibb_vreg.pwrdn_dly =  ibb_pwrdn_dly_table[index];
+
+		labibb->ibb_vreg.vreg_enabled = 1;
+	} else {
+		/* SWIRE_RDY and IBB_MODULE_EN not enabled */
+		rc = qpnp_ibb_dt_init(labibb, of_node);
+		if (rc < 0) {
+			pr_err("qpnp-ibb: wrong DT parameter specified: rc = %d\n",
+				rc);
+			return rc;
+		}
+	}
+
+	if (labibb->mode == QPNP_LABIBB_AMOLED_MODE &&
+			qpnp_ibb_poff_ctl_required(labibb)) {
+
+		val = IBB_OVERRIDE_NONOVERLAP | IBB_NFET_GATE_DELAY_2;
+		rc = qpnp_labibb_sec_masked_write(labibb, labibb->ibb_base,
+			REG_IBB_NONOVERLAP_TIME_1,
+			IBB_OVERRIDE_NONOVERLAP | IBB_NONOVERLAP_NFET_MASK,
+			val);
+
+		if (rc < 0) {
+			pr_err("qpnp_labibb_sec_masked_write register %x failed rc = %d\n",
+				REG_IBB_NONOVERLAP_TIME_1, rc);
+			return rc;
+		}
+
+		val = IBB_N2P_MUX_SEL;
+		rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
+			REG_IBB_NONOVERLAP_TIME_2, val);
+
+		if (rc < 0) {
+			pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
+				REG_IBB_NONOVERLAP_TIME_2, rc);
+			return rc;
+		}
+
+		val = IBB_FASTER_PFET_OFF;
+		rc = qpnp_labibb_masked_write(labibb,
+				labibb->ibb_base + REG_IBB_SPARE_CTL,
+				IBB_POFF_CTL_MASK, val);
+		if (rc < 0) {
+			pr_err("write to register %x failed rc = %d\n",
+				 REG_IBB_SPARE_CTL, rc);
+			return rc;
+		}
+	}
+
+	if (labibb->standalone) {
+		val = 0;
+		rc = qpnp_labibb_sec_write(labibb, labibb->ibb_base,
+				REG_IBB_PWRUP_PWRDN_CTL_1, val);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_sec_write register %x failed rc = %d\n",
+				REG_IBB_PWRUP_PWRDN_CTL_1, rc);
+			return rc;
+		}
+		labibb->ibb_vreg.pwrup_dly = 0;
+		labibb->ibb_vreg.pwrdn_dly = 0;
+	}
+
+	rc = qpnp_labibb_read(labibb, labibb->ibb_base + REG_IBB_MODULE_RDY,
+				&val, 1);
+	if (rc < 0) {
+		pr_err("qpnp_ibb_read read register %x failed rc = %d\n",
+			REG_IBB_MODULE_RDY, rc);
+		return rc;
+	}
+
+	if (!(val & IBB_MODULE_RDY_EN)) {
+		val = IBB_MODULE_RDY_EN;
+
+		rc = qpnp_labibb_write(labibb, labibb->ibb_base +
+			REG_IBB_MODULE_RDY, &val, 1);
+
+		if (rc < 0) {
+			pr_err("qpnp_ibb_dt_init write register %x failed rc = %d\n",
+				REG_IBB_MODULE_RDY, rc);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(of_node,
+			"qcom,qpnp-ibb-enable-pfm-mode")) {
+		rc = qpnp_ibb_pfm_mode_enable(labibb, of_node);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (labibb->pbs_control) {
+		rc = qpnp_labibb_pbs_mode_enable(labibb, of_node);
+		if (rc < 0)
+			return rc;
+	}
+
+	if (init_data->constraints.name) {
+		rdesc->owner		= THIS_MODULE;
+		rdesc->type		= REGULATOR_VOLTAGE;
+		rdesc->ops		= &qpnp_ibb_ops;
+		rdesc->name		= init_data->constraints.name;
+
+		cfg.dev = labibb->dev;
+		cfg.init_data = init_data;
+		cfg.driver_data = labibb;
+		cfg.of_node = of_node;
+
+		if (of_get_property(labibb->dev->of_node, "parent-supply",
+				 NULL))
+			init_data->supply_regulator = "parent";
+
+		init_data->constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_VOLTAGE |
+					REGULATOR_CHANGE_STATUS;
+
+		labibb->ibb_vreg.rdev = regulator_register(rdesc, &cfg);
+		if (IS_ERR(labibb->ibb_vreg.rdev)) {
+			rc = PTR_ERR(labibb->ibb_vreg.rdev);
+			labibb->ibb_vreg.rdev = NULL;
+			pr_err("unable to get regulator init data for qpnp ibb regulator, rc = %d\n",
+				rc);
+
+			return rc;
+		}
+	} else {
+		dev_err(labibb->dev, "qpnp ibb regulator name missing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_lab_register_irq(struct device_node *child,
+				struct qpnp_labibb *labibb)
+{
+	if (is_lab_vreg_ok_irq_available(labibb)) {
+		labibb->lab_vreg.lab_vreg_ok_irq =
+					of_irq_get_byname(child, "lab-vreg-ok");
+		if (labibb->lab_vreg.lab_vreg_ok_irq < 0) {
+			pr_err("Invalid lab-vreg-ok irq\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_labibb_check_ttw_supported(struct qpnp_labibb *labibb)
+{
+	int rc = 0;
+	u8 val;
+
+	switch (labibb->pmic_rev_id->pmic_subtype) {
+	case PMI8996_SUBTYPE:
+		rc = qpnp_labibb_read(labibb, labibb->ibb_base +
+					REG_IBB_REVISION4, &val, 1);
+		if (rc < 0) {
+			pr_err("qpnp_labibb_read register %x failed rc = %d\n",
+				REG_IBB_REVISION4, rc);
+			return rc;
+		}
+
+		/* PMI8996 has revision 1 */
+		if (val < 1) {
+			pr_err("TTW feature cannot be enabled for revision %d\n",
+									val);
+			labibb->ttw_en = false;
+		}
+		/* FORCE_LAB_ON in TTW is not required for PMI8996 */
+		labibb->ttw_force_lab_on = false;
+		break;
+	case PMI8950_SUBTYPE:
+		/* TTW supported for all revisions */
+		break;
+	default:
+		pr_info("TTW mode not supported for PMIC-subtype = %d\n",
+					labibb->pmic_rev_id->pmic_subtype);
+		labibb->ttw_en = false;
+		break;
+
+	}
+	return rc;
+}
+
+static int qpnp_labibb_regulator_probe(struct platform_device *pdev)
+{
+	struct qpnp_labibb *labibb;
+	unsigned int base;
+	struct device_node *child, *revid_dev_node;
+	const char *mode_name;
+	u8 type, revision;
+	int rc = 0;
+
+	labibb = devm_kzalloc(&pdev->dev, sizeof(*labibb), GFP_KERNEL);
+	if (labibb == NULL)
+		return -ENOMEM;
+
+	labibb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!labibb->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	labibb->dev = &(pdev->dev);
+	labibb->pdev = pdev;
+
+	mutex_init(&(labibb->lab_vreg.lab_mutex));
+	mutex_init(&(labibb->ibb_vreg.ibb_mutex));
+	mutex_init(&(labibb->bus_mutex));
+
+	revid_dev_node = of_parse_phandle(labibb->dev->of_node,
+					"qcom,pmic-revid", 0);
+	if (!revid_dev_node) {
+		pr_err("Missing qcom,pmic-revid property - driver failed\n");
+		return -EINVAL;
+	}
+
+	labibb->pmic_rev_id = get_revid_data(revid_dev_node);
+	if (IS_ERR(labibb->pmic_rev_id)) {
+		pr_debug("Unable to get revid data\n");
+		return -EPROBE_DEFER;
+	}
+
+	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+		labibb->ibb_ver_ops = &ibb_ops_v2;
+		labibb->lab_ver_ops = &lab_ops_v2;
+	} else {
+		labibb->ibb_ver_ops = &ibb_ops_v1;
+		labibb->lab_ver_ops = &lab_ops_v1;
+	}
+
+	if (labibb->pmic_rev_id->pmic_subtype == PM660L_SUBTYPE) {
+		labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+	} else {
+		rc = of_property_read_string(labibb->dev->of_node,
+				"qcom,qpnp-labibb-mode", &mode_name);
+		if (!rc) {
+			if (strcmp("lcd", mode_name) == 0) {
+				labibb->mode = QPNP_LABIBB_LCD_MODE;
+			} else if (strcmp("amoled", mode_name) == 0) {
+				labibb->mode = QPNP_LABIBB_AMOLED_MODE;
+			} else {
+				pr_err("Invalid device property in qcom,qpnp-labibb-mode: %s\n",
+					mode_name);
+				return -EINVAL;
+			}
+		} else {
+			pr_err("qpnp_labibb: qcom,qpnp-labibb-mode is missing.\n");
+			return rc;
+		}
+	}
+
+	labibb->standalone = of_property_read_bool(labibb->dev->of_node,
+				"qcom,labibb-standalone");
+
+	labibb->ttw_en = of_property_read_bool(labibb->dev->of_node,
+				"qcom,labibb-touch-to-wake-en");
+	if (labibb->ttw_en && labibb->mode != QPNP_LABIBB_LCD_MODE) {
+		pr_err("Invalid mode for TTW\n");
+		return -EINVAL;
+	}
+
+	labibb->ttw_force_lab_on = of_property_read_bool(
+		labibb->dev->of_node, "qcom,labibb-ttw-force-lab-on");
+
+	labibb->swire_control = of_property_read_bool(labibb->dev->of_node,
+							"qcom,swire-control");
+
+	labibb->pbs_control = of_property_read_bool(labibb->dev->of_node,
+							"qcom,pbs-control");
+	if (labibb->swire_control && labibb->mode != QPNP_LABIBB_AMOLED_MODE) {
+		pr_err("Invalid mode for SWIRE control\n");
+		return -EINVAL;
+	}
+
+	if (labibb->swire_control) {
+		labibb->skip_2nd_swire_cmd =
+				of_property_read_bool(labibb->dev->of_node,
+				"qcom,skip-2nd-swire-cmd");
+
+		rc = of_property_read_u32(labibb->dev->of_node,
+				"qcom,swire-2nd-cmd-delay",
+				&labibb->swire_2nd_cmd_delay);
+		if (rc < 0)
+			labibb->swire_2nd_cmd_delay =
+					SWIRE_DEFAULT_2ND_CMD_DLY_MS;
+
+		rc = of_property_read_u32(labibb->dev->of_node,
+				"qcom,swire-ibb-ps-enable-delay",
+				&labibb->swire_ibb_ps_enable_delay);
+		if (rc < 0)
+			labibb->swire_ibb_ps_enable_delay =
+					SWIRE_DEFAULT_IBB_PS_ENABLE_DLY_MS;
+	}
+
+	if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		return -ENXIO;
+	}
+
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				child->full_name, rc);
+			return rc;
+		}
+
+		rc = qpnp_labibb_read(labibb, base + REG_REVISION_2,
+					 &revision, 1);
+		if (rc < 0) {
+			pr_err("Reading REVISION_2 failed rc=%d\n", rc);
+			goto fail_registration;
+		}
+
+		rc = qpnp_labibb_read(labibb, base + REG_PERPH_TYPE,
+					&type, 1);
+		if (rc < 0) {
+			pr_err("Peripheral type read failed rc=%d\n", rc);
+			goto fail_registration;
+		}
+
+		switch (type) {
+		case QPNP_LAB_TYPE:
+			labibb->lab_base = base;
+			labibb->lab_dig_major = revision;
+			rc = qpnp_lab_register_irq(child, labibb);
+			if (rc) {
+				pr_err("Failed to register LAB IRQ rc=%d\n",
+							rc);
+				goto fail_registration;
+			}
+			rc = register_qpnp_lab_regulator(labibb, child);
+			if (rc < 0)
+				goto fail_registration;
+		break;
+
+		case QPNP_IBB_TYPE:
+			labibb->ibb_base = base;
+			labibb->ibb_dig_major = revision;
+			rc = register_qpnp_ibb_regulator(labibb, child);
+			if (rc < 0)
+				goto fail_registration;
+		break;
+
+		default:
+			pr_err("qpnp_labibb: unknown peripheral type %x\n",
+				type);
+			rc = -EINVAL;
+			goto fail_registration;
+		}
+	}
+
+	if (labibb->ttw_en) {
+		rc = qpnp_labibb_check_ttw_supported(labibb);
+		if (rc < 0) {
+			pr_err("pmic revision check failed for TTW rc=%d\n",
+									rc);
+			goto fail_registration;
+		}
+	}
+	dev_set_drvdata(&pdev->dev, labibb);
+	pr_info("LAB/IBB registered successfully, lab_vreg enable=%d ibb_vreg enable=%d swire_control=%d\n",
+						labibb->lab_vreg.vreg_enabled,
+						labibb->ibb_vreg.vreg_enabled,
+						labibb->swire_control);
+
+	return 0;
+
+fail_registration:
+	if (labibb->lab_vreg.rdev)
+		regulator_unregister(labibb->lab_vreg.rdev);
+	if (labibb->ibb_vreg.rdev)
+		regulator_unregister(labibb->ibb_vreg.rdev);
+
+	return rc;
+}
+
+static int qpnp_labibb_regulator_remove(struct platform_device *pdev)
+{
+	struct qpnp_labibb *labibb = dev_get_drvdata(&pdev->dev);
+
+	if (labibb) {
+		if (labibb->lab_vreg.rdev)
+			regulator_unregister(labibb->lab_vreg.rdev);
+		if (labibb->ibb_vreg.rdev)
+			regulator_unregister(labibb->ibb_vreg.rdev);
+	}
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = QPNP_LABIBB_REGULATOR_DRIVER_NAME, },
+	{ },
+};
+
+static struct platform_driver qpnp_labibb_regulator_driver = {
+	.driver		= {
+		.name		= QPNP_LABIBB_REGULATOR_DRIVER_NAME,
+		.of_match_table	= spmi_match_table,
+	},
+	.probe		= qpnp_labibb_regulator_probe,
+	.remove		= qpnp_labibb_regulator_remove,
+};
+
+static int __init qpnp_labibb_regulator_init(void)
+{
+	return platform_driver_register(&qpnp_labibb_regulator_driver);
+}
+arch_initcall(qpnp_labibb_regulator_init);
+
+static void __exit qpnp_labibb_regulator_exit(void)
+{
+	platform_driver_unregister(&qpnp_labibb_regulator_driver);
+}
+module_exit(qpnp_labibb_regulator_exit);
+
+MODULE_DESCRIPTION("QPNP labibb driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qpnp-lcdb-regulator.c b/drivers/regulator/qpnp-lcdb-regulator.c
new file mode 100644
index 0000000..a08ade6
--- /dev/null
+++ b/drivers/regulator/qpnp-lcdb-regulator.c
@@ -0,0 +1,1692 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"LCDB: %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
+
+#define QPNP_LCDB_REGULATOR_DRIVER_NAME		"qcom,qpnp-lcdb-regulator"
+
+/* LCDB */
+#define LCDB_STS1_REG			0x08
+
+#define INT_RT_STATUS_REG		0x10
+#define VREG_OK_RT_STS_BIT		BIT(0)
+
+#define LCDB_AUTO_TOUCH_WAKE_CTL_REG	0x40
+#define EN_AUTO_TOUCH_WAKE_BIT		BIT(7)
+#define ATTW_TOFF_TIME_MASK		GENMASK(3, 2)
+#define ATTW_TON_TIME_MASK		GENMASK(1, 0)
+#define ATTW_TOFF_TIME_SHIFT		2
+#define ATTW_MIN_MS			4
+#define ATTW_MAX_MS			32
+
+#define LCDB_BST_OUTPUT_VOLTAGE_REG	0x41
+
+#define LCDB_MODULE_RDY_REG		0x45
+#define MODULE_RDY_BIT			BIT(7)
+
+#define LCDB_ENABLE_CTL1_REG		0x46
+#define MODULE_EN_BIT			BIT(7)
+#define HWEN_RDY_BIT			BIT(6)
+
+/* BST */
+#define LCDB_BST_PD_CTL_REG		0x47
+#define BOOST_DIS_PULLDOWN_BIT		BIT(1)
+#define BOOST_PD_STRENGTH_BIT		BIT(0)
+
+#define LCDB_BST_ILIM_CTL_REG		0x4B
+#define EN_BST_ILIM_BIT			BIT(7)
+#define SET_BST_ILIM_MASK		GENMASK(2, 0)
+#define MIN_BST_ILIM_MA			200
+#define MAX_BST_ILIM_MA			1600
+
+#define LCDB_PS_CTL_REG			0x50
+#define EN_PS_BIT			BIT(7)
+#define PS_THRESHOLD_MASK		GENMASK(1, 0)
+#define MIN_BST_PS_MA			50
+#define MAX_BST_PS_MA			80
+
+#define LCDB_RDSON_MGMNT_REG		0x53
+#define NFET_SW_SIZE_MASK		GENMASK(3, 2)
+#define NFET_SW_SIZE_SHIFT		2
+#define PFET_SW_SIZE_MASK		GENMASK(1, 0)
+
+#define LCDB_BST_VREG_OK_CTL_REG	0x55
+#define BST_VREG_OK_DEB_MASK		GENMASK(1, 0)
+
+#define LCDB_SOFT_START_CTL_REG		0x5F
+
+#define LCDB_MISC_CTL_REG		0x60
+#define AUTO_GM_EN_BIT			BIT(4)
+#define EN_TOUCH_WAKE_BIT		BIT(3)
+#define DIS_SCP_BIT			BIT(0)
+
+#define LCDB_PFM_CTL_REG		0x62
+#define EN_PFM_BIT			BIT(7)
+#define BYP_BST_SOFT_START_COMP_BIT	BIT(0)
+#define PFM_HYSTERESIS_SHIFT		4
+#define PFM_CURRENT_SHIFT		2
+
+#define LCDB_PWRUP_PWRDN_CTL_REG	0x66
+
+/* LDO */
+#define LCDB_LDO_OUTPUT_VOLTAGE_REG	0x71
+#define SET_OUTPUT_VOLTAGE_MASK		GENMASK(4, 0)
+
+#define LCDB_LDO_VREG_OK_CTL_REG	0x75
+#define VREG_OK_DEB_MASK		GENMASK(1, 0)
+
+#define LCDB_LDO_PD_CTL_REG		0x77
+#define LDO_DIS_PULLDOWN_BIT		BIT(1)
+#define LDO_PD_STRENGTH_BIT		BIT(0)
+
+#define LCDB_LDO_ILIM_CTL1_REG		0x7B
+#define EN_LDO_ILIM_BIT			BIT(7)
+#define SET_LDO_ILIM_MASK		GENMASK(2, 0)
+#define MIN_LDO_ILIM_MA			110
+#define MAX_LDO_ILIM_MA			460
+#define LDO_ILIM_STEP_MA		50
+
+#define LCDB_LDO_ILIM_CTL2_REG		0x7C
+
+#define LCDB_LDO_SOFT_START_CTL_REG	0x7F
+#define SOFT_START_MASK			GENMASK(1, 0)
+
+/* NCP */
+#define LCDB_NCP_OUTPUT_VOLTAGE_REG	0x81
+
+#define LCDB_NCP_VREG_OK_CTL_REG	0x85
+
+#define LCDB_NCP_PD_CTL_REG		0x87
+#define NCP_DIS_PULLDOWN_BIT		BIT(1)
+#define NCP_PD_STRENGTH_BIT		BIT(0)
+
+#define LCDB_NCP_ILIM_CTL1_REG		0x8B
+#define EN_NCP_ILIM_BIT			BIT(7)
+#define SET_NCP_ILIM_MASK		GENMASK(1, 0)
+#define MIN_NCP_ILIM_MA			260
+#define MAX_NCP_ILIM_MA			810
+
+#define LCDB_NCP_ILIM_CTL2_REG		0x8C
+
+#define LCDB_NCP_SOFT_START_CTL_REG	0x8F
+
+/* common for BST/NCP/LDO */
+#define MIN_DBC_US			2
+#define MAX_DBC_US			32
+
+#define MIN_SOFT_START_US		0
+#define MAX_SOFT_START_US		2000
+
+struct ldo_regulator {
+	struct regulator_desc		rdesc;
+	struct regulator_dev		*rdev;
+	struct device_node		*node;
+
+	/* LDO DT params */
+	int				pd;
+	int				pd_strength;
+	int				ilim_ma;
+	int				soft_start_us;
+	int				vreg_ok_dbc_us;
+	int				voltage_mv;
+};
+
+struct ncp_regulator {
+	struct regulator_desc		rdesc;
+	struct regulator_dev		*rdev;
+	struct device_node		*node;
+
+	/* NCP DT params */
+	int				pd;
+	int				pd_strength;
+	int				ilim_ma;
+	int				soft_start_us;
+	int				vreg_ok_dbc_us;
+	int				voltage_mv;
+};
+
+struct bst_params {
+	struct device_node		*node;
+
+	/* BST DT params */
+	int				pd;
+	int				pd_strength;
+	int				ilim_ma;
+	int				ps;
+	int				ps_threshold;
+	int				soft_start_us;
+	int				vreg_ok_dbc_us;
+	int				voltage_mv;
+};
+
+struct qpnp_lcdb {
+	struct device			*dev;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	u32				base;
+
+	/* TTW params */
+	bool				ttw_enable;
+	bool				ttw_mode_sw;
+
+	/* status parameters */
+	bool				lcdb_enabled;
+	bool				settings_saved;
+
+	struct mutex			lcdb_mutex;
+	struct mutex			read_write_mutex;
+	struct bst_params		bst;
+	struct ldo_regulator		ldo;
+	struct ncp_regulator		ncp;
+};
+
+struct settings {
+	u16	address;
+	u8	value;
+	bool	sec_access;
+};
+
+enum lcdb_module {
+	LDO,
+	NCP,
+	BST,
+};
+
+enum pfm_hysteresis {
+	PFM_HYST_15MV,
+	PFM_HYST_25MV,
+	PFM_HYST_35MV,
+	PFM_HYST_45MV,
+};
+
+enum pfm_peak_current {
+	PFM_PEAK_CURRENT_300MA,
+	PFM_PEAK_CURRENT_400MA,
+	PFM_PEAK_CURRENT_500MA,
+	PFM_PEAK_CURRENT_600MA,
+};
+
+enum rdson_fet_size {
+	RDSON_QUARTER,
+	RDSON_HALF,
+	RDSON_THREE_FOURTH,
+	RDSON_FULLSIZE,
+};
+
+enum lcdb_settings_index {
+	LCDB_BST_PD_CTL = 0,
+	LCDB_RDSON_MGMNT,
+	LCDB_MISC_CTL,
+	LCDB_SOFT_START_CTL,
+	LCDB_PFM_CTL,
+	LCDB_PWRUP_PWRDN_CTL,
+	LCDB_LDO_PD_CTL,
+	LCDB_LDO_SOFT_START_CTL,
+	LCDB_NCP_PD_CTL,
+	LCDB_NCP_SOFT_START_CTL,
+	LCDB_SETTING_MAX,
+};
+
+static u32 soft_start_us[] = {
+	0,
+	500,
+	1000,
+	2000,
+};
+
+static u32 dbc_us[] = {
+	2,
+	4,
+	16,
+	32,
+};
+
+static u32 ncp_ilim_ma[] = {
+	260,
+	460,
+	640,
+	810,
+};
+
+#define SETTING(_id, _sec_access)		\
+	[_id] = {				\
+		.address = _id##_REG,		\
+		.sec_access = _sec_access,	\
+	}					\
+
+static bool is_between(int value, int min, int max)
+{
+	if (value < min || value > max)
+		return false;
+	return true;
+}
+
+static int qpnp_lcdb_read(struct qpnp_lcdb *lcdb,
+			u16 addr, u8 *value, u8 count)
+{
+	int rc = 0;
+
+	mutex_lock(&lcdb->read_write_mutex);
+	rc = regmap_bulk_read(lcdb->regmap, addr, value, count);
+	if (rc < 0)
+		pr_err("Failed to read from addr=0x%02x rc=%d\n", addr, rc);
+	mutex_unlock(&lcdb->read_write_mutex);
+
+	return rc;
+}
+
+static int qpnp_lcdb_write(struct qpnp_lcdb *lcdb,
+			u16 addr, u8 *value, u8 count)
+{
+	int rc;
+
+	mutex_lock(&lcdb->read_write_mutex);
+	rc = regmap_bulk_write(lcdb->regmap, addr, value, count);
+	if (rc < 0)
+		pr_err("Failed to write to addr=0x%02x rc=%d\n", addr, rc);
+	mutex_unlock(&lcdb->read_write_mutex);
+
+	return rc;
+}
+
+#define SEC_ADDRESS_REG			0xD0
+#define SECURE_UNLOCK_VALUE		0xA5
+static int qpnp_lcdb_secure_write(struct qpnp_lcdb *lcdb,
+					u16 addr, u8 value)
+{
+	int rc;
+	u8 val = SECURE_UNLOCK_VALUE;
+
+	mutex_lock(&lcdb->read_write_mutex);
+	rc = regmap_write(lcdb->regmap, lcdb->base + SEC_ADDRESS_REG, val);
+	if (rc < 0) {
+		pr_err("Failed to unlock register rc=%d\n", rc);
+		goto fail_write;
+	}
+	rc = regmap_write(lcdb->regmap, addr, value);
+	if (rc < 0)
+		pr_err("Failed to write to addr=0x%02x rc=%d\n", addr, rc);
+
+fail_write:
+	mutex_unlock(&lcdb->read_write_mutex);
+	return rc;
+}
+
+static int qpnp_lcdb_masked_write(struct qpnp_lcdb *lcdb,
+				u16 addr, u8 mask, u8 value)
+{
+	int rc = 0;
+
+	mutex_lock(&lcdb->read_write_mutex);
+	rc = regmap_update_bits(lcdb->regmap, addr, mask, value);
+	if (rc < 0)
+		pr_err("Failed to write addr=0x%02x value=0x%02x rc=%d\n",
+			addr, value, rc);
+	mutex_unlock(&lcdb->read_write_mutex);
+
+	return rc;
+}
+
+static bool is_lcdb_enabled(struct qpnp_lcdb *lcdb)
+{
+	int rc;
+	u8 val = 0;
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG, &val, 1);
+	if (rc < 0)
+		pr_err("Failed to read ENABLE_CTL1 rc=%d\n", rc);
+
+	return rc ? false : !!(val & MODULE_EN_BIT);
+}
+
+static int dump_status_registers(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	u8 sts[6] = {0};
+
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_STS1_REG, &sts[0], 6);
+	if (rc < 0) {
+		pr_err("Failed to write to STS registers rc=%d\n", rc);
+	} else {
+		rc = qpnp_lcdb_read(lcdb, lcdb->base + LCDB_STS1_REG, sts, 6);
+		if (rc < 0)
+			pr_err("Failed to read lcdb status rc=%d\n", rc);
+		else
+			pr_err("STS1=0x%02x STS2=0x%02x STS3=0x%02x STS4=0x%02x STS5=0x%02x, STS6=0x%02x\n",
+				sts[0], sts[1], sts[2], sts[3], sts[4], sts[5]);
+	}
+
+	return rc;
+}
+
+static struct settings lcdb_settings[] = {
+	SETTING(LCDB_BST_PD_CTL, false),
+	SETTING(LCDB_RDSON_MGMNT, false),
+	SETTING(LCDB_MISC_CTL, false),
+	SETTING(LCDB_SOFT_START_CTL, false),
+	SETTING(LCDB_PFM_CTL, false),
+	SETTING(LCDB_PWRUP_PWRDN_CTL, true),
+	SETTING(LCDB_LDO_PD_CTL, false),
+	SETTING(LCDB_LDO_SOFT_START_CTL, false),
+	SETTING(LCDB_NCP_PD_CTL, false),
+	SETTING(LCDB_NCP_SOFT_START_CTL, false),
+};
+
+static int qpnp_lcdb_save_settings(struct qpnp_lcdb *lcdb)
+{
+	int i, rc = 0;
+
+	for (i = 0; i < ARRAY_SIZE(lcdb_settings); i++) {
+		rc = qpnp_lcdb_read(lcdb, lcdb->base +
+				lcdb_settings[i].address,
+				&lcdb_settings[i].value, 1);
+		if (rc < 0) {
+			pr_err("Failed to read lcdb register address=%x\n",
+						lcdb_settings[i].address);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int qpnp_lcdb_restore_settings(struct qpnp_lcdb *lcdb)
+{
+	int i, rc = 0;
+
+	for (i = 0; i < ARRAY_SIZE(lcdb_settings); i++) {
+		if (lcdb_settings[i].sec_access)
+			rc = qpnp_lcdb_secure_write(lcdb, lcdb->base +
+					lcdb_settings[i].address,
+					lcdb_settings[i].value);
+		else
+			rc = qpnp_lcdb_write(lcdb, lcdb->base +
+					lcdb_settings[i].address,
+					&lcdb_settings[i].value, 1);
+		if (rc < 0) {
+			pr_err("Failed to write register address=%x\n",
+						lcdb_settings[i].address);
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int qpnp_lcdb_ttw_enter(struct qpnp_lcdb *lcdb)
+{
+	int rc;
+	u8 val;
+
+	if (!lcdb->settings_saved) {
+		rc = qpnp_lcdb_save_settings(lcdb);
+		if (rc < 0) {
+			pr_err("Failed to save LCDB settings rc=%d\n", rc);
+			return rc;
+		}
+		lcdb->settings_saved = true;
+	}
+
+	val = BOOST_DIS_PULLDOWN_BIT;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_BST_PD_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set BST PD rc=%d\n", rc);
+		return rc;
+	}
+
+	val = (RDSON_HALF << NFET_SW_SIZE_SHIFT) | RDSON_HALF;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_RDSON_MGMNT_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set RDSON MGMT rc=%d\n", rc);
+		return rc;
+	}
+
+	val = AUTO_GM_EN_BIT | EN_TOUCH_WAKE_BIT | DIS_SCP_BIT;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_MISC_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set MISC CTL rc=%d\n", rc);
+		return rc;
+	}
+
+	val = 0;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_SOFT_START_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set LCDB_SOFT_START rc=%d\n", rc);
+		return rc;
+	}
+
+	val = EN_PFM_BIT | (PFM_HYST_25MV << PFM_HYSTERESIS_SHIFT) |
+		     (PFM_PEAK_CURRENT_400MA << PFM_CURRENT_SHIFT) |
+				BYP_BST_SOFT_START_COMP_BIT;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_PFM_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set PFM_CTL rc=%d\n", rc);
+		return rc;
+	}
+
+	val = 0;
+	rc = qpnp_lcdb_secure_write(lcdb, lcdb->base + LCDB_PWRUP_PWRDN_CTL_REG,
+							val);
+	if (rc < 0) {
+		pr_err("Failed to set PWRUP_PWRDN_CTL rc=%d\n", rc);
+		return rc;
+	}
+
+	val = LDO_DIS_PULLDOWN_BIT;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_LDO_PD_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set LDO_PD_CTL rc=%d\n", rc);
+		return rc;
+	}
+
+	val = 0;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_LDO_SOFT_START_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set LDO_SOFT_START rc=%d\n", rc);
+		return rc;
+	}
+
+	val = NCP_DIS_PULLDOWN_BIT;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_NCP_PD_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set NCP_PD_CTL rc=%d\n", rc);
+		return rc;
+	}
+
+	val = 0;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_NCP_SOFT_START_CTL_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to set NCP_SOFT_START rc=%d\n", rc);
+		return rc;
+	}
+
+	if (lcdb->ttw_mode_sw) {
+		rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_AUTO_TOUCH_WAKE_CTL_REG,
+				EN_AUTO_TOUCH_WAKE_BIT,
+				EN_AUTO_TOUCH_WAKE_BIT);
+		if (rc < 0)
+			pr_err("Failed to enable auto(sw) TTW\n rc = %d\n", rc);
+	} else {
+		val = HWEN_RDY_BIT;
+		rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
+							&val, 1);
+		if (rc < 0)
+			pr_err("Failed to hw_enable lcdb rc= %d\n", rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_lcdb_ttw_exit(struct qpnp_lcdb *lcdb)
+{
+	int rc;
+
+	if (lcdb->settings_saved) {
+		rc = qpnp_lcdb_restore_settings(lcdb);
+		if (rc < 0) {
+			pr_err("Failed to restore lcdb settings rc=%d\n", rc);
+			return rc;
+		}
+		lcdb->settings_saved = false;
+	}
+
+	return 0;
+}
+
+static int qpnp_lcdb_enable(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0, timeout, delay;
+	u8 val = 0;
+
+	if (lcdb->lcdb_enabled)
+		return 0;
+
+	if (lcdb->ttw_enable) {
+		rc = qpnp_lcdb_ttw_exit(lcdb);
+		if (rc < 0) {
+			pr_err("Failed to exit TTW mode rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	val = MODULE_EN_BIT;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
+							&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to enable lcdb rc= %d\n", rc);
+		goto fail_enable;
+	}
+
+	/* poll for vreg_ok */
+	timeout = 10;
+	delay = lcdb->bst.soft_start_us + lcdb->ldo.soft_start_us +
+					lcdb->ncp.soft_start_us;
+	delay += lcdb->bst.vreg_ok_dbc_us + lcdb->ldo.vreg_ok_dbc_us +
+					lcdb->ncp.vreg_ok_dbc_us;
+	while (timeout--) {
+		rc = qpnp_lcdb_read(lcdb, lcdb->base + INT_RT_STATUS_REG,
+								&val, 1);
+		if (rc < 0) {
+			pr_err("Failed to poll for vreg-ok status rc=%d\n", rc);
+			break;
+		}
+		if (val & VREG_OK_RT_STS_BIT)
+			break;
+
+		usleep_range(delay, delay + 100);
+	}
+
+	if (rc || !timeout) {
+		if (!timeout) {
+			pr_err("lcdb-vreg-ok status failed to change\n");
+			rc = -ETIMEDOUT;
+		}
+		goto fail_enable;
+	}
+
+	lcdb->lcdb_enabled = true;
+	pr_debug("lcdb enabled successfully!\n");
+
+	return 0;
+
+fail_enable:
+	dump_status_registers(lcdb);
+	pr_err("Failed to enable lcdb rc=%d\n", rc);
+	return rc;
+}
+
+static int qpnp_lcdb_disable(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	u8 val;
+
+	if (!lcdb->lcdb_enabled)
+		return 0;
+
+	if (lcdb->ttw_enable) {
+		rc = qpnp_lcdb_ttw_enter(lcdb);
+		if (rc < 0) {
+			pr_err("Failed to enable TTW mode rc=%d\n", rc);
+			return rc;
+		}
+		lcdb->lcdb_enabled = false;
+
+		return 0;
+	}
+
+	val = 0;
+	rc = qpnp_lcdb_write(lcdb, lcdb->base + LCDB_ENABLE_CTL1_REG,
+							&val, 1);
+	if (rc < 0)
+		pr_err("Failed to disable lcdb rc= %d\n", rc);
+	else
+		lcdb->lcdb_enabled = false;
+
+	return rc;
+}
+
+#define MIN_BST_VOLTAGE_MV			4700
+#define MAX_BST_VOLTAGE_MV			6250
+#define MIN_VOLTAGE_MV				4000
+#define MAX_VOLTAGE_MV				6000
+#define VOLTAGE_MIN_STEP_100_MV			4000
+#define VOLTAGE_MIN_STEP_50_MV			4950
+#define VOLTAGE_STEP_100_MV			100
+#define VOLTAGE_STEP_50_MV			50
+#define VOLTAGE_STEP_50MV_OFFSET		0xA
+static int qpnp_lcdb_set_bst_voltage(struct qpnp_lcdb *lcdb,
+					int voltage_mv)
+{
+	int rc = 0;
+	u8 val = 0;
+
+	if (voltage_mv < MIN_BST_VOLTAGE_MV)
+		voltage_mv = MIN_BST_VOLTAGE_MV;
+	else if (voltage_mv > MAX_BST_VOLTAGE_MV)
+		voltage_mv = MAX_BST_VOLTAGE_MV;
+
+	val = DIV_ROUND_UP(voltage_mv - MIN_BST_VOLTAGE_MV,
+					VOLTAGE_STEP_50_MV);
+
+	rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_BST_OUTPUT_VOLTAGE_REG,
+				SET_OUTPUT_VOLTAGE_MASK, val);
+	if (rc < 0)
+		pr_err("Failed to set boost voltage %d mv rc=%d\n",
+			voltage_mv, rc);
+	else
+		pr_debug("Boost voltage set = %d mv (0x%02x = 0x%02x)\n",
+			voltage_mv, LCDB_BST_OUTPUT_VOLTAGE_REG, val);
+
+	return rc;
+}
+
+static int qpnp_lcdb_get_bst_voltage(struct qpnp_lcdb *lcdb,
+					int *voltage_mv)
+{
+	int rc;
+	u8 val = 0;
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base + LCDB_BST_OUTPUT_VOLTAGE_REG,
+						&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to reat BST voltage rc=%d\n", rc);
+		return rc;
+	}
+
+	val &= SET_OUTPUT_VOLTAGE_MASK;
+	*voltage_mv = (val * VOLTAGE_STEP_50_MV) + MIN_BST_VOLTAGE_MV;
+
+	return 0;
+}
+
+static int qpnp_lcdb_set_voltage(struct qpnp_lcdb *lcdb,
+					int voltage_mv, u8 type)
+{
+	int rc = 0;
+	u16 offset = LCDB_LDO_OUTPUT_VOLTAGE_REG;
+	u8 val = 0;
+
+	if (type == BST)
+		return qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv);
+
+	if (type == NCP)
+		offset = LCDB_NCP_OUTPUT_VOLTAGE_REG;
+
+	if (!is_between(voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV)) {
+		pr_err("Invalid voltage %dmv (min=%d max=%d)\n",
+			voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV);
+		return -EINVAL;
+	}
+
+	/* Change the BST voltage to LDO + 100mV */
+	if (type == LDO) {
+		rc = qpnp_lcdb_set_bst_voltage(lcdb, voltage_mv + 100);
+		if (rc < 0) {
+			pr_err("Failed to set boost voltage rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Below logic is only valid for LDO and NCP type */
+	if (voltage_mv < VOLTAGE_MIN_STEP_50_MV) {
+		val = DIV_ROUND_UP(voltage_mv - VOLTAGE_MIN_STEP_100_MV,
+						VOLTAGE_STEP_100_MV);
+	} else {
+		val = DIV_ROUND_UP(voltage_mv - VOLTAGE_MIN_STEP_50_MV,
+						VOLTAGE_STEP_50_MV);
+		val += VOLTAGE_STEP_50MV_OFFSET;
+	}
+
+	rc = qpnp_lcdb_masked_write(lcdb, lcdb->base + offset,
+				SET_OUTPUT_VOLTAGE_MASK, val);
+	if (rc < 0)
+		pr_err("Failed to set output voltage %d mv for %s rc=%d\n",
+			voltage_mv, (type == LDO) ? "LDO" : "NCP", rc);
+	else
+		pr_debug("%s voltage set = %d mv (0x%02x = 0x%02x)\n",
+			(type == LDO) ? "LDO" : "NCP", voltage_mv, offset, val);
+
+	return rc;
+}
+
+static int qpnp_lcdb_get_voltage(struct qpnp_lcdb *lcdb,
+					u32 *voltage_mv, u8 type)
+{
+	int rc = 0;
+	u16 offset = LCDB_LDO_OUTPUT_VOLTAGE_REG;
+	u8 val = 0;
+
+	if (type == BST)
+		return qpnp_lcdb_get_bst_voltage(lcdb, voltage_mv);
+
+	if (type == NCP)
+		offset = LCDB_NCP_OUTPUT_VOLTAGE_REG;
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base + offset, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read %s volatge rc=%d\n",
+			(type == LDO) ? "LDO" : "NCP", rc);
+		return rc;
+	}
+
+	if (val < VOLTAGE_STEP_50MV_OFFSET) {
+		*voltage_mv = VOLTAGE_MIN_STEP_100_MV +
+				(val * VOLTAGE_STEP_100_MV);
+	} else {
+		*voltage_mv = VOLTAGE_MIN_STEP_50_MV +
+			((val - VOLTAGE_STEP_50MV_OFFSET) * VOLTAGE_STEP_50_MV);
+	}
+
+	if (!rc)
+		pr_debug("%s voltage read-back = %d mv (0x%02x = 0x%02x)\n",
+					(type == LDO) ? "LDO" : "NCP",
+					*voltage_mv, offset, val);
+
+	return rc;
+}
+
+static int qpnp_lcdb_set_soft_start(struct qpnp_lcdb *lcdb,
+					u32 ss_us, u8 type)
+{
+	int rc = 0, i = 0;
+	u16 offset = LCDB_LDO_SOFT_START_CTL_REG;
+	u8 val = 0;
+
+	if (type == NCP)
+		offset = LCDB_NCP_SOFT_START_CTL_REG;
+
+	if (!is_between(ss_us, MIN_SOFT_START_US, MAX_SOFT_START_US)) {
+		pr_err("Invalid soft_start_us %d (min=%d max=%d)\n",
+			ss_us, MIN_SOFT_START_US, MAX_SOFT_START_US);
+		return -EINVAL;
+	}
+
+	i = 0;
+	while (ss_us > soft_start_us[i])
+		i++;
+	val = ((i == 0) ? 0 : i - 1) & SOFT_START_MASK;
+
+	rc = qpnp_lcdb_masked_write(lcdb,
+			lcdb->base + offset, SOFT_START_MASK, val);
+	if (rc < 0)
+		pr_err("Failed to write %s soft-start time %d rc=%d",
+			(type == LDO) ? "LDO" : "NCP", soft_start_us[i], rc);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ldo_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	mutex_lock(&lcdb->lcdb_mutex);
+	rc = qpnp_lcdb_enable(lcdb);
+	if (rc < 0)
+		pr_err("Failed to enable lcdb rc=%d\n", rc);
+	mutex_unlock(&lcdb->lcdb_mutex);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ldo_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	mutex_lock(&lcdb->lcdb_mutex);
+	rc = qpnp_lcdb_disable(lcdb);
+	if (rc < 0)
+		pr_err("Failed to disable lcdb rc=%d\n", rc);
+	mutex_unlock(&lcdb->lcdb_mutex);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ldo_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	return lcdb->lcdb_enabled;
+}
+
+static int qpnp_lcdb_ldo_regulator_set_voltage(struct regulator_dev *rdev,
+				int min_uV, int max_uV, unsigned int *selector)
+{
+	int rc = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	rc = qpnp_lcdb_set_voltage(lcdb, min_uV / 1000, LDO);
+	if (rc < 0)
+		pr_err("Failed to set LDO voltage rc=%c\n", rc);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ldo_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	u32 voltage_mv = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	rc = qpnp_lcdb_get_voltage(lcdb, &voltage_mv, LDO);
+	if (rc < 0) {
+		pr_err("Failed to get ldo voltage rc=%d\n", rc);
+		return rc;
+	}
+
+	return voltage_mv * 1000;
+}
+
+static struct regulator_ops qpnp_lcdb_ldo_ops = {
+	.enable			= qpnp_lcdb_ldo_regulator_enable,
+	.disable		= qpnp_lcdb_ldo_regulator_disable,
+	.is_enabled		= qpnp_lcdb_ldo_regulator_is_enabled,
+	.set_voltage		= qpnp_lcdb_ldo_regulator_set_voltage,
+	.get_voltage		= qpnp_lcdb_ldo_regulator_get_voltage,
+};
+
+static int qpnp_lcdb_ncp_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	mutex_lock(&lcdb->lcdb_mutex);
+	rc = qpnp_lcdb_enable(lcdb);
+	if (rc < 0)
+		pr_err("Failed to enable lcdb rc=%d\n", rc);
+	mutex_unlock(&lcdb->lcdb_mutex);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ncp_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	mutex_lock(&lcdb->lcdb_mutex);
+	rc = qpnp_lcdb_disable(lcdb);
+	if (rc < 0)
+		pr_err("Failed to disable lcdb rc=%d\n", rc);
+	mutex_unlock(&lcdb->lcdb_mutex);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ncp_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	return lcdb->lcdb_enabled;
+}
+
+static int qpnp_lcdb_ncp_regulator_set_voltage(struct regulator_dev *rdev,
+				int min_uV, int max_uV, unsigned int *selector)
+{
+	int rc = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	rc = qpnp_lcdb_set_voltage(lcdb, min_uV / 1000, NCP);
+	if (rc < 0)
+		pr_err("Failed to set LDO voltage rc=%c\n", rc);
+
+	return rc;
+}
+
+static int qpnp_lcdb_ncp_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	int rc;
+	u32 voltage_mv = 0;
+	struct qpnp_lcdb *lcdb  = rdev_get_drvdata(rdev);
+
+	rc = qpnp_lcdb_get_voltage(lcdb, &voltage_mv, NCP);
+	if (rc < 0) {
+		pr_err("Failed to get ncp voltage rc=%d\n", rc);
+		return rc;
+	}
+
+	return voltage_mv * 1000;
+}
+
+static struct regulator_ops qpnp_lcdb_ncp_ops = {
+	.enable			= qpnp_lcdb_ncp_regulator_enable,
+	.disable		= qpnp_lcdb_ncp_regulator_disable,
+	.is_enabled		= qpnp_lcdb_ncp_regulator_is_enabled,
+	.set_voltage		= qpnp_lcdb_ncp_regulator_set_voltage,
+	.get_voltage		= qpnp_lcdb_ncp_regulator_get_voltage,
+};
+
+static int qpnp_lcdb_regulator_register(struct qpnp_lcdb *lcdb, u8 type)
+{
+	int rc = 0;
+	struct regulator_init_data *init_data;
+	struct regulator_config cfg = {};
+	struct regulator_desc *rdesc;
+	struct regulator_dev *rdev;
+	struct device_node *node;
+
+	if (type == LDO) {
+		node			= lcdb->ldo.node;
+		rdesc			= &lcdb->ldo.rdesc;
+		rdesc->ops		= &qpnp_lcdb_ldo_ops;
+		rdev			= lcdb->ldo.rdev;
+	} else if (type == NCP) {
+		node			= lcdb->ncp.node;
+		rdesc			= &lcdb->ncp.rdesc;
+		rdesc->ops		= &qpnp_lcdb_ncp_ops;
+		rdev			= lcdb->ncp.rdev;
+	} else {
+		pr_err("Invalid regulator type %d\n", type);
+		return -EINVAL;
+	}
+
+	init_data = of_get_regulator_init_data(lcdb->dev, node, rdesc);
+	if (!init_data) {
+		pr_err("Failed to get regulator_init_data for %s\n",
+					(type == LDO) ? "LDO" : "NCP");
+		return -ENOMEM;
+	}
+
+	if (init_data->constraints.name) {
+		rdesc->owner		= THIS_MODULE;
+		rdesc->type		= REGULATOR_VOLTAGE;
+		rdesc->name		= init_data->constraints.name;
+
+		cfg.dev = lcdb->dev;
+		cfg.init_data = init_data;
+		cfg.driver_data = lcdb;
+		cfg.of_node = node;
+
+		if (of_get_property(lcdb->dev->of_node, "parent-supply", NULL))
+			init_data->supply_regulator = "parent";
+
+		init_data->constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_VOLTAGE
+				| REGULATOR_CHANGE_STATUS;
+
+		rdev = devm_regulator_register(lcdb->dev, rdesc, &cfg);
+		if (IS_ERR(rdev)) {
+			rc = PTR_ERR(rdev);
+			rdev = NULL;
+			pr_err("Failed to register lcdb_%s regulator rc = %d\n",
+				(type == LDO) ? "LDO" : "NCP", rc);
+			return rc;
+		}
+	} else {
+		pr_err("%s_regulator name missing\n",
+				(type == LDO) ? "LDO" : "NCP");
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int qpnp_lcdb_parse_ttw(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	u32 temp;
+	u8 val = 0;
+	struct device_node *node = lcdb->dev->of_node;
+
+	if (of_property_read_bool(node, "qcom,ttw-mode-sw")) {
+		lcdb->ttw_mode_sw = true;
+		rc = of_property_read_u32(node, "qcom,attw-toff-ms", &temp);
+		if (!rc) {
+			if (!is_between(temp, ATTW_MIN_MS, ATTW_MAX_MS)) {
+				pr_err("Invalid TOFF val %d (min=%d max=%d)\n",
+					temp, ATTW_MIN_MS, ATTW_MAX_MS);
+					return -EINVAL;
+			}
+			val = ilog2(temp / 4) << ATTW_TOFF_TIME_SHIFT;
+		} else {
+			pr_err("qcom,attw-toff-ms not specified for TTW SW mode\n");
+			return rc;
+		}
+
+		rc = of_property_read_u32(node, "qcom,attw-ton-ms", &temp);
+		if (!rc) {
+			if (!is_between(temp, ATTW_MIN_MS, ATTW_MAX_MS)) {
+				pr_err("Invalid TON value %d (min=%d max=%d)\n",
+					temp, ATTW_MIN_MS, ATTW_MAX_MS);
+				return -EINVAL;
+			}
+			val |= ilog2(temp / 4);
+		} else {
+			pr_err("qcom,attw-ton-ms not specified for TTW SW mode\n");
+			return rc;
+		}
+		rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_AUTO_TOUCH_WAKE_CTL_REG,
+				ATTW_TON_TIME_MASK | ATTW_TOFF_TIME_MASK, val);
+		if (rc < 0) {
+			pr_err("Failed to write ATTW ON/OFF rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_lcdb_ldo_dt_init(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	struct device_node *node = lcdb->ldo.node;
+
+	/* LDO output voltage */
+	lcdb->ldo.voltage_mv = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,ldo-voltage-mv",
+					&lcdb->ldo.voltage_mv);
+	if (!rc && !is_between(lcdb->ldo.voltage_mv, MIN_VOLTAGE_MV,
+						MAX_VOLTAGE_MV)) {
+		pr_err("Invalid LDO voltage %dmv (min=%d max=%d)\n",
+			lcdb->ldo.voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV);
+		return -EINVAL;
+	}
+
+	/* LDO PD configuration */
+	lcdb->ldo.pd = -EINVAL;
+	of_property_read_u32(node, "qcom,ldo-pd", &lcdb->ldo.pd);
+
+	lcdb->ldo.pd_strength = -EINVAL;
+	of_property_read_u32(node, "qcom,ldo-pd-strength",
+					&lcdb->ldo.pd_strength);
+
+	/* LDO ILIM configuration */
+	lcdb->ldo.ilim_ma = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,ldo-ilim-ma", &lcdb->ldo.ilim_ma);
+	if (!rc && !is_between(lcdb->ldo.ilim_ma, MIN_LDO_ILIM_MA,
+						MAX_LDO_ILIM_MA)) {
+		pr_err("Invalid ilim_ma %d (min=%d, max=%d)\n",
+			lcdb->ldo.ilim_ma, MIN_LDO_ILIM_MA,
+					MAX_LDO_ILIM_MA);
+		return -EINVAL;
+	}
+
+	/* LDO soft-start (SS) configuration */
+	lcdb->ldo.soft_start_us = -EINVAL;
+	of_property_read_u32(node, "qcom,ldo-soft-start-us",
+					&lcdb->ldo.soft_start_us);
+
+	return 0;
+}
+
+static int qpnp_lcdb_ncp_dt_init(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	struct device_node *node = lcdb->ncp.node;
+
+	/* NCP output voltage */
+	lcdb->ncp.voltage_mv = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,ncp-voltage-mv",
+					&lcdb->ncp.voltage_mv);
+	if (!rc && !is_between(lcdb->ncp.voltage_mv, MIN_VOLTAGE_MV,
+						MAX_VOLTAGE_MV)) {
+		pr_err("Invalid NCP voltage %dmv (min=%d max=%d)\n",
+			lcdb->ldo.voltage_mv, MIN_VOLTAGE_MV, MAX_VOLTAGE_MV);
+		return -EINVAL;
+	}
+
+	/* NCP PD configuration */
+	lcdb->ncp.pd = -EINVAL;
+	of_property_read_u32(node, "qcom,ncp-pd", &lcdb->ncp.pd);
+
+	lcdb->ncp.pd_strength = -EINVAL;
+	of_property_read_u32(node, "qcom,ncp-pd-strength",
+					&lcdb->ncp.pd_strength);
+
+	/* NCP ILIM configuration */
+	lcdb->ncp.ilim_ma = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,ncp-ilim-ma", &lcdb->ncp.ilim_ma);
+	if (!rc && !is_between(lcdb->ncp.ilim_ma, MIN_NCP_ILIM_MA,
+						MAX_NCP_ILIM_MA)) {
+		pr_err("Invalid ilim_ma %d (min=%d, max=%d)\n",
+			lcdb->ncp.ilim_ma, MIN_NCP_ILIM_MA, MAX_NCP_ILIM_MA);
+		return -EINVAL;
+	}
+
+	/* NCP soft-start (SS) configuration */
+	lcdb->ncp.soft_start_us = -EINVAL;
+	of_property_read_u32(node, "qcom,ncp-soft-start-us",
+					&lcdb->ncp.soft_start_us);
+
+	return 0;
+}
+
+static int qpnp_lcdb_bst_dt_init(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	struct device_node *node = lcdb->bst.node;
+
+	/* Boost PD  configuration */
+	lcdb->bst.pd = -EINVAL;
+	of_property_read_u32(node, "qcom,bst-pd", &lcdb->bst.pd);
+
+	lcdb->bst.pd_strength = -EINVAL;
+	of_property_read_u32(node, "qcom,bst-pd-strength",
+					&lcdb->bst.pd_strength);
+
+	/* Boost ILIM */
+	lcdb->bst.ilim_ma = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,bst-ilim-ma", &lcdb->bst.ilim_ma);
+	if (!rc && !is_between(lcdb->bst.ilim_ma, MIN_BST_ILIM_MA,
+						MAX_BST_ILIM_MA)) {
+		pr_err("Invalid ilim_ma %d (min=%d, max=%d)\n",
+			lcdb->bst.ilim_ma, MIN_BST_ILIM_MA, MAX_BST_ILIM_MA);
+			return -EINVAL;
+	}
+
+	/* Boost PS configuration */
+	lcdb->bst.ps = -EINVAL;
+	of_property_read_u32(node, "qcom,bst-ps", &lcdb->bst.ps);
+
+	lcdb->bst.ps_threshold = -EINVAL;
+	rc = of_property_read_u32(node, "qcom,bst-ps-threshold-ma",
+					&lcdb->bst.ps_threshold);
+	if (!rc && !is_between(lcdb->bst.ps_threshold,
+				MIN_BST_PS_MA, MAX_BST_PS_MA)) {
+		pr_err("Invalid bst ps_threshold %d (min=%d, max=%d)\n",
+			lcdb->bst.ps_threshold, MIN_BST_PS_MA, MAX_BST_PS_MA);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_lcdb_init_ldo(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0, ilim_ma;
+	u8 val = 0;
+
+	/* configure parameters only if LCDB is disabled */
+	if (!is_lcdb_enabled(lcdb)) {
+		if (lcdb->ldo.voltage_mv != -EINVAL) {
+			rc = qpnp_lcdb_set_voltage(lcdb,
+					lcdb->ldo.voltage_mv, LDO);
+			if (rc < 0) {
+				pr_err("Failed to set voltage rc=%d\n", rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ldo.pd != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_LDO_PD_CTL_REG, LDO_DIS_PULLDOWN_BIT,
+				lcdb->ldo.pd ? 0 : LDO_DIS_PULLDOWN_BIT);
+			if (rc < 0) {
+				pr_err("Failed to configure LDO PD rc=%d\n",
+								rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ldo.pd_strength != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_LDO_PD_CTL_REG, LDO_PD_STRENGTH_BIT,
+				lcdb->ldo.pd_strength ?
+				LDO_PD_STRENGTH_BIT : 0);
+			if (rc < 0) {
+				pr_err("Failed to configure LDO PD strength %s rc=%d",
+						lcdb->ldo.pd_strength ?
+						"(strong)" : "(weak)", rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ldo.ilim_ma != -EINVAL) {
+			ilim_ma = lcdb->ldo.ilim_ma - MIN_LDO_ILIM_MA;
+			ilim_ma /= LDO_ILIM_STEP_MA;
+			val = (ilim_ma & SET_LDO_ILIM_MASK) | EN_LDO_ILIM_BIT;
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+					LCDB_LDO_ILIM_CTL1_REG,
+					SET_LDO_ILIM_MASK | EN_LDO_ILIM_BIT,
+					val);
+			if (rc < 0) {
+				pr_err("Failed to configure LDO ilim_ma (CTL1=%d) rc=%d",
+							val, rc);
+				return rc;
+			}
+
+			val = ilim_ma & SET_LDO_ILIM_MASK;
+			rc = qpnp_lcdb_masked_write(lcdb,
+					lcdb->base + LCDB_LDO_ILIM_CTL2_REG,
+					SET_LDO_ILIM_MASK, val);
+			if (rc < 0) {
+				pr_err("Failed to configure LDO ilim_ma (CTL2=%d) rc=%d",
+							val, rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ldo.soft_start_us != -EINVAL) {
+			rc = qpnp_lcdb_set_soft_start(lcdb,
+					lcdb->ldo.soft_start_us, LDO);
+			if (rc < 0) {
+				pr_err("Failed to set LDO soft_start rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+	}
+
+	rc = qpnp_lcdb_get_voltage(lcdb, &lcdb->ldo.voltage_mv, LDO);
+	if (rc < 0) {
+		pr_err("Failed to get LDO volatge rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base +
+			LCDB_LDO_VREG_OK_CTL_REG, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read ldo_vreg_ok rc=%d\n", rc);
+		return rc;
+	}
+	lcdb->ldo.vreg_ok_dbc_us = dbc_us[val & VREG_OK_DEB_MASK];
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base +
+			LCDB_LDO_SOFT_START_CTL_REG, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read ldo_soft_start_ctl rc=%d\n", rc);
+		return rc;
+	}
+	lcdb->ldo.soft_start_us = soft_start_us[val & SOFT_START_MASK];
+
+	rc = qpnp_lcdb_regulator_register(lcdb, LDO);
+	if (rc < 0)
+		pr_err("Failed to register ldo rc=%d\n", rc);
+
+	return rc;
+}
+
+static int qpnp_lcdb_init_ncp(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0, i = 0;
+	u8 val = 0;
+
+	/* configure parameters only if LCDB is disabled */
+	if (!is_lcdb_enabled(lcdb)) {
+		if (lcdb->ncp.voltage_mv != -EINVAL) {
+			rc = qpnp_lcdb_set_voltage(lcdb,
+					lcdb->ncp.voltage_mv, NCP);
+			if (rc < 0) {
+				pr_err("Failed to set voltage rc=%d\n", rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ncp.pd != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_NCP_PD_CTL_REG, NCP_DIS_PULLDOWN_BIT,
+				lcdb->ncp.pd ? 0 : NCP_DIS_PULLDOWN_BIT);
+			if (rc < 0) {
+				pr_err("Failed to configure NCP PD rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ncp.pd_strength != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_NCP_PD_CTL_REG, NCP_PD_STRENGTH_BIT,
+				lcdb->ncp.pd_strength ?
+				NCP_PD_STRENGTH_BIT : 0);
+			if (rc < 0) {
+				pr_err("Failed to configure NCP PD strength %s rc=%d",
+					lcdb->ncp.pd_strength ?
+					"(strong)" : "(weak)", rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ncp.ilim_ma != -EINVAL) {
+			while (lcdb->ncp.ilim_ma > ncp_ilim_ma[i])
+				i++;
+			val = (i == 0) ? 0 : i - 1;
+			val = (lcdb->ncp.ilim_ma & SET_NCP_ILIM_MASK) |
+							EN_NCP_ILIM_BIT;
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+						LCDB_NCP_ILIM_CTL1_REG,
+				SET_NCP_ILIM_MASK | EN_NCP_ILIM_BIT, val);
+			if (rc < 0) {
+				pr_err("Failed to configure NCP ilim_ma (CTL1=%d) rc=%d",
+								val, rc);
+				return rc;
+			}
+			val = lcdb->ncp.ilim_ma & SET_NCP_ILIM_MASK;
+			rc = qpnp_lcdb_masked_write(lcdb,
+					lcdb->base + LCDB_NCP_ILIM_CTL2_REG,
+					SET_NCP_ILIM_MASK, val);
+			if (rc < 0) {
+				pr_err("Failed to configure NCP ilim_ma (CTL2=%d) rc=%d",
+							val, rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->ncp.soft_start_us != -EINVAL) {
+			rc = qpnp_lcdb_set_soft_start(lcdb,
+				lcdb->ncp.soft_start_us, NCP);
+			if (rc < 0) {
+				pr_err("Failed to set NCP soft_start rc=%d\n",
+								rc);
+				return rc;
+			}
+		}
+	}
+
+	rc = qpnp_lcdb_get_voltage(lcdb, &lcdb->ncp.voltage_mv, NCP);
+	if (rc < 0) {
+		pr_err("Failed to get NCP volatge rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base +
+			LCDB_NCP_VREG_OK_CTL_REG, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read ncp_vreg_ok rc=%d\n", rc);
+		return rc;
+	}
+	lcdb->ncp.vreg_ok_dbc_us = dbc_us[val & VREG_OK_DEB_MASK];
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base +
+			LCDB_NCP_SOFT_START_CTL_REG, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read ncp_soft_start_ctl rc=%d\n", rc);
+		return rc;
+	}
+	lcdb->ncp.soft_start_us = soft_start_us[val & SOFT_START_MASK];
+
+	rc = qpnp_lcdb_regulator_register(lcdb, NCP);
+	if (rc < 0)
+		pr_err("Failed to register NCP rc=%d\n", rc);
+
+	return rc;
+}
+
+static int qpnp_lcdb_init_bst(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	u8 val = 0;
+
+	/* configure parameters only if LCDB is disabled */
+	if (!is_lcdb_enabled(lcdb)) {
+		if (lcdb->bst.pd != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_BST_PD_CTL_REG, BOOST_DIS_PULLDOWN_BIT,
+				lcdb->bst.pd ? 0 : BOOST_DIS_PULLDOWN_BIT);
+			if (rc < 0) {
+				pr_err("Failed to configure BST PD rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->bst.pd_strength != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_NCP_PD_CTL_REG, BOOST_PD_STRENGTH_BIT,
+				lcdb->bst.pd_strength ?
+				BOOST_PD_STRENGTH_BIT : 0);
+			if (rc < 0) {
+				pr_err("Failed to configure NCP PD strength %s rc=%d",
+					lcdb->bst.pd_strength ?
+					"(strong)" : "(weak)", rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->bst.ilim_ma != -EINVAL) {
+			val = (lcdb->bst.ilim_ma / MIN_BST_ILIM_MA) - 1;
+			val = (lcdb->bst.ilim_ma & SET_BST_ILIM_MASK) |
+							EN_BST_ILIM_BIT;
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_BST_ILIM_CTL_REG,
+				SET_BST_ILIM_MASK | EN_BST_ILIM_BIT, val);
+			if (rc < 0) {
+				pr_err("Failed to configure BST ilim_ma rc=%d",
+									rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->bst.ps != -EINVAL) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+					LCDB_PS_CTL_REG, EN_PS_BIT,
+					&lcdb->bst.ps ? EN_PS_BIT : 0);
+			if (rc < 0) {
+				pr_err("Failed to disable BST PS rc=%d", rc);
+				return rc;
+			}
+		}
+
+		if (lcdb->bst.ps_threshold != -EINVAL) {
+			val = (lcdb->bst.ps_threshold - MIN_BST_PS_MA) / 10;
+			val = (lcdb->bst.ps_threshold & PS_THRESHOLD_MASK) |
+								EN_PS_BIT;
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+						LCDB_PS_CTL_REG,
+						PS_THRESHOLD_MASK | EN_PS_BIT,
+						val);
+			if (rc < 0) {
+				pr_err("Failed to configure BST PS threshold rc=%d",
+								rc);
+				return rc;
+			}
+		}
+	}
+
+	rc = qpnp_lcdb_get_voltage(lcdb, &lcdb->bst.voltage_mv, BST);
+	if (rc < 0) {
+		pr_err("Failed to get BST volatge rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base +
+			LCDB_BST_VREG_OK_CTL_REG, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read bst_vreg_ok rc=%d\n", rc);
+		return rc;
+	}
+	lcdb->bst.vreg_ok_dbc_us = dbc_us[val & VREG_OK_DEB_MASK];
+
+	rc = qpnp_lcdb_read(lcdb, lcdb->base +
+			LCDB_SOFT_START_CTL_REG, &val, 1);
+	if (rc < 0) {
+		pr_err("Failed to read ncp_soft_start_ctl rc=%d\n", rc);
+		return rc;
+	}
+	lcdb->bst.soft_start_us = (val & SOFT_START_MASK) * 200	+ 200;
+
+	return 0;
+}
+
+static int qpnp_lcdb_hw_init(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	u8 val = 0;
+
+	rc = qpnp_lcdb_init_bst(lcdb);
+	if (rc < 0) {
+		pr_err("Failed to initialize BOOST rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_lcdb_init_ldo(lcdb);
+	if (rc < 0) {
+		pr_err("Failed to initialize LDO rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_lcdb_init_ncp(lcdb);
+	if (rc < 0) {
+		pr_err("Failed to initialize NCP rc=%d\n", rc);
+		return rc;
+	}
+
+	if (!is_lcdb_enabled(lcdb)) {
+		rc = qpnp_lcdb_read(lcdb, lcdb->base +
+				LCDB_MODULE_RDY_REG, &val, 1);
+		if (rc < 0) {
+			pr_err("Failed to read MODULE_RDY rc=%d\n", rc);
+			return rc;
+		}
+		if (!(val & MODULE_RDY_BIT)) {
+			rc = qpnp_lcdb_masked_write(lcdb, lcdb->base +
+				LCDB_MODULE_RDY_REG, MODULE_RDY_BIT,
+						MODULE_RDY_BIT);
+			if (rc < 0) {
+				pr_err("Failed to set MODULE RDY rc=%d\n", rc);
+				return rc;
+			}
+		}
+	} else {
+		/* module already enabled */
+		lcdb->lcdb_enabled = true;
+	}
+
+	return 0;
+}
+
+static int qpnp_lcdb_parse_dt(struct qpnp_lcdb *lcdb)
+{
+	int rc = 0;
+	const char *label;
+	struct device_node *temp, *node = lcdb->dev->of_node;
+
+	for_each_available_child_of_node(node, temp) {
+		rc = of_property_read_string(temp, "label", &label);
+		if (rc < 0) {
+			pr_err("Failed to read label rc=%d\n", rc);
+			return rc;
+		}
+
+		if (!strcmp(label, "ldo")) {
+			lcdb->ldo.node = temp;
+			rc = qpnp_lcdb_ldo_dt_init(lcdb);
+		} else if (!strcmp(label, "ncp")) {
+			lcdb->ncp.node = temp;
+			rc = qpnp_lcdb_ncp_dt_init(lcdb);
+		} else if (!strcmp(label, "bst")) {
+			lcdb->bst.node = temp;
+			rc = qpnp_lcdb_bst_dt_init(lcdb);
+		} else {
+			pr_err("Failed to identify label %s\n", label);
+			return -EINVAL;
+		}
+		if (rc < 0) {
+			pr_err("Failed to register %s module\n", label);
+			return rc;
+		}
+	}
+
+	if (of_property_read_bool(node, "qcom,ttw-enable")) {
+		rc = qpnp_lcdb_parse_ttw(lcdb);
+		if (rc < 0) {
+			pr_err("Failed to parse ttw-params rc=%d\n", rc);
+			return rc;
+		}
+		lcdb->ttw_enable = true;
+	}
+
+	return rc;
+}
+
+static int qpnp_lcdb_regulator_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct device_node *node;
+	struct qpnp_lcdb *lcdb;
+
+	node = pdev->dev.of_node;
+	if (!node) {
+		pr_err("No nodes defined\n");
+		return -ENODEV;
+	}
+
+	lcdb = devm_kzalloc(&pdev->dev, sizeof(*lcdb), GFP_KERNEL);
+	if (!lcdb)
+		return -ENOMEM;
+
+	rc = of_property_read_u32(node, "reg", &lcdb->base);
+	if (rc < 0) {
+		pr_err("Failed to find reg node rc=%d\n", rc);
+		return rc;
+	}
+
+	lcdb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!lcdb->regmap) {
+		pr_err("Failed to get the regmap handle rc=%d\n", rc);
+		return -EINVAL;
+	}
+
+	lcdb->dev = &pdev->dev;
+	lcdb->pdev = pdev;
+	mutex_init(&lcdb->lcdb_mutex);
+	mutex_init(&lcdb->read_write_mutex);
+
+	rc = qpnp_lcdb_parse_dt(lcdb);
+	if (rc < 0) {
+		pr_err("Failed to parse dt rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_lcdb_hw_init(lcdb);
+	if (rc < 0)
+		pr_err("Failed to initialize LCDB module rc=%d\n", rc);
+	else
+		pr_info("LCDB module successfully registered! lcdb_en=%d ldo_voltage=%dmV ncp_voltage=%dmV bst_voltage=%dmV\n",
+			lcdb->lcdb_enabled, lcdb->ldo.voltage_mv,
+			lcdb->ncp.voltage_mv, lcdb->bst.voltage_mv);
+
+	return rc;
+}
+
+static int qpnp_lcdb_regulator_remove(struct platform_device *pdev)
+{
+	struct qpnp_lcdb *lcdb = dev_get_drvdata(&pdev->dev);
+
+	mutex_destroy(&lcdb->lcdb_mutex);
+	mutex_destroy(&lcdb->read_write_mutex);
+
+	return 0;
+}
+
+static const struct of_device_id lcdb_match_table[] = {
+	{ .compatible = QPNP_LCDB_REGULATOR_DRIVER_NAME, },
+	{ },
+};
+
+static struct platform_driver qpnp_lcdb_regulator_driver = {
+	.driver		= {
+		.name		= QPNP_LCDB_REGULATOR_DRIVER_NAME,
+		.of_match_table	= lcdb_match_table,
+	},
+	.probe		= qpnp_lcdb_regulator_probe,
+	.remove		= qpnp_lcdb_regulator_remove,
+};
+
+static int __init qpnp_lcdb_regulator_init(void)
+{
+	return platform_driver_register(&qpnp_lcdb_regulator_driver);
+}
+arch_initcall(qpnp_lcdb_regulator_init);
+
+static void __exit qpnp_lcdb_regulator_exit(void)
+{
+	platform_driver_unregister(&qpnp_lcdb_regulator_driver);
+}
+module_exit(qpnp_lcdb_regulator_exit);
+
+MODULE_DESCRIPTION("QPNP LCDB regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/qpnp-oledb-regulator.c b/drivers/regulator/qpnp-oledb-regulator.c
new file mode 100644
index 0000000..8d017fb
--- /dev/null
+++ b/drivers/regulator/qpnp-oledb-regulator.c
@@ -0,0 +1,1201 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt)	"OLEDB: %s: " fmt, __func__
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#define QPNP_OLEDB_REGULATOR_DRIVER_NAME	"qcom,qpnp-oledb-regulator"
+#define OLEDB_VOUT_STEP_MV				100
+#define OLEDB_VOUT_MIN_MV				5000
+#define OLEDB_VOUT_MAX_MV				8100
+#define OLEDB_VOUT_HW_DEFAULT				6400
+
+#define OLEDB_MODULE_RDY				0x45
+#define OLEDB_MODULE_RDY_BIT				BIT(7)
+
+#define OLEDB_MODULE_ENABLE				0x46
+#define OLEDB_MODULE_ENABLE_BIT				BIT(7)
+
+#define OLEDB_EXT_PIN_CTL				0x47
+#define OLEDB_EXT_PIN_CTL_BIT				BIT(7)
+
+#define OLEDB_SWIRE_CONTROL				0x48
+#define OLEDB_EN_SWIRE_VOUT_UPD_BIT			BIT(6)
+#define OLEDB_EN_SWIRE_PD_UPD_BIT			BIT(7)
+
+#define OLEDB_VOUT_PGM					0x49
+#define OLEDB_VOUT_PGM_MASK				GENMASK(4, 0)
+
+#define OLEDB_VOUT_DEFAULT				0x4A
+#define OLEDB_VOUT_DEFAULT_MASK				GENMASK(4, 0)
+
+#define OLEDB_PD_CTL					0x4B
+
+#define OLEDB_ILIM_NFET					0x4E
+#define OLEDB_ILIMIT_NFET_MASK				GENMASK(2, 0)
+
+#define OLEDB_BIAS_GEN_WARMUP_DELAY			0x52
+#define OLEDB_BIAS_GEN_WARMUP_DELAY_MASK		GENMASK(1, 0)
+
+#define OLEDB_SHORT_PROTECT				0x59
+#define OLEDB_ENABLE_SC_DETECTION_BIT			BIT(7)
+#define OLEDB_DBNC_SHORT_DETECTION_MASK			GENMASK(1, 0)
+
+#define OLEDB_FAST_PRECHARGE				0x5A
+#define OLEDB_FAST_PRECHG_PPULSE_EN_BIT			BIT(7)
+#define OLEDB_DBNC_PRECHARGE_MASK			GENMASK(5, 4)
+#define OLEDB_DBNC_PRECHARGE_SHIFT			4
+#define OLEDB_PRECHARGE_PULSE_PERIOD_MASK		GENMASK(3, 2)
+#define OLEDB_PRECHARGE_PULSE_PERIOD_SHIFT		2
+#define OLEDB_PRECHARGE_PULSE_TON_MASK			GENMASK(1, 0)
+
+#define OLEDB_EN_PSM					0x5B
+#define OLEDB_PSM_ENABLE_BIT				BIT(7)
+
+#define OLEDB_PSM_CTL					0x5C
+#define OLEDB_PSM_HYSTERYSIS_CTL_BIT			BIT(3)
+#define OLEDB_PSM_HYSTERYSIS_CTL_BIT_SHIFT		3
+#define OLEDB_VREF_PSM_MASK				GENMASK(2, 0)
+
+#define OLEDB_PFM_CTL					0x5D
+#define OLEDB_PFM_ENABLE_BIT				BIT(7)
+#define OLEDB_PFM_HYSTERYSIS_CTRL_BIT_MASK		BIT(4)
+#define OLEDB_PFM_HYSTERYSIS_CTL_BIT_SHIFT		4
+#define OLEDB_PFM_CURR_LIMIT_MASK			GENMASK(3, 2)
+#define OLEDB_PFM_CURR_LIMIT_SHIFT			2
+#define OLEDB_PFM_OFF_TIME_NS_MASK			GENMASK(1, 0)
+
+#define OLEDB_NLIMIT					0x64
+#define OLEDB_ENABLE_NLIMIT_BIT				BIT(7)
+#define OLEDB_ENABLE_NLIMIT_BIT_SHIFT			7
+#define OLEDB_NLIMIT_PGM_MASK				GENMASK(1, 0)
+
+#define OLEDB_PSM_HYS_CTRL_MIN				13
+#define OLEDB_PSM_HYS_CTRL_MAX				26
+
+#define OLEDB_PFM_HYS_CTRL_MIN				13
+#define OLEDB_PFM_HYS_CTRL_MAX				26
+
+#define OLEDB_PFM_OFF_TIME_MIN				110
+#define OLEDB_PFM_OFF_TIME_MAX				480
+
+#define OLEDB_PRECHG_TIME_MIN				1
+#define OLEDB_PRECHG_TIME_MAX				8
+
+#define OLEDB_PRECHG_PULSE_PERIOD_MIN			3
+#define OLEDB_PRECHG_PULSE_PERIOD_MAX			12
+
+#define OLEDB_MIN_SC_DBNC_TIME_FSW			2
+#define OLEDB_MAX_SC_DBNC_TIME_FSW			16
+
+#define OLEDB_PRECHG_PULSE_ON_TIME_MIN			1200
+#define OLEDB_PRECHG_PULSE_ON_TIME_MAX			3000
+
+#define PSM_HYSTERYSIS_MV_TO_VAL(val_mv)		((val_mv/13) - 1)
+#define PFM_HYSTERYSIS_MV_TO_VAL(val_mv)		((val_mv/13) - 1)
+#define PFM_OFF_TIME_NS_TO_VAL(val_ns)			((val_ns/110) - 1)
+#define PRECHG_DEBOUNCE_TIME_MS_TO_VAL(val_ms)		((val_ms/2) - \
+								(val_ms/8))
+#define PRECHG_PULSE_PERIOD_US_TO_VAL(val_us)		((val_us/3) - 1)
+#define PRECHG_PULSE_ON_TIME_NS_TO_VAL(val_ns)		(val_ns/600 - 2)
+#define SHORT_CIRCUIT_DEBOUNCE_TIME_TO_VAL(val)		((val/4) - (val/16))
+
+struct qpnp_oledb_psm_ctl {
+	int psm_enable;
+	int   psm_hys_ctl;
+	int  psm_vref;
+};
+
+struct qpnp_oledb_pfm_ctl {
+	int pfm_enable;
+	int pfm_hys_ctl;
+	int pfm_curr_limit;
+	int pfm_off_time;
+};
+
+struct qpnp_oledb_fast_precharge_ctl {
+	int fast_prechg_ppulse_en;
+	int prechg_debounce_time;
+	int prechg_pulse_period;
+	int prechg_pulse_on_time;
+};
+
+struct qpnp_oledb {
+	struct platform_device			*pdev;
+	struct device				*dev;
+	struct regmap				*regmap;
+	struct regulator_desc			rdesc;
+	struct regulator_dev			*rdev;
+	struct qpnp_oledb_psm_ctl		psm_ctl;
+	struct qpnp_oledb_pfm_ctl		pfm_ctl;
+	struct qpnp_oledb_fast_precharge_ctl	fast_prechg_ctl;
+
+	u32					base;
+	u8					mod_enable;
+	u8					ext_pinctl_state;
+	int					current_voltage;
+	int					default_voltage;
+	int					vout_mv;
+	int					warmup_delay;
+	int					peak_curr_limit;
+	int					pd_ctl;
+	int					negative_curr_limit;
+	int					nlimit_enable;
+	int					sc_en;
+	int					sc_dbnc_time;
+	bool					swire_control;
+	bool					ext_pin_control;
+	bool					dynamic_ext_pinctl_config;
+	bool					pbs_control;
+};
+
+static const u16 oledb_warmup_dly_ns[] = {6700, 13300, 26700, 53400};
+static const u16 oledb_peak_curr_limit_ma[] = {115, 265, 415, 570,
+					      720, 870, 1020, 1170};
+static const u16 oledb_psm_vref_mv[] = {440, 510, 580, 650, 715,
+					 780, 850, 920};
+static const u16 oledb_pfm_curr_limit_ma[] = {130, 200, 270, 340};
+static const u16 oledb_nlimit_ma[] = {170, 300, 420, 550};
+
+static int qpnp_oledb_read(struct qpnp_oledb *oledb, u32 address,
+					u8 *val, int count)
+{
+	int rc = 0;
+	struct platform_device *pdev = oledb->pdev;
+
+	rc = regmap_bulk_read(oledb->regmap, address, val, count);
+	if (rc)
+		pr_err("Failed to read address=0x%02x sid=0x%02x rc=%d\n",
+			address, to_spmi_device(pdev->dev.parent)->usid, rc);
+
+	return rc;
+}
+
+static int qpnp_oledb_masked_write(struct qpnp_oledb *oledb,
+						u32 address, u8 mask, u8 val)
+{
+	int rc;
+
+	rc = regmap_update_bits(oledb->regmap, address, mask, val);
+	if (rc < 0)
+		pr_err("Failed to write address 0x%04X, rc = %d\n",
+					address, rc);
+	else
+		pr_debug("Wrote 0x%02X to addr 0x%04X\n",
+			val, address);
+
+	return rc;
+}
+
+static int qpnp_oledb_write(struct qpnp_oledb *oledb, u16 address, u8 *val,
+				int count)
+{
+	int rc = 0;
+	struct platform_device *pdev = oledb->pdev;
+
+	rc = regmap_bulk_write(oledb->regmap, address, val, count);
+	if (rc)
+		pr_err("Failed to write address=0x%02x sid=0x%02x rc=%d\n",
+			address, to_spmi_device(pdev->dev.parent)->usid, rc);
+	else
+		pr_debug("Wrote 0x%02X to addr 0x%04X\n",
+			*val, address);
+
+	return 0;
+}
+
+static int qpnp_oledb_regulator_enable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+	u8 val = 0;
+
+	struct qpnp_oledb *oledb  = rdev_get_drvdata(rdev);
+
+	if (oledb->ext_pin_control) {
+		rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
+								 &val, 1);
+		if (rc < 0) {
+			pr_err("Failed to read EXT_PIN_CTL rc=%d\n", rc);
+			return rc;
+		}
+
+		/*
+		 * Enable ext-pin-ctl after display-supply is turned on.
+		 * This is to avoid glitches on the external pin.
+		 */
+		if (!(val & OLEDB_EXT_PIN_CTL_BIT) &&
+					oledb->dynamic_ext_pinctl_config) {
+			val = OLEDB_EXT_PIN_CTL_BIT;
+			rc = qpnp_oledb_write(oledb, oledb->base +
+					OLEDB_EXT_PIN_CTL, &val, 1);
+			if (rc < 0) {
+				pr_err("Failed to write EXT_PIN_CTL rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+		pr_debug("ext-pin-ctrl mode enabled\n");
+	} else {
+		val = OLEDB_MODULE_ENABLE_BIT;
+		rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_MODULE_ENABLE,
+					&val, 1);
+		if (rc < 0) {
+			pr_err("Failed to write MODULE_ENABLE rc=%d\n", rc);
+			return rc;
+		}
+
+		ndelay(oledb->warmup_delay);
+		pr_debug("register-control mode, module enabled\n");
+	}
+
+	oledb->mod_enable = true;
+	if (oledb->pbs_control) {
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+			OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT |
+					OLEDB_EN_SWIRE_VOUT_UPD_BIT, 0);
+		if (rc < 0)
+			pr_err("Failed to write SWIRE_CTL for pbs mode rc=%d\n",
+									 rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_oledb_regulator_disable(struct regulator_dev *rdev)
+{
+	int rc = 0;
+
+	struct qpnp_oledb *oledb  = rdev_get_drvdata(rdev);
+
+	/*
+	 * Disable ext-pin-ctl after display-supply is turned off. This is to
+	 * avoid glitches on the external pin.
+	 */
+	if (oledb->ext_pin_control) {
+		if (oledb->dynamic_ext_pinctl_config) {
+			rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				 OLEDB_EXT_PIN_CTL, OLEDB_EXT_PIN_CTL_BIT, 0);
+			if (rc < 0) {
+				pr_err("Failed to write EXT_PIN_CTL rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+		pr_debug("ext-pin-ctrl mode disabled\n");
+	} else {
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+					OLEDB_MODULE_ENABLE,
+					OLEDB_MODULE_ENABLE_BIT, 0);
+		if (rc < 0) {
+			pr_err("Failed to write MODULE_ENABLE rc=%d\n", rc);
+			return rc;
+		}
+		pr_debug("Register-control mode, module disabled\n");
+	}
+
+	oledb->mod_enable = false;
+
+	return rc;
+}
+
+static int qpnp_oledb_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_oledb *oledb  = rdev_get_drvdata(rdev);
+
+	return oledb->mod_enable;
+}
+
+static int qpnp_oledb_regulator_set_voltage(struct regulator_dev *rdev,
+				int min_uV, int max_uV, unsigned int *selector)
+{
+	u8 val;
+	int rc = 0;
+
+	struct qpnp_oledb *oledb = rdev_get_drvdata(rdev);
+
+	if (oledb->swire_control)
+		return 0;
+
+	val = DIV_ROUND_UP(min_uV - OLEDB_VOUT_MIN_MV, OLEDB_VOUT_STEP_MV);
+
+	rc = qpnp_oledb_write(oledb, oledb->base + OLEDB_VOUT_PGM,
+					&val, 1);
+	if (rc < 0) {
+		pr_err("Failed to write VOUT_PGM rc=%d\n", rc);
+		return rc;
+	}
+
+	oledb->current_voltage = min_uV;
+	pr_debug("register-control mode, current voltage %d\n",
+						oledb->current_voltage);
+
+	return 0;
+}
+
+static int qpnp_oledb_regulator_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_oledb *oledb  = rdev_get_drvdata(rdev);
+
+	if (oledb->swire_control)
+		return 0;
+
+	return oledb->current_voltage;
+}
+
+static struct regulator_ops qpnp_oledb_ops = {
+	.enable			= qpnp_oledb_regulator_enable,
+	.disable		= qpnp_oledb_regulator_disable,
+	.is_enabled		= qpnp_oledb_regulator_is_enabled,
+	.set_voltage		= qpnp_oledb_regulator_set_voltage,
+	.get_voltage		= qpnp_oledb_regulator_get_voltage,
+};
+
+static int qpnp_oledb_register_regulator(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	struct platform_device *pdev = oledb->pdev;
+	struct regulator_init_data *init_data;
+	struct regulator_desc *rdesc = &oledb->rdesc;
+	struct regulator_config cfg = {};
+
+	init_data = of_get_regulator_init_data(&pdev->dev,
+						pdev->dev.of_node, rdesc);
+	if (!init_data) {
+		pr_err("Unable to get OLEDB regulator init data\n");
+		return -ENOMEM;
+	}
+
+	if (init_data->constraints.name) {
+		rdesc->owner		= THIS_MODULE;
+		rdesc->type		= REGULATOR_VOLTAGE;
+		rdesc->ops		= &qpnp_oledb_ops;
+		rdesc->name		= init_data->constraints.name;
+
+		cfg.dev = &pdev->dev;
+		cfg.init_data = init_data;
+		cfg.driver_data = oledb;
+		cfg.of_node = pdev->dev.of_node;
+
+		if (of_get_property(pdev->dev.of_node, "parent-supply",
+				 NULL))
+			init_data->supply_regulator = "parent";
+
+		init_data->constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_VOLTAGE |
+					REGULATOR_CHANGE_STATUS;
+
+		oledb->rdev = devm_regulator_register(oledb->dev, rdesc, &cfg);
+		if (IS_ERR(oledb->rdev)) {
+			rc = PTR_ERR(oledb->rdev);
+			oledb->rdev = NULL;
+			pr_err("Unable to register OLEDB regulator, rc = %d\n",
+				rc);
+			return rc;
+		}
+	} else {
+		pr_err("OLEDB regulator name missing\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_oledb_get_curr_voltage(struct qpnp_oledb *oledb,
+					u16 *current_voltage)
+{
+	int rc = 0;
+	u8 val;
+
+	if (!(oledb->mod_enable || oledb->ext_pinctl_state)) {
+		rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_VOUT_DEFAULT,
+						&val, 1);
+		if (rc < 0) {
+			pr_err("Failed to read VOUT_DEFAULT rc=%d\n", rc);
+			return rc;
+		}
+	} else {
+		rc = qpnp_oledb_read(oledb, oledb->base +
+					OLEDB_VOUT_PGM, &val, 1);
+		if (rc < 0) {
+			pr_err("Failed to read VOUT_PGM rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	*current_voltage = (val * OLEDB_VOUT_STEP_MV) + OLEDB_VOUT_MIN_MV;
+
+	return rc;
+}
+
+static int qpnp_oledb_init_nlimit(struct qpnp_oledb *oledb)
+{
+	int rc = 0, i = 0;
+	u32 val, mask = 0;
+
+	if (oledb->nlimit_enable != -EINVAL) {
+		val = oledb->nlimit_enable <<
+					OLEDB_ENABLE_NLIMIT_BIT_SHIFT;
+		mask = OLEDB_ENABLE_NLIMIT_BIT;
+		if (oledb->negative_curr_limit != -EINVAL) {
+			for (i = 0; i < ARRAY_SIZE(oledb_nlimit_ma); i++) {
+				if (oledb->negative_curr_limit ==
+						oledb_nlimit_ma[i])
+					break;
+			}
+			val |= i;
+			mask |= OLEDB_NLIMIT_PGM_MASK;
+		}
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_NLIMIT, mask, val);
+		if (rc < 0)
+			pr_err("Failed to write NLIMT rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_oledb_init_psm(struct qpnp_oledb *oledb)
+{
+	int rc = 0, i = 0;
+	u32 val = 0, mask = 0, temp = 0;
+	struct qpnp_oledb_psm_ctl *psm_ctl = &oledb->psm_ctl;
+
+	if (psm_ctl->psm_enable == -EINVAL)
+		return rc;
+
+	if (psm_ctl->psm_enable) {
+		val = OLEDB_PSM_ENABLE_BIT;
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_EN_PSM, OLEDB_PSM_ENABLE_BIT, val);
+		if (rc < 0) {
+			pr_err("Failed to write PSM_EN rc=%d\n", rc);
+			return rc;
+		}
+
+		val = 0;
+		if (psm_ctl->psm_vref != -EINVAL) {
+			for (i = 0; i < ARRAY_SIZE(oledb_psm_vref_mv); i++) {
+				if (psm_ctl->psm_vref ==
+						oledb_psm_vref_mv[i])
+					break;
+			}
+			val = i;
+			mask = OLEDB_VREF_PSM_MASK;
+		}
+
+		if (psm_ctl->psm_hys_ctl != -EINVAL) {
+			temp = PSM_HYSTERYSIS_MV_TO_VAL(psm_ctl->psm_hys_ctl);
+			val |= (temp << OLEDB_PSM_HYSTERYSIS_CTL_BIT_SHIFT);
+			mask |= OLEDB_PSM_HYSTERYSIS_CTL_BIT;
+		}
+		if (val) {
+			rc = qpnp_oledb_masked_write(oledb, oledb->base +
+					OLEDB_PSM_CTL, mask, val);
+			if (rc < 0)
+				pr_err("Failed to write PSM_CTL rc=%d\n", rc);
+		}
+	} else {
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_EN_PSM, OLEDB_PSM_ENABLE_BIT, 0);
+		if (rc < 0)
+			pr_err("Failed to write PSM_CTL rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_oledb_init_pfm(struct qpnp_oledb *oledb)
+{
+	int rc = 0, i = 0;
+	u32 val = 0, temp = 0, mask = 0;
+	struct qpnp_oledb_pfm_ctl *pfm_ctl = &oledb->pfm_ctl;
+
+	if (pfm_ctl->pfm_enable == -EINVAL)
+		return rc;
+
+	if (pfm_ctl->pfm_enable) {
+		mask = val = OLEDB_PFM_ENABLE_BIT;
+		if (pfm_ctl->pfm_hys_ctl != -EINVAL) {
+			temp = PFM_HYSTERYSIS_MV_TO_VAL(pfm_ctl->pfm_hys_ctl);
+			val |= temp <<
+				OLEDB_PFM_HYSTERYSIS_CTL_BIT_SHIFT;
+			mask |= OLEDB_PFM_HYSTERYSIS_CTRL_BIT_MASK;
+		}
+
+		if (pfm_ctl->pfm_curr_limit != -EINVAL) {
+			for (i = 0; i < ARRAY_SIZE(oledb_pfm_curr_limit_ma);
+									i++) {
+				if (pfm_ctl->pfm_curr_limit ==
+						oledb_pfm_curr_limit_ma[i])
+					break;
+			}
+			val |= (i << OLEDB_PFM_CURR_LIMIT_SHIFT);
+			mask |= OLEDB_PFM_CURR_LIMIT_MASK;
+		}
+
+		if (pfm_ctl->pfm_off_time != -EINVAL) {
+			val |= PFM_OFF_TIME_NS_TO_VAL(pfm_ctl->pfm_off_time);
+			mask |= OLEDB_PFM_OFF_TIME_NS_MASK;
+		}
+
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_PFM_CTL, mask, val);
+		if (rc < 0)
+			pr_err("Failed to write PFM_CTL rc=%d\n", rc);
+	} else {
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_PFM_CTL, OLEDB_PFM_ENABLE_BIT, 0);
+		if (rc < 0)
+			pr_err("Failed to write PFM_CTL rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_oledb_init_fast_precharge(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	u32 val = 0, temp = 0, mask = 0;
+	struct qpnp_oledb_fast_precharge_ctl *prechg_ctl =
+					&oledb->fast_prechg_ctl;
+
+	if (prechg_ctl->fast_prechg_ppulse_en == -EINVAL)
+		return rc;
+
+	if (prechg_ctl->fast_prechg_ppulse_en) {
+		mask = val = OLEDB_FAST_PRECHG_PPULSE_EN_BIT;
+		if (prechg_ctl->prechg_debounce_time != -EINVAL) {
+			temp = PRECHG_DEBOUNCE_TIME_MS_TO_VAL(
+					prechg_ctl->prechg_debounce_time);
+			val |= temp << OLEDB_DBNC_PRECHARGE_SHIFT;
+			mask |= OLEDB_DBNC_PRECHARGE_MASK;
+		}
+
+		if (prechg_ctl->prechg_pulse_period != -EINVAL) {
+			temp = PRECHG_PULSE_PERIOD_US_TO_VAL(
+					prechg_ctl->prechg_pulse_period);
+			val |= temp << OLEDB_PRECHARGE_PULSE_PERIOD_SHIFT;
+			mask |= OLEDB_PRECHARGE_PULSE_PERIOD_MASK;
+		}
+
+		if (prechg_ctl->prechg_pulse_on_time != -EINVAL) {
+			val |= PRECHG_PULSE_ON_TIME_NS_TO_VAL(
+					prechg_ctl->prechg_pulse_on_time);
+			mask |= OLEDB_PRECHARGE_PULSE_TON_MASK;
+		}
+
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_FAST_PRECHARGE, mask, val);
+		if (rc < 0)
+			pr_err("Failed to write FAST_PRECHARGE rc=%d\n", rc);
+	} else {
+		rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_FAST_PRECHARGE,
+				OLEDB_FAST_PRECHG_PPULSE_EN_BIT, 0);
+		if (rc < 0)
+			pr_err("Failed to write FAST_PRECHARGE rc=%d\n", rc);
+	}
+
+	return rc;
+}
+
+static int qpnp_oledb_hw_init(struct qpnp_oledb *oledb)
+{
+	int rc, i = 0;
+	u8 val = 0, mask = 0;
+	u16 current_voltage;
+
+	if (oledb->default_voltage != -EINVAL) {
+		val = (oledb->default_voltage - OLEDB_VOUT_MIN_MV) /
+					 OLEDB_VOUT_STEP_MV;
+		rc = qpnp_oledb_write(oledb, oledb->base +
+					OLEDB_VOUT_DEFAULT, &val, 1);
+		if (rc < 0) {
+			pr_err("Failed to write VOUT_DEFAULT rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_MODULE_ENABLE,
+				&oledb->mod_enable, 1);
+	if (rc < 0) {
+		pr_err("Failed to read MODULE_ENABLE rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_EXT_PIN_CTL,
+					&oledb->ext_pinctl_state, 1);
+	if (rc < 0) {
+		pr_err("Failed to read EXT_PIN_CTL rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_oledb_get_curr_voltage(oledb, &current_voltage);
+	if (rc < 0)
+		return rc;
+
+	/*
+	 * Go through if the module is not enabled either through
+	 * external pin control or SPMI interface.
+	 */
+	if (!((oledb->ext_pinctl_state & OLEDB_EXT_PIN_CTL_BIT)
+				|| oledb->mod_enable)) {
+		if (oledb->warmup_delay != -EINVAL) {
+			for (i = 0; i < ARRAY_SIZE(oledb_warmup_dly_ns); i++) {
+				if (oledb->warmup_delay ==
+							oledb_warmup_dly_ns[i])
+					break;
+			}
+			val = i;
+			rc = qpnp_oledb_masked_write(oledb,
+				oledb->base + OLEDB_BIAS_GEN_WARMUP_DELAY,
+				OLEDB_BIAS_GEN_WARMUP_DELAY_MASK, val);
+			if (rc < 0) {
+				pr_err("Failed to write WARMUP_DELAY rc=%d\n",
+									rc);
+				return rc;
+			}
+		} else {
+			rc = qpnp_oledb_read(oledb, oledb->base +
+					 OLEDB_BIAS_GEN_WARMUP_DELAY,
+					&val, 1);
+			if (rc < 0) {
+				pr_err("Failed to read WARMUP_DELAY rc=%d\n",
+									rc);
+				return rc;
+			}
+			oledb->warmup_delay = oledb_warmup_dly_ns[val];
+		}
+
+		if (oledb->peak_curr_limit != -EINVAL) {
+			for (i = 0; i < ARRAY_SIZE(oledb_peak_curr_limit_ma);
+									i++) {
+				if (oledb->peak_curr_limit ==
+						oledb_peak_curr_limit_ma[i])
+					break;
+			}
+			val = i;
+			rc = qpnp_oledb_write(oledb,
+					oledb->base + OLEDB_ILIM_NFET,
+					&val, 1);
+			if (rc < 0) {
+				pr_err("Failed to write ILIM_NEFT rc=%d\n", rc);
+				return rc;
+			}
+		}
+
+		if (oledb->pd_ctl != -EINVAL) {
+			val = oledb->pd_ctl;
+			rc = qpnp_oledb_write(oledb, oledb->base +
+					OLEDB_PD_CTL, &val, 1);
+			if (rc < 0) {
+				pr_err("Failed to write PD_CTL rc=%d\n", rc);
+				return rc;
+			}
+		}
+
+		if (oledb->sc_en != -EINVAL) {
+			val = oledb->sc_en ? OLEDB_ENABLE_SC_DETECTION_BIT : 0;
+			mask = OLEDB_ENABLE_SC_DETECTION_BIT;
+			if (oledb->sc_dbnc_time != -EINVAL) {
+				val |= SHORT_CIRCUIT_DEBOUNCE_TIME_TO_VAL(
+							oledb->sc_dbnc_time);
+				mask |= OLEDB_DBNC_PRECHARGE_MASK;
+			}
+
+			rc = qpnp_oledb_write(oledb, oledb->base +
+					OLEDB_SHORT_PROTECT, &val, 1);
+			if (rc < 0) {
+				pr_err("Failed to write SHORT_PROTECT rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+
+		rc = qpnp_oledb_init_nlimit(oledb);
+		if (rc < 0)
+			return rc;
+
+		rc = qpnp_oledb_init_psm(oledb);
+		if (rc < 0)
+			return rc;
+
+		rc = qpnp_oledb_init_pfm(oledb);
+		if (rc < 0)
+			return rc;
+
+		rc = qpnp_oledb_init_fast_precharge(oledb);
+		if (rc < 0)
+			return rc;
+
+		if (oledb->swire_control) {
+			val = OLEDB_EN_SWIRE_PD_UPD_BIT |
+				OLEDB_EN_SWIRE_VOUT_UPD_BIT;
+			rc = qpnp_oledb_masked_write(oledb, oledb->base +
+				OLEDB_SWIRE_CONTROL, OLEDB_EN_SWIRE_PD_UPD_BIT |
+				OLEDB_EN_SWIRE_VOUT_UPD_BIT, val);
+			if (rc < 0)
+				return rc;
+		}
+
+		rc = qpnp_oledb_read(oledb, oledb->base + OLEDB_MODULE_RDY,
+								&val, 1);
+		if (rc < 0) {
+			pr_err("Failed to read MODULE_RDY rc=%d\n", rc);
+			return rc;
+		}
+
+		if (!(val & OLEDB_MODULE_RDY_BIT)) {
+			val = OLEDB_MODULE_RDY_BIT;
+			rc = qpnp_oledb_write(oledb, oledb->base +
+				OLEDB_MODULE_RDY, &val, 1);
+			if (rc < 0) {
+				pr_err("Failed to write MODULE_RDY rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+
+		if (!oledb->dynamic_ext_pinctl_config) {
+			if (oledb->ext_pin_control) {
+				val = OLEDB_EXT_PIN_CTL_BIT;
+				rc = qpnp_oledb_write(oledb, oledb->base +
+						OLEDB_EXT_PIN_CTL, &val, 1);
+				if (rc < 0) {
+					pr_err("Failed to write EXT_PIN_CTL rc=%d\n",
+									rc);
+					return rc;
+				}
+			} else {
+				val = OLEDB_MODULE_ENABLE_BIT;
+				rc = qpnp_oledb_write(oledb, oledb->base +
+						 OLEDB_MODULE_ENABLE, &val, 1);
+				if (rc < 0) {
+					pr_err("Failed to write MODULE_ENABLE rc=%d\n",
+									rc);
+					return rc;
+				}
+
+				ndelay(oledb->warmup_delay);
+			}
+
+			oledb->mod_enable = true;
+			if (oledb->pbs_control) {
+				rc = qpnp_oledb_masked_write(oledb,
+				oledb->base + OLEDB_SWIRE_CONTROL,
+				OLEDB_EN_SWIRE_PD_UPD_BIT |
+				OLEDB_EN_SWIRE_VOUT_UPD_BIT, 0);
+				if (rc < 0) {
+					pr_err("Failed to write SWIRE_CTL rc=%d\n",
+									rc);
+					return rc;
+				}
+			}
+		}
+
+		oledb->current_voltage = current_voltage;
+	} else {
+		 /* module is enabled */
+		if (oledb->current_voltage == -EINVAL) {
+			oledb->current_voltage = current_voltage;
+		} else if (!oledb->swire_control) {
+			if (oledb->current_voltage < OLEDB_VOUT_MIN_MV) {
+				pr_err("current_voltage %d is less than min_volt %d\n",
+				    oledb->current_voltage, OLEDB_VOUT_MIN_MV);
+				return -EINVAL;
+			}
+			val = DIV_ROUND_UP(oledb->current_voltage -
+					OLEDB_VOUT_MIN_MV, OLEDB_VOUT_STEP_MV);
+			rc = qpnp_oledb_write(oledb, oledb->base +
+						OLEDB_VOUT_PGM, &val, 1);
+			if (rc < 0) {
+				pr_err("Failed to write VOUT_PGM rc=%d\n",
+									rc);
+				return rc;
+			}
+		}
+
+		oledb->mod_enable = true;
+	}
+
+	return rc;
+}
+
+static int qpnp_oledb_parse_nlimit(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	struct device_node *of_node = oledb->dev->of_node;
+
+	oledb->nlimit_enable = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,negative-curr-limit-enable",
+					&oledb->nlimit_enable);
+	if (!rc) {
+		oledb->negative_curr_limit = -EINVAL;
+		rc = of_property_read_u32(of_node,
+					 "qcom,negative-curr-limit-ma",
+					 &oledb->negative_curr_limit);
+		if (!rc) {
+			u16 min_curr_limit = oledb_nlimit_ma[0];
+			u16 max_curr_limit = oledb_nlimit_ma[ARRAY_SIZE(
+						oledb_nlimit_ma) - 1];
+			if (oledb->negative_curr_limit < min_curr_limit ||
+				oledb->negative_curr_limit > max_curr_limit) {
+				pr_err("Invalid value in qcom,negative-curr-limit-ma\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_oledb_parse_psm(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	struct qpnp_oledb_psm_ctl *psm_ctl = &oledb->psm_ctl;
+	struct device_node *of_node = oledb->dev->of_node;
+
+	psm_ctl->psm_enable = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,psm-enable",
+					&psm_ctl->psm_enable);
+	if (!rc) {
+		psm_ctl->psm_hys_ctl = -EINVAL;
+		rc = of_property_read_u32(of_node, "qcom,psm-hys-mv",
+						&psm_ctl->psm_hys_ctl);
+		if (!rc) {
+			if (psm_ctl->psm_hys_ctl < OLEDB_PSM_HYS_CTRL_MIN ||
+			      psm_ctl->psm_hys_ctl > OLEDB_PSM_HYS_CTRL_MAX) {
+				pr_err("Invalid value in qcom,psm-hys-mv\n");
+				return -EINVAL;
+			}
+		}
+
+		psm_ctl->psm_vref = -EINVAL;
+		rc = of_property_read_u32(of_node, "qcom,psm-vref-mv",
+							&psm_ctl->psm_vref);
+		if (!rc) {
+			u16 min_vref = oledb_psm_vref_mv[0];
+			u16 max_vref = oledb_psm_vref_mv[ARRAY_SIZE(
+						oledb_psm_vref_mv) - 1];
+			if (psm_ctl->psm_vref < min_vref ||
+					psm_ctl->psm_vref > max_vref) {
+				pr_err("Invalid value in qcom,psm-vref-mv\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_oledb_parse_pfm(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	struct qpnp_oledb_pfm_ctl *pfm_ctl = &oledb->pfm_ctl;
+	struct device_node *of_node = oledb->dev->of_node;
+
+	pfm_ctl->pfm_enable = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,pfm-enable",
+					&pfm_ctl->pfm_enable);
+	if (!rc) {
+		pfm_ctl->pfm_hys_ctl = -EINVAL;
+		rc = of_property_read_u32(of_node, "qcom,pfm-hys-mv",
+						&pfm_ctl->pfm_hys_ctl);
+		if (!rc) {
+			if (pfm_ctl->pfm_hys_ctl < OLEDB_PFM_HYS_CTRL_MIN ||
+			       pfm_ctl->pfm_hys_ctl > OLEDB_PFM_HYS_CTRL_MAX) {
+				pr_err("Invalid value in qcom,pfm-hys-mv\n");
+				return -EINVAL;
+			}
+		}
+
+		pfm_ctl->pfm_curr_limit = -EINVAL;
+		rc = of_property_read_u32(of_node,
+		      "qcom,pfm-curr-limit-ma", &pfm_ctl->pfm_curr_limit);
+		if (!rc) {
+			u16 min_limit = oledb_pfm_curr_limit_ma[0];
+			u16 max_limit = oledb_pfm_curr_limit_ma[ARRAY_SIZE(
+						oledb_pfm_curr_limit_ma) - 1];
+			if (pfm_ctl->pfm_curr_limit < min_limit ||
+					pfm_ctl->pfm_curr_limit > max_limit) {
+				pr_err("Invalid value in qcom,pfm-curr-limit-ma\n");
+				return -EINVAL;
+			}
+		}
+
+		pfm_ctl->pfm_off_time = -EINVAL;
+		rc = of_property_read_u32(of_node, "qcom,pfm-off-time-ns",
+							&pfm_ctl->pfm_off_time);
+		if (!rc) {
+			if (pfm_ctl->pfm_off_time < OLEDB_PFM_OFF_TIME_MIN ||
+			      pfm_ctl->pfm_off_time > OLEDB_PFM_OFF_TIME_MAX) {
+				pr_err("Invalid value in qcom,pfm-off-time-ns\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_oledb_parse_fast_precharge(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	struct device_node *of_node = oledb->dev->of_node;
+	struct qpnp_oledb_fast_precharge_ctl *fast_prechg =
+					&oledb->fast_prechg_ctl;
+
+	fast_prechg->fast_prechg_ppulse_en = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,fast-precharge-ppulse-enable",
+				&fast_prechg->fast_prechg_ppulse_en);
+	if (!rc) {
+		fast_prechg->prechg_debounce_time = -EINVAL;
+		rc = of_property_read_u32(of_node,
+					"qcom,precharge-debounce-time-ms",
+					&fast_prechg->prechg_debounce_time);
+		if (!rc) {
+			int dbnc_time = fast_prechg->prechg_debounce_time;
+
+			if (dbnc_time < OLEDB_PRECHG_TIME_MIN || dbnc_time >
+						      OLEDB_PRECHG_TIME_MAX) {
+				pr_err("Invalid value in qcom,precharge-debounce-time-ms\n");
+				return -EINVAL;
+			}
+		}
+
+		fast_prechg->prechg_pulse_period = -EINVAL;
+		rc = of_property_read_u32(of_node,
+					"qcom,precharge-pulse-period-us",
+					&fast_prechg->prechg_pulse_period);
+		if (!rc) {
+			int pulse_period = fast_prechg->prechg_pulse_period;
+
+			if (pulse_period < OLEDB_PRECHG_PULSE_PERIOD_MIN ||
+			       pulse_period > OLEDB_PRECHG_PULSE_PERIOD_MAX) {
+				pr_err("Invalid value in qcom,precharge-pulse-period-us\n");
+				return -EINVAL;
+			}
+		}
+
+		fast_prechg->prechg_pulse_on_time = -EINVAL;
+		rc = of_property_read_u32(of_node,
+					"qcom,precharge-pulse-on-time-ns",
+					&fast_prechg->prechg_pulse_on_time);
+		if (!rc) {
+			int pulse_on_time = fast_prechg->prechg_pulse_on_time;
+
+			if (pulse_on_time < OLEDB_PRECHG_PULSE_ON_TIME_MIN ||
+			      pulse_on_time > OLEDB_PRECHG_PULSE_ON_TIME_MAX) {
+				pr_err("Invalid value in qcom,precharge-pulse-on-time-ns\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int qpnp_oledb_parse_dt(struct qpnp_oledb *oledb)
+{
+	int rc = 0;
+	struct device_node *of_node = oledb->dev->of_node;
+
+	oledb->swire_control =
+			of_property_read_bool(of_node, "qcom,swire-control");
+
+	oledb->ext_pin_control =
+			of_property_read_bool(of_node, "qcom,ext-pin-control");
+
+	if (oledb->ext_pin_control)
+		oledb->dynamic_ext_pinctl_config =
+				of_property_read_bool(of_node,
+					"qcom,dynamic-ext-pinctl-config");
+	oledb->pbs_control =
+			of_property_read_bool(of_node, "qcom,pbs-control");
+
+	oledb->current_voltage = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,oledb-init-voltage-mv",
+						&oledb->current_voltage);
+	if (!rc && (oledb->current_voltage < OLEDB_VOUT_MIN_MV ||
+				oledb->current_voltage > OLEDB_VOUT_MAX_MV)) {
+		pr_err("Invalid value in qcom,oledb-init-voltage-mv\n");
+		return -EINVAL;
+	}
+
+	oledb->default_voltage = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,oledb-default-voltage-mv",
+					&oledb->default_voltage);
+	if (!rc && (oledb->default_voltage < OLEDB_VOUT_MIN_MV ||
+				oledb->default_voltage > OLEDB_VOUT_MAX_MV)) {
+		pr_err("Invalid value in qcom,oledb-default-voltage-mv\n");
+		return -EINVAL;
+	}
+
+	oledb->warmup_delay = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,bias-gen-warmup-delay-ns",
+					&oledb->warmup_delay);
+	if (!rc) {
+		u16 min_delay = oledb_warmup_dly_ns[0];
+		u16 max_delay = oledb_warmup_dly_ns[ARRAY_SIZE(
+						oledb_warmup_dly_ns) - 1];
+		if (oledb->warmup_delay < min_delay ||
+					oledb->warmup_delay > max_delay) {
+			pr_err("Invalid value in qcom,bias-gen-warmup-delay-ns\n");
+			return -EINVAL;
+		}
+	}
+
+	oledb->peak_curr_limit = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,peak-curr-limit-ma",
+					&oledb->peak_curr_limit);
+	if (!rc) {
+		u16 min_limit = oledb_peak_curr_limit_ma[0];
+		u16 max_limit = oledb_peak_curr_limit_ma[ARRAY_SIZE(
+					oledb_peak_curr_limit_ma) - 1];
+		if (oledb->peak_curr_limit < min_limit ||
+				oledb->peak_curr_limit > max_limit) {
+			pr_err("Invalid value in qcom,peak-curr-limit-ma\n");
+			return -EINVAL;
+		}
+	}
+
+	oledb->pd_ctl = -EINVAL;
+	of_property_read_u32(of_node, "qcom,pull-down-enable", &oledb->pd_ctl);
+
+	oledb->sc_en = -EINVAL;
+	rc = of_property_read_u32(of_node, "qcom,enable-short-circuit",
+					&oledb->sc_en);
+	if (!rc) {
+		oledb->sc_dbnc_time = -EINVAL;
+		rc = of_property_read_u32(of_node,
+			"qcom,short-circuit-dbnc-time", &oledb->sc_dbnc_time);
+		if (!rc) {
+			if (oledb->sc_dbnc_time < OLEDB_MIN_SC_DBNC_TIME_FSW ||
+			    oledb->sc_dbnc_time > OLEDB_MAX_SC_DBNC_TIME_FSW) {
+				pr_err("Invalid value in qcom,short-circuit-dbnc-time\n");
+				return -EINVAL;
+			}
+		}
+	}
+
+	rc = qpnp_oledb_parse_nlimit(oledb);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_oledb_parse_psm(oledb);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_oledb_parse_pfm(oledb);
+	if (rc < 0)
+		return rc;
+
+	rc = qpnp_oledb_parse_fast_precharge(oledb);
+
+	return rc;
+}
+
+static int qpnp_oledb_regulator_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	u32 val;
+	struct qpnp_oledb *oledb;
+	struct device_node *of_node = pdev->dev.of_node;
+
+	oledb = devm_kzalloc(&pdev->dev,
+			sizeof(struct qpnp_oledb), GFP_KERNEL);
+	if (!oledb)
+		return -ENOMEM;
+
+	oledb->pdev = pdev;
+	oledb->dev = &pdev->dev;
+	oledb->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	dev_set_drvdata(&pdev->dev, oledb);
+	if (!oledb->regmap) {
+		pr_err("Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(of_node, "reg", &val);
+	if (rc < 0) {
+		pr_err("Couldn't find reg in node, rc = %d\n", rc);
+		return rc;
+	}
+
+	oledb->base = val;
+	rc = qpnp_oledb_parse_dt(oledb);
+	if (rc < 0) {
+		pr_err("Failed to parse common OLEDB device tree\n");
+		return rc;
+	}
+
+	rc = qpnp_oledb_hw_init(oledb);
+	if (rc < 0) {
+		pr_err("Failed to initialize OLEDB, rc=%d\n", rc);
+		return rc;
+	}
+
+	rc = qpnp_oledb_register_regulator(oledb);
+	if (!rc)
+		pr_info("OLEDB registered successfully, ext_pin_en=%d mod_en=%d cuurent_voltage=%d mV\n",
+			oledb->ext_pin_control, oledb->mod_enable,
+						oledb->current_voltage);
+
+	return rc;
+}
+
+static int qpnp_oledb_regulator_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+const struct of_device_id qpnp_oledb_regulator_match_table[] = {
+	{ .compatible = QPNP_OLEDB_REGULATOR_DRIVER_NAME,},
+	{ },
+};
+
+static struct platform_driver qpnp_oledb_regulator_driver = {
+	.driver		= {
+		.name		= QPNP_OLEDB_REGULATOR_DRIVER_NAME,
+		.of_match_table	= qpnp_oledb_regulator_match_table,
+	},
+	.probe		= qpnp_oledb_regulator_probe,
+	.remove		= qpnp_oledb_regulator_remove,
+};
+
+static int __init qpnp_oledb_regulator_init(void)
+{
+	return platform_driver_register(&qpnp_oledb_regulator_driver);
+}
+arch_initcall(qpnp_oledb_regulator_init);
+
+static void __exit qpnp_oledb_regulator_exit(void)
+{
+	platform_driver_unregister(&qpnp_oledb_regulator_driver);
+}
+module_exit(qpnp_oledb_regulator_exit);
+
+MODULE_DESCRIPTION("QPNP OLEDB driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("qpnp-oledb-regulator");
diff --git a/drivers/regulator/qpnp-regulator.c b/drivers/regulator/qpnp-regulator.c
new file mode 100644
index 0000000..bd70665
--- /dev/null
+++ b/drivers/regulator/qpnp-regulator.c
@@ -0,0 +1,2436 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regulator/qpnp-regulator.h>
+
+/* Debug Flag Definitions */
+enum {
+	QPNP_VREG_DEBUG_REQUEST		= BIT(0), /* Show requests */
+	QPNP_VREG_DEBUG_DUPLICATE	= BIT(1), /* Show duplicate requests */
+	QPNP_VREG_DEBUG_INIT		= BIT(2), /* Show state after probe */
+	QPNP_VREG_DEBUG_WRITES		= BIT(3), /* Show SPMI writes */
+	QPNP_VREG_DEBUG_READS		= BIT(4), /* Show SPMI reads */
+	QPNP_VREG_DEBUG_OCP		= BIT(5), /* Show VS OCP IRQ events */
+};
+
+static int qpnp_vreg_debug_mask;
+module_param_named(
+	debug_mask, qpnp_vreg_debug_mask, int, 0600
+);
+
+#define vreg_err(vreg, fmt, ...) \
+	pr_err("%s: " fmt, vreg->rdesc.name, ##__VA_ARGS__)
+
+/* These types correspond to unique register layouts. */
+enum qpnp_regulator_logical_type {
+	QPNP_REGULATOR_LOGICAL_TYPE_SMPS,
+	QPNP_REGULATOR_LOGICAL_TYPE_LDO,
+	QPNP_REGULATOR_LOGICAL_TYPE_VS,
+	QPNP_REGULATOR_LOGICAL_TYPE_BOOST,
+	QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS,
+	QPNP_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+	QPNP_REGULATOR_LOGICAL_TYPE_LN_LDO,
+	QPNP_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+	QPNP_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+	QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+	QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2,
+};
+
+enum qpnp_regulator_type {
+	QPNP_REGULATOR_TYPE_BUCK		= 0x03,
+	QPNP_REGULATOR_TYPE_LDO			= 0x04,
+	QPNP_REGULATOR_TYPE_VS			= 0x05,
+	QPNP_REGULATOR_TYPE_BOOST		= 0x1B,
+	QPNP_REGULATOR_TYPE_FTS			= 0x1C,
+	QPNP_REGULATOR_TYPE_BOOST_BYP		= 0x1F,
+	QPNP_REGULATOR_TYPE_ULT_LDO		= 0x21,
+	QPNP_REGULATOR_TYPE_ULT_BUCK		= 0x22,
+};
+
+enum qpnp_regulator_subtype {
+	QPNP_REGULATOR_SUBTYPE_GP_CTL		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_RF_CTL		= 0x09,
+	QPNP_REGULATOR_SUBTYPE_N50		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_N150		= 0x02,
+	QPNP_REGULATOR_SUBTYPE_N300		= 0x03,
+	QPNP_REGULATOR_SUBTYPE_N600		= 0x04,
+	QPNP_REGULATOR_SUBTYPE_N1200		= 0x05,
+	QPNP_REGULATOR_SUBTYPE_N600_ST		= 0x06,
+	QPNP_REGULATOR_SUBTYPE_N1200_ST		= 0x07,
+	QPNP_REGULATOR_SUBTYPE_N300_ST		= 0x15,
+	QPNP_REGULATOR_SUBTYPE_P50		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_P150		= 0x09,
+	QPNP_REGULATOR_SUBTYPE_P300		= 0x0A,
+	QPNP_REGULATOR_SUBTYPE_P600		= 0x0B,
+	QPNP_REGULATOR_SUBTYPE_P1200		= 0x0C,
+	QPNP_REGULATOR_SUBTYPE_LN		= 0x10,
+	QPNP_REGULATOR_SUBTYPE_LV_P50		= 0x28,
+	QPNP_REGULATOR_SUBTYPE_LV_P150		= 0x29,
+	QPNP_REGULATOR_SUBTYPE_LV_P300		= 0x2A,
+	QPNP_REGULATOR_SUBTYPE_LV_P600		= 0x2B,
+	QPNP_REGULATOR_SUBTYPE_LV_P1200		= 0x2C,
+	QPNP_REGULATOR_SUBTYPE_LV100		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_LV300		= 0x02,
+	QPNP_REGULATOR_SUBTYPE_MV300		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_MV500		= 0x09,
+	QPNP_REGULATOR_SUBTYPE_HDMI		= 0x10,
+	QPNP_REGULATOR_SUBTYPE_OTG		= 0x11,
+	QPNP_REGULATOR_SUBTYPE_5V_BOOST		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_FTS_CTL		= 0x08,
+	QPNP_REGULATOR_SUBTYPE_FTS2p5_CTL	= 0x09,
+	QPNP_REGULATOR_SUBTYPE_FTS426		= 0x0A,
+	QPNP_REGULATOR_SUBTYPE_BB_2A		= 0x01,
+	QPNP_REGULATOR_SUBTYPE_ULT_HF_CTL1	= 0x0D,
+	QPNP_REGULATOR_SUBTYPE_ULT_HF_CTL2	= 0x0E,
+	QPNP_REGULATOR_SUBTYPE_ULT_HF_CTL3	= 0x0F,
+	QPNP_REGULATOR_SUBTYPE_ULT_HF_CTL4	= 0x10,
+};
+
+/* First common register layout used by older devices */
+enum qpnp_common_regulator_registers {
+	QPNP_COMMON_REG_DIG_MAJOR_REV		= 0x01,
+	QPNP_COMMON_REG_TYPE			= 0x04,
+	QPNP_COMMON_REG_SUBTYPE			= 0x05,
+	QPNP_COMMON_REG_VOLTAGE_RANGE		= 0x40,
+	QPNP_COMMON_REG_VOLTAGE_SET		= 0x41,
+	QPNP_COMMON_REG_MODE			= 0x45,
+	QPNP_COMMON_REG_ENABLE			= 0x46,
+	QPNP_COMMON_REG_PULL_DOWN		= 0x48,
+	QPNP_COMMON_REG_STEP_CTRL		= 0x61,
+};
+
+/*
+ * Second common register layout used by newer devices
+ * Note that some of the registers from the first common layout remain
+ * unchanged and their definition is not duplicated.
+ */
+enum qpnp_common2_regulator_registers {
+	QPNP_COMMON2_REG_VOLTAGE_LSB		= 0x40,
+	QPNP_COMMON2_REG_VOLTAGE_MSB		= 0x41,
+	QPNP_COMMON2_REG_MODE			= 0x45,
+	QPNP_COMMON2_REG_STEP_CTRL		= 0x61,
+};
+
+enum qpnp_ldo_registers {
+	QPNP_LDO_REG_SOFT_START			= 0x4C,
+};
+
+enum qpnp_vs_registers {
+	QPNP_VS_REG_OCP				= 0x4A,
+	QPNP_VS_REG_SOFT_START			= 0x4C,
+};
+
+enum qpnp_boost_registers {
+	QPNP_BOOST_REG_CURRENT_LIMIT		= 0x4A,
+};
+
+enum qpnp_boost_byp_registers {
+	QPNP_BOOST_BYP_REG_CURRENT_LIMIT	= 0x4B,
+};
+
+/* Used for indexing into ctrl_reg.  These are offets from 0x40 */
+enum qpnp_common_control_register_index {
+	QPNP_COMMON_IDX_VOLTAGE_RANGE		= 0,
+	QPNP_COMMON_IDX_VOLTAGE_SET		= 1,
+	QPNP_COMMON_IDX_MODE			= 5,
+	QPNP_COMMON_IDX_ENABLE			= 6,
+};
+
+enum qpnp_common2_control_register_index {
+	QPNP_COMMON2_IDX_VOLTAGE_LSB		= 0,
+	QPNP_COMMON2_IDX_VOLTAGE_MSB		= 1,
+	QPNP_COMMON2_IDX_MODE			= 5,
+};
+
+/* Common regulator control register layout */
+#define QPNP_COMMON_ENABLE_MASK			0x80
+#define QPNP_COMMON_ENABLE			0x80
+#define QPNP_COMMON_DISABLE			0x00
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN3_MASK	0x08
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN2_MASK	0x04
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN1_MASK	0x02
+#define QPNP_COMMON_ENABLE_FOLLOW_HW_EN0_MASK	0x01
+#define QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK	0x0F
+
+/* First common regulator mode register layout */
+#define QPNP_COMMON_MODE_HPM_MASK		0x80
+#define QPNP_COMMON_MODE_AUTO_MASK		0x40
+#define QPNP_COMMON_MODE_BYPASS_MASK		0x20
+#define QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK	0x10
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN3_MASK	0x08
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN2_MASK	0x04
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN1_MASK	0x02
+#define QPNP_COMMON_MODE_FOLLOW_HW_EN0_MASK	0x01
+#define QPNP_COMMON_MODE_FOLLOW_ALL_MASK	0x1F
+
+/* Second common regulator mode register values */
+#define QPNP_COMMON2_MODE_BYPASS		3
+#define QPNP_COMMON2_MODE_RETENTION		4
+#define QPNP_COMMON2_MODE_LPM			5
+#define QPNP_COMMON2_MODE_AUTO			6
+#define QPNP_COMMON2_MODE_HPM			7
+
+#define QPNP_COMMON2_MODE_MASK			0x07
+
+/* Common regulator pull down control register layout */
+#define QPNP_COMMON_PULL_DOWN_ENABLE_MASK	0x80
+
+/* LDO regulator current limit control register layout */
+#define QPNP_LDO_CURRENT_LIMIT_ENABLE_MASK	0x80
+
+/* LDO regulator soft start control register layout */
+#define QPNP_LDO_SOFT_START_ENABLE_MASK		0x80
+
+/* VS regulator over current protection control register layout */
+#define QPNP_VS_OCP_OVERRIDE			0x01
+#define QPNP_VS_OCP_NO_OVERRIDE			0x00
+
+/* VS regulator soft start control register layout */
+#define QPNP_VS_SOFT_START_ENABLE_MASK		0x80
+#define QPNP_VS_SOFT_START_SEL_MASK		0x03
+
+/* Boost regulator current limit control register layout */
+#define QPNP_BOOST_CURRENT_LIMIT_ENABLE_MASK	0x80
+#define QPNP_BOOST_CURRENT_LIMIT_MASK		0x07
+
+#define QPNP_VS_OCP_DEFAULT_MAX_RETRIES		10
+#define QPNP_VS_OCP_DEFAULT_RETRY_DELAY_MS	30
+#define QPNP_VS_OCP_FALL_DELAY_US		90
+#define QPNP_VS_OCP_FAULT_DELAY_US		20000
+
+#define QPNP_FTSMPS_STEP_CTRL_STEP_MASK		0x18
+#define QPNP_FTSMPS_STEP_CTRL_STEP_SHIFT	3
+#define QPNP_FTSMPS_STEP_CTRL_DELAY_MASK	0x07
+#define QPNP_FTSMPS_STEP_CTRL_DELAY_SHIFT	0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define QPNP_FTSMPS_CLOCK_RATE		19200
+
+/* Minimum voltage stepper delay for each step. */
+#define QPNP_FTSMPS_STEP_DELAY		8
+
+/*
+ * The ratio QPNP_FTSMPS_STEP_MARGIN_NUM/QPNP_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define QPNP_FTSMPS_STEP_MARGIN_NUM	4
+#define QPNP_FTSMPS_STEP_MARGIN_DEN	5
+
+#define QPNP_FTSMPS2_STEP_CTRL_DELAY_MASK	0x03
+#define QPNP_FTSMPS2_STEP_CTRL_DELAY_SHIFT	0
+
+/* Clock rate in kHz of the FTSMPS2 regulator reference clock. */
+#define QPNP_FTSMPS2_CLOCK_RATE		4800
+
+/* Minimum voltage stepper delay for each step. */
+#define QPNP_FTSMPS2_STEP_DELAY		2
+
+/*
+ * The ratio QPNP_FTSMPS2_STEP_MARGIN_NUM/QPNP_FTSMPS2_STEP_MARGIN_DEN is used
+ * to adjust the step rate in order to account for oscillator variance.
+ */
+#define QPNP_FTSMPS2_STEP_MARGIN_NUM	10
+#define QPNP_FTSMPS2_STEP_MARGIN_DEN	11
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level.  It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct qpnp_voltage_range - regulator set point voltage mapping description
+ * @min_uV:		Minimum programmable output voltage resulting from
+ *			set point register value 0x00
+ * @max_uV:		Maximum programmable output voltage
+ * @step_uV:		Output voltage increase resulting from the set point
+ *			register value increasing by 1
+ * @set_point_min_uV:	Minimum allowed voltage
+ * @set_point_max_uV:	Maximum allowed voltage.  This may be tweaked in order
+ *			to pick which range should be used in the case of
+ *			overlapping set points.
+ * @n_voltages:		Number of preferred voltage set points present in this
+ *			range
+ * @range_sel:		Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct qpnp_voltage_range {
+	int					min_uV;
+	int					max_uV;
+	int					step_uV;
+	int					set_point_min_uV;
+	int					set_point_max_uV;
+	unsigned int				n_voltages;
+	u8					range_sel;
+};
+
+/*
+ * The ranges specified in the qpnp_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct qpnp_voltage_set_points {
+	struct qpnp_voltage_range		*range;
+	int					count;
+	unsigned int				n_voltages;
+};
+
+struct qpnp_regulator_mapping {
+	enum qpnp_regulator_type		type;
+	enum qpnp_regulator_subtype		subtype;
+	enum qpnp_regulator_logical_type	logical_type;
+	u32					revision_min;
+	u32					revision_max;
+	struct regulator_ops			*ops;
+	struct qpnp_voltage_set_points		*set_points;
+	int					hpm_min_load;
+};
+
+struct qpnp_regulator {
+	struct regulator_desc			rdesc;
+	struct delayed_work			ocp_work;
+	struct platform_device			*pdev;
+	struct regmap				*regmap;
+	struct regulator_dev			*rdev;
+	struct qpnp_voltage_set_points		*set_points;
+	enum qpnp_regulator_logical_type	logical_type;
+	int					enable_time;
+	int					ocp_enable;
+	int					ocp_irq;
+	int					ocp_count;
+	int					ocp_max_retries;
+	int					ocp_retry_delay_ms;
+	int					system_load;
+	int					hpm_min_load;
+	int					slew_rate;
+	u32					write_count;
+	u32					prev_write_count;
+	ktime_t					vs_enable_time;
+	u16					base_addr;
+	/* ctrl_reg provides a shadow copy of register values 0x40 to 0x47. */
+	u8					ctrl_reg[8];
+	u8					init_mode;
+};
+
+#define QPNP_VREG_MAP(_type, _subtype, _dig_major_min, _dig_major_max, \
+		      _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+	{ \
+		.type		= QPNP_REGULATOR_TYPE_##_type, \
+		.subtype	= QPNP_REGULATOR_SUBTYPE_##_subtype, \
+		.revision_min	= _dig_major_min, \
+		.revision_max	= _dig_major_max, \
+		.logical_type	= QPNP_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+		.ops		= &qpnp_##_ops_val##_ops, \
+		.set_points	= &_set_points_val##_set_points, \
+		.hpm_min_load	= _hpm_min_load, \
+	}
+
+#define VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+			_set_point_max_uV, _max_uV, _step_uV) \
+	{ \
+		.min_uV			= _min_uV, \
+		.max_uV			= _max_uV, \
+		.set_point_min_uV	= _set_point_min_uV, \
+		.set_point_max_uV	= _set_point_max_uV, \
+		.step_uV		= _step_uV, \
+		.range_sel		= _range_sel, \
+	}
+
+#define SET_POINTS(_ranges) \
+{ \
+	.range	= _ranges, \
+	.count	= ARRAY_SIZE(_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges.  Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique.  The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct qpnp_voltage_range pldo_ranges[] = {
+	VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+	VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+	VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct qpnp_voltage_range nldo1_ranges[] = {
+	VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+};
+
+static struct qpnp_voltage_range nldo2_ranges[] = {
+	VOLTAGE_RANGE(0,  375000,       0,       0, 1537500, 12500),
+	VOLTAGE_RANGE(1,  375000,  375000,  768750,  768750,  6250),
+	VOLTAGE_RANGE(2,  750000,  775000, 1537500, 1537500, 12500),
+};
+
+static struct qpnp_voltage_range nldo3_ranges[] = {
+	VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+	VOLTAGE_RANGE(1,  375000,       0,       0, 1537500, 12500),
+	VOLTAGE_RANGE(2,  750000,       0,       0, 1537500, 12500),
+};
+
+static struct qpnp_voltage_range ln_ldo_ranges[] = {
+	VOLTAGE_RANGE(1,  690000,  690000, 1110000, 1110000, 60000),
+	VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct qpnp_voltage_range smps_ranges[] = {
+	VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+	VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct qpnp_voltage_range ftsmps_ranges[] = {
+	VOLTAGE_RANGE(0,       0,  350000, 1275000, 1275000,  5000),
+	VOLTAGE_RANGE(1,       0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct qpnp_voltage_range ftsmps2p5_ranges[] = {
+	VOLTAGE_RANGE(0,   80000,  350000, 1355000, 1355000,  5000),
+	VOLTAGE_RANGE(1,  160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct qpnp_voltage_range boost_ranges[] = {
+	VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct qpnp_voltage_range boost_byp_ranges[] = {
+	VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct qpnp_voltage_range ult_lo_smps_ranges[] = {
+	VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+	VOLTAGE_RANGE(1,  750000,       0,       0, 1525000, 25000),
+};
+
+static struct qpnp_voltage_range ult_ho_smps_ranges[] = {
+	VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct qpnp_voltage_range ult_nldo_ranges[] = {
+	VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+};
+
+static struct qpnp_voltage_range ult_pldo_ranges[] = {
+	VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static struct qpnp_voltage_range ftsmps426_ranges[] = {
+	VOLTAGE_RANGE(0,       0,  320000, 1352000, 1352000,  4000),
+};
+
+static struct qpnp_voltage_set_points pldo_set_points = SET_POINTS(pldo_ranges);
+static struct qpnp_voltage_set_points nldo1_set_points
+					= SET_POINTS(nldo1_ranges);
+static struct qpnp_voltage_set_points nldo2_set_points
+					= SET_POINTS(nldo2_ranges);
+static struct qpnp_voltage_set_points nldo3_set_points
+					= SET_POINTS(nldo3_ranges);
+static struct qpnp_voltage_set_points ln_ldo_set_points
+					= SET_POINTS(ln_ldo_ranges);
+static struct qpnp_voltage_set_points smps_set_points = SET_POINTS(smps_ranges);
+static struct qpnp_voltage_set_points ftsmps_set_points
+					= SET_POINTS(ftsmps_ranges);
+static struct qpnp_voltage_set_points ftsmps2p5_set_points
+					= SET_POINTS(ftsmps2p5_ranges);
+static struct qpnp_voltage_set_points boost_set_points
+					= SET_POINTS(boost_ranges);
+static struct qpnp_voltage_set_points boost_byp_set_points
+					= SET_POINTS(boost_byp_ranges);
+static struct qpnp_voltage_set_points ult_lo_smps_set_points
+					= SET_POINTS(ult_lo_smps_ranges);
+static struct qpnp_voltage_set_points ult_ho_smps_set_points
+					= SET_POINTS(ult_ho_smps_ranges);
+static struct qpnp_voltage_set_points ult_nldo_set_points
+					= SET_POINTS(ult_nldo_ranges);
+static struct qpnp_voltage_set_points ult_pldo_set_points
+					= SET_POINTS(ult_pldo_ranges);
+static struct qpnp_voltage_set_points ftsmps426_set_points
+					= SET_POINTS(ftsmps426_ranges);
+static struct qpnp_voltage_set_points none_set_points;
+
+static struct qpnp_voltage_set_points *all_set_points[] = {
+	&pldo_set_points,
+	&nldo1_set_points,
+	&nldo2_set_points,
+	&nldo3_set_points,
+	&ln_ldo_set_points,
+	&smps_set_points,
+	&ftsmps_set_points,
+	&ftsmps2p5_set_points,
+	&boost_set_points,
+	&boost_byp_set_points,
+	&ult_lo_smps_set_points,
+	&ult_ho_smps_set_points,
+	&ult_nldo_set_points,
+	&ult_pldo_set_points,
+	&ftsmps426_set_points,
+};
+
+/* Determines which label to add to a debug print statement. */
+enum qpnp_regulator_action {
+	QPNP_REGULATOR_ACTION_INIT,
+	QPNP_REGULATOR_ACTION_ENABLE,
+	QPNP_REGULATOR_ACTION_DISABLE,
+	QPNP_REGULATOR_ACTION_VOLTAGE,
+	QPNP_REGULATOR_ACTION_MODE,
+};
+
+static void qpnp_vreg_show_state(struct regulator_dev *rdev,
+				   enum qpnp_regulator_action action);
+
+#define DEBUG_PRINT_BUFFER_SIZE 64
+static void fill_string(char *str, size_t str_len, u8 *buf, int buf_len)
+{
+	int pos = 0;
+	int i;
+
+	for (i = 0; i < buf_len; i++) {
+		pos += scnprintf(str + pos, str_len - pos, "0x%02X", buf[i]);
+		if (i < buf_len - 1)
+			pos += scnprintf(str + pos, str_len - pos, ", ");
+	}
+}
+
+static inline int qpnp_vreg_read(struct qpnp_regulator *vreg, u16 addr, u8 *buf,
+				 int len)
+{
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+	int rc = 0;
+
+	rc = regmap_bulk_read(vreg->regmap, vreg->base_addr + addr, buf, len);
+
+	if (!rc && (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_READS)) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
+		pr_info(" %-11s:  read(0x%04X), sid=%d, len=%d; %s\n",
+			vreg->rdesc.name, vreg->base_addr + addr,
+			to_spmi_device(vreg->pdev->dev.parent)->usid, len,
+			str);
+	}
+
+	return rc;
+}
+
+static inline int qpnp_vreg_write(struct qpnp_regulator *vreg, u16 addr,
+				u8 *buf, int len)
+{
+	char str[DEBUG_PRINT_BUFFER_SIZE];
+	int rc = 0;
+
+	if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_WRITES) {
+		str[0] = '\0';
+		fill_string(str, DEBUG_PRINT_BUFFER_SIZE, buf, len);
+		pr_info("%-11s: write(0x%04X), sid=%d, len=%d; %s\n",
+			vreg->rdesc.name, vreg->base_addr + addr,
+			to_spmi_device(vreg->pdev->dev.parent)->usid, len,
+			str);
+	}
+
+	rc = regmap_bulk_write(vreg->regmap, vreg->base_addr + addr, buf, len);
+	if (!rc)
+		vreg->write_count += len;
+
+	return rc;
+}
+
+/*
+ * qpnp_vreg_write_optimized - write the minimum sized contiguous subset of buf
+ * @vreg:	qpnp_regulator pointer for this regulator
+ * @addr:	local SPMI address offset from this peripheral's base address
+ * @buf:	new data to write into the SPMI registers
+ * @buf_save:	old data in the registers
+ * @len:	number of bytes to write
+ *
+ * This function checks for unchanged register values between buf and buf_save
+ * starting at both ends of buf.  Only the contiguous subset in the middle of
+ * buf starting and ending with new values is sent.
+ *
+ * Consider the following example:
+ * buf offset: 0 1 2 3 4 5 6 7
+ * reg state:  U U C C U C U U
+ * (U = unchanged, C = changed)
+ * In this example registers 2 through 5 will be written with a single
+ * transaction.
+ */
+static inline int qpnp_vreg_write_optimized(struct qpnp_regulator *vreg,
+		u16 addr, u8 *buf, u8 *buf_save, int len)
+{
+	int i, rc, start, end;
+
+	for (i = 0; i < len; i++)
+		if (buf[i] != buf_save[i])
+			break;
+	start = i;
+
+	for (i = len - 1; i >= 0; i--)
+		if (buf[i] != buf_save[i])
+			break;
+	end = i;
+
+	if (start > end) {
+		/* No modified register values present. */
+		return 0;
+	}
+
+	rc = qpnp_vreg_write(vreg, addr + start, &buf[start], end - start + 1);
+	if (!rc)
+		for (i = start; i <= end; i++)
+			buf_save[i] = buf[i];
+
+	return rc;
+}
+
+/*
+ * Perform a masked write to a PMIC register only if the new value differs
+ * from the last value written to the register.  This removes redundant
+ * register writing.
+ */
+static int qpnp_vreg_masked_write(struct qpnp_regulator *vreg, u16 addr, u8 val,
+		u8 mask, u8 *reg_save)
+{
+	int rc = 0;
+	u8 reg;
+
+	reg = (*reg_save & ~mask) | (val & mask);
+	if (reg != *reg_save) {
+		rc = qpnp_vreg_write(vreg, addr, &reg, 1);
+
+		if (rc) {
+			vreg_err(vreg, "write failed; addr=0x%03X, rc=%d\n",
+				addr, rc);
+		} else {
+			*reg_save = reg;
+		}
+	}
+
+	return rc;
+}
+
+/*
+ * Perform a masked read-modify-write to a PMIC register only if the new value
+ * differs from the value currently in the register.  This removes redundant
+ * register writing.
+ */
+static int qpnp_vreg_masked_read_write(struct qpnp_regulator *vreg, u16 addr,
+		u8 val, u8 mask)
+{
+	int rc;
+	u8 reg;
+
+	rc = qpnp_vreg_read(vreg, addr, &reg, 1);
+	if (rc) {
+		vreg_err(vreg, "read failed; addr=0x%03X, rc=%d\n", addr, rc);
+		return rc;
+	}
+
+	return qpnp_vreg_masked_write(vreg, addr, val, mask, &reg);
+}
+
+static int qpnp_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return (vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]
+		& QPNP_COMMON_ENABLE_MASK)
+			== QPNP_COMMON_ENABLE;
+}
+
+static int qpnp_regulator_common_enable(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_ENABLE,
+		QPNP_COMMON_ENABLE, QPNP_COMMON_ENABLE_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]);
+
+	if (rc)
+		vreg_err(vreg, "qpnp_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_ENABLE);
+
+	return rc;
+}
+
+static int qpnp_regulator_vs_enable(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	if (vreg->ocp_irq) {
+		vreg->ocp_count = 0;
+		vreg->vs_enable_time = ktime_get();
+	}
+
+	return qpnp_regulator_common_enable(rdev);
+}
+
+static int qpnp_regulator_common_disable(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc;
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_ENABLE,
+		QPNP_COMMON_DISABLE, QPNP_COMMON_ENABLE_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]);
+
+	if (rc)
+		vreg_err(vreg, "qpnp_vreg_masked_write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_DISABLE);
+
+	return rc;
+}
+
+/*
+ * Returns 1 if the voltage can be set in the current range, 0 if the voltage
+ * cannot be set in the current range, or errno if an error occurred.
+ */
+static int qpnp_regulator_select_voltage_same_range(struct qpnp_regulator *vreg,
+		int min_uV, int max_uV, int *range_sel, int *voltage_sel,
+		unsigned int *selector)
+{
+	struct qpnp_voltage_range *range = NULL;
+	int uV = min_uV;
+	int i;
+
+	*range_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE];
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (vreg->set_points->range[i].range_sel == *range_sel) {
+			range = &vreg->set_points->range[i];
+			break;
+		}
+	}
+
+	if (!range) {
+		/* Unknown range */
+		return 0;
+	}
+
+	if (uV < range->min_uV && max_uV >= range->min_uV)
+		uV = range->min_uV;
+
+	if (uV < range->min_uV || uV > range->max_uV) {
+		/* Current range doesn't support the requested voltage. */
+		return 0;
+	}
+
+	/*
+	 * Force uV to be an allowed set point by applying a ceiling function to
+	 * the uV value.
+	 */
+	*voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+	uV = *voltage_sel * range->step_uV + range->min_uV;
+
+	if (uV > max_uV) {
+		/*
+		 * No set point in the current voltage range is within the
+		 * requested min_uV to max_uV range.
+		 */
+		return 0;
+	}
+
+	*selector = 0;
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (uV >= vreg->set_points->range[i].set_point_min_uV
+		    && uV <= vreg->set_points->range[i].set_point_max_uV) {
+			*selector +=
+			    (uV - vreg->set_points->range[i].set_point_min_uV)
+				/ vreg->set_points->range[i].step_uV;
+			break;
+		}
+
+		*selector += vreg->set_points->range[i].n_voltages;
+	}
+
+	if (*selector >= vreg->set_points->n_voltages)
+		return 0;
+
+	return 1;
+}
+
+static int qpnp_regulator_select_voltage(struct qpnp_regulator *vreg,
+		int min_uV, int max_uV, int *range_sel, int *voltage_sel,
+		unsigned int *selector)
+{
+	struct qpnp_voltage_range *range;
+	int uV = min_uV;
+	int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+
+	/* Check if request voltage is outside of physically settable range. */
+	lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+	lim_max_uV =
+	  vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+	if (uV < lim_min_uV && max_uV >= lim_min_uV)
+		uV = lim_min_uV;
+
+	if (uV < lim_min_uV || uV > lim_max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] is outside possible v=[%d, %d]\n",
+			 min_uV, max_uV, lim_min_uV, lim_max_uV);
+		return -EINVAL;
+	}
+
+	/* Find the range which uV is inside of. */
+	for (i = vreg->set_points->count - 1; i > 0; i--) {
+		range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+		if (uV > range_max_uV && range_max_uV > 0)
+			break;
+	}
+
+	range_id = i;
+	range = &vreg->set_points->range[range_id];
+	*range_sel = range->range_sel;
+
+	/*
+	 * Force uV to be an allowed set point by applying a ceiling function to
+	 * the uV value.
+	 */
+	*voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+			/ range->step_uV;
+	uV = *voltage_sel * range->step_uV + range->min_uV;
+
+	if (uV > max_uV) {
+		vreg_err(vreg,
+			"request v=[%d, %d] cannot be met by any set point; "
+			"next set point: %d\n",
+			min_uV, max_uV, uV);
+		return -EINVAL;
+	}
+
+	*selector = 0;
+	for (i = 0; i < range_id; i++)
+		*selector += vreg->set_points->range[i].n_voltages;
+	*selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+	return 0;
+}
+
+static int qpnp_regulator_delay_for_slewing(struct qpnp_regulator *vreg,
+		int prev_voltage)
+{
+	int current_voltage;
+
+	/* Delay for voltage slewing if a step rate is specified. */
+	if (vreg->slew_rate && vreg->rdesc.ops->get_voltage) {
+		current_voltage = vreg->rdesc.ops->get_voltage(vreg->rdev);
+		if (current_voltage < 0) {
+			vreg_err(vreg, "could not get new voltage, rc=%d\n",
+				current_voltage);
+			return current_voltage;
+		}
+
+		udelay(DIV_ROUND_UP(abs(current_voltage - prev_voltage),
+					vreg->slew_rate));
+	}
+
+	return 0;
+}
+
+static int qpnp_regulator_common_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned int *selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc, range_sel, voltage_sel, voltage_old = 0;
+	u8 buf[2];
+
+	if (vreg->slew_rate && vreg->rdesc.ops->get_voltage) {
+		voltage_old = vreg->rdesc.ops->get_voltage(rdev);
+		if (voltage_old < 0) {
+			vreg_err(vreg, "could not get current voltage, rc=%d\n",
+				voltage_old);
+			return voltage_old;
+		}
+	}
+
+	/*
+	 * Favor staying in the current voltage range if possible.  This avoids
+	 * voltage spikes that occur when changing the voltage range.
+	 */
+	rc = qpnp_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+		&range_sel, &voltage_sel, selector);
+	if (rc == 0)
+		rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV,
+			&range_sel, &voltage_sel, selector);
+	if (rc < 0) {
+		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	buf[0] = range_sel;
+	buf[1] = voltage_sel;
+	if ((vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] != range_sel)
+	    && (vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET] == voltage_sel)) {
+		/* Handle latched range change. */
+		rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE,
+				buf, 2);
+		if (!rc) {
+			vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] = buf[0];
+			vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET] = buf[1];
+		}
+	} else {
+		/* Either write can be optimized away safely. */
+		rc = qpnp_vreg_write_optimized(vreg,
+			QPNP_COMMON_REG_VOLTAGE_RANGE, buf,
+			&vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE], 2);
+	}
+
+	if (rc) {
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	} else {
+		rc = qpnp_regulator_delay_for_slewing(vreg, voltage_old);
+		if (rc)
+			return rc;
+
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
+	}
+
+	return rc;
+}
+
+static int qpnp_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	struct qpnp_voltage_range *range = NULL;
+	int range_sel, voltage_sel, i;
+
+	range_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE];
+	voltage_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET];
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (vreg->set_points->range[i].range_sel == range_sel) {
+			range = &vreg->set_points->range[i];
+			break;
+		}
+	}
+
+	if (!range) {
+		vreg_err(vreg, "voltage unknown, range %d is invalid\n",
+			range_sel);
+		return VOLTAGE_UNKNOWN;
+	}
+
+	return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int qpnp_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned int *selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc, range_sel, voltage_sel;
+
+	rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+		&voltage_sel, selector);
+	if (rc) {
+		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Certain types of regulators do not have a range select register so
+	 * only voltage set register needs to be written.
+	 */
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_VOLTAGE_SET,
+	       voltage_sel, 0xFF, &vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET]);
+
+	if (rc)
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
+
+	return rc;
+}
+
+static int qpnp_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	struct qpnp_voltage_range *range = &vreg->set_points->range[0];
+	int voltage_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET];
+
+	return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int qpnp_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned int *selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc, range_sel, voltage_sel;
+
+	/*
+	 * Favor staying in the current voltage range if possible. This avoids
+	 * voltage spikes that occur when changing the voltage range.
+	 */
+	rc = qpnp_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+		&range_sel, &voltage_sel, selector);
+	if (rc == 0)
+		rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV,
+			&range_sel, &voltage_sel, selector);
+	if (rc < 0) {
+		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Calculate VSET based on range
+	 * In case of range 0: voltage_sel is a 7 bit value, can be written
+	 *			witout any modification.
+	 * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+	 *			[011].
+	 */
+	if (range_sel == 1)
+		voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_VOLTAGE_SET,
+	       voltage_sel, 0xFF, &vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET]);
+	if (rc) {
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	} else {
+		vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] = range_sel;
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
+	}
+
+	return rc;
+}
+
+static int qpnp_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	struct qpnp_voltage_range *range = NULL;
+	int range_sel, voltage_sel, i;
+
+	range_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE];
+	voltage_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET];
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (vreg->set_points->range[i].range_sel == range_sel) {
+			range = &vreg->set_points->range[i];
+			break;
+		}
+	}
+
+	if (!range) {
+		vreg_err(vreg, "voltage unknown, range %d is invalid\n",
+			range_sel);
+		return VOLTAGE_UNKNOWN;
+	}
+
+	if (range_sel == 1)
+		voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+	return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int qpnp_regulator_common_list_voltage(struct regulator_dev *rdev,
+			unsigned int selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int uV = 0;
+	int i;
+
+	if (selector >= vreg->set_points->n_voltages)
+		return 0;
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (selector < vreg->set_points->range[i].n_voltages) {
+			uV = selector * vreg->set_points->range[i].step_uV
+				+ vreg->set_points->range[i].set_point_min_uV;
+			break;
+		}
+
+		selector -= vreg->set_points->range[i].n_voltages;
+	}
+
+	return uV;
+}
+
+static int qpnp_regulator_common2_set_voltage(struct regulator_dev *rdev,
+		int min_uV, int max_uV, unsigned int *selector)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc, range_sel, voltage_sel, voltage_old = 0;
+	int voltage_uV, voltage_mV;
+	u8 buf[2];
+
+	if (vreg->slew_rate && vreg->rdesc.ops->get_voltage) {
+		voltage_old = vreg->rdesc.ops->get_voltage(rdev);
+		if (voltage_old < 0) {
+			vreg_err(vreg, "could not get current voltage, rc=%d\n",
+				voltage_old);
+			return voltage_old;
+		}
+	}
+
+	rc = qpnp_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+					   &voltage_sel, selector);
+	if (rc < 0) {
+		vreg_err(vreg, "could not set voltage, rc=%d\n", rc);
+		return rc;
+	}
+
+	voltage_uV = qpnp_regulator_common_list_voltage(rdev, *selector);
+	voltage_mV = voltage_uV / 1000;
+	buf[0] = voltage_mV & 0xFF;
+	buf[1] = (voltage_mV >> 8) & 0xFF;
+
+	if (vreg->ctrl_reg[QPNP_COMMON2_IDX_VOLTAGE_LSB] != buf[0]
+	    || vreg->ctrl_reg[QPNP_COMMON2_IDX_VOLTAGE_MSB] != buf[1]) {
+		/* MSB must always be written even if it is unchanged. */
+		rc = qpnp_vreg_write(vreg, QPNP_COMMON2_REG_VOLTAGE_LSB,
+				     buf, 2);
+		if (rc) {
+			vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+			return rc;
+		}
+
+		vreg->ctrl_reg[QPNP_COMMON2_IDX_VOLTAGE_LSB] = buf[0];
+		vreg->ctrl_reg[QPNP_COMMON2_IDX_VOLTAGE_MSB] = buf[1];
+
+		rc = qpnp_regulator_delay_for_slewing(vreg, voltage_old);
+		if (rc)
+			return rc;
+
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_VOLTAGE);
+	}
+
+	return rc;
+}
+
+static int qpnp_regulator_common2_get_voltage(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return (((int)vreg->ctrl_reg[QPNP_COMMON2_IDX_VOLTAGE_MSB] << 8)
+		| (int)vreg->ctrl_reg[QPNP_COMMON2_IDX_VOLTAGE_LSB]) * 1000;
+}
+
+static unsigned int qpnp_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return (vreg->ctrl_reg[QPNP_COMMON_IDX_MODE]
+		& QPNP_COMMON_MODE_HPM_MASK)
+			? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int qpnp_regulator_common_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	u8 val;
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	val = (mode == REGULATOR_MODE_NORMAL ? QPNP_COMMON_MODE_HPM_MASK : 0);
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_MODE, val,
+		QPNP_COMMON_MODE_HPM_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_MODE]);
+
+	if (rc)
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static unsigned int qpnp_regulator_common_get_optimum_mode(
+		struct regulator_dev *rdev, int input_uV, int output_uV,
+		int load_uA)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	unsigned int mode;
+
+	if (load_uA + vreg->system_load >= vreg->hpm_min_load)
+		mode = REGULATOR_MODE_NORMAL;
+	else
+		mode = REGULATOR_MODE_IDLE;
+
+	return mode;
+}
+
+static unsigned int qpnp_regulator_common2_get_mode(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->ctrl_reg[QPNP_COMMON2_IDX_MODE] == QPNP_COMMON2_MODE_HPM
+		? REGULATOR_MODE_NORMAL : REGULATOR_MODE_IDLE;
+}
+
+static int qpnp_regulator_common2_set_mode(struct regulator_dev *rdev,
+					unsigned int mode)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	int rc = 0;
+	u8 val = QPNP_COMMON2_MODE_HPM;
+
+	if (mode != REGULATOR_MODE_NORMAL && mode != REGULATOR_MODE_IDLE) {
+		vreg_err(vreg, "invalid mode: %u\n", mode);
+		return -EINVAL;
+	}
+
+	/*
+	 * Use init_mode as the low power mode unless it is equal to HPM.  This
+	 * ensures that AUTO mode is re-asserted after switching away from
+	 * forced HPM if it was configured initially.
+	 */
+	if (mode == REGULATOR_MODE_NORMAL)
+		val = QPNP_COMMON2_MODE_HPM;
+	else if (vreg->init_mode == QPNP_COMMON2_MODE_HPM)
+		val = QPNP_COMMON2_MODE_LPM;
+	else
+		val = vreg->init_mode;
+
+	rc = qpnp_vreg_write_optimized(vreg, QPNP_COMMON2_REG_MODE, &val,
+				&vreg->ctrl_reg[QPNP_COMMON2_IDX_MODE], 1);
+	if (rc)
+		vreg_err(vreg, "SPMI write failed, rc=%d\n", rc);
+	else
+		qpnp_vreg_show_state(rdev, QPNP_REGULATOR_ACTION_MODE);
+
+	return rc;
+}
+
+static int qpnp_regulator_common_enable_time(struct regulator_dev *rdev)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+
+	return vreg->enable_time;
+}
+
+static int qpnp_regulator_vs_clear_ocp(struct qpnp_regulator *vreg)
+{
+	int rc;
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_ENABLE,
+		QPNP_COMMON_DISABLE, QPNP_COMMON_ENABLE_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]);
+	if (rc)
+		vreg_err(vreg, "qpnp_vreg_masked_write failed, rc=%d\n", rc);
+
+	vreg->vs_enable_time = ktime_get();
+
+	rc = qpnp_vreg_masked_write(vreg, QPNP_COMMON_REG_ENABLE,
+		QPNP_COMMON_ENABLE, QPNP_COMMON_ENABLE_MASK,
+		&vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE]);
+	if (rc)
+		vreg_err(vreg, "qpnp_vreg_masked_write failed, rc=%d\n", rc);
+
+	if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_OCP) {
+		pr_info("%s: switch state toggled after OCP event\n",
+			vreg->rdesc.name);
+	}
+
+	return rc;
+}
+
+static void qpnp_regulator_vs_ocp_work(struct work_struct *work)
+{
+	struct delayed_work *dwork
+		= container_of(work, struct delayed_work, work);
+	struct qpnp_regulator *vreg
+		= container_of(dwork, struct qpnp_regulator, ocp_work);
+
+	qpnp_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t qpnp_regulator_vs_ocp_isr(int irq, void *data)
+{
+	struct qpnp_regulator *vreg = data;
+	ktime_t ocp_irq_time;
+	s64 ocp_trigger_delay_us;
+
+	ocp_irq_time = ktime_get();
+	ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+						vreg->vs_enable_time);
+
+	/*
+	 * Reset the OCP count if there is a large delay between switch enable
+	 * and when OCP triggers.  This is indicative of a hotplug event as
+	 * opposed to a fault.
+	 */
+	if (ocp_trigger_delay_us > QPNP_VS_OCP_FAULT_DELAY_US)
+		vreg->ocp_count = 0;
+
+	/* Wait for switch output to settle back to 0 V after OCP triggered. */
+	udelay(QPNP_VS_OCP_FALL_DELAY_US);
+
+	vreg->ocp_count++;
+
+	if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_OCP) {
+		pr_info("%s: VS OCP triggered, count = %d, delay = %lld us\n",
+			vreg->rdesc.name, vreg->ocp_count,
+			ocp_trigger_delay_us);
+	}
+
+	if (vreg->ocp_count == 1) {
+		/* Immediately clear the over current condition. */
+		qpnp_regulator_vs_clear_ocp(vreg);
+	} else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+		/* Schedule the over current clear task to run later. */
+		schedule_delayed_work(&vreg->ocp_work,
+			msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+	} else {
+		vreg_err(vreg, "OCP triggered %d times; no further retries\n",
+			vreg->ocp_count);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static const char * const qpnp_print_actions[] = {
+	[QPNP_REGULATOR_ACTION_INIT]	= "initial    ",
+	[QPNP_REGULATOR_ACTION_ENABLE]	= "enable     ",
+	[QPNP_REGULATOR_ACTION_DISABLE]	= "disable    ",
+	[QPNP_REGULATOR_ACTION_VOLTAGE]	= "set voltage",
+	[QPNP_REGULATOR_ACTION_MODE]	= "set mode   ",
+};
+
+static const char * const qpnp_common2_mode_label[] = {
+	[0]				= "RSV",
+	[1]				= "RSV",
+	[2]				= "RSV",
+	[QPNP_COMMON2_MODE_BYPASS]	= "BYP",
+	[QPNP_COMMON2_MODE_RETENTION]	= "RET",
+	[QPNP_COMMON2_MODE_LPM]		= "LPM",
+	[QPNP_COMMON2_MODE_AUTO]	= "AUTO",
+	[QPNP_COMMON2_MODE_HPM]		= "HPM",
+};
+
+static void qpnp_vreg_show_state(struct regulator_dev *rdev,
+				   enum qpnp_regulator_action action)
+{
+	struct qpnp_regulator *vreg = rdev_get_drvdata(rdev);
+	const char *action_label = qpnp_print_actions[action];
+	unsigned int mode = 0;
+	int uV = 0;
+	const char *mode_label = "";
+	enum qpnp_regulator_logical_type type;
+	const char *enable_label = "";
+	char pc_enable_label[5] = {'\0'};
+	char pc_mode_label[8] = {'\0'};
+	bool show_req, show_dupe, show_init, has_changed;
+	u8 en_reg, mode_reg;
+
+	/* Do not print unless appropriate flags are set. */
+	show_req = qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_REQUEST;
+	show_dupe = qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_DUPLICATE;
+	show_init = qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_INIT;
+	has_changed = vreg->write_count != vreg->prev_write_count;
+	if (!((show_init && action == QPNP_REGULATOR_ACTION_INIT)
+	      || (show_req && (has_changed || show_dupe)))) {
+		return;
+	}
+
+	vreg->prev_write_count = vreg->write_count;
+
+	type = vreg->logical_type;
+
+	if (vreg->rdesc.ops->is_enabled)
+		enable_label = vreg->rdesc.ops->is_enabled(rdev)
+				? "on " : "off";
+
+	if (vreg->rdesc.ops->get_voltage)
+		uV = vreg->rdesc.ops->get_voltage(rdev);
+
+	if (vreg->rdesc.ops->get_mode) {
+		mode = vreg->rdesc.ops->get_mode(rdev);
+		mode_label = mode == REGULATOR_MODE_NORMAL ? "HPM" : "LPM";
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_VS) {
+		en_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_ENABLE];
+		pc_enable_label[0] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN3_MASK ? '3' : '_';
+		pc_enable_label[1] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN2_MASK ? '2' : '_';
+		pc_enable_label[2] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN1_MASK ? '1' : '_';
+		pc_enable_label[3] =
+		     en_reg & QPNP_COMMON_ENABLE_FOLLOW_HW_EN0_MASK ? '0' : '_';
+	}
+
+	switch (type) {
+	case QPNP_REGULATOR_LOGICAL_TYPE_SMPS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+		pc_mode_label[2] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN3_MASK ? '3' : '_';
+		pc_mode_label[3] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN2_MASK ? '2' : '_';
+		pc_mode_label[4] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN1_MASK ? '1' : '_';
+		pc_mode_label[5] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN0_MASK ? '0' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, pc_en=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_enable_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_LDO:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_BYPASS_MASK        ? 'B' : '_';
+		pc_mode_label[2] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+		pc_mode_label[3] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN3_MASK ? '3' : '_';
+		pc_mode_label[4] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN2_MASK ? '2' : '_';
+		pc_mode_label[5] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN1_MASK ? '1' : '_';
+		pc_mode_label[6] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_HW_EN0_MASK ? '0' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, pc_en=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_enable_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_LN_LDO:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_BYPASS_MASK ? 'B' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_VS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+
+		pr_info("%s %-11s: %s, mode=%s, pc_en=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label,
+			mode_label, pc_enable_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_BOOST:
+		pr_info("%s %-11s: %s, v=%7d uV\n",
+			action_label, vreg->rdesc.name, enable_label, uV);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_BOOST_BYP:
+		pr_info("%s %-11s: %s, v=%7d uV\n",
+			action_label, vreg->rdesc.name, enable_label, uV);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_AUTO_MASK          ? 'A' : '_';
+
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS:
+	case QPNP_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		pc_mode_label[0] =
+		     mode_reg & QPNP_COMMON_MODE_BYPASS_MASK        ? 'B' : '_';
+		pc_mode_label[1] =
+		     mode_reg & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK  ? 'W' : '_';
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s, alt_mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label, pc_mode_label);
+		break;
+	case QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2:
+		mode_reg = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+		mode_label = qpnp_common2_mode_label[mode_reg
+						     & QPNP_COMMON2_MODE_MASK];
+		pr_info("%s %-11s: %s, v=%7d uV, mode=%s\n",
+			action_label, vreg->rdesc.name, enable_label, uV,
+			mode_label);
+		break;
+	default:
+		break;
+	}
+}
+
+static struct regulator_ops qpnp_smps_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ldo_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ln_ldo_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_vs_ops = {
+	.enable			= qpnp_regulator_vs_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_boost_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_single_range_set_voltage,
+	.get_voltage		= qpnp_regulator_single_range_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ftsmps_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common_set_voltage,
+	.get_voltage		= qpnp_regulator_common_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ult_lo_smps_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_ult_lo_smps_set_voltage,
+	.get_voltage		= qpnp_regulator_ult_lo_smps_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ult_ho_smps_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_single_range_set_voltage,
+	.get_voltage		= qpnp_regulator_single_range_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ult_ldo_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_single_range_set_voltage,
+	.get_voltage		= qpnp_regulator_single_range_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common_set_mode,
+	.get_mode		= qpnp_regulator_common_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+static struct regulator_ops qpnp_ftsmps426_ops = {
+	.enable			= qpnp_regulator_common_enable,
+	.disable		= qpnp_regulator_common_disable,
+	.is_enabled		= qpnp_regulator_common_is_enabled,
+	.set_voltage		= qpnp_regulator_common2_set_voltage,
+	.get_voltage		= qpnp_regulator_common2_get_voltage,
+	.list_voltage		= qpnp_regulator_common_list_voltage,
+	.set_mode		= qpnp_regulator_common2_set_mode,
+	.get_mode		= qpnp_regulator_common2_get_mode,
+	.get_optimum_mode	= qpnp_regulator_common_get_optimum_mode,
+	.enable_time		= qpnp_regulator_common_enable_time,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct qpnp_regulator_mapping supported_regulators[] = {
+	/*           type subtype dig_min dig_max ltype ops setpoints hpm_min */
+	QPNP_VREG_MAP(BUCK,  GP_CTL,   0, INF, SMPS,   smps,   smps,   100000),
+	QPNP_VREG_MAP(LDO,   N300,     0, INF, LDO,    ldo,    nldo1,   10000),
+	QPNP_VREG_MAP(LDO,   N600,     0,   0, LDO,    ldo,    nldo2,   10000),
+	QPNP_VREG_MAP(LDO,   N1200,    0,   0, LDO,    ldo,    nldo2,   10000),
+	QPNP_VREG_MAP(LDO,   N600,     1, INF, LDO,    ldo,    nldo3,   10000),
+	QPNP_VREG_MAP(LDO,   N1200,    1, INF, LDO,    ldo,    nldo3,   10000),
+	QPNP_VREG_MAP(LDO,   N600_ST,  0,   0, LDO,    ldo,    nldo2,   10000),
+	QPNP_VREG_MAP(LDO,   N1200_ST, 0,   0, LDO,    ldo,    nldo2,   10000),
+	QPNP_VREG_MAP(LDO,   N600_ST,  1, INF, LDO,    ldo,    nldo3,   10000),
+	QPNP_VREG_MAP(LDO,   N1200_ST, 1, INF, LDO,    ldo,    nldo3,   10000),
+	QPNP_VREG_MAP(LDO,   P50,      0, INF, LDO,    ldo,    pldo,     5000),
+	QPNP_VREG_MAP(LDO,   P150,     0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   P300,     0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   P600,     0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   P1200,    0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   LN,       0, INF, LN_LDO, ln_ldo, ln_ldo,      0),
+	QPNP_VREG_MAP(LDO,   LV_P50,   0, INF, LDO,    ldo,    pldo,     5000),
+	QPNP_VREG_MAP(LDO,   LV_P150,  0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   LV_P300,  0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   LV_P600,  0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(LDO,   LV_P1200, 0, INF, LDO,    ldo,    pldo,    10000),
+	QPNP_VREG_MAP(VS,    LV100,    0, INF, VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,    LV300,    0, INF, VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,    MV300,    0, INF, VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,    MV500,    0, INF, VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,    HDMI,     0, INF, VS,     vs,     none,        0),
+	QPNP_VREG_MAP(VS,    OTG,      0, INF, VS,     vs,     none,        0),
+	QPNP_VREG_MAP(BOOST, 5V_BOOST, 0, INF, BOOST,  boost,  boost,       0),
+	QPNP_VREG_MAP(FTS,   FTS_CTL,  0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+	QPNP_VREG_MAP(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5,
+								       100000),
+	QPNP_VREG_MAP(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+	QPNP_VREG_MAP(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+							ult_lo_smps,   100000),
+	QPNP_VREG_MAP(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+							ult_lo_smps,   100000),
+	QPNP_VREG_MAP(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+							ult_lo_smps,   100000),
+	QPNP_VREG_MAP(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+							ult_ho_smps,   100000),
+	QPNP_VREG_MAP(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, LV_P150,  0, INF, ULT_LDO, ult_ldo, ult_pldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, LV_P300,  0, INF, ULT_LDO, ult_ldo, ult_pldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, P600,     0, INF, ULT_LDO, ult_ldo, ult_pldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, P150,     0, INF, ULT_LDO, ult_ldo, ult_pldo,
+									10000),
+	QPNP_VREG_MAP(ULT_LDO, P50,     0, INF, ULT_LDO, ult_ldo, ult_pldo,
+									 5000),
+	QPNP_VREG_MAP(FTS,     FTS426,  0, INF, FTSMPS2, ftsmps426, ftsmps426,
+								       100000),
+};
+
+static int qpnp_regulator_match(struct qpnp_regulator *vreg)
+{
+	const struct qpnp_regulator_mapping *mapping;
+	struct device_node *node = vreg->pdev->dev.of_node;
+	int rc, i;
+	u32 type_reg[2], dig_major_rev;
+	u8 version[QPNP_COMMON_REG_SUBTYPE - QPNP_COMMON_REG_DIG_MAJOR_REV + 1];
+	u8 type, subtype;
+
+	rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_DIG_MAJOR_REV, version,
+		ARRAY_SIZE(version));
+	if (rc) {
+		vreg_err(vreg, "could not read version registers, rc=%d\n", rc);
+		return rc;
+	}
+	dig_major_rev	= version[QPNP_COMMON_REG_DIG_MAJOR_REV
+					- QPNP_COMMON_REG_DIG_MAJOR_REV];
+	type		= version[QPNP_COMMON_REG_TYPE
+					- QPNP_COMMON_REG_DIG_MAJOR_REV];
+	subtype		= version[QPNP_COMMON_REG_SUBTYPE
+					- QPNP_COMMON_REG_DIG_MAJOR_REV];
+
+	/*
+	 * Override type and subtype register values if qcom,force-type is
+	 * present in the device tree node.
+	 */
+	rc = of_property_read_u32_array(node, "qcom,force-type", type_reg, 2);
+	if (!rc) {
+		type = type_reg[0];
+		subtype = type_reg[1];
+	}
+
+	rc = -ENODEV;
+	for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+		mapping = &supported_regulators[i];
+		if (mapping->type == type && mapping->subtype == subtype
+		    && mapping->revision_min <= dig_major_rev
+		    && mapping->revision_max >= dig_major_rev) {
+			vreg->logical_type	= mapping->logical_type;
+			vreg->set_points	= mapping->set_points;
+			vreg->hpm_min_load	= mapping->hpm_min_load;
+			vreg->rdesc.ops		= mapping->ops;
+			vreg->rdesc.n_voltages
+				= mapping->set_points->n_voltages;
+			rc = 0;
+			break;
+		}
+	}
+
+	if (rc)
+		vreg_err(vreg, "unsupported regulator: type=0x%02X, subtype=0x%02X, dig major rev=0x%02X\n",
+			type, subtype, dig_major_rev);
+
+	return rc;
+}
+
+static int qpnp_regulator_ftsmps_init_slew_rate(struct qpnp_regulator *vreg)
+{
+	int rc;
+	u8 reg = 0;
+	int step = 0, delay, i, range_sel;
+	struct qpnp_voltage_range *range = NULL;
+
+	rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_STEP_CTRL, &reg, 1);
+	if (rc) {
+		vreg_err(vreg, "spmi read failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	range_sel = vreg->ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE];
+
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (vreg->set_points->range[i].range_sel == range_sel) {
+			range = &vreg->set_points->range[i];
+			break;
+		}
+	}
+
+	if (!range) {
+		vreg_err(vreg, "range %d is invalid\n", range_sel);
+		return -EINVAL;
+	}
+
+	step = (reg & QPNP_FTSMPS_STEP_CTRL_STEP_MASK)
+		>> QPNP_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+	delay = (reg & QPNP_FTSMPS_STEP_CTRL_DELAY_MASK)
+		>> QPNP_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+	/* slew_rate has units of uV/us. */
+	vreg->slew_rate = QPNP_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+
+	vreg->slew_rate /= 1000 * (QPNP_FTSMPS_STEP_DELAY << delay);
+
+	vreg->slew_rate = vreg->slew_rate * QPNP_FTSMPS_STEP_MARGIN_NUM
+				/ QPNP_FTSMPS_STEP_MARGIN_DEN;
+
+	/* Ensure that the slew rate is greater than 0. */
+	vreg->slew_rate = max(vreg->slew_rate, 1);
+
+	return rc;
+}
+
+static int qpnp_regulator_ftsmps2_init_slew_rate(struct qpnp_regulator *vreg)
+{
+	struct qpnp_voltage_range *range = NULL;
+	int i, rc, delay;
+	u8 reg = 0;
+
+	rc = qpnp_vreg_read(vreg, QPNP_COMMON2_REG_STEP_CTRL, &reg, 1);
+	if (rc) {
+		vreg_err(vreg, "spmi read failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/*
+	 * Regulators using the common #2 register layout do not have a voltage
+	 * range select register.  Choose the lowest possible step size to be
+	 * conservative in the slew rate calculation.
+	 */
+	for (i = 0; i < vreg->set_points->count; i++) {
+		if (!range || vreg->set_points->range[i].step_uV
+				< range->step_uV)
+			range = &vreg->set_points->range[i];
+	}
+
+	if (!range) {
+		vreg_err(vreg, "range is invalid\n");
+		return -EINVAL;
+	}
+
+	delay = (reg & QPNP_FTSMPS2_STEP_CTRL_DELAY_MASK)
+		>> QPNP_FTSMPS2_STEP_CTRL_DELAY_SHIFT;
+
+	/* slew_rate has units of uV/us. */
+	vreg->slew_rate = QPNP_FTSMPS2_CLOCK_RATE * range->step_uV;
+	vreg->slew_rate /= 1000 * (QPNP_FTSMPS2_STEP_DELAY << delay);
+	vreg->slew_rate = vreg->slew_rate * QPNP_FTSMPS2_STEP_MARGIN_NUM
+				/ QPNP_FTSMPS2_STEP_MARGIN_DEN;
+
+	/* Ensure that the slew rate is greater than 0. */
+	vreg->slew_rate = max(vreg->slew_rate, 1);
+
+	return rc;
+}
+
+static int qpnp_regulator_init_registers(struct qpnp_regulator *vreg,
+				struct qpnp_regulator_platform_data *pdata)
+{
+	int rc, i;
+	enum qpnp_regulator_logical_type type;
+	u8 ctrl_reg[8], reg, mask;
+
+	type = vreg->logical_type;
+
+	rc = qpnp_vreg_read(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE,
+			    vreg->ctrl_reg, 8);
+	if (rc) {
+		vreg_err(vreg, "spmi read failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ctrl_reg); i++)
+		ctrl_reg[i] = vreg->ctrl_reg[i];
+
+	/* Set up enable pin control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_VS)
+	    && !(pdata->pin_ctrl_enable
+			& QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_ENABLE] &=
+			~QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_ENABLE] |=
+		    pdata->pin_ctrl_enable & QPNP_COMMON_ENABLE_FOLLOW_ALL_MASK;
+	}
+
+	/* Set up HPM control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_VS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS)
+	    && (pdata->hpm_enable != QPNP_REGULATOR_USE_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &= ~QPNP_COMMON_MODE_HPM_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+		     (pdata->hpm_enable ? QPNP_COMMON_MODE_HPM_MASK : 0);
+	}
+
+	/* Set up auto mode control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_VS
+	     || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS)
+	    && (pdata->auto_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_AUTO_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+		     (pdata->auto_mode_enable ? QPNP_COMMON_MODE_AUTO_MASK : 0);
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2) {
+		if (pdata->hpm_enable == QPNP_REGULATOR_ENABLE)
+			ctrl_reg[QPNP_COMMON2_IDX_MODE]
+				= QPNP_COMMON2_MODE_HPM;
+		else if (pdata->auto_mode_enable == QPNP_REGULATOR_ENABLE)
+			ctrl_reg[QPNP_COMMON2_IDX_MODE]
+				= QPNP_COMMON2_MODE_AUTO;
+		else if (pdata->hpm_enable == QPNP_REGULATOR_DISABLE
+			 && ctrl_reg[QPNP_COMMON2_IDX_MODE]
+					== QPNP_COMMON2_MODE_HPM)
+			ctrl_reg[QPNP_COMMON2_IDX_MODE]
+				= QPNP_COMMON2_MODE_LPM;
+		else if (pdata->auto_mode_enable == QPNP_REGULATOR_DISABLE
+			 && ctrl_reg[QPNP_COMMON2_IDX_MODE]
+					== QPNP_COMMON2_MODE_AUTO)
+			ctrl_reg[QPNP_COMMON2_IDX_MODE]
+				= QPNP_COMMON2_MODE_LPM;
+	}
+
+	/* Set up mode pin control. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO)
+		&& !(pdata->pin_ctrl_hpm
+			& QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_FOLLOW_ALL_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+			pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_ALL_MASK;
+	}
+
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS
+	   && !(pdata->pin_ctrl_hpm & QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+		       pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK;
+	}
+
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS
+		|| type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS
+		|| type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO)
+		&& !(pdata->pin_ctrl_hpm
+			& QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT)) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+		       pdata->pin_ctrl_hpm & QPNP_COMMON_MODE_FOLLOW_AWAKE_MASK;
+	}
+
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LN_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO)
+	      && pdata->bypass_mode_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		ctrl_reg[QPNP_COMMON_IDX_MODE] &=
+			~QPNP_COMMON_MODE_BYPASS_MASK;
+		ctrl_reg[QPNP_COMMON_IDX_MODE] |=
+			(pdata->bypass_mode_enable
+				? QPNP_COMMON_MODE_BYPASS_MASK : 0);
+	}
+
+	/* Set boost current limit. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST_BYP)
+		&& pdata->boost_current_limit
+			!= QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT) {
+		reg = pdata->boost_current_limit;
+		mask = QPNP_BOOST_CURRENT_LIMIT_MASK;
+		rc = qpnp_vreg_masked_read_write(vreg,
+			(type == QPNP_REGULATOR_LOGICAL_TYPE_BOOST
+				? QPNP_BOOST_REG_CURRENT_LIMIT
+				: QPNP_BOOST_BYP_REG_CURRENT_LIMIT),
+			reg, mask);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Write back any control register values that were modified. */
+	rc = qpnp_vreg_write_optimized(vreg, QPNP_COMMON_REG_VOLTAGE_RANGE,
+		ctrl_reg, vreg->ctrl_reg, 8);
+	if (rc) {
+		vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	/* Setup initial range for ULT_LO_SMPS */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS) {
+		ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_RANGE] =
+			(ctrl_reg[QPNP_COMMON_IDX_VOLTAGE_SET]
+			 < ULT_SMPS_RANGE_SPLIT) ? 0 : 1;
+	}
+
+	/* Set pull down. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_VS)
+	    && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		reg = pdata->pull_down_enable
+			? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0;
+		rc = qpnp_vreg_write(vreg, QPNP_COMMON_REG_PULL_DOWN, &reg, 1);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2)
+	    && pdata->pull_down_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		/* FTSMPS has other bits in the pull down control register. */
+		reg = pdata->pull_down_enable
+			? QPNP_COMMON_PULL_DOWN_ENABLE_MASK : 0;
+		rc = qpnp_vreg_masked_read_write(vreg,
+			QPNP_COMMON_REG_PULL_DOWN, reg,
+			QPNP_COMMON_PULL_DOWN_ENABLE_MASK);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Set soft start for LDO. */
+	if ((type == QPNP_REGULATOR_LOGICAL_TYPE_LDO
+	    || type == QPNP_REGULATOR_LOGICAL_TYPE_ULT_LDO)
+	    && pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+		reg = pdata->soft_start_enable
+			? QPNP_LDO_SOFT_START_ENABLE_MASK : 0;
+		rc = qpnp_vreg_write(vreg, QPNP_LDO_REG_SOFT_START, &reg, 1);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+	}
+
+	/* Set soft start strength and over current protection for VS. */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_VS) {
+		reg = 0;
+		mask = 0;
+		if (pdata->soft_start_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+			reg |= pdata->soft_start_enable
+				? QPNP_VS_SOFT_START_ENABLE_MASK : 0;
+			mask |= QPNP_VS_SOFT_START_ENABLE_MASK;
+		}
+		if (pdata->vs_soft_start_strength
+				!= QPNP_VS_SOFT_START_STR_HW_DEFAULT) {
+			reg |= pdata->vs_soft_start_strength
+				& QPNP_VS_SOFT_START_SEL_MASK;
+			mask |= QPNP_VS_SOFT_START_SEL_MASK;
+		}
+		rc = qpnp_vreg_masked_read_write(vreg, QPNP_VS_REG_SOFT_START,
+						 reg, mask);
+		if (rc) {
+			vreg_err(vreg, "spmi write failed, rc=%d\n", rc);
+			return rc;
+		}
+
+		if (pdata->ocp_enable != QPNP_REGULATOR_USE_HW_DEFAULT) {
+			reg = pdata->ocp_enable ? QPNP_VS_OCP_NO_OVERRIDE
+						: QPNP_VS_OCP_OVERRIDE;
+			rc = qpnp_vreg_write(vreg, QPNP_VS_REG_OCP, &reg, 1);
+			if (rc) {
+				vreg_err(vreg, "spmi write failed, rc=%d\n",
+					rc);
+				return rc;
+			}
+		}
+	}
+
+	/* Calculate the slew rate for FTSMPS regulators. */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+		rc = qpnp_regulator_ftsmps_init_slew_rate(vreg);
+		if (rc) {
+			vreg_err(vreg, "failed to initialize step rate, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	/* Calculate the slew rate for FTSMPS2 regulators. */
+	if (type == QPNP_REGULATOR_LOGICAL_TYPE_FTSMPS2) {
+		rc = qpnp_regulator_ftsmps2_init_slew_rate(vreg);
+		if (rc) {
+			vreg_err(vreg, "failed to initialize step rate, rc=%d\n",
+				 rc);
+			return rc;
+		}
+	}
+
+	vreg->init_mode = vreg->ctrl_reg[QPNP_COMMON_IDX_MODE];
+
+	return rc;
+}
+
+/* Fill in pdata elements based on values found in device tree. */
+static int qpnp_regulator_get_dt_config(struct platform_device *pdev,
+				struct qpnp_regulator_platform_data *pdata)
+{
+	unsigned int base;
+	struct device_node *node = pdev->dev.of_node;
+	int rc = 0;
+
+	pdata->init_data.constraints.input_uV
+		= pdata->init_data.constraints.max_uV;
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		return rc;
+	}
+	pdata->base_addr = base;
+
+	/* OCP IRQ is optional so ignore get errors. */
+	pdata->ocp_irq = platform_get_irq_byname(pdev, "ocp");
+	if (pdata->ocp_irq < 0)
+		pdata->ocp_irq = 0;
+
+	/*
+	 * Initialize configuration parameters to use hardware default in case
+	 * no value is specified via device tree.
+	 */
+	pdata->auto_mode_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->bypass_mode_enable	= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->ocp_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->pull_down_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->soft_start_enable	= QPNP_REGULATOR_USE_HW_DEFAULT;
+	pdata->boost_current_limit	= QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT;
+	pdata->pin_ctrl_enable	    = QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT;
+	pdata->pin_ctrl_hpm	    = QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT;
+	pdata->vs_soft_start_strength	= QPNP_VS_SOFT_START_STR_HW_DEFAULT;
+	pdata->hpm_enable		= QPNP_REGULATOR_USE_HW_DEFAULT;
+
+	/* These bindings are optional, so it is okay if they are not found. */
+	of_property_read_u32(node, "qcom,auto-mode-enable",
+		&pdata->auto_mode_enable);
+	of_property_read_u32(node, "qcom,bypass-mode-enable",
+		&pdata->bypass_mode_enable);
+	of_property_read_u32(node, "qcom,ocp-enable", &pdata->ocp_enable);
+	of_property_read_u32(node, "qcom,ocp-max-retries",
+		&pdata->ocp_max_retries);
+	of_property_read_u32(node, "qcom,ocp-retry-delay",
+		&pdata->ocp_retry_delay_ms);
+	of_property_read_u32(node, "qcom,pull-down-enable",
+		&pdata->pull_down_enable);
+	of_property_read_u32(node, "qcom,soft-start-enable",
+		&pdata->soft_start_enable);
+	of_property_read_u32(node, "qcom,boost-current-limit",
+		&pdata->boost_current_limit);
+	of_property_read_u32(node, "qcom,pin-ctrl-enable",
+		&pdata->pin_ctrl_enable);
+	of_property_read_u32(node, "qcom,pin-ctrl-hpm", &pdata->pin_ctrl_hpm);
+	of_property_read_u32(node, "qcom,hpm-enable", &pdata->hpm_enable);
+	of_property_read_u32(node, "qcom,vs-soft-start-strength",
+		&pdata->vs_soft_start_strength);
+	of_property_read_u32(node, "qcom,system-load", &pdata->system_load);
+	of_property_read_u32(node, "qcom,enable-time", &pdata->enable_time);
+
+	return rc;
+}
+
+static const struct of_device_id spmi_match_table[];
+
+#define MAX_NAME_LEN	127
+
+static int qpnp_regulator_probe(struct platform_device *pdev)
+{
+	struct regulator_config reg_config = {};
+	struct qpnp_regulator_platform_data *pdata;
+	struct qpnp_regulator *vreg;
+	struct regulator_desc *rdesc;
+	struct qpnp_regulator_platform_data of_pdata;
+	struct regulator_init_data *init_data;
+	char *reg_name;
+	int rc;
+	bool is_dt;
+
+	vreg = kzalloc(sizeof(struct qpnp_regulator), GFP_KERNEL);
+	if (!vreg)
+		return -ENOMEM;
+
+	vreg->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!vreg->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	is_dt = of_match_device(spmi_match_table, &pdev->dev);
+
+	/* Check if device tree is in use. */
+	if (is_dt) {
+		init_data = of_get_regulator_init_data(&pdev->dev,
+						       pdev->dev.of_node,
+						       &vreg->rdesc);
+		if (!init_data) {
+			dev_err(&pdev->dev, "%s: unable to allocate memory\n",
+					__func__);
+			kfree(vreg);
+			return -ENOMEM;
+		}
+		memset(&of_pdata, 0,
+			sizeof(struct qpnp_regulator_platform_data));
+		memcpy(&of_pdata.init_data, init_data,
+			sizeof(struct regulator_init_data));
+
+		if (of_get_property(pdev->dev.of_node, "parent-supply", NULL))
+			of_pdata.init_data.supply_regulator = "parent";
+
+		rc = qpnp_regulator_get_dt_config(pdev, &of_pdata);
+		if (rc) {
+			dev_err(&pdev->dev, "%s: DT parsing failed, rc=%d\n",
+					__func__, rc);
+			kfree(vreg);
+			return -ENOMEM;
+		}
+
+		pdata = &of_pdata;
+	} else {
+		pdata = pdev->dev.platform_data;
+	}
+
+	if (pdata == NULL) {
+		dev_err(&pdev->dev, "%s: no platform data specified\n",
+			__func__);
+		kfree(vreg);
+		return -EINVAL;
+	}
+
+	vreg->pdev		= pdev;
+	vreg->prev_write_count	= -1;
+	vreg->write_count	= 0;
+	vreg->base_addr		= pdata->base_addr;
+	vreg->enable_time	= pdata->enable_time;
+	vreg->system_load	= pdata->system_load;
+	vreg->ocp_enable	= pdata->ocp_enable;
+	vreg->ocp_irq		= pdata->ocp_irq;
+	vreg->ocp_max_retries	= pdata->ocp_max_retries;
+	vreg->ocp_retry_delay_ms = pdata->ocp_retry_delay_ms;
+
+	if (vreg->ocp_max_retries == 0)
+		vreg->ocp_max_retries = QPNP_VS_OCP_DEFAULT_MAX_RETRIES;
+	if (vreg->ocp_retry_delay_ms == 0)
+		vreg->ocp_retry_delay_ms = QPNP_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+	rdesc			= &vreg->rdesc;
+	rdesc->id		= to_spmi_device(pdev->dev.parent)->ctrl->nr;
+	rdesc->owner		= THIS_MODULE;
+	rdesc->type		= REGULATOR_VOLTAGE;
+
+	reg_name = kzalloc(strnlen(pdata->init_data.constraints.name,
+				MAX_NAME_LEN) + 1, GFP_KERNEL);
+	if (!reg_name) {
+		kfree(vreg);
+		return -ENOMEM;
+	}
+	strlcpy(reg_name, pdata->init_data.constraints.name,
+		strnlen(pdata->init_data.constraints.name, MAX_NAME_LEN) + 1);
+	rdesc->name = reg_name;
+
+	dev_set_drvdata(&pdev->dev, vreg);
+
+	rc = qpnp_regulator_match(vreg);
+	if (rc)
+		goto bail;
+
+	if (is_dt && rdesc->ops) {
+		/* Fill in ops and mode masks when using device tree. */
+		if (rdesc->ops->enable)
+			pdata->init_data.constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_STATUS;
+		if (rdesc->ops->get_voltage)
+			pdata->init_data.constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_VOLTAGE;
+		if (rdesc->ops->get_mode) {
+			pdata->init_data.constraints.valid_ops_mask
+				|= REGULATOR_CHANGE_MODE
+				| REGULATOR_CHANGE_DRMS;
+			pdata->init_data.constraints.valid_modes_mask
+				= REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+		}
+	}
+
+	rc = qpnp_regulator_init_registers(vreg, pdata);
+	if (rc) {
+		vreg_err(vreg, "common initialization failed, rc=%d\n", rc);
+		goto bail;
+	}
+
+	if (vreg->logical_type != QPNP_REGULATOR_LOGICAL_TYPE_VS)
+		vreg->ocp_irq = 0;
+
+	if (vreg->ocp_irq) {
+		rc = devm_request_irq(&pdev->dev, vreg->ocp_irq,
+			qpnp_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+			vreg);
+		if (rc < 0) {
+			vreg_err(vreg, "failed to request irq %d, rc=%d\n",
+				vreg->ocp_irq, rc);
+			goto bail;
+		}
+
+		INIT_DELAYED_WORK(&vreg->ocp_work, qpnp_regulator_vs_ocp_work);
+	}
+
+	reg_config.dev = &pdev->dev;
+	reg_config.init_data = &pdata->init_data;
+	reg_config.driver_data = vreg;
+	reg_config.of_node = pdev->dev.of_node;
+	vreg->rdev = regulator_register(rdesc, &reg_config);
+	if (IS_ERR(vreg->rdev)) {
+		rc = PTR_ERR(vreg->rdev);
+		if (rc != -EPROBE_DEFER)
+			vreg_err(vreg, "regulator_register failed, rc=%d\n",
+				rc);
+		goto cancel_ocp_work;
+	}
+
+	if (qpnp_vreg_debug_mask & QPNP_VREG_DEBUG_INIT && vreg->slew_rate)
+		pr_info("%-11s: step rate=%d uV/us\n", vreg->rdesc.name,
+			vreg->slew_rate);
+
+	qpnp_vreg_show_state(vreg->rdev, QPNP_REGULATOR_ACTION_INIT);
+
+	return 0;
+
+cancel_ocp_work:
+	if (vreg->ocp_irq)
+		cancel_delayed_work_sync(&vreg->ocp_work);
+bail:
+	if (rc && rc != -EPROBE_DEFER)
+		vreg_err(vreg, "probe failed, rc=%d\n", rc);
+
+	kfree(vreg->rdesc.name);
+	kfree(vreg);
+
+	return rc;
+}
+
+static int qpnp_regulator_remove(struct platform_device *pdev)
+{
+	struct qpnp_regulator *vreg;
+
+	vreg = dev_get_drvdata(&pdev->dev);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	if (vreg) {
+		regulator_unregister(vreg->rdev);
+		if (vreg->ocp_irq)
+			cancel_delayed_work_sync(&vreg->ocp_work);
+		kfree(vreg->rdesc.name);
+		kfree(vreg);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{ .compatible = QPNP_REGULATOR_DRIVER_NAME, },
+	{}
+};
+
+static const struct platform_device_id qpnp_regulator_id[] = {
+	{ QPNP_REGULATOR_DRIVER_NAME, 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(spmi, qpnp_regulator_id);
+
+static struct platform_driver qpnp_regulator_driver = {
+	.driver		= {
+		.name		= QPNP_REGULATOR_DRIVER_NAME,
+		.of_match_table	= spmi_match_table,
+		.owner		= THIS_MODULE,
+	},
+	.probe		= qpnp_regulator_probe,
+	.remove		= qpnp_regulator_remove,
+	.id_table	= qpnp_regulator_id,
+};
+
+/*
+ * Pre-compute the number of set points available for each regulator type to
+ * avoid unnecessary calculations later in runtime.
+ */
+static void qpnp_regulator_set_point_init(void)
+{
+	struct qpnp_voltage_set_points **set_points;
+	int i, j, temp;
+
+	set_points = all_set_points;
+
+	for (i = 0; i < ARRAY_SIZE(all_set_points); i++) {
+		temp = 0;
+		for (j = 0; j < all_set_points[i]->count; j++) {
+			all_set_points[i]->range[j].n_voltages
+				= (all_set_points[i]->range[j].set_point_max_uV
+				 - all_set_points[i]->range[j].set_point_min_uV)
+				   / all_set_points[i]->range[j].step_uV + 1;
+			if (all_set_points[i]->range[j].set_point_max_uV == 0)
+				all_set_points[i]->range[j].n_voltages = 0;
+			temp += all_set_points[i]->range[j].n_voltages;
+		}
+		all_set_points[i]->n_voltages = temp;
+	}
+}
+
+/**
+ * qpnp_regulator_init() - register spmi driver for qpnp-regulator
+ *
+ * This initialization function should be called in systems in which driver
+ * registration ordering must be controlled precisely.
+ */
+int __init qpnp_regulator_init(void)
+{
+	static bool has_registered;
+
+	if (has_registered)
+		return 0;
+	has_registered = true;
+
+	qpnp_regulator_set_point_init();
+
+	return platform_driver_register(&qpnp_regulator_driver);
+}
+EXPORT_SYMBOL(qpnp_regulator_init);
+
+static void __exit qpnp_regulator_exit(void)
+{
+	platform_driver_unregister(&qpnp_regulator_driver);
+}
+
+MODULE_DESCRIPTION("QPNP PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+
+arch_initcall(qpnp_regulator_init);
+module_exit(qpnp_regulator_exit);
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e859d14..6e5c443 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -1589,6 +1589,15 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called rtc-pm8xxx.
 
+config RTC_DRV_QPNP
+	tristate "Qualcomm Technologies, Inc. QPNP PMIC RTC"
+	depends on SPMI
+	help
+	  This enables support for the RTC found on Qualcomm Technologies, Inc.
+	  QPNP PMIC chips.  This driver supports using the PMIC RTC peripheral
+	  to wake a mobile device up from suspend or to wake it up from power-
+	  off.
+
 config RTC_DRV_TEGRA
 	tristate "NVIDIA Tegra Internal RTC driver"
 	depends on ARCH_TEGRA || COMPILE_TEST
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 1ac694a..f282f73 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -120,6 +120,7 @@
 obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
 obj-$(CONFIG_RTC_DRV_PUV3)	+= rtc-puv3.o
 obj-$(CONFIG_RTC_DRV_PXA)	+= rtc-pxa.o
+obj-$(CONFIG_RTC_DRV_QPNP)	+= qpnp-rtc.o
 obj-$(CONFIG_RTC_DRV_R9701)	+= rtc-r9701.o
 obj-$(CONFIG_RTC_DRV_RC5T583)	+= rtc-rc5t583.o
 obj-$(CONFIG_RTC_DRV_RK808)	+= rtc-rk808.o
diff --git a/drivers/rtc/qpnp-rtc.c b/drivers/rtc/qpnp-rtc.c
new file mode 100644
index 0000000..a2c004e
--- /dev/null
+++ b/drivers/rtc/qpnp-rtc.c
@@ -0,0 +1,717 @@
+/* Copyright (c) 2012-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/init.h>
+#include <linux/rtc.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/alarmtimer.h>
+
+/* RTC/ALARM Register offsets */
+#define REG_OFFSET_ALARM_RW	0x40
+#define REG_OFFSET_ALARM_CTRL1	0x46
+#define REG_OFFSET_ALARM_CTRL2	0x48
+#define REG_OFFSET_RTC_WRITE	0x40
+#define REG_OFFSET_RTC_CTRL	0x46
+#define REG_OFFSET_RTC_READ	0x48
+#define REG_OFFSET_PERP_SUBTYPE	0x05
+
+/* RTC_CTRL register bit fields */
+#define BIT_RTC_ENABLE		BIT(7)
+#define BIT_RTC_ALARM_ENABLE	BIT(7)
+#define BIT_RTC_ABORT_ENABLE	BIT(0)
+#define BIT_RTC_ALARM_CLEAR	BIT(0)
+
+/* RTC/ALARM peripheral subtype values */
+#define RTC_PERPH_SUBTYPE       0x1
+#define ALARM_PERPH_SUBTYPE     0x3
+
+#define NUM_8_BIT_RTC_REGS	0x4
+
+#define TO_SECS(arr)		(arr[0] | (arr[1] << 8) | (arr[2] << 16) | \
+							(arr[3] << 24))
+
+/* Module parameter to control power-on-alarm */
+bool poweron_alarm;
+EXPORT_SYMBOL(poweron_alarm);
+module_param(poweron_alarm, bool, 0644);
+MODULE_PARM_DESC(poweron_alarm, "Enable/Disable power-on alarm");
+
+/* rtc driver internal structure */
+struct qpnp_rtc {
+	u8			rtc_ctrl_reg;
+	u8			alarm_ctrl_reg1;
+	u16			rtc_base;
+	u16			alarm_base;
+	u32			rtc_write_enable;
+	u32			rtc_alarm_powerup;
+	int			rtc_alarm_irq;
+	struct device		*rtc_dev;
+	struct rtc_device	*rtc;
+	struct platform_device	*pdev;
+	struct regmap		*regmap;
+	spinlock_t		alarm_ctrl_lock;
+};
+
+static int qpnp_read_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
+			u16 base, int count)
+{
+	int rc;
+
+	rc = regmap_bulk_read(rtc_dd->regmap, base, rtc_val, count);
+	if (rc) {
+		dev_err(rtc_dd->rtc_dev, "SPMI read failed\n");
+		return rc;
+	}
+	return 0;
+}
+
+static int qpnp_write_wrapper(struct qpnp_rtc *rtc_dd, u8 *rtc_val,
+			u16 base, int count)
+{
+	int rc;
+
+	rc = regmap_bulk_write(rtc_dd->regmap, base, rtc_val, count);
+	if (rc) {
+		dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int
+qpnp_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	unsigned long secs, irq_flags;
+	u8 value[4], reg = 0, alarm_enabled = 0, ctrl_reg;
+	u8 rtc_disabled = 0, rtc_ctrl_reg;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rtc_tm_to_time(tm, &secs);
+
+	value[0] = secs & 0xFF;
+	value[1] = (secs >> 8) & 0xFF;
+	value[2] = (secs >> 16) & 0xFF;
+	value[3] = (secs >> 24) & 0xFF;
+
+	dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	ctrl_reg = rtc_dd->alarm_ctrl_reg1;
+
+	if (ctrl_reg & BIT_RTC_ALARM_ENABLE) {
+		alarm_enabled = 1;
+		ctrl_reg &= ~BIT_RTC_ALARM_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+		if (rc) {
+			dev_err(dev, "Write to ALARM ctrl reg failed\n");
+			goto rtc_rw_fail;
+		}
+	} else
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	/*
+	 * 32 bit seconds value is coverted to four 8 bit values
+	 *	|<------  32 bit time value in seconds  ------>|
+	 *      <- 8 bit ->|<- 8 bit ->|<- 8 bit ->|<- 8 bit ->|
+	 *       ----------------------------------------------
+	 *      | BYTE[3]  |  BYTE[2]  |  BYTE[1]  |  BYTE[0]  |
+	 *       ----------------------------------------------
+	 *
+	 * RTC has four 8 bit registers for writing time in seconds:
+	 *             WDATA[3], WDATA[2], WDATA[1], WDATA[0]
+	 *
+	 * Write to the RTC registers should be done in following order
+	 * Clear WDATA[0] register
+	 *
+	 * Write BYTE[1], BYTE[2] and BYTE[3] of time to
+	 * RTC WDATA[3], WDATA[2], WDATA[1] registers
+	 *
+	 * Write BYTE[0] of time to RTC WDATA[0] register
+	 *
+	 * Clearing BYTE[0] and writing in the end will prevent any
+	 * unintentional overflow from WDATA[0] to higher bytes during the
+	 * write operation
+	 */
+
+	/* Disable RTC H/w before writing on RTC register*/
+	rtc_ctrl_reg = rtc_dd->rtc_ctrl_reg;
+	if (rtc_ctrl_reg & BIT_RTC_ENABLE) {
+		rtc_disabled = 1;
+		rtc_ctrl_reg &= ~BIT_RTC_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &rtc_ctrl_reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
+		if (rc) {
+			dev_err(dev, "Disabling of RTC control reg failed with error:%d\n",
+				rc);
+			goto rtc_rw_fail;
+		}
+		rtc_dd->rtc_ctrl_reg = rtc_ctrl_reg;
+	}
+
+	/* Clear WDATA[0] */
+	reg = 0x0;
+	rc = qpnp_write_wrapper(rtc_dd, &reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_WRITE, 1);
+	if (rc) {
+		dev_err(dev, "Write to RTC reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Write to WDATA[3], WDATA[2] and WDATA[1] */
+	rc = qpnp_write_wrapper(rtc_dd, &value[1],
+			rtc_dd->rtc_base + REG_OFFSET_RTC_WRITE + 1, 3);
+	if (rc) {
+		dev_err(dev, "Write to RTC reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Write to WDATA[0] */
+	rc = qpnp_write_wrapper(rtc_dd, value,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_WRITE, 1);
+	if (rc) {
+		dev_err(dev, "Write to RTC reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	/* Enable RTC H/w after writing on RTC register*/
+	if (rtc_disabled) {
+		rtc_ctrl_reg |= BIT_RTC_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &rtc_ctrl_reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
+		if (rc) {
+			dev_err(dev, "Enabling of RTC control reg failed with error:%d\n",
+				rc);
+			goto rtc_rw_fail;
+		}
+		rtc_dd->rtc_ctrl_reg = rtc_ctrl_reg;
+	}
+
+	if (alarm_enabled) {
+		ctrl_reg |= BIT_RTC_ALARM_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+		if (rc) {
+			dev_err(dev, "Write to ALARM ctrl reg failed\n");
+			goto rtc_rw_fail;
+		}
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+
+rtc_rw_fail:
+	if (alarm_enabled)
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	return rc;
+}
+
+static int
+qpnp_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+	int rc;
+	u8 value[4], reg;
+	unsigned long secs;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rc = qpnp_read_wrapper(rtc_dd, value,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_READ,
+				NUM_8_BIT_RTC_REGS);
+	if (rc) {
+		dev_err(dev, "Read from RTC reg failed\n");
+		return rc;
+	}
+
+	/*
+	 * Read the LSB again and check if there has been a carry over
+	 * If there is, redo the read operation
+	 */
+	rc = qpnp_read_wrapper(rtc_dd, &reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_READ, 1);
+	if (rc) {
+		dev_err(dev, "Read from RTC reg failed\n");
+		return rc;
+	}
+
+	if (reg < value[0]) {
+		rc = qpnp_read_wrapper(rtc_dd, value,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_READ,
+				NUM_8_BIT_RTC_REGS);
+		if (rc) {
+			dev_err(dev, "Read from RTC reg failed\n");
+			return rc;
+		}
+	}
+
+	secs = TO_SECS(value);
+
+	rtc_time_to_tm(secs, tm);
+
+	rc = rtc_valid_tm(tm);
+	if (rc) {
+		dev_err(dev, "Invalid time read from RTC\n");
+		return rc;
+	}
+
+	dev_dbg(dev, "secs = %lu, h:m:s == %d:%d:%d, d/m/y = %d/%d/%d\n",
+			secs, tm->tm_hour, tm->tm_min, tm->tm_sec,
+			tm->tm_mday, tm->tm_mon, tm->tm_year);
+
+	return 0;
+}
+
+static int
+qpnp_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	int rc;
+	u8 value[4], ctrl_reg;
+	unsigned long secs, secs_rtc, irq_flags;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+	struct rtc_time rtc_tm;
+
+	rtc_tm_to_time(&alarm->time, &secs);
+
+	/*
+	 * Read the current RTC time and verify if the alarm time is in the
+	 * past. If yes, return invalid
+	 */
+	rc = qpnp_rtc_read_time(dev, &rtc_tm);
+	if (rc) {
+		dev_err(dev, "Unable to read RTC time\n");
+		return -EINVAL;
+	}
+
+	rtc_tm_to_time(&rtc_tm, &secs_rtc);
+	if (secs < secs_rtc) {
+		dev_err(dev, "Trying to set alarm in the past\n");
+		return -EINVAL;
+	}
+
+	value[0] = secs & 0xFF;
+	value[1] = (secs >> 8) & 0xFF;
+	value[2] = (secs >> 16) & 0xFF;
+	value[3] = (secs >> 24) & 0xFF;
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	rc = qpnp_write_wrapper(rtc_dd, value,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+				NUM_8_BIT_RTC_REGS);
+	if (rc) {
+		dev_err(dev, "Write to ALARM reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	ctrl_reg = (alarm->enabled) ?
+			(rtc_dd->alarm_ctrl_reg1 | BIT_RTC_ALARM_ENABLE) :
+			(rtc_dd->alarm_ctrl_reg1 & ~BIT_RTC_ALARM_ENABLE);
+
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(dev, "Write to ALARM cntrol reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+
+	dev_dbg(dev, "Alarm Set for h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+			alarm->time.tm_hour, alarm->time.tm_min,
+			alarm->time.tm_sec, alarm->time.tm_mday,
+			alarm->time.tm_mon, alarm->time.tm_year);
+rtc_rw_fail:
+	spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	return rc;
+}
+
+static int
+qpnp_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+	int rc;
+	u8 value[4];
+	unsigned long secs;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+
+	rc = qpnp_read_wrapper(rtc_dd, value,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+				NUM_8_BIT_RTC_REGS);
+	if (rc) {
+		dev_err(dev, "Read from ALARM reg failed\n");
+		return rc;
+	}
+
+	secs = TO_SECS(value);
+	rtc_time_to_tm(secs, &alarm->time);
+
+	rc = rtc_valid_tm(&alarm->time);
+	if (rc) {
+		dev_err(dev, "Invalid time read from RTC\n");
+		return rc;
+	}
+
+	dev_dbg(dev, "Alarm set for - h:r:s=%d:%d:%d, d/m/y=%d/%d/%d\n",
+		alarm->time.tm_hour, alarm->time.tm_min,
+				alarm->time.tm_sec, alarm->time.tm_mday,
+				alarm->time.tm_mon, alarm->time.tm_year);
+
+	return 0;
+}
+
+
+static int
+qpnp_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
+{
+	int rc;
+	unsigned long irq_flags;
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(dev);
+	u8 ctrl_reg;
+	u8 value[4] = {0};
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	ctrl_reg = rtc_dd->alarm_ctrl_reg1;
+	ctrl_reg = enabled ? (ctrl_reg | BIT_RTC_ALARM_ENABLE) :
+				(ctrl_reg & ~BIT_RTC_ALARM_ENABLE);
+
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(dev, "Write to ALARM control reg failed\n");
+		goto rtc_rw_fail;
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+
+	/* Clear Alarm register */
+	if (!enabled) {
+		rc = qpnp_write_wrapper(rtc_dd, value,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+			NUM_8_BIT_RTC_REGS);
+		if (rc)
+			dev_err(dev, "Clear ALARM value reg failed\n");
+	}
+
+rtc_rw_fail:
+	spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	return rc;
+}
+
+static const struct rtc_class_ops qpnp_rtc_ro_ops = {
+	.read_time = qpnp_rtc_read_time,
+	.set_alarm = qpnp_rtc_set_alarm,
+	.read_alarm = qpnp_rtc_read_alarm,
+	.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
+};
+
+static const struct rtc_class_ops qpnp_rtc_rw_ops = {
+	.read_time = qpnp_rtc_read_time,
+	.set_alarm = qpnp_rtc_set_alarm,
+	.read_alarm = qpnp_rtc_read_alarm,
+	.alarm_irq_enable = qpnp_rtc_alarm_irq_enable,
+	.set_time = qpnp_rtc_set_time,
+};
+
+static irqreturn_t qpnp_alarm_trigger(int irq, void *dev_id)
+{
+	struct qpnp_rtc *rtc_dd = dev_id;
+	u8 ctrl_reg;
+	int rc;
+	unsigned long irq_flags;
+
+	rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
+
+	spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	/* Clear the alarm enable bit */
+	ctrl_reg = rtc_dd->alarm_ctrl_reg1;
+	ctrl_reg &= ~BIT_RTC_ALARM_ENABLE;
+
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+		dev_err(rtc_dd->rtc_dev,
+				"Write to ALARM control reg failed\n");
+		goto rtc_alarm_handled;
+	}
+
+	rtc_dd->alarm_ctrl_reg1 = ctrl_reg;
+	spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+
+	/* Set ALARM_CLR bit */
+	ctrl_reg = 0x1;
+	rc = qpnp_write_wrapper(rtc_dd, &ctrl_reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL2, 1);
+	if (rc)
+		dev_err(rtc_dd->rtc_dev,
+				"Write to ALARM control reg failed\n");
+
+rtc_alarm_handled:
+	return IRQ_HANDLED;
+}
+
+static int qpnp_rtc_probe(struct platform_device *pdev)
+{
+	const struct rtc_class_ops *rtc_ops = &qpnp_rtc_ro_ops;
+	int rc;
+	u8 subtype;
+	struct qpnp_rtc *rtc_dd;
+	unsigned int base;
+	struct device_node *child;
+
+	rtc_dd = devm_kzalloc(&pdev->dev, sizeof(*rtc_dd), GFP_KERNEL);
+	if (rtc_dd == NULL)
+		return -ENOMEM;
+
+	rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!rtc_dd->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	/* Get the rtc write property */
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,qpnp-rtc-write",
+						&rtc_dd->rtc_write_enable);
+	if (rc && rc != -EINVAL) {
+		dev_err(&pdev->dev,
+			"Error reading rtc_write_enable property %d\n", rc);
+		return rc;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+						"qcom,qpnp-rtc-alarm-pwrup",
+						&rtc_dd->rtc_alarm_powerup);
+	if (rc && rc != -EINVAL) {
+		dev_err(&pdev->dev,
+			"Error reading rtc_alarm_powerup property %d\n", rc);
+		return rc;
+	}
+
+	/* Initialise spinlock to protect RTC control register */
+	spin_lock_init(&rtc_dd->alarm_ctrl_lock);
+
+	rtc_dd->rtc_dev = &(pdev->dev);
+	rtc_dd->pdev = pdev;
+
+
+	if (of_get_available_child_count(pdev->dev.of_node) == 0) {
+		pr_err("no child nodes\n");
+		rc = -ENXIO;
+		goto fail_rtc_enable;
+	}
+
+	/* Get RTC/ALARM resources */
+	for_each_available_child_of_node(pdev->dev.of_node, child) {
+		rc = of_property_read_u32(child, "reg", &base);
+		if (rc < 0) {
+			dev_err(&pdev->dev,
+				"Couldn't find reg in node = %s rc = %d\n",
+				child->full_name, rc);
+			goto fail_rtc_enable;
+		}
+
+		rc = qpnp_read_wrapper(rtc_dd, &subtype,
+				base + REG_OFFSET_PERP_SUBTYPE, 1);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"Peripheral subtype read failed\n");
+			goto fail_rtc_enable;
+		}
+
+		switch (subtype) {
+		case RTC_PERPH_SUBTYPE:
+			rtc_dd->rtc_base = base;
+			break;
+		case ALARM_PERPH_SUBTYPE:
+			rtc_dd->alarm_base = base;
+			rtc_dd->rtc_alarm_irq = of_irq_get(child, 0);
+			if (rtc_dd->rtc_alarm_irq < 0) {
+				dev_err(&pdev->dev, "ALARM IRQ absent\n");
+				rc = -ENXIO;
+				goto fail_rtc_enable;
+			}
+			break;
+		default:
+			dev_err(&pdev->dev, "Invalid peripheral subtype\n");
+			rc = -EINVAL;
+			goto fail_rtc_enable;
+		}
+	}
+
+	rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->rtc_ctrl_reg,
+				rtc_dd->rtc_base + REG_OFFSET_RTC_CTRL, 1);
+	if (rc) {
+		dev_err(&pdev->dev, "Read from RTC control reg failed\n");
+		goto fail_rtc_enable;
+	}
+
+	if (!(rtc_dd->rtc_ctrl_reg & BIT_RTC_ENABLE)) {
+		dev_err(&pdev->dev, "RTC h/w disabled, rtc not registered\n");
+		goto fail_rtc_enable;
+	}
+
+	rc = qpnp_read_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(&pdev->dev, "Read from  Alarm control reg failed\n");
+		goto fail_rtc_enable;
+	}
+	/* Enable abort enable feature */
+	rtc_dd->alarm_ctrl_reg1 |= BIT_RTC_ABORT_ENABLE;
+	rc = qpnp_write_wrapper(rtc_dd, &rtc_dd->alarm_ctrl_reg1,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+	if (rc) {
+		dev_err(&pdev->dev, "SPMI write failed!\n");
+		goto fail_rtc_enable;
+	}
+
+	if (rtc_dd->rtc_write_enable == true)
+		rtc_ops = &qpnp_rtc_rw_ops;
+
+	dev_set_drvdata(&pdev->dev, rtc_dd);
+
+	/* Register the RTC device */
+	rtc_dd->rtc = rtc_device_register("qpnp_rtc", &pdev->dev,
+					  rtc_ops, THIS_MODULE);
+	if (IS_ERR(rtc_dd->rtc)) {
+		dev_err(&pdev->dev, "%s: RTC registration failed (%ld)\n",
+					__func__, PTR_ERR(rtc_dd->rtc));
+		rc = PTR_ERR(rtc_dd->rtc);
+		goto fail_rtc_enable;
+	}
+
+	/* Init power_on_alarm after adding rtc device */
+	power_on_alarm_init();
+
+	/* Request the alarm IRQ */
+	rc = request_any_context_irq(rtc_dd->rtc_alarm_irq,
+				 qpnp_alarm_trigger, IRQF_TRIGGER_RISING,
+				 "qpnp_rtc_alarm", rtc_dd);
+	if (rc) {
+		dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
+		goto fail_req_irq;
+	}
+
+	device_init_wakeup(&pdev->dev, 1);
+	enable_irq_wake(rtc_dd->rtc_alarm_irq);
+
+	dev_dbg(&pdev->dev, "Probe success !!\n");
+
+	return 0;
+
+fail_req_irq:
+	rtc_device_unregister(rtc_dd->rtc);
+fail_rtc_enable:
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return rc;
+}
+
+static int qpnp_rtc_remove(struct platform_device *pdev)
+{
+	struct qpnp_rtc *rtc_dd = dev_get_drvdata(&pdev->dev);
+
+	device_init_wakeup(&pdev->dev, 0);
+	free_irq(rtc_dd->rtc_alarm_irq, rtc_dd);
+	rtc_device_unregister(rtc_dd->rtc);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static void qpnp_rtc_shutdown(struct platform_device *pdev)
+{
+	u8 value[4] = {0};
+	u8 reg;
+	int rc;
+	unsigned long irq_flags;
+	struct qpnp_rtc *rtc_dd;
+	bool rtc_alarm_powerup;
+
+	if (!pdev) {
+		pr_err("qpnp-rtc: spmi device not found\n");
+		return;
+	}
+	rtc_dd = dev_get_drvdata(&pdev->dev);
+	if (!rtc_dd) {
+		pr_err("qpnp-rtc: rtc driver data not found\n");
+		return;
+	}
+	rtc_alarm_powerup = rtc_dd->rtc_alarm_powerup;
+	if (!rtc_alarm_powerup && !poweron_alarm) {
+		spin_lock_irqsave(&rtc_dd->alarm_ctrl_lock, irq_flags);
+		dev_dbg(&pdev->dev, "Disabling alarm interrupts\n");
+
+		/* Disable RTC alarms */
+		reg = rtc_dd->alarm_ctrl_reg1;
+		reg &= ~BIT_RTC_ALARM_ENABLE;
+		rc = qpnp_write_wrapper(rtc_dd, &reg,
+			rtc_dd->alarm_base + REG_OFFSET_ALARM_CTRL1, 1);
+		if (rc) {
+			dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
+			goto fail_alarm_disable;
+		}
+
+		/* Clear Alarm register */
+		rc = qpnp_write_wrapper(rtc_dd, value,
+				rtc_dd->alarm_base + REG_OFFSET_ALARM_RW,
+				NUM_8_BIT_RTC_REGS);
+		if (rc)
+			dev_err(rtc_dd->rtc_dev, "SPMI write failed\n");
+
+fail_alarm_disable:
+		spin_unlock_irqrestore(&rtc_dd->alarm_ctrl_lock, irq_flags);
+	}
+}
+
+static const struct of_device_id spmi_match_table[] = {
+	{
+		.compatible = "qcom,qpnp-rtc",
+	},
+	{}
+};
+
+static struct platform_driver qpnp_rtc_driver = {
+	.probe		= qpnp_rtc_probe,
+	.remove		= qpnp_rtc_remove,
+	.shutdown	= qpnp_rtc_shutdown,
+	.driver		= {
+		.name		= "qcom,qpnp-rtc",
+		.owner		= THIS_MODULE,
+		.of_match_table	= spmi_match_table,
+	},
+};
+
+static int __init qpnp_rtc_init(void)
+{
+	return platform_driver_register(&qpnp_rtc_driver);
+}
+module_init(qpnp_rtc_init);
+
+static void __exit qpnp_rtc_exit(void)
+{
+	platform_driver_unregister(&qpnp_rtc_driver);
+}
+module_exit(qpnp_rtc_exit);
+
+MODULE_DESCRIPTION("SMPI PMIC RTC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/slimbus/Kconfig b/drivers/slimbus/Kconfig
new file mode 100644
index 0000000..b7d040e
--- /dev/null
+++ b/drivers/slimbus/Kconfig
@@ -0,0 +1,32 @@
+#
+# SLIMBUS driver configuration
+#
+menuconfig SLIMBUS
+	bool "Slimbus support"
+	depends on HAS_IOMEM
+	help
+	  Slimbus is standard interface between baseband and
+	  application processors and peripheral components in mobile
+	  terminals.
+
+if SLIMBUS
+config SLIMBUS_MSM_CTRL
+	tristate "QTI Slimbus Master Component"
+	default n
+	help
+	  Select driver for Qualcomm Technologies Inc. (QTI) Slimbus
+	  Master Component. This driver is responsible for configuring
+	  SLIMbus and performing bus administration, administration of
+	  components on the bus and dynamic channel allocation.
+
+config SLIMBUS_MSM_NGD
+	tristate "QTI Slimbus Satellite Component"
+	help
+	  Select driver for Qualcomm Technologies Inc. (QTI) Slimbus
+	  Satellite Component. This is light-weight slimbus controller
+	  driver responsible for communicating with slave HW directly over
+	  the bus using messaging interface, and communicating with master
+	  component residing on ADSP for bandwidth and data-channel
+	  management.
+
+endif
diff --git a/drivers/slimbus/Makefile b/drivers/slimbus/Makefile
new file mode 100644
index 0000000..45d6e6e
--- /dev/null
+++ b/drivers/slimbus/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for kernel slimbus framework.
+#
+obj-$(CONFIG_SLIMBUS)			+= slimbus.o
+obj-$(CONFIG_SLIMBUS_MSM_CTRL)		+= slim-msm.o slim-msm-ctrl.o
+obj-$(CONFIG_SLIMBUS_MSM_NGD)		+= slim-msm.o slim-msm-ngd.o
diff --git a/drivers/slimbus/slim-msm-ctrl.c b/drivers/slimbus/slim-msm-ctrl.c
new file mode 100644
index 0000000..3f99b2b
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ctrl.c
@@ -0,0 +1,1641 @@
+/* Copyright (c) 2011-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_slimbus.h>
+#include <linux/msm-sps.h>
+#include <linux/qdsp6v2/apr.h>
+#include "slim-msm.h"
+
+#define MSM_SLIM_NAME	"msm_slim_ctrl"
+#define SLIM_ROOT_FREQ 24576000
+
+#define QC_MSM_DEVS	5
+
+/* Manager registers */
+enum mgr_reg {
+	MGR_CFG		= 0x200,
+	MGR_STATUS	= 0x204,
+	MGR_RX_MSGQ_CFG	= 0x208,
+	MGR_INT_EN	= 0x210,
+	MGR_INT_STAT	= 0x214,
+	MGR_INT_CLR	= 0x218,
+	MGR_TX_MSG	= 0x230,
+	MGR_RX_MSG	= 0x270,
+	MGR_IE_STAT	= 0x2F0,
+	MGR_VE_STAT	= 0x300,
+};
+
+enum msg_cfg {
+	MGR_CFG_ENABLE		= 1,
+	MGR_CFG_RX_MSGQ_EN	= 1 << 1,
+	MGR_CFG_TX_MSGQ_EN_HIGH	= 1 << 2,
+	MGR_CFG_TX_MSGQ_EN_LOW	= 1 << 3,
+};
+/* Message queue types */
+enum msm_slim_msgq_type {
+	MSGQ_RX		= 0,
+	MSGQ_TX_LOW	= 1,
+	MSGQ_TX_HIGH	= 2,
+};
+/* Framer registers */
+enum frm_reg {
+	FRM_CFG		= 0x400,
+	FRM_STAT	= 0x404,
+	FRM_INT_EN	= 0x410,
+	FRM_INT_STAT	= 0x414,
+	FRM_INT_CLR	= 0x418,
+	FRM_WAKEUP	= 0x41C,
+	FRM_CLKCTL_DONE	= 0x420,
+	FRM_IE_STAT	= 0x430,
+	FRM_VE_STAT	= 0x440,
+};
+
+/* Interface registers */
+enum intf_reg {
+	INTF_CFG	= 0x600,
+	INTF_STAT	= 0x604,
+	INTF_INT_EN	= 0x610,
+	INTF_INT_STAT	= 0x614,
+	INTF_INT_CLR	= 0x618,
+	INTF_IE_STAT	= 0x630,
+	INTF_VE_STAT	= 0x640,
+};
+
+enum mgr_intr {
+	MGR_INT_RECFG_DONE	= 1 << 24,
+	MGR_INT_TX_NACKED_2	= 1 << 25,
+	MGR_INT_MSG_BUF_CONTE	= 1 << 26,
+	MGR_INT_RX_MSG_RCVD	= 1 << 30,
+	MGR_INT_TX_MSG_SENT	= 1 << 31,
+};
+
+enum frm_cfg {
+	FRM_ACTIVE	= 1,
+	CLK_GEAR	= 7,
+	ROOT_FREQ	= 11,
+	REF_CLK_GEAR	= 15,
+	INTR_WAKE	= 19,
+};
+
+static struct msm_slim_sat *msm_slim_alloc_sat(struct msm_slim_ctrl *dev);
+
+static int msm_sat_enqueue(struct msm_slim_sat *sat, u32 *buf, u8 len)
+{
+	struct msm_slim_ctrl *dev = sat->dev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sat->lock, flags);
+	if ((sat->stail + 1) % SAT_CONCUR_MSG == sat->shead) {
+		spin_unlock_irqrestore(&sat->lock, flags);
+		dev_err(dev->dev, "SAT QUEUE full!");
+		return -EXFULL;
+	}
+	memcpy(sat->sat_msgs[sat->stail], (u8 *)buf, len);
+	sat->stail = (sat->stail + 1) % SAT_CONCUR_MSG;
+	spin_unlock_irqrestore(&sat->lock, flags);
+	return 0;
+}
+
+static int msm_sat_dequeue(struct msm_slim_sat *sat, u8 *buf)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sat->lock, flags);
+	if (sat->stail == sat->shead) {
+		spin_unlock_irqrestore(&sat->lock, flags);
+		return -ENODATA;
+	}
+	memcpy(buf, sat->sat_msgs[sat->shead], 40);
+	sat->shead = (sat->shead + 1) % SAT_CONCUR_MSG;
+	spin_unlock_irqrestore(&sat->lock, flags);
+	return 0;
+}
+
+static void msm_get_eaddr(u8 *e_addr, u32 *buffer)
+{
+	e_addr[0] = (buffer[1] >> 24) & 0xff;
+	e_addr[1] = (buffer[1] >> 16) & 0xff;
+	e_addr[2] = (buffer[1] >> 8) & 0xff;
+	e_addr[3] = buffer[1] & 0xff;
+	e_addr[4] = (buffer[0] >> 24) & 0xff;
+	e_addr[5] = (buffer[0] >> 16) & 0xff;
+}
+
+static bool msm_is_sat_dev(u8 *e_addr)
+{
+	if (e_addr[5] == QC_MFGID_LSB && e_addr[4] == QC_MFGID_MSB &&
+		e_addr[2] != QC_CHIPID_SL &&
+		(e_addr[1] == QC_DEVID_SAT1 || e_addr[1] == QC_DEVID_SAT2))
+		return true;
+	return false;
+}
+
+static struct msm_slim_sat *addr_to_sat(struct msm_slim_ctrl *dev, u8 laddr)
+{
+	struct msm_slim_sat *sat = NULL;
+	int i = 0;
+
+	while (!sat && i < dev->nsats) {
+		if (laddr == dev->satd[i]->satcl.laddr)
+			sat = dev->satd[i];
+		i++;
+	}
+	return sat;
+}
+
+static irqreturn_t msm_slim_interrupt(int irq, void *d)
+{
+	struct msm_slim_ctrl *dev = d;
+	u32 pstat;
+	u32 stat = readl_relaxed(dev->base + MGR_INT_STAT);
+
+	if (stat & MGR_INT_TX_MSG_SENT || stat & MGR_INT_TX_NACKED_2) {
+		if (stat & MGR_INT_TX_MSG_SENT)
+			writel_relaxed(MGR_INT_TX_MSG_SENT,
+					dev->base + MGR_INT_CLR);
+		else {
+			u32 mgr_stat = readl_relaxed(dev->base + MGR_STATUS);
+			u32 mgr_ie_stat = readl_relaxed(dev->base +
+						MGR_IE_STAT);
+			u32 frm_stat = readl_relaxed(dev->base + FRM_STAT);
+			u32 frm_cfg = readl_relaxed(dev->base + FRM_CFG);
+			u32 frm_intr_stat = readl_relaxed(dev->base +
+						FRM_INT_STAT);
+			u32 frm_ie_stat = readl_relaxed(dev->base +
+						FRM_IE_STAT);
+			u32 intf_stat = readl_relaxed(dev->base + INTF_STAT);
+			u32 intf_intr_stat = readl_relaxed(dev->base +
+						INTF_INT_STAT);
+			u32 intf_ie_stat = readl_relaxed(dev->base +
+						INTF_IE_STAT);
+
+			writel_relaxed(MGR_INT_TX_NACKED_2,
+					dev->base + MGR_INT_CLR);
+			pr_err("TX Nack MGR dump:int_stat:0x%x, mgr_stat:0x%x",
+					stat, mgr_stat);
+			pr_err("TX Nack MGR dump:ie_stat:0x%x", mgr_ie_stat);
+			pr_err("TX Nack FRM dump:int_stat:0x%x, frm_stat:0x%x",
+					frm_intr_stat, frm_stat);
+			pr_err("TX Nack FRM dump:frm_cfg:0x%x, ie_stat:0x%x",
+					frm_cfg, frm_ie_stat);
+			pr_err("TX Nack INTF dump:intr_st:0x%x, intf_stat:0x%x",
+					intf_intr_stat, intf_stat);
+			pr_err("TX Nack INTF dump:ie_stat:0x%x", intf_ie_stat);
+
+			dev->err = -EIO;
+		}
+		/*
+		 * Guarantee that interrupt clear bit write goes through before
+		 * signalling completion/exiting ISR
+		 */
+		mb();
+		msm_slim_manage_tx_msgq(dev, false, NULL);
+	}
+	if (stat & MGR_INT_RX_MSG_RCVD) {
+		u32 rx_buf[10];
+		u32 mc, mt;
+		u8 len, i;
+
+		rx_buf[0] = readl_relaxed(dev->base + MGR_RX_MSG);
+		len = rx_buf[0] & 0x1F;
+		for (i = 1; i < ((len + 3) >> 2); i++) {
+			rx_buf[i] = readl_relaxed(dev->base + MGR_RX_MSG +
+						(4 * i));
+			dev_dbg(dev->dev, "reading data: %x\n", rx_buf[i]);
+		}
+		mt = (rx_buf[0] >> 5) & 0x7;
+		mc = (rx_buf[0] >> 8) & 0xff;
+		dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+		if (mt == SLIM_MSG_MT_DEST_REFERRED_USER ||
+				mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+			u8 laddr = (u8)((rx_buf[0] >> 16) & 0xFF);
+			struct msm_slim_sat *sat = addr_to_sat(dev, laddr);
+
+			if (sat)
+				msm_sat_enqueue(sat, rx_buf, len);
+			else
+				dev_err(dev->dev, "unknown sat:%d message",
+						laddr);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD,
+					dev->base + MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through before
+			 * queuing work
+			 */
+			mb();
+			if (sat)
+				queue_work(sat->wq, &sat->wd);
+		} else if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_PRESENT) {
+			u8 e_addr[6];
+
+			msm_get_eaddr(e_addr, rx_buf);
+			msm_slim_rx_enqueue(dev, rx_buf, len);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before signalling completion
+			 */
+			mb();
+			complete(&dev->rx_msgq_notify);
+		} else if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_ABSENT) {
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before signalling completion
+			 */
+			mb();
+			complete(&dev->rx_msgq_notify);
+
+		} else if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+				mc == SLIM_MSG_MC_REPLY_VALUE) {
+			msm_slim_rx_enqueue(dev, rx_buf, len);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before signalling completion
+			 */
+			mb();
+			complete(&dev->rx_msgq_notify);
+		} else if (mc == SLIM_MSG_MC_REPORT_INFORMATION) {
+			u8 *buf = (u8 *)rx_buf;
+			u8 l_addr = buf[2];
+			u16 ele = (u16)buf[4] << 4;
+
+			ele |= ((buf[3] & 0xf0) >> 4);
+			dev_err(dev->dev, "Slim-dev:%d report inf element:0x%x",
+					l_addr, ele);
+			for (i = 0; i < len - 5; i++)
+				dev_err(dev->dev, "offset:0x%x:bit mask:%x",
+						i, buf[i+5]);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before exiting
+			 */
+			mb();
+		} else {
+			dev_err(dev->dev, "Unexpected MC,%x MT:%x, len:%d",
+						mc, mt, len);
+			for (i = 0; i < ((len + 3) >> 2); i++)
+				dev_err(dev->dev, "error msg: %x", rx_buf[i]);
+			writel_relaxed(MGR_INT_RX_MSG_RCVD, dev->base +
+						MGR_INT_CLR);
+			/*
+			 * Guarantee that CLR bit write goes through
+			 * before exiting
+			 */
+			mb();
+		}
+	}
+	if (stat & MGR_INT_RECFG_DONE) {
+		writel_relaxed(MGR_INT_RECFG_DONE, dev->base + MGR_INT_CLR);
+		/*
+		 * Guarantee that CLR bit write goes through
+		 * before exiting ISR
+		 */
+		mb();
+		complete(&dev->reconf);
+	}
+	pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
+	if (pstat != 0)
+		return msm_slim_port_irq_handler(dev, pstat);
+
+	return IRQ_HANDLED;
+}
+
+static int msm_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
+{
+	DECLARE_COMPLETION_ONSTACK(done);
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	u32 *pbuf;
+	u8 *puc;
+	int timeout;
+	int msgv = -1;
+	u8 la = txn->la;
+	u8 mc = (u8)(txn->mc & 0xFF);
+	/*
+	 * Voting for runtime PM: Slimbus has 2 possible use cases:
+	 * 1. messaging
+	 * 2. Data channels
+	 * Messaging case goes through messaging slots and data channels
+	 * use their own slots
+	 * This "get" votes for messaging bandwidth
+	 */
+	if (!(txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG))
+		msgv = msm_slim_get_ctrl(dev);
+	if (msgv >= 0)
+		dev->state = MSM_CTRL_AWAKE;
+	mutex_lock(&dev->tx_lock);
+	if (dev->state == MSM_CTRL_ASLEEP ||
+		((!(txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
+		dev->state == MSM_CTRL_IDLE)) {
+		dev_err(dev->dev, "runtime or system PM suspended state");
+		mutex_unlock(&dev->tx_lock);
+		if (msgv >= 0)
+			msm_slim_put_ctrl(dev);
+		return -EBUSY;
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION) {
+		if (dev->reconf_busy) {
+			wait_for_completion(&dev->reconf);
+			dev->reconf_busy = false;
+		}
+		/* This "get" votes for data channels */
+		if (dev->ctrl.sched.usedslots != 0 &&
+			!dev->chan_active) {
+			int chv = msm_slim_get_ctrl(dev);
+
+			if (chv >= 0)
+				dev->chan_active = true;
+		}
+	}
+	txn->rl--;
+	pbuf = msm_get_msg_buf(dev, txn->rl, &done);
+	dev->err = 0;
+
+	if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
+		mutex_unlock(&dev->tx_lock);
+		if (msgv >= 0)
+			msm_slim_put_ctrl(dev);
+		return -EPROTONOSUPPORT;
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE && txn->la == 0xFF &&
+		(mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+		 mc == SLIM_MSG_MC_CONNECT_SINK ||
+		 mc == SLIM_MSG_MC_DISCONNECT_PORT))
+		la = dev->pgdla;
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, mc, 0, la);
+	else
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, mc, 1, la);
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		puc = ((u8 *)pbuf) + 3;
+	else
+		puc = ((u8 *)pbuf) + 2;
+	if (txn->rbuf)
+		*(puc++) = txn->tid;
+	if ((txn->mt == SLIM_MSG_MT_CORE) &&
+		((mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+		mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+		(mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+		 mc <= SLIM_MSG_MC_CHANGE_VALUE))) {
+		*(puc++) = (txn->ec & 0xFF);
+		*(puc++) = (txn->ec >> 8)&0xFF;
+	}
+	if (txn->wbuf)
+		memcpy(puc, txn->wbuf, txn->len);
+	if (txn->mt == SLIM_MSG_MT_CORE && txn->la == 0xFF &&
+		(mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+		 mc == SLIM_MSG_MC_CONNECT_SINK ||
+		 mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
+		if (mc != SLIM_MSG_MC_DISCONNECT_PORT)
+			dev->err = msm_slim_connect_pipe_port(dev, *puc);
+		else {
+			/*
+			 * Remove channel disconnects master-side ports from
+			 * channel. No need to send that again on the bus
+			 * Only disable port
+			 */
+			writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+					dev->pipes[*puc].port_b, dev->ver));
+			mutex_unlock(&dev->tx_lock);
+			if (msgv >= 0)
+				msm_slim_put_ctrl(dev);
+			return 0;
+		}
+		if (dev->err) {
+			dev_err(dev->dev, "pipe-port connect err:%d", dev->err);
+			mutex_unlock(&dev->tx_lock);
+			if (msgv >= 0)
+				msm_slim_put_ctrl(dev);
+			return dev->err;
+		}
+		*(puc) = (u8)dev->pipes[*puc].port_b;
+	}
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		mc == SLIM_MSG_MC_BEGIN_RECONFIGURATION)
+		dev->reconf_busy = true;
+	msm_send_msg_buf(dev, pbuf, txn->rl, MGR_TX_MSG);
+	timeout = wait_for_completion_timeout(&done, HZ);
+	if (mc == SLIM_MSG_MC_RECONFIGURE_NOW) {
+		if ((txn->mc == (SLIM_MSG_MC_RECONFIGURE_NOW |
+					SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
+				timeout) {
+			timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+			dev->reconf_busy = false;
+			if (timeout) {
+				clk_disable_unprepare(dev->rclk);
+				disable_irq(dev->irq);
+			}
+		}
+		if ((txn->mc == (SLIM_MSG_MC_RECONFIGURE_NOW |
+					SLIM_MSG_CLK_PAUSE_SEQ_FLG)) &&
+				!timeout) {
+			dev->reconf_busy = false;
+			dev_err(dev->dev, "clock pause failed");
+			mutex_unlock(&dev->tx_lock);
+			return -ETIMEDOUT;
+		}
+		if (txn->mt == SLIM_MSG_MT_CORE &&
+			txn->mc == SLIM_MSG_MC_RECONFIGURE_NOW) {
+			if (dev->ctrl.sched.usedslots == 0 &&
+					dev->chan_active) {
+				dev->chan_active = false;
+				msm_slim_put_ctrl(dev);
+			}
+		}
+	}
+	mutex_unlock(&dev->tx_lock);
+	if (msgv >= 0)
+		msm_slim_put_ctrl(dev);
+
+	if (!timeout)
+		dev_err(dev->dev, "TX timed out:MC:0x%x,mt:0x%x", txn->mc,
+					txn->mt);
+
+	return timeout ? dev->err : -ETIMEDOUT;
+}
+
+static void msm_slim_wait_retry(struct msm_slim_ctrl *dev)
+{
+	int msec_per_frm = 0;
+	int sfr_per_sec;
+	/* Wait for 1 superframe, or default time and then retry */
+	sfr_per_sec = dev->framer.superfreq /
+			(1 << (SLIM_MAX_CLK_GEAR - dev->ctrl.clkgear));
+	if (sfr_per_sec)
+		msec_per_frm = MSEC_PER_SEC / sfr_per_sec;
+	if (msec_per_frm < DEF_RETRY_MS)
+		msec_per_frm = DEF_RETRY_MS;
+	msleep(msec_per_frm);
+}
+static int msm_set_laddr(struct slim_controller *ctrl, const u8 *ea,
+				u8 elen, u8 laddr)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	struct completion done;
+	int timeout, ret, retries = 0;
+	u32 *buf;
+retry_laddr:
+	init_completion(&done);
+	mutex_lock(&dev->tx_lock);
+	buf = msm_get_msg_buf(dev, 9, &done);
+	if (buf == NULL)
+		return -ENOMEM;
+	buf[0] = SLIM_MSG_ASM_FIRST_WORD(9, SLIM_MSG_MT_CORE,
+					SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS,
+					SLIM_MSG_DEST_LOGICALADDR,
+					ea[5] | ea[4] << 8);
+	buf[1] = ea[3] | (ea[2] << 8) | (ea[1] << 16) | (ea[0] << 24);
+	buf[2] = laddr;
+
+	ret = msm_send_msg_buf(dev, buf, 9, MGR_TX_MSG);
+	timeout = wait_for_completion_timeout(&done, HZ);
+	if (!timeout)
+		dev->err = -ETIMEDOUT;
+	if (dev->err) {
+		ret = dev->err;
+		dev->err = 0;
+	}
+	mutex_unlock(&dev->tx_lock);
+	if (ret) {
+		pr_err("set LADDR:0x%x failed:ret:%d, retrying", laddr, ret);
+		if (retries < INIT_MX_RETRIES) {
+			msm_slim_wait_retry(dev);
+			retries++;
+			goto retry_laddr;
+		} else {
+			pr_err("set LADDR failed after retrying:ret:%d", ret);
+		}
+	}
+	return ret;
+}
+
+static int msm_clk_pause_wakeup(struct slim_controller *ctrl)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+
+	enable_irq(dev->irq);
+	clk_prepare_enable(dev->rclk);
+	writel_relaxed(1, dev->base + FRM_WAKEUP);
+	/* Make sure framer wakeup write goes through before exiting function */
+	mb();
+	/*
+	 * Workaround: Currently, slave is reporting lost-sync messages
+	 * after slimbus comes out of clock pause.
+	 * Transaction with slave fail before slave reports that message
+	 * Give some time for that report to come
+	 * Slimbus wakes up in clock gear 10 at 24.576MHz. With each superframe
+	 * being 250 usecs, we wait for 20 superframes here to ensure
+	 * we get the message
+	 */
+	usleep_range(4950, 5000);
+	return 0;
+}
+
+static int msm_sat_define_ch(struct msm_slim_sat *sat, u8 *buf, u8 len, u8 mc)
+{
+	struct msm_slim_ctrl *dev = sat->dev;
+	enum slim_ch_control oper;
+	int i;
+	int ret = 0;
+
+	if (mc == SLIM_USR_MC_CHAN_CTRL) {
+		for (i = 0; i < sat->nsatch; i++) {
+			if (buf[5] == sat->satch[i].chan)
+				break;
+		}
+		if (i >= sat->nsatch)
+			return -ENOTCONN;
+		oper = ((buf[3] & 0xC0) >> 6);
+		/* part of grp. activating/removing 1 will take care of rest */
+		ret = slim_control_ch(&sat->satcl, sat->satch[i].chanh, oper,
+					false);
+		if (!ret) {
+			for (i = 5; i < len; i++) {
+				int j;
+
+				for (j = 0; j < sat->nsatch; j++) {
+					if (buf[i] != sat->satch[j].chan)
+						continue;
+
+					if (oper == SLIM_CH_REMOVE)
+						sat->satch[j].req_rem++;
+					else
+						sat->satch[j].req_def++;
+					break;
+				}
+			}
+		}
+	} else {
+		u16 chh[40];
+		struct slim_ch prop;
+		u32 exp;
+		u16 *grph = NULL;
+		u8 coeff, cc;
+		u8 prrate = buf[6];
+
+		if (len <= 8)
+			return -EINVAL;
+		for (i = 8; i < len; i++) {
+			int j = 0;
+
+			for (j = 0; j < sat->nsatch; j++) {
+				if (sat->satch[j].chan == buf[i]) {
+					chh[i - 8] = sat->satch[j].chanh;
+					break;
+				}
+			}
+			if (j < sat->nsatch) {
+				u16 dummy;
+
+				ret = slim_query_ch(&sat->satcl, buf[i],
+							&dummy);
+				if (ret)
+					return ret;
+				if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
+					sat->satch[j].req_def++;
+				/* First channel in group from satellite */
+				if (i == 8)
+					grph = &sat->satch[j].chanh;
+				continue;
+			}
+			if (sat->nsatch >= MSM_MAX_SATCH)
+				return -EXFULL;
+			ret = slim_query_ch(&sat->satcl, buf[i], &chh[i - 8]);
+			if (ret)
+				return ret;
+			sat->satch[j].chan = buf[i];
+			sat->satch[j].chanh = chh[i - 8];
+			if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
+				sat->satch[j].req_def++;
+			if (i == 8)
+				grph = &sat->satch[j].chanh;
+			sat->nsatch++;
+		}
+		prop.dataf = (enum slim_ch_dataf)((buf[3] & 0xE0) >> 5);
+		prop.auxf = (enum slim_ch_auxf)((buf[4] & 0xC0) >> 5);
+		prop.baser = SLIM_RATE_4000HZ;
+		if (prrate & 0x8)
+			prop.baser = SLIM_RATE_11025HZ;
+		else
+			prop.baser = SLIM_RATE_4000HZ;
+		prop.prot = (enum slim_ch_proto)(buf[5] & 0x0F);
+		prop.sampleszbits = (buf[4] & 0x1F)*SLIM_CL_PER_SL;
+		exp = (u32)((buf[5] & 0xF0) >> 4);
+		coeff = (buf[4] & 0x20) >> 5;
+		cc = (coeff ? 3 : 1);
+		prop.ratem = cc * (1 << exp);
+		if (i > 9)
+			ret = slim_define_ch(&sat->satcl, &prop, chh, len - 8,
+					true, &chh[0]);
+		else
+			ret = slim_define_ch(&sat->satcl, &prop,
+					chh, 1, true, &chh[0]);
+		dev_dbg(dev->dev, "define sat grp returned:%d", ret);
+		if (ret)
+			return ret;
+		else if (grph)
+			*grph = chh[0];
+
+		/* part of group so activating 1 will take care of rest */
+		if (mc == SLIM_USR_MC_DEF_ACT_CHAN)
+			ret = slim_control_ch(&sat->satcl,
+					chh[0],
+					SLIM_CH_ACTIVATE, false);
+	}
+	return ret;
+}
+
+static void msm_slim_rxwq(struct msm_slim_ctrl *dev)
+{
+	u8 buf[40];
+	u8 mc, mt, len;
+	int i, ret;
+
+	if ((msm_slim_rx_dequeue(dev, (u8 *)buf)) != -ENODATA) {
+		len = buf[0] & 0x1F;
+		mt = (buf[0] >> 5) & 0x7;
+		mc = buf[1];
+		if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_PRESENT) {
+			u8 laddr;
+			u8 e_addr[6];
+
+			for (i = 0; i < 6; i++)
+				e_addr[i] = buf[7-i];
+
+			ret = slim_assign_laddr(&dev->ctrl, e_addr, 6, &laddr,
+						false);
+			/* Is this QTI ported generic device? */
+			if (!ret && e_addr[5] == QC_MFGID_LSB &&
+				e_addr[4] == QC_MFGID_MSB &&
+				e_addr[1] == QC_DEVID_PGD &&
+				e_addr[2] != QC_CHIPID_SL)
+				dev->pgdla = laddr;
+			if (!ret && !pm_runtime_enabled(dev->dev) &&
+				laddr == (QC_MSM_DEVS - 1))
+				pm_runtime_enable(dev->dev);
+
+			if (!ret && msm_is_sat_dev(e_addr)) {
+				struct msm_slim_sat *sat = addr_to_sat(dev,
+								laddr);
+				if (!sat)
+					sat = msm_slim_alloc_sat(dev);
+				if (!sat)
+					return;
+
+				sat->satcl.laddr = laddr;
+				msm_sat_enqueue(sat, (u32 *)buf, len);
+				queue_work(sat->wq, &sat->wd);
+			}
+			if (ret)
+				pr_err("assign laddr failed, error:%d", ret);
+		} else if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+				mc == SLIM_MSG_MC_REPLY_VALUE) {
+			u8 tid = buf[3];
+
+			dev_dbg(dev->dev, "tid:%d, len:%d\n", tid, len - 4);
+			slim_msg_response(&dev->ctrl, &buf[4], tid,
+						len - 4);
+			pm_runtime_mark_last_busy(dev->dev);
+		} else if (mc == SLIM_MSG_MC_REPORT_INFORMATION) {
+			u8 l_addr = buf[2];
+			u16 ele = (u16)buf[4] << 4;
+
+			ele |= ((buf[3] & 0xf0) >> 4);
+			dev_err(dev->dev, "Slim-dev:%d report inf element:0x%x",
+					l_addr, ele);
+			for (i = 0; i < len - 5; i++)
+				dev_err(dev->dev, "offset:0x%x:bit mask:%x",
+						i, buf[i+5]);
+		} else {
+			dev_err(dev->dev, "unexpected message:mc:%x, mt:%x",
+					mc, mt);
+			for (i = 0; i < len; i++)
+				dev_err(dev->dev, "error msg: %x", buf[i]);
+
+		}
+	} else
+		dev_err(dev->dev, "rxwq called and no dequeue");
+}
+
+static void slim_sat_rxprocess(struct work_struct *work)
+{
+	struct msm_slim_sat *sat = container_of(work, struct msm_slim_sat, wd);
+	struct msm_slim_ctrl *dev = sat->dev;
+	u8 buf[40];
+
+	while ((msm_sat_dequeue(sat, buf)) != -ENODATA) {
+		struct slim_msg_txn txn;
+		u8 len, mc, mt;
+		u32 bw_sl;
+		int ret = 0;
+		int satv = -1;
+		bool gen_ack = false;
+		u8 tid;
+		u8 wbuf[8];
+		int i, retries = 0;
+
+		txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+		txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+		txn.ec = 0;
+		txn.rbuf = NULL;
+		txn.la = sat->satcl.laddr;
+		/* satellite handling */
+		len = buf[0] & 0x1F;
+		mc = buf[1];
+		mt = (buf[0] >> 5) & 0x7;
+
+		if (mt == SLIM_MSG_MT_CORE &&
+			mc == SLIM_MSG_MC_REPORT_PRESENT) {
+			u8 e_addr[6];
+
+			for (i = 0; i < 6; i++)
+				e_addr[i] = buf[7-i];
+
+			if (pm_runtime_enabled(dev->dev)) {
+				satv = msm_slim_get_ctrl(dev);
+				if (satv >= 0)
+					sat->pending_capability = true;
+			}
+			/*
+			 * Since capability message is already sent, present
+			 * message will indicate subsystem hosting this
+			 * satellite has restarted.
+			 * Remove all active channels of this satellite
+			 * when this is detected
+			 */
+			if (sat->sent_capability) {
+				for (i = 0; i < sat->nsatch; i++) {
+					if (sat->satch[i].reconf) {
+						pr_err("SSR, sat:%d, rm ch:%d",
+							sat->satcl.laddr,
+							sat->satch[i].chan);
+						slim_control_ch(&sat->satcl,
+							sat->satch[i].chanh,
+							SLIM_CH_REMOVE, true);
+						slim_dealloc_ch(&sat->satcl,
+							sat->satch[i].chanh);
+						sat->satch[i].reconf = false;
+					}
+				}
+			}
+		} else if (mt != SLIM_MSG_MT_CORE &&
+				mc != SLIM_MSG_MC_REPORT_PRESENT) {
+			satv = msm_slim_get_ctrl(dev);
+		}
+		switch (mc) {
+		case SLIM_MSG_MC_REPORT_PRESENT:
+			/* Remove runtime_pm vote once satellite acks */
+			if (mt != SLIM_MSG_MT_CORE) {
+				if (pm_runtime_enabled(dev->dev) &&
+					sat->pending_capability) {
+					msm_slim_put_ctrl(dev);
+					sat->pending_capability = false;
+				}
+				continue;
+			}
+			/* send a Manager capability msg */
+			if (sat->sent_capability) {
+				if (mt == SLIM_MSG_MT_CORE)
+					goto send_capability;
+				else
+					continue;
+			}
+			ret = slim_add_device(&dev->ctrl, &sat->satcl);
+			if (ret) {
+				dev_err(dev->dev,
+					"Satellite-init failed");
+				continue;
+			}
+			/* Satellite-channels */
+			sat->satch = kzalloc(MSM_MAX_SATCH *
+					sizeof(struct msm_sat_chan),
+					GFP_KERNEL);
+send_capability:
+			txn.mc = SLIM_USR_MC_MASTER_CAPABILITY;
+			txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+			txn.la = sat->satcl.laddr;
+			txn.rl = 8;
+			wbuf[0] = SAT_MAGIC_LSB;
+			wbuf[1] = SAT_MAGIC_MSB;
+			wbuf[2] = SAT_MSG_VER;
+			wbuf[3] = SAT_MSG_PROT;
+			txn.wbuf = wbuf;
+			txn.len = 4;
+			ret = msm_xfer_msg(&dev->ctrl, &txn);
+			if (ret) {
+				pr_err("capability for:0x%x fail:%d, retry:%d",
+						sat->satcl.laddr, ret, retries);
+				if (retries < INIT_MX_RETRIES) {
+					msm_slim_wait_retry(dev);
+					retries++;
+					goto send_capability;
+				} else {
+					pr_err("failed after all retries:%d",
+							ret);
+				}
+			} else {
+				sat->sent_capability = true;
+			}
+			break;
+		case SLIM_USR_MC_ADDR_QUERY:
+			memcpy(&wbuf[1], &buf[4], 6);
+			ret = slim_get_logical_addr(&sat->satcl,
+					&wbuf[1], 6, &wbuf[7]);
+			if (ret)
+				memset(&wbuf[1], 0, 6);
+			wbuf[0] = buf[3];
+			txn.mc = SLIM_USR_MC_ADDR_REPLY;
+			txn.rl = 12;
+			txn.len = 8;
+			txn.wbuf = wbuf;
+			msm_xfer_msg(&dev->ctrl, &txn);
+			break;
+		case SLIM_USR_MC_DEFINE_CHAN:
+		case SLIM_USR_MC_DEF_ACT_CHAN:
+		case SLIM_USR_MC_CHAN_CTRL:
+			if (mc != SLIM_USR_MC_CHAN_CTRL)
+				tid = buf[7];
+			else
+				tid = buf[4];
+			gen_ack = true;
+			ret = msm_sat_define_ch(sat, buf, len, mc);
+			if (ret) {
+				dev_err(dev->dev,
+					"SAT define_ch returned:%d",
+					ret);
+			}
+			if (!sat->pending_reconf) {
+				int chv = msm_slim_get_ctrl(dev);
+
+				if (chv >= 0)
+					sat->pending_reconf = true;
+			}
+			break;
+		case SLIM_USR_MC_RECONFIG_NOW:
+			tid = buf[3];
+			gen_ack = true;
+			ret = slim_reconfigure_now(&sat->satcl);
+			for (i = 0; i < sat->nsatch; i++) {
+				struct msm_sat_chan *sch = &sat->satch[i];
+
+				if (sch->req_rem && sch->reconf) {
+					if (!ret) {
+						slim_dealloc_ch(&sat->satcl,
+								sch->chanh);
+						sch->reconf = false;
+					}
+					sch->req_rem--;
+				} else if (sch->req_def) {
+					if (ret)
+						slim_dealloc_ch(&sat->satcl,
+								sch->chanh);
+					else
+						sch->reconf = true;
+					sch->req_def--;
+				}
+			}
+			if (sat->pending_reconf) {
+				msm_slim_put_ctrl(dev);
+				sat->pending_reconf = false;
+			}
+			break;
+		case SLIM_USR_MC_REQ_BW:
+			/* what we get is in SLOTS */
+			bw_sl = (u32)buf[4] << 3 |
+						((buf[3] & 0xE0) >> 5);
+			sat->satcl.pending_msgsl = bw_sl;
+			tid = buf[5];
+			gen_ack = true;
+			break;
+		case SLIM_USR_MC_CONNECT_SRC:
+		case SLIM_USR_MC_CONNECT_SINK:
+			if (mc == SLIM_USR_MC_CONNECT_SRC)
+				txn.mc = SLIM_MSG_MC_CONNECT_SOURCE;
+			else
+				txn.mc = SLIM_MSG_MC_CONNECT_SINK;
+			wbuf[0] = buf[4] & 0x1F;
+			wbuf[1] = buf[5];
+			tid = buf[6];
+			txn.la = buf[3];
+			txn.mt = SLIM_MSG_MT_CORE;
+			txn.rl = 6;
+			txn.len = 2;
+			txn.wbuf = wbuf;
+			gen_ack = true;
+			ret = msm_xfer_msg(&dev->ctrl, &txn);
+			break;
+		case SLIM_USR_MC_DISCONNECT_PORT:
+			txn.mc = SLIM_MSG_MC_DISCONNECT_PORT;
+			wbuf[0] = buf[4] & 0x1F;
+			tid = buf[5];
+			txn.la = buf[3];
+			txn.rl = 5;
+			txn.len = 1;
+			txn.mt = SLIM_MSG_MT_CORE;
+			txn.wbuf = wbuf;
+			gen_ack = true;
+			ret = msm_xfer_msg(&dev->ctrl, &txn);
+			break;
+		case SLIM_MSG_MC_REPORT_ABSENT:
+			dev_info(dev->dev, "Received Report Absent Message\n");
+			break;
+		default:
+			break;
+		}
+		if (!gen_ack) {
+			if (mc != SLIM_MSG_MC_REPORT_PRESENT && satv >= 0)
+				msm_slim_put_ctrl(dev);
+			continue;
+		}
+
+		wbuf[0] = tid;
+		if (!ret)
+			wbuf[1] = MSM_SAT_SUCCSS;
+		else
+			wbuf[1] = 0;
+		txn.mc = SLIM_USR_MC_GENERIC_ACK;
+		txn.la = sat->satcl.laddr;
+		txn.rl = 6;
+		txn.len = 2;
+		txn.wbuf = wbuf;
+		txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+		msm_xfer_msg(&dev->ctrl, &txn);
+		if (satv >= 0)
+			msm_slim_put_ctrl(dev);
+	}
+}
+
+static struct msm_slim_sat *msm_slim_alloc_sat(struct msm_slim_ctrl *dev)
+{
+	struct msm_slim_sat *sat;
+	char *name;
+
+	if (dev->nsats >= MSM_MAX_NSATS)
+		return NULL;
+
+	sat = kzalloc(sizeof(struct msm_slim_sat), GFP_KERNEL);
+	if (!sat)
+		return NULL;
+	name = kzalloc(SLIMBUS_NAME_SIZE, GFP_KERNEL);
+	if (!name) {
+		kfree(sat);
+		return NULL;
+	}
+	dev->satd[dev->nsats] = sat;
+	sat->dev = dev;
+	snprintf(name, SLIMBUS_NAME_SIZE, "msm_sat%d", dev->nsats);
+	sat->satcl.name = name;
+	spin_lock_init(&sat->lock);
+	INIT_WORK(&sat->wd, slim_sat_rxprocess);
+	sat->wq = create_singlethread_workqueue(sat->satcl.name);
+	if (!sat->wq) {
+		kfree(name);
+		kfree(sat);
+		return NULL;
+	}
+	/*
+	 * Both sats will be allocated from RX thread and RX thread will
+	 * process messages sequentially. No synchronization necessary
+	 */
+	dev->nsats++;
+	return sat;
+}
+
+static int msm_slim_rx_msgq_thread(void *data)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+	struct completion *notify = &dev->rx_msgq_notify;
+	struct msm_slim_sat *sat = NULL;
+	u32 mc = 0;
+	u32 mt = 0;
+	u32 buffer[10];
+	int index = 0;
+	u8 msg_len = 0;
+	int ret;
+
+	dev_dbg(dev->dev, "rx thread started");
+
+	while (!kthread_should_stop()) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		ret = wait_for_completion_interruptible(notify);
+
+		if (ret)
+			dev_err(dev->dev, "rx thread wait error:%d", ret);
+
+		/* 1 irq notification per message */
+		if (dev->use_rx_msgqs != MSM_MSGQ_ENABLED) {
+			msm_slim_rxwq(dev);
+			continue;
+		}
+
+		ret = msm_slim_rx_msgq_get(dev, buffer, index);
+		if (ret) {
+			dev_err(dev->dev, "rx_msgq_get() failed 0x%x\n", ret);
+			continue;
+		}
+
+		pr_debug("message[%d] = 0x%x\n", index, *buffer);
+
+		/* Decide if we use generic RX or satellite RX */
+		if (index++ == 0) {
+			msg_len = *buffer & 0x1F;
+			pr_debug("Start of new message, len = %d\n", msg_len);
+			mt = (buffer[0] >> 5) & 0x7;
+			mc = (buffer[0] >> 8) & 0xff;
+			dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+			if (mt == SLIM_MSG_MT_DEST_REFERRED_USER ||
+				mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+				u8 laddr;
+
+				laddr = (u8)((buffer[0] >> 16) & 0xff);
+				sat = addr_to_sat(dev, laddr);
+			}
+		}
+		if ((index * 4) >= msg_len) {
+			index = 0;
+			if (sat) {
+				msm_sat_enqueue(sat, buffer, msg_len);
+				queue_work(sat->wq, &sat->wd);
+				sat = NULL;
+			} else {
+				msm_slim_rx_enqueue(dev, buffer, msg_len);
+				msm_slim_rxwq(dev);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static void msm_slim_prg_slew(struct platform_device *pdev,
+				struct msm_slim_ctrl *dev)
+{
+	struct resource *slew_io;
+	void __iomem *slew_reg;
+	/* SLEW RATE register for this slimbus */
+	dev->slew_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_slew_reg");
+	if (!dev->slew_mem) {
+		dev_dbg(&pdev->dev, "no slimbus slew resource\n");
+		return;
+	}
+	slew_io = request_mem_region(dev->slew_mem->start,
+				resource_size(dev->slew_mem), pdev->name);
+	if (!slew_io) {
+		dev_dbg(&pdev->dev, "slimbus-slew mem claimed\n");
+		dev->slew_mem = NULL;
+		return;
+	}
+
+	slew_reg = ioremap(dev->slew_mem->start, resource_size(dev->slew_mem));
+	if (!slew_reg) {
+		dev_dbg(dev->dev, "slew register mapping failed");
+		release_mem_region(dev->slew_mem->start,
+					resource_size(dev->slew_mem));
+		dev->slew_mem = NULL;
+		return;
+	}
+	writel_relaxed(1, slew_reg);
+	/* Make sure slimbus-slew rate enabling goes through */
+	wmb();
+	iounmap(slew_reg);
+}
+
+static int msm_slim_probe(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev;
+	int ret;
+	enum apr_subsys_state q6_state;
+	struct resource		*bam_mem, *bam_io;
+	struct resource		*slim_mem, *slim_io;
+	struct resource		*irq, *bam_irq;
+	bool			rxreg_access = false;
+
+	q6_state = apr_get_q6_state();
+	if (q6_state == APR_SUBSYS_DOWN) {
+		dev_dbg(&pdev->dev, "defering %s, adsp_state %d\n", __func__,
+			q6_state);
+		return -EPROBE_DEFER;
+	}
+	dev_dbg(&pdev->dev, "adsp is ready\n");
+
+	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_physical");
+	if (!slim_mem) {
+		dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+		return -ENODEV;
+	}
+	slim_io = request_mem_region(slim_mem->start, resource_size(slim_mem),
+					pdev->name);
+	if (!slim_io) {
+		dev_err(&pdev->dev, "slimbus memory already claimed\n");
+		return -EBUSY;
+	}
+
+	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_bam_physical");
+	if (!bam_mem) {
+		dev_err(&pdev->dev, "no slimbus BAM memory resource\n");
+		ret = -ENODEV;
+		goto err_get_res_bam_failed;
+	}
+	bam_io = request_mem_region(bam_mem->start, resource_size(bam_mem),
+					pdev->name);
+	if (!bam_io) {
+		release_mem_region(slim_mem->start, resource_size(slim_mem));
+		dev_err(&pdev->dev, "slimbus BAM memory already claimed\n");
+		ret = -EBUSY;
+		goto err_get_res_bam_failed;
+	}
+	irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_irq");
+	if (!irq) {
+		dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+		ret = -ENODEV;
+		goto err_get_res_failed;
+	}
+	bam_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_bam_irq");
+	if (!bam_irq) {
+		dev_err(&pdev->dev, "no slimbus BAM IRQ resource\n");
+		ret = -ENODEV;
+		goto err_get_res_failed;
+	}
+
+	dev = kzalloc(sizeof(struct msm_slim_ctrl), GFP_KERNEL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err_get_res_failed;
+	}
+	dev->wr_comp = kzalloc(sizeof(struct completion *) * MSM_TX_BUFS,
+				GFP_KERNEL);
+	if (!dev->wr_comp)
+		return -ENOMEM;
+	dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, dev);
+	slim_set_ctrldata(&dev->ctrl, dev);
+	dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
+	if (!dev->base) {
+		dev_err(&pdev->dev, "IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_failed;
+	}
+	dev->bam.base = ioremap(bam_mem->start, resource_size(bam_mem));
+	if (!dev->bam.base) {
+		dev_err(&pdev->dev, "BAM IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_bam_failed;
+	}
+	if (pdev->dev.of_node) {
+
+		ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+					&dev->ctrl.nr);
+		if (ret) {
+			dev_err(&pdev->dev, "Cell index not specified:%d", ret);
+			goto err_of_init_failed;
+		}
+		rxreg_access = of_property_read_bool(pdev->dev.of_node,
+					"qcom,rxreg-access");
+		/* Optional properties */
+		ret = of_property_read_u32(pdev->dev.of_node,
+					"qcom,min-clk-gear", &dev->ctrl.min_cg);
+		ret = of_property_read_u32(pdev->dev.of_node,
+					"qcom,max-clk-gear", &dev->ctrl.max_cg);
+		pr_debug("min_cg:%d, max_cg:%d, rxreg: %d", dev->ctrl.min_cg,
+					dev->ctrl.max_cg, rxreg_access);
+	} else {
+		dev->ctrl.nr = pdev->id;
+	}
+	dev->ctrl.nchans = MSM_SLIM_NCHANS;
+	dev->ctrl.nports = MSM_SLIM_NPORTS;
+	dev->ctrl.set_laddr = msm_set_laddr;
+	dev->ctrl.xfer_msg = msm_xfer_msg;
+	dev->ctrl.wakeup =  msm_clk_pause_wakeup;
+	dev->ctrl.alloc_port = msm_alloc_port;
+	dev->ctrl.dealloc_port = msm_dealloc_port;
+	dev->ctrl.port_xfer = msm_slim_port_xfer;
+	dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
+	/* Reserve some messaging BW for satellite-apps driver communication */
+	dev->ctrl.sched.pending_msgsl = 30;
+
+	init_completion(&dev->reconf);
+	mutex_init(&dev->tx_lock);
+	spin_lock_init(&dev->rx_lock);
+	dev->ee = 1;
+	if (rxreg_access)
+		dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+	else
+		dev->use_rx_msgqs = MSM_MSGQ_RESET;
+
+	dev->irq = irq->start;
+	dev->bam.irq = bam_irq->start;
+
+	dev->hclk = clk_get(dev->dev, "iface_clk");
+	if (IS_ERR(dev->hclk))
+		dev->hclk = NULL;
+	else
+		clk_prepare_enable(dev->hclk);
+
+	ret = msm_slim_sps_init(dev, bam_mem, MGR_STATUS, false);
+	if (ret != 0) {
+		dev_err(dev->dev, "error SPS init\n");
+		goto err_sps_init_failed;
+	}
+
+	/* Fire up the Rx message queue thread */
+	dev->rx_msgq_thread = kthread_run(msm_slim_rx_msgq_thread, dev,
+					MSM_SLIM_NAME "_rx_msgq_thread");
+	if (IS_ERR(dev->rx_msgq_thread)) {
+		ret = PTR_ERR(dev->rx_msgq_thread);
+		dev_err(dev->dev, "Failed to start Rx message queue thread\n");
+		goto err_thread_create_failed;
+	}
+
+	dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+	dev->framer.superfreq =
+		dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+	dev->ctrl.a_framer = &dev->framer;
+	dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+	dev->ctrl.dev.parent = &pdev->dev;
+	dev->ctrl.dev.of_node = pdev->dev.of_node;
+
+	ret = request_threaded_irq(dev->irq, NULL, msm_slim_interrupt,
+				IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				"msm_slim_irq", dev);
+	if (ret) {
+		dev_err(&pdev->dev, "request IRQ failed\n");
+		goto err_request_irq_failed;
+	}
+
+	msm_slim_prg_slew(pdev, dev);
+
+	/* Register with framework before enabling frame, clock */
+	ret = slim_add_numbered_controller(&dev->ctrl);
+	if (ret) {
+		dev_err(dev->dev, "error adding controller\n");
+		goto err_ctrl_failed;
+	}
+
+
+	dev->rclk = clk_get(dev->dev, "core_clk");
+	if (!dev->rclk) {
+		dev_err(dev->dev, "slimbus clock not found");
+		goto err_clk_get_failed;
+	}
+	clk_set_rate(dev->rclk, SLIM_ROOT_FREQ);
+	clk_prepare_enable(dev->rclk);
+
+	dev->ver = readl_relaxed(dev->base);
+	/* Version info in 16 MSbits */
+	dev->ver >>= 16;
+	/* Component register initialization */
+	writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+	writel_relaxed((EE_MGR_RSC_GRP | EE_NGD_2 | EE_NGD_1),
+				dev->base + CFG_PORT(COMP_TRUST_CFG, dev->ver));
+
+	/*
+	 * Manager register initialization
+	 * If RX msg Q is used, disable RX_MSG_RCVD interrupt
+	 */
+	if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+		writel_relaxed((MGR_INT_RECFG_DONE | MGR_INT_TX_NACKED_2 |
+			MGR_INT_MSG_BUF_CONTE | /* MGR_INT_RX_MSG_RCVD | */
+			MGR_INT_TX_MSG_SENT), dev->base + MGR_INT_EN);
+	else
+		writel_relaxed((MGR_INT_RECFG_DONE | MGR_INT_TX_NACKED_2 |
+			MGR_INT_MSG_BUF_CONTE | MGR_INT_RX_MSG_RCVD |
+			MGR_INT_TX_MSG_SENT), dev->base + MGR_INT_EN);
+	writel_relaxed(1, dev->base + MGR_CFG);
+	/*
+	 * Framer registers are beyond 1K memory region after Manager and/or
+	 * component registers. Make sure those writes are ordered
+	 * before framer register writes
+	 */
+	wmb();
+
+	/* Framer register initialization */
+	writel_relaxed((1 << INTR_WAKE) | (0xA << REF_CLK_GEAR) |
+		(0xA << CLK_GEAR) | (1 << ROOT_FREQ) | (1 << FRM_ACTIVE) | 1,
+		dev->base + FRM_CFG);
+	/*
+	 * Make sure that framer wake-up and enabling writes go through
+	 * before any other component is enabled. Framer is responsible for
+	 * clocking the bus and enabling framer first will ensure that other
+	 * devices can report presence when they are enabled
+	 */
+	mb();
+
+	/* Enable RX msg Q */
+	if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+		writel_relaxed(MGR_CFG_ENABLE | MGR_CFG_RX_MSGQ_EN,
+					dev->base + MGR_CFG);
+	else
+		writel_relaxed(MGR_CFG_ENABLE, dev->base + MGR_CFG);
+	/*
+	 * Make sure that manager-enable is written through before interface
+	 * device is enabled
+	 */
+	mb();
+	writel_relaxed(1, dev->base + INTF_CFG);
+	/*
+	 * Make sure that interface-enable is written through before enabling
+	 * ported generic device inside MSM manager
+	 */
+	mb();
+	writel_relaxed(1, dev->base + CFG_PORT(PGD_CFG, dev->ver));
+	writel_relaxed(0x3F<<17, dev->base + CFG_PORT(PGD_OWN_EEn, dev->ver) +
+				(4 * dev->ee));
+	/*
+	 * Make sure that ported generic device is enabled and port-EE settings
+	 * are written through before finally enabling the component
+	 */
+	mb();
+
+	writel_relaxed(1, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+	/*
+	 * Make sure that all writes have gone through before exiting this
+	 * function
+	 */
+	mb();
+
+	/* Add devices registered with board-info now that controller is up */
+	slim_ctrl_add_boarddevs(&dev->ctrl);
+
+	if (pdev->dev.of_node)
+		of_register_slim_devices(&dev->ctrl);
+
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_autosuspend_delay(&pdev->dev, MSM_SLIM_AUTOSUSPEND);
+	pm_runtime_set_active(&pdev->dev);
+
+	dev_dbg(dev->dev, "MSM SB controller is up!\n");
+	return 0;
+
+err_ctrl_failed:
+	writel_relaxed(0, dev->base + CFG_PORT(COMP_CFG, dev->ver));
+err_clk_get_failed:
+	kfree(dev->satd);
+err_request_irq_failed:
+	kthread_stop(dev->rx_msgq_thread);
+err_thread_create_failed:
+	msm_slim_sps_exit(dev, true);
+	msm_slim_deinit_ep(dev, &dev->rx_msgq,
+				&dev->use_rx_msgqs);
+	msm_slim_deinit_ep(dev, &dev->tx_msgq,
+				&dev->use_tx_msgqs);
+err_sps_init_failed:
+	if (dev->hclk) {
+		clk_disable_unprepare(dev->hclk);
+		clk_put(dev->hclk);
+	}
+err_of_init_failed:
+	iounmap(dev->bam.base);
+err_ioremap_bam_failed:
+	iounmap(dev->base);
+err_ioremap_failed:
+	kfree(dev->wr_comp);
+	kfree(dev);
+err_get_res_failed:
+	release_mem_region(bam_mem->start, resource_size(bam_mem));
+err_get_res_bam_failed:
+	release_mem_region(slim_mem->start, resource_size(slim_mem));
+	return ret;
+}
+
+static int msm_slim_remove(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	struct resource *bam_mem;
+	struct resource *slim_mem;
+	struct resource *slew_mem = dev->slew_mem;
+	int i;
+
+	for (i = 0; i < dev->nsats; i++) {
+		struct msm_slim_sat *sat = dev->satd[i];
+		int j;
+
+		for (j = 0; j < sat->nsatch; j++)
+			slim_dealloc_ch(&sat->satcl, sat->satch[j].chanh);
+		slim_remove_device(&sat->satcl);
+		kfree(sat->satch);
+		destroy_workqueue(sat->wq);
+		kfree(sat->satcl.name);
+		kfree(sat);
+	}
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	free_irq(dev->irq, dev);
+	slim_del_controller(&dev->ctrl);
+	clk_put(dev->rclk);
+	if (dev->hclk)
+		clk_put(dev->hclk);
+	msm_slim_sps_exit(dev, true);
+	msm_slim_deinit_ep(dev, &dev->rx_msgq,
+				&dev->use_rx_msgqs);
+	msm_slim_deinit_ep(dev, &dev->tx_msgq,
+				&dev->use_tx_msgqs);
+
+	kthread_stop(dev->rx_msgq_thread);
+	iounmap(dev->bam.base);
+	iounmap(dev->base);
+	kfree(dev->wr_comp);
+	kfree(dev);
+	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_bam_physical");
+	if (bam_mem)
+		release_mem_region(bam_mem->start, resource_size(bam_mem));
+	if (slew_mem)
+		release_mem_region(slew_mem->start, resource_size(slew_mem));
+	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_physical");
+	if (slim_mem)
+		release_mem_region(slim_mem->start, resource_size(slim_mem));
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int msm_slim_runtime_idle(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+	if (dev->state == MSM_CTRL_AWAKE)
+		dev->state = MSM_CTRL_IDLE;
+	dev_dbg(device, "pm_runtime: idle...\n");
+	pm_request_autosuspend(device);
+	return -EAGAIN;
+}
+#endif
+
+/*
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume. So they are not
+ * inside ifdef CONFIG_PM_RUNTIME
+ */
+#ifdef CONFIG_PM
+static int msm_slim_runtime_suspend(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret;
+
+	dev_dbg(device, "pm_runtime: suspending...\n");
+	ret = slim_ctrl_clk_pause(&dev->ctrl, false, SLIM_CLK_UNSPECIFIED);
+	if (ret) {
+		dev_err(device, "clk pause not entered:%d", ret);
+		dev->state = MSM_CTRL_AWAKE;
+	} else {
+		dev->state = MSM_CTRL_ASLEEP;
+	}
+	return ret;
+}
+
+static int msm_slim_runtime_resume(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	dev_dbg(device, "pm_runtime: resuming...\n");
+	if (dev->state == MSM_CTRL_ASLEEP)
+		ret = slim_ctrl_clk_pause(&dev->ctrl, true, 0);
+	if (ret) {
+		dev_err(device, "clk pause not exited:%d", ret);
+		dev->state = MSM_CTRL_ASLEEP;
+	} else {
+		dev->state = MSM_CTRL_AWAKE;
+	}
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int msm_slim_suspend(struct device *dev)
+{
+	int ret = -EBUSY;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
+
+	if (!pm_runtime_enabled(dev) ||
+		(!pm_runtime_suspended(dev) &&
+			cdev->state == MSM_CTRL_IDLE)) {
+		dev_dbg(dev, "system suspend");
+		ret = msm_slim_runtime_suspend(dev);
+		if (!ret) {
+			if (cdev->hclk)
+				clk_disable_unprepare(cdev->hclk);
+		}
+	}
+	if (ret == -EBUSY) {
+		/*
+		 * If the clock pause failed due to active channels, there is
+		 * a possibility that some audio stream is active during suspend
+		 * We dont want to return suspend failure in that case so that
+		 * display and relevant components can still go to suspend.
+		 * If there is some other error, then it should be passed-on
+		 * to system level suspend
+		 */
+		ret = 0;
+	}
+	return ret;
+}
+
+static int msm_slim_resume(struct device *dev)
+{
+	/* If runtime_pm is enabled, this resume shouldn't do anything */
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		struct platform_device *pdev = to_platform_device(dev);
+		struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
+		int ret;
+
+		dev_dbg(dev, "system resume");
+		if (cdev->hclk)
+			clk_prepare_enable(cdev->hclk);
+		ret = msm_slim_runtime_resume(dev);
+		if (!ret) {
+			pm_runtime_mark_last_busy(dev);
+			pm_request_autosuspend(dev);
+		}
+		return ret;
+
+	}
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops msm_slim_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		msm_slim_suspend,
+		msm_slim_resume
+	)
+	SET_RUNTIME_PM_OPS(
+		msm_slim_runtime_suspend,
+		msm_slim_runtime_resume,
+		msm_slim_runtime_idle
+	)
+};
+
+static const struct of_device_id msm_slim_dt_match[] = {
+	{
+		.compatible = "qcom,slim-msm",
+	},
+	{}
+};
+
+static struct platform_driver msm_slim_driver = {
+	.probe = msm_slim_probe,
+	.remove = msm_slim_remove,
+	.driver	= {
+		.name = MSM_SLIM_NAME,
+		.owner = THIS_MODULE,
+		.pm = &msm_slim_dev_pm_ops,
+		.of_match_table = msm_slim_dt_match,
+	},
+};
+
+static int msm_slim_init(void)
+{
+	return platform_driver_register(&msm_slim_driver);
+}
+subsys_initcall(msm_slim_init);
+
+static void msm_slim_exit(void)
+{
+	platform_driver_unregister(&msm_slim_driver);
+}
+module_exit(msm_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Slimbus controller");
+MODULE_ALIAS("platform:msm-slim");
diff --git a/drivers/slimbus/slim-msm-ngd.c b/drivers/slimbus/slim-msm-ngd.c
new file mode 100644
index 0000000..f7f0269
--- /dev/null
+++ b/drivers/slimbus/slim-msm-ngd.c
@@ -0,0 +1,2110 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/of_slimbus.h>
+#include <linux/timer.h>
+#include <linux/msm-sps.h>
+#include <soc/qcom/service-locator.h>
+#include <soc/qcom/service-notifier.h>
+#include <soc/qcom/subsystem_notif.h>
+#include "slim-msm.h"
+
+#define NGD_SLIM_NAME	"ngd_msm_ctrl"
+#define SLIM_LA_MGR	0xFF
+#define SLIM_ROOT_FREQ	24576000
+#define LADDR_RETRY	5
+
+#define NGD_BASE_V1(r)	(((r) % 2) ? 0x800 : 0xA00)
+#define NGD_BASE_V2(r)	(((r) % 2) ? 0x1000 : 0x2000)
+#define NGD_BASE(r, v) ((v) ? NGD_BASE_V2(r) : NGD_BASE_V1(r))
+/* NGD (Non-ported Generic Device) registers */
+enum ngd_reg {
+	NGD_CFG		= 0x0,
+	NGD_STATUS	= 0x4,
+	NGD_RX_MSGQ_CFG	= 0x8,
+	NGD_INT_EN	= 0x10,
+	NGD_INT_STAT	= 0x14,
+	NGD_INT_CLR	= 0x18,
+	NGD_TX_MSG	= 0x30,
+	NGD_RX_MSG	= 0x70,
+	NGD_IE_STAT	= 0xF0,
+	NGD_VE_STAT	= 0x100,
+};
+
+enum ngd_msg_cfg {
+	NGD_CFG_ENABLE		= 1,
+	NGD_CFG_RX_MSGQ_EN	= 1 << 1,
+	NGD_CFG_TX_MSGQ_EN	= 1 << 2,
+};
+
+enum ngd_intr {
+	NGD_INT_RECFG_DONE	= 1 << 24,
+	NGD_INT_TX_NACKED_2	= 1 << 25,
+	NGD_INT_MSG_BUF_CONTE	= 1 << 26,
+	NGD_INT_MSG_TX_INVAL	= 1 << 27,
+	NGD_INT_IE_VE_CHG	= 1 << 28,
+	NGD_INT_DEV_ERR		= 1 << 29,
+	NGD_INT_RX_MSG_RCVD	= 1 << 30,
+	NGD_INT_TX_MSG_SENT	= 1 << 31,
+};
+
+enum ngd_offsets {
+	NGD_NACKED_MC		= 0x7F00000,
+	NGD_ACKED_MC		= 0xFE000,
+	NGD_ERROR		= 0x1800,
+	NGD_MSGQ_SUPPORT	= 0x400,
+	NGD_RX_MSGQ_TIME_OUT	= 0x16,
+	NGD_ENUMERATED		= 0x1,
+	NGD_TX_BUSY		= 0x0,
+};
+
+enum ngd_status {
+	NGD_LADDR		= 1 << 1,
+};
+
+static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf);
+static int ngd_slim_runtime_resume(struct device *device);
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart);
+static void ngd_dom_down(struct msm_slim_ctrl *dev);
+static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
+				void *_cmd);
+
+static irqreturn_t ngd_slim_interrupt(int irq, void *d)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)d;
+	void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+	u32 stat = readl_relaxed(ngd + NGD_INT_STAT);
+	u32 pstat;
+
+	if ((stat & NGD_INT_MSG_BUF_CONTE) ||
+		(stat & NGD_INT_MSG_TX_INVAL) || (stat & NGD_INT_DEV_ERR) ||
+		(stat & NGD_INT_TX_NACKED_2)) {
+		writel_relaxed(stat, ngd + NGD_INT_CLR);
+		if (stat & NGD_INT_MSG_TX_INVAL)
+			dev->err = -EINVAL;
+		else
+			dev->err = -EIO;
+
+		SLIM_WARN(dev, "NGD interrupt error:0x%x, err:%d\n", stat,
+								dev->err);
+		/* Guarantee that error interrupts are cleared */
+		mb();
+		msm_slim_manage_tx_msgq(dev, false, NULL, dev->err);
+
+	} else if (stat & NGD_INT_TX_MSG_SENT) {
+		writel_relaxed(NGD_INT_TX_MSG_SENT, ngd + NGD_INT_CLR);
+		/* Make sure interrupt is cleared */
+		mb();
+		msm_slim_manage_tx_msgq(dev, false, NULL, 0);
+	}
+	if (stat & NGD_INT_RX_MSG_RCVD) {
+		u32 rx_buf[10];
+		u8 len, i;
+
+		rx_buf[0] = readl_relaxed(ngd + NGD_RX_MSG);
+		len = rx_buf[0] & 0x1F;
+		for (i = 1; i < ((len + 3) >> 2); i++) {
+			rx_buf[i] = readl_relaxed(ngd + NGD_RX_MSG +
+						(4 * i));
+			SLIM_DBG(dev, "REG-RX data: %x\n", rx_buf[i]);
+		}
+		writel_relaxed(NGD_INT_RX_MSG_RCVD,
+				ngd + NGD_INT_CLR);
+		/*
+		 * Guarantee that CLR bit write goes through before
+		 * queuing work
+		 */
+		mb();
+		ngd_slim_rx(dev, (u8 *)rx_buf);
+	}
+	if (stat & NGD_INT_RECFG_DONE) {
+		writel_relaxed(NGD_INT_RECFG_DONE, ngd + NGD_INT_CLR);
+		/* Guarantee RECONFIG DONE interrupt is cleared */
+		mb();
+		/* In satellite mode, just log the reconfig done IRQ */
+		SLIM_DBG(dev, "reconfig done IRQ for NGD\n");
+	}
+	if (stat & NGD_INT_IE_VE_CHG) {
+		writel_relaxed(NGD_INT_IE_VE_CHG, ngd + NGD_INT_CLR);
+		/* Guarantee IE VE change interrupt is cleared */
+		mb();
+		SLIM_DBG(dev, "NGD IE VE change\n");
+	}
+
+	pstat = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_ST_EEn, dev->ver));
+	if (pstat != 0)
+		return msm_slim_port_irq_handler(dev, pstat);
+	return IRQ_HANDLED;
+}
+
+static int ngd_qmi_available(struct notifier_block *n, unsigned long code,
+				void *_cmd)
+{
+	struct msm_slim_qmi *qmi = container_of(n, struct msm_slim_qmi, nb);
+	struct msm_slim_ctrl *dev =
+		container_of(qmi, struct msm_slim_ctrl, qmi);
+	SLIM_INFO(dev, "Slimbus QMI NGD CB received event:%ld\n", code);
+	switch (code) {
+	case QMI_SERVER_ARRIVE:
+		atomic_set(&dev->ssr_in_progress, 0);
+		schedule_work(&dev->dsp.dom_up);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static void ngd_reg_ssr(struct msm_slim_ctrl *dev)
+{
+	int ret;
+	const char *subsys_name = NULL;
+
+	dev->dsp.dom_t = MSM_SLIM_DOM_NONE;
+	ret = of_property_read_string(dev->dev->of_node,
+				"qcom,subsys-name", &subsys_name);
+	if (ret)
+		subsys_name = "adsp";
+
+	dev->dsp.nb.notifier_call = dsp_domr_notify_cb;
+	dev->dsp.domr = subsys_notif_register_notifier(subsys_name,
+							&dev->dsp.nb);
+	if (IS_ERR_OR_NULL(dev->dsp.domr)) {
+		dev_err(dev->dev,
+			"subsys_notif_register_notifier failed %ld",
+			PTR_ERR(dev->dsp.domr));
+		return;
+	}
+	dev->dsp.dom_t = MSM_SLIM_DOM_SS;
+	SLIM_INFO(dev, "reg-SSR with:%s, PDR not available\n",
+			subsys_name);
+}
+
+static int dsp_domr_notify_cb(struct notifier_block *n, unsigned long code,
+				void *_cmd)
+{
+	int cur = -1;
+	struct msm_slim_ss *dsp = container_of(n, struct msm_slim_ss, nb);
+	struct msm_slim_ctrl *dev = container_of(dsp, struct msm_slim_ctrl,
+						dsp);
+	struct pd_qmi_client_data *reg;
+
+	SLIM_INFO(dev, "SLIM DSP SSR/PDR notify cb:0x%lx, type:%d\n",
+			code, dsp->dom_t);
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+	case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+		SLIM_INFO(dev, "SLIM DSP SSR notify cb:%lu\n", code);
+		atomic_set(&dev->ssr_in_progress, 1);
+		/* wait for current transaction */
+		mutex_lock(&dev->tx_lock);
+		/* make sure autosuspend is not called until ADSP comes up*/
+		pm_runtime_get_noresume(dev->dev);
+		dev->state = MSM_CTRL_DOWN;
+		msm_slim_sps_exit(dev, false);
+		ngd_dom_down(dev);
+		mutex_unlock(&dev->tx_lock);
+		break;
+	case LOCATOR_UP:
+		reg = _cmd;
+		if (!reg || reg->total_domains != 1) {
+			SLIM_WARN(dev, "error locating audio-PD\n");
+			if (reg)
+				SLIM_WARN(dev, "audio-PDs matched:%d\n",
+						reg->total_domains);
+
+			/* Fall back to SSR */
+			ngd_reg_ssr(dev);
+			return NOTIFY_DONE;
+		}
+		dev->dsp.domr = service_notif_register_notifier(
+				reg->domain_list->name,
+				reg->domain_list->instance_id,
+				&dev->dsp.nb,
+				&cur);
+		SLIM_INFO(dev, "reg-PD client:%s with service:%s\n",
+				reg->client_name, reg->service_name);
+		SLIM_INFO(dev, "reg-PD dom:%s instance:%d, cur:%d\n",
+				reg->domain_list->name,
+				reg->domain_list->instance_id, cur);
+		if (IS_ERR_OR_NULL(dev->dsp.domr))
+			ngd_reg_ssr(dev);
+		else
+			dev->dsp.dom_t = MSM_SLIM_DOM_PD;
+		break;
+	case LOCATOR_DOWN:
+		ngd_reg_ssr(dev);
+	default:
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static void ngd_dom_init(struct msm_slim_ctrl *dev)
+{
+	struct pd_qmi_client_data reg;
+	int ret;
+
+	memset(&reg, 0, sizeof(struct pd_qmi_client_data));
+	dev->dsp.nb.priority = 4;
+	dev->dsp.nb.notifier_call = dsp_domr_notify_cb;
+	scnprintf(reg.client_name, QMI_SERVREG_LOC_NAME_LENGTH_V01, "appsngd%d",
+		 dev->ctrl.nr);
+	scnprintf(reg.service_name, QMI_SERVREG_LOC_NAME_LENGTH_V01,
+		 "avs/audio");
+	ret = get_service_location(reg.client_name, reg.service_name,
+				   &dev->dsp.nb);
+	if (ret)
+		ngd_reg_ssr(dev);
+}
+
+static int mdm_ssr_notify_cb(struct notifier_block *n, unsigned long code,
+				void *_cmd)
+{
+	void __iomem *ngd;
+	struct msm_slim_ss *ext_mdm = container_of(n, struct msm_slim_ss, nb);
+	struct msm_slim_ctrl *dev = container_of(ext_mdm, struct msm_slim_ctrl,
+						ext_mdm);
+	struct slim_controller *ctrl = &dev->ctrl;
+	u32 laddr;
+	struct slim_device *sbdev;
+
+	switch (code) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+		SLIM_INFO(dev, "SLIM %lu external_modem SSR notify cb\n", code);
+		/* vote for runtime-pm so that ADSP doesn't go down */
+		msm_slim_get_ctrl(dev);
+		/*
+		 * checking framer here will wake-up ADSP and may avoid framer
+		 * handover later
+		 */
+		msm_slim_qmi_check_framer_request(dev);
+		dev->ext_mdm.state = MSM_CTRL_DOWN;
+		msm_slim_put_ctrl(dev);
+		break;
+	case SUBSYS_AFTER_POWERUP:
+		if (dev->ext_mdm.state != MSM_CTRL_DOWN)
+			return NOTIFY_DONE;
+		SLIM_INFO(dev,
+			"SLIM %lu external_modem SSR notify cb\n", code);
+		/* vote for runtime-pm so that ADSP doesn't go down */
+		msm_slim_get_ctrl(dev);
+		msm_slim_qmi_check_framer_request(dev);
+		/* If NGD enumeration is lost, we will need to power us up */
+		ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+		laddr = readl_relaxed(ngd + NGD_STATUS);
+		if (!(laddr & NGD_LADDR)) {
+			mutex_lock(&dev->tx_lock);
+			/* runtime-pm state should be consistent with HW */
+			pm_runtime_disable(dev->dev);
+			pm_runtime_set_suspended(dev->dev);
+			dev->state = MSM_CTRL_DOWN;
+			mutex_unlock(&dev->tx_lock);
+			SLIM_INFO(dev,
+				"SLIM MDM SSR (active framer on MDM) dev-down\n");
+			list_for_each_entry(sbdev, &ctrl->devs, dev_list)
+				slim_report_absent(sbdev);
+			ngd_slim_runtime_resume(dev->dev);
+			pm_runtime_set_active(dev->dev);
+			pm_runtime_enable(dev->dev);
+		}
+		dev->ext_mdm.state = MSM_CTRL_AWAKE;
+		msm_slim_put_ctrl(dev);
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static int ngd_get_tid(struct slim_controller *ctrl, struct slim_msg_txn *txn,
+				u8 *tid, struct completion *done)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ctrl->txn_lock, flags);
+	if (ctrl->last_tid <= 255) {
+		dev->msg_cnt = ctrl->last_tid;
+		ctrl->last_tid++;
+	} else {
+		int i;
+
+		for (i = 0; i < 256; i++) {
+			dev->msg_cnt = ((dev->msg_cnt + 1) & 0xFF);
+			if (ctrl->txnt[dev->msg_cnt] == NULL)
+				break;
+		}
+		if (i >= 256) {
+			dev_err(&ctrl->dev, "out of TID");
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+			return -ENOMEM;
+		}
+	}
+	ctrl->txnt[dev->msg_cnt] = txn;
+	txn->tid = dev->msg_cnt;
+	txn->comp = done;
+	*tid = dev->msg_cnt;
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	return 0;
+}
+
+static void slim_reinit_tx_msgq(struct msm_slim_ctrl *dev)
+{
+	/*
+	 * disconnect/recoonect pipe so that subsequent
+	 * transactions don't timeout due to unavailable
+	 * descriptors
+	 */
+	if (dev->state != MSM_CTRL_DOWN) {
+		msm_slim_disconnect_endp(dev, &dev->tx_msgq,
+					&dev->use_tx_msgqs);
+		msm_slim_connect_endp(dev, &dev->tx_msgq);
+	}
+}
+
+static int ngd_check_hw_status(struct msm_slim_ctrl *dev)
+{
+	void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+	u32 laddr = readl_relaxed(ngd + NGD_STATUS);
+	int ret = 0;
+
+	/* Lost logical addr due to noise */
+	if (!(laddr & NGD_LADDR)) {
+		SLIM_WARN(dev, "NGD lost LADDR: status:0x%x\n", laddr);
+		ret = ngd_slim_power_up(dev, false);
+
+		if (ret) {
+			SLIM_WARN(dev, "slim resume ret:%d, state:%d\n",
+					ret, dev->state);
+			ret = -EREMOTEIO;
+		}
+	}
+	return ret;
+}
+
+static int ngd_xfer_msg(struct slim_controller *ctrl, struct slim_msg_txn *txn)
+{
+	DECLARE_COMPLETION_ONSTACK(done);
+	DECLARE_COMPLETION_ONSTACK(tx_sent);
+
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	u32 *pbuf;
+	u8 *puc;
+	int ret = 0;
+	u8 la = txn->la;
+	u8 txn_mt;
+	u16 txn_mc = txn->mc;
+	u8 wbuf[SLIM_MSGQ_BUF_LEN];
+	bool report_sat = false;
+	bool sync_wr = true;
+
+	if (txn->mc & SLIM_MSG_CLK_PAUSE_SEQ_FLG)
+		return -EPROTONOSUPPORT;
+
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		(txn->mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+		 txn->mc <= SLIM_MSG_MC_RECONFIGURE_NOW))
+		return 0;
+
+	if (txn->mc == SLIM_USR_MC_REPORT_SATELLITE &&
+		txn->mt == SLIM_MSG_MT_SRC_REFERRED_USER)
+		report_sat = true;
+	else
+		mutex_lock(&dev->tx_lock);
+
+	if (!report_sat && !pm_runtime_enabled(dev->dev) &&
+			dev->state == MSM_CTRL_ASLEEP) {
+		/*
+		 * Counter-part of system-suspend when runtime-pm is not enabled
+		 * This way, resume can be left empty and device will be put in
+		 * active mode only if client requests anything on the bus
+		 * If the state was DOWN, SSR UP notification will take
+		 * care of putting the device in active state.
+		 */
+		mutex_unlock(&dev->tx_lock);
+		ret = ngd_slim_runtime_resume(dev->dev);
+
+		if (ret) {
+			SLIM_ERR(dev, "slim resume failed ret:%d, state:%d",
+					ret, dev->state);
+			return -EREMOTEIO;
+		}
+		mutex_lock(&dev->tx_lock);
+	}
+
+	/* If txn is tried when controller is down, wait for ADSP to boot */
+	if (!report_sat) {
+		if (dev->state == MSM_CTRL_DOWN) {
+			u8 mc = (u8)txn->mc;
+			int timeout;
+
+			mutex_unlock(&dev->tx_lock);
+			SLIM_INFO(dev, "ADSP slimbus not up yet\n");
+			/*
+			 * Messages related to data channel management can't
+			 * wait since they are holding reconfiguration lock.
+			 * clk_pause in resume (which can change state back to
+			 * MSM_CTRL_AWAKE), will need that lock.
+			 * Port disconnection, channel removal calls should pass
+			 * through since there is no activity on the bus and
+			 * those calls are triggered by clients due to
+			 * device_down callback in that situation.
+			 * Returning 0 on the disconnections and
+			 * removals will ensure consistent state of channels,
+			 * ports with the HW
+			 * Remote requests to remove channel/port will be
+			 * returned from the path where they wait on
+			 * acknowledgment from ADSP
+			 */
+			if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
+				((mc == SLIM_USR_MC_CHAN_CTRL ||
+				mc == SLIM_USR_MC_DISCONNECT_PORT ||
+				mc == SLIM_USR_MC_RECONFIG_NOW)))
+				return -EREMOTEIO;
+			if ((txn->mt == SLIM_MSG_MT_CORE) &&
+				((mc == SLIM_MSG_MC_DISCONNECT_PORT ||
+				mc == SLIM_MSG_MC_NEXT_REMOVE_CHANNEL ||
+				mc == SLIM_USR_MC_RECONFIG_NOW)))
+				return 0;
+			if ((txn->mt == SLIM_MSG_MT_CORE) &&
+				((mc >= SLIM_MSG_MC_CONNECT_SOURCE &&
+				mc <= SLIM_MSG_MC_CHANGE_CONTENT) ||
+				(mc >= SLIM_MSG_MC_BEGIN_RECONFIGURATION &&
+				mc <= SLIM_MSG_MC_RECONFIGURE_NOW)))
+				return -EREMOTEIO;
+			if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
+				((mc >= SLIM_USR_MC_DEFINE_CHAN &&
+				mc < SLIM_USR_MC_DISCONNECT_PORT)))
+				return -EREMOTEIO;
+			timeout = wait_for_completion_timeout(&dev->ctrl_up,
+							HZ);
+			if (!timeout)
+				return -ETIMEDOUT;
+			mutex_lock(&dev->tx_lock);
+		}
+
+		mutex_unlock(&dev->tx_lock);
+		ret = msm_slim_get_ctrl(dev);
+		mutex_lock(&dev->tx_lock);
+		/*
+		 * Runtime-pm's callbacks are not called until runtime-pm's
+		 * error status is cleared
+		 * Setting runtime status to suspended clears the error
+		 * It also makes HW status cosistent with what SW has it here
+		 */
+		if ((pm_runtime_enabled(dev->dev) && ret < 0) ||
+				dev->state >= MSM_CTRL_ASLEEP) {
+			SLIM_ERR(dev, "slim ctrl vote failed ret:%d, state:%d",
+					ret, dev->state);
+			pm_runtime_set_suspended(dev->dev);
+			mutex_unlock(&dev->tx_lock);
+			msm_slim_put_ctrl(dev);
+			return -EREMOTEIO;
+		}
+		ret = ngd_check_hw_status(dev);
+		if (ret) {
+			mutex_unlock(&dev->tx_lock);
+			msm_slim_put_ctrl(dev);
+			return ret;
+		}
+	}
+
+	if (txn->mt == SLIM_MSG_MT_CORE &&
+		(txn->mc == SLIM_MSG_MC_CONNECT_SOURCE ||
+		txn->mc == SLIM_MSG_MC_CONNECT_SINK ||
+		txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)) {
+		int i = 0;
+
+		if (txn->mc != SLIM_MSG_MC_DISCONNECT_PORT)
+			SLIM_INFO(dev,
+				"Connect port: laddr 0x%x  port_num %d chan_num %d\n",
+					txn->la, txn->wbuf[0], txn->wbuf[1]);
+		else
+			SLIM_INFO(dev,
+				"Disconnect port: laddr 0x%x  port_num %d\n",
+					txn->la, txn->wbuf[0]);
+		txn->mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+		if (txn->mc == SLIM_MSG_MC_CONNECT_SOURCE)
+			txn->mc = SLIM_USR_MC_CONNECT_SRC;
+		else if (txn->mc == SLIM_MSG_MC_CONNECT_SINK)
+			txn->mc = SLIM_USR_MC_CONNECT_SINK;
+		else if (txn->mc == SLIM_MSG_MC_DISCONNECT_PORT)
+			txn->mc = SLIM_USR_MC_DISCONNECT_PORT;
+		if (txn->la == SLIM_LA_MGR) {
+			if (dev->pgdla == SLIM_LA_MGR) {
+				u8 ea[] = {0, QC_DEVID_PGD, 0, 0, QC_MFGID_MSB,
+						QC_MFGID_LSB};
+				ea[2] = (u8)(dev->pdata.eapc & 0xFF);
+				ea[3] = (u8)((dev->pdata.eapc & 0xFF00) >> 8);
+				mutex_unlock(&dev->tx_lock);
+				ret = dev->ctrl.get_laddr(&dev->ctrl, ea, 6,
+						&dev->pgdla);
+				SLIM_DBG(dev, "SLIM PGD LA:0x%x, ret:%d\n",
+					dev->pgdla, ret);
+				if (ret) {
+					SLIM_ERR(dev,
+						"Incorrect SLIM-PGD EAPC:0x%x\n",
+							dev->pdata.eapc);
+					return ret;
+				}
+				mutex_lock(&dev->tx_lock);
+			}
+			txn->la = dev->pgdla;
+		}
+		wbuf[i++] = txn->la;
+		la = SLIM_LA_MGR;
+		wbuf[i++] = txn->wbuf[0];
+		if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
+			wbuf[i++] = txn->wbuf[1];
+		ret = ngd_get_tid(ctrl, txn, &wbuf[i++], &done);
+		if (ret) {
+			SLIM_ERR(dev, "TID for connect/disconnect fail:%d\n",
+					ret);
+			goto ngd_xfer_err;
+		}
+		txn->len = i;
+		txn->wbuf = wbuf;
+		txn->rl = txn->len + 4;
+	}
+	txn->rl--;
+
+	if (txn->len > SLIM_MSGQ_BUF_LEN || txn->rl > SLIM_MSGQ_BUF_LEN) {
+		SLIM_WARN(dev, "msg exeeds HW lim:%d, rl:%d, mc:0x%x, mt:0x%x",
+					txn->len, txn->rl, txn->mc, txn->mt);
+		ret = -EDQUOT;
+		goto ngd_xfer_err;
+	}
+
+	if (txn->mt == SLIM_MSG_MT_CORE && txn->comp &&
+		dev->use_tx_msgqs == MSM_MSGQ_ENABLED &&
+		(txn_mc != SLIM_MSG_MC_REQUEST_INFORMATION &&
+		 txn_mc != SLIM_MSG_MC_REQUEST_VALUE &&
+		 txn_mc != SLIM_MSG_MC_REQUEST_CHANGE_VALUE &&
+		 txn_mc != SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION)) {
+		sync_wr = false;
+		pbuf = msm_get_msg_buf(dev, txn->rl, txn->comp);
+	} else if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+			dev->use_tx_msgqs == MSM_MSGQ_ENABLED &&
+			txn->mc == SLIM_USR_MC_REPEAT_CHANGE_VALUE &&
+			txn->comp) {
+		sync_wr = false;
+		pbuf = msm_get_msg_buf(dev, txn->rl, txn->comp);
+	} else {
+		pbuf = msm_get_msg_buf(dev, txn->rl, &tx_sent);
+	}
+
+	if (!pbuf) {
+		SLIM_ERR(dev, "Message buffer unavailable\n");
+		ret = -ENOMEM;
+		goto ngd_xfer_err;
+	}
+	dev->err = 0;
+
+	if (txn->dt == SLIM_MSG_DEST_ENUMADDR) {
+		ret = -EPROTONOSUPPORT;
+		goto ngd_xfer_err;
+	}
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 0,
+				la);
+	else
+		*pbuf = SLIM_MSG_ASM_FIRST_WORD(txn->rl, txn->mt, txn->mc, 1,
+				la);
+	if (txn->dt == SLIM_MSG_DEST_LOGICALADDR)
+		puc = ((u8 *)pbuf) + 3;
+	else
+		puc = ((u8 *)pbuf) + 2;
+	if (txn->rbuf)
+		*(puc++) = txn->tid;
+	if (((txn->mt == SLIM_MSG_MT_CORE) &&
+		((txn->mc >= SLIM_MSG_MC_REQUEST_INFORMATION &&
+		txn->mc <= SLIM_MSG_MC_REPORT_INFORMATION) ||
+		(txn->mc >= SLIM_MSG_MC_REQUEST_VALUE &&
+		 txn->mc <= SLIM_MSG_MC_CHANGE_VALUE))) ||
+		(txn->mc == SLIM_USR_MC_REPEAT_CHANGE_VALUE &&
+		txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER)) {
+		*(puc++) = (txn->ec & 0xFF);
+		*(puc++) = (txn->ec >> 8)&0xFF;
+	}
+	if (txn->wbuf)
+		memcpy(puc, txn->wbuf, txn->len);
+	if (txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+		(txn->mc == SLIM_USR_MC_CONNECT_SRC ||
+		 txn->mc == SLIM_USR_MC_CONNECT_SINK ||
+		 txn->mc == SLIM_USR_MC_DISCONNECT_PORT) && txn->wbuf &&
+		wbuf[0] == dev->pgdla) {
+		if (txn->mc != SLIM_USR_MC_DISCONNECT_PORT)
+			dev->err = msm_slim_connect_pipe_port(dev, wbuf[1]);
+		else
+			writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+					(dev->pipes[wbuf[1]].port_b),
+						dev->ver));
+		if (dev->err) {
+			SLIM_ERR(dev, "pipe-port connect err:%d\n", dev->err);
+			goto ngd_xfer_err;
+		}
+		/* Add port-base to port number if this is manager side port */
+		puc[1] = (u8)dev->pipes[wbuf[1]].port_b;
+	}
+	dev->err = 0;
+	/*
+	 * If it's a read txn, it may be freed if a response is received by
+	 * received thread before reaching end of this function.
+	 * mc, mt may have changed to convert standard slimbus code/type to
+	 * satellite user-defined message. Reinitialize again
+	 */
+	txn_mc = txn->mc;
+	txn_mt = txn->mt;
+	ret = msm_send_msg_buf(dev, pbuf, txn->rl,
+			NGD_BASE(dev->ctrl.nr, dev->ver) + NGD_TX_MSG);
+	if (!ret && sync_wr) {
+		int i;
+		int timeout = wait_for_completion_timeout(&tx_sent, HZ);
+
+		if (!timeout && dev->use_tx_msgqs == MSM_MSGQ_ENABLED) {
+			struct msm_slim_endp *endpoint = &dev->tx_msgq;
+			struct sps_mem_buffer *mem = &endpoint->buf;
+			u32 idx = (u32) (((u8 *)pbuf - (u8 *)mem->base) /
+						SLIM_MSGQ_BUF_LEN);
+			phys_addr_t addr = mem->phys_base +
+						(idx * SLIM_MSGQ_BUF_LEN);
+			ret = -ETIMEDOUT;
+			SLIM_WARN(dev, "timeout, BAM desc_idx:%d, phys:%llx",
+					idx, (u64)addr);
+			for (i = 0; i < (SLIM_MSGQ_BUF_LEN >> 2) ; i++)
+				SLIM_WARN(dev, "timeout:bam-desc[%d]:0x%x",
+							i, *(pbuf + i));
+			if (idx < MSM_TX_BUFS)
+				dev->wr_comp[idx] = NULL;
+			slim_reinit_tx_msgq(dev);
+		} else if (!timeout) {
+			ret = -ETIMEDOUT;
+			SLIM_WARN(dev, "timeout non-BAM TX,len:%d", txn->rl);
+			for (i = 0; i < (SLIM_MSGQ_BUF_LEN >> 2) ; i++)
+				SLIM_WARN(dev, "timeout:txbuf[%d]:0x%x", i,
+						dev->tx_buf[i]);
+		} else {
+			ret = dev->err;
+		}
+	}
+	if (ret) {
+		u32 conf, stat, rx_msgq, int_stat, int_en, int_clr;
+		void __iomem *ngd = dev->base + NGD_BASE(dev->ctrl.nr,
+							dev->ver);
+		SLIM_WARN(dev, "TX failed :MC:0x%x,mt:0x%x, ret:%d, ver:%d\n",
+				txn_mc, txn_mt, ret, dev->ver);
+		conf = readl_relaxed(ngd);
+		stat = readl_relaxed(ngd + NGD_STATUS);
+		rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG);
+		int_stat = readl_relaxed(ngd + NGD_INT_STAT);
+		int_en = readl_relaxed(ngd + NGD_INT_EN);
+		int_clr = readl_relaxed(ngd + NGD_INT_CLR);
+
+		SLIM_WARN(dev, "conf:0x%x,stat:0x%x,rxmsgq:0x%x\n",
+				conf, stat, rx_msgq);
+		SLIM_ERR(dev, "int_stat:0x%x,int_en:0x%x,int_cll:0x%x\n",
+				int_stat, int_en, int_clr);
+	}
+
+	if (txn_mt == SLIM_MSG_MT_DEST_REFERRED_USER &&
+		(txn_mc == SLIM_USR_MC_CONNECT_SRC ||
+		 txn_mc == SLIM_USR_MC_CONNECT_SINK ||
+		 txn_mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+		int timeout;
+		unsigned long flags;
+
+		mutex_unlock(&dev->tx_lock);
+		msm_slim_put_ctrl(dev);
+		if (!ret) {
+			timeout = wait_for_completion_timeout(txn->comp, HZ);
+			/* remote side did not acknowledge */
+			if (!timeout)
+				ret = -EREMOTEIO;
+			else
+				ret = txn->ec;
+		}
+		if (ret) {
+			SLIM_ERR(dev,
+				"connect/disconnect:0x%x,tid:%d err:%d\n",
+					txn->mc, txn->tid, ret);
+			spin_lock_irqsave(&ctrl->txn_lock, flags);
+			ctrl->txnt[txn->tid] = NULL;
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+		}
+		return ret ? ret : dev->err;
+	}
+ngd_xfer_err:
+	if (!report_sat) {
+		mutex_unlock(&dev->tx_lock);
+		msm_slim_put_ctrl(dev);
+	}
+	return ret ? ret : dev->err;
+}
+
+static int ngd_get_ec(u16 start_offset, u8 len, u16 *ec)
+{
+	if (len > SLIM_MAX_VE_SLC_BYTES ||
+		start_offset > MSM_SLIM_VE_MAX_MAP_ADDR)
+		return -EINVAL;
+	if (len <= 4) {
+		*ec = len - 1;
+	} else if (len <= 8) {
+		if (len & 0x1)
+			return -EINVAL;
+		*ec = ((len >> 1) + 1);
+	} else {
+		if (len & 0x3)
+			return -EINVAL;
+		*ec = ((len >> 2) + 3);
+	}
+	*ec |= (0x8 | ((start_offset & 0xF) << 4));
+	*ec |= ((start_offset & 0xFF0) << 4);
+	return 0;
+}
+
+static int ngd_user_msg(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc,
+				struct slim_ele_access *msg, u8 *buf, u8 len)
+{
+	int ret;
+	struct slim_msg_txn txn;
+
+	if (mt != SLIM_MSG_MT_DEST_REFERRED_USER ||
+		mc != SLIM_USR_MC_REPEAT_CHANGE_VALUE) {
+		return -EPROTONOSUPPORT;
+	}
+
+	ret = ngd_get_ec(msg->start_offset, len, &txn.ec);
+	if (ret)
+		return ret;
+	txn.la = la;
+	txn.mt = mt;
+	txn.mc = mc;
+	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+	txn.len = len;
+	txn.rl = len + 6;
+	txn.wbuf = buf;
+	txn.rbuf = NULL;
+	txn.comp = msg->comp;
+	return ngd_xfer_msg(ctrl, &txn);
+}
+
+static int ngd_bulk_cb(void *ctx, int err)
+{
+	if (ctx)
+		complete(ctx);
+	return err;
+}
+
+static int ngd_bulk_wr(struct slim_controller *ctrl, u8 la, u8 mt, u8 mc,
+			struct slim_val_inf msgs[], int n,
+			int (*comp_cb)(void *ctx, int err), void *ctx)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	int i, ret;
+	struct msm_slim_endp *endpoint = &dev->tx_msgq;
+	u32 *header;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	ret = msm_slim_get_ctrl(dev);
+	mutex_lock(&dev->tx_lock);
+
+	if ((pm_runtime_enabled(dev->dev) && ret < 0) ||
+			dev->state >= MSM_CTRL_ASLEEP) {
+		SLIM_WARN(dev, "vote failed/SSR in-progress ret:%d, state:%d",
+				ret, dev->state);
+		pm_runtime_set_suspended(dev->dev);
+		mutex_unlock(&dev->tx_lock);
+		msm_slim_put_ctrl(dev);
+		return -EREMOTEIO;
+	}
+	if (!pm_runtime_enabled(dev->dev) && dev->state == MSM_CTRL_ASLEEP) {
+		mutex_unlock(&dev->tx_lock);
+		ret = ngd_slim_runtime_resume(dev->dev);
+
+		if (ret) {
+			SLIM_ERR(dev, "slim resume failed ret:%d, state:%d",
+					ret, dev->state);
+			return -EREMOTEIO;
+		}
+		mutex_lock(&dev->tx_lock);
+	}
+
+	ret = ngd_check_hw_status(dev);
+	if (ret) {
+		mutex_unlock(&dev->tx_lock);
+		msm_slim_put_ctrl(dev);
+		return ret;
+	}
+
+	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
+		SLIM_WARN(dev, "bulk wr not supported");
+		ret = -EPROTONOSUPPORT;
+		goto retpath;
+	}
+	if (dev->bulk.in_progress) {
+		SLIM_WARN(dev, "bulk wr in progress:");
+		ret = -EAGAIN;
+		goto retpath;
+	}
+	dev->bulk.in_progress = true;
+	/* every txn has 5 bytes of overhead: la, mc, mt, ec, len */
+	dev->bulk.size = n * 5;
+	for (i = 0; i < n; i++) {
+		dev->bulk.size += msgs[i].num_bytes;
+		dev->bulk.size += (4 - ((msgs[i].num_bytes + 1) & 0x3));
+	}
+
+	if (dev->bulk.size > 0xffff) {
+		SLIM_WARN(dev, "len exceeds limit, split bulk and retry");
+		ret = -EDQUOT;
+		goto retpath;
+	}
+	if (dev->bulk.size > dev->bulk.buf_sz) {
+		void *temp = krealloc(dev->bulk.base, dev->bulk.size,
+				      GFP_KERNEL | GFP_DMA);
+		if (!temp) {
+			ret = -ENOMEM;
+			goto retpath;
+		}
+		dev->bulk.base = temp;
+		dev->bulk.buf_sz = dev->bulk.size;
+	}
+
+	header = dev->bulk.base;
+	for (i = 0; i < n; i++) {
+		u8 *buf = (u8 *)header;
+		int rl = msgs[i].num_bytes + 5;
+		u16 ec;
+
+		*header = SLIM_MSG_ASM_FIRST_WORD(rl, mt, mc, 0, la);
+		buf += 3;
+		ret = ngd_get_ec(msgs[i].start_offset, msgs[i].num_bytes, &ec);
+		if (ret)
+			goto retpath;
+		*(buf++) = (ec & 0xFF);
+		*(buf++) = (ec >> 8) & 0xFF;
+		memcpy(buf, msgs[i].wbuf, msgs[i].num_bytes);
+		buf += msgs[i].num_bytes;
+		header += (rl >> 2);
+		if (rl & 3) {
+			header++;
+			memset(buf, 0, ((u8 *)header - buf));
+		}
+	}
+	header = dev->bulk.base;
+	if (comp_cb) {
+		dev->bulk.cb = comp_cb;
+		dev->bulk.ctx = ctx;
+	} else {
+		dev->bulk.cb = ngd_bulk_cb;
+		dev->bulk.ctx = &done;
+	}
+	dev->bulk.wr_dma = dma_map_single(dev->dev, dev->bulk.base,
+					  dev->bulk.size, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev->dev, dev->bulk.wr_dma)) {
+		ret = -ENOMEM;
+		goto retpath;
+	}
+
+	ret = sps_transfer_one(endpoint->sps, dev->bulk.wr_dma, dev->bulk.size,
+						NULL, SPS_IOVEC_FLAG_EOT);
+	if (ret) {
+		SLIM_WARN(dev, "sps transfer one returned error:%d", ret);
+		goto retpath;
+	}
+	if (dev->bulk.cb == ngd_bulk_cb) {
+		int timeout = wait_for_completion_timeout(&done, HZ);
+
+		if (!timeout) {
+			SLIM_WARN(dev, "timeout for bulk wr");
+			dma_unmap_single(dev->dev, dev->bulk.wr_dma,
+					 dev->bulk.size, DMA_TO_DEVICE);
+			ret = -ETIMEDOUT;
+		}
+	}
+retpath:
+	if (ret) {
+		dev->bulk.in_progress = false;
+		dev->bulk.ctx = NULL;
+		dev->bulk.wr_dma = 0;
+		slim_reinit_tx_msgq(dev);
+	}
+	mutex_unlock(&dev->tx_lock);
+	msm_slim_put_ctrl(dev);
+	return ret;
+}
+
+static int ngd_xferandwait_ack(struct slim_controller *ctrl,
+				struct slim_msg_txn *txn)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	unsigned long flags;
+	int ret;
+
+	if (dev->state == MSM_CTRL_DOWN) {
+		/*
+		 * no need to send anything to the bus due to SSR
+		 * transactions related to channel removal marked as success
+		 * since HW is down
+		 */
+		if ((txn->mt == SLIM_MSG_MT_DEST_REFERRED_USER) &&
+			((txn->mc >= SLIM_USR_MC_CHAN_CTRL &&
+			  txn->mc <= SLIM_USR_MC_REQ_BW) ||
+			txn->mc == SLIM_USR_MC_DISCONNECT_PORT)) {
+			spin_lock_irqsave(&ctrl->txn_lock, flags);
+			ctrl->txnt[txn->tid] = NULL;
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+			return 0;
+		}
+	}
+
+	ret = ngd_xfer_msg(ctrl, txn);
+	if (!ret) {
+		int timeout;
+
+		timeout = wait_for_completion_timeout(txn->comp, HZ);
+		if (!timeout)
+			ret = -ETIMEDOUT;
+		else
+			ret = txn->ec;
+	}
+
+	if (ret) {
+		if (ret != -EREMOTEIO || txn->mc != SLIM_USR_MC_CHAN_CTRL)
+			SLIM_ERR(dev, "master msg:0x%x,tid:%d ret:%d\n",
+				txn->mc, txn->tid, ret);
+		spin_lock_irqsave(&ctrl->txn_lock, flags);
+		ctrl->txnt[txn->tid] = NULL;
+		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	}
+
+	return ret;
+}
+
+static int ngd_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
+{
+	int ret = 0, num_chan = 0;
+	struct slim_pending_ch *pch;
+	struct slim_msg_txn txn;
+	struct slim_controller *ctrl = sb->ctrl;
+	DECLARE_COMPLETION_ONSTACK(done);
+	u8 wbuf[SLIM_MSGQ_BUF_LEN];
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+
+	*clkgear = ctrl->clkgear;
+	*subfrmc = 0;
+	txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+	txn.la = SLIM_LA_MGR;
+	txn.len = 0;
+	txn.ec = 0;
+	txn.wbuf = wbuf;
+	txn.rbuf = NULL;
+
+	if (ctrl->sched.msgsl != ctrl->sched.pending_msgsl) {
+		SLIM_DBG(dev, "slim reserve BW for messaging: req: %d\n",
+				ctrl->sched.pending_msgsl);
+		txn.mc = SLIM_USR_MC_REQ_BW;
+		wbuf[txn.len++] = ((sb->laddr & 0x1f) |
+				((u8)(ctrl->sched.pending_msgsl & 0x7) << 5));
+		wbuf[txn.len++] = (u8)(ctrl->sched.pending_msgsl >> 3);
+		ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+		if (ret)
+			return ret;
+		txn.rl = txn.len + 4;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+
+		txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+		txn.len = 2;
+		wbuf[1] = sb->laddr;
+		txn.rl = txn.len + 4;
+		ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+		if (ret)
+			return ret;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+
+		txn.len = 0;
+	}
+	list_for_each_entry(pch, &sb->mark_define, pending) {
+		struct slim_ich *slc;
+
+		slc = &ctrl->chans[pch->chan];
+		if (!slc) {
+			SLIM_WARN(dev, "no channel in define?\n");
+			return -ENXIO;
+		}
+		if (txn.len == 0) {
+			/* Per protocol, only last 5 bits for client no. */
+			wbuf[txn.len++] = (u8) (slc->prop.dataf << 5) |
+					(sb->laddr & 0x1f);
+			wbuf[txn.len] = slc->prop.sampleszbits >> 2;
+			if (slc->srch && slc->prop.prot == SLIM_PUSH)
+				slc->prop.prot = SLIM_PULL;
+			if (slc->coeff == SLIM_COEFF_3)
+				wbuf[txn.len] |= 1 << 5;
+			wbuf[txn.len++] |= slc->prop.auxf << 6;
+			wbuf[txn.len++] = slc->rootexp << 4 | slc->prop.prot;
+			wbuf[txn.len++] = slc->prrate;
+			ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+			if (ret) {
+				SLIM_WARN(dev, "no tid for channel define?\n");
+				return -ENXIO;
+			}
+		}
+		num_chan++;
+		wbuf[txn.len++] = slc->chan;
+		SLIM_INFO(dev, "slim activate chan:%d, laddr: 0x%x\n",
+				slc->chan, sb->laddr);
+	}
+	if (txn.len) {
+		txn.mc = SLIM_USR_MC_DEF_ACT_CHAN;
+		txn.rl = txn.len + 4;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+
+		txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+		txn.len = 2;
+		wbuf[1] = sb->laddr;
+		txn.rl = txn.len + 4;
+		ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+		if (ret)
+			return ret;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+	}
+	txn.len = 0;
+	list_for_each_entry(pch, &sb->mark_removal, pending) {
+		struct slim_ich *slc;
+
+		slc = &ctrl->chans[pch->chan];
+		if (!slc) {
+			SLIM_WARN(dev, "no channel in removal?\n");
+			return -ENXIO;
+		}
+		if (txn.len == 0) {
+			/* Per protocol, only last 5 bits for client no. */
+			wbuf[txn.len++] = (u8) (SLIM_CH_REMOVE << 6) |
+					(sb->laddr & 0x1f);
+			ret = ngd_get_tid(ctrl, &txn, &wbuf[txn.len++], &done);
+			if (ret) {
+				SLIM_WARN(dev, "no tid for channel define?\n");
+				return -ENXIO;
+			}
+		}
+		wbuf[txn.len++] = slc->chan;
+		SLIM_INFO(dev, "slim remove chan:%d, laddr: 0x%x\n",
+			   slc->chan, sb->laddr);
+	}
+	if (txn.len) {
+		txn.mc = SLIM_USR_MC_CHAN_CTRL;
+		txn.rl = txn.len + 4;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		/* HW restarting, channel removal should succeed */
+		if (ret == -EREMOTEIO)
+			return 0;
+		else if (ret)
+			return ret;
+
+		txn.mc = SLIM_USR_MC_RECONFIG_NOW;
+		txn.len = 2;
+		wbuf[1] = sb->laddr;
+		txn.rl = txn.len + 4;
+		ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+		if (ret)
+			return ret;
+		ret = ngd_xferandwait_ack(ctrl, &txn);
+		if (ret)
+			return ret;
+		txn.len = 0;
+	}
+	return 0;
+}
+
+static int ngd_set_laddr(struct slim_controller *ctrl, const u8 *ea,
+				u8 elen, u8 laddr)
+{
+	return 0;
+}
+
+static int ngd_get_laddr(struct slim_controller *ctrl, const u8 *ea,
+				u8 elen, u8 *laddr)
+{
+	int ret;
+	u8 wbuf[10];
+	struct slim_msg_txn txn;
+	DECLARE_COMPLETION_ONSTACK(done);
+
+	txn.mt = SLIM_MSG_MT_DEST_REFERRED_USER;
+	txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+	txn.la = SLIM_LA_MGR;
+	txn.ec = 0;
+	ret = ngd_get_tid(ctrl, &txn, &wbuf[0], &done);
+	if (ret)
+		return ret;
+	memcpy(&wbuf[1], ea, elen);
+	txn.mc = SLIM_USR_MC_ADDR_QUERY;
+	txn.rl = 11;
+	txn.len = 7;
+	txn.wbuf = wbuf;
+	txn.rbuf = NULL;
+	ret = ngd_xferandwait_ack(ctrl, &txn);
+	if (!ret && txn.la == 0xFF)
+		ret = -ENXIO;
+	else if (!ret)
+		*laddr = txn.la;
+	return ret;
+}
+
+static void ngd_slim_setup(struct msm_slim_ctrl *dev)
+{
+	u32 new_cfg = NGD_CFG_ENABLE;
+	u32 cfg = readl_relaxed(dev->base +
+				 NGD_BASE(dev->ctrl.nr, dev->ver));
+	if (dev->state == MSM_CTRL_DOWN) {
+		/* if called after SSR, cleanup and re-assign */
+		if (dev->use_tx_msgqs != MSM_MSGQ_RESET)
+			msm_slim_deinit_ep(dev, &dev->tx_msgq,
+					   &dev->use_tx_msgqs);
+
+		if (dev->use_rx_msgqs != MSM_MSGQ_RESET)
+			msm_slim_deinit_ep(dev, &dev->rx_msgq,
+					   &dev->use_rx_msgqs);
+
+		msm_slim_sps_init(dev, dev->bam_mem,
+			NGD_BASE(dev->ctrl.nr,
+			dev->ver) + NGD_STATUS, true);
+	} else {
+		if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED)
+			goto setup_tx_msg_path;
+
+		if ((dev->use_rx_msgqs == MSM_MSGQ_ENABLED) &&
+			(cfg & NGD_CFG_RX_MSGQ_EN))
+			goto setup_tx_msg_path;
+
+		if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+			msm_slim_disconnect_endp(dev, &dev->rx_msgq,
+						 &dev->use_rx_msgqs);
+		msm_slim_connect_endp(dev, &dev->rx_msgq);
+
+setup_tx_msg_path:
+		if (dev->use_tx_msgqs == MSM_MSGQ_DISABLED)
+			goto ngd_enable;
+		if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED &&
+			cfg & NGD_CFG_TX_MSGQ_EN)
+			goto ngd_enable;
+
+		if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
+			msm_slim_disconnect_endp(dev, &dev->tx_msgq,
+						 &dev->use_tx_msgqs);
+		msm_slim_connect_endp(dev, &dev->tx_msgq);
+	}
+ngd_enable:
+
+	if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+		new_cfg |= NGD_CFG_RX_MSGQ_EN;
+	if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
+		new_cfg |= NGD_CFG_TX_MSGQ_EN;
+
+	/* Enable NGD, and program MSGQs if not already */
+	if (cfg == new_cfg)
+		return;
+
+	writel_relaxed(new_cfg, dev->base + NGD_BASE(dev->ctrl.nr, dev->ver));
+	/* make sure NGD MSG-Q config goes through */
+	mb();
+}
+
+static void ngd_slim_rx(struct msm_slim_ctrl *dev, u8 *buf)
+{
+	unsigned long flags;
+	u8 mc, mt, len;
+
+	len = buf[0] & 0x1F;
+	mt = (buf[0] >> 5) & 0x7;
+	mc = buf[1];
+	if (mc == SLIM_USR_MC_MASTER_CAPABILITY &&
+		mt == SLIM_MSG_MT_SRC_REFERRED_USER)
+		complete(&dev->rx_msgq_notify);
+
+	if (mc == SLIM_MSG_MC_REPLY_INFORMATION ||
+			mc == SLIM_MSG_MC_REPLY_VALUE) {
+		u8 tid = buf[3];
+
+		dev_dbg(dev->dev, "tid:%d, len:%d\n", tid, len);
+		slim_msg_response(&dev->ctrl, &buf[4], tid,
+					len - 4);
+		pm_runtime_mark_last_busy(dev->dev);
+	}
+	if (mc == SLIM_USR_MC_ADDR_REPLY &&
+		mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+		struct slim_msg_txn *txn;
+		u8 failed_ea[6] = {0, 0, 0, 0, 0, 0};
+
+		spin_lock_irqsave(&dev->ctrl.txn_lock, flags);
+		txn = dev->ctrl.txnt[buf[3]];
+		if (!txn) {
+			spin_unlock_irqrestore(&dev->ctrl.txn_lock, flags);
+			SLIM_WARN(dev,
+				"LADDR response after timeout, tid:0x%x\n",
+					buf[3]);
+			return;
+		}
+		if (memcmp(&buf[4], failed_ea, 6))
+			txn->la = buf[10];
+		dev->ctrl.txnt[buf[3]] = NULL;
+		complete(txn->comp);
+		spin_unlock_irqrestore(&dev->ctrl.txn_lock, flags);
+	}
+	if (mc == SLIM_USR_MC_GENERIC_ACK &&
+		mt == SLIM_MSG_MT_SRC_REFERRED_USER) {
+		struct slim_msg_txn *txn;
+
+		spin_lock_irqsave(&dev->ctrl.txn_lock, flags);
+		txn = dev->ctrl.txnt[buf[3]];
+		if (!txn) {
+			spin_unlock_irqrestore(&dev->ctrl.txn_lock, flags);
+			SLIM_WARN(dev, "ACK received after timeout, tid:0x%x\n",
+				buf[3]);
+			return;
+		}
+		dev_dbg(dev->dev, "got response:tid:%d, response:0x%x",
+				(int)buf[3], buf[4]);
+		if (!(buf[4] & MSM_SAT_SUCCSS)) {
+			SLIM_WARN(dev, "TID:%d, NACK code:0x%x\n", (int)buf[3],
+						buf[4]);
+			txn->ec = -EIO;
+		}
+		dev->ctrl.txnt[buf[3]] = NULL;
+		complete(txn->comp);
+		spin_unlock_irqrestore(&dev->ctrl.txn_lock, flags);
+	}
+}
+
+static int ngd_slim_power_up(struct msm_slim_ctrl *dev, bool mdm_restart)
+{
+	void __iomem *ngd;
+	int timeout, retries = 0, ret = 0;
+	enum msm_ctrl_state cur_state = dev->state;
+	u32 laddr;
+	u32 rx_msgq;
+	u32 ngd_int = (NGD_INT_TX_NACKED_2 |
+			NGD_INT_MSG_BUF_CONTE | NGD_INT_MSG_TX_INVAL |
+			NGD_INT_IE_VE_CHG | NGD_INT_DEV_ERR |
+			NGD_INT_TX_MSG_SENT);
+
+	if (!mdm_restart && cur_state == MSM_CTRL_DOWN) {
+		int timeout = wait_for_completion_timeout(&dev->qmi.qmi_comp,
+						HZ);
+		if (!timeout) {
+			SLIM_ERR(dev, "slimbus QMI init timed out\n");
+			return -EREMOTEIO;
+		}
+	}
+
+hw_init_retry:
+	/* No need to vote if contorller is not in low power mode */
+	if (!mdm_restart &&
+		(cur_state == MSM_CTRL_DOWN || cur_state == MSM_CTRL_ASLEEP)) {
+		ret = msm_slim_qmi_power_request(dev, true);
+		if (ret) {
+			SLIM_WARN(dev, "SLIM power req failed:%d, retry:%d\n",
+					ret, retries);
+			if (!atomic_read(&dev->ssr_in_progress))
+				msm_slim_qmi_power_request(dev, false);
+			if (retries < INIT_MX_RETRIES &&
+				!atomic_read(&dev->ssr_in_progress)) {
+				retries++;
+				goto hw_init_retry;
+			}
+			return ret;
+		}
+	}
+	retries = 0;
+
+	if (!dev->ver) {
+		dev->ver = readl_relaxed(dev->base);
+		/* Version info in 16 MSbits */
+		dev->ver >>= 16;
+	}
+	ngd = dev->base + NGD_BASE(dev->ctrl.nr, dev->ver);
+	laddr = readl_relaxed(ngd + NGD_STATUS);
+	if (laddr & NGD_LADDR) {
+		u32 int_en = readl_relaxed(ngd + NGD_INT_EN);
+
+		/*
+		 * external MDM restart case where ADSP itself was active framer
+		 * For example, modem restarted when playback was active
+		 */
+		if (cur_state == MSM_CTRL_AWAKE) {
+			SLIM_INFO(dev, "Subsys restart: ADSP active framer\n");
+			return 0;
+		}
+		/*
+		 * ADSP power collapse case, where HW wasn't reset.
+		 */
+		if (int_en != 0)
+			return 0;
+
+		/* Retention */
+		if (dev->use_rx_msgqs == MSM_MSGQ_ENABLED)
+			msm_slim_disconnect_endp(dev, &dev->rx_msgq,
+						 &dev->use_rx_msgqs);
+		if (dev->use_tx_msgqs == MSM_MSGQ_ENABLED)
+			msm_slim_disconnect_endp(dev, &dev->tx_msgq,
+						 &dev->use_tx_msgqs);
+
+		writel_relaxed(ngd_int, (dev->base + NGD_INT_EN +
+					NGD_BASE(dev->ctrl.nr, dev->ver)));
+
+		rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG);
+		/**
+		 * Program with minimum value so that signal get
+		 * triggered immediately after receiving the message
+		 */
+		writel_relaxed((rx_msgq | SLIM_RX_MSGQ_TIMEOUT_VAL),
+						(ngd + NGD_RX_MSGQ_CFG));
+		/* reconnect BAM pipes if needed and enable NGD */
+		ngd_slim_setup(dev);
+		return 0;
+	}
+
+	if (mdm_restart) {
+		/*
+		 * external MDM SSR when MDM is active framer
+		 * ADSP will reset slimbus HW. disconnect BAM pipes so that
+		 * they can be connected after capability message is received.
+		 * Set device state to ASLEEP to be synchronous with the HW
+		 */
+		/* make current state as DOWN */
+		cur_state = MSM_CTRL_DOWN;
+		SLIM_INFO(dev,
+			"SLIM MDM restart: MDM active framer: reinit HW\n");
+		/* disconnect BAM pipes */
+		msm_slim_sps_exit(dev, false);
+		dev->state = MSM_CTRL_DOWN;
+	}
+
+capability_retry:
+	/*
+	 * ADSP power collapse case (OR SSR), where HW was reset
+	 * BAM programming will happen when capability message is received
+	 */
+	writel_relaxed(ngd_int, dev->base + NGD_INT_EN +
+				NGD_BASE(dev->ctrl.nr, dev->ver));
+
+	rx_msgq = readl_relaxed(ngd + NGD_RX_MSGQ_CFG);
+	/*
+	 * Program with minimum value so that signal get
+	 * triggered immediately after receiving the message
+	 */
+	writel_relaxed(rx_msgq|SLIM_RX_MSGQ_TIMEOUT_VAL,
+					ngd + NGD_RX_MSGQ_CFG);
+	/* make sure register got updated */
+	mb();
+
+	/* reconnect BAM pipes if needed and enable NGD */
+	ngd_slim_setup(dev);
+
+	timeout = wait_for_completion_timeout(&dev->reconf, HZ);
+	if (!timeout) {
+		u32 cfg = readl_relaxed(dev->base +
+					 NGD_BASE(dev->ctrl.nr, dev->ver));
+		laddr = readl_relaxed(ngd + NGD_STATUS);
+		SLIM_WARN(dev,
+			  "slim capability time-out:%d, stat:0x%x,cfg:0x%x\n",
+				retries, laddr, cfg);
+		if ((retries < INIT_MX_RETRIES) &&
+				!atomic_read(&dev->ssr_in_progress)) {
+			retries++;
+			goto capability_retry;
+		}
+		return -ETIMEDOUT;
+	}
+	/* mutliple transactions waiting on slimbus to power up? */
+	if (cur_state == MSM_CTRL_DOWN)
+		complete_all(&dev->ctrl_up);
+	/* Resetting the log level */
+	SLIM_RST_LOGLVL(dev);
+	return 0;
+}
+
+static int ngd_slim_enable(struct msm_slim_ctrl *dev, bool enable)
+{
+	int ret = 0;
+
+	if (enable) {
+		ret = msm_slim_qmi_init(dev, false);
+		/* controller state should be in sync with framework state */
+		if (!ret) {
+			complete(&dev->qmi.qmi_comp);
+			if (!pm_runtime_enabled(dev->dev) ||
+					!pm_runtime_suspended(dev->dev))
+				ngd_slim_runtime_resume(dev->dev);
+			else
+				pm_runtime_resume(dev->dev);
+			pm_runtime_mark_last_busy(dev->dev);
+			pm_runtime_put(dev->dev);
+		} else
+			SLIM_ERR(dev, "qmi init fail, ret:%d, state:%d\n",
+					ret, dev->state);
+	} else {
+		msm_slim_qmi_exit(dev);
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int ngd_slim_power_down(struct msm_slim_ctrl *dev)
+{
+	unsigned long flags;
+	int i;
+	struct slim_controller *ctrl = &dev->ctrl;
+
+	spin_lock_irqsave(&ctrl->txn_lock, flags);
+	/* Pending response for a message */
+	for (i = 0; i < ctrl->last_tid; i++) {
+		if (ctrl->txnt[i]) {
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+			SLIM_INFO(dev, "NGD down:txn-rsp for %d pending", i);
+			return -EBUSY;
+		}
+	}
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	return msm_slim_qmi_power_request(dev, false);
+}
+#endif
+
+static int ngd_slim_rx_msgq_thread(void *data)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+	struct completion *notify = &dev->rx_msgq_notify;
+	int ret = 0;
+
+	while (!kthread_should_stop()) {
+		struct slim_msg_txn txn;
+		int retries = 0;
+		u8 wbuf[8];
+
+		wait_for_completion_interruptible(notify);
+
+		txn.dt = SLIM_MSG_DEST_LOGICALADDR;
+		txn.ec = 0;
+		txn.rbuf = NULL;
+		txn.mc = SLIM_USR_MC_REPORT_SATELLITE;
+		txn.mt = SLIM_MSG_MT_SRC_REFERRED_USER;
+		txn.la = SLIM_LA_MGR;
+		wbuf[0] = SAT_MAGIC_LSB;
+		wbuf[1] = SAT_MAGIC_MSB;
+		wbuf[2] = SAT_MSG_VER;
+		wbuf[3] = SAT_MSG_PROT;
+		txn.wbuf = wbuf;
+		txn.len = 4;
+		SLIM_INFO(dev, "SLIM SAT: Rcvd master capability\n");
+capability_retry:
+		txn.rl = 8;
+		ret = ngd_xfer_msg(&dev->ctrl, &txn);
+		if (!ret) {
+			enum msm_ctrl_state prev_state = dev->state;
+
+			SLIM_INFO(dev,
+				"SLIM SAT: capability exchange successful\n");
+			if (prev_state < MSM_CTRL_ASLEEP)
+				SLIM_WARN(dev,
+					"capability due to noise, state:%d\n",
+						prev_state);
+			complete(&dev->reconf);
+			/* ADSP SSR, send device_up notifications */
+			if (prev_state == MSM_CTRL_DOWN)
+				complete(&dev->qmi.slave_notify);
+		} else if (ret == -EIO) {
+			SLIM_WARN(dev, "capability message NACKed, retrying\n");
+			if (retries < INIT_MX_RETRIES) {
+				msleep(DEF_RETRY_MS);
+				retries++;
+				goto capability_retry;
+			}
+		} else {
+			SLIM_WARN(dev, "SLIM: capability TX failed:%d\n", ret);
+		}
+	}
+	return 0;
+}
+
+static int ngd_notify_slaves(void *data)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)data;
+	struct slim_controller *ctrl = &dev->ctrl;
+	struct slim_device *sbdev;
+	struct list_head *pos, *next;
+	int ret, i = 0;
+
+	ret = qmi_svc_event_notifier_register(SLIMBUS_QMI_SVC_ID,
+				SLIMBUS_QMI_SVC_V1,
+				SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+	if (ret) {
+		pr_err("Slimbus QMI service registration failed:%d", ret);
+		return ret;
+	}
+
+	while (!kthread_should_stop()) {
+		wait_for_completion_interruptible(&dev->qmi.slave_notify);
+		/* Probe devices for first notification */
+		if (!i) {
+			i++;
+			dev->err = 0;
+			if (dev->dev->of_node)
+				of_register_slim_devices(&dev->ctrl);
+
+			/*
+			 * Add devices registered with board-info now that
+			 * controller is up
+			 */
+			slim_ctrl_add_boarddevs(&dev->ctrl);
+			ngd_dom_init(dev);
+		} else {
+			slim_framer_booted(ctrl);
+		}
+		mutex_lock(&ctrl->m_ctrl);
+		list_for_each_safe(pos, next, &ctrl->devs) {
+			int j;
+
+			sbdev = list_entry(pos, struct slim_device, dev_list);
+			mutex_unlock(&ctrl->m_ctrl);
+			for (j = 0; j < LADDR_RETRY; j++) {
+				ret = slim_get_logical_addr(sbdev,
+						sbdev->e_addr,
+						6, &sbdev->laddr);
+				if (!ret)
+					break;
+				/* time for ADSP to assign LA */
+				msleep(20);
+			}
+			mutex_lock(&ctrl->m_ctrl);
+		}
+		mutex_unlock(&ctrl->m_ctrl);
+	}
+	return 0;
+}
+
+static void ngd_dom_down(struct msm_slim_ctrl *dev)
+{
+	struct slim_controller *ctrl = &dev->ctrl;
+	struct slim_device *sbdev;
+
+	mutex_lock(&dev->ssr_lock);
+	ngd_slim_enable(dev, false);
+	/* device up should be called again after SSR */
+	list_for_each_entry(sbdev, &ctrl->devs, dev_list)
+		slim_report_absent(sbdev);
+	SLIM_INFO(dev, "SLIM ADSP SSR (DOWN) done\n");
+	mutex_unlock(&dev->ssr_lock);
+}
+
+static void ngd_dom_up(struct work_struct *work)
+{
+	struct msm_slim_ss *dsp =
+		container_of(work, struct msm_slim_ss, dom_up);
+	struct msm_slim_ctrl *dev =
+		container_of(dsp, struct msm_slim_ctrl, dsp);
+	mutex_lock(&dev->ssr_lock);
+	ngd_slim_enable(dev, true);
+	mutex_unlock(&dev->ssr_lock);
+}
+
+static ssize_t show_mask(struct device *device, struct device_attribute *attr,
+			char *buf)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+	return snprintf(buf, sizeof(int), "%u\n", dev->ipc_log_mask);
+}
+
+static ssize_t set_mask(struct device *device, struct device_attribute *attr,
+			const char *buf, size_t count)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+	dev->ipc_log_mask = buf[0] - '0';
+	if (dev->ipc_log_mask > DBG_LEV)
+		dev->ipc_log_mask = DBG_LEV;
+	return count;
+}
+
+static DEVICE_ATTR(debug_mask, 0644, show_mask, set_mask);
+
+static int ngd_slim_probe(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev;
+	int ret;
+	struct resource		*bam_mem;
+	struct resource		*slim_mem;
+	struct resource		*irq, *bam_irq;
+	bool			rxreg_access = false;
+	bool			slim_mdm = false;
+	const char		*ext_modem_id = NULL;
+
+	slim_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_physical");
+	if (!slim_mem) {
+		dev_err(&pdev->dev, "no slimbus physical memory resource\n");
+		return -ENODEV;
+	}
+	bam_mem = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						"slimbus_bam_physical");
+	if (!bam_mem) {
+		dev_err(&pdev->dev, "no slimbus BAM memory resource\n");
+		return -ENODEV;
+	}
+	irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_irq");
+	if (!irq) {
+		dev_err(&pdev->dev, "no slimbus IRQ resource\n");
+		return -ENODEV;
+	}
+	bam_irq = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+						"slimbus_bam_irq");
+	if (!bam_irq) {
+		dev_err(&pdev->dev, "no slimbus BAM IRQ resource\n");
+		return -ENODEV;
+	}
+
+	dev = kzalloc(sizeof(struct msm_slim_ctrl), GFP_KERNEL);
+	if (IS_ERR_OR_NULL(dev)) {
+		dev_err(&pdev->dev, "no memory for MSM slimbus controller\n");
+		return PTR_ERR(dev);
+	}
+	dev->wr_comp = kzalloc(sizeof(struct completion *) * MSM_TX_BUFS,
+				GFP_KERNEL);
+	if (!dev->wr_comp) {
+		ret = -ENOMEM;
+		goto err_nobulk;
+	}
+
+	/* typical txn numbers and size used in bulk operation */
+	dev->bulk.buf_sz = SLIM_MAX_TXNS * 8;
+	dev->bulk.base = kzalloc(dev->bulk.buf_sz, GFP_KERNEL | GFP_DMA);
+	if (!dev->bulk.base) {
+		ret = -ENOMEM;
+		goto err_nobulk;
+	}
+
+	dev->dev = &pdev->dev;
+	platform_set_drvdata(pdev, dev);
+	slim_set_ctrldata(&dev->ctrl, dev);
+
+	/* Create IPC log context */
+	dev->ipc_slimbus_log = ipc_log_context_create(IPC_SLIMBUS_LOG_PAGES,
+						dev_name(dev->dev), 0);
+	if (!dev->ipc_slimbus_log)
+		dev_err(&pdev->dev, "error creating ipc_logging context\n");
+	else {
+		/* Initialize the log mask */
+		dev->ipc_log_mask = INFO_LEV;
+		dev->default_ipc_log_mask = INFO_LEV;
+		SLIM_INFO(dev, "start logging for slim dev %s\n",
+				dev_name(dev->dev));
+	}
+	ret = sysfs_create_file(&dev->dev->kobj, &dev_attr_debug_mask.attr);
+	if (ret) {
+		dev_err(&pdev->dev, "Failed to create dev. attr\n");
+		dev->sysfs_created = false;
+	} else
+		dev->sysfs_created = true;
+
+	dev->base = ioremap(slim_mem->start, resource_size(slim_mem));
+	if (!dev->base) {
+		dev_err(&pdev->dev, "IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_failed;
+	}
+	dev->bam.base = ioremap(bam_mem->start, resource_size(bam_mem));
+	if (!dev->bam.base) {
+		dev_err(&pdev->dev, "BAM IOremap failed\n");
+		ret = -ENOMEM;
+		goto err_ioremap_bam_failed;
+	}
+	if (pdev->dev.of_node) {
+
+		ret = of_property_read_u32(pdev->dev.of_node, "cell-index",
+					&dev->ctrl.nr);
+		if (ret) {
+			dev_err(&pdev->dev, "Cell index not specified:%d", ret);
+			goto err_ctrl_failed;
+		}
+		rxreg_access = of_property_read_bool(pdev->dev.of_node,
+					"qcom,rxreg-access");
+		of_property_read_u32(pdev->dev.of_node, "qcom,apps-ch-pipes",
+					&dev->pdata.apps_pipes);
+		of_property_read_u32(pdev->dev.of_node, "qcom,ea-pc",
+					&dev->pdata.eapc);
+		ret = of_property_read_string(pdev->dev.of_node,
+					"qcom,slim-mdm", &ext_modem_id);
+		if (!ret)
+			slim_mdm = true;
+	} else {
+		dev->ctrl.nr = pdev->id;
+	}
+	/*
+	 * Keep PGD's logical address as manager's. Query it when first data
+	 * channel request comes in
+	 */
+	dev->pgdla = SLIM_LA_MGR;
+	dev->ctrl.nchans = MSM_SLIM_NCHANS;
+	dev->ctrl.nports = MSM_SLIM_NPORTS;
+	dev->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
+	dev->framer.superfreq =
+		dev->framer.rootfreq / SLIM_CL_PER_SUPERFRAME_DIV8;
+	dev->ctrl.a_framer = &dev->framer;
+	dev->ctrl.clkgear = SLIM_MAX_CLK_GEAR;
+	dev->ctrl.set_laddr = ngd_set_laddr;
+	dev->ctrl.get_laddr = ngd_get_laddr;
+	dev->ctrl.allocbw = ngd_allocbw;
+	dev->ctrl.xfer_msg = ngd_xfer_msg;
+	dev->ctrl.xfer_user_msg = ngd_user_msg;
+	dev->ctrl.xfer_bulk_wr = ngd_bulk_wr;
+	dev->ctrl.wakeup = NULL;
+	dev->ctrl.alloc_port = msm_alloc_port;
+	dev->ctrl.dealloc_port = msm_dealloc_port;
+	dev->ctrl.port_xfer = msm_slim_port_xfer;
+	dev->ctrl.port_xfer_status = msm_slim_port_xfer_status;
+	dev->bam_mem = bam_mem;
+	dev->rx_slim = ngd_slim_rx;
+
+	init_completion(&dev->reconf);
+	init_completion(&dev->ctrl_up);
+	mutex_init(&dev->tx_lock);
+	mutex_init(&dev->ssr_lock);
+	spin_lock_init(&dev->tx_buf_lock);
+	spin_lock_init(&dev->rx_lock);
+	dev->ee = 1;
+	dev->irq = irq->start;
+	dev->bam.irq = bam_irq->start;
+	atomic_set(&dev->ssr_in_progress, 0);
+
+	if (rxreg_access)
+		dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+	else
+		dev->use_rx_msgqs = MSM_MSGQ_RESET;
+
+	/* Enable TX message queues by default as recommended by HW */
+	dev->use_tx_msgqs = MSM_MSGQ_RESET;
+
+	init_completion(&dev->rx_msgq_notify);
+	init_completion(&dev->qmi.slave_notify);
+
+	/* Register with framework */
+	ret = slim_add_numbered_controller(&dev->ctrl);
+	if (ret) {
+		dev_err(dev->dev, "error adding controller\n");
+		goto err_ctrl_failed;
+	}
+
+	dev->ctrl.dev.parent = &pdev->dev;
+	dev->ctrl.dev.of_node = pdev->dev.of_node;
+	dev->state = MSM_CTRL_DOWN;
+
+	/*
+	 * As this does not perform expensive
+	 * operations, it can execute in an
+	 * interrupt context. This avoids
+	 * context switches, provides
+	 * extensive benifits and performance
+	 * improvements.
+	 */
+	ret = request_irq(dev->irq,
+			ngd_slim_interrupt,
+			IRQF_TRIGGER_HIGH,
+			"ngd_slim_irq", dev);
+
+	if (ret) {
+		dev_err(&pdev->dev, "request IRQ failed\n");
+		goto err_request_irq_failed;
+	}
+
+	init_completion(&dev->qmi.qmi_comp);
+	dev->err = -EPROBE_DEFER;
+	pm_runtime_use_autosuspend(dev->dev);
+	pm_runtime_set_autosuspend_delay(dev->dev, MSM_SLIM_AUTOSUSPEND);
+	pm_runtime_set_suspended(dev->dev);
+	pm_runtime_enable(dev->dev);
+
+	if (slim_mdm) {
+		dev->ext_mdm.nb.notifier_call = mdm_ssr_notify_cb;
+		dev->ext_mdm.domr = subsys_notif_register_notifier(ext_modem_id,
+							&dev->ext_mdm.nb);
+		if (IS_ERR_OR_NULL(dev->ext_mdm.domr))
+			dev_err(dev->dev,
+				"subsys_notif_register_notifier failed %p",
+				dev->ext_mdm.domr);
+	}
+
+	INIT_WORK(&dev->dsp.dom_up, ngd_dom_up);
+	dev->qmi.nb.notifier_call = ngd_qmi_available;
+	pm_runtime_get_noresume(dev->dev);
+
+	/* Fire up the Rx message queue thread */
+	dev->rx_msgq_thread = kthread_run(ngd_slim_rx_msgq_thread, dev,
+					"ngd_rx_thread%d", dev->ctrl.nr);
+	if (IS_ERR(dev->rx_msgq_thread)) {
+		ret = PTR_ERR(dev->rx_msgq_thread);
+		dev_err(dev->dev, "Failed to start Rx thread:%d\n", ret);
+		goto err_rx_thread_create_failed;
+	}
+
+	/* Start thread to probe, and notify slaves */
+	dev->qmi.slave_thread = kthread_run(ngd_notify_slaves, dev,
+					"ngd_notify_sl%d", dev->ctrl.nr);
+	if (IS_ERR(dev->qmi.slave_thread)) {
+		ret = PTR_ERR(dev->qmi.slave_thread);
+		dev_err(dev->dev, "Failed to start notifier thread:%d\n", ret);
+		goto err_notify_thread_create_failed;
+	}
+	SLIM_INFO(dev, "NGD SB controller is up!\n");
+	return 0;
+
+err_notify_thread_create_failed:
+	kthread_stop(dev->rx_msgq_thread);
+err_rx_thread_create_failed:
+	free_irq(dev->irq, dev);
+err_request_irq_failed:
+err_ctrl_failed:
+	iounmap(dev->bam.base);
+err_ioremap_bam_failed:
+	iounmap(dev->base);
+err_ioremap_failed:
+	if (dev->sysfs_created)
+		sysfs_remove_file(&dev->dev->kobj,
+				&dev_attr_debug_mask.attr);
+	kfree(dev->bulk.base);
+err_nobulk:
+	kfree(dev->wr_comp);
+	kfree(dev);
+	return ret;
+}
+
+static int ngd_slim_remove(struct platform_device *pdev)
+{
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+	ngd_slim_enable(dev, false);
+	if (dev->sysfs_created)
+		sysfs_remove_file(&dev->dev->kobj,
+				&dev_attr_debug_mask.attr);
+	qmi_svc_event_notifier_unregister(SLIMBUS_QMI_SVC_ID,
+				SLIMBUS_QMI_SVC_V1,
+				SLIMBUS_QMI_INS_ID, &dev->qmi.nb);
+	pm_runtime_disable(&pdev->dev);
+	if (dev->dsp.dom_t == MSM_SLIM_DOM_SS)
+		subsys_notif_unregister_notifier(dev->dsp.domr,
+						&dev->dsp.nb);
+	if (dev->dsp.dom_t == MSM_SLIM_DOM_PD)
+		service_notif_unregister_notifier(dev->dsp.domr,
+						&dev->dsp.nb);
+	if (!IS_ERR_OR_NULL(dev->ext_mdm.domr))
+		subsys_notif_unregister_notifier(dev->ext_mdm.domr,
+						&dev->ext_mdm.nb);
+	kfree(dev->bulk.base);
+	free_irq(dev->irq, dev);
+	slim_del_controller(&dev->ctrl);
+	kthread_stop(dev->rx_msgq_thread);
+	iounmap(dev->bam.base);
+	iounmap(dev->base);
+	kfree(dev->wr_comp);
+	kfree(dev);
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int ngd_slim_runtime_idle(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+
+	mutex_lock(&dev->tx_lock);
+	if (dev->state == MSM_CTRL_AWAKE)
+		dev->state = MSM_CTRL_IDLE;
+	mutex_unlock(&dev->tx_lock);
+	dev_dbg(device, "pm_runtime: idle...\n");
+	pm_request_autosuspend(device);
+	return -EAGAIN;
+}
+#endif
+
+/*
+ * If PM_RUNTIME is not defined, these 2 functions become helper
+ * functions to be called from system suspend/resume. So they are not
+ * inside ifdef CONFIG_PM_RUNTIME
+ */
+static int ngd_slim_runtime_resume(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	mutex_lock(&dev->tx_lock);
+	if (dev->state >= MSM_CTRL_ASLEEP)
+		ret = ngd_slim_power_up(dev, false);
+	if (ret) {
+		/* Did SSR cause this power up failure */
+		if (dev->state != MSM_CTRL_DOWN)
+			dev->state = MSM_CTRL_ASLEEP;
+		else
+			SLIM_WARN(dev, "HW wakeup attempt during SSR\n");
+	} else {
+		dev->state = MSM_CTRL_AWAKE;
+	}
+	mutex_unlock(&dev->tx_lock);
+	SLIM_INFO(dev, "Slim runtime resume: ret %d\n", ret);
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static int ngd_slim_runtime_suspend(struct device *device)
+{
+	struct platform_device *pdev = to_platform_device(device);
+	struct msm_slim_ctrl *dev = platform_get_drvdata(pdev);
+	int ret = 0;
+
+	mutex_lock(&dev->tx_lock);
+	ret = ngd_slim_power_down(dev);
+	if (ret && ret != -EBUSY)
+		SLIM_INFO(dev, "slim resource not idle:%d\n", ret);
+	if (!ret || ret == -ETIMEDOUT)
+		dev->state = MSM_CTRL_ASLEEP;
+	mutex_unlock(&dev->tx_lock);
+	SLIM_INFO(dev, "Slim runtime suspend: ret %d\n", ret);
+	return ret;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+static int ngd_slim_suspend(struct device *dev)
+{
+	int ret = -EBUSY;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
+
+	if (!pm_runtime_enabled(dev) ||
+		(!pm_runtime_suspended(dev) &&
+			cdev->state == MSM_CTRL_IDLE)) {
+		ret = ngd_slim_runtime_suspend(dev);
+		/*
+		 * If runtime-PM still thinks it's active, then make sure its
+		 * status is in sync with HW status.
+		 * Since this suspend calls QMI api, it results in holding a
+		 * wakelock. That results in failure of first suspend.
+		 * Subsequent suspend should not call low-power transition
+		 * again since the HW is already in suspended state.
+		 */
+		if (!ret) {
+			pm_runtime_disable(dev);
+			pm_runtime_set_suspended(dev);
+			pm_runtime_enable(dev);
+		}
+	}
+	if (ret == -EBUSY) {
+		/*
+		 * There is a possibility that some audio stream is active
+		 * during suspend. We dont want to return suspend failure in
+		 * that case so that display and relevant components can still
+		 * go to suspend.
+		 * If there is some other error, then it should be passed-on
+		 * to system level suspend
+		 */
+		ret = 0;
+	}
+	SLIM_INFO(cdev, "system suspend\n");
+	return ret;
+}
+
+static int ngd_slim_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct msm_slim_ctrl *cdev = platform_get_drvdata(pdev);
+	/*
+	 * Rely on runtime-PM to call resume in case it is enabled.
+	 * Even if it's not enabled, rely on 1st client transaction to do
+	 * clock/power on
+	 */
+	SLIM_INFO(cdev, "system resume\n");
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops ngd_slim_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		ngd_slim_suspend,
+		ngd_slim_resume
+	)
+	SET_RUNTIME_PM_OPS(
+		ngd_slim_runtime_suspend,
+		ngd_slim_runtime_resume,
+		ngd_slim_runtime_idle
+	)
+};
+
+static const struct of_device_id ngd_slim_dt_match[] = {
+	{
+		.compatible = "qcom,slim-ngd",
+	},
+	{}
+};
+
+static struct platform_driver ngd_slim_driver = {
+	.probe = ngd_slim_probe,
+	.remove = ngd_slim_remove,
+	.driver	= {
+		.name = NGD_SLIM_NAME,
+		.owner = THIS_MODULE,
+		.pm = &ngd_slim_dev_pm_ops,
+		.of_match_table = ngd_slim_dt_match,
+	},
+};
+
+static int ngd_slim_init(void)
+{
+	return platform_driver_register(&ngd_slim_driver);
+}
+late_initcall(ngd_slim_init);
+
+static void ngd_slim_exit(void)
+{
+	platform_driver_unregister(&ngd_slim_driver);
+}
+module_exit(ngd_slim_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Slimbus controller");
+MODULE_ALIAS("platform:msm-slim-ngd");
diff --git a/drivers/slimbus/slim-msm.c b/drivers/slimbus/slim-msm.c
new file mode 100644
index 0000000..b426a2c
--- /dev/null
+++ b/drivers/slimbus/slim-msm.c
@@ -0,0 +1,1634 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/msm-sps.h>
+#include <linux/gcd.h>
+#include "slim-msm.h"
+
+int msm_slim_rx_enqueue(struct msm_slim_ctrl *dev, u32 *buf, u8 len)
+{
+	spin_lock(&dev->rx_lock);
+	if ((dev->tail + 1) % MSM_CONCUR_MSG == dev->head) {
+		spin_unlock(&dev->rx_lock);
+		dev_err(dev->dev, "RX QUEUE full!");
+		return -EXFULL;
+	}
+	memcpy((u8 *)dev->rx_msgs[dev->tail], (u8 *)buf, len);
+	dev->tail = (dev->tail + 1) % MSM_CONCUR_MSG;
+	spin_unlock(&dev->rx_lock);
+	return 0;
+}
+
+int msm_slim_rx_dequeue(struct msm_slim_ctrl *dev, u8 *buf)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->rx_lock, flags);
+	if (dev->tail == dev->head) {
+		spin_unlock_irqrestore(&dev->rx_lock, flags);
+		return -ENODATA;
+	}
+	memcpy(buf, (u8 *)dev->rx_msgs[dev->head], 40);
+	dev->head = (dev->head + 1) % MSM_CONCUR_MSG;
+	spin_unlock_irqrestore(&dev->rx_lock, flags);
+	return 0;
+}
+
+int msm_slim_get_ctrl(struct msm_slim_ctrl *dev)
+{
+#ifdef CONFIG_PM
+	int ref = 0;
+	int ret = pm_runtime_get_sync(dev->dev);
+
+	if (ret >= 0) {
+		ref = atomic_read(&dev->dev->power.usage_count);
+		if (ref <= 0) {
+			SLIM_WARN(dev, "reference count -ve:%d", ref);
+			ret = -ENODEV;
+		}
+	}
+	return ret;
+#else
+	return -ENODEV;
+#endif
+}
+void msm_slim_put_ctrl(struct msm_slim_ctrl *dev)
+{
+#ifdef CONFIG_PM
+	int ref;
+
+	pm_runtime_mark_last_busy(dev->dev);
+	ref = atomic_read(&dev->dev->power.usage_count);
+	if (ref <= 0)
+		SLIM_WARN(dev, "reference count mismatch:%d", ref);
+	else
+		pm_runtime_put_sync(dev->dev);
+#endif
+}
+
+irqreturn_t msm_slim_port_irq_handler(struct msm_slim_ctrl *dev, u32 pstat)
+{
+	int i;
+	u32 int_en = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+							dev->ver));
+	/*
+	 * different port-interrupt than what we enabled, ignore.
+	 * This may happen if overflow/underflow is reported, but
+	 * was disabled due to unavailability of buffers provided by
+	 * client.
+	 */
+	if ((pstat & int_en) == 0)
+		return IRQ_HANDLED;
+	for (i = 0; i < dev->port_nums; i++) {
+		struct msm_slim_endp *endpoint = &dev->pipes[i];
+
+		if (pstat & (1 << endpoint->port_b)) {
+			u32 val = readl_relaxed(PGD_PORT(PGD_PORT_STATn,
+					endpoint->port_b, dev->ver));
+			if (val & MSM_PORT_OVERFLOW) {
+				dev->ctrl.ports[i].err =
+						SLIM_P_OVERFLOW;
+			} else if (val & MSM_PORT_UNDERFLOW) {
+				dev->ctrl.ports[i].err =
+					SLIM_P_UNDERFLOW;
+			}
+		}
+	}
+	/*
+	 * Disable port interrupt here. Re-enable when more
+	 * buffers are provided for this port.
+	 */
+	writel_relaxed((int_en & (~pstat)),
+			PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+					dev->ver));
+	/* clear port interrupts */
+	writel_relaxed(pstat, PGD_THIS_EE(PGD_PORT_INT_CL_EEn,
+							dev->ver));
+	SLIM_INFO(dev, "disabled overflow/underflow for port 0x%x", pstat);
+
+	/*
+	 * Guarantee that port interrupt bit(s) clearing writes go
+	 * through before exiting ISR
+	 */
+	mb();
+	return IRQ_HANDLED;
+}
+
+int msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep)
+{
+	int ret;
+	struct sps_pipe *endpoint;
+	struct sps_connect *config = &ep->config;
+
+	/* Allocate the endpoint */
+	endpoint = sps_alloc_endpoint();
+	if (!endpoint) {
+		dev_err(dev->dev, "sps_alloc_endpoint failed\n");
+		return -ENOMEM;
+	}
+
+	/* Get default connection configuration for an endpoint */
+	ret = sps_get_config(endpoint, config);
+	if (ret) {
+		dev_err(dev->dev, "sps_get_config failed 0x%x\n", ret);
+		goto sps_config_failed;
+	}
+
+	ep->sps = endpoint;
+	return 0;
+
+sps_config_failed:
+	sps_free_endpoint(endpoint);
+	return ret;
+}
+
+void msm_slim_free_endpoint(struct msm_slim_endp *ep)
+{
+	sps_free_endpoint(ep->sps);
+	ep->sps = NULL;
+}
+
+int msm_slim_sps_mem_alloc(
+		struct msm_slim_ctrl *dev, struct sps_mem_buffer *mem, u32 len)
+{
+	dma_addr_t phys;
+
+	mem->size = len;
+	mem->min_size = 0;
+	mem->base = dma_alloc_coherent(dev->dev, mem->size, &phys, GFP_KERNEL);
+
+	if (!mem->base) {
+		dev_err(dev->dev, "dma_alloc_coherent(%d) failed\n", len);
+		return -ENOMEM;
+	}
+
+	mem->phys_base = phys;
+	memset(mem->base, 0x00, mem->size);
+	return 0;
+}
+
+void
+msm_slim_sps_mem_free(struct msm_slim_ctrl *dev, struct sps_mem_buffer *mem)
+{
+	if (mem->base && mem->phys_base)
+		dma_free_coherent(dev->dev, mem->size, mem->base,
+							mem->phys_base);
+	else
+		dev_err(dev->dev, "cant dma free. they are NULL\n");
+	mem->size = 0;
+	mem->base = NULL;
+	mem->phys_base = 0;
+}
+
+void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pipenum, u8 portnum)
+{
+	struct slim_controller *ctrl;
+	struct slim_ch *chan;
+	struct msm_slim_pshpull_parm *parm;
+	u32 set_cfg = 0;
+	struct slim_port_cfg cfg = dev->ctrl.ports[portnum].cfg;
+
+	if (!dev) {
+		pr_err("%s:Dev node is null\n", __func__);
+		return;
+	}
+	if (portnum >= dev->port_nums) {
+		pr_err("%s:Invalid port\n", __func__);
+		return;
+	}
+	ctrl = &dev->ctrl;
+	chan = ctrl->ports[portnum].ch;
+	parm = &dev->pipes[portnum].psh_pull;
+
+	if (cfg.watermark)
+		set_cfg = (cfg.watermark << 1);
+	else
+		set_cfg = DEF_WATERMARK;
+
+	if (cfg.port_opts & SLIM_OPT_NO_PACK)
+		set_cfg |= DEF_NO_PACK;
+	else
+		set_cfg |= DEF_PACK;
+
+	if (cfg.port_opts & SLIM_OPT_ALIGN_MSB)
+		set_cfg |= DEF_ALIGN_MSB;
+	else
+		set_cfg |= DEF_ALIGN_LSB;
+
+	set_cfg |= ENABLE_PORT;
+
+	writel_relaxed(set_cfg, PGD_PORT(PGD_PORT_CFGn, pipenum, dev->ver));
+	writel_relaxed(DEF_BLKSZ, PGD_PORT(PGD_PORT_BLKn, pipenum, dev->ver));
+	writel_relaxed(DEF_TRANSZ, PGD_PORT(PGD_PORT_TRANn, pipenum, dev->ver));
+
+	if (chan->prot == SLIM_PUSH || chan->prot == SLIM_PULL) {
+		set_cfg = 0;
+		set_cfg |= ((0xFFFF & parm->num_samples)<<16);
+		set_cfg |= (0xFFFF & parm->rpt_period);
+		writel_relaxed(set_cfg, PGD_PORT(PGD_PORT_PSHPLLn,
+							pipenum, dev->ver));
+	}
+	/* Make sure that port registers are updated before returning */
+	mb();
+}
+
+static void msm_slim_disconn_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
+{
+	struct msm_slim_endp *endpoint = &dev->pipes[pn];
+	struct sps_register_event sps_event;
+	u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+					dev->ver));
+	writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn, (endpoint->port_b),
+					dev->ver));
+	writel_relaxed((int_port & ~(1 << endpoint->port_b)),
+		PGD_THIS_EE(PGD_PORT_INT_EN_EEn, dev->ver));
+	/* Make sure port register is updated */
+	mb();
+	memset(&sps_event, 0, sizeof(sps_event));
+	sps_register_event(endpoint->sps, &sps_event);
+	sps_disconnect(endpoint->sps);
+	dev->pipes[pn].connected = false;
+}
+
+static void msm_slim_calc_pshpull_parm(struct msm_slim_ctrl *dev,
+					u8 pn, struct slim_ch *prop)
+{
+	struct msm_slim_endp *endpoint = &dev->pipes[pn];
+	struct msm_slim_pshpull_parm *parm = &endpoint->psh_pull;
+	int	chan_freq, round_off, divisor, super_freq;
+
+	super_freq = dev->ctrl.a_framer->superfreq;
+
+	if (prop->baser == SLIM_RATE_4000HZ)
+		chan_freq = 4000 * prop->ratem;
+	else if (prop->baser == SLIM_RATE_11025HZ)
+		chan_freq = 11025 * prop->ratem;
+	else
+		chan_freq = prop->baser * prop->ratem;
+
+	/*
+	 * If channel frequency is multiple of super frame frequency
+	 * ISO protocol is suggested
+	 */
+	if (!(chan_freq % super_freq)) {
+		prop->prot = SLIM_HARD_ISO;
+		return;
+	}
+	round_off = DIV_ROUND_UP(chan_freq, super_freq);
+	divisor = gcd(round_off * super_freq, chan_freq);
+	parm->num_samples = chan_freq/divisor;
+	parm->rpt_period = (round_off * super_freq)/divisor;
+}
+
+int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn)
+{
+	struct msm_slim_endp *endpoint;
+	struct sps_connect *cfg;
+	struct slim_ch *prop;
+	u32 stat;
+	int ret;
+
+	if (!dev || pn >= dev->port_nums)
+		return -ENODEV;
+	endpoint = &dev->pipes[pn];
+	cfg = &endpoint->config;
+	prop = dev->ctrl.ports[pn].ch;
+
+	endpoint = &dev->pipes[pn];
+	ret = sps_get_config(dev->pipes[pn].sps, cfg);
+	if (ret) {
+		dev_err(dev->dev, "sps pipe-port get config error%x\n", ret);
+		return ret;
+	}
+	cfg->options = SPS_O_DESC_DONE | SPS_O_ERROR |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	if (prop->prot == SLIM_PUSH || prop->prot ==  SLIM_PULL)
+		msm_slim_calc_pshpull_parm(dev, pn, prop);
+
+	if (dev->pipes[pn].connected &&
+			dev->ctrl.ports[pn].state == SLIM_P_CFG) {
+		return -EISCONN;
+	} else if (dev->pipes[pn].connected) {
+		writel_relaxed(0, PGD_PORT(PGD_PORT_CFGn,
+			(endpoint->port_b), dev->ver));
+		/* Make sure port disabling goes through */
+		mb();
+		/* Is pipe already connected in desired direction */
+		if ((dev->ctrl.ports[pn].flow == SLIM_SRC &&
+			cfg->mode == SPS_MODE_DEST) ||
+			(dev->ctrl.ports[pn].flow == SLIM_SINK &&
+			 cfg->mode == SPS_MODE_SRC)) {
+			msm_hw_set_port(dev, endpoint->port_b, pn);
+			return 0;
+		}
+		msm_slim_disconn_pipe_port(dev, pn);
+	}
+
+	stat = readl_relaxed(PGD_PORT(PGD_PORT_STATn, endpoint->port_b,
+					dev->ver));
+	if (dev->ctrl.ports[pn].flow == SLIM_SRC) {
+		cfg->destination = dev->bam.hdl;
+		cfg->source = SPS_DEV_HANDLE_MEM;
+		cfg->dest_pipe_index = ((stat & (0xFF << 4)) >> 4);
+		cfg->src_pipe_index = 0;
+		dev_dbg(dev->dev, "flow src:pipe num:%d",
+					cfg->dest_pipe_index);
+		cfg->mode = SPS_MODE_DEST;
+	} else {
+		cfg->source = dev->bam.hdl;
+		cfg->destination = SPS_DEV_HANDLE_MEM;
+		cfg->src_pipe_index = ((stat & (0xFF << 4)) >> 4);
+		cfg->dest_pipe_index = 0;
+		dev_dbg(dev->dev, "flow dest:pipe num:%d",
+					cfg->src_pipe_index);
+		cfg->mode = SPS_MODE_SRC;
+	}
+	/* Space for desciptor FIFOs */
+	ret = msm_slim_sps_mem_alloc(dev, &cfg->desc,
+				MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
+	if (ret)
+		pr_err("mem alloc for descr failed:%d", ret);
+	else
+		ret = sps_connect(dev->pipes[pn].sps, cfg);
+
+	if (!ret) {
+		dev->pipes[pn].connected = true;
+		msm_hw_set_port(dev, endpoint->port_b, pn);
+	}
+	return ret;
+}
+
+int msm_alloc_port(struct slim_controller *ctrl, u8 pn)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	struct msm_slim_endp *endpoint;
+	int ret = 0;
+
+	if (ctrl->ports[pn].req == SLIM_REQ_HALF_DUP ||
+		ctrl->ports[pn].req == SLIM_REQ_MULTI_CH)
+		return -EPROTONOSUPPORT;
+	if (pn >= dev->port_nums)
+		return -ENODEV;
+
+	endpoint = &dev->pipes[pn];
+	ret = msm_slim_init_endpoint(dev, endpoint);
+	dev_dbg(dev->dev, "sps register bam error code:%x\n", ret);
+	return ret;
+}
+
+void msm_dealloc_port(struct slim_controller *ctrl, u8 pn)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+	struct msm_slim_endp *endpoint;
+
+	if (pn >= dev->port_nums)
+		return;
+	endpoint = &dev->pipes[pn];
+	if (dev->pipes[pn].connected) {
+		struct sps_connect *config = &endpoint->config;
+
+		msm_slim_disconn_pipe_port(dev, pn);
+		msm_slim_sps_mem_free(dev, &config->desc);
+	}
+	if (endpoint->sps)
+		msm_slim_free_endpoint(endpoint);
+}
+
+enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
+				u8 pn, phys_addr_t *done_buf, u32 *done_len)
+{
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctr);
+	struct sps_iovec sio;
+	int ret;
+
+	if (done_len)
+		*done_len = 0;
+	if (done_buf)
+		*done_buf = 0;
+	if (!dev->pipes[pn].connected)
+		return SLIM_P_DISCONNECT;
+	ret = sps_get_iovec(dev->pipes[pn].sps, &sio);
+	if (!ret) {
+		if (done_len)
+			*done_len = sio.size;
+		if (done_buf)
+			*done_buf = (phys_addr_t)sio.addr;
+	}
+	dev_dbg(dev->dev, "get iovec returned %d\n", ret);
+	return SLIM_P_INPROGRESS;
+}
+
+static void msm_slim_port_cb(struct sps_event_notify *ev)
+{
+
+	struct completion *comp = ev->data.transfer.user;
+	struct sps_iovec *iovec = &ev->data.transfer.iovec;
+
+	if (ev->event_id == SPS_EVENT_DESC_DONE) {
+
+		pr_debug("desc done iovec = (0x%x 0x%x 0x%x)\n",
+			iovec->addr, iovec->size, iovec->flags);
+
+	} else {
+		pr_err("%s: ERR event %d\n",
+					__func__, ev->event_id);
+	}
+	if (comp)
+		complete(comp);
+}
+
+int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
+			u32 len, struct completion *comp)
+{
+	struct sps_register_event sreg;
+	int ret;
+	struct msm_slim_ctrl *dev = slim_get_ctrldata(ctrl);
+
+	if (pn >= dev->port_nums)
+		return -ENODEV;
+
+	if (!dev->pipes[pn].connected)
+		return -ENOTCONN;
+
+	sreg.options = (SPS_EVENT_DESC_DONE|SPS_EVENT_ERROR);
+	sreg.mode = SPS_TRIGGER_WAIT;
+	sreg.xfer_done = NULL;
+	sreg.callback = msm_slim_port_cb;
+	sreg.user = NULL;
+	ret = sps_register_event(dev->pipes[pn].sps, &sreg);
+	if (ret) {
+		dev_dbg(dev->dev, "sps register event error:%x\n", ret);
+		return ret;
+	}
+	ret = sps_transfer_one(dev->pipes[pn].sps, iobuf, len, comp,
+				SPS_IOVEC_FLAG_INT);
+	dev_dbg(dev->dev, "sps submit xfer error code:%x\n", ret);
+	if (!ret) {
+		/* Enable port interrupts */
+		u32 int_port = readl_relaxed(PGD_THIS_EE(PGD_PORT_INT_EN_EEn,
+						dev->ver));
+		if (!(int_port & (1 << (dev->pipes[pn].port_b))))
+			writel_relaxed((int_port |
+				(1 << dev->pipes[pn].port_b)),
+				PGD_THIS_EE(PGD_PORT_INT_EN_EEn, dev->ver));
+		/* Make sure that port registers are updated before returning */
+		mb();
+	}
+
+	return ret;
+}
+
+/* Queue up Tx message buffer */
+static int msm_slim_post_tx_msgq(struct msm_slim_ctrl *dev, u8 *buf, int len)
+{
+	int ret;
+	struct msm_slim_endp *endpoint = &dev->tx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct sps_pipe *pipe = endpoint->sps;
+	int ix = (buf - (u8 *)mem->base);
+
+	phys_addr_t phys_addr = mem->phys_base + ix;
+
+	for (ret = 0; ret < ((len + 3) >> 2); ret++)
+		pr_debug("BAM TX buf[%d]:0x%x", ret, ((u32 *)buf)[ret]);
+
+	ret = sps_transfer_one(pipe, phys_addr, ((len + 3) & 0xFC), NULL,
+				SPS_IOVEC_FLAG_EOT);
+	if (ret)
+		dev_err(dev->dev, "transfer_one() failed 0x%x, %d\n", ret, ix);
+
+	return ret;
+}
+
+void msm_slim_tx_msg_return(struct msm_slim_ctrl *dev, int err)
+{
+	struct msm_slim_endp *endpoint = &dev->tx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct sps_pipe *pipe = endpoint->sps;
+	struct sps_iovec iovec;
+	int idx, ret = 0;
+	phys_addr_t addr;
+
+	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
+		/* use 1 buffer, non-blocking writes are not possible */
+		if (dev->wr_comp[0]) {
+			struct completion *comp = dev->wr_comp[0];
+
+			dev->wr_comp[0] = NULL;
+			complete(comp);
+		}
+		return;
+	}
+	while (!ret) {
+		memset(&iovec, 0, sizeof(iovec));
+		ret = sps_get_iovec(pipe, &iovec);
+		addr = DESC_FULL_ADDR(iovec.flags, iovec.addr);
+		if (ret || addr == 0) {
+			if (ret)
+				pr_err("SLIM TX get IOVEC failed:%d", ret);
+			return;
+		}
+		if (addr == dev->bulk.wr_dma) {
+			dma_unmap_single(dev->dev, dev->bulk.wr_dma,
+					 dev->bulk.size, DMA_TO_DEVICE);
+			if (!dev->bulk.cb)
+				SLIM_WARN(dev, "no callback for bulk WR?");
+			else
+				dev->bulk.cb(dev->bulk.ctx, err);
+			dev->bulk.in_progress = false;
+			pm_runtime_mark_last_busy(dev->dev);
+			return;
+		} else if (addr < mem->phys_base ||
+			   (addr > (mem->phys_base +
+				    (MSM_TX_BUFS * SLIM_MSGQ_BUF_LEN)))) {
+			SLIM_WARN(dev, "BUF out of bounds:base:0x%pa, io:0x%pa",
+					&mem->phys_base, &addr);
+			continue;
+		}
+		idx = (int) ((addr - mem->phys_base)
+			/ SLIM_MSGQ_BUF_LEN);
+		if (dev->wr_comp[idx]) {
+			struct completion *comp = dev->wr_comp[idx];
+
+			dev->wr_comp[idx] = NULL;
+			complete(comp);
+		}
+		if (err) {
+			int i;
+			u32 *addr = (u32 *)mem->base +
+					(idx * (SLIM_MSGQ_BUF_LEN >> 2));
+			/* print the descriptor that resulted in error */
+			for (i = 0; i < (SLIM_MSGQ_BUF_LEN >> 2); i++)
+				SLIM_WARN(dev, "err desc[%d]:0x%x", i, addr[i]);
+		}
+		/* reclaim all packets that were delivered out of order */
+		if (idx != dev->tx_head)
+			SLIM_WARN(dev, "SLIM OUT OF ORDER TX:idx:%d, head:%d",
+				idx, dev->tx_head);
+		dev->tx_head = (dev->tx_head + 1) % MSM_TX_BUFS;
+	}
+}
+
+static u32 *msm_slim_modify_tx_buf(struct msm_slim_ctrl *dev,
+					struct completion *comp)
+{
+	struct msm_slim_endp *endpoint = &dev->tx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	u32 *retbuf = NULL;
+
+	if ((dev->tx_tail + 1) % MSM_TX_BUFS == dev->tx_head)
+		return NULL;
+
+	retbuf = (u32 *)((u8 *)mem->base +
+				(dev->tx_tail * SLIM_MSGQ_BUF_LEN));
+	dev->wr_comp[dev->tx_tail] = comp;
+	dev->tx_tail = (dev->tx_tail + 1) % MSM_TX_BUFS;
+	return retbuf;
+}
+u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
+					struct completion *comp, int err)
+{
+	int ret = 0;
+	int retries = 0;
+	u32 *retbuf = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&dev->tx_buf_lock, flags);
+	if (!getbuf) {
+		msm_slim_tx_msg_return(dev, err);
+		spin_unlock_irqrestore(&dev->tx_buf_lock, flags);
+		return NULL;
+	}
+
+	retbuf = msm_slim_modify_tx_buf(dev, comp);
+	if (retbuf) {
+		spin_unlock_irqrestore(&dev->tx_buf_lock, flags);
+		return retbuf;
+	}
+
+	do {
+		msm_slim_tx_msg_return(dev, err);
+		retbuf = msm_slim_modify_tx_buf(dev, comp);
+		if (!retbuf)
+			ret = -EAGAIN;
+		else {
+			if (retries > 0)
+				SLIM_INFO(dev, "SLIM TX retrieved:%d retries",
+							retries);
+			spin_unlock_irqrestore(&dev->tx_buf_lock, flags);
+			return retbuf;
+		}
+
+		/*
+		 * superframe size will vary based on clock gear
+		 * 1 superframe will consume at least 1 message
+		 * if HW is in good condition. With MX_RETRIES,
+		 * make sure we wait for ~2 superframes
+		 * before deciding HW couldn't process descriptors
+		 */
+		udelay(50);
+		retries++;
+	} while (ret && (retries < INIT_MX_RETRIES));
+
+	spin_unlock_irqrestore(&dev->tx_buf_lock, flags);
+	return NULL;
+}
+
+int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg)
+{
+	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
+		int i;
+
+		for (i = 0; i < (len + 3) >> 2; i++) {
+			dev_dbg(dev->dev, "AHB TX data:0x%x\n", buf[i]);
+			writel_relaxed(buf[i], dev->base + tx_reg + (i * 4));
+		}
+		/* Guarantee that message is sent before returning */
+		mb();
+		return 0;
+	}
+	return msm_slim_post_tx_msgq(dev, (u8 *)buf, len);
+}
+
+u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
+			struct completion *comp)
+{
+	/*
+	 * Currently we block a transaction until the current one completes.
+	 * In case we need multiple transactions, use message Q
+	 */
+	if (dev->use_tx_msgqs != MSM_MSGQ_ENABLED) {
+		dev->wr_comp[0] = comp;
+		return dev->tx_buf;
+	}
+
+	return msm_slim_manage_tx_msgq(dev, true, comp, 0);
+}
+
+static void
+msm_slim_rx_msgq_event(struct msm_slim_ctrl *dev, struct sps_event_notify *ev)
+{
+	if (ev->event_id == SPS_EVENT_DESC_DONE)
+		complete(&dev->rx_msgq_notify);
+	else
+		dev_err(dev->dev, "%s: unknown event %d\n",
+					__func__, ev->event_id);
+}
+
+static void
+msm_slim_handle_rx(struct msm_slim_ctrl *dev, struct sps_event_notify *ev)
+{
+	int ret = 0;
+	u32 mc = 0;
+	u32 mt = 0;
+	u8 msg_len = 0;
+
+	if (ev->event_id != SPS_EVENT_EOT) {
+		dev_err(dev->dev, "%s: unknown event %d\n",
+					__func__, ev->event_id);
+		return;
+	}
+
+	do {
+		ret = msm_slim_rx_msgq_get(dev, dev->current_rx_buf,
+					   dev->current_count);
+		if (ret == -ENODATA) {
+			return;
+		} else if (ret) {
+			SLIM_ERR(dev, "rx_msgq_get() failed 0x%x\n",
+								ret);
+			return;
+		}
+
+		/* Traverse first byte of message for message length */
+		if (dev->current_count++ == 0) {
+			msg_len = *(dev->current_rx_buf) & 0x1F;
+			mt = (*(dev->current_rx_buf) >> 5) & 0x7;
+			mc = (*(dev->current_rx_buf) >> 8) & 0xff;
+			dev_dbg(dev->dev, "MC: %x, MT: %x\n", mc, mt);
+		}
+
+		msg_len = (msg_len < 4) ? 0 : (msg_len - 4);
+
+		if (!msg_len) {
+			dev->rx_slim(dev, (u8 *)dev->current_rx_buf);
+			dev->current_count = 0;
+		}
+
+	} while (1);
+}
+
+static void msm_slim_rx_msgq_cb(struct sps_event_notify *notify)
+{
+	struct msm_slim_ctrl *dev = (struct msm_slim_ctrl *)notify->user;
+	/* is this manager controller or NGD controller? */
+	if (dev->ctrl.wakeup)
+		msm_slim_rx_msgq_event(dev, notify);
+	else
+		msm_slim_handle_rx(dev, notify);
+}
+
+/* Queue up Rx message buffer */
+static int msm_slim_post_rx_msgq(struct msm_slim_ctrl *dev, int ix)
+{
+	int ret;
+	struct msm_slim_endp *endpoint = &dev->rx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct sps_pipe *pipe = endpoint->sps;
+
+	/* Rx message queue buffers are 4 bytes in length */
+	u8 *virt_addr = mem->base + (4 * ix);
+	phys_addr_t phys_addr = mem->phys_base + (4 * ix);
+
+	ret = sps_transfer_one(pipe, phys_addr, 4, virt_addr, 0);
+	if (ret)
+		dev_err(dev->dev, "transfer_one() failed 0x%x, %d\n", ret, ix);
+
+	return ret;
+}
+
+int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset)
+{
+	struct msm_slim_endp *endpoint = &dev->rx_msgq;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+	struct sps_pipe *pipe = endpoint->sps;
+	struct sps_iovec iovec;
+	phys_addr_t addr;
+	int index;
+	int ret;
+
+	ret = sps_get_iovec(pipe, &iovec);
+	if (ret) {
+		dev_err(dev->dev, "sps_get_iovec() failed 0x%x\n", ret);
+		goto err_exit;
+	}
+
+	addr = DESC_FULL_ADDR(iovec.flags, iovec.addr);
+	pr_debug("iovec = (0x%x 0x%x 0x%x)\n",
+		iovec.addr, iovec.size, iovec.flags);
+
+	/* no more descriptors */
+	if (!ret && (iovec.addr == 0) && (iovec.size == 0)) {
+		ret = -ENODATA;
+		goto err_exit;
+	}
+
+	/* Calculate buffer index */
+	index = (addr - mem->phys_base) / 4;
+	*(data + offset) = *((u32 *)mem->base + index);
+
+	pr_debug("buf = 0x%p, data = 0x%x\n", (u32 *)mem->base + index, *data);
+
+	/* Add buffer back to the queue */
+	(void)msm_slim_post_rx_msgq(dev, index);
+
+err_exit:
+	return ret;
+}
+
+int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
+				struct msm_slim_endp *endpoint)
+{
+	int i, ret;
+	struct sps_register_event sps_error_event; /* SPS_ERROR */
+	struct sps_register_event sps_descr_event; /* DESCR_DONE */
+	struct sps_connect *config = &endpoint->config;
+	unsigned long flags;
+
+	ret = sps_connect(endpoint->sps, config);
+	if (ret) {
+		dev_err(dev->dev, "sps_connect failed 0x%x\n", ret);
+		return ret;
+	}
+
+	memset(&sps_descr_event, 0x00, sizeof(sps_descr_event));
+
+	if (endpoint == &dev->rx_msgq) {
+		sps_descr_event.mode = SPS_TRIGGER_CALLBACK;
+		sps_descr_event.options = SPS_O_EOT;
+		sps_descr_event.user = (void *)dev;
+		sps_descr_event.callback = msm_slim_rx_msgq_cb;
+		sps_descr_event.xfer_done = NULL;
+
+		ret = sps_register_event(endpoint->sps, &sps_descr_event);
+		if (ret) {
+			dev_err(dev->dev, "sps_connect() failed 0x%x\n", ret);
+			goto sps_reg_event_failed;
+		}
+	}
+
+	/* Register callback for errors */
+	memset(&sps_error_event, 0x00, sizeof(sps_error_event));
+	sps_error_event.mode = SPS_TRIGGER_CALLBACK;
+	sps_error_event.options = SPS_O_ERROR;
+	sps_error_event.user = (void *)dev;
+	sps_error_event.callback = msm_slim_rx_msgq_cb;
+
+	ret = sps_register_event(endpoint->sps, &sps_error_event);
+	if (ret) {
+		dev_err(dev->dev, "sps_connect() failed 0x%x\n", ret);
+		goto sps_reg_event_failed;
+	}
+
+	/*
+	 * Call transfer_one for each 4-byte buffer
+	 * Use (buf->size/4) - 1 for the number of buffer to post
+	 */
+
+	if (endpoint == &dev->rx_msgq) {
+		/* Setup the transfer */
+		for (i = 0; i < (MSM_SLIM_DESC_NUM - 1); i++) {
+			ret = msm_slim_post_rx_msgq(dev, i);
+			if (ret) {
+				dev_err(dev->dev,
+					"post_rx_msgq() failed 0x%x\n", ret);
+				goto sps_transfer_failed;
+			}
+		}
+		dev->use_rx_msgqs = MSM_MSGQ_ENABLED;
+	} else {
+		spin_lock_irqsave(&dev->tx_buf_lock, flags);
+		dev->tx_tail = 0;
+		dev->tx_head = 0;
+		for (i = 0; i < MSM_TX_BUFS; i++)
+			dev->wr_comp[i] = NULL;
+		spin_unlock_irqrestore(&dev->tx_buf_lock, flags);
+		dev->use_tx_msgqs = MSM_MSGQ_ENABLED;
+	}
+
+	return 0;
+sps_transfer_failed:
+	memset(&sps_error_event, 0x00, sizeof(sps_error_event));
+	sps_register_event(endpoint->sps, &sps_error_event);
+sps_reg_event_failed:
+	sps_disconnect(endpoint->sps);
+	return ret;
+}
+
+static int msm_slim_init_rx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
+{
+	int ret;
+	u32 pipe_offset;
+	struct msm_slim_endp *endpoint = &dev->rx_msgq;
+	struct sps_connect *config = &endpoint->config;
+	struct sps_mem_buffer *descr = &config->desc;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+
+	if (dev->use_rx_msgqs == MSM_MSGQ_DISABLED)
+		return 0;
+
+	/* Allocate the endpoint */
+	ret = msm_slim_init_endpoint(dev, endpoint);
+	if (ret) {
+		dev_err(dev->dev, "init_endpoint failed 0x%x\n", ret);
+		goto sps_init_endpoint_failed;
+	}
+
+	/* Get the pipe indices for the message queues */
+	pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
+	dev_dbg(dev->dev, "Message queue pipe offset %d\n", pipe_offset);
+
+	config->mode = SPS_MODE_SRC;
+	config->source = dev->bam.hdl;
+	config->destination = SPS_DEV_HANDLE_MEM;
+	config->src_pipe_index = pipe_offset;
+	config->options = SPS_O_EOT | SPS_O_ERROR |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	/* Allocate memory for the FIFO descriptors */
+	ret = msm_slim_sps_mem_alloc(dev, descr,
+				MSM_SLIM_DESC_NUM * sizeof(struct sps_iovec));
+	if (ret) {
+		dev_err(dev->dev, "unable to allocate SPS descriptors\n");
+		goto alloc_descr_failed;
+	}
+
+	/* Allocate memory for the message buffer(s), N descrs, 4-byte mesg */
+	ret = msm_slim_sps_mem_alloc(dev, mem, MSM_SLIM_DESC_NUM * 4);
+	if (ret) {
+		dev_err(dev->dev, "dma_alloc_coherent failed\n");
+		goto alloc_buffer_failed;
+	}
+
+	ret = msm_slim_connect_endp(dev, endpoint);
+
+	if (!ret)
+		return 0;
+
+	msm_slim_sps_mem_free(dev, mem);
+alloc_buffer_failed:
+	msm_slim_sps_mem_free(dev, descr);
+alloc_descr_failed:
+	msm_slim_free_endpoint(endpoint);
+sps_init_endpoint_failed:
+	dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+	return ret;
+}
+
+static int msm_slim_init_tx_msgq(struct msm_slim_ctrl *dev, u32 pipe_reg)
+{
+	int ret;
+	u32 pipe_offset;
+	struct msm_slim_endp *endpoint = &dev->tx_msgq;
+	struct sps_connect *config = &endpoint->config;
+	struct sps_mem_buffer *descr = &config->desc;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+
+	if (dev->use_tx_msgqs == MSM_MSGQ_DISABLED)
+		return 0;
+
+	/* Allocate the endpoint */
+	ret = msm_slim_init_endpoint(dev, endpoint);
+	if (ret) {
+		dev_err(dev->dev, "init_endpoint failed 0x%x\n", ret);
+		goto sps_init_endpoint_failed;
+	}
+
+	/* Get the pipe indices for the message queues */
+	pipe_offset = (readl_relaxed(dev->base + pipe_reg) & 0xfc) >> 2;
+	pipe_offset += 1;
+	dev_dbg(dev->dev, "TX Message queue pipe offset %d\n", pipe_offset);
+
+	config->mode = SPS_MODE_DEST;
+	config->source = SPS_DEV_HANDLE_MEM;
+	config->destination = dev->bam.hdl;
+	config->dest_pipe_index = pipe_offset;
+	config->src_pipe_index = 0;
+	config->options = SPS_O_ERROR | SPS_O_NO_Q |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	/* Desc and TX buf are circular queues */
+	/* Allocate memory for the FIFO descriptors */
+	ret = msm_slim_sps_mem_alloc(dev, descr,
+				(MSM_TX_BUFS + 1) * sizeof(struct sps_iovec));
+	if (ret) {
+		dev_err(dev->dev, "unable to allocate SPS descriptors\n");
+		goto alloc_descr_failed;
+	}
+
+	/* Allocate TX buffer from which descriptors are created */
+	ret = msm_slim_sps_mem_alloc(dev, mem, ((MSM_TX_BUFS + 1) *
+					SLIM_MSGQ_BUF_LEN));
+	if (ret) {
+		dev_err(dev->dev, "dma_alloc_coherent failed\n");
+		goto alloc_buffer_failed;
+	}
+	ret = msm_slim_connect_endp(dev, endpoint);
+
+	if (!ret)
+		return 0;
+
+	msm_slim_sps_mem_free(dev, mem);
+alloc_buffer_failed:
+	msm_slim_sps_mem_free(dev, descr);
+alloc_descr_failed:
+	msm_slim_free_endpoint(endpoint);
+sps_init_endpoint_failed:
+	dev->use_tx_msgqs = MSM_MSGQ_DISABLED;
+	return ret;
+}
+
+static int msm_slim_data_port_assign(struct msm_slim_ctrl *dev)
+{
+	int i, data_ports = 0;
+	/* First 7 bits are for message Qs */
+	for (i = 7; i < 32; i++) {
+		/* Check what pipes are owned by Apps. */
+		if ((dev->pdata.apps_pipes >> i) & 0x1) {
+			if (dev->pipes)
+				dev->pipes[data_ports].port_b = i - 7;
+			data_ports++;
+		}
+	}
+	return data_ports;
+}
+/* Registers BAM h/w resource with SPS driver and initializes msgq endpoints */
+int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
+			u32 pipe_reg, bool remote)
+{
+	int ret;
+	unsigned long bam_handle;
+	struct sps_bam_props bam_props = {0};
+
+	static struct sps_bam_sec_config_props sec_props = {
+		.ees = {
+			[0] = {		/* LPASS */
+				.vmid = 0,
+				.pipe_mask = 0xFFFF98,
+			},
+			[1] = {		/* Krait Apps */
+				.vmid = 1,
+				.pipe_mask = 0x3F000007,
+			},
+			[2] = {		/* Modem */
+				.vmid = 2,
+				.pipe_mask = 0x00000060,
+			},
+		},
+	};
+
+	if (dev->bam.hdl) {
+		bam_handle = dev->bam.hdl;
+		goto init_pipes;
+	}
+	bam_props.ee = dev->ee;
+	bam_props.virt_addr = dev->bam.base;
+	bam_props.phys_addr = bam_mem->start;
+	bam_props.irq = dev->bam.irq;
+	if (!remote) {
+		bam_props.manage = SPS_BAM_MGR_LOCAL;
+		bam_props.sec_config = SPS_BAM_SEC_DO_CONFIG;
+	} else {
+		bam_props.manage = SPS_BAM_MGR_DEVICE_REMOTE |
+					SPS_BAM_MGR_MULTI_EE;
+		bam_props.sec_config = SPS_BAM_SEC_DO_NOT_CONFIG;
+	}
+	bam_props.summing_threshold = MSM_SLIM_PERF_SUMM_THRESHOLD;
+
+	bam_props.p_sec_config_props = &sec_props;
+
+	bam_props.options = SPS_O_DESC_DONE | SPS_O_ERROR |
+				SPS_O_ACK_TRANSFERS | SPS_O_AUTO_ENABLE;
+
+	/* override apps channel pipes if specified in platform-data or DT */
+	if (dev->pdata.apps_pipes)
+		sec_props.ees[dev->ee].pipe_mask = dev->pdata.apps_pipes;
+
+	/* Register the BAM device with the SPS driver */
+	ret = sps_register_bam_device(&bam_props, &bam_handle);
+	if (ret) {
+		dev_err(dev->dev, "disabling BAM: reg-bam failed 0x%x\n", ret);
+		dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+		dev->use_tx_msgqs = MSM_MSGQ_DISABLED;
+		return ret;
+	}
+	dev->bam.hdl = bam_handle;
+	dev_dbg(dev->dev, "SLIM BAM registered, handle = 0x%lx\n", bam_handle);
+
+init_pipes:
+	if (dev->port_nums)
+		goto init_msgq;
+
+	/* get the # of ports first */
+	dev->port_nums = msm_slim_data_port_assign(dev);
+	if (dev->port_nums && !dev->pipes) {
+		dev->pipes = kzalloc(sizeof(struct msm_slim_endp) *
+					dev->port_nums,
+					GFP_KERNEL);
+		if (IS_ERR_OR_NULL(dev->pipes)) {
+			dev_err(dev->dev, "no memory for data ports");
+			sps_deregister_bam_device(bam_handle);
+			return PTR_ERR(dev->pipes);
+		}
+		/* assign the ports now */
+		msm_slim_data_port_assign(dev);
+	}
+
+init_msgq:
+	ret = msm_slim_init_rx_msgq(dev, pipe_reg);
+	if (ret)
+		dev_err(dev->dev, "msm_slim_init_rx_msgq failed 0x%x\n", ret);
+	if (ret && bam_handle)
+		dev->use_rx_msgqs = MSM_MSGQ_DISABLED;
+
+	ret = msm_slim_init_tx_msgq(dev, pipe_reg);
+	if (ret)
+		dev_err(dev->dev, "msm_slim_init_tx_msgq failed 0x%x\n", ret);
+	if (ret && bam_handle)
+		dev->use_tx_msgqs = MSM_MSGQ_DISABLED;
+
+	/*
+	 * If command interface for BAM fails, register interface is used for
+	 * commands.
+	 * It is possible that other BAM usecases (e.g. apps channels) will
+	 * still need BAM. Since BAM is successfully initialized, we can
+	 * continue using it for non-command use cases.
+	 */
+
+	return 0;
+}
+
+void msm_slim_disconnect_endp(struct msm_slim_ctrl *dev,
+					struct msm_slim_endp *endpoint,
+					enum msm_slim_msgq *msgq_flag)
+{
+	if (*msgq_flag >= MSM_MSGQ_ENABLED) {
+		sps_disconnect(endpoint->sps);
+		*msgq_flag = MSM_MSGQ_RESET;
+	}
+}
+
+static int msm_slim_discard_rx_data(struct msm_slim_ctrl *dev,
+					struct msm_slim_endp *endpoint)
+{
+	struct sps_iovec sio;
+	int desc_num = 0, ret = 0;
+
+	ret = sps_get_unused_desc_num(endpoint->sps, &desc_num);
+	if (ret) {
+		dev_err(dev->dev, "sps_get_iovec() failed 0x%x\n", ret);
+		return ret;
+	}
+	while (desc_num--)
+		sps_get_iovec(endpoint->sps, &sio);
+	return ret;
+}
+
+static void msm_slim_remove_ep(struct msm_slim_ctrl *dev,
+					struct msm_slim_endp *endpoint,
+					enum msm_slim_msgq *msgq_flag)
+{
+	struct sps_connect *config = &endpoint->config;
+	struct sps_mem_buffer *descr = &config->desc;
+	struct sps_mem_buffer *mem = &endpoint->buf;
+
+	msm_slim_sps_mem_free(dev, mem);
+	msm_slim_sps_mem_free(dev, descr);
+	msm_slim_free_endpoint(endpoint);
+}
+
+void msm_slim_deinit_ep(struct msm_slim_ctrl *dev,
+				struct msm_slim_endp *endpoint,
+				enum msm_slim_msgq *msgq_flag)
+{
+	int ret = 0;
+	struct sps_connect *config = &endpoint->config;
+
+	if (*msgq_flag == MSM_MSGQ_ENABLED) {
+		if (config->mode == SPS_MODE_SRC) {
+			ret = msm_slim_discard_rx_data(dev, endpoint);
+			if (ret)
+				SLIM_WARN(dev, "discarding Rx data failed\n");
+		}
+		msm_slim_disconnect_endp(dev, endpoint, msgq_flag);
+		msm_slim_remove_ep(dev, endpoint, msgq_flag);
+	}
+}
+
+static void msm_slim_sps_unreg_event(struct sps_pipe *sps)
+{
+	struct sps_register_event sps_event;
+
+	memset(&sps_event, 0x00, sizeof(sps_event));
+	/* Disable interrupt and signal notification for Rx/Tx pipe */
+	sps_register_event(sps, &sps_event);
+}
+
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg)
+{
+	int i;
+
+	if (dev->use_rx_msgqs >= MSM_MSGQ_ENABLED)
+		msm_slim_sps_unreg_event(dev->rx_msgq.sps);
+	if (dev->use_tx_msgqs >= MSM_MSGQ_ENABLED)
+		msm_slim_sps_unreg_event(dev->tx_msgq.sps);
+
+	for (i = 0; i < dev->port_nums; i++) {
+		if (dev->pipes[i].connected)
+			msm_slim_disconn_pipe_port(dev, i);
+	}
+	if (dereg) {
+		for (i = 0; i < dev->port_nums; i++) {
+			if (dev->pipes[i].connected)
+				msm_dealloc_port(&dev->ctrl, i);
+		}
+		sps_deregister_bam_device(dev->bam.hdl);
+		dev->bam.hdl = 0L;
+		kfree(dev->pipes);
+		dev->pipes = NULL;
+	}
+	dev->port_nums = 0;
+}
+
+/* Slimbus QMI Messaging */
+#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01 0x0020
+#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01 0x0020
+#define SLIMBUS_QMI_POWER_REQ_V01 0x0021
+#define SLIMBUS_QMI_POWER_RESP_V01 0x0021
+#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ 0x0022
+#define SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP 0x0022
+
+#define SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN 14
+#define SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN 7
+#define SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN 7
+
+enum slimbus_mode_enum_type_v01 {
+	/* To force a 32 bit signed enum. Do not change or use*/
+	SLIMBUS_MODE_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+	SLIMBUS_MODE_SATELLITE_V01 = 1,
+	SLIMBUS_MODE_MASTER_V01 = 2,
+	SLIMBUS_MODE_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+enum slimbus_pm_enum_type_v01 {
+	/* To force a 32 bit signed enum. Do not change or use*/
+	SLIMBUS_PM_ENUM_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
+	SLIMBUS_PM_INACTIVE_V01 = 1,
+	SLIMBUS_PM_ACTIVE_V01 = 2,
+	SLIMBUS_PM_ENUM_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
+};
+
+struct slimbus_select_inst_req_msg_v01 {
+	/* Mandatory */
+	/* Hardware Instance Selection */
+	uint32_t instance;
+
+	/* Optional */
+	/* Optional Mode Request Operation */
+	/* Must be set to true if mode is being passed */
+	uint8_t mode_valid;
+	enum slimbus_mode_enum_type_v01 mode;
+};
+
+struct slimbus_select_inst_resp_msg_v01 {
+	/* Mandatory */
+	/* Result Code */
+	struct qmi_response_type_v01 resp;
+};
+
+struct slimbus_power_req_msg_v01 {
+	/* Mandatory */
+	/* Power Request Operation */
+	enum slimbus_pm_enum_type_v01 pm_req;
+};
+
+struct slimbus_power_resp_msg_v01 {
+	/* Mandatory */
+	/* Result Code */
+	struct qmi_response_type_v01 resp;
+};
+
+struct slimbus_chkfrm_resp_msg {
+	/* Mandatory */
+	/* Result Code */
+	struct qmi_response_type_v01 resp;
+};
+
+
+static struct elem_info slimbus_select_inst_req_msg_v01_ei[] = {
+	{
+		.data_type = QMI_UNSIGNED_4_BYTE,
+		.elem_len  = 1,
+		.elem_size = sizeof(uint32_t),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x01,
+		.offset    = offsetof(struct slimbus_select_inst_req_msg_v01,
+				      instance),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_OPT_FLAG,
+		.elem_len  = 1,
+		.elem_size = sizeof(uint8_t),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x10,
+		.offset    = offsetof(struct slimbus_select_inst_req_msg_v01,
+				      mode_valid),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_UNSIGNED_4_BYTE,
+		.elem_len  = 1,
+		.elem_size = sizeof(enum slimbus_mode_enum_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x10,
+		.offset    = offsetof(struct slimbus_select_inst_req_msg_v01,
+				      mode),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_select_inst_resp_msg_v01_ei[] = {
+	{
+		.data_type = QMI_STRUCT,
+		.elem_len  = 1,
+		.elem_size = sizeof(struct qmi_response_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x02,
+		.offset    = offsetof(struct slimbus_select_inst_resp_msg_v01,
+				      resp),
+		.ei_array  = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_power_req_msg_v01_ei[] = {
+	{
+		.data_type = QMI_UNSIGNED_4_BYTE,
+		.elem_len  = 1,
+		.elem_size = sizeof(enum slimbus_pm_enum_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x01,
+		.offset    = offsetof(struct slimbus_power_req_msg_v01, pm_req),
+		.ei_array  = NULL,
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_power_resp_msg_v01_ei[] = {
+	{
+		.data_type = QMI_STRUCT,
+		.elem_len  = 1,
+		.elem_size = sizeof(struct qmi_response_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x02,
+		.offset    = offsetof(struct slimbus_power_resp_msg_v01, resp),
+		.ei_array  = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static struct elem_info slimbus_chkfrm_resp_msg_v01_ei[] = {
+	{
+		.data_type = QMI_STRUCT,
+		.elem_len  = 1,
+		.elem_size = sizeof(struct qmi_response_type_v01),
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x02,
+		.offset    = offsetof(struct slimbus_chkfrm_resp_msg, resp),
+		.ei_array  = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type = QMI_EOTI,
+		.elem_len  = 0,
+		.elem_size = 0,
+		.is_array  = NO_ARRAY,
+		.tlv_type  = 0x00,
+		.offset    = 0,
+		.ei_array  = NULL,
+	},
+};
+
+static void msm_slim_qmi_recv_msg(struct kthread_work *work)
+{
+	int rc;
+	struct msm_slim_qmi *qmi =
+			container_of(work, struct msm_slim_qmi, kwork);
+
+	/* Drain all packets received */
+	do {
+		rc = qmi_recv_msg(qmi->handle);
+	} while (rc == 0);
+	if (rc != -ENOMSG)
+		pr_err("%s: Error receiving QMI message:%d\n", __func__, rc);
+}
+
+static void msm_slim_qmi_notify(struct qmi_handle *handle,
+				enum qmi_event_type event, void *notify_priv)
+{
+	struct msm_slim_ctrl *dev = notify_priv;
+	struct msm_slim_qmi *qmi = &dev->qmi;
+
+	switch (event) {
+	case QMI_RECV_MSG:
+		kthread_queue_work(&qmi->kworker, &qmi->kwork);
+		break;
+	default:
+		break;
+	}
+}
+
+static const char *get_qmi_error(struct qmi_response_type_v01 *r)
+{
+	if (r->result == QMI_RESULT_SUCCESS_V01 || r->error == QMI_ERR_NONE_V01)
+		return "No Error";
+	else if (r->error == QMI_ERR_NO_MEMORY_V01)
+		return "Out of Memory";
+	else if (r->error == QMI_ERR_INTERNAL_V01)
+		return "Unexpected error occurred";
+	else if (r->error == QMI_ERR_INCOMPATIBLE_STATE_V01)
+		return "Slimbus s/w already configured to a different mode";
+	else if (r->error == QMI_ERR_INVALID_ID_V01)
+		return "Slimbus hardware instance is not valid";
+	else
+		return "Unknown error";
+}
+
+static int msm_slim_qmi_send_select_inst_req(struct msm_slim_ctrl *dev,
+				struct slimbus_select_inst_req_msg_v01 *req)
+{
+	struct slimbus_select_inst_resp_msg_v01 resp = { { 0, 0 } };
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_REQ_V01;
+	req_desc.max_msg_len = SLIMBUS_QMI_SELECT_INSTANCE_REQ_MAX_MSG_LEN;
+	req_desc.ei_array = slimbus_select_inst_req_msg_v01_ei;
+
+	resp_desc.msg_id = SLIMBUS_QMI_SELECT_INSTANCE_RESP_V01;
+	resp_desc.max_msg_len = SLIMBUS_QMI_SELECT_INSTANCE_RESP_MAX_MSG_LEN;
+	resp_desc.ei_array = slimbus_select_inst_resp_msg_v01_ei;
+
+	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
+			&resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
+	if (rc < 0) {
+		SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
+		return rc;
+	}
+
+	/* Check the response */
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n", __func__,
+				resp.resp.result, get_qmi_error(&resp.resp));
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+static int msm_slim_qmi_send_power_request(struct msm_slim_ctrl *dev,
+				struct slimbus_power_req_msg_v01 *req)
+{
+	struct slimbus_power_resp_msg_v01 resp = { { 0, 0 } };
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.msg_id = SLIMBUS_QMI_POWER_REQ_V01;
+	req_desc.max_msg_len = SLIMBUS_QMI_POWER_REQ_MAX_MSG_LEN;
+	req_desc.ei_array = slimbus_power_req_msg_v01_ei;
+
+	resp_desc.msg_id = SLIMBUS_QMI_POWER_RESP_V01;
+	resp_desc.max_msg_len = SLIMBUS_QMI_POWER_RESP_MAX_MSG_LEN;
+	resp_desc.ei_array = slimbus_power_resp_msg_v01_ei;
+
+	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, req, sizeof(*req),
+			&resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
+	if (rc < 0) {
+		SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
+		return rc;
+	}
+
+	/* Check the response */
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n", __func__,
+				resp.resp.result, get_qmi_error(&resp.resp));
+		return -EREMOTEIO;
+	}
+
+	return 0;
+}
+
+int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master)
+{
+	int rc = 0;
+	struct qmi_handle *handle;
+	struct slimbus_select_inst_req_msg_v01 req;
+
+	kthread_init_worker(&dev->qmi.kworker);
+
+	dev->qmi.task = kthread_run(kthread_worker_fn,
+			&dev->qmi.kworker, "msm_slim_qmi_clnt%d", dev->ctrl.nr);
+
+	if (IS_ERR(dev->qmi.task)) {
+		pr_err("%s: Failed to create QMI client kthread\n", __func__);
+		return -ENOMEM;
+	}
+
+	kthread_init_work(&dev->qmi.kwork, msm_slim_qmi_recv_msg);
+
+	handle = qmi_handle_create(msm_slim_qmi_notify, dev);
+	if (!handle) {
+		rc = -ENOMEM;
+		pr_err("%s: QMI client handle alloc failed\n", __func__);
+		goto qmi_handle_create_failed;
+	}
+
+	rc = qmi_connect_to_service(handle, SLIMBUS_QMI_SVC_ID,
+						SLIMBUS_QMI_SVC_V1,
+						SLIMBUS_QMI_INS_ID);
+	if (rc < 0) {
+		SLIM_ERR(dev, "%s: QMI server not found\n", __func__);
+		goto qmi_connect_to_service_failed;
+	}
+
+	/* Instance is 0 based */
+	req.instance = (dev->ctrl.nr >> 1);
+	req.mode_valid = 1;
+
+	/* Mode indicates the role of the ADSP */
+	if (apps_is_master)
+		req.mode = SLIMBUS_MODE_SATELLITE_V01;
+	else
+		req.mode = SLIMBUS_MODE_MASTER_V01;
+
+	dev->qmi.handle = handle;
+
+	rc = msm_slim_qmi_send_select_inst_req(dev, &req);
+	if (rc) {
+		pr_err("%s: failed to select h/w instance\n", __func__);
+		goto qmi_select_instance_failed;
+	}
+
+	return 0;
+
+qmi_select_instance_failed:
+	dev->qmi.handle = NULL;
+qmi_connect_to_service_failed:
+	qmi_handle_destroy(handle);
+qmi_handle_create_failed:
+	kthread_flush_worker(&dev->qmi.kworker);
+	kthread_stop(dev->qmi.task);
+	dev->qmi.task = NULL;
+	return rc;
+}
+
+void msm_slim_qmi_exit(struct msm_slim_ctrl *dev)
+{
+	if (!dev->qmi.handle || !dev->qmi.task)
+		return;
+	qmi_handle_destroy(dev->qmi.handle);
+	kthread_flush_worker(&dev->qmi.kworker);
+	kthread_stop(dev->qmi.task);
+	dev->qmi.task = NULL;
+	dev->qmi.handle = NULL;
+}
+
+int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active)
+{
+	struct slimbus_power_req_msg_v01 req;
+
+	if (active)
+		req.pm_req = SLIMBUS_PM_ACTIVE_V01;
+	else
+		req.pm_req = SLIMBUS_PM_INACTIVE_V01;
+
+	return msm_slim_qmi_send_power_request(dev, &req);
+}
+
+int msm_slim_qmi_check_framer_request(struct msm_slim_ctrl *dev)
+{
+	struct slimbus_chkfrm_resp_msg resp = { { 0, 0 } };
+	struct msg_desc req_desc, resp_desc;
+	int rc;
+
+	req_desc.msg_id = SLIMBUS_QMI_CHECK_FRAMER_STATUS_REQ;
+	req_desc.max_msg_len = 0;
+	req_desc.ei_array = NULL;
+
+	resp_desc.msg_id = SLIMBUS_QMI_CHECK_FRAMER_STATUS_RESP;
+	resp_desc.max_msg_len = SLIMBUS_QMI_CHECK_FRAMER_STAT_RESP_MAX_MSG_LEN;
+	resp_desc.ei_array = slimbus_chkfrm_resp_msg_v01_ei;
+
+	rc = qmi_send_req_wait(dev->qmi.handle, &req_desc, NULL, 0,
+		&resp_desc, &resp, sizeof(resp), SLIM_QMI_RESP_TOUT);
+	if (rc < 0) {
+		SLIM_ERR(dev, "%s: QMI send req failed %d\n", __func__, rc);
+		return rc;
+	}
+	/* Check the response */
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		SLIM_ERR(dev, "%s: QMI request failed 0x%x (%s)\n",
+			__func__, resp.resp.result, get_qmi_error(&resp.resp));
+		return -EREMOTEIO;
+	}
+	return 0;
+}
diff --git a/drivers/slimbus/slim-msm.h b/drivers/slimbus/slim-msm.h
new file mode 100644
index 0000000..65b9fae
--- /dev/null
+++ b/drivers/slimbus/slim-msm.h
@@ -0,0 +1,440 @@
+/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SLIM_MSM_H
+#define _SLIM_MSM_H
+
+#include <linux/irq.h>
+#include <linux/kthread.h>
+#include <soc/qcom/msm_qmi_interface.h>
+#include <linux/ipc_logging.h>
+
+/* Per spec.max 40 bytes per received message */
+#define SLIM_MSGQ_BUF_LEN	40
+
+#define MSM_TX_BUFS		32
+
+#define SLIM_USR_MC_GENERIC_ACK		0x25
+#define SLIM_USR_MC_MASTER_CAPABILITY	0x0
+#define SLIM_USR_MC_REPORT_SATELLITE	0x1
+#define SLIM_USR_MC_ADDR_QUERY		0xD
+#define SLIM_USR_MC_ADDR_REPLY		0xE
+#define SLIM_USR_MC_DEFINE_CHAN		0x20
+#define SLIM_USR_MC_DEF_ACT_CHAN	0x21
+#define SLIM_USR_MC_CHAN_CTRL		0x23
+#define SLIM_USR_MC_RECONFIG_NOW	0x24
+#define SLIM_USR_MC_REQ_BW		0x28
+#define SLIM_USR_MC_CONNECT_SRC		0x2C
+#define SLIM_USR_MC_CONNECT_SINK	0x2D
+#define SLIM_USR_MC_DISCONNECT_PORT	0x2E
+
+#define SLIM_USR_MC_REPEAT_CHANGE_VALUE	0x0
+#define MSM_SLIM_VE_MAX_MAP_ADDR	0xFFF
+#define SLIM_MAX_VE_SLC_BYTES		16
+
+#define MSM_SLIM_AUTOSUSPEND		MSEC_PER_SEC
+
+#define SLIM_RX_MSGQ_TIMEOUT_VAL	0x10000
+/*
+ * Messages that can be received simultaneously:
+ * Client reads, LPASS master responses, announcement messages
+ * Receive upto 10 messages simultaneously.
+ */
+#define MSM_SLIM_DESC_NUM		32
+
+/* MSM Slimbus peripheral settings */
+#define MSM_SLIM_PERF_SUMM_THRESHOLD	0x8000
+#define MSM_SLIM_NPORTS			24
+#define MSM_SLIM_NCHANS			32
+
+#define QC_MFGID_LSB	0x2
+#define QC_MFGID_MSB	0x17
+#define QC_CHIPID_SL	0x10
+#define QC_DEVID_SAT1	0x3
+#define QC_DEVID_SAT2	0x4
+#define QC_DEVID_PGD	0x5
+
+#define SLIM_MSG_ASM_FIRST_WORD(l, mt, mc, dt, ad) \
+		((l) | ((mt) << 5) | ((mc) << 8) | ((dt) << 15) | ((ad) << 16))
+
+#define INIT_MX_RETRIES 3
+#define DEF_RETRY_MS	10
+#define MSM_CONCUR_MSG	8
+#define SAT_CONCUR_MSG	8
+
+#define DEF_WATERMARK	(8 << 1)
+#define DEF_ALIGN_LSB	0
+#define DEF_ALIGN_MSB	(1 << 7)
+#define DEF_PACK	(1 << 6)
+#define DEF_NO_PACK	0
+#define ENABLE_PORT	1
+
+#define DEF_BLKSZ	0
+#define DEF_TRANSZ	0
+
+#define SAT_MAGIC_LSB	0xD9
+#define SAT_MAGIC_MSB	0xC5
+#define SAT_MSG_VER	0x1
+#define SAT_MSG_PROT	0x1
+#define MSM_SAT_SUCCSS	0x20
+#define MSM_MAX_NSATS	2
+#define MSM_MAX_SATCH	32
+
+/* Slimbus QMI service */
+#define SLIMBUS_QMI_SVC_ID 0x0301
+#define SLIMBUS_QMI_SVC_V1 1
+#define SLIMBUS_QMI_INS_ID 0
+
+/* QMI response timeout of 500ms */
+#define SLIM_QMI_RESP_TOUT 1000
+
+#define PGD_THIS_EE(r, v) ((v) ? PGD_THIS_EE_V2(r) : PGD_THIS_EE_V1(r))
+#define PGD_PORT(r, p, v) ((v) ? PGD_PORT_V2(r, p) : PGD_PORT_V1(r, p))
+#define CFG_PORT(r, v) ((v) ? CFG_PORT_V2(r) : CFG_PORT_V1(r))
+
+#define PGD_THIS_EE_V2(r) (dev->base + (r ## _V2) + (dev->ee * 0x1000))
+#define PGD_PORT_V2(r, p) (dev->base + (r ## _V2) + ((p) * 0x1000))
+#define CFG_PORT_V2(r) ((r ## _V2))
+/* Component registers */
+enum comp_reg_v2 {
+	COMP_CFG_V2		= 4,
+	COMP_TRUST_CFG_V2	= 0x3000,
+};
+
+/* Manager PGD registers */
+enum pgd_reg_v2 {
+	PGD_CFG_V2		= 0x800,
+	PGD_STAT_V2		= 0x804,
+	PGD_INT_EN_V2		= 0x810,
+	PGD_INT_STAT_V2		= 0x814,
+	PGD_INT_CLR_V2		= 0x818,
+	PGD_OWN_EEn_V2		= 0x300C,
+	PGD_PORT_INT_EN_EEn_V2	= 0x5000,
+	PGD_PORT_INT_ST_EEn_V2	= 0x5004,
+	PGD_PORT_INT_CL_EEn_V2	= 0x5008,
+	PGD_PORT_CFGn_V2	= 0x14000,
+	PGD_PORT_STATn_V2	= 0x14004,
+	PGD_PORT_PARAMn_V2	= 0x14008,
+	PGD_PORT_BLKn_V2	= 0x1400C,
+	PGD_PORT_TRANn_V2	= 0x14010,
+	PGD_PORT_MCHANn_V2	= 0x14014,
+	PGD_PORT_PSHPLLn_V2	= 0x14018,
+	PGD_PORT_PC_CFGn_V2	= 0x8000,
+	PGD_PORT_PC_VALn_V2	= 0x8004,
+	PGD_PORT_PC_VFR_TSn_V2	= 0x8008,
+	PGD_PORT_PC_VFR_STn_V2	= 0x800C,
+	PGD_PORT_PC_VFR_CLn_V2	= 0x8010,
+	PGD_IE_STAT_V2		= 0x820,
+	PGD_VE_STAT_V2		= 0x830,
+};
+
+#define PGD_THIS_EE_V1(r) (dev->base + (r ## _V1) + (dev->ee * 16))
+#define PGD_PORT_V1(r, p) (dev->base + (r ## _V1) + ((p) * 32))
+#define CFG_PORT_V1(r) ((r ## _V1))
+/* Component registers */
+enum comp_reg_v1 {
+	COMP_CFG_V1		= 0,
+	COMP_TRUST_CFG_V1	= 0x14,
+};
+
+/* Manager PGD registers */
+enum pgd_reg_v1 {
+	PGD_CFG_V1		= 0x1000,
+	PGD_STAT_V1		= 0x1004,
+	PGD_INT_EN_V1		= 0x1010,
+	PGD_INT_STAT_V1		= 0x1014,
+	PGD_INT_CLR_V1		= 0x1018,
+	PGD_OWN_EEn_V1		= 0x1020,
+	PGD_PORT_INT_EN_EEn_V1	= 0x1030,
+	PGD_PORT_INT_ST_EEn_V1	= 0x1034,
+	PGD_PORT_INT_CL_EEn_V1	= 0x1038,
+	PGD_PORT_CFGn_V1	= 0x1080,
+	PGD_PORT_STATn_V1	= 0x1084,
+	PGD_PORT_PARAMn_V1	= 0x1088,
+	PGD_PORT_BLKn_V1	= 0x108C,
+	PGD_PORT_TRANn_V1	= 0x1090,
+	PGD_PORT_MCHANn_V1	= 0x1094,
+	PGD_PORT_PSHPLLn_V1	= 0x1098,
+	PGD_PORT_PC_CFGn_V1	= 0x1600,
+	PGD_PORT_PC_VALn_V1	= 0x1604,
+	PGD_PORT_PC_VFR_TSn_V1	= 0x1608,
+	PGD_PORT_PC_VFR_STn_V1	= 0x160C,
+	PGD_PORT_PC_VFR_CLn_V1	= 0x1610,
+	PGD_IE_STAT_V1		= 0x1700,
+	PGD_VE_STAT_V1		= 0x1710,
+};
+
+enum msm_slim_port_status {
+	MSM_PORT_OVERFLOW	= 1 << 2,
+	MSM_PORT_UNDERFLOW	= 1 << 3,
+	MSM_PORT_DISCONNECT	= 1 << 19,
+};
+
+enum msm_ctrl_state {
+	MSM_CTRL_AWAKE,
+	MSM_CTRL_IDLE,
+	MSM_CTRL_ASLEEP,
+	MSM_CTRL_DOWN,
+};
+
+enum msm_slim_msgq {
+	MSM_MSGQ_DISABLED,
+	MSM_MSGQ_RESET,
+	MSM_MSGQ_ENABLED,
+	MSM_MSGQ_DOWN,
+};
+
+struct msm_slim_sps_bam {
+	unsigned long		hdl;
+	void __iomem		*base;
+	int			irq;
+};
+
+/*
+ * struct slim_pshpull_parm: Structure to store push pull protocol parameters
+ * @num_samples: Number of samples in a period
+ * @rpt_period: Repeat period value
+ */
+struct msm_slim_pshpull_parm {
+	int		num_samples;
+	int		rpt_period;
+};
+
+struct msm_slim_endp {
+	struct sps_pipe			*sps;
+	struct sps_connect		config;
+	struct sps_register_event	event;
+	struct sps_mem_buffer		buf;
+	bool				connected;
+	int				port_b;
+	struct msm_slim_pshpull_parm	psh_pull;
+};
+
+struct msm_slim_qmi {
+	struct qmi_handle		*handle;
+	struct task_struct		*task;
+	struct task_struct		*slave_thread;
+	struct completion		slave_notify;
+	struct kthread_work		kwork;
+	struct kthread_worker		kworker;
+	struct completion		qmi_comp;
+	struct notifier_block		nb;
+};
+
+enum msm_slim_dom {
+	MSM_SLIM_DOM_NONE,
+	MSM_SLIM_DOM_PD,
+	MSM_SLIM_DOM_SS,
+};
+
+struct msm_slim_ss {
+	struct notifier_block nb;
+	void *domr;
+	enum msm_ctrl_state state;
+	struct work_struct dom_up;
+	enum msm_slim_dom dom_t;
+};
+
+struct msm_slim_pdata {
+	u32 apps_pipes;
+	u32 eapc;
+};
+
+struct msm_slim_bulk_wr {
+	dma_addr_t	wr_dma;
+	void		*base;
+	int		size;
+	int		buf_sz;
+	int		(*cb)(void *ctx, int err);
+	void		*ctx;
+	bool		in_progress;
+};
+
+struct msm_slim_ctrl {
+	struct slim_controller  ctrl;
+	struct slim_framer	framer;
+	struct device		*dev;
+	void __iomem		*base;
+	struct resource		*slew_mem;
+	struct resource		*bam_mem;
+	u32			curr_bw;
+	u8			msg_cnt;
+	u32			tx_buf[10];
+	u8			rx_msgs[MSM_CONCUR_MSG][SLIM_MSGQ_BUF_LEN];
+	int			tx_tail;
+	int			tx_head;
+	spinlock_t		rx_lock;
+	int			head;
+	int			tail;
+	int			irq;
+	int			err;
+	int			ee;
+	struct completion	**wr_comp;
+	struct msm_slim_sat	*satd[MSM_MAX_NSATS];
+	struct msm_slim_endp	*pipes;
+	struct msm_slim_sps_bam	bam;
+	struct msm_slim_endp	tx_msgq;
+	struct msm_slim_endp	rx_msgq;
+	struct completion	rx_msgq_notify;
+	struct task_struct	*rx_msgq_thread;
+	struct clk		*rclk;
+	struct clk		*hclk;
+	struct mutex		tx_lock;
+	struct mutex		ssr_lock;
+	spinlock_t		tx_buf_lock;
+	u8			pgdla;
+	enum msm_slim_msgq	use_rx_msgqs;
+	enum msm_slim_msgq	use_tx_msgqs;
+	int			port_nums;
+	struct completion	reconf;
+	bool			reconf_busy;
+	bool			chan_active;
+	enum msm_ctrl_state	state;
+	struct completion	ctrl_up;
+	int			nsats;
+	u32			ver;
+	struct msm_slim_qmi	qmi;
+	struct msm_slim_pdata	pdata;
+	struct msm_slim_ss	ext_mdm;
+	struct msm_slim_ss	dsp;
+	struct msm_slim_bulk_wr	bulk;
+	int			default_ipc_log_mask;
+	int			ipc_log_mask;
+	bool			sysfs_created;
+	void			*ipc_slimbus_log;
+	void (*rx_slim)(struct msm_slim_ctrl *dev, u8 *buf);
+	u32			current_rx_buf[10];
+	int			current_count;
+	atomic_t		ssr_in_progress;
+};
+
+struct msm_sat_chan {
+	u8 chan;
+	u16 chanh;
+	int req_rem;
+	int req_def;
+	bool reconf;
+};
+
+struct msm_slim_sat {
+	struct slim_device	satcl;
+	struct msm_slim_ctrl	*dev;
+	struct workqueue_struct *wq;
+	struct work_struct	wd;
+	u8			sat_msgs[SAT_CONCUR_MSG][40];
+	struct msm_sat_chan	*satch;
+	u8			nsatch;
+	bool			sent_capability;
+	bool			pending_reconf;
+	bool			pending_capability;
+	int			shead;
+	int			stail;
+	spinlock_t lock;
+};
+
+enum rsc_grp {
+	EE_MGR_RSC_GRP	= 1 << 10,
+	EE_NGD_2	= 2 << 6,
+	EE_NGD_1	= 0,
+};
+
+
+/* IPC logging stuff */
+#define IPC_SLIMBUS_LOG_PAGES 5
+
+/* Log levels */
+enum {
+	FATAL_LEV = 0U,
+	ERR_LEV = 1U,
+	WARN_LEV = 2U,
+	INFO_LEV = 3U,
+	DBG_LEV = 4U,
+};
+
+/* Default IPC log level INFO */
+#define SLIM_DBG(dev, x...) do { \
+	pr_debug(x); \
+	if (dev->ipc_slimbus_log && dev->ipc_log_mask >= DBG_LEV) { \
+		ipc_log_string(dev->ipc_slimbus_log, x); \
+	} \
+} while (0)
+
+#define SLIM_INFO(dev, x...) do { \
+	pr_debug(x); \
+	if (dev->ipc_slimbus_log && dev->ipc_log_mask >= INFO_LEV) {\
+		ipc_log_string(dev->ipc_slimbus_log, x); \
+	} \
+} while (0)
+
+/* warnings and errors show up on console always */
+#define SLIM_WARN(dev, x...) do { \
+	pr_warn(x); \
+	if (dev->ipc_slimbus_log && dev->ipc_log_mask >= WARN_LEV) \
+		ipc_log_string(dev->ipc_slimbus_log, x); \
+} while (0)
+
+/* ERROR condition in the driver sets the hs_serial_debug_mask
+ * to ERR_FATAL level, so that this message can be seen
+ * in IPC logging. Further errors continue to log on the console
+ */
+#define SLIM_ERR(dev, x...) do { \
+	pr_err(x); \
+	if (dev->ipc_slimbus_log && dev->ipc_log_mask >= ERR_LEV) { \
+		ipc_log_string(dev->ipc_slimbus_log, x); \
+		dev->default_ipc_log_mask = dev->ipc_log_mask; \
+		dev->ipc_log_mask = FATAL_LEV; \
+	} \
+} while (0)
+
+#define SLIM_RST_LOGLVL(dev) { \
+	dev->ipc_log_mask = dev->default_ipc_log_mask; \
+}
+
+int msm_slim_rx_enqueue(struct msm_slim_ctrl *dev, u32 *buf, u8 len);
+int msm_slim_rx_dequeue(struct msm_slim_ctrl *dev, u8 *buf);
+int msm_slim_get_ctrl(struct msm_slim_ctrl *dev);
+void msm_slim_put_ctrl(struct msm_slim_ctrl *dev);
+irqreturn_t msm_slim_port_irq_handler(struct msm_slim_ctrl *dev, u32 pstat);
+int msm_slim_init_endpoint(struct msm_slim_ctrl *dev, struct msm_slim_endp *ep);
+void msm_slim_free_endpoint(struct msm_slim_endp *ep);
+void msm_hw_set_port(struct msm_slim_ctrl *dev, u8 pipenum, u8 portnum);
+int msm_alloc_port(struct slim_controller *ctrl, u8 pn);
+void msm_dealloc_port(struct slim_controller *ctrl, u8 pn);
+int msm_slim_connect_pipe_port(struct msm_slim_ctrl *dev, u8 pn);
+enum slim_port_err msm_slim_port_xfer_status(struct slim_controller *ctr,
+				u8 pn, phys_addr_t *done_buf, u32 *done_len);
+int msm_slim_port_xfer(struct slim_controller *ctrl, u8 pn, phys_addr_t iobuf,
+			u32 len, struct completion *comp);
+int msm_send_msg_buf(struct msm_slim_ctrl *dev, u32 *buf, u8 len, u32 tx_reg);
+u32 *msm_get_msg_buf(struct msm_slim_ctrl *dev, int len,
+			struct completion *comp);
+u32 *msm_slim_manage_tx_msgq(struct msm_slim_ctrl *dev, bool getbuf,
+			struct completion *comp, int err);
+int msm_slim_rx_msgq_get(struct msm_slim_ctrl *dev, u32 *data, int offset);
+int msm_slim_sps_init(struct msm_slim_ctrl *dev, struct resource *bam_mem,
+			u32 pipe_reg, bool remote);
+void msm_slim_sps_exit(struct msm_slim_ctrl *dev, bool dereg);
+
+int msm_slim_connect_endp(struct msm_slim_ctrl *dev,
+				struct msm_slim_endp *endpoint);
+void msm_slim_disconnect_endp(struct msm_slim_ctrl *dev,
+					struct msm_slim_endp *endpoint,
+					enum msm_slim_msgq *msgq_flag);
+void msm_slim_deinit_ep(struct msm_slim_ctrl *dev,
+				struct msm_slim_endp *endpoint,
+				enum msm_slim_msgq *msgq_flag);
+
+void msm_slim_qmi_exit(struct msm_slim_ctrl *dev);
+int msm_slim_qmi_init(struct msm_slim_ctrl *dev, bool apps_is_master);
+int msm_slim_qmi_power_request(struct msm_slim_ctrl *dev, bool active);
+int msm_slim_qmi_check_framer_request(struct msm_slim_ctrl *dev);
+#endif
diff --git a/drivers/slimbus/slimbus.c b/drivers/slimbus/slimbus.c
new file mode 100644
index 0000000..aa7ff12
--- /dev/null
+++ b/drivers/slimbus/slimbus.c
@@ -0,0 +1,3439 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+#include <linux/slimbus/slimbus.h>
+
+#define SLIM_PORT_HDL(la, f, p) ((la)<<24 | (f) << 16 | (p))
+
+#define SLIM_HDL_TO_LA(hdl)	((u32)((hdl) & 0xFF000000) >> 24)
+#define SLIM_HDL_TO_FLOW(hdl)	(((u32)(hdl) & 0xFF0000) >> 16)
+#define SLIM_HDL_TO_PORT(hdl)	((u32)(hdl) & 0xFF)
+
+#define SLIM_HDL_TO_CHIDX(hdl)	((u16)(hdl) & 0xFF)
+#define SLIM_GRP_TO_NCHAN(hdl)	((u16)(hdl >> 8) & 0xFF)
+
+#define SLIM_SLAVE_PORT(p, la)	(((la)<<16) | (p))
+#define SLIM_MGR_PORT(p)	((0xFF << 16) | (p))
+#define SLIM_LA_MANAGER		0xFF
+
+#define SLIM_START_GRP		(1 << 8)
+#define SLIM_END_GRP		(1 << 9)
+
+#define SLIM_MAX_INTR_COEFF_3	(SLIM_SL_PER_SUPERFRAME/3)
+#define SLIM_MAX_INTR_COEFF_1	SLIM_SL_PER_SUPERFRAME
+
+static DEFINE_MUTEX(slim_lock);
+static DEFINE_IDR(ctrl_idr);
+static struct device_type slim_dev_type;
+static struct device_type slim_ctrl_type;
+
+#define DEFINE_SLIM_LDEST_TXN(name, mc, len, rl, rbuf, wbuf, la) \
+	struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_LOGICALADDR, 0,\
+					len, 0, la, false, rbuf, wbuf, NULL, }
+
+#define DEFINE_SLIM_BCAST_TXN(name, mc, len, rl, rbuf, wbuf, la) \
+	struct slim_msg_txn name = { rl, 0, mc, SLIM_MSG_DEST_BROADCAST, 0,\
+					len, 0, la, false, rbuf, wbuf, NULL, }
+
+static const struct slim_device_id *slim_match(const struct slim_device_id *id,
+					const struct slim_device *slim_dev)
+{
+	while (id->name[0]) {
+		if (strcmp(slim_dev->name, id->name) == 0)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+const struct slim_device_id *slim_get_device_id(const struct slim_device *sdev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(sdev->dev.driver);
+
+	return slim_match(sdrv->id_table, sdev);
+}
+EXPORT_SYMBOL(slim_get_device_id);
+
+static int slim_device_match(struct device *dev, struct device_driver *driver)
+{
+	struct slim_device *slim_dev;
+	struct slim_driver *drv = to_slim_driver(driver);
+
+	if (dev->type == &slim_dev_type)
+		slim_dev = to_slim_device(dev);
+	else
+		return 0;
+	if (drv->id_table)
+		return slim_match(drv->id_table, slim_dev) != NULL;
+
+	if (driver->name)
+		return strcmp(slim_dev->name, driver->name) == 0;
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int slim_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct slim_device *slim_dev = NULL;
+	struct slim_driver *driver;
+
+	if (dev->type == &slim_dev_type)
+		slim_dev = to_slim_device(dev);
+
+	if (!slim_dev || !dev->driver)
+		return 0;
+
+	driver = to_slim_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+
+	return driver->suspend(slim_dev, mesg);
+}
+
+static int slim_legacy_resume(struct device *dev)
+{
+	struct slim_device *slim_dev = NULL;
+	struct slim_driver *driver;
+
+	if (dev->type == &slim_dev_type)
+		slim_dev = to_slim_device(dev);
+
+	if (!slim_dev || !dev->driver)
+		return 0;
+
+	driver = to_slim_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+
+	return driver->resume(slim_dev);
+}
+
+static int slim_pm_suspend(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_suspend(dev);
+	else
+		return slim_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int slim_pm_resume(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_resume(dev);
+	else
+		return slim_legacy_resume(dev);
+}
+
+#else
+#define slim_pm_suspend		NULL
+#define slim_pm_resume		NULL
+#endif
+
+static const struct dev_pm_ops slimbus_pm = {
+	.suspend = slim_pm_suspend,
+	.resume = slim_pm_resume,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_suspend,
+		pm_generic_resume,
+		NULL
+		)
+};
+struct bus_type slimbus_type = {
+	.name		= "slimbus",
+	.match		= slim_device_match,
+	.pm		= &slimbus_pm,
+};
+EXPORT_SYMBOL(slimbus_type);
+
+struct device slimbus_dev = {
+	.init_name = "slimbus",
+};
+
+static void __exit slimbus_exit(void)
+{
+	device_unregister(&slimbus_dev);
+	bus_unregister(&slimbus_type);
+}
+
+static int __init slimbus_init(void)
+{
+	int retval;
+
+	retval = bus_register(&slimbus_type);
+	if (!retval)
+		retval = device_register(&slimbus_dev);
+
+	if (retval)
+		bus_unregister(&slimbus_type);
+
+	return retval;
+}
+postcore_initcall(slimbus_init);
+module_exit(slimbus_exit);
+
+static int slim_drv_probe(struct device *dev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(dev->driver);
+	struct slim_device *sbdev = to_slim_device(dev);
+	struct slim_controller *ctrl = sbdev->ctrl;
+
+	if (sdrv->probe) {
+		int ret;
+
+		ret = sdrv->probe(sbdev);
+		if (ret)
+			return ret;
+		if (sdrv->device_up)
+			queue_work(ctrl->wq, &sbdev->wd);
+		return 0;
+	}
+	return -ENODEV;
+}
+
+static int slim_drv_remove(struct device *dev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(dev->driver);
+	struct slim_device *sbdev = to_slim_device(dev);
+
+	sbdev->notified = false;
+	if (sdrv->remove)
+		return sdrv->remove(to_slim_device(dev));
+	return -ENODEV;
+}
+
+static void slim_drv_shutdown(struct device *dev)
+{
+	const struct slim_driver *sdrv = to_slim_driver(dev->driver);
+
+	if (sdrv->shutdown)
+		sdrv->shutdown(to_slim_device(dev));
+}
+
+/*
+ * slim_driver_register: Client driver registration with slimbus
+ * @drv:Client driver to be associated with client-device.
+ * This API will register the client driver with the slimbus
+ * It is called from the driver's module-init function.
+ */
+int slim_driver_register(struct slim_driver *drv)
+{
+	drv->driver.bus = &slimbus_type;
+	if (drv->probe)
+		drv->driver.probe = slim_drv_probe;
+
+	if (drv->remove)
+		drv->driver.remove = slim_drv_remove;
+
+	if (drv->shutdown)
+		drv->driver.shutdown = slim_drv_shutdown;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(slim_driver_register);
+
+/*
+ * slim_driver_unregister: Undo effects of slim_driver_register
+ * @drv: Client driver to be unregistered
+ */
+void slim_driver_unregister(struct slim_driver *drv)
+{
+	if (drv)
+		driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(slim_driver_unregister);
+
+#define slim_ctrl_attr_gr NULL
+
+static void slim_ctrl_release(struct device *dev)
+{
+	struct slim_controller *ctrl = to_slim_controller(dev);
+
+	complete(&ctrl->dev_released);
+}
+
+static struct device_type slim_ctrl_type = {
+	.groups		= slim_ctrl_attr_gr,
+	.release	= slim_ctrl_release,
+};
+
+static struct slim_controller *slim_ctrl_get(struct slim_controller *ctrl)
+{
+	if (!ctrl || !get_device(&ctrl->dev))
+		return NULL;
+
+	return ctrl;
+}
+
+static void slim_ctrl_put(struct slim_controller *ctrl)
+{
+	if (ctrl)
+		put_device(&ctrl->dev);
+}
+
+#define slim_device_attr_gr NULL
+#define slim_device_uevent NULL
+static void slim_dev_release(struct device *dev)
+{
+	struct slim_device *sbdev = to_slim_device(dev);
+
+	slim_ctrl_put(sbdev->ctrl);
+}
+
+static struct device_type slim_dev_type = {
+	.groups		= slim_device_attr_gr,
+	.uevent		= slim_device_uevent,
+	.release	= slim_dev_release,
+};
+
+static void slim_report(struct work_struct *work)
+{
+	struct slim_driver *sbdrv;
+	struct slim_device *sbdev =
+			container_of(work, struct slim_device, wd);
+	if (!sbdev->dev.driver)
+		return;
+	/* check if device-up or down needs to be called */
+	if ((!sbdev->reported && !sbdev->notified) ||
+			(sbdev->reported && sbdev->notified))
+		return;
+
+	sbdrv = to_slim_driver(sbdev->dev.driver);
+	/*
+	 * address no longer valid, means device reported absent, whereas
+	 * address valid, means device reported present
+	 */
+	if (sbdev->notified && !sbdev->reported) {
+		sbdev->notified = false;
+		if (sbdrv->device_down)
+			sbdrv->device_down(sbdev);
+	} else if (!sbdev->notified && sbdev->reported) {
+		sbdev->notified = true;
+		if (sbdrv->device_up)
+			sbdrv->device_up(sbdev);
+	}
+}
+
+/*
+ * slim_add_device: Add a new device without register board info.
+ * @ctrl: Controller to which this device is to be added to.
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+int slim_add_device(struct slim_controller *ctrl, struct slim_device *sbdev)
+{
+	sbdev->dev.bus = &slimbus_type;
+	sbdev->dev.parent = ctrl->dev.parent;
+	sbdev->dev.type = &slim_dev_type;
+	sbdev->dev.driver = NULL;
+	sbdev->ctrl = ctrl;
+	slim_ctrl_get(ctrl);
+	dev_set_name(&sbdev->dev, "%s", sbdev->name);
+	mutex_init(&sbdev->sldev_reconf);
+	INIT_LIST_HEAD(&sbdev->mark_define);
+	INIT_LIST_HEAD(&sbdev->mark_suspend);
+	INIT_LIST_HEAD(&sbdev->mark_removal);
+	INIT_WORK(&sbdev->wd, slim_report);
+	mutex_lock(&ctrl->m_ctrl);
+	list_add_tail(&sbdev->dev_list, &ctrl->devs);
+	mutex_unlock(&ctrl->m_ctrl);
+	/* probe slave on this controller */
+	return device_register(&sbdev->dev);
+}
+EXPORT_SYMBOL(slim_add_device);
+
+struct sbi_boardinfo {
+	struct list_head	list;
+	struct slim_boardinfo	board_info;
+};
+
+static LIST_HEAD(board_list);
+static LIST_HEAD(slim_ctrl_list);
+static DEFINE_MUTEX(board_lock);
+
+/* If controller is not present, only add to boards list */
+static void slim_match_ctrl_to_boardinfo(struct slim_controller *ctrl,
+				struct slim_boardinfo *bi)
+{
+	int ret;
+
+	if (ctrl->nr != bi->bus_num)
+		return;
+
+	ret = slim_add_device(ctrl, bi->slim_slave);
+	if (ret != 0)
+		dev_err(ctrl->dev.parent, "can't create new device for %s\n",
+			bi->slim_slave->name);
+}
+
+/*
+ * slim_register_board_info: Board-initialization routine.
+ * @info: List of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+int slim_register_board_info(struct slim_boardinfo const *info, unsigned int n)
+{
+	struct sbi_boardinfo *bi;
+	int i;
+
+	bi = kcalloc(n, sizeof(*bi), GFP_KERNEL);
+	if (!bi)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++, bi++, info++) {
+		struct slim_controller *ctrl;
+
+		memcpy(&bi->board_info, info, sizeof(*info));
+		mutex_lock(&board_lock);
+		list_add_tail(&bi->list, &board_list);
+		list_for_each_entry(ctrl, &slim_ctrl_list, list)
+			slim_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
+		mutex_unlock(&board_lock);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(slim_register_board_info);
+
+/*
+ * slim_ctrl_add_boarddevs: Add devices registered by board-info
+ * @ctrl: Controller to which these devices are to be added to.
+ * This API is called by controller when it is up and running.
+ * If devices on a controller were registered before controller,
+ * this will make sure that they get probed when controller is up.
+ */
+void slim_ctrl_add_boarddevs(struct slim_controller *ctrl)
+{
+	struct sbi_boardinfo *bi;
+
+	mutex_lock(&board_lock);
+	list_add_tail(&ctrl->list, &slim_ctrl_list);
+	list_for_each_entry(bi, &board_list, list)
+		slim_match_ctrl_to_boardinfo(ctrl, &bi->board_info);
+	mutex_unlock(&board_lock);
+}
+EXPORT_SYMBOL(slim_ctrl_add_boarddevs);
+
+/*
+ * slim_busnum_to_ctrl: Map bus number to controller
+ * @busnum: Bus number
+ * Returns controller representing this bus number
+ */
+struct slim_controller *slim_busnum_to_ctrl(u32 bus_num)
+{
+	struct slim_controller *ctrl;
+
+	mutex_lock(&board_lock);
+	list_for_each_entry(ctrl, &slim_ctrl_list, list)
+		if (bus_num == ctrl->nr) {
+			mutex_unlock(&board_lock);
+			return ctrl;
+		}
+	mutex_unlock(&board_lock);
+	return NULL;
+}
+EXPORT_SYMBOL(slim_busnum_to_ctrl);
+
+static int slim_register_controller(struct slim_controller *ctrl)
+{
+	int ret = 0;
+
+	/* Can't register until after driver model init */
+	if (WARN_ON(!slimbus_type.p)) {
+		ret = -EPROBE_DEFER;
+		goto out_list;
+	}
+
+	dev_set_name(&ctrl->dev, "sb-%d", ctrl->nr);
+	ctrl->dev.bus = &slimbus_type;
+	ctrl->dev.type = &slim_ctrl_type;
+	ctrl->num_dev = 0;
+	if (!ctrl->min_cg)
+		ctrl->min_cg = SLIM_MIN_CLK_GEAR;
+	if (!ctrl->max_cg)
+		ctrl->max_cg = SLIM_MAX_CLK_GEAR;
+	spin_lock_init(&ctrl->txn_lock);
+	mutex_init(&ctrl->m_ctrl);
+	mutex_init(&ctrl->sched.m_reconf);
+	ret = device_register(&ctrl->dev);
+	if (ret)
+		goto out_list;
+
+	dev_dbg(&ctrl->dev, "Bus [%s] registered:dev:%p\n", ctrl->name,
+							&ctrl->dev);
+
+	if (ctrl->nports) {
+		ctrl->ports = kcalloc(ctrl->nports, sizeof(struct slim_port),
+					GFP_KERNEL);
+		if (!ctrl->ports) {
+			ret = -ENOMEM;
+			goto err_port_failed;
+		}
+	}
+	if (ctrl->nchans) {
+		ctrl->chans = kcalloc(ctrl->nchans, sizeof(struct slim_ich),
+					GFP_KERNEL);
+		if (!ctrl->chans) {
+			ret = -ENOMEM;
+			goto err_chan_failed;
+		}
+
+		ctrl->sched.chc1 = kcalloc(ctrl->nchans,
+					sizeof(struct slim_ich *), GFP_KERNEL);
+		if (!ctrl->sched.chc1) {
+			kfree(ctrl->chans);
+			ret = -ENOMEM;
+			goto err_chan_failed;
+		}
+		ctrl->sched.chc3 = kcalloc(ctrl->nchans,
+					sizeof(struct slim_ich *), GFP_KERNEL);
+		if (!ctrl->sched.chc3) {
+			kfree(ctrl->sched.chc1);
+			kfree(ctrl->chans);
+			ret = -ENOMEM;
+			goto err_chan_failed;
+		}
+	}
+#ifdef DEBUG
+	ctrl->sched.slots = kzalloc(SLIM_SL_PER_SUPERFRAME, GFP_KERNEL);
+#endif
+	init_completion(&ctrl->pause_comp);
+
+	INIT_LIST_HEAD(&ctrl->devs);
+	ctrl->wq = create_singlethread_workqueue(dev_name(&ctrl->dev));
+	if (!ctrl->wq)
+		goto err_workq_failed;
+
+	return 0;
+
+err_workq_failed:
+	kfree(ctrl->sched.chc3);
+	kfree(ctrl->sched.chc1);
+	kfree(ctrl->chans);
+err_chan_failed:
+	kfree(ctrl->ports);
+err_port_failed:
+	device_unregister(&ctrl->dev);
+out_list:
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+	return ret;
+}
+
+/* slim_remove_device: Remove the effect of slim_add_device() */
+void slim_remove_device(struct slim_device *sbdev)
+{
+	struct slim_controller *ctrl = sbdev->ctrl;
+
+	mutex_lock(&ctrl->m_ctrl);
+	list_del_init(&sbdev->dev_list);
+	mutex_unlock(&ctrl->m_ctrl);
+	device_unregister(&sbdev->dev);
+}
+EXPORT_SYMBOL(slim_remove_device);
+
+static void slim_ctrl_remove_device(struct slim_controller *ctrl,
+				struct slim_boardinfo *bi)
+{
+	if (ctrl->nr == bi->bus_num)
+		slim_remove_device(bi->slim_slave);
+}
+
+/*
+ * slim_del_controller: Controller tear-down.
+ * Controller added with the above API is teared down using this API.
+ */
+int slim_del_controller(struct slim_controller *ctrl)
+{
+	struct slim_controller *found;
+	struct sbi_boardinfo *bi;
+
+	/* First make sure that this bus was added */
+	mutex_lock(&slim_lock);
+	found = idr_find(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+	if (found != ctrl)
+		return -EINVAL;
+
+	/* Remove all clients */
+	mutex_lock(&board_lock);
+	list_for_each_entry(bi, &board_list, list)
+		slim_ctrl_remove_device(ctrl, &bi->board_info);
+	mutex_unlock(&board_lock);
+
+	init_completion(&ctrl->dev_released);
+	device_unregister(&ctrl->dev);
+
+	wait_for_completion(&ctrl->dev_released);
+	list_del(&ctrl->list);
+	destroy_workqueue(ctrl->wq);
+	/* free bus id */
+	mutex_lock(&slim_lock);
+	idr_remove(&ctrl_idr, ctrl->nr);
+	mutex_unlock(&slim_lock);
+
+	kfree(ctrl->sched.chc1);
+	kfree(ctrl->sched.chc3);
+#ifdef DEBUG
+	kfree(ctrl->sched.slots);
+#endif
+	kfree(ctrl->chans);
+	kfree(ctrl->ports);
+
+	return 0;
+}
+EXPORT_SYMBOL(slim_del_controller);
+
+/*
+ * slim_add_numbered_controller: Controller bring-up.
+ * @ctrl: Controller to be registered.
+ * A controller is registered with the framework using this API. ctrl->nr is the
+ * desired number with which slimbus framework registers the controller.
+ * Function will return -EBUSY if the number is in use.
+ */
+int slim_add_numbered_controller(struct slim_controller *ctrl)
+{
+	int	id;
+
+	mutex_lock(&slim_lock);
+	id = idr_alloc(&ctrl_idr, ctrl, ctrl->nr, ctrl->nr + 1, GFP_KERNEL);
+	mutex_unlock(&slim_lock);
+
+	if (id < 0)
+		return id;
+
+	ctrl->nr = id;
+	return slim_register_controller(ctrl);
+}
+EXPORT_SYMBOL(slim_add_numbered_controller);
+
+/*
+ * slim_report_absent: Controller calls this function when a device
+ *	reports absent, OR when the device cannot be communicated with
+ * @sbdev: Device that cannot be reached, or sent report absent
+ */
+void slim_report_absent(struct slim_device *sbdev)
+{
+	struct slim_controller *ctrl;
+	int i;
+
+	if (!sbdev)
+		return;
+	ctrl = sbdev->ctrl;
+	if (!ctrl)
+		return;
+	/* invalidate logical addresses */
+	mutex_lock(&ctrl->m_ctrl);
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (sbdev->laddr == ctrl->addrt[i].laddr)
+			ctrl->addrt[i].valid = false;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	sbdev->reported = false;
+	queue_work(ctrl->wq, &sbdev->wd);
+}
+EXPORT_SYMBOL(slim_report_absent);
+
+static int slim_remove_ch(struct slim_controller *ctrl, struct slim_ich *slc);
+/*
+ * slim_framer_booted: This function is called by controller after the active
+ * framer has booted (using Bus Reset sequence, or after it has shutdown and has
+ * come back up). Components, devices on the bus may be in undefined state,
+ * and this function triggers their drivers to do the needful
+ * to bring them back in Reset state so that they can acquire sync, report
+ * present and be operational again.
+ */
+void slim_framer_booted(struct slim_controller *ctrl)
+{
+	struct slim_device *sbdev;
+	struct list_head *pos, *next;
+	int i;
+
+	if (!ctrl)
+		return;
+
+	/* Since framer has rebooted, reset all data channels */
+	mutex_lock(&ctrl->sched.m_reconf);
+	for (i = 0; i < ctrl->nchans; i++) {
+		struct slim_ich *slc = &ctrl->chans[i];
+
+		if (slc->state > SLIM_CH_DEFINED)
+			slim_remove_ch(ctrl, slc);
+	}
+	mutex_unlock(&ctrl->sched.m_reconf);
+	mutex_lock(&ctrl->m_ctrl);
+	list_for_each_safe(pos, next, &ctrl->devs) {
+		struct slim_driver *sbdrv;
+
+		sbdev = list_entry(pos, struct slim_device, dev_list);
+		mutex_unlock(&ctrl->m_ctrl);
+		if (sbdev && sbdev->dev.driver) {
+			sbdrv = to_slim_driver(sbdev->dev.driver);
+			if (sbdrv->reset_device)
+				sbdrv->reset_device(sbdev);
+		}
+		mutex_lock(&ctrl->m_ctrl);
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+}
+EXPORT_SYMBOL(slim_framer_booted);
+
+/*
+ * slim_msg_response: Deliver Message response received from a device to the
+ *	framework.
+ * @ctrl: Controller handle
+ * @reply: Reply received from the device
+ * @len: Length of the reply
+ * @tid: Transaction ID received with which framework can associate reply.
+ * Called by controller to inform framework about the response received.
+ * This helps in making the API asynchronous, and controller-driver doesn't need
+ * to manage 1 more table other than the one managed by framework mapping TID
+ * with buffers
+ */
+void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid, u8 len)
+{
+	int i;
+	unsigned long flags;
+	bool async;
+	struct slim_msg_txn *txn;
+
+	spin_lock_irqsave(&ctrl->txn_lock, flags);
+	txn = ctrl->txnt[tid];
+	if (txn == NULL || txn->rbuf == NULL) {
+		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+		if (txn == NULL)
+			dev_err(&ctrl->dev, "Got response to invalid TID:%d, len:%d",
+				tid, len);
+		else
+			dev_err(&ctrl->dev, "Invalid client buffer passed\n");
+		return;
+	}
+	async = txn->async;
+	for (i = 0; i < len; i++)
+		txn->rbuf[i] = reply[i];
+	if (txn->comp)
+		complete(txn->comp);
+	ctrl->txnt[tid] = NULL;
+	spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	if (async)
+		kfree(txn);
+}
+EXPORT_SYMBOL(slim_msg_response);
+
+static int slim_processtxn(struct slim_controller *ctrl,
+				struct slim_msg_txn *txn, bool need_tid)
+{
+	u8 i = 0;
+	int ret = 0;
+	unsigned long flags;
+
+	if (need_tid) {
+		spin_lock_irqsave(&ctrl->txn_lock, flags);
+		for (i = 0; i < ctrl->last_tid; i++) {
+			if (ctrl->txnt[i] == NULL)
+				break;
+		}
+		if (i >= ctrl->last_tid) {
+			if (ctrl->last_tid == 255) {
+				spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+				return -ENOMEM;
+			}
+			ctrl->last_tid++;
+		}
+		ctrl->txnt[i] = txn;
+		txn->tid = i;
+		spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+	}
+
+	ret = ctrl->xfer_msg(ctrl, txn);
+	return ret;
+}
+
+static int ctrl_getlogical_addr(struct slim_controller *ctrl, const u8 *eaddr,
+				u8 e_len, u8 *entry)
+{
+	u8 i;
+
+	for (i = 0; i < ctrl->num_dev; i++) {
+		if (ctrl->addrt[i].valid &&
+			memcmp(ctrl->addrt[i].eaddr, eaddr, e_len) == 0) {
+			*entry = i;
+			return 0;
+		}
+	}
+	return -ENXIO;
+}
+
+/*
+ * slim_assign_laddr: Assign logical address to a device enumerated.
+ * @ctrl: Controller with which device is enumerated.
+ * @e_addr: 6-byte elemental address of the device.
+ * @e_len: buffer length for e_addr
+ * @laddr: Return logical address (if valid flag is false)
+ * @valid: true if laddr holds a valid address that controller wants to
+ *	set for this enumeration address. Otherwise framework sets index into
+ *	address table as logical address.
+ * Called by controller in response to REPORT_PRESENT. Framework will assign
+ * a logical address to this enumeration address.
+ * Function returns -EXFULL to indicate that all logical addresses are already
+ * taken.
+ */
+int slim_assign_laddr(struct slim_controller *ctrl, const u8 *e_addr,
+				u8 e_len, u8 *laddr, bool valid)
+{
+	int ret;
+	u8 i = 0;
+	bool exists = false;
+	struct slim_device *sbdev;
+	struct list_head *pos, *next;
+	void *new_addrt = NULL;
+
+	mutex_lock(&ctrl->m_ctrl);
+	/* already assigned */
+	if (ctrl_getlogical_addr(ctrl, e_addr, e_len, &i) == 0) {
+		*laddr = ctrl->addrt[i].laddr;
+		exists = true;
+	} else {
+		if (ctrl->num_dev >= 254) {
+			ret = -EXFULL;
+			goto ret_assigned_laddr;
+		}
+		for (i = 0; i < ctrl->num_dev; i++) {
+			if (ctrl->addrt[i].valid == false)
+				break;
+		}
+		if (i == ctrl->num_dev) {
+			new_addrt = krealloc(ctrl->addrt,
+					(ctrl->num_dev + 1) *
+					sizeof(struct slim_addrt),
+					GFP_KERNEL);
+			if (!new_addrt) {
+				ret = -ENOMEM;
+				goto ret_assigned_laddr;
+			}
+			ctrl->addrt = new_addrt;
+			ctrl->num_dev++;
+		}
+		memcpy(ctrl->addrt[i].eaddr, e_addr, e_len);
+		ctrl->addrt[i].valid = true;
+		/* Preferred address is index into table */
+		if (!valid)
+			*laddr = i;
+	}
+
+	ret = ctrl->set_laddr(ctrl, (const u8 *)&ctrl->addrt[i].eaddr, 6,
+				*laddr);
+	if (ret) {
+		ctrl->addrt[i].valid = false;
+		goto ret_assigned_laddr;
+	}
+	ctrl->addrt[i].laddr = *laddr;
+
+	dev_dbg(&ctrl->dev, "setting slimbus l-addr:%x\n", *laddr);
+ret_assigned_laddr:
+	mutex_unlock(&ctrl->m_ctrl);
+	if (exists || ret)
+		return ret;
+
+	pr_info("slimbus:%d laddr:0x%x, EAPC:0x%x:0x%x", ctrl->nr, *laddr,
+				e_addr[1], e_addr[2]);
+	mutex_lock(&ctrl->m_ctrl);
+	list_for_each_safe(pos, next, &ctrl->devs) {
+		sbdev = list_entry(pos, struct slim_device, dev_list);
+		if (memcmp(sbdev->e_addr, e_addr, 6) == 0) {
+			struct slim_driver *sbdrv;
+
+			sbdev->laddr = *laddr;
+			sbdev->reported = true;
+			if (sbdev->dev.driver) {
+				sbdrv = to_slim_driver(sbdev->dev.driver);
+				if (sbdrv->device_up)
+					queue_work(ctrl->wq, &sbdev->wd);
+			}
+			break;
+		}
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	return 0;
+}
+EXPORT_SYMBOL(slim_assign_laddr);
+
+/*
+ * slim_get_logical_addr: Return the logical address of a slimbus device.
+ * @sb: client handle requesting the adddress.
+ * @e_addr: Elemental address of the device.
+ * @e_len: Length of e_addr
+ * @laddr: output buffer to store the address
+ * context: can sleep
+ * -EINVAL is returned in case of invalid parameters, and -ENXIO is returned if
+ *  the device with this elemental address is not found.
+ */
+int slim_get_logical_addr(struct slim_device *sb, const u8 *e_addr,
+				u8 e_len, u8 *laddr)
+{
+	int ret = 0;
+	u8 entry;
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl || !laddr || !e_addr || e_len != 6)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	ret = ctrl_getlogical_addr(ctrl, e_addr, e_len, &entry);
+	if (!ret)
+		*laddr = ctrl->addrt[entry].laddr;
+	mutex_unlock(&ctrl->m_ctrl);
+	if (ret == -ENXIO && ctrl->get_laddr) {
+		ret = ctrl->get_laddr(ctrl, e_addr, e_len, laddr);
+		if (!ret)
+			ret = slim_assign_laddr(ctrl, e_addr, e_len, laddr,
+						true);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(slim_get_logical_addr);
+
+static int slim_ele_access_sanity(struct slim_ele_access *msg, int oper,
+				u8 *rbuf, const u8 *wbuf, u8 len)
+{
+	if (!msg || msg->num_bytes > 16 || msg->start_offset + len > 0xC00)
+		return -EINVAL;
+	switch (oper) {
+	case SLIM_MSG_MC_REQUEST_VALUE:
+	case SLIM_MSG_MC_REQUEST_INFORMATION:
+		if (rbuf == NULL)
+			return -EINVAL;
+		return 0;
+	case SLIM_MSG_MC_CHANGE_VALUE:
+	case SLIM_MSG_MC_CLEAR_INFORMATION:
+		if (wbuf == NULL)
+			return -EINVAL;
+		return 0;
+	case SLIM_MSG_MC_REQUEST_CHANGE_VALUE:
+	case SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION:
+		if (rbuf == NULL || wbuf == NULL)
+			return -EINVAL;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static u16 slim_slicecodefromsize(u32 req)
+{
+	u8 codetosize[8] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+	if (req >= 8)
+		return 0;
+	else
+		return codetosize[req];
+}
+
+static u16 slim_slicesize(u32 code)
+{
+	u8 sizetocode[16] = {0, 1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7};
+
+	if (code == 0)
+		code = 1;
+	if (code > 16)
+		code = 16;
+	return sizetocode[code - 1];
+}
+
+
+/* Message APIs Unicast message APIs used by slimbus slave drivers */
+
+/*
+ * Message API access routines.
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to read.
+ * @rbuf: data buffer to be filled with values read.
+ * @len: data buffer size
+ * @wbuf: data buffer containing value/information to be written
+ * context: can sleep
+ * Returns:
+ * -EINVAL: Invalid parameters
+ * -ETIMEDOUT: If controller could not complete the request. This may happen if
+ *  the bus lines are not clocked, controller is not powered-on, slave with
+ *  given address is not enumerated/responding.
+ */
+int slim_request_val_element(struct slim_device *sb,
+				struct slim_ele_access *msg, u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_VALUE, buf,
+			NULL, len);
+}
+EXPORT_SYMBOL(slim_request_val_element);
+
+int slim_request_inf_element(struct slim_device *sb,
+				struct slim_ele_access *msg, u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_INFORMATION,
+			buf, NULL, len);
+}
+EXPORT_SYMBOL(slim_request_inf_element);
+
+int slim_change_val_element(struct slim_device *sb, struct slim_ele_access *msg,
+				const u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_CHANGE_VALUE, NULL, buf,
+					len);
+}
+EXPORT_SYMBOL(slim_change_val_element);
+
+int slim_clear_inf_element(struct slim_device *sb, struct slim_ele_access *msg,
+				u8 *buf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_CLEAR_INFORMATION, NULL,
+					buf, len);
+}
+EXPORT_SYMBOL(slim_clear_inf_element);
+
+int slim_request_change_val_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *rbuf,
+					const u8 *wbuf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg, SLIM_MSG_MC_REQUEST_CHANGE_VALUE,
+					rbuf, wbuf, len);
+}
+EXPORT_SYMBOL(slim_request_change_val_element);
+
+int slim_request_clear_inf_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *rbuf,
+					const u8 *wbuf, u8 len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl)
+		return -EINVAL;
+	return slim_xfer_msg(ctrl, sb, msg,
+					SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION,
+					rbuf, wbuf, len);
+}
+EXPORT_SYMBOL(slim_request_clear_inf_element);
+
+/*
+ * Broadcast message API:
+ * call this API directly with sbdev = NULL.
+ * For broadcast reads, make sure that buffers are big-enough to incorporate
+ * replies from all logical addresses.
+ * All controllers may not support broadcast
+ */
+int slim_xfer_msg(struct slim_controller *ctrl, struct slim_device *sbdev,
+			struct slim_ele_access *msg, u16 mc, u8 *rbuf,
+			const u8 *wbuf, u8 len)
+{
+	DECLARE_COMPLETION_ONSTACK(complete);
+	DEFINE_SLIM_LDEST_TXN(txn_stack, mc, len, 6, rbuf, wbuf, sbdev->laddr);
+	struct slim_msg_txn *txn;
+	int ret;
+	u16 sl, cur;
+
+	if (msg->comp && rbuf) {
+		txn = kmalloc(sizeof(struct slim_msg_txn),
+						GFP_KERNEL);
+		if (IS_ERR_OR_NULL(txn))
+			return PTR_ERR(txn);
+		*txn = txn_stack;
+		txn->async = true;
+		txn->comp = msg->comp;
+	} else {
+		txn = &txn_stack;
+		if (rbuf)
+			txn->comp = &complete;
+	}
+
+	ret = slim_ele_access_sanity(msg, mc, rbuf, wbuf, len);
+	if (ret)
+		goto xfer_err;
+
+	sl = slim_slicesize(len);
+	dev_dbg(&ctrl->dev, "SB xfer msg:os:%x, len:%d, MC:%x, sl:%x\n",
+				msg->start_offset, len, mc, sl);
+
+	cur = slim_slicecodefromsize(sl);
+	txn->ec = ((sl | (1 << 3)) | ((msg->start_offset & 0xFFF) << 4));
+
+	if (wbuf)
+		txn->rl += len;
+	if (rbuf) {
+		unsigned long flags;
+
+		txn->rl++;
+		ret = slim_processtxn(ctrl, txn, true);
+
+		/* sync read */
+		if (!ret && !msg->comp) {
+			ret = wait_for_completion_timeout(&complete, HZ);
+			if (!ret) {
+				dev_err(&ctrl->dev, "slimbus Read timed out");
+				spin_lock_irqsave(&ctrl->txn_lock, flags);
+				/* Invalidate the transaction */
+				ctrl->txnt[txn->tid] = NULL;
+				spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+				ret = -ETIMEDOUT;
+			} else
+				ret = 0;
+		} else if (ret < 0 && !msg->comp) {
+			dev_err(&ctrl->dev, "slimbus Read error");
+			spin_lock_irqsave(&ctrl->txn_lock, flags);
+			/* Invalidate the transaction */
+			ctrl->txnt[txn->tid] = NULL;
+			spin_unlock_irqrestore(&ctrl->txn_lock, flags);
+		}
+
+	} else
+		ret = slim_processtxn(ctrl, txn, false);
+xfer_err:
+	return ret;
+}
+EXPORT_SYMBOL(slim_xfer_msg);
+
+/*
+ * User message:
+ * slim_user_msg: Send user message that is interpreted by destination device
+ * @sb: Client handle sending the message
+ * @la: Destination device for this user message
+ * @mt: Message Type (Soruce-referred, or Destination-referred)
+ * @mc: Message Code
+ * @msg: Message structure (start offset, number of bytes) to be sent
+ * @buf: data buffer to be sent
+ * @len: data buffer size in bytes
+ */
+int slim_user_msg(struct slim_device *sb, u8 la, u8 mt, u8 mc,
+				struct slim_ele_access *msg, u8 *buf, u8 len)
+{
+	if (!sb || !sb->ctrl || !msg || mt == SLIM_MSG_MT_CORE)
+		return -EINVAL;
+	if (!sb->ctrl->xfer_user_msg)
+		return -EPROTONOSUPPORT;
+	return sb->ctrl->xfer_user_msg(sb->ctrl, la, mt, mc, msg, buf, len);
+}
+EXPORT_SYMBOL(slim_user_msg);
+
+/*
+ * Queue bulk of message writes:
+ * slim_bulk_msg_write: Write bulk of messages (e.g. downloading FW)
+ * @sb: Client handle sending these messages
+ * @la: Destination device for these messages
+ * @mt: Message Type
+ * @mc: Message Code
+ * @msgs: List of messages to be written in bulk
+ * @n: Number of messages in the list
+ * @cb: Callback if client needs this to be non-blocking
+ * @ctx: Context for this callback
+ * If supported by controller, this message list will be sent in bulk to the HW
+ * If the client specifies this to be non-blocking, the callback will be
+ * called from atomic context.
+ */
+int slim_bulk_msg_write(struct slim_device *sb, u8 mt, u8 mc,
+			struct slim_val_inf msgs[], int n,
+			int (*comp_cb)(void *ctx, int err), void *ctx)
+{
+	int i, ret = 0;
+
+	if (!sb || !sb->ctrl || !msgs || n <= 0)
+		return -EINVAL;
+	if (!sb->ctrl->xfer_bulk_wr) {
+		pr_warn("controller does not support bulk WR, serializing");
+		for (i = 0; i < n; i++) {
+			struct slim_ele_access ele;
+
+			ele.comp = NULL;
+			ele.start_offset = msgs[i].start_offset;
+			ele.num_bytes = msgs[i].num_bytes;
+			ret = slim_xfer_msg(sb->ctrl, sb, &ele, mc,
+					msgs[i].rbuf, msgs[i].wbuf,
+					ele.num_bytes);
+			if (ret)
+				return ret;
+		}
+		return ret;
+	}
+	return sb->ctrl->xfer_bulk_wr(sb->ctrl, sb->laddr, mt, mc, msgs, n,
+					comp_cb, ctx);
+}
+EXPORT_SYMBOL(slim_bulk_msg_write);
+
+/*
+ * slim_alloc_mgrports: Allocate port on manager side.
+ * @sb: device/client handle.
+ * @req: Port request type.
+ * @nports: Number of ports requested
+ * @rh: output buffer to store the port handles
+ * @hsz: size of buffer storing handles
+ * context: can sleep
+ * This port will be typically used by SW. e.g. client driver wants to receive
+ * some data from audio codec HW using a data channel.
+ * Port allocated using this API will be used to receive the data.
+ * If half-duplex ports are requested, two adjacent ports are allocated for
+ * 1 half-duplex port. So the handle-buffer size should be twice the number
+ * of half-duplex ports to be allocated.
+ * -EDQUOT is returned if all ports are in use.
+ */
+int slim_alloc_mgrports(struct slim_device *sb, enum slim_port_req req,
+				int nports, u32 *rh, int hsz)
+{
+	int i, j;
+	int ret = -EINVAL;
+	int nphysp = nports;
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!rh || !ctrl)
+		return -EINVAL;
+	if (req == SLIM_REQ_HALF_DUP)
+		nphysp *= 2;
+	if (hsz/sizeof(u32) < nphysp)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+
+	for (i = 0; i < ctrl->nports; i++) {
+		bool multiok = true;
+
+		if (ctrl->ports[i].state != SLIM_P_FREE)
+			continue;
+		/* Start half duplex channel at even port */
+		if (req == SLIM_REQ_HALF_DUP && (i % 2))
+			continue;
+		/* Allocate ports contiguously for multi-ch */
+		if (ctrl->nports < (i + nphysp)) {
+			i = ctrl->nports;
+			break;
+		}
+		if (req == SLIM_REQ_MULTI_CH) {
+			multiok = true;
+			for (j = i; j < i + nphysp; j++) {
+				if (ctrl->ports[j].state != SLIM_P_FREE) {
+					multiok = false;
+					break;
+				}
+			}
+			if (!multiok)
+				continue;
+		}
+		break;
+	}
+	if (i >= ctrl->nports) {
+		ret = -EDQUOT;
+		goto alloc_err;
+	}
+	ret = 0;
+	for (j = i; j < i + nphysp; j++) {
+		ctrl->ports[j].state = SLIM_P_UNCFG;
+		ctrl->ports[j].req = req;
+		if (req == SLIM_REQ_HALF_DUP && (j % 2))
+			ctrl->ports[j].flow = SLIM_SINK;
+		else
+			ctrl->ports[j].flow = SLIM_SRC;
+		if (ctrl->alloc_port)
+			ret = ctrl->alloc_port(ctrl, j);
+		if (ret) {
+			for (; j >= i; j--)
+				ctrl->ports[j].state = SLIM_P_FREE;
+			goto alloc_err;
+		}
+		*rh++ = SLIM_PORT_HDL(SLIM_LA_MANAGER, 0, j);
+	}
+alloc_err:
+	mutex_unlock(&ctrl->m_ctrl);
+	return ret;
+}
+EXPORT_SYMBOL(slim_alloc_mgrports);
+
+/* Deallocate the port(s) allocated using the API above */
+int slim_dealloc_mgrports(struct slim_device *sb, u32 *hdl, int nports)
+{
+	int i;
+	struct slim_controller *ctrl = sb->ctrl;
+
+	if (!ctrl || !hdl)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->m_ctrl);
+
+	for (i = 0; i < nports; i++) {
+		u8 pn;
+
+		pn = SLIM_HDL_TO_PORT(hdl[i]);
+
+		if (pn >= ctrl->nports || ctrl->ports[pn].state == SLIM_P_CFG) {
+			int j, ret;
+
+			if (pn >= ctrl->nports) {
+				dev_err(&ctrl->dev, "invalid port number");
+				ret = -EINVAL;
+			} else {
+				dev_err(&ctrl->dev,
+					"Can't dealloc connected port:%d", i);
+				ret = -EISCONN;
+			}
+			for (j = i - 1; j >= 0; j--) {
+				pn = SLIM_HDL_TO_PORT(hdl[j]);
+				ctrl->ports[pn].state = SLIM_P_UNCFG;
+			}
+			mutex_unlock(&ctrl->m_ctrl);
+			return ret;
+		}
+		if (ctrl->dealloc_port)
+			ctrl->dealloc_port(ctrl, pn);
+		ctrl->ports[pn].state = SLIM_P_FREE;
+	}
+	mutex_unlock(&ctrl->m_ctrl);
+	return 0;
+}
+EXPORT_SYMBOL(slim_dealloc_mgrports);
+
+/*
+ * slim_config_mgrports: Configure manager side ports
+ * @sb: device/client handle.
+ * @ph: array of port handles for which this configuration is valid
+ * @nports: Number of ports in ph
+ * @cfg: configuration requested for port(s)
+ * Configure port settings if they are different than the default ones.
+ * Returns success if the config could be applied. Returns -EISCONN if the
+ * port is in use
+ */
+int slim_config_mgrports(struct slim_device *sb, u32 *ph, int nports,
+				struct slim_port_cfg *cfg)
+{
+	int i;
+	struct slim_controller *ctrl;
+
+	if (!sb || !ph || !nports || !sb->ctrl || !cfg)
+		return -EINVAL;
+
+	ctrl = sb->ctrl;
+	mutex_lock(&ctrl->sched.m_reconf);
+	for (i = 0; i < nports; i++) {
+		u8 pn = SLIM_HDL_TO_PORT(ph[i]);
+
+		if (ctrl->ports[pn].state == SLIM_P_CFG)
+			return -EISCONN;
+		ctrl->ports[pn].cfg = *cfg;
+	}
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return 0;
+}
+EXPORT_SYMBOL(slim_config_mgrports);
+
+/*
+ * slim_get_slaveport: Get slave port handle
+ * @la: slave device logical address.
+ * @idx: port index at slave
+ * @rh: return handle
+ * @flw: Flow type (source or destination)
+ * This API only returns a slave port's representation as expected by slimbus
+ * driver. This port is not managed by the slimbus driver. Caller is expected
+ * to have visibility of this port since it's a device-port.
+ */
+int slim_get_slaveport(u8 la, int idx, u32 *rh, enum slim_port_flow flw)
+{
+	if (rh == NULL)
+		return -EINVAL;
+	*rh = SLIM_PORT_HDL(la, flw, idx);
+	return 0;
+}
+EXPORT_SYMBOL(slim_get_slaveport);
+
+static int connect_port_ch(struct slim_controller *ctrl, u8 ch, u32 ph,
+				enum slim_port_flow flow)
+{
+	int ret;
+	u8 buf[2];
+	u32 la = SLIM_HDL_TO_LA(ph);
+	u8 pn = (u8)SLIM_HDL_TO_PORT(ph);
+	DEFINE_SLIM_LDEST_TXN(txn, 0, 2, 6, NULL, buf, la);
+
+	if (flow == SLIM_SRC)
+		txn.mc = SLIM_MSG_MC_CONNECT_SOURCE;
+	else
+		txn.mc = SLIM_MSG_MC_CONNECT_SINK;
+	buf[0] = pn;
+	buf[1] = ctrl->chans[ch].chan;
+	if (la == SLIM_LA_MANAGER)
+		ctrl->ports[pn].flow = flow;
+	ret = slim_processtxn(ctrl, &txn, false);
+	if (!ret && la == SLIM_LA_MANAGER)
+		ctrl->ports[pn].state = SLIM_P_CFG;
+	return ret;
+}
+
+static int disconnect_port_ch(struct slim_controller *ctrl, u32 ph)
+{
+	int ret;
+	u32 la = SLIM_HDL_TO_LA(ph);
+	u8 pn = (u8)SLIM_HDL_TO_PORT(ph);
+	DEFINE_SLIM_LDEST_TXN(txn, 0, 1, 5, NULL, &pn, la);
+
+	txn.mc = SLIM_MSG_MC_DISCONNECT_PORT;
+	ret = slim_processtxn(ctrl, &txn, false);
+	if (ret)
+		return ret;
+	if (la == SLIM_LA_MANAGER) {
+		ctrl->ports[pn].state = SLIM_P_UNCFG;
+		ctrl->ports[pn].cfg.watermark = 0;
+		ctrl->ports[pn].cfg.port_opts = 0;
+		ctrl->ports[pn].ch = NULL;
+	}
+	return 0;
+}
+
+/*
+ * slim_connect_src: Connect source port to channel.
+ * @sb: client handle
+ * @srch: source handle to be connected to this channel
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have 1 source port.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if source is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid direction is specified for non-manager port,
+ * or if the manager side port number is out of bounds, or in incorrect state
+ */
+int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int ret;
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+	enum slim_port_flow flow = SLIM_HDL_TO_FLOW(srch);
+	u8 la = SLIM_HDL_TO_LA(srch);
+	u8 pn = SLIM_HDL_TO_PORT(srch);
+
+	/* manager ports don't have direction when they are allocated */
+	if (la != SLIM_LA_MANAGER && flow != SLIM_SRC)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->sched.m_reconf);
+
+	if (la == SLIM_LA_MANAGER) {
+		if (pn >= ctrl->nports ||
+			ctrl->ports[pn].state != SLIM_P_UNCFG) {
+			ret = -EINVAL;
+			goto connect_src_err;
+		}
+	}
+
+	if (slc->state == SLIM_CH_FREE) {
+		ret = -ENOTCONN;
+		goto connect_src_err;
+	}
+	/*
+	 * Once channel is removed, its ports can be considered disconnected
+	 * So its ports can be reassigned. Source port is zeroed
+	 * when channel is deallocated.
+	 */
+	if (slc->srch) {
+		ret = -EALREADY;
+		goto connect_src_err;
+	}
+	ctrl->ports[pn].ch = &slc->prop;
+	ret = connect_port_ch(ctrl, chan, srch, SLIM_SRC);
+
+	if (!ret)
+		slc->srch = srch;
+
+connect_src_err:
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_connect_src);
+
+/*
+ * slim_connect_sink: Connect sink port(s) to channel.
+ * @sb: client handle
+ * @sinkh: sink handle(s) to be connected to this channel
+ * @nsink: number of sinks
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have multiple sink-ports.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if sink is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid parameters are passed, or invalid direction is
+ * specified for non-manager port, or if the manager side port number is out of
+ * bounds, or in incorrect state
+ */
+int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink, u16 chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int j;
+	int ret = 0;
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+	void *new_sinkh = NULL;
+
+	if (!sinkh || !nsink)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->sched.m_reconf);
+
+	/*
+	 * Once channel is removed, its ports can be considered disconnected
+	 * So its ports can be reassigned. Sink ports are freed when channel
+	 * is deallocated.
+	 */
+	if (slc->state == SLIM_CH_FREE) {
+		ret = -ENOTCONN;
+		goto connect_sink_err;
+	}
+
+	for (j = 0; j < nsink; j++) {
+		enum slim_port_flow flow = SLIM_HDL_TO_FLOW(sinkh[j]);
+		u8 la = SLIM_HDL_TO_LA(sinkh[j]);
+		u8 pn = SLIM_HDL_TO_PORT(sinkh[j]);
+
+		if (la != SLIM_LA_MANAGER && flow != SLIM_SINK) {
+			ret = -EINVAL;
+		} else if (la == SLIM_LA_MANAGER &&
+			   (pn >= ctrl->nports ||
+			    ctrl->ports[pn].state != SLIM_P_UNCFG)) {
+			ret = -EINVAL;
+		} else {
+			ctrl->ports[pn].ch = &slc->prop;
+			ret = connect_port_ch(ctrl, chan, sinkh[j], SLIM_SINK);
+		}
+		if (ret) {
+			for (j = j - 1; j >= 0; j--)
+				disconnect_port_ch(ctrl, sinkh[j]);
+			goto connect_sink_err;
+		}
+	}
+
+	new_sinkh = krealloc(slc->sinkh, (sizeof(u32) * (slc->nsink + nsink)),
+				GFP_KERNEL);
+	if (!new_sinkh) {
+		ret = -ENOMEM;
+		for (j = 0; j < nsink; j++)
+			disconnect_port_ch(ctrl, sinkh[j]);
+		goto connect_sink_err;
+	}
+
+	slc->sinkh = new_sinkh;
+	memcpy(slc->sinkh + slc->nsink, sinkh, (sizeof(u32) * nsink));
+	slc->nsink += nsink;
+
+connect_sink_err:
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_connect_sink);
+
+/*
+ * slim_disconnect_ports: Disconnect port(s) from channel
+ * @sb: client handle
+ * @ph: ports to be disconnected
+ * @nph: number of ports.
+ * Disconnects ports from a channel.
+ */
+int slim_disconnect_ports(struct slim_device *sb, u32 *ph, int nph)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int i;
+
+	mutex_lock(&ctrl->sched.m_reconf);
+
+	for (i = 0; i < nph; i++)
+		disconnect_port_ch(ctrl, ph[i]);
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return 0;
+}
+EXPORT_SYMBOL(slim_disconnect_ports);
+
+/*
+ * slim_port_xfer: Schedule buffer to be transferred/received using port-handle.
+ * @sb: client handle
+ * @ph: port-handle
+ * @iobuf: buffer to be transferred or populated
+ * @len: buffer size.
+ * @comp: completion signal to indicate transfer done or error.
+ * context: can sleep
+ * Returns number of bytes transferred/received if used synchronously.
+ * Will return 0 if used asynchronously.
+ * Client will call slim_port_get_xfer_status to get error and/or number of
+ * bytes transferred if used asynchronously.
+ */
+int slim_port_xfer(struct slim_device *sb, u32 ph, phys_addr_t iobuf, u32 len,
+				struct completion *comp)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u8 pn = SLIM_HDL_TO_PORT(ph);
+
+	dev_dbg(&ctrl->dev, "port xfer: num:%d", pn);
+	return ctrl->port_xfer(ctrl, pn, iobuf, len, comp);
+}
+EXPORT_SYMBOL(slim_port_xfer);
+
+/*
+ * slim_port_get_xfer_status: Poll for port transfers, or get transfer status
+ *	after completion is done.
+ * @sb: client handle
+ * @ph: port-handle
+ * @done_buf: return pointer (iobuf from slim_port_xfer) which is processed.
+ * @done_len: Number of bytes transferred.
+ * This can be called when port_xfer complition is signalled.
+ * The API will return port transfer error (underflow/overflow/disconnect)
+ * and/or done_len will reflect number of bytes transferred. Note that
+ * done_len may be valid even if port error (overflow/underflow) has happened.
+ * e.g. If the transfer was scheduled with a few bytes to be transferred and
+ * client has not supplied more data to be transferred, done_len will indicate
+ * number of bytes transferred with underflow error. To avoid frequent underflow
+ * errors, multiple transfers can be queued (e.g. ping-pong buffers) so that
+ * channel has data to be transferred even if client is not ready to transfer
+ * data all the time. done_buf will indicate address of the last buffer
+ * processed from the multiple transfers.
+ */
+enum slim_port_err slim_port_get_xfer_status(struct slim_device *sb, u32 ph,
+			phys_addr_t *done_buf, u32 *done_len)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u8 pn = SLIM_HDL_TO_PORT(ph);
+	u32 la = SLIM_HDL_TO_LA(ph);
+	enum slim_port_err err;
+
+	dev_dbg(&ctrl->dev, "get status port num:%d", pn);
+	/*
+	 * Framework only has insight into ports managed by ported device
+	 * used by the manager and not slave
+	 */
+	if (la != SLIM_LA_MANAGER) {
+		if (done_buf)
+			*done_buf = 0;
+		if (done_len)
+			*done_len = 0;
+		return SLIM_P_NOT_OWNED;
+	}
+	err = ctrl->port_xfer_status(ctrl, pn, done_buf, done_len);
+	if (err == SLIM_P_INPROGRESS)
+		err = ctrl->ports[pn].err;
+	return err;
+}
+EXPORT_SYMBOL(slim_port_get_xfer_status);
+
+static void slim_add_ch(struct slim_controller *ctrl, struct slim_ich *slc)
+{
+	struct slim_ich **arr;
+	int i, j;
+	int *len;
+	int sl = slc->seglen << slc->rootexp;
+	/* Channel is already active and other end is transmitting data */
+	if (slc->state >= SLIM_CH_ACTIVE)
+		return;
+	if (slc->coeff == SLIM_COEFF_1) {
+		arr = ctrl->sched.chc1;
+		len = &ctrl->sched.num_cc1;
+	} else {
+		arr = ctrl->sched.chc3;
+		len = &ctrl->sched.num_cc3;
+		sl *= 3;
+	}
+
+	*len += 1;
+
+	/* Insert the channel based on rootexp and seglen */
+	for (i = 0; i < *len - 1; i++) {
+		/*
+		 * Primary key: exp low to high.
+		 * Secondary key: seglen: high to low
+		 */
+		if ((slc->rootexp > arr[i]->rootexp) ||
+			((slc->rootexp == arr[i]->rootexp) &&
+			(slc->seglen < arr[i]->seglen)))
+			continue;
+		else
+			break;
+	}
+	for (j = *len - 1; j > i; j--)
+		arr[j] = arr[j - 1];
+	arr[i] = slc;
+	if (!ctrl->allocbw)
+		ctrl->sched.usedslots += sl;
+}
+
+static int slim_remove_ch(struct slim_controller *ctrl, struct slim_ich *slc)
+{
+	struct slim_ich **arr;
+	int i;
+	u32 la, ph;
+	int *len;
+
+	if (slc->coeff == SLIM_COEFF_1) {
+		arr = ctrl->sched.chc1;
+		len = &ctrl->sched.num_cc1;
+	} else {
+		arr = ctrl->sched.chc3;
+		len = &ctrl->sched.num_cc3;
+	}
+
+	for (i = 0; i < *len; i++) {
+		if (arr[i] == slc)
+			break;
+	}
+	if (i >= *len)
+		return -EXFULL;
+	for (; i < *len - 1; i++)
+		arr[i] = arr[i + 1];
+	*len -= 1;
+	arr[*len] = NULL;
+
+	slc->state = SLIM_CH_ALLOCATED;
+	slc->def = 0;
+	slc->newintr = 0;
+	slc->newoff = 0;
+	for (i = 0; i < slc->nsink; i++) {
+		ph = slc->sinkh[i];
+		la = SLIM_HDL_TO_LA(ph);
+		/*
+		 * For ports managed by manager's ported device, no need to send
+		 * disconnect. It is client's responsibility to call disconnect
+		 * on ports owned by the slave device
+		 */
+		if (la == SLIM_LA_MANAGER) {
+			ctrl->ports[SLIM_HDL_TO_PORT(ph)].state = SLIM_P_UNCFG;
+			ctrl->ports[SLIM_HDL_TO_PORT(ph)].ch = NULL;
+		}
+	}
+
+	ph = slc->srch;
+	la = SLIM_HDL_TO_LA(ph);
+	if (la == SLIM_LA_MANAGER) {
+		u8 pn = SLIM_HDL_TO_PORT(ph);
+
+		ctrl->ports[pn].state = SLIM_P_UNCFG;
+		ctrl->ports[pn].cfg.watermark = 0;
+		ctrl->ports[pn].cfg.port_opts = 0;
+	}
+
+	kfree(slc->sinkh);
+	slc->sinkh = NULL;
+	slc->srch = 0;
+	slc->nsink = 0;
+	return 0;
+}
+
+static u32 slim_calc_prrate(struct slim_controller *ctrl, struct slim_ch *prop)
+{
+	u32 rate = 0, rate4k = 0, rate11k = 0;
+	u32 exp = 0;
+	u32 pr = 0;
+	bool exact = true;
+	bool done = false;
+	enum slim_ch_rate ratefam;
+
+	if (prop->prot >= SLIM_ASYNC_SMPLX)
+		return 0;
+	if (prop->baser == SLIM_RATE_1HZ) {
+		rate = prop->ratem / 4000;
+		rate4k = rate;
+		if (rate * 4000 == prop->ratem)
+			ratefam = SLIM_RATE_4000HZ;
+		else {
+			rate = prop->ratem / 11025;
+			rate11k = rate;
+			if (rate * 11025 == prop->ratem)
+				ratefam = SLIM_RATE_11025HZ;
+			else
+				ratefam = SLIM_RATE_1HZ;
+		}
+	} else {
+		ratefam = prop->baser;
+		rate = prop->ratem;
+	}
+	if (ratefam == SLIM_RATE_1HZ) {
+		exact = false;
+		if ((rate4k + 1) * 4000 < (rate11k + 1) * 11025) {
+			rate = rate4k + 1;
+			ratefam = SLIM_RATE_4000HZ;
+		} else {
+			rate = rate11k + 1;
+			ratefam = SLIM_RATE_11025HZ;
+		}
+	}
+	/* covert rate to coeff-exp */
+	while (!done) {
+		while ((rate & 0x1) != 0x1) {
+			rate >>= 1;
+			exp++;
+		}
+		if (rate > 3) {
+			/* roundup if not exact */
+			rate++;
+			exact = false;
+		} else
+			done = true;
+	}
+	if (ratefam == SLIM_RATE_4000HZ) {
+		if (rate == 1)
+			pr = 0x10;
+		else {
+			pr = 0;
+			exp++;
+		}
+	} else {
+		pr = 8;
+		exp++;
+	}
+	if (exp <= 7) {
+		pr |= exp;
+		if (exact)
+			pr |= 0x80;
+	} else
+		pr = 0;
+	return pr;
+}
+
+static int slim_nextdefine_ch(struct slim_device *sb, u8 chan)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u32 chrate = 0;
+	u32 exp = 0;
+	u32 coeff = 0;
+	bool exact = true;
+	bool done = false;
+	int ret = 0;
+	struct slim_ich *slc = &ctrl->chans[chan];
+	struct slim_ch *prop = &slc->prop;
+
+	slc->prrate = slim_calc_prrate(ctrl, prop);
+	dev_dbg(&ctrl->dev, "ch:%d, chan PR rate:%x\n", chan, slc->prrate);
+	if (prop->baser == SLIM_RATE_4000HZ)
+		chrate = 4000 * prop->ratem;
+	else if (prop->baser == SLIM_RATE_11025HZ)
+		chrate = 11025 * prop->ratem;
+	else
+		chrate = prop->ratem;
+	/* max allowed sample freq = 768 seg/frame */
+	if (chrate > 3600000)
+		return -EDQUOT;
+	if (prop->baser == SLIM_RATE_4000HZ &&
+			ctrl->a_framer->superfreq == 4000)
+		coeff = prop->ratem;
+	else if (prop->baser == SLIM_RATE_11025HZ &&
+			ctrl->a_framer->superfreq == 3675)
+		coeff = 3 * prop->ratem;
+	else {
+		u32 tempr = 0;
+
+		tempr = chrate * SLIM_CL_PER_SUPERFRAME_DIV8;
+		coeff = tempr / ctrl->a_framer->rootfreq;
+		if (coeff * ctrl->a_framer->rootfreq != tempr) {
+			coeff++;
+			exact = false;
+		}
+	}
+
+	/* convert coeff to coeff-exponent */
+	exp = 0;
+	while (!done) {
+		while ((coeff & 0x1) != 0x1) {
+			coeff >>= 1;
+			exp++;
+		}
+		if (coeff > 3) {
+			coeff++;
+			exact = false;
+		} else
+			done = true;
+	}
+	if (prop->prot == SLIM_HARD_ISO && !exact)
+		return -EPROTONOSUPPORT;
+	else if (prop->prot == SLIM_AUTO_ISO) {
+		if (exact)
+			prop->prot = SLIM_HARD_ISO;
+		else
+			prop->prot = SLIM_PUSH;
+	}
+	slc->rootexp = exp;
+	slc->seglen = prop->sampleszbits/SLIM_CL_PER_SL;
+	if (prop->prot != SLIM_HARD_ISO)
+		slc->seglen++;
+	if (prop->prot >= SLIM_EXT_SMPLX)
+		slc->seglen++;
+	/* convert coeff to enum */
+	if (coeff == 1) {
+		if (exp > 9)
+			ret = -EIO;
+		coeff = SLIM_COEFF_1;
+	} else {
+		if (exp > 8)
+			ret = -EIO;
+		coeff = SLIM_COEFF_3;
+	}
+	slc->coeff = coeff;
+
+	return ret;
+}
+
+/*
+ * slim_alloc_ch: Allocate a slimbus channel and return its handle.
+ * @sb: client handle.
+ * @chanh: return channel handle
+ * Slimbus channels are limited to 256 per specification.
+ * -EXFULL is returned if all channels are in use.
+ * Although slimbus specification supports 256 channels, a controller may not
+ * support that many channels.
+ */
+int slim_alloc_ch(struct slim_device *sb, u16 *chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u16 i;
+
+	if (!ctrl)
+		return -EINVAL;
+	mutex_lock(&ctrl->sched.m_reconf);
+	for (i = 0; i < ctrl->nchans; i++) {
+		if (ctrl->chans[i].state == SLIM_CH_FREE)
+			break;
+	}
+	if (i >= ctrl->nchans) {
+		mutex_unlock(&ctrl->sched.m_reconf);
+		return -EXFULL;
+	}
+	*chanh = i;
+	ctrl->chans[i].nextgrp = 0;
+	ctrl->chans[i].state = SLIM_CH_ALLOCATED;
+	ctrl->chans[i].chan = (u8)(ctrl->reserved + i);
+
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return 0;
+}
+EXPORT_SYMBOL(slim_alloc_ch);
+
+/*
+ * slim_query_ch: Get reference-counted handle for a channel number. Every
+ * channel is reference counted by upto one as producer and the others as
+ * consumer)
+ * @sb: client handle
+ * @chan: slimbus channel number
+ * @chanh: return channel handle
+ * If request channel number is not in use, it is allocated, and reference
+ * count is set to one. If the channel was was already allocated, this API
+ * will return handle to that channel and reference count is incremented.
+ * -EXFULL is returned if all channels are in use
+ */
+int slim_query_ch(struct slim_device *sb, u8 ch, u16 *chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u16 i, j;
+	int ret = 0;
+
+	if (!ctrl || !chanh)
+		return -EINVAL;
+	mutex_lock(&ctrl->sched.m_reconf);
+	/* start with modulo number */
+	i = ch % ctrl->nchans;
+
+	for (j = 0; j < ctrl->nchans; j++) {
+		if (ctrl->chans[i].chan == ch) {
+			*chanh = i;
+			ctrl->chans[i].ref++;
+			if (ctrl->chans[i].state == SLIM_CH_FREE)
+				ctrl->chans[i].state = SLIM_CH_ALLOCATED;
+			goto query_out;
+		}
+		i = (i + 1) % ctrl->nchans;
+	}
+
+	/* Channel not in table yet */
+	ret = -EXFULL;
+	for (j = 0; j < ctrl->nchans; j++) {
+		if (ctrl->chans[i].state == SLIM_CH_FREE) {
+			ctrl->chans[i].state =
+				SLIM_CH_ALLOCATED;
+			*chanh = i;
+			ctrl->chans[i].ref++;
+			ctrl->chans[i].chan = ch;
+			ctrl->chans[i].nextgrp = 0;
+			ret = 0;
+			break;
+		}
+		i = (i + 1) % ctrl->nchans;
+	}
+query_out:
+	mutex_unlock(&ctrl->sched.m_reconf);
+	dev_dbg(&ctrl->dev, "query ch:%d,hdl:%d,ref:%d,ret:%d",
+				ch, i, ctrl->chans[i].ref, ret);
+	return ret;
+}
+EXPORT_SYMBOL(slim_query_ch);
+
+/*
+ * slim_dealloc_ch: Deallocate channel allocated using the API above
+ * -EISCONN is returned if the channel is tried to be deallocated without
+ *  being removed first.
+ *  -ENOTCONN is returned if deallocation is tried on a channel that's not
+ *  allocated.
+ */
+int slim_dealloc_ch(struct slim_device *sb, u16 chanh)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &ctrl->chans[chan];
+
+	if (!ctrl)
+		return -EINVAL;
+
+	mutex_lock(&ctrl->sched.m_reconf);
+	if (slc->state == SLIM_CH_FREE) {
+		mutex_unlock(&ctrl->sched.m_reconf);
+		return -ENOTCONN;
+	}
+	if (slc->ref > 1) {
+		slc->ref--;
+		mutex_unlock(&ctrl->sched.m_reconf);
+		dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
+					slc->chan, chanh, slc->ref);
+		return 0;
+	}
+	if (slc->state >= SLIM_CH_PENDING_ACTIVE) {
+		dev_err(&ctrl->dev, "Channel:%d should be removed first", chan);
+		mutex_unlock(&ctrl->sched.m_reconf);
+		return -EISCONN;
+	}
+	slc->ref--;
+	slc->state = SLIM_CH_FREE;
+	mutex_unlock(&ctrl->sched.m_reconf);
+	dev_dbg(&ctrl->dev, "remove chan:%d,hdl:%d,ref:%d",
+				slc->chan, chanh, slc->ref);
+	return 0;
+}
+EXPORT_SYMBOL(slim_dealloc_ch);
+
+/*
+ * slim_get_ch_state: Channel state.
+ * This API returns the channel's state (active, suspended, inactive etc)
+ */
+enum slim_ch_state slim_get_ch_state(struct slim_device *sb, u16 chanh)
+{
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	struct slim_ich *slc = &sb->ctrl->chans[chan];
+
+	return slc->state;
+}
+EXPORT_SYMBOL(slim_get_ch_state);
+
+/*
+ * slim_define_ch: Define a channel.This API defines channel parameters for a
+ *	given channel.
+ * @sb: client handle.
+ * @prop: slim_ch structure with channel parameters desired to be used.
+ * @chanh: list of channels to be defined.
+ * @nchan: number of channels in a group (1 if grp is false)
+ * @grp: Are the channels grouped
+ * @grph: return group handle if grouping of channels is desired.
+ * Channels can be grouped if multiple channels use same parameters
+ * (e.g. 5.1 audio has 6 channels with same parameters. They will all be grouped
+ * and given 1 handle for simplicity and avoid repeatedly calling the API)
+ * -EISCONN is returned if channel is already used with different parameters.
+ * -ENXIO is returned if the channel is not yet allocated.
+ */
+int slim_define_ch(struct slim_device *sb, struct slim_ch *prop, u16 *chanh,
+			u8 nchan, bool grp, u16 *grph)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int i, ret = 0;
+
+	if (!ctrl || !chanh || !prop || !nchan)
+		return -EINVAL;
+	mutex_lock(&ctrl->sched.m_reconf);
+	for (i = 0; i < nchan; i++) {
+		u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
+		struct slim_ich *slc = &ctrl->chans[chan];
+
+		dev_dbg(&ctrl->dev, "define_ch: ch:%d, state:%d", chan,
+				(int)ctrl->chans[chan].state);
+		if (slc->state < SLIM_CH_ALLOCATED) {
+			ret = -ENXIO;
+			goto err_define_ch;
+		}
+		if (slc->state >= SLIM_CH_DEFINED && slc->ref >= 2) {
+			if (prop->ratem != slc->prop.ratem ||
+			prop->sampleszbits != slc->prop.sampleszbits ||
+			prop->baser != slc->prop.baser) {
+				ret = -EISCONN;
+				goto err_define_ch;
+			}
+		} else if (slc->state > SLIM_CH_DEFINED) {
+			ret = -EISCONN;
+			goto err_define_ch;
+		} else {
+			ctrl->chans[chan].prop = *prop;
+			ret = slim_nextdefine_ch(sb, chan);
+			if (ret)
+				goto err_define_ch;
+		}
+		if (i < (nchan - 1))
+			ctrl->chans[chan].nextgrp = chanh[i + 1];
+		if (i == 0)
+			ctrl->chans[chan].nextgrp |= SLIM_START_GRP;
+		if (i == (nchan - 1))
+			ctrl->chans[chan].nextgrp |= SLIM_END_GRP;
+	}
+
+	if (grp)
+		*grph = ((nchan << 8) | SLIM_HDL_TO_CHIDX(chanh[0]));
+	for (i = 0; i < nchan; i++) {
+		u8 chan = SLIM_HDL_TO_CHIDX(chanh[i]);
+		struct slim_ich *slc = &ctrl->chans[chan];
+
+		if (slc->state == SLIM_CH_ALLOCATED)
+			slc->state = SLIM_CH_DEFINED;
+	}
+err_define_ch:
+	dev_dbg(&ctrl->dev, "define_ch: ch:%d, ret:%d", *chanh, ret);
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_define_ch);
+
+static u32 getsubfrmcoding(u32 *ctrlw, u32 *subfrml, u32 *msgsl)
+{
+	u32 code = 0;
+
+	if (*ctrlw == *subfrml) {
+		*ctrlw = 8;
+		*subfrml = 8;
+		*msgsl = SLIM_SL_PER_SUPERFRAME - SLIM_FRM_SLOTS_PER_SUPERFRAME
+				- SLIM_GDE_SLOTS_PER_SUPERFRAME;
+		return 0;
+	}
+	if (*subfrml == 6) {
+		code = 0;
+		*msgsl = 256;
+	} else if (*subfrml == 8) {
+		code = 1;
+		*msgsl = 192;
+	} else if (*subfrml == 24) {
+		code = 2;
+		*msgsl = 64;
+	} else { /* 32 */
+		code = 3;
+		*msgsl = 48;
+	}
+
+	if (*ctrlw < 8) {
+		if (*ctrlw >= 6) {
+			*ctrlw = 6;
+			code |= 0x14;
+		} else {
+			if (*ctrlw == 5)
+				*ctrlw = 4;
+			code |= (*ctrlw << 2);
+		}
+	} else {
+		code -= 2;
+		if (*ctrlw >= 24) {
+			*ctrlw = 24;
+			code |= 0x1e;
+		} else if (*ctrlw >= 16) {
+			*ctrlw = 16;
+			code |= 0x1c;
+		} else if (*ctrlw >= 12) {
+			*ctrlw = 12;
+			code |= 0x1a;
+		} else {
+			*ctrlw = 8;
+			code |= 0x18;
+		}
+	}
+
+	*msgsl = (*msgsl * *ctrlw) - SLIM_FRM_SLOTS_PER_SUPERFRAME -
+				SLIM_GDE_SLOTS_PER_SUPERFRAME;
+	return code;
+}
+
+static void shiftsegoffsets(struct slim_controller *ctrl, struct slim_ich **ach,
+				int sz, u32 shft)
+{
+	int i;
+	u32 oldoff;
+
+	for (i = 0; i < sz; i++) {
+		struct slim_ich *slc;
+
+		if (ach[i] == NULL)
+			continue;
+		slc = ach[i];
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		oldoff = slc->newoff;
+		slc->newoff += shft;
+		/* seg. offset must be <= interval */
+		if (slc->newoff >= slc->newintr)
+			slc->newoff -= slc->newintr;
+	}
+}
+
+static inline int slim_sched_4k_coeff1_chans(struct slim_controller *ctrl,
+			struct slim_ich **slc, int *coeff, int *opensl1,
+			u32 expshft, u32 curintr, u32 curmaxsl,
+			int curexp, int finalexp)
+{
+	int coeff1;
+	struct slim_ich *slc1;
+
+	if (unlikely(!coeff || !slc || !ctrl || !opensl1))
+		return -EINVAL;
+
+	coeff1 = *coeff;
+	slc1 = *slc;
+	while ((coeff1 < ctrl->sched.num_cc1) &&
+	       (curexp == (int)(slc1->rootexp + expshft))) {
+		if (slc1->state == SLIM_CH_PENDING_REMOVAL) {
+			coeff1++;
+			slc1 = ctrl->sched.chc1[coeff1];
+			continue;
+		}
+		if (opensl1[1] >= opensl1[0] ||
+		    (finalexp == (int)slc1->rootexp &&
+		     curintr <= 24 && opensl1[0] == curmaxsl)) {
+			opensl1[1] -= slc1->seglen;
+			slc1->newoff = curmaxsl + opensl1[1];
+			if (opensl1[1] < 0 && opensl1[0] == curmaxsl) {
+				opensl1[0] += opensl1[1];
+				opensl1[1] = 0;
+				if (opensl1[0] < 0) {
+					dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+					return -EXFULL;
+				}
+			}
+		} else {
+			if (slc1->seglen > opensl1[0]) {
+				dev_dbg(&ctrl->dev,
+					"reconfig failed:%d\n",	__LINE__);
+				return -EXFULL;
+			}
+			slc1->newoff = opensl1[0] - slc1->seglen;
+			opensl1[0] = slc1->newoff;
+		}
+		slc1->newintr = curintr;
+		coeff1++;
+		slc1 = ctrl->sched.chc1[coeff1];
+	}
+	*coeff = coeff1;
+	*slc = slc1;
+	return 0;
+}
+
+static int slim_sched_chans(struct slim_device *sb, u32 clkgear,
+			u32 *ctrlw, u32 *subfrml)
+{
+	int coeff1, coeff3;
+	enum slim_ch_coeff bias;
+	struct slim_controller *ctrl = sb->ctrl;
+	int last1 = ctrl->sched.num_cc1 - 1;
+	int last3 = ctrl->sched.num_cc3 - 1;
+
+	/*
+	 * Find first channels with coeff 1 & 3 as starting points for
+	 * scheduling
+	 */
+	for (coeff3 = 0; coeff3 < ctrl->sched.num_cc3; coeff3++) {
+		struct slim_ich *slc = ctrl->sched.chc3[coeff3];
+
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		else
+			break;
+	}
+	for (coeff1 = 0; coeff1 < ctrl->sched.num_cc1; coeff1++) {
+		struct slim_ich *slc = ctrl->sched.chc1[coeff1];
+
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		else
+			break;
+	}
+	if (coeff3 == ctrl->sched.num_cc3 && coeff1 == ctrl->sched.num_cc1) {
+		*ctrlw = 8;
+		*subfrml = 8;
+		return 0;
+	} else if (coeff3 == ctrl->sched.num_cc3)
+		bias = SLIM_COEFF_1;
+	else
+		bias = SLIM_COEFF_3;
+
+	/*
+	 * Find last chan in coeff1, 3 list, we will use to know when we
+	 * have done scheduling all coeff1 channels
+	 */
+	while (last1 >= 0) {
+		if (ctrl->sched.chc1[last1] != NULL &&
+			(ctrl->sched.chc1[last1])->state !=
+			SLIM_CH_PENDING_REMOVAL)
+			break;
+		last1--;
+	}
+	while (last3 >= 0) {
+		if (ctrl->sched.chc3[last3] != NULL &&
+			(ctrl->sched.chc3[last3])->state !=
+			SLIM_CH_PENDING_REMOVAL)
+			break;
+		last3--;
+	}
+
+	if (bias == SLIM_COEFF_1) {
+		struct slim_ich *slc1 = ctrl->sched.chc1[coeff1];
+		u32 expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		int curexp, finalexp;
+		u32 curintr, curmaxsl;
+		int opensl1[2];
+		int maxctrlw1;
+		int ret;
+
+		finalexp = (ctrl->sched.chc1[last1])->rootexp;
+		curexp = (int)expshft - 1;
+
+		curintr = (SLIM_MAX_INTR_COEFF_1 * 2) >> (curexp + 1);
+		curmaxsl = curintr >> 1;
+		opensl1[0] = opensl1[1] = curmaxsl;
+
+		while ((coeff1 < ctrl->sched.num_cc1) || (curintr > 24)) {
+			curintr >>= 1;
+			curmaxsl >>= 1;
+
+			/* update 4K family open slot records */
+			if (opensl1[1] < opensl1[0])
+				opensl1[1] -= curmaxsl;
+			else
+				opensl1[1] = opensl1[0] - curmaxsl;
+			opensl1[0] = curmaxsl;
+			if (opensl1[1] < 0) {
+				opensl1[0] += opensl1[1];
+				opensl1[1] = 0;
+			}
+			if (opensl1[0] <= 0) {
+				dev_dbg(&ctrl->dev, "reconfig failed:%d\n",
+					__LINE__);
+				return -EXFULL;
+			}
+			curexp++;
+			/* schedule 4k family channels */
+			ret = slim_sched_4k_coeff1_chans(ctrl, &slc1, &coeff1,
+					opensl1, expshft, curintr, curmaxsl,
+					curexp, finalexp);
+			if (ret)
+				return ret;
+		}
+		/* Leave some slots for messaging space */
+		if (opensl1[1] <= 0 && opensl1[0] <= 0)
+			return -EXFULL;
+		if (opensl1[1] > opensl1[0]) {
+			int temp = opensl1[0];
+
+			opensl1[0] = opensl1[1];
+			opensl1[1] = temp;
+			shiftsegoffsets(ctrl, ctrl->sched.chc1,
+					ctrl->sched.num_cc1, curmaxsl);
+		}
+		/* choose subframe mode to maximize bw */
+		maxctrlw1 = opensl1[0];
+		if (opensl1[0] == curmaxsl)
+			maxctrlw1 += opensl1[1];
+		if (curintr >= 24) {
+			*subfrml = 24;
+			*ctrlw = maxctrlw1;
+		} else if (curintr == 12) {
+			if (maxctrlw1 > opensl1[1] * 4) {
+				*subfrml = 24;
+				*ctrlw = maxctrlw1;
+			} else {
+				*subfrml = 6;
+				*ctrlw = opensl1[1];
+			}
+		} else {
+			*subfrml = 6;
+			*ctrlw = maxctrlw1;
+		}
+	} else {
+		struct slim_ich *slc1 = NULL;
+		struct slim_ich *slc3 = ctrl->sched.chc3[coeff3];
+		u32 expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		int curexp, finalexp, exp1;
+		u32 curintr, curmaxsl;
+		int opensl3[2];
+		int opensl1[6];
+		bool opensl1valid = false;
+		int maxctrlw1, maxctrlw3, i;
+
+		finalexp = (ctrl->sched.chc3[last3])->rootexp;
+		if (last1 >= 0) {
+			slc1 = ctrl->sched.chc1[coeff1];
+			exp1 = (ctrl->sched.chc1[last1])->rootexp;
+			if (exp1 > finalexp)
+				finalexp = exp1;
+		}
+		curexp = (int)expshft - 1;
+
+		curintr = (SLIM_MAX_INTR_COEFF_3 * 2) >> (curexp + 1);
+		curmaxsl = curintr >> 1;
+		opensl3[0] = opensl3[1] = curmaxsl;
+
+		while (coeff1 < ctrl->sched.num_cc1 ||
+			coeff3 < ctrl->sched.num_cc3 ||
+			curintr > 32) {
+			curintr >>= 1;
+			curmaxsl >>= 1;
+
+			/* update 12k family open slot records */
+			if (opensl3[1] < opensl3[0])
+				opensl3[1] -= curmaxsl;
+			else
+				opensl3[1] = opensl3[0] - curmaxsl;
+			opensl3[0] = curmaxsl;
+			if (opensl3[1] < 0) {
+				opensl3[0] += opensl3[1];
+				opensl3[1] = 0;
+			}
+			if (opensl3[0] <= 0) {
+				dev_dbg(&ctrl->dev, "reconfig failed:%d\n",
+						__LINE__);
+				return -EXFULL;
+			}
+			curexp++;
+
+			/* schedule 12k family channels */
+			while (coeff3 < ctrl->sched.num_cc3 &&
+				curexp == (int)slc3->rootexp + expshft) {
+				if (slc3->state == SLIM_CH_PENDING_REMOVAL) {
+					coeff3++;
+					slc3 = ctrl->sched.chc3[coeff3];
+					continue;
+				}
+				opensl1valid = false;
+				if (opensl3[1] >= opensl3[0] ||
+					(finalexp == (int)slc3->rootexp &&
+					 curintr <= 32 &&
+					 opensl3[0] == curmaxsl &&
+					 last1 < 0)) {
+					opensl3[1] -= slc3->seglen;
+					slc3->newoff = curmaxsl + opensl3[1];
+					if (opensl3[1] < 0 &&
+						opensl3[0] == curmaxsl) {
+						opensl3[0] += opensl3[1];
+						opensl3[1] = 0;
+					}
+					if (opensl3[0] < 0) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+				} else {
+					if (slc3->seglen > opensl3[0]) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+					slc3->newoff = opensl3[0] -
+							slc3->seglen;
+					opensl3[0] = slc3->newoff;
+				}
+				slc3->newintr = curintr;
+				coeff3++;
+				slc3 = ctrl->sched.chc3[coeff3];
+			}
+			/* update 4k openslot records */
+			if (opensl1valid == false) {
+				for (i = 0; i < 3; i++) {
+					opensl1[i * 2] = opensl3[0];
+					opensl1[(i * 2) + 1] = opensl3[1];
+				}
+			} else {
+				int opensl1p[6];
+
+				memcpy(opensl1p, opensl1, sizeof(opensl1));
+				for (i = 0; i < 3; i++) {
+					if (opensl1p[i] < opensl1p[i + 3])
+						opensl1[(i * 2) + 1] =
+							opensl1p[i];
+					else
+						opensl1[(i * 2) + 1] =
+							opensl1p[i + 3];
+				}
+				for (i = 0; i < 3; i++) {
+					opensl1[(i * 2) + 1] -= curmaxsl;
+					opensl1[i * 2] = curmaxsl;
+					if (opensl1[(i * 2) + 1] < 0) {
+						opensl1[i * 2] +=
+							opensl1[(i * 2) + 1];
+						opensl1[(i * 2) + 1] = 0;
+					}
+					if (opensl1[i * 2] < 0) {
+						dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+						return -EXFULL;
+					}
+				}
+			}
+			/* schedule 4k family channels */
+			while (coeff1 < ctrl->sched.num_cc1 &&
+				curexp == (int)slc1->rootexp + expshft) {
+				/* searchorder effective when opensl valid */
+				static const int srcho[] = { 5, 2, 4, 1, 3, 0 };
+				int maxopensl = 0;
+				int maxi = 0;
+
+				if (slc1->state == SLIM_CH_PENDING_REMOVAL) {
+					coeff1++;
+					slc1 = ctrl->sched.chc1[coeff1];
+					continue;
+				}
+				opensl1valid = true;
+				for (i = 0; i < 6; i++) {
+					if (opensl1[srcho[i]] > maxopensl) {
+						maxopensl = opensl1[srcho[i]];
+						maxi = srcho[i];
+					}
+				}
+				opensl1[maxi] -= slc1->seglen;
+				slc1->newoff = (curmaxsl * maxi) +
+						opensl1[maxi];
+				if (opensl1[maxi] < 0 && (maxi & 1) == 1 &&
+				    opensl1[maxi - 1] == curmaxsl) {
+					opensl1[maxi - 1] += opensl1[maxi];
+					if (opensl3[0] > opensl1[maxi - 1])
+						opensl3[0] = opensl1[maxi - 1];
+					opensl3[1] = 0;
+					opensl1[maxi] = 0;
+					if (opensl1[maxi - 1] < 0) {
+						dev_dbg(&ctrl->dev,
+							"reconfig failed:%d\n",
+							__LINE__);
+						return -EXFULL;
+					}
+				} else if (opensl1[maxi] < 0) {
+					dev_dbg(&ctrl->dev,
+						"reconfig failed:%d\n",
+						__LINE__);
+					return -EXFULL;
+				} else if (opensl3[maxi & 1] > opensl1[maxi]) {
+					opensl3[maxi & 1] = opensl1[maxi];
+				}
+				slc1->newintr = curintr * 3;
+				coeff1++;
+				slc1 = ctrl->sched.chc1[coeff1];
+			}
+		}
+		/* Leave some slots for messaging space */
+		if (opensl3[1] <= 0 && opensl3[0] <= 0)
+			return -EXFULL;
+		/* swap 1st and 2nd bucket if 2nd bucket has more open slots */
+		if (opensl3[1] > opensl3[0]) {
+			int temp = opensl3[0];
+
+			opensl3[0] = opensl3[1];
+			opensl3[1] = temp;
+			temp = opensl1[5];
+			opensl1[5] = opensl1[4];
+			opensl1[4] = opensl1[3];
+			opensl1[3] = opensl1[2];
+			opensl1[2] = opensl1[1];
+			opensl1[1] = opensl1[0];
+			opensl1[0] = temp;
+			shiftsegoffsets(ctrl, ctrl->sched.chc1,
+					ctrl->sched.num_cc1, curmaxsl);
+			shiftsegoffsets(ctrl, ctrl->sched.chc3,
+					ctrl->sched.num_cc3, curmaxsl);
+		}
+		/* subframe mode to maximize BW */
+		maxctrlw3 = opensl3[0];
+		maxctrlw1 = opensl1[0];
+		if (opensl3[0] == curmaxsl)
+			maxctrlw3 += opensl3[1];
+		for (i = 0; i < 5 && opensl1[i] == curmaxsl; i++)
+			maxctrlw1 += opensl1[i + 1];
+		if (curintr >= 32) {
+			*subfrml = 32;
+			*ctrlw = maxctrlw3;
+		} else if (curintr == 16) {
+			if (maxctrlw3 > (opensl3[1] * 4)) {
+				*subfrml = 32;
+				*ctrlw = maxctrlw3;
+			} else {
+				*subfrml = 8;
+				*ctrlw = opensl3[1];
+			}
+		} else {
+			if ((maxctrlw1 * 8) >= (maxctrlw3 * 24)) {
+				*subfrml = 24;
+				*ctrlw = maxctrlw1;
+			} else {
+				*subfrml = 8;
+				*ctrlw = maxctrlw3;
+			}
+		}
+	}
+	return 0;
+}
+
+#ifdef DEBUG
+static int slim_verifychansched(struct slim_controller *ctrl, u32 ctrlw,
+				u32 subfrml, u32 clkgear)
+{
+	int sl, i;
+	int cc1 = 0;
+	int cc3 = 0;
+	struct slim_ich *slc = NULL;
+
+	if (!ctrl->sched.slots)
+		return 0;
+	memset(ctrl->sched.slots, 0, SLIM_SL_PER_SUPERFRAME);
+	dev_dbg(&ctrl->dev, "Clock gear is:%d\n", clkgear);
+	for (sl = 0; sl < SLIM_SL_PER_SUPERFRAME; sl += subfrml) {
+		for (i = 0; i < ctrlw; i++)
+			ctrl->sched.slots[sl + i] = 33;
+	}
+	while (cc1 < ctrl->sched.num_cc1) {
+		slc = ctrl->sched.chc1[cc1];
+		if (slc == NULL) {
+			dev_err(&ctrl->dev, "SLC1 null in verify: chan%d\n",
+				cc1);
+			return -EIO;
+		}
+		dev_dbg(&ctrl->dev, "chan:%d, offset:%d, intr:%d, seglen:%d\n",
+				(slc - ctrl->chans), slc->newoff,
+				slc->newintr, slc->seglen);
+
+		if (slc->state != SLIM_CH_PENDING_REMOVAL) {
+			for (sl = slc->newoff;
+				sl < SLIM_SL_PER_SUPERFRAME;
+				sl += slc->newintr) {
+				for (i = 0; i < slc->seglen; i++) {
+					if (ctrl->sched.slots[sl + i])
+						return -EXFULL;
+					ctrl->sched.slots[sl + i] = cc1 + 1;
+				}
+			}
+		}
+		cc1++;
+	}
+	while (cc3 < ctrl->sched.num_cc3) {
+		slc = ctrl->sched.chc3[cc3];
+		if (slc == NULL) {
+			dev_err(&ctrl->dev, "SLC3 null in verify: chan%d\n",
+				cc3);
+			return -EIO;
+		}
+		dev_dbg(&ctrl->dev, "chan:%d, offset:%d, intr:%d, seglen:%d\n",
+				(slc - ctrl->chans), slc->newoff,
+				slc->newintr, slc->seglen);
+		if (slc->state != SLIM_CH_PENDING_REMOVAL) {
+			for (sl = slc->newoff;
+				sl < SLIM_SL_PER_SUPERFRAME;
+				sl += slc->newintr) {
+				for (i = 0; i < slc->seglen; i++) {
+					if (ctrl->sched.slots[sl + i])
+						return -EXFULL;
+					ctrl->sched.slots[sl + i] = cc3 + 1;
+				}
+			}
+		}
+		cc3++;
+	}
+
+	return 0;
+}
+#else
+static int slim_verifychansched(struct slim_controller *ctrl, u32 ctrlw,
+				u32 subfrml, u32 clkgear)
+{
+	return 0;
+}
+#endif
+
+static void slim_sort_chan_grp(struct slim_controller *ctrl,
+				struct slim_ich *slc)
+{
+	u8  last = (u8)-1;
+	u8 second = 0;
+
+	for (; last > 0; last--) {
+		struct slim_ich *slc1 = slc;
+		struct slim_ich *slc2;
+		u8 next = SLIM_HDL_TO_CHIDX(slc1->nextgrp);
+
+		slc2 = &ctrl->chans[next];
+		for (second = 1; second <= last && slc2 &&
+			(slc2->state == SLIM_CH_ACTIVE ||
+			 slc2->state == SLIM_CH_PENDING_ACTIVE); second++) {
+			if (slc1->newoff > slc2->newoff) {
+				u32 temp = slc2->newoff;
+
+				slc2->newoff = slc1->newoff;
+				slc1->newoff = temp;
+			}
+			if (slc2->nextgrp & SLIM_END_GRP) {
+				last = second;
+				break;
+			}
+			slc1 = slc2;
+			next = SLIM_HDL_TO_CHIDX(slc1->nextgrp);
+			slc2 = &ctrl->chans[next];
+		}
+		if (slc2 == NULL)
+			last = second - 1;
+	}
+}
+
+
+static int slim_allocbw(struct slim_device *sb, int *subfrmc, int *clkgear)
+{
+	u32 msgsl = 0;
+	u32 ctrlw = 0;
+	u32 subfrml = 0;
+	int ret = -EIO;
+	struct slim_controller *ctrl = sb->ctrl;
+	u32 usedsl = ctrl->sched.usedslots + ctrl->sched.pending_msgsl;
+	u32 availsl = SLIM_SL_PER_SUPERFRAME - SLIM_FRM_SLOTS_PER_SUPERFRAME -
+			SLIM_GDE_SLOTS_PER_SUPERFRAME;
+	*clkgear = SLIM_MAX_CLK_GEAR;
+
+	dev_dbg(&ctrl->dev, "used sl:%u, availlable sl:%u\n", usedsl, availsl);
+	dev_dbg(&ctrl->dev, "pending:chan sl:%u, :msg sl:%u, clkgear:%u\n",
+				ctrl->sched.usedslots,
+				ctrl->sched.pending_msgsl, *clkgear);
+	/*
+	 * If number of slots are 0, that means channels are inactive.
+	 * It is very likely that the manager will call clock pause very soon.
+	 * By making sure that bus is in MAX_GEAR, clk pause sequence will take
+	 * minimum amount of time.
+	 */
+	if (ctrl->sched.usedslots != 0) {
+		while ((usedsl * 2 <= availsl) && (*clkgear > ctrl->min_cg)) {
+			*clkgear -= 1;
+			usedsl *= 2;
+		}
+	}
+
+	/*
+	 * Try scheduling data channels at current clock gear, if all channels
+	 * can be scheduled, or reserved BW can't be satisfied, increase clock
+	 * gear and try again
+	 */
+	for (; *clkgear <= ctrl->max_cg; (*clkgear)++) {
+		ret = slim_sched_chans(sb, *clkgear, &ctrlw, &subfrml);
+
+		if (ret == 0) {
+			*subfrmc = getsubfrmcoding(&ctrlw, &subfrml, &msgsl);
+			if ((msgsl >> (ctrl->max_cg - *clkgear) <
+				ctrl->sched.pending_msgsl) &&
+				(*clkgear < ctrl->max_cg))
+				continue;
+			else
+				break;
+		}
+	}
+	if (ret == 0) {
+		int i;
+		/* Sort channel-groups */
+		for (i = 0; i < ctrl->sched.num_cc1; i++) {
+			struct slim_ich *slc = ctrl->sched.chc1[i];
+
+			if (slc->state == SLIM_CH_PENDING_REMOVAL)
+				continue;
+			if ((slc->nextgrp & SLIM_START_GRP) &&
+				!(slc->nextgrp & SLIM_END_GRP)) {
+				slim_sort_chan_grp(ctrl, slc);
+			}
+		}
+		for (i = 0; i < ctrl->sched.num_cc3; i++) {
+			struct slim_ich *slc = ctrl->sched.chc3[i];
+
+			if (slc->state == SLIM_CH_PENDING_REMOVAL)
+				continue;
+			if ((slc->nextgrp & SLIM_START_GRP) &&
+				!(slc->nextgrp & SLIM_END_GRP)) {
+				slim_sort_chan_grp(ctrl, slc);
+			}
+		}
+
+		ret = slim_verifychansched(ctrl, ctrlw, subfrml, *clkgear);
+	}
+
+	return ret;
+}
+
+static void slim_change_existing_chans(struct slim_controller *ctrl, int coeff)
+{
+	struct slim_ich **arr;
+	int len, i;
+
+	if (coeff == SLIM_COEFF_1) {
+		arr = ctrl->sched.chc1;
+		len = ctrl->sched.num_cc1;
+	} else {
+		arr = ctrl->sched.chc3;
+		len = ctrl->sched.num_cc3;
+	}
+	for (i = 0; i < len; i++) {
+		struct slim_ich *slc = arr[i];
+
+		if (slc->state == SLIM_CH_ACTIVE ||
+			slc->state == SLIM_CH_SUSPENDED)
+			slc->offset = slc->newoff;
+			slc->interval = slc->newintr;
+	}
+}
+static void slim_chan_changes(struct slim_device *sb, bool revert)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+
+	while (!list_empty(&sb->mark_define)) {
+		struct slim_ich *slc;
+		struct slim_pending_ch *pch =
+				list_entry(sb->mark_define.next,
+					struct slim_pending_ch, pending);
+		slc = &ctrl->chans[pch->chan];
+		if (revert) {
+			if (slc->state == SLIM_CH_PENDING_ACTIVE) {
+				u32 sl = slc->seglen << slc->rootexp;
+
+				if (slc->coeff == SLIM_COEFF_3)
+					sl *= 3;
+				if (!ctrl->allocbw)
+					ctrl->sched.usedslots -= sl;
+				slim_remove_ch(ctrl, slc);
+				slc->state = SLIM_CH_DEFINED;
+			}
+		} else {
+			slc->state = SLIM_CH_ACTIVE;
+			slc->def++;
+		}
+		list_del_init(&pch->pending);
+		kfree(pch);
+	}
+
+	while (!list_empty(&sb->mark_removal)) {
+		struct slim_pending_ch *pch =
+				list_entry(sb->mark_removal.next,
+					struct slim_pending_ch, pending);
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		u32 sl = slc->seglen << slc->rootexp;
+
+		if (revert || slc->def > 0) {
+			if (slc->coeff == SLIM_COEFF_3)
+				sl *= 3;
+			if (!ctrl->allocbw)
+				ctrl->sched.usedslots += sl;
+			if (revert)
+				slc->def++;
+			slc->state = SLIM_CH_ACTIVE;
+		} else
+			slim_remove_ch(ctrl, slc);
+		list_del_init(&pch->pending);
+		kfree(pch);
+	}
+
+	while (!list_empty(&sb->mark_suspend)) {
+		struct slim_pending_ch *pch =
+				list_entry(sb->mark_suspend.next,
+					struct slim_pending_ch, pending);
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+
+		if (revert)
+			slc->state = SLIM_CH_ACTIVE;
+		list_del_init(&pch->pending);
+		kfree(pch);
+	}
+	/* Change already active channel if reconfig succeeded */
+	if (!revert) {
+		slim_change_existing_chans(ctrl, SLIM_COEFF_1);
+		slim_change_existing_chans(ctrl, SLIM_COEFF_3);
+	}
+}
+
+/*
+ * slim_reconfigure_now: Request reconfiguration now.
+ * @sb: client handle
+ * This API does what commit flag in other scheduling APIs do.
+ * -EXFULL is returned if there is no space in TDM to reserve the
+ * bandwidth. -EBUSY is returned if reconfiguration request is already in
+ * progress.
+ */
+int slim_reconfigure_now(struct slim_device *sb)
+{
+	u8 i;
+	u8 wbuf[4];
+	u32 clkgear, subframe;
+	u32 curexp;
+	int ret;
+	struct slim_controller *ctrl = sb->ctrl;
+	u32 expshft;
+	u32 segdist;
+	struct slim_pending_ch *pch;
+	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_MC_BEGIN_RECONFIGURATION, 0, 3,
+				NULL, NULL, sb->laddr);
+
+	mutex_lock(&ctrl->sched.m_reconf);
+	/*
+	 * If there are no pending changes from this client, avoid sending
+	 * the reconfiguration sequence
+	 */
+	if (sb->pending_msgsl == sb->cur_msgsl &&
+		list_empty(&sb->mark_define) &&
+		list_empty(&sb->mark_suspend)) {
+		struct list_head *pos, *next;
+
+		list_for_each_safe(pos, next, &sb->mark_removal) {
+			struct slim_ich *slc;
+
+			pch = list_entry(pos, struct slim_pending_ch, pending);
+			slc = &ctrl->chans[pch->chan];
+			if (slc->def > 0)
+				slc->def--;
+			/* Disconnect source port to free it up */
+			if (SLIM_HDL_TO_LA(slc->srch) == sb->laddr)
+				slc->srch = 0;
+			/*
+			 * If controller overrides BW allocation,
+			 * delete this in remove channel itself
+			 */
+			if (slc->def != 0 && !ctrl->allocbw) {
+				list_del(&pch->pending);
+				kfree(pch);
+			}
+		}
+		if (list_empty(&sb->mark_removal)) {
+			mutex_unlock(&ctrl->sched.m_reconf);
+			pr_info("SLIM_CL: skip reconfig sequence");
+			return 0;
+		}
+	}
+
+	ctrl->sched.pending_msgsl += sb->pending_msgsl - sb->cur_msgsl;
+	list_for_each_entry(pch, &sb->mark_define, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+
+		slim_add_ch(ctrl, slc);
+		if (slc->state < SLIM_CH_ACTIVE)
+			slc->state = SLIM_CH_PENDING_ACTIVE;
+	}
+
+	list_for_each_entry(pch, &sb->mark_removal, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		u32 sl = slc->seglen << slc->rootexp;
+
+		if (slc->coeff == SLIM_COEFF_3)
+			sl *= 3;
+		if (!ctrl->allocbw)
+			ctrl->sched.usedslots -= sl;
+		slc->state = SLIM_CH_PENDING_REMOVAL;
+	}
+	list_for_each_entry(pch, &sb->mark_suspend, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+
+		slc->state = SLIM_CH_SUSPENDED;
+	}
+
+	/*
+	 * Controller can override default channel scheduling algorithm.
+	 * (e.g. if controller needs to use fixed channel scheduling based
+	 * on number of channels)
+	 */
+	if (ctrl->allocbw)
+		ret = ctrl->allocbw(sb, &subframe, &clkgear);
+	else
+		ret = slim_allocbw(sb, &subframe, &clkgear);
+
+	if (!ret) {
+		ret = slim_processtxn(ctrl, &txn, false);
+		dev_dbg(&ctrl->dev, "sending begin_reconfig:ret:%d\n", ret);
+	}
+
+	if (!ret && subframe != ctrl->sched.subfrmcode) {
+		wbuf[0] = (u8)(subframe & 0xFF);
+		txn.mc = SLIM_MSG_MC_NEXT_SUBFRAME_MODE;
+		txn.len = 1;
+		txn.rl = 4;
+		txn.wbuf = wbuf;
+		ret = slim_processtxn(ctrl, &txn, false);
+		dev_dbg(&ctrl->dev, "sending subframe:%d,ret:%d\n",
+				(int)wbuf[0], ret);
+	}
+	if (!ret && clkgear != ctrl->clkgear) {
+		wbuf[0] = (u8)(clkgear & 0xFF);
+		txn.mc = SLIM_MSG_MC_NEXT_CLOCK_GEAR;
+		txn.len = 1;
+		txn.rl = 4;
+		txn.wbuf = wbuf;
+		ret = slim_processtxn(ctrl, &txn, false);
+		dev_dbg(&ctrl->dev, "sending clkgear:%d,ret:%d\n",
+				(int)wbuf[0], ret);
+	}
+	if (ret)
+		goto revert_reconfig;
+
+	expshft = SLIM_MAX_CLK_GEAR - clkgear;
+	/* activate/remove channel */
+	list_for_each_entry(pch, &sb->mark_define, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+		/* Define content */
+		wbuf[0] = slc->chan;
+		wbuf[1] = slc->prrate;
+		wbuf[2] = slc->prop.dataf | (slc->prop.auxf << 4);
+		wbuf[3] = slc->prop.sampleszbits / SLIM_CL_PER_SL;
+		txn.mc = SLIM_MSG_MC_NEXT_DEFINE_CONTENT;
+		txn.len = 4;
+		txn.rl = 7;
+		txn.wbuf = wbuf;
+		dev_dbg(&ctrl->dev, "define content, activate:%x, %x, %x, %x\n",
+				wbuf[0], wbuf[1], wbuf[2], wbuf[3]);
+		/* Right now, channel link bit is not supported */
+		ret = slim_processtxn(ctrl, &txn, false);
+		if (ret)
+			goto revert_reconfig;
+
+		txn.mc = SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL;
+		txn.len = 1;
+		txn.rl = 4;
+		ret = slim_processtxn(ctrl, &txn, false);
+		if (ret)
+			goto revert_reconfig;
+	}
+
+	list_for_each_entry(pch, &sb->mark_removal, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+
+		dev_dbg(&ctrl->dev, "remove chan:%x\n", pch->chan);
+		wbuf[0] = slc->chan;
+		txn.mc = SLIM_MSG_MC_NEXT_REMOVE_CHANNEL;
+		txn.len = 1;
+		txn.rl = 4;
+		txn.wbuf = wbuf;
+		ret = slim_processtxn(ctrl, &txn, false);
+		if (ret)
+			goto revert_reconfig;
+	}
+	list_for_each_entry(pch, &sb->mark_suspend, pending) {
+		struct slim_ich *slc = &ctrl->chans[pch->chan];
+
+		dev_dbg(&ctrl->dev, "suspend chan:%x\n", pch->chan);
+		wbuf[0] = slc->chan;
+		txn.mc = SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL;
+		txn.len = 1;
+		txn.rl = 4;
+		txn.wbuf = wbuf;
+		ret = slim_processtxn(ctrl, &txn, false);
+		if (ret)
+			goto revert_reconfig;
+	}
+
+	/* Define CC1 channel */
+	for (i = 0; i < ctrl->sched.num_cc1; i++) {
+		struct slim_ich *slc = ctrl->sched.chc1[i];
+
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		curexp = slc->rootexp + expshft;
+		segdist = (slc->newoff << curexp) & 0x1FF;
+		expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		dev_dbg(&ctrl->dev, "new-intr:%d, old-intr:%d, dist:%d\n",
+				slc->newintr, slc->interval, segdist);
+		dev_dbg(&ctrl->dev, "new-off:%d, old-off:%d\n",
+				slc->newoff, slc->offset);
+
+		if (slc->state < SLIM_CH_ACTIVE || slc->def < slc->ref ||
+			slc->newintr != slc->interval ||
+			slc->newoff != slc->offset) {
+			segdist |= 0x200;
+			segdist >>= curexp;
+			segdist |= (slc->newoff << (curexp + 1)) & 0xC00;
+			wbuf[0] = slc->chan;
+			wbuf[1] = (u8)(segdist & 0xFF);
+			wbuf[2] = (u8)((segdist & 0xF00) >> 8) |
+					(slc->prop.prot << 4);
+			wbuf[3] = slc->seglen;
+			txn.mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
+			txn.len = 4;
+			txn.rl = 7;
+			txn.wbuf = wbuf;
+			ret = slim_processtxn(ctrl, &txn, false);
+			if (ret)
+				goto revert_reconfig;
+		}
+	}
+
+	/* Define CC3 channels */
+	for (i = 0; i < ctrl->sched.num_cc3; i++) {
+		struct slim_ich *slc = ctrl->sched.chc3[i];
+
+		if (slc->state == SLIM_CH_PENDING_REMOVAL)
+			continue;
+		curexp = slc->rootexp + expshft;
+		segdist = (slc->newoff << curexp) & 0x1FF;
+		expshft = SLIM_MAX_CLK_GEAR - clkgear;
+		dev_dbg(&ctrl->dev, "new-intr:%d, old-intr:%d, dist:%d\n",
+				slc->newintr, slc->interval, segdist);
+		dev_dbg(&ctrl->dev, "new-off:%d, old-off:%d\n",
+				slc->newoff, slc->offset);
+
+		if (slc->state < SLIM_CH_ACTIVE || slc->def < slc->ref ||
+			slc->newintr != slc->interval ||
+			slc->newoff != slc->offset) {
+			segdist |= 0x200;
+			segdist >>= curexp;
+			segdist |= 0xC00;
+			wbuf[0] = slc->chan;
+			wbuf[1] = (u8)(segdist & 0xFF);
+			wbuf[2] = (u8)((segdist & 0xF00) >> 8) |
+					(slc->prop.prot << 4);
+			wbuf[3] = (u8)(slc->seglen);
+			txn.mc = SLIM_MSG_MC_NEXT_DEFINE_CHANNEL;
+			txn.len = 4;
+			txn.rl = 7;
+			txn.wbuf = wbuf;
+			ret = slim_processtxn(ctrl, &txn, false);
+			if (ret)
+				goto revert_reconfig;
+		}
+	}
+	txn.mc = SLIM_MSG_MC_RECONFIGURE_NOW;
+	txn.len = 0;
+	txn.rl = 3;
+	txn.wbuf = NULL;
+	ret = slim_processtxn(ctrl, &txn, false);
+	dev_dbg(&ctrl->dev, "reconfig now:ret:%d\n", ret);
+	if (!ret) {
+		ctrl->sched.subfrmcode = subframe;
+		ctrl->clkgear = clkgear;
+		ctrl->sched.msgsl = ctrl->sched.pending_msgsl;
+		sb->cur_msgsl = sb->pending_msgsl;
+		slim_chan_changes(sb, false);
+		mutex_unlock(&ctrl->sched.m_reconf);
+		return 0;
+	}
+
+revert_reconfig:
+	/* Revert channel changes */
+	slim_chan_changes(sb, true);
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_reconfigure_now);
+
+static int add_pending_ch(struct list_head *listh, u8 chan)
+{
+	struct slim_pending_ch *pch;
+
+	pch = kmalloc(sizeof(struct slim_pending_ch), GFP_KERNEL);
+	if (!pch)
+		return -ENOMEM;
+	pch->chan = chan;
+	list_add_tail(&pch->pending, listh);
+	return 0;
+}
+
+/*
+ * slim_control_ch: Channel control API.
+ * @sb: client handle
+ * @chanh: group or channel handle to be controlled
+ * @chctrl: Control command (activate/suspend/remove)
+ * @commit: flag to indicate whether the control should take effect right-away.
+ * This API activates, removes or suspends a channel (or group of channels)
+ * chanh indicates the channel or group handle (returned by the define_ch API).
+ * Reconfiguration may be time-consuming since it can change all other active
+ * channel allocations on the bus, change in clock gear used by the slimbus,
+ * and change in the control space width used for messaging.
+ * commit makes sure that multiple channels can be activated/deactivated before
+ * reconfiguration is started.
+ * -EXFULL is returned if there is no space in TDM to reserve the bandwidth.
+ * -EISCONN/-ENOTCONN is returned if the channel is already connected or not
+ * yet defined.
+ * -EINVAL is returned if individual control of a grouped-channel is attempted.
+ */
+int slim_control_ch(struct slim_device *sb, u16 chanh,
+			enum slim_ch_control chctrl, bool commit)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int ret = 0;
+	/* Get rid of the group flag in MSB if any */
+	u8 chan = SLIM_HDL_TO_CHIDX(chanh);
+	u8 nchan = 0;
+	struct slim_ich *slc = &ctrl->chans[chan];
+
+	if (!(slc->nextgrp & SLIM_START_GRP))
+		return -EINVAL;
+
+	mutex_lock(&sb->sldev_reconf);
+	mutex_lock(&ctrl->sched.m_reconf);
+	do {
+		struct slim_pending_ch *pch;
+		u8 add_mark_removal  = true;
+
+		slc = &ctrl->chans[chan];
+		dev_dbg(&ctrl->dev, "chan:%d,ctrl:%d,def:%d", chan, chctrl,
+					slc->def);
+		if (slc->state < SLIM_CH_DEFINED) {
+			ret = -ENOTCONN;
+			break;
+		}
+		if (chctrl == SLIM_CH_SUSPEND) {
+			ret = add_pending_ch(&sb->mark_suspend, chan);
+			if (ret)
+				break;
+		} else if (chctrl == SLIM_CH_ACTIVATE) {
+			if (slc->state > SLIM_CH_ACTIVE) {
+				ret = -EISCONN;
+				break;
+			}
+			ret = add_pending_ch(&sb->mark_define, chan);
+			if (ret)
+				break;
+		} else {
+			if (slc->state < SLIM_CH_ACTIVE) {
+				ret = -ENOTCONN;
+				break;
+			}
+			/* If channel removal request comes when pending
+			 * in the mark_define, remove it from the define
+			 * list instead of adding it to removal list
+			 */
+			if (!list_empty(&sb->mark_define)) {
+				struct list_head *pos, *next;
+
+				list_for_each_safe(pos, next,
+						  &sb->mark_define) {
+					pch = list_entry(pos,
+						struct slim_pending_ch,
+						pending);
+					if (pch->chan == chan) {
+						list_del(&pch->pending);
+						kfree(pch);
+						add_mark_removal = false;
+						break;
+					}
+				}
+			}
+			if (add_mark_removal == true) {
+				ret = add_pending_ch(&sb->mark_removal, chan);
+				if (ret)
+					break;
+			}
+		}
+
+		nchan++;
+		if (nchan < SLIM_GRP_TO_NCHAN(chanh))
+			chan = SLIM_HDL_TO_CHIDX(slc->nextgrp);
+	} while (nchan < SLIM_GRP_TO_NCHAN(chanh));
+	mutex_unlock(&ctrl->sched.m_reconf);
+	if (!ret && commit == true)
+		ret = slim_reconfigure_now(sb);
+	mutex_unlock(&sb->sldev_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_control_ch);
+
+/*
+ * slim_reservemsg_bw: Request to reserve bandwidth for messages.
+ * @sb: client handle
+ * @bw_bps: message bandwidth in bits per second to be requested
+ * @commit: indicates whether the reconfiguration needs to be acted upon.
+ * This API call can be grouped with slim_control_ch API call with only one of
+ * the APIs specifying the commit flag to avoid reconfiguration being called too
+ * frequently. -EXFULL is returned if there is no space in TDM to reserve the
+ * bandwidth. -EBUSY is returned if reconfiguration is requested, but a request
+ * is already in progress.
+ */
+int slim_reservemsg_bw(struct slim_device *sb, u32 bw_bps, bool commit)
+{
+	struct slim_controller *ctrl = sb->ctrl;
+	int ret = 0;
+	int sl;
+
+	mutex_lock(&sb->sldev_reconf);
+	if ((bw_bps >> 3) >= ctrl->a_framer->rootfreq)
+		sl = SLIM_SL_PER_SUPERFRAME;
+	else {
+		sl = (bw_bps * (SLIM_CL_PER_SUPERFRAME_DIV8/SLIM_CL_PER_SL/2) +
+			(ctrl->a_framer->rootfreq/2 - 1)) /
+			(ctrl->a_framer->rootfreq/2);
+	}
+	dev_dbg(&ctrl->dev, "request:bw:%d, slots:%d, current:%d\n", bw_bps, sl,
+						sb->cur_msgsl);
+	sb->pending_msgsl = sl;
+	if (commit == true)
+		ret = slim_reconfigure_now(sb);
+	mutex_unlock(&sb->sldev_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_reservemsg_bw);
+
+/*
+ * slim_ctrl_clk_pause: Called by slimbus controller to request clock to be
+ *	paused or woken up out of clock pause
+ * or woken up from clock pause
+ * @ctrl: controller requesting bus to be paused or woken up
+ * @wakeup: Wakeup this controller from clock pause.
+ * @restart: Restart time value per spec used for clock pause. This value
+ *	isn't used when controller is to be woken up.
+ * This API executes clock pause reconfiguration sequence if wakeup is false.
+ * If wakeup is true, controller's wakeup is called
+ * Slimbus clock is idle and can be disabled by the controller later.
+ */
+int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup, u8 restart)
+{
+	int ret = 0;
+	int i;
+	DEFINE_SLIM_BCAST_TXN(txn, SLIM_MSG_CLK_PAUSE_SEQ_FLG |
+				SLIM_MSG_MC_BEGIN_RECONFIGURATION, 0, 3,
+				NULL, NULL, 0);
+
+	if (wakeup == false && restart > SLIM_CLK_UNSPECIFIED)
+		return -EINVAL;
+	mutex_lock(&ctrl->m_ctrl);
+	if (wakeup) {
+		if (ctrl->clk_state == SLIM_CLK_ACTIVE) {
+			mutex_unlock(&ctrl->m_ctrl);
+			return 0;
+		}
+		wait_for_completion(&ctrl->pause_comp);
+		/*
+		 * Slimbus framework will call controller wakeup
+		 * Controller should make sure that it sets active framer
+		 * out of clock pause by doing appropriate setting
+		 */
+		if (ctrl->clk_state == SLIM_CLK_PAUSED && ctrl->wakeup)
+			ret = ctrl->wakeup(ctrl);
+		/*
+		 * If wakeup fails, make sure that next attempt can succeed.
+		 * Since we already consumed pause_comp, complete it so
+		 * that next wakeup isn't blocked forever
+		 */
+		if (!ret)
+			ctrl->clk_state = SLIM_CLK_ACTIVE;
+		else
+			complete(&ctrl->pause_comp);
+		mutex_unlock(&ctrl->m_ctrl);
+		return ret;
+	}
+
+	switch (ctrl->clk_state) {
+	case SLIM_CLK_ENTERING_PAUSE:
+	case SLIM_CLK_PAUSE_FAILED:
+		/*
+		 * If controller is already trying to enter clock pause,
+		 * let it finish.
+		 * In case of error, retry
+		 * In both cases, previous clock pause has signalled
+		 * completion.
+		 */
+		wait_for_completion(&ctrl->pause_comp);
+		/* retry upon failure */
+		if (ctrl->clk_state == SLIM_CLK_PAUSE_FAILED) {
+			ctrl->clk_state = SLIM_CLK_ACTIVE;
+		} else {
+			mutex_unlock(&ctrl->m_ctrl);
+			/*
+			 * Signal completion so that wakeup can wait on
+			 * it.
+			 */
+			complete(&ctrl->pause_comp);
+			return 0;
+		}
+		break;
+	case SLIM_CLK_PAUSED:
+		/* already paused */
+		mutex_unlock(&ctrl->m_ctrl);
+		return 0;
+	case SLIM_CLK_ACTIVE:
+	default:
+		break;
+	}
+	/* Pending response for a message */
+	for (i = 0; i < ctrl->last_tid; i++) {
+		if (ctrl->txnt[i]) {
+			ret = -EBUSY;
+			pr_info("slim_clk_pause: txn-rsp for %d pending", i);
+			mutex_unlock(&ctrl->m_ctrl);
+			return -EBUSY;
+		}
+	}
+	ctrl->clk_state = SLIM_CLK_ENTERING_PAUSE;
+	mutex_unlock(&ctrl->m_ctrl);
+
+	mutex_lock(&ctrl->sched.m_reconf);
+	/* Data channels active */
+	if (ctrl->sched.usedslots) {
+		pr_info("slim_clk_pause: data channel active");
+		ret = -EBUSY;
+		goto clk_pause_ret;
+	}
+
+	ret = slim_processtxn(ctrl, &txn, false);
+	if (ret)
+		goto clk_pause_ret;
+
+	txn.mc = SLIM_MSG_CLK_PAUSE_SEQ_FLG | SLIM_MSG_MC_NEXT_PAUSE_CLOCK;
+	txn.len = 1;
+	txn.rl = 4;
+	txn.wbuf = &restart;
+	ret = slim_processtxn(ctrl, &txn, false);
+	if (ret)
+		goto clk_pause_ret;
+
+	txn.mc = SLIM_MSG_CLK_PAUSE_SEQ_FLG | SLIM_MSG_MC_RECONFIGURE_NOW;
+	txn.len = 0;
+	txn.rl = 3;
+	txn.wbuf = NULL;
+	ret = slim_processtxn(ctrl, &txn, false);
+	if (ret)
+		goto clk_pause_ret;
+
+clk_pause_ret:
+	if (ret)
+		ctrl->clk_state = SLIM_CLK_PAUSE_FAILED;
+	else
+		ctrl->clk_state = SLIM_CLK_PAUSED;
+	complete(&ctrl->pause_comp);
+	mutex_unlock(&ctrl->sched.m_reconf);
+	return ret;
+}
+EXPORT_SYMBOL(slim_ctrl_clk_pause);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Slimbus module");
+MODULE_ALIAS("platform:slimbus");
diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig
index 6c27099..2829a02 100644
--- a/drivers/soc/qcom/Kconfig
+++ b/drivers/soc/qcom/Kconfig
@@ -247,6 +247,14 @@
 	 use this memory and no unauthorized access is made to the
 	 buffer
 
+config QCOM_EARLY_RANDOM
+        bool "Initialize random pool very early"
+        help
+          The standard random pool may not initialize until late in the boot
+          process which means that any calls to get random numbers before then
+          may not be truly random. Select this option to make an early call
+          to get some random data to put in the pool. If unsure, say N.
+
 config MSM_SMEM
 	depends on ARCH_QCOM
 	depends on REMOTE_SPINLOCK_MSM
@@ -294,6 +302,19 @@
 	  allows for G-Link communication with remote subsystems that are
 	  external to the System-on-Chip.
 
+config MSM_SPCOM
+	depends on MSM_GLINK
+	bool "Secure Processor Communication over GLINK"
+	help
+	  spcom driver allows loading Secure Processor Applications and
+	  sending messages to Secure Processor Applications.
+	  spcom provides interface to both user space app and kernel driver.
+	  It is using glink as the transport layer, which provides multiple
+	  logical channels over single physical channel.
+	  The physical layer is based on shared memory and interrupts.
+	  spcom provides clients/server API, although currently only one client
+	  or server is allowed per logical channel.
+
 config TRACER_PKT
 	bool "Tracer Packet"
 	help
@@ -476,18 +497,18 @@
 	  Command DB queries shared memory by key string for shared system
 	  resources
 
-config MSM_QDSP6_APRV2
-        bool "Audio QDSP6 APRv2 support"
-        depends on MSM_SMD
+config MSM_QDSP6_APRV2_GLINK
+        bool "Audio QDSP6 APRv2 Over Glink support"
+	depends on MSM_GLINK
         help
           Enable APRv2 IPC protocol support between
           application processor and QDSP6. APR is
           used by audio driver to configure QDSP6's
           ASM, ADM and AFE.
 
-config MSM_QDSP6_APRV3
-	bool "Audio QDSP6 APRv3 support"
-	depends on MSM_SMD
+config MSM_QDSP6_APRV3_GLINK
+	bool "Audio QDSP6 APRv3 Over Glink support"
+	depends on MSM_GLINK
 	help
 	  Enable APRv3 IPC protocol support between
 	  application processor and QDSP6. APR is
@@ -497,7 +518,7 @@
 config MSM_ADSP_LOADER
 	tristate "ADSP loader support"
 	select SND_SOC_MSM_APRV2_INTF
-	depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3
+	depends on MSM_QDSP6_APRV2_GLINK || MSM_QDSP6_APRV3_GLINK
 	help
 	  Enable ADSP image loader.
 	  The ADSP loader brings ADSP out of reset
@@ -506,7 +527,16 @@
 
 config MSM_AVTIMER
 	tristate "Avtimer Driver"
-	depends on MSM_QDSP6_APRV2 || MSM_QDSP6_APRV3
+	depends on MSM_QDSP6_APRV2_GLINK || MSM_QDSP6_APRV3_GLINK
 	help
 		This driver gets the Q6 out of power collapsed state and
 		exposes ioctl control to read avtimer tick.
+
+config WCD_DSP_GLINK
+	tristate "WCD DSP GLINK Driver"
+	depends on MSM_GLINK
+	default y if SND_SOC_WCD934X=y
+	help
+	   This option enables driver which provides communication interface
+	   between MSM and WCD DSP over glink transport protocol. This driver
+	   provides read and write interface via char device.
diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile
index 85342d1..1700319 100644
--- a/drivers/soc/qcom/Makefile
+++ b/drivers/soc/qcom/Makefile
@@ -13,6 +13,7 @@
 obj-$(CONFIG_QCOM_WCNSS_CTRL) += wcnss_ctrl.o
 CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
 obj-$(CONFIG_QCOM_SCM)  +=      scm.o scm-boot.o
+obj-$(CONFIG_QCOM_EARLY_RANDOM)	+= early_random.o
 obj-$(CONFIG_SOC_BUS) += socinfo.o
 obj-$(CONFIG_MSM_BOOT_STATS) += boot_stats.o
 obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o
@@ -28,6 +29,7 @@
 obj-$(CONFIG_MSM_GLINK_LOOPBACK_SERVER) += glink_loopback_server.o
 obj-$(CONFIG_MSM_GLINK_SMEM_NATIVE_XPRT) += glink_smem_native_xprt.o
 obj-$(CONFIG_MSM_GLINK_SPI_XPRT) += glink_spi_xprt.o
+obj-$(CONFIG_MSM_SPCOM) += spcom.o
 obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o
 obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
 obj-$(CONFIG_QTI_RPMH_API) += rpmh.o
@@ -42,6 +44,7 @@
 obj-$(CONFIG_MSM_IPC_ROUTER_GLINK_XPRT) += ipc_router_glink_xprt.o
 obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o
 obj-$(CONFIG_MSM_GLINK_PKT) += msm_glink_pkt.o
+obj-y			+=	qdsp6v2/
 obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR)	+=	system_health_monitor_v01.o
 obj-$(CONFIG_MSM_SYSTEM_HEALTH_MONITOR)	+=	system_health_monitor.o
 obj-$(CONFIG_MSM_SYSMON_GLINK_COMM) += sysmon-glink.o sysmon-qmi.o
@@ -58,3 +61,4 @@
        obj-y += ramdump.o
 endif
 obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o
+obj-$(CONFIG_WCD_DSP_GLINK) += wcd-dsp-glink.o
diff --git a/drivers/soc/qcom/avtimer.c b/drivers/soc/qcom/avtimer.c
index 5482fbf..757cdb0 100644
--- a/drivers/soc/qcom/avtimer.c
+++ b/drivers/soc/qcom/avtimer.c
@@ -517,7 +517,7 @@ static int  __init avtimer_init(void)
 	s32 rc;
 
 	rc = platform_driver_register(&dev_avtimer_driver);
-	if (IS_ERR_VALUE(rc)) {
+	if (rc < 0) {
 		pr_err("%s: platform_driver_register failed\n", __func__);
 		goto error_platform_driver;
 	}
diff --git a/drivers/soc/qcom/early_random.c b/drivers/soc/qcom/early_random.c
new file mode 100644
index 0000000..0c562ec
--- /dev/null
+++ b/drivers/soc/qcom/early_random.c
@@ -0,0 +1,63 @@
+/* Copyright (c) 2013-2014, 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/random.h>
+#include <linux/io.h>
+
+#include <soc/qcom/scm.h>
+
+#include <asm/cacheflush.h>
+
+#define TZ_SVC_CRYPTO	10
+#define PRNG_CMD_ID	0x01
+
+struct tz_prng_data {
+	uint8_t		*out_buf;
+	uint32_t	out_buf_sz;
+} __packed;
+
+DEFINE_SCM_BUFFER(common_scm_buf)
+#define RANDOM_BUFFER_SIZE	PAGE_SIZE
+char random_buffer[RANDOM_BUFFER_SIZE] __aligned(PAGE_SIZE);
+
+void __init init_random_pool(void)
+{
+	struct tz_prng_data data;
+	int ret;
+	u32 resp;
+	struct scm_desc desc;
+
+	data.out_buf = (uint8_t *) virt_to_phys(random_buffer);
+	desc.args[0] = (unsigned long) data.out_buf;
+	desc.args[1] = data.out_buf_sz = SZ_512;
+	desc.arginfo = SCM_ARGS(2, SCM_RW, SCM_VAL);
+
+	dmac_flush_range(random_buffer, random_buffer + RANDOM_BUFFER_SIZE);
+
+	if (!is_scm_armv8())
+		ret = scm_call_noalloc(TZ_SVC_CRYPTO, PRNG_CMD_ID, &data,
+				sizeof(data), &resp, sizeof(resp),
+				common_scm_buf,
+				SCM_BUFFER_SIZE(common_scm_buf));
+	else
+		ret = scm_call2(SCM_SIP_FNID(TZ_SVC_CRYPTO, PRNG_CMD_ID),
+					&desc);
+
+	if (!ret) {
+		dmac_inv_range(random_buffer, random_buffer +
+						RANDOM_BUFFER_SIZE);
+		add_device_randomness(random_buffer, SZ_512);
+	}
+}
+
diff --git a/drivers/soc/qcom/icnss.c b/drivers/soc/qcom/icnss.c
index 59d7f64..7c08c28 100644
--- a/drivers/soc/qcom/icnss.c
+++ b/drivers/soc/qcom/icnss.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -36,6 +36,7 @@
 #include <linux/thread_info.h>
 #include <linux/uaccess.h>
 #include <linux/qpnp/qpnp-adc.h>
+#include <linux/etherdevice.h>
 #include <soc/qcom/memory_dump.h>
 #include <soc/qcom/icnss.h>
 #include <soc/qcom/msm_qmi_interface.h>
@@ -50,12 +51,12 @@
 #include "wlan_firmware_service_v01.h"
 
 #ifdef CONFIG_ICNSS_DEBUG
-unsigned long qmi_timeout = 3000;
+unsigned long qmi_timeout = 10000;
 module_param(qmi_timeout, ulong, 0600);
 
 #define WLFW_TIMEOUT_MS			qmi_timeout
 #else
-#define WLFW_TIMEOUT_MS			3000
+#define WLFW_TIMEOUT_MS			10000
 #endif
 #define WLFW_SERVICE_INS_ID_V01		0
 #define WLFW_CLIENT_ID			0x4b4e454c
@@ -64,126 +65,6 @@ module_param(qmi_timeout, ulong, 0600);
 #define NUM_REG_LOG_PAGES		4
 #define ICNSS_MAGIC			0x5abc5abc
 
-/*
- * Registers: MPM2_PSHOLD
- * Base Address: 0x10AC000
- */
-#define MPM_WCSSAON_CONFIG_OFFSET				0x18
-#define MPM_WCSSAON_CONFIG_ARES_N				BIT(0)
-#define MPM_WCSSAON_CONFIG_WLAN_DISABLE				BIT(1)
-#define MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD			BIT(6)
-#define MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD_VAL		BIT(7)
-#define MPM_WCSSAON_CONFIG_FORCE_ACTIVE				BIT(14)
-#define MPM_WCSSAON_CONFIG_FORCE_XO_ENABLE			BIT(19)
-#define MPM_WCSSAON_CONFIG_DISCONNECT_CLR			BIT(21)
-#define MPM_WCSSAON_CONFIG_M2W_CLAMP_EN				BIT(22)
-
-/*
- * Registers: WCSS_SR_SHADOW_REGISTERS
- * Base Address: 0x18820000
- */
-#define SR_WCSSAON_SR_LSB_OFFSET				0x22070
-#define SR_WCSSAON_SR_LSB_RETENTION_STATUS			BIT(20)
-
-#define SR_PMM_SR_MSB						0x2206C
-#define SR_PMM_SR_MSB_AHB_CLOCK_MASK				GENMASK(26, 22)
-#define SR_PMM_SR_MSB_XO_CLOCK_MASK				GENMASK(31, 27)
-
-/*
- * Registers: WCSS_HM_A_WCSS_CLK_CTL_WCSS_CC_REG
- * Base Address: 0x189D0000
- */
-#define WCSS_WLAN1_GDSCR_OFFSET					0x1D3004
-#define WCSS_WLAN1_GDSCR_SW_COLLAPSE				BIT(0)
-#define WCSS_WLAN1_GDSCR_HW_CONTROL				BIT(1)
-#define WCSS_WLAN1_GDSCR_PWR_ON					BIT(31)
-
-#define WCSS_RFACTRL_GDSCR_OFFSET				0x1D60C8
-#define WCSS_RFACTRL_GDSCR_SW_COLLAPSE				BIT(0)
-#define WCSS_RFACTRL_GDSCR_HW_CONTROL				BIT(1)
-#define WCSS_RFACTRL_GDSCR_PWR_ON				BIT(31)
-
-#define WCSS_CLK_CTL_WCSS_CSS_GDSCR_OFFSET			0x1D1004
-#define WCSS_CLK_CTL_WCSS_CSS_GDSCR_SW_COLLAPSE			BIT(0)
-#define WCSS_CLK_CTL_WCSS_CSS_GDSCR_HW_CONTROL			BIT(1)
-#define WCSS_CLK_CTL_WCSS_CSS_GDSCR_PWR_ON			BIT(31)
-
-#define WCSS_CLK_CTL_NOC_CMD_RCGR_OFFSET			0x1D1030
-#define WCSS_CLK_CTL_NOC_CMD_RCGR_UPDATE			BIT(0)
-
-#define WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET			0x1D1034
-#define WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL			GENMASK(10, 8)
-
-#define WCSS_CLK_CTL_REF_CMD_RCGR_OFFSET			0x1D602C
-#define WCSS_CLK_CTL_REF_CMD_RCGR_UPDATE			BIT(0)
-
-#define WCSS_CLK_CTL_REF_CFG_RCGR_OFFSET			0x1D6030
-#define WCSS_CLK_CTL_REF_CFG_RCGR_SRC_SEL			GENMASK(10, 8)
-
-/*
- * Registers: WCSS_HM_A_WIFI_APB_3_A_WCMN_MAC_WCMN_REG
- * Base Address: 0x18AF0000
- */
-#define WCMN_PMM_WLAN1_CFG_REG1_OFFSET				0x2F0804
-#define WCMN_PMM_WLAN1_CFG_REG1_RFIF_ADC_PORDN_N		BIT(9)
-#define WCMN_PMM_WLAN1_CFG_REG1_ADC_DIGITAL_CLAMP		BIT(10)
-
-/*
- * Registers: WCSS_HM_A_PMM_PMM
- * Base Address: 0x18880000
- */
-#define WCSS_HM_A_PMM_ROOT_CLK_ENABLE				0x80010
-#define PMM_TCXO_CLK_ENABLE					BIT(13)
-
-#define PMM_COMMON_IDLEREQ_CSR_OFFSET				0x80120
-#define PMM_COMMON_IDLEREQ_CSR_SW_WNOC_IDLEREQ_SET		BIT(16)
-#define PMM_COMMON_IDLEREQ_CSR_WNOC_IDLEACK			BIT(26)
-#define PMM_COMMON_IDLEREQ_CSR_WNOC_IDLE			BIT(27)
-
-#define PMM_RFACTRL_IDLEREQ_CSR_OFFSET				0x80164
-#define PMM_RFACTRL_IDLEREQ_CSR_SW_RFACTRL_IDLEREQ_SET		BIT(16)
-#define PMM_RFACTRL_IDLEREQ_CSR_RFACTRL_IDLETACK		BIT(26)
-
-#define PMM_WSI_CMD_OFFSET					0x800E0
-#define PMM_WSI_CMD_USE_WLAN1_WSI				BIT(0)
-#define PMM_WSI_CMD_SW_USE_PMM_WSI				BIT(2)
-#define PMM_WSI_CMD_SW_BUS_SYNC					BIT(3)
-#define PMM_WSI_CMD_SW_RF_RESET					BIT(4)
-#define PMM_WSI_CMD_SW_REG_READ					BIT(5)
-#define PMM_WSI_CMD_SW_XO_DIS					BIT(8)
-#define PMM_WSI_CMD_SW_FORCE_IDLE				BIT(9)
-#define PMM_WSI_CMD_PMM_WSI_SM					GENMASK(24, 16)
-#define PMM_WSI_CMD_RF_CMD_IP					BIT(31)
-
-#define PMM_REG_RW_ADDR_OFFSET					0x800F0
-#define PMM_REG_RW_ADDR_SW_REG_RW_ADDR				GENMASK(15, 0)
-
-#define PMM_REG_READ_DATA_OFFSET				0x800F8
-
-#define PMM_RF_VAULT_REG_ADDR_OFFSET				0x800FC
-#define PMM_RF_VAULT_REG_ADDR_RF_VAULT_REG_ADDR			GENMASK(15, 0)
-
-#define PMM_RF_VAULT_REG_DATA_OFFSET				0x80100
-#define PMM_RF_VAULT_REG_DATA_RF_VAULT_REG_DATA			GENMASK(31, 0)
-
-#define PMM_XO_DIS_ADDR_OFFSET					0x800E8
-#define PMM_XO_DIS_ADDR_XO_DIS_ADDR				GENMASK(15, 0)
-
-#define PMM_XO_DIS_DATA_OFFSET					0x800EC
-#define PMM_XO_DIS_DATA_XO_DIS_DATA				GENMASK(31, 0)
-
-#define PMM_RF_RESET_ADDR_OFFSET				0x80104
-#define PMM_RF_RESET_ADDR_RF_RESET_ADDR				GENMASK(15, 0)
-
-#define PMM_RF_RESET_DATA_OFFSET				0x80108
-#define PMM_RF_RESET_DATA_RF_RESET_DATA				GENMASK(31, 0)
-
-#define ICNSS_HW_REG_RETRY					10
-
-#define WCSS_HM_A_PMM_HW_VERSION_V10				0x40000000
-#define WCSS_HM_A_PMM_HW_VERSION_V20				0x40010000
-#define WCSS_HM_A_PMM_HW_VERSION_Q10				0x40010001
-
 #define ICNSS_SERVICE_LOCATION_CLIENT_NAME			"ICNSS-WLAN"
 #define ICNSS_WLAN_SERVICE_NAME					"wlan/fw"
 
@@ -262,13 +143,18 @@ enum icnss_debug_quirks {
 	SSR_ONLY,
 	PDR_ONLY,
 	VBATT_DISABLE,
+	FW_REJUVENATE_ENABLE,
 };
 
-#define ICNSS_QUIRKS_DEFAULT		BIT(VBATT_DISABLE)
+#define ICNSS_QUIRKS_DEFAULT		(BIT(VBATT_DISABLE) | \
+					 BIT(FW_REJUVENATE_ENABLE))
 
 unsigned long quirks = ICNSS_QUIRKS_DEFAULT;
 module_param(quirks, ulong, 0600);
 
+uint64_t dynamic_feature_mask = QMI_WLFW_FW_REJUVENATE_V01;
+module_param(dynamic_feature_mask, ullong, 0600);
+
 void *icnss_ipc_log_context;
 
 #ifdef CONFIG_ICNSS_DEBUG
@@ -294,6 +180,7 @@ enum icnss_driver_event_type {
 
 struct icnss_event_pd_service_down_data {
 	bool crashed;
+	bool fw_rejuvenate;
 };
 
 struct icnss_driver_event {
@@ -325,38 +212,6 @@ struct ce_irq_list {
 	irqreturn_t (*handler)(int, void *);
 };
 
-struct icnss_vreg_info {
-	struct regulator *reg;
-	const char *name;
-	u32 min_v;
-	u32 max_v;
-	u32 load_ua;
-	unsigned long settle_delay;
-	bool required;
-};
-
-struct icnss_clk_info {
-	struct clk *handle;
-	const char *name;
-	u32 freq;
-	bool required;
-};
-
-static struct icnss_vreg_info icnss_vreg_info[] = {
-	{NULL, "vdd-0.8-cx-mx", 800000, 800000, 0, 0, true},
-	{NULL, "vdd-1.8-xo", 1800000, 1800000, 0, 0, false},
-	{NULL, "vdd-1.3-rfa", 1304000, 1304000, 0, 0, false},
-	{NULL, "vdd-3.3-ch0", 3312000, 3312000, 0, 0, false},
-};
-
-#define ICNSS_VREG_INFO_SIZE		ARRAY_SIZE(icnss_vreg_info)
-
-static struct icnss_clk_info icnss_clk_info[] = {
-	{NULL, "cxo_ref_clk_pin", 0, false},
-};
-
-#define ICNSS_CLK_INFO_SIZE		ARRAY_SIZE(icnss_clk_info)
-
 struct icnss_stats {
 	struct {
 		uint32_t posted;
@@ -407,6 +262,21 @@ struct icnss_stats {
 	uint32_t vbatt_req;
 	uint32_t vbatt_resp;
 	uint32_t vbatt_req_err;
+	uint32_t rejuvenate_ack_req;
+	uint32_t rejuvenate_ack_resp;
+	uint32_t rejuvenate_ack_err;
+};
+
+#define MAX_NO_OF_MAC_ADDR 4
+struct icnss_wlan_mac_addr {
+	u8 mac_addr[MAX_NO_OF_MAC_ADDR][ETH_ALEN];
+	uint32_t no_of_mac_addr_set;
+};
+
+struct service_notifier_context {
+	void *handle;
+	uint32_t instance_id;
+	char name[QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1];
 };
 
 static struct icnss_priv {
@@ -414,13 +284,9 @@ static struct icnss_priv {
 	struct platform_device *pdev;
 	struct icnss_driver_ops *ops;
 	struct ce_irq_list ce_irq_list[ICNSS_MAX_IRQ_REGISTRATIONS];
-	struct icnss_vreg_info vreg_info[ICNSS_VREG_INFO_SIZE];
-	struct icnss_clk_info clk_info[ICNSS_CLK_INFO_SIZE];
 	u32 ce_irqs[ICNSS_MAX_IRQ_REGISTRATIONS];
 	phys_addr_t mem_base_pa;
 	void __iomem *mem_base_va;
-	phys_addr_t mpm_config_pa;
-	void __iomem *mpm_config_va;
 	struct dma_iommu_mapping *smmu_mapping;
 	dma_addr_t smmu_iova_start;
 	size_t smmu_iova_len;
@@ -450,7 +316,7 @@ static struct icnss_priv {
 	spinlock_t on_off_lock;
 	struct icnss_stats stats;
 	struct work_struct service_notifier_work;
-	void **service_notifier;
+	struct service_notifier_context *service_notifier;
 	struct notifier_block service_notifier_nb;
 	int total_domains;
 	struct notifier_block get_service_nb;
@@ -466,73 +332,10 @@ static struct icnss_priv {
 	uint64_t vph_pwr;
 	atomic_t pm_count;
 	struct ramdump_device *msa0_dump_dev;
+	bool is_wlan_mac_set;
+	struct icnss_wlan_mac_addr wlan_mac_addr;
 } *penv;
 
-static void icnss_hw_write_reg(void *base, u32 offset, u32 val)
-{
-	writel_relaxed(val, base + offset);
-	wmb(); /* Ensure data is written to hardware register */
-}
-
-static u32 icnss_hw_read_reg(void *base, u32 offset)
-{
-	u32 rdata = readl_relaxed(base + offset);
-
-	icnss_reg_dbg(" READ: offset: 0x%06x 0x%08x\n", offset, rdata);
-
-	return rdata;
-}
-
-static void icnss_hw_write_reg_field(void *base, u32 offset, u32 mask, u32 val)
-{
-	u32 shift = find_first_bit((void *)&mask, 32);
-	u32 rdata = readl_relaxed(base + offset);
-
-	val = (rdata & ~mask) | (val << shift);
-
-	icnss_reg_dbg("WRITE: offset: 0x%06x 0x%08x -> 0x%08x\n",
-		     offset, rdata, val);
-
-	icnss_hw_write_reg(base, offset, val);
-}
-
-static int icnss_hw_poll_reg_field(void *base, u32 offset, u32 mask, u32 val,
-				    unsigned long usecs, int retry)
-{
-	u32 shift;
-	u32 rdata;
-	int r = 0;
-
-	shift = find_first_bit((void *)&mask, 32);
-
-	val = val << shift;
-
-	rdata  = readl_relaxed(base + offset);
-
-	icnss_reg_dbg(" POLL: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
-		     offset, val, rdata, mask);
-
-	while ((rdata & mask) != val) {
-		if (retry != 0 && r >= retry) {
-			icnss_pr_err("POLL FAILED: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
-				     offset, val, rdata, mask);
-
-			return -EIO;
-		}
-
-		r++;
-		udelay(usecs);
-		rdata = readl_relaxed(base + offset);
-
-		if (retry)
-			icnss_reg_dbg(" POLL: offset: 0x%06x 0x%08x == 0x%08x & 0x%08x\n",
-					 offset, val, rdata, mask);
-
-	}
-
-	return 0;
-}
-
 static void icnss_pm_stay_awake(struct icnss_priv *priv)
 {
 	if (atomic_inc_return(&priv->pm_count) != 1)
@@ -877,683 +680,6 @@ static int icnss_qmi_pin_connect_result_ind(void *msg, unsigned int msg_len)
 	return ret;
 }
 
-static int icnss_vreg_on(struct icnss_priv *priv)
-{
-	int ret = 0;
-	struct icnss_vreg_info *vreg_info;
-	int i;
-
-	for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
-		vreg_info = &priv->vreg_info[i];
-
-		if (!vreg_info->reg)
-			continue;
-
-		icnss_pr_dbg("Regulator %s being enabled\n", vreg_info->name);
-
-		ret = regulator_set_voltage(vreg_info->reg, vreg_info->min_v,
-					    vreg_info->max_v);
-
-		if (ret) {
-			icnss_pr_err("Regulator %s, can't set voltage: min_v: %u, max_v: %u, ret: %d\n",
-				     vreg_info->name, vreg_info->min_v,
-				     vreg_info->max_v, ret);
-			break;
-		}
-
-		if (vreg_info->load_ua) {
-			ret = regulator_set_load(vreg_info->reg,
-						 vreg_info->load_ua);
-
-			if (ret < 0) {
-				icnss_pr_err("Regulator %s, can't set load: %u, ret: %d\n",
-					     vreg_info->name,
-					     vreg_info->load_ua, ret);
-				break;
-			}
-		}
-
-		ret = regulator_enable(vreg_info->reg);
-		if (ret) {
-			icnss_pr_err("Regulator %s, can't enable: %d\n",
-				     vreg_info->name, ret);
-			break;
-		}
-
-		if (vreg_info->settle_delay)
-			udelay(vreg_info->settle_delay);
-	}
-
-	if (!ret)
-		return 0;
-
-	for (; i >= 0; i--) {
-		vreg_info = &priv->vreg_info[i];
-
-		if (!vreg_info->reg)
-			continue;
-
-		regulator_disable(vreg_info->reg);
-
-		regulator_set_load(vreg_info->reg, 0);
-
-		regulator_set_voltage(vreg_info->reg, 0, vreg_info->max_v);
-	}
-
-	return ret;
-}
-
-static int icnss_vreg_off(struct icnss_priv *priv)
-{
-	int ret = 0;
-	struct icnss_vreg_info *vreg_info;
-	int i;
-
-	for (i = ICNSS_VREG_INFO_SIZE - 1; i >= 0; i--) {
-		vreg_info = &priv->vreg_info[i];
-
-		if (!vreg_info->reg)
-			continue;
-
-		icnss_pr_dbg("Regulator %s being disabled\n", vreg_info->name);
-
-		ret = regulator_disable(vreg_info->reg);
-		if (ret)
-			icnss_pr_err("Regulator %s, can't disable: %d\n",
-				     vreg_info->name, ret);
-
-		ret = regulator_set_load(vreg_info->reg, 0);
-		if (ret < 0)
-			icnss_pr_err("Regulator %s, can't set load: %d\n",
-				     vreg_info->name, ret);
-
-		ret = regulator_set_voltage(vreg_info->reg, 0,
-					    vreg_info->max_v);
-
-		if (ret)
-			icnss_pr_err("Regulator %s, can't set voltage: %d\n",
-				     vreg_info->name, ret);
-	}
-
-	return ret;
-}
-
-static int icnss_clk_init(struct icnss_priv *priv)
-{
-	struct icnss_clk_info *clk_info;
-	int i;
-	int ret = 0;
-
-	for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
-		clk_info = &priv->clk_info[i];
-
-		if (!clk_info->handle)
-			continue;
-
-		icnss_pr_dbg("Clock %s being enabled\n", clk_info->name);
-
-		if (clk_info->freq) {
-			ret = clk_set_rate(clk_info->handle, clk_info->freq);
-
-			if (ret) {
-				icnss_pr_err("Clock %s, can't set frequency: %u, ret: %d\n",
-					     clk_info->name, clk_info->freq,
-					     ret);
-				break;
-			}
-		}
-
-		ret = clk_prepare_enable(clk_info->handle);
-
-		if (ret) {
-			icnss_pr_err("Clock %s, can't enable: %d\n",
-				     clk_info->name, ret);
-			break;
-		}
-	}
-
-	if (ret == 0)
-		return 0;
-
-	for (; i >= 0; i--) {
-		clk_info = &priv->clk_info[i];
-
-		if (!clk_info->handle)
-			continue;
-
-		clk_disable_unprepare(clk_info->handle);
-	}
-
-	return ret;
-}
-
-static int icnss_clk_deinit(struct icnss_priv *priv)
-{
-	struct icnss_clk_info *clk_info;
-	int i;
-
-	for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
-		clk_info = &priv->clk_info[i];
-
-		if (!clk_info->handle)
-			continue;
-
-		icnss_pr_dbg("Clock %s being disabled\n", clk_info->name);
-
-		clk_disable_unprepare(clk_info->handle);
-	}
-
-	return 0;
-}
-
-static void icnss_hw_top_level_release_reset(struct icnss_priv *priv)
-{
-	icnss_pr_dbg("RESET: HW Release reset: state: 0x%lx\n", priv->state);
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_ARES_N, 1);
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_WLAN_DISABLE, 0x0);
-
-	icnss_hw_poll_reg_field(priv->mpm_config_va,
-				MPM_WCSSAON_CONFIG_OFFSET,
-				MPM_WCSSAON_CONFIG_ARES_N, 1, 10,
-				ICNSS_HW_REG_RETRY);
-}
-
-static void icnss_hw_top_level_reset(struct icnss_priv *priv)
-{
-	icnss_pr_dbg("RESET: HW top level reset: state: 0x%lx\n", priv->state);
-
-	icnss_hw_write_reg_field(priv->mpm_config_va,
-				 MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_ARES_N, 0);
-
-	icnss_hw_poll_reg_field(priv->mpm_config_va,
-				MPM_WCSSAON_CONFIG_OFFSET,
-				MPM_WCSSAON_CONFIG_ARES_N, 0, 10,
-				ICNSS_HW_REG_RETRY);
-}
-
-static void icnss_hw_io_reset(struct icnss_priv *priv, bool on)
-{
-	u32 hw_version = priv->soc_info.soc_id;
-
-	if (on && !test_bit(ICNSS_FW_READY, &priv->state))
-		return;
-
-	icnss_pr_dbg("HW io reset: %s, SoC: 0x%x, state: 0x%lx\n",
-		     on ? "ON" : "OFF", priv->soc_info.soc_id, priv->state);
-
-	if (hw_version == WCSS_HM_A_PMM_HW_VERSION_V10 ||
-	    hw_version == WCSS_HM_A_PMM_HW_VERSION_V20) {
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-				MPM_WCSSAON_CONFIG_OFFSET,
-				MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD_VAL, 0);
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-				MPM_WCSSAON_CONFIG_OFFSET,
-				MPM_WCSSAON_CONFIG_MSM_CLAMP_EN_OVRD, on);
-	} else if (hw_version == WCSS_HM_A_PMM_HW_VERSION_Q10) {
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-				MPM_WCSSAON_CONFIG_OFFSET,
-				MPM_WCSSAON_CONFIG_M2W_CLAMP_EN,
-				on);
-	}
-}
-
-static int icnss_hw_reset_wlan_ss_power_down(struct icnss_priv *priv)
-{
-	u32 rdata;
-
-	icnss_pr_dbg("RESET: WLAN SS power down, state: 0x%lx\n", priv->state);
-
-	rdata = icnss_hw_read_reg(priv->mem_base_va, WCSS_WLAN1_GDSCR_OFFSET);
-
-	if ((rdata & WCSS_WLAN1_GDSCR_PWR_ON) == 0)
-		return 0;
-
-	icnss_hw_write_reg_field(priv->mem_base_va, WCSS_WLAN1_GDSCR_OFFSET,
-				 WCSS_WLAN1_GDSCR_HW_CONTROL, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, WCSS_WLAN1_GDSCR_OFFSET,
-				 WCSS_WLAN1_GDSCR_SW_COLLAPSE, 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va, WCSS_WLAN1_GDSCR_OFFSET,
-				WCSS_WLAN1_GDSCR_PWR_ON, 0, 10,
-				ICNSS_HW_REG_RETRY);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCMN_PMM_WLAN1_CFG_REG1_OFFSET,
-				 WCMN_PMM_WLAN1_CFG_REG1_ADC_DIGITAL_CLAMP, 1);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCMN_PMM_WLAN1_CFG_REG1_OFFSET,
-				 WCMN_PMM_WLAN1_CFG_REG1_RFIF_ADC_PORDN_N, 0);
-
-	return 0;
-}
-
-static int icnss_hw_reset_common_ss_power_down(struct icnss_priv *priv)
-{
-	u32 rdata;
-
-	icnss_pr_dbg("RESET: Common SS power down, state: 0x%lx\n",
-		     priv->state);
-
-	rdata = icnss_hw_read_reg(priv->mem_base_va,
-				  WCSS_CLK_CTL_WCSS_CSS_GDSCR_OFFSET);
-
-	if ((rdata & WCSS_CLK_CTL_WCSS_CSS_GDSCR_PWR_ON) == 0)
-		return 0;
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 PMM_COMMON_IDLEREQ_CSR_OFFSET,
-				 PMM_COMMON_IDLEREQ_CSR_SW_WNOC_IDLEREQ_SET,
-				 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va,
-				PMM_COMMON_IDLEREQ_CSR_OFFSET,
-				PMM_COMMON_IDLEREQ_CSR_WNOC_IDLEACK,
-				1, 20, ICNSS_HW_REG_RETRY);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va,
-				PMM_COMMON_IDLEREQ_CSR_OFFSET,
-				PMM_COMMON_IDLEREQ_CSR_WNOC_IDLE,
-				1, 10, ICNSS_HW_REG_RETRY);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCSS_CLK_CTL_WCSS_CSS_GDSCR_OFFSET,
-				 WCSS_CLK_CTL_WCSS_CSS_GDSCR_HW_CONTROL, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCSS_CLK_CTL_WCSS_CSS_GDSCR_OFFSET,
-				 WCSS_CLK_CTL_WCSS_CSS_GDSCR_SW_COLLAPSE, 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va,
-				WCSS_CLK_CTL_WCSS_CSS_GDSCR_OFFSET,
-				WCSS_CLK_CTL_WCSS_CSS_GDSCR_PWR_ON, 0, 10,
-				ICNSS_HW_REG_RETRY);
-
-	return 0;
-
-}
-
-static int icnss_hw_reset_wlan_rfactrl_power_down(struct icnss_priv *priv)
-{
-	u32 rdata;
-
-	icnss_pr_dbg("RESET: RFACTRL power down, state: 0x%lx\n", priv->state);
-
-	rdata = icnss_hw_read_reg(priv->mem_base_va, WCSS_RFACTRL_GDSCR_OFFSET);
-
-	if ((rdata & WCSS_RFACTRL_GDSCR_PWR_ON) == 0)
-		return 0;
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 PMM_RFACTRL_IDLEREQ_CSR_OFFSET,
-				 PMM_RFACTRL_IDLEREQ_CSR_SW_RFACTRL_IDLEREQ_SET,
-				 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va,
-				PMM_RFACTRL_IDLEREQ_CSR_OFFSET,
-				PMM_RFACTRL_IDLEREQ_CSR_RFACTRL_IDLETACK,
-				1, 10, ICNSS_HW_REG_RETRY);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, WCSS_RFACTRL_GDSCR_OFFSET,
-				 WCSS_RFACTRL_GDSCR_HW_CONTROL, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, WCSS_RFACTRL_GDSCR_OFFSET,
-				 WCSS_RFACTRL_GDSCR_SW_COLLAPSE, 1);
-
-	return 0;
-}
-
-static void icnss_hw_wsi_cmd_error_recovery(struct icnss_priv *priv)
-{
-	icnss_pr_dbg("RESET: WSI CMD Error recovery, state: 0x%lx\n",
-		     priv->state);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_FORCE_IDLE, 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				PMM_WSI_CMD_PMM_WSI_SM, 1, 100, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_FORCE_IDLE, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_BUS_SYNC, 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				PMM_WSI_CMD_RF_CMD_IP, 0, 100, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_BUS_SYNC, 0);
-}
-
-static u32 icnss_hw_rf_register_read_command(struct icnss_priv *priv, u32 addr)
-{
-	u32 rdata = 0;
-	int ret;
-	int i;
-
-	icnss_pr_dbg("RF register read command, addr: 0x%04x, state: 0x%lx\n",
-		     addr, priv->state);
-
-	for (i = 0; i < ICNSS_HW_REG_RETRY; i++) {
-		icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-					 PMM_WSI_CMD_USE_WLAN1_WSI, 1);
-
-		icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-					 PMM_WSI_CMD_SW_USE_PMM_WSI, 1);
-
-		icnss_hw_write_reg_field(priv->mem_base_va,
-					 PMM_REG_RW_ADDR_OFFSET,
-					 PMM_REG_RW_ADDR_SW_REG_RW_ADDR,
-					 addr & 0xFFFF);
-
-		icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-					 PMM_WSI_CMD_SW_REG_READ, 1);
-
-		ret = icnss_hw_poll_reg_field(priv->mem_base_va,
-					      PMM_WSI_CMD_OFFSET,
-					      PMM_WSI_CMD_RF_CMD_IP, 0, 10,
-					      ICNSS_HW_REG_RETRY);
-		if (ret == 0)
-			break;
-
-		icnss_hw_wsi_cmd_error_recovery(priv);
-	}
-
-
-	rdata = icnss_hw_read_reg(priv->mem_base_va, PMM_REG_READ_DATA_OFFSET);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_USE_PMM_WSI, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_REG_READ, 0);
-
-	icnss_pr_dbg("RF register read command, data: 0x%08x, state: 0x%lx\n",
-		     rdata, priv->state);
-
-	return rdata;
-}
-
-static int icnss_hw_reset_rf_reset_cmd(struct icnss_priv *priv)
-{
-	u32 rdata;
-	int ret;
-
-	icnss_pr_dbg("RESET: RF reset command, state: 0x%lx\n", priv->state);
-
-	rdata = icnss_hw_rf_register_read_command(priv, 0x5080);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_USE_WLAN1_WSI, 1);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_USE_PMM_WSI, 1);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 PMM_RF_VAULT_REG_ADDR_OFFSET,
-				 PMM_RF_VAULT_REG_ADDR_RF_VAULT_REG_ADDR,
-				 0x5082);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 PMM_RF_VAULT_REG_DATA_OFFSET,
-				 PMM_RF_VAULT_REG_DATA_RF_VAULT_REG_DATA,
-				 0x12AB8FAD);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_RF_RESET_ADDR_OFFSET,
-				 PMM_RF_RESET_ADDR_RF_RESET_ADDR, 0x5080);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_RF_RESET_DATA_OFFSET,
-				 PMM_RF_RESET_DATA_RF_RESET_DATA,
-				 rdata & 0xBFFF);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_RF_RESET, 1);
-
-	ret = icnss_hw_poll_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				      PMM_WSI_CMD_RF_CMD_IP, 0, 10,
-				      ICNSS_HW_REG_RETRY);
-
-	if (ret) {
-		icnss_pr_err("RESET: RF reset command failed, state: 0x%lx\n",
-			     priv->state);
-		return ret;
-	}
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_USE_PMM_WSI, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_RF_RESET, 0);
-
-	return 0;
-}
-
-static int icnss_hw_reset_switch_to_cxo(struct icnss_priv *priv)
-{
-	u32 rdata;
-
-	icnss_pr_dbg("RESET: Switch to CXO, state: 0x%lx\n", priv->state);
-
-	rdata = icnss_hw_read_reg(priv->mem_base_va,
-				  WCSS_HM_A_PMM_ROOT_CLK_ENABLE);
-
-	icnss_pr_dbg("RESET: PMM_TCXO_CLK_ENABLE : 0x%05lx\n",
-		     rdata & PMM_TCXO_CLK_ENABLE);
-
-	if ((rdata & PMM_TCXO_CLK_ENABLE) == 0) {
-		icnss_pr_dbg("RESET: Set PMM_TCXO_CLK_ENABLE to 1\n");
-
-		icnss_hw_write_reg_field(priv->mem_base_va,
-					 WCSS_HM_A_PMM_ROOT_CLK_ENABLE,
-					 PMM_TCXO_CLK_ENABLE, 1);
-		icnss_hw_poll_reg_field(priv->mem_base_va,
-					WCSS_HM_A_PMM_ROOT_CLK_ENABLE,
-					PMM_TCXO_CLK_ENABLE, 1, 10,
-					ICNSS_HW_REG_RETRY);
-	}
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCSS_CLK_CTL_NOC_CFG_RCGR_OFFSET,
-				 WCSS_CLK_CTL_NOC_CFG_RCGR_SRC_SEL, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCSS_CLK_CTL_NOC_CMD_RCGR_OFFSET,
-				 WCSS_CLK_CTL_NOC_CMD_RCGR_UPDATE, 1);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCSS_CLK_CTL_REF_CFG_RCGR_OFFSET,
-				 WCSS_CLK_CTL_REF_CFG_RCGR_SRC_SEL, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 WCSS_CLK_CTL_REF_CMD_RCGR_OFFSET,
-				 WCSS_CLK_CTL_REF_CMD_RCGR_UPDATE, 1);
-
-	return 0;
-}
-
-static int icnss_hw_reset_xo_disable_cmd(struct icnss_priv *priv)
-{
-	int ret;
-
-	icnss_pr_dbg("RESET: XO disable command, state: 0x%lx\n", priv->state);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_USE_WLAN1_WSI, 1);
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_USE_PMM_WSI, 1);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 PMM_RF_VAULT_REG_ADDR_OFFSET,
-				 PMM_RF_VAULT_REG_ADDR_RF_VAULT_REG_ADDR,
-				 0x5082);
-
-	icnss_hw_write_reg_field(priv->mem_base_va,
-				 PMM_RF_VAULT_REG_DATA_OFFSET,
-				 PMM_RF_VAULT_REG_DATA_RF_VAULT_REG_DATA,
-				 0x12AB8FAD);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_XO_DIS_ADDR_OFFSET,
-				 PMM_XO_DIS_ADDR_XO_DIS_ADDR, 0x5081);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_XO_DIS_DATA_OFFSET,
-				 PMM_XO_DIS_DATA_XO_DIS_DATA, 1);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_XO_DIS, 1);
-
-	ret = icnss_hw_poll_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				      PMM_WSI_CMD_RF_CMD_IP, 0, 10,
-				      ICNSS_HW_REG_RETRY);
-	if (ret) {
-		icnss_pr_err("RESET: XO disable command failed, state: 0x%lx\n",
-			     priv->state);
-		return ret;
-	}
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_USE_PMM_WSI, 0);
-
-	icnss_hw_write_reg_field(priv->mem_base_va, PMM_WSI_CMD_OFFSET,
-				 PMM_WSI_CMD_SW_XO_DIS, 0);
-
-	return 0;
-}
-
-static int icnss_hw_reset(struct icnss_priv *priv)
-{
-	u32 rdata;
-	u32 rdata1;
-	int i;
-	int ret = 0;
-
-	if (test_bit(HW_ONLY_TOP_LEVEL_RESET, &quirks))
-		goto top_level_reset;
-
-	icnss_pr_dbg("RESET: START, state: 0x%lx\n", priv->state);
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va, SR_WCSSAON_SR_LSB_OFFSET,
-				SR_WCSSAON_SR_LSB_RETENTION_STATUS, 1, 200,
-				ICNSS_HW_REG_RETRY);
-
-	for (i = 0; i < ICNSS_HW_REG_RETRY; i++) {
-		rdata = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB);
-		udelay(10);
-		rdata1 = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB);
-
-		icnss_pr_dbg("RESET: XO: 0x%05lx/0x%05lx, AHB: 0x%05lx/0x%05lx\n",
-			     rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK,
-			     rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK,
-			     rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK,
-			     rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK);
-
-		if ((rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK) !=
-		    (rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK) &&
-		    (rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK) !=
-		    (rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK))
-			break;
-
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_FORCE_XO_ENABLE,
-					 0x1);
-		usleep_range(2000, 3000);
-	}
-
-	if (i >= ICNSS_HW_REG_RETRY)
-		goto top_level_reset;
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_DISCONNECT_CLR, 0x1);
-
-	usleep_range(200, 300);
-
-	icnss_hw_reset_wlan_ss_power_down(priv);
-
-	icnss_hw_reset_common_ss_power_down(priv);
-
-	icnss_hw_reset_wlan_rfactrl_power_down(priv);
-
-	ret = icnss_hw_reset_rf_reset_cmd(priv);
-	if (ret) {
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 0);
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_DISCONNECT_CLR, 0);
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_WLAN_DISABLE, 1);
-		goto top_level_reset;
-	}
-
-	icnss_hw_reset_switch_to_cxo(priv);
-
-	for (i = 0; i < ICNSS_HW_REG_RETRY; i++) {
-		rdata = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB);
-		usleep_range(5, 10);
-		rdata1 = icnss_hw_read_reg(priv->mem_base_va, SR_PMM_SR_MSB);
-
-		icnss_pr_dbg("RESET: SR_PMM_SR_MSB: 0x%08x/0x%08x, XO: 0x%05lx/0x%05lx, AHB: 0x%05lx/0x%05lx\n",
-			     rdata, rdata1,
-			     rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK,
-			     rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK,
-			     rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK,
-			     rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK);
-
-		if ((rdata & SR_PMM_SR_MSB_AHB_CLOCK_MASK) !=
-		    (rdata1 & SR_PMM_SR_MSB_AHB_CLOCK_MASK) &&
-		    (rdata & SR_PMM_SR_MSB_XO_CLOCK_MASK) !=
-		    (rdata1 & SR_PMM_SR_MSB_XO_CLOCK_MASK))
-			break;
-		usleep_range(5, 10);
-	}
-
-	ret = icnss_hw_reset_xo_disable_cmd(priv);
-	if (ret) {
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 0);
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_DISCONNECT_CLR, 0);
-		icnss_hw_write_reg_field(priv->mpm_config_va,
-					 MPM_WCSSAON_CONFIG_OFFSET,
-					 MPM_WCSSAON_CONFIG_WLAN_DISABLE, 1);
-		goto top_level_reset;
-	}
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_FORCE_ACTIVE, 0);
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_DISCONNECT_CLR, 0);
-
-	icnss_hw_write_reg_field(priv->mpm_config_va, MPM_WCSSAON_CONFIG_OFFSET,
-				 MPM_WCSSAON_CONFIG_WLAN_DISABLE, 1);
-
-	icnss_hw_poll_reg_field(priv->mem_base_va, SR_WCSSAON_SR_LSB_OFFSET,
-				BIT(26), 1, 200, ICNSS_HW_REG_RETRY);
-
-top_level_reset:
-	icnss_hw_top_level_reset(priv);
-
-	icnss_pr_dbg("RESET: DONE, state: 0x%lx\n", priv->state);
-
-	return 0;
-}
-
 static int icnss_hw_power_on(struct icnss_priv *priv)
 {
 	int ret = 0;
@@ -1569,21 +695,6 @@ static int icnss_hw_power_on(struct icnss_priv *priv)
 	set_bit(ICNSS_POWER_ON, &priv->state);
 	spin_unlock_irqrestore(&priv->on_off_lock, flags);
 
-	ret = icnss_vreg_on(priv);
-	if (ret)
-		goto out;
-
-	ret = icnss_clk_init(priv);
-	if (ret)
-		goto out;
-
-	icnss_hw_top_level_release_reset(priv);
-
-	icnss_hw_io_reset(penv, 1);
-
-	return ret;
-out:
-	clear_bit(ICNSS_POWER_ON, &priv->state);
 	return ret;
 }
 
@@ -1605,19 +716,6 @@ static int icnss_hw_power_off(struct icnss_priv *priv)
 	clear_bit(ICNSS_POWER_ON, &priv->state);
 	spin_unlock_irqrestore(&priv->on_off_lock, flags);
 
-	icnss_hw_io_reset(penv, 0);
-
-	icnss_hw_reset(priv);
-
-	icnss_clk_deinit(priv);
-
-	ret = icnss_vreg_off(priv);
-	if (ret)
-		goto out;
-
-	return ret;
-out:
-	set_bit(ICNSS_POWER_ON, &priv->state);
 	return ret;
 }
 
@@ -1637,6 +735,15 @@ int icnss_power_on(struct device *dev)
 }
 EXPORT_SYMBOL(icnss_power_on);
 
+bool icnss_is_fw_ready(void)
+{
+	if (!penv)
+		return false;
+	else
+		return test_bit(ICNSS_FW_READY, &penv->state);
+}
+EXPORT_SYMBOL(icnss_is_fw_ready);
+
 int icnss_power_off(struct device *dev)
 {
 	struct icnss_priv *priv = dev_get_drvdata(dev);
@@ -1698,7 +805,7 @@ static int icnss_unmap_msa_permissions(struct icnss_priv *priv, u32 index)
 	u32 size;
 	u32 dest_vmids[1] = {VMID_HLOS};
 	int source_vmlist[3] = {VMID_MSS_MSA, VMID_WLAN, 0};
-	int dest_perms[1] = {PERM_READ|PERM_WRITE};
+	int dest_perms[1] = {PERM_READ|PERM_WRITE|PERM_EXEC};
 	int source_nelems = 0;
 	int dest_nelems = sizeof(dest_vmids)/sizeof(u32);
 
@@ -1909,6 +1016,10 @@ static int wlfw_ind_register_send_sync_msg(void)
 	req.msa_ready_enable = 1;
 	req.pin_connect_result_enable_valid = 1;
 	req.pin_connect_result_enable = 1;
+	if (test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
+		req.rejuvenate_enable_valid = 1;
+		req.rejuvenate_enable = 1;
+	}
 
 	req_desc.max_msg_len = WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN;
 	req_desc.msg_id = QMI_WLFW_IND_REGISTER_REQ_V01;
@@ -2124,7 +1235,7 @@ static int wlfw_wlan_cfg_send_sync_msg(struct wlfw_wlan_cfg_req_msg_v01 *data)
 	return ret;
 }
 
-static int wlfw_ini_send_sync_msg(bool enable_fw_log)
+static int wlfw_ini_send_sync_msg(uint8_t fw_log_mode)
 {
 	int ret;
 	struct wlfw_ini_req_msg_v01 req;
@@ -2134,14 +1245,14 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
 	if (!penv || !penv->wlfw_clnt)
 		return -ENODEV;
 
-	icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log: %d\n",
-		     penv->state, enable_fw_log);
+	icnss_pr_dbg("Sending ini sync request, state: 0x%lx, fw_log_mode: %d\n",
+		     penv->state, fw_log_mode);
 
 	memset(&req, 0, sizeof(req));
 	memset(&resp, 0, sizeof(resp));
 
 	req.enablefwlog_valid = 1;
-	req.enablefwlog = enable_fw_log;
+	req.enablefwlog = fw_log_mode;
 
 	req_desc.max_msg_len = WLFW_INI_REQ_MSG_V01_MAX_MSG_LEN;
 	req_desc.msg_id = QMI_WLFW_INI_REQ_V01;
@@ -2156,14 +1267,14 @@ static int wlfw_ini_send_sync_msg(bool enable_fw_log)
 	ret = qmi_send_req_wait(penv->wlfw_clnt, &req_desc, &req, sizeof(req),
 			&resp_desc, &resp, sizeof(resp), WLFW_TIMEOUT_MS);
 	if (ret < 0) {
-		icnss_pr_err("Send INI req failed fw_log: %d, ret: %d\n",
-			     enable_fw_log, ret);
+		icnss_pr_err("Send INI req failed fw_log_mode: %d, ret: %d\n",
+			     fw_log_mode, ret);
 		goto out;
 	}
 
 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
-		icnss_pr_err("QMI INI request rejected, fw_log:%d result:%d error:%d\n",
-			     enable_fw_log, resp.resp.result, resp.resp.error);
+		icnss_pr_err("QMI INI request rejected, fw_log_mode:%d result:%d error:%d\n",
+			     fw_log_mode, resp.resp.result, resp.resp.error);
 		ret = resp.resp.result;
 		goto out;
 	}
@@ -2298,6 +1409,114 @@ static int wlfw_athdiag_write_send_sync_msg(struct icnss_priv *priv,
 	return ret;
 }
 
+static int wlfw_rejuvenate_ack_send_sync_msg(struct icnss_priv *priv)
+{
+	int ret;
+	struct wlfw_rejuvenate_ack_req_msg_v01 req;
+	struct wlfw_rejuvenate_ack_resp_msg_v01 resp;
+	struct msg_desc req_desc, resp_desc;
+
+	icnss_pr_dbg("Sending rejuvenate ack request, state: 0x%lx\n",
+		     priv->state);
+
+	memset(&req, 0, sizeof(req));
+	memset(&resp, 0, sizeof(resp));
+
+	req_desc.max_msg_len = WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN;
+	req_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_REQ_V01;
+	req_desc.ei_array = wlfw_rejuvenate_ack_req_msg_v01_ei;
+
+	resp_desc.max_msg_len = WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN;
+	resp_desc.msg_id = QMI_WLFW_REJUVENATE_ACK_RESP_V01;
+	resp_desc.ei_array = wlfw_rejuvenate_ack_resp_msg_v01_ei;
+
+	priv->stats.rejuvenate_ack_req++;
+	ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
+				&resp_desc, &resp, sizeof(resp),
+				WLFW_TIMEOUT_MS);
+	if (ret < 0) {
+		icnss_pr_err("Send rejuvenate ack req failed %d\n", ret);
+		goto out;
+	}
+
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI rejuvenate ack request rejected, result:%d error %d\n",
+			     resp.resp.result, resp.resp.error);
+		ret = resp.resp.result;
+		goto out;
+	}
+	priv->stats.rejuvenate_ack_resp++;
+	return 0;
+
+out:
+	priv->stats.rejuvenate_ack_err++;
+	ICNSS_ASSERT(false);
+	return ret;
+}
+
+static int wlfw_dynamic_feature_mask_send_sync_msg(struct icnss_priv *priv,
+					   uint64_t dynamic_feature_mask)
+{
+	int ret;
+	struct wlfw_dynamic_feature_mask_req_msg_v01 req;
+	struct wlfw_dynamic_feature_mask_resp_msg_v01 resp;
+	struct msg_desc req_desc, resp_desc;
+
+	if (!test_bit(ICNSS_WLFW_QMI_CONNECTED, &priv->state)) {
+		icnss_pr_err("Invalid state for dynamic feature: 0x%lx\n",
+			     priv->state);
+		return -EINVAL;
+	}
+
+	if (!test_bit(FW_REJUVENATE_ENABLE, &quirks)) {
+		icnss_pr_dbg("FW rejuvenate is disabled from quirks\n");
+		return 0;
+	}
+
+	icnss_pr_dbg("Sending dynamic feature mask request, val 0x%llx, state: 0x%lx\n",
+		     dynamic_feature_mask, priv->state);
+
+	memset(&req, 0, sizeof(req));
+	memset(&resp, 0, sizeof(resp));
+
+	req.mask_valid = 1;
+	req.mask = dynamic_feature_mask;
+
+	req_desc.max_msg_len =
+		WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN;
+	req_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01;
+	req_desc.ei_array = wlfw_dynamic_feature_mask_req_msg_v01_ei;
+
+	resp_desc.max_msg_len =
+		WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN;
+	resp_desc.msg_id = QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01;
+	resp_desc.ei_array = wlfw_dynamic_feature_mask_resp_msg_v01_ei;
+
+	ret = qmi_send_req_wait(priv->wlfw_clnt, &req_desc, &req, sizeof(req),
+				&resp_desc, &resp, sizeof(resp),
+				WLFW_TIMEOUT_MS);
+	if (ret < 0) {
+		icnss_pr_err("Send dynamic feature mask req failed %d\n", ret);
+		goto out;
+	}
+
+	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
+		icnss_pr_err("QMI dynamic feature mask request rejected, result:%d error %d\n",
+			     resp.resp.result, resp.resp.error);
+		ret = resp.resp.result;
+		goto out;
+	}
+
+	icnss_pr_dbg("prev_mask_valid %u, prev_mask 0x%llx, curr_maks_valid %u, curr_mask 0x%llx\n",
+		     resp.prev_mask_valid, resp.prev_mask,
+		     resp.curr_mask_valid, resp.curr_mask);
+
+	return 0;
+
+out:
+	return ret;
+}
+
 static void icnss_qmi_wlfw_clnt_notify_work(struct work_struct *work)
 {
 	int ret;
@@ -2338,6 +1557,8 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
 			  unsigned int msg_id, void *msg,
 			  unsigned int msg_len, void *ind_cb_priv)
 {
+	struct icnss_event_pd_service_down_data *event_data;
+
 	if (!penv)
 		return;
 
@@ -2358,6 +1579,17 @@ static void icnss_qmi_wlfw_clnt_ind(struct qmi_handle *handle,
 			     msg_id);
 		icnss_qmi_pin_connect_result_ind(msg, msg_len);
 		break;
+	case QMI_WLFW_REJUVENATE_IND_V01:
+		icnss_pr_dbg("Received Rejuvenate Indication msg_id 0x%x, state: 0x%lx\n",
+			     msg_id, penv->state);
+		event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
+		if (event_data == NULL)
+			return;
+		event_data->crashed = true;
+		event_data->fw_rejuvenate = true;
+		icnss_driver_event_post(ICNSS_DRIVER_EVENT_PD_SERVICE_DOWN,
+					0, event_data);
+		break;
 	default:
 		icnss_pr_err("Invalid msg_id 0x%x\n", msg_id);
 		break;
@@ -2430,6 +1662,9 @@ static int icnss_driver_event_server_arrive(void *data)
 	if (ret < 0)
 		goto err_setup_msa;
 
+	wlfw_dynamic_feature_mask_send_sync_msg(penv,
+						dynamic_feature_mask);
+
 	icnss_init_vph_monitor(penv);
 
 	return ret;
@@ -2489,19 +1724,21 @@ static int icnss_call_driver_probe(struct icnss_priv *priv)
 
 out:
 	icnss_hw_power_off(priv);
-	penv->ops = NULL;
 	return ret;
 }
 
-static int icnss_call_driver_reinit(struct icnss_priv *priv)
+static int icnss_pd_restart_complete(struct icnss_priv *priv)
 {
-	int ret = 0;
+	int ret;
+
+	clear_bit(ICNSS_PD_RESTART, &priv->state);
+	icnss_pm_relax(priv);
 
 	if (!priv->ops || !priv->ops->reinit)
 		goto out;
 
 	if (!test_bit(ICNSS_DRIVER_PROBED, &penv->state))
-		goto out;
+		goto call_probe;
 
 	icnss_pr_dbg("Calling driver reinit state: 0x%lx\n", priv->state);
 
@@ -2516,18 +1753,14 @@ static int icnss_call_driver_reinit(struct icnss_priv *priv)
 	}
 
 out:
-	clear_bit(ICNSS_PD_RESTART, &priv->state);
-
-	icnss_pm_relax(priv);
-
 	return 0;
 
+call_probe:
+	return icnss_call_driver_probe(priv);
+
 out_power_off:
 	icnss_hw_power_off(priv);
 
-	clear_bit(ICNSS_PD_RESTART, &priv->state);
-
-	icnss_pm_relax(priv);
 	return ret;
 }
 
@@ -2552,7 +1785,7 @@ static int icnss_driver_event_fw_ready_ind(void *data)
 	}
 
 	if (test_bit(ICNSS_PD_RESTART, &penv->state))
-		ret = icnss_call_driver_reinit(penv);
+		ret = icnss_pd_restart_complete(penv);
 	else
 		ret = icnss_call_driver_probe(penv);
 
@@ -2681,6 +1914,9 @@ static int icnss_driver_event_pd_service_down(struct icnss_priv *priv,
 	else
 		icnss_call_driver_remove(priv);
 
+	if (event_data->fw_rejuvenate)
+		wlfw_rejuvenate_ack_send_sync_msg(priv);
+
 out:
 	ret = icnss_hw_power_off(priv);
 
@@ -2813,7 +2049,8 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
 
 	icnss_pr_dbg("Modem-Notify: event %lu\n", code);
 
-	if (code == SUBSYS_AFTER_SHUTDOWN) {
+	if (code == SUBSYS_AFTER_SHUTDOWN &&
+		notif->crashed == CRASH_STATUS_ERR_FATAL) {
 		icnss_remove_msa_permissions(priv);
 		icnss_pr_info("Collecting msa0 segment dump\n");
 		icnss_msa0_ramdump(priv);
@@ -2828,7 +2065,7 @@ static int icnss_modem_notifier_nb(struct notifier_block *nb,
 
 	icnss_pr_info("Modem went down, state: %lx\n", priv->state);
 
-	event_data = kzalloc(sizeof(*data), GFP_KERNEL);
+	event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
 
 	if (event_data == NULL)
 		return notifier_from_errno(-ENOMEM);
@@ -2880,8 +2117,9 @@ static int icnss_pdr_unregister_notifier(struct icnss_priv *priv)
 		return 0;
 
 	for (i = 0; i < priv->total_domains; i++)
-		service_notif_unregister_notifier(priv->service_notifier[i],
-						  &priv->service_notifier_nb);
+		service_notif_unregister_notifier(
+				priv->service_notifier[i].handle,
+				&priv->service_notifier_nb);
 
 	kfree(priv->service_notifier);
 
@@ -2902,7 +2140,7 @@ static int icnss_service_notifier_notify(struct notifier_block *nb,
 	case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
 		icnss_pr_info("Service down, data: 0x%p, state: 0x%lx\n", data,
 			      priv->state);
-		event_data = kzalloc(sizeof(*data), GFP_KERNEL);
+		event_data = kzalloc(sizeof(*event_data), GFP_KERNEL);
 
 		if (event_data == NULL)
 			return notifier_from_errno(-ENOMEM);
@@ -2934,7 +2172,7 @@ static int icnss_get_service_location_notify(struct notifier_block *nb,
 	int curr_state;
 	int ret;
 	int i;
-	void **handle;
+	struct service_notifier_context *notifier;
 
 	icnss_pr_dbg("Get service notify opcode: %lu, state: 0x%lx\n", opcode,
 		     priv->state);
@@ -2948,9 +2186,10 @@ static int icnss_get_service_location_notify(struct notifier_block *nb,
 		goto out;
 	}
 
-	handle = kcalloc(pd->total_domains, sizeof(void *), GFP_KERNEL);
-
-	if (!handle) {
+	notifier = kcalloc(pd->total_domains,
+				sizeof(struct service_notifier_context),
+				GFP_KERNEL);
+	if (!notifier) {
 		ret = -ENOMEM;
 		goto out;
 	}
@@ -2962,21 +2201,24 @@ static int icnss_get_service_location_notify(struct notifier_block *nb,
 			     pd->domain_list[i].name,
 			     pd->domain_list[i].instance_id);
 
-		handle[i] =
+		notifier[i].handle =
 			service_notif_register_notifier(pd->domain_list[i].name,
 				pd->domain_list[i].instance_id,
 				&priv->service_notifier_nb, &curr_state);
+		notifier[i].instance_id = pd->domain_list[i].instance_id;
+		strlcpy(notifier[i].name, pd->domain_list[i].name,
+			QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
 
-		if (IS_ERR(handle[i])) {
+		if (IS_ERR(notifier[i].handle)) {
 			icnss_pr_err("%d: Unable to register notifier for %s(0x%x)\n",
 				     i, pd->domain_list->name,
 				     pd->domain_list->instance_id);
-			ret = PTR_ERR(handle[i]);
+			ret = PTR_ERR(notifier[i].handle);
 			goto free_handle;
 		}
 	}
 
-	priv->service_notifier = handle;
+	priv->service_notifier = notifier;
 	priv->total_domains = pd->total_domains;
 
 	set_bit(ICNSS_PDR_ENABLED, &priv->state);
@@ -2987,11 +2229,11 @@ static int icnss_get_service_location_notify(struct notifier_block *nb,
 
 free_handle:
 	for (i = 0; i < pd->total_domains; i++) {
-		if (handle[i])
-			service_notif_unregister_notifier(handle[i],
+		if (notifier[i].handle)
+			service_notif_unregister_notifier(notifier[i].handle,
 					&priv->service_notifier_nb);
 	}
-	kfree(handle);
+	kfree(notifier);
 
 out:
 	icnss_pr_err("PD restart not enabled: %d, state: 0x%lx\n", ret,
@@ -3274,21 +2516,19 @@ int icnss_get_soc_info(struct icnss_soc_info *info)
 }
 EXPORT_SYMBOL(icnss_get_soc_info);
 
-int icnss_set_fw_debug_mode(bool enable_fw_log)
+int icnss_set_fw_log_mode(uint8_t fw_log_mode)
 {
 	int ret;
 
-	icnss_pr_dbg("%s FW debug mode",
-		     enable_fw_log ? "Enalbing" : "Disabling");
+	icnss_pr_dbg("FW log mode: %u\n", fw_log_mode);
 
-	ret = wlfw_ini_send_sync_msg(enable_fw_log);
+	ret = wlfw_ini_send_sync_msg(fw_log_mode);
 	if (ret)
-		icnss_pr_err("Fail to send ini, ret = %d, fw_log: %d\n", ret,
-		       enable_fw_log);
-
+		icnss_pr_err("Fail to send ini, ret = %d, fw_log_mode: %u\n",
+			     ret, fw_log_mode);
 	return ret;
 }
-EXPORT_SYMBOL(icnss_set_fw_debug_mode);
+EXPORT_SYMBOL(icnss_set_fw_log_mode);
 
 int icnss_athdiag_read(struct device *dev, uint32_t offset,
 		       uint32_t mem_type, uint32_t data_len,
@@ -3548,6 +2788,114 @@ unsigned int icnss_socinfo_get_serial_number(struct device *dev)
 }
 EXPORT_SYMBOL(icnss_socinfo_get_serial_number);
 
+int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len)
+{
+	struct icnss_priv *priv = penv;
+	uint32_t no_of_mac_addr;
+	struct icnss_wlan_mac_addr *addr = NULL;
+	int iter;
+	u8 *temp = NULL;
+
+	if (!priv) {
+		icnss_pr_err("Priv data is NULL\n");
+		return -EINVAL;
+	}
+
+	if (priv->is_wlan_mac_set) {
+		icnss_pr_dbg("WLAN MAC address is already set\n");
+		return 0;
+	}
+
+	if (len == 0 || (len % ETH_ALEN) != 0) {
+		icnss_pr_err("Invalid length %d\n", len);
+		return -EINVAL;
+	}
+
+	no_of_mac_addr = len / ETH_ALEN;
+	if (no_of_mac_addr > MAX_NO_OF_MAC_ADDR) {
+		icnss_pr_err("Exceed maxinum supported MAC address %u %u\n",
+			     MAX_NO_OF_MAC_ADDR, no_of_mac_addr);
+		return -EINVAL;
+	}
+
+	priv->is_wlan_mac_set = true;
+	addr = &priv->wlan_mac_addr;
+	addr->no_of_mac_addr_set = no_of_mac_addr;
+	temp = &addr->mac_addr[0][0];
+
+	for (iter = 0; iter < no_of_mac_addr;
+	     ++iter, temp += ETH_ALEN, in += ETH_ALEN) {
+		ether_addr_copy(temp, in);
+		icnss_pr_dbg("MAC_ADDR:%02x:%02x:%02x:%02x:%02x:%02x\n",
+			     temp[0], temp[1], temp[2],
+			     temp[3], temp[4], temp[5]);
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(icnss_set_wlan_mac_address);
+
+u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num)
+{
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+	struct icnss_wlan_mac_addr *addr = NULL;
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata: dev %p, data %p, magic 0x%x\n",
+			     dev, priv, priv->magic);
+		goto out;
+	}
+
+	if (!priv->is_wlan_mac_set) {
+		icnss_pr_dbg("WLAN MAC address is not set\n");
+		goto out;
+	}
+
+	addr = &priv->wlan_mac_addr;
+	*num = addr->no_of_mac_addr_set;
+	return &addr->mac_addr[0][0];
+out:
+	*num = 0;
+	return NULL;
+}
+EXPORT_SYMBOL(icnss_get_wlan_mac_address);
+
+int icnss_trigger_recovery(struct device *dev)
+{
+	int ret = 0;
+	struct icnss_priv *priv = dev_get_drvdata(dev);
+
+	if (priv->magic != ICNSS_MAGIC) {
+		icnss_pr_err("Invalid drvdata: magic 0x%x\n", priv->magic);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (test_bit(ICNSS_PD_RESTART, &priv->state)) {
+		icnss_pr_err("PD recovery already in progress: state: 0x%lx\n",
+			     priv->state);
+		ret = -EPERM;
+		goto out;
+	}
+
+	if (!priv->service_notifier[0].handle) {
+		icnss_pr_err("Invalid handle during recovery\n");
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/*
+	 * Initiate PDR, required only for the first instance
+	 */
+	ret = service_notif_pd_restart(priv->service_notifier[0].name,
+		priv->service_notifier[0].instance_id);
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(icnss_trigger_recovery);
+
+
 static int icnss_smmu_init(struct icnss_priv *priv)
 {
 	struct dma_iommu_mapping *mapping;
@@ -3611,144 +2959,35 @@ static void icnss_smmu_deinit(struct icnss_priv *priv)
 	priv->smmu_mapping = NULL;
 }
 
-static int icnss_get_vreg_info(struct device *dev,
-			       struct icnss_vreg_info *vreg_info)
-{
-	int ret = 0;
-	char prop_name[MAX_PROP_SIZE];
-	struct regulator *reg;
-	const __be32 *prop;
-	int len = 0;
-	int i;
-
-	reg = devm_regulator_get_optional(dev, vreg_info->name);
-
-	if (PTR_ERR(reg) == -EPROBE_DEFER) {
-		icnss_pr_err("EPROBE_DEFER for regulator: %s\n",
-			     vreg_info->name);
-		ret = PTR_ERR(reg);
-		goto out;
-	}
-
-	if (IS_ERR(reg)) {
-		ret = PTR_ERR(reg);
-
-		if (vreg_info->required) {
-
-			icnss_pr_err("Regulator %s doesn't exist: %d\n",
-				     vreg_info->name, ret);
-			goto out;
-		} else {
-			icnss_pr_dbg("Optional regulator %s doesn't exist: %d\n",
-				     vreg_info->name, ret);
-			goto done;
-		}
-	}
-
-	vreg_info->reg = reg;
-
-	snprintf(prop_name, MAX_PROP_SIZE,
-		 "qcom,%s-config", vreg_info->name);
-
-	prop = of_get_property(dev->of_node, prop_name, &len);
-
-	icnss_pr_dbg("Got regulator config, prop: %s, len: %d\n",
-		     prop_name, len);
-
-	if (!prop || len < (2 * sizeof(__be32))) {
-		icnss_pr_dbg("Property %s %s\n", prop_name,
-			     prop ? "invalid format" : "doesn't exist");
-		goto done;
-	}
-
-	for (i = 0; (i * sizeof(__be32)) < len; i++) {
-		switch (i) {
-		case 0:
-			vreg_info->min_v = be32_to_cpup(&prop[0]);
-			break;
-		case 1:
-			vreg_info->max_v = be32_to_cpup(&prop[1]);
-			break;
-		case 2:
-			vreg_info->load_ua = be32_to_cpup(&prop[2]);
-			break;
-		case 3:
-			vreg_info->settle_delay = be32_to_cpup(&prop[3]);
-			break;
-		default:
-			icnss_pr_dbg("Property %s, ignoring value at %d\n",
-				     prop_name, i);
-			break;
-		}
-	}
-
-done:
-	icnss_pr_dbg("Regulator: %s, min_v: %u, max_v: %u, load: %u, delay: %lu\n",
-		     vreg_info->name, vreg_info->min_v, vreg_info->max_v,
-		     vreg_info->load_ua, vreg_info->settle_delay);
-
-	return 0;
-
-out:
-	return ret;
-}
-
-static int icnss_get_clk_info(struct device *dev,
-			      struct icnss_clk_info *clk_info)
-{
-	struct clk *handle;
-	int ret = 0;
-
-	handle = devm_clk_get(dev, clk_info->name);
-
-	if (IS_ERR(handle)) {
-		ret = PTR_ERR(handle);
-		if (clk_info->required) {
-			icnss_pr_err("Clock %s isn't available: %d\n",
-				     clk_info->name, ret);
-			goto out;
-		} else {
-			icnss_pr_dbg("Ignoring clock %s: %d\n", clk_info->name,
-				     ret);
-			ret = 0;
-			goto out;
-		}
-	}
-
-	icnss_pr_dbg("Clock: %s, freq: %u\n", clk_info->name, clk_info->freq);
-
-	clk_info->handle = handle;
-out:
-	return ret;
-}
-
-static int icnss_test_mode_show(struct seq_file *s, void *data)
+static int icnss_fw_debug_show(struct seq_file *s, void *data)
 {
 	struct icnss_priv *priv = s->private;
 
-	seq_puts(s, "0 : Test mode disable\n");
-	seq_puts(s, "1 : WLAN Firmware test\n");
-	seq_puts(s, "2 : CCPM test\n");
+	seq_puts(s, "\nUsage: echo <CMD> <VAL> > <DEBUGFS>/icnss/fw_debug\n");
 
-	seq_puts(s, "\n");
+	seq_puts(s, "\nCMD: test_mode\n");
+	seq_puts(s, "  VAL: 0 (Test mode disable)\n");
+	seq_puts(s, "  VAL: 1 (WLAN FW test)\n");
+	seq_puts(s, "  VAL: 2 (CCPM test)\n");
+
+	seq_puts(s, "\nCMD: dynamic_feature_mask\n");
+	seq_puts(s, "  VAL: (64 bit feature mask)\n");
 
 	if (!test_bit(ICNSS_FW_READY, &priv->state)) {
-		seq_puts(s, "Firmware is not ready yet!, wait for FW READY\n");
+		seq_puts(s, "Firmware is not ready yet, can't run test_mode!\n");
 		goto out;
 	}
 
 	if (test_bit(ICNSS_DRIVER_PROBED, &priv->state)) {
-		seq_puts(s, "Machine mode is running, can't run test mode!\n");
+		seq_puts(s, "Machine mode is running, can't run test_mode!\n");
 		goto out;
 	}
 
 	if (test_bit(ICNSS_FW_TEST_MODE, &priv->state)) {
-		seq_puts(s, "Test mode is running!\n");
+		seq_puts(s, "test_mode is running, can't run test_mode!\n");
 		goto out;
 	}
 
-	seq_puts(s, "Test can be run, Have fun!\n");
-
 out:
 	seq_puts(s, "\n");
 	return 0;
@@ -3834,31 +3073,61 @@ static int icnss_test_mode_fw_test(struct icnss_priv *priv,
 	return ret;
 }
 
-static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf,
+static ssize_t icnss_fw_debug_write(struct file *fp,
+				    const char __user *user_buf,
 				    size_t count, loff_t *off)
 {
 	struct icnss_priv *priv =
 		((struct seq_file *)fp->private_data)->private;
-	int ret;
-	u32 val;
+	char buf[64];
+	char *sptr, *token;
+	unsigned int len = 0;
+	char *cmd;
+	uint64_t val;
+	const char *delim = " ";
+	int ret = 0;
 
-	ret = kstrtou32_from_user(buf, count, 0, &val);
-	if (ret)
-		return ret;
+	len = min(count, sizeof(buf) - 1);
+	if (copy_from_user(buf, user_buf, len))
+		return -EINVAL;
 
-	switch (val) {
-	case 0:
-		ret = icnss_test_mode_fw_test_off(priv);
-		break;
-	case 1:
-		ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
-		break;
-	case 2:
-		ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
-		break;
-	default:
-		ret = -EINVAL;
-		break;
+	buf[len] = '\0';
+	sptr = buf;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+	if (!sptr)
+		return -EINVAL;
+	cmd = token;
+
+	token = strsep(&sptr, delim);
+	if (!token)
+		return -EINVAL;
+	if (kstrtou64(token, 0, &val))
+		return -EINVAL;
+
+	if (strcmp(cmd, "test_mode") == 0) {
+		switch (val) {
+		case 0:
+			ret = icnss_test_mode_fw_test_off(priv);
+			break;
+		case 1:
+			ret = icnss_test_mode_fw_test(priv, ICNSS_WALTEST);
+			break;
+		case 2:
+			ret = icnss_test_mode_fw_test(priv, ICNSS_CCPM);
+			break;
+		case 3:
+			ret = icnss_trigger_recovery(&priv->pdev->dev);
+			break;
+		default:
+			return -EINVAL;
+		}
+	} else if (strcmp(cmd, "dynamic_feature_mask") == 0) {
+		ret = wlfw_dynamic_feature_mask_send_sync_msg(priv, val);
+	} else {
+		return -EINVAL;
 	}
 
 	if (ret)
@@ -3870,16 +3139,16 @@ static ssize_t icnss_test_mode_write(struct file *fp, const char __user *buf,
 	return count;
 }
 
-static int icnss_test_mode_open(struct inode *inode, struct file *file)
+static int icnss_fw_debug_open(struct inode *inode, struct file *file)
 {
-	return single_open(file, icnss_test_mode_show, inode->i_private);
+	return single_open(file, icnss_fw_debug_show, inode->i_private);
 }
 
-static const struct file_operations icnss_test_mode_fops = {
+static const struct file_operations icnss_fw_debug_fops = {
 	.read		= seq_read,
-	.write		= icnss_test_mode_write,
+	.write		= icnss_fw_debug_write,
 	.release	= single_release,
-	.open		= icnss_test_mode_open,
+	.open		= icnss_fw_debug_open,
 	.owner		= THIS_MODULE,
 	.llseek		= seq_lseek,
 };
@@ -4049,6 +3318,9 @@ static int icnss_stats_show(struct seq_file *s, void *data)
 	ICNSS_STATS_DUMP(s, priv, vbatt_req);
 	ICNSS_STATS_DUMP(s, priv, vbatt_resp);
 	ICNSS_STATS_DUMP(s, priv, vbatt_req_err);
+	ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_req);
+	ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_resp);
+	ICNSS_STATS_DUMP(s, priv, rejuvenate_ack_err);
 
 	seq_puts(s, "\n<------------------ PM stats ------------------->\n");
 	ICNSS_STATS_DUMP(s, priv, pm_suspend);
@@ -4304,8 +3576,8 @@ static int icnss_debugfs_create(struct icnss_priv *priv)
 
 	priv->root_dentry = root_dentry;
 
-	debugfs_create_file("test_mode", 0644, root_dentry, priv,
-			    &icnss_test_mode_fops);
+	debugfs_create_file("fw_debug", 0644, root_dentry, priv,
+			    &icnss_fw_debug_fops);
 
 	debugfs_create_file("stats", 0644, root_dentry, priv,
 			    &icnss_stats_fops);
@@ -4394,21 +3666,6 @@ static int icnss_probe(struct platform_device *pdev)
 	if (ret == -EPROBE_DEFER)
 		goto out;
 
-	memcpy(priv->vreg_info, icnss_vreg_info, sizeof(icnss_vreg_info));
-	for (i = 0; i < ICNSS_VREG_INFO_SIZE; i++) {
-		ret = icnss_get_vreg_info(dev, &priv->vreg_info[i]);
-
-		if (ret)
-			goto out;
-	}
-
-	memcpy(priv->clk_info, icnss_clk_info, sizeof(icnss_clk_info));
-	for (i = 0; i < ICNSS_CLK_INFO_SIZE; i++) {
-		ret = icnss_get_clk_info(dev, &priv->clk_info[i]);
-		if (ret)
-			goto out;
-	}
-
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "membase");
 	if (!res) {
 		icnss_pr_err("Memory base not found in DT\n");
@@ -4428,26 +3685,6 @@ static int icnss_probe(struct platform_device *pdev)
 	icnss_pr_dbg("MEM_BASE pa: %pa, va: 0x%p\n", &priv->mem_base_pa,
 		     priv->mem_base_va);
 
-	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-					   "mpm_config");
-	if (!res) {
-		icnss_pr_err("MPM Config not found\n");
-		ret = -EINVAL;
-		goto out;
-	}
-	priv->mpm_config_pa = res->start;
-	priv->mpm_config_va = devm_ioremap(dev, priv->mpm_config_pa,
-					   resource_size(res));
-	if (!priv->mpm_config_va) {
-		icnss_pr_err("MPM Config ioremap failed, phy addr: %pa\n",
-			     &priv->mpm_config_pa);
-		ret = -EINVAL;
-		goto out;
-	}
-
-	icnss_pr_dbg("MPM_CONFIG pa: %pa, va: 0x%p\n", &priv->mpm_config_pa,
-		     priv->mpm_config_va);
-
 	for (i = 0; i < ICNSS_MAX_IRQ_REGISTRATIONS; i++) {
 		res = platform_get_resource(priv->pdev, IORESOURCE_IRQ, i);
 		if (!res) {
diff --git a/drivers/soc/qcom/pil-q6v5-mss.c b/drivers/soc/qcom/pil-q6v5-mss.c
index 9308b8d..ec0187a 100644
--- a/drivers/soc/qcom/pil-q6v5-mss.c
+++ b/drivers/soc/qcom/pil-q6v5-mss.c
@@ -80,7 +80,7 @@ static irqreturn_t modem_err_fatal_intr_handler(int irq, void *dev_id)
 		return IRQ_HANDLED;
 
 	pr_err("Fatal error on the modem.\n");
-	subsys_set_crash_status(drv->subsys, true);
+	subsys_set_crash_status(drv->subsys, CRASH_STATUS_ERR_FATAL);
 	restart_modem(drv);
 	return IRQ_HANDLED;
 }
@@ -194,7 +194,7 @@ static irqreturn_t modem_wdog_bite_intr_handler(int irq, void *dev_id)
 			!gpio_get_value(drv->subsys_desc.err_fatal_gpio))
 		panic("%s: System ramdump requested. Triggering device restart!\n",
 							__func__);
-	subsys_set_crash_status(drv->subsys, true);
+	subsys_set_crash_status(drv->subsys, CRASH_STATUS_WDOG_BITE);
 	restart_modem(drv);
 	return IRQ_HANDLED;
 }
diff --git a/drivers/soc/qcom/pil-q6v5.c b/drivers/soc/qcom/pil-q6v5.c
index ee528f8..fb4d0ea 100644
--- a/drivers/soc/qcom/pil-q6v5.c
+++ b/drivers/soc/qcom/pil-q6v5.c
@@ -204,9 +204,9 @@ void pil_q6v5_halt_axi_port(struct pil_desc *pil, void __iomem *halt_base)
 	ret = readl_poll_timeout(halt_base + AXI_HALTACK,
 		status, status != 0, 50, HALT_ACK_TIMEOUT_US);
 	if (ret)
-		dev_warn(pil->dev, "Port %p halt timeout\n", halt_base);
+		dev_warn(pil->dev, "Port %pK halt timeout\n", halt_base);
 	else if (!readl_relaxed(halt_base + AXI_IDLE))
-		dev_warn(pil->dev, "Port %p halt failed\n", halt_base);
+		dev_warn(pil->dev, "Port %pK halt failed\n", halt_base);
 
 	/* Clear halt request (port will remain halted until reset) */
 	writel_relaxed(0, halt_base + AXI_HALTREQ);
@@ -435,7 +435,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
 	mb();
 	udelay(1);
 
-	if (drv->qdsp6v62_1_2 || drv->qdsp6v62_1_5) {
+	if (drv->qdsp6v62_1_2 || drv->qdsp6v62_1_5 || drv->qdsp6v62_1_4) {
 		for (i = BHS_CHECK_MAX_LOOPS; i > 0; i--) {
 			if (readl_relaxed(drv->reg_base + QDSP6V62SS_BHS_STATUS)
 			    & QDSP6v55_BHS_EN_REST_ACK)
@@ -536,7 +536,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
 			udelay(1);
 		}
 	} else if (drv->qdsp6v61_1_1 || drv->qdsp6v62_1_2 ||
-						drv->qdsp6v62_1_5) {
+			drv->qdsp6v62_1_4 || drv->qdsp6v62_1_5) {
 		/* Deassert QDSP6 compiler memory clamp */
 		val = readl_relaxed(drv->reg_base + QDSP6SS_PWR_CTL);
 		val &= ~QDSP6v55_CLAMP_QMC_MEM;
@@ -550,7 +550,7 @@ static int __pil_q6v55_reset(struct pil_desc *pil)
 		val = readl_relaxed(drv->reg_base +
 				QDSP6V6SS_MEM_PWR_CTL);
 
-		if (drv->qdsp6v62_1_5)
+		if (drv->qdsp6v62_1_4 || drv->qdsp6v62_1_5)
 			i = 29;
 		else
 			i = 28;
@@ -721,6 +721,9 @@ struct q6v5_data *pil_q6v5_init(struct platform_device *pdev)
 	drv->qdsp6v62_1_2 = of_property_read_bool(pdev->dev.of_node,
 						"qcom,qdsp6v62-1-2");
 
+	drv->qdsp6v62_1_4 = of_property_read_bool(pdev->dev.of_node,
+						"qcom,qdsp6v62-1-4");
+
 	drv->qdsp6v62_1_5 = of_property_read_bool(pdev->dev.of_node,
 						"qcom,qdsp6v62-1-5");
 
diff --git a/drivers/soc/qcom/pil-q6v5.h b/drivers/soc/qcom/pil-q6v5.h
index 7f7bb97..49aee97 100644
--- a/drivers/soc/qcom/pil-q6v5.h
+++ b/drivers/soc/qcom/pil-q6v5.h
@@ -63,6 +63,7 @@ struct q6v5_data {
 	bool qdsp6v56_1_10;
 	bool qdsp6v61_1_1;
 	bool qdsp6v62_1_2;
+	bool qdsp6v62_1_4;
 	bool qdsp6v62_1_5;
 	bool qdsp6v65_1_0;
 	bool non_elf_image;
diff --git a/drivers/soc/qcom/qdsp6v2/Makefile b/drivers/soc/qcom/qdsp6v2/Makefile
new file mode 100644
index 0000000..f3505ba
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/Makefile
@@ -0,0 +1,9 @@
+obj-$(CONFIG_MSM_QDSP6_APRV2) += apr.o apr_v2.o apr_tal.o voice_svc.o
+obj-$(CONFIG_MSM_QDSP6_APRV3) += apr.o apr_v3.o apr_tal.o voice_svc.o
+obj-$(CONFIG_MSM_QDSP6_APRV2_GLINK) += apr.o apr_v2.o apr_tal_glink.o voice_svc.o
+obj-$(CONFIG_MSM_QDSP6_APRV3_GLINK) += apr.o apr_v3.o apr_tal_glink.o voice_svc.o
+obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += msm_audio_ion.o
+obj-$(CONFIG_MSM_ADSP_LOADER) += adsp-loader.o
+obj-$(CONFIG_MSM_QDSP6_SSR) += audio_ssr.o
+obj-$(CONFIG_MSM_QDSP6_PDR) += audio_pdr.o
+obj-$(CONFIG_MSM_QDSP6_NOTIFIER) += audio_notifier.o
diff --git a/drivers/soc/qcom/qdsp6v2/adsp-loader.c b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
new file mode 100644
index 0000000..1bde1bf
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/adsp-loader.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/of_device.h>
+#include <linux/sysfs.h>
+#include <soc/qcom/subsystem_restart.h>
+
+#define Q6_PIL_GET_DELAY_MS 100
+#define BOOT_CMD 1
+#define IMAGE_UNLOAD_CMD 0
+
+static ssize_t adsp_boot_store(struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf, size_t count);
+
+struct adsp_loader_private {
+	void *pil_h;
+	struct kobject *boot_adsp_obj;
+	struct attribute_group *attr_group;
+};
+
+static struct kobj_attribute adsp_boot_attribute =
+	__ATTR(boot, 0220, NULL, adsp_boot_store);
+
+static struct attribute *attrs[] = {
+	&adsp_boot_attribute.attr,
+	NULL,
+};
+
+static struct platform_device *adsp_private;
+static void adsp_loader_unload(struct platform_device *pdev);
+
+static void adsp_loader_do(struct platform_device *pdev)
+{
+
+	struct adsp_loader_private *priv = NULL;
+
+	const char *adsp_dt = "qcom,adsp-state";
+	int rc = 0;
+	u32 adsp_state;
+	const char *img_name;
+
+	if (!pdev) {
+		dev_err(&pdev->dev, "%s: Platform device null\n", __func__);
+		goto fail;
+	}
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev,
+			"%s: Device tree information missing\n", __func__);
+		goto fail;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, adsp_dt, &adsp_state);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: ADSP state = %x\n", __func__, adsp_state);
+		goto fail;
+	}
+
+	rc = of_property_read_string(pdev->dev.of_node,
+					"qcom,proc-img-to-load",
+					&img_name);
+
+	if (rc) {
+		dev_dbg(&pdev->dev,
+			"%s: loading default image ADSP\n", __func__);
+		goto load_adsp;
+	}
+	if (!strcmp(img_name, "modem")) {
+		/* adsp_state always returns "0". So load modem image based on
+		 * apr_modem_state to prevent loading of image twice
+		 */
+		adsp_state = apr_get_modem_state();
+		if (adsp_state == APR_SUBSYS_DOWN) {
+			priv = platform_get_drvdata(pdev);
+			if (!priv) {
+				dev_err(&pdev->dev,
+				" %s: Private data get failed\n", __func__);
+				goto fail;
+			}
+
+			priv->pil_h = subsystem_get("modem");
+			if (IS_ERR(priv->pil_h)) {
+				dev_err(&pdev->dev, "%s: pil get failed,\n",
+					__func__);
+				goto fail;
+			}
+
+			/* Set the state of the ADSP in APR driver */
+			apr_set_modem_state(APR_SUBSYS_LOADED);
+		} else if (adsp_state == APR_SUBSYS_LOADED) {
+			dev_dbg(&pdev->dev,
+			"%s: MDSP state = %x\n", __func__, adsp_state);
+		}
+
+		dev_dbg(&pdev->dev, "%s: Q6/MDSP image is loaded\n", __func__);
+		return;
+	}
+load_adsp:
+	{
+		adsp_state = apr_get_q6_state();
+		if (adsp_state == APR_SUBSYS_DOWN) {
+			priv = platform_get_drvdata(pdev);
+			if (!priv) {
+				dev_err(&pdev->dev,
+				" %s: Private data get failed\n", __func__);
+				goto fail;
+			}
+
+			priv->pil_h = subsystem_get("adsp");
+			if (IS_ERR(priv->pil_h)) {
+				dev_err(&pdev->dev, "%s: pil get failed,\n",
+					__func__);
+				goto fail;
+			}
+
+			/* Set the state of the ADSP in APR driver */
+			apr_set_q6_state(APR_SUBSYS_LOADED);
+		} else if (adsp_state == APR_SUBSYS_LOADED) {
+			dev_dbg(&pdev->dev,
+			"%s: ADSP state = %x\n", __func__, adsp_state);
+		}
+
+		dev_dbg(&pdev->dev, "%s: Q6/ADSP image is loaded\n", __func__);
+		return;
+	}
+fail:
+	dev_err(&pdev->dev, "%s: Q6 image loading failed\n", __func__);
+}
+
+
+static ssize_t adsp_boot_store(struct kobject *kobj,
+	struct kobj_attribute *attr,
+	const char *buf,
+	size_t count)
+{
+	int boot = 0;
+
+	if (sscanf(buf, "%du", &boot) != 1) {
+		pr_err("%s: failed to read boot info from string\n", __func__);
+		return -EINVAL;
+	}
+
+	if (boot == BOOT_CMD) {
+		pr_debug("%s: going to call adsp_loader_do\n", __func__);
+		adsp_loader_do(adsp_private);
+	} else if (boot == IMAGE_UNLOAD_CMD) {
+		pr_debug("%s: going to call adsp_unloader\n", __func__);
+		adsp_loader_unload(adsp_private);
+	}
+	return count;
+}
+
+static void adsp_loader_unload(struct platform_device *pdev)
+{
+	struct adsp_loader_private *priv = NULL;
+
+	priv = platform_get_drvdata(pdev);
+
+	if (!priv)
+		return;
+
+	if (priv->pil_h) {
+		dev_dbg(&pdev->dev, "%s: calling subsystem put\n", __func__);
+		subsystem_put(priv->pil_h);
+		priv->pil_h = NULL;
+	}
+}
+
+static int adsp_loader_init_sysfs(struct platform_device *pdev)
+{
+	int ret = -EINVAL;
+	struct adsp_loader_private *priv = NULL;
+
+	adsp_private = NULL;
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		ret = -ENOMEM;
+		return ret;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	priv->pil_h = NULL;
+	priv->boot_adsp_obj = NULL;
+	priv->attr_group = devm_kzalloc(&pdev->dev,
+				sizeof(*(priv->attr_group)),
+				GFP_KERNEL);
+	if (!priv->attr_group) {
+		ret = -ENOMEM;
+		goto error_return;
+	}
+
+	priv->attr_group->attrs = attrs;
+
+	priv->boot_adsp_obj = kobject_create_and_add("boot_adsp", kernel_kobj);
+	if (!priv->boot_adsp_obj) {
+		dev_err(&pdev->dev, "%s: sysfs create and add failed\n",
+						__func__);
+		ret = -ENOMEM;
+		goto error_return;
+	}
+
+	ret = sysfs_create_group(priv->boot_adsp_obj, priv->attr_group);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: sysfs create group failed %d\n",
+							__func__, ret);
+		goto error_return;
+	}
+
+	adsp_private = pdev;
+
+	return 0;
+
+error_return:
+
+	if (priv->boot_adsp_obj) {
+		kobject_del(priv->boot_adsp_obj);
+		priv->boot_adsp_obj = NULL;
+	}
+
+	return ret;
+}
+
+static int adsp_loader_remove(struct platform_device *pdev)
+{
+	struct adsp_loader_private *priv = NULL;
+
+	priv = platform_get_drvdata(pdev);
+
+	if (!priv)
+		return 0;
+
+	if (priv->pil_h) {
+		subsystem_put(priv->pil_h);
+		priv->pil_h = NULL;
+	}
+
+	if (priv->boot_adsp_obj) {
+		sysfs_remove_group(priv->boot_adsp_obj, priv->attr_group);
+		kobject_del(priv->boot_adsp_obj);
+		priv->boot_adsp_obj = NULL;
+	}
+
+	return 0;
+}
+
+static int adsp_loader_probe(struct platform_device *pdev)
+{
+	int ret = adsp_loader_init_sysfs(pdev);
+
+	if (ret != 0) {
+		dev_err(&pdev->dev, "%s: Error in initing sysfs\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id adsp_loader_dt_match[] = {
+	{ .compatible = "qcom,adsp-loader" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, adsp_loader_dt_match);
+
+static struct platform_driver adsp_loader_driver = {
+	.driver = {
+		.name = "adsp-loader",
+		.owner = THIS_MODULE,
+		.of_match_table = adsp_loader_dt_match,
+	},
+	.probe = adsp_loader_probe,
+	.remove = adsp_loader_remove,
+};
+
+static int __init adsp_loader_init(void)
+{
+	return platform_driver_register(&adsp_loader_driver);
+}
+module_init(adsp_loader_init);
+
+static void __exit adsp_loader_exit(void)
+{
+	platform_driver_unregister(&adsp_loader_driver);
+}
+module_exit(adsp_loader_exit);
+
+MODULE_DESCRIPTION("ADSP Loader module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/qdsp6v2/apr.c b/drivers/soc/qcom/qdsp6v2/apr.c
new file mode 100644
index 0000000..5d860a3
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr.c
@@ -0,0 +1,969 @@
+/* Copyright (c) 2010-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/sysfs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/scm.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <linux/qdsp6v2/dsp_debug.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include <linux/ipc_logging.h>
+
+#define APR_PKT_IPC_LOG_PAGE_CNT 2
+
+static struct apr_q6 q6;
+static struct apr_client client[APR_DEST_MAX][APR_CLIENT_MAX];
+static void *apr_pkt_ctx;
+static wait_queue_head_t dsp_wait;
+static wait_queue_head_t modem_wait;
+static bool is_modem_up;
+static bool is_initial_boot;
+/* Subsystem restart: QDSP6 data, functions */
+static struct workqueue_struct *apr_reset_workqueue;
+static void apr_reset_deregister(struct work_struct *work);
+static void dispatch_event(unsigned long code, uint16_t proc);
+struct apr_reset_work {
+	void *handle;
+	struct work_struct work;
+};
+
+static bool apr_cf_debug;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_apr_debug;
+static ssize_t apr_debug_write(struct file *filp, const char __user *ubuf,
+			       size_t cnt, loff_t *ppos)
+{
+	char cmd;
+
+	if (copy_from_user(&cmd, ubuf, 1))
+		return -EFAULT;
+
+	apr_cf_debug = (cmd == '1') ? true : false;
+
+	return cnt;
+}
+
+static const struct file_operations apr_debug_ops = {
+	.write = apr_debug_write,
+};
+#endif
+
+#define APR_PKT_INFO(x...) \
+do { \
+	if (apr_pkt_ctx) \
+		ipc_log_string(apr_pkt_ctx, "<APR>: "x); \
+} while (0)
+
+
+struct apr_svc_table {
+	char name[64];
+	int idx;
+	int id;
+	int client_id;
+};
+
+static const struct apr_svc_table svc_tbl_qdsp6[] = {
+	{
+		.name = "AFE",
+		.idx = 0,
+		.id = APR_SVC_AFE,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "ASM",
+		.idx = 1,
+		.id = APR_SVC_ASM,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "ADM",
+		.idx = 2,
+		.id = APR_SVC_ADM,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "CORE",
+		.idx = 3,
+		.id = APR_SVC_ADSP_CORE,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "TEST",
+		.idx = 4,
+		.id = APR_SVC_TEST_CLIENT,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "MVM",
+		.idx = 5,
+		.id = APR_SVC_ADSP_MVM,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "CVS",
+		.idx = 6,
+		.id = APR_SVC_ADSP_CVS,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "CVP",
+		.idx = 7,
+		.id = APR_SVC_ADSP_CVP,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "USM",
+		.idx = 8,
+		.id = APR_SVC_USM,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+	{
+		.name = "VIDC",
+		.idx = 9,
+		.id = APR_SVC_VIDC,
+	},
+	{
+		.name = "LSM",
+		.idx = 10,
+		.id = APR_SVC_LSM,
+		.client_id = APR_CLIENT_AUDIO,
+	},
+};
+
+static struct apr_svc_table svc_tbl_voice[] = {
+	{
+		.name = "VSM",
+		.idx = 0,
+		.id = APR_SVC_VSM,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "VPM",
+		.idx = 1,
+		.id = APR_SVC_VPM,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "MVS",
+		.idx = 2,
+		.id = APR_SVC_MVS,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "MVM",
+		.idx = 3,
+		.id = APR_SVC_MVM,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "CVS",
+		.idx = 4,
+		.id = APR_SVC_CVS,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "CVP",
+		.idx = 5,
+		.id = APR_SVC_CVP,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "SRD",
+		.idx = 6,
+		.id = APR_SVC_SRD,
+		.client_id = APR_CLIENT_VOICE,
+	},
+	{
+		.name = "TEST",
+		.idx = 7,
+		.id = APR_SVC_TEST_CLIENT,
+		.client_id = APR_CLIENT_VOICE,
+	},
+};
+
+enum apr_subsys_state apr_get_modem_state(void)
+{
+	return atomic_read(&q6.modem_state);
+}
+
+void apr_set_modem_state(enum apr_subsys_state state)
+{
+	atomic_set(&q6.modem_state, state);
+}
+
+enum apr_subsys_state apr_cmpxchg_modem_state(enum apr_subsys_state prev,
+					      enum apr_subsys_state new)
+{
+	return atomic_cmpxchg(&q6.modem_state, prev, new);
+}
+
+static void apr_modem_down(unsigned long opcode)
+{
+	apr_set_modem_state(APR_SUBSYS_DOWN);
+	dispatch_event(opcode, APR_DEST_MODEM);
+}
+
+static void apr_modem_up(void)
+{
+	if (apr_cmpxchg_modem_state(APR_SUBSYS_DOWN, APR_SUBSYS_UP) ==
+							APR_SUBSYS_DOWN)
+		wake_up(&modem_wait);
+	is_modem_up = 1;
+}
+
+enum apr_subsys_state apr_get_q6_state(void)
+{
+	return atomic_read(&q6.q6_state);
+}
+EXPORT_SYMBOL(apr_get_q6_state);
+
+int apr_set_q6_state(enum apr_subsys_state state)
+{
+	pr_debug("%s: setting adsp state %d\n", __func__, state);
+	if (state < APR_SUBSYS_DOWN || state > APR_SUBSYS_LOADED)
+		return -EINVAL;
+	atomic_set(&q6.q6_state, state);
+	return 0;
+}
+EXPORT_SYMBOL(apr_set_q6_state);
+
+enum apr_subsys_state apr_cmpxchg_q6_state(enum apr_subsys_state prev,
+					   enum apr_subsys_state new)
+{
+	return atomic_cmpxchg(&q6.q6_state, prev, new);
+}
+
+static void apr_adsp_down(unsigned long opcode)
+{
+	apr_set_q6_state(APR_SUBSYS_DOWN);
+	dispatch_event(opcode, APR_DEST_QDSP6);
+}
+
+static void apr_adsp_up(void)
+{
+	if (apr_cmpxchg_q6_state(APR_SUBSYS_DOWN, APR_SUBSYS_LOADED) ==
+							APR_SUBSYS_DOWN)
+		wake_up(&dsp_wait);
+}
+
+int apr_wait_for_device_up(int dest_id)
+{
+	int rc = -1;
+
+	if (dest_id == APR_DEST_MODEM)
+		rc = wait_event_interruptible_timeout(modem_wait,
+				    (apr_get_modem_state() == APR_SUBSYS_UP),
+				    (1 * HZ));
+	else if (dest_id == APR_DEST_QDSP6)
+		rc = wait_event_interruptible_timeout(dsp_wait,
+				    (apr_get_q6_state() == APR_SUBSYS_UP),
+				    (1 * HZ));
+	else
+		pr_err("%s: unknown dest_id %d\n", __func__, dest_id);
+	/* returns left time */
+	return rc;
+}
+
+int apr_load_adsp_image(void)
+{
+	int rc = 0;
+
+	mutex_lock(&q6.lock);
+	if (apr_get_q6_state() == APR_SUBSYS_UP) {
+		q6.pil = subsystem_get("adsp");
+		if (IS_ERR(q6.pil)) {
+			rc = PTR_ERR(q6.pil);
+			pr_err("APR: Unable to load q6 image, error:%d\n", rc);
+		} else {
+			apr_set_q6_state(APR_SUBSYS_LOADED);
+			pr_debug("APR: Image is loaded, stated\n");
+		}
+	} else if (apr_get_q6_state() == APR_SUBSYS_LOADED) {
+		pr_debug("APR: q6 image already loaded\n");
+	} else {
+		pr_debug("APR: cannot load state %d\n", apr_get_q6_state());
+	}
+	mutex_unlock(&q6.lock);
+	return rc;
+}
+
+struct apr_client *apr_get_client(int dest_id, int client_id)
+{
+	return &client[dest_id][client_id];
+}
+
+int apr_send_pkt(void *handle, uint32_t *buf)
+{
+	struct apr_svc *svc = handle;
+	struct apr_client *clnt;
+	struct apr_hdr *hdr;
+	uint16_t dest_id;
+	uint16_t client_id;
+	uint16_t w_len;
+	int rc;
+	unsigned long flags;
+
+	if (!handle || !buf) {
+		pr_err("APR: Wrong parameters\n");
+		return -EINVAL;
+	}
+	if (svc->need_reset) {
+		pr_err("apr: send_pkt service need reset\n");
+		return -ENETRESET;
+	}
+
+	if ((svc->dest_id == APR_DEST_QDSP6) &&
+	    (apr_get_q6_state() != APR_SUBSYS_LOADED)) {
+		pr_err("%s: Still dsp is not Up\n", __func__);
+		return -ENETRESET;
+	} else if ((svc->dest_id == APR_DEST_MODEM) &&
+		   (apr_get_modem_state() == APR_SUBSYS_DOWN)) {
+		pr_err("apr: Still Modem is not Up\n");
+		return -ENETRESET;
+	}
+
+	spin_lock_irqsave(&svc->w_lock, flags);
+	dest_id = svc->dest_id;
+	client_id = svc->client_id;
+	clnt = &client[dest_id][client_id];
+
+	if (!client[dest_id][client_id].handle) {
+		pr_err("APR: Still service is not yet opened\n");
+		spin_unlock_irqrestore(&svc->w_lock, flags);
+		return -EINVAL;
+	}
+	hdr = (struct apr_hdr *)buf;
+
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->src_svc = svc->id;
+	hdr->dest_domain = svc->dest_domain;
+	hdr->dest_svc = svc->id;
+
+	if (unlikely(apr_cf_debug)) {
+		APR_PKT_INFO(
+		"Tx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
+		(hdr->src_domain << 8) | hdr->src_svc,
+		(hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
+		hdr->token);
+	}
+
+	rc = apr_tal_write(clnt->handle, buf,
+			(struct apr_pkt_priv *)&svc->pkt_owner,
+			hdr->pkt_size);
+	if (rc >= 0) {
+		w_len = rc;
+		if (w_len != hdr->pkt_size) {
+			pr_err("%s: Unable to write whole APR pkt successfully: %d\n",
+			       __func__, rc);
+			rc = -EINVAL;
+		}
+	} else {
+		pr_err("%s: Write APR pkt failed with error %d\n",
+			__func__, rc);
+	}
+	spin_unlock_irqrestore(&svc->w_lock, flags);
+
+	return rc;
+}
+
+int apr_pkt_config(void *handle, struct apr_pkt_cfg *cfg)
+{
+	struct apr_svc *svc = (struct apr_svc *)handle;
+	uint16_t dest_id;
+	uint16_t client_id;
+	struct apr_client *clnt;
+
+	if (!handle) {
+		pr_err("%s: Invalid handle\n", __func__);
+		return -EINVAL;
+	}
+
+	if (svc->need_reset) {
+		pr_err("%s: service need reset\n", __func__);
+		return -ENETRESET;
+	}
+
+	svc->pkt_owner = cfg->pkt_owner;
+	dest_id = svc->dest_id;
+	client_id = svc->client_id;
+	clnt = &client[dest_id][client_id];
+
+	return apr_tal_rx_intents_config(clnt->handle,
+		cfg->intents.num_of_intents, cfg->intents.size);
+}
+
+struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
+				uint32_t src_port, void *priv)
+{
+	struct apr_client *clnt;
+	int client_id = 0;
+	int svc_idx = 0;
+	int svc_id = 0;
+	int dest_id = 0;
+	int domain_id = 0;
+	int temp_port = 0;
+	struct apr_svc *svc = NULL;
+	int rc = 0;
+	bool can_open_channel = true;
+
+	if (!dest || !svc_name || !svc_fn)
+		return NULL;
+
+	if (!strcmp(dest, "ADSP"))
+		domain_id = APR_DOMAIN_ADSP;
+	else if (!strcmp(dest, "MODEM")) {
+		/* Don't request for SMD channels if destination is MODEM,
+		 * as these channels are no longer used and these clients
+		 * are to listen only for MODEM SSR events
+		 */
+		can_open_channel = false;
+		domain_id = APR_DOMAIN_MODEM;
+	} else {
+		pr_err("APR: wrong destination\n");
+		goto done;
+	}
+
+	dest_id = apr_get_dest_id(dest);
+
+	if (dest_id == APR_DEST_QDSP6) {
+		if (apr_get_q6_state() != APR_SUBSYS_LOADED) {
+			pr_err("%s: adsp not up\n", __func__);
+			return NULL;
+		}
+		pr_debug("%s: adsp Up\n", __func__);
+	} else if (dest_id == APR_DEST_MODEM) {
+		if (apr_get_modem_state() == APR_SUBSYS_DOWN) {
+			if (is_modem_up) {
+				pr_err("%s: modem shutdown due to SSR, ret",
+					__func__);
+				return NULL;
+			}
+			pr_debug("%s: Wait for modem to bootup\n", __func__);
+			rc = apr_wait_for_device_up(APR_DEST_MODEM);
+			if (rc == 0) {
+				pr_err("%s: Modem is not Up\n", __func__);
+				return NULL;
+			}
+		}
+		pr_debug("%s: modem Up\n", __func__);
+	}
+
+	if (apr_get_svc(svc_name, domain_id, &client_id, &svc_idx, &svc_id)) {
+		pr_err("%s: apr_get_svc failed\n", __func__);
+		goto done;
+	}
+
+	clnt = &client[dest_id][client_id];
+	mutex_lock(&clnt->m_lock);
+	if (!clnt->handle && can_open_channel) {
+		clnt->handle = apr_tal_open(client_id, dest_id,
+				APR_DL_SMD, apr_cb_func, NULL);
+		if (!clnt->handle) {
+			svc = NULL;
+			pr_err("APR: Unable to open handle\n");
+			mutex_unlock(&clnt->m_lock);
+			goto done;
+		}
+	}
+	mutex_unlock(&clnt->m_lock);
+	svc = &clnt->svc[svc_idx];
+	mutex_lock(&svc->m_lock);
+	clnt->id = client_id;
+	if (svc->need_reset) {
+		mutex_unlock(&svc->m_lock);
+		pr_err("APR: Service needs reset\n");
+		goto done;
+	}
+	svc->id = svc_id;
+	svc->dest_id = dest_id;
+	svc->client_id = client_id;
+	svc->dest_domain = domain_id;
+	svc->pkt_owner = APR_PKT_OWNER_DRIVER;
+
+	if (src_port != 0xFFFFFFFF) {
+		temp_port = ((src_port >> 8) * 8) + (src_port & 0xFF);
+		pr_debug("port = %d t_port = %d\n", src_port, temp_port);
+		if (temp_port >= APR_MAX_PORTS || temp_port < 0) {
+			pr_err("APR: temp_port out of bounds\n");
+			mutex_unlock(&svc->m_lock);
+			return NULL;
+		}
+		if (!svc->port_cnt && !svc->svc_cnt)
+			clnt->svc_cnt++;
+		svc->port_cnt++;
+		svc->port_fn[temp_port] = svc_fn;
+		svc->port_priv[temp_port] = priv;
+	} else {
+		if (!svc->fn) {
+			if (!svc->port_cnt && !svc->svc_cnt)
+				clnt->svc_cnt++;
+			svc->fn = svc_fn;
+			if (svc->port_cnt)
+				svc->svc_cnt++;
+			svc->priv = priv;
+		}
+	}
+
+	mutex_unlock(&svc->m_lock);
+done:
+	return svc;
+}
+
+
+void apr_cb_func(void *buf, int len, void *priv)
+{
+	struct apr_client_data data;
+	struct apr_client *apr_client;
+	struct apr_svc *c_svc;
+	struct apr_hdr *hdr;
+	uint16_t hdr_size;
+	uint16_t msg_type;
+	uint16_t ver;
+	uint16_t src;
+	uint16_t svc;
+	uint16_t clnt;
+	int i;
+	int temp_port = 0;
+	uint32_t *ptr;
+
+	pr_debug("APR2: len = %d\n", len);
+	ptr = buf;
+	pr_debug("\n*****************\n");
+	for (i = 0; i < len/4; i++)
+		pr_debug("%x  ", ptr[i]);
+	pr_debug("\n");
+	pr_debug("\n*****************\n");
+
+	if (!buf || len <= APR_HDR_SIZE) {
+		pr_err("APR: Improper apr pkt received:%pK %d\n", buf, len);
+		return;
+	}
+	hdr = buf;
+
+	ver = hdr->hdr_field;
+	ver = (ver & 0x000F);
+	if (ver > APR_PKT_VER + 1) {
+		pr_err("APR: Wrong version: %d\n", ver);
+		return;
+	}
+
+	hdr_size = hdr->hdr_field;
+	hdr_size = ((hdr_size & 0x00F0) >> 0x4) * 4;
+	if (hdr_size < APR_HDR_SIZE) {
+		pr_err("APR: Wrong hdr size:%d\n", hdr_size);
+		return;
+	}
+
+	if (hdr->pkt_size < APR_HDR_SIZE) {
+		pr_err("APR: Wrong paket size\n");
+		return;
+	}
+	msg_type = hdr->hdr_field;
+	msg_type = (msg_type >> 0x08) & 0x0003;
+	if (msg_type >= APR_MSG_TYPE_MAX && msg_type != APR_BASIC_RSP_RESULT) {
+		pr_err("APR: Wrong message type: %d\n", msg_type);
+		return;
+	}
+
+	if (hdr->src_domain >= APR_DOMAIN_MAX ||
+		hdr->dest_domain >= APR_DOMAIN_MAX ||
+		hdr->src_svc >= APR_SVC_MAX ||
+		hdr->dest_svc >= APR_SVC_MAX) {
+		pr_err("APR: Wrong APR header\n");
+		return;
+	}
+
+	svc = hdr->dest_svc;
+	if (hdr->src_domain == APR_DOMAIN_MODEM) {
+		if (svc == APR_SVC_MVS || svc == APR_SVC_MVM ||
+		    svc == APR_SVC_CVS || svc == APR_SVC_CVP ||
+		    svc == APR_SVC_TEST_CLIENT)
+			clnt = APR_CLIENT_VOICE;
+		else {
+			pr_err("APR: Wrong svc :%d\n", svc);
+			return;
+		}
+	} else if (hdr->src_domain == APR_DOMAIN_ADSP) {
+		if (svc == APR_SVC_AFE || svc == APR_SVC_ASM ||
+		    svc == APR_SVC_VSM || svc == APR_SVC_VPM ||
+		    svc == APR_SVC_ADM || svc == APR_SVC_ADSP_CORE ||
+		    svc == APR_SVC_USM ||
+		    svc == APR_SVC_TEST_CLIENT || svc == APR_SVC_ADSP_MVM ||
+		    svc == APR_SVC_ADSP_CVS || svc == APR_SVC_ADSP_CVP ||
+		    svc == APR_SVC_LSM)
+			clnt = APR_CLIENT_AUDIO;
+		else if (svc == APR_SVC_VIDC)
+			clnt = APR_CLIENT_AUDIO;
+		else {
+			pr_err("APR: Wrong svc :%d\n", svc);
+			return;
+		}
+	} else {
+		pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
+		return;
+	}
+
+	src = apr_get_data_src(hdr);
+	if (src == APR_DEST_MAX)
+		return;
+
+	pr_debug("src =%d clnt = %d\n", src, clnt);
+	apr_client = &client[src][clnt];
+	for (i = 0; i < APR_SVC_MAX; i++)
+		if (apr_client->svc[i].id == svc) {
+			pr_debug("%d\n", apr_client->svc[i].id);
+			c_svc = &apr_client->svc[i];
+			break;
+		}
+
+	if (i == APR_SVC_MAX) {
+		pr_err("APR: service is not registered\n");
+		return;
+	}
+	pr_debug("svc_idx = %d\n", i);
+	pr_debug("%x %x %x %pK %pK\n", c_svc->id, c_svc->dest_id,
+		 c_svc->client_id, c_svc->fn, c_svc->priv);
+	data.payload_size = hdr->pkt_size - hdr_size;
+	data.opcode = hdr->opcode;
+	data.src = src;
+	data.src_port = hdr->src_port;
+	data.dest_port = hdr->dest_port;
+	data.token = hdr->token;
+	data.msg_type = msg_type;
+	data.payload = NULL;
+	if (data.payload_size > 0)
+		data.payload = (char *)hdr + hdr_size;
+
+	if (unlikely(apr_cf_debug)) {
+		if (hdr->opcode == APR_BASIC_RSP_RESULT && data.payload) {
+			uint32_t *ptr = data.payload;
+
+			APR_PKT_INFO(
+			"Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X] rc[0x%X]",
+			(hdr->src_domain << 8) | hdr->src_svc,
+			(hdr->dest_domain << 8) | hdr->dest_svc,
+			hdr->opcode, hdr->token, ptr[1]);
+		} else {
+			APR_PKT_INFO(
+			"Rx: src_addr[0x%X] dest_addr[0x%X] opcode[0x%X] token[0x%X]",
+			(hdr->src_domain << 8) | hdr->src_svc,
+			(hdr->dest_domain << 8) | hdr->dest_svc, hdr->opcode,
+			hdr->token);
+		}
+	}
+
+	temp_port = ((data.dest_port >> 8) * 8) + (data.dest_port & 0xFF);
+	pr_debug("port = %d t_port = %d\n", data.src_port, temp_port);
+	if (c_svc->port_cnt && c_svc->port_fn[temp_port])
+		c_svc->port_fn[temp_port](&data,  c_svc->port_priv[temp_port]);
+	else if (c_svc->fn)
+		c_svc->fn(&data, c_svc->priv);
+	else
+		pr_err("APR: Rxed a packet for NULL callback\n");
+}
+
+int apr_get_svc(const char *svc_name, int domain_id, int *client_id,
+		int *svc_idx, int *svc_id)
+{
+	int i;
+	int size;
+	struct apr_svc_table *tbl;
+	int ret = 0;
+
+	if (domain_id == APR_DOMAIN_ADSP) {
+		tbl = (struct apr_svc_table *)&svc_tbl_qdsp6;
+		size = ARRAY_SIZE(svc_tbl_qdsp6);
+	} else {
+		tbl = (struct apr_svc_table *)&svc_tbl_voice;
+		size = ARRAY_SIZE(svc_tbl_voice);
+	}
+
+	for (i = 0; i < size; i++) {
+		if (!strcmp(svc_name, tbl[i].name)) {
+			*client_id = tbl[i].client_id;
+			*svc_idx = tbl[i].idx;
+			*svc_id = tbl[i].id;
+			break;
+		}
+	}
+
+	pr_debug("%s: svc_name = %s c_id = %d domain_id = %d\n",
+		 __func__, svc_name, *client_id, domain_id);
+	if (i == size) {
+		pr_err("%s: APR: Wrong svc name %s\n", __func__, svc_name);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void apr_reset_deregister(struct work_struct *work)
+{
+	struct apr_svc *handle = NULL;
+	struct apr_reset_work *apr_reset =
+			container_of(work, struct apr_reset_work, work);
+
+	handle = apr_reset->handle;
+	pr_debug("%s:handle[%pK]\n", __func__, handle);
+	apr_deregister(handle);
+	kfree(apr_reset);
+}
+
+int apr_deregister(void *handle)
+{
+	struct apr_svc *svc = handle;
+	struct apr_client *clnt;
+	uint16_t dest_id;
+	uint16_t client_id;
+
+	if (!handle)
+		return -EINVAL;
+
+	mutex_lock(&svc->m_lock);
+	dest_id = svc->dest_id;
+	client_id = svc->client_id;
+	clnt = &client[dest_id][client_id];
+
+	if (svc->port_cnt > 0 || svc->svc_cnt > 0) {
+		if (svc->port_cnt)
+			svc->port_cnt--;
+		else if (svc->svc_cnt)
+			svc->svc_cnt--;
+		if (!svc->port_cnt && !svc->svc_cnt) {
+			client[dest_id][client_id].svc_cnt--;
+			svc->need_reset = 0x0;
+		}
+	} else if (client[dest_id][client_id].svc_cnt > 0) {
+		client[dest_id][client_id].svc_cnt--;
+		if (!client[dest_id][client_id].svc_cnt) {
+			svc->need_reset = 0x0;
+			pr_debug("%s: service is reset %pK\n", __func__, svc);
+		}
+	}
+
+	if (!svc->port_cnt && !svc->svc_cnt) {
+		svc->priv = NULL;
+		svc->id = 0;
+		svc->fn = NULL;
+		svc->dest_id = 0;
+		svc->client_id = 0;
+		svc->need_reset = 0x0;
+	}
+	if (client[dest_id][client_id].handle &&
+	    !client[dest_id][client_id].svc_cnt) {
+		apr_tal_close(client[dest_id][client_id].handle);
+		client[dest_id][client_id].handle = NULL;
+	}
+	mutex_unlock(&svc->m_lock);
+
+	return 0;
+}
+
+void apr_reset(void *handle)
+{
+	struct apr_reset_work *apr_reset_worker = NULL;
+
+	if (!handle)
+		return;
+	pr_debug("%s: handle[%pK]\n", __func__, handle);
+
+	if (apr_reset_workqueue == NULL) {
+		pr_err("%s: apr_reset_workqueue is NULL\n", __func__);
+		return;
+	}
+
+	apr_reset_worker = kzalloc(sizeof(struct apr_reset_work),
+							GFP_ATOMIC);
+
+	if (apr_reset_worker == NULL) {
+		pr_err("%s: mem failure\n", __func__);
+		return;
+	}
+
+	apr_reset_worker->handle = handle;
+	INIT_WORK(&apr_reset_worker->work, apr_reset_deregister);
+	queue_work(apr_reset_workqueue, &apr_reset_worker->work);
+}
+
+/* Dispatch the Reset events to Modem and audio clients */
+static void dispatch_event(unsigned long code, uint16_t proc)
+{
+	struct apr_client *apr_client;
+	struct apr_client_data data;
+	struct apr_svc *svc;
+	uint16_t clnt;
+	int i, j;
+
+	data.opcode = RESET_EVENTS;
+	data.reset_event = code;
+
+	/* Service domain can be different from the processor */
+	data.reset_proc = apr_get_reset_domain(proc);
+
+	clnt = APR_CLIENT_AUDIO;
+	apr_client = &client[proc][clnt];
+	for (i = 0; i < APR_SVC_MAX; i++) {
+		mutex_lock(&apr_client->svc[i].m_lock);
+		if (apr_client->svc[i].fn) {
+			apr_client->svc[i].need_reset = 0x1;
+			apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
+		}
+		if (apr_client->svc[i].port_cnt) {
+			svc = &(apr_client->svc[i]);
+			svc->need_reset = 0x1;
+			for (j = 0; j < APR_MAX_PORTS; j++)
+				if (svc->port_fn[j])
+					svc->port_fn[j](&data,
+						svc->port_priv[j]);
+		}
+		mutex_unlock(&apr_client->svc[i].m_lock);
+	}
+
+	clnt = APR_CLIENT_VOICE;
+	apr_client = &client[proc][clnt];
+	for (i = 0; i < APR_SVC_MAX; i++) {
+		mutex_lock(&apr_client->svc[i].m_lock);
+		if (apr_client->svc[i].fn) {
+			apr_client->svc[i].need_reset = 0x1;
+			apr_client->svc[i].fn(&data, apr_client->svc[i].priv);
+		}
+		if (apr_client->svc[i].port_cnt) {
+			svc = &(apr_client->svc[i]);
+			svc->need_reset = 0x1;
+			for (j = 0; j < APR_MAX_PORTS; j++)
+				if (svc->port_fn[j])
+					svc->port_fn[j](&data,
+						svc->port_priv[j]);
+		}
+		mutex_unlock(&apr_client->svc[i].m_lock);
+	}
+}
+
+static int apr_notifier_service_cb(struct notifier_block *this,
+				   unsigned long opcode, void *data)
+{
+	struct audio_notifier_cb_data *cb_data = data;
+
+	if (cb_data == NULL) {
+		pr_err("%s: Callback data is NULL!\n", __func__);
+		goto done;
+	}
+
+	pr_debug("%s: Service opcode 0x%lx, domain %d\n",
+		__func__, opcode, cb_data->domain);
+
+	switch (opcode) {
+	case AUDIO_NOTIFIER_SERVICE_DOWN:
+		/*
+		 * Use flag to ignore down notifications during
+		 * initial boot. There is no benefit from error
+		 * recovery notifications during initial boot
+		 * up since everything is expected to be down.
+		 */
+		if (is_initial_boot)
+			break;
+		if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+			apr_modem_down(opcode);
+		else
+			apr_adsp_down(opcode);
+		break;
+	case AUDIO_NOTIFIER_SERVICE_UP:
+		is_initial_boot = false;
+		if (cb_data->domain == AUDIO_NOTIFIER_MODEM_DOMAIN)
+			apr_modem_up();
+		else
+			apr_adsp_up();
+		break;
+	default:
+		break;
+	}
+done:
+	return NOTIFY_OK;
+}
+
+static struct notifier_block service_nb = {
+	.notifier_call  = apr_notifier_service_cb,
+	.priority = 0,
+};
+
+static int __init apr_init(void)
+{
+	int i, j, k;
+
+	for (i = 0; i < APR_DEST_MAX; i++)
+		for (j = 0; j < APR_CLIENT_MAX; j++) {
+			mutex_init(&client[i][j].m_lock);
+			for (k = 0; k < APR_SVC_MAX; k++) {
+				mutex_init(&client[i][j].svc[k].m_lock);
+				spin_lock_init(&client[i][j].svc[k].w_lock);
+			}
+		}
+	apr_set_subsys_state();
+	mutex_init(&q6.lock);
+	apr_reset_workqueue = create_singlethread_workqueue("apr_driver");
+	if (!apr_reset_workqueue)
+		return -ENOMEM;
+
+	apr_pkt_ctx = ipc_log_context_create(APR_PKT_IPC_LOG_PAGE_CNT,
+						"apr", 0);
+	if (!apr_pkt_ctx)
+		pr_err("%s: Unable to create ipc log context\n", __func__);
+
+	is_initial_boot = true;
+	subsys_notif_register("apr_adsp", AUDIO_NOTIFIER_ADSP_DOMAIN,
+			      &service_nb);
+	subsys_notif_register("apr_modem", AUDIO_NOTIFIER_MODEM_DOMAIN,
+			      &service_nb);
+
+	return 0;
+}
+device_initcall(apr_init);
+
+static int __init apr_late_init(void)
+{
+	int ret = 0;
+
+	init_waitqueue_head(&dsp_wait);
+	init_waitqueue_head(&modem_wait);
+
+	return ret;
+}
+late_initcall(apr_late_init);
+
+#ifdef CONFIG_DEBUG_FS
+static int __init apr_debug_init(void)
+{
+	debugfs_apr_debug = debugfs_create_file("msm_apr_debug",
+						 S_IFREG | 0444, NULL, NULL,
+						 &apr_debug_ops);
+	return 0;
+}
+device_initcall(apr_debug_init);
+#endif
diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal.c b/drivers/soc/qcom/qdsp6v2/apr_tal.c
new file mode 100644
index 0000000..5c296f6
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_tal.c
@@ -0,0 +1,298 @@
+/* Copyright (c) 2010-2011, 2013-2014, 2016-2017 The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <soc/qcom/smd.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = {
+	{
+		"apr_audio_svc",
+		"apr_voice_svc",
+	},
+	{
+		"apr_audio_svc",
+		"apr_voice_svc",
+	},
+};
+
+struct apr_svc_ch_dev apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
+
+int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
+			struct apr_pkt_priv *pkt_priv, int len)
+{
+	int w_len;
+	unsigned long flags;
+
+	spin_lock_irqsave(&apr_ch->w_lock, flags);
+	if (smd_write_avail(apr_ch->ch) < len) {
+		spin_unlock_irqrestore(&apr_ch->w_lock, flags);
+		return -EAGAIN;
+	}
+
+	w_len = smd_write(apr_ch->ch, data, len);
+	spin_unlock_irqrestore(&apr_ch->w_lock, flags);
+
+	pr_debug("apr_tal:w_len = %d\n", w_len);
+
+	if (w_len != len) {
+		pr_err("apr_tal: Error in write\n");
+		return -ENETRESET;
+	}
+	return w_len;
+}
+
+int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
+			struct apr_pkt_priv *pkt_priv, int len)
+{
+	int rc = 0, retries = 0;
+
+	if (!apr_ch->ch)
+		return -EINVAL;
+
+	do {
+		if (rc == -EAGAIN)
+			udelay(50);
+
+		rc = __apr_tal_write(apr_ch, data, pkt_priv, len);
+	} while (rc == -EAGAIN && retries++ < 300);
+
+	if (rc == -EAGAIN)
+		pr_err("apr_tal: TIMEOUT for write\n");
+
+	return rc;
+}
+
+static void apr_tal_notify(void *priv, unsigned int event)
+{
+	struct apr_svc_ch_dev *apr_ch = priv;
+	int len, r_len, sz;
+	int pkt_cnt = 0;
+	unsigned long flags;
+
+	pr_debug("event = %d\n", event);
+	switch (event) {
+	case SMD_EVENT_DATA:
+		pkt_cnt = 0;
+		spin_lock_irqsave(&apr_ch->lock, flags);
+check_pending:
+		len = smd_read_avail(apr_ch->ch);
+		if (len < 0) {
+			pr_err("apr_tal: Invalid Read Event :%d\n", len);
+			spin_unlock_irqrestore(&apr_ch->lock, flags);
+			return;
+		}
+		sz = smd_cur_packet_size(apr_ch->ch);
+		if (sz < 0) {
+			pr_debug("pkt size is zero\n");
+			spin_unlock_irqrestore(&apr_ch->lock, flags);
+			return;
+		}
+		if (!len && !sz && !pkt_cnt)
+			goto check_write_avail;
+		if (!len) {
+			pr_debug("len = %d pkt_cnt = %d\n", len, pkt_cnt);
+			spin_unlock_irqrestore(&apr_ch->lock, flags);
+			return;
+		}
+		r_len = smd_read_from_cb(apr_ch->ch, apr_ch->data, len);
+		if (len != r_len) {
+			pr_err("apr_tal: Invalid Read\n");
+			spin_unlock_irqrestore(&apr_ch->lock, flags);
+			return;
+		}
+		pkt_cnt++;
+		pr_debug("%d %d %d\n", len, sz, pkt_cnt);
+		if (apr_ch->func)
+			apr_ch->func(apr_ch->data, r_len, apr_ch->priv);
+		goto check_pending;
+check_write_avail:
+		if (smd_write_avail(apr_ch->ch))
+			wake_up(&apr_ch->wait);
+		spin_unlock_irqrestore(&apr_ch->lock, flags);
+		break;
+	case SMD_EVENT_OPEN:
+		pr_debug("apr_tal: SMD_EVENT_OPEN\n");
+		apr_ch->smd_state = 1;
+		wake_up(&apr_ch->wait);
+		break;
+	case SMD_EVENT_CLOSE:
+		pr_debug("apr_tal: SMD_EVENT_CLOSE\n");
+		break;
+	}
+}
+
+int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch,
+			int num_of_intents, uint32_t size)
+{
+	/* Rx intents configuration is required for Glink
+	 * but not for SMD. No-op for this function.
+	 */
+	return 0;
+}
+
+struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest,
+				uint32_t dl, apr_svc_cb_fn func, void *priv)
+{
+	int rc;
+
+	if ((clnt >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) ||
+						(dl >= APR_DL_MAX)) {
+		pr_err("apr_tal: Invalid params\n");
+		return NULL;
+	}
+
+	if (apr_svc_ch[dl][dest][clnt].ch) {
+		pr_err("apr_tal: This channel alreday openend\n");
+		return NULL;
+	}
+
+	mutex_lock(&apr_svc_ch[dl][dest][clnt].m_lock);
+	if (!apr_svc_ch[dl][dest][clnt].dest_state) {
+		rc = wait_event_timeout(apr_svc_ch[dl][dest][clnt].dest,
+			apr_svc_ch[dl][dest][clnt].dest_state,
+				msecs_to_jiffies(APR_OPEN_TIMEOUT_MS));
+		if (rc == 0) {
+			pr_err("apr_tal:open timeout\n");
+			mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock);
+			return NULL;
+		}
+		pr_debug("apr_tal:Wakeup done\n");
+		apr_svc_ch[dl][dest][clnt].dest_state = 0;
+	}
+	rc = smd_named_open_on_edge(svc_names[dest][clnt], dest,
+			&apr_svc_ch[dl][dest][clnt].ch,
+			&apr_svc_ch[dl][dest][clnt],
+			apr_tal_notify);
+	if (rc < 0) {
+		pr_err("apr_tal: smd_open failed %s\n",
+					svc_names[dest][clnt]);
+		mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock);
+		return NULL;
+	}
+	rc = wait_event_timeout(apr_svc_ch[dl][dest][clnt].wait,
+		(apr_svc_ch[dl][dest][clnt].smd_state == 1), 5 * HZ);
+	if (rc == 0) {
+		pr_err("apr_tal:TIMEOUT for OPEN event\n");
+		mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock);
+		apr_tal_close(&apr_svc_ch[dl][dest][clnt]);
+		return NULL;
+	}
+
+	smd_disable_read_intr(apr_svc_ch[dl][dest][clnt].ch);
+
+	if (!apr_svc_ch[dl][dest][clnt].dest_state) {
+		apr_svc_ch[dl][dest][clnt].dest_state = 1;
+		pr_debug("apr_tal:Waiting for apr svc init\n");
+		msleep(200);
+		pr_debug("apr_tal:apr svc init done\n");
+	}
+	apr_svc_ch[dl][dest][clnt].smd_state = 0;
+
+	apr_svc_ch[dl][dest][clnt].func = func;
+	apr_svc_ch[dl][dest][clnt].priv = priv;
+	mutex_unlock(&apr_svc_ch[dl][dest][clnt].m_lock);
+
+	return &apr_svc_ch[dl][dest][clnt];
+}
+
+int apr_tal_close(struct apr_svc_ch_dev *apr_ch)
+{
+	int r;
+
+	if (!apr_ch->ch)
+		return -EINVAL;
+
+	mutex_lock(&apr_ch->m_lock);
+	r = smd_close(apr_ch->ch);
+	apr_ch->ch = NULL;
+	apr_ch->func = NULL;
+	apr_ch->priv = NULL;
+	mutex_unlock(&apr_ch->m_lock);
+	return r;
+}
+
+static int apr_smd_probe(struct platform_device *pdev)
+{
+	int dest;
+	int clnt;
+
+	if (pdev->id == APR_DEST_MODEM) {
+		pr_info("apr_tal:Modem Is Up\n");
+		dest = APR_DEST_MODEM;
+		if (!strcmp(pdev->name, "apr_audio_svc"))
+			clnt = APR_CLIENT_AUDIO;
+		else
+			clnt = APR_CLIENT_VOICE;
+		apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1;
+		wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest);
+	} else if (pdev->id == APR_DEST_QDSP6) {
+		pr_info("apr_tal:Q6 Is Up\n");
+		dest = APR_DEST_QDSP6;
+		clnt = APR_CLIENT_AUDIO;
+		apr_svc_ch[APR_DL_SMD][dest][clnt].dest_state = 1;
+		wake_up(&apr_svc_ch[APR_DL_SMD][dest][clnt].dest);
+	} else
+		pr_err("apr_tal:Invalid Dest Id: %d\n", pdev->id);
+
+	return 0;
+}
+
+static struct platform_driver apr_q6_driver = {
+	.probe = apr_smd_probe,
+	.driver = {
+		.name = "apr_audio_svc",
+		.owner = THIS_MODULE,
+	},
+};
+
+static struct platform_driver apr_modem_driver = {
+	.probe = apr_smd_probe,
+	.driver = {
+		.name = "apr_voice_svc",
+		.owner = THIS_MODULE,
+	},
+};
+
+static int __init apr_tal_init(void)
+{
+	int i, j, k;
+
+	for (i = 0; i < APR_DL_MAX; i++)
+		for (j = 0; j < APR_DEST_MAX; j++)
+			for (k = 0; k < APR_CLIENT_MAX; k++) {
+				init_waitqueue_head(&apr_svc_ch[i][j][k].wait);
+				init_waitqueue_head(&apr_svc_ch[i][j][k].dest);
+				spin_lock_init(&apr_svc_ch[i][j][k].lock);
+				spin_lock_init(&apr_svc_ch[i][j][k].w_lock);
+				mutex_init(&apr_svc_ch[i][j][k].m_lock);
+			}
+	platform_driver_register(&apr_q6_driver);
+	platform_driver_register(&apr_modem_driver);
+	return 0;
+}
+device_initcall(apr_tal_init);
diff --git a/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
new file mode 100644
index 0000000..40a539e
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_tal_glink.c
@@ -0,0 +1,513 @@
+/* Copyright (c) 2016-2017 The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <soc/qcom/glink.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+#define APR_MAXIMUM_NUM_OF_RETRIES 2
+
+struct apr_tx_buf {
+	struct list_head list;
+	struct apr_pkt_priv pkt_priv;
+	char buf[APR_MAX_BUF];
+};
+
+struct apr_buf_list {
+	struct list_head list;
+	spinlock_t lock;
+};
+
+struct link_state {
+	uint32_t dest;
+	void *handle;
+	enum glink_link_state link_state;
+	wait_queue_head_t wait;
+};
+
+static struct link_state link_state[APR_DEST_MAX];
+static struct apr_buf_list buf_list;
+
+static char *svc_names[APR_DEST_MAX][APR_CLIENT_MAX] = {
+	{
+		"apr_audio_svc",
+		"apr_voice_svc",
+	},
+	{
+		"apr_audio_svc",
+		"apr_voice_svc",
+	},
+};
+
+static struct apr_svc_ch_dev
+	apr_svc_ch[APR_DL_MAX][APR_DEST_MAX][APR_CLIENT_MAX];
+
+static struct apr_tx_buf *apr_get_free_buf(int len)
+{
+	struct apr_tx_buf *tx_buf;
+	unsigned long flags;
+
+	if (len > APR_MAX_BUF) {
+		pr_err("%s: buf too large [%d]\n", __func__, len);
+		return ERR_PTR(-EINVAL);
+	}
+
+	spin_lock_irqsave(&buf_list.lock, flags);
+	if (list_empty(&buf_list.list)) {
+		spin_unlock_irqrestore(&buf_list.lock, flags);
+		pr_err("%s: No buf available\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	tx_buf = list_first_entry(&buf_list.list, struct apr_tx_buf, list);
+	list_del(&tx_buf->list);
+	spin_unlock_irqrestore(&buf_list.lock, flags);
+
+	return tx_buf;
+}
+
+static void apr_buf_add_tail(const void *buf)
+{
+	struct apr_tx_buf *list;
+	unsigned long flags;
+
+	if (!buf)
+		return;
+
+	spin_lock_irqsave(&buf_list.lock, flags);
+	list = container_of((void *)buf, struct apr_tx_buf, buf);
+	list_add_tail(&list->list, &buf_list.list);
+	spin_unlock_irqrestore(&buf_list.lock, flags);
+}
+
+static int __apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
+			   struct apr_pkt_priv *pkt_priv, int len)
+{
+	int rc = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&apr_ch->w_lock, flags);
+	rc = glink_tx(apr_ch->handle, pkt_priv, data, len,
+			GLINK_TX_REQ_INTENT | GLINK_TX_ATOMIC);
+	spin_unlock_irqrestore(&apr_ch->w_lock, flags);
+
+	if (rc)
+		pr_err("%s: glink_tx failed, rc[%d]\n", __func__, rc);
+	else
+		rc = len;
+
+	return rc;
+}
+
+int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
+		  struct apr_pkt_priv *pkt_priv, int len)
+{
+	int rc = 0, retries = 0;
+	void *pkt_data = NULL;
+	struct apr_tx_buf *tx_buf;
+	struct apr_pkt_priv *pkt_priv_ptr = pkt_priv;
+
+	if (!apr_ch->handle || !pkt_priv)
+		return -EINVAL;
+
+	if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER) {
+		tx_buf = apr_get_free_buf(len);
+		if (IS_ERR_OR_NULL(tx_buf)) {
+			rc = -EINVAL;
+			goto exit;
+		}
+		memcpy(tx_buf->buf, data, len);
+		memcpy(&tx_buf->pkt_priv, pkt_priv, sizeof(tx_buf->pkt_priv));
+		pkt_priv_ptr = &tx_buf->pkt_priv;
+		pkt_data = tx_buf->buf;
+	} else {
+		pkt_data = data;
+	}
+
+	do {
+		if (rc == -EAGAIN)
+			udelay(50);
+
+		rc = __apr_tal_write(apr_ch, pkt_data, pkt_priv_ptr, len);
+	} while (rc == -EAGAIN && retries++ < APR_MAXIMUM_NUM_OF_RETRIES);
+
+	if (rc < 0) {
+		pr_err("%s: Unable to send the packet, rc:%d\n", __func__, rc);
+		if (pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER)
+			apr_buf_add_tail(pkt_data);
+	}
+exit:
+	return rc;
+}
+
+void apr_tal_notify_rx(void *handle, const void *priv, const void *pkt_priv,
+		       const void *ptr, size_t size)
+{
+	struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
+	unsigned long flags;
+
+	if (!apr_ch || !ptr) {
+		pr_err("%s: Invalid apr_ch or ptr\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: Rx packet received\n", __func__);
+
+	spin_lock_irqsave(&apr_ch->r_lock, flags);
+	if (apr_ch->func)
+		apr_ch->func((void *)ptr, size, (void *)pkt_priv);
+	spin_unlock_irqrestore(&apr_ch->r_lock, flags);
+	glink_rx_done(apr_ch->handle, ptr, true);
+}
+
+static void apr_tal_notify_tx_abort(void *handle, const void *priv,
+				    const void *pkt_priv)
+{
+	struct apr_pkt_priv *apr_pkt_priv_ptr =
+				(struct apr_pkt_priv *)pkt_priv;
+	struct apr_tx_buf *list_node;
+
+	if (!apr_pkt_priv_ptr) {
+		pr_err("%s: Invalid pkt_priv\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: tx_abort received for apr_pkt_priv_ptr:%pK\n",
+		 __func__, apr_pkt_priv_ptr);
+
+	if (apr_pkt_priv_ptr->pkt_owner == APR_PKT_OWNER_DRIVER) {
+		list_node = container_of(apr_pkt_priv_ptr,
+					 struct apr_tx_buf, pkt_priv);
+		apr_buf_add_tail(list_node->buf);
+	}
+}
+
+void apr_tal_notify_tx_done(void *handle, const void *priv,
+			    const void *pkt_priv, const void *ptr)
+{
+	struct apr_pkt_priv *apr_pkt_priv = (struct apr_pkt_priv *)pkt_priv;
+
+	if (!pkt_priv || !ptr) {
+		pr_err("%s: Invalid pkt_priv or ptr\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: tx_done received\n", __func__);
+
+	if (apr_pkt_priv->pkt_owner == APR_PKT_OWNER_DRIVER)
+		apr_buf_add_tail(ptr);
+}
+
+bool apr_tal_notify_rx_intent_req(void *handle, const void *priv,
+				  size_t req_size)
+{
+	struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
+
+	if (!apr_ch) {
+		pr_err("%s: Invalid apr_ch\n", __func__);
+		return false;
+	}
+
+	pr_err("%s: No rx intents queued, unable to receive\n", __func__);
+	return false;
+}
+
+static void apr_tal_notify_remote_rx_intent(void *handle, const void *priv,
+					    size_t size)
+{
+	struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
+
+	if (!apr_ch) {
+		pr_err("%s: Invalid apr_ch\n", __func__);
+		return;
+	}
+	/*
+	 * This is to make sure that the far end has queued at least one intent
+	 * before we attmpt any IPC. A simple bool flag is used here instead of
+	 * a counter, as the far end is required to guarantee intent
+	 * availability for all use cases once the channel is fully opened.
+	 */
+	pr_debug("%s: remote queued an intent\n", __func__);
+	apr_ch->if_remote_intent_ready = true;
+}
+
+void apr_tal_notify_state(void *handle, const void *priv, unsigned int event)
+{
+	struct apr_svc_ch_dev *apr_ch = (struct apr_svc_ch_dev *)priv;
+
+	if (!apr_ch) {
+		pr_err("%s: Invalid apr_ch\n", __func__);
+		return;
+	}
+
+	apr_ch->channel_state = event;
+	pr_info("%s: Channel state[%d]\n", __func__, event);
+
+	if (event == GLINK_CONNECTED)
+		wake_up(&apr_ch->wait);
+}
+
+int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch,
+			      int num_of_intents, uint32_t size)
+{
+	int i;
+	int rc;
+
+	if (!apr_ch || !num_of_intents || !size) {
+		pr_err("%s: Invalid parameter\n", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < num_of_intents; i++) {
+		rc = glink_queue_rx_intent(apr_ch->handle, apr_ch, size);
+		if (rc) {
+			pr_err("%s: Failed to queue rx intent, iteration[%d]\n",
+			       __func__, i);
+			break;
+		}
+	}
+
+	return rc;
+}
+
+struct apr_svc_ch_dev *apr_tal_open(uint32_t clnt, uint32_t dest, uint32_t dl,
+				    apr_svc_cb_fn func, void *priv)
+{
+	int rc;
+	struct glink_open_config open_cfg;
+	struct apr_svc_ch_dev *apr_ch;
+
+	if ((clnt >= APR_CLIENT_MAX) || (dest >= APR_DEST_MAX) ||
+	    (dl >= APR_DL_MAX)) {
+		pr_err("%s: Invalid params, clnt:%d, dest:%d, dl:%d\n",
+		       __func__, clnt, dest, dl);
+		return NULL;
+	}
+
+	apr_ch = &apr_svc_ch[dl][dest][clnt];
+	mutex_lock(&apr_ch->m_lock);
+	if (apr_ch->handle) {
+		pr_err("%s: This channel is already opened\n", __func__);
+		rc = -EBUSY;
+		goto unlock;
+	}
+
+	if (link_state[dest].link_state != GLINK_LINK_STATE_UP) {
+		rc = wait_event_timeout(link_state[dest].wait,
+			link_state[dest].link_state == GLINK_LINK_STATE_UP,
+			msecs_to_jiffies(APR_OPEN_TIMEOUT_MS));
+		if (rc == 0) {
+			pr_err("%s: Open timeout, dest:%d\n", __func__, dest);
+			rc = -ETIMEDOUT;
+			goto unlock;
+		}
+		pr_debug("%s: Wakeup done, dest:%d\n", __func__, dest);
+	}
+
+	memset(&open_cfg, 0, sizeof(struct glink_open_config));
+	open_cfg.options = GLINK_OPT_INITIAL_XPORT;
+	if (dest == APR_DEST_MODEM)
+		open_cfg.edge = "mpss";
+	else
+		open_cfg.edge = "lpass";
+
+	open_cfg.name = svc_names[dest][clnt];
+	open_cfg.notify_rx = apr_tal_notify_rx;
+	open_cfg.notify_tx_done = apr_tal_notify_tx_done;
+	open_cfg.notify_state = apr_tal_notify_state;
+	open_cfg.notify_rx_intent_req = apr_tal_notify_rx_intent_req;
+	open_cfg.notify_remote_rx_intent = apr_tal_notify_remote_rx_intent;
+	open_cfg.notify_tx_abort = apr_tal_notify_tx_abort;
+	open_cfg.priv = apr_ch;
+	open_cfg.transport = "smem";
+
+	apr_ch->channel_state = GLINK_REMOTE_DISCONNECTED;
+	apr_ch->handle = glink_open(&open_cfg);
+	if (IS_ERR_OR_NULL(apr_ch->handle)) {
+		pr_err("%s: glink_open failed %s\n", __func__,
+		       svc_names[dest][clnt]);
+		apr_ch->handle = NULL;
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	rc = wait_event_timeout(apr_ch->wait,
+		(apr_ch->channel_state == GLINK_CONNECTED), 5 * HZ);
+	if (rc == 0) {
+		pr_err("%s: TIMEOUT for OPEN event\n", __func__);
+		rc = -ETIMEDOUT;
+		goto close_link;
+	}
+
+	/*
+	 * Remote intent is not required for GLINK <--> SMD IPC, so this is
+	 * designed not to fail the open call.
+	 */
+	rc = wait_event_timeout(apr_ch->wait,
+		apr_ch->if_remote_intent_ready, 5 * HZ);
+	if (rc == 0)
+		pr_err("%s: TIMEOUT for remote intent readiness\n", __func__);
+
+	rc = apr_tal_rx_intents_config(apr_ch, APR_DEFAULT_NUM_OF_INTENTS,
+				       APR_MAX_BUF);
+	if (rc) {
+		pr_err("%s: Unable to queue intents\n", __func__);
+		goto close_link;
+	}
+
+	apr_ch->func = func;
+	apr_ch->priv = priv;
+
+close_link:
+	if (rc) {
+		glink_close(apr_ch->handle);
+		apr_ch->handle = NULL;
+	}
+unlock:
+	mutex_unlock(&apr_ch->m_lock);
+
+	return rc ? NULL : apr_ch;
+}
+
+int apr_tal_close(struct apr_svc_ch_dev *apr_ch)
+{
+	int rc;
+
+	if (!apr_ch || !apr_ch->handle) {
+		rc = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&apr_ch->m_lock);
+	rc = glink_close(apr_ch->handle);
+	apr_ch->handle = NULL;
+	apr_ch->func = NULL;
+	apr_ch->priv = NULL;
+	apr_ch->if_remote_intent_ready = false;
+	mutex_unlock(&apr_ch->m_lock);
+exit:
+	return rc;
+}
+
+static void apr_tal_link_state_cb(struct glink_link_state_cb_info *cb_info,
+				  void *priv)
+{
+	uint32_t dest;
+
+	if (!cb_info) {
+		pr_err("%s: Invalid cb_info\n", __func__);
+		return;
+	}
+
+	if (!strcmp(cb_info->edge, "mpss"))
+		dest = APR_DEST_MODEM;
+	else if (!strcmp(cb_info->edge, "lpass"))
+		dest = APR_DEST_QDSP6;
+	else {
+		pr_err("%s:Unknown edge[%s]\n", __func__, cb_info->edge);
+		return;
+	}
+
+	pr_info("%s: edge[%s] link state[%d]\n", __func__, cb_info->edge,
+		cb_info->link_state);
+
+	link_state[dest].link_state = cb_info->link_state;
+	if (link_state[dest].link_state == GLINK_LINK_STATE_UP)
+		wake_up(&link_state[dest].wait);
+}
+
+static struct glink_link_info mpss_link_info = {
+	.transport = "smem",
+	.edge = "mpss",
+	.glink_link_state_notif_cb = apr_tal_link_state_cb,
+};
+
+static struct glink_link_info lpass_link_info = {
+	.transport = "smem",
+	.edge = "lpass",
+	.glink_link_state_notif_cb = apr_tal_link_state_cb,
+};
+
+static int __init apr_tal_init(void)
+{
+	int i, j, k;
+	struct apr_tx_buf *buf;
+	struct list_head *ptr, *next;
+
+	for (i = 0; i < APR_DL_MAX; i++) {
+		for (j = 0; j < APR_DEST_MAX; j++) {
+			for (k = 0; k < APR_CLIENT_MAX; k++) {
+				init_waitqueue_head(&apr_svc_ch[i][j][k].wait);
+				spin_lock_init(&apr_svc_ch[i][j][k].w_lock);
+				spin_lock_init(&apr_svc_ch[i][j][k].r_lock);
+				mutex_init(&apr_svc_ch[i][j][k].m_lock);
+			}
+		}
+	}
+
+	for (i = 0; i < APR_DEST_MAX; i++)
+		init_waitqueue_head(&link_state[i].wait);
+
+	spin_lock_init(&buf_list.lock);
+	INIT_LIST_HEAD(&buf_list.list);
+	for (i = 0; i < APR_NUM_OF_TX_BUF; i++) {
+		buf = kzalloc(sizeof(struct apr_tx_buf), GFP_KERNEL);
+		if (!buf) {
+			pr_err("%s: Unable to allocate tx buf\n", __func__);
+			goto tx_buf_alloc_fail;
+		}
+
+		INIT_LIST_HEAD(&buf->list);
+		spin_lock(&buf_list.lock);
+		list_add_tail(&buf->list, &buf_list.list);
+		spin_unlock(&buf_list.lock);
+	}
+
+	link_state[APR_DEST_MODEM].link_state = GLINK_LINK_STATE_DOWN;
+	link_state[APR_DEST_MODEM].handle =
+		glink_register_link_state_cb(&mpss_link_info, NULL);
+	if (!link_state[APR_DEST_MODEM].handle)
+		pr_err("%s: Unable to register mpss link state\n", __func__);
+
+	link_state[APR_DEST_QDSP6].link_state = GLINK_LINK_STATE_DOWN;
+	link_state[APR_DEST_QDSP6].handle =
+		glink_register_link_state_cb(&lpass_link_info, NULL);
+	if (!link_state[APR_DEST_QDSP6].handle)
+		pr_err("%s: Unable to register lpass link state\n", __func__);
+
+	return 0;
+
+tx_buf_alloc_fail:
+	list_for_each_safe(ptr, next, &buf_list.list) {
+		buf = list_entry(ptr, struct apr_tx_buf, list);
+		list_del(&buf->list);
+		kfree(buf);
+	}
+	return -ENOMEM;
+}
+device_initcall(apr_tal_init);
diff --git a/drivers/soc/qcom/qdsp6v2/apr_v2.c b/drivers/soc/qcom/qdsp6v2/apr_v2.c
new file mode 100644
index 0000000..4ddf39b
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_v2.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <linux/qdsp6v2/dsp_debug.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+
+enum apr_subsys_state apr_get_subsys_state(void)
+{
+	return apr_get_q6_state();
+}
+
+void apr_set_subsys_state(void)
+{
+	apr_set_q6_state(APR_SUBSYS_DOWN);
+	apr_set_modem_state(APR_SUBSYS_UP);
+}
+
+uint16_t apr_get_data_src(struct apr_hdr *hdr)
+{
+	if (hdr->src_domain == APR_DOMAIN_MODEM)
+		return APR_DEST_MODEM;
+	else if (hdr->src_domain == APR_DOMAIN_ADSP)
+		return APR_DEST_QDSP6;
+
+	pr_err("APR: Pkt from wrong source: %d\n", hdr->src_domain);
+	return APR_DEST_MAX;		/*RETURN INVALID VALUE*/
+}
+
+int apr_get_dest_id(char *dest)
+{
+	if (!strcmp(dest, "ADSP"))
+		return APR_DEST_QDSP6;
+	else
+		return APR_DEST_MODEM;
+}
+
+void subsys_notif_register(char *client_name, int domain,
+			   struct notifier_block *nb)
+{
+	int ret;
+
+	ret = audio_notifier_register(client_name, domain, nb);
+	if (ret < 0)
+		pr_err("%s: Audio notifier register failed for domain %d ret = %d\n",
+			__func__, domain, ret);
+}
+
+uint16_t apr_get_reset_domain(uint16_t proc)
+{
+	return proc;
+}
diff --git a/drivers/soc/qcom/qdsp6v2/apr_v3.c b/drivers/soc/qcom/qdsp6v2/apr_v3.c
new file mode 100644
index 0000000..2bfc518
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/apr_v3.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <linux/qdsp6v2/dsp_debug.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+
+#define DEST_ID APR_DEST_MODEM
+
+enum apr_subsys_state apr_get_subsys_state(void)
+{
+	return apr_get_modem_state();
+}
+
+void apr_set_subsys_state(void)
+{
+	apr_set_modem_state(APR_SUBSYS_DOWN);
+}
+
+uint16_t apr_get_data_src(struct apr_hdr *hdr)
+{
+	return DEST_ID;
+}
+
+int apr_get_dest_id(char *dest)
+{
+	return DEST_ID;
+}
+
+void subsys_notif_register(char *client_name, int domain,
+			   struct notifier_block *nb)
+{
+	int ret;
+
+	if (domain != AUDIO_NOTIFIER_MODEM_DOMAIN) {
+		pr_debug("%s: Unused domain %d not registering with notifier\n",
+			 __func__, domain);
+		return;
+	}
+
+	ret = audio_notifier_register(client_name, domain, nb);
+	if (ret < 0)
+		pr_err("%s: Audio notifier register failed for domain %d ret = %d\n",
+			__func__, domain, ret);
+}
+
+uint16_t apr_get_reset_domain(uint16_t proc)
+{
+	return APR_DEST_QDSP6;
+}
diff --git a/drivers/soc/qcom/qdsp6v2/audio_notifier.c b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
new file mode 100644
index 0000000..a2b0f0e
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_notifier.c
@@ -0,0 +1,635 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/qdsp6v2/audio_pdr.h>
+#include <linux/qdsp6v2/audio_ssr.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/subsystem_notif.h>
+#include <soc/qcom/service-notifier.h>
+
+/* Audio states internal to notifier. Client */
+/* used states defined in audio_notifier.h */
+/* for AUDIO_NOTIFIER_SERVICE_DOWN & UP */
+#define NO_SERVICE -2
+#define UNINIT_SERVICE -1
+
+/*
+ * Used for each client registered with audio notifier
+ */
+struct client_data {
+	struct list_head        list;
+	/* Notifier block given by client */
+	struct notifier_block   *nb;
+	char                    client_name[20];
+	int                     service;
+	int                     domain;
+};
+
+/*
+ * Used for each service and domain combination
+ * Tracks information specific to the underlying
+ * service.
+ */
+struct service_info {
+	const char                      name[20];
+	int                             domain_id;
+	int                             state;
+	void                            *handle;
+	/* Notifier block registered to service */
+	struct notifier_block           *nb;
+	/* Used to determine when to register and deregister service */
+	int                             num_of_clients;
+	/* List of all clients registered to the service and domain */
+	struct srcu_notifier_head       client_nb_list;
+};
+
+static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
+				     unsigned long opcode, void *data);
+static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
+				     unsigned long opcode, void *data);
+static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
+				     unsigned long opcode, void *data);
+
+static struct notifier_block notifier_ssr_adsp_nb = {
+	.notifier_call  = audio_notifer_ssr_adsp_cb,
+	.priority = 0,
+};
+
+static struct notifier_block notifier_ssr_modem_nb = {
+	.notifier_call  = audio_notifer_ssr_modem_cb,
+	.priority = 0,
+};
+
+static struct notifier_block notifier_pdr_adsp_nb = {
+	.notifier_call  = audio_notifer_pdr_adsp_cb,
+	.priority = 0,
+};
+
+static struct service_info service_data[AUDIO_NOTIFIER_MAX_SERVICES]
+				       [AUDIO_NOTIFIER_MAX_DOMAINS] = {
+
+	{{
+		.name = "SSR_ADSP",
+		.domain_id = AUDIO_SSR_DOMAIN_ADSP,
+		.state = AUDIO_NOTIFIER_SERVICE_DOWN,
+		.nb = &notifier_ssr_adsp_nb
+	},
+	{
+		.name = "SSR_MODEM",
+		.domain_id = AUDIO_SSR_DOMAIN_MODEM,
+		.state = AUDIO_NOTIFIER_SERVICE_DOWN,
+		.nb = &notifier_ssr_modem_nb
+	} },
+
+	{{
+		.name = "PDR_ADSP",
+		.domain_id = AUDIO_PDR_DOMAIN_ADSP,
+		.state = UNINIT_SERVICE,
+		.nb = &notifier_pdr_adsp_nb
+	},
+	{	/* PDR MODEM service not enabled */
+		.name = "INVALID",
+		.state = NO_SERVICE,
+		.nb = NULL
+	} }
+};
+
+/* Master list of all audio notifier clients */
+struct list_head   client_list;
+struct mutex       notifier_mutex;
+
+static int audio_notifer_get_default_service(int domain)
+{
+	int service = NO_SERVICE;
+
+	/* initial service to connect per domain */
+	switch (domain) {
+	case AUDIO_NOTIFIER_ADSP_DOMAIN:
+		service = AUDIO_NOTIFIER_PDR_SERVICE;
+		break;
+	case AUDIO_NOTIFIER_MODEM_DOMAIN:
+		service = AUDIO_NOTIFIER_SSR_SERVICE;
+		break;
+	}
+
+	return service;
+}
+
+static void audio_notifer_disable_service(int service)
+{
+	int i;
+
+	for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
+		service_data[service][i].state = NO_SERVICE;
+}
+
+static bool audio_notifer_is_service_enabled(int service)
+{
+	int i;
+
+	for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++)
+		if (service_data[service][i].state != NO_SERVICE)
+			return true;
+	return false;
+}
+
+static void audio_notifer_init_service(int service)
+{
+	int i;
+
+	for (i = 0; i < AUDIO_NOTIFIER_MAX_DOMAINS; i++) {
+		if (service_data[service][i].state == UNINIT_SERVICE)
+			service_data[service][i].state =
+				AUDIO_NOTIFIER_SERVICE_DOWN;
+	}
+}
+
+static int audio_notifer_reg_service(int service, int domain)
+{
+	void *handle;
+	int ret = 0;
+	int curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
+
+	switch (service) {
+	case AUDIO_NOTIFIER_SSR_SERVICE:
+		handle = audio_ssr_register(
+			service_data[service][domain].domain_id,
+			service_data[service][domain].nb);
+		break;
+	case AUDIO_NOTIFIER_PDR_SERVICE:
+		handle = audio_pdr_service_register(
+			service_data[service][domain].domain_id,
+			service_data[service][domain].nb, &curr_state);
+
+		if (curr_state == SERVREG_NOTIF_SERVICE_STATE_UP_V01)
+			curr_state = AUDIO_NOTIFIER_SERVICE_UP;
+		else
+			curr_state = AUDIO_NOTIFIER_SERVICE_DOWN;
+		break;
+	default:
+		pr_err("%s: Invalid service %d\n",
+			__func__, service);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (IS_ERR_OR_NULL(handle)) {
+		pr_err("%s: handle is incorrect for service %s\n",
+			__func__, service_data[service][domain].name);
+		ret = -EINVAL;
+		goto done;
+	}
+	service_data[service][domain].state = curr_state;
+	service_data[service][domain].handle = handle;
+
+	pr_info("%s: service %s is in use\n",
+		__func__, service_data[service][domain].name);
+	pr_debug("%s: service %s has current state %d, handle 0x%pK\n",
+		__func__, service_data[service][domain].name,
+		service_data[service][domain].state,
+		service_data[service][domain].handle);
+done:
+	return ret;
+}
+
+static int audio_notifer_dereg_service(int service, int domain)
+{
+	int ret;
+
+	switch (service) {
+	case AUDIO_NOTIFIER_SSR_SERVICE:
+		ret = audio_ssr_deregister(
+			service_data[service][domain].handle,
+			service_data[service][domain].nb);
+		break;
+	case AUDIO_NOTIFIER_PDR_SERVICE:
+		ret = audio_pdr_service_deregister(
+			service_data[service][domain].handle,
+			service_data[service][domain].nb);
+		break;
+	default:
+		pr_err("%s: Invalid service %d\n",
+			__func__, service);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (ret < 0) {
+		pr_err("%s: deregister failed for service %s, ret %d\n",
+			__func__, service_data[service][domain].name, ret);
+		goto done;
+	}
+
+	pr_debug("%s: service %s with handle 0x%pK deregistered\n",
+		__func__, service_data[service][domain].name,
+		service_data[service][domain].handle);
+
+	service_data[service][domain].state = AUDIO_NOTIFIER_SERVICE_DOWN;
+	service_data[service][domain].handle = NULL;
+done:
+	return ret;
+}
+
+static int audio_notifer_reg_client_service(struct client_data *client_data,
+					    int service)
+{
+	int ret = 0;
+	int domain = client_data->domain;
+	struct audio_notifier_cb_data data;
+
+	switch (service) {
+	case AUDIO_NOTIFIER_SSR_SERVICE:
+	case AUDIO_NOTIFIER_PDR_SERVICE:
+		if (service_data[service][domain].num_of_clients == 0)
+			ret = audio_notifer_reg_service(service, domain);
+		break;
+	default:
+		pr_err("%s: Invalid service for client %s, service %d, domain %d\n",
+			__func__, client_data->client_name, service, domain);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (ret < 0) {
+		pr_err("%s: service registration failed on service %s for client %s\n",
+			__func__, service_data[service][domain].name,
+			client_data->client_name);
+		goto done;
+	}
+
+	client_data->service = service;
+	srcu_notifier_chain_register(
+		&service_data[service][domain].client_nb_list,
+		client_data->nb);
+	service_data[service][domain].num_of_clients++;
+
+	pr_debug("%s: registered client %s on service %s, current state 0x%x\n",
+		__func__, client_data->client_name,
+		service_data[service][domain].name,
+		service_data[service][domain].state);
+
+	/*
+	 * PDR registration returns current state
+	 * Force callback of client with current state for PDR
+	 */
+	if (client_data->service == AUDIO_NOTIFIER_PDR_SERVICE) {
+		data.service = service;
+		data.domain = domain;
+		(void)client_data->nb->notifier_call(client_data->nb,
+			service_data[service][domain].state, &data);
+	}
+done:
+	return ret;
+}
+
+static int audio_notifer_reg_client(struct client_data *client_data)
+{
+	int ret = 0;
+	int service;
+	int domain = client_data->domain;
+
+	service = audio_notifer_get_default_service(domain);
+	if (service < 0) {
+		pr_err("%s: service %d is incorrect\n", __func__, service);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Search through services to find a valid one to register client on. */
+	for (; service >= 0; service--) {
+		/* If a service is not initialized, wait for it to come up. */
+		if (service_data[service][domain].state == UNINIT_SERVICE)
+			goto done;
+		/* Skip unsupported service and domain combinations. */
+		if (service_data[service][domain].state < 0)
+			continue;
+		/* Only register clients who have not acquired a service. */
+		if (client_data->service != NO_SERVICE)
+			continue;
+
+		/*
+		 * Only register clients, who have not acquired a service, on
+		 * the best available service for their domain. Uninitialized
+		 * services will try to register all of their clients after
+		 * they initialize correctly or will disable their service and
+		 * register clients on the next best avaialable service.
+		 */
+		pr_debug("%s: register client %s on service %s",
+				__func__, client_data->client_name,
+				service_data[service][domain].name);
+
+		ret = audio_notifer_reg_client_service(client_data, service);
+		if (ret < 0)
+			pr_err("%s: client %s failed to register on service %s",
+				__func__, client_data->client_name,
+				service_data[service][domain].name);
+	}
+
+done:
+	return ret;
+}
+
+static int audio_notifer_dereg_client(struct client_data *client_data)
+{
+	int ret = 0;
+	int service = client_data->service;
+	int domain = client_data->domain;
+
+	switch (client_data->service) {
+	case AUDIO_NOTIFIER_SSR_SERVICE:
+	case AUDIO_NOTIFIER_PDR_SERVICE:
+		if (service_data[service][domain].num_of_clients == 1)
+			ret = audio_notifer_dereg_service(service, domain);
+		break;
+	case NO_SERVICE:
+		goto done;
+	default:
+		pr_err("%s: Invalid service for client %s, service %d\n",
+			__func__, client_data->client_name,
+			client_data->service);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (ret < 0) {
+		pr_err("%s: deregister failed for client %s on service %s, ret %d\n",
+			__func__, client_data->client_name,
+			service_data[service][domain].name, ret);
+		goto done;
+	}
+
+	ret = srcu_notifier_chain_unregister(&service_data[service][domain].
+					     client_nb_list, client_data->nb);
+	if (ret < 0) {
+		pr_err("%s: srcu_notifier_chain_unregister failed, ret %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	pr_debug("%s: deregistered client %s on service %s\n",
+		__func__, client_data->client_name,
+		service_data[service][domain].name);
+
+	client_data->service = NO_SERVICE;
+	if (service_data[service][domain].num_of_clients > 0)
+		service_data[service][domain].num_of_clients--;
+done:
+	return ret;
+}
+
+static void audio_notifer_reg_all_clients(void)
+{
+	struct list_head *ptr, *next;
+	struct client_data *client_data;
+	int ret;
+
+	list_for_each_safe(ptr, next, &client_list) {
+		client_data = list_entry(ptr,
+			struct client_data, list);
+		ret = audio_notifer_reg_client(client_data);
+		if (ret < 0)
+			pr_err("%s: audio_notifer_reg_client failed for client %s, ret %d\n",
+				__func__, client_data->client_name,
+				ret);
+	}
+}
+
+static int audio_notifer_pdr_callback(struct notifier_block *this,
+				      unsigned long opcode, void *data)
+{
+	pr_debug("%s: Audio PDR framework state 0x%lx\n",
+		__func__, opcode);
+	mutex_lock(&notifier_mutex);
+	if (opcode == AUDIO_PDR_FRAMEWORK_DOWN)
+		audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
+	else
+		audio_notifer_init_service(AUDIO_NOTIFIER_PDR_SERVICE);
+
+	audio_notifer_reg_all_clients();
+	mutex_unlock(&notifier_mutex);
+	return 0;
+}
+
+static struct notifier_block pdr_nb = {
+	.notifier_call  = audio_notifer_pdr_callback,
+	.priority = 0,
+};
+
+static int audio_notifer_convert_opcode(unsigned long opcode,
+					unsigned long *notifier_opcode)
+{
+	int ret = 0;
+
+	switch (opcode) {
+	case SUBSYS_BEFORE_SHUTDOWN:
+	case SERVREG_NOTIF_SERVICE_STATE_DOWN_V01:
+		*notifier_opcode = AUDIO_NOTIFIER_SERVICE_DOWN;
+		break;
+	case SUBSYS_AFTER_POWERUP:
+	case SERVREG_NOTIF_SERVICE_STATE_UP_V01:
+		*notifier_opcode = AUDIO_NOTIFIER_SERVICE_UP;
+		break;
+	default:
+		pr_debug("%s: Unused opcode 0x%lx\n", __func__, opcode);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int audio_notifer_service_cb(unsigned long opcode,
+				    int service, int domain)
+{
+	int ret = 0;
+	unsigned long notifier_opcode;
+	struct audio_notifier_cb_data data;
+
+	if (audio_notifer_convert_opcode(opcode, &notifier_opcode) < 0)
+		goto done;
+
+	data.service = service;
+	data.domain = domain;
+
+	pr_debug("%s: service %s, opcode 0x%lx\n",
+		__func__, service_data[service][domain].name, notifier_opcode);
+
+	mutex_lock(&notifier_mutex);
+
+	service_data[service][domain].state = notifier_opcode;
+	ret = srcu_notifier_call_chain(&service_data[service][domain].
+		client_nb_list, notifier_opcode, &data);
+	if (ret < 0)
+		pr_err("%s: srcu_notifier_call_chain returned %d, service %s, opcode 0x%lx\n",
+			__func__, ret, service_data[service][domain].name,
+			notifier_opcode);
+
+	mutex_unlock(&notifier_mutex);
+done:
+	return NOTIFY_OK;
+}
+
+static int audio_notifer_pdr_adsp_cb(struct notifier_block *this,
+				     unsigned long opcode, void *data)
+{
+	return audio_notifer_service_cb(opcode,
+					AUDIO_NOTIFIER_PDR_SERVICE,
+					AUDIO_NOTIFIER_ADSP_DOMAIN);
+}
+
+static int audio_notifer_ssr_adsp_cb(struct notifier_block *this,
+				     unsigned long opcode, void *data)
+{
+	if (opcode == SUBSYS_BEFORE_SHUTDOWN)
+		audio_ssr_send_nmi(data);
+
+	return audio_notifer_service_cb(opcode,
+					AUDIO_NOTIFIER_SSR_SERVICE,
+					AUDIO_NOTIFIER_ADSP_DOMAIN);
+}
+
+static int audio_notifer_ssr_modem_cb(struct notifier_block *this,
+				      unsigned long opcode, void *data)
+{
+	return audio_notifer_service_cb(opcode,
+					AUDIO_NOTIFIER_SSR_SERVICE,
+					AUDIO_NOTIFIER_MODEM_DOMAIN);
+}
+
+int audio_notifier_deregister(char *client_name)
+{
+	int ret = 0;
+	int ret2;
+	struct list_head *ptr, *next;
+	struct client_data *client_data;
+
+	if (client_name == NULL) {
+		pr_err("%s: client_name is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	mutex_lock(&notifier_mutex);
+	list_for_each_safe(ptr, next, &client_data->list) {
+		client_data = list_entry(ptr, struct client_data,
+					list);
+		if (!strcmp(client_name, client_data->client_name)) {
+			ret2 = audio_notifer_dereg_client(client_data);
+			if (ret2 < 0) {
+				pr_err("%s: audio_notifer_dereg_client failed, ret %d\n, service %d, domain %d",
+					__func__, ret2, client_data->service,
+					client_data->domain);
+				ret = ret2;
+				continue;
+			}
+			list_del(&client_data->list);
+			kfree(client_data);
+		}
+	}
+	mutex_unlock(&notifier_mutex);
+done:
+	return ret;
+}
+EXPORT_SYMBOL(audio_notifier_deregister);
+
+int audio_notifier_register(char *client_name, int domain,
+			    struct notifier_block *nb)
+{
+	int ret;
+	struct client_data *client_data;
+
+	if (client_name == NULL) {
+		pr_err("%s: client_name is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if (nb == NULL) {
+		pr_err("%s: Notifier block is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	client_data = kmalloc(sizeof(*client_data), GFP_KERNEL);
+	if (client_data == NULL) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	INIT_LIST_HEAD(&client_data->list);
+	client_data->nb = nb;
+	strlcpy(client_data->client_name, client_name,
+		sizeof(client_data->client_name));
+	client_data->service = NO_SERVICE;
+	client_data->domain = domain;
+
+	mutex_lock(&notifier_mutex);
+	ret = audio_notifer_reg_client(client_data);
+	if (ret < 0) {
+		mutex_unlock(&notifier_mutex);
+		pr_err("%s: audio_notifer_reg_client for client %s failed ret = %d\n",
+			__func__, client_data->client_name,
+			ret);
+		kfree(client_data);
+		goto done;
+	}
+	list_add_tail(&client_data->list, &client_list);
+	mutex_unlock(&notifier_mutex);
+done:
+	return ret;
+}
+EXPORT_SYMBOL(audio_notifier_register);
+
+static int __init audio_notifier_subsys_init(void)
+{
+	int i, j;
+
+	mutex_init(&notifier_mutex);
+	INIT_LIST_HEAD(&client_list);
+	for (i = 0; i < AUDIO_NOTIFIER_MAX_SERVICES; i++) {
+		for (j = 0; j < AUDIO_NOTIFIER_MAX_DOMAINS; j++) {
+			if (service_data[i][j].state <= NO_SERVICE)
+				continue;
+
+			srcu_init_notifier_head(
+				&service_data[i][j].client_nb_list);
+		}
+	}
+
+	return 0;
+}
+subsys_initcall(audio_notifier_subsys_init);
+
+static int __init audio_notifier_init(void)
+{
+	int ret;
+
+	ret = audio_pdr_register(&pdr_nb);
+	if (ret < 0) {
+		pr_debug("%s: PDR register failed, ret = %d, disable service\n",
+			__func__, ret);
+		audio_notifer_disable_service(AUDIO_NOTIFIER_PDR_SERVICE);
+	}
+
+	/* Do not return error since PDR enablement is not critical */
+	return 0;
+}
+module_init(audio_notifier_init);
+
+static int __init audio_notifier_late_init(void)
+{
+	/*
+	 * If pdr registration failed, register clients on next service
+	 * Do in late init to ensure that SSR subsystem is initialized
+	 */
+	if (!audio_notifer_is_service_enabled(AUDIO_NOTIFIER_PDR_SERVICE))
+		audio_notifer_reg_all_clients();
+
+	return 0;
+}
+late_initcall(audio_notifier_late_init);
diff --git a/drivers/soc/qcom/qdsp6v2/audio_pdr.c b/drivers/soc/qcom/qdsp6v2/audio_pdr.c
new file mode 100644
index 0000000..449d19a
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_pdr.c
@@ -0,0 +1,147 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/qdsp6v2/audio_pdr.h>
+#include <soc/qcom/service-locator.h>
+#include <soc/qcom/service-notifier.h>
+
+static struct pd_qmi_client_data audio_pdr_services[AUDIO_PDR_DOMAIN_MAX] = {
+	{	/* AUDIO_PDR_DOMAIN_ADSP */
+		.client_name = "audio_pdr_adsp",
+		.service_name = "avs/audio"
+	}
+};
+
+struct srcu_notifier_head audio_pdr_cb_list;
+
+static int audio_pdr_locator_callback(struct notifier_block *this,
+				      unsigned long opcode, void *data)
+{
+	unsigned long pdr_state = AUDIO_PDR_FRAMEWORK_DOWN;
+
+	if (opcode == LOCATOR_DOWN) {
+		pr_debug("%s: Service %s is down!", __func__,
+			audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
+			service_name);
+		goto done;
+	}
+
+	memcpy(&audio_pdr_services, data,
+		sizeof(audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP]));
+	if (audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].total_domains == 1) {
+		pr_debug("%s: Service %s, returned total domains %d, ",
+			__func__,
+			audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
+			audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
+			total_domains);
+		pdr_state = AUDIO_PDR_FRAMEWORK_UP;
+		goto done;
+	} else
+		pr_err("%s: Service %s returned invalid total domains %d",
+			__func__,
+			audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
+			audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].
+			total_domains);
+done:
+	srcu_notifier_call_chain(&audio_pdr_cb_list, pdr_state, NULL);
+	return NOTIFY_OK;
+}
+
+static struct notifier_block audio_pdr_locator_nb = {
+	.notifier_call = audio_pdr_locator_callback,
+	.priority = 0,
+};
+
+int audio_pdr_register(struct notifier_block *nb)
+{
+	if (nb == NULL) {
+		pr_err("%s: Notifier block is NULL\n", __func__);
+		return -EINVAL;
+	}
+	return srcu_notifier_chain_register(&audio_pdr_cb_list, nb);
+}
+EXPORT_SYMBOL(audio_pdr_register);
+
+void *audio_pdr_service_register(int domain_id,
+				 struct notifier_block *nb, int *curr_state)
+{
+	void *handle;
+
+	if ((domain_id < 0) ||
+	    (domain_id >= AUDIO_PDR_DOMAIN_MAX)) {
+		pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
+		return ERR_PTR(-EINVAL);
+	}
+
+	handle = service_notif_register_notifier(
+		audio_pdr_services[domain_id].domain_list[0].name,
+		audio_pdr_services[domain_id].domain_list[0].instance_id,
+		nb, curr_state);
+	if (IS_ERR_OR_NULL(handle)) {
+		pr_err("%s: Failed to register for service %s, instance %d\n",
+			__func__,
+			audio_pdr_services[domain_id].domain_list[0].name,
+			audio_pdr_services[domain_id].domain_list[0].
+			instance_id);
+	}
+	return handle;
+}
+EXPORT_SYMBOL(audio_pdr_service_register);
+
+int audio_pdr_service_deregister(void *service_handle,
+	struct notifier_block *nb)
+{
+	int ret;
+
+	if (service_handle == NULL) {
+		pr_err("%s: service handle is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = service_notif_unregister_notifier(
+		service_handle, nb);
+	if (ret < 0)
+		pr_err("%s: Failed to deregister service ret %d\n",
+			__func__, ret);
+done:
+	return ret;
+}
+EXPORT_SYMBOL(audio_pdr_service_deregister);
+
+static int __init audio_pdr_subsys_init(void)
+{
+	srcu_init_notifier_head(&audio_pdr_cb_list);
+	return 0;
+}
+subsys_initcall(audio_pdr_subsys_init);
+
+static int __init audio_pdr_late_init(void)
+{
+	int ret;
+
+	ret = get_service_location(
+		audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].client_name,
+		audio_pdr_services[AUDIO_PDR_DOMAIN_ADSP].service_name,
+		&audio_pdr_locator_nb);
+	if (ret < 0) {
+		pr_err("%s get_service_location failed ret %d\n",
+			__func__, ret);
+		srcu_notifier_call_chain(&audio_pdr_cb_list,
+					 AUDIO_PDR_FRAMEWORK_DOWN, NULL);
+	}
+
+	return ret;
+}
+late_initcall(audio_pdr_late_init);
diff --git a/drivers/soc/qcom/qdsp6v2/audio_ssr.c b/drivers/soc/qcom/qdsp6v2/audio_ssr.c
new file mode 100644
index 0000000..a66fb2a
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/audio_ssr.c
@@ -0,0 +1,66 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/qdsp6v2/audio_ssr.h>
+#include <soc/qcom/scm.h>
+#include <soc/qcom/subsystem_restart.h>
+#include <soc/qcom/subsystem_notif.h>
+
+#define SCM_Q6_NMI_CMD 0x1
+
+static char *audio_ssr_domains[] = {
+	"adsp",
+	"modem"
+};
+
+void *audio_ssr_register(int domain_id, struct notifier_block *nb)
+{
+	if ((domain_id < 0) ||
+	    (domain_id >= AUDIO_SSR_DOMAIN_MAX)) {
+		pr_err("%s: Invalid service ID %d\n", __func__, domain_id);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return subsys_notif_register_notifier(
+		audio_ssr_domains[domain_id], nb);
+}
+EXPORT_SYMBOL(audio_ssr_register);
+
+int audio_ssr_deregister(void *handle, struct notifier_block *nb)
+{
+	return subsys_notif_unregister_notifier(handle, nb);
+}
+EXPORT_SYMBOL(audio_ssr_deregister);
+
+void audio_ssr_send_nmi(void *ssr_cb_data)
+{
+	struct notif_data *data = (struct notif_data *)ssr_cb_data;
+	struct scm_desc desc;
+
+	if (data && data->crashed) {
+		/* Send NMI to QDSP6 via an SCM call. */
+		if (!is_scm_armv8()) {
+			scm_call_atomic1(SCM_SVC_UTIL,
+					 SCM_Q6_NMI_CMD, 0x1);
+		} else {
+			desc.args[0] = 0x1;
+			desc.arginfo = SCM_ARGS(1);
+			scm_call2_atomic(SCM_SIP_FNID(SCM_SVC_UTIL,
+					 SCM_Q6_NMI_CMD), &desc);
+		}
+		/* The write should go through before q6 is shutdown */
+		mb();
+		pr_debug("%s: Q6 NMI was sent.\n", __func__);
+	}
+}
+EXPORT_SYMBOL(audio_ssr_send_nmi);
diff --git a/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
new file mode 100644
index 0000000..6ba20a4
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/msm_audio_ion.c
@@ -0,0 +1,917 @@
+/*
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-buf.h>
+#include <linux/iommu.h>
+#include <linux/platform_device.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/of_device.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/export.h>
+#include <linux/qcom_iommu.h>
+#include <asm/dma-iommu.h>
+
+#define MSM_AUDIO_ION_PROBED (1 << 0)
+
+#define MSM_AUDIO_ION_PHYS_ADDR(alloc_data) \
+	alloc_data->table->sgl->dma_address
+
+#define MSM_AUDIO_ION_VA_START 0x10000000
+#define MSM_AUDIO_ION_VA_LEN 0x0FFFFFFF
+
+#define MSM_AUDIO_SMMU_SID_OFFSET 32
+
+struct addr_range {
+	dma_addr_t start;
+	size_t size;
+};
+
+struct context_bank_info {
+	const char *name;
+	struct addr_range addr_range;
+};
+
+struct msm_audio_ion_private {
+	bool smmu_enabled;
+	bool audioheap_enabled;
+	struct device *cb_dev;
+	struct dma_iommu_mapping *mapping;
+	u8 device_status;
+	struct list_head alloc_list;
+	struct mutex list_mutex;
+	u64 smmu_sid_bits;
+	u32 smmu_version;
+};
+
+struct msm_audio_alloc_data {
+	struct ion_client *client;
+	struct ion_handle *handle;
+	size_t len;
+	struct dma_buf *dma_buf;
+	struct dma_buf_attachment *attach;
+	struct sg_table *table;
+	struct list_head list;
+};
+
+static struct msm_audio_ion_private msm_audio_ion_data = {0,};
+
+static int msm_audio_ion_get_phys(struct ion_client *client,
+				  struct ion_handle *handle,
+				  ion_phys_addr_t *addr, size_t *len);
+
+static int msm_audio_dma_buf_map(struct ion_client *client,
+				  struct ion_handle *handle,
+				  ion_phys_addr_t *addr, size_t *len);
+
+static int msm_audio_dma_buf_unmap(struct ion_client *client,
+				   struct ion_handle *handle);
+
+static void msm_audio_ion_add_allocation(
+	struct msm_audio_ion_private *msm_audio_ion_data,
+	struct msm_audio_alloc_data *alloc_data)
+{
+	/*
+	 * Since these APIs can be invoked by multiple
+	 * clients, there is need to make sure the list
+	 * of allocations is always protected
+	 */
+	mutex_lock(&(msm_audio_ion_data->list_mutex));
+	list_add_tail(&(alloc_data->list),
+		      &(msm_audio_ion_data->alloc_list));
+	mutex_unlock(&(msm_audio_ion_data->list_mutex));
+}
+
+int msm_audio_ion_alloc(const char *name, struct ion_client **client,
+			struct ion_handle **handle, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+	int rc = -EINVAL;
+	unsigned long err_ion_ptr = 0;
+
+	if ((msm_audio_ion_data.smmu_enabled == true) &&
+	    !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+		pr_debug("%s:probe is not done, deferred\n", __func__);
+		return -EPROBE_DEFER;
+	}
+	if (!name || !client || !handle || !paddr || !vaddr
+		|| !bufsz || !pa_len) {
+		pr_err("%s: Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	*client = msm_audio_ion_client_create(name);
+	if (IS_ERR_OR_NULL((void *)(*client))) {
+		pr_err("%s: ION create client for AUDIO failed\n", __func__);
+		goto err;
+	}
+
+	*handle = ion_alloc(*client, bufsz, SZ_4K,
+			ION_HEAP(ION_AUDIO_HEAP_ID), 0);
+	if (IS_ERR_OR_NULL((void *) (*handle))) {
+		if (msm_audio_ion_data.smmu_enabled == true) {
+			pr_debug("system heap is used");
+			msm_audio_ion_data.audioheap_enabled = 0;
+			*handle = ion_alloc(*client, bufsz, SZ_4K,
+					ION_HEAP(ION_SYSTEM_HEAP_ID), 0);
+		}
+		if (IS_ERR_OR_NULL((void *) (*handle))) {
+			if (IS_ERR((void *)(*handle)))
+				err_ion_ptr = PTR_ERR((int *)(*handle));
+			pr_err("%s:ION alloc fail err ptr=%ld, smmu_enabled=%d\n",
+			__func__, err_ion_ptr, msm_audio_ion_data.smmu_enabled);
+			rc = -ENOMEM;
+			goto err_ion_client;
+		}
+	} else {
+		pr_debug("audio heap is used");
+		msm_audio_ion_data.audioheap_enabled = 1;
+	}
+
+	rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
+	if (rc) {
+		pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+			__func__, rc);
+		goto err_ion_handle;
+	}
+
+	*vaddr = ion_map_kernel(*client, *handle);
+	if (IS_ERR_OR_NULL((void *)*vaddr)) {
+		pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+		goto err_ion_handle;
+	}
+	pr_debug("%s: mapped address = %pK, size=%zd\n", __func__,
+		*vaddr, bufsz);
+
+	if (bufsz != 0) {
+		pr_debug("%s: memset to 0 %pK %zd\n", __func__, *vaddr, bufsz);
+		memset((void *)*vaddr, 0, bufsz);
+	}
+
+	return rc;
+
+err_ion_handle:
+	ion_free(*client, *handle);
+err_ion_client:
+	msm_audio_ion_client_destroy(*client);
+	*handle = NULL;
+	*client = NULL;
+err:
+	return rc;
+}
+EXPORT_SYMBOL(msm_audio_ion_alloc);
+
+int msm_audio_ion_import(const char *name, struct ion_client **client,
+			struct ion_handle **handle, int fd,
+			unsigned long *ionflag, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+	int rc = 0;
+
+	if ((msm_audio_ion_data.smmu_enabled == true) &&
+	    !(msm_audio_ion_data.device_status & MSM_AUDIO_ION_PROBED)) {
+		pr_debug("%s:probe is not done, deferred\n", __func__);
+		return -EPROBE_DEFER;
+	}
+
+	if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
+		pr_err("%s: Invalid params\n", __func__);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	*client = msm_audio_ion_client_create(name);
+	if (IS_ERR_OR_NULL((void *)(*client))) {
+		pr_err("%s: ION create client for AUDIO failed\n", __func__);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	/* name should be audio_acdb_client or Audio_Dec_Client,
+	 * bufsz should be 0 and fd shouldn't be 0 as of now
+	 */
+	*handle = ion_import_dma_buf_fd(*client, fd);
+	pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__,
+							name, fd, *handle);
+	if (IS_ERR_OR_NULL((void *) (*handle))) {
+		pr_err("%s: ion import dma buffer failed\n",
+				__func__);
+		rc = -EINVAL;
+		goto err_destroy_client;
+	}
+
+	if (ionflag != NULL) {
+		rc = ion_handle_get_flags(*client, *handle, ionflag);
+		if (rc) {
+			pr_err("%s: could not get flags for the handle\n",
+				__func__);
+			goto err_ion_handle;
+		}
+	}
+
+	rc = msm_audio_ion_get_phys(*client, *handle, paddr, pa_len);
+	if (rc) {
+		pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+				__func__, rc);
+		goto err_ion_handle;
+	}
+
+	*vaddr = ion_map_kernel(*client, *handle);
+	if (IS_ERR_OR_NULL((void *)*vaddr)) {
+		pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+		rc = -ENOMEM;
+		goto err_ion_handle;
+	}
+	pr_debug("%s: mapped address = %pK, size=%zd\n", __func__,
+		*vaddr, bufsz);
+
+	return 0;
+
+err_ion_handle:
+	ion_free(*client, *handle);
+err_destroy_client:
+	msm_audio_ion_client_destroy(*client);
+	*client = NULL;
+	*handle = NULL;
+err:
+	return rc;
+}
+
+int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle)
+{
+	if (!client || !handle) {
+		pr_err("%s Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	if (msm_audio_ion_data.smmu_enabled)
+		msm_audio_dma_buf_unmap(client, handle);
+
+	ion_unmap_kernel(client, handle);
+
+	ion_free(client, handle);
+	msm_audio_ion_client_destroy(client);
+	return 0;
+}
+EXPORT_SYMBOL(msm_audio_ion_free);
+
+int msm_audio_ion_mmap(struct audio_buffer *ab,
+		       struct vm_area_struct *vma)
+{
+	struct sg_table *table;
+	unsigned long addr = vma->vm_start;
+	unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
+	struct scatterlist *sg;
+	unsigned int i;
+	struct page *page;
+	int ret;
+
+	pr_debug("%s\n", __func__);
+
+	table = ion_sg_table(ab->client, ab->handle);
+
+	if (IS_ERR(table)) {
+		pr_err("%s: Unable to get sg_table from ion: %ld\n",
+			__func__, PTR_ERR(table));
+		return PTR_ERR(table);
+	} else if (!table) {
+		pr_err("%s: sg_list is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/* uncached */
+	vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	/* We need to check if a page is associated with this sg list because:
+	 * If the allocation came from a carveout we currently don't have
+	 * pages associated with carved out memory. This might change in the
+	 * future and we can remove this check and the else statement.
+	 */
+	page = sg_page(table->sgl);
+	if (page) {
+		pr_debug("%s: page is NOT null\n", __func__);
+		for_each_sg(table->sgl, sg, table->nents, i) {
+			unsigned long remainder = vma->vm_end - addr;
+			unsigned long len = sg->length;
+
+			page = sg_page(sg);
+
+			if (offset >= len) {
+				offset -= len;
+				continue;
+			} else if (offset) {
+				page += offset / PAGE_SIZE;
+				len -= offset;
+				offset = 0;
+			}
+			len = min(len, remainder);
+			pr_debug("vma=%pK, addr=%x len=%ld vm_start=%x vm_end=%x vm_page_prot=%lu\n",
+				vma, (unsigned int)addr, len,
+				(unsigned int)vma->vm_start,
+				(unsigned int)vma->vm_end,
+				(unsigned long)vma->vm_page_prot.pgprot);
+			remap_pfn_range(vma, addr, page_to_pfn(page), len,
+					vma->vm_page_prot);
+			addr += len;
+			if (addr >= vma->vm_end)
+				return 0;
+		}
+	} else {
+		ion_phys_addr_t phys_addr;
+		size_t phys_len;
+		size_t va_len = 0;
+
+		pr_debug("%s: page is NULL\n", __func__);
+		ret = ion_phys(ab->client, ab->handle, &phys_addr, &phys_len);
+		if (ret) {
+			pr_err("%s: Unable to get phys address from ION buffer: %d\n"
+				, __func__, ret);
+			return ret;
+		}
+		pr_debug("phys=%pKK len=%zd\n", &phys_addr, phys_len);
+		pr_debug("vma=%pK, vm_start=%x vm_end=%x vm_pgoff=%ld vm_page_prot=%lu\n",
+			vma, (unsigned int)vma->vm_start,
+			(unsigned int)vma->vm_end, vma->vm_pgoff,
+			(unsigned long)vma->vm_page_prot.pgprot);
+		va_len = vma->vm_end - vma->vm_start;
+		if ((offset > phys_len) || (va_len > phys_len-offset)) {
+			pr_err("wrong offset size %ld, lens= %zd, va_len=%zd\n",
+				offset, phys_len, va_len);
+			return -EINVAL;
+		}
+		ret =  remap_pfn_range(vma, vma->vm_start,
+				__phys_to_pfn(phys_addr) + vma->vm_pgoff,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot);
+	}
+	return 0;
+}
+
+
+bool msm_audio_ion_is_smmu_available(void)
+{
+	return msm_audio_ion_data.smmu_enabled;
+}
+
+/* move to static section again */
+struct ion_client *msm_audio_ion_client_create(const char *name)
+{
+	struct ion_client *pclient = NULL;
+
+	pclient = msm_ion_client_create(name);
+	return pclient;
+}
+
+
+void msm_audio_ion_client_destroy(struct ion_client *client)
+{
+	pr_debug("%s: client = %pK smmu_enabled = %d\n", __func__,
+		client, msm_audio_ion_data.smmu_enabled);
+
+	ion_client_destroy(client);
+}
+
+int msm_audio_ion_import_legacy(const char *name, struct ion_client *client,
+			struct ion_handle **handle, int fd,
+			unsigned long *ionflag, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr)
+{
+	int rc = 0;
+
+	if (!name || !client || !handle || !paddr || !vaddr || !pa_len) {
+		pr_err("%s: Invalid params\n", __func__);
+		rc = -EINVAL;
+		goto err;
+	}
+	/* client is already created for legacy and given
+	 * name should be audio_acdb_client or Audio_Dec_Client,
+	 * bufsz should be 0 and fd shouldn't be 0 as of now
+	 */
+	*handle = ion_import_dma_buf_fd(client, fd);
+	pr_debug("%s: DMA Buf name=%s, fd=%d handle=%pK\n", __func__,
+							name, fd, *handle);
+	if (IS_ERR_OR_NULL((void *)(*handle))) {
+		pr_err("%s: ion import dma buffer failed\n",
+			__func__);
+		rc = -EINVAL;
+		goto err;
+	}
+
+	if (ionflag != NULL) {
+		rc = ion_handle_get_flags(client, *handle, ionflag);
+		if (rc) {
+			pr_err("%s: could not get flags for the handle\n",
+							__func__);
+			rc = -EINVAL;
+			goto err_ion_handle;
+		}
+	}
+
+	rc = msm_audio_ion_get_phys(client, *handle, paddr, pa_len);
+	if (rc) {
+		pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+			__func__, rc);
+		rc = -EINVAL;
+		goto err_ion_handle;
+	}
+
+	/*Need to add condition SMMU enable or not */
+	*vaddr = ion_map_kernel(client, *handle);
+	if (IS_ERR_OR_NULL((void *)*vaddr)) {
+		pr_err("%s: ION memory mapping for AUDIO failed\n", __func__);
+		rc = -EINVAL;
+		goto err_ion_handle;
+	}
+
+	if (bufsz != 0)
+		memset((void *)*vaddr, 0, bufsz);
+
+	return 0;
+
+err_ion_handle:
+	ion_free(client, *handle);
+err:
+	return rc;
+}
+
+int msm_audio_ion_free_legacy(struct ion_client *client,
+			      struct ion_handle *handle)
+{
+	if (msm_audio_ion_data.smmu_enabled)
+		msm_audio_dma_buf_unmap(client, handle);
+
+	ion_unmap_kernel(client, handle);
+
+	ion_free(client, handle);
+	/* no client_destrody in legacy*/
+	return 0;
+}
+
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op)
+{
+	unsigned long ionflag = 0;
+	int rc = 0;
+	int msm_cache_ops = 0;
+
+	if (!abuff) {
+		pr_err("%s: Invalid params: %pK\n", __func__, abuff);
+		return -EINVAL;
+	}
+	rc = ion_handle_get_flags(abuff->client, abuff->handle,
+		&ionflag);
+	if (rc) {
+		pr_err("ion_handle_get_flags failed: %d\n", rc);
+		goto cache_op_failed;
+	}
+
+	/* has to be CACHED */
+	if (ION_IS_CACHED(ionflag)) {
+		/* ION_IOC_INV_CACHES or ION_IOC_CLEAN_CACHES */
+		msm_cache_ops = cache_op;
+		rc = msm_ion_do_cache_op(abuff->client,
+				abuff->handle,
+				(unsigned long *) abuff->data,
+				(unsigned long)abuff->size,
+				msm_cache_ops);
+		if (rc) {
+			pr_err("cache operation failed %d\n", rc);
+			goto cache_op_failed;
+		}
+	}
+cache_op_failed:
+	return rc;
+}
+
+
+static int msm_audio_dma_buf_map(struct ion_client *client,
+		struct ion_handle *handle,
+		ion_phys_addr_t *addr, size_t *len)
+{
+
+	struct msm_audio_alloc_data *alloc_data;
+	struct device *cb_dev;
+	int rc = 0;
+
+	cb_dev = msm_audio_ion_data.cb_dev;
+
+	/* Data required per buffer mapping */
+	alloc_data = kzalloc(sizeof(*alloc_data), GFP_KERNEL);
+	if (!alloc_data)
+		return -ENOMEM;
+
+	/* Get the ION handle size */
+	ion_handle_get_size(client, handle, len);
+
+	alloc_data->client = client;
+	alloc_data->handle = handle;
+	alloc_data->len = *len;
+
+	/* Get the dma_buf handle from ion_handle */
+	alloc_data->dma_buf = ion_share_dma_buf(client, handle);
+	if (IS_ERR(alloc_data->dma_buf)) {
+		rc = PTR_ERR(alloc_data->dma_buf);
+		dev_err(cb_dev,
+			"%s: Fail to get dma_buf handle, rc = %d\n",
+			__func__, rc);
+		goto err_dma_buf;
+	}
+
+	/* Attach the dma_buf to context bank device */
+	alloc_data->attach = dma_buf_attach(alloc_data->dma_buf,
+					    cb_dev);
+	if (IS_ERR(alloc_data->attach)) {
+		rc = PTR_ERR(alloc_data->attach);
+		dev_err(cb_dev,
+			"%s: Fail to attach dma_buf to CB, rc = %d\n",
+			__func__, rc);
+		goto err_attach;
+	}
+
+	/*
+	 * Get the scatter-gather list.
+	 * There is no info as this is a write buffer or
+	 * read buffer, hence the request is bi-directional
+	 * to accommodate both read and write mappings.
+	 */
+	alloc_data->table = dma_buf_map_attachment(alloc_data->attach,
+				DMA_BIDIRECTIONAL);
+	if (IS_ERR(alloc_data->table)) {
+		rc = PTR_ERR(alloc_data->table);
+		dev_err(cb_dev,
+			"%s: Fail to map attachment, rc = %d\n",
+			__func__, rc);
+		goto err_map_attach;
+	}
+
+	rc = dma_map_sg(cb_dev, alloc_data->table->sgl,
+			alloc_data->table->nents,
+			DMA_BIDIRECTIONAL);
+	if (rc != alloc_data->table->nents) {
+		dev_err(cb_dev,
+			"%s: Fail to map SG, rc = %d, nents = %d\n",
+			__func__, rc, alloc_data->table->nents);
+		goto err_map_sg;
+	}
+	/* Make sure not to return rc from dma_map_sg, as it can be nonzero */
+	rc = 0;
+
+	/* physical address from mapping */
+	*addr = MSM_AUDIO_ION_PHYS_ADDR(alloc_data);
+
+	msm_audio_ion_add_allocation(&msm_audio_ion_data,
+				     alloc_data);
+	return rc;
+
+err_map_sg:
+	dma_buf_unmap_attachment(alloc_data->attach,
+				 alloc_data->table,
+				 DMA_BIDIRECTIONAL);
+err_map_attach:
+	dma_buf_detach(alloc_data->dma_buf,
+		       alloc_data->attach);
+err_attach:
+	dma_buf_put(alloc_data->dma_buf);
+
+err_dma_buf:
+	kfree(alloc_data);
+
+	return rc;
+}
+
+static int msm_audio_dma_buf_unmap(struct ion_client *client,
+				   struct ion_handle *handle)
+{
+	int rc = 0;
+	struct msm_audio_alloc_data *alloc_data = NULL;
+	struct list_head *ptr, *next;
+	struct device *cb_dev = msm_audio_ion_data.cb_dev;
+	bool found = false;
+
+	/*
+	 * Though list_for_each_safe is delete safe, lock
+	 * should be explicitly acquired to avoid race condition
+	 * on adding elements to the list.
+	 */
+	mutex_lock(&(msm_audio_ion_data.list_mutex));
+	list_for_each_safe(ptr, next,
+			    &(msm_audio_ion_data.alloc_list)) {
+
+		alloc_data = list_entry(ptr, struct msm_audio_alloc_data,
+					list);
+
+		if (alloc_data->handle == handle &&
+		    alloc_data->client == client) {
+			found = true;
+			dma_unmap_sg(cb_dev,
+				    alloc_data->table->sgl,
+				    alloc_data->table->nents,
+				    DMA_BIDIRECTIONAL);
+
+			dma_buf_unmap_attachment(alloc_data->attach,
+						 alloc_data->table,
+						 DMA_BIDIRECTIONAL);
+
+			dma_buf_detach(alloc_data->dma_buf,
+				       alloc_data->attach);
+
+			dma_buf_put(alloc_data->dma_buf);
+
+			list_del(&(alloc_data->list));
+			kfree(alloc_data);
+			break;
+		}
+	}
+	mutex_unlock(&(msm_audio_ion_data.list_mutex));
+
+	if (!found) {
+		dev_err(cb_dev,
+			"%s: cannot find allocation, ion_handle %pK, ion_client %pK",
+			__func__, handle, client);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int msm_audio_ion_get_phys(struct ion_client *client,
+		struct ion_handle *handle,
+		ion_phys_addr_t *addr, size_t *len)
+{
+	int rc = 0;
+
+	pr_debug("%s: smmu_enabled = %d\n", __func__,
+		msm_audio_ion_data.smmu_enabled);
+
+	if (msm_audio_ion_data.smmu_enabled) {
+		rc = msm_audio_dma_buf_map(client, handle, addr, len);
+		if (rc) {
+			pr_err("%s: failed to map DMA buf, err = %d\n",
+				__func__, rc);
+			goto err;
+		}
+		/* Append the SMMU SID information to the IOVA address */
+		*addr |= msm_audio_ion_data.smmu_sid_bits;
+	} else {
+		rc = ion_phys(client, handle, addr, len);
+	}
+
+	pr_debug("phys=%pK, len=%zd, rc=%d\n", &(*addr), *len, rc);
+err:
+	return rc;
+}
+
+static int msm_audio_smmu_init_legacy(struct device *dev)
+{
+	struct dma_iommu_mapping *mapping;
+	struct device_node *ctx_node = NULL;
+	struct context_bank_info *cb;
+	int ret;
+	u32 read_val[2];
+
+	cb = devm_kzalloc(dev, sizeof(struct context_bank_info), GFP_KERNEL);
+	if (!cb)
+		return -ENOMEM;
+
+	ctx_node = of_parse_phandle(dev->of_node, "iommus", 0);
+	if (!ctx_node) {
+		dev_err(dev, "%s Could not find any iommus for audio\n",
+			__func__);
+		return -EINVAL;
+	}
+	ret = of_property_read_string(ctx_node, "label", &(cb->name));
+	if (ret) {
+		dev_err(dev, "%s Could not find label\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("label found : %s\n", cb->name);
+	ret = of_property_read_u32_array(ctx_node,
+				"qcom,virtual-addr-pool",
+				read_val, 2);
+	if (ret) {
+		dev_err(dev, "%s Could not read addr pool for group : (%d)\n",
+			__func__, ret);
+		return -EINVAL;
+	}
+	msm_audio_ion_data.cb_dev = msm_iommu_get_ctx(cb->name);
+	cb->addr_range.start = (dma_addr_t) read_val[0];
+	cb->addr_range.size = (size_t) read_val[1];
+	dev_dbg(dev, "%s Legacy iommu usage\n", __func__);
+	mapping = arm_iommu_create_mapping(
+				msm_iommu_get_bus(msm_audio_ion_data.cb_dev),
+					   cb->addr_range.start,
+					   cb->addr_range.size);
+	if (IS_ERR(mapping))
+		return PTR_ERR(mapping);
+
+	ret = arm_iommu_attach_device(msm_audio_ion_data.cb_dev, mapping);
+	if (ret) {
+		dev_err(dev, "%s: Attach failed, err = %d\n",
+			__func__, ret);
+		goto fail_attach;
+	}
+
+	msm_audio_ion_data.mapping = mapping;
+	INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list);
+	mutex_init(&(msm_audio_ion_data.list_mutex));
+
+	return 0;
+
+fail_attach:
+	arm_iommu_release_mapping(mapping);
+	return ret;
+}
+
+static int msm_audio_smmu_init(struct device *dev)
+{
+	struct dma_iommu_mapping *mapping;
+	int ret;
+
+	mapping = arm_iommu_create_mapping(
+					msm_iommu_get_bus(dev),
+					   MSM_AUDIO_ION_VA_START,
+					   MSM_AUDIO_ION_VA_LEN);
+	if (IS_ERR(mapping))
+		return PTR_ERR(mapping);
+
+	ret = arm_iommu_attach_device(dev, mapping);
+	if (ret) {
+		dev_err(dev, "%s: Attach failed, err = %d\n",
+			__func__, ret);
+		goto fail_attach;
+	}
+
+	msm_audio_ion_data.cb_dev = dev;
+	msm_audio_ion_data.mapping = mapping;
+	INIT_LIST_HEAD(&msm_audio_ion_data.alloc_list);
+	mutex_init(&(msm_audio_ion_data.list_mutex));
+
+	return 0;
+
+fail_attach:
+	arm_iommu_release_mapping(mapping);
+	return ret;
+}
+
+static const struct of_device_id msm_audio_ion_dt_match[] = {
+	{ .compatible = "qcom,msm-audio-ion" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, msm_audio_ion_dt_match);
+
+
+u32 msm_audio_ion_get_smmu_sid_mode32(void)
+{
+	if (msm_audio_ion_data.smmu_enabled)
+		return upper_32_bits(msm_audio_ion_data.smmu_sid_bits);
+	else
+		return 0;
+}
+
+u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa)
+{
+	if (sizeof(ion_phys_addr_t) == sizeof(u32))
+		return msm_audio_ion_get_smmu_sid_mode32();
+	else
+		return upper_32_bits(pa);
+}
+
+static int msm_audio_ion_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	const char *msm_audio_ion_dt = "qcom,smmu-enabled";
+	const char *msm_audio_ion_smmu = "qcom,smmu-version";
+	bool smmu_enabled;
+	enum apr_subsys_state q6_state;
+	struct device *dev = &pdev->dev;
+
+	if (dev->of_node == NULL) {
+		dev_err(dev,
+			"%s: device tree is not found\n",
+			__func__);
+		msm_audio_ion_data.smmu_enabled = 0;
+		return 0;
+	}
+
+	smmu_enabled = of_property_read_bool(dev->of_node,
+					     msm_audio_ion_dt);
+	msm_audio_ion_data.smmu_enabled = smmu_enabled;
+
+	if (smmu_enabled) {
+		rc = of_property_read_u32(dev->of_node,
+					msm_audio_ion_smmu,
+					&msm_audio_ion_data.smmu_version);
+		if (rc) {
+			dev_err(dev,
+				"%s: qcom,smmu_version missing in DT node\n",
+				__func__);
+			return rc;
+		}
+		dev_dbg(dev, "%s: SMMU version is (%d)", __func__,
+				msm_audio_ion_data.smmu_version);
+		q6_state = apr_get_q6_state();
+		if (q6_state == APR_SUBSYS_DOWN) {
+			dev_dbg(dev,
+				"defering %s, adsp_state %d\n",
+				__func__, q6_state);
+			return -EPROBE_DEFER;
+		}
+		dev_dbg(dev, "%s: adsp is ready\n", __func__);
+	}
+
+	dev_dbg(dev, "%s: SMMU is %s\n", __func__,
+		(smmu_enabled) ? "Enabled" : "Disabled");
+
+	if (smmu_enabled) {
+		u64 smmu_sid = 0;
+		struct of_phandle_args iommuspec;
+
+		/* Get SMMU SID information from Devicetree */
+		rc = of_parse_phandle_with_args(dev->of_node, "iommus",
+						"#iommu-cells", 0, &iommuspec);
+		if (rc)
+			dev_err(dev, "%s: could not get smmu SID, ret = %d\n",
+				__func__, rc);
+		else
+			smmu_sid = iommuspec.args[0];
+
+		msm_audio_ion_data.smmu_sid_bits =
+			smmu_sid << MSM_AUDIO_SMMU_SID_OFFSET;
+
+		if (msm_audio_ion_data.smmu_version == 0x1) {
+			rc = msm_audio_smmu_init_legacy(dev);
+		} else if (msm_audio_ion_data.smmu_version == 0x2) {
+			rc = msm_audio_smmu_init(dev);
+		} else {
+			dev_err(dev, "%s: smmu version invalid %d\n",
+				__func__, msm_audio_ion_data.smmu_version);
+			rc = -EINVAL;
+		}
+		if (rc)
+			dev_err(dev, "%s: smmu init failed, err = %d\n",
+				__func__, rc);
+	}
+
+	if (!rc)
+		msm_audio_ion_data.device_status |= MSM_AUDIO_ION_PROBED;
+
+	return rc;
+}
+
+static int msm_audio_ion_remove(struct platform_device *pdev)
+{
+	struct dma_iommu_mapping *mapping;
+	struct device *audio_cb_dev;
+
+	mapping = msm_audio_ion_data.mapping;
+	audio_cb_dev = msm_audio_ion_data.cb_dev;
+
+	if (audio_cb_dev && mapping) {
+		arm_iommu_detach_device(audio_cb_dev);
+		arm_iommu_release_mapping(mapping);
+	}
+
+	msm_audio_ion_data.smmu_enabled = 0;
+	msm_audio_ion_data.device_status = 0;
+	return 0;
+}
+
+static struct platform_driver msm_audio_ion_driver = {
+	.driver = {
+		.name = "msm-audio-ion",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_audio_ion_dt_match,
+	},
+	.probe = msm_audio_ion_probe,
+	.remove = msm_audio_ion_remove,
+};
+
+static int __init msm_audio_ion_init(void)
+{
+	return platform_driver_register(&msm_audio_ion_driver);
+}
+module_init(msm_audio_ion_init);
+
+static void __exit msm_audio_ion_exit(void)
+{
+	platform_driver_unregister(&msm_audio_ion_driver);
+}
+module_exit(msm_audio_ion_exit);
+
+MODULE_DESCRIPTION("MSM Audio ION module");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/qdsp6v2/voice_svc.c b/drivers/soc/qcom/qdsp6v2/voice_svc.c
new file mode 100644
index 0000000..07e8991
--- /dev/null
+++ b/drivers/soc/qcom/qdsp6v2/voice_svc.c
@@ -0,0 +1,792 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/cdev.h>
+#include <linux/qdsp6v2/apr_tal.h>
+#include <linux/qdsp6v2/apr.h>
+#include <sound/voice_svc.h>
+
+#define MINOR_NUMBER 1
+#define APR_MAX_RESPONSE 10
+#define TIMEOUT_MS 1000
+
+#define MAX(a, b) ((a) >= (b) ? (a) : (b))
+
+struct voice_svc_device {
+	struct cdev *cdev;
+	struct device *dev;
+	int major;
+};
+
+struct voice_svc_prvt {
+	void *apr_q6_mvm;
+	void *apr_q6_cvs;
+	uint16_t response_count;
+	struct list_head response_queue;
+	wait_queue_head_t response_wait;
+	spinlock_t response_lock;
+};
+
+struct apr_data {
+	struct apr_hdr hdr;
+	__u8 payload[0];
+} __packed;
+
+struct apr_response_list {
+	struct list_head list;
+	struct voice_svc_cmd_response resp;
+};
+
+static struct voice_svc_device *voice_svc_dev;
+static struct class *voice_svc_class;
+static bool reg_dummy_sess;
+static void *dummy_q6_mvm;
+static void *dummy_q6_cvs;
+dev_t device_num;
+
+static int voice_svc_dummy_reg(void);
+static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data,
+					void *priv);
+
+static int32_t qdsp_apr_callback(struct apr_client_data *data, void *priv)
+{
+	struct voice_svc_prvt *prtd;
+	struct apr_response_list *response_list;
+	unsigned long spin_flags;
+
+	if ((data == NULL) || (priv == NULL)) {
+		pr_err("%s: data or priv is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	prtd = (struct voice_svc_prvt *)priv;
+	if (prtd == NULL) {
+		pr_err("%s: private data is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	pr_debug("%s: data->opcode %x\n", __func__,
+		 data->opcode);
+
+	if (data->opcode == RESET_EVENTS) {
+		if (data->reset_proc == APR_DEST_QDSP6) {
+			pr_debug("%s: Received ADSP reset event\n", __func__);
+
+			if (prtd->apr_q6_mvm != NULL) {
+				apr_reset(prtd->apr_q6_mvm);
+				prtd->apr_q6_mvm = NULL;
+			}
+
+			if (prtd->apr_q6_cvs != NULL) {
+				apr_reset(prtd->apr_q6_cvs);
+				prtd->apr_q6_cvs = NULL;
+			}
+		} else if (data->reset_proc == APR_DEST_MODEM) {
+			pr_debug("%s: Received Modem reset event\n", __func__);
+		}
+		/* Set the remaining member variables to default values
+		 * for RESET_EVENTS
+		 */
+		data->payload_size = 0;
+		data->payload = NULL;
+		data->src_port = 0;
+		data->dest_port = 0;
+		data->token = 0;
+	}
+
+	spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+	if (prtd->response_count < APR_MAX_RESPONSE) {
+		response_list = kmalloc(sizeof(struct apr_response_list) +
+					data->payload_size, GFP_ATOMIC);
+		if (response_list == NULL) {
+			spin_unlock_irqrestore(&prtd->response_lock,
+					       spin_flags);
+			return -ENOMEM;
+		}
+
+		response_list->resp.src_port = data->src_port;
+
+		/* Reverting the bit manipulation done in voice_svc_update_hdr
+		 * to the src_port which is returned to us as dest_port.
+		 */
+		response_list->resp.dest_port = ((data->dest_port) >> 8);
+		response_list->resp.token = data->token;
+		response_list->resp.opcode = data->opcode;
+		response_list->resp.payload_size = data->payload_size;
+		if (data->payload != NULL && data->payload_size > 0) {
+			memcpy(response_list->resp.payload, data->payload,
+			       data->payload_size);
+		}
+
+		list_add_tail(&response_list->list, &prtd->response_queue);
+		prtd->response_count++;
+		spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+
+		wake_up(&prtd->response_wait);
+	} else {
+		spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+		pr_err("%s: Response dropped since the queue is full\n",
+		       __func__);
+	}
+
+	return 0;
+}
+
+static int32_t qdsp_dummy_apr_callback(struct apr_client_data *data, void *priv)
+{
+	/* Do Nothing */
+	return 0;
+}
+
+static void voice_svc_update_hdr(struct voice_svc_cmd_request *apr_req_data,
+				 struct apr_data *aprdata)
+{
+
+	aprdata->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				       APR_HDR_LEN(sizeof(struct apr_hdr)),
+				       APR_PKT_VER);
+	/* Bit manipulation is done on src_port so that a unique ID is sent.
+	 * This manipulation can be used in the future where the same service
+	 * is tried to open multiple times with the same src_port. At that
+	 * time 0x0001 can be replaced with other values depending on the
+	 * count.
+	 */
+	aprdata->hdr.src_port = ((apr_req_data->src_port) << 8 | 0x0001);
+	aprdata->hdr.dest_port = apr_req_data->dest_port;
+	aprdata->hdr.token = apr_req_data->token;
+	aprdata->hdr.opcode = apr_req_data->opcode;
+	aprdata->hdr.pkt_size  = APR_PKT_SIZE(APR_HDR_SIZE,
+					apr_req_data->payload_size);
+	memcpy(aprdata->payload, apr_req_data->payload,
+	       apr_req_data->payload_size);
+}
+
+static int voice_svc_send_req(struct voice_svc_cmd_request *apr_request,
+			      struct voice_svc_prvt *prtd)
+{
+	int ret = 0;
+	void *apr_handle = NULL;
+	struct apr_data *aprdata = NULL;
+	uint32_t user_payload_size;
+	uint32_t payload_size;
+
+	pr_debug("%s\n", __func__);
+
+	if (apr_request == NULL) {
+		pr_err("%s: apr_request is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	user_payload_size = apr_request->payload_size;
+	payload_size = sizeof(struct apr_data) + user_payload_size;
+
+	if (payload_size <= user_payload_size) {
+		pr_err("%s: invalid payload size ( 0x%x ).\n",
+			__func__, user_payload_size);
+		ret = -EINVAL;
+		goto done;
+	} else {
+		aprdata = kmalloc(payload_size, GFP_KERNEL);
+		if (aprdata == NULL) {
+			ret = -ENOMEM;
+			goto done;
+		}
+	}
+
+	voice_svc_update_hdr(apr_request, aprdata);
+
+	if (!strcmp(apr_request->svc_name, VOICE_SVC_CVS_STR)) {
+		apr_handle = prtd->apr_q6_cvs;
+	} else if (!strcmp(apr_request->svc_name, VOICE_SVC_MVM_STR)) {
+		apr_handle = prtd->apr_q6_mvm;
+	} else {
+		pr_err("%s: Invalid service %.*s\n", __func__,
+			MAX_APR_SERVICE_NAME_LEN, apr_request->svc_name);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = apr_send_pkt(apr_handle, (uint32_t *)aprdata);
+
+	if (ret < 0) {
+		pr_err("%s: Fail in sending request %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+	} else {
+		pr_debug("%s: apr packet sent successfully %d\n",
+			 __func__, ret);
+		ret = 0;
+	}
+
+done:
+	kfree(aprdata);
+	return ret;
+}
+static int voice_svc_reg(char *svc, uint32_t src_port,
+			 struct voice_svc_prvt *prtd, void **handle)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (handle == NULL) {
+		pr_err("%s: handle is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (*handle != NULL) {
+		pr_err("%s: svc handle not NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (src_port == (APR_MAX_PORTS - 1)) {
+		pr_err("%s: SRC port reserved for dummy session\n", __func__);
+		pr_err("%s: Unable to register %s\n", __func__, svc);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	*handle = apr_register("ADSP",
+			       svc, qdsp_apr_callback,
+			       ((src_port) << 8 | 0x0001),
+			       prtd);
+
+	if (*handle == NULL) {
+		pr_err("%s: Unable to register %s\n",
+		       __func__, svc);
+
+		ret = -EFAULT;
+		goto done;
+	}
+	pr_debug("%s: Register %s successful\n",
+		__func__, svc);
+done:
+	return ret;
+}
+
+static int voice_svc_dereg(char *svc, void **handle)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (handle == NULL) {
+		pr_err("%s: handle is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (*handle == NULL) {
+		pr_err("%s: svc handle is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = apr_deregister(*handle);
+	if (ret) {
+		pr_err("%s: Unable to deregister service %s; error: %d\n",
+		       __func__, svc, ret);
+
+		goto done;
+	}
+	*handle = NULL;
+	pr_debug("%s: deregister %s successful\n", __func__, svc);
+
+done:
+	return ret;
+}
+
+static int process_reg_cmd(struct voice_svc_register *apr_reg_svc,
+			   struct voice_svc_prvt *prtd)
+{
+	int ret = 0;
+	char *svc = NULL;
+	void **handle = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (!strcmp(apr_reg_svc->svc_name, VOICE_SVC_MVM_STR)) {
+		svc = VOICE_SVC_MVM_STR;
+		handle = &prtd->apr_q6_mvm;
+	} else if (!strcmp(apr_reg_svc->svc_name, VOICE_SVC_CVS_STR)) {
+		svc = VOICE_SVC_CVS_STR;
+		handle = &prtd->apr_q6_cvs;
+	} else {
+		pr_err("%s: Invalid Service: %.*s\n", __func__,
+			MAX_APR_SERVICE_NAME_LEN, apr_reg_svc->svc_name);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (apr_reg_svc->reg_flag) {
+		ret = voice_svc_reg(svc, apr_reg_svc->src_port, prtd,
+				    handle);
+	} else if (!apr_reg_svc->reg_flag) {
+		ret = voice_svc_dereg(svc, handle);
+	}
+
+done:
+	return ret;
+}
+
+static ssize_t voice_svc_write(struct file *file, const char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	int ret = 0;
+	struct voice_svc_prvt *prtd;
+	struct voice_svc_write_msg *data = NULL;
+	uint32_t cmd;
+
+	pr_debug("%s\n", __func__);
+
+	/*
+	 * Check if enough memory is allocated to parse the message type.
+	 * Will check there is enough to hold the payload later.
+	 */
+	if (count >= sizeof(struct voice_svc_write_msg)) {
+		data = kmalloc(count, GFP_KERNEL);
+	} else {
+		pr_debug("%s: invalid data size\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (data == NULL) {
+		pr_err("%s: data kmalloc failed.\n", __func__);
+
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ret = copy_from_user(data, buf, count);
+	if (ret) {
+		pr_err("%s: copy_from_user failed %d\n", __func__, ret);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	cmd = data->msg_type;
+	prtd = (struct voice_svc_prvt *) file->private_data;
+	if (prtd == NULL) {
+		pr_err("%s: prtd is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	switch (cmd) {
+	case MSG_REGISTER:
+		/*
+		 * Check that count reflects the expected size to ensure
+		 * sufficient memory was allocated. Since voice_svc_register
+		 * has a static size, this should be exact.
+		 */
+		if (count == (sizeof(struct voice_svc_write_msg) +
+			      sizeof(struct voice_svc_register))) {
+			ret = process_reg_cmd(
+			(struct voice_svc_register *)data->payload, prtd);
+			if (!ret)
+				ret = count;
+		} else {
+			pr_err("%s: invalid payload size\n", __func__);
+			ret = -EINVAL;
+			goto done;
+		}
+		break;
+	case MSG_REQUEST:
+		/*
+		 * Check that count reflects the expected size to ensure
+		 * sufficient memory was allocated. Since voice_svc_cmd_request
+		 * has a variable size, check the minimum value count must be.
+		 */
+		if (count >= (sizeof(struct voice_svc_write_msg) +
+			      sizeof(struct voice_svc_cmd_request))) {
+		ret = voice_svc_send_req(
+			(struct voice_svc_cmd_request *)data->payload, prtd);
+		if (!ret)
+			ret = count;
+	} else {
+		pr_err("%s: invalid payload size\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+		break;
+	default:
+		pr_debug("%s: Invalid command: %u\n", __func__, cmd);
+		ret = -EINVAL;
+	}
+
+done:
+	kfree(data);
+	return ret;
+}
+
+static ssize_t voice_svc_read(struct file *file, char __user *arg,
+			      size_t count, loff_t *ppos)
+{
+	int ret = 0;
+	struct voice_svc_prvt *prtd;
+	struct apr_response_list *resp;
+	unsigned long spin_flags;
+	int size;
+
+	pr_debug("%s\n", __func__);
+
+	prtd = (struct voice_svc_prvt *)file->private_data;
+	if (prtd == NULL) {
+		pr_err("%s: prtd is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+	if (list_empty(&prtd->response_queue)) {
+		spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+		pr_debug("%s: wait for a response\n", __func__);
+
+		ret = wait_event_interruptible_timeout(prtd->response_wait,
+					!list_empty(&prtd->response_queue),
+					msecs_to_jiffies(TIMEOUT_MS));
+		if (ret == 0) {
+			pr_debug("%s: Read timeout\n", __func__);
+
+			ret = -ETIMEDOUT;
+			goto done;
+		} else if (ret > 0 && !list_empty(&prtd->response_queue)) {
+			pr_debug("%s: Interrupt received for response\n",
+				 __func__);
+		} else if (ret < 0) {
+			pr_debug("%s: Interrupted by SIGNAL %d\n",
+				 __func__, ret);
+
+			goto done;
+		}
+
+		spin_lock_irqsave(&prtd->response_lock, spin_flags);
+	}
+
+	resp = list_first_entry(&prtd->response_queue,
+				struct apr_response_list, list);
+
+	spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+
+	size = resp->resp.payload_size +
+	       sizeof(struct voice_svc_cmd_response);
+
+	if (count < size) {
+		pr_err("%s: Invalid payload size %zd, %d\n",
+		       __func__, count, size);
+
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	if (!access_ok(VERIFY_WRITE, arg, size)) {
+		pr_err("%s: Access denied to write\n",
+		       __func__);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	ret = copy_to_user(arg, &resp->resp,
+			 sizeof(struct voice_svc_cmd_response) +
+			 resp->resp.payload_size);
+	if (ret) {
+		pr_err("%s: copy_to_user failed %d\n", __func__, ret);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+	list_del(&resp->list);
+	prtd->response_count--;
+	kfree(resp);
+
+	spin_unlock_irqrestore(&prtd->response_lock,
+				spin_flags);
+
+	ret = count;
+
+done:
+	return ret;
+}
+
+static int voice_svc_dummy_reg(void)
+{
+	uint32_t src_port = APR_MAX_PORTS - 1;
+
+	pr_debug("%s\n", __func__);
+	dummy_q6_mvm = apr_register("ADSP", "MVM",
+				qdsp_dummy_apr_callback,
+				src_port,
+				NULL);
+	if (dummy_q6_mvm == NULL) {
+		pr_err("%s: Unable to register dummy MVM\n", __func__);
+		goto err;
+	}
+
+	dummy_q6_cvs = apr_register("ADSP", "CVS",
+				qdsp_dummy_apr_callback,
+				src_port,
+				NULL);
+	if (dummy_q6_cvs == NULL) {
+		pr_err("%s: Unable to register dummy CVS\n", __func__);
+		goto err;
+	}
+	return 0;
+err:
+	if (dummy_q6_mvm != NULL) {
+		apr_deregister(dummy_q6_mvm);
+		dummy_q6_mvm = NULL;
+	}
+	return -EINVAL;
+}
+
+static int voice_svc_open(struct inode *inode, struct file *file)
+{
+	struct voice_svc_prvt *prtd = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	prtd = kmalloc(sizeof(struct voice_svc_prvt), GFP_KERNEL);
+
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	memset(prtd, 0, sizeof(struct voice_svc_prvt));
+	prtd->apr_q6_cvs = NULL;
+	prtd->apr_q6_mvm = NULL;
+	prtd->response_count = 0;
+	INIT_LIST_HEAD(&prtd->response_queue);
+	init_waitqueue_head(&prtd->response_wait);
+	spin_lock_init(&prtd->response_lock);
+	file->private_data = (void *)prtd;
+
+	/* Current APR implementation doesn't support session based
+	 * multiple service registrations. The apr_deregister()
+	 * function sets the destination and client IDs to zero, if
+	 * deregister is called for a single service instance.
+	 * To avoid this, register for additional services.
+	 */
+	if (!reg_dummy_sess) {
+		voice_svc_dummy_reg();
+		reg_dummy_sess = 1;
+	}
+	return 0;
+}
+
+static int voice_svc_release(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct apr_response_list *resp = NULL;
+	unsigned long spin_flags;
+	struct voice_svc_prvt *prtd = NULL;
+	char *svc_name = NULL;
+	void **handle = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	prtd = (struct voice_svc_prvt *)file->private_data;
+	if (prtd == NULL) {
+		pr_err("%s: prtd is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (prtd->apr_q6_cvs != NULL) {
+		svc_name = VOICE_SVC_MVM_STR;
+		handle = &prtd->apr_q6_cvs;
+		ret = voice_svc_dereg(svc_name, handle);
+		if (ret)
+			pr_err("%s: Failed to dereg CVS %d\n", __func__, ret);
+	}
+
+	if (prtd->apr_q6_mvm != NULL) {
+		svc_name = VOICE_SVC_MVM_STR;
+		handle = &prtd->apr_q6_mvm;
+		ret = voice_svc_dereg(svc_name, handle);
+		if (ret)
+			pr_err("%s: Failed to dereg MVM %d\n", __func__, ret);
+	}
+
+	spin_lock_irqsave(&prtd->response_lock, spin_flags);
+
+	while (!list_empty(&prtd->response_queue)) {
+		pr_debug("%s: Remove item from response queue\n", __func__);
+
+		resp = list_first_entry(&prtd->response_queue,
+					struct apr_response_list, list);
+		list_del(&resp->list);
+		prtd->response_count--;
+		kfree(resp);
+	}
+
+	spin_unlock_irqrestore(&prtd->response_lock, spin_flags);
+
+	kfree(file->private_data);
+	file->private_data = NULL;
+
+done:
+	return ret;
+}
+
+static const struct file_operations voice_svc_fops = {
+	.owner =                THIS_MODULE,
+	.open =                 voice_svc_open,
+	.read =                 voice_svc_read,
+	.write =                voice_svc_write,
+	.release =              voice_svc_release,
+};
+
+
+static int voice_svc_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	voice_svc_dev = devm_kzalloc(&pdev->dev,
+				  sizeof(struct voice_svc_device), GFP_KERNEL);
+	if (!voice_svc_dev) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ret = alloc_chrdev_region(&device_num, 0, MINOR_NUMBER,
+				  VOICE_SVC_DRIVER_NAME);
+	if (ret) {
+		pr_err("%s: Failed to alloc chrdev\n", __func__);
+		ret = -ENODEV;
+		goto chrdev_err;
+	}
+
+	voice_svc_dev->major = MAJOR(device_num);
+	voice_svc_class = class_create(THIS_MODULE, VOICE_SVC_DRIVER_NAME);
+	if (IS_ERR(voice_svc_class)) {
+		ret = PTR_ERR(voice_svc_class);
+		pr_err("%s: Failed to create class; err = %d\n", __func__,
+			ret);
+		goto class_err;
+	}
+
+	voice_svc_dev->dev = device_create(voice_svc_class, NULL, device_num,
+					   NULL, VOICE_SVC_DRIVER_NAME);
+	if (IS_ERR(voice_svc_dev->dev)) {
+		ret = PTR_ERR(voice_svc_dev->dev);
+		pr_err("%s: Failed to create device; err = %d\n", __func__,
+			ret);
+		goto dev_err;
+	}
+
+	voice_svc_dev->cdev = cdev_alloc();
+	if (!voice_svc_dev->cdev) {
+		pr_err("%s: Failed to alloc cdev\n", __func__);
+		ret = -ENOMEM;
+		goto cdev_alloc_err;
+	}
+
+	cdev_init(voice_svc_dev->cdev, &voice_svc_fops);
+	ret = cdev_add(voice_svc_dev->cdev, device_num, MINOR_NUMBER);
+	if (ret) {
+		pr_err("%s: Failed to register chrdev; err = %d\n", __func__,
+			ret);
+		goto add_err;
+	}
+	pr_debug("%s: Device created\n", __func__);
+	goto done;
+
+add_err:
+	cdev_del(voice_svc_dev->cdev);
+cdev_alloc_err:
+	device_destroy(voice_svc_class, device_num);
+dev_err:
+	class_destroy(voice_svc_class);
+class_err:
+	unregister_chrdev_region(0, MINOR_NUMBER);
+chrdev_err:
+	kfree(voice_svc_dev);
+done:
+	return ret;
+}
+
+static int voice_svc_remove(struct platform_device *pdev)
+{
+	pr_debug("%s\n", __func__);
+
+	cdev_del(voice_svc_dev->cdev);
+	kfree(voice_svc_dev->cdev);
+	device_destroy(voice_svc_class, device_num);
+	class_destroy(voice_svc_class);
+	unregister_chrdev_region(0, MINOR_NUMBER);
+	kfree(voice_svc_dev);
+
+	return 0;
+}
+
+static const struct of_device_id voice_svc_of_match[] = {
+	{.compatible = "qcom,msm-voice-svc"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, voice_svc_of_match);
+
+static struct platform_driver voice_svc_driver = {
+	.probe          = voice_svc_probe,
+	.remove         = voice_svc_remove,
+	.driver         = {
+		.name   = "msm-voice-svc",
+		.owner  = THIS_MODULE,
+		.of_match_table = voice_svc_of_match,
+	},
+};
+
+static int __init voice_svc_init(void)
+{
+	pr_debug("%s\n", __func__);
+
+	return platform_driver_register(&voice_svc_driver);
+}
+
+static void __exit voice_svc_exit(void)
+{
+	pr_debug("%s\n", __func__);
+
+	platform_driver_unregister(&voice_svc_driver);
+}
+
+module_init(voice_svc_init);
+module_exit(voice_svc_exit);
+
+MODULE_DESCRIPTION("Soc QDSP6v2 Voice Service driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/scm.c b/drivers/soc/qcom/scm.c
index 714c848..b4713ac 100644
--- a/drivers/soc/qcom/scm.c
+++ b/drivers/soc/qcom/scm.c
@@ -56,9 +56,16 @@ DEFINE_MUTEX(scm_lmh_lock);
 #define SMC_ATOMIC_MASK 0x80000000
 #define IS_CALL_AVAIL_CMD 1
 
-#define SCM_BUF_LEN(__cmd_size, __resp_size)	\
-	(sizeof(struct scm_command) + sizeof(struct scm_response) + \
-		__cmd_size + __resp_size)
+#define SCM_BUF_LEN(__cmd_size, __resp_size) ({ \
+	size_t x =  __cmd_size + __resp_size; \
+	size_t y = sizeof(struct scm_command) + sizeof(struct scm_response); \
+	size_t result; \
+	if (x < __cmd_size || (x + y) < x) \
+		result = 0; \
+	else \
+		result = x + y; \
+	result; \
+	})
 /**
  * struct scm_command - one SCM command buffer
  * @len: total available memory for command and response
@@ -356,8 +363,7 @@ int scm_call_noalloc(u32 svc_id, u32 cmd_id, const void *cmd_buf,
 	int ret;
 	size_t len = SCM_BUF_LEN(cmd_len, resp_len);
 
-	if (cmd_len > scm_buf_len || resp_len > scm_buf_len ||
-	    len > scm_buf_len)
+	if (len == 0)
 		return -EINVAL;
 
 	if (!IS_ALIGNED((unsigned long)scm_buf, PAGE_SIZE))
@@ -780,7 +786,7 @@ int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
 	int ret;
 	size_t len = SCM_BUF_LEN(cmd_len, resp_len);
 
-	if (cmd_len > len || resp_len > len)
+	if (len == 0 || PAGE_ALIGN(len) < len)
 		return -EINVAL;
 
 	cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
diff --git a/drivers/soc/qcom/service-notifier.c b/drivers/soc/qcom/service-notifier.c
index dae3c42..2136771 100644
--- a/drivers/soc/qcom/service-notifier.c
+++ b/drivers/soc/qcom/service-notifier.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -215,7 +215,7 @@ static void send_ind_ack(struct work_struct *work)
 	if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01)
 		pr_err("QMI request failed 0x%x\n",
 			QMI_RESP_BIT_SHIFT(resp.resp.error));
-	pr_debug("Indication ACKed for transid %d, service %s, instance %d!\n",
+	pr_info("Indication ACKed for transid %d, service %s, instance %d!\n",
 		data->ind_msg.transaction_id, data->ind_msg.service_path,
 		data->instance_id);
 }
@@ -240,7 +240,7 @@ static void root_service_service_ind_cb(struct qmi_handle *handle,
 		return;
 	}
 
-	pr_debug("Indication received from %s, state: 0x%x, trans-id: %d\n",
+	pr_info("Indication received from %s, state: 0x%x, trans-id: %d\n",
 		ind_msg.service_name, ind_msg.curr_state,
 		ind_msg.transaction_id);
 
@@ -336,11 +336,13 @@ static void root_service_service_arrive(struct work_struct *work)
 	int rc;
 	int curr_state;
 
+	mutex_lock(&qmi_client_release_lock);
 	/* Create a Local client port for QMI communication */
 	data->clnt_handle = qmi_handle_create(root_service_clnt_notify, work);
 	if (!data->clnt_handle) {
 		pr_err("QMI client handle alloc failed (instance-id: %d)\n",
 							data->instance_id);
+		mutex_unlock(&qmi_client_release_lock);
 		return;
 	}
 
@@ -353,9 +355,11 @@ static void root_service_service_arrive(struct work_struct *work)
 							data->instance_id, rc);
 		qmi_handle_destroy(data->clnt_handle);
 		data->clnt_handle = NULL;
+		mutex_unlock(&qmi_client_release_lock);
 		return;
 	}
 	data->service_connected = true;
+	mutex_unlock(&qmi_client_release_lock);
 	pr_info("Connection established between QMI handle and %d service\n",
 							data->instance_id);
 	/* Register for indication messages about service */
diff --git a/drivers/soc/qcom/spcom.c b/drivers/soc/qcom/spcom.c
new file mode 100644
index 0000000..f381f16
--- /dev/null
+++ b/drivers/soc/qcom/spcom.c
@@ -0,0 +1,2560 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * Secure-Processor-Communication (SPCOM).
+ *
+ * This driver provides communication to Secure Processor (SP)
+ * over G-Link transport layer.
+ *
+ * It provides interface to both User Space spcomlib and kernel drivers.
+ *
+ * User Space App shall use spcomlib for communication with SP.
+ * User Space App can be either Client or Server.
+ * spcomlib shall use write() file operation to send data,
+ * and read() file operation to read data.
+ *
+ * This driver uses glink as the transport layer.
+ * This driver exposes "/dev/<sp-channel-name>" file node for each glink
+ * logical channel.
+ * This driver exposes "/dev/spcom" file node for some debug/control command.
+ * The predefined channel "/dev/sp_kernel" is used for loading SP Application
+ * from HLOS.
+ * This driver exposes "/dev/sp_ssr" file node to allow user space poll for SSR.
+ * After the remote SP App is loaded, this driver exposes a new file node
+ * "/dev/<ch-name>" for the matching HLOS App to use.
+ * The access to predefined file node is restricted by using unix group
+ * and SELinux.
+ *
+ * No message routing is used, but using the G-Link "multiplexing" feature
+ * to use a dedicated logical channel for HLOS and SP Application-Pair.
+ *
+ * Each HLOS/SP Application can be either Client or Server or both,
+ * Messaging is allays point-to-point between 2 HLOS<=>SP applications.
+ *
+ * User Space Request & Response are synchronous.
+ * read() & write() operations are blocking until completed or terminated.
+ *
+ * This driver registers to G-Link callbacks to be aware on channel state.
+ * A notify callback is called upon channel connect/disconnect.
+ *
+ */
+
+/* Uncomment the line below to test spcom against modem rather than SP */
+/* #define SPCOM_TEST_HLOS_WITH_MODEM 1 */
+
+/* Uncomment the line below to enable debug messages */
+/* #define DEBUG 1 */
+
+#define pr_fmt(fmt)	"spcom [%s]: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/errno.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/poll.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/msm_ion.h>
+
+#include <soc/qcom/glink.h>
+#include <soc/qcom/smem.h>
+#include <soc/qcom/spcom.h>
+
+#include <uapi/linux/spcom.h>
+
+#include "glink_private.h"
+
+/* "SPCM" string */
+#define SPCOM_MAGIC_ID	((uint32_t)(0x5350434D))
+
+/* Request/Response */
+#define SPCOM_FLAG_REQ		BIT(0)
+#define SPCOM_FLAG_RESP	BIT(1)
+#define SPCOM_FLAG_ENCODED	BIT(2)
+#define SPCOM_FLAG_NON_ENCODED	BIT(3)
+
+/* SPCOM driver name */
+#define DEVICE_NAME	"spcom"
+
+#define SPCOM_MAX_CHANNELS	0x20
+
+/* maximum ION buffers should be >= SPCOM_MAX_CHANNELS  */
+#define SPCOM_MAX_ION_BUF_PER_CH (SPCOM_MAX_CHANNELS + 4)
+
+/* maximum ION buffer per send request/response command */
+#define SPCOM_MAX_ION_BUF_PER_CMD SPCOM_MAX_ION_BUF
+
+/* Maximum command size */
+#define SPCOM_MAX_COMMAND_SIZE	(PAGE_SIZE)
+
+/* Maximum input size */
+#define SPCOM_MAX_READ_SIZE	(PAGE_SIZE)
+
+/* Current Process ID */
+#define current_pid() ((u32)(current->pid))
+
+/* Maximum channel name size (including null) - matching GLINK_NAME_SIZE */
+#define MAX_CH_NAME_LEN	32
+
+/* Connection negotiation timeout, if remote channel is open */
+#define OPEN_CHANNEL_TIMEOUT_MSEC	100
+
+/*
+ * After both sides get CONNECTED,
+ * there is a race between one side queueing rx buffer and the other side
+ * trying to call glink_tx() , this race is only on the 1st tx.
+ * Do tx retry with some delay to allow the other side to queue rx buffer.
+ */
+#define TX_RETRY_DELAY_MSEC	100
+
+/* number of tx retries */
+#define TX_MAX_RETRY	3
+
+/* SPCOM_MAX_REQUEST_SIZE-or-SPCOM_MAX_RESPONSE_SIZE + header */
+#define SPCOM_RX_BUF_SIZE	300
+
+/* The SPSS RAM size is 256 KB so SP App must fit into it */
+#define SPCOM_MAX_APP_SIZE	SZ_256K
+
+/*
+ * ACK timeout from remote side for TX data.
+ * Normally, it takes few msec for SPSS to respond with ACK for TX data.
+ * However, due to SPSS HW issue, the SPSS might disable interrupts
+ * for a very long time.
+ */
+#define TX_DONE_TIMEOUT_MSEC	5000
+
+/*
+ * Initial transaction id, use non-zero nonce for debug.
+ * Incremented by client on request, and copied back by server on response.
+ */
+#define INITIAL_TXN_ID	0x12345678
+
+/**
+ * struct spcom_msg_hdr - Request/Response message header between HLOS and SP.
+ *
+ * This header is proceeding any request specific parameters.
+ * The transaction id is used to match request with response.
+ * Note: glink API provides the rx/tx data size, so user payload size is
+ * calculated by reducing the header size.
+ */
+struct spcom_msg_hdr {
+	uint32_t reserved;	/* for future use */
+	uint32_t txn_id;	/* transaction id */
+	char buf[0];		/* Variable buffer size, must be last field */
+} __packed;
+
+/**
+ * struct spcom_client - Client handle
+ */
+struct spcom_client {
+	struct spcom_channel *ch;
+};
+
+/**
+ * struct spcom_server - Server handle
+ */
+struct spcom_server {
+	struct spcom_channel *ch;
+};
+
+/**
+ * struct spcom_channel - channel context
+ */
+struct spcom_channel {
+	char name[MAX_CH_NAME_LEN];
+	struct mutex lock;
+	void *glink_handle;
+	uint32_t txn_id;	/* incrementing nonce per channel */
+	bool is_server;		/* for txn_id and response_timeout_msec */
+	uint32_t response_timeout_msec; /* for client only */
+
+	/* char dev */
+	struct cdev *cdev;
+	struct device *dev;
+	struct device_attribute attr;
+
+	/*
+	 * glink state: CONNECTED / LOCAL_DISCONNECTED, REMOTE_DISCONNECTED
+	 */
+	unsigned int glink_state;
+
+	/* Events notification */
+	struct completion connect;
+	struct completion disconnect;
+	struct completion tx_done;
+	struct completion rx_done;
+
+	/*
+	 * Only one client or server per channel.
+	 * Only one rx/tx transaction at a time (request + response).
+	 */
+	int ref_count;
+	u32 pid;
+
+	/* link UP/DOWN callback */
+	void (*notify_link_state_cb)(bool up);
+
+	/* abort flags */
+	bool rx_abort;
+	bool tx_abort;
+
+	/* rx data info */
+	int rx_buf_size;	/* allocated rx buffer size */
+	bool rx_buf_ready;
+	int actual_rx_size;	/* actual data size received */
+	const void *glink_rx_buf;
+
+	/* ION lock/unlock support */
+	int ion_fd_table[SPCOM_MAX_ION_BUF_PER_CH];
+	struct ion_handle *ion_handle_table[SPCOM_MAX_ION_BUF_PER_CH];
+};
+
+/**
+ * struct spcom_device - device state structure.
+ */
+struct spcom_device {
+	char predefined_ch_name[SPCOM_MAX_CHANNELS][MAX_CH_NAME_LEN];
+
+	/* char device info */
+	struct cdev cdev;
+	dev_t device_no;
+	struct class *driver_class;
+	struct device *class_dev;
+
+	/* G-Link channels */
+	struct spcom_channel channels[SPCOM_MAX_CHANNELS];
+	int channel_count;
+
+	/* private */
+	struct mutex lock;
+
+	/* Link state */
+	struct completion link_state_changed;
+	enum glink_link_state link_state;
+
+	/* ION support */
+	struct ion_client *ion_client;
+};
+
+#ifdef SPCOM_TEST_HLOS_WITH_MODEM
+	static const char *spcom_edge = "mpss";
+	static const char *spcom_transport = "smem";
+#else
+	static const char *spcom_edge = "spss";
+	static const char *spcom_transport = "mailbox";
+#endif
+
+/* Device Driver State */
+static struct spcom_device *spcom_dev;
+
+/* static functions declaration */
+static int spcom_create_channel_chardev(const char *name);
+static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec);
+static int spcom_close(struct spcom_channel *ch);
+static void spcom_notify_rx_abort(void *handle, const void *priv,
+				  const void *pkt_priv);
+static struct spcom_channel *spcom_find_channel_by_name(const char *name);
+static int spcom_unlock_ion_buf(struct spcom_channel *ch, int fd);
+
+/**
+ * spcom_is_ready() - driver is initialized and ready.
+ */
+static inline bool spcom_is_ready(void)
+{
+	return spcom_dev != NULL;
+}
+
+/**
+ * spcom_is_channel_open() - channel is open on this side.
+ *
+ * Channel might not be fully connected if remote side didn't open the channel
+ * yet.
+ */
+static inline bool spcom_is_channel_open(struct spcom_channel *ch)
+{
+	return ch->glink_handle != NULL;
+}
+
+/**
+ * spcom_is_channel_connected() - channel is fully connected by both sides.
+ */
+static inline bool spcom_is_channel_connected(struct spcom_channel *ch)
+{
+	return (ch->glink_state == GLINK_CONNECTED);
+}
+
+/**
+ * spcom_create_predefined_channels_chardev() - expose predefined channels to
+ * user space.
+ *
+ * Predefined channels list is provided by device tree.
+ * Typically, it is for known servers on remote side that are not loaded by the
+ * HLOS.
+ */
+static int spcom_create_predefined_channels_chardev(void)
+{
+	int i;
+	int ret;
+
+	for (i = 0; i < SPCOM_MAX_CHANNELS; i++) {
+		const char *name = spcom_dev->predefined_ch_name[i];
+
+		if (name[0] == 0)
+			break;
+		ret = spcom_create_channel_chardev(name);
+		if (ret) {
+			pr_err("failed to create chardev [%s], ret [%d].\n",
+			       name, ret);
+			return -EFAULT;
+		}
+	}
+
+	return 0;
+}
+
+/*======================================================================*/
+/*		GLINK CALLBACKS						*/
+/*======================================================================*/
+
+/**
+ * spcom_link_state_notif_cb() - glink callback for link state change.
+ *
+ * glink notifies link layer is up, before any channel opened on remote side.
+ * Calling glink_open() locally allowed only after link is up.
+ * Notify link down, normally upon Remote Subsystem Reset (SSR).
+ * Note: upon SSR, glink will also notify each channel about remote disconnect,
+ * and abort any pending rx buffer.
+ */
+static void spcom_link_state_notif_cb(struct glink_link_state_cb_info *cb_info,
+				      void *priv)
+{
+	struct spcom_channel *ch = NULL;
+	const char *ch_name = "sp_kernel";
+
+	spcom_dev->link_state = cb_info->link_state;
+
+	pr_debug("spcom_link_state_notif_cb called. transport = %s edge = %s\n",
+		 cb_info->transport, cb_info->edge);
+
+	switch (cb_info->link_state) {
+	case GLINK_LINK_STATE_UP:
+		pr_info("GLINK_LINK_STATE_UP.\n");
+		spcom_create_predefined_channels_chardev();
+		break;
+	case GLINK_LINK_STATE_DOWN:
+		pr_err("GLINK_LINK_STATE_DOWN.\n");
+
+		/*
+		 * Free all the SKP ION buffers that were locked
+		 * for SPSS app swapping, when remote subsystem reset.
+		 */
+		pr_debug("Free all SKP ION buffers on SSR.\n");
+		ch = spcom_find_channel_by_name(ch_name);
+		if (!ch)
+			pr_err("failed to find channel [%s].\n", ch_name);
+		else
+			spcom_unlock_ion_buf(ch, SPCOM_ION_FD_UNLOCK_ALL);
+		break;
+	default:
+		pr_err("unknown link_state [%d].\n", cb_info->link_state);
+		break;
+	}
+	complete_all(&spcom_dev->link_state_changed);
+}
+
+/**
+ * spcom_notify_rx() - glink callback on receiving data.
+ *
+ * Glink notify rx data is ready. The glink internal rx buffer was
+ * allocated upon glink_queue_rx_intent().
+ */
+static void spcom_notify_rx(void *handle,
+			    const void *priv, const void *pkt_priv,
+			    const void *buf, size_t size)
+{
+	struct spcom_channel *ch = (struct spcom_channel *) priv;
+
+	if (!ch) {
+		pr_err("invalid ch parameter.\n");
+		return;
+	}
+
+	pr_debug("ch [%s] rx size [%d].\n", ch->name, (int) size);
+
+	ch->actual_rx_size = (int) size;
+	ch->glink_rx_buf = (void *) buf;
+
+	complete_all(&ch->rx_done);
+}
+
+/**
+ * spcom_notify_tx_done() - glink callback on ACK sent data.
+ *
+ * after calling glink_tx() the remote side ACK receiving the data.
+ */
+static void spcom_notify_tx_done(void *handle,
+				 const void *priv, const void *pkt_priv,
+				 const void *buf)
+{
+	struct spcom_channel *ch = (struct spcom_channel *) priv;
+	int *tx_buf = (int *) buf;
+
+	if (!ch) {
+		pr_err("invalid ch parameter.\n");
+		return;
+	}
+
+	pr_debug("ch [%s] buf[0] = [0x%x].\n", ch->name, tx_buf[0]);
+
+	complete_all(&ch->tx_done);
+}
+
+/**
+ * spcom_notify_state() - glink callback on channel connect/disconnect.
+ *
+ * Channel is fully CONNECTED after both sides opened the channel.
+ * Channel is LOCAL_DISCONNECTED after both sides closed the channel.
+ * If the remote side closed the channel, it is expected that the local side
+ * will also close the channel.
+ * Upon connection, rx buffer is allocated to receive data,
+ * the maximum transfer size is agreed by both sides.
+ */
+static void spcom_notify_state(void *handle, const void *priv,
+				unsigned int event)
+{
+	int ret;
+	struct spcom_channel *ch = (struct spcom_channel *) priv;
+
+	switch (event) {
+	case GLINK_CONNECTED:
+		pr_debug("GLINK_CONNECTED, ch name [%s].\n", ch->name);
+		complete_all(&ch->connect);
+
+		/*
+		 * if spcom_notify_state() is called within glink_open()
+		 * then ch->glink_handle is not updated yet.
+		 */
+		if (!ch->glink_handle) {
+			pr_debug("update glink_handle, ch [%s].\n", ch->name);
+			ch->glink_handle = handle;
+		}
+
+		/* prepare default rx buffer after connected */
+		ret = glink_queue_rx_intent(ch->glink_handle,
+					    ch, ch->rx_buf_size);
+		if (ret) {
+			pr_err("glink_queue_rx_intent() err [%d]\n", ret);
+		} else {
+			pr_debug("rx buf is ready, size [%d].\n",
+				 ch->rx_buf_size);
+			ch->rx_buf_ready = true;
+		}
+		break;
+	case GLINK_LOCAL_DISCONNECTED:
+		/*
+		 * Channel state is GLINK_LOCAL_DISCONNECTED
+		 * only after *both* sides closed the channel.
+		 */
+		pr_debug("GLINK_LOCAL_DISCONNECTED, ch [%s].\n", ch->name);
+		complete_all(&ch->disconnect);
+		break;
+	case GLINK_REMOTE_DISCONNECTED:
+		/*
+		 * Remote side initiates glink_close().
+		 * This is not expected on normal operation.
+		 * This may happen upon remote SSR.
+		 */
+		pr_err("GLINK_REMOTE_DISCONNECTED, ch [%s].\n", ch->name);
+
+		/*
+		 * Abort any blocking read() operation.
+		 * The glink notification might be after REMOTE_DISCONNECT.
+		 */
+		spcom_notify_rx_abort(NULL, ch, NULL);
+
+		/*
+		 * after glink_close(),
+		 * expecting notify GLINK_LOCAL_DISCONNECTED
+		 */
+		spcom_close(ch);
+		break;
+	default:
+		pr_err("unknown event id = %d, ch name [%s].\n",
+		       (int) event, ch->name);
+		return;
+	}
+
+	ch->glink_state = event;
+}
+
+/**
+ * spcom_notify_rx_intent_req() - glink callback on intent request.
+ *
+ * glink allows the remote side to request for a local rx buffer if such
+ * buffer is not ready.
+ * However, for spcom simplicity on SP, and to reduce latency, we decided
+ * that glink_tx() on both side is not using INTENT_REQ flag, so this
+ * callback should not be called.
+ * Anyhow, return "false" to reject the request.
+ */
+static bool spcom_notify_rx_intent_req(void *handle, const void *priv,
+				       size_t req_size)
+{
+	struct spcom_channel *ch = (struct spcom_channel *) priv;
+
+	pr_err("Unexpected intent request for ch [%s].\n", ch->name);
+
+	return false;
+}
+
+/**
+ * spcom_notify_rx_abort() - glink callback on aborting rx pending buffer.
+ *
+ * Rx abort may happen if channel is closed by remote side, while rx buffer is
+ * pending in the queue.
+ */
+static void spcom_notify_rx_abort(void *handle, const void *priv,
+				  const void *pkt_priv)
+{
+	struct spcom_channel *ch = (struct spcom_channel *) priv;
+
+	pr_debug("ch [%s] pending rx aborted.\n", ch->name);
+
+	if (spcom_is_channel_connected(ch) && (!ch->rx_abort)) {
+		ch->rx_abort = true;
+		complete_all(&ch->rx_done);
+	}
+}
+
+/**
+ * spcom_notify_tx_abort() - glink callback on aborting tx data.
+ *
+ * This is probably not relevant, since glink_txv() is not used.
+ * Tx abort may happen if channel is closed by remote side,
+ * while multiple tx buffers are in a middle of tx operation.
+ */
+static void spcom_notify_tx_abort(void *handle, const void *priv,
+				  const void *pkt_priv)
+{
+	struct spcom_channel *ch = (struct spcom_channel *) priv;
+
+	pr_debug("ch [%s] pending tx aborted.\n", ch->name);
+
+	if (spcom_is_channel_connected(ch) && (!ch->tx_abort)) {
+		ch->tx_abort = true;
+		complete_all(&ch->tx_done);
+	}
+}
+
+/*======================================================================*/
+/*		UTILITIES						*/
+/*======================================================================*/
+
+/**
+ * spcom_init_open_config() - Fill glink_open() configuration parameters.
+ *
+ * @cfg: glink configuration struct pointer
+ * @name: channel name
+ * @priv: private caller data, provided back by callbacks, channel state.
+ *
+ * specify callbacks and other parameters for glink open channel.
+ */
+static void spcom_init_open_config(struct glink_open_config *cfg,
+				   const char *name, void *priv)
+{
+	cfg->notify_rx		= spcom_notify_rx;
+	cfg->notify_rxv		= NULL;
+	cfg->notify_tx_done	= spcom_notify_tx_done;
+	cfg->notify_state	= spcom_notify_state;
+	cfg->notify_rx_intent_req = spcom_notify_rx_intent_req;
+	cfg->notify_rx_sigs	= NULL;
+	cfg->notify_rx_abort	= spcom_notify_rx_abort;
+	cfg->notify_tx_abort	= spcom_notify_tx_abort;
+
+	cfg->options	= 0; /* not using GLINK_OPT_INITIAL_XPORT */
+	cfg->priv	= priv; /* provided back by callbacks */
+
+	cfg->name	= name;
+
+	cfg->transport	= spcom_transport;
+	cfg->edge	= spcom_edge;
+}
+
+/**
+ * spcom_init_channel() - initialize channel state.
+ *
+ * @ch: channel state struct pointer
+ * @name: channel name
+ */
+static int spcom_init_channel(struct spcom_channel *ch, const char *name)
+{
+	if (!ch || !name || !name[0]) {
+		pr_err("invalid parameters.\n");
+		return -EINVAL;
+	}
+
+	strlcpy(ch->name, name, sizeof(ch->name));
+
+	init_completion(&ch->connect);
+	init_completion(&ch->disconnect);
+	init_completion(&ch->tx_done);
+	init_completion(&ch->rx_done);
+
+	mutex_init(&ch->lock);
+	ch->glink_state = GLINK_LOCAL_DISCONNECTED;
+	ch->actual_rx_size = 0;
+	ch->rx_buf_size = SPCOM_RX_BUF_SIZE;
+
+	return 0;
+}
+
+/**
+ * spcom_find_channel_by_name() - find a channel by name.
+ *
+ * @name: channel name
+ *
+ * Return: a channel state struct.
+ */
+static struct spcom_channel *spcom_find_channel_by_name(const char *name)
+{
+	int i;
+
+	for (i = 0 ; i < ARRAY_SIZE(spcom_dev->channels); i++) {
+		struct spcom_channel *ch = &spcom_dev->channels[i];
+
+		if (strcmp(ch->name, name) == 0)
+			return ch;
+	}
+
+	return NULL;
+}
+
+/**
+ * spcom_open() - Open glink channel and wait for connection ACK.
+ *
+ * @ch: channel state struct pointer
+ *
+ * Normally, a local client opens a channel after remote server has opened
+ * the channel.
+ * A local server may open the channel before remote client is running.
+ */
+static int spcom_open(struct spcom_channel *ch, unsigned int timeout_msec)
+{
+	struct glink_open_config cfg = {0};
+	unsigned long jiffies = msecs_to_jiffies(timeout_msec);
+	long timeleft;
+	const char *name;
+	void *handle;
+
+	mutex_lock(&ch->lock);
+	name = ch->name;
+
+	/* only one client/server may use the channel */
+	if (ch->ref_count) {
+		pr_err("channel [%s] already in use.\n", name);
+		goto exit_err;
+	}
+	ch->ref_count++;
+	ch->pid = current_pid();
+	ch->txn_id = INITIAL_TXN_ID;
+
+	pr_debug("ch [%s] opened by PID [%d], count [%d]\n",
+		 name, ch->pid, ch->ref_count);
+
+	pr_debug("Open channel [%s] timeout_msec [%d].\n", name, timeout_msec);
+
+	if (spcom_is_channel_open(ch)) {
+		pr_debug("channel [%s] already open.\n", name);
+		mutex_unlock(&ch->lock);
+		return 0;
+	}
+
+	spcom_init_open_config(&cfg, name, ch);
+
+	/* init completion before calling glink_open() */
+	reinit_completion(&ch->connect);
+
+	handle = glink_open(&cfg);
+	if (IS_ERR_OR_NULL(handle)) {
+		pr_err("glink_open failed.\n");
+		goto exit_err;
+	} else {
+		pr_debug("glink_open [%s] ok.\n", name);
+	}
+	ch->glink_handle = handle;
+
+	pr_debug("Wait for connection on channel [%s] timeout_msec [%d].\n",
+		 name, timeout_msec);
+
+	/* Wait for remote side to connect */
+	if (timeout_msec) {
+		timeleft = wait_for_completion_timeout(&(ch->connect), jiffies);
+		if (timeleft == 0)
+			pr_debug("Channel [%s] is NOT connected.\n", name);
+		else
+			pr_debug("Channel [%s] fully connect.\n", name);
+	} else {
+		pr_debug("wait for connection ch [%s] no timeout.\n", name);
+		wait_for_completion(&(ch->connect));
+		pr_debug("Channel [%s] opened, no timeout.\n", name);
+	}
+
+	mutex_unlock(&ch->lock);
+
+	return 0;
+exit_err:
+	mutex_unlock(&ch->lock);
+
+	return -EFAULT;
+}
+
+/**
+ * spcom_close() - Close glink channel.
+ *
+ * @ch: channel state struct pointer
+ *
+ * A calling API functions should wait for disconnecting by both sides.
+ */
+static int spcom_close(struct spcom_channel *ch)
+{
+	int ret = 0;
+
+	mutex_lock(&ch->lock);
+
+	if (!spcom_is_channel_open(ch)) {
+		pr_err("ch already closed.\n");
+		mutex_unlock(&ch->lock);
+		return 0;
+	}
+
+	ret = glink_close(ch->glink_handle);
+	if (ret)
+		pr_err("glink_close() fail, ret [%d].\n", ret);
+	else
+		pr_debug("glink_close() ok.\n");
+
+	ch->glink_handle = NULL;
+	ch->ref_count = 0;
+	ch->rx_abort = false;
+	ch->tx_abort = false;
+	ch->glink_state = GLINK_LOCAL_DISCONNECTED;
+	ch->txn_id = INITIAL_TXN_ID; /* use non-zero nonce for debug */
+	ch->pid = 0;
+
+	pr_debug("Channel closed [%s].\n", ch->name);
+	mutex_unlock(&ch->lock);
+
+	return 0;
+}
+
+/**
+ * spcom_tx() - Send data and wait for ACK or timeout.
+ *
+ * @ch: channel state struct pointer
+ * @buf: buffer pointer
+ * @size: buffer size
+ *
+ * ACK is expected within a very short time (few msec).
+ */
+static int spcom_tx(struct spcom_channel *ch,
+		    void *buf,
+		    uint32_t size,
+		    uint32_t timeout_msec)
+{
+	int ret;
+	void *pkt_priv = NULL;
+	uint32_t tx_flags = 0 ; /* don't use GLINK_TX_REQ_INTENT */
+	unsigned long jiffies = msecs_to_jiffies(timeout_msec);
+	long timeleft;
+	int retry = 0;
+
+	mutex_lock(&ch->lock);
+
+	/* reset completion before calling glink */
+	reinit_completion(&ch->tx_done);
+
+	for (retry = 0; retry < TX_MAX_RETRY ; retry++) {
+		ret = glink_tx(ch->glink_handle, pkt_priv, buf, size, tx_flags);
+		if (ret == -EAGAIN) {
+			pr_err("glink_tx() fail, try again.\n");
+			/*
+			 * Delay to allow remote side to queue rx buffer.
+			 * This may happen after the first channel connection.
+			 */
+			msleep(TX_RETRY_DELAY_MSEC);
+		} else if (ret < 0) {
+			pr_err("glink_tx() error %d.\n", ret);
+			goto exit_err;
+		} else {
+			break; /* no retry needed */
+		}
+	}
+
+	pr_debug("Wait for Tx done.\n");
+
+	/* Wait for Tx Completion */
+	timeleft = wait_for_completion_timeout(&ch->tx_done, jiffies);
+	if (timeleft == 0) {
+		pr_err("tx_done timeout %d msec expired.\n", timeout_msec);
+		goto exit_err;
+	} else if (ch->tx_abort) {
+		pr_err("tx aborted.\n");
+		goto exit_err;
+	}
+
+	mutex_unlock(&ch->lock);
+
+	return ret;
+exit_err:
+	mutex_unlock(&ch->lock);
+	return -EFAULT;
+}
+
+/**
+ * spcom_rx() - Wait for received data until timeout, unless pending rx data is
+ * already ready
+ *
+ * @ch: channel state struct pointer
+ * @buf: buffer pointer
+ * @size: buffer size
+ *
+ * ACK is expected within a very short time (few msec).
+ */
+static int spcom_rx(struct spcom_channel *ch,
+		     void *buf,
+		     uint32_t size,
+		     uint32_t timeout_msec)
+{
+	int ret;
+	unsigned long jiffies = msecs_to_jiffies(timeout_msec);
+	long timeleft = 1;
+
+	mutex_lock(&ch->lock);
+
+	/* check for already pending data */
+	if (ch->actual_rx_size) {
+		pr_debug("already pending data size [%d].\n",
+			 ch->actual_rx_size);
+		goto copy_buf;
+	}
+
+	/* reset completion before calling glink */
+	reinit_completion(&ch->rx_done);
+
+	/* Wait for Rx response */
+	pr_debug("Wait for Rx done.\n");
+	if (timeout_msec)
+		timeleft = wait_for_completion_timeout(&ch->rx_done, jiffies);
+	else
+		wait_for_completion(&ch->rx_done);
+
+	if (timeleft == 0) {
+		pr_err("rx_done timeout [%d] msec expired.\n", timeout_msec);
+		goto exit_err;
+	} else if (ch->rx_abort) {
+		pr_err("rx aborted.\n");
+		goto exit_err;
+	} else if (ch->actual_rx_size) {
+		pr_debug("actual_rx_size is [%d].\n", ch->actual_rx_size);
+	} else {
+		pr_err("actual_rx_size is zero.\n");
+		goto exit_err;
+	}
+
+	if (!ch->glink_rx_buf) {
+		pr_err("invalid glink_rx_buf.\n");
+		goto exit_err;
+	}
+
+copy_buf:
+	/* Copy from glink buffer to spcom buffer */
+	size = min_t(int, ch->actual_rx_size, size);
+	memcpy(buf, ch->glink_rx_buf, size);
+
+	pr_debug("copy size [%d].\n", (int) size);
+
+	/* free glink buffer after copy to spcom buffer */
+	glink_rx_done(ch->glink_handle, ch->glink_rx_buf, false);
+	ch->glink_rx_buf = NULL;
+	ch->actual_rx_size = 0;
+
+	/* queue rx buffer for the next time */
+	ret = glink_queue_rx_intent(ch->glink_handle, ch, ch->rx_buf_size);
+	if (ret) {
+		pr_err("glink_queue_rx_intent() failed, ret [%d]", ret);
+		goto exit_err;
+	} else {
+		pr_debug("queue rx_buf, size [%d].\n", ch->rx_buf_size);
+	}
+
+	mutex_unlock(&ch->lock);
+
+	return size;
+exit_err:
+	mutex_unlock(&ch->lock);
+	return -EFAULT;
+}
+
+/**
+ * spcom_get_next_request_size() - get request size.
+ * already ready
+ *
+ * @ch: channel state struct pointer
+ *
+ * Server needs the size of the next request to allocate a request buffer.
+ * Initially used intent-request, however this complicated the remote side,
+ * so both sides are not using glink_tx() with INTENT_REQ anymore.
+ */
+static int spcom_get_next_request_size(struct spcom_channel *ch)
+{
+	int size = -1;
+
+	/* NOTE: Remote clients might not be connected yet.*/
+	mutex_lock(&ch->lock);
+	reinit_completion(&ch->rx_done);
+
+	/* check if already got it via callback */
+	if (ch->actual_rx_size) {
+		pr_debug("next-req-size already ready ch [%s] size [%d].\n",
+			 ch->name, ch->actual_rx_size);
+		goto exit_ready;
+	}
+
+	pr_debug("Wait for Rx Done, ch [%s].\n", ch->name);
+	wait_for_completion(&ch->rx_done);
+	if (ch->actual_rx_size <= 0) {
+		pr_err("invalid rx size [%d] ch [%s].\n",
+		       ch->actual_rx_size, ch->name);
+		goto exit_error;
+	}
+
+exit_ready:
+	size = ch->actual_rx_size;
+	if (size > sizeof(struct spcom_msg_hdr)) {
+		size -= sizeof(struct spcom_msg_hdr);
+	} else {
+		pr_err("rx size [%d] too small.\n", size);
+		goto exit_error;
+	}
+
+	mutex_unlock(&ch->lock);
+	return size;
+
+exit_error:
+	mutex_unlock(&ch->lock);
+	return -EFAULT;
+
+
+}
+
+/*======================================================================*/
+/*		General API for kernel drivers				*/
+/*======================================================================*/
+
+/**
+ * spcom_is_sp_subsystem_link_up() - check if SPSS link is up.
+ *
+ * return: true if link is up, false if link is down.
+ */
+bool spcom_is_sp_subsystem_link_up(void)
+{
+	return (spcom_dev->link_state == GLINK_LINK_STATE_UP);
+}
+EXPORT_SYMBOL(spcom_is_sp_subsystem_link_up);
+
+/*======================================================================*/
+/*		Client API for kernel drivers				*/
+/*======================================================================*/
+
+/**
+ * spcom_register_client() - register a client.
+ *
+ * @info: channel name and ssr callback.
+ *
+ * Return: client handle
+ */
+struct spcom_client *spcom_register_client(struct spcom_client_info *info)
+{
+	int ret;
+	const char *name;
+	struct spcom_channel *ch;
+	struct spcom_client *client;
+
+	if (!info) {
+		pr_err("Invalid parameter.\n");
+			return NULL;
+	}
+	name = info->ch_name;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return NULL;
+
+	ch = spcom_find_channel_by_name(name);
+	if (!ch) {
+		pr_err("channel %s doesn't exist, load App first.\n", name);
+		return NULL;
+	}
+
+	client->ch = ch; /* backtrack */
+
+	ret = spcom_open(ch, OPEN_CHANNEL_TIMEOUT_MSEC);
+	if (ret) {
+		pr_err("failed to open channel [%s].\n", name);
+		kfree(client);
+		client = NULL;
+	} else {
+		pr_info("remote side connect to channel [%s].\n", name);
+	}
+
+	return client;
+}
+EXPORT_SYMBOL(spcom_register_client);
+
+
+/**
+ * spcom_unregister_client() - unregister a client.
+ *
+ * @client: client handle
+ */
+int spcom_unregister_client(struct spcom_client *client)
+{
+	struct spcom_channel *ch;
+
+	if (!client) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	ch = client->ch;
+
+	kfree(client);
+
+	spcom_close(ch);
+
+	return 0;
+}
+EXPORT_SYMBOL(spcom_unregister_client);
+
+
+/**
+ * spcom_client_send_message_sync() - send request and wait for response.
+ *
+ * @client: client handle
+ * @req_ptr: request pointer
+ * @req_size: request size
+ * @resp_ptr: response pointer
+ * @resp_size: response size
+ * @timeout_msec: timeout waiting for response.
+ *
+ * The timeout depends on the specific request handling time at the remote side.
+ */
+int spcom_client_send_message_sync(struct spcom_client	*client,
+				    void	*req_ptr,
+				    uint32_t	req_size,
+				    void	*resp_ptr,
+				    uint32_t	resp_size,
+				    uint32_t	timeout_msec)
+{
+	int ret;
+	struct spcom_channel *ch;
+
+	if (!client || !req_ptr || !resp_ptr) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	ch = client->ch;
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	ret = spcom_tx(ch, req_ptr, req_size, TX_DONE_TIMEOUT_MSEC);
+	if (ret < 0) {
+		pr_err("tx error %d.\n", ret);
+		return ret;
+	}
+
+	ret = spcom_rx(ch, resp_ptr, resp_size, timeout_msec);
+	if (ret < 0) {
+		pr_err("rx error %d.\n", ret);
+		return ret;
+	}
+
+	/* @todo verify response transaction id match the request */
+
+	return ret;
+}
+EXPORT_SYMBOL(spcom_client_send_message_sync);
+
+
+/**
+ * spcom_client_is_server_connected() - is remote server connected.
+ *
+ * @client: client handle
+ */
+bool spcom_client_is_server_connected(struct spcom_client *client)
+{
+	bool connected;
+
+	if (!client) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	connected = spcom_is_channel_connected(client->ch);
+
+	return connected;
+}
+EXPORT_SYMBOL(spcom_client_is_server_connected);
+
+/*======================================================================*/
+/*		Server API for kernel drivers				*/
+/*======================================================================*/
+
+/**
+ * spcom_register_service() - register a server.
+ *
+ * @info: channel name and ssr callback.
+ *
+ * Return: server handle
+ */
+struct spcom_server *spcom_register_service(struct spcom_service_info *info)
+{
+	int ret;
+	const char *name;
+	struct spcom_channel *ch;
+	struct spcom_server *server;
+
+	if (!info) {
+		pr_err("Invalid parameter.\n");
+		return NULL;
+	}
+	name = info->ch_name;
+
+	server = kzalloc(sizeof(*server), GFP_KERNEL);
+	if (!server)
+		return NULL;
+
+	ch = spcom_find_channel_by_name(name);
+	if (!ch) {
+		pr_err("channel %s doesn't exist, load App first.\n", name);
+		return NULL;
+	}
+
+	server->ch = ch; /* backtrack */
+
+	ret = spcom_open(ch, 0);
+	if (ret) {
+		pr_err("failed to open channel [%s].\n", name);
+		kfree(server);
+		server = NULL;
+	}
+
+	return server;
+}
+EXPORT_SYMBOL(spcom_register_service);
+
+/**
+ * spcom_unregister_service() - unregister a server.
+ *
+ * @server: server handle
+ */
+int spcom_unregister_service(struct spcom_server *server)
+{
+	struct spcom_channel *ch;
+
+	if (!server) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	ch = server->ch;
+
+	kfree(server);
+
+	spcom_close(ch);
+
+	return 0;
+}
+EXPORT_SYMBOL(spcom_unregister_service);
+
+/**
+ * spcom_server_get_next_request_size() - get request size.
+ *
+ * @server: server handle
+ *
+ * Return: request size in bytes.
+ */
+int spcom_server_get_next_request_size(struct spcom_server *server)
+{
+	int size;
+	struct spcom_channel *ch;
+
+	if (!server) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	ch = server->ch;
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	size = spcom_get_next_request_size(ch);
+
+	pr_debug("next_request_size [%d].\n", size);
+
+	return size;
+}
+EXPORT_SYMBOL(spcom_server_get_next_request_size);
+
+/**
+ * spcom_server_wait_for_request() - wait for request.
+ *
+ * @server: server handle
+ * @req_ptr: request buffer pointer
+ * @req_size: max request size
+ *
+ * Return: request size in bytes.
+ */
+int spcom_server_wait_for_request(struct spcom_server	*server,
+				  void			*req_ptr,
+				  uint32_t		req_size)
+{
+	int ret;
+	struct spcom_channel *ch;
+
+	if (!server || !req_ptr) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	ch = server->ch;
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	ret = spcom_rx(ch, req_ptr, req_size, 0);
+
+	return ret;
+}
+EXPORT_SYMBOL(spcom_server_wait_for_request);
+
+/**
+ * spcom_server_send_response() - Send response
+ *
+ * @server: server handle
+ * @resp_ptr: response buffer pointer
+ * @resp_size: response size
+ */
+int spcom_server_send_response(struct spcom_server	*server,
+			       void			*resp_ptr,
+			       uint32_t			resp_size)
+{
+	int ret;
+	struct spcom_channel *ch;
+
+	if (!server || !resp_ptr) {
+		pr_err("Invalid parameter.\n");
+		return -EINVAL;
+	}
+
+	ch = server->ch;
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	ret = spcom_tx(ch, resp_ptr, resp_size, TX_DONE_TIMEOUT_MSEC);
+
+	return ret;
+}
+EXPORT_SYMBOL(spcom_server_send_response);
+
+/*======================================================================*/
+/*	USER SPACE commands handling					*/
+/*======================================================================*/
+
+/**
+ * spcom_handle_create_channel_command() - Handle Create Channel command from
+ * user space.
+ *
+ * @cmd_buf:	command buffer.
+ * @cmd_size:	command buffer size.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int spcom_handle_create_channel_command(void *cmd_buf, int cmd_size)
+{
+	int ret = 0;
+	struct spcom_user_create_channel_command *cmd = cmd_buf;
+	const char *ch_name;
+
+	if (cmd_size != sizeof(*cmd)) {
+		pr_err("cmd_size [%d] , expected [%d].\n",
+		       (int) cmd_size,  (int) sizeof(*cmd));
+		return -EINVAL;
+	}
+
+	ch_name = cmd->ch_name;
+
+	pr_debug("ch_name [%s].\n", ch_name);
+
+	ret = spcom_create_channel_chardev(ch_name);
+
+	return ret;
+}
+
+/**
+ * spcom_handle_send_command() - Handle send request/response from user space.
+ *
+ * @buf:	command buffer.
+ * @buf_size:	command buffer size.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int spcom_handle_send_command(struct spcom_channel *ch,
+					     void *cmd_buf, int size)
+{
+	int ret = 0;
+	struct spcom_send_command *cmd = cmd_buf;
+	uint32_t buf_size;
+	void *buf;
+	struct spcom_msg_hdr *hdr;
+	void *tx_buf;
+	int tx_buf_size;
+	uint32_t timeout_msec;
+
+	pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size);
+
+	/*
+	 * check that cmd buf size is at least struct size,
+	 * to allow access to struct fields.
+	 */
+	if (size < sizeof(*cmd)) {
+		pr_err("ch [%s] invalid cmd buf.\n",
+			ch->name);
+		return -EINVAL;
+	}
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	/* parse command buffer */
+	buf = &cmd->buf;
+	buf_size = cmd->buf_size;
+	timeout_msec = cmd->timeout_msec;
+
+	/* Check param validity */
+	if (buf_size > SPCOM_MAX_RESPONSE_SIZE) {
+		pr_err("ch [%s] invalid buf size [%d].\n",
+			ch->name, buf_size);
+		return -EINVAL;
+	}
+	if (size != sizeof(*cmd) + buf_size) {
+		pr_err("ch [%s] invalid cmd size [%d].\n",
+			ch->name, size);
+		return -EINVAL;
+	}
+
+	/* Allocate Buffers*/
+	tx_buf_size = sizeof(*hdr) + buf_size;
+	tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
+	if (!tx_buf)
+		return -ENOMEM;
+
+	/* Prepare Tx Buf */
+	hdr = tx_buf;
+
+	/* Header */
+	hdr->txn_id = ch->txn_id;
+	if (!ch->is_server) {
+		ch->txn_id++;   /* client sets the request txn_id */
+		ch->response_timeout_msec = timeout_msec;
+	}
+
+	/* user buf */
+	memcpy(hdr->buf, buf, buf_size);
+
+	/*
+	 * remote side should have rx buffer ready.
+	 * tx_done is expected to be received quickly.
+	 */
+	ret = spcom_tx(ch, tx_buf, tx_buf_size, TX_DONE_TIMEOUT_MSEC);
+	if (ret < 0)
+		pr_err("tx error %d.\n", ret);
+
+	kfree(tx_buf);
+
+	return ret;
+}
+
+/**
+ * modify_ion_addr() - replace the ION buffer virtual address with physical
+ * address in a request or response buffer.
+ *
+ * @buf: buffer to modify
+ * @buf_size: buffer size
+ * @ion_info: ION buffer info such as FD and offset in buffer.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int modify_ion_addr(void *buf,
+			    uint32_t buf_size,
+			    struct spcom_ion_info ion_info)
+{
+	struct ion_handle *handle = NULL;
+	ion_phys_addr_t ion_phys_addr;
+	size_t len;
+	int fd;
+	uint32_t buf_offset;
+	char *ptr = (char *)buf;
+	int ret;
+
+	fd = ion_info.fd;
+	buf_offset = ion_info.buf_offset;
+	ptr += buf_offset;
+
+	if (fd < 0) {
+		pr_err("invalid fd [%d].\n", fd);
+		return -ENODEV;
+	}
+
+	if (buf_size < sizeof(uint64_t)) {
+		pr_err("buf size too small [%d].\n", buf_size);
+		return -ENODEV;
+	}
+
+	if (buf_offset > buf_size - sizeof(uint64_t)) {
+		pr_err("invalid buf_offset [%d].\n", buf_offset);
+		return -ENODEV;
+	}
+
+	/* Get ION handle from fd */
+	handle = ion_import_dma_buf_fd(spcom_dev->ion_client, fd);
+	if (handle == NULL) {
+		pr_err("fail to get ion handle.\n");
+		return -EINVAL;
+	}
+	pr_debug("ion handle ok.\n");
+
+	/* Get the ION buffer Physical Address */
+	ret = ion_phys(spcom_dev->ion_client, handle, &ion_phys_addr, &len);
+	if (ret < 0) {
+		pr_err("fail to get ion phys addr.\n");
+		ion_free(spcom_dev->ion_client, handle);
+		return -EINVAL;
+	}
+	if (buf_offset % sizeof(uint64_t))
+		pr_debug("offset [%d] is NOT 64-bit aligned.\n", buf_offset);
+	else
+		pr_debug("offset [%d] is 64-bit aligned.\n", buf_offset);
+
+	/* Set the ION Physical Address at the buffer offset */
+	pr_debug("ion phys addr = [0x%lx].\n", (long int) ion_phys_addr);
+	memcpy(ptr, &ion_phys_addr, sizeof(uint64_t));
+
+	/* Release the ION handle */
+	ion_free(spcom_dev->ion_client, handle);
+
+	return 0;
+}
+
+/**
+ * spcom_handle_send_modified_command() - send a request/response with ION
+ * buffer address. Modify the request/response by replacing the ION buffer
+ * virtual address with the physical address.
+ *
+ * @ch: channel pointer
+ * @cmd_buf: User space command buffer
+ * @size: size of user command buffer
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int spcom_handle_send_modified_command(struct spcom_channel *ch,
+					       void *cmd_buf, int size)
+{
+	int ret = 0;
+	struct spcom_user_send_modified_command *cmd = cmd_buf;
+	uint32_t buf_size;
+	void *buf;
+	struct spcom_msg_hdr *hdr;
+	void *tx_buf;
+	int tx_buf_size;
+	uint32_t timeout_msec;
+	struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF_PER_CMD];
+	int i;
+
+	pr_debug("send req/resp ch [%s] size [%d] .\n", ch->name, size);
+
+	/*
+	 * check that cmd buf size is at least struct size,
+	 * to allow access to struct fields.
+	 */
+	if (size < sizeof(*cmd)) {
+		pr_err("ch [%s] invalid cmd buf.\n",
+			ch->name);
+		return -EINVAL;
+	}
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	/* parse command buffer */
+	buf = &cmd->buf;
+	buf_size = cmd->buf_size;
+	timeout_msec = cmd->timeout_msec;
+	memcpy(ion_info, cmd->ion_info, sizeof(ion_info));
+
+	/* Check param validity */
+	if (buf_size > SPCOM_MAX_RESPONSE_SIZE) {
+		pr_err("ch [%s] invalid buf size [%d].\n",
+			ch->name, buf_size);
+		return -EINVAL;
+	}
+	if (size != sizeof(*cmd) + buf_size) {
+		pr_err("ch [%s] invalid cmd size [%d].\n",
+			ch->name, size);
+		return -EINVAL;
+	}
+
+	/* Allocate Buffers*/
+	tx_buf_size = sizeof(*hdr) + buf_size;
+	tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
+	if (!tx_buf)
+		return -ENOMEM;
+
+	/* Prepare Tx Buf */
+	hdr = tx_buf;
+
+	/* Header */
+	hdr->txn_id = ch->txn_id;
+	if (!ch->is_server) {
+		ch->txn_id++;   /* client sets the request txn_id */
+		ch->response_timeout_msec = timeout_msec;
+	}
+
+	/* user buf */
+	memcpy(hdr->buf, buf, buf_size);
+
+	for (i = 0 ; i < ARRAY_SIZE(ion_info) ; i++) {
+		if (ion_info[i].fd >= 0) {
+			ret = modify_ion_addr(hdr->buf, buf_size, ion_info[i]);
+			if (ret < 0) {
+				pr_err("modify_ion_addr() error [%d].\n", ret);
+				kfree(tx_buf);
+				return -EFAULT;
+			}
+		}
+	}
+
+	/*
+	 * remote side should have rx buffer ready.
+	 * tx_done is expected to be received quickly.
+	 */
+	ret = spcom_tx(ch, tx_buf, tx_buf_size, TX_DONE_TIMEOUT_MSEC);
+	if (ret < 0)
+		pr_err("tx error %d.\n", ret);
+
+	kfree(tx_buf);
+
+	return ret;
+}
+
+
+/**
+ * spcom_handle_lock_ion_buf_command() - Lock an ION buffer.
+ *
+ * Lock an ION buffer, prevent it from being free if the user space App crash,
+ * while it is used by the remote subsystem.
+ */
+static int spcom_handle_lock_ion_buf_command(struct spcom_channel *ch,
+					      void *cmd_buf, int size)
+{
+	struct spcom_user_command *cmd = cmd_buf;
+	int fd = cmd->arg;
+	struct ion_handle *ion_handle;
+	int i;
+
+	if (size != sizeof(*cmd)) {
+		pr_err("cmd size [%d] , expected [%d].\n",
+		       (int) size,  (int) sizeof(*cmd));
+		return -EINVAL;
+	}
+
+	/* Check ION client */
+	if (spcom_dev->ion_client == NULL) {
+		pr_err("invalid ion client.\n");
+		return -ENODEV;
+	}
+
+	/* Get ION handle from fd - this increments the ref count */
+	ion_handle = ion_import_dma_buf_fd(spcom_dev->ion_client, fd);
+	if (ion_handle == NULL) {
+		pr_err("fail to get ion handle.\n");
+		return -EINVAL;
+	}
+	pr_debug("ion handle ok.\n");
+
+	/* Check if this ION buffer is already locked */
+	for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
+		if (ch->ion_handle_table[i] == ion_handle) {
+			pr_debug("fd [%d] ion buf is already locked.\n", fd);
+			/* decrement back the ref count */
+			ion_free(spcom_dev->ion_client, ion_handle);
+			return -EINVAL;
+		}
+	}
+
+       /* Store the ION handle */
+	for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
+		if (ch->ion_handle_table[i] == NULL) {
+			ch->ion_handle_table[i] = ion_handle;
+			ch->ion_fd_table[i] = fd;
+			pr_debug("locked ion buf#[%d], fd [%d].\n", i, fd);
+			return 0;
+		}
+	}
+
+	return -EFAULT;
+}
+
+/**
+ * spcom_unlock_ion_buf() - Unlock an ION buffer.
+ *
+ * Unlock an ION buffer, let it be free, when it is no longer being used by
+ * the remote subsystem.
+ */
+static int spcom_unlock_ion_buf(struct spcom_channel *ch, int fd)
+{
+	struct ion_client *ion_client = spcom_dev->ion_client;
+	int i;
+	bool found = false;
+
+	pr_debug("Unlock ion buf ch [%s] fd [%d].\n", ch->name, fd);
+
+	/* Check ION client */
+	if (ion_client == NULL) {
+		pr_err("fail to create ion client.\n");
+		return -ENODEV;
+	}
+
+	if (fd == (int) SPCOM_ION_FD_UNLOCK_ALL) {
+		pr_debug("unlocked ALL ion buf ch [%s].\n", ch->name);
+		found = true;
+		/* unlock all ION buf */
+		for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
+			if (ch->ion_handle_table[i] != NULL) {
+				ion_free(ion_client, ch->ion_handle_table[i]);
+				ch->ion_handle_table[i] = NULL;
+				ch->ion_fd_table[i] = -1;
+				pr_debug("unlocked ion buf#[%d].\n", i);
+			}
+		}
+	} else {
+		/* unlock specific ION buf */
+		for (i = 0 ; i < ARRAY_SIZE(ch->ion_handle_table) ; i++) {
+			if (ch->ion_fd_table[i] == fd) {
+				ion_free(ion_client, ch->ion_handle_table[i]);
+				ch->ion_handle_table[i] = NULL;
+				ch->ion_fd_table[i] = -1;
+				pr_debug("unlocked ion buf#[%d].\n", i);
+				found = true;
+				break;
+			}
+		}
+	}
+
+	if (!found) {
+		pr_err("ch [%s] fd [%d] was not found.\n", ch->name, fd);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/**
+ * spcom_handle_unlock_ion_buf_command() - Unlock an ION buffer.
+ *
+ * Unlock an ION buffer, let it be free, when it is no longer being used by
+ * the remote subsystem.
+ */
+static int spcom_handle_unlock_ion_buf_command(struct spcom_channel *ch,
+					      void *cmd_buf, int size)
+{
+	int ret;
+	struct spcom_user_command *cmd = cmd_buf;
+	int fd = cmd->arg;
+
+	if (size != sizeof(*cmd)) {
+		pr_err("cmd size [%d] , expected [%d].\n",
+		       (int) size,  (int) sizeof(*cmd));
+		return -EINVAL;
+	}
+
+	ret = spcom_unlock_ion_buf(ch, fd);
+
+	return ret;
+}
+
+/**
+ * spcom_handle_fake_ssr_command() - Handle fake ssr command from user space.
+ */
+static int spcom_handle_fake_ssr_command(struct spcom_channel *ch, int arg)
+{
+	pr_debug("Start Fake glink SSR subsystem [%s].\n", spcom_edge);
+	glink_ssr(spcom_edge);
+	pr_debug("Fake glink SSR subsystem [%s] done.\n", spcom_edge);
+
+	return 0;
+}
+
+/**
+ * spcom_handle_write() - Handle user space write commands.
+ *
+ * @buf:	command buffer.
+ * @buf_size:	command buffer size.
+ *
+ * Return: 0 on successful operation, negative value otherwise.
+ */
+static int spcom_handle_write(struct spcom_channel *ch,
+			       void *buf,
+			       int buf_size)
+{
+	int ret = 0;
+	struct spcom_user_command *cmd = NULL;
+	int cmd_id = 0;
+	int swap_id;
+	char cmd_name[5] = {0}; /* debug only */
+
+	/* opcode field is the minimum length of cmd */
+	if (buf_size < sizeof(cmd->cmd_id)) {
+		pr_err("Invalid argument user buffer size %d.\n", buf_size);
+		return -EINVAL;
+	}
+
+	cmd = (struct spcom_user_command *)buf;
+	cmd_id = (int) cmd->cmd_id;
+	swap_id = htonl(cmd->cmd_id);
+	memcpy(cmd_name, &swap_id, sizeof(int));
+
+	pr_debug("cmd_id [0x%x] cmd_name [%s].\n", cmd_id, cmd_name);
+
+	switch (cmd_id) {
+	case SPCOM_CMD_SEND:
+		ret = spcom_handle_send_command(ch, buf, buf_size);
+		break;
+	case SPCOM_CMD_SEND_MODIFIED:
+		ret = spcom_handle_send_modified_command(ch, buf, buf_size);
+		break;
+	case SPCOM_CMD_LOCK_ION_BUF:
+		ret = spcom_handle_lock_ion_buf_command(ch, buf, buf_size);
+		break;
+	case SPCOM_CMD_UNLOCK_ION_BUF:
+		ret = spcom_handle_unlock_ion_buf_command(ch, buf, buf_size);
+		break;
+	case SPCOM_CMD_FSSR:
+		ret = spcom_handle_fake_ssr_command(ch, cmd->arg);
+		break;
+	case SPCOM_CMD_CREATE_CHANNEL:
+		ret = spcom_handle_create_channel_command(buf, buf_size);
+		break;
+	default:
+		pr_err("Invalid Command Id [0x%x].\n", (int) cmd->cmd_id);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * spcom_handle_get_req_size() - Handle user space get request size command
+ *
+ * @ch:	channel handle
+ * @buf:	command buffer.
+ * @size:	command buffer size.
+ *
+ * Return: size in bytes.
+ */
+static int spcom_handle_get_req_size(struct spcom_channel *ch,
+				      void *buf,
+				      uint32_t size)
+{
+	uint32_t next_req_size = 0;
+
+	if (size < sizeof(next_req_size)) {
+		pr_err("buf size [%d] too small.\n", (int) size);
+		return -EINVAL;
+	}
+
+	next_req_size = spcom_get_next_request_size(ch);
+
+	memcpy(buf, &next_req_size, sizeof(next_req_size));
+	pr_debug("next_req_size [%d].\n", next_req_size);
+
+	return sizeof(next_req_size); /* can't exceed user buffer size */
+}
+
+/**
+ * spcom_handle_read_req_resp() - Handle user space get request/response command
+ *
+ * @ch:	channel handle
+ * @buf:	command buffer.
+ * @size:	command buffer size.
+ *
+ * Return: size in bytes.
+ */
+static int spcom_handle_read_req_resp(struct spcom_channel *ch,
+				       void *buf,
+				       uint32_t size)
+{
+	int ret;
+	struct spcom_msg_hdr *hdr;
+	void *rx_buf;
+	int rx_buf_size;
+	uint32_t timeout_msec = 0; /* client only */
+
+	/* Check if remote side connect */
+	if (!spcom_is_channel_connected(ch)) {
+		pr_err("ch [%s] remote side not connect.\n", ch->name);
+		return -ENOTCONN;
+	}
+
+	/* Check param validity */
+	if (size > SPCOM_MAX_RESPONSE_SIZE) {
+		pr_err("ch [%s] inavlid size [%d].\n",
+			ch->name, size);
+		return -EINVAL;
+	}
+
+	/* Allocate Buffers*/
+	rx_buf_size = sizeof(*hdr) + size;
+	rx_buf = kzalloc(rx_buf_size, GFP_KERNEL);
+	if (!rx_buf)
+		return -ENOMEM;
+
+	/*
+	 * client response timeout depends on the request
+	 * handling time on the remote side .
+	 */
+	if (!ch->is_server) {
+		timeout_msec = ch->response_timeout_msec;
+		pr_debug("response_timeout_msec = %d.\n", (int) timeout_msec);
+	}
+
+	ret = spcom_rx(ch, rx_buf, rx_buf_size, timeout_msec);
+	if (ret < 0) {
+		pr_err("rx error %d.\n", ret);
+		goto exit_err;
+	} else {
+		size = ret; /* actual_rx_size */
+	}
+
+	hdr = rx_buf;
+
+	if (ch->is_server) {
+		ch->txn_id = hdr->txn_id;
+		pr_debug("request txn_id [0x%x].\n", ch->txn_id);
+	}
+
+	/* copy data to user without the header */
+	if (size > sizeof(*hdr)) {
+		size -= sizeof(*hdr);
+		memcpy(buf, hdr->buf, size);
+	} else {
+		pr_err("rx size [%d] too small.\n", size);
+		goto exit_err;
+	}
+
+	kfree(rx_buf);
+	return size;
+exit_err:
+	kfree(rx_buf);
+	return -EFAULT;
+
+}
+
+/**
+ * spcom_handle_read() - Handle user space read request/response or
+ * request-size command
+ *
+ * @ch:	channel handle
+ * @buf:	command buffer.
+ * @size:	command buffer size.
+ *
+ * A special size SPCOM_GET_NEXT_REQUEST_SIZE, which is bigger than the max
+ * response/request tells the kernel that user space only need the size.
+ *
+ * Return: size in bytes.
+ */
+static int spcom_handle_read(struct spcom_channel *ch,
+			      void *buf,
+			      uint32_t size)
+{
+	if (size == SPCOM_GET_NEXT_REQUEST_SIZE) {
+		pr_debug("get next request size, ch [%s].\n", ch->name);
+		size = spcom_handle_get_req_size(ch, buf, size);
+		ch->is_server = true;
+	} else {
+		pr_debug("get request/response, ch [%s].\n", ch->name);
+		size = spcom_handle_read_req_resp(ch, buf, size);
+	}
+
+	pr_debug("ch [%s] , size = %d.\n", ch->name, size);
+
+	return size;
+}
+
+/*======================================================================*/
+/*		CHAR DEVICE USER SPACE INTERFACE			*/
+/*======================================================================*/
+
+/**
+ * file_to_filename() - get the filename from file pointer.
+ *
+ * @filp: file pointer
+ *
+ * it is used for debug prints.
+ *
+ * Return: filename string or "unknown".
+ */
+static char *file_to_filename(struct file *filp)
+{
+	struct dentry *dentry = NULL;
+	char *filename = NULL;
+
+	if (!filp || !filp->f_path.dentry)
+		return "unknown";
+
+	dentry = filp->f_path.dentry;
+	filename = dentry->d_iname;
+
+	return filename;
+}
+
+/**
+ * spcom_device_open() - handle channel file open() from user space.
+ *
+ * @filp: file pointer
+ *
+ * The file name (without path) is the channel name.
+ * Open the relevant glink channel.
+ * Store the channel context in the file private
+ * date pointer for future read/write/close
+ * operations.
+ */
+static int spcom_device_open(struct inode *inode, struct file *filp)
+{
+	int ret = 0;
+	struct spcom_channel *ch;
+	const char *name = file_to_filename(filp);
+
+	pr_debug("Open file [%s].\n", name);
+
+	if (strcmp(name, DEVICE_NAME) == 0) {
+		pr_debug("root dir skipped.\n");
+		return 0;
+	}
+
+	if (strcmp(name, "sp_ssr") == 0) {
+		pr_debug("sp_ssr dev node skipped.\n");
+		return 0;
+	}
+
+	ch = spcom_find_channel_by_name(name);
+	if (!ch) {
+		pr_err("channel %s doesn't exist, load App first.\n", name);
+		return -ENODEV;
+	}
+
+	filp->private_data = ch;
+
+	ret = spcom_open(ch, OPEN_CHANNEL_TIMEOUT_MSEC);
+	if (ret == -ETIMEDOUT) {
+		pr_err("Connection timeout channel [%s].\n", name);
+	} else if (ret) {
+		pr_err("failed to open channel [%s] , err=%d.\n", name, ret);
+		return ret;
+	}
+
+	pr_debug("finished.\n");
+
+	return 0;
+}
+
+/**
+ * spcom_device_release() - handle channel file close() from user space.
+ *
+ * @filp: file pointer
+ *
+ * The file name (without path) is the channel name.
+ * Open the relevant glink channel.
+ * Store the channel context in the file private
+ * date pointer for future read/write/close
+ * operations.
+ */
+static int spcom_device_release(struct inode *inode, struct file *filp)
+{
+	struct spcom_channel *ch;
+	const char *name = file_to_filename(filp);
+	bool connected = false;
+
+	pr_debug("Close file [%s].\n", name);
+
+	if (strcmp(name, DEVICE_NAME) == 0) {
+		pr_debug("root dir skipped.\n");
+		return 0;
+	}
+
+	if (strcmp(name, "sp_ssr") == 0) {
+		pr_debug("sp_ssr dev node skipped.\n");
+		return 0;
+	}
+
+	ch = filp->private_data;
+
+	if (!ch) {
+		pr_debug("ch is NULL, file name %s.\n", file_to_filename(filp));
+		return -ENODEV;
+	}
+
+	/* channel might be already closed or disconnected */
+	if (spcom_is_channel_open(ch) && spcom_is_channel_connected(ch))
+		connected = true;
+
+	reinit_completion(&ch->disconnect);
+
+	spcom_close(ch);
+
+	if (connected) {
+		pr_debug("Wait for event GLINK_LOCAL_DISCONNECTED, ch [%s].\n",
+			 name);
+		wait_for_completion(&ch->disconnect);
+		pr_debug("GLINK_LOCAL_DISCONNECTED signaled, ch [%s].\n", name);
+	}
+
+	return 0;
+}
+
+/**
+ * spcom_device_write() - handle channel file write() from user space.
+ *
+ * @filp: file pointer
+ *
+ * Return: On Success - same size as number of bytes to write.
+ * On Failure - negative value.
+ */
+static ssize_t spcom_device_write(struct file *filp,
+				   const char __user *user_buff,
+				   size_t size, loff_t *f_pos)
+{
+	int ret;
+	char *buf;
+	struct spcom_channel *ch;
+	const char *name = file_to_filename(filp);
+
+	pr_debug("Write file [%s] size [%d] pos [%d].\n",
+		 name, (int) size, (int) *f_pos);
+
+	if (!user_buff || !f_pos || !filp) {
+		pr_err("invalid null parameters.\n");
+		return -EINVAL;
+	}
+
+	ch = filp->private_data;
+	if (!ch) {
+		pr_debug("invalid ch pointer.\n");
+		/* Allow some special commands via /dev/spcom and /dev/sp_ssr */
+	} else {
+		/* Check if remote side connect */
+		if (!spcom_is_channel_connected(ch)) {
+			pr_err("ch [%s] remote side not connect.\n", ch->name);
+			return -ENOTCONN;
+		}
+	}
+
+	if (size > SPCOM_MAX_COMMAND_SIZE) {
+		pr_err("size [%d] > max size [%d].\n",
+			   (int) size, (int) SPCOM_MAX_COMMAND_SIZE);
+		return -EINVAL;
+	}
+
+	if (*f_pos != 0) {
+		pr_err("offset should be zero, no sparse buffer.\n");
+		return -EINVAL;
+	}
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	ret = copy_from_user(buf, user_buff, size);
+	if (ret) {
+		pr_err("Unable to copy from user (err %d).\n", ret);
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	ret = spcom_handle_write(ch, buf, size);
+	if (ret) {
+		pr_err("handle command error [%d].\n", ret);
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	kfree(buf);
+
+	return size;
+}
+
+/**
+ * spcom_device_read() - handle channel file write() from user space.
+ *
+ * @filp: file pointer
+ *
+ * Return: number of bytes to read on success, negative value on
+ * failure.
+ */
+static ssize_t spcom_device_read(struct file *filp, char __user *user_buff,
+				 size_t size, loff_t *f_pos)
+{
+	int ret = 0;
+	int actual_size = 0;
+	char *buf;
+	struct spcom_channel *ch;
+	const char *name = file_to_filename(filp);
+
+	pr_debug("Read file [%s], size = %d bytes.\n", name, (int) size);
+
+	if (!filp || !user_buff || !f_pos ||
+	    (size == 0) || (size > SPCOM_MAX_READ_SIZE)) {
+		pr_err("invalid parameters.\n");
+		return -EINVAL;
+	}
+
+	ch = filp->private_data;
+
+	buf = kzalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	actual_size = spcom_handle_read(ch, buf, size);
+	if ((actual_size <= 0) || (actual_size > size)) {
+		pr_err("invalid actual_size [%d].\n", actual_size);
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	ret = copy_to_user(user_buff, buf, actual_size);
+
+	if (ret) {
+		pr_err("Unable to copy to user, err = %d.\n", ret);
+		kfree(buf);
+		return -EFAULT;
+	}
+
+	kfree(buf);
+
+	pr_debug("ch [%s] ret [%d].\n", name, (int) actual_size);
+
+	return actual_size;
+}
+
+/**
+ * spcom_device_poll() - handle channel file poll() from user space.
+ *
+ * @filp: file pointer
+ *
+ * This allows user space to wait/check for channel connection,
+ * or wait for SSR event.
+ *
+ * Return: event bitmask on success, set POLLERR on failure.
+ */
+static unsigned int spcom_device_poll(struct file *filp,
+				       struct poll_table_struct *poll_table)
+{
+	/*
+	 * when user call with timeout -1 for blocking mode,
+	 * any bit must be set in response
+	 */
+	unsigned int ret = SPCOM_POLL_READY_FLAG;
+	unsigned long mask;
+	struct spcom_channel *ch;
+	const char *name = file_to_filename(filp);
+	bool wait = false;
+	bool done = false;
+	/* Event types always implicitly polled for */
+	unsigned long reserved = POLLERR | POLLHUP | POLLNVAL;
+	int ready = 0;
+
+	ch = filp->private_data;
+
+	mask = poll_requested_events(poll_table);
+
+	pr_debug("== ch [%s] mask [0x%x] ==.\n", name, (int) mask);
+
+	/* user space API has poll use "short" and not "long" */
+	mask &= 0x0000FFFF;
+
+	wait = mask & SPCOM_POLL_WAIT_FLAG;
+	if (wait)
+		pr_debug("ch [%s] wait for event flag is ON.\n", name);
+	mask &= ~SPCOM_POLL_WAIT_FLAG; /* clear the wait flag */
+	mask &= ~SPCOM_POLL_READY_FLAG; /* clear the ready flag */
+	mask &= ~reserved; /* clear the implicitly set reserved bits */
+
+	switch (mask) {
+	case SPCOM_POLL_LINK_STATE:
+		pr_debug("ch [%s] SPCOM_POLL_LINK_STATE.\n", name);
+		if (wait) {
+			reinit_completion(&spcom_dev->link_state_changed);
+			ready = wait_for_completion_interruptible(
+				&spcom_dev->link_state_changed);
+			pr_debug("ch [%s] poll LINK_STATE signaled.\n", name);
+		}
+		done = (spcom_dev->link_state == GLINK_LINK_STATE_UP);
+		break;
+	case SPCOM_POLL_CH_CONNECT:
+		pr_debug("ch [%s] SPCOM_POLL_CH_CONNECT.\n", name);
+		if (wait) {
+			reinit_completion(&ch->connect);
+			ready = wait_for_completion_interruptible(&ch->connect);
+			pr_debug("ch [%s] poll CH_CONNECT signaled.\n", name);
+		}
+		done = completion_done(&ch->connect);
+		break;
+	default:
+		pr_err("ch [%s] poll, invalid mask [0x%x].\n",
+			 name, (int) mask);
+		ret = POLLERR;
+		break;
+	}
+
+	if (ready < 0) { /* wait was interrupted */
+		pr_debug("ch [%s] poll interrupted, ret [%d].\n", name, ready);
+		ret = POLLERR | SPCOM_POLL_READY_FLAG | mask;
+	}
+	if (done)
+		ret |= mask;
+
+	pr_debug("ch [%s] poll, mask = 0x%x, ret=0x%x.\n",
+		 name, (int) mask, ret);
+
+	return ret;
+}
+
+/* file operation supported from user space */
+static const struct file_operations fops = {
+	.owner = THIS_MODULE,
+	.read = spcom_device_read,
+	.poll = spcom_device_poll,
+	.write = spcom_device_write,
+	.open = spcom_device_open,
+	.release = spcom_device_release,
+};
+
+/**
+ * spcom_create_channel_chardev() - Create a channel char-dev node file
+ * for user space interface
+ */
+static int spcom_create_channel_chardev(const char *name)
+{
+	int ret;
+	struct device *dev;
+	struct spcom_channel *ch;
+	dev_t devt;
+	struct class *cls = spcom_dev->driver_class;
+	struct device *parent = spcom_dev->class_dev;
+	void *priv;
+	struct cdev *cdev;
+
+	pr_debug("Add channel [%s].\n", name);
+
+	ch = spcom_find_channel_by_name(name);
+	if (ch) {
+		pr_err("channel [%s] already exist.\n", name);
+		return -EINVAL;
+	}
+
+	ch = spcom_find_channel_by_name(""); /* find reserved channel */
+	if (!ch) {
+		pr_err("no free channel.\n");
+		return -ENODEV;
+	}
+
+	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
+	if (!cdev)
+		return -ENOMEM;
+
+	spcom_dev->channel_count++;
+	devt = spcom_dev->device_no + spcom_dev->channel_count;
+	priv = ch;
+	dev = device_create(cls, parent, devt, priv, name);
+	if (!dev) {
+		pr_err("device_create failed.\n");
+		kfree(cdev);
+		return -ENODEV;
+	}
+
+	cdev_init(cdev, &fops);
+	cdev->owner = THIS_MODULE;
+
+	ret = cdev_add(cdev, devt, 1);
+	if (ret < 0) {
+		pr_err("cdev_add failed %d\n", ret);
+		goto exit_destroy_device;
+	}
+
+	spcom_init_channel(ch, name);
+
+	ch->cdev = cdev;
+	ch->dev = dev;
+
+	return 0;
+
+exit_destroy_device:
+	device_destroy(spcom_dev->driver_class, devt);
+	kfree(cdev);
+	return -EFAULT;
+}
+
+static int __init spcom_register_chardev(void)
+{
+	int ret;
+	unsigned int baseminor = 0;
+	unsigned int count = 1;
+	void *priv = spcom_dev;
+
+	ret = alloc_chrdev_region(&spcom_dev->device_no, baseminor, count,
+				 DEVICE_NAME);
+	if (ret < 0) {
+		pr_err("alloc_chrdev_region failed %d\n", ret);
+		return ret;
+	}
+
+	spcom_dev->driver_class = class_create(THIS_MODULE, DEVICE_NAME);
+	if (IS_ERR(spcom_dev->driver_class)) {
+		ret = -ENOMEM;
+		pr_err("class_create failed %d\n", ret);
+		goto exit_unreg_chrdev_region;
+	}
+
+	spcom_dev->class_dev = device_create(spcom_dev->driver_class, NULL,
+				  spcom_dev->device_no, priv,
+				  DEVICE_NAME);
+
+	if (!spcom_dev->class_dev) {
+		pr_err("class_device_create failed %d\n", ret);
+		ret = -ENOMEM;
+		goto exit_destroy_class;
+	}
+
+	cdev_init(&spcom_dev->cdev, &fops);
+	spcom_dev->cdev.owner = THIS_MODULE;
+
+	ret = cdev_add(&spcom_dev->cdev,
+		       MKDEV(MAJOR(spcom_dev->device_no), 0),
+		       SPCOM_MAX_CHANNELS);
+	if (ret < 0) {
+		pr_err("cdev_add failed %d\n", ret);
+		goto exit_destroy_device;
+	}
+
+	pr_debug("char device created.\n");
+
+	return 0;
+
+exit_destroy_device:
+	device_destroy(spcom_dev->driver_class, spcom_dev->device_no);
+exit_destroy_class:
+	class_destroy(spcom_dev->driver_class);
+exit_unreg_chrdev_region:
+	unregister_chrdev_region(spcom_dev->device_no, 1);
+	return ret;
+}
+
+static void spcom_unregister_chrdev(void)
+{
+	cdev_del(&spcom_dev->cdev);
+	device_destroy(spcom_dev->driver_class, spcom_dev->device_no);
+	class_destroy(spcom_dev->driver_class);
+	unregister_chrdev_region(spcom_dev->device_no, 1);
+
+}
+
+/*======================================================================*/
+/*		Device Tree						*/
+/*======================================================================*/
+
+static int spcom_parse_dt(struct device_node *np)
+{
+	int ret;
+	const char *propname = "qcom,spcom-ch-names";
+	int num_ch = of_property_count_strings(np, propname);
+	int i;
+	const char *name;
+
+	pr_debug("num of predefined channels [%d].\n", num_ch);
+
+	for (i = 0; i < num_ch; i++) {
+		ret = of_property_read_string_index(np, propname, i, &name);
+		if (ret) {
+			pr_err("failed to read DT channel [%d] name .\n", i);
+			return -EFAULT;
+		}
+		strlcpy(spcom_dev->predefined_ch_name[i],
+			name,
+			sizeof(spcom_dev->predefined_ch_name[i]));
+
+		pr_debug("found ch [%s].\n", name);
+	}
+
+	return num_ch;
+}
+
+static int spcom_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct spcom_device *dev = NULL;
+	struct glink_link_info link_info;
+	struct device_node *np;
+	struct link_state_notifier_info *notif_handle;
+
+	if (!pdev) {
+		pr_err("invalid pdev.\n");
+		return -ENODEV;
+	}
+
+	np = pdev->dev.of_node;
+	if (!np) {
+		pr_err("invalid DT node.\n");
+		return -EINVAL;
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (dev == NULL)
+		return -ENOMEM;
+
+	spcom_dev = dev;
+	mutex_init(&dev->lock);
+	init_completion(&dev->link_state_changed);
+	spcom_dev->link_state = GLINK_LINK_STATE_DOWN;
+
+	ret = spcom_register_chardev();
+	if (ret) {
+		pr_err("create character device failed.\n");
+		goto fail_reg_chardev;
+	}
+
+	link_info.glink_link_state_notif_cb = spcom_link_state_notif_cb;
+	link_info.transport = spcom_transport;
+	link_info.edge = spcom_edge;
+
+	ret = spcom_parse_dt(np);
+	if (ret < 0)
+		goto fail_reg_chardev;
+
+	/*
+	 * Register for glink link up/down notification.
+	 * glink channels can't be opened before link is up.
+	 */
+	pr_debug("register_link_state_cb(), transport [%s] edge [%s]\n",
+		link_info.transport, link_info.edge);
+	notif_handle = glink_register_link_state_cb(&link_info, spcom_dev);
+	if (!notif_handle) {
+		pr_err("glink_register_link_state_cb(), err [%d]\n", ret);
+		goto fail_reg_chardev;
+	}
+
+	spcom_dev->ion_client = msm_ion_client_create(DEVICE_NAME);
+	if (spcom_dev->ion_client == NULL) {
+		pr_err("fail to create ion client.\n");
+		goto fail_reg_chardev;
+	}
+
+	pr_info("Driver Initialization ok.\n");
+
+	return 0;
+
+fail_reg_chardev:
+	pr_err("Failed to init driver.\n");
+	spcom_unregister_chrdev();
+	kfree(dev);
+	spcom_dev = NULL;
+
+	return -ENODEV;
+}
+
+static const struct of_device_id spcom_match_table[] = {
+	{ .compatible = "qcom,spcom", },
+	{ },
+};
+
+static struct platform_driver spcom_driver = {
+	.probe = spcom_probe,
+	.driver = {
+		.name = DEVICE_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(spcom_match_table),
+	},
+};
+
+/*======================================================================*/
+/*		Driver Init/Exit					*/
+/*======================================================================*/
+
+static int __init spcom_init(void)
+{
+	int ret;
+
+	pr_info("spcom driver Ver 1.0 23-Nov-2015.\n");
+
+	ret = platform_driver_register(&spcom_driver);
+	if (ret)
+		pr_err("spcom_driver register failed %d\n", ret);
+
+	return 0;
+}
+module_init(spcom_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Secure Processor Communication");
diff --git a/drivers/soc/qcom/subsys-pil-tz.c b/drivers/soc/qcom/subsys-pil-tz.c
index a46dbbd..0063ae1 100644
--- a/drivers/soc/qcom/subsys-pil-tz.c
+++ b/drivers/soc/qcom/subsys-pil-tz.c
@@ -882,7 +882,7 @@ static irqreturn_t subsys_err_fatal_intr_handler (int irq, void *dev_id)
 							d->subsys_desc.name);
 		return IRQ_HANDLED;
 	}
-	subsys_set_crash_status(d->subsys, true);
+	subsys_set_crash_status(d->subsys, CRASH_STATUS_ERR_FATAL);
 	log_failure_reason(d);
 	subsystem_restart_dev(d->subsys);
 
@@ -901,7 +901,7 @@ static irqreturn_t subsys_wdog_bite_irq_handler(int irq, void *dev_id)
 			!gpio_get_value(d->subsys_desc.err_fatal_gpio))
 		panic("%s: System ramdump requested. Triggering device restart!\n",
 							__func__);
-	subsys_set_crash_status(d->subsys, true);
+	subsys_set_crash_status(d->subsys, CRASH_STATUS_WDOG_BITE);
 	log_failure_reason(d);
 	subsystem_restart_dev(d->subsys);
 
@@ -917,7 +917,7 @@ static irqreturn_t subsys_stop_ack_intr_handler(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
-static void check_pbl_done(struct pil_tz_data *d)
+static void clear_pbl_done(struct pil_tz_data *d)
 {
 	uint32_t err_value;
 
@@ -944,20 +944,21 @@ static void check_pbl_done(struct pil_tz_data *d)
 	__raw_writel(BIT(d->bits_arr[PBL_DONE]), d->irq_clear);
 }
 
-static void check_err_ready(struct pil_tz_data *d)
+static void clear_err_ready(struct pil_tz_data *d)
 {
-	uint32_t err_value;
-
-	err_value =  __raw_readl(d->err_status_spare);
-	if (!err_value) {
-		pr_debug("Subsystem error services up received from %s!\n",
+	pr_debug("Subsystem error services up received from %s\n",
 							d->subsys_desc.name);
-		__raw_writel(BIT(d->bits_arr[ERR_READY]), d->irq_clear);
-		complete_err_ready(d->subsys);
-	} else if (err_value == 0x44554d50) {
+	__raw_writel(BIT(d->bits_arr[ERR_READY]), d->irq_clear);
+	complete_err_ready(d->subsys);
+}
+
+static void clear_wdog(struct pil_tz_data *d)
+{
+	/* Check crash status to know if device is restarting*/
+	if (!subsys_get_crash_status(d->subsys)) {
 		pr_err("wdog bite received from %s!\n", d->subsys_desc.name);
 		__raw_writel(BIT(d->bits_arr[ERR_READY]), d->irq_clear);
-		subsys_set_crash_status(d->subsys, true);
+		subsys_set_crash_status(d->subsys, CRASH_STATUS_WDOG_BITE);
 		log_failure_reason(d);
 		subsystem_restart_dev(d->subsys);
 	}
@@ -966,17 +967,21 @@ static void check_err_ready(struct pil_tz_data *d)
 static irqreturn_t subsys_generic_handler(int irq, void *dev_id)
 {
 	struct pil_tz_data *d = subsys_to_data(dev_id);
-	uint32_t status_val;
+	uint32_t status_val, err_value;
 
-	if (subsys_get_crash_status(d->subsys))
-		return IRQ_HANDLED;
-
+	err_value =  __raw_readl(d->err_status_spare);
 	status_val = __raw_readl(d->irq_status);
 
-	if (status_val & BIT(d->bits_arr[ERR_READY]))
-		check_err_ready(d);
-	else if (status_val & BIT(d->bits_arr[PBL_DONE]))
-		check_pbl_done(d);
+	if ((status_val & BIT(d->bits_arr[ERR_READY])) && !err_value)
+		clear_err_ready(d);
+
+	if ((status_val & BIT(d->bits_arr[ERR_READY])) &&
+					err_value == 0x44554d50)
+		clear_wdog(d);
+
+	if (status_val & BIT(d->bits_arr[PBL_DONE]))
+		clear_pbl_done(d);
+
 	return IRQ_HANDLED;
 }
 
diff --git a/drivers/soc/qcom/subsystem_notif.c b/drivers/soc/qcom/subsystem_notif.c
index f099dd5..d237a31 100644
--- a/drivers/soc/qcom/subsystem_notif.c
+++ b/drivers/soc/qcom/subsystem_notif.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2011, 2013, 2016 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2011, 2013, 2016-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -195,7 +195,7 @@ static int subsys_notifier_test_call(struct notifier_block *this,
 	switch (code) {
 
 	default:
-		printk(KERN_WARNING "%s: Notification %s from subsystem %p\n",
+		pr_warn("%s: Notification %s from subsystem %pK\n",
 			__func__, notif_to_string(code), data);
 	break;
 
@@ -212,7 +212,7 @@ static void subsys_notif_reg_test_notifier(const char *subsys_name)
 {
 	void *handle = subsys_notif_register_notifier(subsys_name, &nb);
 
-	printk(KERN_WARNING "%s: Registered test notifier, handle=%p",
+	pr_warn("%s: Registered test notifier, handle=%pK",
 			__func__, handle);
 }
 #endif
diff --git a/drivers/soc/qcom/subsystem_restart.c b/drivers/soc/qcom/subsystem_restart.c
index cb91789..c846d26 100644
--- a/drivers/soc/qcom/subsystem_restart.c
+++ b/drivers/soc/qcom/subsystem_restart.c
@@ -178,7 +178,7 @@ struct subsys_device {
 	struct cdev char_dev;
 	dev_t dev_no;
 	struct completion err_ready;
-	bool crashed;
+	enum crash_status crashed;
 	int notif_state;
 	struct list_head list;
 };
@@ -601,10 +601,11 @@ static void subsystem_shutdown(struct subsys_device *dev, void *data)
 {
 	const char *name = dev->desc->name;
 
-	pr_info("[%p]: Shutting down %s\n", current, name);
+	pr_info("[%s:%d]: Shutting down %s\n",
+			current->comm, current->pid, name);
 	if (dev->desc->shutdown(dev->desc, true) < 0)
-		panic("subsys-restart: [%p]: Failed to shutdown %s!",
-			current, name);
+		panic("subsys-restart: [%s:%d]: Failed to shutdown %s!",
+			current->comm, current->pid, name);
 	dev->crash_count++;
 	subsys_set_state(dev, SUBSYS_OFFLINE);
 	disable_all_irqs(dev);
@@ -616,7 +617,8 @@ static void subsystem_ramdump(struct subsys_device *dev, void *data)
 
 	if (dev->desc->ramdump)
 		if (dev->desc->ramdump(is_ramdump_enabled(dev), dev->desc) < 0)
-			pr_warn("%s[%p]: Ramdump failed.\n", name, current);
+			pr_warn("%s[%s:%d]: Ramdump failed.\n",
+				name, current->comm, current->pid);
 	dev->do_ramdump_on_put = false;
 }
 
@@ -631,13 +633,14 @@ static void subsystem_powerup(struct subsys_device *dev, void *data)
 	const char *name = dev->desc->name;
 	int ret;
 
-	pr_info("[%p]: Powering up %s\n", current, name);
+	pr_info("[%s:%d]: Powering up %s\n", current->comm, current->pid, name);
 	init_completion(&dev->err_ready);
 
 	if (dev->desc->powerup(dev->desc) < 0) {
 		notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
 								NULL);
-		panic("[%p]: Powerup error: %s!", current, name);
+		panic("[%s:%d]: Powerup error: %s!",
+			current->comm, current->pid, name);
 	}
 	enable_all_irqs(dev);
 
@@ -645,11 +648,11 @@ static void subsystem_powerup(struct subsys_device *dev, void *data)
 	if (ret) {
 		notify_each_subsys_device(&dev, 1, SUBSYS_POWERUP_FAILURE,
 								NULL);
-		panic("[%p]: Timed out waiting for error ready: %s!",
-			current, name);
+		panic("[%s:%d]: Timed out waiting for error ready: %s!",
+			current->comm, current->pid, name);
 	}
 	subsys_set_state(dev, SUBSYS_ONLINE);
-	subsys_set_crash_status(dev, false);
+	subsys_set_crash_status(dev, CRASH_STATUS_NO_CRASH);
 }
 
 static int __find_subsys(struct device *dev, void *data)
@@ -955,8 +958,8 @@ static void subsystem_restart_wq_func(struct work_struct *work)
 	 */
 	mutex_lock(&soc_order_reg_lock);
 
-	pr_debug("[%p]: Starting restart sequence for %s\n", current,
-			desc->name);
+	pr_debug("[%s:%d]: Starting restart sequence for %s\n",
+			current->comm, current->pid, desc->name);
 	notify_each_subsys_device(list, count, SUBSYS_BEFORE_SHUTDOWN, NULL);
 	for_each_subsys_device(list, count, NULL, subsystem_shutdown);
 	notify_each_subsys_device(list, count, SUBSYS_AFTER_SHUTDOWN, NULL);
@@ -977,8 +980,8 @@ static void subsystem_restart_wq_func(struct work_struct *work)
 	for_each_subsys_device(list, count, NULL, subsystem_powerup);
 	notify_each_subsys_device(list, count, SUBSYS_AFTER_POWERUP, NULL);
 
-	pr_info("[%p]: Restart sequence for %s completed.\n",
-			current, desc->name);
+	pr_info("[%s:%d]: Restart sequence for %s completed.\n",
+			current->comm, current->pid, desc->name);
 
 	mutex_unlock(&soc_order_reg_lock);
 	mutex_unlock(&track->lock);
@@ -1063,8 +1066,9 @@ int subsystem_restart_dev(struct subsys_device *dev)
 	pr_info("Restart sequence requested for %s, restart_level = %s.\n",
 		name, restart_levels[dev->restart_level]);
 
-	if (WARN(disable_restart_work == DISABLE_SSR,
-		"subsys-restart: Ignoring restart request for %s.\n", name)) {
+	if (disable_restart_work == DISABLE_SSR) {
+		pr_warn("subsys-restart: Ignoring restart request for %s\n",
+									name);
 		return 0;
 	}
 
@@ -1129,12 +1133,13 @@ int subsystem_crashed(const char *name)
 }
 EXPORT_SYMBOL(subsystem_crashed);
 
-void subsys_set_crash_status(struct subsys_device *dev, bool crashed)
+void subsys_set_crash_status(struct subsys_device *dev,
+				enum crash_status crashed)
 {
 	dev->crashed = crashed;
 }
 
-bool subsys_get_crash_status(struct subsys_device *dev)
+enum crash_status subsys_get_crash_status(struct subsys_device *dev)
 {
 	return dev->crashed;
 }
diff --git a/drivers/soc/qcom/watchdog_v2.c b/drivers/soc/qcom/watchdog_v2.c
index d58bfa1..f3d6209 100644
--- a/drivers/soc/qcom/watchdog_v2.c
+++ b/drivers/soc/qcom/watchdog_v2.c
@@ -371,7 +371,7 @@ static void ping_other_cpus(struct msm_watchdog_data *wdog_dd)
 	/* Make sure alive mask is cleared and set in order */
 	smp_mb();
 	for_each_cpu(cpu, cpu_online_mask) {
-		if (!cpu_idle_pc_state[cpu])
+		if (!cpu_idle_pc_state[cpu] && !cpu_isolated(cpu))
 			smp_call_function_single(cpu, keep_alive_response,
 						 wdog_dd, 1);
 	}
diff --git a/drivers/soc/qcom/wcd-dsp-glink.c b/drivers/soc/qcom/wcd-dsp-glink.c
new file mode 100644
index 0000000..758f627
--- /dev/null
+++ b/drivers/soc/qcom/wcd-dsp-glink.c
@@ -0,0 +1,1101 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/cdev.h>
+#include <linux/platform_device.h>
+#include <soc/qcom/glink.h>
+#include "sound/wcd-dsp-glink.h"
+
+#define WDSP_GLINK_DRIVER_NAME "wcd-dsp-glink"
+#define WDSP_MAX_WRITE_SIZE (512 * 1024)
+#define WDSP_MAX_READ_SIZE (4 * 1024)
+
+#define MINOR_NUMBER_COUNT 1
+#define WDSP_EDGE "wdsp"
+#define RESP_QUEUE_SIZE 3
+#define QOS_PKT_SIZE 1024
+#define TIMEOUT_MS 1000
+
+struct wdsp_glink_dev {
+	struct class *cls;
+	struct device *dev;
+	struct cdev cdev;
+	dev_t dev_num;
+};
+
+struct wdsp_glink_rsp_que {
+	/* Size of valid data in buffer */
+	u32 buf_size;
+
+	/* Response buffer */
+	u8 buf[WDSP_MAX_READ_SIZE];
+};
+
+struct wdsp_glink_tx_buf {
+	struct work_struct tx_work;
+
+	/* Glink channel information */
+	struct wdsp_glink_ch *ch;
+
+	/* Tx buffer to send to glink */
+	u8 buf[0];
+};
+
+struct wdsp_glink_ch {
+	struct wdsp_glink_priv *wpriv;
+
+	/* Glink channel handle */
+	void *handle;
+
+	/* Channel states like connect, disconnect */
+	int channel_state;
+	struct mutex mutex;
+
+	/* To free up the channel memory */
+	bool free_mem;
+
+	/* Glink local channel open work */
+	struct work_struct lcl_ch_open_wrk;
+
+	/* Glink local channel close work */
+	struct work_struct lcl_ch_cls_wrk;
+
+	/* Wait for ch connect state before sending any command */
+	wait_queue_head_t ch_connect_wait;
+
+	/*
+	 * Glink channel configuration. This has to be the last
+	 * member of the strucuture as it has variable size
+	 */
+	struct wdsp_glink_ch_cfg ch_cfg;
+};
+
+struct wdsp_glink_state {
+	/* Glink link state information */
+	enum glink_link_state link_state;
+	void *handle;
+};
+
+struct wdsp_glink_priv {
+	/* Respone buffer related */
+	u8 rsp_cnt;
+	struct wdsp_glink_rsp_que rsp[RESP_QUEUE_SIZE];
+	struct completion rsp_complete;
+	struct mutex rsp_mutex;
+
+	/* Glink channel related */
+	struct mutex glink_mutex;
+	struct wdsp_glink_state glink_state;
+	struct wdsp_glink_ch **ch;
+	u8 no_of_channels;
+	struct work_struct ch_open_cls_wrk;
+	struct workqueue_struct *work_queue;
+
+	wait_queue_head_t link_state_wait;
+
+	struct device *dev;
+};
+
+static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch);
+static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch);
+
+/*
+ * wdsp_glink_notify_rx - Glink notify rx callback for responses
+ * handle:      Opaque Channel handle returned by GLink
+ * priv:        Private pointer to the channel
+ * pkt_priv:    Private pointer to the packet
+ * ptr:         Pointer to the Rx data
+ * size:        Size of the Rx data
+ */
+static void wdsp_glink_notify_rx(void *handle, const void *priv,
+				 const void *pkt_priv, const void *ptr,
+				 size_t size)
+{
+	u8 *rx_buf;
+	u8 rsp_cnt;
+	struct wdsp_glink_ch *ch;
+	struct wdsp_glink_priv *wpriv;
+
+	if (!ptr || !priv) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return;
+	}
+
+	ch = (struct wdsp_glink_ch *)priv;
+	wpriv = ch->wpriv;
+	rx_buf = (u8 *)ptr;
+	if (size > WDSP_MAX_READ_SIZE) {
+		dev_err(wpriv->dev, "%s: Size %zd is greater than allowed %d\n",
+			__func__, size, WDSP_MAX_READ_SIZE);
+		size = WDSP_MAX_READ_SIZE;
+	}
+
+	mutex_lock(&wpriv->rsp_mutex);
+	rsp_cnt = wpriv->rsp_cnt;
+	if (rsp_cnt >= RESP_QUEUE_SIZE) {
+		dev_err(wpriv->dev, "%s: Resp Queue is Full\n", __func__);
+		rsp_cnt = 0;
+	}
+	dev_dbg(wpriv->dev, "%s: copy into buffer %d\n", __func__, rsp_cnt);
+
+	memcpy(wpriv->rsp[rsp_cnt].buf, rx_buf, size);
+	wpriv->rsp[rsp_cnt].buf_size = size;
+	wpriv->rsp_cnt = ++rsp_cnt;
+	mutex_unlock(&wpriv->rsp_mutex);
+
+	glink_rx_done(handle, ptr, true);
+	complete(&wpriv->rsp_complete);
+}
+
+/*
+ * wdsp_glink_notify_tx_done - Glink notify tx done callback to
+ * free tx buffer
+ * handle:      Opaque Channel handle returned by GLink
+ * priv:        Private pointer to the channel
+ * pkt_priv:    Private pointer to the packet
+ * ptr:         Pointer to the Tx data
+ */
+static void wdsp_glink_notify_tx_done(void *handle, const void *priv,
+				      const void *pkt_priv, const void *ptr)
+{
+	if (!pkt_priv) {
+		pr_err("%s: Invalid parameter\n", __func__);
+		return;
+	}
+	/* Free tx pkt */
+	kfree(pkt_priv);
+}
+
+/*
+ * wdsp_glink_notify_tx_abort - Glink notify tx abort callback to
+ * free tx buffer
+ * handle:      Opaque Channel handle returned by GLink
+ * priv:        Private pointer to the channel
+ * pkt_priv:    Private pointer to the packet
+ */
+static void wdsp_glink_notify_tx_abort(void *handle, const void *priv,
+				       const void *pkt_priv)
+{
+	if (!pkt_priv) {
+		pr_err("%s: Invalid parameter\n", __func__);
+		return;
+	}
+	/* Free tx pkt */
+	kfree(pkt_priv);
+}
+
+/*
+ * wdsp_glink_notify_rx_intent_req - Glink notify rx intent request callback
+ * to queue buffer to receive from remote client
+ * handle:      Opaque channel handle returned by GLink
+ * priv:        Private pointer to the channel
+ * req_size:    Size of intent to be queued
+ */
+static bool wdsp_glink_notify_rx_intent_req(void *handle, const void *priv,
+					    size_t req_size)
+{
+	struct wdsp_glink_priv *wpriv;
+	struct wdsp_glink_ch *ch;
+	int rc = 0;
+	bool ret = false;
+
+	if (!priv) {
+		pr_err("%s: Invalid priv\n", __func__);
+		goto done;
+	}
+	if (req_size > WDSP_MAX_READ_SIZE) {
+		pr_err("%s: Invalid req_size %zd\n", __func__, req_size);
+		goto done;
+	}
+
+	ch = (struct wdsp_glink_ch *)priv;
+	wpriv = ch->wpriv;
+
+	dev_dbg(wpriv->dev, "%s: intent size %zd requested for ch name %s",
+		 __func__, req_size, ch->ch_cfg.name);
+
+	mutex_lock(&ch->mutex);
+	rc = glink_queue_rx_intent(ch->handle, ch, req_size);
+	if (rc < 0) {
+		dev_err(wpriv->dev, "%s: Failed to queue rx intent, rc = %d\n",
+			__func__, rc);
+		mutex_unlock(&ch->mutex);
+		goto done;
+	}
+	mutex_unlock(&ch->mutex);
+	ret = true;
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_lcl_ch_open_wrk - Work function to open channel again
+ * when local disconnect event happens
+ * work:      Work structure
+ */
+static void wdsp_glink_lcl_ch_open_wrk(struct work_struct *work)
+{
+	struct wdsp_glink_ch *ch;
+
+	ch = container_of(work, struct wdsp_glink_ch,
+			  lcl_ch_open_wrk);
+
+	wdsp_glink_open_ch(ch);
+}
+
+/*
+ * wdsp_glink_lcl_ch_cls_wrk - Work function to close channel locally
+ * when remote disconnect event happens
+ * work:      Work structure
+ */
+static void wdsp_glink_lcl_ch_cls_wrk(struct work_struct *work)
+{
+	struct wdsp_glink_ch *ch;
+
+	ch = container_of(work, struct wdsp_glink_ch,
+			  lcl_ch_cls_wrk);
+
+	wdsp_glink_close_ch(ch);
+}
+
+/*
+ * wdsp_glink_notify_state - Glink channel state information event callback
+ * handle:      Opaque Channel handle returned by GLink
+ * priv:        Private pointer to the channel
+ * event:       channel state event
+ */
+static void wdsp_glink_notify_state(void *handle, const void *priv,
+				    unsigned int event)
+{
+	struct wdsp_glink_priv *wpriv;
+	struct wdsp_glink_ch *ch;
+	int i, ret = 0;
+
+	if (!priv) {
+		pr_err("%s: Invalid priv\n", __func__);
+		return;
+	}
+
+	ch = (struct wdsp_glink_ch *)priv;
+	wpriv = ch->wpriv;
+
+	mutex_lock(&ch->mutex);
+	ch->channel_state = event;
+	if (event == GLINK_CONNECTED) {
+		dev_dbg(wpriv->dev, "%s: glink channel: %s connected\n",
+			__func__, ch->ch_cfg.name);
+
+		for (i = 0; i < ch->ch_cfg.no_of_intents; i++) {
+			dev_dbg(wpriv->dev, "%s: intent_size = %d\n", __func__,
+				ch->ch_cfg.intents_size[i]);
+			ret = glink_queue_rx_intent(ch->handle, ch,
+						    ch->ch_cfg.intents_size[i]);
+			if (ret < 0)
+				dev_warn(wpriv->dev, "%s: Failed to queue intent %d of size %d\n",
+					 __func__, i,
+					 ch->ch_cfg.intents_size[i]);
+		}
+
+		ret = glink_qos_latency(ch->handle, ch->ch_cfg.latency_in_us,
+					QOS_PKT_SIZE);
+		if (ret < 0)
+			dev_warn(wpriv->dev, "%s: Failed to request qos %d for ch %s\n",
+				__func__, ch->ch_cfg.latency_in_us,
+				ch->ch_cfg.name);
+
+		wake_up(&ch->ch_connect_wait);
+		mutex_unlock(&ch->mutex);
+	} else if (event == GLINK_LOCAL_DISCONNECTED) {
+		/*
+		 * Don't use dev_dbg here as dev may not be valid if channel
+		 * closed from driver close.
+		 */
+		pr_debug("%s: channel: %s disconnected locally\n",
+			 __func__, ch->ch_cfg.name);
+		mutex_unlock(&ch->mutex);
+
+		if (ch->free_mem) {
+			kfree(ch);
+			ch = NULL;
+		}
+	} else if (event == GLINK_REMOTE_DISCONNECTED) {
+		dev_dbg(wpriv->dev, "%s: remote channel: %s disconnected remotely\n",
+			 __func__, ch->ch_cfg.name);
+		mutex_unlock(&ch->mutex);
+		/*
+		 * If remote disconnect happens, local side also has
+		 * to close the channel as per glink design in a
+		 * separate work_queue.
+		 */
+		queue_work(wpriv->work_queue, &ch->lcl_ch_cls_wrk);
+	}
+}
+
+/*
+ * wdsp_glink_close_ch - Internal function to close glink channel
+ * ch:       Glink Channel structure.
+ */
+static int wdsp_glink_close_ch(struct wdsp_glink_ch *ch)
+{
+	struct wdsp_glink_priv *wpriv = ch->wpriv;
+	int ret = 0;
+
+	mutex_lock(&wpriv->glink_mutex);
+	if (ch->handle) {
+		ret = glink_close(ch->handle);
+		if (ret < 0) {
+			dev_err(wpriv->dev, "%s: glink_close is failed, ret = %d\n",
+				 __func__, ret);
+		} else {
+			ch->handle = NULL;
+			dev_dbg(wpriv->dev, "%s: ch %s is closed\n", __func__,
+				ch->ch_cfg.name);
+		}
+	} else {
+		dev_dbg(wpriv->dev, "%s: ch %s is already closed\n", __func__,
+			ch->ch_cfg.name);
+	}
+	mutex_unlock(&wpriv->glink_mutex);
+
+
+	return ret;
+}
+
+/*
+ * wdsp_glink_open_ch - Internal function to open glink channel
+ * ch:       Glink Channel structure.
+ */
+static int wdsp_glink_open_ch(struct wdsp_glink_ch *ch)
+{
+	struct wdsp_glink_priv *wpriv = ch->wpriv;
+	struct glink_open_config open_cfg;
+	int ret = 0;
+
+	mutex_lock(&wpriv->glink_mutex);
+	if (!ch->handle) {
+		memset(&open_cfg, 0, sizeof(open_cfg));
+		open_cfg.options = GLINK_OPT_INITIAL_XPORT;
+		open_cfg.edge = WDSP_EDGE;
+		open_cfg.notify_rx = wdsp_glink_notify_rx;
+		open_cfg.notify_tx_done = wdsp_glink_notify_tx_done;
+		open_cfg.notify_tx_abort = wdsp_glink_notify_tx_abort;
+		open_cfg.notify_state = wdsp_glink_notify_state;
+		open_cfg.notify_rx_intent_req = wdsp_glink_notify_rx_intent_req;
+		open_cfg.priv = ch;
+		open_cfg.name = ch->ch_cfg.name;
+
+		dev_dbg(wpriv->dev, "%s: ch->ch_cfg.name = %s, latency_in_us = %d, intents = %d\n",
+			__func__, ch->ch_cfg.name, ch->ch_cfg.latency_in_us,
+			ch->ch_cfg.no_of_intents);
+
+		ch->handle = glink_open(&open_cfg);
+		if (IS_ERR_OR_NULL(ch->handle)) {
+			dev_err(wpriv->dev, "%s: glink_open failed for ch %s\n",
+				__func__, ch->ch_cfg.name);
+			ch->handle = NULL;
+			ret = -EINVAL;
+		}
+	} else {
+		dev_err(wpriv->dev, "%s: ch %s is already opened\n", __func__,
+			ch->ch_cfg.name);
+	}
+	mutex_unlock(&wpriv->glink_mutex);
+
+	return ret;
+}
+
+/*
+ * wdsp_glink_close_all_ch - Internal function to close all glink channels
+ * wpriv:       Wdsp_glink private structure
+ */
+static void wdsp_glink_close_all_ch(struct wdsp_glink_priv *wpriv)
+{
+	int i;
+
+	for (i = 0; i < wpriv->no_of_channels; i++)
+		if (wpriv->ch && wpriv->ch[i])
+			wdsp_glink_close_ch(wpriv->ch[i]);
+}
+
+/*
+ * wdsp_glink_open_all_ch - Internal function to open all glink channels
+ * wpriv:       Wdsp_glink private structure
+ */
+static int wdsp_glink_open_all_ch(struct wdsp_glink_priv *wpriv)
+{
+	int ret = 0, i, j;
+
+	for (i = 0; i < wpriv->no_of_channels; i++) {
+		if (wpriv->ch && wpriv->ch[i]) {
+			ret = wdsp_glink_open_ch(wpriv->ch[i]);
+			if (ret < 0)
+				goto err_open;
+		}
+	}
+	goto done;
+
+err_open:
+	for (j = 0; j < i; j++)
+		if (wpriv->ch[i])
+			wdsp_glink_close_ch(wpriv->ch[j]);
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_ch_open_wq - Work function to open glink channels
+ * work:      Work structure
+ */
+static void wdsp_glink_ch_open_cls_wrk(struct work_struct *work)
+{
+	struct wdsp_glink_priv *wpriv;
+
+	wpriv = container_of(work, struct wdsp_glink_priv,
+			     ch_open_cls_wrk);
+
+	if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) {
+		dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_DOWN\n",
+			 __func__);
+
+		wdsp_glink_close_all_ch(wpriv);
+	} else if (wpriv->glink_state.link_state == GLINK_LINK_STATE_UP) {
+		dev_info(wpriv->dev, "%s: GLINK_LINK_STATE_UP\n",
+			 __func__);
+
+		wdsp_glink_open_all_ch(wpriv);
+	}
+}
+
+/*
+ * wdsp_glink_link_state_cb - Glink link state callback to inform
+ * about link states
+ * cb_info:     Glink link state callback information structure
+ * priv:        Private structure of link state passed while register
+ */
+static void wdsp_glink_link_state_cb(struct glink_link_state_cb_info *cb_info,
+				     void *priv)
+{
+	struct wdsp_glink_priv *wpriv;
+
+	if (!cb_info || !priv) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return;
+	}
+
+	wpriv = (struct wdsp_glink_priv *)priv;
+
+	mutex_lock(&wpriv->glink_mutex);
+	wpriv->glink_state.link_state = cb_info->link_state;
+	wake_up(&wpriv->link_state_wait);
+	mutex_unlock(&wpriv->glink_mutex);
+
+	queue_work(wpriv->work_queue, &wpriv->ch_open_cls_wrk);
+}
+
+/*
+ * wdsp_glink_ch_info_init- Internal function to allocate channel memory
+ * and register with glink
+ * wpriv:     Wdsp_glink private structure.
+ * pkt:       Glink registration packet contains glink channel information.
+ */
+static int wdsp_glink_ch_info_init(struct wdsp_glink_priv *wpriv,
+				   struct wdsp_reg_pkt *pkt)
+{
+	int ret = 0, i, j;
+	struct glink_link_info link_info;
+	struct wdsp_glink_ch_cfg *ch_cfg;
+	struct wdsp_glink_ch **ch;
+	u8 no_of_channels;
+	u8 *payload;
+	u32 ch_size, ch_cfg_size;
+
+	payload = (u8 *)pkt->payload;
+	no_of_channels = pkt->no_of_channels;
+
+	ch = kcalloc(no_of_channels, sizeof(struct wdsp_glink_ch *),
+		     GFP_KERNEL);
+	if (!ch) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	for (i = 0; i < no_of_channels; i++) {
+		ch_cfg = (struct wdsp_glink_ch_cfg *)payload;
+		ch_cfg_size = sizeof(struct wdsp_glink_ch_cfg) +
+					(sizeof(u32) * ch_cfg->no_of_intents);
+		ch_size = sizeof(struct wdsp_glink_ch) +
+					(sizeof(u32) * ch_cfg->no_of_intents);
+
+		dev_dbg(wpriv->dev, "%s: channels = %d, ch_cfg_size %d",
+			 __func__, no_of_channels, ch_cfg_size);
+
+		ch[i] = kzalloc(ch_size, GFP_KERNEL);
+		if (!ch[i]) {
+			ret = -ENOMEM;
+			goto err_ch_mem;
+		}
+		ch[i]->channel_state = GLINK_LOCAL_DISCONNECTED;
+		memcpy(&ch[i]->ch_cfg, payload, ch_cfg_size);
+		payload += ch_cfg_size;
+
+		mutex_init(&ch[i]->mutex);
+		ch[i]->wpriv = wpriv;
+		INIT_WORK(&ch[i]->lcl_ch_open_wrk, wdsp_glink_lcl_ch_open_wrk);
+		INIT_WORK(&ch[i]->lcl_ch_cls_wrk, wdsp_glink_lcl_ch_cls_wrk);
+		init_waitqueue_head(&ch[i]->ch_connect_wait);
+	}
+	wpriv->ch = ch;
+	wpriv->no_of_channels = no_of_channels;
+
+	INIT_WORK(&wpriv->ch_open_cls_wrk, wdsp_glink_ch_open_cls_wrk);
+
+	/* Register glink link_state notification */
+	link_info.glink_link_state_notif_cb = wdsp_glink_link_state_cb;
+	link_info.transport = NULL;
+	link_info.edge = WDSP_EDGE;
+
+	wpriv->glink_state.link_state = GLINK_LINK_STATE_DOWN;
+	wpriv->glink_state.handle = glink_register_link_state_cb(&link_info,
+								 wpriv);
+	if (!wpriv->glink_state.handle) {
+		dev_err(wpriv->dev, "%s: Unable to register wdsp link state\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_ch_mem;
+	}
+	goto done;
+
+err_ch_mem:
+	for (j = 0; j < i; j++) {
+		mutex_destroy(&ch[j]->mutex);
+		kfree(wpriv->ch[j]);
+		wpriv->ch[j] = NULL;
+	}
+	kfree(wpriv->ch);
+	wpriv->ch = NULL;
+	wpriv->no_of_channels = 0;
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_tx_buf_work - Work queue function to send tx buffer to glink
+ * work:     Work structure
+ */
+static void wdsp_glink_tx_buf_work(struct work_struct *work)
+{
+	struct wdsp_glink_priv *wpriv;
+	struct wdsp_glink_ch *ch;
+	struct wdsp_glink_tx_buf *tx_buf;
+	struct wdsp_write_pkt *wpkt;
+	struct wdsp_cmd_pkt *cpkt;
+	int ret = 0;
+
+	tx_buf = container_of(work, struct wdsp_glink_tx_buf,
+			      tx_work);
+	ch = tx_buf->ch;
+	wpriv = ch->wpriv;
+	wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
+	cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
+	dev_dbg(wpriv->dev, "%s: ch name = %s, payload size = %d\n",
+		__func__, cpkt->ch_name, cpkt->payload_size);
+
+	mutex_lock(&tx_buf->ch->mutex);
+	if (ch->channel_state == GLINK_CONNECTED) {
+		mutex_unlock(&tx_buf->ch->mutex);
+		ret = glink_tx(ch->handle, tx_buf,
+			       cpkt->payload, cpkt->payload_size,
+			       GLINK_TX_REQ_INTENT);
+		if (ret < 0) {
+			dev_err(wpriv->dev, "%s: glink tx failed, ret = %d\n",
+				__func__, ret);
+			/*
+			 * If glink_tx() is failed then free tx_buf here as
+			 * there won't be any tx_done notification to
+			 * free the buffer.
+			 */
+			kfree(tx_buf);
+		}
+	} else {
+		mutex_unlock(&tx_buf->ch->mutex);
+		dev_err(wpriv->dev, "%s: channel %s is not in connected state\n",
+			__func__, ch->ch_cfg.name);
+		/*
+		 * Free tx_buf here as there won't be any tx_done
+		 * notification in this case also.
+		 */
+		kfree(tx_buf);
+	}
+}
+
+/*
+ * wdsp_glink_read - Read API to send the data to userspace
+ * file:    Pointer to the file structure
+ * buf:     Pointer to the userspace buffer
+ * count:   Number bytes to read from the file
+ * ppos:    Pointer to the position into the file
+ */
+static ssize_t wdsp_glink_read(struct file *file, char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	int ret = 0, ret1 = 0;
+	struct wdsp_glink_rsp_que *rsp;
+	struct wdsp_glink_priv *wpriv;
+
+	wpriv = (struct wdsp_glink_priv *)file->private_data;
+	if (!wpriv) {
+		pr_err("%s: Invalid private data\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (count > WDSP_MAX_READ_SIZE) {
+		dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_READ_SIZE\n",
+			__func__, count);
+		count = WDSP_MAX_READ_SIZE;
+	}
+	/*
+	 * Complete signal has given from glink rx notification callback
+	 * or from flush API. Also use interruptible wait_for_completion API
+	 * to allow the system to go in suspend.
+	 */
+	ret = wait_for_completion_interruptible(&wpriv->rsp_complete);
+	if (ret)
+		goto done;
+
+	mutex_lock(&wpriv->rsp_mutex);
+	if (wpriv->rsp_cnt) {
+		wpriv->rsp_cnt--;
+		dev_dbg(wpriv->dev, "%s: read from buffer %d\n",
+			__func__, wpriv->rsp_cnt);
+
+		rsp = &wpriv->rsp[wpriv->rsp_cnt];
+		if (count < rsp->buf_size) {
+			ret1 = copy_to_user(buf, &rsp->buf, count);
+			/* Return the number of bytes copied */
+			ret = count;
+		} else {
+			ret1 = copy_to_user(buf, &rsp->buf, rsp->buf_size);
+			/* Return the number of bytes copied */
+			ret = rsp->buf_size;
+		}
+
+		if (ret1) {
+			mutex_unlock(&wpriv->rsp_mutex);
+			dev_err(wpriv->dev, "%s: copy_to_user failed %d\n",
+				__func__, ret);
+			ret = -EFAULT;
+			goto done;
+		}
+	} else {
+		/*
+		 * This will execute only if flush API is called or
+		 * something wrong with ref_cnt
+		 */
+		dev_dbg(wpriv->dev, "%s: resp count = %d\n", __func__,
+			wpriv->rsp_cnt);
+		ret = -EINVAL;
+	}
+	mutex_unlock(&wpriv->rsp_mutex);
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_write - Write API to receive the data from userspace
+ * file:    Pointer to the file structure
+ * buf:     Pointer to the userspace buffer
+ * count:   Number bytes to read from the file
+ * ppos:    Pointer to the position into the file
+ */
+static ssize_t wdsp_glink_write(struct file *file, const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	int ret = 0, i, tx_buf_size;
+	struct wdsp_write_pkt *wpkt;
+	struct wdsp_cmd_pkt *cpkt;
+	struct wdsp_glink_tx_buf *tx_buf;
+	struct wdsp_glink_priv *wpriv;
+
+	wpriv = (struct wdsp_glink_priv *)file->private_data;
+	if (!wpriv) {
+		pr_err("%s: Invalid private data\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	dev_dbg(wpriv->dev, "%s: count = %zd\n", __func__, count);
+
+	if (count > WDSP_MAX_WRITE_SIZE) {
+		dev_info(wpriv->dev, "%s: count = %zd is more than WDSP_MAX_WRITE_SIZE\n",
+			__func__, count);
+		count = WDSP_MAX_WRITE_SIZE;
+	}
+
+	tx_buf_size = count + sizeof(struct wdsp_glink_tx_buf);
+	tx_buf = kzalloc(tx_buf_size, GFP_KERNEL);
+	if (!tx_buf) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ret = copy_from_user(tx_buf->buf, buf, count);
+	if (ret) {
+		dev_err(wpriv->dev, "%s: copy_from_user failed %d\n",
+			__func__, ret);
+		ret = -EFAULT;
+		goto free_buf;
+	}
+
+	wpkt = (struct wdsp_write_pkt *)tx_buf->buf;
+	switch (wpkt->pkt_type) {
+	case WDSP_REG_PKT:
+		ret = wdsp_glink_ch_info_init(wpriv,
+					(struct wdsp_reg_pkt *)wpkt->payload);
+		if (ret < 0)
+			dev_err(wpriv->dev, "%s: glink register failed, ret = %d\n",
+				__func__, ret);
+		kfree(tx_buf);
+		break;
+	case WDSP_READY_PKT:
+		ret = wait_event_timeout(wpriv->link_state_wait,
+					 (wpriv->glink_state.link_state ==
+							GLINK_LINK_STATE_UP),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			dev_err(wpriv->dev, "%s: Link state wait timeout\n",
+				__func__);
+			ret = -ETIMEDOUT;
+			goto free_buf;
+		}
+		ret = 0;
+		kfree(tx_buf);
+		break;
+	case WDSP_CMD_PKT:
+		mutex_lock(&wpriv->glink_mutex);
+		if (wpriv->glink_state.link_state == GLINK_LINK_STATE_DOWN) {
+			mutex_unlock(&wpriv->glink_mutex);
+			dev_err(wpriv->dev, "%s: Link state is Down\n",
+				__func__);
+
+			ret = -ENETRESET;
+			goto free_buf;
+		}
+		mutex_unlock(&wpriv->glink_mutex);
+
+		cpkt = (struct wdsp_cmd_pkt *)wpkt->payload;
+		dev_dbg(wpriv->dev, "%s: requested ch_name: %s\n", __func__,
+			 cpkt->ch_name);
+		for (i = 0; i < wpriv->no_of_channels; i++) {
+			if (wpriv->ch && wpriv->ch[i] &&
+				(!strcmp(cpkt->ch_name,
+						wpriv->ch[i]->ch_cfg.name))) {
+				tx_buf->ch = wpriv->ch[i];
+				break;
+			}
+		}
+		if (!tx_buf->ch) {
+			dev_err(wpriv->dev, "%s: Failed to get glink channel\n",
+				__func__);
+			ret = -EINVAL;
+			goto free_buf;
+		}
+
+		ret = wait_event_timeout(tx_buf->ch->ch_connect_wait,
+					 (tx_buf->ch->channel_state ==
+							GLINK_CONNECTED),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			dev_err(wpriv->dev, "%s: glink channel %s is not in connected state %d\n",
+				__func__, tx_buf->ch->ch_cfg.name,
+				tx_buf->ch->channel_state);
+			ret = -ETIMEDOUT;
+			goto free_buf;
+		}
+		ret = 0;
+
+		INIT_WORK(&tx_buf->tx_work, wdsp_glink_tx_buf_work);
+		queue_work(wpriv->work_queue, &tx_buf->tx_work);
+		break;
+	default:
+		dev_err(wpriv->dev, "%s: Invalid packet type\n", __func__);
+		ret = -EINVAL;
+		kfree(tx_buf);
+		break;
+	}
+	goto done;
+
+free_buf:
+	kfree(tx_buf);
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_open - Open API to initialize private data
+ * inode:   Pointer to the inode structure
+ * file:    Pointer to the file structure
+ */
+static int wdsp_glink_open(struct inode *inode, struct file *file)
+{
+	int ret = 0;
+	struct wdsp_glink_priv *wpriv;
+	struct wdsp_glink_dev *wdev;
+
+	if (!inode->i_cdev) {
+		pr_err("%s: cdev is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	wdev = container_of(inode->i_cdev, struct wdsp_glink_dev, cdev);
+
+	wpriv = kzalloc(sizeof(struct wdsp_glink_priv), GFP_KERNEL);
+	if (!wpriv) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	wpriv->dev = wdev->dev;
+	wpriv->work_queue = create_singlethread_workqueue("wdsp_glink_wq");
+	if (!wpriv->work_queue) {
+		dev_err(wpriv->dev, "%s: Error creating wdsp_glink_wq\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_wq;
+	}
+
+	init_completion(&wpriv->rsp_complete);
+	init_waitqueue_head(&wpriv->link_state_wait);
+	mutex_init(&wpriv->rsp_mutex);
+	mutex_init(&wpriv->glink_mutex);
+	file->private_data = wpriv;
+
+	goto done;
+
+err_wq:
+	kfree(wpriv);
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_flush - Flush API to unblock read.
+ * file:    Pointer to the file structure
+ * id:      Lock owner ID
+ */
+static int wdsp_glink_flush(struct file *file, fl_owner_t id)
+{
+	struct wdsp_glink_priv *wpriv;
+
+	wpriv = (struct wdsp_glink_priv *)file->private_data;
+	if (!wpriv) {
+		pr_err("%s: Invalid private data\n", __func__);
+		return -EINVAL;
+	}
+
+	complete(&wpriv->rsp_complete);
+
+	return 0;
+}
+
+/*
+ * wdsp_glink_release - Release API to clean up resources.
+ * Whenever a file structure is shared across multiple threads,
+ * release won't be invoked until all copies are closed
+ * (file->f_count.counter should be 0). If we need to flush pending
+ * data when any copy is closed, you should implement the flush method.
+ *
+ * inode:   Pointer to the inode structure
+ * file:    Pointer to the file structure
+ */
+static int wdsp_glink_release(struct inode *inode, struct file *file)
+{
+	int i, ret = 0;
+	struct wdsp_glink_priv *wpriv;
+
+	wpriv = (struct wdsp_glink_priv *)file->private_data;
+	if (!wpriv) {
+		pr_err("%s: Invalid private data\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (wpriv->glink_state.handle)
+		glink_unregister_link_state_cb(wpriv->glink_state.handle);
+
+	flush_workqueue(wpriv->work_queue);
+	destroy_workqueue(wpriv->work_queue);
+
+	/*
+	 * Clean up glink channel memory in channel state
+	 * callback only if close channels are called from here.
+	 */
+	if (wpriv->ch) {
+		for (i = 0; i < wpriv->no_of_channels; i++) {
+			if (wpriv->ch[i]) {
+				wpriv->ch[i]->free_mem = true;
+				/*
+				 * Channel handle NULL means channel is already
+				 * closed. Free the channel memory here itself.
+				 */
+				if (!wpriv->ch[i]->handle) {
+					kfree(wpriv->ch[i]);
+					wpriv->ch[i] = NULL;
+				} else {
+					wdsp_glink_close_ch(wpriv->ch[i]);
+				}
+			}
+		}
+
+		kfree(wpriv->ch);
+		wpriv->ch = NULL;
+	}
+
+	mutex_destroy(&wpriv->glink_mutex);
+	mutex_destroy(&wpriv->rsp_mutex);
+	kfree(wpriv);
+	file->private_data = NULL;
+
+done:
+	return ret;
+}
+
+static const struct file_operations wdsp_glink_fops = {
+	.owner =                THIS_MODULE,
+	.open =                 wdsp_glink_open,
+	.read =                 wdsp_glink_read,
+	.write =                wdsp_glink_write,
+	.flush =                wdsp_glink_flush,
+	.release =              wdsp_glink_release,
+};
+
+/*
+ * wdsp_glink_probe - Driver probe to expose char device
+ * pdev:    Pointer to device tree data.
+ */
+static int wdsp_glink_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct wdsp_glink_dev *wdev;
+
+	wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
+	if (!wdev) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	ret = alloc_chrdev_region(&wdev->dev_num, 0, MINOR_NUMBER_COUNT,
+				  WDSP_GLINK_DRIVER_NAME);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: Failed to alloc char dev, err = %d\n",
+			__func__, ret);
+		goto err_chrdev;
+	}
+
+	wdev->cls = class_create(THIS_MODULE, WDSP_GLINK_DRIVER_NAME);
+	if (IS_ERR(wdev->cls)) {
+		ret = PTR_ERR(wdev->cls);
+		dev_err(&pdev->dev, "%s: Failed to create class, err = %d\n",
+			__func__, ret);
+		goto err_class;
+	}
+
+	wdev->dev = device_create(wdev->cls, NULL, wdev->dev_num,
+				  NULL, WDSP_GLINK_DRIVER_NAME);
+	if (IS_ERR(wdev->dev)) {
+		ret = PTR_ERR(wdev->dev);
+		dev_err(&pdev->dev, "%s: Failed to create device, err = %d\n",
+			__func__, ret);
+		goto err_dev_create;
+	}
+
+	cdev_init(&wdev->cdev, &wdsp_glink_fops);
+	ret = cdev_add(&wdev->cdev, wdev->dev_num, MINOR_NUMBER_COUNT);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: Failed to register char dev, err = %d\n",
+			__func__, ret);
+		goto err_cdev_add;
+	}
+	platform_set_drvdata(pdev, wdev);
+	goto done;
+
+err_cdev_add:
+	device_destroy(wdev->cls, wdev->dev_num);
+
+err_dev_create:
+	class_destroy(wdev->cls);
+
+err_class:
+	unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
+
+err_chrdev:
+	devm_kfree(&pdev->dev, wdev);
+
+done:
+	return ret;
+}
+
+/*
+ * wdsp_glink_remove - Driver remove to handle cleanup
+ * pdev:     Pointer to device tree data.
+ */
+static int wdsp_glink_remove(struct platform_device *pdev)
+{
+	struct wdsp_glink_dev *wdev = platform_get_drvdata(pdev);
+
+	if (wdev) {
+		cdev_del(&wdev->cdev);
+		device_destroy(wdev->cls, wdev->dev_num);
+		class_destroy(wdev->cls);
+		unregister_chrdev_region(0, MINOR_NUMBER_COUNT);
+		devm_kfree(&pdev->dev, wdev);
+	} else {
+		dev_err(&pdev->dev, "%s: Invalid device data\n", __func__);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id wdsp_glink_of_match[] = {
+	{.compatible = "qcom,wcd-dsp-glink"},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, wdsp_glink_of_match);
+
+static struct platform_driver wdsp_glink_driver = {
+	.probe          = wdsp_glink_probe,
+	.remove         = wdsp_glink_remove,
+	.driver         = {
+		.name   = WDSP_GLINK_DRIVER_NAME,
+		.owner  = THIS_MODULE,
+		.of_match_table = wdsp_glink_of_match,
+	},
+};
+
+module_platform_driver(wdsp_glink_driver);
+
+MODULE_DESCRIPTION("SoC WCD_DSP GLINK Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.c b/drivers/soc/qcom/wlan_firmware_service_v01.c
index 23d18e3..96e7d68 100644
--- a/drivers/soc/qcom/wlan_firmware_service_v01.c
+++ b/drivers/soc/qcom/wlan_firmware_service_v01.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -130,6 +130,23 @@ static struct elem_info wlfw_shadow_reg_cfg_s_v01_ei[] = {
 	},
 };
 
+static struct elem_info wlfw_shadow_reg_v2_cfg_s_v01_ei[] = {
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint32_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0,
+		.offset         = offsetof(struct wlfw_shadow_reg_v2_cfg_s_v01,
+					   addr),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
 static struct elem_info wlfw_memory_region_info_s_v01_ei[] = {
 	{
 		.data_type      = QMI_UNSIGNED_8_BYTE,
@@ -361,6 +378,78 @@ struct elem_info wlfw_ind_register_req_msg_v01_ei[] = {
 					   client_id),
 	},
 	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x16,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   request_mem_enable_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x16,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   request_mem_enable),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x17,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   fw_mem_ready_enable_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x17,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   fw_mem_ready_enable),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x18,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   cold_boot_cal_done_enable_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x18,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   cold_boot_cal_done_enable),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x19,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   rejuvenate_enable_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint32_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x19,
+		.offset         = offsetof(struct wlfw_ind_register_req_msg_v01,
+					   rejuvenate_enable),
+	},
+	{
 		.data_type      = QMI_EOTI,
 		.is_array       = NO_ARRAY,
 		.is_array       = QMI_COMMON_TLV_TYPE,
@@ -647,6 +736,34 @@ struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[] = {
 		.ei_array      = wlfw_shadow_reg_cfg_s_v01_ei,
 	},
 	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x14,
+		.offset         = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+					   shadow_reg_v2_valid),
+	},
+	{
+		.data_type      = QMI_DATA_LEN,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x14,
+		.offset         = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+					   shadow_reg_v2_len),
+	},
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01,
+		.elem_size      = sizeof(struct wlfw_shadow_reg_v2_cfg_s_v01),
+		.is_array       = VAR_LEN_ARRAY,
+		.tlv_type       = 0x14,
+		.offset         = offsetof(struct wlfw_wlan_cfg_req_msg_v01,
+					   shadow_reg_v2),
+		.ei_array      = wlfw_shadow_reg_v2_cfg_s_v01_ei,
+	},
+	{
 		.data_type      = QMI_EOTI,
 		.is_array       = NO_ARRAY,
 		.is_array       = QMI_COMMON_TLV_TYPE,
@@ -978,9 +1095,8 @@ struct elem_info wlfw_initiate_cal_download_ind_msg_v01_ei[] = {
 		.is_array       = NO_ARRAY,
 		.tlv_type       = 0x01,
 		.offset         = offsetof(
-				   struct
-				   wlfw_initiate_cal_download_ind_msg_v01,
-				   cal_id),
+			   struct wlfw_initiate_cal_download_ind_msg_v01,
+			   cal_id),
 	},
 	{
 		.data_type      = QMI_EOTI,
@@ -1657,3 +1773,318 @@ struct elem_info wlfw_mac_addr_resp_msg_v01_ei[] = {
 		.is_array       = QMI_COMMON_TLV_TYPE,
 	},
 };
+
+struct elem_info wlfw_host_cap_req_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct wlfw_host_cap_req_msg_v01,
+					   daemon_support_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct wlfw_host_cap_req_msg_v01,
+					   daemon_support),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_host_cap_resp_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct qmi_response_type_v01),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x02,
+		.offset         = offsetof(struct wlfw_host_cap_resp_msg_v01,
+					   resp),
+		.ei_array      = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_request_mem_ind_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint32_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x01,
+		.offset         = offsetof(struct wlfw_request_mem_ind_msg_v01,
+					   size),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_respond_mem_req_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_UNSIGNED_8_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint64_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x01,
+		.offset         = offsetof(struct wlfw_respond_mem_req_msg_v01,
+					   addr),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_4_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint32_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x02,
+		.offset         = offsetof(struct wlfw_respond_mem_req_msg_v01,
+					   size),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_respond_mem_resp_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct qmi_response_type_v01),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x02,
+		.offset         = offsetof(struct wlfw_respond_mem_resp_msg_v01,
+					   resp),
+		.ei_array      = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   cause_for_rejuvenation_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   cause_for_rejuvenation),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x11,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   requesting_sub_system_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_1_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x11,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   requesting_sub_system),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x12,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   line_number_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_2_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint16_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x12,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   line_number),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x13,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   function_name_valid),
+	},
+	{
+		.data_type      = QMI_STRING,
+		.elem_len       = QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1,
+		.elem_size      = sizeof(char),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x13,
+		.offset         = offsetof(struct wlfw_rejuvenate_ind_msg_v01,
+					   function_name),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct qmi_response_type_v01),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x02,
+		.offset         = offsetof(
+				   struct wlfw_rejuvenate_ack_resp_msg_v01,
+				   resp),
+		.ei_array      = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(struct
+				   wlfw_dynamic_feature_mask_req_msg_v01,
+				   mask_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_8_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint64_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(
+				   struct wlfw_dynamic_feature_mask_req_msg_v01,
+				   mask),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
+
+struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[] = {
+	{
+		.data_type      = QMI_STRUCT,
+		.elem_len       = 1,
+		.elem_size      = sizeof(struct qmi_response_type_v01),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x02,
+		.offset         = offsetof(
+			   struct wlfw_dynamic_feature_mask_resp_msg_v01,
+			   resp),
+		.ei_array      = get_qmi_response_type_v01_ei(),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(
+			   struct wlfw_dynamic_feature_mask_resp_msg_v01,
+			   prev_mask_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_8_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint64_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x10,
+		.offset         = offsetof(
+			   struct wlfw_dynamic_feature_mask_resp_msg_v01,
+			   prev_mask),
+	},
+	{
+		.data_type      = QMI_OPT_FLAG,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint8_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x11,
+		.offset         = offsetof(
+			   struct wlfw_dynamic_feature_mask_resp_msg_v01,
+			   curr_mask_valid),
+	},
+	{
+		.data_type      = QMI_UNSIGNED_8_BYTE,
+		.elem_len       = 1,
+		.elem_size      = sizeof(uint64_t),
+		.is_array       = NO_ARRAY,
+		.tlv_type       = 0x11,
+		.offset         = offsetof(
+			   struct wlfw_dynamic_feature_mask_resp_msg_v01,
+			   curr_mask),
+	},
+	{
+		.data_type      = QMI_EOTI,
+		.is_array       = NO_ARRAY,
+		.is_array       = QMI_COMMON_TLV_TYPE,
+	},
+};
diff --git a/drivers/soc/qcom/wlan_firmware_service_v01.h b/drivers/soc/qcom/wlan_firmware_service_v01.h
index 3c8a267..96c03d6 100644
--- a/drivers/soc/qcom/wlan_firmware_service_v01.h
+++ b/drivers/soc/qcom/wlan_firmware_service_v01.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ /* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -17,7 +17,10 @@
 #define WLFW_SERVICE_VERS_V01 0x01
 
 #define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025
+#define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037
 #define QMI_WLFW_INITIATE_CAL_UPDATE_IND_V01 0x002A
+#define QMI_WLFW_HOST_CAP_REQ_V01 0x0034
+#define QMI_WLFW_DYNAMIC_FEATURE_MASK_RESP_V01 0x003B
 #define QMI_WLFW_CAP_REQ_V01 0x0024
 #define QMI_WLFW_CAL_REPORT_REQ_V01 0x0026
 #define QMI_WLFW_CAL_UPDATE_RESP_V01 0x0029
@@ -26,25 +29,34 @@
 #define QMI_WLFW_CAL_REPORT_RESP_V01 0x0026
 #define QMI_WLFW_MAC_ADDR_RESP_V01 0x0033
 #define QMI_WLFW_INITIATE_CAL_DOWNLOAD_IND_V01 0x0028
+#define QMI_WLFW_HOST_CAP_RESP_V01 0x0034
 #define QMI_WLFW_MSA_READY_IND_V01 0x002B
 #define QMI_WLFW_ATHDIAG_WRITE_RESP_V01 0x0031
 #define QMI_WLFW_WLAN_MODE_REQ_V01 0x0022
 #define QMI_WLFW_IND_REGISTER_REQ_V01 0x0020
 #define QMI_WLFW_WLAN_CFG_RESP_V01 0x0023
+#define QMI_WLFW_COLD_BOOT_CAL_DONE_IND_V01 0x0038
+#define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035
+#define QMI_WLFW_REJUVENATE_IND_V01 0x0039
+#define QMI_WLFW_DYNAMIC_FEATURE_MASK_REQ_V01 0x003B
 #define QMI_WLFW_ATHDIAG_WRITE_REQ_V01 0x0031
 #define QMI_WLFW_WLAN_MODE_RESP_V01 0x0022
+#define QMI_WLFW_RESPOND_MEM_REQ_V01 0x0036
 #define QMI_WLFW_PIN_CONNECT_RESULT_IND_V01 0x002C
 #define QMI_WLFW_FW_READY_IND_V01 0x0021
 #define QMI_WLFW_MSA_READY_RESP_V01 0x002E
 #define QMI_WLFW_CAL_UPDATE_REQ_V01 0x0029
 #define QMI_WLFW_INI_REQ_V01 0x002F
 #define QMI_WLFW_BDF_DOWNLOAD_RESP_V01 0x0025
+#define QMI_WLFW_REJUVENATE_ACK_RESP_V01 0x003A
 #define QMI_WLFW_MSA_INFO_RESP_V01 0x002D
 #define QMI_WLFW_MSA_READY_REQ_V01 0x002E
 #define QMI_WLFW_CAP_RESP_V01 0x0024
+#define QMI_WLFW_REJUVENATE_ACK_REQ_V01 0x003A
 #define QMI_WLFW_ATHDIAG_READ_RESP_V01 0x0030
 #define QMI_WLFW_VBATT_REQ_V01 0x0032
 #define QMI_WLFW_MAC_ADDR_REQ_V01 0x0033
+#define QMI_WLFW_RESPOND_MEM_RESP_V01 0x0036
 #define QMI_WLFW_VBATT_RESP_V01 0x0032
 #define QMI_WLFW_MSA_INFO_REQ_V01 0x002D
 #define QMI_WLFW_CAL_DOWNLOAD_REQ_V01 0x0027
@@ -55,12 +67,14 @@
 #define QMI_WLFW_MAX_NUM_MEMORY_REGIONS_V01 2
 #define QMI_WLFW_MAX_NUM_CAL_V01 5
 #define QMI_WLFW_MAX_DATA_SIZE_V01 6144
+#define QMI_WLFW_FUNCTION_NAME_LEN_V01 128
 #define QMI_WLFW_MAX_NUM_CE_V01 12
 #define QMI_WLFW_MAX_TIMESTAMP_LEN_V01 32
+#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128
 #define QMI_WLFW_MAX_STR_LEN_V01 16
 #define QMI_WLFW_MAX_NUM_SHADOW_REG_V01 24
 #define QMI_WLFW_MAC_ADDR_SIZE_V01 6
-#define QMI_WLFW_MAX_BUILD_ID_LEN_V01 128
+#define QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01 36
 #define QMI_WLFW_MAX_NUM_SVC_V01 24
 
 enum wlfw_driver_mode_enum_v01 {
@@ -72,6 +86,7 @@ enum wlfw_driver_mode_enum_v01 {
 	QMI_WLFW_OFF_V01 = 4,
 	QMI_WLFW_CCPM_V01 = 5,
 	QMI_WLFW_QVIT_V01 = 6,
+	QMI_WLFW_CALIBRATION_V01 = 7,
 	WLFW_DRIVER_MODE_ENUM_MAX_VAL_V01 = INT_MAX,
 };
 
@@ -104,6 +119,9 @@ enum wlfw_pipedir_enum_v01 {
 #define QMI_WLFW_ALREADY_REGISTERED_V01 ((uint64_t)0x01ULL)
 #define QMI_WLFW_FW_READY_V01 ((uint64_t)0x02ULL)
 #define QMI_WLFW_MSA_READY_V01 ((uint64_t)0x04ULL)
+#define QMI_WLFW_FW_MEM_READY_V01 ((uint64_t)0x08ULL)
+
+#define QMI_WLFW_FW_REJUVENATE_V01 ((uint64_t)0x01ULL)
 
 struct wlfw_ce_tgt_pipe_cfg_s_v01 {
 	uint32_t pipe_num;
@@ -124,6 +142,10 @@ struct wlfw_shadow_reg_cfg_s_v01 {
 	uint16_t offset;
 };
 
+struct wlfw_shadow_reg_v2_cfg_s_v01 {
+	uint32_t addr;
+};
+
 struct wlfw_memory_region_info_s_v01 {
 	uint64_t region_addr;
 	uint32_t size;
@@ -161,8 +183,16 @@ struct wlfw_ind_register_req_msg_v01 {
 	uint8_t pin_connect_result_enable;
 	uint8_t client_id_valid;
 	uint32_t client_id;
+	uint8_t request_mem_enable_valid;
+	uint8_t request_mem_enable;
+	uint8_t fw_mem_ready_enable_valid;
+	uint8_t fw_mem_ready_enable;
+	uint8_t cold_boot_cal_done_enable_valid;
+	uint8_t cold_boot_cal_done_enable;
+	uint8_t rejuvenate_enable_valid;
+	uint32_t rejuvenate_enable;
 };
-#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 27
+#define WLFW_IND_REGISTER_REQ_MSG_V01_MAX_MSG_LEN 46
 extern struct elem_info wlfw_ind_register_req_msg_v01_ei[];
 
 struct wlfw_ind_register_resp_msg_v01 {
@@ -223,8 +253,12 @@ struct wlfw_wlan_cfg_req_msg_v01 {
 	uint32_t shadow_reg_len;
 	struct wlfw_shadow_reg_cfg_s_v01
 	shadow_reg[QMI_WLFW_MAX_NUM_SHADOW_REG_V01];
+	uint8_t shadow_reg_v2_valid;
+	uint32_t shadow_reg_v2_len;
+	struct wlfw_shadow_reg_v2_cfg_s_v01
+	shadow_reg_v2[QMI_WLFW_MAX_NUM_SHADOW_REG_V2_V01];
 };
-#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 655
+#define WLFW_WLAN_CFG_REQ_MSG_V01_MAX_MSG_LEN 803
 extern struct elem_info wlfw_wlan_cfg_req_msg_v01_ei[];
 
 struct wlfw_wlan_cfg_resp_msg_v01 {
@@ -449,4 +483,90 @@ struct wlfw_mac_addr_resp_msg_v01 {
 #define WLFW_MAC_ADDR_RESP_MSG_V01_MAX_MSG_LEN 7
 extern struct elem_info wlfw_mac_addr_resp_msg_v01_ei[];
 
+struct wlfw_host_cap_req_msg_v01 {
+	uint8_t daemon_support_valid;
+	uint8_t daemon_support;
+};
+#define WLFW_HOST_CAP_REQ_MSG_V01_MAX_MSG_LEN 4
+extern struct elem_info wlfw_host_cap_req_msg_v01_ei[];
+
+struct wlfw_host_cap_resp_msg_v01 {
+	struct qmi_response_type_v01 resp;
+};
+#define WLFW_HOST_CAP_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_host_cap_resp_msg_v01_ei[];
+
+struct wlfw_request_mem_ind_msg_v01 {
+	uint32_t size;
+};
+#define WLFW_REQUEST_MEM_IND_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_request_mem_ind_msg_v01_ei[];
+
+struct wlfw_respond_mem_req_msg_v01 {
+	uint64_t addr;
+	uint32_t size;
+};
+#define WLFW_RESPOND_MEM_REQ_MSG_V01_MAX_MSG_LEN 18
+extern struct elem_info wlfw_respond_mem_req_msg_v01_ei[];
+
+struct wlfw_respond_mem_resp_msg_v01 {
+	struct qmi_response_type_v01 resp;
+};
+#define WLFW_RESPOND_MEM_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_respond_mem_resp_msg_v01_ei[];
+
+struct wlfw_fw_mem_ready_ind_msg_v01 {
+	char placeholder;
+};
+#define WLFW_FW_MEM_READY_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_fw_mem_ready_ind_msg_v01_ei[];
+
+struct wlfw_cold_boot_cal_done_ind_msg_v01 {
+	char placeholder;
+};
+#define WLFW_COLD_BOOT_CAL_DONE_IND_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_cold_boot_cal_done_ind_msg_v01_ei[];
+
+struct wlfw_rejuvenate_ind_msg_v01 {
+	uint8_t cause_for_rejuvenation_valid;
+	uint8_t cause_for_rejuvenation;
+	uint8_t requesting_sub_system_valid;
+	uint8_t requesting_sub_system;
+	uint8_t line_number_valid;
+	uint16_t line_number;
+	uint8_t function_name_valid;
+	char function_name[QMI_WLFW_FUNCTION_NAME_LEN_V01 + 1];
+};
+#define WLFW_REJUVENATE_IND_MSG_V01_MAX_MSG_LEN 144
+extern struct elem_info wlfw_rejuvenate_ind_msg_v01_ei[];
+
+struct wlfw_rejuvenate_ack_req_msg_v01 {
+	char placeholder;
+};
+#define WLFW_REJUVENATE_ACK_REQ_MSG_V01_MAX_MSG_LEN 0
+extern struct elem_info wlfw_rejuvenate_ack_req_msg_v01_ei[];
+
+struct wlfw_rejuvenate_ack_resp_msg_v01 {
+	struct qmi_response_type_v01 resp;
+};
+#define WLFW_REJUVENATE_ACK_RESP_MSG_V01_MAX_MSG_LEN 7
+extern struct elem_info wlfw_rejuvenate_ack_resp_msg_v01_ei[];
+
+struct wlfw_dynamic_feature_mask_req_msg_v01 {
+	uint8_t mask_valid;
+	uint64_t mask;
+};
+#define WLFW_DYNAMIC_FEATURE_MASK_REQ_MSG_V01_MAX_MSG_LEN 11
+extern struct elem_info wlfw_dynamic_feature_mask_req_msg_v01_ei[];
+
+struct wlfw_dynamic_feature_mask_resp_msg_v01 {
+	struct qmi_response_type_v01 resp;
+	uint8_t prev_mask_valid;
+	uint64_t prev_mask;
+	uint8_t curr_mask_valid;
+	uint64_t curr_mask;
+};
+#define WLFW_DYNAMIC_FEATURE_MASK_RESP_MSG_V01_MAX_MSG_LEN 29
+extern struct elem_info wlfw_dynamic_feature_mask_resp_msg_v01_ei[];
+
 #endif
diff --git a/drivers/soundwire/Kconfig b/drivers/soundwire/Kconfig
new file mode 100644
index 0000000..ef8a351
--- /dev/null
+++ b/drivers/soundwire/Kconfig
@@ -0,0 +1,17 @@
+#
+# SOUNDWIRE driver configuration
+#
+menuconfig SOUNDWIRE
+	bool "Soundwire support"
+	help
+	  Soundwire is a two wire interface for audio to connect
+	  simple peripheral components in mobile devices.
+
+if SOUNDWIRE
+config SOUNDWIRE_WCD_CTRL
+	depends on WCD9335_CODEC
+	tristate "QTI WCD CODEC Soundwire controller"
+	default n
+	help
+	  Select driver for QTI's Soundwire Master Component.
+endif
diff --git a/drivers/soundwire/Makefile b/drivers/soundwire/Makefile
new file mode 100644
index 0000000..53acff1
--- /dev/null
+++ b/drivers/soundwire/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for kernel soundwire framework.
+#
+obj-$(CONFIG_SOUNDWIRE)			+= soundwire.o
+obj-$(CONFIG_SOUNDWIRE_WCD_CTRL)	+= swr-wcd-ctrl.o
diff --git a/drivers/soundwire/soundwire.c b/drivers/soundwire/soundwire.c
new file mode 100644
index 0000000..68655a5
--- /dev/null
+++ b/drivers/soundwire/soundwire.c
@@ -0,0 +1,1051 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/soundwire.h>
+
+struct boardinfo {
+	struct list_head	list;
+	struct swr_boardinfo	board_info;
+};
+
+static LIST_HEAD(board_list);
+static LIST_HEAD(swr_master_list);
+static DEFINE_MUTEX(board_lock);
+static DEFINE_IDR(master_idr);
+static DEFINE_MUTEX(swr_lock);
+
+static struct device_type swr_dev_type;
+
+#define SOUNDWIRE_NAME_SIZE	32
+
+static void swr_master_put(struct swr_master *master)
+{
+	if (master)
+		put_device(&master->dev);
+}
+
+static struct swr_master *swr_master_get(struct swr_master *master)
+{
+	if (!master || !get_device(&master->dev))
+		return NULL;
+	return master;
+}
+
+static void swr_dev_release(struct device *dev)
+{
+	struct swr_device *swr_dev = to_swr_device(dev);
+	struct swr_master *master;
+
+	if (!swr_dev)
+		return;
+	master = swr_dev->master;
+	if (!master)
+		return;
+	mutex_lock(&master->mlock);
+	list_del_init(&swr_dev->dev_list);
+	mutex_unlock(&master->mlock);
+	swr_master_put(swr_dev->master);
+	kfree(swr_dev);
+}
+
+/**
+ * swr_new_device - instantiate a new soundwire device
+ * @master: Controller to which device is connected
+ * @info: Describes the soundwire device
+ * Context: can sleep
+ *
+ * Create a soundwire device. Binding is handled through driver model
+ * probe/remove methods. A driver may be bound to this device when
+ * the function gets returned.
+ *
+ * Returns a soundwire new device or NULL
+ */
+struct swr_device *swr_new_device(struct swr_master *master,
+				 struct swr_boardinfo const *info)
+{
+	int result;
+	struct swr_device *swr;
+
+	if (!master || !swr_master_get(master)) {
+		pr_err("%s: master is NULL\n", __func__);
+		return NULL;
+	}
+
+	swr = kzalloc(sizeof(*swr), GFP_KERNEL);
+	if (!swr) {
+		put_device(&master->dev);
+		return NULL;
+	}
+	swr->master = master;
+	swr->addr = info->addr;
+	strlcpy(swr->name, info->name, sizeof(swr->name));
+	swr->dev.type = &swr_dev_type;
+	swr->dev.parent = &master->dev;
+	swr->dev.bus = &soundwire_type;
+	swr->dev.release = swr_dev_release;
+	swr->dev.of_node = info->of_node;
+	mutex_lock(&master->mlock);
+	list_add_tail(&swr->dev_list, &master->devices);
+	mutex_unlock(&master->mlock);
+
+	dev_set_name(&swr->dev, "%s.%lx", swr->name, swr->addr);
+	result = device_register(&swr->dev);
+	if (result) {
+		dev_err(&master->dev, "device [%s] register failed err %d\n",
+			swr->name, result);
+		goto err_out;
+	}
+	dev_dbg(&master->dev, "Device [%s] registered with bus id %s\n",
+		swr->name, dev_name(&swr->dev));
+	return swr;
+
+err_out:
+	dev_dbg(&master->dev, "Failed to register swr device %s at 0x%lx %d\n",
+		swr->name, swr->addr, result);
+	swr_master_put(master);
+	kfree(swr);
+	return NULL;
+}
+EXPORT_SYMBOL(swr_new_device);
+
+/**
+ * swr_startup_devices - perform additional initialization for child devices
+ *
+ * @swr_dev: pointer to soundwire slave device
+ *
+ * Performs any additional initialization needed for a soundwire slave device.
+ * This is a optional functionality defined by slave devices.
+ * Removes the slave node from the list, in case there is any failure.
+ */
+int swr_startup_devices(struct swr_device *swr_dev)
+{
+	struct swr_driver *swr_drv;
+	struct device *dev;
+	int ret = 0;
+
+	if (!swr_dev)
+		return -EINVAL;
+
+	dev = &swr_dev->dev;
+	if (!dev)
+		return -EINVAL;
+
+	swr_drv = to_swr_driver(dev->driver);
+	if (!swr_drv)
+		return -EINVAL;
+
+	if (swr_drv->startup) {
+		ret = swr_drv->startup(swr_dev);
+		if (ret)
+			goto out;
+
+		dev_dbg(&swr_dev->dev,
+			"%s: startup complete for device %lx\n",
+			__func__, swr_dev->addr);
+	}
+
+out:
+	return ret;
+}
+EXPORT_SYMBOL(swr_startup_devices);
+
+/**
+ * of_register_swr_devices - register child devices on to the soundwire bus
+ * @master: pointer to soundwire master device
+ *
+ * Registers a soundwire device for each child node of master node which has
+ * a "swr-devid" property
+ *
+ */
+int of_register_swr_devices(struct swr_master *master)
+{
+	struct swr_device *swr;
+	struct device_node *node;
+
+	if (!master->dev.of_node)
+		return -EINVAL;
+
+	for_each_available_child_of_node(master->dev.of_node, node) {
+		struct swr_boardinfo info = {};
+		u64 addr;
+
+		dev_dbg(&master->dev, "of_swr:register %s\n", node->full_name);
+
+		if (of_modalias_node(node, info.name, sizeof(info.name)) < 0) {
+			dev_err(&master->dev, "of_swr:modalias failure %s\n",
+				node->full_name);
+			continue;
+		}
+		if (of_property_read_u64(node, "reg", &addr)) {
+			dev_err(&master->dev, "of_swr:invalid reg %s\n",
+				node->full_name);
+			continue;
+		}
+		info.addr = addr;
+		info.of_node = of_node_get(node);
+		swr = swr_new_device(master, &info);
+		if (!swr) {
+			dev_err(&master->dev, "of_swr: Register failed %s\n",
+				node->full_name);
+			of_node_put(node);
+			continue;
+		}
+		master->num_dev++;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(of_register_swr_devices);
+
+/**
+ * swr_port_response - response from master to free the completed transaction
+ * @mstr: pointer to soundwire master device
+ * @tid: transaction id that indicates transaction to be freed.
+ *
+ * Master calls this function to free the compeleted transaction memory
+ */
+void swr_port_response(struct swr_master *mstr, u8 tid)
+{
+	struct swr_params *txn;
+
+	txn = mstr->port_txn[tid];
+
+	if (txn == NULL) {
+		dev_err(&mstr->dev, "%s: transaction is already NULL\n",
+			__func__);
+		return;
+	}
+	mstr->port_txn[tid] = NULL;
+	kfree(txn);
+}
+EXPORT_SYMBOL(swr_port_response);
+
+/**
+ * swr_remove_from_group - remove soundwire slave devices from group
+ * @dev: pointer to the soundwire slave device
+ * dev_num: device number of the soundwire slave device
+ *
+ * Returns error code for failure and 0 for success
+ */
+int swr_remove_from_group(struct swr_device *dev, u8 dev_num)
+{
+	struct swr_master *master;
+
+	if (!dev)
+		return -ENODEV;
+
+	master = dev->master;
+	if (!master)
+		return -EINVAL;
+
+	if (!dev->group_id)
+		return 0;
+
+	if (master->gr_sid == dev_num)
+		return 0;
+
+	if (master->remove_from_group && master->remove_from_group(master))
+		dev_dbg(&master->dev, "%s: falling back to GROUP_NONE\n",
+			__func__);
+
+	return 0;
+}
+EXPORT_SYMBOL(swr_remove_from_group);
+
+/**
+ * swr_slvdev_datapath_control - Enables/Disables soundwire slave device
+ *                               data path
+ * @dev: pointer to soundwire slave device
+ * @dev_num: device number of the soundwire slave device
+ *
+ * Returns error code for failure and 0 for success
+ */
+int swr_slvdev_datapath_control(struct swr_device *dev, u8 dev_num,
+				bool enable)
+{
+	struct swr_master *master;
+
+	if (!dev)
+		return -ENODEV;
+
+	master = dev->master;
+	if (!master)
+		return -EINVAL;
+
+	if (dev->group_id) {
+		/* Broadcast */
+		if (master->gr_sid != dev_num) {
+			if (!master->gr_sid)
+				master->gr_sid = dev_num;
+			else
+				return 0;
+		}
+	}
+
+	if (master->slvdev_datapath_control)
+		master->slvdev_datapath_control(master, enable);
+
+	return 0;
+}
+EXPORT_SYMBOL(swr_slvdev_datapath_control);
+
+/**
+ * swr_connect_port - enable soundwire slave port(s)
+ * @dev: pointer to soundwire slave device
+ * @port_id: logical port id(s) of soundwire slave device
+ * @num_port: number of slave device ports need to be enabled
+ * @ch_mask: channels for each port that needs to be enabled
+ * @ch_rate: rate at which each port/channels operate
+ * @num_ch: number of channels for each port
+ *
+ * soundwire slave device call swr_connect_port API to enable all/some of
+ * its ports and corresponding channels and channel rate. This API will
+ * call master connect_port callback function to calculate frame structure
+ * and enable master and slave ports
+ */
+int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port,
+			u8 *ch_mask, u32 *ch_rate, u8 *num_ch)
+{
+	u8 i = 0;
+	int ret = 0;
+	struct swr_params *txn = NULL;
+	struct swr_params **temp_txn = NULL;
+	struct swr_master *master = dev->master;
+
+	if (!master) {
+		pr_err("%s: Master is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (num_port > SWR_MAX_DEV_PORT_NUM) {
+		dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n",
+			__func__, num_port, SWR_MAX_DEV_PORT_NUM);
+		return -EINVAL;
+	}
+
+	/*
+	 * create "txn" to accommodate ports enablement of
+	 * different slave devices calling swr_connect_port at the
+	 * same time. Once master process the txn data, it calls
+	 * swr_port_response() to free the transaction. Maximum
+	 * of 256 transactions can be allocated.
+	 */
+	txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL);
+	if (!txn)
+		return -ENOMEM;
+
+	mutex_lock(&master->mlock);
+	for (i = 0; i < master->last_tid; i++) {
+		if (master->port_txn[i] == NULL)
+			break;
+	}
+	if (i >= master->last_tid) {
+		if (master->last_tid == 255) {
+			mutex_unlock(&master->mlock);
+			kfree(txn);
+			dev_err(&master->dev, "%s Max tid reached\n",
+				__func__);
+			return -ENOMEM;
+		}
+		temp_txn = krealloc(master->port_txn,
+				(i + 1) * sizeof(struct swr_params *),
+				GFP_KERNEL);
+		if (!temp_txn) {
+			mutex_unlock(&master->mlock);
+			kfree(txn);
+			dev_err(&master->dev, "%s Not able to allocate\n"
+				"master port transaction memory\n",
+				__func__);
+			return -ENOMEM;
+		}
+		master->port_txn = temp_txn;
+		master->last_tid++;
+	}
+	master->port_txn[i] = txn;
+	mutex_unlock(&master->mlock);
+	txn->tid = i;
+
+	txn->dev_id = dev->dev_num;
+	txn->num_port = num_port;
+	for (i = 0; i < num_port; i++) {
+		txn->port_id[i] = port_id[i];
+		txn->num_ch[i]  = num_ch[i];
+		txn->ch_rate[i] = ch_rate[i];
+		txn->ch_en[i]   = ch_mask[i];
+	}
+	ret = master->connect_port(master, txn);
+	return ret;
+}
+EXPORT_SYMBOL(swr_connect_port);
+
+/**
+ * swr_disconnect_port - disable soundwire slave port(s)
+ * @dev: pointer to soundwire slave device
+ * @port_id: logical port id(s) of soundwire slave device
+ * @num_port: number of slave device ports need to be disabled
+ *
+ * soundwire slave device call swr_disconnect_port API to disable all/some of
+ * its ports. This API will call master disconnect_port callback function to
+ * disable master and slave port and (re)configure frame structure
+ */
+int swr_disconnect_port(struct swr_device *dev, u8 *port_id, u8 num_port)
+{
+	u8 i = 0;
+	int ret;
+	struct swr_params *txn = NULL;
+	struct swr_params **temp_txn = NULL;
+	struct swr_master *master = dev->master;
+
+	if (!master) {
+		pr_err("%s: Master is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (num_port > SWR_MAX_DEV_PORT_NUM) {
+		dev_err(&master->dev, "%s: num_port %d exceeds max port %d\n",
+			__func__, num_port, SWR_MAX_DEV_PORT_NUM);
+		return -EINVAL;
+	}
+
+	txn = kzalloc(sizeof(struct swr_params), GFP_KERNEL);
+	if (!txn)
+		return -ENOMEM;
+
+	mutex_lock(&master->mlock);
+	for (i = 0; i < master->last_tid; i++) {
+		if (master->port_txn[i] == NULL)
+			break;
+	}
+	if (i >= master->last_tid) {
+		if (master->last_tid == 255) {
+			mutex_unlock(&master->mlock);
+			kfree(txn);
+			dev_err(&master->dev, "%s Max tid reached\n",
+				__func__);
+			return -ENOMEM;
+		}
+		temp_txn = krealloc(master->port_txn,
+				(i + 1) * sizeof(struct swr_params *),
+				GFP_KERNEL);
+		if (!temp_txn) {
+			mutex_unlock(&master->mlock);
+			kfree(txn);
+			dev_err(&master->dev, "%s Not able to allocate\n"
+				"master port transaction memory\n",
+				__func__);
+			return -ENOMEM;
+		}
+		master->port_txn = temp_txn;
+		master->last_tid++;
+	}
+	master->port_txn[i] = txn;
+	mutex_unlock(&master->mlock);
+	txn->tid = i;
+
+	txn->dev_id = dev->dev_num;
+	txn->num_port = num_port;
+	for (i = 0; i < num_port; i++)
+		txn->port_id[i] = port_id[i];
+	ret = master->disconnect_port(master, txn);
+	return ret;
+}
+EXPORT_SYMBOL(swr_disconnect_port);
+
+/**
+ * swr_get_logical_dev_num - Get soundwire slave logical device number
+ * @dev: pointer to soundwire slave device
+ * @dev_id: physical device id of soundwire slave device
+ * @dev_num: pointer to logical device num of soundwire slave device
+ *
+ * This API will get the logical device number of soundwire slave device
+ */
+int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id,
+			u8 *dev_num)
+{
+	int ret = 0;
+	struct swr_master *master = dev->master;
+
+	if (!master) {
+		pr_err("%s: Master is NULL\n", __func__);
+		return -EINVAL;
+	}
+	mutex_lock(&master->mlock);
+	ret = master->get_logical_dev_num(master, dev_id, dev_num);
+	if (ret) {
+		pr_err("%s: Error %d to get logical addr for device %llx\n",
+			__func__, ret, dev_id);
+	}
+	mutex_unlock(&master->mlock);
+	return ret;
+}
+EXPORT_SYMBOL(swr_get_logical_dev_num);
+
+/**
+ * swr_read - read soundwire slave device registers
+ * @dev: pointer to soundwire slave device
+ * @dev_num: logical device num of soundwire slave device
+ * @reg_addr: base register address that needs to be read
+ * @buf: pointer to store the values of registers from base address
+ * @len: length of the buffer
+ *
+ * This API will read the value of the register address from
+ * soundwire slave device
+ */
+int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr,
+	     void *buf, u32 len)
+{
+	struct swr_master *master = dev->master;
+
+	if (!master)
+		return -EINVAL;
+	return master->read(master, dev_num, reg_addr, buf, len);
+}
+EXPORT_SYMBOL(swr_read);
+
+/**
+ * swr_bulk_write - write soundwire slave device registers
+ * @dev: pointer to soundwire slave device
+ * @dev_num: logical device num of soundwire slave device
+ * @reg_addr: register address of soundwire slave device
+ * @buf: contains value of register address
+ * @len: indicates number of registers
+ *
+ * This API will write the value of the register address to
+ * soundwire slave device
+ */
+int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg,
+		   const void *buf, size_t len)
+{
+	struct swr_master *master;
+
+	if (!dev || !dev->master)
+		return -EINVAL;
+
+	master = dev->master;
+	if (dev->group_id) {
+		if (master->gr_sid != dev_num) {
+			if (!master->gr_sid)
+				master->gr_sid = dev_num;
+			else
+				return 0;
+		}
+		dev_num = dev->group_id;
+	}
+	if (master->bulk_write)
+		return master->bulk_write(master, dev_num, reg, buf, len);
+
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(swr_bulk_write);
+
+/**
+ * swr_write - write soundwire slave device registers
+ * @dev: pointer to soundwire slave device
+ * @dev_num: logical device num of soundwire slave device
+ * @reg_addr: register address of soundwire slave device
+ * @buf: contains value of register address
+ *
+ * This API will write the value of the register address to
+ * soundwire slave device
+ */
+int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr,
+	      const void *buf)
+{
+	struct swr_master *master = dev->master;
+
+	if (!master)
+		return -EINVAL;
+
+	if (dev->group_id) {
+		if (master->gr_sid != dev_num) {
+			if (!master->gr_sid)
+				master->gr_sid = dev_num;
+			else
+				return 0;
+		}
+		dev_num = dev->group_id;
+	}
+	return master->write(master, dev_num, reg_addr, buf);
+}
+EXPORT_SYMBOL(swr_write);
+
+/**
+ * swr_device_up - Function to bringup the soundwire slave device
+ * @swr_dev: pointer to soundwire slave device
+ * Context: can sleep
+ *
+ * This API will be called by soundwire master to bringup the slave
+ * device.
+ */
+int swr_device_up(struct swr_device *swr_dev)
+{
+	struct device *dev;
+	const struct swr_driver *sdrv;
+
+	if (!swr_dev)
+		return -EINVAL;
+
+	dev = &swr_dev->dev;
+	sdrv = to_swr_driver(dev->driver);
+	if (!sdrv)
+		return -EINVAL;
+
+	if (sdrv->device_up)
+		return sdrv->device_up(to_swr_device(dev));
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL(swr_device_up);
+
+/**
+ * swr_device_down - Function to call soundwire slave device down
+ * @swr_dev: pointer to soundwire slave device
+ * Context: can sleep
+ *
+ * This API will be called by soundwire master to put slave device in
+ * shutdown state.
+ */
+int swr_device_down(struct swr_device *swr_dev)
+{
+	struct device *dev;
+	const struct swr_driver *sdrv;
+
+	if (!swr_dev)
+		return -EINVAL;
+
+	dev = &swr_dev->dev;
+	sdrv = to_swr_driver(dev->driver);
+	if (!sdrv)
+		return -EINVAL;
+
+	if (sdrv->device_down)
+		return sdrv->device_down(to_swr_device(dev));
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL(swr_device_down);
+
+/**
+ * swr_reset_device - reset soundwire slave device
+ * @swr_dev: pointer to soundwire slave device
+ * Context: can sleep
+ *
+ * This API will be called by soundwire master to reset the slave
+ * device when the slave device is not responding or in undefined
+ * state
+ */
+int swr_reset_device(struct swr_device *swr_dev)
+{
+	struct device *dev;
+	const struct swr_driver *sdrv;
+
+	if (!swr_dev)
+		return -EINVAL;
+
+	dev = &swr_dev->dev;
+	sdrv = to_swr_driver(dev->driver);
+	if (!sdrv)
+		return -EINVAL;
+
+	if (sdrv->reset_device)
+		return sdrv->reset_device(to_swr_device(dev));
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL(swr_reset_device);
+
+/**
+ * swr_set_device_group - Assign group id to the slave devices
+ * @swr_dev: pointer to soundwire slave device
+ * @id: group id to be assigned to slave device
+ * Context: can sleep
+ *
+ * This API will be called either from soundwire master or slave
+ * device to assign group id.
+ */
+int swr_set_device_group(struct swr_device *swr_dev, u8 id)
+{
+	struct swr_master *master;
+
+	if (!swr_dev)
+		return -EINVAL;
+
+	swr_dev->group_id = id;
+	master = swr_dev->master;
+	if (!id && master)
+		master->gr_sid = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(swr_set_device_group);
+
+static int swr_drv_probe(struct device *dev)
+{
+	const struct swr_driver *sdrv = to_swr_driver(dev->driver);
+
+	if (!sdrv)
+		return -EINVAL;
+
+	if (sdrv->probe)
+		return sdrv->probe(to_swr_device(dev));
+	return -ENODEV;
+}
+
+static int swr_drv_remove(struct device *dev)
+{
+	const struct swr_driver *sdrv = to_swr_driver(dev->driver);
+
+	if (!sdrv)
+		return -EINVAL;
+
+	if (sdrv->remove)
+		return sdrv->remove(to_swr_device(dev));
+	return -ENODEV;
+}
+
+static void swr_drv_shutdown(struct device *dev)
+{
+	const struct swr_driver *sdrv = to_swr_driver(dev->driver);
+
+	if (!sdrv)
+		return;
+
+	if (sdrv->shutdown)
+		sdrv->shutdown(to_swr_device(dev));
+}
+
+/**
+ * swr_driver_register - register a soundwire driver
+ * @drv: the driver to register
+ * Context: can sleep
+ */
+int swr_driver_register(struct swr_driver *drv)
+{
+	drv->driver.bus = &soundwire_type;
+	if (drv->probe)
+		drv->driver.probe = swr_drv_probe;
+	if (drv->remove)
+		drv->driver.remove = swr_drv_remove;
+
+	if (drv->shutdown)
+		drv->driver.shutdown = swr_drv_shutdown;
+
+	return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL(swr_driver_register);
+
+/**
+ * swr_driver_unregister - unregister a soundwire driver
+ * @drv: the driver to unregister
+ */
+void swr_driver_unregister(struct swr_driver *drv)
+{
+	if (drv)
+		driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL(swr_driver_unregister);
+
+static void swr_match_ctrl_to_boardinfo(struct swr_master *master,
+				struct swr_boardinfo *bi)
+{
+	struct swr_device *swr;
+
+	if (master->bus_num != bi->bus_num) {
+		dev_dbg(&master->dev,
+			"%s: master# %d and bi# %d does not match\n",
+			__func__, master->bus_num, bi->bus_num);
+		return;
+	}
+
+	swr = swr_new_device(master, bi);
+	if (!swr)
+		dev_err(&master->dev, "can't create new device for %s\n",
+			bi->swr_slave->name);
+}
+
+/**
+ * swr_master_add_boarddevices - Add devices registered by board info
+ * @master: master to which these devices are to be added to.
+ *
+ * This API is called by master when it is up and running. If devices
+ * on a master were registered before master, this will make sure that
+ * they get probed when master is up.
+ */
+void swr_master_add_boarddevices(struct swr_master *master)
+{
+	struct boardinfo *bi;
+
+	mutex_lock(&board_lock);
+	list_add_tail(&master->list, &swr_master_list);
+	list_for_each_entry(bi, &board_list, list)
+		swr_match_ctrl_to_boardinfo(master, &bi->board_info);
+	mutex_unlock(&board_lock);
+}
+EXPORT_SYMBOL(swr_master_add_boarddevices);
+
+static void swr_unregister_device(struct swr_device *swr)
+{
+	if (swr)
+		device_unregister(&swr->dev);
+}
+
+static void swr_master_release(struct device *dev)
+{
+	struct swr_master *master = to_swr_master(dev);
+
+	kfree(master);
+}
+
+#define swr_master_attr_gr NULL
+static struct device_type swr_master_type = {
+	.groups     = swr_master_attr_gr,
+	.release    = swr_master_release,
+};
+
+static int __unregister(struct device *dev, void *null)
+{
+	swr_unregister_device(to_swr_device(dev));
+	return 0;
+}
+
+/**
+ * swr_unregister_master - unregister soundwire master controller
+ * @master: the master being unregistered
+ *
+ * This API is called by master controller driver to unregister
+ *  master controller that was registered by swr_register_master API.
+ */
+void swr_unregister_master(struct swr_master *master)
+{
+	int dummy;
+	struct swr_master *m_ctrl;
+
+	mutex_lock(&swr_lock);
+	m_ctrl = idr_find(&master_idr, master->bus_num);
+	mutex_unlock(&swr_lock);
+	if (m_ctrl != master)
+		return;
+
+	mutex_lock(&board_lock);
+	list_del(&master->list);
+	mutex_unlock(&board_lock);
+
+	/* free bus id */
+	mutex_lock(&swr_lock);
+	idr_remove(&master_idr, master->bus_num);
+	mutex_unlock(&swr_lock);
+
+	dummy = device_for_each_child(&master->dev, NULL, __unregister);
+	device_unregister(&master->dev);
+}
+EXPORT_SYMBOL(swr_unregister_master);
+
+/**
+ * swr_register_master - register soundwire master controller
+ * @master: master to be registered
+ *
+ * This API will register master with the framework. master->bus_num
+ * is the desired number with which soundwire framework registers the
+ * master.
+ */
+int swr_register_master(struct swr_master *master)
+{
+	int id;
+	int status = 0;
+
+	mutex_lock(&swr_lock);
+	id = idr_alloc(&master_idr, master, master->bus_num,
+			master->bus_num+1, GFP_KERNEL);
+	mutex_unlock(&swr_lock);
+	if (id < 0)
+		return id;
+	master->bus_num = id;
+
+	/* Can't register until driver model init */
+	if (WARN_ON(!soundwire_type.p)) {
+		status = -EAGAIN;
+		goto done;
+	}
+
+	dev_set_name(&master->dev, "swr%u", master->bus_num);
+	master->dev.bus = &soundwire_type;
+	master->dev.type = &swr_master_type;
+	mutex_init(&master->mlock);
+	status = device_register(&master->dev);
+	if (status < 0)
+		goto done;
+
+	INIT_LIST_HEAD(&master->devices);
+	pr_debug("%s: SWR master registered successfully %s\n",
+		__func__, dev_name(&master->dev));
+	return 0;
+
+done:
+	idr_remove(&master_idr, master->bus_num);
+	return status;
+}
+EXPORT_SYMBOL(swr_register_master);
+
+#define swr_device_attr_gr NULL
+#define swr_device_uevent NULL
+static struct device_type swr_dev_type = {
+	.groups    = swr_device_attr_gr,
+	.uevent    = swr_device_uevent,
+	.release   = swr_dev_release,
+};
+
+static const struct swr_device_id *swr_match(const struct swr_device_id *id,
+					     const struct swr_device *swr_dev)
+{
+	while (id->name[0]) {
+		if (strcmp(swr_dev->name, id->name) == 0)
+			return id;
+		id++;
+	}
+	return NULL;
+}
+
+static int swr_device_match(struct device *dev, struct device_driver *driver)
+{
+	struct swr_device *swr_dev;
+	struct swr_driver *drv = to_swr_driver(driver);
+
+	if (!drv)
+		return -EINVAL;
+
+	if (dev->type == &swr_dev_type)
+		swr_dev = to_swr_device(dev);
+	else
+		return 0;
+	if (drv->id_table)
+		return swr_match(drv->id_table, swr_dev) != NULL;
+
+	if (driver->name)
+		return strcmp(swr_dev->name, driver->name) == 0;
+	return 0;
+}
+#ifdef CONFIG_PM_SLEEP
+static int swr_legacy_suspend(struct device *dev, pm_message_t mesg)
+{
+	struct swr_device *swr_dev = NULL;
+	struct swr_driver *driver;
+
+	if (dev->type == &swr_dev_type)
+		swr_dev = to_swr_device(dev);
+
+	if (!swr_dev || !dev->driver)
+		return 0;
+
+	driver = to_swr_driver(dev->driver);
+	if (!driver->suspend)
+		return 0;
+
+	return driver->suspend(swr_dev, mesg);
+}
+
+static int swr_legacy_resume(struct device *dev)
+{
+	struct swr_device *swr_dev = NULL;
+	struct swr_driver *driver;
+
+	if (dev->type == &swr_dev_type)
+		swr_dev = to_swr_device(dev);
+
+	if (!swr_dev || !dev->driver)
+		return 0;
+
+	driver = to_swr_driver(dev->driver);
+	if (!driver->resume)
+		return 0;
+
+	return driver->resume(swr_dev);
+}
+
+static int swr_pm_suspend(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_suspend(dev);
+	else
+		return swr_legacy_suspend(dev, PMSG_SUSPEND);
+}
+
+static int swr_pm_resume(struct device *dev)
+{
+	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+	if (pm)
+		return pm_generic_resume(dev);
+	else
+		return swr_legacy_resume(dev);
+}
+#else
+#define swr_pm_suspend	NULL
+#define swr_pm_resume	NULL
+#endif /*CONFIG_PM_SLEEP*/
+
+static const struct dev_pm_ops soundwire_pm = {
+	.suspend = swr_pm_suspend,
+	.resume = swr_pm_resume,
+	SET_RUNTIME_PM_OPS(
+		pm_generic_suspend,
+		pm_generic_resume,
+		NULL
+		)
+};
+
+struct device soundwire_dev = {
+	.init_name = "soundwire",
+};
+
+struct bus_type soundwire_type = {
+	.name		= "soundwire",
+	.match		= swr_device_match,
+	.pm		= &soundwire_pm,
+};
+EXPORT_SYMBOL(soundwire_type);
+
+static void __exit soundwire_exit(void)
+{
+	device_unregister(&soundwire_dev);
+	bus_unregister(&soundwire_type);
+}
+
+static int __init soundwire_init(void)
+{
+	int retval;
+
+	retval = bus_register(&soundwire_type);
+	if (!retval)
+		retval = device_register(&soundwire_dev);
+
+	if (retval)
+		bus_unregister(&soundwire_type);
+
+	return retval;
+}
+postcore_initcall(soundwire_init);
+module_exit(soundwire_exit);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Soundwire module");
+MODULE_ALIAS("platform:soundwire");
diff --git a/drivers/soundwire/swr-wcd-ctrl.c b/drivers/soundwire/swr-wcd-ctrl.c
new file mode 100644
index 0000000..b9984f2
--- /dev/null
+++ b/drivers/soundwire/swr-wcd-ctrl.c
@@ -0,0 +1,1853 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/soundwire/soundwire.h>
+#include <linux/soundwire/swr-wcd.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include "swrm_registers.h"
+#include "swr-wcd-ctrl.h"
+
+#define SWR_BROADCAST_CMD_ID            0x0F
+#define SWR_AUTO_SUSPEND_DELAY          3 /* delay in sec */
+#define SWR_DEV_ID_MASK			0xFFFFFFFF
+#define SWR_REG_VAL_PACK(data, dev, id, reg)	\
+			((reg) | ((id) << 16) | ((dev) << 20) | ((data) << 24))
+
+/* pm runtime auto suspend timer in msecs */
+static int auto_suspend_timer = SWR_AUTO_SUSPEND_DELAY * 1000;
+module_param(auto_suspend_timer, int, 0664);
+MODULE_PARM_DESC(auto_suspend_timer, "timer for auto suspend");
+
+static u8 mstr_ports[] = {100, 101, 102, 103, 104, 105, 106, 107};
+static u8 mstr_port_type[] = {SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT,
+			      SWR_DAC_PORT, SWR_COMP_PORT, SWR_BOOST_PORT,
+			      SWR_VISENSE_PORT, SWR_VISENSE_PORT};
+
+struct usecase uc[] = {
+	{0, 0, 0},		/* UC0: no ports */
+	{1, 1, 2400},		/* UC1: Spkr */
+	{1, 4, 600},		/* UC2: Compander */
+	{1, 2, 300},		/* UC3: Smart Boost */
+	{1, 2, 1200},		/* UC4: VI Sense */
+	{4, 9, 4500},		/* UC5: Spkr + Comp + SB + VI */
+	{8, 18, 9000},		/* UC6: 2*(Spkr + Comp + SB + VI) */
+	{2, 2, 4800},		/* UC7: 2*Spkr */
+	{2, 5, 3000},		/* UC8: Spkr + Comp */
+	{4, 10, 6000},		/* UC9: 2*(Spkr + Comp) */
+	{3, 7, 3300},		/* UC10: Spkr + Comp + SB */
+	{6, 14, 6600},		/* UC11: 2*(Spkr + Comp + SB) */
+	{2, 3, 2700},		/* UC12: Spkr + SB */
+	{4, 6, 5400},		/* UC13: 2*(Spkr + SB) */
+	{3, 5, 3900},		/* UC14: Spkr + SB + VI */
+	{6, 10, 7800},		/* UC15: 2*(Spkr + SB + VI) */
+	{2, 3, 3600},		/* UC16: Spkr + VI */
+	{4, 6, 7200},		/* UC17: 2*(Spkr + VI) */
+};
+#define MAX_USECASE	ARRAY_SIZE(uc)
+
+struct port_params pp[MAX_USECASE][SWR_MSTR_PORT_LEN] = {
+	/* UC 0 */
+	{
+		{0, 0, 0},
+	},
+	/* UC 1 */
+	{
+		{7, 1, 0},
+	},
+	/* UC 2 */
+	{
+		{31, 2, 0},
+	},
+	/* UC 3 */
+	{
+		{63, 12, 31},
+	},
+	/* UC 4 */
+	{
+		{15, 7, 0},
+	},
+	/* UC 5 */
+	{
+		{7, 1, 0},
+		{31, 2, 0},
+		{63, 12, 31},
+		{15, 7, 0},
+	},
+	/* UC 6 */
+	{
+		{7, 1, 0},
+		{31, 2, 0},
+		{63, 12, 31},
+		{15, 7, 0},
+		{7, 6, 0},
+		{31, 18, 0},
+		{63, 13, 31},
+		{15, 10, 0},
+	},
+	/* UC 7 */
+	{
+		{7, 1, 0},
+		{7, 6, 0},
+
+	},
+	/* UC 8 */
+	{
+		{7, 1, 0},
+		{31, 2, 0},
+	},
+	/* UC 9 */
+	{
+		{7, 1, 0},
+		{31, 2, 0},
+		{7, 6, 0},
+		{31, 18, 0},
+	},
+	/* UC 10 */
+	{
+		{7, 1, 0},
+		{31, 2, 0},
+		{63, 12, 31},
+	},
+	/* UC 11 */
+	{
+		{7, 1, 0},
+		{31, 2, 0},
+		{63, 12, 31},
+		{7, 6, 0},
+		{31, 18, 0},
+		{63, 13, 31},
+	},
+	/* UC 12 */
+	{
+		{7, 1, 0},
+		{63, 12, 31},
+	},
+	/* UC 13 */
+	{
+		{7, 1, 0},
+		{63, 12, 31},
+		{7, 6, 0},
+		{63, 13, 31},
+	},
+	/* UC 14 */
+	{
+		{7, 1, 0},
+		{63, 12, 31},
+		{15, 7, 0},
+	},
+	/* UC 15 */
+	{
+		{7, 1, 0},
+		{63, 12, 31},
+		{15, 7, 0},
+		{7, 6, 0},
+		{63, 13, 31},
+		{15, 10, 0},
+	},
+	/* UC 16 */
+	{
+		{7, 1, 0},
+		{15, 7, 0},
+	},
+	/* UC 17 */
+	{
+		{7, 1, 0},
+		{15, 7, 0},
+		{7, 6, 0},
+		{15, 10, 0},
+	},
+};
+
+enum {
+	SWR_NOT_PRESENT, /* Device is detached/not present on the bus */
+	SWR_ATTACHED_OK, /* Device is attached */
+	SWR_ALERT,       /* Device alters master for any interrupts */
+	SWR_RESERVED,    /* Reserved */
+};
+
+#define SWRM_MAX_PORT_REG    40
+#define SWRM_MAX_INIT_REG    8
+
+#define SWR_MSTR_MAX_REG_ADDR	0x1740
+#define SWR_MSTR_START_REG_ADDR	0x00
+#define SWR_MSTR_MAX_BUF_LEN     32
+#define BYTES_PER_LINE          12
+#define SWR_MSTR_RD_BUF_LEN      8
+#define SWR_MSTR_WR_BUF_LEN      32
+
+static void swrm_copy_data_port_config(struct swr_master *master,
+				       u8 inactive_bank);
+static struct swr_mstr_ctrl *dbgswrm;
+static struct dentry *debugfs_swrm_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_reg_dump;
+static unsigned int read_data;
+
+static int swrm_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, u32 *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (kstrtou32(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+		} else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t swrm_reg_show(char __user *ubuf, size_t count,
+					  loff_t *ppos)
+{
+	int i, reg_val, len;
+	ssize_t total = 0;
+	char tmp_buf[SWR_MSTR_MAX_BUF_LEN];
+
+	if (!ubuf || !ppos)
+		return 0;
+
+	for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_MSTR_START_REG_ADDR);
+		i <= SWR_MSTR_MAX_REG_ADDR; i += 4) {
+		reg_val = dbgswrm->read(dbgswrm->handle, i);
+		len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i, reg_val);
+		if ((total + len) >= count - 1)
+			break;
+		if (copy_to_user((ubuf + total), tmp_buf, len)) {
+			pr_err("%s: fail to copy reg dump\n", __func__);
+			total = -EFAULT;
+			goto copy_err;
+		}
+		*ppos += len;
+		total += len;
+	}
+
+copy_err:
+	return total;
+}
+
+static ssize_t swrm_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[SWR_MSTR_RD_BUF_LEN];
+	char *access_str;
+	ssize_t ret_cnt;
+
+	if (!count || !file || !ppos || !ubuf)
+		return -EINVAL;
+
+	access_str = file->private_data;
+	if (*ppos < 0)
+		return -EINVAL;
+
+	if (!strcmp(access_str, "swrm_peek")) {
+		snprintf(lbuf, sizeof(lbuf), "0x%x\n", read_data);
+		ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf,
+					       strnlen(lbuf, 7));
+	} else if (!strcmp(access_str, "swrm_reg_dump")) {
+		ret_cnt = swrm_reg_show(ubuf, count, ppos);
+	} else {
+		pr_err("%s: %s not permitted to read\n", __func__, access_str);
+		ret_cnt = -EPERM;
+	}
+	return ret_cnt;
+}
+
+static ssize_t swrm_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char lbuf[SWR_MSTR_WR_BUF_LEN];
+	int rc;
+	u32 param[5];
+	char *access_str;
+
+	if (!filp || !ppos || !ubuf)
+		return -EINVAL;
+
+	access_str = filp->private_data;
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+	if (!strcmp(access_str, "swrm_poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) &&
+			(param[1] <= 0xFFFFFFFF) &&
+			(rc == 0))
+			rc = dbgswrm->write(dbgswrm->handle, param[0],
+					    param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "swrm_peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 1);
+		if ((param[0] <= SWR_MSTR_MAX_REG_ADDR) && (rc == 0))
+			read_data = dbgswrm->read(dbgswrm->handle, param[0]);
+		else
+			rc = -EINVAL;
+	}
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations swrm_debug_ops = {
+	.open = swrm_debug_open,
+	.write = swrm_debug_write,
+	.read = swrm_debug_read,
+};
+
+static int swrm_set_ch_map(struct swr_mstr_ctrl *swrm, void *data)
+{
+	struct swr_mstr_port *pinfo = (struct swr_mstr_port *)data;
+
+	swrm->mstr_port = kzalloc(sizeof(struct swr_mstr_port), GFP_KERNEL);
+	if (swrm->mstr_port == NULL)
+		return -ENOMEM;
+	swrm->mstr_port->num_port = pinfo->num_port;
+	swrm->mstr_port->port = kzalloc((pinfo->num_port * sizeof(u8)),
+					GFP_KERNEL);
+	if (!swrm->mstr_port->port) {
+		kfree(swrm->mstr_port);
+		swrm->mstr_port = NULL;
+		return -ENOMEM;
+	}
+	memcpy(swrm->mstr_port->port, pinfo->port, pinfo->num_port);
+	return 0;
+}
+
+static bool swrm_is_port_en(struct swr_master *mstr)
+{
+	return !!(mstr->num_port);
+}
+
+static int swrm_clk_request(struct swr_mstr_ctrl *swrm, bool enable)
+{
+	if (!swrm->clk || !swrm->handle)
+		return -EINVAL;
+
+	if (enable) {
+		swrm->clk(swrm->handle, true);
+		swrm->state = SWR_MSTR_UP;
+	} else {
+		swrm->clk(swrm->handle, false);
+		swrm->state = SWR_MSTR_DOWN;
+	}
+	return 0;
+}
+
+static int swrm_get_port_config(struct swr_master *master)
+{
+	u32 ch_rate = 0;
+	u32 num_ch = 0;
+	int i, uc_idx;
+	u32 portcount = 0;
+
+	for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
+		if (master->port[i].port_en) {
+			ch_rate += master->port[i].ch_rate;
+			num_ch += master->port[i].num_ch;
+			portcount++;
+		}
+	}
+	for (i = 0; i < ARRAY_SIZE(uc); i++) {
+		if ((uc[i].num_port == portcount) &&
+		    (uc[i].num_ch == num_ch) &&
+		    (uc[i].chrate == ch_rate)) {
+			uc_idx = i;
+			break;
+		}
+	}
+
+	if (i >= ARRAY_SIZE(uc)) {
+		dev_err(&master->dev,
+			"%s: usecase port:%d, num_ch:%d, chrate:%d not found\n",
+			__func__, master->num_port, num_ch, ch_rate);
+		return -EINVAL;
+	}
+	for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
+		if (master->port[i].port_en) {
+			master->port[i].sinterval = pp[uc_idx][i].si;
+			master->port[i].offset1 = pp[uc_idx][i].off1;
+			master->port[i].offset2 = pp[uc_idx][i].off2;
+		}
+	}
+	return 0;
+}
+
+static int swrm_get_master_port(u8 *mstr_port_id, u8 slv_port_id)
+{
+	int i;
+
+	for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
+		if (mstr_ports[i] == slv_port_id) {
+			*mstr_port_id = i;
+			return 0;
+		}
+	}
+	return -EINVAL;
+}
+
+static u32 swrm_get_packed_reg_val(u8 *cmd_id, u8 cmd_data,
+				 u8 dev_addr, u16 reg_addr)
+{
+	u32 val;
+	u8 id = *cmd_id;
+
+	if (id != SWR_BROADCAST_CMD_ID) {
+		if (id < 14)
+			id += 1;
+		else
+			id = 0;
+		*cmd_id = id;
+	}
+	val = SWR_REG_VAL_PACK(cmd_data, dev_addr, id, reg_addr);
+
+	return val;
+}
+
+static int swrm_cmd_fifo_rd_cmd(struct swr_mstr_ctrl *swrm, int *cmd_data,
+				 u8 dev_addr, u8 cmd_id, u16 reg_addr,
+				 u32 len)
+{
+	u32 val;
+	int ret = 0;
+
+	val = swrm_get_packed_reg_val(&swrm->rcmd_id, len, dev_addr, reg_addr);
+	ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_RD_CMD, val);
+	if (ret < 0) {
+		dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n",
+			__func__, val, ret);
+		goto err;
+	}
+	*cmd_data = swrm->read(swrm->handle, SWRM_CMD_FIFO_RD_FIFO_ADDR);
+	dev_dbg(swrm->dev,
+		"%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n",
+		__func__, reg_addr, cmd_id, dev_addr, *cmd_data);
+err:
+	return ret;
+}
+
+static int swrm_cmd_fifo_wr_cmd(struct swr_mstr_ctrl *swrm, u8 cmd_data,
+				 u8 dev_addr, u8 cmd_id, u16 reg_addr)
+{
+	u32 val;
+	int ret = 0;
+
+	if (!cmd_id)
+		val = swrm_get_packed_reg_val(&swrm->wcmd_id, cmd_data,
+					      dev_addr, reg_addr);
+	else
+		val = swrm_get_packed_reg_val(&cmd_id, cmd_data,
+					      dev_addr, reg_addr);
+
+	dev_dbg(swrm->dev,
+		"%s: reg: 0x%x, cmd_id: 0x%x, dev_id: 0x%x, cmd_data: 0x%x\n",
+		__func__, reg_addr, cmd_id, dev_addr, cmd_data);
+	ret = swrm->write(swrm->handle, SWRM_CMD_FIFO_WR_CMD, val);
+	if (ret < 0) {
+		dev_err(swrm->dev, "%s: reg 0x%x write failed, err:%d\n",
+			__func__, val, ret);
+		goto err;
+	}
+	if (cmd_id == 0xF)
+		wait_for_completion_timeout(&swrm->broadcast, (2 * HZ/10));
+err:
+	return ret;
+}
+
+static int swrm_read(struct swr_master *master, u8 dev_num, u16 reg_addr,
+		     void *buf, u32 len)
+{
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+	int ret = 0;
+	int val;
+	u8 *reg_val = (u8 *)buf;
+
+	if (!swrm) {
+		dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dev_num)
+		ret = swrm_cmd_fifo_rd_cmd(swrm, &val, dev_num, 0, reg_addr,
+					   len);
+	else
+		val = swrm->read(swrm->handle, reg_addr);
+
+	*reg_val = (u8)val;
+	pm_runtime_mark_last_busy(&swrm->pdev->dev);
+
+	return ret;
+}
+
+static int swrm_write(struct swr_master *master, u8 dev_num, u16 reg_addr,
+		      const void *buf)
+{
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+	int ret = 0;
+	u8 reg_val = *(u8 *)buf;
+
+	if (!swrm) {
+		dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dev_num)
+		ret = swrm_cmd_fifo_wr_cmd(swrm, reg_val, dev_num, 0, reg_addr);
+	else
+		ret = swrm->write(swrm->handle, reg_addr, reg_val);
+
+	pm_runtime_mark_last_busy(&swrm->pdev->dev);
+
+	return ret;
+}
+
+static int swrm_bulk_write(struct swr_master *master, u8 dev_num, void *reg,
+			   const void *buf, size_t len)
+{
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+	int ret = 0;
+	int i;
+	u32 *val;
+	u32 *swr_fifo_reg;
+
+	if (!swrm || !swrm->handle) {
+		dev_err(&master->dev, "%s: swrm is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (len <= 0)
+		return -EINVAL;
+
+	if (dev_num) {
+		swr_fifo_reg = kcalloc(len, sizeof(u32), GFP_KERNEL);
+		if (!swr_fifo_reg) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		val = kcalloc(len, sizeof(u32), GFP_KERNEL);
+		if (!val) {
+			ret = -ENOMEM;
+			goto mem_fail;
+		}
+
+		for (i = 0; i < len; i++) {
+			val[i] = swrm_get_packed_reg_val(&swrm->wcmd_id,
+							 ((u8 *)buf)[i],
+							 dev_num,
+							 ((u16 *)reg)[i]);
+			swr_fifo_reg[i] = SWRM_CMD_FIFO_WR_CMD;
+		}
+		ret = swrm->bulk_write(swrm->handle, swr_fifo_reg, val, len);
+		if (ret) {
+			dev_err(&master->dev, "%s: bulk write failed\n",
+				__func__);
+			ret = -EINVAL;
+		}
+	} else {
+		dev_err(&master->dev,
+			"%s: No support of Bulk write for master regs\n",
+			__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+	kfree(val);
+mem_fail:
+	kfree(swr_fifo_reg);
+err:
+	pm_runtime_mark_last_busy(&swrm->pdev->dev);
+	return ret;
+}
+
+static u8 get_inactive_bank_num(struct swr_mstr_ctrl *swrm)
+{
+	return (swrm->read(swrm->handle, SWRM_MCP_STATUS) &
+		SWRM_MCP_STATUS_BANK_NUM_MASK) ? 0 : 1;
+}
+
+static void enable_bank_switch(struct swr_mstr_ctrl *swrm, u8 bank,
+				u8 row, u8 col)
+{
+	swrm_cmd_fifo_wr_cmd(swrm, ((row << 3) | col), 0xF, 0xF,
+			SWRS_SCP_FRAME_CTRL_BANK(bank));
+}
+
+static struct swr_port_info *swrm_get_port(struct swr_master *master,
+					   u8 port_id)
+{
+	int i;
+	struct swr_port_info *port = NULL;
+
+	for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
+		port = &master->port[i];
+		if (port->port_id == port_id) {
+			dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n",
+				__func__, port_id, i);
+			return port;
+		}
+	}
+
+	return NULL;
+}
+
+static struct swr_port_info *swrm_get_avail_port(struct swr_master *master)
+{
+	int i;
+	struct swr_port_info *port = NULL;
+
+	for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
+		port = &master->port[i];
+		if (port->port_en)
+			continue;
+
+		dev_dbg(&master->dev, "%s: port_id: %d, index: %d\n",
+			__func__, port->port_id, i);
+		return port;
+	}
+
+	return NULL;
+}
+
+static struct swr_port_info *swrm_get_enabled_port(struct swr_master *master,
+						   u8 port_id)
+{
+	int i;
+	struct swr_port_info *port = NULL;
+
+	for (i = 0; i < SWR_MSTR_PORT_LEN; i++) {
+		port = &master->port[i];
+		if ((port->port_id == port_id) && (port->port_en == true))
+			break;
+	}
+	if (i == SWR_MSTR_PORT_LEN)
+		port = NULL;
+	return port;
+}
+
+static bool swrm_remove_from_group(struct swr_master *master)
+{
+	struct swr_device *swr_dev;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+	bool is_removed = false;
+
+	if (!swrm)
+		goto end;
+
+	mutex_lock(&swrm->mlock);
+	if ((swrm->num_rx_chs > 1) &&
+	    (swrm->num_rx_chs == swrm->num_cfg_devs)) {
+		list_for_each_entry(swr_dev, &master->devices,
+				dev_list) {
+			swr_dev->group_id = SWR_GROUP_NONE;
+			master->gr_sid = 0;
+		}
+		is_removed = true;
+	}
+	mutex_unlock(&swrm->mlock);
+
+end:
+	return is_removed;
+}
+
+static void swrm_cleanup_disabled_data_ports(struct swr_master *master,
+					     u8 bank)
+{
+	u32 value;
+	struct swr_port_info *port;
+	int i;
+	int port_type;
+	struct swrm_mports *mport, *mport_next = NULL;
+	int port_disable_cnt = 0;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+
+	if (!swrm) {
+		pr_err("%s: swrm is null\n", __func__);
+		return;
+	}
+
+	dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__,
+		master->num_port);
+
+	mport = list_first_entry_or_null(&swrm->mport_list,
+					struct swrm_mports,
+					list);
+	if (!mport) {
+		dev_err(swrm->dev, "%s: list is empty\n", __func__);
+		return;
+	}
+
+	for (i = 0; i < master->num_port; i++) {
+		port = swrm_get_port(master, mstr_ports[mport->id]);
+		if (!port || port->ch_en)
+			goto inc_loop;
+
+		port_disable_cnt++;
+		port_type = mstr_port_type[mport->id];
+		value = ((port->ch_en)
+				<< SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
+		value |= ((port->offset2)
+				<< SWRM_DP_PORT_CTRL_OFFSET2_SHFT);
+		value |= ((port->offset1)
+				<< SWRM_DP_PORT_CTRL_OFFSET1_SHFT);
+		value |= port->sinterval;
+
+		swrm->write(swrm->handle,
+			    SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank),
+			    value);
+		swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_id, 0x00,
+				SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank));
+
+		dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n",
+			__func__, mport->id,
+			(SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value);
+
+inc_loop:
+		mport_next = list_next_entry(mport, list);
+		if (port && !port->ch_en) {
+			list_del(&mport->list);
+			kfree(mport);
+		}
+		if (!mport_next) {
+			dev_err(swrm->dev, "%s: end of list\n", __func__);
+			break;
+		}
+		mport = mport_next;
+	}
+	master->num_port -= port_disable_cnt;
+
+	dev_dbg(swrm->dev, "%s:disable ports: %d, active ports (rem): %d\n",
+		__func__, port_disable_cnt,  master->num_port);
+}
+
+static void swrm_slvdev_datapath_control(struct swr_master *master,
+					 bool enable)
+{
+	u8 bank;
+	u32 value, n_col;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+	int mask = (SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK |
+		    SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK |
+		    SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK);
+	u8 inactive_bank;
+
+	if (!swrm) {
+		pr_err("%s: swrm is null\n", __func__);
+		return;
+	}
+
+	bank = get_inactive_bank_num(swrm);
+
+	dev_dbg(swrm->dev, "%s: enable: %d, cfg_devs: %d\n",
+		__func__, enable, swrm->num_cfg_devs);
+
+	if (enable) {
+		/* set Row = 48 and col = 16 */
+		n_col = SWR_MAX_COL;
+	} else {
+		/*
+		 * Do not change to 48x2 if number of channels configured
+		 * as stereo and if disable datapath is called for the
+		 * first slave device
+		 */
+		if (swrm->num_cfg_devs > 0)
+			n_col = SWR_MAX_COL;
+		else
+			n_col = SWR_MIN_COL;
+
+		/*
+		 * All ports are already disabled, no need to perform
+		 * bank-switch and copy operation. This case can arise
+		 * when speaker channels are enabled in stereo mode with
+		 * BROADCAST and disabled in GROUP_NONE
+		 */
+		if (master->num_port == 0)
+			return;
+	}
+
+	value = swrm->read(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank));
+	value &= (~mask);
+	value |= ((0 << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) |
+		  (n_col << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) |
+		  (0 << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT));
+	swrm->write(swrm->handle, SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value);
+
+	dev_dbg(swrm->dev, "%s: regaddr: 0x%x, value: 0x%x\n", __func__,
+		SWRM_MCP_FRAME_CTRL_BANK_ADDR(bank), value);
+
+	enable_bank_switch(swrm, bank, SWR_MAX_ROW, n_col);
+
+	inactive_bank = bank ? 0 : 1;
+	if (enable)
+		swrm_copy_data_port_config(master, inactive_bank);
+	else
+		swrm_cleanup_disabled_data_ports(master, inactive_bank);
+
+	if (!swrm_is_port_en(master)) {
+		dev_dbg(&master->dev, "%s: pm_runtime auto suspend triggered\n",
+			__func__);
+		pm_runtime_mark_last_busy(&swrm->pdev->dev);
+		pm_runtime_put_autosuspend(&swrm->pdev->dev);
+	}
+}
+
+static void swrm_apply_port_config(struct swr_master *master)
+{
+	u8 bank;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+
+	if (!swrm) {
+		pr_err("%s: Invalid handle to swr controller\n",
+			__func__);
+		return;
+	}
+
+	bank = get_inactive_bank_num(swrm);
+	dev_dbg(swrm->dev, "%s: enter bank: %d master_ports: %d\n",
+		__func__, bank, master->num_port);
+
+
+	swrm_cmd_fifo_wr_cmd(swrm, 0x01, 0xF, 0x00,
+			SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(bank));
+
+	swrm_copy_data_port_config(master, bank);
+}
+
+static void swrm_copy_data_port_config(struct swr_master *master, u8 bank)
+{
+	u32 value;
+	struct swr_port_info *port;
+	int i;
+	int port_type;
+	struct swrm_mports *mport;
+	u32 reg[SWRM_MAX_PORT_REG];
+	u32 val[SWRM_MAX_PORT_REG];
+	int len = 0;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+
+	if (!swrm) {
+		pr_err("%s: swrm is null\n", __func__);
+		return;
+	}
+
+	dev_dbg(swrm->dev, "%s: master num_port: %d\n", __func__,
+		master->num_port);
+
+	mport = list_first_entry_or_null(&swrm->mport_list,
+					struct swrm_mports,
+					list);
+	if (!mport) {
+		dev_err(swrm->dev, "%s: list is empty\n", __func__);
+		return;
+	}
+	for (i = 0; i < master->num_port; i++) {
+
+		port = swrm_get_enabled_port(master, mstr_ports[mport->id]);
+		if (!port)
+			continue;
+		port_type = mstr_port_type[mport->id];
+		if (!port->dev_id || (port->dev_id > master->num_dev)) {
+			dev_dbg(swrm->dev, "%s: invalid device id = %d\n",
+				__func__, port->dev_id);
+			continue;
+		}
+		value = ((port->ch_en)
+				<< SWRM_DP_PORT_CTRL_EN_CHAN_SHFT);
+		value |= ((port->offset2)
+				<< SWRM_DP_PORT_CTRL_OFFSET2_SHFT);
+		value |= ((port->offset1)
+				<< SWRM_DP_PORT_CTRL_OFFSET1_SHFT);
+		value |= port->sinterval;
+
+		reg[len] = SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank);
+		val[len++] = value;
+
+		dev_dbg(swrm->dev, "%s: mport :%d, reg: 0x%x, val: 0x%x\n",
+			__func__, mport->id,
+			(SWRM_DP_PORT_CTRL_BANK((mport->id+1), bank)), value);
+
+		reg[len] = SWRM_CMD_FIFO_WR_CMD;
+		val[len++] = SWR_REG_VAL_PACK(port->ch_en, port->dev_id, 0x00,
+				SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank));
+
+		reg[len] = SWRM_CMD_FIFO_WR_CMD;
+		val[len++] = SWR_REG_VAL_PACK(port->sinterval,
+				port->dev_id, 0x00,
+				SWRS_DP_SAMPLE_CONTROL_1_BANK(port_type, bank));
+
+		reg[len] = SWRM_CMD_FIFO_WR_CMD;
+		val[len++] = SWR_REG_VAL_PACK(port->offset1,
+				port->dev_id, 0x00,
+				SWRS_DP_OFFSET_CONTROL_1_BANK(port_type, bank));
+
+		if (port_type != 0) {
+			reg[len] = SWRM_CMD_FIFO_WR_CMD;
+			val[len++] = SWR_REG_VAL_PACK(port->offset2,
+					port->dev_id, 0x00,
+					SWRS_DP_OFFSET_CONTROL_2_BANK(port_type,
+									bank));
+		}
+		mport = list_next_entry(mport, list);
+		if (!mport) {
+			dev_err(swrm->dev, "%s: end of list\n", __func__);
+			break;
+		}
+	}
+	swrm->bulk_write(swrm->handle, reg, val, len);
+}
+
+static int swrm_connect_port(struct swr_master *master,
+			struct swr_params *portinfo)
+{
+	int i;
+	struct swr_port_info *port;
+	int ret = 0;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+	struct swrm_mports *mport;
+	struct list_head *ptr, *next;
+
+	dev_dbg(&master->dev, "%s: enter\n", __func__);
+	if (!portinfo)
+		return -EINVAL;
+
+	if (!swrm) {
+		dev_err(&master->dev,
+			"%s: Invalid handle to swr controller\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&swrm->mlock);
+	if (!swrm_is_port_en(master))
+		pm_runtime_get_sync(&swrm->pdev->dev);
+
+	for (i = 0; i < portinfo->num_port; i++) {
+		mport = kzalloc(sizeof(struct swrm_mports), GFP_KERNEL);
+		if (!mport) {
+			ret = -ENOMEM;
+			goto mem_fail;
+		}
+		ret = swrm_get_master_port(&mport->id,
+						portinfo->port_id[i]);
+		if (ret < 0) {
+			dev_err(&master->dev,
+				"%s: mstr portid for slv port %d not found\n",
+				__func__, portinfo->port_id[i]);
+			goto port_fail;
+		}
+		port = swrm_get_avail_port(master);
+		if (!port) {
+			dev_err(&master->dev,
+				"%s: avail ports not found!\n", __func__);
+			goto port_fail;
+		}
+		list_add(&mport->list, &swrm->mport_list);
+		port->dev_id = portinfo->dev_id;
+		port->port_id = portinfo->port_id[i];
+		port->num_ch = portinfo->num_ch[i];
+		port->ch_rate = portinfo->ch_rate[i];
+		port->ch_en = portinfo->ch_en[i];
+		port->port_en = true;
+		dev_dbg(&master->dev,
+			"%s: mstr port %d, slv port %d ch_rate %d num_ch %d\n",
+			__func__, mport->id, port->port_id, port->ch_rate,
+			port->num_ch);
+	}
+	master->num_port += portinfo->num_port;
+	if (master->num_port >= SWR_MSTR_PORT_LEN)
+		master->num_port = SWR_MSTR_PORT_LEN;
+
+	swrm_get_port_config(master);
+	swr_port_response(master, portinfo->tid);
+	swrm->num_cfg_devs += 1;
+	dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d\n",
+		__func__, swrm->num_cfg_devs, swrm->num_rx_chs);
+	if (swrm->num_rx_chs > 1) {
+		if (swrm->num_rx_chs == swrm->num_cfg_devs)
+			swrm_apply_port_config(master);
+	} else {
+		swrm_apply_port_config(master);
+	}
+	mutex_unlock(&swrm->mlock);
+	return 0;
+
+port_fail:
+	kfree(mport);
+mem_fail:
+	list_for_each_safe(ptr, next, &swrm->mport_list) {
+		mport = list_entry(ptr, struct swrm_mports, list);
+		for (i = 0; i < portinfo->num_port; i++) {
+			if (portinfo->port_id[i] == mstr_ports[mport->id]) {
+				port = swrm_get_port(master,
+						portinfo->port_id[i]);
+				if (port)
+					port->ch_en = false;
+				list_del(&mport->list);
+				kfree(mport);
+				break;
+			}
+		}
+	}
+	mutex_unlock(&swrm->mlock);
+	return ret;
+}
+
+static int swrm_disconnect_port(struct swr_master *master,
+			struct swr_params *portinfo)
+{
+	int i;
+	struct swr_port_info *port;
+	u8 bank;
+	u32 value;
+	int ret = 0;
+	u8 mport_id = 0;
+	int port_type = 0;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(master);
+
+	if (!swrm) {
+		dev_err(&master->dev,
+			"%s: Invalid handle to swr controller\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!portinfo) {
+		dev_err(&master->dev, "%s: portinfo is NULL\n", __func__);
+		return -EINVAL;
+	}
+	mutex_lock(&swrm->mlock);
+	bank = get_inactive_bank_num(swrm);
+	for (i = 0; i < portinfo->num_port; i++) {
+		ret = swrm_get_master_port(&mport_id,
+						portinfo->port_id[i]);
+		if (ret < 0) {
+			dev_err(&master->dev,
+				"%s: mstr portid for slv port %d not found\n",
+				__func__, portinfo->port_id[i]);
+			mutex_unlock(&swrm->mlock);
+			return -EINVAL;
+		}
+		port = swrm_get_enabled_port(master, portinfo->port_id[i]);
+		if (!port) {
+			dev_dbg(&master->dev, "%s: port %d already disabled\n",
+				__func__, portinfo->port_id[i]);
+			continue;
+		}
+		port_type = mstr_port_type[mport_id];
+		port->dev_id = portinfo->dev_id;
+		port->port_en = false;
+		port->ch_en = 0;
+		value = port->ch_en << SWRM_DP_PORT_CTRL_EN_CHAN_SHFT;
+		value |= (port->offset2 << SWRM_DP_PORT_CTRL_OFFSET2_SHFT);
+		value |= (port->offset1 << SWRM_DP_PORT_CTRL_OFFSET1_SHFT);
+		value |= port->sinterval;
+
+
+		swrm->write(swrm->handle,
+			    SWRM_DP_PORT_CTRL_BANK((mport_id+1), bank),
+			    value);
+		swrm_cmd_fifo_wr_cmd(swrm, 0x00, port->dev_id, 0x00,
+				SWRS_DP_CHANNEL_ENABLE_BANK(port_type, bank));
+	}
+
+	swr_port_response(master, portinfo->tid);
+	swrm->num_cfg_devs -= 1;
+	dev_dbg(&master->dev, "%s: cfg_devs: %d, rx_chs: %d, active ports: %d\n",
+		__func__, swrm->num_cfg_devs, swrm->num_rx_chs,
+		master->num_port);
+	mutex_unlock(&swrm->mlock);
+
+	return 0;
+}
+
+static int swrm_check_slave_change_status(struct swr_mstr_ctrl *swrm,
+					int status, u8 *devnum)
+{
+	int i;
+	int new_sts = status;
+	int ret = SWR_NOT_PRESENT;
+
+	if (status != swrm->slave_status) {
+		for (i = 0; i < (swrm->master.num_dev + 1); i++) {
+			if ((status & SWRM_MCP_SLV_STATUS_MASK) !=
+			    (swrm->slave_status & SWRM_MCP_SLV_STATUS_MASK)) {
+				ret = (status & SWRM_MCP_SLV_STATUS_MASK);
+				*devnum = i;
+				break;
+			}
+			status >>= 2;
+			swrm->slave_status >>= 2;
+		}
+		swrm->slave_status = new_sts;
+	}
+	return ret;
+}
+
+static irqreturn_t swr_mstr_interrupt(int irq, void *dev)
+{
+	struct swr_mstr_ctrl *swrm = dev;
+	u32 value, intr_sts;
+	int status, chg_sts, i;
+	u8 devnum = 0;
+	int ret = IRQ_HANDLED;
+
+	pm_runtime_get_sync(&swrm->pdev->dev);
+	intr_sts = swrm->read(swrm->handle, SWRM_INTERRUPT_STATUS);
+	intr_sts &= SWRM_INTERRUPT_STATUS_RMSK;
+	for (i = 0; i < SWRM_INTERRUPT_MAX; i++) {
+		value = intr_sts & (1 << i);
+		if (!value)
+			continue;
+
+		swrm->write(swrm->handle, SWRM_INTERRUPT_CLEAR, value);
+		switch (value) {
+		case SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ:
+			dev_dbg(swrm->dev, "SWR slave pend irq\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED:
+			dev_dbg(swrm->dev, "SWR new slave attached\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS:
+			status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS);
+			if (status == swrm->slave_status) {
+				dev_dbg(swrm->dev,
+					"%s: No change in slave status: %d\n",
+					__func__, status);
+				break;
+			}
+			chg_sts = swrm_check_slave_change_status(swrm, status,
+								&devnum);
+			switch (chg_sts) {
+			case SWR_NOT_PRESENT:
+				dev_dbg(swrm->dev, "device %d got detached\n",
+					devnum);
+				break;
+			case SWR_ATTACHED_OK:
+				dev_dbg(swrm->dev, "device %d got attached\n",
+					devnum);
+				break;
+			case SWR_ALERT:
+				dev_dbg(swrm->dev,
+					"device %d has pending interrupt\n",
+					devnum);
+				break;
+			}
+			break;
+		case SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET:
+			dev_err_ratelimited(swrm->dev, "SWR bus clash detected\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW:
+			dev_dbg(swrm->dev, "SWR read FIFO overflow\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW:
+			dev_dbg(swrm->dev, "SWR read FIFO underflow\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW:
+			dev_dbg(swrm->dev, "SWR write FIFO overflow\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_CMD_ERROR:
+			value = swrm->read(swrm->handle, SWRM_CMD_FIFO_STATUS);
+			dev_err_ratelimited(swrm->dev,
+			"SWR CMD error, fifo status 0x%x, flushing fifo\n",
+					    value);
+			swrm->write(swrm->handle, SWRM_CMD_FIFO_CMD, 0x1);
+			break;
+		case SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION:
+			dev_dbg(swrm->dev, "SWR Port collision detected\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH:
+			dev_dbg(swrm->dev, "SWR read enable valid mismatch\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED:
+			complete(&swrm->broadcast);
+			dev_dbg(swrm->dev, "SWR cmd id finished\n");
+			break;
+		case SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED:
+			break;
+		case SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED:
+			break;
+		case SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL:
+			break;
+		case SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED:
+			complete(&swrm->reset);
+			break;
+		case SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED:
+			break;
+		default:
+			dev_err_ratelimited(swrm->dev, "SWR unknown interrupt\n");
+			ret = IRQ_NONE;
+			break;
+		}
+	}
+	pm_runtime_mark_last_busy(&swrm->pdev->dev);
+	pm_runtime_put_autosuspend(&swrm->pdev->dev);
+	return ret;
+}
+
+static int swrm_get_device_status(struct swr_mstr_ctrl *swrm, u8 devnum)
+{
+	u32 val;
+
+	swrm->slave_status = swrm->read(swrm->handle, SWRM_MCP_SLV_STATUS);
+	val = (swrm->slave_status >> (devnum * 2));
+	val &= SWRM_MCP_SLV_STATUS_MASK;
+	return val;
+}
+
+static int swrm_get_logical_dev_num(struct swr_master *mstr, u64 dev_id,
+				u8 *dev_num)
+{
+	int i;
+	u64 id = 0;
+	int ret = -EINVAL;
+	struct swr_mstr_ctrl *swrm = swr_get_ctrl_data(mstr);
+
+	if (!swrm) {
+		pr_err("%s: Invalid handle to swr controller\n",
+			__func__);
+		return ret;
+	}
+
+	pm_runtime_get_sync(&swrm->pdev->dev);
+	for (i = 1; i < (mstr->num_dev + 1); i++) {
+		id = ((u64)(swrm->read(swrm->handle,
+			    SWRM_ENUMERATOR_SLAVE_DEV_ID_2(i))) << 32);
+		id |= swrm->read(swrm->handle,
+			    SWRM_ENUMERATOR_SLAVE_DEV_ID_1(i));
+		if ((id & SWR_DEV_ID_MASK) == dev_id) {
+			if (swrm_get_device_status(swrm, i) == 0x01) {
+				*dev_num = i;
+				ret = 0;
+			} else {
+				dev_err(swrm->dev, "%s: device is not ready\n",
+					 __func__);
+			}
+			goto found;
+		}
+	}
+	dev_err(swrm->dev, "%s: device id 0x%llx does not match with 0x%llx\n",
+		__func__, id, dev_id);
+found:
+	pm_runtime_mark_last_busy(&swrm->pdev->dev);
+	pm_runtime_put_autosuspend(&swrm->pdev->dev);
+	return ret;
+}
+static int swrm_master_init(struct swr_mstr_ctrl *swrm)
+{
+	int ret = 0;
+	u32 val;
+	u8 row_ctrl = SWR_MAX_ROW;
+	u8 col_ctrl = SWR_MIN_COL;
+	u8 ssp_period = 1;
+	u8 retry_cmd_num = 3;
+	u32 reg[SWRM_MAX_INIT_REG];
+	u32 value[SWRM_MAX_INIT_REG];
+	int len = 0;
+
+	/* Clear Rows and Cols */
+	val = ((row_ctrl << SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT) |
+		(col_ctrl << SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT) |
+		(ssp_period << SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT));
+
+	reg[len] = SWRM_MCP_FRAME_CTRL_BANK_ADDR(0);
+	value[len++] = val;
+
+	/* Set Auto enumeration flag */
+	reg[len] = SWRM_ENUMERATOR_CFG_ADDR;
+	value[len++] = 1;
+
+	/* Mask soundwire interrupts */
+	reg[len] = SWRM_INTERRUPT_MASK_ADDR;
+	value[len++] = 0x1FFFD;
+
+	/* Configure No pings */
+	val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR);
+	val &= ~SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK;
+	val |= (0x1f << SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT);
+	reg[len] = SWRM_MCP_CFG_ADDR;
+	value[len++] = val;
+
+	/* Configure number of retries of a read/write cmd */
+	val = (retry_cmd_num << SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT);
+	reg[len] = SWRM_CMD_FIFO_CFG_ADDR;
+	value[len++] = val;
+
+	/* Set IRQ to PULSE */
+	reg[len] = SWRM_COMP_CFG_ADDR;
+	value[len++] = 0x02;
+
+	reg[len] = SWRM_COMP_CFG_ADDR;
+	value[len++] = 0x03;
+
+	reg[len] = SWRM_INTERRUPT_CLEAR;
+	value[len++] = 0x08;
+
+	swrm->bulk_write(swrm->handle, reg, value, len);
+
+	return ret;
+}
+
+static int swrm_probe(struct platform_device *pdev)
+{
+	struct swr_mstr_ctrl *swrm;
+	struct swr_ctrl_platform_data *pdata;
+	struct swr_device *swr_dev, *safe;
+	int ret;
+
+	/* Allocate soundwire master driver structure */
+	swrm = kzalloc(sizeof(struct swr_mstr_ctrl), GFP_KERNEL);
+	if (!swrm) {
+		ret = -ENOMEM;
+		goto err_memory_fail;
+	}
+	swrm->dev = &pdev->dev;
+	swrm->pdev = pdev;
+	platform_set_drvdata(pdev, swrm);
+	swr_set_ctrl_data(&swrm->master, swrm);
+	pdata = dev_get_platdata(&pdev->dev);
+	if (!pdata) {
+		dev_err(&pdev->dev, "%s: pdata from parent is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->handle = (void *)pdata->handle;
+	if (!swrm->handle) {
+		dev_err(&pdev->dev, "%s: swrm->handle is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->read = pdata->read;
+	if (!swrm->read) {
+		dev_err(&pdev->dev, "%s: swrm->read is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->write = pdata->write;
+	if (!swrm->write) {
+		dev_err(&pdev->dev, "%s: swrm->write is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->bulk_write = pdata->bulk_write;
+	if (!swrm->bulk_write) {
+		dev_err(&pdev->dev, "%s: swrm->bulk_write is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->clk = pdata->clk;
+	if (!swrm->clk) {
+		dev_err(&pdev->dev, "%s: swrm->clk is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->reg_irq = pdata->reg_irq;
+	if (!swrm->reg_irq) {
+		dev_err(&pdev->dev, "%s: swrm->reg_irq is NULL\n",
+			__func__);
+		ret = -EINVAL;
+		goto err_pdata_fail;
+	}
+	swrm->master.read = swrm_read;
+	swrm->master.write = swrm_write;
+	swrm->master.bulk_write = swrm_bulk_write;
+	swrm->master.get_logical_dev_num = swrm_get_logical_dev_num;
+	swrm->master.connect_port = swrm_connect_port;
+	swrm->master.disconnect_port = swrm_disconnect_port;
+	swrm->master.slvdev_datapath_control = swrm_slvdev_datapath_control;
+	swrm->master.remove_from_group = swrm_remove_from_group;
+	swrm->master.dev.parent = &pdev->dev;
+	swrm->master.dev.of_node = pdev->dev.of_node;
+	swrm->master.num_port = 0;
+	swrm->num_enum_slaves = 0;
+	swrm->rcmd_id = 0;
+	swrm->wcmd_id = 0;
+	swrm->slave_status = 0;
+	swrm->num_rx_chs = 0;
+	swrm->state = SWR_MSTR_RESUME;
+	init_completion(&swrm->reset);
+	init_completion(&swrm->broadcast);
+	mutex_init(&swrm->mlock);
+	INIT_LIST_HEAD(&swrm->mport_list);
+	mutex_init(&swrm->reslock);
+
+	ret = swrm->reg_irq(swrm->handle, swr_mstr_interrupt, swrm,
+			    SWR_IRQ_REGISTER);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: IRQ register failed ret %d\n",
+			__func__, ret);
+		goto err_irq_fail;
+	}
+
+	ret = swr_register_master(&swrm->master);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: error adding swr master\n", __func__);
+		goto err_mstr_fail;
+	}
+
+	if (pdev->dev.of_node)
+		of_register_swr_devices(&swrm->master);
+
+	/* Add devices registered with board-info as the
+	 * controller will be up now
+	 */
+	swr_master_add_boarddevices(&swrm->master);
+	mutex_lock(&swrm->mlock);
+	swrm_clk_request(swrm, true);
+	ret = swrm_master_init(swrm);
+	if (ret < 0) {
+		dev_err(&pdev->dev,
+			"%s: Error in master Initializaiton, err %d\n",
+			__func__, ret);
+		mutex_unlock(&swrm->mlock);
+		goto err_mstr_fail;
+	}
+
+	/* Enumerate slave devices */
+	list_for_each_entry_safe(swr_dev, safe, &swrm->master.devices,
+				 dev_list) {
+		ret = swr_startup_devices(swr_dev);
+		if (ret)
+			list_del(&swr_dev->dev_list);
+	}
+	mutex_unlock(&swrm->mlock);
+
+	dbgswrm = swrm;
+	debugfs_swrm_dent = debugfs_create_dir(dev_name(&pdev->dev), 0);
+	if (!IS_ERR(debugfs_swrm_dent)) {
+		debugfs_peek = debugfs_create_file("swrm_peek",
+				S_IFREG | 0444, debugfs_swrm_dent,
+				(void *) "swrm_peek", &swrm_debug_ops);
+
+		debugfs_poke = debugfs_create_file("swrm_poke",
+				S_IFREG | 0444, debugfs_swrm_dent,
+				(void *) "swrm_poke", &swrm_debug_ops);
+
+		debugfs_reg_dump = debugfs_create_file("swrm_reg_dump",
+				   S_IFREG | 0444, debugfs_swrm_dent,
+				   (void *) "swrm_reg_dump",
+				   &swrm_debug_ops);
+	}
+	pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer);
+	pm_runtime_use_autosuspend(&pdev->dev);
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+	pm_runtime_mark_last_busy(&pdev->dev);
+
+	return 0;
+err_mstr_fail:
+	swrm->reg_irq(swrm->handle, swr_mstr_interrupt,
+			swrm, SWR_IRQ_FREE);
+err_irq_fail:
+err_pdata_fail:
+	kfree(swrm);
+err_memory_fail:
+	return ret;
+}
+
+static int swrm_remove(struct platform_device *pdev)
+{
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+
+	swrm->reg_irq(swrm->handle, swr_mstr_interrupt,
+			swrm, SWR_IRQ_FREE);
+	if (swrm->mstr_port) {
+		kfree(swrm->mstr_port->port);
+		swrm->mstr_port->port = NULL;
+		kfree(swrm->mstr_port);
+		swrm->mstr_port = NULL;
+	}
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_set_suspended(&pdev->dev);
+	swr_unregister_master(&swrm->master);
+	mutex_destroy(&swrm->mlock);
+	mutex_destroy(&swrm->reslock);
+	kfree(swrm);
+	return 0;
+}
+
+static int swrm_clk_pause(struct swr_mstr_ctrl *swrm)
+{
+	u32 val;
+
+	dev_dbg(swrm->dev, "%s: state: %d\n", __func__, swrm->state);
+	swrm->write(swrm->handle, SWRM_INTERRUPT_MASK_ADDR, 0x1FDFD);
+	val = swrm->read(swrm->handle, SWRM_MCP_CFG_ADDR);
+	val |= SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK;
+	swrm->write(swrm->handle, SWRM_MCP_CFG_ADDR, val);
+	swrm->state = SWR_MSTR_PAUSE;
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int swrm_runtime_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+	int ret = 0;
+	struct swr_master *mstr = &swrm->master;
+	struct swr_device *swr_dev;
+
+	dev_dbg(dev, "%s: pm_runtime: resume, state:%d\n",
+		__func__, swrm->state);
+	mutex_lock(&swrm->reslock);
+	if ((swrm->state == SWR_MSTR_PAUSE) ||
+	    (swrm->state == SWR_MSTR_DOWN)) {
+		if (swrm->state == SWR_MSTR_DOWN) {
+			if (swrm_clk_request(swrm, true))
+				goto exit;
+		}
+		list_for_each_entry(swr_dev, &mstr->devices, dev_list) {
+			ret = swr_device_up(swr_dev);
+			if (ret) {
+				dev_err(dev,
+					"%s: failed to wakeup swr dev %d\n",
+					__func__, swr_dev->dev_num);
+				swrm_clk_request(swrm, false);
+				goto exit;
+			}
+		}
+		swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01);
+		swrm->write(swrm->handle, SWRM_COMP_SW_RESET, 0x01);
+		swrm_master_init(swrm);
+	}
+exit:
+	pm_runtime_set_autosuspend_delay(&pdev->dev, auto_suspend_timer);
+	mutex_unlock(&swrm->reslock);
+	return ret;
+}
+
+static int swrm_runtime_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+	int ret = 0;
+	struct swr_master *mstr = &swrm->master;
+	struct swr_device *swr_dev;
+
+	dev_dbg(dev, "%s: pm_runtime: suspend state: %d\n",
+		__func__, swrm->state);
+	mutex_lock(&swrm->reslock);
+	if ((swrm->state == SWR_MSTR_RESUME) ||
+	    (swrm->state == SWR_MSTR_UP)) {
+		if (swrm_is_port_en(&swrm->master)) {
+			dev_dbg(dev, "%s ports are enabled\n", __func__);
+			ret = -EBUSY;
+			goto exit;
+		}
+		swrm_clk_pause(swrm);
+		swrm->write(swrm->handle, SWRM_COMP_CFG_ADDR, 0x00);
+		list_for_each_entry(swr_dev, &mstr->devices, dev_list) {
+			ret = swr_device_down(swr_dev);
+			if (ret) {
+				dev_err(dev,
+					"%s: failed to shutdown swr dev %d\n",
+					__func__, swr_dev->dev_num);
+				goto exit;
+			}
+		}
+		swrm_clk_request(swrm, false);
+	}
+exit:
+	mutex_unlock(&swrm->reslock);
+	return ret;
+}
+#endif /* CONFIG_PM */
+
+static int swrm_device_down(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+	int ret = 0;
+	struct swr_master *mstr = &swrm->master;
+	struct swr_device *swr_dev;
+
+	dev_dbg(dev, "%s: swrm state: %d\n", __func__, swrm->state);
+	mutex_lock(&swrm->reslock);
+	if ((swrm->state == SWR_MSTR_RESUME) ||
+	    (swrm->state == SWR_MSTR_UP)) {
+		list_for_each_entry(swr_dev, &mstr->devices, dev_list) {
+			ret = swr_device_down(swr_dev);
+			if (ret)
+				dev_err(dev,
+					"%s: failed to shutdown swr dev %d\n",
+					__func__, swr_dev->dev_num);
+		}
+		dev_dbg(dev, "%s: Shutting down SWRM\n", __func__);
+		pm_runtime_disable(dev);
+		pm_runtime_set_suspended(dev);
+		pm_runtime_enable(dev);
+		swrm_clk_request(swrm, false);
+	}
+	mutex_unlock(&swrm->reslock);
+	return ret;
+}
+
+/**
+ * swrm_wcd_notify - parent device can notify to soundwire master through
+ * this function
+ * @pdev: pointer to platform device structure
+ * @id: command id from parent to the soundwire master
+ * @data: data from parent device to soundwire master
+ */
+int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data)
+{
+	struct swr_mstr_ctrl *swrm;
+	int ret = 0;
+	struct swr_master *mstr;
+	struct swr_device *swr_dev;
+
+	if (!pdev) {
+		pr_err("%s: pdev is NULL\n", __func__);
+		return -EINVAL;
+	}
+	swrm = platform_get_drvdata(pdev);
+	if (!swrm) {
+		dev_err(&pdev->dev, "%s: swrm is NULL\n", __func__);
+		return -EINVAL;
+	}
+	mstr = &swrm->master;
+
+	switch (id) {
+	case SWR_CH_MAP:
+		if (!data) {
+			dev_err(swrm->dev, "%s: data is NULL\n", __func__);
+			ret = -EINVAL;
+		} else {
+			ret = swrm_set_ch_map(swrm, data);
+		}
+		break;
+	case SWR_DEVICE_DOWN:
+		dev_dbg(swrm->dev, "%s: swr master down called\n", __func__);
+		mutex_lock(&swrm->mlock);
+		if ((swrm->state == SWR_MSTR_PAUSE) ||
+		    (swrm->state == SWR_MSTR_DOWN))
+			dev_dbg(swrm->dev, "%s: SWR master is already Down: %d\n",
+				__func__, swrm->state);
+		else
+			swrm_device_down(&pdev->dev);
+		mutex_unlock(&swrm->mlock);
+		break;
+	case SWR_DEVICE_UP:
+		dev_dbg(swrm->dev, "%s: swr master up called\n", __func__);
+		mutex_lock(&swrm->mlock);
+		mutex_lock(&swrm->reslock);
+		if ((swrm->state == SWR_MSTR_RESUME) ||
+		    (swrm->state == SWR_MSTR_UP)) {
+			dev_dbg(swrm->dev, "%s: SWR master is already UP: %d\n",
+				__func__, swrm->state);
+		} else {
+			pm_runtime_mark_last_busy(&pdev->dev);
+			mutex_unlock(&swrm->reslock);
+			pm_runtime_get_sync(&pdev->dev);
+			mutex_lock(&swrm->reslock);
+			list_for_each_entry(swr_dev, &mstr->devices, dev_list) {
+				ret = swr_reset_device(swr_dev);
+				if (ret) {
+					dev_err(swrm->dev,
+						"%s: failed to reset swr device %d\n",
+						__func__, swr_dev->dev_num);
+					swrm_clk_request(swrm, false);
+				}
+			}
+			pm_runtime_mark_last_busy(&pdev->dev);
+			pm_runtime_put_autosuspend(&pdev->dev);
+		}
+		mutex_unlock(&swrm->reslock);
+		mutex_unlock(&swrm->mlock);
+		break;
+	case SWR_SET_NUM_RX_CH:
+		if (!data) {
+			dev_err(swrm->dev, "%s: data is NULL\n", __func__);
+			ret = -EINVAL;
+		} else {
+			mutex_lock(&swrm->mlock);
+			swrm->num_rx_chs = *(int *)data;
+			if ((swrm->num_rx_chs > 1) && !swrm->num_cfg_devs) {
+				list_for_each_entry(swr_dev, &mstr->devices,
+						    dev_list) {
+					ret = swr_set_device_group(swr_dev,
+								SWR_BROADCAST);
+					if (ret)
+						dev_err(swrm->dev,
+							"%s: set num ch failed\n",
+							__func__);
+				}
+			} else {
+				list_for_each_entry(swr_dev, &mstr->devices,
+						    dev_list) {
+					ret = swr_set_device_group(swr_dev,
+								SWR_GROUP_NONE);
+					if (ret)
+						dev_err(swrm->dev,
+							"%s: set num ch failed\n",
+							__func__);
+				}
+			}
+			mutex_unlock(&swrm->mlock);
+		}
+		break;
+	default:
+		dev_err(swrm->dev, "%s: swr master unknown id %d\n",
+			__func__, id);
+		break;
+	}
+	return ret;
+}
+EXPORT_SYMBOL(swrm_wcd_notify);
+
+#ifdef CONFIG_PM_SLEEP
+static int swrm_suspend(struct device *dev)
+{
+	int ret = -EBUSY;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s: system suspend, state: %d\n", __func__, swrm->state);
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspended(dev)) {
+		ret = swrm_runtime_suspend(dev);
+		if (!ret) {
+			/*
+			 * Synchronize runtime-pm and system-pm states:
+			 * At this point, we are already suspended. If
+			 * runtime-pm still thinks its active, then
+			 * make sure its status is in sync with HW
+			 * status. The three below calls let the
+			 * runtime-pm know that we are suspended
+			 * already without re-invoking the suspend
+			 * callback
+			 */
+			pm_runtime_disable(dev);
+			pm_runtime_set_suspended(dev);
+			pm_runtime_enable(dev);
+		}
+	}
+	if (ret == -EBUSY) {
+		/*
+		 * There is a possibility that some audio stream is active
+		 * during suspend. We dont want to return suspend failure in
+		 * that case so that display and relevant components can still
+		 * go to suspend.
+		 * If there is some other error, then it should be passed-on
+		 * to system level suspend
+		 */
+		ret = 0;
+	}
+	return ret;
+}
+
+static int swrm_resume(struct device *dev)
+{
+	int ret = 0;
+	struct platform_device *pdev = to_platform_device(dev);
+	struct swr_mstr_ctrl *swrm = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s: system resume, state: %d\n", __func__, swrm->state);
+	if (!pm_runtime_enabled(dev) || !pm_runtime_suspend(dev)) {
+		ret = swrm_runtime_resume(dev);
+		if (!ret) {
+			pm_runtime_mark_last_busy(dev);
+			pm_request_autosuspend(dev);
+		}
+	}
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops swrm_dev_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(
+		swrm_suspend,
+		swrm_resume
+	)
+	SET_RUNTIME_PM_OPS(
+		swrm_runtime_suspend,
+		swrm_runtime_resume,
+		NULL
+	)
+};
+
+static const struct of_device_id swrm_dt_match[] = {
+	{
+		.compatible = "qcom,swr-wcd",
+	},
+	{}
+};
+
+static struct platform_driver swr_mstr_driver = {
+	.probe = swrm_probe,
+	.remove = swrm_remove,
+	.driver = {
+		.name = SWR_WCD_NAME,
+		.owner = THIS_MODULE,
+		.pm = &swrm_dev_pm_ops,
+		.of_match_table = swrm_dt_match,
+	},
+};
+
+static int __init swrm_init(void)
+{
+	return platform_driver_register(&swr_mstr_driver);
+}
+subsys_initcall(swrm_init);
+
+static void __exit swrm_exit(void)
+{
+	platform_driver_unregister(&swr_mstr_driver);
+}
+module_exit(swrm_exit);
+
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("WCD SoundWire Controller");
+MODULE_ALIAS("platform:swr-wcd");
diff --git a/drivers/soundwire/swr-wcd-ctrl.h b/drivers/soundwire/swr-wcd-ctrl.h
new file mode 100644
index 0000000..8992318
--- /dev/null
+++ b/drivers/soundwire/swr-wcd-ctrl.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SWR_WCD_CTRL_H
+#define _SWR_WCD_CTRL_H
+#include <linux/module.h>
+#include <linux/soundwire/swr-wcd.h>
+
+#define SWR_MAX_ROW		0 /* Rows = 48 */
+#define SWR_MAX_COL		7 /* Cols = 16 */
+#define SWR_MIN_COL		0 /* Cols = 2 */
+
+#define SWR_WCD_NAME	"swr-wcd"
+
+#define SWR_MSTR_PORT_LEN	8 /* Number of master ports */
+
+enum {
+	SWR_MSTR_PAUSE,
+	SWR_MSTR_RESUME,
+	SWR_MSTR_UP,
+	SWR_MSTR_DOWN,
+};
+
+enum {
+	SWR_IRQ_FREE,
+	SWR_IRQ_REGISTER,
+};
+
+enum {
+	SWR_DAC_PORT,
+	SWR_COMP_PORT,
+	SWR_BOOST_PORT,
+	SWR_VISENSE_PORT,
+};
+
+struct usecase {
+	u8 num_port;
+	u8 num_ch;
+	u32 chrate;
+};
+
+struct port_params {
+	u8 si;
+	u8 off1;
+	u8 off2;
+};
+
+struct swrm_mports {
+	struct list_head list;
+	u8 id;
+};
+
+struct swr_ctrl_platform_data {
+	void *handle; /* holds priv data */
+	int (*read)(void *handle, int reg);
+	int (*write)(void *handle, int reg, int val);
+	int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+	int (*clk)(void *handle, bool enable);
+	int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
+			void *data), void *swr_handle, int type);
+};
+
+struct swr_mstr_ctrl {
+	struct swr_master master;
+	struct device *dev;
+	struct resource *supplies;
+	struct clk *mclk;
+	struct completion reset;
+	struct completion broadcast;
+	struct mutex mlock;
+	struct mutex reslock;
+	u8 rcmd_id;
+	u8 wcmd_id;
+	void *handle; /* SWR Master handle from client for read and writes */
+	int (*read)(void *handle, int reg);
+	int (*write)(void *handle, int reg, int val);
+	int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+	int (*clk)(void *handle, bool enable);
+	int (*reg_irq)(void *handle, irqreturn_t(*irq_handler)(int irq,
+			void *data), void *swr_handle, int type);
+	int irq;
+	int num_enum_slaves;
+	int slave_status;
+	struct swr_mstr_port *mstr_port;
+	struct list_head mport_list;
+	int state;
+	struct platform_device *pdev;
+	int num_rx_chs;
+	u8 num_cfg_devs;
+};
+
+#endif /* _SWR_WCD_CTRL_H */
diff --git a/drivers/soundwire/swrm_registers.h b/drivers/soundwire/swrm_registers.h
new file mode 100644
index 0000000..c6923f3
--- /dev/null
+++ b/drivers/soundwire/swrm_registers.h
@@ -0,0 +1,203 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SWRM_REGISTERS_H
+#define _SWRM_REGISTERS_H
+
+#define SWRM_BASE_ADDRESS				0x00
+
+#define SWRM_COMP_CFG_ADDR			(SWRM_BASE_ADDRESS+0x00000004)
+#define SWRM_COMP_CFG_RMSK				0x3
+#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_BMSK		0x2
+#define SWRM_COMP_CFG_IRQ_LEVEL_OR_PULSE_SHFT		0x1
+#define SWRM_COMP_CFG_ENABLE_BMSK			0x1
+#define SWRM_COMP_CFG_ENABLE_SHFT			0x0
+
+#define SWRM_COMP_SW_RESET		(SWRM_BASE_ADDRESS+0x00000008)
+
+#define SWRM_COMP_PARAMS		(SWRM_BASE_ADDRESS+0x100)
+#define SWRM_COMP_PARAMS_DOUT_PORTS_MASK	0x0000001F
+#define SWRM_COMP_PARAMS_DIN_PORTS_MASK		0x000003E0
+#define SWRM_COMP_PARAMS_WR_FIFO_DEPTH		0x00007C00
+#define SWRM_COMP_PARAMS_RD_FIFO_DEPTH		0x000F8000
+#define SWRM_COMP_PARAMS_AUTO_ENUM_SLAVES	0x00F00000
+#define SWRM_COMP_PARAMS_DATA_LANES		0x07000000
+
+
+#define SWRM_INTERRUPT_STATUS		(SWRM_BASE_ADDRESS+0x00000200)
+#define SWRM_INTERRUPT_STATUS_RMSK		0x1FFFD
+
+#define SWRM_INTERRUPT_STATUS_SLAVE_PEND_IRQ			0x1
+#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_ATTACHED		0x2
+#define SWRM_INTERRUPT_STATUS_CHANGE_ENUM_SLAVE_STATUS		0x4
+#define SWRM_INTERRUPT_STATUS_MASTER_CLASH_DET			0x8
+#define SWRM_INTERRUPT_STATUS_RD_FIFO_OVERFLOW			0x10
+#define SWRM_INTERRUPT_STATUS_RD_FIFO_UNDERFLOW			0x20
+#define SWRM_INTERRUPT_STATUS_WR_CMD_FIFO_OVERFLOW		0x40
+#define SWRM_INTERRUPT_STATUS_CMD_ERROR				0x80
+#define SWRM_INTERRUPT_STATUS_DOUT_PORT_COLLISION		0x100
+#define SWRM_INTERRUPT_STATUS_READ_EN_RD_VALID_MISMATCH		0x200
+#define SWRM_INTERRUPT_STATUS_SPECIAL_CMD_ID_FINISHED		0x400
+#define SWRM_INTERRUPT_STATUS_NEW_SLAVE_AUTO_ENUM_FINISHED	0x800
+#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_FAILED			0x1000
+#define SWRM_INTERRUPT_STATUS_AUTO_ENUM_TABLE_IS_FULL		0x2000
+#define SWRM_INTERRUPT_STATUS_BUS_RESET_FINISHED		0x4000
+#define SWRM_INTERRUPT_STATUS_CLK_STOP_FINISHED			0x8000
+#define SWRM_INTERRUPT_STATUS_ERROR_PORT_TEST			0x10000
+
+#define SWRM_INTERRUPT_MASK_ADDR		(SWRM_BASE_ADDRESS+0x00000204)
+#define SWRM_INTERRUPT_MASK_RMSK		0x1FFFF
+
+#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_BMSK			0x1
+#define SWRM_INTERRUPT_MASK_SLAVE_PEND_IRQ_SHFT			0x0
+
+#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_BMSK		0x2
+#define SWRM_INTERRUPT_MASK_NEW_SLAVE_ATTACHED_SHFT		0x1
+
+#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_BMSK	0x4
+#define SWRM_INTERRUPT_MASK_CHANGE_ENUM_SLAVE_STATUS_SHFT	0x2
+
+#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_BMSK		0x8
+#define SWRM_INTERRUPT_MASK_MASTER_CLASH_DET_SHFT		0x3
+
+#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_BMSK		0x10
+#define SWRM_INTERRUPT_MASK_RD_FIFO_OVERFLOW_SHFT		0x4
+
+#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_BMSK		0x20
+#define SWRM_INTERRUPT_MASK_RD_FIFO_UNDERFLOW_SHFT		0x5
+
+#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_BMSK		0x40
+#define SWRM_INTERRUPT_MASK_WR_CMD_FIFO_OVERFLOW_SHFT		0x6
+
+#define SWRM_INTERRUPT_MASK_CMD_ERROR_BMSK			0x80
+#define SWRM_INTERRUPT_MASK_CMD_ERROR_SHFT			0x7
+
+#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_BMSK		0x100
+#define SWRM_INTERRUPT_MASK_DOUT_PORT_COLLISION_SHFT		0x8
+
+#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_BMSK	0x200
+#define SWRM_INTERRUPT_MASK_READ_EN_RD_VALID_MISMATCH_SHFT	0x9
+
+#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_BMSK	0x400
+#define SWRM_INTERRUPT_MASK_SPECIAL_CMD_ID_FINISHED_SHFT	0xA
+
+#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_BMSK	0x800
+#define SWRM_INTERRUPT_MASK_NEW_SLAVE_AUTO_ENUM_FINISHED_SHFT	0xB
+
+#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_BMSK		0x1000
+#define SWRM_INTERRUPT_MASK_AUTO_ENUM_FAILED_SHFT		0xC
+
+#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_BMSK	0x2000
+#define SWRM_INTERRUPT_MASK_AUTO_ENUM_TABLE_IS_FULL_SHFT	0xD
+
+#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_BMSK		0x4000
+#define SWRM_INTERRUPT_MASK_BUS_RESET_FINISHED_SHFT		0xE
+
+#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_BMSK		0x8000
+#define SWRM_INTERRUPT_MASK_CLK_STOP_FINISHED_SHFT		0xF
+
+#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_BMSK		0x10000
+#define SWRM_INTERRUPT_MASK_ERROR_PORT_TEST_SHFT		0x10
+
+#define SWRM_INTERRUPT_MAX					0x11
+
+#define SWRM_INTERRUPT_CLEAR		(SWRM_BASE_ADDRESS+0x00000208)
+
+#define SWRM_CMD_FIFO_WR_CMD		(SWRM_BASE_ADDRESS + 0x00000300)
+#define SWRM_CMD_FIFO_WR_CMD_MASK	0xFFFFFFFF
+#define SWRM_CMD_FIFO_RD_CMD		(SWRM_BASE_ADDRESS + 0x00000304)
+#define SWRM_CMD_FIFO_RD_CMD_MASK	0xFFFFFFF
+#define SWRM_CMD_FIFO_CMD		(SWRM_BASE_ADDRESS + 0x00000308)
+#define SWRM_CMD_FIFO_STATUS		(SWRM_BASE_ADDRESS + 0x0000030C)
+
+#define SWRM_CMD_FIFO_STATUS_WR_CMD_FIFO_CNT_MASK	0x1F00
+#define SWRM_CMD_FIFO_STATUS_RD_CMD_FIFO_CNT_MASK	0x7C00000
+
+#define SWRM_CMD_FIFO_CFG_ADDR			(SWRM_BASE_ADDRESS+0x00000314)
+#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_BMSK		0x7
+#define SWRM_CMD_FIFO_CFG_NUM_OF_CMD_RETRY_SHFT		0x0
+
+#define SWRM_CMD_FIFO_RD_FIFO_ADDR	(SWRM_BASE_ADDRESS + 0x00000318)
+
+#define SWRM_ENUMERATOR_CFG_ADDR		(SWRM_BASE_ADDRESS+0x00000500)
+#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_BMSK		0x1
+#define SWRM_ENUMERATOR_CFG_AUTO_ENUM_EN_SHFT		0x0
+
+#define SWRM_ENUMERATOR_SLAVE_DEV_ID_1(m)   (SWRM_BASE_ADDRESS+0x530+0x8*m)
+#define SWRM_ENUMERATOR_SLAVE_DEV_ID_2(m)   (SWRM_BASE_ADDRESS+0x534+0x8*m)
+
+#define SWRM_MCP_FRAME_CTRL_BANK_ADDR(m)    (SWRM_BASE_ADDRESS+0x101C+0x40*m)
+#define SWRM_MCP_FRAME_CTRL_BANK_RMSK			0x00ff07ff
+#define SWRM_MCP_FRAME_CTRL_BANK_SHFT			0
+#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_BMSK	0xff0000
+#define SWRM_MCP_FRAME_CTRL_BANK_SSP_PERIOD_SHFT	16
+#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_BMSK		0xf800
+#define SWRM_MCP_FRAME_CTRL_BANK_PHASE_SHFT		11
+#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_BMSK	0x700
+#define SWRM_MCP_FRAME_CTRL_BANK_CLK_DIV_VALUE_SHFT	8
+#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_BMSK		0xF8
+#define SWRM_MCP_FRAME_CTRL_BANK_ROW_CTRL_SHFT		3
+#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_BMSK		0x7
+#define SWRM_MCP_FRAME_CTRL_BANK_COL_CTRL_SHFT		0
+
+#define SWRM_MCP_BUS_CTRL_ADDR			(SWRM_BASE_ADDRESS+0x00001044)
+#define SWRM_MCP_BUS_CTRL_BUS_RESET_BMSK		0x1
+#define SWRM_MCP_BUS_CTRL_BUS_RESET_SHFT		0x0
+#define SWRM_MCP_BUS_CTRL_CLK_START_BMSK		0x2
+#define SWRM_MCP_BUS_CTRL_CLK_START_SHFT		0x1
+
+#define SWRM_MCP_CFG_ADDR			(SWRM_BASE_ADDRESS+0x00001048)
+#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_BMSK	0x3E0000
+#define SWRM_MCP_CFG_MAX_NUM_OF_CMD_NO_PINGS_SHFT	0x11
+#define SWRM_MCP_CFG_BUS_CLK_PAUSE_BMSK			0x02
+
+#define SWRM_MCP_STATUS			(SWRM_BASE_ADDRESS+0x104C)
+#define SWRM_MCP_STATUS_BANK_NUM_MASK	0x01
+
+#define SWRM_MCP_SLV_STATUS		(SWRM_BASE_ADDRESS+0x1090)
+#define SWRM_MCP_SLV_STATUS_MASK	0x03
+
+#define SWRM_DP_PORT_CTRL_BANK(n, m)		(SWRM_BASE_ADDRESS + \
+							0x00001124 + \
+							0x100*(n-1) + \
+							0x40*m)
+#define SWRM_DP_PORT_CTRL_BANK_MASK		0xFFFFFFFF
+#define SWRM_DP_PORT_CTRL_EN_CHAN_MASK		0xFF000000
+#define SWRM_DP_PORT_CTRL_EN_CHAN_SHFT		0x18
+#define SWRM_DP_PORT_CTRL_OFFSET2_SHFT		0x10
+#define SWRM_DP_PORT_CTRL_OFFSET1_SHFT		0x08
+#define SWRM_DP_PORT_CTRL_SAMPLE_INTERVAL	0x00
+
+/* Soundwire Slave Register definition */
+
+#define SWRS_BASE_ADDRESS			0x00
+
+#define SWRS_DP_REG_OFFSET(port, bank)		((0x100*port)+(0x10*bank))
+
+#define SWRS_DP_CHANNEL_ENABLE_BANK(n, m)	(SWRS_BASE_ADDRESS + 0x120 + \
+						 SWRS_DP_REG_OFFSET(n, m))
+#define SWRS_DP_SAMPLE_CONTROL_1_BANK(n, m)	(SWRS_BASE_ADDRESS + 0x122 + \
+						 SWRS_DP_REG_OFFSET(n, m))
+#define SWRS_DP_OFFSET_CONTROL_1_BANK(n, m)	(SWRS_BASE_ADDRESS + 0x124 + \
+						 SWRS_DP_REG_OFFSET(n, m))
+#define SWRS_DP_OFFSET_CONTROL_2_BANK(n, m)	(SWRS_BASE_ADDRESS + 0x125 + \
+						 SWRS_DP_REG_OFFSET(n, m))
+#define SWRS_DP_HCONTROL_BANK(n, m)		(SWRS_BASE_ADDRESS + 0x126 + \
+						 SWRS_DP_REG_OFFSET(n, m))
+#define SWRS_DP_BLOCK_CONTROL_3_BANK(n, m)	(SWRS_BASE_ADDRESS + 0x127 + \
+						 SWRS_DP_REG_OFFSET(n, m))
+#define SWRS_SCP_FRAME_CTRL_BANK(m)		(SWRS_BASE_ADDRESS + 0x60 + \
+						 0x10*m)
+#define SWRS_SCP_HOST_CLK_DIV2_CTL_BANK(m)	(SWRS_BASE_ADDRESS + 0xE0 + \
+						0x10*m)
+
+#endif /* _SWRM_REGISTERS_H */
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c
index 6c8154d..a5bfeab 100644
--- a/drivers/spmi/spmi-pmic-arb.c
+++ b/drivers/spmi/spmi-pmic-arb.c
@@ -533,18 +533,6 @@ static void cleanup_irq(struct spmi_pmic_arb *pa, u16 apid, int id)
 		"cleanup_irq apid=%d sid=0x%x per=0x%x irq=%d\n",
 		apid, sid, per, id);
 	writel_relaxed(irq_mask, pa->intr + pa->ver_ops->irq_clear(apid));
-
-	if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
-		       (per << 8) + QPNPINT_REG_LATCHED_CLR, &irq_mask, 1))
-		dev_err_ratelimited(&pa->spmic->dev,
-				"failed to ack irq_mask = 0x%x for ppid = %x\n",
-				irq_mask, ppid);
-
-	if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid,
-			       (per << 8) + QPNPINT_REG_EN_CLR, &irq_mask, 1))
-		dev_err_ratelimited(&pa->spmic->dev,
-				"failed to ack irq_mask = 0x%x for ppid = %x\n",
-				irq_mask, ppid);
 }
 
 static void periph_interrupt(struct spmi_pmic_arb *pa, u16 apid, bool show)
@@ -721,6 +709,17 @@ static struct irq_chip pmic_arb_irqchip = {
 			| IRQCHIP_SKIP_SET_WAKE,
 };
 
+static void qpnpint_irq_domain_activate(struct irq_domain *domain,
+					struct irq_data *d)
+{
+	u8 irq = HWIRQ_IRQ(d->hwirq);
+	u8 buf;
+
+	buf = BIT(irq);
+	qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &buf, 1);
+	qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &buf, 1);
+}
+
 static int qpnpint_irq_domain_dt_translate(struct irq_domain *d,
 					   struct device_node *controller,
 					   const u32 *intspec,
@@ -1186,6 +1185,7 @@ static const struct pmic_arb_ver_ops pmic_arb_v5 = {
 static const struct irq_domain_ops pmic_arb_irq_domain_ops = {
 	.map	= qpnpint_irq_domain_map,
 	.xlate	= qpnpint_irq_domain_dt_translate,
+	.activate	= qpnpint_irq_domain_activate,
 };
 
 static void spmi_pmic_arb_resume(void)
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index a13541b..2fda339 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -419,6 +419,20 @@
 	  real time die temperature if an ADC is present or an estimate of the
 	  temperature based upon the over temperature stage value.
 
+config THERMAL_QPNP
+	tristate "Qualcomm Technologies, Inc. QPNP PMIC Temperature Alarm"
+	depends on OF && SPMI
+	help
+	  This enables a thermal Sysfs driver for Qualcomm Technologies, Inc.
+	  QPNP PMIC devices. It shows up in Sysfs as a thermal zone with
+	  multiple trip points. The temperature reported by the thermal zone
+	  reflects the real time die temperature if an ADC is present or an
+	  estimate of the temperature based upon the over temperature stage
+	  value if no ADC is available. If allowed via compile time
+	  configuration; enabling the thermal zone device via the mode file
+	  results in shifting PMIC over temperature shutdown control from
+	  hardware to software.
+
 config GENERIC_ADC_THERMAL
 	tristate "Generic ADC based thermal sensor"
 	depends on IIO
@@ -429,6 +443,17 @@
 	  to this driver. This driver reports the temperature by reading ADC
 	  channel and converts it to temperature based on lookup table.
 
+config THERMAL_QPNP_ADC_TM
+	tristate "Qualcomm Technologies Inc. Thermal Monitor ADC Driver"
+	depends on THERMAL
+	depends on  SPMI
+	help
+	  This enables the thermal Sysfs driver for the ADC thermal monitoring
+	  device. It shows up in Sysfs as a thermal zone with multiple trip points.
+	  Disabling the thermal zone device via the mode file results in disabling
+	  the sensor. Also able to set threshold temperature for both hot and cold
+	  and update when a threshold is reached.
+
 menu "Qualcomm thermal drivers"
 depends on (ARCH_QCOM && OF) || COMPILE_TEST
 source "drivers/thermal/qcom/Kconfig"
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index c92eb22..d9489a7 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -27,6 +27,7 @@
 
 # platform thermal drivers
 obj-$(CONFIG_QCOM_SPMI_TEMP_ALARM)	+= qcom-spmi-temp-alarm.o
+obj-$(CONFIG_THERMAL_QPNP)	+= qpnp-temp-alarm.o
 obj-$(CONFIG_SPEAR_THERMAL)	+= spear_thermal.o
 obj-$(CONFIG_ROCKCHIP_THERMAL)	+= rockchip_thermal.o
 obj-$(CONFIG_RCAR_THERMAL)	+= rcar_thermal.o
@@ -55,3 +56,4 @@
 obj-$(CONFIG_HISI_THERMAL)     += hisi_thermal.o
 obj-$(CONFIG_MTK_THERMAL)	+= mtk_thermal.o
 obj-$(CONFIG_GENERIC_ADC_THERMAL)	+= thermal-generic-adc.o
+obj-$(CONFIG_THERMAL_QPNP_ADC_TM)	+= qpnp-adc-tm.o
diff --git a/drivers/thermal/qpnp-adc-tm.c b/drivers/thermal/qpnp-adc-tm.c
new file mode 100644
index 0000000..8d706cd
--- /dev/null
+++ b/drivers/thermal/qpnp-adc-tm.c
@@ -0,0 +1,3365 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/hwmon.h>
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/qpnp/qpnp-adc.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+
+/* QPNP VADC TM register definition */
+#define QPNP_REVISION3					0x2
+#define QPNP_PERPH_SUBTYPE				0x5
+#define QPNP_PERPH_TYPE2				0x2
+#define QPNP_REVISION_EIGHT_CHANNEL_SUPPORT		2
+#define QPNP_PERPH_SUBTYPE_TWO_CHANNEL_SUPPORT		0x22
+#define QPNP_STATUS1					0x8
+#define QPNP_STATUS1_OP_MODE				4
+#define QPNP_STATUS1_MEAS_INTERVAL_EN_STS		BIT(2)
+#define QPNP_STATUS1_REQ_STS				BIT(1)
+#define QPNP_STATUS1_EOC				BIT(0)
+#define QPNP_STATUS2					0x9
+#define QPNP_STATUS2_CONV_SEQ_STATE			6
+#define QPNP_STATUS2_FIFO_NOT_EMPTY_FLAG		BIT(1)
+#define QPNP_STATUS2_CONV_SEQ_TIMEOUT_STS		BIT(0)
+#define QPNP_CONV_TIMEOUT_ERR				2
+
+#define QPNP_MODE_CTL					0x40
+#define QPNP_OP_MODE_SHIFT				3
+#define QPNP_VREF_XO_THM_FORCE				BIT(2)
+#define QPNP_AMUX_TRIM_EN				BIT(1)
+#define QPNP_ADC_TRIM_EN				BIT(0)
+#define QPNP_EN_CTL1					0x46
+#define QPNP_ADC_TM_EN					BIT(7)
+#define QPNP_BTM_CONV_REQ				0x47
+#define QPNP_ADC_CONV_REQ_EN				BIT(7)
+
+#define QPNP_ADC_CH_SEL_CTL				0x48
+#define QPNP_ADC_DIG_PARAM				0x50
+#define QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT		3
+#define QPNP_HW_SETTLE_DELAY				0x51
+#define QPNP_CONV_REQ					0x52
+#define QPNP_CONV_REQ_SET				BIT(7)
+#define QPNP_CONV_SEQ_CTL				0x54
+#define QPNP_CONV_SEQ_HOLDOFF_SHIFT			4
+#define QPNP_CONV_SEQ_TRIG_CTL				0x55
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL			0x57
+#define QPNP_ADC_TM_MEAS_INTERVAL_TIME_SHIFT		0x3
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2			0x58
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT		0x4
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL2_MASK		0xf0
+#define QPNP_ADC_TM_MEAS_INTERVAL_CTL3_MASK		0xf
+
+#define QPNP_ADC_MEAS_INTERVAL_OP_CTL			0x59
+#define QPNP_ADC_MEAS_INTERVAL_OP			BIT(7)
+
+#define QPNP_FAST_AVG_CTL				0x5a
+#define QPNP_FAST_AVG_EN				0x5b
+#define QPNP_FAST_AVG_ENABLED				BIT(7)
+
+#define QPNP_M0_LOW_THR_LSB				0x5c
+#define QPNP_M0_LOW_THR_MSB				0x5d
+#define QPNP_M0_HIGH_THR_LSB				0x5e
+#define QPNP_M0_HIGH_THR_MSB				0x5f
+#define QPNP_M1_ADC_CH_SEL_CTL				0x68
+#define QPNP_M1_LOW_THR_LSB				0x69
+#define QPNP_M1_LOW_THR_MSB				0x6a
+#define QPNP_M1_HIGH_THR_LSB				0x6b
+#define QPNP_M1_HIGH_THR_MSB				0x6c
+#define QPNP_M2_ADC_CH_SEL_CTL				0x70
+#define QPNP_M2_LOW_THR_LSB				0x71
+#define QPNP_M2_LOW_THR_MSB				0x72
+#define QPNP_M2_HIGH_THR_LSB				0x73
+#define QPNP_M2_HIGH_THR_MSB				0x74
+#define QPNP_M3_ADC_CH_SEL_CTL				0x78
+#define QPNP_M3_LOW_THR_LSB				0x79
+#define QPNP_M3_LOW_THR_MSB				0x7a
+#define QPNP_M3_HIGH_THR_LSB				0x7b
+#define QPNP_M3_HIGH_THR_MSB				0x7c
+#define QPNP_M4_ADC_CH_SEL_CTL				0x80
+#define QPNP_M4_LOW_THR_LSB				0x81
+#define QPNP_M4_LOW_THR_MSB				0x82
+#define QPNP_M4_HIGH_THR_LSB				0x83
+#define QPNP_M4_HIGH_THR_MSB				0x84
+#define QPNP_M5_ADC_CH_SEL_CTL				0x88
+#define QPNP_M5_LOW_THR_LSB				0x89
+#define QPNP_M5_LOW_THR_MSB				0x8a
+#define QPNP_M5_HIGH_THR_LSB				0x8b
+#define QPNP_M5_HIGH_THR_MSB				0x8c
+#define QPNP_M6_ADC_CH_SEL_CTL				0x90
+#define QPNP_M6_LOW_THR_LSB				0x91
+#define QPNP_M6_LOW_THR_MSB				0x92
+#define QPNP_M6_HIGH_THR_LSB				0x93
+#define QPNP_M6_HIGH_THR_MSB				0x94
+#define QPNP_M7_ADC_CH_SEL_CTL				0x98
+#define QPNP_M7_LOW_THR_LSB				0x99
+#define QPNP_M7_LOW_THR_MSB				0x9a
+#define QPNP_M7_HIGH_THR_LSB				0x9b
+#define QPNP_M7_HIGH_THR_MSB				0x9c
+
+#define QPNP_ADC_TM_MULTI_MEAS_EN			0x41
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M0			BIT(0)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M1			BIT(1)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M2			BIT(2)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M3			BIT(3)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M4			BIT(4)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M5			BIT(5)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M6			BIT(6)
+#define QPNP_ADC_TM_MULTI_MEAS_EN_M7			BIT(7)
+#define QPNP_ADC_TM_LOW_THR_INT_EN			0x42
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M0			BIT(0)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M1			BIT(1)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M2			BIT(2)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M3			BIT(3)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M4			BIT(4)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M5			BIT(5)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M6			BIT(6)
+#define QPNP_ADC_TM_LOW_THR_INT_EN_M7			BIT(7)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN			0x43
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M0			BIT(0)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M1			BIT(1)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M2			BIT(2)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M3			BIT(3)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M4			BIT(4)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M5			BIT(5)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M6			BIT(6)
+#define QPNP_ADC_TM_HIGH_THR_INT_EN_M7			BIT(7)
+
+#define QPNP_ADC_TM_M0_MEAS_INTERVAL_CTL			0x59
+#define QPNP_ADC_TM_M1_MEAS_INTERVAL_CTL			0x6d
+#define QPNP_ADC_TM_M2_MEAS_INTERVAL_CTL			0x75
+#define QPNP_ADC_TM_M3_MEAS_INTERVAL_CTL			0x7d
+#define QPNP_ADC_TM_M4_MEAS_INTERVAL_CTL			0x85
+#define QPNP_ADC_TM_M5_MEAS_INTERVAL_CTL			0x8d
+#define QPNP_ADC_TM_M6_MEAS_INTERVAL_CTL			0x95
+#define QPNP_ADC_TM_M7_MEAS_INTERVAL_CTL			0x9d
+#define QPNP_ADC_TM_STATUS1				0x8
+#define QPNP_ADC_TM_STATUS_LOW				0xa
+#define QPNP_ADC_TM_STATUS_HIGH				0xb
+
+#define QPNP_ADC_TM_M0_LOW_THR				0x5d5c
+#define QPNP_ADC_TM_M0_HIGH_THR				0x5f5e
+#define QPNP_ADC_TM_MEAS_INTERVAL			0x0
+
+#define QPNP_ADC_TM_THR_LSB_MASK(val)			(val & 0xff)
+#define QPNP_ADC_TM_THR_MSB_MASK(val)			((val & 0xff00) >> 8)
+
+#define QPNP_MIN_TIME			2000
+#define QPNP_MAX_TIME			2100
+#define QPNP_RETRY			1000
+
+/* QPNP ADC TM HC start */
+#define QPNP_BTM_HC_STATUS1		0x08
+#define QPNP_BTM_HC_STATUS_LOW		0x0a
+#define QPNP_BTM_HC_STATUS_HIGH		0x0b
+
+#define QPNP_BTM_HC_ADC_DIG_PARAM	0x42
+#define QPNP_BTM_HC_FAST_AVG_CTL	0x43
+#define QPNP_BTM_EN_CTL1		0x46
+#define QPNP_BTM_CONV_REQ		0x47
+
+#define QPNP_BTM_MEAS_INTERVAL_CTL	0x50
+#define QPNP_BTM_MEAS_INTERVAL_CTL2	0x51
+
+#define QPNP_BTM_Mn_ADC_CH_SEL_CTL(n)		((n * 8) + 0x60)
+#define QPNP_BTM_Mn_LOW_THR0(n)			((n * 8) + 0x61)
+#define QPNP_BTM_Mn_LOW_THR1(n)			((n * 8) + 0x62)
+#define QPNP_BTM_Mn_HIGH_THR0(n)		((n * 8) + 0x63)
+#define QPNP_BTM_Mn_HIGH_THR1(n)		((n * 8) + 0x64)
+#define QPNP_BTM_Mn_MEAS_INTERVAL_CTL(n)	((n * 8) + 0x65)
+#define QPNP_BTM_Mn_CTL(n)			((n * 8) + 0x66)
+#define QPNP_BTM_CTL_HW_SETTLE_DELAY_MASK	0xf
+#define QPNP_BTM_CTL_CAL_SEL			0x30
+#define QPNP_BTM_CTL_CAL_SEL_MASK_SHIFT		4
+#define QPNP_BTM_CTL_CAL_VAL			0x40
+
+#define QPNP_BTM_Mn_EN(n)			((n * 8) + 0x67)
+#define QPNP_BTM_Mn_MEAS_EN			BIT(7)
+#define QPNP_BTM_Mn_HIGH_THR_INT_EN		BIT(1)
+#define QPNP_BTM_Mn_LOW_THR_INT_EN		BIT(0)
+
+#define QPNP_BTM_Mn_DATA0(n)			((n * 2) + 0xa0)
+#define QPNP_BTM_Mn_DATA1(n)			((n * 2) + 0xa1)
+
+/* QPNP ADC TM HC end */
+
+struct qpnp_adc_thr_info {
+	u8		status_low;
+	u8		status_high;
+	u8		qpnp_adc_tm_meas_en;
+	u8		adc_tm_low_enable;
+	u8		adc_tm_high_enable;
+	u8		adc_tm_low_thr_set;
+	u8		adc_tm_high_thr_set;
+};
+
+struct qpnp_adc_thr_client_info {
+	struct list_head		list;
+	struct qpnp_adc_tm_btm_param	*btm_param;
+	int32_t				low_thr_requested;
+	int32_t				high_thr_requested;
+	enum qpnp_state_request		state_requested;
+	enum qpnp_state_request		state_req_copy;
+	bool				low_thr_set;
+	bool				high_thr_set;
+	bool				notify_low_thr;
+	bool				notify_high_thr;
+};
+
+struct qpnp_adc_tm_sensor {
+	struct thermal_zone_device	*tz_dev;
+	struct qpnp_adc_tm_chip		*chip;
+	enum thermal_device_mode	mode;
+	uint32_t			sensor_num;
+	enum qpnp_adc_meas_timer_select	timer_select;
+	uint32_t			meas_interval;
+	uint32_t			low_thr;
+	uint32_t			high_thr;
+	uint32_t			btm_channel_num;
+	uint32_t			vadc_channel_num;
+	struct workqueue_struct		*req_wq;
+	struct work_struct		work;
+	bool				thermal_node;
+	uint32_t			scale_type;
+	struct list_head		thr_list;
+	bool				high_thr_triggered;
+	bool				low_thr_triggered;
+};
+
+struct qpnp_adc_tm_chip {
+	struct device			*dev;
+	struct qpnp_adc_drv		*adc;
+	struct list_head		list;
+	bool				adc_tm_initialized;
+	bool				adc_tm_recalib_check;
+	int				max_channels_available;
+	atomic_t			wq_cnt;
+	struct qpnp_vadc_chip		*vadc_dev;
+	struct workqueue_struct		*high_thr_wq;
+	struct workqueue_struct		*low_thr_wq;
+	struct workqueue_struct		*thr_wq;
+	struct work_struct		trigger_high_thr_work;
+	struct work_struct		trigger_low_thr_work;
+	struct work_struct		trigger_thr_work;
+	bool				adc_vote_enable;
+	struct qpnp_adc_thr_info	th_info;
+	bool				adc_tm_hc;
+	struct qpnp_adc_tm_sensor	sensor[0];
+};
+
+LIST_HEAD(qpnp_adc_tm_device_list);
+
+struct qpnp_adc_tm_trip_reg_type {
+	enum qpnp_adc_tm_channel_select	btm_amux_chan;
+	uint16_t			low_thr_lsb_addr;
+	uint16_t			low_thr_msb_addr;
+	uint16_t			high_thr_lsb_addr;
+	uint16_t			high_thr_msb_addr;
+	u8				multi_meas_en;
+	u8				low_thr_int_chan_en;
+	u8				high_thr_int_chan_en;
+	u8				meas_interval_ctl;
+};
+
+static struct qpnp_adc_tm_trip_reg_type adc_tm_data[] = {
+	[QPNP_ADC_TM_CHAN0] = {QPNP_ADC_TM_M0_ADC_CH_SEL_CTL,
+		QPNP_M0_LOW_THR_LSB,
+		QPNP_M0_LOW_THR_MSB, QPNP_M0_HIGH_THR_LSB,
+		QPNP_M0_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M0,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M0, QPNP_ADC_TM_HIGH_THR_INT_EN_M0,
+		QPNP_ADC_TM_M0_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN1] = {QPNP_ADC_TM_M1_ADC_CH_SEL_CTL,
+		QPNP_M1_LOW_THR_LSB,
+		QPNP_M1_LOW_THR_MSB, QPNP_M1_HIGH_THR_LSB,
+		QPNP_M1_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M1,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M1, QPNP_ADC_TM_HIGH_THR_INT_EN_M1,
+		QPNP_ADC_TM_M1_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN2] = {QPNP_ADC_TM_M2_ADC_CH_SEL_CTL,
+		QPNP_M2_LOW_THR_LSB,
+		QPNP_M2_LOW_THR_MSB, QPNP_M2_HIGH_THR_LSB,
+		QPNP_M2_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M2,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M2, QPNP_ADC_TM_HIGH_THR_INT_EN_M2,
+		QPNP_ADC_TM_M2_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN3] = {QPNP_ADC_TM_M3_ADC_CH_SEL_CTL,
+		QPNP_M3_LOW_THR_LSB,
+		QPNP_M3_LOW_THR_MSB, QPNP_M3_HIGH_THR_LSB,
+		QPNP_M3_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M3,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M3, QPNP_ADC_TM_HIGH_THR_INT_EN_M3,
+		QPNP_ADC_TM_M3_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN4] = {QPNP_ADC_TM_M4_ADC_CH_SEL_CTL,
+		QPNP_M4_LOW_THR_LSB,
+		QPNP_M4_LOW_THR_MSB, QPNP_M4_HIGH_THR_LSB,
+		QPNP_M4_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M4,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M4, QPNP_ADC_TM_HIGH_THR_INT_EN_M4,
+		QPNP_ADC_TM_M4_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN5] = {QPNP_ADC_TM_M5_ADC_CH_SEL_CTL,
+		QPNP_M5_LOW_THR_LSB,
+		QPNP_M5_LOW_THR_MSB, QPNP_M5_HIGH_THR_LSB,
+		QPNP_M5_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M5,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M5, QPNP_ADC_TM_HIGH_THR_INT_EN_M5,
+		QPNP_ADC_TM_M5_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN6] = {QPNP_ADC_TM_M6_ADC_CH_SEL_CTL,
+		QPNP_M6_LOW_THR_LSB,
+		QPNP_M6_LOW_THR_MSB, QPNP_M6_HIGH_THR_LSB,
+		QPNP_M6_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M6,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M6, QPNP_ADC_TM_HIGH_THR_INT_EN_M6,
+		QPNP_ADC_TM_M6_MEAS_INTERVAL_CTL},
+	[QPNP_ADC_TM_CHAN7] = {QPNP_ADC_TM_M7_ADC_CH_SEL_CTL,
+		QPNP_M7_LOW_THR_LSB,
+		QPNP_M7_LOW_THR_MSB, QPNP_M7_HIGH_THR_LSB,
+		QPNP_M7_HIGH_THR_MSB, QPNP_ADC_TM_MULTI_MEAS_EN_M7,
+		QPNP_ADC_TM_LOW_THR_INT_EN_M7, QPNP_ADC_TM_HIGH_THR_INT_EN_M7,
+		QPNP_ADC_TM_M7_MEAS_INTERVAL_CTL},
+};
+
+static struct qpnp_adc_tm_reverse_scale_fn adc_tm_rscale_fn[] = {
+	[SCALE_R_VBATT] = {qpnp_adc_vbatt_rscaler},
+	[SCALE_RBATT_THERM] = {qpnp_adc_btm_scaler},
+	[SCALE_R_USB_ID] = {qpnp_adc_usb_scaler},
+	[SCALE_RPMIC_THERM] = {qpnp_adc_scale_millidegc_pmic_voltage_thr},
+	[SCALE_R_SMB_BATT_THERM] = {qpnp_adc_smb_btm_rscaler},
+	[SCALE_R_ABSOLUTE] = {qpnp_adc_absolute_rthr},
+	[SCALE_QRD_SKUH_RBATT_THERM] = {qpnp_adc_qrd_skuh_btm_scaler},
+	[SCALE_QRD_SKUT1_RBATT_THERM] = {qpnp_adc_qrd_skut1_btm_scaler},
+};
+
+static int32_t qpnp_adc_tm_read_reg(struct qpnp_adc_tm_chip *chip,
+					int16_t reg, u8 *data, int len)
+{
+	int rc = 0;
+
+	rc = regmap_bulk_read(chip->adc->regmap, (chip->adc->offset + reg),
+								data, len);
+	if (rc < 0)
+		pr_err("adc-tm read reg %d failed with %d\n", reg, rc);
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_write_reg(struct qpnp_adc_tm_chip *chip,
+					int16_t reg, u8 data, int len)
+{
+	int rc = 0;
+	u8 *buf;
+
+	buf = &data;
+
+	rc = regmap_bulk_write(chip->adc->regmap, (chip->adc->offset + reg),
+								buf, len);
+	if (rc < 0)
+		pr_err("adc-tm write reg %d failed with %d\n", reg, rc);
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_fast_avg_en(struct qpnp_adc_tm_chip *chip,
+				uint32_t *fast_avg_sample)
+{
+	int rc = 0, version = 0;
+	u8 fast_avg_en = 0;
+
+	version = qpnp_adc_get_revid_version(chip->dev);
+	if (!((version == QPNP_REV_ID_8916_1_0) ||
+		(version == QPNP_REV_ID_8916_1_1) ||
+		(version == QPNP_REV_ID_8916_2_0))) {
+		pr_debug("fast-avg-en not required for this version\n");
+		return rc;
+	}
+
+	fast_avg_en = QPNP_FAST_AVG_ENABLED;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_FAST_AVG_EN, fast_avg_en, 1);
+	if (rc < 0) {
+		pr_err("adc-tm fast-avg enable err\n");
+		return rc;
+	}
+
+	if (*fast_avg_sample >= 3)
+		*fast_avg_sample = 2;
+
+	return rc;
+}
+
+static int qpnp_adc_tm_check_vreg_vote(struct qpnp_adc_tm_chip *chip)
+{
+	int rc = 0;
+
+	if (!chip->adc_vote_enable) {
+		if (chip->adc->hkadc_ldo && chip->adc->hkadc_ldo_ok) {
+			rc = qpnp_adc_enable_voltage(chip->adc);
+			if (rc) {
+				pr_err("failed enabling VADC LDO\n");
+				return rc;
+			}
+			chip->adc_vote_enable = true;
+		}
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_enable(struct qpnp_adc_tm_chip *chip)
+{
+	int rc = 0;
+	u8 data = 0;
+
+	rc = qpnp_adc_tm_check_vreg_vote(chip);
+	if (rc) {
+		pr_err("ADC TM VREG enable failed:%d\n", rc);
+		return rc;
+	}
+
+	data = QPNP_ADC_TM_EN;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_EN_CTL1, data, 1);
+	if (rc < 0) {
+		pr_err("adc-tm enable failed\n");
+		return rc;
+	}
+
+	if (chip->adc_tm_hc) {
+		data = QPNP_ADC_CONV_REQ_EN;
+		rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_CONV_REQ, data, 1);
+		if (rc < 0) {
+			pr_err("adc-tm enable failed\n");
+			return rc;
+		}
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_disable(struct qpnp_adc_tm_chip *chip)
+{
+	u8 data = 0;
+	int rc = 0;
+
+	if (chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_CONV_REQ, data, 1);
+		if (rc < 0) {
+			pr_err("adc-tm enable failed\n");
+			return rc;
+		}
+	}
+
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_EN_CTL1, data, 1);
+	if (rc < 0) {
+		pr_err("adc-tm disable failed\n");
+		return rc;
+	}
+
+	return rc;
+}
+
+static int qpnp_adc_tm_is_valid(struct qpnp_adc_tm_chip *chip)
+{
+	struct qpnp_adc_tm_chip *adc_tm_chip = NULL;
+
+	list_for_each_entry(adc_tm_chip, &qpnp_adc_tm_device_list, list)
+		if (chip == adc_tm_chip)
+			return 0;
+
+	return -EINVAL;
+}
+
+static int32_t qpnp_adc_tm_rc_check_channel_en(struct qpnp_adc_tm_chip *chip)
+{
+	u8 adc_tm_ctl = 0, status_low = 0, status_high = 0;
+	int rc = 0, i = 0;
+	bool ldo_en = false;
+
+	for (i = 0; i < chip->max_channels_available; i++) {
+		rc = qpnp_adc_tm_read_reg(chip, QPNP_BTM_Mn_CTL(i),
+							&adc_tm_ctl, 1);
+		if (rc) {
+			pr_err("adc-tm-tm read ctl failed with %d\n", rc);
+			return rc;
+		}
+
+		adc_tm_ctl &= QPNP_BTM_Mn_MEAS_EN;
+		status_low = adc_tm_ctl & QPNP_BTM_Mn_LOW_THR_INT_EN;
+		status_high = adc_tm_ctl & QPNP_BTM_Mn_HIGH_THR_INT_EN;
+
+		/* Enable only if there are pending measurement requests */
+		if ((adc_tm_ctl && status_high) ||
+					(adc_tm_ctl && status_low)) {
+			qpnp_adc_tm_enable(chip);
+			ldo_en = true;
+
+			/* Request conversion */
+			rc = qpnp_adc_tm_write_reg(chip, QPNP_CONV_REQ,
+							QPNP_CONV_REQ_SET, 1);
+			if (rc < 0) {
+				pr_err("adc-tm request conversion failed\n");
+				return rc;
+			}
+		}
+		break;
+	}
+
+	if (!ldo_en) {
+		/* disable the vote if applicable */
+		if (chip->adc_vote_enable && chip->adc->hkadc_ldo &&
+					chip->adc->hkadc_ldo_ok) {
+			qpnp_adc_disable_voltage(chip->adc);
+			chip->adc_vote_enable = false;
+		}
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_enable_if_channel_meas(
+					struct qpnp_adc_tm_chip *chip)
+{
+	u8 adc_tm_meas_en = 0, status_low = 0, status_high = 0;
+	int rc = 0;
+
+	if (chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_rc_check_channel_en(chip);
+		if (rc) {
+			pr_err("adc_tm channel check failed\n");
+			return rc;
+		}
+	} else {
+		/* Check if a measurement request is still required */
+		rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+							&adc_tm_meas_en, 1);
+		if (rc) {
+			pr_err("read status high failed with %d\n", rc);
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+							&status_low, 1);
+		if (rc) {
+			pr_err("read status low failed with %d\n", rc);
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+							&status_high, 1);
+		if (rc) {
+			pr_err("read status high failed with %d\n", rc);
+			return rc;
+		}
+
+		/* Enable only if there are pending measurement requests */
+		if ((adc_tm_meas_en && status_high) ||
+				(adc_tm_meas_en && status_low)) {
+			qpnp_adc_tm_enable(chip);
+
+			/* Request conversion */
+			rc = qpnp_adc_tm_write_reg(chip, QPNP_CONV_REQ,
+							QPNP_CONV_REQ_SET, 1);
+			if (rc < 0) {
+				pr_err("adc-tm request conversion failed\n");
+				return rc;
+			}
+		} else {
+			/* disable the vote if applicable */
+			if (chip->adc_vote_enable && chip->adc->hkadc_ldo &&
+					chip->adc->hkadc_ldo_ok) {
+				qpnp_adc_disable_voltage(chip->adc);
+				chip->adc_vote_enable = false;
+			}
+		}
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_mode_select(struct qpnp_adc_tm_chip *chip,
+								u8 mode_ctl)
+{
+	int rc;
+
+	mode_ctl |= (QPNP_ADC_TRIM_EN | QPNP_AMUX_TRIM_EN);
+
+	/* VADC_BTM current sets mode to recurring measurements */
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_MODE_CTL, mode_ctl, 1);
+	if (rc < 0)
+		pr_err("adc-tm write mode selection err\n");
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_req_sts_check(struct qpnp_adc_tm_chip *chip)
+{
+	u8 status1 = 0, mode_ctl = 0;
+	int rc, count = 0;
+
+	/* Re-enable the peripheral */
+	rc = qpnp_adc_tm_enable(chip);
+	if (rc) {
+		pr_err("adc-tm re-enable peripheral failed\n");
+		return rc;
+	}
+
+	/* The VADC_TM bank needs to be disabled for new conversion request */
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+	if (rc) {
+		pr_err("adc-tm read status1 failed\n");
+		return rc;
+	}
+
+	/* Disable the bank if a conversion is occurring */
+	while (status1 & QPNP_STATUS1_REQ_STS) {
+		if (count > QPNP_RETRY) {
+			pr_err("retry error=%d with 0x%x\n", count, status1);
+			break;
+		}
+		/*
+		 * Wait time is based on the optimum sampling rate
+		 * and adding enough time buffer to account for ADC conversions
+		 * occurring on different peripheral banks
+		 */
+		usleep_range(QPNP_MIN_TIME, QPNP_MAX_TIME);
+		rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1,
+							&status1, 1);
+		if (rc < 0) {
+			pr_err("adc-tm disable failed\n");
+			return rc;
+		}
+		count++;
+	}
+
+	if (!chip->adc_tm_hc) {
+		/* Change the mode back to recurring measurement mode */
+		mode_ctl = ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+		rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+		if (rc < 0) {
+			pr_err("adc-tm mode change to recurring failed\n");
+			return rc;
+		}
+	}
+
+	/* Disable the peripheral */
+	rc = qpnp_adc_tm_disable(chip);
+	if (rc < 0) {
+		pr_err("adc-tm peripheral disable failed\n");
+		return rc;
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_get_btm_idx(struct qpnp_adc_tm_chip *chip,
+				uint32_t btm_chan, uint32_t *btm_chan_idx)
+{
+	int rc = 0, i;
+	bool chan_found = false;
+
+	if (!chip->adc_tm_hc) {
+		for (i = 0; i < QPNP_ADC_TM_CHAN_NONE; i++) {
+			if (adc_tm_data[i].btm_amux_chan == btm_chan) {
+				*btm_chan_idx = i;
+				chan_found = true;
+			}
+		}
+	} else {
+		for (i = 0; i < chip->max_channels_available; i++) {
+			if (chip->sensor[i].btm_channel_num == btm_chan) {
+				*btm_chan_idx = i;
+				chan_found = true;
+				break;
+			}
+		}
+	}
+
+	if (!chan_found)
+		return -EINVAL;
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_check_revision(struct qpnp_adc_tm_chip *chip,
+							uint32_t btm_chan_num)
+{
+	u8 rev, perph_subtype;
+	int rc = 0;
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_REVISION3, &rev, 1);
+	if (rc) {
+		pr_err("adc-tm revision read failed\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_PERPH_SUBTYPE, &perph_subtype, 1);
+	if (rc) {
+		pr_err("adc-tm perph_subtype read failed\n");
+		return rc;
+	}
+
+	if (perph_subtype == QPNP_PERPH_TYPE2) {
+		if ((rev < QPNP_REVISION_EIGHT_CHANNEL_SUPPORT) &&
+			(btm_chan_num > QPNP_ADC_TM_M4_ADC_CH_SEL_CTL)) {
+			pr_debug("Version does not support more than 5 channels\n");
+			return -EINVAL;
+		}
+	}
+
+	if (perph_subtype == QPNP_PERPH_SUBTYPE_TWO_CHANNEL_SUPPORT) {
+		if (btm_chan_num > QPNP_ADC_TM_M1_ADC_CH_SEL_CTL) {
+			pr_debug("Version does not support more than 2 channels\n");
+			return -EINVAL;
+		}
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_timer_interval_select(
+		struct qpnp_adc_tm_chip *chip, uint32_t btm_chan,
+		struct qpnp_vadc_chan_properties *chan_prop)
+{
+	int rc, chan_idx = 0, i = 0;
+	bool chan_found = false;
+	u8 meas_interval_timer2 = 0, timer_interval_store = 0;
+	uint32_t btm_chan_idx = 0;
+
+	while (i < chip->max_channels_available) {
+		if (chip->sensor[i].btm_channel_num == btm_chan) {
+			chan_idx = i;
+			chan_found = true;
+			i++;
+		} else
+			i++;
+	}
+
+	if (!chan_found) {
+		pr_err("Channel not found\n");
+		return -EINVAL;
+	}
+
+	switch (chip->sensor[chan_idx].timer_select) {
+	case ADC_MEAS_TIMER_SELECT1:
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_ADC_TM_MEAS_INTERVAL_CTL,
+				chip->sensor[chan_idx].meas_interval, 1);
+		else
+			rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_BTM_MEAS_INTERVAL_CTL,
+				chip->sensor[chan_idx].meas_interval, 1);
+		if (rc < 0) {
+			pr_err("timer1 configure failed\n");
+			return rc;
+		}
+	break;
+	case ADC_MEAS_TIMER_SELECT2:
+		/* Thermal channels uses timer2, default to 1 second */
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_read_reg(chip,
+				QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+				&meas_interval_timer2, 1);
+		else
+			rc = qpnp_adc_tm_read_reg(chip,
+				QPNP_BTM_MEAS_INTERVAL_CTL2,
+				&meas_interval_timer2, 1);
+		if (rc < 0) {
+			pr_err("timer2 configure read failed\n");
+			return rc;
+		}
+		timer_interval_store = chip->sensor[chan_idx].meas_interval;
+		timer_interval_store <<= QPNP_ADC_TM_MEAS_INTERVAL_CTL2_SHIFT;
+		timer_interval_store &= QPNP_ADC_TM_MEAS_INTERVAL_CTL2_MASK;
+		meas_interval_timer2 |= timer_interval_store;
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+				meas_interval_timer2, 1);
+		else
+			rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_BTM_MEAS_INTERVAL_CTL2,
+				meas_interval_timer2, 1);
+		if (rc < 0) {
+			pr_err("timer2 configure failed\n");
+			return rc;
+		}
+	break;
+	case ADC_MEAS_TIMER_SELECT3:
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_read_reg(chip,
+				QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+				&meas_interval_timer2, 1);
+		else
+			rc = qpnp_adc_tm_read_reg(chip,
+				QPNP_BTM_MEAS_INTERVAL_CTL2,
+				&meas_interval_timer2, 1);
+		if (rc < 0) {
+			pr_err("timer3 read failed\n");
+			return rc;
+		}
+		timer_interval_store = chip->sensor[chan_idx].meas_interval;
+		timer_interval_store &= QPNP_ADC_TM_MEAS_INTERVAL_CTL3_MASK;
+		meas_interval_timer2 |= timer_interval_store;
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_ADC_TM_MEAS_INTERVAL_CTL2,
+				meas_interval_timer2, 1);
+		else
+			rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_BTM_MEAS_INTERVAL_CTL2,
+				meas_interval_timer2, 1);
+		if (rc < 0) {
+			pr_err("timer3 configure failed\n");
+			return rc;
+		}
+	break;
+	default:
+		pr_err("Invalid timer selection\n");
+		return -EINVAL;
+	}
+
+	/* Select the timer to use for the corresponding channel */
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+	if (!chip->adc_tm_hc)
+		rc = qpnp_adc_tm_write_reg(chip,
+			adc_tm_data[btm_chan_idx].meas_interval_ctl,
+				chip->sensor[chan_idx].timer_select, 1);
+	else
+		rc = qpnp_adc_tm_write_reg(chip,
+				QPNP_BTM_Mn_MEAS_INTERVAL_CTL(btm_chan_idx),
+				chip->sensor[chan_idx].timer_select, 1);
+	if (rc < 0) {
+		pr_err("TM channel timer configure failed\n");
+		return rc;
+	}
+
+	pr_debug("timer select:%d, timer_value_within_select:%d, channel:%x\n",
+			chip->sensor[chan_idx].timer_select,
+			chip->sensor[chan_idx].meas_interval,
+			btm_chan);
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_add_to_list(struct qpnp_adc_tm_chip *chip,
+				uint32_t dt_index,
+				struct qpnp_adc_tm_btm_param *param,
+				struct qpnp_vadc_chan_properties *chan_prop)
+{
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+	bool client_info_exists = false;
+
+	list_for_each_entry(client_info,
+			&chip->sensor[dt_index].thr_list, list) {
+		if (client_info->btm_param == param) {
+			client_info->low_thr_requested = chan_prop->low_thr;
+			client_info->high_thr_requested = chan_prop->high_thr;
+			client_info->state_requested = param->state_request;
+			client_info->state_req_copy = param->state_request;
+			client_info->notify_low_thr = false;
+			client_info->notify_high_thr = false;
+			client_info_exists = true;
+			pr_debug("client found\n");
+		}
+	}
+
+	if (!client_info_exists) {
+		client_info = devm_kzalloc(chip->dev,
+			sizeof(struct qpnp_adc_thr_client_info), GFP_KERNEL);
+		if (!client_info)
+			return -ENOMEM;
+
+		pr_debug("new client\n");
+		client_info->btm_param = param;
+		client_info->low_thr_requested = chan_prop->low_thr;
+		client_info->high_thr_requested = chan_prop->high_thr;
+		client_info->state_requested = param->state_request;
+		client_info->state_req_copy = param->state_request;
+
+		list_add_tail(&client_info->list,
+					&chip->sensor[dt_index].thr_list);
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_adc_tm_reg_update(struct qpnp_adc_tm_chip *chip,
+		uint16_t addr, u8 mask, bool state)
+{
+	u8 reg_value = 0;
+	int rc = 0;
+
+	rc = qpnp_adc_tm_read_reg(chip, addr, &reg_value, 1);
+	if (rc < 0) {
+		pr_err("read failed for addr:0x%x\n", addr);
+		return rc;
+	}
+
+	reg_value = reg_value & ~mask;
+	if (state)
+		reg_value |= mask;
+
+	pr_debug("state:%d, reg:0x%x with bits:0x%x and mask:0x%x\n",
+					state, addr, reg_value, ~mask);
+	rc = qpnp_adc_tm_write_reg(chip, addr, reg_value, 1);
+	if (rc < 0) {
+		pr_err("write failed for addr:%x\n", addr);
+		return rc;
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_read_thr_value(struct qpnp_adc_tm_chip *chip,
+			uint32_t btm_chan)
+{
+	int rc = 0;
+	u8 data_lsb = 0, data_msb = 0;
+	uint32_t btm_chan_idx = 0;
+	int32_t low_thr = 0, high_thr = 0;
+
+	if (!chip->adc_tm_hc) {
+		pr_err("Not applicable for VADC HC peripheral\n");
+		return -EINVAL;
+	}
+
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip,
+			adc_tm_data[btm_chan_idx].low_thr_lsb_addr,
+			&data_lsb, 1);
+	if (rc < 0) {
+		pr_err("low threshold lsb setting failed\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip,
+		adc_tm_data[btm_chan_idx].low_thr_msb_addr,
+		&data_msb, 1);
+	if (rc < 0) {
+		pr_err("low threshold msb setting failed\n");
+		return rc;
+	}
+
+	low_thr = (data_msb << 8) | data_lsb;
+
+	rc = qpnp_adc_tm_read_reg(chip,
+		adc_tm_data[btm_chan_idx].high_thr_lsb_addr,
+		&data_lsb, 1);
+	if (rc < 0) {
+		pr_err("high threshold lsb setting failed\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip,
+		adc_tm_data[btm_chan_idx].high_thr_msb_addr,
+		&data_msb, 1);
+	if (rc < 0) {
+		pr_err("high threshold msb setting failed\n");
+		return rc;
+	}
+
+	high_thr = (data_msb << 8) | data_lsb;
+
+	pr_debug("configured thresholds high:0x%x and low:0x%x\n",
+		high_thr, low_thr);
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_thr_update(struct qpnp_adc_tm_chip *chip,
+			uint32_t btm_chan, int32_t high_thr, int32_t low_thr)
+{
+	int rc = 0;
+	uint32_t btm_chan_idx = 0;
+
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	if (!chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_write_reg(chip,
+			adc_tm_data[btm_chan_idx].low_thr_lsb_addr,
+			QPNP_ADC_TM_THR_LSB_MASK(low_thr), 1);
+		if (rc < 0) {
+			pr_err("low threshold lsb setting failed\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip,
+			adc_tm_data[btm_chan_idx].low_thr_msb_addr,
+			QPNP_ADC_TM_THR_MSB_MASK(low_thr), 1);
+		if (rc < 0) {
+			pr_err("low threshold msb setting failed\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip,
+			adc_tm_data[btm_chan_idx].high_thr_lsb_addr,
+			QPNP_ADC_TM_THR_LSB_MASK(high_thr), 1);
+		if (rc < 0) {
+			pr_err("high threshold lsb setting failed\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip,
+			adc_tm_data[btm_chan_idx].high_thr_msb_addr,
+			QPNP_ADC_TM_THR_MSB_MASK(high_thr), 1);
+		if (rc < 0)
+			pr_err("high threshold msb setting failed\n");
+	} else {
+		rc = qpnp_adc_tm_write_reg(chip,
+			QPNP_BTM_Mn_LOW_THR0(btm_chan_idx),
+			QPNP_ADC_TM_THR_LSB_MASK(low_thr), 1);
+		if (rc < 0) {
+			pr_err("low threshold lsb setting failed\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip,
+			QPNP_BTM_Mn_LOW_THR1(btm_chan_idx),
+			QPNP_ADC_TM_THR_MSB_MASK(low_thr), 1);
+		if (rc < 0) {
+			pr_err("low threshold msb setting failed\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip,
+			QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx),
+			QPNP_ADC_TM_THR_LSB_MASK(high_thr), 1);
+		if (rc < 0) {
+			pr_err("high threshold lsb setting failed\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip,
+			QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx),
+			QPNP_ADC_TM_THR_MSB_MASK(high_thr), 1);
+		if (rc < 0)
+			pr_err("high threshold msb setting failed\n");
+
+	}
+
+	pr_debug("client requested high:%d and low:%d\n",
+		high_thr, low_thr);
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_manage_thresholds(struct qpnp_adc_tm_chip *chip,
+		uint32_t dt_index, uint32_t btm_chan)
+{
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+	struct list_head *thr_list;
+	int high_thr = 0, low_thr = 0, rc = 0;
+
+
+	/*
+	 * high_thr/low_thr starting point and reset the high_thr_set and
+	 * low_thr_set back to reset since the thresholds will be
+	 * recomputed.
+	 */
+	list_for_each(thr_list,
+			&chip->sensor[dt_index].thr_list) {
+		client_info = list_entry(thr_list,
+					struct qpnp_adc_thr_client_info, list);
+		high_thr = client_info->high_thr_requested;
+		low_thr = client_info->low_thr_requested;
+		client_info->high_thr_set = false;
+		client_info->low_thr_set = false;
+	}
+
+	pr_debug("init threshold is high:%d and low:%d\n", high_thr, low_thr);
+
+	/* Find the min of high_thr and max of low_thr */
+	list_for_each(thr_list,
+			&chip->sensor[dt_index].thr_list) {
+		client_info = list_entry(thr_list,
+					struct qpnp_adc_thr_client_info, list);
+		if ((client_info->state_req_copy == ADC_TM_HIGH_THR_ENABLE) ||
+			(client_info->state_req_copy ==
+						ADC_TM_HIGH_LOW_THR_ENABLE))
+			if (client_info->high_thr_requested < high_thr)
+				high_thr = client_info->high_thr_requested;
+
+		if ((client_info->state_req_copy == ADC_TM_LOW_THR_ENABLE) ||
+			(client_info->state_req_copy ==
+						ADC_TM_HIGH_LOW_THR_ENABLE))
+			if (client_info->low_thr_requested > low_thr)
+				low_thr = client_info->low_thr_requested;
+
+		pr_debug("threshold compared is high:%d and low:%d\n",
+				client_info->high_thr_requested,
+				client_info->low_thr_requested);
+		pr_debug("current threshold is high:%d and low:%d\n",
+							high_thr, low_thr);
+	}
+
+	/* Check which of the high_thr and low_thr got set */
+	list_for_each(thr_list,
+			&chip->sensor[dt_index].thr_list) {
+		client_info = list_entry(thr_list,
+					struct qpnp_adc_thr_client_info, list);
+		if ((client_info->state_req_copy == ADC_TM_HIGH_THR_ENABLE) ||
+			(client_info->state_req_copy ==
+						ADC_TM_HIGH_LOW_THR_ENABLE))
+			if (high_thr == client_info->high_thr_requested)
+				client_info->high_thr_set = true;
+
+		if ((client_info->state_req_copy == ADC_TM_LOW_THR_ENABLE) ||
+			(client_info->state_req_copy ==
+						ADC_TM_HIGH_LOW_THR_ENABLE))
+			if (low_thr == client_info->low_thr_requested)
+				client_info->low_thr_set = true;
+	}
+
+	rc = qpnp_adc_tm_thr_update(chip, btm_chan, high_thr, low_thr);
+	if (rc < 0)
+		pr_err("setting chan:%d threshold failed\n", btm_chan);
+
+	pr_debug("threshold written is high:%d and low:%d\n",
+							high_thr, low_thr);
+
+	return 0;
+}
+
+static int32_t qpnp_adc_tm_channel_configure(struct qpnp_adc_tm_chip *chip,
+			uint32_t btm_chan,
+			struct qpnp_vadc_chan_properties *chan_prop,
+			uint32_t amux_channel)
+{
+	int rc = 0, i = 0, chan_idx = 0;
+	bool chan_found = false, high_thr_set = false, low_thr_set = false;
+	u8 sensor_mask = 0;
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+	uint32_t btm_chan_idx = 0;
+
+	while (i < chip->max_channels_available) {
+		if (chip->sensor[i].btm_channel_num == btm_chan) {
+			chan_idx = i;
+			chan_found = true;
+			i++;
+		} else
+			i++;
+	}
+
+	if (!chan_found) {
+		pr_err("Channel not found\n");
+		return -EINVAL;
+	}
+
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	sensor_mask = 1 << chan_idx;
+	if (!chip->sensor[chan_idx].thermal_node) {
+		/* Update low and high notification thresholds */
+		rc = qpnp_adc_tm_manage_thresholds(chip, chan_idx,
+				btm_chan);
+		if (rc < 0) {
+			pr_err("setting chan:%d threshold failed\n", btm_chan);
+			return rc;
+		}
+
+		list_for_each_entry(client_info,
+				&chip->sensor[chan_idx].thr_list, list) {
+			if (client_info->high_thr_set == true)
+				high_thr_set = true;
+			if (client_info->low_thr_set == true)
+				low_thr_set = true;
+		}
+
+		if (low_thr_set) {
+			pr_debug("low sensor mask:%x with state:%d\n",
+					sensor_mask, chan_prop->state_request);
+			/* Enable low threshold's interrupt */
+			if (!chip->adc_tm_hc)
+				rc = qpnp_adc_tm_reg_update(chip,
+					QPNP_ADC_TM_LOW_THR_INT_EN,
+					sensor_mask, true);
+			else
+				rc = qpnp_adc_tm_reg_update(chip,
+					QPNP_BTM_Mn_EN(btm_chan_idx),
+					QPNP_BTM_Mn_LOW_THR_INT_EN, true);
+			if (rc < 0) {
+				pr_err("low thr enable err:%d\n", btm_chan);
+				return rc;
+			}
+		}
+
+		if (high_thr_set) {
+			/* Enable high threshold's interrupt */
+			pr_debug("high sensor mask:%x\n", sensor_mask);
+			if (!chip->adc_tm_hc)
+				rc = qpnp_adc_tm_reg_update(chip,
+					QPNP_ADC_TM_HIGH_THR_INT_EN,
+					sensor_mask, true);
+			else
+				rc = qpnp_adc_tm_reg_update(chip,
+					QPNP_BTM_Mn_EN(btm_chan_idx),
+					QPNP_BTM_Mn_HIGH_THR_INT_EN, true);
+			if (rc < 0) {
+				pr_err("high thr enable err:%d\n", btm_chan);
+				return rc;
+			}
+		}
+	}
+
+	/* Enable corresponding BTM channel measurement */
+	if (!chip->adc_tm_hc)
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_ADC_TM_MULTI_MEAS_EN, sensor_mask, true);
+	else
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_idx),
+			QPNP_BTM_Mn_MEAS_EN, true);
+	if (rc < 0) {
+		pr_err("multi measurement en failed\n");
+		return rc;
+	}
+
+	return rc;
+}
+
+static int32_t qpnp_adc_tm_hc_configure(struct qpnp_adc_tm_chip *chip,
+			struct qpnp_adc_amux_properties *chan_prop)
+{
+	u8 decimation = 0, fast_avg_ctl = 0;
+	u8 buf[8];
+	int rc = 0;
+	uint32_t btm_chan = 0, cal_type = 0, btm_chan_idx = 0;
+
+	/* Disable bank */
+	rc = qpnp_adc_tm_disable(chip);
+	if (rc)
+		return rc;
+
+	/* Decimation setup */
+	decimation = chan_prop->decimation;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_HC_ADC_DIG_PARAM,
+						decimation, 1);
+	if (rc < 0) {
+		pr_err("adc-tm digital parameter setup err\n");
+		return rc;
+	}
+
+	/* Fast averaging setup/enable */
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_BTM_HC_FAST_AVG_CTL,
+						&fast_avg_ctl, 1);
+	if (rc < 0) {
+		pr_err("adc-tm fast-avg enable read err\n");
+		return rc;
+	}
+	fast_avg_ctl |= chan_prop->fast_avg_setup;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_HC_FAST_AVG_CTL,
+						fast_avg_ctl, 1);
+	if (rc < 0) {
+		pr_err("adc-tm fast-avg enable write err\n");
+		return rc;
+	}
+
+	/* Read block registers for respective BTM channel */
+	btm_chan = chan_prop->chan_prop->tm_channel_select;
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip,
+			QPNP_BTM_Mn_ADC_CH_SEL_CTL(btm_chan_idx), buf, 8);
+	if (rc < 0) {
+		pr_err("qpnp adc configure block read failed\n");
+		return rc;
+	}
+
+	/* Update ADC channel sel */
+	rc = qpnp_adc_tm_write_reg(chip,
+			QPNP_BTM_Mn_ADC_CH_SEL_CTL(btm_chan_idx),
+				chan_prop->amux_channel, 1);
+	if (rc < 0) {
+		pr_err("adc-tm channel amux select failed\n");
+		return rc;
+	}
+
+	/* Manage thresholds */
+	rc = qpnp_adc_tm_channel_configure(chip, btm_chan,
+			chan_prop->chan_prop, chan_prop->amux_channel);
+	if (rc < 0) {
+		pr_err("adc-tm channel threshold configure failed\n");
+		return rc;
+	}
+
+	/* Measurement interval setup */
+	rc = qpnp_adc_tm_timer_interval_select(chip, btm_chan,
+						chan_prop->chan_prop);
+	if (rc < 0) {
+		pr_err("adc-tm timer select failed\n");
+		return rc;
+	}
+
+	/* Set calibration select, hw_settle delay */
+	cal_type |= (chan_prop->calib_type << QPNP_BTM_CTL_CAL_SEL_MASK_SHIFT);
+	buf[6] &= ~QPNP_BTM_CTL_HW_SETTLE_DELAY_MASK;
+	buf[6] |= chan_prop->hw_settle_time;
+	buf[6] &= ~QPNP_BTM_CTL_CAL_SEL;
+	buf[6] |= cal_type;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_BTM_Mn_CTL(btm_chan_idx),
+								buf[6], 1);
+	if (rc < 0) {
+		pr_err("adc-tm hw-settle, calib sel failed\n");
+		return rc;
+	}
+
+	/* Enable bank */
+	rc = qpnp_adc_tm_enable(chip);
+	if (rc)
+		return rc;
+
+	/* Request conversion */
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_CONV_REQ, QPNP_CONV_REQ_SET, 1);
+	if (rc < 0) {
+		pr_err("adc-tm request conversion failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int32_t qpnp_adc_tm_configure(struct qpnp_adc_tm_chip *chip,
+			struct qpnp_adc_amux_properties *chan_prop)
+{
+	u8 decimation = 0, op_cntrl = 0, mode_ctl = 0;
+	int rc = 0;
+	uint32_t btm_chan = 0;
+
+	/* Set measurement in single measurement mode */
+	mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+	rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+	if (rc < 0) {
+		pr_err("adc-tm single mode select failed\n");
+		return rc;
+	}
+
+	/* Disable bank */
+	rc = qpnp_adc_tm_disable(chip);
+	if (rc)
+		return rc;
+
+	/* Check if a conversion is in progress */
+	rc = qpnp_adc_tm_req_sts_check(chip);
+	if (rc < 0) {
+		pr_err("adc-tm req_sts check failed\n");
+		return rc;
+	}
+
+	/* Configure AMUX channel select for the corresponding BTM channel*/
+	btm_chan = chan_prop->chan_prop->tm_channel_select;
+	rc = qpnp_adc_tm_write_reg(chip, btm_chan, chan_prop->amux_channel, 1);
+	if (rc < 0) {
+		pr_err("adc-tm channel selection err\n");
+		return rc;
+	}
+
+	/* Digital parameter setup */
+	decimation |= chan_prop->decimation <<
+				QPNP_ADC_DIG_DEC_RATIO_SEL_SHIFT;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_DIG_PARAM, decimation, 1);
+	if (rc < 0) {
+		pr_err("adc-tm digital parameter setup err\n");
+		return rc;
+	}
+
+	/* Hardware setting time */
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_HW_SETTLE_DELAY,
+					chan_prop->hw_settle_time, 1);
+	if (rc < 0) {
+		pr_err("adc-tm hw settling time setup err\n");
+		return rc;
+	}
+
+	/* Fast averaging setup/enable */
+	rc = qpnp_adc_tm_fast_avg_en(chip, &chan_prop->fast_avg_setup);
+	if (rc < 0) {
+		pr_err("adc-tm fast-avg enable err\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_FAST_AVG_CTL,
+				chan_prop->fast_avg_setup, 1);
+	if (rc < 0) {
+		pr_err("adc-tm fast-avg setup err\n");
+		return rc;
+	}
+
+	/* Measurement interval setup */
+	rc = qpnp_adc_tm_timer_interval_select(chip, btm_chan,
+						chan_prop->chan_prop);
+	if (rc < 0) {
+		pr_err("adc-tm timer select failed\n");
+		return rc;
+	}
+
+	/* Channel configuration setup */
+	rc = qpnp_adc_tm_channel_configure(chip, btm_chan,
+			chan_prop->chan_prop, chan_prop->amux_channel);
+	if (rc < 0) {
+		pr_err("adc-tm channel configure failed\n");
+		return rc;
+	}
+
+	/* Recurring interval measurement enable */
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_MEAS_INTERVAL_OP_CTL,
+							&op_cntrl, 1);
+	op_cntrl |= QPNP_ADC_MEAS_INTERVAL_OP;
+	rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_MEAS_INTERVAL_OP_CTL,
+			op_cntrl, true);
+	if (rc < 0) {
+		pr_err("adc-tm meas interval op configure failed\n");
+		return rc;
+	}
+
+	/* Enable bank */
+	rc = qpnp_adc_tm_enable(chip);
+	if (rc)
+		return rc;
+
+	/* Request conversion */
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_CONV_REQ, QPNP_CONV_REQ_SET, 1);
+	if (rc < 0) {
+		pr_err("adc-tm request conversion failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int qpnp_adc_tm_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+
+	if ((IS_ERR(adc_tm)) || qpnp_adc_tm_check_revision(
+			adc_tm->chip, adc_tm->btm_channel_num))
+		return -EINVAL;
+
+	*mode = adc_tm->mode;
+
+	return 0;
+}
+
+static int qpnp_adc_tm_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+	struct qpnp_adc_tm_chip *chip = adc_tm->chip;
+	int rc = 0, channel;
+	u8 sensor_mask = 0, mode_ctl = 0;
+	uint32_t btm_chan_idx = 0, btm_chan = 0;
+
+	if (qpnp_adc_tm_is_valid(chip)) {
+		pr_err("invalid device\n");
+		return -ENODEV;
+	}
+
+	if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num))
+		return -EINVAL;
+
+	mutex_lock(&chip->adc->adc_lock);
+
+	btm_chan = adc_tm->btm_channel_num;
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		goto fail;
+	}
+
+	if (mode == THERMAL_DEVICE_ENABLED) {
+		chip->adc->amux_prop->amux_channel =
+					adc_tm->vadc_channel_num;
+		channel = adc_tm->sensor_num;
+		chip->adc->amux_prop->decimation =
+			chip->adc->adc_channels[channel].adc_decimation;
+		chip->adc->amux_prop->hw_settle_time =
+			chip->adc->adc_channels[channel].hw_settle_time;
+		chip->adc->amux_prop->fast_avg_setup =
+			chip->adc->adc_channels[channel].fast_avg_setup;
+		chip->adc->amux_prop->mode_sel =
+			ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+		chip->adc->amux_prop->chan_prop->low_thr = adc_tm->low_thr;
+		chip->adc->amux_prop->chan_prop->high_thr = adc_tm->high_thr;
+		chip->adc->amux_prop->chan_prop->tm_channel_select =
+			adc_tm->btm_channel_num;
+		chip->adc->amux_prop->calib_type =
+			chip->adc->adc_channels[channel].calib_type;
+
+		if (!chip->adc_tm_hc) {
+			rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
+			if (rc) {
+				pr_err("adc-tm configure failed with %d\n", rc);
+				goto fail;
+			}
+		} else {
+			rc = qpnp_adc_tm_hc_configure(chip,
+							chip->adc->amux_prop);
+			if (rc) {
+				pr_err("hc configure failed with %d\n", rc);
+				goto fail;
+			}
+		}
+	} else if (mode == THERMAL_DEVICE_DISABLED) {
+		sensor_mask = 1 << adc_tm->sensor_num;
+
+		if (!chip->adc_tm_hc) {
+			mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+			rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+			if (rc < 0) {
+				pr_err("adc-tm single mode select failed\n");
+				goto fail;
+			}
+		}
+
+		/* Disable bank */
+		rc = qpnp_adc_tm_disable(chip);
+		if (rc < 0) {
+			pr_err("adc-tm disable failed\n");
+			goto fail;
+		}
+
+		if (!chip->adc_tm_hc) {
+			/* Check if a conversion is in progress */
+			rc = qpnp_adc_tm_req_sts_check(chip);
+			if (rc < 0) {
+				pr_err("adc-tm req_sts check failed\n");
+				goto fail;
+			}
+
+			rc = qpnp_adc_tm_reg_update(chip,
+				QPNP_ADC_TM_MULTI_MEAS_EN, sensor_mask, false);
+			if (rc < 0) {
+				pr_err("multi measurement update failed\n");
+				goto fail;
+			}
+		} else {
+			rc = qpnp_adc_tm_reg_update(chip,
+				QPNP_BTM_Mn_EN(btm_chan_idx),
+				QPNP_BTM_Mn_MEAS_EN, false);
+			if (rc < 0) {
+				pr_err("multi measurement disable failed\n");
+				goto fail;
+			}
+		}
+
+		rc = qpnp_adc_tm_enable_if_channel_meas(chip);
+		if (rc < 0) {
+			pr_err("re-enabling measurement failed\n");
+			goto fail;
+		}
+	}
+
+	adc_tm->mode = mode;
+
+fail:
+	mutex_unlock(&chip->adc->adc_lock);
+
+	return 0;
+}
+
+static int qpnp_adc_tm_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+	struct qpnp_adc_tm_chip *chip = adc_tm->chip;
+
+	if (qpnp_adc_tm_is_valid(chip))
+		return -ENODEV;
+
+	if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num))
+		return -EINVAL;
+
+	switch (trip) {
+	case ADC_TM_TRIP_HIGH_WARM:
+		*type = THERMAL_TRIP_CONFIGURABLE_HI;
+	break;
+	case ADC_TM_TRIP_LOW_COOL:
+		*type = THERMAL_TRIP_CONFIGURABLE_LOW;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_adc_tm_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, int *temp)
+{
+	struct qpnp_adc_tm_sensor *adc_tm_sensor = thermal->devdata;
+	struct qpnp_adc_tm_chip *chip = adc_tm_sensor->chip;
+	int64_t result = 0;
+	u8 trip_cool_thr0, trip_cool_thr1, trip_warm_thr0, trip_warm_thr1;
+	unsigned int reg, rc = 0;
+	uint16_t reg_low_thr_lsb, reg_low_thr_msb;
+	uint16_t reg_high_thr_lsb, reg_high_thr_msb;
+	uint32_t btm_chan_idx = 0, btm_chan = 0;
+
+	if (qpnp_adc_tm_is_valid(chip))
+		return -ENODEV;
+
+	if (qpnp_adc_tm_check_revision(chip, adc_tm_sensor->btm_channel_num))
+		return -EINVAL;
+
+	btm_chan = adc_tm_sensor->btm_channel_num;
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	if (!chip->adc_tm_hc) {
+		reg_low_thr_lsb = adc_tm_data[btm_chan_idx].low_thr_lsb_addr;
+		reg_low_thr_msb = adc_tm_data[btm_chan_idx].low_thr_msb_addr;
+		reg_high_thr_lsb = adc_tm_data[btm_chan_idx].high_thr_lsb_addr;
+		reg_high_thr_msb = adc_tm_data[btm_chan_idx].high_thr_msb_addr;
+	} else {
+		reg_low_thr_lsb = QPNP_BTM_Mn_LOW_THR0(btm_chan_idx);
+		reg_low_thr_msb = QPNP_BTM_Mn_LOW_THR1(btm_chan_idx);
+		reg_high_thr_lsb = QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx);
+		reg_high_thr_msb = QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx);
+	}
+
+	switch (trip) {
+	case ADC_TM_TRIP_HIGH_WARM:
+		rc = qpnp_adc_tm_read_reg(chip, reg_low_thr_lsb,
+						&trip_warm_thr0, 1);
+		if (rc) {
+			pr_err("adc-tm low_thr_lsb err\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_read_reg(chip, reg_low_thr_msb,
+						&trip_warm_thr1, 1);
+		if (rc) {
+			pr_err("adc-tm low_thr_msb err\n");
+			return rc;
+		}
+	reg = (trip_warm_thr1 << 8) | trip_warm_thr0;
+	break;
+	case ADC_TM_TRIP_LOW_COOL:
+		rc = qpnp_adc_tm_read_reg(chip, reg_high_thr_lsb,
+						&trip_cool_thr0, 1);
+		if (rc) {
+			pr_err("adc-tm_tm high_thr_lsb err\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_read_reg(chip, reg_high_thr_msb,
+						&trip_cool_thr1, 1);
+		if (rc) {
+			pr_err("adc-tm_tm high_thr_lsb err\n");
+			return rc;
+		}
+	reg = (trip_cool_thr1 << 8) | trip_cool_thr0;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	rc = qpnp_adc_tm_scale_voltage_therm_pu2(chip->vadc_dev,
+					chip->adc->adc_prop, reg, &result);
+	if (rc < 0) {
+		pr_err("Failed to lookup the therm thresholds\n");
+		return rc;
+	}
+
+	*temp = result;
+
+	return 0;
+}
+
+static int qpnp_adc_tm_set_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, int temp)
+{
+	struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+	struct qpnp_adc_tm_chip *chip = adc_tm->chip;
+	struct qpnp_adc_tm_config tm_config;
+	u8 trip_cool_thr0, trip_cool_thr1, trip_warm_thr0, trip_warm_thr1;
+	uint16_t reg_low_thr_lsb, reg_low_thr_msb;
+	uint16_t reg_high_thr_lsb, reg_high_thr_msb;
+	int rc = 0;
+	uint32_t btm_chan = 0, btm_chan_idx = 0;
+
+	if (qpnp_adc_tm_is_valid(chip))
+		return -ENODEV;
+
+	if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num))
+		return -EINVAL;
+
+	tm_config.channel = adc_tm->vadc_channel_num;
+	tm_config.high_thr_temp = tm_config.low_thr_temp = 0;
+	switch (trip) {
+	case ADC_TM_TRIP_HIGH_WARM:
+		tm_config.high_thr_temp = temp;
+		break;
+	case ADC_TM_TRIP_LOW_COOL:
+		tm_config.low_thr_temp = temp;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	pr_debug("requested a high - %d and low - %d with trip - %d\n",
+			tm_config.high_thr_temp, tm_config.low_thr_temp, trip);
+	rc = qpnp_adc_tm_scale_therm_voltage_pu2(chip->vadc_dev,
+				chip->adc->adc_prop, &tm_config);
+	if (rc < 0) {
+		pr_err("Failed to lookup the adc-tm thresholds\n");
+		return rc;
+	}
+
+	trip_warm_thr0 = ((tm_config.low_thr_voltage << 24) >> 24);
+	trip_warm_thr1 = ((tm_config.low_thr_voltage << 16) >> 24);
+	trip_cool_thr0 = ((tm_config.high_thr_voltage << 24) >> 24);
+	trip_cool_thr1 = ((tm_config.high_thr_voltage << 16) >> 24);
+
+	pr_debug("low_thr:0x%llx, high_thr:0x%llx\n", tm_config.low_thr_voltage,
+				tm_config.high_thr_voltage);
+
+	btm_chan = adc_tm->btm_channel_num;
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	if (!chip->adc_tm_hc) {
+		reg_low_thr_lsb = adc_tm_data[btm_chan_idx].low_thr_lsb_addr;
+		reg_low_thr_msb = adc_tm_data[btm_chan_idx].low_thr_msb_addr;
+		reg_high_thr_lsb = adc_tm_data[btm_chan_idx].high_thr_lsb_addr;
+		reg_high_thr_msb = adc_tm_data[btm_chan_idx].high_thr_msb_addr;
+	} else {
+		reg_low_thr_lsb = QPNP_BTM_Mn_LOW_THR0(btm_chan_idx);
+		reg_low_thr_msb = QPNP_BTM_Mn_LOW_THR1(btm_chan_idx);
+		reg_high_thr_lsb = QPNP_BTM_Mn_HIGH_THR0(btm_chan_idx);
+		reg_high_thr_msb = QPNP_BTM_Mn_HIGH_THR1(btm_chan_idx);
+	}
+
+	switch (trip) {
+	case ADC_TM_TRIP_HIGH_WARM:
+		rc = qpnp_adc_tm_write_reg(chip, reg_low_thr_lsb,
+						trip_cool_thr0, 1);
+		if (rc) {
+			pr_err("adc-tm_tm read threshold err\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip, reg_low_thr_msb,
+						trip_cool_thr1, 1);
+		if (rc) {
+			pr_err("adc-tm_tm read threshold err\n");
+			return rc;
+		}
+	adc_tm->low_thr = tm_config.high_thr_voltage;
+	break;
+	case ADC_TM_TRIP_LOW_COOL:
+		rc = qpnp_adc_tm_write_reg(chip, reg_high_thr_lsb,
+						trip_warm_thr0, 1);
+		if (rc) {
+			pr_err("adc-tm_tm read threshold err\n");
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_write_reg(chip, reg_high_thr_msb,
+						trip_warm_thr1, 1);
+		if (rc) {
+			pr_err("adc-tm_tm read threshold err\n");
+			return rc;
+		}
+	adc_tm->high_thr = tm_config.low_thr_voltage;
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void notify_battery_therm(struct qpnp_adc_tm_sensor *adc_tm)
+{
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+
+	list_for_each_entry(client_info,
+			&adc_tm->thr_list, list) {
+		/* Batt therm's warm temperature translates to low voltage */
+		if (client_info->notify_low_thr) {
+			/* HIGH_STATE = WARM_TEMP for battery client */
+			client_info->btm_param->threshold_notification(
+			ADC_TM_WARM_STATE, client_info->btm_param->btm_ctx);
+			client_info->notify_low_thr = false;
+		}
+
+		/* Batt therm's cool temperature translates to high voltage */
+		if (client_info->notify_high_thr) {
+			/* LOW_STATE = COOL_TEMP for battery client */
+			client_info->btm_param->threshold_notification(
+			ADC_TM_COOL_STATE, client_info->btm_param->btm_ctx);
+			client_info->notify_high_thr = false;
+		}
+	}
+}
+
+static void notify_clients(struct qpnp_adc_tm_sensor *adc_tm)
+{
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+
+	list_for_each_entry(client_info,
+			&adc_tm->thr_list, list) {
+		/* For non batt therm clients */
+		if (client_info->notify_low_thr) {
+			if (client_info->btm_param->threshold_notification
+								!= NULL) {
+				pr_debug("notify kernel with low state\n");
+				client_info->btm_param->threshold_notification(
+					ADC_TM_LOW_STATE,
+					client_info->btm_param->btm_ctx);
+				client_info->notify_low_thr = false;
+			}
+		}
+
+		if (client_info->notify_high_thr) {
+			if (client_info->btm_param->threshold_notification
+								!= NULL) {
+				pr_debug("notify kernel with high state\n");
+				client_info->btm_param->threshold_notification(
+					ADC_TM_HIGH_STATE,
+					client_info->btm_param->btm_ctx);
+				client_info->notify_high_thr = false;
+			}
+		}
+	}
+}
+
+static void notify_adc_tm_fn(struct work_struct *work)
+{
+	struct qpnp_adc_tm_sensor *adc_tm = container_of(work,
+		struct qpnp_adc_tm_sensor, work);
+	struct qpnp_adc_tm_chip *chip = adc_tm->chip;
+
+	if (adc_tm->thermal_node) {
+		sysfs_notify(&adc_tm->tz_dev->device.kobj,
+					NULL, "type");
+		pr_debug("notifying uspace client\n");
+	} else {
+		if (adc_tm->scale_type == SCALE_RBATT_THERM)
+			notify_battery_therm(adc_tm);
+		else
+			notify_clients(adc_tm);
+	}
+
+	atomic_dec(&chip->wq_cnt);
+}
+
+static int qpnp_adc_tm_activate_trip_type(struct thermal_zone_device *thermal,
+			int trip, enum thermal_trip_activation_mode mode)
+{
+	struct qpnp_adc_tm_sensor *adc_tm = thermal->devdata;
+	struct qpnp_adc_tm_chip *chip = adc_tm->chip;
+	int rc = 0, sensor_mask = 0;
+	u8 thr_int_en = 0;
+	bool state = false;
+	uint32_t btm_chan_idx = 0, btm_chan = 0;
+
+	if (qpnp_adc_tm_is_valid(chip))
+		return -ENODEV;
+
+	if (qpnp_adc_tm_check_revision(chip, adc_tm->btm_channel_num))
+		return -EINVAL;
+
+	if (mode == THERMAL_TRIP_ACTIVATION_ENABLED)
+		state = true;
+
+	sensor_mask = 1 << adc_tm->sensor_num;
+
+	pr_debug("Sensor number:%x with state:%d\n",
+					adc_tm->sensor_num, state);
+
+	btm_chan = adc_tm->btm_channel_num;
+	rc = qpnp_adc_tm_get_btm_idx(chip, btm_chan, &btm_chan_idx);
+	if (rc < 0) {
+		pr_err("Invalid btm channel idx\n");
+		return rc;
+	}
+
+	switch (trip) {
+	case ADC_TM_TRIP_HIGH_WARM:
+		/* low_thr (lower voltage) for higher temp */
+		thr_int_en = adc_tm_data[btm_chan_idx].low_thr_int_chan_en;
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_reg_update(chip,
+				QPNP_ADC_TM_LOW_THR_INT_EN,
+				sensor_mask, state);
+		else
+			rc = qpnp_adc_tm_reg_update(chip,
+				QPNP_BTM_Mn_EN(btm_chan_idx),
+				QPNP_BTM_Mn_LOW_THR_INT_EN, state);
+		if (rc)
+			pr_err("channel:%x failed\n", btm_chan);
+	break;
+	case ADC_TM_TRIP_LOW_COOL:
+		/* high_thr (higher voltage) for cooler temp */
+		thr_int_en = adc_tm_data[btm_chan_idx].high_thr_int_chan_en;
+		if (!chip->adc_tm_hc)
+			rc = qpnp_adc_tm_reg_update(chip,
+				QPNP_ADC_TM_HIGH_THR_INT_EN,
+				sensor_mask, state);
+		else
+			rc = qpnp_adc_tm_reg_update(chip,
+				QPNP_BTM_Mn_EN(btm_chan_idx),
+				QPNP_BTM_Mn_HIGH_THR_INT_EN, state);
+		if (rc)
+			pr_err("channel:%x failed\n", btm_chan);
+	break;
+	default:
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int qpnp_adc_tm_recalib_request_check(struct qpnp_adc_tm_chip *chip,
+			int sensor_num, u8 status_high, u8 *notify_check)
+{
+	int rc = 0;
+	u8 sensor_mask = 0, mode_ctl = 0;
+	int32_t old_thr = 0, new_thr = 0;
+	uint32_t channel, btm_chan_num, scale_type;
+	struct qpnp_vadc_result result;
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+	struct list_head *thr_list;
+	bool status = false;
+
+	if (!chip->adc_tm_recalib_check) {
+		*notify_check = 1;
+		return rc;
+	}
+
+	list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
+		client_info = list_entry(thr_list,
+				struct qpnp_adc_thr_client_info, list);
+		channel = client_info->btm_param->channel;
+		btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
+		sensor_mask = 1 << sensor_num;
+
+		rc = qpnp_vadc_read(chip->vadc_dev, channel, &result);
+		if (rc < 0) {
+			pr_err("failure to read vadc channel=%d\n",
+					client_info->btm_param->channel);
+			goto fail;
+		}
+		new_thr = result.physical;
+
+		if (status_high)
+			old_thr = client_info->btm_param->high_thr;
+		else
+			old_thr = client_info->btm_param->low_thr;
+
+		if (new_thr > old_thr)
+			status = (status_high) ? true : false;
+		else
+			status = (status_high) ? false : true;
+
+		pr_debug(
+			"recalib:sen=%d, new_thr=%d, new_thr_adc_code=0x%x, old_thr=%d status=%d valid_status=%d\n",
+			sensor_num, new_thr, result.adc_code,
+			old_thr, status_high, status);
+
+		rc = qpnp_adc_tm_read_thr_value(chip, btm_chan_num);
+		if (rc < 0) {
+			pr_err("adc-tm thresholds read failed\n");
+			goto fail;
+		}
+
+		if (status) {
+			*notify_check = 1;
+			pr_debug("Client can be notify\n");
+			return rc;
+		}
+
+		pr_debug("Client can not be notify, restart measurement\n");
+		/* Set measurement in single measurement mode */
+		mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+		rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+		if (rc < 0) {
+			pr_err("adc-tm single mode select failed\n");
+			goto fail;
+		}
+
+		/* Disable bank */
+		rc = qpnp_adc_tm_disable(chip);
+		if (rc < 0) {
+			pr_err("adc-tm disable failed\n");
+			goto fail;
+		}
+
+		/* Check if a conversion is in progress */
+		rc = qpnp_adc_tm_req_sts_check(chip);
+		if (rc < 0) {
+			pr_err("adc-tm req_sts check failed\n");
+			goto fail;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+							sensor_mask, false);
+		if (rc < 0) {
+			pr_err("low threshold int write failed\n");
+			goto fail;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+							sensor_mask, false);
+		if (rc < 0) {
+			pr_err("high threshold int enable failed\n");
+			goto fail;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+							sensor_mask, false);
+		if (rc < 0) {
+			pr_err("multi measurement en failed\n");
+			goto fail;
+		}
+
+		/* restart measurement */
+		scale_type = chip->sensor[sensor_num].scale_type;
+		chip->adc->amux_prop->amux_channel = channel;
+		chip->adc->amux_prop->decimation =
+			chip->adc->adc_channels[sensor_num].adc_decimation;
+		chip->adc->amux_prop->hw_settle_time =
+			chip->adc->adc_channels[sensor_num].hw_settle_time;
+		chip->adc->amux_prop->fast_avg_setup =
+			chip->adc->adc_channels[sensor_num].fast_avg_setup;
+		chip->adc->amux_prop->mode_sel =
+			ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+		adc_tm_rscale_fn[scale_type].chan(chip->vadc_dev,
+				client_info->btm_param,
+				&chip->adc->amux_prop->chan_prop->low_thr,
+				&chip->adc->amux_prop->chan_prop->high_thr);
+		qpnp_adc_tm_add_to_list(chip, sensor_num,
+				client_info->btm_param,
+				chip->adc->amux_prop->chan_prop);
+		chip->adc->amux_prop->chan_prop->tm_channel_select =
+				chip->sensor[sensor_num].btm_channel_num;
+		chip->adc->amux_prop->chan_prop->state_request =
+				client_info->btm_param->state_request;
+
+		rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
+		if (rc) {
+			pr_err("adc-tm configure failed with %d\n", rc);
+			goto fail;
+		}
+		*notify_check = 0;
+		pr_debug("BTM channel reconfigured for measuremnt\n");
+	}
+fail:
+	return rc;
+}
+
+static int qpnp_adc_tm_disable_rearm_high_thresholds(
+			struct qpnp_adc_tm_chip *chip, int sensor_num)
+{
+
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+	struct list_head *thr_list;
+	uint32_t btm_chan_num = 0;
+	u8 sensor_mask = 0, notify_check = 0;
+	int rc = 0;
+
+	btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
+	pr_debug("high:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n",
+		sensor_num, chip->th_info.adc_tm_high_enable,
+		chip->th_info.adc_tm_low_enable,
+		chip->th_info.qpnp_adc_tm_meas_en);
+	if (!chip->sensor[sensor_num].thermal_node) {
+		/*
+		 * For non thermal registered clients such as usb_id,
+		 * vbatt, pmic_therm
+		 */
+		sensor_mask = 1 << sensor_num;
+		pr_debug("non thermal node - mask:%x\n", sensor_mask);
+		rc = qpnp_adc_tm_recalib_request_check(chip,
+				sensor_num, true, &notify_check);
+		if (rc < 0 || !notify_check) {
+			pr_debug("Calib recheck re-armed rc=%d\n", rc);
+			chip->th_info.adc_tm_high_enable = 0;
+			return rc;
+		}
+	} else {
+		/*
+		 * Uses the thermal sysfs registered device to disable
+		 * the corresponding high voltage threshold which
+		 * is triggered by low temp
+		 */
+		sensor_mask = 1 << sensor_num;
+		pr_debug("thermal node with mask:%x\n", sensor_mask);
+		rc = qpnp_adc_tm_activate_trip_type(
+			chip->sensor[sensor_num].tz_dev,
+			ADC_TM_TRIP_LOW_COOL,
+			THERMAL_TRIP_ACTIVATION_DISABLED);
+		if (rc < 0) {
+			pr_err("notify error:%d\n", sensor_num);
+			return rc;
+		}
+	}
+	list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
+		client_info = list_entry(thr_list,
+				struct qpnp_adc_thr_client_info, list);
+		if (client_info->high_thr_set) {
+			client_info->high_thr_set = false;
+			client_info->notify_high_thr = true;
+			if (client_info->state_req_copy ==
+					ADC_TM_HIGH_LOW_THR_ENABLE)
+				client_info->state_req_copy =
+						ADC_TM_LOW_THR_ENABLE;
+			else
+				client_info->state_req_copy =
+						ADC_TM_HIGH_THR_DISABLE;
+		}
+	}
+	qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
+
+	if (!chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_ADC_TM_MULTI_MEAS_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("multi meas disable failed\n");
+			return rc;
+		}
+	} else {
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_BTM_Mn_EN(sensor_num),
+			QPNP_BTM_Mn_MEAS_EN, false);
+		if (rc < 0) {
+			pr_err("multi meas disable failed\n");
+			return rc;
+		}
+	}
+
+	rc = qpnp_adc_tm_enable_if_channel_meas(chip);
+	if (rc < 0) {
+		pr_err("re-enabling measurement failed\n");
+		return rc;
+	}
+
+	queue_work(chip->sensor[sensor_num].req_wq,
+				&chip->sensor[sensor_num].work);
+
+	return rc;
+}
+
+static int qpnp_adc_tm_disable_rearm_low_thresholds(
+			struct qpnp_adc_tm_chip *chip, int sensor_num)
+{
+	struct qpnp_adc_thr_client_info *client_info = NULL;
+	struct list_head *thr_list;
+	uint32_t btm_chan_num = 0;
+	u8 sensor_mask = 0, notify_check = 0;
+	int rc = 0;
+
+	btm_chan_num = chip->sensor[sensor_num].btm_channel_num;
+	pr_debug("low:sen:%d, hs:0x%x, ls:0x%x, meas_en:0x%x\n",
+		sensor_num, chip->th_info.adc_tm_high_enable,
+		chip->th_info.adc_tm_low_enable,
+		chip->th_info.qpnp_adc_tm_meas_en);
+	if (!chip->sensor[sensor_num].thermal_node) {
+		/*
+		 * For non thermal registered clients such as usb_id,
+		 * vbatt, pmic_therm
+		 */
+		pr_debug("non thermal node - mask:%x\n", sensor_mask);
+		rc = qpnp_adc_tm_recalib_request_check(chip,
+				sensor_num, false, &notify_check);
+		if (rc < 0 || !notify_check) {
+			pr_debug("Calib recheck re-armed rc=%d\n", rc);
+			chip->th_info.adc_tm_low_enable = 0;
+			return rc;
+		}
+		sensor_mask = 1 << sensor_num;
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_ADC_TM_LOW_THR_INT_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("low threshold int read failed\n");
+			return rc;
+		}
+	} else {
+		/*
+		 * Uses the thermal sysfs registered device to disable
+		 * the corresponding high voltage threshold which
+		 * is triggered by low temp
+		 */
+		sensor_mask = 1 << sensor_num;
+		pr_debug("thermal node with mask:%x\n", sensor_mask);
+		rc = qpnp_adc_tm_activate_trip_type(
+			chip->sensor[sensor_num].tz_dev,
+			ADC_TM_TRIP_HIGH_WARM,
+			THERMAL_TRIP_ACTIVATION_DISABLED);
+		if (rc < 0) {
+			pr_err("notify error:%d\n", sensor_num);
+			return rc;
+		}
+	}
+	list_for_each(thr_list, &chip->sensor[sensor_num].thr_list) {
+		client_info = list_entry(thr_list,
+				struct qpnp_adc_thr_client_info, list);
+		if (client_info->low_thr_set) {
+			client_info->low_thr_set = false;
+			client_info->notify_low_thr = true;
+			if (client_info->state_req_copy ==
+					ADC_TM_HIGH_LOW_THR_ENABLE)
+				client_info->state_req_copy =
+						ADC_TM_HIGH_THR_ENABLE;
+			else
+				client_info->state_req_copy =
+						ADC_TM_LOW_THR_DISABLE;
+		}
+	}
+	qpnp_adc_tm_manage_thresholds(chip, sensor_num, btm_chan_num);
+
+	if (!chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_ADC_TM_MULTI_MEAS_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("multi meas disable failed\n");
+			return rc;
+		}
+	} else {
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_BTM_Mn_EN(sensor_num),
+			QPNP_BTM_Mn_MEAS_EN, false);
+		if (rc < 0) {
+			pr_err("multi meas disable failed\n");
+			return rc;
+		}
+	}
+
+	rc = qpnp_adc_tm_enable_if_channel_meas(chip);
+	if (rc < 0) {
+		pr_err("re-enabling measurement failed\n");
+		return rc;
+	}
+
+	queue_work(chip->sensor[sensor_num].req_wq,
+				&chip->sensor[sensor_num].work);
+
+	return rc;
+}
+
+static int qpnp_adc_tm_read_status(struct qpnp_adc_tm_chip *chip)
+{
+	int rc = 0, sensor_num = 0;
+
+	if (qpnp_adc_tm_is_valid(chip))
+		return -ENODEV;
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&chip->adc->adc_lock);
+
+	if (!chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_req_sts_check(chip);
+		if (rc) {
+			pr_err("adc-tm-tm req sts check failed with %d\n", rc);
+			goto fail;
+		}
+	}
+
+	while (sensor_num < chip->max_channels_available) {
+		if (chip->sensor[sensor_num].high_thr_triggered) {
+			rc = qpnp_adc_tm_disable_rearm_high_thresholds(
+					chip, sensor_num);
+			if (rc) {
+				pr_err("rearm threshold failed\n");
+				goto fail;
+			}
+			chip->sensor[sensor_num].high_thr_triggered = false;
+		}
+		sensor_num++;
+	}
+
+	sensor_num = 0;
+	while (sensor_num < chip->max_channels_available) {
+		if (chip->sensor[sensor_num].low_thr_triggered) {
+			rc = qpnp_adc_tm_disable_rearm_low_thresholds(
+					chip, sensor_num);
+			if (rc) {
+				pr_err("rearm threshold failed\n");
+				goto fail;
+			}
+			chip->sensor[sensor_num].low_thr_triggered = false;
+		}
+		sensor_num++;
+	}
+
+fail:
+	mutex_unlock(&chip->adc->adc_lock);
+
+	if (rc < 0 || (!chip->th_info.adc_tm_high_enable &&
+					!chip->th_info.adc_tm_low_enable))
+		atomic_dec(&chip->wq_cnt);
+
+	return rc;
+}
+
+static void qpnp_adc_tm_high_thr_work(struct work_struct *work)
+{
+	struct qpnp_adc_tm_chip *chip = container_of(work,
+			struct qpnp_adc_tm_chip, trigger_high_thr_work);
+	int rc;
+
+	/* disable the vote if applicable */
+	if (chip->adc_vote_enable && chip->adc->hkadc_ldo &&
+					chip->adc->hkadc_ldo_ok) {
+		qpnp_adc_disable_voltage(chip->adc);
+		chip->adc_vote_enable = false;
+	}
+
+	pr_debug("thr:0x%x\n", chip->th_info.adc_tm_high_enable);
+
+	rc = qpnp_adc_tm_read_status(chip);
+	if (rc < 0)
+		pr_err("adc-tm high thr work failed\n");
+}
+
+static irqreturn_t qpnp_adc_tm_high_thr_isr(int irq, void *data)
+{
+	struct qpnp_adc_tm_chip *chip = data;
+	u8 mode_ctl = 0, status1 = 0, sensor_mask = 0;
+	int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0;
+
+	mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+	/* Set measurement in single measurement mode */
+	qpnp_adc_tm_mode_select(chip, mode_ctl);
+
+	qpnp_adc_tm_disable(chip);
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+	if (rc) {
+		pr_err("adc-tm read status1 failed\n");
+		return IRQ_HANDLED;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS_HIGH,
+					&chip->th_info.status_high, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read status high failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+				&chip->th_info.adc_tm_high_thr_set, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read high thr failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	/*
+	 * Check which interrupt threshold is lower and measure against the
+	 * enabled channel.
+	 */
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+				&chip->th_info.qpnp_adc_tm_meas_en, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read status high failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	chip->th_info.adc_tm_high_enable = chip->th_info.qpnp_adc_tm_meas_en &
+						chip->th_info.status_high;
+	chip->th_info.adc_tm_high_enable &= chip->th_info.adc_tm_high_thr_set;
+
+	sensor_notify_num = chip->th_info.adc_tm_high_enable;
+	while (i < chip->max_channels_available) {
+		if ((sensor_notify_num & 0x1) == 1)
+			sensor_num = i;
+		sensor_notify_num >>= 1;
+		i++;
+	}
+
+	if (!chip->sensor[sensor_num].thermal_node) {
+		sensor_mask = 1 << sensor_num;
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_ADC_TM_HIGH_THR_INT_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("high threshold int read failed\n");
+			return IRQ_HANDLED;
+		}
+	} else {
+		/*
+		 * Uses the thermal sysfs registered device to disable
+		 * the corresponding high voltage threshold which
+		 * is triggered by low temp
+		 */
+		pr_debug("thermal node with mask:%x\n", sensor_mask);
+		rc = qpnp_adc_tm_activate_trip_type(
+			chip->sensor[sensor_num].tz_dev,
+			ADC_TM_TRIP_LOW_COOL,
+			THERMAL_TRIP_ACTIVATION_DISABLED);
+		if (rc < 0) {
+			pr_err("notify error:%d\n", sensor_num);
+			return IRQ_HANDLED;
+		}
+	}
+
+	atomic_inc(&chip->wq_cnt);
+	queue_work(chip->high_thr_wq, &chip->trigger_high_thr_work);
+
+	return IRQ_HANDLED;
+}
+
+static void qpnp_adc_tm_low_thr_work(struct work_struct *work)
+{
+	struct qpnp_adc_tm_chip *chip = container_of(work,
+			struct qpnp_adc_tm_chip, trigger_low_thr_work);
+	int rc;
+
+	/* disable the vote if applicable */
+	if (chip->adc_vote_enable && chip->adc->hkadc_ldo &&
+					chip->adc->hkadc_ldo_ok) {
+		qpnp_adc_disable_voltage(chip->adc);
+		chip->adc_vote_enable = false;
+	}
+
+	pr_debug("thr:0x%x\n", chip->th_info.adc_tm_low_enable);
+
+	rc = qpnp_adc_tm_read_status(chip);
+	if (rc < 0)
+		pr_err("adc-tm low thr work failed\n");
+}
+
+static irqreturn_t qpnp_adc_tm_low_thr_isr(int irq, void *data)
+{
+	struct qpnp_adc_tm_chip *chip = data;
+	u8 mode_ctl = 0, status1 = 0, sensor_mask = 0;
+	int rc = 0, sensor_notify_num = 0, i = 0, sensor_num = 0;
+
+	mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+	/* Set measurement in single measurement mode */
+	qpnp_adc_tm_mode_select(chip, mode_ctl);
+
+	qpnp_adc_tm_disable(chip);
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+	if (rc) {
+		pr_err("adc-tm read status1 failed\n");
+		return IRQ_HANDLED;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS_LOW,
+					&chip->th_info.status_low, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read status low failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+				&chip->th_info.adc_tm_low_thr_set, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read low thr failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+				&chip->th_info.qpnp_adc_tm_meas_en, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read status high failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	chip->th_info.adc_tm_low_enable = chip->th_info.qpnp_adc_tm_meas_en &
+					chip->th_info.status_low;
+	chip->th_info.adc_tm_low_enable &= chip->th_info.adc_tm_low_thr_set;
+
+	sensor_notify_num = chip->th_info.adc_tm_low_enable;
+	while (i < chip->max_channels_available) {
+		if ((sensor_notify_num & 0x1) == 1)
+			sensor_num = i;
+		sensor_notify_num >>= 1;
+		i++;
+	}
+
+	if (!chip->sensor[sensor_num].thermal_node) {
+		sensor_mask = 1 << sensor_num;
+		rc = qpnp_adc_tm_reg_update(chip,
+			QPNP_ADC_TM_LOW_THR_INT_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("low threshold int read failed\n");
+			return IRQ_HANDLED;
+		}
+	} else {
+		/* Uses the thermal sysfs registered device to disable
+		 * the corresponding low voltage threshold which
+		 * is triggered by high temp
+		 */
+		pr_debug("thermal node with mask:%x\n", sensor_mask);
+		rc = qpnp_adc_tm_activate_trip_type(
+			chip->sensor[sensor_num].tz_dev,
+			ADC_TM_TRIP_HIGH_WARM,
+			THERMAL_TRIP_ACTIVATION_DISABLED);
+		if (rc < 0) {
+			pr_err("notify error:%d\n", sensor_num);
+			return IRQ_HANDLED;
+		}
+	}
+
+	atomic_inc(&chip->wq_cnt);
+	queue_work(chip->low_thr_wq, &chip->trigger_low_thr_work);
+
+	return IRQ_HANDLED;
+}
+
+static int qpnp_adc_tm_rc_check_sensor_trip(struct qpnp_adc_tm_chip *chip,
+			u8 status_low, u8 status_high, int i,
+			int *sensor_low_notify_num, int *sensor_high_notify_num)
+{
+	int rc = 0;
+	u8 ctl = 0, sensor_mask = 0;
+
+	if (((status_low & 0x1) == 1) || ((status_high & 0x1) == 1)) {
+		rc = qpnp_adc_tm_read_reg(chip,
+					QPNP_BTM_Mn_EN(i), &ctl, 1);
+		if (rc) {
+			pr_err("ctl read failed with %d\n", rc);
+			return IRQ_HANDLED;
+		}
+
+		if ((status_low & 0x1) && (ctl & QPNP_BTM_Mn_MEAS_EN)
+			&& (ctl & QPNP_BTM_Mn_LOW_THR_INT_EN)) {
+			/* Mask the corresponding low threshold interrupt en */
+			if (!chip->sensor[i].thermal_node) {
+				rc = qpnp_adc_tm_reg_update(chip,
+					QPNP_BTM_Mn_EN(i),
+					QPNP_BTM_Mn_LOW_THR_INT_EN, false);
+				if (rc < 0) {
+					pr_err("low thr_int en failed\n");
+					return IRQ_HANDLED;
+				}
+			} else {
+			/*
+			 * Uses the thermal sysfs registered device to disable
+			 * the corresponding low voltage threshold which
+			 * is triggered by high temp
+			 */
+			pr_debug("thermal node with mask:%x\n", sensor_mask);
+				rc = qpnp_adc_tm_activate_trip_type(
+					chip->sensor[i].tz_dev,
+					ADC_TM_TRIP_HIGH_WARM,
+					THERMAL_TRIP_ACTIVATION_DISABLED);
+				if (rc < 0) {
+					pr_err("notify error:%d\n", i);
+					return IRQ_HANDLED;
+				}
+			}
+			*sensor_low_notify_num |= (status_low & 0x1);
+			chip->sensor[i].low_thr_triggered = true;
+		}
+
+		if ((status_high & 0x1) && (ctl & QPNP_BTM_Mn_MEAS_EN) &&
+					(ctl & QPNP_BTM_Mn_HIGH_THR_INT_EN)) {
+			/* Mask the corresponding high threshold interrupt en */
+			if (!chip->sensor[i].thermal_node) {
+				rc = qpnp_adc_tm_reg_update(chip,
+					QPNP_BTM_Mn_EN(i),
+					QPNP_BTM_Mn_HIGH_THR_INT_EN, false);
+				if (rc < 0) {
+					pr_err("high thr_int en failed\n");
+					return IRQ_HANDLED;
+				}
+			} else {
+			/*
+			 * Uses the thermal sysfs registered device to disable
+			 * the corresponding high voltage threshold which
+			 * is triggered by low temp
+			 */
+				pr_debug("thermal node with mask:%x\n", i);
+				rc = qpnp_adc_tm_activate_trip_type(
+					chip->sensor[i].tz_dev,
+					ADC_TM_TRIP_LOW_COOL,
+					THERMAL_TRIP_ACTIVATION_DISABLED);
+				if (rc < 0) {
+					pr_err("notify error:%d\n", i);
+					return IRQ_HANDLED;
+				}
+			}
+			*sensor_high_notify_num |= (status_high & 0x1);
+			chip->sensor[i].high_thr_triggered = true;
+		}
+	}
+
+	return rc;
+}
+
+static irqreturn_t qpnp_adc_tm_rc_thr_isr(int irq, void *data)
+{
+	struct qpnp_adc_tm_chip *chip = data;
+	u8 status_low = 0, status_high = 0;
+	int rc = 0, sensor_low_notify_num = 0, i = 0;
+	int sensor_high_notify_num = 0;
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS_LOW,
+						&status_low, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read status low failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	if (status_low)
+		chip->th_info.adc_tm_low_enable = status_low;
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS_HIGH,
+							&status_high, 1);
+	if (rc) {
+		pr_err("adc-tm-tm read status high failed with %d\n", rc);
+		return IRQ_HANDLED;
+	}
+
+	if (status_high)
+		chip->th_info.adc_tm_high_enable = status_high;
+
+	while (i < chip->max_channels_available) {
+		rc = qpnp_adc_tm_rc_check_sensor_trip(chip,
+				status_low, status_high, i,
+				&sensor_low_notify_num,
+				&sensor_high_notify_num);
+		if (rc) {
+			pr_err("Sensor trip read failed\n");
+			return IRQ_HANDLED;
+		}
+		status_low >>= 1;
+		status_high >>= 1;
+		i++;
+	}
+
+	if (sensor_low_notify_num) {
+		atomic_inc(&chip->wq_cnt);
+		queue_work(chip->low_thr_wq, &chip->trigger_low_thr_work);
+	}
+
+	if (sensor_high_notify_num) {
+		atomic_inc(&chip->wq_cnt);
+		queue_work(chip->high_thr_wq, &chip->trigger_high_thr_work);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int qpnp_adc_read_temp(struct thermal_zone_device *thermal,
+			     int *temp)
+{
+	struct qpnp_adc_tm_sensor *adc_tm_sensor = thermal->devdata;
+	struct qpnp_adc_tm_chip *chip = adc_tm_sensor->chip;
+	struct qpnp_vadc_result result;
+	int rc = 0;
+
+	rc = qpnp_vadc_read(chip->vadc_dev,
+				adc_tm_sensor->vadc_channel_num, &result);
+	if (rc)
+		return rc;
+
+	*temp = result.physical;
+
+	return rc;
+}
+
+static struct thermal_zone_device_ops qpnp_adc_tm_thermal_ops = {
+	.get_temp = qpnp_adc_read_temp,
+	.get_mode = qpnp_adc_tm_get_mode,
+	.set_mode = qpnp_adc_tm_set_mode,
+	.get_trip_type = qpnp_adc_tm_get_trip_type,
+	.activate_trip_type = qpnp_adc_tm_activate_trip_type,
+	.get_trip_temp = qpnp_adc_tm_get_trip_temp,
+	.set_trip_temp = qpnp_adc_tm_set_trip_temp,
+};
+
+int32_t qpnp_adc_tm_channel_measure(struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param)
+{
+	uint32_t channel, amux_prescaling, dt_index = 0, scale_type = 0;
+	int rc = 0, i = 0, version = 0;
+	bool chan_found = false;
+
+	if (qpnp_adc_tm_is_valid(chip)) {
+		pr_err("chip not valid\n");
+		return -ENODEV;
+	}
+
+	if (param->threshold_notification == NULL) {
+		pr_debug("No notification for high/low temp??\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&chip->adc->adc_lock);
+
+	channel = param->channel;
+
+	if (channel == VSYS) {
+		version = qpnp_adc_get_revid_version(chip->dev);
+		if (version == QPNP_REV_ID_PM8950_1_0) {
+			pr_debug("Channel not supported\n");
+			rc = -EINVAL;
+			goto fail_unlock;
+		}
+	}
+
+	while (i < chip->max_channels_available) {
+		if (chip->adc->adc_channels[i].channel_num ==
+							channel) {
+			dt_index = i;
+			chan_found = true;
+			i++;
+		} else
+			i++;
+	}
+
+	if (!chan_found)  {
+		pr_err("not a valid ADC_TM channel\n");
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	rc = qpnp_adc_tm_check_revision(chip,
+			chip->sensor[dt_index].btm_channel_num);
+	if (rc < 0)
+		goto fail_unlock;
+
+	scale_type = chip->adc->adc_channels[dt_index].adc_scale_fn;
+	if (scale_type >= SCALE_RSCALE_NONE) {
+		rc = -EBADF;
+		goto fail_unlock;
+	}
+
+
+	amux_prescaling =
+		chip->adc->adc_channels[dt_index].chan_path_prescaling;
+
+	if (amux_prescaling >= PATH_SCALING_NONE) {
+		rc = -EINVAL;
+		goto fail_unlock;
+	}
+
+	pr_debug("channel:%d, scale_type:%d, dt_idx:%d",
+					channel, scale_type, dt_index);
+	param->gain_num = qpnp_vadc_amux_scaling_ratio[amux_prescaling].num;
+	param->gain_den = qpnp_vadc_amux_scaling_ratio[amux_prescaling].den;
+	param->adc_tm_hc = chip->adc_tm_hc;
+	chip->adc->amux_prop->amux_channel = channel;
+	chip->adc->amux_prop->decimation =
+			chip->adc->adc_channels[dt_index].adc_decimation;
+	chip->adc->amux_prop->hw_settle_time =
+			chip->adc->adc_channels[dt_index].hw_settle_time;
+	chip->adc->amux_prop->fast_avg_setup =
+			chip->adc->adc_channels[dt_index].fast_avg_setup;
+	chip->adc->amux_prop->mode_sel =
+		ADC_OP_MEASUREMENT_INTERVAL << QPNP_OP_MODE_SHIFT;
+	adc_tm_rscale_fn[scale_type].chan(chip->vadc_dev, param,
+			&chip->adc->amux_prop->chan_prop->low_thr,
+			&chip->adc->amux_prop->chan_prop->high_thr);
+	qpnp_adc_tm_add_to_list(chip, dt_index, param,
+				chip->adc->amux_prop->chan_prop);
+	chip->adc->amux_prop->chan_prop->tm_channel_select =
+				chip->sensor[dt_index].btm_channel_num;
+	chip->adc->amux_prop->chan_prop->state_request =
+					param->state_request;
+	chip->adc->amux_prop->calib_type =
+			chip->adc->adc_channels[dt_index].calib_type;
+	if (!chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_configure(chip, chip->adc->amux_prop);
+		if (rc) {
+			pr_err("adc-tm configure failed with %d\n", rc);
+			goto fail_unlock;
+		}
+	} else {
+		rc = qpnp_adc_tm_hc_configure(chip, chip->adc->amux_prop);
+		if (rc) {
+			pr_err("adc-tm hc configure failed with %d\n", rc);
+			goto fail_unlock;
+		}
+	}
+
+	chip->sensor[dt_index].scale_type = scale_type;
+
+fail_unlock:
+	mutex_unlock(&chip->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_channel_measure);
+
+int32_t qpnp_adc_tm_disable_chan_meas(struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param)
+{
+	uint32_t channel, dt_index = 0, btm_chan_num;
+	u8 sensor_mask = 0, mode_ctl = 0;
+	int rc = 0;
+
+	if (qpnp_adc_tm_is_valid(chip))
+		return -ENODEV;
+
+	mutex_lock(&chip->adc->adc_lock);
+
+	if (!chip->adc_tm_hc) {
+		/* Set measurement in single measurement mode */
+		mode_ctl = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+		rc = qpnp_adc_tm_mode_select(chip, mode_ctl);
+		if (rc < 0) {
+			pr_err("adc-tm single mode select failed\n");
+			goto fail;
+		}
+	}
+
+	/* Disable bank */
+	rc = qpnp_adc_tm_disable(chip);
+	if (rc < 0) {
+		pr_err("adc-tm disable failed\n");
+		goto fail;
+	}
+
+	if (!chip->adc_tm_hc) {
+		/* Check if a conversion is in progress */
+		rc = qpnp_adc_tm_req_sts_check(chip);
+		if (rc < 0) {
+			pr_err("adc-tm req_sts check failed\n");
+			goto fail;
+		}
+	}
+
+	channel = param->channel;
+	while ((chip->adc->adc_channels[dt_index].channel_num
+		!= channel) && (dt_index < chip->max_channels_available))
+		dt_index++;
+
+	if (dt_index >= chip->max_channels_available) {
+		pr_err("not a valid ADC_TMN channel\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	btm_chan_num = chip->sensor[dt_index].btm_channel_num;
+
+	if (!chip->adc_tm_hc) {
+		sensor_mask = 1 << chip->sensor[dt_index].sensor_num;
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("low threshold int write failed\n");
+			goto fail;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("high threshold int enable failed\n");
+			goto fail;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+			sensor_mask, false);
+		if (rc < 0) {
+			pr_err("multi measurement en failed\n");
+			goto fail;
+		}
+	} else {
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
+					QPNP_BTM_Mn_HIGH_THR_INT_EN, false);
+		if (rc < 0) {
+			pr_err("high thr disable err:%d\n", btm_chan_num);
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
+					QPNP_BTM_Mn_LOW_THR_INT_EN, false);
+		if (rc < 0) {
+			pr_err("low thr disable err:%d\n", btm_chan_num);
+			return rc;
+		}
+
+		rc = qpnp_adc_tm_reg_update(chip, QPNP_BTM_Mn_EN(btm_chan_num),
+					QPNP_BTM_Mn_MEAS_EN, false);
+		if (rc < 0) {
+			pr_err("multi measurement disable failed\n");
+			return rc;
+		}
+	}
+
+	rc = qpnp_adc_tm_enable_if_channel_meas(chip);
+	if (rc < 0)
+		pr_err("re-enabling measurement failed\n");
+
+fail:
+	mutex_unlock(&chip->adc->adc_lock);
+
+	return rc;
+}
+EXPORT_SYMBOL(qpnp_adc_tm_disable_chan_meas);
+
+int32_t qpnp_adc_tm_usbid_configure(struct qpnp_adc_tm_chip *chip,
+				struct qpnp_adc_tm_btm_param *param)
+{
+	param->channel = LR_MUX10_PU2_AMUX_USB_ID_LV;
+	return qpnp_adc_tm_channel_measure(chip, param);
+}
+EXPORT_SYMBOL(qpnp_adc_tm_usbid_configure);
+
+int32_t qpnp_adc_tm_usbid_end(struct qpnp_adc_tm_chip *chip)
+{
+	struct qpnp_adc_tm_btm_param param;
+
+	return qpnp_adc_tm_disable_chan_meas(chip, &param);
+}
+EXPORT_SYMBOL(qpnp_adc_tm_usbid_end);
+
+struct qpnp_adc_tm_chip *qpnp_get_adc_tm(struct device *dev, const char *name)
+{
+	struct qpnp_adc_tm_chip *chip;
+	struct device_node *node = NULL;
+	char prop_name[QPNP_MAX_PROP_NAME_LEN];
+
+	snprintf(prop_name, QPNP_MAX_PROP_NAME_LEN, "qcom,%s-adc_tm", name);
+
+	node = of_parse_phandle(dev->of_node, prop_name, 0);
+	if (node == NULL)
+		return ERR_PTR(-ENODEV);
+
+	list_for_each_entry(chip, &qpnp_adc_tm_device_list, list)
+		if (chip->adc->pdev->dev.of_node == node)
+			return chip;
+
+	return ERR_PTR(-EPROBE_DEFER);
+}
+EXPORT_SYMBOL(qpnp_get_adc_tm);
+
+static int qpnp_adc_tm_initial_setup(struct qpnp_adc_tm_chip *chip)
+{
+	u8 thr_init = 0;
+	int rc = 0;
+
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_HIGH_THR_INT_EN,
+							thr_init, 1);
+	if (rc < 0) {
+		pr_err("high thr init failed\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_LOW_THR_INT_EN,
+							thr_init, 1);
+	if (rc < 0) {
+		pr_err("low thr init failed\n");
+		return rc;
+	}
+
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN,
+							thr_init, 1);
+	if (rc < 0) {
+		pr_err("multi meas en failed\n");
+		return rc;
+	}
+
+	return rc;
+}
+
+static const struct of_device_id qpnp_adc_tm_match_table[] = {
+	{	.compatible = "qcom,qpnp-adc-tm" },
+	{	.compatible = "qcom,qpnp-adc-tm-hc" },
+	{}
+};
+
+static int qpnp_adc_tm_probe(struct platform_device *pdev)
+{
+	struct device_node *node = pdev->dev.of_node, *child;
+	struct qpnp_adc_tm_chip *chip;
+	struct qpnp_adc_drv *adc_qpnp;
+	int32_t count_adc_channel_list = 0, rc, sen_idx = 0, i = 0;
+	bool thermal_node = false;
+	const struct of_device_id *id;
+
+	for_each_child_of_node(node, child)
+		count_adc_channel_list++;
+
+	if (!count_adc_channel_list) {
+		pr_err("No channel listing\n");
+		return -EINVAL;
+	}
+
+	id = of_match_node(qpnp_adc_tm_match_table, node);
+	if (id == NULL) {
+		pr_err("qpnp_adc_tm_match of_node prop not present\n");
+		return -ENODEV;
+	}
+
+	chip = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_adc_tm_chip) +
+			(count_adc_channel_list *
+			sizeof(struct qpnp_adc_tm_sensor)),
+				GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	adc_qpnp = devm_kzalloc(&pdev->dev, sizeof(struct qpnp_adc_drv),
+			GFP_KERNEL);
+	if (!adc_qpnp) {
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	chip->dev = &(pdev->dev);
+	chip->adc = adc_qpnp;
+	chip->adc->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!chip->adc->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		rc = -EINVAL;
+		goto fail;
+	}
+
+	if (of_device_is_compatible(node, "qcom,qpnp-adc-tm-hc")) {
+		chip->adc_tm_hc = true;
+		chip->adc->adc_hc = true;
+	}
+
+	rc = qpnp_adc_get_devicetree_data(pdev, chip->adc);
+	if (rc) {
+		dev_err(&pdev->dev, "failed to read device tree\n");
+		goto fail;
+	}
+	mutex_init(&chip->adc->adc_lock);
+
+	/* Register the ADC peripheral interrupt */
+	if (!chip->adc_tm_hc) {
+		chip->adc->adc_high_thr_irq = platform_get_irq_byname(pdev,
+						"high-thr-en-set");
+		if (chip->adc->adc_high_thr_irq < 0) {
+			pr_err("Invalid irq\n");
+			rc = -ENXIO;
+			goto fail;
+		}
+
+		chip->adc->adc_low_thr_irq = platform_get_irq_byname(pdev,
+						"low-thr-en-set");
+		if (chip->adc->adc_low_thr_irq < 0) {
+			pr_err("Invalid irq\n");
+			rc = -ENXIO;
+			goto fail;
+		}
+	}
+
+	chip->vadc_dev = qpnp_get_vadc(&pdev->dev, "adc_tm");
+	if (IS_ERR(chip->vadc_dev)) {
+		rc = PTR_ERR(chip->vadc_dev);
+		if (rc != -EPROBE_DEFER)
+			pr_err("vadc property missing, rc=%d\n", rc);
+		goto fail;
+	}
+
+	chip->adc_tm_recalib_check = of_property_read_bool(node,
+				"qcom,adc-tm-recalib-check");
+
+	for_each_child_of_node(node, child) {
+		char name[25];
+		int btm_channel_num, timer_select = 0;
+
+		rc = of_property_read_u32(child,
+				"qcom,btm-channel-number", &btm_channel_num);
+		if (rc) {
+			pr_err("Invalid btm channel number\n");
+			goto fail;
+		}
+		rc = of_property_read_u32(child,
+				"qcom,meas-interval-timer-idx", &timer_select);
+		if (rc) {
+			pr_debug("Default to timer2 with interval of 1 sec\n");
+			chip->sensor[sen_idx].timer_select =
+							ADC_MEAS_TIMER_SELECT2;
+			chip->sensor[sen_idx].meas_interval =
+							ADC_MEAS2_INTERVAL_1S;
+		} else {
+			if (timer_select >= ADC_MEAS_TIMER_NUM) {
+				pr_err("Invalid timer selection number\n");
+				goto fail;
+			}
+			chip->sensor[sen_idx].timer_select = timer_select;
+			if (timer_select == ADC_MEAS_TIMER_SELECT1)
+				chip->sensor[sen_idx].meas_interval =
+						ADC_MEAS1_INTERVAL_3P9MS;
+			else if (timer_select == ADC_MEAS_TIMER_SELECT3)
+				chip->sensor[sen_idx].meas_interval =
+						ADC_MEAS3_INTERVAL_4S;
+			else if (timer_select == ADC_MEAS_TIMER_SELECT2)
+				chip->sensor[sen_idx].meas_interval =
+						ADC_MEAS2_INTERVAL_1S;
+		}
+
+		chip->sensor[sen_idx].btm_channel_num = btm_channel_num;
+		chip->sensor[sen_idx].vadc_channel_num =
+				chip->adc->adc_channels[sen_idx].channel_num;
+		chip->sensor[sen_idx].sensor_num = sen_idx;
+		chip->sensor[sen_idx].chip = chip;
+		pr_debug("btm_chan:%x, vadc_chan:%x\n", btm_channel_num,
+			chip->adc->adc_channels[sen_idx].channel_num);
+		thermal_node = of_property_read_bool(child,
+					"qcom,thermal-node");
+		if (thermal_node) {
+			/* Register with the thermal zone */
+			pr_debug("thermal node%x\n", btm_channel_num);
+			chip->sensor[sen_idx].mode = THERMAL_DEVICE_DISABLED;
+			chip->sensor[sen_idx].thermal_node = true;
+			snprintf(name, sizeof(name), "%s",
+				chip->adc->adc_channels[sen_idx].name);
+			chip->sensor[sen_idx].meas_interval =
+				QPNP_ADC_TM_MEAS_INTERVAL;
+			chip->sensor[sen_idx].low_thr =
+						QPNP_ADC_TM_M0_LOW_THR;
+			chip->sensor[sen_idx].high_thr =
+						QPNP_ADC_TM_M0_HIGH_THR;
+			chip->sensor[sen_idx].tz_dev =
+				thermal_zone_device_register(name,
+				ADC_TM_TRIP_NUM, ADC_TM_WRITABLE_TRIPS_MASK,
+				&chip->sensor[sen_idx],
+				&qpnp_adc_tm_thermal_ops, NULL, 0, 0);
+			if (IS_ERR(chip->sensor[sen_idx].tz_dev))
+				pr_err("thermal device register failed.\n");
+		}
+		chip->sensor[sen_idx].req_wq = alloc_workqueue(
+				"qpnp_adc_notify_wq", WQ_HIGHPRI, 0);
+		if (!chip->sensor[sen_idx].req_wq) {
+			pr_err("Requesting priority wq failed\n");
+			goto fail;
+		}
+		INIT_WORK(&chip->sensor[sen_idx].work, notify_adc_tm_fn);
+		INIT_LIST_HEAD(&chip->sensor[sen_idx].thr_list);
+		sen_idx++;
+	}
+	chip->max_channels_available = count_adc_channel_list;
+	chip->high_thr_wq = alloc_workqueue("qpnp_adc_tm_high_thr_wq",
+							WQ_HIGHPRI, 0);
+	if (!chip->high_thr_wq) {
+		pr_err("Requesting high thr priority wq failed\n");
+		goto fail;
+	}
+	chip->low_thr_wq = alloc_workqueue("qpnp_adc_tm_low_thr_wq",
+							WQ_HIGHPRI, 0);
+	if (!chip->low_thr_wq) {
+		pr_err("Requesting low thr priority wq failed\n");
+		goto fail;
+	}
+	chip->thr_wq = alloc_workqueue("qpnp_adc_tm_thr_wq",
+						WQ_HIGHPRI, 0);
+	if (!chip->thr_wq) {
+		pr_err("Requesting thr priority wq failed\n");
+		goto fail;
+	}
+
+	INIT_WORK(&chip->trigger_high_thr_work, qpnp_adc_tm_high_thr_work);
+	INIT_WORK(&chip->trigger_low_thr_work, qpnp_adc_tm_low_thr_work);
+	atomic_set(&chip->wq_cnt, 0);
+
+	if (!chip->adc_tm_hc) {
+		rc = qpnp_adc_tm_initial_setup(chip);
+		if (rc)
+			goto fail;
+
+		rc = devm_request_irq(&pdev->dev, chip->adc->adc_high_thr_irq,
+				qpnp_adc_tm_high_thr_isr,
+		IRQF_TRIGGER_RISING, "qpnp_adc_tm_high_interrupt", chip);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to request adc irq\n");
+			goto fail;
+		} else {
+			enable_irq_wake(chip->adc->adc_high_thr_irq);
+		}
+
+		rc = devm_request_irq(&pdev->dev, chip->adc->adc_low_thr_irq,
+					qpnp_adc_tm_low_thr_isr,
+			IRQF_TRIGGER_RISING, "qpnp_adc_tm_low_interrupt", chip);
+		if (rc) {
+			dev_err(&pdev->dev, "failed to request adc irq\n");
+			goto fail;
+		} else {
+			enable_irq_wake(chip->adc->adc_low_thr_irq);
+		}
+	} else {
+		rc = devm_request_irq(&pdev->dev, chip->adc->adc_irq_eoc,
+				qpnp_adc_tm_rc_thr_isr,
+			IRQF_TRIGGER_RISING, "qpnp_adc_tm_interrupt", chip);
+		if (rc)
+			dev_err(&pdev->dev, "failed to request adc irq\n");
+		else
+			enable_irq_wake(chip->adc->adc_irq_eoc);
+	}
+
+	chip->adc_vote_enable = false;
+	dev_set_drvdata(&pdev->dev, chip);
+	list_add(&chip->list, &qpnp_adc_tm_device_list);
+
+	pr_debug("OK\n");
+	return 0;
+fail:
+	for_each_child_of_node(node, child) {
+		thermal_node = of_property_read_bool(child,
+					"qcom,thermal-node");
+		if (thermal_node) {
+			thermal_zone_device_unregister(chip->sensor[i].tz_dev);
+			if (chip->sensor[i].req_wq)
+				destroy_workqueue(chip->sensor[sen_idx].req_wq);
+		}
+	}
+	if (chip->high_thr_wq)
+		destroy_workqueue(chip->high_thr_wq);
+	if (chip->low_thr_wq)
+		destroy_workqueue(chip->low_thr_wq);
+	dev_set_drvdata(&pdev->dev, NULL);
+	return rc;
+}
+
+static int qpnp_adc_tm_remove(struct platform_device *pdev)
+{
+	struct qpnp_adc_tm_chip *chip = dev_get_drvdata(&pdev->dev);
+	struct device_node *node = pdev->dev.of_node, *child;
+	bool thermal_node = false;
+	int i = 0;
+
+	for_each_child_of_node(node, child) {
+		thermal_node = of_property_read_bool(child,
+					"qcom,thermal-node");
+		if (thermal_node) {
+			thermal_zone_device_unregister(chip->sensor[i].tz_dev);
+			if (chip->sensor[i].req_wq)
+				destroy_workqueue(chip->sensor[i].req_wq);
+		}
+		i++;
+	}
+
+	if (chip->high_thr_wq)
+		destroy_workqueue(chip->high_thr_wq);
+	if (chip->low_thr_wq)
+		destroy_workqueue(chip->low_thr_wq);
+	if (chip->adc->hkadc_ldo && chip->adc->hkadc_ldo_ok)
+		qpnp_adc_free_voltage_resource(chip->adc);
+	dev_set_drvdata(&pdev->dev, NULL);
+
+	return 0;
+}
+
+static void qpnp_adc_tm_shutdown(struct platform_device *pdev)
+{
+	struct qpnp_adc_tm_chip *chip = dev_get_drvdata(&pdev->dev);
+	int rc = 0;
+	u8 reg_val = 0, status1 = 0, en_ctl1 = 0;
+
+	/* Set measurement in single measurement mode */
+	reg_val = ADC_OP_NORMAL_MODE << QPNP_OP_MODE_SHIFT;
+	rc = qpnp_adc_tm_mode_select(chip, reg_val);
+	if (rc < 0)
+		pr_err("adc-tm single mode select failed\n");
+
+	/* Disable bank */
+	rc = qpnp_adc_tm_disable(chip);
+	if (rc < 0)
+		pr_err("adc-tm disable failed\n");
+
+	/* Check if a conversion is in progress */
+	rc = qpnp_adc_tm_req_sts_check(chip);
+	if (rc < 0)
+		pr_err("adc-tm req_sts check failed\n");
+
+	/* Disable multimeasurement */
+	reg_val = 0;
+	rc = qpnp_adc_tm_write_reg(chip, QPNP_ADC_TM_MULTI_MEAS_EN, reg_val, 1);
+	if (rc < 0)
+		pr_err("adc-tm multi-measurement mode disable failed\n");
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_ADC_TM_STATUS1, &status1, 1);
+	if (rc < 0)
+		pr_err("adc-tm status1 read failed\n");
+
+	rc = qpnp_adc_tm_read_reg(chip, QPNP_EN_CTL1, &en_ctl1, 1);
+	if (rc < 0)
+		pr_err("adc-tm en_ctl1 read failed\n");
+
+	pr_debug("adc-tm status1=0%x, en_ctl1=0x%x\n", status1, en_ctl1);
+}
+
+static int qpnp_adc_tm_suspend_noirq(struct device *dev)
+{
+	struct qpnp_adc_tm_chip *chip = dev_get_drvdata(dev);
+
+	if (atomic_read(&chip->wq_cnt) != 0) {
+		pr_err(
+			"Aborting suspend, adc_tm notification running while suspending\n");
+		return -EBUSY;
+	}
+	return 0;
+}
+
+static const struct dev_pm_ops qpnp_adc_tm_pm_ops = {
+	.suspend_noirq	= qpnp_adc_tm_suspend_noirq,
+};
+
+static struct platform_driver qpnp_adc_tm_driver = {
+	.driver		= {
+		.name		= "qcom,qpnp-adc-tm",
+		.of_match_table	= qpnp_adc_tm_match_table,
+		.pm		= &qpnp_adc_tm_pm_ops,
+	},
+	.probe		= qpnp_adc_tm_probe,
+	.remove		= qpnp_adc_tm_remove,
+	.shutdown	= qpnp_adc_tm_shutdown,
+};
+
+static int __init qpnp_adc_tm_init(void)
+{
+	return platform_driver_register(&qpnp_adc_tm_driver);
+}
+module_init(qpnp_adc_tm_init);
+
+static void __exit qpnp_adc_tm_exit(void)
+{
+	platform_driver_unregister(&qpnp_adc_tm_driver);
+}
+module_exit(qpnp_adc_tm_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC ADC Threshold Monitoring driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/thermal/qpnp-temp-alarm.c b/drivers/thermal/qpnp-temp-alarm.c
new file mode 100644
index 0000000..e86a297
--- /dev/null
+++ b/drivers/thermal/qpnp-temp-alarm.c
@@ -0,0 +1,813 @@
+/*
+ * Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/thermal.h>
+#include <linux/qpnp/qpnp-adc.h>
+
+#define QPNP_TM_DRIVER_NAME "qcom,qpnp-temp-alarm"
+
+enum qpnp_tm_registers {
+	QPNP_TM_REG_TYPE		= 0x04,
+	QPNP_TM_REG_SUBTYPE		= 0x05,
+	QPNP_TM_REG_STATUS		= 0x08,
+	QPNP_TM_REG_SHUTDOWN_CTRL1	= 0x40,
+	QPNP_TM_REG_SHUTDOWN_CTRL2	= 0x42,
+	QPNP_TM_REG_ALARM_CTRL		= 0x46,
+};
+
+#define QPNP_TM_TYPE			0x09
+#define QPNP_TM_SUBTYPE_GEN1		0x08
+#define QPNP_TM_SUBTYPE_GEN2		0x09
+
+#define STATUS_STATE_MASK		0x70
+#define STATUS_STATE_SHIFT		4
+#define STATUS_STAGE_MASK		0x03
+
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE3	0x80
+#define SHUTDOWN_CTRL1_OVERRIDE_STAGE2	0x40
+#define SHUTDOWN_CTRL1_CLK_RATE_MASK	0x0C
+#define SHUTDOWN_CTRL1_CLK_RATE_SHIFT	2
+#define SHUTDOWN_CTRL1_THRESHOLD_MASK	0x03
+
+#define SHUTDOWN_CTRL2_CLEAR_STAGE3	0x80
+#define SHUTDOWN_CTRL2_CLEAR_STAGE2	0x40
+
+#define ALARM_CTRL_FORCE_ENABLE		0x80
+#define ALARM_CTRL_FOLLOW_HW_ENABLE	0x01
+
+#define TEMP_STAGE_STEP			20000	/* Stage step: 20.000 C */
+#define TEMP_STAGE_HYSTERESIS		2000
+
+#define TEMP_THRESH_MIN			105000	/* Threshold Min: 105 C */
+#define TEMP_THRESH_STEP		5000	/* Threshold step: 5 C */
+
+#define THRESH_MIN			0
+#define THRESH_MAX			3
+
+#define CLOCK_RATE_MIN			0
+#define CLOCK_RATE_MAX			3
+
+/* Trip points from most critical to least critical */
+#define TRIP_STAGE3			0
+#define TRIP_STAGE2			1
+#define TRIP_STAGE1			2
+#define TRIP_NUM			3
+
+enum qpnp_tm_adc_type {
+	QPNP_TM_ADC_NONE,	/* Estimates temp based on overload level. */
+	QPNP_TM_ADC_QPNP_ADC,
+};
+
+/*
+ * Temperature in millicelcius reported during stage 0 if no ADC is present and
+ * no value has been specified via device tree.
+ */
+#define DEFAULT_NO_ADC_TEMP		37000
+
+struct qpnp_tm_chip {
+	struct delayed_work		irq_work;
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	struct thermal_zone_device	*tz_dev;
+	const char			*tm_name;
+	unsigned int			subtype;
+	enum qpnp_tm_adc_type		adc_type;
+	int				temperature;
+	enum thermal_device_mode	mode;
+	unsigned int			thresh;
+	unsigned int			clock_rate;
+	unsigned int			stage;
+	unsigned int			prev_stage;
+	int				irq;
+	enum qpnp_vadc_channels		adc_channel;
+	u16				base_addr;
+	bool				allow_software_override;
+	struct qpnp_vadc_chip		*vadc_dev;
+};
+
+/* Delay between TEMP_STAT IRQ going high and status value changing in ms. */
+#define STATUS_REGISTER_DELAY_MS       40
+
+enum pmic_thermal_override_mode {
+	SOFTWARE_OVERRIDE_DISABLED = 0,
+	SOFTWARE_OVERRIDE_ENABLED,
+};
+
+/* This array maps from GEN2 alarm state to GEN1 alarm stage */
+const unsigned int alarm_state_map[8] = {0, 1, 1, 2, 2, 3, 3, 3};
+
+static inline int qpnp_tm_read(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+				int len)
+{
+	int rc;
+
+	rc = regmap_bulk_read(chip->regmap, chip->base_addr + addr, buf, len);
+
+	if (rc)
+		dev_err(&chip->pdev->dev,
+			"%s: regmap_bulk_readl failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+			__func__,
+			to_spmi_device(chip->pdev->dev.parent)->usid,
+			chip->base_addr + addr,
+			len, rc);
+
+	return rc;
+}
+
+static inline int qpnp_tm_write(struct qpnp_tm_chip *chip, u16 addr, u8 *buf,
+				int len)
+{
+	int rc;
+
+	rc = regmap_bulk_write(chip->regmap, chip->base_addr + addr, buf, len);
+
+	if (rc)
+		dev_err(&chip->pdev->dev,
+			"%s: regmap_bulk_write failed. sid=%d, addr=%04X, len=%d, rc=%d\n",
+			__func__,
+			to_spmi_device(chip->pdev->dev.parent)->usid,
+			chip->base_addr + addr,
+			len, rc);
+
+	return rc;
+}
+
+
+static inline int qpnp_tm_shutdown_override(struct qpnp_tm_chip *chip,
+			    enum pmic_thermal_override_mode mode)
+{
+	int rc = 0;
+	u8 reg;
+
+	if (chip->allow_software_override) {
+		reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+		reg |= (chip->clock_rate << SHUTDOWN_CTRL1_CLK_RATE_SHIFT)
+			& SHUTDOWN_CTRL1_CLK_RATE_MASK;
+
+		if (mode == SOFTWARE_OVERRIDE_ENABLED)
+			reg |= SHUTDOWN_CTRL1_OVERRIDE_STAGE2
+				| SHUTDOWN_CTRL1_OVERRIDE_STAGE3;
+
+		rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
+	}
+
+	return rc;
+}
+
+static int qpnp_tm_update_temp(struct qpnp_tm_chip *chip)
+{
+	struct qpnp_vadc_result adc_result;
+	int rc;
+
+	rc = qpnp_vadc_read(chip->vadc_dev, chip->adc_channel, &adc_result);
+	if (!rc)
+		chip->temperature = adc_result.physical;
+	else
+		dev_err(&chip->pdev->dev,
+			"%s: qpnp_vadc_read(%d) failed, rc=%d\n",
+			__func__, chip->adc_channel, rc);
+
+	return rc;
+}
+
+static int qpnp_tm_get_temp_stage(struct qpnp_tm_chip *chip,
+			unsigned int *stage)
+{
+	int rc;
+	u8 reg;
+
+	rc = qpnp_tm_read(chip, QPNP_TM_REG_STATUS, &reg, 1);
+	if (rc < 0)
+		return rc;
+
+	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
+		*stage = reg & STATUS_STAGE_MASK;
+	else
+		*stage = (reg & STATUS_STATE_MASK) >> STATUS_STATE_SHIFT;
+
+	return 0;
+}
+
+/*
+ * This function initializes the internal temperature value based on only the
+ * current thermal stage and threshold.
+ */
+static int qpnp_tm_init_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+	unsigned int stage;
+	int rc;
+
+	rc = qpnp_tm_get_temp_stage(chip, &chip->stage);
+	if (rc < 0)
+		return rc;
+
+	stage = chip->subtype == QPNP_TM_SUBTYPE_GEN1
+		? chip->stage : alarm_state_map[chip->stage];
+
+	if (stage)
+		chip->temperature = chip->thresh * TEMP_THRESH_STEP +
+			   (stage - 1) * TEMP_STAGE_STEP +
+			   TEMP_THRESH_MIN;
+
+	return 0;
+}
+
+/*
+ * This function updates the internal temperature value based on the
+ * current thermal stage and threshold as well as the previous stage
+ */
+static int qpnp_tm_update_temp_no_adc(struct qpnp_tm_chip *chip)
+{
+	unsigned int stage, stage_new, stage_old;
+	int rc;
+
+	rc = qpnp_tm_get_temp_stage(chip, &stage);
+	if (rc < 0)
+		return rc;
+
+	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
+		stage_new = stage;
+		stage_old = chip->stage;
+	} else {
+		stage_new = alarm_state_map[stage];
+		stage_old = alarm_state_map[chip->stage];
+	}
+
+	if (stage_new > stage_old) {
+		/* increasing stage, use lower bound */
+		chip->temperature = (stage_new - 1) * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				+ TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	} else if (stage_new < stage_old) {
+		/* decreasing stage, use upper bound */
+		chip->temperature = stage_new * TEMP_STAGE_STEP
+				+ chip->thresh * TEMP_THRESH_STEP
+				- TEMP_STAGE_HYSTERESIS + TEMP_THRESH_MIN;
+	}
+
+	chip->stage = stage;
+
+	return 0;
+}
+
+static int qpnp_tz_get_temp_no_adc(struct thermal_zone_device *thermal,
+				     int *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int rc;
+
+	if (!temperature)
+		return -EINVAL;
+
+	rc = qpnp_tm_update_temp_no_adc(chip);
+	if (rc < 0)
+		return rc;
+
+	*temperature = chip->temperature;
+
+	return 0;
+}
+
+static int qpnp_tz_get_temp_qpnp_adc(struct thermal_zone_device *thermal,
+				      int *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int rc;
+
+	if (!temperature)
+		return -EINVAL;
+
+	rc = qpnp_tm_update_temp(chip);
+	if (rc < 0) {
+		dev_err(&chip->pdev->dev,
+			"%s: %s: adc read failed, rc = %d\n",
+			__func__, chip->tm_name, rc);
+		return rc;
+	}
+
+	*temperature = chip->temperature;
+
+	return 0;
+}
+
+static int qpnp_tz_get_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode *mode)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+
+	if (!mode)
+		return -EINVAL;
+
+	*mode = chip->mode;
+
+	return 0;
+}
+
+static int qpnp_tz_set_mode(struct thermal_zone_device *thermal,
+			      enum thermal_device_mode mode)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int rc = 0;
+
+	if (mode != chip->mode) {
+		if (mode == THERMAL_DEVICE_ENABLED)
+			rc = qpnp_tm_shutdown_override(chip,
+				SOFTWARE_OVERRIDE_ENABLED);
+		else
+			rc = qpnp_tm_shutdown_override(chip,
+				SOFTWARE_OVERRIDE_DISABLED);
+
+		chip->mode = mode;
+	}
+
+	return rc;
+}
+
+static int qpnp_tz_get_trip_type(struct thermal_zone_device *thermal,
+				   int trip, enum thermal_trip_type *type)
+{
+	if (trip < 0 || !type)
+		return -EINVAL;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		*type = THERMAL_TRIP_CRITICAL;
+		break;
+	case TRIP_STAGE2:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	case TRIP_STAGE1:
+		*type = THERMAL_TRIP_HOT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int qpnp_tz_get_trip_temp(struct thermal_zone_device *thermal,
+				   int trip, int *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+	int thresh_temperature;
+
+	if (trip < 0 || !temperature)
+		return -EINVAL;
+
+	thresh_temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN;
+
+	switch (trip) {
+	case TRIP_STAGE3:
+		thresh_temperature += 2 * TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE2:
+		thresh_temperature += TEMP_STAGE_STEP;
+		break;
+	case TRIP_STAGE1:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*temperature = thresh_temperature;
+
+	return 0;
+}
+
+static int qpnp_tz_get_crit_temp(struct thermal_zone_device *thermal,
+				   int *temperature)
+{
+	struct qpnp_tm_chip *chip = thermal->devdata;
+
+	if (!temperature)
+		return -EINVAL;
+
+	*temperature = chip->thresh * TEMP_THRESH_STEP + TEMP_THRESH_MIN +
+		2 * TEMP_STAGE_STEP;
+
+	return 0;
+}
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_no_adc = {
+	.get_temp = qpnp_tz_get_temp_no_adc,
+	.get_mode = qpnp_tz_get_mode,
+	.set_mode = qpnp_tz_set_mode,
+	.get_trip_type = qpnp_tz_get_trip_type,
+	.get_trip_temp = qpnp_tz_get_trip_temp,
+	.get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static struct thermal_zone_device_ops qpnp_thermal_zone_ops_qpnp_adc = {
+	.get_temp = qpnp_tz_get_temp_qpnp_adc,
+	.get_mode = qpnp_tz_get_mode,
+	.set_mode = qpnp_tz_set_mode,
+	.get_trip_type = qpnp_tz_get_trip_type,
+	.get_trip_temp = qpnp_tz_get_trip_temp,
+	.get_crit_temp = qpnp_tz_get_crit_temp,
+};
+
+static void qpnp_tm_work(struct work_struct *work)
+{
+	struct delayed_work *dwork
+		= container_of(work, struct delayed_work, work);
+	struct qpnp_tm_chip *chip
+		= container_of(dwork, struct qpnp_tm_chip, irq_work);
+	unsigned int stage_new, stage_old;
+	int rc;
+
+	if (chip->adc_type == QPNP_TM_ADC_NONE) {
+		rc = qpnp_tm_update_temp_no_adc(chip);
+		if (rc < 0)
+			goto bail;
+	} else {
+		rc = qpnp_tm_get_temp_stage(chip, &chip->stage);
+		if (rc < 0)
+			goto bail;
+
+		rc = qpnp_tm_update_temp(chip);
+		if (rc < 0)
+			goto bail;
+	}
+
+	if (chip->subtype == QPNP_TM_SUBTYPE_GEN1) {
+		stage_new = chip->stage;
+		stage_old = chip->prev_stage;
+	} else {
+		stage_new = alarm_state_map[chip->stage];
+		stage_old = alarm_state_map[chip->prev_stage];
+	}
+
+	chip->prev_stage = chip->stage;
+
+	if (stage_new != stage_old) {
+		if (chip->subtype == QPNP_TM_SUBTYPE_GEN1)
+			pr_crit("%s: PMIC Temp Alarm - stage=%u, threshold=%u, temperature=%d mC\n",
+				chip->tm_name, chip->stage, chip->thresh,
+				chip->temperature);
+		else
+			pr_crit("%s: PMIC Temp Alarm - stage=%u, state=%u, threshold=%u, temperature=%d mC\n",
+				chip->tm_name, stage_new, chip->stage,
+				chip->thresh, chip->temperature);
+
+		thermal_zone_device_update(chip->tz_dev,
+						THERMAL_EVENT_UNSPECIFIED);
+
+		/* Notify user space */
+		sysfs_notify(&chip->tz_dev->device.kobj, NULL, "type");
+	}
+
+bail:
+	return;
+}
+
+static irqreturn_t qpnp_tm_isr(int irq, void *data)
+{
+	struct qpnp_tm_chip *chip = data;
+
+	schedule_delayed_work(&chip->irq_work,
+			msecs_to_jiffies(STATUS_REGISTER_DELAY_MS) + 1);
+
+	return IRQ_HANDLED;
+}
+
+static int qpnp_tm_init_reg(struct qpnp_tm_chip *chip)
+{
+	int rc = 0;
+	u8 reg;
+
+	rc = qpnp_tm_read(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
+	if (rc < 0)
+		return rc;
+
+	if (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX) {
+		/* Use hardware threshold value if configuration is invalid. */
+		chip->thresh = reg & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+	}
+
+	if (chip->clock_rate < CLOCK_RATE_MIN
+	    || chip->clock_rate > CLOCK_RATE_MAX) {
+		/* Use hardware clock rate value if configuration is invalid. */
+		chip->clock_rate = (reg & SHUTDOWN_CTRL1_CLK_RATE_MASK)
+					>> SHUTDOWN_CTRL1_CLK_RATE_SHIFT;
+	}
+
+	/*
+	 * Set threshold and clock rate and also disable software override of
+	 * stage 2 and 3 shutdowns.
+	 */
+	reg = chip->thresh & SHUTDOWN_CTRL1_THRESHOLD_MASK;
+	reg |= (chip->clock_rate << SHUTDOWN_CTRL1_CLK_RATE_SHIFT)
+		& SHUTDOWN_CTRL1_CLK_RATE_MASK;
+	rc = qpnp_tm_write(chip, QPNP_TM_REG_SHUTDOWN_CTRL1, &reg, 1);
+	if (rc < 0)
+		return rc;
+
+	/* Enable the thermal alarm PMIC module in always-on mode. */
+	reg = ALARM_CTRL_FORCE_ENABLE;
+	rc = qpnp_tm_write(chip, QPNP_TM_REG_ALARM_CTRL, &reg, 1);
+
+	return rc;
+}
+
+static int qpnp_tm_probe(struct platform_device *pdev)
+{
+	struct device_node *node;
+	unsigned int base;
+	struct qpnp_tm_chip *chip;
+	struct thermal_zone_device_ops *tz_ops;
+	char *tm_name;
+	u32 default_temperature;
+	int rc = 0;
+	u8 raw_type[2], type, subtype;
+
+	if (!pdev || !(&pdev->dev) || !pdev->dev.of_node) {
+		dev_err(&pdev->dev, "%s: device tree node not found\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	node = pdev->dev.of_node;
+
+	chip = kzalloc(sizeof(struct qpnp_tm_chip), GFP_KERNEL);
+	if (!chip)
+		return -ENOMEM;
+
+	chip->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!chip->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(&pdev->dev, chip);
+
+	rc = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (rc < 0) {
+		dev_err(&pdev->dev,
+			"Couldn't find reg in node = %s rc = %d\n",
+			pdev->dev.of_node->full_name, rc);
+		goto free_chip;
+	}
+	chip->base_addr	= base;
+	chip->pdev	= pdev;
+
+	chip->irq = platform_get_irq(pdev, 0);
+	if (chip->irq < 0) {
+		rc = chip->irq;
+		dev_err(&pdev->dev, "%s: node is missing irq, rc=%d\n",
+			__func__, rc);
+		goto free_chip;
+	}
+
+	chip->tm_name = of_get_property(node, "label", NULL);
+	if (chip->tm_name == NULL) {
+		dev_err(&pdev->dev, "%s: node is missing label\n", __func__);
+		rc = -EINVAL;
+		goto free_chip;
+	}
+
+	tm_name = kstrdup(chip->tm_name, GFP_KERNEL);
+	if (tm_name == NULL) {
+		rc = -ENOMEM;
+		goto free_chip;
+	}
+	chip->tm_name = tm_name;
+
+	INIT_DELAYED_WORK(&chip->irq_work, qpnp_tm_work);
+
+	/* These bindings are optional, so it is okay if they are not found. */
+	chip->thresh = THRESH_MAX + 1;
+	rc = of_property_read_u32(node, "qcom,threshold-set", &chip->thresh);
+	if (!rc && (chip->thresh < THRESH_MIN || chip->thresh > THRESH_MAX))
+		dev_err(&pdev->dev,
+			"%s: invalid qcom,threshold-set=%u specified\n",
+			__func__, chip->thresh);
+
+	chip->clock_rate = CLOCK_RATE_MAX + 1;
+	rc = of_property_read_u32(node, "qcom,clock-rate", &chip->clock_rate);
+	if (!rc && (chip->clock_rate < CLOCK_RATE_MIN
+		    || chip->clock_rate > CLOCK_RATE_MAX))
+		dev_err(&pdev->dev,
+			"%s: invalid qcom,clock-rate=%u specified\n", __func__,
+			chip->clock_rate);
+
+	chip->adc_type = QPNP_TM_ADC_NONE;
+	rc = of_property_read_u32(node, "qcom,channel-num", &chip->adc_channel);
+	if (!rc) {
+		if (chip->adc_channel < 0 || chip->adc_channel >= ADC_MAX_NUM) {
+			dev_err(&pdev->dev,
+				"%s: invalid qcom,channel-num=%d specified\n",
+				__func__, chip->adc_channel);
+		} else {
+			chip->adc_type = QPNP_TM_ADC_QPNP_ADC;
+			chip->vadc_dev = qpnp_get_vadc(&pdev->dev,
+							"temp_alarm");
+			if (IS_ERR(chip->vadc_dev)) {
+				rc = PTR_ERR(chip->vadc_dev);
+				if (rc != -EPROBE_DEFER)
+					pr_err("vadc property missing\n");
+				goto err_cancel_work;
+			}
+		}
+	}
+
+	if (chip->adc_type == QPNP_TM_ADC_QPNP_ADC)
+		tz_ops = &qpnp_thermal_zone_ops_qpnp_adc;
+	else
+		tz_ops = &qpnp_thermal_zone_ops_no_adc;
+
+	chip->allow_software_override
+		= of_property_read_bool(node, "qcom,allow-override");
+
+	default_temperature = DEFAULT_NO_ADC_TEMP;
+	rc = of_property_read_u32(node, "qcom,default-temp",
+					&default_temperature);
+	chip->temperature = default_temperature;
+
+	rc = qpnp_tm_read(chip, QPNP_TM_REG_TYPE, raw_type, 2);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: could not read type register, rc=%d\n",
+			__func__, rc);
+		goto err_cancel_work;
+	}
+	type = raw_type[0];
+	subtype = raw_type[1];
+
+	if (type != QPNP_TM_TYPE || (subtype != QPNP_TM_SUBTYPE_GEN1
+				     && subtype != QPNP_TM_SUBTYPE_GEN2)) {
+		dev_err(&pdev->dev,
+			"%s: invalid type=%02X or subtype=%02X register value\n",
+			__func__, type, subtype);
+		rc = -ENODEV;
+		goto err_cancel_work;
+	}
+
+	chip->subtype = subtype;
+
+	rc = qpnp_tm_init_reg(chip);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qpnp_tm_init_reg() failed, rc=%d\n",
+			__func__, rc);
+		goto err_cancel_work;
+	}
+
+	if (chip->adc_type == QPNP_TM_ADC_NONE) {
+		rc = qpnp_tm_init_temp_no_adc(chip);
+		if (rc) {
+			dev_err(&pdev->dev,
+				"%s: qpnp_tm_init_temp_no_adc() failed, rc=%d\n",
+				__func__, rc);
+			goto err_cancel_work;
+		}
+	}
+
+	/* Start in HW control; switch to SW control when user changes mode. */
+	chip->mode = THERMAL_DEVICE_DISABLED;
+	rc = qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: qpnp_tm_shutdown_override() failed, rc=%d\n",
+			__func__, rc);
+		goto err_cancel_work;
+	}
+
+	chip->tz_dev = thermal_zone_device_register(tm_name, TRIP_NUM, 0, chip,
+			tz_ops, NULL, 0, 0);
+	if (chip->tz_dev == NULL) {
+		dev_err(&pdev->dev,
+			"%s: thermal_zone_device_register() failed.\n",
+			__func__);
+		rc = -ENODEV;
+		goto err_cancel_work;
+	}
+
+	rc = request_irq(chip->irq, qpnp_tm_isr, IRQF_TRIGGER_RISING, tm_name,
+			chip);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "%s: request_irq(%d) failed: %d\n",
+			__func__, chip->irq, rc);
+		goto err_free_tz;
+	}
+
+	return 0;
+
+err_free_tz:
+	thermal_zone_device_unregister(chip->tz_dev);
+err_cancel_work:
+	cancel_delayed_work_sync(&chip->irq_work);
+	kfree(chip->tm_name);
+free_chip:
+	dev_set_drvdata(&pdev->dev, NULL);
+	kfree(chip);
+	return rc;
+}
+
+static int qpnp_tm_remove(struct platform_device *pdev)
+{
+	struct qpnp_tm_chip *chip = dev_get_drvdata(&pdev->dev);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+	thermal_zone_device_unregister(chip->tz_dev);
+	kfree(chip->tm_name);
+	qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+	free_irq(chip->irq, chip);
+	cancel_delayed_work_sync(&chip->irq_work);
+	kfree(chip);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int qpnp_tm_suspend(struct device *dev)
+{
+	struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+	/* Clear override bits in suspend to allow hardware control */
+	qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_DISABLED);
+
+	return 0;
+}
+
+static int qpnp_tm_resume(struct device *dev)
+{
+	struct qpnp_tm_chip *chip = dev_get_drvdata(dev);
+
+	/* Override hardware actions so software can control */
+	if (chip->mode == THERMAL_DEVICE_ENABLED)
+		qpnp_tm_shutdown_override(chip, SOFTWARE_OVERRIDE_ENABLED);
+
+	return 0;
+}
+
+static const struct dev_pm_ops qpnp_tm_pm_ops = {
+	.suspend = qpnp_tm_suspend,
+	.resume = qpnp_tm_resume,
+};
+
+#define QPNP_TM_PM_OPS	(&qpnp_tm_pm_ops)
+#else
+#define QPNP_TM_PM_OPS	NULL
+#endif
+
+static const struct of_device_id qpnp_tm_match_table[] = {
+	{ .compatible = QPNP_TM_DRIVER_NAME, },
+	{}
+};
+
+static const struct platform_device_id qpnp_tm_id[] = {
+	{ QPNP_TM_DRIVER_NAME, 0 },
+	{}
+};
+
+static struct platform_driver qpnp_tm_driver = {
+	.driver = {
+		.name		= QPNP_TM_DRIVER_NAME,
+		.of_match_table	= qpnp_tm_match_table,
+		.owner		= THIS_MODULE,
+		.pm		= QPNP_TM_PM_OPS,
+	},
+	.probe	  = qpnp_tm_probe,
+	.remove	  = qpnp_tm_remove,
+	.id_table = qpnp_tm_id,
+};
+
+int __init qpnp_tm_init(void)
+{
+	return platform_driver_register(&qpnp_tm_driver);
+}
+
+static void __exit qpnp_tm_exit(void)
+{
+	platform_driver_unregister(&qpnp_tm_driver);
+}
+
+module_init(qpnp_tm_init);
+module_exit(qpnp_tm_exit);
+
+MODULE_DESCRIPTION("QPNP PMIC Temperature Alarm driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 644e978..001c807 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -105,6 +105,8 @@
 
 source "drivers/usb/isp1760/Kconfig"
 
+source "drivers/usb/pd/Kconfig"
+
 comment "USB port drivers"
 
 if USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index dca7856..7a11362 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -61,3 +61,5 @@
 obj-$(CONFIG_USB_COMMON)	+= common/
 
 obj-$(CONFIG_USBIP_CORE)	+= usbip/
+
+obj-$(CONFIG_USB_PD)            += pd/
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index 8745af1d..1b152a3 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -1258,6 +1258,8 @@ static int dwc3_probe(struct platform_device *pdev)
 				    &dwc->hsphy_interface);
 	device_property_read_u32(dev, "snps,quirk-frame-length-adjustment",
 				 &dwc->fladj);
+	dwc->disable_clk_gating = device_property_read_bool(dev,
+				"snps,disable-clk-gating");
 
 	if (dwc->enable_bus_suspend) {
 		pm_runtime_set_autosuspend_delay(dev, 500);
diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c
index 1dd3f88..2e2b647 100644
--- a/drivers/usb/dwc3/dwc3-msm.c
+++ b/drivers/usb/dwc3/dwc3-msm.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -157,6 +157,7 @@ struct dwc3_msm {
 	struct clk		*xo_clk;
 	struct clk		*core_clk;
 	long			core_clk_rate;
+	long			core_clk_rate_hs;
 	struct clk		*iface_clk;
 	struct clk		*sleep_clk;
 	struct clk		*utmi_clk;
@@ -195,6 +196,7 @@ struct dwc3_msm {
 	struct power_supply	*usb_psy;
 	struct work_struct	vbus_draw_work;
 	bool			in_host_mode;
+	enum usb_device_speed	max_rh_port_speed;
 	unsigned int		tx_fifo_size;
 	bool			vbus_active;
 	bool			suspend;
@@ -213,6 +215,8 @@ struct dwc3_msm {
 	struct notifier_block	vbus_nb;
 	struct notifier_block	id_nb;
 
+	struct notifier_block	host_nb;
+
 	int			pwr_event_irq;
 	atomic_t                in_p3;
 	unsigned int		lpm_to_suspend_delay;
@@ -338,6 +342,23 @@ static inline void dwc3_msm_write_readback(void *base, u32 offset,
 			__func__, val, offset);
 }
 
+static bool dwc3_msm_is_ss_rhport_connected(struct dwc3_msm *mdwc)
+{
+	int i, num_ports;
+	u32 reg;
+
+	reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1);
+	num_ports = HCS_MAX_PORTS(reg);
+
+	for (i = 0; i < num_ports; i++) {
+		reg = dwc3_msm_read_reg(mdwc->base, USB3_PORTSC + i*0x10);
+		if ((reg & PORT_CONNECT) && DEV_SUPERSPEED(reg))
+			return true;
+	}
+
+	return false;
+}
+
 static bool dwc3_msm_is_host_superspeed(struct dwc3_msm *mdwc)
 {
 	int i, num_ports;
@@ -2101,6 +2122,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc)
 static int dwc3_msm_resume(struct dwc3_msm *mdwc)
 {
 	int ret;
+	long core_clk_rate;
 	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
 
 	dev_dbg(mdwc->dev, "%s: exiting lpm\n", __func__);
@@ -2148,7 +2170,15 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc)
 	clk_prepare_enable(mdwc->iface_clk);
 	if (mdwc->noc_aggr_clk)
 		clk_prepare_enable(mdwc->noc_aggr_clk);
-	clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate);
+
+	core_clk_rate = mdwc->core_clk_rate;
+	if (mdwc->in_host_mode && mdwc->max_rh_port_speed == USB_SPEED_HIGH) {
+		core_clk_rate = mdwc->core_clk_rate_hs;
+		dev_dbg(mdwc->dev, "%s: set hs core clk rate %ld\n", __func__,
+			core_clk_rate);
+	}
+
+	clk_set_rate(mdwc->core_clk, core_clk_rate);
 	clk_prepare_enable(mdwc->core_clk);
 
 	/* set Memory core: ON, Memory periphery: ON */
@@ -2474,6 +2504,12 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc)
 									ret);
 	}
 
+	if (of_property_read_u32(mdwc->dev->of_node, "qcom,core-clk-rate-hs",
+				(u32 *)&mdwc->core_clk_rate_hs)) {
+		dev_dbg(mdwc->dev, "USB core-clk-rate-hs is not present\n");
+		mdwc->core_clk_rate_hs = mdwc->core_clk_rate;
+	}
+
 	mdwc->sleep_clk = devm_clk_get(mdwc->dev, "sleep_clk");
 	if (IS_ERR(mdwc->sleep_clk)) {
 		dev_err(mdwc->dev, "failed to get sleep_clk\n");
@@ -3095,6 +3131,69 @@ static int dwc3_msm_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static int dwc3_msm_host_notifier(struct notifier_block *nb,
+	unsigned long event, void *ptr)
+{
+	struct dwc3_msm *mdwc = container_of(nb, struct dwc3_msm, host_nb);
+	struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3);
+	struct usb_device *udev = ptr;
+	union power_supply_propval pval;
+	unsigned int max_power;
+
+	if (event != USB_DEVICE_ADD && event != USB_DEVICE_REMOVE)
+		return NOTIFY_DONE;
+
+	if (!mdwc->usb_psy) {
+		mdwc->usb_psy = power_supply_get_by_name("usb");
+		if (!mdwc->usb_psy)
+			return NOTIFY_DONE;
+	}
+
+	/*
+	 * For direct-attach devices, new udev is direct child of root hub
+	 * i.e. dwc -> xhci -> root_hub -> udev
+	 * root_hub's udev->parent==NULL, so traverse struct device hierarchy
+	 */
+	if (udev->parent && !udev->parent->parent &&
+			udev->dev.parent->parent == &dwc->xhci->dev) {
+		if (event == USB_DEVICE_ADD && udev->actconfig) {
+			if (!dwc3_msm_is_ss_rhport_connected(mdwc)) {
+				/*
+				 * Core clock rate can be reduced only if root
+				 * hub SS port is not enabled/connected.
+				 */
+				clk_set_rate(mdwc->core_clk,
+				mdwc->core_clk_rate_hs);
+				dev_dbg(mdwc->dev,
+					"set hs core clk rate %ld\n",
+					mdwc->core_clk_rate_hs);
+				mdwc->max_rh_port_speed = USB_SPEED_HIGH;
+			} else {
+				mdwc->max_rh_port_speed = USB_SPEED_SUPER;
+			}
+
+			if (udev->speed >= USB_SPEED_SUPER)
+				max_power = udev->actconfig->desc.bMaxPower * 8;
+			else
+				max_power = udev->actconfig->desc.bMaxPower * 2;
+			dev_dbg(mdwc->dev, "%s configured bMaxPower:%d (mA)\n",
+					dev_name(&udev->dev), max_power);
+
+			/* inform PMIC of max power so it can optimize boost */
+			pval.intval = max_power * 1000;
+			power_supply_set_property(mdwc->usb_psy,
+					POWER_SUPPLY_PROP_BOOST_CURRENT, &pval);
+		} else {
+			pval.intval = 0;
+			power_supply_set_property(mdwc->usb_psy,
+					POWER_SUPPLY_PROP_BOOST_CURRENT, &pval);
+			mdwc->max_rh_port_speed = USB_SPEED_UNKNOWN;
+		}
+	}
+
+	return NOTIFY_DONE;
+}
+
 #define VBUS_REG_CHECK_DELAY	(msecs_to_jiffies(1000))
 
 /**
@@ -3152,6 +3251,9 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
 
 		dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST);
 
+		mdwc->host_nb.notifier_call = dwc3_msm_host_notifier;
+		usb_register_notify(&mdwc->host_nb);
+
 		/*
 		 * FIXME If micro A cable is disconnected during system suspend,
 		 * xhci platform device will be removed before runtime pm is
@@ -3170,6 +3272,7 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
 			mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
 			mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
 			pm_runtime_put_sync(mdwc->dev);
+			usb_unregister_notify(&mdwc->host_nb);
 			return ret;
 		}
 
@@ -3202,6 +3305,7 @@ static int dwc3_otg_start_host(struct dwc3_msm *mdwc, int on)
 		mdwc->hs_phy->flags &= ~PHY_HOST_MODE;
 		mdwc->ss_phy->flags &= ~PHY_HOST_MODE;
 		platform_device_del(dwc->xhci);
+		usb_unregister_notify(&mdwc->host_nb);
 
 		/*
 		 * Perform USB hardware RESET (both core reset and DBM reset)
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 987dcef..a30c8e3 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -227,6 +227,12 @@
 config USB_F_CDEV
 	tristate
 
+config USB_F_CCID
+	tristate
+
+config USB_F_GSI
+	tristate
+
 # this first set of drivers all depend on bulk-capable hardware.
 
 config USB_CONFIGFS
@@ -528,6 +534,21 @@
 	help
 	  Generic USB serial character function driver to support DUN/NMEA.
 
+config USB_CONFIGFS_F_CCID
+	bool "USB CCID function"
+	select USB_F_CCID
+	depends on USB_CONFIGFS
+	help
+	  USB CCID function driver creates transport layer between the
+	  userspace CCID component and the Windows Host.
+
+config USB_CONFIGFS_F_GSI
+	bool "USB GSI function"
+	select USB_F_GSI
+	depends on USB_CONFIGFS
+	help
+	  Generic function driver to support h/w acceleration to IPA over GSI.
+
 choice
 	tristate "USB Gadget Drivers"
 	default USB_ETH
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 1d774e6..e78080c 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -58,3 +58,7 @@
 obj-$(CONFIG_USB_F_DIAG)	+= usb_f_diag.o
 usb_f_cdev-y			:= f_cdev.o
 obj-$(CONFIG_USB_F_CDEV)	+= usb_f_cdev.o
+usb_f_ccid-y			:= f_ccid.o
+obj-$(CONFIG_USB_F_CCID)   	+= usb_f_ccid.o
+usb_f_gsi-y			:= f_gsi.o rndis.o
+obj-$(CONFIG_USB_F_GSI)         += usb_f_gsi.o
diff --git a/drivers/usb/gadget/function/f_accessory.c b/drivers/usb/gadget/function/f_accessory.c
index 2ca16a57..cfb57ac 100644
--- a/drivers/usb/gadget/function/f_accessory.c
+++ b/drivers/usb/gadget/function/f_accessory.c
@@ -136,12 +136,47 @@ static struct usb_interface_descriptor acc_interface_desc = {
 	.bInterfaceProtocol     = 0,
 };
 
+static struct usb_endpoint_descriptor acc_superspeed_in_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_IN,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acc_superspeed_in_comp_desc = {
+	.bLength =		sizeof(acc_superspeed_in_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor acc_superspeed_out_desc = {
+	.bLength                = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType        = USB_DT_ENDPOINT,
+	.bEndpointAddress       = USB_DIR_OUT,
+	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize         = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acc_superspeed_out_comp_desc = {
+	.bLength =		sizeof(acc_superspeed_out_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+};
+
+
 static struct usb_endpoint_descriptor acc_highspeed_in_desc = {
 	.bLength                = USB_DT_ENDPOINT_SIZE,
 	.bDescriptorType        = USB_DT_ENDPOINT,
 	.bEndpointAddress       = USB_DIR_IN,
 	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize         = __constant_cpu_to_le16(512),
+	.wMaxPacketSize         = cpu_to_le16(512),
 };
 
 static struct usb_endpoint_descriptor acc_highspeed_out_desc = {
@@ -149,7 +184,7 @@ static struct usb_endpoint_descriptor acc_highspeed_out_desc = {
 	.bDescriptorType        = USB_DT_ENDPOINT,
 	.bEndpointAddress       = USB_DIR_OUT,
 	.bmAttributes           = USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize         = __constant_cpu_to_le16(512),
+	.wMaxPacketSize         = cpu_to_le16(512),
 };
 
 static struct usb_endpoint_descriptor acc_fullspeed_in_desc = {
@@ -180,6 +215,15 @@ static struct usb_descriptor_header *hs_acc_descs[] = {
 	NULL,
 };
 
+static struct usb_descriptor_header *ss_acc_descs[] = {
+	(struct usb_descriptor_header *) &acc_interface_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_in_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_in_comp_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_out_desc,
+	(struct usb_descriptor_header *) &acc_superspeed_out_comp_desc,
+	NULL,
+};
+
 static struct usb_string acc_string_defs[] = {
 	[INTERFACE_STRING_INDEX].s	= "Android Accessory Interface",
 	{  },	/* end of list */
@@ -772,6 +816,9 @@ static const struct file_operations acc_fops = {
 	.read = acc_read,
 	.write = acc_write,
 	.unlocked_ioctl = acc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = acc_ioctl,
+#endif
 	.open = acc_open,
 	.release = acc_release,
 };
@@ -957,6 +1004,14 @@ __acc_function_bind(struct usb_configuration *c,
 			acc_fullspeed_out_desc.bEndpointAddress;
 	}
 
+	/* support super speed hardware */
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		acc_superspeed_in_desc.bEndpointAddress =
+			acc_fullspeed_in_desc.bEndpointAddress;
+		acc_superspeed_out_desc.bEndpointAddress =
+			acc_fullspeed_out_desc.bEndpointAddress;
+	}
+
 	DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
 			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
 			f->name, dev->ep_in->name, dev->ep_out->name);
@@ -1313,6 +1368,7 @@ static struct usb_function *acc_alloc(struct usb_function_instance *fi)
 	dev->function.strings = acc_strings,
 	dev->function.fs_descriptors = fs_acc_descs;
 	dev->function.hs_descriptors = hs_acc_descs;
+	dev->function.ss_descriptors = ss_acc_descs;
 	dev->function.bind = acc_function_bind_configfs;
 	dev->function.unbind = acc_function_unbind;
 	dev->function.set_alt = acc_function_set_alt;
diff --git a/drivers/usb/gadget/function/f_ccid.c b/drivers/usb/gadget/function/f_ccid.c
new file mode 100644
index 0000000..1801a6c
--- /dev/null
+++ b/drivers/usb/gadget/function/f_ccid.c
@@ -0,0 +1,1105 @@
+/*
+ * f_ccid.c -- CCID function Driver
+ *
+ * Copyright (c) 2011, 2013, 2017 The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details
+ */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/usb/ccid_desc.h>
+#include <linux/usb/composite.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+
+#include "f_ccid.h"
+
+#define BULK_IN_BUFFER_SIZE sizeof(struct ccid_bulk_in_header)
+#define BULK_OUT_BUFFER_SIZE sizeof(struct ccid_bulk_out_header)
+#define CTRL_BUF_SIZE	4
+#define FUNCTION_NAME	"ccid"
+#define MAX_INST_NAME_LEN	40
+#define CCID_CTRL_DEV_NAME	"ccid_ctrl"
+#define CCID_BULK_DEV_NAME	"ccid_bulk"
+#define CCID_NOTIFY_INTERVAL	5
+#define CCID_NOTIFY_MAXPACKET	4
+
+/* number of tx requests to allocate */
+#define TX_REQ_MAX 4
+
+struct ccid_ctrl_dev {
+	atomic_t opened;
+	struct list_head tx_q;
+	wait_queue_head_t tx_wait_q;
+	unsigned char buf[CTRL_BUF_SIZE];
+	int tx_ctrl_done;
+	struct miscdevice ccid_ctrl_device;
+};
+
+struct ccid_bulk_dev {
+	atomic_t error;
+	atomic_t opened;
+	atomic_t rx_req_busy;
+	wait_queue_head_t read_wq;
+	wait_queue_head_t write_wq;
+	struct usb_request *rx_req;
+	int rx_done;
+	struct list_head tx_idle;
+	struct miscdevice ccid_bulk_device;
+};
+
+struct ccid_opts {
+	struct usb_function_instance func_inst;
+	struct f_ccid *ccid;
+};
+
+struct f_ccid {
+	struct usb_function function;
+	int ifc_id;
+	spinlock_t lock;
+	atomic_t online;
+	/* usb eps*/
+	struct usb_ep *notify;
+	struct usb_ep *in;
+	struct usb_ep *out;
+	struct usb_request *notify_req;
+	struct ccid_ctrl_dev ctrl_dev;
+	struct ccid_bulk_dev bulk_dev;
+	int dtr_state;
+};
+
+static inline struct f_ccid *ctrl_dev_to_ccid(struct ccid_ctrl_dev *d)
+{
+	return container_of(d, struct f_ccid, ctrl_dev);
+}
+
+static inline struct f_ccid *bulk_dev_to_ccid(struct ccid_bulk_dev *d)
+{
+	return container_of(d, struct f_ccid, bulk_dev);
+}
+
+/* Interface Descriptor: */
+static struct usb_interface_descriptor ccid_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_CSCID,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+};
+/* CCID Class Descriptor */
+static struct usb_ccid_class_descriptor ccid_class_desc = {
+	.bLength =		sizeof(ccid_class_desc),
+	.bDescriptorType =	CCID_DECRIPTOR_TYPE,
+	.bcdCCID =		CCID1_10,
+	.bMaxSlotIndex =	0,
+	/* This value indicates what voltages the CCID can supply to slots */
+	.bVoltageSupport =	VOLTS_3_0,
+	.dwProtocols =		PROTOCOL_TO,
+	/* Default ICC clock frequency in KHz */
+	.dwDefaultClock =	3580,
+	/* Maximum supported ICC clock frequency in KHz */
+	.dwMaximumClock =	3580,
+	.bNumClockSupported =	0,
+	/* Default ICC I/O data rate in bps */
+	.dwDataRate =		9600,
+	/* Maximum supported ICC I/O data rate in bps */
+	.dwMaxDataRate =	9600,
+	.bNumDataRatesSupported = 0,
+	.dwMaxIFSD =		0,
+	.dwSynchProtocols =	0,
+	.dwMechanical =		0,
+	/* This value indicates what intelligent features the CCID has */
+	.dwFeatures =		CCID_FEATURES_EXC_SAPDU |
+				CCID_FEATURES_AUTO_PNEGO |
+				CCID_FEATURES_AUTO_BAUD |
+				CCID_FEATURES_AUTO_CLOCK |
+				CCID_FEATURES_AUTO_VOLT |
+				CCID_FEATURES_AUTO_ACTIV |
+				CCID_FEATURES_AUTO_PCONF,
+	/* extended APDU level Message Length */
+	.dwMaxCCIDMessageLength = 0x200,
+	.bClassGetResponse =	0x0,
+	.bClassEnvelope =	0x0,
+	.wLcdLayout =		0,
+	.bPINSupport =		0,
+	.bMaxCCIDBusySlots =	1
+};
+/* Full speed support: */
+static struct usb_endpoint_descriptor ccid_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		1 << CCID_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor ccid_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor ccid_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *ccid_fs_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_fs_notify_desc,
+	(struct usb_descriptor_header *) &ccid_fs_in_desc,
+	(struct usb_descriptor_header *) &ccid_fs_out_desc,
+	NULL,
+};
+
+/* High speed support: */
+static struct usb_endpoint_descriptor ccid_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(CCID_NOTIFY_MAXPACKET),
+	.bInterval =		CCID_NOTIFY_INTERVAL + 4,
+};
+
+static struct usb_endpoint_descriptor ccid_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor ccid_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ccid_hs_descs[] = {
+	(struct usb_descriptor_header *) &ccid_interface_desc,
+	(struct usb_descriptor_header *) &ccid_class_desc,
+	(struct usb_descriptor_header *) &ccid_hs_notify_desc,
+	(struct usb_descriptor_header *) &ccid_hs_in_desc,
+	(struct usb_descriptor_header *) &ccid_hs_out_desc,
+	NULL,
+};
+
+static inline struct f_ccid *func_to_ccid(struct usb_function *f)
+{
+	return container_of(f, struct f_ccid, function);
+}
+
+static void ccid_req_put(struct f_ccid *ccid_dev, struct list_head *head,
+		struct usb_request *req)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	list_add_tail(&req->list, head);
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+}
+
+static struct usb_request *ccid_req_get(struct f_ccid *ccid_dev,
+					struct list_head *head)
+{
+	unsigned long flags;
+	struct usb_request *req = NULL;
+
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	if (!list_empty(head)) {
+		req = list_first_entry(head, struct usb_request, list);
+		list_del(&req->list);
+	}
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	return req;
+}
+
+static void ccid_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	switch (req->status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+	case 0:
+		break;
+	default:
+		pr_err("CCID notify ep error %d\n", req->status);
+	}
+}
+
+static void ccid_bulk_complete_in(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ccid *ccid_dev = req->context;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+
+	if (req->status != 0)
+		atomic_set(&bulk_dev->error, 1);
+
+	ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+	wake_up(&bulk_dev->write_wq);
+}
+
+static void ccid_bulk_complete_out(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_ccid *ccid_dev = req->context;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	if (req->status != 0)
+		atomic_set(&bulk_dev->error, 1);
+
+	bulk_dev->rx_done = 1;
+	wake_up(&bulk_dev->read_wq);
+}
+
+static struct usb_request *
+ccid_request_alloc(struct usb_ep *ep, size_t len, gfp_t kmalloc_flags)
+{
+	struct usb_request *req;
+
+	req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, kmalloc_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			req = NULL;
+		}
+	}
+
+	return req ? req : ERR_PTR(-ENOMEM);
+}
+
+static void ccid_request_free(struct usb_request *req, struct usb_ep *ep)
+{
+	if (req) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+static int
+ccid_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_ccid *ccid_dev = container_of(f, struct f_ccid, function);
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request      *req = cdev->req;
+	int ret = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+
+	if (!atomic_read(&ccid_dev->online))
+		return -ENOTCONN;
+
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_ABORT:
+		if (w_length != 0)
+			goto invalid;
+		ctrl_dev->buf[0] = CCIDGENERICREQ_ABORT;
+		ctrl_dev->buf[1] = w_value & 0xFF;
+		ctrl_dev->buf[2] = (w_value >> 8) & 0xFF;
+		ctrl_dev->buf[3] = 0x00;
+		ctrl_dev->tx_ctrl_done = 1;
+		wake_up(&ctrl_dev->tx_wait_q);
+		ret = 0;
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_GET_CLOCK_FREQUENCIES:
+		*(u32 *) req->buf =
+				cpu_to_le32(ccid_class_desc.dwDefaultClock);
+		ret = min_t(u32, w_length,
+				sizeof(ccid_class_desc.dwDefaultClock));
+		break;
+
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| CCIDGENERICREQ_GET_DATA_RATES:
+		*(u32 *) req->buf = cpu_to_le32(ccid_class_desc.dwDataRate);
+		ret = min_t(u32, w_length, sizeof(ccid_class_desc.dwDataRate));
+		break;
+
+	default:
+invalid:
+	pr_debug("invalid control req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest,
+		w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (ret >= 0) {
+		pr_debug("ccid req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->length = ret;
+		ret = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (ret < 0)
+			pr_err("ccid ep0 enqueue err %d\n", ret);
+	}
+
+	return ret;
+}
+
+static void ccid_function_disable(struct usb_function *f)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	struct usb_request *req;
+
+	/* Disable endpoints */
+	usb_ep_disable(ccid_dev->notify);
+	usb_ep_disable(ccid_dev->in);
+	usb_ep_disable(ccid_dev->out);
+	/* Free endpoint related requests */
+	ccid_request_free(ccid_dev->notify_req, ccid_dev->notify);
+	if (!atomic_read(&bulk_dev->rx_req_busy))
+		ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+	while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)))
+		ccid_request_free(req, ccid_dev->in);
+
+	ccid_dev->dtr_state = 0;
+	atomic_set(&ccid_dev->online, 0);
+	/* Wake up threads */
+	wake_up(&bulk_dev->write_wq);
+	wake_up(&bulk_dev->read_wq);
+	wake_up(&ctrl_dev->tx_wait_q);
+
+}
+
+static int
+ccid_function_set_alt(struct usb_function *f, unsigned int intf,
+		unsigned int alt)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req;
+	int ret = 0;
+	int i;
+
+	ccid_dev->notify_req = ccid_request_alloc(ccid_dev->notify,
+			sizeof(struct usb_ccid_notification), GFP_ATOMIC);
+	if (IS_ERR(ccid_dev->notify_req)) {
+		pr_err("%s: unable to allocate memory for notify req\n",
+				__func__);
+		return PTR_ERR(ccid_dev->notify_req);
+	}
+	ccid_dev->notify_req->complete = ccid_notify_complete;
+	ccid_dev->notify_req->context = ccid_dev;
+
+	/* now allocate requests for our endpoints */
+	req = ccid_request_alloc(ccid_dev->out, BULK_OUT_BUFFER_SIZE,
+							GFP_ATOMIC);
+	if (IS_ERR(req)) {
+		pr_err("%s: unable to allocate memory for out req\n",
+				__func__);
+		ret = PTR_ERR(req);
+		goto free_notify;
+	}
+	req->complete = ccid_bulk_complete_out;
+	req->context = ccid_dev;
+	bulk_dev->rx_req = req;
+
+	for (i = 0; i < TX_REQ_MAX; i++) {
+		req = ccid_request_alloc(ccid_dev->in, BULK_IN_BUFFER_SIZE,
+				GFP_ATOMIC);
+		if (IS_ERR(req)) {
+			pr_err("%s: unable to allocate memory for in req\n",
+					__func__);
+			ret = PTR_ERR(req);
+			goto free_bulk_out;
+		}
+		req->complete = ccid_bulk_complete_in;
+		req->context = ccid_dev;
+		ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+	}
+
+	/* choose the descriptors and enable endpoints */
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->notify);
+	if (ret) {
+		ccid_dev->notify->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->notify->name, ret);
+		goto free_bulk_in;
+	}
+	ret = usb_ep_enable(ccid_dev->notify);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->notify->name, ret);
+		goto free_bulk_in;
+	}
+	ccid_dev->notify->driver_data = ccid_dev;
+
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->in);
+	if (ret) {
+		ccid_dev->in->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->in->name, ret);
+		goto disable_ep_notify;
+	}
+	ret = usb_ep_enable(ccid_dev->in);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->in->name, ret);
+		goto disable_ep_notify;
+	}
+
+	ret = config_ep_by_speed(cdev->gadget, f, ccid_dev->out);
+	if (ret) {
+		ccid_dev->out->desc = NULL;
+		pr_err("%s: config_ep_by_speed failed for ep#%s, err#%d\n",
+				__func__, ccid_dev->out->name, ret);
+		goto disable_ep_in;
+	}
+	ret = usb_ep_enable(ccid_dev->out);
+	if (ret) {
+		pr_err("%s: usb ep#%s enable failed, err#%d\n",
+				__func__, ccid_dev->out->name, ret);
+		goto disable_ep_in;
+	}
+	ccid_dev->dtr_state = 1;
+	atomic_set(&ccid_dev->online, 1);
+	return ret;
+
+disable_ep_in:
+	usb_ep_disable(ccid_dev->in);
+disable_ep_notify:
+	usb_ep_disable(ccid_dev->notify);
+	ccid_dev->notify->driver_data = NULL;
+free_bulk_in:
+	while ((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)))
+		ccid_request_free(req, ccid_dev->in);
+free_bulk_out:
+	ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+free_notify:
+	ccid_request_free(ccid_dev->notify_req, ccid_dev->notify);
+	return ret;
+}
+
+static void ccid_function_unbind(struct usb_configuration *c,
+					struct usb_function *f)
+{
+	if (gadget_is_dualspeed(c->cdev->gadget))
+		usb_free_descriptors(f->hs_descriptors);
+	usb_free_descriptors(f->fs_descriptors);
+
+}
+
+static int ccid_function_bind(struct usb_configuration *c,
+					struct usb_function *f)
+{
+	struct f_ccid *ccid_dev = func_to_ccid(f);
+	struct usb_ep *ep;
+	struct usb_composite_dev *cdev = c->cdev;
+	int ret = -ENODEV;
+
+	ccid_dev->ifc_id = usb_interface_id(c, f);
+	if (ccid_dev->ifc_id < 0) {
+		pr_err("%s: unable to allocate ifc id, err:%d",
+				__func__, ccid_dev->ifc_id);
+		return ccid_dev->ifc_id;
+	}
+	ccid_interface_desc.bInterfaceNumber = ccid_dev->ifc_id;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_notify_desc);
+	if (!ep) {
+		pr_err("%s: usb epnotify autoconfig failed\n", __func__);
+		return -ENODEV;
+	}
+	ccid_dev->notify = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_in_desc);
+	if (!ep) {
+		pr_err("%s: usb epin autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_in_fail;
+	}
+	ccid_dev->in = ep;
+	ep->driver_data = cdev;
+
+	ep = usb_ep_autoconfig(cdev->gadget, &ccid_fs_out_desc);
+	if (!ep) {
+		pr_err("%s: usb epout autoconfig failed\n", __func__);
+		ret = -ENODEV;
+		goto ep_auto_out_fail;
+	}
+	ccid_dev->out = ep;
+	ep->driver_data = cdev;
+
+	f->fs_descriptors = usb_copy_descriptors(ccid_fs_descs);
+	if (!f->fs_descriptors)
+		goto ep_auto_out_fail;
+
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		ccid_hs_in_desc.bEndpointAddress =
+				ccid_fs_in_desc.bEndpointAddress;
+		ccid_hs_out_desc.bEndpointAddress =
+				ccid_fs_out_desc.bEndpointAddress;
+		ccid_hs_notify_desc.bEndpointAddress =
+				ccid_fs_notify_desc.bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(ccid_hs_descs);
+		if (!f->hs_descriptors)
+			goto ep_auto_out_fail;
+	}
+
+	pr_debug("%s: CCID %s Speed, IN:%s OUT:%s\n", __func__,
+			gadget_is_dualspeed(cdev->gadget) ? "dual" : "full",
+			ccid_dev->in->name, ccid_dev->out->name);
+
+	return 0;
+
+ep_auto_out_fail:
+	ccid_dev->out->driver_data = NULL;
+	ccid_dev->out = NULL;
+ep_auto_in_fail:
+	ccid_dev->in->driver_data = NULL;
+	ccid_dev->in = NULL;
+
+	return ret;
+}
+
+static int ccid_bulk_open(struct inode *ip, struct file *fp)
+{
+	struct ccid_bulk_dev *bulk_dev =  container_of(fp->private_data,
+						struct ccid_bulk_dev,
+						ccid_bulk_device);
+	struct f_ccid *ccid_dev = bulk_dev_to_ccid(bulk_dev);
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_open\n");
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (atomic_read(&bulk_dev->opened)) {
+		pr_debug("%s: bulk device is already opened\n", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&bulk_dev->opened, 1);
+	/* clear the error latch */
+	atomic_set(&bulk_dev->error, 0);
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	fp->private_data = ccid_dev;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+	return 0;
+}
+
+static int ccid_bulk_release(struct inode *ip, struct file *fp)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+
+	pr_debug("ccid_bulk_release\n");
+	atomic_set(&bulk_dev->opened, 0);
+	return 0;
+}
+
+static ssize_t ccid_bulk_read(struct file *fp, char __user *buf,
+				size_t count, loff_t *pos)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req;
+	int r = count, xfer;
+	int ret;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_read(%zu)\n", count);
+
+	if (count > BULK_OUT_BUFFER_SIZE) {
+		pr_err("%s: max_buffer_size:%zu given_pkt_size:%zu\n",
+				__func__, BULK_OUT_BUFFER_SIZE, count);
+		return -ENOMEM;
+	}
+
+	if (atomic_read(&bulk_dev->error)) {
+		r = -EIO;
+		pr_err("%s bulk_dev_error\n", __func__);
+		goto done;
+	}
+
+requeue_req:
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	/* queue a request */
+	req = bulk_dev->rx_req;
+	req->length = count;
+	bulk_dev->rx_done = 0;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	ret = usb_ep_queue(ccid_dev->out, req, GFP_KERNEL);
+	if (ret < 0) {
+		r = -EIO;
+		pr_err("%s usb ep queue failed\n", __func__);
+		atomic_set(&bulk_dev->error, 1);
+		goto done;
+	}
+	/* wait for a request to complete */
+	ret = wait_event_interruptible(bulk_dev->read_wq, bulk_dev->rx_done ||
+					atomic_read(&bulk_dev->error) ||
+					!atomic_read(&ccid_dev->online));
+	if (ret < 0) {
+		atomic_set(&bulk_dev->error, 1);
+		r = ret;
+		usb_ep_dequeue(ccid_dev->out, req);
+		goto done;
+	}
+	if (!atomic_read(&bulk_dev->error)) {
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		if (!atomic_read(&ccid_dev->online)) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n", __func__);
+			r = -ENODEV;
+			goto done;
+		}
+		/* If we got a 0-len packet, throw it back and try again. */
+		if (req->actual == 0) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			goto requeue_req;
+		}
+		xfer = (req->actual < count) ? req->actual : count;
+		atomic_set(&bulk_dev->rx_req_busy, 1);
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+		if (copy_to_user(buf, req->buf, xfer))
+			r = -EFAULT;
+
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		atomic_set(&bulk_dev->rx_req_busy, 0);
+		if (!atomic_read(&ccid_dev->online)) {
+			ccid_request_free(bulk_dev->rx_req, ccid_dev->out);
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n", __func__);
+			r = -ENODEV;
+			goto done;
+		} else {
+			r = xfer;
+		}
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+	} else {
+		r = -EIO;
+	}
+done:
+	pr_debug("ccid_bulk_read returning %d\n", r);
+	return r;
+}
+
+static ssize_t ccid_bulk_write(struct file *fp, const char __user *buf,
+				 size_t count, loff_t *pos)
+{
+	struct f_ccid *ccid_dev =  fp->private_data;
+	struct ccid_bulk_dev *bulk_dev = &ccid_dev->bulk_dev;
+	struct usb_request *req = 0;
+	int r = count;
+	int ret;
+	unsigned long flags;
+
+	pr_debug("ccid_bulk_write(%zu)\n", count);
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!count) {
+		pr_err("%s: zero length ctrl pkt\n", __func__);
+		return -ENODEV;
+	}
+	if (count > BULK_IN_BUFFER_SIZE) {
+		pr_err("%s: max_buffer_size:%zu given_pkt_size:%zu\n",
+				__func__, BULK_IN_BUFFER_SIZE, count);
+		return -ENOMEM;
+	}
+
+
+	/* get an idle tx request to use */
+	ret = wait_event_interruptible(bulk_dev->write_wq,
+		((req = ccid_req_get(ccid_dev, &bulk_dev->tx_idle)) ||
+		 atomic_read(&bulk_dev->error)));
+
+	if (ret < 0) {
+		r = ret;
+		goto done;
+	}
+
+	if (!req || atomic_read(&bulk_dev->error)) {
+		pr_err(" %s dev->error\n", __func__);
+		r = -EIO;
+		goto done;
+	}
+	if (copy_from_user(req->buf, buf, count)) {
+		if (!atomic_read(&ccid_dev->online)) {
+			pr_debug("%s: USB cable not connected\n",
+						__func__);
+			ccid_request_free(req, ccid_dev->in);
+			r = -ENODEV;
+		} else {
+			ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+			r = -EFAULT;
+		}
+		goto done;
+	}
+	req->length = count;
+	ret = usb_ep_queue(ccid_dev->in, req, GFP_KERNEL);
+	if (ret < 0) {
+		pr_debug("ccid_bulk_write: xfer error %d\n", ret);
+		atomic_set(&bulk_dev->error, 1);
+		ccid_req_put(ccid_dev, &bulk_dev->tx_idle, req);
+		r = -EIO;
+		spin_lock_irqsave(&ccid_dev->lock, flags);
+		if (!atomic_read(&ccid_dev->online)) {
+			spin_unlock_irqrestore(&ccid_dev->lock, flags);
+			pr_debug("%s: USB cable not connected\n",
+							__func__);
+			while ((req = ccid_req_get(ccid_dev,
+						&bulk_dev->tx_idle)))
+				ccid_request_free(req, ccid_dev->in);
+			r = -ENODEV;
+		}
+		spin_unlock_irqrestore(&ccid_dev->lock, flags);
+		goto done;
+	}
+done:
+	pr_debug("ccid_bulk_write returning %d\n", r);
+	return r;
+}
+
+static const struct file_operations ccid_bulk_fops = {
+	.owner = THIS_MODULE,
+	.read = ccid_bulk_read,
+	.write = ccid_bulk_write,
+	.open = ccid_bulk_open,
+	.release = ccid_bulk_release,
+};
+
+static int ccid_bulk_device_init(struct f_ccid *dev)
+{
+	int ret;
+	struct ccid_bulk_dev *bulk_dev = &dev->bulk_dev;
+
+	init_waitqueue_head(&bulk_dev->read_wq);
+	init_waitqueue_head(&bulk_dev->write_wq);
+	INIT_LIST_HEAD(&bulk_dev->tx_idle);
+
+	bulk_dev->ccid_bulk_device.name = CCID_BULK_DEV_NAME;
+	bulk_dev->ccid_bulk_device.fops = &ccid_bulk_fops;
+	bulk_dev->ccid_bulk_device.minor = MISC_DYNAMIC_MINOR;
+
+	ret = misc_register(&bulk_dev->ccid_bulk_device);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int ccid_ctrl_open(struct inode *inode, struct file *fp)
+{
+	struct ccid_ctrl_dev *ctrl_dev =  container_of(fp->private_data,
+						struct ccid_ctrl_dev,
+						ccid_ctrl_device);
+	struct f_ccid *ccid_dev = ctrl_dev_to_ccid(ctrl_dev);
+	unsigned long flags;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	if (atomic_read(&ctrl_dev->opened)) {
+		pr_debug("%s: ctrl device is already opened\n", __func__);
+		return -EBUSY;
+	}
+	atomic_set(&ctrl_dev->opened, 1);
+	spin_lock_irqsave(&ccid_dev->lock, flags);
+	fp->private_data = ccid_dev;
+	spin_unlock_irqrestore(&ccid_dev->lock, flags);
+
+	return 0;
+}
+
+
+static int ccid_ctrl_release(struct inode *inode, struct file *fp)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+
+	atomic_set(&ctrl_dev->opened, 0);
+
+	return 0;
+}
+
+static ssize_t ccid_ctrl_read(struct file *fp, char __user *buf,
+		      size_t count, loff_t *ppos)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct ccid_ctrl_dev *ctrl_dev = &ccid_dev->ctrl_dev;
+	int ret = 0;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	if (count > CTRL_BUF_SIZE)
+		count = CTRL_BUF_SIZE;
+
+	ret = wait_event_interruptible(ctrl_dev->tx_wait_q,
+					 ctrl_dev->tx_ctrl_done);
+	if (ret < 0)
+		return ret;
+	ctrl_dev->tx_ctrl_done = 0;
+
+	if (!atomic_read(&ccid_dev->online)) {
+		pr_debug("%s: USB cable not connected\n", __func__);
+		return -ENODEV;
+	}
+	ret = copy_to_user(buf, ctrl_dev->buf, count);
+	if (ret)
+		return -EFAULT;
+
+	return count;
+}
+
+static long
+ccid_ctrl_ioctl(struct file *fp, unsigned int cmd, u_long arg)
+{
+	struct f_ccid *ccid_dev = fp->private_data;
+	struct usb_request              *req = ccid_dev->notify_req;
+	struct usb_ccid_notification     *ccid_notify = req->buf;
+	void __user *argp = (void __user *)arg;
+	int ret = 0;
+
+	switch (cmd) {
+	case CCID_NOTIFY_CARD:
+		if (copy_from_user(ccid_notify, argp,
+				sizeof(struct usb_ccid_notification)))
+			return -EFAULT;
+		req->length = 2;
+		break;
+	case CCID_NOTIFY_HWERROR:
+		if (copy_from_user(ccid_notify, argp,
+				sizeof(struct usb_ccid_notification)))
+			return -EFAULT;
+		req->length = 4;
+		break;
+	case CCID_READ_DTR:
+		if (copy_to_user((int *)arg, &ccid_dev->dtr_state, sizeof(int)))
+			return -EFAULT;
+		return 0;
+	}
+	ret = usb_ep_queue(ccid_dev->notify, ccid_dev->notify_req, GFP_KERNEL);
+	if (ret < 0) {
+		pr_err("ccid notify ep enqueue error %d\n", ret);
+		return ret;
+	}
+	return 0;
+}
+
+static const struct file_operations ccid_ctrl_fops = {
+	.owner		= THIS_MODULE,
+	.open		= ccid_ctrl_open,
+	.release	= ccid_ctrl_release,
+	.read		= ccid_ctrl_read,
+	.unlocked_ioctl	= ccid_ctrl_ioctl,
+};
+
+static int ccid_ctrl_device_init(struct f_ccid *dev)
+{
+	int ret;
+	struct ccid_ctrl_dev *ctrl_dev = &dev->ctrl_dev;
+
+	INIT_LIST_HEAD(&ctrl_dev->tx_q);
+	init_waitqueue_head(&ctrl_dev->tx_wait_q);
+
+	ctrl_dev->ccid_ctrl_device.name = CCID_CTRL_DEV_NAME;
+	ctrl_dev->ccid_ctrl_device.fops = &ccid_ctrl_fops;
+	ctrl_dev->ccid_ctrl_device.minor = MISC_DYNAMIC_MINOR;
+
+	ret = misc_register(&ctrl_dev->ccid_ctrl_device);
+	if (ret) {
+		pr_err("%s: failed to register misc device\n", __func__);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void ccid_free_func(struct usb_function *f)
+{
+	pr_debug("%s\n", __func__);
+}
+
+static int ccid_bind_config(struct f_ccid *ccid_dev)
+{
+	pr_debug("ccid_bind_config\n");
+
+	ccid_dev->function.name = FUNCTION_NAME;
+	ccid_dev->function.fs_descriptors = ccid_fs_descs;
+	ccid_dev->function.hs_descriptors = ccid_hs_descs;
+	ccid_dev->function.bind = ccid_function_bind;
+	ccid_dev->function.unbind = ccid_function_unbind;
+	ccid_dev->function.set_alt = ccid_function_set_alt;
+	ccid_dev->function.setup = ccid_function_setup;
+	ccid_dev->function.disable = ccid_function_disable;
+	ccid_dev->function.free_func = ccid_free_func;
+
+	return 0;
+}
+
+static struct f_ccid *ccid_setup(void)
+{
+	struct f_ccid  *ccid_dev;
+	int ret;
+
+	ccid_dev = kzalloc(sizeof(*ccid_dev), GFP_KERNEL);
+	if (!ccid_dev) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	spin_lock_init(&ccid_dev->lock);
+
+	ret = ccid_ctrl_device_init(ccid_dev);
+	if (ret) {
+		pr_err("%s: ccid_ctrl_device_init failed, err:%d\n",
+				__func__, ret);
+		goto err_ctrl_init;
+	}
+	ret = ccid_bulk_device_init(ccid_dev);
+	if (ret) {
+		pr_err("%s: ccid_bulk_device_init failed, err:%d\n",
+				__func__, ret);
+		goto err_bulk_init;
+	}
+
+	return ccid_dev;
+err_bulk_init:
+	misc_deregister(&ccid_dev->ctrl_dev.ccid_ctrl_device);
+err_ctrl_init:
+	kfree(ccid_dev);
+error:
+	pr_err("ccid gadget driver failed to initialize\n");
+	return ERR_PTR(ret);
+}
+
+static inline struct ccid_opts *to_ccid_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct ccid_opts,
+			    func_inst.group);
+}
+
+static void ccid_attr_release(struct config_item *item)
+{
+	struct ccid_opts *opts = to_ccid_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations ccid_item_ops = {
+	.release        = ccid_attr_release,
+};
+
+static struct config_item_type ccid_func_type = {
+	.ct_item_ops    = &ccid_item_ops,
+	.ct_owner       = THIS_MODULE,
+};
+
+static int ccid_set_inst_name(struct usb_function_instance *fi,
+	const char *name)
+{
+	int name_len;
+	struct f_ccid *ccid;
+	struct ccid_opts *opts = container_of(fi, struct ccid_opts, func_inst);
+
+	name_len = strlen(name) + 1;
+	if (name_len > MAX_INST_NAME_LEN)
+		return -ENAMETOOLONG;
+
+	ccid = ccid_setup();
+	if (IS_ERR(ccid))
+		return PTR_ERR(ccid);
+
+	opts->ccid = ccid;
+
+	return 0;
+}
+
+static void ccid_free_inst(struct usb_function_instance *f)
+{
+	struct ccid_opts *opts = container_of(f, struct ccid_opts, func_inst);
+
+	if (!opts->ccid)
+		return;
+
+	misc_deregister(&opts->ccid->ctrl_dev.ccid_ctrl_device);
+	misc_deregister(&opts->ccid->bulk_dev.ccid_bulk_device);
+
+	kfree(opts->ccid);
+	kfree(opts);
+}
+
+
+static struct usb_function_instance *ccid_alloc_inst(void)
+{
+	struct ccid_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.set_inst_name = ccid_set_inst_name;
+	opts->func_inst.free_func_inst = ccid_free_inst;
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &ccid_func_type);
+
+	return &opts->func_inst;
+}
+
+static struct usb_function *ccid_alloc(struct usb_function_instance *fi)
+{
+	struct ccid_opts *opts;
+	int ret;
+
+	opts = container_of(fi, struct ccid_opts, func_inst);
+
+	ret = ccid_bind_config(opts->ccid);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return &opts->ccid->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(ccid, ccid_alloc_inst, ccid_alloc);
+MODULE_DESCRIPTION("USB CCID function Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/gadget/function/f_ccid.h b/drivers/usb/gadget/function/f_ccid.h
new file mode 100644
index 0000000..42a7ebb
--- /dev/null
+++ b/drivers/usb/gadget/function/f_ccid.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2011, 2017 The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details
+ */
+
+#ifndef __F_CCID_H
+#define __F_CCID_H
+
+#define PROTOCOL_TO 0x01
+#define PROTOCOL_T1 0x02
+#define ABDATA_SIZE 512
+
+/* define for dwFeatures for Smart Card Device Class Descriptors */
+/* No special characteristics */
+#define CCID_FEATURES_NADA       0x00000000
+/* Automatic parameter configuration based on ATR data */
+#define CCID_FEATURES_AUTO_PCONF 0x00000002
+/* Automatic activation of ICC on inserting */
+#define CCID_FEATURES_AUTO_ACTIV 0x00000004
+/* Automatic ICC voltage selection */
+#define CCID_FEATURES_AUTO_VOLT  0x00000008
+/* Automatic ICC clock frequency change */
+#define CCID_FEATURES_AUTO_CLOCK 0x00000010
+/* Automatic baud rate change */
+#define CCID_FEATURES_AUTO_BAUD  0x00000020
+/*Automatic parameters negotiation made by the CCID */
+#define CCID_FEATURES_AUTO_PNEGO 0x00000040
+/* Automatic PPS made by the CCID according to the active parameters */
+#define CCID_FEATURES_AUTO_PPS   0x00000080
+/* CCID can set ICC in clock stop mode */
+#define CCID_FEATURES_ICCSTOP    0x00000100
+/* NAD value other than 00 accepted (T=1 protocol in use) */
+#define CCID_FEATURES_NAD        0x00000200
+/* Automatic IFSD exchange as first exchange (T=1 protocol in use) */
+#define CCID_FEATURES_AUTO_IFSD  0x00000400
+/* TPDU level exchanges with CCID */
+#define CCID_FEATURES_EXC_TPDU   0x00010000
+/* Short APDU level exchange with CCID */
+#define CCID_FEATURES_EXC_SAPDU  0x00020000
+/* Short and Extended APDU level exchange with CCID */
+#define CCID_FEATURES_EXC_APDU   0x00040000
+/* USB Wake up signaling supported on card insertion and removal */
+#define CCID_FEATURES_WAKEUP     0x00100000
+
+#define CCID_NOTIFY_CARD	_IOW('C', 1, struct usb_ccid_notification)
+#define CCID_NOTIFY_HWERROR	_IOW('C', 2, struct usb_ccid_notification)
+#define CCID_READ_DTR		_IOR('C', 3, int)
+
+struct usb_ccid_notification {
+	unsigned char buf[4];
+} __packed;
+
+struct ccid_bulk_in_header {
+	unsigned char bMessageType;
+	unsigned long wLength;
+	unsigned char bSlot;
+	unsigned char bSeq;
+	unsigned char bStatus;
+	unsigned char bError;
+	unsigned char bSpecific;
+	unsigned char abData[ABDATA_SIZE];
+	unsigned char bSizeToSend;
+} __packed;
+
+struct ccid_bulk_out_header {
+	unsigned char bMessageType;
+	unsigned long wLength;
+	unsigned char bSlot;
+	unsigned char bSeq;
+	unsigned char bSpecific_0;
+	unsigned char bSpecific_1;
+	unsigned char bSpecific_2;
+	unsigned char APDU[ABDATA_SIZE];
+} __packed;
+#endif
diff --git a/drivers/usb/gadget/function/f_diag.c b/drivers/usb/gadget/function/f_diag.c
index 767f53c..51e6104 100644
--- a/drivers/usb/gadget/function/f_diag.c
+++ b/drivers/usb/gadget/function/f_diag.c
@@ -2,7 +2,7 @@
  * Diag Function Device - Route ARM9 and ARM11 DIAG messages
  * between HOST and DEVICE.
  * Copyright (C) 2007 Google, Inc.
- * Copyright (c) 2008-2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2008-2017, The Linux Foundation. All rights reserved.
  * Author: Brian Swetland <swetland@google.com>
  * This software is licensed under the terms of the GNU General Public
  * License version 2, as published by the Free Software Foundation, and
@@ -70,7 +70,7 @@ static struct usb_interface_descriptor intf_desc = {
 	.bNumEndpoints      =	2,
 	.bInterfaceClass    =	0xFF,
 	.bInterfaceSubClass =	0xFF,
-	.bInterfaceProtocol =	0xFF,
+	.bInterfaceProtocol =	0x30,
 };
 
 static struct usb_endpoint_descriptor hs_bulk_in_desc = {
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index cc718a4..cc3ac26 100644
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -3534,9 +3534,11 @@ static int ffs_func_set_alt(struct usb_function *f,
 
 	ffs->func = func;
 	ret = ffs_func_eps_enable(func);
-	if (likely(ret >= 0))
+	if (likely(ret >= 0)) {
 		ffs_event_add(ffs, FUNCTIONFS_ENABLE);
-
+		/* Disable USB LPM later on bus_suspend */
+		usb_gadget_autopm_get_async(ffs->gadget);
+	}
 	ffs_log("exit: ret %d", ret);
 
 	return ret;
@@ -3544,8 +3546,13 @@ static int ffs_func_set_alt(struct usb_function *f,
 
 static void ffs_func_disable(struct usb_function *f)
 {
+	struct ffs_function *func = ffs_func_from_usb(f);
+	struct ffs_data *ffs = func->ffs;
+
 	ffs_log("enter");
 	ffs_func_set_alt(f, 0, (unsigned)-1);
+	/* matching put to allow LPM on disconnect */
+	usb_gadget_autopm_put_async(ffs->gadget);
 	ffs_log("exit");
 }
 
diff --git a/drivers/usb/gadget/function/f_gsi.c b/drivers/usb/gadget/function/f_gsi.c
new file mode 100644
index 0000000..e41fd34
--- /dev/null
+++ b/drivers/usb/gadget/function/f_gsi.c
@@ -0,0 +1,3094 @@
+/* Copyright (c) 2015-2016, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "f_gsi.h"
+#include "rndis.h"
+
+#define log_event_err(x, ...) pr_err(x, ##__VA_ARGS__)
+#define log_event_dbg(x, ...) pr_debug(x, ##__VA_ARGS__)
+#define log_event_info(x, ...) pr_info(x, ##__VA_ARGS__)
+
+static unsigned int gsi_in_aggr_size;
+module_param(gsi_in_aggr_size, uint, 0644);
+MODULE_PARM_DESC(gsi_in_aggr_size,
+		"Aggr size of bus transfer to host");
+
+static unsigned int gsi_out_aggr_size;
+module_param(gsi_out_aggr_size, uint, 0644);
+MODULE_PARM_DESC(gsi_out_aggr_size,
+		"Aggr size of bus transfer to device");
+
+static unsigned int num_in_bufs = GSI_NUM_IN_BUFFERS;
+module_param(num_in_bufs, uint, 0644);
+MODULE_PARM_DESC(num_in_bufs,
+		"Number of IN buffers");
+
+static unsigned int num_out_bufs = GSI_NUM_OUT_BUFFERS;
+module_param(num_out_bufs, uint, 0644);
+MODULE_PARM_DESC(num_out_bufs,
+		"Number of OUT buffers");
+
+static bool qti_packet_debug;
+module_param(qti_packet_debug, bool, 0644);
+MODULE_PARM_DESC(qti_packet_debug, "Print QTI Packet's Raw Data");
+
+static struct workqueue_struct *ipa_usb_wq;
+
+static void ipa_disconnect_handler(struct gsi_data_port *d_port);
+static int gsi_ctrl_send_notification(struct f_gsi *gsi);
+static int gsi_alloc_trb_buffer(struct f_gsi *gsi);
+static void gsi_free_trb_buffer(struct f_gsi *gsi);
+static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned int len, gfp_t flags);
+static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt);
+
+static inline bool usb_gsi_remote_wakeup_allowed(struct usb_function *f)
+{
+	bool remote_wakeup_allowed;
+
+	if (f->config->cdev->gadget->speed == USB_SPEED_SUPER)
+		remote_wakeup_allowed = f->func_wakeup_allowed;
+	else
+		remote_wakeup_allowed = f->config->cdev->gadget->remote_wakeup;
+
+	log_event_dbg("%s: remote_wakeup_allowed:%s", __func__,
+			remote_wakeup_allowed ? "true" : "false");
+	return remote_wakeup_allowed;
+}
+
+static void post_event(struct gsi_data_port *port, u8 event)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->evt_q.q_lock, flags);
+
+	port->evt_q.tail++;
+	/* Check for wraparound and make room */
+	port->evt_q.tail = port->evt_q.tail % MAXQUEUELEN;
+
+	/* Check for overflow */
+	if (port->evt_q.tail == port->evt_q.head) {
+		log_event_err("%s: event queue overflow error", __func__);
+		spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+		return;
+	}
+	/* Add event to queue */
+	port->evt_q.event[port->evt_q.tail] = event;
+	spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+}
+
+static void __maybe_unused post_event_to_evt_queue(struct gsi_data_port *port,
+								u8 event)
+{
+	post_event(port, event);
+	queue_work(port->ipa_usb_wq, &port->usb_ipa_w);
+}
+
+static u8 read_event(struct gsi_data_port *port)
+{
+	u8 event;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->evt_q.q_lock, flags);
+	if (port->evt_q.head == port->evt_q.tail) {
+		log_event_dbg("%s: event queue empty", __func__);
+		spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+		return EVT_NONE;
+	}
+
+	port->evt_q.head++;
+	/* Check for wraparound and make room */
+	port->evt_q.head = port->evt_q.head % MAXQUEUELEN;
+
+	event = port->evt_q.event[port->evt_q.head];
+	spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+
+	return event;
+}
+
+static u8 peek_event(struct gsi_data_port *port)
+{
+	u8 event;
+	unsigned long flags;
+	u8 peek_index = 0;
+
+	spin_lock_irqsave(&port->evt_q.q_lock, flags);
+	if (port->evt_q.head == port->evt_q.tail) {
+		log_event_dbg("%s: event queue empty", __func__);
+		spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+		return EVT_NONE;
+	}
+
+	peek_index = (port->evt_q.head + 1) % MAXQUEUELEN;
+	event = port->evt_q.event[peek_index];
+	spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+
+	return event;
+}
+
+static void __maybe_unused reset_event_queue(struct gsi_data_port *port)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->evt_q.q_lock, flags);
+	port->evt_q.head = port->evt_q.tail = MAXQUEUELEN - 1;
+	memset(&port->evt_q.event[0], EVT_NONE, MAXQUEUELEN);
+	spin_unlock_irqrestore(&port->evt_q.q_lock, flags);
+}
+
+static int gsi_wakeup_host(struct f_gsi *gsi)
+{
+
+	int ret;
+	struct usb_gadget *gadget;
+	struct usb_function *func;
+
+	func = &gsi->function;
+	gadget = gsi->function.config->cdev->gadget;
+
+	log_event_dbg("Entering %s", __func__);
+
+	if (!gadget) {
+		log_event_err("FAILED: d_port->cdev->gadget == NULL");
+		return -ENODEV;
+	}
+
+	/*
+	 * In Super-Speed mode, remote wakeup is not allowed for suspended
+	 * functions which have been disallowed by the host to issue Function
+	 * Remote Wakeup.
+	 * Note - We deviate here from the USB 3.0 spec and allow
+	 * non-suspended functions to issue remote-wakeup even if they were not
+	 * allowed to do so by the host. This is done in order to support non
+	 * fully USB 3.0 compatible hosts.
+	 */
+	if ((gadget->speed == USB_SPEED_SUPER) && (func->func_is_suspended)) {
+		log_event_dbg("%s: Calling usb_func_wakeup", __func__);
+		ret = usb_func_wakeup(func);
+	} else {
+		log_event_dbg("%s: Calling usb_gadget_wakeup", __func__);
+		ret = usb_gadget_wakeup(gadget);
+	}
+
+	if ((ret == -EBUSY) || (ret == -EAGAIN))
+		log_event_dbg("RW delayed due to LPM exit.");
+	else if (ret)
+		log_event_err("wakeup failed. ret=%d.", ret);
+
+	return ret;
+}
+
+/*
+ * Callback for when when network interface is up
+ * and userspace is ready to answer DHCP requests,  or remote wakeup
+ */
+int ipa_usb_notify_cb(enum ipa_usb_notify_event event,
+	void *driver_data)
+{
+	struct f_gsi *gsi = driver_data;
+	unsigned long flags;
+	struct gsi_ctrl_pkt *cpkt_notify_connect, *cpkt_notify_speed;
+
+	if (!gsi) {
+		log_event_err("%s: invalid driver data", __func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&gsi->d_port.lock, flags);
+
+	switch (event) {
+	case IPA_USB_DEVICE_READY:
+
+		if (gsi->d_port.net_ready_trigger) {
+			spin_unlock_irqrestore(&gsi->d_port.lock, flags);
+			log_event_dbg("%s: Already triggered", __func__);
+			return 1;
+		}
+
+		log_event_err("%s: Set net_ready_trigger", __func__);
+		gsi->d_port.net_ready_trigger = true;
+
+		if (gsi->prot_id == IPA_USB_ECM) {
+			cpkt_notify_connect = gsi_ctrl_pkt_alloc(0, GFP_ATOMIC);
+			if (IS_ERR(cpkt_notify_connect)) {
+				spin_unlock_irqrestore(&gsi->d_port.lock,
+								flags);
+				log_event_dbg("%s: err cpkt_notify_connect\n",
+								__func__);
+				return -ENOMEM;
+			}
+			cpkt_notify_connect->type = GSI_CTRL_NOTIFY_CONNECT;
+
+			cpkt_notify_speed = gsi_ctrl_pkt_alloc(0, GFP_ATOMIC);
+			if (IS_ERR(cpkt_notify_speed)) {
+				spin_unlock_irqrestore(&gsi->d_port.lock,
+								flags);
+				gsi_ctrl_pkt_free(cpkt_notify_connect);
+				log_event_dbg("%s: err cpkt_notify_speed\n",
+								__func__);
+				return -ENOMEM;
+			}
+			cpkt_notify_speed->type = GSI_CTRL_NOTIFY_SPEED;
+			spin_lock_irqsave(&gsi->c_port.lock, flags);
+			list_add_tail(&cpkt_notify_connect->list,
+					&gsi->c_port.cpkt_resp_q);
+			list_add_tail(&cpkt_notify_speed->list,
+					&gsi->c_port.cpkt_resp_q);
+			spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+			gsi_ctrl_send_notification(gsi);
+		}
+
+		/*
+		 * Do not post EVT_CONNECTED for RNDIS.
+		 * Data path for RNDIS is enabled on EVT_HOST_READY.
+		 */
+		if (gsi->prot_id != IPA_USB_RNDIS) {
+			post_event(&gsi->d_port, EVT_CONNECTED);
+			queue_work(gsi->d_port.ipa_usb_wq,
+					&gsi->d_port.usb_ipa_w);
+		}
+		break;
+
+	case IPA_USB_REMOTE_WAKEUP:
+		gsi_wakeup_host(gsi);
+		break;
+
+	case IPA_USB_SUSPEND_COMPLETED:
+		post_event(&gsi->d_port, EVT_IPA_SUSPEND);
+		queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
+		break;
+	}
+
+	spin_unlock_irqrestore(&gsi->d_port.lock, flags);
+	return 1;
+}
+
+static int ipa_connect_channels(struct gsi_data_port *d_port)
+{
+	int ret;
+	struct f_gsi *gsi = d_port_to_gsi(d_port);
+	struct ipa_usb_xdci_chan_params *in_params =
+				&d_port->ipa_in_channel_params;
+	struct ipa_usb_xdci_chan_params *out_params =
+				&d_port->ipa_out_channel_params;
+	struct ipa_usb_xdci_connect_params *conn_params =
+				&d_port->ipa_conn_pms;
+	struct usb_composite_dev *cdev = gsi->function.config->cdev;
+	struct gsi_channel_info gsi_channel_info;
+	struct ipa_req_chan_out_params ipa_in_channel_out_params;
+	struct ipa_req_chan_out_params ipa_out_channel_out_params;
+
+	log_event_dbg("%s: USB GSI IN OPS", __func__);
+	usb_gsi_ep_op(d_port->in_ep, &d_port->in_request,
+		GSI_EP_OP_PREPARE_TRBS);
+	usb_gsi_ep_op(d_port->in_ep, &d_port->in_request,
+			GSI_EP_OP_STARTXFER);
+	d_port->in_xfer_rsc_index = usb_gsi_ep_op(d_port->in_ep, NULL,
+			GSI_EP_OP_GET_XFER_IDX);
+
+	memset(in_params, 0x0, sizeof(*in_params));
+	gsi_channel_info.ch_req = &d_port->in_request;
+	usb_gsi_ep_op(d_port->in_ep, (void *)&gsi_channel_info,
+			GSI_EP_OP_GET_CH_INFO);
+
+	log_event_dbg("%s: USB GSI IN OPS Completed", __func__);
+	in_params->client =
+		(gsi->prot_id != IPA_USB_DIAG) ? IPA_CLIENT_USB_CONS :
+						IPA_CLIENT_USB_DPL_CONS;
+	in_params->ipa_ep_cfg.mode.mode = IPA_BASIC;
+	in_params->teth_prot = gsi->prot_id;
+	in_params->gevntcount_low_addr =
+		gsi_channel_info.gevntcount_low_addr;
+	in_params->gevntcount_hi_addr =
+		gsi_channel_info.gevntcount_hi_addr;
+	in_params->dir = GSI_CHAN_DIR_FROM_GSI;
+	in_params->xfer_ring_len = gsi_channel_info.xfer_ring_len;
+	in_params->xfer_ring_base_addr = gsi_channel_info.xfer_ring_base_addr;
+	in_params->xfer_scratch.last_trb_addr_iova =
+					gsi_channel_info.last_trb_addr;
+	in_params->xfer_ring_base_addr = in_params->xfer_ring_base_addr_iova =
+					gsi_channel_info.xfer_ring_base_addr;
+	in_params->data_buff_base_len = d_port->in_request.buf_len *
+					d_port->in_request.num_bufs;
+	in_params->data_buff_base_addr = in_params->data_buff_base_addr_iova =
+					d_port->in_request.dma;
+	in_params->xfer_scratch.const_buffer_size =
+		gsi_channel_info.const_buffer_size;
+	in_params->xfer_scratch.depcmd_low_addr =
+		gsi_channel_info.depcmd_low_addr;
+	in_params->xfer_scratch.depcmd_hi_addr =
+		gsi_channel_info.depcmd_hi_addr;
+
+	if (d_port->out_ep) {
+		log_event_dbg("%s: USB GSI OUT OPS", __func__);
+		usb_gsi_ep_op(d_port->out_ep, &d_port->out_request,
+			GSI_EP_OP_PREPARE_TRBS);
+		usb_gsi_ep_op(d_port->out_ep, &d_port->out_request,
+				GSI_EP_OP_STARTXFER);
+		d_port->out_xfer_rsc_index =
+			usb_gsi_ep_op(d_port->out_ep,
+				NULL, GSI_EP_OP_GET_XFER_IDX);
+		memset(out_params, 0x0, sizeof(*out_params));
+		gsi_channel_info.ch_req = &d_port->out_request;
+		usb_gsi_ep_op(d_port->out_ep, (void *)&gsi_channel_info,
+				GSI_EP_OP_GET_CH_INFO);
+		log_event_dbg("%s: USB GSI OUT OPS Completed", __func__);
+		out_params->client = IPA_CLIENT_USB_PROD;
+		out_params->ipa_ep_cfg.mode.mode = IPA_BASIC;
+		out_params->teth_prot = gsi->prot_id;
+		out_params->gevntcount_low_addr =
+			gsi_channel_info.gevntcount_low_addr;
+		out_params->gevntcount_hi_addr =
+			gsi_channel_info.gevntcount_hi_addr;
+		out_params->dir = GSI_CHAN_DIR_TO_GSI;
+		out_params->xfer_ring_len =
+			gsi_channel_info.xfer_ring_len;
+		out_params->xfer_ring_base_addr =
+			out_params->xfer_ring_base_addr_iova =
+			gsi_channel_info.xfer_ring_base_addr;
+		out_params->data_buff_base_len = d_port->out_request.buf_len *
+			d_port->out_request.num_bufs;
+		out_params->data_buff_base_addr =
+			out_params->data_buff_base_addr_iova =
+			d_port->out_request.dma;
+		out_params->xfer_scratch.last_trb_addr_iova =
+			gsi_channel_info.last_trb_addr;
+		out_params->xfer_scratch.const_buffer_size =
+			gsi_channel_info.const_buffer_size;
+		out_params->xfer_scratch.depcmd_low_addr =
+			gsi_channel_info.depcmd_low_addr;
+		out_params->xfer_scratch.depcmd_hi_addr =
+			gsi_channel_info.depcmd_hi_addr;
+	}
+
+	/* Populate connection params */
+	conn_params->max_pkt_size =
+		(cdev->gadget->speed == USB_SPEED_SUPER) ?
+		IPA_USB_SUPER_SPEED_1024B : IPA_USB_HIGH_SPEED_512B;
+	conn_params->ipa_to_usb_xferrscidx =
+			d_port->in_xfer_rsc_index;
+	conn_params->usb_to_ipa_xferrscidx =
+			d_port->out_xfer_rsc_index;
+	conn_params->usb_to_ipa_xferrscidx_valid =
+			(gsi->prot_id != IPA_USB_DIAG) ? true : false;
+	conn_params->ipa_to_usb_xferrscidx_valid = true;
+	conn_params->teth_prot = gsi->prot_id;
+	conn_params->teth_prot_params.max_xfer_size_bytes_to_dev = 23700;
+	if (gsi_out_aggr_size)
+		conn_params->teth_prot_params.max_xfer_size_bytes_to_dev
+				= gsi_out_aggr_size;
+	else
+		conn_params->teth_prot_params.max_xfer_size_bytes_to_dev
+				= d_port->out_aggr_size;
+	if (gsi_in_aggr_size)
+		conn_params->teth_prot_params.max_xfer_size_bytes_to_host
+					= gsi_in_aggr_size;
+	else
+		conn_params->teth_prot_params.max_xfer_size_bytes_to_host
+					= d_port->in_aggr_size;
+	conn_params->teth_prot_params.max_packet_number_to_dev =
+		DEFAULT_MAX_PKT_PER_XFER;
+	conn_params->max_supported_bandwidth_mbps =
+		(cdev->gadget->speed == USB_SPEED_SUPER) ? 3600 : 400;
+
+	memset(&ipa_in_channel_out_params, 0x0,
+				sizeof(ipa_in_channel_out_params));
+	memset(&ipa_out_channel_out_params, 0x0,
+				sizeof(ipa_out_channel_out_params));
+
+	log_event_dbg("%s: Calling xdci_connect", __func__);
+	ret = ipa_usb_xdci_connect(out_params, in_params,
+					&ipa_out_channel_out_params,
+					&ipa_in_channel_out_params,
+					conn_params);
+	if (ret) {
+		log_event_err("%s: IPA connect failed %d", __func__, ret);
+		return ret;
+	}
+	log_event_dbg("%s: xdci_connect done", __func__);
+
+	log_event_dbg("%s: IN CH HDL %x", __func__,
+			ipa_in_channel_out_params.clnt_hdl);
+	log_event_dbg("%s: IN CH DBL addr %x", __func__,
+			ipa_in_channel_out_params.db_reg_phs_addr_lsb);
+
+	log_event_dbg("%s: OUT CH HDL %x", __func__,
+			ipa_out_channel_out_params.clnt_hdl);
+	log_event_dbg("%s: OUT CH DBL addr %x", __func__,
+			ipa_out_channel_out_params.db_reg_phs_addr_lsb);
+
+	d_port->in_channel_handle = ipa_in_channel_out_params.clnt_hdl;
+	d_port->in_db_reg_phs_addr_lsb =
+		ipa_in_channel_out_params.db_reg_phs_addr_lsb;
+	d_port->in_db_reg_phs_addr_msb =
+		ipa_in_channel_out_params.db_reg_phs_addr_msb;
+
+	if (gsi->prot_id != IPA_USB_DIAG) {
+		d_port->out_channel_handle =
+			ipa_out_channel_out_params.clnt_hdl;
+		d_port->out_db_reg_phs_addr_lsb =
+			ipa_out_channel_out_params.db_reg_phs_addr_lsb;
+		d_port->out_db_reg_phs_addr_msb =
+			ipa_out_channel_out_params.db_reg_phs_addr_msb;
+	}
+	return ret;
+}
+
+static void ipa_data_path_enable(struct gsi_data_port *d_port)
+{
+	struct f_gsi *gsi = d_port_to_gsi(d_port);
+	struct usb_gsi_request req;
+	u64 dbl_register_addr;
+	bool block_db = false;
+
+
+	log_event_dbg("in_db_reg_phs_addr_lsb = %x",
+			gsi->d_port.in_db_reg_phs_addr_lsb);
+	usb_gsi_ep_op(gsi->d_port.in_ep,
+			(void *)&gsi->d_port.in_db_reg_phs_addr_lsb,
+			GSI_EP_OP_STORE_DBL_INFO);
+
+	if (gsi->d_port.out_ep) {
+		log_event_dbg("out_db_reg_phs_addr_lsb = %x",
+				gsi->d_port.out_db_reg_phs_addr_lsb);
+		usb_gsi_ep_op(gsi->d_port.out_ep,
+				(void *)&gsi->d_port.out_db_reg_phs_addr_lsb,
+				GSI_EP_OP_STORE_DBL_INFO);
+
+		usb_gsi_ep_op(gsi->d_port.out_ep, &gsi->d_port.out_request,
+				GSI_EP_OP_ENABLE_GSI);
+	}
+
+	/* Unblock doorbell to GSI */
+	usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+				GSI_EP_OP_SET_CLR_BLOCK_DBL);
+
+	dbl_register_addr = gsi->d_port.in_db_reg_phs_addr_msb;
+	dbl_register_addr = dbl_register_addr << 32;
+	dbl_register_addr =
+		dbl_register_addr | gsi->d_port.in_db_reg_phs_addr_lsb;
+
+	/* use temp gsi request to pass 64 bit dbl reg addr and num_bufs */
+	req.buf_base_addr = &dbl_register_addr;
+
+	req.num_bufs = gsi->d_port.in_request.num_bufs;
+	usb_gsi_ep_op(gsi->d_port.in_ep, &req, GSI_EP_OP_RING_IN_DB);
+
+	if (gsi->d_port.out_ep) {
+		usb_gsi_ep_op(gsi->d_port.out_ep, &gsi->d_port.out_request,
+			GSI_EP_OP_UPDATEXFER);
+	}
+}
+
+static void ipa_disconnect_handler(struct gsi_data_port *d_port)
+{
+	struct f_gsi *gsi = d_port_to_gsi(d_port);
+	bool block_db = true;
+
+	log_event_dbg("%s: EP Disable for data", __func__);
+
+	if (gsi->d_port.in_ep) {
+		/*
+		 * Block doorbell to GSI to avoid USB wrapper from
+		 * ringing doorbell in case IPA clocks are OFF.
+		 */
+		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+				GSI_EP_OP_SET_CLR_BLOCK_DBL);
+		gsi->in_ep_desc_backup = gsi->d_port.in_ep->desc;
+		usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_DISABLE);
+	}
+
+	if (gsi->d_port.out_ep) {
+		gsi->out_ep_desc_backup = gsi->d_port.out_ep->desc;
+		usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_DISABLE);
+	}
+
+	gsi->d_port.net_ready_trigger = false;
+}
+
+static void ipa_disconnect_work_handler(struct gsi_data_port *d_port)
+{
+	int ret;
+	struct f_gsi *gsi = d_port_to_gsi(d_port);
+
+	log_event_dbg("%s: Calling xdci_disconnect", __func__);
+
+	ret = ipa_usb_xdci_disconnect(gsi->d_port.out_channel_handle,
+			gsi->d_port.in_channel_handle, gsi->prot_id);
+	if (ret)
+		log_event_err("%s: IPA disconnect failed %d",
+				__func__, ret);
+
+	log_event_dbg("%s: xdci_disconnect done", __func__);
+
+	/* invalidate channel handles*/
+	gsi->d_port.in_channel_handle = -EINVAL;
+	gsi->d_port.out_channel_handle = -EINVAL;
+
+	usb_gsi_ep_op(gsi->d_port.in_ep, NULL, GSI_EP_OP_FREE_TRBS);
+
+	if (gsi->d_port.out_ep)
+		usb_gsi_ep_op(gsi->d_port.out_ep, NULL, GSI_EP_OP_FREE_TRBS);
+
+	/* free buffers allocated with each TRB */
+	gsi_free_trb_buffer(gsi);
+}
+
+static int ipa_suspend_work_handler(struct gsi_data_port *d_port)
+{
+	int ret = 0;
+	bool block_db, f_suspend;
+	struct f_gsi *gsi = d_port_to_gsi(d_port);
+	struct usb_function *f = &gsi->function;
+
+	f_suspend = f->func_wakeup_allowed;
+	log_event_dbg("%s: f_suspend:%d", __func__, f_suspend);
+
+	if (!usb_gsi_ep_op(gsi->d_port.in_ep, (void *) &f_suspend,
+				GSI_EP_OP_CHECK_FOR_SUSPEND)) {
+		ret = -EFAULT;
+		goto done;
+	}
+
+	log_event_dbg("%s: Calling xdci_suspend", __func__);
+	ret = ipa_usb_xdci_suspend(gsi->d_port.out_channel_handle,
+				gsi->d_port.in_channel_handle, gsi->prot_id,
+				usb_gsi_remote_wakeup_allowed(f));
+	if (!ret) {
+		d_port->sm_state = STATE_SUSPENDED;
+		log_event_dbg("%s: STATE SUSPENDED", __func__);
+		goto done;
+	}
+
+	if (ret == -EFAULT) {
+		block_db = false;
+		usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+					GSI_EP_OP_SET_CLR_BLOCK_DBL);
+		gsi_wakeup_host(gsi);
+	} else if (ret == -EINPROGRESS) {
+		d_port->sm_state = STATE_SUSPEND_IN_PROGRESS;
+	} else {
+		log_event_err("%s: Error %d for %d", __func__, ret,
+							gsi->prot_id);
+	}
+done:
+	log_event_dbg("%s: xdci_suspend ret %d", __func__, ret);
+	return ret;
+}
+
+static void ipa_resume_work_handler(struct gsi_data_port *d_port)
+{
+	bool block_db;
+	struct f_gsi *gsi = d_port_to_gsi(d_port);
+	int ret;
+
+	log_event_dbg("%s: Calling xdci_resume", __func__);
+
+	ret = ipa_usb_xdci_resume(gsi->d_port.out_channel_handle,
+					gsi->d_port.in_channel_handle,
+					gsi->prot_id);
+	if (ret)
+		log_event_dbg("%s: xdci_resume ret %d", __func__, ret);
+
+	log_event_dbg("%s: xdci_resume done", __func__);
+
+	block_db = false;
+	usb_gsi_ep_op(d_port->in_ep, (void *)&block_db,
+			GSI_EP_OP_SET_CLR_BLOCK_DBL);
+}
+
+static void ipa_work_handler(struct work_struct *w)
+{
+	struct gsi_data_port *d_port = container_of(w, struct gsi_data_port,
+						  usb_ipa_w);
+	u8 event;
+	int ret = 0;
+	struct usb_gadget *gadget = d_port->gadget;
+	struct device *dev;
+	struct device *gad_dev;
+	struct f_gsi *gsi;
+
+	event = read_event(d_port);
+
+	log_event_dbg("%s: event = %x sm_state %x", __func__,
+			event, d_port->sm_state);
+
+	if (gadget) {
+		dev = &gadget->dev;
+		if (!dev || !dev->parent) {
+			log_event_err("%s(): dev or dev->parent is NULL.\n",
+					__func__);
+			return;
+		}
+		gad_dev = dev->parent;
+	} else {
+		log_event_err("%s(): gadget is NULL.\n", __func__);
+		return;
+	}
+
+	gsi = d_port_to_gsi(d_port);
+
+	switch (d_port->sm_state) {
+	case STATE_UNINITIALIZED:
+		break;
+	case STATE_INITIALIZED:
+		if (event == EVT_CONNECT_IN_PROGRESS) {
+			usb_gadget_autopm_get(d_port->gadget);
+			log_event_dbg("%s: get = %d", __func__,
+				atomic_read(&gad_dev->power.usage_count));
+			/* allocate buffers used with each TRB */
+			ret = gsi_alloc_trb_buffer(gsi);
+			if (ret) {
+				log_event_err("%s: gsi_alloc_trb_failed\n",
+								__func__);
+				break;
+			}
+			ipa_connect_channels(d_port);
+			d_port->sm_state = STATE_CONNECT_IN_PROGRESS;
+			log_event_dbg("%s: ST_INIT_EVT_CONN_IN_PROG",
+					__func__);
+		} else if (event == EVT_HOST_READY) {
+			/*
+			 * When in a composition such as RNDIS + ADB,
+			 * RNDIS host sends a GEN_CURRENT_PACKET_FILTER msg
+			 * to enable/disable flow control eg. during RNDIS
+			 * adaptor disable/enable from device manager.
+			 * In the case of the msg to disable flow control,
+			 * connect IPA channels and enable data path.
+			 * EVT_HOST_READY is posted to the state machine
+			 * in the handler for this msg.
+			 */
+			usb_gadget_autopm_get(d_port->gadget);
+			log_event_dbg("%s: get = %d", __func__,
+				atomic_read(&gad_dev->power.usage_count));
+			/* allocate buffers used with each TRB */
+			ret = gsi_alloc_trb_buffer(gsi);
+			if (ret) {
+				log_event_err("%s: gsi_alloc_trb_failed\n",
+								__func__);
+				break;
+			}
+
+			/*
+			 * Update desc and reconfigure USB GSI OUT and IN
+			 * endpoint for RNDIS Adaptor enable case.
+			 */
+			if (d_port->out_ep && !d_port->out_ep->desc &&
+					gsi->out_ep_desc_backup) {
+				d_port->out_ep->desc = gsi->out_ep_desc_backup;
+				d_port->out_ep->ep_intr_num = 1;
+				log_event_dbg("%s: OUT ep_op_config", __func__);
+				usb_gsi_ep_op(d_port->out_ep,
+					&d_port->out_request, GSI_EP_OP_CONFIG);
+			}
+
+			if (d_port->in_ep && !d_port->in_ep->desc &&
+					gsi->in_ep_desc_backup) {
+				d_port->in_ep->desc = gsi->in_ep_desc_backup;
+				d_port->in_ep->ep_intr_num = 2;
+				log_event_dbg("%s: IN ep_op_config", __func__);
+				usb_gsi_ep_op(d_port->in_ep,
+					&d_port->in_request, GSI_EP_OP_CONFIG);
+			}
+
+			ipa_connect_channels(d_port);
+			ipa_data_path_enable(d_port);
+			d_port->sm_state = STATE_CONNECTED;
+			log_event_dbg("%s: ST_INIT_EVT_HOST_READY", __func__);
+		}
+		break;
+	case STATE_CONNECT_IN_PROGRESS:
+		if (event == EVT_HOST_READY) {
+			ipa_data_path_enable(d_port);
+			d_port->sm_state = STATE_CONNECTED;
+			log_event_dbg("%s: ST_CON_IN_PROG_EVT_HOST_READY",
+					 __func__);
+		} else if (event == EVT_CONNECTED) {
+			ipa_data_path_enable(d_port);
+			d_port->sm_state = STATE_CONNECTED;
+			log_event_dbg("%s: ST_CON_IN_PROG_EVT_CON %d",
+					__func__, __LINE__);
+		} else if (event == EVT_SUSPEND) {
+			if (peek_event(d_port) == EVT_DISCONNECTED) {
+				read_event(d_port);
+				ipa_disconnect_work_handler(d_port);
+				d_port->sm_state = STATE_INITIALIZED;
+				usb_gadget_autopm_put_async(d_port->gadget);
+				log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS_DIS",
+						__func__);
+				log_event_dbg("%s: put_async1 = %d", __func__,
+						atomic_read(
+						&gad_dev->power.usage_count));
+				break;
+			}
+			ret = ipa_suspend_work_handler(d_port);
+			if (!ret) {
+				usb_gadget_autopm_put_async(d_port->gadget);
+				log_event_dbg("%s: ST_CON_IN_PROG_EVT_SUS",
+						__func__);
+				log_event_dbg("%s: put_async2 = %d", __func__,
+						atomic_read(
+						&gad_dev->power.usage_count));
+			}
+		} else if (event == EVT_DISCONNECTED) {
+			ipa_disconnect_work_handler(d_port);
+			d_port->sm_state = STATE_INITIALIZED;
+			usb_gadget_autopm_put_async(d_port->gadget);
+			log_event_dbg("%s: ST_CON_IN_PROG_EVT_DIS",
+						__func__);
+			log_event_dbg("%s: put_async3 = %d",
+					__func__, atomic_read(
+						&gad_dev->power.usage_count));
+		}
+		break;
+	case STATE_CONNECTED:
+		if (event == EVT_DISCONNECTED || event == EVT_HOST_NRDY) {
+			if (peek_event(d_port) == EVT_HOST_READY) {
+				read_event(d_port);
+				log_event_dbg("%s: NO_OP NRDY_RDY", __func__);
+				break;
+			}
+
+			if (event == EVT_HOST_NRDY) {
+				log_event_dbg("%s: ST_CON_HOST_NRDY\n",
+								__func__);
+				ipa_disconnect_handler(d_port);
+			}
+
+			ipa_disconnect_work_handler(d_port);
+			d_port->sm_state = STATE_INITIALIZED;
+			usb_gadget_autopm_put_async(d_port->gadget);
+			log_event_dbg("%s: ST_CON_EVT_DIS", __func__);
+			log_event_dbg("%s: put_async4 = %d",
+					__func__, atomic_read(
+						&gad_dev->power.usage_count));
+		} else if (event == EVT_SUSPEND) {
+			if (peek_event(d_port) == EVT_DISCONNECTED) {
+				read_event(d_port);
+				ipa_disconnect_work_handler(d_port);
+				d_port->sm_state = STATE_INITIALIZED;
+				usb_gadget_autopm_put_async(d_port->gadget);
+				log_event_dbg("%s: ST_CON_EVT_SUS_DIS",
+						__func__);
+				log_event_dbg("%s: put_async5 = %d",
+						__func__, atomic_read(
+						&gad_dev->power.usage_count));
+				break;
+			}
+			ret = ipa_suspend_work_handler(d_port);
+			if (!ret) {
+				usb_gadget_autopm_put_async(d_port->gadget);
+				log_event_dbg("%s: ST_CON_EVT_SUS",
+						__func__);
+				log_event_dbg("%s: put_async6 = %d",
+						__func__, atomic_read(
+						&gad_dev->power.usage_count));
+			}
+		} else if (event == EVT_CONNECTED) {
+			d_port->sm_state = STATE_CONNECTED;
+			log_event_dbg("%s: ST_CON_EVT_CON", __func__);
+		}
+		break;
+	case STATE_DISCONNECTED:
+		if (event == EVT_CONNECT_IN_PROGRESS) {
+			ipa_connect_channels(d_port);
+			d_port->sm_state = STATE_CONNECT_IN_PROGRESS;
+			log_event_dbg("%s: ST_DIS_EVT_CON_IN_PROG", __func__);
+		} else if (event == EVT_UNINITIALIZED) {
+			d_port->sm_state = STATE_UNINITIALIZED;
+			log_event_dbg("%s: ST_DIS_EVT_UNINIT", __func__);
+		}
+		break;
+	case STATE_SUSPEND_IN_PROGRESS:
+		if (event == EVT_IPA_SUSPEND) {
+			d_port->sm_state = STATE_SUSPENDED;
+			usb_gadget_autopm_put_async(d_port->gadget);
+			log_event_dbg("%s: ST_SUS_IN_PROG_EVT_IPA_SUS",
+					__func__);
+			log_event_dbg("%s: put_async6 = %d",
+						__func__, atomic_read(
+						&gad_dev->power.usage_count));
+		} else	if (event == EVT_RESUMED) {
+			ipa_resume_work_handler(d_port);
+			d_port->sm_state = STATE_CONNECTED;
+			/*
+			 * Increment usage count here to disallow gadget
+			 * parent suspend. This counter will decrement
+			 * after IPA disconnect is done in disconnect work
+			 * (due to cable disconnect) or in suspended state.
+			 */
+			usb_gadget_autopm_get_noresume(d_port->gadget);
+			log_event_dbg("%s: ST_SUS_IN_PROG_EVT_RES", __func__);
+			log_event_dbg("%s: get_nores1 = %d", __func__,
+					atomic_read(
+						&gad_dev->power.usage_count));
+		} else if (event == EVT_DISCONNECTED) {
+			ipa_disconnect_work_handler(d_port);
+			d_port->sm_state = STATE_INITIALIZED;
+			usb_gadget_autopm_put_async(d_port->gadget);
+			log_event_dbg("%s: ST_SUS_IN_PROG_EVT_DIS", __func__);
+			log_event_dbg("%s: put_async7 = %d", __func__,
+					atomic_read(
+						&gad_dev->power.usage_count));
+		}
+		break;
+
+	case STATE_SUSPENDED:
+		if (event == EVT_RESUMED) {
+			usb_gadget_autopm_get(d_port->gadget);
+			log_event_dbg("%s: ST_SUS_EVT_RES", __func__);
+			log_event_dbg("%s: get = %d", __func__,
+				atomic_read(&gad_dev->power.usage_count));
+			ipa_resume_work_handler(d_port);
+			d_port->sm_state = STATE_CONNECTED;
+		} else if (event == EVT_DISCONNECTED) {
+			ipa_disconnect_work_handler(d_port);
+			d_port->sm_state = STATE_INITIALIZED;
+			log_event_dbg("%s: ST_SUS_EVT_DIS", __func__);
+		}
+		break;
+	default:
+		log_event_dbg("%s: Invalid state to SM", __func__);
+	}
+
+	if (peek_event(d_port) != EVT_NONE) {
+		log_event_dbg("%s: New events to process", __func__);
+		queue_work(d_port->ipa_usb_wq, &d_port->usb_ipa_w);
+	}
+}
+
+static struct gsi_ctrl_pkt *gsi_ctrl_pkt_alloc(unsigned int len, gfp_t flags)
+{
+	struct gsi_ctrl_pkt *pkt;
+
+	pkt = kzalloc(sizeof(struct gsi_ctrl_pkt), flags);
+	if (!pkt)
+		return ERR_PTR(-ENOMEM);
+
+	pkt->buf = kmalloc(len, flags);
+	if (!pkt->buf) {
+		kfree(pkt);
+		return ERR_PTR(-ENOMEM);
+	}
+	pkt->len = len;
+
+	return pkt;
+}
+
+static void gsi_ctrl_pkt_free(struct gsi_ctrl_pkt *pkt)
+{
+	if (pkt) {
+		kfree(pkt->buf);
+		kfree(pkt);
+	}
+}
+
+static void gsi_ctrl_clear_cpkt_queues(struct f_gsi *gsi, bool skip_req_q)
+{
+	struct gsi_ctrl_pkt *cpkt = NULL;
+	struct list_head *act, *tmp;
+
+	spin_lock(&gsi->c_port.lock);
+	if (skip_req_q)
+		goto clean_resp_q;
+
+	list_for_each_safe(act, tmp, &gsi->c_port.cpkt_req_q) {
+		cpkt = list_entry(act, struct gsi_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		gsi_ctrl_pkt_free(cpkt);
+	}
+clean_resp_q:
+	list_for_each_safe(act, tmp, &gsi->c_port.cpkt_resp_q) {
+		cpkt = list_entry(act, struct gsi_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		gsi_ctrl_pkt_free(cpkt);
+	}
+	spin_unlock(&gsi->c_port.lock);
+}
+
+static int gsi_ctrl_send_cpkt_tomodem(struct f_gsi *gsi, void *buf, size_t len)
+{
+	unsigned long flags;
+	struct gsi_ctrl_port *c_port = &gsi->c_port;
+	struct gsi_ctrl_pkt *cpkt;
+
+	spin_lock_irqsave(&c_port->lock, flags);
+	/* drop cpkt if port is not open */
+	if (!gsi->c_port.is_open) {
+		log_event_dbg("%s: ctrl device %s is not open",
+			   __func__, gsi->c_port.name);
+		c_port->cpkt_drop_cnt++;
+		spin_unlock_irqrestore(&c_port->lock, flags);
+		return -ENODEV;
+	}
+
+	cpkt = gsi_ctrl_pkt_alloc(len, GFP_ATOMIC);
+	if (IS_ERR(cpkt)) {
+		log_event_err("%s: Reset func pkt allocation failed", __func__);
+		spin_unlock_irqrestore(&c_port->lock, flags);
+		return -ENOMEM;
+	}
+
+	memcpy(cpkt->buf, buf, len);
+	cpkt->len = len;
+
+	list_add_tail(&cpkt->list, &c_port->cpkt_req_q);
+	c_port->host_to_modem++;
+	spin_unlock_irqrestore(&c_port->lock, flags);
+
+	log_event_dbg("%s: Wake up read queue", __func__);
+	wake_up(&c_port->read_wq);
+
+	return 0;
+}
+
+static int gsi_ctrl_dev_open(struct inode *ip, struct file *fp)
+{
+	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
+						struct gsi_ctrl_port,
+						ctrl_device);
+
+	if (!c_port) {
+		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+		return -ENODEV;
+	}
+
+	log_event_dbg("%s: open ctrl dev %s", __func__, c_port->name);
+
+	if (c_port->is_open) {
+		log_event_err("%s: Already opened", __func__);
+		return -EBUSY;
+	}
+
+	c_port->is_open = true;
+
+	return 0;
+}
+
+static int gsi_ctrl_dev_release(struct inode *ip, struct file *fp)
+{
+	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
+						struct gsi_ctrl_port,
+						ctrl_device);
+
+	if (!c_port) {
+		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+		return -ENODEV;
+	}
+
+	log_event_dbg("close ctrl dev %s", c_port->name);
+
+	c_port->is_open = false;
+
+	return 0;
+}
+
+static ssize_t
+gsi_ctrl_dev_read(struct file *fp, char __user *buf, size_t count, loff_t *pos)
+{
+	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
+						struct gsi_ctrl_port,
+						ctrl_device);
+
+	struct gsi_ctrl_pkt *cpkt = NULL;
+	unsigned long flags;
+	int ret = 0;
+
+	log_event_dbg("%s: Enter %zu", __func__, count);
+
+	if (!c_port) {
+		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+		return -ENODEV;
+	}
+
+	if (count > GSI_MAX_CTRL_PKT_SIZE) {
+		log_event_err("Large buff size %zu, should be %d",
+			count, GSI_MAX_CTRL_PKT_SIZE);
+		return -EINVAL;
+	}
+
+	/* block until a new packet is available */
+	spin_lock_irqsave(&c_port->lock, flags);
+	while (list_empty(&c_port->cpkt_req_q)) {
+		log_event_dbg("Requests list is empty. Wait.");
+		spin_unlock_irqrestore(&c_port->lock, flags);
+		ret = wait_event_interruptible(c_port->read_wq,
+			!list_empty(&c_port->cpkt_req_q));
+		if (ret < 0) {
+			log_event_err("Waiting failed");
+			return -ERESTARTSYS;
+		}
+		log_event_dbg("Received request packet");
+		spin_lock_irqsave(&c_port->lock, flags);
+	}
+
+	cpkt = list_first_entry(&c_port->cpkt_req_q, struct gsi_ctrl_pkt,
+							list);
+	list_del(&cpkt->list);
+	spin_unlock_irqrestore(&c_port->lock, flags);
+
+	if (cpkt->len > count) {
+		log_event_err("cpkt size large:%d > buf size:%zu",
+				cpkt->len, count);
+		gsi_ctrl_pkt_free(cpkt);
+		return -ENOMEM;
+	}
+
+	log_event_dbg("%s: cpkt size:%d", __func__, cpkt->len);
+	if (qti_packet_debug)
+		print_hex_dump(KERN_DEBUG, "READ:", DUMP_PREFIX_OFFSET, 16, 1,
+			buf, min_t(int, 30, cpkt->len), false);
+
+	ret = copy_to_user(buf, cpkt->buf, cpkt->len);
+	if (ret) {
+		log_event_err("copy_to_user failed: err %d", ret);
+		ret = -EFAULT;
+	} else {
+		log_event_dbg("%s: copied %d bytes to user", __func__,
+							cpkt->len);
+		ret = cpkt->len;
+		c_port->copied_to_modem++;
+	}
+
+	gsi_ctrl_pkt_free(cpkt);
+
+	log_event_dbg("%s: Exit %zu", __func__, count);
+
+	return ret;
+}
+
+static ssize_t gsi_ctrl_dev_write(struct file *fp, const char __user *buf,
+		size_t count, loff_t *pos)
+{
+	int ret = 0;
+	unsigned long flags;
+	struct gsi_ctrl_pkt *cpkt;
+	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
+						struct gsi_ctrl_port,
+						ctrl_device);
+	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	struct usb_request *req = c_port->notify_req;
+
+	log_event_dbg("Enter %zu", count);
+
+	if (!c_port || !req || !req->buf) {
+		log_event_err("%s: c_port %p req %p req->buf %p",
+			__func__, c_port, req, req ? req->buf : req);
+		return -ENODEV;
+	}
+
+	if (!count || count > GSI_MAX_CTRL_PKT_SIZE) {
+		log_event_err("error: ctrl pkt length %zu", count);
+		return -EINVAL;
+	}
+
+	if (!atomic_read(&gsi->connected)) {
+		log_event_err("USB cable not connected\n");
+		return -ECONNRESET;
+	}
+
+	if (gsi->function.func_is_suspended &&
+			!gsi->function.func_wakeup_allowed) {
+		c_port->cpkt_drop_cnt++;
+		log_event_err("drop ctrl pkt of len %zu", count);
+		return -ENOTSUPP;
+	}
+
+	cpkt = gsi_ctrl_pkt_alloc(count, GFP_KERNEL);
+	if (IS_ERR(cpkt)) {
+		log_event_err("failed to allocate ctrl pkt");
+		return -ENOMEM;
+	}
+
+	ret = copy_from_user(cpkt->buf, buf, count);
+	if (ret) {
+		log_event_err("copy_from_user failed err:%d", ret);
+		gsi_ctrl_pkt_free(cpkt);
+		return ret;
+	}
+	cpkt->type = GSI_CTRL_NOTIFY_RESPONSE_AVAILABLE;
+	c_port->copied_from_modem++;
+	if (qti_packet_debug)
+		print_hex_dump(KERN_DEBUG, "WRITE:", DUMP_PREFIX_OFFSET, 16, 1,
+			buf, min_t(int, 30, count), false);
+
+	spin_lock_irqsave(&c_port->lock, flags);
+	list_add_tail(&cpkt->list, &c_port->cpkt_resp_q);
+	spin_unlock_irqrestore(&c_port->lock, flags);
+
+	if (!gsi_ctrl_send_notification(gsi))
+		c_port->modem_to_host++;
+
+	log_event_dbg("Exit %zu", count);
+
+	return ret ? ret : count;
+}
+
+static long gsi_ctrl_dev_ioctl(struct file *fp, unsigned int cmd,
+		unsigned long arg)
+{
+	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
+						struct gsi_ctrl_port,
+						ctrl_device);
+	struct f_gsi *gsi = c_port_to_gsi(c_port);
+	struct gsi_ctrl_pkt *cpkt;
+	struct ep_info info;
+	int val, ret = 0;
+	unsigned long flags;
+
+	if (!c_port) {
+		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+		return -ENODEV;
+	}
+
+	switch (cmd) {
+	case QTI_CTRL_MODEM_OFFLINE:
+		if (gsi->prot_id == IPA_USB_DIAG) {
+			log_event_dbg("%s:Modem Offline not handled", __func__);
+			goto exit_ioctl;
+		}
+		atomic_set(&c_port->ctrl_online, 0);
+		gsi_ctrl_clear_cpkt_queues(gsi, true);
+		cpkt = gsi_ctrl_pkt_alloc(0, GFP_KERNEL);
+		if (IS_ERR(cpkt)) {
+			log_event_err("%s: err allocating cpkt\n", __func__);
+			return -ENOMEM;
+		}
+		cpkt->type = GSI_CTRL_NOTIFY_OFFLINE;
+		spin_lock_irqsave(&c_port->lock, flags);
+		list_add_tail(&cpkt->list, &c_port->cpkt_resp_q);
+		spin_unlock_irqrestore(&c_port->lock, flags);
+		gsi_ctrl_send_notification(gsi);
+		break;
+	case QTI_CTRL_MODEM_ONLINE:
+		if (gsi->prot_id == IPA_USB_DIAG) {
+			log_event_dbg("%s:Modem Online not handled", __func__);
+			goto exit_ioctl;
+		}
+
+		atomic_set(&c_port->ctrl_online, 1);
+		break;
+	case QTI_CTRL_GET_LINE_STATE:
+		val = atomic_read(&gsi->connected);
+		ret = copy_to_user((void __user *)arg, &val, sizeof(val));
+		if (ret) {
+			log_event_err("copy_to_user fail LINE_STATE");
+			ret = -EFAULT;
+		}
+		log_event_dbg("%s: Sent line_state: %d for prot id:%d",
+				__func__,
+				atomic_read(&gsi->connected), gsi->prot_id);
+		break;
+	case QTI_CTRL_EP_LOOKUP:
+	case GSI_MBIM_EP_LOOKUP:
+		log_event_dbg("%s: EP_LOOKUP for prot id:%d", __func__,
+							gsi->prot_id);
+		if (!atomic_read(&gsi->connected)) {
+			log_event_dbg("EP_LOOKUP failed: not connected");
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (gsi->prot_id == IPA_USB_DIAG &&
+				(gsi->d_port.in_channel_handle == -EINVAL)) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		if (gsi->d_port.in_channel_handle == -EINVAL &&
+			gsi->d_port.out_channel_handle == -EINVAL) {
+			ret = -EAGAIN;
+			break;
+		}
+
+		info.ph_ep_info.ep_type = GSI_MBIM_DATA_EP_TYPE_HSUSB;
+		info.ph_ep_info.peripheral_iface_id = gsi->data_id;
+		info.ipa_ep_pair.cons_pipe_num =
+		(gsi->prot_id == IPA_USB_DIAG) ? -1 :
+				gsi->d_port.out_channel_handle;
+		info.ipa_ep_pair.prod_pipe_num = gsi->d_port.in_channel_handle;
+
+		log_event_dbg("%s: prot id :%d ep_type:%d intf:%d",
+				__func__, gsi->prot_id, info.ph_ep_info.ep_type,
+				info.ph_ep_info.peripheral_iface_id);
+
+		log_event_dbg("%s: ipa_cons_idx:%d ipa_prod_idx:%d",
+				__func__, info.ipa_ep_pair.cons_pipe_num,
+				info.ipa_ep_pair.prod_pipe_num);
+
+		ret = copy_to_user((void __user *)arg, &info,
+			sizeof(info));
+		if (ret) {
+			log_event_err("copy_to_user fail MBIM");
+			ret = -EFAULT;
+		}
+		break;
+	case GSI_MBIM_GET_NTB_SIZE:
+		ret = copy_to_user((void __user *)arg,
+			&gsi->d_port.ntb_info.ntb_input_size,
+			sizeof(gsi->d_port.ntb_info.ntb_input_size));
+		if (ret) {
+			log_event_err("copy_to_user failNTB_SIZE");
+			ret = -EFAULT;
+		}
+		log_event_dbg("Sent NTB size %d",
+				gsi->d_port.ntb_info.ntb_input_size);
+		break;
+	case GSI_MBIM_GET_DATAGRAM_COUNT:
+		ret = copy_to_user((void __user *)arg,
+			&gsi->d_port.ntb_info.ntb_max_datagrams,
+			sizeof(gsi->d_port.ntb_info.ntb_max_datagrams));
+		if (ret) {
+			log_event_err("copy_to_user fail DATAGRAM");
+			ret = -EFAULT;
+		}
+		log_event_dbg("Sent NTB datagrams count %d",
+			gsi->d_port.ntb_info.ntb_max_datagrams);
+		break;
+	default:
+		log_event_err("wrong parameter");
+		ret = -EINVAL;
+	}
+
+exit_ioctl:
+	return ret;
+}
+
+static unsigned int gsi_ctrl_dev_poll(struct file *fp, poll_table *wait)
+{
+	struct gsi_ctrl_port *c_port = container_of(fp->private_data,
+						struct gsi_ctrl_port,
+						ctrl_device);
+	unsigned long flags;
+	unsigned int mask = 0;
+
+	if (!c_port) {
+		log_event_err("%s: gsi ctrl port %p", __func__, c_port);
+		return -ENODEV;
+	}
+
+	poll_wait(fp, &c_port->read_wq, wait);
+
+	spin_lock_irqsave(&c_port->lock, flags);
+	if (!list_empty(&c_port->cpkt_req_q)) {
+		mask |= POLLIN | POLLRDNORM;
+		log_event_dbg("%s sets POLLIN for %s", __func__, c_port->name);
+	}
+	spin_unlock_irqrestore(&c_port->lock, flags);
+
+	return mask;
+}
+
+/* file operations for rmnet/mbim/dpl devices */
+static const struct file_operations gsi_ctrl_dev_fops = {
+	.owner = THIS_MODULE,
+	.open = gsi_ctrl_dev_open,
+	.release = gsi_ctrl_dev_release,
+	.read = gsi_ctrl_dev_read,
+	.write = gsi_ctrl_dev_write,
+	.unlocked_ioctl = gsi_ctrl_dev_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = gsi_ctrl_dev_ioctl,
+#endif
+	.poll = gsi_ctrl_dev_poll,
+};
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static unsigned int gsi_xfer_bitrate(struct usb_gadget *g)
+{
+	if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+		return 13 * 1024 * 8 * 1000 * 8;
+	else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+		return 13 * 512 * 8 * 1000 * 8;
+	else
+		return 19 * 64 * 1 * 1000 * 8;
+}
+
+static int gsi_function_ctrl_port_init(struct f_gsi *gsi)
+{
+	int ret;
+	int sz = GSI_CTRL_NAME_LEN;
+	bool ctrl_dev_create = true;
+
+	if (!gsi) {
+		log_event_err("%s: gsi prot ctx is NULL", __func__);
+		return -EINVAL;
+	}
+
+	INIT_LIST_HEAD(&gsi->c_port.cpkt_req_q);
+	INIT_LIST_HEAD(&gsi->c_port.cpkt_resp_q);
+
+	spin_lock_init(&gsi->c_port.lock);
+
+	init_waitqueue_head(&gsi->c_port.read_wq);
+
+	if (gsi->prot_id == IPA_USB_RMNET)
+		strlcat(gsi->c_port.name, GSI_RMNET_CTRL_NAME, sz);
+	else if (gsi->prot_id == IPA_USB_MBIM)
+		strlcat(gsi->c_port.name, GSI_MBIM_CTRL_NAME, sz);
+	else if (gsi->prot_id == IPA_USB_DIAG)
+		strlcat(gsi->c_port.name, GSI_DPL_CTRL_NAME, sz);
+	else
+		ctrl_dev_create = false;
+
+	if (!ctrl_dev_create)
+		return 0;
+
+	gsi->c_port.ctrl_device.name = gsi->c_port.name;
+	gsi->c_port.ctrl_device.fops = &gsi_ctrl_dev_fops;
+	gsi->c_port.ctrl_device.minor = MISC_DYNAMIC_MINOR;
+
+	ret = misc_register(&gsi->c_port.ctrl_device);
+	if (ret) {
+		log_event_err("%s: misc register failed prot id %d",
+				__func__, gsi->prot_id);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct net_device *gsi_rndis_get_netdev(const char *netname)
+{
+	struct net_device *net_dev;
+
+	net_dev = dev_get_by_name(&init_net, netname);
+	if (!net_dev)
+		return ERR_PTR(-EINVAL);
+
+	/*
+	 * Decrement net_dev refcount as it was incremented in
+	 * dev_get_by_name().
+	 */
+	dev_put(net_dev);
+	return net_dev;
+}
+
+static void gsi_rndis_open(struct f_gsi *rndis)
+{
+	struct usb_composite_dev *cdev = rndis->function.config->cdev;
+
+	log_event_dbg("%s", __func__);
+
+	rndis_set_param_medium(rndis->params, RNDIS_MEDIUM_802_3,
+				gsi_xfer_bitrate(cdev->gadget) / 100);
+	rndis_signal_connect(rndis->params);
+}
+
+void gsi_rndis_flow_ctrl_enable(bool enable, struct rndis_params *param)
+{
+	struct f_gsi *rndis = param->v;
+	struct gsi_data_port *d_port;
+
+	if (!rndis) {
+		log_event_err("%s: gsi prot ctx is %p", __func__, rndis);
+		return;
+	}
+
+	d_port = &rndis->d_port;
+
+	if (enable) {
+		log_event_dbg("%s: posting HOST_NRDY\n", __func__);
+		post_event(d_port, EVT_HOST_NRDY);
+	} else {
+		log_event_dbg("%s: posting HOST_READY\n", __func__);
+		post_event(d_port, EVT_HOST_READY);
+	}
+
+	queue_work(rndis->d_port.ipa_usb_wq, &rndis->d_port.usb_ipa_w);
+}
+
+static int queue_notification_request(struct f_gsi *gsi)
+{
+	int ret;
+	unsigned long flags;
+
+	ret = usb_func_ep_queue(&gsi->function, gsi->c_port.notify,
+			   gsi->c_port.notify_req, GFP_ATOMIC);
+	if (ret < 0) {
+		spin_lock_irqsave(&gsi->c_port.lock, flags);
+		gsi->c_port.notify_req_queued = false;
+		spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+	}
+
+	log_event_dbg("%s: ret:%d req_queued:%d",
+		__func__, ret, gsi->c_port.notify_req_queued);
+
+	return ret;
+}
+
+static int gsi_ctrl_send_notification(struct f_gsi *gsi)
+{
+	__le32 *data;
+	struct usb_cdc_notification *event;
+	struct usb_request *req = gsi->c_port.notify_req;
+	struct usb_composite_dev *cdev = gsi->function.config->cdev;
+	struct gsi_ctrl_pkt *cpkt;
+	unsigned long flags;
+	bool del_free_cpkt = false;
+
+	if (!atomic_read(&gsi->connected)) {
+		log_event_dbg("%s: cable disconnect", __func__);
+		return -ENODEV;
+	}
+
+	spin_lock_irqsave(&gsi->c_port.lock, flags);
+	if (list_empty(&gsi->c_port.cpkt_resp_q)) {
+		spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+		log_event_dbg("%s: cpkt_resp_q is empty\n", __func__);
+		return 0;
+	}
+
+	log_event_dbg("%s: notify_req_queued:%d\n",
+		__func__, gsi->c_port.notify_req_queued);
+
+	if (gsi->c_port.notify_req_queued) {
+		spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+		log_event_dbg("%s: notify_req is already queued.\n", __func__);
+		return 0;
+	}
+
+	cpkt = list_first_entry(&gsi->c_port.cpkt_resp_q,
+			struct gsi_ctrl_pkt, list);
+	log_event_dbg("%s: cpkt->type:%d\n", __func__, cpkt->type);
+
+	event = req->buf;
+
+	switch (cpkt->type) {
+	case GSI_CTRL_NOTIFY_CONNECT:
+		del_free_cpkt = true;
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		event->wValue = cpu_to_le16(1);
+		event->wLength = cpu_to_le16(0);
+		break;
+	case GSI_CTRL_NOTIFY_SPEED:
+		del_free_cpkt = true;
+		event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(8);
+
+		/* SPEED_CHANGE data is up/down speeds in bits/sec */
+		data = req->buf + sizeof(*event);
+		data[0] = cpu_to_le32(gsi_xfer_bitrate(cdev->gadget));
+		data[1] = data[0];
+
+		log_event_dbg("notify speed %d",
+				gsi_xfer_bitrate(cdev->gadget));
+		break;
+	case GSI_CTRL_NOTIFY_OFFLINE:
+		del_free_cpkt = true;
+		event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(0);
+		break;
+	case GSI_CTRL_NOTIFY_RESPONSE_AVAILABLE:
+		event->bNotificationType = USB_CDC_NOTIFY_RESPONSE_AVAILABLE;
+		event->wValue = cpu_to_le16(0);
+		event->wLength = cpu_to_le16(0);
+
+		if (gsi->prot_id == IPA_USB_RNDIS) {
+			data = req->buf;
+			data[0] = cpu_to_le32(1);
+			data[1] = cpu_to_le32(0);
+			/*
+			 * we need to free dummy packet for RNDIS as sending
+			 * notification about response available multiple time,
+			 * RNDIS host driver doesn't like. All SEND/GET
+			 * ENCAPSULATED response is one-to-one for RNDIS case
+			 * and host expects to have below sequence:
+			 * ep0: USB_CDC_SEND_ENCAPSULATED_COMMAND
+			 * int_ep: device->host: RESPONSE_AVAILABLE
+			 * ep0: USB_GET_SEND_ENCAPSULATED_COMMAND
+			 * For RMNET case: host ignores multiple notification.
+			 */
+			del_free_cpkt = true;
+		}
+		break;
+	default:
+		spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+		log_event_err("%s:unknown notify state", __func__);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	/*
+	 * Delete and free cpkt related to non NOTIFY_RESPONSE_AVAILABLE
+	 * notification whereas NOTIFY_RESPONSE_AVAILABLE related cpkt is
+	 * deleted from USB_CDC_GET_ENCAPSULATED_RESPONSE setup request
+	 */
+	if (del_free_cpkt) {
+		list_del(&cpkt->list);
+		gsi_ctrl_pkt_free(cpkt);
+	}
+
+	gsi->c_port.notify_req_queued = true;
+	spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+	log_event_dbg("send Notify type %02x", event->bNotificationType);
+
+	return queue_notification_request(gsi);
+}
+
+static void gsi_ctrl_notify_resp_complete(struct usb_ep *ep,
+					struct usb_request *req)
+{
+	struct f_gsi *gsi = req->context;
+	struct usb_cdc_notification *event = req->buf;
+	int status = req->status;
+	unsigned long flags;
+
+	spin_lock_irqsave(&gsi->c_port.lock, flags);
+	gsi->c_port.notify_req_queued = false;
+	spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+
+	switch (status) {
+	case -ECONNRESET:
+	case -ESHUTDOWN:
+		/* connection gone */
+		log_event_dbg("ESHUTDOWN/ECONNRESET, connection gone");
+		gsi_ctrl_clear_cpkt_queues(gsi, false);
+		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
+		break;
+	default:
+		log_event_err("Unknown event %02x --> %d",
+			event->bNotificationType, req->status);
+		/* FALLTHROUGH */
+	case 0:
+		/*
+		 * handle multiple pending resp available
+		 * notifications by queuing same until we're done,
+		 * rest of the notification require queuing new
+		 * request.
+		 */
+		gsi_ctrl_send_notification(gsi);
+		break;
+	}
+}
+
+static void gsi_rndis_response_available(void *_rndis)
+{
+	struct f_gsi *gsi = _rndis;
+	struct gsi_ctrl_pkt *cpkt;
+	unsigned long flags;
+
+	cpkt = gsi_ctrl_pkt_alloc(0, GFP_ATOMIC);
+	if (IS_ERR(cpkt)) {
+		log_event_err("%s: err allocating cpkt\n", __func__);
+		return;
+	}
+
+	cpkt->type = GSI_CTRL_NOTIFY_RESPONSE_AVAILABLE;
+	spin_lock_irqsave(&gsi->c_port.lock, flags);
+	list_add_tail(&cpkt->list, &gsi->c_port.cpkt_resp_q);
+	spin_unlock_irqrestore(&gsi->c_port.lock, flags);
+	gsi_ctrl_send_notification(gsi);
+}
+
+static void gsi_rndis_command_complete(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct f_gsi *rndis = req->context;
+	int status;
+
+	status = rndis_msg_parser(rndis->params, (u8 *) req->buf);
+	if (status < 0)
+		log_event_err("RNDIS command error %d, %d/%d",
+			status, req->actual, req->length);
+}
+
+static void
+gsi_ctrl_set_ntb_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	/* now for SET_NTB_INPUT_SIZE only */
+	unsigned int in_size = 0;
+	struct f_gsi *gsi = req->context;
+	struct gsi_ntb_info *ntb = NULL;
+
+	log_event_dbg("dev:%p", gsi);
+
+	req->context = NULL;
+	if (req->status || req->actual != req->length) {
+		log_event_err("Bad control-OUT transfer");
+		goto invalid;
+	}
+
+	if (req->length == 4) {
+		in_size = get_unaligned_le32(req->buf);
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		in_size > le32_to_cpu(mbim_gsi_ntb_parameters.dwNtbInMaxSize))
+			goto invalid;
+	} else if (req->length == 8) {
+		ntb = (struct gsi_ntb_info *)req->buf;
+		in_size = get_unaligned_le32(&(ntb->ntb_input_size));
+		if (in_size < USB_CDC_NCM_NTB_MIN_IN_SIZE ||
+		in_size > le32_to_cpu(mbim_gsi_ntb_parameters.dwNtbInMaxSize))
+			goto invalid;
+
+		gsi->d_port.ntb_info.ntb_max_datagrams =
+			get_unaligned_le16(&(ntb->ntb_max_datagrams));
+	} else {
+		goto invalid;
+	}
+
+	log_event_dbg("Set NTB INPUT SIZE %d", in_size);
+
+	gsi->d_port.ntb_info.ntb_input_size = in_size;
+	return;
+
+invalid:
+	log_event_err("Illegal NTB INPUT SIZE %d from host", in_size);
+	usb_ep_set_halt(ep);
+}
+
+static void gsi_ctrl_cmd_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct f_gsi *gsi = req->context;
+
+	gsi_ctrl_send_cpkt_tomodem(gsi, req->buf, req->actual);
+}
+
+static void gsi_ctrl_reset_cmd_complete(struct usb_ep *ep,
+		struct usb_request *req)
+{
+	struct f_gsi *gsi = req->context;
+
+	gsi_ctrl_send_cpkt_tomodem(gsi, req->buf, 0);
+}
+
+static int
+gsi_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+	struct f_gsi *gsi = func_to_gsi(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request *req = cdev->req;
+	int id, value = -EOPNOTSUPP;
+	u16 w_index = le16_to_cpu(ctrl->wIndex);
+	u16 w_value = le16_to_cpu(ctrl->wValue);
+	u16 w_length = le16_to_cpu(ctrl->wLength);
+	struct gsi_ctrl_pkt *cpkt;
+	u8 *buf;
+	u32 n;
+
+	if (!atomic_read(&gsi->connected)) {
+		log_event_dbg("usb cable is not connected");
+		return -ENOTCONN;
+	}
+
+	/* rmnet and dpl does not have ctrl_id */
+	if (gsi->ctrl_id == -ENODEV)
+		id = gsi->data_id;
+	else
+		id = gsi->ctrl_id;
+
+	/* composite driver infrastructure handles everything except
+	 * CDC class messages; interface activation uses set_alt().
+	 */
+	switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_RESET_FUNCTION:
+
+		log_event_dbg("USB_CDC_RESET_FUNCTION");
+		value = 0;
+		req->complete = gsi_ctrl_reset_cmd_complete;
+		req->context = gsi;
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SEND_ENCAPSULATED_COMMAND:
+		log_event_dbg("USB_CDC_SEND_ENCAPSULATED_COMMAND");
+
+		if (w_value || w_index != id)
+			goto invalid;
+		/* read the request; process it later */
+		value = w_length;
+		req->context = gsi;
+		if (gsi->prot_id == IPA_USB_RNDIS)
+			req->complete = gsi_rndis_command_complete;
+		else
+			req->complete = gsi_ctrl_cmd_complete;
+		/* later, rndis_response_available() sends a notification */
+		break;
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_GET_ENCAPSULATED_RESPONSE:
+		log_event_dbg("USB_CDC_GET_ENCAPSULATED_RESPONSE");
+		if (w_value || w_index != id)
+			goto invalid;
+
+		if (gsi->prot_id == IPA_USB_RNDIS) {
+			/* return the result */
+			buf = rndis_get_next_response(gsi->params, &n);
+			if (buf) {
+				memcpy(req->buf, buf, n);
+				rndis_free_response(gsi->params, buf);
+				value = n;
+			}
+			break;
+		}
+
+		spin_lock(&gsi->c_port.lock);
+		if (list_empty(&gsi->c_port.cpkt_resp_q)) {
+			log_event_dbg("ctrl resp queue empty");
+			spin_unlock(&gsi->c_port.lock);
+			break;
+		}
+
+		cpkt = list_first_entry(&gsi->c_port.cpkt_resp_q,
+					struct gsi_ctrl_pkt, list);
+		list_del(&cpkt->list);
+		gsi->c_port.get_encap_cnt++;
+		spin_unlock(&gsi->c_port.lock);
+
+		value = min_t(unsigned int, w_length, cpkt->len);
+		memcpy(req->buf, cpkt->buf, value);
+		gsi_ctrl_pkt_free(cpkt);
+
+		log_event_dbg("copied encap_resp %d bytes",
+			value);
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+		log_event_dbg("%s: USB_CDC_REQ_SET_CONTROL_LINE_STATE DTR:%d\n",
+				__func__, w_value & GSI_CTRL_DTR ? 1 : 0);
+		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
+		value = 0;
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+			| USB_CDC_SET_ETHERNET_PACKET_FILTER:
+		/* see 6.2.30: no data, wIndex = interface,
+		 * wValue = packet filter bitmap
+		 */
+		if (w_length != 0 || w_index != id)
+			goto invalid;
+		log_event_dbg("packet filter %02x", w_value);
+		/* REVISIT locking of cdc_filter.  This assumes the UDC
+		 * driver won't have a concurrent packet TX irq running on
+		 * another CPU; or that if it does, this write is atomic...
+		 */
+		gsi->d_port.cdc_filter = w_value;
+		value = 0;
+		break;
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_PARAMETERS:
+		log_event_dbg("USB_CDC_GET_NTB_PARAMETERS");
+
+		if (w_length == 0 || w_value != 0 || w_index != id)
+			break;
+
+		value = w_length > sizeof(mbim_gsi_ntb_parameters) ?
+			sizeof(mbim_gsi_ntb_parameters) : w_length;
+		memcpy(req->buf, &mbim_gsi_ntb_parameters, value);
+		break;
+	case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_GET_NTB_INPUT_SIZE:
+
+		log_event_dbg("USB_CDC_GET_NTB_INPUT_SIZE");
+
+		if (w_length < 4 || w_value != 0 || w_index != id)
+			break;
+
+		put_unaligned_le32(gsi->d_port.ntb_info.ntb_input_size,
+				req->buf);
+		value = 4;
+		log_event_dbg("Reply to host INPUT SIZE %d",
+			 gsi->d_port.ntb_info.ntb_input_size);
+		break;
+	case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+		| USB_CDC_SET_NTB_INPUT_SIZE:
+		log_event_dbg("USB_CDC_SET_NTB_INPUT_SIZE");
+
+		if (w_length != 4 && w_length != 8) {
+			log_event_err("wrong NTB length %d", w_length);
+			break;
+		}
+
+		if (w_value != 0 || w_index != id)
+			break;
+
+		req->complete = gsi_ctrl_set_ntb_cmd_complete;
+		req->length = w_length;
+		req->context = gsi;
+
+		value = req->length;
+		break;
+	default:
+invalid:
+		log_event_err("inval ctrl req%02x.%02x v%04x i%04x l%d",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+	}
+
+	/* respond with data transfer or status phase? */
+	if (value >= 0) {
+		log_event_dbg("req%02x.%02x v%04x i%04x l%d",
+			ctrl->bRequestType, ctrl->bRequest,
+			w_value, w_index, w_length);
+		req->zero = (value < w_length);
+		req->length = value;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0)
+			log_event_err("response on err %d", value);
+	}
+
+	/* device either stalls (value < 0) or reports success */
+	return value;
+}
+
+/*
+ * Because the data interface supports multiple altsettings,
+ * function *MUST* implement a get_alt() method.
+ */
+static int gsi_get_alt(struct usb_function *f, unsigned int intf)
+{
+	struct f_gsi *gsi = func_to_gsi(f);
+
+	/* RNDIS, RMNET and DPL only support alt 0*/
+	if (intf == gsi->ctrl_id || gsi->prot_id == IPA_USB_RNDIS ||
+			gsi->prot_id == IPA_USB_RMNET ||
+			gsi->prot_id == IPA_USB_DIAG)
+		return 0;
+	else if (intf == gsi->data_id)
+		return gsi->data_interface_up;
+
+	return -EINVAL;
+}
+
+static int gsi_alloc_trb_buffer(struct f_gsi *gsi)
+{
+	u32 len_in = 0, len_out = 0;
+	int ret = 0;
+
+	log_event_dbg("allocate trb's buffer\n");
+
+	if (gsi->d_port.in_ep && !gsi->d_port.in_request.buf_base_addr) {
+		log_event_dbg("IN: num_bufs:=%zu, buf_len=%zu\n",
+			gsi->d_port.in_request.num_bufs,
+			gsi->d_port.in_request.buf_len);
+
+		len_in = gsi->d_port.in_request.buf_len *
+				gsi->d_port.in_request.num_bufs;
+		gsi->d_port.in_request.buf_base_addr =
+			dma_zalloc_coherent(gsi->d_port.gadget->dev.parent,
+			len_in, &gsi->d_port.in_request.dma, GFP_KERNEL);
+		if (!gsi->d_port.in_request.buf_base_addr) {
+			dev_err(&gsi->d_port.gadget->dev,
+					"IN buf_base_addr allocate failed %s\n",
+					gsi->function.name);
+			ret = -ENOMEM;
+			goto fail1;
+		}
+	}
+
+	if (gsi->d_port.out_ep && !gsi->d_port.out_request.buf_base_addr) {
+		log_event_dbg("OUT: num_bufs:=%zu, buf_len=%zu\n",
+			gsi->d_port.out_request.num_bufs,
+			gsi->d_port.out_request.buf_len);
+
+		len_out = gsi->d_port.out_request.buf_len *
+				gsi->d_port.out_request.num_bufs;
+		gsi->d_port.out_request.buf_base_addr =
+			dma_zalloc_coherent(gsi->d_port.gadget->dev.parent,
+			len_out, &gsi->d_port.out_request.dma, GFP_KERNEL);
+		if (!gsi->d_port.out_request.buf_base_addr) {
+			dev_err(&gsi->d_port.gadget->dev,
+					"OUT buf_base_addr allocate failed %s\n",
+					gsi->function.name);
+			ret = -ENOMEM;
+			goto fail;
+		}
+	}
+
+	log_event_dbg("finished allocating trb's buffer\n");
+	return ret;
+
+fail:
+	if (len_in && gsi->d_port.in_request.buf_base_addr) {
+		dma_free_coherent(gsi->d_port.gadget->dev.parent, len_in,
+				gsi->d_port.in_request.buf_base_addr,
+				gsi->d_port.in_request.dma);
+		gsi->d_port.in_request.buf_base_addr = NULL;
+	}
+fail1:
+	return ret;
+}
+
+static void gsi_free_trb_buffer(struct f_gsi *gsi)
+{
+	u32 len;
+
+	log_event_dbg("freeing trb's buffer\n");
+
+	if (gsi->d_port.out_ep &&
+			gsi->d_port.out_request.buf_base_addr) {
+		len = gsi->d_port.out_request.buf_len *
+			gsi->d_port.out_request.num_bufs;
+		dma_free_coherent(gsi->d_port.gadget->dev.parent, len,
+			gsi->d_port.out_request.buf_base_addr,
+			gsi->d_port.out_request.dma);
+		gsi->d_port.out_request.buf_base_addr = NULL;
+	}
+
+	if (gsi->d_port.in_ep &&
+			gsi->d_port.in_request.buf_base_addr) {
+		len = gsi->d_port.in_request.buf_len *
+			gsi->d_port.in_request.num_bufs;
+		dma_free_coherent(gsi->d_port.gadget->dev.parent, len,
+			gsi->d_port.in_request.buf_base_addr,
+			gsi->d_port.in_request.dma);
+		gsi->d_port.in_request.buf_base_addr = NULL;
+	}
+}
+
+static int gsi_set_alt(struct usb_function *f, unsigned int intf,
+						unsigned int alt)
+{
+	struct f_gsi	 *gsi = func_to_gsi(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct net_device	*net;
+	int ret;
+
+	log_event_dbg("intf=%u, alt=%u", intf, alt);
+
+	/* Control interface has only altsetting 0 */
+	if (intf == gsi->ctrl_id || gsi->prot_id == IPA_USB_RMNET) {
+		if (alt != 0)
+			goto fail;
+
+		if (!gsi->c_port.notify)
+			goto fail;
+
+		if (gsi->c_port.notify->driver_data) {
+			log_event_dbg("reset gsi control %d", intf);
+			usb_ep_disable(gsi->c_port.notify);
+		}
+
+		ret = config_ep_by_speed(cdev->gadget, f,
+					gsi->c_port.notify);
+		if (ret) {
+			gsi->c_port.notify->desc = NULL;
+			log_event_err("Config-fail notify ep %s: err %d",
+				gsi->c_port.notify->name, ret);
+			goto fail;
+		}
+
+		ret = usb_ep_enable(gsi->c_port.notify);
+		if (ret) {
+			log_event_err("usb ep#%s enable failed, err#%d",
+				gsi->c_port.notify->name, ret);
+			goto fail;
+		}
+		gsi->c_port.notify->driver_data = gsi;
+	}
+
+	/* Data interface has two altsettings, 0 and 1 */
+	if (intf == gsi->data_id) {
+		gsi->d_port.net_ready_trigger = false;
+		/* for rndis and rmnet alt is always 0 update alt accordingly */
+		if (gsi->prot_id == IPA_USB_RNDIS ||
+				gsi->prot_id == IPA_USB_RMNET ||
+				gsi->prot_id == IPA_USB_DIAG) {
+			if (gsi->d_port.in_ep &&
+				!gsi->d_port.in_ep->driver_data)
+				alt = 1;
+			else
+				alt = 0;
+		}
+
+		if (alt > 1)
+			goto notify_ep_disable;
+
+		if (gsi->data_interface_up == alt)
+			return 0;
+
+		if (gsi->d_port.in_ep && gsi->d_port.in_ep->driver_data)
+			gsi->d_port.ntb_info.ntb_input_size =
+				MBIM_NTB_DEFAULT_IN_SIZE;
+		if (alt == 1) {
+			if (gsi->d_port.in_ep && !gsi->d_port.in_ep->desc
+				&& config_ep_by_speed(cdev->gadget, f,
+					gsi->d_port.in_ep)) {
+				gsi->d_port.in_ep->desc = NULL;
+				goto notify_ep_disable;
+			}
+
+			if (gsi->d_port.out_ep && !gsi->d_port.out_ep->desc
+				&& config_ep_by_speed(cdev->gadget, f,
+					gsi->d_port.out_ep)) {
+				gsi->d_port.out_ep->desc = NULL;
+				goto notify_ep_disable;
+			}
+
+			/* Configure EPs for GSI */
+			if (gsi->d_port.in_ep) {
+				if (gsi->prot_id == IPA_USB_DIAG)
+					gsi->d_port.in_ep->ep_intr_num = 3;
+				else
+					gsi->d_port.in_ep->ep_intr_num = 2;
+				usb_gsi_ep_op(gsi->d_port.in_ep,
+					&gsi->d_port.in_request,
+						GSI_EP_OP_CONFIG);
+			}
+
+			if (gsi->d_port.out_ep) {
+				gsi->d_port.out_ep->ep_intr_num = 1;
+				usb_gsi_ep_op(gsi->d_port.out_ep,
+					&gsi->d_port.out_request,
+						GSI_EP_OP_CONFIG);
+			}
+
+			gsi->d_port.gadget = cdev->gadget;
+
+			if (gsi->prot_id == IPA_USB_RNDIS) {
+				gsi_rndis_open(gsi);
+				net = gsi_rndis_get_netdev("rndis0");
+				if (IS_ERR(net))
+					goto notify_ep_disable;
+
+				log_event_dbg("RNDIS RX/TX early activation");
+				gsi->d_port.cdc_filter = 0;
+				rndis_set_param_dev(gsi->params, net,
+						&gsi->d_port.cdc_filter);
+			}
+
+			if (gsi->prot_id == IPA_USB_ECM)
+				gsi->d_port.cdc_filter = DEFAULT_FILTER;
+
+			/*
+			 * For RNDIS the event is posted from the flow control
+			 * handler which is invoked when the host sends the
+			 * GEN_CURRENT_PACKET_FILTER message.
+			 */
+			if (gsi->prot_id != IPA_USB_RNDIS)
+				post_event(&gsi->d_port,
+						EVT_CONNECT_IN_PROGRESS);
+			queue_work(gsi->d_port.ipa_usb_wq,
+					&gsi->d_port.usb_ipa_w);
+		}
+
+		if (alt == 0 && ((gsi->d_port.in_ep &&
+				!gsi->d_port.in_ep->driver_data) ||
+				(gsi->d_port.out_ep &&
+				!gsi->d_port.out_ep->driver_data)))
+			ipa_disconnect_handler(&gsi->d_port);
+
+		gsi->data_interface_up = alt;
+		log_event_dbg("DATA_INTERFACE id = %d, status = %d",
+				gsi->data_id, gsi->data_interface_up);
+	}
+
+	atomic_set(&gsi->connected, 1);
+
+	/* send 0 len pkt to qti to notify state change */
+	if (gsi->prot_id == IPA_USB_DIAG)
+		gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
+
+	return 0;
+
+notify_ep_disable:
+	if (gsi->c_port.notify && gsi->c_port.notify->driver_data)
+		usb_ep_disable(gsi->c_port.notify);
+fail:
+	return -EINVAL;
+}
+
+static void gsi_disable(struct usb_function *f)
+{
+	struct f_gsi *gsi = func_to_gsi(f);
+
+	atomic_set(&gsi->connected, 0);
+
+	if (gsi->prot_id == IPA_USB_RNDIS)
+		rndis_uninit(gsi->params);
+
+	 /* Disable Control Path */
+	if (gsi->c_port.notify &&
+		gsi->c_port.notify->driver_data) {
+		usb_ep_disable(gsi->c_port.notify);
+		gsi->c_port.notify->driver_data = NULL;
+	}
+
+	gsi_ctrl_clear_cpkt_queues(gsi, false);
+	/* send 0 len pkt to qti/qbi to notify state change */
+	gsi_ctrl_send_cpkt_tomodem(gsi, NULL, 0);
+	gsi->c_port.notify_req_queued = false;
+	/* Disable Data Path  - only if it was initialized already (alt=1) */
+	if (!gsi->data_interface_up) {
+		log_event_dbg("%s: data intf is closed", __func__);
+		return;
+	}
+
+	gsi->data_interface_up = false;
+
+	log_event_dbg("%s deactivated", gsi->function.name);
+	ipa_disconnect_handler(&gsi->d_port);
+	post_event(&gsi->d_port, EVT_DISCONNECTED);
+	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
+}
+
+static void gsi_suspend(struct usb_function *f)
+{
+	bool block_db;
+	struct f_gsi *gsi = func_to_gsi(f);
+
+	/* Check if function is already suspended in gsi_func_suspend() */
+	if (f->func_is_suspended) {
+		log_event_dbg("%s: func already suspended, return\n", __func__);
+		return;
+	}
+
+	block_db = true;
+	usb_gsi_ep_op(gsi->d_port.in_ep, (void *)&block_db,
+			GSI_EP_OP_SET_CLR_BLOCK_DBL);
+	post_event(&gsi->d_port, EVT_SUSPEND);
+	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
+	log_event_dbg("gsi suspended");
+}
+
+static void gsi_resume(struct usb_function *f)
+{
+	struct f_gsi *gsi = func_to_gsi(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+
+	log_event_dbg("%s", __func__);
+
+	/*
+	 * If the function is in USB3 Function Suspend state, resume is
+	 * canceled. In this case resume is done by a Function Resume request.
+	 */
+	if ((cdev->gadget->speed == USB_SPEED_SUPER) &&
+		f->func_is_suspended)
+		return;
+
+	if (gsi->c_port.notify && !gsi->c_port.notify->desc)
+		config_ep_by_speed(cdev->gadget, f, gsi->c_port.notify);
+
+	/* Check any pending cpkt, and queue immediately on resume */
+	gsi_ctrl_send_notification(gsi);
+
+	/*
+	 * Linux host does not send RNDIS_MSG_INIT or non-zero
+	 * RNDIS_MESSAGE_PACKET_FILTER after performing bus resume.
+	 * Trigger state machine explicitly on resume.
+	 */
+	if (gsi->prot_id == IPA_USB_RNDIS &&
+			!usb_gsi_remote_wakeup_allowed(f))
+		rndis_flow_control(gsi->params, false);
+
+	post_event(&gsi->d_port, EVT_RESUMED);
+	queue_work(gsi->d_port.ipa_usb_wq, &gsi->d_port.usb_ipa_w);
+
+	log_event_dbg("%s: completed", __func__);
+}
+
+static int gsi_func_suspend(struct usb_function *f, u8 options)
+{
+	bool func_wakeup_allowed;
+
+	log_event_dbg("func susp %u cmd for %s",
+		options, f->name ? f->name : "");
+
+	func_wakeup_allowed =
+		((options & FUNC_SUSPEND_OPT_RW_EN_MASK) != 0);
+
+	if (options & FUNC_SUSPEND_OPT_SUSP_MASK) {
+		f->func_wakeup_allowed = func_wakeup_allowed;
+		if (!f->func_is_suspended) {
+			gsi_suspend(f);
+			f->func_is_suspended = true;
+		}
+	} else {
+		if (f->func_is_suspended) {
+			f->func_is_suspended = false;
+			gsi_resume(f);
+		}
+		f->func_wakeup_allowed = func_wakeup_allowed;
+	}
+
+	return 0;
+}
+
+static int gsi_update_function_bind_params(struct f_gsi *gsi,
+	struct usb_composite_dev *cdev,
+	struct gsi_function_bind_info *info)
+{
+	struct usb_ep *ep;
+	struct usb_cdc_notification *event;
+	struct usb_function *f = &gsi->function;
+	int status;
+
+	/* maybe allocate device-global string IDs */
+	if (info->string_defs[0].id != 0)
+		goto skip_string_id_alloc;
+
+	if (info->ctrl_str_idx >= 0 && info->ctrl_desc) {
+		/* ctrl interface label */
+		status = usb_string_id(cdev);
+		if (status < 0)
+			return status;
+		info->string_defs[info->ctrl_str_idx].id = status;
+		info->ctrl_desc->iInterface = status;
+	}
+
+	if (info->data_str_idx >= 0 && info->data_desc) {
+		/* data interface label */
+		status = usb_string_id(cdev);
+		if (status < 0)
+			return status;
+		info->string_defs[info->data_str_idx].id = status;
+		info->data_desc->iInterface = status;
+	}
+
+	if (info->iad_str_idx >= 0 && info->iad_desc) {
+		/* IAD iFunction label */
+		status = usb_string_id(cdev);
+		if (status < 0)
+			return status;
+		info->string_defs[info->iad_str_idx].id = status;
+		info->iad_desc->iFunction = status;
+	}
+
+	if (info->mac_str_idx >= 0 && info->cdc_eth_desc) {
+		/* IAD iFunction label */
+		status = usb_string_id(cdev);
+		if (status < 0)
+			return status;
+		info->string_defs[info->mac_str_idx].id = status;
+		info->cdc_eth_desc->iMACAddress = status;
+	}
+
+skip_string_id_alloc:
+	if (info->ctrl_desc)
+		info->ctrl_desc->bInterfaceNumber = gsi->ctrl_id;
+
+	if (info->iad_desc)
+		info->iad_desc->bFirstInterface = gsi->ctrl_id;
+
+	if (info->union_desc) {
+		info->union_desc->bMasterInterface0 = gsi->ctrl_id;
+		info->union_desc->bSlaveInterface0 = gsi->data_id;
+	}
+
+	if (info->data_desc)
+		info->data_desc->bInterfaceNumber = gsi->data_id;
+
+	if (info->data_nop_desc)
+		info->data_nop_desc->bInterfaceNumber = gsi->data_id;
+
+	/* allocate instance-specific endpoints */
+	if (info->fs_in_desc) {
+		ep = usb_ep_autoconfig_by_name(cdev->gadget,
+				info->fs_in_desc, info->in_epname);
+		if (!ep)
+			goto fail;
+		gsi->d_port.in_ep = ep;
+		msm_ep_config(gsi->d_port.in_ep);
+		ep->driver_data = cdev;	/* claim */
+	}
+
+	if (info->fs_out_desc) {
+		ep = usb_ep_autoconfig_by_name(cdev->gadget,
+				info->fs_out_desc, info->out_epname);
+		if (!ep)
+			goto fail;
+		gsi->d_port.out_ep = ep;
+		msm_ep_config(gsi->d_port.out_ep);
+		ep->driver_data = cdev;	/* claim */
+	}
+
+	if (info->fs_notify_desc) {
+		ep = usb_ep_autoconfig(cdev->gadget, info->fs_notify_desc);
+		if (!ep)
+			goto fail;
+		gsi->c_port.notify = ep;
+		ep->driver_data = cdev;	/* claim */
+
+		/* allocate notification request and buffer */
+		gsi->c_port.notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+		if (!gsi->c_port.notify_req)
+			goto fail;
+
+		gsi->c_port.notify_req->buf =
+			kmalloc(info->notify_buf_len, GFP_KERNEL);
+		if (!gsi->c_port.notify_req->buf)
+			goto fail;
+
+		gsi->c_port.notify_req->length = info->notify_buf_len;
+		gsi->c_port.notify_req->context = gsi;
+		gsi->c_port.notify_req->complete =
+				gsi_ctrl_notify_resp_complete;
+		event = gsi->c_port.notify_req->buf;
+		event->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+				| USB_RECIP_INTERFACE;
+
+		if (gsi->ctrl_id == -ENODEV)
+			event->wIndex = cpu_to_le16(gsi->data_id);
+		else
+			event->wIndex = cpu_to_le16(gsi->ctrl_id);
+
+		event->wLength = cpu_to_le16(0);
+	}
+
+	gsi->d_port.in_request.buf_len = info->in_req_buf_len;
+	gsi->d_port.in_request.num_bufs = info->in_req_num_buf;
+	if (gsi->d_port.out_ep) {
+		gsi->d_port.out_request.buf_len = info->out_req_buf_len;
+		gsi->d_port.out_request.num_bufs = info->out_req_num_buf;
+	}
+
+	/* Initialize event queue */
+	spin_lock_init(&gsi->d_port.evt_q.q_lock);
+	gsi->d_port.evt_q.head = gsi->d_port.evt_q.tail = MAXQUEUELEN - 1;
+
+	/* copy descriptors, and track endpoint copies */
+	f->fs_descriptors = usb_copy_descriptors(info->fs_desc_hdr);
+	if (!gsi->function.fs_descriptors)
+		goto fail;
+
+	/* support all relevant hardware speeds... we expect that when
+	 * hardware is dual speed, all bulk-capable endpoints work at
+	 * both speeds
+	 */
+	if (gadget_is_dualspeed(cdev->gadget)) {
+		if (info->fs_in_desc)
+			info->hs_in_desc->bEndpointAddress =
+					info->fs_in_desc->bEndpointAddress;
+		if (info->fs_out_desc)
+			info->hs_out_desc->bEndpointAddress =
+					info->fs_out_desc->bEndpointAddress;
+		if (info->fs_notify_desc)
+			info->hs_notify_desc->bEndpointAddress =
+					info->fs_notify_desc->bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->hs_descriptors = usb_copy_descriptors(info->hs_desc_hdr);
+		if (!f->hs_descriptors)
+			goto fail;
+	}
+
+	if (gadget_is_superspeed(cdev->gadget)) {
+		if (info->fs_in_desc)
+			info->ss_in_desc->bEndpointAddress =
+					info->fs_in_desc->bEndpointAddress;
+
+		if (info->fs_out_desc)
+			info->ss_out_desc->bEndpointAddress =
+					info->fs_out_desc->bEndpointAddress;
+		if (info->fs_notify_desc)
+			info->ss_notify_desc->bEndpointAddress =
+					info->fs_notify_desc->bEndpointAddress;
+
+		/* copy descriptors, and track endpoint copies */
+		f->ss_descriptors = usb_copy_descriptors(info->ss_desc_hdr);
+		if (!f->ss_descriptors)
+			goto fail;
+	}
+
+	return 0;
+
+fail:
+	if (gadget_is_superspeed(cdev->gadget) && f->ss_descriptors)
+		usb_free_descriptors(f->ss_descriptors);
+	if (gadget_is_dualspeed(cdev->gadget) && f->hs_descriptors)
+		usb_free_descriptors(f->hs_descriptors);
+	if (f->fs_descriptors)
+		usb_free_descriptors(f->fs_descriptors);
+	if (gsi->c_port.notify_req) {
+		kfree(gsi->c_port.notify_req->buf);
+		usb_ep_free_request(gsi->c_port.notify, gsi->c_port.notify_req);
+	}
+	/* we might as well release our claims on endpoints */
+	if (gsi->c_port.notify)
+		gsi->c_port.notify->driver_data = NULL;
+	if (gsi->d_port.out_ep && gsi->d_port.out_ep->desc)
+		gsi->d_port.out_ep->driver_data = NULL;
+	if (gsi->d_port.in_ep && gsi->d_port.in_ep->desc)
+		gsi->d_port.in_ep->driver_data = NULL;
+	log_event_err("%s: bind failed for %s", __func__, f->name);
+	return -ENOMEM;
+}
+
+static void ipa_ready_callback(void *user_data)
+{
+	struct f_gsi *gsi = user_data;
+
+	log_event_info("%s: ipa is ready\n", __func__);
+
+	gsi->d_port.ipa_ready = true;
+	wake_up_interruptible(&gsi->d_port.wait_for_ipa_ready);
+}
+
+static int gsi_bind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct usb_composite_dev *cdev = c->cdev;
+	struct gsi_function_bind_info info = {0};
+	struct f_gsi *gsi = func_to_gsi(f);
+	struct rndis_params *params;
+	int status;
+
+	if (gsi->prot_id == IPA_USB_RMNET ||
+		gsi->prot_id == IPA_USB_DIAG)
+		gsi->ctrl_id = -ENODEV;
+	else {
+		status = gsi->ctrl_id = usb_interface_id(c, f);
+		if (status < 0)
+			goto fail;
+	}
+
+	status = gsi->data_id = usb_interface_id(c, f);
+	if (status < 0)
+		goto fail;
+
+	switch (gsi->prot_id) {
+	case IPA_USB_RNDIS:
+		info.string_defs = rndis_gsi_string_defs;
+		info.ctrl_desc = &rndis_gsi_control_intf;
+		info.ctrl_str_idx = 0;
+		info.data_desc = &rndis_gsi_data_intf;
+		info.data_str_idx = 1;
+		info.iad_desc = &rndis_gsi_iad_descriptor;
+		info.iad_str_idx = 2;
+		info.union_desc = &rndis_gsi_union_desc;
+		info.fs_in_desc = &rndis_gsi_fs_in_desc;
+		info.fs_out_desc = &rndis_gsi_fs_out_desc;
+		info.fs_notify_desc = &rndis_gsi_fs_notify_desc;
+		info.hs_in_desc = &rndis_gsi_hs_in_desc;
+		info.hs_out_desc = &rndis_gsi_hs_out_desc;
+		info.hs_notify_desc = &rndis_gsi_hs_notify_desc;
+		info.ss_in_desc = &rndis_gsi_ss_in_desc;
+		info.ss_out_desc = &rndis_gsi_ss_out_desc;
+		info.ss_notify_desc = &rndis_gsi_ss_notify_desc;
+		info.fs_desc_hdr = gsi_eth_fs_function;
+		info.hs_desc_hdr = gsi_eth_hs_function;
+		info.ss_desc_hdr = gsi_eth_ss_function;
+		info.in_epname = "gsi-epin";
+		info.out_epname = "gsi-epout";
+		info.in_req_buf_len = GSI_IN_BUFF_SIZE;
+		gsi->d_port.in_aggr_size = GSI_IN_RNDIS_AGGR_SIZE;
+		info.in_req_num_buf = num_in_bufs;
+		gsi->d_port.out_aggr_size = GSI_OUT_AGGR_SIZE;
+		info.out_req_buf_len = GSI_OUT_AGGR_SIZE;
+		info.out_req_num_buf = num_out_bufs;
+		info.notify_buf_len = sizeof(struct usb_cdc_notification);
+
+		params = rndis_register(gsi_rndis_response_available, gsi,
+				gsi_rndis_flow_ctrl_enable);
+		if (IS_ERR(params))
+			goto fail;
+
+		gsi->params = params;
+
+		rndis_set_param_medium(gsi->params, RNDIS_MEDIUM_802_3, 0);
+
+		/* export host's Ethernet address in CDC format */
+		random_ether_addr(gsi->d_port.ipa_init_params.device_ethaddr);
+		random_ether_addr(gsi->d_port.ipa_init_params.host_ethaddr);
+		log_event_dbg("setting host_ethaddr=%pM, device_ethaddr = %pM",
+		gsi->d_port.ipa_init_params.host_ethaddr,
+		gsi->d_port.ipa_init_params.device_ethaddr);
+		memcpy(gsi->ethaddr, &gsi->d_port.ipa_init_params.host_ethaddr,
+				ETH_ALEN);
+		rndis_set_host_mac(gsi->params, gsi->ethaddr);
+
+		if (gsi->manufacturer && gsi->vendorID &&
+			rndis_set_param_vendor(gsi->params, gsi->vendorID,
+				gsi->manufacturer))
+			goto dereg_rndis;
+
+		log_event_dbg("%s: max_pkt_per_xfer : %d", __func__,
+					DEFAULT_MAX_PKT_PER_XFER);
+		rndis_set_max_pkt_xfer(gsi->params, DEFAULT_MAX_PKT_PER_XFER);
+
+		/* In case of aggregated packets QC device will request
+		 * aliment to 4 (2^2).
+		 */
+		log_event_dbg("%s: pkt_alignment_factor : %d", __func__,
+					DEFAULT_PKT_ALIGNMENT_FACTOR);
+		rndis_set_pkt_alignment_factor(gsi->params,
+					DEFAULT_PKT_ALIGNMENT_FACTOR);
+		break;
+	case IPA_USB_MBIM:
+		info.string_defs = mbim_gsi_string_defs;
+		info.ctrl_desc = &mbim_gsi_control_intf;
+		info.ctrl_str_idx = 0;
+		info.data_desc = &mbim_gsi_data_intf;
+		info.data_str_idx = 1;
+		info.data_nop_desc = &mbim_gsi_data_nop_intf;
+		info.iad_desc = &mbim_gsi_iad_desc;
+		info.iad_str_idx = -1;
+		info.union_desc = &mbim_gsi_union_desc;
+		info.fs_in_desc = &mbim_gsi_fs_in_desc;
+		info.fs_out_desc = &mbim_gsi_fs_out_desc;
+		info.fs_notify_desc = &mbim_gsi_fs_notify_desc;
+		info.hs_in_desc = &mbim_gsi_hs_in_desc;
+		info.hs_out_desc = &mbim_gsi_hs_out_desc;
+		info.hs_notify_desc = &mbim_gsi_hs_notify_desc;
+		info.ss_in_desc = &mbim_gsi_ss_in_desc;
+		info.ss_out_desc = &mbim_gsi_ss_out_desc;
+		info.ss_notify_desc = &mbim_gsi_ss_notify_desc;
+		info.fs_desc_hdr = mbim_gsi_fs_function;
+		info.hs_desc_hdr = mbim_gsi_hs_function;
+		info.ss_desc_hdr = mbim_gsi_ss_function;
+		info.in_epname = "gsi-epin";
+		info.out_epname = "gsi-epout";
+		gsi->d_port.in_aggr_size = GSI_IN_MBIM_AGGR_SIZE;
+		info.in_req_buf_len = GSI_IN_MBIM_AGGR_SIZE;
+		info.in_req_num_buf = num_in_bufs;
+		gsi->d_port.out_aggr_size = GSI_OUT_AGGR_SIZE;
+		info.out_req_buf_len = GSI_OUT_MBIM_BUF_LEN;
+		info.out_req_num_buf = num_out_bufs;
+		info.notify_buf_len = sizeof(struct usb_cdc_notification);
+		mbim_gsi_desc.wMaxSegmentSize = cpu_to_le16(0x800);
+
+		/*
+		 * If MBIM is bound in a config other than the first, tell
+		 * Windows about it by returning the num as a string in the
+		 * OS descriptor's subCompatibleID field. Windows only supports
+		 * up to config #4.
+		 */
+		if (c->bConfigurationValue >= 2 &&
+				c->bConfigurationValue <= 4) {
+			log_event_dbg("MBIM in configuration %d",
+					c->bConfigurationValue);
+			mbim_gsi_ext_config_desc.function.subCompatibleID[0] =
+				c->bConfigurationValue + '0';
+		}
+		break;
+	case IPA_USB_RMNET:
+		info.string_defs = rmnet_gsi_string_defs;
+		info.data_desc = &rmnet_gsi_interface_desc;
+		info.data_str_idx = 0;
+		info.fs_in_desc = &rmnet_gsi_fs_in_desc;
+		info.fs_out_desc = &rmnet_gsi_fs_out_desc;
+		info.fs_notify_desc = &rmnet_gsi_fs_notify_desc;
+		info.hs_in_desc = &rmnet_gsi_hs_in_desc;
+		info.hs_out_desc = &rmnet_gsi_hs_out_desc;
+		info.hs_notify_desc = &rmnet_gsi_hs_notify_desc;
+		info.ss_in_desc = &rmnet_gsi_ss_in_desc;
+		info.ss_out_desc = &rmnet_gsi_ss_out_desc;
+		info.ss_notify_desc = &rmnet_gsi_ss_notify_desc;
+		info.fs_desc_hdr = rmnet_gsi_fs_function;
+		info.hs_desc_hdr = rmnet_gsi_hs_function;
+		info.ss_desc_hdr = rmnet_gsi_ss_function;
+		info.in_epname = "gsi-epin";
+		info.out_epname = "gsi-epout";
+		gsi->d_port.in_aggr_size = GSI_IN_RMNET_AGGR_SIZE;
+		info.in_req_buf_len = GSI_IN_BUFF_SIZE;
+		info.in_req_num_buf = num_in_bufs;
+		gsi->d_port.out_aggr_size = GSI_OUT_AGGR_SIZE;
+		info.out_req_buf_len = GSI_OUT_RMNET_BUF_LEN;
+		info.out_req_num_buf = num_out_bufs;
+		info.notify_buf_len = sizeof(struct usb_cdc_notification);
+		break;
+	case IPA_USB_ECM:
+		info.string_defs = ecm_gsi_string_defs;
+		info.ctrl_desc = &ecm_gsi_control_intf;
+		info.ctrl_str_idx = 0;
+		info.data_desc = &ecm_gsi_data_intf;
+		info.data_str_idx = 2;
+		info.data_nop_desc = &ecm_gsi_data_nop_intf;
+		info.cdc_eth_desc = &ecm_gsi_desc;
+		info.mac_str_idx = 1;
+		info.union_desc = &ecm_gsi_union_desc;
+		info.fs_in_desc = &ecm_gsi_fs_in_desc;
+		info.fs_out_desc = &ecm_gsi_fs_out_desc;
+		info.fs_notify_desc = &ecm_gsi_fs_notify_desc;
+		info.hs_in_desc = &ecm_gsi_hs_in_desc;
+		info.hs_out_desc = &ecm_gsi_hs_out_desc;
+		info.hs_notify_desc = &ecm_gsi_hs_notify_desc;
+		info.ss_in_desc = &ecm_gsi_ss_in_desc;
+		info.ss_out_desc = &ecm_gsi_ss_out_desc;
+		info.ss_notify_desc = &ecm_gsi_ss_notify_desc;
+		info.fs_desc_hdr = ecm_gsi_fs_function;
+		info.hs_desc_hdr = ecm_gsi_hs_function;
+		info.ss_desc_hdr = ecm_gsi_ss_function;
+		info.in_epname = "gsi-epin";
+		info.out_epname = "gsi-epout";
+		gsi->d_port.in_aggr_size = GSI_ECM_AGGR_SIZE;
+		info.in_req_buf_len = GSI_IN_BUFF_SIZE;
+		info.in_req_num_buf = num_in_bufs;
+		gsi->d_port.out_aggr_size = GSI_ECM_AGGR_SIZE;
+		info.out_req_buf_len = GSI_OUT_ECM_BUF_LEN;
+		info.out_req_num_buf = GSI_ECM_NUM_OUT_BUFFERS;
+		info.notify_buf_len = GSI_CTRL_NOTIFY_BUFF_LEN;
+
+		/* export host's Ethernet address in CDC format */
+		random_ether_addr(gsi->d_port.ipa_init_params.device_ethaddr);
+		random_ether_addr(gsi->d_port.ipa_init_params.host_ethaddr);
+		log_event_dbg("setting host_ethaddr=%pM, device_ethaddr = %pM",
+		gsi->d_port.ipa_init_params.host_ethaddr,
+		gsi->d_port.ipa_init_params.device_ethaddr);
+
+		snprintf(gsi->ethaddr, sizeof(gsi->ethaddr),
+		"%02X%02X%02X%02X%02X%02X",
+		gsi->d_port.ipa_init_params.host_ethaddr[0],
+		gsi->d_port.ipa_init_params.host_ethaddr[1],
+		gsi->d_port.ipa_init_params.host_ethaddr[2],
+		gsi->d_port.ipa_init_params.host_ethaddr[3],
+		gsi->d_port.ipa_init_params.host_ethaddr[4],
+		gsi->d_port.ipa_init_params.host_ethaddr[5]);
+		info.string_defs[1].s = gsi->ethaddr;
+		break;
+	case IPA_USB_DIAG:
+		info.string_defs = qdss_gsi_string_defs;
+		info.data_desc = &qdss_gsi_data_intf_desc;
+		info.data_str_idx = 0;
+		info.fs_in_desc = &qdss_gsi_hs_data_desc;
+		info.hs_in_desc = &qdss_gsi_hs_data_desc;
+		info.ss_in_desc = &qdss_gsi_ss_data_desc;
+		info.fs_desc_hdr = qdss_gsi_hs_data_only_desc;
+		info.hs_desc_hdr = qdss_gsi_hs_data_only_desc;
+		info.ss_desc_hdr = qdss_gsi_ss_data_only_desc;
+		info.in_epname = "gsi-epin";
+		info.out_epname = "";
+		info.in_req_buf_len = 16384;
+		info.in_req_num_buf = num_in_bufs;
+		info.notify_buf_len = sizeof(struct usb_cdc_notification);
+		break;
+	default:
+		log_event_err("%s: Invalid prot id %d", __func__,
+							gsi->prot_id);
+		return -EINVAL;
+	}
+
+	status = gsi_update_function_bind_params(gsi, cdev, &info);
+	if (status)
+		goto dereg_rndis;
+
+	status = ipa_register_ipa_ready_cb(ipa_ready_callback, gsi);
+	if (!status) {
+		log_event_info("%s: ipa is not ready", __func__);
+		status = wait_event_interruptible_timeout(
+			gsi->d_port.wait_for_ipa_ready, gsi->d_port.ipa_ready,
+			msecs_to_jiffies(GSI_IPA_READY_TIMEOUT));
+		if (!status) {
+			log_event_err("%s: ipa ready timeout", __func__);
+			status = -ETIMEDOUT;
+			goto dereg_rndis;
+		}
+	}
+
+	gsi->d_port.ipa_usb_notify_cb = ipa_usb_notify_cb;
+	status = ipa_usb_init_teth_prot(gsi->prot_id,
+		&gsi->d_port.ipa_init_params, gsi->d_port.ipa_usb_notify_cb,
+		gsi);
+	if (status) {
+		log_event_err("%s: failed to init teth prot %d",
+						__func__, gsi->prot_id);
+		goto dereg_rndis;
+	}
+
+	gsi->d_port.sm_state = STATE_INITIALIZED;
+
+	DBG(cdev, "%s: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+			f->name,
+			gadget_is_superspeed(c->cdev->gadget) ? "super" :
+			gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+			gsi->d_port.in_ep->name, gsi->d_port.out_ep->name,
+			gsi->c_port.notify->name);
+	return 0;
+
+dereg_rndis:
+	rndis_deregister(gsi->params);
+fail:
+	return status;
+}
+
+static void gsi_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+	struct f_gsi *gsi = func_to_gsi(f);
+
+	/*
+	 * Use drain_workqueue to accomplish below conditions:
+	 * 1. Make sure that any running work completed
+	 * 2. Make sure to wait until all pending work completed i.e. workqueue
+	 * is not having any pending work.
+	 * Above conditions are making sure that ipa_usb_deinit_teth_prot()
+	 * with ipa driver shall not fail due to unexpected state.
+	 */
+	drain_workqueue(gsi->d_port.ipa_usb_wq);
+	ipa_usb_deinit_teth_prot(gsi->prot_id);
+
+	if (gsi->prot_id == IPA_USB_RNDIS) {
+		gsi->d_port.sm_state = STATE_UNINITIALIZED;
+		rndis_deregister(gsi->params);
+	}
+
+	if (gsi->prot_id == IPA_USB_MBIM)
+		mbim_gsi_ext_config_desc.function.subCompatibleID[0] = 0;
+
+	if (gadget_is_superspeed(c->cdev->gadget)) {
+		usb_free_descriptors(f->ss_descriptors);
+		f->ss_descriptors = NULL;
+	}
+	if (gadget_is_dualspeed(c->cdev->gadget)) {
+		usb_free_descriptors(f->hs_descriptors);
+		f->hs_descriptors = NULL;
+	}
+	usb_free_descriptors(f->fs_descriptors);
+	f->fs_descriptors = NULL;
+
+	if (gsi->c_port.notify) {
+		kfree(gsi->c_port.notify_req->buf);
+		usb_ep_free_request(gsi->c_port.notify, gsi->c_port.notify_req);
+	}
+}
+
+
+static void gsi_free_func(struct usb_function *f)
+{
+	pr_debug("%s\n", __func__);
+}
+
+static int gsi_bind_config(struct f_gsi *gsi)
+{
+	int status = 0;
+	enum ipa_usb_teth_prot prot_id = gsi->prot_id;
+
+	log_event_dbg("%s: prot id %d", __func__, prot_id);
+
+	switch (prot_id) {
+	case IPA_USB_RNDIS:
+		gsi->function.name = "rndis";
+		gsi->function.strings = rndis_gsi_strings;
+		break;
+	case IPA_USB_ECM:
+		gsi->function.name = "cdc_ethernet";
+		gsi->function.strings = ecm_gsi_strings;
+		break;
+	case IPA_USB_RMNET:
+		gsi->function.name = "rmnet";
+		gsi->function.strings = rmnet_gsi_strings;
+		break;
+	case IPA_USB_MBIM:
+		gsi->function.name = "mbim";
+		gsi->function.strings = mbim_gsi_strings;
+		break;
+	case IPA_USB_DIAG:
+		gsi->function.name = "dpl";
+		gsi->function.strings = qdss_gsi_strings;
+		break;
+	default:
+		log_event_err("%s: invalid prot id %d", __func__, prot_id);
+		return -EINVAL;
+	}
+
+	/* descriptors are per-instance copies */
+	gsi->function.bind = gsi_bind;
+	gsi->function.unbind = gsi_unbind;
+	gsi->function.set_alt = gsi_set_alt;
+	gsi->function.get_alt = gsi_get_alt;
+	gsi->function.setup = gsi_setup;
+	gsi->function.disable = gsi_disable;
+	gsi->function.free_func = gsi_free_func;
+	gsi->function.suspend = gsi_suspend;
+	gsi->function.func_suspend = gsi_func_suspend;
+	gsi->function.resume = gsi_resume;
+
+	INIT_WORK(&gsi->d_port.usb_ipa_w, ipa_work_handler);
+
+	return status;
+}
+
+static struct f_gsi *gsi_function_init(enum ipa_usb_teth_prot prot_id)
+{
+	struct f_gsi *gsi;
+	int ret = 0;
+
+	if (prot_id >= IPA_USB_MAX_TETH_PROT_SIZE) {
+		log_event_err("%s: invalid prot id %d", __func__, prot_id);
+		ret = -EINVAL;
+		goto error;
+	}
+
+	gsi = kzalloc(sizeof(*gsi), GFP_KERNEL);
+	if (!gsi) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	spin_lock_init(&gsi->d_port.lock);
+
+	init_waitqueue_head(&gsi->d_port.wait_for_ipa_ready);
+
+	gsi->d_port.in_channel_handle = -EINVAL;
+	gsi->d_port.out_channel_handle = -EINVAL;
+
+	gsi->prot_id = prot_id;
+
+	gsi->d_port.ipa_usb_wq = ipa_usb_wq;
+
+	ret = gsi_function_ctrl_port_init(gsi);
+	if (ret) {
+		kfree(gsi);
+		goto error;
+	}
+
+	return gsi;
+error:
+	return ERR_PTR(ret);
+}
+
+static void gsi_opts_release(struct config_item *item)
+{
+	struct gsi_opts *opts = to_gsi_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations gsi_item_ops = {
+	.release	= gsi_opts_release,
+};
+
+static ssize_t gsi_info_show(struct config_item *item, char *page)
+{
+	struct ipa_usb_xdci_chan_params *ipa_chnl_params;
+	struct ipa_usb_xdci_connect_params *con_pms;
+	struct f_gsi *gsi = to_gsi_opts(item)->gsi;
+	int ret, j = 0;
+	unsigned int len = 0;
+	char *buf;
+
+	buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	if (gsi && atomic_read(&gsi->connected)) {
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+			"Info: Prot_id:%d\n", gsi->prot_id);
+		ipa_chnl_params = &gsi->d_port.ipa_in_channel_params;
+		con_pms = &gsi->d_port.ipa_conn_pms;
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%55s\n",
+		"==================================================");
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10s\n", "Ctrl Name: ", gsi->c_port.name);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Online: ",
+				gsi->c_port.ctrl_online.counter);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Open: ",
+				gsi->c_port.is_open);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Host to Modem: ",
+				gsi->c_port.host_to_modem);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Modem to Host: ",
+				gsi->c_port.modem_to_host);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Cpd to Modem: ",
+				gsi->c_port.copied_to_modem);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Cpd From Modem: ",
+				gsi->c_port.copied_from_modem);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Ctrl Pkt Drops: ",
+				gsi->c_port.cpkt_drop_cnt);
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%25s\n",
+		"==============");
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Protocol ID: ", gsi->prot_id);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "SM State: ", gsi->d_port.sm_state);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "IN XferRscIndex: ",
+				gsi->d_port.in_xfer_rsc_index);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10d\n", "IN Chnl Hdl: ",
+				gsi->d_port.in_channel_handle);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "IN Chnl Dbl Addr: ",
+				gsi->d_port.in_db_reg_phs_addr_lsb);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "IN TRB Ring Len: ",
+				ipa_chnl_params->xfer_ring_len);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "IN TRB Base Addr: ", (unsigned int)
+			ipa_chnl_params->xfer_ring_base_addr);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "GEVENTCNTLO IN Addr: ",
+			ipa_chnl_params->gevntcount_low_addr);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "DEPCMDLO IN Addr: ",
+		ipa_chnl_params->xfer_scratch.depcmd_low_addr);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "IN LastTRB Addr Off: ",
+		ipa_chnl_params->xfer_scratch.last_trb_addr_iova);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "IN Buffer Size: ",
+		ipa_chnl_params->xfer_scratch.const_buffer_size);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "IN/DL Aggr Size: ",
+		con_pms->teth_prot_params.max_xfer_size_bytes_to_host);
+
+		ipa_chnl_params = &gsi->d_port.ipa_out_channel_params;
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%25s\n",
+		"==============");
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "OUT XferRscIndex: ",
+			gsi->d_port.out_xfer_rsc_index);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10d\n", "OUT Channel Hdl: ",
+			gsi->d_port.out_channel_handle);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "OUT Channel Dbl Addr: ",
+			gsi->d_port.out_db_reg_phs_addr_lsb);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "OUT TRB Ring Len: ",
+			ipa_chnl_params->xfer_ring_len);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "OUT TRB Base Addr: ", (unsigned int)
+			ipa_chnl_params->xfer_ring_base_addr);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "GEVENTCNTLO OUT Addr: ",
+			ipa_chnl_params->gevntcount_low_addr);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "DEPCMDLO OUT Addr: ",
+			ipa_chnl_params->xfer_scratch.depcmd_low_addr);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10x\n", "OUT LastTRB Addr Off: ",
+		ipa_chnl_params->xfer_scratch.last_trb_addr_iova);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "OUT Buffer Size: ",
+		ipa_chnl_params->xfer_scratch.const_buffer_size);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "OUT/UL Aggr Size: ",
+		con_pms->teth_prot_params.max_xfer_size_bytes_to_dev);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "OUT/UL Packets to dev: ",
+		con_pms->teth_prot_params.max_packet_number_to_dev);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Net_ready_trigger:",
+		gsi->d_port.net_ready_trigger);
+		len += scnprintf(buf + len, PAGE_SIZE - len, "%25s\n",
+		"USB Bus Events");
+		for (j = 0; j < MAXQUEUELEN; j++)
+			len += scnprintf(buf + len, PAGE_SIZE - len,
+				"%d\t", gsi->d_port.evt_q.event[j]);
+		len += scnprintf(buf + len, PAGE_SIZE - len, "\n");
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Eventq head: ",
+				gsi->d_port.evt_q.head);
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+		"%25s %10u\n", "Eventq tail: ",
+				gsi->d_port.evt_q.tail);
+	}
+
+	if (len > PAGE_SIZE)
+		len = PAGE_SIZE;
+
+	ret = scnprintf(page, len, buf);
+
+	kfree(buf);
+
+	return ret;
+}
+
+CONFIGFS_ATTR_RO(gsi_, info);
+
+static struct configfs_attribute *gsi_attrs[] = {
+	&gsi_attr_info,
+	NULL,
+};
+
+static struct config_item_type gsi_func_type = {
+	.ct_item_ops	= &gsi_item_ops,
+	.ct_attrs	= gsi_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static int gsi_set_inst_name(struct usb_function_instance *fi,
+	const char *name)
+{
+	int ret, name_len;
+	struct f_gsi *gsi;
+	struct gsi_opts *opts = container_of(fi, struct gsi_opts, func_inst);
+
+	name_len = strlen(name) + 1;
+	if (name_len > MAX_INST_NAME_LEN)
+		return -ENAMETOOLONG;
+
+	ret = name_to_prot_id(name);
+	if (ret < 0) {
+		pr_err("%s: failed to find prot id for %s instance\n",
+		__func__, name);
+		return -EINVAL;
+	}
+
+	gsi = gsi_function_init(ret);
+	if (IS_ERR(gsi))
+		return PTR_ERR(gsi);
+
+	opts->gsi = gsi;
+
+	return 0;
+}
+
+static void gsi_free_inst(struct usb_function_instance *f)
+{
+	struct gsi_opts *opts = container_of(f, struct gsi_opts, func_inst);
+
+	if (opts->gsi->c_port.ctrl_device.fops)
+		misc_deregister(&opts->gsi->c_port.ctrl_device);
+
+	kfree(opts->gsi);
+	kfree(opts);
+}
+
+static struct usb_function_instance *gsi_alloc_inst(void)
+{
+	struct gsi_opts *opts;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	opts->func_inst.set_inst_name = gsi_set_inst_name;
+	opts->func_inst.free_func_inst = gsi_free_inst;
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &gsi_func_type);
+
+	return &opts->func_inst;
+}
+
+static struct usb_function *gsi_alloc(struct usb_function_instance *fi)
+{
+	struct gsi_opts *opts;
+	int ret;
+
+	opts = container_of(fi, struct gsi_opts, func_inst);
+
+	ret = gsi_bind_config(opts->gsi);
+	if (ret)
+		return ERR_PTR(ret);
+
+	return &opts->gsi->function;
+}
+
+DECLARE_USB_FUNCTION(gsi, gsi_alloc_inst, gsi_alloc);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("GSI function driver");
+
+static int fgsi_init(void)
+{
+	ipa_usb_wq = alloc_workqueue("k_ipa_usb",
+				WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_FREEZABLE, 1);
+	if (!ipa_usb_wq) {
+		log_event_err("Failed to create workqueue for IPA");
+		return -ENOMEM;
+	}
+
+	return usb_function_register(&gsiusb_func);
+}
+module_init(fgsi_init);
+
+static void __exit fgsi_exit(void)
+{
+	if (ipa_usb_wq)
+		destroy_workqueue(ipa_usb_wq);
+	usb_function_unregister(&gsiusb_func);
+}
+module_exit(fgsi_exit);
diff --git a/drivers/usb/gadget/function/f_gsi.h b/drivers/usb/gadget/function/f_gsi.h
new file mode 100644
index 0000000..9e2941c
--- /dev/null
+++ b/drivers/usb/gadget/function/f_gsi.h
@@ -0,0 +1,1372 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details
+ */
+
+#ifndef _F_GSI_H
+#define _F_GSI_H
+
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/ipa.h>
+#include <uapi/linux/usb/cdc.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/usb_ctrl_qti.h>
+#include <linux/etherdevice.h>
+#include <linux/debugfs.h>
+#include <linux/ipa_usb.h>
+
+#define GSI_RMNET_CTRL_NAME "rmnet_ctrl"
+#define GSI_MBIM_CTRL_NAME "android_mbim"
+#define GSI_DPL_CTRL_NAME "dpl_ctrl"
+#define GSI_CTRL_NAME_LEN (sizeof(GSI_MBIM_CTRL_NAME)+2)
+#define GSI_MAX_CTRL_PKT_SIZE 4096
+#define GSI_CTRL_DTR (1 << 0)
+
+
+#define GSI_NUM_IN_BUFFERS 15
+#define GSI_IN_BUFF_SIZE 2048
+#define GSI_NUM_OUT_BUFFERS 15
+#define GSI_ECM_NUM_OUT_BUFFERS 31
+#define GSI_OUT_AGGR_SIZE 24576
+
+#define GSI_IN_RNDIS_AGGR_SIZE 9216
+#define GSI_IN_MBIM_AGGR_SIZE 16384
+#define GSI_IN_RMNET_AGGR_SIZE 16384
+#define GSI_ECM_AGGR_SIZE 2048
+
+#define GSI_OUT_MBIM_BUF_LEN 16384
+#define GSI_OUT_RMNET_BUF_LEN 16384
+#define GSI_OUT_ECM_BUF_LEN 2048
+
+#define GSI_IPA_READY_TIMEOUT 5000
+
+#define ETH_ADDR_STR_LEN 14
+
+/* mbin and ecm */
+#define GSI_CTRL_NOTIFY_BUFF_LEN 16
+
+/* default max packets per tarnsfer value */
+#define DEFAULT_MAX_PKT_PER_XFER 15
+
+/* default pkt alignment factor */
+#define DEFAULT_PKT_ALIGNMENT_FACTOR 4
+
+#define GSI_MBIM_IOCTL_MAGIC 'o'
+#define GSI_MBIM_GET_NTB_SIZE  _IOR(GSI_MBIM_IOCTL_MAGIC, 2, u32)
+#define GSI_MBIM_GET_DATAGRAM_COUNT  _IOR(GSI_MBIM_IOCTL_MAGIC, 3, u16)
+#define GSI_MBIM_EP_LOOKUP _IOR(GSI_MBIM_IOCTL_MAGIC, 4, struct ep_info)
+#define GSI_MBIM_DATA_EP_TYPE_HSUSB 0x2
+/* ID for Microsoft OS String */
+#define GSI_MBIM_OS_STRING_ID 0xEE
+
+#define EVT_NONE			0
+#define EVT_UNINITIALIZED		1
+#define EVT_INITIALIZED			2
+#define EVT_CONNECT_IN_PROGRESS		3
+#define EVT_CONNECTED			4
+#define EVT_HOST_NRDY			5
+#define EVT_HOST_READY			6
+#define EVT_DISCONNECTED		7
+#define	EVT_SUSPEND			8
+#define	EVT_IPA_SUSPEND			9
+#define	EVT_RESUMED			10
+
+enum connection_state {
+	STATE_UNINITIALIZED,
+	STATE_INITIALIZED,
+	STATE_CONNECT_IN_PROGRESS,
+	STATE_CONNECTED,
+	STATE_DISCONNECTED,
+	STATE_SUSPEND_IN_PROGRESS,
+	STATE_SUSPENDED
+};
+
+enum gsi_ctrl_notify_state {
+	GSI_CTRL_NOTIFY_NONE,
+	GSI_CTRL_NOTIFY_CONNECT,
+	GSI_CTRL_NOTIFY_SPEED,
+	GSI_CTRL_NOTIFY_OFFLINE,
+	GSI_CTRL_NOTIFY_RESPONSE_AVAILABLE,
+};
+
+#define MAXQUEUELEN 128
+struct event_queue {
+	u8 event[MAXQUEUELEN];
+	u8 head, tail;
+	spinlock_t q_lock;
+};
+
+struct gsi_ntb_info {
+	u32	ntb_input_size;
+	u16	ntb_max_datagrams;
+	u16	reserved;
+};
+
+struct gsi_ctrl_pkt {
+	void				*buf;
+	int				len;
+	enum gsi_ctrl_notify_state	type;
+	struct list_head		list;
+};
+
+struct gsi_function_bind_info {
+	struct usb_string *string_defs;
+	int ctrl_str_idx;
+	int data_str_idx;
+	int iad_str_idx;
+	int mac_str_idx;
+	struct usb_interface_descriptor *ctrl_desc;
+	struct usb_interface_descriptor *data_desc;
+	struct usb_interface_assoc_descriptor *iad_desc;
+	struct usb_cdc_ether_desc *cdc_eth_desc;
+	struct usb_cdc_union_desc *union_desc;
+	struct usb_interface_descriptor *data_nop_desc;
+	struct usb_endpoint_descriptor *fs_in_desc;
+	struct usb_endpoint_descriptor *fs_out_desc;
+	struct usb_endpoint_descriptor *fs_notify_desc;
+	struct usb_endpoint_descriptor *hs_in_desc;
+	struct usb_endpoint_descriptor *hs_out_desc;
+	struct usb_endpoint_descriptor *hs_notify_desc;
+	struct usb_endpoint_descriptor *ss_in_desc;
+	struct usb_endpoint_descriptor *ss_out_desc;
+	struct usb_endpoint_descriptor *ss_notify_desc;
+
+	struct usb_descriptor_header **fs_desc_hdr;
+	struct usb_descriptor_header **hs_desc_hdr;
+	struct usb_descriptor_header **ss_desc_hdr;
+	const char *in_epname;
+	const char *out_epname;
+
+	u32 in_req_buf_len;
+	u32 in_req_num_buf;
+	u32 out_req_buf_len;
+	u32 out_req_num_buf;
+	u32 notify_buf_len;
+};
+
+struct gsi_ctrl_port {
+	char name[GSI_CTRL_NAME_LEN];
+	struct miscdevice ctrl_device;
+
+	struct usb_ep *notify;
+	struct usb_request *notify_req;
+	bool notify_req_queued;
+
+	atomic_t ctrl_online;
+
+	bool is_open;
+
+	wait_queue_head_t read_wq;
+
+	struct list_head cpkt_req_q;
+	struct list_head cpkt_resp_q;
+	unsigned long cpkts_len;
+
+	spinlock_t lock;
+
+	int ipa_cons_clnt_hdl;
+	int ipa_prod_clnt_hdl;
+
+	unsigned int host_to_modem;
+	unsigned int copied_to_modem;
+	unsigned int copied_from_modem;
+	unsigned int modem_to_host;
+	unsigned int cpkt_drop_cnt;
+	unsigned int get_encap_cnt;
+};
+
+struct gsi_data_port {
+	struct usb_ep *in_ep;
+	struct usb_ep *out_ep;
+	struct usb_gsi_request in_request;
+	struct usb_gsi_request out_request;
+	struct usb_gadget *gadget;
+	int (*ipa_usb_notify_cb)(enum ipa_usb_notify_event, void *driver_data);
+	struct ipa_usb_teth_params ipa_init_params;
+	int in_channel_handle;
+	int out_channel_handle;
+	u32 in_db_reg_phs_addr_lsb;
+	u32 in_db_reg_phs_addr_msb;
+	u32 out_db_reg_phs_addr_lsb;
+	u32 out_db_reg_phs_addr_msb;
+	u32 in_xfer_rsc_index;
+	u32 out_xfer_rsc_index;
+	u16 in_last_trb_addr;
+	u16 cdc_filter;
+	u32 in_aggr_size;
+	u32 out_aggr_size;
+
+	bool ipa_ready;
+	bool net_ready_trigger;
+	struct gsi_ntb_info ntb_info;
+
+	spinlock_t lock;
+
+	struct work_struct usb_ipa_w;
+	struct workqueue_struct *ipa_usb_wq;
+	enum connection_state sm_state;
+	struct event_queue evt_q;
+	wait_queue_head_t wait_for_ipa_ready;
+
+	/* Track these for debugfs */
+	struct ipa_usb_xdci_chan_params ipa_in_channel_params;
+	struct ipa_usb_xdci_chan_params ipa_out_channel_params;
+	struct ipa_usb_xdci_connect_params ipa_conn_pms;
+};
+
+struct f_gsi {
+	struct usb_function function;
+	enum ipa_usb_teth_prot prot_id;
+	int ctrl_id;
+	int data_id;
+	u32 vendorID;
+	u8 ethaddr[ETH_ADDR_STR_LEN];
+	const char *manufacturer;
+	struct rndis_params *params;
+	atomic_t connected;
+	bool data_interface_up;
+
+	const struct usb_endpoint_descriptor *in_ep_desc_backup;
+	const struct usb_endpoint_descriptor *out_ep_desc_backup;
+
+	struct gsi_data_port d_port;
+	struct gsi_ctrl_port c_port;
+};
+
+static inline struct f_gsi *func_to_gsi(struct usb_function *f)
+{
+	return container_of(f, struct f_gsi, function);
+}
+
+static inline struct f_gsi *d_port_to_gsi(struct gsi_data_port *d)
+{
+	return container_of(d, struct f_gsi, d_port);
+}
+
+static inline struct f_gsi *c_port_to_gsi(struct gsi_ctrl_port *d)
+{
+	return container_of(d, struct f_gsi, c_port);
+}
+
+/* for configfs support */
+#define MAX_INST_NAME_LEN	40
+
+struct gsi_opts {
+	struct usb_function_instance func_inst;
+	struct f_gsi *gsi;
+};
+
+static inline struct gsi_opts *to_gsi_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct gsi_opts,
+			    func_inst.group);
+}
+
+static enum ipa_usb_teth_prot name_to_prot_id(const char *name)
+{
+	if (!name)
+		goto error;
+
+	if (!strncasecmp(name, "rndis", strlen("rndis")))
+		return IPA_USB_RNDIS;
+	if (!strncasecmp(name, "ecm", strlen("ecm")))
+		return IPA_USB_ECM;
+	if (!strncasecmp(name, "rmnet", strlen("rmnet")))
+		return IPA_USB_RMNET;
+	if (!strncasecmp(name, "mbim", strlen("mbim")))
+		return IPA_USB_MBIM;
+	if (!strncasecmp(name, "dpl", strlen("dpl")))
+		return IPA_USB_DIAG;
+
+error:
+	return -EINVAL;
+}
+
+/* device descriptors */
+
+#define LOG2_STATUS_INTERVAL_MSEC 5
+#define MAX_NOTIFY_SIZE sizeof(struct usb_cdc_notification)
+
+/* rmnet device descriptors */
+
+static struct usb_interface_descriptor rmnet_gsi_interface_desc = {
+	.bLength =		USB_DT_INTERFACE_SIZE,
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	3,
+	.bInterfaceClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceSubClass =	USB_CLASS_VENDOR_SPEC,
+	.bInterfaceProtocol =	USB_CLASS_VENDOR_SPEC,
+	/* .iInterface = DYNAMIC */
+};
+
+/* Full speed support */
+static struct usb_endpoint_descriptor rmnet_gsi_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(MAX_NOTIFY_SIZE),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor rmnet_gsi_fs_in_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	cpu_to_le16(64),
+};
+
+static struct usb_endpoint_descriptor rmnet_gsi_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize   =	cpu_to_le16(64),
+};
+
+static struct usb_descriptor_header *rmnet_gsi_fs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_gsi_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_fs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_fs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_fs_out_desc,
+	NULL,
+};
+
+/* High speed support */
+static struct usb_endpoint_descriptor rmnet_gsi_hs_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(MAX_NOTIFY_SIZE),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_endpoint_descriptor rmnet_gsi_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rmnet_gsi_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *rmnet_gsi_hs_function[] = {
+	(struct usb_descriptor_header *) &rmnet_gsi_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_hs_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_hs_in_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_hs_out_desc,
+	NULL,
+};
+
+/* Super speed support */
+static struct usb_endpoint_descriptor rmnet_gsi_ss_notify_desc  = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(MAX_NOTIFY_SIZE),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor rmnet_gsi_ss_notify_comp_desc = {
+	.bLength =		sizeof(rmnet_gsi_ss_notify_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 3 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+	.wBytesPerInterval =	cpu_to_le16(MAX_NOTIFY_SIZE),
+};
+
+static struct usb_endpoint_descriptor rmnet_gsi_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor rmnet_gsi_ss_in_comp_desc = {
+	.bLength =		sizeof(rmnet_gsi_ss_in_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =		2,
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_endpoint_descriptor rmnet_gsi_ss_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor rmnet_gsi_ss_out_comp_desc = {
+	.bLength =		sizeof(rmnet_gsi_ss_out_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =		2,
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_descriptor_header *rmnet_gsi_ss_function[] = {
+	(struct usb_descriptor_header *) &rmnet_gsi_interface_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_ss_notify_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_ss_notify_comp_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_ss_in_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_ss_in_comp_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_ss_out_desc,
+	(struct usb_descriptor_header *) &rmnet_gsi_ss_out_comp_desc,
+	NULL,
+};
+
+/* String descriptors */
+static struct usb_string rmnet_gsi_string_defs[] = {
+	[0].s = "RmNet",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rmnet_gsi_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rmnet_gsi_string_defs,
+};
+
+static struct usb_gadget_strings *rmnet_gsi_strings[] = {
+	&rmnet_gsi_string_table,
+	NULL,
+};
+
+/* rndis device descriptors */
+
+/* interface descriptor: Supports "Wireless" RNDIS; auto-detected by Windows*/
+static struct usb_interface_descriptor rndis_gsi_control_intf = {
+	.bLength =		sizeof(rndis_gsi_control_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	/* status endpoint is optional; this could be patched later */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_WIRELESS_CONTROLLER,
+	.bInterfaceSubClass =   0x01,
+	.bInterfaceProtocol =   0x03,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc rndis_gsi_header_desc = {
+	.bLength =		sizeof(rndis_gsi_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor rndis_gsi_call_mgmt_descriptor = {
+	.bLength =		sizeof(rndis_gsi_call_mgmt_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_CALL_MANAGEMENT_TYPE,
+
+	.bmCapabilities =	0x00,
+	.bDataInterface =	0x01,
+};
+
+static struct usb_cdc_acm_descriptor rndis_gsi_acm_descriptor = {
+	.bLength =		sizeof(rndis_gsi_acm_descriptor),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ACM_TYPE,
+
+	.bmCapabilities =	0x00,
+};
+
+static struct usb_cdc_union_desc rndis_gsi_union_desc = {
+	.bLength =		sizeof(rndis_gsi_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+/* the data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor rndis_gsi_data_intf = {
+	.bLength =		sizeof(rndis_gsi_data_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+/*  Supports "Wireless" RNDIS; auto-detected by Windows */
+static struct usb_interface_assoc_descriptor
+rndis_gsi_iad_descriptor = {
+	.bLength =		sizeof(rndis_gsi_iad_descriptor),
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+	.bFirstInterface =	0, /* XXX, hardcoded */
+	.bInterfaceCount =	2, /* control + data */
+	.bFunctionClass =	USB_CLASS_WIRELESS_CONTROLLER,
+	.bFunctionSubClass =	0x01,
+	.bFunctionProtocol =	0x03,
+	/* .iFunction = DYNAMIC */
+};
+
+/* full speed support: */
+static struct usb_endpoint_descriptor rndis_gsi_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(MAX_NOTIFY_SIZE),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor rndis_gsi_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor rndis_gsi_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *gsi_eth_fs_function[] = {
+	(struct usb_descriptor_header *) &gsi_eth_fs_function,
+	/* control interface matches ACM, not Ethernet */
+	(struct usb_descriptor_header *) &rndis_gsi_control_intf,
+	(struct usb_descriptor_header *) &rndis_gsi_header_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &rndis_gsi_acm_descriptor,
+	(struct usb_descriptor_header *) &rndis_gsi_union_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_fs_notify_desc,
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_gsi_data_intf,
+	(struct usb_descriptor_header *) &rndis_gsi_fs_in_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+static struct usb_endpoint_descriptor rndis_gsi_hs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(MAX_NOTIFY_SIZE),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor rndis_gsi_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor rndis_gsi_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *gsi_eth_hs_function[] = {
+	(struct usb_descriptor_header *) &rndis_gsi_iad_descriptor,
+	/* control interface matches ACM, not Ethernet */
+	(struct usb_descriptor_header *) &rndis_gsi_control_intf,
+	(struct usb_descriptor_header *) &rndis_gsi_header_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &rndis_gsi_acm_descriptor,
+	(struct usb_descriptor_header *) &rndis_gsi_union_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_hs_notify_desc,
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_gsi_data_intf,
+	(struct usb_descriptor_header *) &rndis_gsi_hs_in_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_hs_out_desc,
+	NULL,
+};
+
+/* super speed support: */
+static struct usb_endpoint_descriptor rndis_gsi_ss_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(MAX_NOTIFY_SIZE),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor rndis_gsi_ss_intr_comp_desc = {
+	.bLength =		sizeof(rndis_gsi_ss_intr_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 3 values can be tweaked if necessary */
+	/* .bMaxBurst =		0, */
+	/* .bmAttributes =	0, */
+	.wBytesPerInterval =	cpu_to_le16(MAX_NOTIFY_SIZE),
+};
+
+static struct usb_endpoint_descriptor rndis_gsi_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor rndis_gsi_ss_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor rndis_gsi_ss_bulk_comp_desc = {
+	.bLength =		sizeof(rndis_gsi_ss_bulk_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =		2,
+	/* .bmAttributes =	0, */
+};
+
+static struct usb_descriptor_header *gsi_eth_ss_function[] = {
+	(struct usb_descriptor_header *) &rndis_gsi_iad_descriptor,
+
+	/* control interface matches ACM, not Ethernet */
+	(struct usb_descriptor_header *) &rndis_gsi_control_intf,
+	(struct usb_descriptor_header *) &rndis_gsi_header_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_call_mgmt_descriptor,
+	(struct usb_descriptor_header *) &rndis_gsi_acm_descriptor,
+	(struct usb_descriptor_header *) &rndis_gsi_union_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_ss_notify_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_ss_intr_comp_desc,
+
+	/* data interface has no altsetting */
+	(struct usb_descriptor_header *) &rndis_gsi_data_intf,
+	(struct usb_descriptor_header *) &rndis_gsi_ss_in_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_ss_bulk_comp_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_ss_out_desc,
+	(struct usb_descriptor_header *) &rndis_gsi_ss_bulk_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+static struct usb_string rndis_gsi_string_defs[] = {
+	[0].s = "RNDIS Communications Control",
+	[1].s = "RNDIS Ethernet Data",
+	[2].s = "RNDIS",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings rndis_gsi_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		rndis_gsi_string_defs,
+};
+
+static struct usb_gadget_strings *rndis_gsi_strings[] = {
+	&rndis_gsi_string_table,
+	NULL,
+};
+
+/* mbim device descriptors */
+#define MBIM_NTB_DEFAULT_IN_SIZE	(0x4000)
+
+static struct usb_cdc_ncm_ntb_parameters mbim_gsi_ntb_parameters = {
+	.wLength = sizeof(mbim_gsi_ntb_parameters),
+	.bmNtbFormatsSupported = cpu_to_le16(USB_CDC_NCM_NTB16_SUPPORTED),
+	.dwNtbInMaxSize = cpu_to_le32(MBIM_NTB_DEFAULT_IN_SIZE),
+	.wNdpInDivisor = cpu_to_le16(4),
+	.wNdpInPayloadRemainder = cpu_to_le16(0),
+	.wNdpInAlignment = cpu_to_le16(4),
+
+	.dwNtbOutMaxSize = cpu_to_le32(0x4000),
+	.wNdpOutDivisor = cpu_to_le16(4),
+	.wNdpOutPayloadRemainder = cpu_to_le16(0),
+	.wNdpOutAlignment = cpu_to_le16(4),
+	.wNtbOutMaxDatagrams = 16,
+};
+
+/*
+ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+ * packet, to simplify cancellation;
+ */
+#define NCM_STATUS_BYTECOUNT		16	/* 8 byte header + data */
+
+static struct usb_interface_assoc_descriptor mbim_gsi_iad_desc = {
+	.bLength =		sizeof(mbim_gsi_iad_desc),
+	.bDescriptorType =	USB_DT_INTERFACE_ASSOCIATION,
+
+	/* .bFirstInterface =	DYNAMIC, */
+	.bInterfaceCount =	2,	/* control + data */
+	.bFunctionClass =	2,
+	.bFunctionSubClass =	0x0e,
+	.bFunctionProtocol =	0,
+	/* .iFunction =		DYNAMIC */
+};
+
+/* interface descriptor: */
+static struct usb_interface_descriptor mbim_gsi_control_intf = {
+	.bLength =		sizeof(mbim_gsi_control_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	0x02,
+	.bInterfaceSubClass =	0x0e,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc mbim_gsi_header_desc = {
+	.bLength =		sizeof(mbim_gsi_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc mbim_gsi_union_desc = {
+	.bLength =		sizeof(mbim_gsi_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_mbim_desc mbim_gsi_desc = {
+	.bLength =		sizeof(mbim_gsi_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_MBIM_TYPE,
+
+	.bcdMBIMVersion =	cpu_to_le16(0x0100),
+
+	.wMaxControlMessage =	cpu_to_le16(0x1000),
+	.bNumberFilters =	0x20,
+	.bMaxFilterSize =	0x80,
+	.wMaxSegmentSize =	cpu_to_le16(0xfe0),
+	.bmNetworkCapabilities = 0x20,
+};
+
+static struct usb_cdc_mbim_extended_desc mbim_gsi_ext_mbb_desc = {
+	.bLength =	sizeof(mbim_gsi_ext_mbb_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_MBIM_EXTENDED_TYPE,
+
+	.bcdMBIMExtendedVersion =		cpu_to_le16(0x0100),
+	.bMaxOutstandingCommandMessages =	64,
+	.wMTU =					cpu_to_le16(1500),
+};
+
+/* the default data interface has no endpoints ... */
+static struct usb_interface_descriptor mbim_gsi_data_nop_intf = {
+	.bLength =		sizeof(mbim_gsi_data_nop_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+static struct usb_interface_descriptor mbim_gsi_data_intf = {
+	.bLength =		sizeof(mbim_gsi_data_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	0x0a,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0x02,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor mbim_gsi_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor mbim_gsi_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor mbim_gsi_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *mbim_gsi_fs_function[] = {
+	(struct usb_descriptor_header *) &mbim_gsi_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_gsi_control_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_header_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_union_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ext_mbb_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_fs_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_gsi_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_data_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_fs_in_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+static struct usb_endpoint_descriptor mbim_gsi_hs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor mbim_gsi_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor mbim_gsi_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *mbim_gsi_hs_function[] = {
+	(struct usb_descriptor_header *) &mbim_gsi_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_gsi_control_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_header_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_union_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ext_mbb_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_hs_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_gsi_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_data_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_hs_in_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_hs_out_desc,
+	NULL,
+};
+
+/* Super Speed Support */
+static struct usb_endpoint_descriptor mbim_gsi_ss_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor mbim_gsi_ss_notify_comp_desc = {
+	.bLength =		sizeof(mbim_gsi_ss_notify_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 3 values can be tweaked if necessary */
+	/* .bMaxBurst =         0, */
+	/* .bmAttributes =      0, */
+	.wBytesPerInterval =	4*cpu_to_le16(NCM_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor mbim_gsi_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor mbim_gsi_ss_in_comp_desc = {
+	.bLength =              sizeof(mbim_gsi_ss_in_comp_desc),
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =         2,
+	/* .bmAttributes =      0, */
+};
+
+static struct usb_endpoint_descriptor mbim_gsi_ss_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor mbim_gsi_ss_out_comp_desc = {
+	.bLength =		sizeof(mbim_gsi_ss_out_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =         2,
+	/* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *mbim_gsi_ss_function[] = {
+	(struct usb_descriptor_header *) &mbim_gsi_iad_desc,
+	/* MBIM control descriptors */
+	(struct usb_descriptor_header *) &mbim_gsi_control_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_header_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_union_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ext_mbb_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ss_notify_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ss_notify_comp_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &mbim_gsi_data_nop_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_data_intf,
+	(struct usb_descriptor_header *) &mbim_gsi_ss_in_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ss_in_comp_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ss_out_desc,
+	(struct usb_descriptor_header *) &mbim_gsi_ss_out_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+static struct usb_string mbim_gsi_string_defs[] = {
+	[0].s = "MBIM Control",
+	[1].s = "MBIM Data",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings mbim_gsi_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		mbim_gsi_string_defs,
+};
+
+static struct usb_gadget_strings *mbim_gsi_strings[] = {
+	&mbim_gsi_string_table,
+	NULL,
+};
+
+/* Microsoft OS Descriptors */
+
+/*
+ * We specify our own bMS_VendorCode byte which Windows will use
+ * as the bRequest value in subsequent device get requests.
+ */
+#define MBIM_VENDOR_CODE	0xA5
+
+/* Microsoft Extended Configuration Descriptor Header Section */
+struct mbim_gsi_ext_config_desc_header {
+	__le32	dwLength;
+	__u16	bcdVersion;
+	__le16	wIndex;
+	__u8	bCount;
+	__u8	reserved[7];
+};
+
+/* Microsoft Extended Configuration Descriptor Function Section */
+struct mbim_gsi_ext_config_desc_function {
+	__u8	bFirstInterfaceNumber;
+	__u8	bInterfaceCount;
+	__u8	compatibleID[8];
+	__u8	subCompatibleID[8];
+	__u8	reserved[6];
+};
+
+/* Microsoft Extended Configuration Descriptor */
+static struct {
+	struct mbim_gsi_ext_config_desc_header	header;
+	struct mbim_gsi_ext_config_desc_function    function;
+} mbim_gsi_ext_config_desc = {
+	.header = {
+		.dwLength = cpu_to_le32(sizeof(mbim_gsi_ext_config_desc)),
+		.bcdVersion = cpu_to_le16(0x0100),
+		.wIndex = cpu_to_le16(4),
+		.bCount = 1,
+	},
+	.function = {
+		.bFirstInterfaceNumber = 0,
+		.bInterfaceCount = 1,
+		.compatibleID = { 'A', 'L', 'T', 'R', 'C', 'F', 'G' },
+		/* .subCompatibleID = DYNAMIC */
+	},
+};
+/* ecm device descriptors */
+#define ECM_QC_LOG2_STATUS_INTERVAL_MSEC	5
+#define ECM_QC_STATUS_BYTECOUNT			16 /* 8 byte header + data */
+
+/* interface descriptor: */
+static struct usb_interface_descriptor ecm_gsi_control_intf = {
+	.bLength =		sizeof(ecm_gsi_control_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	/* .bInterfaceNumber = DYNAMIC */
+	/* status endpoint is optional; this could be patched later */
+	.bNumEndpoints =	1,
+	.bInterfaceClass =	USB_CLASS_COMM,
+	.bInterfaceSubClass =	USB_CDC_SUBCLASS_ETHERNET,
+	.bInterfaceProtocol =	USB_CDC_PROTO_NONE,
+	/* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc ecm_gsi_header_desc = {
+	.bLength =		sizeof(ecm_gsi_header_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_HEADER_TYPE,
+
+	.bcdCDC =		cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_union_desc ecm_gsi_union_desc = {
+	.bLength =		sizeof(ecm_gsi_union_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_UNION_TYPE,
+	/* .bMasterInterface0 =	DYNAMIC */
+	/* .bSlaveInterface0 =	DYNAMIC */
+};
+
+static struct usb_cdc_ether_desc ecm_gsi_desc = {
+	.bLength =		sizeof(ecm_gsi_desc),
+	.bDescriptorType =	USB_DT_CS_INTERFACE,
+	.bDescriptorSubType =	USB_CDC_ETHERNET_TYPE,
+
+	/* this descriptor actually adds value, surprise! */
+	/* .iMACAddress = DYNAMIC */
+	.bmEthernetStatistics =	cpu_to_le32(0), /* no statistics */
+	.wMaxSegmentSize =	cpu_to_le16(ETH_FRAME_LEN),
+	.wNumberMCFilters =	cpu_to_le16(0),
+	.bNumberPowerFilters =	0,
+};
+
+/* the default data interface has no endpoints ... */
+
+static struct usb_interface_descriptor ecm_gsi_data_nop_intf = {
+	.bLength =		sizeof(ecm_gsi_data_nop_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	0,
+	.bNumEndpoints =	0,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+/* ... but the "real" data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor ecm_gsi_data_intf = {
+	.bLength =		sizeof(ecm_gsi_data_intf),
+	.bDescriptorType =	USB_DT_INTERFACE,
+
+	.bInterfaceNumber =	1,
+	.bAlternateSetting =	1,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_CDC_DATA,
+	.bInterfaceSubClass =	0,
+	.bInterfaceProtocol =	0,
+	/* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+static struct usb_endpoint_descriptor ecm_gsi_fs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(ECM_QC_STATUS_BYTECOUNT),
+	.bInterval =		1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor ecm_gsi_fs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor ecm_gsi_fs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *ecm_gsi_fs_function[] = {
+	/* CDC ECM control descriptors */
+	(struct usb_descriptor_header *) &ecm_gsi_control_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_header_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_union_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_desc,
+	/* NOTE: status endpoint might need to be removed */
+	(struct usb_descriptor_header *) &ecm_gsi_fs_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &ecm_gsi_data_nop_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_data_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_fs_in_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_fs_out_desc,
+	NULL,
+};
+
+/* high speed support: */
+static struct usb_endpoint_descriptor ecm_gsi_hs_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(ECM_QC_STATUS_BYTECOUNT),
+	.bInterval =		LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+static struct usb_endpoint_descriptor ecm_gsi_hs_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor ecm_gsi_hs_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *ecm_gsi_hs_function[] = {
+	/* CDC ECM control descriptors */
+	(struct usb_descriptor_header *) &ecm_gsi_control_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_header_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_union_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_desc,
+	/* NOTE: status endpoint might need to be removed */
+	(struct usb_descriptor_header *) &ecm_gsi_hs_notify_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &ecm_gsi_data_nop_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_data_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_hs_in_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_hs_out_desc,
+	NULL,
+};
+
+static struct usb_endpoint_descriptor ecm_gsi_ss_notify_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize =	cpu_to_le16(ECM_QC_STATUS_BYTECOUNT),
+	.bInterval =		ECM_QC_LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ecm_gsi_ss_notify_comp_desc = {
+	.bLength =		sizeof(ecm_gsi_ss_notify_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 3 values can be tweaked if necessary */
+	/* .bMaxBurst =         0, */
+	/* .bmAttributes =      0, */
+	.wBytesPerInterval =	cpu_to_le16(ECM_QC_STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ecm_gsi_ss_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ecm_gsi_ss_in_comp_desc = {
+	.bLength =		sizeof(ecm_gsi_ss_in_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =         2,
+	/* .bmAttributes =      0, */
+};
+
+static struct usb_endpoint_descriptor ecm_gsi_ss_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ecm_gsi_ss_out_comp_desc = {
+	.bLength =		sizeof(ecm_gsi_ss_out_comp_desc),
+	.bDescriptorType =	USB_DT_SS_ENDPOINT_COMP,
+
+	/* the following 2 values can be tweaked if necessary */
+	.bMaxBurst =         2,
+	/* .bmAttributes =      0, */
+};
+
+static struct usb_descriptor_header *ecm_gsi_ss_function[] = {
+	/* CDC ECM control descriptors */
+	(struct usb_descriptor_header *) &ecm_gsi_control_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_header_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_union_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_desc,
+	/* NOTE: status endpoint might need to be removed */
+	(struct usb_descriptor_header *) &ecm_gsi_ss_notify_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_ss_notify_comp_desc,
+	/* data interface, altsettings 0 and 1 */
+	(struct usb_descriptor_header *) &ecm_gsi_data_nop_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_data_intf,
+	(struct usb_descriptor_header *) &ecm_gsi_ss_in_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_ss_in_comp_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_ss_out_desc,
+	(struct usb_descriptor_header *) &ecm_gsi_ss_out_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+static struct usb_string ecm_gsi_string_defs[] = {
+	[0].s = "CDC Ethernet Control Model (ECM)",
+	[1].s = NULL /* DYNAMIC */,
+	[2].s = "CDC Ethernet Data",
+	{  } /* end of list */
+};
+
+static struct usb_gadget_strings ecm_gsi_string_table = {
+	.language =		0x0409,	/* en-us */
+	.strings =		ecm_gsi_string_defs,
+};
+
+static struct usb_gadget_strings *ecm_gsi_strings[] = {
+	&ecm_gsi_string_table,
+	NULL,
+};
+
+/* qdss device descriptor */
+
+static struct usb_interface_descriptor qdss_gsi_data_intf_desc = {
+	.bLength            =	sizeof(qdss_gsi_data_intf_desc),
+	.bDescriptorType    =	USB_DT_INTERFACE,
+	.bAlternateSetting  =   0,
+	.bNumEndpoints      =	1,
+	.bInterfaceClass    =	0xff,
+	.bInterfaceSubClass =	0xff,
+	.bInterfaceProtocol =	0xff,
+};
+
+static struct usb_endpoint_descriptor qdss_gsi_hs_data_desc = {
+	.bLength              =	 USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType      =	 USB_DT_ENDPOINT,
+	.bEndpointAddress     =	 USB_DIR_IN,
+	.bmAttributes         =	 USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize       =	 cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor qdss_gsi_ss_data_desc = {
+	.bLength              =	 USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType      =	 USB_DT_ENDPOINT,
+	.bEndpointAddress     =	 USB_DIR_IN,
+	.bmAttributes         =  USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize       =	 cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor qdss_gsi_data_ep_comp_desc = {
+	.bLength              =	 sizeof(qdss_gsi_data_ep_comp_desc),
+	.bDescriptorType      =	 USB_DT_SS_ENDPOINT_COMP,
+	.bMaxBurst            =	 1,
+	.bmAttributes         =	 0,
+	.wBytesPerInterval    =	 0,
+};
+
+static struct usb_descriptor_header *qdss_gsi_hs_data_only_desc[] = {
+	(struct usb_descriptor_header *) &qdss_gsi_data_intf_desc,
+	(struct usb_descriptor_header *) &qdss_gsi_hs_data_desc,
+	NULL,
+};
+
+static struct usb_descriptor_header *qdss_gsi_ss_data_only_desc[] = {
+	(struct usb_descriptor_header *) &qdss_gsi_data_intf_desc,
+	(struct usb_descriptor_header *) &qdss_gsi_ss_data_desc,
+	(struct usb_descriptor_header *) &qdss_gsi_data_ep_comp_desc,
+	NULL,
+};
+
+/* string descriptors: */
+static struct usb_string qdss_gsi_string_defs[] = {
+	[0].s = "QDSS DATA",
+	{}, /* end of list */
+};
+
+static struct usb_gadget_strings qdss_gsi_string_table = {
+	.language =		0x0409,
+	.strings =		qdss_gsi_string_defs,
+};
+
+static struct usb_gadget_strings *qdss_gsi_strings[] = {
+	&qdss_gsi_string_table,
+	NULL,
+};
+#endif
diff --git a/drivers/usb/gadget/function/f_mtp.c b/drivers/usb/gadget/function/f_mtp.c
index e21d3e0..5840180 100644
--- a/drivers/usb/gadget/function/f_mtp.c
+++ b/drivers/usb/gadget/function/f_mtp.c
@@ -26,6 +26,8 @@
 #include <linux/err.h>
 #include <linux/interrupt.h>
 
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
 #include <linux/types.h>
 #include <linux/file.h>
 #include <linux/device.h>
@@ -40,6 +42,8 @@
 
 #include "configfs.h"
 
+#define MTP_RX_BUFFER_INIT_SIZE    1048576
+#define MTP_TX_BUFFER_INIT_SIZE    1048576
 #define MTP_BULK_BUFFER_SIZE       16384
 #define INTR_BUFFER_SIZE           28
 #define MAX_INST_NAME_LEN          40
@@ -55,7 +59,7 @@
 #define STATE_ERROR                 4   /* error from completion routine */
 
 /* number of tx and rx requests to allocate */
-#define TX_REQ_MAX 4
+#define MTP_TX_REQ_MAX 8
 #define RX_REQ_MAX 2
 #define INTR_REQ_MAX 5
 
@@ -73,6 +77,17 @@
 #define MTP_RESPONSE_DEVICE_BUSY    0x2019
 #define DRIVER_NAME "mtp"
 
+#define MAX_ITERATION		100
+
+unsigned int mtp_rx_req_len = MTP_RX_BUFFER_INIT_SIZE;
+module_param(mtp_rx_req_len, uint, 0644);
+
+unsigned int mtp_tx_req_len = MTP_TX_BUFFER_INIT_SIZE;
+module_param(mtp_tx_req_len, uint, 0644);
+
+unsigned int mtp_tx_reqs = MTP_TX_REQ_MAX;
+module_param(mtp_tx_reqs, uint, 0644);
+
 static const char mtp_shortname[] = DRIVER_NAME "_usb";
 
 struct mtp_dev {
@@ -113,6 +128,14 @@ struct mtp_dev {
 	uint16_t xfer_command;
 	uint32_t xfer_transaction_id;
 	int xfer_result;
+	struct {
+		unsigned long vfs_rbytes;
+		unsigned long vfs_wbytes;
+		unsigned int vfs_rtime;
+		unsigned int vfs_wtime;
+	} perf[MAX_ITERATION];
+	unsigned int dbg_read_index;
+	unsigned int dbg_write_index;
 };
 
 static struct usb_interface_descriptor mtp_interface_desc = {
@@ -309,10 +332,12 @@ struct mtp_ext_config_desc_function {
 };
 
 /* MTP Extended Configuration Descriptor */
-struct {
+struct mtp_ext_config_desc {
 	struct mtp_ext_config_desc_header	header;
 	struct mtp_ext_config_desc_function    function;
-} mtp_ext_config_desc = {
+};
+
+static struct mtp_ext_config_desc mtp_ext_config_desc = {
 	.header = {
 		.dwLength = __constant_cpu_to_le32(sizeof(mtp_ext_config_desc)),
 		.bcdVersion = __constant_cpu_to_le16(0x0100),
@@ -430,7 +455,7 @@ static void mtp_complete_in(struct usb_ep *ep, struct usb_request *req)
 {
 	struct mtp_dev *dev = _mtp_dev;
 
-	if (req->status != 0)
+	if (req->status != 0 && dev->state != STATE_OFFLINE)
 		dev->state = STATE_ERROR;
 
 	mtp_req_put(dev, &dev->tx_idle, req);
@@ -443,7 +468,7 @@ static void mtp_complete_out(struct usb_ep *ep, struct usb_request *req)
 	struct mtp_dev *dev = _mtp_dev;
 
 	dev->rx_done = 1;
-	if (req->status != 0)
+	if (req->status != 0 && dev->state != STATE_OFFLINE)
 		dev->state = STATE_ERROR;
 
 	wake_up(&dev->read_wq);
@@ -453,7 +478,7 @@ static void mtp_complete_intr(struct usb_ep *ep, struct usb_request *req)
 {
 	struct mtp_dev *dev = _mtp_dev;
 
-	if (req->status != 0)
+	if (req->status != 0 && dev->state != STATE_OFFLINE)
 		dev->state = STATE_ERROR;
 
 	mtp_req_put(dev, &dev->intr_idle, req);
@@ -500,18 +525,43 @@ static int mtp_create_bulk_endpoints(struct mtp_dev *dev,
 	ep->driver_data = dev;		/* claim the endpoint */
 	dev->ep_intr = ep;
 
+retry_tx_alloc:
 	/* now allocate requests for our endpoints */
-	for (i = 0; i < TX_REQ_MAX; i++) {
-		req = mtp_request_new(dev->ep_in, MTP_BULK_BUFFER_SIZE);
-		if (!req)
-			goto fail;
+	for (i = 0; i < mtp_tx_reqs; i++) {
+		req = mtp_request_new(dev->ep_in, mtp_tx_req_len);
+		if (!req) {
+			if (mtp_tx_req_len <= MTP_BULK_BUFFER_SIZE)
+				goto fail;
+			while ((req = mtp_req_get(dev, &dev->tx_idle)))
+				mtp_request_free(req, dev->ep_in);
+			mtp_tx_req_len = MTP_BULK_BUFFER_SIZE;
+			mtp_tx_reqs = MTP_TX_REQ_MAX;
+			goto retry_tx_alloc;
+		}
 		req->complete = mtp_complete_in;
 		mtp_req_put(dev, &dev->tx_idle, req);
 	}
+
+	/*
+	 * The RX buffer should be aligned to EP max packet for
+	 * some controllers.  At bind time, we don't know the
+	 * operational speed.  Hence assuming super speed max
+	 * packet size.
+	 */
+	if (mtp_rx_req_len % 1024)
+		mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+
+retry_rx_alloc:
 	for (i = 0; i < RX_REQ_MAX; i++) {
-		req = mtp_request_new(dev->ep_out, MTP_BULK_BUFFER_SIZE);
-		if (!req)
-			goto fail;
+		req = mtp_request_new(dev->ep_out, mtp_rx_req_len);
+		if (!req) {
+			if (mtp_rx_req_len <= MTP_BULK_BUFFER_SIZE)
+				goto fail;
+			for (--i; i >= 0; i--)
+				mtp_request_free(dev->rx_req[i], dev->ep_out);
+			mtp_rx_req_len = MTP_BULK_BUFFER_SIZE;
+			goto retry_rx_alloc;
+		}
 		req->complete = mtp_complete_out;
 		dev->rx_req[i] = req;
 	}
@@ -536,14 +586,10 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
 	struct mtp_dev *dev = fp->private_data;
 	struct usb_composite_dev *cdev = dev->cdev;
 	struct usb_request *req;
-	ssize_t r = count;
-	unsigned xfer;
+	ssize_t r = count, xfer, len;
 	int ret = 0;
 
-	DBG(cdev, "mtp_read(%zu)\n", count);
-
-	if (count > MTP_BULK_BUFFER_SIZE)
-		return -EINVAL;
+	DBG(cdev, "mtp_read(%zu) state:%d\n", count, dev->state);
 
 	/* we will block until we're online */
 	DBG(cdev, "mtp_read: waiting for online state\n");
@@ -553,6 +599,11 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
 		r = ret;
 		goto done;
 	}
+
+	len = ALIGN(count, dev->ep_out->maxpacket);
+	if (len > mtp_rx_req_len)
+		return -EINVAL;
+
 	spin_lock_irq(&dev->lock);
 	if (dev->state == STATE_CANCELED) {
 		/* report cancelation to userspace */
@@ -566,7 +617,7 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
 requeue_req:
 	/* queue a request */
 	req = dev->rx_req[0];
-	req->length = count;
+	req->length = len;
 	dev->rx_done = 0;
 	ret = usb_ep_queue(dev->ep_out, req, GFP_KERNEL);
 	if (ret < 0) {
@@ -577,7 +628,17 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
 	}
 
 	/* wait for a request to complete */
-	ret = wait_event_interruptible(dev->read_wq, dev->rx_done);
+	ret = wait_event_interruptible(dev->read_wq,
+				dev->rx_done || dev->state != STATE_BUSY);
+	if (dev->state == STATE_CANCELED) {
+		r = -ECANCELED;
+		if (!dev->rx_done)
+			usb_ep_dequeue(dev->ep_out, req);
+		spin_lock_irq(&dev->lock);
+		dev->state = STATE_CANCELED;
+		spin_unlock_irq(&dev->lock);
+		goto done;
+	}
 	if (ret < 0) {
 		r = ret;
 		usb_ep_dequeue(dev->ep_out, req);
@@ -604,7 +665,7 @@ static ssize_t mtp_read(struct file *fp, char __user *buf,
 		dev->state = STATE_READY;
 	spin_unlock_irq(&dev->lock);
 
-	DBG(cdev, "mtp_read returning %zd\n", r);
+	DBG(cdev, "mtp_read returning %zd state:%d\n", r, dev->state);
 	return r;
 }
 
@@ -619,7 +680,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf,
 	int sendZLP = 0;
 	int ret;
 
-	DBG(cdev, "mtp_write(%zu)\n", count);
+	DBG(cdev, "mtp_write(%zu) state:%d\n", count, dev->state);
 
 	spin_lock_irq(&dev->lock);
 	if (dev->state == STATE_CANCELED) {
@@ -658,12 +719,14 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf,
 			((req = mtp_req_get(dev, &dev->tx_idle))
 				|| dev->state != STATE_BUSY));
 		if (!req) {
+			DBG(cdev, "mtp_write request NULL ret:%d state:%d\n",
+				ret, dev->state);
 			r = ret;
 			break;
 		}
 
-		if (count > MTP_BULK_BUFFER_SIZE)
-			xfer = MTP_BULK_BUFFER_SIZE;
+		if (count > mtp_tx_req_len)
+			xfer = mtp_tx_req_len;
 		else
 			xfer = count;
 		if (xfer && copy_from_user(req->buf, buf, xfer)) {
@@ -696,7 +759,7 @@ static ssize_t mtp_write(struct file *fp, const char __user *buf,
 		dev->state = STATE_READY;
 	spin_unlock_irq(&dev->lock);
 
-	DBG(cdev, "mtp_write returning %zd\n", r);
+	DBG(cdev, "mtp_write returning %zd state:%d\n", r, dev->state);
 	return r;
 }
 
@@ -714,6 +777,7 @@ static void send_file_work(struct work_struct *data)
 	int xfer, ret, hdr_size;
 	int r = 0;
 	int sendZLP = 0;
+	ktime_t start_time;
 
 	/* read our parameters */
 	smp_rmb();
@@ -751,12 +815,15 @@ static void send_file_work(struct work_struct *data)
 			break;
 		}
 		if (!req) {
+			DBG(cdev,
+				"send_file_work request NULL ret:%d state:%d\n",
+				ret, dev->state);
 			r = ret;
 			break;
 		}
 
-		if (count > MTP_BULK_BUFFER_SIZE)
-			xfer = MTP_BULK_BUFFER_SIZE;
+		if (count > mtp_tx_req_len)
+			xfer = mtp_tx_req_len;
 		else
 			xfer = count;
 
@@ -769,21 +836,27 @@ static void send_file_work(struct work_struct *data)
 			header->transaction_id =
 					__cpu_to_le32(dev->xfer_transaction_id);
 		}
-
+		start_time = ktime_get();
 		ret = vfs_read(filp, req->buf + hdr_size, xfer - hdr_size,
 								&offset);
 		if (ret < 0) {
 			r = ret;
 			break;
 		}
+
 		xfer = ret + hdr_size;
+		dev->perf[dev->dbg_read_index].vfs_rtime =
+			ktime_to_us(ktime_sub(ktime_get(), start_time));
+		dev->perf[dev->dbg_read_index].vfs_rbytes = xfer;
+		dev->dbg_read_index = (dev->dbg_read_index + 1) % MAX_ITERATION;
 		hdr_size = 0;
 
 		req->length = xfer;
 		ret = usb_ep_queue(dev->ep_in, req, GFP_KERNEL);
 		if (ret < 0) {
 			DBG(cdev, "send_file_work: xfer error %d\n", ret);
-			dev->state = STATE_ERROR;
+			if (dev->state != STATE_OFFLINE)
+				dev->state = STATE_ERROR;
 			r = -EIO;
 			break;
 		}
@@ -797,7 +870,7 @@ static void send_file_work(struct work_struct *data)
 	if (req)
 		mtp_req_put(dev, &dev->tx_idle, req);
 
-	DBG(cdev, "send_file_work returning %d\n", r);
+	DBG(cdev, "send_file_work returning %d state:%d\n", r, dev->state);
 	/* write the result */
 	dev->xfer_result = r;
 	smp_wmb();
@@ -815,6 +888,7 @@ static void receive_file_work(struct work_struct *data)
 	int64_t count;
 	int ret, cur_buf = 0;
 	int r = 0;
+	ktime_t start_time;
 
 	/* read our parameters */
 	smp_rmb();
@@ -823,6 +897,9 @@ static void receive_file_work(struct work_struct *data)
 	count = dev->xfer_file_length;
 
 	DBG(cdev, "receive_file_work(%lld)\n", count);
+	if (!IS_ALIGNED(count, dev->ep_out->maxpacket))
+		DBG(cdev, "%s- count(%lld) not multiple of mtu(%d)\n", __func__,
+						count, dev->ep_out->maxpacket);
 
 	while (count > 0 || write_req) {
 		if (count > 0) {
@@ -830,27 +907,36 @@ static void receive_file_work(struct work_struct *data)
 			read_req = dev->rx_req[cur_buf];
 			cur_buf = (cur_buf + 1) % RX_REQ_MAX;
 
-			read_req->length = (count > MTP_BULK_BUFFER_SIZE
-					? MTP_BULK_BUFFER_SIZE : count);
+			/* some h/w expects size to be aligned to ep's MTU */
+			read_req->length = mtp_rx_req_len;
+
 			dev->rx_done = 0;
 			ret = usb_ep_queue(dev->ep_out, read_req, GFP_KERNEL);
 			if (ret < 0) {
 				r = -EIO;
-				dev->state = STATE_ERROR;
+				if (dev->state != STATE_OFFLINE)
+					dev->state = STATE_ERROR;
 				break;
 			}
 		}
 
 		if (write_req) {
 			DBG(cdev, "rx %p %d\n", write_req, write_req->actual);
+			start_time = ktime_get();
 			ret = vfs_write(filp, write_req->buf, write_req->actual,
 				&offset);
 			DBG(cdev, "vfs_write %d\n", ret);
 			if (ret != write_req->actual) {
 				r = -EIO;
-				dev->state = STATE_ERROR;
+				if (dev->state != STATE_OFFLINE)
+					dev->state = STATE_ERROR;
 				break;
 			}
+			dev->perf[dev->dbg_write_index].vfs_wtime =
+				ktime_to_us(ktime_sub(ktime_get(), start_time));
+			dev->perf[dev->dbg_write_index].vfs_wbytes = ret;
+			dev->dbg_write_index =
+				(dev->dbg_write_index + 1) % MAX_ITERATION;
 			write_req = NULL;
 		}
 
@@ -858,12 +944,21 @@ static void receive_file_work(struct work_struct *data)
 			/* wait for our last read to complete */
 			ret = wait_event_interruptible(dev->read_wq,
 				dev->rx_done || dev->state != STATE_BUSY);
-			if (dev->state == STATE_CANCELED) {
-				r = -ECANCELED;
+			if (dev->state == STATE_CANCELED
+					|| dev->state == STATE_OFFLINE) {
+				if (dev->state == STATE_OFFLINE)
+					r = -EIO;
+				else
+					r = -ECANCELED;
 				if (!dev->rx_done)
 					usb_ep_dequeue(dev->ep_out, read_req);
 				break;
 			}
+
+			/* Check if we aligned the size due to MTU constraint */
+			if (count < read_req->length)
+				read_req->actual = (read_req->actual > count ?
+						count : read_req->actual);
 			/* if xfer_file_length is 0xFFFFFFFF, then we read until
 			 * we get a zero length packet
 			 */
@@ -920,95 +1015,73 @@ static int mtp_send_event(struct mtp_dev *dev, struct mtp_event *event)
 	return ret;
 }
 
-static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
+static long mtp_send_receive_ioctl(struct file *fp, unsigned int code,
+	struct mtp_file_range *mfr)
 {
 	struct mtp_dev *dev = fp->private_data;
 	struct file *filp = NULL;
+	struct work_struct *work;
 	int ret = -EINVAL;
 
-	if (mtp_lock(&dev->ioctl_excl))
+	if (mtp_lock(&dev->ioctl_excl)) {
+		DBG(dev->cdev, "ioctl returning EBUSY state:%d\n", dev->state);
 		return -EBUSY;
-
-	switch (code) {
-	case MTP_SEND_FILE:
-	case MTP_RECEIVE_FILE:
-	case MTP_SEND_FILE_WITH_HEADER:
-	{
-		struct mtp_file_range	mfr;
-		struct work_struct *work;
-
-		spin_lock_irq(&dev->lock);
-		if (dev->state == STATE_CANCELED) {
-			/* report cancelation to userspace */
-			dev->state = STATE_READY;
-			spin_unlock_irq(&dev->lock);
-			ret = -ECANCELED;
-			goto out;
-		}
-		if (dev->state == STATE_OFFLINE) {
-			spin_unlock_irq(&dev->lock);
-			ret = -ENODEV;
-			goto out;
-		}
-		dev->state = STATE_BUSY;
-		spin_unlock_irq(&dev->lock);
-
-		if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
-			ret = -EFAULT;
-			goto fail;
-		}
-		/* hold a reference to the file while we are working with it */
-		filp = fget(mfr.fd);
-		if (!filp) {
-			ret = -EBADF;
-			goto fail;
-		}
-
-		/* write the parameters */
-		dev->xfer_file = filp;
-		dev->xfer_file_offset = mfr.offset;
-		dev->xfer_file_length = mfr.length;
-		smp_wmb();
-
-		if (code == MTP_SEND_FILE_WITH_HEADER) {
-			work = &dev->send_file_work;
-			dev->xfer_send_header = 1;
-			dev->xfer_command = mfr.command;
-			dev->xfer_transaction_id = mfr.transaction_id;
-		} else if (code == MTP_SEND_FILE) {
-			work = &dev->send_file_work;
-			dev->xfer_send_header = 0;
-		} else {
-			work = &dev->receive_file_work;
-		}
-
-		/* We do the file transfer on a work queue so it will run
-		 * in kernel context, which is necessary for vfs_read and
-		 * vfs_write to use our buffers in the kernel address space.
-		 */
-		queue_work(dev->wq, work);
-		/* wait for operation to complete */
-		flush_workqueue(dev->wq);
-		fput(filp);
-
-		/* read the result */
-		smp_rmb();
-		ret = dev->xfer_result;
-		break;
 	}
-	case MTP_SEND_EVENT:
-	{
-		struct mtp_event	event;
-		/* return here so we don't change dev->state below,
-		 * which would interfere with bulk transfer state.
-		 */
-		if (copy_from_user(&event, (void __user *)value, sizeof(event)))
-			ret = -EFAULT;
-		else
-			ret = mtp_send_event(dev, &event);
+
+	spin_lock_irq(&dev->lock);
+	if (dev->state == STATE_CANCELED) {
+		/* report cancellation to userspace */
+		dev->state = STATE_READY;
+		spin_unlock_irq(&dev->lock);
+		ret = -ECANCELED;
 		goto out;
 	}
+	if (dev->state == STATE_OFFLINE) {
+		spin_unlock_irq(&dev->lock);
+		ret = -ENODEV;
+		goto out;
 	}
+	dev->state = STATE_BUSY;
+	spin_unlock_irq(&dev->lock);
+
+	/* hold a reference to the file while we are working with it */
+	filp = fget(mfr->fd);
+	if (!filp) {
+		ret = -EBADF;
+		goto fail;
+	}
+
+	/* write the parameters */
+	dev->xfer_file = filp;
+	dev->xfer_file_offset = mfr->offset;
+	dev->xfer_file_length = mfr->length;
+	/* make sure write is done before parameters are read */
+	smp_wmb();
+
+	if (code == MTP_SEND_FILE_WITH_HEADER) {
+		work = &dev->send_file_work;
+		dev->xfer_send_header = 1;
+		dev->xfer_command = mfr->command;
+		dev->xfer_transaction_id = mfr->transaction_id;
+	} else if (code == MTP_SEND_FILE) {
+		work = &dev->send_file_work;
+		dev->xfer_send_header = 0;
+	} else {
+		work = &dev->receive_file_work;
+	}
+
+	/* We do the file transfer on a work queue so it will run
+	 * in kernel context, which is necessary for vfs_read and
+	 * vfs_write to use our buffers in the kernel address space.
+	 */
+	queue_work(dev->wq, work);
+	/* wait for operation to complete */
+	flush_workqueue(dev->wq);
+	fput(filp);
+
+	/* read the result */
+	smp_rmb();
+	ret = dev->xfer_result;
 
 fail:
 	spin_lock_irq(&dev->lock);
@@ -1019,15 +1092,124 @@ static long mtp_ioctl(struct file *fp, unsigned code, unsigned long value)
 	spin_unlock_irq(&dev->lock);
 out:
 	mtp_unlock(&dev->ioctl_excl);
-	DBG(dev->cdev, "ioctl returning %d\n", ret);
+	DBG(dev->cdev, "ioctl returning %d state:%d\n", ret, dev->state);
 	return ret;
 }
 
+static long mtp_ioctl(struct file *fp, unsigned int code, unsigned long value)
+{
+	struct mtp_dev *dev = fp->private_data;
+	struct mtp_file_range	mfr;
+	struct mtp_event	event;
+	int ret = -EINVAL;
+
+	switch (code) {
+	case MTP_SEND_FILE:
+	case MTP_RECEIVE_FILE:
+	case MTP_SEND_FILE_WITH_HEADER:
+		if (copy_from_user(&mfr, (void __user *)value, sizeof(mfr))) {
+			ret = -EFAULT;
+			goto fail;
+		}
+		ret = mtp_send_receive_ioctl(fp, code, &mfr);
+	break;
+	case MTP_SEND_EVENT:
+		if (mtp_lock(&dev->ioctl_excl))
+			return -EBUSY;
+		/* return here so we don't change dev->state below,
+		 * which would interfere with bulk transfer state.
+		 */
+		if (copy_from_user(&event, (void __user *)value, sizeof(event)))
+			ret = -EFAULT;
+		else
+			ret = mtp_send_event(dev, &event);
+		mtp_unlock(&dev->ioctl_excl);
+	break;
+	default:
+		DBG(dev->cdev, "unknown ioctl code: %d\n", code);
+	}
+fail:
+	return ret;
+}
+
+/*
+ * 32 bit userspace calling into 64 bit kernel. handle ioctl code
+ * and userspace pointer
+ */
+#ifdef CONFIG_COMPAT
+static long compat_mtp_ioctl(struct file *fp, unsigned int code,
+	unsigned long value)
+{
+	struct mtp_dev *dev = fp->private_data;
+	struct mtp_file_range	mfr;
+	struct __compat_mtp_file_range	cmfr;
+	struct mtp_event	event;
+	struct __compat_mtp_event cevent;
+	unsigned int cmd;
+	bool send_file = false;
+	int ret = -EINVAL;
+
+	switch (code) {
+	case COMPAT_MTP_SEND_FILE:
+		cmd = MTP_SEND_FILE;
+		send_file = true;
+		break;
+	case COMPAT_MTP_RECEIVE_FILE:
+		cmd = MTP_RECEIVE_FILE;
+		send_file = true;
+		break;
+	case COMPAT_MTP_SEND_FILE_WITH_HEADER:
+		cmd = MTP_SEND_FILE_WITH_HEADER;
+		send_file = true;
+		break;
+	case COMPAT_MTP_SEND_EVENT:
+		cmd = MTP_SEND_EVENT;
+		break;
+	default:
+		DBG(dev->cdev, "unknown compat_ioctl code: %d\n", code);
+		ret = -ENOIOCTLCMD;
+		goto fail;
+	}
+
+	if (send_file) {
+		if (copy_from_user(&cmfr, (void __user *)value, sizeof(cmfr))) {
+			ret = -EFAULT;
+			goto fail;
+		}
+		mfr.fd = cmfr.fd;
+		mfr.offset = cmfr.offset;
+		mfr.length = cmfr.length;
+		mfr.command = cmfr.command;
+		mfr.transaction_id = cmfr.transaction_id;
+		ret = mtp_send_receive_ioctl(fp, cmd, &mfr);
+	} else {
+		if (mtp_lock(&dev->ioctl_excl))
+			return -EBUSY;
+		/* return here so we don't change dev->state below,
+		 * which would interfere with bulk transfer state.
+		 */
+		if (copy_from_user(&cevent, (void __user *)value,
+			sizeof(cevent))) {
+			ret = -EFAULT;
+			goto fail;
+		}
+		event.length = cevent.length;
+		event.data = compat_ptr(cevent.data);
+		ret = mtp_send_event(dev, &event);
+		mtp_unlock(&dev->ioctl_excl);
+	}
+fail:
+	return ret;
+}
+#endif
+
 static int mtp_open(struct inode *ip, struct file *fp)
 {
 	printk(KERN_INFO "mtp_open\n");
-	if (mtp_lock(&_mtp_dev->open_excl))
+	if (mtp_lock(&_mtp_dev->open_excl)) {
+		pr_err("%s mtp_release not called returning EBUSY\n", __func__);
 		return -EBUSY;
+	}
 
 	/* clear any error condition */
 	if (_mtp_dev->state != STATE_OFFLINE)
@@ -1051,6 +1233,9 @@ static const struct file_operations mtp_fops = {
 	.read = mtp_read,
 	.write = mtp_write,
 	.unlocked_ioctl = mtp_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl = compat_mtp_ioctl,
+#endif
 	.open = mtp_open,
 	.release = mtp_release,
 };
@@ -1096,6 +1281,13 @@ static int mtp_ctrlrequest(struct usb_composite_dev *cdev,
 			value = (w_length < sizeof(mtp_ext_config_desc) ?
 					w_length : sizeof(mtp_ext_config_desc));
 			memcpy(cdev->req->buf, &mtp_ext_config_desc, value);
+
+			/* update compatibleID if PTP */
+			if (dev->function.fs_descriptors == fs_ptp_descs) {
+				struct mtp_ext_config_desc *d = cdev->req->buf;
+
+				d->function.compatibleID[0] = 'P';
+			}
 		}
 	} else if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) {
 		DBG(cdev, "class request: %d index: %d value: %d length: %d\n",
@@ -1230,7 +1422,6 @@ mtp_function_unbind(struct usb_configuration *c, struct usb_function *f)
 	struct usb_request *req;
 	int i;
 
-	mtp_string_defs[INTERFACE_STRING_INDEX].id = 0;
 	while ((req = mtp_req_get(dev, &dev->tx_idle)))
 		mtp_request_free(req, dev->ep_in);
 	for (i = 0; i < RX_REQ_MAX; i++)
@@ -1303,6 +1494,120 @@ static void mtp_function_disable(struct usb_function *f)
 	VDBG(cdev, "%s disabled\n", dev->function.name);
 }
 
+static int debug_mtp_read_stats(struct seq_file *s, void *unused)
+{
+	struct mtp_dev *dev = _mtp_dev;
+	int i;
+	unsigned long flags;
+	unsigned int min, max = 0, sum = 0, iteration = 0;
+
+	seq_puts(s, "\n=======================\n");
+	seq_puts(s, "USB MTP OUT related VFS write stats:\n");
+	seq_puts(s, "\n=======================\n");
+	spin_lock_irqsave(&dev->lock, flags);
+	min = dev->perf[0].vfs_wtime;
+	for (i = 0; i < MAX_ITERATION; i++) {
+		seq_printf(s, "vfs write: bytes:%ld\t\t time:%d\n",
+				dev->perf[i].vfs_wbytes,
+				dev->perf[i].vfs_wtime);
+		if (dev->perf[i].vfs_wbytes == mtp_rx_req_len) {
+			sum += dev->perf[i].vfs_wtime;
+			if (min > dev->perf[i].vfs_wtime)
+				min = dev->perf[i].vfs_wtime;
+			if (max < dev->perf[i].vfs_wtime)
+				max = dev->perf[i].vfs_wtime;
+			iteration++;
+		}
+	}
+
+	seq_printf(s, "vfs_write(time in usec) min:%d\t max:%d\t avg:%d\n",
+						min, max, sum / iteration);
+	min = max = sum = iteration = 0;
+	seq_puts(s, "\n=======================\n");
+	seq_puts(s, "USB MTP IN related VFS read stats:\n");
+	seq_puts(s, "\n=======================\n");
+
+	min = dev->perf[0].vfs_rtime;
+	for (i = 0; i < MAX_ITERATION; i++) {
+		seq_printf(s, "vfs read: bytes:%ld\t\t time:%d\n",
+				dev->perf[i].vfs_rbytes,
+				dev->perf[i].vfs_rtime);
+		if (dev->perf[i].vfs_rbytes == mtp_tx_req_len) {
+			sum += dev->perf[i].vfs_rtime;
+			if (min > dev->perf[i].vfs_rtime)
+				min = dev->perf[i].vfs_rtime;
+			if (max < dev->perf[i].vfs_rtime)
+				max = dev->perf[i].vfs_rtime;
+			iteration++;
+		}
+	}
+
+	seq_printf(s, "vfs_read(time in usec) min:%d\t max:%d\t avg:%d\n",
+						min, max, sum / iteration);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 0;
+}
+
+static ssize_t debug_mtp_reset_stats(struct file *file, const char __user *buf,
+				 size_t count, loff_t *ppos)
+{
+	int clear_stats;
+	unsigned long flags;
+	struct mtp_dev *dev = _mtp_dev;
+
+	if (buf == NULL) {
+		pr_err("[%s] EINVAL\n", __func__);
+		goto done;
+	}
+
+	if (kstrtoint(buf, 0, &clear_stats) || clear_stats != 0) {
+		pr_err("Wrong value. To clear stats, enter value as 0.\n");
+		goto done;
+	}
+
+	spin_lock_irqsave(&dev->lock, flags);
+	memset(&dev->perf[0], 0, MAX_ITERATION * sizeof(dev->perf[0]));
+	dev->dbg_read_index = 0;
+	dev->dbg_write_index = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+done:
+	return count;
+}
+
+static int debug_mtp_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, debug_mtp_read_stats, inode->i_private);
+}
+
+static const struct file_operations debug_mtp_ops = {
+	.open = debug_mtp_open,
+	.read = seq_read,
+	.write = debug_mtp_reset_stats,
+};
+
+struct dentry *dent_mtp;
+static void mtp_debugfs_init(void)
+{
+	struct dentry *dent_mtp_status;
+
+	dent_mtp = debugfs_create_dir("usb_mtp", 0);
+	if (!dent_mtp || IS_ERR(dent_mtp))
+		return;
+
+	dent_mtp_status = debugfs_create_file("status", 0644, dent_mtp,
+						0, &debug_mtp_ops);
+	if (!dent_mtp_status || IS_ERR(dent_mtp_status)) {
+		debugfs_remove(dent_mtp);
+		dent_mtp = NULL;
+		return;
+	}
+}
+
+static void mtp_debugfs_remove(void)
+{
+	debugfs_remove_recursive(dent_mtp);
+}
+
 static int __mtp_setup(struct mtp_instance *fi_mtp)
 {
 	struct mtp_dev *dev;
@@ -1339,6 +1644,7 @@ static int __mtp_setup(struct mtp_instance *fi_mtp)
 	if (ret)
 		goto err2;
 
+	mtp_debugfs_init();
 	return 0;
 
 err2:
@@ -1363,6 +1669,7 @@ static void mtp_cleanup(void)
 	if (!dev)
 		return;
 
+	mtp_debugfs_remove();
 	misc_deregister(&mtp_device);
 	destroy_workqueue(dev->wq);
 	_mtp_dev = NULL;
@@ -1518,6 +1825,7 @@ struct usb_function *function_alloc_mtp_ptp(struct usb_function_instance *fi,
 	dev->function.disable = mtp_function_disable;
 	dev->function.setup = mtp_ctrlreq_configfs;
 	dev->function.free_func = mtp_free;
+	fi->f = &dev->function;
 
 	return &dev->function;
 }
diff --git a/drivers/usb/pd/Kconfig b/drivers/usb/pd/Kconfig
new file mode 100644
index 0000000..f3facc6
--- /dev/null
+++ b/drivers/usb/pd/Kconfig
@@ -0,0 +1,29 @@
+#
+# USB Power Delivery driver configuration
+#
+menu "USB Power Delivery"
+
+config USB_PD
+	def_bool n
+
+config USB_PD_POLICY
+	tristate "USB Power Delivery Protocol and Policy Engine"
+	depends on EXTCON
+	depends on DUAL_ROLE_USB_INTF
+	select USB_PD
+	help
+          Say Y here to enable USB PD protocol and policy engine.
+	  This driver provides a class that implements the upper
+	  layers of the USB Power Delivery stack. It requires a
+	  PD PHY driver in order to transmit and receive PD
+	  messages on its behalf.
+
+config QPNP_USB_PDPHY
+	tristate "QPNP USB Power Delivery PHY"
+	depends on SPMI
+	help
+          Say Y here to enable QPNP USB PD PHY peripheral driver
+	  which communicates over the SPMI bus. The is used to handle
+	  the PHY layer communication of the Power Delivery stack.
+
+endmenu
diff --git a/drivers/usb/pd/Makefile b/drivers/usb/pd/Makefile
new file mode 100644
index 0000000..f487070
--- /dev/null
+++ b/drivers/usb/pd/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for USB Power Delivery drivers
+#
+
+obj-$(CONFIG_USB_PD_POLICY)	+= policy_engine.o
+obj-$(CONFIG_QPNP_USB_PDPHY)	+= qpnp-pdphy.o
diff --git a/drivers/usb/pd/policy_engine.c b/drivers/usb/pd/policy_engine.c
new file mode 100644
index 0000000..2e731af
--- /dev/null
+++ b/drivers/usb/pd/policy_engine.c
@@ -0,0 +1,3250 @@
+/* Copyright (c) 2016-2017, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/hrtimer.h>
+#include <linux/ipc_logging.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/extcon.h>
+#include <linux/usb/class-dual-role.h>
+#include <linux/usb/usbpd.h>
+#include "usbpd.h"
+
+enum usbpd_state {
+	PE_UNKNOWN,
+	PE_ERROR_RECOVERY,
+	PE_SRC_DISABLED,
+	PE_SRC_STARTUP,
+	PE_SRC_SEND_CAPABILITIES,
+	PE_SRC_SEND_CAPABILITIES_WAIT, /* substate to wait for Request */
+	PE_SRC_NEGOTIATE_CAPABILITY,
+	PE_SRC_TRANSITION_SUPPLY,
+	PE_SRC_READY,
+	PE_SRC_HARD_RESET,
+	PE_SRC_SOFT_RESET,
+	PE_SRC_SEND_SOFT_RESET,
+	PE_SRC_DISCOVERY,
+	PE_SRC_TRANSITION_TO_DEFAULT,
+	PE_SNK_STARTUP,
+	PE_SNK_DISCOVERY,
+	PE_SNK_WAIT_FOR_CAPABILITIES,
+	PE_SNK_EVALUATE_CAPABILITY,
+	PE_SNK_SELECT_CAPABILITY,
+	PE_SNK_TRANSITION_SINK,
+	PE_SNK_READY,
+	PE_SNK_HARD_RESET,
+	PE_SNK_SOFT_RESET,
+	PE_SNK_SEND_SOFT_RESET,
+	PE_SNK_TRANSITION_TO_DEFAULT,
+	PE_DRS_SEND_DR_SWAP,
+	PE_PRS_SNK_SRC_SEND_SWAP,
+	PE_PRS_SNK_SRC_TRANSITION_TO_OFF,
+	PE_PRS_SNK_SRC_SOURCE_ON,
+	PE_PRS_SRC_SNK_SEND_SWAP,
+	PE_PRS_SRC_SNK_TRANSITION_TO_OFF,
+	PE_PRS_SRC_SNK_WAIT_SOURCE_ON,
+	PE_VCS_WAIT_FOR_VCONN,
+};
+
+static const char * const usbpd_state_strings[] = {
+	"UNKNOWN",
+	"ERROR_RECOVERY",
+	"SRC_Disabled",
+	"SRC_Startup",
+	"SRC_Send_Capabilities",
+	"SRC_Send_Capabilities (Wait for Request)",
+	"SRC_Negotiate_Capability",
+	"SRC_Transition_Supply",
+	"SRC_Ready",
+	"SRC_Hard_Reset",
+	"SRC_Soft_Reset",
+	"SRC_Send_Soft_Reset",
+	"SRC_Discovery",
+	"SRC_Transition_to_default",
+	"SNK_Startup",
+	"SNK_Discovery",
+	"SNK_Wait_for_Capabilities",
+	"SNK_Evaluate_Capability",
+	"SNK_Select_Capability",
+	"SNK_Transition_Sink",
+	"SNK_Ready",
+	"SNK_Hard_Reset",
+	"SNK_Soft_Reset",
+	"SNK_Send_Soft_Reset",
+	"SNK_Transition_to_default",
+	"DRS_Send_DR_Swap",
+	"PRS_SNK_SRC_Send_Swap",
+	"PRS_SNK_SRC_Transition_to_off",
+	"PRS_SNK_SRC_Source_on",
+	"PRS_SRC_SNK_Send_Swap",
+	"PRS_SRC_SNK_Transition_to_off",
+	"PRS_SRC_SNK_Wait_Source_on",
+	"VCS_Wait_for_VCONN",
+};
+
+enum usbpd_control_msg_type {
+	MSG_RESERVED = 0,
+	MSG_GOODCRC,
+	MSG_GOTOMIN,
+	MSG_ACCEPT,
+	MSG_REJECT,
+	MSG_PING,
+	MSG_PS_RDY,
+	MSG_GET_SOURCE_CAP,
+	MSG_GET_SINK_CAP,
+	MSG_DR_SWAP,
+	MSG_PR_SWAP,
+	MSG_VCONN_SWAP,
+	MSG_WAIT,
+	MSG_SOFT_RESET,
+};
+
+enum usbpd_data_msg_type {
+	MSG_SOURCE_CAPABILITIES = 1,
+	MSG_REQUEST,
+	MSG_BIST,
+	MSG_SINK_CAPABILITIES,
+	MSG_VDM = 0xF,
+};
+
+enum vdm_state {
+	VDM_NONE,
+	DISCOVERED_ID,
+	DISCOVERED_SVIDS,
+	DISCOVERED_MODES,
+	MODE_ENTERED,
+	MODE_EXITED,
+};
+
+static void *usbpd_ipc_log;
+#define usbpd_dbg(dev, fmt, ...) do { \
+	ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \
+			##__VA_ARGS__); \
+	dev_dbg(dev, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define usbpd_info(dev, fmt, ...) do { \
+	ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \
+			##__VA_ARGS__); \
+	dev_info(dev, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define usbpd_warn(dev, fmt, ...) do { \
+	ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \
+			##__VA_ARGS__); \
+	dev_warn(dev, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define usbpd_err(dev, fmt, ...) do { \
+	ipc_log_string(usbpd_ipc_log, "%s: %s: " fmt, dev_name(dev), __func__, \
+			##__VA_ARGS__); \
+	dev_err(dev, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define NUM_LOG_PAGES		10
+
+/* Timeouts (in ms) */
+#define ERROR_RECOVERY_TIME	25
+#define SENDER_RESPONSE_TIME	26
+#define SINK_WAIT_CAP_TIME	500
+#define PS_TRANSITION_TIME	450
+#define SRC_CAP_TIME		120
+#define SRC_TRANSITION_TIME	25
+#define SRC_RECOVER_TIME	750
+#define PS_HARD_RESET_TIME	25
+#define PS_SOURCE_ON		400
+#define PS_SOURCE_OFF		750
+#define SWAP_SOURCE_START_TIME	20
+#define VDM_BUSY_TIME		50
+#define VCONN_ON_TIME		100
+
+/* tPSHardReset + tSafe0V */
+#define SNK_HARD_RESET_VBUS_OFF_TIME	(35 + 650)
+
+/* tSrcRecover + tSrcTurnOn */
+#define SNK_HARD_RESET_VBUS_ON_TIME	(1000 + 275)
+
+#define PD_CAPS_COUNT		50
+
+#define PD_MAX_MSG_ID		7
+
+#define PD_MSG_HDR(type, dr, pr, id, cnt, rev) \
+	(((type) & 0xF) | ((dr) << 5) | (rev << 6) | \
+	 ((pr) << 8) | ((id) << 9) | ((cnt) << 12))
+#define PD_MSG_HDR_COUNT(hdr) (((hdr) >> 12) & 7)
+#define PD_MSG_HDR_TYPE(hdr) ((hdr) & 0xF)
+#define PD_MSG_HDR_ID(hdr) (((hdr) >> 9) & 7)
+#define PD_MSG_HDR_REV(hdr) (((hdr) >> 6) & 3)
+
+#define PD_RDO_FIXED(obj, gb, mismatch, usb_comm, no_usb_susp, curr1, curr2) \
+		(((obj) << 28) | ((gb) << 27) | ((mismatch) << 26) | \
+		 ((usb_comm) << 25) | ((no_usb_susp) << 24) | \
+		 ((curr1) << 10) | (curr2))
+
+#define PD_RDO_AUGMENTED(obj, mismatch, usb_comm, no_usb_susp, volt, curr) \
+		(((obj) << 28) | ((mismatch) << 26) | ((usb_comm) << 25) | \
+		 ((no_usb_susp) << 24) | ((volt) << 9) | (curr))
+
+#define PD_RDO_OBJ_POS(rdo)		((rdo) >> 28 & 7)
+#define PD_RDO_GIVEBACK(rdo)		((rdo) >> 27 & 1)
+#define PD_RDO_MISMATCH(rdo)		((rdo) >> 26 & 1)
+#define PD_RDO_USB_COMM(rdo)		((rdo) >> 25 & 1)
+#define PD_RDO_NO_USB_SUSP(rdo)		((rdo) >> 24 & 1)
+#define PD_RDO_FIXED_CURR(rdo)		((rdo) >> 10 & 0x3FF)
+#define PD_RDO_FIXED_CURR_MINMAX(rdo)	((rdo) & 0x3FF)
+#define PD_RDO_PROG_VOLTAGE(rdo)	((rdo) >> 9 & 0x7FF)
+#define PD_RDO_PROG_CURR(rdo)		((rdo) & 0x7F)
+
+#define PD_SRC_PDO_TYPE(pdo)		(((pdo) >> 30) & 3)
+#define PD_SRC_PDO_TYPE_FIXED		0
+#define PD_SRC_PDO_TYPE_BATTERY		1
+#define PD_SRC_PDO_TYPE_VARIABLE	2
+#define PD_SRC_PDO_TYPE_AUGMENTED	3
+
+#define PD_SRC_PDO_FIXED_PR_SWAP(pdo)		(((pdo) >> 29) & 1)
+#define PD_SRC_PDO_FIXED_USB_SUSP(pdo)		(((pdo) >> 28) & 1)
+#define PD_SRC_PDO_FIXED_EXT_POWERED(pdo)	(((pdo) >> 27) & 1)
+#define PD_SRC_PDO_FIXED_USB_COMM(pdo)		(((pdo) >> 26) & 1)
+#define PD_SRC_PDO_FIXED_DR_SWAP(pdo)		(((pdo) >> 25) & 1)
+#define PD_SRC_PDO_FIXED_PEAK_CURR(pdo)		(((pdo) >> 20) & 3)
+#define PD_SRC_PDO_FIXED_VOLTAGE(pdo)		(((pdo) >> 10) & 0x3FF)
+#define PD_SRC_PDO_FIXED_MAX_CURR(pdo)		((pdo) & 0x3FF)
+
+#define PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo)	(((pdo) >> 20) & 0x3FF)
+#define PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo)	(((pdo) >> 10) & 0x3FF)
+#define PD_SRC_PDO_VAR_BATT_MAX(pdo)		((pdo) & 0x3FF)
+
+#define PD_APDO_PPS(pdo)			(((pdo) >> 28) & 3)
+#define PD_APDO_MAX_VOLT(pdo)			(((pdo) >> 17) & 0xFF)
+#define PD_APDO_MIN_VOLT(pdo)			(((pdo) >> 8) & 0xFF)
+#define PD_APDO_MAX_CURR(pdo)			((pdo) & 0x7F)
+
+/* Vendor Defined Messages */
+#define MAX_CRC_RECEIVE_TIME	9 /* ~(2 * tReceive_max(1.1ms) * # retry 4) */
+#define MAX_VDM_RESPONSE_TIME	60 /* 2 * tVDMSenderResponse_max(30ms) */
+#define MAX_VDM_BUSY_TIME	100 /* 2 * tVDMBusy (50ms) */
+
+/* VDM header is the first 32-bit object following the 16-bit PD header */
+#define VDM_HDR_SVID(hdr)	((hdr) >> 16)
+#define VDM_IS_SVDM(hdr)	((hdr) & 0x8000)
+#define SVDM_HDR_OBJ_POS(hdr)	(((hdr) >> 8) & 0x7)
+#define SVDM_HDR_CMD_TYPE(hdr)	(((hdr) >> 6) & 0x3)
+#define SVDM_HDR_CMD(hdr)	((hdr) & 0x1f)
+
+#define SVDM_HDR(svid, ver, obj, cmd_type, cmd) \
+	(((svid) << 16) | (1 << 15) | ((ver) << 13) \
+	| ((obj) << 8) | ((cmd_type) << 6) | (cmd))
+
+/* discover id response vdo bit fields */
+#define ID_HDR_USB_HOST		BIT(31)
+#define ID_HDR_USB_DEVICE	BIT(30)
+#define ID_HDR_MODAL_OPR	BIT(26)
+#define ID_HDR_PRODUCT_TYPE(n)	((n) >> 27)
+#define ID_HDR_PRODUCT_PER_MASK	(2 << 27)
+#define ID_HDR_PRODUCT_HUB	1
+#define ID_HDR_PRODUCT_PER	2
+#define ID_HDR_PRODUCT_AMA	5
+#define ID_HDR_VID		0x05c6 /* qcom */
+#define PROD_VDO_PID		0x0a00 /* TBD */
+
+static bool check_vsafe0v = true;
+module_param(check_vsafe0v, bool, 0600);
+
+static int min_sink_current = 900;
+module_param(min_sink_current, int, 0600);
+
+static const u32 default_src_caps[] = { 0x36019096 };	/* VSafe5V @ 1.5A */
+static const u32 default_snk_caps[] = { 0x2601905A };	/* 5V @ 900mA */
+
+struct vdm_tx {
+	u32			data[7];
+	int			size;
+};
+
+struct rx_msg {
+	u8			type;
+	u8			len;
+	u32			payload[7];
+	struct list_head	entry;
+};
+
+#define IS_DATA(m, t) ((m) && ((m)->len) && ((m)->type == (t)))
+#define IS_CTRL(m, t) ((m) && !((m)->len) && ((m)->type == (t)))
+
+struct usbpd {
+	struct device		dev;
+	struct workqueue_struct	*wq;
+	struct work_struct	sm_work;
+	struct hrtimer		timer;
+	bool			sm_queued;
+
+	struct extcon_dev	*extcon;
+
+	enum usbpd_state	current_state;
+	bool			hard_reset_recvd;
+	struct list_head	rx_q;
+	spinlock_t		rx_lock;
+
+	u32			received_pdos[7];
+	int			src_cap_id;
+	u8			selected_pdo;
+	u8			requested_pdo;
+	u32			rdo;	/* can be either source or sink */
+	int			current_voltage;	/* uV */
+	int			requested_voltage;	/* uV */
+	int			requested_current;	/* mA */
+	bool			pd_connected;
+	bool			in_explicit_contract;
+	bool			peer_usb_comm;
+	bool			peer_pr_swap;
+	bool			peer_dr_swap;
+
+	struct power_supply	*usb_psy;
+	struct notifier_block	psy_nb;
+
+	enum power_supply_typec_mode typec_mode;
+	enum power_supply_type	psy_type;
+	enum power_supply_typec_power_role forced_pr;
+	bool			vbus_present;
+
+	enum pd_spec_rev	spec_rev;
+	enum data_role		current_dr;
+	enum power_role		current_pr;
+	bool			in_pr_swap;
+	bool			pd_phy_opened;
+	struct completion	swap_complete;
+
+	struct dual_role_phy_instance	*dual_role;
+	struct dual_role_phy_desc	dr_desc;
+	bool			send_pr_swap;
+	bool			send_dr_swap;
+
+	struct regulator	*vbus;
+	struct regulator	*vconn;
+	bool			vbus_enabled;
+	bool			vconn_enabled;
+	bool			vconn_is_external;
+
+	u8			tx_msgid;
+	u8			rx_msgid;
+	int			caps_count;
+	int			hard_reset_count;
+
+	enum vdm_state		vdm_state;
+	u16			*discovered_svids;
+	int			num_svids;
+	struct vdm_tx		*vdm_tx;
+	struct vdm_tx		*vdm_tx_retry;
+	struct list_head	svid_handlers;
+
+	struct list_head	instance;
+};
+
+static LIST_HEAD(_usbpd);	/* useful for debugging */
+
+static const unsigned int usbpd_extcon_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_USB_CC,
+	EXTCON_USB_SPEED,
+	EXTCON_NONE,
+};
+
+/* EXTCON_USB and EXTCON_USB_HOST are mutually exclusive */
+static const u32 usbpd_extcon_exclusive[] = {0x3, 0};
+
+enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd)
+{
+	int ret;
+	union power_supply_propval val;
+
+	ret = power_supply_get_property(pd->usb_psy,
+		POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, &val);
+	if (ret)
+		return ORIENTATION_NONE;
+
+	return val.intval;
+}
+EXPORT_SYMBOL(usbpd_get_plug_orientation);
+
+static inline void stop_usb_host(struct usbpd *pd)
+{
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 0);
+}
+
+static inline void start_usb_host(struct usbpd *pd, bool ss)
+{
+	enum plug_orientation cc = usbpd_get_plug_orientation(pd);
+
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
+			cc == ORIENTATION_CC2);
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, ss);
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB_HOST, 1);
+}
+
+static inline void stop_usb_peripheral(struct usbpd *pd)
+{
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
+}
+
+static inline void start_usb_peripheral(struct usbpd *pd)
+{
+	enum plug_orientation cc = usbpd_get_plug_orientation(pd);
+
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB_CC,
+			cc == ORIENTATION_CC2);
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB_SPEED, 1);
+	extcon_set_cable_state_(pd->extcon, EXTCON_USB, 1);
+}
+
+static int set_power_role(struct usbpd *pd, enum power_role pr)
+{
+	union power_supply_propval val = {0};
+
+	switch (pr) {
+	case PR_NONE:
+		val.intval = POWER_SUPPLY_TYPEC_PR_NONE;
+		break;
+	case PR_SINK:
+		val.intval = POWER_SUPPLY_TYPEC_PR_SINK;
+		break;
+	case PR_SRC:
+		val.intval = POWER_SUPPLY_TYPEC_PR_SOURCE;
+		break;
+	}
+
+	return power_supply_set_property(pd->usb_psy,
+			POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
+}
+
+static struct usbpd_svid_handler *find_svid_handler(struct usbpd *pd, u16 svid)
+{
+	struct usbpd_svid_handler *handler;
+
+	list_for_each_entry(handler, &pd->svid_handlers, entry)
+		if (svid == handler->svid)
+			return handler;
+
+	return NULL;
+}
+
+/* Reset protocol layer */
+static inline void pd_reset_protocol(struct usbpd *pd)
+{
+	/*
+	 * first Rx ID should be 0; set this to a sentinel of -1 so that in
+	 * phy_msg_received() we can check if we had seen it before.
+	 */
+	pd->rx_msgid = -1;
+	pd->tx_msgid = 0;
+}
+
+static int pd_send_msg(struct usbpd *pd, u8 hdr_type, const u32 *data,
+		size_t num_data, enum pd_msg_type type)
+{
+	int ret;
+	u16 hdr;
+
+	hdr = PD_MSG_HDR(hdr_type, pd->current_dr, pd->current_pr,
+			pd->tx_msgid, num_data, pd->spec_rev);
+	ret = pd_phy_write(hdr, (u8 *)data, num_data * sizeof(u32), type, 15);
+	/* TODO figure out timeout. based on tReceive=1.1ms x nRetryCount? */
+
+	/* MessageID incremented regardless of Tx error */
+	pd->tx_msgid = (pd->tx_msgid + 1) & PD_MAX_MSG_ID;
+
+	if (ret < 0)
+		return ret;
+	else if (ret != num_data * sizeof(u32))
+		return -EIO;
+	return 0;
+}
+
+static int pd_select_pdo(struct usbpd *pd, int pdo_pos, int uv, int ua)
+{
+	int curr;
+	int max_current;
+	bool mismatch = false;
+	u8 type;
+	u32 pdo = pd->received_pdos[pdo_pos - 1];
+
+	type = PD_SRC_PDO_TYPE(pdo);
+	if (type == PD_SRC_PDO_TYPE_FIXED) {
+		curr = max_current = PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10;
+
+		/*
+		 * Check if the PDO has enough current, otherwise set the
+		 * Capability Mismatch flag
+		 */
+		if (curr < min_sink_current) {
+			mismatch = true;
+			max_current = min_sink_current;
+		}
+
+		pd->requested_voltage =
+			PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50 * 1000;
+		pd->rdo = PD_RDO_FIXED(pdo_pos, 0, mismatch, 1, 1, curr / 10,
+				max_current / 10);
+	} else if (type == PD_SRC_PDO_TYPE_AUGMENTED) {
+		if ((uv / 100000) > PD_APDO_MAX_VOLT(pdo) ||
+			(uv / 100000) < PD_APDO_MIN_VOLT(pdo) ||
+			(ua / 50000) > PD_APDO_MAX_CURR(pdo) || (ua < 0)) {
+			usbpd_err(&pd->dev, "uv (%d) and ua (%d) out of range of APDO\n",
+					uv, ua);
+			return -EINVAL;
+		}
+
+		curr = ua / 1000;
+		pd->requested_voltage = uv;
+		pd->rdo = PD_RDO_AUGMENTED(pdo_pos, mismatch, 1, 1,
+				uv / 20000, ua / 50000);
+	} else {
+		usbpd_err(&pd->dev, "Only Fixed or Programmable PDOs supported\n");
+		return -ENOTSUPP;
+	}
+
+	/* Can't sink more than 5V if VCONN is sourced from the VBUS input */
+	if (pd->vconn_enabled && !pd->vconn_is_external &&
+			pd->requested_voltage > 5000000)
+		return -ENOTSUPP;
+
+	pd->requested_current = curr;
+	pd->requested_pdo = pdo_pos;
+
+	return 0;
+}
+
+static int pd_eval_src_caps(struct usbpd *pd)
+{
+	union power_supply_propval val;
+	u32 first_pdo = pd->received_pdos[0];
+
+	if (PD_SRC_PDO_TYPE(first_pdo) != PD_SRC_PDO_TYPE_FIXED) {
+		usbpd_err(&pd->dev, "First src_cap invalid! %08x\n", first_pdo);
+		return -EINVAL;
+	}
+
+	pd->peer_usb_comm = PD_SRC_PDO_FIXED_USB_COMM(first_pdo);
+	pd->peer_pr_swap = PD_SRC_PDO_FIXED_PR_SWAP(first_pdo);
+	pd->peer_dr_swap = PD_SRC_PDO_FIXED_DR_SWAP(first_pdo);
+
+	val.intval = PD_SRC_PDO_FIXED_USB_SUSP(first_pdo);
+	power_supply_set_property(pd->usb_psy,
+			POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED, &val);
+
+	/* Select the first PDO (vSafe5V) immediately. */
+	pd_select_pdo(pd, 1, 0, 0);
+
+	return 0;
+}
+
+static void pd_send_hard_reset(struct usbpd *pd)
+{
+	usbpd_dbg(&pd->dev, "send hard reset");
+
+	/* Force CC logic to source/sink to keep Rp/Rd unchanged */
+	set_power_role(pd, pd->current_pr);
+	pd->hard_reset_count++;
+	pd_phy_signal(HARD_RESET_SIG, 5); /* tHardResetComplete */
+	pd->in_pr_swap = false;
+}
+
+static void kick_sm(struct usbpd *pd, int ms)
+{
+	pm_stay_awake(&pd->dev);
+	pd->sm_queued = true;
+
+	if (ms)
+		hrtimer_start(&pd->timer, ms_to_ktime(ms), HRTIMER_MODE_REL);
+	else
+		queue_work(pd->wq, &pd->sm_work);
+}
+
+static void phy_sig_received(struct usbpd *pd, enum pd_sig_type type)
+{
+	if (type != HARD_RESET_SIG) {
+		usbpd_err(&pd->dev, "invalid signal (%d) received\n", type);
+		return;
+	}
+
+	usbpd_dbg(&pd->dev, "hard reset received\n");
+
+	/* Force CC logic to source/sink to keep Rp/Rd unchanged */
+	set_power_role(pd, pd->current_pr);
+	pd->hard_reset_recvd = true;
+	kick_sm(pd, 0);
+}
+
+static void phy_msg_received(struct usbpd *pd, enum pd_msg_type type,
+		u8 *buf, size_t len)
+{
+	struct rx_msg *rx_msg;
+	unsigned long flags;
+	u16 header;
+
+	if (type != SOP_MSG) {
+		usbpd_err(&pd->dev, "invalid msg type (%d) received; only SOP supported\n",
+				type);
+		return;
+	}
+
+	if (len < 2) {
+		usbpd_err(&pd->dev, "invalid message received, len=%zd\n", len);
+		return;
+	}
+
+	header = *((u16 *)buf);
+	buf += sizeof(u16);
+	len -= sizeof(u16);
+
+	if (len % 4 != 0) {
+		usbpd_err(&pd->dev, "len=%zd not multiple of 4\n", len);
+		return;
+	}
+
+	/* if MSGID already seen, discard */
+	if (PD_MSG_HDR_ID(header) == pd->rx_msgid &&
+			PD_MSG_HDR_TYPE(header) != MSG_SOFT_RESET) {
+		usbpd_dbg(&pd->dev, "MessageID already seen, discarding\n");
+		return;
+	}
+
+	pd->rx_msgid = PD_MSG_HDR_ID(header);
+
+	/* discard Pings */
+	if (PD_MSG_HDR_TYPE(header) == MSG_PING && !len)
+		return;
+
+	/* check header's count field to see if it matches len */
+	if (PD_MSG_HDR_COUNT(header) != (len / 4)) {
+		usbpd_err(&pd->dev, "header count (%d) mismatch, len=%zd\n",
+				PD_MSG_HDR_COUNT(header), len);
+		return;
+	}
+
+	/* if spec rev differs (i.e. is older), update PHY */
+	if (PD_MSG_HDR_REV(header) < pd->spec_rev) {
+		pd->spec_rev = PD_MSG_HDR_REV(header);
+		pd_phy_update_spec_rev(pd->spec_rev);
+	}
+
+	rx_msg = kzalloc(sizeof(*rx_msg), GFP_KERNEL);
+	if (!rx_msg)
+		return;
+
+	rx_msg->type = PD_MSG_HDR_TYPE(header);
+	rx_msg->len = PD_MSG_HDR_COUNT(header);
+	memcpy(&rx_msg->payload, buf, min(len, sizeof(rx_msg->payload)));
+
+	spin_lock_irqsave(&pd->rx_lock, flags);
+	list_add_tail(&rx_msg->entry, &pd->rx_q);
+	spin_unlock_irqrestore(&pd->rx_lock, flags);
+
+	usbpd_dbg(&pd->dev, "received message: type(%d) len(%d)\n",
+			rx_msg->type, rx_msg->len);
+
+	kick_sm(pd, 0);
+}
+
+static void phy_shutdown(struct usbpd *pd)
+{
+	usbpd_dbg(&pd->dev, "shutdown");
+}
+
+static enum hrtimer_restart pd_timeout(struct hrtimer *timer)
+{
+	struct usbpd *pd = container_of(timer, struct usbpd, timer);
+
+	usbpd_dbg(&pd->dev, "timeout");
+	queue_work(pd->wq, &pd->sm_work);
+
+	return HRTIMER_NORESTART;
+}
+
+/* Enters new state and executes actions on entry */
+static void usbpd_set_state(struct usbpd *pd, enum usbpd_state next_state)
+{
+	struct pd_phy_params phy_params = {
+		.signal_cb		= phy_sig_received,
+		.msg_rx_cb		= phy_msg_received,
+		.shutdown_cb		= phy_shutdown,
+		.frame_filter_val	= FRAME_FILTER_EN_SOP |
+					  FRAME_FILTER_EN_HARD_RESET,
+		.spec_rev		= USBPD_REV_20,
+	};
+	union power_supply_propval val = {0};
+	unsigned long flags;
+	int ret;
+
+	usbpd_dbg(&pd->dev, "%s -> %s\n",
+			usbpd_state_strings[pd->current_state],
+			usbpd_state_strings[next_state]);
+
+	pd->current_state = next_state;
+
+	switch (next_state) {
+	case PE_ERROR_RECOVERY: /* perform hard disconnect/reconnect */
+		pd->in_pr_swap = false;
+		pd->current_pr = PR_NONE;
+		set_power_role(pd, PR_NONE);
+		pd->typec_mode = POWER_SUPPLY_TYPEC_NONE;
+		kick_sm(pd, 0);
+		break;
+
+	/* Source states */
+	case PE_SRC_STARTUP:
+		if (pd->current_dr == DR_NONE) {
+			pd->current_dr = DR_DFP;
+			/*
+			 * Defer starting USB host mode until PE_SRC_READY or
+			 * when PE_SRC_SEND_CAPABILITIES fails
+			 */
+		}
+
+		dual_role_instance_changed(pd->dual_role);
+
+		/* Set CC back to DRP toggle for the next disconnect */
+		val.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
+
+		/* support only PD 2.0 as a source */
+		pd->spec_rev = USBPD_REV_20;
+		pd_reset_protocol(pd);
+
+		if (!pd->in_pr_swap) {
+			if (pd->pd_phy_opened) {
+				pd_phy_close();
+				pd->pd_phy_opened = false;
+			}
+
+			phy_params.data_role = pd->current_dr;
+			phy_params.power_role = pd->current_pr;
+			phy_params.spec_rev = pd->spec_rev;
+
+			ret = pd_phy_open(&phy_params);
+			if (ret) {
+				WARN_ON_ONCE(1);
+				usbpd_err(&pd->dev, "error opening PD PHY %d\n",
+						ret);
+				pd->current_state = PE_UNKNOWN;
+				return;
+			}
+
+			pd->pd_phy_opened = true;
+		} else {
+			pd_phy_update_spec_rev(pd->spec_rev);
+		}
+
+		pd->current_state = PE_SRC_SEND_CAPABILITIES;
+		if (pd->in_pr_swap) {
+			kick_sm(pd, SWAP_SOURCE_START_TIME);
+			pd->in_pr_swap = false;
+			break;
+		}
+
+		/* fall-through */
+
+	case PE_SRC_SEND_CAPABILITIES:
+		kick_sm(pd, 0);
+		break;
+
+	case PE_SRC_NEGOTIATE_CAPABILITY:
+		if (PD_RDO_OBJ_POS(pd->rdo) != 1 ||
+			PD_RDO_FIXED_CURR(pd->rdo) >
+				PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps) ||
+			PD_RDO_FIXED_CURR_MINMAX(pd->rdo) >
+				PD_SRC_PDO_FIXED_MAX_CURR(*default_src_caps)) {
+			/* send Reject */
+			ret = pd_send_msg(pd, MSG_REJECT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Reject\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			usbpd_err(&pd->dev, "Invalid request: %08x\n", pd->rdo);
+
+			if (pd->in_explicit_contract)
+				usbpd_set_state(pd, PE_SRC_READY);
+			else
+				/*
+				 * bypass PE_SRC_Capability_Response and
+				 * PE_SRC_Wait_New_Capabilities in this
+				 * implementation for simplicity.
+				 */
+				usbpd_set_state(pd, PE_SRC_SEND_CAPABILITIES);
+			break;
+		}
+
+		/* PE_SRC_TRANSITION_SUPPLY pseudo-state */
+		ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending Accept\n");
+			usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+			break;
+		}
+
+		/* tSrcTransition required after ACCEPT */
+		usleep_range(SRC_TRANSITION_TIME * USEC_PER_MSEC,
+				(SRC_TRANSITION_TIME + 5) * USEC_PER_MSEC);
+
+		/*
+		 * Normally a voltage change should occur within tSrcReady
+		 * but since we only support VSafe5V there is nothing more to
+		 * prepare from the power supply so send PS_RDY right away.
+		 */
+		ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending PS_RDY\n");
+			usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+			break;
+		}
+
+		usbpd_set_state(pd, PE_SRC_READY);
+		break;
+
+	case PE_SRC_READY:
+		pd->in_explicit_contract = true;
+		if (pd->current_dr == DR_DFP) {
+			/* don't start USB host until after SVDM discovery */
+			if (pd->vdm_state == VDM_NONE)
+				usbpd_send_svdm(pd, USBPD_SID,
+						USBPD_SVDM_DISCOVER_IDENTITY,
+						SVDM_CMD_TYPE_INITIATOR, 0,
+						NULL, 0);
+		}
+
+		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
+		complete(&pd->swap_complete);
+		dual_role_instance_changed(pd->dual_role);
+		break;
+
+	case PE_SRC_HARD_RESET:
+	case PE_SNK_HARD_RESET:
+		/* hard reset may sleep; handle it in the workqueue */
+		kick_sm(pd, 0);
+		break;
+
+	case PE_SRC_SEND_SOFT_RESET:
+	case PE_SNK_SEND_SOFT_RESET:
+		pd_reset_protocol(pd);
+
+		ret = pd_send_msg(pd, MSG_SOFT_RESET, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending Soft Reset, do Hard Reset\n");
+			usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
+			break;
+		}
+
+		/* wait for ACCEPT */
+		kick_sm(pd, SENDER_RESPONSE_TIME);
+		break;
+
+	/* Sink states */
+	case PE_SNK_STARTUP:
+		if (pd->current_dr == DR_NONE || pd->current_dr == DR_UFP) {
+			pd->current_dr = DR_UFP;
+
+			if (pd->psy_type == POWER_SUPPLY_TYPE_USB ||
+				pd->psy_type == POWER_SUPPLY_TYPE_USB_CDP)
+				start_usb_peripheral(pd);
+		}
+
+		dual_role_instance_changed(pd->dual_role);
+
+		ret = power_supply_get_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_ALLOWED, &val);
+		if (ret) {
+			usbpd_err(&pd->dev, "Unable to read USB PROP_PD_ALLOWED: %d\n",
+					ret);
+			break;
+		}
+
+		if (!val.intval)
+			break;
+
+		/*
+		 * support up to PD 3.0 as a sink; if source is 2.0,
+		 * phy_msg_received() will handle the downgrade.
+		 */
+		pd->spec_rev = USBPD_REV_30;
+		pd_reset_protocol(pd);
+
+		if (!pd->in_pr_swap) {
+			if (pd->pd_phy_opened) {
+				pd_phy_close();
+				pd->pd_phy_opened = false;
+			}
+
+			phy_params.data_role = pd->current_dr;
+			phy_params.power_role = pd->current_pr;
+			phy_params.spec_rev = pd->spec_rev;
+
+			ret = pd_phy_open(&phy_params);
+			if (ret) {
+				WARN_ON_ONCE(1);
+				usbpd_err(&pd->dev, "error opening PD PHY %d\n",
+						ret);
+				pd->current_state = PE_UNKNOWN;
+				return;
+			}
+
+			pd->pd_phy_opened = true;
+		} else {
+			pd_phy_update_spec_rev(pd->spec_rev);
+		}
+
+		pd->current_voltage = pd->requested_voltage = 5000000;
+		val.intval = pd->requested_voltage; /* set max range to 5V */
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
+
+		if (!pd->vbus_present) {
+			pd->current_state = PE_SNK_DISCOVERY;
+			/* max time for hard reset to turn vbus back on */
+			kick_sm(pd, SNK_HARD_RESET_VBUS_ON_TIME);
+			break;
+		}
+
+		pd->current_state = PE_SNK_WAIT_FOR_CAPABILITIES;
+		/* fall-through */
+
+	case PE_SNK_WAIT_FOR_CAPABILITIES:
+		spin_lock_irqsave(&pd->rx_lock, flags);
+		if (list_empty(&pd->rx_q))
+			kick_sm(pd, SINK_WAIT_CAP_TIME);
+		spin_unlock_irqrestore(&pd->rx_lock, flags);
+		break;
+
+	case PE_SNK_EVALUATE_CAPABILITY:
+		pd->pd_connected = true; /* we know peer is PD capable */
+		pd->hard_reset_count = 0;
+
+		/* evaluate PDOs and select one */
+		ret = pd_eval_src_caps(pd);
+		if (ret < 0) {
+			usbpd_err(&pd->dev, "Invalid src_caps received. Skipping request\n");
+			break;
+		}
+		pd->current_state = PE_SNK_SELECT_CAPABILITY;
+		/* fall-through */
+
+	case PE_SNK_SELECT_CAPABILITY:
+		ret = pd_send_msg(pd, MSG_REQUEST, &pd->rdo, 1, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending Request\n");
+			usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+			break;
+		}
+
+		/* wait for ACCEPT */
+		kick_sm(pd, SENDER_RESPONSE_TIME);
+		break;
+
+	case PE_SNK_TRANSITION_SINK:
+		/* wait for PS_RDY */
+		kick_sm(pd, PS_TRANSITION_TIME);
+		break;
+
+	case PE_SNK_READY:
+		pd->in_explicit_contract = true;
+		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
+		complete(&pd->swap_complete);
+		dual_role_instance_changed(pd->dual_role);
+		break;
+
+	case PE_SNK_TRANSITION_TO_DEFAULT:
+		if (pd->current_dr != DR_UFP) {
+			stop_usb_host(pd);
+			start_usb_peripheral(pd);
+			pd->current_dr = DR_UFP;
+			pd_phy_update_roles(pd->current_dr, pd->current_pr);
+		}
+		if (pd->vconn_enabled) {
+			regulator_disable(pd->vconn);
+			pd->vconn_enabled = false;
+		}
+
+		/* max time for hard reset to turn vbus off */
+		kick_sm(pd, SNK_HARD_RESET_VBUS_OFF_TIME);
+		break;
+
+	case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
+		val.intval = pd->requested_current = 0; /* suspend charging */
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+
+		pd->in_explicit_contract = false;
+
+		/*
+		 * need to update PR bit in message header so that
+		 * proper GoodCRC is sent when receiving next PS_RDY
+		 */
+		pd_phy_update_roles(pd->current_dr, PR_SRC);
+
+		/* wait for PS_RDY */
+		kick_sm(pd, PS_SOURCE_OFF);
+		break;
+
+	default:
+		usbpd_dbg(&pd->dev, "No action for state %s\n",
+				usbpd_state_strings[pd->current_state]);
+		break;
+	}
+}
+
+int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
+{
+	if (find_svid_handler(pd, hdlr->svid)) {
+		usbpd_err(&pd->dev, "SVID 0x%04x already registered\n",
+				hdlr->svid);
+		return -EINVAL;
+	}
+
+	/* require connect/disconnect callbacks be implemented */
+	if (!hdlr->connect || !hdlr->disconnect) {
+		usbpd_err(&pd->dev, "SVID 0x%04x connect/disconnect must be non-NULL\n",
+				hdlr->svid);
+		return -EINVAL;
+	}
+
+	usbpd_dbg(&pd->dev, "registered handler for SVID 0x%04x\n", hdlr->svid);
+
+	list_add_tail(&hdlr->entry, &pd->svid_handlers);
+
+	/* already connected with this SVID discovered? */
+	if (pd->vdm_state >= DISCOVERED_SVIDS) {
+		int i;
+
+		for (i = 0; i < pd->num_svids; i++) {
+			if (pd->discovered_svids[i] == hdlr->svid) {
+				hdlr->connect(hdlr);
+				hdlr->discovered = true;
+				break;
+			}
+		}
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(usbpd_register_svid);
+
+void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr)
+{
+	list_del_init(&hdlr->entry);
+}
+EXPORT_SYMBOL(usbpd_unregister_svid);
+
+int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos, int num_vdos)
+{
+	struct vdm_tx *vdm_tx;
+
+	if (!pd->in_explicit_contract || pd->vdm_tx)
+		return -EBUSY;
+
+	vdm_tx = kzalloc(sizeof(*vdm_tx), GFP_KERNEL);
+	if (!vdm_tx)
+		return -ENOMEM;
+
+	vdm_tx->data[0] = vdm_hdr;
+	if (vdos && num_vdos)
+		memcpy(&vdm_tx->data[1], vdos, num_vdos * sizeof(u32));
+	vdm_tx->size = num_vdos + 1; /* include the header */
+
+	/* VDM will get sent in PE_SRC/SNK_READY state handling */
+	pd->vdm_tx = vdm_tx;
+
+	/* slight delay before queuing to prioritize handling of incoming VDM */
+	kick_sm(pd, 2);
+
+	return 0;
+}
+EXPORT_SYMBOL(usbpd_send_vdm);
+
+int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
+		enum usbpd_svdm_cmd_type cmd_type, int obj_pos,
+		const u32 *vdos, int num_vdos)
+{
+	u32 svdm_hdr = SVDM_HDR(svid, 0, obj_pos, cmd_type, cmd);
+
+	usbpd_dbg(&pd->dev, "VDM tx: svid:%x cmd:%x cmd_type:%x svdm_hdr:%x\n",
+			svid, cmd, cmd_type, svdm_hdr);
+
+	return usbpd_send_vdm(pd, svdm_hdr, vdos, num_vdos);
+}
+EXPORT_SYMBOL(usbpd_send_svdm);
+
+static void handle_vdm_rx(struct usbpd *pd, struct rx_msg *rx_msg)
+{
+	u32 vdm_hdr = rx_msg->payload[0];
+	u32 *vdos = &rx_msg->payload[1];
+	u16 svid = VDM_HDR_SVID(vdm_hdr);
+	u16 *psvid;
+	u8 i, num_vdos = rx_msg->len - 1;	/* num objects minus header */
+	u8 cmd = SVDM_HDR_CMD(vdm_hdr);
+	u8 cmd_type = SVDM_HDR_CMD_TYPE(vdm_hdr);
+	bool has_dp = false;
+	struct usbpd_svid_handler *handler;
+
+	usbpd_dbg(&pd->dev, "VDM rx: svid:%x cmd:%x cmd_type:%x vdm_hdr:%x\n",
+			svid, cmd, cmd_type, vdm_hdr);
+
+	/* if it's a supported SVID, pass the message to the handler */
+	handler = find_svid_handler(pd, svid);
+
+	/* Unstructured VDM */
+	if (!VDM_IS_SVDM(vdm_hdr)) {
+		if (handler && handler->vdm_received)
+			handler->vdm_received(handler, vdm_hdr, vdos, num_vdos);
+		return;
+	}
+
+	/* if this interrupts a previous exchange, abort queued response */
+	if (cmd_type == SVDM_CMD_TYPE_INITIATOR && pd->vdm_tx) {
+		usbpd_dbg(&pd->dev, "Discarding previously queued SVDM tx (SVID:0x%04x)\n",
+				VDM_HDR_SVID(pd->vdm_tx->data[0]));
+
+		kfree(pd->vdm_tx);
+		pd->vdm_tx = NULL;
+	}
+
+	if (handler && handler->svdm_received) {
+		handler->svdm_received(handler, cmd, cmd_type, vdos, num_vdos);
+		return;
+	}
+
+	/* Standard Discovery or unhandled messages go here */
+	switch (cmd_type) {
+	case SVDM_CMD_TYPE_INITIATOR:
+		if (svid == USBPD_SID && cmd == USBPD_SVDM_DISCOVER_IDENTITY) {
+			u32 tx_vdos[3] = {
+				ID_HDR_USB_HOST | ID_HDR_USB_DEVICE |
+					ID_HDR_PRODUCT_PER_MASK | ID_HDR_VID,
+				0x0, /* TBD: Cert Stat VDO */
+				(PROD_VDO_PID << 16),
+				/* TBD: Get these from gadget */
+			};
+
+			usbpd_send_svdm(pd, USBPD_SID, cmd,
+					SVDM_CMD_TYPE_RESP_ACK, 0, tx_vdos, 3);
+		} else if (cmd != USBPD_SVDM_ATTENTION) {
+			usbpd_send_svdm(pd, svid, cmd, SVDM_CMD_TYPE_RESP_NAK,
+					SVDM_HDR_OBJ_POS(vdm_hdr), NULL, 0);
+		}
+		break;
+
+	case SVDM_CMD_TYPE_RESP_ACK:
+		if (svid != USBPD_SID) {
+			usbpd_err(&pd->dev, "unhandled ACK for SVID:0x%x\n",
+					svid);
+			break;
+		}
+
+		switch (cmd) {
+		case USBPD_SVDM_DISCOVER_IDENTITY:
+			kfree(pd->vdm_tx_retry);
+			pd->vdm_tx_retry = NULL;
+
+			pd->vdm_state = DISCOVERED_ID;
+			usbpd_send_svdm(pd, USBPD_SID,
+					USBPD_SVDM_DISCOVER_SVIDS,
+					SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
+			break;
+
+		case USBPD_SVDM_DISCOVER_SVIDS:
+			pd->vdm_state = DISCOVERED_SVIDS;
+
+			kfree(pd->vdm_tx_retry);
+			pd->vdm_tx_retry = NULL;
+
+			if (!pd->discovered_svids) {
+				pd->num_svids = 2 * num_vdos;
+				pd->discovered_svids = kcalloc(pd->num_svids,
+								sizeof(u16),
+								GFP_KERNEL);
+				if (!pd->discovered_svids) {
+					usbpd_err(&pd->dev, "unable to allocate SVIDs\n");
+					break;
+				}
+
+				psvid = pd->discovered_svids;
+			} else { /* handle > 12 SVIDs */
+				void *ptr;
+				size_t oldsize = pd->num_svids * sizeof(u16);
+				size_t newsize = oldsize +
+						(2 * num_vdos * sizeof(u16));
+
+				ptr = krealloc(pd->discovered_svids, newsize,
+						GFP_KERNEL);
+				if (!ptr) {
+					usbpd_err(&pd->dev, "unable to realloc SVIDs\n");
+					break;
+				}
+
+				pd->discovered_svids = ptr;
+				psvid = pd->discovered_svids + pd->num_svids;
+				memset(psvid, 0, (2 * num_vdos));
+				pd->num_svids += 2 * num_vdos;
+			}
+
+			/* convert 32-bit VDOs to list of 16-bit SVIDs */
+			for (i = 0; i < num_vdos * 2; i++) {
+				/*
+				 * Within each 32-bit VDO,
+				 *    SVID[i]: upper 16-bits
+				 *    SVID[i+1]: lower 16-bits
+				 * where i is even.
+				 */
+				if (!(i & 1))
+					svid = vdos[i >> 1] >> 16;
+				else
+					svid = vdos[i >> 1] & 0xFFFF;
+
+				/*
+				 * There are some devices that incorrectly
+				 * swap the order of SVIDs within a VDO. So in
+				 * case of an odd-number of SVIDs it could end
+				 * up with SVID[i] as 0 while SVID[i+1] is
+				 * non-zero. Just skip over the zero ones.
+				 */
+				if (svid) {
+					usbpd_dbg(&pd->dev, "Discovered SVID: 0x%04x\n",
+							svid);
+					*psvid++ = svid;
+				}
+			}
+
+			/* if more than 12 SVIDs, resend the request */
+			if (num_vdos == 6 && vdos[5] != 0) {
+				usbpd_send_svdm(pd, USBPD_SID,
+						USBPD_SVDM_DISCOVER_SVIDS,
+						SVDM_CMD_TYPE_INITIATOR, 0,
+						NULL, 0);
+				break;
+			}
+
+			/* now that all SVIDs are discovered, notify handlers */
+			for (i = 0; i < pd->num_svids; i++) {
+				svid = pd->discovered_svids[i];
+				if (svid) {
+					handler = find_svid_handler(pd, svid);
+					if (handler) {
+						handler->connect(handler);
+						handler->discovered = true;
+					}
+				}
+
+				if (svid == 0xFF01)
+					has_dp = true;
+			}
+
+			/*
+			 * Finally start USB host now that we have determined
+			 * if DisplayPort mode is present or not and limit USB
+			 * to HS-only mode if so.
+			 */
+			start_usb_host(pd, !has_dp);
+
+			break;
+
+		default:
+			usbpd_dbg(&pd->dev, "unhandled ACK for command:0x%x\n",
+					cmd);
+			break;
+		}
+		break;
+
+	case SVDM_CMD_TYPE_RESP_NAK:
+		usbpd_info(&pd->dev, "VDM NAK received for SVID:0x%04x command:0x%x\n",
+				svid, cmd);
+
+		switch (cmd) {
+		case USBPD_SVDM_DISCOVER_IDENTITY:
+		case USBPD_SVDM_DISCOVER_SVIDS:
+			start_usb_host(pd, true);
+			break;
+		default:
+			break;
+		}
+
+		break;
+
+	case SVDM_CMD_TYPE_RESP_BUSY:
+		switch (cmd) {
+		case USBPD_SVDM_DISCOVER_IDENTITY:
+		case USBPD_SVDM_DISCOVER_SVIDS:
+			if (!pd->vdm_tx_retry) {
+				usbpd_err(&pd->dev, "Discover command %d VDM was unexpectedly freed\n",
+						cmd);
+				break;
+			}
+
+			/* wait tVDMBusy, then retry */
+			pd->vdm_tx = pd->vdm_tx_retry;
+			pd->vdm_tx_retry = NULL;
+			kick_sm(pd, VDM_BUSY_TIME);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+}
+
+static void handle_vdm_tx(struct usbpd *pd)
+{
+	int ret;
+	unsigned long flags;
+
+	/* only send one VDM at a time */
+	if (pd->vdm_tx) {
+		u32 vdm_hdr = pd->vdm_tx->data[0];
+
+		/* bail out and try again later if a message just arrived */
+		spin_lock_irqsave(&pd->rx_lock, flags);
+		if (!list_empty(&pd->rx_q)) {
+			spin_unlock_irqrestore(&pd->rx_lock, flags);
+			return;
+		}
+		spin_unlock_irqrestore(&pd->rx_lock, flags);
+
+		ret = pd_send_msg(pd, MSG_VDM, pd->vdm_tx->data,
+				pd->vdm_tx->size, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error (%d) sending VDM command %d\n",
+					ret, SVDM_HDR_CMD(pd->vdm_tx->data[0]));
+
+			/* retry when hitting PE_SRC/SNK_Ready again */
+			if (ret != -EBUSY)
+				usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_SEND_SOFT_RESET :
+					PE_SNK_SEND_SOFT_RESET);
+
+			return;
+		}
+
+		/*
+		 * special case: keep initiated Discover ID/SVIDs
+		 * around in case we need to re-try when receiving BUSY
+		 */
+		if (VDM_IS_SVDM(vdm_hdr) &&
+			SVDM_HDR_CMD_TYPE(vdm_hdr) == SVDM_CMD_TYPE_INITIATOR &&
+			SVDM_HDR_CMD(vdm_hdr) <= USBPD_SVDM_DISCOVER_SVIDS) {
+			if (pd->vdm_tx_retry) {
+				usbpd_dbg(&pd->dev, "Previous Discover VDM command %d not ACKed/NAKed\n",
+					SVDM_HDR_CMD(
+						pd->vdm_tx_retry->data[0]));
+				kfree(pd->vdm_tx_retry);
+			}
+			pd->vdm_tx_retry = pd->vdm_tx;
+		} else {
+			kfree(pd->vdm_tx);
+		}
+
+		pd->vdm_tx = NULL;
+	}
+}
+
+static void reset_vdm_state(struct usbpd *pd)
+{
+	struct usbpd_svid_handler *handler;
+
+	list_for_each_entry(handler, &pd->svid_handlers, entry) {
+		if (handler->discovered) {
+			handler->disconnect(handler);
+			handler->discovered = false;
+		}
+	}
+
+	pd->vdm_state = VDM_NONE;
+	kfree(pd->vdm_tx_retry);
+	pd->vdm_tx_retry = NULL;
+	kfree(pd->discovered_svids);
+	pd->discovered_svids = NULL;
+	pd->num_svids = 0;
+	kfree(pd->vdm_tx);
+	pd->vdm_tx = NULL;
+}
+
+static void dr_swap(struct usbpd *pd)
+{
+	reset_vdm_state(pd);
+
+	if (pd->current_dr == DR_DFP) {
+		stop_usb_host(pd);
+		start_usb_peripheral(pd);
+		pd->current_dr = DR_UFP;
+	} else if (pd->current_dr == DR_UFP) {
+		stop_usb_peripheral(pd);
+		pd->current_dr = DR_DFP;
+
+		/* don't start USB host until after SVDM discovery */
+		usbpd_send_svdm(pd, USBPD_SID, USBPD_SVDM_DISCOVER_IDENTITY,
+				SVDM_CMD_TYPE_INITIATOR, 0, NULL, 0);
+	}
+
+	pd_phy_update_roles(pd->current_dr, pd->current_pr);
+}
+
+
+static void vconn_swap(struct usbpd *pd)
+{
+	int ret;
+
+	if (pd->vconn_enabled) {
+		pd->current_state = PE_VCS_WAIT_FOR_VCONN;
+		kick_sm(pd, VCONN_ON_TIME);
+	} else {
+		ret = regulator_enable(pd->vconn);
+		if (ret) {
+			usbpd_err(&pd->dev, "Unable to enable vconn\n");
+			return;
+		}
+
+		pd->vconn_enabled = true;
+
+		/*
+		 * Small delay to ensure Vconn has ramped up. This is well
+		 * below tVCONNSourceOn (100ms) so we still send PS_RDY within
+		 * the allowed time.
+		 */
+		usleep_range(5000, 10000);
+
+		ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending PS_RDY\n");
+			usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_SEND_SOFT_RESET :
+					PE_SNK_SEND_SOFT_RESET);
+			return;
+		}
+	}
+}
+
+static int enable_vbus(struct usbpd *pd)
+{
+	union power_supply_propval val = {0};
+	int count = 100;
+	int ret;
+
+	if (!check_vsafe0v)
+		goto enable_reg;
+
+	/*
+	 * Check to make sure there's no lingering charge on
+	 * VBUS before enabling it as a source. If so poll here
+	 * until it goes below VSafe0V (0.8V) before proceeding.
+	 */
+	while (count--) {
+		ret = power_supply_get_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
+		if (ret || val.intval <= 800000)
+			break;
+		usleep_range(20000, 30000);
+	}
+
+	if (count < 99)
+		msleep(100);	/* need to wait an additional tCCDebounce */
+
+enable_reg:
+	ret = regulator_enable(pd->vbus);
+	if (ret)
+		usbpd_err(&pd->dev, "Unable to enable vbus (%d)\n", ret);
+	else
+		pd->vbus_enabled = true;
+
+	return ret;
+}
+
+static inline void rx_msg_cleanup(struct usbpd *pd)
+{
+	struct rx_msg *msg, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&pd->rx_lock, flags);
+	list_for_each_entry_safe(msg, tmp, &pd->rx_q, entry) {
+		list_del(&msg->entry);
+		kfree(msg);
+	}
+	spin_unlock_irqrestore(&pd->rx_lock, flags);
+}
+
+/* For PD 3.0, check SinkTxOk before allowing initiating AMS */
+static inline bool is_sink_tx_ok(struct usbpd *pd)
+{
+	if (pd->spec_rev == USBPD_REV_30)
+		return pd->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH;
+
+	return true;
+}
+
+/* Handles current state and determines transitions */
+static void usbpd_sm(struct work_struct *w)
+{
+	struct usbpd *pd = container_of(w, struct usbpd, sm_work);
+	union power_supply_propval val = {0};
+	int ret;
+	struct rx_msg *rx_msg = NULL;
+	unsigned long flags;
+
+	usbpd_dbg(&pd->dev, "handle state %s\n",
+			usbpd_state_strings[pd->current_state]);
+
+	hrtimer_cancel(&pd->timer);
+	pd->sm_queued = false;
+
+	spin_lock_irqsave(&pd->rx_lock, flags);
+	if (!list_empty(&pd->rx_q)) {
+		rx_msg = list_first_entry(&pd->rx_q, struct rx_msg, entry);
+		list_del(&rx_msg->entry);
+	}
+	spin_unlock_irqrestore(&pd->rx_lock, flags);
+
+	/* Disconnect? */
+	if (pd->current_pr == PR_NONE) {
+		if (pd->current_state == PE_UNKNOWN)
+			goto sm_done;
+
+		usbpd_info(&pd->dev, "USB Type-C disconnect\n");
+
+		if (pd->pd_phy_opened) {
+			pd_phy_close();
+			pd->pd_phy_opened = false;
+		}
+
+		pd->in_pr_swap = false;
+		pd->pd_connected = false;
+		pd->in_explicit_contract = false;
+		pd->hard_reset_recvd = false;
+		pd->caps_count = 0;
+		pd->hard_reset_count = 0;
+		pd->src_cap_id = 0;
+		pd->requested_voltage = 0;
+		pd->requested_current = 0;
+		memset(&pd->received_pdos, 0, sizeof(pd->received_pdos));
+		rx_msg_cleanup(pd);
+
+		val.intval = 0;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED,
+				&val);
+
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+
+		if (pd->vbus_enabled) {
+			regulator_disable(pd->vbus);
+			pd->vbus_enabled = false;
+		}
+
+		if (pd->vconn_enabled) {
+			regulator_disable(pd->vconn);
+			pd->vconn_enabled = false;
+		}
+
+		if (pd->current_dr == DR_UFP)
+			stop_usb_peripheral(pd);
+		else if (pd->current_dr == DR_DFP)
+			stop_usb_host(pd);
+
+		pd->current_pr = PR_NONE;
+		pd->current_dr = DR_NONE;
+
+		reset_vdm_state(pd);
+
+		if (pd->current_state == PE_ERROR_RECOVERY)
+			/* forced disconnect, wait before resetting to DRP */
+			usleep_range(ERROR_RECOVERY_TIME * USEC_PER_MSEC,
+				(ERROR_RECOVERY_TIME + 5) * USEC_PER_MSEC);
+
+		/* set due to dual_role class "mode" change */
+		if (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE)
+			val.intval = pd->forced_pr;
+		else
+			/* Set CC back to DRP toggle */
+			val.intval = POWER_SUPPLY_TYPEC_PR_DUAL;
+
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_TYPEC_POWER_ROLE, &val);
+		pd->forced_pr = POWER_SUPPLY_TYPEC_PR_NONE;
+
+		pd->current_state = PE_UNKNOWN;
+
+		kobject_uevent(&pd->dev.kobj, KOBJ_CHANGE);
+		dual_role_instance_changed(pd->dual_role);
+
+		goto sm_done;
+	}
+
+	/* Hard reset? */
+	if (pd->hard_reset_recvd) {
+		pd->hard_reset_recvd = false;
+
+		val.intval = 1;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+		pd->in_pr_swap = false;
+		rx_msg_cleanup(pd);
+		reset_vdm_state(pd);
+
+		if (pd->current_pr == PR_SINK) {
+			usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
+		} else {
+			pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT;
+			kick_sm(pd, PS_HARD_RESET_TIME);
+		}
+
+		goto sm_done;
+	}
+
+	/* Soft reset? */
+	if (IS_CTRL(rx_msg, MSG_SOFT_RESET)) {
+		usbpd_dbg(&pd->dev, "Handle soft reset\n");
+
+		if (pd->current_pr == PR_SRC)
+			pd->current_state = PE_SRC_SOFT_RESET;
+		else if (pd->current_pr == PR_SINK)
+			pd->current_state = PE_SNK_SOFT_RESET;
+	}
+
+	switch (pd->current_state) {
+	case PE_UNKNOWN:
+		if (pd->current_pr == PR_SINK) {
+			usbpd_set_state(pd, PE_SNK_STARTUP);
+		} else if (pd->current_pr == PR_SRC) {
+			enable_vbus(pd);
+			if (!pd->vconn_enabled &&
+					pd->typec_mode ==
+					POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE) {
+				ret = regulator_enable(pd->vconn);
+				if (ret)
+					usbpd_err(&pd->dev, "Unable to enable vconn\n");
+				else
+					pd->vconn_enabled = true;
+			}
+
+			usbpd_set_state(pd, PE_SRC_STARTUP);
+		}
+		break;
+
+	case PE_SRC_STARTUP:
+		usbpd_set_state(pd, PE_SRC_STARTUP);
+		break;
+
+	case PE_SRC_SEND_CAPABILITIES:
+		ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES, default_src_caps,
+				ARRAY_SIZE(default_src_caps), SOP_MSG);
+		if (ret) {
+			pd->caps_count++;
+
+			if (pd->caps_count == 10 && pd->current_dr == DR_DFP) {
+				/* Likely not PD-capable, start host now */
+				start_usb_host(pd, true);
+			} else if (pd->caps_count >= PD_CAPS_COUNT) {
+				usbpd_dbg(&pd->dev, "Src CapsCounter exceeded, disabling PD\n");
+				usbpd_set_state(pd, PE_SRC_DISABLED);
+
+				val.intval = 0;
+				power_supply_set_property(pd->usb_psy,
+						POWER_SUPPLY_PROP_PD_ACTIVE,
+						&val);
+				break;
+			}
+
+			kick_sm(pd, SRC_CAP_TIME);
+			break;
+		}
+
+		/* transmit was successful if GoodCRC was received */
+		pd->caps_count = 0;
+		pd->hard_reset_count = 0;
+		pd->pd_connected = true; /* we know peer is PD capable */
+
+		/* wait for REQUEST */
+		pd->current_state = PE_SRC_SEND_CAPABILITIES_WAIT;
+		kick_sm(pd, SENDER_RESPONSE_TIME);
+
+		val.intval = 1;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+		break;
+
+	case PE_SRC_SEND_CAPABILITIES_WAIT:
+		if (IS_DATA(rx_msg, MSG_REQUEST)) {
+			pd->rdo = rx_msg->payload[0];
+			usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
+		} else if (rx_msg) {
+			usbpd_err(&pd->dev, "Unexpected message received\n");
+			usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+		} else {
+			usbpd_set_state(pd, PE_SRC_HARD_RESET);
+		}
+		break;
+
+	case PE_SRC_READY:
+		if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) {
+			ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
+					default_src_caps,
+					ARRAY_SIZE(default_src_caps), SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending SRC CAPs\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+		} else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) {
+			ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
+					default_snk_caps,
+					ARRAY_SIZE(default_snk_caps), SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Sink Caps\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+			}
+		} else if (IS_DATA(rx_msg, MSG_REQUEST)) {
+			pd->rdo = rx_msg->payload[0];
+			usbpd_set_state(pd, PE_SRC_NEGOTIATE_CAPABILITY);
+		} else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) {
+			if (pd->vdm_state == MODE_ENTERED) {
+				usbpd_set_state(pd, PE_SRC_HARD_RESET);
+				break;
+			}
+
+			ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Accept\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			dr_swap(pd);
+		} else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) {
+			/* lock in current mode */
+			set_power_role(pd, pd->current_pr);
+
+			/* we'll happily accept Src->Sink requests anytime */
+			ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Accept\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
+			kick_sm(pd, SRC_TRANSITION_TIME);
+			break;
+		} else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) {
+			ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Accept\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			vconn_swap(pd);
+		} else if (IS_DATA(rx_msg, MSG_VDM)) {
+			handle_vdm_rx(pd, rx_msg);
+		} else if (pd->send_pr_swap) {
+			pd->send_pr_swap = false;
+			ret = pd_send_msg(pd, MSG_PR_SWAP, NULL, 0, SOP_MSG);
+			if (ret) {
+				dev_err(&pd->dev, "Error sending PR Swap\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			pd->current_state = PE_PRS_SRC_SNK_SEND_SWAP;
+			kick_sm(pd, SENDER_RESPONSE_TIME);
+		} else if (pd->send_dr_swap) {
+			pd->send_dr_swap = false;
+			ret = pd_send_msg(pd, MSG_DR_SWAP, NULL, 0, SOP_MSG);
+			if (ret) {
+				dev_err(&pd->dev, "Error sending DR Swap\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			pd->current_state = PE_DRS_SEND_DR_SWAP;
+			kick_sm(pd, SENDER_RESPONSE_TIME);
+		} else {
+			handle_vdm_tx(pd);
+		}
+		break;
+
+	case PE_SRC_TRANSITION_TO_DEFAULT:
+		if (pd->vconn_enabled)
+			regulator_disable(pd->vconn);
+		if (pd->vbus_enabled)
+			regulator_disable(pd->vbus);
+
+		if (pd->current_dr != DR_DFP) {
+			extcon_set_cable_state_(pd->extcon, EXTCON_USB, 0);
+			pd->current_dr = DR_DFP;
+			pd_phy_update_roles(pd->current_dr, pd->current_pr);
+		}
+
+		msleep(SRC_RECOVER_TIME);
+
+		pd->vbus_enabled = false;
+		enable_vbus(pd);
+
+		if (pd->vconn_enabled) {
+			ret = regulator_enable(pd->vconn);
+			if (ret) {
+				usbpd_err(&pd->dev, "Unable to enable vconn\n");
+				pd->vconn_enabled = false;
+			}
+		}
+
+		val.intval = 0;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+		usbpd_set_state(pd, PE_SRC_STARTUP);
+		break;
+
+	case PE_SRC_HARD_RESET:
+		val.intval = 1;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+		pd_send_hard_reset(pd);
+		pd->in_explicit_contract = false;
+		rx_msg_cleanup(pd);
+		reset_vdm_state(pd);
+
+		pd->current_state = PE_SRC_TRANSITION_TO_DEFAULT;
+		kick_sm(pd, PS_HARD_RESET_TIME);
+		break;
+
+	case PE_SNK_STARTUP:
+		usbpd_set_state(pd, PE_SNK_STARTUP);
+		break;
+
+	case PE_SNK_DISCOVERY:
+		if (!rx_msg) {
+			if (pd->vbus_present)
+				usbpd_set_state(pd,
+						PE_SNK_WAIT_FOR_CAPABILITIES);
+
+			/*
+			 * Handle disconnection in the middle of PR_Swap.
+			 * Since in psy_changed() if pd->in_pr_swap is true
+			 * we ignore the typec_mode==NONE change since that is
+			 * expected to happen. However if the cable really did
+			 * get disconnected we need to check for it here after
+			 * waiting for VBUS presence times out.
+			 */
+			if (!pd->typec_mode) {
+				pd->current_pr = PR_NONE;
+				kick_sm(pd, 0);
+			}
+
+			break;
+		}
+		/* else fall-through */
+
+	case PE_SNK_WAIT_FOR_CAPABILITIES:
+		pd->in_pr_swap = false;
+
+		if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
+			val.intval = 0;
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+					&val);
+
+			/* save the PDOs so userspace can further evaluate */
+			memcpy(&pd->received_pdos, rx_msg->payload,
+					sizeof(pd->received_pdos));
+			pd->src_cap_id++;
+
+			usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
+
+			val.intval = 1;
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+		} else if (pd->hard_reset_count < 3) {
+			usbpd_set_state(pd, PE_SNK_HARD_RESET);
+		} else if (pd->pd_connected) {
+			usbpd_info(&pd->dev, "Sink hard reset count exceeded, forcing reconnect\n");
+
+			val.intval = 0;
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+					&val);
+
+			usbpd_set_state(pd, PE_ERROR_RECOVERY);
+		} else {
+			usbpd_dbg(&pd->dev, "Sink hard reset count exceeded, disabling PD\n");
+
+			val.intval = 0;
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+					&val);
+
+			val.intval = 0;
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_ACTIVE, &val);
+		}
+		break;
+
+	case PE_SNK_SELECT_CAPABILITY:
+		if (IS_CTRL(rx_msg, MSG_ACCEPT)) {
+			usbpd_set_state(pd, PE_SNK_TRANSITION_SINK);
+
+			/* prepare for voltage increase/decrease */
+			val.intval = pd->requested_voltage;
+			power_supply_set_property(pd->usb_psy,
+				pd->requested_voltage >= pd->current_voltage ?
+					POWER_SUPPLY_PROP_VOLTAGE_MAX :
+					POWER_SUPPLY_PROP_VOLTAGE_MIN,
+					&val);
+
+			/*
+			 * if we are changing voltages, we must lower input
+			 * current to pSnkStdby (2.5W). Calculate it and set
+			 * PD_CURRENT_MAX accordingly.
+			 */
+			if (pd->requested_voltage != pd->current_voltage) {
+				int mv = max(pd->requested_voltage,
+						pd->current_voltage) / 1000;
+				val.intval = (2500000 / mv) * 1000;
+				power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+			} else {
+				/* decreasing current? */
+				ret = power_supply_get_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+				if (!ret &&
+					pd->requested_current < val.intval) {
+					val.intval =
+						pd->requested_current * 1000;
+					power_supply_set_property(pd->usb_psy,
+					     POWER_SUPPLY_PROP_PD_CURRENT_MAX,
+					     &val);
+				}
+			}
+
+			pd->selected_pdo = pd->requested_pdo;
+		} else if (IS_CTRL(rx_msg, MSG_REJECT) ||
+				IS_CTRL(rx_msg, MSG_WAIT)) {
+			if (pd->in_explicit_contract)
+				usbpd_set_state(pd, PE_SNK_READY);
+			else
+				usbpd_set_state(pd,
+						PE_SNK_WAIT_FOR_CAPABILITIES);
+		} else if (rx_msg) {
+			usbpd_err(&pd->dev, "Invalid response to sink request\n");
+			usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+		} else {
+			/* timed out; go to hard reset */
+			usbpd_set_state(pd, PE_SNK_HARD_RESET);
+		}
+		break;
+
+	case PE_SNK_TRANSITION_SINK:
+		if (IS_CTRL(rx_msg, MSG_PS_RDY)) {
+			val.intval = pd->requested_voltage;
+			power_supply_set_property(pd->usb_psy,
+				pd->requested_voltage >= pd->current_voltage ?
+					POWER_SUPPLY_PROP_VOLTAGE_MIN :
+					POWER_SUPPLY_PROP_VOLTAGE_MAX, &val);
+			pd->current_voltage = pd->requested_voltage;
+
+			/* resume charging */
+			val.intval = pd->requested_current * 1000; /* mA->uA */
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+
+			usbpd_set_state(pd, PE_SNK_READY);
+		} else {
+			/* timed out; go to hard reset */
+			usbpd_set_state(pd, PE_SNK_HARD_RESET);
+		}
+		break;
+
+	case PE_SNK_READY:
+		if (IS_DATA(rx_msg, MSG_SOURCE_CAPABILITIES)) {
+			/* save the PDOs so userspace can further evaluate */
+			memcpy(&pd->received_pdos, rx_msg->payload,
+					sizeof(pd->received_pdos));
+			pd->src_cap_id++;
+
+			usbpd_set_state(pd, PE_SNK_EVALUATE_CAPABILITY);
+		} else if (IS_CTRL(rx_msg, MSG_GET_SINK_CAP)) {
+			ret = pd_send_msg(pd, MSG_SINK_CAPABILITIES,
+					default_snk_caps,
+					ARRAY_SIZE(default_snk_caps), SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Sink Caps\n");
+				usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+			}
+		} else if (IS_CTRL(rx_msg, MSG_GET_SOURCE_CAP)) {
+			ret = pd_send_msg(pd, MSG_SOURCE_CAPABILITIES,
+					default_src_caps,
+					ARRAY_SIZE(default_src_caps), SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending SRC CAPs\n");
+				usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+				break;
+			}
+		} else if (IS_CTRL(rx_msg, MSG_DR_SWAP)) {
+			if (pd->vdm_state == MODE_ENTERED) {
+				usbpd_set_state(pd, PE_SNK_HARD_RESET);
+				break;
+			}
+
+			ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Accept\n");
+				usbpd_set_state(pd, PE_SRC_SEND_SOFT_RESET);
+				break;
+			}
+
+			dr_swap(pd);
+		} else if (IS_CTRL(rx_msg, MSG_PR_SWAP)) {
+			/* lock in current mode */
+			set_power_role(pd, pd->current_pr);
+
+			/* TODO: should we Reject in certain circumstances? */
+			ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Accept\n");
+				usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+				break;
+			}
+
+			pd->in_pr_swap = true;
+			usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
+			break;
+		} else if (IS_CTRL(rx_msg, MSG_VCONN_SWAP)) {
+			/*
+			 * if VCONN is connected to VBUS, make sure we are
+			 * not in high voltage contract, otherwise reject.
+			 */
+			if (!pd->vconn_is_external &&
+					(pd->requested_voltage > 5000000)) {
+				ret = pd_send_msg(pd, MSG_REJECT, NULL, 0,
+						SOP_MSG);
+				if (ret) {
+					usbpd_err(&pd->dev, "Error sending Reject\n");
+					usbpd_set_state(pd,
+							PE_SNK_SEND_SOFT_RESET);
+				}
+
+				break;
+			}
+
+			ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+			if (ret) {
+				usbpd_err(&pd->dev, "Error sending Accept\n");
+				usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+				break;
+			}
+
+			vconn_swap(pd);
+		} else if (IS_DATA(rx_msg, MSG_VDM)) {
+			handle_vdm_rx(pd, rx_msg);
+		} else if (pd->send_pr_swap && is_sink_tx_ok(pd)) {
+			pd->send_pr_swap = false;
+			ret = pd_send_msg(pd, MSG_PR_SWAP, NULL, 0, SOP_MSG);
+			if (ret) {
+				dev_err(&pd->dev, "Error sending PR Swap\n");
+				usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+				break;
+			}
+
+			pd->current_state = PE_PRS_SNK_SRC_SEND_SWAP;
+			kick_sm(pd, SENDER_RESPONSE_TIME);
+		} else if (pd->send_dr_swap && is_sink_tx_ok(pd)) {
+			pd->send_dr_swap = false;
+			ret = pd_send_msg(pd, MSG_DR_SWAP, NULL, 0, SOP_MSG);
+			if (ret) {
+				dev_err(&pd->dev, "Error sending DR Swap\n");
+				usbpd_set_state(pd, PE_SNK_SEND_SOFT_RESET);
+				break;
+			}
+
+			pd->current_state = PE_DRS_SEND_DR_SWAP;
+			kick_sm(pd, SENDER_RESPONSE_TIME);
+		} else if (is_sink_tx_ok(pd)) {
+			handle_vdm_tx(pd);
+		}
+		break;
+
+	case PE_SNK_TRANSITION_TO_DEFAULT:
+		usbpd_set_state(pd, PE_SNK_STARTUP);
+		break;
+
+	case PE_SRC_SOFT_RESET:
+	case PE_SNK_SOFT_RESET:
+		pd_reset_protocol(pd);
+
+		ret = pd_send_msg(pd, MSG_ACCEPT, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "%s: Error sending Accept, do Hard Reset\n",
+					usbpd_state_strings[pd->current_state]);
+			usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
+			break;
+		}
+
+		usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+				PE_SRC_SEND_CAPABILITIES :
+				PE_SNK_WAIT_FOR_CAPABILITIES);
+		break;
+
+	case PE_SRC_SEND_SOFT_RESET:
+	case PE_SNK_SEND_SOFT_RESET:
+		if (IS_CTRL(rx_msg, MSG_ACCEPT)) {
+			usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_SEND_CAPABILITIES :
+					PE_SNK_WAIT_FOR_CAPABILITIES);
+		} else {
+			usbpd_err(&pd->dev, "%s: Did not see Accept, do Hard Reset\n",
+					usbpd_state_strings[pd->current_state]);
+			usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
+		}
+		break;
+
+	case PE_SNK_HARD_RESET:
+		/* prepare charger for VBUS change */
+		val.intval = 1;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_PD_IN_HARD_RESET, &val);
+
+		pd->requested_voltage = 5000000;
+
+		if (pd->requested_current) {
+			val.intval = pd->requested_current = 0;
+			power_supply_set_property(pd->usb_psy,
+					POWER_SUPPLY_PROP_PD_CURRENT_MAX, &val);
+		}
+
+		val.intval = pd->requested_voltage;
+		power_supply_set_property(pd->usb_psy,
+				POWER_SUPPLY_PROP_VOLTAGE_MIN, &val);
+
+		pd_send_hard_reset(pd);
+		pd->in_explicit_contract = false;
+		reset_vdm_state(pd);
+		usbpd_set_state(pd, PE_SNK_TRANSITION_TO_DEFAULT);
+		break;
+
+	case PE_DRS_SEND_DR_SWAP:
+		if (IS_CTRL(rx_msg, MSG_ACCEPT))
+			dr_swap(pd);
+
+		usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+				PE_SRC_READY : PE_SNK_READY);
+		break;
+
+	case PE_PRS_SRC_SNK_SEND_SWAP:
+		if (!IS_CTRL(rx_msg, MSG_ACCEPT)) {
+			pd->current_state = PE_SRC_READY;
+			break;
+		}
+
+		pd->current_state = PE_PRS_SRC_SNK_TRANSITION_TO_OFF;
+		kick_sm(pd, SRC_TRANSITION_TIME);
+		break;
+
+	case PE_PRS_SRC_SNK_TRANSITION_TO_OFF:
+		pd->in_pr_swap = true;
+		pd->in_explicit_contract = false;
+
+		if (pd->vbus_enabled) {
+			regulator_disable(pd->vbus);
+			pd->vbus_enabled = false;
+		}
+
+		/* PE_PRS_SRC_SNK_Assert_Rd */
+		pd->current_pr = PR_SINK;
+		set_power_role(pd, pd->current_pr);
+		pd_phy_update_roles(pd->current_dr, pd->current_pr);
+
+		/* allow time for Vbus discharge, must be < tSrcSwapStdby */
+		msleep(500);
+
+		ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending PS_RDY\n");
+			usbpd_set_state(pd, PE_ERROR_RECOVERY);
+			break;
+		}
+
+		pd->current_state = PE_PRS_SRC_SNK_WAIT_SOURCE_ON;
+		kick_sm(pd, PS_SOURCE_ON);
+		break;
+
+	case PE_PRS_SRC_SNK_WAIT_SOURCE_ON:
+		if (IS_CTRL(rx_msg, MSG_PS_RDY))
+			usbpd_set_state(pd, PE_SNK_STARTUP);
+		else
+			usbpd_set_state(pd, PE_ERROR_RECOVERY);
+		break;
+
+	case PE_PRS_SNK_SRC_SEND_SWAP:
+		if (!IS_CTRL(rx_msg, MSG_ACCEPT)) {
+			pd->current_state = PE_SNK_READY;
+			break;
+		}
+
+		pd->in_pr_swap = true;
+		usbpd_set_state(pd, PE_PRS_SNK_SRC_TRANSITION_TO_OFF);
+		break;
+
+	case PE_PRS_SNK_SRC_TRANSITION_TO_OFF:
+		if (!IS_CTRL(rx_msg, MSG_PS_RDY)) {
+			usbpd_set_state(pd, PE_ERROR_RECOVERY);
+			break;
+		}
+
+		/* PE_PRS_SNK_SRC_Assert_Rp */
+		pd->current_pr = PR_SRC;
+		set_power_role(pd, pd->current_pr);
+		pd->current_state = PE_PRS_SNK_SRC_SOURCE_ON;
+
+		/* fall-through */
+
+	case PE_PRS_SNK_SRC_SOURCE_ON:
+		enable_vbus(pd);
+		msleep(200); /* allow time VBUS ramp-up, must be < tNewSrc */
+
+		ret = pd_send_msg(pd, MSG_PS_RDY, NULL, 0, SOP_MSG);
+		if (ret) {
+			usbpd_err(&pd->dev, "Error sending PS_RDY\n");
+			usbpd_set_state(pd, PE_ERROR_RECOVERY);
+			break;
+		}
+
+		usbpd_set_state(pd, PE_SRC_STARTUP);
+		break;
+
+	case PE_VCS_WAIT_FOR_VCONN:
+		if (IS_CTRL(rx_msg, MSG_PS_RDY)) {
+			/*
+			 * hopefully redundant check but in case not enabled
+			 * avoids unbalanced regulator disable count
+			 */
+			if (pd->vconn_enabled)
+				regulator_disable(pd->vconn);
+			pd->vconn_enabled = false;
+
+			pd->current_state = pd->current_pr == PR_SRC ?
+				PE_SRC_READY : PE_SNK_READY;
+		} else {
+			/* timed out; go to hard reset */
+			usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+					PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
+		}
+
+		break;
+
+	default:
+		usbpd_err(&pd->dev, "Unhandled state %s\n",
+				usbpd_state_strings[pd->current_state]);
+		break;
+	}
+
+sm_done:
+	kfree(rx_msg);
+
+	if (!pd->sm_queued)
+		pm_relax(&pd->dev);
+}
+
+static inline const char *src_current(enum power_supply_typec_mode typec_mode)
+{
+	switch (typec_mode) {
+	case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+		return "default";
+	case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+		return "medium - 1.5A";
+	case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+		return "high - 3.0A";
+	default:
+		return "";
+	}
+}
+
+static int psy_changed(struct notifier_block *nb, unsigned long evt, void *ptr)
+{
+	struct usbpd *pd = container_of(nb, struct usbpd, psy_nb);
+	union power_supply_propval val;
+	enum power_supply_typec_mode typec_mode;
+	int ret;
+
+	if (ptr != pd->usb_psy || evt != PSY_EVENT_PROP_CHANGED)
+		return 0;
+
+	ret = power_supply_get_property(pd->usb_psy,
+			POWER_SUPPLY_PROP_TYPEC_MODE, &val);
+	if (ret) {
+		usbpd_err(&pd->dev, "Unable to read USB TYPEC_MODE: %d\n", ret);
+		return ret;
+	}
+
+	typec_mode = val.intval;
+
+	ret = power_supply_get_property(pd->usb_psy,
+			POWER_SUPPLY_PROP_PE_START, &val);
+	if (ret) {
+		usbpd_err(&pd->dev, "Unable to read USB PROP_PE_START: %d\n",
+				ret);
+		return ret;
+	}
+
+	/* Don't proceed if PE_START=0 as other props may still change */
+	if (!val.intval && !pd->pd_connected &&
+			typec_mode != POWER_SUPPLY_TYPEC_NONE)
+		return 0;
+
+	ret = power_supply_get_property(pd->usb_psy,
+			POWER_SUPPLY_PROP_PRESENT, &val);
+	if (ret) {
+		usbpd_err(&pd->dev, "Unable to read USB PRESENT: %d\n", ret);
+		return ret;
+	}
+
+	pd->vbus_present = val.intval;
+
+	ret = power_supply_get_property(pd->usb_psy,
+			POWER_SUPPLY_PROP_TYPE, &val);
+	if (ret) {
+		usbpd_err(&pd->dev, "Unable to read USB TYPE: %d\n", ret);
+		return ret;
+	}
+
+	pd->psy_type = val.intval;
+
+	/*
+	 * For sink hard reset, state machine needs to know when VBUS changes
+	 *   - when in PE_SNK_TRANSITION_TO_DEFAULT, notify when VBUS falls
+	 *   - when in PE_SNK_DISCOVERY, notify when VBUS rises
+	 */
+	if (typec_mode && ((!pd->vbus_present &&
+			pd->current_state == PE_SNK_TRANSITION_TO_DEFAULT) ||
+		(pd->vbus_present && pd->current_state == PE_SNK_DISCOVERY))) {
+		usbpd_dbg(&pd->dev, "hard reset: typec mode:%d present:%d\n",
+			typec_mode, pd->vbus_present);
+		pd->typec_mode = typec_mode;
+		kick_sm(pd, 0);
+		return 0;
+	}
+
+	if (pd->typec_mode == typec_mode)
+		return 0;
+
+	pd->typec_mode = typec_mode;
+
+	usbpd_dbg(&pd->dev, "typec mode:%d present:%d type:%d orientation:%d\n",
+			typec_mode, pd->vbus_present, pd->psy_type,
+			usbpd_get_plug_orientation(pd));
+
+	switch (typec_mode) {
+	/* Disconnect */
+	case POWER_SUPPLY_TYPEC_NONE:
+		if (pd->in_pr_swap) {
+			usbpd_dbg(&pd->dev, "Ignoring disconnect due to PR swap\n");
+			return 0;
+		}
+
+		pd->current_pr = PR_NONE;
+		break;
+
+	/* Sink states */
+	case POWER_SUPPLY_TYPEC_SOURCE_DEFAULT:
+	case POWER_SUPPLY_TYPEC_SOURCE_MEDIUM:
+	case POWER_SUPPLY_TYPEC_SOURCE_HIGH:
+		usbpd_info(&pd->dev, "Type-C Source (%s) connected\n",
+				src_current(typec_mode));
+
+		/* if waiting for SinkTxOk to start an AMS */
+		if (pd->spec_rev == USBPD_REV_30 &&
+			typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH &&
+			(pd->send_pr_swap || pd->send_dr_swap || pd->vdm_tx))
+			break;
+
+		if (pd->current_pr == PR_SINK)
+			return 0;
+
+		pd->current_pr = PR_SINK;
+		break;
+
+	/* Source states */
+	case POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE:
+	case POWER_SUPPLY_TYPEC_SINK:
+		usbpd_info(&pd->dev, "Type-C Sink%s connected\n",
+				typec_mode == POWER_SUPPLY_TYPEC_SINK ?
+					"" : " (powered)");
+
+		if (pd->current_pr == PR_SRC)
+			return 0;
+
+		pd->current_pr = PR_SRC;
+		break;
+
+	case POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY:
+		usbpd_info(&pd->dev, "Type-C Debug Accessory connected\n");
+		break;
+	case POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER:
+		usbpd_info(&pd->dev, "Type-C Analog Audio Adapter connected\n");
+		break;
+	default:
+		usbpd_warn(&pd->dev, "Unsupported typec mode:%d\n",
+				typec_mode);
+		break;
+	}
+
+	/* queue state machine due to CC state change */
+	kick_sm(pd, 0);
+	return 0;
+}
+
+static enum dual_role_property usbpd_dr_properties[] = {
+	DUAL_ROLE_PROP_SUPPORTED_MODES,
+	DUAL_ROLE_PROP_MODE,
+	DUAL_ROLE_PROP_PR,
+	DUAL_ROLE_PROP_DR,
+};
+
+static int usbpd_dr_get_property(struct dual_role_phy_instance *dual_role,
+		enum dual_role_property prop, unsigned int *val)
+{
+	struct usbpd *pd = dual_role_get_drvdata(dual_role);
+
+	if (!pd)
+		return -ENODEV;
+
+	switch (prop) {
+	case DUAL_ROLE_PROP_MODE:
+		/* For now associate UFP/DFP with data role only */
+		if (pd->current_dr == DR_UFP)
+			*val = DUAL_ROLE_PROP_MODE_UFP;
+		else if (pd->current_dr == DR_DFP)
+			*val = DUAL_ROLE_PROP_MODE_DFP;
+		else
+			*val = DUAL_ROLE_PROP_MODE_NONE;
+		break;
+	case DUAL_ROLE_PROP_PR:
+		if (pd->current_pr == PR_SRC)
+			*val = DUAL_ROLE_PROP_PR_SRC;
+		else if (pd->current_pr == PR_SINK)
+			*val = DUAL_ROLE_PROP_PR_SNK;
+		else
+			*val = DUAL_ROLE_PROP_PR_NONE;
+		break;
+	case DUAL_ROLE_PROP_DR:
+		if (pd->current_dr == DR_UFP)
+			*val = DUAL_ROLE_PROP_DR_DEVICE;
+		else if (pd->current_dr == DR_DFP)
+			*val = DUAL_ROLE_PROP_DR_HOST;
+		else
+			*val = DUAL_ROLE_PROP_DR_NONE;
+		break;
+	default:
+		usbpd_warn(&pd->dev, "unsupported property %d\n", prop);
+		return -ENODATA;
+	}
+
+	return 0;
+}
+
+static int usbpd_dr_set_property(struct dual_role_phy_instance *dual_role,
+		enum dual_role_property prop, const unsigned int *val)
+{
+	struct usbpd *pd = dual_role_get_drvdata(dual_role);
+	bool do_swap = false;
+
+	if (!pd)
+		return -ENODEV;
+
+	switch (prop) {
+	case DUAL_ROLE_PROP_MODE:
+		usbpd_dbg(&pd->dev, "Setting mode to %d\n", *val);
+
+		/*
+		 * Forces disconnect on CC and re-establishes connection.
+		 * This does not use PD-based PR/DR swap
+		 */
+		if (*val == DUAL_ROLE_PROP_MODE_UFP)
+			pd->forced_pr = POWER_SUPPLY_TYPEC_PR_SINK;
+		else if (*val == DUAL_ROLE_PROP_MODE_DFP)
+			pd->forced_pr = POWER_SUPPLY_TYPEC_PR_SOURCE;
+
+		/* new mode will be applied in disconnect handler */
+		set_power_role(pd, PR_NONE);
+
+		/* wait until it takes effect */
+		while (pd->forced_pr != POWER_SUPPLY_TYPEC_PR_NONE)
+			msleep(20);
+
+		break;
+
+	case DUAL_ROLE_PROP_DR:
+		usbpd_dbg(&pd->dev, "Setting data_role to %d\n", *val);
+
+		if (*val == DUAL_ROLE_PROP_DR_HOST) {
+			if (pd->current_dr == DR_UFP)
+				do_swap = true;
+		} else if (*val == DUAL_ROLE_PROP_DR_DEVICE) {
+			if (pd->current_dr == DR_DFP)
+				do_swap = true;
+		} else {
+			usbpd_warn(&pd->dev, "setting data_role to 'none' unsupported\n");
+			return -ENOTSUPP;
+		}
+
+		if (do_swap) {
+			if (pd->current_state != PE_SRC_READY &&
+					pd->current_state != PE_SNK_READY) {
+				usbpd_err(&pd->dev, "data_role swap not allowed: PD not in Ready state\n");
+				return -EAGAIN;
+			}
+
+			if (pd->current_state == PE_SNK_READY &&
+					!is_sink_tx_ok(pd)) {
+				usbpd_err(&pd->dev, "Rp indicates SinkTxNG\n");
+				return -EAGAIN;
+			}
+
+			reinit_completion(&pd->swap_complete);
+			pd->send_dr_swap = true;
+			kick_sm(pd, 0);
+
+			/* wait for operation to complete */
+			if (!wait_for_completion_timeout(&pd->swap_complete,
+					msecs_to_jiffies(100))) {
+				usbpd_err(&pd->dev, "data_role swap timed out\n");
+				return -ETIMEDOUT;
+			}
+
+			if ((*val == DUAL_ROLE_PROP_DR_HOST &&
+					pd->current_dr != DR_DFP) ||
+				(*val == DUAL_ROLE_PROP_DR_DEVICE &&
+					 pd->current_dr != DR_UFP)) {
+				usbpd_err(&pd->dev, "incorrect state (%s) after data_role swap\n",
+						pd->current_dr == DR_DFP ?
+						"dfp" : "ufp");
+				return -EPROTO;
+			}
+		}
+
+		break;
+
+	case DUAL_ROLE_PROP_PR:
+		usbpd_dbg(&pd->dev, "Setting power_role to %d\n", *val);
+
+		if (*val == DUAL_ROLE_PROP_PR_SRC) {
+			if (pd->current_pr == PR_SINK)
+				do_swap = true;
+		} else if (*val == DUAL_ROLE_PROP_PR_SNK) {
+			if (pd->current_pr == PR_SRC)
+				do_swap = true;
+		} else {
+			usbpd_warn(&pd->dev, "setting power_role to 'none' unsupported\n");
+			return -ENOTSUPP;
+		}
+
+		if (do_swap) {
+			if (pd->current_state != PE_SRC_READY &&
+					pd->current_state != PE_SNK_READY) {
+				usbpd_err(&pd->dev, "power_role swap not allowed: PD not in Ready state\n");
+				return -EAGAIN;
+			}
+
+			if (pd->current_state == PE_SNK_READY &&
+					!is_sink_tx_ok(pd)) {
+				usbpd_err(&pd->dev, "Rp indicates SinkTxNG\n");
+				return -EAGAIN;
+			}
+
+			reinit_completion(&pd->swap_complete);
+			pd->send_pr_swap = true;
+			kick_sm(pd, 0);
+
+			/* wait for operation to complete */
+			if (!wait_for_completion_timeout(&pd->swap_complete,
+					msecs_to_jiffies(2000))) {
+				usbpd_err(&pd->dev, "power_role swap timed out\n");
+				return -ETIMEDOUT;
+			}
+
+			if ((*val == DUAL_ROLE_PROP_PR_SRC &&
+					pd->current_pr != PR_SRC) ||
+				(*val == DUAL_ROLE_PROP_PR_SNK &&
+					 pd->current_pr != PR_SINK)) {
+				usbpd_err(&pd->dev, "incorrect state (%s) after power_role swap\n",
+						pd->current_pr == PR_SRC ?
+						"source" : "sink");
+				return -EPROTO;
+			}
+		}
+		break;
+
+	default:
+		usbpd_warn(&pd->dev, "unsupported property %d\n", prop);
+		return -ENOTSUPP;
+	}
+
+	return 0;
+}
+
+static int usbpd_dr_prop_writeable(struct dual_role_phy_instance *dual_role,
+		enum dual_role_property prop)
+{
+	switch (prop) {
+	case DUAL_ROLE_PROP_MODE:
+	case DUAL_ROLE_PROP_DR:
+	case DUAL_ROLE_PROP_PR:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int usbpd_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	int i;
+
+	add_uevent_var(env, "DATA_ROLE=%s", pd->current_dr == DR_DFP ?
+			"dfp" : "ufp");
+
+	if (pd->current_pr == PR_SINK) {
+		add_uevent_var(env, "POWER_ROLE=sink");
+		add_uevent_var(env, "SRC_CAP_ID=%d", pd->src_cap_id);
+
+		for (i = 0; i < ARRAY_SIZE(pd->received_pdos); i++)
+			add_uevent_var(env, "PDO%d=%08x", i,
+					pd->received_pdos[i]);
+
+		add_uevent_var(env, "REQUESTED_PDO=%d", pd->requested_pdo);
+		add_uevent_var(env, "SELECTED_PDO=%d", pd->selected_pdo);
+	} else {
+		add_uevent_var(env, "POWER_ROLE=source");
+		for (i = 0; i < ARRAY_SIZE(default_src_caps); i++)
+			add_uevent_var(env, "PDO%d=%08x", i,
+					default_src_caps[i]);
+	}
+
+	add_uevent_var(env, "RDO=%08x", pd->rdo);
+	add_uevent_var(env, "CONTRACT=%s", pd->in_explicit_contract ?
+				"explicit" : "implicit");
+	add_uevent_var(env, "ALT_MODE=%d", pd->vdm_state == MODE_ENTERED);
+
+	return 0;
+}
+
+static ssize_t contract_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%s\n",
+			pd->in_explicit_contract ?  "explicit" : "implicit");
+}
+static DEVICE_ATTR_RO(contract);
+
+static ssize_t current_pr_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	const char *pr = "none";
+
+	if (pd->current_pr == PR_SINK)
+		pr = "sink";
+	else if (pd->current_pr == PR_SRC)
+		pr = "source";
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", pr);
+}
+static DEVICE_ATTR_RO(current_pr);
+
+static ssize_t initial_pr_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	const char *pr = "none";
+
+	if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT)
+		pr = "sink";
+	else if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SINK)
+		pr = "source";
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", pr);
+}
+static DEVICE_ATTR_RO(initial_pr);
+
+static ssize_t current_dr_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	const char *dr = "none";
+
+	if (pd->current_dr == DR_UFP)
+		dr = "ufp";
+	else if (pd->current_dr == DR_DFP)
+		dr = "dfp";
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", dr);
+}
+static DEVICE_ATTR_RO(current_dr);
+
+static ssize_t initial_dr_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	const char *dr = "none";
+
+	if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SOURCE_DEFAULT)
+		dr = "ufp";
+	else if (pd->typec_mode >= POWER_SUPPLY_TYPEC_SINK)
+		dr = "dfp";
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", dr);
+}
+static DEVICE_ATTR_RO(initial_dr);
+
+static ssize_t src_cap_id_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pd->src_cap_id);
+}
+static DEVICE_ATTR_RO(src_cap_id);
+
+/* Dump received source PDOs in human-readable format */
+static ssize_t pdo_h_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	int i;
+	ssize_t cnt = 0;
+
+	for (i = 0; i < ARRAY_SIZE(pd->received_pdos); i++) {
+		u32 pdo = pd->received_pdos[i];
+
+		if (pdo == 0)
+			break;
+
+		cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt, "PDO %d\n", i + 1);
+
+		if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_FIXED) {
+			cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt,
+					"\tFixed supply\n"
+					"\tDual-Role Power:%d\n"
+					"\tUSB Suspend Supported:%d\n"
+					"\tExternally Powered:%d\n"
+					"\tUSB Communications Capable:%d\n"
+					"\tData Role Swap:%d\n"
+					"\tPeak Current:%d\n"
+					"\tVoltage:%d (mV)\n"
+					"\tMax Current:%d (mA)\n",
+					PD_SRC_PDO_FIXED_PR_SWAP(pdo),
+					PD_SRC_PDO_FIXED_USB_SUSP(pdo),
+					PD_SRC_PDO_FIXED_EXT_POWERED(pdo),
+					PD_SRC_PDO_FIXED_USB_COMM(pdo),
+					PD_SRC_PDO_FIXED_DR_SWAP(pdo),
+					PD_SRC_PDO_FIXED_PEAK_CURR(pdo),
+					PD_SRC_PDO_FIXED_VOLTAGE(pdo) * 50,
+					PD_SRC_PDO_FIXED_MAX_CURR(pdo) * 10);
+		} else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_BATTERY) {
+			cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt,
+					"\tBattery supply\n"
+					"\tMax Voltage:%d (mV)\n"
+					"\tMin Voltage:%d (mV)\n"
+					"\tMax Power:%d (mW)\n",
+					PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) * 50,
+					PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) * 50,
+					PD_SRC_PDO_VAR_BATT_MAX(pdo) * 250);
+		} else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_VARIABLE) {
+			cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt,
+					"\tVariable supply\n"
+					"\tMax Voltage:%d (mV)\n"
+					"\tMin Voltage:%d (mV)\n"
+					"\tMax Current:%d (mA)\n",
+					PD_SRC_PDO_VAR_BATT_MAX_VOLT(pdo) * 50,
+					PD_SRC_PDO_VAR_BATT_MIN_VOLT(pdo) * 50,
+					PD_SRC_PDO_VAR_BATT_MAX(pdo) * 10);
+		} else if (PD_SRC_PDO_TYPE(pdo) == PD_SRC_PDO_TYPE_AUGMENTED) {
+			cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt,
+					"\tProgrammable Power supply\n"
+					"\tMax Voltage:%d (mV)\n"
+					"\tMin Voltage:%d (mV)\n"
+					"\tMax Current:%d (mA)\n",
+					PD_APDO_MAX_VOLT(pdo) * 100,
+					PD_APDO_MIN_VOLT(pdo) * 100,
+					PD_APDO_MAX_CURR(pdo) * 50);
+		} else {
+			cnt += scnprintf(&buf[cnt], PAGE_SIZE - cnt,
+					"Invalid PDO\n");
+		}
+
+		buf[cnt++] = '\n';
+	}
+
+	return cnt;
+}
+static DEVICE_ATTR_RO(pdo_h);
+
+static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr,
+		char *buf);
+
+#define PDO_ATTR(n) {					\
+	.attr	= { .name = __stringify(pdo##n), .mode = 0444 },	\
+	.show	= pdo_n_show,				\
+}
+static struct device_attribute dev_attr_pdos[] = {
+	PDO_ATTR(1),
+	PDO_ATTR(2),
+	PDO_ATTR(3),
+	PDO_ATTR(4),
+	PDO_ATTR(5),
+	PDO_ATTR(6),
+	PDO_ATTR(7),
+};
+
+static ssize_t pdo_n_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(dev_attr_pdos); i++)
+		if (attr == &dev_attr_pdos[i])
+			/* dump the PDO as a hex string */
+			return snprintf(buf, PAGE_SIZE, "%08x\n",
+					pd->received_pdos[i]);
+
+	usbpd_err(&pd->dev, "Invalid PDO index\n");
+	return -EINVAL;
+}
+
+static ssize_t select_pdo_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	int src_cap_id;
+	int pdo, uv = 0, ua = 0;
+	int ret;
+
+	/* Only allowed if we are already in explicit sink contract */
+	if (pd->current_state != PE_SNK_READY || !is_sink_tx_ok(pd)) {
+		usbpd_err(&pd->dev, "select_pdo: Cannot select new PDO yet\n");
+		return -EBUSY;
+	}
+
+	ret = sscanf(buf, "%d %d %d %d", &src_cap_id, &pdo, &uv, &ua);
+	if (ret != 2 && ret != 4) {
+		usbpd_err(&pd->dev, "select_pdo: Must specify <src cap id> <PDO> [<uV> <uA>]\n");
+		return -EINVAL;
+	}
+
+	if (src_cap_id != pd->src_cap_id) {
+		usbpd_err(&pd->dev, "select_pdo: src_cap_id mismatch.  Requested:%d, current:%d\n",
+				src_cap_id, pd->src_cap_id);
+		return -EINVAL;
+	}
+
+	if (pdo < 1 || pdo > 7) {
+		usbpd_err(&pd->dev, "select_pdo: invalid PDO:%d\n", pdo);
+		return -EINVAL;
+	}
+
+	ret = pd_select_pdo(pd, pdo, uv, ua);
+	if (ret)
+		return ret;
+
+	usbpd_set_state(pd, PE_SNK_SELECT_CAPABILITY);
+
+	return size;
+}
+
+static ssize_t select_pdo_show(struct device *dev,
+		struct device_attribute *attr, char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+
+	return snprintf(buf, PAGE_SIZE, "%d\n", pd->selected_pdo);
+}
+static DEVICE_ATTR_RW(select_pdo);
+
+static ssize_t rdo_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+
+	/* dump the RDO as a hex string */
+	return snprintf(buf, PAGE_SIZE, "%08x\n", pd->rdo);
+}
+static DEVICE_ATTR_RO(rdo);
+
+static ssize_t rdo_h_show(struct device *dev, struct device_attribute *attr,
+		char *buf)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	int pos = PD_RDO_OBJ_POS(pd->rdo);
+	int type = PD_SRC_PDO_TYPE(pd->received_pdos[pos]);
+	int len;
+
+	len = scnprintf(buf, PAGE_SIZE, "Request Data Object\n"
+			"\tObj Pos:%d\n"
+			"\tGiveback:%d\n"
+			"\tCapability Mismatch:%d\n"
+			"\tUSB Communications Capable:%d\n"
+			"\tNo USB Suspend:%d\n",
+			PD_RDO_OBJ_POS(pd->rdo),
+			PD_RDO_GIVEBACK(pd->rdo),
+			PD_RDO_MISMATCH(pd->rdo),
+			PD_RDO_USB_COMM(pd->rdo),
+			PD_RDO_NO_USB_SUSP(pd->rdo));
+
+	switch (type) {
+	case PD_SRC_PDO_TYPE_FIXED:
+	case PD_SRC_PDO_TYPE_VARIABLE:
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+				"(Fixed/Variable)\n"
+				"\tOperating Current:%d (mA)\n"
+				"\t%s Current:%d (mA)\n",
+				PD_RDO_FIXED_CURR(pd->rdo) * 10,
+				PD_RDO_GIVEBACK(pd->rdo) ? "Min" : "Max",
+				PD_RDO_FIXED_CURR_MINMAX(pd->rdo) * 10);
+		break;
+
+	case PD_SRC_PDO_TYPE_BATTERY:
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+				"(Battery)\n"
+				"\tOperating Power:%d (mW)\n"
+				"\t%s Power:%d (mW)\n",
+				PD_RDO_FIXED_CURR(pd->rdo) * 250,
+				PD_RDO_GIVEBACK(pd->rdo) ? "Min" : "Max",
+				PD_RDO_FIXED_CURR_MINMAX(pd->rdo) * 250);
+		break;
+
+	case PD_SRC_PDO_TYPE_AUGMENTED:
+		len += scnprintf(buf + len, PAGE_SIZE - len,
+				"(Programmable)\n"
+				"\tOutput Voltage:%d (mV)\n"
+				"\tOperating Current:%d (mA)\n",
+				PD_RDO_PROG_VOLTAGE(pd->rdo) * 20,
+				PD_RDO_PROG_CURR(pd->rdo) * 50);
+		break;
+	}
+
+	return len;
+}
+static DEVICE_ATTR_RO(rdo_h);
+
+static ssize_t hard_reset_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t size)
+{
+	struct usbpd *pd = dev_get_drvdata(dev);
+	int val = 0;
+
+	if (sscanf(buf, "%d\n", &val) != 1)
+		return -EINVAL;
+
+	if (val)
+		usbpd_set_state(pd, pd->current_pr == PR_SRC ?
+				PE_SRC_HARD_RESET : PE_SNK_HARD_RESET);
+
+	return size;
+}
+static DEVICE_ATTR_WO(hard_reset);
+
+static struct attribute *usbpd_attrs[] = {
+	&dev_attr_contract.attr,
+	&dev_attr_initial_pr.attr,
+	&dev_attr_current_pr.attr,
+	&dev_attr_initial_dr.attr,
+	&dev_attr_current_dr.attr,
+	&dev_attr_src_cap_id.attr,
+	&dev_attr_pdo_h.attr,
+	&dev_attr_pdos[0].attr,
+	&dev_attr_pdos[1].attr,
+	&dev_attr_pdos[2].attr,
+	&dev_attr_pdos[3].attr,
+	&dev_attr_pdos[4].attr,
+	&dev_attr_pdos[5].attr,
+	&dev_attr_pdos[6].attr,
+	&dev_attr_select_pdo.attr,
+	&dev_attr_rdo.attr,
+	&dev_attr_rdo_h.attr,
+	&dev_attr_hard_reset.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(usbpd);
+
+static struct class usbpd_class = {
+	.name = "usbpd",
+	.owner = THIS_MODULE,
+	.dev_uevent = usbpd_uevent,
+	.dev_groups = usbpd_groups,
+};
+
+static int match_usbpd_device(struct device *dev, const void *data)
+{
+	return dev->parent == data;
+}
+
+static void devm_usbpd_put(struct device *dev, void *res)
+{
+	struct usbpd **ppd = res;
+
+	put_device(&(*ppd)->dev);
+}
+
+struct usbpd *devm_usbpd_get_by_phandle(struct device *dev, const char *phandle)
+{
+	struct usbpd **ptr, *pd = NULL;
+	struct device_node *pd_np;
+	struct platform_device *pdev;
+	struct device *pd_dev;
+
+	if (!usbpd_class.p) /* usbpd_init() not yet called */
+		return ERR_PTR(-EAGAIN);
+
+	if (!dev->of_node)
+		return ERR_PTR(-EINVAL);
+
+	pd_np = of_parse_phandle(dev->of_node, phandle, 0);
+	if (!pd_np)
+		return ERR_PTR(-ENXIO);
+
+	pdev = of_find_device_by_node(pd_np);
+	if (!pdev)
+		return ERR_PTR(-ENODEV);
+
+	pd_dev = class_find_device(&usbpd_class, NULL, &pdev->dev,
+			match_usbpd_device);
+	if (!pd_dev) {
+		platform_device_put(pdev);
+		/* device was found but maybe hadn't probed yet, so defer */
+		return ERR_PTR(-EPROBE_DEFER);
+	}
+
+	ptr = devres_alloc(devm_usbpd_put, sizeof(*ptr), GFP_KERNEL);
+	if (!ptr) {
+		put_device(pd_dev);
+		platform_device_put(pdev);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	pd = dev_get_drvdata(pd_dev);
+	if (!pd)
+		return ERR_PTR(-EPROBE_DEFER);
+
+	*ptr = pd;
+	devres_add(dev, ptr);
+
+	return pd;
+}
+EXPORT_SYMBOL(devm_usbpd_get_by_phandle);
+
+static int num_pd_instances;
+
+/**
+ * usbpd_create - Create a new instance of USB PD protocol/policy engine
+ * @parent - parent device to associate with
+ *
+ * This creates a new usbpd class device which manages the state of a
+ * USB PD-capable port. The parent device that is passed in should be
+ * associated with the physical device port, e.g. a PD PHY.
+ *
+ * Return: struct usbpd pointer, or an ERR_PTR value
+ */
+struct usbpd *usbpd_create(struct device *parent)
+{
+	int ret;
+	struct usbpd *pd;
+
+	pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+	if (!pd)
+		return ERR_PTR(-ENOMEM);
+
+	device_initialize(&pd->dev);
+	pd->dev.class = &usbpd_class;
+	pd->dev.parent = parent;
+	dev_set_drvdata(&pd->dev, pd);
+
+	ret = dev_set_name(&pd->dev, "usbpd%d", num_pd_instances++);
+	if (ret)
+		goto free_pd;
+
+	ret = device_init_wakeup(&pd->dev, true);
+	if (ret)
+		goto free_pd;
+
+	ret = device_add(&pd->dev);
+	if (ret)
+		goto free_pd;
+
+	pd->wq = alloc_ordered_workqueue("usbpd_wq", WQ_FREEZABLE);
+	if (!pd->wq) {
+		ret = -ENOMEM;
+		goto del_pd;
+	}
+	INIT_WORK(&pd->sm_work, usbpd_sm);
+	hrtimer_init(&pd->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	pd->timer.function = pd_timeout;
+
+	pd->usb_psy = power_supply_get_by_name("usb");
+	if (!pd->usb_psy) {
+		usbpd_dbg(&pd->dev, "Could not get USB power_supply, deferring probe\n");
+		ret = -EPROBE_DEFER;
+		goto destroy_wq;
+	}
+
+	/*
+	 * associate extcon with the parent dev as it could have a DT
+	 * node which will be useful for extcon_get_edev_by_phandle()
+	 */
+	pd->extcon = devm_extcon_dev_allocate(parent, usbpd_extcon_cable);
+	if (IS_ERR(pd->extcon)) {
+		usbpd_err(&pd->dev, "failed to allocate extcon device\n");
+		ret = PTR_ERR(pd->extcon);
+		goto put_psy;
+	}
+
+	pd->extcon->mutually_exclusive = usbpd_extcon_exclusive;
+	ret = devm_extcon_dev_register(parent, pd->extcon);
+	if (ret) {
+		usbpd_err(&pd->dev, "failed to register extcon device\n");
+		goto put_psy;
+	}
+
+	pd->vbus = devm_regulator_get(parent, "vbus");
+	if (IS_ERR(pd->vbus)) {
+		ret = PTR_ERR(pd->vbus);
+		goto put_psy;
+	}
+
+	pd->vconn = devm_regulator_get(parent, "vconn");
+	if (IS_ERR(pd->vconn)) {
+		ret = PTR_ERR(pd->vconn);
+		goto put_psy;
+	}
+
+	pd->vconn_is_external = device_property_present(parent,
+					"qcom,vconn-uses-external-source");
+
+	/*
+	 * Register the Android dual-role class (/sys/class/dual_role_usb/).
+	 * The first instance should be named "otg_default" as that's what
+	 * Android expects.
+	 * Note this is different than the /sys/class/usbpd/ created above.
+	 */
+	pd->dr_desc.name = (num_pd_instances == 1) ?
+				"otg_default" : dev_name(&pd->dev);
+	pd->dr_desc.supported_modes = DUAL_ROLE_SUPPORTED_MODES_DFP_AND_UFP;
+	pd->dr_desc.properties = usbpd_dr_properties;
+	pd->dr_desc.num_properties = ARRAY_SIZE(usbpd_dr_properties);
+	pd->dr_desc.get_property = usbpd_dr_get_property;
+	pd->dr_desc.set_property = usbpd_dr_set_property;
+	pd->dr_desc.property_is_writeable = usbpd_dr_prop_writeable;
+
+	pd->dual_role = devm_dual_role_instance_register(&pd->dev,
+			&pd->dr_desc);
+	if (IS_ERR(pd->dual_role)) {
+		usbpd_err(&pd->dev, "could not register dual_role instance\n");
+		goto put_psy;
+	} else {
+		pd->dual_role->drv_data = pd;
+	}
+
+	pd->current_pr = PR_NONE;
+	pd->current_dr = DR_NONE;
+	list_add_tail(&pd->instance, &_usbpd);
+
+	spin_lock_init(&pd->rx_lock);
+	INIT_LIST_HEAD(&pd->rx_q);
+	INIT_LIST_HEAD(&pd->svid_handlers);
+	init_completion(&pd->swap_complete);
+
+	pd->psy_nb.notifier_call = psy_changed;
+	ret = power_supply_reg_notifier(&pd->psy_nb);
+	if (ret)
+		goto del_inst;
+
+	/* force read initial power_supply values */
+	psy_changed(&pd->psy_nb, PSY_EVENT_PROP_CHANGED, pd->usb_psy);
+
+	return pd;
+
+del_inst:
+	list_del(&pd->instance);
+put_psy:
+	power_supply_put(pd->usb_psy);
+destroy_wq:
+	destroy_workqueue(pd->wq);
+del_pd:
+	device_del(&pd->dev);
+free_pd:
+	num_pd_instances--;
+	kfree(pd);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(usbpd_create);
+
+/**
+ * usbpd_destroy - Removes and frees a usbpd instance
+ * @pd: the instance to destroy
+ */
+void usbpd_destroy(struct usbpd *pd)
+{
+	if (!pd)
+		return;
+
+	list_del(&pd->instance);
+	power_supply_unreg_notifier(&pd->psy_nb);
+	power_supply_put(pd->usb_psy);
+	destroy_workqueue(pd->wq);
+	device_del(&pd->dev);
+	kfree(pd);
+}
+EXPORT_SYMBOL(usbpd_destroy);
+
+static int __init usbpd_init(void)
+{
+	usbpd_ipc_log = ipc_log_context_create(NUM_LOG_PAGES, "usb_pd", 0);
+	return class_register(&usbpd_class);
+}
+module_init(usbpd_init);
+
+static void __exit usbpd_exit(void)
+{
+	class_unregister(&usbpd_class);
+}
+module_exit(usbpd_exit);
+
+MODULE_DESCRIPTION("USB Power Delivery Policy Engine");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/pd/qpnp-pdphy.c b/drivers/usb/pd/qpnp-pdphy.c
new file mode 100644
index 0000000..4caee72
--- /dev/null
+++ b/drivers/usb/pd/qpnp-pdphy.c
@@ -0,0 +1,903 @@
+/* Copyright (c) 2016-2017, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include "usbpd.h"
+
+#define USB_PDPHY_MAX_DATA_OBJ_LEN	28
+#define USB_PDPHY_MSG_HDR_LEN		2
+
+/* PD PHY register offsets and bit fields */
+#define USB_PDPHY_MSG_CONFIG		0x40
+#define MSG_CONFIG_PORT_DATA_ROLE	BIT(3)
+#define MSG_CONFIG_PORT_POWER_ROLE	BIT(2)
+#define MSG_CONFIG_SPEC_REV_MASK	(BIT(1) | BIT(0))
+
+#define USB_PDPHY_EN_CONTROL		0x46
+#define CONTROL_ENABLE			BIT(0)
+
+#define USB_PDPHY_RX_STATUS		0x4A
+#define RX_FRAME_TYPE			(BIT(0) | BIT(1) | BIT(2))
+
+#define USB_PDPHY_FRAME_FILTER		0x4C
+#define FRAME_FILTER_EN_HARD_RESET	BIT(5)
+#define FRAME_FILTER_EN_SOP		BIT(0)
+
+#define USB_PDPHY_TX_SIZE		0x42
+#define TX_SIZE_MASK			0xF
+
+#define USB_PDPHY_TX_CONTROL		0x44
+#define TX_CONTROL_RETRY_COUNT		(BIT(6) | BIT(5))
+#define TX_CONTROL_FRAME_TYPE		(BIT(4) | BIT(3) | BIT(2))
+#define TX_CONTROL_FRAME_TYPE_CABLE_RESET (0x1 << 2)
+#define TX_CONTROL_SEND_SIGNAL		BIT(1)
+#define TX_CONTROL_SEND_MSG		BIT(0)
+
+#define USB_PDPHY_RX_SIZE		0x48
+
+#define USB_PDPHY_RX_ACKNOWLEDGE	0x4B
+#define RX_BUFFER_TOKEN			BIT(0)
+
+#define USB_PDPHY_BIST_MODE		0x4E
+#define BIST_MODE_MASK			0xF
+#define BIST_ENABLE			BIT(7)
+#define PD_MSG_BIST			0x3
+#define PD_BIST_TEST_DATA_MODE		0x8
+
+#define USB_PDPHY_TX_BUFFER_HDR		0x60
+#define USB_PDPHY_TX_BUFFER_DATA	0x62
+
+#define USB_PDPHY_RX_BUFFER		0x80
+
+#define USB_PDPHY_SEC_ACCESS		0xD0
+#define USB_PDPHY_TRIM_3		0xF3
+
+/* VDD regulator */
+#define VDD_PDPHY_VOL_MIN		3088000 /* uV */
+#define VDD_PDPHY_VOL_MAX		3088000 /* uV */
+#define VDD_PDPHY_HPM_LOAD		3000 /* uA */
+
+struct usb_pdphy {
+	struct device *dev;
+	struct regmap *regmap;
+
+	u16 base;
+	struct regulator *vdd_pdphy;
+
+	/* irqs */
+	int sig_tx_irq;
+	int sig_rx_irq;
+	int msg_tx_irq;
+	int msg_rx_irq;
+	int msg_tx_failed_irq;
+	int msg_tx_discarded_irq;
+	int msg_rx_discarded_irq;
+
+	void (*signal_cb)(struct usbpd *pd, enum pd_sig_type type);
+	void (*msg_rx_cb)(struct usbpd *pd, enum pd_msg_type type,
+			  u8 *buf, size_t len);
+	void (*shutdown_cb)(struct usbpd *pd);
+
+	/* write waitq */
+	wait_queue_head_t tx_waitq;
+
+	bool is_opened;
+	int tx_status;
+	u8 frame_filter_val;
+	bool in_test_data_mode;
+
+	enum data_role data_role;
+	enum power_role power_role;
+
+	struct usbpd *usbpd;
+
+	/* debug */
+	struct dentry *debug_root;
+	unsigned int tx_bytes; /* hdr + data */
+	unsigned int rx_bytes; /* hdr + data */
+	unsigned int sig_tx_cnt;
+	unsigned int sig_rx_cnt;
+	unsigned int msg_tx_cnt;
+	unsigned int msg_rx_cnt;
+	unsigned int msg_tx_failed_cnt;
+	unsigned int msg_tx_discarded_cnt;
+	unsigned int msg_rx_discarded_cnt;
+};
+
+static struct usb_pdphy *__pdphy;
+
+static int pdphy_dbg_status(struct seq_file *s, void *p)
+{
+	struct usb_pdphy *pdphy = s->private;
+
+	seq_printf(s,
+		"PD Phy driver status\n"
+		"==================================================\n");
+	seq_printf(s, "opened:         %10d\n", pdphy->is_opened);
+	seq_printf(s, "tx status:      %10d\n", pdphy->tx_status);
+	seq_printf(s, "tx bytes:       %10u\n", pdphy->tx_bytes);
+	seq_printf(s, "rx bytes:       %10u\n", pdphy->rx_bytes);
+	seq_printf(s, "data role:      %10u\n", pdphy->data_role);
+	seq_printf(s, "power role:     %10u\n", pdphy->power_role);
+	seq_printf(s, "frame filter:   %10u\n", pdphy->frame_filter_val);
+	seq_printf(s, "sig tx cnt:     %10u\n", pdphy->sig_tx_cnt);
+	seq_printf(s, "sig rx cnt:     %10u\n", pdphy->sig_rx_cnt);
+	seq_printf(s, "msg tx cnt:     %10u\n", pdphy->msg_tx_cnt);
+	seq_printf(s, "msg rx cnt:     %10u\n", pdphy->msg_rx_cnt);
+	seq_printf(s, "msg tx failed cnt:    %10u\n",
+			pdphy->msg_tx_failed_cnt);
+	seq_printf(s, "msg tx discarded cnt: %10u\n",
+			pdphy->msg_tx_discarded_cnt);
+	seq_printf(s, "msg rx discarded cnt: %10u\n",
+			pdphy->msg_rx_discarded_cnt);
+
+	return 0;
+}
+
+static int pdphy_dbg_status_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, pdphy_dbg_status, inode->i_private);
+}
+
+static const struct file_operations status_ops = {
+	.owner		= THIS_MODULE,
+	.open		= pdphy_dbg_status_open,
+	.llseek		= seq_lseek,
+	.read		= seq_read,
+	.release	= single_release,
+};
+
+static void pdphy_create_debugfs_entries(struct usb_pdphy *pdphy)
+{
+	struct dentry *ent;
+
+	pdphy->debug_root = debugfs_create_dir("usb-pdphy", NULL);
+	if (!pdphy->debug_root) {
+		dev_warn(pdphy->dev, "Couldn't create debug dir\n");
+		return;
+	}
+
+	ent = debugfs_create_file("status", 0400, pdphy->debug_root, pdphy,
+				  &status_ops);
+	if (!ent) {
+		dev_warn(pdphy->dev, "Couldn't create status file\n");
+		debugfs_remove(pdphy->debug_root);
+	}
+}
+
+static int pdphy_enable_power(struct usb_pdphy *pdphy, bool on)
+{
+	int ret = 0;
+
+	dev_dbg(pdphy->dev, "%s turn %s regulator.\n", __func__,
+		on ? "on" : "off");
+
+	if (!on)
+		goto disable_pdphy_vdd;
+
+	ret = regulator_set_load(pdphy->vdd_pdphy, VDD_PDPHY_HPM_LOAD);
+	if (ret < 0) {
+		dev_err(pdphy->dev, "Unable to set HPM of vdd_pdphy:%d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_set_voltage(pdphy->vdd_pdphy, VDD_PDPHY_VOL_MIN,
+						VDD_PDPHY_VOL_MAX);
+	if (ret) {
+		dev_err(pdphy->dev,
+				"set voltage failed for vdd_pdphy:%d\n", ret);
+		goto put_pdphy_vdd_lpm;
+	}
+
+	ret = regulator_enable(pdphy->vdd_pdphy);
+	if (ret) {
+		dev_err(pdphy->dev, "Unable to enable vdd_pdphy:%d\n", ret);
+		goto unset_pdphy_vdd;
+	}
+
+	dev_dbg(pdphy->dev, "%s: PD PHY regulator turned ON.\n", __func__);
+	return ret;
+
+disable_pdphy_vdd:
+	ret = regulator_disable(pdphy->vdd_pdphy);
+	if (ret)
+		dev_err(pdphy->dev, "Unable to disable vdd_pdphy:%d\n", ret);
+
+unset_pdphy_vdd:
+	ret = regulator_set_voltage(pdphy->vdd_pdphy, 0, VDD_PDPHY_VOL_MAX);
+	if (ret)
+		dev_err(pdphy->dev,
+			"Unable to set (0) voltage for vdd_pdphy:%d\n", ret);
+
+put_pdphy_vdd_lpm:
+	ret = regulator_set_load(pdphy->vdd_pdphy, 0);
+	if (ret < 0)
+		dev_err(pdphy->dev, "Unable to set (0) HPM of vdd_pdphy\n");
+
+	return ret;
+}
+
+void pdphy_enable_irq(struct usb_pdphy *pdphy, bool enable)
+{
+	if (enable) {
+		enable_irq(pdphy->sig_tx_irq);
+		enable_irq(pdphy->sig_rx_irq);
+		enable_irq_wake(pdphy->sig_rx_irq);
+		enable_irq(pdphy->msg_tx_irq);
+		if (!pdphy->in_test_data_mode) {
+			enable_irq(pdphy->msg_rx_irq);
+			enable_irq_wake(pdphy->msg_rx_irq);
+		}
+		enable_irq(pdphy->msg_tx_failed_irq);
+		enable_irq(pdphy->msg_tx_discarded_irq);
+		enable_irq(pdphy->msg_rx_discarded_irq);
+		return;
+	}
+
+	disable_irq(pdphy->sig_tx_irq);
+	disable_irq(pdphy->sig_rx_irq);
+	disable_irq_wake(pdphy->sig_rx_irq);
+	disable_irq(pdphy->msg_tx_irq);
+	if (!pdphy->in_test_data_mode) {
+		disable_irq(pdphy->msg_rx_irq);
+		disable_irq_wake(pdphy->msg_rx_irq);
+	}
+	disable_irq(pdphy->msg_tx_failed_irq);
+	disable_irq(pdphy->msg_tx_discarded_irq);
+	disable_irq(pdphy->msg_rx_discarded_irq);
+}
+
+static int pdphy_reg_read(struct usb_pdphy *pdphy, u8 *val, u16 addr, int count)
+{
+	int ret;
+
+	ret = regmap_bulk_read(pdphy->regmap, pdphy->base + addr, val, count);
+	if (ret) {
+		dev_err(pdphy->dev, "read failed: addr=0x%04x, ret=%d\n",
+			pdphy->base + addr, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Write multiple registers to device with block of data */
+static int pdphy_bulk_reg_write(struct usb_pdphy *pdphy, u16 addr,
+	const void *val, u8 val_cnt)
+{
+	int ret;
+
+	ret = regmap_bulk_write(pdphy->regmap, pdphy->base + addr,
+			val, val_cnt);
+	if (ret) {
+		dev_err(pdphy->dev, "bulk write failed: addr=0x%04x, ret=%d\n",
+				pdphy->base + addr, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+/* Writes a single byte to the specified register */
+static inline int pdphy_reg_write(struct usb_pdphy *pdphy, u16 addr, u8 val)
+{
+	return pdphy_bulk_reg_write(pdphy, addr, &val, 1);
+}
+
+/* Writes to the specified register limited by the bit mask */
+static int pdphy_masked_write(struct usb_pdphy *pdphy, u16 addr,
+	u8 mask, u8 val)
+{
+	int ret;
+
+	ret = regmap_update_bits(pdphy->regmap, pdphy->base + addr, mask, val);
+	if (ret) {
+		dev_err(pdphy->dev, "write failed: addr=0x%04x, ret=%d\n",
+				pdphy->base + addr, ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+int pd_phy_update_roles(enum data_role dr, enum power_role pr)
+{
+	struct usb_pdphy *pdphy = __pdphy;
+
+	return pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG,
+		(MSG_CONFIG_PORT_DATA_ROLE | MSG_CONFIG_PORT_POWER_ROLE),
+		((dr == DR_DFP ? MSG_CONFIG_PORT_DATA_ROLE : 0) |
+		 (pr == PR_SRC ? MSG_CONFIG_PORT_POWER_ROLE : 0)));
+}
+EXPORT_SYMBOL(pd_phy_update_roles);
+
+int pd_phy_update_spec_rev(enum pd_spec_rev rev)
+{
+	struct usb_pdphy *pdphy = __pdphy;
+
+	return pdphy_masked_write(pdphy, USB_PDPHY_MSG_CONFIG,
+			MSG_CONFIG_SPEC_REV_MASK, rev);
+}
+EXPORT_SYMBOL(pd_phy_update_spec_rev);
+
+int pd_phy_open(struct pd_phy_params *params)
+{
+	int ret;
+	struct usb_pdphy *pdphy = __pdphy;
+
+	if (!pdphy) {
+		pr_err("%s: pdphy not found\n", __func__);
+		return -ENODEV;
+	}
+
+	if (pdphy->is_opened) {
+		dev_err(pdphy->dev, "%s: already opened\n", __func__);
+		return -EBUSY;
+	}
+
+	pdphy->signal_cb = params->signal_cb;
+	pdphy->msg_rx_cb = params->msg_rx_cb;
+	pdphy->shutdown_cb = params->shutdown_cb;
+	pdphy->data_role = params->data_role;
+	pdphy->power_role = params->power_role;
+	pdphy->frame_filter_val = params->frame_filter_val;
+
+	dev_dbg(pdphy->dev, "%s: DR %x PR %x frame filter val %x\n", __func__,
+		pdphy->data_role, pdphy->power_role, pdphy->frame_filter_val);
+
+	ret = pdphy_enable_power(pdphy, true);
+	if (ret)
+		return ret;
+
+	/* update data and power role to be used in GoodCRC generation */
+	ret = pd_phy_update_roles(pdphy->data_role, pdphy->power_role);
+	if (ret)
+		return ret;
+
+	ret = pd_phy_update_spec_rev(params->spec_rev);
+	if (ret)
+		return ret;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_EN_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_EN_CONTROL, CONTROL_ENABLE);
+	if (ret)
+		return ret;
+
+	/* update frame filter */
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_FRAME_FILTER,
+			pdphy->frame_filter_val);
+	if (ret)
+		return ret;
+
+	/* initialize Rx buffer ownership to PDPHY HW */
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_RX_ACKNOWLEDGE, 0);
+	if (ret)
+		return ret;
+
+	pdphy->is_opened = true;
+	pdphy_enable_irq(pdphy, true);
+
+	return ret;
+}
+EXPORT_SYMBOL(pd_phy_open);
+
+int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
+{
+	u8 val;
+	int ret;
+	struct usb_pdphy *pdphy = __pdphy;
+
+	dev_dbg(pdphy->dev, "%s: type %d timeout %u\n", __func__, type,
+			timeout_ms);
+
+	if (!pdphy) {
+		pr_err("%s: pdphy not found\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!pdphy->is_opened) {
+		dev_dbg(pdphy->dev, "%s: pdphy disabled\n", __func__);
+		return -ENODEV;
+	}
+
+	pdphy->tx_status = -EINPROGRESS;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	usleep_range(2, 3);
+
+	val = (type == CABLE_RESET_SIG ? TX_CONTROL_FRAME_TYPE_CABLE_RESET : 0)
+		| TX_CONTROL_SEND_SIGNAL;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, val);
+	if (ret)
+		return ret;
+
+	ret = wait_event_interruptible_timeout(pdphy->tx_waitq,
+		pdphy->tx_status != -EINPROGRESS, msecs_to_jiffies(timeout_ms));
+	if (ret <= 0) {
+		dev_err(pdphy->dev, "%s: failed ret %d", __func__, ret);
+		return ret ? ret : -ETIMEDOUT;
+	}
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, 0);
+
+	if (pdphy->tx_status)
+		return pdphy->tx_status;
+
+	if (type == HARD_RESET_SIG)
+		/* Frame filter is reconfigured in pd_phy_open() */
+		return pdphy_reg_write(pdphy, USB_PDPHY_FRAME_FILTER, 0);
+
+	return 0;
+}
+EXPORT_SYMBOL(pd_phy_signal);
+
+int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
+	enum pd_msg_type type, unsigned int timeout_ms)
+{
+	u8 val;
+	int ret;
+	size_t total_len = data_len + USB_PDPHY_MSG_HDR_LEN;
+	struct usb_pdphy *pdphy = __pdphy;
+
+	dev_dbg(pdphy->dev, "%s: hdr %x frame type %d timeout %u\n",
+			__func__, hdr, type, timeout_ms);
+
+	if (data && data_len)
+		print_hex_dump_debug("tx data obj:", DUMP_PREFIX_NONE, 32, 4,
+				data, data_len, false);
+
+	if (!pdphy) {
+		pr_err("%s: pdphy not found\n", __func__);
+		return -ENODEV;
+	}
+
+	if (!pdphy->is_opened) {
+		dev_dbg(pdphy->dev, "%s: pdphy disabled\n", __func__);
+		return -ENODEV;
+	}
+
+	if (data_len > USB_PDPHY_MAX_DATA_OBJ_LEN) {
+		dev_err(pdphy->dev, "%s: invalid data object len %zu\n",
+			__func__, data_len);
+		return -EINVAL;
+	}
+
+	pdphy->tx_status = -EINPROGRESS;
+
+	/* write 2 byte SOP message header */
+	ret = pdphy_bulk_reg_write(pdphy, USB_PDPHY_TX_BUFFER_HDR, (u8 *)&hdr,
+			USB_PDPHY_MSG_HDR_LEN);
+	if (ret)
+		return ret;
+
+	if (data && data_len) {
+		/* write data objects of SOP message */
+		ret = pdphy_bulk_reg_write(pdphy, USB_PDPHY_TX_BUFFER_DATA,
+				data, data_len);
+		if (ret)
+			return ret;
+	}
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_SIZE, total_len - 1);
+	if (ret)
+		return ret;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, 0);
+	if (ret)
+		return ret;
+
+	usleep_range(2, 3);
+
+	val = TX_CONTROL_RETRY_COUNT | (type << 2) | TX_CONTROL_SEND_MSG;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, val);
+	if (ret)
+		return ret;
+
+	ret = wait_event_interruptible_timeout(pdphy->tx_waitq,
+		pdphy->tx_status != -EINPROGRESS, msecs_to_jiffies(timeout_ms));
+	if (ret <= 0) {
+		dev_err(pdphy->dev, "%s: failed ret %d", __func__, ret);
+		return ret ? ret : -ETIMEDOUT;
+	}
+
+	if (hdr && !pdphy->tx_status)
+		pdphy->tx_bytes += data_len + USB_PDPHY_MSG_HDR_LEN;
+
+	return pdphy->tx_status ? pdphy->tx_status : data_len;
+}
+EXPORT_SYMBOL(pd_phy_write);
+
+void pd_phy_close(void)
+{
+	int ret;
+	struct usb_pdphy *pdphy = __pdphy;
+
+	if (!pdphy) {
+		pr_err("%s: pdphy not found\n", __func__);
+		return;
+	}
+
+	if (!pdphy->is_opened) {
+		dev_err(pdphy->dev, "%s: not opened\n", __func__);
+		return;
+	}
+
+	pdphy->is_opened = false;
+	pdphy_enable_irq(pdphy, false);
+
+	pdphy->tx_status = -ESHUTDOWN;
+
+	wake_up_all(&pdphy->tx_waitq);
+
+	pdphy_reg_write(pdphy, USB_PDPHY_BIST_MODE, 0);
+	pdphy->in_test_data_mode = false;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TX_CONTROL, 0);
+	if (ret)
+		return;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_EN_CONTROL, 0);
+	if (ret)
+		return;
+
+	pdphy_enable_power(pdphy, false);
+}
+EXPORT_SYMBOL(pd_phy_close);
+
+static irqreturn_t pdphy_msg_tx_irq(int irq, void *data)
+{
+	struct usb_pdphy *pdphy = data;
+
+	if (irq == pdphy->msg_tx_irq) {
+		pdphy->msg_tx_cnt++;
+		pdphy->tx_status = 0;
+	} else if (irq == pdphy->msg_tx_discarded_irq) {
+		pdphy->msg_tx_discarded_cnt++;
+		pdphy->tx_status = -EBUSY;
+	} else if (irq == pdphy->msg_tx_failed_irq) {
+		pdphy->msg_tx_failed_cnt++;
+		pdphy->tx_status = -EFAULT;
+	} else {
+		dev_err(pdphy->dev, "spurious irq #%d received\n", irq);
+		return IRQ_NONE;
+	}
+
+	wake_up(&pdphy->tx_waitq);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pdphy_msg_rx_discarded_irq(int irq, void *data)
+{
+	struct usb_pdphy *pdphy = data;
+
+	pdphy->msg_rx_discarded_cnt++;
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pdphy_sig_rx_irq_thread(int irq, void *data)
+{
+	u8 rx_status, frame_type;
+	int ret;
+	struct usb_pdphy *pdphy = data;
+
+	pdphy->sig_rx_cnt++;
+
+	ret = pdphy_reg_read(pdphy, &rx_status, USB_PDPHY_RX_STATUS, 1);
+	if (ret)
+		goto done;
+
+	frame_type = rx_status & RX_FRAME_TYPE;
+	if (frame_type != HARD_RESET_SIG) {
+		dev_err(pdphy->dev, "%s:unsupported frame type %d\n",
+			__func__, frame_type);
+		goto done;
+	}
+
+	/* Frame filter is reconfigured in pd_phy_open() */
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_FRAME_FILTER, 0);
+
+	if (pdphy->signal_cb)
+		pdphy->signal_cb(pdphy->usbpd, frame_type);
+
+done:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t pdphy_sig_tx_irq_thread(int irq, void *data)
+{
+	struct usb_pdphy *pdphy = data;
+
+	/* in case of exit from BIST Carrier Mode 2, clear BIST_MODE */
+	pdphy_reg_write(pdphy, USB_PDPHY_BIST_MODE, 0);
+
+	pdphy->sig_tx_cnt++;
+	pdphy->tx_status = 0;
+	wake_up(&pdphy->tx_waitq);
+
+	return IRQ_HANDLED;
+}
+
+static int pd_phy_bist_mode(u8 bist_mode)
+{
+	struct usb_pdphy *pdphy = __pdphy;
+
+	dev_dbg(pdphy->dev, "%s: enter BIST mode %d\n", __func__, bist_mode);
+
+	pdphy_reg_write(pdphy, USB_PDPHY_BIST_MODE, 0);
+
+	udelay(5);
+
+	return pdphy_masked_write(pdphy, USB_PDPHY_BIST_MODE,
+			BIST_MODE_MASK | BIST_ENABLE, bist_mode | BIST_ENABLE);
+}
+
+static irqreturn_t pdphy_msg_rx_irq_thread(int irq, void *data)
+{
+	u8 size, rx_status, frame_type;
+	u8 buf[32];
+	int ret;
+	struct usb_pdphy *pdphy = data;
+
+	pdphy->msg_rx_cnt++;
+
+	ret = pdphy_reg_read(pdphy, &size, USB_PDPHY_RX_SIZE, 1);
+	if (ret)
+		goto done;
+
+	if (!size || size > 31) {
+		dev_err(pdphy->dev, "%s: invalid size %d\n", __func__, size);
+		goto done;
+	}
+
+	ret = pdphy_reg_read(pdphy, &rx_status, USB_PDPHY_RX_STATUS, 1);
+	if (ret)
+		goto done;
+
+	frame_type = rx_status & RX_FRAME_TYPE;
+	if (frame_type != SOP_MSG) {
+		dev_err(pdphy->dev, "%s:unsupported frame type %d\n",
+			__func__, frame_type);
+		goto done;
+	}
+
+	ret = pdphy_reg_read(pdphy, buf, USB_PDPHY_RX_BUFFER, size + 1);
+	if (ret)
+		goto done;
+
+	/* ack to change ownership of rx buffer back to PDPHY RX HW */
+	pdphy_reg_write(pdphy, USB_PDPHY_RX_ACKNOWLEDGE, 0);
+
+	if (((buf[0] & 0xf) == PD_MSG_BIST) && size >= 5) { /* BIST */
+		u8 mode = buf[5] >> 4; /* [31:28] of 1st data object */
+
+		pd_phy_bist_mode(mode);
+		pdphy_reg_write(pdphy, USB_PDPHY_RX_ACKNOWLEDGE, 0);
+
+		if (mode == PD_BIST_TEST_DATA_MODE) {
+			pdphy->in_test_data_mode = true;
+			disable_irq_nosync(irq);
+		}
+		goto done;
+	}
+
+	if (pdphy->msg_rx_cb)
+		pdphy->msg_rx_cb(pdphy->usbpd, frame_type, buf, size + 1);
+
+	print_hex_dump_debug("rx msg:", DUMP_PREFIX_NONE, 32, 4, buf, size + 1,
+		false);
+	pdphy->rx_bytes += size + 1;
+done:
+	return IRQ_HANDLED;
+}
+
+static int pdphy_request_irq(struct usb_pdphy *pdphy,
+				struct device_node *node,
+				int *irq_num, const char *irq_name,
+				irqreturn_t (irq_handler)(int irq, void *data),
+				irqreturn_t (thread_fn)(int irq, void *data),
+				int flags)
+{
+	int ret;
+
+	*irq_num = of_irq_get_byname(node, irq_name);
+	if (*irq_num < 0) {
+		dev_err(pdphy->dev, "Unable to get %s irqn", irq_name);
+		ret = -ENXIO;
+	}
+
+	irq_set_status_flags(*irq_num, IRQ_NOAUTOEN);
+	ret = devm_request_threaded_irq(pdphy->dev, *irq_num, irq_handler,
+			thread_fn, flags, irq_name, pdphy);
+	if (ret < 0) {
+		dev_err(pdphy->dev, "Unable to request %s irq: %dn",
+				irq_name, ret);
+		ret = -ENXIO;
+	}
+
+	return 0;
+}
+
+static int pdphy_probe(struct platform_device *pdev)
+{
+	int ret;
+	unsigned int base;
+	struct usb_pdphy *pdphy;
+
+	pdphy = devm_kzalloc(&pdev->dev, sizeof(*pdphy), GFP_KERNEL);
+	if (!pdphy)
+		return -ENOMEM;
+
+	pdphy->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+	if (!pdphy->regmap) {
+		dev_err(&pdev->dev, "Couldn't get parent's regmap\n");
+		return -EINVAL;
+	}
+
+	dev_set_drvdata(&pdev->dev, pdphy);
+
+	ret = of_property_read_u32(pdev->dev.of_node, "reg", &base);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "failed to get reg base address ret = %d\n",
+			ret);
+		return ret;
+	}
+
+	pdphy->base = base;
+	pdphy->dev = &pdev->dev;
+
+	init_waitqueue_head(&pdphy->tx_waitq);
+
+	pdphy->vdd_pdphy = devm_regulator_get(&pdev->dev, "vdd-pdphy");
+	if (IS_ERR(pdphy->vdd_pdphy)) {
+		dev_err(&pdev->dev, "unable to get vdd-pdphy\n");
+		return PTR_ERR(pdphy->vdd_pdphy);
+	}
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->sig_tx_irq, "sig-tx", NULL,
+		pdphy_sig_tx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->sig_rx_irq, "sig-rx", NULL,
+		pdphy_sig_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->msg_tx_irq, "msg-tx", pdphy_msg_tx_irq,
+		NULL, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->msg_rx_irq, "msg-rx", NULL,
+		pdphy_msg_rx_irq_thread, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->msg_tx_failed_irq, "msg-tx-failed", pdphy_msg_tx_irq,
+		NULL, (IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->msg_tx_discarded_irq, "msg-tx-discarded",
+		pdphy_msg_tx_irq, NULL,
+		(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_request_irq(pdphy, pdev->dev.of_node,
+		&pdphy->msg_rx_discarded_irq, "msg-rx-discarded",
+		pdphy_msg_rx_discarded_irq, NULL,
+		(IRQF_TRIGGER_RISING | IRQF_ONESHOT));
+	if (ret < 0)
+		return ret;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_SEC_ACCESS, 0xA5);
+	if (ret)
+		return ret;
+
+	ret = pdphy_reg_write(pdphy, USB_PDPHY_TRIM_3, 0x2);
+	if (ret)
+		return ret;
+
+	/* usbpd_create() could call back to us, so have __pdphy ready */
+	__pdphy = pdphy;
+
+	pdphy->usbpd = usbpd_create(&pdev->dev);
+	if (IS_ERR(pdphy->usbpd)) {
+		dev_err(&pdev->dev, "usbpd_create failed: %ld\n",
+				PTR_ERR(pdphy->usbpd));
+		__pdphy = NULL;
+		return PTR_ERR(pdphy->usbpd);
+	}
+
+	pdphy_create_debugfs_entries(pdphy);
+
+	return 0;
+}
+
+static int pdphy_remove(struct platform_device *pdev)
+{
+	struct usb_pdphy *pdphy = platform_get_drvdata(pdev);
+
+	debugfs_remove_recursive(pdphy->debug_root);
+	usbpd_destroy(pdphy->usbpd);
+
+	if (pdphy->is_opened)
+		pd_phy_close();
+
+	__pdphy = NULL;
+
+	return 0;
+}
+
+static void pdphy_shutdown(struct platform_device *pdev)
+{
+	struct usb_pdphy *pdphy = platform_get_drvdata(pdev);
+
+	/* let protocol engine shutdown the pdphy synchronously */
+	if (pdphy->shutdown_cb)
+		pdphy->shutdown_cb(pdphy->usbpd);
+}
+
+static const struct of_device_id pdphy_match_table[] = {
+	{
+		.compatible	 = "qcom,qpnp-pdphy",
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pdphy_match_table);
+
+static struct platform_driver pdphy_driver = {
+	 .driver	 = {
+		 .name			= "qpnp-pdphy",
+		 .of_match_table	= pdphy_match_table,
+	 },
+	 .probe		= pdphy_probe,
+	 .remove	= pdphy_remove,
+	 .shutdown	= pdphy_shutdown,
+};
+
+module_platform_driver(pdphy_driver);
+
+MODULE_DESCRIPTION("QPNP PD PHY Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qpnp-pdphy");
diff --git a/drivers/usb/pd/usbpd.h b/drivers/usb/pd/usbpd.h
new file mode 100644
index 0000000..b2663ad
--- /dev/null
+++ b/drivers/usb/pd/usbpd.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2016-2017, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _USBPD_H
+#define _USBPD_H
+
+#include <linux/device.h>
+
+struct usbpd;
+
+#if IS_ENABLED(CONFIG_USB_PD_POLICY)
+struct usbpd *usbpd_create(struct device *parent);
+void usbpd_destroy(struct usbpd *pd);
+#else
+static inline struct usbpd *usbpd_create(struct device *parent)
+{
+	return ERR_PTR(-ENODEV);
+}
+static inline void usbpd_destroy(struct usbpd *pd) { }
+#endif
+
+enum data_role {
+	DR_NONE = -1,
+	DR_UFP = 0,
+	DR_DFP = 1,
+};
+
+enum power_role {
+	PR_NONE = -1,
+	PR_SINK = 0,
+	PR_SRC = 1,
+};
+
+enum pd_sig_type {
+	HARD_RESET_SIG = 0,
+	CABLE_RESET_SIG,
+};
+
+enum pd_msg_type {
+	SOP_MSG = 0,
+	SOPI_MSG,
+	SOPII_MSG,
+};
+
+enum pd_spec_rev {
+	USBPD_REV_20 = 1,
+	USBPD_REV_30 = 2,
+};
+
+/* enable msg and signal to be received by phy */
+#define FRAME_FILTER_EN_SOP		BIT(0)
+#define FRAME_FILTER_EN_HARD_RESET	BIT(5)
+
+struct pd_phy_params {
+	void		(*signal_cb)(struct usbpd *pd, enum pd_sig_type type);
+	void		(*msg_rx_cb)(struct usbpd *pd, enum pd_msg_type type,
+					u8 *buf, size_t len);
+	void		(*shutdown_cb)(struct usbpd *pd);
+	enum data_role	data_role;
+	enum power_role power_role;
+	u8		frame_filter_val;
+	u8		spec_rev;
+};
+
+#if IS_ENABLED(CONFIG_QPNP_USB_PDPHY)
+int pd_phy_open(struct pd_phy_params *params);
+int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms);
+int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
+	enum pd_msg_type type, unsigned int timeout_ms);
+int pd_phy_update_roles(enum data_role dr, enum power_role pr);
+int pd_phy_update_spec_rev(enum pd_spec_rev rev);
+void pd_phy_close(void);
+#else
+static inline int pd_phy_open(struct pd_phy_params *params)
+{
+	return -ENODEV;
+}
+
+static inline int pd_phy_signal(enum pd_sig_type type, unsigned int timeout_ms)
+{
+	return -ENODEV;
+}
+
+static inline int pd_phy_write(u16 hdr, const u8 *data, size_t data_len,
+	enum pd_msg_type type, unsigned int timeout_ms)
+{
+	return -ENODEV;
+}
+
+static inline int pd_phy_update_roles(enum data_role dr, enum power_role pr)
+{
+	return -ENODEV;
+}
+
+static inline int pd_phy_update_spec_rev(enum pd_spec_rev rev)
+{
+	return -ENODEV;
+}
+
+static inline void pd_phy_close(void)
+{
+}
+#endif
+#endif /* _USBPD_H */
diff --git a/fs/timerfd.c b/fs/timerfd.c
index 9ae4abb..b938fa7 100644
--- a/fs/timerfd.c
+++ b/fs/timerfd.c
@@ -49,7 +49,8 @@ static DEFINE_SPINLOCK(cancel_lock);
 static inline bool isalarm(struct timerfd_ctx *ctx)
 {
 	return ctx->clockid == CLOCK_REALTIME_ALARM ||
-		ctx->clockid == CLOCK_BOOTTIME_ALARM;
+		ctx->clockid == CLOCK_BOOTTIME_ALARM ||
+		ctx->clockid == CLOCK_POWEROFF_ALARM;
 }
 
 /*
@@ -133,7 +134,8 @@ static bool timerfd_canceled(struct timerfd_ctx *ctx)
 static void timerfd_setup_cancel(struct timerfd_ctx *ctx, int flags)
 {
 	if ((ctx->clockid == CLOCK_REALTIME ||
-	     ctx->clockid == CLOCK_REALTIME_ALARM) &&
+	     ctx->clockid == CLOCK_REALTIME_ALARM ||
+	     ctx->clockid == CLOCK_POWEROFF_ALARM) &&
 	    (flags & TFD_TIMER_ABSTIME) && (flags & TFD_TIMER_CANCEL_ON_SET)) {
 		if (!ctx->might_cancel) {
 			ctx->might_cancel = true;
@@ -164,6 +166,7 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
 	enum hrtimer_mode htmode;
 	ktime_t texp;
 	int clockid = ctx->clockid;
+	enum alarmtimer_type type;
 
 	htmode = (flags & TFD_TIMER_ABSTIME) ?
 		HRTIMER_MODE_ABS: HRTIMER_MODE_REL;
@@ -174,10 +177,8 @@ static int timerfd_setup(struct timerfd_ctx *ctx, int flags,
 	ctx->tintv = timespec_to_ktime(ktmr->it_interval);
 
 	if (isalarm(ctx)) {
-		alarm_init(&ctx->t.alarm,
-			   ctx->clockid == CLOCK_REALTIME_ALARM ?
-			   ALARM_REALTIME : ALARM_BOOTTIME,
-			   timerfd_alarmproc);
+		type = clock2alarm(ctx->clockid);
+		alarm_init(&ctx->t.alarm, type, timerfd_alarmproc);
 	} else {
 		hrtimer_init(&ctx->t.tmr, clockid, htmode);
 		hrtimer_set_expires(&ctx->t.tmr, texp);
@@ -377,6 +378,7 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
 {
 	int ufd;
 	struct timerfd_ctx *ctx;
+	enum alarmtimer_type type;
 
 	/* Check the TFD_* constants for consistency.  */
 	BUILD_BUG_ON(TFD_CLOEXEC != O_CLOEXEC);
@@ -387,7 +389,8 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
 	     clockid != CLOCK_REALTIME &&
 	     clockid != CLOCK_REALTIME_ALARM &&
 	     clockid != CLOCK_BOOTTIME &&
-	     clockid != CLOCK_BOOTTIME_ALARM))
+	     clockid != CLOCK_BOOTTIME_ALARM &&
+	     clockid != CLOCK_POWEROFF_ALARM))
 		return -EINVAL;
 
 	if (!capable(CAP_WAKE_ALARM) &&
@@ -402,13 +405,12 @@ SYSCALL_DEFINE2(timerfd_create, int, clockid, int, flags)
 	init_waitqueue_head(&ctx->wqh);
 	ctx->clockid = clockid;
 
-	if (isalarm(ctx))
-		alarm_init(&ctx->t.alarm,
-			   ctx->clockid == CLOCK_REALTIME_ALARM ?
-			   ALARM_REALTIME : ALARM_BOOTTIME,
-			   timerfd_alarmproc);
-	else
+	if (isalarm(ctx)) {
+		type = clock2alarm(ctx->clockid);
+		alarm_init(&ctx->t.alarm, type, timerfd_alarmproc);
+	} else {
 		hrtimer_init(&ctx->t.tmr, clockid, HRTIMER_MODE_ABS);
+	}
 
 	ctx->moffs = ktime_mono_to_real((ktime_t){ .tv64 = 0 });
 
@@ -485,6 +487,10 @@ static int do_timerfd_settime(int ufd, int flags,
 	ret = timerfd_setup(ctx, flags, new);
 
 	spin_unlock_irq(&ctx->wqh.lock);
+
+	if (ctx->clockid == CLOCK_POWEROFF_ALARM)
+		set_power_on_alarm();
+
 	fdput(f);
 	return ret;
 }
diff --git a/include/dt-bindings/clock/qcom,audio-ext-clk.h b/include/dt-bindings/clock/qcom,audio-ext-clk.h
new file mode 100644
index 0000000..c9a8286
--- /dev/null
+++ b/include/dt-bindings/clock/qcom,audio-ext-clk.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __AUDIO_EXT_CLK_H
+#define __AUDIO_EXT_CLK_H
+
+/* Audio External Clocks */
+#ifdef CONFIG_COMMON_CLK_QCOM
+#define AUDIO_PMI_CLK		0
+#define AUDIO_PMIC_LNBB_CLK	1
+#define AUDIO_AP_CLK		2
+#define AUDIO_AP_CLK2		3
+#define AUDIO_LPASS_MCLK	4
+#define AUDIO_LPASS_MCLK2	5
+#else
+#define clk_audio_ap_clk        0x9b5727cb
+#define clk_audio_pmi_clk       0xcbfe416d
+#define clk_audio_ap_clk2       0x454d1e91
+#define clk_audio_lpass_mclk    0xf0f2a284
+#define clk_audio_pmi_lnbb_clk   0x57312343
+#endif
+
+#endif
diff --git a/include/linux/alarmtimer.h b/include/linux/alarmtimer.h
index 9d80312..8a30cb5 100644
--- a/include/linux/alarmtimer.h
+++ b/include/linux/alarmtimer.h
@@ -5,10 +5,12 @@
 #include <linux/hrtimer.h>
 #include <linux/timerqueue.h>
 #include <linux/rtc.h>
+#include <linux/types.h>
 
 enum alarmtimer_type {
 	ALARM_REALTIME,
 	ALARM_BOOTTIME,
+	ALARM_POWEROFF_REALTIME,
 
 	ALARM_NUMTYPE,
 };
@@ -48,6 +50,9 @@ void alarm_start_relative(struct alarm *alarm, ktime_t start);
 void alarm_restart(struct alarm *alarm);
 int alarm_try_to_cancel(struct alarm *alarm);
 int alarm_cancel(struct alarm *alarm);
+void set_power_on_alarm(void);
+void power_on_alarm_init(void);
+enum alarmtimer_type clock2alarm(clockid_t clockid);
 
 u64 alarm_forward(struct alarm *alarm, ktime_t now, ktime_t interval);
 u64 alarm_forward_now(struct alarm *alarm, ktime_t interval);
@@ -55,5 +60,8 @@ ktime_t alarm_expires_remaining(const struct alarm *alarm);
 
 /* Provide way to access the rtc device being used by alarmtimers */
 struct rtc_device *alarmtimer_get_rtcdev(void);
+#ifdef CONFIG_RTC_DRV_QPNP
+extern bool poweron_alarm;
+#endif
 
 #endif
diff --git a/include/linux/bluetooth-power.h b/include/linux/bluetooth-power.h
new file mode 100644
index 0000000..ef8b519b
--- /dev/null
+++ b/include/linux/bluetooth-power.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __LINUX_BLUETOOTH_POWER_H
+#define __LINUX_BLUETOOTH_POWER_H
+
+/*
+ * voltage regulator information required for configuring the
+ * bluetooth chipset
+ */
+struct bt_power_vreg_data {
+	/* voltage regulator handle */
+	struct regulator *reg;
+	/* regulator name */
+	const char *name;
+	/* voltage levels to be set */
+	unsigned int low_vol_level;
+	unsigned int high_vol_level;
+	/* current level to be set */
+	unsigned int load_uA;
+	/*
+	 * is set voltage supported for this regulator?
+	 * false => set voltage is not supported
+	 * true  => set voltage is supported
+	 *
+	 * Some regulators (like gpio-regulators, LVS (low voltage swtiches)
+	 * PMIC regulators) dont have the capability to call
+	 * regulator_set_voltage or regulator_set_optimum_mode
+	 * Use this variable to indicate if its a such regulator or not
+	 */
+	bool set_voltage_sup;
+	/* is this regulator enabled? */
+	bool is_enabled;
+};
+
+struct bt_power_clk_data {
+	/* clock regulator handle */
+	struct clk *clk;
+	/* clock name */
+	const char *name;
+	/* is this clock enabled? */
+	bool is_enabled;
+};
+
+/*
+ * Platform data for the bluetooth power driver.
+ */
+struct bluetooth_power_platform_data {
+	/* Bluetooth reset gpio */
+	int bt_gpio_sys_rst;
+	struct device *slim_dev;
+	/* VDDIO voltage regulator */
+	struct bt_power_vreg_data *bt_vdd_io;
+	/* VDD_PA voltage regulator */
+	struct bt_power_vreg_data *bt_vdd_pa;
+	/* VDD_LDOIN voltage regulator */
+	struct bt_power_vreg_data *bt_vdd_ldo;
+	/* VDD_XTAL voltage regulator */
+	struct bt_power_vreg_data *bt_vdd_xtal;
+	/* VDD_CORE voltage regulator */
+	struct bt_power_vreg_data *bt_vdd_core;
+	/* Optional: chip power down gpio-regulator
+	 * chip power down data is required when bluetooth module
+	 * and other modules like wifi co-exist in a single chip and
+	 * shares a common gpio to bring chip out of reset.
+	 */
+	struct bt_power_vreg_data *bt_chip_pwd;
+	/* bluetooth reference clock */
+	struct bt_power_clk_data *bt_chip_clk;
+	/* Optional: Bluetooth power setup function */
+	int (*bt_power_setup)(int);
+};
+
+int bt_register_slimdev(struct device *dev);
+
+#define BT_CMD_SLIM_TEST		0xbfac
+#define BT_CMD_PWR_CTRL			0xbfad
+#endif /* __LINUX_BLUETOOTH_POWER_H */
diff --git a/include/linux/cgroup_subsys.h b/include/linux/cgroup_subsys.h
index 0df0336a..7f4a2a5 100644
--- a/include/linux/cgroup_subsys.h
+++ b/include/linux/cgroup_subsys.h
@@ -20,6 +20,10 @@ SUBSYS(cpu)
 SUBSYS(cpuacct)
 #endif
 
+#if IS_ENABLED(CONFIG_CGROUP_SCHEDTUNE)
+SUBSYS(schedtune)
+#endif
+
 #if IS_ENABLED(CONFIG_BLK_CGROUP)
 SUBSYS(io)
 #endif
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 3dbcd5b..31a7f91 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -830,6 +830,9 @@ void clk_hw_reparent(struct clk_hw *hw, struct clk_hw *new_parent);
 void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
 			   unsigned long max_rate);
 
+unsigned long clk_aggregate_rate(struct clk_hw *hw,
+					const struct clk_core *parent);
+
 static inline void __clk_hw_set_clk(struct clk_hw *dst, struct clk_hw *src)
 {
 	dst->clk = src->clk;
diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h
index da7fbf1..eec093c 100644
--- a/include/linux/cpumask.h
+++ b/include/linux/cpumask.h
@@ -53,6 +53,7 @@ extern int nr_cpu_ids;
  *     cpu_present_mask - has bit 'cpu' set iff cpu is populated
  *     cpu_online_mask  - has bit 'cpu' set iff cpu available to scheduler
  *     cpu_active_mask  - has bit 'cpu' set iff cpu available to migration
+ *     cpu_isolated_mask- has bit 'cpu' set iff cpu isolated
  *
  *  If !CONFIG_HOTPLUG_CPU, present == possible, and active == online.
  *
@@ -89,29 +90,35 @@ extern struct cpumask __cpu_possible_mask;
 extern struct cpumask __cpu_online_mask;
 extern struct cpumask __cpu_present_mask;
 extern struct cpumask __cpu_active_mask;
+extern struct cpumask __cpu_isolated_mask;
 #define cpu_possible_mask ((const struct cpumask *)&__cpu_possible_mask)
 #define cpu_online_mask   ((const struct cpumask *)&__cpu_online_mask)
 #define cpu_present_mask  ((const struct cpumask *)&__cpu_present_mask)
 #define cpu_active_mask   ((const struct cpumask *)&__cpu_active_mask)
+#define cpu_isolated_mask ((const struct cpumask *)&__cpu_isolated_mask)
 
 #if NR_CPUS > 1
 #define num_online_cpus()	cpumask_weight(cpu_online_mask)
 #define num_possible_cpus()	cpumask_weight(cpu_possible_mask)
 #define num_present_cpus()	cpumask_weight(cpu_present_mask)
 #define num_active_cpus()	cpumask_weight(cpu_active_mask)
+#define num_isolated_cpus()	cpumask_weight(cpu_isolated_mask)
 #define cpu_online(cpu)		cpumask_test_cpu((cpu), cpu_online_mask)
 #define cpu_possible(cpu)	cpumask_test_cpu((cpu), cpu_possible_mask)
 #define cpu_present(cpu)	cpumask_test_cpu((cpu), cpu_present_mask)
 #define cpu_active(cpu)		cpumask_test_cpu((cpu), cpu_active_mask)
+#define cpu_isolated(cpu)	cpumask_test_cpu((cpu), cpu_isolated_mask)
 #else
 #define num_online_cpus()	1U
 #define num_possible_cpus()	1U
 #define num_present_cpus()	1U
 #define num_active_cpus()	1U
+#define num_isolated_cpus()	0U
 #define cpu_online(cpu)		((cpu) == 0)
 #define cpu_possible(cpu)	((cpu) == 0)
 #define cpu_present(cpu)	((cpu) == 0)
 #define cpu_active(cpu)		((cpu) == 0)
+#define cpu_isolated(cpu)	((cpu) != 0)
 #endif
 
 /* verify cpu argument to cpumask_* operators */
@@ -716,6 +723,7 @@ extern const DECLARE_BITMAP(cpu_all_bits, NR_CPUS);
 #define for_each_possible_cpu(cpu) for_each_cpu((cpu), cpu_possible_mask)
 #define for_each_online_cpu(cpu)   for_each_cpu((cpu), cpu_online_mask)
 #define for_each_present_cpu(cpu)  for_each_cpu((cpu), cpu_present_mask)
+#define for_each_isolated_cpu(cpu) for_each_cpu((cpu), cpu_isolated_mask)
 
 /* Wrappers for arch boot code to manipulate normally-constant masks */
 void init_cpu_present(const struct cpumask *src);
@@ -758,6 +766,15 @@ set_cpu_active(unsigned int cpu, bool active)
 		cpumask_clear_cpu(cpu, &__cpu_active_mask);
 }
 
+static inline void
+set_cpu_isolated(unsigned int cpu, bool isolated)
+{
+	if (isolated)
+		cpumask_set_cpu(cpu, &__cpu_isolated_mask);
+	else
+		cpumask_clear_cpu(cpu, &__cpu_isolated_mask);
+}
+
 
 /**
  * to_cpumask - convert an NR_CPUS bitmap to a struct cpumask *
diff --git a/include/linux/device.h b/include/linux/device.h
index f54e6dd..d85101c 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -1030,6 +1030,7 @@ static inline bool device_supports_offline(struct device *dev)
 extern void lock_device_hotplug(void);
 extern void unlock_device_hotplug(void);
 extern int lock_device_hotplug_sysfs(void);
+extern void lock_device_hotplug_assert(void);
 extern int device_offline(struct device *dev);
 extern int device_online(struct device *dev);
 extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index 5e00f80..d3b4cf4 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -53,6 +53,7 @@ enum hrtimer_restart {
  *
  * 0x00		inactive
  * 0x01		enqueued into rbtree
+ * 0x02		timer is pinned to a cpu
  *
  * The callback state is not part of the timer->state because clearing it would
  * mean touching the timer after the callback, this makes it impossible to free
@@ -72,6 +73,8 @@ enum hrtimer_restart {
  */
 #define HRTIMER_STATE_INACTIVE	0x00
 #define HRTIMER_STATE_ENQUEUED	0x01
+#define HRTIMER_PINNED_SHIFT	1
+#define HRTIMER_STATE_PINNED	(1 << HRTIMER_PINNED_SHIFT)
 
 /**
  * struct hrtimer - the basic hrtimer structure
@@ -357,6 +360,9 @@ DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
 
 /* Exported timer functions: */
 
+/* To be used from cpusets, only */
+extern void hrtimer_quiesce_cpu(void *cpup);
+
 /* Initialize timers: */
 extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
 			 enum hrtimer_mode mode);
diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h
index 9edccfb..72723e8 100644
--- a/include/linux/iio/consumer.h
+++ b/include/linux/iio/consumer.h
@@ -226,6 +226,16 @@ int iio_read_channel_processed(struct iio_channel *chan, int *val);
 int iio_write_channel_raw(struct iio_channel *chan, int val);
 
 /**
+ * iio_write_channel_processed() - write to a given channel
+ * @chan:		The channel being queried.
+ * @val:		Value being written.
+ *
+ * Note processed writes to iio channels are converted to raw
+ * values before being written.
+ */
+int iio_write_channel_processed(struct iio_channel *chan, int val);
+
+/**
  * iio_get_channel_type() - get the type of a channel
  * @channel:		The channel being queried.
  * @type:		The type of the channel.
diff --git a/include/linux/leds-qpnp-flash-v2.h b/include/linux/leds-qpnp-flash-v2.h
new file mode 100644
index 0000000..1ae77e2
--- /dev/null
+++ b/include/linux/leds-qpnp-flash-v2.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.	1
+ */
+
+#ifndef __LEDS_QPNP_FLASH_V2_H
+#define __LEDS_QPNP_FLASH_V2_H
+
+#include <linux/leds.h>
+#include <linux/notifier.h>
+
+enum flash_led_irq_type {
+	LED_FAULT_IRQ = BIT(0),
+	MITIGATION_IRQ = BIT(1),
+	FLASH_TIMER_EXP_IRQ = BIT(2),
+	ALL_RAMP_DOWN_DONE_IRQ = BIT(3),
+	ALL_RAMP_UP_DONE_IRQ = BIT(4),
+	LED3_RAMP_UP_DONE_IRQ = BIT(5),
+	LED2_RAMP_UP_DONE_IRQ = BIT(6),
+	LED1_RAMP_UP_DONE_IRQ = BIT(7),
+	INVALID_IRQ = BIT(8),
+};
+
+int qpnp_flash_led_register_irq_notifier(struct notifier_block *nb);
+int qpnp_flash_led_unregister_irq_notifier(struct notifier_block *nb);
+
+#endif
diff --git a/include/linux/leds-qpnp-flash.h b/include/linux/leds-qpnp-flash.h
new file mode 100644
index 0000000..4b5a339
--- /dev/null
+++ b/include/linux/leds-qpnp-flash.h
@@ -0,0 +1,28 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LEDS_QPNP_FLASH_H
+#define __LEDS_QPNP_FLASH_H
+
+#include <linux/leds.h>
+
+#define ENABLE_REGULATOR	BIT(0)
+#define DISABLE_REGULATOR	BIT(1)
+#define QUERY_MAX_CURRENT	BIT(2)
+#define PRE_FLASH		BIT(3)
+
+#define FLASH_LED_PREPARE_OPTIONS_MASK	GENMASK(3, 0)
+
+int qpnp_flash_led_prepare(struct led_trigger *trig, int options,
+					int *max_current);
+
+#endif
diff --git a/include/linux/leds-qpnp-wled.h b/include/linux/leds-qpnp-wled.h
new file mode 100644
index 0000000..6880bc4
--- /dev/null
+++ b/include/linux/leds-qpnp-wled.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __LEDS_QPNP_WLED_H
+
+#ifdef CONFIG_LEDS_QPNP_WLED
+int qpnp_ibb_enable(bool state);
+#else
+int qpnp_ibb_enable(bool state)
+{
+	return 0;
+}
+#endif
+#endif
diff --git a/include/linux/leds.h b/include/linux/leds.h
index ddfcb2d..66a1f4e 100644
--- a/include/linux/leds.h
+++ b/include/linux/leds.h
@@ -52,6 +52,7 @@ struct led_classdev {
 #define LED_DEV_CAP_FLASH	(1 << 24)
 #define LED_HW_PLUGGABLE	(1 << 25)
 #define LED_PANIC_INDICATOR	(1 << 26)
+#define LED_KEEP_TRIGGER	(1 << 27)
 
 	/* Set LED brightness level
 	 * Must not sleep. Use brightness_set_blocking for drivers
diff --git a/include/linux/mfd/msm-cdc-pinctrl.h b/include/linux/mfd/msm-cdc-pinctrl.h
new file mode 100644
index 0000000..14b18fe
--- /dev/null
+++ b/include/linux/mfd/msm-cdc-pinctrl.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MFD_CDC_PINCTRL_H_
+#define __MFD_CDC_PINCTRL_H_
+
+#include <linux/types.h>
+#include <linux/of.h>
+
+#ifdef CONFIG_MSM_CDC_PINCTRL
+extern int msm_cdc_pinctrl_select_sleep_state(struct device_node *np);
+extern int msm_cdc_pinctrl_select_active_state(struct device_node *np);
+extern bool msm_cdc_pinctrl_get_state(struct device_node *np);
+extern int msm_cdc_get_gpio_state(struct device_node *np);
+
+#else
+int msm_cdc_pinctrl_select_sleep_state(struct device_node *np)
+{
+	return 0;
+}
+int msm_cdc_pinctrl_select_active_state(struct device_node *np)
+{
+	return 0;
+}
+int msm_cdc_get_gpio_state(struct device_node *np)
+{
+	return 0;
+}
+#
+#endif
+
+#endif
diff --git a/include/linux/mfd/msm-cdc-supply.h b/include/linux/mfd/msm-cdc-supply.h
new file mode 100644
index 0000000..b40f44b
--- /dev/null
+++ b/include/linux/mfd/msm-cdc-supply.h
@@ -0,0 +1,48 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CODEC_POWER_SUPPLY_H__
+#define __CODEC_POWER_SUPPLY_H__
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+
+struct cdc_regulator {
+	const char *name;
+	int min_uV;
+	int max_uV;
+	int optimum_uA;
+	bool ondemand;
+	struct regulator *regulator;
+};
+
+extern int msm_cdc_get_power_supplies(struct device *dev,
+				      struct cdc_regulator **cdc_vreg,
+				      int *total_num_supplies);
+extern int msm_cdc_disable_static_supplies(struct device *dev,
+					struct regulator_bulk_data *supplies,
+					struct cdc_regulator *cdc_vreg,
+					int num_supplies);
+extern int msm_cdc_release_supplies(struct device *dev,
+				    struct regulator_bulk_data *supplies,
+				    struct cdc_regulator *cdc_vreg,
+				    int num_supplies);
+extern int msm_cdc_enable_static_supplies(struct device *dev,
+					  struct regulator_bulk_data *supplies,
+					  struct cdc_regulator *cdc_vreg,
+					  int num_supplies);
+extern int msm_cdc_init_supplies(struct device *dev,
+				 struct regulator_bulk_data **supplies,
+				 struct cdc_regulator *cdc_vreg,
+				 int num_supplies);
+#endif
diff --git a/include/linux/mfd/wcd9335/registers.h b/include/linux/mfd/wcd9335/registers.h
new file mode 100644
index 0000000..c50430d
--- /dev/null
+++ b/include/linux/mfd/wcd9335/registers.h
@@ -0,0 +1,1348 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD9335_REGISTERS_H
+#define _WCD9335_REGISTERS_H
+
+#define WCD9335_PAGE_SIZE 256
+#define WCD9335_NUM_PAGES 256
+
+extern const u8 *wcd9335_reg[WCD9335_NUM_PAGES];
+
+enum {
+	PAGE_0 = 0,
+	PAGE_1,
+	PAGE_2,
+	PAGE_6 = 6,
+	PAGE_10 = 0xA,
+	PAGE_11,
+	PAGE_12,
+	PAGE_13,
+	PAGE_0X80,
+};
+
+/* Page-0 Registers */
+#define WCD9335_PAGE0_PAGE_REGISTER                      0x0000
+#define WCD9335_CODEC_RPM_CLK_BYPASS                     0x0001
+#define WCD9335_CODEC_RPM_CLK_GATE                       0x0002
+#define WCD9335_CODEC_RPM_CLK_MCLK_CFG                   0x0003
+#define WCD9335_CODEC_RPM_RST_CTL                        0x0009
+#define WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL             0x0011
+#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_1              0x0012
+#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_2              0x0013
+#define WCD9335_CODEC_RPM_PWR_CPE_DEEPSLP_3              0x0014
+#define WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN          0x0015
+#define WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN         0x0016
+#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1       0x0017
+#define WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2       0x0018
+#define WCD9335_CODEC_RPM_INT_MASK                       0x001d
+#define WCD9335_CODEC_RPM_INT_STATUS                     0x001e
+#define WCD9335_CODEC_RPM_INT_CLEAR                      0x001f
+#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0             0x0021
+#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE1             0x0022
+#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2             0x0023
+#define WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE3             0x0024
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_CTL                 0x0025
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST0               0x0026
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_TEST1               0x0027
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0            0x0029
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1            0x002a
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2            0x002b
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT3            0x002c
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT4            0x002d
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT5            0x002e
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT6            0x002f
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT7            0x0030
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT8            0x0031
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT9            0x0032
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10           0x0033
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11           0x0034
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12           0x0035
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT13           0x0036
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT14           0x0037
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT15           0x0038
+#define WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS              0x0039
+#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO      0x003a
+#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_1            0x003b
+#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_2            0x003c
+#define WCD9335_CHIP_TIER_CTRL_I2C_SLAVE_ID_3            0x003d
+#define WCD9335_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL        0x003e
+#define WCD9335_CHIP_TIER_CTRL_I2C_ACTIVE                0x003f
+#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CTL             0x0041
+#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_STATUS          0x0042
+#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_MSB         0x0043
+#define WCD9335_CHIP_TIER_CTRL_PROC1_MON_CNT_LSB         0x0044
+#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CTL             0x0045
+#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_STATUS          0x0046
+#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_MSB         0x0047
+#define WCD9335_CHIP_TIER_CTRL_PROC2_MON_CNT_LSB         0x0048
+#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CTL             0x0049
+#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_STATUS          0x004a
+#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_MSB         0x004b
+#define WCD9335_CHIP_TIER_CTRL_PROC3_MON_CNT_LSB         0x004c
+#define WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL             0x0051
+#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL             0x0052
+#define WCD9335_DATA_HUB_DATA_HUB_I2S_CLK                0x0053
+#define WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG            0x0054
+#define WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG            0x0055
+#define WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG            0x0056
+#define WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG            0x0057
+#define WCD9335_DATA_HUB_DATA_HUB_RX4_INP_CFG            0x0058
+#define WCD9335_DATA_HUB_DATA_HUB_RX5_INP_CFG            0x0059
+#define WCD9335_DATA_HUB_DATA_HUB_RX6_INP_CFG            0x005a
+#define WCD9335_DATA_HUB_DATA_HUB_RX7_INP_CFG            0x005b
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX0_INP_CFG         0x0061
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX1_INP_CFG         0x0062
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX2_INP_CFG         0x0063
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX3_INP_CFG         0x0064
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX4_INP_CFG         0x0065
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX5_INP_CFG         0x0066
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX6_INP_CFG         0x0067
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX7_INP_CFG         0x0068
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX8_INP_CFG         0x0069
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX9_INP_CFG         0x006a
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX10_INP_CFG        0x006b
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG        0x006c
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG        0x006e
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX14_INP_CFG        0x006f
+#define WCD9335_DATA_HUB_DATA_HUB_SB_TX15_INP_CFG        0x0070
+#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG       0x0071
+#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG       0x0072
+#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG       0x0073
+#define WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG       0x0074
+#define WCD9335_DATA_HUB_NATIVE_FIFO_SYNC                0x0075
+#define WCD9335_DATA_HUB_NATIVE_FIFO_STATUS              0x007D
+#define WCD9335_INTR_CFG                                 0x0081
+#define WCD9335_INTR_CLR_COMMIT                          0x0082
+#define WCD9335_INTR_PIN1_MASK0                          0x0089
+#define WCD9335_INTR_PIN1_MASK1                          0x008a
+#define WCD9335_INTR_PIN1_MASK2                          0x008b
+#define WCD9335_INTR_PIN1_MASK3                          0x008c
+#define WCD9335_INTR_PIN1_STATUS0                        0x0091
+#define WCD9335_INTR_PIN1_STATUS1                        0x0092
+#define WCD9335_INTR_PIN1_STATUS2                        0x0093
+#define WCD9335_INTR_PIN1_STATUS3                        0x0094
+#define WCD9335_INTR_PIN1_CLEAR0                         0x0099
+#define WCD9335_INTR_PIN1_CLEAR1                         0x009a
+#define WCD9335_INTR_PIN1_CLEAR2                         0x009b
+#define WCD9335_INTR_PIN1_CLEAR3                         0x009c
+#define WCD9335_INTR_PIN2_MASK0                          0x00a1
+#define WCD9335_INTR_PIN2_MASK1                          0x00a2
+#define WCD9335_INTR_PIN2_MASK2                          0x00a3
+#define WCD9335_INTR_PIN2_MASK3                          0x00a4
+#define WCD9335_INTR_PIN2_STATUS0                        0x00a9
+#define WCD9335_INTR_PIN2_STATUS1                        0x00aa
+#define WCD9335_INTR_PIN2_STATUS2                        0x00ab
+#define WCD9335_INTR_PIN2_STATUS3                        0x00ac
+#define WCD9335_INTR_PIN2_CLEAR0                         0x00b1
+#define WCD9335_INTR_PIN2_CLEAR1                         0x00b2
+#define WCD9335_INTR_PIN2_CLEAR2                         0x00b3
+#define WCD9335_INTR_PIN2_CLEAR3                         0x00b4
+#define WCD9335_INTR_LEVEL0                              0x00e1
+#define WCD9335_INTR_LEVEL1                              0x00e2
+#define WCD9335_INTR_LEVEL2                              0x00e3
+#define WCD9335_INTR_LEVEL3                              0x00e4
+#define WCD9335_INTR_BYPASS0                             0x00e9
+#define WCD9335_INTR_BYPASS1                             0x00ea
+#define WCD9335_INTR_BYPASS2                             0x00eb
+#define WCD9335_INTR_BYPASS3                             0x00ec
+#define WCD9335_INTR_SET0                                0x00f1
+#define WCD9335_INTR_SET1                                0x00f2
+#define WCD9335_INTR_SET2                                0x00f3
+#define WCD9335_INTR_SET3                                0x00f4
+
+/* Page-1 Registers */
+#define WCD9335_PAGE1_PAGE_REGISTER                      0x0100
+#define WCD9335_CPE_FLL_USER_CTL_0                       0x0101
+#define WCD9335_CPE_FLL_USER_CTL_1                       0x0102
+#define WCD9335_CPE_FLL_USER_CTL_2                       0x0103
+#define WCD9335_CPE_FLL_USER_CTL_3                       0x0104
+#define WCD9335_CPE_FLL_USER_CTL_4                       0x0105
+#define WCD9335_CPE_FLL_USER_CTL_5                       0x0106
+#define WCD9335_CPE_FLL_USER_CTL_6                       0x0107
+#define WCD9335_CPE_FLL_USER_CTL_7                       0x0108
+#define WCD9335_CPE_FLL_USER_CTL_8                       0x0109
+#define WCD9335_CPE_FLL_USER_CTL_9                       0x010a
+#define WCD9335_CPE_FLL_L_VAL_CTL_0                      0x010b
+#define WCD9335_CPE_FLL_L_VAL_CTL_1                      0x010c
+#define WCD9335_CPE_FLL_DSM_FRAC_CTL_0                   0x010d
+#define WCD9335_CPE_FLL_DSM_FRAC_CTL_1                   0x010e
+#define WCD9335_CPE_FLL_CONFIG_CTL_0                     0x010f
+#define WCD9335_CPE_FLL_CONFIG_CTL_1                     0x0110
+#define WCD9335_CPE_FLL_CONFIG_CTL_2                     0x0111
+#define WCD9335_CPE_FLL_CONFIG_CTL_3                     0x0112
+#define WCD9335_CPE_FLL_CONFIG_CTL_4                     0x0113
+#define WCD9335_CPE_FLL_TEST_CTL_0                       0x0114
+#define WCD9335_CPE_FLL_TEST_CTL_1                       0x0115
+#define WCD9335_CPE_FLL_TEST_CTL_2                       0x0116
+#define WCD9335_CPE_FLL_TEST_CTL_3                       0x0117
+#define WCD9335_CPE_FLL_TEST_CTL_4                       0x0118
+#define WCD9335_CPE_FLL_TEST_CTL_5                       0x0119
+#define WCD9335_CPE_FLL_TEST_CTL_6                       0x011a
+#define WCD9335_CPE_FLL_TEST_CTL_7                       0x011b
+#define WCD9335_CPE_FLL_FREQ_CTL_0                       0x011c
+#define WCD9335_CPE_FLL_FREQ_CTL_1                       0x011d
+#define WCD9335_CPE_FLL_FREQ_CTL_2                       0x011e
+#define WCD9335_CPE_FLL_FREQ_CTL_3                       0x011f
+#define WCD9335_CPE_FLL_SSC_CTL_0                        0x0120
+#define WCD9335_CPE_FLL_SSC_CTL_1                        0x0121
+#define WCD9335_CPE_FLL_SSC_CTL_2                        0x0122
+#define WCD9335_CPE_FLL_SSC_CTL_3                        0x0123
+#define WCD9335_CPE_FLL_FLL_MODE                         0x0124
+#define WCD9335_CPE_FLL_STATUS_0                         0x0125
+#define WCD9335_CPE_FLL_STATUS_1                         0x0126
+#define WCD9335_CPE_FLL_STATUS_2                         0x0127
+#define WCD9335_CPE_FLL_STATUS_3                         0x0128
+#define WCD9335_I2S_FLL_USER_CTL_0                       0x0141
+#define WCD9335_I2S_FLL_USER_CTL_1                       0x0142
+#define WCD9335_I2S_FLL_USER_CTL_2                       0x0143
+#define WCD9335_I2S_FLL_USER_CTL_3                       0x0144
+#define WCD9335_I2S_FLL_USER_CTL_4                       0x0145
+#define WCD9335_I2S_FLL_USER_CTL_5                       0x0146
+#define WCD9335_I2S_FLL_USER_CTL_6                       0x0147
+#define WCD9335_I2S_FLL_USER_CTL_7                       0x0148
+#define WCD9335_I2S_FLL_USER_CTL_8                       0x0149
+#define WCD9335_I2S_FLL_USER_CTL_9                       0x014a
+#define WCD9335_I2S_FLL_L_VAL_CTL_0                      0x014b
+#define WCD9335_I2S_FLL_L_VAL_CTL_1                      0x014c
+#define WCD9335_I2S_FLL_DSM_FRAC_CTL_0                   0x014d
+#define WCD9335_I2S_FLL_DSM_FRAC_CTL_1                   0x014e
+#define WCD9335_I2S_FLL_CONFIG_CTL_0                     0x014f
+#define WCD9335_I2S_FLL_CONFIG_CTL_1                     0x0150
+#define WCD9335_I2S_FLL_CONFIG_CTL_2                     0x0151
+#define WCD9335_I2S_FLL_CONFIG_CTL_3                     0x0152
+#define WCD9335_I2S_FLL_CONFIG_CTL_4                     0x0153
+#define WCD9335_I2S_FLL_TEST_CTL_0                       0x0154
+#define WCD9335_I2S_FLL_TEST_CTL_1                       0x0155
+#define WCD9335_I2S_FLL_TEST_CTL_2                       0x0156
+#define WCD9335_I2S_FLL_TEST_CTL_3                       0x0157
+#define WCD9335_I2S_FLL_TEST_CTL_4                       0x0158
+#define WCD9335_I2S_FLL_TEST_CTL_5                       0x0159
+#define WCD9335_I2S_FLL_TEST_CTL_6                       0x015a
+#define WCD9335_I2S_FLL_TEST_CTL_7                       0x015b
+#define WCD9335_I2S_FLL_FREQ_CTL_0                       0x015c
+#define WCD9335_I2S_FLL_FREQ_CTL_1                       0x015d
+#define WCD9335_I2S_FLL_FREQ_CTL_2                       0x015e
+#define WCD9335_I2S_FLL_FREQ_CTL_3                       0x015f
+#define WCD9335_I2S_FLL_SSC_CTL_0                        0x0160
+#define WCD9335_I2S_FLL_SSC_CTL_1                        0x0161
+#define WCD9335_I2S_FLL_SSC_CTL_2                        0x0162
+#define WCD9335_I2S_FLL_SSC_CTL_3                        0x0163
+#define WCD9335_I2S_FLL_FLL_MODE                         0x0164
+#define WCD9335_I2S_FLL_STATUS_0                         0x0165
+#define WCD9335_I2S_FLL_STATUS_1                         0x0166
+#define WCD9335_I2S_FLL_STATUS_2                         0x0167
+#define WCD9335_I2S_FLL_STATUS_3                         0x0168
+#define WCD9335_SB_FLL_USER_CTL_0                        0x0181
+#define WCD9335_SB_FLL_USER_CTL_1                        0x0182
+#define WCD9335_SB_FLL_USER_CTL_2                        0x0183
+#define WCD9335_SB_FLL_USER_CTL_3                        0x0184
+#define WCD9335_SB_FLL_USER_CTL_4                        0x0185
+#define WCD9335_SB_FLL_USER_CTL_5                        0x0186
+#define WCD9335_SB_FLL_USER_CTL_6                        0x0187
+#define WCD9335_SB_FLL_USER_CTL_7                        0x0188
+#define WCD9335_SB_FLL_USER_CTL_8                        0x0189
+#define WCD9335_SB_FLL_USER_CTL_9                        0x018a
+#define WCD9335_SB_FLL_L_VAL_CTL_0                       0x018b
+#define WCD9335_SB_FLL_L_VAL_CTL_1                       0x018c
+#define WCD9335_SB_FLL_DSM_FRAC_CTL_0                    0x018d
+#define WCD9335_SB_FLL_DSM_FRAC_CTL_1                    0x018e
+#define WCD9335_SB_FLL_CONFIG_CTL_0                      0x018f
+#define WCD9335_SB_FLL_CONFIG_CTL_1                      0x0190
+#define WCD9335_SB_FLL_CONFIG_CTL_2                      0x0191
+#define WCD9335_SB_FLL_CONFIG_CTL_3                      0x0192
+#define WCD9335_SB_FLL_CONFIG_CTL_4                      0x0193
+#define WCD9335_SB_FLL_TEST_CTL_0                        0x0194
+#define WCD9335_SB_FLL_TEST_CTL_1                        0x0195
+#define WCD9335_SB_FLL_TEST_CTL_2                        0x0196
+#define WCD9335_SB_FLL_TEST_CTL_3                        0x0197
+#define WCD9335_SB_FLL_TEST_CTL_4                        0x0198
+#define WCD9335_SB_FLL_TEST_CTL_5                        0x0199
+#define WCD9335_SB_FLL_TEST_CTL_6                        0x019a
+#define WCD9335_SB_FLL_TEST_CTL_7                        0x019b
+#define WCD9335_SB_FLL_FREQ_CTL_0                        0x019c
+#define WCD9335_SB_FLL_FREQ_CTL_1                        0x019d
+#define WCD9335_SB_FLL_FREQ_CTL_2                        0x019e
+#define WCD9335_SB_FLL_FREQ_CTL_3                        0x019f
+#define WCD9335_SB_FLL_SSC_CTL_0                         0x01a0
+#define WCD9335_SB_FLL_SSC_CTL_1                         0x01a1
+#define WCD9335_SB_FLL_SSC_CTL_2                         0x01a2
+#define WCD9335_SB_FLL_SSC_CTL_3                         0x01a3
+#define WCD9335_SB_FLL_FLL_MODE                          0x01a4
+#define WCD9335_SB_FLL_STATUS_0                          0x01a5
+#define WCD9335_SB_FLL_STATUS_1                          0x01a6
+#define WCD9335_SB_FLL_STATUS_2                          0x01a7
+#define WCD9335_SB_FLL_STATUS_3                          0x01a8
+
+/* Page-2 Registers */
+#define WCD9335_PAGE2_PAGE_REGISTER                      0x0200
+#define WCD9335_CPE_SS_MEM_PTR_0                         0x0201
+#define WCD9335_CPE_SS_MEM_PTR_1                         0x0202
+#define WCD9335_CPE_SS_MEM_PTR_2                         0x0203
+#define WCD9335_CPE_SS_MEM_CTRL                          0x0205
+#define WCD9335_CPE_SS_MEM_BANK_0                        0x0206
+#define WCD9335_CPE_SS_MEM_BANK_1                        0x0207
+#define WCD9335_CPE_SS_MEM_BANK_2                        0x0208
+#define WCD9335_CPE_SS_MEM_BANK_3                        0x0209
+#define WCD9335_CPE_SS_MEM_BANK_4                        0x020a
+#define WCD9335_CPE_SS_MEM_BANK_5                        0x020b
+#define WCD9335_CPE_SS_MEM_BANK_6                        0x020c
+#define WCD9335_CPE_SS_MEM_BANK_7                        0x020d
+#define WCD9335_CPE_SS_MEM_BANK_8                        0x020e
+#define WCD9335_CPE_SS_MEM_BANK_9                        0x020f
+#define WCD9335_CPE_SS_MEM_BANK_10                       0x0210
+#define WCD9335_CPE_SS_MEM_BANK_11                       0x0211
+#define WCD9335_CPE_SS_MEM_BANK_12                       0x0212
+#define WCD9335_CPE_SS_MEM_BANK_13                       0x0213
+#define WCD9335_CPE_SS_MEM_BANK_14                       0x0214
+#define WCD9335_CPE_SS_MEM_BANK_15                       0x0215
+#define WCD9335_CPE_SS_INBOX1_TRG                        0x0216
+#define WCD9335_CPE_SS_INBOX2_TRG                        0x0217
+#define WCD9335_CPE_SS_INBOX1_0                          0x0218
+#define WCD9335_CPE_SS_INBOX1_1                          0x0219
+#define WCD9335_CPE_SS_INBOX1_2                          0x021a
+#define WCD9335_CPE_SS_INBOX1_3                          0x021b
+#define WCD9335_CPE_SS_INBOX1_4                          0x021c
+#define WCD9335_CPE_SS_INBOX1_5                          0x021d
+#define WCD9335_CPE_SS_INBOX1_6                          0x021e
+#define WCD9335_CPE_SS_INBOX1_7                          0x021f
+#define WCD9335_CPE_SS_INBOX1_8                          0x0220
+#define WCD9335_CPE_SS_INBOX1_9                          0x0221
+#define WCD9335_CPE_SS_INBOX1_10                         0x0222
+#define WCD9335_CPE_SS_INBOX1_11                         0x0223
+#define WCD9335_CPE_SS_INBOX1_12                         0x0224
+#define WCD9335_CPE_SS_INBOX1_13                         0x0225
+#define WCD9335_CPE_SS_INBOX1_14                         0x0226
+#define WCD9335_CPE_SS_INBOX1_15                         0x0227
+#define WCD9335_CPE_SS_OUTBOX1_0                         0x0228
+#define WCD9335_CPE_SS_OUTBOX1_1                         0x0229
+#define WCD9335_CPE_SS_OUTBOX1_2                         0x022a
+#define WCD9335_CPE_SS_OUTBOX1_3                         0x022b
+#define WCD9335_CPE_SS_OUTBOX1_4                         0x022c
+#define WCD9335_CPE_SS_OUTBOX1_5                         0x022d
+#define WCD9335_CPE_SS_OUTBOX1_6                         0x022e
+#define WCD9335_CPE_SS_OUTBOX1_7                         0x022f
+#define WCD9335_CPE_SS_OUTBOX1_8                         0x0230
+#define WCD9335_CPE_SS_OUTBOX1_9                         0x0231
+#define WCD9335_CPE_SS_OUTBOX1_10                        0x0232
+#define WCD9335_CPE_SS_OUTBOX1_11                        0x0233
+#define WCD9335_CPE_SS_OUTBOX1_12                        0x0234
+#define WCD9335_CPE_SS_OUTBOX1_13                        0x0235
+#define WCD9335_CPE_SS_OUTBOX1_14                        0x0236
+#define WCD9335_CPE_SS_OUTBOX1_15                        0x0237
+#define WCD9335_CPE_SS_INBOX2_0                          0x0238
+#define WCD9335_CPE_SS_INBOX2_1                          0x0239
+#define WCD9335_CPE_SS_INBOX2_2                          0x023a
+#define WCD9335_CPE_SS_INBOX2_3                          0x023b
+#define WCD9335_CPE_SS_INBOX2_4                          0x023c
+#define WCD9335_CPE_SS_INBOX2_5                          0x023d
+#define WCD9335_CPE_SS_INBOX2_6                          0x023e
+#define WCD9335_CPE_SS_INBOX2_7                          0x023f
+#define WCD9335_CPE_SS_INBOX2_8                          0x0240
+#define WCD9335_CPE_SS_INBOX2_9                          0x0241
+#define WCD9335_CPE_SS_INBOX2_10                         0x0242
+#define WCD9335_CPE_SS_INBOX2_11                         0x0243
+#define WCD9335_CPE_SS_INBOX2_12                         0x0244
+#define WCD9335_CPE_SS_INBOX2_13                         0x0245
+#define WCD9335_CPE_SS_INBOX2_14                         0x0246
+#define WCD9335_CPE_SS_INBOX2_15                         0x0247
+#define WCD9335_CPE_SS_OUTBOX2_0                         0x0248
+#define WCD9335_CPE_SS_OUTBOX2_1                         0x0249
+#define WCD9335_CPE_SS_OUTBOX2_2                         0x024a
+#define WCD9335_CPE_SS_OUTBOX2_3                         0x024b
+#define WCD9335_CPE_SS_OUTBOX2_4                         0x024c
+#define WCD9335_CPE_SS_OUTBOX2_5                         0x024d
+#define WCD9335_CPE_SS_OUTBOX2_6                         0x024e
+#define WCD9335_CPE_SS_OUTBOX2_7                         0x024f
+#define WCD9335_CPE_SS_OUTBOX2_8                         0x0250
+#define WCD9335_CPE_SS_OUTBOX2_9                         0x0251
+#define WCD9335_CPE_SS_OUTBOX2_10                        0x0252
+#define WCD9335_CPE_SS_OUTBOX2_11                        0x0253
+#define WCD9335_CPE_SS_OUTBOX2_12                        0x0254
+#define WCD9335_CPE_SS_OUTBOX2_13                        0x0255
+#define WCD9335_CPE_SS_OUTBOX2_14                        0x0256
+#define WCD9335_CPE_SS_OUTBOX2_15                        0x0257
+#define WCD9335_CPE_SS_OUTBOX1_ACK                       0x0258
+#define WCD9335_CPE_SS_OUTBOX2_ACK                       0x0259
+#define WCD9335_CPE_SS_EC_BUF_INT_PERIOD                 0x025a
+#define WCD9335_CPE_SS_US_BUF_INT_PERIOD                 0x025b
+#define WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD         0x025c
+#define WCD9335_CPE_SS_CFG                               0x025d
+#define WCD9335_CPE_SS_US_EC_MUX_CFG                     0x025e
+#define WCD9335_CPE_SS_MAD_CTL                           0x025f
+#define WCD9335_CPE_SS_CPAR_CTL                          0x0260
+#define WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD              0x0261
+#define WCD9335_CPE_SS_TX_PP_CFG                         0x0262
+#define WCD9335_CPE_SS_DMIC0_CTL                         0x0263
+#define WCD9335_CPE_SS_DMIC1_CTL                         0x0264
+#define WCD9335_CPE_SS_DMIC2_CTL                         0x0265
+#define WCD9335_CPE_SS_DMIC_CFG                          0x0266
+#define WCD9335_CPE_SS_SVA_CFG                           0x0267
+#define WCD9335_CPE_SS_CPAR_CFG                          0x0271
+#define WCD9335_CPE_SS_WDOG_CFG                          0x0272
+#define WCD9335_CPE_SS_BACKUP_INT                        0x0273
+#define WCD9335_CPE_SS_STATUS                            0x0274
+#define WCD9335_CPE_SS_CPE_OCD_CFG                       0x0275
+#define WCD9335_CPE_SS_SS_ERROR_INT_MASK                 0x0276
+#define WCD9335_CPE_SS_SS_ERROR_INT_STATUS               0x0277
+#define WCD9335_CPE_SS_SS_ERROR_INT_CLEAR                0x0278
+#define WCD9335_SOC_MAD_MAIN_CTL_1                       0x0281
+#define WCD9335_SOC_MAD_MAIN_CTL_2                       0x0282
+#define WCD9335_SOC_MAD_AUDIO_CTL_1                      0x0283
+#define WCD9335_SOC_MAD_AUDIO_CTL_2                      0x0284
+#define WCD9335_SOC_MAD_AUDIO_CTL_3                      0x0285
+#define WCD9335_SOC_MAD_AUDIO_CTL_4                      0x0286
+#define WCD9335_SOC_MAD_AUDIO_CTL_5                      0x0287
+#define WCD9335_SOC_MAD_AUDIO_CTL_6                      0x0288
+#define WCD9335_SOC_MAD_AUDIO_CTL_7                      0x0289
+#define WCD9335_SOC_MAD_AUDIO_CTL_8                      0x028a
+#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR                0x028b
+#define WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL                0x028c
+#define WCD9335_SOC_MAD_ULTR_CTL_1                       0x028d
+#define WCD9335_SOC_MAD_ULTR_CTL_2                       0x028e
+#define WCD9335_SOC_MAD_ULTR_CTL_3                       0x028f
+#define WCD9335_SOC_MAD_ULTR_CTL_4                       0x0290
+#define WCD9335_SOC_MAD_ULTR_CTL_5                       0x0291
+#define WCD9335_SOC_MAD_ULTR_CTL_6                       0x0292
+#define WCD9335_SOC_MAD_ULTR_CTL_7                       0x0293
+#define WCD9335_SOC_MAD_BEACON_CTL_1                     0x0294
+#define WCD9335_SOC_MAD_BEACON_CTL_2                     0x0295
+#define WCD9335_SOC_MAD_BEACON_CTL_3                     0x0296
+#define WCD9335_SOC_MAD_BEACON_CTL_4                     0x0297
+#define WCD9335_SOC_MAD_BEACON_CTL_5                     0x0298
+#define WCD9335_SOC_MAD_BEACON_CTL_6                     0x0299
+#define WCD9335_SOC_MAD_BEACON_CTL_7                     0x029a
+#define WCD9335_SOC_MAD_BEACON_CTL_8                     0x029b
+#define WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR               0x029c
+#define WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL               0x029d
+#define WCD9335_SOC_MAD_INP_SEL                          0x029e
+
+/* Page-6 Registers */
+#define WCD9335_PAGE6_PAGE_REGISTER                      0x0600
+#define WCD9335_ANA_BIAS                                 0x0601
+#define WCD9335_ANA_CLK_TOP                              0x0602
+#define WCD9335_ANA_RCO                                  0x0603
+#define WCD9335_ANA_BUCK_VOUT_A                          0x0604
+#define WCD9335_ANA_BUCK_VOUT_D                          0x0605
+#define WCD9335_ANA_BUCK_CTL                             0x0606
+#define WCD9335_ANA_BUCK_STATUS                          0x0607
+#define WCD9335_ANA_RX_SUPPLIES                          0x0608
+#define WCD9335_ANA_HPH                                  0x0609
+#define WCD9335_ANA_EAR                                  0x060a
+#define WCD9335_ANA_LO_1_2                               0x060b
+#define WCD9335_ANA_LO_3_4                               0x060c
+#define WCD9335_ANA_MAD_SETUP                            0x060d
+#define WCD9335_ANA_AMIC1                                0x060e
+#define WCD9335_ANA_AMIC2                                0x060f
+#define WCD9335_ANA_AMIC3                                0x0610
+#define WCD9335_ANA_AMIC4                                0x0611
+#define WCD9335_ANA_AMIC5                                0x0612
+#define WCD9335_ANA_AMIC6                                0x0613
+#define WCD9335_ANA_MBHC_MECH                            0x0614
+#define WCD9335_ANA_MBHC_ELECT                           0x0615
+#define WCD9335_ANA_MBHC_ZDET                            0x0616
+#define WCD9335_ANA_MBHC_RESULT_1                        0x0617
+#define WCD9335_ANA_MBHC_RESULT_2                        0x0618
+#define WCD9335_ANA_MBHC_RESULT_3                        0x0619
+#define WCD9335_ANA_MBHC_BTN0                            0x061a
+#define WCD9335_ANA_MBHC_BTN1                            0x061b
+#define WCD9335_ANA_MBHC_BTN2                            0x061c
+#define WCD9335_ANA_MBHC_BTN3                            0x061d
+#define WCD9335_ANA_MBHC_BTN4                            0x061e
+#define WCD9335_ANA_MBHC_BTN5                            0x061f
+#define WCD9335_ANA_MBHC_BTN6                            0x0620
+#define WCD9335_ANA_MBHC_BTN7                            0x0621
+#define WCD9335_ANA_MICB1                                0x0622
+#define WCD9335_ANA_MICB2                                0x0623
+#define WCD9335_ANA_MICB2_RAMP                           0x0624
+#define WCD9335_ANA_MICB3                                0x0625
+#define WCD9335_ANA_MICB4                                0x0626
+#define WCD9335_ANA_VBADC                                0x0627
+#define WCD9335_BIAS_CTL                                 0x0628
+#define WCD9335_BIAS_VBG_FINE_ADJ                        0x0629
+#define WCD9335_CLOCK_TEST_CTL                           0x062d
+#define WCD9335_RCO_CTRL_1                               0x062e
+#define WCD9335_RCO_CTRL_2                               0x062f
+#define WCD9335_RCO_CAL                                  0x0630
+#define WCD9335_RCO_CAL_1                                0x0631
+#define WCD9335_RCO_CAL_2                                0x0632
+#define WCD9335_RCO_TEST_CTRL                            0x0633
+#define WCD9335_RCO_CAL_OUT_1                            0x0634
+#define WCD9335_RCO_CAL_OUT_2                            0x0635
+#define WCD9335_RCO_CAL_OUT_3                            0x0636
+#define WCD9335_RCO_CAL_OUT_4                            0x0637
+#define WCD9335_RCO_CAL_OUT_5                            0x0638
+#define WCD9335_SIDO_SIDO_MODE_1                         0x063a
+#define WCD9335_SIDO_SIDO_MODE_2                         0x063b
+#define WCD9335_SIDO_SIDO_MODE_3                         0x063c
+#define WCD9335_SIDO_SIDO_MODE_4                         0x063d
+#define WCD9335_SIDO_SIDO_VCL_1                          0x063e
+#define WCD9335_SIDO_SIDO_VCL_2                          0x063f
+#define WCD9335_SIDO_SIDO_VCL_3                          0x0640
+#define WCD9335_SIDO_SIDO_CCL_1                          0x0641
+#define WCD9335_SIDO_SIDO_CCL_2                          0x0642
+#define WCD9335_SIDO_SIDO_CCL_3                          0x0643
+#define WCD9335_SIDO_SIDO_CCL_4                          0x0644
+#define WCD9335_SIDO_SIDO_CCL_5                          0x0645
+#define WCD9335_SIDO_SIDO_CCL_6                          0x0646
+#define WCD9335_SIDO_SIDO_CCL_7                          0x0647
+#define WCD9335_SIDO_SIDO_CCL_8                          0x0648
+#define WCD9335_SIDO_SIDO_CCL_9                          0x0649
+#define WCD9335_SIDO_SIDO_CCL_10                         0x064a
+#define WCD9335_SIDO_SIDO_FILTER_1                       0x064b
+#define WCD9335_SIDO_SIDO_FILTER_2                       0x064c
+#define WCD9335_SIDO_SIDO_DRIVER_1                       0x064d
+#define WCD9335_SIDO_SIDO_DRIVER_2                       0x064e
+#define WCD9335_SIDO_SIDO_DRIVER_3                       0x064f
+#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_1                 0x0650
+#define WCD9335_SIDO_SIDO_CAL_CODE_EXT_2                 0x0651
+#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_1                 0x0652
+#define WCD9335_SIDO_SIDO_CAL_CODE_OUT_2                 0x0653
+#define WCD9335_SIDO_SIDO_TEST_1                         0x0654
+#define WCD9335_SIDO_SIDO_TEST_2                         0x0655
+#define WCD9335_MBHC_CTL_1                               0x0656
+#define WCD9335_MBHC_CTL_2                               0x0657
+#define WCD9335_MBHC_PLUG_DETECT_CTL                     0x0658
+#define WCD9335_MBHC_ZDET_ANA_CTL                        0x0659
+#define WCD9335_MBHC_ZDET_RAMP_CTL                       0x065a
+#define WCD9335_MBHC_FSM_DEBUG                           0x065b /* v1.x */
+#define WCD9335_MBHC_FSM_STATUS                          0x065b /* v2.0 */
+#define WCD9335_MBHC_TEST_CTL                            0x065c
+#define WCD9335_VBADC_SUBBLOCK_EN                        0x065d
+#define WCD9335_VBADC_IBIAS_FE                           0x065e
+#define WCD9335_VBADC_BIAS_ADC                           0x065f
+#define WCD9335_VBADC_FE_CTRL                            0x0660
+#define WCD9335_VBADC_ADC_REF                            0x0661
+#define WCD9335_VBADC_ADC_IO                             0x0662
+#define WCD9335_VBADC_ADC_SAR                            0x0663
+#define WCD9335_VBADC_DEBUG                              0x0664
+#define WCD9335_VBADC_ADC_DOUTMSB                        0x0665
+#define WCD9335_VBADC_ADC_DOUTLSB                        0x0666
+#define WCD9335_LDOH_MODE                                0x0667
+#define WCD9335_LDOH_BIAS                                0x0668
+#define WCD9335_LDOH_STB_LOADS                           0x0669
+#define WCD9335_LDOH_SLOWRAMP                            0x066a
+#define WCD9335_MICB1_TEST_CTL_1                         0x066b
+#define WCD9335_MICB1_TEST_CTL_2                         0x066c
+#define WCD9335_MICB1_TEST_CTL_3                         0x066d
+#define WCD9335_MICB2_TEST_CTL_1                         0x066e
+#define WCD9335_MICB2_TEST_CTL_2                         0x066f
+#define WCD9335_MICB2_TEST_CTL_3                         0x0670
+#define WCD9335_MICB3_TEST_CTL_1                         0x0671
+#define WCD9335_MICB3_TEST_CTL_2                         0x0672
+#define WCD9335_MICB3_TEST_CTL_3                         0x0673
+#define WCD9335_MICB4_TEST_CTL_1                         0x0674
+#define WCD9335_MICB4_TEST_CTL_2                         0x0675
+#define WCD9335_MICB4_TEST_CTL_3                         0x0676
+#define WCD9335_TX_COM_ADC_VCM                           0x0677
+#define WCD9335_TX_COM_BIAS_ATEST                        0x0678
+#define WCD9335_TX_COM_ADC_INT1_IB                       0x0679
+#define WCD9335_TX_COM_ADC_INT2_IB                       0x067a
+#define WCD9335_TX_COM_TXFE_DIV_CTL                      0x067b
+#define WCD9335_TX_COM_TXFE_DIV_START                    0x067c
+#define WCD9335_TX_COM_TXFE_DIV_STOP_9P6M                0x067d
+#define WCD9335_TX_COM_TXFE_DIV_STOP_12P288M             0x067e
+#define WCD9335_TX_1_2_TEST_EN                           0x067f
+#define WCD9335_TX_1_2_ADC_IB                            0x0680
+#define WCD9335_TX_1_2_ATEST_REFCTL                      0x0681
+#define WCD9335_TX_1_2_TEST_CTL                          0x0682
+#define WCD9335_TX_1_2_TEST_BLK_EN                       0x0683
+#define WCD9335_TX_1_2_TXFE_CLKDIV                       0x0684
+#define WCD9335_TX_1_2_SAR1_ERR                          0x0685
+#define WCD9335_TX_1_2_SAR2_ERR                          0x0686
+#define WCD9335_TX_3_4_TEST_EN                           0x0687
+#define WCD9335_TX_3_4_ADC_IB                            0x0688
+#define WCD9335_TX_3_4_ATEST_REFCTL                      0x0689
+#define WCD9335_TX_3_4_TEST_CTL                          0x068a
+#define WCD9335_TX_3_4_TEST_BLK_EN                       0x068b
+#define WCD9335_TX_3_4_TXFE_CLKDIV                       0x068c
+#define WCD9335_TX_3_4_SAR1_ERR                          0x068d
+#define WCD9335_TX_3_4_SAR2_ERR                          0x068e
+#define WCD9335_TX_5_6_TEST_EN                           0x068f
+#define WCD9335_TX_5_6_ADC_IB                            0x0690
+#define WCD9335_TX_5_6_ATEST_REFCTL                      0x0691
+#define WCD9335_TX_5_6_TEST_CTL                          0x0692
+#define WCD9335_TX_5_6_TEST_BLK_EN                       0x0693
+#define WCD9335_TX_5_6_TXFE_CLKDIV                       0x0694
+#define WCD9335_TX_5_6_SAR1_ERR                          0x0695
+#define WCD9335_TX_5_6_SAR2_ERR                          0x0696
+#define WCD9335_CLASSH_MODE_1                            0x0697
+#define WCD9335_CLASSH_MODE_2                            0x0698
+#define WCD9335_CLASSH_MODE_3                            0x0699
+#define WCD9335_CLASSH_CTRL_VCL_1                        0x069a
+#define WCD9335_CLASSH_CTRL_VCL_2                        0x069b
+#define WCD9335_CLASSH_CTRL_CCL_1                        0x069c
+#define WCD9335_CLASSH_CTRL_CCL_2                        0x069d
+#define WCD9335_CLASSH_CTRL_CCL_3                        0x069e
+#define WCD9335_CLASSH_CTRL_CCL_4                        0x069f
+#define WCD9335_CLASSH_CTRL_CCL_5                        0x06a0
+#define WCD9335_CLASSH_BUCK_TMUX_A_D                     0x06a1
+#define WCD9335_CLASSH_BUCK_SW_DRV_CNTL                  0x06a2
+#define WCD9335_CLASSH_SPARE                             0x06a3
+#define WCD9335_FLYBACK_EN                               0x06a4
+#define WCD9335_FLYBACK_VNEG_CTRL_1                      0x06a5
+#define WCD9335_FLYBACK_VNEG_CTRL_2                      0x06a6
+#define WCD9335_FLYBACK_VNEG_CTRL_3                      0x06a7
+#define WCD9335_FLYBACK_VNEG_CTRL_4                      0x06a8
+#define WCD9335_FLYBACK_VNEG_CTRL_5                      0x06a9
+#define WCD9335_FLYBACK_VNEG_CTRL_6                      0x06aa
+#define WCD9335_FLYBACK_VNEG_CTRL_7                      0x06ab
+#define WCD9335_FLYBACK_VNEG_CTRL_8                      0x06ac
+#define WCD9335_FLYBACK_VNEG_CTRL_9                      0x06ad
+#define WCD9335_FLYBACK_VNEG_DAC_CTRL_1                  0x06ae
+#define WCD9335_FLYBACK_VNEG_DAC_CTRL_2                  0x06af
+#define WCD9335_FLYBACK_VNEG_DAC_CTRL_3                  0x06b0
+#define WCD9335_FLYBACK_VNEG_DAC_CTRL_4                  0x06b1 /* v1.x */
+#define WCD9335_FLYBACK_CTRL_1                           0x06b1 /* v2.0 */
+#define WCD9335_FLYBACK_TEST_CTL                         0x06b2
+#define WCD9335_RX_AUX_SW_CTL                            0x06b3
+#define WCD9335_RX_PA_AUX_IN_CONN                        0x06b4
+#define WCD9335_RX_TIMER_DIV                             0x06b5
+#define WCD9335_RX_OCP_CTL                               0x06b6
+#define WCD9335_RX_OCP_COUNT                             0x06b7
+#define WCD9335_RX_BIAS_EAR_DAC                          0x06b8
+#define WCD9335_RX_BIAS_EAR_AMP                          0x06b9
+#define WCD9335_RX_BIAS_HPH_LDO                          0x06ba
+#define WCD9335_RX_BIAS_HPH_PA                           0x06bb
+#define WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2                0x06bc
+#define WCD9335_RX_BIAS_HPH_RDAC_LDO                     0x06bd
+#define WCD9335_RX_BIAS_HPH_CNP1                         0x06be
+#define WCD9335_RX_BIAS_HPH_LOWPOWER                     0x06bf
+#define WCD9335_RX_BIAS_DIFFLO_PA                        0x06c0
+#define WCD9335_RX_BIAS_DIFFLO_REF                       0x06c1
+#define WCD9335_RX_BIAS_DIFFLO_LDO                       0x06c2
+#define WCD9335_RX_BIAS_SELO_DAC_PA                      0x06c3
+#define WCD9335_RX_BIAS_BUCK_RST                         0x06c4
+#define WCD9335_RX_BIAS_BUCK_VREF_ERRAMP                 0x06c5
+#define WCD9335_RX_BIAS_FLYB_ERRAMP                      0x06c6
+#define WCD9335_RX_BIAS_FLYB_BUFF                        0x06c7
+#define WCD9335_RX_BIAS_FLYB_MID_RST                     0x06c8
+#define WCD9335_HPH_L_STATUS                             0x06c9
+#define WCD9335_HPH_R_STATUS                             0x06ca
+#define WCD9335_HPH_CNP_EN                               0x06cb
+#define WCD9335_HPH_CNP_WG_CTL                           0x06cc
+#define WCD9335_HPH_CNP_WG_TIME                          0x06cd
+#define WCD9335_HPH_OCP_CTL                              0x06ce
+#define WCD9335_HPH_AUTO_CHOP                            0x06cf
+#define WCD9335_HPH_CHOP_CTL                             0x06d0
+#define WCD9335_HPH_PA_CTL1                              0x06d1
+#define WCD9335_HPH_PA_CTL2                              0x06d2
+#define WCD9335_HPH_L_EN                                 0x06d3
+#define WCD9335_HPH_L_TEST                               0x06d4
+#define WCD9335_HPH_L_ATEST                              0x06d5
+#define WCD9335_HPH_R_EN                                 0x06d6
+#define WCD9335_HPH_R_TEST                               0x06d7
+#define WCD9335_HPH_R_ATEST                              0x06d8
+#define WCD9335_HPH_RDAC_CLK_CTL1                        0x06d9
+#define WCD9335_HPH_RDAC_CLK_CTL2                        0x06da
+#define WCD9335_HPH_RDAC_LDO_CTL                         0x06db
+#define WCD9335_HPH_RDAC_CHOP_CLK_LP_CTL                 0x06dc
+#define WCD9335_HPH_REFBUFF_UHQA_CTL                     0x06dd
+#define WCD9335_HPH_REFBUFF_LP_CTL                       0x06de
+#define WCD9335_HPH_L_DAC_CTL                            0x06df
+#define WCD9335_HPH_R_DAC_CTL                            0x06e0
+#define WCD9335_EAR_EN_REG                               0x06e1
+#define WCD9335_EAR_CMBUFF                               0x06e2
+#define WCD9335_EAR_ICTL                                 0x06e3
+#define WCD9335_EAR_EN_DBG_CTL                           0x06e4
+#define WCD9335_EAR_CNP                                  0x06e5
+#define WCD9335_EAR_DAC_CTL_ATEST                        0x06e6
+#define WCD9335_EAR_STATUS_REG                           0x06e7
+#define WCD9335_EAR_OUT_SHORT                            0x06e8
+#define WCD9335_DIFF_LO_MISC                             0x06e9
+#define WCD9335_DIFF_LO_LO2_COMPANDER                    0x06ea
+#define WCD9335_DIFF_LO_LO1_COMPANDER                    0x06eb
+#define WCD9335_DIFF_LO_COMMON                           0x06ec
+#define WCD9335_DIFF_LO_BYPASS_EN                        0x06ed
+#define WCD9335_DIFF_LO_CNP                              0x06ee
+#define WCD9335_DIFF_LO_CORE_OUT_PROG                    0x06ef
+#define WCD9335_DIFF_LO_LDO_OUT_PROG                     0x06f0
+#define WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ            0x06f1
+#define WCD9335_DIFF_LO_COM_PA_FREQ                      0x06f2
+#define WCD9335_DIFF_LO_RESERVED_REG                     0x06f3
+#define WCD9335_DIFF_LO_LO1_STATUS_1                     0x06f4
+#define WCD9335_DIFF_LO_LO1_STATUS_2                     0x06f5
+#define WCD9335_SE_LO_COM1                               0x06f6
+#define WCD9335_SE_LO_COM2                               0x06f7
+#define WCD9335_SE_LO_LO3_GAIN                           0x06f8
+#define WCD9335_SE_LO_LO3_CTRL                           0x06f9
+#define WCD9335_SE_LO_LO4_GAIN                           0x06fa
+#define WCD9335_SE_LO_LO4_CTRL                           0x06fb
+#define WCD9335_SE_LO_LO3_STATUS                         0x06fe
+#define WCD9335_SE_LO_LO4_STATUS                         0x06ff
+
+/* Page-10 Registers */
+#define WCD9335_PAGE10_PAGE_REGISTER                     0x0a00
+#define WCD9335_CDC_ANC0_CLK_RESET_CTL                   0x0a01
+#define WCD9335_CDC_ANC0_MODE_1_CTL                      0x0a02
+#define WCD9335_CDC_ANC0_MODE_2_CTL                      0x0a03
+#define WCD9335_CDC_ANC0_FF_SHIFT                        0x0a04
+#define WCD9335_CDC_ANC0_FB_SHIFT                        0x0a05
+#define WCD9335_CDC_ANC0_LPF_FF_A_CTL                    0x0a06
+#define WCD9335_CDC_ANC0_LPF_FF_B_CTL                    0x0a07
+#define WCD9335_CDC_ANC0_LPF_FB_CTL                      0x0a08
+#define WCD9335_CDC_ANC0_SMLPF_CTL                       0x0a09
+#define WCD9335_CDC_ANC0_DCFLT_SHIFT_CTL                 0x0a0a
+#define WCD9335_CDC_ANC0_IIR_ADAPT_CTL                   0x0a0b
+#define WCD9335_CDC_ANC0_IIR_COEFF_1_CTL                 0x0a0c
+#define WCD9335_CDC_ANC0_IIR_COEFF_2_CTL                 0x0a0d
+#define WCD9335_CDC_ANC0_FF_A_GAIN_CTL                   0x0a0e
+#define WCD9335_CDC_ANC0_FF_B_GAIN_CTL                   0x0a0f
+#define WCD9335_CDC_ANC0_FB_GAIN_CTL                     0x0a10
+#define WCD9335_CDC_ANC1_CLK_RESET_CTL                   0x0a19
+#define WCD9335_CDC_ANC1_MODE_1_CTL                      0x0a1a
+#define WCD9335_CDC_ANC1_MODE_2_CTL                      0x0a1b
+#define WCD9335_CDC_ANC1_FF_SHIFT                        0x0a1c
+#define WCD9335_CDC_ANC1_FB_SHIFT                        0x0a1d
+#define WCD9335_CDC_ANC1_LPF_FF_A_CTL                    0x0a1e
+#define WCD9335_CDC_ANC1_LPF_FF_B_CTL                    0x0a1f
+#define WCD9335_CDC_ANC1_LPF_FB_CTL                      0x0a20
+#define WCD9335_CDC_ANC1_SMLPF_CTL                       0x0a21
+#define WCD9335_CDC_ANC1_DCFLT_SHIFT_CTL                 0x0a22
+#define WCD9335_CDC_ANC1_IIR_ADAPT_CTL                   0x0a23
+#define WCD9335_CDC_ANC1_IIR_COEFF_1_CTL                 0x0a24
+#define WCD9335_CDC_ANC1_IIR_COEFF_2_CTL                 0x0a25
+#define WCD9335_CDC_ANC1_FF_A_GAIN_CTL                   0x0a26
+#define WCD9335_CDC_ANC1_FF_B_GAIN_CTL                   0x0a27
+#define WCD9335_CDC_ANC1_FB_GAIN_CTL                     0x0a28
+#define WCD9335_CDC_TX0_TX_PATH_CTL                      0x0a31
+#define WCD9335_CDC_TX0_TX_PATH_CFG0                     0x0a32
+#define WCD9335_CDC_TX0_TX_PATH_CFG1                     0x0a33
+#define WCD9335_CDC_TX0_TX_VOL_CTL                       0x0a34
+#define WCD9335_CDC_TX0_TX_PATH_192_CTL                  0x0a35
+#define WCD9335_CDC_TX0_TX_PATH_192_CFG                  0x0a36
+#define WCD9335_CDC_TX0_TX_PATH_SEC0                     0x0a37
+#define WCD9335_CDC_TX0_TX_PATH_SEC1                     0x0a38
+#define WCD9335_CDC_TX0_TX_PATH_SEC2                     0x0a39
+#define WCD9335_CDC_TX0_TX_PATH_SEC3                     0x0a3a
+#define WCD9335_CDC_TX0_TX_PATH_SEC4                     0x0a3b
+#define WCD9335_CDC_TX0_TX_PATH_SEC5                     0x0a3c
+#define WCD9335_CDC_TX0_TX_PATH_SEC6                     0x0a3d
+#define WCD9335_CDC_TX0_TX_PATH_SEC7                     0x0a3e
+#define WCD9335_CDC_TX1_TX_PATH_CTL                      0x0a41
+#define WCD9335_CDC_TX1_TX_PATH_CFG0                     0x0a42
+#define WCD9335_CDC_TX1_TX_PATH_CFG1                     0x0a43
+#define WCD9335_CDC_TX1_TX_VOL_CTL                       0x0a44
+#define WCD9335_CDC_TX1_TX_PATH_192_CTL                  0x0a45
+#define WCD9335_CDC_TX1_TX_PATH_192_CFG                  0x0a46
+#define WCD9335_CDC_TX1_TX_PATH_SEC0                     0x0a47
+#define WCD9335_CDC_TX1_TX_PATH_SEC1                     0x0a48
+#define WCD9335_CDC_TX1_TX_PATH_SEC2                     0x0a49
+#define WCD9335_CDC_TX1_TX_PATH_SEC3                     0x0a4a
+#define WCD9335_CDC_TX1_TX_PATH_SEC4                     0x0a4b
+#define WCD9335_CDC_TX1_TX_PATH_SEC5                     0x0a4c
+#define WCD9335_CDC_TX1_TX_PATH_SEC6                     0x0a4d
+#define WCD9335_CDC_TX2_TX_PATH_CTL                      0x0a51
+#define WCD9335_CDC_TX2_TX_PATH_CFG0                     0x0a52
+#define WCD9335_CDC_TX2_TX_PATH_CFG1                     0x0a53
+#define WCD9335_CDC_TX2_TX_VOL_CTL                       0x0a54
+#define WCD9335_CDC_TX2_TX_PATH_192_CTL                  0x0a55
+#define WCD9335_CDC_TX2_TX_PATH_192_CFG                  0x0a56
+#define WCD9335_CDC_TX2_TX_PATH_SEC0                     0x0a57
+#define WCD9335_CDC_TX2_TX_PATH_SEC1                     0x0a58
+#define WCD9335_CDC_TX2_TX_PATH_SEC2                     0x0a59
+#define WCD9335_CDC_TX2_TX_PATH_SEC3                     0x0a5a
+#define WCD9335_CDC_TX2_TX_PATH_SEC4                     0x0a5b
+#define WCD9335_CDC_TX2_TX_PATH_SEC5                     0x0a5c
+#define WCD9335_CDC_TX2_TX_PATH_SEC6                     0x0a5d
+#define WCD9335_CDC_TX3_TX_PATH_CTL                      0x0a61
+#define WCD9335_CDC_TX3_TX_PATH_CFG0                     0x0a62
+#define WCD9335_CDC_TX3_TX_PATH_CFG1                     0x0a63
+#define WCD9335_CDC_TX3_TX_VOL_CTL                       0x0a64
+#define WCD9335_CDC_TX3_TX_PATH_192_CTL                  0x0a65
+#define WCD9335_CDC_TX3_TX_PATH_192_CFG                  0x0a66
+#define WCD9335_CDC_TX3_TX_PATH_SEC0                     0x0a67
+#define WCD9335_CDC_TX3_TX_PATH_SEC1                     0x0a68
+#define WCD9335_CDC_TX3_TX_PATH_SEC2                     0x0a69
+#define WCD9335_CDC_TX3_TX_PATH_SEC3                     0x0a6a
+#define WCD9335_CDC_TX3_TX_PATH_SEC4                     0x0a6b
+#define WCD9335_CDC_TX3_TX_PATH_SEC5                     0x0a6c
+#define WCD9335_CDC_TX3_TX_PATH_SEC6                     0x0a6d
+#define WCD9335_CDC_TX4_TX_PATH_CTL                      0x0a71
+#define WCD9335_CDC_TX4_TX_PATH_CFG0                     0x0a72
+#define WCD9335_CDC_TX4_TX_PATH_CFG1                     0x0a73
+#define WCD9335_CDC_TX4_TX_VOL_CTL                       0x0a74
+#define WCD9335_CDC_TX4_TX_PATH_192_CTL                  0x0a75
+#define WCD9335_CDC_TX4_TX_PATH_192_CFG                  0x0a76
+#define WCD9335_CDC_TX4_TX_PATH_SEC0                     0x0a77
+#define WCD9335_CDC_TX4_TX_PATH_SEC1                     0x0a78
+#define WCD9335_CDC_TX4_TX_PATH_SEC2                     0x0a79
+#define WCD9335_CDC_TX4_TX_PATH_SEC3                     0x0a7a
+#define WCD9335_CDC_TX4_TX_PATH_SEC4                     0x0a7b
+#define WCD9335_CDC_TX4_TX_PATH_SEC5                     0x0a7c
+#define WCD9335_CDC_TX4_TX_PATH_SEC6                     0x0a7d
+#define WCD9335_CDC_TX5_TX_PATH_CTL                      0x0a81
+#define WCD9335_CDC_TX5_TX_PATH_CFG0                     0x0a82
+#define WCD9335_CDC_TX5_TX_PATH_CFG1                     0x0a83
+#define WCD9335_CDC_TX5_TX_VOL_CTL                       0x0a84
+#define WCD9335_CDC_TX5_TX_PATH_192_CTL                  0x0a85
+#define WCD9335_CDC_TX5_TX_PATH_192_CFG                  0x0a86
+#define WCD9335_CDC_TX5_TX_PATH_SEC0                     0x0a87
+#define WCD9335_CDC_TX5_TX_PATH_SEC1                     0x0a88
+#define WCD9335_CDC_TX5_TX_PATH_SEC2                     0x0a89
+#define WCD9335_CDC_TX5_TX_PATH_SEC3                     0x0a8a
+#define WCD9335_CDC_TX5_TX_PATH_SEC4                     0x0a8b
+#define WCD9335_CDC_TX5_TX_PATH_SEC5                     0x0a8c
+#define WCD9335_CDC_TX5_TX_PATH_SEC6                     0x0a8d
+#define WCD9335_CDC_TX6_TX_PATH_CTL                      0x0a91
+#define WCD9335_CDC_TX6_TX_PATH_CFG0                     0x0a92
+#define WCD9335_CDC_TX6_TX_PATH_CFG1                     0x0a93
+#define WCD9335_CDC_TX6_TX_VOL_CTL                       0x0a94
+#define WCD9335_CDC_TX6_TX_PATH_192_CTL                  0x0a95
+#define WCD9335_CDC_TX6_TX_PATH_192_CFG                  0x0a96
+#define WCD9335_CDC_TX6_TX_PATH_SEC0                     0x0a97
+#define WCD9335_CDC_TX6_TX_PATH_SEC1                     0x0a98
+#define WCD9335_CDC_TX6_TX_PATH_SEC2                     0x0a99
+#define WCD9335_CDC_TX6_TX_PATH_SEC3                     0x0a9a
+#define WCD9335_CDC_TX6_TX_PATH_SEC4                     0x0a9b
+#define WCD9335_CDC_TX6_TX_PATH_SEC5                     0x0a9c
+#define WCD9335_CDC_TX6_TX_PATH_SEC6                     0x0a9d
+#define WCD9335_CDC_TX7_TX_PATH_CTL                      0x0aa1
+#define WCD9335_CDC_TX7_TX_PATH_CFG0                     0x0aa2
+#define WCD9335_CDC_TX7_TX_PATH_CFG1                     0x0aa3
+#define WCD9335_CDC_TX7_TX_VOL_CTL                       0x0aa4
+#define WCD9335_CDC_TX7_TX_PATH_192_CTL                  0x0aa5
+#define WCD9335_CDC_TX7_TX_PATH_192_CFG                  0x0aa6
+#define WCD9335_CDC_TX7_TX_PATH_SEC0                     0x0aa7
+#define WCD9335_CDC_TX7_TX_PATH_SEC1                     0x0aa8
+#define WCD9335_CDC_TX7_TX_PATH_SEC2                     0x0aa9
+#define WCD9335_CDC_TX7_TX_PATH_SEC3                     0x0aaa
+#define WCD9335_CDC_TX7_TX_PATH_SEC4                     0x0aab
+#define WCD9335_CDC_TX7_TX_PATH_SEC5                     0x0aac
+#define WCD9335_CDC_TX7_TX_PATH_SEC6                     0x0aad
+#define WCD9335_CDC_TX8_TX_PATH_CTL                      0x0ab1
+#define WCD9335_CDC_TX8_TX_PATH_CFG0                     0x0ab2
+#define WCD9335_CDC_TX8_TX_PATH_CFG1                     0x0ab3
+#define WCD9335_CDC_TX8_TX_VOL_CTL                       0x0ab4
+#define WCD9335_CDC_TX8_TX_PATH_192_CTL                  0x0ab5
+#define WCD9335_CDC_TX8_TX_PATH_192_CFG                  0x0ab6
+#define WCD9335_CDC_TX8_TX_PATH_SEC0                     0x0ab7
+#define WCD9335_CDC_TX8_TX_PATH_SEC1                     0x0ab8
+#define WCD9335_CDC_TX8_TX_PATH_SEC2                     0x0ab9
+#define WCD9335_CDC_TX8_TX_PATH_SEC3                     0x0aba
+#define WCD9335_CDC_TX8_TX_PATH_SEC4                     0x0abb
+#define WCD9335_CDC_TX8_TX_PATH_SEC5                     0x0abc
+#define WCD9335_CDC_TX8_TX_PATH_SEC6                     0x0abd
+#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL               0x0ac2
+#define WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0              0x0ac3
+#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL              0x0ac6
+#define WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0             0x0ac7
+#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL              0x0aca
+#define WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0             0x0acb
+#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL              0x0ace
+#define WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0             0x0acf
+
+/* Page-11 Registers */
+#define WCD9335_PAGE11_PAGE_REGISTER                     0x0b00
+#define WCD9335_CDC_COMPANDER1_CTL0                      0x0b01
+#define WCD9335_CDC_COMPANDER1_CTL1                      0x0b02
+#define WCD9335_CDC_COMPANDER1_CTL2                      0x0b03
+#define WCD9335_CDC_COMPANDER1_CTL3                      0x0b04
+#define WCD9335_CDC_COMPANDER1_CTL4                      0x0b05
+#define WCD9335_CDC_COMPANDER1_CTL5                      0x0b06
+#define WCD9335_CDC_COMPANDER1_CTL6                      0x0b07
+#define WCD9335_CDC_COMPANDER1_CTL7                      0x0b08
+#define WCD9335_CDC_COMPANDER2_CTL0                      0x0b09
+#define WCD9335_CDC_COMPANDER2_CTL1                      0x0b0a
+#define WCD9335_CDC_COMPANDER2_CTL2                      0x0b0b
+#define WCD9335_CDC_COMPANDER2_CTL3                      0x0b0c
+#define WCD9335_CDC_COMPANDER2_CTL4                      0x0b0d
+#define WCD9335_CDC_COMPANDER2_CTL5                      0x0b0e
+#define WCD9335_CDC_COMPANDER2_CTL6                      0x0b0f
+#define WCD9335_CDC_COMPANDER2_CTL7                      0x0b10
+#define WCD9335_CDC_COMPANDER3_CTL0                      0x0b11
+#define WCD9335_CDC_COMPANDER3_CTL1                      0x0b12
+#define WCD9335_CDC_COMPANDER3_CTL2                      0x0b13
+#define WCD9335_CDC_COMPANDER3_CTL3                      0x0b14
+#define WCD9335_CDC_COMPANDER3_CTL4                      0x0b15
+#define WCD9335_CDC_COMPANDER3_CTL5                      0x0b16
+#define WCD9335_CDC_COMPANDER3_CTL6                      0x0b17
+#define WCD9335_CDC_COMPANDER3_CTL7                      0x0b18
+#define WCD9335_CDC_COMPANDER4_CTL0                      0x0b19
+#define WCD9335_CDC_COMPANDER4_CTL1                      0x0b1a
+#define WCD9335_CDC_COMPANDER4_CTL2                      0x0b1b
+#define WCD9335_CDC_COMPANDER4_CTL3                      0x0b1c
+#define WCD9335_CDC_COMPANDER4_CTL4                      0x0b1d
+#define WCD9335_CDC_COMPANDER4_CTL5                      0x0b1e
+#define WCD9335_CDC_COMPANDER4_CTL6                      0x0b1f
+#define WCD9335_CDC_COMPANDER4_CTL7                      0x0b20
+#define WCD9335_CDC_COMPANDER5_CTL0                      0x0b21
+#define WCD9335_CDC_COMPANDER5_CTL1                      0x0b22
+#define WCD9335_CDC_COMPANDER5_CTL2                      0x0b23
+#define WCD9335_CDC_COMPANDER5_CTL3                      0x0b24
+#define WCD9335_CDC_COMPANDER5_CTL4                      0x0b25
+#define WCD9335_CDC_COMPANDER5_CTL5                      0x0b26
+#define WCD9335_CDC_COMPANDER5_CTL6                      0x0b27
+#define WCD9335_CDC_COMPANDER5_CTL7                      0x0b28
+#define WCD9335_CDC_COMPANDER6_CTL0                      0x0b29
+#define WCD9335_CDC_COMPANDER6_CTL1                      0x0b2a
+#define WCD9335_CDC_COMPANDER6_CTL2                      0x0b2b
+#define WCD9335_CDC_COMPANDER6_CTL3                      0x0b2c
+#define WCD9335_CDC_COMPANDER6_CTL4                      0x0b2d
+#define WCD9335_CDC_COMPANDER6_CTL5                      0x0b2e
+#define WCD9335_CDC_COMPANDER6_CTL6                      0x0b2f
+#define WCD9335_CDC_COMPANDER6_CTL7                      0x0b30
+#define WCD9335_CDC_COMPANDER7_CTL0                      0x0b31
+#define WCD9335_CDC_COMPANDER7_CTL1                      0x0b32
+#define WCD9335_CDC_COMPANDER7_CTL2                      0x0b33
+#define WCD9335_CDC_COMPANDER7_CTL3                      0x0b34
+#define WCD9335_CDC_COMPANDER7_CTL4                      0x0b35
+#define WCD9335_CDC_COMPANDER7_CTL5                      0x0b36
+#define WCD9335_CDC_COMPANDER7_CTL6                      0x0b37
+#define WCD9335_CDC_COMPANDER7_CTL7                      0x0b38
+#define WCD9335_CDC_COMPANDER8_CTL0                      0x0b39
+#define WCD9335_CDC_COMPANDER8_CTL1                      0x0b3a
+#define WCD9335_CDC_COMPANDER8_CTL2                      0x0b3b
+#define WCD9335_CDC_COMPANDER8_CTL3                      0x0b3c
+#define WCD9335_CDC_COMPANDER8_CTL4                      0x0b3d
+#define WCD9335_CDC_COMPANDER8_CTL5                      0x0b3e
+#define WCD9335_CDC_COMPANDER8_CTL6                      0x0b3f
+#define WCD9335_CDC_COMPANDER8_CTL7                      0x0b40
+#define WCD9335_CDC_RX0_RX_PATH_CTL                      0x0b41
+#define WCD9335_CDC_RX0_RX_PATH_CFG0                     0x0b42
+#define WCD9335_CDC_RX0_RX_PATH_CFG1                     0x0b43
+#define WCD9335_CDC_RX0_RX_PATH_CFG2                     0x0b44
+#define WCD9335_CDC_RX0_RX_VOL_CTL                       0x0b45
+#define WCD9335_CDC_RX0_RX_PATH_MIX_CTL                  0x0b46
+#define WCD9335_CDC_RX0_RX_PATH_MIX_CFG                  0x0b47
+#define WCD9335_CDC_RX0_RX_VOL_MIX_CTL                   0x0b48
+#define WCD9335_CDC_RX0_RX_PATH_SEC0                     0x0b49
+#define WCD9335_CDC_RX0_RX_PATH_SEC1                     0x0b4a
+#define WCD9335_CDC_RX0_RX_PATH_SEC2                     0x0b4b
+#define WCD9335_CDC_RX0_RX_PATH_SEC3                     0x0b4c
+#define WCD9335_CDC_RX0_RX_PATH_SEC5                     0x0b4e
+#define WCD9335_CDC_RX0_RX_PATH_SEC6                     0x0b4f
+#define WCD9335_CDC_RX0_RX_PATH_SEC7                     0x0b50
+#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC0                 0x0b51
+#define WCD9335_CDC_RX0_RX_PATH_MIX_SEC1                 0x0b52
+#define WCD9335_CDC_RX1_RX_PATH_CTL                      0x0b55
+#define WCD9335_CDC_RX1_RX_PATH_CFG0                     0x0b56
+#define WCD9335_CDC_RX1_RX_PATH_CFG1                     0x0b57
+#define WCD9335_CDC_RX1_RX_PATH_CFG2                     0x0b58
+#define WCD9335_CDC_RX1_RX_VOL_CTL                       0x0b59
+#define WCD9335_CDC_RX1_RX_PATH_MIX_CTL                  0x0b5a
+#define WCD9335_CDC_RX1_RX_PATH_MIX_CFG                  0x0b5b
+#define WCD9335_CDC_RX1_RX_VOL_MIX_CTL                   0x0b5c
+#define WCD9335_CDC_RX1_RX_PATH_SEC0                     0x0b5d
+#define WCD9335_CDC_RX1_RX_PATH_SEC1                     0x0b5e
+#define WCD9335_CDC_RX1_RX_PATH_SEC2                     0x0b5f
+#define WCD9335_CDC_RX1_RX_PATH_SEC3                     0x0b60
+#define WCD9335_CDC_RX1_RX_PATH_SEC4                     0x0b61
+#define WCD9335_CDC_RX1_RX_PATH_SEC5                     0x0b62
+#define WCD9335_CDC_RX1_RX_PATH_SEC6                     0x0b63
+#define WCD9335_CDC_RX1_RX_PATH_SEC7                     0x0b64
+#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC0                 0x0b65
+#define WCD9335_CDC_RX1_RX_PATH_MIX_SEC1                 0x0b66
+#define WCD9335_CDC_RX2_RX_PATH_CTL                      0x0b69
+#define WCD9335_CDC_RX2_RX_PATH_CFG0                     0x0b6a
+#define WCD9335_CDC_RX2_RX_PATH_CFG1                     0x0b6b
+#define WCD9335_CDC_RX2_RX_PATH_CFG2                     0x0b6c
+#define WCD9335_CDC_RX2_RX_VOL_CTL                       0x0b6d
+#define WCD9335_CDC_RX2_RX_PATH_MIX_CTL                  0x0b6e
+#define WCD9335_CDC_RX2_RX_PATH_MIX_CFG                  0x0b6f
+#define WCD9335_CDC_RX2_RX_VOL_MIX_CTL                   0x0b70
+#define WCD9335_CDC_RX2_RX_PATH_SEC0                     0x0b71
+#define WCD9335_CDC_RX2_RX_PATH_SEC1                     0x0b72
+#define WCD9335_CDC_RX2_RX_PATH_SEC2                     0x0b73
+#define WCD9335_CDC_RX2_RX_PATH_SEC3                     0x0b74
+#define WCD9335_CDC_RX2_RX_PATH_SEC4                     0x0b75
+#define WCD9335_CDC_RX2_RX_PATH_SEC5                     0x0b76
+#define WCD9335_CDC_RX2_RX_PATH_SEC6                     0x0b77
+#define WCD9335_CDC_RX2_RX_PATH_SEC7                     0x0b78
+#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC0                 0x0b79
+#define WCD9335_CDC_RX2_RX_PATH_MIX_SEC1                 0x0b7a
+#define WCD9335_CDC_RX3_RX_PATH_CTL                      0x0b7d
+#define WCD9335_CDC_RX3_RX_PATH_CFG0                     0x0b7e
+#define WCD9335_CDC_RX3_RX_PATH_CFG1                     0x0b7f
+#define WCD9335_CDC_RX3_RX_PATH_CFG2                     0x0b80
+#define WCD9335_CDC_RX3_RX_VOL_CTL                       0x0b81
+#define WCD9335_CDC_RX3_RX_PATH_MIX_CTL                  0x0b82
+#define WCD9335_CDC_RX3_RX_PATH_MIX_CFG                  0x0b83
+#define WCD9335_CDC_RX3_RX_VOL_MIX_CTL                   0x0b84
+#define WCD9335_CDC_RX3_RX_PATH_SEC0                     0x0b85
+#define WCD9335_CDC_RX3_RX_PATH_SEC1                     0x0b86
+#define WCD9335_CDC_RX3_RX_PATH_SEC2                     0x0b87
+#define WCD9335_CDC_RX3_RX_PATH_SEC3                     0x0b88
+#define WCD9335_CDC_RX3_RX_PATH_SEC5                     0x0b8a
+#define WCD9335_CDC_RX3_RX_PATH_SEC6                     0x0b8b
+#define WCD9335_CDC_RX3_RX_PATH_SEC7                     0x0b8c
+#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC0                 0x0b8d
+#define WCD9335_CDC_RX3_RX_PATH_MIX_SEC1                 0x0b8e
+#define WCD9335_CDC_RX4_RX_PATH_CTL                      0x0b91
+#define WCD9335_CDC_RX4_RX_PATH_CFG0                     0x0b92
+#define WCD9335_CDC_RX4_RX_PATH_CFG1                     0x0b93
+#define WCD9335_CDC_RX4_RX_PATH_CFG2                     0x0b94
+#define WCD9335_CDC_RX4_RX_VOL_CTL                       0x0b95
+#define WCD9335_CDC_RX4_RX_PATH_MIX_CTL                  0x0b96
+#define WCD9335_CDC_RX4_RX_PATH_MIX_CFG                  0x0b97
+#define WCD9335_CDC_RX4_RX_VOL_MIX_CTL                   0x0b98
+#define WCD9335_CDC_RX4_RX_PATH_SEC0                     0x0b99
+#define WCD9335_CDC_RX4_RX_PATH_SEC1                     0x0b9a
+#define WCD9335_CDC_RX4_RX_PATH_SEC2                     0x0b9b
+#define WCD9335_CDC_RX4_RX_PATH_SEC3                     0x0b9c
+#define WCD9335_CDC_RX4_RX_PATH_SEC5                     0x0b9e
+#define WCD9335_CDC_RX4_RX_PATH_SEC6                     0x0b9f
+#define WCD9335_CDC_RX4_RX_PATH_SEC7                     0x0ba0
+#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC0                 0x0ba1
+#define WCD9335_CDC_RX4_RX_PATH_MIX_SEC1                 0x0ba2
+#define WCD9335_CDC_RX5_RX_PATH_CTL                      0x0ba5
+#define WCD9335_CDC_RX5_RX_PATH_CFG0                     0x0ba6
+#define WCD9335_CDC_RX5_RX_PATH_CFG1                     0x0ba7
+#define WCD9335_CDC_RX5_RX_PATH_CFG2                     0x0ba8
+#define WCD9335_CDC_RX5_RX_VOL_CTL                       0x0ba9
+#define WCD9335_CDC_RX5_RX_PATH_MIX_CTL                  0x0baa
+#define WCD9335_CDC_RX5_RX_PATH_MIX_CFG                  0x0bab
+#define WCD9335_CDC_RX5_RX_VOL_MIX_CTL                   0x0bac
+#define WCD9335_CDC_RX5_RX_PATH_SEC0                     0x0bad
+#define WCD9335_CDC_RX5_RX_PATH_SEC1                     0x0bae
+#define WCD9335_CDC_RX5_RX_PATH_SEC2                     0x0baf
+#define WCD9335_CDC_RX5_RX_PATH_SEC3                     0x0bb0
+#define WCD9335_CDC_RX5_RX_PATH_SEC5                     0x0bb2
+#define WCD9335_CDC_RX5_RX_PATH_SEC6                     0x0bb3
+#define WCD9335_CDC_RX5_RX_PATH_SEC7                     0x0bb4
+#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC0                 0x0bb5
+#define WCD9335_CDC_RX5_RX_PATH_MIX_SEC1                 0x0bb6
+#define WCD9335_CDC_RX6_RX_PATH_CTL                      0x0bb9
+#define WCD9335_CDC_RX6_RX_PATH_CFG0                     0x0bba
+#define WCD9335_CDC_RX6_RX_PATH_CFG1                     0x0bbb
+#define WCD9335_CDC_RX6_RX_PATH_CFG2                     0x0bbc
+#define WCD9335_CDC_RX6_RX_VOL_CTL                       0x0bbd
+#define WCD9335_CDC_RX6_RX_PATH_MIX_CTL                  0x0bbe
+#define WCD9335_CDC_RX6_RX_PATH_MIX_CFG                  0x0bbf
+#define WCD9335_CDC_RX6_RX_VOL_MIX_CTL                   0x0bc0
+#define WCD9335_CDC_RX6_RX_PATH_SEC0                     0x0bc1
+#define WCD9335_CDC_RX6_RX_PATH_SEC1                     0x0bc2
+#define WCD9335_CDC_RX6_RX_PATH_SEC2                     0x0bc3
+#define WCD9335_CDC_RX6_RX_PATH_SEC3                     0x0bc4
+#define WCD9335_CDC_RX6_RX_PATH_SEC5                     0x0bc6
+#define WCD9335_CDC_RX6_RX_PATH_SEC6                     0x0bc7
+#define WCD9335_CDC_RX6_RX_PATH_SEC7                     0x0bc8
+#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC0                 0x0bc9
+#define WCD9335_CDC_RX6_RX_PATH_MIX_SEC1                 0x0bca
+#define WCD9335_CDC_RX7_RX_PATH_CTL                      0x0bcd
+#define WCD9335_CDC_RX7_RX_PATH_CFG0                     0x0bce
+#define WCD9335_CDC_RX7_RX_PATH_CFG1                     0x0bcf
+#define WCD9335_CDC_RX7_RX_PATH_CFG2                     0x0bd0
+#define WCD9335_CDC_RX7_RX_VOL_CTL                       0x0bd1
+#define WCD9335_CDC_RX7_RX_PATH_MIX_CTL                  0x0bd2
+#define WCD9335_CDC_RX7_RX_PATH_MIX_CFG                  0x0bd3
+#define WCD9335_CDC_RX7_RX_VOL_MIX_CTL                   0x0bd4
+#define WCD9335_CDC_RX7_RX_PATH_SEC0                     0x0bd5
+#define WCD9335_CDC_RX7_RX_PATH_SEC1                     0x0bd6
+#define WCD9335_CDC_RX7_RX_PATH_SEC2                     0x0bd7
+#define WCD9335_CDC_RX7_RX_PATH_SEC3                     0x0bd8
+#define WCD9335_CDC_RX7_RX_PATH_SEC5                     0x0bda
+#define WCD9335_CDC_RX7_RX_PATH_SEC6                     0x0bdb
+#define WCD9335_CDC_RX7_RX_PATH_SEC7                     0x0bdc
+#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC0                 0x0bdd
+#define WCD9335_CDC_RX7_RX_PATH_MIX_SEC1                 0x0bde
+#define WCD9335_CDC_RX8_RX_PATH_CTL                      0x0be1
+#define WCD9335_CDC_RX8_RX_PATH_CFG0                     0x0be2
+#define WCD9335_CDC_RX8_RX_PATH_CFG1                     0x0be3
+#define WCD9335_CDC_RX8_RX_PATH_CFG2                     0x0be4
+#define WCD9335_CDC_RX8_RX_VOL_CTL                       0x0be5
+#define WCD9335_CDC_RX8_RX_PATH_MIX_CTL                  0x0be6
+#define WCD9335_CDC_RX8_RX_PATH_MIX_CFG                  0x0be7
+#define WCD9335_CDC_RX8_RX_VOL_MIX_CTL                   0x0be8
+#define WCD9335_CDC_RX8_RX_PATH_SEC0                     0x0be9
+#define WCD9335_CDC_RX8_RX_PATH_SEC1                     0x0bea
+#define WCD9335_CDC_RX8_RX_PATH_SEC2                     0x0beb
+#define WCD9335_CDC_RX8_RX_PATH_SEC3                     0x0bec
+#define WCD9335_CDC_RX8_RX_PATH_SEC5                     0x0bee
+#define WCD9335_CDC_RX8_RX_PATH_SEC6                     0x0bef
+#define WCD9335_CDC_RX8_RX_PATH_SEC7                     0x0bf0
+#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC0                 0x0bf1
+#define WCD9335_CDC_RX8_RX_PATH_MIX_SEC1                 0x0bf2
+
+/* Page-12 Registers */
+#define WCD9335_PAGE12_PAGE_REGISTER                     0x0c00
+#define WCD9335_CDC_CLSH_CRC                             0x0c01
+#define WCD9335_CDC_CLSH_DLY_CTRL                        0x0c02
+#define WCD9335_CDC_CLSH_DECAY_CTRL                      0x0c03
+#define WCD9335_CDC_CLSH_HPH_V_PA                        0x0c04
+#define WCD9335_CDC_CLSH_EAR_V_PA                        0x0c05
+#define WCD9335_CDC_CLSH_HPH_V_HD                        0x0c06
+#define WCD9335_CDC_CLSH_EAR_V_HD                        0x0c07
+#define WCD9335_CDC_CLSH_K1_MSB                          0x0c08
+#define WCD9335_CDC_CLSH_K1_LSB                          0x0c09
+#define WCD9335_CDC_CLSH_K2_MSB                          0x0c0a
+#define WCD9335_CDC_CLSH_K2_LSB                          0x0c0b
+#define WCD9335_CDC_CLSH_IDLE_CTRL                       0x0c0c
+#define WCD9335_CDC_CLSH_IDLE_HPH                        0x0c0d
+#define WCD9335_CDC_CLSH_IDLE_EAR                        0x0c0e
+#define WCD9335_CDC_CLSH_TEST0                           0x0c0f
+#define WCD9335_CDC_CLSH_TEST1                           0x0c10
+#define WCD9335_CDC_CLSH_OVR_VREF                        0x0c11
+#define WCD9335_CDC_BOOST0_BOOST_PATH_CTL                0x0c19
+#define WCD9335_CDC_BOOST0_BOOST_CTL                     0x0c1a
+#define WCD9335_CDC_BOOST0_BOOST_CFG1                    0x0c1b
+#define WCD9335_CDC_BOOST0_BOOST_CFG2                    0x0c1c
+#define WCD9335_CDC_BOOST1_BOOST_PATH_CTL                0x0c21
+#define WCD9335_CDC_BOOST1_BOOST_CTL                     0x0c22
+#define WCD9335_CDC_BOOST1_BOOST_CFG1                    0x0c23
+#define WCD9335_CDC_BOOST1_BOOST_CFG2                    0x0c24
+#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_0                 0x0c29
+#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_1                 0x0c2a
+#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_2                 0x0c2b
+#define WCD9335_SWR_AHB_BRIDGE_WR_DATA_3                 0x0c2c
+#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0                 0x0c2d
+#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_1                 0x0c2e
+#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_2                 0x0c2f
+#define WCD9335_SWR_AHB_BRIDGE_WR_ADDR_3                 0x0c30
+#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0                 0x0c31
+#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_1                 0x0c32
+#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_2                 0x0c33
+#define WCD9335_SWR_AHB_BRIDGE_RD_ADDR_3                 0x0c34
+#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_0                 0x0c35
+#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_1                 0x0c36
+#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_2                 0x0c37
+#define WCD9335_SWR_AHB_BRIDGE_RD_DATA_3                 0x0c38
+#define WCD9335_SWR_AHB_BRIDGE_ACCESS_CFG                0x0c39
+#define WCD9335_SWR_AHB_BRIDGE_ACCESS_STATUS             0x0c3a
+#define WCD9335_CDC_VBAT_VBAT_PATH_CTL                   0x0c3d
+#define WCD9335_CDC_VBAT_VBAT_CFG                        0x0c3e
+#define WCD9335_CDC_VBAT_VBAT_ADC_CAL1                   0x0c3f
+#define WCD9335_CDC_VBAT_VBAT_ADC_CAL2                   0x0c40
+#define WCD9335_CDC_VBAT_VBAT_ADC_CAL3                   0x0c41
+#define WCD9335_CDC_VBAT_VBAT_PK_EST1                    0x0c42
+#define WCD9335_CDC_VBAT_VBAT_PK_EST2                    0x0c43
+#define WCD9335_CDC_VBAT_VBAT_PK_EST3                    0x0c44
+#define WCD9335_CDC_VBAT_VBAT_RF_PROC1                   0x0c45
+#define WCD9335_CDC_VBAT_VBAT_RF_PROC2                   0x0c46
+#define WCD9335_CDC_VBAT_VBAT_TAC1                       0x0c47
+#define WCD9335_CDC_VBAT_VBAT_TAC2                       0x0c48
+#define WCD9335_CDC_VBAT_VBAT_TAC3                       0x0c49
+#define WCD9335_CDC_VBAT_VBAT_TAC4                       0x0c4a
+#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD1                  0x0c4b
+#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD2                  0x0c4c
+#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD3                  0x0c4d
+#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD4                  0x0c4e
+#define WCD9335_CDC_VBAT_VBAT_DEBUG1                     0x0c4f
+#define WCD9335_CDC_VBAT_VBAT_GAIN_UPD_MON               0x0c50
+#define WCD9335_CDC_VBAT_VBAT_GAIN_MON_VAL               0x0c51
+#define WCD9335_SPLINE_SRC0_CLK_RST_CTL_0                0x0c55
+#define WCD9335_SPLINE_SRC0_STATUS                       0x0c56
+#define WCD9335_SPLINE_SRC1_CLK_RST_CTL_0                0x0c6d
+#define WCD9335_SPLINE_SRC1_STATUS                       0x0c6e
+#define WCD9335_SPLINE_SRC2_CLK_RST_CTL_0                0x0c85
+#define WCD9335_SPLINE_SRC2_STATUS                       0x0c86
+#define WCD9335_SPLINE_SRC3_CLK_RST_CTL_0                0x0c9d
+#define WCD9335_SPLINE_SRC3_STATUS                       0x0c9e
+#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL        0x0cb5
+#define WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1       0x0cb6
+#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL        0x0cb9
+#define WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1       0x0cba
+
+/* Page-13 Registers */
+#define WCD9335_PAGE13_PAGE_REGISTER                     0x0d00
+#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0              0x0d01
+#define WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1              0x0d02
+#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0              0x0d03
+#define WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1              0x0d04
+#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0              0x0d05
+#define WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1              0x0d06
+#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0              0x0d07
+#define WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1              0x0d08
+#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0              0x0d09
+#define WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1              0x0d0a
+#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0              0x0d0b
+#define WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1              0x0d0c
+#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0              0x0d0d
+#define WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1              0x0d0e
+#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0              0x0d0f
+#define WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1              0x0d10
+#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0              0x0d11
+#define WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1              0x0d12
+#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0               0x0d13
+#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1               0x0d14
+#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2               0x0d15
+#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3               0x0d16
+#define WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4               0x0d17
+#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0         0x0d18
+#define WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1         0x0d19
+#define WCD9335_CDC_RX_INP_MUX_ANC_CFG0                  0x0d1a
+#define WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0           0x0d1b
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0             0x0d1d
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1             0x0d1e
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0             0x0d1f
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1             0x0d20
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0             0x0d21
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1             0x0d22
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0             0x0d23
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1             0x0d24
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0             0x0d25
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0             0x0d26
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0             0x0d27
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0             0x0d28
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0             0x0d29
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0            0x0d2b
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0            0x0d2c
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0            0x0d2d
+#define WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0            0x0d2e
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0   0x0d31
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1   0x0d32
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2   0x0d33
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3   0x0d34
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0   0x0d35
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1   0x0d36
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2   0x0d37
+#define WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3   0x0d38
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0                0x0d3a
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1                0x0d3b
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2                0x0d3c
+#define WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3                0x0d3d
+#define WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL            0x0d41
+#define WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL          0x0d42
+#define WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL             0x0d43
+#define WCD9335_CDC_PROX_DETECT_PROX_CTL                 0x0d49
+#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD0        0x0d4a
+#define WCD9335_CDC_PROX_DETECT_PROX_POLL_PERIOD1        0x0d4b
+#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB     0x0d4c
+#define WCD9335_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB     0x0d4d
+#define WCD9335_CDC_PROX_DETECT_PROX_STATUS              0x0d4e
+#define WCD9335_CDC_PROX_DETECT_PROX_TEST_CTRL           0x0d4f
+#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB       0x0d50
+#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB       0x0d51
+#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD    0x0d52
+#define WCD9335_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD    0x0d53
+#define WCD9335_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT      0x0d54
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL           0x0d55
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL        0x0d56
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL        0x0d57
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL        0x0d58
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL        0x0d59
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL        0x0d5a
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL        0x0d5b
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL        0x0d5c
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL        0x0d5d
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_CTL                0x0d5e
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL     0x0d5f
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL        0x0d60
+#define WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL        0x0d61
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL           0x0d65
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL        0x0d66
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL        0x0d67
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL        0x0d68
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL        0x0d69
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL        0x0d6a
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL        0x0d6b
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL        0x0d6c
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL        0x0d6d
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_CTL                0x0d6e
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL     0x0d6f
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL        0x0d70
+#define WCD9335_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL        0x0d71
+#define WCD9335_CDC_TOP_TOP_CFG0                         0x0d81
+#define WCD9335_CDC_TOP_TOP_CFG1                         0x0d82
+#define WCD9335_CDC_TOP_TOP_CFG2                         0x0d83
+#define WCD9335_CDC_TOP_TOP_CFG3                         0x0d84
+#define WCD9335_CDC_TOP_TOP_CFG4                         0x0d85
+#define WCD9335_CDC_TOP_TOP_CFG5                         0x0d86
+#define WCD9335_CDC_TOP_TOP_CFG6                         0x0d87
+#define WCD9335_CDC_TOP_TOP_CFG7                         0x0d88
+#define WCD9335_CDC_TOP_HPHL_COMP_WR_LSB                 0x0d89
+#define WCD9335_CDC_TOP_HPHL_COMP_WR_MSB                 0x0d8a
+#define WCD9335_CDC_TOP_HPHL_COMP_LUT                    0x0d8b
+#define WCD9335_CDC_TOP_HPHL_COMP_RD_LSB                 0x0d8c
+#define WCD9335_CDC_TOP_HPHL_COMP_RD_MSB                 0x0d8d
+#define WCD9335_CDC_TOP_HPHR_COMP_WR_LSB                 0x0d8e
+#define WCD9335_CDC_TOP_HPHR_COMP_WR_MSB                 0x0d8f
+#define WCD9335_CDC_TOP_HPHR_COMP_LUT                    0x0d90
+#define WCD9335_CDC_TOP_HPHR_COMP_RD_LSB                 0x0d91
+#define WCD9335_CDC_TOP_HPHR_COMP_RD_MSB                 0x0d92
+#define WCD9335_CDC_TOP_DIFFL_COMP_WR_LSB                0x0d93
+#define WCD9335_CDC_TOP_DIFFL_COMP_WR_MSB                0x0d94
+#define WCD9335_CDC_TOP_DIFFL_COMP_LUT                   0x0d95
+#define WCD9335_CDC_TOP_DIFFL_COMP_RD_LSB                0x0d96
+#define WCD9335_CDC_TOP_DIFFL_COMP_RD_MSB                0x0d97
+#define WCD9335_CDC_TOP_DIFFR_COMP_WR_LSB                0x0d98
+#define WCD9335_CDC_TOP_DIFFR_COMP_WR_MSB                0x0d99
+#define WCD9335_CDC_TOP_DIFFR_COMP_LUT                   0x0d9a
+#define WCD9335_CDC_TOP_DIFFR_COMP_RD_LSB                0x0d9b
+#define WCD9335_CDC_TOP_DIFFR_COMP_RD_MSB                0x0d9c
+
+/* Page-0x80 Registers */
+#define WCD9335_PAGE80_PAGE_REGISTER                     0x8000
+#define WCD9335_TLMM_BIST_MODE_PINCFG                    0x8001
+#define WCD9335_TLMM_RF_PA_ON_PINCFG                     0x8002
+#define WCD9335_TLMM_INTR1_PINCFG                        0x8003
+#define WCD9335_TLMM_INTR2_PINCFG                        0x8004
+#define WCD9335_TLMM_SWR_DATA_PINCFG                     0x8005
+#define WCD9335_TLMM_SWR_CLK_PINCFG                      0x8006
+#define WCD9335_TLMM_SLIMBUS_DATA2_PINCFG                0x8007
+#define WCD9335_TLMM_I2C_CLK_PINCFG                      0x8008
+#define WCD9335_TLMM_I2C_DATA_PINCFG                     0x8009
+#define WCD9335_TLMM_I2S_RX_SD0_PINCFG                   0x800a
+#define WCD9335_TLMM_I2S_RX_SD1_PINCFG                   0x800b
+#define WCD9335_TLMM_I2S_RX_SCK_PINCFG                   0x800c
+#define WCD9335_TLMM_I2S_RX_WS_PINCFG                    0x800d
+#define WCD9335_TLMM_I2S_TX_SD0_PINCFG                   0x800e
+#define WCD9335_TLMM_I2S_TX_SD1_PINCFG                   0x800f
+#define WCD9335_TLMM_I2S_TX_SCK_PINCFG                   0x8010
+#define WCD9335_TLMM_I2S_TX_WS_PINCFG                    0x8011
+#define WCD9335_TLMM_DMIC1_CLK_PINCFG                    0x8012
+#define WCD9335_TLMM_DMIC1_DATA_PINCFG                   0x8013
+#define WCD9335_TLMM_DMIC2_CLK_PINCFG                    0x8014
+#define WCD9335_TLMM_DMIC2_DATA_PINCFG                   0x8015
+#define WCD9335_TLMM_DMIC3_CLK_PINCFG                    0x8016
+#define WCD9335_TLMM_DMIC3_DATA_PINCFG                   0x8017
+#define WCD9335_TLMM_JTDI_PINCFG                         0x8018
+#define WCD9335_TLMM_JTDO_PINCFG                         0x8019
+#define WCD9335_TLMM_JTMS_PINCFG                         0x801a
+#define WCD9335_TLMM_JTCK_PINCFG                         0x801b
+#define WCD9335_TLMM_JTRST_PINCFG                        0x801c
+#define WCD9335_TEST_DEBUG_PIN_CTL_OE_0                  0x8031
+#define WCD9335_TEST_DEBUG_PIN_CTL_OE_1                  0x8032
+#define WCD9335_TEST_DEBUG_PIN_CTL_OE_2                  0x8033
+#define WCD9335_TEST_DEBUG_PIN_CTL_OE_3                  0x8034
+#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_0                0x8035
+#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_1                0x8036
+#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_2                0x8037
+#define WCD9335_TEST_DEBUG_PIN_CTL_DATA_3                0x8038
+#define WCD9335_TEST_DEBUG_PAD_DRVCTL                    0x8039
+#define WCD9335_TEST_DEBUG_PIN_STATUS                    0x803a
+#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_1                0x803b
+#define WCD9335_TEST_DEBUG_NPL_DLY_TEST_2                0x803c
+#define WCD9335_TEST_DEBUG_MEM_CTRL                      0x803d
+#define WCD9335_TEST_DEBUG_DEBUG_BUS_SEL                 0x8041
+#define WCD9335_TEST_DEBUG_DEBUG_JTAG                    0x8042
+#define WCD9335_TEST_DEBUG_DEBUG_EN_1                    0x8043
+#define WCD9335_TEST_DEBUG_DEBUG_EN_2                    0x8044
+#define WCD9335_TEST_DEBUG_DEBUG_EN_3                    0x8045
+#define WCD9335_MAX_REGISTER                             0x80FF
+
+/* SLIMBUS Slave Registers */
+#define TASHA_SLIM_PGD_PORT_INT_EN0                     (0x30)
+#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0             (0x34)
+#define TASHA_SLIM_PGD_PORT_INT_STATUS_RX_1             (0x35)
+#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_0             (0x36)
+#define TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1             (0x37)
+#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_0                (0x38)
+#define TASHA_SLIM_PGD_PORT_INT_CLR_RX_1                (0x39)
+#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_0                (0x3A)
+#define TASHA_SLIM_PGD_PORT_INT_CLR_TX_1                (0x3B)
+#define TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0		(0x60)
+#define TASHA_SLIM_PGD_PORT_INT_TX_SOURCE0		(0x70)
+
+/* Macros for Packing Register Writes into a U32 */
+#define TASHA_PACKED_REG_SIZE sizeof(u32)
+
+#define TASHA_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\
+	((mask & 0xff) << 8)|((reg & 0xffff) << 16))
+#define TASHA_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+	do { \
+		((reg) = ((packed >> 16) & (0xffff))); \
+		((mask) = ((packed >> 8) & (0xff))); \
+		((val) = ((packed) & (0xff))); \
+	} while (0)
+#endif
diff --git a/include/linux/mfd/wcd934x/registers.h b/include/linux/mfd/wcd934x/registers.h
new file mode 100644
index 0000000..a824875
--- /dev/null
+++ b/include/linux/mfd/wcd934x/registers.h
@@ -0,0 +1,1848 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD934X_REGISTERS_H
+#define _WCD934X_REGISTERS_H
+
+#define WCD934X_PAGE_SIZE 256
+#define WCD934X_NUM_PAGES 256
+
+extern const u8 * const wcd934x_reg[WCD934X_NUM_PAGES];
+
+enum {
+	WCD934X_PAGE_0 = 0,
+	WCD934X_PAGE_1,
+	WCD934X_PAGE_2,
+	WCD934X_PAGE_4 = 4,
+	WCD934X_PAGE_5,
+	WCD934X_PAGE_6,
+	WCD934X_PAGE_7,
+	WCD934X_PAGE_10 = 0xA,
+	WCD934X_PAGE_11,
+	WCD934X_PAGE_12,
+	WCD934X_PAGE_13,
+	WCD934X_PAGE_14,
+	WCD934X_PAGE_15,
+	WCD934X_PAGE_0x50,
+	WCD934X_PAGE_0X80,
+};
+
+enum {
+	WCD934X_WRITE = 0,
+	WCD934X_READ,
+	WCD934X_READ_WRITE,
+};
+
+/* Page-0 Registers */
+#define WCD934X_PAGE0_PAGE_REGISTER                        0x0000
+#define WCD934X_CODEC_RPM_CLK_BYPASS                       0x0001
+#define WCD934X_CODEC_RPM_CLK_GATE                         0x0002
+#define WCD934X_CODEC_RPM_CLK_MCLK_CFG                     0x0003
+#define WCD934X_CODEC_RPM_CLK_MCLK2_CFG                    0x0004
+#define WCD934X_CODEC_RPM_I2S_DSD_CLK_SEL                  0x0005
+#define WCD934X_CODEC_RPM_RST_CTL                          0x0009
+#define WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL               0x0011
+#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0               0x0021
+#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE1               0x0022
+#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2               0x0023
+#define WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE3               0x0024
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_CTL                   0x0025
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST0                 0x0026
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_TEST1                 0x0027
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT0              0x0029
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1              0x002a
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2              0x002b
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT3              0x002c
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT4              0x002d
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT5              0x002e
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT6              0x002f
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT7              0x0030
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT8              0x0031
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT9              0x0032
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT10             0x0033
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT11             0x0034
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT12             0x0035
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT13             0x0036
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14             0x0037
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15             0x0038
+#define WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS                0x0039
+#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_NONNEGO        0x003a
+#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_1              0x003b
+#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_2              0x003c
+#define WCD934X_CHIP_TIER_CTRL_I2C_SLAVE_ID_3              0x003d
+#define WCD934X_CHIP_TIER_CTRL_ANA_WAIT_STATE_CTL          0x003e
+#define WCD934X_CHIP_TIER_CTRL_SLNQ_WAIT_STATE_CTL         0x003f
+#define WCD934X_CHIP_TIER_CTRL_I2C_ACTIVE                  0x0040
+#define WCD934X_CHIP_TIER_CTRL_ALT_FUNC_EN                 0x0041
+#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_OE                 0x0042
+#define WCD934X_CHIP_TIER_CTRL_GPIO_CTL_DATA               0x0043
+#define WCD934X_DATA_HUB_RX0_CFG                           0x0051
+#define WCD934X_DATA_HUB_RX1_CFG                           0x0052
+#define WCD934X_DATA_HUB_RX2_CFG                           0x0053
+#define WCD934X_DATA_HUB_RX3_CFG                           0x0054
+#define WCD934X_DATA_HUB_RX4_CFG                           0x0055
+#define WCD934X_DATA_HUB_RX5_CFG                           0x0056
+#define WCD934X_DATA_HUB_RX6_CFG                           0x0057
+#define WCD934X_DATA_HUB_RX7_CFG                           0x0058
+#define WCD934X_DATA_HUB_SB_TX0_INP_CFG                    0x0061
+#define WCD934X_DATA_HUB_SB_TX1_INP_CFG                    0x0062
+#define WCD934X_DATA_HUB_SB_TX2_INP_CFG                    0x0063
+#define WCD934X_DATA_HUB_SB_TX3_INP_CFG                    0x0064
+#define WCD934X_DATA_HUB_SB_TX4_INP_CFG                    0x0065
+#define WCD934X_DATA_HUB_SB_TX5_INP_CFG                    0x0066
+#define WCD934X_DATA_HUB_SB_TX6_INP_CFG                    0x0067
+#define WCD934X_DATA_HUB_SB_TX7_INP_CFG                    0x0068
+#define WCD934X_DATA_HUB_SB_TX8_INP_CFG                    0x0069
+#define WCD934X_DATA_HUB_SB_TX9_INP_CFG                    0x006a
+#define WCD934X_DATA_HUB_SB_TX10_INP_CFG                   0x006b
+#define WCD934X_DATA_HUB_SB_TX11_INP_CFG                   0x006c
+#define WCD934X_DATA_HUB_SB_TX13_INP_CFG                   0x006e
+#define WCD934X_DATA_HUB_SB_TX14_INP_CFG                   0x006f
+#define WCD934X_DATA_HUB_SB_TX15_INP_CFG                   0x0070
+#define WCD934X_DATA_HUB_I2S_TX0_CFG                       0x0071
+#define WCD934X_DATA_HUB_I2S_TX1_0_CFG                     0x0073
+#define WCD934X_DATA_HUB_I2S_TX1_1_CFG                     0x0074
+#define WCD934X_DATA_HUB_I2S_0_CTL                         0x0081
+#define WCD934X_DATA_HUB_I2S_1_CTL                         0x0082
+#define WCD934X_DATA_HUB_I2S_2_CTL                         0x0083
+#define WCD934X_DATA_HUB_I2S_3_CTL                         0x0084
+#define WCD934X_DATA_HUB_I2S_CLKSRC_CTL                    0x0085
+#define WCD934X_DATA_HUB_I2S_COMMON_CTL                    0x0086
+#define WCD934X_DATA_HUB_I2S_0_TDM_CTL                     0x0087
+#define WCD934X_DATA_HUB_I2S_STATUS                        0x0088
+#define WCD934X_DMA_RDMA_CTL_0                             0x0091
+#define WCD934X_DMA_CH_2_3_CFG_RDMA_0                      0x0092
+#define WCD934X_DMA_CH_0_1_CFG_RDMA_0                      0x0093
+#define WCD934X_DMA_RDMA_CTL_1                             0x0094
+#define WCD934X_DMA_CH_2_3_CFG_RDMA_1                      0x0095
+#define WCD934X_DMA_CH_0_1_CFG_RDMA_1                      0x0096
+#define WCD934X_DMA_RDMA_CTL_2                             0x0097
+#define WCD934X_DMA_CH_2_3_CFG_RDMA_2                      0x0098
+#define WCD934X_DMA_CH_0_1_CFG_RDMA_2                      0x0099
+#define WCD934X_DMA_RDMA_CTL_3                             0x009A
+#define WCD934X_DMA_CH_2_3_CFG_RDMA_3                      0x009B
+#define WCD934X_DMA_CH_0_1_CFG_RDMA_3                      0x009C
+#define WCD934X_DMA_RDMA_CTL_4                             0x009D
+#define WCD934X_DMA_CH_2_3_CFG_RDMA_4                      0x009E
+#define WCD934X_DMA_CH_0_1_CFG_RDMA_4                      0x009F
+#define WCD934X_DMA_RDMA4_PRT_CFG                          0x00b1
+#define WCD934X_DMA_RDMA_SBTX0_7_CFG                       0x00b9
+#define WCD934X_DMA_RDMA_SBTX8_11_CFG                      0x00ba
+#define WCD934X_DMA_WDMA_CTL_0                             0x00c1
+#define WCD934X_DMA_CH_4_5_CFG_WDMA_0                      0x00c2
+#define WCD934X_DMA_CH_2_3_CFG_WDMA_0                      0x00c3
+#define WCD934X_DMA_CH_0_1_CFG_WDMA_0                      0x00c4
+#define WCD934X_DMA_WDMA_CTL_1                             0x00C6
+#define WCD934X_DMA_CH_4_5_CFG_WDMA_1                      0x00C7
+#define WCD934X_DMA_CH_2_3_CFG_WDMA_1                      0x00C8
+#define WCD934X_DMA_CH_0_1_CFG_WDMA_1                      0x00C9
+#define WCD934X_DMA_WDMA_CTL_2                             0x00CB
+#define WCD934X_DMA_CH_4_5_CFG_WDMA_2                      0x00CC
+#define WCD934X_DMA_CH_2_3_CFG_WDMA_2                      0x00CD
+#define WCD934X_DMA_CH_0_1_CFG_WDMA_2                      0x00CE
+#define WCD934X_DMA_WDMA_CTL_3                             0x00D0
+#define WCD934X_DMA_CH_4_5_CFG_WDMA_3                      0x00D1
+#define WCD934X_DMA_CH_2_3_CFG_WDMA_3                      0x00D2
+#define WCD934X_DMA_CH_0_1_CFG_WDMA_3                      0x00D3
+#define WCD934X_DMA_WDMA_CTL_4                             0x00D5
+#define WCD934X_DMA_CH_4_5_CFG_WDMA_4                      0x00D6
+#define WCD934X_DMA_CH_2_3_CFG_WDMA_4                      0x00D7
+#define WCD934X_DMA_CH_0_1_CFG_WDMA_4                      0x00D8
+#define WCD934X_DMA_WDMA0_PRT_CFG                          0x00E1
+#define WCD934X_DMA_WDMA3_PRT_CFG                          0x00E2
+#define WCD934X_DMA_WDMA4_PRT0_3_CFG                       0x00E3
+#define WCD934X_DMA_WDMA4_PRT4_7_CFG                       0x00E4
+#define WCD934X_PAGE1_PAGE_REGISTER                        0x0100
+#define WCD934X_CPE_FLL_USER_CTL_0                         0x0101
+#define WCD934X_CPE_FLL_USER_CTL_1                         0x0102
+#define WCD934X_CPE_FLL_USER_CTL_2                         0x0103
+#define WCD934X_CPE_FLL_USER_CTL_3                         0x0104
+#define WCD934X_CPE_FLL_USER_CTL_4                         0x0105
+#define WCD934X_CPE_FLL_USER_CTL_5                         0x0106
+#define WCD934X_CPE_FLL_USER_CTL_6                         0x0107
+#define WCD934X_CPE_FLL_USER_CTL_7                         0x0108
+#define WCD934X_CPE_FLL_USER_CTL_8                         0x0109
+#define WCD934X_CPE_FLL_USER_CTL_9                         0x010a
+#define WCD934X_CPE_FLL_L_VAL_CTL_0                        0x010b
+#define WCD934X_CPE_FLL_L_VAL_CTL_1                        0x010c
+#define WCD934X_CPE_FLL_DSM_FRAC_CTL_0                     0x010d
+#define WCD934X_CPE_FLL_DSM_FRAC_CTL_1                     0x010e
+#define WCD934X_CPE_FLL_CONFIG_CTL_0                       0x010f
+#define WCD934X_CPE_FLL_CONFIG_CTL_1                       0x0110
+#define WCD934X_CPE_FLL_CONFIG_CTL_2                       0x0111
+#define WCD934X_CPE_FLL_CONFIG_CTL_3                       0x0112
+#define WCD934X_CPE_FLL_CONFIG_CTL_4                       0x0113
+#define WCD934X_CPE_FLL_TEST_CTL_0                         0x0114
+#define WCD934X_CPE_FLL_TEST_CTL_1                         0x0115
+#define WCD934X_CPE_FLL_TEST_CTL_2                         0x0116
+#define WCD934X_CPE_FLL_TEST_CTL_3                         0x0117
+#define WCD934X_CPE_FLL_TEST_CTL_4                         0x0118
+#define WCD934X_CPE_FLL_TEST_CTL_5                         0x0119
+#define WCD934X_CPE_FLL_TEST_CTL_6                         0x011a
+#define WCD934X_CPE_FLL_TEST_CTL_7                         0x011b
+#define WCD934X_CPE_FLL_FREQ_CTL_0                         0x011c
+#define WCD934X_CPE_FLL_FREQ_CTL_1                         0x011d
+#define WCD934X_CPE_FLL_FREQ_CTL_2                         0x011e
+#define WCD934X_CPE_FLL_FREQ_CTL_3                         0x011f
+#define WCD934X_CPE_FLL_SSC_CTL_0                          0x0120
+#define WCD934X_CPE_FLL_SSC_CTL_1                          0x0121
+#define WCD934X_CPE_FLL_SSC_CTL_2                          0x0122
+#define WCD934X_CPE_FLL_SSC_CTL_3                          0x0123
+#define WCD934X_CPE_FLL_FLL_MODE                           0x0124
+#define WCD934X_CPE_FLL_STATUS_0                           0x0125
+#define WCD934X_CPE_FLL_STATUS_1                           0x0126
+#define WCD934X_CPE_FLL_STATUS_2                           0x0127
+#define WCD934X_CPE_FLL_STATUS_3                           0x0128
+#define WCD934X_I2S_FLL_USER_CTL_0                         0x0141
+#define WCD934X_I2S_FLL_USER_CTL_1                         0x0142
+#define WCD934X_I2S_FLL_USER_CTL_2                         0x0143
+#define WCD934X_I2S_FLL_USER_CTL_3                         0x0144
+#define WCD934X_I2S_FLL_USER_CTL_4                         0x0145
+#define WCD934X_I2S_FLL_USER_CTL_5                         0x0146
+#define WCD934X_I2S_FLL_USER_CTL_6                         0x0147
+#define WCD934X_I2S_FLL_USER_CTL_7                         0x0148
+#define WCD934X_I2S_FLL_USER_CTL_8                         0x0149
+#define WCD934X_I2S_FLL_USER_CTL_9                         0x014a
+#define WCD934X_I2S_FLL_L_VAL_CTL_0                        0x014b
+#define WCD934X_I2S_FLL_L_VAL_CTL_1                        0x014c
+#define WCD934X_I2S_FLL_DSM_FRAC_CTL_0                     0x014d
+#define WCD934X_I2S_FLL_DSM_FRAC_CTL_1                     0x014e
+#define WCD934X_I2S_FLL_CONFIG_CTL_0                       0x014f
+#define WCD934X_I2S_FLL_CONFIG_CTL_1                       0x0150
+#define WCD934X_I2S_FLL_CONFIG_CTL_2                       0x0151
+#define WCD934X_I2S_FLL_CONFIG_CTL_3                       0x0152
+#define WCD934X_I2S_FLL_CONFIG_CTL_4                       0x0153
+#define WCD934X_I2S_FLL_TEST_CTL_0                         0x0154
+#define WCD934X_I2S_FLL_TEST_CTL_1                         0x0155
+#define WCD934X_I2S_FLL_TEST_CTL_2                         0x0156
+#define WCD934X_I2S_FLL_TEST_CTL_3                         0x0157
+#define WCD934X_I2S_FLL_TEST_CTL_4                         0x0158
+#define WCD934X_I2S_FLL_TEST_CTL_5                         0x0159
+#define WCD934X_I2S_FLL_TEST_CTL_6                         0x015a
+#define WCD934X_I2S_FLL_TEST_CTL_7                         0x015b
+#define WCD934X_I2S_FLL_FREQ_CTL_0                         0x015c
+#define WCD934X_I2S_FLL_FREQ_CTL_1                         0x015d
+#define WCD934X_I2S_FLL_FREQ_CTL_2                         0x015e
+#define WCD934X_I2S_FLL_FREQ_CTL_3                         0x015f
+#define WCD934X_I2S_FLL_SSC_CTL_0                          0x0160
+#define WCD934X_I2S_FLL_SSC_CTL_1                          0x0161
+#define WCD934X_I2S_FLL_SSC_CTL_2                          0x0162
+#define WCD934X_I2S_FLL_SSC_CTL_3                          0x0163
+#define WCD934X_I2S_FLL_FLL_MODE                           0x0164
+#define WCD934X_I2S_FLL_STATUS_0                           0x0165
+#define WCD934X_I2S_FLL_STATUS_1                           0x0166
+#define WCD934X_I2S_FLL_STATUS_2                           0x0167
+#define WCD934X_I2S_FLL_STATUS_3                           0x0168
+#define WCD934X_SB_FLL_USER_CTL_0                          0x0181
+#define WCD934X_SB_FLL_USER_CTL_1                          0x0182
+#define WCD934X_SB_FLL_USER_CTL_2                          0x0183
+#define WCD934X_SB_FLL_USER_CTL_3                          0x0184
+#define WCD934X_SB_FLL_USER_CTL_4                          0x0185
+#define WCD934X_SB_FLL_USER_CTL_5                          0x0186
+#define WCD934X_SB_FLL_USER_CTL_6                          0x0187
+#define WCD934X_SB_FLL_USER_CTL_7                          0x0188
+#define WCD934X_SB_FLL_USER_CTL_8                          0x0189
+#define WCD934X_SB_FLL_USER_CTL_9                          0x018a
+#define WCD934X_SB_FLL_L_VAL_CTL_0                         0x018b
+#define WCD934X_SB_FLL_L_VAL_CTL_1                         0x018c
+#define WCD934X_SB_FLL_DSM_FRAC_CTL_0                      0x018d
+#define WCD934X_SB_FLL_DSM_FRAC_CTL_1                      0x018e
+#define WCD934X_SB_FLL_CONFIG_CTL_0                        0x018f
+#define WCD934X_SB_FLL_CONFIG_CTL_1                        0x0190
+#define WCD934X_SB_FLL_CONFIG_CTL_2                        0x0191
+#define WCD934X_SB_FLL_CONFIG_CTL_3                        0x0192
+#define WCD934X_SB_FLL_CONFIG_CTL_4                        0x0193
+#define WCD934X_SB_FLL_TEST_CTL_0                          0x0194
+#define WCD934X_SB_FLL_TEST_CTL_1                          0x0195
+#define WCD934X_SB_FLL_TEST_CTL_2                          0x0196
+#define WCD934X_SB_FLL_TEST_CTL_3                          0x0197
+#define WCD934X_SB_FLL_TEST_CTL_4                          0x0198
+#define WCD934X_SB_FLL_TEST_CTL_5                          0x0199
+#define WCD934X_SB_FLL_TEST_CTL_6                          0x019a
+#define WCD934X_SB_FLL_TEST_CTL_7                          0x019b
+#define WCD934X_SB_FLL_FREQ_CTL_0                          0x019c
+#define WCD934X_SB_FLL_FREQ_CTL_1                          0x019d
+#define WCD934X_SB_FLL_FREQ_CTL_2                          0x019e
+#define WCD934X_SB_FLL_FREQ_CTL_3                          0x019f
+#define WCD934X_SB_FLL_SSC_CTL_0                           0x01a0
+#define WCD934X_SB_FLL_SSC_CTL_1                           0x01a1
+#define WCD934X_SB_FLL_SSC_CTL_2                           0x01a2
+#define WCD934X_SB_FLL_SSC_CTL_3                           0x01a3
+#define WCD934X_SB_FLL_FLL_MODE                            0x01a4
+#define WCD934X_SB_FLL_STATUS_0                            0x01a5
+#define WCD934X_SB_FLL_STATUS_1                            0x01a6
+#define WCD934X_SB_FLL_STATUS_2                            0x01a7
+#define WCD934X_SB_FLL_STATUS_3                            0x01a8
+#define WCD934X_PAGE2_PAGE_REGISTER                        0x0200
+#define WCD934X_CPE_SS_CPE_CTL                             0x0201
+#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0                0x0202
+#define WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_1                0x0203
+#define WCD934X_CPE_SS_PWR_CPEFLL_CTL                      0x0204
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0            0x0205
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1            0x0206
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_OVERRIDE     0x0207
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0           0x0208
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1           0x0209
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2           0x020a
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3           0x020b
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_4           0x020c
+#define WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_5           0x020d
+#define WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN              0x020e
+#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL                 0x020f
+#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL        0x0210
+#define WCD934X_CPE_SS_SOC_SW_COLLAPSE_OVERRIDE_CTL1       0x0211
+#define WCD934X_CPE_SS_US_BUF_INT_PERIOD                   0x0212
+#define WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD           0x0213
+#define WCD934X_CPE_SS_SVA_CFG                             0x0214
+#define WCD934X_CPE_SS_US_CFG                              0x0215
+#define WCD934X_CPE_SS_MAD_CTL                             0x0216
+#define WCD934X_CPE_SS_CPAR_CTL                            0x0217
+#define WCD934X_CPE_SS_DMIC0_CTL                           0x0218
+#define WCD934X_CPE_SS_DMIC1_CTL                           0x0219
+#define WCD934X_CPE_SS_DMIC2_CTL                           0x021a
+#define WCD934X_CPE_SS_DMIC_CFG                            0x021b
+#define WCD934X_CPE_SS_CPAR_CFG                            0x021c
+#define WCD934X_CPE_SS_WDOG_CFG                            0x021d
+#define WCD934X_CPE_SS_BACKUP_INT                          0x021e
+#define WCD934X_CPE_SS_STATUS                              0x021f
+#define WCD934X_CPE_SS_CPE_OCD_CFG                         0x0220
+#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A                0x0221
+#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B                0x0222
+#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1A                0x0223
+#define WCD934X_CPE_SS_SS_ERROR_INT_MASK_1B                0x0224
+#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A              0x0225
+#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B              0x0226
+#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1A              0x0227
+#define WCD934X_CPE_SS_SS_ERROR_INT_STATUS_1B              0x0228
+#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A               0x0229
+#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B               0x022a
+#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1A               0x022b
+#define WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_1B               0x022c
+#define WCD934X_SOC_MAD_MAIN_CTL_1                         0x0281
+#define WCD934X_SOC_MAD_MAIN_CTL_2                         0x0282
+#define WCD934X_SOC_MAD_AUDIO_CTL_1                        0x0283
+#define WCD934X_SOC_MAD_AUDIO_CTL_2                        0x0284
+#define WCD934X_SOC_MAD_AUDIO_CTL_3                        0x0285
+#define WCD934X_SOC_MAD_AUDIO_CTL_4                        0x0286
+#define WCD934X_SOC_MAD_AUDIO_CTL_5                        0x0287
+#define WCD934X_SOC_MAD_AUDIO_CTL_6                        0x0288
+#define WCD934X_SOC_MAD_AUDIO_CTL_7                        0x0289
+#define WCD934X_SOC_MAD_AUDIO_CTL_8                        0x028a
+#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR                  0x028b
+#define WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL                  0x028c
+#define WCD934X_SOC_MAD_ULTR_CTL_1                         0x028d
+#define WCD934X_SOC_MAD_ULTR_CTL_2                         0x028e
+#define WCD934X_SOC_MAD_ULTR_CTL_3                         0x028f
+#define WCD934X_SOC_MAD_ULTR_CTL_4                         0x0290
+#define WCD934X_SOC_MAD_ULTR_CTL_5                         0x0291
+#define WCD934X_SOC_MAD_ULTR_CTL_6                         0x0292
+#define WCD934X_SOC_MAD_ULTR_CTL_7                         0x0293
+#define WCD934X_SOC_MAD_BEACON_CTL_1                       0x0294
+#define WCD934X_SOC_MAD_BEACON_CTL_2                       0x0295
+#define WCD934X_SOC_MAD_BEACON_CTL_3                       0x0296
+#define WCD934X_SOC_MAD_BEACON_CTL_4                       0x0297
+#define WCD934X_SOC_MAD_BEACON_CTL_5                       0x0298
+#define WCD934X_SOC_MAD_BEACON_CTL_6                       0x0299
+#define WCD934X_SOC_MAD_BEACON_CTL_7                       0x029a
+#define WCD934X_SOC_MAD_BEACON_CTL_8                       0x029b
+#define WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR                 0x029c
+#define WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL                 0x029d
+#define WCD934X_SOC_MAD_INP_SEL                            0x029e
+#define WCD934X_PAGE4_PAGE_REGISTER                        0x0400
+#define WCD934X_INTR_CFG                                   0x0401
+#define WCD934X_INTR_CLR_COMMIT                            0x0402
+#define WCD934X_INTR_PIN1_MASK0                            0x0409
+#define WCD934X_INTR_PIN1_MASK1                            0x040a
+#define WCD934X_INTR_PIN1_MASK2                            0x040b
+#define WCD934X_INTR_PIN1_MASK3                            0x040c
+#define WCD934X_INTR_PIN1_STATUS0                          0x0411
+#define WCD934X_INTR_PIN1_STATUS1                          0x0412
+#define WCD934X_INTR_PIN1_STATUS2                          0x0413
+#define WCD934X_INTR_PIN1_STATUS3                          0x0414
+#define WCD934X_INTR_PIN1_CLEAR0                           0x0419
+#define WCD934X_INTR_PIN1_CLEAR1                           0x041a
+#define WCD934X_INTR_PIN1_CLEAR2                           0x041b
+#define WCD934X_INTR_PIN1_CLEAR3                           0x041c
+#define WCD934X_INTR_PIN2_MASK3                            0x0424
+#define WCD934X_INTR_PIN2_STATUS3                          0x042c
+#define WCD934X_INTR_PIN2_CLEAR3                           0x0434
+#define WCD934X_INTR_CPESS_SUMRY_MASK2                     0x043b
+#define WCD934X_INTR_CPESS_SUMRY_MASK3                     0x043c
+#define WCD934X_INTR_CPESS_SUMRY_STATUS2                   0x0443
+#define WCD934X_INTR_CPESS_SUMRY_STATUS3                   0x0444
+#define WCD934X_INTR_CPESS_SUMRY_CLEAR2                    0x044b
+#define WCD934X_INTR_CPESS_SUMRY_CLEAR3                    0x044c
+#define WCD934X_INTR_LEVEL0                                0x0461
+#define WCD934X_INTR_LEVEL1                                0x0462
+#define WCD934X_INTR_LEVEL2                                0x0463
+#define WCD934X_INTR_LEVEL3                                0x0464
+#define WCD934X_INTR_BYPASS0                               0x0469
+#define WCD934X_INTR_BYPASS1                               0x046a
+#define WCD934X_INTR_BYPASS2                               0x046b
+#define WCD934X_INTR_BYPASS3                               0x046c
+#define WCD934X_INTR_SET0                                  0x0471
+#define WCD934X_INTR_SET1                                  0x0472
+#define WCD934X_INTR_SET2                                  0x0473
+#define WCD934X_INTR_SET3                                  0x0474
+#define WCD934X_INTR_CODEC_MISC_MASK                       0x04b1
+#define WCD934X_INTR_CODEC_MISC_STATUS                     0x04b2
+#define WCD934X_INTR_CODEC_MISC_CLEAR                      0x04b3
+#define WCD934X_PAGE5_PAGE_REGISTER                        0x0500
+#define WCD934X_SLNQ_DIG_DEVICE                            0x0501
+#define WCD934X_SLNQ_DIG_REVISION                          0x0502
+#define WCD934X_SLNQ_DIG_H_COMMAND                         0x0511
+#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_MSB                0x0512
+#define WCD934X_SLNQ_DIG_NUMBER_OF_BYTE_LSB                0x0513
+#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_MSB                0x0514
+#define WCD934X_SLNQ_DIG_MASTER_ADDRESS_LSB                0x0515
+#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_MSB                 0x0516
+#define WCD934X_SLNQ_DIG_SLAVE_ADDRESS_LSB                 0x0517
+#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_MSB              0x0518
+#define WCD934X_SLNQ_DIG_TIMER0_INTERRUPT_LSB              0x0519
+#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_MSB              0x051a
+#define WCD934X_SLNQ_DIG_TIMER1_INTERRUPT_LSB              0x051b
+#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_MSB              0x051c
+#define WCD934X_SLNQ_DIG_TIMER2_INTERRUPT_LSB              0x051d
+#define WCD934X_SLNQ_DIG_COMM_CTL                          0x0520
+#define WCD934X_SLNQ_DIG_FRAME_CTRL                        0x0542
+#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH1_2                0x055c
+#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH3_4                0x055d
+#define WCD934X_SLNQ_DIG_PDM_2ND_DATA_CH5                  0x055e
+#define WCD934X_SLNQ_DIG_SW_EVENT_RD                       0x0561
+#define WCD934X_SLNQ_DIG_SW_EVENT_CTRL                     0x0562
+#define WCD934X_SLNQ_DIG_PDM_SELECT_1                      0x0563
+#define WCD934X_SLNQ_DIG_PDM_SELECT_2                      0x0564
+#define WCD934X_SLNQ_DIG_PDM_SELECT_3                      0x0565
+#define WCD934X_SLNQ_DIG_PDM_SAMPLING_FREQ                 0x0566
+#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_CTL             0x0569
+#define WCD934X_SLNQ_DIG_PDM_DC_CONVERSION_SEL             0x056a
+#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_MSB               0x056b
+#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHA_LSB               0x056c
+#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_MSB               0x056d
+#define WCD934X_SLNQ_DIG_PDM_DC_CONV_CHB_LSB               0x056e
+#define WCD934X_SLNQ_DIG_RAM_CNTRL                         0x0571
+#define WCD934X_SLNQ_DIG_SRAM_BANK                         0x0572
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_0                       0x0573
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1                       0x0574
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2                       0x0575
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3                       0x0576
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_4                       0x0577
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_5                       0x0578
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_6                       0x0579
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_7                       0x057a
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_8                       0x057b
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_9                       0x057c
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_A                       0x057d
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_B                       0x057e
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_C                       0x057f
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_D                       0x0580
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_E                       0x0581
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_F                       0x0582
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_10                      0x0583
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_11                      0x0584
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_12                      0x0585
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_13                      0x0586
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_14                      0x0587
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_15                      0x0588
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_16                      0x0589
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_17                      0x058a
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_18                      0x058b
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_19                      0x058c
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1A                      0x058d
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1B                      0x058e
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1C                      0x058f
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1D                      0x0590
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1E                      0x0591
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_1F                      0x0592
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_20                      0x0593
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_21                      0x0594
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_22                      0x0595
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_23                      0x0596
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_24                      0x0597
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_25                      0x0598
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_26                      0x0599
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_27                      0x059a
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_28                      0x059b
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_29                      0x059c
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2A                      0x059d
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2B                      0x059e
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2C                      0x059f
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2D                      0x05a0
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2E                      0x05a1
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_2F                      0x05a2
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_30                      0x05a3
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_31                      0x05a4
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_32                      0x05a5
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_33                      0x05a6
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_34                      0x05a7
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_35                      0x05a8
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_36                      0x05a9
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_37                      0x05aa
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_38                      0x05ab
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_39                      0x05ac
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3A                      0x05ad
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3B                      0x05ae
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3C                      0x05af
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3D                      0x05b0
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3E                      0x05b1
+#define WCD934X_SLNQ_DIG_SRAM_BYTE_3F                      0x05b2
+#define WCD934X_SLNQ_DIG_TOP_CTRL1                         0x05b3
+#define WCD934X_SLNQ_DIG_TOP_CTRL2                         0x05b4
+#define WCD934X_SLNQ_DIG_PDM_CTRL                          0x05b5
+#define WCD934X_SLNQ_DIG_PDM_MUTE_CTRL                     0x05b6
+#define WCD934X_SLNQ_DIG_DEC_BYPASS_CTRL                   0x05b7
+#define WCD934X_SLNQ_DIG_DEC_BYPASS_STATUS                 0x05b8
+#define WCD934X_SLNQ_DIG_DEC_BYPASS_FS                     0x05b9
+#define WCD934X_SLNQ_DIG_DEC_BYPASS_IN_SEL                 0x05ba
+#define WCD934X_SLNQ_DIG_GPOUT_ENABLE                      0x05bb
+#define WCD934X_SLNQ_DIG_GPOUT_VAL                         0x05bc
+#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_MASK                0x05be
+#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_STATUS              0x05bf
+#define WCD934X_SLNQ_DIG_ANA_INTERRUPT_CLR                 0x05c0
+#define WCD934X_SLNQ_DIG_IP_TESTING                        0x05c1
+#define WCD934X_SLNQ_DIG_INTERRUPT_CNTRL                   0x05e3
+#define WCD934X_SLNQ_DIG_INTERRUPT_CNT                     0x05e9
+#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_MSB                 0x05eb
+#define WCD934X_SLNQ_DIG_INTERRUPT_CNT_LSB                 0x05ec
+#define WCD934X_SLNQ_DIG_INTERRUPT_MASK0                   0x05f1
+#define WCD934X_SLNQ_DIG_INTERRUPT_MASK1                   0x05f2
+#define WCD934X_SLNQ_DIG_INTERRUPT_MASK2                   0x05f3
+#define WCD934X_SLNQ_DIG_INTERRUPT_MASK3                   0x05f4
+#define WCD934X_SLNQ_DIG_INTERRUPT_MASK4                   0x05f5
+#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS0                 0x05f6
+#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS1                 0x05f7
+#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS2                 0x05f8
+#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS3                 0x05f9
+#define WCD934X_SLNQ_DIG_INTERRUPT_STATUS4                 0x05fa
+#define WCD934X_SLNQ_DIG_INTERRUPT_CLR0                    0x05fb
+#define WCD934X_SLNQ_DIG_INTERRUPT_CLR1                    0x05fc
+#define WCD934X_SLNQ_DIG_INTERRUPT_CLR2                    0x05fd
+#define WCD934X_SLNQ_DIG_INTERRUPT_CLR3                    0x05fe
+#define WCD934X_SLNQ_DIG_INTERRUPT_CLR4                    0x05ff
+#define WCD934X_ANA_PAGE_REGISTER                          0x0600
+#define WCD934X_ANA_BIAS                                   0x0601
+#define WCD934X_ANA_RCO                                    0x0603
+#define WCD934X_ANA_PAGE6_SPARE2                           0x0604
+#define WCD934X_ANA_PAGE6_SPARE3                           0x0605
+#define WCD934X_ANA_BUCK_CTL                               0x0606
+#define WCD934X_ANA_BUCK_STATUS                            0x0607
+#define WCD934X_ANA_RX_SUPPLIES                            0x0608
+#define WCD934X_ANA_HPH                                    0x0609
+#define WCD934X_ANA_EAR                                    0x060a
+#define WCD934X_ANA_LO_1_2                                 0x060b
+#define WCD934X_ANA_MAD_SETUP                              0x060d
+#define WCD934X_ANA_AMIC1                                  0x060e
+#define WCD934X_ANA_AMIC2                                  0x060f
+#define WCD934X_ANA_AMIC3                                  0x0610
+#define WCD934X_ANA_AMIC4                                  0x0611
+#define WCD934X_ANA_MBHC_MECH                              0x0614
+#define WCD934X_ANA_MBHC_ELECT                             0x0615
+#define WCD934X_ANA_MBHC_ZDET                              0x0616
+#define WCD934X_ANA_MBHC_RESULT_1                          0x0617
+#define WCD934X_ANA_MBHC_RESULT_2                          0x0618
+#define WCD934X_ANA_MBHC_RESULT_3                          0x0619
+#define WCD934X_ANA_MBHC_BTN0                              0x061a
+#define WCD934X_ANA_MBHC_BTN1                              0x061b
+#define WCD934X_ANA_MBHC_BTN2                              0x061c
+#define WCD934X_ANA_MBHC_BTN3                              0x061d
+#define WCD934X_ANA_MBHC_BTN4                              0x061e
+#define WCD934X_ANA_MBHC_BTN5                              0x061f
+#define WCD934X_ANA_MBHC_BTN6                              0x0620
+#define WCD934X_ANA_MBHC_BTN7                              0x0621
+#define WCD934X_ANA_MICB1                                  0x0622
+#define WCD934X_ANA_MICB2                                  0x0623
+#define WCD934X_ANA_MICB2_RAMP                             0x0624
+#define WCD934X_ANA_MICB3                                  0x0625
+#define WCD934X_ANA_MICB4                                  0x0626
+#define WCD934X_ANA_VBADC                                  0x0627
+#define WCD934X_BIAS_CTL                                   0x0628
+#define WCD934X_BIAS_VBG_FINE_ADJ                          0x0629
+#define WCD934X_RCO_CTRL_1                                 0x062e
+#define WCD934X_RCO_CTRL_2                                 0x062f
+#define WCD934X_RCO_CAL                                    0x0630
+#define WCD934X_RCO_CAL_1                                  0x0631
+#define WCD934X_RCO_CAL_2                                  0x0632
+#define WCD934X_RCO_TEST_CTRL                              0x0633
+#define WCD934X_RCO_CAL_OUT_1                              0x0634
+#define WCD934X_RCO_CAL_OUT_2                              0x0635
+#define WCD934X_RCO_CAL_OUT_3                              0x0636
+#define WCD934X_RCO_CAL_OUT_4                              0x0637
+#define WCD934X_RCO_CAL_OUT_5                              0x0638
+#define WCD934X_SIDO_MODE_1                                0x063a
+#define WCD934X_SIDO_MODE_2                                0x063b
+#define WCD934X_SIDO_MODE_3                                0x063c
+#define WCD934X_SIDO_MODE_4                                0x063d
+#define WCD934X_SIDO_VCL_1                                 0x063e
+#define WCD934X_SIDO_VCL_2                                 0x063f
+#define WCD934X_SIDO_VCL_3                                 0x0640
+#define WCD934X_SIDO_CCL_1                                 0x0641
+#define WCD934X_SIDO_CCL_2                                 0x0642
+#define WCD934X_SIDO_CCL_3                                 0x0643
+#define WCD934X_SIDO_CCL_4                                 0x0644
+#define WCD934X_SIDO_CCL_5                                 0x0645
+#define WCD934X_SIDO_CCL_6                                 0x0646
+#define WCD934X_SIDO_CCL_7                                 0x0647
+#define WCD934X_SIDO_CCL_8                                 0x0648
+#define WCD934X_SIDO_CCL_9                                 0x0649
+#define WCD934X_SIDO_CCL_10                                0x064a
+#define WCD934X_SIDO_FILTER_1                              0x064b
+#define WCD934X_SIDO_FILTER_2                              0x064c
+#define WCD934X_SIDO_DRIVER_1                              0x064d
+#define WCD934X_SIDO_DRIVER_2                              0x064e
+#define WCD934X_SIDO_DRIVER_3                              0x064f
+#define WCD934X_SIDO_CAL_CODE_EXT_1                        0x0650
+#define WCD934X_SIDO_CAL_CODE_EXT_2                        0x0651
+#define WCD934X_SIDO_CAL_CODE_OUT_1                        0x0652
+#define WCD934X_SIDO_CAL_CODE_OUT_2                        0x0653
+#define WCD934X_SIDO_TEST_1                                0x0654
+#define WCD934X_SIDO_TEST_2                                0x0655
+#define WCD934X_MBHC_CTL_CLK                               0x0656
+#define WCD934X_MBHC_CTL_ANA                               0x0657
+#define WCD934X_MBHC_CTL_SPARE_1                           0x0658
+#define WCD934X_MBHC_CTL_SPARE_2                           0x0659
+#define WCD934X_MBHC_CTL_BCS                               0x065a
+#define WCD934X_MBHC_STATUS_SPARE_1                        0x065b
+#define WCD934X_MBHC_TEST_CTL                              0x065c
+#define WCD934X_VBADC_SUBBLOCK_EN                          0x065d
+#define WCD934X_VBADC_IBIAS_FE                             0x065e
+#define WCD934X_VBADC_BIAS_ADC                             0x065f
+#define WCD934X_VBADC_FE_CTRL                              0x0660
+#define WCD934X_VBADC_ADC_REF                              0x0661
+#define WCD934X_VBADC_ADC_IO                               0x0662
+#define WCD934X_VBADC_ADC_SAR                              0x0663
+#define WCD934X_VBADC_DEBUG                                0x0664
+#define WCD934X_LDOH_MODE                                  0x0667
+#define WCD934X_LDOH_BIAS                                  0x0668
+#define WCD934X_LDOH_STB_LOADS                             0x0669
+#define WCD934X_LDOH_SLOWRAMP                              0x066a
+#define WCD934X_MICB1_TEST_CTL_1                           0x066b
+#define WCD934X_MICB1_TEST_CTL_2                           0x066c
+#define WCD934X_MICB1_TEST_CTL_3                           0x066d
+#define WCD934X_MICB2_TEST_CTL_1                           0x066e
+#define WCD934X_MICB2_TEST_CTL_2                           0x066f
+#define WCD934X_MICB2_TEST_CTL_3                           0x0670
+#define WCD934X_MICB3_TEST_CTL_1                           0x0671
+#define WCD934X_MICB3_TEST_CTL_2                           0x0672
+#define WCD934X_MICB3_TEST_CTL_3                           0x0673
+#define WCD934X_MICB4_TEST_CTL_1                           0x0674
+#define WCD934X_MICB4_TEST_CTL_2                           0x0675
+#define WCD934X_MICB4_TEST_CTL_3                           0x0676
+#define WCD934X_TX_COM_ADC_VCM                             0x0677
+#define WCD934X_TX_COM_BIAS_ATEST                          0x0678
+#define WCD934X_TX_COM_ADC_INT1_IB                         0x0679
+#define WCD934X_TX_COM_ADC_INT2_IB                         0x067a
+#define WCD934X_TX_COM_TXFE_DIV_CTL                        0x067b
+#define WCD934X_TX_COM_TXFE_DIV_START                      0x067c
+#define WCD934X_TX_COM_TXFE_DIV_STOP_9P6M                  0x067d
+#define WCD934X_TX_COM_TXFE_DIV_STOP_12P288M               0x067e
+#define WCD934X_TX_1_2_TEST_EN                             0x067f
+#define WCD934X_TX_1_2_ADC_IB                              0x0680
+#define WCD934X_TX_1_2_ATEST_REFCTL                        0x0681
+#define WCD934X_TX_1_2_TEST_CTL                            0x0682
+#define WCD934X_TX_1_2_TEST_BLK_EN                         0x0683
+#define WCD934X_TX_1_2_TXFE_CLKDIV                         0x0684
+#define WCD934X_TX_1_2_SAR1_ERR                            0x0685
+#define WCD934X_TX_1_2_SAR2_ERR                            0x0686
+#define WCD934X_TX_3_4_TEST_EN                             0x0687
+#define WCD934X_TX_3_4_ADC_IB                              0x0688
+#define WCD934X_TX_3_4_ATEST_REFCTL                        0x0689
+#define WCD934X_TX_3_4_TEST_CTL                            0x068a
+#define WCD934X_TX_3_4_TEST_BLK_EN                         0x068b
+#define WCD934X_TX_3_4_TXFE_CLKDIV                         0x068c
+#define WCD934X_TX_3_4_SAR1_ERR                            0x068d
+#define WCD934X_TX_3_4_SAR2_ERR                            0x068e
+#define WCD934X_CLASSH_MODE_1                              0x0697
+#define WCD934X_CLASSH_MODE_2                              0x0698
+#define WCD934X_CLASSH_MODE_3                              0x0699
+#define WCD934X_CLASSH_CTRL_VCL_1                          0x069a
+#define WCD934X_CLASSH_CTRL_VCL_2                          0x069b
+#define WCD934X_CLASSH_CTRL_CCL_1                          0x069c
+#define WCD934X_CLASSH_CTRL_CCL_2                          0x069d
+#define WCD934X_CLASSH_CTRL_CCL_3                          0x069e
+#define WCD934X_CLASSH_CTRL_CCL_4                          0x069f
+#define WCD934X_CLASSH_CTRL_CCL_5                          0x06a0
+#define WCD934X_CLASSH_BUCK_TMUX_A_D                       0x06a1
+#define WCD934X_CLASSH_BUCK_SW_DRV_CNTL                    0x06a2
+#define WCD934X_CLASSH_SPARE                               0x06a3
+#define WCD934X_FLYBACK_EN                                 0x06a4
+#define WCD934X_FLYBACK_VNEG_CTRL_1                        0x06a5
+#define WCD934X_FLYBACK_VNEG_CTRL_2                        0x06a6
+#define WCD934X_FLYBACK_VNEG_CTRL_3                        0x06a7
+#define WCD934X_FLYBACK_VNEG_CTRL_4                        0x06a8
+#define WCD934X_FLYBACK_VNEG_CTRL_5                        0x06a9
+#define WCD934X_FLYBACK_VNEG_CTRL_6                        0x06aa
+#define WCD934X_FLYBACK_VNEG_CTRL_7                        0x06ab
+#define WCD934X_FLYBACK_VNEG_CTRL_8                        0x06ac
+#define WCD934X_FLYBACK_VNEG_CTRL_9                        0x06ad
+#define WCD934X_FLYBACK_VNEGDAC_CTRL_1                     0x06ae
+#define WCD934X_FLYBACK_VNEGDAC_CTRL_2                     0x06af
+#define WCD934X_FLYBACK_VNEGDAC_CTRL_3                     0x06b0
+#define WCD934X_FLYBACK_CTRL_1                             0x06b1
+#define WCD934X_FLYBACK_TEST_CTL                           0x06b2
+#define WCD934X_RX_AUX_SW_CTL                              0x06b3
+#define WCD934X_RX_PA_AUX_IN_CONN                          0x06b4
+#define WCD934X_RX_TIMER_DIV                               0x06b5
+#define WCD934X_RX_OCP_CTL                                 0x06b6
+#define WCD934X_RX_OCP_COUNT                               0x06b7
+#define WCD934X_RX_BIAS_EAR_DAC                            0x06b8
+#define WCD934X_RX_BIAS_EAR_AMP                            0x06b9
+#define WCD934X_RX_BIAS_HPH_LDO                            0x06ba
+#define WCD934X_RX_BIAS_HPH_PA                             0x06bb
+#define WCD934X_RX_BIAS_HPH_RDACBUFF_CNP2                  0x06bc
+#define WCD934X_RX_BIAS_HPH_RDAC_LDO                       0x06bd
+#define WCD934X_RX_BIAS_HPH_CNP1                           0x06be
+#define WCD934X_RX_BIAS_HPH_LOWPOWER                       0x06bf
+#define WCD934X_RX_BIAS_DIFFLO_PA                          0x06c0
+#define WCD934X_RX_BIAS_DIFFLO_REF                         0x06c1
+#define WCD934X_RX_BIAS_DIFFLO_LDO                         0x06c2
+#define WCD934X_RX_BIAS_SELO_DAC_PA                        0x06c3
+#define WCD934X_RX_BIAS_BUCK_RST                           0x06c4
+#define WCD934X_RX_BIAS_BUCK_VREF_ERRAMP                   0x06c5
+#define WCD934X_RX_BIAS_FLYB_ERRAMP                        0x06c6
+#define WCD934X_RX_BIAS_FLYB_BUFF                          0x06c7
+#define WCD934X_RX_BIAS_FLYB_MID_RST                       0x06c8
+#define WCD934X_HPH_L_STATUS                               0x06c9
+#define WCD934X_HPH_R_STATUS                               0x06ca
+#define WCD934X_HPH_CNP_EN                                 0x06cb
+#define WCD934X_HPH_CNP_WG_CTL                             0x06cc
+#define WCD934X_HPH_CNP_WG_TIME                            0x06cd
+#define WCD934X_HPH_OCP_CTL                                0x06ce
+#define WCD934X_HPH_AUTO_CHOP                              0x06cf
+#define WCD934X_HPH_CHOP_CTL                               0x06d0
+#define WCD934X_HPH_PA_CTL1                                0x06d1
+#define WCD934X_HPH_PA_CTL2                                0x06d2
+#define WCD934X_HPH_L_EN                                   0x06d3
+#define WCD934X_HPH_L_TEST                                 0x06d4
+#define WCD934X_HPH_L_ATEST                                0x06d5
+#define WCD934X_HPH_R_EN                                   0x06d6
+#define WCD934X_HPH_R_TEST                                 0x06d7
+#define WCD934X_HPH_R_ATEST                                0x06d8
+#define WCD934X_HPH_RDAC_CLK_CTL1                          0x06d9
+#define WCD934X_HPH_RDAC_CLK_CTL2                          0x06da
+#define WCD934X_HPH_RDAC_LDO_CTL                           0x06db
+#define WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL                   0x06dc
+#define WCD934X_HPH_REFBUFF_UHQA_CTL                       0x06dd
+#define WCD934X_HPH_REFBUFF_LP_CTL                         0x06de
+#define WCD934X_HPH_L_DAC_CTL                              0x06df
+#define WCD934X_HPH_R_DAC_CTL                              0x06e0
+#define WCD934X_EAR_EN_REG                                 0x06e1
+#define WCD934X_EAR_CMBUFF                                 0x06e2
+#define WCD934X_EAR_ICTL                                   0x06e3
+#define WCD934X_EAR_EN_DBG_CTL                             0x06e4
+#define WCD934X_EAR_CNP                                    0x06e5
+#define WCD934X_EAR_DAC_CTL_ATEST                          0x06e6
+#define WCD934X_EAR_STATUS_REG                             0x06e7
+#define WCD934X_EAR_EAR_MISC                               0x06e8
+#define WCD934X_DIFF_LO_MISC                               0x06e9
+#define WCD934X_DIFF_LO_LO2_COMPANDER                      0x06ea
+#define WCD934X_DIFF_LO_LO1_COMPANDER                      0x06eb
+#define WCD934X_DIFF_LO_COMMON                             0x06ec
+#define WCD934X_DIFF_LO_BYPASS_EN                          0x06ed
+#define WCD934X_DIFF_LO_CNP                                0x06ee
+#define WCD934X_DIFF_LO_CORE_OUT_PROG                      0x06ef
+#define WCD934X_DIFF_LO_LDO_OUT_PROG                       0x06f0
+#define WCD934X_DIFF_LO_COM_SWCAP_REFBUF_FREQ              0x06f1
+#define WCD934X_DIFF_LO_COM_PA_FREQ                        0x06f2
+#define WCD934X_DIFF_LO_RESERVED_REG                       0x06f3
+#define WCD934X_DIFF_LO_LO1_STATUS_1                       0x06f4
+#define WCD934X_DIFF_LO_LO1_STATUS_2                       0x06f5
+#define WCD934X_ANA_NEW_PAGE_REGISTER                      0x0700
+#define WCD934X_HPH_NEW_ANA_HPH2                           0x0701
+#define WCD934X_HPH_NEW_ANA_HPH3                           0x0702
+#define WCD934X_SLNQ_ANA_EN                                0x0703
+#define WCD934X_SLNQ_ANA_STATUS                            0x0704
+#define WCD934X_SLNQ_ANA_LDO_CONFIG                        0x0705
+#define WCD934X_SLNQ_ANA_LDO_OCP_CONFIG                    0x0706
+#define WCD934X_SLNQ_ANA_TX_LDO_CONFIG                     0x0707
+#define WCD934X_SLNQ_ANA_TX_DRV_CONFIG                     0x0708
+#define WCD934X_SLNQ_ANA_RX_CONFIG_1                       0x0709
+#define WCD934X_SLNQ_ANA_RX_CONFIG_2                       0x070a
+#define WCD934X_SLNQ_ANA_PLL_ENABLES                       0x070b
+#define WCD934X_SLNQ_ANA_PLL_PRESET                        0x070c
+#define WCD934X_SLNQ_ANA_PLL_STATUS                        0x070d
+#define WCD934X_CLK_SYS_PLL_ENABLES                        0x070e
+#define WCD934X_CLK_SYS_PLL_PRESET                         0x070f
+#define WCD934X_CLK_SYS_PLL_STATUS                         0x0710
+#define WCD934X_CLK_SYS_MCLK_PRG                           0x0711
+#define WCD934X_CLK_SYS_MCLK2_PRG1                         0x0712
+#define WCD934X_CLK_SYS_MCLK2_PRG2                         0x0713
+#define WCD934X_CLK_SYS_XO_PRG                             0x0714
+#define WCD934X_CLK_SYS_XO_CAP_XTP                         0x0715
+#define WCD934X_CLK_SYS_XO_CAP_XTM                         0x0716
+#define WCD934X_BOOST_BST_EN_DLY                           0x0718
+#define WCD934X_BOOST_CTRL_ILIM                            0x0719
+#define WCD934X_BOOST_VOUT_SETTING                         0x071a
+#define WCD934X_SIDO_NEW_VOUT_A_STARTUP                    0x071b
+#define WCD934X_SIDO_NEW_VOUT_D_STARTUP                    0x071c
+#define WCD934X_SIDO_NEW_VOUT_D_FREQ1                      0x071d
+#define WCD934X_SIDO_NEW_VOUT_D_FREQ2                      0x071e
+#define WCD934X_MBHC_NEW_ELECT_REM_CLAMP_CTL               0x071f
+#define WCD934X_MBHC_NEW_CTL_1                             0x0720
+#define WCD934X_MBHC_NEW_CTL_2                             0x0721
+#define WCD934X_MBHC_NEW_PLUG_DETECT_CTL                   0x0722
+#define WCD934X_MBHC_NEW_ZDET_ANA_CTL                      0x0723
+#define WCD934X_MBHC_NEW_ZDET_RAMP_CTL                     0x0724
+#define WCD934X_MBHC_NEW_FSM_STATUS                        0x0725
+#define WCD934X_MBHC_NEW_ADC_RESULT                        0x0726
+#define WCD934X_TX_NEW_AMIC_4_5_SEL                        0x0727
+#define WCD934X_VBADC_NEW_ADC_MODE                         0x072f
+#define WCD934X_VBADC_NEW_ADC_DOUTMSB                      0x0730
+#define WCD934X_VBADC_NEW_ADC_DOUTLSB                      0x0731
+#define WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL                  0x0732
+#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL                   0x0733
+#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L                 0x0733
+#define WCD934X_HPH_NEW_INT_RDAC_VREF_CTL                  0x0734
+#define WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL              0x0735
+#define WCD934X_HPH_NEW_INT_RDAC_MISC1                     0x0736
+#define WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R                 0x0736
+#define WCD934X_HPH_NEW_INT_PA_MISC1                       0x0737
+#define WCD934X_HPH_NEW_INT_PA_MISC2                       0x0738
+#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC                   0x0739
+#define WCD934X_HPH_NEW_INT_HPH_TIMER1                     0x073a
+#define WCD934X_HPH_NEW_INT_HPH_TIMER2                     0x073b
+#define WCD934X_HPH_NEW_INT_HPH_TIMER3                     0x073c
+#define WCD934X_HPH_NEW_INT_HPH_TIMER4                     0x073d
+#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC2                  0x073e
+#define WCD934X_HPH_NEW_INT_PA_RDAC_MISC3                  0x073f
+#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_LOHIFI            0x0745
+#define WCD934X_RX_NEW_INT_HPH_RDAC_BIAS_ULP               0x0746
+#define WCD934X_RX_NEW_INT_HPH_RDAC_LDO_LP                 0x0747
+#define WCD934X_SLNQ_INT_ANA_INT_LDO_TEST                  0x074b
+#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_1               0x074c
+#define WCD934X_SLNQ_INT_ANA_INT_LDO_DEBUG_2               0x074d
+#define WCD934X_SLNQ_INT_ANA_INT_TX_LDO_TEST               0x074e
+#define WCD934X_SLNQ_INT_ANA_INT_TX_DRV_TEST               0x074f
+#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST                   0x0750
+#define WCD934X_SLNQ_INT_ANA_INT_RX_TEST_STATUS            0x0751
+#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_1                0x0752
+#define WCD934X_SLNQ_INT_ANA_INT_RX_DEBUG_2                0x0753
+#define WCD934X_SLNQ_INT_ANA_INT_CLK_CTRL                  0x0754
+#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_1                0x0755
+#define WCD934X_SLNQ_INT_ANA_INT_RESERVED_2                0x0756
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG0         0x0757
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_POST_DIV_REG1         0x0758
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG0          0x0759
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_REF_DIV_REG1          0x075a
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG0           0x075b
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_FILTER_REG1           0x075c
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_L_VAL                 0x075d
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_M_VAL                 0x075e
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_N_VAL                 0x075f
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG0             0x0760
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_PFD_CP_DSM_PROG       0x0761
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_VCO_PROG              0x0762
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_TEST_REG1             0x0763
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_LDO_LOCK_CFG          0x0764
+#define WCD934X_SLNQ_INT_ANA_INT_PLL_DIG_LOCK_DET_CFG      0x0765
+#define WCD934X_CLK_SYS_INT_POST_DIV_REG0                  0x076c
+#define WCD934X_CLK_SYS_INT_POST_DIV_REG1                  0x076d
+#define WCD934X_CLK_SYS_INT_REF_DIV_REG0                   0x076e
+#define WCD934X_CLK_SYS_INT_REF_DIV_REG1                   0x076f
+#define WCD934X_CLK_SYS_INT_FILTER_REG0                    0x0770
+#define WCD934X_CLK_SYS_INT_FILTER_REG1                    0x0771
+#define WCD934X_CLK_SYS_INT_PLL_L_VAL                      0x0772
+#define WCD934X_CLK_SYS_INT_PLL_M_VAL                      0x0773
+#define WCD934X_CLK_SYS_INT_PLL_N_VAL                      0x0774
+#define WCD934X_CLK_SYS_INT_TEST_REG0                      0x0775
+#define WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG                0x0776
+#define WCD934X_CLK_SYS_INT_VCO_PROG                       0x0777
+#define WCD934X_CLK_SYS_INT_TEST_REG1                      0x0778
+#define WCD934X_CLK_SYS_INT_LDO_LOCK_CFG                   0x0779
+#define WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG               0x077a
+#define WCD934X_CLK_SYS_INT_CLK_TEST1                      0x077b
+#define WCD934X_CLK_SYS_INT_CLK_TEST2                      0x077c
+#define WCD934X_CLK_SYS_INT_CLK_TEST3                      0x077d
+#define WCD934X_CLK_SYS_INT_XO_TEST1                       0x077e
+#define WCD934X_CLK_SYS_INT_XO_TEST2                       0x077f
+#define WCD934X_BOOST_INT_VCOMP_HYST                       0x0787
+#define WCD934X_BOOST_INT_VLOOP_FILTER                     0x0788
+#define WCD934X_BOOST_INT_CTRL_IDELTA                      0x0789
+#define WCD934X_BOOST_INT_CTRL_ILIM_STARTUP                0x078a
+#define WCD934X_BOOST_INT_CTRL_MIN_ONTIME                  0x078b
+#define WCD934X_BOOST_INT_CTRL_MAX_ONTIME                  0x078c
+#define WCD934X_BOOST_INT_CTRL_TIMING                      0x078d
+#define WCD934X_BOOST_INT_TMUX_A_D                         0x078e
+#define WCD934X_BOOST_INT_SW_DRV_CNTL                      0x078f
+#define WCD934X_BOOST_INT_SPARE1                           0x0790
+#define WCD934X_BOOST_INT_SPARE2                           0x0791
+#define WCD934X_SIDO_NEW_INT_RAMP_STATUS                   0x0796
+#define WCD934X_SIDO_NEW_INT_SPARE_1                       0x0797
+#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_A          0x0798
+#define WCD934X_SIDO_NEW_INT_DEBUG_VOUT_SETTING_D          0x0799
+#define WCD934X_SIDO_NEW_INT_RAMP_INC_WAIT                 0x079a
+#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_CTL             0x079b
+#define WCD934X_SIDO_NEW_INT_RAMP_IBLEED_CTL               0x079c
+#define WCD934X_SIDO_NEW_INT_DEBUG_CPROVR_TEST             0x079d
+#define WCD934X_SIDO_NEW_INT_RAMP_CTL_A                    0x079e
+#define WCD934X_SIDO_NEW_INT_RAMP_CTL_D                    0x079f
+#define WCD934X_SIDO_NEW_INT_RAMP_TIMEOUT_PERIOD           0x07a0
+#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING1        0x07a1
+#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING2        0x07a2
+#define WCD934X_SIDO_NEW_INT_DYNAMIC_IPEAK_SETTING3        0x07a3
+#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL1           0x07a4
+#define WCD934X_SIDO_NEW_INT_HIGH_ACCU_MODE_SEL2           0x07a5
+#define WCD934X_MBHC_NEW_INT_SLNQ_HPF                      0x07af
+#define WCD934X_MBHC_NEW_INT_SLNQ_REF                      0x07b0
+#define WCD934X_MBHC_NEW_INT_SLNQ_COMP                     0x07b1
+#define WCD934X_MBHC_NEW_INT_SPARE_2                       0x07b2
+#define WCD934X_PAGE10_PAGE_REGISTER                       0x0a00
+#define WCD934X_CDC_ANC0_CLK_RESET_CTL                     0x0a01
+#define WCD934X_CDC_ANC0_MODE_1_CTL                        0x0a02
+#define WCD934X_CDC_ANC0_MODE_2_CTL                        0x0a03
+#define WCD934X_CDC_ANC0_FF_SHIFT                          0x0a04
+#define WCD934X_CDC_ANC0_FB_SHIFT                          0x0a05
+#define WCD934X_CDC_ANC0_LPF_FF_A_CTL                      0x0a06
+#define WCD934X_CDC_ANC0_LPF_FF_B_CTL                      0x0a07
+#define WCD934X_CDC_ANC0_LPF_FB_CTL                        0x0a08
+#define WCD934X_CDC_ANC0_SMLPF_CTL                         0x0a09
+#define WCD934X_CDC_ANC0_DCFLT_SHIFT_CTL                   0x0a0a
+#define WCD934X_CDC_ANC0_IIR_ADAPT_CTL                     0x0a0b
+#define WCD934X_CDC_ANC0_IIR_COEFF_1_CTL                   0x0a0c
+#define WCD934X_CDC_ANC0_IIR_COEFF_2_CTL                   0x0a0d
+#define WCD934X_CDC_ANC0_FF_A_GAIN_CTL                     0x0a0e
+#define WCD934X_CDC_ANC0_FF_B_GAIN_CTL                     0x0a0f
+#define WCD934X_CDC_ANC0_FB_GAIN_CTL                       0x0a10
+#define WCD934X_CDC_ANC0_RC_COMMON_CTL                     0x0a11
+#define WCD934X_CDC_ANC0_FIFO_COMMON_CTL                   0x0a13
+#define WCD934X_CDC_ANC0_RC0_STATUS_FMIN_CNTR              0x0a14
+#define WCD934X_CDC_ANC0_RC1_STATUS_FMIN_CNTR              0x0a15
+#define WCD934X_CDC_ANC0_RC0_STATUS_FMAX_CNTR              0x0a16
+#define WCD934X_CDC_ANC0_RC1_STATUS_FMAX_CNTR              0x0a17
+#define WCD934X_CDC_ANC0_STATUS_FIFO                       0x0a18
+#define WCD934X_CDC_ANC1_CLK_RESET_CTL                     0x0a19
+#define WCD934X_CDC_ANC1_MODE_1_CTL                        0x0a1a
+#define WCD934X_CDC_ANC1_MODE_2_CTL                        0x0a1b
+#define WCD934X_CDC_ANC1_FF_SHIFT                          0x0a1c
+#define WCD934X_CDC_ANC1_FB_SHIFT                          0x0a1d
+#define WCD934X_CDC_ANC1_LPF_FF_A_CTL                      0x0a1e
+#define WCD934X_CDC_ANC1_LPF_FF_B_CTL                      0x0a1f
+#define WCD934X_CDC_ANC1_LPF_FB_CTL                        0x0a20
+#define WCD934X_CDC_ANC1_SMLPF_CTL                         0x0a21
+#define WCD934X_CDC_ANC1_DCFLT_SHIFT_CTL                   0x0a22
+#define WCD934X_CDC_ANC1_IIR_ADAPT_CTL                     0x0a23
+#define WCD934X_CDC_ANC1_IIR_COEFF_1_CTL                   0x0a24
+#define WCD934X_CDC_ANC1_IIR_COEFF_2_CTL                   0x0a25
+#define WCD934X_CDC_ANC1_FF_A_GAIN_CTL                     0x0a26
+#define WCD934X_CDC_ANC1_FF_B_GAIN_CTL                     0x0a27
+#define WCD934X_CDC_ANC1_FB_GAIN_CTL                       0x0a28
+#define WCD934X_CDC_ANC1_RC_COMMON_CTL                     0x0a29
+#define WCD934X_CDC_ANC1_FIFO_COMMON_CTL                   0x0a2b
+#define WCD934X_CDC_ANC1_RC0_STATUS_FMIN_CNTR              0x0a2c
+#define WCD934X_CDC_ANC1_RC1_STATUS_FMIN_CNTR              0x0a2d
+#define WCD934X_CDC_ANC1_RC0_STATUS_FMAX_CNTR              0x0a2e
+#define WCD934X_CDC_ANC1_RC1_STATUS_FMAX_CNTR              0x0a2f
+#define WCD934X_CDC_ANC1_STATUS_FIFO                       0x0a30
+#define WCD934X_CDC_TX0_TX_PATH_CTL                        0x0a31
+#define WCD934X_CDC_TX0_TX_PATH_CFG0                       0x0a32
+#define WCD934X_CDC_TX0_TX_PATH_CFG1                       0x0a33
+#define WCD934X_CDC_TX0_TX_VOL_CTL                         0x0a34
+#define WCD934X_CDC_TX0_TX_PATH_192_CTL                    0x0a35
+#define WCD934X_CDC_TX0_TX_PATH_192_CFG                    0x0a36
+#define WCD934X_CDC_TX0_TX_PATH_SEC0                       0x0a37
+#define WCD934X_CDC_TX0_TX_PATH_SEC1                       0x0a38
+#define WCD934X_CDC_TX0_TX_PATH_SEC2                       0x0a39
+#define WCD934X_CDC_TX0_TX_PATH_SEC3                       0x0a3a
+#define WCD934X_CDC_TX0_TX_PATH_SEC4                       0x0a3b
+#define WCD934X_CDC_TX0_TX_PATH_SEC5                       0x0a3c
+#define WCD934X_CDC_TX0_TX_PATH_SEC6                       0x0a3d
+#define WCD934X_CDC_TX0_TX_PATH_SEC7                       0x0a3e
+#define WCD934X_CDC_TX1_TX_PATH_CTL                        0x0a41
+#define WCD934X_CDC_TX1_TX_PATH_CFG0                       0x0a42
+#define WCD934X_CDC_TX1_TX_PATH_CFG1                       0x0a43
+#define WCD934X_CDC_TX1_TX_VOL_CTL                         0x0a44
+#define WCD934X_CDC_TX1_TX_PATH_192_CTL                    0x0a45
+#define WCD934X_CDC_TX1_TX_PATH_192_CFG                    0x0a46
+#define WCD934X_CDC_TX1_TX_PATH_SEC0                       0x0a47
+#define WCD934X_CDC_TX1_TX_PATH_SEC1                       0x0a48
+#define WCD934X_CDC_TX1_TX_PATH_SEC2                       0x0a49
+#define WCD934X_CDC_TX1_TX_PATH_SEC3                       0x0a4a
+#define WCD934X_CDC_TX1_TX_PATH_SEC4                       0x0a4b
+#define WCD934X_CDC_TX1_TX_PATH_SEC5                       0x0a4c
+#define WCD934X_CDC_TX1_TX_PATH_SEC6                       0x0a4d
+#define WCD934X_CDC_TX2_TX_PATH_CTL                        0x0a51
+#define WCD934X_CDC_TX2_TX_PATH_CFG0                       0x0a52
+#define WCD934X_CDC_TX2_TX_PATH_CFG1                       0x0a53
+#define WCD934X_CDC_TX2_TX_VOL_CTL                         0x0a54
+#define WCD934X_CDC_TX2_TX_PATH_192_CTL                    0x0a55
+#define WCD934X_CDC_TX2_TX_PATH_192_CFG                    0x0a56
+#define WCD934X_CDC_TX2_TX_PATH_SEC0                       0x0a57
+#define WCD934X_CDC_TX2_TX_PATH_SEC1                       0x0a58
+#define WCD934X_CDC_TX2_TX_PATH_SEC2                       0x0a59
+#define WCD934X_CDC_TX2_TX_PATH_SEC3                       0x0a5a
+#define WCD934X_CDC_TX2_TX_PATH_SEC4                       0x0a5b
+#define WCD934X_CDC_TX2_TX_PATH_SEC5                       0x0a5c
+#define WCD934X_CDC_TX2_TX_PATH_SEC6                       0x0a5d
+#define WCD934X_CDC_TX3_TX_PATH_CTL                        0x0a61
+#define WCD934X_CDC_TX3_TX_PATH_CFG0                       0x0a62
+#define WCD934X_CDC_TX3_TX_PATH_CFG1                       0x0a63
+#define WCD934X_CDC_TX3_TX_VOL_CTL                         0x0a64
+#define WCD934X_CDC_TX3_TX_PATH_192_CTL                    0x0a65
+#define WCD934X_CDC_TX3_TX_PATH_192_CFG                    0x0a66
+#define WCD934X_CDC_TX3_TX_PATH_SEC0                       0x0a67
+#define WCD934X_CDC_TX3_TX_PATH_SEC1                       0x0a68
+#define WCD934X_CDC_TX3_TX_PATH_SEC2                       0x0a69
+#define WCD934X_CDC_TX3_TX_PATH_SEC3                       0x0a6a
+#define WCD934X_CDC_TX3_TX_PATH_SEC4                       0x0a6b
+#define WCD934X_CDC_TX3_TX_PATH_SEC5                       0x0a6c
+#define WCD934X_CDC_TX3_TX_PATH_SEC6                       0x0a6d
+#define WCD934X_CDC_TX4_TX_PATH_CTL                        0x0a71
+#define WCD934X_CDC_TX4_TX_PATH_CFG0                       0x0a72
+#define WCD934X_CDC_TX4_TX_PATH_CFG1                       0x0a73
+#define WCD934X_CDC_TX4_TX_VOL_CTL                         0x0a74
+#define WCD934X_CDC_TX4_TX_PATH_192_CTL                    0x0a75
+#define WCD934X_CDC_TX4_TX_PATH_192_CFG                    0x0a76
+#define WCD934X_CDC_TX4_TX_PATH_SEC0                       0x0a77
+#define WCD934X_CDC_TX4_TX_PATH_SEC1                       0x0a78
+#define WCD934X_CDC_TX4_TX_PATH_SEC2                       0x0a79
+#define WCD934X_CDC_TX4_TX_PATH_SEC3                       0x0a7a
+#define WCD934X_CDC_TX4_TX_PATH_SEC4                       0x0a7b
+#define WCD934X_CDC_TX4_TX_PATH_SEC5                       0x0a7c
+#define WCD934X_CDC_TX4_TX_PATH_SEC6                       0x0a7d
+#define WCD934X_CDC_TX5_TX_PATH_CTL                        0x0a81
+#define WCD934X_CDC_TX5_TX_PATH_CFG0                       0x0a82
+#define WCD934X_CDC_TX5_TX_PATH_CFG1                       0x0a83
+#define WCD934X_CDC_TX5_TX_VOL_CTL                         0x0a84
+#define WCD934X_CDC_TX5_TX_PATH_192_CTL                    0x0a85
+#define WCD934X_CDC_TX5_TX_PATH_192_CFG                    0x0a86
+#define WCD934X_CDC_TX5_TX_PATH_SEC0                       0x0a87
+#define WCD934X_CDC_TX5_TX_PATH_SEC1                       0x0a88
+#define WCD934X_CDC_TX5_TX_PATH_SEC2                       0x0a89
+#define WCD934X_CDC_TX5_TX_PATH_SEC3                       0x0a8a
+#define WCD934X_CDC_TX5_TX_PATH_SEC4                       0x0a8b
+#define WCD934X_CDC_TX5_TX_PATH_SEC5                       0x0a8c
+#define WCD934X_CDC_TX5_TX_PATH_SEC6                       0x0a8d
+#define WCD934X_CDC_TX6_TX_PATH_CTL                        0x0a91
+#define WCD934X_CDC_TX6_TX_PATH_CFG0                       0x0a92
+#define WCD934X_CDC_TX6_TX_PATH_CFG1                       0x0a93
+#define WCD934X_CDC_TX6_TX_VOL_CTL                         0x0a94
+#define WCD934X_CDC_TX6_TX_PATH_192_CTL                    0x0a95
+#define WCD934X_CDC_TX6_TX_PATH_192_CFG                    0x0a96
+#define WCD934X_CDC_TX6_TX_PATH_SEC0                       0x0a97
+#define WCD934X_CDC_TX6_TX_PATH_SEC1                       0x0a98
+#define WCD934X_CDC_TX6_TX_PATH_SEC2                       0x0a99
+#define WCD934X_CDC_TX6_TX_PATH_SEC3                       0x0a9a
+#define WCD934X_CDC_TX6_TX_PATH_SEC4                       0x0a9b
+#define WCD934X_CDC_TX6_TX_PATH_SEC5                       0x0a9c
+#define WCD934X_CDC_TX6_TX_PATH_SEC6                       0x0a9d
+#define WCD934X_CDC_TX7_TX_PATH_CTL                        0x0aa1
+#define WCD934X_CDC_TX7_TX_PATH_CFG0                       0x0aa2
+#define WCD934X_CDC_TX7_TX_PATH_CFG1                       0x0aa3
+#define WCD934X_CDC_TX7_TX_VOL_CTL                         0x0aa4
+#define WCD934X_CDC_TX7_TX_PATH_192_CTL                    0x0aa5
+#define WCD934X_CDC_TX7_TX_PATH_192_CFG                    0x0aa6
+#define WCD934X_CDC_TX7_TX_PATH_SEC0                       0x0aa7
+#define WCD934X_CDC_TX7_TX_PATH_SEC1                       0x0aa8
+#define WCD934X_CDC_TX7_TX_PATH_SEC2                       0x0aa9
+#define WCD934X_CDC_TX7_TX_PATH_SEC3                       0x0aaa
+#define WCD934X_CDC_TX7_TX_PATH_SEC4                       0x0aab
+#define WCD934X_CDC_TX7_TX_PATH_SEC5                       0x0aac
+#define WCD934X_CDC_TX7_TX_PATH_SEC6                       0x0aad
+#define WCD934X_CDC_TX8_TX_PATH_CTL                        0x0ab1
+#define WCD934X_CDC_TX8_TX_PATH_CFG0                       0x0ab2
+#define WCD934X_CDC_TX8_TX_PATH_CFG1                       0x0ab3
+#define WCD934X_CDC_TX8_TX_VOL_CTL                         0x0ab4
+#define WCD934X_CDC_TX8_TX_PATH_192_CTL                    0x0ab5
+#define WCD934X_CDC_TX8_TX_PATH_192_CFG                    0x0ab6
+#define WCD934X_CDC_TX8_TX_PATH_SEC0                       0x0ab7
+#define WCD934X_CDC_TX8_TX_PATH_SEC1                       0x0ab8
+#define WCD934X_CDC_TX8_TX_PATH_SEC2                       0x0ab9
+#define WCD934X_CDC_TX8_TX_PATH_SEC3                       0x0aba
+#define WCD934X_CDC_TX8_TX_PATH_SEC4                       0x0abb
+#define WCD934X_CDC_TX8_TX_PATH_SEC5                       0x0abc
+#define WCD934X_CDC_TX8_TX_PATH_SEC6                       0x0abd
+#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL                 0x0ac2
+#define WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0                0x0ac3
+#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL                0x0ac6
+#define WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0               0x0ac7
+#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL                0x0aca
+#define WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0               0x0acb
+#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL                0x0ace
+#define WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0               0x0acf
+#define WCD934X_PAGE11_PAGE_REGISTER                       0x0b00
+#define WCD934X_CDC_COMPANDER1_CTL0                        0x0b01
+#define WCD934X_CDC_COMPANDER1_CTL1                        0x0b02
+#define WCD934X_CDC_COMPANDER1_CTL2                        0x0b03
+#define WCD934X_CDC_COMPANDER1_CTL3                        0x0b04
+#define WCD934X_CDC_COMPANDER1_CTL4                        0x0b05
+#define WCD934X_CDC_COMPANDER1_CTL5                        0x0b06
+#define WCD934X_CDC_COMPANDER1_CTL6                        0x0b07
+#define WCD934X_CDC_COMPANDER1_CTL7                        0x0b08
+#define WCD934X_CDC_COMPANDER2_CTL0                        0x0b09
+#define WCD934X_CDC_COMPANDER2_CTL1                        0x0b0a
+#define WCD934X_CDC_COMPANDER2_CTL2                        0x0b0b
+#define WCD934X_CDC_COMPANDER2_CTL3                        0x0b0c
+#define WCD934X_CDC_COMPANDER2_CTL4                        0x0b0d
+#define WCD934X_CDC_COMPANDER2_CTL5                        0x0b0e
+#define WCD934X_CDC_COMPANDER2_CTL6                        0x0b0f
+#define WCD934X_CDC_COMPANDER2_CTL7                        0x0b10
+#define WCD934X_CDC_COMPANDER3_CTL0                        0x0b11
+#define WCD934X_CDC_COMPANDER3_CTL1                        0x0b12
+#define WCD934X_CDC_COMPANDER3_CTL2                        0x0b13
+#define WCD934X_CDC_COMPANDER3_CTL3                        0x0b14
+#define WCD934X_CDC_COMPANDER3_CTL4                        0x0b15
+#define WCD934X_CDC_COMPANDER3_CTL5                        0x0b16
+#define WCD934X_CDC_COMPANDER3_CTL6                        0x0b17
+#define WCD934X_CDC_COMPANDER3_CTL7                        0x0b18
+#define WCD934X_CDC_COMPANDER4_CTL0                        0x0b19
+#define WCD934X_CDC_COMPANDER4_CTL1                        0x0b1a
+#define WCD934X_CDC_COMPANDER4_CTL2                        0x0b1b
+#define WCD934X_CDC_COMPANDER4_CTL3                        0x0b1c
+#define WCD934X_CDC_COMPANDER4_CTL4                        0x0b1d
+#define WCD934X_CDC_COMPANDER4_CTL5                        0x0b1e
+#define WCD934X_CDC_COMPANDER4_CTL6                        0x0b1f
+#define WCD934X_CDC_COMPANDER4_CTL7                        0x0b20
+#define WCD934X_CDC_COMPANDER7_CTL0                        0x0b31
+#define WCD934X_CDC_COMPANDER7_CTL1                        0x0b32
+#define WCD934X_CDC_COMPANDER7_CTL2                        0x0b33
+#define WCD934X_CDC_COMPANDER7_CTL3                        0x0b34
+#define WCD934X_CDC_COMPANDER7_CTL4                        0x0b35
+#define WCD934X_CDC_COMPANDER7_CTL5                        0x0b36
+#define WCD934X_CDC_COMPANDER7_CTL6                        0x0b37
+#define WCD934X_CDC_COMPANDER7_CTL7                        0x0b38
+#define WCD934X_CDC_COMPANDER8_CTL0                        0x0b39
+#define WCD934X_CDC_COMPANDER8_CTL1                        0x0b3a
+#define WCD934X_CDC_COMPANDER8_CTL2                        0x0b3b
+#define WCD934X_CDC_COMPANDER8_CTL3                        0x0b3c
+#define WCD934X_CDC_COMPANDER8_CTL4                        0x0b3d
+#define WCD934X_CDC_COMPANDER8_CTL5                        0x0b3e
+#define WCD934X_CDC_COMPANDER8_CTL6                        0x0b3f
+#define WCD934X_CDC_COMPANDER8_CTL7                        0x0b40
+#define WCD934X_CDC_RX0_RX_PATH_CTL                        0x0b41
+#define WCD934X_CDC_RX0_RX_PATH_CFG0                       0x0b42
+#define WCD934X_CDC_RX0_RX_PATH_CFG1                       0x0b43
+#define WCD934X_CDC_RX0_RX_PATH_CFG2                       0x0b44
+#define WCD934X_CDC_RX0_RX_VOL_CTL                         0x0b45
+#define WCD934X_CDC_RX0_RX_PATH_MIX_CTL                    0x0b46
+#define WCD934X_CDC_RX0_RX_PATH_MIX_CFG                    0x0b47
+#define WCD934X_CDC_RX0_RX_VOL_MIX_CTL                     0x0b48
+#define WCD934X_CDC_RX0_RX_PATH_SEC0                       0x0b49
+#define WCD934X_CDC_RX0_RX_PATH_SEC1                       0x0b4a
+#define WCD934X_CDC_RX0_RX_PATH_SEC2                       0x0b4b
+#define WCD934X_CDC_RX0_RX_PATH_SEC3                       0x0b4c
+#define WCD934X_CDC_RX0_RX_PATH_SEC5                       0x0b4e
+#define WCD934X_CDC_RX0_RX_PATH_SEC6                       0x0b4f
+#define WCD934X_CDC_RX0_RX_PATH_SEC7                       0x0b50
+#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC0                   0x0b51
+#define WCD934X_CDC_RX0_RX_PATH_MIX_SEC1                   0x0b52
+#define WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL                 0x0b53
+#define WCD934X_CDC_RX1_RX_PATH_CTL                        0x0b55
+#define WCD934X_CDC_RX1_RX_PATH_CFG0                       0x0b56
+#define WCD934X_CDC_RX1_RX_PATH_CFG1                       0x0b57
+#define WCD934X_CDC_RX1_RX_PATH_CFG2                       0x0b58
+#define WCD934X_CDC_RX1_RX_VOL_CTL                         0x0b59
+#define WCD934X_CDC_RX1_RX_PATH_MIX_CTL                    0x0b5a
+#define WCD934X_CDC_RX1_RX_PATH_MIX_CFG                    0x0b5b
+#define WCD934X_CDC_RX1_RX_VOL_MIX_CTL                     0x0b5c
+#define WCD934X_CDC_RX1_RX_PATH_SEC0                       0x0b5d
+#define WCD934X_CDC_RX1_RX_PATH_SEC1                       0x0b5e
+#define WCD934X_CDC_RX1_RX_PATH_SEC2                       0x0b5f
+#define WCD934X_CDC_RX1_RX_PATH_SEC3                       0x0b60
+#define WCD934X_CDC_RX1_RX_PATH_SEC4                       0x0b61
+#define WCD934X_CDC_RX1_RX_PATH_SEC5                       0x0b62
+#define WCD934X_CDC_RX1_RX_PATH_SEC6                       0x0b63
+#define WCD934X_CDC_RX1_RX_PATH_SEC7                       0x0b64
+#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC0                   0x0b65
+#define WCD934X_CDC_RX1_RX_PATH_MIX_SEC1                   0x0b66
+#define WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL                 0x0b67
+#define WCD934X_CDC_RX2_RX_PATH_CTL                        0x0b69
+#define WCD934X_CDC_RX2_RX_PATH_CFG0                       0x0b6a
+#define WCD934X_CDC_RX2_RX_PATH_CFG1                       0x0b6b
+#define WCD934X_CDC_RX2_RX_PATH_CFG2                       0x0b6c
+#define WCD934X_CDC_RX2_RX_VOL_CTL                         0x0b6d
+#define WCD934X_CDC_RX2_RX_PATH_MIX_CTL                    0x0b6e
+#define WCD934X_CDC_RX2_RX_PATH_MIX_CFG                    0x0b6f
+#define WCD934X_CDC_RX2_RX_VOL_MIX_CTL                     0x0b70
+#define WCD934X_CDC_RX2_RX_PATH_SEC0                       0x0b71
+#define WCD934X_CDC_RX2_RX_PATH_SEC1                       0x0b72
+#define WCD934X_CDC_RX2_RX_PATH_SEC2                       0x0b73
+#define WCD934X_CDC_RX2_RX_PATH_SEC3                       0x0b74
+#define WCD934X_CDC_RX2_RX_PATH_SEC4                       0x0b75
+#define WCD934X_CDC_RX2_RX_PATH_SEC5                       0x0b76
+#define WCD934X_CDC_RX2_RX_PATH_SEC6                       0x0b77
+#define WCD934X_CDC_RX2_RX_PATH_SEC7                       0x0b78
+#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC0                   0x0b79
+#define WCD934X_CDC_RX2_RX_PATH_MIX_SEC1                   0x0b7a
+#define WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL                 0x0b7b
+#define WCD934X_CDC_RX3_RX_PATH_CTL                        0x0b7d
+#define WCD934X_CDC_RX3_RX_PATH_CFG0                       0x0b7e
+#define WCD934X_CDC_RX3_RX_PATH_CFG1                       0x0b7f
+#define WCD934X_CDC_RX3_RX_PATH_CFG2                       0x0b80
+#define WCD934X_CDC_RX3_RX_VOL_CTL                         0x0b81
+#define WCD934X_CDC_RX3_RX_PATH_MIX_CTL                    0x0b82
+#define WCD934X_CDC_RX3_RX_PATH_MIX_CFG                    0x0b83
+#define WCD934X_CDC_RX3_RX_VOL_MIX_CTL                     0x0b84
+#define WCD934X_CDC_RX3_RX_PATH_SEC0                       0x0b85
+#define WCD934X_CDC_RX3_RX_PATH_SEC1                       0x0b86
+#define WCD934X_CDC_RX3_RX_PATH_SEC2                       0x0b87
+#define WCD934X_CDC_RX3_RX_PATH_SEC3                       0x0b88
+#define WCD934X_CDC_RX3_RX_PATH_SEC5                       0x0b8a
+#define WCD934X_CDC_RX3_RX_PATH_SEC6                       0x0b8b
+#define WCD934X_CDC_RX3_RX_PATH_SEC7                       0x0b8c
+#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC0                   0x0b8d
+#define WCD934X_CDC_RX3_RX_PATH_MIX_SEC1                   0x0b8e
+#define WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL                 0x0b8f
+#define WCD934X_CDC_RX4_RX_PATH_CTL                        0x0b91
+#define WCD934X_CDC_RX4_RX_PATH_CFG0                       0x0b92
+#define WCD934X_CDC_RX4_RX_PATH_CFG1                       0x0b93
+#define WCD934X_CDC_RX4_RX_PATH_CFG2                       0x0b94
+#define WCD934X_CDC_RX4_RX_VOL_CTL                         0x0b95
+#define WCD934X_CDC_RX4_RX_PATH_MIX_CTL                    0x0b96
+#define WCD934X_CDC_RX4_RX_PATH_MIX_CFG                    0x0b97
+#define WCD934X_CDC_RX4_RX_VOL_MIX_CTL                     0x0b98
+#define WCD934X_CDC_RX4_RX_PATH_SEC0                       0x0b99
+#define WCD934X_CDC_RX4_RX_PATH_SEC1                       0x0b9a
+#define WCD934X_CDC_RX4_RX_PATH_SEC2                       0x0b9b
+#define WCD934X_CDC_RX4_RX_PATH_SEC3                       0x0b9c
+#define WCD934X_CDC_RX4_RX_PATH_SEC5                       0x0b9e
+#define WCD934X_CDC_RX4_RX_PATH_SEC6                       0x0b9f
+#define WCD934X_CDC_RX4_RX_PATH_SEC7                       0x0ba0
+#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC0                   0x0ba1
+#define WCD934X_CDC_RX4_RX_PATH_MIX_SEC1                   0x0ba2
+#define WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL                 0x0ba3
+#define WCD934X_CDC_RX7_RX_PATH_CTL                        0x0bcd
+#define WCD934X_CDC_RX7_RX_PATH_CFG0                       0x0bce
+#define WCD934X_CDC_RX7_RX_PATH_CFG1                       0x0bcf
+#define WCD934X_CDC_RX7_RX_PATH_CFG2                       0x0bd0
+#define WCD934X_CDC_RX7_RX_VOL_CTL                         0x0bd1
+#define WCD934X_CDC_RX7_RX_PATH_MIX_CTL                    0x0bd2
+#define WCD934X_CDC_RX7_RX_PATH_MIX_CFG                    0x0bd3
+#define WCD934X_CDC_RX7_RX_VOL_MIX_CTL                     0x0bd4
+#define WCD934X_CDC_RX7_RX_PATH_SEC0                       0x0bd5
+#define WCD934X_CDC_RX7_RX_PATH_SEC1                       0x0bd6
+#define WCD934X_CDC_RX7_RX_PATH_SEC2                       0x0bd7
+#define WCD934X_CDC_RX7_RX_PATH_SEC3                       0x0bd8
+#define WCD934X_CDC_RX7_RX_PATH_SEC5                       0x0bda
+#define WCD934X_CDC_RX7_RX_PATH_SEC6                       0x0bdb
+#define WCD934X_CDC_RX7_RX_PATH_SEC7                       0x0bdc
+#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC0                   0x0bdd
+#define WCD934X_CDC_RX7_RX_PATH_MIX_SEC1                   0x0bde
+#define WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL                 0x0bdf
+#define WCD934X_CDC_RX8_RX_PATH_CTL                        0x0be1
+#define WCD934X_CDC_RX8_RX_PATH_CFG0                       0x0be2
+#define WCD934X_CDC_RX8_RX_PATH_CFG1                       0x0be3
+#define WCD934X_CDC_RX8_RX_PATH_CFG2                       0x0be4
+#define WCD934X_CDC_RX8_RX_VOL_CTL                         0x0be5
+#define WCD934X_CDC_RX8_RX_PATH_MIX_CTL                    0x0be6
+#define WCD934X_CDC_RX8_RX_PATH_MIX_CFG                    0x0be7
+#define WCD934X_CDC_RX8_RX_VOL_MIX_CTL                     0x0be8
+#define WCD934X_CDC_RX8_RX_PATH_SEC0                       0x0be9
+#define WCD934X_CDC_RX8_RX_PATH_SEC1                       0x0bea
+#define WCD934X_CDC_RX8_RX_PATH_SEC2                       0x0beb
+#define WCD934X_CDC_RX8_RX_PATH_SEC3                       0x0bec
+#define WCD934X_CDC_RX8_RX_PATH_SEC5                       0x0bee
+#define WCD934X_CDC_RX8_RX_PATH_SEC6                       0x0bef
+#define WCD934X_CDC_RX8_RX_PATH_SEC7                       0x0bf0
+#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC0                   0x0bf1
+#define WCD934X_CDC_RX8_RX_PATH_MIX_SEC1                   0x0bf2
+#define WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL                 0x0bf3
+#define WCD934X_PAGE12_PAGE_REGISTER                       0x0c00
+#define WCD934X_CDC_CLSH_CRC                               0x0c01
+#define WCD934X_CDC_CLSH_DLY_CTRL                          0x0c02
+#define WCD934X_CDC_CLSH_DECAY_CTRL                        0x0c03
+#define WCD934X_CDC_CLSH_HPH_V_PA                          0x0c04
+#define WCD934X_CDC_CLSH_EAR_V_PA                          0x0c05
+#define WCD934X_CDC_CLSH_HPH_V_HD                          0x0c06
+#define WCD934X_CDC_CLSH_EAR_V_HD                          0x0c07
+#define WCD934X_CDC_CLSH_K1_MSB                            0x0c08
+#define WCD934X_CDC_CLSH_K1_LSB                            0x0c09
+#define WCD934X_CDC_CLSH_K2_MSB                            0x0c0a
+#define WCD934X_CDC_CLSH_K2_LSB                            0x0c0b
+#define WCD934X_CDC_CLSH_IDLE_CTRL                         0x0c0c
+#define WCD934X_CDC_CLSH_IDLE_HPH                          0x0c0d
+#define WCD934X_CDC_CLSH_IDLE_EAR                          0x0c0e
+#define WCD934X_CDC_CLSH_TEST0                             0x0c0f
+#define WCD934X_CDC_CLSH_TEST1                             0x0c10
+#define WCD934X_CDC_CLSH_OVR_VREF                          0x0c11
+#define WCD934X_CDC_BOOST0_BOOST_PATH_CTL                  0x0c19
+#define WCD934X_CDC_BOOST0_BOOST_CTL                       0x0c1a
+#define WCD934X_CDC_BOOST0_BOOST_CFG1                      0x0c1b
+#define WCD934X_CDC_BOOST0_BOOST_CFG2                      0x0c1c
+#define WCD934X_CDC_BOOST1_BOOST_PATH_CTL                  0x0c21
+#define WCD934X_CDC_BOOST1_BOOST_CTL                       0x0c22
+#define WCD934X_CDC_BOOST1_BOOST_CFG1                      0x0c23
+#define WCD934X_CDC_BOOST1_BOOST_CFG2                      0x0c24
+#define WCD934X_CDC_VBAT_VBAT_PATH_CTL                     0x0c3d
+#define WCD934X_CDC_VBAT_VBAT_CFG                          0x0c3e
+#define WCD934X_CDC_VBAT_VBAT_ADC_CAL1                     0x0c3f
+#define WCD934X_CDC_VBAT_VBAT_ADC_CAL2                     0x0c40
+#define WCD934X_CDC_VBAT_VBAT_ADC_CAL3                     0x0c41
+#define WCD934X_CDC_VBAT_VBAT_PK_EST1                      0x0c42
+#define WCD934X_CDC_VBAT_VBAT_PK_EST2                      0x0c43
+#define WCD934X_CDC_VBAT_VBAT_PK_EST3                      0x0c44
+#define WCD934X_CDC_VBAT_VBAT_RF_PROC1                     0x0c45
+#define WCD934X_CDC_VBAT_VBAT_RF_PROC2                     0x0c46
+#define WCD934X_CDC_VBAT_VBAT_TAC1                         0x0c47
+#define WCD934X_CDC_VBAT_VBAT_TAC2                         0x0c48
+#define WCD934X_CDC_VBAT_VBAT_TAC3                         0x0c49
+#define WCD934X_CDC_VBAT_VBAT_TAC4                         0x0c4a
+#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD1                    0x0c4b
+#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD2                    0x0c4c
+#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD3                    0x0c4d
+#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD4                    0x0c4e
+#define WCD934X_CDC_VBAT_VBAT_DEBUG1                       0x0c4f
+#define WCD934X_CDC_VBAT_VBAT_GAIN_UPD_MON                 0x0c50
+#define WCD934X_CDC_VBAT_VBAT_GAIN_MON_VAL                 0x0c51
+#define WCD934X_CDC_VBAT_VBAT_BAN                          0x0c52
+#define WCD934X_MIXING_ASRC0_CLK_RST_CTL                   0x0c55
+#define WCD934X_MIXING_ASRC0_CTL0                          0x0c56
+#define WCD934X_MIXING_ASRC0_CTL1                          0x0c57
+#define WCD934X_MIXING_ASRC0_FIFO_CTL                      0x0c58
+#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_LSB          0x0c59
+#define WCD934X_MIXING_ASRC0_STATUS_FMIN_CNTR_MSB          0x0c5a
+#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_LSB          0x0c5b
+#define WCD934X_MIXING_ASRC0_STATUS_FMAX_CNTR_MSB          0x0c5c
+#define WCD934X_MIXING_ASRC0_STATUS_FIFO                   0x0c5d
+#define WCD934X_MIXING_ASRC1_CLK_RST_CTL                   0x0c61
+#define WCD934X_MIXING_ASRC1_CTL0                          0x0c62
+#define WCD934X_MIXING_ASRC1_CTL1                          0x0c63
+#define WCD934X_MIXING_ASRC1_FIFO_CTL                      0x0c64
+#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_LSB          0x0c65
+#define WCD934X_MIXING_ASRC1_STATUS_FMIN_CNTR_MSB          0x0c66
+#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_LSB          0x0c67
+#define WCD934X_MIXING_ASRC1_STATUS_FMAX_CNTR_MSB          0x0c68
+#define WCD934X_MIXING_ASRC1_STATUS_FIFO                   0x0c69
+#define WCD934X_MIXING_ASRC2_CLK_RST_CTL                   0x0c6d
+#define WCD934X_MIXING_ASRC2_CTL0                          0x0c6e
+#define WCD934X_MIXING_ASRC2_CTL1                          0x0c6f
+#define WCD934X_MIXING_ASRC2_FIFO_CTL                      0x0c70
+#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_LSB          0x0c71
+#define WCD934X_MIXING_ASRC2_STATUS_FMIN_CNTR_MSB          0x0c72
+#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_LSB          0x0c73
+#define WCD934X_MIXING_ASRC2_STATUS_FMAX_CNTR_MSB          0x0c74
+#define WCD934X_MIXING_ASRC2_STATUS_FIFO                   0x0c75
+#define WCD934X_MIXING_ASRC3_CLK_RST_CTL                   0x0c79
+#define WCD934X_MIXING_ASRC3_CTL0                          0x0c7a
+#define WCD934X_MIXING_ASRC3_CTL1                          0x0c7b
+#define WCD934X_MIXING_ASRC3_FIFO_CTL                      0x0c7c
+#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_LSB          0x0c7d
+#define WCD934X_MIXING_ASRC3_STATUS_FMIN_CNTR_MSB          0x0c7e
+#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_LSB          0x0c7f
+#define WCD934X_MIXING_ASRC3_STATUS_FMAX_CNTR_MSB          0x0c80
+#define WCD934X_MIXING_ASRC3_STATUS_FIFO                   0x0c81
+#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_0                   0x0c85
+#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_1                   0x0c86
+#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_2                   0x0c87
+#define WCD934X_SWR_AHB_BRIDGE_WR_DATA_3                   0x0c88
+#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0                   0x0c89
+#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_1                   0x0c8a
+#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_2                   0x0c8b
+#define WCD934X_SWR_AHB_BRIDGE_WR_ADDR_3                   0x0c8c
+#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0                   0x0c8d
+#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_1                   0x0c8e
+#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_2                   0x0c8f
+#define WCD934X_SWR_AHB_BRIDGE_RD_ADDR_3                   0x0c90
+#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_0                   0x0c91
+#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_1                   0x0c92
+#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_2                   0x0c93
+#define WCD934X_SWR_AHB_BRIDGE_RD_DATA_3                   0x0c94
+#define WCD934X_SWR_AHB_BRIDGE_ACCESS_CFG                  0x0c95
+#define WCD934X_SWR_AHB_BRIDGE_ACCESS_STATUS               0x0c96
+#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL          0x0cb5
+#define WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CFG1         0x0cb6
+#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL          0x0cb9
+#define WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CFG1         0x0cba
+#define WCD934X_SIDETONE_ASRC0_CLK_RST_CTL                 0x0cbd
+#define WCD934X_SIDETONE_ASRC0_CTL0                        0x0cbe
+#define WCD934X_SIDETONE_ASRC0_CTL1                        0x0cbf
+#define WCD934X_SIDETONE_ASRC0_FIFO_CTL                    0x0cc0
+#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_LSB        0x0cc1
+#define WCD934X_SIDETONE_ASRC0_STATUS_FMIN_CNTR_MSB        0x0cc2
+#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_LSB        0x0cc3
+#define WCD934X_SIDETONE_ASRC0_STATUS_FMAX_CNTR_MSB        0x0cc4
+#define WCD934X_SIDETONE_ASRC0_STATUS_FIFO                 0x0cc5
+#define WCD934X_SIDETONE_ASRC1_CLK_RST_CTL                 0x0cc9
+#define WCD934X_SIDETONE_ASRC1_CTL0                        0x0cca
+#define WCD934X_SIDETONE_ASRC1_CTL1                        0x0ccb
+#define WCD934X_SIDETONE_ASRC1_FIFO_CTL                    0x0ccc
+#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_LSB        0x0ccd
+#define WCD934X_SIDETONE_ASRC1_STATUS_FMIN_CNTR_MSB        0x0cce
+#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_LSB        0x0ccf
+#define WCD934X_SIDETONE_ASRC1_STATUS_FMAX_CNTR_MSB        0x0cd0
+#define WCD934X_SIDETONE_ASRC1_STATUS_FIFO                 0x0cd1
+#define WCD934X_EC_REF_HQ0_EC_REF_HQ_PATH_CTL              0x0cd5
+#define WCD934X_EC_REF_HQ0_EC_REF_HQ_CFG0                  0x0cd6
+#define WCD934X_EC_REF_HQ1_EC_REF_HQ_PATH_CTL              0x0cdd
+#define WCD934X_EC_REF_HQ1_EC_REF_HQ_CFG0                  0x0cde
+#define WCD934X_EC_ASRC0_CLK_RST_CTL                       0x0ce5
+#define WCD934X_EC_ASRC0_CTL0                              0x0ce6
+#define WCD934X_EC_ASRC0_CTL1                              0x0ce7
+#define WCD934X_EC_ASRC0_FIFO_CTL                          0x0ce8
+#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_LSB              0x0ce9
+#define WCD934X_EC_ASRC0_STATUS_FMIN_CNTR_MSB              0x0cea
+#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_LSB              0x0ceb
+#define WCD934X_EC_ASRC0_STATUS_FMAX_CNTR_MSB              0x0cec
+#define WCD934X_EC_ASRC0_STATUS_FIFO                       0x0ced
+#define WCD934X_EC_ASRC1_CLK_RST_CTL                       0x0cf1
+#define WCD934X_EC_ASRC1_CTL0                              0x0cf2
+#define WCD934X_EC_ASRC1_CTL1                              0x0cf3
+#define WCD934X_EC_ASRC1_FIFO_CTL                          0x0cf4
+#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_LSB              0x0cf5
+#define WCD934X_EC_ASRC1_STATUS_FMIN_CNTR_MSB              0x0cf6
+#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_LSB              0x0cf7
+#define WCD934X_EC_ASRC1_STATUS_FMAX_CNTR_MSB              0x0cf8
+#define WCD934X_EC_ASRC1_STATUS_FIFO                       0x0cf9
+#define WCD934X_PAGE13_PAGE_REGISTER                       0x0d00
+#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0                0x0d01
+#define WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1                0x0d02
+#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0                0x0d03
+#define WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1                0x0d04
+#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0                0x0d05
+#define WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1                0x0d06
+#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0                0x0d07
+#define WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1                0x0d08
+#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0                0x0d09
+#define WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1                0x0d0a
+#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0                0x0d0f
+#define WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1                0x0d10
+#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0                0x0d11
+#define WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1                0x0d12
+#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0                 0x0d13
+#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1                 0x0d14
+#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2                 0x0d15
+#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3                 0x0d16
+#define WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4                 0x0d17
+#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0           0x0d18
+#define WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1           0x0d19
+#define WCD934X_CDC_RX_INP_MUX_ANC_CFG0                    0x0d1a
+#define WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0            0x0d1b
+#define WCD934X_CDC_RX_INP_MUX_EC_REF_HQ_CFG0              0x0d1c
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0               0x0d1d
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1               0x0d1e
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0               0x0d1f
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1               0x0d20
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0               0x0d21
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1               0x0d22
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0               0x0d23
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1               0x0d25
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0               0x0d26
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0               0x0d27
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0               0x0d28
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0               0x0d29
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0               0x0d2a
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0              0x0d2b
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0              0x0d2c
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0              0x0d2d
+#define WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0              0x0d2e
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0     0x0d31
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1     0x0d32
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2     0x0d33
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3     0x0d34
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0     0x0d35
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1     0x0d36
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2     0x0d37
+#define WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3     0x0d38
+#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0                  0x0d3a
+#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1                  0x0d3b
+#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2                  0x0d3c
+#define WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3                  0x0d3d
+#define WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL              0x0d41
+#define WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL            0x0d42
+#define WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL               0x0d43
+#define WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL               0x0d44
+#define WCD934X_CDC_CLK_RST_CTRL_ASRC_SHARE_CONTROL        0x0d45
+#define WCD934X_CDC_CLK_RST_CTRL_GFM_CONTROL               0x0d46
+#define WCD934X_CDC_PROX_DETECT_PROX_CTL                   0x0d49
+#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD0          0x0d4a
+#define WCD934X_CDC_PROX_DETECT_PROX_POLL_PERIOD1          0x0d4b
+#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_LSB       0x0d4c
+#define WCD934X_CDC_PROX_DETECT_PROX_SIG_PATTERN_MSB       0x0d4d
+#define WCD934X_CDC_PROX_DETECT_PROX_STATUS                0x0d4e
+#define WCD934X_CDC_PROX_DETECT_PROX_TEST_CTRL             0x0d4f
+#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB         0x0d50
+#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB         0x0d51
+#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_LSB_RD      0x0d52
+#define WCD934X_CDC_PROX_DETECT_PROX_TEST_BUFF_MSB_RD      0x0d53
+#define WCD934X_CDC_PROX_DETECT_PROX_CTL_REPEAT_PAT        0x0d54
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL             0x0d55
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL          0x0d56
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL          0x0d57
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL          0x0d58
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL          0x0d59
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B5_CTL          0x0d5a
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B6_CTL          0x0d5b
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B7_CTL          0x0d5c
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B8_CTL          0x0d5d
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_CTL                  0x0d5e
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_TIMER_CTL       0x0d5f
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL          0x0d60
+#define WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL          0x0d61
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL             0x0d65
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL          0x0d66
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL          0x0d67
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL          0x0d68
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL          0x0d69
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B5_CTL          0x0d6a
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B6_CTL          0x0d6b
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B7_CTL          0x0d6c
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B8_CTL          0x0d6d
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_CTL                  0x0d6e
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_TIMER_CTL       0x0d6f
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B1_CTL          0x0d70
+#define WCD934X_CDC_SIDETONE_IIR1_IIR_COEF_B2_CTL          0x0d71
+#define WCD934X_CDC_TOP_TOP_CFG0                           0x0d81
+#define WCD934X_CDC_TOP_TOP_CFG1                           0x0d82
+#define WCD934X_CDC_TOP_TOP_CFG7                           0x0d88
+#define WCD934X_CDC_TOP_HPHL_COMP_WR_LSB                   0x0d89
+#define WCD934X_CDC_TOP_HPHL_COMP_WR_MSB                   0x0d8a
+#define WCD934X_CDC_TOP_HPHL_COMP_LUT                      0x0d8b
+#define WCD934X_CDC_TOP_HPHL_COMP_RD_LSB                   0x0d8c
+#define WCD934X_CDC_TOP_HPHL_COMP_RD_MSB                   0x0d8d
+#define WCD934X_CDC_TOP_HPHR_COMP_WR_LSB                   0x0d8e
+#define WCD934X_CDC_TOP_HPHR_COMP_WR_MSB                   0x0d8f
+#define WCD934X_CDC_TOP_HPHR_COMP_LUT                      0x0d90
+#define WCD934X_CDC_TOP_HPHR_COMP_RD_LSB                   0x0d91
+#define WCD934X_CDC_TOP_HPHR_COMP_RD_MSB                   0x0d92
+#define WCD934X_CDC_TOP_DIFFL_COMP_WR_LSB                  0x0d93
+#define WCD934X_CDC_TOP_DIFFL_COMP_WR_MSB                  0x0d94
+#define WCD934X_CDC_TOP_DIFFL_COMP_LUT                     0x0d95
+#define WCD934X_CDC_TOP_DIFFL_COMP_RD_LSB                  0x0d96
+#define WCD934X_CDC_TOP_DIFFL_COMP_RD_MSB                  0x0d97
+#define WCD934X_CDC_TOP_DIFFR_COMP_WR_LSB                  0x0d98
+#define WCD934X_CDC_TOP_DIFFR_COMP_WR_MSB                  0x0d99
+#define WCD934X_CDC_TOP_DIFFR_COMP_LUT                     0x0d9a
+#define WCD934X_CDC_TOP_DIFFR_COMP_RD_LSB                  0x0d9b
+#define WCD934X_CDC_TOP_DIFFR_COMP_RD_MSB                  0x0d9c
+#define WCD934X_CDC_DSD0_PATH_CTL                          0x0db1
+#define WCD934X_CDC_DSD0_CFG0                              0x0db2
+#define WCD934X_CDC_DSD0_CFG1                              0x0db3
+#define WCD934X_CDC_DSD0_CFG2                              0x0db4
+#define WCD934X_CDC_DSD0_CFG3                              0x0db5
+#define WCD934X_CDC_DSD0_CFG4                              0x0db6
+#define WCD934X_CDC_DSD0_CFG5                              0x0db7
+#define WCD934X_CDC_DSD1_PATH_CTL                          0x0dc1
+#define WCD934X_CDC_DSD1_CFG0                              0x0dc2
+#define WCD934X_CDC_DSD1_CFG1                              0x0dc3
+#define WCD934X_CDC_DSD1_CFG2                              0x0dc4
+#define WCD934X_CDC_DSD1_CFG3                              0x0dc5
+#define WCD934X_CDC_DSD1_CFG4                              0x0dc6
+#define WCD934X_CDC_DSD1_CFG5                              0x0dc7
+#define WCD934X_CDC_RX_IDLE_DET_PATH_CTL                   0x0dd1
+#define WCD934X_CDC_RX_IDLE_DET_CFG0                       0x0dd2
+#define WCD934X_CDC_RX_IDLE_DET_CFG1                       0x0dd3
+#define WCD934X_CDC_RX_IDLE_DET_CFG2                       0x0dd4
+#define WCD934X_CDC_RX_IDLE_DET_CFG3                       0x0dd5
+#define WCD934X_PAGE14_PAGE_REGISTER                       0x0e00
+#define WCD934X_CDC_RATE_EST0_RE_CLK_RST_CTL               0x0e01
+#define WCD934X_CDC_RATE_EST0_RE_CTL                       0x0e02
+#define WCD934X_CDC_RATE_EST0_RE_PULSE_SUPR_CTL            0x0e03
+#define WCD934X_CDC_RATE_EST0_RE_TIMER                     0x0e04
+#define WCD934X_CDC_RATE_EST0_RE_BW_SW                     0x0e05
+#define WCD934X_CDC_RATE_EST0_RE_THRESH                    0x0e06
+#define WCD934X_CDC_RATE_EST0_RE_STATUS                    0x0e07
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_CTRL                 0x0e09
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_TIMER2               0x0e0c
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW1           0x0e0d
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW2           0x0e0e
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW3           0x0e0f
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW4           0x0e10
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_OFFSET_BW5           0x0e11
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW1            0x0e12
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW2            0x0e13
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW3            0x0e14
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW4            0x0e15
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMIT_BW5            0x0e16
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW1          0x0e17
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW2          0x0e18
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW3          0x0e19
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW4          0x0e1a
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_LIMITD1_BW5          0x0e1b
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW1             0x0e1c
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW2             0x0e1d
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW3             0x0e1e
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW4             0x0e1f
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_HYST_BW5             0x0e20
+#define WCD934X_CDC_RATE_EST0_RE_RMAX_DIAG                 0x0e21
+#define WCD934X_CDC_RATE_EST0_RE_RMIN_DIAG                 0x0e22
+#define WCD934X_CDC_RATE_EST0_RE_PH_DET                    0x0e23
+#define WCD934X_CDC_RATE_EST0_RE_DIAG_CLR                  0x0e24
+#define WCD934X_CDC_RATE_EST0_RE_MB_SW_STATE               0x0e25
+#define WCD934X_CDC_RATE_EST0_RE_MAST_DIAG_STATE           0x0e26
+#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_7_0              0x0e27
+#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_15_8             0x0e28
+#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_23_16            0x0e29
+#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_31_24            0x0e2a
+#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_39_32            0x0e2b
+#define WCD934X_CDC_RATE_EST0_RE_RATE_OUT_40_43            0x0e2c
+#define WCD934X_CDC_RATE_EST1_RE_CLK_RST_CTL               0x0e31
+#define WCD934X_CDC_RATE_EST1_RE_CTL                       0x0e32
+#define WCD934X_CDC_RATE_EST1_RE_PULSE_SUPR_CTL            0x0e33
+#define WCD934X_CDC_RATE_EST1_RE_TIMER                     0x0e34
+#define WCD934X_CDC_RATE_EST1_RE_BW_SW                     0x0e35
+#define WCD934X_CDC_RATE_EST1_RE_THRESH                    0x0e36
+#define WCD934X_CDC_RATE_EST1_RE_STATUS                    0x0e37
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_CTRL                 0x0e39
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_TIMER2               0x0e3c
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW1           0x0e3d
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW2           0x0e3e
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW3           0x0e3f
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW4           0x0e40
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_OFFSET_BW5           0x0e41
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW1            0x0e42
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW2            0x0e43
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW3            0x0e44
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW4            0x0e45
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMIT_BW5            0x0e46
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW1          0x0e47
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW2          0x0e48
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW3          0x0e49
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW4          0x0e4a
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_LIMITD1_BW5          0x0e4b
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW1             0x0e4c
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW2             0x0e4d
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW3             0x0e4e
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW4             0x0e4f
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_HYST_BW5             0x0e50
+#define WCD934X_CDC_RATE_EST1_RE_RMAX_DIAG                 0x0e51
+#define WCD934X_CDC_RATE_EST1_RE_RMIN_DIAG                 0x0e52
+#define WCD934X_CDC_RATE_EST1_RE_PH_DET                    0x0e53
+#define WCD934X_CDC_RATE_EST1_RE_DIAG_CLR                  0x0e54
+#define WCD934X_CDC_RATE_EST1_RE_MB_SW_STATE               0x0e55
+#define WCD934X_CDC_RATE_EST1_RE_MAST_DIAG_STATE           0x0e56
+#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_7_0              0x0e57
+#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_15_8             0x0e58
+#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_23_16            0x0e59
+#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_31_24            0x0e5a
+#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_39_32            0x0e5b
+#define WCD934X_CDC_RATE_EST1_RE_RATE_OUT_40_43            0x0e5c
+#define WCD934X_CDC_RATE_EST2_RE_CLK_RST_CTL               0x0e61
+#define WCD934X_CDC_RATE_EST2_RE_CTL                       0x0e62
+#define WCD934X_CDC_RATE_EST2_RE_PULSE_SUPR_CTL            0x0e63
+#define WCD934X_CDC_RATE_EST2_RE_TIMER                     0x0e64
+#define WCD934X_CDC_RATE_EST2_RE_BW_SW                     0x0e65
+#define WCD934X_CDC_RATE_EST2_RE_THRESH                    0x0e66
+#define WCD934X_CDC_RATE_EST2_RE_STATUS                    0x0e67
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_CTRL                 0x0e69
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_TIMER2               0x0e6c
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW1           0x0e6d
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW2           0x0e6e
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW3           0x0e6f
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW4           0x0e70
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_OFFSET_BW5           0x0e71
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW1            0x0e72
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW2            0x0e73
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW3            0x0e74
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW4            0x0e75
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMIT_BW5            0x0e76
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW1          0x0e77
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW2          0x0e78
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW3          0x0e79
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW4          0x0e7a
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_LIMITD1_BW5          0x0e7b
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW1             0x0e7c
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW2             0x0e7d
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW3             0x0e7e
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW4             0x0e7f
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_HYST_BW5             0x0e80
+#define WCD934X_CDC_RATE_EST2_RE_RMAX_DIAG                 0x0e81
+#define WCD934X_CDC_RATE_EST2_RE_RMIN_DIAG                 0x0e82
+#define WCD934X_CDC_RATE_EST2_RE_PH_DET                    0x0e83
+#define WCD934X_CDC_RATE_EST2_RE_DIAG_CLR                  0x0e84
+#define WCD934X_CDC_RATE_EST2_RE_MB_SW_STATE               0x0e85
+#define WCD934X_CDC_RATE_EST2_RE_MAST_DIAG_STATE           0x0e86
+#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_7_0              0x0e87
+#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_15_8             0x0e88
+#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_23_16            0x0e89
+#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_31_24            0x0e8a
+#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_39_32            0x0e8b
+#define WCD934X_CDC_RATE_EST2_RE_RATE_OUT_40_43            0x0e8c
+#define WCD934X_CDC_RATE_EST3_RE_CLK_RST_CTL               0x0e91
+#define WCD934X_CDC_RATE_EST3_RE_CTL                       0x0e92
+#define WCD934X_CDC_RATE_EST3_RE_PULSE_SUPR_CTL            0x0e93
+#define WCD934X_CDC_RATE_EST3_RE_TIMER                     0x0e94
+#define WCD934X_CDC_RATE_EST3_RE_BW_SW                     0x0e95
+#define WCD934X_CDC_RATE_EST3_RE_THRESH                    0x0e96
+#define WCD934X_CDC_RATE_EST3_RE_STATUS                    0x0e97
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_CTRL                 0x0e99
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_TIMER2               0x0e9c
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW1           0x0e9d
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW2           0x0e9e
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW3           0x0e9f
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW4           0x0ea0
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_OFFSET_BW5           0x0ea1
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW1            0x0ea2
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW2            0x0ea3
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW3            0x0ea4
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW4            0x0ea5
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMIT_BW5            0x0ea6
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW1          0x0ea7
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW2          0x0ea8
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW3          0x0ea9
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW4          0x0eaa
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_LIMITD1_BW5          0x0eab
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW1             0x0eac
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW2             0x0ead
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW3             0x0eae
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW4             0x0eaf
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_HYST_BW5             0x0eb0
+#define WCD934X_CDC_RATE_EST3_RE_RMAX_DIAG                 0x0eb1
+#define WCD934X_CDC_RATE_EST3_RE_RMIN_DIAG                 0x0eb2
+#define WCD934X_CDC_RATE_EST3_RE_PH_DET                    0x0eb3
+#define WCD934X_CDC_RATE_EST3_RE_DIAG_CLR                  0x0eb4
+#define WCD934X_CDC_RATE_EST3_RE_MB_SW_STATE               0x0eb5
+#define WCD934X_CDC_RATE_EST3_RE_MAST_DIAG_STATE           0x0eb6
+#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_7_0              0x0eb7
+#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_15_8             0x0eb8
+#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_23_16            0x0eb9
+#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_31_24            0x0eba
+#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_39_32            0x0ebb
+#define WCD934X_CDC_RATE_EST3_RE_RATE_OUT_40_43            0x0ebc
+#define WCD934X_PAGE15_PAGE_REGISTER                       0x0f00
+#define WCD934X_SPLINE_SRC0_CLK_RST_CTL_0                  0x0f01
+#define WCD934X_SPLINE_SRC0_STATUS                         0x0f02
+#define WCD934X_SPLINE_SRC1_CLK_RST_CTL_0                  0x0f19
+#define WCD934X_SPLINE_SRC1_STATUS                         0x0f1a
+#define WCD934X_SPLINE_SRC2_CLK_RST_CTL_0                  0x0f31
+#define WCD934X_SPLINE_SRC2_STATUS                         0x0f32
+#define WCD934X_SPLINE_SRC3_CLK_RST_CTL_0                  0x0f49
+#define WCD934X_SPLINE_SRC3_STATUS                         0x0f4a
+#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0                  0x0fa1
+#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1                  0x0fa2
+#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG2                  0x0fa3
+#define WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3                  0x0fa4
+#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0                  0x0fa5
+#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1                  0x0fa6
+#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG2                  0x0fa7
+#define WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3                  0x0fa8
+#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG0            0x0fa9
+#define WCD934X_CDC_DEBUG_SPLINE_SRC_DEBUG_CFG1            0x0faa
+#define WCD934X_CDC_DEBUG_RC_RE_ASRC_DEBUG_CFG0            0x0fab
+#define WCD934X_CDC_DEBUG_ANC0_RC0_FIFO_CTL                0x0fac
+#define WCD934X_CDC_DEBUG_ANC0_RC1_FIFO_CTL                0x0fad
+#define WCD934X_CDC_DEBUG_ANC1_RC0_FIFO_CTL                0x0fae
+#define WCD934X_CDC_DEBUG_ANC1_RC1_FIFO_CTL                0x0faf
+#define WCD934X_CDC_DEBUG_ANC_RC_RST_DBG_CNTR              0x0fb0
+#define WCD934X_PAGE80_PAGE_REGISTER                       0x5000
+#define WCD934X_CODEC_CPR_WR_DATA_0                        0x5001
+#define WCD934X_CODEC_CPR_WR_DATA_1                        0x5002
+#define WCD934X_CODEC_CPR_WR_DATA_2                        0x5003
+#define WCD934X_CODEC_CPR_WR_DATA_3                        0x5004
+#define WCD934X_CODEC_CPR_WR_ADDR_0                        0x5005
+#define WCD934X_CODEC_CPR_WR_ADDR_1                        0x5006
+#define WCD934X_CODEC_CPR_WR_ADDR_2                        0x5007
+#define WCD934X_CODEC_CPR_WR_ADDR_3                        0x5008
+#define WCD934X_CODEC_CPR_RD_ADDR_0                        0x5009
+#define WCD934X_CODEC_CPR_RD_ADDR_1                        0x500a
+#define WCD934X_CODEC_CPR_RD_ADDR_2                        0x500b
+#define WCD934X_CODEC_CPR_RD_ADDR_3                        0x500c
+#define WCD934X_CODEC_CPR_RD_DATA_0                        0x500d
+#define WCD934X_CODEC_CPR_RD_DATA_1                        0x500e
+#define WCD934X_CODEC_CPR_RD_DATA_2                        0x500f
+#define WCD934X_CODEC_CPR_RD_DATA_3                        0x5010
+#define WCD934X_CODEC_CPR_ACCESS_CFG                       0x5011
+#define WCD934X_CODEC_CPR_ACCESS_STATUS                    0x5012
+#define WCD934X_CODEC_CPR_NOM_CX_VDD                       0x5021
+#define WCD934X_CODEC_CPR_SVS_CX_VDD                       0x5022
+#define WCD934X_CODEC_CPR_SVS2_CX_VDD                      0x5023
+#define WCD934X_CODEC_CPR_NOM_MX_VDD                       0x5024
+#define WCD934X_CODEC_CPR_SVS_MX_VDD                       0x5025
+#define WCD934X_CODEC_CPR_SVS2_MX_VDD                      0x5026
+#define WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD                  0x5027
+#define WCD934X_CODEC_CPR_MAX_SVS2_STEP                    0x5028
+#define WCD934X_CODEC_CPR_CTL                              0x5029
+#define WCD934X_CODEC_CPR_SW_MODECHNG_STATUS               0x502a
+#define WCD934X_CODEC_CPR_SW_MODECHNG_START                0x502b
+#define WCD934X_CODEC_CPR_CPR_STATUS                       0x502c
+#define WCD934X_PAGE128_PAGE_REGISTER                      0x8000
+#define WCD934X_TLMM_BIST_MODE_PINCFG                      0x8001
+#define WCD934X_TLMM_RF_PA_ON_PINCFG                       0x8002
+#define WCD934X_TLMM_INTR1_PINCFG                          0x8003
+#define WCD934X_TLMM_INTR2_PINCFG                          0x8004
+#define WCD934X_TLMM_SWR_DATA_PINCFG                       0x8005
+#define WCD934X_TLMM_SWR_CLK_PINCFG                        0x8006
+#define WCD934X_TLMM_I2S_2_SCK_PINCFG                      0x8007
+#define WCD934X_TLMM_SLIMBUS_DATA1_PINCFG                  0x8008
+#define WCD934X_TLMM_SLIMBUS_DATA2_PINCFG                  0x8009
+#define WCD934X_TLMM_SLIMBUS_CLK_PINCFG                    0x800a
+#define WCD934X_TLMM_I2C_CLK_PINCFG                        0x800b
+#define WCD934X_TLMM_I2C_DATA_PINCFG                       0x800c
+#define WCD934X_TLMM_I2S_0_RX_PINCFG                       0x800d
+#define WCD934X_TLMM_I2S_0_TX_PINCFG                       0x800e
+#define WCD934X_TLMM_I2S_0_SCK_PINCFG                      0x800f
+#define WCD934X_TLMM_I2S_0_WS_PINCFG                       0x8010
+#define WCD934X_TLMM_I2S_1_RX_PINCFG                       0x8011
+#define WCD934X_TLMM_I2S_1_TX_PINCFG                       0x8012
+#define WCD934X_TLMM_I2S_1_SCK_PINCFG                      0x8013
+#define WCD934X_TLMM_I2S_1_WS_PINCFG                       0x8014
+#define WCD934X_TLMM_DMIC1_CLK_PINCFG                      0x8015
+#define WCD934X_TLMM_DMIC1_DATA_PINCFG                     0x8016
+#define WCD934X_TLMM_DMIC2_CLK_PINCFG                      0x8017
+#define WCD934X_TLMM_DMIC2_DATA_PINCFG                     0x8018
+#define WCD934X_TLMM_DMIC3_CLK_PINCFG                      0x8019
+#define WCD934X_TLMM_DMIC3_DATA_PINCFG                     0x801a
+#define WCD934X_TLMM_JTCK_PINCFG                           0x801b
+#define WCD934X_TLMM_GPIO1_PINCFG                          0x801c
+#define WCD934X_TLMM_GPIO2_PINCFG                          0x801d
+#define WCD934X_TLMM_GPIO3_PINCFG                          0x801e
+#define WCD934X_TLMM_GPIO4_PINCFG                          0x801f
+#define WCD934X_TLMM_SPI_S_CSN_PINCFG                      0x8020
+#define WCD934X_TLMM_SPI_S_CLK_PINCFG                      0x8021
+#define WCD934X_TLMM_SPI_S_DOUT_PINCFG                     0x8022
+#define WCD934X_TLMM_SPI_S_DIN_PINCFG                      0x8023
+#define WCD934X_TLMM_BA_N_PINCFG                           0x8024
+#define WCD934X_TLMM_GPIO0_PINCFG                          0x8025
+#define WCD934X_TLMM_I2S_2_RX_PINCFG                       0x8026
+#define WCD934X_TLMM_I2S_2_WS_PINCFG                       0x8027
+#define WCD934X_TEST_DEBUG_PIN_CTL_OE_0                    0x8031
+#define WCD934X_TEST_DEBUG_PIN_CTL_OE_1                    0x8032
+#define WCD934X_TEST_DEBUG_PIN_CTL_OE_2                    0x8033
+#define WCD934X_TEST_DEBUG_PIN_CTL_OE_3                    0x8034
+#define WCD934X_TEST_DEBUG_PIN_CTL_OE_4                    0x8035
+#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_0                  0x8036
+#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_1                  0x8037
+#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_2                  0x8038
+#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_3                  0x8039
+#define WCD934X_TEST_DEBUG_PIN_CTL_DATA_4                  0x803a
+#define WCD934X_TEST_DEBUG_PAD_DRVCTL_0                    0x803b
+#define WCD934X_TEST_DEBUG_PAD_DRVCTL_1                    0x803c
+#define WCD934X_TEST_DEBUG_PIN_STATUS                      0x803d
+#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_1                  0x803e
+#define WCD934X_TEST_DEBUG_NPL_DLY_TEST_2                  0x803f
+#define WCD934X_TEST_DEBUG_MEM_CTRL                        0x8040
+#define WCD934X_TEST_DEBUG_DEBUG_BUS_SEL                   0x8041
+#define WCD934X_TEST_DEBUG_DEBUG_JTAG                      0x8042
+#define WCD934X_TEST_DEBUG_DEBUG_EN_1                      0x8043
+#define WCD934X_TEST_DEBUG_DEBUG_EN_2                      0x8044
+#define WCD934X_TEST_DEBUG_DEBUG_EN_3                      0x8045
+#define WCD934X_TEST_DEBUG_DEBUG_EN_4                      0x8046
+#define WCD934X_TEST_DEBUG_DEBUG_EN_5                      0x8047
+#define WCD934X_TEST_DEBUG_ANA_DTEST_DIR                   0x804a
+#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_0               0x804b
+#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_1               0x804c
+#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_2               0x804d
+#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_3               0x804e
+#define WCD934X_TEST_DEBUG_PAD_INP_DISABLE_4               0x804f
+#define WCD934X_TEST_DEBUG_SYSMEM_CTRL                     0x8050
+#define WCD934X_TEST_DEBUG_SOC_SW_PWR_SEQ_DELAY            0x8051
+#define WCD934X_TEST_DEBUG_LVAL_NOM_LOW                    0x8052
+#define WCD934X_TEST_DEBUG_LVAL_NOM_HIGH                   0x8053
+#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW               0x8054
+#define WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH              0x8055
+#define WCD934X_TEST_DEBUG_SPI_SLAVE_CHAR                  0x8056
+#define WCD934X_TEST_DEBUG_CODEC_DIAGS                     0x8057
+#define WCD934X_MAX_REGISTER                               0x80FF
+
+/* SLIMBUS Slave Registers */
+#define WCD934X_SLIM_PGD_PORT_INT_RX_EN0                     (0x30)
+#define WCD934X_SLIM_PGD_PORT_INT_TX_EN0                     (0x32)
+#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0                (0x34)
+#define WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_1                (0x35)
+#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_0                (0x36)
+#define WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1                (0x37)
+#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0                   (0x38)
+#define WCD934X_SLIM_PGD_PORT_INT_CLR_RX_1                   (0x39)
+#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_0                   (0x3A)
+#define WCD934X_SLIM_PGD_PORT_INT_CLR_TX_1                   (0x3B)
+#define WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0                 (0x60)
+#define WCD934X_SLIM_PGD_PORT_INT_TX_SOURCE0                 (0x70)
+
+#endif
diff --git a/include/linux/mfd/wcd9xxx/Kbuild b/include/linux/mfd/wcd9xxx/Kbuild
new file mode 100644
index 0000000..8e55965
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/Kbuild
@@ -0,0 +1,2 @@
+header-y += wcd9xxx_registers.h
+header-y += wcd9320_registers.h
diff --git a/include/linux/mfd/wcd9xxx/core.h b/include/linux/mfd/wcd9xxx/core.h
new file mode 100644
index 0000000..8a507d2
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/core.h
@@ -0,0 +1,436 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MFD_TABLA_CORE_H__
+#define __MFD_TABLA_CORE_H__
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/of_irq.h>
+#include <linux/interrupt.h>
+#include <linux/pm_qos.h>
+
+#define WCD9XXX_MAX_IRQ_REGS 4
+#define WCD9XXX_MAX_NUM_IRQS (WCD9XXX_MAX_IRQ_REGS * 8)
+#define WCD9XXX_SLIM_NUM_PORT_REG 3
+#define TABLA_VERSION_1_0	0
+#define TABLA_VERSION_1_1	1
+#define TABLA_VERSION_2_0	2
+#define TABLA_IS_1_X(ver) \
+	(((ver == TABLA_VERSION_1_0) || (ver == TABLA_VERSION_1_1)) ? 1 : 0)
+#define TABLA_IS_2_0(ver) ((ver == TABLA_VERSION_2_0) ? 1 : 0)
+
+#define WCD9XXX_SUPPLY_BUCK_NAME "cdc-vdd-buck"
+
+#define SITAR_VERSION_1P0 0
+#define SITAR_VERSION_1P1 1
+#define SITAR_IS_1P0(ver) \
+	((ver == SITAR_VERSION_1P0) ? 1 : 0)
+#define SITAR_IS_1P1(ver) \
+	((ver == SITAR_VERSION_1P1) ? 1 : 0)
+
+#define TAIKO_VERSION_1_0	1
+#define TAIKO_IS_1_0(ver) \
+	((ver == TAIKO_VERSION_1_0) ? 1 : 0)
+
+#define TAPAN_VERSION_1_0	0
+#define TAPAN_IS_1_0(ver) \
+	((ver == TAPAN_VERSION_1_0) ? 1 : 0)
+
+#define TOMTOM_VERSION_1_0	1
+#define TOMTOM_IS_1_0(ver) \
+	((ver == TOMTOM_VERSION_1_0) ? 1 : 0)
+
+#define TASHA_VERSION_1_0     0
+#define TASHA_VERSION_1_1     1
+#define TASHA_VERSION_2_0     2
+
+#define TASHA_IS_1_0(wcd) \
+	((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
+	((wcd->version == TASHA_VERSION_1_0) ? 1 : 0) : 0)
+
+#define TASHA_IS_1_1(wcd) \
+	((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
+	((wcd->version == TASHA_VERSION_1_1) ? 1 : 0) : 0)
+
+#define TASHA_IS_2_0(wcd) \
+	((wcd->type == WCD9335 || wcd->type == WCD9326) ? \
+	((wcd->version == TASHA_VERSION_2_0) ? 1 : 0) : 0)
+
+/*
+ * As fine version info cannot be retrieved before tavil probe.
+ * Define three coarse versions for possible future use before tavil probe.
+ */
+#define TAVIL_VERSION_1_0             0
+#define TAVIL_VERSION_1_1             1
+#define TAVIL_VERSION_WCD9340_1_0     2
+#define TAVIL_VERSION_WCD9341_1_0     3
+#define TAVIL_VERSION_WCD9340_1_1     4
+#define TAVIL_VERSION_WCD9341_1_1     5
+
+#define TAVIL_IS_1_0(wcd) \
+	((wcd->type == WCD934X) ? \
+	 ((wcd->version == TAVIL_VERSION_1_0 || \
+	   wcd->version == TAVIL_VERSION_WCD9340_1_0 || \
+	   wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
+#define TAVIL_IS_1_1(wcd) \
+	((wcd->type == WCD934X) ? \
+	 ((wcd->version == TAVIL_VERSION_1_1 || \
+	   wcd->version == TAVIL_VERSION_WCD9340_1_1 || \
+	   wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9340_1_0(wcd) \
+	((wcd->type == WCD934X) ? \
+	 ((wcd->version == TAVIL_VERSION_WCD9340_1_0) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9341_1_0(wcd) \
+	((wcd->type == WCD934X) ? \
+	 ((wcd->version == TAVIL_VERSION_WCD9341_1_0) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9340_1_1(wcd) \
+	((wcd->type == WCD934X) ? \
+	 ((wcd->version == TAVIL_VERSION_WCD9340_1_1) ? 1 : 0) : 0)
+#define TAVIL_IS_WCD9341_1_1(wcd) \
+	((wcd->type == WCD934X) ? \
+	 ((wcd->version == TAVIL_VERSION_WCD9341_1_1) ? 1 : 0) : 0)
+
+#define IS_CODEC_TYPE(wcd, wcdtype) \
+	((wcd->type == wcdtype) ? true : false)
+#define IS_CODEC_VERSION(wcd, wcdversion) \
+	((wcd->version == wcdversion) ? true : false)
+
+enum {
+	CDC_V_1_0,
+	CDC_V_1_1,
+	CDC_V_2_0,
+};
+
+enum codec_variant {
+	WCD9XXX,
+	WCD9330,
+	WCD9335,
+	WCD9326,
+	WCD934X,
+};
+
+enum wcd9xxx_slim_slave_addr_type {
+	WCD9XXX_SLIM_SLAVE_ADDR_TYPE_0,
+	WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1,
+};
+
+enum wcd9xxx_pm_state {
+	WCD9XXX_PM_SLEEPABLE,
+	WCD9XXX_PM_AWAKE,
+	WCD9XXX_PM_ASLEEP,
+};
+
+enum {
+	WCD9XXX_INTR_STATUS_BASE = 0,
+	WCD9XXX_INTR_CLEAR_BASE,
+	WCD9XXX_INTR_MASK_BASE,
+	WCD9XXX_INTR_LEVEL_BASE,
+	WCD9XXX_INTR_CLR_COMMIT,
+	WCD9XXX_INTR_REG_MAX,
+};
+
+enum wcd9xxx_intf_status {
+	WCD9XXX_INTERFACE_TYPE_PROBING,
+	WCD9XXX_INTERFACE_TYPE_SLIMBUS,
+	WCD9XXX_INTERFACE_TYPE_I2C,
+};
+
+enum {
+	/* INTR_REG 0 */
+	WCD9XXX_IRQ_SLIMBUS = 0,
+	WCD9XXX_IRQ_MBHC_REMOVAL,
+	WCD9XXX_IRQ_MBHC_SHORT_TERM,
+	WCD9XXX_IRQ_MBHC_PRESS,
+	WCD9XXX_IRQ_MBHC_RELEASE,
+	WCD9XXX_IRQ_MBHC_POTENTIAL,
+	WCD9XXX_IRQ_MBHC_INSERTION,
+	WCD9XXX_IRQ_BG_PRECHARGE,
+	/* INTR_REG 1 */
+	WCD9XXX_IRQ_PA1_STARTUP,
+	WCD9XXX_IRQ_PA2_STARTUP,
+	WCD9XXX_IRQ_PA3_STARTUP,
+	WCD9XXX_IRQ_PA4_STARTUP,
+	WCD9306_IRQ_HPH_PA_OCPR_FAULT = WCD9XXX_IRQ_PA4_STARTUP,
+	WCD9XXX_IRQ_PA5_STARTUP,
+	WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
+	WCD9306_IRQ_HPH_PA_OCPL_FAULT = WCD9XXX_IRQ_MICBIAS1_PRECHARGE,
+	WCD9XXX_IRQ_MICBIAS2_PRECHARGE,
+	WCD9XXX_IRQ_MICBIAS3_PRECHARGE,
+	/* INTR_REG 2 */
+	WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+	WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+	WCD9XXX_IRQ_EAR_PA_OCPL_FAULT,
+	WCD9XXX_IRQ_HPH_L_PA_STARTUP,
+	WCD9XXX_IRQ_HPH_R_PA_STARTUP,
+	WCD9320_IRQ_EAR_PA_STARTUP,
+	WCD9306_IRQ_MBHC_JACK_SWITCH = WCD9320_IRQ_EAR_PA_STARTUP,
+	WCD9310_NUM_IRQS,
+	WCD9XXX_IRQ_RESERVED_0 = WCD9310_NUM_IRQS,
+	WCD9XXX_IRQ_RESERVED_1,
+	WCD9330_IRQ_SVASS_ERR_EXCEPTION = WCD9310_NUM_IRQS,
+	WCD9330_IRQ_MBHC_JACK_SWITCH,
+	/* INTR_REG 3 */
+	WCD9XXX_IRQ_MAD_AUDIO,
+	WCD9XXX_IRQ_MAD_ULTRASOUND,
+	WCD9XXX_IRQ_MAD_BEACON,
+	WCD9XXX_IRQ_SPEAKER_CLIPPING,
+	WCD9320_IRQ_MBHC_JACK_SWITCH,
+	WCD9306_NUM_IRQS,
+	WCD9XXX_IRQ_VBAT_MONITOR_ATTACK = WCD9306_NUM_IRQS,
+	WCD9XXX_IRQ_VBAT_MONITOR_RELEASE,
+	WCD9XXX_NUM_IRQS,
+	/* WCD9330 INTR1_REG 3*/
+	WCD9330_IRQ_SVASS_ENGINE = WCD9XXX_IRQ_MAD_AUDIO,
+	WCD9330_IRQ_MAD_AUDIO,
+	WCD9330_IRQ_MAD_ULTRASOUND,
+	WCD9330_IRQ_MAD_BEACON,
+	WCD9330_IRQ_SPEAKER1_CLIPPING,
+	WCD9330_IRQ_SPEAKER2_CLIPPING,
+	WCD9330_IRQ_VBAT_MONITOR_ATTACK,
+	WCD9330_IRQ_VBAT_MONITOR_RELEASE,
+	WCD9330_NUM_IRQS,
+	WCD9XXX_IRQ_RESERVED_2 = WCD9330_NUM_IRQS,
+};
+
+enum {
+	TABLA_NUM_IRQS = WCD9310_NUM_IRQS,
+	SITAR_NUM_IRQS = WCD9310_NUM_IRQS,
+	TAIKO_NUM_IRQS = WCD9XXX_NUM_IRQS,
+	TAPAN_NUM_IRQS = WCD9306_NUM_IRQS,
+	TOMTOM_NUM_IRQS = WCD9330_NUM_IRQS,
+};
+
+struct intr_data {
+	int intr_num;
+	bool clear_first;
+};
+
+struct wcd9xxx_core_resource {
+	struct mutex irq_lock;
+	struct mutex nested_irq_lock;
+
+	enum wcd9xxx_pm_state pm_state;
+	struct mutex pm_lock;
+	/* pm_wq notifies change of pm_state */
+	wait_queue_head_t pm_wq;
+	struct pm_qos_request pm_qos_req;
+	int wlock_holders;
+
+
+	/* holds the table of interrupts per codec */
+	const struct intr_data *intr_table;
+	int intr_table_size;
+	unsigned int irq_base;
+	unsigned int irq;
+	u8 irq_masks_cur[WCD9XXX_MAX_IRQ_REGS];
+	u8 irq_masks_cache[WCD9XXX_MAX_IRQ_REGS];
+	bool irq_level_high[WCD9XXX_MAX_NUM_IRQS];
+	int num_irqs;
+	int num_irq_regs;
+	u16 intr_reg[WCD9XXX_INTR_REG_MAX];
+	struct regmap *wcd_core_regmap;
+
+	/* Pointer to parent container data structure */
+	void *parent;
+
+	struct device *dev;
+	struct irq_domain *domain;
+};
+
+/*
+ * data structure for Slimbus and I2S channel.
+ * Some of fields are only used in smilbus mode
+ */
+struct wcd9xxx_ch {
+	u32 sph;		/* share channel handle - slimbus only	*/
+	u32 ch_num;		/*
+				 * vitrual channel number, such as 128 -144.
+				 * apply for slimbus only
+				 */
+	u16 ch_h;		/* chanel handle - slimbus only */
+	u16 port;		/*
+				 * tabla port for RX and TX
+				 * such as 0-9 for TX and 10 -16 for RX
+				 * apply for both i2s and slimbus
+				 */
+	u16 shift;		/*
+				 * shift bit for RX and TX
+				 * apply for both i2s and slimbus
+				 */
+	struct list_head list;	/*
+				 * channel link list
+				 * apply for both i2s and slimbus
+				 */
+};
+
+struct wcd9xxx_codec_dai_data {
+	u32 rate;				/* sample rate          */
+	u32 bit_width;				/* sit width 16,24,32   */
+	struct list_head wcd9xxx_ch_list;	/* channel list         */
+	u16 grph;				/* slimbus group handle */
+	unsigned long ch_mask;
+	wait_queue_head_t dai_wait;
+	bool bus_down_in_recovery;
+};
+
+#define WCD9XXX_CH(xport, xshift) \
+	{.port = xport, .shift = xshift}
+
+enum wcd9xxx_chipid_major {
+	TABLA_MAJOR = cpu_to_le16(0x100),
+	SITAR_MAJOR = cpu_to_le16(0x101),
+	TAIKO_MAJOR = cpu_to_le16(0x102),
+	TAPAN_MAJOR = cpu_to_le16(0x103),
+	TOMTOM_MAJOR = cpu_to_le16(0x105),
+	TASHA_MAJOR = cpu_to_le16(0x0),
+	TASHA2P0_MAJOR = cpu_to_le16(0x107),
+	TAVIL_MAJOR = cpu_to_le16(0x108),
+};
+
+enum codec_power_states {
+	WCD_REGION_POWER_COLLAPSE_REMOVE,
+	WCD_REGION_POWER_COLLAPSE_BEGIN,
+	WCD_REGION_POWER_DOWN,
+};
+
+enum wcd_power_regions {
+	WCD9XXX_DIG_CORE_REGION_1,
+	WCD9XXX_MAX_PWR_REGIONS,
+};
+
+struct wcd9xxx_codec_type {
+	u16 id_major;
+	u16 id_minor;
+	struct mfd_cell *dev;
+	int size;
+	int num_irqs;
+	int version; /* -1 to retrieve version from chip version register */
+	enum wcd9xxx_slim_slave_addr_type slim_slave_type;
+	u16 i2c_chip_status;
+	const struct intr_data *intr_tbl;
+	int intr_tbl_size;
+	u16 intr_reg[WCD9XXX_INTR_REG_MAX];
+};
+
+struct wcd9xxx_power_region {
+	enum codec_power_states power_state;
+	u16 pwr_collapse_reg_min;
+	u16 pwr_collapse_reg_max;
+};
+
+struct wcd9xxx {
+	struct device *dev;
+	struct slim_device *slim;
+	struct slim_device *slim_slave;
+	struct mutex io_lock;
+	struct mutex xfer_lock;
+	u8 version;
+
+	int reset_gpio;
+	struct device_node *wcd_rst_np;
+
+	int (*read_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *dest, bool interface_reg);
+	int (*write_dev)(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			int bytes, void *src, bool interface_reg);
+	int (*multi_reg_write)(struct wcd9xxx *wcd9xxx, const void *data,
+			       size_t count);
+	int (*dev_down)(struct wcd9xxx *wcd9xxx);
+	int (*post_reset)(struct wcd9xxx *wcd9xxx);
+
+	void *ssr_priv;
+	bool dev_up;
+
+	u32 num_of_supplies;
+	struct regulator_bulk_data *supplies;
+
+	struct wcd9xxx_core_resource core_res;
+
+	u16 id_minor;
+	u16 id_major;
+
+	/* Slimbus or I2S port */
+	u32 num_rx_port;
+	u32 num_tx_port;
+	struct wcd9xxx_ch *rx_chs;
+	struct wcd9xxx_ch *tx_chs;
+	u32 mclk_rate;
+	enum codec_variant type;
+	struct regmap *regmap;
+
+	struct wcd9xxx_codec_type *codec_type;
+	bool prev_pg_valid;
+	u8 prev_pg;
+	u8 avoid_cdc_rstlow;
+	struct wcd9xxx_power_region *wcd9xxx_pwr[WCD9XXX_MAX_PWR_REGIONS];
+};
+
+struct wcd9xxx_reg_val {
+	unsigned short reg; /* register address */
+	u8 *buf;            /* buffer to be written to reg. addr */
+	int bytes;          /* number of bytes to be written */
+};
+
+int wcd9xxx_interface_reg_read(struct wcd9xxx *wcd9xxx, unsigned short reg);
+int wcd9xxx_interface_reg_write(struct wcd9xxx *wcd9xxx, unsigned short reg,
+		u8 val);
+int wcd9xxx_get_logical_addresses(u8 *pgd_la, u8 *inf_la);
+int wcd9xxx_slim_write_repeat(struct wcd9xxx *wcd9xxx, unsigned short reg,
+			     int bytes, void *src);
+int wcd9xxx_slim_reserve_bw(struct wcd9xxx *wcd9xxx,
+			    u32 bw_ops, bool commit);
+int wcd9xxx_set_power_state(struct wcd9xxx *wcd9xxx, enum codec_power_states,
+			    enum wcd_power_regions);
+int wcd9xxx_get_current_power_state(struct wcd9xxx *wcd9xxx,
+				    enum wcd_power_regions);
+
+int wcd9xxx_page_write(struct wcd9xxx *wcd9xxx, unsigned short *reg);
+
+int wcd9xxx_slim_bulk_write(struct wcd9xxx *wcd9xxx,
+			    struct wcd9xxx_reg_val *bulk_reg,
+			    unsigned int size, bool interface);
+
+extern int wcd9xxx_core_res_init(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	int num_irqs, int num_irq_regs, struct regmap *wcd_regmap);
+
+extern void wcd9xxx_core_res_deinit(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res);
+
+extern int wcd9xxx_core_res_suspend(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res,
+	pm_message_t pmesg);
+
+extern int wcd9xxx_core_res_resume(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res);
+
+extern int wcd9xxx_core_irq_init(
+	struct wcd9xxx_core_resource *wcd9xxx_core_res);
+
+extern int wcd9xxx_assign_irq(struct wcd9xxx_core_resource *wcd9xxx_core_res,
+			      unsigned int irq,
+			      unsigned int irq_base);
+
+extern enum wcd9xxx_intf_status wcd9xxx_get_intf_type(void);
+extern void wcd9xxx_set_intf_type(enum wcd9xxx_intf_status);
+
+extern enum wcd9xxx_pm_state wcd9xxx_pm_cmpxchg(
+			struct wcd9xxx_core_resource *wcd9xxx_core_res,
+			enum wcd9xxx_pm_state o,
+			enum wcd9xxx_pm_state n);
+static inline int __init wcd9xxx_irq_of_init(struct device_node *node,
+			       struct device_node *parent)
+{
+	return 0;
+}
+#endif
diff --git a/include/linux/mfd/wcd9xxx/pdata.h b/include/linux/mfd/wcd9xxx/pdata.h
new file mode 100644
index 0000000..f188e85
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/pdata.h
@@ -0,0 +1,197 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MFD_WCD9XXX_PDATA_H__
+
+#define __MFD_WCD9XXX_PDATA_H__
+
+#include <linux/slimbus/slimbus.h>
+#include <linux/mfd/msm-cdc-supply.h>
+
+#define MICBIAS_EXT_BYP_CAP 0x00
+#define MICBIAS_NO_EXT_BYP_CAP 0x01
+
+#define SITAR_LDOH_1P95_V 0x0
+#define SITAR_LDOH_2P35_V 0x1
+#define SITAR_LDOH_2P75_V 0x2
+#define SITAR_LDOH_2P85_V 0x3
+
+#define SITAR_CFILT1_SEL 0x0
+#define SITAR_CFILT2_SEL 0x1
+#define SITAR_CFILT3_SEL 0x2
+
+#define WCD9XXX_LDOH_1P95_V 0x0
+#define WCD9XXX_LDOH_2P35_V 0x1
+#define WCD9XXX_LDOH_2P75_V 0x2
+#define WCD9XXX_LDOH_2P85_V 0x3
+#define WCD9XXX_LDOH_3P0_V 0x3
+
+#define TABLA_LDOH_1P95_V 0x0
+#define TABLA_LDOH_2P35_V 0x1
+#define TABLA_LDOH_2P75_V 0x2
+#define TABLA_LDOH_2P85_V 0x3
+
+#define TABLA_CFILT1_SEL 0x0
+#define TABLA_CFILT2_SEL 0x1
+#define TABLA_CFILT3_SEL 0x2
+
+#define MAX_AMIC_CHANNEL 7
+
+#define TABLA_OCP_300_MA 0x0
+#define TABLA_OCP_350_MA 0x2
+#define TABLA_OCP_365_MA 0x3
+#define TABLA_OCP_150_MA 0x4
+#define TABLA_OCP_190_MA 0x6
+#define TABLA_OCP_220_MA 0x7
+
+#define TABLA_DCYCLE_255  0x0
+#define TABLA_DCYCLE_511  0x1
+#define TABLA_DCYCLE_767  0x2
+#define TABLA_DCYCLE_1023 0x3
+#define TABLA_DCYCLE_1279 0x4
+#define TABLA_DCYCLE_1535 0x5
+#define TABLA_DCYCLE_1791 0x6
+#define TABLA_DCYCLE_2047 0x7
+#define TABLA_DCYCLE_2303 0x8
+#define TABLA_DCYCLE_2559 0x9
+#define TABLA_DCYCLE_2815 0xA
+#define TABLA_DCYCLE_3071 0xB
+#define TABLA_DCYCLE_3327 0xC
+#define TABLA_DCYCLE_3583 0xD
+#define TABLA_DCYCLE_3839 0xE
+#define TABLA_DCYCLE_4095 0xF
+
+#define WCD9XXX_MCLK_CLK_12P288MHZ 12288000
+#define WCD9XXX_MCLK_CLK_9P6HZ 9600000
+
+/* Only valid for 9.6 MHz mclk */
+#define WCD9XXX_DMIC_SAMPLE_RATE_600KHZ 600000
+#define WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ 2400000
+#define WCD9XXX_DMIC_SAMPLE_RATE_3P2MHZ 3200000
+#define WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ 4800000
+
+/* Only valid for 12.288 MHz mclk */
+#define WCD9XXX_DMIC_SAMPLE_RATE_768KHZ 768000
+#define WCD9XXX_DMIC_SAMPLE_RATE_2P048MHZ 2048000
+#define WCD9XXX_DMIC_SAMPLE_RATE_3P072MHZ 3072000
+#define WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ 4096000
+#define WCD9XXX_DMIC_SAMPLE_RATE_6P144MHZ 6144000
+
+#define WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED 0
+
+#define WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED 0
+
+struct wcd9xxx_amic {
+	/*legacy mode, txfe_enable and txfe_buff take 7 input
+	 * each bit represent the channel / TXFE number
+	 * and numbered as below
+	 * bit 0 = channel 1 / TXFE1_ENABLE / TXFE1_BUFF
+	 * bit 1 = channel 2 / TXFE2_ENABLE / TXFE2_BUFF
+	 * ...
+	 * bit 7 = channel 7 / TXFE7_ENABLE / TXFE7_BUFF
+	 */
+	u8 legacy_mode:MAX_AMIC_CHANNEL;
+	u8 txfe_enable:MAX_AMIC_CHANNEL;
+	u8 txfe_buff:MAX_AMIC_CHANNEL;
+	u8 use_pdata:MAX_AMIC_CHANNEL;
+};
+
+/* Each micbias can be assigned to one of three cfilters
+ * Vbatt_min >= .15V + ldoh_v
+ * ldoh_v >= .15v + cfiltx_mv
+ * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv
+ * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv
+ * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv
+ * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv
+ */
+
+struct wcd9xxx_micbias_setting {
+	u8 ldoh_v;
+	u32 cfilt1_mv; /* in mv */
+	u32 cfilt2_mv; /* in mv */
+	u32 cfilt3_mv; /* in mv */
+	u32 micb1_mv;
+	u32 micb2_mv;
+	u32 micb3_mv;
+	u32 micb4_mv;
+	/* Different WCD9xxx series codecs may not
+	 * have 4 mic biases. If a codec has fewer
+	 * mic biases, some of these properties will
+	 * not be used.
+	 */
+	u8 bias1_cfilt_sel;
+	u8 bias2_cfilt_sel;
+	u8 bias3_cfilt_sel;
+	u8 bias4_cfilt_sel;
+	u8 bias1_cap_mode;
+	u8 bias2_cap_mode;
+	u8 bias3_cap_mode;
+	u8 bias4_cap_mode;
+	bool bias2_is_headset_only;
+};
+
+struct wcd9xxx_ocp_setting {
+	unsigned int	use_pdata:1; /* 0 - use sys default as recommended */
+	unsigned int	num_attempts:4; /* up to 15 attempts */
+	unsigned int	run_time:4; /* in duty cycle */
+	unsigned int	wait_time:4; /* in duty cycle */
+	unsigned int	hph_ocp_limit:3; /* Headphone OCP current limit */
+};
+
+#define WCD9XXX_MAX_REGULATOR	9
+/*
+ *      format : TABLA_<POWER_SUPPLY_PIN_NAME>_CUR_MAX
+ *
+ *      <POWER_SUPPLY_PIN_NAME> from Tabla objective spec
+ */
+
+#define  WCD9XXX_CDC_VDDA_CP_CUR_MAX      500000
+#define  WCD9XXX_CDC_VDDA_RX_CUR_MAX      20000
+#define  WCD9XXX_CDC_VDDA_TX_CUR_MAX      20000
+#define  WCD9XXX_VDDIO_CDC_CUR_MAX        5000
+
+#define  WCD9XXX_VDDD_CDC_D_CUR_MAX       5000
+#define  WCD9XXX_VDDD_CDC_A_CUR_MAX       5000
+
+#define WCD9XXX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv"
+#define WCD9XXX_VDD_SPKDRV2_NAME "cdc-vdd-spkdrv-2"
+
+struct wcd9xxx_regulator {
+	const char *name;
+	int min_uV;
+	int max_uV;
+	int optimum_uA;
+	bool ondemand;
+	struct regulator *regulator;
+};
+
+struct wcd9xxx_pdata {
+	int irq;
+	int irq_base;
+	int num_irqs;
+	int reset_gpio;
+	struct device_node *wcd_rst_np;
+	struct wcd9xxx_amic amic_settings;
+	struct slim_device slimbus_slave_device;
+	struct wcd9xxx_micbias_setting micbias;
+	struct wcd9xxx_ocp_setting ocp;
+	struct cdc_regulator *regulator;
+	int num_supplies;
+	u32 mclk_rate;
+	u32 dmic_sample_rate;
+	u32 mad_dmic_sample_rate;
+	u32 ecpp_dmic_sample_rate;
+	u32 dmic_clk_drv;
+	u16 use_pinctrl;
+};
+
+#endif
diff --git a/include/linux/mfd/wcd9xxx/wcd9330_registers.h b/include/linux/mfd/wcd9xxx/wcd9330_registers.h
new file mode 100644
index 0000000..c37d25f
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/wcd9330_registers.h
@@ -0,0 +1,1626 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WCD9330_REGISTERS_H
+#define WCD9330_REGISTERS_H
+
+#include <linux/types.h>
+
+#define TOMTOM_A_CHIP_CTL			(0x000)
+#define TOMTOM_A_CHIP_CTL__POR				(0x38)
+#define TOMTOM_A_CHIP_STATUS			(0x001)
+#define TOMTOM_A_CHIP_STATUS__POR				(0x00)
+#define TOMTOM_A_CHIP_ID_BYTE_0			(0x004)
+#define TOMTOM_A_CHIP_ID_BYTE_0__POR				(0x00)
+#define TOMTOM_A_CHIP_ID_BYTE_1			(0x005)
+#define TOMTOM_A_CHIP_ID_BYTE_1__POR				(0x00)
+#define TOMTOM_A_CHIP_ID_BYTE_2			(0x006)
+#define TOMTOM_A_CHIP_ID_BYTE_2__POR				(0x05)
+#define TOMTOM_A_CHIP_ID_BYTE_3			(0x007)
+#define TOMTOM_A_CHIP_ID_BYTE_3__POR				(0x01)
+#define TOMTOM_A_CHIP_I2C_SLAVE_ID			(0x008)
+#define TOMTOM_A_CHIP_I2C_SLAVE_ID__POR				(0x01)
+#define TOMTOM_A_SLAVE_ID_1			(0x00C)
+#define TOMTOM_A_SLAVE_ID_1__POR				(0x77)
+#define TOMTOM_A_SLAVE_ID_2			(0x00D)
+#define TOMTOM_A_SLAVE_ID_2__POR				(0x66)
+#define TOMTOM_A_SLAVE_ID_3			(0x00E)
+#define TOMTOM_A_SLAVE_ID_3__POR				(0x55)
+#define TOMTOM_A_PIN_CTL_OE0			(0x010)
+#define TOMTOM_A_PIN_CTL_OE0__POR				(0x00)
+#define TOMTOM_A_PIN_CTL_OE1			(0x011)
+#define TOMTOM_A_PIN_CTL_OE1__POR				(0x00)
+#define TOMTOM_A_PIN_CTL_OE2			(0x012)
+#define TOMTOM_A_PIN_CTL_OE2__POR				(0x00)
+#define TOMTOM_A_PIN_CTL_DATA0			(0x013)
+#define TOMTOM_A_PIN_CTL_DATA0__POR				(0x00)
+#define TOMTOM_A_PIN_CTL_DATA1			(0x014)
+#define TOMTOM_A_PIN_CTL_DATA1__POR				(0x00)
+#define TOMTOM_A_PIN_CTL_DATA2			(0x015)
+#define TOMTOM_A_PIN_CTL_DATA2__POR				(0x00)
+#define TOMTOM_A_HDRIVE_GENERIC			(0x018)
+#define TOMTOM_A_HDRIVE_GENERIC__POR				(0x00)
+#define TOMTOM_A_HDRIVE_OVERRIDE			(0x019)
+#define TOMTOM_A_HDRIVE_OVERRIDE__POR				(0x08)
+#define TOMTOM_A_ANA_CSR_WAIT_STATE			(0x01C)
+#define TOMTOM_A_ANA_CSR_WAIT_STATE__POR				(0x44)
+#define TOMTOM_A_PROCESS_MONITOR_CTL0			(0x020)
+#define TOMTOM_A_PROCESS_MONITOR_CTL0__POR				(0x80)
+#define TOMTOM_A_PROCESS_MONITOR_CTL1			(0x021)
+#define TOMTOM_A_PROCESS_MONITOR_CTL1__POR				(0x00)
+#define TOMTOM_A_PROCESS_MONITOR_CTL2			(0x022)
+#define TOMTOM_A_PROCESS_MONITOR_CTL2__POR				(0x00)
+#define TOMTOM_A_PROCESS_MONITOR_CTL3			(0x023)
+#define TOMTOM_A_PROCESS_MONITOR_CTL3__POR				(0x01)
+#define TOMTOM_A_QFUSE_CTL			(0x028)
+#define TOMTOM_A_QFUSE_CTL__POR				(0x00)
+#define TOMTOM_A_QFUSE_STATUS			(0x029)
+#define TOMTOM_A_QFUSE_STATUS__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT0			(0x02A)
+#define TOMTOM_A_QFUSE_DATA_OUT0__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT1			(0x02B)
+#define TOMTOM_A_QFUSE_DATA_OUT1__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT2			(0x02C)
+#define TOMTOM_A_QFUSE_DATA_OUT2__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT3			(0x02D)
+#define TOMTOM_A_QFUSE_DATA_OUT3__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT4			(0x02E)
+#define TOMTOM_A_QFUSE_DATA_OUT4__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT5			(0x02F)
+#define TOMTOM_A_QFUSE_DATA_OUT5__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT6			(0x030)
+#define TOMTOM_A_QFUSE_DATA_OUT6__POR				(0x00)
+#define TOMTOM_A_QFUSE_DATA_OUT7			(0x031)
+#define TOMTOM_A_QFUSE_DATA_OUT7__POR				(0x00)
+#define TOMTOM_A_CDC_CTL			(0x034)
+#define TOMTOM_A_CDC_CTL__POR				(0x00)
+#define TOMTOM_A_LEAKAGE_CTL			(0x03C)
+#define TOMTOM_A_LEAKAGE_CTL__POR				(0x04)
+#define TOMTOM_A_SVASS_MEM_PTR0			(0x044)
+#define TOMTOM_A_SVASS_MEM_PTR0__POR				(0x00)
+#define TOMTOM_A_SVASS_MEM_PTR1			(0x045)
+#define TOMTOM_A_SVASS_MEM_PTR1__POR				(0x00)
+#define TOMTOM_A_SVASS_MEM_PTR2			(0x046)
+#define TOMTOM_A_SVASS_MEM_PTR2__POR				(0x00)
+#define TOMTOM_A_SVASS_MEM_CTL			(0x048)
+#define TOMTOM_A_SVASS_MEM_CTL__POR				(0x04)
+#define TOMTOM_A_SVASS_MEM_BANK			(0x049)
+#define TOMTOM_A_SVASS_MEM_BANK__POR				(0x00)
+#define TOMTOM_A_DMIC_B1_CTL			(0x04A)
+#define TOMTOM_A_DMIC_B1_CTL__POR				(0x00)
+#define TOMTOM_A_DMIC_B2_CTL			(0x04B)
+#define TOMTOM_A_DMIC_B2_CTL__POR				(0x00)
+#define TOMTOM_A_SVASS_CLKRST_CTL			(0x04C)
+#define TOMTOM_A_SVASS_CLKRST_CTL__POR				(0x00)
+#define TOMTOM_A_SVASS_CPAR_CFG			(0x04D)
+#define TOMTOM_A_SVASS_CPAR_CFG__POR				(0x00)
+#define TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD			(0x04E)
+#define TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR				(0x14)
+#define TOMTOM_A_SVASS_CPAR_WDOG_CFG			(0x04F)
+#define TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR				(0x00)
+#define TOMTOM_A_SVASS_CFG			(0x050)
+#define TOMTOM_A_SVASS_CFG__POR				(0x01)
+#define TOMTOM_A_SVASS_SPE_CFG			(0x051)
+#define TOMTOM_A_SVASS_SPE_CFG__POR				(0x04)
+#define TOMTOM_A_SVASS_STATUS			(0x052)
+#define TOMTOM_A_SVASS_STATUS__POR				(0x00)
+#define TOMTOM_A_SVASS_INT_MASK			(0x053)
+#define TOMTOM_A_SVASS_INT_MASK__POR				(0x3F)
+#define TOMTOM_A_SVASS_INT_STATUS			(0x054)
+#define TOMTOM_A_SVASS_INT_STATUS__POR				(0x00)
+#define TOMTOM_A_SVASS_INT_CLR			(0x055)
+#define TOMTOM_A_SVASS_INT_CLR__POR				(0x00)
+#define TOMTOM_A_SVASS_DEBUG			(0x056)
+#define TOMTOM_A_SVASS_DEBUG__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_BKUP_INT			(0x057)
+#define TOMTOM_A_SVASS_SPE_BKUP_INT__POR				(0x00)
+#define TOMTOM_A_SVASS_MEM_ACC			(0x058)
+#define TOMTOM_A_SVASS_MEM_ACC__POR				(0x00)
+#define TOMTOM_A_MEM_LEAKAGE_CTL			(0x059)
+#define TOMTOM_A_MEM_LEAKAGE_CTL__POR				(0x04)
+#define TOMTOM_A_SVASS_SPE_INBOX_TRG			(0x05A)
+#define TOMTOM_A_SVASS_SPE_INBOX_TRG__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_0			(0x060)
+#define TOMTOM_A_SVASS_SPE_INBOX_0__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_1			(0x061)
+#define TOMTOM_A_SVASS_SPE_INBOX_1__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_2			(0x062)
+#define TOMTOM_A_SVASS_SPE_INBOX_2__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_3			(0x063)
+#define TOMTOM_A_SVASS_SPE_INBOX_3__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_4			(0x064)
+#define TOMTOM_A_SVASS_SPE_INBOX_4__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_5			(0x065)
+#define TOMTOM_A_SVASS_SPE_INBOX_5__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_6			(0x066)
+#define TOMTOM_A_SVASS_SPE_INBOX_6__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_7			(0x067)
+#define TOMTOM_A_SVASS_SPE_INBOX_7__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_8			(0x068)
+#define TOMTOM_A_SVASS_SPE_INBOX_8__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_9			(0x069)
+#define TOMTOM_A_SVASS_SPE_INBOX_9__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_10			(0x06A)
+#define TOMTOM_A_SVASS_SPE_INBOX_10__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_INBOX_11			(0x06B)
+#define TOMTOM_A_SVASS_SPE_INBOX_11__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_0			(0x070)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_0__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_1			(0x071)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_1__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_2			(0x072)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_2__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_3			(0x073)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_3__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_4			(0x074)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_4__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_5			(0x075)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_5__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_6			(0x076)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_6__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_7			(0x077)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_7__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_8			(0x078)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_8__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_9			(0x079)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_9__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_10			(0x07A)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_10__POR				(0x00)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_11			(0x07B)
+#define TOMTOM_A_SVASS_SPE_OUTBOX_11__POR				(0x00)
+#define TOMTOM_A_INTR_MODE			(0x090)
+#define TOMTOM_A_INTR_MODE__POR				(0x00)
+#define TOMTOM_A_INTR1_MASK0			(0x094)
+#define TOMTOM_A_INTR1_MASK0__POR				(0xFF)
+#define TOMTOM_A_INTR1_MASK1			(0x095)
+#define TOMTOM_A_INTR1_MASK1__POR				(0xFF)
+#define TOMTOM_A_INTR1_MASK2			(0x096)
+#define TOMTOM_A_INTR1_MASK2__POR				(0xFF)
+#define TOMTOM_A_INTR1_MASK3			(0x097)
+#define TOMTOM_A_INTR1_MASK3__POR				(0xFF)
+#define TOMTOM_A_INTR1_STATUS0			(0x098)
+#define TOMTOM_A_INTR1_STATUS0__POR				(0x00)
+#define TOMTOM_A_INTR1_STATUS1			(0x099)
+#define TOMTOM_A_INTR1_STATUS1__POR				(0x00)
+#define TOMTOM_A_INTR1_STATUS2			(0x09A)
+#define TOMTOM_A_INTR1_STATUS2__POR				(0x00)
+#define TOMTOM_A_INTR1_STATUS3			(0x09B)
+#define TOMTOM_A_INTR1_STATUS3__POR				(0x00)
+#define TOMTOM_A_INTR1_CLEAR0			(0x09C)
+#define TOMTOM_A_INTR1_CLEAR0__POR				(0x00)
+#define TOMTOM_A_INTR1_CLEAR1			(0x09D)
+#define TOMTOM_A_INTR1_CLEAR1__POR				(0x00)
+#define TOMTOM_A_INTR1_CLEAR2			(0x09E)
+#define TOMTOM_A_INTR1_CLEAR2__POR				(0x00)
+#define TOMTOM_A_INTR1_CLEAR3			(0x09F)
+#define TOMTOM_A_INTR1_CLEAR3__POR				(0x00)
+#define TOMTOM_A_INTR1_LEVEL0			(0x0A0)
+#define TOMTOM_A_INTR1_LEVEL0__POR				(0x01)
+#define TOMTOM_A_INTR1_LEVEL1			(0x0A1)
+#define TOMTOM_A_INTR1_LEVEL1__POR				(0x00)
+#define TOMTOM_A_INTR1_LEVEL2			(0x0A2)
+#define TOMTOM_A_INTR1_LEVEL2__POR				(0x40)
+#define TOMTOM_A_INTR1_LEVEL3			(0x0A3)
+#define TOMTOM_A_INTR1_LEVEL3__POR				(0x00)
+#define TOMTOM_A_INTR1_TEST0			(0x0A4)
+#define TOMTOM_A_INTR1_TEST0__POR				(0x00)
+#define TOMTOM_A_INTR1_TEST1			(0x0A5)
+#define TOMTOM_A_INTR1_TEST1__POR				(0x00)
+#define TOMTOM_A_INTR1_TEST2			(0x0A6)
+#define TOMTOM_A_INTR1_TEST2__POR				(0x00)
+#define TOMTOM_A_INTR1_TEST3			(0x0A7)
+#define TOMTOM_A_INTR1_TEST3__POR				(0x00)
+#define TOMTOM_A_INTR1_SET0			(0x0A8)
+#define TOMTOM_A_INTR1_SET0__POR				(0x00)
+#define TOMTOM_A_INTR1_SET1			(0x0A9)
+#define TOMTOM_A_INTR1_SET1__POR				(0x00)
+#define TOMTOM_A_INTR1_SET2			(0x0AA)
+#define TOMTOM_A_INTR1_SET2__POR				(0x00)
+#define TOMTOM_A_INTR1_SET3			(0x0AB)
+#define TOMTOM_A_INTR1_SET3__POR				(0x00)
+#define TOMTOM_A_INTR2_MASK0			(0x0B0)
+#define TOMTOM_A_INTR2_MASK0__POR				(0xFF)
+#define TOMTOM_A_INTR2_STATUS0			(0x0B2)
+#define TOMTOM_A_INTR2_STATUS0__POR				(0x00)
+#define TOMTOM_A_INTR2_CLEAR0			(0x0B4)
+#define TOMTOM_A_INTR2_CLEAR0__POR				(0x00)
+#define TOMTOM_A_INTR2_LEVEL0			(0x0B6)
+#define TOMTOM_A_INTR2_LEVEL0__POR				(0x00)
+#define TOMTOM_A_INTR2_TEST0			(0x0B8)
+#define TOMTOM_A_INTR2_TEST0__POR				(0x00)
+#define TOMTOM_A_INTR2_SET0			(0x0BA)
+#define TOMTOM_A_INTR2_SET0__POR				(0x00)
+#define TOMTOM_A_CDC_TX_I2S_SCK_MODE			(0x0C0)
+#define TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_TX_I2S_WS_MODE			(0x0C1)
+#define TOMTOM_A_CDC_TX_I2S_WS_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_DMIC_DATA0_MODE			(0x0C4)
+#define TOMTOM_A_CDC_DMIC_DATA0_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_DMIC_CLK0_MODE			(0x0C5)
+#define TOMTOM_A_CDC_DMIC_CLK0_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_DMIC_DATA1_MODE			(0x0C6)
+#define TOMTOM_A_CDC_DMIC_DATA1_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_DMIC_CLK1_MODE			(0x0C7)
+#define TOMTOM_A_CDC_DMIC_CLK1_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_RX_I2S_SCK_MODE			(0x0C8)
+#define TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_RX_I2S_WS_MODE			(0x0C9)
+#define TOMTOM_A_CDC_RX_I2S_WS_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_DMIC_DATA2_MODE			(0x0CA)
+#define TOMTOM_A_CDC_DMIC_DATA2_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_DMIC_CLK2_MODE			(0x0CB)
+#define TOMTOM_A_CDC_DMIC_CLK2_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_INTR1_MODE			(0x0CC)
+#define TOMTOM_A_CDC_INTR1_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_SB_NRZ_SEL_MODE			(0x0CD)
+#define TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_INTR2_MODE			(0x0CE)
+#define TOMTOM_A_CDC_INTR2_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_RF_PA_ON_MODE			(0x0CF)
+#define TOMTOM_A_CDC_RF_PA_ON_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_BOOST_MODE			(0x0D0)
+#define TOMTOM_A_CDC_BOOST_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_JTCK_MODE			(0x0D1)
+#define TOMTOM_A_CDC_JTCK_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_JTDI_MODE			(0x0D2)
+#define TOMTOM_A_CDC_JTDI_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_JTMS_MODE			(0x0D3)
+#define TOMTOM_A_CDC_JTMS_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_JTDO_MODE			(0x0D4)
+#define TOMTOM_A_CDC_JTDO_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_JTRST_MODE			(0x0D5)
+#define TOMTOM_A_CDC_JTRST_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_BIST_MODE_MODE			(0x0D6)
+#define TOMTOM_A_CDC_BIST_MODE_MODE__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_MAIN_CTL_1			(0x0E0)
+#define TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_MAIN_CTL_2			(0x0E1)
+#define TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_1			(0x0E2)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_2			(0x0E3)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_3			(0x0E4)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_4			(0x0E5)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_5			(0x0E6)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_6			(0x0E7)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_7			(0x0E8)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_8			(0x0E9)
+#define TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR			(0x0EA)
+#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL			(0x0EB)
+#define TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR				(0x40)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_1			(0x0EC)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_2			(0x0ED)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_3			(0x0EE)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_4			(0x0EF)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_5			(0x0F0)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_6			(0x0F1)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_7			(0x0F2)
+#define TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_1			(0x0F3)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_2			(0x0F4)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_3			(0x0F5)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_4			(0x0F6)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_5			(0x0F7)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_6			(0x0F8)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_7			(0x0F9)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_8			(0x0FA)
+#define TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR				(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR			(0x0FB)
+#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR			(0x00)
+#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL			(0x0FC)
+#define TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR			(0x00)
+#define TOMTOM_A_CDC_MAD_INP_SEL			(0x0FD)
+#define TOMTOM_A_CDC_MAD_INP_SEL__POR				(0x00)
+#define TOMTOM_A_BIAS_REF_CTL			(0x100)
+#define TOMTOM_A_BIAS_REF_CTL__POR				(0x1C)
+#define TOMTOM_A_BIAS_CENTRAL_BG_CTL			(0x101)
+#define TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR				(0x50)
+#define TOMTOM_A_BIAS_PRECHRG_CTL			(0x102)
+#define TOMTOM_A_BIAS_PRECHRG_CTL__POR				(0x07)
+#define TOMTOM_A_BIAS_CURR_CTL_1			(0x103)
+#define TOMTOM_A_BIAS_CURR_CTL_1__POR				(0x52)
+#define TOMTOM_A_BIAS_CURR_CTL_2			(0x104)
+#define TOMTOM_A_BIAS_CURR_CTL_2__POR				(0x00)
+#define TOMTOM_A_BIAS_OSC_BG_CTL			(0x105)
+#define TOMTOM_A_BIAS_OSC_BG_CTL__POR				(0x36)
+#define TOMTOM_A_CLK_BUFF_EN1			(0x108)
+#define TOMTOM_A_CLK_BUFF_EN1__POR				(0x04)
+#define TOMTOM_A_CLK_BUFF_EN2			(0x109)
+#define TOMTOM_A_CLK_BUFF_EN2__POR				(0x02)
+#define TOMTOM_A_LDO_L_MODE_1			(0x10A)
+#define TOMTOM_A_LDO_L_MODE_1__POR				(0x08)
+#define TOMTOM_A_LDO_L_MODE_2			(0x10B)
+#define TOMTOM_A_LDO_L_MODE_2__POR				(0x50)
+#define TOMTOM_A_LDO_L_CTRL_1			(0x10C)
+#define TOMTOM_A_LDO_L_CTRL_1__POR				(0x70)
+#define TOMTOM_A_LDO_L_CTRL_2			(0x10D)
+#define TOMTOM_A_LDO_L_CTRL_2__POR				(0x55)
+#define TOMTOM_A_LDO_L_CTRL_3			(0x10E)
+#define TOMTOM_A_LDO_L_CTRL_3__POR				(0x56)
+#define TOMTOM_A_LDO_L_CTRL_4			(0x10F)
+#define TOMTOM_A_LDO_L_CTRL_4__POR				(0x55)
+#define TOMTOM_A_LDO_H_MODE_1			(0x110)
+#define TOMTOM_A_LDO_H_MODE_1__POR				(0x65)
+#define TOMTOM_A_LDO_H_MODE_2			(0x111)
+#define TOMTOM_A_LDO_H_MODE_2__POR				(0xA8)
+#define TOMTOM_A_LDO_H_LOOP_CTL			(0x112)
+#define TOMTOM_A_LDO_H_LOOP_CTL__POR				(0x6B)
+#define TOMTOM_A_LDO_H_COMP_1			(0x113)
+#define TOMTOM_A_LDO_H_COMP_1__POR				(0x84)
+#define TOMTOM_A_LDO_H_COMP_2			(0x114)
+#define TOMTOM_A_LDO_H_COMP_2__POR				(0xE0)
+#define TOMTOM_A_LDO_H_BIAS_1			(0x115)
+#define TOMTOM_A_LDO_H_BIAS_1__POR				(0x6D)
+#define TOMTOM_A_LDO_H_BIAS_2			(0x116)
+#define TOMTOM_A_LDO_H_BIAS_2__POR				(0xA5)
+#define TOMTOM_A_LDO_H_BIAS_3			(0x117)
+#define TOMTOM_A_LDO_H_BIAS_3__POR				(0x60)
+#define TOMTOM_A_VBAT_CLK			(0x118)
+#define TOMTOM_A_VBAT_CLK__POR				(0x03)
+#define TOMTOM_A_VBAT_LOOP			(0x119)
+#define TOMTOM_A_VBAT_LOOP__POR				(0x02)
+#define TOMTOM_A_VBAT_REF			(0x11A)
+#define TOMTOM_A_VBAT_REF__POR				(0x20)
+#define TOMTOM_A_VBAT_ADC_TEST			(0x11B)
+#define TOMTOM_A_VBAT_ADC_TEST__POR				(0x00)
+#define TOMTOM_A_VBAT_FE			(0x11C)
+#define TOMTOM_A_VBAT_FE__POR				(0x48)
+#define TOMTOM_A_VBAT_BIAS_1			(0x11D)
+#define TOMTOM_A_VBAT_BIAS_1__POR				(0x03)
+#define TOMTOM_A_VBAT_BIAS_2			(0x11E)
+#define TOMTOM_A_VBAT_BIAS_2__POR				(0x00)
+#define TOMTOM_A_VBAT_ADC_DATA_MSB			(0x11F)
+#define TOMTOM_A_VBAT_ADC_DATA_MSB__POR				(0x00)
+#define TOMTOM_A_VBAT_ADC_DATA_LSB			(0x120)
+#define TOMTOM_A_VBAT_ADC_DATA_LSB__POR				(0x00)
+#define TOMTOM_A_FLL_NREF			(0x121)
+#define TOMTOM_A_FLL_NREF__POR				(0x12)
+#define TOMTOM_A_FLL_KDCO_TUNE			(0x122)
+#define TOMTOM_A_FLL_KDCO_TUNE__POR				(0x05)
+#define TOMTOM_A_FLL_LOCK_THRESH			(0x123)
+#define TOMTOM_A_FLL_LOCK_THRESH__POR				(0xC2)
+#define TOMTOM_A_FLL_LOCK_DET_COUNT			(0x124)
+#define TOMTOM_A_FLL_LOCK_DET_COUNT__POR				(0x40)
+#define TOMTOM_A_FLL_DAC_THRESHOLD			(0x125)
+#define TOMTOM_A_FLL_DAC_THRESHOLD__POR				(0xC8)
+#define TOMTOM_A_FLL_TEST_DCO_FREERUN			(0x126)
+#define TOMTOM_A_FLL_TEST_DCO_FREERUN__POR				(0x00)
+#define TOMTOM_A_FLL_TEST_ENABLE			(0x127)
+#define TOMTOM_A_FLL_TEST_ENABLE__POR				(0x00)
+#define TOMTOM_A_MICB_CFILT_1_CTL			(0x128)
+#define TOMTOM_A_MICB_CFILT_1_CTL__POR				(0x40)
+#define TOMTOM_A_MICB_CFILT_1_VAL			(0x129)
+#define TOMTOM_A_MICB_CFILT_1_VAL__POR				(0x80)
+#define TOMTOM_A_MICB_CFILT_1_PRECHRG			(0x12A)
+#define TOMTOM_A_MICB_CFILT_1_PRECHRG__POR				(0x38)
+#define TOMTOM_A_MICB_1_CTL			(0x12B)
+#define TOMTOM_A_MICB_1_CTL__POR				(0x16)
+#define TOMTOM_A_MICB_1_INT_RBIAS			(0x12C)
+#define TOMTOM_A_MICB_1_INT_RBIAS__POR				(0x24)
+#define TOMTOM_A_MICB_1_MBHC			(0x12D)
+#define TOMTOM_A_MICB_1_MBHC__POR				(0x01)
+#define TOMTOM_A_MICB_CFILT_2_CTL			(0x12E)
+#define TOMTOM_A_MICB_CFILT_2_CTL__POR				(0x41)
+#define TOMTOM_A_MICB_CFILT_2_VAL			(0x12F)
+#define TOMTOM_A_MICB_CFILT_2_VAL__POR				(0x80)
+#define TOMTOM_A_MICB_CFILT_2_PRECHRG			(0x130)
+#define TOMTOM_A_MICB_CFILT_2_PRECHRG__POR				(0x38)
+#define TOMTOM_A_MICB_2_CTL			(0x131)
+#define TOMTOM_A_MICB_2_CTL__POR				(0x16)
+#define TOMTOM_A_MICB_2_INT_RBIAS			(0x132)
+#define TOMTOM_A_MICB_2_INT_RBIAS__POR				(0x24)
+#define TOMTOM_A_MICB_2_MBHC			(0x133)
+#define TOMTOM_A_MICB_2_MBHC__POR				(0x02)
+#define TOMTOM_A_MICB_CFILT_3_CTL			(0x134)
+#define TOMTOM_A_MICB_CFILT_3_CTL__POR				(0x40)
+#define TOMTOM_A_MICB_CFILT_3_VAL			(0x135)
+#define TOMTOM_A_MICB_CFILT_3_VAL__POR				(0x80)
+#define TOMTOM_A_MICB_CFILT_3_PRECHRG			(0x136)
+#define TOMTOM_A_MICB_CFILT_3_PRECHRG__POR				(0x38)
+#define TOMTOM_A_MICB_3_CTL			(0x137)
+#define TOMTOM_A_MICB_3_CTL__POR				(0x16)
+#define TOMTOM_A_MICB_3_INT_RBIAS			(0x138)
+#define TOMTOM_A_MICB_3_INT_RBIAS__POR				(0x24)
+#define TOMTOM_A_MICB_3_MBHC			(0x139)
+#define TOMTOM_A_MICB_3_MBHC__POR				(0x00)
+#define TOMTOM_A_MICB_4_CTL			(0x13A)
+#define TOMTOM_A_MICB_4_CTL__POR				(0x16)
+#define TOMTOM_A_MICB_4_INT_RBIAS			(0x13B)
+#define TOMTOM_A_MICB_4_INT_RBIAS__POR				(0x24)
+#define TOMTOM_A_MICB_4_MBHC			(0x13C)
+#define TOMTOM_A_MICB_4_MBHC__POR				(0x01)
+#define TOMTOM_A_SPKR_DRV2_EN			(0x13D)
+#define TOMTOM_A_SPKR_DRV2_EN__POR				(0x6F)
+#define TOMTOM_A_SPKR_DRV2_GAIN			(0x13E)
+#define TOMTOM_A_SPKR_DRV2_GAIN__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV2_DAC_CTL			(0x13F)
+#define TOMTOM_A_SPKR_DRV2_DAC_CTL__POR				(0x04)
+#define TOMTOM_A_SPKR_DRV2_OCP_CTL			(0x140)
+#define TOMTOM_A_SPKR_DRV2_OCP_CTL__POR				(0x97)
+#define TOMTOM_A_SPKR_DRV2_CLIP_DET			(0x141)
+#define TOMTOM_A_SPKR_DRV2_CLIP_DET__POR				(0x01)
+#define TOMTOM_A_SPKR_DRV2_DBG_DAC			(0x142)
+#define TOMTOM_A_SPKR_DRV2_DBG_DAC__POR				(0x05)
+#define TOMTOM_A_SPKR_DRV2_DBG_PA			(0x143)
+#define TOMTOM_A_SPKR_DRV2_DBG_PA__POR				(0x18)
+#define TOMTOM_A_SPKR_DRV2_DBG_PWRSTG			(0x144)
+#define TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV2_BIAS_LDO			(0x145)
+#define TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR				(0x45)
+#define TOMTOM_A_SPKR_DRV2_BIAS_INT			(0x146)
+#define TOMTOM_A_SPKR_DRV2_BIAS_INT__POR				(0xA5)
+#define TOMTOM_A_SPKR_DRV2_BIAS_PA			(0x147)
+#define TOMTOM_A_SPKR_DRV2_BIAS_PA__POR				(0x55)
+#define TOMTOM_A_SPKR_DRV2_STATUS_OCP			(0x148)
+#define TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV2_STATUS_PA			(0x149)
+#define TOMTOM_A_SPKR_DRV2_STATUS_PA__POR				(0x00)
+#define TOMTOM_A_MBHC_INSERT_DETECT			(0x14A)
+#define TOMTOM_A_MBHC_INSERT_DETECT__POR				(0x00)
+#define TOMTOM_A_MBHC_INSERT_DET_STATUS			(0x14B)
+#define TOMTOM_A_MBHC_INSERT_DET_STATUS__POR				(0x00)
+#define TOMTOM_A_TX_COM_BIAS			(0x14C)
+#define TOMTOM_A_TX_COM_BIAS__POR				(0xF0)
+#define TOMTOM_A_MBHC_INSERT_DETECT2			(0x14D)
+#define TOMTOM_A_MBHC_INSERT_DETECT2__POR				(0xD0)
+#define TOMTOM_A_MBHC_SCALING_MUX_1			(0x14E)
+#define TOMTOM_A_MBHC_SCALING_MUX_1__POR				(0x00)
+#define TOMTOM_A_MBHC_SCALING_MUX_2			(0x14F)
+#define TOMTOM_A_MBHC_SCALING_MUX_2__POR				(0x80)
+#define TOMTOM_A_MAD_ANA_CTRL			(0x150)
+#define TOMTOM_A_MAD_ANA_CTRL__POR				(0xF1)
+#define TOMTOM_A_TX_SUP_SWITCH_CTRL_1			(0x151)
+#define TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR				(0x00)
+#define TOMTOM_A_TX_SUP_SWITCH_CTRL_2			(0x152)
+#define TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR				(0x80)
+#define TOMTOM_A_TX_1_GAIN			(0x153)
+#define TOMTOM_A_TX_1_GAIN__POR				(0x02)
+#define TOMTOM_A_TX_1_2_TEST_EN			(0x154)
+#define TOMTOM_A_TX_1_2_TEST_EN__POR				(0xCC)
+#define TOMTOM_A_TX_2_GAIN			(0x155)
+#define TOMTOM_A_TX_2_GAIN__POR				(0x02)
+#define TOMTOM_A_TX_1_2_ADC_IB			(0x156)
+#define TOMTOM_A_TX_1_2_ADC_IB__POR				(0x44)
+#define TOMTOM_A_TX_1_2_ATEST_REFCTRL			(0x157)
+#define TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR				(0x00)
+#define TOMTOM_A_TX_1_2_TEST_CTL			(0x158)
+#define TOMTOM_A_TX_1_2_TEST_CTL__POR				(0x38)
+#define TOMTOM_A_TX_1_2_TEST_BLOCK_EN			(0x159)
+#define TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR				(0xFC)
+#define TOMTOM_A_TX_1_2_TXFE_CLKDIV			(0x15A)
+#define TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR				(0x55)
+#define TOMTOM_A_TX_1_2_SAR_ERR_CH1			(0x15B)
+#define TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR				(0x00)
+#define TOMTOM_A_TX_1_2_SAR_ERR_CH2			(0x15C)
+#define TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR				(0x00)
+#define TOMTOM_A_TX_3_GAIN			(0x15D)
+#define TOMTOM_A_TX_3_GAIN__POR				(0x02)
+#define TOMTOM_A_TX_3_4_TEST_EN			(0x15E)
+#define TOMTOM_A_TX_3_4_TEST_EN__POR				(0xCC)
+#define TOMTOM_A_TX_4_GAIN			(0x15F)
+#define TOMTOM_A_TX_4_GAIN__POR				(0x02)
+#define TOMTOM_A_TX_3_4_ADC_IB			(0x160)
+#define TOMTOM_A_TX_3_4_ADC_IB__POR				(0x44)
+#define TOMTOM_A_TX_3_4_ATEST_REFCTRL			(0x161)
+#define TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR				(0x00)
+#define TOMTOM_A_TX_3_4_TEST_CTL			(0x162)
+#define TOMTOM_A_TX_3_4_TEST_CTL__POR				(0x38)
+#define TOMTOM_A_TX_3_4_TEST_BLOCK_EN			(0x163)
+#define TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR				(0xFC)
+#define TOMTOM_A_TX_3_4_TXFE_CKDIV			(0x164)
+#define TOMTOM_A_TX_3_4_TXFE_CKDIV__POR				(0x55)
+#define TOMTOM_A_TX_3_4_SAR_ERR_CH3			(0x165)
+#define TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR				(0x00)
+#define TOMTOM_A_TX_3_4_SAR_ERR_CH4			(0x166)
+#define TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR				(0x00)
+#define TOMTOM_A_TX_5_GAIN			(0x167)
+#define TOMTOM_A_TX_5_GAIN__POR				(0x02)
+#define TOMTOM_A_TX_5_6_TEST_EN			(0x168)
+#define TOMTOM_A_TX_5_6_TEST_EN__POR				(0xCC)
+#define TOMTOM_A_TX_6_GAIN			(0x169)
+#define TOMTOM_A_TX_6_GAIN__POR				(0x02)
+#define TOMTOM_A_TX_5_6_ADC_IB			(0x16A)
+#define TOMTOM_A_TX_5_6_ADC_IB__POR				(0x44)
+#define TOMTOM_A_TX_5_6_ATEST_REFCTRL			(0x16B)
+#define TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR				(0x00)
+#define TOMTOM_A_TX_5_6_TEST_CTL			(0x16C)
+#define TOMTOM_A_TX_5_6_TEST_CTL__POR				(0x38)
+#define TOMTOM_A_TX_5_6_TEST_BLOCK_EN			(0x16D)
+#define TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR				(0xFC)
+#define TOMTOM_A_TX_5_6_TXFE_CKDIV			(0x16E)
+#define TOMTOM_A_TX_5_6_TXFE_CKDIV__POR				(0x55)
+#define TOMTOM_A_TX_5_6_SAR_ERR_CH5			(0x16F)
+#define TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR				(0x00)
+#define TOMTOM_A_TX_5_6_SAR_ERR_CH6			(0x170)
+#define TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR				(0x00)
+#define TOMTOM_A_TX_7_MBHC_EN			(0x171)
+#define TOMTOM_A_TX_7_MBHC_EN__POR				(0x0C)
+#define TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL			(0x172)
+#define TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR				(0x00)
+#define TOMTOM_A_TX_7_MBHC_ADC			(0x173)
+#define TOMTOM_A_TX_7_MBHC_ADC__POR				(0x44)
+#define TOMTOM_A_TX_7_MBHC_TEST_CTL			(0x174)
+#define TOMTOM_A_TX_7_MBHC_TEST_CTL__POR				(0x38)
+#define TOMTOM_A_TX_7_MBHC_SAR_ERR			(0x175)
+#define TOMTOM_A_TX_7_MBHC_SAR_ERR__POR				(0x00)
+#define TOMTOM_A_TX_7_TXFE_CLKDIV			(0x176)
+#define TOMTOM_A_TX_7_TXFE_CLKDIV__POR				(0x8B)
+#define TOMTOM_A_RCO_CTRL			(0x177)
+#define TOMTOM_A_RCO_CTRL__POR				(0x00)
+#define TOMTOM_A_RCO_CALIBRATION_CTRL1			(0x178)
+#define TOMTOM_A_RCO_CALIBRATION_CTRL1__POR				(0x00)
+#define TOMTOM_A_RCO_CALIBRATION_CTRL2			(0x179)
+#define TOMTOM_A_RCO_CALIBRATION_CTRL2__POR				(0x00)
+#define TOMTOM_A_RCO_CALIBRATION_CTRL3			(0x17A)
+#define TOMTOM_A_RCO_CALIBRATION_CTRL3__POR				(0x00)
+#define TOMTOM_A_RCO_TEST_CTRL			(0x17B)
+#define TOMTOM_A_RCO_TEST_CTRL__POR				(0x00)
+#define TOMTOM_A_RCO_CALIBRATION_RESULT1			(0x17C)
+#define TOMTOM_A_RCO_CALIBRATION_RESULT1__POR				(0x00)
+#define TOMTOM_A_RCO_CALIBRATION_RESULT2			(0x17D)
+#define TOMTOM_A_RCO_CALIBRATION_RESULT2__POR				(0x00)
+#define TOMTOM_A_BUCK_MODE_1			(0x181)
+#define TOMTOM_A_BUCK_MODE_1__POR				(0x21)
+#define TOMTOM_A_BUCK_MODE_2			(0x182)
+#define TOMTOM_A_BUCK_MODE_2__POR				(0xFF)
+#define TOMTOM_A_BUCK_MODE_3			(0x183)
+#define TOMTOM_A_BUCK_MODE_3__POR				(0xCE)
+#define TOMTOM_A_BUCK_MODE_4			(0x184)
+#define TOMTOM_A_BUCK_MODE_4__POR				(0x3A)
+#define TOMTOM_A_BUCK_MODE_5			(0x185)
+#define TOMTOM_A_BUCK_MODE_5__POR				(0x00)
+#define TOMTOM_A_BUCK_CTRL_VCL_1			(0x186)
+#define TOMTOM_A_BUCK_CTRL_VCL_1__POR				(0x08)
+#define TOMTOM_A_BUCK_CTRL_VCL_2			(0x187)
+#define TOMTOM_A_BUCK_CTRL_VCL_2__POR				(0xA3)
+#define TOMTOM_A_BUCK_CTRL_VCL_3			(0x188)
+#define TOMTOM_A_BUCK_CTRL_VCL_3__POR				(0x82)
+#define TOMTOM_A_BUCK_CTRL_CCL_1			(0x189)
+#define TOMTOM_A_BUCK_CTRL_CCL_1__POR				(0x5B)
+#define TOMTOM_A_BUCK_CTRL_CCL_2			(0x18A)
+#define TOMTOM_A_BUCK_CTRL_CCL_2__POR				(0xDC)
+#define TOMTOM_A_BUCK_CTRL_CCL_3			(0x18B)
+#define TOMTOM_A_BUCK_CTRL_CCL_3__POR				(0x6A)
+#define TOMTOM_A_BUCK_CTRL_CCL_4			(0x18C)
+#define TOMTOM_A_BUCK_CTRL_CCL_4__POR				(0x51)
+#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_1			(0x18D)
+#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR				(0x50)
+#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_2			(0x18E)
+#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR				(0x64)
+#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_3			(0x18F)
+#define TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR				(0x77)
+#define TOMTOM_A_BUCK_TMUX_A_D			(0x190)
+#define TOMTOM_A_BUCK_TMUX_A_D__POR				(0x00)
+#define TOMTOM_A_NCP_BUCKREF			(0x191)
+#define TOMTOM_A_NCP_BUCKREF__POR				(0x00)
+#define TOMTOM_A_NCP_EN			(0x192)
+#define TOMTOM_A_NCP_EN__POR				(0xFE)
+#define TOMTOM_A_NCP_CLK			(0x193)
+#define TOMTOM_A_NCP_CLK__POR				(0x94)
+#define TOMTOM_A_NCP_STATIC			(0x194)
+#define TOMTOM_A_NCP_STATIC__POR				(0x28)
+#define TOMTOM_A_NCP_VTH_LOW			(0x195)
+#define TOMTOM_A_NCP_VTH_LOW__POR				(0x88)
+#define TOMTOM_A_NCP_VTH_HIGH			(0x196)
+#define TOMTOM_A_NCP_VTH_HIGH__POR				(0xA0)
+#define TOMTOM_A_NCP_ATEST			(0x197)
+#define TOMTOM_A_NCP_ATEST__POR				(0x00)
+#define TOMTOM_A_NCP_DTEST			(0x198)
+#define TOMTOM_A_NCP_DTEST__POR				(0x10)
+#define TOMTOM_A_NCP_DLY1			(0x199)
+#define TOMTOM_A_NCP_DLY1__POR				(0x06)
+#define TOMTOM_A_NCP_DLY2			(0x19A)
+#define TOMTOM_A_NCP_DLY2__POR				(0x06)
+#define TOMTOM_A_RX_AUX_SW_CTL			(0x19B)
+#define TOMTOM_A_RX_AUX_SW_CTL__POR				(0x00)
+#define TOMTOM_A_RX_PA_AUX_IN_CONN			(0x19C)
+#define TOMTOM_A_RX_PA_AUX_IN_CONN__POR				(0x00)
+#define TOMTOM_A_RX_COM_TIMER_DIV			(0x19E)
+#define TOMTOM_A_RX_COM_TIMER_DIV__POR				(0xE8)
+#define TOMTOM_A_RX_COM_OCP_CTL			(0x19F)
+#define TOMTOM_A_RX_COM_OCP_CTL__POR				(0x1F)
+#define TOMTOM_A_RX_COM_OCP_COUNT			(0x1A0)
+#define TOMTOM_A_RX_COM_OCP_COUNT__POR				(0x77)
+#define TOMTOM_A_RX_COM_DAC_CTL			(0x1A1)
+#define TOMTOM_A_RX_COM_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_COM_BIAS			(0x1A2)
+#define TOMTOM_A_RX_COM_BIAS__POR				(0x20)
+#define TOMTOM_A_RX_HPH_AUTO_CHOP			(0x1A4)
+#define TOMTOM_A_RX_HPH_AUTO_CHOP__POR				(0x38)
+#define TOMTOM_A_RX_HPH_CHOP_CTL			(0x1A5)
+#define TOMTOM_A_RX_HPH_CHOP_CTL__POR				(0xA4)
+#define TOMTOM_A_RX_HPH_BIAS_PA			(0x1A6)
+#define TOMTOM_A_RX_HPH_BIAS_PA__POR				(0x7A)
+#define TOMTOM_A_RX_HPH_BIAS_LDO			(0x1A7)
+#define TOMTOM_A_RX_HPH_BIAS_LDO__POR				(0x87)
+#define TOMTOM_A_RX_HPH_BIAS_CNP			(0x1A8)
+#define TOMTOM_A_RX_HPH_BIAS_CNP__POR				(0x8A)
+#define TOMTOM_A_RX_HPH_BIAS_WG_OCP			(0x1A9)
+#define TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR				(0x2A)
+#define TOMTOM_A_RX_HPH_OCP_CTL			(0x1AA)
+#define TOMTOM_A_RX_HPH_OCP_CTL__POR				(0x69)
+#define TOMTOM_A_RX_HPH_CNP_EN			(0x1AB)
+#define TOMTOM_A_RX_HPH_CNP_EN__POR				(0x80)
+#define TOMTOM_A_RX_HPH_CNP_WG_CTL			(0x1AC)
+#define TOMTOM_A_RX_HPH_CNP_WG_CTL__POR				(0xDA)
+#define TOMTOM_A_RX_HPH_CNP_WG_TIME			(0x1AD)
+#define TOMTOM_A_RX_HPH_CNP_WG_TIME__POR				(0x15)
+#define TOMTOM_A_RX_HPH_L_GAIN			(0x1AE)
+#define TOMTOM_A_RX_HPH_L_GAIN__POR				(0xC0)
+#define TOMTOM_A_RX_HPH_L_TEST			(0x1AF)
+#define TOMTOM_A_RX_HPH_L_TEST__POR				(0x02)
+#define TOMTOM_A_RX_HPH_L_PA_CTL			(0x1B0)
+#define TOMTOM_A_RX_HPH_L_PA_CTL__POR				(0x42)
+#define TOMTOM_A_RX_HPH_L_DAC_CTL			(0x1B1)
+#define TOMTOM_A_RX_HPH_L_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_HPH_L_ATEST			(0x1B2)
+#define TOMTOM_A_RX_HPH_L_ATEST__POR				(0x00)
+#define TOMTOM_A_RX_HPH_L_STATUS			(0x1B3)
+#define TOMTOM_A_RX_HPH_L_STATUS__POR				(0x00)
+#define TOMTOM_A_RX_HPH_R_GAIN			(0x1B4)
+#define TOMTOM_A_RX_HPH_R_GAIN__POR				(0x00)
+#define TOMTOM_A_RX_HPH_R_TEST			(0x1B5)
+#define TOMTOM_A_RX_HPH_R_TEST__POR				(0x02)
+#define TOMTOM_A_RX_HPH_R_PA_CTL			(0x1B6)
+#define TOMTOM_A_RX_HPH_R_PA_CTL__POR				(0x42)
+#define TOMTOM_A_RX_HPH_R_DAC_CTL			(0x1B7)
+#define TOMTOM_A_RX_HPH_R_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_HPH_R_ATEST			(0x1B8)
+#define TOMTOM_A_RX_HPH_R_ATEST__POR				(0x00)
+#define TOMTOM_A_RX_HPH_R_STATUS			(0x1B9)
+#define TOMTOM_A_RX_HPH_R_STATUS__POR				(0x00)
+#define TOMTOM_A_RX_EAR_BIAS_PA			(0x1BA)
+#define TOMTOM_A_RX_EAR_BIAS_PA__POR				(0x76)
+#define TOMTOM_A_RX_EAR_BIAS_CMBUFF			(0x1BB)
+#define TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR				(0xA0)
+#define TOMTOM_A_RX_EAR_EN			(0x1BC)
+#define TOMTOM_A_RX_EAR_EN__POR				(0x00)
+#define TOMTOM_A_RX_EAR_GAIN			(0x1BD)
+#define TOMTOM_A_RX_EAR_GAIN__POR				(0x02)
+#define TOMTOM_A_RX_EAR_CMBUFF			(0x1BE)
+#define TOMTOM_A_RX_EAR_CMBUFF__POR				(0x05)
+#define TOMTOM_A_RX_EAR_ICTL			(0x1BF)
+#define TOMTOM_A_RX_EAR_ICTL__POR				(0x40)
+#define TOMTOM_A_RX_EAR_CCOMP			(0x1C0)
+#define TOMTOM_A_RX_EAR_CCOMP__POR				(0x08)
+#define TOMTOM_A_RX_EAR_VCM			(0x1C1)
+#define TOMTOM_A_RX_EAR_VCM__POR				(0x03)
+#define TOMTOM_A_RX_EAR_CNP			(0x1C2)
+#define TOMTOM_A_RX_EAR_CNP__POR				(0xC0)
+#define TOMTOM_A_RX_EAR_DAC_CTL_ATEST			(0x1C3)
+#define TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR				(0x00)
+#define TOMTOM_A_RX_EAR_STATUS			(0x1C5)
+#define TOMTOM_A_RX_EAR_STATUS__POR				(0x04)
+#define TOMTOM_A_RX_LINE_BIAS_PA			(0x1C6)
+#define TOMTOM_A_RX_LINE_BIAS_PA__POR				(0x78)
+#define TOMTOM_A_RX_BUCK_BIAS1			(0x1C7)
+#define TOMTOM_A_RX_BUCK_BIAS1__POR				(0x42)
+#define TOMTOM_A_RX_BUCK_BIAS2			(0x1C8)
+#define TOMTOM_A_RX_BUCK_BIAS2__POR				(0x84)
+#define TOMTOM_A_RX_LINE_COM			(0x1C9)
+#define TOMTOM_A_RX_LINE_COM__POR				(0x80)
+#define TOMTOM_A_RX_LINE_CNP_EN			(0x1CA)
+#define TOMTOM_A_RX_LINE_CNP_EN__POR				(0x00)
+#define TOMTOM_A_RX_LINE_CNP_WG_CTL			(0x1CB)
+#define TOMTOM_A_RX_LINE_CNP_WG_CTL__POR				(0x00)
+#define TOMTOM_A_RX_LINE_CNP_WG_TIME			(0x1CC)
+#define TOMTOM_A_RX_LINE_CNP_WG_TIME__POR				(0x04)
+#define TOMTOM_A_RX_LINE_1_GAIN			(0x1CD)
+#define TOMTOM_A_RX_LINE_1_GAIN__POR				(0x00)
+#define TOMTOM_A_RX_LINE_1_TEST			(0x1CE)
+#define TOMTOM_A_RX_LINE_1_TEST__POR				(0x02)
+#define TOMTOM_A_RX_LINE_1_DAC_CTL			(0x1CF)
+#define TOMTOM_A_RX_LINE_1_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_LINE_1_STATUS			(0x1D0)
+#define TOMTOM_A_RX_LINE_1_STATUS__POR				(0x00)
+#define TOMTOM_A_RX_LINE_2_GAIN			(0x1D1)
+#define TOMTOM_A_RX_LINE_2_GAIN__POR				(0x00)
+#define TOMTOM_A_RX_LINE_2_TEST			(0x1D2)
+#define TOMTOM_A_RX_LINE_2_TEST__POR				(0x02)
+#define TOMTOM_A_RX_LINE_2_DAC_CTL			(0x1D3)
+#define TOMTOM_A_RX_LINE_2_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_LINE_2_STATUS			(0x1D4)
+#define TOMTOM_A_RX_LINE_2_STATUS__POR				(0x00)
+#define TOMTOM_A_RX_LINE_3_GAIN			(0x1D5)
+#define TOMTOM_A_RX_LINE_3_GAIN__POR				(0x00)
+#define TOMTOM_A_RX_LINE_3_TEST			(0x1D6)
+#define TOMTOM_A_RX_LINE_3_TEST__POR				(0x02)
+#define TOMTOM_A_RX_LINE_3_DAC_CTL			(0x1D7)
+#define TOMTOM_A_RX_LINE_3_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_LINE_3_STATUS			(0x1D8)
+#define TOMTOM_A_RX_LINE_3_STATUS__POR				(0x00)
+#define TOMTOM_A_RX_LINE_4_GAIN			(0x1D9)
+#define TOMTOM_A_RX_LINE_4_GAIN__POR				(0x00)
+#define TOMTOM_A_RX_LINE_4_TEST			(0x1DA)
+#define TOMTOM_A_RX_LINE_4_TEST__POR				(0x02)
+#define TOMTOM_A_RX_LINE_4_DAC_CTL			(0x1DB)
+#define TOMTOM_A_RX_LINE_4_DAC_CTL__POR				(0x00)
+#define TOMTOM_A_RX_LINE_4_STATUS			(0x1DC)
+#define TOMTOM_A_RX_LINE_4_STATUS__POR				(0x00)
+#define TOMTOM_A_RX_LINE_CNP_DBG			(0x1DD)
+#define TOMTOM_A_RX_LINE_CNP_DBG__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV1_EN			(0x1DF)
+#define TOMTOM_A_SPKR_DRV1_EN__POR				(0x6F)
+#define TOMTOM_A_SPKR_DRV1_GAIN			(0x1E0)
+#define TOMTOM_A_SPKR_DRV1_GAIN__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV1_DAC_CTL			(0x1E1)
+#define TOMTOM_A_SPKR_DRV1_DAC_CTL__POR				(0x04)
+#define TOMTOM_A_SPKR_DRV1_OCP_CTL			(0x1E2)
+#define TOMTOM_A_SPKR_DRV1_OCP_CTL__POR				(0x97)
+#define TOMTOM_A_SPKR_DRV1_CLIP_DET			(0x1E3)
+#define TOMTOM_A_SPKR_DRV1_CLIP_DET__POR				(0x01)
+#define TOMTOM_A_SPKR_DRV1_IEC			(0x1E4)
+#define TOMTOM_A_SPKR_DRV1_IEC__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV1_DBG_DAC			(0x1E5)
+#define TOMTOM_A_SPKR_DRV1_DBG_DAC__POR				(0x05)
+#define TOMTOM_A_SPKR_DRV1_DBG_PA			(0x1E6)
+#define TOMTOM_A_SPKR_DRV1_DBG_PA__POR				(0x18)
+#define TOMTOM_A_SPKR_DRV1_DBG_PWRSTG			(0x1E7)
+#define TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV1_BIAS_LDO			(0x1E8)
+#define TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR				(0x45)
+#define TOMTOM_A_SPKR_DRV1_BIAS_INT			(0x1E9)
+#define TOMTOM_A_SPKR_DRV1_BIAS_INT__POR				(0xA5)
+#define TOMTOM_A_SPKR_DRV1_BIAS_PA			(0x1EA)
+#define TOMTOM_A_SPKR_DRV1_BIAS_PA__POR				(0x55)
+#define TOMTOM_A_SPKR_DRV1_STATUS_OCP			(0x1EB)
+#define TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR				(0x00)
+#define TOMTOM_A_SPKR_DRV1_STATUS_PA			(0x1EC)
+#define TOMTOM_A_SPKR_DRV1_STATUS_PA__POR				(0x00)
+#define TOMTOM_A_SPKR1_PROT_EN			(0x1ED)
+#define TOMTOM_A_SPKR1_PROT_EN__POR				(0x00)
+#define TOMTOM_A_SPKR1_PROT_ADC_TEST_EN			(0x1EE)
+#define TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR				(0x44)
+#define TOMTOM_A_SPKR1_PROT_ATEST			(0x1EF)
+#define TOMTOM_A_SPKR1_PROT_ATEST__POR				(0x00)
+#define TOMTOM_A_SPKR1_PROT_LDO_CTRL			(0x1F0)
+#define TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR				(0x00)
+#define TOMTOM_A_SPKR1_PROT_ISENSE_CTRL			(0x1F1)
+#define TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR				(0x00)
+#define TOMTOM_A_SPKR1_PROT_VSENSE_CTRL			(0x1F2)
+#define TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR				(0x00)
+#define TOMTOM_A_SPKR2_PROT_EN			(0x1F3)
+#define TOMTOM_A_SPKR2_PROT_EN__POR				(0x00)
+#define TOMTOM_A_SPKR2_PROT_ADC_TEST_EN			(0x1F4)
+#define TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR				(0x44)
+#define TOMTOM_A_SPKR2_PROT_ATEST			(0x1F5)
+#define TOMTOM_A_SPKR2_PROT_ATEST__POR				(0x00)
+#define TOMTOM_A_SPKR2_PROT_LDO_CTRL			(0x1F6)
+#define TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR				(0x00)
+#define TOMTOM_A_SPKR2_PROT_ISENSE_CTRL			(0x1F7)
+#define TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR				(0x00)
+#define TOMTOM_A_SPKR2_PROT_VSENSE_CTRL			(0x1F8)
+#define TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR				(0x00)
+#define TOMTOM_A_MBHC_HPH			(0x1FE)
+#define TOMTOM_A_MBHC_HPH__POR				(0x44)
+#define TOMTOM_A_CDC_ANC1_B1_CTL			(0x200)
+#define TOMTOM_A_CDC_ANC1_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_B1_CTL			(0x280)
+#define TOMTOM_A_CDC_ANC2_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_SHIFT			(0x201)
+#define TOMTOM_A_CDC_ANC1_SHIFT__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_SHIFT			(0x281)
+#define TOMTOM_A_CDC_ANC2_SHIFT__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_IIR_B1_CTL			(0x202)
+#define TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_IIR_B1_CTL			(0x282)
+#define TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_IIR_B2_CTL			(0x203)
+#define TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_IIR_B2_CTL			(0x283)
+#define TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_IIR_B3_CTL			(0x204)
+#define TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_IIR_B3_CTL			(0x284)
+#define TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_LPF_B1_CTL			(0x206)
+#define TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_LPF_B1_CTL			(0x286)
+#define TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_LPF_B2_CTL			(0x207)
+#define TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_LPF_B2_CTL			(0x287)
+#define TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_SPARE			(0x209)
+#define TOMTOM_A_CDC_ANC1_SPARE__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_SPARE			(0x289)
+#define TOMTOM_A_CDC_ANC2_SPARE__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_SMLPF_CTL			(0x20A)
+#define TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_SMLPF_CTL			(0x28A)
+#define TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_DCFLT_CTL			(0x20B)
+#define TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_DCFLT_CTL			(0x28B)
+#define TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_GAIN_CTL			(0x20C)
+#define TOMTOM_A_CDC_ANC1_GAIN_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_GAIN_CTL			(0x28C)
+#define TOMTOM_A_CDC_ANC2_GAIN_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC1_B2_CTL			(0x20D)
+#define TOMTOM_A_CDC_ANC1_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_ANC2_B2_CTL			(0x28D)
+#define TOMTOM_A_CDC_ANC2_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX1_VOL_CTL_TIMER			(0x220)
+#define TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX2_VOL_CTL_TIMER			(0x228)
+#define TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX3_VOL_CTL_TIMER			(0x230)
+#define TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX4_VOL_CTL_TIMER			(0x238)
+#define TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX5_VOL_CTL_TIMER			(0x240)
+#define TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX6_VOL_CTL_TIMER			(0x248)
+#define TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX7_VOL_CTL_TIMER			(0x250)
+#define TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX8_VOL_CTL_TIMER			(0x258)
+#define TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX9_VOL_CTL_TIMER			(0x260)
+#define TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX10_VOL_CTL_TIMER			(0x268)
+#define TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR				(0x00)
+#define TOMTOM_A_CDC_TX1_VOL_CTL_GAIN			(0x221)
+#define TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX2_VOL_CTL_GAIN			(0x229)
+#define TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX3_VOL_CTL_GAIN			(0x231)
+#define TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX4_VOL_CTL_GAIN			(0x239)
+#define TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX5_VOL_CTL_GAIN			(0x241)
+#define TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX6_VOL_CTL_GAIN			(0x249)
+#define TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX7_VOL_CTL_GAIN			(0x251)
+#define TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX8_VOL_CTL_GAIN			(0x259)
+#define TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX9_VOL_CTL_GAIN			(0x261)
+#define TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX10_VOL_CTL_GAIN			(0x269)
+#define TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR				(0x00)
+#define TOMTOM_A_CDC_TX1_VOL_CTL_CFG			(0x222)
+#define TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX2_VOL_CTL_CFG			(0x22A)
+#define TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX3_VOL_CTL_CFG			(0x232)
+#define TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX4_VOL_CTL_CFG			(0x23A)
+#define TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX5_VOL_CTL_CFG			(0x242)
+#define TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX6_VOL_CTL_CFG			(0x24A)
+#define TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX7_VOL_CTL_CFG			(0x252)
+#define TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX8_VOL_CTL_CFG			(0x25A)
+#define TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX9_VOL_CTL_CFG			(0x262)
+#define TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX10_VOL_CTL_CFG			(0x26A)
+#define TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_TX1_MUX_CTL			(0x223)
+#define TOMTOM_A_CDC_TX1_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX2_MUX_CTL			(0x22B)
+#define TOMTOM_A_CDC_TX2_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX3_MUX_CTL			(0x233)
+#define TOMTOM_A_CDC_TX3_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX4_MUX_CTL			(0x23B)
+#define TOMTOM_A_CDC_TX4_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX5_MUX_CTL			(0x243)
+#define TOMTOM_A_CDC_TX5_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX6_MUX_CTL			(0x24B)
+#define TOMTOM_A_CDC_TX6_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX7_MUX_CTL			(0x253)
+#define TOMTOM_A_CDC_TX7_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX8_MUX_CTL			(0x25B)
+#define TOMTOM_A_CDC_TX8_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX9_MUX_CTL			(0x263)
+#define TOMTOM_A_CDC_TX9_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX10_MUX_CTL			(0x26B)
+#define TOMTOM_A_CDC_TX10_MUX_CTL__POR				(0x48)
+#define TOMTOM_A_CDC_TX1_CLK_FS_CTL			(0x224)
+#define TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX2_CLK_FS_CTL			(0x22C)
+#define TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX3_CLK_FS_CTL			(0x234)
+#define TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX4_CLK_FS_CTL			(0x23C)
+#define TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX5_CLK_FS_CTL			(0x244)
+#define TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX6_CLK_FS_CTL			(0x24C)
+#define TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX7_CLK_FS_CTL			(0x254)
+#define TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX8_CLK_FS_CTL			(0x25C)
+#define TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX9_CLK_FS_CTL			(0x264)
+#define TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX10_CLK_FS_CTL			(0x26C)
+#define TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_TX1_DMIC_CTL			(0x225)
+#define TOMTOM_A_CDC_TX1_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX2_DMIC_CTL			(0x22D)
+#define TOMTOM_A_CDC_TX2_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX3_DMIC_CTL			(0x235)
+#define TOMTOM_A_CDC_TX3_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX4_DMIC_CTL			(0x23D)
+#define TOMTOM_A_CDC_TX4_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX5_DMIC_CTL			(0x245)
+#define TOMTOM_A_CDC_TX5_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX6_DMIC_CTL			(0x24D)
+#define TOMTOM_A_CDC_TX6_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX7_DMIC_CTL			(0x255)
+#define TOMTOM_A_CDC_TX7_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX8_DMIC_CTL			(0x25D)
+#define TOMTOM_A_CDC_TX8_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX9_DMIC_CTL			(0x265)
+#define TOMTOM_A_CDC_TX9_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TX10_DMIC_CTL			(0x26D)
+#define TOMTOM_A_CDC_TX10_DMIC_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL0			(0x270)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL1			(0x271)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL2			(0x272)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL3			(0x273)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL4			(0x274)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL5			(0x275)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL6			(0x276)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL7			(0x277)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B1_CTL			(0x278)
+#define TOMTOM_A_CDC_DEBUG_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B2_CTL			(0x279)
+#define TOMTOM_A_CDC_DEBUG_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B3_CTL			(0x27A)
+#define TOMTOM_A_CDC_DEBUG_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B4_CTL			(0x27B)
+#define TOMTOM_A_CDC_DEBUG_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B5_CTL			(0x27C)
+#define TOMTOM_A_CDC_DEBUG_B5_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B6_CTL			(0x27D)
+#define TOMTOM_A_CDC_DEBUG_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_DEBUG_B7_CTL			(0x27E)
+#define TOMTOM_A_CDC_DEBUG_B7_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_SRC1_PDA_CFG			(0x2A0)
+#define TOMTOM_A_CDC_SRC1_PDA_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_SRC2_PDA_CFG			(0x2A8)
+#define TOMTOM_A_CDC_SRC2_PDA_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_SRC1_FS_CTL			(0x2A1)
+#define TOMTOM_A_CDC_SRC1_FS_CTL__POR				(0x1B)
+#define TOMTOM_A_CDC_SRC2_FS_CTL			(0x2A9)
+#define TOMTOM_A_CDC_SRC2_FS_CTL__POR				(0x1B)
+#define TOMTOM_A_CDC_RX1_B1_CTL			(0x2B0)
+#define TOMTOM_A_CDC_RX1_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX2_B1_CTL			(0x2B8)
+#define TOMTOM_A_CDC_RX2_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX3_B1_CTL			(0x2C0)
+#define TOMTOM_A_CDC_RX3_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX4_B1_CTL			(0x2C8)
+#define TOMTOM_A_CDC_RX4_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX5_B1_CTL			(0x2D0)
+#define TOMTOM_A_CDC_RX5_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX6_B1_CTL			(0x2D8)
+#define TOMTOM_A_CDC_RX6_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX7_B1_CTL			(0x2E0)
+#define TOMTOM_A_CDC_RX7_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX1_B2_CTL			(0x2B1)
+#define TOMTOM_A_CDC_RX1_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX2_B2_CTL			(0x2B9)
+#define TOMTOM_A_CDC_RX2_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX3_B2_CTL			(0x2C1)
+#define TOMTOM_A_CDC_RX3_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX4_B2_CTL			(0x2C9)
+#define TOMTOM_A_CDC_RX4_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX5_B2_CTL			(0x2D1)
+#define TOMTOM_A_CDC_RX5_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX6_B2_CTL			(0x2D9)
+#define TOMTOM_A_CDC_RX6_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX7_B2_CTL			(0x2E1)
+#define TOMTOM_A_CDC_RX7_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX1_B3_CTL			(0x2B2)
+#define TOMTOM_A_CDC_RX1_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX2_B3_CTL			(0x2BA)
+#define TOMTOM_A_CDC_RX2_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX3_B3_CTL			(0x2C2)
+#define TOMTOM_A_CDC_RX3_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX4_B3_CTL			(0x2CA)
+#define TOMTOM_A_CDC_RX4_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX5_B3_CTL			(0x2D2)
+#define TOMTOM_A_CDC_RX5_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX6_B3_CTL			(0x2DA)
+#define TOMTOM_A_CDC_RX6_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX7_B3_CTL			(0x2E2)
+#define TOMTOM_A_CDC_RX7_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX1_B4_CTL			(0x2B3)
+#define TOMTOM_A_CDC_RX1_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX2_B4_CTL			(0x2BB)
+#define TOMTOM_A_CDC_RX2_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX3_B4_CTL			(0x2C3)
+#define TOMTOM_A_CDC_RX3_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX4_B4_CTL			(0x2CB)
+#define TOMTOM_A_CDC_RX4_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX5_B4_CTL			(0x2D3)
+#define TOMTOM_A_CDC_RX5_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX6_B4_CTL			(0x2DB)
+#define TOMTOM_A_CDC_RX6_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX7_B4_CTL			(0x2E3)
+#define TOMTOM_A_CDC_RX7_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX1_B5_CTL			(0x2B4)
+#define TOMTOM_A_CDC_RX1_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX2_B5_CTL			(0x2BC)
+#define TOMTOM_A_CDC_RX2_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX3_B5_CTL			(0x2C4)
+#define TOMTOM_A_CDC_RX3_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX4_B5_CTL			(0x2CC)
+#define TOMTOM_A_CDC_RX4_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX5_B5_CTL			(0x2D4)
+#define TOMTOM_A_CDC_RX5_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX6_B5_CTL			(0x2DC)
+#define TOMTOM_A_CDC_RX6_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX7_B5_CTL			(0x2E4)
+#define TOMTOM_A_CDC_RX7_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX1_B6_CTL			(0x2B5)
+#define TOMTOM_A_CDC_RX1_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX2_B6_CTL			(0x2BD)
+#define TOMTOM_A_CDC_RX2_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX3_B6_CTL			(0x2C5)
+#define TOMTOM_A_CDC_RX3_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX4_B6_CTL			(0x2CD)
+#define TOMTOM_A_CDC_RX4_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX5_B6_CTL			(0x2D5)
+#define TOMTOM_A_CDC_RX5_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX6_B6_CTL			(0x2DD)
+#define TOMTOM_A_CDC_RX6_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX7_B6_CTL			(0x2E5)
+#define TOMTOM_A_CDC_RX7_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL			(0x2B6)
+#define TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL			(0x2BE)
+#define TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL			(0x2C6)
+#define TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL			(0x2CE)
+#define TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL			(0x2D6)
+#define TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL			(0x2DE)
+#define TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL			(0x2E6)
+#define TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL			(0x2B7)
+#define TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL			(0x2BF)
+#define TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL			(0x2C7)
+#define TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL			(0x2CF)
+#define TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL			(0x2D7)
+#define TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL			(0x2DF)
+#define TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL			(0x2E7)
+#define TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_VBAT_CFG			(0x2E8)
+#define TOMTOM_A_CDC_VBAT_CFG__POR				(0x1A)
+#define TOMTOM_A_CDC_VBAT_ADC_CAL1			(0x2E9)
+#define TOMTOM_A_CDC_VBAT_ADC_CAL1__POR				(0x00)
+#define TOMTOM_A_CDC_VBAT_ADC_CAL2			(0x2EA)
+#define TOMTOM_A_CDC_VBAT_ADC_CAL2__POR				(0x00)
+#define TOMTOM_A_CDC_VBAT_ADC_CAL3			(0x2EB)
+#define TOMTOM_A_CDC_VBAT_ADC_CAL3__POR				(0x04)
+#define TOMTOM_A_CDC_VBAT_PK_EST1			(0x2EC)
+#define TOMTOM_A_CDC_VBAT_PK_EST1__POR				(0xE0)
+#define TOMTOM_A_CDC_VBAT_PK_EST2			(0x2ED)
+#define TOMTOM_A_CDC_VBAT_PK_EST2__POR				(0x01)
+#define TOMTOM_A_CDC_VBAT_PK_EST3			(0x2EE)
+#define TOMTOM_A_CDC_VBAT_PK_EST3__POR				(0x40)
+#define TOMTOM_A_CDC_VBAT_RF_PROC1			(0x2EF)
+#define TOMTOM_A_CDC_VBAT_RF_PROC1__POR				(0x2A)
+#define TOMTOM_A_CDC_VBAT_RF_PROC2			(0x2F0)
+#define TOMTOM_A_CDC_VBAT_RF_PROC2__POR				(0x86)
+#define TOMTOM_A_CDC_VBAT_TAC1			(0x2F1)
+#define TOMTOM_A_CDC_VBAT_TAC1__POR				(0x70)
+#define TOMTOM_A_CDC_VBAT_TAC2			(0x2F2)
+#define TOMTOM_A_CDC_VBAT_TAC2__POR				(0x18)
+#define TOMTOM_A_CDC_VBAT_TAC3			(0x2F3)
+#define TOMTOM_A_CDC_VBAT_TAC3__POR				(0x18)
+#define TOMTOM_A_CDC_VBAT_TAC4			(0x2F4)
+#define TOMTOM_A_CDC_VBAT_TAC4__POR				(0x03)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD1			(0x2F5)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR				(0x01)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD2			(0x2F6)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR				(0x00)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD3			(0x2F7)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR				(0x64)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD4			(0x2F8)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR				(0x01)
+#define TOMTOM_A_CDC_VBAT_DEBUG1			(0x2F9)
+#define TOMTOM_A_CDC_VBAT_DEBUG1__POR				(0x00)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD_MON			(0x2FA)
+#define TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR				(0x00)
+#define TOMTOM_A_CDC_VBAT_GAIN_MON_VAL			(0x2FB)
+#define TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_ANC_RESET_CTL			(0x300)
+#define TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_RX_RESET_CTL			(0x301)
+#define TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL			(0x302)
+#define TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL			(0x303)
+#define TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_RX_I2S_CTL			(0x306)
+#define TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_CLK_TX_I2S_CTL			(0x307)
+#define TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL			(0x308)
+#define TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL			(0x309)
+#define TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL			(0x30A)
+#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL			(0x30B)
+#define TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_OTHR_CTL			(0x30C)
+#define TOMTOM_A_CDC_CLK_OTHR_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL			(0x30E)
+#define TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_RX_B1_CTL			(0x30F)
+#define TOMTOM_A_CDC_CLK_RX_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_RX_B2_CTL			(0x310)
+#define TOMTOM_A_CDC_CLK_RX_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_MCLK_CTL			(0x311)
+#define TOMTOM_A_CDC_CLK_MCLK_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_PDM_CTL			(0x312)
+#define TOMTOM_A_CDC_CLK_PDM_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLK_SD_CTL			(0x313)
+#define TOMTOM_A_CDC_CLK_SD_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_B1_CTL			(0x320)
+#define TOMTOM_A_CDC_CLSH_B1_CTL__POR				(0xE4)
+#define TOMTOM_A_CDC_CLSH_B2_CTL			(0x321)
+#define TOMTOM_A_CDC_CLSH_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_B3_CTL			(0x322)
+#define TOMTOM_A_CDC_CLSH_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS			(0x323)
+#define TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD			(0x324)
+#define TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR				(0x12)
+#define TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD			(0x325)
+#define TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR				(0x0C)
+#define TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD			(0x326)
+#define TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR			(0x18)
+#define TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD			(0x327)
+#define TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR			(0x23)
+#define TOMTOM_A_CDC_CLSH_K_ADDR			(0x328)
+#define TOMTOM_A_CDC_CLSH_K_ADDR__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_K_DATA			(0x329)
+#define TOMTOM_A_CDC_CLSH_K_DATA__POR				(0xA4)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L			(0x32A)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR				(0xD7)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U			(0x32B)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR				(0x05)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L			(0x32C)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR				(0x60)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U			(0x32D)
+#define TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR				(0x09)
+#define TOMTOM_A_CDC_CLSH_V_PA_HD_EAR			(0x32E)
+#define TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_V_PA_HD_HPH			(0x32F)
+#define TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR			(0x330)
+#define TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR				(0x00)
+#define TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH			(0x331)
+#define TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B1_CTL			(0x340)
+#define TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B1_CTL			(0x350)
+#define TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B2_CTL			(0x341)
+#define TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B2_CTL			(0x351)
+#define TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B3_CTL			(0x342)
+#define TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B3_CTL			(0x352)
+#define TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B4_CTL			(0x343)
+#define TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B4_CTL			(0x353)
+#define TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B5_CTL			(0x344)
+#define TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B5_CTL			(0x354)
+#define TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B6_CTL			(0x345)
+#define TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B6_CTL			(0x355)
+#define TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B7_CTL			(0x346)
+#define TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B7_CTL			(0x356)
+#define TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_GAIN_B8_CTL			(0x347)
+#define TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_B8_CTL			(0x357)
+#define TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_CTL			(0x348)
+#define TOMTOM_A_CDC_IIR1_CTL__POR				(0x40)
+#define TOMTOM_A_CDC_IIR2_CTL			(0x358)
+#define TOMTOM_A_CDC_IIR2_CTL__POR				(0x40)
+#define TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL			(0x349)
+#define TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL			(0x359)
+#define TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_COEF_B1_CTL			(0x34A)
+#define TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_COEF_B1_CTL			(0x35A)
+#define TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR1_COEF_B2_CTL			(0x34B)
+#define TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_IIR2_COEF_B2_CTL			(0x35B)
+#define TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_TOP_GAIN_UPDATE			(0x360)
+#define TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR				(0x00)
+#define TOMTOM_A_CDC_PA_RAMP_B1_CTL			(0x361)
+#define TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_PA_RAMP_B2_CTL			(0x362)
+#define TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_PA_RAMP_B3_CTL			(0x363)
+#define TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_PA_RAMP_B4_CTL			(0x364)
+#define TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL			(0x365)
+#define TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL			(0x366)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_COMP0_B1_CTL			(0x368)
+#define TOMTOM_A_CDC_COMP0_B1_CTL__POR				(0x30)
+#define TOMTOM_A_CDC_COMP1_B1_CTL			(0x370)
+#define TOMTOM_A_CDC_COMP1_B1_CTL__POR				(0x30)
+#define TOMTOM_A_CDC_COMP2_B1_CTL			(0x378)
+#define TOMTOM_A_CDC_COMP2_B1_CTL__POR				(0x30)
+#define TOMTOM_A_CDC_COMP0_B2_CTL			(0x369)
+#define TOMTOM_A_CDC_COMP0_B2_CTL__POR				(0xB5)
+#define TOMTOM_A_CDC_COMP1_B2_CTL			(0x371)
+#define TOMTOM_A_CDC_COMP1_B2_CTL__POR				(0xB5)
+#define TOMTOM_A_CDC_COMP2_B2_CTL			(0x379)
+#define TOMTOM_A_CDC_COMP2_B2_CTL__POR				(0xB5)
+#define TOMTOM_A_CDC_COMP0_B3_CTL			(0x36A)
+#define TOMTOM_A_CDC_COMP0_B3_CTL__POR				(0x28)
+#define TOMTOM_A_CDC_COMP1_B3_CTL			(0x372)
+#define TOMTOM_A_CDC_COMP1_B3_CTL__POR				(0x28)
+#define TOMTOM_A_CDC_COMP2_B3_CTL			(0x37A)
+#define TOMTOM_A_CDC_COMP2_B3_CTL__POR				(0x28)
+#define TOMTOM_A_CDC_COMP0_B4_CTL			(0x36B)
+#define TOMTOM_A_CDC_COMP0_B4_CTL__POR				(0x37)
+#define TOMTOM_A_CDC_COMP1_B4_CTL			(0x373)
+#define TOMTOM_A_CDC_COMP1_B4_CTL__POR				(0x37)
+#define TOMTOM_A_CDC_COMP2_B4_CTL			(0x37B)
+#define TOMTOM_A_CDC_COMP2_B4_CTL__POR				(0x37)
+#define TOMTOM_A_CDC_COMP0_B5_CTL			(0x36C)
+#define TOMTOM_A_CDC_COMP0_B5_CTL__POR				(0x7F)
+#define TOMTOM_A_CDC_COMP1_B5_CTL			(0x374)
+#define TOMTOM_A_CDC_COMP1_B5_CTL__POR				(0x7F)
+#define TOMTOM_A_CDC_COMP2_B5_CTL			(0x37C)
+#define TOMTOM_A_CDC_COMP2_B5_CTL__POR				(0x7F)
+#define TOMTOM_A_CDC_COMP0_B6_CTL			(0x36D)
+#define TOMTOM_A_CDC_COMP0_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_COMP1_B6_CTL			(0x375)
+#define TOMTOM_A_CDC_COMP1_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_COMP2_B6_CTL			(0x37D)
+#define TOMTOM_A_CDC_COMP2_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS			(0x36E)
+#define TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR			(0x03)
+#define TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS			(0x376)
+#define TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR			(0x03)
+#define TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS			(0x37E)
+#define TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR			(0x03)
+#define TOMTOM_A_CDC_COMP0_FS_CFG			(0x36F)
+#define TOMTOM_A_CDC_COMP0_FS_CFG__POR				(0x03)
+#define TOMTOM_A_CDC_COMP1_FS_CFG			(0x377)
+#define TOMTOM_A_CDC_COMP1_FS_CFG__POR				(0x03)
+#define TOMTOM_A_CDC_COMP2_FS_CFG			(0x37F)
+#define TOMTOM_A_CDC_COMP2_FS_CFG__POR				(0x03)
+#define TOMTOM_A_CDC_CONN_RX1_B1_CTL			(0x380)
+#define TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX1_B2_CTL			(0x381)
+#define TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX1_B3_CTL			(0x382)
+#define TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX2_B1_CTL			(0x383)
+#define TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX2_B2_CTL			(0x384)
+#define TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX2_B3_CTL			(0x385)
+#define TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX3_B1_CTL			(0x386)
+#define TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX3_B2_CTL			(0x387)
+#define TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX4_B1_CTL			(0x388)
+#define TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX4_B2_CTL			(0x389)
+#define TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX5_B1_CTL			(0x38A)
+#define TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX5_B2_CTL			(0x38B)
+#define TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX6_B1_CTL			(0x38C)
+#define TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX6_B2_CTL			(0x38D)
+#define TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX7_B1_CTL			(0x38E)
+#define TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX7_B2_CTL			(0x38F)
+#define TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX7_B3_CTL			(0x390)
+#define TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_ANC_B1_CTL			(0x391)
+#define TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_ANC_B2_CTL			(0x392)
+#define TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_B1_CTL			(0x393)
+#define TOMTOM_A_CDC_CONN_TX_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_B2_CTL			(0x394)
+#define TOMTOM_A_CDC_CONN_TX_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_B3_CTL			(0x395)
+#define TOMTOM_A_CDC_CONN_TX_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_B4_CTL			(0x396)
+#define TOMTOM_A_CDC_CONN_TX_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ1_B1_CTL			(0x397)
+#define TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ1_B2_CTL			(0x398)
+#define TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ1_B3_CTL			(0x399)
+#define TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ1_B4_CTL			(0x39A)
+#define TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ2_B1_CTL			(0x39B)
+#define TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ2_B2_CTL			(0x39C)
+#define TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ2_B3_CTL			(0x39D)
+#define TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_EQ2_B4_CTL			(0x39E)
+#define TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_SRC1_B1_CTL			(0x39F)
+#define TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_SRC1_B2_CTL			(0x3A0)
+#define TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_SRC2_B1_CTL			(0x3A1)
+#define TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_SRC2_B2_CTL			(0x3A2)
+#define TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B1_CTL			(0x3A3)
+#define TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B2_CTL			(0x3A4)
+#define TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B3_CTL			(0x3A5)
+#define TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B4_CTL			(0x3A6)
+#define TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B5_CTL			(0x3A7)
+#define TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B6_CTL			(0x3A8)
+#define TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B7_CTL			(0x3A9)
+#define TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B8_CTL			(0x3AA)
+#define TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B9_CTL			(0x3AB)
+#define TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B10_CTL			(0x3AC)
+#define TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_TX_SB_B11_CTL			(0x3AD)
+#define TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX_SB_B1_CTL			(0x3AE)
+#define TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_RX_SB_B2_CTL			(0x3AF)
+#define TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_CLSH_CTL			(0x3B0)
+#define TOMTOM_A_CDC_CONN_CLSH_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CONN_MISC			(0x3B1)
+#define TOMTOM_A_CDC_CONN_MISC__POR				(0x01)
+#define TOMTOM_A_CDC_CONN_RX8_B1_CTL			(0x3B3)
+#define TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL		(0x3B4)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR				(0x81)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST	(0x3B5)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR		(0x00)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD	(0x3B6)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR		(0xFF)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS	(0x3B7)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR		(0x00)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK		(0x3B8)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR			(0x04)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING		(0x3B9)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR			(0x04)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL		(0x3BA)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR				(0x81)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST	(0x3BB)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR		(0x00)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD	(0x3BC)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR		(0xFF)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS	(0x3BD)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR		(0x00)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK		(0x3BE)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR			(0x04)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING	(0x3BF)
+#define TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR			(0x04)
+#define TOMTOM_A_CDC_MBHC_EN_CTL			(0x3C0)
+#define TOMTOM_A_CDC_MBHC_EN_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_FIR_B1_CFG			(0x3C1)
+#define TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_FIR_B2_CFG			(0x3C2)
+#define TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR				(0x06)
+#define TOMTOM_A_CDC_MBHC_TIMER_B1_CTL			(0x3C3)
+#define TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR				(0x03)
+#define TOMTOM_A_CDC_MBHC_TIMER_B2_CTL			(0x3C4)
+#define TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR				(0x09)
+#define TOMTOM_A_CDC_MBHC_TIMER_B3_CTL			(0x3C5)
+#define TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR				(0x1E)
+#define TOMTOM_A_CDC_MBHC_TIMER_B4_CTL			(0x3C6)
+#define TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR				(0x45)
+#define TOMTOM_A_CDC_MBHC_TIMER_B5_CTL			(0x3C7)
+#define TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR				(0x04)
+#define TOMTOM_A_CDC_MBHC_TIMER_B6_CTL			(0x3C8)
+#define TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_MBHC_B1_STATUS			(0x3C9)
+#define TOMTOM_A_CDC_MBHC_B1_STATUS__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_B2_STATUS			(0x3CA)
+#define TOMTOM_A_CDC_MBHC_B2_STATUS__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_B3_STATUS			(0x3CB)
+#define TOMTOM_A_CDC_MBHC_B3_STATUS__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_B4_STATUS			(0x3CC)
+#define TOMTOM_A_CDC_MBHC_B4_STATUS__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_B5_STATUS			(0x3CD)
+#define TOMTOM_A_CDC_MBHC_B5_STATUS__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_B1_CTL			(0x3CE)
+#define TOMTOM_A_CDC_MBHC_B1_CTL__POR				(0xC0)
+#define TOMTOM_A_CDC_MBHC_B2_CTL			(0x3CF)
+#define TOMTOM_A_CDC_MBHC_B2_CTL__POR				(0x5D)
+#define TOMTOM_A_CDC_MBHC_VOLT_B1_CTL			(0x3D0)
+#define TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B2_CTL			(0x3D1)
+#define TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B3_CTL			(0x3D2)
+#define TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B4_CTL			(0x3D3)
+#define TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B5_CTL			(0x3D4)
+#define TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B6_CTL			(0x3D5)
+#define TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B7_CTL			(0x3D6)
+#define TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR				(0xFF)
+#define TOMTOM_A_CDC_MBHC_VOLT_B8_CTL			(0x3D7)
+#define TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR				(0x07)
+#define TOMTOM_A_CDC_MBHC_VOLT_B9_CTL			(0x3D8)
+#define TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR				(0xFF)
+#define TOMTOM_A_CDC_MBHC_VOLT_B10_CTL			(0x3D9)
+#define TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR				(0x7F)
+#define TOMTOM_A_CDC_MBHC_VOLT_B11_CTL			(0x3DA)
+#define TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_VOLT_B12_CTL			(0x3DB)
+#define TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_MBHC_CLK_CTL			(0x3DC)
+#define TOMTOM_A_CDC_MBHC_CLK_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_INT_CTL			(0x3DD)
+#define TOMTOM_A_CDC_MBHC_INT_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_DEBUG_CTL			(0x3DE)
+#define TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_MBHC_SPARE			(0x3DF)
+#define TOMTOM_A_CDC_MBHC_SPARE__POR				(0x00)
+#define TOMTOM_A_CDC_RX8_B1_CTL			(0x3E0)
+#define TOMTOM_A_CDC_RX8_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX8_B2_CTL			(0x3E1)
+#define TOMTOM_A_CDC_RX8_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX8_B3_CTL			(0x3E2)
+#define TOMTOM_A_CDC_RX8_B3_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX8_B4_CTL			(0x3E3)
+#define TOMTOM_A_CDC_RX8_B4_CTL__POR				(0x0B)
+#define TOMTOM_A_CDC_RX8_B5_CTL			(0x3E4)
+#define TOMTOM_A_CDC_RX8_B5_CTL__POR				(0x78)
+#define TOMTOM_A_CDC_RX8_B6_CTL			(0x3E5)
+#define TOMTOM_A_CDC_RX8_B6_CTL__POR				(0x80)
+#define TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL			(0x3E6)
+#define TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL			(0x3E7)
+#define TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0			(0x3E8)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1			(0x3E9)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2			(0x3EA)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3			(0x3EB)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4			(0x3EC)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5			(0x3ED)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6			(0x3EE)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR				(0x00)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7			(0x3EF)
+#define TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR				(0x00)
+#define TOMTOM_A_CDC_BOOST_MODE_CTL			(0x3F0)
+#define TOMTOM_A_CDC_BOOST_MODE_CTL__POR				(0x00)
+#define TOMTOM_A_CDC_BOOST_THRESHOLD			(0x3F1)
+#define TOMTOM_A_CDC_BOOST_THRESHOLD__POR				(0x02)
+#define TOMTOM_A_CDC_BOOST_TAP_SEL			(0x3F2)
+#define TOMTOM_A_CDC_BOOST_TAP_SEL__POR				(0x00)
+#define TOMTOM_A_CDC_BOOST_HOLD_TIME			(0x3F3)
+#define TOMTOM_A_CDC_BOOST_HOLD_TIME__POR				(0x02)
+#define TOMTOM_A_CDC_BOOST_TRGR_EN			(0x3F4)
+#define TOMTOM_A_CDC_BOOST_TRGR_EN__POR				(0x00)
+
+/* SLIMBUS Slave Registers */
+#define TOMTOM_SLIM_PGD_PORT_INT_EN0                     (0x30)
+#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_0             (0x34)
+#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_1             (0x35)
+#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_0             (0x36)
+#define TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_1             (0x37)
+#define TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_0                (0x38)
+#define TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_1                (0x39)
+#define TOMTOM_SLIM_PGD_PORT_INT_CLR_TX_0                (0x3A)
+#define TOMTOM_SLIM_PGD_PORT_INT_CLR_TX_1                (0x3B)
+#define TOMTOM_SLIM_PGD_PORT_INT_RX_SOURCE0		(0x60)
+#define TOMTOM_SLIM_PGD_PORT_INT_TX_SOURCE0		(0x70)
+
+/* Macros for Packing Register Writes into a U32 */
+#define TOMTOM_PACKED_REG_SIZE sizeof(u32)
+
+#define TOMTOM_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\
+	((mask & 0xff) << 8)|((reg & 0xffff) << 16))
+#define TOMTOM_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+	do { \
+		((reg) = ((packed >> 16) & (0xffff))); \
+		((mask) = ((packed >> 8) & (0xff))); \
+		((val) = ((packed) & (0xff))); \
+	} while (0)
+
+#define TOMTOM_SB_PGD_PORT_TX_BASE    0x50
+#define TOMTOM_SB_PGD_PORT_RX_BASE    0x40
+#define WCD9330_MAX_REGISTER 0x3FF
+extern const u8 tomtom_reg_readable[WCD9330_MAX_REGISTER + 1];
+#endif
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h b/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h
new file mode 100644
index 0000000..1e428a1
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-irq.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/mfd/wcd9xxx/core.h>
+
+#ifndef __MFD_WCD9XXX_IRQ_H
+#define __MFD_WCD9XXX_IRQ_H
+bool wcd9xxx_lock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res);
+void wcd9xxx_unlock_sleep(struct wcd9xxx_core_resource *wcd9xxx_res);
+void wcd9xxx_nested_irq_lock(struct wcd9xxx_core_resource *wcd9xxx_res);
+void wcd9xxx_nested_irq_unlock(struct wcd9xxx_core_resource *wcd9xxx_res);
+int wcd9xxx_request_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq,
+			irq_handler_t handler, const char *name, void *data);
+
+void wcd9xxx_free_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq, void *data);
+void wcd9xxx_enable_irq(struct wcd9xxx_core_resource *wcd9xxx_res, int irq);
+void wcd9xxx_disable_irq(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq);
+void wcd9xxx_disable_irq_sync(struct wcd9xxx_core_resource *wcd9xxx_res,
+			int irq);
+
+int wcd9xxx_irq_init(struct wcd9xxx_core_resource *wcd9xxx_res);
+void wcd9xxx_irq_exit(struct wcd9xxx_core_resource *wcd9xxx_res);
+#endif
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
new file mode 100644
index 0000000..96fdb00
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-slimslave.h
@@ -0,0 +1,119 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD9310_SLIMSLAVE_H_
+#define __WCD9310_SLIMSLAVE_H_
+
+#include <linux/slimbus/slimbus.h>
+#include <linux/mfd/wcd9xxx/core.h>
+
+
+/*
+ *  client is expected to give port ids in the range of
+ *  1-10 for pre Taiko Tx ports and 1-16 for Taiko
+ *  1-7 for pre Taiko Rx ports and 1-16 for Tako,
+ *  we need to add offset for getting the absolute slave
+ *  port id before configuring the HW
+ */
+#define TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 10
+#define TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS 16
+
+#define SLIM_MAX_TX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS
+
+#define TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \
+	TABLA_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS
+#define TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS \
+	TAIKO_SB_PGD_MAX_NUMBER_OF_TX_SLAVE_DEV_PORTS
+
+#define TABLA_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 7
+#define TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS 13
+
+#define SLIM_MAX_RX_PORTS TAIKO_SB_PGD_MAX_NUMBER_OF_RX_SLAVE_DEV_PORTS
+
+#define SLIM_MAX_REG_ADDR (0x180 + 4 * (SLIM_MAX_RX_PORTS))
+
+#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \
+	TABLA_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS
+#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_START_PORT_ID \
+	TAIKO_SB_PGD_OFFSET_OF_RX_SLAVE_DEV_PORTS
+
+#define TABLA_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 16
+#define TAIKO_SB_PGD_RX_PORT_MULTI_CHANNEL_0_END_PORT_ID 31
+
+#define TABLA_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 9
+#define TAIKO_SB_PGD_TX_PORT_MULTI_CHANNEL_1_END_PORT_ID 15
+
+/* below details are taken from SLIMBUS slave SWI */
+#define SB_PGD_PORT_BASE 0x000
+
+#define SB_PGD_PORT_CFG_BYTE_ADDR(offset, port_num) \
+		(SB_PGD_PORT_BASE + offset + (1 * port_num))
+
+#define SB_PGD_TX_PORT_MULTI_CHANNEL_0(port_num) \
+		(SB_PGD_PORT_BASE + 0x100 + 4*port_num)
+#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_START_PORT_ID   0
+#define SB_PGD_TX_PORT_MULTI_CHANNEL_0_END_PORT_ID     7
+
+#define SB_PGD_TX_PORT_MULTI_CHANNEL_1(port_num) \
+		(SB_PGD_PORT_BASE + 0x101 + 4*port_num)
+#define SB_PGD_TX_PORT_MULTI_CHANNEL_1_START_PORT_ID   8
+
+#define SB_PGD_RX_PORT_MULTI_CHANNEL_0(offset, port_num) \
+		(SB_PGD_PORT_BASE + offset + (4 * port_num))
+
+/* slave port water mark level
+ *   (0: 6bytes, 1: 9bytes, 2: 12 bytes, 3: 15 bytes)
+ */
+#define SLAVE_PORT_WATER_MARK_6BYTES  0
+#define SLAVE_PORT_WATER_MARK_9BYTES  1
+#define SLAVE_PORT_WATER_MARK_12BYTES 2
+#define SLAVE_PORT_WATER_MARK_15BYTES 3
+#define SLAVE_PORT_WATER_MARK_SHIFT 1
+#define SLAVE_PORT_ENABLE           1
+#define SLAVE_PORT_DISABLE          0
+#define WATER_MARK_VAL \
+	((SLAVE_PORT_WATER_MARK_12BYTES << SLAVE_PORT_WATER_MARK_SHIFT) | \
+	 (SLAVE_PORT_ENABLE))
+#define BASE_CH_NUM 128
+
+
+int wcd9xxx_init_slimslave(struct wcd9xxx *wcd9xxx,
+			   u8 wcd9xxx_pgd_la,
+			   unsigned int tx_num, unsigned int *tx_slot,
+			   unsigned int rx_num, unsigned int *rx_slot);
+
+int wcd9xxx_deinit_slimslave(struct wcd9xxx *wcd9xxx);
+
+int wcd9xxx_cfg_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+			    u16 *grph);
+int wcd9xxx_cfg_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list,
+			    unsigned int rate, unsigned int bit_width,
+				u16 *grph);
+int wcd9xxx_close_slim_sch_rx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_close_slim_sch_tx(struct wcd9xxx *wcd9xxx,
+			      struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_get_channel(struct wcd9xxx *wcd9xxx,
+			unsigned int *rx_ch,
+			unsigned int *tx_ch);
+int wcd9xxx_get_slave_port(unsigned int ch_num);
+int wcd9xxx_disconnect_port(struct wcd9xxx *wcd9xxx,
+			    struct list_head *wcd9xxx_ch_list, u16 grph);
+int wcd9xxx_rx_vport_validation(u32 port_id,
+				struct list_head *codec_dai_list);
+int wcd9xxx_tx_vport_validation(u32 vtable, u32 port_id,
+				struct wcd9xxx_codec_dai_data *codec_dai,
+				u32 num_codec_dais);
+#endif /* __WCD9310_SLIMSLAVE_H_ */
diff --git a/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h b/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h
new file mode 100644
index 0000000..d0ac0ac
--- /dev/null
+++ b/include/linux/mfd/wcd9xxx/wcd9xxx-utils.h
@@ -0,0 +1,140 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD9XXX_UTILS_H__
+#define __WCD9XXX_UTILS_H__
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/mfd/wcd9xxx/core.h>
+
+struct wcd9xxx_pdata *wcd9xxx_populate_dt_data(struct device *dev);
+int wcd9xxx_bringup(struct device *dev);
+int wcd9xxx_bringdown(struct device *dev);
+struct regmap *wcd9xxx_regmap_init(struct device *dev,
+				   const struct regmap_config *config);
+int wcd9xxx_reset(struct device *dev);
+int wcd9xxx_reset_low(struct device *dev);
+int wcd9xxx_get_codec_info(struct device *dev);
+
+typedef int (*codec_bringup_fn)(struct wcd9xxx *);
+typedef int (*codec_bringdown_fn)(struct wcd9xxx *);
+typedef int (*codec_type_fn)(struct wcd9xxx *,
+			     struct wcd9xxx_codec_type *);
+
+#ifdef CONFIG_WCD934X_CODEC
+extern int wcd934x_bringup(struct wcd9xxx *wcd9xxx);
+extern int wcd934x_bringdown(struct wcd9xxx *wcd9xxx);
+extern int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx,
+				  struct wcd9xxx_codec_type *wcd_type);
+#endif
+
+#ifdef CONFIG_WCD9335_CODEC
+extern int wcd9335_bringup(struct wcd9xxx *wcd9xxx);
+extern int wcd9335_bringdown(struct wcd9xxx *wcd9xxx);
+extern int wcd9335_get_codec_info(struct wcd9xxx *wcd9xxx,
+				  struct wcd9xxx_codec_type *wcd_type);
+#endif
+
+#ifdef CONFIG_WCD9330_CODEC
+extern int wcd9330_bringup(struct wcd9xxx *wcd9xxx);
+extern int wcd9330_bringdown(struct wcd9xxx *wcd9xxx);
+extern int wcd9330_get_codec_info(struct wcd9xxx *wcd9xxx,
+				  struct wcd9xxx_codec_type *wcd_type);
+#endif
+
+static inline codec_bringdown_fn wcd9xxx_bringdown_fn(int type)
+{
+	codec_bringdown_fn cdc_bdown_fn;
+
+	switch (type) {
+#ifdef CONFIG_WCD934X_CODEC
+	case WCD934X:
+		cdc_bdown_fn = wcd934x_bringdown;
+		break;
+#endif
+#ifdef CONFIG_WCD9335_CODEC
+	case WCD9335:
+		cdc_bdown_fn = wcd9335_bringdown;
+		break;
+#endif
+#ifdef CONFIG_WCD9330_CODEC
+	case WCD9330:
+		cdc_bdown_fn = wcd9330_bringdown;
+		break;
+#endif
+	default:
+		cdc_bdown_fn = NULL;
+		break;
+	}
+
+	return cdc_bdown_fn;
+}
+
+static inline codec_bringup_fn wcd9xxx_bringup_fn(int type)
+{
+	codec_bringup_fn cdc_bup_fn;
+
+	switch (type) {
+#ifdef CONFIG_WCD934X_CODEC
+	case WCD934X:
+		cdc_bup_fn = wcd934x_bringup;
+		break;
+#endif
+#ifdef CONFIG_WCD9335_CODEC
+	case WCD9335:
+		cdc_bup_fn = wcd9335_bringup;
+		break;
+#endif
+#ifdef CONFIG_WCD9330_CODEC
+	case WCD9330:
+		cdc_bup_fn = wcd9330_bringup;
+		break;
+#endif
+	default:
+		cdc_bup_fn = NULL;
+		break;
+	}
+
+	return cdc_bup_fn;
+}
+
+static inline codec_type_fn wcd9xxx_get_codec_info_fn(int type)
+{
+	codec_type_fn cdc_type_fn;
+
+	switch (type) {
+#ifdef CONFIG_WCD934X_CODEC
+	case WCD934X:
+		cdc_type_fn = wcd934x_get_codec_info;
+		break;
+#endif
+#ifdef CONFIG_WCD9335_CODEC
+	case WCD9335:
+		cdc_type_fn = wcd9335_get_codec_info;
+		break;
+#endif
+#ifdef CONFIG_WCD9330_CODEC
+	case WCD9330:
+		cdc_type_fn = wcd9330_get_codec_info;
+		break;
+#endif
+	default:
+		cdc_type_fn = NULL;
+		break;
+	}
+
+	return cdc_type_fn;
+}
+#endif
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index d0fc40b..579fd0a 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -502,6 +502,14 @@ struct dmi_system_id {
 #define DMI_MATCH(a, b)	{ .slot = a, .substr = b }
 #define DMI_EXACT_MATCH(a, b)	{ .slot = a, .substr = b, .exact_match = 1 }
 
+#define SLIMBUS_NAME_SIZE	32
+#define SLIMBUS_MODULE_PREFIX "slim:"
+
+struct slim_device_id {
+	char name[SLIMBUS_NAME_SIZE];
+	kernel_ulong_t driver_data;	/* Data private to the driver */
+};
+
 #define PLATFORM_NAME_SIZE	20
 #define PLATFORM_MODULE_PREFIX	"platform:"
 
diff --git a/include/linux/msm_audio_ion.h b/include/linux/msm_audio_ion.h
new file mode 100644
index 0000000..0761b88
--- /dev/null
+++ b/include/linux/msm_audio_ion.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_MSM_AUDIO_ION_H
+#define _LINUX_MSM_AUDIO_ION_H
+#include <sound/q6asm-v2.h>
+#include <sound/pcm.h>
+#include <linux/msm_ion.h>
+
+
+int msm_audio_ion_alloc(const char *name, struct ion_client **client,
+			struct ion_handle **handle, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr);
+
+int msm_audio_ion_import(const char *name, struct ion_client **client,
+			struct ion_handle **handle, int fd,
+			unsigned long *ionflag, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr);
+int msm_audio_ion_free(struct ion_client *client, struct ion_handle *handle);
+int msm_audio_ion_mmap(struct audio_buffer *substream,
+		       struct vm_area_struct *vma);
+
+bool msm_audio_ion_is_smmu_available(void);
+int msm_audio_ion_cache_operations(struct audio_buffer *abuff, int cache_op);
+
+struct ion_client *msm_audio_ion_client_create(const char *name);
+void msm_audio_ion_client_destroy(struct ion_client *client);
+int msm_audio_ion_import_legacy(const char *name, struct ion_client *client,
+			struct ion_handle **handle, int fd,
+			unsigned long *ionflag, size_t bufsz,
+			ion_phys_addr_t *paddr, size_t *pa_len, void **vaddr);
+int msm_audio_ion_free_legacy(struct ion_client *client,
+			struct ion_handle *handle);
+u32 msm_audio_populate_upper_32_bits(ion_phys_addr_t pa);
+#endif /* _LINUX_MSM_AUDIO_ION_H */
diff --git a/include/linux/msm_gsi.h b/include/linux/msm_gsi.h
index fb2607d..6037fbf 100644
--- a/include/linux/msm_gsi.h
+++ b/include/linux/msm_gsi.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -1040,6 +1040,19 @@ int gsi_configure_regs(phys_addr_t gsi_base_addr, u32 gsi_size,
  */
 int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size);
 
+/**
+ * gsi_get_inst_ram_offset_and_size - Peripheral should call this function
+ * to get instruction RAM base address offset and size. Peripheral typically
+ * uses this info to load GSI FW into the IRAM.
+ *
+ * @base_offset:[OUT] - IRAM base offset address
+ * @size:	[OUT] - IRAM size
+
+ * @Return none
+ */
+void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset,
+		unsigned long *size);
+
 /*
  * Here is a typical sequence of calls
  *
@@ -1227,9 +1240,15 @@ static inline int gsi_configure_regs(phys_addr_t gsi_base_addr, u32 gsi_size,
 {
 	return -GSI_STATUS_UNSUPPORTED_OP;
 }
+
 static inline int gsi_enable_fw(phys_addr_t gsi_base_addr, u32 gsi_size)
 {
 	return -GSI_STATUS_UNSUPPORTED_OP;
 }
+
+static inline void gsi_get_inst_ram_offset_and_size(unsigned long *base_offset,
+		unsigned long *size)
+{
+}
 #endif
 #endif
diff --git a/include/linux/msm_pcie.h b/include/linux/msm_pcie.h
new file mode 100644
index 0000000..8316aaa
--- /dev/null
+++ b/include/linux/msm_pcie.h
@@ -0,0 +1,217 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_PCIE_H
+#define __MSM_PCIE_H
+
+#include <linux/types.h>
+#include <linux/pci.h>
+
+enum msm_pcie_config {
+	MSM_PCIE_CONFIG_INVALID = 0,
+	MSM_PCIE_CONFIG_NO_CFG_RESTORE = 0x1,
+	MSM_PCIE_CONFIG_LINKDOWN = 0x2,
+	MSM_PCIE_CONFIG_NO_RECOVERY = 0x4,
+};
+
+enum msm_pcie_pm_opt {
+	MSM_PCIE_SUSPEND,
+	MSM_PCIE_RESUME,
+	MSM_PCIE_DISABLE_PC,
+	MSM_PCIE_ENABLE_PC,
+};
+
+enum msm_pcie_event {
+	MSM_PCIE_EVENT_INVALID = 0,
+	MSM_PCIE_EVENT_LINKDOWN = 0x1,
+	MSM_PCIE_EVENT_LINKUP = 0x2,
+	MSM_PCIE_EVENT_WAKEUP = 0x4,
+};
+
+enum msm_pcie_trigger {
+	MSM_PCIE_TRIGGER_CALLBACK,
+	MSM_PCIE_TRIGGER_COMPLETION,
+};
+
+struct msm_pcie_notify {
+	enum msm_pcie_event event;
+	void *user;
+	void *data;
+	u32 options;
+};
+
+struct msm_pcie_register_event {
+	u32 events;
+	void *user;
+	enum msm_pcie_trigger mode;
+	void (*callback)(struct msm_pcie_notify *notify);
+	struct msm_pcie_notify notify;
+	struct completion *completion;
+	u32 options;
+};
+
+#ifdef CONFIG_PCI_MSM
+/**
+ * msm_pcie_pm_control - control the power state of a PCIe link.
+ * @pm_opt:	power management operation
+ * @busnr:	bus number of PCIe endpoint
+ * @user:	handle of the caller
+ * @data:	private data from the caller
+ * @options:	options for pm control
+ *
+ * This function gives PCIe endpoint device drivers the control to change
+ * the power state of a PCIe link for their device.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr, void *user,
+			void *data, u32 options);
+
+/**
+ * msm_pcie_register_event - register an event with PCIe bus driver.
+ * @reg:	event structure
+ *
+ * This function gives PCIe endpoint device drivers an option to register
+ * events with PCIe bus driver.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_register_event(struct msm_pcie_register_event *reg);
+
+/**
+ * msm_pcie_deregister_event - deregister an event with PCIe bus driver.
+ * @reg:	event structure
+ *
+ * This function gives PCIe endpoint device drivers an option to deregister
+ * events with PCIe bus driver.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_deregister_event(struct msm_pcie_register_event *reg);
+
+/**
+ * msm_pcie_recover_config - recover config space.
+ * @dev:	pci device structure
+ *
+ * This function recovers the config space of both RC and Endpoint.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_recover_config(struct pci_dev *dev);
+
+/**
+ * msm_pcie_enumerate - enumerate Endpoints.
+ * @rc_idx:	RC that Endpoints connect to.
+ *
+ * This function enumerates Endpoints connected to RC.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_enumerate(u32 rc_idx);
+
+/**
+ * msm_pcie_recover_config - recover config space.
+ * @dev:	pci device structure
+ *
+ * This function recovers the config space of both RC and Endpoint.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_recover_config(struct pci_dev *dev);
+
+/**
+ * msm_pcie_shadow_control - control the shadowing of PCIe config space.
+ * @dev:	pci device structure
+ * @enable:	shadowing should be enabled or disabled
+ *
+ * This function gives PCIe endpoint device drivers the control to enable
+ * or disable the shadowing of PCIe config space.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_shadow_control(struct pci_dev *dev, bool enable);
+
+/*
+ * msm_pcie_debug_info - run a PCIe specific debug testcase.
+ * @dev:	pci device structure
+ * @option:	specifies which PCIe debug testcase to execute
+ * @base:	PCIe specific range
+ * @offset:	offset of destination register
+ * @mask:	mask the bit(s) of destination register
+ * @value:	value to be written to destination register
+ *
+ * This function gives PCIe endpoint device drivers the control to
+ * run a debug testcase.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
+			u32 offset, u32 mask, u32 value);
+
+/*
+ * msm_pcie_configure_sid - calculates the SID for a PCIe endpoint.
+ * @dev:	device structure
+ * @sid:	the calculated SID
+ * @domain:	the domain number of the Root Complex
+ *
+ * This function calculates the SID for a PCIe endpoint device.
+ *
+ * Return: 0 on success, negative value on error
+ */
+int msm_pcie_configure_sid(struct device *dev, u32 *sid,
+			int *domain);
+#else /* !CONFIG_PCI_MSM */
+static inline int msm_pcie_pm_control(enum msm_pcie_pm_opt pm_opt, u32 busnr,
+			void *user, void *data, u32 options)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_register_event(struct msm_pcie_register_event *reg)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_deregister_event(struct msm_pcie_register_event *reg)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_recover_config(struct pci_dev *dev)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_enumerate(u32 rc_idx)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_shadow_control(struct pci_dev *dev, bool enable)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_debug_info(struct pci_dev *dev, u32 option, u32 base,
+			u32 offset, u32 mask, u32 value)
+{
+	return -ENODEV;
+}
+
+static inline int msm_pcie_configure_sid(struct device *dev, u32 *sid,
+			int *domain)
+{
+	return -ENODEV;
+}
+#endif /* CONFIG_PCI_MSM */
+
+#endif /* __MSM_PCIE_H */
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 3786473..65003a0 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -37,6 +37,8 @@ extern struct device_node *of_find_matching_node_by_address(
 					const struct of_device_id *matches,
 					u64 base_address);
 extern void __iomem *of_iomap(struct device_node *device, int index);
+extern void __iomem *of_iomap_by_name(struct device_node *device,
+					const char *name);
 void __iomem *of_io_request_and_map(struct device_node *device,
 				    int index, const char *name);
 
@@ -46,6 +48,8 @@ void __iomem *of_io_request_and_map(struct device_node *device,
  */
 extern const __be32 *of_get_address(struct device_node *dev, int index,
 			   u64 *size, unsigned int *flags);
+extern const __be32 *of_get_address_by_name(struct device_node *dev,
+			   const char *name, u64 *size, unsigned int *flags);
 
 extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
 			struct device_node *node);
diff --git a/include/linux/of_slimbus.h b/include/linux/of_slimbus.h
new file mode 100644
index 0000000..f686cdc
--- /dev/null
+++ b/include/linux/of_slimbus.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slimbus/slimbus.h>
+#include <linux/of_irq.h>
+
+#ifdef CONFIG_OF_SLIMBUS
+/*
+ * of_slim_register_devices() - Register devices in the SLIMbus Device Tree
+ * @ctrl: slim_controller which devices should be registered to.
+ *
+ * This routine scans the SLIMbus Device Tree, allocating resources and
+ * creating slim_devices according to the SLIMbus Device Tree
+ * hierarchy. Details of this hierarchy can be found in
+ * Documentation/devicetree/bindings/slimbus. This routine is normally
+ * called from the probe routine of the driver registering as a
+ * slim_controller.
+ */
+extern int of_register_slim_devices(struct slim_controller *ctrl);
+#else
+static int of_register_slim_devices(struct slim_controller *ctrl)
+{
+	return 0;
+}
+#endif /* CONFIG_OF_SLIMBUS */
diff --git a/include/linux/power/qcom/apm.h b/include/linux/power/qcom/apm.h
new file mode 100644
index 0000000..8ac8863
--- /dev/null
+++ b/include/linux/power/qcom/apm.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_POWER_QCOM_APM_H__
+#define __LINUX_POWER_QCOM_APM_H__
+
+#include <linux/device.h>
+#include <linux/err.h>
+
+/**
+ * enum msm_apm_supply - supported power rails to supply memory arrays
+ * %MSM_APM_SUPPLY_APCC:	to enable selection of VDD_APCC rail as supply
+ * %MSM_APM_SUPPLY_MX:		to enable selection of VDD_MX rail as supply
+ */
+enum msm_apm_supply {
+	MSM_APM_SUPPLY_APCC,
+	MSM_APM_SUPPLY_MX,
+};
+
+/* Handle used to identify an APM controller device  */
+struct msm_apm_ctrl_dev;
+
+#ifdef CONFIG_MSM_APM
+struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev);
+int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev,
+		       enum msm_apm_supply supply);
+int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev);
+
+#else
+static inline struct msm_apm_ctrl_dev *msm_apm_ctrl_dev_get(struct device *dev)
+{ return ERR_PTR(-EPERM); }
+static inline int msm_apm_set_supply(struct msm_apm_ctrl_dev *ctrl_dev,
+		       enum msm_apm_supply supply)
+{ return -EPERM; }
+static inline int msm_apm_get_supply(struct msm_apm_ctrl_dev *ctrl_dev)
+{ return -EPERM; }
+#endif
+#endif
diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h
index c30fd4b..42b590f 100644
--- a/include/linux/power_supply.h
+++ b/include/linux/power_supply.h
@@ -153,7 +153,18 @@ enum power_supply_property {
 	POWER_SUPPLY_PROP_USB_HC,
 	POWER_SUPPLY_PROP_USB_OTG,
 	POWER_SUPPLY_PROP_CHARGE_ENABLED,
+	POWER_SUPPLY_PROP_TYPEC_MODE,
+	POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, /* 0: N/C, 1: CC1, 2: CC2 */
+	POWER_SUPPLY_PROP_TYPEC_POWER_ROLE,
+	POWER_SUPPLY_PROP_PD_ALLOWED,
+	POWER_SUPPLY_PROP_PD_ACTIVE,
+	POWER_SUPPLY_PROP_PD_IN_HARD_RESET,
+	POWER_SUPPLY_PROP_PD_CURRENT_MAX,
+	POWER_SUPPLY_PROP_PD_USB_SUSPEND_SUPPORTED,
+	POWER_SUPPLY_PROP_PE_START,
 	POWER_SUPPLY_PROP_SET_SHIP_MODE,
+	POWER_SUPPLY_PROP_BOOST_CURRENT,
+	POWER_SUPPLY_PROP_FORCE_TLIM,
 	/* Local extensions of type int64_t */
 	POWER_SUPPLY_PROP_CHARGE_COUNTER_EXT,
 	/* Properties of type `const char *' */
@@ -176,6 +187,31 @@ enum power_supply_type {
 	POWER_SUPPLY_TYPE_USB_PD_DRP,	/* PD Dual Role Port */
 };
 
+/* Indicates USB Type-C CC connection status */
+enum power_supply_typec_mode {
+	POWER_SUPPLY_TYPEC_NONE,
+
+	/* Acting as source */
+	POWER_SUPPLY_TYPEC_SINK,			/* Rd only */
+	POWER_SUPPLY_TYPEC_SINK_POWERED_CABLE,		/* Rd/Ra */
+	POWER_SUPPLY_TYPEC_SINK_DEBUG_ACCESSORY,	/* Rd/Rd */
+	POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER,		/* Ra/Ra */
+	POWER_SUPPLY_TYPEC_POWERED_CABLE_ONLY,		/* Ra only */
+
+	/* Acting as sink */
+	POWER_SUPPLY_TYPEC_SOURCE_DEFAULT,		/* Rp default */
+	POWER_SUPPLY_TYPEC_SOURCE_MEDIUM,		/* Rp 1.5A */
+	POWER_SUPPLY_TYPEC_SOURCE_HIGH,			/* Rp 3A */
+	POWER_SUPPLY_TYPEC_NON_COMPLIANT,
+};
+
+enum power_supply_typec_power_role {
+	POWER_SUPPLY_TYPEC_PR_NONE,	/* CC lines in high-Z */
+	POWER_SUPPLY_TYPEC_PR_DUAL,
+	POWER_SUPPLY_TYPEC_PR_SINK,
+	POWER_SUPPLY_TYPEC_PR_SOURCE,
+};
+
 enum power_supply_notifier_events {
 	PSY_EVENT_PROP_CHANGED,
 };
diff --git a/include/linux/qdsp6v2/apr.h b/include/linux/qdsp6v2/apr.h
new file mode 100644
index 0000000..29deb3c
--- /dev/null
+++ b/include/linux/qdsp6v2/apr.h
@@ -0,0 +1,190 @@
+/* Copyright (c) 2010-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __APR_H_
+#define __APR_H_
+
+#include <linux/mutex.h>
+#include <soc/qcom/subsystem_notif.h>
+
+enum apr_subsys_state {
+	APR_SUBSYS_DOWN,
+	APR_SUBSYS_UP,
+	APR_SUBSYS_LOADED,
+};
+
+struct apr_q6 {
+	void *pil;
+	atomic_t q6_state;
+	atomic_t modem_state;
+	struct mutex lock;
+};
+
+struct apr_hdr {
+	uint16_t hdr_field;
+	uint16_t pkt_size;
+	uint8_t src_svc;
+	uint8_t src_domain;
+	uint16_t src_port;
+	uint8_t dest_svc;
+	uint8_t dest_domain;
+	uint16_t dest_port;
+	uint32_t token;
+	uint32_t opcode;
+};
+
+#define APR_HDR_LEN(hdr_len) ((hdr_len)/4)
+#define APR_PKT_SIZE(hdr_len, payload_len) ((hdr_len) + (payload_len))
+#define APR_HDR_FIELD(msg_type, hdr_len, ver)\
+	(((msg_type & 0x3) << 8) | ((hdr_len & 0xF) << 4) | (ver & 0xF))
+
+#define APR_HDR_SIZE sizeof(struct apr_hdr)
+
+/* Version */
+#define APR_PKT_VER		0x0
+
+/* Command and Response Types */
+#define APR_MSG_TYPE_EVENT	0x0
+#define APR_MSG_TYPE_CMD_RSP	0x1
+#define APR_MSG_TYPE_SEQ_CMD	0x2
+#define APR_MSG_TYPE_NSEQ_CMD	0x3
+#define APR_MSG_TYPE_MAX	0x04
+
+/* APR Basic Response Message */
+#define APR_BASIC_RSP_RESULT 0x000110E8
+#define APR_RSP_ACCEPTED     0x000100BE
+
+/* Domain IDs */
+#define APR_DOMAIN_SIM	0x1
+#define APR_DOMAIN_PC		0x2
+#define APR_DOMAIN_MODEM	0x3
+#define APR_DOMAIN_ADSP	0x4
+#define APR_DOMAIN_APPS	0x5
+#define APR_DOMAIN_MAX	0x6
+
+/* ADSP service IDs */
+#define APR_SVC_TEST_CLIENT     0x2
+#define APR_SVC_ADSP_CORE	0x3
+#define APR_SVC_AFE		0x4
+#define APR_SVC_VSM		0x5
+#define APR_SVC_VPM		0x6
+#define APR_SVC_ASM		0x7
+#define APR_SVC_ADM		0x8
+#define APR_SVC_ADSP_MVM	0x09
+#define APR_SVC_ADSP_CVS	0x0A
+#define APR_SVC_ADSP_CVP	0x0B
+#define APR_SVC_USM		0x0C
+#define APR_SVC_LSM		0x0D
+#define APR_SVC_VIDC		0x16
+#define APR_SVC_MAX		0x17
+
+/* Modem Service IDs */
+#define APR_SVC_MVS		0x3
+#define APR_SVC_MVM		0x4
+#define APR_SVC_CVS		0x5
+#define APR_SVC_CVP		0x6
+#define APR_SVC_SRD		0x7
+
+/* APR Port IDs */
+#define APR_MAX_PORTS		0x80
+
+#define APR_NAME_MAX		0x40
+
+#define RESET_EVENTS		0x000130D7
+
+#define LPASS_RESTART_EVENT	0x1000
+#define LPASS_RESTART_READY	0x1001
+
+struct apr_client_data {
+	uint16_t reset_event;
+	uint16_t reset_proc;
+	uint16_t payload_size;
+	uint16_t hdr_len;
+	uint16_t msg_type;
+	uint16_t src;
+	uint16_t dest_svc;
+	uint16_t src_port;
+	uint16_t dest_port;
+	uint32_t token;
+	uint32_t opcode;
+	void *payload;
+};
+
+typedef int32_t (*apr_fn)(struct apr_client_data *data, void *priv);
+
+struct apr_svc {
+	uint16_t id;
+	uint16_t dest_id;
+	uint16_t client_id;
+	uint16_t dest_domain;
+	uint8_t rvd;
+	uint8_t port_cnt;
+	uint8_t svc_cnt;
+	uint8_t need_reset;
+	apr_fn port_fn[APR_MAX_PORTS];
+	void *port_priv[APR_MAX_PORTS];
+	apr_fn fn;
+	void *priv;
+	struct mutex m_lock;
+	spinlock_t w_lock;
+	uint8_t pkt_owner;
+};
+
+struct apr_client {
+	uint8_t id;
+	uint8_t svc_cnt;
+	uint8_t rvd;
+	struct mutex m_lock;
+	struct apr_svc_ch_dev *handle;
+	struct apr_svc svc[APR_SVC_MAX];
+};
+
+struct apr_rx_intents {
+	int num_of_intents;
+	uint32_t size;
+};
+
+struct apr_pkt_cfg {
+	uint8_t pkt_owner;
+	struct apr_rx_intents intents;
+};
+
+int apr_load_adsp_image(void);
+struct apr_client *apr_get_client(int dest_id, int client_id);
+int apr_wait_for_device_up(int dest_id);
+int apr_get_svc(const char *svc_name, int dest_id, int *client_id,
+		int *svc_idx, int *svc_id);
+void apr_cb_func(void *buf, int len, void *priv);
+struct apr_svc *apr_register(char *dest, char *svc_name, apr_fn svc_fn,
+					uint32_t src_port, void *priv);
+inline int apr_fill_hdr(void *handle, uint32_t *buf, uint16_t src_port,
+			uint16_t msg_type, uint16_t dest_port,
+			uint32_t token, uint32_t opcode, uint16_t len);
+
+int apr_send_pkt(void *handle, uint32_t *buf);
+int apr_deregister(void *handle);
+void subsys_notif_register(char *client_name, int domain,
+			   struct notifier_block *nb);
+int apr_get_dest_id(char *dest);
+uint16_t apr_get_data_src(struct apr_hdr *hdr);
+void change_q6_state(int state);
+void q6audio_dsp_not_responding(void);
+void apr_reset(void *handle);
+enum apr_subsys_state apr_get_subsys_state(void);
+enum apr_subsys_state apr_get_modem_state(void);
+void apr_set_modem_state(enum apr_subsys_state state);
+enum apr_subsys_state apr_get_q6_state(void);
+int apr_set_q6_state(enum apr_subsys_state state);
+void apr_set_subsys_state(void);
+const char *apr_get_lpass_subsys_name(void);
+uint16_t apr_get_reset_domain(uint16_t proc);
+#endif
diff --git a/include/linux/qdsp6v2/apr_tal.h b/include/linux/qdsp6v2/apr_tal.h
new file mode 100644
index 0000000..32c977f
--- /dev/null
+++ b/include/linux/qdsp6v2/apr_tal.h
@@ -0,0 +1,108 @@
+/* Copyright (c) 2010-2011, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __APR_TAL_H_
+#define __APR_TAL_H_
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+
+/* APR Client IDs */
+#define APR_CLIENT_AUDIO	0x0
+#define APR_CLIENT_VOICE	0x1
+#define APR_CLIENT_MAX		0x2
+
+#define APR_DL_SMD    0
+#define APR_DL_MAX    1
+
+#define APR_DEST_MODEM 0
+#define APR_DEST_QDSP6 1
+#define APR_DEST_MAX   2
+
+#if defined(CONFIG_MSM_QDSP6_APRV2_GLINK) || \
+	defined(CONFIG_MSM_QDSP6_APRV3_GLINK)
+#define APR_MAX_BUF			512
+#define APR_NUM_OF_TX_BUF		30
+#else
+#define APR_MAX_BUF			8092
+#endif
+
+#define APR_DEFAULT_NUM_OF_INTENTS 20
+
+#define APR_OPEN_TIMEOUT_MS 5000
+
+enum {
+	/* If client sets the pkt_owner to APR_PKT_OWNER_DRIVER, APR
+	 * driver will allocate a buffer, where the user packet is
+	 * copied into, for each and every single Tx transmission.
+	 * The buffer is thereafter passed to underlying link layer
+	 * and freed upon the notification received from the link layer
+	 * that the packet has been consumed.
+	 */
+	APR_PKT_OWNER_DRIVER,
+	/* If client sets the pkt_owner to APR_PKT_OWNER_CLIENT, APR
+	 * will pass the user packet memory address directly to underlying
+	 * link layer. In this case it is the client's responsibility to
+	 * make sure the packet is intact until being notified that the
+	 * packet has been consumed.
+	 */
+	APR_PKT_OWNER_CLIENT,
+};
+
+struct apr_pkt_priv {
+	/* This property is only applicable for APR over Glink.
+	 * It is ignored in APR over SMD cases.
+	 */
+	uint8_t pkt_owner;
+};
+
+typedef void (*apr_svc_cb_fn)(void *buf, int len, void *priv);
+struct apr_svc_ch_dev *apr_tal_open(uint32_t svc, uint32_t dest,
+			uint32_t dl, apr_svc_cb_fn func, void *priv);
+int apr_tal_write(struct apr_svc_ch_dev *apr_ch, void *data,
+		struct apr_pkt_priv *pkt_priv, int len);
+int apr_tal_close(struct apr_svc_ch_dev *apr_ch);
+int apr_tal_rx_intents_config(struct apr_svc_ch_dev *apr_ch,
+		int num_of_intents, uint32_t size);
+
+
+#if defined(CONFIG_MSM_QDSP6_APRV2_GLINK) || \
+	 defined(CONFIG_MSM_QDSP6_APRV3_GLINK)
+struct apr_svc_ch_dev {
+	void               *handle;
+	spinlock_t         w_lock;
+	spinlock_t         r_lock;
+	struct mutex       m_lock;
+	apr_svc_cb_fn      func;
+	wait_queue_head_t  wait;
+	void               *priv;
+	unsigned int       channel_state;
+	bool               if_remote_intent_ready;
+};
+#else
+struct apr_svc_ch_dev {
+	struct smd_channel *ch;
+	spinlock_t         lock;
+	spinlock_t         w_lock;
+	struct mutex       m_lock;
+	apr_svc_cb_fn      func;
+	char               data[APR_MAX_BUF];
+	wait_queue_head_t  wait;
+	void               *priv;
+	uint32_t           smd_state;
+	wait_queue_head_t  dest;
+	uint32_t           dest_state;
+};
+#endif
+
+#endif
diff --git a/include/linux/qdsp6v2/apr_us.h b/include/linux/qdsp6v2/apr_us.h
new file mode 100644
index 0000000..9a6804a
--- /dev/null
+++ b/include/linux/qdsp6v2/apr_us.h
@@ -0,0 +1,193 @@
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __APR_US_H__
+#define __APR_US_H__
+
+#include <linux/qdsp6v2/apr.h>
+
+/* ======================================================================= */
+/*  Session Level commands */
+
+#define USM_SESSION_CMD_RUN				0x00012306
+struct usm_stream_cmd_run {
+	struct apr_hdr hdr;
+	u32            flags;
+	u32            msw_ts;
+	u32            lsw_ts;
+} __packed;
+
+/* Stream level commands */
+#define USM_STREAM_CMD_OPEN_READ			0x00012309
+struct usm_stream_cmd_open_read {
+	struct apr_hdr hdr;
+	u32            uMode;
+	u32            src_endpoint;
+	u32            pre_proc_top;
+	u32            format;
+} __packed;
+
+#define USM_STREAM_CMD_OPEN_WRITE			0x00011271
+struct usm_stream_cmd_open_write {
+	struct apr_hdr hdr;
+	u32            format;
+} __packed;
+
+
+#define USM_STREAM_CMD_CLOSE				0x0001230A
+
+#define USM_STREAM_CMD_SET_PARAM			0x00012731
+struct usm_stream_cmd_set_param {
+	struct apr_hdr hdr;
+	u32            buf_addr_lsw;
+	u32            buf_addr_msw;
+	u32            mem_map_handle;
+	u32            buf_size;
+	u32            module_id;
+	u32            param_id;
+} __packed;
+
+#define USM_STREAM_CMD_GET_PARAM			0x00012732
+struct usm_stream_cmd_get_param {
+	struct apr_hdr hdr;
+	u32            buf_addr_lsw;
+	u32            buf_addr_msw;
+	u32            mem_map_handle;
+	u32            buf_size;
+	u32            module_id;
+	u32            param_id;
+} __packed;
+
+/* Encoder configuration definitions */
+#define USM_STREAM_CMD_SET_ENC_PARAM			0x0001230B
+/* Decoder configuration definitions */
+#define USM_DATA_CMD_MEDIA_FORMAT_UPDATE		0x00011272
+
+/* Encoder/decoder configuration block */
+#define USM_PARAM_ID_ENCDEC_ENC_CFG_BLK			0x0001230D
+
+/* Max number of static located ports (bytes) */
+#define USM_MAX_PORT_NUMBER 8
+
+/* Max number of static located transparent data (bytes) */
+#define USM_MAX_CFG_DATA_SIZE 100
+
+/* Parameter structures used in  USM_STREAM_CMD_SET_ENCDEC_PARAM command */
+/* common declarations */
+struct usm_cfg_common {
+	u16 ch_cfg;
+	u16 bits_per_sample;
+	u32 sample_rate;
+	u32 dev_id;
+	u8 data_map[USM_MAX_PORT_NUMBER];
+} __packed;
+
+struct us_encdec_cfg {
+	u32 format_id;
+	struct usm_cfg_common cfg_common;
+	u16 params_size;
+	u8 *params;
+} __packed;
+
+/* Start/stop US signal detection */
+#define USM_SESSION_CMD_SIGNAL_DETECT_MODE		0x00012719
+
+struct usm_session_cmd_detect_info {
+	struct apr_hdr hdr;
+	u32 detect_mode;
+	u32 skip_interval;
+	u32 algorithm_cfg_size;
+} __packed;
+
+/* US signal detection result */
+#define USM_SESSION_EVENT_SIGNAL_DETECT_RESULT		0x00012720
+
+/* ======================================================================= */
+/*  Session Level commands */
+#define USM_CMD_SHARED_MEM_MAP_REGION		0x00012728
+struct usm_cmd_memory_map_region {
+	struct apr_hdr hdr;
+	u16            mempool_id;
+	u16            num_regions;
+	u32            flags;
+	u32            shm_addr_lsw;
+	u32            shm_addr_msw;
+	u32            mem_size_bytes;
+} __packed;
+
+#define USM_CMDRSP_SHARED_MEM_MAP_REGION	0x00012729
+struct usm_cmdrsp_memory_map_region {
+	u32            mem_map_handle;
+} __packed;
+
+#define USM_CMD_SHARED_MEM_UNMAP_REGION         0x0001272A
+struct usm_cmd_memory_unmap_region {
+	struct apr_hdr hdr;
+	u32            mem_map_handle;
+} __packed;
+
+#define USM_DATA_CMD_READ			0x00012724
+struct usm_stream_cmd_read {
+	struct apr_hdr hdr;
+	u32            buf_addr_lsw;
+	u32            buf_addr_msw;
+	u32            mem_map_handle;
+	u32            buf_size;
+	u32            seq_id;
+	u32            counter;
+} __packed;
+
+#define USM_DATA_EVENT_READ_DONE		0x00012725
+
+#define USM_DATA_CMD_WRITE			0x00012726
+struct usm_stream_cmd_write {
+	struct apr_hdr hdr;
+	u32            buf_addr_lsw;
+	u32            buf_addr_msw;
+	u32            mem_map_handle;
+	u32            buf_size;
+	u32            seq_id;
+	u32            res0;
+	u32            res1;
+	u32            res2;
+} __packed;
+
+#define USM_DATA_EVENT_WRITE_DONE		0x00012727
+
+struct usm_stream_media_format_update {
+	struct apr_hdr hdr;
+	u32 format_id;
+	/* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+	u32 cfg_size;
+	struct usm_cfg_common cfg_common;
+	/* Transparent configuration data for specific encoder */
+	u8  transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_encode_cfg_blk {
+	u32 frames_per_buf;
+	u32 format_id;
+	/* <cfg_size> = sizeof(usm_cfg_common)+|transp_data| */
+	u32 cfg_size;
+	struct usm_cfg_common cfg_common;
+	/* Transparent configuration data for specific encoder */
+	u8  transp_data[USM_MAX_CFG_DATA_SIZE];
+} __packed;
+
+struct usm_stream_cmd_encdec_cfg_blk {
+	struct apr_hdr hdr;
+	u32 param_id;
+	u32 param_size;
+	struct usm_encode_cfg_blk enc_blk;
+} __packed;
+
+#endif /* __APR_US_H__ */
diff --git a/include/linux/qdsp6v2/audio_notifier.h b/include/linux/qdsp6v2/audio_notifier.h
new file mode 100644
index 0000000..3587b49
--- /dev/null
+++ b/include/linux/qdsp6v2/audio_notifier.h
@@ -0,0 +1,105 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __AUDIO_NOTIFIER_H_
+#define __AUDIO_NOTIFIER_H_
+
+/* State of the notifier domain */
+enum {
+	AUDIO_NOTIFIER_SERVICE_DOWN,
+	AUDIO_NOTIFIER_SERVICE_UP
+};
+
+/* Service order determines connection priority
+ * Highest number connected first
+ */
+enum {
+	AUDIO_NOTIFIER_SSR_SERVICE,
+	AUDIO_NOTIFIER_PDR_SERVICE,
+	AUDIO_NOTIFIER_MAX_SERVICES
+};
+
+enum {
+	AUDIO_NOTIFIER_ADSP_DOMAIN,
+	AUDIO_NOTIFIER_MODEM_DOMAIN,
+	AUDIO_NOTIFIER_MAX_DOMAINS
+};
+
+/* Structure populated in void *data of nb function
+ * callback used for audio_notifier_register
+ */
+struct audio_notifier_cb_data {
+	int service;
+	int domain;
+};
+
+#ifdef CONFIG_MSM_QDSP6_NOTIFIER
+
+/*
+ * Use audio_notifier_register to register any audio
+ * clients who need to be notified of a remote process.
+ * This API will determine and register the client with
+ * the best available subsystem (SSR or PDR) for that
+ * domain (Adsp or Modem). When an event is sent from that
+ * domain the notifier block callback function will be called.
+ *
+ * client_name - A unique user name defined by the client.
+ *	If the same name is used for multiple calls each will
+ *	be tracked & called back separately and a single call
+ *	to deregister will delete them all.
+ * domain - Domain the client wants to get events from.
+ *	AUDIO_NOTIFIER_ADSP_DOMAIN
+ *	AUDIO_NOTIFIER_MODEM_DOMAIN
+ * *nb - Pointer to a notifier block. Provide a callback function
+ *	to be notified of an even on that domain.
+ *
+ *      nb_func(struct notifier_block *this, unsigned long opcode, void *data)
+ *		this - pointer to own nb
+ *		opcode - event from registered domain
+ *			AUDIO_NOTIFIER_SERVICE_DOWN
+ *			AUDIO_NOTIFIER_SERVICE_UP
+ *		*data - pointer to struct audio_notifier_cb_data
+ *
+ * Returns:	Success: 0
+ *		Error: -#
+ */
+int audio_notifier_register(char *client_name, int domain,
+			    struct notifier_block *nb);
+
+/*
+ * Use audio_notifier_deregister to deregister the clients from
+ * all domains registered using audio_notifier_register that
+ * match the client name.
+ *
+ * client_name - Unique user name used in audio_notifier_register.
+ * Returns:	Success: 0
+ *		Error: -#
+ */
+int audio_notifier_deregister(char *client_name);
+
+#else
+
+static inline int audio_notifier_register(char *client_name, int domain,
+					  struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+
+static inline int audio_notifier_deregister(char *client_name)
+{
+	return 0;
+}
+
+#endif /* CONFIG_MSM_QDSP6_PDR */
+
+#endif
diff --git a/include/linux/qdsp6v2/audio_pdr.h b/include/linux/qdsp6v2/audio_pdr.h
new file mode 100644
index 0000000..ebfd366
--- /dev/null
+++ b/include/linux/qdsp6v2/audio_pdr.h
@@ -0,0 +1,101 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __AUDIO_PDR_H_
+#define __AUDIO_PDR_H_
+
+enum {
+	AUDIO_PDR_DOMAIN_ADSP,
+	AUDIO_PDR_DOMAIN_MAX
+};
+
+enum {
+	AUDIO_PDR_FRAMEWORK_DOWN,
+	AUDIO_PDR_FRAMEWORK_UP
+};
+
+#ifdef CONFIG_MSM_QDSP6_PDR
+
+/*
+ * Use audio_pdr_register to register with the PDR subsystem this
+ * should be done before module late init otherwise notification
+ * of the AUDIO_PDR_FRAMEWORK_UP cannot be guaranteed.
+ *
+ * *nb - Pointer to a notifier block. Provide a callback function
+ *       to be notified once the PDR framework has been initialized.
+ *       Callback will receive either the AUDIO_PDR_FRAMEWORK_DOWN
+ *       or AUDIO_PDR_FRAMEWORK_UP ioctl depending on the state of
+ *       the PDR framework.
+ *
+ * Returns: Success: 0
+ *          Failure: Error code
+ */
+int audio_pdr_register(struct notifier_block *nb);
+
+/*
+ * Use audio_pdr_service_register to register with a PDR service
+ * Function should be called after nb callback registered with
+ * audio_pdr_register has been called back with the
+ * AUDIO_PDR_FRAMEWORK_UP ioctl.
+ *
+ * domain_id - Domain to use, example: AUDIO_PDR_ADSP
+ * *nb - Pointer to a notifier block. Provide a callback function
+ *       that will be notified of the state of the domain
+ *       requested. The ioctls received by the callback are
+ *       defined in service-notifier.h.
+ *
+ * Returns: Success: Client handle
+ *          Failure: Pointer error code
+ */
+void *audio_pdr_service_register(int domain_id,
+				 struct notifier_block *nb, int *curr_state);
+
+/*
+ * Use audio_pdr_service_deregister to deregister with a PDR
+ * service that was registered using the audio_pdr_service_register
+ * API.
+ *
+ * *service_handle - Service handle returned by audio_pdr_service_register
+ * *nb - Pointer to the notifier block. Used in the call to
+ *       audio_pdr_service_register.
+ *
+ * Returns: Success: Client handle
+ *          Failure: Error code
+ */
+int audio_pdr_service_deregister(void *service_handle,
+				 struct notifier_block *nb);
+
+#else
+
+static inline int audio_pdr_register(struct notifier_block *nb)
+{
+	return -ENODEV;
+}
+
+
+static inline void *audio_pdr_service_register(int domain_id,
+					       struct notifier_block *nb,
+					       int *curr_state)
+{
+	return NULL;
+}
+
+static inline int audio_pdr_service_deregister(void *service_handle,
+					       struct notifier_block *nb)
+{
+	return 0;
+}
+
+#endif /* CONFIG_MSM_QDSP6_PDR */
+
+#endif
diff --git a/include/linux/qdsp6v2/audio_ssr.h b/include/linux/qdsp6v2/audio_ssr.h
new file mode 100644
index 0000000..a807021
--- /dev/null
+++ b/include/linux/qdsp6v2/audio_ssr.h
@@ -0,0 +1,78 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __AUDIO_SSR_H_
+#define __AUDIO_SSR_H_
+
+enum {
+	AUDIO_SSR_DOMAIN_ADSP,
+	AUDIO_SSR_DOMAIN_MODEM,
+	AUDIO_SSR_DOMAIN_MAX
+};
+
+#ifdef CONFIG_MSM_QDSP6_SSR
+
+/*
+ * Use audio_ssr_register to register with the SSR subsystem
+ *
+ * domain_id - Service to use, example: AUDIO_SSR_DOMAIN_ADSP
+ * *nb - Pointer to a notifier block. Provide a callback function
+ *       to be notified of an event for that service. The ioctls
+ *       used by the callback are defined in subsystem_notif.h.
+ *
+ * Returns: Success: Client handle
+ *          Failure: Pointer error code
+ */
+void *audio_ssr_register(int domain_id, struct notifier_block *nb);
+
+/*
+ * Use audio_ssr_deregister to register with the SSR subsystem
+ *
+ * handle - Handle received from audio_ssr_register
+ * *nb - Pointer to a notifier block. Callback function
+ *       Used from audio_ssr_register.
+ *
+ * Returns: Success: 0
+ *          Failure: Error code
+ */
+int audio_ssr_deregister(void *handle, struct notifier_block *nb);
+
+
+/*
+ * Use audio_ssr_send_nmi to force a RAM dump on ADSP
+ * down event.
+ *
+ * *ssr_cb_data - *data received from notifier callback
+ */
+void audio_ssr_send_nmi(void *ssr_cb_data);
+
+#else
+
+static inline void *audio_ssr_register(int domain_id,
+				       struct notifier_block *nb)
+{
+	return NULL;
+}
+
+static inline int audio_ssr_deregister(void *handle, struct notifier_block *nb)
+{
+	return 0;
+}
+
+static inline void audio_ssr_send_nmi(void *ssr_cb_data)
+{
+}
+
+#endif /* CONFIG_MSM_QDSP6_SSR */
+
+#endif
diff --git a/include/linux/qdsp6v2/dsp_debug.h b/include/linux/qdsp6v2/dsp_debug.h
new file mode 100644
index 0000000..bc1cd9e
--- /dev/null
+++ b/include/linux/qdsp6v2/dsp_debug.h
@@ -0,0 +1,22 @@
+/* Copyright (c) 2010, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef __DSP_DEBUG_H_
+#define __DSP_DEBUG_H_
+
+typedef int (*dsp_state_cb)(int state);
+int dsp_debug_register(dsp_state_cb ptr);
+
+#define DSP_STATE_CRASHED         0x0
+#define DSP_STATE_CRASH_DUMP_DONE 0x1
+
+#endif
diff --git a/include/linux/qdsp6v2/rtac.h b/include/linux/qdsp6v2/rtac.h
new file mode 100644
index 0000000..3e5433b
--- /dev/null
+++ b/include/linux/qdsp6v2/rtac.h
@@ -0,0 +1,98 @@
+/* Copyright (c) 2011, 2013-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __RTAC_H__
+#define __RTAC_H__
+
+#include <sound/apr_audio-v2.h>
+
+/* Voice Modes */
+#define RTAC_CVP		0
+#define RTAC_CVS		1
+#define RTAC_VOICE_MODES	2
+
+#define RTAC_MAX_ACTIVE_DEVICES		4
+#define RTAC_MAX_ACTIVE_POPP		8
+
+#define DEFAULT_APP_TYPE	0x00011130
+
+enum {
+	ADM_RTAC_CAL,
+	ASM_RTAC_CAL,
+	VOICE_RTAC_CAL,
+	AFE_RTAC_CAL,
+	MAX_RTAC_BLOCKS
+};
+
+struct rtac_cal_mem_map_data {
+	uint32_t		map_size;
+	uint32_t		map_handle;
+	struct ion_client	*ion_client;
+	struct ion_handle	*ion_handle;
+};
+
+struct rtac_cal_data {
+	size_t			size;
+	void			*kvaddr;
+	phys_addr_t		paddr;
+};
+
+struct rtac_cal_block_data {
+	struct rtac_cal_mem_map_data	map_data;
+	struct rtac_cal_data		cal_data;
+};
+
+struct rtac_popp_data {
+	uint32_t	popp;
+	uint32_t	popp_topology;
+	uint32_t	app_type;
+};
+
+struct rtac_adm_data {
+	uint32_t		topology_id;
+	uint32_t		afe_topology;
+	uint32_t		afe_port;
+	uint32_t		copp;
+	uint32_t		num_of_popp;
+	uint32_t		app_type;
+	uint32_t		acdb_dev_id;
+	struct rtac_popp_data	popp[RTAC_MAX_ACTIVE_POPP];
+};
+
+struct rtac_adm {
+	uint32_t			num_of_dev;
+	struct rtac_adm_data		device[RTAC_MAX_ACTIVE_DEVICES];
+};
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id,
+			u32 app_type, u32 acdb_dev_id);
+void rtac_remove_adm_device(u32 port_id, u32 copp_id);
+void rtac_remove_popp_from_adm_devices(u32 popp_id);
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+	u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id, u32 session_id);
+void rtac_remove_voice(u32 cvs_handle);
+void rtac_set_adm_handle(void *handle);
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size);
+void rtac_copy_adm_payload_to_user(void *payload, u32 payload_size);
+void rtac_set_asm_handle(u32 session_id, void *handle);
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+	u32 payload_size);
+void rtac_copy_asm_payload_to_user(void *payload, u32 payload_size);
+void rtac_set_voice_handle(u32 mode, void *handle);
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size);
+void rtac_copy_voice_payload_to_user(void *payload, u32 payload_size);
+int rtac_clear_mapping(uint32_t cal_type);
+bool rtac_make_afe_callback(uint32_t *payload, u32 payload_size);
+void rtac_set_afe_handle(void *handle);
+void get_rtac_adm_data(struct rtac_adm *adm_data);
+#endif
diff --git a/include/linux/qdsp6v2/usf.h b/include/linux/qdsp6v2/usf.h
new file mode 100644
index 0000000..544b624
--- /dev/null
+++ b/include/linux/qdsp6v2/usf.h
@@ -0,0 +1,298 @@
+/* Copyright (c) 2011-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __USF_H__
+#define __USF_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define USF_IOCTL_MAGIC 'U'
+
+#define US_SET_TX_INFO   _IOW(USF_IOCTL_MAGIC, 0, \
+				struct us_tx_info_type)
+#define US_START_TX      _IO(USF_IOCTL_MAGIC, 1)
+#define US_GET_TX_UPDATE _IOWR(USF_IOCTL_MAGIC, 2, \
+				struct us_tx_update_info_type)
+#define US_SET_RX_INFO   _IOW(USF_IOCTL_MAGIC, 3, \
+				struct us_rx_info_type)
+#define US_SET_RX_UPDATE _IOWR(USF_IOCTL_MAGIC, 4, \
+				struct us_rx_update_info_type)
+#define US_START_RX      _IO(USF_IOCTL_MAGIC, 5)
+
+#define US_STOP_TX      _IO(USF_IOCTL_MAGIC, 6)
+#define US_STOP_RX      _IO(USF_IOCTL_MAGIC, 7)
+
+#define US_SET_DETECTION _IOWR(USF_IOCTL_MAGIC, 8, \
+				struct us_detect_info_type)
+
+#define US_GET_VERSION  _IOWR(USF_IOCTL_MAGIC, 9, \
+				struct us_version_info_type)
+
+#define US_SET_TX_STREAM_PARAM   _IOW(USF_IOCTL_MAGIC, 10, \
+				struct us_stream_param_type)
+#define US_GET_TX_STREAM_PARAM  _IOWR(USF_IOCTL_MAGIC, 11, \
+				struct us_stream_param_type)
+#define US_SET_RX_STREAM_PARAM   _IOW(USF_IOCTL_MAGIC, 12, \
+				struct us_stream_param_type)
+#define US_GET_RX_STREAM_PARAM  _IOWR(USF_IOCTL_MAGIC, 13, \
+				struct us_stream_param_type)
+
+/* Special timeout values */
+#define USF_NO_WAIT_TIMEOUT	0x00000000
+/* Infinitive */
+#define USF_INFINITIVE_TIMEOUT	0xffffffff
+/* Default value, used by the driver */
+#define USF_DEFAULT_TIMEOUT	0xfffffffe
+
+/* US detection place (HW|FW) */
+enum us_detect_place_enum {
+/* US is detected in HW */
+	US_DETECT_HW,
+/* US is detected in FW */
+	US_DETECT_FW
+};
+
+/* US detection mode */
+enum us_detect_mode_enum {
+/* US detection is disabled */
+	US_DETECT_DISABLED_MODE,
+/* US detection is enabled in continue mode */
+	US_DETECT_CONTINUE_MODE,
+/* US detection is enabled in one shot mode */
+	US_DETECT_SHOT_MODE
+};
+
+/* Encoder (TX), decoder (RX) supported US data formats */
+#define USF_POINT_EPOS_FORMAT	0
+#define USF_RAW_FORMAT		1
+
+/* Indexes of event types, produced by the calculators */
+#define USF_TSC_EVENT_IND      0
+#define USF_TSC_PTR_EVENT_IND  1
+#define USF_MOUSE_EVENT_IND    2
+#define USF_KEYBOARD_EVENT_IND 3
+#define USF_TSC_EXT_EVENT_IND  4
+#define USF_MAX_EVENT_IND      5
+
+/* Types of events, produced by the calculators */
+#define USF_NO_EVENT 0
+#define USF_TSC_EVENT      (1 << USF_TSC_EVENT_IND)
+#define USF_TSC_PTR_EVENT  (1 << USF_TSC_PTR_EVENT_IND)
+#define USF_MOUSE_EVENT    (1 << USF_MOUSE_EVENT_IND)
+#define USF_KEYBOARD_EVENT (1 << USF_KEYBOARD_EVENT_IND)
+#define USF_TSC_EXT_EVENT  (1 << USF_TSC_EXT_EVENT_IND)
+#define USF_ALL_EVENTS         (USF_TSC_EVENT |\
+				USF_TSC_PTR_EVENT |\
+				USF_MOUSE_EVENT |\
+				USF_KEYBOARD_EVENT |\
+				USF_TSC_EXT_EVENT)
+
+/* min, max array dimension */
+#define MIN_MAX_DIM 2
+
+/* coordinates (x,y,z) array dimension */
+#define COORDINATES_DIM 3
+
+/* tilts (x,y) array dimension */
+#define TILTS_DIM 2
+
+/* Max size of the client name */
+#define USF_MAX_CLIENT_NAME_SIZE	20
+
+/* Max number of the ports (mics/speakers) */
+#define USF_MAX_PORT_NUM                8
+
+/* Info structure common for TX and RX */
+struct us_xx_info_type {
+/* Input:  general info */
+/* Name of the client - event calculator */
+	const char __user *client_name;
+/* Selected device identification, accepted in the kernel's CAD */
+	uint32_t dev_id;
+/* 0 - point_epos type; (e.g. 1 - gr_mmrd) */
+	uint32_t stream_format;
+/* Required sample rate in Hz */
+	uint32_t sample_rate;
+/* Size of a buffer (bytes) for US data transfer between the module and USF */
+	uint32_t buf_size;
+/* Number of the buffers for the US data transfer */
+	uint16_t buf_num;
+/* Number of the microphones (TX) or speakers(RX) */
+	uint16_t port_cnt;
+/* Microphones(TX) or speakers(RX) indexes in their enumeration */
+	uint8_t  port_id[USF_MAX_PORT_NUM];
+/* Bits per sample 16 or 32 */
+	uint16_t bits_per_sample;
+/* Input:  Transparent info for encoder in the LPASS */
+/* Parameters data size in bytes */
+	uint16_t params_data_size;
+/* Pointer to the parameters */
+	uint8_t __user *params_data;
+/* Max size of buffer for get and set parameter */
+	uint32_t max_get_set_param_buf_size;
+};
+
+struct us_input_info_type {
+	/* Touch screen dimensions: min & max;for input module */
+	int tsc_x_dim[MIN_MAX_DIM];
+	int tsc_y_dim[MIN_MAX_DIM];
+	int tsc_z_dim[MIN_MAX_DIM];
+	/* Touch screen tilt dimensions: min & max;for input module */
+	int tsc_x_tilt[MIN_MAX_DIM];
+	int tsc_y_tilt[MIN_MAX_DIM];
+	/* Touch screen pressure limits: min & max; for input module */
+	int tsc_pressure[MIN_MAX_DIM];
+	/* The requested buttons bitmap */
+	uint16_t req_buttons_bitmap;
+	/* Bitmap of types of events (USF_X_EVENT), produced by calculator */
+	uint16_t event_types;
+	/* Bitmap of types of events from devs, conflicting with USF */
+	uint16_t conflicting_event_types;
+};
+
+struct us_tx_info_type {
+	/* Common info */
+	struct us_xx_info_type us_xx_info;
+	/* Info specific for TX*/
+	struct us_input_info_type input_info;
+};
+
+struct us_rx_info_type {
+	/* Common info */
+	struct us_xx_info_type us_xx_info;
+	/* Info specific for RX*/
+};
+
+struct point_event_type {
+/* Pen coordinates (x, y, z) in units, defined by <coordinates_type>  */
+	int coordinates[COORDINATES_DIM];
+	/* {x;y}  in transparent units */
+	int inclinations[TILTS_DIM];
+/* [0-1023] (10bits); 0 - pen up */
+	uint32_t pressure;
+/* Bitmap for button state. 1 - down, 0 - up */
+	uint16_t buttons_state_bitmap;
+};
+
+/* Mouse buttons, supported by USF */
+#define USF_BUTTON_LEFT_MASK   1
+#define USF_BUTTON_MIDDLE_MASK 2
+#define USF_BUTTON_RIGHT_MASK  4
+struct mouse_event_type {
+/* The mouse relative movement (dX, dY, dZ) */
+	int rels[COORDINATES_DIM];
+/* Bitmap of mouse buttons states: 1 - down, 0 - up; */
+	uint16_t buttons_states;
+};
+
+struct key_event_type {
+/*  Calculated MS key- see input.h. */
+	uint32_t key;
+/* Keyboard's key state: 1 - down, 0 - up; */
+	uint8_t key_state;
+};
+
+struct usf_event_type {
+/* Event sequence number */
+	uint32_t seq_num;
+/* Event generation system time */
+	uint32_t timestamp;
+/* Destination input event type index (e.g. touch screen, mouse, key) */
+	uint16_t event_type_ind;
+	union {
+		struct point_event_type point_event;
+		struct mouse_event_type mouse_event;
+		struct key_event_type   key_event;
+	} event_data;
+};
+
+struct us_tx_update_info_type {
+/* Input  general: */
+/* Number of calculated events */
+	uint16_t event_counter;
+/* Calculated events or NULL */
+	struct usf_event_type __user *event;
+/* Pointer (read index) to the end of available region */
+/* in the shared US data memory */
+	uint32_t free_region;
+/* Time (sec) to wait for data or special values: */
+/* USF_NO_WAIT_TIMEOUT, USF_INFINITIVE_TIMEOUT, USF_DEFAULT_TIMEOUT */
+	uint32_t timeout;
+/* Events (from conflicting devs) to be disabled/enabled */
+	uint16_t event_filters;
+
+/* Input  transparent data: */
+/* Parameters size */
+	uint16_t params_data_size;
+/* Pointer to the parameters */
+	uint8_t __user *params_data;
+/* Output parameters: */
+/* Pointer (write index) to the end of ready US data region */
+/* in the shared memory */
+	uint32_t ready_region;
+};
+
+struct us_rx_update_info_type {
+/* Input  general: */
+/* Pointer (write index) to the end of ready US data region */
+/* in the shared memory */
+	uint32_t ready_region;
+/* Input  transparent data: */
+/* Parameters size */
+	uint16_t params_data_size;
+/* pPointer to the parameters */
+	uint8_t __user *params_data;
+/* Output parameters: */
+/* Pointer (read index) to the end of available region */
+/* in the shared US data memory */
+	uint32_t free_region;
+};
+
+struct us_detect_info_type {
+/* US detection place (HW|FW) */
+/* NA in the Active and OFF states */
+	enum us_detect_place_enum us_detector;
+/* US detection mode */
+	enum us_detect_mode_enum  us_detect_mode;
+/* US data dropped during this time (msec) */
+	uint32_t skip_time;
+/* Transparent data size */
+	uint16_t params_data_size;
+/* Pointer to the transparent data */
+	uint8_t __user *params_data;
+/* Time (sec) to wait for US presence event */
+	uint32_t detect_timeout;
+/* Out parameter: US presence */
+	bool is_us;
+};
+
+struct us_version_info_type {
+/* Size of memory for the version string */
+	uint16_t buf_size;
+/* Pointer to the memory for the version string */
+	char __user *pbuf;
+};
+
+struct us_stream_param_type {
+/* Id of module */
+	uint32_t module_id;
+/* Id of parameter */
+	uint32_t param_id;
+/* Size of memory of the parameter buffer */
+	uint32_t buf_size;
+/* Pointer to the memory of the parameter buffer */
+	uint8_t __user *pbuf;
+};
+
+#endif /* __USF_H__ */
diff --git a/include/linux/qpnp/pin.h b/include/linux/qpnp/pin.h
new file mode 100644
index 0000000..7fb57aa
--- /dev/null
+++ b/include/linux/qpnp/pin.h
@@ -0,0 +1,226 @@
+/* Copyright (c) 2012, 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+/* Mode select */
+#define QPNP_PIN_MODE_DIG_IN			0
+#define QPNP_PIN_MODE_DIG_OUT			1
+#define QPNP_PIN_MODE_DIG_IN_OUT		2
+#define QPNP_PIN_MODE_ANA_PASS_THRU		3
+#define QPNP_PIN_MODE_BIDIR			3
+#define QPNP_PIN_MODE_AIN			4
+#define QPNP_PIN_MODE_AOUT			5
+#define QPNP_PIN_MODE_SINK			6
+
+/* Invert source select (GPIO, MPP) */
+#define QPNP_PIN_INVERT_DISABLE			0
+#define QPNP_PIN_INVERT_ENABLE			1
+
+/* Output type (GPIO) */
+#define QPNP_PIN_OUT_BUF_CMOS			0
+#define QPNP_PIN_OUT_BUF_OPEN_DRAIN_NMOS	1
+#define QPNP_PIN_OUT_BUF_OPEN_DRAIN_PMOS	2
+#define QPNP_PIN_OUT_BUF_NO_DRIVE		3
+
+/* Voltage select (GPIO, MPP) */
+#define QPNP_PIN_VIN0				0
+#define QPNP_PIN_VIN1				1
+#define QPNP_PIN_VIN2				2
+#define QPNP_PIN_VIN3				3
+#define QPNP_PIN_VIN4				4
+#define QPNP_PIN_VIN5				5
+#define QPNP_PIN_VIN6				6
+#define QPNP_PIN_VIN7				7
+
+/* Pull Up Values (GPIO) */
+#define QPNP_PIN_GPIO_PULL_UP_30		0
+#define QPNP_PIN_GPIO_PULL_UP_1P5		1
+#define QPNP_PIN_GPIO_PULL_UP_31P5		2
+#define QPNP_PIN_GPIO_PULL_UP_1P5_30		3
+#define QPNP_PIN_GPIO_PULL_DN			4
+#define QPNP_PIN_GPIO_PULL_NO			5
+
+/* Pull Up Values (MPP) */
+#define QPNP_PIN_MPP_PULL_UP_0P6KOHM		0
+#define QPNP_PIN_MPP_PULL_UP_OPEN		1
+#define QPNP_PIN_MPP_PULL_UP_10KOHM		2
+#define QPNP_PIN_MPP_PULL_UP_30KOHM		3
+
+/* Out Strength (GPIO) */
+#define QPNP_PIN_OUT_STRENGTH_LOW		1
+#define QPNP_PIN_OUT_STRENGTH_MED		2
+#define QPNP_PIN_OUT_STRENGTH_HIGH		3
+
+/* Digital-in CTL (GPIO/MPP) */
+#define QPNP_PIN_DIG_IN_CTL_DTEST1		1
+#define QPNP_PIN_DIG_IN_CTL_DTEST2		2
+#define QPNP_PIN_DIG_IN_CTL_DTEST3		3
+#define QPNP_PIN_DIG_IN_CTL_DTEST4		4
+
+/* Source Select (GPIO) / Enable Select (MPP) */
+#define QPNP_PIN_SEL_FUNC_CONSTANT		0
+#define QPNP_PIN_SEL_FUNC_PAIRED		1
+#define QPNP_PIN_SEL_FUNC_1			2
+#define QPNP_PIN_SEL_FUNC_2			3
+#define QPNP_PIN_SEL_DTEST1			4
+#define QPNP_PIN_SEL_DTEST2			5
+#define QPNP_PIN_SEL_DTEST3			6
+#define QPNP_PIN_SEL_DTEST4			7
+
+/* Source Select for GPIO_LV/GPIO_MV only */
+#define QPNP_PIN_LV_MV_SEL_FUNC_CONSTANT	0
+#define QPNP_PIN_LV_MV_SEL_FUNC_PAIRED		1
+#define QPNP_PIN_LV_MV_SEL_FUNC_1		2
+#define QPNP_PIN_LV_MV_SEL_FUNC_2		3
+#define QPNP_PIN_LV_MV_SEL_FUNC_3		4
+#define QPNP_PIN_LV_MV_SEL_FUNC_4		5
+#define QPNP_PIN_LV_MV_SEL_DTEST1		6
+#define QPNP_PIN_LV_MV_SEL_DTEST2		7
+#define QPNP_PIN_LV_MV_SEL_DTEST3		8
+#define QPNP_PIN_LV_MV_SEL_DTEST4		9
+
+/* Master enable (GPIO, MPP) */
+#define QPNP_PIN_MASTER_DISABLE			0
+#define QPNP_PIN_MASTER_ENABLE			1
+
+/* Analog Output (MPP) */
+#define QPNP_PIN_AOUT_1V25			0
+#define QPNP_PIN_AOUT_0V625			1
+#define QPNP_PIN_AOUT_0V3125			2
+#define QPNP_PIN_AOUT_MPP			3
+#define QPNP_PIN_AOUT_ABUS1			4
+#define QPNP_PIN_AOUT_ABUS2			5
+#define QPNP_PIN_AOUT_ABUS3			6
+#define QPNP_PIN_AOUT_ABUS4			7
+
+/* Analog Input (MPP) */
+#define QPNP_PIN_AIN_AMUX_CH5			0
+#define QPNP_PIN_AIN_AMUX_CH6			1
+#define QPNP_PIN_AIN_AMUX_CH7			2
+#define QPNP_PIN_AIN_AMUX_CH8			3
+#define QPNP_PIN_AIN_AMUX_ABUS1			4
+#define QPNP_PIN_AIN_AMUX_ABUS2			5
+#define QPNP_PIN_AIN_AMUX_ABUS3			6
+#define QPNP_PIN_AIN_AMUX_ABUS4			7
+
+/* Current Sink (MPP) */
+#define QPNP_PIN_CS_OUT_5MA			0
+#define QPNP_PIN_CS_OUT_10MA			1
+#define QPNP_PIN_CS_OUT_15MA			2
+#define QPNP_PIN_CS_OUT_20MA			3
+#define QPNP_PIN_CS_OUT_25MA			4
+#define QPNP_PIN_CS_OUT_30MA			5
+#define QPNP_PIN_CS_OUT_35MA			6
+#define QPNP_PIN_CS_OUT_40MA			7
+
+/* ANALOG PASS SEL (GPIO LV/MV) */
+#define QPNP_PIN_APASS_SEL_ATEST1		0
+#define QPNP_PIN_APASS_SEL_ATEST2		1
+#define QPNP_PIN_APASS_SEL_ATEST3		2
+#define QPNP_PIN_APASS_SEL_ATEST4		3
+
+/**
+ * struct qpnp_pin_cfg - structure to specify pin configurtion values
+ * @mode:		indicates whether the pin should be input, output, or
+ *			both for gpios. mpp pins also support bidirectional,
+ *			analog in, analog out and current sink. This value
+ *			should be of type QPNP_PIN_MODE_*.
+ * @output_type:	indicates pin should be configured as CMOS or open
+ *			drain. Should be of the type QPNP_PIN_OUT_BUF_*. This
+ *			setting applies for gpios only.
+ * @invert:		Invert the signal of the line -
+ *			QPNP_PIN_INVERT_DISABLE or QPNP_PIN_INVERT_ENABLE.
+ * @pull:		This parameter should be programmed to different values
+ *			depending on whether it's GPIO or MPP.
+ *			For GPIO, it indicates whether a pull up or pull down
+ *			should be applied. If a pullup is required the
+ *			current strength needs to be specified.
+ *			Current values of 30uA, 1.5uA, 31.5uA, 1.5uA with 30uA
+ *			boost are supported. This value should be one of
+ *			the QPNP_PIN_GPIO_PULL_*. Note that the hardware ignores
+ *			this configuration if the GPIO is not set to input or
+ *			output open-drain mode.
+ *			For MPP, it indicates whether a pullup should be
+ *			applied for bidirectitional mode only. The hardware
+ *			ignores the configuration when operating in other modes.
+ *			This value should be one of the QPNP_PIN_MPP_PULL_*.
+ * @vin_sel:		specifies the voltage level when the output is set to 1.
+ *			For an input gpio specifies the voltage level at which
+ *			the input is interpreted as a logical 1.
+ * @out_strength:	the amount of current supplied for an output gpio,
+ *			should be of the type QPNP_PIN_STRENGTH_*.
+ * @src_sel:		select alternate function for the pin. Certain pins
+ *			can be paired (shorted) with each other. Some pins
+ *			can act as alternate functions. In the context of
+ *			gpio, this acts as a source select. For mpps,
+ *			this is an enable select.
+ *			This parameter should be of type QPNP_PIN_SEL_*.
+ * @master_en:		QPNP_PIN_MASTER_ENABLE = Enable features within the
+ *			pin block based on configurations.
+ *			QPNP_PIN_MASTER_DISABLE = Completely disable the pin
+ *			block and let the pin float with high impedance
+ *			regardless of other settings.
+ * @aout_ref:		Set the analog output reference. This parameter should
+ *			be of type QPNP_PIN_AOUT_*. This parameter only applies
+ *			to mpp pins.
+ * @ain_route:		Set the source for analog input. This parameter
+ *			should be of type QPNP_PIN_AIN_*. This parameter only
+ *			applies to mpp pins.
+ * @cs_out:		Set the the amount of current to sync in mA. This
+ *			parameter should be of type QPNP_PIN_CS_OUT_*. This
+ *			parameter only applies to mpp pins.
+ * @apass_sel:		Set the ATEST line to which the signal is to be
+ *			routed to. The parameter should be of type
+ *			QPNP_PIN_APASS_SEL_*. This
+ *			parameter only applies to GPIO LV/MV pins.
+ * @dtest_sel:		Select the DTEST line to which the signal needs
+ *			is routed to. The parameter should be of type
+ *			QPNP_PIN_DIG_IN_CTL_*. The parameter applies
+ *			to both gpio and mpp pins.
+ */
+struct qpnp_pin_cfg {
+	int mode;
+	int output_type;
+	int invert;
+	int pull;
+	int vin_sel;
+	int out_strength;
+	int src_sel;
+	int master_en;
+	int aout_ref;
+	int ain_route;
+	int cs_out;
+	int apass_sel;
+	int dtest_sel;
+};
+
+/**
+ * qpnp_pin_config - Apply pin configuration for Linux gpio
+ * @gpio: Linux gpio number to configure.
+ * @param: parameters to configure.
+ *
+ * This routine takes a Linux gpio number that corresponds with a
+ * PMIC pin and applies the configuration specified in 'param'.
+ * This gpio number can be ascertained by of_get_gpio_flags() or
+ * the qpnp_pin_map_gpio() API.
+ */
+int qpnp_pin_config(int gpio, struct qpnp_pin_cfg *param);
+
+/**
+ * qpnp_pin_map - Obtain Linux GPIO number from device spec
+ * @name: Name assigned by the 'label' binding for the primary node.
+ * @pmic_pin: PMIC pin number to lookup.
+ *
+ * This routine is used in legacy configurations that do not support
+ * Device Tree. If you are using Device Tree, you should not use this.
+ * For such cases, use of_get_gpio() or friends instead.
+ */
+int qpnp_pin_map(const char *name, uint32_t pmic_pin);
diff --git a/include/linux/qpnp/pwm.h b/include/linux/qpnp/pwm.h
new file mode 100644
index 0000000..020f18b
--- /dev/null
+++ b/include/linux/qpnp/pwm.h
@@ -0,0 +1,217 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QPNP_PWM_H__
+#define __QPNP_PWM_H__
+
+#include <linux/pwm.h>
+
+/* usec: 19.2M, n=6, m=0, pre=2 */
+#define PM_PWM_PERIOD_MIN			7
+/* 1K, n=9, m=7, pre=6 */
+#define PM_PWM_PERIOD_MAX			(384 * USEC_PER_SEC)
+#define PM_PWM_LUT_RAMP_STEP_TIME_MAX		499
+#define PM_PWM_MAX_PAUSE_CNT			8191
+/*
+ * Formula from HSID,
+ * pause_time (hi/lo) = (pause_code - 1)*(duty_ms)
+ */
+#define PM_PWM_LUT_PAUSE_MAX \
+	((PM_PWM_MAX_PAUSE_CNT - 1) * PM_PWM_LUT_RAMP_STEP_TIME_MAX) /* ms */
+
+/* Flags for Look Up Table */
+#define PM_PWM_LUT_LOOP			0x01
+#define PM_PWM_LUT_RAMP_UP		0x02
+#define PM_PWM_LUT_REVERSE		0x04
+#define PM_PWM_LUT_PAUSE_HI_EN		0x08
+#define PM_PWM_LUT_PAUSE_LO_EN		0x10
+
+#define PM_PWM_LUT_NO_TABLE		0x20
+#define PM_PWM_LUT_USE_RAW_VALUE	0x40
+
+/*
+ * PWM frequency/period control
+ *
+ * PWM Frequency = ClockFrequency / (N * T)
+ *   or
+ * PWM Period = Clock Period * (N * T)
+ *   where
+ * N = 2^9 or 2^6 for 9-bit or 6-bit PWM size
+ * T = Pre-divide * 2^m, m = 0..7 (exponent)
+ */
+
+/*
+ * enum pm_pwm_size - PWM bit mode selection
+ * %PM_PWM_SIZE_6BIT - Select 6 bit mode; 64 levels
+ * %PM_PWM_SIZE_9BIT - Select 9 bit mode; 512 levels
+ */
+enum pm_pwm_size {
+	PM_PWM_SIZE_6BIT =	6,
+	PM_PWM_SIZE_9BIT =	9,
+};
+
+/*
+ * enum pm_pwm_clk - PWM clock selection
+ * %PM_PWM_CLK_1KHZ - 1KHz clock
+ * %PM_PWM_CLK_32KHZ - 32KHz clock
+ * %PM_PWM_CLK_19P2MHZ - 19.2MHz clock
+ * Note: Here 1KHz = 1024Hz
+ */
+enum pm_pwm_clk {
+	PM_PWM_CLK_1KHZ,
+	PM_PWM_CLK_32KHZ,
+	PM_PWM_CLK_19P2MHZ,
+};
+
+/* PWM pre-divider selection */
+enum pm_pwm_pre_div {
+	PM_PWM_PDIV_2,
+	PM_PWM_PDIV_3,
+	PM_PWM_PDIV_5,
+	PM_PWM_PDIV_6,
+};
+
+/*
+ * struct pwm_period_config - PWM period configuration
+ * @pwm_size: enum pm_pwm_size
+ * @clk: enum pm_pwm_clk
+ * @pre_div: enum pm_pwm_pre_div
+ * @pre_div_exp: exponent of 2 as part of pre-divider: 0..7
+ */
+struct pwm_period_config {
+	enum pm_pwm_size	pwm_size;
+	enum pm_pwm_clk		clk;
+	enum pm_pwm_pre_div	pre_div;
+	int			pre_div_exp;
+};
+
+/*
+ * struct pwm_duty_cycles - PWM duty cycle info
+ * duty_pcts - pointer to an array of duty percentage for a pwm period
+ * num_duty_pcts - total entries in duty_pcts array
+ * duty_ms - duty cycle time in ms
+ * start_idx - index in the LUT
+ */
+struct pwm_duty_cycles {
+	int *duty_pcts;
+	int num_duty_pcts;
+	int duty_ms;
+	int start_idx;
+};
+
+/*
+ * enum pm_pwm_mode - PWM mode selection
+ * %PM_PWM_MODE_PWM - Select PWM mode
+ * %PM_PWM_MODE_LPG - Select LPG mode
+ */
+enum pm_pwm_mode {
+	PM_PWM_MODE_PWM,
+	PM_PWM_MODE_LPG,
+};
+
+/*
+ * lut_params: Lookup table (LUT) parameters
+ * @start_idx: start index in lookup table from 0 to MAX-1
+ * @idx_len: number of index
+ * @pause_lo: pause time in millisecond at low index
+ * @pause_hi: pause time in millisecond at high index
+ * @ramp_step_ms: time before loading next LUT pattern in millisecond
+ * @flags: control flags
+ */
+struct lut_params {
+	int start_idx;
+	int idx_len;
+	int lut_pause_hi;
+	int lut_pause_lo;
+	int ramp_step_ms;
+	int flags;
+};
+
+#if IS_ENABLED(CONFIG_PWM_QPNP)
+int pwm_config_period(struct pwm_device *pwm,
+			     struct pwm_period_config *pwm_p);
+
+int pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value);
+
+
+int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode);
+
+
+int pwm_lut_config(struct pwm_device *pwm, int period_us,
+		int duty_pct[], struct lut_params lut_params);
+
+/*
+ * support microsecond level configuration
+ */
+int pwm_config_us(struct pwm_device *pwm,
+		int duty_us, int period_us);
+
+#else
+static inline int pwm_config_period(struct pwm_device *pwm,
+			     struct pwm_period_config *pwm_p)
+{
+	return -EINVAL;
+}
+
+static inline int pwm_config_pwm_value(struct pwm_device *pwm, int pwm_value)
+{
+	return -EINVAL;
+}
+
+static inline int pwm_change_mode(struct pwm_device *pwm, enum pm_pwm_mode mode)
+{
+	return -EINVAL;
+}
+
+static inline int pwm_lut_config(struct pwm_device *pwm, int period_us,
+		int duty_pct[], struct lut_params lut_params)
+{
+	return -EINVAL;
+}
+
+static inline int pwm_config_us(struct pwm_device *pwm,
+		int duty_us, int period_us)
+{
+	return -EINVAL;
+}
+#endif
+
+/* Standard APIs supported */
+/*
+ * pwm_request - request a PWM device
+ * @pwm_id: PWM id or channel
+ * @label: the label to identify the user
+ */
+
+/*
+ * pwm_free - free a PWM device
+ * @pwm: the PWM device
+ */
+
+/*
+ * pwm_config - change a PWM device configuration
+ * @pwm: the PWM device
+ * @period_ns: period in nanosecond
+ * @duty_ns: duty cycle in nanosecond
+ */
+
+/*
+ * pwm_enable - start a PWM output toggling
+ * @pwm: the PWM device
+ */
+
+/*
+ * pwm_disable - stop a PWM output toggling
+ * @pwm: the PWM device
+ */
+
+#endif /* __QPNP_PWM_H__ */
diff --git a/include/linux/qpnp/qpnp-adc.h b/include/linux/qpnp/qpnp-adc.h
new file mode 100644
index 0000000..1c13cd2
--- /dev/null
+++ b/include/linux/qpnp/qpnp-adc.h
@@ -0,0 +1,2290 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+/*
+ * Qualcomm Technologies Inc. PMIC QPNP ADC driver header file
+ *
+ */
+
+#ifndef __QPNP_ADC_H
+#define __QPNP_ADC_H
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/qpnp/qpnp-revid.h>
+#include <linux/regulator/consumer.h>
+/**
+ * enum qpnp_vadc_channels - QPNP AMUX arbiter channels
+ */
+enum qpnp_vadc_channels {
+	USBIN = 0,
+	DCIN,
+	VCHG_SNS,
+	SPARE1_03,
+	USB_ID_MV,
+	VCOIN,
+	VBAT_SNS,
+	VSYS,
+	DIE_TEMP,
+	REF_625MV,
+	REF_125V,
+	CHG_TEMP,
+	SPARE1,
+	SPARE2,
+	GND_REF,
+	VDD_VADC,
+	P_MUX1_1_1,
+	P_MUX2_1_1,
+	P_MUX3_1_1,
+	P_MUX4_1_1,
+	P_MUX5_1_1,
+	P_MUX6_1_1,
+	P_MUX7_1_1,
+	P_MUX8_1_1,
+	P_MUX9_1_1,
+	P_MUX10_1_1,
+	P_MUX11_1_1,
+	P_MUX12_1_1,
+	P_MUX13_1_1,
+	P_MUX14_1_1,
+	P_MUX15_1_1,
+	P_MUX16_1_1,
+	P_MUX1_1_3,
+	P_MUX2_1_3,
+	P_MUX3_1_3,
+	P_MUX4_1_3,
+	P_MUX5_1_3,
+	P_MUX6_1_3,
+	P_MUX7_1_3,
+	P_MUX8_1_3,
+	P_MUX9_1_3,
+	P_MUX10_1_3,
+	P_MUX11_1_3,
+	P_MUX12_1_3,
+	P_MUX13_1_3,
+	P_MUX14_1_3,
+	P_MUX15_1_3,
+	P_MUX16_1_3,
+	LR_MUX1_BATT_THERM,
+	LR_MUX2_BAT_ID,
+	LR_MUX3_XO_THERM,
+	LR_MUX4_AMUX_THM1,
+	LR_MUX5_AMUX_THM2,
+	LR_MUX6_AMUX_THM3,
+	LR_MUX7_HW_ID,
+	LR_MUX8_AMUX_THM4,
+	LR_MUX9_AMUX_THM5,
+	LR_MUX10_USB_ID_LV,
+	AMUX_PU1,
+	AMUX_PU2,
+	LR_MUX3_BUF_XO_THERM_BUF,
+	LR_MUX1_PU1_BAT_THERM = 112,
+	LR_MUX2_PU1_BAT_ID = 113,
+	LR_MUX3_PU1_XO_THERM = 114,
+	LR_MUX4_PU1_AMUX_THM1 = 115,
+	LR_MUX5_PU1_AMUX_THM2 = 116,
+	LR_MUX6_PU1_AMUX_THM3 = 117,
+	LR_MUX7_PU1_AMUX_HW_ID = 118,
+	LR_MUX8_PU1_AMUX_THM4 = 119,
+	LR_MUX9_PU1_AMUX_THM5 = 120,
+	LR_MUX10_PU1_AMUX_USB_ID_LV = 121,
+	LR_MUX3_BUF_PU1_XO_THERM_BUF = 124,
+	LR_MUX1_PU2_BAT_THERM = 176,
+	LR_MUX2_PU2_BAT_ID = 177,
+	LR_MUX3_PU2_XO_THERM = 178,
+	LR_MUX4_PU2_AMUX_THM1 = 179,
+	LR_MUX5_PU2_AMUX_THM2 = 180,
+	LR_MUX6_PU2_AMUX_THM3 = 181,
+	LR_MUX7_PU2_AMUX_HW_ID = 182,
+	LR_MUX8_PU2_AMUX_THM4 = 183,
+	LR_MUX9_PU2_AMUX_THM5 = 184,
+	LR_MUX10_PU2_AMUX_USB_ID_LV = 185,
+	LR_MUX3_BUF_PU2_XO_THERM_BUF = 188,
+	LR_MUX1_PU1_PU2_BAT_THERM = 240,
+	LR_MUX2_PU1_PU2_BAT_ID = 241,
+	LR_MUX3_PU1_PU2_XO_THERM = 242,
+	LR_MUX4_PU1_PU2_AMUX_THM1 = 243,
+	LR_MUX5_PU1_PU2_AMUX_THM2 = 244,
+	LR_MUX6_PU1_PU2_AMUX_THM3 = 245,
+	LR_MUX7_PU1_PU2_AMUX_HW_ID = 246,
+	LR_MUX8_PU1_PU2_AMUX_THM4 = 247,
+	LR_MUX9_PU1_PU2_AMUX_THM5 = 248,
+	LR_MUX10_PU1_PU2_AMUX_USB_ID_LV = 249,
+	LR_MUX3_BUF_PU1_PU2_XO_THERM_BUF = 252,
+	ALL_OFF = 255,
+	ADC_MAX_NUM = 0xffff,
+
+	/* Channel listing for refreshed VADC in hex format */
+	VADC_VREF_GND = 0,
+	VADC_CALIB_VREF_1P25 = 1,
+	VADC_CALIB_VREF = 2,
+	VADC_CALIB_VREF_1_DIV_3 = 0x82,
+	VADC_VPH_PWR = 0x83,
+	VADC_VBAT_SNS = 0x84,
+	VADC_VCOIN = 0x85,
+	VADC_DIE_TEMP = 6,
+	VADC_CHG_TEMP = 7,
+	VADC_USB_IN = 8,
+	VADC_IREG_FB = 9,
+	/* External input connection */
+	VADC_BAT_THERM = 0xa,
+	VADC_BAT_ID = 0xb,
+	VADC_XO_THERM = 0xc,
+	VADC_AMUX_THM1 = 0xd,
+	VADC_AMUX_THM2 = 0xe,
+	VADC_AMUX_THM3 = 0xf,
+	VADC_AMUX_THM4 = 0x10,
+	VADC_AMUX_THM5 = 0x11,
+	VADC_AMUX1_GPIO = 0x12,
+	VADC_AMUX2_GPIO = 0x13,
+	VADC_AMUX3_GPIO = 0x14,
+	VADC_AMUX4_GPIO = 0x15,
+	VADC_AMUX5_GPIO = 0x16,
+	VADC_AMUX6_GPIO = 0x17,
+	VADC_AMUX7_GPIO = 0x18,
+	VADC_AMUX8_GPIO = 0x19,
+	VADC_ATEST1 = 0x1a,
+	VADC_ATEST2 = 0x1b,
+	VADC_ATEST3 = 0x1c,
+	VADC_ATEST4 = 0x1d,
+	VADC_OFF = 0xff,
+	/* PU1 is 30K pull up */
+	VADC_BAT_THERM_PU1 = 0x2a,
+	VADC_BAT_ID_PU1 = 0x2b,
+	VADC_XO_THERM_PU1 = 0x2c,
+	VADC_AMUX_THM1_PU1 = 0x2d,
+	VADC_AMUX_THM2_PU1 = 0x2e,
+	VADC_AMUX_THM3_PU1 = 0x2f,
+	VADC_AMUX_THM4_PU1 = 0x30,
+	VADC_AMUX_THM5_PU1 = 0x31,
+	VADC_AMUX1_GPIO_PU1 = 0x32,
+	VADC_AMUX2_GPIO_PU1 = 0x33,
+	VADC_AMUX3_GPIO_PU1 = 0x34,
+	VADC_AMUX4_GPIO_PU1 = 0x35,
+	VADC_AMUX5_GPIO_PU1 = 0x36,
+	VADC_AMUX6_GPIO_PU1 = 0x37,
+	VADC_AMUX7_GPIO_PU1 = 0x38,
+	VADC_AMUX8_GPIO_PU1 = 0x39,
+	/* PU2 is 100K pull up */
+	VADC_BAT_THERM_PU2 = 0x4a,
+	VADC_BAT_ID_PU2 = 0x4b,
+	VADC_XO_THERM_PU2 = 0x4c,
+	VADC_AMUX_THM1_PU2 = 0x4d,
+	VADC_AMUX_THM2_PU2 = 0x4e,
+	VADC_AMUX_THM3_PU2 = 0x4f,
+	VADC_AMUX_THM4_PU2 = 0x50,
+	VADC_AMUX_THM5_PU2 = 0x51,
+	VADC_AMUX1_GPIO_PU2 = 0x52,
+	VADC_AMUX2_GPIO_PU2 = 0x53,
+	VADC_AMUX3_GPIO_PU2 = 0x54,
+	VADC_AMUX4_GPIO_PU2 = 0x55,
+	VADC_AMUX5_GPIO_PU2 = 0x56,
+	VADC_AMUX6_GPIO_PU2 = 0x57,
+	VADC_AMUX7_GPIO_PU2 = 0x58,
+	VADC_AMUX8_GPIO_PU2 = 0x59,
+	/* PU3 is 400K pull up */
+	VADC_BAT_THERM_PU3 = 0x6a,
+	VADC_BAT_ID_PU3 = 0x6b,
+	VADC_XO_THERM_PU3 = 0x6c,
+	VADC_AMUX_THM1_PU3 = 0x6d,
+	VADC_AMUX_THM2_PU3 = 0x6e,
+	VADC_AMUX_THM3_PU3 = 0x6f,
+	VADC_AMUX_THM4_PU3 = 0x70,
+	VADC_AMUX_THM5_PU3 = 0x71,
+	VADC_AMUX1_GPIO_PU3 = 0x72,
+	VADC_AMUX2_GPIO_PU3 = 0x73,
+	VADC_AMUX3_GPIO_PU3 = 0x74,
+	VADC_AMUX4_GPIO_PU3 = 0x75,
+	VADC_AMUX5_GPIO_PU3 = 0x76,
+	VADC_AMUX6_GPIO_PU3 = 0x77,
+	VADC_AMUX7_GPIO_PU3 = 0x78,
+	VADC_AMUX8_GPIO_PU3 = 0x79,
+	/* External input connection with 1/3 div */
+	VADC_AMUX1_GPIO_DIV_3 = 0x92,
+	VADC_AMUX2_GPIO_DIV_3 = 0x93,
+	VADC_AMUX3_GPIO_DIV_3 = 0x94,
+	VADC_AMUX4_GPIO_DIV_3 = 0x95,
+	VADC_AMUX5_GPIO_DIV_3 = 0x96,
+	VADC_AMUX6_GPIO_DIV_3 = 0x97,
+	VADC_AMUX7_GPIO_DIV_3 = 0x98,
+	VADC_AMUX8_GPIO_DIV_3 = 0x99,
+	VADC_ATEST1_DIV_3 = 0x9a,
+	VADC_ATEST2_DIV_3 = 0x9b,
+	VADC_ATEST3_DIV_3 = 0x9c,
+	VADC_ATEST4_DIV_3 = 0x9d,
+	VADC_REFRESH_MAX_NUM = 0xffff,
+};
+
+/**
+ * enum qpnp_iadc_channels - QPNP IADC channel list
+ */
+enum qpnp_iadc_channels {
+	INTERNAL_RSENSE = 0,
+	EXTERNAL_RSENSE,
+	ALT_LEAD_PAIR,
+	GAIN_CALIBRATION_17P857MV,
+	OFFSET_CALIBRATION_SHORT_CADC_LEADS,
+	OFFSET_CALIBRATION_CSP_CSN,
+	OFFSET_CALIBRATION_CSP2_CSN2,
+	IADC_MUX_NUM,
+};
+
+#define QPNP_ADC_625_UV	625000
+#define QPNP_ADC_HWMON_NAME_LENGTH				64
+#define QPNP_MAX_PROP_NAME_LEN					32
+#define QPNP_THERMALNODE_NAME_LENGTH                            25
+#define QPNP_ADC_1P25_UV					1250000
+
+/* Structure device for qpnp vadc */
+struct qpnp_vadc_chip;
+
+/* Structure device for qpnp iadc */
+struct qpnp_iadc_chip;
+
+/* Structure device for qpnp adc tm */
+struct qpnp_adc_tm_chip;
+
+/**
+ * enum qpnp_adc_clk_type - Clock rate supported.
+ * %CLK_TYPE1: 2P4MHZ
+ * %CLK_TYPE2: 4P8MHZ
+ * %CLK_TYPE3: 9P6MHZ
+ * %CLK_TYPE4: 19P2MHZ
+ * %CLK_NONE: Do not use this Clk type.
+ *
+ * The Clock rate is specific to each channel of the QPNP ADC arbiter.
+ */
+enum qpnp_adc_clk_type {
+	CLK_TYPE1 = 0,
+	CLK_TYPE2,
+	CLK_TYPE3,
+	CLK_TYPE4,
+	CLK_NONE,
+};
+
+/**
+ * enum qpnp_adc_decimation_type - Sampling rate supported.
+ * %DECIMATION_TYPE1: 512
+ * %DECIMATION_TYPE2: 1K
+ * %DECIMATION_TYPE3: 2K
+ * %DECIMATION_TYPE4: 4k
+ * %DECIMATION_NONE: Do not use this Sampling type.
+ *
+ * The Sampling rate is specific to each channel of the QPNP ADC arbiter.
+ */
+enum qpnp_adc_decimation_type {
+	DECIMATION_TYPE1 = 0,
+	DECIMATION_TYPE2,
+	DECIMATION_TYPE3,
+	DECIMATION_TYPE4,
+	DECIMATION_NONE = 0xff,
+
+	ADC_HC_DEC_RATIO_256 = 0,
+	ADC_HC_DEC_RATIO_512 = 1,
+	ADC_HC_DEC_RATIO_1024 = 2,
+	ADC_HC_DEC_RATIO_NONE = 0xff,
+};
+
+/**
+ * enum qpnp_adc_calib_type - QPNP ADC Calibration type.
+ * %ADC_CALIB_ABSOLUTE: Use 625mV and 1.25V reference channels.
+ * %ADC_CALIB_RATIOMETRIC: Use reference Voltage/GND.
+ * %ADC_CALIB_CONFIG_NONE: Do not use this calibration type.
+ *
+ * enum qpnp_adc_cal_sel - Selects the calibration type that is applied
+ *			   on the corresponding channel measurement after
+ *			   the ADC data is read.
+ * %ADC_HC_NO_CAL :	To obtain raw, uncalibrated data on qpnp-vadc-hc type.
+ * %ADC_HC_RATIO_CAL :	Applies ratiometric calibration. Note the calibration
+ *			values stored in the CAL peripheral for VADC_VREF and
+ *			VREF_1P25 already have GND_REF value removed. Used
+ *			only with qpnp-vadc-hc type of VADC.
+ * %ADC_HC_ABS_CAL :	Applies absolute calibration. Note the calibration
+ *			values stored in the CAL peripheral for VADC_VREF and
+ *			VREF_1P25 already have GND_REF value removed. Used
+ *			only with qpnp-vadc-hc type of VADC.
+ *
+ * Use the input reference voltage depending on the calibration type
+ * to calcluate the offset and gain parameters. The calibration is
+ * specific to each channel of the QPNP ADC.
+ */
+enum qpnp_adc_calib_type {
+	CALIB_ABSOLUTE = 0,
+	CALIB_RATIOMETRIC,
+	CALIB_NONE,
+
+	ADC_HC_NO_CAL = 0,
+	ADC_HC_RATIO_CAL = 1,
+	ADC_HC_ABS_CAL = 2,
+	ADC_HC_CAL_SEL_NONE,
+};
+
+/**
+ * enum qpnp_adc_channel_scaling_param - pre-scaling AMUX ratio.
+ * %CHAN_PATH_SCALING0: ratio of {1, 1}
+ * %CHAN_PATH_SCALING1: ratio of {1, 3}
+ * %CHAN_PATH_SCALING2: ratio of {1, 4}
+ * %CHAN_PATH_SCALING3: ratio of {1, 6}
+ * %CHAN_PATH_SCALING4: ratio of {1, 20}
+ * %CHAN_PATH_SCALING5: ratio of {1, 8}
+ * %CHAN_PATH_SCALING6: ratio of {10, 81} The actual ratio is (1/8.1).
+ * %CHAN_PATH_SCALING7: ratio of {1, 10}
+ * %CHAN_PATH_NONE: Do not use this pre-scaling ratio type.
+ *
+ * The pre-scaling is applied for signals to be within the voltage range
+ * of the ADC.
+ */
+enum qpnp_adc_channel_scaling_param {
+	PATH_SCALING0 = 0,
+	PATH_SCALING1,
+	PATH_SCALING2,
+	PATH_SCALING3,
+	PATH_SCALING4,
+	PATH_SCALING5,
+	PATH_SCALING6,
+	PATH_SCALING7,
+	PATH_SCALING_NONE,
+};
+
+/**
+ * enum qpnp_adc_scale_fn_type - Scaling function for pm8941 pre calibrated
+ *				   digital data relative to ADC reference.
+ * %SCALE_DEFAULT: Default scaling to convert raw adc code to voltage (uV).
+ * %SCALE_BATT_THERM: Conversion to temperature(decidegC) based on btm
+ *			parameters.
+ * %SCALE_THERM_100K_PULLUP: Returns temperature in degC.
+ *				 Uses a mapping table with 100K pullup.
+ * %SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * %SCALE_XOTHERM: Returns XO thermistor voltage in degree's Centigrade.
+ * %SCALE_THERM_150K_PULLUP: Returns temperature in degC.
+ *				 Uses a mapping table with 150K pullup.
+ * %SCALE_QRD_BATT_THERM: Conversion to temperature(decidegC) based on
+ *			btm parameters.
+ * %SCALE_QRD_SKUAA_BATT_THERM: Conversion to temperature(decidegC) based on
+ *          btm parameters for SKUAA.
+ * %SCALE_SMB_BATT_THERM: Conversion to temperature(decidegC) based on
+ *          btm parameters for SMB.
+ * %SCALE_QRD_SKUG_BATT_THERM: Conversion to temperature(decidegC) based on
+ *          btm parameters for SKUG.
+ * %SCALE_QRD_SKUH_BATT_THERM: Conversion to temperature(decidegC) based on
+ *          btm parameters for SKUH
+ * %SCALE_QRD_SKUT1_BATT_THERM: Conversion to temperature(decidegC) based on
+ *          btm parameters for SKUT1
+ * %SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
+ * %SCALE_NONE: Do not use this scaling type.
+ */
+enum qpnp_adc_scale_fn_type {
+	SCALE_DEFAULT = 0,
+	SCALE_BATT_THERM,
+	SCALE_THERM_100K_PULLUP,
+	SCALE_PMIC_THERM,
+	SCALE_XOTHERM,
+	SCALE_THERM_150K_PULLUP,
+	SCALE_QRD_BATT_THERM,
+	SCALE_QRD_SKUAA_BATT_THERM,
+	SCALE_SMB_BATT_THERM,
+	SCALE_QRD_SKUG_BATT_THERM,
+	SCALE_QRD_SKUH_BATT_THERM,
+	SCALE_NCP_03WF683_THERM,
+	SCALE_QRD_SKUT1_BATT_THERM,
+	SCALE_PMI_CHG_TEMP = 16,
+	SCALE_NONE,
+};
+
+/**
+ * enum qpnp_adc_tm_rscale_fn_type - Scaling function used to convert the
+ *	channels input voltage/temperature to corresponding ADC code that is
+ *	applied for thresholds. Check the corresponding channels scaling to
+ *	determine the appropriate temperature/voltage units that are passed
+ *	to the scaling function. Example battery follows the power supply
+ *	framework that needs its units to be in decidegreesC so it passes
+ *	deci-degreesC. PA_THERM clients pass the temperature in degrees.
+ *	The order below should match the one in the driver for
+ *	adc_tm_rscale_fn[].
+ */
+enum qpnp_adc_tm_rscale_fn_type {
+	SCALE_R_VBATT = 0,
+	SCALE_RBATT_THERM,
+	SCALE_R_USB_ID,
+	SCALE_RPMIC_THERM,
+	SCALE_R_SMB_BATT_THERM,
+	SCALE_R_ABSOLUTE,
+	SCALE_QRD_SKUH_RBATT_THERM,
+	SCALE_QRD_SKUT1_RBATT_THERM,
+	SCALE_RSCALE_NONE,
+};
+
+/**
+ * enum qpnp_vadc_rscale_fn_type - Scaling function used to convert the
+ *	channels input voltage/temperature to corresponding ADC code that is
+ *	applied for thresholds. Check the corresponding channels scaling to
+ *	determine the appropriate temperature/voltage units that are passed
+ *	to the scaling function. The order below should match the one in the
+ *	driver for qpnp_adc_scale_fn[].
+ */
+enum qpnp_vadc_rscale_fn_type {
+	SCALE_RVADC_ABSOLUTE = 0,
+	SCALE_RVADC_SCALE_NONE,
+};
+
+/**
+ * enum qpnp_adc_fast_avg_ctl - Provides ability to obtain single result
+ *		from the ADC that is an average of multiple measurement
+ *		samples. Select number of samples for use in fast
+ *		average mode (i.e. 2 ^ value).
+ * %ADC_FAST_AVG_SAMPLE_1:   0x0 = 1
+ * %ADC_FAST_AVG_SAMPLE_2:   0x1 = 2
+ * %ADC_FAST_AVG_SAMPLE_4:   0x2 = 4
+ * %ADC_FAST_AVG_SAMPLE_8:   0x3 = 8
+ * %ADC_FAST_AVG_SAMPLE_16:  0x4 = 16
+ * %ADC_FAST_AVG_SAMPLE_32:  0x5 = 32
+ * %ADC_FAST_AVG_SAMPLE_64:  0x6 = 64
+ * %ADC_FAST_AVG_SAMPLE_128: 0x7 = 128
+ * %ADC_FAST_AVG_SAMPLE_256: 0x8 = 256
+ * %ADC_FAST_AVG_SAMPLE_512: 0x9 = 512
+ */
+enum qpnp_adc_fast_avg_ctl {
+	ADC_FAST_AVG_SAMPLE_1 = 0,
+	ADC_FAST_AVG_SAMPLE_2,
+	ADC_FAST_AVG_SAMPLE_4,
+	ADC_FAST_AVG_SAMPLE_8,
+	ADC_FAST_AVG_SAMPLE_16,
+	ADC_FAST_AVG_SAMPLE_32,
+	ADC_FAST_AVG_SAMPLE_64,
+	ADC_FAST_AVG_SAMPLE_128,
+	ADC_FAST_AVG_SAMPLE_256,
+	ADC_FAST_AVG_SAMPLE_512,
+	ADC_FAST_AVG_SAMPLE_NONE,
+};
+
+/**
+ * enum qpnp_adc_hw_settle_time - Time between AMUX getting configured and
+ *		the ADC starting conversion. Delay = 100us * value for
+ *		value < 11 and 2ms * (value - 10) otherwise.
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_0US:   0us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_100US: 100us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_200US: 200us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_300US: 300us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_400US: 400us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_500US: 500us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_600US: 600us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_700US: 700us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_800US: 800us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_900US: 900us
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_1MS:   1ms
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_2MS:   2ms
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_4MS:   4ms
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_6MS:   6ms
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_8MS:   8ms
+ * %ADC_CHANNEL_HW_SETTLE_DELAY_10MS:  10ms
+ * %ADC_CHANNEL_HW_SETTLE_NONE
+ */
+enum qpnp_adc_hw_settle_time {
+	ADC_CHANNEL_HW_SETTLE_DELAY_0US = 0,
+	ADC_CHANNEL_HW_SETTLE_DELAY_100US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_2000US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_300US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_400US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_500US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_600US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_700US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_800US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_900US,
+	ADC_CHANNEL_HW_SETTLE_DELAY_1MS,
+	ADC_CHANNEL_HW_SETTLE_DELAY_2MS,
+	ADC_CHANNEL_HW_SETTLE_DELAY_4MS,
+	ADC_CHANNEL_HW_SETTLE_DELAY_6MS,
+	ADC_CHANNEL_HW_SETTLE_DELAY_8MS,
+	ADC_CHANNEL_HW_SETTLE_DELAY_10MS,
+	ADC_CHANNEL_HW_SETTLE_NONE,
+};
+
+/**
+ * enum qpnp_adc_dec_ratio_sel - Selects the decimation ratio of the ADC.
+ *				 Support values are 256, 512 and 1024.
+ */
+enum qpnp_vadc_dec_ratio_sel {
+	ADC_DEC_RATIO_256 = 0,
+	ADC_DEC_RATIO_512,
+	ADC_DEC_RATIO_1024,
+	ADC_DEC_RATIO_NONE,
+};
+
+/**
+ * enum qpnp_adc_cal_sel - Selects the calibration type that is applied
+ *			   on the corresponding channel measurement after
+ *			   the ADC data is read.
+ * %ADC_NO_CAL :	To obtain raw, uncalibrated data.
+ * %ADC_RATIO_CAL :	Applies ratiometric calibration. Note the calibration
+ *			values stored in the CAL peripheral for VADC_VREF and
+ *			VREF_1P25 already have GND_REF value removed.
+ * %ADC_ABS_CAL :	Applies absolute calibration. Note the calibration
+ *			values stored in the CAL peripheral for VADC_VREF and
+ *			VREF_1P25 already have GND_REF value removed.
+ */
+
+/**
+ * enum qpnp_adc_cal_val - Selects if the calibration values applied
+ *			    are the ones when collected on a timer interval
+ *			    or if an immediate calibration needs to be forced.
+ * %ADC_TIMER_CAL : Uses calibration value collected on the timer interval.
+ * %ADC_NEW_CAL : Forces an immediate calibration. Use only when necessary
+ *		  since it forces 3 calibration measurements in addition to
+ *		  the channel measurement. For most measurement, using
+ *		  calibration based on the timer interval is sufficient.
+ */
+enum qpnp_adc_cal_val {
+	ADC_TIMER_CAL = 0,
+	ADC_NEW_CAL,
+	ADC_CAL_VAL_NONE,
+};
+
+/**
+ * enum qpnp_vadc_mode_sel - Selects the basic mode of operation.
+ *		- The normal mode is used for single measurement.
+ *		- The Conversion sequencer is used to trigger an
+ *		  ADC read when a HW trigger is selected.
+ *		- The measurement interval performs a single or
+ *		  continuous measurement at a specified interval/delay.
+ * %ADC_OP_NORMAL_MODE : Normal mode used for single measurement.
+ * %ADC_OP_CONVERSION_SEQUENCER : Conversion sequencer used to trigger
+ *		  an ADC read on a HW supported trigger.
+ *		  Refer to enum qpnp_vadc_trigger for
+ *		  supported HW triggers.
+ * %ADC_OP_MEASUREMENT_INTERVAL : The measurement interval performs a
+ *		  single or continuous measurement after a specified delay.
+ *		  For delay look at qpnp_adc_meas_timer.
+ */
+enum qpnp_vadc_mode_sel {
+	ADC_OP_NORMAL_MODE = 0,
+	ADC_OP_CONVERSION_SEQUENCER,
+	ADC_OP_MEASUREMENT_INTERVAL,
+	ADC_OP_MODE_NONE,
+};
+
+/**
+ * enum qpnp_vadc_trigger - Select the HW trigger to be used while
+ *		measuring the ADC reading.
+ * %ADC_GSM_PA_ON : GSM power amplifier on.
+ * %ADC_TX_GTR_THRES : Transmit power greater than threshold.
+ * %ADC_CAMERA_FLASH_RAMP : Flash ramp up done.
+ * %ADC_DTEST : DTEST.
+ */
+enum qpnp_vadc_trigger {
+	ADC_GSM_PA_ON = 0,
+	ADC_TX_GTR_THRES,
+	ADC_CAMERA_FLASH_RAMP,
+	ADC_DTEST,
+	ADC_SEQ_NONE,
+};
+
+/**
+ * enum qpnp_vadc_conv_seq_timeout - Select delay (0 to 15ms) from
+ *		conversion request to triggering conversion sequencer
+ *		hold off time.
+ */
+enum qpnp_vadc_conv_seq_timeout {
+	ADC_CONV_SEQ_TIMEOUT_0MS = 0,
+	ADC_CONV_SEQ_TIMEOUT_1MS,
+	ADC_CONV_SEQ_TIMEOUT_2MS,
+	ADC_CONV_SEQ_TIMEOUT_3MS,
+	ADC_CONV_SEQ_TIMEOUT_4MS,
+	ADC_CONV_SEQ_TIMEOUT_5MS,
+	ADC_CONV_SEQ_TIMEOUT_6MS,
+	ADC_CONV_SEQ_TIMEOUT_7MS,
+	ADC_CONV_SEQ_TIMEOUT_8MS,
+	ADC_CONV_SEQ_TIMEOUT_9MS,
+	ADC_CONV_SEQ_TIMEOUT_10MS,
+	ADC_CONV_SEQ_TIMEOUT_11MS,
+	ADC_CONV_SEQ_TIMEOUT_12MS,
+	ADC_CONV_SEQ_TIMEOUT_13MS,
+	ADC_CONV_SEQ_TIMEOUT_14MS,
+	ADC_CONV_SEQ_TIMEOUT_15MS,
+	ADC_CONV_SEQ_TIMEOUT_NONE,
+};
+
+/**
+ * enum qpnp_adc_conv_seq_holdoff - Select delay from conversion
+ *		trigger signal (i.e. adc_conv_seq_trig) transition
+ *		to ADC enable. Delay = 25us * (value + 1).
+ */
+enum qpnp_adc_conv_seq_holdoff {
+	ADC_SEQ_HOLD_25US = 0,
+	ADC_SEQ_HOLD_50US,
+	ADC_SEQ_HOLD_75US,
+	ADC_SEQ_HOLD_100US,
+	ADC_SEQ_HOLD_125US,
+	ADC_SEQ_HOLD_150US,
+	ADC_SEQ_HOLD_175US,
+	ADC_SEQ_HOLD_200US,
+	ADC_SEQ_HOLD_225US,
+	ADC_SEQ_HOLD_250US,
+	ADC_SEQ_HOLD_275US,
+	ADC_SEQ_HOLD_300US,
+	ADC_SEQ_HOLD_325US,
+	ADC_SEQ_HOLD_350US,
+	ADC_SEQ_HOLD_375US,
+	ADC_SEQ_HOLD_400US,
+	ADC_SEQ_HOLD_NONE,
+};
+
+/**
+ * enum qpnp_adc_conv_seq_state - Conversion sequencer operating state
+ * %ADC_CONV_SEQ_IDLE : Sequencer is in idle.
+ * %ADC_CONV_TRIG_RISE : Waiting for rising edge trigger.
+ * %ADC_CONV_TRIG_HOLDOFF : Waiting for rising trigger hold off time.
+ * %ADC_CONV_MEAS_RISE : Measuring selected ADC signal.
+ * %ADC_CONV_TRIG_FALL : Waiting for falling trigger edge.
+ * %ADC_CONV_FALL_HOLDOFF : Waiting for falling trigger hold off time.
+ * %ADC_CONV_MEAS_FALL : Measuring selected ADC signal.
+ * %ADC_CONV_ERROR : Aberrant Hardware problem.
+ */
+enum qpnp_adc_conv_seq_state {
+	ADC_CONV_SEQ_IDLE = 0,
+	ADC_CONV_TRIG_RISE,
+	ADC_CONV_TRIG_HOLDOFF,
+	ADC_CONV_MEAS_RISE,
+	ADC_CONV_TRIG_FALL,
+	ADC_CONV_FALL_HOLDOFF,
+	ADC_CONV_MEAS_FALL,
+	ADC_CONV_ERROR,
+	ADC_CONV_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_1 - Selects the measurement interval time.
+ *		If value = 0, use 0ms else use 2^(value + 4)/ 32768).
+ * The timer period is used by the USB_ID. Do not set a polling rate
+ * greater than 1 second on PMIC 2.0. The max polling rate on the PMIC 2.0
+ * appears to be limited to 1 second.
+ * %ADC_MEAS_INTERVAL_0MS : 0ms
+ * %ADC_MEAS_INTERVAL_1P0MS : 1ms
+ * %ADC_MEAS_INTERVAL_2P0MS : 2ms
+ * %ADC_MEAS_INTERVAL_3P9MS : 3.9ms
+ * %ADC_MEAS_INTERVAL_7P8MS : 7.8ms
+ * %ADC_MEAS_INTERVAL_15P6MS : 15.6ms
+ * %ADC_MEAS_INTERVAL_31P3MS : 31.3ms
+ * %ADC_MEAS_INTERVAL_62P5MS : 62.5ms
+ * %ADC_MEAS_INTERVAL_125MS : 125ms
+ * %ADC_MEAS_INTERVAL_250MS : 250ms
+ * %ADC_MEAS_INTERVAL_500MS : 500ms
+ * %ADC_MEAS_INTERVAL_1S : 1seconds
+ * %ADC_MEAS_INTERVAL_2S : 2seconds
+ * %ADC_MEAS_INTERVAL_4S : 4seconds
+ * %ADC_MEAS_INTERVAL_8S : 8seconds
+ * %ADC_MEAS_INTERVAL_16S: 16seconds
+ */
+enum qpnp_adc_meas_timer_1 {
+	ADC_MEAS1_INTERVAL_0MS = 0,
+	ADC_MEAS1_INTERVAL_1P0MS,
+	ADC_MEAS1_INTERVAL_2P0MS,
+	ADC_MEAS1_INTERVAL_3P9MS,
+	ADC_MEAS1_INTERVAL_7P8MS,
+	ADC_MEAS1_INTERVAL_15P6MS,
+	ADC_MEAS1_INTERVAL_31P3MS,
+	ADC_MEAS1_INTERVAL_62P5MS,
+	ADC_MEAS1_INTERVAL_125MS,
+	ADC_MEAS1_INTERVAL_250MS,
+	ADC_MEAS1_INTERVAL_500MS,
+	ADC_MEAS1_INTERVAL_1S,
+	ADC_MEAS1_INTERVAL_2S,
+	ADC_MEAS1_INTERVAL_4S,
+	ADC_MEAS1_INTERVAL_8S,
+	ADC_MEAS1_INTERVAL_16S,
+	ADC_MEAS1_INTERVAL_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_2 - Selects the measurement interval time.
+ *		If value = 0, use 0ms else use 2^(value + 4)/ 32768).
+ * The timer period is used by the batt_therm. Do not set a polling rate
+ * greater than 1 second on PMIC 2.0. The max polling rate on the PMIC 2.0
+ * appears to be limited to 1 second.
+ * %ADC_MEAS_INTERVAL_0MS : 0ms
+ * %ADC_MEAS_INTERVAL_100MS : 100ms
+ * %ADC_MEAS_INTERVAL_200MS : 200ms
+ * %ADC_MEAS_INTERVAL_300MS : 300ms
+ * %ADC_MEAS_INTERVAL_400MS : 400ms
+ * %ADC_MEAS_INTERVAL_500MS : 500ms
+ * %ADC_MEAS_INTERVAL_600MS : 600ms
+ * %ADC_MEAS_INTERVAL_700MS : 700ms
+ * %ADC_MEAS_INTERVAL_800MS : 800ms
+ * %ADC_MEAS_INTERVAL_900MS : 900ms
+ * %ADC_MEAS_INTERVAL_1S: 1seconds
+ * %ADC_MEAS_INTERVAL_1P1S: 1.1seconds
+ * %ADC_MEAS_INTERVAL_1P2S: 1.2seconds
+ * %ADC_MEAS_INTERVAL_1P3S: 1.3seconds
+ * %ADC_MEAS_INTERVAL_1P4S: 1.4seconds
+ * %ADC_MEAS_INTERVAL_1P5S: 1.5seconds
+ */
+enum qpnp_adc_meas_timer_2 {
+	ADC_MEAS2_INTERVAL_0MS = 0,
+	ADC_MEAS2_INTERVAL_100MS,
+	ADC_MEAS2_INTERVAL_200MS,
+	ADC_MEAS2_INTERVAL_300MS,
+	ADC_MEAS2_INTERVAL_400MS,
+	ADC_MEAS2_INTERVAL_500MS,
+	ADC_MEAS2_INTERVAL_600MS,
+	ADC_MEAS2_INTERVAL_700MS,
+	ADC_MEAS2_INTERVAL_800MS,
+	ADC_MEAS2_INTERVAL_900MS,
+	ADC_MEAS2_INTERVAL_1S,
+	ADC_MEAS2_INTERVAL_1P1S,
+	ADC_MEAS2_INTERVAL_1P2S,
+	ADC_MEAS2_INTERVAL_1P3S,
+	ADC_MEAS2_INTERVAL_1P4S,
+	ADC_MEAS2_INTERVAL_1P5S,
+	ADC_MEAS2_INTERVAL_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_3 - Selects the measurement interval time.
+ *		If value = 0, use 0ms else use 2^(value + 4)/ 32768).
+ * Do not set a polling rate greater than 1 second on PMIC 2.0.
+ * The max polling rate on the PMIC 2.0 appears to be limited to 1 second.
+ * %ADC_MEAS_INTERVAL_0MS : 0ms
+ * %ADC_MEAS_INTERVAL_1S : 1seconds
+ * %ADC_MEAS_INTERVAL_2S : 2seconds
+ * %ADC_MEAS_INTERVAL_3S : 3seconds
+ * %ADC_MEAS_INTERVAL_4S : 4seconds
+ * %ADC_MEAS_INTERVAL_5S : 5seconds
+ * %ADC_MEAS_INTERVAL_6S: 6seconds
+ * %ADC_MEAS_INTERVAL_7S : 7seconds
+ * %ADC_MEAS_INTERVAL_8S : 8seconds
+ * %ADC_MEAS_INTERVAL_9S : 9seconds
+ * %ADC_MEAS_INTERVAL_10S : 10seconds
+ * %ADC_MEAS_INTERVAL_11S : 11seconds
+ * %ADC_MEAS_INTERVAL_12S : 12seconds
+ * %ADC_MEAS_INTERVAL_13S : 13seconds
+ * %ADC_MEAS_INTERVAL_14S : 14seconds
+ * %ADC_MEAS_INTERVAL_15S : 15seconds
+ */
+enum qpnp_adc_meas_timer_3 {
+	ADC_MEAS3_INTERVAL_0S = 0,
+	ADC_MEAS3_INTERVAL_1S,
+	ADC_MEAS3_INTERVAL_2S,
+	ADC_MEAS3_INTERVAL_3S,
+	ADC_MEAS3_INTERVAL_4S,
+	ADC_MEAS3_INTERVAL_5S,
+	ADC_MEAS3_INTERVAL_6S,
+	ADC_MEAS3_INTERVAL_7S,
+	ADC_MEAS3_INTERVAL_8S,
+	ADC_MEAS3_INTERVAL_9S,
+	ADC_MEAS3_INTERVAL_10S,
+	ADC_MEAS3_INTERVAL_11S,
+	ADC_MEAS3_INTERVAL_12S,
+	ADC_MEAS3_INTERVAL_13S,
+	ADC_MEAS3_INTERVAL_14S,
+	ADC_MEAS3_INTERVAL_15S,
+	ADC_MEAS3_INTERVAL_NONE,
+};
+
+/**
+ * enum qpnp_adc_meas_timer_select - Selects the timer for which
+ *	the appropriate polling frequency is set.
+ * %ADC_MEAS_TIMER_SELECT1 - Select this timer for measurement polling interval
+ *				for 1 second.
+ * %ADC_MEAS_TIMER_SELECT2 - Select this timer for 500ms measurement interval.
+ * %ADC_MEAS_TIMER_SELECT3 - Select this timer for 5 second interval.
+ */
+enum qpnp_adc_meas_timer_select {
+	ADC_MEAS_TIMER_SELECT1 = 0,
+	ADC_MEAS_TIMER_SELECT2,
+	ADC_MEAS_TIMER_SELECT3,
+	ADC_MEAS_TIMER_NUM,
+};
+
+/**
+ * enum qpnp_adc_meas_interval_op_ctl - Select operating mode.
+ * %ADC_MEAS_INTERVAL_OP_SINGLE : Conduct single measurement at specified time
+ *			delay.
+ * %ADC_MEAS_INTERVAL_OP_CONTINUOUS : Make measurements at measurement interval
+ *			times.
+ */
+enum qpnp_adc_meas_interval_op_ctl {
+	ADC_MEAS_INTERVAL_OP_SINGLE = 0,
+	ADC_MEAS_INTERVAL_OP_CONTINUOUS,
+	ADC_MEAS_INTERVAL_OP_NONE,
+};
+
+/**
+ * Channel selection registers for each of the configurable measurements
+ * Channels allotment is set at device config for a channel.
+ * The USB_ID, BATT_THERM, PMIC_THERM and VBAT channels are used by the
+ * kernel space USB, Battery and IADC drivers.
+ * The other 3 channels are configurable for use by userspace clients.
+ */
+enum qpnp_adc_tm_channel_select	{
+	QPNP_ADC_TM_M0_ADC_CH_SEL_CTL = 0x48,
+	QPNP_ADC_TM_M1_ADC_CH_SEL_CTL = 0x68,
+	QPNP_ADC_TM_M2_ADC_CH_SEL_CTL = 0x70,
+	QPNP_ADC_TM_M3_ADC_CH_SEL_CTL = 0x78,
+	QPNP_ADC_TM_M4_ADC_CH_SEL_CTL = 0x80,
+	QPNP_ADC_TM_M5_ADC_CH_SEL_CTL = 0x88,
+	QPNP_ADC_TM_M6_ADC_CH_SEL_CTL = 0x90,
+	QPNP_ADC_TM_M7_ADC_CH_SEL_CTL = 0x98,
+	QPNP_ADC_TM_CH_SELECT_NONE
+};
+
+/**
+ * Channel index for the corresponding index to qpnp_adc_tm_channel_selec
+ */
+enum qpnp_adc_tm_channel_num {
+	QPNP_ADC_TM_CHAN0 = 0,
+	QPNP_ADC_TM_CHAN1,
+	QPNP_ADC_TM_CHAN2,
+	QPNP_ADC_TM_CHAN3,
+	QPNP_ADC_TM_CHAN4,
+	QPNP_ADC_TM_CHAN5,
+	QPNP_ADC_TM_CHAN6,
+	QPNP_ADC_TM_CHAN7,
+	QPNP_ADC_TM_CHAN_NONE
+};
+
+enum qpnp_comp_scheme_type {
+	COMP_ID_GF = 0,
+	COMP_ID_SMIC,
+	COMP_ID_TSMC,
+	COMP_ID_NUM,
+};
+
+/**
+ * struct qpnp_adc_tm_config - Represent ADC Thermal Monitor configuration.
+ * @channel: ADC channel for which thermal monitoring is requested.
+ * @adc_code: The pre-calibrated digital output of a given ADC releative to the
+ *		ADC reference.
+ * @high_thr_temp: Temperature at which high threshold notification is required.
+ * @low_thr_temp: Temperature at which low threshold notification is required.
+ * @low_thr_voltage : Low threshold voltage ADC code used for reverse
+ *			calibration.
+ * @high_thr_voltage: High threshold voltage ADC code used for reverse
+ *			calibration.
+ */
+struct qpnp_adc_tm_config {
+	int	channel;
+	int	adc_code;
+	int	high_thr_temp;
+	int	low_thr_temp;
+	int64_t	high_thr_voltage;
+	int64_t	low_thr_voltage;
+};
+
+/**
+ * enum qpnp_adc_tm_trip_type - Type for setting high/low temperature/voltage.
+ * %ADC_TM_TRIP_HIGH_WARM: Setting high temperature. Note that high temperature
+ *			corresponds to low voltage. Driver handles this case
+ *			appropriately to set high/low thresholds for voltage.
+ *			threshold.
+ * %ADC_TM_TRIP_LOW_COOL: Setting low temperature.
+ */
+enum qpnp_adc_tm_trip_type {
+	ADC_TM_TRIP_HIGH_WARM = 0,
+	ADC_TM_TRIP_LOW_COOL,
+	ADC_TM_TRIP_NUM,
+};
+
+#define ADC_TM_WRITABLE_TRIPS_MASK ((1 << ADC_TM_TRIP_NUM) - 1)
+
+/**
+ * enum qpnp_tm_state - This lets the client know whether the threshold
+ *		that was crossed was high/low.
+ * %ADC_TM_HIGH_STATE: Client is notified of crossing the requested high
+ *			voltage threshold.
+ * %ADC_TM_COOL_STATE: Client is notified of crossing the requested cool
+ *			temperature threshold.
+ * %ADC_TM_LOW_STATE: Client is notified of crossing the requested low
+ *			voltage threshold.
+ * %ADC_TM_WARM_STATE: Client is notified of crossing the requested high
+ *			temperature threshold.
+ */
+enum qpnp_tm_state {
+	ADC_TM_HIGH_STATE = 0,
+	ADC_TM_COOL_STATE = ADC_TM_HIGH_STATE,
+	ADC_TM_LOW_STATE,
+	ADC_TM_WARM_STATE = ADC_TM_LOW_STATE,
+	ADC_TM_STATE_NUM,
+};
+
+/**
+ * enum qpnp_state_request - Request to enable/disable the corresponding
+ *			high/low voltage/temperature thresholds.
+ * %ADC_TM_HIGH_THR_ENABLE: Enable high voltage threshold.
+ * %ADC_TM_COOL_THR_ENABLE = Enables cool temperature threshold.
+ * %ADC_TM_LOW_THR_ENABLE: Enable low voltage/temperature threshold.
+ * %ADC_TM_WARM_THR_ENABLE = Enables warm temperature threshold.
+ * %ADC_TM_HIGH_LOW_THR_ENABLE: Enable high and low voltage/temperature
+ *				threshold.
+ * %ADC_TM_HIGH_THR_DISABLE: Disable high voltage/temperature threshold.
+ * %ADC_TM_COOL_THR_ENABLE = Disables cool temperature threshold.
+ * %ADC_TM_LOW_THR_DISABLE: Disable low voltage/temperature threshold.
+ * %ADC_TM_WARM_THR_ENABLE = Disables warm temperature threshold.
+ * %ADC_TM_HIGH_THR_DISABLE: Disable high and low voltage/temperature
+ *				threshold.
+ */
+enum qpnp_state_request {
+	ADC_TM_HIGH_THR_ENABLE = 0,
+	ADC_TM_COOL_THR_ENABLE = ADC_TM_HIGH_THR_ENABLE,
+	ADC_TM_LOW_THR_ENABLE,
+	ADC_TM_WARM_THR_ENABLE = ADC_TM_LOW_THR_ENABLE,
+	ADC_TM_HIGH_LOW_THR_ENABLE,
+	ADC_TM_HIGH_THR_DISABLE,
+	ADC_TM_COOL_THR_DISABLE = ADC_TM_HIGH_THR_DISABLE,
+	ADC_TM_LOW_THR_DISABLE,
+	ADC_TM_WARM_THR_DISABLE = ADC_TM_LOW_THR_DISABLE,
+	ADC_TM_HIGH_LOW_THR_DISABLE,
+	ADC_TM_THR_NUM,
+};
+
+/**
+ * struct qpnp_adc_tm_btm_param - Represent Battery temperature threshold
+ *				monitoring configuration.
+ * @high_temp: High temperature threshold for which notification is requested.
+ * @low_temp: Low temperature threshold for which notification is requested.
+ * @high_thr_voltage: High voltage for which notification is requested.
+ * @low_thr_voltage: Low voltage for which notification is requested.
+ * @adc_tm_hc: Represents the refreshed BTM register design.
+ * @state_request: Enable/disable the corresponding high and low temperature
+ *		thresholds.
+ * @timer_interval1: Select polling rate from qpnp_adc_meas_timer_1 type.
+ * @timer_interval2: Select polling rate from qpnp_adc_meas_timer_2 type.
+ * @timer_interval3: Select polling rate from qpnp_adc_meas_timer_3 type.
+ * @btmid_ctx: A context of void type.
+ * @threshold_notification: Notification callback once threshold are crossed.
+ * units to be used for High/Low temperature and voltage notification -
+ * This depends on the clients usage. Check the rscaling function
+ * for the appropriate channel nodes.
+ * @Batt therm clients temperature units is decidegreesCentigrate.
+ * @USB_ID inputs the voltage units in milli-volts.
+ * @PA_THERM inputs the units in degC.
+ * @PMIC_THERM inputs the units in millidegC.
+ */
+struct qpnp_adc_tm_btm_param {
+	int32_t					high_temp;
+	int32_t					low_temp;
+	int32_t					high_thr;
+	int32_t					low_thr;
+	int32_t					gain_num;
+	int32_t					gain_den;
+	bool					adc_tm_hc;
+	enum qpnp_vadc_channels			channel;
+	enum qpnp_state_request			state_request;
+	enum qpnp_adc_meas_timer_1		timer_interval;
+	enum qpnp_adc_meas_timer_2		timer_interval2;
+	enum qpnp_adc_meas_timer_3		timer_interval3;
+	void					*btm_ctx;
+	void	(*threshold_notification)(enum qpnp_tm_state state,
+						void *ctx);
+};
+
+/**
+ * struct qpnp_vadc_linear_graph - Represent ADC characteristics.
+ * @dy: Numerator slope to calculate the gain.
+ * @dx: Denominator slope to calculate the gain.
+ * @adc_vref: A/D word of the voltage reference used for the channel.
+ * @adc_gnd: A/D word of the ground reference used for the channel.
+ *
+ * Each ADC device has different offset and gain parameters which are computed
+ * to calibrate the device.
+ */
+struct qpnp_vadc_linear_graph {
+	int64_t dy;
+	int64_t dx;
+	int64_t adc_vref;
+	int64_t adc_gnd;
+};
+
+/**
+ * struct qpnp_vadc_map_pt - Map the graph representation for ADC channel
+ * @x: Represent the ADC digitized code.
+ * @y: Represent the physical data which can be temperature, voltage,
+ *     resistance.
+ */
+struct qpnp_vadc_map_pt {
+	int32_t x;
+	int32_t y;
+};
+
+/**
+ * struct qpnp_vadc_scaling_ratio - Represent scaling ratio for adc input.
+ * @num: Numerator scaling parameter.
+ * @den: Denominator scaling parameter.
+ */
+struct qpnp_vadc_scaling_ratio {
+	int32_t num;
+	int32_t den;
+};
+
+/**
+ * struct qpnp_adc_properties - Represent the ADC properties.
+ * @adc_reference: Reference voltage for QPNP ADC.
+ * @bitresolution: ADC bit resolution for QPNP ADC.
+ * @biploar: Polarity for QPNP ADC.
+ * @adc_hc: Represents using HC variant of the ADC controller.
+ */
+struct qpnp_adc_properties {
+	uint32_t	adc_vdd_reference;
+	uint32_t	bitresolution;
+	bool		bipolar;
+	bool		adc_hc;
+};
+
+/**
+ * struct qpnp_vadc_chan_properties - Represent channel properties of the ADC.
+ * @offset_gain_numerator: The inverse numerator of the gain applied to the
+ *			   input channel.
+ * @offset_gain_denominator: The inverse denominator of the gain applied to the
+ *			     input channel.
+ * @high_thr: High threshold voltage that is requested to be set.
+ * @low_thr: Low threshold voltage that is requested to be set.
+ * @timer_select: Chosen from one of the 3 timers to set the polling rate for
+ *		  the VADC_BTM channel.
+ * @meas_interval1: Polling rate to set for timer 1.
+ * @meas_interval2: Polling rate to set for timer 2.
+ * @tm_channel_select: BTM channel number for the 5 VADC_BTM channels.
+ * @state_request: User can select either enable or disable high/low or both
+ * activation levels based on the qpnp_state_request type.
+ * @adc_graph: ADC graph for the channel of struct type qpnp_adc_linear_graph.
+ */
+struct qpnp_vadc_chan_properties {
+	uint32_t			offset_gain_numerator;
+	uint32_t			offset_gain_denominator;
+	uint32_t				high_thr;
+	uint32_t				low_thr;
+	enum qpnp_adc_meas_timer_select		timer_select;
+	enum qpnp_adc_meas_timer_1		meas_interval1;
+	enum qpnp_adc_meas_timer_2		meas_interval2;
+	enum qpnp_adc_tm_channel_select		tm_channel_select;
+	enum qpnp_state_request			state_request;
+	enum qpnp_adc_calib_type		calib_type;
+	struct qpnp_vadc_linear_graph	adc_graph[ADC_HC_CAL_SEL_NONE];
+};
+
+/**
+ * struct qpnp_vadc_result - Represent the result of the QPNP ADC.
+ * @chan: The channel number of the requested conversion.
+ * @adc_code: The pre-calibrated digital output of a given ADC relative to the
+ *	      the ADC reference.
+ * @measurement: In units specific for a given ADC; most ADC uses reference
+ *		 voltage but some ADC uses reference current. This measurement
+ *		 here is a number relative to a reference of a given ADC.
+ * @physical: The data meaningful for each individual channel whether it is
+ *	      voltage, current, temperature, etc.
+ *	      All voltage units are represented in micro - volts.
+ *	      -Battery temperature units are represented as 0.1 DegC.
+ *	      -PA Therm temperature units are represented as DegC.
+ *	      -PMIC Die temperature units are represented as 0.001 DegC.
+ */
+struct qpnp_vadc_result {
+	uint32_t	chan;
+	int32_t		adc_code;
+	int64_t		measurement;
+	int64_t		physical;
+};
+
+/**
+ * struct qpnp_adc_amux - AMUX properties for individual channel
+ * @name: Channel string name.
+ * @channel_num: Channel in integer used from qpnp_adc_channels.
+ * @chan_path_prescaling: Channel scaling performed on the input signal.
+ * @adc_decimation: Sampling rate desired for the channel.
+ * adc_scale_fn: Scaling function to convert to the data meaningful for
+ *		 each individual channel whether it is voltage, current,
+ *		 temperature, etc and compensates the channel properties.
+ */
+struct qpnp_adc_amux {
+	char					*name;
+	enum qpnp_vadc_channels			channel_num;
+	enum qpnp_adc_channel_scaling_param	chan_path_prescaling;
+	enum qpnp_adc_decimation_type		adc_decimation;
+	enum qpnp_adc_scale_fn_type		adc_scale_fn;
+	enum qpnp_adc_fast_avg_ctl		fast_avg_setup;
+	enum qpnp_adc_hw_settle_time		hw_settle_time;
+	enum qpnp_adc_calib_type		calib_type;
+	enum qpnp_adc_cal_val			cal_val;
+};
+
+/**
+ * struct qpnp_vadc_scaling_ratio
+ *
+ */
+static const struct qpnp_vadc_scaling_ratio qpnp_vadc_amux_scaling_ratio[] = {
+	{1, 1},
+	{1, 3},
+	{1, 4},
+	{1, 6},
+	{1, 20},
+	{1, 8},
+	{10, 81},
+	{1, 10}
+};
+
+/**
+ * struct qpnp_vadc_scale_fn - Scaling function prototype
+ * @chan: Function pointer to one of the scaling functions
+ *	which takes the adc properties, channel properties,
+ *	and returns the physical result
+ */
+struct qpnp_vadc_scale_fn {
+	int32_t (*chan)(struct qpnp_vadc_chip *, int32_t,
+		const struct qpnp_adc_properties *,
+		const struct qpnp_vadc_chan_properties *,
+		struct qpnp_vadc_result *);
+};
+
+/**
+ * struct qpnp_adc_tm_reverse_scale_fn - Reverse scaling prototype
+ * @chan: Function pointer to one of the scaling functions
+ *	which takes the adc properties, channel properties,
+ *	and returns the physical result
+ */
+struct qpnp_adc_tm_reverse_scale_fn {
+	int32_t (*chan)(struct qpnp_vadc_chip *,
+		struct qpnp_adc_tm_btm_param *,
+		uint32_t *, uint32_t *);
+};
+
+/**
+ * struct qpnp_vadc_rscale_fn - Scaling function prototype
+ * @chan: Function pointer to one of the scaling functions
+ *	which takes the adc properties, channel properties,
+ *	and returns the physical result
+ */
+struct qpnp_vadc_rscale_fn {
+	int32_t (*chan)(struct qpnp_vadc_chip *,
+		const struct qpnp_vadc_chan_properties *,
+		struct qpnp_adc_tm_btm_param *,
+		uint32_t *, uint32_t *);
+};
+
+/**
+ * struct qpnp_iadc_calib - IADC channel calibration structure.
+ * @channel - Channel for which the historical offset and gain is
+ *	      calculated. Available channels are internal rsense,
+ *	      external rsense and alternate lead pairs.
+ * @offset_raw - raw Offset value for the channel.
+ * @gain_raw - raw Gain of the channel.
+ * @ideal_offset_uv - ideal offset value for the channel.
+ * @ideal_gain_nv - ideal gain for the channel.
+ * @offset_uv - converted value of offset in uV.
+ * @gain_uv - converted value of gain in uV.
+ */
+struct qpnp_iadc_calib {
+	enum qpnp_iadc_channels		channel;
+	uint16_t			offset_raw;
+	uint16_t			gain_raw;
+	uint32_t			ideal_offset_uv;
+	uint32_t			ideal_gain_nv;
+	uint32_t			offset_uv;
+	uint32_t			gain_uv;
+};
+
+/**
+ * struct qpnp_iadc_result - IADC read result structure.
+ * @oresult_uv - Result of ADC in uV.
+ * @result_ua - Result of ADC in uA.
+ */
+struct qpnp_iadc_result {
+	int32_t				result_uv;
+	int32_t				result_ua;
+};
+
+/**
+ * struct qpnp_adc_drv - QPNP ADC device structure.
+ * @spmi - spmi device for ADC peripheral.
+ * @offset - base offset for the ADC peripheral.
+ * @adc_prop - ADC properties specific to the ADC peripheral.
+ * @amux_prop - AMUX properties representing the ADC peripheral.
+ * @adc_channels - ADC channel properties for the ADC peripheral.
+ * @adc_irq_eoc - End of Conversion IRQ.
+ * @adc_irq_fifo_not_empty - Conversion sequencer request written
+ *			to FIFO when not empty.
+ * @adc_irq_conv_seq_timeout - Conversion sequencer trigger timeout.
+ * @adc_high_thr_irq - Output higher than high threshold set for measurement.
+ * @adc_low_thr_irq - Output lower than low threshold set for measurement.
+ * @adc_lock - ADC lock for access to the peripheral.
+ * @adc_rslt_completion - ADC result notification after interrupt
+ *			  is received.
+ * @calib - Internal rsens calibration values for gain and offset.
+ */
+struct qpnp_adc_drv {
+	struct platform_device		*pdev;
+	struct regmap			*regmap;
+	uint8_t				slave;
+	uint16_t			offset;
+	struct qpnp_adc_properties	*adc_prop;
+	struct qpnp_adc_amux_properties	*amux_prop;
+	struct qpnp_adc_amux		*adc_channels;
+	int				adc_irq_eoc;
+	int				adc_irq_fifo_not_empty;
+	int				adc_irq_conv_seq_timeout;
+	int				adc_high_thr_irq;
+	int				adc_low_thr_irq;
+	struct mutex			adc_lock;
+	struct completion		adc_rslt_completion;
+	struct qpnp_iadc_calib		calib;
+	struct regulator		*hkadc_ldo;
+	struct regulator		*hkadc_ldo_ok;
+	bool				adc_hc;
+};
+
+/**
+ * struct qpnp_adc_amux_properties - QPNP VADC amux channel property.
+ * @amux_channel - Refer to the qpnp_vadc_channel list.
+ * @decimation - Sampling rate supported for the channel.
+ * @mode_sel - The basic mode of operation.
+ * @hw_settle_time - The time between AMUX being configured and the
+ *			start of conversion.
+ * @fast_avg_setup - Ability to provide single result from the ADC
+ *			that is an average of multiple measurements.
+ * @trigger_channel - HW trigger channel for conversion sequencer.
+ * @calib_type - Used to store the calibration type for the channel
+ *		 absolute/ratiometric.
+ * @cal_val - Used to determine if fresh calibration value or timer
+ *	      updated calibration value is to be used.
+ * @chan_prop - Represent the channel properties of the ADC.
+ */
+struct qpnp_adc_amux_properties {
+	uint32_t				amux_channel;
+	uint32_t				decimation;
+	uint32_t				mode_sel;
+	uint32_t				hw_settle_time;
+	uint32_t				fast_avg_setup;
+	enum qpnp_vadc_trigger			trigger_channel;
+	enum qpnp_adc_calib_type		calib_type;
+	enum qpnp_adc_cal_val			cal_val;
+	struct qpnp_vadc_chan_properties	chan_prop[0];
+};
+
+/* SW index's for PMIC type and version used by QPNP VADC and IADC */
+#define QPNP_REV_ID_8941_3_1	1
+#define QPNP_REV_ID_8026_1_0	2
+#define QPNP_REV_ID_8026_2_0	3
+#define QPNP_REV_ID_8110_1_0	4
+#define QPNP_REV_ID_8026_2_1	5
+#define QPNP_REV_ID_8110_2_0	6
+#define QPNP_REV_ID_8026_2_2	7
+#define QPNP_REV_ID_8941_3_0	8
+#define QPNP_REV_ID_8941_2_0	9
+#define QPNP_REV_ID_8916_1_0	10
+#define QPNP_REV_ID_8916_1_1	11
+#define QPNP_REV_ID_8916_2_0	12
+#define QPNP_REV_ID_8909_1_0	13
+#define QPNP_REV_ID_8909_1_1	14
+#define QPNP_REV_ID_PM8950_1_0	16
+
+/* Public API */
+#if defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE)				\
+			|| defined(CONFIG_SENSORS_QPNP_ADC_VOLTAGE_MODULE)
+/**
+ * qpnp_vadc_read() - Performs ADC read on the channel.
+ * @dev:	Structure device for qpnp vadc
+ * @channel:	Input channel to perform the ADC read.
+ * @result:	Structure pointer of type adc_chan_result
+ *		in which the ADC read results are stored.
+ */
+int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev,
+				enum qpnp_vadc_channels channel,
+				struct qpnp_vadc_result *result);
+
+/**
+ * qpnp_vadc_hc_read() - Performs ADC read on the channel.
+ *		It uses the refreshed VADC design from qpnp-vadc-hc.
+ * @dev:	Structure device for qpnp vadc
+ * @channel:	Input channel to perform the ADC read.
+ * @result:	Structure pointer of type adc_chan_result
+ *		in which the ADC read results are stored.
+ */
+int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *dev,
+				enum qpnp_vadc_channels channel,
+				struct qpnp_vadc_result *result);
+
+/**
+ * qpnp_vadc_conv_seq_request() - Performs ADC read on the conversion
+ *				sequencer channel.
+ * @dev:	Structure device for qpnp vadc
+ * @channel:	Input channel to perform the ADC read.
+ * @result:	Structure pointer of type adc_chan_result
+ *		in which the ADC read results are stored.
+ */
+int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *dev,
+			enum qpnp_vadc_trigger trigger_channel,
+			enum qpnp_vadc_channels channel,
+			struct qpnp_vadc_result *result);
+
+/**
+ * qpnp_adc_get_devicetree_data() - Abstracts the ADC devicetree data.
+ * @spmi:	spmi ADC device.
+ * @adc_qpnp:	spmi device tree node structure
+ */
+int32_t qpnp_adc_get_devicetree_data(struct platform_device *pdev,
+					struct qpnp_adc_drv *adc_qpnp);
+
+/**
+ * qpnp_adc_scale_default() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	Individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	Physical result to be stored.
+ */
+int32_t qpnp_adc_scale_default(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_pmic_therm() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Performs the AMUX out as 2mV/K and returns
+ *		the temperature in milli degC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	Individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	Physical result to be stored.
+ */
+int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_pmi_chg_temp() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. The voltage measured by HKADC is related to
+ *		the junction temperature according to
+ *		Tj = -137.67 degC * (V_adc * 2) + 382.04 degC
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	Individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	Physical result to be stored.
+ */
+int32_t qpnp_adc_scale_pmi_chg_temp(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_batt_therm() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_qrd_batt_therm() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_qrd_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_qrd_skuaa_batt_therm() - Scales the pre-calibrated digital
+ *		output of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_qrd_skuaa_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_qrd_skug_batt_therm() - Scales the pre-calibrated digital
+ *		output of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_qrd_skug_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_qrd_skuh_batt_therm() - Scales the pre-calibrated digital
+ *		output of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_qrd_skuh_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_qrd_skut1_batt_therm() - Scales the pre-calibrated digital
+ *		output of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_qrd_skut1_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_smb_batt_therm() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature in decidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_smb_batt_therm(struct qpnp_vadc_chip *dev,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_batt_id() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_batt_id(struct qpnp_vadc_chip *dev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_tdkntcg_therm() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature of the xo therm in
+ *		milidegC.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_tdkntcg_therm(struct qpnp_vadc_chip *dev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_therm_pu1() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature of the therm in degC.
+ *		It uses a mapping table computed for a 150K pull-up.
+ *		Pull-up1 is an internal pull-up on the AMUX of 150K.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_therm_pu1(struct qpnp_vadc_chip *dev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_therm_pu2() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature of the therm in degC.
+ *		It uses a mapping table computed for a 100K pull-up.
+ *		Pull-up2 is an internal pull-up on the AMUX of 100K.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *dev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_adc_scale_therm_ncp03() - Scales the pre-calibrated digital output
+ *		of an ADC to the ADC reference and compensates for the
+ *		gain and offset. Returns the temperature of the therm in degC.
+ *		It uses a mapping table computed for a NCP03WF683.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_code:	pre-calibrated digital output of the ADC.
+ * @adc_prop:	adc properties of the pm8xxx adc such as bit resolution,
+ *		reference voltage.
+ * @chan_prop:	individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @chan_rslt:	physical result to be stored.
+ */
+int32_t qpnp_adc_scale_therm_ncp03(struct qpnp_vadc_chip *dev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt);
+/**
+ * qpnp_get_vadc() - Clients need to register with the vadc using the
+ *		corresponding device instance it wants to read the channels
+ *		from. Read the bindings document on how to pass the phandle
+ *		for the corresponding vadc driver to register with.
+ * @dev:	Clients device structure
+ * @name:	Corresponding client's DT parser name. Read the DT bindings
+ *		document on how to register with the vadc
+ * @struct qpnp_vadc_chip * - On success returns the vadc device structure
+ *		pointer that needs to be used during an ADC request.
+ */
+struct qpnp_vadc_chip *qpnp_get_vadc(struct device *dev, const char *name);
+/**
+ * qpnp_adc_tm_scaler() - Performs reverse calibration.
+ * @config:	Thermal monitoring configuration.
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution and
+ *		reference voltage.
+ * @chan_prop:	Individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ */
+static inline int32_t qpnp_adc_tm_scaler(struct qpnp_adc_tm_config *tm_config,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop)
+{ return -ENXIO; }
+/**
+ * qpnp_get_vadc_gain_and_offset() - Obtains the VADC gain and offset
+ *		for absolute and ratiometric calibration.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The result in which the ADC offset and gain values are stored.
+ * @type:	The calibration type whether client needs the absolute or
+ *		ratiometric gain and offset values.
+ */
+int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *dev,
+			struct qpnp_vadc_linear_graph *param,
+			enum qpnp_adc_calib_type calib_type);
+/**
+ * qpnp_adc_scale_millidegc_pmic_voltage_thr() - Performs reverse calibration
+ *		on the low/high temperature threshold values passed by the
+ *		client. The function coverts milldegC to voltage threshold
+ *		and accounts for the corresponding channels scaling as (2mV/K).
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high temperature
+ *		values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_scale_millidegc_pmic_voltage_thr(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_btm_scaler() - Performs reverse calibration on the low/high
+ *		temperature threshold values passed by the client.
+ *		The function maps the temperature to voltage and applies
+ *		ratiometric calibration on the voltage values.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high temperature
+ *		values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_btm_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+
+/**
+ * qpnp_adc_qrd_skuh_btm_scaler() - Performs reverse calibration on the low/high
+ *		temperature threshold values passed by the client.
+ *		The function maps the temperature to voltage and applies
+ *		ratiometric calibration on the voltage values for SKUH board.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high temperature
+ *		values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_qrd_skuh_btm_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_qrd_skut1_btm_scaler() - Performs reverse calibration on the
+ *		low/high temperature threshold values passed by the client.
+ *		The function maps the temperature to voltage and applies
+ *		ratiometric calibration on the voltage values for SKUT1 board.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high temperature
+ *		values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_qrd_skut1_btm_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_tm_scale_therm_voltage_pu2() - Performs reverse calibration
+ *		and convert given temperature to voltage on supported
+ *		thermistor channels using 100k pull-up.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution,
+ *		reference voltage.
+ * @param:	The input temperature values.
+ */
+int32_t qpnp_adc_tm_scale_therm_voltage_pu2(struct qpnp_vadc_chip *dev,
+		const struct qpnp_adc_properties *adc_properties,
+				struct qpnp_adc_tm_config *param);
+/**
+ * qpnp_adc_tm_scale_therm_voltage_pu2() - Performs reverse calibration
+ *		and converts the given ADC code to temperature for
+ *		thermistor channels using 100k pull-up.
+ * @dev:	Structure device for qpnp vadc
+ * @adc_prop:	adc properties of the qpnp adc such as bit resolution,
+ *		reference voltage.
+ * @reg:	The input ADC code.
+ * @result:	The physical measurement temperature on the thermistor.
+ */
+int32_t qpnp_adc_tm_scale_voltage_therm_pu2(struct qpnp_vadc_chip *dev,
+			const struct qpnp_adc_properties *adc_prop,
+				uint32_t reg, int64_t *result);
+/**
+ * qpnp_adc_usb_scaler() - Performs reverse calibration on the low/high
+ *		voltage threshold values passed by the client.
+ *		The function applies ratiometric calibration on the
+ *		voltage values.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high voltage
+ *		threshold values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_usb_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_vbatt_rscaler() - Performs reverse calibration on the low/high
+ *		voltage threshold values passed by the client.
+ *		The function applies ratiometric calibration on the
+ *		voltage values.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high voltage
+ *		threshold values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_vadc_absolute_rthr() - Performs reverse calibration on the low/high
+ *		voltage threshold values passed by the client.
+ *		The function applies absolute calibration on the
+ *		voltage values.
+ * @dev:	Structure device for qpnp vadc
+ * @chan_prop:	Individual channel properties to compensate the i/p scaling,
+ *		slope and offset.
+ * @param:	The input parameters that contain the low/high voltage
+ *		threshold values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_vadc_absolute_rthr(struct qpnp_vadc_chip *dev,
+		const struct qpnp_vadc_chan_properties *chan_prop,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_absolute_rthr() - Performs reverse calibration on the low/high
+ *		voltage threshold values passed by the client.
+ *		The function applies absolute calibration on the
+ *		voltage values.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high voltage
+ *		threshold values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_adc_smb_btm_rscaler() - Performs reverse calibration on the low/high
+ *		temperature threshold values passed by the client.
+ *		The function maps the temperature to voltage and applies
+ *		ratiometric calibration on the voltage values.
+ * @dev:	Structure device for qpnp vadc
+ * @param:	The input parameters that contain the low/high temperature
+ *		values.
+ * @low_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ * @high_threshold: The low threshold value that needs to be updated with
+ *		the above calibrated voltage value.
+ */
+int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold);
+/**
+ * qpnp_vadc_iadc_sync_request() - Performs Voltage ADC read and
+ *		locks the peripheral. When performing simultaneous
+ *		voltage and current request the VADC peripheral is
+ *		prepared for conversion and the IADC sync conversion
+ *		is done from the IADC peripheral.
+ * @dev:	Structure device for qpnp vadc
+ * @channel:	Input channel to perform the voltage ADC read.
+ */
+int32_t qpnp_vadc_iadc_sync_request(struct qpnp_vadc_chip *dev,
+				enum qpnp_vadc_channels channel);
+/**
+ * qpnp_vadc_iadc_sync_complete_request() - Reads the ADC result and
+ *		unlocks the peripheral.
+ * @dev:	Structure device for qpnp vadc
+ * @result:	Structure pointer of type adc_chan_result
+ *		in which the ADC read results are stored.
+ */
+int32_t qpnp_vadc_iadc_sync_complete_request(struct qpnp_vadc_chip *dev,
+	enum qpnp_vadc_channels channel, struct qpnp_vadc_result *result);
+/**
+ * qpnp_vadc_sns_comp_result() - Compensate vbatt readings based on temperature
+ * @dev:	Structure device for qpnp vadc
+ * @result:	Voltage in uV that needs compensation.
+ * @is_pon_ocv: Whether the reading is from a power on OCV or not
+ */
+int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev,
+					int64_t *result, bool is_pon_ocv);
+/**
+ * qpnp_adc_get_revid_version() - Obtain the PMIC number and revision.
+ * @dev:	Structure device node.
+ * returns internal mapped PMIC number and revision id.
+ */
+int qpnp_adc_get_revid_version(struct device *dev);
+/**
+ * qpnp_vadc_channel_monitor() - Configures kernel clients a channel to
+ *		monitor the corresponding ADC channel for threshold detection.
+ *		Driver passes the high/low voltage threshold along
+ *		with the notification callback once the set thresholds
+ *		are crossed.
+ * @param:	Structure pointer of qpnp_adc_tm_btm_param type.
+ *		Clients pass the low/high temperature along with the threshold
+ *		notification callback.
+ */
+int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip,
+					struct qpnp_adc_tm_btm_param *param);
+/**
+ * qpnp_vadc_end_channel_monitor() - Disables recurring measurement mode for
+ *		VADC_USR and disables the bank.
+ * @param:	device instance for the VADC
+ */
+int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip);
+/**
+ * qpnp_vadc_calib_vref() - Read calibration channel REF_125V/VDD_VADC
+ * @dev:	Structure device for qpnp vadc
+ * @calib_type:	absolute or ratiometric calib type.
+ * returns calibration channel adc code.
+ */
+int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
+				enum qpnp_adc_calib_type calib_type,
+				int *calib_data);
+/**
+ * qpnp_vadc_calib_gnd() - Read calibration channel REF_625MV/GND_REF
+ * @dev:	Structure device for qpnp vadc
+ * @calib_type:	absolute or ratiometric calib type.
+ * returns calibration channel adc code.
+ */
+int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
+				enum qpnp_adc_calib_type calib_type,
+				int *calib_data);
+
+/**
+ * qpnp_adc_enable_voltage() - Enable LDO for HKADC
+ * @dev:	Structure device for qpnp vadc
+ * returns result of enabling the regulator interface.
+ */
+int32_t qpnp_adc_enable_voltage(struct qpnp_adc_drv *adc);
+
+/**
+ * qpnp_adc_disable_voltage() - Disable vote for HKADC LDO
+ * @dev:	Structure device for qpnp vadc
+ */
+void qpnp_adc_disable_voltage(struct qpnp_adc_drv *adc);
+
+/**
+ * qpnp_adc_free_voltage_resource() - Puts HKADC LDO
+ * @dev:	Structure device for qpnp vadc
+ */
+void qpnp_adc_free_voltage_resource(struct qpnp_adc_drv *adc);
+
+#else
+static inline int32_t qpnp_vadc_read(struct qpnp_vadc_chip *dev,
+				uint32_t channel,
+				struct qpnp_vadc_result *result)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_hc_read(struct qpnp_vadc_chip *dev,
+				uint32_t channel,
+				struct qpnp_vadc_result *result)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_conv_seq_request(struct qpnp_vadc_chip *dev,
+			enum qpnp_vadc_trigger trigger_channel,
+			enum qpnp_vadc_channels channel,
+			struct qpnp_vadc_result *result)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_default(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_pmic_therm(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_pmi_chg_temp(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_batt_therm(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_qrd_batt_therm(
+			struct qpnp_vadc_chip *vadc, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_qrd_skuaa_batt_therm(
+			struct qpnp_vadc_chip *vadc, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_qrd_skug_batt_therm(
+			struct qpnp_vadc_chip *vadc, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_qrd_skuh_batt_therm(
+			struct qpnp_vadc_chip *vdev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_qrd_skut1_batt_therm(
+			struct qpnp_vadc_chip *vdev, int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_smb_batt_therm(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_batt_id(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tdkntcg_therm(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_therm_pu1(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_therm_pu2(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_therm_ncp03(struct qpnp_vadc_chip *vadc,
+			int32_t adc_code,
+			const struct qpnp_adc_properties *adc_prop,
+			const struct qpnp_vadc_chan_properties *chan_prop,
+			struct qpnp_vadc_result *chan_rslt)
+{ return -ENXIO; }
+static inline struct qpnp_vadc_chip *qpnp_get_vadc(struct device *dev,
+							const char *name)
+{ return ERR_PTR(-ENXIO); }
+static inline int32_t qpnp_get_vadc_gain_and_offset(struct qpnp_vadc_chip *dev,
+			struct qpnp_vadc_linear_graph *param,
+			enum qpnp_adc_calib_type calib_type)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_usb_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_vbatt_rscaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_absolute_rthr(struct qpnp_vadc_chip *dev,
+		const struct qpnp_vadc_chan_properties *chan_prop,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_absolute_rthr(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_btm_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_qrd_skuh_btm_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_qrd_skut1_btm_scaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_scale_millidegc_pmic_voltage_thr(
+		struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_scale_therm_voltage_pu2(
+				struct qpnp_vadc_chip *dev,
+			const struct qpnp_adc_properties *adc_properties,
+				struct qpnp_adc_tm_config *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_scale_voltage_therm_pu2(
+				struct qpnp_vadc_chip *dev,
+			const struct qpnp_adc_properties *adc_prop,
+			uint32_t reg, int64_t *result)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_smb_btm_rscaler(struct qpnp_vadc_chip *dev,
+		struct qpnp_adc_tm_btm_param *param,
+		uint32_t *low_threshold, uint32_t *high_threshold)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_iadc_sync_request(struct qpnp_vadc_chip *dev,
+				enum qpnp_vadc_channels channel)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_iadc_sync_complete_request(
+				struct qpnp_vadc_chip *dev,
+				enum qpnp_vadc_channels channel,
+				struct qpnp_vadc_result *result)
+{ return -ENXIO; }
+static inline int32_t qpnp_vbat_sns_comp_result(struct qpnp_vadc_chip *dev,
+						int64_t *result)
+{ return -ENXIO; }
+static inline int qpnp_adc_get_revid_version(struct device *dev)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_channel_monitor(struct qpnp_vadc_chip *chip,
+					struct qpnp_adc_tm_btm_param *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_end_channel_monitor(struct qpnp_vadc_chip *chip)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_calib_vref(struct qpnp_vadc_chip *vadc,
+					enum qpnp_adc_calib_type calib_type,
+					int *calib_data)
+{ return -ENXIO; }
+static inline int32_t qpnp_vadc_calib_gnd(struct qpnp_vadc_chip *vadc,
+					enum qpnp_adc_calib_type calib_type,
+					int *calib_data)
+{ return -ENXIO; }
+
+static inline int32_t qpnp_adc_enable_voltage(struct qpnp_adc_drv *adc)
+{ return -ENXIO; }
+
+static inline void qpnp_adc_disable_voltage(struct qpnp_adc_drv *adc)
+{ return; }
+
+static inline void qpnp_adc_free_voltage_resource(struct qpnp_adc_drv *adc)
+{ return; }
+
+static inline int32_t qpnp_adc_get_devicetree_data(
+		struct platform_device *pdev, struct qpnp_adc_drv *adc_qpnp)
+{ return -ENXIO; }
+
+#endif
+
+/* Public API */
+#if defined(CONFIG_SENSORS_QPNP_ADC_CURRENT)				\
+			|| defined(CONFIG_SENSORS_QPNP_ADC_CURRENT_MODULE)
+/**
+ * qpnp_iadc_read() - Performs ADC read on the current channel.
+ * @dev:	Structure device for qpnp iadc
+ * @channel:	Input channel to perform the ADC read.
+ * @result:	Current across rsense in mA.
+ * @return:	0 on success.
+ */
+int32_t qpnp_iadc_read(struct qpnp_iadc_chip *dev,
+				enum qpnp_iadc_channels channel,
+				struct qpnp_iadc_result *result);
+/**
+ * qpnp_iadc_get_rsense() - Reads the RDS resistance value from the
+			trim registers.
+ * @dev:	Structure device for qpnp iadc
+ * @rsense:	RDS resistance in nOhms.
+ * @return:	0 on success.
+ */
+int32_t qpnp_iadc_get_rsense(struct qpnp_iadc_chip *dev, int32_t *rsense);
+/**
+ * qpnp_iadc_get_gain_and_offset() - Performs gain calibration
+ *				over 17.8571mV and offset over selected
+ *				channel. Channel can be internal rsense,
+ *				external rsense and alternate lead pair.
+ * @dev:	Structure device for qpnp iadc
+ * @result:	result structure where the gain and offset is stored of
+ *		type qpnp_iadc_calib.
+ * @return:	0 on success.
+ */
+int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_chip *dev,
+					struct qpnp_iadc_calib *result);
+/**
+ * qpnp_get_iadc() - Clients need to register with the iadc with the
+ *		corresponding device instance it wants to read the channels.
+ *		Read the bindings document on how to pass the phandle for
+ *		the corresponding vadc driver to register with.
+ * @dev:	Clients device structure
+ * @name:	Corresponding client's DT parser name. Read the DT bindings
+ *		document on how to register with the iadc
+ * @struct qpnp_iadc_chip * - On success returns the iadc device structure
+ *		pointer used everytime client makes an ADC request.
+ */
+struct qpnp_iadc_chip *qpnp_get_iadc(struct device *dev, const char *name);
+/**
+ * qpnp_iadc_vadc_sync_read() - Performs synchronous VADC and IADC read.
+ *		The api is to be used only by the BMS to perform
+ *		simultaneous VADC and IADC measurement for battery voltage
+ *		and current.
+ * @dev:	Structure device for qpnp iadc
+ * @i_channel:	Input battery current channel to perform the IADC read.
+ * @i_result:	Current across the rsense in mA.
+ * @v_channel:	Input battery voltage channel to perform VADC read.
+ * @v_result:	Voltage on the vbatt channel with units in mV.
+ * @return:	0 on success.
+ */
+int32_t qpnp_iadc_vadc_sync_read(struct qpnp_iadc_chip *dev,
+	enum qpnp_iadc_channels i_channel, struct qpnp_iadc_result *i_result,
+	enum qpnp_vadc_channels v_channel, struct qpnp_vadc_result *v_result);
+/**
+ * qpnp_iadc_calibrate_for_trim - Clients can use this API to re-calibrate
+ *		IADC. The offset and gain values are programmed in the trim
+ *		registers. The offset and the gain can be retrieved using
+ *		qpnp_iadc_get_gain_and_offset
+ * @dev:	Structure device for qpnp iadc
+ * @batfet_closed: batfet is opened or closed. The IADC chooses proper
+ *			channel (internal/external) based on batfet status
+ *			for calibration.
+ * RETURNS:	0 on success.
+ */
+int32_t qpnp_iadc_calibrate_for_trim(struct qpnp_iadc_chip *dev,
+						bool batfet_closed);
+/**
+ * qpnp_iadc_comp_result() - Compensates the result of the current based on
+ *		the gain and offset co-effients and rsense parameters.
+ * @dev:	Structure device for qpnp iadc
+ * @result:	Current value to perform the compensation.
+ * @return:	0 on success.
+ */
+int32_t qpnp_iadc_comp_result(struct qpnp_iadc_chip *dev, int64_t *result);
+/**
+ * qpnp_iadc_skip_calibration() - Clients can use this API to ask the driver
+ *				to skip iadc calibrations
+ * @dev:	Structure device for qpnp iadc
+ * @result:	0 on success and -EPROBE_DEFER when probe for the device
+ *		has not occurred.
+ */
+int qpnp_iadc_skip_calibration(struct qpnp_iadc_chip *dev);
+/**
+ * qpnp_iadc_resume_calibration() - Clients can use this API to ask the driver
+ *				to resume iadc calibrations
+ * @dev:	Structure device for qpnp iadc
+ * @result:	0 on success and -EPROBE_DEFER when probe for the device
+ *		has not occurred.
+ */
+int qpnp_iadc_resume_calibration(struct qpnp_iadc_chip *dev);
+#else
+static inline int32_t qpnp_iadc_read(struct qpnp_iadc_chip *iadc,
+	enum qpnp_iadc_channels channel, struct qpnp_iadc_result *result)
+{ return -ENXIO; }
+static inline int32_t qpnp_iadc_get_rsense(struct qpnp_iadc_chip *iadc,
+							int32_t *rsense)
+{ return -ENXIO; }
+static inline int32_t qpnp_iadc_get_gain_and_offset(struct qpnp_iadc_chip *iadc,
+				struct qpnp_iadc_calib *result)
+{ return -ENXIO; }
+static inline struct qpnp_iadc_chip *qpnp_get_iadc(struct device *dev,
+							const char *name)
+{ return ERR_PTR(-ENXIO); }
+static inline int32_t qpnp_iadc_vadc_sync_read(struct qpnp_iadc_chip *iadc,
+	enum qpnp_iadc_channels i_channel, struct qpnp_iadc_result *i_result,
+	enum qpnp_vadc_channels v_channel, struct qpnp_vadc_result *v_result)
+{ return -ENXIO; }
+static inline int32_t qpnp_iadc_calibrate_for_trim(struct qpnp_iadc_chip *iadc,
+							bool batfet_closed)
+{ return -ENXIO; }
+static inline int32_t qpnp_iadc_comp_result(struct qpnp_iadc_chip *iadc,
+						int64_t *result)
+{ return -ENXIO; }
+static inline int qpnp_iadc_skip_calibration(struct qpnp_iadc_chip *iadc)
+{ return -ENXIO; }
+static inline int qpnp_iadc_resume_calibration(struct qpnp_iadc_chip *iadc)
+{ return -ENXIO; }
+#endif
+
+/* Public API */
+#if defined(CONFIG_THERMAL_QPNP_ADC_TM)				\
+			|| defined(CONFIG_THERMAL_QPNP_ADC_TM_MODULE)
+/**
+ * qpnp_adc_tm_usbid_configure() - Configures Channel 0 of VADC_BTM to
+ *		monitor USB_ID channel using 100k internal pull-up.
+ *		USB driver passes the high/low voltage threshold along
+ *		with the notification callback once the set thresholds
+ *		are crossed.
+ * @param:	Structure pointer of qpnp_adc_tm_usbid_param type.
+ *		Clients pass the low/high voltage along with the threshold
+ *		notification callback.
+ */
+int32_t qpnp_adc_tm_usbid_configure(struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param);
+/**
+ * qpnp_adc_tm_usbid_end() - Disables the monitoring of channel 0 thats
+ *		assigned for monitoring USB_ID. Disables the low/high
+ *		threshold activation for channel 0 as well.
+ * @param:	none.
+ */
+int32_t qpnp_adc_tm_usbid_end(struct qpnp_adc_tm_chip *chip);
+/**
+ * qpnp_adc_tm_channel_measure() - Configures kernel clients a channel to
+ *		monitor the corresponding ADC channel for threshold detection.
+ *		Driver passes the high/low voltage threshold along
+ *		with the notification callback once the set thresholds
+ *		are crossed.
+ * @param:	Structure pointer of qpnp_adc_tm_btm_param type.
+ *		Clients pass the low/high temperature along with the threshold
+ *		notification callback.
+ */
+int32_t qpnp_adc_tm_channel_measure(struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param);
+/**
+ * qpnp_adc_tm_disable_chan_meas() - Disables the monitoring of channel thats
+ *		assigned for monitoring kernel clients. Disables the low/high
+ *		threshold activation for the corresponding channel.
+ * @param:	Structure pointer of qpnp_adc_tm_btm_param type.
+ *		This is used to identify the channel for which the corresponding
+ *		channels high/low threshold notification will be disabled.
+ */
+int32_t qpnp_adc_tm_disable_chan_meas(struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param);
+/**
+ * qpnp_get_adc_tm() - Clients need to register with the adc_tm using the
+ *		corresponding device instance it wants to read the channels
+ *		from. Read the bindings document on how to pass the phandle
+ *		for the corresponding adc_tm driver to register with.
+ * @name:	Corresponding client's DT parser name. Read the DT bindings
+ *		document on how to register with the vadc
+ * @struct qpnp_adc_tm_chip * - On success returns the vadc device structure
+ *		pointer that needs to be used during an ADC TM request.
+ */
+struct qpnp_adc_tm_chip *qpnp_get_adc_tm(struct device *dev, const char *name);
+#else
+static inline int32_t qpnp_adc_tm_usbid_configure(
+			struct qpnp_adc_tm_chip *chip,
+			struct qpnp_adc_tm_btm_param *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_usbid_end(struct qpnp_adc_tm_chip *chip)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_channel_measure(
+					struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param)
+{ return -ENXIO; }
+static inline int32_t qpnp_adc_tm_disable_chan_meas(
+					struct qpnp_adc_tm_chip *chip,
+					struct qpnp_adc_tm_btm_param *param)
+{ return -ENXIO; }
+static inline struct qpnp_adc_tm_chip *qpnp_get_adc_tm(struct device *dev,
+							const char *name)
+{ return ERR_PTR(-ENXIO); }
+#endif
+
+#endif
diff --git a/include/linux/qpnp/qpnp-revid.h b/include/linux/qpnp/qpnp-revid.h
new file mode 100644
index 0000000..4023e3a
--- /dev/null
+++ b/include/linux/qpnp/qpnp-revid.h
@@ -0,0 +1,231 @@
+/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __QPNP_REVID
+#define __QPNP_REVID
+
+/* Common TYPE for all PMICs */
+#define PMIC_TYPE		0x51
+
+/* PM8994 */
+#define PM8941_SUBTYPE		0x01
+
+#define PM8941_V1P0_REV1	0x00
+#define PM8941_V1P0_REV2	0x00
+#define PM8941_V1P0_REV3	0x00
+#define PM8941_V1P0_REV4	0x01
+
+#define PM8941_V2P0_REV1	0x00
+#define PM8941_V2P0_REV2	0x00
+#define PM8941_V2P0_REV3	0x00
+#define PM8941_V2P0_REV4	0x01
+
+#define PM8941_V3P0_REV1	0x00
+#define PM8941_V3P0_REV2	0x00
+#define PM8941_V3P0_REV3	0x00
+#define PM8941_V3P0_REV4	0x03
+
+#define PM8941_V3P1_REV1	0x00
+#define PM8941_V3P1_REV2	0x00
+#define PM8941_V3P1_REV3	0x01
+#define PM8941_V3P1_REV4	0x03
+
+/* PM8841 */
+#define PM8841_SUBTYPE		0x02
+
+/* PM8019 */
+#define PM8019_SUBTYPE		0x03
+
+/* PM8226 */
+#define PM8226_SUBTYPE		0x04
+
+#define PM8226_V2P2_REV1	0x00
+#define PM8226_V2P2_REV2	0x00
+#define PM8226_V2P2_REV3	0x02
+#define PM8226_V2P2_REV4	0x02
+
+#define PM8226_V2P1_REV1	0x00
+#define PM8226_V2P1_REV2	0x00
+#define PM8226_V2P1_REV3	0x01
+#define PM8226_V2P1_REV4	0x02
+
+#define PM8226_V2P0_REV1	0x00
+#define PM8226_V2P0_REV2	0x00
+#define PM8226_V2P0_REV3	0x00
+#define PM8226_V2P0_REV4	0x02
+
+#define PM8226_V1P0_REV1	0x00
+#define PM8226_V1P0_REV2	0x00
+#define PM8226_V1P0_REV3	0x00
+#define PM8226_V1P0_REV4	0x00
+
+/* PM8110 */
+#define PM8110_SUBTYPE		0x05
+
+#define PM8110_V1P0_REV1	0x00
+#define PM8110_V1P0_REV2	0x00
+#define PM8110_V1P0_REV3	0x00
+#define PM8110_V1P0_REV4	0x01
+
+#define PM8110_V1P1_REV1	0x00
+#define PM8110_V1P1_REV2	0x01
+#define PM8110_V1P1_REV3	0x00
+#define PM8110_V1P1_REV4	0x01
+
+#define PM8110_V1P3_REV1	0x00
+#define PM8110_V1P3_REV2	0x03
+#define PM8110_V1P3_REV3	0x00
+#define PM8110_V1P3_REV4	0x01
+
+#define PM8110_V2P0_REV1	0x00
+#define PM8110_V2P0_REV2	0x00
+#define PM8110_V2P0_REV3	0x00
+#define PM8110_V2P0_REV4	0x02
+
+/* PMA8084 */
+#define PMA8084_SUBTYPE		0x06
+
+/* PMI8962 */
+#define PMI8962_SUBTYPE		0x07
+
+/* PMD9635 */
+#define PMD9635_SUBTYPE		0x08
+/* PM8994 */
+#define PM8994_SUBTYPE		0x09
+
+/* PMI8994 */
+#define PMI8994_TYPE		0x51
+#define PMI8994_SUBTYPE		0x0A
+
+#define PMI8994_V1P0_REV1	0x00
+#define PMI8994_V1P0_REV2	0x00
+#define PMI8994_V1P0_REV3	0x00
+#define PMI8994_V1P0_REV4	0x01
+
+#define PMI8994_V2P0_REV1	0x00
+#define PMI8994_V2P0_REV2	0x00
+#define PMI8994_V2P0_REV3	0x00
+#define PMI8994_V2P0_REV4	0x02
+
+/* PM8916 */
+#define PM8916_SUBTYPE		0x0B
+
+#define PM8916_V1P0_REV1	0x00
+#define PM8916_V1P0_REV2	0x00
+#define PM8916_V1P0_REV3	0x00
+#define PM8916_V1P0_REV4	0x01
+
+#define PM8916_V1P1_REV1	0x00
+#define PM8916_V1P1_REV2	0x00
+#define PM8916_V1P1_REV3	0x01
+#define PM8916_V1P1_REV4	0x01
+
+#define PM8916_V2P0_REV1	0x00
+#define PM8916_V2P0_REV2	0x00
+#define PM8916_V2P0_REV3	0x00
+#define PM8916_V2P0_REV4	0x02
+
+/* PM8004 */
+#define PM8004_SUBTYPE		0x0C
+
+/* PM8909 */
+#define PM8909_SUBTYPE		0x0D
+
+#define PM8909_V1P0_REV1	0x00
+#define PM8909_V1P0_REV2	0x00
+#define PM8909_V1P0_REV3	0x00
+#define PM8909_V1P0_REV4	0x01
+
+#define PM8909_V1P1_REV1	0x00
+#define PM8909_V1P1_REV2	0x00
+#define PM8909_V1P1_REV3	0x01
+#define PM8909_V1P1_REV4	0x01
+
+/* PM2433 */
+#define PM2433_SUBTYPE		0x0E
+
+/* PMD9655 */
+#define PMD9655_SUBTYPE		0x0F
+
+/* PM8950 */
+#define PM8950_SUBTYPE		0x10
+#define PM8950_V1P0_REV4	0x01
+
+#define PM8950_V2P0_REV4	0x02
+
+/* PMI8950 */
+#define PMI8950_SUBTYPE		0x11
+
+/* PMK8001 */
+#define PMK8001_SUBTYPE		0x12
+
+/* PMI8996 */
+#define PMI8996_SUBTYPE		0x13
+
+/* PM8998 */
+#define PM8998_SUBTYPE	0x14
+
+/* PMI8998 */
+#define PMI8998_SUBTYPE	0x15
+
+/* PM660 */
+#define PM660L_SUBTYPE	0x1A
+#define PM660_SUBTYPE	0x1B
+
+#define PMI8998_V1P0_REV1	0x00
+#define PMI8998_V1P0_REV2	0x00
+#define PMI8998_V1P0_REV3	0x00
+#define PMI8998_V1P0_REV4	0x01
+
+#define PMI8998_V1P1_REV1	0x00
+#define PMI8998_V1P1_REV2	0x00
+#define PMI8998_V1P1_REV3	0x01
+#define PMI8998_V1P1_REV4	0x01
+
+#define PMI8998_V2P0_REV1	0x00
+#define PMI8998_V2P0_REV2	0x00
+#define PMI8998_V2P0_REV3	0x00
+#define PMI8998_V2P0_REV4	0x02
+
+/* PM8005 */
+#define PM8005_SUBTYPE		0x18
+
+/* PM8937 */
+#define PM8937_SUBTYPE		0x19
+
+/* PMI8937 */
+#define PMI8937_SUBTYPE		0x37
+
+/* SMB1381 */
+#define SMB1381_SUBTYPE		0x17
+
+struct pmic_revid_data {
+	u8		rev1;
+	u8		rev2;
+	u8		rev3;
+	u8		rev4;
+	u8		pmic_type;
+	u8		pmic_subtype;
+	const char	*pmic_name;
+	int		fab_id;
+};
+
+#ifdef CONFIG_QPNP_REVID
+struct pmic_revid_data *get_revid_data(struct device_node *dev_node);
+#else
+static inline
+struct pmic_revid_data *get_revid_data(struct device_node *dev_node)
+{
+	return NULL;
+}
+#endif
+#endif
diff --git a/include/linux/regulator/msm-ldo-regulator.h b/include/linux/regulator/msm-ldo-regulator.h
new file mode 100644
index 0000000..ad04e29
--- /dev/null
+++ b/include/linux/regulator/msm-ldo-regulator.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_LDO_REGULATOR_H__
+#define __MSM_LDO_REGULATOR_H__
+
+/**
+ * enum msm_ldo_supply_mode - supported operating modes by this regulator type.
+ * Use negative logic to ensure BHS mode is treated as the safe default by the
+ * the regulator framework. This is necessary since LDO mode can only be enabled
+ * when several constraints are satisfied. Consumers of this regulator are
+ * expected to request changes in operating modes through the use of
+ * regulator_allow_bypass() passing in the desired LDO supply mode.
+ * %BHS_MODE:	to select BHS as operating mode
+ * %LDO_MODE:	to select LDO as operating mode
+ */
+enum msm_ldo_supply_mode {
+	BHS_MODE = false,
+	LDO_MODE = true,
+};
+
+#endif /* __MSM_LDO_REGULATOR_H__ */
diff --git a/include/linux/regulator/qpnp-regulator.h b/include/linux/regulator/qpnp-regulator.h
new file mode 100644
index 0000000..36288c0
--- /dev/null
+++ b/include/linux/regulator/qpnp-regulator.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2012-2013, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __REGULATOR_QPNP_REGULATOR_H__
+#define __REGULATOR_QPNP_REGULATOR_H__
+
+#include <linux/regulator/machine.h>
+
+#define QPNP_REGULATOR_DRIVER_NAME "qcom,qpnp-regulator"
+
+/* Pin control enable input pins. */
+#define QPNP_REGULATOR_PIN_CTRL_ENABLE_NONE		0x00
+#define QPNP_REGULATOR_PIN_CTRL_ENABLE_EN0		0x01
+#define QPNP_REGULATOR_PIN_CTRL_ENABLE_EN1		0x02
+#define QPNP_REGULATOR_PIN_CTRL_ENABLE_EN2		0x04
+#define QPNP_REGULATOR_PIN_CTRL_ENABLE_EN3		0x08
+#define QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT	0x10
+
+/* Pin control high power mode input pins. */
+#define QPNP_REGULATOR_PIN_CTRL_HPM_NONE		0x00
+#define QPNP_REGULATOR_PIN_CTRL_HPM_EN0			0x01
+#define QPNP_REGULATOR_PIN_CTRL_HPM_EN1			0x02
+#define QPNP_REGULATOR_PIN_CTRL_HPM_EN2			0x04
+#define QPNP_REGULATOR_PIN_CTRL_HPM_EN3			0x08
+#define QPNP_REGULATOR_PIN_CTRL_HPM_SLEEP_B		0x10
+#define QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT		0x20
+
+/*
+ * Used with enable parameters to specify that hardware default register values
+ * should be left unaltered.
+ */
+#define QPNP_REGULATOR_DISABLE				0
+#define QPNP_REGULATOR_ENABLE				1
+#define QPNP_REGULATOR_USE_HW_DEFAULT			2
+
+/* Soft start strength of a voltage switch type regulator */
+enum qpnp_vs_soft_start_str {
+	QPNP_VS_SOFT_START_STR_0P05_UA,
+	QPNP_VS_SOFT_START_STR_0P25_UA,
+	QPNP_VS_SOFT_START_STR_0P55_UA,
+	QPNP_VS_SOFT_START_STR_0P75_UA,
+	QPNP_VS_SOFT_START_STR_HW_DEFAULT,
+};
+
+/* Current limit of a boost type regulator */
+enum qpnp_boost_current_limit {
+	QPNP_BOOST_CURRENT_LIMIT_300_MA,
+	QPNP_BOOST_CURRENT_LIMIT_600_MA,
+	QPNP_BOOST_CURRENT_LIMIT_900_MA,
+	QPNP_BOOST_CURRENT_LIMIT_1200_MA,
+	QPNP_BOOST_CURRENT_LIMIT_1500_MA,
+	QPNP_BOOST_CURRENT_LIMIT_1800_MA,
+	QPNP_BOOST_CURRENT_LIMIT_2100_MA,
+	QPNP_BOOST_CURRENT_LIMIT_2400_MA,
+	QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT,
+};
+
+/**
+ * struct qpnp_regulator_platform_data - qpnp-regulator initialization data
+ * @init_data:		regulator constraints
+ * @pull_down_enable:       1 = Enable output pull down resistor when the
+ *			        regulator is disabled
+ *			    0 = Disable pull down resistor
+ *			    QPNP_REGULATOR_USE_HW_DEFAULT = do not modify
+ *			        pull down state
+ * @pin_ctrl_enable:        Bit mask specifying which hardware pins should be
+ *				used to enable the regulator, if any
+ *			    Value should be an ORing of
+ *				QPNP_REGULATOR_PIN_CTRL_ENABLE_* constants.  If
+ *				the bit specified by
+ *				QPNP_REGULATOR_PIN_CTRL_ENABLE_HW_DEFAULT is
+ *				set, then pin control enable hardware registers
+ *				will not be modified.
+ * @pin_ctrl_hpm:           Bit mask specifying which hardware pins should be
+ *				used to force the regulator into high power
+ *				mode, if any
+ *			    Value should be an ORing of
+ *				QPNP_REGULATOR_PIN_CTRL_HPM_* constants.  If
+ *				the bit specified by
+ *				QPNP_REGULATOR_PIN_CTRL_HPM_HW_DEFAULT is
+ *				set, then pin control mode hardware registers
+ *				will not be modified.
+ * @system_load:            Load in uA present on regulator that is not captured
+ *				by any consumer request
+ * @enable_time:            Time in us to delay after enabling the regulator
+ * @ocp_enable:             1 = Allow over current protection (OCP) to be
+ *				enabled for voltage switch type regulators so
+ *				that they latch off automatically when over
+ *				current is detected.  OCP is enabled when in HPM
+ *				or auto mode.
+ *			    0 = Disable OCP
+ *			    QPNP_REGULATOR_USE_HW_DEFAULT = do not modify
+ *			        OCP state
+ * @ocp_irq:                IRQ number of the voltage switch OCP IRQ.  If
+ *				specified the voltage switch will be toggled off
+ *				and back on when OCP triggers in order to handle
+ *				high in-rush current.
+ * @ocp_max_retries:        Maximum number of times to try toggling a voltage
+ *				switch off and back on as a result of
+ *				consecutive over current events.
+ * @ocp_retry_delay_ms:     Time to delay in milliseconds between each
+ *				voltage switch toggle after an over current
+ *				event takes place.
+ * @boost_current_limit:    This parameter sets the current limit of boost type
+ *				regulators.  Its value should be one of
+ *				QPNP_BOOST_CURRENT_LIMIT_*.  If its value is
+ *				QPNP_BOOST_CURRENT_LIMIT_HW_DEFAULT, then the
+ *				boost current limit will be left at its default
+ *				hardware value.
+ * @soft_start_enable:      1 = Enable soft start for LDO and voltage switch
+ *				type regulators so that output voltage slowly
+ *				ramps up when the regulator is enabled
+ *			    0 = Disable soft start
+ *			    QPNP_REGULATOR_USE_HW_DEFAULT = do not modify
+ *			        soft start state
+ * @vs_soft_start_strength: This parameter sets the soft start strength for
+ *				voltage switch type regulators.  Its value
+ *				should be one of QPNP_VS_SOFT_START_STR_*.  If
+ *				its value is QPNP_VS_SOFT_START_STR_HW_DEFAULT,
+ *				then the soft start strength will be left at its
+ *				default hardware value.
+ * @auto_mode_enable:       1 = Enable automatic hardware selection of regulator
+ *				mode (HPM vs LPM).  Auto mode is not available
+ *				on boost type regulators
+ *			    0 = Disable auto mode selection
+ *			    QPNP_REGULATOR_USE_HW_DEFAULT = do not modify
+ *			        auto mode state
+ * @bypass_mode_enable:     1 = Enable bypass mode for an LDO type regulator so
+ *				that it acts like a switch and simply outputs
+ *				its input voltage
+ *			    0 = Do not enable bypass mode
+ *			    QPNP_REGULATOR_USE_HW_DEFAULT = do not modify
+ *			        bypass mode state
+ * @hpm_enable:             1 = Enable high power mode (HPM), also referred to
+ *				as NPM.  HPM consumes more ground current than
+ *				LPM, but it can source significantly higher load
+ *				current.  HPM is not available on boost type
+ *				regulators.  For voltage switch type regulators,
+ *				HPM implies that over current protection and
+ *				soft start are active all the time.  This
+ *				configuration can be overwritten by changing the
+ *				regulator's mode dynamically.
+ *			    0 = Do not enable HPM
+ *			    QPNP_REGULATOR_USE_HW_DEFAULT = do not modify
+ *			        HPM state
+ * @base_addr:              SMPI base address for the regulator peripheral
+ */
+struct qpnp_regulator_platform_data {
+	struct regulator_init_data		init_data;
+	int					pull_down_enable;
+	unsigned int				pin_ctrl_enable;
+	unsigned int				pin_ctrl_hpm;
+	int					system_load;
+	int					enable_time;
+	int					ocp_enable;
+	int					ocp_irq;
+	int					ocp_max_retries;
+	int					ocp_retry_delay_ms;
+	enum qpnp_boost_current_limit		boost_current_limit;
+	int					soft_start_enable;
+	enum qpnp_vs_soft_start_str		vs_soft_start_strength;
+	int					auto_mode_enable;
+	int					bypass_mode_enable;
+	int					hpm_enable;
+	u16					base_addr;
+};
+
+#ifdef CONFIG_REGULATOR_QPNP
+
+/**
+ * qpnp_regulator_init() - register spmi driver for qpnp-regulator
+ *
+ * This initialization function should be called in systems in which driver
+ * registration ordering must be controlled precisely.
+ */
+int __init qpnp_regulator_init(void);
+
+#else
+
+static inline int __init qpnp_regulator_init(void)
+{
+	return -ENODEV;
+}
+
+#endif /* CONFIG_REGULATOR_QPNP */
+
+#endif
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 4c1a2f1..50973f1 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -333,8 +333,6 @@ enum task_event {
 enum migrate_types {
 	GROUP_TO_RQ,
 	RQ_TO_GROUP,
-	RQ_TO_RQ,
-	GROUP_TO_GROUP,
 };
 
 #include <linux/spinlock.h>
@@ -357,13 +355,48 @@ extern int lockdep_tasklist_lock_is_held(void);
 extern void sched_init(void);
 extern void sched_init_smp(void);
 extern asmlinkage void schedule_tail(struct task_struct *prev);
-extern void init_idle(struct task_struct *idle, int cpu);
+extern void init_idle(struct task_struct *idle, int cpu, bool hotplug);
 extern void init_idle_bootup_task(struct task_struct *idle);
 
 extern cpumask_var_t cpu_isolated_map;
 
 extern int runqueue_is_locked(int cpu);
 
+#ifdef CONFIG_HOTPLUG_CPU
+extern int sched_isolate_count(const cpumask_t *mask, bool include_offline);
+extern int sched_isolate_cpu(int cpu);
+extern int sched_unisolate_cpu(int cpu);
+extern int sched_unisolate_cpu_unlocked(int cpu);
+#else
+static inline int sched_isolate_count(const cpumask_t *mask,
+				      bool include_offline)
+{
+	cpumask_t count_mask;
+
+	if (include_offline)
+		cpumask_andnot(&count_mask, mask, cpu_online_mask);
+	else
+		return 0;
+
+	return cpumask_weight(&count_mask);
+}
+
+static inline int sched_isolate_cpu(int cpu)
+{
+	return 0;
+}
+
+static inline int sched_unisolate_cpu(int cpu)
+{
+	return 0;
+}
+
+static inline int sched_unisolate_cpu_unlocked(int cpu)
+{
+	return 0;
+}
+#endif
+
 #if defined(CONFIG_SMP) && defined(CONFIG_NO_HZ_COMMON)
 extern void nohz_balance_enter_idle(int cpu);
 extern void set_cpu_sd_state_idle(void);
@@ -419,6 +452,9 @@ extern int proc_dowatchdog_thresh(struct ctl_table *table, int write,
 extern unsigned int  softlockup_panic;
 extern unsigned int  hardlockup_panic;
 void lockup_detector_init(void);
+extern void watchdog_enable(unsigned int cpu);
+extern void watchdog_disable(unsigned int cpu);
+extern bool watchdog_configured(unsigned int cpu);
 #else
 static inline void touch_softlockup_watchdog_sched(void)
 {
@@ -435,6 +471,20 @@ static inline void touch_all_softlockup_watchdogs(void)
 static inline void lockup_detector_init(void)
 {
 }
+static inline void watchdog_enable(unsigned int cpu)
+{
+}
+static inline void watchdog_disable(unsigned int cpu)
+{
+}
+static inline bool watchdog_configured(unsigned int cpu)
+{
+	/*
+	 * Predend the watchdog is always configured.
+	 * We will be waiting for the watchdog to be enabled in core isolation
+	 */
+	return true;
+}
 #endif
 
 #ifdef CONFIG_DETECT_HUNG_TASK
@@ -1378,11 +1428,15 @@ struct ravg {
 	 * sysctl_sched_ravg_hist_size windows. 'demand' could drive frequency
 	 * demand for tasks.
 	 *
-	 * 'curr_window' represents task's contribution to cpu busy time
-	 * statistics (rq->curr_runnable_sum) in current window
+	 * 'curr_window_cpu' represents task's contribution to cpu busy time on
+	 * various CPUs in the current window
 	 *
-	 * 'prev_window' represents task's contribution to cpu busy time
-	 * statistics (rq->prev_runnable_sum) in previous window
+	 * 'prev_window_cpu' represents task's contribution to cpu busy time on
+	 * various CPUs in the previous window
+	 *
+	 * 'curr_window' represents the sum of all entries in curr_window_cpu
+	 *
+	 * 'prev_window' represents the sum of all entries in prev_window_cpu
 	 *
 	 * 'pred_demand' represents task's current predicted cpu busy time
 	 *
@@ -1392,7 +1446,9 @@ struct ravg {
 	u64 mark_start;
 	u32 sum, demand;
 	u32 sum_history[RAVG_HIST_SIZE_MAX];
+	u32 *curr_window_cpu, *prev_window_cpu;
 	u32 curr_window, prev_window;
+	u64 curr_burst, avg_burst, avg_sleep_time;
 	u16 active_windows;
 	u32 pred_demand;
 	u8 busy_buckets[NUM_BUSY_BUCKETS];
@@ -2527,7 +2583,10 @@ struct cpu_cycle_counter_cb {
 	u64 (*get_cpu_cycle_counter)(int cpu);
 };
 
+#define MAX_NUM_CGROUP_COLOC_ID	20
+
 #ifdef CONFIG_SCHED_HMP
+extern void free_task_load_ptrs(struct task_struct *p);
 extern int sched_set_window(u64 window_start, unsigned int window_size);
 extern unsigned long sched_get_busy(int cpu);
 extern void sched_get_cpus_busy(struct sched_load *busy,
@@ -2540,6 +2599,8 @@ extern int sched_set_static_cpu_pwr_cost(int cpu, unsigned int cost);
 extern unsigned int sched_get_static_cpu_pwr_cost(int cpu);
 extern int sched_set_static_cluster_pwr_cost(int cpu, unsigned int cost);
 extern unsigned int sched_get_static_cluster_pwr_cost(int cpu);
+extern int sched_set_cluster_wake_idle(int cpu, unsigned int wake_idle);
+extern unsigned int sched_get_cluster_wake_idle(int cpu);
 extern int sched_update_freq_max_load(const cpumask_t *cpumask);
 extern void sched_update_cpu_freq_min_max(const cpumask_t *cpus,
 							u32 fmin, u32 fmax);
@@ -2553,6 +2614,8 @@ extern int sched_set_group_id(struct task_struct *p, unsigned int group_id);
 extern unsigned int sched_get_group_id(struct task_struct *p);
 
 #else /* CONFIG_SCHED_HMP */
+static inline void free_task_load_ptrs(struct task_struct *p) { }
+
 static inline u64 sched_ktime_clock(void)
 {
 	return 0;
diff --git a/include/linux/sched/core_ctl.h b/include/linux/sched/core_ctl.h
new file mode 100644
index 0000000..98d7cb3
--- /dev/null
+++ b/include/linux/sched/core_ctl.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CORE_CTL_H
+#define __CORE_CTL_H
+
+#ifdef CONFIG_SCHED_CORE_CTL
+void core_ctl_check(u64 wallclock);
+int core_ctl_set_boost(bool boost);
+#else
+static inline void core_ctl_check(u64 wallclock) {}
+static inline int core_ctl_set_boost(bool boost)
+{
+	return 0;
+}
+#endif
+#endif
diff --git a/include/linux/sched/sysctl.h b/include/linux/sched/sysctl.h
index 6726f05..00101b3 100644
--- a/include/linux/sched/sysctl.h
+++ b/include/linux/sched/sysctl.h
@@ -18,11 +18,19 @@ extern unsigned int sysctl_sched_latency;
 extern unsigned int sysctl_sched_min_granularity;
 extern unsigned int sysctl_sched_wakeup_granularity;
 extern unsigned int sysctl_sched_child_runs_first;
-extern unsigned int sysctl_sched_wake_to_idle;
 
 #ifdef CONFIG_SCHED_HMP
+
+enum freq_reporting_policy {
+	FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK,
+	FREQ_REPORT_CPU_LOAD,
+	FREQ_REPORT_TOP_TASK,
+	FREQ_REPORT_INVALID_POLICY
+};
+
 extern int sysctl_sched_freq_inc_notify;
 extern int sysctl_sched_freq_dec_notify;
+extern unsigned int sysctl_sched_freq_reporting_policy;
 extern unsigned int sysctl_sched_window_stats_policy;
 extern unsigned int sysctl_sched_ravg_hist_size;
 extern unsigned int sysctl_sched_cpu_high_irqload;
@@ -31,18 +39,22 @@ extern unsigned int sysctl_sched_spill_nr_run;
 extern unsigned int sysctl_sched_spill_load_pct;
 extern unsigned int sysctl_sched_upmigrate_pct;
 extern unsigned int sysctl_sched_downmigrate_pct;
+extern unsigned int sysctl_sched_group_upmigrate_pct;
+extern unsigned int sysctl_sched_group_downmigrate_pct;
 extern unsigned int sysctl_early_detection_duration;
 extern unsigned int sysctl_sched_boost;
 extern unsigned int sysctl_sched_small_wakee_task_load_pct;
 extern unsigned int sysctl_sched_big_waker_task_load_pct;
 extern unsigned int sysctl_sched_select_prev_cpu_us;
-extern unsigned int sysctl_sched_enable_colocation;
 extern unsigned int sysctl_sched_restrict_cluster_spill;
 extern unsigned int sysctl_sched_new_task_windows;
 extern unsigned int sysctl_sched_pred_alert_freq;
 extern unsigned int sysctl_sched_freq_aggregate;
 extern unsigned int sysctl_sched_enable_thread_grouping;
 extern unsigned int sysctl_sched_freq_aggregate_threshold_pct;
+extern unsigned int sysctl_sched_prefer_sync_wakee_to_waker;
+extern unsigned int sysctl_sched_short_burst;
+extern unsigned int sysctl_sched_short_sleep;
 #endif /* CONFIG_SCHED_HMP */
 
 enum sched_tunable_scaling {
@@ -94,6 +106,22 @@ extern int sysctl_sched_rt_runtime;
 extern unsigned int sysctl_sched_cfs_bandwidth_slice;
 #endif
 
+#ifdef CONFIG_SCHED_TUNE
+extern unsigned int sysctl_sched_cfs_boost;
+int sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
+				   void __user *buffer, size_t *length,
+				   loff_t *ppos);
+static inline unsigned int get_sysctl_sched_cfs_boost(void)
+{
+	return sysctl_sched_cfs_boost;
+}
+#else
+static inline unsigned int get_sysctl_sched_cfs_boost(void)
+{
+	return 0;
+}
+#endif
+
 #ifdef CONFIG_SCHED_AUTOGROUP
 extern unsigned int sysctl_sched_autogroup_enabled;
 #endif
diff --git a/include/linux/slimbus/slimbus.h b/include/linux/slimbus/slimbus.h
new file mode 100644
index 0000000..f1b1a7f
--- /dev/null
+++ b/include/linux/slimbus/slimbus.h
@@ -0,0 +1,1213 @@
+/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_SLIMBUS_H
+#define _LINUX_SLIMBUS_H
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/mod_devicetable.h>
+
+/* Interfaces between SLIMbus manager drivers and SLIMbus infrastructure. */
+
+extern struct bus_type slimbus_type;
+
+/* Standard values per SLIMbus spec needed by controllers and devices */
+#define SLIM_CL_PER_SUPERFRAME		6144
+#define SLIM_CL_PER_SUPERFRAME_DIV8	(SLIM_CL_PER_SUPERFRAME >> 3)
+#define SLIM_MAX_TXNS			256
+#define SLIM_MAX_CLK_GEAR		10
+#define SLIM_MIN_CLK_GEAR		1
+#define SLIM_CL_PER_SL			4
+#define SLIM_SL_PER_SUPERFRAME		(SLIM_CL_PER_SUPERFRAME >> 2)
+#define SLIM_FRM_SLOTS_PER_SUPERFRAME	16
+#define SLIM_GDE_SLOTS_PER_SUPERFRAME	2
+
+/*
+ * SLIMbus message types. Related to interpretation of message code.
+ * Values are defined in Table 32 (slimbus spec 1.01.01)
+ */
+#define SLIM_MSG_MT_CORE			0x0
+#define SLIM_MSG_MT_DEST_REFERRED_CLASS		0x1
+#define SLIM_MSG_MT_DEST_REFERRED_USER		0x2
+#define SLIM_MSG_MT_SRC_REFERRED_CLASS		0x5
+#define SLIM_MSG_MT_SRC_REFERRED_USER		0x6
+
+/*
+ * SLIMbus core type Message Codes.
+ * Values are defined in Table 65 (slimbus spec 1.01.01)
+ */
+/* Device management messages */
+#define SLIM_MSG_MC_REPORT_PRESENT               0x1
+#define SLIM_MSG_MC_ASSIGN_LOGICAL_ADDRESS       0x2
+#define SLIM_MSG_MC_RESET_DEVICE                 0x4
+#define SLIM_MSG_MC_CHANGE_LOGICAL_ADDRESS       0x8
+#define SLIM_MSG_MC_CHANGE_ARBITRATION_PRIORITY  0x9
+#define SLIM_MSG_MC_REQUEST_SELF_ANNOUNCEMENT    0xC
+#define SLIM_MSG_MC_REPORT_ABSENT                0xF
+
+/* Data channel management messages */
+#define SLIM_MSG_MC_CONNECT_SOURCE               0x10
+#define SLIM_MSG_MC_CONNECT_SINK                 0x11
+#define SLIM_MSG_MC_DISCONNECT_PORT              0x14
+#define SLIM_MSG_MC_CHANGE_CONTENT               0x18
+
+/* Information management messages */
+#define SLIM_MSG_MC_REQUEST_INFORMATION          0x20
+#define SLIM_MSG_MC_REQUEST_CLEAR_INFORMATION    0x21
+#define SLIM_MSG_MC_REPLY_INFORMATION            0x24
+#define SLIM_MSG_MC_CLEAR_INFORMATION            0x28
+#define SLIM_MSG_MC_REPORT_INFORMATION           0x29
+
+/* Reconfiguration messages */
+#define SLIM_MSG_MC_BEGIN_RECONFIGURATION        0x40
+#define SLIM_MSG_MC_NEXT_ACTIVE_FRAMER           0x44
+#define SLIM_MSG_MC_NEXT_SUBFRAME_MODE           0x45
+#define SLIM_MSG_MC_NEXT_CLOCK_GEAR              0x46
+#define SLIM_MSG_MC_NEXT_ROOT_FREQUENCY          0x47
+#define SLIM_MSG_MC_NEXT_PAUSE_CLOCK             0x4A
+#define SLIM_MSG_MC_NEXT_RESET_BUS               0x4B
+#define SLIM_MSG_MC_NEXT_SHUTDOWN_BUS            0x4C
+#define SLIM_MSG_MC_NEXT_DEFINE_CHANNEL          0x50
+#define SLIM_MSG_MC_NEXT_DEFINE_CONTENT          0x51
+#define SLIM_MSG_MC_NEXT_ACTIVATE_CHANNEL        0x54
+#define SLIM_MSG_MC_NEXT_DEACTIVATE_CHANNEL      0x55
+#define SLIM_MSG_MC_NEXT_REMOVE_CHANNEL          0x58
+#define SLIM_MSG_MC_RECONFIGURE_NOW              0x5F
+
+/*
+ * Clock pause flag to indicate that the reconfig message
+ * corresponds to clock pause sequence
+ */
+#define SLIM_MSG_CLK_PAUSE_SEQ_FLG		(1U << 8)
+
+/* Value management messages */
+#define SLIM_MSG_MC_REQUEST_VALUE                0x60
+#define SLIM_MSG_MC_REQUEST_CHANGE_VALUE         0x61
+#define SLIM_MSG_MC_REPLY_VALUE                  0x64
+#define SLIM_MSG_MC_CHANGE_VALUE                 0x68
+
+/* Clock pause values defined in Table 66 (slimbus spec 1.01.01) */
+#define SLIM_CLK_FAST				0
+#define SLIM_CLK_CONST_PHASE			1
+#define SLIM_CLK_UNSPECIFIED			2
+
+struct slim_controller;
+struct slim_device;
+
+/* Destination type Values defined in Table 33 (slimbus spec 1.01.01) */
+#define SLIM_MSG_DEST_LOGICALADDR	0
+#define SLIM_MSG_DEST_ENUMADDR		1
+#define	SLIM_MSG_DEST_BROADCAST		3
+
+/*
+ * @start_offset: Specifies starting offset in information/value element map
+ * @num_bytes: Can be 1, 2, 3, 4, 6, 8, 12, 16 per spec. This ensures that the
+ *	message will fit in the 40-byte message limit and the slicesize can be
+ *	compatible with values in table 21 (slimbus spec 1.01.01)
+ * @comp: Completion to indicate end of message-transfer. Used if client wishes
+ *	to use the API asynchronously.
+ */
+struct slim_ele_access {
+	u16			start_offset;
+	u8			num_bytes;
+	struct completion	*comp;
+};
+
+/*
+ * struct slim_framer - Represents Slimbus framer.
+ * Every controller may have multiple framers.
+ * Manager is responsible for framer hand-over.
+ * @e_addr: 6 byte Elemental address of the framer.
+ * @rootfreq: Root Frequency at which the framer can run. This is maximum
+ *	frequency (clock gear 10 per slimbus spec) at which the bus can operate.
+ * @superfreq: Superframes per root frequency. Every frame is 6144 cells (bits)
+ *	per slimbus specification.
+ */
+struct slim_framer {
+	u8	e_addr[6];
+	int	rootfreq;
+	int	superfreq;
+};
+#define to_slim_framer(d) container_of(d, struct slim_framer, dev)
+
+/*
+ * struct slim_addrt: slimbus address used internally by the slimbus framework.
+ * @valid: If the device is still there or if the address can be reused.
+ * @eaddr: 6-bytes-long elemental address
+ * @laddr: It is possible that controller will set a predefined logical address
+ *	rather than the one assigned by framework. (i.e. logical address may
+ *	not be same as index into this table). This entry will store the
+ *	logical address value for this enumeration address.
+ */
+struct slim_addrt {
+	bool	valid;
+	u8	eaddr[6];
+	u8	laddr;
+};
+
+/*
+ * struct slim_val_inf: slimbus value/information element transaction
+ * @start_offset: Specifies starting offset in information/value element map
+ * @num_bytes: number of bytes to be read/written
+ * @wbuf: buffer if this transaction has 'write' component in it
+ * @rbuf: buffer if this transaction has 'read' component in it
+ */
+struct slim_val_inf {
+	u16 start_offset;
+	u8 num_bytes;
+	u8 *wbuf;
+	u8 *rbuf;
+};
+
+/*
+ * struct slim_msg_txn: Message to be sent by the controller.
+ * Linux framework uses this structure with drivers implementing controller.
+ * This structure has packet header, payload and buffer to be filled (if any)
+ * For the header information, refer to Table 34-36.
+ * @rl: Header field. remaining length.
+ * @mt: Header field. Message type.
+ * @mc: Header field. LSB is message code for type mt. Framework will set MSB to
+ *	SLIM_MSG_CLK_PAUSE_SEQ_FLG in case "mc" in the reconfiguration sequence
+ *	is for pausing the clock.
+ * @dt: Header field. Destination type.
+ * @ec: Element size. Used for elemental access APIs.
+ * @len: Length of payload. (excludes ec)
+ * @tid: Transaction ID. Used for messages expecting response.
+ *	(e.g. relevant for mc = SLIM_MSG_MC_REQUEST_INFORMATION)
+ * @la: Logical address of the device this message is going to.
+ *	(Not used when destination type is broadcast.)
+ * @async: If this transaction is async
+ * @rbuf: Buffer to be populated by controller when response is received.
+ * @wbuf: Payload of the message. (e.g. channel number for DATA channel APIs)
+ * @comp: Completion structure. Used by controller to notify response.
+ *	(Field is relevant when tid is used)
+ */
+struct slim_msg_txn {
+	u8			rl;
+	u8			mt;
+	u16			mc;
+	u8			dt;
+	u16			ec;
+	u8			len;
+	u8			tid;
+	u8			la;
+	bool			async;
+	u8			*rbuf;
+	const u8		*wbuf;
+	struct completion	*comp;
+};
+
+/* Internal port state used by slimbus framework to manage data-ports */
+enum slim_port_state {
+	SLIM_P_FREE,
+	SLIM_P_UNCFG,
+	SLIM_P_CFG,
+};
+
+/*
+ * enum slim_port_req: Request port type by user through APIs to manage ports
+ * User can request default, half-duplex or port to be used in multi-channel
+ * configuration. Default indicates a simplex port.
+ */
+enum slim_port_req {
+	SLIM_REQ_DEFAULT,
+	SLIM_REQ_HALF_DUP,
+	SLIM_REQ_MULTI_CH,
+};
+
+/*
+ * enum slim_port_opts: Port options requested.
+ * User can request no configuration, packed data, and/or MSB aligned data port
+ */
+enum slim_port_opts {
+	SLIM_OPT_NONE = 0,
+	SLIM_OPT_NO_PACK = 1U,
+	SLIM_OPT_ALIGN_MSB = 1U << 1,
+};
+
+/* enum slim_port_flow: Port flow type (inbound/outbound). */
+enum slim_port_flow {
+	SLIM_SRC,
+	SLIM_SINK,
+};
+
+/* enum slim_port_err: Port errors */
+enum slim_port_err {
+	SLIM_P_INPROGRESS,
+	SLIM_P_OVERFLOW,
+	SLIM_P_UNDERFLOW,
+	SLIM_P_DISCONNECT,
+	SLIM_P_NOT_OWNED,
+};
+
+/*
+ * struct slim_port_cfg: Port config for the manager port
+ * port_opts: port options (bit-map) for this port
+ * watermark: watermark level set for this port
+ */
+struct slim_port_cfg {
+	u32 port_opts;
+	u32 watermark;
+};
+
+/*
+ * struct slim_port: Internal structure used by framework to manage ports
+ * @err: Port error if any for this port. Refer to enum above.
+ * @state: Port state. Refer to enum above.
+ * @req: Port request for this port.
+ * @cfg: Port configuration for this port.
+ * @flow: Flow type of this port.
+ * @ch: Channel association of this port.
+ * @xcomp: Completion to indicate error, data transfer done event.
+ * @ctrl: Controller to which this port belongs to. This is useful to associate
+ *	port with the SW since port hardware interrupts may only contain port
+ *	information.
+ */
+struct slim_port {
+	enum slim_port_err	err;
+	enum slim_port_state	state;
+	enum slim_port_req	req;
+	struct slim_port_cfg	cfg;
+	enum slim_port_flow	flow;
+	struct slim_ch		*ch;
+	struct completion	*xcomp;
+	struct slim_controller	*ctrl;
+};
+
+/*
+ * enum slim_ch_state: Channel state of a channel.
+ * Channel transition happens from free-to-allocated-to-defined-to-pending-
+ * active-to-active.
+ * Once active, channel can be removed or suspended. Suspended channels are
+ * still scheduled, but data transfer doesn't happen.
+ * Removed channels are not deallocated until dealloc_ch API is used.
+ * Deallocation reset channel state back to free.
+ * Removed channels can be defined with different parameters.
+ */
+enum slim_ch_state {
+	SLIM_CH_FREE,
+	SLIM_CH_ALLOCATED,
+	SLIM_CH_DEFINED,
+	SLIM_CH_PENDING_ACTIVE,
+	SLIM_CH_ACTIVE,
+	SLIM_CH_SUSPENDED,
+	SLIM_CH_PENDING_REMOVAL,
+};
+
+/*
+ * enum slim_ch_proto: Channel protocol used by the channel.
+ * Hard Isochronous channel is not scheduled if current frequency doesn't allow
+ * the channel to be run without flow-control.
+ * Auto isochronous channel will be scheduled as hard-isochronous or push-pull
+ * depending on current bus frequency.
+ * Currently, Push-pull or async or extended channels are not supported.
+ * For more details, refer to slimbus spec
+ */
+enum slim_ch_proto {
+	SLIM_HARD_ISO,
+	SLIM_AUTO_ISO,
+	SLIM_PUSH,
+	SLIM_PULL,
+	SLIM_ASYNC_SMPLX,
+	SLIM_ASYNC_HALF_DUP,
+	SLIM_EXT_SMPLX,
+	SLIM_EXT_HALF_DUP,
+};
+
+/*
+ * enum slim_ch_rate: Most commonly used frequency rate families.
+ * Use 1HZ for push-pull transport.
+ * 4KHz and 11.025KHz are most commonly used in audio applications.
+ * Typically, slimbus runs at frequencies to support channels running at 4KHz
+ * and/or 11.025KHz isochronously.
+ */
+enum slim_ch_rate {
+	SLIM_RATE_1HZ,
+	SLIM_RATE_4000HZ,
+	SLIM_RATE_11025HZ,
+};
+
+/*
+ * enum slim_ch_coeff: Coefficient of a channel used internally by framework.
+ * Coefficient is applicable to channels running isochronously.
+ * Coefficient is calculated based on channel rate multiplier.
+ * (If rate multiplier is power of 2, it's coeff.1 channel. Otherwise it's
+ * coeff.3 channel.
+ */
+enum slim_ch_coeff {
+	SLIM_COEFF_1,
+	SLIM_COEFF_3,
+};
+
+/*
+ * enum slim_ch_control: Channel control.
+ * Activate will schedule channel and/or group of channels in the TDM frame.
+ * Suspend will keep the schedule but data-transfer won't happen.
+ * Remove will remove the channel/group from the TDM frame.
+ */
+enum slim_ch_control {
+	SLIM_CH_ACTIVATE,
+	SLIM_CH_SUSPEND,
+	SLIM_CH_REMOVE,
+};
+
+/* enum slim_ch_dataf: Data format per table 60 from slimbus spec 1.01.01 */
+enum slim_ch_dataf {
+	SLIM_CH_DATAF_NOT_DEFINED = 0,
+	SLIM_CH_DATAF_LPCM_AUDIO = 1,
+	SLIM_CH_DATAF_IEC61937_COMP_AUDIO = 2,
+	SLIM_CH_DATAF_PACKED_PDM_AUDIO = 3,
+};
+
+/* enum slim_ch_auxf: Auxiliary field format per table 59 from slimbus spec */
+enum slim_ch_auxf {
+	SLIM_CH_AUXF_NOT_APPLICABLE = 0,
+	SLIM_CH_AUXF_ZCUV_TUNNEL_IEC60958 = 1,
+	SLIM_CH_USER_DEFINED = 0xF,
+};
+
+/*
+ * struct slim_ch: Channel structure used externally by users of channel APIs.
+ * @prot: Desired slimbus protocol.
+ * @baser: Desired base rate. (Typical isochronous rates are: 4KHz, or 11.025KHz
+ * @dataf: Data format.
+ * @auxf: Auxiliary format.
+ * @ratem: Channel rate multiplier. (e.g. 48KHz channel will have 4KHz base rate
+ *	and 12 as rate multiplier.
+ * @sampleszbits: Sample size in bits.
+ */
+struct slim_ch {
+	enum slim_ch_proto	prot;
+	enum slim_ch_rate	baser;
+	enum slim_ch_dataf	dataf;
+	enum slim_ch_auxf	auxf;
+	u32			ratem;
+	u32			sampleszbits;
+};
+
+/*
+ * struct slim_ich: Internal channel structure used by slimbus framework.
+ * @prop: structure passed by the client.
+ * @coeff: Coefficient of this channel.
+ * @state: Current state of the channel.
+ * @nextgrp: If this channel is part of group, next channel in this group.
+ * @prrate: Presence rate of this channel (per table 62 of the spec)
+ * @offset: Offset of this channel in the superframe.
+ * @newoff: Used during scheduling to hold temporary new offset until the offset
+ *	is accepted/rejected by slimbus reconfiguration.
+ * @interval: Interval of this channel per superframe.
+ * @newintr: Used during scheduling to new interval temporarily.
+ * @seglen: Segment length of this channel.
+ * @rootexp: root exponent of this channel. Rate can be found using rootexp and
+ *	coefficient. Used during scheduling.
+ * @srch: Source port used by this channel.
+ * @sinkh: Sink ports used by this channel.
+ * @nsink: number of sink ports used by this channel.
+ * @chan: Channel number sent on hardware lines for this channel. May not be
+ *	equal to array-index into chans if client requested to use number beyond
+ *	channel-array for the controller.
+ * @ref: Reference number to keep track of how many clients (upto 2) are using
+ *	this channel.
+ * @def: Used to keep track of how many times the channel definition is sent
+ *	to hardware and this will decide if channel-remove can be sent for the
+ *	channel. Channel definition may be sent upto twice (once per producer
+ *	and once per consumer). Channel removal should be sent only once to
+ *	avoid clients getting underflow/overflow errors.
+ */
+struct slim_ich {
+	struct slim_ch		prop;
+	enum slim_ch_coeff	coeff;
+	enum slim_ch_state	state;
+	u16			nextgrp;
+	u32			prrate;
+	u32			offset;
+	u32			newoff;
+	u32			interval;
+	u32			newintr;
+	u32			seglen;
+	u8			rootexp;
+	u32			srch;
+	u32			*sinkh;
+	int			nsink;
+	u8			chan;
+	int			ref;
+	int			def;
+};
+
+/*
+ * struct slim_sched: Framework uses this structure internally for scheduling.
+ * @chc3: Array of all active coeffient 3 channels.
+ * @num_cc3: Number of active coeffient 3 channels.
+ * @chc1: Array of all active coeffient 1 channels.
+ * @num_cc1: Number of active coeffient 1 channels.
+ * @subfrmcode: Current subframe-code used by TDM. This is decided based on
+ *	requested message bandwidth and current channels scheduled.
+ * @usedslots: Slots used by all active channels.
+ * @msgsl: Slots used by message-bandwidth.
+ * @pending_msgsl: Used to store pending request of message bandwidth (in slots)
+ *	until the scheduling is accepted by reconfiguration.
+ * @m_reconf: This mutex is held until current reconfiguration (data channel
+ *	scheduling, message bandwidth reservation) is done. Message APIs can
+ *	use the bus concurrently when this mutex is held since elemental access
+ *	messages can be sent on the bus when reconfiguration is in progress.
+ * @slots: Used for debugging purposes to debug/verify current schedule in TDM.
+ */
+struct slim_sched {
+	struct slim_ich	**chc3;
+	int		num_cc3;
+	struct slim_ich	**chc1;
+	int		num_cc1;
+	u32		subfrmcode;
+	u32		usedslots;
+	u32		msgsl;
+	u32		pending_msgsl;
+	struct mutex	m_reconf;
+	u8		*slots;
+};
+
+/*
+ * enum slim_clk_state: Slimbus controller's clock state used internally for
+ *	maintaining current clock state.
+ * @SLIM_CLK_ACTIVE: Slimbus clock is active
+ * @SLIM_CLK_PAUSE_FAILED: Slimbus controlled failed to go in clock pause.
+ *	Hardware-wise, this state is same as active but controller will wait on
+ *	completion before making transition to SLIM_CLK_ACTIVE in framework
+ * @SLIM_CLK_ENTERING_PAUSE: Slimbus clock pause sequence is being sent on the
+ *	bus. If this succeeds, state changes to SLIM_CLK_PAUSED. If the
+ *	transition fails, state changes to SLIM_CLK_PAUSE_FAILED
+ * @SLIM_CLK_PAUSED: Slimbus controller clock has paused.
+ */
+enum slim_clk_state {
+	SLIM_CLK_ACTIVE,
+	SLIM_CLK_ENTERING_PAUSE,
+	SLIM_CLK_PAUSE_FAILED,
+	SLIM_CLK_PAUSED,
+};
+/*
+ * struct slim_controller: Represents manager for a SlimBUS
+ *				(similar to 'master' on I2C)
+ * @dev: Device interface to this driver
+ * @nr: Board-specific number identifier for this controller/bus
+ * @list: Link with other slimbus controllers
+ * @name: Name for this controller
+ * @clkgear: Current clock gear in which this bus is running
+ * @min_cg: Minimum clock gear supported by this controller (default value: 1)
+ * @max_cg: Maximum clock gear supported by this controller (default value: 10)
+ * @clk_state: Controller's clock state from enum slim_clk_state
+ * @pause_comp: Signals completion of clock pause sequence. This is useful when
+ *	client tries to call slimbus transaction when controller may be entering
+ *	clock pause.
+ * @a_framer: Active framer which is clocking the bus managed by this controller
+ * @m_ctrl: Mutex protecting controller data structures (ports, channels etc)
+ * @addrt: Logical address table
+ * @num_dev: Number of active slimbus slaves on this bus
+ * @devs: List of devices on this controller
+ * @wq: Workqueue per controller used to notify devices when they report present
+ * @txnt: Table of transactions having transaction ID
+ * @last_tid: size of the table txnt (can't grow beyond 256 since TID is 8-bits)
+ * @ports: Ports associated with this controller
+ * @nports: Number of ports supported by the controller
+ * @chans: Channels associated with this controller
+ * @nchans: Number of channels supported
+ * @reserved: Reserved channels that controller wants to use internally
+ *		Clients will be assigned channel numbers after this number
+ * @sched: scheduler structure used by the controller
+ * @dev_released: completion used to signal when sysfs has released this
+ *	controller so that it can be deleted during shutdown
+ * @xfer_msg: Transfer a message on this controller (this can be a broadcast
+ *	control/status message like data channel setup, or a unicast message
+ *	like value element read/write.
+ * @set_laddr: Setup logical address at laddr for the slave with elemental
+ *	address e_addr. Drivers implementing controller will be expected to
+ *	send unicast message to this device with its logical address.
+ * @allocbw: Controller can override default reconfiguration and channel
+ *	scheduling algorithm.
+ * @get_laddr: It is possible that controller needs to set fixed logical
+ *	address table and get_laddr can be used in that case so that controller
+ *	can do this assignment.
+ * @wakeup: This function pointer implements controller-specific procedure
+ *	to wake it up from clock-pause. Framework will call this to bring
+ *	the controller out of clock pause.
+ * @alloc_port: Allocate a port and make it ready for data transfer. This is
+ *	called by framework to make sure controller can take necessary steps
+ *	to initialize its port
+ * @dealloc_port: Counter-part of alloc_port. This is called by framework so
+ *	that controller can free resources associated with this port
+ * @framer_handover: If this controller has multiple framers, this API will
+ *	be called to switch between framers if controller desires to change
+ *	the active framer.
+ * @port_xfer: Called to schedule a transfer on port pn. iobuf is physical
+ *	address and the buffer may have to be DMA friendly since data channels
+ *	will be using data from this buffers without SW intervention.
+ * @port_xfer_status: Called by framework when client calls get_xfer_status
+ *	API. Returns how much buffer is actually processed and the port
+ *	errors (e.g. overflow/underflow) if any.
+ * @xfer_user_msg: Send user message to specified logical address. Underlying
+ *	controller has to support sending user messages. Returns error if any.
+ * @xfer_bulk_wr: Send bulk of write messages to specified logical address.
+ *	Underlying controller has to support this. Typically useful to transfer
+ *	messages to download firmware, or messages where strict ordering for
+ *	slave is necessary
+ */
+struct slim_controller {
+	struct device		dev;
+	unsigned int		nr;
+	struct list_head	list;
+	char			name[SLIMBUS_NAME_SIZE];
+	int			clkgear;
+	int			min_cg;
+	int			max_cg;
+	enum slim_clk_state	clk_state;
+	struct completion	pause_comp;
+	struct slim_framer	*a_framer;
+	struct mutex		m_ctrl;
+	struct slim_addrt	*addrt;
+	u8			num_dev;
+	struct list_head	devs;
+	struct workqueue_struct *wq;
+	struct slim_msg_txn	*txnt[SLIM_MAX_TXNS];
+	u8			last_tid;
+	spinlock_t		txn_lock;
+	struct slim_port	*ports;
+	int			nports;
+	struct slim_ich		*chans;
+	int			nchans;
+	u8			reserved;
+	struct slim_sched	sched;
+	struct completion	dev_released;
+	int			(*xfer_msg)(struct slim_controller *ctrl,
+				struct slim_msg_txn *txn);
+	int			(*set_laddr)(struct slim_controller *ctrl,
+				const u8 *ea, u8 elen, u8 laddr);
+	int			(*allocbw)(struct slim_device *sb,
+				int *subfrmc, int *clkgear);
+	int			(*get_laddr)(struct slim_controller *ctrl,
+				const u8 *ea, u8 elen, u8 *laddr);
+	int			(*wakeup)(struct slim_controller *ctrl);
+	int			(*alloc_port)(struct slim_controller *ctrl,
+				u8 port);
+	void			(*dealloc_port)(struct slim_controller *ctrl,
+				u8 port);
+	int			(*framer_handover)(struct slim_controller *ctrl,
+				struct slim_framer *new_framer);
+	int			(*port_xfer)(struct slim_controller *ctrl,
+				u8 pn, phys_addr_t iobuf, u32 len,
+				struct completion *comp);
+	enum slim_port_err	(*port_xfer_status)(struct slim_controller *ctr,
+				u8 pn, phys_addr_t *done_buf, u32 *done_len);
+	int			(*xfer_user_msg)(struct slim_controller *ctrl,
+				u8 la, u8 mt, u8 mc,
+				struct slim_ele_access *msg, u8 *buf, u8 len);
+	int			(*xfer_bulk_wr)(struct slim_controller *ctrl,
+				u8 la, u8 mt, u8 mc, struct slim_val_inf msgs[],
+				int n, int (*comp_cb)(void *ctx, int err),
+				void *ctx);
+};
+#define to_slim_controller(d) container_of(d, struct slim_controller, dev)
+
+/*
+ * struct slim_driver: Manage Slimbus generic/slave device driver
+ * @probe: Binds this driver to a slimbus device.
+ * @remove: Unbinds this driver from the slimbus device.
+ * @shutdown: Standard shutdown callback used during powerdown/halt.
+ * @suspend: Standard suspend callback used during system suspend
+ * @resume: Standard resume callback used during system resume
+ * @device_up: This callback is called when the device reports present and
+ *		gets a logical address assigned to it
+ * @device_down: This callback is called when device reports absent, or the
+ *		bus goes down. Device will report present when bus is up and
+ *		device_up callback will be called again when that happens
+ * @reset_device: This callback is called after framer is booted.
+ *		Driver should do the needful to reset the device,
+ *		so that device acquires sync and be operational.
+ * @driver: Slimbus device drivers should initialize name and owner field of
+ *	this structure
+ * @id_table: List of slimbus devices supported by this driver
+ */
+struct slim_driver {
+	int (*probe)(struct slim_device *sldev);
+	int (*remove)(struct slim_device *sldev);
+	void (*shutdown)(struct slim_device *sldev);
+	int (*suspend)(struct slim_device *sldev, pm_message_t pmesg);
+	int (*resume)(struct slim_device *sldev);
+	int (*device_up)(struct slim_device *sldev);
+	int (*device_down)(struct slim_device *sldev);
+	int (*reset_device)(struct slim_device *sldev);
+
+	struct device_driver		driver;
+	const struct slim_device_id	*id_table;
+};
+#define to_slim_driver(d) container_of(d, struct slim_driver, driver)
+
+/*
+ * struct slim_pending_ch: List of pending channels used by framework.
+ * @chan: Channel number
+ * @pending: list of channels
+ */
+struct slim_pending_ch {
+	u8	chan;
+	struct	list_head pending;
+};
+
+/*
+ * Client/device handle (struct slim_device):
+ * ------------------------------------------
+ *  This is the client/device handle returned when a slimbus
+ *  device is registered with a controller. This structure can be provided
+ *  during register_board_info, or can be allocated using slim_add_device API.
+ *  Pointer to this structure is used by client-driver as a handle.
+ *  @dev: Driver model representation of the device.
+ *  @name: Name of driver to use with this device.
+ *  @e_addr: 6-byte elemental address of this device.
+ *  @driver: Device's driver. Pointer to access routines.
+ *  @ctrl: Slimbus controller managing the bus hosting this device.
+ *  @laddr: 1-byte Logical address of this device.
+ *  @reported: Flag to indicate whether this device reported present. The flag
+ *	is set when device reports present, and is reset when it reports
+ *	absent. This flag alongwith notified flag below is used to call
+ *	device_up, or device_down callbacks for driver of this device.
+ *  @mark_define: List of channels pending definition/activation.
+ *  @mark_suspend: List of channels pending suspend.
+ *  @mark_removal: List of channels pending removal.
+ *  @notified: Flag to indicate whether this device has been notified. The
+ *	device may report present multiple times, but should be notified only
+ *	first time it has reported present.
+ *  @dev_list: List of devices on a controller
+ *  @wd: Work structure associated with workqueue for presence notification
+ *  @sldev_reconf: Mutex to protect the pending data-channel lists.
+ *  @pending_msgsl: Message bandwidth reservation request by this client in
+ *	slots that's pending reconfiguration.
+ *  @cur_msgsl: Message bandwidth reserved by this client in slots.
+ *  These 3 lists are managed by framework. Lists are populated when client
+ *  calls channel control API without reconfig-flag set and the lists are
+ *  emptied when the reconfiguration is done by this client.
+ */
+struct slim_device {
+	struct device		dev;
+	const char		*name;
+	u8			e_addr[6];
+	struct slim_driver	*driver;
+	struct slim_controller	*ctrl;
+	u8			laddr;
+	bool			reported;
+	struct list_head	mark_define;
+	struct list_head	mark_suspend;
+	struct list_head	mark_removal;
+	bool			notified;
+	struct list_head	dev_list;
+	struct work_struct	wd;
+	struct mutex		sldev_reconf;
+	u32			pending_msgsl;
+	u32			cur_msgsl;
+};
+#define to_slim_device(d) container_of(d, struct slim_device, dev)
+
+/*
+ * struct slim_boardinfo: Declare board info for Slimbus device bringup.
+ * @bus_num: Controller number (bus) on which this device will sit.
+ * @slim_slave: Device to be registered with slimbus.
+ */
+struct slim_boardinfo {
+	int			bus_num;
+	struct slim_device	*slim_slave;
+};
+
+/*
+ * slim_get_logical_addr: Return the logical address of a slimbus device.
+ * @sb: client handle requesting the adddress.
+ * @e_addr: Elemental address of the device.
+ * @e_len: Length of e_addr
+ * @laddr: output buffer to store the address
+ * context: can sleep
+ * -EINVAL is returned in case of invalid parameters, and -ENXIO is returned if
+ *  the device with this elemental address is not found.
+ */
+
+extern int slim_get_logical_addr(struct slim_device *sb, const u8 *e_addr,
+					u8 e_len, u8 *laddr);
+
+
+/* Message APIs Unicast message APIs used by slimbus slave drivers */
+
+/*
+ * Message API access routines.
+ * @sb: client handle requesting elemental message reads, writes.
+ * @msg: Input structure for start-offset, number of bytes to read.
+ * @rbuf: data buffer to be filled with values read.
+ * @len: data buffer size
+ * @wbuf: data buffer containing value/information to be written
+ * context: can sleep
+ * Returns:
+ * -EINVAL: Invalid parameters
+ * -ETIMEDOUT: If controller could not complete the request. This may happen if
+ *  the bus lines are not clocked, controller is not powered-on, slave with
+ *  given address is not enumerated/responding.
+ */
+extern int slim_request_val_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *buf,
+					u8 len);
+extern int slim_request_inf_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *buf,
+					u8 len);
+extern int slim_change_val_element(struct slim_device *sb,
+					struct slim_ele_access *msg,
+					const u8 *buf, u8 len);
+extern int slim_clear_inf_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *buf,
+					u8 len);
+extern int slim_request_change_val_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *rbuf,
+					const u8 *wbuf, u8 len);
+extern int slim_request_clear_inf_element(struct slim_device *sb,
+					struct slim_ele_access *msg, u8 *rbuf,
+					const u8 *wbuf, u8 len);
+
+/*
+ * Broadcast message API:
+ * call this API directly with sbdev = NULL.
+ * For broadcast reads, make sure that buffers are big-enough to incorporate
+ * replies from all logical addresses.
+ * All controllers may not support broadcast
+ */
+extern int slim_xfer_msg(struct slim_controller *ctrl,
+			struct slim_device *sbdev, struct slim_ele_access *msg,
+			u16 mc, u8 *rbuf, const u8 *wbuf, u8 len);
+
+/*
+ * User message:
+ * slim_user_msg: Send user message that is interpreted by destination device
+ * @sb: Client handle sending the message
+ * @la: Destination device for this user message
+ * @mt: Message Type (Soruce-referred, or Destination-referred)
+ * @mc: Message Code
+ * @msg: Message structure (start offset, number of bytes) to be sent
+ * @buf: data buffer to be sent
+ * @len: data buffer size in bytes
+ */
+extern int slim_user_msg(struct slim_device *sb, u8 la, u8 mt, u8 mc,
+				struct slim_ele_access *msg, u8 *buf, u8 len);
+
+/*
+ * Queue bulk of message writes:
+ * slim_bulk_msg_write: Write bulk of messages (e.g. downloading FW)
+ * @sb: Client handle sending these messages
+ * @la: Destination device for these messages
+ * @mt: Message Type
+ * @mc: Message Code
+ * @msgs: List of messages to be written in bulk
+ * @n: Number of messages in the list
+ * @cb: Callback if client needs this to be non-blocking
+ * @ctx: Context for this callback
+ * If supported by controller, this message list will be sent in bulk to the HW
+ * If the client specifies this to be non-blocking, the callback will be
+ * called from atomic context.
+ */
+extern int slim_bulk_msg_write(struct slim_device *sb, u8 mt, u8 mc,
+			struct slim_val_inf msgs[], int n,
+			int (*comp_cb)(void *ctx, int err), void *ctx);
+/* end of message apis */
+
+/* Port management for manager device APIs */
+
+/*
+ * slim_alloc_mgrports: Allocate port on manager side.
+ * @sb: device/client handle.
+ * @req: Port request type.
+ * @nports: Number of ports requested
+ * @rh: output buffer to store the port handles
+ * @hsz: size of buffer storing handles
+ * context: can sleep
+ * This port will be typically used by SW. e.g. client driver wants to receive
+ * some data from audio codec HW using a data channel.
+ * Port allocated using this API will be used to receive the data.
+ * If half-duplex ports are requested, two adjacent ports are allocated for
+ * 1 half-duplex port. So the handle-buffer size should be twice the number
+ * of half-duplex ports to be allocated.
+ * -EDQUOT is returned if all ports are in use.
+ */
+extern int slim_alloc_mgrports(struct slim_device *sb, enum slim_port_req req,
+				int nports, u32 *rh, int hsz);
+
+/* Deallocate the port(s) allocated using the API above */
+extern int slim_dealloc_mgrports(struct slim_device *sb, u32 *hdl, int hsz);
+
+/*
+ * slim_config_mgrports: Configure manager side ports
+ * @sb: device/client handle.
+ * @ph: array of port handles for which this configuration is valid
+ * @nports: Number of ports in ph
+ * @cfg: configuration requested for port(s)
+ * Configure port settings if they are different than the default ones.
+ * Returns success if the config could be applied. Returns -EISCONN if the
+ * port is in use
+ */
+extern int slim_config_mgrports(struct slim_device *sb, u32 *ph, int nports,
+				struct slim_port_cfg *cfg);
+
+/*
+ * slim_port_xfer: Schedule buffer to be transferred/received using port-handle.
+ * @sb: client handle
+ * @ph: port-handle
+ * @iobuf: buffer to be transferred or populated
+ * @len: buffer size.
+ * @comp: completion signal to indicate transfer done or error.
+ * context: can sleep
+ * Returns number of bytes transferred/received if used synchronously.
+ * Will return 0 if used asynchronously.
+ * Client will call slim_port_get_xfer_status to get error and/or number of
+ * bytes transferred if used asynchronously.
+ */
+extern int slim_port_xfer(struct slim_device *sb, u32 ph, phys_addr_t iobuf,
+				u32 len, struct completion *comp);
+
+/*
+ * slim_port_get_xfer_status: Poll for port transfers, or get transfer status
+ *	after completion is done.
+ * @sb: client handle
+ * @ph: port-handle
+ * @done_buf: return pointer (iobuf from slim_port_xfer) which is processed.
+ * @done_len: Number of bytes transferred.
+ * This can be called when port_xfer complition is signalled.
+ * The API will return port transfer error (underflow/overflow/disconnect)
+ * and/or done_len will reflect number of bytes transferred. Note that
+ * done_len may be valid even if port error (overflow/underflow) has happened.
+ * e.g. If the transfer was scheduled with a few bytes to be transferred and
+ * client has not supplied more data to be transferred, done_len will indicate
+ * number of bytes transferred with underflow error. To avoid frequent underflow
+ * errors, multiple transfers can be queued (e.g. ping-pong buffers) so that
+ * channel has data to be transferred even if client is not ready to transfer
+ * data all the time. done_buf will indicate address of the last buffer
+ * processed from the multiple transfers.
+ */
+extern enum slim_port_err slim_port_get_xfer_status(struct slim_device *sb,
+			u32 ph, phys_addr_t *done_buf, u32 *done_len);
+
+/*
+ * slim_connect_src: Connect source port to channel.
+ * @sb: client handle
+ * @srch: source handle to be connected to this channel
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have 1 source port.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if source is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid direction is specified for non-manager port,
+ * or if the manager side port number is out of bounds, or in incorrect state
+ */
+extern int slim_connect_src(struct slim_device *sb, u32 srch, u16 chanh);
+
+/*
+ * slim_connect_sink: Connect sink port(s) to channel.
+ * @sb: client handle
+ * @sinkh: sink handle(s) to be connected to this channel
+ * @nsink: number of sinks
+ * @chanh: Channel with which the ports need to be associated with.
+ * Per slimbus specification, a channel may have multiple sink-ports.
+ * Channel specified in chanh needs to be allocated first.
+ * Returns -EALREADY if sink is already configured for this channel.
+ * Returns -ENOTCONN if channel is not allocated
+ * Returns -EINVAL if invalid parameters are passed, or invalid direction is
+ * specified for non-manager port, or if the manager side port number is out of
+ * bounds, or in incorrect state
+ */
+extern int slim_connect_sink(struct slim_device *sb, u32 *sinkh, int nsink,
+				u16 chanh);
+/*
+ * slim_disconnect_ports: Disconnect port(s) from channel
+ * @sb: client handle
+ * @ph: ports to be disconnected
+ * @nph: number of ports.
+ * Disconnects ports from a channel.
+ */
+extern int slim_disconnect_ports(struct slim_device *sb, u32 *ph, int nph);
+
+/*
+ * slim_get_slaveport: Get slave port handle
+ * @la: slave device logical address.
+ * @idx: port index at slave
+ * @rh: return handle
+ * @flw: Flow type (source or destination)
+ * This API only returns a slave port's representation as expected by slimbus
+ * driver. This port is not managed by the slimbus driver. Caller is expected
+ * to have visibility of this port since it's a device-port.
+ */
+extern int slim_get_slaveport(u8 la, int idx, u32 *rh, enum slim_port_flow flw);
+
+
+/* Channel functions. */
+
+/*
+ * slim_alloc_ch: Allocate a slimbus channel and return its handle.
+ * @sb: client handle.
+ * @chanh: return channel handle
+ * Slimbus channels are limited to 256 per specification.
+ * -EXFULL is returned if all channels are in use.
+ * Although slimbus specification supports 256 channels, a controller may not
+ * support that many channels.
+ */
+extern int slim_alloc_ch(struct slim_device *sb, u16 *chanh);
+
+/*
+ * slim_query_ch: Get reference-counted handle for a channel number. Every
+ * channel is reference counted by one as producer and the others as
+ * consumer)
+ * @sb: client handle
+ * @chan: slimbus channel number
+ * @chanh: return channel handle
+ * If request channel number is not in use, it is allocated, and reference
+ * count is set to one. If the channel was was already allocated, this API
+ * will return handle to that channel and reference count is incremented.
+ * -EXFULL is returned if all channels are in use
+ */
+extern int slim_query_ch(struct slim_device *sb, u8 chan, u16 *chanh);
+/*
+ * slim_dealloc_ch: Deallocate channel allocated using the API above
+ * -EISCONN is returned if the channel is tried to be deallocated without
+ *  being removed first.
+ *  -ENOTCONN is returned if deallocation is tried on a channel that's not
+ *  allocated.
+ */
+extern int slim_dealloc_ch(struct slim_device *sb, u16 chanh);
+
+
+/*
+ * slim_define_ch: Define a channel.This API defines channel parameters for a
+ *	given channel.
+ * @sb: client handle.
+ * @prop: slim_ch structure with channel parameters desired to be used.
+ * @chanh: list of channels to be defined.
+ * @nchan: number of channels in a group (1 if grp is false)
+ * @grp: Are the channels grouped
+ * @grph: return group handle if grouping of channels is desired.
+ *	Channels can be grouped if multiple channels use same parameters
+ *	(e.g. 5.1 audio has 6 channels with same parameters. They will all be
+ *	grouped and given 1 handle for simplicity and avoid repeatedly calling
+ *	the API)
+ * -EISCONN is returned if channel is already used with different parameters.
+ * -ENXIO is returned if the channel is not yet allocated.
+ */
+extern int slim_define_ch(struct slim_device *sb, struct slim_ch *prop,
+				u16 *chanh, u8 nchan, bool grp, u16 *grph);
+
+/*
+ * slim_control_ch: Channel control API.
+ * @sb: client handle
+ * @grpchanh: group or channel handle to be controlled
+ * @chctrl: Control command (activate/suspend/remove)
+ * @commit: flag to indicate whether the control should take effect right-away.
+ * This API activates, removes or suspends a channel (or group of channels)
+ * grpchanh indicates the channel or group handle (returned by the define_ch
+ * API). Reconfiguration may be time-consuming since it can change all other
+ * active channel allocations on the bus, change in clock gear used by the
+ * slimbus, and change in the control space width used for messaging.
+ * commit makes sure that multiple channels can be activated/deactivated before
+ * reconfiguration is started.
+ * -EXFULL is returned if there is no space in TDM to reserve the bandwidth.
+ * -EISCONN/-ENOTCONN is returned if the channel is already connected or not
+ * yet defined.
+ * -EINVAL is returned if individual control of a grouped-channel is attempted.
+ */
+extern int slim_control_ch(struct slim_device *sb, u16 grpchanh,
+				enum slim_ch_control chctrl, bool commit);
+
+/*
+ * slim_get_ch_state: Channel state.
+ * This API returns the channel's state (active, suspended, inactive etc)
+ */
+extern enum slim_ch_state slim_get_ch_state(struct slim_device *sb,
+						u16 chanh);
+
+/*
+ * slim_reservemsg_bw: Request to reserve bandwidth for messages.
+ * @sb: client handle
+ * @bw_bps: message bandwidth in bits per second to be requested
+ * @commit: indicates whether the reconfiguration needs to be acted upon.
+ * This API call can be grouped with slim_control_ch API call with only one of
+ * the APIs specifying the commit flag to avoid reconfiguration being called too
+ * frequently. -EXFULL is returned if there is no space in TDM to reserve the
+ * bandwidth. -EBUSY is returned if reconfiguration is requested, but a request
+ * is already in progress.
+ */
+extern int slim_reservemsg_bw(struct slim_device *sb, u32 bw_bps, bool commit);
+
+/*
+ * slim_reconfigure_now: Request reconfiguration now.
+ * @sb: client handle
+ * This API does what commit flag in other scheduling APIs do.
+ * -EXFULL is returned if there is no space in TDM to reserve the
+ * bandwidth. -EBUSY is returned if reconfiguration request is already in
+ * progress.
+ */
+extern int slim_reconfigure_now(struct slim_device *sb);
+
+/*
+ * slim_ctrl_clk_pause: Called by slimbus controller to request clock to be
+ *	paused or woken up out of clock pause
+ * @ctrl: controller requesting bus to be paused or woken up
+ * @wakeup: Wakeup this controller from clock pause.
+ * @restart: Restart time value per spec used for clock pause. This value
+ *	isn't used when controller is to be woken up.
+ * This API executes clock pause reconfiguration sequence if wakeup is false.
+ * If wakeup is true, controller's wakeup is called
+ * Slimbus clock is idle and can be disabled by the controller later.
+ */
+extern int slim_ctrl_clk_pause(struct slim_controller *ctrl, bool wakeup,
+		u8 restart);
+
+/*
+ * slim_driver_register: Client driver registration with slimbus
+ * @drv:Client driver to be associated with client-device.
+ * This API will register the client driver with the slimbus
+ * It is called from the driver's module-init function.
+ */
+extern int slim_driver_register(struct slim_driver *drv);
+
+/*
+ * slim_driver_unregister: Undo effects of slim_driver_register
+ * @drv: Client driver to be unregistered
+ */
+extern void slim_driver_unregister(struct slim_driver *drv);
+
+/*
+ * slim_add_numbered_controller: Controller bring-up.
+ * @ctrl: Controller to be registered.
+ * A controller is registered with the framework using this API. ctrl->nr is the
+ * desired number with which slimbus framework registers the controller.
+ * Function will return -EBUSY if the number is in use.
+ */
+extern int slim_add_numbered_controller(struct slim_controller *ctrl);
+
+/*
+ * slim_del_controller: Controller tear-down.
+ * Controller added with the above API is teared down using this API.
+ */
+extern int slim_del_controller(struct slim_controller *ctrl);
+
+/*
+ * slim_add_device: Add a new device without register board info.
+ * @ctrl: Controller to which this device is to be added to.
+ * Called when device doesn't have an explicit client-driver to be probed, or
+ * the client-driver is a module installed dynamically.
+ */
+extern int slim_add_device(struct slim_controller *ctrl,
+			struct slim_device *sbdev);
+
+/* slim_remove_device: Remove the effect of slim_add_device() */
+extern void slim_remove_device(struct slim_device *sbdev);
+
+/*
+ * slim_assign_laddr: Assign logical address to a device enumerated.
+ * @ctrl: Controller with which device is enumerated.
+ * @e_addr: 6-byte elemental address of the device.
+ * @e_len: buffer length for e_addr
+ * @laddr: Return logical address (if valid flag is false)
+ * @valid: true if laddr holds a valid address that controller wants to
+ *	set for this enumeration address. Otherwise framework sets index into
+ *	address table as logical address.
+ * Called by controller in response to REPORT_PRESENT. Framework will assign
+ * a logical address to this enumeration address.
+ * Function returns -EXFULL to indicate that all logical addresses are already
+ * taken.
+ */
+extern int slim_assign_laddr(struct slim_controller *ctrl, const u8 *e_addr,
+				u8 e_len, u8 *laddr, bool valid);
+
+/*
+ * slim_report_absent: Controller calls this function when a device
+ *	reports absent, OR when the device cannot be communicated with
+ * @sbdev: Device that cannot be reached, or that sent report absent
+ */
+void slim_report_absent(struct slim_device *sbdev);
+
+/*
+ * slim_framer_booted: This function is called by controller after the active
+ * framer has booted (using Bus Reset sequence, or after it has shutdown and has
+ * come back up). Components, devices on the bus may be in undefined state,
+ * and this function triggers their drivers to do the needful
+ * to bring them back in Reset state so that they can acquire sync, report
+ * present and be operational again.
+ */
+void slim_framer_booted(struct slim_controller *ctrl);
+
+/*
+ * slim_msg_response: Deliver Message response received from a device to the
+ *	framework.
+ * @ctrl: Controller handle
+ * @reply: Reply received from the device
+ * @len: Length of the reply
+ * @tid: Transaction ID received with which framework can associate reply.
+ * Called by controller to inform framework about the response received.
+ * This helps in making the API asynchronous, and controller-driver doesn't need
+ * to manage 1 more table other than the one managed by framework mapping TID
+ * with buffers
+ */
+extern void slim_msg_response(struct slim_controller *ctrl, u8 *reply, u8 tid,
+				u8 len);
+
+/*
+ * slim_busnum_to_ctrl: Map bus number to controller
+ * @busnum: Bus number
+ * Returns controller representing this bus number
+ */
+extern struct slim_controller *slim_busnum_to_ctrl(u32 busnum);
+
+/*
+ * slim_ctrl_add_boarddevs: Add devices registered by board-info
+ * @ctrl: Controller to which these devices are to be added to.
+ * This API is called by controller when it is up and running.
+ * If devices on a controller were registered before controller,
+ * this will make sure that they get probed when controller is up
+ */
+extern void slim_ctrl_add_boarddevs(struct slim_controller *ctrl);
+
+extern const
+struct slim_device_id *slim_get_device_id(const struct slim_device *sdev);
+
+/*
+ * slim_register_board_info: Board-initialization routine.
+ * @info: List of all devices on all controllers present on the board.
+ * @n: number of entries.
+ * API enumerates respective devices on corresponding controller.
+ * Called from board-init function.
+ */
+#ifdef CONFIG_SLIMBUS
+extern int slim_register_board_info(struct slim_boardinfo const *info,
+					unsigned int n);
+#else
+static inline int slim_register_board_info(struct slim_boardinfo const *info,
+					unsigned int n)
+{
+	return 0;
+}
+#endif
+
+static inline void *slim_get_ctrldata(const struct slim_controller *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void slim_set_ctrldata(struct slim_controller *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+static inline void *slim_get_devicedata(const struct slim_device *dev)
+{
+	return dev_get_drvdata(&dev->dev);
+}
+
+static inline void slim_set_clientdata(struct slim_device *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+#endif /* _LINUX_SLIMBUS_H */
diff --git a/include/linux/soundwire/soundwire.h b/include/linux/soundwire/soundwire.h
new file mode 100644
index 0000000..752a001
--- /dev/null
+++ b/include/linux/soundwire/soundwire.h
@@ -0,0 +1,312 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_SOUNDWIRE_H
+#define _LINUX_SOUNDWIRE_H
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/mod_devicetable.h>
+
+extern struct bus_type soundwire_type;
+
+/* Soundwire supports max. of 8 channels per port */
+#define SWR_MAX_CHANNEL_NUM	8
+/* Soundwire supports max. of 14 ports on each device */
+#define SWR_MAX_DEV_PORT_NUM	14
+/* Maximum number of slave devices that a master can control */
+#define SWR_MAX_DEV_NUM		11
+/* Maximum number of ports on master so that it can accommodate all the port
+ * configurations of all devices
+ */
+#define SWR_MAX_MSTR_PORT_NUM	(SWR_MAX_DEV_NUM * SWR_MAX_DEV_PORT_NUM)
+
+/* Indicates soundwire devices group information */
+enum {
+	SWR_GROUP_NONE = 0,
+	SWR_GROUP_12 = 12,
+	SWR_GROUP_13 = 13,
+	SWR_BROADCAST = 15,
+};
+
+/*
+ * struct swr_port_info - represent soundwire frame shape
+ * @dev_id: logical device number of the soundwire slave device
+ * @port_en: flag indicates whether the port is enabled
+ * @port_id: logical port number of the soundwire slave device
+ * @offset1: sample offset indicating the offset of the channel
+ * from the start of the frame
+ * @offset2: channel offset indicating offset between to channels
+ * @sinterval: sample interval indicates spacing from one sample
+ * event to the next
+ * @ch_en: channels in a port that need to be enabled
+ * @num_ch: number of channels enabled in a port
+ * @ch_rate: sampling rate of the channel with which data will be
+ * transferred
+ *
+ * Soundwire frame shape is created based on swr_port_info struct
+ * parameters.
+ */
+struct swr_port_info {
+	u8 dev_id;
+	u8 port_en;
+	u8 port_id;
+	u8 offset1;
+	u8 offset2;
+	u8 sinterval;
+	u8 ch_en;
+	u8 num_ch;
+	u32 ch_rate;
+};
+
+/*
+ * struct swr_params - represent transfer of data from soundwire slave
+ * to soundwire master
+ * @tid: transaction ID to track each transaction
+ * @dev_id: logical device number of the soundwire slave device
+ * @num_port: number of ports that needs to be configured
+ * @port_id: array of logical port numbers of the soundwire slave device
+ * @num_ch: array of number of channels enabled
+ * @ch_rate: array of sampling rate of different channels that need to
+ * be configured
+ * @ch_en: array of channels mask for all the ports
+ */
+struct swr_params {
+	u8 tid;
+	u8 dev_id;
+	u8 num_port;
+	u8 port_id[SWR_MAX_DEV_PORT_NUM];
+	u8 num_ch[SWR_MAX_DEV_PORT_NUM];
+	u32 ch_rate[SWR_MAX_DEV_PORT_NUM];
+	u8 ch_en[SWR_MAX_DEV_PORT_NUM];
+};
+
+/*
+ * struct swr_reg - struct to handle soundwire slave register read/writes
+ * @tid: transaction id for reg read/writes
+ * @dev_id: logical device number of the soundwire slave device
+ * @regaddr: 16 bit regaddr of soundwire slave
+ * @buf: value to be written/read to/from regaddr
+ * @len: length of the buffer buf
+ */
+struct swr_reg {
+	u8  tid;
+	u8  dev_id;
+	u32 regaddr;
+	u32 *buf;
+	u32 len;
+};
+
+/*
+ * struct swr_master - Interface to the soundwire master controller
+ * @dev: device interface to this driver
+ * @list: link with other soundwire master controllers
+ * @bus_num: board/SoC specific identifier for a soundwire master
+ * @mlock: mutex protecting master data structures
+ * @devices: list of devices on this master
+ * @port: logical port numbers of the soundwire master. This array
+ * can hold maximum master ports which is equal to number of slave
+ * devices multiplied by number of ports in each slave device
+ * @port_txn: table of port config transactions with transaction id
+ * @reg_txn: table of register transactions with transaction id
+ * @last_tid: size of table port_txn (can't grow beyond 256 since
+ * tid is 8 bits)
+ * @num_port: number of active ports on soundwire master
+ * @gr_sid: slave id used by the group for write operations
+ * @connect_port: callback for configuration of soundwire port(s)
+ * @disconnect_port: callback for disable of soundwire port(s)
+ * @read: callback for soundwire slave register read
+ * @write: callback for soundwire slave register write
+ * @get_logical_dev_num: callback to get soundwire slave logical
+ * device number
+ */
+struct swr_master {
+	struct device dev;
+	struct list_head list;
+	unsigned int bus_num;
+	struct mutex mlock;
+	struct list_head devices;
+	struct swr_port_info port[SWR_MAX_MSTR_PORT_NUM];
+	struct swr_params **port_txn;
+	struct swr_reg **reg_txn;
+	u8 last_tid;
+	u8 num_port;
+	u8 num_dev;
+	u8 gr_sid;
+	int (*connect_port)(struct swr_master *mstr, struct swr_params *txn);
+	int (*disconnect_port)(struct swr_master *mstr, struct swr_params *txn);
+	int (*read)(struct swr_master *mstr, u8 dev_num, u16 reg_addr,
+			void *buf, u32 len);
+	int (*write)(struct swr_master *mstr, u8 dev_num, u16 reg_addr,
+			const void *buf);
+	int (*bulk_write)(struct swr_master *master, u8 dev_num, void *reg,
+			  const void *buf, size_t len);
+	int (*get_logical_dev_num)(struct swr_master *mstr, u64 dev_id,
+				u8 *dev_num);
+	void (*slvdev_datapath_control)(struct swr_master *mstr, bool enable);
+	bool (*remove_from_group)(struct swr_master *mstr);
+};
+
+static inline struct swr_master *to_swr_master(struct device *dev)
+{
+	return dev ? container_of(dev, struct swr_master, dev) : NULL;
+}
+
+/*
+ * struct swr_device - represent a soundwire slave device
+ * @name: indicates the name of the device, defined in devicetree
+ * binding under soundwire slave device node as a compatible field.
+ * @master: soundwire master managing the bus hosting this device
+ * @driver: Device's driver. Pointer to access routines
+ * @dev_list: list of devices on a controller
+ * @dev_num: logical device number of the soundwire slave device
+ * @dev: driver model representation of the device
+ * @addr: represents "ea-addr" which is unique-id of soundwire slave
+ * device
+ * @group_id: group id supported by the slave device
+ */
+struct swr_device {
+	char name[SOUNDWIRE_NAME_SIZE];
+	struct swr_master *master;
+	struct swr_driver *driver;
+	struct list_head dev_list;
+	u8               dev_num;
+	struct device    dev;
+	unsigned long    addr;
+	u8 group_id;
+};
+
+static inline struct swr_device *to_swr_device(struct device *dev)
+{
+	return dev ? container_of(dev, struct swr_device, dev) : NULL;
+}
+
+/*
+ * struct swr_driver - Manage soundwire slave device driver
+ * @probe: binds this driver to soundwire device
+ * @remove: unbinds this driver from soundwire device
+ * @shutdown: standard shutdown callback used during power down/halt
+ * @suspend: standard suspend callback used during system suspend
+ * @resume: standard resume callback used during system resume
+ * @startup: additional init operation for slave devices
+ * @driver: soundwire device drivers should initialize name and
+ * owner field of this structure
+ * @id_table: list of soundwire devices supported by this driver
+ */
+struct swr_driver {
+	int	(*probe)(struct swr_device *swr);
+	int	(*remove)(struct swr_device *swr);
+	void	(*shutdown)(struct swr_device *swr);
+	int	(*suspend)(struct swr_device *swr, pm_message_t pmesg);
+	int	(*resume)(struct swr_device *swr);
+	int	(*device_up)(struct swr_device *swr);
+	int	(*device_down)(struct swr_device *swr);
+	int	(*reset_device)(struct swr_device *swr);
+	int	(*startup)(struct swr_device *swr);
+	struct device_driver		driver;
+	const struct swr_device_id	*id_table;
+};
+
+static inline struct swr_driver *to_swr_driver(struct device_driver *drv)
+{
+	return drv ? container_of(drv, struct swr_driver, driver) : NULL;
+}
+
+/*
+ * struct swr_boardinfo - Declare board info for soundwire device bringup
+ * @name: name to initialize swr_device.name
+ * @bus_num: identifies which soundwire master parents the soundwire
+ * slave_device
+ * @addr: represents "ea-addr" of soundwire slave device
+ * @of_node: pointer to OpenFirmware device node
+ * @swr_slave: device to be registered with soundwire
+ */
+struct swr_boardinfo {
+	char               name[SOUNDWIRE_NAME_SIZE];
+	int                bus_num;
+	u64		   addr;
+	struct device_node *of_node;
+	struct swr_device  *swr_slave;
+};
+
+static inline void *swr_get_ctrl_data(const struct swr_master *master)
+{
+	return master ? dev_get_drvdata(&master->dev) : NULL;
+}
+
+static inline void swr_set_ctrl_data(struct swr_master *master, void *data)
+{
+	dev_set_drvdata(&master->dev, data);
+}
+
+static inline void *swr_get_dev_data(const struct swr_device *dev)
+{
+	return dev ? dev_get_drvdata(&dev->dev) : NULL;
+}
+
+static inline void swr_set_dev_data(struct swr_device *dev, void *data)
+{
+	dev_set_drvdata(&dev->dev, data);
+}
+
+extern int swr_startup_devices(struct swr_device *swr_dev);
+
+extern struct swr_device *swr_new_device(struct swr_master *master,
+				struct swr_boardinfo const *info);
+
+extern int of_register_swr_devices(struct swr_master *master);
+
+extern void swr_port_response(struct swr_master *mstr, u8 tid);
+
+extern int swr_get_logical_dev_num(struct swr_device *dev, u64 dev_id,
+			u8 *dev_num);
+
+extern int swr_read(struct swr_device *dev, u8 dev_num, u16 reg_addr,
+			void *buf, u32 len);
+
+extern int swr_write(struct swr_device *dev, u8 dev_num, u16 reg_addr,
+			const void *buf);
+
+extern int swr_bulk_write(struct swr_device *dev, u8 dev_num, void *reg_addr,
+			  const void *buf, size_t len);
+
+extern int swr_connect_port(struct swr_device *dev, u8 *port_id, u8 num_port,
+				u8 *ch_mask, u32 *ch_rate, u8 *num_ch);
+
+extern int swr_disconnect_port(struct swr_device *dev,
+				u8 *port_id, u8 num_port);
+
+extern int swr_set_device_group(struct swr_device *swr_dev, u8 id);
+
+extern int swr_driver_register(struct swr_driver *drv);
+
+extern void swr_driver_unregister(struct swr_driver *drv);
+
+extern int swr_add_device(struct swr_master *master,
+				struct swr_device *swrdev);
+extern void swr_remove_device(struct swr_device *swr);
+
+extern void swr_master_add_boarddevices(struct swr_master *master);
+
+extern void swr_unregister_master(struct swr_master *master);
+
+extern int swr_register_master(struct swr_master *master);
+
+extern int swr_device_up(struct swr_device *swr_dev);
+
+extern int swr_device_down(struct swr_device *swr_dev);
+
+extern int swr_reset_device(struct swr_device *swr_dev);
+
+extern int swr_slvdev_datapath_control(struct swr_device *swr_dev, u8 dev_num,
+				       bool enable);
+extern int swr_remove_from_group(struct swr_device *dev, u8 dev_num);
+#endif /* _LINUX_SOUNDWIRE_H */
diff --git a/include/linux/soundwire/swr-wcd.h b/include/linux/soundwire/swr-wcd.h
new file mode 100644
index 0000000..041b901
--- /dev/null
+++ b/include/linux/soundwire/swr-wcd.h
@@ -0,0 +1,35 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _LINUX_SWR_WCD_H
+#define _LINUX_SWR_WCD_H
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/bitops.h>
+
+enum {
+	SWR_CH_MAP,
+	SWR_DEVICE_DOWN,
+	SWR_DEVICE_UP,
+	SWR_SUBSYS_RESTART,
+	SWR_SET_NUM_RX_CH,
+};
+
+struct swr_mstr_port {
+	int num_port;
+	u8 *port;
+};
+
+extern int swrm_wcd_notify(struct platform_device *pdev, u32 id, void *data);
+
+#endif /* _LINUX_SWR_WCD_H */
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 511182a..8d0210e 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -77,11 +77,19 @@ enum thermal_device_mode {
 	THERMAL_DEVICE_ENABLED,
 };
 
+enum thermal_trip_activation_mode {
+	THERMAL_TRIP_ACTIVATION_DISABLED = 0,
+	THERMAL_TRIP_ACTIVATION_ENABLED,
+};
+
 enum thermal_trip_type {
 	THERMAL_TRIP_ACTIVE = 0,
 	THERMAL_TRIP_PASSIVE,
 	THERMAL_TRIP_HOT,
 	THERMAL_TRIP_CRITICAL,
+	THERMAL_TRIP_CONFIGURABLE_HI,
+	THERMAL_TRIP_CONFIGURABLE_LOW,
+	THERMAL_TRIP_CRITICAL_LOW,
 };
 
 enum thermal_trend {
@@ -122,6 +130,8 @@ struct thermal_zone_device_ops {
 	int (*set_trip_hyst) (struct thermal_zone_device *, int, int);
 	int (*get_crit_temp) (struct thermal_zone_device *, int *);
 	int (*set_emul_temp) (struct thermal_zone_device *, int);
+	int (*activate_trip_type)(struct thermal_zone_device *, int,
+		enum thermal_trip_activation_mode);
 	int (*get_trend) (struct thermal_zone_device *, int,
 			  enum thermal_trend *);
 	int (*notify) (struct thermal_zone_device *, int,
diff --git a/include/linux/tick.h b/include/linux/tick.h
index 9e207e3..7511544 100644
--- a/include/linux/tick.h
+++ b/include/linux/tick.h
@@ -240,7 +240,15 @@ extern void __tick_nohz_task_switch(void);
 #else
 static inline int housekeeping_any_cpu(void)
 {
-	return smp_processor_id();
+	cpumask_t available;
+	int cpu;
+
+	cpumask_andnot(&available, cpu_online_mask, cpu_isolated_mask);
+	cpu = cpumask_any(&available);
+	if (cpu >= nr_cpu_ids)
+		cpu = smp_processor_id();
+
+	return cpu;
 }
 static inline bool tick_nohz_full_enabled(void) { return false; }
 static inline bool tick_nohz_full_cpu(int cpu) { return false; }
@@ -278,7 +286,7 @@ static inline bool is_housekeeping_cpu(int cpu)
 	if (tick_nohz_full_enabled())
 		return cpumask_test_cpu(cpu, housekeeping_mask);
 #endif
-	return true;
+	return !cpu_isolated(cpu);
 }
 
 static inline void housekeeping_affine(struct task_struct *t)
diff --git a/include/linux/timer.h b/include/linux/timer.h
index 51d601f..356793e 100644
--- a/include/linux/timer.h
+++ b/include/linux/timer.h
@@ -197,6 +197,9 @@ extern int mod_timer_pending(struct timer_list *timer, unsigned long expires);
  */
 #define NEXT_TIMER_MAX_DELTA	((1UL << 30) - 1)
 
+/* To be used from cpusets, only */
+extern void timer_quiesce_cpu(void *cpup);
+
 /*
  * Timer-statistics info:
  */
diff --git a/include/linux/types.h b/include/linux/types.h
index baf7183..f647f4a 100644
--- a/include/linux/types.h
+++ b/include/linux/types.h
@@ -9,6 +9,9 @@
 #define DECLARE_BITMAP(name,bits) \
 	unsigned long name[BITS_TO_LONGS(bits)]
 
+#define DECLARE_BITMAP_ARRAY(name,nr,bits) \
+	unsigned long name[nr][BITS_TO_LONGS(bits)]
+
 typedef __u32 __kernel_dev_t;
 
 typedef __kernel_fd_set		fd_set;
diff --git a/include/linux/usb/ccid_desc.h b/include/linux/usb/ccid_desc.h
new file mode 100644
index 0000000..9a0c726
--- /dev/null
+++ b/include/linux/usb/ccid_desc.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2011, 2017 The Linux Foundation. All rights reserved.
+
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details
+ */
+
+#ifndef __LINUX_USB_CCID_DESC_H
+#define __LINUX_USB_CCID_DESC_H
+
+/*CCID specification version 1.10*/
+#define CCID1_10                               0x0110
+
+#define SMART_CARD_DEVICE_CLASS                0x0B
+/* Smart Card Device Class Descriptor Type */
+#define CCID_DECRIPTOR_TYPE                    0x21
+
+/* Table 5.3-1 Summary of CCID Class Specific Request */
+#define CCIDGENERICREQ_ABORT                    0x01
+#define CCIDGENERICREQ_GET_CLOCK_FREQUENCIES    0x02
+#define CCIDGENERICREQ_GET_DATA_RATES           0x03
+
+/* 6.1 Command Pipe, Bulk-OUT Messages */
+#define PC_TO_RDR_ICCPOWERON                   0x62
+#define PC_TO_RDR_ICCPOWEROFF                  0x63
+#define PC_TO_RDR_GETSLOTSTATUS                0x65
+#define PC_TO_RDR_XFRBLOCK                     0x6F
+#define PC_TO_RDR_GETPARAMETERS                0x6C
+#define PC_TO_RDR_RESETPARAMETERS              0x6D
+#define PC_TO_RDR_SETPARAMETERS                0x61
+#define PC_TO_RDR_ESCAPE                       0x6B
+#define PC_TO_RDR_ICCCLOCK                     0x6E
+#define PC_TO_RDR_T0APDU                       0x6A
+#define PC_TO_RDR_SECURE                       0x69
+#define PC_TO_RDR_MECHANICAL                   0x71
+#define PC_TO_RDR_ABORT                        0x72
+#define PC_TO_RDR_SETDATARATEANDCLOCKFREQUENCY 0x73
+
+/* 6.2 Response Pipe, Bulk-IN Messages */
+#define RDR_TO_PC_DATABLOCK                    0x80
+#define RDR_TO_PC_SLOTSTATUS                   0x81
+#define RDR_TO_PC_PARAMETERS                   0x82
+#define RDR_TO_PC_ESCAPE                       0x83
+#define RDR_TO_PC_DATARATEANDCLOCKFREQUENCY    0x84
+
+/* 6.3 Interrupt-IN Messages */
+#define RDR_TO_PC_NOTIFYSLOTCHANGE             0x50
+#define RDR_TO_PC_HARDWAREERROR                0x51
+
+/* Table 6.2-2 Slot error register when bmCommandStatus = 1 */
+#define CMD_ABORTED                            0xFF
+#define ICC_MUTE                               0xFE
+#define XFR_PARITY_ERROR                       0xFD
+#define XFR_OVERRUN                            0xFC
+#define HW_ERROR                               0xFB
+#define BAD_ATR_TS                             0xF8
+#define BAD_ATR_TCK                            0xF7
+#define ICC_PROTOCOL_NOT_SUPPORTED             0xF6
+#define ICC_CLASS_NOT_SUPPORTED                0xF5
+#define PROCEDURE_BYTE_CONFLICT                0xF4
+#define DEACTIVATED_PROTOCOL                   0xF3
+#define BUSY_WITH_AUTO_SEQUENCE                0xF2
+#define PIN_TIMEOUT                            0xF0
+#define PIN_CANCELLED                          0xEF
+#define CMD_SLOT_BUSY                          0xE0
+
+/* CCID rev 1.1, p.27 */
+#define VOLTS_AUTO                             0x00
+#define VOLTS_5_0                              0x01
+#define VOLTS_3_0                              0x02
+#define VOLTS_1_8                              0x03
+
+/* 6.3.1 RDR_to_PC_NotifySlotChange */
+#define ICC_NOT_PRESENT                        0x00
+#define ICC_PRESENT                            0x01
+#define ICC_CHANGE                             0x02
+#define ICC_INSERTED_EVENT                     (ICC_PRESENT+ICC_CHANGE)
+
+/* Identifies the length of type of subordinate descriptors of a CCID device
+ * Table 5.1-1 Smart Card Device Class descriptors
+ */
+struct usb_ccid_class_descriptor {
+	unsigned char  bLength;
+	unsigned char  bDescriptorType;
+	unsigned short bcdCCID;
+	unsigned char  bMaxSlotIndex;
+	unsigned char  bVoltageSupport;
+	unsigned long  dwProtocols;
+	unsigned long  dwDefaultClock;
+	unsigned long  dwMaximumClock;
+	unsigned char  bNumClockSupported;
+	unsigned long  dwDataRate;
+	unsigned long  dwMaxDataRate;
+	unsigned char  bNumDataRatesSupported;
+	unsigned long  dwMaxIFSD;
+	unsigned long  dwSynchProtocols;
+	unsigned long  dwMechanical;
+	unsigned long  dwFeatures;
+	unsigned long  dwMaxCCIDMessageLength;
+	unsigned char  bClassGetResponse;
+	unsigned char  bClassEnvelope;
+	unsigned short wLcdLayout;
+	unsigned char  bPINSupport;
+	unsigned char  bMaxCCIDBusySlots;
+} __packed;
+#endif
diff --git a/include/linux/usb/f_mtp.h b/include/linux/usb/f_mtp.h
index 4e84177..8def143 100644
--- a/include/linux/usb/f_mtp.h
+++ b/include/linux/usb/f_mtp.h
@@ -19,5 +19,35 @@
 #define __LINUX_USB_F_MTP_H
 
 #include <uapi/linux/usb/f_mtp.h>
+#include <linux/ioctl.h>
+#ifdef CONFIG_COMPAT
+#include <linux/compat.h>
+#endif
 
+#ifdef __KERNEL__
+
+#ifdef CONFIG_COMPAT
+struct __compat_mtp_file_range {
+	compat_int_t	fd;
+	compat_loff_t	offset;
+	int64_t		length;
+	uint16_t	command;
+	uint32_t	transaction_id;
+};
+
+struct __compat_mtp_event {
+	compat_size_t	length;
+	compat_caddr_t	data;
+};
+
+#define COMPAT_MTP_SEND_FILE              _IOW('M', 0, \
+						struct __compat_mtp_file_range)
+#define COMPAT_MTP_RECEIVE_FILE           _IOW('M', 1, \
+						struct __compat_mtp_file_range)
+#define COMPAT_MTP_SEND_EVENT             _IOW('M', 3, \
+						struct __compat_mtp_event)
+#define COMPAT_MTP_SEND_FILE_WITH_HEADER  _IOW('M', 4, \
+						struct __compat_mtp_file_range)
+#endif
+#endif
 #endif /* __LINUX_USB_F_MTP_H */
diff --git a/include/linux/usb/usbpd.h b/include/linux/usb/usbpd.h
new file mode 100644
index 0000000..3566a7a
--- /dev/null
+++ b/include/linux/usb/usbpd.h
@@ -0,0 +1,159 @@
+/* Copyright (c) 2016, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __LINUX_USB_USBPD_H
+#define __LINUX_USB_USBPD_H
+
+#include <linux/list.h>
+
+struct usbpd;
+
+/* Standard IDs */
+#define USBPD_SID			0xff00
+
+/* Structured VDM Command Type */
+enum usbpd_svdm_cmd_type {
+	SVDM_CMD_TYPE_INITIATOR,
+	SVDM_CMD_TYPE_RESP_ACK,
+	SVDM_CMD_TYPE_RESP_NAK,
+	SVDM_CMD_TYPE_RESP_BUSY,
+};
+
+/* Structured VDM Commands */
+#define USBPD_SVDM_DISCOVER_IDENTITY	0x1
+#define USBPD_SVDM_DISCOVER_SVIDS	0x2
+#define USBPD_SVDM_DISCOVER_MODES	0x3
+#define USBPD_SVDM_ENTER_MODE		0x4
+#define USBPD_SVDM_EXIT_MODE		0x5
+#define USBPD_SVDM_ATTENTION		0x6
+
+/*
+ * Implemented by client
+ */
+struct usbpd_svid_handler {
+	u16 svid;
+
+	/* Notified when VDM session established/reset; must be implemented */
+	void (*connect)(struct usbpd_svid_handler *hdlr);
+	void (*disconnect)(struct usbpd_svid_handler *hdlr);
+
+	/* Unstructured VDM */
+	void (*vdm_received)(struct usbpd_svid_handler *hdlr, u32 vdm_hdr,
+			const u32 *vdos, int num_vdos);
+
+	/* Structured VDM */
+	void (*svdm_received)(struct usbpd_svid_handler *hdlr, u8 cmd,
+			enum usbpd_svdm_cmd_type cmd_type, const u32 *vdos,
+			int num_vdos);
+
+	/* client should leave these blank; private members used by PD driver */
+	struct list_head entry;
+	bool discovered;
+};
+
+enum plug_orientation {
+	ORIENTATION_NONE,
+	ORIENTATION_CC1,
+	ORIENTATION_CC2,
+};
+
+#if IS_ENABLED(CONFIG_USB_PD_POLICY)
+/*
+ * Obtains an instance of usbpd from a DT phandle
+ */
+struct usbpd *devm_usbpd_get_by_phandle(struct device *dev,
+		const char *phandle);
+
+/*
+ * Called by client to handle specific SVID messages.
+ * Specify callback functions in the usbpd_svid_handler argument
+ */
+int usbpd_register_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr);
+
+void usbpd_unregister_svid(struct usbpd *pd, struct usbpd_svid_handler *hdlr);
+
+/*
+ * Transmit a VDM message.
+ */
+int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos,
+		int num_vdos);
+
+/*
+ * Transmit a Structured VDM message.
+ */
+int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
+		enum usbpd_svdm_cmd_type cmd_type, int obj_pos,
+		const u32 *vdos, int num_vdos);
+
+/*
+ * Get current status of CC pin orientation.
+ *
+ * Return: ORIENTATION_CC1 or ORIENTATION_CC2 if attached,
+ *         otherwise ORIENTATION_NONE if not attached
+ */
+enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd);
+#else
+static inline struct usbpd *devm_usbpd_get_by_phandle(struct device *dev,
+		const char *phandle)
+{
+	return ERR_PTR(-ENODEV);
+}
+
+static inline int usbpd_register_svid(struct usbpd *pd,
+		struct usbpd_svid_handler *hdlr)
+{
+	return -EINVAL;
+}
+
+static inline void usbpd_unregister_svid(struct usbpd *pd,
+		struct usbpd_svid_handler *hdlr)
+{
+}
+
+static inline int usbpd_send_vdm(struct usbpd *pd, u32 vdm_hdr, const u32 *vdos,
+		int num_vdos)
+{
+	return -EINVAL;
+}
+
+static inline int usbpd_send_svdm(struct usbpd *pd, u16 svid, u8 cmd,
+		enum usbpd_svdm_cmd_type cmd_type, int obj_pos,
+		const u32 *vdos, int num_vdos)
+{
+	return -EINVAL;
+}
+
+static inline enum plug_orientation usbpd_get_plug_orientation(struct usbpd *pd)
+{
+	return ORIENTATION_NONE;
+}
+#endif /* IS_ENABLED(CONFIG_USB_PD_POLICY) */
+
+/*
+ * Additional helpers for Enter/Exit Mode commands
+ */
+
+static inline int usbpd_enter_mode(struct usbpd *pd, u16 svid, int mode,
+		const u32 *vdo)
+{
+	return usbpd_send_svdm(pd, svid, USBPD_SVDM_ENTER_MODE,
+			SVDM_CMD_TYPE_INITIATOR, mode, vdo, vdo ? 1 : 0);
+}
+
+static inline int usbpd_exit_mode(struct usbpd *pd, u16 svid, int mode,
+		const u32 *vdo)
+{
+	return usbpd_send_svdm(pd, svid, USBPD_SVDM_EXIT_MODE,
+			SVDM_CMD_TYPE_INITIATOR, mode, vdo, vdo ? 1 : 0);
+}
+
+#endif /* __LINUX_USB_USBPD_H */
diff --git a/include/media/msm_vidc.h b/include/media/msm_vidc.h
new file mode 100644
index 0000000..2a5d963
--- /dev/null
+++ b/include/media/msm_vidc.h
@@ -0,0 +1,122 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _MSM_VIDC_H_
+#define _MSM_VIDC_H_
+
+#include <linux/poll.h>
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/msm_ion.h>
+#include <uapi/media/msm_vidc.h>
+
+#define HAL_BUFFER_MAX 0xb
+
+enum smem_type {
+	SMEM_ION,
+};
+
+enum smem_prop {
+	SMEM_CACHED,
+	SMEM_SECURE,
+};
+
+/* NOTE: if you change this enum you MUST update the
+ * "buffer-type-tz-usage-table" for any affected target
+ * in arch/arm/boot/dts/<arch>.dtsi
+ */
+enum hal_buffer {
+	HAL_BUFFER_NONE = 0x0,
+	HAL_BUFFER_INPUT = 0x1,
+	HAL_BUFFER_OUTPUT = 0x2,
+	HAL_BUFFER_OUTPUT2 = 0x4,
+	HAL_BUFFER_EXTRADATA_INPUT = 0x8,
+	HAL_BUFFER_EXTRADATA_OUTPUT = 0x10,
+	HAL_BUFFER_EXTRADATA_OUTPUT2 = 0x20,
+	HAL_BUFFER_INTERNAL_SCRATCH = 0x40,
+	HAL_BUFFER_INTERNAL_SCRATCH_1 = 0x80,
+	HAL_BUFFER_INTERNAL_SCRATCH_2 = 0x100,
+	HAL_BUFFER_INTERNAL_PERSIST = 0x200,
+	HAL_BUFFER_INTERNAL_PERSIST_1 = 0x400,
+	HAL_BUFFER_INTERNAL_CMD_QUEUE = 0x800,
+};
+
+struct dma_mapping_info {
+	struct device *dev;
+	struct dma_iommu_mapping *mapping;
+	struct sg_table *table;
+	struct dma_buf_attachment *attach;
+	struct dma_buf *buf;
+};
+
+struct msm_smem {
+	int mem_type;
+	size_t size;
+	void *kvaddr;
+	ion_phys_addr_t device_addr;
+	unsigned long flags;
+	void *smem_priv;
+	enum hal_buffer buffer_type;
+	struct dma_mapping_info mapping_info;
+	unsigned int offset;
+};
+
+enum smem_cache_ops {
+	SMEM_CACHE_CLEAN,
+	SMEM_CACHE_INVALIDATE,
+	SMEM_CACHE_CLEAN_INVALIDATE,
+};
+
+enum core_id {
+	MSM_VIDC_CORE_VENUS = 0,
+	MSM_VIDC_CORE_Q6,
+	MSM_VIDC_CORES_MAX,
+};
+enum session_type {
+	MSM_VIDC_ENCODER = 0,
+	MSM_VIDC_DECODER,
+	MSM_VIDC_UNKNOWN,
+	MSM_VIDC_MAX_DEVICES = MSM_VIDC_UNKNOWN,
+};
+
+union msm_v4l2_cmd {
+	struct v4l2_decoder_cmd dec;
+	struct v4l2_encoder_cmd enc;
+};
+
+void *msm_vidc_open(int core_id, int session_type);
+int msm_vidc_close(void *instance);
+int msm_vidc_suspend(int core_id);
+int msm_vidc_querycap(void *instance, struct v4l2_capability *cap);
+int msm_vidc_enum_fmt(void *instance, struct v4l2_fmtdesc *f);
+int msm_vidc_s_fmt(void *instance, struct v4l2_format *f);
+int msm_vidc_g_fmt(void *instance, struct v4l2_format *f);
+int msm_vidc_s_ctrl(void *instance, struct v4l2_control *a);
+int msm_vidc_s_ext_ctrl(void *instance, struct v4l2_ext_controls *a);
+int msm_vidc_g_ctrl(void *instance, struct v4l2_control *a);
+int msm_vidc_reqbufs(void *instance, struct v4l2_requestbuffers *b);
+int msm_vidc_release_buffers(void *instance, int buffer_type);
+int msm_vidc_qbuf(void *instance, struct v4l2_buffer *b);
+int msm_vidc_dqbuf(void *instance, struct v4l2_buffer *b);
+int msm_vidc_streamon(void *instance, enum v4l2_buf_type i);
+int msm_vidc_streamoff(void *instance, enum v4l2_buf_type i);
+int msm_vidc_comm_cmd(void *instance, union msm_v4l2_cmd *cmd);
+int msm_vidc_poll(void *instance, struct file *filp,
+		struct poll_table_struct *pt);
+int msm_vidc_subscribe_event(void *instance,
+		const struct v4l2_event_subscription *sub);
+int msm_vidc_unsubscribe_event(void *instance,
+		const struct v4l2_event_subscription *sub);
+int msm_vidc_dqevent(void *instance, struct v4l2_event *event);
+int msm_vidc_enum_framesizes(void *instance, struct v4l2_frmsizeenum *fsize);
+#endif
diff --git a/include/soc/qcom/icnss.h b/include/soc/qcom/icnss.h
index 7e2f328..6b567d7 100644
--- a/include/soc/qcom/icnss.h
+++ b/include/soc/qcom/icnss.h
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -104,7 +104,7 @@ extern int icnss_ce_request_irq(unsigned int ce_id,
 	irqreturn_t (*handler)(int, void *),
 	unsigned long flags, const char *name, void *ctx);
 extern int icnss_get_ce_id(int irq);
-extern int icnss_set_fw_debug_mode(bool enable_fw_log);
+extern int icnss_set_fw_log_mode(uint8_t fw_log_mode);
 extern int icnss_athdiag_read(struct device *dev, uint32_t offset,
 			      uint32_t mem_type, uint32_t data_len,
 			      uint8_t *output);
@@ -124,5 +124,9 @@ extern int icnss_get_wlan_unsafe_channel(u16 *unsafe_ch_list, u16 *ch_count,
 extern int icnss_wlan_set_dfs_nol(const void *info, u16 info_len);
 extern int icnss_wlan_get_dfs_nol(void *info, u16 info_len);
 extern bool icnss_is_qmi_disable(void);
+extern bool icnss_is_fw_ready(void);
+extern int icnss_set_wlan_mac_address(const u8 *in, const uint32_t len);
+extern u8 *icnss_get_wlan_mac_address(struct device *dev, uint32_t *num);
+extern int icnss_trigger_recovery(struct device *dev);
 
 #endif /* _ICNSS_WLAN_H_ */
diff --git a/include/soc/qcom/liquid_dock.h b/include/soc/qcom/liquid_dock.h
new file mode 100644
index 0000000..3668224
--- /dev/null
+++ b/include/soc/qcom/liquid_dock.h
@@ -0,0 +1,21 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/notifier.h>
+
+#if IS_ENABLED(CONFIG_QCOM_LIQUID_DOCK)
+void register_liquid_dock_notify(struct notifier_block *nb);
+void unregister_liquid_dock_notify(struct notifier_block *nb);
+#else
+static inline void register_liquid_dock_notify(struct notifier_block *nb) { }
+static inline void unregister_liquid_dock_notify(struct notifier_block *nb) { }
+#endif
diff --git a/include/soc/qcom/spcom.h b/include/soc/qcom/spcom.h
new file mode 100644
index 0000000..31369a5
--- /dev/null
+++ b/include/soc/qcom/spcom.h
@@ -0,0 +1,223 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _SPCOM_H_
+#define _SPCOM_H_
+
+#include <linux/types.h>	/* uint32_t ,bool */
+
+/**
+ * @brief - Secure Processor Communication API
+ *
+ * This API should be used by Linux Kernel drivers,
+ * similar API is provided to user space applications
+ * via spcomlib.h API file.
+ * Sending Request and receiving Response is synchronous, only one at a time.
+ * The API is based on Client/Server model.
+ * The API resemble the trustzone QSEECOM API.
+ * In most cases, the Secure Processor side has servers and the HLOS
+ * side has clients. Request is initiated by the client and responded by the
+ * server.
+ */
+
+/*===========================================================================*/
+/*                           defines, enums , types                          */
+/*===========================================================================*/
+
+/* Maximum size (including null) for channel names - match glink */
+#define SPCOM_CHANNEL_NAME_SIZE		32
+
+/**
+ * Request buffer size.
+ * Any large data (multiply of 4KB) is provided by temp buffer in DDR.
+ * Request shall provide the temp buffer physical address (align to 4KB).
+ * Maximum request/response size of 268 is used to accommodate APDU size.
+ * From kernel spcom driver perspective a PAGE_SIZE of 4K
+ * is the actual maximum size for a single read/write file operation.
+ */
+#define SPCOM_MAX_REQUEST_SIZE		268
+#define SPCOM_MAX_RESPONSE_SIZE		268
+
+/**
+ * Abstract spcom handle.
+ * The actual struct definition is internal to the spcom driver.
+ */
+struct spcom_client; /* Forward declaration */
+struct spcom_server; /* Forward declaration */
+
+/**
+ * Client registration info
+ *
+ * @ch_name:	glink logical channel name
+ * @notify_ssr_cb: callback when the remote SP side reset (power down).
+ *      This is likely to happen due to remote subsystem restart (SSR).
+ *      NULL callback means no notification required.
+ *      Upon ssr callback, the user should unregister,
+ *      Poll for link up and then register again.
+ */
+struct spcom_client_info {
+	const char *ch_name;
+	void (*notify_ssr_cb)(void);
+};
+
+/**
+ * Server registration info
+ *
+ * @ch_name:	glink logical channel name
+ * @notify_ssr_cb: callback when the remote SP side reset (power down).
+ *      This is likely to happen due to remote subsystem restart (SSR).
+ *      NULL callback means no notification required.
+ *      Upon ssr callback, the user should unregister,
+ *      Poll for link up and then register again.
+ */
+struct spcom_service_info {
+	const char *ch_name;
+	void (*notify_ssr_cb)(void);
+};
+
+/*===========================================================================*/
+/*                           General API                                     */
+/*===========================================================================*/
+
+/**
+ * spcom_is_sp_subsystem_link_up() - check if SPSS link is up.
+ *
+ * return: true if link is up, false if link is down.
+ */
+bool spcom_is_sp_subsystem_link_up(void);
+
+/*===========================================================================*/
+/*                           Client Send Message                             */
+/*===========================================================================*/
+/**
+ * spcom_register_client() - register client for channel
+ *
+ * Only one client/Server can register on each side of a channel.
+ * Server on remote side is expected to be running and connected,
+ * therefore connection expected within the provided timeout.
+ * Handle is returned even if timeout expired.
+ * use spcom_client_is_server_connected() to check fully connected.
+ *
+ * @info:	Client configuration info (input).
+ *
+ * return: client handle on success, NULL on failure.
+ */
+struct spcom_client *spcom_register_client(struct spcom_client_info *info);
+
+/**
+ * spcom_unregister_client() - unregister client for channel
+ *
+ * @client:	Client Handle.
+ *
+ * return: 0 on success, negative error code on failure (see errno.h)
+ */
+int spcom_unregister_client(struct spcom_client *client);
+
+/**
+ * spcom_client_send_message_sync() - Send a synchronous request and response
+ *
+ * @client:	a pointer to spcom client
+ * @req_ptr:	a pointer to the request C struct representation
+ * @req_size:	size of the request C struct
+ * @resp_ptr:	a pointer to the response C struct representation
+ * @resp_size:  size of the response C struct
+ * @timeout_msec: Timeout in msec between command and response, 0=no timeout.
+ *
+ * return: number of rx bytes on success, negative value on failure.
+ */
+int spcom_client_send_message_sync(struct spcom_client	*client,
+				   void			*req_ptr,
+				   uint32_t		req_size,
+				   void			*resp_ptr,
+				   uint32_t		resp_size,
+				   uint32_t		timeout_msec);
+
+/**
+ * spcom_client_is_server_connected() - Check if remote server connected.
+ *
+ * This API checks that the logical channel is fully connected between
+ * the client and the server.
+ * Normally, the server should be up first and connect first.
+ *
+ * @client:	a pointer to spcom client
+ *
+ * return: true if server connected, false otherwise.
+ */
+bool spcom_client_is_server_connected(struct spcom_client *client);
+
+/*===========================================================================*/
+/*                           Service                                         */
+/*===========================================================================*/
+
+/**
+ * spcom_register_service() - register server for channel
+ *
+ * Only one client/Server can register on each side of a channel.
+ *
+ * @info:	Server configuration info (input).
+ *
+ * return: server handle on success, NULL on failure.
+ */
+struct spcom_server *spcom_register_service(struct spcom_service_info *info);
+
+/**
+ * spcom_unregister_service() - unregister server for channel
+ *
+ * @server:	server Handle.
+ *
+ * return: 0 on success, negative error code on failure (see errno.h)
+ */
+int spcom_unregister_service(struct spcom_server *server);
+
+/**
+ * spcom_server_get_next_request_size() - get the size of the
+ * next request
+ *
+ * This API MUST be called before calling spcom_server_wait_for_request().
+ * The server should allocate the relevant buffer size.
+ *
+ * @server:	a pointer to spcom server
+ *
+ * return: size of request in bytes on success, negative value on failure.
+ */
+int spcom_server_get_next_request_size(struct spcom_server *server);
+
+/**
+ * spcom_server_wait_for_request() - server wait for request
+ *
+ * @server:     a pointer to spcom server
+ * @req_ptr:	a pointer to the request buffer
+ * @req_size:	size of the buffer provided.
+ * The server should provide a buffer of at least the size
+ * returned by spcom_server_get_next_request_size() and up to
+ * SPCOM_MAX_REQUEST_SIZE.
+ *
+ * return: size of request on success, negative value on failure (see errno.h)
+ */
+int spcom_server_wait_for_request(struct spcom_server	*server,
+				  void			*req_ptr,
+				  uint32_t		req_size);
+
+/**
+ * spcom_server_send_response() - Send a the response to request
+ *
+ * @server:	a pointer to spcom server
+ * @resp_ptr:	a pointer to the response C struct representation
+ * @resp_size:  size of the response C struct
+ *
+ * return: sent data size on success, negative value on failure (see errno.h)
+ */
+int spcom_server_send_response(struct spcom_server	*server,
+			       void			*resp_ptr,
+			       uint32_t		resp_size);
+
+#endif /* _SPCOM_H_ */
diff --git a/include/soc/qcom/subsystem_restart.h b/include/soc/qcom/subsystem_restart.h
index a32de45..9ea0736 100644
--- a/include/soc/qcom/subsystem_restart.h
+++ b/include/soc/qcom/subsystem_restart.h
@@ -25,6 +25,12 @@ enum {
 	RESET_LEVEL_MAX
 };
 
+enum crash_status {
+	CRASH_STATUS_NO_CRASH = 0,
+	CRASH_STATUS_ERR_FATAL,
+	CRASH_STATUS_WDOG_BITE,
+};
+
 struct device;
 struct module;
 
@@ -89,7 +95,7 @@ struct subsys_desc {
 
 /**
  * struct notif_data - additional notif information
- * @crashed: indicates if subsystem has crashed
+ * @crashed: indicates if subsystem has crashed due to wdog bite or err fatal
  * @enable_ramdump: ramdumps disabled if set to 0
  * @enable_mini_ramdumps: enable flag for minimized critical-memory-only
  * ramdumps
@@ -97,7 +103,7 @@ struct subsys_desc {
  * @pdev: subsystem platform device pointer
  */
 struct notif_data {
-	bool crashed;
+	enum crash_status crashed;
 	int enable_ramdump;
 	int enable_mini_ramdumps;
 	bool no_auth;
@@ -120,8 +126,9 @@ extern struct subsys_device *subsys_register(struct subsys_desc *desc);
 extern void subsys_unregister(struct subsys_device *dev);
 
 extern void subsys_default_online(struct subsys_device *dev);
-extern void subsys_set_crash_status(struct subsys_device *dev, bool crashed);
-extern bool subsys_get_crash_status(struct subsys_device *dev);
+extern void subsys_set_crash_status(struct subsys_device *dev,
+					enum crash_status crashed);
+extern enum crash_status subsys_get_crash_status(struct subsys_device *dev);
 void notify_proxy_vote(struct device *device);
 void notify_proxy_unvote(struct device *device);
 void complete_err_ready(struct subsys_device *subsys);
@@ -174,9 +181,10 @@ struct subsys_device *subsys_register(struct subsys_desc *desc)
 static inline void subsys_unregister(struct subsys_device *dev) { }
 
 static inline void subsys_default_online(struct subsys_device *dev) { }
+static inline void subsys_set_crash_status(struct subsys_device *dev,
+						enum crash_status crashed) { }
 static inline
-void subsys_set_crash_status(struct subsys_device *dev, bool crashed) { }
-static inline bool subsys_get_crash_status(struct subsys_device *dev)
+enum crash_status subsys_get_crash_status(struct subsys_device *dev)
 {
 	return false;
 }
diff --git a/include/sound/adsp_err.h b/include/sound/adsp_err.h
new file mode 100644
index 0000000..43be91d
--- /dev/null
+++ b/include/sound/adsp_err.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __ADSP_ERR__
+#define __ADSP_ERR__
+
+int adsp_err_get_lnx_err_code(u32 adsp_error);
+
+char *adsp_err_get_err_str(u32 adsp_error);
+
+#endif
diff --git a/include/sound/apr_audio-v2.h b/include/sound/apr_audio-v2.h
new file mode 100644
index 0000000..812ea65
--- /dev/null
+++ b/include/sound/apr_audio-v2.h
@@ -0,0 +1,9901 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#ifndef _APR_AUDIO_V2_H_
+#define _APR_AUDIO_V2_H_
+
+#include <linux/qdsp6v2/apr.h>
+
+/* size of header needed for passing data out of band */
+#define APR_CMD_OB_HDR_SZ  12
+
+/* size of header needed for getting data */
+#define APR_CMD_GET_HDR_SZ 16
+
+struct param_outband {
+	size_t       size;
+	void        *kvaddr;
+	phys_addr_t  paddr;
+};
+
+#define ADSP_ADM_VERSION    0x00070000
+
+#define ADM_CMD_SHARED_MEM_MAP_REGIONS    0x00010322
+#define ADM_CMDRSP_SHARED_MEM_MAP_REGIONS 0x00010323
+#define ADM_CMD_SHARED_MEM_UNMAP_REGIONS 0x00010324
+
+#define ADM_CMD_MATRIX_MAP_ROUTINGS_V5 0x00010325
+#define ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5 0x0001033D
+/* Enumeration for an audio Rx matrix ID.*/
+#define ADM_MATRIX_ID_AUDIO_RX              0
+
+#define ADM_MATRIX_ID_AUDIO_TX              1
+
+#define ADM_MATRIX_ID_COMPRESSED_AUDIO_RX   2
+/* Enumeration for an audio Tx matrix ID.*/
+#define ADM_MATRIX_ID_AUDIOX              1
+
+#define ADM_MAX_COPPS 5
+
+/* make sure this matches with msm_audio_calibration */
+#define SP_V2_NUM_MAX_SPKR 2
+
+/* Session map node structure.
+ * Immediately following this structure are num_copps
+ * entries of COPP IDs. The COPP IDs are 16 bits, so
+ * there might be a padding 16-bit field if num_copps
+ * is odd.
+ */
+struct adm_session_map_node_v5 {
+	u16                  session_id;
+	/* Handle of the ASM session to be routed. Supported values: 1
+	 * to 8.
+	 */
+
+
+	u16                  num_copps;
+	/* Number of COPPs to which this session is to be routed.
+	 * Supported values: 0 < num_copps <= ADM_MAX_COPPS.
+	 */
+} __packed;
+
+/*  Payload of the #ADM_CMD_MATRIX_MAP_ROUTINGS_V5 command.
+ *	Immediately following this structure are num_sessions of the session map
+ *	node payload (adm_session_map_node_v5).
+ */
+
+struct adm_cmd_matrix_map_routings_v5 {
+	struct apr_hdr	hdr;
+
+	u32                  matrix_id;
+	/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx
+	 * (1). Use the ADM_MATRIX_ID_AUDIO_RX or ADM_MATRIX_ID_AUDIOX
+	 * macros to set this field.
+	 */
+	u32                  num_sessions;
+	/* Number of sessions being updated by this command (optional). */
+} __packed;
+
+/* This command allows a client to open a COPP/Voice Proc. TX module
+ * and sets up the device session: Matrix -> COPP -> AFE on the RX
+ * and AFE -> COPP -> Matrix on the TX. This enables PCM data to
+ * be transferred to/from the endpoint (AFEPortID).
+ *
+ * @return
+ * #ADM_CMDRSP_DEVICE_OPEN_V5 with the resulting status and COPP ID.
+ */
+#define ADM_CMD_DEVICE_OPEN_V5                          0x00010326
+
+/* Definition for a low latency stream session. */
+#define ADM_LOW_LATENCY_DEVICE_SESSION			0x2000
+
+/* Definition for a ultra low latency stream session. */
+#define ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION		0x4000
+
+/* Definition for a ultra low latency with Post Processing stream session. */
+#define ADM_ULL_POST_PROCESSING_DEVICE_SESSION		0x8000
+
+/* Definition for a legacy device session. */
+#define ADM_LEGACY_DEVICE_SESSION                                      0
+
+/* Indicates that endpoint_id_2 is to be ignored.*/
+#define ADM_CMD_COPP_OPEN_END_POINT_ID_2_IGNORE				0xFFFF
+
+#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_RX_PATH_COPP		 1
+
+#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATIONX_PATH_LIVE_COPP		 2
+
+#define ADM_CMD_COPP_OPEN_MODE_OF_OPERATIONX_PATH_NON_LIVE_COPP	 3
+
+/* Indicates that an audio COPP is to send/receive a mono PCM
+ * stream to/from
+ *	END_POINT_ID_1.
+ */
+#define ADM_CMD_COPP_OPEN_CHANNEL_CONFIG_MONO		1
+
+/* Indicates that an audio COPP is to send/receive a
+ *	stereo PCM stream to/from END_POINT_ID_1.
+ */
+#define ADM_CMD_COPP_OPEN_CHANNEL_CONFIG_STEREO		2
+
+/* Sample rate is 8000 Hz.*/
+#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_8K 8000
+
+/* Sample rate is 16000 Hz.*/
+#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_16K 16000
+
+/* Sample rate is 48000 Hz.*/
+#define ADM_CMD_COPP_OPEN_SAMPLE_RATE_48K 48000
+
+/* Definition for a COPP live input flag bitmask.*/
+#define ADM_BIT_MASK_COPP_LIVE_INPUT_FLAG (0x0001U)
+
+/* Definition for a COPP live shift value bitmask.*/
+#define ADM_SHIFT_COPP_LIVE_INPUT_FLAG	 0
+
+/* Definition for the COPP ID bitmask.*/
+#define ADM_BIT_MASK_COPP_ID  (0x0000FFFFUL)
+
+/* Definition for the COPP ID shift value.*/
+#define ADM_SHIFT_COPP_ID	0
+
+/* Definition for the service ID bitmask.*/
+#define ADM_BIT_MASK_SERVICE_ID  (0x00FF0000UL)
+
+/* Definition for the service ID shift value.*/
+#define ADM_SHIFT_SERVICE_ID	16
+
+/* Definition for the domain ID bitmask.*/
+#define ADM_BIT_MASK_DOMAIN_ID    (0xFF000000UL)
+
+/* Definition for the domain ID shift value.*/
+#define ADM_SHIFT_DOMAIN_ID	24
+
+/* ADM device open command payload of the
+ * #ADM_CMD_DEVICE_OPEN_V5 command.
+ */
+struct adm_cmd_device_open_v5 {
+	struct apr_hdr		hdr;
+	u16                  flags;
+/* Reserved for future use. Clients must set this field
+ * to zero.
+ */
+
+	u16                  mode_of_operation;
+/* Specifies whether the COPP must be opened on the Tx or Rx
+ * path. Use the ADM_CMD_COPP_OPEN_MODE_OF_OPERATION_* macros for
+ * supported values and interpretation.
+ * Supported values:
+ * - 0x1 -- Rx path COPP
+ * - 0x2 -- Tx path live COPP
+ * - 0x3 -- Tx path nonlive COPP
+ * Live connections cause sample discarding in the Tx device
+ * matrix if the destination output ports do not pull them
+ * fast enough. Nonlive connections queue the samples
+ * indefinitely.
+ */
+
+	u16                  endpoint_id_1;
+/* Logical and physical endpoint ID of the audio path.
+ * If the ID is a voice processor Tx block, it receives near
+ * samples.	Supported values: Any pseudoport, AFE Rx port,
+ * or AFE Tx port For a list of valid IDs, refer to
+ * @xhyperref{Q4,[Q4]}.
+ * Q4 = Hexagon Multimedia: AFE Interface Specification
+ */
+
+	u16                  endpoint_id_2;
+/* Logical and physical endpoint ID 2 for a voice processor
+ * Tx block.
+ * This is not applicable to audio COPP.
+ * Supported values:
+ * - AFE Rx port
+ * - 0xFFFF -- Endpoint 2 is unavailable and the voice
+ * processor Tx
+ * block ignores this endpoint
+ * When the voice processor Tx block is created on the audio
+ * record path,
+ * it can receive far-end samples from an AFE Rx port if the
+ * voice call
+ * is active. The ID of the AFE port is provided in this
+ * field.
+ * For a list of valid IDs, refer @xhyperref{Q4,[Q4]}.
+ */
+
+	u32                  topology_id;
+/* Audio COPP topology ID; 32-bit GUID. */
+
+	u16                  dev_num_channel;
+/* Number of channels the audio COPP sends to/receives from
+ * the endpoint.
+ * Supported values: 1 to 8.
+ * The value is ignored for the voice processor Tx block,
+ * where channel
+ * configuration is derived from the topology ID.
+ */
+
+	u16                  bit_width;
+/* Bit width (in bits) that the audio COPP sends to/receives
+ * from the
+ * endpoint. The value is ignored for the voice processing
+ * Tx block,
+ * where the PCM width is 16 bits.
+ */
+
+	u32                  sample_rate;
+/* Sampling rate at which the audio COPP/voice processor
+ * Tx block
+ * interfaces with the endpoint.
+ * Supported values for voice processor Tx: 8000, 16000,
+ * 48000 Hz
+ * Supported values for audio COPP: >0 and <=192 kHz
+ */
+
+	u8                   dev_channel_mapping[8];
+/* Array of channel mapping of buffers that the audio COPP
+ * sends to the endpoint. Channel[i] mapping describes channel
+ * I inside the buffer, where 0 < i < dev_num_channel.
+ * This value is relevant only for an audio Rx COPP.
+ * For the voice processor block and Tx audio block, this field
+ * is set to zero and is ignored.
+ */
+} __packed;
+
+/*
+ *	This command allows the client to close a COPP and disconnect
+ *	the device session.
+ */
+#define ADM_CMD_DEVICE_CLOSE_V5                         0x00010327
+
+/* Sets one or more parameters to a COPP. */
+#define ADM_CMD_SET_PP_PARAMS_V5                        0x00010328
+
+/*  Payload of the #ADM_CMD_SET_PP_PARAMS_V5 command.
+ *	If the data_payload_addr_lsw and data_payload_addr_msw element
+ *	are NULL, a series of adm_param_datastructures immediately
+ *	follows, whose total size is data_payload_size bytes.
+ */
+struct adm_cmd_set_pp_params_v5 {
+	struct apr_hdr hdr;
+	u32		payload_addr_lsw;
+/* LSW of parameter data payload address. */
+	u32		payload_addr_msw;
+/* MSW of parameter data payload address. */
+
+	u32		mem_map_handle;
+/* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS
+ * command
+ *
+ * If mem_map_handle is zero implies the message is in
+ * the payload
+ */
+
+	u32		payload_size;
+/* Size in bytes of the variable payload accompanying this
+ * message or
+ * in shared memory. This is used for parsing the parameter
+ * payload.
+ */
+} __packed;
+
+/*  Payload format for COPP parameter data.
+ * Immediately following this structure are param_size bytes
+ * of parameter
+ * data.
+ */
+struct adm_param_data_v5 {
+	u32                  module_id;
+	/* Unique ID of the module. */
+	u32                  param_id;
+	/* Unique ID of the parameter. */
+	u16                  param_size;
+	/* Data size of the param_id/module_id combination.
+	 * This value is a
+	 * multiple of 4 bytes.
+	 */
+	u16                  reserved;
+	/* Reserved for future enhancements.
+	 * This field must be set to zero.
+	 */
+} __packed;
+
+/* set customized mixing on matrix mixer */
+#define ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5                        0x00010344
+struct adm_cmd_set_pspd_mtmx_strtr_params_v5 {
+	struct apr_hdr hdr;
+	/* LSW of parameter data payload address.*/
+	u32		payload_addr_lsw;
+	/* MSW of parameter data payload address.*/
+	u32		payload_addr_msw;
+	/* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */
+	/* command. If mem_map_handle is zero implies the message is in */
+	/* the payload */
+	u32		mem_map_handle;
+	/* Size in bytes of the variable payload accompanying this */
+	/* message or in shared memory. This is used for parsing the */
+	/* parameter payload. */
+	u32		payload_size;
+	u16		direction;
+	u16		sessionid;
+	u16		deviceid;
+	u16		reserved;
+} __packed;
+
+/* Defined specifically for in-band use, includes params */
+struct adm_cmd_set_pp_params_inband_v5 {
+	struct apr_hdr hdr;
+	/* LSW of parameter data payload address.*/
+	u32             payload_addr_lsw;
+	/* MSW of parameter data payload address.*/
+	u32             payload_addr_msw;
+	/* Memory map handle returned by ADM_CMD_SHARED_MEM_MAP_REGIONS */
+	/* command. If mem_map_handle is zero implies the message is in */
+	/* the payload */
+	u32             mem_map_handle;
+	/* Size in bytes of the variable payload accompanying this */
+	/* message or in shared memory. This is used for parsing the */
+	/* parameter payload. */
+	u32             payload_size;
+	/* Parameters passed for in band payload */
+	struct adm_param_data_v5        params;
+} __packed;
+
+/* Returns the status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command.
+ */
+#define ADM_CMDRSP_DEVICE_OPEN_V5                      0x00010329
+
+/*  Payload of the #ADM_CMDRSP_DEVICE_OPEN_V5 message,
+ *	which returns the
+ *	status and COPP ID to an #ADM_CMD_DEVICE_OPEN_V5 command.
+ */
+struct adm_cmd_rsp_device_open_v5 {
+	u32                  status;
+	/* Status message (error code).*/
+
+	u16                  copp_id;
+	/* COPP ID:  Supported values: 0 <= copp_id < ADM_MAX_COPPS*/
+
+	u16                  reserved;
+	/* Reserved. This field must be set to zero.*/
+} __packed;
+
+/* This command allows a query of one COPP parameter. */
+#define ADM_CMD_GET_PP_PARAMS_V5                                0x0001032A
+
+/*  Payload an #ADM_CMD_GET_PP_PARAMS_V5 command. */
+struct adm_cmd_get_pp_params_v5 {
+	struct apr_hdr hdr;
+	u32                  data_payload_addr_lsw;
+	/* LSW of parameter data payload address.*/
+
+	u32                  data_payload_addr_msw;
+	/* MSW of parameter data payload address.*/
+
+	/* If the mem_map_handle is non zero,
+	 * on ACK, the ParamData payloads begin at
+	 * the address specified (out-of-band).
+	 */
+
+	u32                  mem_map_handle;
+	/* Memory map handle returned
+	 * by ADM_CMD_SHARED_MEM_MAP_REGIONS command.
+	 * If the mem_map_handle is 0, it implies that
+	 * the ACK's payload will contain the ParamData (in-band).
+	 */
+
+	u32                  module_id;
+	/* Unique ID of the module. */
+
+	u32                  param_id;
+	/* Unique ID of the parameter. */
+
+	u16                  param_max_size;
+	/* Maximum data size of the parameter
+	 *ID/module ID combination. This
+	 * field is a multiple of 4 bytes.
+	 */
+	u16                  reserved;
+	/* Reserved for future enhancements.
+	 * This field must be set to zero.
+	 */
+} __packed;
+
+/* Returns parameter values
+ *	in response to an #ADM_CMD_GET_PP_PARAMS_V5 command.
+ */
+#define ADM_CMDRSP_GET_PP_PARAMS_V5		0x0001032B
+
+/* Payload of the #ADM_CMDRSP_GET_PP_PARAMS_V5 message,
+ * which returns parameter values in response
+ * to an #ADM_CMD_GET_PP_PARAMS_V5 command.
+ * Immediately following this
+ * structure is the adm_param_data_v5
+ * structure containing the pre/postprocessing
+ * parameter data. For an in-band
+ * scenario, the variable payload depends
+ * on the size of the parameter.
+ */
+struct adm_cmd_rsp_get_pp_params_v5 {
+	u32                  status;
+	/* Status message (error code).*/
+} __packed;
+
+/* Structure for holding soft stepping volume parameters. */
+
+/*
+ * Payload of the #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS
+ * parameters used by the Volume Control module.
+ */
+
+struct audproc_softvolume_params {
+	u32 period;
+	u32 step;
+	u32 rampingcurve;
+} __packed;
+
+/*
+ * ID of the Media Format Converter (MFC) module.
+ * This module supports the following parameter IDs:
+ * #AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT
+ * #AUDPROC_CHMIXER_PARAM_ID_COEFF
+ */
+#define AUDPROC_MODULE_ID_MFC                               0x00010912
+
+/* ID of the Output Media Format parameters used by AUDPROC_MODULE_ID_MFC.
+ *
+ */
+#define AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT            0x00010913
+
+
+struct audproc_mfc_output_media_fmt {
+	struct adm_cmd_set_pp_params_v5 params;
+	struct adm_param_data_v5 data;
+	uint32_t sampling_rate;
+	uint16_t bits_per_sample;
+	uint16_t num_channels;
+	uint16_t channel_type[8];
+} __packed;
+
+struct audproc_volume_ctrl_master_gain {
+	struct adm_cmd_set_pp_params_v5 params;
+	struct adm_param_data_v5 data;
+	/* Linear gain in Q13 format. */
+	uint16_t                  master_gain;
+	/* Clients must set this field to zero. */
+	uint16_t                  reserved;
+} __packed;
+
+struct audproc_soft_step_volume_params {
+	struct adm_cmd_set_pp_params_v5 params;
+	struct adm_param_data_v5 data;
+/*
+ * Period in milliseconds.
+ * Supported values: 0 to 15000
+ */
+	uint32_t                  period;
+/*
+ * Step in microseconds.
+ * Supported values: 0 to 15000000
+ */
+	uint32_t                  step;
+/*
+ * Ramping curve type.
+ * Supported values:
+ * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_LINEAR
+ * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_EXP
+ * - #AUDPROC_PARAM_SVC_RAMPINGCURVE_LOG
+ */
+	uint32_t                  ramping_curve;
+} __packed;
+
+struct audproc_enable_param_t {
+	struct adm_cmd_set_pp_params_inband_v5 pp_params;
+	/*
+	 * Specifies whether the Audio processing module is enabled.
+	 * This parameter is generic/common parameter to configure or
+	 * determine the state of any audio processing module.
+
+	 * @values 0 : Disable 1: Enable
+	 */
+	uint32_t                  enable;
+};
+
+/*
+ * Allows a client to control the gains on various session-to-COPP paths.
+ */
+#define ADM_CMD_MATRIX_RAMP_GAINS_V5                                 0x0001032C
+
+/* Indicates that the target gain in the
+ *	current adm_session_copp_gain_v5
+ *	structure is to be applied to all
+ *	the session-to-COPP paths that exist for
+ *	the specified session.
+ */
+#define ADM_CMD_MATRIX_RAMP_GAINS_COPP_ID_ALL_CONNECTED_COPPS     0xFFFF
+
+/* Indicates that the target gain is
+ * to be immediately applied to the
+ * specified session-to-COPP path,
+ * without a ramping fashion.
+ */
+#define ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE         0x0000
+
+/* Enumeration for a linear ramping curve.*/
+#define ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR               0x0000
+
+/*  Payload of the #ADM_CMD_MATRIX_RAMP_GAINS_V5 command.
+ * Immediately following this structure are num_gains of the
+ * adm_session_copp_gain_v5structure.
+ */
+struct adm_cmd_matrix_ramp_gains_v5 {
+	u32                  matrix_id;
+/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1).
+ * Use the ADM_MATRIX_ID_AUDIO_RX or  ADM_MATRIX_ID_AUDIOX
+ * macros to set this field.
+ */
+
+	u16                  num_gains;
+	/* Number of gains being applied. */
+
+	u16                  reserved_for_align;
+	/* Reserved. This field must be set to zero.*/
+} __packed;
+
+/*  Session-to-COPP path gain structure, used by the
+ *	#ADM_CMD_MATRIX_RAMP_GAINS_V5 command.
+ *	This structure specifies the target
+ *	gain (per channel) that must be applied
+ *	to a particular session-to-COPP path in
+ *	the audio matrix. The structure can
+ *	also be used to apply the gain globally
+ *	to all session-to-COPP paths that
+ *	exist for the given session.
+ *	The aDSP uses device channel mapping to
+ *	determine which channel gains to
+ *	use from this command. For example,
+ *	if the device is configured as stereo,
+ *	the aDSP uses only target_gain_ch_1 and
+ *	target_gain_ch_2, and it ignores
+ *	the others.
+ */
+struct adm_session_copp_gain_v5 {
+	u16                  session_id;
+/* Handle of the ASM session.
+ *	Supported values: 1 to 8.
+ */
+
+	u16                  copp_id;
+/* Handle of the COPP. Gain will be applied on the Session ID
+ * COPP ID path.
+ */
+
+	u16                  ramp_duration;
+/* Duration (in milliseconds) of the ramp over
+ * which target gains are
+ * to be applied. Use
+ * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE
+ * to indicate that gain must be applied immediately.
+ */
+
+	u16                  step_duration;
+/* Duration (in milliseconds) of each step in the ramp.
+ * This parameter is ignored if ramp_duration is equal to
+ * #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_DURATION_IMMEDIATE.
+ * Supported value: 1
+ */
+
+	u16                  ramp_curve;
+/* Type of ramping curve.
+ * Supported value: #ADM_CMD_MATRIX_RAMP_GAINS_RAMP_CURVE_LINEAR
+ */
+
+	u16                  reserved_for_align;
+	/* Reserved. This field must be set to zero. */
+
+	u16                  target_gain_ch_1;
+	/* Target linear gain for channel 1 in Q13 format; */
+
+	u16                  target_gain_ch_2;
+	/* Target linear gain for channel 2 in Q13 format; */
+
+	u16                  target_gain_ch_3;
+	/* Target linear gain for channel 3 in Q13 format; */
+
+	u16                  target_gain_ch_4;
+	/* Target linear gain for channel 4 in Q13 format; */
+
+	u16                  target_gain_ch_5;
+	/* Target linear gain for channel 5 in Q13 format; */
+
+	u16                  target_gain_ch_6;
+	/* Target linear gain for channel 6 in Q13 format; */
+
+	u16                  target_gain_ch_7;
+	/* Target linear gain for channel 7 in Q13 format; */
+
+	u16                  target_gain_ch_8;
+	/* Target linear gain for channel 8 in Q13 format; */
+} __packed;
+
+/* Allows to set mute/unmute on various session-to-COPP paths.
+ *	For every session-to-COPP path (stream-device interconnection),
+ *	mute/unmute can be set individually on the output channels.
+ */
+#define ADM_CMD_MATRIX_MUTE_V5                                0x0001032D
+
+/* Indicates that mute/unmute in the
+ *	current adm_session_copp_mute_v5structure
+ *	is to be applied to all the session-to-COPP
+ *	paths that exist for the specified session.
+ */
+#define ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS     0xFFFF
+
+/*  Payload of the #ADM_CMD_MATRIX_MUTE_V5 command*/
+struct adm_cmd_matrix_mute_v5 {
+	u32                  matrix_id;
+/* Specifies whether the matrix ID is Audio Rx (0) or Audio Tx (1).
+ * Use the ADM_MATRIX_ID_AUDIO_RX or  ADM_MATRIX_ID_AUDIOX
+ * macros to set this field.
+ */
+
+	u16                  session_id;
+/* Handle of the ASM session.
+ * Supported values: 1 to 8.
+ */
+
+	u16                  copp_id;
+/* Handle of the COPP.
+ * Use ADM_CMD_MATRIX_MUTE_COPP_ID_ALL_CONNECTED_COPPS
+ * to indicate that mute/unmute must be applied to
+ * all the COPPs connected to session_id.
+ * Supported values:
+ * - 0xFFFF -- Apply mute/unmute to all connected COPPs
+ * - Other values -- Valid COPP ID
+ */
+
+	u8                  mute_flag_ch_1;
+	/* Mute flag for channel 1 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_2;
+	/* Mute flag for channel 2 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_3;
+	/* Mute flag for channel 3 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_4;
+	/* Mute flag for channel 4 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_5;
+	/* Mute flag for channel 5 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_6;
+	/* Mute flag for channel 6 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_7;
+	/* Mute flag for channel 7 is set to unmute (0) or mute (1). */
+
+	u8                  mute_flag_ch_8;
+	/* Mute flag for channel 8 is set to unmute (0) or mute (1). */
+
+	u16                 ramp_duration;
+/* Period (in milliseconds) over which the soft mute/unmute will be
+ * applied.
+ * Supported values: 0 (Default) to 0xFFFF
+ * The default of 0 means mute/unmute will be applied immediately.
+ */
+
+	u16                 reserved_for_align;
+	/* Clients must set this field to zero.*/
+} __packed;
+
+#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2 (0x00010DD8)
+
+struct asm_aac_stereo_mix_coeff_selection_param_v2 {
+	struct apr_hdr          hdr;
+	u32                     param_id;
+	u32                     param_size;
+	u32                     aac_stereo_mix_coeff_flag;
+} __packed;
+
+/* Allows a client to connect the desired stream to
+ * the desired AFE port through the stream router
+ *
+ * This command allows the client to connect specified session to
+ * specified AFE port. This is used for compressed streams only
+ * opened using the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED or
+ * #ASM_STREAM_CMD_OPEN_READ_COMPRESSED command.
+ *
+ * @prerequisites
+ * Session ID and AFE Port ID must be valid.
+ * #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED or
+ * #ASM_STREAM_CMD_OPEN_READ_COMPRESSED
+ * must have been called on this session.
+ */
+
+#define ADM_CMD_CONNECT_AFE_PORT_V5	0x0001032E
+#define ADM_CMD_DISCONNECT_AFE_PORT_V5	0x0001032F
+/* Enumeration for the Rx stream router ID.*/
+#define ADM_STRTR_ID_RX                    0
+/* Enumeration for the Tx stream router ID.*/
+#define ADM_STRTR_IDX                    1
+
+/*  Payload of the #ADM_CMD_CONNECT_AFE_PORT_V5 command.*/
+struct adm_cmd_connect_afe_port_v5 {
+	struct apr_hdr     hdr;
+	u8                  mode;
+/* ID of the stream router (RX/TX). Use the
+ * ADM_STRTR_ID_RX or ADM_STRTR_IDX macros
+ * to set this field.
+ */
+
+	u8                  session_id;
+	/* Session ID of the stream to connect */
+
+	u16                 afe_port_id;
+	/* Port ID of the AFE port to connect to.*/
+	u32                 num_channels;
+/* Number of device channels
+ * Supported values: 2(Audio Sample Packet),
+ * 8 (HBR Audio Stream Sample Packet)
+ */
+
+	u32                 sampling_rate;
+/* Device sampling rate
+ * Supported values: Any
+ */
+} __packed;
+
+
+/* adsp_adm_api.h */
+
+
+/* Port ID. Update afe_get_port_index
+ * when a new port is added here.
+ */
+#define PRIMARY_I2S_RX 0
+#define PRIMARY_I2S_TX 1
+#define SECONDARY_I2S_RX 4
+#define SECONDARY_I2S_TX 5
+#define MI2S_RX 6
+#define MI2S_TX 7
+#define HDMI_RX 8
+#define RSVD_2 9
+#define RSVD_3 10
+#define DIGI_MIC_TX 11
+#define VOICE2_PLAYBACK_TX 0x8002
+#define VOICE_RECORD_RX 0x8003
+#define VOICE_RECORD_TX 0x8004
+#define VOICE_PLAYBACK_TX 0x8005
+
+/* Slimbus Multi channel port id pool  */
+#define SLIMBUS_0_RX		0x4000
+#define SLIMBUS_0_TX		0x4001
+#define SLIMBUS_1_RX		0x4002
+#define SLIMBUS_1_TX		0x4003
+#define SLIMBUS_2_RX		0x4004
+#define SLIMBUS_2_TX		0x4005
+#define SLIMBUS_3_RX		0x4006
+#define SLIMBUS_3_TX		0x4007
+#define SLIMBUS_4_RX		0x4008
+#define SLIMBUS_4_TX		0x4009
+#define SLIMBUS_5_RX		0x400a
+#define SLIMBUS_5_TX		0x400b
+#define SLIMBUS_6_RX		0x400c
+#define SLIMBUS_6_TX		0x400d
+#define SLIMBUS_7_RX		0x400e
+#define SLIMBUS_7_TX		0x400f
+#define SLIMBUS_8_RX		0x4010
+#define SLIMBUS_8_TX		0x4011
+#define SLIMBUS_PORT_LAST	SLIMBUS_8_TX
+#define INT_BT_SCO_RX 0x3000
+#define INT_BT_SCO_TX 0x3001
+#define INT_BT_A2DP_RX 0x3002
+#define INT_FM_RX 0x3004
+#define INT_FM_TX 0x3005
+#define RT_PROXY_PORT_001_RX	0x2000
+#define RT_PROXY_PORT_001_TX	0x2001
+#define DISPLAY_PORT_RX	0x6020
+
+#define AFE_PORT_INVALID 0xFFFF
+#define SLIMBUS_INVALID AFE_PORT_INVALID
+
+#define AFE_PORT_CMD_START 0x000100ca
+
+#define AFE_EVENT_RTPORT_START 0
+#define AFE_EVENT_RTPORT_STOP 1
+#define AFE_EVENT_RTPORT_LOW_WM 2
+#define AFE_EVENT_RTPORT_HI_WM 3
+
+#define ADSP_AFE_VERSION    0x00200000
+
+/* Size of the range of port IDs for the audio interface. */
+#define  AFE_PORT_ID_AUDIO_IF_PORT_RANGE_SIZE	0xF
+
+/* Size of the range of port IDs for internal BT-FM ports. */
+#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_SIZE	0x6
+
+/* Size of the range of port IDs for SLIMbus<sup>&reg;
+ * </sup> multichannel
+ * ports.
+ */
+#define AFE_PORT_ID_SLIMBUS_RANGE_SIZE	0xA
+
+/* Size of the range of port IDs for real-time proxy ports. */
+#define  AFE_PORT_ID_RT_PROXY_PORT_RANGE_SIZE	0x2
+
+/* Size of the range of port IDs for pseudoports. */
+#define AFE_PORT_ID_PSEUDOPORT_RANGE_SIZE	0x5
+
+/* Start of the range of port IDs for the audio interface. */
+#define  AFE_PORT_ID_AUDIO_IF_PORT_RANGE_START	0x1000
+
+/* End of the range of port IDs for the audio interface. */
+#define  AFE_PORT_ID_AUDIO_IF_PORT_RANGE_END \
+	(AFE_PORT_ID_AUDIO_IF_PORT_RANGE_START +\
+	AFE_PORT_ID_AUDIO_IF_PORT_RANGE_SIZE - 1)
+
+/* Start of the range of port IDs for real-time proxy ports. */
+#define  AFE_PORT_ID_RT_PROXY_PORT_RANGE_START	0x2000
+
+/* End of the range of port IDs for real-time proxy ports. */
+#define  AFE_PORT_ID_RT_PROXY_PORT_RANGE_END \
+	(AFE_PORT_ID_RT_PROXY_PORT_RANGE_START +\
+	AFE_PORT_ID_RT_PROXY_PORT_RANGE_SIZE-1)
+
+/* Start of the range of port IDs for internal BT-FM devices. */
+#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_START	0x3000
+
+/* End of the range of port IDs for internal BT-FM devices. */
+#define AFE_PORT_ID_INTERNAL_BT_FM_RANGE_END \
+	(AFE_PORT_ID_INTERNAL_BT_FM_RANGE_START +\
+	AFE_PORT_ID_INTERNAL_BT_FM_RANGE_SIZE-1)
+
+/*	Start of the range of port IDs for SLIMbus devices. */
+#define AFE_PORT_ID_SLIMBUS_RANGE_START	0x4000
+
+/*	End of the range of port IDs for SLIMbus devices. */
+#define AFE_PORT_ID_SLIMBUS_RANGE_END \
+	(AFE_PORT_ID_SLIMBUS_RANGE_START +\
+	AFE_PORT_ID_SLIMBUS_RANGE_SIZE-1)
+
+/* Start of the range of port IDs for pseudoports. */
+#define AFE_PORT_ID_PSEUDOPORT_RANGE_START	0x8001
+
+/* End of the range of port IDs for pseudoports.  */
+#define AFE_PORT_ID_PSEUDOPORT_RANGE_END \
+	(AFE_PORT_ID_PSEUDOPORT_RANGE_START +\
+	AFE_PORT_ID_PSEUDOPORT_RANGE_SIZE-1)
+
+/* Start of the range of port IDs for TDM devices. */
+#define AFE_PORT_ID_TDM_PORT_RANGE_START	0x9000
+
+/* End of the range of port IDs for TDM devices. */
+#define AFE_PORT_ID_TDM_PORT_RANGE_END \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START+0x40-1)
+
+/* Size of the range of port IDs for TDM ports. */
+#define AFE_PORT_ID_TDM_PORT_RANGE_SIZE \
+	(AFE_PORT_ID_TDM_PORT_RANGE_END - \
+	AFE_PORT_ID_TDM_PORT_RANGE_START+1)
+
+#define AFE_PORT_ID_PRIMARY_MI2S_RX         0x1000
+#define AFE_PORT_ID_PRIMARY_MI2S_TX         0x1001
+#define AFE_PORT_ID_SECONDARY_MI2S_RX       0x1002
+#define AFE_PORT_ID_SECONDARY_MI2S_TX       0x1003
+#define AFE_PORT_ID_TERTIARY_MI2S_RX        0x1004
+#define AFE_PORT_ID_TERTIARY_MI2S_TX        0x1005
+#define AFE_PORT_ID_QUATERNARY_MI2S_RX      0x1006
+#define AFE_PORT_ID_QUATERNARY_MI2S_TX      0x1007
+#define AUDIO_PORT_ID_I2S_RX                0x1008
+#define AFE_PORT_ID_DIGITAL_MIC_TX          0x1009
+#define AFE_PORT_ID_PRIMARY_PCM_RX          0x100A
+#define AFE_PORT_ID_PRIMARY_PCM_TX          0x100B
+#define AFE_PORT_ID_SECONDARY_PCM_RX        0x100C
+#define AFE_PORT_ID_SECONDARY_PCM_TX        0x100D
+#define AFE_PORT_ID_MULTICHAN_HDMI_RX       0x100E
+#define AFE_PORT_ID_SECONDARY_MI2S_RX_SD1   0x1010
+#define AFE_PORT_ID_TERTIARY_PCM_RX         0x1012
+#define AFE_PORT_ID_TERTIARY_PCM_TX         0x1013
+#define AFE_PORT_ID_QUATERNARY_PCM_RX       0x1014
+#define AFE_PORT_ID_QUATERNARY_PCM_TX       0x1015
+#define AFE_PORT_ID_QUINARY_MI2S_RX         0x1016
+#define AFE_PORT_ID_QUINARY_MI2S_TX         0x1017
+/* ID of the senary MI2S Rx port. */
+#define AFE_PORT_ID_SENARY_MI2S_RX          0x1018
+/* ID of the senary MI2S Tx port. */
+#define AFE_PORT_ID_SENARY_MI2S_TX          0x1019
+/* ID of the Internal 0 MI2S Rx port */
+#define AFE_PORT_ID_INT0_MI2S_RX                 0x102E
+/* ID of the Internal 0 MI2S Tx port */
+#define AFE_PORT_ID_INT0_MI2S_TX                 0x102F
+/* ID of the Internal 1 MI2S Rx port */
+#define AFE_PORT_ID_INT1_MI2S_RX                 0x1030
+/* ID of the Internal 1 MI2S Tx port */
+#define AFE_PORT_ID_INT1_MI2S_TX                 0x1031
+/* ID of the Internal 2 MI2S Rx port */
+#define AFE_PORT_ID_INT2_MI2S_RX                 0x1032
+/* ID of the Internal 2 MI2S Tx port */
+#define AFE_PORT_ID_INT2_MI2S_TX                 0x1033
+/* ID of the Internal 3 MI2S Rx port */
+#define AFE_PORT_ID_INT3_MI2S_RX                 0x1034
+/* ID of the Internal 3 MI2S Tx port */
+#define AFE_PORT_ID_INT3_MI2S_TX                 0x1035
+/* ID of the Internal 4 MI2S Rx port */
+#define AFE_PORT_ID_INT4_MI2S_RX                 0x1036
+/* ID of the Internal 4 MI2S Tx port */
+#define AFE_PORT_ID_INT4_MI2S_TX                 0x1037
+/* ID of the Internal 5 MI2S Rx port */
+#define AFE_PORT_ID_INT5_MI2S_RX                 0x1038
+/* ID of the Internal 5 MI2S Tx port */
+#define AFE_PORT_ID_INT5_MI2S_TX                 0x1039
+/* ID of the Internal 6 MI2S Rx port */
+#define AFE_PORT_ID_INT6_MI2S_RX                 0x103A
+/* ID of the Internal 6 MI2S Tx port */
+#define AFE_PORT_ID_INT6_MI2S_TX                 0x103B
+#define AFE_PORT_ID_SPDIF_RX                0x5000
+#define  AFE_PORT_ID_RT_PROXY_PORT_001_RX   0x2000
+#define  AFE_PORT_ID_RT_PROXY_PORT_001_TX   0x2001
+#define AFE_PORT_ID_INTERNAL_BT_SCO_RX      0x3000
+#define AFE_PORT_ID_INTERNAL_BT_SCO_TX      0x3001
+#define AFE_PORT_ID_INTERNAL_BT_A2DP_RX     0x3002
+#define AFE_PORT_ID_INTERNAL_FM_RX          0x3004
+#define AFE_PORT_ID_INTERNAL_FM_TX          0x3005
+/* SLIMbus Rx port on channel 0. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX      0x4000
+/* SLIMbus Tx port on channel 0. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX      0x4001
+/* SLIMbus Rx port on channel 1. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX      0x4002
+/* SLIMbus Tx port on channel 1. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX      0x4003
+/* SLIMbus Rx port on channel 2. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX      0x4004
+/* SLIMbus Tx port on channel 2. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX      0x4005
+/* SLIMbus Rx port on channel 3. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX      0x4006
+/* SLIMbus Tx port on channel 3. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX      0x4007
+/* SLIMbus Rx port on channel 4. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX      0x4008
+/* SLIMbus Tx port on channel 4. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX      0x4009
+/* SLIMbus Rx port on channel 5. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX      0x400a
+/* SLIMbus Tx port on channel 5. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX      0x400b
+/* SLIMbus Rx port on channel 6. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX      0x400c
+/* SLIMbus Tx port on channel 6. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX      0x400d
+/* SLIMbus Rx port on channel 7. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX      0x400e
+/* SLIMbus Tx port on channel 7. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX      0x400f
+/* SLIMbus Rx port on channel 8. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX      0x4010
+/* SLIMbus Tx port on channel 8. */
+#define AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX      0x4011
+/* AFE Rx port for audio over Display port */
+#define AFE_PORT_ID_HDMI_OVER_DP_RX              0x6020
+/*USB AFE port */
+#define AFE_PORT_ID_USB_RX                       0x7000
+#define AFE_PORT_ID_USB_TX                       0x7001
+
+/* Generic pseudoport 1. */
+#define AFE_PORT_ID_PSEUDOPORT_01      0x8001
+/* Generic pseudoport 2. */
+#define AFE_PORT_ID_PSEUDOPORT_02      0x8002
+
+/* @xreflabel{hdr:AfePortIdPrimaryAuxPcmTx}
+ * Primary Aux PCM Tx port ID.
+ */
+#define AFE_PORT_ID_PRIMARY_PCM_TX      0x100B
+/* Pseudoport that corresponds to the voice Rx path.
+ * For recording, the voice Rx path samples are written to this
+ * port and consumed by the audio path.
+ */
+
+#define AFE_PORT_ID_VOICE_RECORD_RX	0x8003
+
+/* Pseudoport that corresponds to the voice Tx path.
+ * For recording, the voice Tx path samples are written to this
+ * port and consumed by the audio path.
+ */
+
+#define AFE_PORT_ID_VOICE_RECORD_TX	0x8004
+/* Pseudoport that corresponds to in-call voice delivery samples.
+ * During in-call audio delivery, the audio path delivers samples
+ * to this port from where the voice path delivers them on the
+ * Rx path.
+ */
+#define AFE_PORT_ID_VOICE2_PLAYBACK_TX  0x8002
+#define AFE_PORT_ID_VOICE_PLAYBACK_TX   0x8005
+
+#define AFE_PORT_ID_PRIMARY_TDM_RX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x00)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_1 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_2 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_3 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_4 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_5 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_6 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_PRIMARY_TDM_RX_7 \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_PRIMARY_TDM_TX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x01)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_1 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_2 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_3 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_4 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_5 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_6 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_PRIMARY_TDM_TX_7 \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_SECONDARY_TDM_RX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x10)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_1 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_2 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_3 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_4 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_5 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_6 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_SECONDARY_TDM_RX_7 \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_SECONDARY_TDM_TX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x11)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_1 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_2 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_3 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_4 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_5 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_6 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_SECONDARY_TDM_TX_7 \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_TERTIARY_TDM_RX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x20)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_1 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_2 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_3 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_4 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_5 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_6 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_TERTIARY_TDM_RX_7 \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_TERTIARY_TDM_TX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x21)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_1 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_2 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_3 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_4 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_5 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_6 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_TERTIARY_TDM_TX_7 \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_QUATERNARY_TDM_RX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x30)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_1 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x02)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_2 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x04)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_3 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x06)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_4 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x08)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_5 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0A)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_6 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0C)
+#define AFE_PORT_ID_QUATERNARY_TDM_RX_7 \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x0E)
+
+#define AFE_PORT_ID_QUATERNARY_TDM_TX \
+	(AFE_PORT_ID_TDM_PORT_RANGE_START + 0x31)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_1 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x02)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_2 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x04)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_3 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x06)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_4 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x08)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_5 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0A)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_6 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0C)
+#define AFE_PORT_ID_QUATERNARY_TDM_TX_7 \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x0E)
+
+#define AFE_PORT_ID_INVALID             0xFFFF
+
+#define AAC_ENC_MODE_AAC_LC 0x02
+#define AAC_ENC_MODE_AAC_P 0x05
+#define AAC_ENC_MODE_EAAC_P 0x1D
+
+#define AFE_PSEUDOPORT_CMD_START 0x000100cf
+struct afe_pseudoport_start_command {
+	struct apr_hdr hdr;
+	u16 port_id;		/* Pseudo Port 1 = 0x8000 */
+				/* Pseudo Port 2 = 0x8001 */
+				/* Pseudo Port 3 = 0x8002 */
+	u16 timing;		/* FTRT = 0 , AVTimer = 1, */
+} __packed;
+
+#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0
+struct afe_pseudoport_stop_command {
+	struct apr_hdr hdr;
+	u16 port_id;		/* Pseudo Port 1 = 0x8000 */
+				/* Pseudo Port 2 = 0x8001 */
+				/* Pseudo Port 3 = 0x8002 */
+	u16 reserved;
+} __packed;
+
+
+#define AFE_MODULE_SIDETONE_IIR_FILTER	0x00010202
+#define AFE_PARAM_ID_ENABLE	0x00010203
+
+/*  Payload of the #AFE_PARAM_ID_ENABLE
+ * parameter, which enables or
+ * disables any module.
+ * The fixed size of this structure is four bytes.
+ */
+
+struct afe_mod_enable_param {
+	u16                  enable;
+	/* Enables (1) or disables (0) the module. */
+
+	u16                  reserved;
+	/* This field must be set to zero. */
+} __packed;
+
+/* ID of the configuration parameter used by the
+ * #AFE_MODULE_SIDETONE_IIR_FILTER module.
+ */
+#define AFE_PARAM_ID_SIDETONE_IIR_FILTER_CONFIG	0x00010204
+
+struct afe_sidetone_iir_filter_config_params {
+	u16                  num_biquad_stages;
+/* Number of stages.
+ * Supported values: Minimum of 5 and maximum of 10
+ */
+
+	u16                  pregain;
+/* Pregain for the compensating filter response.
+ * Supported values: Any number in Q13 format
+ */
+} __packed;
+
+#define AFE_MODULE_LOOPBACK	0x00010205
+#define AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH	0x00010206
+
+/* Payload of the #AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH parameter,
+ * which gets/sets loopback gain of a port to an Rx port.
+ * The Tx port ID of the loopback is part of the set_param command.
+ */
+
+/*  Payload of the #AFE_PORT_CMD_SET_PARAM_V2 command's
+ * configuration/calibration settings for the AFE port.
+ */
+struct afe_port_cmd_set_param_v2 {
+	u16 port_id;
+/* Port interface and direction (Rx or Tx) to start. */
+
+	u16 payload_size;
+/* Actual size of the payload in bytes.
+ * This is used for parsing the parameter payload.
+ * Supported values: > 0
+ */
+
+u32 payload_address_lsw;
+/* LSW of 64 bit Payload address.
+ * Address should be 32-byte,
+ * 4kbyte aligned and must be contiguous memory.
+ */
+
+u32 payload_address_msw;
+/* MSW of 64 bit Payload address.
+ * In case of 32-bit shared memory address,
+ * this field must be set to zero.
+ * In case of 36-bit shared memory address,
+ * bit-4 to bit-31 must be set to zero.
+ * Address should be 32-byte, 4kbyte aligned
+ * and must be contiguous memory.
+ */
+
+u32 mem_map_handle;
+/* Memory map handle returned by
+ * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands.
+ * Supported Values:
+ * - NULL -- Message. The parameter data is in-band.
+ * - Non-NULL -- The parameter data is Out-band.Pointer to
+ * the physical address
+ * in shared memory of the payload data.
+ * An optional field is available if parameter
+ * data is in-band:
+ * afe_param_data_v2 param_data[...].
+ * For detailed payload content, see the
+ * afe_port_param_data_v2 structure.
+ */
+} __packed;
+
+#define AFE_PORT_CMD_SET_PARAM_V2	0x000100EF
+
+struct afe_port_param_data_v2 {
+	u32 module_id;
+/* ID of the module to be configured.
+ * Supported values: Valid module ID
+ */
+
+u32 param_id;
+/* ID of the parameter corresponding to the supported parameters
+ * for the module ID.
+ * Supported values: Valid parameter ID
+ */
+
+u16 param_size;
+/* Actual size of the data for the
+ * module_id/param_id pair. The size is a
+ * multiple of four bytes.
+ * Supported values: > 0
+ */
+
+u16 reserved;
+/* This field must be set to zero.
+ */
+} __packed;
+
+struct afe_loopback_gain_per_path_param {
+	struct apr_hdr	hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	u16                  rx_port_id;
+/* Rx port of the loopback. */
+
+u16                  gain;
+/* Loopback gain per path of the port.
+ * Supported values: Any number in Q13 format
+ */
+} __packed;
+
+/* Parameter ID used to configure and enable/disable the
+ * loopback path. The difference with respect to the existing
+ * API, AFE_PORT_CMD_LOOPBACK, is that it allows Rx port to be
+ * configured as source port in loopback path. Port-id in
+ * AFE_PORT_CMD_SET_PARAM cmd is the source port which can be
+ * Tx or Rx port. In addition, we can configure the type of
+ * routing mode to handle different use cases.
+ */
+#define AFE_PARAM_ID_LOOPBACK_CONFIG	0x0001020B
+#define AFE_API_VERSION_LOOPBACK_CONFIG	0x1
+
+enum afe_loopback_routing_mode {
+	LB_MODE_DEFAULT = 1,
+	/* Regular loopback from source to destination port */
+	LB_MODE_SIDETONE,
+	/* Sidetone feed from Tx source to Rx destination port */
+	LB_MODE_EC_REF_VOICE_AUDIO,
+	/* Echo canceller reference, voice + audio + DTMF */
+	LB_MODE_EC_REF_VOICE
+	/* Echo canceller reference, voice alone */
+} __packed;
+
+/*  Payload of the #AFE_PARAM_ID_LOOPBACK_CONFIG ,
+ * which enables/disables one AFE loopback.
+ */
+struct afe_loopback_cfg_v1 {
+	struct apr_hdr	hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	u32		loopback_cfg_minor_version;
+/* Minor version used for tracking the version of the RMC module
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_LOOPBACK_CONFIG
+ */
+	u16                  dst_port_id;
+	/* Destination Port Id. */
+	u16                  routing_mode;
+/* Specifies data path type from src to dest port.
+ * Supported values:
+ * #LB_MODE_DEFAULT
+ * #LB_MODE_SIDETONE
+ * #LB_MODE_EC_REF_VOICE_AUDIO
+ * #LB_MODE_EC_REF_VOICE_A
+ * #LB_MODE_EC_REF_VOICE
+ */
+
+	u16                  enable;
+/* Specifies whether to enable (1) or
+ * disable (0) an AFE loopback.
+ */
+	u16                  reserved;
+/* Reserved for 32-bit alignment. This field must be set to 0.
+ */
+
+} __packed;
+
+#define AFE_MODULE_SPEAKER_PROTECTION	0x00010209
+#define AFE_PARAM_ID_SPKR_PROT_CONFIG	0x0001020a
+#define AFE_API_VERSION_SPKR_PROT_CONFIG	0x1
+#define AFE_SPKR_PROT_EXCURSIONF_LEN	512
+struct afe_spkr_prot_cfg_param_v1 {
+	u32       spkr_prot_minor_version;
+/*
+ * Minor version used for tracking the version of the
+ * speaker protection module configuration interface.
+ * Supported values: #AFE_API_VERSION_SPKR_PROT_CONFIG
+ */
+
+int16_t        win_size;
+/* Analysis and synthesis window size (nWinSize).
+ * Supported values: 1024, 512, 256 samples
+ */
+
+int16_t        margin;
+/* Allowable margin for excursion prediction,
+ * in L16Q15 format. This is a
+ * control parameter to allow
+ * for overestimation of peak excursion.
+ */
+
+int16_t        spkr_exc_limit;
+/* Speaker excursion limit, in L16Q15 format.*/
+
+int16_t        spkr_resonance_freq;
+/* Resonance frequency of the speaker; used
+ * to define a frequency range
+ * for signal modification.
+ *
+ * Supported values: 0 to 2000 Hz
+ */
+
+int16_t        limhresh;
+/* Threshold of the hard limiter; used to
+ * prevent overshooting beyond a
+ * signal level that was set by the limiter
+ * prior to speaker protection.
+ * Supported values: 0 to 32767
+ */
+
+int16_t        hpf_cut_off_freq;
+/* High pass filter cutoff frequency.
+ * Supported values: 100, 200, 300 Hz
+ */
+
+int16_t        hpf_enable;
+/* Specifies whether the high pass filter
+ * is enabled (0) or disabled (1).
+ */
+
+int16_t        reserved;
+/* This field must be set to zero. */
+
+int32_t        amp_gain;
+/* Amplifier gain in L32Q15 format.
+ * This is the RMS voltage at the
+ * loudspeaker when a 0dBFS tone
+ * is played in the digital domain.
+ */
+
+int16_t        excursionf[AFE_SPKR_PROT_EXCURSIONF_LEN];
+/* Array of the excursion transfer function.
+ * The peak excursion of the
+ * loudspeaker diaphragm is
+ * measured in millimeters for 1 Vrms Sine
+ * tone at all FFT bin frequencies.
+ * Supported values: Q15 format
+ */
+} __packed;
+
+
+#define AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER	0x000100E0
+
+/*  Payload of the #AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER
+ * command, which registers a real-time port driver
+ * with the AFE service.
+ */
+struct afe_service_cmd_register_rt_port_driver {
+	struct apr_hdr hdr;
+	u16                  port_id;
+/* Port ID with which the real-time driver exchanges data
+ * (registers for events).
+ * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to
+ * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END
+ */
+
+	u16                  reserved;
+	/* This field must be set to zero. */
+} __packed;
+
+#define AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER	0x000100E1
+
+/*  Payload of the #AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER
+ * command, which unregisters a real-time port driver from
+ * the AFE service.
+ */
+struct afe_service_cmd_unregister_rt_port_driver {
+	struct apr_hdr hdr;
+	u16                  port_id;
+/* Port ID from which the real-time
+ * driver unregisters for events.
+ * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to
+ * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END
+ */
+
+	u16                  reserved;
+	/* This field must be set to zero.	*/
+} __packed;
+
+#define AFE_EVENT_RT_PROXY_PORT_STATUS	0x00010105
+#define AFE_EVENTYPE_RT_PROXY_PORT_START	0
+#define AFE_EVENTYPE_RT_PROXY_PORT_STOP	1
+#define AFE_EVENTYPE_RT_PROXY_PORT_LOW_WATER_MARK	2
+#define AFE_EVENTYPE_RT_PROXY_PORT_HIGH_WATER_MARK	3
+#define AFE_EVENTYPE_RT_PROXY_PORT_INVALID	0xFFFF
+
+/*  Payload of the #AFE_EVENT_RT_PROXY_PORT_STATUS
+ * message, which sends an event from the AFE service
+ * to a registered client.
+ */
+struct afe_event_rt_proxy_port_status {
+	u16                  port_id;
+/* Port ID to which the event is sent.
+ * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to
+ * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END
+ */
+
+	u16                  eventype;
+/* Type of event.
+ * Supported values:
+ * - #AFE_EVENTYPE_RT_PROXY_PORT_START
+ * - #AFE_EVENTYPE_RT_PROXY_PORT_STOP
+ * - #AFE_EVENTYPE_RT_PROXY_PORT_LOW_WATER_MARK
+ * - #AFE_EVENTYPE_RT_PROXY_PORT_HIGH_WATER_MARK
+ */
+} __packed;
+
+#define AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2 0x000100ED
+
+struct afe_port_data_cmd_rt_proxy_port_write_v2 {
+	struct apr_hdr hdr;
+	u16                  port_id;
+/* Tx (mic) proxy port ID with which the real-time
+ * driver exchanges data.
+ * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to
+ * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END
+ */
+
+	u16                  reserved;
+	/* This field must be set to zero. */
+
+	u32                  buffer_address_lsw;
+/* LSW Address of the buffer containing the
+ * data from the real-time source
+ * device on a client.
+ */
+
+	u32                  buffer_address_msw;
+/* MSW Address of the buffer containing the
+ * data from the real-time source
+ * device on a client.
+ */
+
+	u32					mem_map_handle;
+/* A memory map handle encapsulating shared memory
+ * attributes is returned if
+ * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS
+ * command is successful.
+ * Supported Values:
+ * - Any 32 bit value
+ */
+
+	u32                  available_bytes;
+/* Number of valid bytes available
+ * in the buffer (including all
+ * channels: number of bytes per
+ * channel = availableBytesumChannels).
+ * Supported values: > 0
+ *
+ * This field must be equal to the frame
+ * size specified in the #AFE_PORT_AUDIO_IF_CONFIG
+ * command that was sent to configure this
+ * port.
+ */
+} __packed;
+
+#define AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2	0x000100EE
+
+/*  Payload of the
+ * #AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2 command, which
+ * delivers an empty buffer to the AFE service. On
+ * acknowledgment, data is filled in the buffer.
+ */
+struct afe_port_data_cmd_rt_proxy_port_read_v2 {
+	struct apr_hdr hdr;
+	u16                  port_id;
+/* Rx proxy port ID with which the real-time
+ * driver exchanges data.
+ * Supported values: #AFE_PORT_ID_RT_PROXY_PORT_RANGE_START to
+ * #AFE_PORT_ID_RT_PROXY_PORT_RANGE_END
+ * (This must be an Rx (speaker) port.)
+ */
+
+	u16                  reserved;
+	/* This field must be set to zero. */
+
+	u32                  buffer_address_lsw;
+/* LSW Address of the buffer containing the data sent from the AFE
+ * service to a real-time sink device on the client.
+ */
+
+
+	u32                  buffer_address_msw;
+/* MSW Address of the buffer containing the data sent from the AFE
+ * service to a real-time sink device on the client.
+ */
+
+		u32				mem_map_handle;
+/* A memory map handle encapsulating shared memory attributes is
+ * returned if AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS command is
+ * successful.
+ * Supported Values:
+ * - Any 32 bit value
+ */
+
+	u32                  available_bytes;
+/* Number of valid bytes available in the buffer (including all
+ * channels).
+ * Supported values: > 0
+ * This field must be equal to the frame size specified in the
+ * #AFE_PORT_AUDIO_IF_CONFIG command that was sent to configure
+ * this port.
+ */
+} __packed;
+
+/* This module ID is related to device configuring like I2S,PCM,
+ * HDMI, SLIMBus etc. This module supports following parameter ids.
+ * - #AFE_PARAM_ID_I2S_CONFIG
+ * - #AFE_PARAM_ID_PCM_CONFIG
+ * - #AFE_PARAM_ID_DIGI_MIC_CONFIG
+ * - #AFE_PARAM_ID_HDMI_CONFIG
+ * - #AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG
+ * - #AFE_PARAM_ID_SLIMBUS_CONFIG
+ * - #AFE_PARAM_ID_RT_PROXY_CONFIG
+ */
+
+#define AFE_MODULE_AUDIO_DEV_INTERFACE    0x0001020C
+#define AFE_PORT_SAMPLE_RATE_8K           8000
+#define AFE_PORT_SAMPLE_RATE_16K          16000
+#define AFE_PORT_SAMPLE_RATE_48K          48000
+#define AFE_PORT_SAMPLE_RATE_96K          96000
+#define AFE_PORT_SAMPLE_RATE_192K         192000
+#define AFE_LINEAR_PCM_DATA				0x0
+#define AFE_NON_LINEAR_DATA				0x1
+#define AFE_LINEAR_PCM_DATA_PACKED_60958 0x2
+#define AFE_NON_LINEAR_DATA_PACKED_60958 0x3
+
+/* This param id is used to configure I2S interface */
+#define AFE_PARAM_ID_I2S_CONFIG	0x0001020D
+#define AFE_API_VERSION_I2S_CONFIG	0x1
+/*	Enumeration for setting the I2S configuration
+ * channel_mode parameter to
+ * serial data wire number 1-3 (SD3).
+ */
+#define AFE_PORT_I2S_SD0                     0x1
+#define AFE_PORT_I2S_SD1                     0x2
+#define AFE_PORT_I2S_SD2                     0x3
+#define AFE_PORT_I2S_SD3                     0x4
+#define AFE_PORT_I2S_QUAD01                  0x5
+#define AFE_PORT_I2S_QUAD23                  0x6
+#define AFE_PORT_I2S_6CHS                    0x7
+#define AFE_PORT_I2S_8CHS                    0x8
+#define AFE_PORT_I2S_MONO                    0x0
+#define AFE_PORT_I2S_STEREO                  0x1
+#define AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL  0x0
+#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL  0x1
+
+/*  Payload of the #AFE_PARAM_ID_I2S_CONFIG
+ * command's (I2S configuration
+ * parameter).
+ */
+struct afe_param_id_i2s_cfg {
+	u32                  i2s_cfg_minor_version;
+/* Minor version used for tracking the version of the I2S
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_I2S_CONFIG
+ */
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+
+	u16                  channel_mode;
+/* I2S lines and multichannel operation.
+ * Supported values:
+ * - #AFE_PORT_I2S_SD0
+ * - #AFE_PORT_I2S_SD1
+ * - #AFE_PORT_I2S_SD2
+ * - #AFE_PORT_I2S_SD3
+ * - #AFE_PORT_I2S_QUAD01
+ * - #AFE_PORT_I2S_QUAD23
+ * - #AFE_PORT_I2S_6CHS
+ * - #AFE_PORT_I2S_8CHS
+ */
+
+	u16                  mono_stereo;
+/* Specifies mono or stereo. This applies only when
+ * a single I2S line is used.
+ * Supported values:
+ * - #AFE_PORT_I2S_MONO
+ * - #AFE_PORT_I2S_STEREO
+ */
+
+	u16                  ws_src;
+/* Word select source: internal or external.
+ * Supported values:
+ * - #AFE_PORT_CONFIG_I2S_WS_SRC_EXTERNAL
+ * - #AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL
+ */
+
+	u32                  sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ * - #AFE_PORT_SAMPLE_RATE_96K
+ * - #AFE_PORT_SAMPLE_RATE_192K
+ */
+
+	u16					data_format;
+/* data format
+ * Supported values:
+ * - #LINEAR_PCM_DATA
+ * - #NON_LINEAR_DATA
+ * - #LINEAR_PCM_DATA_PACKED_IN_60958
+ * - #NON_LINEAR_DATA_PACKED_IN_60958
+ */
+		u16                  reserved;
+	/* This field must be set to zero. */
+} __packed;
+
+/*
+ * This param id is used to configure PCM interface
+ */
+
+#define AFE_API_VERSION_SPDIF_CONFIG 0x1
+#define AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG 0x1
+#define AFE_API_VERSION_SPDIF_CLK_CONFIG 0x1
+#define AFE_CH_STATUS_A 1
+#define AFE_CH_STATUS_B 2
+
+#define AFE_PARAM_ID_SPDIF_CONFIG 0x00010244
+#define AFE_PARAM_ID_CH_STATUS_CONFIG 0x00010245
+#define AFE_PARAM_ID_SPDIF_CLK_CONFIG 0x00010246
+
+#define AFE_PORT_CLK_ROOT_LPAPLL 0x3
+#define AFE_PORT_CLK_ROOT_LPAQ6PLL   0x4
+
+struct afe_param_id_spdif_cfg {
+/* Minor version used for tracking the version of the SPDIF
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_SPDIF_CONFIG
+ */
+	u32	spdif_cfg_minor_version;
+
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_22_05K
+ * - #AFE_PORT_SAMPLE_RATE_32K
+ * - #AFE_PORT_SAMPLE_RATE_44_1K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ * - #AFE_PORT_SAMPLE_RATE_96K
+ * - #AFE_PORT_SAMPLE_RATE_176_4K
+ * - #AFE_PORT_SAMPLE_RATE_192K
+ */
+	u32	sample_rate;
+
+/* data format
+ * Supported values:
+ * - #AFE_LINEAR_PCM_DATA
+ * - #AFE_NON_LINEAR_DATA
+ */
+	u16	data_format;
+/* Number of channels supported by the port
+ * - PCM - 1, Compressed Case - 2
+ */
+	u16	num_channels;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+	u16	bit_width;
+/* This field must be set to zero. */
+	u16	reserved;
+} __packed;
+
+struct afe_param_id_spdif_ch_status_cfg {
+	u32 ch_status_cfg_minor_version;
+/* Minor version used for tracking the version of channel
+ * status configuration. Current supported version is 1
+ */
+
+	u32 status_type;
+/* Indicate if the channel status is for channel A or B
+ * Supported values:
+ * - #AFE_CH_STATUS_A
+ * - #AFE_CH_STATUS_B
+ */
+
+	u8 status_bits[24];
+/* Channel status - 192 bits for channel
+ * Byte ordering as defined by IEC60958-3
+ */
+
+	u8 status_mask[24];
+/* Channel status with mask bits 1 will be applied.
+ * Byte ordering as defined by IEC60958-3
+ */
+} __packed;
+
+struct afe_param_id_spdif_clk_cfg {
+	u32 clk_cfg_minor_version;
+/* Minor version used for tracking the version of SPDIF
+ * interface clock configuration. Current supported version
+ * is 1
+ */
+
+	u32 clk_value;
+/* Specifies the clock frequency in Hz to set
+ * Supported values:
+ * 0 - Disable the clock
+ * 2 (byphase) * 32 (60958 subframe size) * sampling rate * 2
+ * (channels A and B)
+ */
+
+	u32 clk_root;
+/* Specifies SPDIF root clk source
+ * Supported Values:
+ * - #AFE_PORT_CLK_ROOT_LPAPLL
+ * - #AFE_PORT_CLK_ROOT_LPAQ6PLL
+ */
+} __packed;
+
+struct afe_spdif_clk_config_command {
+	struct apr_hdr                    hdr;
+	struct afe_port_cmd_set_param_v2  param;
+	struct afe_port_param_data_v2     pdata;
+	struct afe_param_id_spdif_clk_cfg clk_cfg;
+} __packed;
+
+struct afe_spdif_chstatus_config_command {
+	struct apr_hdr                    hdr;
+	struct afe_port_cmd_set_param_v2  param;
+	struct afe_port_param_data_v2     pdata;
+	struct afe_param_id_spdif_ch_status_cfg ch_status;
+} __packed;
+
+struct afe_spdif_port_config {
+	struct afe_param_id_spdif_cfg            cfg;
+	struct afe_param_id_spdif_ch_status_cfg  ch_status;
+} __packed;
+
+#define AFE_PARAM_ID_PCM_CONFIG        0x0001020E
+#define AFE_API_VERSION_PCM_CONFIG	0x1
+/* Enumeration for the auxiliary PCM synchronization signal
+ * provided by an external source.
+ */
+
+#define AFE_PORT_PCM_SYNC_SRC_EXTERNAL 0x0
+/*	Enumeration for the auxiliary PCM synchronization signal
+ * provided by an internal source.
+ */
+#define AFE_PORT_PCM_SYNC_SRC_INTERNAL  0x1
+/*	Enumeration for the PCM configuration aux_mode parameter,
+ * which configures the auxiliary PCM interface to use
+ * short synchronization.
+ */
+#define AFE_PORT_PCM_AUX_MODE_PCM  0x0
+/*
+ * Enumeration for the PCM configuration aux_mode parameter,
+ * which configures the auxiliary PCM interface to use long
+ * synchronization.
+ */
+#define AFE_PORT_PCM_AUX_MODE_AUX    0x1
+/*
+ * Enumeration for setting the PCM configuration frame to 8.
+ */
+#define AFE_PORT_PCM_BITS_PER_FRAME_8  0x0
+/*
+ * Enumeration for setting the PCM configuration frame to 16.
+ */
+#define AFE_PORT_PCM_BITS_PER_FRAME_16   0x1
+
+/*	Enumeration for setting the PCM configuration frame to 32.*/
+#define AFE_PORT_PCM_BITS_PER_FRAME_32 0x2
+
+/*	Enumeration for setting the PCM configuration frame to 64.*/
+#define AFE_PORT_PCM_BITS_PER_FRAME_64   0x3
+
+/*	Enumeration for setting the PCM configuration frame to 128.*/
+#define AFE_PORT_PCM_BITS_PER_FRAME_128 0x4
+
+/*	Enumeration for setting the PCM configuration frame to 256.*/
+#define AFE_PORT_PCM_BITS_PER_FRAME_256 0x5
+
+/*	Enumeration for setting the PCM configuration
+ * quantype parameter to A-law with no padding.
+ */
+#define AFE_PORT_PCM_ALAW_NOPADDING 0x0
+
+/* Enumeration for setting the PCM configuration quantype
+ * parameter to mu-law with no padding.
+ */
+#define AFE_PORT_PCM_MULAW_NOPADDING 0x1
+/*	Enumeration for setting the PCM configuration quantype
+ * parameter to linear with no padding.
+ */
+#define AFE_PORT_PCM_LINEAR_NOPADDING 0x2
+/*	Enumeration for setting the PCM configuration quantype
+ * parameter to A-law with padding.
+ */
+#define AFE_PORT_PCM_ALAW_PADDING  0x3
+/*	Enumeration for setting the PCM configuration quantype
+ * parameter to mu-law with padding.
+ */
+#define AFE_PORT_PCM_MULAW_PADDING 0x4
+/*	Enumeration for setting the PCM configuration quantype
+ * parameter to linear with padding.
+ */
+#define AFE_PORT_PCM_LINEAR_PADDING 0x5
+/*	Enumeration for disabling the PCM configuration
+ * ctrl_data_out_enable parameter.
+ * The PCM block is the only master.
+ */
+#define AFE_PORT_PCM_CTRL_DATA_OE_DISABLE 0x0
+/*
+ * Enumeration for enabling the PCM configuration
+ * ctrl_data_out_enable parameter. The PCM block shares
+ * the signal with other masters.
+ */
+#define AFE_PORT_PCM_CTRL_DATA_OE_ENABLE  0x1
+
+/*  Payload of the #AFE_PARAM_ID_PCM_CONFIG command's
+ * (PCM configuration parameter).
+ */
+
+struct afe_param_id_pcm_cfg {
+	u32                  pcm_cfg_minor_version;
+/* Minor version used for tracking the version of the AUX PCM
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_PCM_CONFIG
+ */
+
+	u16                  aux_mode;
+/* PCM synchronization setting.
+ * Supported values:
+ * - #AFE_PORT_PCM_AUX_MODE_PCM
+ * - #AFE_PORT_PCM_AUX_MODE_AUX
+ */
+
+	u16                  sync_src;
+/* Synchronization source.
+ * Supported values:
+ * - #AFE_PORT_PCM_SYNC_SRC_EXTERNAL
+ * - #AFE_PORT_PCM_SYNC_SRC_INTERNAL
+ */
+
+	u16                  frame_setting;
+/* Number of bits per frame.
+ * Supported values:
+ * - #AFE_PORT_PCM_BITS_PER_FRAME_8
+ * - #AFE_PORT_PCM_BITS_PER_FRAME_16
+ * - #AFE_PORT_PCM_BITS_PER_FRAME_32
+ * - #AFE_PORT_PCM_BITS_PER_FRAME_64
+ * - #AFE_PORT_PCM_BITS_PER_FRAME_128
+ * - #AFE_PORT_PCM_BITS_PER_FRAME_256
+ */
+
+	u16                  quantype;
+/* PCM quantization type.
+ * Supported values:
+ * - #AFE_PORT_PCM_ALAW_NOPADDING
+ * - #AFE_PORT_PCM_MULAW_NOPADDING
+ * - #AFE_PORT_PCM_LINEAR_NOPADDING
+ * - #AFE_PORT_PCM_ALAW_PADDING
+ * - #AFE_PORT_PCM_MULAW_PADDING
+ * - #AFE_PORT_PCM_LINEAR_PADDING
+ */
+
+	u16                  ctrl_data_out_enable;
+/* Specifies whether the PCM block shares the data-out
+ * signal to the drive with other masters.
+ * Supported values:
+ * - #AFE_PORT_PCM_CTRL_DATA_OE_DISABLE
+ * - #AFE_PORT_PCM_CTRL_DATA_OE_ENABLE
+ */
+		u16                  reserved;
+	/* This field must be set to zero. */
+
+	u32                  sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ */
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16
+ */
+
+	u16                  num_channels;
+/* Number of channels.
+ * Supported values: 1 to 4
+ */
+
+	u16                  slot_number_mapping[4];
+/* Specifies the slot number for the each channel in
+ * multi channel scenario.
+ * Supported values: 1 to 32
+ */
+} __packed;
+
+/*
+ * This param id is used to configure DIGI MIC interface
+ */
+#define AFE_PARAM_ID_DIGI_MIC_CONFIG	0x0001020F
+/*  This version information is used to handle the new
+ *   additions to the config interface in future in backward
+ *   compatible manner.
+ */
+#define AFE_API_VERSION_DIGI_MIC_CONFIG 0x1
+
+/* Enumeration for setting the digital mic configuration
+ * channel_mode parameter to left 0.
+ */
+
+#define AFE_PORT_DIGI_MIC_MODE_LEFT0  0x1
+
+/*Enumeration for setting the digital mic configuration
+ * channel_mode parameter to right 0.
+ */
+
+
+#define AFE_PORT_DIGI_MIC_MODE_RIGHT0  0x2
+
+/* Enumeration for setting the digital mic configuration
+ * channel_mode parameter to left 1.
+ */
+
+#define AFE_PORT_DIGI_MIC_MODE_LEFT1  0x3
+
+/* Enumeration for setting the digital mic configuration
+ * channel_mode parameter to right 1.
+ */
+
+#define AFE_PORT_DIGI_MIC_MODE_RIGHT1 0x4
+
+/* Enumeration for setting the digital mic configuration
+ * channel_mode parameter to stereo 0.
+ */
+#define AFE_PORT_DIGI_MIC_MODE_STEREO0  0x5
+
+/* Enumeration for setting the digital mic configuration
+ * channel_mode parameter to stereo 1.
+ */
+
+
+#define AFE_PORT_DIGI_MIC_MODE_STEREO1    0x6
+
+/* Enumeration for setting the digital mic configuration
+ * channel_mode parameter to quad.
+ */
+
+#define AFE_PORT_DIGI_MIC_MODE_QUAD     0x7
+
+/*  Payload of the #AFE_PARAM_ID_DIGI_MIC_CONFIG command's
+ * (DIGI MIC configuration
+ * parameter).
+ */
+struct afe_param_id_digi_mic_cfg {
+	u32                  digi_mic_cfg_minor_version;
+/* Minor version used for tracking the version of the DIGI Mic
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_DIGI_MIC_CONFIG
+ */
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16
+ */
+
+	u16                  channel_mode;
+/* Digital mic and multichannel operation.
+ * Supported values:
+ * - #AFE_PORT_DIGI_MIC_MODE_LEFT0
+ * - #AFE_PORT_DIGI_MIC_MODE_RIGHT0
+ * - #AFE_PORT_DIGI_MIC_MODE_LEFT1
+ * - #AFE_PORT_DIGI_MIC_MODE_RIGHT1
+ * - #AFE_PORT_DIGI_MIC_MODE_STEREO0
+ * - #AFE_PORT_DIGI_MIC_MODE_STEREO1
+ * - #AFE_PORT_DIGI_MIC_MODE_QUAD
+ */
+
+	u32                  sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ */
+} __packed;
+
+/* This param id is used to configure HDMI interface */
+#define AFE_PARAM_ID_HDMI_CONFIG     0x00010210
+
+/* This version information is used to handle the new
+ * additions to the config interface in future in backward
+ * compatible manner.
+ */
+#define AFE_API_VERSION_HDMI_CONFIG 0x1
+
+/* Payload of the #AFE_PARAM_ID_HDMI_CONFIG command,
+ * which configures a multichannel HDMI audio interface.
+ */
+struct afe_param_id_hdmi_multi_chan_audio_cfg {
+	u32                  hdmi_cfg_minor_version;
+/* Minor version used for tracking the version of the HDMI
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_HDMI_CONFIG
+ */
+
+u16                  datatype;
+/* data type
+ * Supported values:
+ * - #LINEAR_PCM_DATA
+ * - #NON_LINEAR_DATA
+ * - #LINEAR_PCM_DATA_PACKED_IN_60958
+ * - #NON_LINEAR_DATA_PACKED_IN_60958
+ */
+
+u16                  channel_allocation;
+/* HDMI channel allocation information for programming an HDMI
+ * frame. The default is 0 (Stereo).
+ *
+ * This information is defined in the HDMI standard, CEA 861-D
+ * (refer to @xhyperref{S1,[S1]}). The number of channels is also
+ * inferred from this parameter.
+ */
+
+
+u32                  sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ * - #AFE_PORT_SAMPLE_RATE_96K
+ * - 22050, 44100, 176400 for compressed streams
+ */
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+		u16                  reserved;
+	/* This field must be set to zero. */
+} __packed;
+
+/* This param id is used to configure BT or FM(RIVA) interface */
+#define AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG  0x00010211
+
+/* This version information is used to handle the new
+ * additions to the config interface in future in backward
+ * compatible manner.
+ */
+#define AFE_API_VERSION_INTERNAL_BT_FM_CONFIG	0x1
+
+/* Payload of the #AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG
+ * command's BT voice/BT audio/FM configuration parameter.
+ */
+struct afe_param_id_internal_bt_fm_cfg {
+	u32                  bt_fm_cfg_minor_version;
+/* Minor version used for tracking the version of the BT and FM
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_INTERNAL_BT_FM_CONFIG
+ */
+
+	u16                  num_channels;
+/* Number of channels.
+ * Supported values: 1 to 2
+ */
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16
+ */
+
+	u32                  sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K (only for BTSCO)
+ * - #AFE_PORT_SAMPLE_RATE_16K (only for BTSCO)
+ * - #AFE_PORT_SAMPLE_RATE_48K (FM and A2DP)
+ */
+} __packed;
+
+/* This param id is used to configure SLIMBUS interface using
+ * shared channel approach.
+ */
+
+
+#define AFE_PARAM_ID_SLIMBUS_CONFIG    0x00010212
+
+/* This version information is used to handle the new
+ * additions to the config interface in future in backward
+ * compatible manner.
+ */
+#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1
+
+/* Enumeration for setting SLIMbus device ID 1. */
+#define AFE_SLIMBUS_DEVICE_1           0x0
+
+/* Enumeration for setting SLIMbus device ID 2. */
+#define AFE_SLIMBUS_DEVICE_2          0x1
+
+/* Enumeration for setting the SLIMbus data formats. */
+#define AFE_SB_DATA_FORMAT_NOT_INDICATED 0x0
+
+/* Enumeration for setting the maximum number of streams per
+ * device.
+ */
+
+#define AFE_PORT_MAX_AUDIO_CHAN_CNT	0x8
+
+/* Payload of the #AFE_PORT_CMD_SLIMBUS_CONFIG command's SLIMbus
+ * port configuration parameter.
+ */
+
+struct afe_param_id_slimbus_cfg {
+	u32                  sb_cfg_minor_version;
+/* Minor version used for tracking the version of the SLIMBUS
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_SLIMBUS_CONFIG
+ */
+
+	u16                  slimbus_dev_id;
+/* SLIMbus hardware device ID, which is required to handle
+ * multiple SLIMbus hardware blocks.
+ * Supported values: - #AFE_SLIMBUS_DEVICE_1 - #AFE_SLIMBUS_DEVICE_2
+ */
+
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+
+	u16                  data_format;
+/* Data format supported by the SLIMbus hardware. The default is
+ * 0 (#AFE_SB_DATA_FORMAT_NOT_INDICATED), which indicates the
+ * hardware does not perform any format conversions before the data
+ * transfer.
+ */
+
+
+	u16                  num_channels;
+/* Number of channels.
+ * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT
+ */
+
+	u8  shared_ch_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+/* Mapping of shared channel IDs (128 to 255) to which the
+ * master port is to be connected.
+ * Shared_channel_mapping[i] represents the shared channel assigned
+ * for audio channel i in multichannel audio data.
+ */
+
+	u32              sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ * - #AFE_PORT_SAMPLE_RATE_96K
+ * - #AFE_PORT_SAMPLE_RATE_192K
+ */
+} __packed;
+
+
+/* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS to configure
+ * USB audio device parameter. It should be used with
+ * AFE_MODULE_AUDIO_DEV_INTERFACE
+ */
+#define AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS    0x000102A5
+
+/* Minor version used for tracking USB audio  configuration */
+#define AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG 0x1
+
+/* Payload of the AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS parameter used by
+ * AFE_MODULE_AUDIO_DEV_INTERFACE.
+ */
+struct afe_param_id_usb_audio_dev_params {
+/* Minor version used for tracking USB audio device parameter.
+ * Supported values: AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG
+ */
+	u32                  cfg_minor_version;
+/* Token of actual end USB aduio device */
+	u32                  dev_token;
+} __packed;
+
+/* ID of the parameter used by AFE_PARAM_ID_USB_AUDIO_CONFIG to configure
+ * USB audio interface. It should be used with AFE_MODULE_AUDIO_DEV_INTERFACE
+ */
+#define AFE_PARAM_ID_USB_AUDIO_CONFIG    0x000102A4
+
+/* Payload of the AFE_PARAM_ID_USB_AUDIO_CONFIG parameter used by
+ * AFE_MODULE_AUDIO_DEV_INTERFACE.
+ */
+struct afe_param_id_usb_audio_cfg {
+/* Minor version used for tracking USB audio device configuration.
+ * Supported values: AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG
+ */
+	u32                  cfg_minor_version;
+/* Sampling rate of the port.
+ * Supported values:
+ * - AFE_PORT_SAMPLE_RATE_8K
+ * - AFE_PORT_SAMPLE_RATE_11025
+ * - AFE_PORT_SAMPLE_RATE_12K
+ * - AFE_PORT_SAMPLE_RATE_16K
+ * - AFE_PORT_SAMPLE_RATE_22050
+ * - AFE_PORT_SAMPLE_RATE_24K
+ * - AFE_PORT_SAMPLE_RATE_32K
+ * - AFE_PORT_SAMPLE_RATE_44P1K
+ * - AFE_PORT_SAMPLE_RATE_48K
+ * - AFE_PORT_SAMPLE_RATE_96K
+ * - AFE_PORT_SAMPLE_RATE_192K
+ */
+	u32                  sample_rate;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+	u16                  bit_width;
+/* Number of channels.
+ * Supported values: 1 and 2
+ */
+	u16                  num_channels;
+/* Data format supported by the USB. The supported value is
+ * 0 (#AFE_USB_AUDIO_DATA_FORMAT_LINEAR_PCM).
+ */
+	u16                  data_format;
+/* this field must be 0 */
+	u16                  reserved;
+/* device token of actual end USB aduio device */
+	u32                  dev_token;
+} __packed;
+
+struct afe_usb_audio_dev_param_command {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	struct afe_param_id_usb_audio_dev_params usb_dev;
+} __packed;
+
+/* This param id is used to configure Real Time Proxy interface. */
+#define AFE_PARAM_ID_RT_PROXY_CONFIG 0x00010213
+
+/* This version information is used to handle the new
+ * additions to the config interface in future in backward
+ * compatible manner.
+ */
+#define AFE_API_VERSION_RT_PROXY_CONFIG 0x1
+
+/*  Payload of the #AFE_PARAM_ID_RT_PROXY_CONFIG
+ * command (real-time proxy port configuration parameter).
+ */
+struct afe_param_id_rt_proxy_port_cfg {
+	u32                  rt_proxy_cfg_minor_version;
+/* Minor version used for tracking the version of rt-proxy
+ * config interface.
+ */
+
+	u16                  bit_width;
+/* Bit width of the sample.
+ * Supported values: 16
+ */
+
+	u16                  interleaved;
+/* Specifies whether the data exchanged between the AFE
+ * interface and real-time port is interleaved.
+ * Supported values: - 0 -- Non-interleaved (samples from each
+ * channel are contiguous in the buffer) - 1 -- Interleaved
+ * (corresponding samples from each input channel are interleaved
+ * within the buffer)
+ */
+
+
+	u16                  frame_size;
+/* Size of the frames that are used for PCM exchanges with this
+ * port.
+ * Supported values: > 0, in bytes
+ * For example, 5 ms buffers of 16 bits and 16 kHz stereo samples
+ * is 5 ms * 16 samples/ms * 2 bytes/sample * 2 channels = 320
+ * bytes.
+ */
+	u16                  jitter_allowance;
+/* Configures the amount of jitter that the port will allow.
+ * Supported values: > 0
+ * For example, if +/-10 ms of jitter is anticipated in the timing
+ * of sending frames to the port, and the configuration is 16 kHz
+ * mono with 16-bit samples, this field is 10 ms * 16 samples/ms * 2
+ * bytes/sample = 320.
+ */
+
+	u16                  low_water_mark;
+/* Low watermark in bytes (including all channels).
+ * Supported values:
+ * - 0 -- Do not send any low watermark events
+ * - > 0 -- Low watermark for triggering an event
+ * If the number of bytes in an internal circular buffer is lower
+ * than this low_water_mark parameter, a LOW_WATER_MARK event is
+ * sent to applications (via the #AFE_EVENT_RT_PROXY_PORT_STATUS
+ * event).
+ * Use of watermark events is optional for debugging purposes.
+ */
+
+	u16                  high_water_mark;
+/* High watermark in bytes (including all channels).
+ * Supported values:
+ * - 0 -- Do not send any high watermark events
+ * - > 0 -- High watermark for triggering an event
+ * If the number of bytes in an internal circular buffer exceeds
+ * TOTAL_CIRC_BUF_SIZE minus high_water_mark, a high watermark event
+ * is sent to applications (via the #AFE_EVENT_RT_PROXY_PORT_STATUS
+ * event).
+ * The use of watermark events is optional and for debugging
+ * purposes.
+ */
+
+
+	u32					sample_rate;
+/* Sampling rate of the port.
+ * Supported values:
+ * - #AFE_PORT_SAMPLE_RATE_8K
+ * - #AFE_PORT_SAMPLE_RATE_16K
+ * - #AFE_PORT_SAMPLE_RATE_48K
+ */
+
+	u16                  num_channels;
+/* Number of channels.
+ * Supported values: 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT
+ */
+
+	u16                  reserved;
+	/* For 32 bit alignment. */
+} __packed;
+
+
+/* This param id is used to configure the Pseudoport interface */
+
+#define AFE_PARAM_ID_PSEUDO_PORT_CONFIG	0x00010219
+
+/* Version information used to handle future additions to the configuration
+ * interface (for backward compatibility).
+ */
+#define AFE_API_VERSION_PSEUDO_PORT_CONFIG                          0x1
+
+/* Enumeration for setting the timing_mode parameter to faster than real
+ * time.
+ */
+#define AFE_PSEUDOPORT_TIMING_MODE_FTRT                             0x0
+
+/* Enumeration for setting the timing_mode parameter to real time using
+ * timers.
+ */
+#define AFE_PSEUDOPORT_TIMING_MODE_TIMER                            0x1
+
+/* Payload of the AFE_PARAM_ID_PSEUDO_PORT_CONFIG parameter used by
+ * AFE_MODULE_AUDIO_DEV_INTERFACE.
+ */
+struct afe_param_id_pseudo_port_cfg {
+	u32                  pseud_port_cfg_minor_version;
+	/*
+	 * Minor version used for tracking the version of the pseudoport
+	 * configuration interface.
+	 */
+
+	u16                  bit_width;
+	/* Bit width of the sample at values 16, 24 */
+
+	u16                  num_channels;
+	/* Number of channels at values  1 to 8 */
+
+	u16                  data_format;
+	/* Non-linear data format supported by the pseudoport (for future use).
+	 * At values #AFE_LINEAR_PCM_DATA
+	 */
+
+	u16                  timing_mode;
+	/* Indicates whether the pseudoport synchronizes to the clock or
+	 * operates faster than real time.
+	 * at values
+	 * - #AFE_PSEUDOPORT_TIMING_MODE_FTRT
+	 * - #AFE_PSEUDOPORT_TIMING_MODE_TIMER @tablebulletend
+	 */
+
+	u32                  sample_rate;
+	/* Sample rate at which the pseudoport will run.
+	 * at values
+	 * - #AFE_PORT_SAMPLE_RATE_8K
+	 * - #AFE_PORT_SAMPLE_RATE_32K
+	 * - #AFE_PORT_SAMPLE_RATE_48K
+	 * - #AFE_PORT_SAMPLE_RATE_96K
+	 * - #AFE_PORT_SAMPLE_RATE_192K @tablebulletend
+	 */
+} __packed;
+
+#define AFE_PARAM_ID_TDM_CONFIG		0x0001029D
+
+#define AFE_API_VERSION_TDM_CONFIG              1
+
+#define AFE_PORT_TDM_SHORT_SYNC_BIT_MODE        0
+#define AFE_PORT_TDM_LONG_SYNC_MODE             1
+#define AFE_PORT_TDM_SHORT_SYNC_SLOT_MODE       2
+
+#define AFE_PORT_TDM_SYNC_SRC_EXTERNAL          0
+#define AFE_PORT_TDM_SYNC_SRC_INTERNAL          1
+
+#define AFE_PORT_TDM_CTRL_DATA_OE_DISABLE       0
+#define AFE_PORT_TDM_CTRL_DATA_OE_ENABLE        1
+
+#define AFE_PORT_TDM_SYNC_NORMAL                0
+#define AFE_PORT_TDM_SYNC_INVERT                1
+
+#define AFE_PORT_TDM_DATA_DELAY_0_BCLK_CYCLE    0
+#define AFE_PORT_TDM_DATA_DELAY_1_BCLK_CYCLE    1
+#define AFE_PORT_TDM_DATA_DELAY_2_BCLK_CYCLE    2
+
+/* Payload of the AFE_PARAM_ID_TDM_CONFIG parameter used by
+ * AFE_MODULE_AUDIO_DEV_INTERFACE.
+ */
+struct afe_param_id_tdm_cfg {
+	u32	tdm_cfg_minor_version;
+	/* < Minor version used to track TDM configuration.
+	 * @values #AFE_API_VERSION_TDM_CONFIG
+	 */
+
+	u32	num_channels;
+	/* < Number of enabled slots for TDM frame.
+	 * @values 1 to 8
+	 */
+
+	u32	sample_rate;
+	/* < Sampling rate of the port.
+	 * @values
+	 * - #AFE_PORT_SAMPLE_RATE_8K
+	 * - #AFE_PORT_SAMPLE_RATE_16K
+	 * - #AFE_PORT_SAMPLE_RATE_24K
+	 * - #AFE_PORT_SAMPLE_RATE_32K
+	 * - #AFE_PORT_SAMPLE_RATE_48K @tablebulletend
+	 */
+
+	u32	bit_width;
+	/* < Bit width of the sample.
+	 * @values 16, 24
+	 */
+
+	u16	data_format;
+	/* < Data format: linear and compressed
+	 * @values
+	 * - #AFE_LINEAR_PCM_DATA
+	 * - #AFE_NON_LINEAR_DATA @tablebulletend
+	 */
+
+	u16	sync_mode;
+	/* < TDM synchronization setting.
+	 * @values (short, long, slot) sync mode
+	 * - #AFE_PORT_TDM_SHORT_SYNC_BIT_MODE
+	 * - #AFE_PORT_TDM_LONG_SYNC_MODE
+	 * - #AFE_PORT_TDM_SHORT_SYNC_SLOT_MODE @tablebulletend
+	 */
+
+	u16	sync_src;
+	/* < Synchronization source.
+	 * @values
+	 * - #AFE_PORT_TDM_SYNC_SRC_EXTERNAL
+	 * - #AFE_PORT_TDM_SYNC_SRC_INTERNAL @tablebulletend
+	 */
+
+	u16	nslots_per_frame;
+	/* < Number of slots per frame. Typical : 1, 2, 4, 8, 16, 32.
+	 * @values 1 - 32
+	 */
+
+	u16	ctrl_data_out_enable;
+	/* < Specifies whether the TDM block shares the data-out signal to the
+	 * drive with other masters.
+	 * @values
+	 * - #AFE_PORT_TDM_CTRL_DATA_OE_DISABLE
+	 * - #AFE_PORT_TDM_CTRL_DATA_OE_ENABLE @tablebulletend
+	 */
+
+	u16	ctrl_invert_sync_pulse;
+	/* < Specifies whether to invert the sync or not.
+	 * @values
+	 * - #AFE_PORT_TDM_SYNC_NORMAL
+	 * - #AFE_PORT_TDM_SYNC_INVERT @tablebulletend
+	 */
+
+	u16	ctrl_sync_data_delay;
+	/* < Specifies the number of bit clock to delay data with respect to
+	 * sync edge.
+	 * @values
+	 * - #AFE_PORT_TDM_DATA_DELAY_0_BCLK_CYCLE
+	 * - #AFE_PORT_TDM_DATA_DELAY_1_BCLK_CYCLE
+	 * - #AFE_PORT_TDM_DATA_DELAY_2_BCLK_CYCLE @tablebulletend
+	 */
+
+	u16	slot_width;
+	/* < Slot width of the slot in a TDM frame.  (slot_width >= bit_width)
+	 * have to be satisfied.
+	 * @values 16, 24, 32
+	 */
+
+	u32	slot_mask;
+	/* < Position of active slots.  When that bit is set,
+	 * that paricular slot is active.
+	 * Number of active slots can be inferred by number of
+	 * bits set in the mask.  Only 8 individual bits can be enabled.
+	 * Bits 0..31 corresponding to slot 0..31
+	 * @values 1 to 2^32 - 1
+	 */
+} __packed;
+
+/* ID of Time Divsion Multiplexing (TDM) module,
+ * which is used for configuring the AFE TDM.
+ *
+ * This module supports following parameter IDs:
+ * - #AFE_PORT_TDM_SLOT_CONFIG
+ *
+ * To configure the TDM interface, the client must use the
+ * #AFE_PORT_CMD_SET_PARAM command, and fill the module ID with the
+ * respective parameter IDs as listed above.
+ */
+
+#define AFE_MODULE_TDM		0x0001028A
+
+/* ID of the parameter used by #AFE_MODULE_TDM to configure
+ * the TDM slot mapping. #AFE_PORT_CMD_SET_PARAM can use this parameter ID.
+ */
+#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG	0x00010297
+
+/* Version information used to handle future additions to slot mapping
+ * configuration (for backward compatibility).
+ */
+#define AFE_API_VERSION_SLOT_MAPPING_CONFIG	0x1
+
+/* Data align type  */
+#define AFE_SLOT_MAPPING_DATA_ALIGN_MSB		0
+#define AFE_SLOT_MAPPING_DATA_ALIGN_LSB		1
+
+#define AFE_SLOT_MAPPING_OFFSET_INVALID		0xFFFF
+
+/* Payload of the AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG
+ * command's TDM configuration parameter.
+ */
+struct afe_param_id_slot_mapping_cfg {
+	u32	minor_version;
+	/* < Minor version used for tracking TDM slot configuration.
+	 * @values #AFE_API_VERSION_TDM_SLOT_CONFIG
+	 */
+
+	u16	num_channel;
+	/* < number of channel of the audio sample.
+	 * @values 1, 2, 4, 6, 8 @tablebulletend
+	 */
+
+	u16	bitwidth;
+	/* < Slot bit width for each channel
+	 * @values 16, 24, 32
+	 */
+
+	u32	data_align_type;
+	/* < indicate how data packed from slot_offset for 32 slot bit width
+	 * in case of sample bit width is 24.
+	 * @values
+	 * #AFE_SLOT_MAPPING_DATA_ALIGN_MSB
+	 * #AFE_SLOT_MAPPING_DATA_ALIGN_LSB
+	 */
+
+	u16	offset[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+	/* < Array of the slot mapping start offset in bytes for this frame.
+	 * The bytes is counted from 0. The 0 is mapped to the 1st byte
+	 * in or out of the digital serial data line this sub-frame belong to.
+	 * slot_offset[] setting is per-channel based.
+	 * The max num of channel supported is 8.
+	 * The valid offset value must always be continuly placed in from
+	 * index 0.
+	 * Set offset as AFE_SLOT_MAPPING_OFFSET_INVALID for not used arrays.
+	 * If "slot_bitwidth_per_channel" is 32 and "sample_bitwidth" is 24,
+	 * "data_align_type" is used to indicate how 24 bit sample data in
+	 * aligning with 32 bit slot width per-channel.
+	 * @values, in byte
+	 */
+} __packed;
+
+/* ID of the parameter used by #AFE_MODULE_TDM to configure
+ * the customer TDM header. #AFE_PORT_CMD_SET_PARAM can use this parameter ID.
+ */
+#define AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG		0x00010298
+
+/* Version information used to handle future additions to custom TDM header
+ * configuration (for backward compatibility).
+ */
+#define AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG	0x1
+
+#define AFE_CUSTOM_TDM_HEADER_TYPE_INVALID		0x0
+#define AFE_CUSTOM_TDM_HEADER_TYPE_DEFAULT		0x1
+#define AFE_CUSTOM_TDM_HEADER_TYPE_ENTERTAINMENT_MOST	0x2
+
+#define AFE_CUSTOM_TDM_HEADER_MAX_CNT	0x8
+
+/* Payload of the AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG parameter ID */
+struct afe_param_id_custom_tdm_header_cfg {
+	u32	minor_version;
+	/* < Minor version used for tracking custom TDM header configuration.
+	 * @values #AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG
+	 */
+
+	u16	start_offset;
+	/* < the slot mapping start offset in bytes from this sub-frame
+	 * The bytes is counted from 0. The 0 is mapped to the 1st byte in or
+	 * out of the digital serial data line this sub-frame belong to.
+	 * @values, in byte,
+	 * supported values are 0, 4, 8
+	 */
+
+	u16	header_width;
+	/* < the header width per-frame followed.
+	 * 2 bytes for MOST/TDM case
+	 * @values, in byte
+	 * supported value is 2
+	 */
+
+	u16	header_type;
+	/* < Indicate what kind of custom TDM header it is.
+	 * @values #AFE_CUSTOM_TDM_HEADER_TYPE_INVALID = 0
+	 * #AFE_CUSTOM_TDM_HEADER_TYPE_DEFAULT = 1  (for AAN channel per MOST)
+	 * #AFE_CUSTOM_TDM_HEADER_TYPE_ENTERTAINMENT_MOST = 2
+	 * (for entertainment channel, which will overwrite
+	 * AFE_API_VERSION_TDM_SAD_HEADER_TYPE_DEFAULT per MOST)
+	 */
+
+	u16	num_frame_repeat;
+	/* < num of header followed.
+	 * @values, supported value is 8
+	 */
+	u16	header[AFE_CUSTOM_TDM_HEADER_MAX_CNT];
+	/* < SAD header for MOST/TDM case is followed as payload as below.
+	 * The size of followed SAD header in bytes is num_of_frame_repeat *
+	 * header_width_per_frame, which is 2 * 8 = 16 bytes here.
+	 * the supported payload format is in uint16_t as below
+	 * uint16_t header0; SyncHi 0x3C Info[4] - CodecType -> 0x3C00
+	 * uint16_t header1; SyncLo 0xB2 Info[5] - SampleWidth -> 0xB218
+	 * uint16_t header2; DTCP Info     Info[6] - unused -> 0x0
+	 * uint16_t header3; Extension Info[7] - ASAD-Value -> 0xC0
+	 * uint16_t header4; Reserved Info[0] - Num of bytes following  -> 0x7
+	 * uint16_t header5; Reserved Info[1] - Media Type -> 0x0
+	 * uint16_t header6; Reserved Info[2] - Bitrate[kbps] - High Byte -> 0x0
+	 * uint16_t header7; Reserved Info[3] - Bitrate[kbps] - Low  Byte -> 0x0
+	 */
+} __packed;
+
+struct afe_slot_mapping_config_command {
+	struct apr_hdr	hdr;
+	struct afe_port_cmd_set_param_v2	param;
+	struct afe_port_param_data_v2	pdata;
+	struct afe_param_id_slot_mapping_cfg	slot_mapping;
+} __packed;
+
+struct afe_custom_tdm_header_config_command {
+	struct apr_hdr	hdr;
+	struct afe_port_cmd_set_param_v2	param;
+	struct afe_port_param_data_v2	pdata;
+	struct afe_param_id_custom_tdm_header_cfg	custom_tdm_header;
+} __packed;
+
+struct afe_tdm_port_config {
+	struct afe_param_id_tdm_cfg				tdm;
+	struct afe_param_id_slot_mapping_cfg		slot_mapping;
+	struct afe_param_id_custom_tdm_header_cfg	custom_tdm_header;
+} __packed;
+
+#define AFE_PARAM_ID_DEVICE_HW_DELAY     0x00010243
+#define AFE_API_VERSION_DEVICE_HW_DELAY  0x1
+
+struct afe_param_id_device_hw_delay_cfg {
+	uint32_t    device_hw_delay_minor_version;
+	uint32_t    delay_in_us;
+} __packed;
+
+#define AFE_PARAM_ID_SET_TOPOLOGY    0x0001025A
+#define AFE_API_VERSION_TOPOLOGY_V1 0x1
+
+struct afe_param_id_set_topology_cfg {
+	/*
+	 * Minor version used for tracking afe topology id configuration.
+	 * @values #AFE_API_VERSION_TOPOLOGY_V1
+	 */
+	u32		minor_version;
+	/*
+	 * Id of the topology for the afe session.
+	 * @values Any valid AFE topology ID
+	 */
+	u32		topology_id;
+} __packed;
+
+
+/*
+ * Generic encoder module ID.
+ * This module supports the following parameter IDs:
+ * #AVS_ENCODER_PARAM_ID_ENC_FMT_ID (cannot be set run time)
+ * #AVS_ENCODER_PARAM_ID_ENC_CFG_BLK (may be set run time)
+ * #AVS_ENCODER_PARAM_ID_ENC_BITRATE (may be set run time)
+ * #AVS_ENCODER_PARAM_ID_PACKETIZER_ID (cannot be set run time)
+ * Opcode - AVS_MODULE_ID_ENCODER
+ * AFE Command AFE_PORT_CMD_SET_PARAM_V2 supports this module ID.
+ */
+#define AFE_MODULE_ID_ENCODER        0x00013229
+
+/* Macro for defining the packetizer ID: COP. */
+#define AFE_MODULE_ID_PACKETIZER_COP 0x0001322A
+
+/*
+ * Packetizer type parameter for the #AVS_MODULE_ID_ENCODER module.
+ * This parameter cannot be set runtime.
+ */
+#define AFE_ENCODER_PARAM_ID_PACKETIZER_ID 0x0001322E
+
+/*
+ * Encoder config block  parameter for the #AVS_MODULE_ID_ENCODER module.
+ * This parameter may be set runtime.
+ */
+#define AFE_ENCODER_PARAM_ID_ENC_CFG_BLK 0x0001322C
+
+/*
+ * Encoder format ID parameter for the #AVS_MODULE_ID_ENCODER module.
+ * This parameter cannot be set runtime.
+ */
+#define AFE_ENCODER_PARAM_ID_ENC_FMT_ID         0x0001322B
+
+/*
+ * Data format to send compressed data
+ * is transmitted/received over Slimbus lines.
+ */
+#define AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED    0x3
+
+/*
+ * ID for AFE port module. This will be used to define port properties.
+ * This module supports following parameter IDs:
+ * #AFE_PARAM_ID_PORT_MEDIA_TYPE
+ * To configure the port property, the client must use the
+ * #AFE_PORT_CMD_SET_PARAM_V2 command,
+ * and fill the module ID with the respective parameter IDs as listed above.
+ * @apr_hdr_fields
+ * Opcode -- AFE_MODULE_PORT
+ */
+#define AFE_MODULE_PORT                          0x000102a6
+
+/*
+ * ID of the parameter used by #AFE_MODULE_PORT to set the port media type.
+ * parameter ID is currently supported using#AFE_PORT_CMD_SET_PARAM_V2 command.
+ */
+#define AFE_PARAM_ID_PORT_MEDIA_TYPE              0x000102a7
+
+/*
+ * Macros for defining the "data_format" field in the
+ * #AFE_PARAM_ID_PORT_MEDIA_TYPE
+ */
+#define AFE_PORT_DATA_FORMAT_PCM                  0x0
+#define AFE_PORT_DATA_FORMAT_GENERIC_COMPRESSED   0x1
+
+/*
+ * Macro for defining the "minor_version" field in the
+ * #AFE_PARAM_ID_PORT_MEDIA_TYPE
+ */
+#define AFE_API_VERSION_PORT_MEDIA_TYPE           0x1
+
+#define ASM_MEDIA_FMT_NONE                        0x0
+
+/*
+ * Media format ID for SBC encode configuration.
+ * @par SBC encode configuration (asm_sbc_enc_cfg_t)
+ * @table{weak__asm__sbc__enc__cfg__t}
+ */
+#define ASM_MEDIA_FMT_SBC                         0x00010BF2
+
+/* SBC channel Mono mode.*/
+#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_MONO                     1
+
+/* SBC channel Stereo mode. */
+#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_STEREO                   2
+
+/* SBC channel Dual Mono mode. */
+#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO                8
+
+/* SBC channel Joint Stereo mode. */
+#define ASM_MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO             9
+
+/* SBC bit allocation method = loudness. */
+#define ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS            0
+
+/* SBC bit allocation method = SNR. */
+#define ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR                 1
+
+
+/*
+ * Payload of the SBC encoder configuration parameters in the
+ * #ASM_MEDIA_FMT_SBC media format.
+ */
+struct asm_sbc_enc_cfg_t {
+	/*
+	 * Number of subbands.
+	 * @values 4, 8
+	 */
+	uint32_t    num_subbands;
+
+	/*
+	 * Size of the encoded block in samples.
+	 * @values 4, 8, 12, 16
+	 */
+	uint32_t    blk_len;
+
+	/*
+	 * Mode used to allocate bits between channels.
+	 * @values
+	 * 0 (Native mode)
+	 * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_MONO
+	 * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_STEREO
+	 * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_DUAL_MONO
+	 * #ASM_MEDIA_FMT_SBC_CHANNEL_MODE_JOINT_STEREO
+	 * Native mode indicates that encoding must be performed with the number
+	 * of channels at the input.
+	 * If postprocessing outputs one-channel data, Mono mode is used. If
+	 * postprocessing outputs two-channel data, Stereo mode is used.
+	 * The number of channels must not change during encoding.
+	 */
+	uint32_t    channel_mode;
+
+	/*
+	 * Encoder bit allocation method.
+	 * @values
+	 * #ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_LOUDNESS
+	 * #ASM_MEDIA_FMT_SBC_ALLOCATION_METHOD_SNR @tablebulletend
+	 */
+	uint32_t    alloc_method;
+
+	/*
+	 * Number of encoded bits per second.
+	 * @values
+	 * Mono channel -- Maximum of 320 kbps
+	 * Stereo channel -- Maximum of 512 kbps @tablebulletend
+	 */
+	uint32_t    bit_rate;
+
+	/*
+	 * Number of samples per second.
+	 * @values 0 (Native mode), 16000, 32000, 44100, 48000&nbsp;Hz
+	 * Native mode indicates that encoding must be performed with the
+	 * sampling rate at the input.
+	 * The sampling rate must not change during encoding.
+	 */
+	uint32_t    sample_rate;
+};
+
+#define ASM_MEDIA_FMT_AAC_AOT_LC            2
+#define ASM_MEDIA_FMT_AAC_AOT_SBR           5
+#define ASM_MEDIA_FMT_AAC_AOT_PS            29
+#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS  0
+#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW   3
+
+struct asm_aac_enc_cfg_v2_t {
+
+	/* Encoding rate in bits per second.*/
+	uint32_t     bit_rate;
+
+	/*
+	 * Encoding mode.
+	 * Supported values:
+	 * #ASM_MEDIA_FMT_AAC_AOT_LC
+	 * #ASM_MEDIA_FMT_AAC_AOT_SBR
+	 * #ASM_MEDIA_FMT_AAC_AOT_PS
+	 */
+	uint32_t     enc_mode;
+
+	/*
+	 * AAC format flag.
+	 * Supported values:
+	 * #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS
+	 * #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW
+	 */
+	uint16_t     aac_fmt_flag;
+
+	/*
+	 * Number of channels to encode.
+	 * Supported values:
+	 * 0 - Native mode
+	 * 1 - Mono
+	 * 2 - Stereo
+	 * Other values are not supported.
+	 * @note1hang The eAAC+ encoder mode supports only stereo.
+	 * Native mode indicates that encoding must be performed with the
+	 * number of channels at the input.
+	 * The number of channels must not change during encoding.
+	 */
+	uint16_t     channel_cfg;
+
+	/*
+	 * Number of samples per second.
+	 * Supported values: - 0 -- Native mode - For other values,
+	 * Native mode indicates that encoding must be performed with the
+	 * sampling rate at the input.
+	 * The sampling rate must not change during encoding.
+	 */
+	uint32_t     sample_rate;
+} __packed;
+
+/* FMT ID for apt-X Classic */
+#define ASM_MEDIA_FMT_APTX 0x000131ff
+
+/* FMT ID for apt-X HD */
+#define ASM_MEDIA_FMT_APTX_HD 0x00013200
+
+#define PCM_CHANNEL_L         1
+#define PCM_CHANNEL_R         2
+#define PCM_CHANNEL_C         3
+
+struct asm_custom_enc_cfg_aptx_t {
+	uint32_t    sample_rate;
+	/* Mono or stereo */
+	uint16_t    num_channels;
+	uint16_t    reserved;
+	/* num_ch == 1, then PCM_CHANNEL_C,
+	 * num_ch == 2, then {PCM_CHANNEL_L, PCM_CHANNEL_R}
+	 */
+	uint8_t     channel_mapping[8];
+	uint32_t    custom_size;
+} __packed;
+
+struct afe_enc_fmt_id_param_t {
+	/*
+	 * Supported values:
+	 *  #ASM_MEDIA_FMT_SBC
+	 *  #ASM_MEDIA_FMT_AAC_V2
+	 * Any OpenDSP supported values
+	 */
+	uint32_t    fmt_id;
+} __packed;
+
+struct afe_port_media_type_t {
+	/*
+	 * Minor version
+	 * @values #AFE_API_VERSION_PORT_MEDIA_TYPE.
+	 */
+	uint32_t    minor_version;
+
+	/*
+	 * Sampling rate of the port.
+	 * @values
+	 * #AFE_PORT_SAMPLE_RATE_8K
+	 * #AFE_PORT_SAMPLE_RATE_11_025K
+	 * #AFE_PORT_SAMPLE_RATE_12K
+	 * #AFE_PORT_SAMPLE_RATE_16K
+	 * #AFE_PORT_SAMPLE_RATE_22_05K
+	 * #AFE_PORT_SAMPLE_RATE_24K
+	 * #AFE_PORT_SAMPLE_RATE_32K
+	 * #AFE_PORT_SAMPLE_RATE_44_1K
+	 * #AFE_PORT_SAMPLE_RATE_48K
+	 * #AFE_PORT_SAMPLE_RATE_88_2K
+	 * #AFE_PORT_SAMPLE_RATE_96K
+	 * #AFE_PORT_SAMPLE_RATE_176_4K
+	 * #AFE_PORT_SAMPLE_RATE_192K
+	 * #AFE_PORT_SAMPLE_RATE_352_8K
+	 * #AFE_PORT_SAMPLE_RATE_384K
+	 */
+	uint32_t    sample_rate;
+
+	/*
+	 * Bit width of the sample.
+	 * @values 16, 24
+	 */
+	uint16_t    bit_width;
+
+	/*
+	 * Number of channels.
+	 * @values 1 to #AFE_PORT_MAX_AUDIO_CHAN_CNT
+	 */
+	uint16_t    num_channels;
+
+	/*
+	 * Data format supported by this port.
+	 * If the port media type and device media type are different,
+	 * it signifies a encoding/decoding use case
+	 * @values
+	 * #AFE_PORT_DATA_FORMAT_PCM
+	 * #AFE_PORT_DATA_FORMAT_GENERIC_COMPRESSED
+	 */
+	uint16_t   data_format;
+
+	/*This field must be set to zero.*/
+	uint16_t   reserved;
+} __packed;
+
+union afe_enc_config_data {
+	struct asm_sbc_enc_cfg_t sbc_config;
+	struct asm_aac_enc_cfg_v2_t aac_config;
+	struct asm_custom_enc_cfg_aptx_t  aptx_config;
+};
+
+struct afe_enc_config {
+	u32 format;
+	union afe_enc_config_data data;
+};
+
+struct afe_enc_cfg_blk_param_t {
+	uint32_t enc_cfg_blk_size;
+	/*
+	 *Size of the encoder configuration block that follows this member
+	 */
+	union afe_enc_config_data enc_blk_config;
+};
+
+/*
+ * Payload of the AVS_ENCODER_PARAM_ID_PACKETIZER_ID parameter.
+ */
+struct avs_enc_packetizer_id_param_t {
+	/*
+	 * Supported values:
+	 * #AVS_MODULE_ID_PACKETIZER_COP
+	 * Any OpenDSP supported values
+	 */
+	uint32_t enc_packetizer_id;
+};
+
+union afe_port_config {
+	struct afe_param_id_pcm_cfg               pcm;
+	struct afe_param_id_i2s_cfg               i2s;
+	struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch;
+	struct afe_param_id_slimbus_cfg           slim_sch;
+	struct afe_param_id_rt_proxy_port_cfg     rtproxy;
+	struct afe_param_id_internal_bt_fm_cfg    int_bt_fm;
+	struct afe_param_id_pseudo_port_cfg       pseudo_port;
+	struct afe_param_id_device_hw_delay_cfg   hw_delay;
+	struct afe_param_id_spdif_cfg             spdif;
+	struct afe_param_id_set_topology_cfg      topology;
+	struct afe_param_id_tdm_cfg               tdm;
+	struct afe_param_id_usb_audio_cfg         usb_audio;
+	struct afe_enc_fmt_id_param_t             enc_fmt;
+	struct afe_port_media_type_t              media_type;
+	struct afe_enc_cfg_blk_param_t            enc_blk_param;
+	struct avs_enc_packetizer_id_param_t      enc_pkt_id_param;
+} __packed;
+
+struct afe_audioif_config_command_no_payload {
+	struct apr_hdr			hdr;
+	struct afe_port_cmd_set_param_v2 param;
+} __packed;
+
+struct afe_audioif_config_command {
+	struct apr_hdr			hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	union afe_port_config            port;
+} __packed;
+
+#define AFE_PORT_CMD_DEVICE_START 0x000100E5
+
+/*  Payload of the #AFE_PORT_CMD_DEVICE_START.*/
+struct afe_port_cmd_device_start {
+	struct apr_hdr hdr;
+	u16                  port_id;
+/* Port interface and direction (Rx or Tx) to start. An even
+ * number represents the Rx direction, and an odd number represents
+ * the Tx direction.
+ */
+
+
+	u16                  reserved;
+/* Reserved for 32-bit alignment. This field must be set to 0.*/
+
+} __packed;
+
+#define AFE_PORT_CMD_DEVICE_STOP  0x000100E6
+
+/* Payload of the #AFE_PORT_CMD_DEVICE_STOP. */
+struct afe_port_cmd_device_stop {
+	struct apr_hdr hdr;
+	u16                  port_id;
+/* Port interface and direction (Rx or Tx) to start. An even
+ * number represents the Rx direction, and an odd number represents
+ * the Tx direction.
+ */
+
+	u16                  reserved;
+/* Reserved for 32-bit alignment. This field must be set to 0.*/
+} __packed;
+
+#define AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS 0x000100EA
+
+/*  Memory map regions command payload used by the
+ * #AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS .
+ * This structure allows clients to map multiple shared memory
+ * regions in a single command. Following this structure are
+ * num_regions of afe_service_shared_map_region_payload.
+ */
+struct afe_service_cmd_shared_mem_map_regions {
+	struct apr_hdr hdr;
+u16                  mem_pool_id;
+/* Type of memory on which this memory region is mapped.
+ * Supported values:
+ * - #ADSP_MEMORY_MAP_EBI_POOL
+ * - #ADSP_MEMORY_MAP_SMI_POOL
+ * - #ADSP_MEMORY_MAP_SHMEM8_4K_POOL
+ * - Other values are reserved
+ *
+ * The memory pool ID implicitly defines the characteristics of the
+ * memory. Characteristics may include alignment type, permissions,
+ * etc.
+ *
+ * ADSP_MEMORY_MAP_EBI_POOL is External Buffer Interface type memory
+ * ADSP_MEMORY_MAP_SMI_POOL is Shared Memory Interface type memory
+ * ADSP_MEMORY_MAP_SHMEM8_4K_POOL is shared memory, byte
+ * addressable, and 4 KB aligned.
+ */
+
+
+	u16                  num_regions;
+/* Number of regions to map.
+ * Supported values:
+ * - Any value greater than zero
+ */
+
+	u32                  property_flag;
+/* Configures one common property for all the regions in the
+ * payload.
+ *
+ * Supported values: - 0x00000000 to 0x00000001
+ *
+ * b0 - bit 0 indicates physical or virtual mapping 0 Shared memory
+ * address provided in afe_service_shared_map_region_payloadis a
+ * physical address. The shared memory needs to be mapped( hardware
+ * TLB entry) and a software entry needs to be added for internal
+ * book keeping.
+ *
+ * 1 Shared memory address provided in
+ * afe_service_shared_map_region_payloadis a virtual address. The
+ * shared memory must not be mapped (since hardware TLB entry is
+ * already available) but a software entry needs to be added for
+ * internal book keeping. This can be useful if two services with in
+ * ADSP is communicating via APR. They can now directly communicate
+ * via the Virtual address instead of Physical address. The virtual
+ * regions must be contiguous. num_regions must be 1 in this case.
+ *
+ * b31-b1 - reserved bits. must be set to zero
+ */
+
+
+} __packed;
+/*  Map region payload used by the
+ * afe_service_shared_map_region_payloadstructure.
+ */
+struct afe_service_shared_map_region_payload {
+	u32                  shm_addr_lsw;
+/* least significant word of starting address in the memory
+ * region to map. It must be contiguous memory, and it must be 4 KB
+ * aligned.
+ * Supported values: - Any 32 bit value
+ */
+
+
+	u32                  shm_addr_msw;
+/* most significant word of startng address in the memory region
+ * to map. For 32 bit shared memory address, this field must be set
+ * to zero. For 36 bit shared memory address, bit31 to bit 4 must be
+ * set to zero
+ *
+ * Supported values: - For 32 bit shared memory address, this field
+ * must be set to zero. - For 36 bit shared memory address, bit31 to
+ * bit 4 must be set to zero - For 64 bit shared memory address, any
+ * 32 bit value
+ */
+
+
+	u32                  mem_size_bytes;
+/* Number of bytes in the region. The aDSP will always map the
+ * regions as virtual contiguous memory, but the memory size must be
+ * in multiples of 4 KB to avoid gaps in the virtually contiguous
+ * mapped memory.
+ *
+ * Supported values: - multiples of 4KB
+ */
+
+} __packed;
+
+#define AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS 0x000100EB
+struct afe_service_cmdrsp_shared_mem_map_regions {
+	u32                  mem_map_handle;
+/* A memory map handle encapsulating shared memory attributes is
+ * returned iff AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS command is
+ * successful. In the case of failure , a generic APR error response
+ * is returned to the client.
+ *
+ * Supported Values: - Any 32 bit value
+ */
+
+} __packed;
+#define AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS 0x000100EC
+/* Memory unmap regions command payload used by the
+ * #AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS
+ *
+ * This structure allows clients to unmap multiple shared memory
+ * regions in a single command.
+ */
+
+
+struct afe_service_cmd_shared_mem_unmap_regions {
+	struct apr_hdr hdr;
+u32                  mem_map_handle;
+/* memory map handle returned by
+ * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands
+ *
+ * Supported Values:
+ * - Any 32 bit value
+ */
+} __packed;
+
+#define  AFE_PORT_CMD_GET_PARAM_V2 0x000100F0
+
+/*  Payload of the #AFE_PORT_CMD_GET_PARAM_V2 command,
+ * which queries for one post/preprocessing parameter of a
+ * stream.
+ */
+struct afe_port_cmd_get_param_v2 {
+	u16 port_id;
+/* Port interface and direction (Rx or Tx) to start. */
+
+	u16 payload_size;
+/* Maximum data size of the parameter ID/module ID combination.
+ * This is a multiple of four bytes
+ * Supported values: > 0
+ */
+
+	u32 payload_address_lsw;
+/* LSW of 64 bit Payload address. Address should be 32-byte,
+ * 4kbyte aligned and must be contig memory.
+ */
+
+
+	u32 payload_address_msw;
+/* MSW of 64 bit Payload address. In case of 32-bit shared
+ * memory address, this field must be set to zero. In case of 36-bit
+ * shared memory address, bit-4 to bit-31 must be set to zero.
+ * Address should be 32-byte, 4kbyte aligned and must be contiguous
+ * memory.
+ */
+
+	u32 mem_map_handle;
+/* Memory map handle returned by
+ * AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS commands.
+ * Supported Values: - NULL -- Message. The parameter data is
+ * in-band. - Non-NULL -- The parameter data is Out-band.Pointer to
+ * - the physical address in shared memory of the payload data.
+ * For detailed payload content, see the afe_port_param_data_v2
+ * structure
+ */
+
+
+	u32 module_id;
+/* ID of the module to be queried.
+ * Supported values: Valid module ID
+ */
+
+	u32 param_id;
+/* ID of the parameter to be queried.
+ * Supported values: Valid parameter ID
+ */
+} __packed;
+
+#define AFE_PORT_CMDRSP_GET_PARAM_V2 0x00010106
+
+/* Payload of the #AFE_PORT_CMDRSP_GET_PARAM_V2 message, which
+ * responds to an #AFE_PORT_CMD_GET_PARAM_V2 command.
+ *
+ * Immediately following this structure is the parameters structure
+ * (afe_port_param_data) containing the response(acknowledgment)
+ * parameter payload. This payload is included for an in-band
+ * scenario. For an address/shared memory-based set parameter, this
+ * payload is not needed.
+ */
+
+
+struct afe_port_cmdrsp_get_param_v2 {
+	u32                  status;
+} __packed;
+
+#define AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG	0x0001028C
+#define AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG	0x1
+
+/* Payload of the AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG parameter used by
+ * AFE_MODULE_AUDIO_DEV_INTERFACE.
+ */
+struct afe_param_id_lpass_core_shared_clk_cfg {
+	u32	lpass_core_shared_clk_cfg_minor_version;
+/*
+ * Minor version used for lpass core shared clock configuration
+ * Supported value: AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG
+ */
+	u32	enable;
+/*
+ * Specifies whether the lpass core shared clock is
+ * enabled (1) or disabled (0).
+ */
+} __packed;
+
+struct afe_lpass_core_shared_clk_config_command {
+	struct apr_hdr		   hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	struct afe_param_id_lpass_core_shared_clk_cfg clk_cfg;
+} __packed;
+
+/* adsp_afe_service_commands.h */
+
+#define ADSP_MEMORY_MAP_EBI_POOL      0
+
+#define ADSP_MEMORY_MAP_SMI_POOL      1
+#define ADSP_MEMORY_MAP_IMEM_POOL      2
+#define ADSP_MEMORY_MAP_SHMEM8_4K_POOL      3
+
+/* Definition of virtual memory flag */
+#define ADSP_MEMORY_MAP_VIRTUAL_MEMORY 1
+
+/* Definition of physical memory flag */
+#define ADSP_MEMORY_MAP_PHYSICAL_MEMORY 0
+
+#define NULL_POPP_TOPOLOGY				0x00010C68
+#define NULL_COPP_TOPOLOGY				0x00010312
+#define DEFAULT_COPP_TOPOLOGY				0x00010314
+#define DEFAULT_POPP_TOPOLOGY				0x00010BE4
+#define COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY         0x0001076B
+#define COMPRESS_PASSTHROUGH_NONE_TOPOLOGY      0x00010774
+#define VPM_TX_SM_ECNS_COPP_TOPOLOGY			0x00010F71
+#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY			0x00010F72
+#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY		0x00010F75
+#define VPM_TX_DM_RFECNS_COPP_TOPOLOGY			0x00010F86
+#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX		0x10015002
+#define ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE	0x10028000
+
+/* Memory map regions command payload used by the
+ * #ASM_CMD_SHARED_MEM_MAP_REGIONS ,#ADM_CMD_SHARED_MEM_MAP_REGIONS
+ * commands.
+ *
+ * This structure allows clients to map multiple shared memory
+ * regions in a single command. Following this structure are
+ * num_regions of avs_shared_map_region_payload.
+ */
+
+
+struct avs_cmd_shared_mem_map_regions {
+	struct apr_hdr hdr;
+	u16                  mem_pool_id;
+/* Type of memory on which this memory region is mapped.
+ *
+ * Supported values: - #ADSP_MEMORY_MAP_EBI_POOL -
+ * #ADSP_MEMORY_MAP_SMI_POOL - #ADSP_MEMORY_MAP_IMEM_POOL
+ * (unsupported) - #ADSP_MEMORY_MAP_SHMEM8_4K_POOL - Other values
+ * are reserved
+ *
+ * The memory ID implicitly defines the characteristics of the
+ * memory. Characteristics may include alignment type, permissions,
+ * etc.
+ *
+ * SHMEM8_4K is shared memory, byte addressable, and 4 KB aligned.
+ */
+
+
+	u16                  num_regions;
+	/* Number of regions to map.*/
+
+	u32                  property_flag;
+/* Configures one common property for all the regions in the
+ * payload. No two regions in the same memory map regions cmd can
+ * have differnt property. Supported values: - 0x00000000 to
+ * 0x00000001
+ *
+ * b0 - bit 0 indicates physical or virtual mapping 0 shared memory
+ * address provided in avs_shared_map_regions_payload is physical
+ * address. The shared memory needs to be mapped( hardware TLB
+ * entry)
+ *
+ * and a software entry needs to be added for internal book keeping.
+ *
+ * 1 Shared memory address provided in MayPayload[usRegions] is
+ * virtual address. The shared memory must not be mapped (since
+ * hardware TLB entry is already available) but a software entry
+ * needs to be added for internal book keeping. This can be useful
+ * if two services with in ADSP is communicating via APR. They can
+ * now directly communicate via the Virtual address instead of
+ * Physical address. The virtual regions must be contiguous.
+ *
+ * b31-b1 - reserved bits. must be set to zero
+ */
+
+} __packed;
+
+struct avs_shared_map_region_payload {
+	u32                  shm_addr_lsw;
+/* least significant word of shared memory address of the memory
+ * region to map. It must be contiguous memory, and it must be 4 KB
+ * aligned.
+ */
+
+	u32                  shm_addr_msw;
+/* most significant word of shared memory address of the memory
+ * region to map. For 32 bit shared memory address, this field must
+ * tbe set to zero. For 36 bit shared memory address, bit31 to bit 4
+ * must be set to zero
+ */
+
+	u32                  mem_size_bytes;
+/* Number of bytes in the region.
+ *
+ * The aDSP will always map the regions as virtual contiguous
+ * memory, but the memory size must be in multiples of 4 KB to avoid
+ * gaps in the virtually contiguous mapped memory.
+ */
+
+} __packed;
+
+struct avs_cmd_shared_mem_unmap_regions {
+	struct apr_hdr       hdr;
+	u32                  mem_map_handle;
+/* memory map handle returned by ASM_CMD_SHARED_MEM_MAP_REGIONS
+ * , ADM_CMD_SHARED_MEM_MAP_REGIONS, commands
+ */
+
+} __packed;
+
+/* Memory map command response payload used by the
+ * #ASM_CMDRSP_SHARED_MEM_MAP_REGIONS
+ * ,#ADM_CMDRSP_SHARED_MEM_MAP_REGIONS
+ */
+
+
+struct avs_cmdrsp_shared_mem_map_regions {
+	u32                  mem_map_handle;
+/* A memory map handle encapsulating shared memory attributes is
+ * returned
+ */
+
+} __packed;
+
+/*adsp_audio_memmap_api.h*/
+
+/* ASM related data structures */
+struct asm_wma_cfg {
+	u16 format_tag;
+	u16 ch_cfg;
+	u32 sample_rate;
+	u32 avg_bytes_per_sec;
+	u16 block_align;
+	u16 valid_bits_per_sample;
+	u32 ch_mask;
+	u16 encode_opt;
+	u16 adv_encode_opt;
+	u32 adv_encode_opt2;
+	u32 drc_peak_ref;
+	u32 drc_peak_target;
+	u32 drc_ave_ref;
+	u32 drc_ave_target;
+} __packed;
+
+struct asm_wmapro_cfg {
+	u16 format_tag;
+	u16 ch_cfg;
+	u32 sample_rate;
+	u32 avg_bytes_per_sec;
+	u16 block_align;
+	u16 valid_bits_per_sample;
+	u32 ch_mask;
+	u16 encode_opt;
+	u16 adv_encode_opt;
+	u32 adv_encode_opt2;
+	u32 drc_peak_ref;
+	u32 drc_peak_target;
+	u32 drc_ave_ref;
+	u32 drc_ave_target;
+} __packed;
+
+struct asm_aac_cfg {
+	u16 format;
+	u16 aot;
+	u16 ep_config;
+	u16 section_data_resilience;
+	u16 scalefactor_data_resilience;
+	u16 spectral_data_resilience;
+	u16 ch_cfg;
+	u16 reserved;
+	u32 sample_rate;
+} __packed;
+
+struct asm_amrwbplus_cfg {
+	u32  size_bytes;
+	u32  version;
+	u32  num_channels;
+	u32  amr_band_mode;
+	u32  amr_dtx_mode;
+	u32  amr_frame_fmt;
+	u32  amr_lsf_idx;
+} __packed;
+
+struct asm_flac_cfg {
+	u32 sample_rate;
+	u32 ext_sample_rate;
+	u32 min_frame_size;
+	u32 max_frame_size;
+	u16 stream_info_present;
+	u16 min_blk_size;
+	u16 max_blk_size;
+	u16 ch_cfg;
+	u16 sample_size;
+	u16 md5_sum;
+};
+
+struct asm_alac_cfg {
+	u32 frame_length;
+	u8 compatible_version;
+	u8 bit_depth;
+	u8 pb;
+	u8 mb;
+	u8 kb;
+	u8 num_channels;
+	u16 max_run;
+	u32 max_frame_bytes;
+	u32 avg_bit_rate;
+	u32 sample_rate;
+	u32 channel_layout_tag;
+};
+
+struct asm_g711_dec_cfg {
+	u32 sample_rate;
+};
+
+struct asm_vorbis_cfg {
+	u32 bit_stream_fmt;
+};
+
+struct asm_ape_cfg {
+	u16 compatible_version;
+	u16 compression_level;
+	u32 format_flags;
+	u32 blocks_per_frame;
+	u32 final_frame_blocks;
+	u32 total_frames;
+	u16 bits_per_sample;
+	u16 num_channels;
+	u32 sample_rate;
+	u32 seek_table_present;
+};
+
+struct asm_dsd_cfg {
+	u16 num_version;
+	u16 is_bitwise_big_endian;
+	u16 dsd_channel_block_size;
+	u16 num_channels;
+	u8  channel_mapping[8];
+	u32 dsd_data_rate;
+};
+
+struct asm_softpause_params {
+	u32 enable;
+	u32 period;
+	u32 step;
+	u32 rampingcurve;
+} __packed;
+
+struct asm_softvolume_params {
+	u32 period;
+	u32 step;
+	u32 rampingcurve;
+} __packed;
+
+#define ASM_END_POINT_DEVICE_MATRIX     0
+
+#define PCM_CHANNEL_NULL 0
+
+/* Front left channel. */
+#define PCM_CHANNEL_FL    1
+
+/* Front right channel. */
+#define PCM_CHANNEL_FR    2
+
+/* Front center channel. */
+#define PCM_CHANNEL_FC    3
+
+/* Left surround channel.*/
+#define PCM_CHANNEL_LS   4
+
+/* Right surround channel.*/
+#define PCM_CHANNEL_RS   5
+
+/* Low frequency effect channel. */
+#define PCM_CHANNEL_LFE  6
+
+/* Center surround channel; Rear center channel. */
+#define PCM_CHANNEL_CS   7
+
+/* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_LB   8
+
+/* Right back channel; Rear right channel. */
+#define PCM_CHANNEL_RB   9
+
+/* Top surround channel. */
+#define PCM_CHANNELS   10
+
+/* Center vertical height channel.*/
+#define PCM_CHANNEL_CVH  11
+
+/* Mono surround channel.*/
+#define PCM_CHANNEL_MS   12
+
+/* Front left of center. */
+#define PCM_CHANNEL_FLC  13
+
+/* Front right of center. */
+#define PCM_CHANNEL_FRC  14
+
+/* Rear left of center. */
+#define PCM_CHANNEL_RLC  15
+
+/* Rear right of center. */
+#define PCM_CHANNEL_RRC  16
+
+#define PCM_FORMAT_MAX_NUM_CHANNEL  8
+
+#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2 0x00010DA5
+
+#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 0x00010DDC
+
+#define ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 0x0001320C
+
+#define ASM_MEDIA_FMT_EVRCB_FS 0x00010BEF
+
+#define ASM_MEDIA_FMT_EVRCWB_FS 0x00010BF0
+
+#define ASM_MAX_EQ_BANDS 12
+
+#define ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2 0x00010D98
+
+struct asm_data_cmd_media_fmt_update_v2 {
+u32                    fmt_blk_size;
+	/* Media format block size in bytes.*/
+}  __packed;
+
+struct asm_multi_channel_pcm_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+
+	u16  num_channels;
+	/* Number of channels. Supported values: 1 to 8 */
+	u16  bits_per_sample;
+/* Number of bits per sample per channel. * Supported values:
+ * 16, 24 * When used for playback, the client must send 24-bit
+ * samples packed in 32-bit words. The 24-bit samples must be placed
+ * in the most significant 24 bits of the 32-bit word. When used for
+ * recording, the aDSP sends 24-bit samples packed in 32-bit words.
+ * The 24-bit samples are placed in the most significant 24 bits of
+ * the 32-bit word.
+ */
+
+
+	u32  sample_rate;
+/* Number of samples per second (in Hertz).
+ * Supported values: 2000 to 48000
+ */
+
+	u16  is_signed;
+	/* Flag that indicates the samples are signed (1). */
+
+	u16  reserved;
+	/* reserved field for 32 bit alignment. must be set to zero. */
+
+	u8   channel_mapping[8];
+/* Channel array of size 8.
+ * Supported values:
+ * - #PCM_CHANNEL_L
+ * - #PCM_CHANNEL_R
+ * - #PCM_CHANNEL_C
+ * - #PCM_CHANNEL_LS
+ * - #PCM_CHANNEL_RS
+ * - #PCM_CHANNEL_LFE
+ * - #PCM_CHANNEL_CS
+ * - #PCM_CHANNEL_LB
+ * - #PCM_CHANNEL_RB
+ * - #PCM_CHANNELS
+ * - #PCM_CHANNEL_CVH
+ * - #PCM_CHANNEL_MS
+ * - #PCM_CHANNEL_FLC
+ * - #PCM_CHANNEL_FRC
+ * - #PCM_CHANNEL_RLC
+ * - #PCM_CHANNEL_RRC
+ *
+ * Channel[i] mapping describes channel I. Each element i of the
+ * array describes channel I inside the buffer where 0 @le I <
+ * num_channels. An unused channel is set to zero.
+ */
+} __packed;
+
+struct asm_multi_channel_pcm_fmt_blk_v3 {
+	uint16_t                num_channels;
+/*
+ * Number of channels
+ * Supported values: 1 to 8
+ */
+
+	uint16_t                bits_per_sample;
+/*
+ * Number of bits per sample per channel
+ * Supported values: 16, 24
+ */
+
+	uint32_t                sample_rate;
+/*
+ * Number of samples per second
+ * Supported values: 2000 to 48000, 96000,192000 Hz
+ */
+
+	uint16_t                is_signed;
+/* Flag that indicates that PCM samples are signed (1) */
+
+	uint16_t                sample_word_size;
+/*
+ * Size in bits of the word that holds a sample of a channel.
+ * Supported values: 12,24,32
+ */
+
+	uint8_t                 channel_mapping[8];
+/*
+ * Each element, i, in the array describes channel i inside the buffer where
+ * 0 <= i < num_channels. Unused channels are set to 0.
+ */
+} __packed;
+
+struct asm_multi_channel_pcm_fmt_blk_v4 {
+	uint16_t                num_channels;
+/*
+ * Number of channels
+ * Supported values: 1 to 8
+ */
+
+	uint16_t                bits_per_sample;
+/*
+ * Number of bits per sample per channel
+ * Supported values: 16, 24, 32
+ */
+
+	uint32_t                sample_rate;
+/*
+ * Number of samples per second
+ * Supported values: 2000 to 48000, 96000,192000 Hz
+ */
+
+	uint16_t                is_signed;
+/* Flag that indicates that PCM samples are signed (1) */
+
+	uint16_t                sample_word_size;
+/*
+ * Size in bits of the word that holds a sample of a channel.
+ * Supported values: 12,24,32
+ */
+
+	uint8_t                 channel_mapping[8];
+/*
+ * Each element, i, in the array describes channel i inside the buffer where
+ * 0 <= i < num_channels. Unused channels are set to 0.
+ */
+	uint16_t                endianness;
+/*
+ * Flag to indicate the endianness of the pcm sample
+ * Supported values: 0 - Little endian (all other formats)
+ *                   1 - Big endian (AIFF)
+ */
+	uint16_t                mode;
+/*
+ * Mode to provide additional info about the pcm input data.
+ * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b,
+ *                       Q31 for unpacked 24b or 32b)
+ *                  15 - for 16 bit
+ *                  23 - for 24b packed or 8.24 format
+ *                  31 - for 24b unpacked or 32bit
+ */
+} __packed;
+
+/*
+ * Payload of the multichannel PCM configuration parameters in
+ * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format.
+ */
+struct asm_multi_channel_pcm_fmt_blk_param_v3 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+	struct asm_multi_channel_pcm_fmt_blk_v3 param;
+} __packed;
+
+/*
+ * Payload of the multichannel PCM configuration parameters in
+ * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format.
+ */
+struct asm_multi_channel_pcm_fmt_blk_param_v4 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+	struct asm_multi_channel_pcm_fmt_blk_v4 param;
+} __packed;
+
+struct asm_stream_cmd_set_encdec_param {
+	u32                  param_id;
+	/* ID of the parameter. */
+
+	u32                  param_size;
+/* Data size of this parameter, in bytes. The size is a multiple
+ * of 4 bytes.
+ */
+
+} __packed;
+
+struct asm_enc_cfg_blk_param_v2 {
+	u32                  frames_per_buf;
+/* Number of encoded frames to pack into each buffer.
+ *
+ * @note1hang This is only guidance information for the aDSP. The
+ * number of encoded frames put into each buffer (specified by the
+ * client) is less than or equal to this number.
+ */
+
+	u32                  enc_cfg_blk_size;
+/* Size in bytes of the encoder configuration block that follows
+ * this member.
+ */
+
+} __packed;
+
+/* @brief Dolby Digital Plus end point configuration structure
+ */
+struct asm_dec_ddp_endp_param_v2 {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	int endp_param_value;
+} __packed;
+
+/*
+ * Payload of the multichannel PCM encoder configuration parameters in
+ * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4 media format.
+ */
+
+struct asm_multi_channel_pcm_enc_cfg_v4 {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param encdec;
+	struct asm_enc_cfg_blk_param_v2 encblk;
+	uint16_t num_channels;
+	/*
+	 * Number of PCM channels.
+	 * @values
+	 * - 0 -- Native mode
+	 * - 1 -- 8 channels
+	 * Native mode indicates that encoding must be performed with the number
+	 * of channels at the input.
+	 */
+	uint16_t  bits_per_sample;
+	/*
+	 * Number of bits per sample per channel.
+	 * @values 16, 24
+	 */
+	uint32_t  sample_rate;
+	/*
+	 * Number of samples per second.
+	 * @values 0, 8000 to 48000 Hz
+	 * A value of 0 indicates the native sampling rate. Encoding is
+	 * performed at the input sampling rate.
+	 */
+	uint16_t  is_signed;
+	/*
+	 * Flag that indicates the PCM samples are signed (1). Currently, only
+	 * signed PCM samples are supported.
+	 */
+	uint16_t    sample_word_size;
+	/*
+	 * The size in bits of the word that holds a sample of a channel.
+	 * @values 16, 24, 32
+	 * 16-bit samples are always placed in 16-bit words:
+	 * sample_word_size = 1.
+	 * 24-bit samples can be placed in 32-bit words or in consecutive
+	 * 24-bit words.
+	 * - If sample_word_size = 32, 24-bit samples are placed in the
+	 * most significant 24 bits of a 32-bit word.
+	 * - If sample_word_size = 24, 24-bit samples are placed in
+	 * 24-bit words. @tablebulletend
+	 */
+	uint8_t   channel_mapping[8];
+	/*
+	 * Channel mapping array expected at the encoder output.
+	 *  Channel[i] mapping describes channel i inside the buffer, where
+	 *  0 @le i < num_channels. All valid used channels must be present at
+	 *  the beginning of the array.
+	 * If Native mode is set for the channels, this field is ignored.
+	 * @values See Section @xref{dox:PcmChannelDefs}
+	 */
+	uint16_t                endianness;
+	/*
+	 * Flag to indicate the endianness of the pcm sample
+	 * Supported values: 0 - Little endian (all other formats)
+	 *                   1 - Big endian (AIFF)
+	 */
+	uint16_t                mode;
+	/*
+	 * Mode to provide additional info about the pcm input data.
+	 * Supported values: 0 - Default QFs (Q15 for 16b, Q23 for packed 24b,
+	 *                       Q31 for unpacked 24b or 32b)
+	 *                  15 - for 16 bit
+	 *                  23 - for 24b packed or 8.24 format
+	 *                  31 - for 24b unpacked or 32bit
+	 */
+} __packed;
+
+/*
+ * Payload of the multichannel PCM encoder configuration parameters in
+ * the ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3 media format.
+ */
+
+struct asm_multi_channel_pcm_enc_cfg_v3 {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param encdec;
+	struct asm_enc_cfg_blk_param_v2 encblk;
+	uint16_t num_channels;
+	/*
+	 * Number of PCM channels.
+	 * @values
+	 * - 0 -- Native mode
+	 * - 1 -- 8 channels
+	 * Native mode indicates that encoding must be performed with the number
+	 * of channels at the input.
+	 */
+	uint16_t  bits_per_sample;
+	/*
+	 * Number of bits per sample per channel.
+	 * @values 16, 24
+	 */
+	uint32_t  sample_rate;
+	/*
+	 * Number of samples per second.
+	 * @values 0, 8000 to 48000 Hz
+	 * A value of 0 indicates the native sampling rate. Encoding is
+	 * performed at the input sampling rate.
+	 */
+	uint16_t  is_signed;
+	/*
+	 * Flag that indicates the PCM samples are signed (1). Currently, only
+	 * signed PCM samples are supported.
+	 */
+	uint16_t    sample_word_size;
+	/*
+	 * The size in bits of the word that holds a sample of a channel.
+	 * @values 16, 24, 32
+	 * 16-bit samples are always placed in 16-bit words:
+	 * sample_word_size = 1.
+	 * 24-bit samples can be placed in 32-bit words or in consecutive
+	 * 24-bit words.
+	 * - If sample_word_size = 32, 24-bit samples are placed in the
+	 * most significant 24 bits of a 32-bit word.
+	 * - If sample_word_size = 24, 24-bit samples are placed in
+	 * 24-bit words. @tablebulletend
+	 */
+	uint8_t   channel_mapping[8];
+	/*
+	 * Channel mapping array expected at the encoder output.
+	 *  Channel[i] mapping describes channel i inside the buffer, where
+	 *  0 @le i < num_channels. All valid used channels must be present at
+	 *  the beginning of the array.
+	 * If Native mode is set for the channels, this field is ignored.
+	 * @values See Section @xref{dox:PcmChannelDefs}
+	 */
+};
+
+/* @brief Multichannel PCM encoder configuration structure used
+ * in the #ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 command.
+ */
+
+struct asm_multi_channel_pcm_enc_cfg_v2 {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+	uint16_t  num_channels;
+/*< Number of PCM channels.
+ *
+ * Supported values: - 0 -- Native mode - 1 -- 8 Native mode
+ * indicates that encoding must be performed with the number of
+ * channels at the input.
+ */
+
+	uint16_t  bits_per_sample;
+/*< Number of bits per sample per channel.
+ * Supported values: 16, 24
+ */
+
+	uint32_t  sample_rate;
+/*< Number of samples per second (in Hertz).
+ *
+ * Supported values: 0, 8000 to 48000 A value of 0 indicates the
+ * native sampling rate. Encoding is performed at the input sampling
+ * rate.
+ */
+
+	uint16_t  is_signed;
+/*< Specifies whether the samples are signed (1). Currently,
+ * only signed samples are supported.
+ */
+
+	uint16_t  reserved;
+/*< reserved field for 32 bit alignment. must be set to zero.*/
+
+
+	uint8_t   channel_mapping[8];
+} __packed;
+
+#define ASM_MEDIA_FMT_MP3 0x00010BE9
+#define ASM_MEDIA_FMT_AAC_V2 0x00010DA6
+
+/* @xreflabel
+ * {hdr:AsmMediaFmtDolbyAac} Media format ID for the
+ * Dolby AAC decoder. This format ID is be used if the client wants
+ * to use the Dolby AAC decoder to decode MPEG2 and MPEG4 AAC
+ * contents.
+ */
+
+#define ASM_MEDIA_FMT_DOLBY_AAC 0x00010D86
+
+/* Enumeration for the audio data transport stream AAC format. */
+#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS 0
+
+/* Enumeration for low overhead audio stream AAC format. */
+#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS                      1
+
+/* Enumeration for the audio data interchange format
+ * AAC format.
+ */
+#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF   2
+
+/* Enumeration for the raw AAC format. */
+#define ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW    3
+
+#define ASM_MEDIA_FMT_AAC_AOT_LC             2
+#define ASM_MEDIA_FMT_AAC_AOT_SBR            5
+#define ASM_MEDIA_FMT_AAC_AOT_PS             29
+#define ASM_MEDIA_FMT_AAC_AOT_BSAC           22
+
+struct asm_aac_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmt_blk;
+
+		u16          aac_fmt_flag;
+/* Bitstream format option.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS
+ * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_LOAS
+ * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADIF
+ * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW
+ */
+
+	u16          audio_objype;
+/* Audio Object Type (AOT) present in the AAC stream.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AAC_AOT_LC
+ * - #ASM_MEDIA_FMT_AAC_AOT_SBR
+ * - #ASM_MEDIA_FMT_AAC_AOT_BSAC
+ * - #ASM_MEDIA_FMT_AAC_AOT_PS
+ * - Otherwise -- Not supported
+ */
+
+	u16          channel_config;
+/* Number of channels present in the AAC stream.
+ * Supported values:
+ * - 1 -- Mono
+ * - 2 -- Stereo
+ * - 6 -- 5.1 content
+ */
+
+	u16          total_size_of_PCE_bits;
+/* greater or equal to zero. * -In case of RAW formats and
+ * channel config = 0 (PCE), client can send * the bit stream
+ * containing PCE immediately following this structure * (in-band).
+ * -This number does not include bits included for 32 bit alignment.
+ * -If zero, then the PCE info is assumed to be available in the
+ * audio -bit stream & not in-band.
+ */
+
+	u32          sample_rate;
+/* Number of samples per second (in Hertz).
+ *
+ * Supported values: 8000, 11025, 12000, 16000, 22050, 24000, 32000,
+ * 44100, 48000
+ *
+ * This field must be equal to the sample rate of the AAC-LC
+ * decoder's output. - For MP4 or 3GP containers, this is indicated
+ * by the samplingFrequencyIndex field in the AudioSpecificConfig
+ * element. - For ADTS format, this is indicated by the
+ * samplingFrequencyIndex in the ADTS fixed header. - For ADIF
+ * format, this is indicated by the samplingFrequencyIndex in the
+ * program_config_element present in the ADIF header.
+ */
+
+} __packed;
+
+struct asm_aac_enc_cfg_v2 {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+
+	u32          bit_rate;
+	/* Encoding rate in bits per second. */
+	u32          enc_mode;
+/* Encoding mode.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AAC_AOT_LC
+ * - #ASM_MEDIA_FMT_AAC_AOT_SBR
+ * - #ASM_MEDIA_FMT_AAC_AOT_PS
+ */
+	u16          aac_fmt_flag;
+/* AAC format flag.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_ADTS
+ * - #ASM_MEDIA_FMT_AAC_FORMAT_FLAG_RAW
+ */
+	u16          channel_cfg;
+/* Number of channels to encode.
+ * Supported values:
+ * - 0 -- Native mode
+ * - 1 -- Mono
+ * - 2 -- Stereo
+ * - Other values are not supported.
+ * @note1hang The eAAC+ encoder mode supports only stereo.
+ * Native mode indicates that encoding must be performed with the
+ * number of channels at the input.
+ * The number of channels must not change during encoding.
+ */
+
+	u32          sample_rate;
+/* Number of samples per second.
+ * Supported values: - 0 -- Native mode - For other values,
+ * Native mode indicates that encoding must be performed with the
+ * sampling rate at the input.
+ * The sampling rate must not change during encoding.
+ */
+
+} __packed;
+
+#define ASM_MEDIA_FMT_G711_ALAW_FS 0x00010BF7
+#define ASM_MEDIA_FMT_G711_MLAW_FS 0x00010C2E
+
+struct asm_g711_enc_cfg_v2 {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param encdec;
+	struct asm_enc_cfg_blk_param_v2 encblk;
+
+	u32          sample_rate;
+/*
+ * Number of samples per second.
+ * Supported values: 8000, 16000 Hz
+ */
+
+} __packed;
+
+struct asm_vorbis_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+	u32          bit_stream_fmt;
+/* Bit stream format.
+ * Supported values:
+ * - 0 -- Raw bitstream
+ * - 1 -- Transcoded bitstream
+ *
+ * Transcoded bitstream containing the size of the frame as the first
+ * word in each frame.
+ */
+
+} __packed;
+
+struct asm_flac_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+
+	u16 is_stream_info_present;
+/* Specifies whether stream information is present in the FLAC format
+ * block.
+ *
+ * Supported values:
+ * - 0 -- Stream information is not present in this message
+ * - 1 -- Stream information is present in this message
+ *
+ * When set to 1, the FLAC bitstream was successfully parsed by the
+ * client, and other fields in the FLAC format block can be read by the
+ * decoder to get metadata stream information.
+ */
+
+	u16 num_channels;
+/* Number of channels for decoding.
+ * Supported values: 1 to 2
+ */
+
+	u16 min_blk_size;
+/* Minimum block size (in samples) used in the stream. It must be less
+ * than or equal to max_blk_size.
+ */
+
+	u16 max_blk_size;
+/* Maximum block size (in samples) used in the stream. If the
+ * minimum block size equals the maximum block size, a fixed block
+ * size stream is implied.
+ */
+
+	u16 md5_sum[8];
+/* MD5 signature array of the unencoded audio data. This allows the
+ * decoder to determine if an error exists in the audio data, even when
+ * the error does not result in an invalid bitstream.
+ */
+
+	u32 sample_rate;
+/* Number of samples per second.
+ * Supported values: 8000 to 48000 Hz
+ */
+
+	u32 min_frame_size;
+/* Minimum frame size used in the stream.
+ * Supported values:
+ * - > 0 bytes
+ * - 0 -- The value is unknown
+ */
+
+	u32 max_frame_size;
+/* Maximum frame size used in the stream.
+ * Supported values:
+ * -- > 0 bytes
+ * -- 0 . The value is unknown
+ */
+
+	u16 sample_size;
+/* Bits per sample.Supported values: 8, 16 */
+
+	u16 reserved;
+/* Clients must set this field to zero
+ */
+
+} __packed;
+
+struct asm_alac_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+
+	u32 frame_length;
+	u8 compatible_version;
+	u8 bit_depth;
+	u8 pb;
+	u8 mb;
+	u8 kb;
+	u8 num_channels;
+	u16 max_run;
+	u32 max_frame_bytes;
+	u32 avg_bit_rate;
+	u32 sample_rate;
+	u32 channel_layout_tag;
+
+} __packed;
+
+struct asm_g711_dec_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+	u32 sample_rate;
+} __packed;
+
+struct asm_ape_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+
+	u16 compatible_version;
+	u16 compression_level;
+	u32 format_flags;
+	u32 blocks_per_frame;
+	u32 final_frame_blocks;
+	u32 total_frames;
+	u16 bits_per_sample;
+	u16 num_channels;
+	u32 sample_rate;
+	u32 seek_table_present;
+
+} __packed;
+
+struct asm_dsd_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+
+	u16 num_version;
+	u16 is_bitwise_big_endian;
+	u16 dsd_channel_block_size;
+	u16 num_channels;
+	u8  channel_mapping[8];
+	u32 dsd_data_rate;
+
+} __packed;
+
+#define ASM_MEDIA_FMT_AMRNB_FS                  0x00010BEB
+
+/* Enumeration for 4.75 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MR475                0
+
+/* Enumeration for 5.15 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MR515                1
+
+/* Enumeration for 5.90 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR59                2
+
+/* Enumeration for 6.70 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR67                3
+
+/* Enumeration for 7.40 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR74                4
+
+/* Enumeration for 7.95 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR795               5
+
+/* Enumeration for 10.20 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR102               6
+
+/* Enumeration for 12.20 kbps AMR-NB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_MMR122               7
+
+/* Enumeration for AMR-NB Discontinuous Transmission mode off. */
+#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF                     0
+
+/* Enumeration for AMR-NB DTX mode VAD1. */
+#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1                    1
+
+/* Enumeration for AMR-NB DTX mode VAD2. */
+#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD2                    2
+
+/* Enumeration for AMR-NB DTX mode auto. */
+#define ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_AUTO                    3
+
+struct asm_amrnb_enc_cfg {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+
+	u16          enc_mode;
+/* AMR-NB encoding rate.
+ * Supported values:
+ * Use the ASM_MEDIA_FMT_AMRNB_FS_ENCODE_MODE_*
+ * macros
+ */
+
+	u16          dtx_mode;
+/* Specifies whether DTX mode is disabled or enabled.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF
+ * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1
+ */
+} __packed;
+
+#define ASM_MEDIA_FMT_AMRWB_FS                  0x00010BEC
+
+/* Enumeration for 6.6 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR66                 0
+
+/* Enumeration for 8.85 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR885                1
+
+/* Enumeration for 12.65 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1265               2
+
+/* Enumeration for 14.25 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1425               3
+
+/* Enumeration for 15.85 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1585               4
+
+/* Enumeration for 18.25 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1825               5
+
+/* Enumeration for 19.85 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR1985               6
+
+/* Enumeration for 23.05 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR2305               7
+
+/* Enumeration for 23.85 kbps AMR-WB Encoding mode. */
+#define ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_MR2385               8
+
+struct asm_amrwb_enc_cfg {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+
+	u16          enc_mode;
+/* AMR-WB encoding rate.
+ * Suupported values:
+ * Use the ASM_MEDIA_FMT_AMRWB_FS_ENCODE_MODE_*
+ * macros
+ */
+
+	u16          dtx_mode;
+/* Specifies whether DTX mode is disabled or enabled.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_OFF
+ * - #ASM_MEDIA_FMT_AMRNB_FS_DTX_MODE_VAD1
+ */
+} __packed;
+
+#define ASM_MEDIA_FMT_V13K_FS                      0x00010BED
+
+/* Enumeration for 14.4 kbps V13K Encoding mode. */
+#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1440                0
+
+/* Enumeration for 12.2 kbps V13K Encoding mode. */
+#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1220                1
+
+/* Enumeration for 11.2 kbps V13K Encoding mode. */
+#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1120                2
+
+/* Enumeration for 9.0 kbps V13K Encoding mode. */
+#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR90                  3
+
+/* Enumeration for 7.2 kbps V13K eEncoding mode. */
+#define ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR720                 4
+
+/* Enumeration for 1/8 vocoder rate.*/
+#define ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE          1
+
+/* Enumeration for 1/4 vocoder rate. */
+#define ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE       2
+
+/* Enumeration for 1/2 vocoder rate. */
+#define ASM_MEDIA_FMT_VOC_HALF_RATE             3
+
+/* Enumeration for full vocoder rate. */
+#define ASM_MEDIA_FMT_VOC_FULL_RATE             4
+
+struct asm_v13k_enc_cfg {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+		u16          max_rate;
+/* Maximum allowed encoder frame rate.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_HALF_RATE
+ * - #ASM_MEDIA_FMT_VOC_FULL_RATE
+ */
+
+	u16          min_rate;
+/* Minimum allowed encoder frame rate.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_HALF_RATE
+ * - #ASM_MEDIA_FMT_VOC_FULL_RATE
+ */
+
+	u16          reduced_rate_cmd;
+/* Reduced rate command, used to change
+ * the average bitrate of the V13K
+ * vocoder.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1440 (Default)
+ * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1220
+ * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR1120
+ * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR90
+ * - #ASM_MEDIA_FMT_V13K_FS_ENCODE_MODE_MR720
+ */
+
+	u16          rate_mod_cmd;
+/* Rate modulation command. Default = 0.
+ *- If bit 0=1, rate control is enabled.
+ *- If bit 1=1, the maximum number of consecutive full rate
+ *			frames is limited with numbers supplied in
+ *			bits 2 to 10.
+ *- If bit 1=0, the minimum number of non-full rate frames
+ *			in between two full rate frames is forced to
+ * the number supplied in bits 2 to 10. In both cases, if necessary,
+ * half rate is used to substitute full rate. - Bits 15 to 10 are
+ * reserved and must all be set to zero.
+ */
+
+} __packed;
+
+#define ASM_MEDIA_FMT_EVRC_FS                   0x00010BEE
+
+/*  EVRC encoder configuration structure used in the
+ * #ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 command.
+ */
+struct asm_evrc_enc_cfg {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+	u16          max_rate;
+/* Maximum allowed encoder frame rate.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_HALF_RATE
+ * - #ASM_MEDIA_FMT_VOC_FULL_RATE
+ */
+
+	u16          min_rate;
+/* Minimum allowed encoder frame rate.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_VOC_ONE_EIGHTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_ONE_FOURTH_RATE
+ * - #ASM_MEDIA_FMT_VOC_HALF_RATE
+ * - #ASM_MEDIA_FMT_VOC_FULL_RATE
+ */
+
+	u16          rate_mod_cmd;
+/* Rate modulation command. Default: 0.
+ * - If bit 0=1, rate control is enabled.
+ * - If bit 1=1, the maximum number of consecutive full rate frames
+ * is limited with numbers supplied in bits 2 to 10.
+ *
+ * - If bit 1=0, the minimum number of non-full rate frames in
+ * between two full rate frames is forced to the number supplied in
+ * bits 2 to 10. In both cases, if necessary, half rate is used to
+ * substitute full rate.
+ *
+ * - Bits 15 to 10 are reserved and must all be set to zero.
+ */
+
+	u16          reserved;
+	/* Reserved. Clients must set this field to zero. */
+} __packed;
+
+#define ASM_MEDIA_FMT_WMA_V10PRO_V2                0x00010DA7
+
+struct asm_wmaprov10_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+
+	u16          fmtag;
+/* WMA format type.
+ * Supported values:
+ * - 0x162 -- WMA 9 Pro
+ * - 0x163 -- WMA 9 Pro Lossless
+ * - 0x166 -- WMA 10 Pro
+ * - 0x167 -- WMA 10 Pro Lossless
+ */
+
+	u16          num_channels;
+/* Number of channels encoded in the input stream.
+ * Supported values: 1 to 8
+ */
+
+	u32          sample_rate;
+/* Number of samples per second (in Hertz).
+ * Supported values: 11025, 16000, 22050, 32000, 44100, 48000,
+ * 88200, 96000
+ */
+
+	u32          avg_bytes_per_sec;
+/* Bitrate expressed as the average bytes per second.
+ * Supported values: 2000 to 96000
+ */
+
+	u16          blk_align;
+/* Size of the bitstream packet size in bytes. WMA Pro files
+ * have a payload of one block per bitstream packet.
+ * Supported values: @le 13376
+ */
+
+	u16          bits_per_sample;
+/* Number of bits per sample in the encoded WMA stream.
+ * Supported values: 16, 24
+ */
+
+	u32          channel_mask;
+/* Bit-packed double word (32-bits) that indicates the
+ * recommended speaker positions for each source channel.
+ */
+
+	u16          enc_options;
+/* Bit-packed word with values that indicate whether certain
+ * features of the bitstream are used.
+ * Supported values: - 0x0001 -- ENCOPT3_PURE_LOSSLESS - 0x0006 --
+ * ENCOPT3_FRM_SIZE_MOD - 0x0038 -- ENCOPT3_SUBFRM_DIV - 0x0040 --
+ * ENCOPT3_WRITE_FRAMESIZE_IN_HDR - 0x0080 --
+ * ENCOPT3_GENERATE_DRC_PARAMS - 0x0100 -- ENCOPT3_RTMBITS
+ */
+
+
+	u16          usAdvancedEncodeOpt;
+	/* Advanced encoding option.  */
+
+	u32          advanced_enc_options2;
+	/* Advanced encoding option 2. */
+
+} __packed;
+
+#define ASM_MEDIA_FMT_WMA_V9_V2                    0x00010DA8
+struct asm_wmastdv9_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+	u16          fmtag;
+/* WMA format tag.
+ * Supported values: 0x161 (WMA 9 standard)
+ */
+
+	u16          num_channels;
+/* Number of channels in the stream.
+ * Supported values: 1, 2
+ */
+
+	u32          sample_rate;
+/* Number of samples per second (in Hertz).
+ * Supported values: 48000
+ */
+
+	u32          avg_bytes_per_sec;
+	/* Bitrate expressed as the average bytes per second. */
+
+	u16          blk_align;
+/* Block align. All WMA files with a maximum packet size of
+ * 13376 are supported.
+ */
+
+
+	u16          bits_per_sample;
+/* Number of bits per sample in the output.
+ * Supported values: 16
+ */
+
+	u32          channel_mask;
+/* Channel mask.
+ * Supported values:
+ * - 3 -- Stereo (front left/front right)
+ * - 4 -- Mono (center)
+ */
+
+	u16          enc_options;
+	/* Options used during encoding. */
+
+	u16          reserved;
+
+} __packed;
+
+#define ASM_MEDIA_FMT_WMA_V8                    0x00010D91
+
+struct asm_wmastdv8_enc_cfg {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+	u32          bit_rate;
+	/* Encoding rate in bits per second. */
+
+	u32          sample_rate;
+/* Number of samples per second.
+ *
+ * Supported values:
+ * - 0 -- Native mode
+ * - Other Supported values are 22050, 32000, 44100, and 48000.
+ *
+ * Native mode indicates that encoding must be performed with the
+ * sampling rate at the input.
+ * The sampling rate must not change during encoding.
+ */
+
+	u16          channel_cfg;
+/* Number of channels to encode.
+ * Supported values:
+ * - 0 -- Native mode
+ * - 1 -- Mono
+ * - 2 -- Stereo
+ * - Other values are not supported.
+ *
+ * Native mode indicates that encoding must be performed with the
+ * number of channels at the input.
+ * The number of channels must not change during encoding.
+ */
+
+	u16          reserved;
+	/* Reserved. Clients must set this field to zero.*/
+	} __packed;
+
+#define ASM_MEDIA_FMT_AMR_WB_PLUS_V2               0x00010DA9
+
+struct asm_amrwbplus_fmt_blk_v2 {
+	struct apr_hdr hdr;
+	struct asm_data_cmd_media_fmt_update_v2 fmtblk;
+	u32          amr_frame_fmt;
+/* AMR frame format.
+ * Supported values:
+ * - 6 -- Transport Interface Format (TIF)
+ * - Any other value -- File storage format (FSF)
+ *
+ * TIF stream contains 2-byte header for each frame within the
+ * superframe. FSF stream contains one 2-byte header per superframe.
+ */
+
+} __packed;
+
+#define ASM_MEDIA_FMT_AC3			0x00010DEE
+#define ASM_MEDIA_FMT_EAC3			0x00010DEF
+#define ASM_MEDIA_FMT_DTS                    0x00010D88
+#define ASM_MEDIA_FMT_MP2                    0x00010DE9
+#define ASM_MEDIA_FMT_FLAC                   0x00010C16
+#define ASM_MEDIA_FMT_ALAC                   0x00012F31
+#define ASM_MEDIA_FMT_VORBIS                 0x00010C15
+#define ASM_MEDIA_FMT_APE                    0x00012F32
+#define ASM_MEDIA_FMT_DSD                    0x00012F3E
+
+
+/* Media format ID for adaptive transform acoustic coding. This
+ * ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED command
+ * only.
+ */
+
+#define ASM_MEDIA_FMT_ATRAC                  0x00010D89
+
+/* Media format ID for metadata-enhanced audio transmission.
+ * This ID is used by the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED
+ * command only.
+ */
+
+#define ASM_MEDIA_FMT_MAT                    0x00010D8A
+
+/*  adsp_media_fmt.h */
+
+#define ASM_DATA_CMD_WRITE_V2 0x00010DAB
+
+struct asm_data_cmd_write_v2 {
+	struct apr_hdr hdr;
+	u32                  buf_addr_lsw;
+/* The 64 bit address msw-lsw should be a valid, mapped address.
+ * 64 bit address should be a multiple of 32 bytes
+ */
+
+	u32                  buf_addr_msw;
+/* The 64 bit address msw-lsw should be a valid, mapped address.
+ * 64 bit address should be a multiple of 32 bytes.
+ * -Address of the buffer containing the data to be decoded.
+ * The buffer should be aligned to a 32 byte boundary.
+ * -In the case of 32 bit Shared memory address, msw field must
+ * -be set to zero.
+ * -In the case of 36 bit shared memory address, bit 31 to bit 4
+ * -of msw must be set to zero.
+ */
+	u32                  mem_map_handle;
+/* memory map handle returned by DSP through
+ * ASM_CMD_SHARED_MEM_MAP_REGIONS command
+ */
+	u32                  buf_size;
+/* Number of valid bytes available in the buffer for decoding. The
+ * first byte starts at buf_addr.
+ */
+
+	u32                  seq_id;
+	/* Optional buffer sequence ID. */
+
+	u32                  timestamp_lsw;
+/* Lower 32 bits of the 64-bit session time in microseconds of the
+ * first buffer sample.
+ */
+
+	u32                  timestamp_msw;
+/* Upper 32 bits of the 64-bit session time in microseconds of the
+ * first buffer sample.
+ */
+
+	u32                  flags;
+/* Bitfield of flags.
+ * Supported values for bit 31:
+ * - 1 -- Valid timestamp.
+ * - 0 -- Invalid timestamp.
+ * - Use #ASM_BIT_MASKIMESTAMP_VALID_FLAG as the bitmask and
+ * #ASM_SHIFTIMESTAMP_VALID_FLAG as the shift value to set this bit.
+ * Supported values for bit 30:
+ * - 1 -- Last buffer.
+ * - 0 -- Not the last buffer.
+ *
+ * Supported values for bit 29:
+ * - 1 -- Continue the timestamp from the previous buffer.
+ * - 0 -- Timestamp of the current buffer is not related
+ * to the timestamp of the previous buffer.
+ * - Use #ASM_BIT_MASKS_CONTINUE_FLAG and #ASM_SHIFTS_CONTINUE_FLAG
+ * to set this bit.
+ *
+ * Supported values for bit 4:
+ * - 1 -- End of the frame.
+ * - 0 -- Not the end of frame, or this information is not known.
+ * - Use #ASM_BIT_MASK_EOF_FLAG as the bitmask and #ASM_SHIFT_EOF_FLAG
+ * as the shift value to set this bit.
+ *
+ * All other bits are reserved and must be set to 0.
+ *
+ * If bit 31=0 and bit 29=1: The timestamp of the first sample in
+ * this buffer continues from the timestamp of the last sample in
+ * the previous buffer. If there is no previous buffer (i.e., this
+ * is the first buffer sent after opening the stream or after a
+ * flush operation), or if the previous buffer does not have a valid
+ * timestamp, the samples in the current buffer also do not have a
+ * valid timestamp. They are played out as soon as possible.
+ *
+ *
+ * If bit 31=0 and bit 29=0: No timestamp is associated with the
+ * first sample in this buffer. The samples are played out as soon
+ * as possible.
+ *
+ *
+ * If bit 31=1 and bit 29 is ignored: The timestamp specified in
+ * this payload is honored.
+ *
+ *
+ * If bit 30=0: Not the last buffer in the stream. This is useful
+ * in removing trailing samples.
+ *
+ *
+ * For bit 4: The client can set this flag for every buffer sent in
+ * which the last byte is the end of a frame. If this flag is set,
+ * the buffer can contain data from multiple frames, but it should
+ * always end at a frame boundary. Restrictions allow the aDSP to
+ * detect an end of frame without requiring additional processing.
+ */
+
+} __packed;
+
+#define ASM_DATA_CMD_READ_V2 0x00010DAC
+
+struct asm_data_cmd_read_v2 {
+	struct apr_hdr       hdr;
+	u32                  buf_addr_lsw;
+/* the 64 bit address msw-lsw should be a valid mapped address
+ * and should be a multiple of 32 bytes
+ */
+
+
+	u32                  buf_addr_msw;
+/* the 64 bit address msw-lsw should be a valid mapped address
+ * and should be a multiple of 32 bytes.
+ * - Address of the buffer where the DSP puts the encoded data,
+ * potentially, at an offset specified by the uOffset field in
+ * ASM_DATA_EVENT_READ_DONE structure. The buffer should be aligned
+ * to a 32 byte boundary.
+ * - In the case of 32 bit Shared memory address, msw field must
+ * - be set to zero.
+ * - In the case of 36 bit shared memory address, bit 31 to bit
+ * - 4 of msw must be set to zero.
+ */
+	u32                  mem_map_handle;
+/* memory map handle returned by DSP through
+ * ASM_CMD_SHARED_MEM_MAP_REGIONS command.
+ */
+
+	u32                  buf_size;
+/* Number of bytes available for the aDSP to write. The aDSP
+ * starts writing from buf_addr.
+ */
+
+	u32                  seq_id;
+	/* Optional buffer sequence ID. */
+} __packed;
+
+#define ASM_DATA_CMD_EOS               0x00010BDB
+#define ASM_DATA_EVENT_RENDERED_EOS    0x00010C1C
+#define ASM_DATA_EVENT_EOS             0x00010BDD
+
+#define ASM_DATA_EVENT_WRITE_DONE_V2 0x00010D99
+struct asm_data_event_write_done_v2 {
+	u32                  buf_addr_lsw;
+	/* lsw of the 64 bit address */
+	u32                  buf_addr_msw;
+	/* msw of the 64 bit address. address given by the client in
+	 * ASM_DATA_CMD_WRITE_V2 command.
+	 */
+	u32                  mem_map_handle;
+	/* memory map handle in the ASM_DATA_CMD_WRITE_V2 */
+
+	u32                  status;
+/* Status message (error code) that indicates whether the
+ * referenced buffer has been successfully consumed.
+ * Supported values: Refer to @xhyperref{Q3,[Q3]}
+ */
+} __packed;
+
+#define ASM_DATA_EVENT_READ_DONE_V2 0x00010D9A
+
+/* Definition of the frame metadata flag bitmask.*/
+#define ASM_BIT_MASK_FRAME_METADATA_FLAG (0x40000000UL)
+
+/* Definition of the frame metadata flag shift value. */
+#define ASM_SHIFT_FRAME_METADATA_FLAG 30
+
+struct asm_data_event_read_done_v2 {
+	u32                  status;
+/* Status message (error code).
+ * Supported values: Refer to @xhyperref{Q3,[Q3]}
+ */
+
+u32                  buf_addr_lsw;
+/* 64 bit address msw-lsw is a valid, mapped address. 64 bit
+ * address is a multiple of 32 bytes.
+ */
+
+u32                  buf_addr_msw;
+/* 64 bit address msw-lsw is a valid, mapped address. 64 bit
+ * address is a multiple of 32 bytes.
+ *
+ * -Same address provided by the client in ASM_DATA_CMD_READ_V2
+ * -In the case of 32 bit Shared memory address, msw field is set to
+ * zero.
+ * -In the case of 36 bit shared memory address, bit 31 to bit 4
+ * -of msw is set to zero.
+ */
+
+u32                  mem_map_handle;
+/* memory map handle in the ASM_DATA_CMD_READ_V2  */
+
+u32                  enc_framesotal_size;
+/* Total size of the encoded frames in bytes.
+ * Supported values: >0
+ */
+
+u32                  offset;
+/* Offset (from buf_addr) to the first byte of the first encoded
+ * frame. All encoded frames are consecutive, starting from this
+ * offset.
+ * Supported values: > 0
+ */
+
+u32                  timestamp_lsw;
+/* Lower 32 bits of the 64-bit session time in microseconds of
+ * the first sample in the buffer. If Bit 5 of mode_flags flag of
+ * ASM_STREAM_CMD_OPEN_READ_V2 is 1 then the 64 bit timestamp is
+ * absolute capture time otherwise it is relative session time. The
+ * absolute timestamp doesn't reset unless the system is reset.
+ */
+
+
+u32                  timestamp_msw;
+/* Upper 32 bits of the 64-bit session time in microseconds of
+ * the first sample in the buffer.
+ */
+
+
+u32                  flags;
+/* Bitfield of flags. Bit 30 indicates whether frame metadata is
+ * present. If frame metadata is present, num_frames consecutive
+ * instances of @xhyperref{hdr:FrameMetaData,Frame metadata} start
+ * at the buffer address.
+ * Supported values for bit 31:
+ * - 1 -- Timestamp is valid.
+ * - 0 -- Timestamp is invalid.
+ * - Use #ASM_BIT_MASKIMESTAMP_VALID_FLAG and
+ * #ASM_SHIFTIMESTAMP_VALID_FLAG to set this bit.
+ *
+ * Supported values for bit 30:
+ * - 1 -- Frame metadata is present.
+ * - 0 -- Frame metadata is absent.
+ * - Use #ASM_BIT_MASK_FRAME_METADATA_FLAG and
+ * #ASM_SHIFT_FRAME_METADATA_FLAG to set this bit.
+ *
+ * All other bits are reserved; the aDSP sets them to 0.
+ */
+
+u32                  num_frames;
+/* Number of encoded frames in the buffer. */
+
+u32                  seq_id;
+/* Optional buffer sequence ID.	*/
+} __packed;
+
+struct asm_data_read_buf_metadata_v2 {
+	u32          offset;
+/* Offset from buf_addr in #ASM_DATA_EVENT_READ_DONE_PAYLOAD to
+ * the frame associated with this metadata.
+ * Supported values: > 0
+ */
+
+u32          frm_size;
+/* Size of the encoded frame in bytes.
+ * Supported values: > 0
+ */
+
+u32          num_encoded_pcm_samples;
+/* Number of encoded PCM samples (per channel) in the frame
+ * associated with this metadata.
+ * Supported values: > 0
+ */
+
+u32          timestamp_lsw;
+/* Lower 32 bits of the 64-bit session time in microseconds of the
+ * first sample for this frame.
+ * If Bit 5 of mode_flags flag of ASM_STREAM_CMD_OPEN_READ_V2 is 1
+ * then the 64 bit timestamp is absolute capture time otherwise it
+ * is relative session time. The absolute timestamp doesn't reset
+ * unless the system is reset.
+ */
+
+
+u32          timestamp_msw;
+/* Lower 32 bits of the 64-bit session time in microseconds of the
+ * first sample for this frame.
+ */
+
+u32          flags;
+/* Frame flags.
+ * Supported values for bit 31:
+ * - 1 -- Time stamp is valid
+ * - 0 -- Time stamp is not valid
+ * - All other bits are reserved; the aDSP sets them to 0.
+ */
+} __packed;
+
+/* Notifies the client of a change in the data sampling rate or
+ * Channel mode. This event is raised by the decoder service. The
+ * event is enabled through the mode flags of
+ * #ASM_STREAM_CMD_OPEN_WRITE_V2 or
+ * #ASM_STREAM_CMD_OPEN_READWRITE_V2. - The decoder detects a change
+ * in the output sampling frequency or the number/positioning of
+ * output channels, or if it is the first frame decoded.The new
+ * sampling frequency or the new channel configuration is
+ * communicated back to the client asynchronously.
+ */
+
+#define ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY 0x00010C65
+
+/*  Payload of the #ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY event.
+ * This event is raised when the following conditions are both true:
+ * - The event is enabled through the mode_flags of
+ * #ASM_STREAM_CMD_OPEN_WRITE_V2 or
+ * #ASM_STREAM_CMD_OPEN_READWRITE_V2. - The decoder detects a change
+ * in either the output sampling frequency or the number/positioning
+ * of output channels, or if it is the first frame decoded.
+ * This event is not raised (even if enabled) if the decoder is
+ * MIDI, because
+ */
+
+
+struct asm_data_event_sr_cm_change_notify {
+	u32                  sample_rate;
+/* New sampling rate (in Hertz) after detecting a change in the
+ * bitstream.
+ * Supported values: 2000 to 48000
+ */
+
+	u16                  num_channels;
+/* New number of channels after detecting a change in the
+ * bitstream.
+ * Supported values: 1 to 8
+ */
+
+
+	u16                  reserved;
+	/* Reserved for future use. This field must be set to 0.*/
+
+	u8                   channel_mapping[8];
+
+} __packed;
+
+/* Notifies the client of a data sampling rate or channel mode
+ * change. This event is raised by the encoder service.
+ * This event is raised when :
+ * - Native mode encoding was requested in the encoder
+ * configuration (i.e., the channel number was 0), the sample rate
+ * was 0, or both were 0.
+ *
+ * - The input data frame at the encoder is the first one, or the
+ * sampling rate/channel mode is different from the previous input
+ * data frame.
+ *
+ */
+#define ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY 0x00010BDE
+
+struct asm_data_event_enc_sr_cm_change_notify {
+	u32                  sample_rate;
+/* New sampling rate (in Hertz) after detecting a change in the
+ * input data.
+ * Supported values: 2000 to 48000
+ */
+
+
+	u16                  num_channels;
+/* New number of channels after detecting a change in the input
+ * data. Supported values: 1 to 8
+ */
+
+
+	u16                  bits_per_sample;
+/* New bits per sample after detecting a change in the input
+ * data.
+ * Supported values: 16, 24
+ */
+
+
+	u8                   channel_mapping[8];
+
+} __packed;
+#define ASM_DATA_CMD_IEC_60958_FRAME_RATE 0x00010D87
+
+
+/* Payload of the #ASM_DATA_CMD_IEC_60958_FRAME_RATE command,
+ * which is used to indicate the IEC 60958 frame rate of a given
+ * packetized audio stream.
+ */
+
+struct asm_data_cmd_iec_60958_frame_rate {
+	u32                  frame_rate;
+/* IEC 60958 frame rate of the incoming IEC 61937 packetized stream.
+ * Supported values: Any valid frame rate
+ */
+} __packed;
+
+/* adsp_asm_data_commands.h*/
+/* Definition of the stream ID bitmask.*/
+#define ASM_BIT_MASK_STREAM_ID                 (0x000000FFUL)
+
+/* Definition of the stream ID shift value.*/
+#define ASM_SHIFT_STREAM_ID                    0
+
+/* Definition of the session ID bitmask.*/
+#define ASM_BIT_MASK_SESSION_ID                (0x0000FF00UL)
+
+/* Definition of the session ID shift value.*/
+#define ASM_SHIFT_SESSION_ID                   8
+
+/* Definition of the service ID bitmask.*/
+#define ASM_BIT_MASK_SERVICE_ID                (0x00FF0000UL)
+
+/* Definition of the service ID shift value.*/
+#define ASM_SHIFT_SERVICE_ID                   16
+
+/* Definition of the domain ID bitmask.*/
+#define ASM_BIT_MASK_DOMAIN_ID                (0xFF000000UL)
+
+/* Definition of the domain ID shift value.*/
+#define ASM_SHIFT_DOMAIN_ID                    24
+
+#define ASM_CMD_SHARED_MEM_MAP_REGIONS               0x00010D92
+#define ASM_CMDRSP_SHARED_MEM_MAP_REGIONS     0x00010D93
+#define ASM_CMD_SHARED_MEM_UNMAP_REGIONS              0x00010D94
+
+/* adsp_asm_service_commands.h */
+
+#define ASM_MAX_SESSION_ID  (15)
+
+/* Maximum number of sessions.*/
+#define ASM_MAX_NUM_SESSIONS                ASM_MAX_SESSION_ID
+
+/* Maximum number of streams per session.*/
+#define ASM_MAX_STREAMS_PER_SESSION (8)
+#define ASM_SESSION_CMD_RUN_V2                   0x00010DAA
+#define ASM_SESSION_CMD_RUN_STARTIME_RUN_IMMEDIATE  0
+#define ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_ABSOLUTEIME 1
+#define ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_RELATIVEIME 2
+#define ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY     3
+
+#define ASM_BIT_MASK_RUN_STARTIME                 (0x00000003UL)
+
+/* Bit shift value used to specify the start time for the
+ * ASM_SESSION_CMD_RUN_V2 command.
+ */
+#define ASM_SHIFT_RUN_STARTIME 0
+struct asm_session_cmd_run_v2 {
+	struct apr_hdr hdr;
+	u32                  flags;
+/* Specifies whether to run immediately or at a specific
+ * rendering time or with a specified delay. Run with delay is
+ * useful for delaying in case of ASM loopback opened through
+ * ASM_STREAM_CMD_OPEN_LOOPBACK_V2. Use #ASM_BIT_MASK_RUN_STARTIME
+ * and #ASM_SHIFT_RUN_STARTIME to set this 2-bit flag.
+ *
+ *
+ *Bits 0 and 1 can take one of four possible values:
+ *
+ *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_IMMEDIATE
+ *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_ABSOLUTEIME
+ *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_AT_RELATIVEIME
+ *- #ASM_SESSION_CMD_RUN_STARTIME_RUN_WITH_DELAY
+ *
+ *All other bits are reserved; clients must set them to zero.
+ */
+
+	u32                  time_lsw;
+/* Lower 32 bits of the time in microseconds used to align the
+ * session origin time. When bits 0-1 of flags is
+ * ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, time lsw is the lsw of
+ * the delay in us. For ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY,
+ * maximum value of the 64 bit delay is 150 ms.
+ */
+
+	u32                  time_msw;
+/* Upper 32 bits of the time in microseconds used to align the
+ * session origin time. When bits 0-1 of flags is
+ * ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY, time msw is the msw of
+ * the delay in us. For ASM_SESSION_CMD_RUN_START_RUN_WITH_DELAY,
+ * maximum value of the 64 bit delay is 150 ms.
+ */
+
+} __packed;
+
+#define ASM_SESSION_CMD_PAUSE 0x00010BD3
+#define ASM_SESSION_CMD_SUSPEND 0x00010DEC
+#define ASM_SESSION_CMD_GET_SESSIONTIME_V3 0x00010D9D
+#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5
+
+struct asm_session_cmd_rgstr_rx_underflow {
+	struct apr_hdr hdr;
+	u16                  enable_flag;
+/* Specifies whether a client is to receive events when an Rx
+ * session underflows.
+ * Supported values:
+ * - 0 -- Do not send underflow events
+ * - 1 -- Send underflow events
+ */
+	u16                  reserved;
+	/* Reserved. This field must be set to zero.*/
+} __packed;
+
+#define ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS 0x00010BD6
+
+struct asm_session_cmd_regx_overflow {
+	struct apr_hdr hdr;
+	u16                  enable_flag;
+/* Specifies whether a client is to receive events when a Tx
+ * session overflows.
+ * Supported values:
+ * - 0 -- Do not send overflow events
+ * - 1 -- Send overflow events
+ */
+
+	u16                  reserved;
+	/* Reserved. This field must be set to zero.*/
+} __packed;
+
+#define ASM_SESSION_EVENT_RX_UNDERFLOW        0x00010C17
+#define ASM_SESSION_EVENTX_OVERFLOW           0x00010C18
+#define ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3 0x00010D9E
+
+struct asm_session_cmdrsp_get_sessiontime_v3 {
+	u32                  status;
+	/* Status message (error code).
+	 * Supported values: Refer to @xhyperref{Q3,[Q3]}
+	 */
+
+	u32                  sessiontime_lsw;
+	/* Lower 32 bits of the current session time in microseconds.*/
+
+	u32                  sessiontime_msw;
+	/* Upper 32 bits of the current session time in microseconds.*/
+
+	u32                  absolutetime_lsw;
+/* Lower 32 bits in micro seconds of the absolute time at which
+ * the * sample corresponding to the above session time gets
+ * rendered * to hardware. This absolute time may be slightly in the
+ * future or past.
+ */
+
+
+	u32                  absolutetime_msw;
+/* Upper 32 bits in micro seconds of the absolute time at which
+ * the * sample corresponding to the above session time gets
+ * rendered to * hardware. This absolute time may be slightly in the
+ * future or past.
+ */
+
+} __packed;
+
+#define ASM_SESSION_CMD_ADJUST_SESSION_CLOCK_V2     0x00010D9F
+
+struct asm_session_cmd_adjust_session_clock_v2 {
+	struct apr_hdr hdr;
+u32                  adjustime_lsw;
+/* Lower 32 bits of the signed 64-bit quantity that specifies the
+ * adjustment time in microseconds to the session clock.
+ *
+ * Positive values indicate advancement of the session clock.
+ * Negative values indicate delay of the session clock.
+ */
+
+
+	u32                  adjustime_msw;
+/* Upper 32 bits of the signed 64-bit quantity that specifies
+ * the adjustment time in microseconds to the session clock.
+ * Positive values indicate advancement of the session clock.
+ * Negative values indicate delay of the session clock.
+ */
+
+} __packed;
+
+#define ASM_SESSION_CMDRSP_ADJUST_SESSION_CLOCK_V2    0x00010DA0
+
+struct asm_session_cmdrsp_adjust_session_clock_v2 {
+	u32                  status;
+/* Status message (error code).
+ * Supported values: Refer to @xhyperref{Q3,[Q3]}
+ * An error means the session clock is not adjusted. In this case,
+ * the next two fields are irrelevant.
+ */
+
+
+	u32                  actual_adjustime_lsw;
+/* Lower 32 bits of the signed 64-bit quantity that specifies
+ * the actual adjustment in microseconds performed by the aDSP.
+ * A positive value indicates advancement of the session clock. A
+ * negative value indicates delay of the session clock.
+ */
+
+
+	u32                  actual_adjustime_msw;
+/* Upper 32 bits of the signed 64-bit quantity that specifies
+ * the actual adjustment in microseconds performed by the aDSP.
+ * A positive value indicates advancement of the session clock. A
+ * negative value indicates delay of the session clock.
+ */
+
+
+	u32                  cmd_latency_lsw;
+/* Lower 32 bits of the unsigned 64-bit quantity that specifies
+ * the amount of time in microseconds taken to perform the session
+ * clock adjustment.
+ */
+
+
+	u32                  cmd_latency_msw;
+/* Upper 32 bits of the unsigned 64-bit quantity that specifies
+ * the amount of time in microseconds taken to perform the session
+ * clock adjustment.
+ */
+
+} __packed;
+
+#define ASM_SESSION_CMD_GET_PATH_DELAY_V2	 0x00010DAF
+#define ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 0x00010DB0
+
+struct asm_session_cmdrsp_get_path_delay_v2 {
+	u32                  status;
+/* Status message (error code). Whether this get delay operation
+ * is successful or not. Delay value is valid only if status is
+ * success.
+ * Supported values: Refer to @xhyperref{Q5,[Q5]}
+ */
+
+	u32                  audio_delay_lsw;
+	/* Upper 32 bits of the aDSP delay in microseconds. */
+
+	u32                  audio_delay_msw;
+	/* Lower 32 bits of the aDSP delay  in microseconds. */
+
+} __packed;
+
+/* adsp_asm_session_command.h*/
+#define ASM_STREAM_CMD_OPEN_WRITE_V3       0x00010DB3
+
+#define ASM_LOW_LATENCY_STREAM_SESSION				0x10000000
+
+#define ASM_ULTRA_LOW_LATENCY_STREAM_SESSION			0x20000000
+
+#define ASM_ULL_POST_PROCESSING_STREAM_SESSION			0x40000000
+
+#define ASM_LEGACY_STREAM_SESSION                                      0
+
+
+struct asm_stream_cmd_open_write_v3 {
+	struct apr_hdr			hdr;
+	uint32_t                    mode_flags;
+/* Mode flags that configure the stream to notify the client
+ * whenever it detects an SR/CM change at the input to its POPP.
+ * Supported values for bits 0 to 1:
+ * - Reserved; clients must set them to zero.
+ * Supported values for bit 2:
+ * - 0 -- SR/CM change notification event is disabled.
+ * - 1 -- SR/CM change notification event is enabled.
+ * - Use #ASM_BIT_MASK_SR_CM_CHANGE_NOTIFY_FLAG and
+ * #ASM_SHIFT_SR_CM_CHANGE_NOTIFY_FLAG to set or get this bit.
+ *
+ * Supported values for bit 31:
+ * - 0 -- Stream to be opened in on-Gapless mode.
+ * - 1 -- Stream to be opened in Gapless mode. In Gapless mode,
+ * successive streams must be opened with same session ID but
+ * different stream IDs.
+ *
+ * - Use #ASM_BIT_MASK_GAPLESS_MODE_FLAG and
+ * #ASM_SHIFT_GAPLESS_MODE_FLAG to set or get this bit.
+ *
+ *
+ * @note1hang MIDI and DTMF streams cannot be opened in Gapless mode.
+ */
+
+	uint16_t                    sink_endpointype;
+/*< Sink point type.
+ * Supported values:
+ * - 0 -- Device matrix
+ * - Other values are reserved.
+ *
+ * The device matrix is the gateway to the hardware ports.
+ */
+
+	uint16_t                    bits_per_sample;
+/*< Number of bits per sample processed by ASM modules.
+ * Supported values: 16 and 24 bits per sample
+ */
+
+	uint32_t                    postprocopo_id;
+/*< Specifies the topology (order of processing) of
+ * postprocessing algorithms. <i>None</i> means no postprocessing.
+ * Supported values:
+ * - #ASM_STREAM_POSTPROCOPO_ID_DEFAULT
+ * - #ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL
+ * - #ASM_STREAM_POSTPROCOPO_ID_NONE
+ *
+ * This field can also be enabled through SetParams flags.
+ */
+
+	uint32_t                    dec_fmt_id;
+/*< Configuration ID of the decoder media format.
+ *
+ * Supported values:
+ * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2
+ * - #ASM_MEDIA_FMT_ADPCM
+ * - #ASM_MEDIA_FMT_MP3
+ * - #ASM_MEDIA_FMT_AAC_V2
+ * - #ASM_MEDIA_FMT_DOLBY_AAC
+ * - #ASM_MEDIA_FMT_AMRNB_FS
+ * - #ASM_MEDIA_FMT_AMRWB_FS
+ * - #ASM_MEDIA_FMT_AMR_WB_PLUS_V2
+ * - #ASM_MEDIA_FMT_V13K_FS
+ * - #ASM_MEDIA_FMT_EVRC_FS
+ * - #ASM_MEDIA_FMT_EVRCB_FS
+ * - #ASM_MEDIA_FMT_EVRCWB_FS
+ * - #ASM_MEDIA_FMT_SBC
+ * - #ASM_MEDIA_FMT_WMA_V10PRO_V2
+ * - #ASM_MEDIA_FMT_WMA_V9_V2
+ * - #ASM_MEDIA_FMT_AC3
+ * - #ASM_MEDIA_FMT_EAC3
+ * - #ASM_MEDIA_FMT_G711_ALAW_FS
+ * - #ASM_MEDIA_FMT_G711_MLAW_FS
+ * - #ASM_MEDIA_FMT_G729A_FS
+ * - #ASM_MEDIA_FMT_FR_FS
+ * - #ASM_MEDIA_FMT_VORBIS
+ * - #ASM_MEDIA_FMT_FLAC
+ * - #ASM_MEDIA_FMT_ALAC
+ * - #ASM_MEDIA_FMT_APE
+ * - #ASM_MEDIA_FMT_EXAMPLE
+ */
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE    0x00010DD9
+
+/* Bitmask for the stream_perf_mode subfield. */
+#define ASM_BIT_MASK_STREAM_PERF_FLAG_PULL_MODE_WRITE 0xE0000000UL
+
+/* Bitmask for the stream_perf_mode subfield. */
+#define ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE 29
+
+#define ASM_STREAM_CMD_OPEN_PUSH_MODE_READ  0x00010DDA
+
+#define ASM_BIT_MASK_STREAM_PERF_FLAG_PUSH_MODE_READ 0xE0000000UL
+
+#define ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ 29
+
+#define ASM_DATA_EVENT_WATERMARK 0x00010DDB
+
+struct asm_shared_position_buffer {
+	volatile uint32_t               frame_counter;
+/* Counter used to handle interprocessor synchronization issues.
+ * When frame_counter is 0: read_index, wall_clock_us_lsw, and
+ * wall_clock_us_msw are invalid.
+ * Supported values: >= 0.
+ */
+
+	volatile uint32_t               index;
+/* Index in bytes from where the aDSP is reading/writing.
+ * Supported values: 0 to circular buffer size - 1
+ */
+
+	volatile uint32_t               wall_clock_us_lsw;
+/* Lower 32 bits of the 64-bit wall clock time in microseconds when the
+ * read index was updated.
+ * Supported values: >= 0
+ */
+
+	volatile uint32_t               wall_clock_us_msw;
+/* Upper 32 bits of the 64 bit wall clock time in microseconds when the
+ * read index was updated
+ * Supported values: >= 0
+ */
+} __packed;
+
+struct asm_shared_watermark_level {
+	uint32_t                watermark_level_bytes;
+} __packed;
+
+struct asm_stream_cmd_open_shared_io {
+	struct apr_hdr          hdr;
+	uint32_t                mode_flags;
+	uint16_t                endpoint_type;
+	uint16_t                topo_bits_per_sample;
+	uint32_t                topo_id;
+	uint32_t                fmt_id;
+	uint32_t                shared_pos_buf_phy_addr_lsw;
+	uint32_t                shared_pos_buf_phy_addr_msw;
+	uint16_t                shared_pos_buf_mem_pool_id;
+	uint16_t                shared_pos_buf_num_regions;
+	uint32_t                shared_pos_buf_property_flag;
+	uint32_t                shared_circ_buf_start_phy_addr_lsw;
+	uint32_t                shared_circ_buf_start_phy_addr_msw;
+	uint32_t                shared_circ_buf_size;
+	uint16_t                shared_circ_buf_mem_pool_id;
+	uint16_t                shared_circ_buf_num_regions;
+	uint32_t                shared_circ_buf_property_flag;
+	uint32_t                num_watermark_levels;
+	struct asm_multi_channel_pcm_fmt_blk_v3         fmt;
+	struct avs_shared_map_region_payload            map_region_pos_buf;
+	struct avs_shared_map_region_payload            map_region_circ_buf;
+	struct asm_shared_watermark_level watermark[0];
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_READ_V3                 0x00010DB4
+
+/* Definition of the timestamp type flag bitmask */
+#define ASM_BIT_MASKIMESTAMPYPE_FLAG        (0x00000020UL)
+
+/* Definition of the timestamp type flag shift value. */
+#define ASM_SHIFTIMESTAMPYPE_FLAG 5
+
+/* Relative timestamp is identified by this value.*/
+#define ASM_RELATIVEIMESTAMP      0
+
+/* Absolute timestamp is identified by this value.*/
+#define ASM_ABSOLUTEIMESTAMP      1
+
+/* Bit value for Low Latency Tx stream subfield */
+#define ASM_LOW_LATENCY_TX_STREAM_SESSION			1
+
+/* Bit shift for the stream_perf_mode subfield. */
+#define ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ              29
+
+struct asm_stream_cmd_open_read_v3 {
+	struct apr_hdr hdr;
+	u32                    mode_flags;
+/* Mode flags that indicate whether meta information per encoded
+ * frame is to be provided.
+ * Supported values for bit 4:
+ *
+ * - 0 -- Return data buffer contains all encoded frames only; it
+ * does not contain frame metadata.
+ *
+ * - 1 -- Return data buffer contains an array of metadata and
+ * encoded frames.
+ *
+ * - Use #ASM_BIT_MASK_META_INFO_FLAG as the bitmask and
+ * #ASM_SHIFT_META_INFO_FLAG as the shift value for this bit.
+ *
+ *
+ * Supported values for bit 5:
+ *
+ * - ASM_RELATIVEIMESTAMP -- ASM_DATA_EVENT_READ_DONE_V2 will have
+ * - relative time-stamp.
+ * - ASM_ABSOLUTEIMESTAMP -- ASM_DATA_EVENT_READ_DONE_V2 will
+ * - have absolute time-stamp.
+ *
+ * - Use #ASM_BIT_MASKIMESTAMPYPE_FLAG as the bitmask and
+ * #ASM_SHIFTIMESTAMPYPE_FLAG as the shift value for this bit.
+ *
+ * All other bits are reserved; clients must set them to zero.
+ */
+
+	u32                    src_endpointype;
+/* Specifies the endpoint providing the input samples.
+ * Supported values:
+ * - 0 -- Device matrix
+ * - All other values are reserved; clients must set them to zero.
+ * Otherwise, an error is returned.
+ * The device matrix is the gateway from the tunneled Tx ports.
+ */
+
+	u32                    preprocopo_id;
+/* Specifies the topology (order of processing) of preprocessing
+ * algorithms. <i>None</i> means no preprocessing.
+ * Supported values:
+ * - #ASM_STREAM_PREPROCOPO_ID_DEFAULT
+ * - #ASM_STREAM_PREPROCOPO_ID_NONE
+ *
+ * This field can also be enabled through SetParams flags.
+ */
+
+	u32                    enc_cfg_id;
+/* Media configuration ID for encoded output.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2
+ * - #ASM_MEDIA_FMT_AAC_V2
+ * - #ASM_MEDIA_FMT_AMRNB_FS
+ * - #ASM_MEDIA_FMT_AMRWB_FS
+ * - #ASM_MEDIA_FMT_V13K_FS
+ * - #ASM_MEDIA_FMT_EVRC_FS
+ * - #ASM_MEDIA_FMT_EVRCB_FS
+ * - #ASM_MEDIA_FMT_EVRCWB_FS
+ * - #ASM_MEDIA_FMT_SBC
+ * - #ASM_MEDIA_FMT_G711_ALAW_FS
+ * - #ASM_MEDIA_FMT_G711_MLAW_FS
+ * - #ASM_MEDIA_FMT_G729A_FS
+ * - #ASM_MEDIA_FMT_EXAMPLE
+ * - #ASM_MEDIA_FMT_WMA_V8
+ */
+
+	u16                    bits_per_sample;
+/* Number of bits per sample processed by ASM modules.
+ * Supported values: 16 and 24 bits per sample
+ */
+
+	u16                    reserved;
+/* Reserved for future use. This field must be set to zero.*/
+} __packed;
+
+#define ASM_POPP_OUTPUT_SR_NATIVE_RATE                                  0
+
+/* Enumeration for the maximum sampling rate at the POPP output.*/
+#define ASM_POPP_OUTPUT_SR_MAX_RATE             48000
+
+#define ASM_STREAM_CMD_OPEN_READWRITE_V2        0x00010D8D
+#define ASM_STREAM_CMD_OPEN_READWRITE_V2        0x00010D8D
+
+struct asm_stream_cmd_open_readwrite_v2 {
+	struct apr_hdr         hdr;
+	u32                    mode_flags;
+/* Mode flags.
+ * Supported values for bit 2:
+ * - 0 -- SR/CM change notification event is disabled.
+ * - 1 -- SR/CM change notification event is enabled. Use
+ * #ASM_BIT_MASK_SR_CM_CHANGE_NOTIFY_FLAG and
+ * #ASM_SHIFT_SR_CM_CHANGE_NOTIFY_FLAG to set or
+ * getting this flag.
+ *
+ * Supported values for bit 4:
+ * - 0 -- Return read data buffer contains all encoded frames only; it
+ * does not contain frame metadata.
+ * - 1 -- Return read data buffer contains an array of metadata and
+ * encoded frames.
+ *
+ * All other bits are reserved; clients must set them to zero.
+ */
+
+	u32                    postprocopo_id;
+/* Specifies the topology (order of processing) of postprocessing
+ * algorithms. <i>None</i> means no postprocessing.
+ *
+ * Supported values:
+ * - #ASM_STREAM_POSTPROCOPO_ID_DEFAULT
+ * - #ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL
+ * - #ASM_STREAM_POSTPROCOPO_ID_NONE
+ */
+
+	u32                    dec_fmt_id;
+/* Specifies the media type of the input data. PCM indicates that
+ * no decoding must be performed, e.g., this is an NT encoder
+ * session.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2
+ * - #ASM_MEDIA_FMT_ADPCM
+ * - #ASM_MEDIA_FMT_MP3
+ * - #ASM_MEDIA_FMT_AAC_V2
+ * - #ASM_MEDIA_FMT_DOLBY_AAC
+ * - #ASM_MEDIA_FMT_AMRNB_FS
+ * - #ASM_MEDIA_FMT_AMRWB_FS
+ * - #ASM_MEDIA_FMT_V13K_FS
+ * - #ASM_MEDIA_FMT_EVRC_FS
+ * - #ASM_MEDIA_FMT_EVRCB_FS
+ * - #ASM_MEDIA_FMT_EVRCWB_FS
+ * - #ASM_MEDIA_FMT_SBC
+ * - #ASM_MEDIA_FMT_WMA_V10PRO_V2
+ * - #ASM_MEDIA_FMT_WMA_V9_V2
+ * - #ASM_MEDIA_FMT_AMR_WB_PLUS_V2
+ * - #ASM_MEDIA_FMT_AC3
+ * - #ASM_MEDIA_FMT_G711_ALAW_FS
+ * - #ASM_MEDIA_FMT_G711_MLAW_FS
+ * - #ASM_MEDIA_FMT_G729A_FS
+ * - #ASM_MEDIA_FMT_EXAMPLE
+ */
+
+	u32                    enc_cfg_id;
+/* Specifies the media type for the output of the stream. PCM
+ * indicates that no encoding must be performed, e.g., this is an NT
+ * decoder session.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2
+ * - #ASM_MEDIA_FMT_AAC_V2
+ * - #ASM_MEDIA_FMT_AMRNB_FS
+ * - #ASM_MEDIA_FMT_AMRWB_FS
+ * - #ASM_MEDIA_FMT_V13K_FS
+ * - #ASM_MEDIA_FMT_EVRC_FS
+ * - #ASM_MEDIA_FMT_EVRCB_FS
+ * - #ASM_MEDIA_FMT_EVRCWB_FS
+ * - #ASM_MEDIA_FMT_SBC
+ * - #ASM_MEDIA_FMT_G711_ALAW_FS
+ * - #ASM_MEDIA_FMT_G711_MLAW_FS
+ * - #ASM_MEDIA_FMT_G729A_FS
+ * - #ASM_MEDIA_FMT_EXAMPLE
+ * - #ASM_MEDIA_FMT_WMA_V8
+ */
+
+	u16                    bits_per_sample;
+/* Number of bits per sample processed by ASM modules.
+ * Supported values: 16 and 24 bits per sample
+ */
+
+	u16                    reserved;
+/* Reserved for future use. This field must be set to zero.*/
+
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_LOOPBACK_V2 0x00010D8E
+struct asm_stream_cmd_open_loopback_v2 {
+	struct apr_hdr         hdr;
+	u32                    mode_flags;
+/* Mode flags.
+ * Bit 0-31: reserved; client should set these bits to 0
+ */
+	u16                    src_endpointype;
+	/* Endpoint type. 0 = Tx Matrix */
+	u16                    sink_endpointype;
+	/* Endpoint type. 0 = Rx Matrix */
+	u32                    postprocopo_id;
+/* Postprocessor topology ID. Specifies the topology of
+ * postprocessing algorithms.
+ */
+
+	u16                    bits_per_sample;
+/* The number of bits per sample processed by ASM modules
+ * Supported values: 16 and 24 bits per sample
+ */
+	u16                    reserved;
+/* Reserved for future use. This field must be set to zero. */
+} __packed;
+
+#define ASM_STREAM_CMD_CLOSE             0x00010BCD
+#define ASM_STREAM_CMD_FLUSH             0x00010BCE
+
+
+#define ASM_STREAM_CMD_FLUSH_READBUFS   0x00010C09
+#define ASM_STREAM_CMD_SET_PP_PARAMS_V2 0x00010DA1
+
+struct asm_stream_cmd_set_pp_params_v2 {
+	u32                  data_payload_addr_lsw;
+/* LSW of parameter data payload address. Supported values: any. */
+	u32                  data_payload_addr_msw;
+/* MSW of Parameter data payload address. Supported values: any.
+ * - Must be set to zero for in-band data.
+ * - In the case of 32 bit Shared memory address, msw  field must be
+ * - set to zero.
+ * - In the case of 36 bit shared memory address, bit 31 to bit 4 of
+ * msw
+ *
+ * - must be set to zero.
+ */
+	u32                  mem_map_handle;
+/* Supported Values: Any.
+ * memory map handle returned by DSP through
+ * ASM_CMD_SHARED_MEM_MAP_REGIONS
+ * command.
+ * if mmhandle is NULL, the ParamData payloads are within the
+ * message payload (in-band).
+ * If mmhandle is non-NULL, the ParamData payloads begin at the
+ * address specified in the address msw and lsw (out-of-band).
+ */
+
+	u32                  data_payload_size;
+/* Size in bytes of the variable payload accompanying the
+ * message, or in shared memory. This field is used for parsing the
+ * parameter payload.
+ */
+} __packed;
+
+
+struct asm_stream_param_data_v2 {
+	u32                  module_id;
+	/* Unique module ID. */
+
+	u32                  param_id;
+	/* Unique parameter ID. */
+
+	u16                  param_size;
+/* Data size of the param_id/module_id combination. This is
+ * a multiple of 4 bytes.
+ */
+
+	u16                  reserved;
+/* Reserved for future enhancements. This field must be set to
+ * zero.
+ */
+
+} __packed;
+
+#define ASM_STREAM_CMD_GET_PP_PARAMS_V2		0x00010DA2
+
+struct asm_stream_cmd_get_pp_params_v2 {
+	u32                  data_payload_addr_lsw;
+	/* LSW of the parameter data payload address. */
+	u32                  data_payload_addr_msw;
+/* MSW of the parameter data payload address.
+ * - Size of the shared memory, if specified, shall be large enough
+ * to contain the whole ParamData payload, including Module ID,
+ * Param ID, Param Size, and Param Values
+ * - Must be set to zero for in-band data
+ * - In the case of 32 bit Shared memory address, msw field must be
+ * set to zero.
+ * - In the case of 36 bit shared memory address, bit 31 to bit 4 of
+ * msw must be set to zero.
+ */
+
+	u32                  mem_map_handle;
+/* Supported Values: Any.
+ * memory map handle returned by DSP through ASM_CMD_SHARED_MEM_MAP_REGIONS
+ * command.
+ * if mmhandle is NULL, the ParamData payloads in the ACK are within the
+ * message payload (in-band).
+ * If mmhandle is non-NULL, the ParamData payloads in the ACK begin at the
+ * address specified in the address msw and lsw.
+ * (out-of-band).
+ */
+
+	u32                  module_id;
+/* Unique module ID. */
+
+	u32                  param_id;
+/* Unique parameter ID. */
+
+	u16                  param_max_size;
+/* Maximum data size of the module_id/param_id combination. This
+ * is a multiple of 4 bytes.
+ */
+
+
+	u16                  reserved;
+/* Reserved for backward compatibility. Clients must set this
+ * field to zero.
+ */
+} __packed;
+
+#define ASM_STREAM_CMD_SET_ENCDEC_PARAM 0x00010C10
+
+#define ASM_PARAM_ID_ENCDEC_BITRATE     0x00010C13
+
+struct asm_bitrate_param {
+	u32                  bitrate;
+/* Maximum supported bitrate. Only the AAC encoder is supported.*/
+
+} __packed;
+
+#define ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2 0x00010DA3
+#define ASM_PARAM_ID_AAC_SBR_PS_FLAG		 0x00010C63
+
+/* Flag to turn off both SBR and PS processing, if they are
+ * present in the bitstream.
+ */
+
+#define ASM_AAC_SBR_OFF_PS_OFF (2)
+
+/* Flag to turn on SBR but turn off PS processing,if they are
+ * present in the bitstream.
+ */
+
+#define ASM_AAC_SBR_ON_PS_OFF  (1)
+
+/* Flag to turn on both SBR and PS processing, if they are
+ * present in the bitstream (default behavior).
+ */
+
+
+#define ASM_AAC_SBR_ON_PS_ON   (0)
+
+/* Structure for an AAC SBR PS processing flag. */
+
+/*  Payload of the #ASM_PARAM_ID_AAC_SBR_PS_FLAG parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ */
+struct asm_aac_sbr_ps_flag_param {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+
+	u32                  sbr_ps_flag;
+/* Control parameter to enable or disable SBR/PS processing in
+ * the AAC bitstream. Use the following macros to set this field:
+ * - #ASM_AAC_SBR_OFF_PS_OFF -- Turn off both SBR and PS
+ * processing, if they are present in the bitstream.
+ * - #ASM_AAC_SBR_ON_PS_OFF -- Turn on SBR processing, but not PS
+ * processing, if they are present in the bitstream.
+ * - #ASM_AAC_SBR_ON_PS_ON -- Turn on both SBR and PS processing,
+ * if they are present in the bitstream (default behavior).
+ * - All other values are invalid.
+ * Changes are applied to the next decoded frame.
+ */
+} __packed;
+
+#define ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING                      0x00010C64
+
+/*	First single channel element in a dual mono bitstream.*/
+#define ASM_AAC_DUAL_MONO_MAP_SCE_1                                 (1)
+
+/*	Second single channel element in a dual mono bitstream.*/
+#define ASM_AAC_DUAL_MONO_MAP_SCE_2                                 (2)
+
+/* Structure for AAC decoder dual mono channel mapping. */
+
+
+struct asm_aac_dual_mono_mapping_param {
+	struct apr_hdr							hdr;
+	struct asm_stream_cmd_set_encdec_param	encdec;
+	u16    left_channel_sce;
+	u16    right_channel_sce;
+
+} __packed;
+
+#define ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 0x00010DA4
+
+struct asm_stream_cmdrsp_get_pp_params_v2 {
+	u32                  status;
+} __packed;
+
+#define ASM_PARAM_ID_AC3_KARAOKE_MODE 0x00010D73
+
+/* Enumeration for both vocals in a karaoke stream.*/
+#define AC3_KARAOKE_MODE_NO_VOCAL     (0)
+
+/* Enumeration for only the left vocal in a karaoke stream.*/
+#define AC3_KARAOKE_MODE_LEFT_VOCAL   (1)
+
+/* Enumeration for only the right vocal in a karaoke stream.*/
+#define AC3_KARAOKE_MODE_RIGHT_VOCAL (2)
+
+/* Enumeration for both vocal channels in a karaoke stream.*/
+#define AC3_KARAOKE_MODE_BOTH_VOCAL             (3)
+#define ASM_PARAM_ID_AC3_DRC_MODE               0x00010D74
+/* Enumeration for the Custom Analog mode.*/
+#define AC3_DRC_MODE_CUSTOM_ANALOG              (0)
+
+/* Enumeration for the Custom Digital mode.*/
+#define AC3_DRC_MODE_CUSTOM_DIGITAL             (1)
+/* Enumeration for the Line Out mode (light compression).*/
+#define AC3_DRC_MODE_LINE_OUT  (2)
+
+/* Enumeration for the RF remodulation mode (heavy compression).*/
+#define AC3_DRC_MODE_RF_REMOD                         (3)
+#define ASM_PARAM_ID_AC3_DUAL_MONO_MODE               0x00010D75
+
+/* Enumeration for playing dual mono in stereo mode.*/
+#define AC3_DUAL_MONO_MODE_STEREO                     (0)
+
+/* Enumeration for playing left mono.*/
+#define AC3_DUAL_MONO_MODE_LEFT_MONO                  (1)
+
+/* Enumeration for playing right mono.*/
+#define AC3_DUAL_MONO_MODE_RIGHT_MONO                 (2)
+
+/* Enumeration for mixing both dual mono channels and playing them.*/
+#define AC3_DUAL_MONO_MODE_MIXED_MONO        (3)
+#define ASM_PARAM_ID_AC3_STEREO_DOWNMIX_MODE 0x00010D76
+
+/* Enumeration for using the Downmix mode indicated in the bitstream. */
+
+#define AC3_STEREO_DOWNMIX_MODE_AUTO_DETECT  (0)
+
+/* Enumeration for Surround Compatible mode (preserves the
+ * surround information).
+ */
+
+#define AC3_STEREO_DOWNMIX_MODE_LT_RT        (1)
+/* Enumeration for Mono Compatible mode (if the output is to be
+ * further downmixed to mono).
+ */
+
+#define AC3_STEREO_DOWNMIX_MODE_LO_RO (2)
+
+/* ID of the AC3 PCM scale factor parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ */
+#define ASM_PARAM_ID_AC3_PCM_SCALEFACTOR 0x00010D78
+
+/* ID of the AC3 DRC boost scale factor parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ */
+#define ASM_PARAM_ID_AC3_DRC_BOOST_SCALEFACTOR 0x00010D79
+
+/* ID of the AC3 DRC cut scale factor parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ */
+#define ASM_PARAM_ID_AC3_DRC_CUT_SCALEFACTOR 0x00010D7A
+
+/* Structure for AC3 Generic Parameter. */
+
+/*  Payload of the AC3 parameters in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ */
+struct asm_ac3_generic_param {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	struct asm_enc_cfg_blk_param_v2	encblk;
+	u32                  generic_parameter;
+/* AC3 generic parameter. Select from one of the following
+ * possible values.
+ *
+ * For #ASM_PARAM_ID_AC3_KARAOKE_MODE, supported values are:
+ * - AC3_KARAOKE_MODE_NO_VOCAL
+ * - AC3_KARAOKE_MODE_LEFT_VOCAL
+ * - AC3_KARAOKE_MODE_RIGHT_VOCAL
+ * - AC3_KARAOKE_MODE_BOTH_VOCAL
+ *
+ * For #ASM_PARAM_ID_AC3_DRC_MODE, supported values are:
+ * - AC3_DRC_MODE_CUSTOM_ANALOG
+ * - AC3_DRC_MODE_CUSTOM_DIGITAL
+ * - AC3_DRC_MODE_LINE_OUT
+ * - AC3_DRC_MODE_RF_REMOD
+ *
+ * For #ASM_PARAM_ID_AC3_DUAL_MONO_MODE, supported values are:
+ * - AC3_DUAL_MONO_MODE_STEREO
+ * - AC3_DUAL_MONO_MODE_LEFT_MONO
+ * - AC3_DUAL_MONO_MODE_RIGHT_MONO
+ * - AC3_DUAL_MONO_MODE_MIXED_MONO
+ *
+ * For #ASM_PARAM_ID_AC3_STEREO_DOWNMIX_MODE, supported values are:
+ * - AC3_STEREO_DOWNMIX_MODE_AUTO_DETECT
+ * - AC3_STEREO_DOWNMIX_MODE_LT_RT
+ * - AC3_STEREO_DOWNMIX_MODE_LO_RO
+ *
+ * For #ASM_PARAM_ID_AC3_PCM_SCALEFACTOR, supported values are
+ * 0 to 1 in Q31 format.
+ *
+ * For #ASM_PARAM_ID_AC3_DRC_BOOST_SCALEFACTOR, supported values are
+ * 0 to 1 in Q31 format.
+ *
+ * For #ASM_PARAM_ID_AC3_DRC_CUT_SCALEFACTOR, supported values are
+ * 0 to 1 in Q31 format.
+ */
+} __packed;
+
+/* Enumeration for Raw mode (no downmixing), which specifies
+ * that all channels in the bitstream are to be played out as is
+ * without any downmixing. (Default)
+ */
+
+#define WMAPRO_CHANNEL_MASK_RAW (-1)
+
+/* Enumeration for setting the channel mask to 0. The 7.1 mode
+ * (Home Theater) is assigned.
+ */
+
+
+#define WMAPRO_CHANNEL_MASK_ZERO 0x0000
+
+/* Speaker layout mask for one channel (Home Theater, mono).
+ * - Speaker front center
+ */
+#define WMAPRO_CHANNEL_MASK_1_C 0x0004
+
+/* Speaker layout mask for two channels (Home Theater, stereo).
+ * - Speaker front left
+ * - Speaker front right
+ */
+#define WMAPRO_CHANNEL_MASK_2_L_R 0x0003
+
+/* Speaker layout mask for three channels (Home Theater).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ */
+#define WMAPRO_CHANNEL_MASK_3_L_C_R 0x0007
+
+/* Speaker layout mask for two channels (stereo).
+ * - Speaker back left
+ * - Speaker back right
+ */
+#define WMAPRO_CHANNEL_MASK_2_Bl_Br  0x0030
+
+/* Speaker layout mask for four channels.
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker back left
+ * - Speaker back right
+ */
+#define WMAPRO_CHANNEL_MASK_4_L_R_Bl_Br 0x0033
+
+/* Speaker layout mask for four channels (Home Theater).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker back center
+ */
+#define WMAPRO_CHANNEL_MASK_4_L_R_C_Bc_HT 0x0107
+/* Speaker layout mask for five channels.
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker back left
+ * - Speaker back right
+ */
+#define WMAPRO_CHANNEL_MASK_5_L_C_R_Bl_Br  0x0037
+
+/* Speaker layout mask for five channels (5 mode, Home Theater).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker side left
+ * - Speaker side right
+ */
+#define WMAPRO_CHANNEL_MASK_5_L_C_R_Sl_Sr_HT   0x0607
+/* Speaker layout mask for six channels (5.1 mode).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker low frequency
+ * - Speaker back left
+ * - Speaker back right
+ */
+#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Bl_Br_SLF  0x003F
+/* Speaker layout mask for six channels (5.1 mode, Home Theater).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker low frequency
+ * - Speaker side left
+ * - Speaker side right
+ */
+#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Sl_Sr_SLF_HT  0x060F
+/* Speaker layout mask for six channels (5.1 mode, no LFE).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker back left
+ * - Speaker back right
+ * - Speaker back center
+ */
+#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Bl_Br_Bc  0x0137
+/* Speaker layout mask for six channels (5.1 mode, Home Theater,
+ * no LFE).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker back center
+ * - Speaker side left
+ * - Speaker side right
+ */
+#define WMAPRO_CHANNEL_MASK_5DOT1_L_C_R_Sl_Sr_Bc_HT   0x0707
+
+/* Speaker layout mask for seven channels (6.1 mode).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker low frequency
+ * - Speaker back left
+ * - Speaker back right
+ * - Speaker back center
+ */
+#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Bl_Br_Bc_SLF   0x013F
+
+/* Speaker layout mask for seven channels (6.1 mode, Home
+ * Theater).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker low frequency
+ * - Speaker back center
+ * - Speaker side left
+ * - Speaker side right
+ */
+#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Sl_Sr_Bc_SLF_HT 0x070F
+
+/* Speaker layout mask for seven channels (6.1 mode, no LFE).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker back left
+ * - Speaker back right
+ * - Speaker front left of center
+ * - Speaker front right of center
+ */
+#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Bl_Br_SFLOC_SFROC   0x00F7
+
+/* Speaker layout mask for seven channels (6.1 mode, Home
+ * Theater, no LFE).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker side left
+ * - Speaker side right
+ * - Speaker front left of center
+ * - Speaker front right of center
+ */
+#define WMAPRO_CHANNEL_MASK_6DOT1_L_C_R_Sl_Sr_SFLOC_SFROC_HT 0x0637
+
+/* Speaker layout mask for eight channels (7.1 mode).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker back left
+ * - Speaker back right
+ * - Speaker low frequency
+ * - Speaker front left of center
+ * - Speaker front right of center
+ */
+#define WMAPRO_CHANNEL_MASK_7DOT1_L_C_R_Bl_Br_SLF_SFLOC_SFROC \
+					0x00FF
+
+/* Speaker layout mask for eight channels (7.1 mode, Home Theater).
+ * - Speaker front left
+ * - Speaker front right
+ * - Speaker front center
+ * - Speaker side left
+ * - Speaker side right
+ * - Speaker low frequency
+ * - Speaker front left of center
+ * - Speaker front right of center
+ *
+ */
+#define WMAPRO_CHANNEL_MASK_7DOT1_L_C_R_Sl_Sr_SLF_SFLOC_SFROC_HT \
+					0x063F
+
+#define ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP  0x00010D82
+
+/* Maximum number of decoder output channels. */
+#define MAX_CHAN_MAP_CHANNELS  16
+
+/* Structure for decoder output channel mapping. */
+
+/* Payload of the #ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ */
+struct asm_dec_out_chan_map_param {
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_encdec_param  encdec;
+	u32                 num_channels;
+/* Number of decoder output channels.
+ * Supported values: 0 to #MAX_CHAN_MAP_CHANNELS
+ *
+ * A value of 0 indicates native channel mapping, which is valid
+ * only for NT mode. This means the output of the decoder is to be
+ * preserved as is.
+ */
+	u8                  channel_mapping[MAX_CHAN_MAP_CHANNELS];
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED  0x00010D84
+
+/* Bitmask for the IEC 61937 enable flag.*/
+#define ASM_BIT_MASK_IEC_61937_STREAM_FLAG   (0x00000001UL)
+
+/* Shift value for the IEC 61937 enable flag.*/
+#define ASM_SHIFT_IEC_61937_STREAM_FLAG  0
+
+/* Bitmask for the IEC 60958 enable flag.*/
+#define ASM_BIT_MASK_IEC_60958_STREAM_FLAG   (0x00000002UL)
+
+/* Shift value for the IEC 60958 enable flag.*/
+#define ASM_SHIFT_IEC_60958_STREAM_FLAG   1
+
+/* Payload format for open write compressed command */
+
+/* Payload format for the #ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED
+ * command, which opens a stream for a given session ID and stream ID
+ * to be rendered in the compressed format.
+ */
+
+struct asm_stream_cmd_open_write_compressed {
+	struct apr_hdr hdr;
+	u32                    flags;
+/* Mode flags that configure the stream for a specific format.
+ * Supported values:
+ * - Bit 0 -- IEC 61937 compatibility
+ *   - 0 -- Stream is not in IEC 61937 format
+ *   - 1 -- Stream is in IEC 61937 format
+ * - Bit 1 -- IEC 60958 compatibility
+ *   - 0 -- Stream is not in IEC 60958 format
+ *   - 1 -- Stream is in IEC 60958 format
+ * - Bits 2 to 31 -- 0 (Reserved)
+ *
+ * For the same stream, bit 0 cannot be set to 0 and bit 1 cannot
+ * be set to 1. A compressed stream connot have IEC 60958
+ * packetization applied without IEC 61937 packetization.
+ * @note1hang Currently, IEC 60958 packetized input streams are not
+ * supported.
+ */
+
+
+	u32                    fmt_id;
+/* Specifies the media type of the HDMI stream to be opened.
+ * Supported values:
+ * - #ASM_MEDIA_FMT_AC3
+ * - #ASM_MEDIA_FMT_EAC3
+ * - #ASM_MEDIA_FMT_DTS
+ * - #ASM_MEDIA_FMT_ATRAC
+ * - #ASM_MEDIA_FMT_MAT
+ *
+ * @note1hang This field must be set to a valid media type even if
+ * IEC 61937 packetization is not performed by the aDSP.
+ */
+
+} __packed;
+
+
+/* Indicates the number of samples per channel to be removed from the
+ * beginning of the stream.
+ */
+#define ASM_DATA_CMD_REMOVE_INITIAL_SILENCE 0x00010D67
+
+/* Indicates the number of samples per channel to be removed from
+ * the end of the stream.
+ */
+#define ASM_DATA_CMD_REMOVE_TRAILING_SILENCE 0x00010D68
+
+struct asm_data_cmd_remove_silence {
+	struct apr_hdr hdr;
+	u32	num_samples_to_remove;
+	/* < Number of samples per channel to be removed.
+	 * @values 0 to (2@sscr{32}-1)
+	 */
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED                        0x00010D95
+
+struct asm_stream_cmd_open_read_compressed {
+	struct apr_hdr hdr;
+	u32                    mode_flags;
+/* Mode flags that indicate whether meta information per encoded
+ * frame is to be provided.
+ * Supported values for bit 4:
+ * - 0 -- Return data buffer contains all encoded frames only; it does
+ *      not contain frame metadata.
+ * - 1 -- Return data buffer contains an array of metadata and encoded
+ *      frames.
+ * - Use #ASM_BIT_MASK_META_INFO_FLAG to set the bitmask and
+ * #ASM_SHIFT_META_INFO_FLAG to set the shift value for this bit.
+ * All other bits are reserved; clients must set them to zero.
+ */
+
+	u32                    frames_per_buf;
+/* Indicates the number of frames that need to be returned per
+ * read buffer
+ * Supported values: should be greater than 0
+ */
+
+} __packed;
+
+/* adsp_asm_stream_commands.h*/
+
+
+/* adsp_asm_api.h (no changes)*/
+#define ASM_STREAM_POSTPROCOPO_ID_DEFAULT \
+								0x00010BE4
+#define ASM_STREAM_POSTPROCOPO_ID_PEAKMETER \
+								0x00010D83
+#define ASM_STREAM_POSTPROCOPO_ID_NONE \
+								0x00010C68
+#define ASM_STREAM_POSTPROCOPO_ID_MCH_PEAK_VOL \
+								0x00010D8B
+#define ASM_STREAM_PREPROCOPO_ID_DEFAULT \
+			ASM_STREAM_POSTPROCOPO_ID_DEFAULT
+#define ASM_STREAM_PREPROCOPO_ID_NONE \
+			ASM_STREAM_POSTPROCOPO_ID_NONE
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_NONE_AUDIO_COPP \
+			0x00010312
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MONO_AUDIO_COPP \
+								0x00010313
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP \
+								0x00010314
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_IIR_AUDIO_COPP\
+								0x00010704
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MONO_AUDIO_COPP_MBDRCV2\
+								0x0001070D
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_MBDRCV2\
+								0x0001070E
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_IIR_AUDIO_COPP_MBDRCV2\
+								0x0001070F
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_STEREO_AUDIO_COPP_MBDRC_V3 \
+								0x11000000
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_SPEAKER_MCH_PEAK_VOL \
+								0x0001031B
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_MONO_AUDIO_COPP  0x00010315
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_MIC_STEREO_AUDIO_COPP 0x00010316
+#define AUDPROC_COPPOPOLOGY_ID_MCHAN_IIR_AUDIO           0x00010715
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_DEFAULT_AUDIO_COPP   0x00010BE3
+#define ADM_CMD_COPP_OPENOPOLOGY_ID_PEAKMETER_AUDIO_COPP 0x00010317
+#define AUDPROC_MODULE_ID_AIG   0x00010716
+#define AUDPROC_PARAM_ID_AIG_ENABLE		0x00010717
+#define AUDPROC_PARAM_ID_AIG_CONFIG		0x00010718
+
+struct Audio_AigParam {
+	uint16_t	mode;
+/*< Mode word for enabling AIG/SIG mode .
+ * Byte offset: 0
+ */
+	int16_t		staticGainL16Q12;
+/*< Static input gain when aigMode is set to 1.
+ * Byte offset: 2
+ */
+	int16_t		initialGainDBL16Q7;
+/*<Initial value that the adaptive gain update starts from dB
+ * Q7 Byte offset: 4
+ */
+	int16_t		idealRMSDBL16Q7;
+/*<Average RMS level that AIG attempts to achieve Q8.7
+ * Byte offset: 6
+ */
+	int32_t		noiseGateL32;
+/*Threshold below which signal is considered as noise and AIG
+ * Byte offset: 8
+ */
+	int32_t		minGainL32Q15;
+/*Minimum gain that can be provided by AIG Q16.15
+ * Byte offset: 12
+ */
+	int32_t		maxGainL32Q15;
+/*Maximum gain that can be provided by AIG Q16.15
+ * Byte offset: 16
+ */
+	uint32_t		gainAtRtUL32Q31;
+/*Attack/release time for AIG update Q1.31
+ * Byte offset: 20
+ */
+	uint32_t		longGainAtRtUL32Q31;
+/*Long attack/release time while updating gain for
+ * noise/silence Q1.31 Byte offset: 24
+ */
+
+	uint32_t		rmsTavUL32Q32;
+/* RMS smoothing time constant used for long-term RMS estimate
+ * Q0.32 Byte offset: 28
+ */
+
+	uint32_t		gainUpdateStartTimMsUL32Q0;
+/* The waiting time before which AIG starts to apply adaptive
+ * gain update Q32.0 Byte offset: 32
+ */
+
+} __packed;
+
+
+#define ADM_MODULE_ID_EANS                            0x00010C4A
+#define ADM_PARAM_ID_EANS_ENABLE                      0x00010C4B
+#define ADM_PARAM_ID_EANS_PARAMS                      0x00010C4C
+
+struct adm_eans_enable {
+
+	uint32_t                  enable_flag;
+/*< Specifies whether EANS is disabled (0) or enabled
+ * (nonzero).
+ * This is supported only for sampling rates of 8, 12, 16, 24, 32,
+ * and 48 kHz. It is not supported for sampling rates of 11.025,
+ * 22.05, or 44.1 kHz.
+ */
+
+} __packed;
+
+
+struct adm_eans_params {
+	int16_t                         eans_mode;
+/*< Mode word for enabling/disabling submodules.
+ * Byte offset: 0
+ */
+
+	int16_t                         eans_input_gain;
+/*< Q2.13 input gain to the EANS module.
+ * Byte offset: 2
+ */
+
+	int16_t                         eans_output_gain;
+/*< Q2.13 output gain to the EANS module.
+ * Byte offset: 4
+ */
+
+	int16_t                         eansarget_ns;
+/*< Target noise suppression level in dB.
+ * Byte offset: 6
+ */
+
+	int16_t                         eans_s_alpha;
+/*< Q3.12 over-subtraction factor for stationary noise
+ * suppression.
+ * Byte offset: 8
+ */
+
+	int16_t                         eans_n_alpha;
+/* < Q3.12 over-subtraction factor for nonstationary noise
+ * suppression.
+ * Byte offset: 10
+ */
+
+	int16_t                         eans_n_alphamax;
+/*< Q3.12 maximum over-subtraction factor for nonstationary
+ * noise suppression.
+ * Byte offset: 12
+ */
+	int16_t                         eans_e_alpha;
+/*< Q15 scaling factor for excess noise suppression.
+ * Byte offset: 14
+ */
+
+	int16_t                         eans_ns_snrmax;
+/*< Upper boundary in dB for SNR estimation.
+ * Byte offset: 16
+ */
+
+	int16_t                         eans_sns_block;
+/*< Quarter block size for stationary noise suppression.
+ * Byte offset: 18
+ */
+
+	int16_t                         eans_ns_i;
+/*< Initialization block size for noise suppression.
+ * Byte offset: 20
+ */
+	int16_t                         eans_np_scale;
+/*< Power scale factor for nonstationary noise update.
+ * Byte offset: 22
+ */
+
+	int16_t                         eans_n_lambda;
+/*< Smoothing factor for higher level nonstationary noise
+ * update.
+ * Byte offset: 24
+ */
+
+	int16_t                         eans_n_lambdaf;
+/*< Medium averaging factor for noise update.
+ * Byte offset: 26
+ */
+
+	int16_t                         eans_gs_bias;
+/*< Bias factor in dB for gain calculation.
+ * Byte offset: 28
+ */
+
+	int16_t                         eans_gs_max;
+/*< SNR lower boundary in dB for aggressive gain calculation.
+ * Byte offset: 30
+ */
+
+	int16_t                         eans_s_alpha_hb;
+/*< Q3.12 over-subtraction factor for high-band stationary
+ * noise suppression.
+ * Byte offset: 32
+ */
+
+	int16_t                         eans_n_alphamax_hb;
+/*< Q3.12 maximum over-subtraction factor for high-band
+ * nonstationary noise suppression.
+ * Byte offset: 34
+ */
+
+	int16_t                         eans_e_alpha_hb;
+/*< Q15 scaling factor for high-band excess noise suppression.
+ * Byte offset: 36
+ */
+
+	int16_t                         eans_n_lambda0;
+/*< Smoothing factor for nonstationary noise update during
+ * speech activity.
+ * Byte offset: 38
+ */
+
+	int16_t                         thresh;
+/*< Threshold for generating a binary VAD decision.
+ * Byte offset: 40
+ */
+
+	int16_t                         pwr_scale;
+/*< Indirect lower boundary of the noise level estimate.
+ * Byte offset: 42
+ */
+
+	int16_t                         hangover_max;
+/*< Avoids mid-speech clipping and reliably detects weak speech
+ * bursts at the end of speech activity.
+ * Byte offset: 44
+ */
+
+	int16_t                         alpha_snr;
+/*< Controls responsiveness of the VAD.
+ * Byte offset: 46
+ */
+
+	int16_t                         snr_diff_max;
+/*< Maximum SNR difference. Decreasing this parameter value may
+ * help in making correct decisions during abrupt changes; however,
+ * decreasing too much may increase false alarms during long
+ * pauses/silences.
+ * Byte offset: 48
+ */
+
+	int16_t                         snr_diff_min;
+/*< Minimum SNR difference. Decreasing this parameter value may
+ * help in making correct decisions during abrupt changes; however,
+ * decreasing too much may increase false alarms during long
+ * pauses/silences.
+ * Byte offset: 50
+ */
+
+	int16_t                         init_length;
+/*< Defines the number of frames for which a noise level
+ * estimate is set to a fixed value.
+ * Byte offset: 52
+ */
+
+	int16_t                         max_val;
+/*< Defines the upper limit of the noise level.
+ * Byte offset: 54
+ */
+
+	int16_t                         init_bound;
+/*< Defines the initial bounding value for the noise level
+ * estimate. This is used during the initial segment defined by the
+ * init_length parameter.
+ * Byte offset: 56
+ */
+
+	int16_t                         reset_bound;
+/*< Reset boundary for noise tracking.
+ * Byte offset: 58
+ */
+
+	int16_t                         avar_scale;
+/*< Defines the bias factor in noise estimation.
+ * Byte offset: 60
+ */
+
+	int16_t                         sub_nc;
+/*< Defines the window length for noise estimation.
+ * Byte offset: 62
+ */
+
+	int16_t                         spow_min;
+/*< Defines the minimum signal power required to update the
+ * boundaries for the noise floor estimate.
+ * Byte offset: 64
+ */
+
+	int16_t                         eans_gs_fast;
+/*< Fast smoothing factor for postprocessor gain.
+ * Byte offset: 66
+ */
+
+	int16_t                         eans_gs_med;
+/*< Medium smoothing factor for postprocessor gain.
+ * Byte offset: 68
+ */
+
+	int16_t                         eans_gs_slow;
+/*< Slow smoothing factor for postprocessor gain.
+ * Byte offset: 70
+ */
+
+	int16_t                         eans_swb_salpha;
+/*< Q3.12 super wideband aggressiveness factor for stationary
+ * noise suppression.
+ * Byte offset: 72
+ */
+
+	int16_t                         eans_swb_nalpha;
+/*< Q3.12 super wideband aggressiveness factor for
+ * nonstationary noise suppression.
+ * Byte offset: 74
+ */
+} __packed;
+#define ADM_MODULE_IDX_MIC_GAIN_CTRL   0x00010C35
+
+/* @addtogroup audio_pp_param_ids
+ * ID of the Tx mic gain control parameter used by the
+ * #ADM_MODULE_IDX_MIC_GAIN_CTRL module.
+ * @messagepayload
+ * @structure{admx_mic_gain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_IDX_MIC_GAIN.tex}
+ */
+#define ADM_PARAM_IDX_MIC_GAIN       0x00010C36
+
+/* Structure for a Tx mic gain parameter for the mic gain
+ * control module.
+ */
+
+
+/* @brief Payload of the #ADM_PARAM_IDX_MIC_GAIN parameter in the
+ * Tx Mic Gain Control module.
+ */
+struct admx_mic_gain {
+	uint16_t                  tx_mic_gain;
+	/*< Linear gain in Q13 format. */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero. */
+} __packed;
+
+/* end_addtogroup audio_pp_param_ids */
+
+/* @ingroup audio_pp_module_ids
+ * ID of the Rx Codec Gain Control module.
+ *
+ * This module supports the following parameter ID:
+ * - #ADM_PARAM_ID_RX_CODEC_GAIN
+ */
+#define ADM_MODULE_ID_RX_CODEC_GAIN_CTRL       0x00010C37
+
+/* @addtogroup audio_pp_param_ids
+ * ID of the Rx codec gain control parameter used by the
+ * #ADM_MODULE_ID_RX_CODEC_GAIN_CTRL module.
+ *
+ * @messagepayload
+ * @structure{adm_rx_codec_gain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_RX_CODEC_GAIN.tex}
+ */
+#define ADM_PARAM_ID_RX_CODEC_GAIN   0x00010C38
+
+/* Structure for the Rx common codec gain control module. */
+
+
+/* @brief Payload of the #ADM_PARAM_ID_RX_CODEC_GAIN parameter
+ * in the Rx Codec Gain Control module.
+ */
+
+
+struct adm_rx_codec_gain {
+	uint16_t                  rx_codec_gain;
+	/* Linear gain in Q13 format. */
+
+	uint16_t                  reserved;
+	/* Clients must set this field to zero.*/
+} __packed;
+
+/* end_addtogroup audio_pp_param_ids */
+
+/* @ingroup audio_pp_module_ids
+ * ID of the HPF Tuning Filter module on the Tx path.
+ * This module supports the following parameter IDs:
+ * - #ADM_PARAM_ID_HPF_IIRX_FILTER_ENABLE_CONFIG
+ * - #ADM_PARAM_ID_HPF_IIRX_FILTER_PRE_GAIN
+ * - #ADM_PARAM_ID_HPF_IIRX_FILTER_CONFIG_PARAMS
+ */
+#define ADM_MODULE_ID_HPF_IIRX_FILTER    0x00010C3D
+
+/* @addtogroup audio_pp_param_ids */
+/* ID of the Tx HPF IIR filter enable parameter used by the
+ * #ADM_MODULE_ID_HPF_IIRX_FILTER module.
+ * @parspace Message payload
+ * @structure{adm_hpfx_iir_filter_enable_cfg}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_HPF_IIRX_FILTER_ENABLE_CONFIG.tex}
+ */
+#define ADM_PARAM_ID_HPF_IIRX_FILTER_ENABLE_CONFIG   0x00010C3E
+
+/* ID of the Tx HPF IIR filter pregain parameter used by the
+ * #ADM_MODULE_ID_HPF_IIRX_FILTER module.
+ * @parspace Message payload
+ * @structure{adm_hpfx_iir_filter_pre_gain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_HPF_IIRX_FILTER_PRE_GAIN.tex}
+ */
+#define ADM_PARAM_ID_HPF_IIRX_FILTER_PRE_GAIN   0x00010C3F
+
+/* ID of the Tx HPF IIR filter configuration parameters used by the
+ * #ADM_MODULE_ID_HPF_IIRX_FILTER module.
+ * @parspace Message payload
+ * @structure{adm_hpfx_iir_filter_cfg_params}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_HPF_IIRX_FILTER_CONFIG_PA
+ * RAMS.tex}
+ */
+#define ADM_PARAM_ID_HPF_IIRX_FILTER_CONFIG_PARAMS  0x00010C40
+
+/* Structure for enabling a configuration parameter for
+ * the HPF IIR tuning filter module on the Tx path.
+ */
+
+/* @brief Payload of the #ADM_PARAM_ID_HPF_IIRX_FILTER_ENABLE_CONFIG
+ * parameter in the Tx path HPF Tuning Filter module.
+ */
+struct adm_hpfx_iir_filter_enable_cfg {
+	uint32_t                  enable_flag;
+/* Specifies whether the HPF tuning filter is disabled (0) or
+ * enabled (nonzero).
+ */
+} __packed;
+
+
+/* Structure for the pregain parameter for the HPF
+ * IIR tuning filter module on the Tx path.
+ */
+
+
+/* @brief Payload of the #ADM_PARAM_ID_HPF_IIRX_FILTER_PRE_GAIN parameter
+ * in the Tx path HPF Tuning Filter module.
+ */
+struct adm_hpfx_iir_filter_pre_gain {
+	uint16_t                  pre_gain;
+	/* Linear gain in Q13 format. */
+
+	uint16_t                  reserved;
+	/* Clients must set this field to zero.*/
+} __packed;
+
+
+/* Structure for the configuration parameter for the
+ * HPF IIR tuning filter module on the Tx path.
+ */
+
+
+/* @brief Payload of the #ADM_PARAM_ID_HPF_IIRX_FILTER_CONFIG_PARAMS
+ * parameters in the Tx path HPF Tuning Filter module. \n
+ * \n
+ * This structure is followed by tuning filter coefficients as follows: \n
+ * - Sequence of int32_t FilterCoeffs.
+ * Each band has five coefficients, each in int32_t format in the order of
+ * b0, b1, b2, a1, a2.
+ * - Sequence of int16_t NumShiftFactor.
+ * One int16_t per band. The numerator shift factor is related to the Q
+ * factor of the filter coefficients.
+ * - Sequence of uint16_t PanSetting.
+ * One uint16_t for each band to indicate application of the filter to
+ * left (0), right (1), or both (2) channels.
+ */
+struct adm_hpfx_iir_filter_cfg_params {
+	uint16_t                  num_biquad_stages;
+/*< Number of bands.
+ * Supported values: 0 to 20
+ */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero.*/
+} __packed;
+
+/* end_addtogroup audio_pp_module_ids */
+
+/* @addtogroup audio_pp_module_ids */
+/* ID of the Tx path IIR Tuning Filter module.
+ *	This module supports the following parameter IDs:
+ *	- #ADM_PARAM_IDX_IIR_FILTER_ENABLE_CONFIG
+ */
+#define ADM_MODULE_IDX_IIR_FILTER 0x00010C41
+
+/* ID of the Rx path IIR Tuning Filter module for the left channel.
+ *	The parameter IDs of the IIR tuning filter module
+ *	(#ASM_MODULE_ID_IIRUNING_FILTER) are used for the left IIR Rx tuning
+ *	filter.
+ *
+ * Pan parameters are not required for this per-channel IIR filter; the pan
+ * parameters are ignored by this module.
+ */
+#define ADM_MODULE_ID_LEFT_IIRUNING_FILTER      0x00010705
+
+/* ID of the the Rx path IIR Tuning Filter module for the right
+ * channel.
+ * The parameter IDs of the IIR tuning filter module
+ * (#ASM_MODULE_ID_IIRUNING_FILTER) are used for the right IIR Rx
+ * tuning filter.
+ *
+ * Pan parameters are not required for this per-channel IIR filter;
+ * the pan parameters are ignored by this module.
+ */
+#define ADM_MODULE_ID_RIGHT_IIRUNING_FILTER    0x00010706
+
+/* end_addtogroup audio_pp_module_ids */
+
+/* @addtogroup audio_pp_param_ids */
+
+/* ID of the Tx IIR filter enable parameter used by the
+ * #ADM_MODULE_IDX_IIR_FILTER module.
+ * @parspace Message payload
+ * @structure{admx_iir_filter_enable_cfg}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_IDX_IIR_FILTER_ENABLE_CONFIG.tex}
+ */
+#define ADM_PARAM_IDX_IIR_FILTER_ENABLE_CONFIG   0x00010C42
+
+/* ID of the Tx IIR filter pregain parameter used by the
+ * #ADM_MODULE_IDX_IIR_FILTER module.
+ * @parspace Message payload
+ * @structure{admx_iir_filter_pre_gain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_IDX_IIR_FILTER_PRE_GAIN.tex}
+ */
+#define ADM_PARAM_IDX_IIR_FILTER_PRE_GAIN    0x00010C43
+
+/* ID of the Tx IIR filter configuration parameters used by the
+ * #ADM_MODULE_IDX_IIR_FILTER module.
+ * @parspace Message payload
+ * @structure{admx_iir_filter_cfg_params}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_IDX_IIR_FILTER_CONFIG_PARAMS.tex}
+ */
+#define ADM_PARAM_IDX_IIR_FILTER_CONFIG_PARAMS     0x00010C44
+
+/* Structure for enabling the configuration parameter for the
+ * IIR filter module on the Tx path.
+ */
+
+/* @brief Payload of the #ADM_PARAM_IDX_IIR_FILTER_ENABLE_CONFIG
+ * parameter in the Tx Path IIR Tuning Filter module.
+ */
+
+struct admx_iir_filter_enable_cfg {
+	uint32_t                  enable_flag;
+/*< Specifies whether the IIR tuning filter is disabled (0) or
+ * enabled (nonzero).
+ */
+
+} __packed;
+
+
+/* Structure for the pregain parameter for the
+ * IIR filter module on the Tx path.
+ */
+
+
+/* @brief Payload of the #ADM_PARAM_IDX_IIR_FILTER_PRE_GAIN
+ * parameter in the Tx Path IIR Tuning Filter module.
+ */
+
+struct admx_iir_filter_pre_gain {
+	uint16_t                  pre_gain;
+	/*< Linear gain in Q13 format. */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero.*/
+} __packed;
+
+
+/* Structure for the configuration parameter for the
+ * IIR filter module on the Tx path.
+ */
+
+
+/* @brief Payload of the #ADM_PARAM_IDX_IIR_FILTER_CONFIG_PARAMS
+ * parameter in the Tx Path IIR Tuning Filter module. \n
+ *	\n
+ * This structure is followed by the HPF IIR filter coefficients on
+ * the Tx path as follows: \n
+ * - Sequence of int32_t ulFilterCoeffs. Each band has five
+ * coefficients, each in int32_t format in the order of b0, b1, b2,
+ * a1, a2.
+ * - Sequence of int16_t sNumShiftFactor. One int16_t per band. The
+ * numerator shift factor is related to the Q factor of the filter
+ * coefficients.
+ * - Sequence of uint16_t usPanSetting. One uint16_t for each band
+ * to indicate if the filter is applied to left (0), right (1), or
+ * both (2) channels.
+ */
+struct admx_iir_filter_cfg_params {
+	uint16_t                  num_biquad_stages;
+/*< Number of bands.
+ * Supported values: 0 to 20
+ */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero.*/
+} __packed;
+
+/* end_addtogroup audio_pp_module_ids */
+
+/* @ingroup audio_pp_module_ids
+ *	ID of the QEnsemble module.
+ *	This module supports the following parameter IDs:
+ *	- #ADM_PARAM_ID_QENSEMBLE_ENABLE
+ *	- #ADM_PARAM_ID_QENSEMBLE_BACKGAIN
+ *	- #ADM_PARAM_ID_QENSEMBLE_SET_NEW_ANGLE
+ */
+#define ADM_MODULE_ID_QENSEMBLE    0x00010C59
+
+/* @addtogroup audio_pp_param_ids */
+/* ID of the QEnsemble enable parameter used by the
+ * #ADM_MODULE_ID_QENSEMBLE module.
+ * @messagepayload
+ * @structure{adm_qensemble_enable}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_QENSEMBLE_ENABLE.tex}
+ */
+#define ADM_PARAM_ID_QENSEMBLE_ENABLE   0x00010C60
+
+/* ID of the QEnsemble back gain parameter used by the
+ * #ADM_MODULE_ID_QENSEMBLE module.
+ * @messagepayload
+ * @structure{adm_qensemble_param_backgain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_QENSEMBLE_BACKGAIN.tex}
+ */
+#define ADM_PARAM_ID_QENSEMBLE_BACKGAIN   0x00010C61
+
+/* ID of the QEnsemble new angle parameter used by the
+ * #ADM_MODULE_ID_QENSEMBLE module.
+ * @messagepayload
+ * @structure{adm_qensemble_param_set_new_angle}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ADM_PARAM_ID_QENSEMBLE_SET_NEW_ANGLE.tex}
+ */
+#define ADM_PARAM_ID_QENSEMBLE_SET_NEW_ANGLE    0x00010C62
+
+/* Structure for enabling the configuration parameter for the
+ * QEnsemble module.
+ */
+
+
+/* @brief Payload of the #ADM_PARAM_ID_QENSEMBLE_ENABLE
+ * parameter used by the QEnsemble module.
+ */
+struct adm_qensemble_enable {
+	uint32_t                  enable_flag;
+/*< Specifies whether the QEnsemble module is disabled (0) or enabled
+ * (nonzero).
+ */
+} __packed;
+
+
+/* Structure for the background gain for the QEnsemble module. */
+
+
+/* @brief Payload of the #ADM_PARAM_ID_QENSEMBLE_BACKGAIN
+ * parameter used by
+ * the QEnsemble module.
+ */
+struct adm_qensemble_param_backgain {
+	int16_t                  back_gain;
+/*< Linear gain in Q15 format.
+ * Supported values: 0 to 32767
+ */
+
+	uint16_t                 reserved;
+	/*< Clients must set this field to zero.*/
+} __packed;
+/* Structure for setting a new angle for the QEnsemble module. */
+
+
+/* @brief Payload of the #ADM_PARAM_ID_QENSEMBLE_SET_NEW_ANGLE
+ * parameter used
+ * by the QEnsemble module.
+ */
+struct adm_qensemble_param_set_new_angle {
+	int16_t                    new_angle;
+/*< New angle in degrees.
+ * Supported values: 0 to 359
+ */
+
+	int16_t                    time_ms;
+/*< Transition time in milliseconds to set the new angle.
+ * Supported values: 0 to 32767
+ */
+} __packed;
+
+
+#define ADM_CMD_GET_PP_TOPO_MODULE_LIST				0x00010349
+#define ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST			0x00010350
+#define AUDPROC_PARAM_ID_ENABLE					0x00010904
+ /*
+  * Payload of the ADM_CMD_GET_PP_TOPO_MODULE_LIST command.
+  */
+struct adm_cmd_get_pp_topo_module_list_t {
+	struct apr_hdr hdr;
+	/* Lower 32 bits of the 64-bit parameter data payload address. */
+	uint32_t                  data_payload_addr_lsw;
+	/*
+	 * Upper 32 bits of the 64-bit parameter data payload address.
+	 *
+	 *
+	 * The size of the shared memory, if specified, must be large enough to
+	 * contain the entire parameter data payload, including the module ID,
+	 * parameter ID, parameter size, and parameter values.
+	 */
+	uint32_t                  data_payload_addr_msw;
+	/*
+	 *  Unique identifier for an address.
+	 *
+	 * This memory map handle is returned by the aDSP through the
+	 * #ADM_CMD_SHARED_MEM_MAP_REGIONS command.
+	 *
+	 * @values
+	 * - Non-NULL -- On acknowledgment, the parameter data payloads begin at
+	 * the address specified (out-of-band)
+	 * - NULL -- The acknowledgment's payload contains the parameter data
+	 * (in-band) @tablebulletend
+	 */
+	uint32_t                  mem_map_handle;
+	/*
+	 * Maximum data size of the list of modules. This
+	 * field is a multiple of 4 bytes.
+	 */
+	uint16_t                  param_max_size;
+	/* This field must be set to zero. */
+	uint16_t                  reserved;
+} __packed;
+
+/*
+ * Payload of the ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST message, which returns
+ * module ids in response to an ADM_CMD_GET_PP_TOPO_MODULE_LIST command.
+ * Immediately following this structure is the acknowledgment <b>module id
+ * data variable payload</b> containing the pre/postprocessing module id
+ * values. For an in-band scenario, the variable payload depends on the size
+ * of the parameter.
+ */
+struct adm_cmd_rsp_get_pp_topo_module_list_t {
+	/* Status message (error code). */
+	uint32_t                  status;
+} __packed;
+
+struct audproc_topology_module_id_info_t {
+	uint32_t	num_modules;
+} __packed;
+
+/* end_addtogroup audio_pp_module_ids */
+
+/* @ingroup audio_pp_module_ids
+ * ID of the Volume Control module pre/postprocessing block.
+ * This module supports the following parameter IDs:
+ * - #ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN
+ * - #ASM_PARAM_ID_MULTICHANNEL_GAIN
+ * - #ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG
+ * - #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS
+ * - #ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS
+ * - #ASM_PARAM_ID_MULTICHANNEL_GAIN
+ * - #ASM_PARAM_ID_MULTICHANNEL_MUTE
+ */
+#define ASM_MODULE_ID_VOL_CTRL   0x00010BFE
+#define ASM_MODULE_ID_VOL_CTRL2  0x00010910
+#define AUDPROC_MODULE_ID_VOL_CTRL ASM_MODULE_ID_VOL_CTRL
+
+/* @addtogroup audio_pp_param_ids */
+/* ID of the master gain parameter used by the #ASM_MODULE_ID_VOL_CTRL
+ * module.
+ * @messagepayload
+ * @structure{asm_volume_ctrl_master_gain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN.tex}
+ */
+#define ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN    0x00010BFF
+#define AUDPROC_PARAM_ID_VOL_CTRL_MASTER_GAIN ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN
+
+/* ID of the left/right channel gain parameter used by the
+ * #ASM_MODULE_ID_VOL_CTRL module.
+ * @messagepayload
+ * @structure{asm_volume_ctrl_lr_chan_gain}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_MULTICHANNEL_GAIN.tex}
+ */
+#define ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN     0x00010C00
+
+/* ID of the mute configuration parameter used by the
+ * #ASM_MODULE_ID_VOL_CTRL module.
+ * @messagepayload
+ * @structure{asm_volume_ctrl_mute_config}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG.tex}
+ */
+#define ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG   0x00010C01
+
+/* ID of the soft stepping volume parameters used by the
+ * #ASM_MODULE_ID_VOL_CTRL module.
+ * @messagepayload
+ * @structure{asm_soft_step_volume_params}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMET
+ * ERS.tex}
+ */
+#define ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS  0x00010C29
+#define AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS\
+			ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS
+
+/* ID of the soft pause parameters used by the #ASM_MODULE_ID_VOL_CTRL
+ * module.
+ */
+#define ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS   0x00010D6A
+
+/* ID of the multiple-channel volume control parameters used by the
+ * #ASM_MODULE_ID_VOL_CTRL module.
+ */
+#define ASM_PARAM_ID_MULTICHANNEL_GAIN  0x00010713
+
+/* ID of the multiple-channel mute configuration parameters used by the
+ * #ASM_MODULE_ID_VOL_CTRL module.
+ */
+
+#define ASM_PARAM_ID_MULTICHANNEL_MUTE  0x00010714
+
+/* Structure for the master gain parameter for a volume control
+ * module.
+ */
+
+
+/* @brief Payload of the #ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN
+ * parameter used by the Volume Control module.
+ */
+
+
+
+struct asm_volume_ctrl_master_gain {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint16_t                  master_gain;
+	/* Linear gain in Q13 format. */
+
+	uint16_t                  reserved;
+	/* Clients must set this field to zero. */
+} __packed;
+
+
+struct asm_volume_ctrl_lr_chan_gain {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+
+	uint16_t                  l_chan_gain;
+	/*< Linear gain in Q13 format for the left channel. */
+
+	uint16_t                  r_chan_gain;
+	/*< Linear gain in Q13 format for the right channel.*/
+} __packed;
+
+
+/* Structure for the mute configuration parameter for a
+ * volume control module.
+ */
+
+
+/* @brief Payload of the #ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG
+ * parameter used by the Volume Control module.
+ */
+
+
+struct asm_volume_ctrl_mute_config {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint32_t                  mute_flag;
+/*< Specifies whether mute is disabled (0) or enabled (nonzero).*/
+
+} __packed;
+
+/*
+ * Supported parameters for a soft stepping linear ramping curve.
+ */
+#define ASM_PARAM_SVC_RAMPINGCURVE_LINEAR  0
+
+/*
+ * Exponential ramping curve.
+ */
+#define ASM_PARAM_SVC_RAMPINGCURVE_EXP    1
+
+/*
+ * Logarithmic ramping curve.
+ */
+#define ASM_PARAM_SVC_RAMPINGCURVE_LOG    2
+
+/* Structure for holding soft stepping volume parameters. */
+
+
+/*  Payload of the #ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS
+ * parameters used by the Volume Control module.
+ */
+struct asm_soft_step_volume_params {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint32_t                  period;
+/*< Period in milliseconds.
+ * Supported values: 0 to 15000
+ */
+
+	uint32_t                  step;
+/*< Step in microseconds.
+ * Supported values: 0 to 15000000
+ */
+
+	uint32_t                  ramping_curve;
+/*< Ramping curve type.
+ * Supported values:
+ * - #ASM_PARAM_SVC_RAMPINGCURVE_LINEAR
+ * - #ASM_PARAM_SVC_RAMPINGCURVE_EXP
+ * - #ASM_PARAM_SVC_RAMPINGCURVE_LOG
+ */
+} __packed;
+
+
+/* Structure for holding soft pause parameters. */
+
+
+/* Payload of the #ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS
+ * parameters used by the Volume Control module.
+ */
+
+
+struct asm_soft_pause_params {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint32_t                  enable_flag;
+/*< Specifies whether soft pause is disabled (0) or enabled
+ * (nonzero).
+ */
+
+
+
+	uint32_t                  period;
+/*< Period in milliseconds.
+ * Supported values: 0 to 15000
+ */
+
+	uint32_t                  step;
+/*< Step in microseconds.
+ * Supported values: 0 to 15000000
+ */
+
+	uint32_t                  ramping_curve;
+/*< Ramping curve.
+ * Supported values:
+ * - #ASM_PARAM_SVC_RAMPINGCURVE_LINEAR
+ * - #ASM_PARAM_SVC_RAMPINGCURVE_EXP
+ * - #ASM_PARAM_SVC_RAMPINGCURVE_LOG
+ */
+} __packed;
+
+
+/* Maximum number of channels.*/
+#define VOLUME_CONTROL_MAX_CHANNELS                       8
+
+/* Structure for holding one channel type - gain pair. */
+
+
+/* Payload of the #ASM_PARAM_ID_MULTICHANNEL_GAIN channel
+ * type/gain pairs used by the Volume Control module. \n \n This
+ * structure immediately follows the
+ * asm_volume_ctrl_multichannel_gain structure.
+ */
+
+
+struct asm_volume_ctrl_channeltype_gain_pair {
+	uint8_t                   channeltype;
+	/*
+	 * Channel type for which the gain setting is to be applied.
+	 * Supported values:
+	 * - #PCM_CHANNEL_L
+	 * - #PCM_CHANNEL_R
+	 * - #PCM_CHANNEL_C
+	 * - #PCM_CHANNEL_LS
+	 * - #PCM_CHANNEL_RS
+	 * - #PCM_CHANNEL_LFE
+	 * - #PCM_CHANNEL_CS
+	 * - #PCM_CHANNEL_LB
+	 * - #PCM_CHANNEL_RB
+	 * - #PCM_CHANNELS
+	 * - #PCM_CHANNEL_CVH
+	 * - #PCM_CHANNEL_MS
+	 * - #PCM_CHANNEL_FLC
+	 * - #PCM_CHANNEL_FRC
+	 * - #PCM_CHANNEL_RLC
+	 * - #PCM_CHANNEL_RRC
+	 */
+
+	uint8_t                   reserved1;
+	/* Clients must set this field to zero. */
+
+	uint8_t                   reserved2;
+	/* Clients must set this field to zero. */
+
+	uint8_t                   reserved3;
+	/* Clients must set this field to zero. */
+
+	uint32_t                  gain;
+	/*
+	 * Gain value for this channel in Q28 format.
+	 * Supported values: Any
+	 */
+} __packed;
+
+
+/* Structure for the multichannel gain command */
+
+
+/* Payload of the #ASM_PARAM_ID_MULTICHANNEL_GAIN
+ * parameters used by the Volume Control module.
+ */
+
+
+struct asm_volume_ctrl_multichannel_gain {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint32_t                  num_channels;
+	/*
+	 * Number of channels for which gain values are provided. Any
+	 * channels present in the data for which gain is not provided are
+	 * set to unity gain.
+	 * Supported values: 1 to 8
+	 */
+
+	struct asm_volume_ctrl_channeltype_gain_pair
+		gain_data[VOLUME_CONTROL_MAX_CHANNELS];
+	/* Array of channel type/gain pairs.*/
+} __packed;
+
+
+/* Structure for holding one channel type - mute pair. */
+
+
+/* Payload of the #ASM_PARAM_ID_MULTICHANNEL_MUTE channel
+ * type/mute setting pairs used by the Volume Control module. \n \n
+ * This structure immediately follows the
+ * asm_volume_ctrl_multichannel_mute structure.
+ */
+
+
+struct asm_volume_ctrl_channelype_mute_pair {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint8_t                   channelype;
+/*< Channel type for which the mute setting is to be applied.
+ * Supported values:
+ * - #PCM_CHANNEL_L
+ * - #PCM_CHANNEL_R
+ * - #PCM_CHANNEL_C
+ * - #PCM_CHANNEL_LS
+ * - #PCM_CHANNEL_RS
+ * - #PCM_CHANNEL_LFE
+ * - #PCM_CHANNEL_CS
+ * - #PCM_CHANNEL_LB
+ * - #PCM_CHANNEL_RB
+ * - #PCM_CHANNELS
+ * - #PCM_CHANNEL_CVH
+ * - #PCM_CHANNEL_MS
+ * - #PCM_CHANNEL_FLC
+ * - #PCM_CHANNEL_FRC
+ * - #PCM_CHANNEL_RLC
+ * - #PCM_CHANNEL_RRC
+ */
+
+	uint8_t                   reserved1;
+	/*< Clients must set this field to zero. */
+
+	uint8_t                   reserved2;
+	/*< Clients must set this field to zero. */
+
+	uint8_t                   reserved3;
+	/*< Clients must set this field to zero. */
+
+	uint32_t                  mute;
+/*< Mute setting for this channel.
+ * Supported values:
+ * - 0 = Unmute
+ * - Nonzero = Mute
+ */
+} __packed;
+
+
+/* Structure for the multichannel mute command */
+
+
+/* @brief Payload of the #ASM_PARAM_ID_MULTICHANNEL_MUTE
+ * parameters used by the Volume Control module.
+ */
+
+
+struct asm_volume_ctrl_multichannel_mute {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	uint32_t                  num_channels;
+/*< Number of channels for which mute configuration is
+ * provided. Any channels present in the data for which mute
+ * configuration is not provided are set to unmute.
+ * Supported values: 1 to 8
+ */
+
+struct asm_volume_ctrl_channelype_mute_pair
+				mute_data[VOLUME_CONTROL_MAX_CHANNELS];
+	/*< Array of channel type/mute setting pairs.*/
+} __packed;
+/* end_addtogroup audio_pp_param_ids */
+
+/* audio_pp_module_ids
+ * ID of the IIR Tuning Filter module.
+ * This module supports the following parameter IDs:
+ * - #ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG
+ * - #ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN
+ * - #ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS
+ */
+#define ASM_MODULE_ID_IIRUNING_FILTER   0x00010C02
+
+/* @addtogroup audio_pp_param_ids */
+/* ID of the IIR tuning filter enable parameter used by the
+ * #ASM_MODULE_ID_IIRUNING_FILTER module.
+ * @messagepayload
+ * @structure{asm_iiruning_filter_enable}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CO
+ * NFIG.tex}
+ */
+#define ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG   0x00010C03
+
+/* ID of the IIR tuning filter pregain parameter used by the
+ * #ASM_MODULE_ID_IIRUNING_FILTER module.
+ */
+#define ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN  0x00010C04
+
+/* ID of the IIR tuning filter configuration parameters used by the
+ * #ASM_MODULE_ID_IIRUNING_FILTER module.
+ */
+#define ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS  0x00010C05
+
+/* Structure for an enable configuration parameter for an
+ * IIR tuning filter module.
+ */
+
+
+/* @brief Payload of the #ASM_PARAM_ID_IIRUNING_FILTER_ENABLE_CONFIG
+ * parameter used by the IIR Tuning Filter module.
+ */
+struct asm_iiruning_filter_enable {
+	uint32_t                  enable_flag;
+/*< Specifies whether the IIR tuning filter is disabled (0) or
+ * enabled (1).
+ */
+} __packed;
+
+/* Structure for the pregain parameter for an IIR tuning filter module. */
+
+
+/* Payload of the #ASM_PARAM_ID_IIRUNING_FILTER_PRE_GAIN
+ * parameters used by the IIR Tuning Filter module.
+ */
+struct asm_iiruning_filter_pregain {
+	uint16_t                  pregain;
+	/*< Linear gain in Q13 format. */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero.*/
+} __packed;
+
+/* Structure for the configuration parameter for an IIR tuning filter
+ * module.
+ */
+
+
+/* @brief Payload of the #ASM_PARAM_ID_IIRUNING_FILTER_CONFIG_PARAMS
+ * parameters used by the IIR Tuning Filter module. \n
+ * \n
+ * This structure is followed by the IIR filter coefficients: \n
+ * - Sequence of int32_t FilterCoeffs \n
+ * Five coefficients for each band. Each coefficient is in int32_t format, in
+ * the order of b0, b1, b2, a1, a2.
+ * - Sequence of int16_t NumShiftFactor \n
+ * One int16_t per band. The numerator shift factor is related to the Q
+ * factor of the filter coefficients.
+ * - Sequence of uint16_t PanSetting \n
+ * One uint16_t per band, indicating if the filter is applied to left (0),
+ * right (1), or both (2) channels.
+ */
+struct asm_iir_filter_config_params {
+	uint16_t                  num_biquad_stages;
+/*< Number of bands.
+ * Supported values: 0 to 20
+ */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero.*/
+} __packed;
+
+/* audio_pp_module_ids
+ * ID of the Multiband Dynamic Range Control (MBDRC) module on the Tx/Rx
+ * paths.
+ * This module supports the following parameter IDs:
+ * - #ASM_PARAM_ID_MBDRC_ENABLE
+ * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS
+ */
+#define ASM_MODULE_ID_MBDRC   0x00010C06
+
+/* audio_pp_param_ids */
+/* ID of the MBDRC enable parameter used by the #ASM_MODULE_ID_MBDRC module.
+ * @messagepayload
+ * @structure{asm_mbdrc_enable}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_MBDRC_ENABLE.tex}
+ */
+#define ASM_PARAM_ID_MBDRC_ENABLE   0x00010C07
+
+/* ID of the MBDRC configuration parameters used by the
+ * #ASM_MODULE_ID_MBDRC module.
+ * @messagepayload
+ * @structure{asm_mbdrc_config_params}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_MBDRC_CONFIG_PARAMS.tex}
+ *
+ * @parspace Sub-band DRC configuration parameters
+ * @structure{asm_subband_drc_config_params}
+ * @tablespace
+ * @inputtable{Audio_Postproc_ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_subband_DRC.tex}
+ *
+ * @keep{6}
+ * To obtain legacy ADRC from MBDRC, use the calibration tool to:
+ *
+ * - Enable MBDRC (EnableFlag = TRUE)
+ * - Set number of bands to 1 (uiNumBands = 1)
+ * - Enable the first MBDRC band (DrcMode[0] = DRC_ENABLED = 1)
+ * - Clear the first band mute flag (MuteFlag[0] = 0)
+ * - Set the first band makeup gain to unity (compMakeUpGain[0] = 0x2000)
+ * - Use the legacy ADRC parameters to calibrate the rest of the MBDRC
+ * parameters.
+ */
+#define ASM_PARAM_ID_MBDRC_CONFIG_PARAMS  0x00010C08
+
+/* end_addtogroup audio_pp_param_ids */
+
+/* audio_pp_module_ids
+ * ID of the MMBDRC module version 2 pre/postprocessing block.
+ * This module differs from the original MBDRC (#ASM_MODULE_ID_MBDRC) in
+ * the length of the filters used in each sub-band.
+ * This module supports the following parameter ID:
+ * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_IMPROVED_FILTBANK_V2
+ */
+#define ASM_MODULE_ID_MBDRCV2                                0x0001070B
+
+/* @addtogroup audio_pp_param_ids */
+/* ID of the configuration parameters used by the
+ * #ASM_MODULE_ID_MBDRCV2 module for the improved filter structure
+ * of the MBDRC v2 pre/postprocessing block.
+ * The update to this configuration structure from the original
+ * MBDRC is the number of filter coefficients in the filter
+ * structure. The sequence for is as follows:
+ * - 1 band = 0 FIR coefficient + 1 mute flag + uint16_t padding
+ * - 2 bands = 141 FIR coefficients + 2 mute flags + uint16_t padding
+ * - 3 bands = 141+81 FIR coefficients + 3 mute flags + uint16_t padding
+ * - 4 bands = 141+81+61 FIR coefficients + 4 mute flags + uint16_t
+ * padding
+ * - 5 bands = 141+81+61+61 FIR coefficients + 5 mute flags +
+ * uint16_t padding
+ *	This block uses the same parameter structure as
+ *	#ASM_PARAM_ID_MBDRC_CONFIG_PARAMS.
+ */
+#define ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_IMPROVED_FILTBANK_V2 \
+								0x0001070C
+
+#define ASM_MODULE_ID_MBDRCV3					0x0001090B
+/*
+ * ID of the MMBDRC module version 3 pre/postprocessing block.
+ * This module differs from MBDRCv2 (#ASM_MODULE_ID_MBDRCV2) in
+ * that it supports both 16- and 24-bit data.
+ * This module supports the following parameter ID:
+ * - #ASM_PARAM_ID_MBDRC_ENABLE
+ * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS
+ * - #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS_V3
+ * - #ASM_PARAM_ID_MBDRC_FILTER_XOVER_FREQS
+ */
+
+/* Structure for the enable parameter for an MBDRC module. */
+
+
+/* Payload of the #ASM_PARAM_ID_MBDRC_ENABLE parameter used by the
+ * MBDRC module.
+ */
+struct asm_mbdrc_enable {
+	uint32_t                  enable_flag;
+/*< Specifies whether MBDRC is disabled (0) or enabled (nonzero).*/
+} __packed;
+
+/* Structure for the configuration parameters for an MBDRC module. */
+
+
+/* Payload of the #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS
+ * parameters used by the MBDRC module. \n \n Following this
+ * structure is the payload for sub-band DRC configuration
+ * parameters (asm_subband_drc_config_params). This sub-band
+ * structure must be repeated for each band.
+ */
+
+
+struct asm_mbdrc_config_params {
+	uint16_t                  num_bands;
+/*< Number of bands.
+ * Supported values: 1 to 5
+ */
+
+	int16_t                   limiterhreshold;
+/*< Threshold in decibels for the limiter output.
+ * Supported values: -72 to 18 \n
+ * Recommended value: 3994 (-0.22 db in Q3.12 format)
+ */
+
+	int16_t                   limiter_makeup_gain;
+/*< Makeup gain in decibels for the limiter output.
+ * Supported values: -42 to 42 \n
+ * Recommended value: 256 (0 dB in Q7.8 format)
+ */
+
+	int16_t                   limiter_gc;
+/*< Limiter gain recovery coefficient.
+ * Supported values: 0.5 to 0.99 \n
+ * Recommended value: 32440 (0.99 in Q15 format)
+ */
+
+	int16_t                   limiter_delay;
+/*< Limiter delay in samples.
+ * Supported values: 0 to 10 \n
+ * Recommended value: 262 (0.008 samples in Q15 format)
+ */
+
+	int16_t                   limiter_max_wait;
+/*< Maximum limiter waiting time in samples.
+ * Supported values: 0 to 10 \n
+ * Recommended value: 262 (0.008 samples in Q15 format)
+ */
+} __packed;
+
+/* DRC configuration structure for each sub-band of an MBDRC module. */
+
+
+/* Payload of the #ASM_PARAM_ID_MBDRC_CONFIG_PARAMS DRC
+ * configuration parameters for each sub-band in the MBDRC module.
+ * After this DRC structure is configured for valid bands, the next
+ * MBDRC setparams expects the sequence of sub-band MBDRC filter
+ * coefficients (the length depends on the number of bands) plus the
+ * mute flag for that band plus uint16_t padding.
+ *
+ * @keep{10}
+ * The filter coefficient and mute flag are of type int16_t:
+ * - FIR coefficient = int16_t firFilter
+ * - Mute flag = int16_t fMuteFlag
+ *
+ * The sequence is as follows:
+ * - 1 band = 0 FIR coefficient + 1 mute flag + uint16_t padding
+ * - 2 bands = 97 FIR coefficients + 2 mute flags + uint16_t padding
+ * - 3 bands = 97+33 FIR coefficients + 3 mute flags + uint16_t padding
+ * - 4 bands = 97+33+33 FIR coefficients + 4 mute flags + uint16_t padding
+ * - 5 bands = 97+33+33+33 FIR coefficients + 5 mute flags + uint16_t padding
+ *
+ * For improved filterbank, the sequence is as follows:
+ * - 1 band = 0 FIR coefficient + 1 mute flag + uint16_t padding
+ * - 2 bands = 141 FIR coefficients + 2 mute flags + uint16_t padding
+ * - 3 bands = 141+81 FIR coefficients + 3 mute flags + uint16_t padding
+ * - 4 bands = 141+81+61 FIR coefficients + 4 mute flags + uint16_t padding
+ * - 5 bands = 141+81+61+61 FIR coefficients + 5 mute flags + uint16_t padding
+ */
+struct asm_subband_drc_config_params {
+	int16_t                   drc_stereo_linked_flag;
+/*< Specifies whether all stereo channels have the same applied
+ * dynamics (1) or if they process their dynamics independently (0).
+ * Supported values:
+ * - 0 -- Not linked
+ * - 1 -- Linked
+ */
+
+	int16_t                   drc_mode;
+/*< Specifies whether DRC mode is bypassed for sub-bands.
+ * Supported values:
+ * - 0 -- Disabled
+ * - 1 -- Enabled
+ */
+
+	int16_t                   drc_down_sample_level;
+/*< DRC down sample level.
+ * Supported values: @ge 1
+ */
+
+	int16_t                   drc_delay;
+/*< DRC delay in samples.
+ * Supported values: 0 to 1200
+ */
+
+	uint16_t                  drc_rmsime_avg_const;
+/*< RMS signal energy time-averaging constant.
+ * Supported values: 0 to 2^16-1
+ */
+
+	uint16_t                  drc_makeup_gain;
+/*< DRC makeup gain in decibels.
+ * Supported values: 258 to 64917
+ */
+	/* Down expander settings */
+	int16_t                   down_expdrhreshold;
+/*< Down expander threshold.
+ * Supported Q7 format values: 1320 to up_cmpsrhreshold
+ */
+
+	int16_t                   down_expdr_slope;
+/*< Down expander slope.
+ * Supported Q8 format values: -32768 to 0.
+ */
+
+	uint32_t                  down_expdr_attack;
+/*< Down expander attack constant.
+ * Supported Q31 format values: 196844 to 2^31.
+ */
+
+	uint32_t                  down_expdr_release;
+/*< Down expander release constant.
+ * Supported Q31 format values: 19685 to 2^31
+ */
+
+	uint16_t                  down_expdr_hysteresis;
+/*< Down expander hysteresis constant.
+ * Supported Q14 format values: 1 to 32690
+ */
+
+	uint16_t                  reserved;
+	/*< Clients must set this field to zero. */
+
+	int32_t                   down_expdr_min_gain_db;
+/*< Down expander minimum gain.
+ * Supported Q23 format values: -805306368 to 0.
+ */
+
+	/* Up compressor settings */
+
+	int16_t                   up_cmpsrhreshold;
+/*< Up compressor threshold.
+ * Supported Q7 format values: down_expdrhreshold to
+ * down_cmpsrhreshold.
+ */
+
+	uint16_t                  up_cmpsr_slope;
+/*< Up compressor slope.
+ * Supported Q16 format values: 0 to 64881.
+ */
+
+	uint32_t                  up_cmpsr_attack;
+/*< Up compressor attack constant.
+ * Supported Q31 format values: 196844 to 2^31.
+ */
+
+	uint32_t                  up_cmpsr_release;
+/*< Up compressor release constant.
+ * Supported Q31 format values: 19685 to 2^31.
+ */
+
+	uint16_t                  up_cmpsr_hysteresis;
+/*< Up compressor hysteresis constant.
+ * Supported Q14 format values: 1 to 32690.
+ */
+
+	/* Down compressor settings */
+
+	int16_t                   down_cmpsrhreshold;
+/*< Down compressor threshold.
+ * Supported Q7 format values: up_cmpsrhreshold to 11560.
+ */
+
+	uint16_t                  down_cmpsr_slope;
+/*< Down compressor slope.
+ * Supported Q16 format values: 0 to 64881.
+ */
+
+	uint16_t                  reserved1;
+/*< Clients must set this field to zero. */
+
+	uint32_t                  down_cmpsr_attack;
+/*< Down compressor attack constant.
+ * Supported Q31 format values: 196844 to 2^31.
+ */
+
+	uint32_t                  down_cmpsr_release;
+/*< Down compressor release constant.
+ * Supported Q31 format values: 19685 to 2^31.
+ */
+
+	uint16_t                  down_cmpsr_hysteresis;
+/*< Down compressor hysteresis constant.
+ * Supported Q14 values: 1 to 32690.
+ */
+
+	uint16_t                  reserved2;
+/*< Clients must set this field to zero.*/
+} __packed;
+
+#define ASM_MODULE_ID_EQUALIZER            0x00010C27
+#define ASM_PARAM_ID_EQUALIZER_PARAMETERS  0x00010C28
+
+#define ASM_MAX_EQ_BANDS 12
+
+struct asm_eq_per_band_params {
+	uint32_t                  band_idx;
+/*< Band index.
+ * Supported values: 0 to 11
+ */
+
+	uint32_t                  filterype;
+/*< Type of filter.
+ * Supported values:
+ * - #ASM_PARAM_EQYPE_NONE
+ * - #ASM_PARAM_EQ_BASS_BOOST
+ * - #ASM_PARAM_EQ_BASS_CUT
+ * - #ASM_PARAM_EQREBLE_BOOST
+ * - #ASM_PARAM_EQREBLE_CUT
+ * - #ASM_PARAM_EQ_BAND_BOOST
+ * - #ASM_PARAM_EQ_BAND_CUT
+ */
+
+	uint32_t                  center_freq_hz;
+	/*< Filter band center frequency in Hertz. */
+
+	int32_t                   filter_gain;
+/*< Filter band initial gain.
+ * Supported values: +12 to -12 dB in 1 dB increments
+ */
+
+	int32_t                   q_factor;
+/*< Filter band quality factor expressed as a Q8 number, i.e., a
+ * fixed-point number with q factor of 8. For example, 3000/(2^8).
+ */
+} __packed;
+
+struct asm_eq_params {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+		uint32_t                  enable_flag;
+/*< Specifies whether the equalizer module is disabled (0) or enabled
+ * (nonzero).
+ */
+
+		uint32_t                  num_bands;
+/*< Number of bands.
+ * Supported values: 1 to 12
+ */
+	struct asm_eq_per_band_params eq_bands[ASM_MAX_EQ_BANDS];
+
+} __packed;
+
+/*	No equalizer effect.*/
+#define ASM_PARAM_EQYPE_NONE      0
+
+/*	Bass boost equalizer effect.*/
+#define ASM_PARAM_EQ_BASS_BOOST     1
+
+/*Bass cut equalizer effect.*/
+#define ASM_PARAM_EQ_BASS_CUT       2
+
+/*	Treble boost equalizer effect */
+#define ASM_PARAM_EQREBLE_BOOST   3
+
+/*	Treble cut equalizer effect.*/
+#define ASM_PARAM_EQREBLE_CUT     4
+
+/*	Band boost equalizer effect.*/
+#define ASM_PARAM_EQ_BAND_BOOST     5
+
+/*	Band cut equalizer effect.*/
+#define ASM_PARAM_EQ_BAND_CUT       6
+
+/* Voice get & set params */
+#define VOICE_CMD_SET_PARAM				0x0001133D
+#define VOICE_CMD_GET_PARAM				0x0001133E
+#define VOICE_EVT_GET_PARAM_ACK				0x00011008
+
+
+/* ID of the Bass Boost module.
+ * This module supports the following parameter IDs:
+ *  - #AUDPROC_PARAM_ID_BASS_BOOST_ENABLE
+ *  - #AUDPROC_PARAM_ID_BASS_BOOST_MODE
+ *  - #AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH
+ */
+#define AUDPROC_MODULE_ID_BASS_BOOST                             0x000108A1
+/* ID of the Bass Boost enable parameter used by
+ * AUDPROC_MODULE_ID_BASS_BOOST.
+ */
+#define AUDPROC_PARAM_ID_BASS_BOOST_ENABLE                       0x000108A2
+/* ID of the Bass Boost mode parameter used by
+ * AUDPROC_MODULE_ID_BASS_BOOST.
+ */
+#define AUDPROC_PARAM_ID_BASS_BOOST_MODE                         0x000108A3
+/* ID of the Bass Boost strength parameter used by
+ * AUDPROC_MODULE_ID_BASS_BOOST.
+ */
+#define AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH                     0x000108A4
+
+/* ID of the PBE module.
+ * This module supports the following parameter IDs:
+ * - #AUDPROC_PARAM_ID_PBE_ENABLE
+ * - #AUDPROC_PARAM_ID_PBE_PARAM_CONFIG
+ */
+#define AUDPROC_MODULE_ID_PBE                                    0x00010C2A
+/* ID of the Bass Boost enable parameter used by
+ * AUDPROC_MODULE_ID_BASS_BOOST.
+ */
+#define AUDPROC_PARAM_ID_PBE_ENABLE                              0x00010C2B
+/* ID of the Bass Boost mode parameter used by
+ * AUDPROC_MODULE_ID_BASS_BOOST.
+ */
+#define AUDPROC_PARAM_ID_PBE_PARAM_CONFIG                        0x00010C49
+
+/* ID of the Virtualizer module. This module supports the
+ * following parameter IDs:
+ * - #AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE
+ * - #AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH
+ * - #AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE
+ * - #AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST
+ */
+#define AUDPROC_MODULE_ID_VIRTUALIZER                            0x000108A5
+/* ID of the Virtualizer enable parameter used by
+ * AUDPROC_MODULE_ID_VIRTUALIZER.
+ */
+#define AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE                      0x000108A6
+/* ID of the Virtualizer strength parameter used by
+ * AUDPROC_MODULE_ID_VIRTUALIZER.
+ */
+#define AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH                    0x000108A7
+/* ID of the Virtualizer out type parameter used by
+ * AUDPROC_MODULE_ID_VIRTUALIZER.
+ */
+#define AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE                    0x000108A8
+/* ID of the Virtualizer out type parameter used by
+ * AUDPROC_MODULE_ID_VIRTUALIZER.
+ */
+#define AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST                 0x000108A9
+
+/* ID of the Reverb module. This module supports the following
+ * parameter IDs:
+ * - #AUDPROC_PARAM_ID_REVERB_ENABLE
+ * - #AUDPROC_PARAM_ID_REVERB_MODE
+ * - #AUDPROC_PARAM_ID_REVERB_PRESET
+ * - #AUDPROC_PARAM_ID_REVERB_WET_MIX
+ * - #AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST
+ * - #AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL
+ * - #AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL
+ * - #AUDPROC_PARAM_ID_REVERB_DECAY_TIME
+ * - #AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO
+ * - #AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL
+ * - #AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY
+ * - #AUDPROC_PARAM_ID_REVERB_LEVEL
+ * - #AUDPROC_PARAM_ID_REVERB_DELAY
+ * - #AUDPROC_PARAM_ID_REVERB_DIFFUSION
+ * - #AUDPROC_PARAM_ID_REVERB_DENSITY
+ */
+#define AUDPROC_MODULE_ID_REVERB                          0x000108AA
+/* ID of the Reverb enable parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_ENABLE                    0x000108AB
+/* ID of the Reverb mode parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_MODE                      0x000108AC
+/* ID of the Reverb preset parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_PRESET                    0x000108AD
+/* ID of the Reverb wet mix parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_WET_MIX                   0x000108AE
+/* ID of the Reverb gain adjust parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST               0x000108AF
+/* ID of the Reverb room level parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL                0x000108B0
+/* ID of the Reverb room hf level parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL             0x000108B1
+/* ID of the Reverb decay time parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_DECAY_TIME                0x000108B2
+/* ID of the Reverb decay hf ratio parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO            0x000108B3
+/* ID of the Reverb reflections level parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL         0x000108B4
+/* ID of the Reverb reflections delay parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY         0x000108B5
+/* ID of the Reverb level parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_LEVEL                      0x000108B6
+/* ID of the Reverb delay parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_DELAY                      0x000108B7
+/* ID of the Reverb diffusion parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_DIFFUSION                  0x000108B8
+/* ID of the Reverb density parameter used by
+ * AUDPROC_MODULE_ID_REVERB.
+ */
+#define AUDPROC_PARAM_ID_REVERB_DENSITY                    0x000108B9
+
+/* ID of the Popless Equalizer module. This module supports the
+ * following parameter IDs:
+ * - #AUDPROC_PARAM_ID_EQ_ENABLE
+ * - #AUDPROC_PARAM_ID_EQ_CONFIG
+ * - #AUDPROC_PARAM_ID_EQ_NUM_BANDS
+ * - #AUDPROC_PARAM_ID_EQ_BAND_LEVELS
+ * - #AUDPROC_PARAM_ID_EQ_BAND_LEVEL_RANGE
+ * - #AUDPROC_PARAM_ID_EQ_BAND_FREQS
+ * - #AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ_RANGE
+ * - #AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ
+ * - #AUDPROC_PARAM_ID_EQ_BAND_INDEX
+ * - #AUDPROC_PARAM_ID_EQ_PRESET_ID
+ * - #AUDPROC_PARAM_ID_EQ_NUM_PRESETS
+ * - #AUDPROC_PARAM_ID_EQ_GET_PRESET_NAME
+ */
+#define AUDPROC_MODULE_ID_POPLESS_EQUALIZER                    0x000108BA
+/* ID of the Popless Equalizer enable parameter used by
+ * AUDPROC_MODULE_ID_POPLESS_EQUALIZER.
+ */
+#define AUDPROC_PARAM_ID_EQ_ENABLE                             0x000108BB
+/* ID of the Popless Equalizer config parameter used by
+ * AUDPROC_MODULE_ID_POPLESS_EQUALIZER.
+ */
+#define AUDPROC_PARAM_ID_EQ_CONFIG                             0x000108BC
+/* ID of the Popless Equalizer number of bands parameter used
+ * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is
+ * used for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_NUM_BANDS                          0x000108BD
+/* ID of the Popless Equalizer band levels parameter used by
+ * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is
+ * used for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_BAND_LEVELS                        0x000108BE
+/* ID of the Popless Equalizer band level range parameter used
+ * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is
+ * used for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_BAND_LEVEL_RANGE                   0x000108BF
+/* ID of the Popless Equalizer band frequencies parameter used
+ * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is
+ * used for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_BAND_FREQS                         0x000108C0
+/* ID of the Popless Equalizer single band frequency range
+ * parameter used by AUDPROC_MODULE_ID_POPLESS_EQUALIZER.
+ *  This param ID is used for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ_RANGE             0x000108C1
+/* ID of the Popless Equalizer single band frequency parameter
+ * used by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID
+ * is used for set param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ                   0x000108C2
+/* ID of the Popless Equalizer band index parameter used by
+ * AUDPROC_MODULE_ID_POPLESS_EQUALIZER.
+ */
+#define AUDPROC_PARAM_ID_EQ_BAND_INDEX                         0x000108C3
+/* ID of the Popless Equalizer preset id parameter used by
+ * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is used
+ * for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_PRESET_ID                          0x000108C4
+/* ID of the Popless Equalizer number of presets parameter used
+ * by AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is used
+ * for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_NUM_PRESETS                        0x000108C5
+/* ID of the Popless Equalizer preset name parameter used by
+ * AUDPROC_MODULE_ID_POPLESS_EQUALIZER. This param ID is used
+ * for get param only.
+ */
+#define AUDPROC_PARAM_ID_EQ_PRESET_NAME                        0x000108C6
+
+/* Set Q6 topologies */
+#define ASM_CMD_ADD_TOPOLOGIES				0x00010DBE
+#define ADM_CMD_ADD_TOPOLOGIES				0x00010335
+#define AFE_CMD_ADD_TOPOLOGIES				0x000100f8
+/* structure used for both ioctls */
+struct cmd_set_topologies {
+	struct apr_hdr hdr;
+	u32		payload_addr_lsw;
+	/* LSW of parameter data payload address.*/
+	u32		payload_addr_msw;
+	/* MSW of parameter data payload address.*/
+	u32		mem_map_handle;
+	/* Memory map handle returned by mem map command */
+	u32		payload_size;
+	/* Size in bytes of the variable payload in shared memory */
+} __packed;
+
+/* This module represents the Rx processing of Feedback speaker protection.
+ * It contains the excursion control, thermal protection,
+ * analog clip manager features in it.
+ * This module id will support following param ids.
+ * - AFE_PARAM_ID_FBSP_MODE_RX_CFG
+ */
+
+#define AFE_MODULE_FB_SPKR_PROT_RX 0x0001021C
+#define AFE_MODULE_FB_SPKR_PROT_V2_RX 0x0001025F
+
+#define AFE_PARAM_ID_FBSP_MODE_RX_CFG 0x0001021D
+#define AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG 0x00010260
+
+struct asm_fbsp_mode_rx_cfg {
+	uint32_t minor_version;
+	uint32_t mode;
+} __packed;
+
+/* This module represents the VI processing of feedback speaker protection.
+ * It will receive Vsens and Isens from codec and generates necessary
+ * parameters needed by Rx processing.
+ * This module id will support following param ids.
+ * - AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG
+ * - AFE_PARAM_ID_CALIB_RES_CFG
+ * - AFE_PARAM_ID_FEEDBACK_PATH_CFG
+ */
+
+#define AFE_MODULE_FB_SPKR_PROT_VI_PROC 0x00010226
+#define AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2 0x0001026A
+
+#define AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG 0x0001022A
+#define AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2  0x0001026B
+
+struct asm_spkr_calib_vi_proc_cfg {
+	uint32_t minor_version;
+	uint32_t operation_mode;
+	uint32_t r0_t0_selection_flag[SP_V2_NUM_MAX_SPKR];
+	int32_t r0_cali_q24[SP_V2_NUM_MAX_SPKR];
+	int16_t	t0_cali_q6[SP_V2_NUM_MAX_SPKR];
+	uint32_t quick_calib_flag;
+} __packed;
+
+#define AFE_PARAM_ID_CALIB_RES_CFG 0x0001022B
+#define AFE_PARAM_ID_CALIB_RES_CFG_V2 0x0001026E
+
+struct asm_calib_res_cfg {
+	uint32_t minor_version;
+	int32_t	r0_cali_q24[SP_V2_NUM_MAX_SPKR];
+	uint32_t th_vi_ca_state;
+} __packed;
+
+#define AFE_PARAM_ID_FEEDBACK_PATH_CFG 0x0001022C
+#define AFE_MODULE_FEEDBACK 0x00010257
+
+struct asm_feedback_path_cfg {
+	uint32_t minor_version;
+	int32_t	dst_portid;
+	int32_t	num_channels;
+	int32_t	chan_info[4];
+} __packed;
+
+#define AFE_PARAM_ID_MODE_VI_PROC_CFG 0x00010227
+
+struct asm_mode_vi_proc_cfg {
+	uint32_t minor_version;
+	uint32_t cal_mode;
+} __packed;
+
+#define AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI	0x0001026A
+#define AFE_PARAM_ID_SP_V2_TH_VI_MODE_CFG	0x0001026B
+#define AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG	0x0001029F
+#define AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS	0x000102A0
+
+struct afe_sp_th_vi_mode_cfg {
+	uint32_t minor_version;
+	uint32_t operation_mode;
+	/*
+	 * Operation mode of thermal VI module.
+	 *   0 -- Normal Running mode
+	 *   1 -- Calibration mode
+	 *   2 -- FTM mode
+	 */
+	uint32_t r0t0_selection_flag[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Specifies which set of R0, T0 values the algorithm will use.
+	 * This field is valid only in Normal mode (operation_mode = 0).
+	 * 0 -- Use calibrated R0, T0 value
+	 * 1 -- Use safe R0, T0 value
+	 */
+	int32_t r0_cali_q24[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Calibration point resistance per device. This field is valid
+	 * only in Normal mode (operation_mode = 0).
+	 * values 33554432 to 1073741824 Ohms (in Q24 format)
+	 */
+	int16_t t0_cali_q6[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Calibration point temperature per device. This field is valid
+	 * in both Normal mode and Calibration mode.
+	 * values -1920 to 5120 degrees C (in Q6 format)
+	 */
+	uint32_t quick_calib_flag;
+	/*
+	 * Indicates whether calibration is to be done in quick mode or not.
+	 * This field is valid only in Calibration mode (operation_mode = 1).
+	 * 0 -- Disabled
+	 * 1 -- Enabled
+	 */
+} __packed;
+
+struct afe_sp_th_vi_ftm_cfg {
+	uint32_t minor_version;
+	uint32_t wait_time_ms[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Wait time to heat up speaker before collecting statistics
+	 * for ftm mode in ms.
+	 * values 0 to 4294967295 ms
+	 */
+	uint32_t ftm_time_ms[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * duration for which FTM statistics are collected in ms.
+	 * values 0 to 2000 ms
+	 */
+} __packed;
+
+struct afe_sp_th_vi_ftm_params {
+	uint32_t minor_version;
+	int32_t dc_res_q24[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * DC resistance value in q24 format
+	 * values 0 to 2147483647 Ohms (in Q24 format)
+	 */
+	int32_t temp_q22[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * temperature value in q22 format
+	 * values -125829120 to 2147483647 degC (in Q22 format)
+	 */
+	uint32_t status[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * FTM packet status
+	 * 0 - Incorrect operation mode.This status is returned
+	 *     when GET_PARAM is called in non FTM Mode
+	 * 1 - Inactive mode -- Port is not yet started.
+	 * 2 - Wait state. wait_time_ms has not yet elapsed
+	 * 3 - In progress state. ftm_time_ms has not yet elapsed.
+	 * 4 - Success.
+	 * 5 - Failed.
+	 */
+} __packed;
+
+struct afe_sp_th_vi_get_param {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_get_param_v2 get_param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_sp_th_vi_ftm_params param;
+} __packed;
+
+struct afe_sp_th_vi_get_param_resp {
+	uint32_t status;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_sp_th_vi_ftm_params param;
+} __packed;
+
+
+#define AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI	0x0001026F
+#define AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG	0x000102A1
+#define AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG	0x000102A2
+#define AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS	0x000102A3
+
+struct afe_sp_ex_vi_mode_cfg {
+	uint32_t minor_version;
+	uint32_t operation_mode;
+	/*
+	 * Operation mode of Excursion VI module.
+	 * 0 - Normal Running mode
+	 * 2 - FTM mode
+	 */
+} __packed;
+
+struct afe_sp_ex_vi_ftm_cfg {
+	uint32_t minor_version;
+	uint32_t wait_time_ms[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Wait time to heat up speaker before collecting statistics
+	 * for ftm mode in ms.
+	 * values 0 to 4294967295 ms
+	 */
+	uint32_t ftm_time_ms[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * duration for which FTM statistics are collected in ms.
+	 * values 0 to 2000 ms
+	 */
+} __packed;
+
+struct afe_sp_ex_vi_ftm_params {
+	uint32_t minor_version;
+	int32_t freq_q20[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Resonance frequency in q20 format
+	 * values 0 to 2147483647 Hz (in Q20 format)
+	 */
+	int32_t resis_q24[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Mechanical resistance in q24 format
+	 * values 0 to 2147483647 Ohms (in Q24 format)
+	 */
+	int32_t qmct_q24[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * Mechanical Qfactor in q24 format
+	 * values 0 to 2147483647 (in Q24 format)
+	 */
+	uint32_t status[SP_V2_NUM_MAX_SPKR];
+	/*
+	 * FTM packet status
+	 * 0 - Incorrect operation mode.This status is returned
+	 *      when GET_PARAM is called in non FTM Mode.
+	 * 1 - Inactive mode -- Port is not yet started.
+	 * 2 - Wait state. wait_time_ms has not yet elapsed
+	 * 3 - In progress state. ftm_time_ms has not yet elapsed.
+	 * 4 - Success.
+	 * 5 - Failed.
+	 */
+} __packed;
+
+struct afe_sp_ex_vi_get_param {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_get_param_v2 get_param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_sp_ex_vi_ftm_params param;
+} __packed;
+
+struct afe_sp_ex_vi_get_param_resp {
+	uint32_t status;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_sp_ex_vi_ftm_params param;
+} __packed;
+
+union afe_spkr_prot_config {
+	struct asm_fbsp_mode_rx_cfg mode_rx_cfg;
+	struct asm_spkr_calib_vi_proc_cfg vi_proc_cfg;
+	struct asm_feedback_path_cfg feedback_path_cfg;
+	struct asm_mode_vi_proc_cfg mode_vi_proc_cfg;
+	struct afe_sp_th_vi_mode_cfg th_vi_mode_cfg;
+	struct afe_sp_th_vi_ftm_cfg th_vi_ftm_cfg;
+	struct afe_sp_ex_vi_mode_cfg ex_vi_mode_cfg;
+	struct afe_sp_ex_vi_ftm_cfg ex_vi_ftm_cfg;
+} __packed;
+
+struct afe_spkr_prot_config_command {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2 pdata;
+	union afe_spkr_prot_config prot_config;
+} __packed;
+
+struct afe_spkr_prot_get_vi_calib {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_get_param_v2 get_param;
+	struct afe_port_param_data_v2 pdata;
+	struct asm_calib_res_cfg res_cfg;
+} __packed;
+
+struct afe_spkr_prot_calib_get_resp {
+	uint32_t status;
+	struct afe_port_param_data_v2 pdata;
+	struct asm_calib_res_cfg res_cfg;
+} __packed;
+
+
+/* SRS TRUMEDIA start */
+/* topology */
+#define SRS_TRUMEDIA_TOPOLOGY_ID			0x00010D90
+/* module */
+#define SRS_TRUMEDIA_MODULE_ID				0x10005010
+/* parameters */
+#define SRS_TRUMEDIA_PARAMS				0x10005011
+#define SRS_TRUMEDIA_PARAMS_WOWHD			0x10005012
+#define SRS_TRUMEDIA_PARAMS_CSHP			0x10005013
+#define SRS_TRUMEDIA_PARAMS_HPF				0x10005014
+#define SRS_TRUMEDIA_PARAMS_AEQ				0x10005015
+#define SRS_TRUMEDIA_PARAMS_HL				0x10005016
+#define SRS_TRUMEDIA_PARAMS_GEQ				0x10005017
+
+#define SRS_ID_GLOBAL	0x00000001
+#define SRS_ID_WOWHD	0x00000002
+#define SRS_ID_CSHP	0x00000003
+#define SRS_ID_HPF	0x00000004
+#define SRS_ID_AEQ	0x00000005
+#define SRS_ID_HL		0x00000006
+#define SRS_ID_GEQ	0x00000007
+
+#define SRS_CMD_UPLOAD		0x7FFF0000
+#define SRS_PARAM_OFFSET_MASK	0x3FFF0000
+#define SRS_PARAM_VALUE_MASK	0x0000FFFF
+
+struct srs_trumedia_params_GLOBAL {
+	uint8_t                  v1;
+	uint8_t                  v2;
+	uint8_t                  v3;
+	uint8_t                  v4;
+	uint8_t                  v5;
+	uint8_t                  v6;
+	uint8_t                  v7;
+	uint8_t                  v8;
+	uint16_t                 v9;
+} __packed;
+
+struct srs_trumedia_params_WOWHD {
+	uint32_t				v1;
+	uint16_t				v2;
+	uint16_t				v3;
+	uint16_t				v4;
+	uint16_t				v5;
+	uint16_t				v6;
+	uint16_t				v7;
+	uint16_t				v8;
+	uint16_t				v____A1;
+	uint32_t				v9;
+	uint16_t				v10;
+	uint16_t				v11;
+	uint32_t				v12[16];
+	uint32_t	v13[16];
+	uint32_t	v14[16];
+	uint32_t	v15[16];
+	uint32_t	v16;
+	uint16_t	v17;
+	uint16_t	v18;
+} __packed;
+
+struct srs_trumedia_params_CSHP {
+	uint32_t		v1;
+	uint16_t		v2;
+	uint16_t		v3;
+	uint16_t		v4;
+	uint16_t		v5;
+	uint16_t		v6;
+	uint16_t		v____A1;
+	uint32_t		v7;
+	uint16_t		v8;
+	uint16_t		v9;
+	uint32_t		v10[16];
+} __packed;
+
+struct srs_trumedia_params_HPF {
+	uint32_t		v1;
+	uint32_t		v2[26];
+} __packed;
+
+struct srs_trumedia_params_AEQ {
+	uint32_t		v1;
+	uint16_t		v2;
+	uint16_t		v3;
+	uint16_t		v4;
+	uint16_t		v____A1;
+	uint32_t	v5[74];
+	uint32_t	v6[74];
+	uint16_t	v7[2048];
+} __packed;
+
+struct srs_trumedia_params_HL {
+	uint16_t		v1;
+	uint16_t		v2;
+	uint16_t		v3;
+	uint16_t		v____A1;
+	int32_t			v4;
+	uint32_t		v5;
+	uint16_t		v6;
+	uint16_t		v____A2;
+	uint32_t		v7;
+} __packed;
+
+struct srs_trumedia_params_GEQ {
+	int16_t		v1[10];
+} __packed;
+struct srs_trumedia_params {
+	struct srs_trumedia_params_GLOBAL	global;
+	struct srs_trumedia_params_WOWHD	wowhd;
+	struct srs_trumedia_params_CSHP		cshp;
+	struct srs_trumedia_params_HPF		hpf;
+	struct srs_trumedia_params_AEQ		aeq;
+	struct srs_trumedia_params_HL		hl;
+	struct srs_trumedia_params_GEQ		geq;
+} __packed;
+/* SRS TruMedia end */
+
+#define AUDPROC_PARAM_ID_ENABLE		0x00010904
+#define ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS 0x1000FFFF
+/* DTS Eagle */
+#define AUDPROC_MODULE_ID_DTS_HPX_PREMIX 0x0001077C
+#define AUDPROC_MODULE_ID_DTS_HPX_POSTMIX 0x0001077B
+#define ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX 0x00010DED
+#define ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS  0x10015000
+#define ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER  0x10015001
+struct asm_dts_eagle_param {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_set_pp_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+} __packed;
+
+struct asm_dts_eagle_param_get {
+	struct apr_hdr	hdr;
+	struct asm_stream_cmd_get_pp_params_v2 param;
+} __packed;
+
+/* LSM Specific */
+#define VW_FEAT_DIM					(39)
+
+#define APRV2_IDS_SERVICE_ID_ADSP_LSM_V			(0xD)
+#define APRV2_IDS_DOMAIN_ID_ADSP_V			(0x4)
+#define APRV2_IDS_DOMAIN_ID_APPS_V			(0x5)
+
+#define LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS		(0x00012A7F)
+#define LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS	(0x00012A80)
+#define LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS	(0x00012A81)
+#define LSM_SESSION_CMD_OPEN_TX				(0x00012A82)
+#define LSM_SESSION_CMD_CLOSE_TX			(0x00012A88)
+#define LSM_SESSION_CMD_SET_PARAMS			(0x00012A83)
+#define LSM_SESSION_CMD_SET_PARAMS_V2			(0x00012A8F)
+#define LSM_SESSION_CMD_REGISTER_SOUND_MODEL		(0x00012A84)
+#define LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL		(0x00012A85)
+#define LSM_SESSION_CMD_START				(0x00012A86)
+#define LSM_SESSION_CMD_STOP				(0x00012A87)
+#define LSM_SESSION_CMD_EOB				(0x00012A89)
+#define LSM_SESSION_CMD_READ				(0x00012A8A)
+#define LSM_SESSION_CMD_OPEN_TX_V2			(0x00012A8B)
+#define LSM_CMD_ADD_TOPOLOGIES				(0x00012A8C)
+
+#define LSM_SESSION_EVENT_DETECTION_STATUS		(0x00012B00)
+#define LSM_SESSION_EVENT_DETECTION_STATUS_V2		(0x00012B01)
+#define LSM_DATA_EVENT_READ_DONE			(0x00012B02)
+#define LSM_DATA_EVENT_STATUS				(0x00012B03)
+
+#define LSM_MODULE_ID_VOICE_WAKEUP			(0x00012C00)
+#define LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD		(0x00012C01)
+#define LSM_PARAM_ID_OPERATION_MODE			(0x00012C02)
+#define LSM_PARAM_ID_GAIN				(0x00012C03)
+#define LSM_PARAM_ID_CONNECT_TO_PORT			(0x00012C04)
+#define LSM_PARAM_ID_FEATURE_COMPENSATION_DATA		(0x00012C07)
+#define LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS		(0x00012C07)
+#define LSM_MODULE_ID_LAB				(0x00012C08)
+#define LSM_PARAM_ID_LAB_ENABLE				(0x00012C09)
+#define LSM_PARAM_ID_LAB_CONFIG				(0x00012C0A)
+#define LSM_MODULE_ID_FRAMEWORK				(0x00012C0E)
+
+/* HW MAD specific */
+#define AFE_MODULE_HW_MAD				(0x00010230)
+#define AFE_PARAM_ID_HW_MAD_CFG				(0x00010231)
+#define AFE_PARAM_ID_HW_MAD_CTRL			(0x00010232)
+#define AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG		(0x00010233)
+
+/* SW MAD specific */
+#define AFE_MODULE_SW_MAD				(0x0001022D)
+#define AFE_PARAM_ID_SW_MAD_CFG				(0x0001022E)
+#define AFE_PARAM_ID_SVM_MODEL				(0x0001022F)
+
+/* Commands/Params to pass the codec/slimbus data to DSP */
+#define AFE_SVC_CMD_SET_PARAM				(0x000100f3)
+#define AFE_MODULE_CDC_DEV_CFG				(0x00010234)
+#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG		(0x00010235)
+#define AFE_PARAM_ID_CDC_REG_CFG			(0x00010236)
+#define AFE_PARAM_ID_CDC_REG_CFG_INIT			(0x00010237)
+#define AFE_PARAM_ID_CDC_REG_PAGE_CFG                   (0x00010296)
+
+#define AFE_MAX_CDC_REGISTERS_TO_CONFIG			(20)
+
+/* AANC Port Config Specific */
+#define AFE_PARAM_ID_AANC_PORT_CONFIG			(0x00010215)
+#define AFE_API_VERSION_AANC_PORT_CONFIG		(0x1)
+#define AANC_TX_MIC_UNUSED				(0)
+#define AANC_TX_VOICE_MIC				(1)
+#define AANC_TX_ERROR_MIC				(2)
+#define AANC_TX_NOISE_MIC				(3)
+#define AFE_PORT_MAX_CHANNEL_CNT			(8)
+#define AFE_MODULE_AANC					(0x00010214)
+#define AFE_PARAM_ID_CDC_AANC_VERSION			(0x0001023A)
+#define AFE_API_VERSION_CDC_AANC_VERSION		(0x1)
+#define AANC_HW_BLOCK_VERSION_1				(1)
+#define AANC_HW_BLOCK_VERSION_2				(2)
+
+/*Clip bank selection*/
+#define AFE_API_VERSION_CLIP_BANK_SEL_CFG 0x1
+#define AFE_CLIP_MAX_BANKS		4
+#define AFE_PARAM_ID_CLIP_BANK_SEL_CFG 0x00010242
+
+struct afe_param_aanc_port_cfg {
+	/* Minor version used for tracking the version of the module's
+	 * source port configuration.
+	 */
+	uint32_t aanc_port_cfg_minor_version;
+
+	/* Sampling rate of the source Tx port. 8k - 192k*/
+	uint32_t tx_port_sample_rate;
+
+	/* Channel mapping for the Tx port signal carrying Noise (X),
+	 * Error (E), and Voice (V) signals.
+	 */
+	uint8_t tx_port_channel_map[AFE_PORT_MAX_CHANNEL_CNT];
+
+	/* Number of channels on the source Tx port. */
+	uint16_t tx_port_num_channels;
+
+	/* Port ID of the Rx path reference signal. */
+	uint16_t rx_path_ref_port_id;
+
+	/* Sampling rate of the reference port. 8k - 192k*/
+	uint32_t ref_port_sample_rate;
+} __packed;
+
+struct afe_param_id_cdc_aanc_version {
+	/* Minor version used for tracking the version of the module's
+	 * hw version
+	 */
+	uint32_t cdc_aanc_minor_version;
+
+	/* HW version. */
+	uint32_t aanc_hw_version;
+} __packed;
+
+struct afe_param_id_clip_bank_sel {
+	/* Minor version used for tracking the version of the module's
+	 * hw version
+	 */
+	uint32_t minor_version;
+
+	/* Number of banks to be read */
+	uint32_t num_banks;
+
+	uint32_t bank_map[AFE_CLIP_MAX_BANKS];
+} __packed;
+
+/* ERROR CODES */
+/* Success. The operation completed with no errors. */
+#define ADSP_EOK          0x00000000
+/* General failure. */
+#define ADSP_EFAILED      0x00000001
+/* Bad operation parameter. */
+#define ADSP_EBADPARAM    0x00000002
+/* Unsupported routine or operation. */
+#define ADSP_EUNSUPPORTED 0x00000003
+/* Unsupported version. */
+#define ADSP_EVERSION     0x00000004
+/* Unexpected problem encountered. */
+#define ADSP_EUNEXPECTED  0x00000005
+/* Unhandled problem occurred. */
+#define ADSP_EPANIC       0x00000006
+/* Unable to allocate resource. */
+#define ADSP_ENORESOURCE  0x00000007
+/* Invalid handle. */
+#define ADSP_EHANDLE      0x00000008
+/* Operation is already processed. */
+#define ADSP_EALREADY     0x00000009
+/* Operation is not ready to be processed. */
+#define ADSP_ENOTREADY    0x0000000A
+/* Operation is pending completion. */
+#define ADSP_EPENDING     0x0000000B
+/* Operation could not be accepted or processed. */
+#define ADSP_EBUSY        0x0000000C
+/* Operation aborted due to an error. */
+#define ADSP_EABORTED     0x0000000D
+/* Operation preempted by a higher priority. */
+#define ADSP_EPREEMPTED   0x0000000E
+/* Operation requests intervention to complete. */
+#define ADSP_ECONTINUE    0x0000000F
+/* Operation requests immediate intervention to complete. */
+#define ADSP_EIMMEDIATE   0x00000010
+/* Operation is not implemented. */
+#define ADSP_ENOTIMPL     0x00000011
+/* Operation needs more data or resources. */
+#define ADSP_ENEEDMORE    0x00000012
+/* Operation does not have memory. */
+#define ADSP_ENOMEMORY    0x00000014
+/* Item does not exist. */
+#define ADSP_ENOTEXIST    0x00000015
+/* Max count for adsp error code sent to HLOS*/
+#define ADSP_ERR_MAX      (ADSP_ENOTEXIST + 1)
+/* Operation is finished. */
+#define ADSP_ETERMINATED    0x00011174
+
+/*bharath, adsp_error_codes.h */
+
+/* LPASS clock for I2S Interface */
+
+/* Supported OSR clock values */
+#define Q6AFE_LPASS_OSR_CLK_12_P288_MHZ		0xBB8000
+#define Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ		0xAC4400
+#define Q6AFE_LPASS_OSR_CLK_9_P600_MHZ		0x927C00
+#define Q6AFE_LPASS_OSR_CLK_8_P192_MHZ		0x7D0000
+#define Q6AFE_LPASS_OSR_CLK_6_P144_MHZ		0x5DC000
+#define Q6AFE_LPASS_OSR_CLK_4_P096_MHZ		0x3E8000
+#define Q6AFE_LPASS_OSR_CLK_3_P072_MHZ		0x2EE000
+#define Q6AFE_LPASS_OSR_CLK_2_P048_MHZ		0x1F4000
+#define Q6AFE_LPASS_OSR_CLK_1_P536_MHZ		0x177000
+#define Q6AFE_LPASS_OSR_CLK_1_P024_MHZ		 0xFA000
+#define Q6AFE_LPASS_OSR_CLK_768_kHZ		 0xBB800
+#define Q6AFE_LPASS_OSR_CLK_512_kHZ		 0x7D000
+#define Q6AFE_LPASS_OSR_CLK_DISABLE		     0x0
+
+/* Supported Bit clock values */
+#define Q6AFE_LPASS_IBIT_CLK_12_P288_MHZ	0xBB8000
+#define Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ	0xAC4400
+#define Q6AFE_LPASS_IBIT_CLK_8_P192_MHZ		0x7D0000
+#define Q6AFE_LPASS_IBIT_CLK_6_P144_MHZ		0x5DC000
+#define Q6AFE_LPASS_IBIT_CLK_4_P096_MHZ		0x3E8000
+#define Q6AFE_LPASS_IBIT_CLK_3_P072_MHZ		0x2EE000
+#define Q6AFE_LPASS_IBIT_CLK_2_P8224_MHZ		0x2b1100
+#define Q6AFE_LPASS_IBIT_CLK_2_P048_MHZ		0x1F4000
+#define Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ		0x177000
+#define Q6AFE_LPASS_IBIT_CLK_1_P4112_MHZ		0x158880
+#define Q6AFE_LPASS_IBIT_CLK_1_P024_MHZ		 0xFA000
+#define Q6AFE_LPASS_IBIT_CLK_768_KHZ		 0xBB800
+#define Q6AFE_LPASS_IBIT_CLK_512_KHZ		 0x7D000
+#define Q6AFE_LPASS_IBIT_CLK_256_KHZ		 0x3E800
+#define Q6AFE_LPASS_IBIT_CLK_DISABLE		     0x0
+
+/* Supported LPASS CLK sources */
+#define Q6AFE_LPASS_CLK_SRC_EXTERNAL 0
+#define Q6AFE_LPASS_CLK_SRC_INTERNAL 1
+
+/* Supported LPASS CLK root*/
+#define Q6AFE_LPASS_CLK_ROOT_DEFAULT 0
+
+enum afe_lpass_clk_mode {
+	Q6AFE_LPASS_MODE_BOTH_INVALID,
+	Q6AFE_LPASS_MODE_CLK1_VALID,
+	Q6AFE_LPASS_MODE_CLK2_VALID,
+	Q6AFE_LPASS_MODE_BOTH_VALID,
+} __packed;
+
+/* Clock ID Enumeration Define. */
+/* Clock ID for Primary I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT                          0x100
+/* Clock ID for Primary I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_MI2S_EBIT                          0x101
+/* Clock ID for Secondary I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT                          0x102
+/* Clock ID for Secondary I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_MI2S_EBIT                          0x103
+/* Clock ID for Tertiary I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT                          0x104
+/* Clock ID for Tertiary I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_MI2S_EBIT                          0x105
+/* Clock ID for Quartnery I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT                         0x106
+/* Clock ID for Quartnery I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_MI2S_EBIT                         0x107
+/* Clock ID for Speaker I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_IBIT                       0x108
+/* Clock ID for Speaker I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_EBIT                       0x109
+/* Clock ID for Speaker I2S OSR */
+#define Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR                        0x10A
+
+/* Clock ID for QUINARY  I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_IBIT			0x10B
+/* Clock ID for QUINARY  I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUI_MI2S_EBIT			0x10C
+/* Clock ID for SENARY  I2S IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_IBIT			0x10D
+/* Clock ID for SENARY  I2S EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEN_MI2S_EBIT			0x10E
+/* Clock ID for INT0 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT                       0x10F
+/* Clock ID for INT1 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT                       0x110
+/* Clock ID for INT2 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT                       0x111
+/* Clock ID for INT3 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT                       0x112
+/* Clock ID for INT4 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT                       0x113
+/* Clock ID for INT5 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT                       0x114
+/* Clock ID for INT6 I2S IBIT  */
+#define Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT                       0x115
+
+/* Clock ID for Primary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT                           0x200
+/* Clock ID for Primary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT                           0x201
+/* Clock ID for Secondary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT                           0x202
+/* Clock ID for Secondary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT                           0x203
+/* Clock ID for Tertiary PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT                           0x204
+/* Clock ID for Tertiary PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT                           0x205
+/* Clock ID for Quartery PCM IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT                          0x206
+/* Clock ID for Quartery PCM EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT                          0x207
+
+/** Clock ID for Primary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT                           0x200
+/** Clock ID for Primary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT                           0x201
+/** Clock ID for Secondary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT                           0x202
+/** Clock ID for Secondary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT                           0x203
+/** Clock ID for Tertiary TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT                           0x204
+/** Clock ID for Tertiary TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT                           0x205
+/** Clock ID for Quartery TDM IBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT                          0x206
+/** Clock ID for Quartery TDM EBIT */
+#define Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT                          0x207
+
+/* Clock ID for MCLK1 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_1                                 0x300
+/* Clock ID for MCLK2 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_2                                 0x301
+/* Clock ID for MCLK3 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_3                                 0x302
+/* Clock ID for MCLK4 */
+#define Q6AFE_LPASS_CLK_ID_MCLK_4                                 0x304
+/* Clock ID for Internal Digital Codec Core */
+#define Q6AFE_LPASS_CLK_ID_INTERNAL_DIGITAL_CODEC_CORE            0x303
+/* Clock ID for INT MCLK0 */
+#define Q6AFE_LPASS_CLK_ID_INT_MCLK_0                             0x305
+/* Clock ID for INT MCLK1 */
+#define Q6AFE_LPASS_CLK_ID_INT_MCLK_1                             0x306
+/*
+ * Clock ID for soundwire NPL.
+ * This is the clock to be used to enable NPL clock for  internal Soundwire.
+ */
+#define AFE_CLOCK_SET_CLOCK_ID_SWR_NPL_CLK                         0x307
+
+/* Clock ID for AHB HDMI input */
+#define Q6AFE_LPASS_CLK_ID_AHB_HDMI_INPUT                         0x400
+
+/* Clock ID for SPDIF core */
+#define Q6AFE_LPASS_CLK_ID_SPDIF_CORE                             0x500
+
+
+/* Clock attribute for invalid use (reserved for internal usage) */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVALID		0x0
+/* Clock attribute for no couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO		0x1
+/* Clock attribute for dividend couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND	0x2
+/* Clock attribute for divisor couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR	0x3
+/* Clock attribute for invert and no couple case */
+#define Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO	0x4
+/* Clock set API version */
+#define Q6AFE_LPASS_CLK_CONFIG_API_VERSION		0x1
+
+struct afe_clk_set {
+	/*
+	 * Minor version used for tracking clock set.
+	 *	@values #AFE_API_VERSION_CLOCK_SET
+	 */
+	uint32_t clk_set_minor_version;
+
+	/*
+	 * Clock ID
+	 *	@values
+	 *	- 0x100 to 0x10A - MSM8996
+	 *	- 0x200 to 0x207 - MSM8996
+	 *	- 0x300 to 0x302 - MSM8996 @tablebulletend
+	 */
+	uint32_t clk_id;
+
+	/*
+	 * Clock frequency  (in Hertz) to be set.
+	 *	@values
+	 *	- >= 0 for clock frequency to set @tablebulletend
+	 */
+	uint32_t clk_freq_in_hz;
+
+	/* Use to specific divider for two clocks if needed.
+	 *	Set to Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO for no divider
+	 *	relation clocks
+	 *	@values
+	 *	- #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO
+	 *	- #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVIDEND
+	 *	- #Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_DIVISOR @tablebulletend
+	 */
+	uint16_t clk_attri;
+
+	/*
+	 * Specifies the root clock source.
+	 *	Currently, only Q6AFE_LPASS_CLK_ROOT_DEFAULT is valid
+	 *	@values
+	 *	- 0 @tablebulletend
+	 */
+	uint16_t clk_root;
+
+	/*
+	 * for enable and disable clock.
+	 *	"clk_freq_in_hz", "clk_attri", and "clk_root"
+	 *	are ignored in disable clock case.
+	 *	@values 
+	 *	- 0 -- Disabled
+	 *	- 1 -- Enabled  @tablebulletend
+	 */
+	uint32_t enable;
+};
+
+struct afe_clk_cfg {
+/* Minor version used for tracking the version of the I2S
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_I2S_CONFIG
+ */
+	u32                  i2s_cfg_minor_version;
+
+/* clk value 1 in MHz. */
+	u32                  clk_val1;
+
+/* clk value 2 in MHz. */
+	u32                  clk_val2;
+
+/* clk_src
+ * #Q6AFE_LPASS_CLK_SRC_EXTERNAL
+ * #Q6AFE_LPASS_CLK_SRC_INTERNAL
+ */
+
+	u16                  clk_src;
+
+/* clk_root -0 for default */
+	u16                  clk_root;
+
+/* clk_set_mode
+ * #Q6AFE_LPASS_MODE_BOTH_INVALID
+ * #Q6AFE_LPASS_MODE_CLK1_VALID
+ * #Q6AFE_LPASS_MODE_CLK2_VALID
+ * #Q6AFE_LPASS_MODE_BOTH_VALID
+ */
+	u16                  clk_set_mode;
+
+/* This param id is used to configure I2S clk */
+	u16                  reserved;
+} __packed;
+
+/* This param id is used to configure I2S clk */
+#define AFE_PARAM_ID_LPAIF_CLK_CONFIG	0x00010238
+#define AFE_MODULE_CLOCK_SET		0x0001028F
+#define AFE_PARAM_ID_CLOCK_SET		0x00010290
+
+struct afe_lpass_clk_config_command {
+	struct apr_hdr			 hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	struct afe_clk_cfg clk_cfg;
+} __packed;
+
+enum afe_lpass_digital_clk_src {
+	Q6AFE_LPASS_DIGITAL_ROOT_INVALID,
+	Q6AFE_LPASS_DIGITAL_ROOT_PRI_MI2S_OSR,
+	Q6AFE_LPASS_DIGITAL_ROOT_SEC_MI2S_OSR,
+	Q6AFE_LPASS_DIGITAL_ROOT_TER_MI2S_OSR,
+	Q6AFE_LPASS_DIGITAL_ROOT_QUAD_MI2S_OSR,
+	Q6AFE_LPASS_DIGITAL_ROOT_CDC_ROOT_CLK,
+} __packed;
+
+/* This param id is used to configure internal clk */
+#define AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG	0x00010239
+
+struct afe_digital_clk_cfg {
+/* Minor version used for tracking the version of the I2S
+ * configuration interface.
+ * Supported values: #AFE_API_VERSION_I2S_CONFIG
+ */
+	u32                  i2s_cfg_minor_version;
+
+/* clk value in MHz. */
+	u32                  clk_val;
+
+/*	INVALID
+ *	PRI_MI2S_OSR
+ *	SEC_MI2S_OSR
+ *	TER_MI2S_OSR
+ *	QUAD_MI2S_OSR
+ *	DIGT_CDC_ROOT
+ */
+	u16                  clk_root;
+
+/* This field must be set to zero. */
+	u16                  reserved;
+} __packed;
+
+
+struct afe_lpass_digital_clk_config_command {
+	struct apr_hdr			 hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2    pdata;
+	struct afe_digital_clk_cfg clk_cfg;
+} __packed;
+
+/*
+ * Opcode for AFE to start DTMF.
+ */
+#define AFE_PORTS_CMD_DTMF_CTL	0x00010102
+
+/** DTMF payload.*/
+struct afe_dtmf_generation_command {
+	struct apr_hdr hdr;
+
+	/*
+	 * Duration of the DTMF tone in ms.
+	 * -1      -> continuous,
+	 *  0      -> disable
+	 */
+	int64_t                   duration_in_ms;
+
+	/*
+	 * The DTMF high tone frequency.
+	 */
+	uint16_t                  high_freq;
+
+	/*
+	 * The DTMF low tone frequency.
+	 */
+	uint16_t                  low_freq;
+
+	/*
+	 * The DTMF volume setting
+	 */
+	uint16_t                  gain;
+
+	/*
+	 * The number of ports to enable/disable on.
+	 */
+	uint16_t                  num_ports;
+
+	/*
+	 * The Destination ports - array  .
+	 * For DTMF on multiple ports, portIds needs to
+	 * be populated numPorts times.
+	 */
+	uint16_t                  port_ids;
+
+	/*
+	 * variable for 32 bit alignment of APR packet.
+	 */
+	uint16_t                  reserved;
+} __packed;
+
+enum afe_config_type {
+	AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+	AFE_SLIMBUS_SLAVE_CONFIG,
+	AFE_CDC_REGISTERS_CONFIG,
+	AFE_AANC_VERSION,
+	AFE_CDC_CLIP_REGISTERS_CONFIG,
+	AFE_CLIP_BANK_SEL,
+	AFE_CDC_REGISTER_PAGE_CONFIG,
+	AFE_MAX_CONFIG_TYPES,
+};
+
+struct afe_param_slimbus_slave_port_cfg {
+	uint32_t minor_version;
+	uint16_t slimbus_dev_id;
+	uint16_t slave_dev_pgd_la;
+	uint16_t slave_dev_intfdev_la;
+	uint16_t bit_width;
+	uint16_t data_format;
+	uint16_t num_channels;
+	uint16_t slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+} __packed;
+
+struct afe_param_cdc_slimbus_slave_cfg {
+	uint32_t minor_version;
+	uint32_t device_enum_addr_lsw;
+	uint32_t device_enum_addr_msw;
+	uint16_t tx_slave_port_offset;
+	uint16_t rx_slave_port_offset;
+} __packed;
+
+struct afe_param_cdc_reg_cfg {
+	uint32_t minor_version;
+	uint32_t reg_logical_addr;
+	uint32_t reg_field_type;
+	uint32_t reg_field_bit_mask;
+	uint16_t reg_bit_width;
+	uint16_t reg_offset_scale;
+} __packed;
+
+#define AFE_API_VERSION_CDC_REG_PAGE_CFG   1
+
+enum {
+	AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_0 = 0,
+	AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1,
+	AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_2,
+	AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_3,
+};
+
+struct afe_param_cdc_reg_page_cfg {
+	uint32_t minor_version;
+	uint32_t enable;
+	uint32_t proc_id;
+} __packed;
+
+struct afe_param_cdc_reg_cfg_data {
+	uint32_t num_registers;
+	struct afe_param_cdc_reg_cfg *reg_data;
+} __packed;
+
+struct afe_svc_cmd_set_param {
+	uint32_t payload_size;
+	uint32_t payload_address_lsw;
+	uint32_t payload_address_msw;
+	uint32_t mem_map_handle;
+} __packed;
+
+struct afe_svc_param_data {
+	uint32_t module_id;
+	uint32_t param_id;
+	uint16_t param_size;
+	uint16_t reserved;
+} __packed;
+
+struct afe_param_hw_mad_ctrl {
+	uint32_t minor_version;
+	uint16_t mad_type;
+	uint16_t mad_enable;
+} __packed;
+
+struct afe_cmd_hw_mad_ctrl {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_param_hw_mad_ctrl payload;
+} __packed;
+
+struct afe_cmd_hw_mad_slimbus_slave_port_cfg {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_param_slimbus_slave_port_cfg sb_port_cfg;
+} __packed;
+
+struct afe_cmd_sw_mad_enable {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2 pdata;
+} __packed;
+
+struct afe_param_cdc_reg_cfg_payload {
+	struct afe_svc_param_data     common;
+	struct afe_param_cdc_reg_cfg  reg_cfg;
+} __packed;
+
+struct afe_lpass_clk_config_command_v2 {
+	struct apr_hdr			hdr;
+	struct afe_svc_cmd_set_param	param;
+	struct afe_svc_param_data	pdata;
+	struct afe_clk_set		clk_cfg;
+} __packed;
+
+/*
+ * reg_data's size can be up to AFE_MAX_CDC_REGISTERS_TO_CONFIG
+ */
+struct afe_svc_cmd_cdc_reg_cfg {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_param_cdc_reg_cfg_payload reg_data[0];
+} __packed;
+
+struct afe_svc_cmd_init_cdc_reg_cfg {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_port_param_data_v2 init;
+} __packed;
+
+struct afe_svc_cmd_sb_slave_cfg {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_param_cdc_slimbus_slave_cfg sb_slave_cfg;
+} __packed;
+
+struct afe_svc_cmd_cdc_reg_page_cfg {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_param_cdc_reg_page_cfg cdc_reg_page_cfg;
+} __packed;
+
+struct afe_svc_cmd_cdc_aanc_version {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_param_id_cdc_aanc_version version;
+} __packed;
+
+struct afe_port_cmd_set_aanc_param {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+	struct afe_port_param_data_v2 pdata;
+	union {
+		struct afe_param_aanc_port_cfg aanc_port_cfg;
+		struct afe_mod_enable_param    mod_enable;
+	} __packed data;
+} __packed;
+
+struct afe_port_cmd_set_aanc_acdb_table {
+	struct apr_hdr hdr;
+	struct afe_port_cmd_set_param_v2 param;
+} __packed;
+
+/* Dolby DAP topology */
+#define DOLBY_ADM_COPP_TOPOLOGY_ID	0x0001033B
+#define DS2_ADM_COPP_TOPOLOGY_ID	0x1301033B
+
+/* RMS value from DSP */
+#define RMS_MODULEID_APPI_PASSTHRU  0x10009011
+#define RMS_PARAM_FIRST_SAMPLE 0x10009012
+#define RMS_PAYLOAD_LEN 4
+
+/* Customized mixing in matix mixer */
+#define MTMX_MODULE_ID_DEFAULT_CHMIXER  0x00010341
+#define DEFAULT_CHMIXER_PARAM_ID_COEFF  0x00010342
+#define CUSTOM_STEREO_PAYLOAD_SIZE	9
+#define CUSTOM_STEREO_CMD_PARAM_SIZE	24
+#define CUSTOM_STEREO_NUM_OUT_CH	0x0002
+#define CUSTOM_STEREO_NUM_IN_CH		0x0002
+#define CUSTOM_STEREO_INDEX_PARAM	0x0002
+#define Q14_GAIN_ZERO_POINT_FIVE	0x2000
+#define Q14_GAIN_UNITY			0x4000
+
+struct afe_svc_cmd_set_clip_bank_selection {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_port_param_data_v2 pdata;
+	struct afe_param_id_clip_bank_sel bank_sel;
+} __packed;
+
+/* Ultrasound supported formats */
+#define US_POINT_EPOS_FORMAT_V2 0x0001272D
+#define US_RAW_FORMAT_V2        0x0001272C
+#define US_PROX_FORMAT_V4       0x0001273B
+#define US_RAW_SYNC_FORMAT      0x0001272F
+#define US_GES_SYNC_FORMAT      0x00012730
+
+#define AFE_MODULE_GROUP_DEVICE	0x00010254
+#define AFE_PARAM_ID_GROUP_DEVICE_CFG	0x00010255
+#define AFE_PARAM_ID_GROUP_DEVICE_ENABLE 0x00010256
+#define AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX	0x1102
+
+/*  Payload of the #AFE_PARAM_ID_GROUP_DEVICE_CFG
+ * parameter, which configures max of 8 AFE ports
+ * into a group.
+ * The fixed size of this structure is sixteen bytes.
+ */
+struct afe_group_device_group_cfg {
+	u32 minor_version;
+	u16 group_id;
+	u16 num_channels;
+	u16 port_id[8];
+} __packed;
+
+#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX \
+	(AFE_PORT_ID_PRIMARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX \
+	(AFE_PORT_ID_PRIMARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX \
+	(AFE_PORT_ID_SECONDARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX \
+	(AFE_PORT_ID_SECONDARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX \
+	(AFE_PORT_ID_TERTIARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX \
+	(AFE_PORT_ID_TERTIARY_TDM_TX + 0x100)
+#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX \
+	(AFE_PORT_ID_QUATERNARY_TDM_RX + 0x100)
+#define AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX \
+	(AFE_PORT_ID_QUATERNARY_TDM_TX + 0x100)
+
+/* ID of the parameter used by #AFE_MODULE_GROUP_DEVICE to configure the
+ * group device. #AFE_SVC_CMD_SET_PARAM can use this parameter ID.
+ *
+ * Requirements:
+ * - Configure the group before the member ports in the group are
+ * configured and started.
+ * - Enable the group only after it is configured.
+ * - Stop all member ports in the group before disabling the group.
+ */
+#define AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG	0x0001029E
+
+/* Version information used to handle future additions to
+ * AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG processing (for backward compatibility).
+ */
+#define AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG	0x1
+
+/* Number of AFE ports in group device  */
+#define AFE_GROUP_DEVICE_NUM_PORTS					8
+
+/* Payload of the AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG parameter ID
+ * used by AFE_MODULE_GROUP_DEVICE.
+ */
+struct afe_param_id_group_device_tdm_cfg {
+	u32	group_device_cfg_minor_version;
+	/* Minor version used to track group device configuration.
+	 * @values #AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG
+	 */
+
+	u16	group_id;
+	/* ID for the group device.
+	 * @values
+	 * - #AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX
+	 * - #AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX
+	 * - #AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX
+	 * - #AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX
+	 * - #AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX
+	 * - #AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX
+	 * - #AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX
+	 * - #AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX
+	 */
+
+	u16	reserved;
+	/* 0 */
+
+	u16	port_id[AFE_GROUP_DEVICE_NUM_PORTS];
+	/* Array of member port IDs of this group.
+	 * @values
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_1
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_2
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_3
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_4
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_5
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_6
+	 * - #AFE_PORT_ID_PRIMARY_TDM_RX_7
+
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_1
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_2
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_3
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_4
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_5
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_6
+	 * - #AFE_PORT_ID_PRIMARY_TDM_TX_7
+
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_1
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_2
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_3
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_4
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_5
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_6
+	 * - #AFE_PORT_ID_SECONDARY_TDM_RX_7
+
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_1
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_2
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_3
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_4
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_5
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_6
+	 * - #AFE_PORT_ID_SECONDARY_TDM_TX_7
+
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_1
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_2
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_3
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_4
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_5
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_6
+	 * - #AFE_PORT_ID_TERTIARY_TDM_RX_7
+
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_1
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_2
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_3
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_4
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_5
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_6
+	 * - #AFE_PORT_ID_TERTIARY_TDM_TX_7
+
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_1
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_2
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_3
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_4
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_5
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_6
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_RX_7
+
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_1
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_2
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_3
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_4
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_5
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_6
+	 * - #AFE_PORT_ID_QUATERNARY_TDM_TX_7
+	 * @tablebulletend
+	 */
+
+	u32	num_channels;
+	/* Number of enabled slots for TDM frame.
+	 * @values 1 to 8
+	 */
+
+	u32	sample_rate;
+	/* Sampling rate of the port.
+	 * @values
+	 * - #AFE_PORT_SAMPLE_RATE_8K
+	 * - #AFE_PORT_SAMPLE_RATE_16K
+	 * - #AFE_PORT_SAMPLE_RATE_24K
+	 * - #AFE_PORT_SAMPLE_RATE_32K
+	 * - #AFE_PORT_SAMPLE_RATE_48K @tablebulletend
+	 */
+
+	u32	bit_width;
+	/* Bit width of the sample.
+	 * @values 16, 24, (32)
+	 */
+
+	u16	nslots_per_frame;
+	/* Number of slots per frame. Typical : 1, 2, 4, 8, 16, 32.
+	 * @values 1 - 32
+	 */
+
+	u16	slot_width;
+	/* Slot width of the slot in a TDM frame.  (slot_width >= bit_width)
+	 * have to be satisfied.
+	 * @values 16, 24, 32
+	 */
+
+	u32	slot_mask;
+	/* Position of active slots.  When that bit is set, that paricular
+	 * slot is active.
+	 * Number of active slots can be inferred by number of bits set in
+	 * the mask.  Only 8 individual bits can be enabled.
+	 * Bits 0..31 corresponding to slot 0..31
+	 * @values 1 to 2^32 -1
+	 */
+} __packed;
+
+/*  Payload of the #AFE_PARAM_ID_GROUP_DEVICE_ENABLE
+ * parameter, which enables or
+ * disables any module.
+ * The fixed size of this structure is four bytes.
+ */
+
+struct afe_group_device_enable {
+	u16 group_id;
+	/* valid value is AFE_GROUP_DEVICE_ID_SECONDARY_MI2S_RX */
+	u16 enable;
+	/* Enables (1) or disables (0) the module. */
+} __packed;
+
+union afe_port_group_config {
+	struct afe_group_device_group_cfg group_cfg;
+	struct afe_group_device_enable group_enable;
+	struct afe_param_id_group_device_tdm_cfg tdm_cfg;
+} __packed;
+
+struct afe_port_group_create {
+	struct apr_hdr hdr;
+	struct afe_svc_cmd_set_param param;
+	struct afe_port_param_data_v2 pdata;
+	union afe_port_group_config data;
+} __packed;
+
+/* Command for Matrix or Stream Router */
+#define ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2    0x00010DCE
+/* Module for AVSYNC */
+#define ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC    0x00010DC6
+
+/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to specify the
+ * render window start value. This parameter is supported only for a Set
+ * command (not a Get command) in the Rx direction
+ * (#ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2).
+ * Render window start is a value (session time minus timestamp, or ST-TS)
+ * below which frames are held, and after which frames are immediately
+ * rendered.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2 0x00010DD1
+
+/* Parameter used by #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC to specify the
+ * render window end value. This parameter is supported only for a Set
+ * command (not a Get command) in the Rx direction
+ * (#ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2). Render window end is a value
+ * (session time minus timestamp) above which frames are dropped, and below
+ * which frames are immediately rendered.
+ */
+#define ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2   0x00010DD2
+
+/* Generic payload of the window parameters in the
+ * #ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC module.
+ * This payload is supported only for a Set command
+ * (not a Get command) on the Rx path.
+ */
+struct asm_session_mtmx_strtr_param_window_v2_t {
+	u32    window_lsw;
+	/* Lower 32 bits of the render window start value. */
+
+	u32    window_msw;
+	/* Upper 32 bits of the render window start value.
+	 *
+	 * The 64-bit number formed by window_lsw and window_msw specifies a
+	 * signed 64-bit window value in microseconds. The sign extension is
+	 * necessary. This value is used by the following parameter IDs:
+	 * #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2
+	 * #ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2
+	 * #ASM_SESSION_MTMX_STRTR_PARAM_STAT_WINDOW_START_V2
+	 * #ASM_SESSION_MTMX_STRTR_PARAM_STAT_WINDOW_END_V2
+	 * The value depends on which parameter ID is used.
+	 * The aDSP honors the windows at a granularity of 1 ms.
+	 */
+};
+
+struct asm_session_cmd_set_mtmx_strstr_params_v2 {
+	uint32_t                  data_payload_addr_lsw;
+	/* Lower 32 bits of the 64-bit data payload address. */
+
+	uint32_t                  data_payload_addr_msw;
+	/* Upper 32 bits of the 64-bit data payload address.
+	 * If the address is not sent (NULL), the message is in the payload.
+	 * If the address is sent (non-NULL), the parameter data payloads
+	 * begin at the specified address.
+	 */
+
+	uint32_t                  mem_map_handle;
+	/* Unique identifier for an address. This memory map handle is returned
+	 * by the aDSP through the #ASM_CMD_SHARED_MEM_MAP_REGIONS command.
+	 * values
+	 * - NULL -- Parameter data payloads are within the message payload
+	 * (in-band).
+	 * - Non-NULL -- Parameter data payloads begin at the address specified
+	 * in the data_payload_addr_lsw and data_payload_addr_msw fields
+	 * (out-of-band).
+	 */
+
+	uint32_t                  data_payload_size;
+	/* Actual size of the variable payload accompanying the message, or in
+	 * shared memory. This field is used for parsing the parameter payload.
+	 * values > 0 bytes
+	 */
+
+	uint32_t                  direction;
+	/* Direction of the entity (matrix mixer or stream router) on which
+	 * the parameter is to be set.
+	 * values
+	 * - 0 -- Rx (for Rx stream router or Rx matrix mixer)
+	 * - 1 -- Tx (for Tx stream router or Tx matrix mixer)
+	 */
+};
+
+struct asm_mtmx_strtr_params {
+	struct apr_hdr  hdr;
+	struct asm_session_cmd_set_mtmx_strstr_params_v2 param;
+	struct asm_stream_param_data_v2 data;
+	u32 window_lsw;
+	u32 window_msw;
+} __packed;
+
+#define ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2 0x00010DCF
+#define ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2 0x00010DD0
+
+#define ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3 0x00012F0B
+#define ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK (0x80000000UL)
+
+struct asm_session_cmd_get_mtmx_strstr_params_v2 {
+	uint32_t                  data_payload_addr_lsw;
+	/* Lower 32 bits of the 64-bit data payload address. */
+
+	uint32_t                  data_payload_addr_msw;
+	/*
+	 * Upper 32 bits of the 64-bit data payload address.
+	 * If the address is not sent (NULL), the message is in the payload.
+	 * If the address is sent (non-NULL), the parameter data payloads
+	 * begin at the specified address.
+	 */
+
+	uint32_t                  mem_map_handle;
+	/*
+	 * Unique identifier for an address. This memory map handle is returned
+	 * by the aDSP through the #ASM_CMD_SHARED_MEM_MAP_REGIONS command.
+	 * values
+	 * - NULL -- Parameter data payloads are within the message payload
+	 * (in-band).
+	 * - Non-NULL -- Parameter data payloads begin at the address specified
+	 * in the data_payload_addr_lsw and data_payload_addr_msw fields
+	 * (out-of-band).
+	 */
+	uint32_t                  direction;
+	/*
+	 * Direction of the entity (matrix mixer or stream router) on which
+	 * the parameter is to be set.
+	 * values
+	 * - 0 -- Rx (for Rx stream router or Rx matrix mixer)
+	 * - 1 -- Tx (for Tx stream router or Tx matrix mixer)
+	 */
+	uint32_t                  module_id;
+	/* Unique module ID. */
+
+	uint32_t                  param_id;
+	/* Unique parameter ID. */
+
+	uint32_t                  param_max_size;
+};
+
+struct asm_session_mtmx_strtr_param_session_time_v3_t {
+	uint32_t                  session_time_lsw;
+	/* Lower 32 bits of the current session time in microseconds */
+
+	uint32_t                  session_time_msw;
+	/*
+	 * Upper 32 bits of the current session time in microseconds.
+	 * The 64-bit number formed by session_time_lsw and session_time_msw
+	 * is treated as signed.
+	 */
+
+	uint32_t                  absolute_time_lsw;
+	/*
+	 * Lower 32 bits of the 64-bit absolute time in microseconds.
+	 * This is the time when the sample corresponding to the
+	 * session_time_lsw is rendered to the hardware. This absolute
+	 * time can be slightly in the future or past.
+	 */
+
+	uint32_t                  absolute_time_msw;
+	/*
+	 * Upper 32 bits of the 64-bit absolute time in microseconds.
+	 * This is the time when the sample corresponding to the
+	 * session_time_msw is rendered to hardware. This absolute
+	 * time can be slightly in the future or past. The 64-bit number
+	 * formed by absolute_time_lsw and absolute_time_msw is treated as
+	 * unsigned.
+	 */
+
+	uint32_t                  time_stamp_lsw;
+	/* Lower 32 bits of the last processed timestamp in microseconds */
+
+	uint32_t                  time_stamp_msw;
+	/*
+	 * Upper 32 bits of the last processed timestamp in microseconds.
+	 * The 64-bit number formed by time_stamp_lsw and time_stamp_lsw
+	 * is treated as unsigned.
+	 */
+
+	uint32_t                  flags;
+	/*
+	 * Keeps track of any additional flags needed.
+	 * @values{for bit 31}
+	 * - 0 -- Uninitialized/invalid
+	 * - 1 -- Valid
+	 * All other bits are reserved; clients must set them to zero.
+	 */
+};
+
+union asm_session_mtmx_strtr_data_type {
+	struct asm_session_mtmx_strtr_param_session_time_v3_t session_time;
+};
+
+struct asm_mtmx_strtr_get_params {
+	struct apr_hdr hdr;
+	struct asm_session_cmd_get_mtmx_strstr_params_v2 param_info;
+} __packed;
+
+struct asm_mtmx_strtr_get_params_cmdrsp {
+	uint32_t err_code;
+	struct asm_stream_param_data_v2 param_info;
+	union asm_session_mtmx_strtr_data_type param_data;
+} __packed;
+
+#define AUDPROC_MODULE_ID_RESAMPLER 0x00010719
+
+enum {
+	LEGACY_PCM = 0,
+	COMPRESSED_PASSTHROUGH,
+	COMPRESSED_PASSTHROUGH_CONVERT,
+	COMPRESSED_PASSTHROUGH_DSD,
+};
+
+#define AUDPROC_MODULE_ID_COMPRESSED_MUTE                0x00010770
+#define AUDPROC_PARAM_ID_COMPRESSED_MUTE                 0x00010771
+
+struct adm_set_compressed_device_mute {
+	struct adm_cmd_set_pp_params_v5 command;
+	struct adm_param_data_v5 params;
+	u32    mute_on;
+} __packed;
+
+#define AUDPROC_MODULE_ID_COMPRESSED_LATENCY             0x0001076E
+#define AUDPROC_PARAM_ID_COMPRESSED_LATENCY              0x0001076F
+
+struct adm_set_compressed_device_latency {
+	struct adm_cmd_set_pp_params_v5 command;
+	struct adm_param_data_v5 params;
+	u32    latency;
+} __packed;
+
+#define VOICEPROC_MODULE_ID_GENERIC_TX                      0x00010EF6
+#define VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS               0x00010E37
+#define VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING           0x00010E38
+#define MAX_SECTORS                                         8
+#define MAX_NOISE_SOURCE_INDICATORS                         3
+#define MAX_POLAR_ACTIVITY_INDICATORS                       360
+
+struct sound_focus_param {
+	uint16_t start_angle[MAX_SECTORS];
+	uint8_t enable[MAX_SECTORS];
+	uint16_t gain_step;
+} __packed;
+
+struct source_tracking_param {
+	uint8_t vad[MAX_SECTORS];
+	uint16_t doa_speech;
+	uint16_t doa_noise[MAX_NOISE_SOURCE_INDICATORS];
+	uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS];
+} __packed;
+
+struct adm_param_fluence_soundfocus_t {
+	uint16_t start_angles[MAX_SECTORS];
+	uint8_t enables[MAX_SECTORS];
+	uint16_t gain_step;
+	uint16_t reserved;
+} __packed;
+
+struct adm_set_fluence_soundfocus_param {
+	struct adm_cmd_set_pp_params_v5 params;
+	struct adm_param_data_v5 data;
+	struct adm_param_fluence_soundfocus_t soundfocus_data;
+} __packed;
+
+struct adm_param_fluence_sourcetracking_t {
+	uint8_t vad[MAX_SECTORS];
+	uint16_t doa_speech;
+	uint16_t doa_noise[MAX_NOISE_SOURCE_INDICATORS];
+	uint8_t polar_activity[MAX_POLAR_ACTIVITY_INDICATORS];
+} __packed;
+
+#define AUDPROC_MODULE_ID_AUDIOSPHERE               0x00010916
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE         0x00010917
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH       0x00010918
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_CONFIG_MODE    0x00010919
+
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_COEFFS_STEREO_INPUT         0x0001091A
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_COEFFS_MULTICHANNEL_INPUT   0x0001091B
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_DESIGN_STEREO_INPUT         0x0001091C
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_DESIGN_MULTICHANNEL_INPUT   0x0001091D
+
+#define AUDPROC_PARAM_ID_AUDIOSPHERE_OPERATING_INPUT_MEDIA_INFO  0x0001091E
+#endif /*_APR_AUDIO_V2_H_ */
diff --git a/include/sound/apr_audio.h b/include/sound/apr_audio.h
new file mode 100644
index 0000000..eb35645
--- /dev/null
+++ b/include/sound/apr_audio.h
@@ -0,0 +1,1931 @@
+/*
+ *
+ * Copyright (c) 2010-2013, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _APR_AUDIO_H_
+#define _APR_AUDIO_H_
+
+/* ASM opcodes without APR payloads*/
+#include <linux/qdsp6v2/apr.h>
+
+/*
+ * Audio Front End (AFE)
+ */
+
+/* Port ID. Update afe_get_port_index when a new port is added here. */
+#define PRIMARY_I2S_RX 0		/* index = 0 */
+#define PRIMARY_I2S_TX 1		/* index = 1 */
+#define PCM_RX 2			/* index = 2 */
+#define PCM_TX 3			/* index = 3 */
+#define SECONDARY_I2S_RX 4		/* index = 4 */
+#define SECONDARY_I2S_TX 5		/* index = 5 */
+#define MI2S_RX 6			/* index = 6 */
+#define MI2S_TX 7			/* index = 7 */
+#define HDMI_RX 8			/* index = 8 */
+#define RSVD_2 9			/* index = 9 */
+#define RSVD_3 10			/* index = 10 */
+#define DIGI_MIC_TX 11			/* index = 11 */
+#define VOICE_RECORD_RX 0x8003		/* index = 12 */
+#define VOICE_RECORD_TX 0x8004		/* index = 13 */
+#define VOICE_PLAYBACK_TX 0x8005	/* index = 14 */
+
+/* Slimbus Multi channel port id pool  */
+#define SLIMBUS_0_RX		0x4000		/* index = 15 */
+#define SLIMBUS_0_TX		0x4001		/* index = 16 */
+#define SLIMBUS_1_RX		0x4002		/* index = 17 */
+#define SLIMBUS_1_TX		0x4003		/* index = 18 */
+#define SLIMBUS_2_RX		0x4004
+#define SLIMBUS_2_TX		0x4005
+#define SLIMBUS_3_RX		0x4006
+#define SLIMBUS_3_TX		0x4007
+#define SLIMBUS_4_RX		0x4008
+#define SLIMBUS_4_TX		0x4009		/* index = 24 */
+
+#define INT_BT_SCO_RX 0x3000		/* index = 25 */
+#define INT_BT_SCO_TX 0x3001		/* index = 26 */
+#define INT_BT_A2DP_RX 0x3002		/* index = 27 */
+#define INT_FM_RX 0x3004		/* index = 28 */
+#define INT_FM_TX 0x3005		/* index = 29 */
+#define RT_PROXY_PORT_001_RX	0x2000    /* index = 30 */
+#define RT_PROXY_PORT_001_TX	0x2001    /* index = 31 */
+#define SECONDARY_PCM_RX 12			/* index = 32 */
+#define SECONDARY_PCM_TX 13			/* index = 33 */
+#define PSEUDOPORT_01           0x8001    /* index =34 */
+
+#define AFE_PORT_INVALID 0xFFFF
+#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID
+
+#define AFE_PORT_CMD_START 0x000100ca
+
+#define AFE_EVENT_RTPORT_START 0
+#define AFE_EVENT_RTPORT_STOP 1
+#define AFE_EVENT_RTPORT_LOW_WM 2
+#define AFE_EVENT_RTPORT_HI_WM 3
+
+struct afe_port_start_command {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 gain;		/* Q13 */
+	u32 sample_rate;	/* 8 , 16, 48khz */
+} __packed;
+
+#define AFE_PORT_CMD_STOP 0x000100cb
+struct afe_port_stop_command {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 reserved;
+} __packed;
+
+#define AFE_PORT_CMD_APPLY_GAIN 0x000100cc
+struct afe_port_gain_command {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16	gain;/* Q13 */
+} __packed;
+
+#define AFE_PORT_CMD_SIDETONE_CTL 0x000100cd
+struct afe_port_sidetone_command {
+	struct apr_hdr hdr;
+	u16 rx_port_id;		/* Primary i2s tx = 1 */
+				/* PCM tx = 3 */
+				/* Secondary i2s tx = 5 */
+				/* Mi2s tx = 7 */
+				/* Digital mic tx = 11 */
+	u16 tx_port_id;		/* Primary i2s rx = 0 */
+				/* PCM rx = 2 */
+				/* Secondary i2s rx = 4 */
+				/* Mi2S rx = 6 */
+				/* HDMI rx = 8 */
+	u16 gain;		/* Q13 */
+	u16 enable;		/* 1 = enable, 0 = disable */
+} __packed;
+
+#define AFE_PORT_CMD_LOOPBACK 0x000100ce
+struct afe_loopback_command {
+	struct apr_hdr hdr;
+	u16 tx_port_id;		/* Primary i2s rx = 0 */
+				/* PCM rx = 2 */
+				/* Secondary i2s rx = 4 */
+				/* Mi2S rx = 6 */
+				/* HDMI rx = 8 */
+	u16 rx_port_id;		/* Primary i2s tx = 1 */
+				/* PCM tx = 3 */
+				/* Secondary i2s tx = 5 */
+				/* Mi2s tx = 7 */
+				/* Digital mic tx = 11 */
+	u16 mode;		/* Default -1, DSP will conver
+				 * the tx to rx format
+				 */
+	u16 enable;		/* 1 = enable, 0 = disable */
+} __packed;
+
+#define AFE_PSEUDOPORT_CMD_START 0x000100cf
+struct afe_pseudoport_start_command {
+	struct apr_hdr hdr;
+	u16 port_id;		/* Pseudo Port 1 = 0x8000 */
+				/* Pseudo Port 2 = 0x8001 */
+				/* Pseudo Port 3 = 0x8002 */
+	u16 timing;		/* FTRT = 0 , AVTimer = 1, */
+} __packed;
+
+#define AFE_PSEUDOPORT_CMD_STOP 0x000100d0
+struct afe_pseudoport_stop_command {
+	struct apr_hdr hdr;
+	u16 port_id;		/* Pseudo Port 1 = 0x8000 */
+				/* Pseudo Port 2 = 0x8001 */
+				/* Pseudo Port 3 = 0x8002 */
+	u16 reserved;
+} __packed;
+
+#define AFE_CMD_GET_ACTIVE_PORTS 0x000100d1
+
+
+#define AFE_CMD_GET_ACTIVE_HANDLES_FOR_PORT 0x000100d2
+struct afe_get_active_handles_command {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 reserved;
+} __packed;
+
+/*
+ * Opcode for AFE to start DTMF.
+ */
+#define AFE_PORTS_CMD_DTMF_CTL	0x00010102
+
+/** DTMF payload.*/
+struct afe_dtmf_generation_command {
+	struct apr_hdr hdr;
+
+	/*
+	 * Duration of the DTMF tone in ms.
+	 * -1      -> continuous,
+	 *  0      -> disable
+	 */
+	int64_t                   duration_in_ms;
+
+	/*
+	 * The DTMF high tone frequency.
+	 */
+	uint16_t                  high_freq;
+
+	/*
+	 * The DTMF low tone frequency.
+	 */
+	uint16_t                  low_freq;
+
+	/*
+	 * The DTMF volume setting
+	 */
+	uint16_t                  gain;
+
+	/*
+	 * The number of ports to enable/disable on.
+	 */
+	uint16_t                  num_ports;
+
+	/*
+	 * The Destination ports - array  .
+	 * For DTMF on multiple ports, portIds needs to
+	 * be populated numPorts times.
+	 */
+	uint16_t                  port_ids;
+
+	/*
+	 * variable for 32 bit alignment of APR packet.
+	 */
+	uint16_t                  reserved;
+} __packed;
+
+#define AFE_PCM_CFG_MODE_PCM			0x0
+#define AFE_PCM_CFG_MODE_AUX			0x1
+#define AFE_PCM_CFG_SYNC_EXT			0x0
+#define AFE_PCM_CFG_SYNC_INT			0x1
+#define AFE_PCM_CFG_FRM_8BPF			0x0
+#define AFE_PCM_CFG_FRM_16BPF			0x1
+#define AFE_PCM_CFG_FRM_32BPF			0x2
+#define AFE_PCM_CFG_FRM_64BPF			0x3
+#define AFE_PCM_CFG_FRM_128BPF			0x4
+#define AFE_PCM_CFG_FRM_256BPF			0x5
+#define AFE_PCM_CFG_QUANT_ALAW_NOPAD		0x0
+#define AFE_PCM_CFG_QUANT_MULAW_NOPAD		0x1
+#define AFE_PCM_CFG_QUANT_LINEAR_NOPAD		0x2
+#define AFE_PCM_CFG_QUANT_ALAW_PAD		0x3
+#define AFE_PCM_CFG_QUANT_MULAW_PAD		0x4
+#define AFE_PCM_CFG_QUANT_LINEAR_PAD		0x5
+#define AFE_PCM_CFG_CDATAOE_MASTER		0x0
+#define AFE_PCM_CFG_CDATAOE_SHARE		0x1
+
+struct afe_port_pcm_cfg {
+	u16	mode;	/* PCM (short sync) = 0, AUXPCM (long sync) = 1 */
+	u16	sync;	/* external = 0 , internal = 1 */
+	u16	frame;	/* 8 bpf = 0 */
+			/* 16 bpf = 1 */
+			/* 32 bpf = 2 */
+			/* 64 bpf = 3 */
+			/* 128 bpf = 4 */
+			/* 256 bpf = 5 */
+	u16     quant;
+	u16	slot;	/* Slot for PCM stream , 0 - 31 */
+	u16	data;	/* 0, PCM block is the only master */
+			/* 1, PCM block is shares to driver data out signal */
+			/*    other master                                  */
+	u16	reserved;
+} __packed;
+
+enum {
+	AFE_I2S_SD0 = 1,
+	AFE_I2S_SD1,
+	AFE_I2S_SD2,
+	AFE_I2S_SD3,
+	AFE_I2S_QUAD01,
+	AFE_I2S_QUAD23,
+	AFE_I2S_6CHS,
+	AFE_I2S_8CHS,
+};
+
+#define AFE_MI2S_MONO 0
+#define AFE_MI2S_STEREO 3
+#define AFE_MI2S_4CHANNELS 4
+#define AFE_MI2S_6CHANNELS 6
+#define AFE_MI2S_8CHANNELS 8
+
+struct afe_port_mi2s_cfg {
+	u16	bitwidth;	/* 16,24,32 */
+	u16	line;		/* Called ChannelMode in documentation */
+				/* i2s_sd0 = 1 */
+				/* i2s_sd1 = 2 */
+				/* i2s_sd2 = 3 */
+				/* i2s_sd3 = 4 */
+				/* i2s_quad01 = 5 */
+				/* i2s_quad23 = 6 */
+				/* i2s_6chs = 7 */
+				/* i2s_8chs = 8 */
+	u16	channel;	/* Called MonoStereo in documentation */
+				/* i2s mono = 0 */
+				/* i2s mono right = 1 */
+				/* i2s mono left = 2 */
+				/* i2s stereo = 3 */
+	u16	ws;		/* 0, word select signal from external source */
+				/* 1, word select signal from internal source */
+	u16	format;	/* don't touch this field if it is not for */
+				/* AFE_PORT_CMD_I2S_CONFIG opcode */
+} __packed;
+
+struct afe_port_hdmi_cfg {
+	u16	bitwidth;	/* 16,24,32 */
+	u16	channel_mode;	/* HDMI Stereo = 0 */
+				/* HDMI_3Point1 (4-ch) = 1 */
+				/* HDMI_5Point1 (6-ch) = 2 */
+				/* HDMI_6Point1 (8-ch) = 3 */
+	u16	data_type;	/* HDMI_Linear = 0 */
+				/* HDMI_non_Linear = 1 */
+} __packed;
+
+
+struct afe_port_hdmi_multi_ch_cfg {
+	u16	data_type;		/* HDMI_Linear = 0 */
+					/* HDMI_non_Linear = 1 */
+	u16	channel_allocation;	/* The default is 0 (Stereo) */
+	u16	reserved;		/* must be set to 0 */
+} __packed;
+
+
+/* Slimbus Device Ids */
+#define AFE_SLIMBUS_DEVICE_1		0x0
+#define AFE_SLIMBUS_DEVICE_2		0x1
+#define AFE_PORT_MAX_AUDIO_CHAN_CNT	16
+
+struct afe_port_slimbus_cfg {
+	u16	slimbus_dev_id;		/* SLIMBUS Device id.*/
+
+	u16	slave_dev_pgd_la;	/* Slave ported generic device
+					 * logical address.
+					 */
+	u16	slave_dev_intfdev_la;	/* Slave interface device logical
+					 * address.
+					 */
+	u16	bit_width;		/* bit width of the samples, 16, 24.*/
+
+	u16	data_format;		/* data format.*/
+
+	u16	num_channels;		/* Number of channels.*/
+
+	/* Slave port mapping for respective channels.*/
+	u16	slave_port_mapping[AFE_PORT_MAX_AUDIO_CHAN_CNT];
+
+	u16	reserved;
+} __packed;
+
+struct afe_port_slimbus_sch_cfg {
+	u16	slimbus_dev_id;		/* SLIMBUS Device id.*/
+	u16	bit_width;		/* bit width of the samples, 16, 24.*/
+	u16	data_format;		/* data format.*/
+	u16	num_channels;		/* Number of channels.*/
+	u16	reserved;
+	/* Slave channel  mapping for respective channels.*/
+	u8	slave_ch_mapping[8];
+} __packed;
+
+struct afe_port_rtproxy_cfg {
+	u16	bitwidth;	/* 16,24,32 */
+	u16	interleaved;    /* interleaved = 1 */
+				/* Noninterleaved = 0 */
+	u16	frame_sz;	/* 5ms buffers = 160bytes */
+	u16	jitter;		/* 10ms of jitter = 320 */
+	u16	lw_mark;	/* Low watermark in bytes for triggering event*/
+	u16	hw_mark;	/* High watermark bytes for triggering event*/
+	u16	rsvd;
+	int	num_ch;		/* 1 to 8 */
+} __packed;
+
+struct afe_port_pseudo_cfg {
+	u16 bit_width;
+	u16 num_channels;
+	u16 data_format;
+	u16 timing_mode;
+	u16 reserved;
+} __packed;
+
+#define AFE_PORT_AUDIO_IF_CONFIG 0x000100d3
+#define AFE_PORT_AUDIO_SLIM_SCH_CONFIG 0x000100e4
+#define AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG	0x000100D9
+#define AFE_PORT_CMD_I2S_CONFIG	0x000100E7
+
+union afe_port_config {
+	struct afe_port_pcm_cfg           pcm;
+	struct afe_port_mi2s_cfg          mi2s;
+	struct afe_port_hdmi_cfg          hdmi;
+	struct afe_port_hdmi_multi_ch_cfg hdmi_multi_ch;
+	struct afe_port_slimbus_cfg	  slimbus;
+	struct afe_port_slimbus_sch_cfg	  slim_sch;
+	struct afe_port_rtproxy_cfg       rtproxy;
+	struct afe_port_pseudo_cfg        pseudo;
+} __packed;
+
+struct afe_audioif_config_command {
+	struct apr_hdr hdr;
+	u16 port_id;
+	union afe_port_config port;
+} __packed;
+
+#define AFE_TEST_CODEC_LOOPBACK_CTL 0x000100d5
+struct afe_codec_loopback_command {
+	u16	port_inf;	/* Primary i2s = 0 */
+				/* PCM = 2 */
+				/* Secondary i2s = 4 */
+				/* Mi2s = 6 */
+	u16	enable;		/* 0, disable. 1, enable */
+} __packed;
+
+
+#define AFE_PARAM_ID_SIDETONE_GAIN	0x00010300
+struct afe_param_sidetone_gain {
+	u16 gain;
+	u16 reserved;
+} __packed;
+
+#define AFE_PARAM_ID_SAMPLING_RATE	0x00010301
+struct afe_param_sampling_rate {
+	u32 sampling_rate;
+} __packed;
+
+
+#define AFE_PARAM_ID_CHANNELS		0x00010302
+struct afe_param_channels {
+	u16 channels;
+	u16 reserved;
+} __packed;
+
+
+#define AFE_PARAM_ID_LOOPBACK_GAIN	0x00010303
+struct afe_param_loopback_gain {
+	u16 gain;
+	u16 reserved;
+} __packed;
+
+/* Parameter ID used to configure and enable/disable the loopback path. The
+ * difference with respect to the existing API, AFE_PORT_CMD_LOOPBACK, is that
+ * it allows Rx port to be configured as source port in loopback path. Port-id
+ * in AFE_PORT_CMD_SET_PARAM cmd is the source port which can be Tx or Rx port.
+ * In addition, we can configure the type of routing mode to handle different
+ * use cases.
+ */
+enum {
+	/* Regular loopback from source to destination port */
+	LB_MODE_DEFAULT = 1,
+	/* Sidetone feed from Tx source to Rx destination port */
+	LB_MODE_SIDETONE,
+	/* Echo canceller reference, voice + audio + DTMF */
+	LB_MODE_EC_REF_VOICE_AUDIO,
+	/* Echo canceller reference, voice alone */
+	LB_MODE_EC_REF_VOICE
+};
+
+#define AFE_PARAM_ID_LOOPBACK_CONFIG 0x0001020B
+#define AFE_API_VERSION_LOOPBACK_CONFIG 0x1
+struct afe_param_loopback_cfg {
+	/* Minor version used for tracking the version of the configuration
+	 * interface.
+	 */
+	uint32_t loopback_cfg_minor_version;
+
+	/* Destination Port Id. */
+	uint16_t dst_port_id;
+
+	/* Specifies data path type from src to dest port. Supported values:
+	 * LB_MODE_DEFAULT
+	 * LB_MODE_SIDETONE
+	 * LB_MODE_EC_REF_VOICE_AUDIO
+	 * LB_MODE_EC_REF_VOICE
+	 */
+	uint16_t routing_mode;
+
+	/* Specifies whether to enable (1) or disable (0) an AFE loopback. */
+	uint16_t enable;
+
+	/* Reserved for 32-bit alignment. This field must be set to 0. */
+	uint16_t reserved;
+} __packed;
+
+#define AFE_MODULE_ID_PORT_INFO		0x00010200
+/* Module ID for the loopback-related parameters. */
+#define AFE_MODULE_LOOPBACK           0x00010205
+struct afe_param_payload_base {
+	u32 module_id;
+	u32 param_id;
+	u16 param_size;
+	u16 reserved;
+} __packed;
+
+struct afe_param_payload {
+	struct afe_param_payload_base base;
+	union {
+		struct afe_param_sidetone_gain sidetone_gain;
+		struct afe_param_sampling_rate sampling_rate;
+		struct afe_param_channels      channels;
+		struct afe_param_loopback_gain loopback_gain;
+		struct afe_param_loopback_cfg loopback_cfg;
+	} __packed param;
+} __packed;
+
+#define AFE_PORT_CMD_SET_PARAM		0x000100dc
+
+struct afe_port_cmd_set_param {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 payload_size;
+	u32 payload_address;
+	struct afe_param_payload payload;
+} __packed;
+
+struct afe_port_cmd_set_param_no_payload {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 payload_size;
+	u32 payload_address;
+} __packed;
+
+#define AFE_EVENT_GET_ACTIVE_PORTS 0x00010100
+struct afe_get_active_ports_rsp {
+	u16	num_ports;
+	u16	port_id;
+} __packed;
+
+
+#define AFE_EVENT_GET_ACTIVE_HANDLES 0x00010102
+struct afe_get_active_handles_rsp {
+	u16	port_id;
+	u16	num_handles;
+	u16	mode;		/* 0, voice rx */
+				/* 1, voice tx */
+				/* 2, audio rx */
+				/* 3, audio tx */
+	u16	handle;
+} __packed;
+
+#define AFE_SERVICE_CMD_MEMORY_MAP 0x000100DE
+struct afe_cmd_memory_map {
+	struct apr_hdr hdr;
+	u32 phy_addr;
+	u32 mem_sz;
+	u16 mem_id;
+	u16 rsvd;
+} __packed;
+
+#define AFE_SERVICE_CMD_MEMORY_UNMAP 0x000100DF
+struct afe_cmd_memory_unmap {
+	struct apr_hdr hdr;
+	u32 phy_addr;
+} __packed;
+
+#define AFE_SERVICE_CMD_REG_RTPORT 0x000100E0
+struct afe_cmd_reg_rtport {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 rsvd;
+} __packed;
+
+#define AFE_SERVICE_CMD_UNREG_RTPORT 0x000100E1
+struct afe_cmd_unreg_rtport {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 rsvd;
+} __packed;
+
+#define AFE_SERVICE_CMD_RTPORT_WR 0x000100E2
+struct afe_cmd_rtport_wr {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 rsvd;
+	u32 buf_addr;
+	u32 bytes_avail;
+} __packed;
+
+#define AFE_SERVICE_CMD_RTPORT_RD 0x000100E3
+struct afe_cmd_rtport_rd {
+	struct apr_hdr hdr;
+	u16 port_id;
+	u16 rsvd;
+	u32 buf_addr;
+	u32 bytes_avail;
+} __packed;
+
+#define AFE_EVENT_RT_PROXY_PORT_STATUS 0x00010105
+
+#define ADM_MAX_COPPS 5
+
+#define ADM_SERVICE_CMD_GET_COPP_HANDLES                 0x00010300
+struct adm_get_copp_handles_command {
+	struct apr_hdr hdr;
+} __packed;
+
+#define ADM_CMD_MATRIX_MAP_ROUTINGS                      0x00010301
+struct adm_routings_session {
+	u16 id;
+	u16 num_copps;
+	u16 copp_id[ADM_MAX_COPPS+1]; /*Padding if numCopps is odd */
+} __packed;
+
+struct adm_routings_command {
+	struct apr_hdr hdr;
+	u32 path; /* 0 = Rx, 1 Tx */
+	u32 num_sessions;
+	struct adm_routings_session session[8];
+} __packed;
+
+
+#define ADM_CMD_MATRIX_RAMP_GAINS                        0x00010302
+struct adm_ramp_gain {
+	struct apr_hdr hdr;
+	u16 session_id;
+	u16 copp_id;
+	u16 initial_gain;
+	u16 gain_increment;
+	u16 ramp_duration;
+	u16 reserved;
+} __packed;
+
+struct adm_ramp_gains_command {
+	struct apr_hdr hdr;
+	u32 id;
+	u32 num_gains;
+	struct adm_ramp_gain gains[ADM_MAX_COPPS];
+} __packed;
+
+
+#define ADM_CMD_COPP_OPEN                                0x00010304
+struct adm_copp_open_command {
+	struct apr_hdr hdr;
+	u16 flags;
+	u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */
+	u16 endpoint_id1;
+	u16 endpoint_id2;
+	u32 topology_id;
+	u16 channel_config;
+	u16 reserved;
+	u32 rate;
+} __packed;
+
+#define ADM_CMD_COPP_CLOSE                               0x00010305
+
+#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN                  0x00010310
+#define ADM_CMD_MULTI_CHANNEL_COPP_OPEN_V3               0x00010333
+struct adm_multi_ch_copp_open_command {
+	struct apr_hdr hdr;
+	u16 flags;
+	u16 mode; /* 1-RX, 2-Live TX, 3-Non Live TX */
+	u16 endpoint_id1;
+	u16 endpoint_id2;
+	u32 topology_id;
+	u16 channel_config;
+	u16 reserved;
+	u32 rate;
+	u8 dev_channel_mapping[8];
+} __packed;
+
+struct adm_multi_channel_copp_open_v3 {
+	struct apr_hdr hdr;
+	u16 flags;
+	u16 mode;
+	u16 endpoint_id1;
+	u16 endpoint_id2;
+	u32 topology_id;
+	u16 channel_config;
+	u16 bit_width;
+	u32 rate;
+	u8  dev_channel_mapping[8];
+};
+
+#define ADM_CMD_MEMORY_MAP				0x00010C30
+struct adm_cmd_memory_map {
+	struct apr_hdr	hdr;
+	u32		buf_add;
+	u32		buf_size;
+	u16		mempool_id;
+	u16		reserved;
+} __packed;
+
+#define ADM_CMD_MEMORY_UNMAP				0x00010C31
+struct adm_cmd_memory_unmap {
+	struct apr_hdr	hdr;
+	u32		buf_add;
+} __packed;
+
+#define ADM_CMD_MEMORY_MAP_REGIONS			0x00010C47
+struct adm_memory_map_regions {
+	u32		phys;
+	u32		buf_size;
+} __packed;
+
+struct adm_cmd_memory_map_regions {
+	struct apr_hdr	hdr;
+	u16		mempool_id;
+	u16		nregions;
+} __packed;
+
+#define ADM_CMD_MEMORY_UNMAP_REGIONS			0x00010C48
+struct adm_memory_unmap_regions {
+	u32		phys;
+} __packed;
+
+struct adm_cmd_memory_unmap_regions {
+	struct apr_hdr	hdr;
+	u16		nregions;
+	u16		reserved;
+} __packed;
+
+#define DEFAULT_COPP_TOPOLOGY				0x00010be3
+#define DEFAULT_POPP_TOPOLOGY				0x00010be4
+#define VPM_TX_SM_ECNS_COPP_TOPOLOGY			0x00010F71
+#define VPM_TX_DM_FLUENCE_COPP_TOPOLOGY			0x00010F72
+#define VPM_TX_QMIC_FLUENCE_COPP_TOPOLOGY		0x00010F75
+
+#define LOWLATENCY_POPP_TOPOLOGY			0x00010C68
+#define LOWLATENCY_COPP_TOPOLOGY			0x00010312
+#define PCM_BITS_PER_SAMPLE				16
+
+#define ASM_OPEN_WRITE_PERF_MODE_BIT			(1<<28)
+#define ASM_OPEN_READ_PERF_MODE_BIT			(1<<29)
+#define ADM_MULTI_CH_COPP_OPEN_PERF_MODE_BIT		(1<<13)
+
+
+#define ASM_MAX_EQ_BANDS 12
+
+struct asm_eq_band {
+	u32 band_idx; /* The band index, 0 .. 11 */
+	u32 filter_type; /* Filter band type */
+	u32 center_freq_hz; /* Filter band center frequency */
+	u32 filter_gain; /* Filter band initial gain (dB) */
+			/* Range is +12 dB to -12 dB with 1dB increments. */
+	u32 q_factor;
+} __packed;
+
+struct asm_equalizer_params {
+	u32 enable;
+	u32 num_bands;
+	struct asm_eq_band eq_bands[ASM_MAX_EQ_BANDS];
+} __packed;
+
+struct asm_master_gain_params {
+	u16 master_gain;
+	u16 padding;
+} __packed;
+
+struct asm_lrchannel_gain_params {
+	u16 left_gain;
+	u16 right_gain;
+} __packed;
+
+struct asm_mute_params {
+	u32 muteflag;
+} __packed;
+
+struct asm_softvolume_params {
+	u32 period;
+	u32 step;
+	u32 rampingcurve;
+} __packed;
+
+struct asm_softpause_params {
+	u32 enable;
+	u32 period;
+	u32 step;
+	u32 rampingcurve;
+} __packed;
+
+struct asm_pp_param_data_hdr {
+	u32 module_id;
+	u32 param_id;
+	u16 param_size;
+	u16 reserved;
+} __packed;
+
+struct asm_pp_params_command {
+	struct apr_hdr	hdr;
+	u32    *payload;
+	u32	payload_size;
+	struct  asm_pp_param_data_hdr params;
+} __packed;
+
+#define EQUALIZER_MODULE_ID		0x00010c27
+#define EQUALIZER_PARAM_ID		0x00010c28
+
+#define VOLUME_CONTROL_MODULE_ID	0x00010bfe
+#define MASTER_GAIN_PARAM_ID		0x00010bff
+#define L_R_CHANNEL_GAIN_PARAM_ID	0x00010c00
+#define MUTE_CONFIG_PARAM_ID 0x00010c01
+#define SOFT_PAUSE_PARAM_ID 0x00010D6A
+#define SOFT_VOLUME_PARAM_ID 0x00010C29
+
+#define IIR_FILTER_ENABLE_PARAM_ID 0x00010c03
+#define IIR_FILTER_PREGAIN_PARAM_ID 0x00010c04
+#define IIR_FILTER_CONFIG_PARAM_ID 0x00010c05
+
+#define MBADRC_MODULE_ID 0x00010c06
+#define MBADRC_ENABLE_PARAM_ID 0x00010c07
+#define MBADRC_CONFIG_PARAM_ID 0x00010c08
+
+
+#define ADM_CMD_SET_PARAMS                               0x00010306
+#define ADM_CMD_GET_PARAMS                               0x0001030B
+#define ADM_CMDRSP_GET_PARAMS                            0x0001030C
+struct adm_set_params_command {
+	struct apr_hdr		hdr;
+	u32			payload;
+	u32			payload_size;
+} __packed;
+
+
+#define ADM_CMD_TAP_COPP_PCM                             0x00010307
+struct adm_tap_copp_pcm_command {
+	struct apr_hdr hdr;
+} __packed;
+
+
+/* QDSP6 to Client messages */
+#define ADM_SERVICE_CMDRSP_GET_COPP_HANDLES              0x00010308
+struct adm_get_copp_handles_respond {
+	struct apr_hdr hdr;
+	u32 handles;
+	u32 copp_id;
+} __packed;
+
+#define ADM_CMDRSP_COPP_OPEN                             0x0001030A
+struct adm_copp_open_respond {
+	u32 status;
+	u16 copp_id;
+	u16 reserved;
+} __packed;
+
+#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN               0x00010311
+#define ADM_CMDRSP_MULTI_CHANNEL_COPP_OPEN_V3            0x00010334
+
+
+#define ASM_STREAM_PRIORITY_NORMAL	0
+#define ASM_STREAM_PRIORITY_LOW		1
+#define ASM_STREAM_PRIORITY_HIGH	2
+#define ASM_STREAM_PRIORITY_RESERVED	3
+
+#define ASM_END_POINT_DEVICE_MATRIX	0
+#define ASM_END_POINT_STREAM		1
+
+#define AAC_ENC_MODE_AAC_LC            0x02
+#define AAC_ENC_MODE_AAC_P             0x05
+#define AAC_ENC_MODE_EAAC_P            0x1D
+
+#define ASM_STREAM_CMD_CLOSE                             0x00010BCD
+#define ASM_STREAM_CMD_FLUSH                             0x00010BCE
+#define ASM_STREAM_CMD_SET_PP_PARAMS                     0x00010BCF
+#define ASM_STREAM_CMD_GET_PP_PARAMS                     0x00010BD0
+#define ASM_STREAM_CMDRSP_GET_PP_PARAMS                  0x00010BD1
+#define ASM_SESSION_CMD_PAUSE                            0x00010BD3
+#define ASM_SESSION_CMD_GET_SESSION_TIME                 0x00010BD4
+#define ASM_DATA_CMD_EOS                                 0x00010BDB
+#define ASM_DATA_EVENT_EOS                               0x00010BDD
+
+#define ASM_SERVICE_CMD_GET_STREAM_HANDLES               0x00010C0B
+#define ASM_STREAM_CMD_FLUSH_READBUFS                    0x00010C09
+
+#define ASM_SESSION_EVENT_RX_UNDERFLOW			 0x00010C17
+#define ASM_SESSION_EVENT_TX_OVERFLOW			 0x00010C18
+#define ASM_SERVICE_CMD_GET_WALLCLOCK_TIME               0x00010C19
+#define ASM_DATA_CMDRSP_EOS                              0x00010C1C
+
+/* ASM Data structures */
+
+/* common declarations */
+struct asm_pcm_cfg {
+	u16 ch_cfg;
+	u16 bits_per_sample;
+	u32 sample_rate;
+	u16 is_signed;
+	u16 interleaved;
+};
+
+#define PCM_CHANNEL_NULL 0
+
+/* Front left channel. */
+#define PCM_CHANNEL_FL    1
+
+/* Front right channel. */
+#define PCM_CHANNEL_FR    2
+
+/* Front center channel. */
+#define PCM_CHANNEL_FC    3
+
+/* Left surround channel.*/
+#define PCM_CHANNEL_LS   4
+
+/* Right surround channel.*/
+#define PCM_CHANNEL_RS   5
+
+/* Low frequency effect channel. */
+#define PCM_CHANNEL_LFE  6
+
+/* Center surround channel; Rear center channel. */
+#define PCM_CHANNEL_CS   7
+
+/* Left back channel; Rear left channel. */
+#define PCM_CHANNEL_LB   8
+
+/* Right back channel; Rear right channel. */
+#define PCM_CHANNEL_RB   9
+
+/* Top surround channel. */
+#define PCM_CHANNEL_TS   10
+
+/* Center vertical height channel.*/
+#define PCM_CHANNEL_CVH  11
+
+/* Mono surround channel.*/
+#define PCM_CHANNEL_MS   12
+
+/* Front left of center. */
+#define PCM_CHANNEL_FLC  13
+
+/* Front right of center. */
+#define PCM_CHANNEL_FRC  14
+
+/* Rear left of center. */
+#define PCM_CHANNEL_RLC  15
+
+/* Rear right of center. */
+#define PCM_CHANNEL_RRC  16
+
+#define PCM_FORMAT_MAX_NUM_CHANNEL  8
+
+/* Maximum number of channels supported
+ * in ASM_ENCDEC_DEC_CHAN_MAP command
+ */
+#define MAX_CHAN_MAP_CHANNELS 16
+/*
+ *  Multiple-channel PCM decoder format block structure used in the
+ *  #ASM_STREAM_CMD_OPEN_WRITE command.
+ *  The data must be in little-endian format.
+ */
+struct asm_multi_channel_pcm_fmt_blk {
+
+	u16 num_channels;	/*
+				 * Number of channels.
+				 * Supported values:1 to 8
+				 */
+
+	u16 bits_per_sample;	/*
+				 * Number of bits per sample per channel.
+				 * Supported values: 16, 24 When used for
+				 * playback, the client must send 24-bit
+				 * samples packed in 32-bit words. The
+				 * 24-bit samples must be placed in the most
+				 * significant 24 bits of the 32-bit word. When
+				 * used for recording, the aDSP sends 24-bit
+				 * samples packed in 32-bit words. The 24-bit
+				 * samples are placed in the most significant
+				 * 24 bits of the 32-bit word.
+				 */
+
+	u32 sample_rate;	/*
+				 * Number of samples per second
+				 * (in Hertz). Supported values:
+				 * 2000 to 48000
+				 */
+
+	u16 is_signed;		/*
+				 * Flag that indicates the samples
+				 * are signed (1).
+				 */
+
+	u16 is_interleaved;	/*
+				 * Flag that indicates whether the channels are
+				 * de-interleaved (0) or interleaved (1).
+				 * Interleaved format means corresponding
+				 * samples from the left and right channels are
+				 * interleaved within the buffer.
+				 * De-interleaved format means samples from
+				 * each channel are contiguous in the buffer.
+				 * The samples from one channel immediately
+				 * follow those of the previous channel.
+				 */
+
+	u8 channel_mapping[8];	/*
+				 * Supported values:
+				 * PCM_CHANNEL_NULL, PCM_CHANNEL_FL,
+				 * PCM_CHANNEL_FR, PCM_CHANNEL_FC,
+				 * PCM_CHANNEL_LS, PCM_CHANNEL_RS,
+				 * PCM_CHANNEL_LFE, PCM_CHANNEL_CS,
+				 * PCM_CHANNEL_LB, PCM_CHANNEL_RB,
+				 * PCM_CHANNEL_TS, PCM_CHANNEL_CVH,
+				 * PCM_CHANNEL_MS, PCM_CHANNEL_FLC,
+				 * PCM_CHANNEL_FRC, PCM_CHANNEL_RLC,
+				 * PCM_CHANNEL_RRC.
+				 * Channel[i] mapping describes channel I. Each
+				 * element i of the array describes channel I
+				 * inside the buffer where  I < num_channels.
+				 * An unused channel is set to zero.
+				 */
+};
+struct asm_dts_enc_cfg {
+	uint32_t	sample_rate;
+	/*
+	 * Samples at which input is to be encoded.
+	 * Supported values:
+	 * 44100 -- encode at 44.1 Khz
+	 * 48000 -- encode at 48 Khz
+	 */
+
+	uint32_t	num_channels;
+	/*
+	 * Number of channels for multi-channel encoding.
+	 * Supported values: 1 to 6
+	 */
+
+	uint8_t		channel_mapping[6];
+	/*
+	 * Channel array of size 16. Channel[i] mapping describes channel I.
+	 * Each element i of the array describes channel I inside the buffer
+	 * where num_channels. An unused channel is set to zero. Only first
+	 * num_channels elements are valid
+	 *
+	 * Supported values:
+	 * - # PCM_CHANNEL_L
+	 * - # PCM_CHANNEL_R
+	 * - # PCM_CHANNEL_C
+	 * - # PCM_CHANNEL_LS
+	 * - # PCM_CHANNEL_RS
+	 * - # PCM_CHANNEL_LFE
+	 */
+
+};
+struct asm_adpcm_cfg {
+	u16 ch_cfg;
+	u16 bits_per_sample;
+	u32 sample_rate;
+	u32 block_size;
+};
+
+struct asm_yadpcm_cfg {
+	u16 ch_cfg;
+	u16 bits_per_sample;
+	u32 sample_rate;
+};
+
+struct asm_midi_cfg {
+	u32 nMode;
+};
+
+struct asm_wma_cfg {
+	u16 format_tag;
+	u16 ch_cfg;
+	u32 sample_rate;
+	u32 avg_bytes_per_sec;
+	u16 block_align;
+	u16 valid_bits_per_sample;
+	u32 ch_mask;
+	u16 encode_opt;
+	u16 adv_encode_opt;
+	u32 adv_encode_opt2;
+	u32 drc_peak_ref;
+	u32 drc_peak_target;
+	u32 drc_ave_ref;
+	u32 drc_ave_target;
+};
+
+struct asm_wmapro_cfg {
+	u16 format_tag;
+	u16 ch_cfg;
+	u32 sample_rate;
+	u32 avg_bytes_per_sec;
+	u16 block_align;
+	u16 valid_bits_per_sample;
+	u32 ch_mask;
+	u16 encode_opt;
+	u16 adv_encode_opt;
+	u32 adv_encode_opt2;
+	u32 drc_peak_ref;
+	u32 drc_peak_target;
+	u32 drc_ave_ref;
+	u32 drc_ave_target;
+};
+
+struct asm_aac_cfg {
+	u16 format;
+	u16 aot;
+	u16 ep_config;
+	u16 section_data_resilience;
+	u16 scalefactor_data_resilience;
+	u16 spectral_data_resilience;
+	u16 ch_cfg;
+	u16 reserved;
+	u32 sample_rate;
+};
+
+struct asm_amrwbplus_cfg {
+	u32  size_bytes;
+	u32  version;
+	u32  num_channels;
+	u32  amr_band_mode;
+	u32  amr_dtx_mode;
+	u32  amr_frame_fmt;
+	u32  amr_lsf_idx;
+};
+
+struct asm_flac_cfg {
+	u16 stream_info_present;
+	u16 min_blk_size;
+	u16 max_blk_size;
+	u16 ch_cfg;
+	u16 sample_size;
+	u16 sample_rate;
+	u16 md5_sum;
+	u32 ext_sample_rate;
+	u32 min_frame_size;
+	u32 max_frame_size;
+};
+
+struct asm_vorbis_cfg {
+	u32 ch_cfg;
+	u32 bit_rate;
+	u32 min_bit_rate;
+	u32 max_bit_rate;
+	u16 bit_depth_pcm_sample;
+	u16 bit_stream_format;
+};
+
+struct asm_aac_read_cfg {
+	u32 bitrate;
+	u32 enc_mode;
+	u16 format;
+	u16 ch_cfg;
+	u32 sample_rate;
+};
+
+struct asm_amrnb_read_cfg {
+	u16 mode;
+	u16 dtx_mode;
+};
+
+struct asm_amrwb_read_cfg {
+	u16 mode;
+	u16 dtx_mode;
+};
+
+struct asm_evrc_read_cfg {
+	u16 max_rate;
+	u16 min_rate;
+	u16 rate_modulation_cmd;
+	u16 reserved;
+};
+
+struct asm_qcelp13_read_cfg {
+	u16 max_rate;
+	u16 min_rate;
+	u16 reduced_rate_level;
+	u16 rate_modulation_cmd;
+};
+
+struct asm_sbc_read_cfg {
+	u32 subband;
+	u32 block_len;
+	u32 ch_mode;
+	u32 alloc_method;
+	u32 bit_rate;
+	u32 sample_rate;
+};
+
+struct asm_sbc_bitrate {
+	u32 bitrate;
+};
+
+struct asm_immed_decode {
+	u32 mode;
+};
+
+struct asm_sbr_ps {
+	u32 enable;
+};
+
+struct asm_dual_mono {
+	u16 sce_left;
+	u16 sce_right;
+};
+
+struct asm_dec_chan_map {
+	u32 num_channels;			  /* Number of decoder output
+						   * channels. A value of 0
+						   * indicates native channel
+						   * mapping, which is valid
+						   * only for NT mode. This
+						   * means the output of the
+						   * decoder is to be preserved
+						   * as is.
+						   */
+
+	u8 channel_mapping[MAX_CHAN_MAP_CHANNELS];/* Channel array of size
+						   * num_channels. It can grow
+						   * till MAX_CHAN_MAP_CHANNELS.
+						   * Channel[i] mapping
+						   * describes channel I inside
+						   * the decoder output buffer.
+						   * Valid channel mapping
+						   * values are to be present at
+						   * the beginning of the array.
+						   * All remaining elements of
+						   * the array are to be filled
+						   * with PCM_CHANNEL_NULL.
+						   */
+};
+
+struct asm_encode_cfg_blk {
+	u32 frames_per_buf;
+	u32 format_id;
+	u32 cfg_size;
+	union {
+		struct asm_pcm_cfg          pcm;
+		struct asm_aac_read_cfg     aac;
+		struct asm_amrnb_read_cfg   amrnb;
+		struct asm_evrc_read_cfg    evrc;
+		struct asm_qcelp13_read_cfg qcelp13;
+		struct asm_sbc_read_cfg     sbc;
+		struct asm_amrwb_read_cfg   amrwb;
+		struct asm_multi_channel_pcm_fmt_blk      mpcm;
+		struct asm_dts_enc_cfg      dts;
+	} __packed cfg;
+};
+
+struct asm_frame_meta_info {
+	u32 offset_to_frame;
+	u32 frame_size;
+	u32 encoded_pcm_samples;
+	u32 msw_ts;
+	u32 lsw_ts;
+	u32 nflags;
+};
+
+/* Stream level commands */
+#define ASM_STREAM_CMD_OPEN_READ                         0x00010BCB
+#define ASM_STREAM_CMD_OPEN_READ_V2_1                    0x00010DB2
+struct asm_stream_cmd_open_read {
+	struct apr_hdr hdr;
+	u32            uMode;
+	u32            src_endpoint;
+	u32            pre_proc_top;
+	u32            format;
+} __packed;
+
+struct asm_stream_cmd_open_read_v2_1 {
+	struct apr_hdr hdr;
+	u32            uMode;
+	u32            src_endpoint;
+	u32            pre_proc_top;
+	u32            format;
+	u16            bits_per_sample;
+	u16            reserved;
+} __packed;
+
+/* Supported formats */
+#define LINEAR_PCM   0x00010BE5
+#define DTMF         0x00010BE6
+#define ADPCM        0x00010BE7
+#define YADPCM       0x00010BE8
+#define MP3          0x00010BE9
+#define MPEG4_AAC    0x00010BEA
+#define AMRNB_FS     0x00010BEB
+#define AMRWB_FS     0x00010BEC
+#define V13K_FS      0x00010BED
+#define EVRC_FS      0x00010BEE
+#define EVRCB_FS     0x00010BEF
+#define EVRCWB_FS    0x00010BF0
+#define MIDI         0x00010BF1
+#define SBC          0x00010BF2
+#define WMA_V10PRO   0x00010BF3
+#define WMA_V9       0x00010BF4
+#define AMR_WB_PLUS  0x00010BF5
+#define AC3_DECODER  0x00010BF6
+#define EAC3_DECODER 0x00010C3C
+#define DTS	0x00010D88
+#define DTS_LBR	0x00010DBB
+#define MP2          0x00010DBE
+#define ATRAC	0x00010D89
+#define MAT	0x00010D8A
+#define G711_ALAW_FS 0x00010BF7
+#define G711_MLAW_FS 0x00010BF8
+#define G711_PCM_FS  0x00010BF9
+#define MPEG4_MULTI_AAC 0x00010D86
+#define US_POINT_EPOS_FORMAT 0x00012310
+#define US_RAW_FORMAT        0x0001127C
+#define US_PROX_FORMAT       0x0001272B
+#define MULTI_CHANNEL_PCM    0x00010C66
+
+#define ASM_ENCDEC_SBCRATE         0x00010C13
+#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14
+#define ASM_ENCDEC_CFG_BLK         0x00010C2C
+
+#define ASM_ENCDEC_SBCRATE         0x00010C13
+#define ASM_ENCDEC_IMMDIATE_DECODE 0x00010C14
+#define ASM_ENCDEC_CFG_BLK         0x00010C2C
+
+#define ASM_STREAM_CMD_OPEN_READ_COMPRESSED               0x00010D95
+struct asm_stream_cmd_open_read_compressed {
+	struct apr_hdr hdr;
+	u32            uMode;
+	u32            frame_per_buf;
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_WRITE                        0x00010BCA
+#define ASM_STREAM_CMD_OPEN_WRITE_V2_1                   0x00010DB1
+struct asm_stream_cmd_open_write {
+	struct apr_hdr hdr;
+	u32            uMode;
+	u16            sink_endpoint;
+	u16            stream_handle;
+	u32            post_proc_top;
+	u32            format;
+} __packed;
+
+#define IEC_61937_MASK	0x00000001
+#define IEC_60958_MASK	0x00000002
+
+#define ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED	0x00010D84
+struct asm_stream_cmd_open_write_compressed {
+	struct apr_hdr hdr;
+	u32	flags;
+	u32	format;
+} __packed;
+#define ASM_STREAM_CMD_OPEN_TRANSCODE_LOOPBACK     0x00010DBA
+struct asm_stream_cmd_open_transcode_loopback {
+	struct apr_hdr hdr;
+	uint32_t	mode_flags;
+	/*
+	 * All bits are reserved. Clients must set them to zero.
+	 */
+
+	uint32_t	src_format_id;
+	/*
+	 * Specifies the media format of the input audio stream.
+	 *
+	 * Supported values:
+	 * - #ASM_MEDIA_FMT_LINEAR_PCM
+	 * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM
+	 */
+
+	uint32_t	sink_format_id;
+	/*
+	 * Specifies the media format of the output stream.
+	 *
+	 * Supported values:
+	 * - #ASM_MEDIA_FMT_LINEAR_PCM
+	 * - #ASM_MEDIA_FMT_MULTI_CHANNEL_PCM
+	 * - #ASM_MEDIA_FMT_DTS
+	 */
+
+	uint32_t	audproc_topo_id;
+	/*
+	 * Postprocessing topology ID, which specifies the topology (order of
+	 * processing) of postprocessing algorithms.
+	 *
+	 * Supported values:
+	 * - #ASM_STREAM_POSTPROC_TOPO_ID_DEFAULT
+	 * - #ASM_STREAM_POSTPROC_TOPO_ID_PEAKMETER
+	 * - #ASM_STREAM_POSTPROC_TOPO_ID_NONE
+	 * - #ASM_STREAM_POSTPROC_TOPO_ID_MCH_PEAK_VOL
+	 */
+
+	uint16_t	src_endpoint_type;
+	/*
+	 * Specifies the source endpoint that provides the input samples.
+	 *
+	 * Supported values:
+	 * - 0 -- Tx device matrix or stream router
+	 * (gateway to the hardware ports)
+	 * - All other values are reserved
+	 *
+	 * Clients must set this field to zero. Otherwise, an error is returned.
+	 */
+
+	uint16_t	sink_endpoint_type;
+	/*
+	 * Specifies the sink endpoint type.
+	 *
+	 * Supported values:
+	 * - 0 -- Rx device matrix or stream router
+	 * (gateway to the hardware ports)
+	 * - All other values are reserved
+	 *
+	 * Clients must set this field to zero. Otherwise, an error is returned.
+	 */
+
+	uint16_t	bits_per_sample;
+	/*
+	 * Number of bits per sample processed by the ASM modules.
+	 * Supported values: 16, 24
+	 */
+
+	uint16_t	reserved;
+	/*
+	 * This field must be set to zero.
+	 */
+} __packed;
+
+/*
+ * ID of the DTS mix LFE channel to front channels parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ * asm_dts_generic_param_t
+ * ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT
+ */
+#define ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT                          0x00010DB6
+
+/*
+ * ID of the DTS DRC ratio parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ * asm_dts_generic_param_t
+ * ASM_PARAM_ID_DTS_DRC_RATIO
+ */
+#define ASM_PARAM_ID_DTS_DRC_RATIO                                   0x00010DB7
+
+/*
+ * ID of the DTS enable dialog normalization parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ *
+ * asm_dts_generic_param_t
+ * ASM_PARAM_ID_DTS_ENABLE_DIALNORM
+ */
+#define ASM_PARAM_ID_DTS_ENABLE_DIALNORM                             0x00010DB8
+
+/*
+ * ID of the DTS enable parse REV2AUX parameter in the
+ * #ASM_STREAM_CMD_SET_ENCDEC_PARAM command.
+ * asm_dts_generic_param_t
+ * ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX
+ */
+#define ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX                         0x00010DB9
+
+struct asm_dts_generic_param {
+	int32_t		generic_parameter;
+	/*
+	 * #ASM_PARAM_ID_DTS_MIX_LFE_TO_FRONT:
+	 * - if enabled, mixes LFE channel to front
+	 * while downmixing (if necessary)
+	 * - Supported values: 1-> enable, 0-> disable
+	 * - Default: disabled
+	 *
+	 * #ASM_PARAM_ID_DTS_DRC_RATIO:
+	 * - percentage of DRC ratio.
+	 * - Supported values: 0-100
+	 * - Default: 0, DRC is disabled.
+	 *
+	 * #ASM_PARAM_ID_DTS_ENABLE_DIALNORM:
+	 * - flag to enable dialog normalization post processing.
+	 * - Supported values: 1-> enable, 0-> disable.
+	 * - Default: enabled.
+	 *
+	 * #ASM_PARAM_ID_DTS_ENABLE_PARSE_REV2AUX:
+	 * - flag to enable parsing of rev2aux chunk in the bitstream.
+	 * This chunk contains broadcast metadata.
+	 * - Supported values: 1-> enable, 0-> disable.
+	 * - Default: disabled.
+	 */
+};
+
+struct asm_stream_cmd_dts_dec_param {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_dts_generic_param generic_param;
+} __packed;
+
+
+#define ASM_STREAM_CMD_OPEN_READWRITE                    0x00010BCC
+
+struct asm_stream_cmd_open_read_write {
+	struct apr_hdr     hdr;
+	u32                uMode;
+	u32                post_proc_top;
+	u32                write_format;
+	u32                read_format;
+} __packed;
+
+#define ASM_STREAM_CMD_OPEN_LOOPBACK	0x00010D6E
+struct asm_stream_cmd_open_loopback {
+	struct apr_hdr         hdr;
+	u32                    mode_flags;
+/* Mode flags.
+ * Bit 0-31: reserved; client should set these bits to 0
+ */
+	u16                    src_endpointype;
+	/* Endpoint type. 0 = Tx Matrix */
+	u16                    sink_endpointype;
+	/* Endpoint type. 0 = Rx Matrix */
+	u32                    postprocopo_id;
+/* Postprocessor topology ID. Specifies the topology of
+ * postprocessing algorithms.
+ */
+} __packed;
+
+#define ADM_CMD_CONNECT_AFE_PORT 0x00010320
+#define ADM_CMD_DISCONNECT_AFE_PORT 0x00010321
+
+struct adm_cmd_connect_afe_port {
+	struct apr_hdr     hdr;
+	u8	mode; /*mode represent the interface is for RX or TX*/
+	u8	session_id; /*ASM session ID*/
+	u16	afe_port_id;
+} __packed;
+
+#define ADM_CMD_CONNECT_AFE_PORT_V2 0x00010332
+
+struct adm_cmd_connect_afe_port_v2 {
+	struct apr_hdr     hdr;
+	u8	mode; /*mode represent the interface is for RX or TX*/
+	u8	session_id; /*ASM session ID*/
+	u16	afe_port_id;
+	u32	num_channels;
+	u32	sampling_rate;
+} __packed;
+
+#define ASM_STREAM_CMD_SET_ENCDEC_PARAM                  0x00010C10
+#define ASM_STREAM_CMD_GET_ENCDEC_PARAM                  0x00010C11
+#define ASM_ENCDEC_CFG_BLK_ID				 0x00010C2C
+#define ASM_ENABLE_SBR_PS				 0x00010C63
+#define ASM_CONFIGURE_DUAL_MONO			 0x00010C64
+struct asm_stream_cmd_encdec_cfg_blk {
+	struct apr_hdr              hdr;
+	u32                         param_id;
+	u32                         param_size;
+	struct asm_encode_cfg_blk   enc_blk;
+} __packed;
+
+struct asm_stream_cmd_encdec_sbc_bitrate {
+	struct apr_hdr hdr;
+	u32            param_id;
+		struct asm_sbc_bitrate      sbc_bitrate;
+} __packed;
+
+struct asm_stream_cmd_encdec_immed_decode {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_immed_decode dec;
+} __packed;
+
+struct asm_stream_cmd_encdec_sbr {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_sbr_ps sbr_ps;
+} __packed;
+
+struct asm_stream_cmd_encdec_dualmono {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_dual_mono channel_map;
+} __packed;
+
+#define ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG        0x00010DD8
+
+/* Structure for AAC decoder stereo coefficient setting. */
+
+struct asm_aac_stereo_mix_coeff_selection_param {
+	struct apr_hdr				hdr;
+	u32					param_id;
+	u32					param_size;
+	u32					aac_stereo_mix_coeff_flag;
+} __packed;
+
+#define ASM_ENCDEC_DEC_CHAN_MAP				 0x00010D82
+struct asm_stream_cmd_encdec_channelmap {
+	struct apr_hdr hdr;
+	u32            param_id;
+	u32            param_size;
+	struct asm_dec_chan_map chan_map;
+} __packed;
+
+#define ASM_STREAM_CMD_ADJUST_SAMPLES                   0x00010C0A
+struct asm_stream_cmd_adjust_samples {
+	struct apr_hdr hdr;
+	u16            nsamples;
+	u16            reserved;
+} __packed;
+
+#define ASM_STREAM_CMD_TAP_POPP_PCM                      0x00010BF9
+struct asm_stream_cmd_tap_popp_pcm {
+	struct apr_hdr hdr;
+	u16            enable;
+	u16            reserved;
+	u32            module_id;
+} __packed;
+
+/*  Session Level commands */
+#define ASM_SESSION_CMD_MEMORY_MAP			0x00010C32
+struct asm_stream_cmd_memory_map {
+	struct apr_hdr	hdr;
+	u32		buf_add;
+	u32		buf_size;
+	u16		mempool_id;
+	u16		reserved;
+} __packed;
+
+#define ASM_SESSION_CMD_MEMORY_UNMAP			0x00010C33
+struct asm_stream_cmd_memory_unmap {
+	struct apr_hdr	hdr;
+	u32		buf_add;
+} __packed;
+
+#define ASM_SESSION_CMD_MEMORY_MAP_REGIONS		0x00010C45
+struct asm_memory_map_regions {
+	u32		phys;
+	u32		buf_size;
+} __packed;
+
+struct asm_stream_cmd_memory_map_regions {
+	struct apr_hdr	hdr;
+	u16		mempool_id;
+	u16		nregions;
+} __packed;
+
+#define ASM_SESSION_CMD_MEMORY_UNMAP_REGIONS		0x00010C46
+struct asm_memory_unmap_regions {
+	u32		phys;
+} __packed;
+
+struct asm_stream_cmd_memory_unmap_regions {
+	struct apr_hdr	hdr;
+	u16		nregions;
+	u16		reserved;
+} __packed;
+
+#define ASM_SESSION_CMD_RUN                              0x00010BD2
+struct asm_stream_cmd_run {
+	struct apr_hdr hdr;
+	u32            flags;
+	u32            msw_ts;
+	u32            lsw_ts;
+} __packed;
+
+/* Session level events */
+#define ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS 0x00010BD5
+struct asm_stream_cmd_reg_rx_underflow_event {
+	struct apr_hdr hdr;
+	u16            enable;
+	u16            reserved;
+} __packed;
+
+#define ASM_SESSION_CMD_REGISTER_FOR_TX_OVERFLOW_EVENTS  0x00010BD6
+struct asm_stream_cmd_reg_tx_overflow_event {
+	struct apr_hdr hdr;
+	u16            enable;
+	u16            reserved;
+} __packed;
+
+/* Data Path commands */
+#define ASM_DATA_CMD_WRITE                               0x00010BD9
+struct asm_stream_cmd_write {
+	struct apr_hdr     hdr;
+	u32	buf_add;
+	u32	avail_bytes;
+	u32	uid;
+	u32	msw_ts;
+	u32	lsw_ts;
+	u32	uflags;
+} __packed;
+
+#define ASM_DATA_CMD_READ                                0x00010BDA
+struct asm_stream_cmd_read {
+	struct apr_hdr     hdr;
+	u32	buf_add;
+	u32	buf_size;
+	u32	uid;
+} __packed;
+
+#define ASM_DATA_CMD_READ_COMPRESSED                     0x00010DBF
+struct asm_stream_cmd_read_compressed {
+	struct apr_hdr     hdr;
+	u32	buf_add;
+	u32	buf_size;
+	u32	uid;
+} __packed;
+
+#define ASM_DATA_CMD_MEDIA_FORMAT_UPDATE                 0x00010BDC
+#define ASM_DATA_EVENT_ENC_SR_CM_NOTIFY                  0x00010BDE
+struct asm_stream_media_format_update {
+	struct apr_hdr hdr;
+	u32            format;
+	u32            cfg_size;
+	union {
+		struct asm_pcm_cfg         pcm_cfg;
+		struct asm_adpcm_cfg       adpcm_cfg;
+		struct asm_yadpcm_cfg      yadpcm_cfg;
+		struct asm_midi_cfg        midi_cfg;
+		struct asm_wma_cfg         wma_cfg;
+		struct asm_wmapro_cfg      wmapro_cfg;
+		struct asm_aac_cfg         aac_cfg;
+		struct asm_flac_cfg        flac_cfg;
+		struct asm_vorbis_cfg      vorbis_cfg;
+		struct asm_multi_channel_pcm_fmt_blk multi_ch_pcm_cfg;
+		struct asm_amrwbplus_cfg   amrwbplus_cfg;
+	} __packed write_cfg;
+} __packed;
+
+
+/* Command Responses */
+#define ASM_STREAM_CMDRSP_GET_ENCDEC_PARAM               0x00010C12
+struct asm_stream_cmdrsp_get_readwrite_param {
+	struct apr_hdr hdr;
+	u32            status;
+	u32            param_id;
+	u16            param_size;
+	u16            padding;
+	union {
+		struct asm_sbc_bitrate      sbc_bitrate;
+		struct asm_immed_decode aac_dec;
+	} __packed read_write_cfg;
+} __packed;
+
+
+#define ASM_SESSION_CMDRSP_GET_SESSION_TIME              0x00010BD8
+struct asm_stream_cmdrsp_get_session_time {
+	struct apr_hdr hdr;
+	u32            status;
+	u32            msw_ts;
+	u32            lsw_ts;
+} __packed;
+
+#define ASM_DATA_EVENT_WRITE_DONE                        0x00010BDF
+struct asm_data_event_write_done {
+	u32	buf_add;
+	u32            status;
+} __packed;
+
+#define ASM_DATA_EVENT_READ_DONE                         0x00010BE0
+struct asm_data_event_read_done {
+	u32            status;
+	u32            buffer_add;
+	u32            enc_frame_size;
+	u32            offset;
+	u32            msw_ts;
+	u32            lsw_ts;
+	u32            flags;
+	u32            num_frames;
+	u32            id;
+} __packed;
+
+#define ASM_DATA_EVENT_READ_COMPRESSED_DONE              0x00010DC0
+struct asm_data_event_read_compressed_done {
+	u32            status;
+	u32            buffer_add;
+	u32            enc_frame_size;
+	u32            offset;
+	u32            msw_ts;
+	u32            lsw_ts;
+	u32            flags;
+	u32            num_frames;
+	u32            id;
+} __packed;
+
+#define ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY               0x00010C65
+struct asm_data_event_sr_cm_change_notify {
+	u32            sample_rate;
+	u16	           no_of_channels;
+	u16            reserved;
+	u8             channel_map[8];
+} __packed;
+
+/* service level events */
+
+#define ASM_SERVICE_CMDRSP_GET_STREAM_HANDLES            0x00010C1B
+struct asm_svc_cmdrsp_get_strm_handles {
+	struct apr_hdr hdr;
+	u32            num_handles;
+	u32            stream_handles;
+} __packed;
+
+
+#define ASM_SERVICE_CMDRSP_GET_WALLCLOCK_TIME            0x00010C1A
+struct asm_svc_cmdrsp_get_wallclock_time {
+	struct apr_hdr hdr;
+	u32            status;
+	u32            msw_ts;
+	u32            lsw_ts;
+} __packed;
+
+/* Error code */
+#define ADSP_EOK          0x00000000 /* Success / completed / no errors. */
+#define ADSP_EFAILED      0x00000001 /* General failure. */
+#define ADSP_EBADPARAM    0x00000002 /* Bad operation parameter(s). */
+#define ADSP_EUNSUPPORTED 0x00000003 /* Unsupported routine/operation. */
+#define ADSP_EVERSION     0x00000004 /* Unsupported version. */
+#define ADSP_EUNEXPECTED  0x00000005 /* Unexpected problem encountered. */
+#define ADSP_EPANIC       0x00000006 /* Unhandled problem occurred. */
+#define ADSP_ENORESOURCE  0x00000007 /* Unable to allocate resource(s). */
+#define ADSP_EHANDLE      0x00000008 /* Invalid handle. */
+#define ADSP_EALREADY     0x00000009 /* Operation is already processed. */
+#define ADSP_ENOTREADY    0x0000000A /* Operation not ready to be processed*/
+#define ADSP_EPENDING     0x0000000B /* Operation is pending completion*/
+#define ADSP_EBUSY        0x0000000C /* Operation could not be accepted or
+				      * processed.
+				      */
+#define ADSP_EABORTED     0x0000000D /* Operation aborted due to an error. */
+#define ADSP_EPREEMPTED   0x0000000E /* Operation preempted by higher priority*/
+#define ADSP_ECONTINUE    0x0000000F /* Operation requests intervention
+				      * to complete.
+				      */
+#define ADSP_EIMMEDIATE   0x00000010 /* Operation requests immediate
+				      * intervention to complete.
+				      */
+#define ADSP_ENOTIMPL     0x00000011 /* Operation is not implemented. */
+#define ADSP_ENEEDMORE    0x00000012 /* Operation needs more data or resources*/
+
+/* SRS TRUMEDIA GUIDS */
+#define SRS_TRUMEDIA_TOPOLOGY_ID    0x00010D90
+#define SRS_TRUMEDIA_MODULE_ID      0x10005010
+#define SRS_TRUMEDIA_PARAMS         0x10005011
+#define SRS_TRUMEDIA_PARAMS_WOWHD   0x10005012
+#define SRS_TRUMEDIA_PARAMS_CSHP    0x10005013
+#define SRS_TRUMEDIA_PARAMS_HPF     0x10005014
+#define SRS_TRUMEDIA_PARAMS_PEQ     0x10005015
+#define SRS_TRUMEDIA_PARAMS_HL      0x10005016
+
+/* SRS STUDIO SOUND 3D GUIDS */
+#define SRS_SS3D_TOPOLOGY_ID        0x00010720
+#define SRS_SS3D_MODULE_ID          0x10005020
+#define SRS_SS3D_PARAMS             0x10005021
+#define SRS_SS3D_PARAMS_CTRL        0x10005022
+#define SRS_SS3D_PARAMS_FILTER      0x10005023
+
+/* SRS ALSA CMD MASKS */
+#define SRS_CMD_UPLOAD              0x7FFF0000
+#define SRS_PARAM_INDEX_MASK        0x80000000
+#define SRS_PARAM_OFFSET_MASK       0x3FFF0000
+#define SRS_PARAM_VALUE_MASK        0x0000FFFF
+
+/* SRS TRUMEDIA start */
+#define SRS_ID_GLOBAL               0x00000001
+#define SRS_ID_WOWHD                0x00000002
+#define SRS_ID_CSHP                 0x00000003
+#define SRS_ID_HPF                  0x00000004
+#define SRS_ID_PEQ                  0x00000005
+#define SRS_ID_HL                   0x00000006
+
+struct srs_trumedia_params_GLOBAL {
+	uint8_t                  v1;
+	uint8_t                  v2;
+	uint8_t                  v3;
+	uint8_t                  v4;
+	uint8_t                  v5;
+	uint8_t                  v6;
+	uint8_t                  v7;
+	uint8_t                  v8;
+} __packed;
+
+struct srs_trumedia_params_WOWHD {
+	uint32_t				v1;
+	uint16_t				v2;
+	uint16_t				v3;
+	uint16_t				v4;
+	uint16_t				v5;
+	uint16_t				v6;
+	uint16_t				v7;
+	uint16_t				v8;
+	uint16_t				v____A1;
+	uint32_t				v9;
+	uint16_t				v10;
+	uint16_t				v11;
+	uint32_t				v12[16];
+} __packed;
+
+struct srs_trumedia_params_CSHP {
+	uint32_t				v1;
+	uint16_t				v2;
+	uint16_t				v3;
+	uint16_t				v4;
+	uint16_t				v5;
+	uint16_t				v6;
+	uint16_t				v____A1;
+	uint32_t				v7;
+	uint16_t				v8;
+	uint16_t				v9;
+	uint32_t				v10[16];
+} __packed;
+
+struct srs_trumedia_params_HPF {
+	uint32_t				v1;
+	uint32_t				v2[26];
+} __packed;
+
+struct srs_trumedia_params_PEQ {
+	uint32_t				v1;
+	uint16_t				v2;
+	uint16_t				v3;
+	uint16_t				v4;
+	uint16_t				v____A1;
+	uint32_t				v5[26];
+	uint32_t				v6[26];
+} __packed;
+
+struct srs_trumedia_params_HL {
+	uint16_t				v1;
+	uint16_t				v2;
+	uint16_t				v3;
+	uint16_t				v____A1;
+	int32_t					v4;
+	uint32_t				v5;
+	uint16_t				v6;
+	uint16_t				v____A2;
+	uint32_t				v7;
+} __packed;
+
+struct srs_trumedia_params {
+	struct srs_trumedia_params_GLOBAL	global;
+	struct srs_trumedia_params_WOWHD	wowhd;
+	struct srs_trumedia_params_CSHP		cshp;
+	struct srs_trumedia_params_HPF		hpf;
+	struct srs_trumedia_params_PEQ		peq;
+	struct srs_trumedia_params_HL		hl;
+} __packed;
+
+int srs_trumedia_open(int port_id, int srs_tech_id, void *srs_params);
+/* SRS TruMedia end */
+
+/* SRS Studio Sound 3D start */
+#define SRS_ID_SS3D_GLOBAL	0x00000001
+#define SRS_ID_SS3D_CTRL	0x00000002
+#define SRS_ID_SS3D_FILTER	0x00000003
+
+struct srs_SS3D_params_GLOBAL {
+	uint8_t                  v1;
+	uint8_t                  v2;
+	uint8_t                  v3;
+	uint8_t                  v4;
+	uint8_t                  v5;
+	uint8_t                  v6;
+	uint8_t                  v7;
+	uint8_t                  v8;
+} __packed;
+
+struct srs_SS3D_ctrl_params {
+	uint8_t				v[236];
+} __packed;
+
+struct srs_SS3D_filter_params {
+	uint8_t				v[28 + 2752];
+} __packed;
+
+struct srs_SS3D_params {
+	struct srs_SS3D_params_GLOBAL   global;
+	struct srs_SS3D_ctrl_params     ss3d;
+	struct srs_SS3D_filter_params   ss3d_f;
+} __packed;
+
+int srs_ss3d_open(int port_id, int srs_tech_id, void *srs_params);
+/* SRS Studio Sound 3D end */
+#endif /*_APR_AUDIO_H_*/
diff --git a/include/sound/audio_cal_utils.h b/include/sound/audio_cal_utils.h
new file mode 100644
index 0000000..b28b3bd
--- /dev/null
+++ b/include/sound/audio_cal_utils.h
@@ -0,0 +1,102 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _AUDIO_CAL_UTILS_H
+#define _AUDIO_CAL_UTILS_H
+
+#include <linux/msm_ion.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/msm_audio_calibration.h>
+#include "audio_calibration.h"
+
+struct cal_data {
+	size_t		size;
+	void		*kvaddr;
+	phys_addr_t	paddr;
+};
+
+struct mem_map_data {
+	size_t			map_size;
+	int32_t			q6map_handle;
+	int32_t			ion_map_handle;
+	struct ion_client	*ion_client;
+	struct ion_handle	*ion_handle;
+};
+
+struct cal_block_data {
+	size_t			client_info_size;
+	void			*client_info;
+	void			*cal_info;
+	struct list_head	list;
+	struct cal_data		cal_data;
+	struct mem_map_data	map_data;
+	int32_t			buffer_number;
+};
+
+struct cal_util_callbacks {
+	int (*map_cal)
+		(int32_t cal_type, struct cal_block_data *cal_block);
+	int (*unmap_cal)
+		(int32_t cal_type, struct cal_block_data *cal_block);
+	bool (*match_block)
+		(struct cal_block_data *cal_block, void *user_data);
+};
+
+struct cal_type_info {
+	struct audio_cal_reg		reg;
+	struct cal_util_callbacks	cal_util_callbacks;
+};
+
+struct cal_type_data {
+	struct cal_type_info		info;
+	struct mutex			lock;
+	struct list_head		cal_blocks;
+};
+
+
+/* to register & degregister with cal util driver */
+int cal_utils_create_cal_types(int num_cal_types,
+			struct cal_type_data **cal_type,
+			struct cal_type_info *info);
+void cal_utils_destroy_cal_types(int num_cal_types,
+			struct cal_type_data **cal_type);
+
+/* common functions for callbacks */
+int cal_utils_alloc_cal(size_t data_size, void *data,
+			struct cal_type_data *cal_type,
+			size_t client_info_size, void *client_info);
+int cal_utils_dealloc_cal(size_t data_size, void *data,
+			struct cal_type_data *cal_type);
+int cal_utils_set_cal(size_t data_size, void *data,
+			struct cal_type_data *cal_type,
+			size_t client_info_size, void *client_info);
+
+/* use for SSR */
+void cal_utils_clear_cal_block_q6maps(int num_cal_types,
+					struct cal_type_data **cal_type);
+
+
+/* common matching functions used to add blocks */
+bool cal_utils_match_buf_num(struct cal_block_data *cal_block,
+					void *user_data);
+
+/* common matching functions to find cal blocks */
+struct cal_block_data *cal_utils_get_only_cal_block(
+			struct cal_type_data *cal_type);
+
+/* Size of calibration specific data */
+size_t get_cal_info_size(int32_t cal_type);
+size_t get_user_cal_type_size(int32_t cal_type);
+
+/* Version of the cal type*/
+int32_t cal_utils_get_cal_type_version(void *cal_type_data);
+#endif
diff --git a/include/sound/audio_calibration.h b/include/sound/audio_calibration.h
new file mode 100644
index 0000000..5decff9
--- /dev/null
+++ b/include/sound/audio_calibration.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _AUDIO_CALIBRATION_H
+#define _AUDIO_CALIBRATION_H
+
+#include <linux/msm_audio_calibration.h>
+
+/* Used by driver in buffer_number field to notify client
+ * To update all blocks, for example: freeing all memory
+ */
+#define ALL_CAL_BLOCKS		-1
+
+
+struct audio_cal_callbacks {
+	int (*alloc)(int32_t cal_type, size_t data_size, void *data);
+	int (*dealloc)(int32_t cal_type, size_t data_size, void *data);
+	int (*pre_cal)(int32_t cal_type, size_t data_size, void *data);
+	int (*set_cal)(int32_t cal_type, size_t data_size, void *data);
+	int (*get_cal)(int32_t cal_type, size_t data_size, void *data);
+	int (*post_cal)(int32_t cal_type, size_t data_size, void *data);
+};
+
+struct audio_cal_reg {
+	int32_t				cal_type;
+	struct audio_cal_callbacks	callbacks;
+};
+
+int audio_cal_register(int num_cal_types, struct audio_cal_reg *reg_data);
+int audio_cal_deregister(int num_cal_types, struct audio_cal_reg *reg_data);
+
+#endif
diff --git a/include/sound/audio_slimslave.h b/include/sound/audio_slimslave.h
new file mode 100644
index 0000000..316a557
--- /dev/null
+++ b/include/sound/audio_slimslave.h
@@ -0,0 +1,18 @@
+#ifndef __AUDIO_SLIMSLAVE_H__
+#define __AUDIO_SLIMSLAVE_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define AUDIO_SLIMSLAVE_IOCTL_NAME "audio_slimslave"
+#define AUDIO_SLIMSLAVE_MAGIC 'S'
+
+#define AUDIO_SLIMSLAVE_IOCTL_UNVOTE	_IO(AUDIO_SLIMSLAVE_MAGIC, 0x00)
+#define AUDIO_SLIMSLAVE_IOCTL_VOTE	_IO(AUDIO_SLIMSLAVE_MAGIC, 0x01)
+
+enum {
+	AUDIO_SLIMSLAVE_UNVOTE,
+	AUDIO_SLIMSLAVE_VOTE
+};
+
+#endif
diff --git a/include/sound/cpe_cmi.h b/include/sound/cpe_cmi.h
new file mode 100644
index 0000000..cbf83e7
--- /dev/null
+++ b/include/sound/cpe_cmi.h
@@ -0,0 +1,492 @@
+/*
+ * Copyright (c) 2014-2016, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CPE_CMI_H__
+#define __CPE_CMI_H__
+
+#include <linux/types.h>
+
+#define CPE_AFE_PORT_1_TX 1
+#define CPE_AFE_PORT_3_TX 3
+#define CPE_AFE_PORT_ID_2_OUT 0x02
+#define CMI_INBAND_MESSAGE_SIZE 127
+
+/*
+ * Multiple mad types can be supported at once.
+ * these values can be OR'ed to form the set of
+ * supported mad types
+ */
+#define MAD_TYPE_AUDIO (1 << 0)
+#define MAD_TYPE_BEACON (1 << 1)
+#define MAD_TYPE_ULTRASND (1 << 2)
+
+/* Core service command opcodes */
+#define CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC	(0x3001)
+#define CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC	(0x3002)
+#define CPE_CORE_SVC_CMD_SHARED_MEM_DEALLOC	(0x3003)
+#define CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ	(0x3004)
+#define CPE_CORE_SVC_EVENT_SYSTEM_BOOT		(0x3005)
+/* core service command opcodes for WCD9335 */
+#define CPE_CORE_SVC_CMD_CFG_CLK_PLAN		(0x3006)
+#define CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST	(0x3007)
+
+#define CPE_BOOT_SUCCESS 0x00
+#define CPE_BOOT_FAILED 0x01
+
+#define CPE_CORE_VERSION_SYSTEM_BOOT_EVENT 0x01
+
+/* LSM Service command opcodes */
+#define CPE_LSM_SESSION_CMD_OPEN_TX		(0x2000)
+#define CPE_LSM_SESSION_CMD_SET_PARAMS		(0x2001)
+#define CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL (0x2002)
+#define CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL (0x2003)
+#define CPE_LSM_SESSION_CMD_START		(0x2004)
+#define CPE_LSM_SESSION_CMD_STOP		(0x2005)
+#define CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2 (0x2006)
+#define CPE_LSM_SESSION_CMD_CLOSE_TX		(0x2007)
+#define CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC	(0x2008)
+#define CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC (0x2009)
+#define CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC	(0x200A)
+#define CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG (0x200f)
+#define CPE_LSM_SESSION_CMD_OPEN_TX_V2		(0x200D)
+#define CPE_LSM_SESSION_CMD_SET_PARAMS_V2	(0x200E)
+
+/* LSM Service module and param IDs */
+#define CPE_LSM_MODULE_ID_VOICE_WAKEUP		(0x00012C00)
+#define CPE_LSM_MODULE_ID_VOICE_WAKEUP_V2	(0x00012C0D)
+#define CPE_LSM_MODULE_FRAMEWORK		(0x00012C0E)
+
+#define CPE_LSM_PARAM_ID_ENDPOINT_DETECT_THRESHOLD (0x00012C01)
+#define CPE_LSM_PARAM_ID_OPERATION_MODE		(0x00012C02)
+#define CPE_LSM_PARAM_ID_GAIN			(0x00012C03)
+#define CPE_LSM_PARAM_ID_CONNECT_TO_PORT	(0x00012C04)
+#define CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS	(0x00012C07)
+
+/* LSM LAB command opcodes */
+#define CPE_LSM_SESSION_CMD_EOB		0x0000200B
+#define CPE_LSM_MODULE_ID_LAB		0x00012C08
+/* used for enable/disable lab*/
+#define CPE_LSM_PARAM_ID_LAB_ENABLE	0x00012C09
+/* used for T in LAB config DSP internal buffer*/
+#define CPE_LSM_PARAM_ID_LAB_CONFIG	0x00012C0A
+#define CPE_LSM_PARAM_ID_REGISTER_SOUND_MODEL	(0x00012C14)
+#define CPE_LSM_PARAM_ID_DEREGISTER_SOUND_MODEL	(0x00012C15)
+#define CPE_LSM_PARAM_ID_MEDIA_FMT		(0x00012C1E)
+
+/* AFE Service command opcodes */
+#define CPE_AFE_PORT_CMD_START			(0x1001)
+#define CPE_AFE_PORT_CMD_STOP			(0x1002)
+#define CPE_AFE_PORT_CMD_SUSPEND		(0x1003)
+#define CPE_AFE_PORT_CMD_RESUME			(0x1004)
+#define CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC	(0x1005)
+#define CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC	(0x1006)
+#define CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC	(0x1007)
+#define CPE_AFE_PORT_CMD_GENERIC_CONFIG		(0x1008)
+#define CPE_AFE_SVC_CMD_LAB_MODE		(0x1009)
+
+/* AFE Service module and param IDs */
+#define CPE_AFE_CMD_SET_PARAM			(0x1000)
+#define CPE_AFE_MODULE_ID_SW_MAD		(0x0001022D)
+#define CPE_AFE_PARAM_ID_SW_MAD_CFG		(0x0001022E)
+#define CPE_AFE_PARAM_ID_SVM_MODEL		(0x0001022F)
+
+#define CPE_AFE_MODULE_HW_MAD			(0x00010230)
+#define CPE_AFE_PARAM_ID_HW_MAD_CTL		(0x00010232)
+#define CPE_AFE_PARAM_ID_HW_MAD_CFG		(0x00010231)
+
+#define CPE_AFE_MODULE_AUDIO_DEV_INTERFACE	(0x0001020C)
+#define CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG	(0x00010253)
+
+#define CPE_CMI_BASIC_RSP_OPCODE	(0x0001)
+#define CPE_HDR_MAX_PLD_SIZE	(0x7F)
+
+#define CMI_OBM_FLAG_IN_BAND	0
+#define CMI_OBM_FLAG_OUT_BAND	1
+
+#define CMI_SHMEM_ALLOC_FAILED 0xff
+
+/*
+ * Future Service ID's can be added one line
+ * before the CMI_CPE_SERVICE_ID_MAX
+ */
+enum {
+	CMI_CPE_SERVICE_ID_MIN = 0,
+	CMI_CPE_CORE_SERVICE_ID,
+	CMI_CPE_AFE_SERVICE_ID,
+	CMI_CPE_LSM_SERVICE_ID,
+	CMI_CPE_SERVICE_ID_MAX,
+};
+
+#define CPE_LSM_SESSION_ID_MAX 2
+
+#define IS_VALID_SESSION_ID(s_id) \
+	(s_id <= CPE_LSM_SESSION_ID_MAX)
+
+#define IS_VALID_SERVICE_ID(s_id) \
+	(s_id > CMI_CPE_SERVICE_ID_MIN && \
+	 s_id < CMI_CPE_SERVICE_ID_MAX)
+
+#define IS_VALID_PLD_SIZE(p_size) \
+	(p_size <= CPE_HDR_MAX_PLD_SIZE)
+
+#define CMI_HDR_SET_OPCODE(hdr, cmd) (hdr->opcode = cmd)
+
+
+#define CMI_HDR_SET(hdr_info, mask, shift, value) \
+		(hdr_info = (((hdr_info) & ~(mask)) | \
+			((value << shift) & mask)))
+
+#define SVC_ID_SHIFT 4
+#define SVC_ID_MASK (0x07 << SVC_ID_SHIFT)
+
+#define SESSION_ID_SHIFT 0
+#define SESSION_ID_MASK (0x0F << SESSION_ID_SHIFT)
+
+#define PAYLD_SIZE_SHIFT 0
+#define PAYLD_SIZE_MASK (0x7F << PAYLD_SIZE_SHIFT)
+
+#define OBM_FLAG_SHIFT 7
+#define OBM_FLAG_MASK (1 << OBM_FLAG_SHIFT)
+
+#define VERSION_SHIFT 7
+#define VERSION_MASK (1 << VERSION_SHIFT)
+
+#define CMI_HDR_SET_SERVICE(hdr, s_id) \
+		CMI_HDR_SET(hdr->hdr_info, SVC_ID_MASK,\
+			    SVC_ID_SHIFT, s_id)
+#define CMI_HDR_GET_SERVICE(hdr) \
+		((hdr->hdr_info >> SVC_ID_SHIFT) & \
+			(SVC_ID_MASK >> SVC_ID_SHIFT))
+
+
+#define CMI_HDR_SET_SESSION(hdr, s_id) \
+		CMI_HDR_SET(hdr->hdr_info, SESSION_ID_MASK,\
+			    SESSION_ID_SHIFT, s_id)
+
+#define CMI_HDR_GET_SESSION_ID(hdr) \
+		((hdr->hdr_info >> SESSION_ID_SHIFT) & \
+			 (SESSION_ID_MASK >> SESSION_ID_SHIFT))
+
+#define CMI_GET_HEADER(msg)	((struct cmi_hdr *)(msg))
+#define CMI_GET_PAYLOAD(msg)	((void *)(CMI_GET_HEADER(msg) + 1))
+#define CMI_GET_OPCODE(msg)	(CMI_GET_HEADER(msg)->opcode)
+
+#define CMI_HDR_SET_VERSION(hdr, ver) \
+		CMI_HDR_SET(hdr->hdr_info, VERSION_MASK, \
+				VERSION_SHIFT, ver)
+
+#define CMI_HDR_SET_PAYLOAD_SIZE(hdr, p_size) \
+		CMI_HDR_SET(hdr->pld_info, PAYLD_SIZE_MASK, \
+			    PAYLD_SIZE_SHIFT, p_size)
+
+#define CMI_HDR_GET_PAYLOAD_SIZE(hdr) \
+		((hdr->pld_info >> PAYLD_SIZE_SHIFT) & \
+			(PAYLD_SIZE_MASK >> PAYLD_SIZE_SHIFT))
+
+#define CMI_HDR_SET_OBM(hdr, obm_flag) \
+		CMI_HDR_SET(hdr->pld_info, OBM_FLAG_MASK, \
+			    OBM_FLAG_SHIFT, obm_flag)
+
+#define CMI_HDR_GET_OBM_FLAG(hdr) \
+	((hdr->pld_info >> OBM_FLAG_SHIFT) & \
+		(OBM_FLAG_MASK >> OBM_FLAG_SHIFT))
+
+struct cmi_hdr {
+	/*
+	 * bits 0:3 is session id
+	 * bits 4:6 is service id
+	 * bit 7 is the version flag
+	 */
+	u8 hdr_info;
+
+	/*
+	 * bits 0:6 is payload size in case of in-band message
+	 * bits 0:6 is size (OBM message size)
+	 * bit 7 is the OBM flag
+	 */
+	u8 pld_info;
+
+	/* 16 bit command opcode */
+	u16 opcode;
+} __packed;
+
+union cpe_addr {
+	u64 msw_lsw;
+	void *kvaddr;
+} __packed;
+
+struct cmi_obm {
+	u32 version;
+	u32 size;
+	union cpe_addr data_ptr;
+	u32 mem_handle;
+} __packed;
+
+struct cmi_obm_msg {
+	struct cmi_hdr hdr;
+	struct cmi_obm pld;
+} __packed;
+
+struct cmi_core_svc_event_system_boot {
+	u8 status;
+	u8 version;
+	u16 sfr_buff_size;
+	u32 sfr_buff_address;
+} __packed;
+
+struct cmi_core_svc_cmd_shared_mem_alloc {
+	u32 size;
+} __packed;
+
+struct cmi_core_svc_cmdrsp_shared_mem_alloc {
+	u32 addr;
+} __packed;
+
+struct cmi_core_svc_cmd_clk_freq_request {
+	u32 clk_freq;
+} __packed;
+
+struct cmi_msg_transport {
+	u32 size;
+	u32 addr;
+} __packed;
+
+struct cmi_basic_rsp_result {
+	u8 status;
+} __packed;
+
+struct cpe_lsm_cmd_open_tx {
+	struct cmi_hdr	hdr;
+	u16 app_id;
+	u16 reserved;
+	u32 sampling_rate;
+} __packed;
+
+struct cpe_lsm_cmd_open_tx_v2 {
+	struct cmi_hdr hdr;
+	u32 topology_id;
+} __packed;
+
+struct cpe_cmd_shmem_alloc {
+	struct cmi_hdr hdr;
+	u32 size;
+} __packed;
+
+struct cpe_cmdrsp_shmem_alloc {
+	struct cmi_hdr hdr;
+	u32 addr;
+} __packed;
+
+struct cpe_cmd_shmem_dealloc {
+	struct cmi_hdr	hdr;
+	u32 addr;
+} __packed;
+
+struct cpe_lsm_event_detect_v2 {
+	struct cmi_hdr	hdr;
+	u8 detection_status;
+	u8 size;
+	u8 payload[0];
+} __packed;
+
+struct cpe_lsm_psize_res {
+	u16 param_size;
+	u16 reserved;
+} __packed;
+
+union cpe_lsm_param_size {
+	u32 param_size;
+	struct cpe_lsm_psize_res sr;
+} __packed;
+
+struct cpe_param_data {
+	u32 module_id;
+	u32 param_id;
+	union cpe_lsm_param_size p_size;
+} __packed;
+
+struct cpe_lsm_param_epd_thres {
+	struct cmi_hdr hdr;
+	struct cpe_param_data param;
+	u32 minor_version;
+	u32 epd_begin;
+	u32 epd_end;
+} __packed;
+
+struct cpe_lsm_param_gain {
+	struct cmi_hdr hdr;
+	struct cpe_param_data param;
+	u32 minor_version;
+	u16 gain;
+	u16 reserved;
+} __packed;
+
+struct cpe_afe_hw_mad_ctrl {
+	struct cpe_param_data param;
+	u32 minor_version;
+	u16 mad_type;
+	u16 mad_enable;
+} __packed;
+
+struct cpe_afe_port_cfg {
+	struct cpe_param_data param;
+	u32 minor_version;
+	u16 bit_width;
+	u16 num_channels;
+	u32 sample_rate;
+} __packed;
+
+struct cpe_afe_cmd_port_cfg {
+	struct cmi_hdr hdr;
+	u8 bit_width;
+	u8 num_channels;
+	u16 buffer_size;
+	u32 sample_rate;
+} __packed;
+
+struct cpe_afe_params {
+	struct cmi_hdr hdr;
+	struct cpe_afe_hw_mad_ctrl hw_mad_ctrl;
+	struct cpe_afe_port_cfg port_cfg;
+} __packed;
+
+struct cpe_afe_svc_cmd_mode {
+	struct cmi_hdr hdr;
+	u8 mode;
+} __packed;
+
+struct cpe_lsm_param_opmode {
+	struct cmi_hdr hdr;
+	struct cpe_param_data param;
+	u32 minor_version;
+	u16 mode;
+	u16 reserved;
+} __packed;
+
+struct cpe_lsm_param_connectport {
+	struct cmi_hdr hdr;
+	struct cpe_param_data param;
+	u32 minor_version;
+	u16 afe_port_id;
+	u16 reserved;
+} __packed;
+
+/*
+ * This cannot be sent to CPE as is,
+ * need to append the conf_levels dynamically
+ */
+struct cpe_lsm_conf_level {
+	struct cmi_hdr hdr;
+	struct cpe_param_data param;
+	u8 num_active_models;
+} __packed;
+
+struct cpe_lsm_output_format_cfg {
+	struct cmi_hdr hdr;
+	u8 format;
+	u8 packing;
+	u8 data_path_events;
+} __packed;
+
+struct cpe_lsm_lab_enable {
+	struct cpe_param_data param;
+	u16 enable;
+	u16 reserved;
+} __packed;
+
+struct cpe_lsm_control_lab {
+	struct cmi_hdr hdr;
+	struct cpe_lsm_lab_enable lab_enable;
+} __packed;
+
+struct cpe_lsm_lab_config {
+	struct cpe_param_data param;
+	u32 minor_ver;
+	u32 latency;
+} __packed;
+
+struct cpe_lsm_lab_latency_config {
+	struct cmi_hdr hdr;
+	struct cpe_lsm_lab_config latency_cfg;
+} __packed;
+
+struct cpe_lsm_media_fmt_param {
+	struct cmi_hdr hdr;
+	struct cpe_param_data param;
+	u32 minor_version;
+	u32 sample_rate;
+	u16 num_channels;
+	u16 bit_width;
+} __packed;
+
+
+#define CPE_PARAM_LSM_LAB_LATENCY_SIZE (\
+				sizeof(struct cpe_lsm_lab_latency_config) - \
+				sizeof(struct cmi_hdr))
+#define PARAM_SIZE_LSM_LATENCY_SIZE (\
+					sizeof(struct cpe_lsm_lab_config) - \
+					sizeof(struct cpe_param_data))
+#define CPE_PARAM_SIZE_LSM_LAB_CONTROL (\
+				sizeof(struct cpe_lsm_control_lab) - \
+				sizeof(struct cmi_hdr))
+#define PARAM_SIZE_LSM_CONTROL_SIZE (sizeof(struct cpe_lsm_lab_enable) - \
+					sizeof(struct cpe_param_data))
+#define PARAM_SIZE_AFE_HW_MAD_CTRL (sizeof(struct cpe_afe_hw_mad_ctrl) - \
+				sizeof(struct cpe_param_data))
+#define PARAM_SIZE_AFE_PORT_CFG (sizeof(struct cpe_afe_port_cfg) - \
+				 sizeof(struct cpe_param_data))
+#define CPE_AFE_PARAM_PAYLOAD_SIZE (sizeof(struct cpe_afe_params) - \
+				sizeof(struct cmi_hdr))
+
+#define OPEN_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx) - \
+			       sizeof(struct cmi_hdr))
+#define OPEN_V2_CMD_PAYLOAD_SIZE (sizeof(struct cpe_lsm_cmd_open_tx_v2) - \
+			       sizeof(struct cmi_hdr))
+#define SHMEM_ALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_alloc) - \
+				      sizeof(struct cmi_hdr))
+
+#define SHMEM_DEALLOC_CMD_PLD_SIZE (sizeof(struct cpe_cmd_shmem_dealloc) - \
+				      sizeof(struct cmi_hdr))
+#define OUT_FMT_CFG_CMD_PAYLOAD_SIZE ( \
+		sizeof(struct cpe_lsm_output_format_cfg) - \
+		sizeof(struct cmi_hdr))
+
+#define CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE \
+		(sizeof(struct cpe_afe_cmd_port_cfg) - \
+		 sizeof(struct cmi_hdr))
+
+#define CPE_AFE_CMD_MODE_PAYLOAD_SIZE \
+		(sizeof(struct cpe_afe_svc_cmd_mode) - \
+		 sizeof(struct cmi_hdr))
+#define CPE_CMD_EPD_THRES_PLD_SIZE (sizeof(struct cpe_lsm_param_epd_thres) - \
+				    sizeof(struct cmi_hdr))
+#define CPE_EPD_THRES_PARAM_SIZE ((CPE_CMD_EPD_THRES_PLD_SIZE) - \
+				  sizeof(struct cpe_param_data))
+#define CPE_CMD_OPMODE_PLD_SIZE (sizeof(struct cpe_lsm_param_opmode) - \
+				 sizeof(struct cmi_hdr))
+#define CPE_OPMODE_PARAM_SIZE ((CPE_CMD_OPMODE_PLD_SIZE) -\
+			       sizeof(struct cpe_param_data))
+#define CPE_CMD_CONNECTPORT_PLD_SIZE \
+	(sizeof(struct cpe_lsm_param_connectport) - \
+	 sizeof(struct cmi_hdr))
+#define CPE_CONNECTPORT_PARAM_SIZE ((CPE_CMD_CONNECTPORT_PLD_SIZE) - \
+				    sizeof(struct cpe_param_data))
+#define CPE_CMD_GAIN_PLD_SIZE (sizeof(struct cpe_lsm_param_gain) - \
+			       sizeof(struct cmi_hdr))
+#define CPE_GAIN_PARAM_SIZE ((CPE_CMD_GAIN_PLD_SIZE) - \
+			     sizeof(struct cpe_param_data))
+#define CPE_MEDIA_FMT_PLD_SIZE (sizeof(struct cpe_lsm_media_fmt_param) - \
+				sizeof(struct cmi_hdr))
+#define CPE_MEDIA_FMT_PARAM_SIZE ((CPE_MEDIA_FMT_PLD_SIZE) - \
+				  sizeof(struct cpe_param_data))
+#endif /* __CPE_CMI_H__ */
diff --git a/include/sound/cpe_core.h b/include/sound/cpe_core.h
new file mode 100644
index 0000000..f4af562
--- /dev/null
+++ b/include/sound/cpe_core.h
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2013-2017, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CPE_CORE_H__
+#define __CPE_CORE_H__
+
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/dma-mapping.h>
+#include <sound/lsm_params.h>
+
+enum {
+	CMD_INIT_STATE = 0,
+	CMD_SENT,
+	CMD_RESP_RCVD,
+};
+
+enum wcd_cpe_event {
+	WCD_CPE_PRE_ENABLE = 1,
+	WCD_CPE_POST_ENABLE,
+	WCD_CPE_PRE_DISABLE,
+	WCD_CPE_POST_DISABLE,
+};
+
+struct wcd_cpe_afe_port_cfg {
+	u8 port_id;
+	u16 bit_width;
+	u16 num_channels;
+	u32 sample_rate;
+};
+
+struct lsm_out_fmt_cfg {
+	u8 format;
+	u8 pack_mode;
+	u8 data_path_events;
+	u8 transfer_mode;
+};
+
+struct lsm_hw_params {
+	u32 sample_rate;
+	u16 num_chs;
+	u16 bit_width;
+};
+
+struct cpe_lsm_session {
+	/* sound model related */
+	void *snd_model_data;
+	u8 *conf_levels;
+	void *cmi_reg_handle;
+
+	/* Clients private data */
+	void *priv_d;
+
+	void (*event_cb)(void *priv_data,
+			  u8 detect_status,
+			  u8 size, u8 *payload);
+
+	struct completion cmd_comp;
+	struct wcd_cpe_afe_port_cfg afe_port_cfg;
+	struct wcd_cpe_afe_port_cfg afe_out_port_cfg;
+	struct mutex lsm_lock;
+
+	u32 snd_model_size;
+	u32 lsm_mem_handle;
+	u16 cmd_err_code;
+	u8 id;
+	u8 num_confidence_levels;
+	u16 afe_out_port_id;
+	struct task_struct *lsm_lab_thread;
+	bool started;
+
+	u32 lab_enable;
+	struct lsm_out_fmt_cfg out_fmt_cfg;
+
+	bool is_topology_used;
+};
+
+struct wcd_cpe_afe_ops {
+	int (*afe_set_params)(void *core_handle,
+			       struct wcd_cpe_afe_port_cfg *cfg,
+			       bool afe_mad_ctl);
+
+	int (*afe_port_start)(void *core_handle,
+			       struct wcd_cpe_afe_port_cfg *cfg);
+
+	int (*afe_port_stop)(void *core_handle,
+			       struct wcd_cpe_afe_port_cfg *cfg);
+
+	int (*afe_port_suspend)(void *core_handle,
+			       struct wcd_cpe_afe_port_cfg *cfg);
+
+	int (*afe_port_resume)(void *core_handle,
+			       struct wcd_cpe_afe_port_cfg *cfg);
+
+	int (*afe_port_cmd_cfg)(void *core_handle,
+				struct wcd_cpe_afe_port_cfg *cfg);
+};
+
+struct wcd_cpe_lsm_ops {
+
+	struct cpe_lsm_session *(*lsm_alloc_session)
+			(void *core_handle, void *lsm_priv_d,
+			 void (*event_cb)(void *priv_data,
+					   u8 detect_status,
+					   u8 size, u8 *payload));
+
+	int (*lsm_dealloc_session)
+		(void *core_handle, struct cpe_lsm_session *);
+
+	int (*lsm_open_tx)(void *core_handle,
+			    struct cpe_lsm_session *, u16, u16);
+
+	int (*lsm_close_tx)(void *core_handle,
+			     struct cpe_lsm_session *);
+
+	int (*lsm_shmem_alloc)(void *core_handle,
+				struct cpe_lsm_session *, u32 size);
+
+	int (*lsm_shmem_dealloc)(void *core_handle,
+				  struct cpe_lsm_session *);
+
+	int (*lsm_register_snd_model)(void *core_handle,
+				       struct cpe_lsm_session *,
+				       enum lsm_detection_mode, bool);
+
+	int (*lsm_deregister_snd_model)(void *core_handle,
+					 struct cpe_lsm_session *);
+
+	int (*lsm_get_afe_out_port_id)(void *core_handle,
+			       struct cpe_lsm_session *session);
+
+	int (*lsm_start)(void *core_handle,
+			  struct cpe_lsm_session *);
+
+	int (*lsm_stop)(void *core_handle,
+			 struct cpe_lsm_session *);
+
+	int (*lsm_lab_control)(void *core_handle,
+			       struct cpe_lsm_session *session,
+			       bool enable);
+
+	int (*lab_ch_setup)(void *core_handle,
+				   struct cpe_lsm_session *session,
+				   enum wcd_cpe_event event);
+
+	int (*lsm_set_data)(void *core_handle,
+			struct cpe_lsm_session *session,
+			enum lsm_detection_mode detect_mode,
+			bool detect_failure);
+	int (*lsm_set_fmt_cfg)(void *core_handle,
+			struct cpe_lsm_session *session);
+	int (*lsm_set_one_param)(void *core_handle,
+			struct cpe_lsm_session *session,
+			struct lsm_params_info *p_info,
+			void *data, enum LSM_PARAM_TYPE param_type);
+	void (*lsm_get_snd_model_offset)
+		(void *core_handle, struct cpe_lsm_session *,
+		 size_t *offset);
+	int (*lsm_set_media_fmt_params)(void *core_handle,
+				       struct cpe_lsm_session *session,
+				       struct lsm_hw_params *param);
+	int (*lsm_set_port)(void *core_handle,
+			    struct cpe_lsm_session *session, void *data);
+};
+
+int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops);
+int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops);
+void *wcd_cpe_get_core_handle(struct snd_soc_codec *codec);
+#endif
diff --git a/include/sound/cpe_err.h b/include/sound/cpe_err.h
new file mode 100644
index 0000000..d355803
--- /dev/null
+++ b/include/sound/cpe_err.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CPE_ERR__
+#define __CPE_ERR__
+
+#include <linux/errno.h>
+
+/* ERROR CODES */
+/* Success. The operation completed with no errors. */
+#define CPE_EOK          0x00000000
+/* General failure. */
+#define CPE_EFAILED      0x00000001
+/* Bad operation parameter. */
+#define CPE_EBADPARAM    0x00000002
+/* Unsupported routine or operation. */
+#define CPE_EUNSUPPORTED 0x00000003
+/* Unsupported version. */
+#define CPE_EVERSION     0x00000004
+/* Unexpected problem encountered. */
+#define CPE_EUNEXPECTED  0x00000005
+/* Unhandled problem occurred. */
+#define CPE_EPANIC       0x00000006
+/* Unable to allocate resource. */
+#define CPE_ENORESOURCE  0x00000007
+/* Invalid handle. */
+#define CPE_EHANDLE      0x00000008
+/* Operation is already processed. */
+#define CPE_EALREADY     0x00000009
+/* Operation is not ready to be processed. */
+#define CPE_ENOTREADY    0x0000000A
+/* Operation is pending completion. */
+#define CPE_EPENDING     0x0000000B
+/* Operation could not be accepted or processed. */
+#define CPE_EBUSY        0x0000000C
+/* Operation aborted due to an error. */
+#define CPE_EABORTED     0x0000000D
+/* Operation preempted by a higher priority. */
+#define CPE_EPREEMPTED   0x0000000E
+/* Operation requests intervention to complete. */
+#define CPE_ECONTINUE    0x0000000F
+/* Operation requests immediate intervention to complete. */
+#define CPE_EIMMEDIATE   0x00000010
+/* Operation is not implemented. */
+#define CPE_ENOTIMPL     0x00000011
+/* Operation needs more data or resources. */
+#define CPE_ENEEDMORE    0x00000012
+/* Operation does not have memory. */
+#define CPE_ENOMEMORY    0x00000014
+/* Item does not exist. */
+#define CPE_ENOTEXIST    0x00000015
+/* Operation is finished. */
+#define CPE_ETERMINATED  0x00000016
+/* Max count for adsp error code sent to HLOS*/
+#define CPE_ERR_MAX      (CPE_ETERMINATED + 1)
+
+
+/* ERROR STRING */
+/* Success. The operation completed with no errors. */
+#define CPE_EOK_STR          "CPE_EOK"
+/* General failure. */
+#define CPE_EFAILED_STR      "CPE_EFAILED"
+/* Bad operation parameter. */
+#define CPE_EBADPARAM_STR    "CPE_EBADPARAM"
+/* Unsupported routine or operation. */
+#define CPE_EUNSUPPORTED_STR "CPE_EUNSUPPORTED"
+/* Unsupported version. */
+#define CPE_EVERSION_STR     "CPE_EVERSION"
+/* Unexpected problem encountered. */
+#define CPE_EUNEXPECTED_STR  "CPE_EUNEXPECTED"
+/* Unhandled problem occurred. */
+#define CPE_EPANIC_STR       "CPE_EPANIC"
+/* Unable to allocate resource. */
+#define CPE_ENORESOURCE_STR  "CPE_ENORESOURCE"
+/* Invalid handle. */
+#define CPE_EHANDLE_STR      "CPE_EHANDLE"
+/* Operation is already processed. */
+#define CPE_EALREADY_STR     "CPE_EALREADY"
+/* Operation is not ready to be processed. */
+#define CPE_ENOTREADY_STR    "CPE_ENOTREADY"
+/* Operation is pending completion. */
+#define CPE_EPENDING_STR     "CPE_EPENDING"
+/* Operation could not be accepted or processed. */
+#define CPE_EBUSY_STR        "CPE_EBUSY"
+/* Operation aborted due to an error. */
+#define CPE_EABORTED_STR     "CPE_EABORTED"
+/* Operation preempted by a higher priority. */
+#define CPE_EPREEMPTED_STR   "CPE_EPREEMPTED"
+/* Operation requests intervention to complete. */
+#define CPE_ECONTINUE_STR    "CPE_ECONTINUE"
+/* Operation requests immediate intervention to complete. */
+#define CPE_EIMMEDIATE_STR   "CPE_EIMMEDIATE"
+/* Operation is not implemented. */
+#define CPE_ENOTIMPL_STR     "CPE_ENOTIMPL"
+/* Operation needs more data or resources. */
+#define CPE_ENEEDMORE_STR    "CPE_ENEEDMORE"
+/* Operation does not have memory. */
+#define CPE_ENOMEMORY_STR    "CPE_ENOMEMORY"
+/* Item does not exist. */
+#define CPE_ENOTEXIST_STR    "CPE_ENOTEXIST"
+/* Operation is finished. */
+#define CPE_ETERMINATED_STR  "CPE_ETERMINATED"
+/* Unexpected error code. */
+#define CPE_ERR_MAX_STR      "CPE_ERR_MAX"
+
+
+struct cpe_err_code {
+	int     lnx_err_code;
+	char    *cpe_err_str;
+};
+
+
+static struct cpe_err_code cpe_err_code_info[CPE_ERR_MAX+1] = {
+	{ 0, CPE_EOK_STR},
+	{ -ENOTRECOVERABLE, CPE_EFAILED_STR},
+	{ -EINVAL, CPE_EBADPARAM_STR},
+	{ -EOPNOTSUPP, CPE_EUNSUPPORTED_STR},
+	{ -ENOPROTOOPT, CPE_EVERSION_STR},
+	{ -ENOTRECOVERABLE, CPE_EUNEXPECTED_STR},
+	{ -ENOTRECOVERABLE, CPE_EPANIC_STR},
+	{ -ENOSPC, CPE_ENORESOURCE_STR},
+	{ -EBADR, CPE_EHANDLE_STR},
+	{ -EALREADY, CPE_EALREADY_STR},
+	{ -EPERM, CPE_ENOTREADY_STR},
+	{ -EINPROGRESS, CPE_EPENDING_STR},
+	{ -EBUSY, CPE_EBUSY_STR},
+	{ -ECANCELED, CPE_EABORTED_STR},
+	{ -EAGAIN, CPE_EPREEMPTED_STR},
+	{ -EAGAIN, CPE_ECONTINUE_STR},
+	{ -EAGAIN, CPE_EIMMEDIATE_STR},
+	{ -EAGAIN, CPE_ENOTIMPL_STR},
+	{ -ENODATA, CPE_ENEEDMORE_STR},
+	{ -EADV, CPE_ERR_MAX_STR},
+	{ -ENOMEM, CPE_ENOMEMORY_STR},
+	{ -ENODEV, CPE_ENOTEXIST_STR},
+	{ -EADV, CPE_ETERMINATED_STR},
+	{ -EADV, CPE_ERR_MAX_STR},
+};
+
+static inline int cpe_err_get_lnx_err_code(u32 cpe_error)
+{
+	if (cpe_error > CPE_ERR_MAX)
+		return cpe_err_code_info[CPE_ERR_MAX].lnx_err_code;
+	else
+		return cpe_err_code_info[cpe_error].lnx_err_code;
+}
+
+static inline char *cpe_err_get_err_str(u32 cpe_error)
+{
+	if (cpe_error > CPE_ERR_MAX)
+		return cpe_err_code_info[CPE_ERR_MAX].cpe_err_str;
+	else
+		return cpe_err_code_info[cpe_error].cpe_err_str;
+}
+
+#endif
diff --git a/include/sound/msm-audio-effects-q6-v2.h b/include/sound/msm-audio-effects-q6-v2.h
new file mode 100644
index 0000000..6bc2338
--- /dev/null
+++ b/include/sound/msm-audio-effects-q6-v2.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_AUDIO_EFFECTS_H
+#define _MSM_AUDIO_EFFECTS_H
+
+#include <sound/audio_effects.h>
+
+#define MAX_PP_PARAMS_SZ   128
+
+bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module,
+						int topology);
+
+int msm_audio_effects_enable_extn(struct audio_client *ac,
+			     struct msm_nt_eff_all_config *effects,
+			     bool flag);
+
+int msm_audio_effects_reverb_handler(struct audio_client *ac,
+				     struct reverb_params *reverb,
+				     long *values);
+
+int msm_audio_effects_bass_boost_handler(struct audio_client *ac,
+					struct bass_boost_params *bass_boost,
+					long *values);
+
+int msm_audio_effects_pbe_handler(struct audio_client *ac,
+					struct pbe_params *pbe,
+					long *values);
+
+int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
+				struct virtualizer_params *virtualizer,
+				long *values);
+
+int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
+					 struct eq_params *eq,
+					 long *values);
+
+int msm_audio_effects_volume_handler(struct audio_client *ac,
+				     struct soft_volume_params *vol,
+				     long *values);
+
+int msm_audio_effects_volume_handler_v2(struct audio_client *ac,
+					struct soft_volume_params *vol,
+					long *values, int instance);
+#endif /*_MSM_AUDIO_EFFECTS_H*/
diff --git a/include/sound/msm-dai-q6-v2.h b/include/sound/msm-dai-q6-v2.h
new file mode 100644
index 0000000..b1d76bf
--- /dev/null
+++ b/include/sound/msm-dai-q6-v2.h
@@ -0,0 +1,92 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_DAI_Q6_PDATA_H__
+
+#define __MSM_DAI_Q6_PDATA_H__
+
+#define MSM_MI2S_SD0 (1 << 0)
+#define MSM_MI2S_SD1 (1 << 1)
+#define MSM_MI2S_SD2 (1 << 2)
+#define MSM_MI2S_SD3 (1 << 3)
+#define MSM_MI2S_CAP_RX 0
+#define MSM_MI2S_CAP_TX 1
+
+#define MSM_PRIM_MI2S 0
+#define MSM_SEC_MI2S  1
+#define MSM_TERT_MI2S 2
+#define MSM_QUAT_MI2S  3
+#define MSM_SEC_MI2S_SD1  4
+#define MSM_QUIN_MI2S  5
+#define MSM_SENARY_MI2S  6
+#define MSM_INT0_MI2S  7
+#define MSM_INT1_MI2S  8
+#define MSM_INT2_MI2S  9
+#define MSM_INT3_MI2S  10
+#define MSM_INT4_MI2S  11
+#define MSM_INT5_MI2S  12
+#define MSM_INT6_MI2S  13
+#define MSM_MI2S_MIN MSM_PRIM_MI2S
+#define MSM_MI2S_MAX MSM_INT6_MI2S
+
+struct msm_dai_auxpcm_config {
+	u16 mode;
+	u16 sync;
+	u16 frame;
+	u16 quant;
+	u16 num_slots;
+	u16 *slot_mapping;
+	u16 data;
+	u32 pcm_clk_rate;
+};
+
+struct msm_dai_auxpcm_pdata {
+	struct msm_dai_auxpcm_config mode_8k;
+	struct msm_dai_auxpcm_config mode_16k;
+};
+
+struct msm_mi2s_pdata {
+	u16 rx_sd_lines;
+	u16 tx_sd_lines;
+	u16 intf_id;
+};
+
+struct msm_i2s_data {
+	u32 capability; /* RX or TX */
+	u16 sd_lines;
+};
+
+struct msm_dai_tdm_group_config {
+	u16 group_id;
+	u16 num_ports;
+	u16 *port_id;
+	u32 clk_rate;
+};
+
+struct msm_dai_tdm_config {
+	u16 sync_mode;
+	u16 sync_src;
+	u16 data_out;
+	u16 invert_sync;
+	u16 data_delay;
+	u32 data_align;
+	u16 header_start_offset;
+	u16 header_width;
+	u16 header_num_frame_repeat;
+};
+
+struct msm_dai_tdm_pdata {
+	struct msm_dai_tdm_group_config group_config;
+	struct msm_dai_tdm_config config;
+};
+
+#endif
diff --git a/include/sound/msm-dts-eagle.h b/include/sound/msm-dts-eagle.h
new file mode 100644
index 0000000..2ef0113
--- /dev/null
+++ b/include/sound/msm-dts-eagle.h
@@ -0,0 +1,148 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_DTS_EAGLE_H__
+#define __MSM_DTS_EAGLE_H__
+
+#include <linux/compat.h>
+#include <sound/soc.h>
+#include <sound/devdep_params.h>
+#include <sound/q6asm-v2.h>
+
+#ifdef CONFIG_COMPAT
+enum {
+	DTS_EAGLE_IOCTL_GET_CACHE_SIZE32 = _IOR(0xF2, 0, __s32),
+	DTS_EAGLE_IOCTL_SET_CACHE_SIZE32 = _IOW(0xF2, 1, __s32),
+	DTS_EAGLE_IOCTL_GET_PARAM32 = _IOR(0xF2, 2, compat_uptr_t),
+	DTS_EAGLE_IOCTL_SET_PARAM32 = _IOW(0xF2, 3, compat_uptr_t),
+	DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32 =
+				_IOW(0xF2, 4, compat_uptr_t),
+	DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32 =
+				_IOW(0xF2, 5, compat_uptr_t),
+	DTS_EAGLE_IOCTL_GET_LICENSE32 =
+				_IOR(0xF2, 6, compat_uptr_t),
+	DTS_EAGLE_IOCTL_SET_LICENSE32 =
+				 _IOW(0xF2, 7, compat_uptr_t),
+	DTS_EAGLE_IOCTL_SEND_LICENSE32 = _IOW(0xF2, 8, __s32),
+	DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32 = _IOW(0xF2, 9,
+						     compat_uptr_t),
+};
+#endif
+
+#ifdef CONFIG_DTS_EAGLE
+void msm_dts_ion_memmap(struct param_outband *po_);
+int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module);
+int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable);
+void msm_dts_eagle_add_controls(struct snd_soc_platform *platform);
+int msm_dts_eagle_set_stream_gain(struct audio_client *ac,
+				  int lgain, int rgain);
+int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf,
+			     bool for_pre, bool get, struct audio_client *ac,
+			     struct param_outband *po);
+int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf,
+			     bool for_pre, bool get);
+int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg);
+int msm_dts_eagle_is_hpx_on(void);
+int msm_dts_eagle_init_pre(struct audio_client *ac);
+int msm_dts_eagle_deinit_pre(struct audio_client *ac);
+int msm_dts_eagle_init_post(int port_id, int copp_id);
+int msm_dts_eagle_deinit_post(int port_id, int topology);
+int msm_dts_eagle_init_master_module(struct audio_client *ac);
+int msm_dts_eagle_deinit_master_module(struct audio_client *ac);
+int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime);
+void msm_dts_eagle_pcm_free(struct snd_pcm *pcm);
+int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg);
+#else
+static inline void msm_dts_ion_memmap(struct param_outband *po_)
+{
+	pr_debug("%s\n", __func__);
+}
+static inline int msm_dts_eagle_enable_asm(struct audio_client *ac,
+					   u32 enable, int module)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_enable_adm(int port_id, int copp_idx,
+					   u32 enable)
+{
+	return 0;
+}
+static inline void msm_dts_eagle_add_controls(struct snd_soc_platform *platform)
+{
+}
+static inline int msm_dts_eagle_set_stream_gain(struct audio_client *ac,
+						int lgain, int rgain)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+static inline int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd,
+					   char *buf, bool for_pre, bool get,
+					   struct audio_client *ac,
+					   struct param_outband *po)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd,
+					   char *buf, bool for_pre, bool get)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg)
+{
+	return -EPERM;
+}
+static inline int msm_dts_eagle_is_hpx_on(void)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_init_pre(struct audio_client *ac)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_deinit_pre(struct audio_client *ac)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_init_post(int port_id, int coppid)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_deinit_post(int port_id, int topology)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_init_master_module(struct audio_client *ac)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_deinit_master_module(struct audio_client *ac)
+{
+	return 0;
+}
+static inline int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+static inline void msm_dts_eagle_pcm_free(struct snd_pcm *pcm)
+{
+	pr_debug("%s\n", __func__);
+}
+static inline int msm_dts_eagle_compat_ioctl(unsigned int cmd,
+					unsigned long arg)
+{
+	return 0;
+}
+#endif
+
+#endif
diff --git a/include/sound/msm-slim-dma.h b/include/sound/msm-slim-dma.h
new file mode 100644
index 0000000..daf394b
--- /dev/null
+++ b/include/sound/msm-slim-dma.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifndef _MSM_SLIMBUS_DMA_H
+#define _MSM_SLIMBUS_DMA_H
+
+#include <linux/slimbus/slimbus.h>
+
+/*
+ * struct msm_slim_dma_data - DMA data for slimbus data transfer
+ *
+ * @sdev: Handle to the slim_device instance associated with the
+ *	  data transfer.
+ * @ph:	Port handle for the slimbus ports.
+ * @dai_channel_ctl: callback function into the CPU dai driver
+ *		     to setup the data path.
+ *
+ * This structure is used to share the slimbus port handles and
+ * other data path setup related handles with other drivers.
+ */
+struct msm_slim_dma_data {
+
+	/* Handle to slimbus device */
+	struct slim_device *sdev;
+
+	/* Port Handle */
+	u32 ph;
+
+	/* Callback for data channel control */
+	int (*dai_channel_ctl)(struct msm_slim_dma_data *dma_data,
+				struct snd_soc_dai *dai, bool enable);
+};
+
+#endif
diff --git a/include/sound/q6adm-v2.h b/include/sound/q6adm-v2.h
new file mode 100644
index 0000000..c9a429d
--- /dev/null
+++ b/include/sound/q6adm-v2.h
@@ -0,0 +1,156 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __Q6_ADM_V2_H__
+#define __Q6_ADM_V2_H__
+
+
+#define ADM_PATH_PLAYBACK 0x1
+#define ADM_PATH_LIVE_REC 0x2
+#define ADM_PATH_NONLIVE_REC 0x3
+#define ADM_PATH_COMPRESSED_RX 0x5
+#include <linux/qdsp6v2/rtac.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6audio-v2.h>
+
+#define MAX_MODULES_IN_TOPO 16
+#define ADM_GET_TOPO_MODULE_LIST_LENGTH\
+		((MAX_MODULES_IN_TOPO + 1) * sizeof(uint32_t))
+#define AUD_PROC_BLOCK_SIZE	4096
+#define AUD_VOL_BLOCK_SIZE	4096
+#define AUDIO_RX_CALIBRATION_SIZE	(AUD_PROC_BLOCK_SIZE + \
+						AUD_VOL_BLOCK_SIZE)
+enum {
+	ADM_CUSTOM_TOP_CAL = 0,
+	ADM_AUDPROC_CAL,
+	ADM_AUDVOL_CAL,
+	ADM_RTAC_INFO_CAL,
+	ADM_RTAC_APR_CAL,
+	ADM_DTS_EAGLE,
+	ADM_SRS_TRUMEDIA,
+	ADM_RTAC_AUDVOL_CAL,
+	ADM_MAX_CAL_TYPES
+};
+
+enum {
+	ADM_MEM_MAP_INDEX_SOURCE_TRACKING = ADM_MAX_CAL_TYPES,
+	ADM_MEM_MAP_INDEX_MAX
+};
+
+enum {
+	ADM_CLIENT_ID_DEFAULT = 0,
+	ADM_CLIENT_ID_SOURCE_TRACKING,
+	ADM_CLIENT_ID_MAX,
+};
+
+#define MAX_COPPS_PER_PORT 0x8
+#define ADM_MAX_CHANNELS 8
+
+/* multiple copp per stream. */
+struct route_payload {
+	unsigned int copp_idx[MAX_COPPS_PER_PORT];
+	unsigned int port_id[MAX_COPPS_PER_PORT];
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+	unsigned short num_copps;
+	unsigned int session_id;
+};
+
+int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id,
+		      void *srs_params);
+
+int adm_dts_eagle_set(int port_id, int copp_idx, int param_id,
+		      void *data, uint32_t size);
+
+int adm_dts_eagle_get(int port_id, int copp_idx, int param_id,
+		      void *data, uint32_t size);
+
+void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate);
+
+int adm_get_params(int port_id, int copp_idx, uint32_t module_id,
+		   uint32_t param_id, uint32_t params_length, char *params);
+
+int adm_send_params_v5(int port_id, int copp_idx, char *params,
+			      uint32_t params_length);
+
+int adm_dolby_dap_send_params(int port_id, int copp_idx, char *params,
+			      uint32_t params_length);
+
+int adm_open(int port, int path, int rate, int mode, int topology,
+			   int perf_mode, uint16_t bits_per_sample,
+			   int app_type, int acdbdev_id);
+
+int adm_map_rtac_block(struct rtac_cal_block_data *cal_block);
+
+int adm_unmap_rtac_block(uint32_t *mem_map_handle);
+
+int adm_close(int port, int topology, int perf_mode);
+
+int adm_matrix_map(int path, struct route_payload payload_map,
+		   int perf_mode);
+
+int adm_connect_afe_port(int mode, int session_id, int port_id);
+
+void adm_ec_ref_rx_id(int  port_id);
+
+int adm_get_lowlatency_copp_id(int port_id);
+
+int adm_set_multi_ch_map(char *channel_map, int path);
+
+int adm_get_multi_ch_map(char *channel_map, int path);
+
+int adm_validate_and_get_port_index(int port_id);
+
+int adm_get_default_copp_idx(int port_id);
+
+int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id);
+
+int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx);
+
+int adm_get_indexes_from_copp_id(int copp_id, int *port_idx, int *copp_idx);
+
+int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx,
+				    unsigned int session_id,
+				    char *params, uint32_t params_length);
+
+int adm_get_pp_topo_module_list(int port_id, int copp_idx, int32_t param_length,
+				char *params);
+
+int adm_set_volume(int port_id, int copp_idx, int volume);
+
+int adm_set_softvolume(int port_id, int copp_idx,
+		       struct audproc_softvolume_params *softvol_param);
+
+int adm_param_enable(int port_id, int copp_idx, int module_id,  int enable);
+
+int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode,
+			 int cal_type, char *params, int size);
+
+int adm_set_wait_parameters(int port_id, int copp_idx);
+
+int adm_reset_wait_parameters(int port_id, int copp_idx);
+
+int adm_wait_timeout(int port_id, int copp_idx, int wait_time);
+
+int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode,
+		       int cal_type, char *params, int *size);
+
+int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on);
+
+int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency);
+int adm_set_sound_focus(int port_id, int copp_idx,
+			struct sound_focus_param soundFocusData);
+int adm_get_sound_focus(int port_id, int copp_idx,
+			struct sound_focus_param *soundFocusData);
+int adm_get_source_tracking(int port_id, int copp_idx,
+			    struct source_tracking_param *sourceTrackingData);
+#endif /* __Q6_ADM_V2_H__ */
diff --git a/include/sound/q6afe-v2.h b/include/sound/q6afe-v2.h
new file mode 100644
index 0000000..367e75d
--- /dev/null
+++ b/include/sound/q6afe-v2.h
@@ -0,0 +1,366 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __Q6AFE_V2_H__
+#define __Q6AFE_V2_H__
+#include <sound/apr_audio-v2.h>
+#include <linux/qdsp6v2/rtac.h>
+
+#define IN			0x000
+#define OUT			0x001
+#define MSM_AFE_MONO        0
+#define MSM_AFE_CH_STEREO   1
+#define MSM_AFE_MONO_RIGHT  1
+#define MSM_AFE_MONO_LEFT   2
+#define MSM_AFE_STEREO      3
+#define MSM_AFE_4CHANNELS   4
+#define MSM_AFE_6CHANNELS   6
+#define MSM_AFE_8CHANNELS   8
+
+#define MSM_AFE_I2S_FORMAT_LPCM		0
+#define MSM_AFE_I2S_FORMAT_COMPR		1
+#define MSM_AFE_I2S_FORMAT_IEC60958_LPCM	2
+#define MSM_AFE_I2S_FORMAT_IEC60958_COMPR	3
+
+#define MSM_AFE_PORT_TYPE_RX 0
+#define MSM_AFE_PORT_TYPE_TX 1
+
+#define RT_PROXY_DAI_001_RX	0xE0
+#define RT_PROXY_DAI_001_TX	0xF0
+#define RT_PROXY_DAI_002_RX	0xF1
+#define RT_PROXY_DAI_002_TX	0xE1
+#define VIRTUAL_ID_TO_PORTID(val) ((val & 0xF) | 0x2000)
+
+#define AFE_CLK_VERSION_V1    1
+#define AFE_CLK_VERSION_V2    2
+
+enum {
+	/* IDX 0->4 */
+	IDX_PRIMARY_I2S_RX,
+	IDX_PRIMARY_I2S_TX,
+	IDX_AFE_PORT_ID_PRIMARY_PCM_RX,
+	IDX_AFE_PORT_ID_PRIMARY_PCM_TX,
+	IDX_SECONDARY_I2S_RX,
+	/* IDX 5->9 */
+	IDX_SECONDARY_I2S_TX,
+	IDX_MI2S_RX,
+	IDX_MI2S_TX,
+	IDX_HDMI_RX,
+	IDX_RSVD_2,
+	/* IDX 10->14 */
+	IDX_RSVD_3,
+	IDX_DIGI_MIC_TX,
+	IDX_VOICE_RECORD_RX,
+	IDX_VOICE_RECORD_TX,
+	IDX_VOICE_PLAYBACK_TX,
+	/* IDX 15->19 */
+	IDX_SLIMBUS_0_RX,
+	IDX_SLIMBUS_0_TX,
+	IDX_SLIMBUS_1_RX,
+	IDX_SLIMBUS_1_TX,
+	IDX_SLIMBUS_2_RX,
+	/* IDX 20->24 */
+	IDX_SLIMBUS_2_TX,
+	IDX_SLIMBUS_3_RX,
+	IDX_SLIMBUS_3_TX,
+	IDX_SLIMBUS_4_RX,
+	IDX_SLIMBUS_4_TX,
+	/* IDX 25->29 */
+	IDX_SLIMBUS_5_RX,
+	IDX_SLIMBUS_5_TX,
+	IDX_INT_BT_SCO_RX,
+	IDX_INT_BT_SCO_TX,
+	IDX_INT_BT_A2DP_RX,
+	/* IDX 30->34 */
+	IDX_INT_FM_RX,
+	IDX_INT_FM_TX,
+	IDX_RT_PROXY_PORT_001_RX,
+	IDX_RT_PROXY_PORT_001_TX,
+	IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX,
+	/* IDX 35->39 */
+	IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX,
+	IDX_AFE_PORT_ID_SECONDARY_MI2S_RX,
+	IDX_AFE_PORT_ID_SECONDARY_MI2S_TX,
+	IDX_AFE_PORT_ID_TERTIARY_MI2S_RX,
+	IDX_AFE_PORT_ID_TERTIARY_MI2S_TX,
+	/* IDX 40->44 */
+	IDX_AFE_PORT_ID_PRIMARY_MI2S_RX,
+	IDX_AFE_PORT_ID_PRIMARY_MI2S_TX,
+	IDX_AFE_PORT_ID_SECONDARY_PCM_RX,
+	IDX_AFE_PORT_ID_SECONDARY_PCM_TX,
+	IDX_VOICE2_PLAYBACK_TX,
+	/* IDX 45->49 */
+	IDX_SLIMBUS_6_RX,
+	IDX_SLIMBUS_6_TX,
+	IDX_SPDIF_RX,
+	IDX_GLOBAL_CFG,
+	IDX_AUDIO_PORT_ID_I2S_RX,
+	/* IDX 50->53 */
+	IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1,
+	IDX_AFE_PORT_ID_QUINARY_MI2S_RX,
+	IDX_AFE_PORT_ID_QUINARY_MI2S_TX,
+	IDX_AFE_PORT_ID_SENARY_MI2S_TX,
+	/* IDX 54->117 */
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7,
+	IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7,
+	IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7,
+	IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7,
+	IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7,
+	/* IDX 118->121 */
+	IDX_SLIMBUS_7_RX,
+	IDX_SLIMBUS_7_TX,
+	IDX_SLIMBUS_8_RX,
+	IDX_SLIMBUS_8_TX,
+	/* IDX 122-> 123 */
+	IDX_AFE_PORT_ID_USB_RX,
+	IDX_AFE_PORT_ID_USB_TX,
+	/* IDX 124 */
+	IDX_DISPLAY_PORT_RX,
+	/* IDX 125-> 128 */
+	IDX_AFE_PORT_ID_TERTIARY_PCM_RX,
+	IDX_AFE_PORT_ID_TERTIARY_PCM_TX,
+	IDX_AFE_PORT_ID_QUATERNARY_PCM_RX,
+	IDX_AFE_PORT_ID_QUATERNARY_PCM_TX,
+	/* IDX 129-> 142 */
+	IDX_AFE_PORT_ID_INT0_MI2S_RX,
+	IDX_AFE_PORT_ID_INT0_MI2S_TX,
+	IDX_AFE_PORT_ID_INT1_MI2S_RX,
+	IDX_AFE_PORT_ID_INT1_MI2S_TX,
+	IDX_AFE_PORT_ID_INT2_MI2S_RX,
+	IDX_AFE_PORT_ID_INT2_MI2S_TX,
+	IDX_AFE_PORT_ID_INT3_MI2S_RX,
+	IDX_AFE_PORT_ID_INT3_MI2S_TX,
+	IDX_AFE_PORT_ID_INT4_MI2S_RX,
+	IDX_AFE_PORT_ID_INT4_MI2S_TX,
+	IDX_AFE_PORT_ID_INT5_MI2S_RX,
+	IDX_AFE_PORT_ID_INT5_MI2S_TX,
+	IDX_AFE_PORT_ID_INT6_MI2S_RX,
+	IDX_AFE_PORT_ID_INT6_MI2S_TX,
+	AFE_MAX_PORTS
+};
+
+enum afe_mad_type {
+	MAD_HW_NONE = 0x00,
+	MAD_HW_AUDIO = 0x01,
+	MAD_HW_BEACON = 0x02,
+	MAD_HW_ULTRASOUND = 0x04,
+	MAD_SW_AUDIO = 0x05,
+};
+
+enum afe_cal_mode {
+	AFE_CAL_MODE_DEFAULT = 0x00,
+	AFE_CAL_MODE_NONE,
+};
+
+struct afe_audio_buffer {
+	dma_addr_t phys;
+	void       *data;
+	uint32_t   used;
+	uint32_t   size;/* size of buffer */
+	uint32_t   actual_size; /* actual number of bytes read by DSP */
+	struct      ion_handle *handle;
+	struct      ion_client *client;
+};
+
+struct afe_audio_port_data {
+	struct afe_audio_buffer *buf;
+	uint32_t	    max_buf_cnt;
+	uint32_t	    dsp_buf;
+	uint32_t	    cpu_buf;
+	struct list_head    mem_map_handle;
+	uint32_t	    tmp_hdl;
+	/* read or write locks */
+	struct mutex	    lock;
+	spinlock_t	    dsp_lock;
+};
+
+struct afe_audio_client {
+	atomic_t	       cmd_state;
+	/* Relative or absolute TS */
+	uint32_t	       time_flag;
+	void		       *priv;
+	uint64_t	       time_stamp;
+	struct mutex	       cmd_lock;
+	/* idx:1 out port, 0: in port*/
+	struct afe_audio_port_data port[2];
+	wait_queue_head_t      cmd_wait;
+	uint32_t               mem_map_handle;
+};
+
+struct aanc_data {
+	bool aanc_active;
+	uint16_t aanc_rx_port;
+	uint16_t aanc_tx_port;
+	uint32_t aanc_rx_port_sample_rate;
+	uint32_t aanc_tx_port_sample_rate;
+};
+
+int afe_open(u16 port_id, union afe_port_config *afe_config, int rate);
+int afe_close(int port_id);
+int afe_loopback(u16 enable, u16 rx_port, u16 tx_port);
+int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain);
+int afe_loopback_gain(u16 port_id, u16 volume);
+int afe_validate_port(u16 port_id);
+int afe_get_port_index(u16 port_id);
+int afe_get_topology(int port_id);
+int afe_start_pseudo_port(u16 port_id);
+int afe_stop_pseudo_port(u16 port_id);
+uint32_t afe_req_mmap_handle(struct afe_audio_client *ac);
+int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz,
+		struct afe_audio_client *ac);
+int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz);
+int afe_cmd_memory_map_nowait(int port_id, phys_addr_t dma_addr_p,
+			u32 dma_buf_sz);
+int afe_cmd_memory_unmap(u32 dma_addr_p);
+int afe_cmd_memory_unmap_nowait(u32 dma_addr_p);
+void afe_set_dtmf_gen_rx_portid(u16 rx_port_id, int set);
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+			 uint16_t high_freq,
+			 uint16_t low_freq, uint16_t gain);
+int afe_register_get_events(u16 port_id,
+		void (*cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv),
+		void *private_data);
+int afe_unregister_get_events(u16 port_id);
+int afe_rt_proxy_port_write(phys_addr_t buf_addr_p,
+			u32 mem_map_handle, int bytes);
+int afe_rt_proxy_port_read(phys_addr_t buf_addr_p,
+			u32 mem_map_handle, int bytes);
+void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode);
+int afe_port_start(u16 port_id, union afe_port_config *afe_config,
+	u32 rate);
+int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config,
+		      u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
+		      struct afe_enc_config *enc_config);
+int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
+	int l_ch, int r_ch, u32 enable);
+int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib);
+int afe_port_stop_nowait(int port_id);
+int afe_apply_gain(u16 port_id, u16 gain);
+int afe_q6_interface_prepare(void);
+int afe_get_port_type(u16 port_id);
+int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir,
+			struct afe_audio_client *ac,
+			unsigned int bufsz,
+			unsigned int bufcnt);
+struct afe_audio_client *q6afe_audio_client_alloc(void *priv);
+int q6afe_audio_client_buf_free_contiguous(unsigned int dir,
+			struct afe_audio_client *ac);
+void q6afe_audio_client_free(struct afe_audio_client *ac);
+/* if port_id is virtual, convert to physical..
+ * if port_id is already physical, return physical
+ */
+int afe_convert_virtual_to_portid(u16 port_id);
+
+int afe_pseudo_port_start_nowait(u16 port_id);
+int afe_pseudo_port_stop_nowait(u16 port_id);
+int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg);
+int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg);
+int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg);
+int afe_set_digital_codec_core_clock(u16 port_id,
+			struct afe_digital_clk_cfg *cfg);
+int afe_set_lpass_internal_digital_codec_clock(u16 port_id,
+				struct afe_digital_clk_cfg *cfg);
+int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable);
+
+int q6afe_check_osr_clk_freq(u32 freq);
+
+int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg,
+		u16 port_id);
+int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg
+		*ch_status_cfg,	u16 port_id);
+
+int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port,
+		u32 rate);
+
+int afe_turn_onoff_hw_mad(u16 mad_type, u16 mad_enable);
+int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type);
+enum afe_mad_type afe_port_get_mad_type(u16 port_id);
+int afe_set_config(enum afe_config_type config_type, void *config_data,
+		   int arg);
+void afe_clear_config(enum afe_config_type config);
+bool afe_has_config(enum afe_config_type config);
+
+void afe_set_aanc_info(struct aanc_data *aanc_info);
+int afe_port_group_set_param(u16 group_id,
+	union afe_port_group_config *afe_group_config);
+int afe_port_group_enable(u16 group_id,
+	union afe_port_group_config *afe_group_config, u16 enable);
+int afe_unmap_rtac_block(uint32_t *mem_map_handle);
+int afe_map_rtac_block(struct rtac_cal_block_data *cal_block);
+int afe_send_slot_mapping_cfg(
+	struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg,
+	u16 port_id);
+int afe_send_custom_tdm_header_cfg(
+	struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg,
+	u16 port_id);
+int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port,
+		u32 rate);
+#endif /* __Q6AFE_V2_H__ */
diff --git a/include/sound/q6asm-v2.h b/include/sound/q6asm-v2.h
new file mode 100644
index 0000000..7321481
--- /dev/null
+++ b/include/sound/q6asm-v2.h
@@ -0,0 +1,637 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __Q6_ASM_V2_H__
+#define __Q6_ASM_V2_H__
+
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/rtac.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/list.h>
+#include <linux/msm_ion.h>
+
+#define IN                      0x000
+#define OUT                     0x001
+#define CH_MODE_MONO            0x001
+#define CH_MODE_STEREO          0x002
+
+#define FORMAT_LINEAR_PCM   0x0000
+#define FORMAT_DTMF         0x0001
+#define FORMAT_ADPCM	    0x0002
+#define FORMAT_YADPCM       0x0003
+#define FORMAT_MP3          0x0004
+#define FORMAT_MPEG4_AAC    0x0005
+#define FORMAT_AMRNB	    0x0006
+#define FORMAT_AMRWB	    0x0007
+#define FORMAT_V13K	    0x0008
+#define FORMAT_EVRC	    0x0009
+#define FORMAT_EVRCB	    0x000a
+#define FORMAT_EVRCWB	    0x000b
+#define FORMAT_MIDI	    0x000c
+#define FORMAT_SBC	    0x000d
+#define FORMAT_WMA_V10PRO   0x000e
+#define FORMAT_WMA_V9	    0x000f
+#define FORMAT_AMR_WB_PLUS  0x0010
+#define FORMAT_MPEG4_MULTI_AAC 0x0011
+#define FORMAT_MULTI_CHANNEL_LINEAR_PCM 0x0012
+#define FORMAT_AC3          0x0013
+#define FORMAT_EAC3         0x0014
+#define FORMAT_MP2          0x0015
+#define FORMAT_FLAC         0x0016
+#define FORMAT_ALAC         0x0017
+#define FORMAT_VORBIS       0x0018
+#define FORMAT_APE          0x0019
+#define FORMAT_G711_ALAW_FS 0x001a
+#define FORMAT_G711_MLAW_FS 0x001b
+#define FORMAT_DTS          0x001c
+#define FORMAT_DSD          0x001d
+
+#define ENCDEC_SBCBITRATE   0x0001
+#define ENCDEC_IMMEDIATE_DECODE 0x0002
+#define ENCDEC_CFG_BLK          0x0003
+
+#define CMD_PAUSE          0x0001
+#define CMD_FLUSH          0x0002
+#define CMD_EOS            0x0003
+#define CMD_CLOSE          0x0004
+#define CMD_OUT_FLUSH      0x0005
+#define CMD_SUSPEND        0x0006
+
+/* bit 0:1 represents priority of stream */
+#define STREAM_PRIORITY_NORMAL	0x0000
+#define STREAM_PRIORITY_LOW	0x0001
+#define STREAM_PRIORITY_HIGH	0x0002
+
+/* bit 4 represents META enable of encoded data buffer */
+#define BUFFER_META_ENABLE	0x0010
+
+/* bit 5 represents timestamp */
+/* bit 5 - 0 -- ASM_DATA_EVENT_READ_DONE will have relative time-stamp*/
+/* bit 5 - 1 -- ASM_DATA_EVENT_READ_DONE will have absolute time-stamp*/
+#define ABSOLUTE_TIMESTAMP_ENABLE  0x0020
+
+/* Enable Sample_Rate/Channel_Mode notification event from Decoder */
+#define SR_CM_NOTIFY_ENABLE	0x0004
+
+#define TUN_WRITE_IO_MODE 0x0008 /* tunnel read write mode */
+#define TUN_READ_IO_MODE  0x0004 /* tunnel read write mode */
+#define SYNC_IO_MODE	0x0001
+#define ASYNC_IO_MODE	0x0002
+#define COMPRESSED_IO	0x0040
+#define COMPRESSED_STREAM_IO	0x0080
+#define NT_MODE        0x0400
+
+#define NO_TIMESTAMP    0xFF00
+#define SET_TIMESTAMP   0x0000
+
+#define SOFT_PAUSE_ENABLE	1
+#define SOFT_PAUSE_DISABLE	0
+
+#define ASM_ACTIVE_STREAMS_ALLOWED	0x8
+/* Control session is used for mapping calibration memory */
+#define ASM_CONTROL_SESSION	(ASM_ACTIVE_STREAMS_ALLOWED + 1)
+
+#define ASM_SHIFT_GAPLESS_MODE_FLAG	31
+#define ASM_SHIFT_LAST_BUFFER_FLAG	30
+
+#define ASM_LITTLE_ENDIAN 0
+#define ASM_BIG_ENDIAN 1
+
+/* PCM_MEDIA_FORMAT_Version */
+enum {
+	PCM_MEDIA_FORMAT_V2 = 0,
+	PCM_MEDIA_FORMAT_V3,
+	PCM_MEDIA_FORMAT_V4,
+};
+
+/* PCM format modes in DSP */
+enum {
+	DEFAULT_QF = 0,
+	Q15 = 15,
+	Q23 = 23,
+	Q31 = 31,
+};
+
+/* payload structure bytes */
+#define READDONE_IDX_STATUS 0
+#define READDONE_IDX_BUFADD_LSW 1
+#define READDONE_IDX_BUFADD_MSW 2
+#define READDONE_IDX_MEMMAP_HDL 3
+#define READDONE_IDX_SIZE 4
+#define READDONE_IDX_OFFSET 5
+#define READDONE_IDX_LSW_TS 6
+#define READDONE_IDX_MSW_TS 7
+#define READDONE_IDX_FLAGS 8
+#define READDONE_IDX_NUMFRAMES 9
+#define READDONE_IDX_SEQ_ID 10
+
+#define SOFT_PAUSE_PERIOD       30   /* ramp up/down for 30ms    */
+#define SOFT_PAUSE_STEP         0 /* Step value 0ms or 0us */
+enum {
+	SOFT_PAUSE_CURVE_LINEAR = 0,
+	SOFT_PAUSE_CURVE_EXP,
+	SOFT_PAUSE_CURVE_LOG,
+};
+
+#define SOFT_VOLUME_PERIOD       30   /* ramp up/down for 30ms    */
+#define SOFT_VOLUME_STEP         0 /* Step value 0ms or 0us */
+enum {
+	SOFT_VOLUME_CURVE_LINEAR = 0,
+	SOFT_VOLUME_CURVE_EXP,
+	SOFT_VOLUME_CURVE_LOG,
+};
+
+#define SOFT_VOLUME_INSTANCE_1	1
+#define SOFT_VOLUME_INSTANCE_2	2
+
+typedef void (*app_cb)(uint32_t opcode, uint32_t token,
+			uint32_t *payload, void *priv);
+
+struct audio_buffer {
+	dma_addr_t phys;
+	void       *data;
+	uint32_t   used;
+	uint32_t   size;/* size of buffer */
+	uint32_t   actual_size; /* actual number of bytes read by DSP */
+	struct      ion_handle *handle;
+	struct      ion_client *client;
+};
+
+struct audio_aio_write_param {
+	phys_addr_t   paddr;
+	uint32_t      len;
+	uint32_t      uid;
+	uint32_t      lsw_ts;
+	uint32_t      msw_ts;
+	uint32_t      flags;
+	uint32_t      metadata_len;
+	uint32_t      last_buffer;
+};
+
+struct audio_aio_read_param {
+	phys_addr_t   paddr;
+	uint32_t      len;
+	uint32_t      uid;
+	uint32_t      flags;/*meta data flags*/
+};
+
+struct audio_port_data {
+	struct audio_buffer *buf;
+	uint32_t	    max_buf_cnt;
+	uint32_t	    dsp_buf;
+	uint32_t	    cpu_buf;
+	struct list_head    mem_map_handle;
+	uint32_t	    tmp_hdl;
+	/* read or write locks */
+	struct mutex	    lock;
+	spinlock_t	    dsp_lock;
+};
+
+struct shared_io_config {
+	uint32_t format;
+	uint16_t bits_per_sample;
+	uint32_t rate;
+	uint32_t channels;
+	uint16_t sample_word_size;
+	uint32_t bufsz;
+	uint32_t bufcnt;
+};
+
+struct audio_client {
+	int                    session;
+	app_cb		       cb;
+	atomic_t	       cmd_state;
+	/* Relative or absolute TS */
+	atomic_t	       time_flag;
+	atomic_t	       nowait_cmd_cnt;
+	atomic_t               mem_state;
+	void		       *priv;
+	uint32_t               io_mode;
+	uint64_t	       time_stamp;
+	struct apr_svc         *apr;
+	struct apr_svc         *mmap_apr;
+	struct apr_svc         *apr2;
+	struct mutex	       cmd_lock;
+	/* idx:1 out port, 0: in port*/
+	struct audio_port_data port[2];
+	wait_queue_head_t      cmd_wait;
+	wait_queue_head_t      time_wait;
+	wait_queue_head_t      mem_wait;
+	int                    perf_mode;
+	int					   stream_id;
+	struct device *dev;
+	int		       topology;
+	int		       app_type;
+	/* audio cache operations fptr*/
+	int (*fptr_cache_ops)(struct audio_buffer *abuff, int cache_op);
+	atomic_t               unmap_cb_success;
+	atomic_t               reset;
+	/* holds latest DSP pipeline delay */
+	uint32_t               path_delay;
+	/* shared io */
+	struct audio_buffer shared_pos_buf;
+	struct shared_io_config config;
+};
+
+void q6asm_audio_client_free(struct audio_client *ac);
+
+struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv);
+
+struct audio_client *q6asm_get_audio_client(int session_id);
+
+int q6asm_audio_client_buf_alloc(unsigned int dir/* 1:Out,0:In */,
+				struct audio_client *ac,
+				unsigned int bufsz,
+				uint32_t bufcnt);
+int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir
+				/* 1:Out,0:In */,
+				struct audio_client *ac,
+				unsigned int bufsz,
+				unsigned int bufcnt);
+
+int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
+			struct audio_client *ac);
+
+int q6asm_open_read(struct audio_client *ac, uint32_t format
+		/*, uint16_t bits_per_sample*/);
+
+int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample);
+
+int q6asm_open_read_v3(struct audio_client *ac, uint32_t format,
+		       uint16_t bits_per_sample);
+
+int q6asm_open_read_v4(struct audio_client *ac, uint32_t format,
+		       uint16_t bits_per_sample);
+
+int q6asm_open_write(struct audio_client *ac, uint32_t format
+		/*, uint16_t bits_per_sample*/);
+
+int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample);
+
+int q6asm_open_shared_io(struct audio_client *ac,
+			 struct shared_io_config *c, int dir);
+
+int q6asm_open_write_v3(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample);
+
+int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample);
+
+int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
+			       uint16_t bits_per_sample, int32_t stream_id,
+			       bool is_gapless_mode);
+
+int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format,
+			       uint16_t bits_per_sample, int32_t stream_id,
+			       bool is_gapless_mode);
+
+int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format,
+			       uint16_t bits_per_sample, int32_t stream_id,
+			       bool is_gapless_mode);
+
+int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format,
+				uint32_t passthrough_flag);
+
+int q6asm_open_read_write(struct audio_client *ac,
+			uint32_t rd_format,
+			uint32_t wr_format);
+
+int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format,
+			     uint32_t wr_format, bool is_meta_data_mode,
+			     uint32_t bits_per_sample, bool overwrite_topology,
+			     int topology);
+
+int q6asm_open_loopback_v2(struct audio_client *ac,
+			   uint16_t bits_per_sample);
+
+int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+				uint32_t lsw_ts, uint32_t flags);
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+				uint32_t lsw_ts, uint32_t flags);
+
+int q6asm_async_write(struct audio_client *ac,
+					  struct audio_aio_write_param *param);
+
+int q6asm_async_read(struct audio_client *ac,
+					  struct audio_aio_read_param *param);
+
+int q6asm_read(struct audio_client *ac);
+int q6asm_read_v2(struct audio_client *ac, uint32_t len);
+int q6asm_read_nolock(struct audio_client *ac);
+
+int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add,
+			int dir, uint32_t bufsz, uint32_t bufcnt);
+
+int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add,
+							int dir);
+
+struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac, int dir);
+
+int q6asm_shared_io_free(struct audio_client *ac, int dir);
+
+int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *si, uint32_t *msw,
+			 uint32_t *lsw);
+
+int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block);
+
+int q6asm_unmap_rtac_block(uint32_t *mem_map_handle);
+
+int q6asm_send_cal(struct audio_client *ac);
+
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+		uint32_t msw_ts, uint32_t lsw_ts);
+
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+		uint32_t msw_ts, uint32_t lsw_ts);
+
+int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags,
+		uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id);
+
+int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable);
+
+int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable);
+
+int q6asm_cmd(struct audio_client *ac, int cmd);
+
+int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id);
+
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd);
+
+int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd,
+			    uint32_t stream_id);
+
+void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac,
+				uint32_t *size, uint32_t *idx);
+
+int q6asm_cpu_buf_release(int dir, struct audio_client *ac);
+
+void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
+					uint32_t *size, uint32_t *idx);
+
+int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac);
+
+/* File format specific configurations to be added below */
+
+int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
+			 uint32_t frames_per_buf,
+			uint32_t sample_rate, uint32_t channels,
+			 uint32_t bit_rate,
+			 uint32_t mode, uint32_t format);
+
+int q6asm_enc_cfg_blk_g711(struct audio_client *ac,
+			 uint32_t frames_per_buf,
+			uint32_t sample_rate);
+
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+			uint32_t rate, uint32_t channels);
+
+int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac,
+			uint32_t rate, uint32_t channels,
+			uint16_t bits_per_sample,
+			bool use_default_chmap, bool use_back_flavor,
+			u8 *channel_map);
+
+int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
+			     uint32_t rate, uint32_t channels,
+			     uint16_t bits_per_sample, bool use_default_chmap,
+			     bool use_back_flavor, u8 *channel_map,
+			     uint16_t sample_word_size);
+
+int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+			     uint32_t rate, uint32_t channels,
+			     uint16_t bits_per_sample, bool use_default_chmap,
+			     bool use_back_flavor, u8 *channel_map,
+			     uint16_t sample_word_size, uint16_t endianness,
+			     uint16_t mode);
+
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+			uint32_t rate, uint32_t channels,
+			uint16_t bits_per_sample);
+
+int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac,
+					    uint32_t rate, uint32_t channels,
+					    uint16_t bits_per_sample,
+					    uint16_t sample_word_size);
+
+int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac,
+					    uint32_t rate, uint32_t channels,
+					    uint16_t bits_per_sample,
+					    uint16_t sample_word_size,
+					    uint16_t endianness,
+					    uint16_t mode);
+
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+		uint32_t num_channels);
+
+int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
+			uint32_t rate, uint32_t channels);
+
+int q6asm_enable_sbrps(struct audio_client *ac,
+			uint32_t sbr_ps);
+
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+			uint16_t sce_left, uint16_t sce_right);
+
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff);
+
+int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
+		uint16_t min_rate, uint16_t max_rate,
+		uint16_t reduced_rate_level, uint16_t rate_modulation_cmd);
+
+int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
+		uint16_t min_rate, uint16_t max_rate,
+		uint16_t rate_modulation_cmd);
+
+int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
+		uint16_t band_mode, uint16_t dtx_enable);
+
+int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
+		uint16_t band_mode, uint16_t dtx_enable);
+
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+			uint32_t rate, uint32_t channels);
+
+int q6asm_media_format_block_pcm_format_support(struct audio_client *ac,
+			uint32_t rate, uint32_t channels,
+			uint16_t bits_per_sample);
+
+int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac,
+				uint32_t rate, uint32_t channels,
+				uint16_t bits_per_sample, int stream_id,
+				bool use_default_chmap, char *channel_map);
+
+int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac,
+						   uint32_t rate,
+						   uint32_t channels,
+						   uint16_t bits_per_sample,
+						   int stream_id,
+						   bool use_default_chmap,
+						   char *channel_map,
+						   uint16_t sample_word_size);
+
+int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac,
+						   uint32_t rate,
+						   uint32_t channels,
+						   uint16_t bits_per_sample,
+						   int stream_id,
+						   bool use_default_chmap,
+						   char *channel_map,
+						   uint16_t sample_word_size,
+						   uint16_t endianness,
+						   uint16_t mode);
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+			uint32_t rate, uint32_t channels,
+			bool use_default_chmap, char *channel_map);
+
+int q6asm_media_format_block_multi_ch_pcm_v2(
+			struct audio_client *ac,
+			uint32_t rate, uint32_t channels,
+			bool use_default_chmap, char *channel_map,
+			uint16_t bits_per_sample);
+
+int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
+					     uint32_t rate, uint32_t channels,
+					     bool use_default_chmap,
+					     char *channel_map,
+					     uint16_t bits_per_sample,
+					     uint16_t sample_word_size);
+
+int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+					     uint32_t rate, uint32_t channels,
+					     bool use_default_chmap,
+					     char *channel_map,
+					     uint16_t bits_per_sample,
+					     uint16_t sample_word_size,
+					     uint16_t endianness,
+					     uint16_t mode);
+
+int q6asm_media_format_block_aac(struct audio_client *ac,
+			struct asm_aac_cfg *cfg);
+
+int q6asm_stream_media_format_block_aac(struct audio_client *ac,
+			struct asm_aac_cfg *cfg, int stream_id);
+
+int q6asm_media_format_block_multi_aac(struct audio_client *ac,
+			struct asm_aac_cfg *cfg);
+
+int q6asm_media_format_block_wma(struct audio_client *ac,
+			void *cfg, int stream_id);
+
+int q6asm_media_format_block_wmapro(struct audio_client *ac,
+			void *cfg, int stream_id);
+
+int q6asm_media_format_block_amrwbplus(struct audio_client *ac,
+			struct asm_amrwbplus_cfg *cfg);
+
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+			struct asm_flac_cfg *cfg, int stream_id);
+
+int q6asm_media_format_block_alac(struct audio_client *ac,
+			struct asm_alac_cfg *cfg, int stream_id);
+
+int q6asm_media_format_block_g711(struct audio_client *ac,
+			struct asm_g711_dec_cfg *cfg, int stream_id);
+
+int q6asm_stream_media_format_block_vorbis(struct audio_client *ac,
+			struct asm_vorbis_cfg *cfg, int stream_id);
+
+int q6asm_media_format_block_ape(struct audio_client *ac,
+			struct asm_ape_cfg *cfg, int stream_id);
+
+int q6asm_media_format_block_dsd(struct audio_client *ac,
+			struct asm_dsd_cfg *cfg, int stream_id);
+
+int q6asm_ds1_set_endp_params(struct audio_client *ac,
+				int param_id, int param_value);
+
+/* Send stream based end params */
+int q6asm_ds1_set_stream_endp_params(struct audio_client *ac, int param_id,
+				     int param_value, int stream_id);
+
+/* PP specific */
+int q6asm_equalizer(struct audio_client *ac, void *eq);
+
+/* Send Volume Command */
+int q6asm_set_volume(struct audio_client *ac, int volume);
+
+/* Send Volume Command */
+int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance);
+
+/* DTS Eagle Params */
+int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size,
+			void *data, struct param_outband *po, int m_id);
+int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size,
+			void *data, struct param_outband *po, int m_id);
+
+/* Set SoftPause Params */
+int q6asm_set_softpause(struct audio_client *ac,
+			struct asm_softpause_params *param);
+
+/* Set Softvolume Params */
+int q6asm_set_softvolume(struct audio_client *ac,
+			struct asm_softvolume_params *param);
+
+/* Set Softvolume Params */
+int q6asm_set_softvolume_v2(struct audio_client *ac,
+			    struct asm_softvolume_params *param, int instance);
+
+/* Send left-right channel gain */
+int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain);
+
+/* Send multi channel gain */
+int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels,
+			   uint32_t *gains, uint8_t *ch_map, bool use_default);
+
+/* Enable Mute/unmute flag */
+int q6asm_set_mute(struct audio_client *ac, int muteflag);
+
+int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp);
+
+int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp);
+
+int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
+				    uint32_t params_length);
+
+/* Client can set the IO mode to either AIO/SIO mode */
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode);
+
+/* Get Service ID for APR communication */
+int q6asm_get_apr_service_id(int session_id);
+
+/* Common format block without any payload */
+int q6asm_media_format_block(struct audio_client *ac, uint32_t format);
+
+/* Send the meta data to remove initial and trailing silence */
+int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples,
+		uint32_t trailing_samples);
+
+/* Send the stream meta data to remove initial and trailing silence */
+int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+		uint32_t initial_samples, uint32_t trailing_samples);
+
+int q6asm_get_asm_topology(int session_id);
+int q6asm_get_asm_app_type(int session_id);
+
+int q6asm_send_mtmx_strtr_window(struct audio_client *ac,
+		struct asm_session_mtmx_strtr_param_window_v2_t *window_param,
+		uint32_t param_id);
+
+/* Retrieve the current DSP path delay */
+int q6asm_get_path_delay(struct audio_client *ac);
+
+/* Helper functions to retrieve data from token */
+uint8_t q6asm_get_buf_index_from_token(uint32_t token);
+uint8_t q6asm_get_stream_id_from_token(uint32_t token);
+
+#endif /* __Q6_ASM_H__ */
diff --git a/include/sound/q6audio-v2.h b/include/sound/q6audio-v2.h
new file mode 100644
index 0000000..fd14f33
--- /dev/null
+++ b/include/sound/q6audio-v2.h
@@ -0,0 +1,36 @@
+/* Copyright (c) 2012-2013, 2015 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _Q6_AUDIO_H_
+#define _Q6_AUDIO_H_
+
+#include <linux/qdsp6v2/apr.h>
+
+enum {
+	LEGACY_PCM_MODE = 0,
+	LOW_LATENCY_PCM_MODE,
+	ULTRA_LOW_LATENCY_PCM_MODE,
+	ULL_POST_PROCESSING_PCM_MODE,
+};
+
+
+int q6audio_get_port_index(u16 port_id);
+
+int q6audio_convert_virtual_to_portid(u16 port_id);
+
+int q6audio_validate_port(u16 port_id);
+
+int q6audio_is_digital_pcm_interface(u16 port_id);
+
+int q6audio_get_port_id(u16 port_id);
+
+#endif
diff --git a/include/sound/q6core.h b/include/sound/q6core.h
new file mode 100644
index 0000000..0b8309a
--- /dev/null
+++ b/include/sound/q6core.h
@@ -0,0 +1,159 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __Q6CORE_H__
+#define __Q6CORE_H__
+#include <linux/qdsp6v2/apr.h>
+
+
+
+#define AVCS_CMD_ADSP_EVENT_GET_STATE		0x0001290C
+#define AVCS_CMDRSP_ADSP_EVENT_GET_STATE	0x0001290D
+
+bool q6core_is_adsp_ready(void);
+
+#define ADSP_CMD_SET_DTS_EAGLE_DATA_ID 0x00012919
+#define DTS_EAGLE_LICENSE_ID           0x00028346
+struct adsp_dts_eagle {
+	struct apr_hdr hdr;
+	uint32_t id;
+	uint32_t overwrite;
+	uint32_t size;
+	char data[];
+};
+int core_dts_eagle_set(int size, char *data);
+int core_dts_eagle_get(int id, int size, char *data);
+
+#define ADSP_CMD_SET_DOLBY_MANUFACTURER_ID 0x00012918
+
+struct adsp_dolby_manufacturer_id {
+	struct apr_hdr hdr;
+	int manufacturer_id;
+};
+
+uint32_t core_set_dolby_manufacturer_id(int manufacturer_id);
+
+/* Dolby Surround1 Module License ID. This ID is used as an identifier
+ * for DS1 license via ADSP generic license mechanism.
+ * Please refer AVCS_CMD_SET_LICENSE for more details.
+ */
+#define DOLBY_DS1_LICENSE_ID	0x00000001
+
+#define AVCS_CMD_SET_LICENSE	0x00012919
+struct avcs_cmd_set_license {
+	struct apr_hdr hdr;
+	uint32_t id; /**< A unique ID used to refer to this license */
+	uint32_t overwrite;
+	/* 0 = do not overwrite an existing license with this id.
+	 * 1 = overwrite an existing license with this id.
+	 */
+	uint32_t size;
+	/**< Size in bytes of the license data following this header. */
+	/* uint8_t* data ,  data and padding follows this structure
+	 * total packet size needs to be multiple of 4 Bytes
+	 */
+
+};
+
+#define AVCS_CMD_GET_LICENSE_VALIDATION_RESULT        0x0001291A
+struct avcs_cmd_get_license_validation_result {
+	struct apr_hdr hdr;
+	uint32_t id; /**< A unique ID used to refer to this license */
+};
+
+#define AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT        0x0001291B
+struct avcs_cmdrsp_get_license_validation_result {
+	uint32_t result;
+	/* ADSP_EOK if the license validation result was successfully retrieved.
+	 * ADSP_ENOTEXIST if there is no license with the given id.
+	 * ADSP_ENOTIMPL if there is no validation function for a license
+	 * with this id.
+	 */
+	uint32_t size;
+	/* Length in bytes of the result that follows this structure*/
+};
+
+/* Set Q6 topologies */
+/*
+ *	Registers custom topologies in the aDSP for
+ *	use in audio, voice, AFE and LSM.
+ */
+
+
+#define AVCS_CMD_SHARED_MEM_MAP_REGIONS                             0x00012924
+#define AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS                          0x00012925
+#define AVCS_CMD_SHARED_MEM_UNMAP_REGIONS                           0x00012926
+
+
+#define AVCS_CMD_REGISTER_TOPOLOGIES                                0x00012923
+
+/* The payload for the AVCS_CMD_REGISTER_TOPOLOGIES command */
+struct avcs_cmd_register_topologies {
+	struct apr_hdr hdr;
+	uint32_t                  payload_addr_lsw;
+	/* Lower 32 bits of the topology buffer address. */
+
+	uint32_t                  payload_addr_msw;
+	/* Upper 32 bits of the topology buffer address. */
+
+	uint32_t                  mem_map_handle;
+	/* Unique identifier for an address.
+	 * -This memory map handle is returned by the aDSP through the
+	 * memory map command.
+	 * -NULL mem_map_handle is interpreted as in-band parameter
+	 * passing.
+	 * -Client has the flexibility to choose in-band or out-of-band.
+	 * -Out-of-band is recommended in this case.
+	 */
+
+	uint32_t                  payload_size;
+	/* Size in bytes of the valid data in the topology buffer. */
+} __packed;
+
+
+#define AVCS_CMD_DEREGISTER_TOPOLOGIES                                0x0001292a
+
+/* The payload for the AVCS_CMD_DEREGISTER_TOPOLOGIES command */
+struct avcs_cmd_deregister_topologies {
+	struct apr_hdr hdr;
+	uint32_t                  payload_addr_lsw;
+	/* Lower 32 bits of the topology buffer address. */
+
+	uint32_t                  payload_addr_msw;
+	/* Upper 32 bits of the topology buffer address. */
+
+	uint32_t                  mem_map_handle;
+	/* Unique identifier for an address.
+	 * -This memory map handle is returned by the aDSP through the
+	 * memory map command.
+	 * -NULL mem_map_handle is interpreted as in-band parameter
+	 * passing.
+	 * -Client has the flexibility to choose in-band or out-of-band.
+	 * -Out-of-band is recommended in this case.
+	 */
+
+	uint32_t                  payload_size;
+	/* Size in bytes of the valid data in the topology buffer. */
+
+	uint32_t                  mode;
+	/* 1: Deregister selected topologies
+	 * 2: Deregister all topologies
+	 */
+} __packed;
+
+#define AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES	2
+
+
+int32_t core_set_license(uint32_t key, uint32_t module_id);
+int32_t core_get_license_status(uint32_t module_id);
+
+#endif /* __Q6CORE_H__ */
diff --git a/include/sound/q6lsm.h b/include/sound/q6lsm.h
new file mode 100644
index 0000000..22a62da
--- /dev/null
+++ b/include/sound/q6lsm.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __Q6LSM_H__
+#define __Q6LSM_H__
+
+#include <linux/list.h>
+#include <linux/msm_ion.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/lsm_params.h>
+#include <linux/qdsp6v2/apr.h>
+
+#define MAX_NUM_CONFIDENCE 20
+
+typedef void (*lsm_app_cb)(uint32_t opcode, uint32_t token,
+		       uint32_t *payload, void *priv);
+
+struct lsm_sound_model {
+	dma_addr_t      phys;
+	void		*data;
+	size_t		size; /* size of buffer */
+	uint32_t	actual_size; /* actual number of bytes read by DSP */
+	struct ion_handle *handle;
+	struct ion_client *client;
+	uint32_t	mem_map_handle;
+};
+
+struct snd_lsm_event_status_v2 {
+	uint16_t status;
+	uint16_t payload_size;
+	uint8_t  confidence_value[0];
+};
+
+struct lsm_lab_buffer {
+	dma_addr_t phys;
+	void *data;
+	size_t size;
+	struct ion_handle *handle;
+	struct ion_client *client;
+	uint32_t mem_map_handle;
+};
+
+struct lsm_lab_hw_params {
+	u16 sample_rate;
+	u16 sample_size;
+	u32 buf_sz;
+	u32 period_count;
+};
+
+struct lsm_client {
+	int		session;
+	lsm_app_cb	cb;
+	atomic_t	cmd_state;
+	void		*priv;
+	struct apr_svc  *apr;
+	struct apr_svc  *mmap_apr;
+	struct mutex    cmd_lock;
+	struct lsm_sound_model sound_model;
+	wait_queue_head_t cmd_wait;
+	uint32_t	cmd_err_code;
+	uint16_t	mode;
+	uint16_t	connect_to_port;
+	uint8_t		num_confidence_levels;
+	uint8_t		*confidence_levels;
+	bool		opened;
+	bool		started;
+	dma_addr_t	lsm_cal_phy_addr;
+	uint32_t	lsm_cal_size;
+	uint32_t	app_id;
+	bool		lab_enable;
+	bool		lab_started;
+	struct lsm_lab_buffer *lab_buffer;
+	struct lsm_lab_hw_params hw_params;
+	bool		use_topology;
+};
+
+struct lsm_stream_cmd_open_tx {
+	struct apr_hdr  hdr;
+	uint16_t	app_id;
+	uint16_t	reserved;
+	uint32_t	sampling_rate;
+} __packed;
+
+struct lsm_stream_cmd_open_tx_v2 {
+	struct apr_hdr hdr;
+	uint32_t	topology_id;
+} __packed;
+
+struct lsm_custom_topologies {
+	struct apr_hdr hdr;
+	uint32_t data_payload_addr_lsw;
+	uint32_t data_payload_addr_msw;
+	uint32_t mem_map_handle;
+	uint32_t buffer_size;
+} __packed;
+
+struct lsm_param_size_reserved {
+	uint16_t param_size;
+	uint16_t reserved;
+} __packed;
+
+union lsm_param_size {
+	uint32_t param_size;
+	struct lsm_param_size_reserved sr;
+} __packed;
+
+struct lsm_param_payload_common {
+	uint32_t	module_id;
+	uint32_t	param_id;
+	union lsm_param_size p_size;
+} __packed;
+
+struct lsm_param_op_mode {
+	struct lsm_param_payload_common common;
+	uint32_t	minor_version;
+	uint16_t	mode;
+	uint16_t	reserved;
+} __packed;
+
+struct lsm_param_connect_to_port {
+	struct lsm_param_payload_common common;
+	uint32_t	minor_version;
+	/* AFE port id that receives voice wake up data */
+	uint16_t	port_id;
+	uint16_t	reserved;
+} __packed;
+
+
+/*
+ * This param cannot be sent in this format.
+ * The actual number of confidence level values
+ * need to appended to this param payload.
+ */
+struct lsm_param_min_confidence_levels {
+	struct lsm_param_payload_common common;
+	uint8_t		num_confidence_levels;
+} __packed;
+
+struct lsm_set_params_hdr {
+	uint32_t	data_payload_size;
+	uint32_t	data_payload_addr_lsw;
+	uint32_t	data_payload_addr_msw;
+	uint32_t	mem_map_handle;
+} __packed;
+
+struct lsm_cmd_set_params {
+	struct apr_hdr  msg_hdr;
+	struct lsm_set_params_hdr param_hdr;
+} __packed;
+
+struct lsm_cmd_set_params_conf {
+	struct apr_hdr  msg_hdr;
+	struct lsm_set_params_hdr params_hdr;
+	struct lsm_param_min_confidence_levels	conf_payload;
+} __packed;
+
+struct lsm_cmd_set_opmode_connectport {
+	struct apr_hdr  msg_hdr;
+	struct lsm_set_params_hdr params_hdr;
+	struct lsm_param_connect_to_port	connect_to_port;
+	struct lsm_param_op_mode		op_mode;
+} __packed;
+
+struct lsm_param_epd_thres {
+	struct lsm_param_payload_common common;
+	uint32_t	minor_version;
+	uint32_t	epd_begin;
+	uint32_t	epd_end;
+} __packed;
+
+struct lsm_cmd_set_epd_threshold {
+	struct apr_hdr msg_hdr;
+	struct lsm_set_params_hdr param_hdr;
+	struct lsm_param_epd_thres epd_thres;
+} __packed;
+
+struct lsm_param_gain {
+	struct lsm_param_payload_common common;
+	uint32_t	minor_version;
+	uint16_t	gain;
+	uint16_t	reserved;
+} __packed;
+
+struct lsm_cmd_set_gain {
+	struct apr_hdr msg_hdr;
+	struct lsm_set_params_hdr param_hdr;
+	struct lsm_param_gain lsm_gain;
+} __packed;
+
+struct lsm_cmd_reg_snd_model {
+	struct apr_hdr	hdr;
+	uint32_t	model_size;
+	uint32_t	model_addr_lsw;
+	uint32_t	model_addr_msw;
+	uint32_t	mem_map_handle;
+} __packed;
+
+struct lsm_lab_enable {
+	struct lsm_param_payload_common common;
+	uint16_t enable;
+	uint16_t reserved;
+} __packed;
+
+struct lsm_params_lab_enable {
+	struct apr_hdr msg_hdr;
+	struct lsm_set_params_hdr params_hdr;
+	struct lsm_lab_enable lab_enable;
+} __packed;
+
+struct lsm_lab_config {
+	struct lsm_param_payload_common common;
+	uint32_t minor_version;
+	uint32_t wake_up_latency_ms;
+} __packed;
+
+
+struct lsm_params_lab_config {
+	struct apr_hdr  msg_hdr;
+	struct lsm_set_params_hdr params_hdr;
+	struct lsm_lab_config lab_config;
+} __packed;
+
+struct lsm_cmd_read {
+	struct apr_hdr hdr;
+	uint32_t buf_addr_lsw;
+	uint32_t buf_addr_msw;
+	uint32_t mem_map_handle;
+	uint32_t buf_size;
+} __packed;
+
+struct lsm_cmd_read_done {
+	struct apr_hdr hdr;
+	uint32_t status;
+	uint32_t buf_addr_lsw;
+	uint32_t buf_addr_msw;
+	uint32_t mem_map_handle;
+	uint32_t total_size;
+	uint32_t offset;
+	uint32_t timestamp_lsw;
+	uint32_t timestamp_msw;
+	uint32_t flags;
+} __packed;
+
+struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv);
+void q6lsm_client_free(struct lsm_client *client);
+int q6lsm_open(struct lsm_client *client, uint16_t app_id);
+int q6lsm_start(struct lsm_client *client, bool wait);
+int q6lsm_stop(struct lsm_client *client, bool wait);
+int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len,
+			      bool allocate_module_data);
+int q6lsm_snd_model_buf_free(struct lsm_client *client);
+int q6lsm_close(struct lsm_client *client);
+int q6lsm_register_sound_model(struct lsm_client *client,
+			       enum lsm_detection_mode mode,
+			       bool detectfailure);
+int q6lsm_set_data(struct lsm_client *client,
+		   enum lsm_detection_mode mode,
+		   bool detectfailure);
+int q6lsm_deregister_sound_model(struct lsm_client *client);
+void set_lsm_port(int lsm_port);
+int get_lsm_port(void);
+int q6lsm_lab_control(struct lsm_client *client, u32 enable);
+int q6lsm_stop_lab(struct lsm_client *client);
+int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read);
+int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc);
+int q6lsm_set_one_param(struct lsm_client *client,
+			struct lsm_params_info *p_info, void *data,
+			enum LSM_PARAM_TYPE param_type);
+void q6lsm_sm_set_param_data(struct lsm_client *client,
+		struct lsm_params_info *p_info,
+		size_t *offset);
+#endif /* __Q6LSM_H__ */
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 1f403c3..71fd438 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -1505,6 +1505,8 @@ int snd_soc_component_update_bits_async(struct snd_soc_component *component,
 void snd_soc_component_async_complete(struct snd_soc_component *component);
 int snd_soc_component_test_bits(struct snd_soc_component *component,
 	unsigned int reg, unsigned int mask, unsigned int value);
+struct snd_soc_component *soc_find_component(
+	const struct device_node *of_node, const char *name);
 
 #ifdef CONFIG_REGMAP
 
diff --git a/include/sound/voice_params.h b/include/sound/voice_params.h
new file mode 100644
index 0000000..43e3b9d
--- /dev/null
+++ b/include/sound/voice_params.h
@@ -0,0 +1,14 @@
+#ifndef __VOICE_PARAMS_H__
+#define __VOICE_PARAMS_H__
+
+#include <linux/types.h>
+#include <sound/asound.h>
+
+enum voice_lch_mode {
+	VOICE_LCH_START = 1,
+	VOICE_LCH_STOP
+};
+
+#define SNDRV_VOICE_IOCTL_LCH _IOW('U', 0x00, enum voice_lch_mode)
+
+#endif
diff --git a/include/sound/voice_svc.h b/include/sound/voice_svc.h
new file mode 100644
index 0000000..035053f
--- /dev/null
+++ b/include/sound/voice_svc.h
@@ -0,0 +1,47 @@
+#ifndef __VOICE_SVC_H__
+#define __VOICE_SVC_H__
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define VOICE_SVC_DRIVER_NAME "voice_svc"
+
+#define VOICE_SVC_MVM_STR "MVM"
+#define VOICE_SVC_CVS_STR "CVS"
+#define MAX_APR_SERVICE_NAME_LEN  64
+
+#define MSG_REGISTER 0x1
+#define MSG_REQUEST  0x2
+#define MSG_RESPONSE 0x3
+
+struct voice_svc_write_msg {
+	__u32 msg_type;
+	__u8 payload[0];
+};
+
+struct voice_svc_register {
+	char svc_name[MAX_APR_SERVICE_NAME_LEN];
+	__u32 src_port;
+	__u8 reg_flag;
+};
+
+struct voice_svc_cmd_response {
+	__u32 src_port;
+	__u32 dest_port;
+	__u32 token;
+	__u32 opcode;
+	__u32 payload_size;
+	__u8 payload[0];
+};
+
+struct voice_svc_cmd_request {
+	char svc_name[MAX_APR_SERVICE_NAME_LEN];
+	__u32 src_port;
+	__u32 dest_port;
+	__u32 token;
+	__u32 opcode;
+	__u32 payload_size;
+	__u8 payload[0];
+};
+
+#endif
diff --git a/include/sound/wcd-dsp-mgr.h b/include/sound/wcd-dsp-mgr.h
new file mode 100644
index 0000000..2beb9b3
--- /dev/null
+++ b/include/sound/wcd-dsp-mgr.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_DSP_MGR_H__
+#define __WCD_DSP_MGR_H__
+
+#include <linux/types.h>
+
+/*
+ * These enums correspond to the component types
+ * that wcd-dsp-manager driver will use. The order
+ * of the enums specifies the order in which the
+ * manager driver will perform the sequencing.
+ * Changing this will cause the sequencing order
+ * to be changed as well.
+ */
+enum wdsp_cmpnt_type {
+	/* Component to control the DSP */
+	WDSP_CMPNT_CONTROL = 0,
+	/* Component to perform data transfer to/from DSP */
+	WDSP_CMPNT_TRANSPORT,
+	/* Component that performs high level IPC */
+	WDSP_CMPNT_IPC,
+
+	WDSP_CMPNT_TYPE_MAX,
+};
+
+enum wdsp_event_type {
+	/* Initialization related */
+	WDSP_EVENT_POST_INIT,
+
+	/* Image download related */
+	WDSP_EVENT_PRE_DLOAD_CODE,
+	WDSP_EVENT_DLOAD_SECTION,
+	WDSP_EVENT_POST_DLOAD_CODE,
+	WDSP_EVENT_PRE_DLOAD_DATA,
+	WDSP_EVENT_POST_DLOAD_DATA,
+	WDSP_EVENT_DLOAD_FAILED,
+
+	WDSP_EVENT_READ_SECTION,
+
+	/* DSP boot related */
+	WDSP_EVENT_PRE_BOOTUP,
+	WDSP_EVENT_DO_BOOT,
+	WDSP_EVENT_POST_BOOTUP,
+	WDSP_EVENT_PRE_SHUTDOWN,
+	WDSP_EVENT_DO_SHUTDOWN,
+	WDSP_EVENT_POST_SHUTDOWN,
+
+	/* IRQ handling related */
+	WDSP_EVENT_IPC1_INTR,
+
+	/* Suspend/Resume related */
+	WDSP_EVENT_SUSPEND,
+	WDSP_EVENT_RESUME,
+};
+
+enum wdsp_signal {
+	/* Hardware generated interrupts signalled to manager */
+	WDSP_IPC1_INTR,
+	WDSP_ERR_INTR,
+
+	/* Other signals */
+	WDSP_CDC_DOWN_SIGNAL,
+	WDSP_CDC_UP_SIGNAL,
+};
+
+/*
+ * wdsp_cmpnt_ops: ops/function callbacks for components
+ * @init: called by manager driver, component is expected
+ *	  to initialize itself in this callback
+ * @deinit: called by manager driver, component should
+ *	    de-initialize itself in this callback
+ * @event_handler: Event handler for each component, called
+ *		   by the manager as per sequence
+ */
+struct wdsp_cmpnt_ops {
+	int (*init)(struct device *, void *priv_data);
+	int (*deinit)(struct device *, void *priv_data);
+	int (*event_handler)(struct device *, void *priv_data,
+			     enum wdsp_event_type, void *data);
+};
+
+struct wdsp_img_section {
+	u32 addr;
+	size_t size;
+	u8 *data;
+};
+
+struct wdsp_err_signal_arg {
+	bool mem_dumps_enabled;
+	u32 remote_start_addr;
+	size_t dump_size;
+};
+
+/*
+ * wdsp_ops: ops/function callbacks for manager driver
+ * @register_cmpnt_ops: components will use this to register
+ *			their own ops to manager driver
+ * @get_dev_for_cmpnt: components can use this to get handle
+ *		       to struct device * of any other component
+ * @signal_handler: callback to notify manager driver that signal
+ *		    has occurred. Cannot be called from interrupt
+ *		    context as this can sleep
+ * @vote_for_dsp: notifies manager that dsp should be booted up
+ * @suspend: notifies manager that one component wants to suspend.
+ *	     Manager will make sure to suspend all components in order
+ * @resume: notifies manager that one component wants to resume.
+ *	    Manager will make sure to resume all components in order
+ */
+
+struct wdsp_mgr_ops {
+	int (*register_cmpnt_ops)(struct device *wdsp_dev,
+				  struct device *cdev,
+				  void *priv_data,
+				  struct wdsp_cmpnt_ops *ops);
+	struct device *(*get_dev_for_cmpnt)(struct device *wdsp_dev,
+					    enum wdsp_cmpnt_type type);
+	int (*signal_handler)(struct device *wdsp_dev,
+			      enum wdsp_signal signal, void *arg);
+	int (*vote_for_dsp)(struct device *wdsp_dev, bool vote);
+	int (*suspend)(struct device *wdsp_dev);
+	int (*resume)(struct device *wdsp_dev);
+};
+
+#endif /* end of __WCD_DSP_MGR_H__ */
diff --git a/include/sound/wcd-spi.h b/include/sound/wcd-spi.h
new file mode 100644
index 0000000..1fff58d
--- /dev/null
+++ b/include/sound/wcd-spi.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_SPI_H__
+#define __WCD_SPI_H__
+
+struct wcd_spi_msg {
+	/*
+	 * Caller's buffer pointer that holds data to
+	 * be transmitted in case of data_write and
+	 * data to be copied to in case of data_read.
+	 */
+	void *data;
+
+	/* Length of data to write/read */
+	size_t len;
+
+	/*
+	 * Address in remote memory to write to
+	 * or read from.
+	 */
+	u32 remote_addr;
+
+	/* Bitmask of flags, currently unused */
+	u32 flags;
+};
+
+#ifdef CONFIG_SND_SOC_WCD_SPI
+
+int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg);
+int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg);
+
+#else
+
+int wcd_spi_data_write(struct spi_device *spi, struct wcd_spi_msg *msg)
+{
+	return -ENODEV;
+}
+
+int wcd_spi_data_read(struct spi_device *spi, struct wcd_spi_msg *msg)
+{
+	return -ENODEV;
+}
+
+#endif /* End of CONFIG_SND_SOC_WCD_SPI */
+
+#endif /* End of __WCD_SPI_H__ */
diff --git a/include/trace/events/msm_vidc.h b/include/trace/events/msm_vidc.h
new file mode 100644
index 0000000..ea698bf
--- /dev/null
+++ b/include/trace/events/msm_vidc.h
@@ -0,0 +1,315 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM msm_vidc
+
+#if !defined(_TRACE_MSM_VIDC_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_MSM_VIDC_H
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+DECLARE_EVENT_CLASS(msm_v4l2_vidc,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy),
+
+	TP_STRUCT__entry(
+		__field(char *, dummy)
+	),
+
+	TP_fast_assign(
+		__entry->dummy = dummy;
+	),
+
+	TP_printk("%s", __entry->dummy)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_open_start,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_open_end,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_close_start,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_close_end,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_fw_load_start,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc, msm_v4l2_vidc_fw_load_end,
+
+	TP_PROTO(char *dummy),
+
+	TP_ARGS(dummy)
+);
+
+DECLARE_EVENT_CLASS(msm_vidc_common,
+
+	TP_PROTO(void *instp, int old_state, int new_state),
+
+	TP_ARGS(instp, old_state, new_state),
+
+	TP_STRUCT__entry(
+		__field(void *, instp)
+		__field(int, old_state)
+		__field(int, new_state)
+	),
+
+	TP_fast_assign(
+		__entry->instp = instp;
+		__entry->old_state = old_state;
+		__entry->new_state = new_state;
+	),
+
+	TP_printk("Moved inst: %p from 0x%x to 0x%x",
+		__entry->instp,
+		__entry->old_state,
+		__entry->new_state)
+);
+
+DEFINE_EVENT(msm_vidc_common, msm_vidc_common_state_change,
+
+	TP_PROTO(void *instp, int old_state, int new_state),
+
+	TP_ARGS(instp, old_state, new_state)
+);
+
+DECLARE_EVENT_CLASS(venus_hfi_var,
+
+	TP_PROTO(u32 cp_start, u32 cp_size,
+		u32 cp_nonpixel_start, u32 cp_nonpixel_size),
+
+	TP_ARGS(cp_start, cp_size, cp_nonpixel_start, cp_nonpixel_size),
+
+	TP_STRUCT__entry(
+		__field(u32, cp_start)
+		__field(u32, cp_size)
+		__field(u32, cp_nonpixel_start)
+		__field(u32, cp_nonpixel_size)
+	),
+
+	TP_fast_assign(
+		__entry->cp_start = cp_start;
+		__entry->cp_size = cp_size;
+		__entry->cp_nonpixel_start = cp_nonpixel_start;
+		__entry->cp_nonpixel_size = cp_nonpixel_size;
+	),
+
+	TP_printk(
+		"TZBSP_MEM_PROTECT_VIDEO_VAR done, cp_start : 0x%x, cp_size : 0x%x, cp_nonpixel_start : 0x%x, cp_nonpixel_size : 0x%x",
+		__entry->cp_start,
+		__entry->cp_size,
+		__entry->cp_nonpixel_start,
+		__entry->cp_nonpixel_size)
+);
+
+DEFINE_EVENT(venus_hfi_var, venus_hfi_var_done,
+
+	TP_PROTO(u32 cp_start, u32 cp_size,
+		u32 cp_nonpixel_start, u32 cp_nonpixel_size),
+
+	TP_ARGS(cp_start, cp_size, cp_nonpixel_start, cp_nonpixel_size)
+);
+
+DECLARE_EVENT_CLASS(msm_v4l2_vidc_buffer_events,
+
+	TP_PROTO(char *event_type, u32 device_addr, int64_t timestamp,
+		u32 alloc_len, u32 filled_len, u32 offset),
+
+	TP_ARGS(event_type, device_addr, timestamp, alloc_len,
+		filled_len, offset),
+
+	TP_STRUCT__entry(
+		__field(char *, event_type)
+		__field(u32, device_addr)
+		__field(int64_t, timestamp)
+		__field(u32, alloc_len)
+		__field(u32, filled_len)
+		__field(u32, offset)
+	),
+
+	TP_fast_assign(
+		__entry->event_type = event_type;
+		__entry->device_addr = device_addr;
+		__entry->timestamp = timestamp;
+		__entry->alloc_len = alloc_len;
+		__entry->filled_len = filled_len;
+		__entry->offset = offset;
+	),
+
+	TP_printk(
+		"%s, device_addr : 0x%x, timestamp : %lld, alloc_len : 0x%x, filled_len : 0x%x, offset : 0x%x",
+		__entry->event_type,
+		__entry->device_addr,
+		__entry->timestamp,
+		__entry->alloc_len,
+		__entry->filled_len,
+		__entry->offset)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc_buffer_events, msm_v4l2_vidc_buffer_event_start,
+
+	TP_PROTO(char *event_type, u32 device_addr, int64_t timestamp,
+		u32 alloc_len, u32 filled_len, u32 offset),
+
+	TP_ARGS(event_type, device_addr, timestamp, alloc_len,
+		filled_len, offset)
+);
+
+DEFINE_EVENT(msm_v4l2_vidc_buffer_events, msm_v4l2_vidc_buffer_event_end,
+
+	TP_PROTO(char *event_type, u32 device_addr, int64_t timestamp,
+		u32 alloc_len, u32 filled_len, u32 offset),
+
+	TP_ARGS(event_type, device_addr, timestamp, alloc_len,
+		filled_len, offset)
+);
+
+DECLARE_EVENT_CLASS(msm_smem_buffer_ion_ops,
+
+	TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask,
+		size_t size, u32 align, u32 flags, int map_kernel),
+
+	TP_ARGS(buffer_op, buffer_type, heap_mask, size, align,
+		flags, map_kernel),
+
+	TP_STRUCT__entry(
+		__field(char *, buffer_op)
+		__field(u32, buffer_type)
+		__field(u32, heap_mask)
+		__field(u32, size)
+		__field(u32, align)
+		__field(u32, flags)
+		__field(int, map_kernel)
+	),
+
+	TP_fast_assign(
+		__entry->buffer_op = buffer_op;
+		__entry->buffer_type = buffer_type;
+		__entry->heap_mask = heap_mask;
+		__entry->size = size;
+		__entry->align = align;
+		__entry->flags = flags;
+		__entry->map_kernel = map_kernel;
+	),
+
+	TP_printk(
+		"%s, buffer_type : 0x%x, heap_mask : 0x%x, size : 0x%x, align : 0x%x, flags : 0x%x, map_kernel : %d",
+		__entry->buffer_op,
+		__entry->buffer_type,
+		__entry->heap_mask,
+		__entry->size,
+		__entry->align,
+		__entry->flags,
+		__entry->map_kernel)
+);
+
+DEFINE_EVENT(msm_smem_buffer_ion_ops, msm_smem_buffer_ion_op_start,
+
+	TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask,
+		size_t size, u32 align, u32 flags, int map_kernel),
+
+	TP_ARGS(buffer_op, buffer_type, heap_mask, size, align,
+		flags, map_kernel)
+);
+
+DEFINE_EVENT(msm_smem_buffer_ion_ops, msm_smem_buffer_ion_op_end,
+
+	TP_PROTO(char *buffer_op, u32 buffer_type, u32 heap_mask,
+		size_t size, u32 align, u32 flags, int map_kernel),
+
+	TP_ARGS(buffer_op, buffer_type, heap_mask, size, align,
+		flags, map_kernel)
+);
+
+DECLARE_EVENT_CLASS(msm_smem_buffer_iommu_ops,
+
+	TP_PROTO(char *buffer_op, int domain_num, int partition_num,
+		unsigned long align, unsigned long iova,
+		unsigned long buffer_size),
+
+	TP_ARGS(buffer_op, domain_num, partition_num, align, iova, buffer_size),
+
+	TP_STRUCT__entry(
+		__field(char *, buffer_op)
+		__field(int, domain_num)
+		__field(int, partition_num)
+		__field(unsigned long, align)
+		__field(unsigned long, iova)
+		__field(unsigned long, buffer_size)
+	),
+
+	TP_fast_assign(
+		__entry->buffer_op = buffer_op;
+		__entry->domain_num = domain_num;
+		__entry->partition_num = partition_num;
+		__entry->align = align;
+		__entry->iova = iova;
+		__entry->buffer_size = buffer_size;
+	),
+
+	TP_printk(
+		"%s, domain : %d, partition : %d, align : %lx, iova : 0x%lx, buffer_size=%lx",
+		__entry->buffer_op,
+		__entry->domain_num,
+		__entry->partition_num,
+		__entry->align,
+		__entry->iova,
+		__entry->buffer_size)
+);
+
+DEFINE_EVENT(msm_smem_buffer_iommu_ops, msm_smem_buffer_iommu_op_start,
+
+	TP_PROTO(char *buffer_op, int domain_num, int partition_num,
+		unsigned long align, unsigned long iova,
+		unsigned long buffer_size),
+
+	TP_ARGS(buffer_op, domain_num, partition_num, align, iova, buffer_size)
+);
+
+DEFINE_EVENT(msm_smem_buffer_iommu_ops, msm_smem_buffer_iommu_op_end,
+
+	TP_PROTO(char *buffer_op, int domain_num, int partition_num,
+		unsigned long align, unsigned long iova,
+		unsigned long buffer_size),
+
+	TP_ARGS(buffer_op, domain_num, partition_num, align, iova, buffer_size)
+);
+
+#endif
+
+#include <trace/define_trace.h>
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index a52c343..3f0b3df 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -133,6 +133,9 @@ TRACE_EVENT(sched_task_load,
 		__field(	u32,	flags			)
 		__field(	int,	best_cpu		)
 		__field(	u64,	latency			)
+		__field(	int,	grp_id			)
+		__field(	u64,	avg_burst		)
+		__field(	u64,	avg_sleep		)
 	),
 
 	TP_fast_assign(
@@ -148,13 +151,17 @@ TRACE_EVENT(sched_task_load,
 		__entry->latency	= p->state == TASK_WAKING ?
 						      sched_ktime_clock() -
 						      p->ravg.mark_start : 0;
+		__entry->grp_id		= p->grp ? p->grp->id : 0;
+		__entry->avg_burst	= p->ravg.avg_burst;
+		__entry->avg_sleep	= p->ravg.avg_sleep_time;
 	),
 
-	TP_printk("%d (%s): demand=%u boost=%d reason=%d sync=%d need_idle=%d flags=%x best_cpu=%d latency=%llu",
+	TP_printk("%d (%s): demand=%u boost=%d reason=%d sync=%d need_idle=%d flags=%x grp=%d best_cpu=%d latency=%llu avg_burst=%llu avg_sleep=%llu",
 		__entry->pid, __entry->comm, __entry->demand,
 		__entry->boost, __entry->reason, __entry->sync,
-		__entry->need_idle, __entry->flags,
-		__entry->best_cpu, __entry->latency)
+		__entry->need_idle, __entry->flags, __entry->grp_id,
+		__entry->best_cpu, __entry->latency, __entry->avg_burst,
+		__entry->avg_sleep)
 );
 
 TRACE_EVENT(sched_set_preferred_cluster,
@@ -164,9 +171,12 @@ TRACE_EVENT(sched_set_preferred_cluster,
 	TP_ARGS(grp, total_demand),
 
 	TP_STRUCT__entry(
-		__field(		int,	id			)
-		__field(		u64,	demand			)
-		__field(		int,	cluster_first_cpu	)
+		__field(	int,	id			)
+		__field(	u64,	demand			)
+		__field(	int,	cluster_first_cpu	)
+		__array(	char,	comm,	TASK_COMM_LEN	)
+		__field(	pid_t,	pid			)
+		__field(unsigned int,	task_demand			)
 	),
 
 	TP_fast_assign(
@@ -245,21 +255,94 @@ DEFINE_EVENT(sched_cpu_load, sched_cpu_load_cgroup,
 
 TRACE_EVENT(sched_set_boost,
 
-	TP_PROTO(int ref_count),
+	TP_PROTO(int type),
 
-	TP_ARGS(ref_count),
+	TP_ARGS(type),
 
 	TP_STRUCT__entry(
-		__field(unsigned int, ref_count			)
+		__field(int, type			)
 	),
 
 	TP_fast_assign(
-		__entry->ref_count = ref_count;
+		__entry->type = type;
 	),
 
-	TP_printk("ref_count=%d", __entry->ref_count)
+	TP_printk("type %d", __entry->type)
 );
 
+#if defined(CREATE_TRACE_POINTS) && defined(CONFIG_SCHED_HMP)
+static inline void __window_data(u32 *dst, u32 *src)
+{
+	if (src)
+		memcpy(dst, src, nr_cpu_ids * sizeof(u32));
+	else
+		memset(dst, 0, nr_cpu_ids * sizeof(u32));
+}
+
+struct trace_seq;
+const char *__window_print(struct trace_seq *p, const u32 *buf, int buf_len)
+{
+	int i;
+	const char *ret = p->buffer + seq_buf_used(&p->seq);
+
+	for (i = 0; i < buf_len; i++)
+		trace_seq_printf(p, "%u ", buf[i]);
+
+	trace_seq_putc(p, 0);
+
+	return ret;
+}
+
+static inline s64 __rq_update_sum(struct rq *rq, bool curr, bool new)
+{
+	if (curr)
+		if (new)
+			return rq->nt_curr_runnable_sum;
+		else
+			return rq->curr_runnable_sum;
+	else
+		if (new)
+			return rq->nt_prev_runnable_sum;
+		else
+			return rq->prev_runnable_sum;
+}
+
+static inline s64 __grp_update_sum(struct rq *rq, bool curr, bool new)
+{
+	if (curr)
+		if (new)
+			return rq->grp_time.nt_curr_runnable_sum;
+		else
+			return rq->grp_time.curr_runnable_sum;
+	else
+		if (new)
+			return rq->grp_time.nt_prev_runnable_sum;
+		else
+			return rq->grp_time.prev_runnable_sum;
+}
+
+static inline s64
+__get_update_sum(struct rq *rq, enum migrate_types migrate_type,
+		 bool src, bool new, bool curr)
+{
+	switch (migrate_type) {
+	case RQ_TO_GROUP:
+		if (src)
+			return __rq_update_sum(rq, curr, new);
+		else
+			return __grp_update_sum(rq, curr, new);
+	case GROUP_TO_RQ:
+		if (src)
+			return __grp_update_sum(rq, curr, new);
+		else
+			return __rq_update_sum(rq, curr, new);
+	default:
+		WARN_ON_ONCE(1);
+		return -1;
+	}
+}
+#endif
+
 TRACE_EVENT(sched_update_task_ravg,
 
 	TP_PROTO(struct task_struct *p, struct rq *rq, enum task_event evt,
@@ -288,13 +371,17 @@ TRACE_EVENT(sched_update_task_ravg,
 		__field(	u64,	rq_ps			)
 		__field(	u64,	grp_cs			)
 		__field(	u64,	grp_ps			)
-		__field(	u64,	grp_nt_cs			)
-		__field(	u64,	grp_nt_ps			)
+		__field(	u64,	grp_nt_cs		)
+		__field(	u64,	grp_nt_ps		)
 		__field(	u32,	curr_window		)
 		__field(	u32,	prev_window		)
+		__dynamic_array(u32,	curr_sum, nr_cpu_ids	)
+		__dynamic_array(u32,	prev_sum, nr_cpu_ids	)
 		__field(	u64,	nt_cs			)
 		__field(	u64,	nt_ps			)
 		__field(	u32,	active_windows		)
+		__field(	u8,	curr_top		)
+		__field(	u8,	prev_top		)
 	),
 
 	TP_fast_assign(
@@ -321,22 +408,30 @@ TRACE_EVENT(sched_update_task_ravg,
 		__entry->grp_nt_ps = cpu_time ? cpu_time->nt_prev_runnable_sum : 0;
 		__entry->curr_window	= p->ravg.curr_window;
 		__entry->prev_window	= p->ravg.prev_window;
+		__window_data(__get_dynamic_array(curr_sum), p->ravg.curr_window_cpu);
+		__window_data(__get_dynamic_array(prev_sum), p->ravg.prev_window_cpu);
 		__entry->nt_cs		= rq->nt_curr_runnable_sum;
 		__entry->nt_ps		= rq->nt_prev_runnable_sum;
 		__entry->active_windows	= p->ravg.active_windows;
+		__entry->curr_top	= rq->curr_top;
+		__entry->prev_top	= rq->prev_top;
 	),
 
-	TP_printk("wc %llu ws %llu delta %llu event %s cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu pred_demand %u rq_cs %llu rq_ps %llu cur_window %u prev_window %u nt_cs %llu nt_ps %llu active_wins %u grp_cs %lld grp_ps %lld, grp_nt_cs %llu, grp_nt_ps: %llu"
-		, __entry->wallclock, __entry->win_start, __entry->delta,
+	TP_printk("wc %llu ws %llu delta %llu event %s cpu %d cur_freq %u cur_pid %d task %d (%s) ms %llu delta %llu demand %u sum %u irqtime %llu pred_demand %u rq_cs %llu rq_ps %llu cur_window %u (%s) prev_window %u (%s) nt_cs %llu nt_ps %llu active_wins %u grp_cs %lld grp_ps %lld, grp_nt_cs %llu, grp_nt_ps: %llu curr_top %u prev_top %u",
+		__entry->wallclock, __entry->win_start, __entry->delta,
 		task_event_names[__entry->evt], __entry->cpu,
 		__entry->cur_freq, __entry->cur_pid,
 		__entry->pid, __entry->comm, __entry->mark_start,
 		__entry->delta_m, __entry->demand,
 		__entry->sum, __entry->irqtime, __entry->pred_demand,
 		__entry->rq_cs, __entry->rq_ps, __entry->curr_window,
-		__entry->prev_window, __entry->nt_cs, __entry->nt_ps,
+		__window_print(p, __get_dynamic_array(curr_sum), nr_cpu_ids),
+		__entry->prev_window,
+		__window_print(p, __get_dynamic_array(prev_sum), nr_cpu_ids),
+		__entry->nt_cs, __entry->nt_ps,
 		__entry->active_windows, __entry->grp_cs,
-		__entry->grp_ps, __entry->grp_nt_cs, __entry->grp_nt_ps)
+		__entry->grp_ps, __entry->grp_nt_cs, __entry->grp_nt_ps,
+		__entry->curr_top, __entry->prev_top)
 );
 
 TRACE_EVENT(sched_get_task_cpu_cycles,
@@ -485,17 +580,13 @@ TRACE_EVENT(sched_update_pred_demand,
 
 TRACE_EVENT(sched_migration_update_sum,
 
-	TP_PROTO(struct task_struct *p, enum migrate_types migrate_type, struct migration_sum_data *d),
+	TP_PROTO(struct task_struct *p, enum migrate_types migrate_type, struct rq *rq),
 
-	TP_ARGS(p, migrate_type, d),
+	TP_ARGS(p, migrate_type, rq),
 
 	TP_STRUCT__entry(
 		__field(int,		tcpu			)
 		__field(int,		pid			)
-		__field(	u64,	cs			)
-		__field(	u64,	ps			)
-		__field(	s64,	nt_cs			)
-		__field(	s64,	nt_ps			)
 		__field(enum migrate_types,	migrate_type	)
 		__field(	s64,	src_cs			)
 		__field(	s64,	src_ps			)
@@ -511,30 +602,22 @@ TRACE_EVENT(sched_migration_update_sum,
 		__entry->tcpu		= task_cpu(p);
 		__entry->pid		= p->pid;
 		__entry->migrate_type	= migrate_type;
-		__entry->src_cs		= d->src_rq ?
-						d->src_rq->curr_runnable_sum :
-						d->src_cpu_time->curr_runnable_sum;
-		__entry->src_ps		= d->src_rq ?
-						d->src_rq->prev_runnable_sum :
-						d->src_cpu_time->prev_runnable_sum;
-		__entry->dst_cs		= d->dst_rq ?
-						d->dst_rq->curr_runnable_sum :
-						d->dst_cpu_time->curr_runnable_sum;
-		__entry->dst_ps		= d->dst_rq ?
-						d->dst_rq->prev_runnable_sum :
-						d->dst_cpu_time->prev_runnable_sum;
-		__entry->src_nt_cs		= d->src_rq ?
-						d->src_rq->nt_curr_runnable_sum :
-						d->src_cpu_time->nt_curr_runnable_sum;
-		__entry->src_nt_ps		= d->src_rq ?
-						d->src_rq->nt_prev_runnable_sum :
-						d->src_cpu_time->nt_prev_runnable_sum;
-		__entry->dst_nt_cs		= d->dst_rq ?
-						d->dst_rq->nt_curr_runnable_sum :
-						d->dst_cpu_time->nt_curr_runnable_sum;
-		__entry->dst_nt_ps		= d->dst_rq ?
-						d->dst_rq->nt_prev_runnable_sum :
-						d->dst_cpu_time->nt_prev_runnable_sum;
+		__entry->src_cs		= __get_update_sum(rq, migrate_type,
+							   true, false, true);
+		__entry->src_ps		= __get_update_sum(rq, migrate_type,
+							   true, false, false);
+		__entry->dst_cs		= __get_update_sum(rq, migrate_type,
+							   false, false, true);
+		__entry->dst_ps		= __get_update_sum(rq, migrate_type,
+							   false, false, false);
+		__entry->src_nt_cs	= __get_update_sum(rq, migrate_type,
+							   true, true, true);
+		__entry->src_nt_ps	= __get_update_sum(rq, migrate_type,
+							   true, true, false);
+		__entry->dst_nt_cs	= __get_update_sum(rq, migrate_type,
+							   false, true, true);
+		__entry->dst_nt_ps	= __get_update_sum(rq, migrate_type,
+							   false, true, false);
 	),
 
 	TP_printk("pid %d task_cpu %d migrate_type %s src_cs %llu src_ps %llu dst_cs %lld dst_ps %lld src_nt_cs %llu src_nt_ps %llu dst_nt_cs %lld dst_nt_ps %lld",
@@ -1242,6 +1325,100 @@ TRACE_EVENT(sched_get_nr_running_avg,
 	TP_printk("avg=%d big_avg=%d iowait_avg=%d",
 		__entry->avg, __entry->big_avg, __entry->iowait_avg)
 );
+
+TRACE_EVENT(core_ctl_eval_need,
+
+	TP_PROTO(unsigned int cpu, unsigned int old_need,
+		 unsigned int new_need, unsigned int updated),
+	TP_ARGS(cpu, old_need, new_need, updated),
+	TP_STRUCT__entry(
+		__field(u32, cpu)
+		__field(u32, old_need)
+		__field(u32, new_need)
+		__field(u32, updated)
+	),
+	TP_fast_assign(
+		__entry->cpu = cpu;
+		__entry->old_need = old_need;
+		__entry->new_need = new_need;
+		__entry->updated = updated;
+	),
+	TP_printk("cpu=%u, old_need=%u, new_need=%u, updated=%u", __entry->cpu,
+		  __entry->old_need, __entry->new_need, __entry->updated)
+);
+
+TRACE_EVENT(core_ctl_set_busy,
+
+	TP_PROTO(unsigned int cpu, unsigned int busy,
+		 unsigned int old_is_busy, unsigned int is_busy),
+	TP_ARGS(cpu, busy, old_is_busy, is_busy),
+	TP_STRUCT__entry(
+		__field(u32, cpu)
+		__field(u32, busy)
+		__field(u32, old_is_busy)
+		__field(u32, is_busy)
+	),
+	TP_fast_assign(
+		__entry->cpu = cpu;
+		__entry->busy = busy;
+		__entry->old_is_busy = old_is_busy;
+		__entry->is_busy = is_busy;
+	),
+	TP_printk("cpu=%u, busy=%u, old_is_busy=%u, new_is_busy=%u",
+		  __entry->cpu, __entry->busy, __entry->old_is_busy,
+		  __entry->is_busy)
+);
+
+TRACE_EVENT(core_ctl_set_boost,
+
+	TP_PROTO(u32 refcount, s32 ret),
+	TP_ARGS(refcount, ret),
+	TP_STRUCT__entry(
+		__field(u32, refcount)
+		__field(s32, ret)
+	),
+	TP_fast_assign(
+		__entry->refcount = refcount;
+		__entry->ret = ret;
+	),
+	TP_printk("refcount=%u, ret=%d", __entry->refcount, __entry->ret)
+);
+
+/*
+ * sched_isolate - called when cores are isolated/unisolated
+ *
+ * @acutal_mask: mask of cores actually isolated/unisolated
+ * @req_mask: mask of cores requested isolated/unisolated
+ * @online_mask: cpu online mask
+ * @time: amount of time in us it took to isolate/unisolate
+ * @isolate: 1 if isolating, 0 if unisolating
+ *
+ */
+TRACE_EVENT(sched_isolate,
+
+	TP_PROTO(unsigned int requested_cpu, unsigned int isolated_cpus,
+		 u64 start_time, unsigned char isolate),
+
+	TP_ARGS(requested_cpu, isolated_cpus, start_time, isolate),
+
+	TP_STRUCT__entry(
+		__field(u32, requested_cpu)
+		__field(u32, isolated_cpus)
+		__field(u32, time)
+		__field(unsigned char, isolate)
+	),
+
+	TP_fast_assign(
+		__entry->requested_cpu = requested_cpu;
+		__entry->isolated_cpus = isolated_cpus;
+		__entry->time = div64_u64(sched_clock() - start_time, 1000);
+		__entry->isolate = isolate;
+	),
+
+	TP_printk("iso cpu=%u cpus=0x%x time=%u us isolated=%d",
+		  __entry->requested_cpu, __entry->isolated_cpus,
+		  __entry->time, __entry->isolate)
+);
 #endif /* _TRACE_SCHED_H */
 
 /* This part must be outside protection */
diff --git a/include/uapi/Kbuild b/include/uapi/Kbuild
index 245aa6e..aeb3366 100644
--- a/include/uapi/Kbuild
+++ b/include/uapi/Kbuild
@@ -13,3 +13,4 @@
 header-y += xen/
 header-y += scsi/
 header-y += misc/
+header-y += media/
diff --git a/include/uapi/drm/msm_drm_pp.h b/include/uapi/drm/msm_drm_pp.h
index 9ed3a13..943940e 100644
--- a/include/uapi/drm/msm_drm_pp.h
+++ b/include/uapi/drm/msm_drm_pp.h
@@ -1,8 +1,7 @@
 #ifndef _MSM_DRM_PP_H_
 #define _MSM_DRM_PP_H_
 
-#include <drm/drm.h>
-
+#include <linux/types.h>
 /**
  * struct drm_msm_pcc_coeff - PCC coefficient structure for each color
  *                            component.
@@ -79,4 +78,60 @@ struct drm_msm_memcol {
 	__u32 val_region;
 };
 
+#define GAMUT_3D_MODE_17 1
+#define GAMUT_3D_MODE_5 2
+#define GAMUT_3D_MODE_13 3
+
+#define GAMUT_3D_MODE17_TBL_SZ 1229
+#define GAMUT_3D_MODE5_TBL_SZ 32
+#define GAMUT_3D_MODE13_TBL_SZ 550
+#define GAMUT_3D_SCALE_OFF_SZ 16
+#define GAMUT_3D_TBL_NUM 4
+#define GAMUT_3D_SCALE_OFF_TBL_NUM 3
+#define GAMUT_3D_MAP_EN (1 << 0)
+
+/**
+ * struct drm_msm_3d_col - 3d gamut color component structure
+ * @c0: Holds c0 value
+ * @c2_c1: Holds c2/c1 values
+ */
+struct drm_msm_3d_col {
+	__u32 c0;
+	__u32 c2_c1;
+};
+/**
+ * struct drm_msm_3d_gamut - 3d gamut feature structure
+ * @flags: flags for the feature values are:
+ *         0 - no map
+ *         GAMUT_3D_MAP_EN - enable map
+ * @mode: lut mode can take following values:
+ *        - GAMUT_3D_MODE_17
+ *        - GAMUT_3D_MODE_5
+ *        - GAMUT_3D_MODE_13
+ * @scale_off: Scale offset table
+ * @col: Color component tables
+ */
+struct drm_msm_3d_gamut {
+	__u64 flags;
+	__u32 mode;
+	__u32 scale_off[GAMUT_3D_SCALE_OFF_TBL_NUM][GAMUT_3D_SCALE_OFF_SZ];
+	struct drm_msm_3d_col col[GAMUT_3D_TBL_NUM][GAMUT_3D_MODE17_TBL_SZ];
+};
+
+#define PGC_TBL_LEN 512
+#define PGC_8B_ROUND (1 << 0)
+/**
+ * struct drm_msm_pgc_lut - pgc lut feature structure
+ * @flags: flags for the featue values can be:
+ *         - PGC_8B_ROUND
+ * @c0: color0 component lut
+ * @c1: color1 component lut
+ * @c2: color2 component lut
+ */
+struct drm_msm_pgc_lut {
+	__u64 flags;
+	__u32 c0[PGC_TBL_LEN];
+	__u32 c1[PGC_TBL_LEN];
+	__u32 c2[PGC_TBL_LEN];
+};
 #endif /* _MSM_DRM_PP_H_ */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index ae6fa2f..f2b39c3 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -22,6 +22,7 @@
 header-y += netfilter_ipv6/
 header-y += usb/
 header-y += wimax/
+header-y += mfd/
 header-y += msm_ipa.h
 genhdr-y += version.h
 
@@ -62,6 +63,7 @@
 header-y += auto_fs4.h
 header-y += auto_fs.h
 header-y += auxvec.h
+header-y += avtimer.h
 header-y += ax25.h
 header-y += b1lli.h
 header-y += baycom.h
@@ -281,6 +283,23 @@
 header-y += mroute.h
 header-y += msdos_fs.h
 header-y += msg.h
+header-y += msm_audio.h
+header-y += msm_audio_aac.h
+header-y += msm_audio_ac3.h
+header-y += msm_audio_amrnb.h
+header-y += msm_audio_amrwb.h
+header-y += msm_audio_amrwbplus.h
+header-y += msm_audio_calibration.h
+header-y += msm_audio_mvs.h
+header-y += msm_audio_qcp.h
+header-y += msm_audio_sbc.h
+header-y += msm_audio_voicememo.h
+header-y += msm_audio_wma.h
+header-y += msm_audio_wmapro.h
+header-y += msm_audio_alac.h
+header-y += msm_audio_ape.h
+header-y += msm_audio_g711.h
+header-y += msm_audio_g711_dec.h
 header-y += msm_ion.h
 header-y += msm_ipc.h
 header-y += msm_kgsl.h
@@ -403,6 +422,7 @@
 header-y += sonypi.h
 header-y += soundcard.h
 header-y += sound.h
+header-y += spcom.h
 header-y += stat.h
 header-y += stddef.h
 header-y += string.h
diff --git a/include/uapi/linux/avtimer.h b/include/uapi/linux/avtimer.h
new file mode 100644
index 0000000..96b5483
--- /dev/null
+++ b/include/uapi/linux/avtimer.h
@@ -0,0 +1,10 @@
+#ifndef _UAPI_AVTIMER_H
+#define _UAPI_AVTIMER_H
+
+#include <linux/ioctl.h>
+
+#define MAJOR_NUM 100
+
+#define IOCTL_GET_AVTIMER_TICK _IOR(MAJOR_NUM, 0, uint64_t)
+
+#endif
diff --git a/include/uapi/linux/mfd/Kbuild b/include/uapi/linux/mfd/Kbuild
new file mode 100644
index 0000000..b398179
--- /dev/null
+++ b/include/uapi/linux/mfd/Kbuild
@@ -0,0 +1,2 @@
+header-y += msm-adie-codec.h
+header-y += wcd9xxx/
diff --git a/include/uapi/linux/mfd/msm-adie-codec.h b/include/uapi/linux/mfd/msm-adie-codec.h
new file mode 100644
index 0000000..3f523c0
--- /dev/null
+++ b/include/uapi/linux/mfd/msm-adie-codec.h
@@ -0,0 +1,147 @@
+#ifndef __UAPI_MFD_MSM_ADIE_CODEC_H
+#define __UAPI_MFD_MSM_ADIE_CODEC_H
+
+#include <linux/types.h>
+
+/* Value Represents a entry */
+#define ADIE_CODEC_ACTION_ENTRY       0x1
+/* Value representing a delay wait */
+#define ADIE_CODEC_ACTION_DELAY_WAIT      0x2
+/* Value representing a stage reached */
+#define ADIE_CODEC_ACTION_STAGE_REACHED   0x3
+
+/* This value is the state after the client sets the path */
+#define ADIE_CODEC_PATH_OFF                                        0x0050
+
+/* State to which client asks the drv to proceed to where it can
+ * set up the clocks and 0-fill PCM buffers
+ */
+#define ADIE_CODEC_DIGITAL_READY                                   0x0100
+
+/* State to which client asks the drv to proceed to where it can
+ * start sending data after internal steady state delay
+ */
+#define ADIE_CODEC_DIGITAL_ANALOG_READY                            0x1000
+
+
+/*  Client Asks adie to switch off the Analog portion of the
+ *  the internal codec. After the use of this path
+ */
+#define ADIE_CODEC_ANALOG_OFF                                      0x0750
+
+
+/* Client Asks adie to switch off the digital portion of the
+ *  the internal codec. After switching off the analog portion.
+ *
+ *  0-fill PCM may or maynot be sent at this point
+ *
+ */
+#define ADIE_CODEC_DIGITAL_OFF                                     0x0600
+
+/* State to which client asks the drv to write the default values
+ * to the registers
+ */
+#define ADIE_CODEC_FLASH_IMAGE                                     0x0001
+
+/* Path type */
+#define ADIE_CODEC_RX 0
+#define ADIE_CODEC_TX 1
+#define ADIE_CODEC_LB 3
+#define ADIE_CODEC_MAX 4
+
+#define ADIE_CODEC_PACK_ENTRY(reg, mask, val) ((val)|(mask << 8)|(reg << 16))
+
+#define ADIE_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+	do { \
+		((reg) = ((packed >> 16) & (0xff))); \
+		((mask) = ((packed >> 8) & (0xff))); \
+		((val) = ((packed) & (0xff))); \
+	} while (0);
+
+struct adie_codec_action_unit {
+	u32 type;
+	u32 action;
+};
+
+struct adie_codec_hwsetting_entry {
+	struct adie_codec_action_unit *actions;
+	u32 action_sz;
+	u32 freq_plan;
+	u32 osr;
+	/* u32  VolMask;
+	 * u32  SidetoneMask;
+	 */
+};
+
+struct adie_codec_dev_profile {
+	u32 path_type; /* RX or TX */
+	u32 setting_sz;
+	struct adie_codec_hwsetting_entry *settings;
+};
+
+struct adie_codec_register {
+	u8 reg;
+	u8 mask;
+	u8 val;
+};
+
+struct adie_codec_register_image {
+	struct adie_codec_register *regs;
+	u32 img_sz;
+};
+
+struct adie_codec_path;
+
+struct adie_codec_anc_data {
+	u32 size;
+	u32 writes[];
+};
+
+struct adie_codec_operations {
+	int	 codec_id;
+	int (*codec_open)(struct adie_codec_dev_profile *profile,
+				struct adie_codec_path **path_pptr);
+	int (*codec_close)(struct adie_codec_path *path_ptr);
+	int (*codec_setpath)(struct adie_codec_path *path_ptr,
+				u32 freq_plan, u32 osr);
+	int (*codec_proceed_stage)(struct adie_codec_path *path_ptr,
+					u32 state);
+	u32 (*codec_freq_supported)(struct adie_codec_dev_profile *profile,
+					u32 requested_freq);
+	int (*codec_enable_sidetone)(struct adie_codec_path *rx_path_ptr,
+					u32 enable);
+	int (*codec_enable_anc)(struct adie_codec_path *rx_path_ptr,
+		u32 enable, struct adie_codec_anc_data *calibration_writes);
+	int (*codec_set_device_digital_volume)(
+					struct adie_codec_path *path_ptr,
+					u32 num_channels,
+					u32 vol_percentage);
+
+	int (*codec_set_device_analog_volume)(struct adie_codec_path *path_ptr,
+						u32 num_channels,
+						u32 volume);
+	int (*codec_set_master_mode)(struct adie_codec_path *path_ptr,
+					u8 master);
+};
+
+int adie_codec_register_codec_operations(
+				const struct adie_codec_operations *codec_ops);
+int adie_codec_open(struct adie_codec_dev_profile *profile,
+	struct adie_codec_path **path_pptr);
+int adie_codec_setpath(struct adie_codec_path *path_ptr,
+	u32 freq_plan, u32 osr);
+int adie_codec_proceed_stage(struct adie_codec_path *path_ptr, u32 state);
+int adie_codec_close(struct adie_codec_path *path_ptr);
+u32 adie_codec_freq_supported(struct adie_codec_dev_profile *profile,
+							u32 requested_freq);
+int adie_codec_enable_sidetone(struct adie_codec_path *rx_path_ptr, u32 enable);
+int adie_codec_enable_anc(struct adie_codec_path *rx_path_ptr, u32 enable,
+	struct adie_codec_anc_data *calibration_writes);
+int adie_codec_set_device_digital_volume(struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 vol_percentage /* in percentage */);
+
+int adie_codec_set_device_analog_volume(struct adie_codec_path *path_ptr,
+		u32 num_channels, u32 volume /* in percentage */);
+
+int adie_codec_set_master_mode(struct adie_codec_path *path_ptr, u8 master);
+#endif
diff --git a/include/uapi/linux/mfd/wcd9xxx/Kbuild b/include/uapi/linux/mfd/wcd9xxx/Kbuild
new file mode 100644
index 0000000..8e55965
--- /dev/null
+++ b/include/uapi/linux/mfd/wcd9xxx/Kbuild
@@ -0,0 +1,2 @@
+header-y += wcd9xxx_registers.h
+header-y += wcd9320_registers.h
diff --git a/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h b/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h
new file mode 100644
index 0000000..63ab624
--- /dev/null
+++ b/include/uapi/linux/mfd/wcd9xxx/wcd9320_registers.h
@@ -0,0 +1,1399 @@
+#ifndef WCD9320_REGISTERS_H
+#define WCD9320_REGISTERS_H
+
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+#define TAIKO_A_CHIP_CTL			WCD9XXX_A_CHIP_CTL
+#define TAIKO_A_CHIP_CTL__POR			WCD9XXX_A_CHIP_CTL__POR
+#define TAIKO_A_CHIP_STATUS			WCD9XXX_A_CHIP_STATUS
+#define TAIKO_A_CHIP_STATUS__POR		WCD9XXX_A_CHIP_STATUS__POR
+#define TAIKO_A_CHIP_ID_BYTE_0			WCD9XXX_A_CHIP_ID_BYTE_0
+#define TAIKO_A_CHIP_ID_BYTE_0__POR		WCD9XXX_A_CHIP_ID_BYTE_0__POR
+#define TAIKO_A_CHIP_ID_BYTE_1			WCD9XXX_A_CHIP_ID_BYTE_1
+#define TAIKO_A_CHIP_ID_BYTE_1__POR		WCD9XXX_A_CHIP_ID_BYTE_1__POR
+#define TAIKO_A_CHIP_ID_BYTE_2			WCD9XXX_A_CHIP_ID_BYTE_2
+#define TAIKO_A_CHIP_ID_BYTE_2__POR		WCD9XXX_A_CHIP_ID_BYTE_2__POR
+#define TAIKO_A_CHIP_ID_BYTE_3			WCD9XXX_A_CHIP_ID_BYTE_3
+#define TAIKO_A_CHIP_ID_BYTE_3__POR		WCD9XXX_A_CHIP_ID_BYTE_3__POR
+#define TAIKO_A_CHIP_VERSION			WCD9XXX_A_CHIP_VERSION
+#define TAIKO_A_CHIP_VERSION__POR		WCD9XXX_A_CHIP_VERSION__POR
+#define TAIKO_A_SB_VERSION			WCD9XXX_A_SB_VERSION
+#define TAIKO_A_SB_VERSION__POR			WCD9XXX_A_SB_VERSION__POR
+#define TAIKO_A_SLAVE_ID_1			WCD9XXX_A_SLAVE_ID_1
+#define TAIKO_A_SLAVE_ID_1__POR			WCD9XXX_A_SLAVE_ID_1__POR
+#define TAIKO_A_SLAVE_ID_2			WCD9XXX_A_SLAVE_ID_2
+#define TAIKO_A_SLAVE_ID_2__POR			WCD9XXX_A_SLAVE_ID_2__POR
+#define TAIKO_A_SLAVE_ID_3			WCD9XXX_A_SLAVE_ID_3
+#define TAIKO_A_SLAVE_ID_3__POR			WCD9XXX_A_SLAVE_ID_3__POR
+#define TAIKO_A_PIN_CTL_OE0			(0x010)
+#define TAIKO_A_PIN_CTL_OE0__POR				(0x00)
+#define TAIKO_A_PIN_CTL_OE1			(0x011)
+#define TAIKO_A_PIN_CTL_OE1__POR				(0x00)
+#define TAIKO_A_PIN_CTL_DATA0			(0x012)
+#define TAIKO_A_PIN_CTL_DATA0__POR				(0x00)
+#define TAIKO_A_PIN_CTL_DATA1			(0x013)
+#define TAIKO_A_PIN_CTL_DATA1__POR				(0x00)
+#define TAIKO_A_HDRIVE_GENERIC			(0x018)
+#define TAIKO_A_HDRIVE_GENERIC__POR				(0x00)
+#define TAIKO_A_HDRIVE_OVERRIDE			(0x019)
+#define TAIKO_A_HDRIVE_OVERRIDE__POR				(0x08)
+#define TAIKO_A_ANA_CSR_WAIT_STATE			(0x020)
+#define TAIKO_A_ANA_CSR_WAIT_STATE__POR				(0x44)
+#define TAIKO_A_PROCESS_MONITOR_CTL0			(0x040)
+#define TAIKO_A_PROCESS_MONITOR_CTL0__POR				(0x80)
+#define TAIKO_A_PROCESS_MONITOR_CTL1			(0x041)
+#define TAIKO_A_PROCESS_MONITOR_CTL1__POR				(0x00)
+#define TAIKO_A_PROCESS_MONITOR_CTL2			(0x042)
+#define TAIKO_A_PROCESS_MONITOR_CTL2__POR				(0x00)
+#define TAIKO_A_PROCESS_MONITOR_CTL3			(0x043)
+#define TAIKO_A_PROCESS_MONITOR_CTL3__POR				(0x01)
+#define TAIKO_A_QFUSE_CTL			(0x048)
+#define TAIKO_A_QFUSE_CTL__POR				(0x00)
+#define TAIKO_A_QFUSE_STATUS			(0x049)
+#define TAIKO_A_QFUSE_STATUS__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT0			(0x04A)
+#define TAIKO_A_QFUSE_DATA_OUT0__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT1			(0x04B)
+#define TAIKO_A_QFUSE_DATA_OUT1__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT2			(0x04C)
+#define TAIKO_A_QFUSE_DATA_OUT2__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT3			(0x04D)
+#define TAIKO_A_QFUSE_DATA_OUT3__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT4			(0x04E)
+#define TAIKO_A_QFUSE_DATA_OUT4__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT5			(0x04F)
+#define TAIKO_A_QFUSE_DATA_OUT5__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT6			(0x050)
+#define TAIKO_A_QFUSE_DATA_OUT6__POR				(0x00)
+#define TAIKO_A_QFUSE_DATA_OUT7			(0x051)
+#define TAIKO_A_QFUSE_DATA_OUT7__POR				(0x00)
+#define TAIKO_A_CDC_CTL				WCD9XXX_A_CDC_CTL
+#define TAIKO_A_CDC_CTL__POR			WCD9XXX_A_CDC_CTL__POR
+#define TAIKO_A_LEAKAGE_CTL			WCD9XXX_A_LEAKAGE_CTL
+#define TAIKO_A_LEAKAGE_CTL__POR		WCD9XXX_A_LEAKAGE_CTL__POR
+#define TAIKO_A_INTR_MODE			(0x090)
+#define TAIKO_A_INTR_MODE__POR				(0x00)
+#define TAIKO_A_INTR_MASK0			(0x094)
+#define TAIKO_A_INTR_MASK0__POR				(0xFF)
+#define TAIKO_A_INTR_MASK1			(0x095)
+#define TAIKO_A_INTR_MASK1__POR				(0xFF)
+#define TAIKO_A_INTR_MASK2			(0x096)
+#define TAIKO_A_INTR_MASK2__POR				(0x3F)
+#define TAIKO_A_INTR_MASK3			(0x097)
+#define TAIKO_A_INTR_MASK3__POR				(0x3F)
+#define TAIKO_A_INTR_STATUS0			(0x098)
+#define TAIKO_A_INTR_STATUS0__POR				(0x00)
+#define TAIKO_A_INTR_STATUS1			(0x099)
+#define TAIKO_A_INTR_STATUS1__POR				(0x00)
+#define TAIKO_A_INTR_STATUS2			(0x09A)
+#define TAIKO_A_INTR_STATUS2__POR				(0x00)
+#define TAIKO_A_INTR_STATUS3			(0x09B)
+#define TAIKO_A_INTR_STATUS3__POR				(0x00)
+#define TAIKO_A_INTR_CLEAR0			(0x09C)
+#define TAIKO_A_INTR_CLEAR0__POR				(0x00)
+#define TAIKO_A_INTR_CLEAR1			(0x09D)
+#define TAIKO_A_INTR_CLEAR1__POR				(0x00)
+#define TAIKO_A_INTR_CLEAR2			(0x09E)
+#define TAIKO_A_INTR_CLEAR2__POR				(0x00)
+#define TAIKO_A_INTR_CLEAR3			(0x09F)
+#define TAIKO_A_INTR_CLEAR3__POR				(0x00)
+#define TAIKO_A_INTR_LEVEL0			(0x0A0)
+#define TAIKO_A_INTR_LEVEL0__POR				(0x01)
+#define TAIKO_A_INTR_LEVEL1			(0x0A1)
+#define TAIKO_A_INTR_LEVEL1__POR				(0x00)
+#define TAIKO_A_INTR_LEVEL2			(0x0A2)
+#define TAIKO_A_INTR_LEVEL2__POR				(0x00)
+#define TAIKO_A_INTR_LEVEL3			(0x0A3)
+#define TAIKO_A_INTR_LEVEL3__POR				(0x00)
+#define TAIKO_A_INTR_TEST0			(0x0A4)
+#define TAIKO_A_INTR_TEST0__POR				(0x00)
+#define TAIKO_A_INTR_TEST1			(0x0A5)
+#define TAIKO_A_INTR_TEST1__POR				(0x00)
+#define TAIKO_A_INTR_TEST2			(0x0A6)
+#define TAIKO_A_INTR_TEST2__POR				(0x00)
+#define TAIKO_A_INTR_TEST3			(0x0A7)
+#define TAIKO_A_INTR_TEST3__POR				(0x00)
+#define TAIKO_A_INTR_SET0			(0x0A8)
+#define TAIKO_A_INTR_SET0__POR				(0x00)
+#define TAIKO_A_INTR_SET1			(0x0A9)
+#define TAIKO_A_INTR_SET1__POR				(0x00)
+#define TAIKO_A_INTR_SET2			(0x0AA)
+#define TAIKO_A_INTR_SET2__POR				(0x00)
+#define TAIKO_A_INTR_SET3			(0x0AB)
+#define TAIKO_A_INTR_SET3__POR				(0x00)
+#define TAIKO_A_INTR_DESTN0			(0x0AC)
+#define TAIKO_A_INTR_DESTN0__POR				(0x00)
+#define TAIKO_A_INTR_DESTN1			(0x0AD)
+#define TAIKO_A_INTR_DESTN1__POR				(0x00)
+#define TAIKO_A_INTR_DESTN2			(0x0AE)
+#define TAIKO_A_INTR_DESTN2__POR				(0x00)
+#define TAIKO_A_INTR_DESTN3			(0x0AF)
+#define TAIKO_A_INTR_DESTN3__POR				(0x00)
+#define TAIKO_A_CDC_TX_I2S_SCK_MODE			(0x0C0)
+#define TAIKO_A_CDC_TX_I2S_SCK_MODE__POR				(0x00)
+#define TAIKO_A_CDC_TX_I2S_WS_MODE			(0x0C1)
+#define TAIKO_A_CDC_TX_I2S_WS_MODE__POR				(0x00)
+#define TAIKO_A_CDC_DMIC_DATA0_MODE			(0x0C4)
+#define TAIKO_A_CDC_DMIC_DATA0_MODE__POR				(0x00)
+#define TAIKO_A_CDC_DMIC_CLK0_MODE			(0x0C5)
+#define TAIKO_A_CDC_DMIC_CLK0_MODE__POR				(0x00)
+#define TAIKO_A_CDC_DMIC_DATA1_MODE			(0x0C6)
+#define TAIKO_A_CDC_DMIC_DATA1_MODE__POR				(0x00)
+#define TAIKO_A_CDC_DMIC_CLK1_MODE			(0x0C7)
+#define TAIKO_A_CDC_DMIC_CLK1_MODE__POR				(0x00)
+#define TAIKO_A_CDC_RX_I2S_SCK_MODE			(0x0C8)
+#define TAIKO_A_CDC_RX_I2S_SCK_MODE__POR				(0x00)
+#define TAIKO_A_CDC_RX_I2S_WS_MODE			(0x0C9)
+#define TAIKO_A_CDC_RX_I2S_WS_MODE__POR				(0x00)
+#define TAIKO_A_CDC_DMIC_DATA2_MODE			(0x0CA)
+#define TAIKO_A_CDC_DMIC_DATA2_MODE__POR				(0x00)
+#define TAIKO_A_CDC_DMIC_CLK2_MODE			(0x0CB)
+#define TAIKO_A_CDC_DMIC_CLK2_MODE__POR				(0x00)
+#define TAIKO_A_CDC_INTR1_MODE			(0x0CC)
+#define TAIKO_A_CDC_INTR1_MODE__POR				(0x00)
+#define TAIKO_A_CDC_SB_NRZ_SEL_MODE			(0x0CD)
+#define TAIKO_A_CDC_SB_NRZ_SEL_MODE__POR				(0x00)
+#define TAIKO_A_CDC_INTR2_MODE			(0x0CE)
+#define TAIKO_A_CDC_INTR2_MODE__POR				(0x00)
+#define TAIKO_A_CDC_RF_PA_ON_MODE			(0x0CF)
+#define TAIKO_A_CDC_RF_PA_ON_MODE__POR				(0x00)
+#define TAIKO_A_BIAS_REF_CTL			(0x100)
+#define TAIKO_A_BIAS_REF_CTL__POR				(0x1C)
+#define TAIKO_A_BIAS_CENTRAL_BG_CTL			(0x101)
+#define TAIKO_A_BIAS_CENTRAL_BG_CTL__POR				(0x50)
+#define TAIKO_A_BIAS_PRECHRG_CTL			(0x102)
+#define TAIKO_A_BIAS_PRECHRG_CTL__POR				(0x07)
+#define TAIKO_A_BIAS_CURR_CTL_1			(0x103)
+#define TAIKO_A_BIAS_CURR_CTL_1__POR				(0x52)
+#define TAIKO_A_BIAS_CURR_CTL_2			(0x104)
+#define TAIKO_A_BIAS_CURR_CTL_2__POR				(0x00)
+#define TAIKO_A_BIAS_OSC_BG_CTL			(0x105)
+#define TAIKO_A_BIAS_OSC_BG_CTL__POR				(0x16)
+#define TAIKO_A_CLK_BUFF_EN1			(0x108)
+#define TAIKO_A_CLK_BUFF_EN1__POR				(0x04)
+#define TAIKO_A_CLK_BUFF_EN2			(0x109)
+#define TAIKO_A_CLK_BUFF_EN2__POR				(0x02)
+#define TAIKO_A_LDO_H_MODE_1			(0x110)
+#define TAIKO_A_LDO_H_MODE_1__POR				(0x65)
+#define TAIKO_A_LDO_H_MODE_2			(0x111)
+#define TAIKO_A_LDO_H_MODE_2__POR				(0xA8)
+#define TAIKO_A_LDO_H_LOOP_CTL			(0x112)
+#define TAIKO_A_LDO_H_LOOP_CTL__POR				(0x6B)
+#define TAIKO_A_LDO_H_COMP_1			(0x113)
+#define TAIKO_A_LDO_H_COMP_1__POR				(0x84)
+#define TAIKO_A_LDO_H_COMP_2			(0x114)
+#define TAIKO_A_LDO_H_COMP_2__POR				(0xE0)
+#define TAIKO_A_LDO_H_BIAS_1			(0x115)
+#define TAIKO_A_LDO_H_BIAS_1__POR				(0x6D)
+#define TAIKO_A_LDO_H_BIAS_2			(0x116)
+#define TAIKO_A_LDO_H_BIAS_2__POR				(0xA5)
+#define TAIKO_A_LDO_H_BIAS_3			(0x117)
+#define TAIKO_A_LDO_H_BIAS_3__POR				(0x60)
+#define TAIKO_A_VBAT_CLK			(0x118)
+#define TAIKO_A_VBAT_CLK__POR				(0x03)
+#define TAIKO_A_VBAT_LOOP			(0x119)
+#define TAIKO_A_VBAT_LOOP__POR				(0x02)
+#define TAIKO_A_VBAT_REF			(0x11A)
+#define TAIKO_A_VBAT_REF__POR				(0x20)
+#define TAIKO_A_VBAT_ADC_TEST			(0x11B)
+#define TAIKO_A_VBAT_ADC_TEST__POR				(0x00)
+#define TAIKO_A_VBAT_FE			(0x11C)
+#define TAIKO_A_VBAT_FE__POR				(0x48)
+#define TAIKO_A_VBAT_BIAS_1			(0x11D)
+#define TAIKO_A_VBAT_BIAS_1__POR				(0x03)
+#define TAIKO_A_VBAT_BIAS_2			(0x11E)
+#define TAIKO_A_VBAT_BIAS_2__POR				(0x00)
+#define TAIKO_A_VBAT_ADC_DATA_MSB			(0x11F)
+#define TAIKO_A_VBAT_ADC_DATA_MSB__POR				(0x00)
+#define TAIKO_A_VBAT_ADC_DATA_LSB			(0x120)
+#define TAIKO_A_VBAT_ADC_DATA_LSB__POR				(0x00)
+#define TAIKO_A_MICB_CFILT_1_CTL			(0x128)
+#define TAIKO_A_MICB_CFILT_1_CTL__POR				(0x40)
+#define TAIKO_A_MICB_CFILT_1_VAL			(0x129)
+#define TAIKO_A_MICB_CFILT_1_VAL__POR				(0x80)
+#define TAIKO_A_MICB_CFILT_1_PRECHRG			(0x12A)
+#define TAIKO_A_MICB_CFILT_1_PRECHRG__POR				(0x38)
+#define TAIKO_A_MICB_1_CTL			(0x12B)
+#define TAIKO_A_MICB_1_CTL__POR				(0x16)
+#define TAIKO_A_MICB_1_INT_RBIAS			(0x12C)
+#define TAIKO_A_MICB_1_INT_RBIAS__POR				(0x24)
+#define TAIKO_A_MICB_1_MBHC			(0x12D)
+#define TAIKO_A_MICB_1_MBHC__POR				(0x01)
+#define TAIKO_A_MICB_CFILT_2_CTL			(0x12E)
+#define TAIKO_A_MICB_CFILT_2_CTL__POR				(0x40)
+#define TAIKO_A_MICB_CFILT_2_VAL			(0x12F)
+#define TAIKO_A_MICB_CFILT_2_VAL__POR				(0x80)
+#define TAIKO_A_MICB_CFILT_2_PRECHRG			(0x130)
+#define TAIKO_A_MICB_CFILT_2_PRECHRG__POR				(0x38)
+#define TAIKO_A_MICB_2_CTL			(0x131)
+#define TAIKO_A_MICB_2_CTL__POR				(0x16)
+#define TAIKO_A_MICB_2_INT_RBIAS			(0x132)
+#define TAIKO_A_MICB_2_INT_RBIAS__POR				(0x24)
+#define TAIKO_A_MICB_2_MBHC			(0x133)
+#define TAIKO_A_MICB_2_MBHC__POR				(0x02)
+#define TAIKO_A_MICB_CFILT_3_CTL			(0x134)
+#define TAIKO_A_MICB_CFILT_3_CTL__POR				(0x40)
+#define TAIKO_A_MICB_CFILT_3_VAL			(0x135)
+#define TAIKO_A_MICB_CFILT_3_VAL__POR				(0x80)
+#define TAIKO_A_MICB_CFILT_3_PRECHRG			(0x136)
+#define TAIKO_A_MICB_CFILT_3_PRECHRG__POR				(0x38)
+#define TAIKO_A_MICB_3_CTL			(0x137)
+#define TAIKO_A_MICB_3_CTL__POR				(0x16)
+#define TAIKO_A_MICB_3_INT_RBIAS			(0x138)
+#define TAIKO_A_MICB_3_INT_RBIAS__POR				(0x24)
+#define TAIKO_A_MICB_3_MBHC			(0x139)
+#define TAIKO_A_MICB_3_MBHC__POR				(0x00)
+#define TAIKO_A_MICB_4_CTL			(0x13D)
+#define TAIKO_A_MICB_4_CTL__POR				(0x16)
+#define TAIKO_A_MICB_4_INT_RBIAS			(0x13E)
+#define TAIKO_A_MICB_4_INT_RBIAS__POR				(0x24)
+#define TAIKO_A_MICB_4_MBHC			(0x13F)
+#define TAIKO_A_MICB_4_MBHC__POR				(0x01)
+#define TAIKO_A_MBHC_INSERT_DETECT			(0x14A)
+#define TAIKO_A_MBHC_INSERT_DETECT__POR				(0x00)
+#define TAIKO_A_MBHC_INSERT_DET_STATUS			(0x14B)
+#define TAIKO_A_MBHC_INSERT_DET_STATUS__POR				(0x00)
+#define TAIKO_A_TX_COM_BIAS			(0x14C)
+#define TAIKO_A_TX_COM_BIAS__POR				(0xF0)
+#define TAIKO_A_MBHC_SCALING_MUX_1			(0x14E)
+#define TAIKO_A_MBHC_SCALING_MUX_1__POR				(0x00)
+#define TAIKO_A_MBHC_SCALING_MUX_2			(0x14F)
+#define TAIKO_A_MBHC_SCALING_MUX_2__POR				(0x80)
+#define TAIKO_A_MAD_ANA_CTRL			(0x150)
+#define TAIKO_A_MAD_ANA_CTRL__POR				(0xF1)
+#define TAIKO_A_TX_SUP_SWITCH_CTRL_1			(0x151)
+#define TAIKO_A_TX_SUP_SWITCH_CTRL_1__POR				(0x00)
+#define TAIKO_A_TX_SUP_SWITCH_CTRL_2			(0x152)
+#define TAIKO_A_TX_SUP_SWITCH_CTRL_2__POR				(0x80)
+#define TAIKO_A_TX_1_2_EN			(0x153)
+#define TAIKO_A_TX_1_2_EN__POR				(0x00)
+#define TAIKO_A_TX_1_2_TEST_EN			(0x154)
+#define TAIKO_A_TX_1_2_TEST_EN__POR				(0xCC)
+#define TAIKO_A_TX_1_2_ADC_CH1			(0x155)
+#define TAIKO_A_TX_1_2_ADC_CH1__POR				(0x44)
+#define TAIKO_A_TX_1_2_ADC_CH2			(0x156)
+#define TAIKO_A_TX_1_2_ADC_CH2__POR				(0x44)
+#define TAIKO_A_TX_1_2_ATEST_REFCTRL			(0x157)
+#define TAIKO_A_TX_1_2_ATEST_REFCTRL__POR				(0x00)
+#define TAIKO_A_TX_1_2_TEST_CTL			(0x158)
+#define TAIKO_A_TX_1_2_TEST_CTL__POR				(0x38)
+#define TAIKO_A_TX_1_2_TEST_BLOCK_EN			(0x159)
+#define TAIKO_A_TX_1_2_TEST_BLOCK_EN__POR				(0xFC)
+#define TAIKO_A_TX_1_2_TXFE_CLKDIV			(0x15A)
+#define TAIKO_A_TX_1_2_TXFE_CLKDIV__POR				(0x55)
+#define TAIKO_A_TX_1_2_SAR_ERR_CH1			(0x15B)
+#define TAIKO_A_TX_1_2_SAR_ERR_CH1__POR				(0x00)
+#define TAIKO_A_TX_1_2_SAR_ERR_CH2			(0x15C)
+#define TAIKO_A_TX_1_2_SAR_ERR_CH2__POR				(0x00)
+#define TAIKO_A_TX_3_4_EN			(0x15D)
+#define TAIKO_A_TX_3_4_EN__POR				(0x00)
+#define TAIKO_A_TX_3_4_TEST_EN			(0x15E)
+#define TAIKO_A_TX_3_4_TEST_EN__POR				(0xCC)
+#define TAIKO_A_TX_3_4_ADC_CH3			(0x15F)
+#define TAIKO_A_TX_3_4_ADC_CH3__POR				(0x44)
+#define TAIKO_A_TX_3_4_ADC_CH4			(0x160)
+#define TAIKO_A_TX_3_4_ADC_CH4__POR				(0x44)
+#define TAIKO_A_TX_3_4_ATEST_REFCTRL			(0x161)
+#define TAIKO_A_TX_3_4_ATEST_REFCTRL__POR				(0x00)
+#define TAIKO_A_TX_3_4_TEST_CTL			(0x162)
+#define TAIKO_A_TX_3_4_TEST_CTL__POR				(0x38)
+#define TAIKO_A_TX_3_4_TEST_BLOCK_EN			(0x163)
+#define TAIKO_A_TX_3_4_TEST_BLOCK_EN__POR				(0xFC)
+#define TAIKO_A_TX_3_4_TXFE_CKDIV			(0x164)
+#define TAIKO_A_TX_3_4_TXFE_CKDIV__POR				(0x55)
+#define TAIKO_A_TX_3_4_SAR_ERR_CH3			(0x165)
+#define TAIKO_A_TX_3_4_SAR_ERR_CH3__POR				(0x00)
+#define TAIKO_A_TX_3_4_SAR_ERR_CH4			(0x166)
+#define TAIKO_A_TX_3_4_SAR_ERR_CH4__POR				(0x00)
+#define TAIKO_A_TX_5_6_EN			(0x167)
+#define TAIKO_A_TX_5_6_EN__POR				(0x11)
+#define TAIKO_A_TX_5_6_TEST_EN			(0x168)
+#define TAIKO_A_TX_5_6_TEST_EN__POR				(0xCC)
+#define TAIKO_A_TX_5_6_ADC_CH5			(0x169)
+#define TAIKO_A_TX_5_6_ADC_CH5__POR				(0x44)
+#define TAIKO_A_TX_5_6_ADC_CH6			(0x16A)
+#define TAIKO_A_TX_5_6_ADC_CH6__POR				(0x44)
+#define TAIKO_A_TX_5_6_ATEST_REFCTRL			(0x16B)
+#define TAIKO_A_TX_5_6_ATEST_REFCTRL__POR				(0x00)
+#define TAIKO_A_TX_5_6_TEST_CTL			(0x16C)
+#define TAIKO_A_TX_5_6_TEST_CTL__POR				(0x38)
+#define TAIKO_A_TX_5_6_TEST_BLOCK_EN			(0x16D)
+#define TAIKO_A_TX_5_6_TEST_BLOCK_EN__POR				(0xFC)
+#define TAIKO_A_TX_5_6_TXFE_CKDIV			(0x16E)
+#define TAIKO_A_TX_5_6_TXFE_CKDIV__POR				(0x55)
+#define TAIKO_A_TX_5_6_SAR_ERR_CH5			(0x16F)
+#define TAIKO_A_TX_5_6_SAR_ERR_CH5__POR				(0x00)
+#define TAIKO_A_TX_5_6_SAR_ERR_CH6			(0x170)
+#define TAIKO_A_TX_5_6_SAR_ERR_CH6__POR				(0x00)
+#define TAIKO_A_TX_7_MBHC_EN			(0x171)
+#define TAIKO_A_TX_7_MBHC_EN__POR				(0x0C)
+#define TAIKO_A_TX_7_MBHC_ATEST_REFCTRL			(0x172)
+#define TAIKO_A_TX_7_MBHC_ATEST_REFCTRL__POR				(0x00)
+#define TAIKO_A_TX_7_MBHC_ADC			(0x173)
+#define TAIKO_A_TX_7_MBHC_ADC__POR				(0x44)
+#define TAIKO_A_TX_7_MBHC_TEST_CTL			(0x174)
+#define TAIKO_A_TX_7_MBHC_TEST_CTL__POR				(0x38)
+#define TAIKO_A_TX_7_MBHC_SAR_ERR			(0x175)
+#define TAIKO_A_TX_7_MBHC_SAR_ERR__POR				(0x00)
+#define TAIKO_A_TX_7_TXFE_CLKDIV			(0x176)
+#define TAIKO_A_TX_7_TXFE_CLKDIV__POR				(0x0B)
+#define TAIKO_A_BUCK_MODE_1			(0x181)
+#define TAIKO_A_BUCK_MODE_1__POR				(0x21)
+#define TAIKO_A_BUCK_MODE_2			(0x182)
+#define TAIKO_A_BUCK_MODE_2__POR				(0xFF)
+#define TAIKO_A_BUCK_MODE_3			(0x183)
+#define TAIKO_A_BUCK_MODE_3__POR				(0xCC)
+#define TAIKO_A_BUCK_MODE_4			(0x184)
+#define TAIKO_A_BUCK_MODE_4__POR				(0x3A)
+#define TAIKO_A_BUCK_MODE_5			(0x185)
+#define TAIKO_A_BUCK_MODE_5__POR				(0x00)
+#define TAIKO_A_BUCK_CTRL_VCL_1			(0x186)
+#define TAIKO_A_BUCK_CTRL_VCL_1__POR				(0x48)
+#define TAIKO_A_BUCK_CTRL_VCL_2			(0x187)
+#define TAIKO_A_BUCK_CTRL_VCL_2__POR				(0xA3)
+#define TAIKO_A_BUCK_CTRL_VCL_3			(0x188)
+#define TAIKO_A_BUCK_CTRL_VCL_3__POR				(0x82)
+#define TAIKO_A_BUCK_CTRL_CCL_1			(0x189)
+#define TAIKO_A_BUCK_CTRL_CCL_1__POR				(0xAB)
+#define TAIKO_A_BUCK_CTRL_CCL_2			(0x18A)
+#define TAIKO_A_BUCK_CTRL_CCL_2__POR				(0xDC)
+#define TAIKO_A_BUCK_CTRL_CCL_3			(0x18B)
+#define TAIKO_A_BUCK_CTRL_CCL_3__POR				(0x6A)
+#define TAIKO_A_BUCK_CTRL_CCL_4			(0x18C)
+#define TAIKO_A_BUCK_CTRL_CCL_4__POR				(0x58)
+#define TAIKO_A_BUCK_CTRL_PWM_DRVR_1			(0x18D)
+#define TAIKO_A_BUCK_CTRL_PWM_DRVR_1__POR				(0x50)
+#define TAIKO_A_BUCK_CTRL_PWM_DRVR_2			(0x18E)
+#define TAIKO_A_BUCK_CTRL_PWM_DRVR_2__POR				(0x64)
+#define TAIKO_A_BUCK_CTRL_PWM_DRVR_3			(0x18F)
+#define TAIKO_A_BUCK_CTRL_PWM_DRVR_3__POR				(0x77)
+#define TAIKO_A_BUCK_TMUX_A_D			(0x190)
+#define TAIKO_A_BUCK_TMUX_A_D__POR				(0x00)
+#define TAIKO_A_NCP_BUCKREF			(0x191)
+#define TAIKO_A_NCP_BUCKREF__POR				(0x00)
+#define TAIKO_A_NCP_EN			(0x192)
+#define TAIKO_A_NCP_EN__POR				(0xFE)
+#define TAIKO_A_NCP_CLK			(0x193)
+#define TAIKO_A_NCP_CLK__POR				(0x94)
+#define TAIKO_A_NCP_STATIC			(0x194)
+#define TAIKO_A_NCP_STATIC__POR				(0x28)
+#define TAIKO_A_NCP_VTH_LOW			(0x195)
+#define TAIKO_A_NCP_VTH_LOW__POR				(0x88)
+#define TAIKO_A_NCP_VTH_HIGH			(0x196)
+#define TAIKO_A_NCP_VTH_HIGH__POR				(0xA0)
+#define TAIKO_A_NCP_ATEST			(0x197)
+#define TAIKO_A_NCP_ATEST__POR				(0x00)
+#define TAIKO_A_NCP_DTEST			(0x198)
+#define TAIKO_A_NCP_DTEST__POR				(0x00)
+#define TAIKO_A_NCP_DLY1			(0x199)
+#define TAIKO_A_NCP_DLY1__POR				(0x06)
+#define TAIKO_A_NCP_DLY2			(0x19A)
+#define TAIKO_A_NCP_DLY2__POR				(0x06)
+#define TAIKO_A_RX_AUX_SW_CTL			(0x19B)
+#define TAIKO_A_RX_AUX_SW_CTL__POR				(0x00)
+#define TAIKO_A_RX_PA_AUX_IN_CONN			(0x19C)
+#define TAIKO_A_RX_PA_AUX_IN_CONN__POR				(0x00)
+#define TAIKO_A_RX_COM_TIMER_DIV			(0x19E)
+#define TAIKO_A_RX_COM_TIMER_DIV__POR				(0xE8)
+#define TAIKO_A_RX_COM_OCP_CTL			(0x19F)
+#define TAIKO_A_RX_COM_OCP_CTL__POR				(0x1F)
+#define TAIKO_A_RX_COM_OCP_COUNT			(0x1A0)
+#define TAIKO_A_RX_COM_OCP_COUNT__POR				(0x77)
+#define TAIKO_A_RX_COM_DAC_CTL			(0x1A1)
+#define TAIKO_A_RX_COM_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_COM_BIAS			(0x1A2)
+#define TAIKO_A_RX_COM_BIAS__POR				(0x00)
+#define TAIKO_A_RX_HPH_AUTO_CHOP			(0x1A4)
+#define TAIKO_A_RX_HPH_AUTO_CHOP__POR				(0x38)
+#define TAIKO_A_RX_HPH_CHOP_CTL			(0x1A5)
+#define TAIKO_A_RX_HPH_CHOP_CTL__POR				(0xB4)
+#define TAIKO_A_RX_HPH_BIAS_PA			(0x1A6)
+#define TAIKO_A_RX_HPH_BIAS_PA__POR				(0xAA)
+#define TAIKO_A_RX_HPH_BIAS_LDO			(0x1A7)
+#define TAIKO_A_RX_HPH_BIAS_LDO__POR				(0x87)
+#define TAIKO_A_RX_HPH_BIAS_CNP			(0x1A8)
+#define TAIKO_A_RX_HPH_BIAS_CNP__POR				(0x8A)
+#define TAIKO_A_RX_HPH_BIAS_WG_OCP			(0x1A9)
+#define TAIKO_A_RX_HPH_BIAS_WG_OCP__POR				(0x2A)
+#define TAIKO_A_RX_HPH_OCP_CTL			(0x1AA)
+#define TAIKO_A_RX_HPH_OCP_CTL__POR				(0x68)
+#define TAIKO_A_RX_HPH_CNP_EN			(0x1AB)
+#define TAIKO_A_RX_HPH_CNP_EN__POR				(0x80)
+#define TAIKO_A_RX_HPH_CNP_WG_CTL			(0x1AC)
+#define TAIKO_A_RX_HPH_CNP_WG_CTL__POR				(0xDE)
+#define TAIKO_A_RX_HPH_CNP_WG_TIME			(0x1AD)
+#define TAIKO_A_RX_HPH_CNP_WG_TIME__POR				(0x2A)
+#define TAIKO_A_RX_HPH_L_GAIN			(0x1AE)
+#define TAIKO_A_RX_HPH_L_GAIN__POR				(0x00)
+#define TAIKO_A_RX_HPH_L_TEST			(0x1AF)
+#define TAIKO_A_RX_HPH_L_TEST__POR				(0x00)
+#define TAIKO_A_RX_HPH_L_PA_CTL			(0x1B0)
+#define TAIKO_A_RX_HPH_L_PA_CTL__POR				(0x40)
+#define TAIKO_A_RX_HPH_L_DAC_CTL			(0x1B1)
+#define TAIKO_A_RX_HPH_L_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_HPH_L_ATEST			(0x1B2)
+#define TAIKO_A_RX_HPH_L_ATEST__POR				(0x00)
+#define TAIKO_A_RX_HPH_L_STATUS			(0x1B3)
+#define TAIKO_A_RX_HPH_L_STATUS__POR				(0x00)
+#define TAIKO_A_RX_HPH_R_GAIN			(0x1B4)
+#define TAIKO_A_RX_HPH_R_GAIN__POR				(0x00)
+#define TAIKO_A_RX_HPH_R_TEST			(0x1B5)
+#define TAIKO_A_RX_HPH_R_TEST__POR				(0x00)
+#define TAIKO_A_RX_HPH_R_PA_CTL			(0x1B6)
+#define TAIKO_A_RX_HPH_R_PA_CTL__POR				(0x40)
+#define TAIKO_A_RX_HPH_R_DAC_CTL			(0x1B7)
+#define TAIKO_A_RX_HPH_R_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_HPH_R_ATEST			(0x1B8)
+#define TAIKO_A_RX_HPH_R_ATEST__POR				(0x00)
+#define TAIKO_A_RX_HPH_R_STATUS			(0x1B9)
+#define TAIKO_A_RX_HPH_R_STATUS__POR				(0x00)
+#define TAIKO_A_RX_EAR_BIAS_PA			(0x1BA)
+#define TAIKO_A_RX_EAR_BIAS_PA__POR				(0xA6)
+#define TAIKO_A_RX_EAR_BIAS_CMBUFF			(0x1BB)
+#define TAIKO_A_RX_EAR_BIAS_CMBUFF__POR				(0xA0)
+#define TAIKO_A_RX_EAR_EN			(0x1BC)
+#define TAIKO_A_RX_EAR_EN__POR				(0x00)
+#define TAIKO_A_RX_EAR_GAIN			(0x1BD)
+#define TAIKO_A_RX_EAR_GAIN__POR				(0x02)
+#define TAIKO_A_RX_EAR_CMBUFF			(0x1BE)
+#define TAIKO_A_RX_EAR_CMBUFF__POR				(0x04)
+#define TAIKO_A_RX_EAR_ICTL			(0x1BF)
+#define TAIKO_A_RX_EAR_ICTL__POR				(0x40)
+#define TAIKO_A_RX_EAR_CCOMP			(0x1C0)
+#define TAIKO_A_RX_EAR_CCOMP__POR				(0x08)
+#define TAIKO_A_RX_EAR_VCM			(0x1C1)
+#define TAIKO_A_RX_EAR_VCM__POR				(0x03)
+#define TAIKO_A_RX_EAR_CNP			(0x1C2)
+#define TAIKO_A_RX_EAR_CNP__POR				(0xF2)
+#define TAIKO_A_RX_EAR_DAC_CTL_ATEST			(0x1C3)
+#define TAIKO_A_RX_EAR_DAC_CTL_ATEST__POR				(0x00)
+#define TAIKO_A_RX_EAR_STATUS			(0x1C5)
+#define TAIKO_A_RX_EAR_STATUS__POR				(0x04)
+#define TAIKO_A_RX_LINE_BIAS_PA			(0x1C6)
+#define TAIKO_A_RX_LINE_BIAS_PA__POR				(0xA8)
+#define TAIKO_A_RX_BUCK_BIAS1			(0x1C7)
+#define TAIKO_A_RX_BUCK_BIAS1__POR				(0x42)
+#define TAIKO_A_RX_BUCK_BIAS2			(0x1C8)
+#define TAIKO_A_RX_BUCK_BIAS2__POR				(0x84)
+#define TAIKO_A_RX_LINE_COM			(0x1C9)
+#define TAIKO_A_RX_LINE_COM__POR				(0x80)
+#define TAIKO_A_RX_LINE_CNP_EN			(0x1CA)
+#define TAIKO_A_RX_LINE_CNP_EN__POR				(0x00)
+#define TAIKO_A_RX_LINE_CNP_WG_CTL			(0x1CB)
+#define TAIKO_A_RX_LINE_CNP_WG_CTL__POR				(0x00)
+#define TAIKO_A_RX_LINE_CNP_WG_TIME			(0x1CC)
+#define TAIKO_A_RX_LINE_CNP_WG_TIME__POR				(0x04)
+#define TAIKO_A_RX_LINE_1_GAIN			(0x1CD)
+#define TAIKO_A_RX_LINE_1_GAIN__POR				(0x00)
+#define TAIKO_A_RX_LINE_1_TEST			(0x1CE)
+#define TAIKO_A_RX_LINE_1_TEST__POR				(0x00)
+#define TAIKO_A_RX_LINE_1_DAC_CTL			(0x1CF)
+#define TAIKO_A_RX_LINE_1_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_LINE_1_STATUS			(0x1D0)
+#define TAIKO_A_RX_LINE_1_STATUS__POR				(0x00)
+#define TAIKO_A_RX_LINE_2_GAIN			(0x1D1)
+#define TAIKO_A_RX_LINE_2_GAIN__POR				(0x00)
+#define TAIKO_A_RX_LINE_2_TEST			(0x1D2)
+#define TAIKO_A_RX_LINE_2_TEST__POR				(0x00)
+#define TAIKO_A_RX_LINE_2_DAC_CTL			(0x1D3)
+#define TAIKO_A_RX_LINE_2_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_LINE_2_STATUS			(0x1D4)
+#define TAIKO_A_RX_LINE_2_STATUS__POR				(0x00)
+#define TAIKO_A_RX_LINE_3_GAIN			(0x1D5)
+#define TAIKO_A_RX_LINE_3_GAIN__POR				(0x00)
+#define TAIKO_A_RX_LINE_3_TEST			(0x1D6)
+#define TAIKO_A_RX_LINE_3_TEST__POR				(0x00)
+#define TAIKO_A_RX_LINE_3_DAC_CTL			(0x1D7)
+#define TAIKO_A_RX_LINE_3_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_LINE_3_STATUS			(0x1D8)
+#define TAIKO_A_RX_LINE_3_STATUS__POR				(0x00)
+#define TAIKO_A_RX_LINE_4_GAIN			(0x1D9)
+#define TAIKO_A_RX_LINE_4_GAIN__POR				(0x00)
+#define TAIKO_A_RX_LINE_4_TEST			(0x1DA)
+#define TAIKO_A_RX_LINE_4_TEST__POR				(0x00)
+#define TAIKO_A_RX_LINE_4_DAC_CTL			(0x1DB)
+#define TAIKO_A_RX_LINE_4_DAC_CTL__POR				(0x00)
+#define TAIKO_A_RX_LINE_4_STATUS			(0x1DC)
+#define TAIKO_A_RX_LINE_4_STATUS__POR				(0x00)
+#define TAIKO_A_RX_LINE_CNP_DBG			(0x1DD)
+#define TAIKO_A_RX_LINE_CNP_DBG__POR				(0x00)
+#define TAIKO_A_SPKR_DRV_EN			(0x1DF)
+#define TAIKO_A_SPKR_DRV_EN__POR				(0x6F)
+#define TAIKO_A_SPKR_DRV_GAIN			(0x1E0)
+#define TAIKO_A_SPKR_DRV_GAIN__POR				(0x00)
+#define TAIKO_A_SPKR_DRV_DAC_CTL			(0x1E1)
+#define TAIKO_A_SPKR_DRV_DAC_CTL__POR				(0x04)
+#define TAIKO_A_SPKR_DRV_OCP_CTL			(0x1E2)
+#define TAIKO_A_SPKR_DRV_OCP_CTL__POR				(0x98)
+#define TAIKO_A_SPKR_DRV_CLIP_DET			(0x1E3)
+#define TAIKO_A_SPKR_DRV_CLIP_DET__POR				(0x48)
+#define TAIKO_A_SPKR_DRV_IEC			(0x1E4)
+#define TAIKO_A_SPKR_DRV_IEC__POR				(0x20)
+#define TAIKO_A_SPKR_DRV_DBG_DAC			(0x1E5)
+#define TAIKO_A_SPKR_DRV_DBG_DAC__POR				(0x05)
+#define TAIKO_A_SPKR_DRV_DBG_PA			(0x1E6)
+#define TAIKO_A_SPKR_DRV_DBG_PA__POR				(0x18)
+#define TAIKO_A_SPKR_DRV_DBG_PWRSTG			(0x1E7)
+#define TAIKO_A_SPKR_DRV_DBG_PWRSTG__POR				(0x00)
+#define TAIKO_A_SPKR_DRV_BIAS_LDO			(0x1E8)
+#define TAIKO_A_SPKR_DRV_BIAS_LDO__POR				(0x45)
+#define TAIKO_A_SPKR_DRV_BIAS_INT			(0x1E9)
+#define TAIKO_A_SPKR_DRV_BIAS_INT__POR				(0xA5)
+#define TAIKO_A_SPKR_DRV_BIAS_PA			(0x1EA)
+#define TAIKO_A_SPKR_DRV_BIAS_PA__POR				(0x55)
+#define TAIKO_A_SPKR_DRV_STATUS_OCP			(0x1EB)
+#define TAIKO_A_SPKR_DRV_STATUS_OCP__POR				(0x00)
+#define TAIKO_A_SPKR_DRV_STATUS_PA			(0x1EC)
+#define TAIKO_A_SPKR_DRV_STATUS_PA__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_EN			(0x1ED)
+#define TAIKO_A_SPKR_PROT_EN__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_ADC_EN			(0x1EE)
+#define TAIKO_A_SPKR_PROT_ADC_EN__POR				(0x44)
+#define TAIKO_A_SPKR_PROT_ISENSE_BIAS			(0x1EF)
+#define TAIKO_A_SPKR_PROT_ISENSE_BIAS__POR				(0x44)
+#define TAIKO_A_SPKR_PROT_VSENSE_BIAS			(0x1F0)
+#define TAIKO_A_SPKR_PROT_VSENSE_BIAS__POR				(0x44)
+#define TAIKO_A_SPKR_PROT_ADC_ATEST_REFCTRL			(0x1F1)
+#define TAIKO_A_SPKR_PROT_ADC_ATEST_REFCTRL__POR			(0x00)
+#define TAIKO_A_SPKR_PROT_ADC_TEST_CTL			(0x1F2)
+#define TAIKO_A_SPKR_PROT_ADC_TEST_CTL__POR				(0x38)
+#define TAIKO_A_SPKR_PROT_TEST_BLOCK_EN			(0x1F3)
+#define TAIKO_A_SPKR_PROT_TEST_BLOCK_EN__POR				(0xFC)
+#define TAIKO_A_SPKR_PROT_ATEST			(0x1F4)
+#define TAIKO_A_SPKR_PROT_ATEST__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_V_SAR_ERR			(0x1F5)
+#define TAIKO_A_SPKR_PROT_V_SAR_ERR__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_I_SAR_ERR			(0x1F6)
+#define TAIKO_A_SPKR_PROT_I_SAR_ERR__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_LDO_CTRL			(0x1F7)
+#define TAIKO_A_SPKR_PROT_LDO_CTRL__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_ISENSE_CTRL			(0x1F8)
+#define TAIKO_A_SPKR_PROT_ISENSE_CTRL__POR				(0x00)
+#define TAIKO_A_SPKR_PROT_VSENSE_CTRL			(0x1F9)
+#define TAIKO_A_SPKR_PROT_VSENSE_CTRL__POR				(0x00)
+#define TAIKO_A_RC_OSC_FREQ			(0x1FA)
+#define TAIKO_A_RC_OSC_FREQ__POR				(0x46)
+#define TAIKO_A_RC_OSC_TEST			(0x1FB)
+#define TAIKO_A_RC_OSC_TEST__POR				(0x0A)
+#define TAIKO_A_RC_OSC_STATUS			(0x1FC)
+#define TAIKO_A_RC_OSC_STATUS__POR				(0x18)
+#define TAIKO_A_RC_OSC_TUNER			(0x1FD)
+#define TAIKO_A_RC_OSC_TUNER__POR				(0x00)
+#define TAIKO_A_MBHC_HPH			(0x1FE)
+#define TAIKO_A_MBHC_HPH__POR				(0x44)
+#define TAIKO_A_CDC_ANC1_B1_CTL			(0x200)
+#define TAIKO_A_CDC_ANC1_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_B1_CTL			(0x280)
+#define TAIKO_A_CDC_ANC2_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_SHIFT			(0x201)
+#define TAIKO_A_CDC_ANC1_SHIFT__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_SHIFT			(0x281)
+#define TAIKO_A_CDC_ANC2_SHIFT__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_IIR_B1_CTL			(0x202)
+#define TAIKO_A_CDC_ANC1_IIR_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_IIR_B1_CTL			(0x282)
+#define TAIKO_A_CDC_ANC2_IIR_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_IIR_B2_CTL			(0x203)
+#define TAIKO_A_CDC_ANC1_IIR_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_IIR_B2_CTL			(0x283)
+#define TAIKO_A_CDC_ANC2_IIR_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_IIR_B3_CTL			(0x204)
+#define TAIKO_A_CDC_ANC1_IIR_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_IIR_B3_CTL			(0x284)
+#define TAIKO_A_CDC_ANC2_IIR_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_LPF_B1_CTL			(0x206)
+#define TAIKO_A_CDC_ANC1_LPF_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_LPF_B1_CTL			(0x286)
+#define TAIKO_A_CDC_ANC2_LPF_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_LPF_B2_CTL			(0x207)
+#define TAIKO_A_CDC_ANC1_LPF_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_LPF_B2_CTL			(0x287)
+#define TAIKO_A_CDC_ANC2_LPF_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_SPARE			(0x209)
+#define TAIKO_A_CDC_ANC1_SPARE__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_SPARE			(0x289)
+#define TAIKO_A_CDC_ANC2_SPARE__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_SMLPF_CTL			(0x20A)
+#define TAIKO_A_CDC_ANC1_SMLPF_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_SMLPF_CTL			(0x28A)
+#define TAIKO_A_CDC_ANC2_SMLPF_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_DCFLT_CTL			(0x20B)
+#define TAIKO_A_CDC_ANC1_DCFLT_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_DCFLT_CTL			(0x28B)
+#define TAIKO_A_CDC_ANC2_DCFLT_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_GAIN_CTL			(0x20C)
+#define TAIKO_A_CDC_ANC1_GAIN_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_GAIN_CTL			(0x28C)
+#define TAIKO_A_CDC_ANC2_GAIN_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC1_B2_CTL			(0x20D)
+#define TAIKO_A_CDC_ANC1_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_ANC2_B2_CTL			(0x28D)
+#define TAIKO_A_CDC_ANC2_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX1_VOL_CTL_TIMER			(0x220)
+#define TAIKO_A_CDC_TX1_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX2_VOL_CTL_TIMER			(0x228)
+#define TAIKO_A_CDC_TX2_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX3_VOL_CTL_TIMER			(0x230)
+#define TAIKO_A_CDC_TX3_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX4_VOL_CTL_TIMER			(0x238)
+#define TAIKO_A_CDC_TX4_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX5_VOL_CTL_TIMER			(0x240)
+#define TAIKO_A_CDC_TX5_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX6_VOL_CTL_TIMER			(0x248)
+#define TAIKO_A_CDC_TX6_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX7_VOL_CTL_TIMER			(0x250)
+#define TAIKO_A_CDC_TX7_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX8_VOL_CTL_TIMER			(0x258)
+#define TAIKO_A_CDC_TX8_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX9_VOL_CTL_TIMER			(0x260)
+#define TAIKO_A_CDC_TX9_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX10_VOL_CTL_TIMER			(0x268)
+#define TAIKO_A_CDC_TX10_VOL_CTL_TIMER__POR				(0x00)
+#define TAIKO_A_CDC_TX1_VOL_CTL_GAIN			(0x221)
+#define TAIKO_A_CDC_TX1_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX2_VOL_CTL_GAIN			(0x229)
+#define TAIKO_A_CDC_TX2_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX3_VOL_CTL_GAIN			(0x231)
+#define TAIKO_A_CDC_TX3_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX4_VOL_CTL_GAIN			(0x239)
+#define TAIKO_A_CDC_TX4_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX5_VOL_CTL_GAIN			(0x241)
+#define TAIKO_A_CDC_TX5_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX6_VOL_CTL_GAIN			(0x249)
+#define TAIKO_A_CDC_TX6_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX7_VOL_CTL_GAIN			(0x251)
+#define TAIKO_A_CDC_TX7_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX8_VOL_CTL_GAIN			(0x259)
+#define TAIKO_A_CDC_TX8_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX9_VOL_CTL_GAIN			(0x261)
+#define TAIKO_A_CDC_TX9_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX10_VOL_CTL_GAIN			(0x269)
+#define TAIKO_A_CDC_TX10_VOL_CTL_GAIN__POR				(0x00)
+#define TAIKO_A_CDC_TX1_VOL_CTL_CFG			(0x222)
+#define TAIKO_A_CDC_TX1_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX2_VOL_CTL_CFG			(0x22A)
+#define TAIKO_A_CDC_TX2_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX3_VOL_CTL_CFG			(0x232)
+#define TAIKO_A_CDC_TX3_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX4_VOL_CTL_CFG			(0x23A)
+#define TAIKO_A_CDC_TX4_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX5_VOL_CTL_CFG			(0x242)
+#define TAIKO_A_CDC_TX5_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX6_VOL_CTL_CFG			(0x24A)
+#define TAIKO_A_CDC_TX6_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX7_VOL_CTL_CFG			(0x252)
+#define TAIKO_A_CDC_TX7_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX8_VOL_CTL_CFG			(0x25A)
+#define TAIKO_A_CDC_TX8_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX9_VOL_CTL_CFG			(0x262)
+#define TAIKO_A_CDC_TX9_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX10_VOL_CTL_CFG			(0x26A)
+#define TAIKO_A_CDC_TX10_VOL_CTL_CFG__POR				(0x00)
+#define TAIKO_A_CDC_TX1_MUX_CTL			(0x223)
+#define TAIKO_A_CDC_TX1_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX2_MUX_CTL			(0x22B)
+#define TAIKO_A_CDC_TX2_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX3_MUX_CTL			(0x233)
+#define TAIKO_A_CDC_TX3_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX4_MUX_CTL			(0x23B)
+#define TAIKO_A_CDC_TX4_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX5_MUX_CTL			(0x243)
+#define TAIKO_A_CDC_TX5_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX6_MUX_CTL			(0x24B)
+#define TAIKO_A_CDC_TX6_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX7_MUX_CTL			(0x253)
+#define TAIKO_A_CDC_TX7_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX8_MUX_CTL			(0x25B)
+#define TAIKO_A_CDC_TX8_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX9_MUX_CTL			(0x263)
+#define TAIKO_A_CDC_TX9_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX10_MUX_CTL			(0x26B)
+#define TAIKO_A_CDC_TX10_MUX_CTL__POR				(0x08)
+#define TAIKO_A_CDC_TX1_CLK_FS_CTL			(0x224)
+#define TAIKO_A_CDC_TX1_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX2_CLK_FS_CTL			(0x22C)
+#define TAIKO_A_CDC_TX2_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX3_CLK_FS_CTL			(0x234)
+#define TAIKO_A_CDC_TX3_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX4_CLK_FS_CTL			(0x23C)
+#define TAIKO_A_CDC_TX4_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX5_CLK_FS_CTL			(0x244)
+#define TAIKO_A_CDC_TX5_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX6_CLK_FS_CTL			(0x24C)
+#define TAIKO_A_CDC_TX6_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX7_CLK_FS_CTL			(0x254)
+#define TAIKO_A_CDC_TX7_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX8_CLK_FS_CTL			(0x25C)
+#define TAIKO_A_CDC_TX8_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX9_CLK_FS_CTL			(0x264)
+#define TAIKO_A_CDC_TX9_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX10_CLK_FS_CTL			(0x26C)
+#define TAIKO_A_CDC_TX10_CLK_FS_CTL__POR				(0x03)
+#define TAIKO_A_CDC_TX1_DMIC_CTL			(0x225)
+#define TAIKO_A_CDC_TX1_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX2_DMIC_CTL			(0x22D)
+#define TAIKO_A_CDC_TX2_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX3_DMIC_CTL			(0x235)
+#define TAIKO_A_CDC_TX3_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX4_DMIC_CTL			(0x23D)
+#define TAIKO_A_CDC_TX4_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX5_DMIC_CTL			(0x245)
+#define TAIKO_A_CDC_TX5_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX6_DMIC_CTL			(0x24D)
+#define TAIKO_A_CDC_TX6_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX7_DMIC_CTL			(0x255)
+#define TAIKO_A_CDC_TX7_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX8_DMIC_CTL			(0x25D)
+#define TAIKO_A_CDC_TX8_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX9_DMIC_CTL			(0x265)
+#define TAIKO_A_CDC_TX9_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TX10_DMIC_CTL			(0x26D)
+#define TAIKO_A_CDC_TX10_DMIC_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B1_CTL			(0x278)
+#define TAIKO_A_CDC_DEBUG_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B2_CTL			(0x279)
+#define TAIKO_A_CDC_DEBUG_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B3_CTL			(0x27A)
+#define TAIKO_A_CDC_DEBUG_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B4_CTL			(0x27B)
+#define TAIKO_A_CDC_DEBUG_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B5_CTL			(0x27C)
+#define TAIKO_A_CDC_DEBUG_B5_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B6_CTL			(0x27D)
+#define TAIKO_A_CDC_DEBUG_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_DEBUG_B7_CTL			(0x27E)
+#define TAIKO_A_CDC_DEBUG_B7_CTL__POR				(0x00)
+#define TAIKO_A_CDC_SRC1_PDA_CFG			(0x2A0)
+#define TAIKO_A_CDC_SRC1_PDA_CFG__POR				(0x00)
+#define TAIKO_A_CDC_SRC2_PDA_CFG			(0x2A8)
+#define TAIKO_A_CDC_SRC2_PDA_CFG__POR				(0x00)
+#define TAIKO_A_CDC_SRC1_FS_CTL			(0x2A1)
+#define TAIKO_A_CDC_SRC1_FS_CTL__POR				(0x1B)
+#define TAIKO_A_CDC_SRC2_FS_CTL			(0x2A9)
+#define TAIKO_A_CDC_SRC2_FS_CTL__POR				(0x1B)
+#define TAIKO_A_CDC_RX1_B1_CTL			(0x2B0)
+#define TAIKO_A_CDC_RX1_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX2_B1_CTL			(0x2B8)
+#define TAIKO_A_CDC_RX2_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX3_B1_CTL			(0x2C0)
+#define TAIKO_A_CDC_RX3_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX4_B1_CTL			(0x2C8)
+#define TAIKO_A_CDC_RX4_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX5_B1_CTL			(0x2D0)
+#define TAIKO_A_CDC_RX5_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX6_B1_CTL			(0x2D8)
+#define TAIKO_A_CDC_RX6_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX7_B1_CTL			(0x2E0)
+#define TAIKO_A_CDC_RX7_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX1_B2_CTL			(0x2B1)
+#define TAIKO_A_CDC_RX1_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX2_B2_CTL			(0x2B9)
+#define TAIKO_A_CDC_RX2_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX3_B2_CTL			(0x2C1)
+#define TAIKO_A_CDC_RX3_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX4_B2_CTL			(0x2C9)
+#define TAIKO_A_CDC_RX4_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX5_B2_CTL			(0x2D1)
+#define TAIKO_A_CDC_RX5_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX6_B2_CTL			(0x2D9)
+#define TAIKO_A_CDC_RX6_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX7_B2_CTL			(0x2E1)
+#define TAIKO_A_CDC_RX7_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX1_B3_CTL			(0x2B2)
+#define TAIKO_A_CDC_RX1_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX2_B3_CTL			(0x2BA)
+#define TAIKO_A_CDC_RX2_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX3_B3_CTL			(0x2C2)
+#define TAIKO_A_CDC_RX3_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX4_B3_CTL			(0x2CA)
+#define TAIKO_A_CDC_RX4_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX5_B3_CTL			(0x2D2)
+#define TAIKO_A_CDC_RX5_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX6_B3_CTL			(0x2DA)
+#define TAIKO_A_CDC_RX6_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX7_B3_CTL			(0x2E2)
+#define TAIKO_A_CDC_RX7_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX1_B4_CTL			(0x2B3)
+#define TAIKO_A_CDC_RX1_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX2_B4_CTL			(0x2BB)
+#define TAIKO_A_CDC_RX2_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX3_B4_CTL			(0x2C3)
+#define TAIKO_A_CDC_RX3_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX4_B4_CTL			(0x2CB)
+#define TAIKO_A_CDC_RX4_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX5_B4_CTL			(0x2D3)
+#define TAIKO_A_CDC_RX5_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX6_B4_CTL			(0x2DB)
+#define TAIKO_A_CDC_RX6_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX7_B4_CTL			(0x2E3)
+#define TAIKO_A_CDC_RX7_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX1_B5_CTL			(0x2B4)
+#define TAIKO_A_CDC_RX1_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX2_B5_CTL			(0x2BC)
+#define TAIKO_A_CDC_RX2_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX3_B5_CTL			(0x2C4)
+#define TAIKO_A_CDC_RX3_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX4_B5_CTL			(0x2CC)
+#define TAIKO_A_CDC_RX4_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX5_B5_CTL			(0x2D4)
+#define TAIKO_A_CDC_RX5_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX6_B5_CTL			(0x2DC)
+#define TAIKO_A_CDC_RX6_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX7_B5_CTL			(0x2E4)
+#define TAIKO_A_CDC_RX7_B5_CTL__POR				(0x78)
+#define TAIKO_A_CDC_RX1_B6_CTL			(0x2B5)
+#define TAIKO_A_CDC_RX1_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX2_B6_CTL			(0x2BD)
+#define TAIKO_A_CDC_RX2_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX3_B6_CTL			(0x2C5)
+#define TAIKO_A_CDC_RX3_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX4_B6_CTL			(0x2CD)
+#define TAIKO_A_CDC_RX4_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX5_B6_CTL			(0x2D5)
+#define TAIKO_A_CDC_RX5_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX6_B6_CTL			(0x2DD)
+#define TAIKO_A_CDC_RX6_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX7_B6_CTL			(0x2E5)
+#define TAIKO_A_CDC_RX7_B6_CTL__POR				(0x80)
+#define TAIKO_A_CDC_RX1_VOL_CTL_B1_CTL			(0x2B6)
+#define TAIKO_A_CDC_RX1_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX2_VOL_CTL_B1_CTL			(0x2BE)
+#define TAIKO_A_CDC_RX2_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX3_VOL_CTL_B1_CTL			(0x2C6)
+#define TAIKO_A_CDC_RX3_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX4_VOL_CTL_B1_CTL			(0x2CE)
+#define TAIKO_A_CDC_RX4_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX5_VOL_CTL_B1_CTL			(0x2D6)
+#define TAIKO_A_CDC_RX5_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX6_VOL_CTL_B1_CTL			(0x2DE)
+#define TAIKO_A_CDC_RX6_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX7_VOL_CTL_B1_CTL			(0x2E6)
+#define TAIKO_A_CDC_RX7_VOL_CTL_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL			(0x2B7)
+#define TAIKO_A_CDC_RX1_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL			(0x2BF)
+#define TAIKO_A_CDC_RX2_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL			(0x2C7)
+#define TAIKO_A_CDC_RX3_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL			(0x2CF)
+#define TAIKO_A_CDC_RX4_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL			(0x2D7)
+#define TAIKO_A_CDC_RX5_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL			(0x2DF)
+#define TAIKO_A_CDC_RX6_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL			(0x2E7)
+#define TAIKO_A_CDC_RX7_VOL_CTL_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_VBAT_CFG			(0x2E8)
+#define TAIKO_A_CDC_VBAT_CFG__POR				(0x1A)
+#define TAIKO_A_CDC_VBAT_ADC_CAL1			(0x2E9)
+#define TAIKO_A_CDC_VBAT_ADC_CAL1__POR				(0x00)
+#define TAIKO_A_CDC_VBAT_ADC_CAL2			(0x2EA)
+#define TAIKO_A_CDC_VBAT_ADC_CAL2__POR				(0x00)
+#define TAIKO_A_CDC_VBAT_ADC_CAL3			(0x2EB)
+#define TAIKO_A_CDC_VBAT_ADC_CAL3__POR				(0x04)
+#define TAIKO_A_CDC_VBAT_PK_EST1			(0x2EC)
+#define TAIKO_A_CDC_VBAT_PK_EST1__POR				(0xE0)
+#define TAIKO_A_CDC_VBAT_PK_EST2			(0x2ED)
+#define TAIKO_A_CDC_VBAT_PK_EST2__POR				(0x01)
+#define TAIKO_A_CDC_VBAT_PK_EST3			(0x2EE)
+#define TAIKO_A_CDC_VBAT_PK_EST3__POR				(0x40)
+#define TAIKO_A_CDC_VBAT_RF_PROC1			(0x2EF)
+#define TAIKO_A_CDC_VBAT_RF_PROC1__POR				(0x2A)
+#define TAIKO_A_CDC_VBAT_RF_PROC2			(0x2F0)
+#define TAIKO_A_CDC_VBAT_RF_PROC2__POR				(0x86)
+#define TAIKO_A_CDC_VBAT_TAC1			(0x2F1)
+#define TAIKO_A_CDC_VBAT_TAC1__POR				(0x70)
+#define TAIKO_A_CDC_VBAT_TAC2			(0x2F2)
+#define TAIKO_A_CDC_VBAT_TAC2__POR				(0x18)
+#define TAIKO_A_CDC_VBAT_TAC3			(0x2F3)
+#define TAIKO_A_CDC_VBAT_TAC3__POR				(0x18)
+#define TAIKO_A_CDC_VBAT_TAC4			(0x2F4)
+#define TAIKO_A_CDC_VBAT_TAC4__POR				(0x03)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD1			(0x2F5)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD1__POR				(0x01)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD2			(0x2F6)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD2__POR				(0x00)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD3			(0x2F7)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD3__POR				(0x64)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD4			(0x2F8)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD4__POR				(0x01)
+#define TAIKO_A_CDC_VBAT_DEBUG1			(0x2F9)
+#define TAIKO_A_CDC_VBAT_DEBUG1__POR				(0x00)
+#define TAIKO_A_CDC_CLK_ANC_RESET_CTL			(0x300)
+#define TAIKO_A_CDC_CLK_ANC_RESET_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_RX_RESET_CTL			(0x301)
+#define TAIKO_A_CDC_CLK_RX_RESET_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_TX_RESET_B1_CTL			(0x302)
+#define TAIKO_A_CDC_CLK_TX_RESET_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_TX_RESET_B2_CTL			(0x303)
+#define TAIKO_A_CDC_CLK_TX_RESET_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_DMIC_B1_CTL			(0x304)
+#define TAIKO_A_CDC_CLK_DMIC_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_DMIC_B2_CTL			(0x305)
+#define TAIKO_A_CDC_CLK_DMIC_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_RX_I2S_CTL			(0x306)
+#define TAIKO_A_CDC_CLK_RX_I2S_CTL__POR				(0x03)
+#define TAIKO_A_CDC_CLK_TX_I2S_CTL			(0x307)
+#define TAIKO_A_CDC_CLK_TX_I2S_CTL__POR				(0x03)
+#define TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL			(0x308)
+#define TAIKO_A_CDC_CLK_OTHR_RESET_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL			(0x309)
+#define TAIKO_A_CDC_CLK_OTHR_RESET_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL			(0x30A)
+#define TAIKO_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL			(0x30B)
+#define TAIKO_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_OTHR_CTL			(0x30C)
+#define TAIKO_A_CDC_CLK_OTHR_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL			(0x30D)
+#define TAIKO_A_CDC_CLK_RDAC_CLK_EN_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_ANC_CLK_EN_CTL			(0x30E)
+#define TAIKO_A_CDC_CLK_ANC_CLK_EN_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_RX_B1_CTL			(0x30F)
+#define TAIKO_A_CDC_CLK_RX_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_RX_B2_CTL			(0x310)
+#define TAIKO_A_CDC_CLK_RX_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_MCLK_CTL			(0x311)
+#define TAIKO_A_CDC_CLK_MCLK_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_PDM_CTL			(0x312)
+#define TAIKO_A_CDC_CLK_PDM_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_SD_CTL			(0x313)
+#define TAIKO_A_CDC_CLK_SD_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLK_POWER_CTL			(0x314)
+#define TAIKO_A_CDC_CLK_POWER_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_B1_CTL			(0x320)
+#define TAIKO_A_CDC_CLSH_B1_CTL__POR				(0xE4)
+#define TAIKO_A_CDC_CLSH_B2_CTL			(0x321)
+#define TAIKO_A_CDC_CLSH_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_B3_CTL			(0x322)
+#define TAIKO_A_CDC_CLSH_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_BUCK_NCP_VARS			(0x323)
+#define TAIKO_A_CDC_CLSH_BUCK_NCP_VARS__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_IDLE_HPH_THSD			(0x324)
+#define TAIKO_A_CDC_CLSH_IDLE_HPH_THSD__POR				(0x12)
+#define TAIKO_A_CDC_CLSH_IDLE_EAR_THSD			(0x325)
+#define TAIKO_A_CDC_CLSH_IDLE_EAR_THSD__POR				(0x0C)
+#define TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD			(0x326)
+#define TAIKO_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR				(0x18)
+#define TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD			(0x327)
+#define TAIKO_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR				(0x23)
+#define TAIKO_A_CDC_CLSH_K_ADDR			(0x328)
+#define TAIKO_A_CDC_CLSH_K_ADDR__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_K_DATA			(0x329)
+#define TAIKO_A_CDC_CLSH_K_DATA__POR				(0xA4)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L			(0x32A)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_L__POR				(0xD7)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U			(0x32B)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_HPH_U__POR				(0x05)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L			(0x32C)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_L__POR				(0x60)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U			(0x32D)
+#define TAIKO_A_CDC_CLSH_I_PA_FACT_EAR_U__POR				(0x09)
+#define TAIKO_A_CDC_CLSH_V_PA_HD_EAR			(0x32E)
+#define TAIKO_A_CDC_CLSH_V_PA_HD_EAR__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_V_PA_HD_HPH			(0x32F)
+#define TAIKO_A_CDC_CLSH_V_PA_HD_HPH__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_V_PA_MIN_EAR			(0x330)
+#define TAIKO_A_CDC_CLSH_V_PA_MIN_EAR__POR				(0x00)
+#define TAIKO_A_CDC_CLSH_V_PA_MIN_HPH			(0x331)
+#define TAIKO_A_CDC_CLSH_V_PA_MIN_HPH__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B1_CTL			(0x340)
+#define TAIKO_A_CDC_IIR1_GAIN_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B1_CTL			(0x350)
+#define TAIKO_A_CDC_IIR2_GAIN_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B2_CTL			(0x341)
+#define TAIKO_A_CDC_IIR1_GAIN_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B2_CTL			(0x351)
+#define TAIKO_A_CDC_IIR2_GAIN_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B3_CTL			(0x342)
+#define TAIKO_A_CDC_IIR1_GAIN_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B3_CTL			(0x352)
+#define TAIKO_A_CDC_IIR2_GAIN_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B4_CTL			(0x343)
+#define TAIKO_A_CDC_IIR1_GAIN_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B4_CTL			(0x353)
+#define TAIKO_A_CDC_IIR2_GAIN_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B5_CTL			(0x344)
+#define TAIKO_A_CDC_IIR1_GAIN_B5_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B5_CTL			(0x354)
+#define TAIKO_A_CDC_IIR2_GAIN_B5_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B6_CTL			(0x345)
+#define TAIKO_A_CDC_IIR1_GAIN_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B6_CTL			(0x355)
+#define TAIKO_A_CDC_IIR2_GAIN_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B7_CTL			(0x346)
+#define TAIKO_A_CDC_IIR1_GAIN_B7_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B7_CTL			(0x356)
+#define TAIKO_A_CDC_IIR2_GAIN_B7_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_GAIN_B8_CTL			(0x347)
+#define TAIKO_A_CDC_IIR1_GAIN_B8_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_B8_CTL			(0x357)
+#define TAIKO_A_CDC_IIR2_GAIN_B8_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_CTL			(0x348)
+#define TAIKO_A_CDC_IIR1_CTL__POR				(0x40)
+#define TAIKO_A_CDC_IIR2_CTL			(0x358)
+#define TAIKO_A_CDC_IIR2_CTL__POR				(0x40)
+#define TAIKO_A_CDC_IIR1_GAIN_TIMER_CTL			(0x349)
+#define TAIKO_A_CDC_IIR1_GAIN_TIMER_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_GAIN_TIMER_CTL			(0x359)
+#define TAIKO_A_CDC_IIR2_GAIN_TIMER_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_COEF_B1_CTL			(0x34A)
+#define TAIKO_A_CDC_IIR1_COEF_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_COEF_B1_CTL			(0x35A)
+#define TAIKO_A_CDC_IIR2_COEF_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR1_COEF_B2_CTL			(0x34B)
+#define TAIKO_A_CDC_IIR1_COEF_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_IIR2_COEF_B2_CTL			(0x35B)
+#define TAIKO_A_CDC_IIR2_COEF_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_TOP_GAIN_UPDATE			(0x360)
+#define TAIKO_A_CDC_TOP_GAIN_UPDATE__POR				(0x00)
+#define TAIKO_A_CDC_COMP0_B1_CTL			(0x368)
+#define TAIKO_A_CDC_COMP0_B1_CTL__POR				(0x30)
+#define TAIKO_A_CDC_COMP1_B1_CTL			(0x370)
+#define TAIKO_A_CDC_COMP1_B1_CTL__POR				(0x30)
+#define TAIKO_A_CDC_COMP2_B1_CTL			(0x378)
+#define TAIKO_A_CDC_COMP2_B1_CTL__POR				(0x30)
+#define TAIKO_A_CDC_COMP0_B2_CTL			(0x369)
+#define TAIKO_A_CDC_COMP0_B2_CTL__POR				(0xB5)
+#define TAIKO_A_CDC_COMP1_B2_CTL			(0x371)
+#define TAIKO_A_CDC_COMP1_B2_CTL__POR				(0xB5)
+#define TAIKO_A_CDC_COMP2_B2_CTL			(0x379)
+#define TAIKO_A_CDC_COMP2_B2_CTL__POR				(0xB5)
+#define TAIKO_A_CDC_COMP0_B3_CTL			(0x36A)
+#define TAIKO_A_CDC_COMP0_B3_CTL__POR				(0x28)
+#define TAIKO_A_CDC_COMP1_B3_CTL			(0x372)
+#define TAIKO_A_CDC_COMP1_B3_CTL__POR				(0x28)
+#define TAIKO_A_CDC_COMP2_B3_CTL			(0x37A)
+#define TAIKO_A_CDC_COMP2_B3_CTL__POR				(0x28)
+#define TAIKO_A_CDC_COMP0_B4_CTL			(0x36B)
+#define TAIKO_A_CDC_COMP0_B4_CTL__POR				(0x3C)
+#define TAIKO_A_CDC_COMP1_B4_CTL			(0x373)
+#define TAIKO_A_CDC_COMP1_B4_CTL__POR				(0x3C)
+#define TAIKO_A_CDC_COMP2_B4_CTL			(0x37B)
+#define TAIKO_A_CDC_COMP2_B4_CTL__POR				(0x3C)
+#define TAIKO_A_CDC_COMP0_B5_CTL			(0x36C)
+#define TAIKO_A_CDC_COMP0_B5_CTL__POR				(0x1F)
+#define TAIKO_A_CDC_COMP1_B5_CTL			(0x374)
+#define TAIKO_A_CDC_COMP1_B5_CTL__POR				(0x1F)
+#define TAIKO_A_CDC_COMP2_B5_CTL			(0x37C)
+#define TAIKO_A_CDC_COMP2_B5_CTL__POR				(0x1F)
+#define TAIKO_A_CDC_COMP0_B6_CTL			(0x36D)
+#define TAIKO_A_CDC_COMP0_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_COMP1_B6_CTL			(0x375)
+#define TAIKO_A_CDC_COMP1_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_COMP2_B6_CTL			(0x37D)
+#define TAIKO_A_CDC_COMP2_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS			(0x36E)
+#define TAIKO_A_CDC_COMP0_SHUT_DOWN_STATUS__POR				(0x03)
+#define TAIKO_A_CDC_COMP1_SHUT_DOWN_STATUS			(0x376)
+#define TAIKO_A_CDC_COMP1_SHUT_DOWN_STATUS__POR				(0x03)
+#define TAIKO_A_CDC_COMP2_SHUT_DOWN_STATUS			(0x37E)
+#define TAIKO_A_CDC_COMP2_SHUT_DOWN_STATUS__POR				(0x03)
+#define TAIKO_A_CDC_COMP0_FS_CFG			(0x36F)
+#define TAIKO_A_CDC_COMP0_FS_CFG__POR				(0x03)
+#define TAIKO_A_CDC_COMP1_FS_CFG			(0x377)
+#define TAIKO_A_CDC_COMP1_FS_CFG__POR				(0x03)
+#define TAIKO_A_CDC_COMP2_FS_CFG			(0x37F)
+#define TAIKO_A_CDC_COMP2_FS_CFG__POR				(0x03)
+#define TAIKO_A_CDC_CONN_RX1_B1_CTL			(0x380)
+#define TAIKO_A_CDC_CONN_RX1_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX1_B2_CTL			(0x381)
+#define TAIKO_A_CDC_CONN_RX1_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX1_B3_CTL			(0x382)
+#define TAIKO_A_CDC_CONN_RX1_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX2_B1_CTL			(0x383)
+#define TAIKO_A_CDC_CONN_RX2_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX2_B2_CTL			(0x384)
+#define TAIKO_A_CDC_CONN_RX2_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX2_B3_CTL			(0x385)
+#define TAIKO_A_CDC_CONN_RX2_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX3_B1_CTL			(0x386)
+#define TAIKO_A_CDC_CONN_RX3_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX3_B2_CTL			(0x387)
+#define TAIKO_A_CDC_CONN_RX3_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX4_B1_CTL			(0x388)
+#define TAIKO_A_CDC_CONN_RX4_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX4_B2_CTL			(0x389)
+#define TAIKO_A_CDC_CONN_RX4_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX5_B1_CTL			(0x38A)
+#define TAIKO_A_CDC_CONN_RX5_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX5_B2_CTL			(0x38B)
+#define TAIKO_A_CDC_CONN_RX5_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX6_B1_CTL			(0x38C)
+#define TAIKO_A_CDC_CONN_RX6_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX6_B2_CTL			(0x38D)
+#define TAIKO_A_CDC_CONN_RX6_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX7_B1_CTL			(0x38E)
+#define TAIKO_A_CDC_CONN_RX7_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX7_B2_CTL			(0x38F)
+#define TAIKO_A_CDC_CONN_RX7_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX7_B3_CTL			(0x390)
+#define TAIKO_A_CDC_CONN_RX7_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_ANC_B1_CTL			(0x391)
+#define TAIKO_A_CDC_CONN_ANC_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_ANC_B2_CTL			(0x392)
+#define TAIKO_A_CDC_CONN_ANC_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_B1_CTL			(0x393)
+#define TAIKO_A_CDC_CONN_TX_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_B2_CTL			(0x394)
+#define TAIKO_A_CDC_CONN_TX_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_B3_CTL			(0x395)
+#define TAIKO_A_CDC_CONN_TX_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_B4_CTL			(0x396)
+#define TAIKO_A_CDC_CONN_TX_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ1_B1_CTL			(0x397)
+#define TAIKO_A_CDC_CONN_EQ1_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ1_B2_CTL			(0x398)
+#define TAIKO_A_CDC_CONN_EQ1_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ1_B3_CTL			(0x399)
+#define TAIKO_A_CDC_CONN_EQ1_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ1_B4_CTL			(0x39A)
+#define TAIKO_A_CDC_CONN_EQ1_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ2_B1_CTL			(0x39B)
+#define TAIKO_A_CDC_CONN_EQ2_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ2_B2_CTL			(0x39C)
+#define TAIKO_A_CDC_CONN_EQ2_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ2_B3_CTL			(0x39D)
+#define TAIKO_A_CDC_CONN_EQ2_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_EQ2_B4_CTL			(0x39E)
+#define TAIKO_A_CDC_CONN_EQ2_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_SRC1_B1_CTL			(0x39F)
+#define TAIKO_A_CDC_CONN_SRC1_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_SRC1_B2_CTL			(0x3A0)
+#define TAIKO_A_CDC_CONN_SRC1_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_SRC2_B1_CTL			(0x3A1)
+#define TAIKO_A_CDC_CONN_SRC2_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_SRC2_B2_CTL			(0x3A2)
+#define TAIKO_A_CDC_CONN_SRC2_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B1_CTL			(0x3A3)
+#define TAIKO_A_CDC_CONN_TX_SB_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B2_CTL			(0x3A4)
+#define TAIKO_A_CDC_CONN_TX_SB_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B3_CTL			(0x3A5)
+#define TAIKO_A_CDC_CONN_TX_SB_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B4_CTL			(0x3A6)
+#define TAIKO_A_CDC_CONN_TX_SB_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B5_CTL			(0x3A7)
+#define TAIKO_A_CDC_CONN_TX_SB_B5_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B6_CTL			(0x3A8)
+#define TAIKO_A_CDC_CONN_TX_SB_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B7_CTL			(0x3A9)
+#define TAIKO_A_CDC_CONN_TX_SB_B7_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B8_CTL			(0x3AA)
+#define TAIKO_A_CDC_CONN_TX_SB_B8_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B9_CTL			(0x3AB)
+#define TAIKO_A_CDC_CONN_TX_SB_B9_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B10_CTL			(0x3AC)
+#define TAIKO_A_CDC_CONN_TX_SB_B10_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_TX_SB_B11_CTL			(0x3AD)
+#define TAIKO_A_CDC_CONN_TX_SB_B11_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX_SB_B1_CTL			(0x3AE)
+#define TAIKO_A_CDC_CONN_RX_SB_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_RX_SB_B2_CTL			(0x3AF)
+#define TAIKO_A_CDC_CONN_RX_SB_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_CLSH_CTL			(0x3B0)
+#define TAIKO_A_CDC_CONN_CLSH_CTL__POR				(0x00)
+#define TAIKO_A_CDC_CONN_MISC			(0x3B1)
+#define TAIKO_A_CDC_CONN_MISC__POR				(0x01)
+#define TAIKO_A_CDC_CONN_MAD			(0x3B2)
+#define TAIKO_A_CDC_CONN_MAD__POR				(0x01)
+#define TAIKO_A_CDC_MBHC_EN_CTL			(0x3C0)
+#define TAIKO_A_CDC_MBHC_EN_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_FIR_B1_CFG			(0x3C1)
+#define TAIKO_A_CDC_MBHC_FIR_B1_CFG__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_FIR_B2_CFG			(0x3C2)
+#define TAIKO_A_CDC_MBHC_FIR_B2_CFG__POR				(0x06)
+#define TAIKO_A_CDC_MBHC_TIMER_B1_CTL			(0x3C3)
+#define TAIKO_A_CDC_MBHC_TIMER_B1_CTL__POR				(0x03)
+#define TAIKO_A_CDC_MBHC_TIMER_B2_CTL			(0x3C4)
+#define TAIKO_A_CDC_MBHC_TIMER_B2_CTL__POR				(0x09)
+#define TAIKO_A_CDC_MBHC_TIMER_B3_CTL			(0x3C5)
+#define TAIKO_A_CDC_MBHC_TIMER_B3_CTL__POR				(0x1E)
+#define TAIKO_A_CDC_MBHC_TIMER_B4_CTL			(0x3C6)
+#define TAIKO_A_CDC_MBHC_TIMER_B4_CTL__POR				(0x45)
+#define TAIKO_A_CDC_MBHC_TIMER_B5_CTL			(0x3C7)
+#define TAIKO_A_CDC_MBHC_TIMER_B5_CTL__POR				(0x04)
+#define TAIKO_A_CDC_MBHC_TIMER_B6_CTL			(0x3C8)
+#define TAIKO_A_CDC_MBHC_TIMER_B6_CTL__POR				(0x78)
+#define TAIKO_A_CDC_MBHC_B1_STATUS			(0x3C9)
+#define TAIKO_A_CDC_MBHC_B1_STATUS__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_B2_STATUS			(0x3CA)
+#define TAIKO_A_CDC_MBHC_B2_STATUS__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_B3_STATUS			(0x3CB)
+#define TAIKO_A_CDC_MBHC_B3_STATUS__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_B4_STATUS			(0x3CC)
+#define TAIKO_A_CDC_MBHC_B4_STATUS__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_B5_STATUS			(0x3CD)
+#define TAIKO_A_CDC_MBHC_B5_STATUS__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_B1_CTL			(0x3CE)
+#define TAIKO_A_CDC_MBHC_B1_CTL__POR				(0xC0)
+#define TAIKO_A_CDC_MBHC_B2_CTL			(0x3CF)
+#define TAIKO_A_CDC_MBHC_B2_CTL__POR				(0x5D)
+#define TAIKO_A_CDC_MBHC_VOLT_B1_CTL			(0x3D0)
+#define TAIKO_A_CDC_MBHC_VOLT_B1_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B2_CTL			(0x3D1)
+#define TAIKO_A_CDC_MBHC_VOLT_B2_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B3_CTL			(0x3D2)
+#define TAIKO_A_CDC_MBHC_VOLT_B3_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B4_CTL			(0x3D3)
+#define TAIKO_A_CDC_MBHC_VOLT_B4_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B5_CTL			(0x3D4)
+#define TAIKO_A_CDC_MBHC_VOLT_B5_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B6_CTL			(0x3D5)
+#define TAIKO_A_CDC_MBHC_VOLT_B6_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B7_CTL			(0x3D6)
+#define TAIKO_A_CDC_MBHC_VOLT_B7_CTL__POR				(0xFF)
+#define TAIKO_A_CDC_MBHC_VOLT_B8_CTL			(0x3D7)
+#define TAIKO_A_CDC_MBHC_VOLT_B8_CTL__POR				(0x07)
+#define TAIKO_A_CDC_MBHC_VOLT_B9_CTL			(0x3D8)
+#define TAIKO_A_CDC_MBHC_VOLT_B9_CTL__POR				(0xFF)
+#define TAIKO_A_CDC_MBHC_VOLT_B10_CTL			(0x3D9)
+#define TAIKO_A_CDC_MBHC_VOLT_B10_CTL__POR				(0x7F)
+#define TAIKO_A_CDC_MBHC_VOLT_B11_CTL			(0x3DA)
+#define TAIKO_A_CDC_MBHC_VOLT_B11_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_VOLT_B12_CTL			(0x3DB)
+#define TAIKO_A_CDC_MBHC_VOLT_B12_CTL__POR				(0x80)
+#define TAIKO_A_CDC_MBHC_CLK_CTL			(0x3DC)
+#define TAIKO_A_CDC_MBHC_CLK_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_INT_CTL			(0x3DD)
+#define TAIKO_A_CDC_MBHC_INT_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_DEBUG_CTL			(0x3DE)
+#define TAIKO_A_CDC_MBHC_DEBUG_CTL__POR				(0x00)
+#define TAIKO_A_CDC_MBHC_SPARE			(0x3DF)
+#define TAIKO_A_CDC_MBHC_SPARE__POR				(0x00)
+#define TAIKO_A_CDC_MAD_MAIN_CTL_1			(0x3E0)
+#define TAIKO_A_CDC_MAD_MAIN_CTL_1__POR				(0x00)
+#define TAIKO_A_CDC_MAD_MAIN_CTL_2			(0x3E1)
+#define TAIKO_A_CDC_MAD_MAIN_CTL_2__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_1			(0x3E2)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_1__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_2			(0x3E3)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_2__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_3			(0x3E4)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_3__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_4			(0x3E5)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_4__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_5			(0x3E6)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_5__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_6			(0x3E7)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_6__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_7			(0x3E8)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_7__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_8			(0x3E9)
+#define TAIKO_A_CDC_MAD_AUDIO_CTL_8__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR			(0x3EA)
+#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR				(0x00)
+#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL			(0x3EB)
+#define TAIKO_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR				(0x40)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_1			(0x3EC)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_1__POR				(0x00)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_2			(0x3ED)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_2__POR				(0x00)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_3			(0x3EE)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_3__POR				(0x00)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_4			(0x3EF)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_4__POR				(0x00)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_5			(0x3F0)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_5__POR				(0x00)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_6			(0x3F1)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_6__POR				(0x00)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_7			(0x3F2)
+#define TAIKO_A_CDC_MAD_ULTR_CTL_7__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_1			(0x3F3)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_1__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_2			(0x3F4)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_2__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_3			(0x3F5)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_3__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_4			(0x3F6)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_4__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_5			(0x3F7)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_5__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_6			(0x3F8)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_6__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_7			(0x3F9)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_7__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_8			(0x3FA)
+#define TAIKO_A_CDC_MAD_BEACON_CTL_8__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR			(0x3FB)
+#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR				(0x00)
+#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL			(0x3FC)
+#define TAIKO_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR				(0x00)
+
+/* Taiko v2+ registers */
+#define TAIKO_A_CDC_TX_1_GAIN			(0x153)
+#define TAIKO_A_CDC_TX_1_GAIN__POR			(0x02)
+#define TAIKO_A_CDC_TX_2_GAIN			(0x155)
+#define TAIKO_A_CDC_TX_2_GAIN__POR			(0x02)
+#define TAIKO_A_CDC_TX_1_2_ADC_IB		(0x156)
+#define TAIKO_A_CDC_TX_1_2_ADC_IB__POR			(0x44)
+#define TAIKO_A_CDC_TX_3_GAIN			(0x15D)
+#define TAIKO_A_CDC_TX_3_GAIN__POR			(0x02)
+#define TAIKO_A_CDC_TX_4_GAIN			(0x15F)
+#define TAIKO_A_CDC_TX_4_GAIN__POR			(0x02)
+#define TAIKO_A_CDC_TX_3_4_ADC_IB		(0x160)
+#define TAIKO_A_CDC_TX_3_4_ADC_IB__POR			(0x44)
+#define TAIKO_A_CDC_TX_5_GAIN			(0x167)
+#define TAIKO_A_CDC_TX_5_GAIN__POR			(0x02)
+#define TAIKO_A_CDC_TX_6_GAIN			(0x169)
+#define TAIKO_A_CDC_TX_6_GAIN__POR			(0x02)
+#define TAIKO_A_CDC_TX_5_6_ADC_IB		(0x16A)
+#define TAIKO_A_CDC_TX_5_6_ADC_IB__POR			(0x44)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL0		(0x270)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL0__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL1		(0x271)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL1__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL2		(0x272)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL2__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL3		(0x273)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL3__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL4		(0x274)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL4__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL5		(0x275)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL5__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL6		(0x276)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL6__POR		(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL7		(0x277)
+#define TAIKO_A_CDC_SPKR_CLIPDET_VAL7__POR		(0x00)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD_MON		(0x2FA)
+#define TAIKO_A_CDC_VBAT_GAIN_UPD_MON__POR		(0x00)
+#define TAIKO_A_CDC_VBAT_GAIN_MON_VAL		(0x2FB)
+#define TAIKO_A_CDC_VBAT_GAIN_MON_VAL__POR		(0x00)
+#define TAIKO_A_CDC_PA_RAMP_B1_CTL		(0x361)
+#define TAIKO_A_CDC_PA_RAMP_B1_CTL__POR			(0x00)
+#define TAIKO_A_CDC_PA_RAMP_B2_CTL		(0x362)
+#define TAIKO_A_CDC_PA_RAMP_B2_CTL__POR			(0x00)
+#define TAIKO_A_CDC_PA_RAMP_B3_CTL		(0x363)
+#define TAIKO_A_CDC_PA_RAMP_B3_CTL__POR			(0x00)
+#define TAIKO_A_CDC_PA_RAMP_B4_CTL		(0x364)
+#define TAIKO_A_CDC_PA_RAMP_B4_CTL__POR			(0x00)
+#define TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL		(0x365)
+#define TAIKO_A_CDC_SPKR_CLIPDET_B1_CTL__POR		(0x00)
+
+/* SLIMBUS Slave Registers */
+#define TAIKO_SLIM_PGD_PORT_INT_EN0                     (0x30)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_0             (0x34)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_RX_1             (0x35)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_0             (0x36)
+#define TAIKO_SLIM_PGD_PORT_INT_STATUS_TX_1             (0x37)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_0                (0x38)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_RX_1                (0x39)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_0                (0x3A)
+#define TAIKO_SLIM_PGD_PORT_INT_CLR_TX_1                (0x3B)
+#define TAIKO_SLIM_PGD_PORT_INT_RX_SOURCE0		(0x60)
+#define TAIKO_SLIM_PGD_PORT_INT_TX_SOURCE0		(0x70)
+
+/* Macros for Packing Register Writes into a U32 */
+#define TAIKO_PACKED_REG_SIZE sizeof(u32)
+
+#define TAIKO_CODEC_PACK_ENTRY(reg, mask, val) ((val & 0xff)|\
+	((mask & 0xff) << 8)|((reg & 0xffff) << 16))
+
+#define TAIKO_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+	do { \
+		((reg) = ((packed >> 16) & (0xffff))); \
+		((mask) = ((packed >> 8) & (0xff))); \
+		((val) = ((packed) & (0xff))); \
+	} while (0);
+
+#endif
diff --git a/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h
new file mode 100644
index 0000000..7902cfb
--- /dev/null
+++ b/include/uapi/linux/mfd/wcd9xxx/wcd9xxx_registers.h
@@ -0,0 +1,361 @@
+#ifndef WCD9XXX_CODEC_DIGITAL_H
+
+#define WCD9XXX_CODEC_DIGITAL_H
+
+#define WCD9XXX_A_CHIP_CTL			(0x00)
+#define WCD9XXX_A_CHIP_CTL__POR			(0x00000000)
+#define WCD9XXX_A_CHIP_STATUS			(0x01)
+#define WCD9XXX_A_CHIP_STATUS__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_0		(0x04)
+#define WCD9XXX_A_CHIP_ID_BYTE_0__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_1		(0x05)
+#define WCD9XXX_A_CHIP_ID_BYTE_1__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_2		(0x06)
+#define WCD9XXX_A_CHIP_ID_BYTE_2__POR		(0x00000000)
+#define WCD9XXX_A_CHIP_ID_BYTE_3		(0x07)
+#define WCD9XXX_A_CHIP_ID_BYTE_3__POR		(0x00000001)
+#define WCD9XXX_A_CHIP_VERSION			(0x08)
+#define WCD9XXX_A_CHIP_VERSION__POR		(0x00000020)
+#define WCD9XXX_A_SB_VERSION			(0x09)
+#define WCD9XXX_A_SB_VERSION__POR		(0x00000010)
+#define WCD9XXX_A_SLAVE_ID_1			(0x0C)
+#define WCD9XXX_A_SLAVE_ID_1__POR		(0x00000077)
+#define WCD9XXX_A_SLAVE_ID_2			(0x0D)
+#define WCD9XXX_A_SLAVE_ID_2__POR		(0x00000066)
+#define WCD9XXX_A_SLAVE_ID_3			(0x0E)
+#define WCD9XXX_A_SLAVE_ID_3__POR		(0x00000055)
+#define WCD9XXX_A_CDC_CTL			(0x80)
+#define WCD9XXX_A_CDC_CTL__POR			(0x00000000)
+#define WCD9XXX_A_LEAKAGE_CTL			(0x88)
+#define WCD9XXX_A_LEAKAGE_CTL__POR		(0x00000004)
+#define WCD9XXX_A_INTR_MODE			(0x90)
+#define WCD9XXX_A_INTR_MASK0			(0x94)
+#define WCD9XXX_A_INTR_STATUS0			(0x98)
+#define WCD9XXX_A_INTR_CLEAR0			(0x9C)
+#define WCD9XXX_A_INTR_LEVEL0			(0xA0)
+#define WCD9XXX_A_INTR_LEVEL1			(0xA1)
+#define WCD9XXX_A_INTR_LEVEL2			(0xA2)
+#define WCD9XXX_A_RX_HPH_CNP_EN			(0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR		(0x80)
+#define WCD9XXX_A_RX_HPH_CNP_EN			(0x1AB)
+#define WCD9XXX_A_RX_HPH_CNP_EN__POR		(0x80)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL		(0x101)
+#define WCD9XXX_A_BIAS_CENTRAL_BG_CTL__POR	(0x50)
+#define WCD9XXX_A_CLK_BUFF_EN1			(0x108)
+#define WCD9XXX_A_CLK_BUFF_EN1__POR		(0x04)
+#define WCD9XXX_A_CLK_BUFF_EN2			(0x109)
+#define WCD9XXX_A_CLK_BUFF_EN2__POR		(0x02)
+#define WCD9XXX_A_RX_COM_BIAS			(0x1A2)
+#define WCD9XXX_A_RX_COM_BIAS__POR		(0x00)
+#define WCD9XXX_A_RC_OSC_FREQ			(0x1FA)
+#define WCD9XXX_A_RC_OSC_FREQ__POR		(0x46)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL		(0x105)
+#define WCD9XXX_A_BIAS_OSC_BG_CTL__POR		(0x16)
+#define WCD9XXX_A_RC_OSC_TEST			(0x1FB)
+#define WCD9XXX_A_RC_OSC_TEST__POR		(0x0A)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL		(0x311)
+#define WCD9XXX_A_CDC_CLK_MCLK_CTL__POR		(0x00)
+
+#define WCD9XXX_A_CDC_MBHC_EN_CTL		(0x3C0)
+#define WCD9XXX_A_CDC_MBHC_EN_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG		(0x3C1)
+#define WCD9XXX_A_CDC_MBHC_FIR_B1_CFG__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG		(0x3C2)
+#define WCD9XXX_A_CDC_MBHC_FIR_B2_CFG__POR	(0x06)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL		(0x3C3)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL__POR	(0x03)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL		(0x3C4)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL__POR	(0x09)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL		(0x3C5)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL__POR	(0x1E)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL		(0x3C6)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL__POR	(0x45)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL		(0x3C7)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL__POR	(0x04)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL		(0x3C8)
+#define WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL__POR	(0x78)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS		(0x3C9)
+#define WCD9XXX_A_CDC_MBHC_B1_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS		(0x3CA)
+#define WCD9XXX_A_CDC_MBHC_B2_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS		(0x3CB)
+#define WCD9XXX_A_CDC_MBHC_B3_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS		(0x3CC)
+#define WCD9XXX_A_CDC_MBHC_B4_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS		(0x3CD)
+#define WCD9XXX_A_CDC_MBHC_B5_STATUS__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL		(0x3CE)
+#define WCD9XXX_A_CDC_MBHC_B1_CTL__POR		(0xC0)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL		(0x3CF)
+#define WCD9XXX_A_CDC_MBHC_B2_CTL__POR		(0x5D)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL		(0x3D0)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL		(0x3D1)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL		(0x3D2)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL		(0x3D3)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL		(0x3D4)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL		(0x3D5)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL		(0x3D6)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B7_CTL__POR	(0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL		(0x3D7)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B8_CTL__POR	(0x07)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL		(0x3D8)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL__POR	(0xFF)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL		(0x3D9)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL__POR	(0x7F)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL		(0x3DA)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL		(0x3DB)
+#define WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL__POR	(0x80)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL		(0x3DC)
+#define WCD9XXX_A_CDC_MBHC_CLK_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL		(0x3DD)
+#define WCD9XXX_A_CDC_MBHC_INT_CTL__POR		(0x00)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL		(0x3DE)
+#define WCD9XXX_A_CDC_MBHC_DEBUG_CTL__POR	(0x00)
+#define WCD9XXX_A_CDC_MBHC_SPARE		(0x3DF)
+#define WCD9XXX_A_CDC_MBHC_SPARE__POR		(0x00)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1		(0x14E)
+#define WCD9XXX_A_MBHC_SCALING_MUX_1__POR	(0x00)
+#define WCD9XXX_A_RX_HPH_OCP_CTL		(0x1AA)
+#define WCD9XXX_A_RX_HPH_OCP_CTL__POR		(0x68)
+#define WCD9XXX_A_MICB_1_CTL			(0x12B)
+#define WCD9XXX_A_MICB_1_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_1_INT_RBIAS		(0x12C)
+#define WCD9XXX_A_MICB_1_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_1_MBHC			(0x12D)
+#define WCD9XXX_A_MICB_1_MBHC__POR		(0x01)
+#define WCD9XXX_A_MICB_CFILT_2_CTL		(0x12E)
+#define WCD9XXX_A_MICB_CFILT_2_CTL__POR		(0x40)
+#define WCD9XXX_A_MICB_CFILT_2_VAL		(0x12F)
+#define WCD9XXX_A_MICB_CFILT_2_VAL__POR		(0x80)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG		(0x130)
+#define WCD9XXX_A_MICB_CFILT_2_PRECHRG__POR	(0x38)
+#define WCD9XXX_A_MICB_2_CTL			(0x131)
+#define WCD9XXX_A_MICB_2_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_2_INT_RBIAS		(0x132)
+#define WCD9XXX_A_MICB_2_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_2_MBHC			(0x133)
+#define WCD9XXX_A_MICB_2_MBHC__POR		(0x02)
+#define WCD9XXX_A_MICB_CFILT_3_CTL		(0x134)
+#define WCD9XXX_A_MICB_CFILT_3_CTL__POR		(0x40)
+#define WCD9XXX_A_MICB_CFILT_3_VAL		(0x135)
+#define WCD9XXX_A_MICB_CFILT_3_VAL__POR		(0x80)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG		(0x136)
+#define WCD9XXX_A_MICB_CFILT_3_PRECHRG__POR	(0x38)
+#define WCD9XXX_A_MICB_3_CTL			(0x137)
+#define WCD9XXX_A_MICB_3_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_3_INT_RBIAS		(0x138)
+#define WCD9XXX_A_MICB_3_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_3_MBHC			(0x139)
+#define WCD9XXX_A_MICB_3_MBHC__POR		(0x00)
+#define WCD9XXX_A_MICB_4_CTL			(0x13D)
+#define WCD9XXX_A_MICB_4_CTL__POR		(0x16)
+#define WCD9XXX_A_MICB_4_INT_RBIAS		(0x13E)
+#define WCD9XXX_A_MICB_4_INT_RBIAS__POR		(0x24)
+#define WCD9XXX_A_MICB_4_MBHC			(0x13F)
+#define WCD9XXX_A_MICB_4_MBHC__POR		(0x01)
+#define WCD9XXX_A_MICB_CFILT_1_VAL		(0x129)
+#define WCD9XXX_A_MICB_CFILT_1_VAL__POR		(0x80)
+#define WCD9XXX_A_RX_HPH_L_STATUS		(0x1B3)
+#define WCD9XXX_A_RX_HPH_L_STATUS__POR		(0x00)
+#define WCD9XXX_A_MBHC_HPH			(0x1FE)
+#define WCD9XXX_A_MBHC_HPH__POR			(0x44)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME		(0x1AD)
+#define WCD9XXX_A_RX_HPH_CNP_WG_TIME__POR	(0x2A)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL		(0x1B7)
+#define WCD9XXX_A_RX_HPH_R_DAC_CTL__POR		(0x00)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL		(0x1B1)
+#define WCD9XXX_A_RX_HPH_L_DAC_CTL__POR		(0x00)
+#define WCD9XXX_A_TX_7_MBHC_EN			(0x171)
+#define WCD9XXX_A_TX_7_MBHC_EN__POR		(0x0C)
+#define WCD9XXX_A_PIN_CTL_OE0			(0x010)
+#define WCD9XXX_A_PIN_CTL_OE0__POR		(0x00)
+#define WCD9XXX_A_PIN_CTL_OE1			(0x011)
+#define WCD9XXX_A_PIN_CTL_OE1__POR		(0x00)
+#define WCD9XXX_A_MICB_CFILT_1_CTL		(0x128)
+#define WCD9XXX_A_LDO_H_MODE_1			(0x110)
+#define WCD9XXX_A_LDO_H_MODE_1__POR		(0x65)
+#define WCD9XXX_A_MICB_CFILT_1_CTL__POR		(0x40)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL		(0x174)
+#define WCD9XXX_A_TX_7_MBHC_TEST_CTL__POR	(0x38)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2		(0x14F)
+#define WCD9XXX_A_MBHC_SCALING_MUX_2__POR	(0x80)
+#define WCD9XXX_A_TX_COM_BIAS			(0x14C)
+#define WCD9XXX_A_TX_COM_BIAS__POR		(0xF0)
+
+#define WCD9XXX_A_MBHC_INSERT_DETECT		(0x14A) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DETECT__POR	(0x00)
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS	(0x14B) /* TAIKO and later */
+#define WCD9XXX_A_MBHC_INSERT_DET_STATUS__POR	(0x00)
+#define WCD9XXX_A_MAD_ANA_CTRL			(0x150)
+#define WCD9XXX_A_MAD_ANA_CTRL__POR		(0xF1)
+
+
+#define WCD9XXX_A_CDC_CLK_OTHR_CTL			(0x30C)
+#define WCD9XXX_A_CDC_CLK_OTHR_CTL__POR				(0x00)
+
+/* Class H related common registers */
+#define WCD9XXX_A_BUCK_MODE_1			(0x181)
+#define WCD9XXX_A_BUCK_MODE_1__POR				(0x21)
+#define WCD9XXX_A_BUCK_MODE_2			(0x182)
+#define WCD9XXX_A_BUCK_MODE_2__POR				(0xFF)
+#define WCD9XXX_A_BUCK_MODE_3			(0x183)
+#define WCD9XXX_A_BUCK_MODE_3__POR				(0xCC)
+#define WCD9XXX_A_BUCK_MODE_4			(0x184)
+#define WCD9XXX_A_BUCK_MODE_4__POR				(0x3A)
+#define WCD9XXX_A_BUCK_MODE_5			(0x185)
+#define WCD9XXX_A_BUCK_MODE_5__POR				(0x00)
+#define WCD9XXX_A_BUCK_CTRL_VCL_1			(0x186)
+#define WCD9XXX_A_BUCK_CTRL_VCL_1__POR				(0x48)
+#define WCD9XXX_A_BUCK_CTRL_VCL_2			(0x187)
+#define WCD9XXX_A_BUCK_CTRL_VCL_2__POR				(0xA3)
+#define WCD9XXX_A_BUCK_CTRL_VCL_3			(0x188)
+#define WCD9XXX_A_BUCK_CTRL_VCL_3__POR				(0x82)
+#define WCD9XXX_A_BUCK_CTRL_CCL_1			(0x189)
+#define WCD9XXX_A_BUCK_CTRL_CCL_1__POR				(0xAB)
+#define WCD9XXX_A_BUCK_CTRL_CCL_2			(0x18A)
+#define WCD9XXX_A_BUCK_CTRL_CCL_2__POR				(0xDC)
+#define WCD9XXX_A_BUCK_CTRL_CCL_3			(0x18B)
+#define WCD9XXX_A_BUCK_CTRL_CCL_3__POR				(0x6A)
+#define WCD9XXX_A_BUCK_CTRL_CCL_4			(0x18C)
+#define WCD9XXX_A_BUCK_CTRL_CCL_4__POR				(0x58)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1			(0x18D)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_1__POR				(0x50)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2			(0x18E)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_2__POR				(0x64)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3			(0x18F)
+#define WCD9XXX_A_BUCK_CTRL_PWM_DRVR_3__POR				(0x77)
+#define WCD9XXX_A_BUCK_TMUX_A_D			(0x190)
+#define WCD9XXX_A_BUCK_TMUX_A_D__POR				(0x00)
+#define WCD9XXX_A_NCP_EN			(0x192)
+#define WCD9XXX_A_NCP_EN__POR				(0xFE)
+#define WCD9XXX_A_NCP_STATIC			(0x194)
+#define WCD9XXX_A_NCP_STATIC__POR				(0x28)
+#define WCD9XXX_A_NCP_BUCKREF			(0x191)
+#define WCD9XXX_A_NCP_BUCKREF__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_B1_CTL			(0x320)
+#define WCD9XXX_A_CDC_CLSH_B1_CTL__POR				(0xE4)
+#define WCD9XXX_A_CDC_CLSH_B2_CTL			(0x321)
+#define WCD9XXX_A_CDC_CLSH_B2_CTL__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_B3_CTL			(0x322)
+#define WCD9XXX_A_CDC_CLSH_B3_CTL__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS			(0x323)
+#define WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS__POR			(0x00)
+#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD			(0x324)
+#define WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD__POR			(0x12)
+#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD			(0x325)
+#define WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD__POR			(0x0C)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD			(0x326)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR		(0x18)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD			(0x327)
+#define WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR		(0x23)
+#define WCD9XXX_A_CDC_CLSH_K_ADDR			(0x328)
+#define WCD9XXX_A_CDC_CLSH_K_ADDR__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_K_DATA			(0x329)
+#define WCD9XXX_A_CDC_CLSH_K_DATA__POR				(0xA4)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L			(0x32A)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L__POR				(0xD7)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U			(0x32B)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U__POR				(0x05)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L			(0x32C)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L__POR				(0x60)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U			(0x32D)
+#define WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U__POR				(0x09)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR			(0x32E)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH			(0x32F)
+#define WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR			(0x330)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR__POR				(0x00)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH			(0x331)
+#define WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH__POR				(0x00)
+
+#define WCD9XXX_A_CDC_RX1_B6_CTL			(0x2B5)
+#define WCD9XXX_A_CDC_RX1_B6_CTL__POR				(0x80)
+#define WCD9XXX_A_CDC_RX2_B6_CTL			(0x2BD)
+#define WCD9XXX_A_CDC_RX2_B6_CTL__POR				(0x80)
+#define WCD9XXX_A_RX_HPH_L_GAIN				(0x1AE)
+#define WCD9XXX_A_RX_HPH_L_GAIN__POR				(0x00)
+#define WCD9XXX_A_RX_HPH_R_GAIN				(0x1B4)
+#define WCD9XXX_A_RX_HPH_R_GAIN__POR				(0x00)
+#define WCD9XXX_A_RX_HPH_CHOP_CTL			(0x1A5)
+#define WCD9XXX_A_RX_HPH_CHOP_CTL__POR				(0xB4)
+#define WCD9XXX_A_RX_HPH_BIAS_PA			(0x1A6)
+#define WCD9XXX_A_RX_HPH_BIAS_PA__POR				(0x7A)
+#define WCD9XXX_A_RX_HPH_L_TEST				(0x1AF)
+#define WCD9XXX_A_RX_HPH_L_TEST__POR				(0x00)
+#define WCD9XXX_A_RX_HPH_R_TEST				(0x1B5)
+#define WCD9XXX_A_RX_HPH_R_TEST__POR				(0x00)
+#define WCD9XXX_A_CDC_CLK_RX_B1_CTL			(0x30F)
+#define WCD9XXX_A_CDC_CLK_RX_B1_CTL__POR			(0x00)
+#define WCD9XXX_A_NCP_CLK				(0x193)
+#define WCD9XXX_A_NCP_CLK__POR					(0x94)
+#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP			(0x1A9)
+#define WCD9XXX_A_RX_HPH_BIAS_WG_OCP__POR			(0x2A)
+#define WCD9XXX_A_RX_HPH_CNP_WG_CTL			(0x1AC)
+#define WCD9XXX_A_RX_HPH_CNP_WG_CTL__POR			(0xDE)
+#define WCD9XXX_A_RX_HPH_L_PA_CTL			(0x1B0)
+#define WCD9XXX_A_RX_HPH_L_PA_CTL__POR				(0x42)
+#define WCD9XXX_A_RX_HPH_R_PA_CTL			(0x1B6)
+#define WCD9XXX_A_RX_HPH_R_PA_CTL__POR				(0x42)
+#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL			(0x383)
+#define WCD9XXX_A_CDC_CONN_RX2_B1_CTL__POR			(0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL			(0x361)
+#define WCD9XXX_A_CDC_PA_RAMP_B1_CTL__POR			(0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL			(0x362)
+#define WCD9XXX_A_CDC_PA_RAMP_B2_CTL__POR			(0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL			(0x363)
+#define WCD9XXX_A_CDC_PA_RAMP_B3_CTL__POR			(0x00)
+#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL			(0x364)
+#define WCD9XXX_A_CDC_PA_RAMP_B4_CTL__POR			(0x00)
+
+#define WCD9330_A_LEAKAGE_CTL				(0x03C)
+#define WCD9330_A_LEAKAGE_CTL__POR				(0x04)
+#define WCD9330_A_CDC_CTL				(0x034)
+#define WCD9330_A_CDC_CTL__POR					(0x00)
+
+/* Class-H registers for codecs from and above WCD9335 */
+#define WCD9XXX_A_CDC_RX0_RX_PATH_CFG0			(0xB42)
+#define WCD9XXX_A_CDC_RX1_RX_PATH_CFG0			(0xB56)
+#define WCD9XXX_A_CDC_RX2_RX_PATH_CFG0			(0xB6A)
+#define WCD9XXX_A_CDC_CLSH_K1_MSB			(0xC08)
+#define WCD9XXX_A_CDC_CLSH_K1_LSB			(0xC09)
+#define WCD9XXX_A_ANA_RX_SUPPLIES			(0x608)
+#define WCD9XXX_A_ANA_HPH				(0x609)
+#define WCD9XXX_A_CDC_CLSH_CRC				(0xC01)
+#define WCD9XXX_FLYBACK_EN				(0x6A4)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_1			(0x6A5)
+#define WCD9XXX_FLYBACK_VNEGDAC_CTRL_2			(0x6AF)
+#define WCD9XXX_RX_BIAS_FLYB_BUFF			(0x6C7)
+#define WCD9XXX_HPH_L_EN				(0x6D3)
+#define WCD9XXX_HPH_R_EN				(0x6D6)
+#define WCD9XXX_HPH_REFBUFF_UHQA_CTL			(0x6DD)
+#define WCD9XXX_CLASSH_CTRL_VCL_2                       (0x69B)
+#define WCD9XXX_CDC_CLSH_HPH_V_PA			(0xC04)
+#define WCD9XXX_CDC_RX0_RX_PATH_SEC0			(0xB49)
+#define WCD9XXX_CDC_RX1_RX_PATH_CTL			(0xB55)
+#define WCD9XXX_CDC_RX2_RX_PATH_CTL			(0xB69)
+#define WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL		(0xD41)
+#define WCD9XXX_CLASSH_CTRL_CCL_1                       (0x69C)
+
+/* RX Gain control registers of codecs from and above WCD9335 */
+#define WCD9XXX_CDC_RX1_RX_VOL_CTL			(0xB59)
+#define WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL			(0xB5C)
+#define WCD9XXX_CDC_RX1_RX_PATH_SEC1			(0xB5E)
+#define WCD9XXX_CDC_RX2_RX_VOL_CTL			(0xB6D)
+#define WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL			(0xB70)
+#define WCD9XXX_CDC_RX2_RX_PATH_SEC1			(0xB72)
+
+/* Class-H registers for codecs from and above WCD934X */
+#define WCD9XXX_HPH_CNP_WG_CTL                          (0x06cc)
+#define WCD9XXX_FLYBACK_VNEG_CTRL_4			(0x06a8)
+#define WCD9XXX_HPH_NEW_INT_PA_MISC2			(0x0738)
+#define WCD9XXX_RX_BIAS_HPH_LOWPOWER			(0x06bf)
+#define WCD9XXX_HPH_PA_CTL1				(0x06d1)
+#endif
diff --git a/include/uapi/linux/msm_audio.h b/include/uapi/linux/msm_audio.h
new file mode 100644
index 0000000..bde27d1
--- /dev/null
+++ b/include/uapi/linux/msm_audio.h
@@ -0,0 +1,464 @@
+/* include/linux/msm_audio.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (c) 2012, 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef _UAPI_LINUX_MSM_AUDIO_H
+#define _UAPI_LINUX_MSM_AUDIO_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* PCM Audio */
+
+#define AUDIO_IOCTL_MAGIC 'a'
+
+#define AUDIO_START        _IOW(AUDIO_IOCTL_MAGIC, 0, unsigned int)
+#define AUDIO_STOP         _IOW(AUDIO_IOCTL_MAGIC, 1, unsigned int)
+#define AUDIO_FLUSH        _IOW(AUDIO_IOCTL_MAGIC, 2, unsigned int)
+#define AUDIO_GET_CONFIG   _IOR(AUDIO_IOCTL_MAGIC, 3, \
+		struct msm_audio_config)
+#define AUDIO_SET_CONFIG   _IOW(AUDIO_IOCTL_MAGIC, 4, \
+		struct msm_audio_config)
+#define AUDIO_GET_STATS    _IOR(AUDIO_IOCTL_MAGIC, 5, \
+		struct msm_audio_stats)
+#define AUDIO_ENABLE_AUDPP _IOW(AUDIO_IOCTL_MAGIC, 6, unsigned int)
+#define AUDIO_SET_ADRC     _IOW(AUDIO_IOCTL_MAGIC, 7, unsigned int)
+#define AUDIO_SET_EQ       _IOW(AUDIO_IOCTL_MAGIC, 8, unsigned int)
+#define AUDIO_SET_RX_IIR   _IOW(AUDIO_IOCTL_MAGIC, 9, unsigned int)
+#define AUDIO_SET_VOLUME   _IOW(AUDIO_IOCTL_MAGIC, 10, unsigned int)
+#define AUDIO_PAUSE        _IOW(AUDIO_IOCTL_MAGIC, 11, unsigned int)
+#define AUDIO_PLAY_DTMF    _IOW(AUDIO_IOCTL_MAGIC, 12, unsigned int)
+#define AUDIO_GET_EVENT    _IOR(AUDIO_IOCTL_MAGIC, 13, \
+		struct msm_audio_event)
+#define AUDIO_ABORT_GET_EVENT _IOW(AUDIO_IOCTL_MAGIC, 14, unsigned int)
+#define AUDIO_REGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 15, unsigned int)
+#define AUDIO_DEREGISTER_PMEM _IOW(AUDIO_IOCTL_MAGIC, 16, unsigned int)
+#define AUDIO_ASYNC_WRITE _IOW(AUDIO_IOCTL_MAGIC, 17, \
+		struct msm_audio_aio_buf)
+#define AUDIO_ASYNC_READ _IOW(AUDIO_IOCTL_MAGIC, 18, \
+		struct msm_audio_aio_buf)
+#define AUDIO_SET_INCALL _IOW(AUDIO_IOCTL_MAGIC, 19, struct msm_voicerec_mode)
+#define AUDIO_GET_NUM_SND_DEVICE _IOR(AUDIO_IOCTL_MAGIC, 20, unsigned int)
+#define AUDIO_GET_SND_DEVICES _IOWR(AUDIO_IOCTL_MAGIC, 21, \
+				struct msm_snd_device_list)
+#define AUDIO_ENABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 22, unsigned int)
+#define AUDIO_DISABLE_SND_DEVICE _IOW(AUDIO_IOCTL_MAGIC, 23, unsigned int)
+#define AUDIO_ROUTE_STREAM _IOW(AUDIO_IOCTL_MAGIC, 24, \
+				struct msm_audio_route_config)
+#define AUDIO_GET_PCM_CONFIG _IOR(AUDIO_IOCTL_MAGIC, 30, unsigned int)
+#define AUDIO_SET_PCM_CONFIG _IOW(AUDIO_IOCTL_MAGIC, 31, unsigned int)
+#define AUDIO_SWITCH_DEVICE  _IOW(AUDIO_IOCTL_MAGIC, 32, unsigned int)
+#define AUDIO_SET_MUTE       _IOW(AUDIO_IOCTL_MAGIC, 33, unsigned int)
+#define AUDIO_UPDATE_ACDB    _IOW(AUDIO_IOCTL_MAGIC, 34, unsigned int)
+#define AUDIO_START_VOICE    _IOW(AUDIO_IOCTL_MAGIC, 35, unsigned int)
+#define AUDIO_STOP_VOICE     _IOW(AUDIO_IOCTL_MAGIC, 36, unsigned int)
+#define AUDIO_REINIT_ACDB    _IOW(AUDIO_IOCTL_MAGIC, 39, unsigned int)
+#define AUDIO_OUTPORT_FLUSH  _IOW(AUDIO_IOCTL_MAGIC, 40, unsigned short)
+#define AUDIO_SET_ERR_THRESHOLD_VALUE _IOW(AUDIO_IOCTL_MAGIC, 41, \
+					unsigned short)
+#define AUDIO_GET_BITSTREAM_ERROR_INFO _IOR(AUDIO_IOCTL_MAGIC, 42, \
+			       struct msm_audio_bitstream_error_info)
+
+#define AUDIO_SET_SRS_TRUMEDIA_PARAM _IOW(AUDIO_IOCTL_MAGIC, 43, unsigned int)
+
+/* Qualcomm technologies inc extensions */
+#define AUDIO_SET_STREAM_CONFIG   _IOW(AUDIO_IOCTL_MAGIC, 80, \
+				struct msm_audio_stream_config)
+#define AUDIO_GET_STREAM_CONFIG   _IOR(AUDIO_IOCTL_MAGIC, 81, \
+				struct msm_audio_stream_config)
+#define AUDIO_GET_SESSION_ID _IOR(AUDIO_IOCTL_MAGIC, 82, unsigned short)
+#define AUDIO_GET_STREAM_INFO   _IOR(AUDIO_IOCTL_MAGIC, 83, \
+			       struct msm_audio_bitstream_info)
+#define AUDIO_SET_PAN       _IOW(AUDIO_IOCTL_MAGIC, 84, unsigned int)
+#define AUDIO_SET_QCONCERT_PLUS       _IOW(AUDIO_IOCTL_MAGIC, 85, unsigned int)
+#define AUDIO_SET_MBADRC       _IOW(AUDIO_IOCTL_MAGIC, 86, unsigned int)
+#define AUDIO_SET_VOLUME_PATH   _IOW(AUDIO_IOCTL_MAGIC, 87, \
+				     struct msm_vol_info)
+#define AUDIO_SET_MAX_VOL_ALL _IOW(AUDIO_IOCTL_MAGIC, 88, unsigned int)
+#define AUDIO_ENABLE_AUDPRE  _IOW(AUDIO_IOCTL_MAGIC, 89, unsigned int)
+#define AUDIO_SET_AGC        _IOW(AUDIO_IOCTL_MAGIC, 90, unsigned int)
+#define AUDIO_SET_NS         _IOW(AUDIO_IOCTL_MAGIC, 91, unsigned int)
+#define AUDIO_SET_TX_IIR     _IOW(AUDIO_IOCTL_MAGIC, 92, unsigned int)
+#define AUDIO_GET_BUF_CFG    _IOW(AUDIO_IOCTL_MAGIC, 93, \
+					struct msm_audio_buf_cfg)
+#define AUDIO_SET_BUF_CFG    _IOW(AUDIO_IOCTL_MAGIC, 94, \
+					struct msm_audio_buf_cfg)
+#define AUDIO_SET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 95,  \
+					struct msm_acdb_cmd_device)
+#define AUDIO_GET_ACDB_BLK _IOW(AUDIO_IOCTL_MAGIC, 96,  \
+					struct msm_acdb_cmd_device)
+
+#define AUDIO_REGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 97, \
+		struct msm_audio_ion_info)
+#define AUDIO_DEREGISTER_ION _IOW(AUDIO_IOCTL_MAGIC, 98, \
+		struct msm_audio_ion_info)
+#define AUDIO_SET_EFFECTS_CONFIG   _IOW(AUDIO_IOCTL_MAGIC, 99, \
+				struct msm_hwacc_effects_config)
+#define AUDIO_EFFECTS_SET_BUF_LEN _IOW(AUDIO_IOCTL_MAGIC, 100, \
+				struct msm_hwacc_buf_cfg)
+#define AUDIO_EFFECTS_GET_BUF_AVAIL _IOW(AUDIO_IOCTL_MAGIC, 101, \
+				struct msm_hwacc_buf_avail)
+#define AUDIO_EFFECTS_WRITE _IOW(AUDIO_IOCTL_MAGIC, 102, void *)
+#define AUDIO_EFFECTS_READ _IOWR(AUDIO_IOCTL_MAGIC, 103, void *)
+#define AUDIO_EFFECTS_SET_PP_PARAMS _IOW(AUDIO_IOCTL_MAGIC, 104, void *)
+
+#define AUDIO_PM_AWAKE      _IOW(AUDIO_IOCTL_MAGIC, 105, unsigned int)
+#define AUDIO_PM_RELAX      _IOW(AUDIO_IOCTL_MAGIC, 106, unsigned int)
+
+#define	AUDIO_MAX_COMMON_IOCTL_NUM	107
+
+
+#define HANDSET_MIC			0x01
+#define HANDSET_SPKR			0x02
+#define HEADSET_MIC			0x03
+#define HEADSET_SPKR_MONO		0x04
+#define HEADSET_SPKR_STEREO		0x05
+#define SPKR_PHONE_MIC			0x06
+#define SPKR_PHONE_MONO			0x07
+#define SPKR_PHONE_STEREO		0x08
+#define BT_SCO_MIC			0x09
+#define BT_SCO_SPKR			0x0A
+#define BT_A2DP_SPKR			0x0B
+#define TTY_HEADSET_MIC			0x0C
+#define TTY_HEADSET_SPKR		0x0D
+
+/* Default devices are not supported in a */
+/* device switching context. Only supported */
+/* for stream devices. */
+/* DO NOT USE */
+#define DEFAULT_TX			0x0E
+#define DEFAULT_RX			0x0F
+
+#define BT_A2DP_TX			0x10
+
+#define HEADSET_MONO_PLUS_SPKR_MONO_RX         0x11
+#define HEADSET_MONO_PLUS_SPKR_STEREO_RX       0x12
+#define HEADSET_STEREO_PLUS_SPKR_MONO_RX       0x13
+#define HEADSET_STEREO_PLUS_SPKR_STEREO_RX     0x14
+
+#define I2S_RX				0x20
+#define I2S_TX				0x21
+
+#define ADRC_ENABLE		0x0001
+#define EQUALIZER_ENABLE	0x0002
+#define IIR_ENABLE		0x0004
+#define QCONCERT_PLUS_ENABLE	0x0008
+#define MBADRC_ENABLE		0x0010
+#define SRS_ENABLE		0x0020
+#define SRS_DISABLE	0x0040
+
+#define AGC_ENABLE		0x0001
+#define NS_ENABLE		0x0002
+#define TX_IIR_ENABLE		0x0004
+#define FLUENCE_ENABLE		0x0008
+
+#define VOC_REC_UPLINK		0x00
+#define VOC_REC_DOWNLINK	0x01
+#define VOC_REC_BOTH		0x02
+
+struct msm_audio_config {
+	uint32_t buffer_size;
+	uint32_t buffer_count;
+	uint32_t channel_count;
+	uint32_t sample_rate;
+	uint32_t type;
+	uint32_t meta_field;
+	uint32_t bits;
+	uint32_t unused[3];
+};
+
+struct msm_audio_stream_config {
+	uint32_t buffer_size;
+	uint32_t buffer_count;
+};
+
+struct msm_audio_buf_cfg {
+	uint32_t meta_info_enable;
+	uint32_t frames_per_buf;
+};
+
+struct msm_audio_stats {
+	uint32_t byte_count;
+	uint32_t sample_count;
+	uint32_t unused[2];
+};
+
+struct msm_audio_ion_info {
+	int fd;
+	void *vaddr;
+};
+
+struct msm_audio_pmem_info {
+	int fd;
+	void *vaddr;
+};
+
+struct msm_audio_aio_buf {
+	void *buf_addr;
+	uint32_t buf_len;
+	uint32_t data_len;
+	void *private_data;
+	unsigned short mfield_sz; /*only useful for data has meta field */
+};
+
+/* Audio routing */
+
+#define SND_IOCTL_MAGIC 's'
+
+#define SND_MUTE_UNMUTED 0
+#define SND_MUTE_MUTED   1
+
+struct msm_mute_info {
+	uint32_t mute;
+	uint32_t path;
+};
+
+struct msm_vol_info {
+	uint32_t vol;
+	uint32_t path;
+};
+
+struct msm_voicerec_mode {
+	uint32_t rec_mode;
+};
+
+struct msm_snd_device_config {
+	uint32_t device;
+	uint32_t ear_mute;
+	uint32_t mic_mute;
+};
+
+#define SND_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_device_config *)
+
+enum cad_device_path_type {
+	CAD_DEVICE_PATH_RX,	/*For Decoding session*/
+	CAD_DEVICE_PATH_TX,	/* For Encoding session*/
+	CAD_DEVICE_PATH_RX_TX, /* For Voice call */
+	CAD_DEVICE_PATH_LB,	/* For loopback (FM Analog)*/
+	CAD_DEVICE_PATH_MAX
+};
+
+struct cad_devices_type {
+	uint32_t rx_device;
+	uint32_t tx_device;
+	enum cad_device_path_type pathtype;
+};
+
+struct msm_cad_device_config {
+	struct cad_devices_type device;
+	uint32_t ear_mute;
+	uint32_t mic_mute;
+};
+
+#define CAD_SET_DEVICE _IOW(SND_IOCTL_MAGIC, 2, struct msm_cad_device_config *)
+
+#define SND_METHOD_VOICE 0
+#define SND_METHOD_MIDI 4
+
+struct msm_snd_volume_config {
+	uint32_t device;
+	uint32_t method;
+	uint32_t volume;
+};
+
+#define SND_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_snd_volume_config *)
+
+struct msm_cad_volume_config {
+	struct cad_devices_type device;
+	uint32_t method;
+	uint32_t volume;
+};
+
+#define CAD_SET_VOLUME _IOW(SND_IOCTL_MAGIC, 3, struct msm_cad_volume_config *)
+
+/* Returns the number of SND endpoints supported. */
+
+#define SND_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned int *)
+
+struct msm_snd_endpoint {
+	int id; /* input and output */
+	char name[64]; /* output only */
+};
+
+/* Takes an index between 0 and one less than the number returned by
+ * SND_GET_NUM_ENDPOINTS, and returns the SND index and name of a
+ * SND endpoint.  On input, the .id field contains the number of the
+ * endpoint, and on exit it contains the SND index, while .name contains
+ * the description of the endpoint.
+ */
+
+#define SND_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_snd_endpoint *)
+
+
+#define SND_AVC_CTL _IOW(SND_IOCTL_MAGIC, 6, unsigned int *)
+#define SND_AGC_CTL _IOW(SND_IOCTL_MAGIC, 7, unsigned int *)
+
+/*return the number of CAD endpoints supported. */
+
+#define CAD_GET_NUM_ENDPOINTS _IOR(SND_IOCTL_MAGIC, 4, unsigned int *)
+
+struct msm_cad_endpoint {
+	int id; /* input and output */
+	char name[64]; /* output only */
+};
+
+/* Takes an index between 0 and one less than the number returned by
+ * SND_GET_NUM_ENDPOINTS, and returns the CAD index and name of a
+ * CAD endpoint.  On input, the .id field contains the number of the
+ * endpoint, and on exit it contains the SND index, while .name contains
+ * the description of the endpoint.
+ */
+
+#define CAD_GET_ENDPOINT _IOWR(SND_IOCTL_MAGIC, 5, struct msm_cad_endpoint *)
+
+struct msm_audio_pcm_config {
+	uint32_t pcm_feedback;	/* 0 - disable > 0 - enable */
+	uint32_t buffer_count;	/* Number of buffers to allocate */
+	uint32_t buffer_size;	/* Size of buffer for capturing of
+				 * PCM samples
+				 */
+};
+
+#define AUDIO_EVENT_SUSPEND 0
+#define AUDIO_EVENT_RESUME 1
+#define AUDIO_EVENT_WRITE_DONE 2
+#define AUDIO_EVENT_READ_DONE   3
+#define AUDIO_EVENT_STREAM_INFO 4
+#define AUDIO_EVENT_BITSTREAM_ERROR_INFO 5
+
+#define AUDIO_CODEC_TYPE_MP3 0
+#define AUDIO_CODEC_TYPE_AAC 1
+
+struct msm_audio_bitstream_info {
+	uint32_t codec_type;
+	uint32_t chan_info;
+	uint32_t sample_rate;
+	uint32_t bit_stream_info;
+	uint32_t bit_rate;
+	uint32_t unused[3];
+};
+
+struct msm_audio_bitstream_error_info {
+	uint32_t dec_id;
+	uint32_t err_msg_indicator;
+	uint32_t err_type;
+};
+
+union msm_audio_event_payload {
+	struct msm_audio_aio_buf aio_buf;
+	struct msm_audio_bitstream_info stream_info;
+	struct msm_audio_bitstream_error_info error_info;
+	int reserved;
+};
+
+struct msm_audio_event {
+	int event_type;
+	int timeout_ms;
+	union msm_audio_event_payload event_payload;
+};
+
+#define MSM_SNDDEV_CAP_RX 0x1
+#define MSM_SNDDEV_CAP_TX 0x2
+#define MSM_SNDDEV_CAP_VOICE 0x4
+
+struct msm_snd_device_info {
+	uint32_t dev_id;
+	uint32_t dev_cap; /* bitmask describe capability of device */
+	char dev_name[64];
+};
+
+struct msm_snd_device_list {
+	uint32_t  num_dev; /* Indicate number of device info to be retrieved */
+	struct msm_snd_device_info *list;
+};
+
+struct msm_dtmf_config {
+	uint16_t path;
+	uint16_t dtmf_hi;
+	uint16_t dtmf_low;
+	uint16_t duration;
+	uint16_t tx_gain;
+	uint16_t rx_gain;
+	uint16_t mixing;
+};
+
+#define AUDIO_ROUTE_STREAM_VOICE_RX 0
+#define AUDIO_ROUTE_STREAM_VOICE_TX 1
+#define AUDIO_ROUTE_STREAM_PLAYBACK 2
+#define AUDIO_ROUTE_STREAM_REC      3
+
+struct msm_audio_route_config {
+	uint32_t stream_type;
+	uint32_t stream_id;
+	uint32_t dev_id;
+};
+
+#define AUDIO_MAX_EQ_BANDS 12
+
+struct msm_audio_eq_band {
+	uint16_t     band_idx; /* The band index, 0 .. 11 */
+	uint32_t     filter_type; /* Filter band type */
+	uint32_t     center_freq_hz; /* Filter band center frequency */
+	uint32_t     filter_gain; /* Filter band initial gain (dB) */
+			/* Range is +12 dB to -12 dB with 1dB increments. */
+	uint32_t     q_factor;
+} __attribute__ ((packed));
+
+struct msm_audio_eq_stream_config {
+	uint32_t	enable; /* Number of consequtive bands specified */
+	uint32_t	num_bands;
+	struct msm_audio_eq_band	eq_bands[AUDIO_MAX_EQ_BANDS];
+} __attribute__ ((packed));
+
+struct msm_acdb_cmd_device {
+	uint32_t     command_id;
+	uint32_t     device_id;
+	uint32_t     network_id;
+	uint32_t     sample_rate_id;      /* Actual sample rate value */
+	uint32_t     interface_id;        /* See interface id's above */
+	uint32_t     algorithm_block_id;  /* See enumerations above */
+	uint32_t     total_bytes;         /* Length in bytes used by buffer */
+	uint32_t     *phys_buf;           /* Physical Address of data */
+};
+
+struct msm_hwacc_data_config {
+	__u32 buf_size;
+	__u32 num_buf;
+	__u32 num_channels;
+	__u8 channel_map[8];
+	__u32 sample_rate;
+	__u32 bits_per_sample;
+};
+
+struct msm_hwacc_buf_cfg {
+	__u32 input_len;
+	__u32 output_len;
+};
+
+struct msm_hwacc_buf_avail {
+	__u32 input_num_avail;
+	__u32 output_num_avail;
+};
+
+struct msm_hwacc_effects_config {
+	struct msm_hwacc_data_config input;
+	struct msm_hwacc_data_config output;
+	struct msm_hwacc_buf_cfg buf_cfg;
+	__u32 meta_mode_enabled;
+	__u32 overwrite_topology;
+	__s32 topology;
+};
+
+#endif
diff --git a/include/uapi/linux/msm_audio_aac.h b/include/uapi/linux/msm_audio_aac.h
new file mode 100644
index 0000000..7e1e1b7
--- /dev/null
+++ b/include/uapi/linux/msm_audio_aac.h
@@ -0,0 +1,76 @@
+#ifndef _UAPI_MSM_AUDIO_AAC_H
+#define _UAPI_MSM_AUDIO_AAC_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_SET_AAC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_aac_config)
+#define AUDIO_GET_AAC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_aac_config)
+
+#define AUDIO_SET_AAC_ENC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_aac_enc_config)
+
+#define AUDIO_GET_AAC_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+4), struct msm_audio_aac_enc_config)
+
+#define AUDIO_SET_AAC_MIX_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+5), uint32_t)
+
+#define AUDIO_AAC_FORMAT_ADTS		-1
+#define	AUDIO_AAC_FORMAT_RAW		0x0000
+#define	AUDIO_AAC_FORMAT_PSUEDO_RAW	0x0001
+#define AUDIO_AAC_FORMAT_LOAS		0x0002
+#define AUDIO_AAC_FORMAT_ADIF		0x0003
+
+#define AUDIO_AAC_OBJECT_LC		0x0002
+#define AUDIO_AAC_OBJECT_LTP		0x0004
+#define AUDIO_AAC_OBJECT_ERLC		0x0011
+#define AUDIO_AAC_OBJECT_BSAC		0x0016
+
+#define AUDIO_AAC_SEC_DATA_RES_ON       0x0001
+#define AUDIO_AAC_SEC_DATA_RES_OFF      0x0000
+
+#define AUDIO_AAC_SCA_DATA_RES_ON       0x0001
+#define AUDIO_AAC_SCA_DATA_RES_OFF      0x0000
+
+#define AUDIO_AAC_SPEC_DATA_RES_ON      0x0001
+#define AUDIO_AAC_SPEC_DATA_RES_OFF     0x0000
+
+#define AUDIO_AAC_SBR_ON_FLAG_ON	0x0001
+#define AUDIO_AAC_SBR_ON_FLAG_OFF	0x0000
+
+#define AUDIO_AAC_SBR_PS_ON_FLAG_ON	0x0001
+#define AUDIO_AAC_SBR_PS_ON_FLAG_OFF	0x0000
+
+/* Primary channel on both left and right channels */
+#define AUDIO_AAC_DUAL_MONO_PL_PR  0
+/* Secondary channel on both left and right channels */
+#define AUDIO_AAC_DUAL_MONO_SL_SR  1
+/* Primary channel on right channel and 2nd on left channel */
+#define AUDIO_AAC_DUAL_MONO_SL_PR  2
+/* 2nd channel on right channel and primary on left channel */
+#define AUDIO_AAC_DUAL_MONO_PL_SR  3
+
+struct msm_audio_aac_config {
+	signed short format;
+	unsigned short audio_object;
+	unsigned short ep_config;	/* 0 ~ 3 useful only obj = ERLC */
+	unsigned short aac_section_data_resilience_flag;
+	unsigned short aac_scalefactor_data_resilience_flag;
+	unsigned short aac_spectral_data_resilience_flag;
+	unsigned short sbr_on_flag;
+	unsigned short sbr_ps_on_flag;
+	unsigned short dual_mono_mode;
+	unsigned short channel_configuration;
+	unsigned short sample_rate;
+};
+
+struct msm_audio_aac_enc_config {
+	uint32_t channels;
+	uint32_t sample_rate;
+	uint32_t bit_rate;
+	uint32_t stream_format;
+};
+
+#endif /* _UAPI_MSM_AUDIO_AAC_H */
diff --git a/include/uapi/linux/msm_audio_ac3.h b/include/uapi/linux/msm_audio_ac3.h
new file mode 100644
index 0000000..1df6e69
--- /dev/null
+++ b/include/uapi/linux/msm_audio_ac3.h
@@ -0,0 +1,41 @@
+#ifndef _UAPI_MSM_AUDIO_AC3_H
+#define _UAPI_MSM_AUDIO_AC3_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_SET_AC3_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int)
+#define AUDIO_GET_AC3_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int)
+
+#define AUDAC3_DEF_WORDSIZE 0
+#define AUDAC3_DEF_USER_DOWNMIX_FLAG 0x0
+#define AUDAC3_DEF_USER_KARAOKE_FLAG 0x0
+#define AUDAC3_DEF_ERROR_CONCEALMENT 0
+#define AUDAC3_DEF_MAX_REPEAT_COUNT  0
+
+struct msm_audio_ac3_config {
+	unsigned short		numChans;
+	unsigned short		wordSize;
+	unsigned short		kCapableMode;
+	unsigned short		compMode;
+	unsigned short		outLfeOn;
+	unsigned short		outputMode;
+	unsigned short		stereoMode;
+	unsigned short		dualMonoMode;
+	unsigned short		fsCod;
+	unsigned short		pcmScaleFac;
+	unsigned short		dynRngScaleHi;
+	unsigned short		dynRngScaleLow;
+	unsigned short		user_downmix_flag;
+	unsigned short		user_karaoke_flag;
+	unsigned short		dm_address_high;
+	unsigned short		dm_address_low;
+	unsigned short		ko_address_high;
+	unsigned short		ko_address_low;
+	unsigned short		error_concealment;
+	unsigned short		max_rep_count;
+	unsigned short		channel_routing_mode[6];
+};
+
+#endif /* _UAPI_MSM_AUDIO_AC3_H */
diff --git a/include/uapi/linux/msm_audio_alac.h b/include/uapi/linux/msm_audio_alac.h
new file mode 100644
index 0000000..5476e96
--- /dev/null
+++ b/include/uapi/linux/msm_audio_alac.h
@@ -0,0 +1,24 @@
+#ifndef _UAPI_MSM_AUDIO_ALAC_H
+#define _UAPI_MSM_AUDIO_ALAC_H
+
+#define AUDIO_GET_ALAC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_alac_config)
+#define AUDIO_SET_ALAC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_alac_config)
+
+struct msm_audio_alac_config {
+	uint32_t frameLength;
+	uint8_t compatVersion;
+	uint8_t bitDepth;
+	uint8_t pb; /* currently unused */
+	uint8_t mb; /* currently unused */
+	uint8_t kb; /* currently unused */
+	uint8_t channelCount;
+	uint16_t maxRun; /* currently unused */
+	uint32_t maxSize;
+	uint32_t averageBitRate;
+	uint32_t sampleRate;
+	uint32_t channelLayout;
+};
+
+#endif /* _UAPI_MSM_AUDIO_ALAC_H */
diff --git a/include/uapi/linux/msm_audio_amrnb.h b/include/uapi/linux/msm_audio_amrnb.h
new file mode 100644
index 0000000..619f928
--- /dev/null
+++ b/include/uapi/linux/msm_audio_amrnb.h
@@ -0,0 +1,34 @@
+#ifndef _UAPI_MSM_AUDIO_AMRNB_H
+#define _UAPI_MSM_AUDIO_AMRNB_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_GET_AMRNB_ENC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int)
+#define AUDIO_SET_AMRNB_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int)
+#define AUDIO_GET_AMRNB_ENC_CONFIG_V2  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+2), \
+	struct msm_audio_amrnb_enc_config_v2)
+#define AUDIO_SET_AMRNB_ENC_CONFIG_V2  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+3), \
+	struct msm_audio_amrnb_enc_config_v2)
+
+struct msm_audio_amrnb_enc_config {
+	unsigned short voicememoencweight1;
+	unsigned short voicememoencweight2;
+	unsigned short voicememoencweight3;
+	unsigned short voicememoencweight4;
+	unsigned short dtx_mode_enable; /* 0xFFFF - enable, 0- disable */
+	unsigned short test_mode_enable; /* 0xFFFF - enable, 0- disable */
+	unsigned short enc_mode; /* 0-MR475,1-MR515,2-MR59,3-MR67,4-MR74
+				  * 5-MR795, 6- MR102, 7- MR122(default)
+				  */
+};
+
+struct msm_audio_amrnb_enc_config_v2 {
+	uint32_t band_mode;
+	uint32_t dtx_enable;
+	uint32_t frame_format;
+};
+#endif /* _UAPI_MSM_AUDIO_AMRNB_H */
diff --git a/include/uapi/linux/msm_audio_amrwb.h b/include/uapi/linux/msm_audio_amrwb.h
new file mode 100644
index 0000000..5124038
--- /dev/null
+++ b/include/uapi/linux/msm_audio_amrwb.h
@@ -0,0 +1,18 @@
+#ifndef _UAPI_MSM_AUDIO_AMRWB_H
+#define _UAPI_MSM_AUDIO_AMRWB_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_GET_AMRWB_ENC_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), \
+	struct msm_audio_amrwb_enc_config)
+#define AUDIO_SET_AMRWB_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), \
+	struct msm_audio_amrwb_enc_config)
+
+struct msm_audio_amrwb_enc_config {
+	uint32_t band_mode;
+	uint32_t dtx_enable;
+	uint32_t frame_format;
+};
+#endif /* _UAPI_MSM_AUDIO_AMRWB_H */
diff --git a/include/uapi/linux/msm_audio_amrwbplus.h b/include/uapi/linux/msm_audio_amrwbplus.h
new file mode 100644
index 0000000..ba2d06e
--- /dev/null
+++ b/include/uapi/linux/msm_audio_amrwbplus.h
@@ -0,0 +1,18 @@
+#ifndef _UAPI_MSM_AUDIO_AMR_WB_PLUS_H
+#define _UAPI_MSM_AUDIO_AMR_WB_PLUS_H
+
+#define AUDIO_GET_AMRWBPLUS_CONFIG_V2  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_amrwbplus_config_v2)
+#define AUDIO_SET_AMRWBPLUS_CONFIG_V2  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_amrwbplus_config_v2)
+
+struct msm_audio_amrwbplus_config_v2 {
+	unsigned int size_bytes;
+	unsigned int version;
+	unsigned int num_channels;
+	unsigned int amr_band_mode;
+	unsigned int amr_dtx_mode;
+	unsigned int amr_frame_fmt;
+	unsigned int amr_lsf_idx;
+};
+#endif /* _UAPI_MSM_AUDIO_AMR_WB_PLUS_H */
diff --git a/include/uapi/linux/msm_audio_ape.h b/include/uapi/linux/msm_audio_ape.h
new file mode 100644
index 0000000..587d3bc
--- /dev/null
+++ b/include/uapi/linux/msm_audio_ape.h
@@ -0,0 +1,26 @@
+/* The following structure has been taken
+ * from Monkey's Audio SDK with permission
+ */
+
+#ifndef _UAPI_MSM_AUDIO_APE_H
+#define _UAPI_MSM_AUDIO_APE_H
+
+#define AUDIO_GET_APE_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_ape_config)
+#define AUDIO_SET_APE_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_ape_config)
+
+struct msm_audio_ape_config {
+	uint16_t compatibleVersion;
+	uint16_t compressionLevel;
+	uint32_t formatFlags;
+	uint32_t blocksPerFrame;
+	uint32_t finalFrameBlocks;
+	uint32_t totalFrames;
+	uint16_t bitsPerSample;
+	uint16_t numChannels;
+	uint32_t sampleRate;
+	uint32_t seekTablePresent;
+};
+
+#endif /* _UAPI_MSM_AUDIO_APE_H */
diff --git a/include/uapi/linux/msm_audio_calibration.h b/include/uapi/linux/msm_audio_calibration.h
new file mode 100644
index 0000000..11af32e
--- /dev/null
+++ b/include/uapi/linux/msm_audio_calibration.h
@@ -0,0 +1,692 @@
+#ifndef _UAPI_MSM_AUDIO_CALIBRATION_H
+#define _UAPI_MSM_AUDIO_CALIBRATION_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define CAL_IOCTL_MAGIC 'a'
+
+#define AUDIO_ALLOCATE_CALIBRATION	_IOWR(CAL_IOCTL_MAGIC, \
+							200, void *)
+#define AUDIO_DEALLOCATE_CALIBRATION	_IOWR(CAL_IOCTL_MAGIC, \
+							201, void *)
+#define AUDIO_PREPARE_CALIBRATION	_IOWR(CAL_IOCTL_MAGIC, \
+							202, void *)
+#define AUDIO_SET_CALIBRATION		_IOWR(CAL_IOCTL_MAGIC, \
+							203, void *)
+#define AUDIO_GET_CALIBRATION		_IOWR(CAL_IOCTL_MAGIC, \
+							204, void *)
+#define AUDIO_POST_CALIBRATION		_IOWR(CAL_IOCTL_MAGIC, \
+							205, void *)
+
+/* For Real-Time Audio Calibration */
+#define AUDIO_GET_RTAC_ADM_INFO		_IOR(CAL_IOCTL_MAGIC, \
+							207, void *)
+#define AUDIO_GET_RTAC_VOICE_INFO	_IOR(CAL_IOCTL_MAGIC, \
+							208, void *)
+#define AUDIO_GET_RTAC_ADM_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							209, void *)
+#define AUDIO_SET_RTAC_ADM_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							210, void *)
+#define AUDIO_GET_RTAC_ASM_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							211, void *)
+#define AUDIO_SET_RTAC_ASM_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							212, void *)
+#define AUDIO_GET_RTAC_CVS_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							213, void *)
+#define AUDIO_SET_RTAC_CVS_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							214, void *)
+#define AUDIO_GET_RTAC_CVP_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							215, void *)
+#define AUDIO_SET_RTAC_CVP_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							216, void *)
+#define AUDIO_GET_RTAC_AFE_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							217, void *)
+#define AUDIO_SET_RTAC_AFE_CAL		_IOWR(CAL_IOCTL_MAGIC, \
+							218, void *)
+enum {
+	CVP_VOC_RX_TOPOLOGY_CAL_TYPE = 0,
+	CVP_VOC_TX_TOPOLOGY_CAL_TYPE,
+	CVP_VOCPROC_STATIC_CAL_TYPE,
+	CVP_VOCPROC_DYNAMIC_CAL_TYPE,
+	CVS_VOCSTRM_STATIC_CAL_TYPE,
+	CVP_VOCDEV_CFG_CAL_TYPE,
+	CVP_VOCPROC_STATIC_COL_CAL_TYPE,
+	CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE,
+	CVS_VOCSTRM_STATIC_COL_CAL_TYPE,
+
+	ADM_TOPOLOGY_CAL_TYPE,
+	ADM_CUST_TOPOLOGY_CAL_TYPE,
+	ADM_AUDPROC_CAL_TYPE,
+	ADM_AUDVOL_CAL_TYPE,
+
+	ASM_TOPOLOGY_CAL_TYPE,
+	ASM_CUST_TOPOLOGY_CAL_TYPE,
+	ASM_AUDSTRM_CAL_TYPE,
+
+	AFE_COMMON_RX_CAL_TYPE,
+	AFE_COMMON_TX_CAL_TYPE,
+	AFE_ANC_CAL_TYPE,
+	AFE_AANC_CAL_TYPE,
+	AFE_FB_SPKR_PROT_CAL_TYPE,
+	AFE_HW_DELAY_CAL_TYPE,
+	AFE_SIDETONE_CAL_TYPE,
+	AFE_TOPOLOGY_CAL_TYPE,
+	AFE_CUST_TOPOLOGY_CAL_TYPE,
+
+	LSM_CUST_TOPOLOGY_CAL_TYPE,
+	LSM_TOPOLOGY_CAL_TYPE,
+	LSM_CAL_TYPE,
+
+	ADM_RTAC_INFO_CAL_TYPE,
+	VOICE_RTAC_INFO_CAL_TYPE,
+	ADM_RTAC_APR_CAL_TYPE,
+	ASM_RTAC_APR_CAL_TYPE,
+	VOICE_RTAC_APR_CAL_TYPE,
+
+	MAD_CAL_TYPE,
+	ULP_AFE_CAL_TYPE,
+	ULP_LSM_CAL_TYPE,
+
+	DTS_EAGLE_CAL_TYPE,
+	AUDIO_CORE_METAINFO_CAL_TYPE,
+	SRS_TRUMEDIA_CAL_TYPE,
+
+	CORE_CUSTOM_TOPOLOGIES_CAL_TYPE,
+	ADM_RTAC_AUDVOL_CAL_TYPE,
+
+	ULP_LSM_TOPOLOGY_ID_CAL_TYPE,
+	AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE,
+	AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE,
+	MAX_CAL_TYPES,
+};
+
+#define AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE
+#define AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE
+
+enum {
+	VERSION_0_0,
+};
+
+enum {
+	PER_VOCODER_CAL_BIT_MASK = 0x10000,
+};
+
+#define MAX_IOCTL_CMD_SIZE	512
+
+/* common structures */
+
+struct audio_cal_header {
+	int32_t		data_size;
+	int32_t		version;
+	int32_t		cal_type;
+	int32_t		cal_type_size;
+};
+
+struct audio_cal_type_header {
+	int32_t		version;
+	int32_t		buffer_number;
+};
+
+struct audio_cal_data {
+	/* Size of cal data at mem_handle allocation or at vaddr */
+	int32_t		cal_size;
+	/* If mem_handle if shared memory is used*/
+	int32_t		mem_handle;
+	/* size of virtual memory if shared memory not used */
+};
+
+
+/* AUDIO_ALLOCATE_CALIBRATION */
+struct audio_cal_type_alloc {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+};
+
+struct audio_cal_alloc {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_alloc	cal_type;
+};
+
+
+/* AUDIO_DEALLOCATE_CALIBRATION */
+struct audio_cal_type_dealloc {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+};
+
+struct audio_cal_dealloc {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_dealloc	cal_type;
+};
+
+
+/* AUDIO_PREPARE_CALIBRATION */
+struct audio_cal_type_prepare {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+};
+
+struct audio_cal_prepare {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_prepare	cal_type;
+};
+
+
+/* AUDIO_POST_CALIBRATION */
+struct audio_cal_type_post {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+};
+
+struct audio_cal_post {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_post	cal_type;
+};
+
+/*AUDIO_CORE_META_INFO */
+
+struct audio_cal_info_metainfo {
+	uint32_t nKey;
+};
+
+/* Cal info types */
+enum {
+	RX_DEVICE,
+	TX_DEVICE,
+	MAX_PATH_TYPE
+};
+
+struct audio_cal_info_adm_top {
+	int32_t		topology;
+	int32_t		acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t		path;
+	int32_t		app_type;
+	int32_t		sample_rate;
+};
+
+struct audio_cal_info_audproc {
+	int32_t		acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t		path;
+	int32_t		app_type;
+	int32_t		sample_rate;
+};
+
+struct audio_cal_info_audvol {
+	int32_t		acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t		path;
+	int32_t		app_type;
+	int32_t		vol_index;
+};
+
+struct audio_cal_info_afe {
+	int32_t		acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t		path;
+	int32_t		sample_rate;
+};
+
+struct audio_cal_info_afe_top {
+	int32_t		topology;
+	int32_t		acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t		path;
+	int32_t		sample_rate;
+};
+
+struct audio_cal_info_asm_top {
+	int32_t		topology;
+	int32_t		app_type;
+};
+
+struct audio_cal_info_audstrm {
+	int32_t		app_type;
+};
+
+struct audio_cal_info_aanc {
+	int32_t		acdb_id;
+};
+
+#define MAX_HW_DELAY_ENTRIES	25
+
+struct audio_cal_hw_delay_entry {
+	uint32_t sample_rate;
+	uint32_t delay_usec;
+};
+
+struct audio_cal_hw_delay_data {
+	uint32_t				num_entries;
+	struct audio_cal_hw_delay_entry		entry[MAX_HW_DELAY_ENTRIES];
+};
+
+struct audio_cal_info_hw_delay {
+	int32_t					acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t					path;
+	int32_t					property_type;
+	struct audio_cal_hw_delay_data		data;
+};
+
+enum msm_spkr_prot_states {
+	MSM_SPKR_PROT_CALIBRATED,
+	MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS,
+	MSM_SPKR_PROT_DISABLED,
+	MSM_SPKR_PROT_NOT_CALIBRATED,
+	MSM_SPKR_PROT_PRE_CALIBRATED,
+	MSM_SPKR_PROT_IN_FTM_MODE
+};
+#define MSM_SPKR_PROT_IN_FTM_MODE MSM_SPKR_PROT_IN_FTM_MODE
+
+enum msm_spkr_count {
+	SP_V2_SPKR_1,
+	SP_V2_SPKR_2,
+	SP_V2_NUM_MAX_SPKRS
+};
+
+struct audio_cal_info_spk_prot_cfg {
+	int32_t		r0[SP_V2_NUM_MAX_SPKRS];
+	int32_t		t0[SP_V2_NUM_MAX_SPKRS];
+	uint32_t	quick_calib_flag;
+	uint32_t	mode;
+	/*
+	 * 0 - Start spk prot
+	 * 1 - Start calib
+	 * 2 - Disable spk prot
+	 */
+};
+
+struct audio_cal_info_sp_th_vi_ftm_cfg {
+	uint32_t	wait_time[SP_V2_NUM_MAX_SPKRS];
+	uint32_t	ftm_time[SP_V2_NUM_MAX_SPKRS];
+	uint32_t	mode;
+	/*
+	 * 0 - normal running mode
+	 * 1 - Calibration
+	 * 2 - FTM mode
+	 */
+};
+
+struct audio_cal_info_sp_ex_vi_ftm_cfg {
+	uint32_t	wait_time[SP_V2_NUM_MAX_SPKRS];
+	uint32_t	ftm_time[SP_V2_NUM_MAX_SPKRS];
+	uint32_t	mode;
+	/*
+	 * 0 - normal running mode
+	 * 2 - FTM mode
+	 */
+};
+
+struct audio_cal_info_sp_ex_vi_param {
+	int32_t		freq_q20[SP_V2_NUM_MAX_SPKRS];
+	int32_t		resis_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		qmct_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		status[SP_V2_NUM_MAX_SPKRS];
+};
+
+struct audio_cal_info_sp_th_vi_param {
+	int32_t		r_dc_q24[SP_V2_NUM_MAX_SPKRS];
+	int32_t		temp_q22[SP_V2_NUM_MAX_SPKRS];
+	int32_t		status[SP_V2_NUM_MAX_SPKRS];
+};
+
+struct audio_cal_info_msm_spk_prot_status {
+	int32_t		r0[SP_V2_NUM_MAX_SPKRS];
+	int32_t		status;
+};
+
+struct audio_cal_info_sidetone {
+	uint16_t	enable;
+	uint16_t	gain;
+	int32_t		tx_acdb_id;
+	int32_t		rx_acdb_id;
+	int32_t		mid;
+	int32_t		pid;
+};
+
+struct audio_cal_info_lsm_top {
+	int32_t		topology;
+	int32_t		acdb_id;
+	int32_t		app_type;
+};
+
+
+struct audio_cal_info_lsm {
+	int32_t		acdb_id;
+	/* RX_DEVICE or TX_DEVICE */
+	int32_t		path;
+	int32_t		app_type;
+};
+
+struct audio_cal_info_voc_top {
+	int32_t		topology;
+	int32_t		acdb_id;
+};
+
+struct audio_cal_info_vocproc {
+	int32_t		tx_acdb_id;
+	int32_t		rx_acdb_id;
+	int32_t		tx_sample_rate;
+	int32_t		rx_sample_rate;
+};
+
+enum {
+	DEFAULT_FEATURE_SET,
+	VOL_BOOST_FEATURE_SET,
+};
+
+struct audio_cal_info_vocvol {
+	int32_t		tx_acdb_id;
+	int32_t		rx_acdb_id;
+	/* DEFAULT_ or VOL_BOOST_FEATURE_SET */
+	int32_t		feature_set;
+};
+
+struct audio_cal_info_vocdev_cfg {
+	int32_t		tx_acdb_id;
+	int32_t		rx_acdb_id;
+};
+
+#define MAX_VOICE_COLUMNS	20
+
+union audio_cal_col_na {
+	uint8_t		val8;
+	uint16_t	val16;
+	uint32_t	val32;
+	uint64_t	val64;
+} __packed;
+
+struct audio_cal_col {
+	uint32_t		id;
+	uint32_t		type;
+	union audio_cal_col_na	na_value;
+} __packed;
+
+struct audio_cal_col_data {
+	uint32_t		num_columns;
+	struct audio_cal_col	column[MAX_VOICE_COLUMNS];
+} __packed;
+
+struct audio_cal_info_voc_col {
+	int32_t				table_id;
+	int32_t				tx_acdb_id;
+	int32_t				rx_acdb_id;
+	struct audio_cal_col_data	data;
+};
+
+/* AUDIO_SET_CALIBRATION & */
+struct audio_cal_type_basic {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+};
+
+struct audio_cal_basic {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_basic	cal_type;
+};
+
+struct audio_cal_type_adm_top {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_adm_top	cal_info;
+};
+
+struct audio_cal_adm_top {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_adm_top	cal_type;
+};
+
+struct audio_cal_type_metainfo {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_metainfo	cal_info;
+};
+
+struct audio_core_metainfo {
+	struct audio_cal_header	  hdr;
+	struct audio_cal_type_metainfo cal_type;
+};
+
+struct audio_cal_type_audproc {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_audproc	cal_info;
+};
+
+struct audio_cal_audproc {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_audproc	cal_type;
+};
+
+struct audio_cal_type_audvol {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_audvol	cal_info;
+};
+
+struct audio_cal_audvol {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_audvol	cal_type;
+};
+
+struct audio_cal_type_asm_top {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_asm_top	cal_info;
+};
+
+struct audio_cal_asm_top {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_asm_top	cal_type;
+};
+
+struct audio_cal_type_audstrm {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_audstrm	cal_info;
+};
+
+struct audio_cal_audstrm {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_audstrm	cal_type;
+};
+
+struct audio_cal_type_afe {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_afe	cal_info;
+};
+
+struct audio_cal_afe {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_afe	cal_type;
+};
+
+struct audio_cal_type_afe_top {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_afe_top	cal_info;
+};
+
+struct audio_cal_afe_top {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_afe_top	cal_type;
+};
+
+struct audio_cal_type_aanc {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_aanc	cal_info;
+};
+
+struct audio_cal_aanc {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_aanc	cal_type;
+};
+
+struct audio_cal_type_fb_spk_prot_cfg {
+	struct audio_cal_type_header		cal_hdr;
+	struct audio_cal_data			cal_data;
+	struct audio_cal_info_spk_prot_cfg	cal_info;
+};
+
+struct audio_cal_fb_spk_prot_cfg {
+	struct audio_cal_header			hdr;
+	struct audio_cal_type_fb_spk_prot_cfg	cal_type;
+};
+
+struct audio_cal_type_sp_th_vi_ftm_cfg {
+	struct audio_cal_type_header		cal_hdr;
+	struct audio_cal_data			cal_data;
+	struct audio_cal_info_sp_th_vi_ftm_cfg	cal_info;
+};
+
+struct audio_cal_sp_th_vi_ftm_cfg {
+	struct audio_cal_header			hdr;
+	struct audio_cal_type_sp_th_vi_ftm_cfg	cal_type;
+};
+
+struct audio_cal_type_sp_ex_vi_ftm_cfg {
+	struct audio_cal_type_header		cal_hdr;
+	struct audio_cal_data			cal_data;
+	struct audio_cal_info_sp_ex_vi_ftm_cfg	cal_info;
+};
+
+struct audio_cal_sp_ex_vi_ftm_cfg {
+	struct audio_cal_header			hdr;
+	struct audio_cal_type_sp_ex_vi_ftm_cfg	cal_type;
+};
+struct audio_cal_type_hw_delay {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_hw_delay	cal_info;
+};
+
+struct audio_cal_hw_delay {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_hw_delay	cal_type;
+};
+
+struct audio_cal_type_sidetone {
+	struct audio_cal_type_header		cal_hdr;
+	struct audio_cal_data			cal_data;
+	struct audio_cal_info_sidetone		cal_info;
+};
+
+struct audio_cal_sidetone {
+	struct audio_cal_header			hdr;
+	struct audio_cal_type_sidetone		cal_type;
+};
+
+struct audio_cal_type_lsm_top {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_lsm_top	cal_info;
+};
+
+struct audio_cal_lsm_top {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_lsm_top	cal_type;
+};
+
+struct audio_cal_type_lsm {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_lsm	cal_info;
+};
+
+struct audio_cal_lsm {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_lsm	cal_type;
+};
+
+struct audio_cal_type_voc_top {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_voc_top	cal_info;
+};
+
+struct audio_cal_voc_top {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_voc_top	cal_type;
+};
+
+struct audio_cal_type_vocproc {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_vocproc	cal_info;
+};
+
+struct audio_cal_vocproc {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_vocproc	cal_type;
+};
+
+struct audio_cal_type_vocvol {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_vocvol	cal_info;
+};
+
+struct audio_cal_vocvol {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_vocvol	cal_type;
+};
+
+struct audio_cal_type_vocdev_cfg {
+	struct audio_cal_type_header		cal_hdr;
+	struct audio_cal_data			cal_data;
+	struct audio_cal_info_vocdev_cfg	cal_info;
+};
+
+struct audio_cal_vocdev_cfg {
+	struct audio_cal_header			hdr;
+	struct audio_cal_type_vocdev_cfg	cal_type;
+};
+
+struct audio_cal_type_voc_col {
+	struct audio_cal_type_header	cal_hdr;
+	struct audio_cal_data		cal_data;
+	struct audio_cal_info_voc_col	cal_info;
+};
+
+struct audio_cal_voc_col {
+	struct audio_cal_header		hdr;
+	struct audio_cal_type_voc_col	cal_type;
+};
+
+/* AUDIO_GET_CALIBRATION */
+struct audio_cal_type_fb_spk_prot_status {
+	struct audio_cal_type_header			cal_hdr;
+	struct audio_cal_data				cal_data;
+	struct audio_cal_info_msm_spk_prot_status	cal_info;
+};
+
+struct audio_cal_fb_spk_prot_status {
+	struct audio_cal_header				hdr;
+	struct audio_cal_type_fb_spk_prot_status	cal_type;
+};
+
+struct audio_cal_type_sp_th_vi_param {
+	struct audio_cal_type_header			cal_hdr;
+	struct audio_cal_data				cal_data;
+	struct audio_cal_info_sp_th_vi_param		cal_info;
+};
+
+struct audio_cal_sp_th_vi_param {
+	struct audio_cal_header				hdr;
+	struct audio_cal_type_sp_th_vi_param		cal_type;
+};
+struct audio_cal_type_sp_ex_vi_param {
+	struct audio_cal_type_header			cal_hdr;
+	struct audio_cal_data				cal_data;
+	struct audio_cal_info_sp_ex_vi_param		cal_info;
+};
+
+struct audio_cal_sp_ex_vi_param {
+	struct audio_cal_header				hdr;
+	struct audio_cal_type_sp_ex_vi_param		cal_type;
+};
+#endif /* _UAPI_MSM_AUDIO_CALIBRATION_H */
diff --git a/include/uapi/linux/msm_audio_g711.h b/include/uapi/linux/msm_audio_g711.h
new file mode 100644
index 0000000..48ebd6a
--- /dev/null
+++ b/include/uapi/linux/msm_audio_g711.h
@@ -0,0 +1,17 @@
+#ifndef _UAPI_MSM_AUDIO_G711_H
+#define _UAPI_MSM_AUDIO_G711_H
+
+#include <linux/msm_audio.h>
+
+struct msm_audio_g711_enc_config {
+	uint32_t sample_rate;
+};
+
+#define AUDIO_SET_G711_ENC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_enc_config)
+
+#define AUDIO_GET_G711_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_enc_config)
+
+
+#endif /* _UAPI_MSM_AUDIO_G711_H */
diff --git a/include/uapi/linux/msm_audio_g711_dec.h b/include/uapi/linux/msm_audio_g711_dec.h
new file mode 100644
index 0000000..ff7e4ce
--- /dev/null
+++ b/include/uapi/linux/msm_audio_g711_dec.h
@@ -0,0 +1,16 @@
+#ifndef _UAPI_MSM_AUDIO_G711_H
+#define _UAPI_MSM_AUDIO_G711_H
+
+#include <linux/msm_audio.h>
+
+struct msm_audio_g711_dec_config {
+	uint32_t sample_rate;
+};
+
+#define AUDIO_SET_G711_DEC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_g711_dec_config)
+
+#define AUDIO_GET_G711_DEC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_g711_dec_config)
+
+#endif /* _UAPI_MSM_AUDIO_G711_H */
diff --git a/include/uapi/linux/msm_audio_mvs.h b/include/uapi/linux/msm_audio_mvs.h
new file mode 100644
index 0000000..5b76bf9
--- /dev/null
+++ b/include/uapi/linux/msm_audio_mvs.h
@@ -0,0 +1,155 @@
+#ifndef _UAPI_MSM_AUDIO_MVS_H
+#define _UAPI_MSM_AUDIO_MVS_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_GET_MVS_CONFIG _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM + 0), unsigned int)
+#define AUDIO_SET_MVS_CONFIG _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM + 1), unsigned int)
+
+/* MVS modes */
+#define MVS_MODE_IS733 0x1 /*QCELP 13K*/
+#define MVS_MODE_IS127 0x2 /*EVRC-8k*/
+#define MVS_MODE_4GV_NB 0x3 /*EVRC-B*/
+#define MVS_MODE_4GV_WB 0x4 /*EVRC-WB*/
+#define MVS_MODE_AMR 0x5
+#define MVS_MODE_EFR 0x6
+#define MVS_MODE_FR 0x7
+#define MVS_MODE_HR 0x8
+#define MVS_MODE_LINEAR_PCM 0x9
+#define MVS_MODE_G711 0xA
+#define MVS_MODE_PCM 0xC
+#define MVS_MODE_AMR_WB 0xD
+#define MVS_MODE_G729A 0xE
+#define MVS_MODE_G711A 0xF
+#define MVS_MODE_G722 0x10
+#define MVS_MODE_PCM_WB 0x12
+
+enum msm_audio_amr_mode {
+	MVS_AMR_MODE_0475, /* AMR 4.75 kbps */
+	MVS_AMR_MODE_0515, /* AMR 5.15 kbps */
+	MVS_AMR_MODE_0590, /* AMR 5.90 kbps */
+	MVS_AMR_MODE_0670, /* AMR 6.70 kbps */
+	MVS_AMR_MODE_0740, /* AMR 7.40 kbps */
+	MVS_AMR_MODE_0795, /* AMR 7.95 kbps */
+	MVS_AMR_MODE_1020, /* AMR 10.20 kbps */
+	MVS_AMR_MODE_1220, /* AMR 12.20 kbps */
+	MVS_AMR_MODE_0660, /* AMR-WB 6.60 kbps */
+	MVS_AMR_MODE_0885, /* AMR-WB 8.85 kbps */
+	MVS_AMR_MODE_1265, /* AMR-WB 12.65 kbps */
+	MVS_AMR_MODE_1425, /* AMR-WB 14.25 kbps */
+	MVS_AMR_MODE_1585, /* AMR-WB 15.85 kbps */
+	MVS_AMR_MODE_1825, /* AMR-WB 18.25 kbps */
+	MVS_AMR_MODE_1985, /* AMR-WB 19.85 kbps */
+	MVS_AMR_MODE_2305, /* AMR-WB 23.05 kbps */
+	MVS_AMR_MODE_2385, /* AMR-WB 23.85 kbps */
+	MVS_AMR_MODE_UNDEF
+};
+
+/* The MVS VOC rate type is used to identify the rate of QCELP 13K(IS733),
+ * EVRC(IS127), 4GV, or 4GV-WB frame.
+ */
+enum msm_audio_voc_rate {
+		MVS_VOC_0_RATE, /* Blank frame */
+		MVS_VOC_8_RATE, /* 1/8 rate    */
+		MVS_VOC_4_RATE, /* 1/4 rate    */
+		MVS_VOC_2_RATE, /* 1/2 rate    */
+		MVS_VOC_1_RATE, /* Full rate   */
+		MVS_VOC_ERASURE, /* erasure frame */
+		MVS_VOC_RATE_MAX,
+		MVS_VOC_RATE_UNDEF = MVS_VOC_RATE_MAX
+};
+
+enum msm_audio_amr_frame_type {
+	MVS_AMR_SPEECH_GOOD,	      /* Good speech frame              */
+	MVS_AMR_SPEECH_DEGRADED,      /* Speech degraded                */
+	MVS_AMR_ONSET,		      /* Onset                          */
+	MVS_AMR_SPEECH_BAD,	      /* Corrupt speech frame (bad CRC) */
+	MVS_AMR_SID_FIRST,	      /* First silence descriptor       */
+	MVS_AMR_SID_UPDATE,	      /* Comfort noise frame            */
+	MVS_AMR_SID_BAD,	      /* Corrupt SID frame (bad CRC)    */
+	MVS_AMR_NO_DATA,	      /* Nothing to transmit            */
+	MVS_AMR_SPEECH_LOST	      /* Downlink speech lost           */
+};
+
+enum msm_audio_g711a_mode {
+	MVS_G711A_MODE_MULAW,
+	MVS_G711A_MODE_ALAW
+};
+
+enum msm_audio_g711_mode {
+	MVS_G711_MODE_MULAW,
+	MVS_G711_MODE_ALAW
+};
+
+enum mvs_g722_mode_type {
+	MVS_G722_MODE_01,
+	MVS_G722_MODE_02,
+	MVS_G722_MODE_03,
+	MVS_G722_MODE_MAX,
+	MVS_G722_MODE_UNDEF
+};
+
+enum msm_audio_g711a_frame_type {
+	MVS_G711A_SPEECH_GOOD,
+	MVS_G711A_SID,
+	MVS_G711A_NO_DATA,
+	MVS_G711A_ERASURE
+};
+
+enum msm_audio_g729a_frame_type {
+	MVS_G729A_NO_DATA,
+	MVS_G729A_SPEECH_GOOD,
+	MVS_G729A_SID,
+	MVS_G729A_ERASURE
+};
+
+struct min_max_rate {
+	uint32_t min_rate;
+	uint32_t max_rate;
+};
+
+struct msm_audio_mvs_config {
+	uint32_t mvs_mode;
+	uint32_t rate_type;
+	struct min_max_rate min_max_rate;
+	uint32_t dtx_mode;
+};
+
+#define MVS_MAX_VOC_PKT_SIZE 640
+
+struct gsm_header {
+	uint8_t bfi;
+	uint8_t sid;
+	uint8_t taf;
+	uint8_t ufi;
+};
+
+struct q6_msm_audio_mvs_frame {
+	union {
+	uint32_t frame_type;
+	uint32_t packet_rate;
+	struct gsm_header gsm_frame_type;
+	} header;
+	uint32_t len;
+	uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE];
+
+};
+
+struct msm_audio_mvs_frame {
+	uint32_t frame_type;
+	uint32_t len;
+	uint8_t voc_pkt[MVS_MAX_VOC_PKT_SIZE];
+
+};
+
+#define Q5V2_MVS_MAX_VOC_PKT_SIZE 320
+
+struct q5v2_msm_audio_mvs_frame {
+	uint32_t frame_type;
+	uint32_t len;
+	uint8_t voc_pkt[Q5V2_MVS_MAX_VOC_PKT_SIZE];
+
+};
+#endif /* _UAPI_MSM_AUDIO_MVS_H */
diff --git a/include/uapi/linux/msm_audio_qcp.h b/include/uapi/linux/msm_audio_qcp.h
new file mode 100644
index 0000000..fdb234e
--- /dev/null
+++ b/include/uapi/linux/msm_audio_qcp.h
@@ -0,0 +1,37 @@
+#ifndef _UAPI_MSM_AUDIO_QCP_H
+#define _UAPI_MSM_AUDIO_QCP_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_SET_QCELP_ENC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	0, struct msm_audio_qcelp_enc_config)
+
+#define AUDIO_GET_QCELP_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	1, struct msm_audio_qcelp_enc_config)
+
+#define AUDIO_SET_EVRC_ENC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	2, struct msm_audio_evrc_enc_config)
+
+#define AUDIO_GET_EVRC_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	3, struct msm_audio_evrc_enc_config)
+
+#define CDMA_RATE_BLANK		0x00
+#define CDMA_RATE_EIGHTH	0x01
+#define CDMA_RATE_QUARTER	0x02
+#define CDMA_RATE_HALF		0x03
+#define CDMA_RATE_FULL		0x04
+#define CDMA_RATE_ERASURE	0x05
+
+struct msm_audio_qcelp_enc_config {
+	uint32_t cdma_rate;
+	uint32_t min_bit_rate;
+	uint32_t max_bit_rate;
+};
+
+struct msm_audio_evrc_enc_config {
+	uint32_t cdma_rate;
+	uint32_t min_bit_rate;
+	uint32_t max_bit_rate;
+};
+
+#endif /* _UAPI_MSM_AUDIO_QCP_H */
diff --git a/include/uapi/linux/msm_audio_sbc.h b/include/uapi/linux/msm_audio_sbc.h
new file mode 100644
index 0000000..1c7c63d
--- /dev/null
+++ b/include/uapi/linux/msm_audio_sbc.h
@@ -0,0 +1,36 @@
+#ifndef _UAPI_MSM_AUDIO_SBC_H
+#define _UAPI_MSM_AUDIO_SBC_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_SET_SBC_ENC_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_sbc_enc_config)
+
+#define AUDIO_GET_SBC_ENC_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_sbc_enc_config)
+
+#define AUDIO_SBC_BA_LOUDNESS		0x0
+#define AUDIO_SBC_BA_SNR		0x1
+
+#define AUDIO_SBC_MODE_MONO		0x0
+#define AUDIO_SBC_MODE_DUAL		0x1
+#define AUDIO_SBC_MODE_STEREO		0x2
+#define AUDIO_SBC_MODE_JSTEREO		0x3
+
+#define AUDIO_SBC_BANDS_8		0x1
+
+#define AUDIO_SBC_BLOCKS_4		0x0
+#define AUDIO_SBC_BLOCKS_8		0x1
+#define AUDIO_SBC_BLOCKS_12		0x2
+#define AUDIO_SBC_BLOCKS_16		0x3
+
+struct msm_audio_sbc_enc_config {
+	uint32_t channels;
+	uint32_t sample_rate;
+	uint32_t bit_allocation;
+	uint32_t number_of_subbands;
+	uint32_t number_of_blocks;
+	uint32_t bit_rate;
+	uint32_t mode;
+};
+#endif /* _UAPI_MSM_AUDIO_SBC_H */
diff --git a/include/uapi/linux/msm_audio_voicememo.h b/include/uapi/linux/msm_audio_voicememo.h
new file mode 100644
index 0000000..a7a7a4d
--- /dev/null
+++ b/include/uapi/linux/msm_audio_voicememo.h
@@ -0,0 +1,66 @@
+#ifndef _UAPI_MSM_AUDIO_VOICEMEMO_H
+#define _UAPI_MSM_AUDIO_VOICEMEMO_H
+
+#include <linux/msm_audio.h>
+
+#define AUDIO_GET_VOICEMEMO_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int)
+#define AUDIO_SET_VOICEMEMO_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	(AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int)
+
+/* rec_type */
+enum rpc_voc_rec_dir_type {
+	RPC_VOC_REC_NONE,
+	RPC_VOC_REC_FORWARD,
+	RPC_VOC_REC_REVERSE,
+	RPC_VOC_REC_BOTH,
+	RPC_VOC_MAX_REC_TYPE
+};
+
+/* capability */
+enum rpc_voc_capability_type {
+	RPC_VOC_CAP_IS733 = 4,
+	RPC_VOC_CAP_IS127 = 8,
+	RPC_VOC_CAP_AMR = 64,
+	RPC_VOC_CAP_32BIT_DUMMY = 2147483647
+};
+
+/* Rate */
+enum rpc_voc_rate_type {
+	RPC_VOC_0_RATE = 0,
+	RPC_VOC_8_RATE,
+	RPC_VOC_4_RATE,
+	RPC_VOC_2_RATE,
+	RPC_VOC_1_RATE,
+	RPC_VOC_ERASURE,
+	RPC_VOC_ERR_RATE,
+	RPC_VOC_AMR_RATE_475 = 0,
+	RPC_VOC_AMR_RATE_515 = 1,
+	RPC_VOC_AMR_RATE_590 = 2,
+	RPC_VOC_AMR_RATE_670 = 3,
+	RPC_VOC_AMR_RATE_740 = 4,
+	RPC_VOC_AMR_RATE_795 = 5,
+	RPC_VOC_AMR_RATE_1020 = 6,
+	RPC_VOC_AMR_RATE_1220 = 7,
+};
+
+/* frame_format */
+enum rpc_voc_pb_len_rate_var_type {
+	RPC_VOC_PB_NATIVE_QCP = 3,
+	RPC_VOC_PB_AMR,
+	RPC_VOC_PB_EVB
+};
+
+struct msm_audio_voicememo_config {
+	uint32_t rec_type;
+	uint32_t rec_interval_ms;
+	uint32_t auto_stop_ms;
+	uint32_t capability;
+	uint32_t max_rate;
+	uint32_t min_rate;
+	uint32_t frame_format;
+	uint32_t dtx_enable;
+	uint32_t data_req_ms;
+};
+
+#endif /* _UAPI_MSM_AUDIO_VOICEMEMO_H */
diff --git a/include/uapi/linux/msm_audio_wma.h b/include/uapi/linux/msm_audio_wma.h
new file mode 100644
index 0000000..523fade
--- /dev/null
+++ b/include/uapi/linux/msm_audio_wma.h
@@ -0,0 +1,33 @@
+#ifndef _UAPI_MSM_AUDIO_WMA_H
+#define _UAPI_MSM_AUDIO_WMA_H
+
+#define AUDIO_GET_WMA_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), unsigned int)
+#define AUDIO_SET_WMA_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), unsigned int)
+
+#define AUDIO_GET_WMA_CONFIG_V2  _IOR(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+2), struct msm_audio_wma_config_v2)
+#define AUDIO_SET_WMA_CONFIG_V2  _IOW(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+3), struct msm_audio_wma_config_v2)
+
+struct msm_audio_wma_config {
+	unsigned short	armdatareqthr;
+	unsigned short	channelsdecoded;
+	unsigned short	wmabytespersec;
+	unsigned short	wmasamplingfreq;
+	unsigned short	wmaencoderopts;
+};
+
+struct msm_audio_wma_config_v2 {
+	unsigned short	format_tag;
+	unsigned short	numchannels;
+	uint32_t	samplingrate;
+	uint32_t	avgbytespersecond;
+	unsigned short	block_align;
+	unsigned short  validbitspersample;
+	uint32_t	channelmask;
+	unsigned short	encodeopt;
+};
+
+#endif /* _UAPI_MSM_AUDIO_WMA_H */
diff --git a/include/uapi/linux/msm_audio_wmapro.h b/include/uapi/linux/msm_audio_wmapro.h
new file mode 100644
index 0000000..64cbf9e
--- /dev/null
+++ b/include/uapi/linux/msm_audio_wmapro.h
@@ -0,0 +1,22 @@
+#ifndef _UAPI_MSM_AUDIO_WMAPRO_H
+#define _UAPI_MSM_AUDIO_WMAPRO_H
+
+#define AUDIO_GET_WMAPRO_CONFIG  _IOR(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+0), struct msm_audio_wmapro_config)
+#define AUDIO_SET_WMAPRO_CONFIG  _IOW(AUDIO_IOCTL_MAGIC, \
+	  (AUDIO_MAX_COMMON_IOCTL_NUM+1), struct msm_audio_wmapro_config)
+
+struct msm_audio_wmapro_config {
+	unsigned short  armdatareqthr;
+	uint8_t         validbitspersample;
+	uint8_t         numchannels;
+	unsigned short  formattag;
+	uint32_t        samplingrate;
+	uint32_t        avgbytespersecond;
+	unsigned short  asfpacketlength;
+	uint32_t        channelmask;
+	unsigned short  encodeopt;
+	unsigned short  advancedencodeopt;
+	uint32_t        advancedencodeopt2;
+};
+#endif /* _UAPI_MSM_AUDIO_WMAPRO_H */
diff --git a/include/uapi/linux/msm_kgsl.h b/include/uapi/linux/msm_kgsl.h
index 573d1a8..7ed5a4c 100644
--- a/include/uapi/linux/msm_kgsl.h
+++ b/include/uapi/linux/msm_kgsl.h
@@ -50,6 +50,7 @@
 #define KGSL_CONTEXT_IFH_NOP            0x00010000
 #define KGSL_CONTEXT_SECURE             0x00020000
 #define KGSL_CONTEXT_NO_SNAPSHOT        0x00040000
+#define KGSL_CONTEXT_SPARSE             0x00080000
 
 #define KGSL_CONTEXT_PREEMPT_STYLE_MASK       0x0E000000
 #define KGSL_CONTEXT_PREEMPT_STYLE_SHIFT      25
@@ -89,6 +90,7 @@
 #define KGSL_CMDBATCH_END_OF_FRAME	KGSL_CONTEXT_END_OF_FRAME   /* 0x100 */
 #define KGSL_CMDBATCH_SYNC		KGSL_CONTEXT_SYNC           /* 0x400 */
 #define KGSL_CMDBATCH_PWR_CONSTRAINT	KGSL_CONTEXT_PWR_CONSTRAINT /* 0x800 */
+#define KGSL_CMDBATCH_SPARSE	    0x1000 /* 0x1000 */
 
 /*
  * Reserve bits [16:19] and bits [28:31] for possible bits shared between
@@ -1561,4 +1563,34 @@ struct kgsl_sparse_bind {
 #define IOCTL_KGSL_SPARSE_BIND \
 	_IOW(KGSL_IOC_TYPE, 0x54, struct kgsl_sparse_bind)
 
+/**
+ * struct kgsl_gpu_sparse_command - Argument for
+ * IOCTL_KGSL_GPU_SPARSE_COMMAND
+ * @flags: Current flags for the object
+ * @sparselist: List of kgsl_sparse_binding_object to bind/unbind
+ * @synclist: List of kgsl_command_syncpoints
+ * @sparsesize: Size of kgsl_sparse_binding_object
+ * @numsparse: Number of elements in list
+ * @sync_size: Size of kgsl_command_syncpoint structure
+ * @numsyncs: Number of kgsl_command_syncpoints in syncpoint list
+ * @context_id: Context ID submitting the kgsl_gpu_command
+ * @timestamp: Timestamp for the submitted commands
+ * @id: Virtual ID to bind/unbind
+ */
+struct kgsl_gpu_sparse_command {
+	uint64_t flags;
+	uint64_t __user sparselist;
+	uint64_t __user synclist;
+	unsigned int sparsesize;
+	unsigned int numsparse;
+	unsigned int syncsize;
+	unsigned int numsyncs;
+	unsigned int context_id;
+	unsigned int timestamp;
+	unsigned int id;
+};
+
+#define IOCTL_KGSL_GPU_SPARSE_COMMAND \
+	_IOWR(KGSL_IOC_TYPE, 0x55, struct kgsl_gpu_sparse_command)
+
 #endif /* _UAPI_MSM_KGSL_H */
diff --git a/include/uapi/linux/spcom.h b/include/uapi/linux/spcom.h
new file mode 100644
index 0000000..9b6b9b7
--- /dev/null
+++ b/include/uapi/linux/spcom.h
@@ -0,0 +1,113 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _UAPI_SPCOM_H_
+#define _UAPI_SPCOM_H_
+
+#include <linux/types.h>	/* uint32_t, bool */
+#ifndef BIT
+	#define BIT(x) (1 << x)
+#endif
+#ifndef PAGE_SIZE
+	#define PAGE_SIZE 4096
+#endif
+
+/**
+ * @brief - Secure Processor Communication interface to user space spcomlib.
+ *
+ * Sending data and control commands by write() file operation.
+ * Receiving data by read() file operation.
+ * Getting the next request size by read() file operation,
+ * with special size SPCOM_GET_NEXT_REQUEST_SIZE.
+ */
+
+/* Maximum size (including null) for channel names */
+#define SPCOM_CHANNEL_NAME_SIZE		32
+
+/*
+ * file read(fd, buf, size) with this size,
+ * hints the kernel that user space wants to read the next-req-size.
+ * This size is bigger than both SPCOM_MAX_REQUEST_SIZE and
+ * SPCOM_MAX_RESPONSE_SIZE , so it is not a valid data size.
+ */
+#define SPCOM_GET_NEXT_REQUEST_SIZE	(PAGE_SIZE-1)
+
+/* Command Id between spcomlib and spcom driver, on write() */
+enum spcom_cmd_id {
+	SPCOM_CMD_LOAD_APP	= 0x4C4F4144, /* "LOAD" = 0x4C4F4144 */
+	SPCOM_CMD_RESET_SP	= 0x52455354, /* "REST" = 0x52455354 */
+	SPCOM_CMD_SEND		= 0x53454E44, /* "SEND" = 0x53454E44 */
+	SPCOM_CMD_SEND_MODIFIED	= 0x534E444D, /* "SNDM" = 0x534E444D */
+	SPCOM_CMD_LOCK_ION_BUF  = 0x4C4F434B, /* "LOCK" = 0x4C4F434B */
+	SPCOM_CMD_UNLOCK_ION_BUF = 0x554C434B, /* "ULCK" = 0x4C4F434B */
+	SPCOM_CMD_FSSR		= 0x46535352, /* "FSSR" = 0x46535352 */
+	SPCOM_CMD_CREATE_CHANNEL = 0x43524554, /* "CRET" = 0x43524554 */
+};
+
+/*
+ * @note: Event types that are always implicitly polled:
+ * POLLERR=0x08 | POLLHUP=0x10 | POLLNVAL=0x20
+ * so bits 3,4,5 can't be used
+ */
+enum spcom_poll_events {
+	SPCOM_POLL_LINK_STATE	= BIT(1),
+	SPCOM_POLL_CH_CONNECT	= BIT(2),
+	SPCOM_POLL_READY_FLAG	= BIT(14), /* output */
+	SPCOM_POLL_WAIT_FLAG	= BIT(15), /* if set , wait for the event */
+};
+
+/* Common Command structure between User Space and spcom driver, on write() */
+struct spcom_user_command {
+	enum spcom_cmd_id cmd_id;
+	uint32_t arg;
+} __packed;
+
+/* Command structure between User Space and spcom driver, on write() */
+struct spcom_send_command {
+	enum spcom_cmd_id cmd_id;
+	uint32_t timeout_msec;
+	uint32_t buf_size;
+	char buf[0]; /* Variable buffer size - must be last field */
+} __packed;
+
+/* Command structure between userspace spcomlib and spcom driver, on write() */
+struct spcom_user_create_channel_command {
+	enum spcom_cmd_id cmd_id;
+	char ch_name[SPCOM_CHANNEL_NAME_SIZE];
+} __packed;
+
+/* maximum ION buf for send-modfied-command */
+#define SPCOM_MAX_ION_BUF 4
+
+struct spcom_ion_info {
+	int32_t fd; /* ION buffer File Descriptor, set -1 for invalid fd */
+	uint32_t buf_offset; /* virtual address offset in request/response */
+};
+
+/* Pass this FD to unlock all ION buffer for the specific channel */
+#define SPCOM_ION_FD_UNLOCK_ALL	0xFFFF
+
+struct spcom_ion_handle {
+	int32_t fd;		/* File Descriptor associated with the buffer */
+};
+
+/* Command structure between User Space and spcom driver, on write() */
+struct spcom_user_send_modified_command {
+	enum spcom_cmd_id cmd_id;
+	struct spcom_ion_info ion_info[SPCOM_MAX_ION_BUF];
+	uint32_t timeout_msec;
+	uint32_t buf_size;
+	char buf[0]; /* Variable buffer size - must be last field */
+} __packed;
+
+
+#endif /* _UAPI_SPCOM_H_ */
diff --git a/include/uapi/linux/time.h b/include/uapi/linux/time.h
index e75e1b6..7fe799e 100644
--- a/include/uapi/linux/time.h
+++ b/include/uapi/linux/time.h
@@ -56,6 +56,7 @@ struct itimerval {
 #define CLOCK_BOOTTIME_ALARM		9
 #define CLOCK_SGI_CYCLE			10	/* Hardware specific */
 #define CLOCK_TAI			11
+#define CLOCK_POWEROFF_ALARM		12
 
 #define MAX_CLOCKS			16
 #define CLOCKS_MASK			(CLOCK_REALTIME | CLOCK_MONOTONIC)
diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h
index e2bc417..30258fb 100644
--- a/include/uapi/linux/usb/cdc.h
+++ b/include/uapi/linux/usb/cdc.h
@@ -231,6 +231,7 @@ struct usb_cdc_mbim_extended_desc {
 
 #define USB_CDC_SEND_ENCAPSULATED_COMMAND	0x00
 #define USB_CDC_GET_ENCAPSULATED_RESPONSE	0x01
+#define USB_CDC_RESET_FUNCTION			0x05
 #define USB_CDC_REQ_SET_LINE_CODING		0x20
 #define USB_CDC_REQ_GET_LINE_CODING		0x21
 #define USB_CDC_REQ_SET_CONTROL_LINE_STATE	0x22
diff --git a/include/uapi/linux/v4l2-controls.h b/include/uapi/linux/v4l2-controls.h
index b6a357a..b59fe1a 100644
--- a/include/uapi/linux/v4l2-controls.h
+++ b/include/uapi/linux/v4l2-controls.h
@@ -428,6 +428,7 @@ enum v4l2_mpeg_video_h264_level {
 	V4L2_MPEG_VIDEO_H264_LEVEL_4_2	= 13,
 	V4L2_MPEG_VIDEO_H264_LEVEL_5_0	= 14,
 	V4L2_MPEG_VIDEO_H264_LEVEL_5_1	= 15,
+	V4L2_MPEG_VIDEO_H264_LEVEL_5_2	= 16,
 };
 #define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_ALPHA	(V4L2_CID_MPEG_BASE+360)
 #define V4L2_CID_MPEG_VIDEO_H264_LOOP_FILTER_BETA	(V4L2_CID_MPEG_BASE+361)
@@ -456,6 +457,7 @@ enum v4l2_mpeg_video_h264_profile {
 	V4L2_MPEG_VIDEO_H264_PROFILE_SCALABLE_HIGH_INTRA	= 14,
 	V4L2_MPEG_VIDEO_H264_PROFILE_STEREO_HIGH		= 15,
 	V4L2_MPEG_VIDEO_H264_PROFILE_MULTIVIEW_HIGH		= 16,
+	V4L2_MPEG_VIDEO_H264_PROFILE_CONSTRAINED_HIGH		= 17,
 };
 #define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_HEIGHT	(V4L2_CID_MPEG_BASE+364)
 #define V4L2_CID_MPEG_VIDEO_H264_VUI_EXT_SAR_WIDTH	(V4L2_CID_MPEG_BASE+365)
@@ -647,6 +649,396 @@ enum v4l2_mpeg_mfc51_video_force_frame_type {
 #define V4L2_CID_MPEG_MFC51_VIDEO_H264_ADAPTIVE_RC_STATIC		(V4L2_CID_MPEG_MFC51_BASE+53)
 #define V4L2_CID_MPEG_MFC51_VIDEO_H264_NUM_REF_PIC_FOR_P		(V4L2_CID_MPEG_MFC51_BASE+54)
 
+/*  MPEG-class control IDs specific to the msm_vidc driver */
+#define V4L2_CID_MPEG_MSM_VIDC_BASE		(V4L2_CTRL_CLASS_MPEG | 0x2000)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_PICTYPE_DEC_MODE \
+			(V4L2_CID_MPEG_MSM_VIDC_BASE+0)
+enum v4l2_mpeg_vidc_video_pictype_dec_mode {
+	V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_OFF = 0,
+	V4L2_MPEG_VIDC_VIDEO_PICTYPE_DECODE_ON = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_FORMAT   (V4L2_CID_MPEG_MSM_VIDC_BASE+2)
+enum v4l2_mpeg_vidc_video_stream_format {
+	V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_STARTCODES         = 0,
+	V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_NAL_PER_BUFFER = 1,
+	V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_ONE_BYTE_LENGTH    = 2,
+	V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_TWO_BYTE_LENGTH    = 3,
+	V4L2_MPEG_VIDC_VIDEO_NAL_FORMAT_FOUR_BYTE_LENGTH   = 4,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_OUTPUT_ORDER   (V4L2_CID_MPEG_MSM_VIDC_BASE+3)
+enum v4l2_mpeg_vidc_video_output_order {
+	V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DISPLAY         = 0,
+	V4L2_MPEG_VIDC_VIDEO_OUTPUT_ORDER_DECODE          = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_FRAME_RATE   (V4L2_CID_MPEG_MSM_VIDC_BASE+4)
+#define V4L2_CID_MPEG_VIDC_VIDEO_IDR_PERIOD   (V4L2_CID_MPEG_MSM_VIDC_BASE+5)
+#define V4L2_CID_MPEG_VIDC_VIDEO_NUM_P_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+6)
+#define V4L2_CID_MPEG_VIDC_VIDEO_NUM_B_FRAMES (V4L2_CID_MPEG_MSM_VIDC_BASE+7)
+#define V4L2_CID_MPEG_VIDC_VIDEO_REQUEST_IFRAME (V4L2_CID_MPEG_MSM_VIDC_BASE+8)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL (V4L2_CID_MPEG_MSM_VIDC_BASE+9)
+enum v4l2_mpeg_vidc_video_rate_control {
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_OFF = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_VFR = 1,
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_VBR_CFR = 2,
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_VFR = 3,
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_CBR_CFR = 4,
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR = 5,
+	V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR = 6,
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR	\
+			V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_CFR
+#define V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR	\
+			V4L2_CID_MPEG_VIDC_VIDEO_RATE_CONTROL_MBR_VFR
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_ROTATION (V4L2_CID_MPEG_MSM_VIDC_BASE+10)
+enum v4l2_mpeg_vidc_video_rotation {
+	V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_NONE = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_90 = 1,
+	V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_180 = 2,
+	V4L2_CID_MPEG_VIDC_VIDEO_ROTATION_270 = 3,
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE+11)
+enum v4l2_mpeg_vidc_h264_cabac_model {
+	V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_0 = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_1 = 1,
+	V4L2_CID_MPEG_VIDC_VIDEO_H264_CABAC_MODEL_2 = 2,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_MODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE+12)
+enum v4l2_mpeg_vidc_video_intra_refresh_mode {
+	V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_NONE = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_CYCLIC = 1,
+	V4L2_CID_MPEG_VIDC_VIDEO_INTRA_REFRESH_RANDOM = 2,
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_IR_MBS (V4L2_CID_MPEG_MSM_VIDC_BASE+13)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_AU_DELIMITER \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 14)
+enum v4l2_mpeg_vidc_video_au_delimiter {
+	V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_DISABLED = 0,
+	V4L2_MPEG_VIDC_VIDEO_AU_DELIMITER_ENABLED = 1
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 15)
+enum v4l2_mpeg_vidc_video_sync_frame_decode {
+	V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_DISABLE = 0,
+	V4L2_MPEG_VIDC_VIDEO_SYNC_FRAME_DECODE_ENABLE = 1
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_SECURE (V4L2_CID_MPEG_MSM_VIDC_BASE+16)
+#define V4L2_CID_MPEG_VIDC_VIDEO_EXTRADATA \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 17)
+enum v4l2_mpeg_vidc_extradata {
+	V4L2_MPEG_VIDC_EXTRADATA_NONE = 0,
+	V4L2_MPEG_VIDC_EXTRADATA_MB_QUANTIZATION = 1,
+	V4L2_MPEG_VIDC_EXTRADATA_INTERLACE_VIDEO = 2,
+	V4L2_MPEG_VIDC_EXTRADATA_TIMESTAMP = 5,
+	V4L2_MPEG_VIDC_EXTRADATA_S3D_FRAME_PACKING = 6,
+	V4L2_MPEG_VIDC_EXTRADATA_FRAME_RATE = 7,
+	V4L2_MPEG_VIDC_EXTRADATA_PANSCAN_WINDOW = 8,
+	V4L2_MPEG_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 9,
+	V4L2_MPEG_VIDC_EXTRADATA_MULTISLICE_INFO = 10,
+	V4L2_MPEG_VIDC_EXTRADATA_NUM_CONCEALED_MB = 11,
+	V4L2_MPEG_VIDC_EXTRADATA_METADATA_FILLER = 12,
+	V4L2_MPEG_VIDC_EXTRADATA_INPUT_CROP = 13,
+	V4L2_MPEG_VIDC_EXTRADATA_DIGITAL_ZOOM = 14,
+	V4L2_MPEG_VIDC_EXTRADATA_ASPECT_RATIO = 15,
+	V4L2_MPEG_VIDC_EXTRADATA_MPEG2_SEQDISP = 16,
+	V4L2_MPEG_VIDC_EXTRADATA_STREAM_USERDATA = 17,
+	V4L2_MPEG_VIDC_EXTRADATA_FRAME_QP = 18,
+	V4L2_MPEG_VIDC_EXTRADATA_FRAME_BITS_INFO = 19,
+	V4L2_MPEG_VIDC_EXTRADATA_LTR = 20,
+	V4L2_MPEG_VIDC_EXTRADATA_METADATA_MBI = 21,
+	V4L2_MPEG_VIDC_EXTRADATA_VQZIP_SEI = 22,
+	V4L2_MPEG_VIDC_EXTRADATA_YUV_STATS = 23,
+	V4L2_MPEG_VIDC_EXTRADATA_ROI_QP = 24,
+#define V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP \
+	V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP
+	V4L2_MPEG_VIDC_EXTRADATA_OUTPUT_CROP = 25,
+#define V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI \
+	V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI
+	V4L2_MPEG_VIDC_EXTRADATA_DISPLAY_COLOUR_SEI = 26,
+#define V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \
+	V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI
+	V4L2_MPEG_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 27,
+#define V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO \
+	V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO
+	V4L2_MPEG_VIDC_EXTRADATA_PQ_INFO = 28,
+#define V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY \
+	V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY
+	V4L2_MPEG_VIDC_EXTRADATA_VUI_DISPLAY = 29,
+#define V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE \
+	V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE
+	V4L2_MPEG_VIDC_EXTRADATA_VPX_COLORSPACE = 30,
+};
+
+#define V4L2_CID_MPEG_VIDEO_MULTI_SLICE_DELIVERY_MODE	\
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 18)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_VUI_TIMING_INFO \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 19)
+enum v4l2_mpeg_vidc_video_vui_timing_info {
+	V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_DISABLED = 0,
+	V4L2_MPEG_VIDC_VIDEO_VUI_TIMING_INFO_ENABLED = 1
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_VP8_PROFILE_LEVEL \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 20)
+enum v4l2_mpeg_vidc_video_vp8_profile_level {
+	V4L2_MPEG_VIDC_VIDEO_VP8_UNUSED,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_0,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_1,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_2,
+	V4L2_MPEG_VIDC_VIDEO_VP8_VERSION_3,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 21)
+enum v4l2_mpeg_vidc_video_preserve_text_quality {
+	V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_DISABLED = 0,
+	V4L2_MPEG_VIDC_VIDEO_PRESERVE_TEXT_QUALITY_ENABLED = 1
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_MODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 22)
+enum v4l2_mpeg_vidc_video_decoder_multi_stream {
+	V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_PRIMARY = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_STREAM_OUTPUT_SECONDARY = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_LEVEL	(V4L2_CID_MPEG_MSM_VIDC_BASE+23)
+enum v4l2_mpeg_vidc_video_mpeg2_level {
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_0	= 0,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_1	= 1,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_2	= 2,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_LEVEL_3	= 3,
+};
+#define V4L2_CID_MPEG_VIDC_VIDEO_MPEG2_PROFILE	(V4L2_CID_MPEG_MSM_VIDC_BASE+24)
+enum v4l2_mpeg_vidc_video_mpeg2_profile {
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SIMPLE		= 0,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_MAIN			= 1,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_422			= 2,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SNR_SCALABLE		= 3,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_SPATIAL_SCALABLE	= 4,
+	V4L2_MPEG_VIDC_VIDEO_MPEG2_PROFILE_HIGH			= 5,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_CONCEAL_COLOR \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 25)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_LTRMODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 26)
+
+enum v4l2_mpeg_vidc_video_ltrmode {
+	V4L2_MPEG_VIDC_VIDEO_LTR_MODE_DISABLE = 0,
+	V4L2_MPEG_VIDC_VIDEO_LTR_MODE_MANUAL = 1,
+	V4L2_MPEG_VIDC_VIDEO_LTR_MODE_PERIODIC = 2
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_LTRCOUNT \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 27)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_USELTRFRAME \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 28)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_MARKLTRFRAME \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 29)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_P_NUM_LAYERS \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 30)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_X_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 32)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_X_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 33)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_X_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 34)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_Y_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 35)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_PFRAME_Y_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 36)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_BFRAME_Y_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 37)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 38)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_BUFFER_SIZE_LIMIT \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 39)
+
+enum vl42_mpeg_vidc_video_vpx_error_resilience {
+	V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_DISABLED = 0,
+	V4L2_MPEG_VIDC_VIDEO_VPX_ERROR_RESILIENCE_ENABLED = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_PROFILE \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 40)
+enum v4l2_mpeg_video_hevc_profile {
+	V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN			= 0,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN10		= 1,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_PROFILE_MAIN_STILL_PIC	= 2,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_HEVC_TIER_LEVEL \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 41)
+enum v4l2_mpeg_video_hevc_level {
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_1	= 0,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_1	= 1,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2	= 2,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2	= 3,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_2_1	= 4,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_2_1	= 5,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3	= 6,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3	= 7,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_3_1	= 8,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_3_1	= 9,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4	= 10,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4	= 11,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_4_1	= 12,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_4_1	= 13,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5	= 14,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5	= 15,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_1	= 16,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_1	= 17,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_5_2	= 18,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_5_2	= 19,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6	= 20,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6	= 21,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_1	= 22,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_1	= 23,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_MAIN_TIER_LEVEL_6_2	= 24,
+	V4L2_MPEG_VIDC_VIDEO_HEVC_LEVEL_HIGH_TIER_LEVEL_6_2	= 25,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_HIER_B_NUM_LAYERS \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 42)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_HYBRID_HIERP_MODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 43)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_DPB_COLOR_FORMAT \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 44)
+
+enum v4l2_mpeg_vidc_video_dpb_color_format {
+	V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_NONE = 0,
+	V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_UBWC = 1,
+	V4L2_MPEG_VIDC_VIDEO_DPB_COLOR_FMT_TP10_UBWC = 2
+};
+
+#define V4L2_CID_VIDC_QBUF_MODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 45)
+enum v4l2_vidc_qbuf_mode {
+	V4L2_VIDC_QBUF_STANDARD = 0,
+	V4L2_VIDC_QBUF_BATCHED = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_MAX_HIERP_LAYERS \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 46)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_BASELAYER_ID \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 47)
+
+#define V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_WIDTH \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 48)
+
+#define V4L2_CID_MPEG_VIDC_VENC_PARAM_SAR_HEIGHT \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 49)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI	\
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 50)
+
+enum v4l2_mpeg_vidc_video_vqzip_sei_enable {
+	V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_DISABLE	= 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_VQZIP_SEI_ENABLE	= 1
+};
+
+#define V4L2_CID_MPEG_VIDC_VENC_PARAM_LAYER_BITRATE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 51)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_PRIORITY \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 52)
+
+enum v4l2_mpeg_vidc_video_priority {
+	V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_ENABLE = 0,
+	V4L2_MPEG_VIDC_VIDEO_PRIORITY_REALTIME_DISABLE = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_OPERATING_RATE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 53)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_TYPE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 54)
+
+enum v4l2_mpeg_vidc_video_venc_bitrate_type_enable {
+	V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_DISABLE	= 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_VENC_BITRATE_ENABLE	= 1
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 55)
+
+enum v4l2_cid_mpeg_vidc_video_vpe_csc_type_enable {
+	V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_DISABLE  = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_VPE_CSC_ENABLE   = 1
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_MODE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 56)
+
+enum v4l2_mpeg_vidc_video_lowlatency_mode {
+	V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_DISABLE     = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_LOWLATENCY_ENABLE      = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_WIDTH \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 57)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_BLUR_HEIGHT \
+	(V4L2_CID_MPEG_MSM_VIDC_BASE + 58)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8 \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 59)
+enum v4l2_mpeg_vidc_video_h264_transform_8x8 {
+	V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_DISABLE = 0,
+	V4L2_MPEG_VIDC_VIDEO_H264_TRANSFORM_8x8_ENABLE = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_COLOR_SPACE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 60)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 61)
+
+enum v4l2_cid_mpeg_vidc_video_full_range {
+	V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_DISABLE = 0,
+	V4L2_CID_MPEG_VIDC_VIDEO_FULL_RANGE_ENABLE = 1,
+};
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_TRANSFER_CHARS \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 62)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_MATRIX_COEFFS \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 63)
+
+#define V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_TYPE \
+		(V4L2_CID_MPEG_MSM_VIDC_BASE + 64)
+enum v4l2_mpeg_vidc_video_venc_iframesize_type {
+	V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_DEFAULT,
+	V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_MEDIUM,
+	V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_HUGE,
+	V4L2_CID_MPEG_VIDC_VIDEO_IFRAME_SIZE_UNLIMITED,
+};
 
 /*  Camera class control IDs */
 
diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h
index 94f123f..cf4c7a1 100644
--- a/include/uapi/linux/videodev2.h
+++ b/include/uapi/linux/videodev2.h
@@ -2,6 +2,7 @@
  *  Video for Linux Two header file
  *
  *  Copyright (C) 1999-2012 the contributors
+ *  Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -492,6 +493,8 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_ARGB32  v4l2_fourcc('B', 'A', '2', '4') /* 32  ARGB-8-8-8-8  */
 #define V4L2_PIX_FMT_XRGB32  v4l2_fourcc('B', 'X', '2', '4') /* 32  XRGB-8-8-8-8  */
 
+#define V4L2_PIX_FMT_RGBA8888_UBWC   v4l2_fourcc('Q', 'R', 'G', 'B')
+
 /* Grey formats */
 #define V4L2_PIX_FMT_GREY    v4l2_fourcc('G', 'R', 'E', 'Y') /*  8  Greyscale     */
 #define V4L2_PIX_FMT_Y4      v4l2_fourcc('Y', '0', '4', ' ') /*  4  Greyscale     */
@@ -533,6 +536,11 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_NV24    v4l2_fourcc('N', 'V', '2', '4') /* 24  Y/CbCr 4:4:4  */
 #define V4L2_PIX_FMT_NV42    v4l2_fourcc('N', 'V', '4', '2') /* 24  Y/CrCb 4:4:4  */
 
+/* UBWC 8-bit Y/CbCr 4:2:0  */
+#define V4L2_PIX_FMT_NV12_UBWC        v4l2_fourcc('Q', '1', '2', '8')
+/* UBWC 10-bit Y/CbCr 4:2:0 */
+#define V4L2_PIX_FMT_NV12_TP10_UBWC   v4l2_fourcc('Q', '1', '2', 'A')
+
 /* two non contiguous planes - one Y, one Cr + Cb interleaved  */
 #define V4L2_PIX_FMT_NV12M   v4l2_fourcc('N', 'M', '1', '2') /* 12  Y/CbCr 4:2:0  */
 #define V4L2_PIX_FMT_NV21M   v4l2_fourcc('N', 'M', '2', '1') /* 21  Y/CrCb 4:2:0  */
@@ -603,6 +611,8 @@ struct v4l2_pix_format {
 #define V4L2_PIX_FMT_VC1_ANNEX_G v4l2_fourcc('V', 'C', '1', 'G') /* SMPTE 421M Annex G compliant stream */
 #define V4L2_PIX_FMT_VC1_ANNEX_L v4l2_fourcc('V', 'C', '1', 'L') /* SMPTE 421M Annex L compliant stream */
 #define V4L2_PIX_FMT_VP8      v4l2_fourcc('V', 'P', '8', '0') /* VP8 */
+#define V4L2_PIX_FMT_VP9      v4l2_fourcc('V', 'P', '9', '0') /* VP9 */
+#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* for HEVC stream */
 
 /*  Vendor-specific formats   */
 #define V4L2_PIX_FMT_CPIA1    v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
@@ -921,6 +931,17 @@ struct v4l2_buffer {
 #define V4L2_BUF_FLAG_TSTAMP_SRC_SOE		0x00010000
 /* mem2mem encoder/decoder */
 #define V4L2_BUF_FLAG_LAST			0x00100000
+/* Vendor extensions */
+#define V4L2_QCOM_BUF_FLAG_CODECCONFIG		0x00020000
+#define V4L2_QCOM_BUF_FLAG_EOSEQ		0x00040000
+#define V4L2_QCOM_BUF_TIMESTAMP_INVALID		0x00080000
+#define V4L2_QCOM_BUF_FLAG_DECODEONLY		0x00200000
+#define V4L2_QCOM_BUF_DATA_CORRUPT		0x00400000
+#define V4L2_QCOM_BUF_INPUT_UNSUPPORTED		0x01000000
+#define V4L2_QCOM_BUF_FLAG_EOS			0x02000000
+#define V4L2_QCOM_BUF_FLAG_READONLY		0x04000000
+#define V4L2_MSM_BUF_FLAG_DEFER			0x40000000
+#define V4L2_QCOM_BUF_FLAG_IDRFRAME		0x80000000
 
 /**
  * struct v4l2_exportbuffer - export of video buffer as DMABUF file descriptor
@@ -1794,6 +1815,8 @@ struct v4l2_encoder_cmd {
 #define V4L2_DEC_CMD_STOP        (1)
 #define V4L2_DEC_CMD_PAUSE       (2)
 #define V4L2_DEC_CMD_RESUME      (3)
+#define V4L2_QCOM_CMD_FLUSH      (4)
+#define V4L2_DEC_QCOM_CMD_RECONFIG_HINT  (5)
 
 /* Flags for V4L2_DEC_CMD_START */
 #define V4L2_DEC_CMD_START_MUTE_AUDIO	(1 << 0)
@@ -1805,6 +1828,9 @@ struct v4l2_encoder_cmd {
 #define V4L2_DEC_CMD_STOP_TO_BLACK	(1 << 0)
 #define V4L2_DEC_CMD_STOP_IMMEDIATELY	(1 << 1)
 
+#define V4L2_QCOM_CMD_FLUSH_OUTPUT  (1 << 0)
+#define V4L2_QCOM_CMD_FLUSH_CAPTURE (1 << 1)
+
 /* Play format requirements (returned by the driver): */
 
 /* The decoder has no special format requirements */
@@ -2070,6 +2096,31 @@ struct v4l2_streamparm {
 #define V4L2_EVENT_MOTION_DET			6
 #define V4L2_EVENT_PRIVATE_START		0x08000000
 
+#define V4L2_EVENT_BITDEPTH_FLAG	0x1
+#define V4L2_EVENT_PICSTRUCT_FLAG	0x2
+#define V4L2_EVENT_COLOUR_SPACE_FLAG    0x4
+
+#define V4L2_EVENT_MSM_VIDC_START	(V4L2_EVENT_PRIVATE_START + 0x00001000)
+#define V4L2_EVENT_MSM_VIDC_FLUSH_DONE	(V4L2_EVENT_MSM_VIDC_START + 1)
+#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_SUFFICIENT	\
+		(V4L2_EVENT_MSM_VIDC_START + 2)
+#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_CHANGED_INSUFFICIENT	\
+		(V4L2_EVENT_MSM_VIDC_START + 3)
+/*
+ * Bitdepth changed insufficient is deprecated now, however retaining
+ * to prevent changing the values of the other macros after bitdepth
+ */
+#define V4L2_EVENT_MSM_VIDC_PORT_SETTINGS_BITDEPTH_CHANGED_INSUFFICIENT \
+		(V4L2_EVENT_MSM_VIDC_START + 4)
+#define V4L2_EVENT_MSM_VIDC_SYS_ERROR	(V4L2_EVENT_MSM_VIDC_START + 5)
+#define V4L2_EVENT_MSM_VIDC_RELEASE_BUFFER_REFERENCE \
+		(V4L2_EVENT_MSM_VIDC_START + 6)
+#define V4L2_EVENT_MSM_VIDC_RELEASE_UNQUEUED_BUFFER \
+		(V4L2_EVENT_MSM_VIDC_START + 7)
+#define V4L2_EVENT_MSM_VIDC_HW_OVERLOAD (V4L2_EVENT_MSM_VIDC_START + 8)
+#define V4L2_EVENT_MSM_VIDC_MAX_CLIENTS (V4L2_EVENT_MSM_VIDC_START + 9)
+#define V4L2_EVENT_MSM_VIDC_HW_UNSUPPORTED (V4L2_EVENT_MSM_VIDC_START + 10)
+
 /* Payload for V4L2_EVENT_VSYNC */
 struct v4l2_event_vsync {
 	/* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
diff --git a/include/uapi/media/Kbuild b/include/uapi/media/Kbuild
new file mode 100644
index 0000000..0210a2a
--- /dev/null
+++ b/include/uapi/media/Kbuild
@@ -0,0 +1,7 @@
+header-y += cam_req_mgr.h
+header-y += cam_defs.h
+header-y += cam_isp.h
+header-y += cam_isp_vfe.h
+header-y += cam_isp_ife.h
+header-y += msm_media_info.h
+header-y += msm_vidc.h
diff --git a/include/uapi/media/cam_defs.h b/include/uapi/media/cam_defs.h
new file mode 100644
index 0000000..ad2adb3
--- /dev/null
+++ b/include/uapi/media/cam_defs.h
@@ -0,0 +1,357 @@
+#ifndef __UAPI_CAM_DEFS_H__
+#define __UAPI_CAM_DEFS_H__
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+
+/* camera op codes */
+#define CAM_COMMON_OPCODE_BASE                  0
+#define CAM_QUERY_CAP                           1
+#define CAM_ACQUIRE_DEV                         2
+#define CAM_START_DEV                           3
+#define CAM_STOP_DEV                            4
+#define CAM_CONFIG_DEV                          5
+#define CAM_RELEASE_DEV                         6
+#define CAM_COMMON_OPCODE_MAX                   7
+
+/* camera handle type */
+#define CAM_HANDLE_USER_POINTER                 1
+#define CAM_HANDLE_MEM_HANDLE                   2
+
+/**
+ * struct cam_control - struct used by ioctl control for camera
+ * @op_code:            This is the op code for camera control
+ * @size:               control command size
+ * @handle_type:        user pointer or shared memory handle
+ * @reserved:           reserved field for 64 bit alignment
+ * @handle:             control command payload
+ */
+struct cam_control {
+	uint32_t        op_code;
+	uint32_t        size;
+	uint32_t        handle_type;
+	uint32_t        reserved;
+	uint64_t        handle;
+};
+
+/* camera IOCTL */
+#define VIDIOC_CAM_CONTROL \
+	_IOWR('V', BASE_VIDIOC_PRIVATE, struct cam_control)
+
+/**
+ * struct cam_hw_version - Structure for HW version of camera devices
+ *
+ * @major    : Hardware version major
+ * @minor    : Hardware version minor
+ * @incr     : Hardware version increment
+ * @reserved : reserved for 64 bit aligngment
+ */
+struct cam_hw_version {
+	uint32_t major;
+	uint32_t minor;
+	uint32_t incr;
+	uint32_t reserved;
+};
+
+/**
+ * struct cam_iommu_handle - Structure for IOMMU handles of camera hw devices
+ *
+ * @non_secure: Device Non Secure IOMMU handle
+ * @secure:     Device Secure IOMMU handle
+ *
+ */
+struct cam_iommu_handle {
+	int32_t non_secure;
+	int32_t secure;
+};
+
+/* camera secure mode */
+#define CAM_SECURE_MODE_NON_SECURE             0
+#define CAM_SECURE_MODE_SECURE                 1
+
+/* Camera Format Type */
+#define CAM_FORMAT_BASE                         0
+#define CAM_FORMAT_MIPI_RAW_6                   1
+#define CAM_FORMAT_MIPI_RAW_8                   2
+#define CAM_FORMAT_MIPI_RAW_10                  3
+#define CAM_FORMAT_MIPI_RAW_12                  4
+#define CAM_FORMAT_MIPI_RAW_14                  5
+#define CAM_FORMAT_MIPI_RAW_16                  6
+#define CAM_FORMAT_MIPI_RAW_20                  7
+#define CAM_FORMAT_QTI_RAW_8                    8
+#define CAM_FORMAT_QTI_RAW_10                   9
+#define CAM_FORMAT_QTI_RAW_12                   10
+#define CAM_FORMAT_QTI_RAW_14                   11
+#define CAM_FORMAT_PLAIN8                       12
+#define CAM_FORMAT_PLAIN16_8                    13
+#define CAM_FORMAT_PLAIN16_10                   14
+#define CAM_FORMAT_PLAIN16_12                   15
+#define CAM_FORMAT_PLAIN16_14                   16
+#define CAM_FORMAT_PLAIN16_16                   17
+#define CAM_FORMAT_PLAIN32_20                   18
+#define CAM_FORMAT_PLAIN64                      19
+#define CAM_FORMAT_PLAIN128                     20
+#define CAM_FORMAT_ARGB                         21
+#define CAM_FORMAT_ARGB_10                      22
+#define CAM_FORMAT_ARGB_12                      23
+#define CAM_FORMAT_ARGB_14                      24
+#define CAM_FORMAT_DPCM_10_6_10                 25
+#define CAM_FORMAT_DPCM_10_8_10                 26
+#define CAM_FORMAT_DPCM_12_6_12                 27
+#define CAM_FORMAT_DPCM_12_8_12                 28
+#define CAM_FORMAT_DPCM_14_8_14                 29
+#define CAM_FORMAT_DPCM_14_10_14                30
+#define CAM_FORMAT_NV21                         31
+#define CAM_FORMAT_NV12                         32
+#define CAM_FORMAT_TP10                         33
+#define CAM_FORMAT_YUV422                       34
+#define CAM_FORMAT_PD8                          35
+#define CAM_FORMAT_PD10                         36
+#define CAM_FORMAT_UBWC_NV12                    37
+#define CAM_FORMAT_UBWC_NV12_4R                 38
+#define CAM_FORMAT_UBWC_TP10                    39
+#define CAM_FORMAT_UBWC_P010                    40
+#define CAM_FORMAT_PLAIN8_SWAP                  41
+#define CAM_FORMAT_PLAIN8_10                    42
+#define CAM_FORMAT_PLAIN8_10_SWAP               43
+#define CAM_FORMAT_YV12                         44
+#define CAM_FORMAT_Y_ONLY                       45
+#define CAM_FORMAT_MAX                          46
+
+
+/* camera packet */
+
+/* camera rotaion */
+#define CAM_ROTATE_CW_0_DEGREE                  0
+#define CAM_ROTATE_CW_90_DEGREE                 1
+#define CAM_RORATE_CW_180_DEGREE                2
+#define CAM_ROTATE_CW_270_DEGREE                3
+
+/* camera Color Space */
+#define CAM_COLOR_SPACE_BASE                    0
+#define CAM_COLOR_SPACE_BT601_FULL              1
+#define CAM_COLOR_SPACE_BT601625                2
+#define CAM_COLOR_SPACE_BT601525                3
+#define CAM_COLOR_SPACE_BT709                   4
+#define CAM_COLOR_SPACE_DEPTH                   5
+#define CAM_COLOR_SPACE_MAX                     6
+
+/* camera buffer direction */
+#define CAM_BUF_INPUT                           1
+#define CAM_BUF_OUTPUT                          2
+
+/* camera packet device Type */
+#define CAM_PACKET_DEV_BASE                     0
+#define CAM_PACKET_DEV_IMG_SENSOR               1
+#define CAM_PACKET_DEV_ACTUATOR                 2
+#define CAM_PACKET_DEV_COMPANION                3
+#define CAM_PACKET_DEV_EEPOM                    4
+#define CAM_PACKET_DEV_CSIPHY                   5
+#define CAM_PACKET_DEV_OIS                      6
+#define CAM_PACKET_DEV_FLASH                    7
+#define CAM_PACKET_DEV_FD                       8
+#define CAM_PACKET_DEV_JPEG_ENC                 9
+#define CAM_PACKET_DEV_JPEG_DEC                 10
+#define CAM_PACKET_DEV_VFE                      11
+#define CAM_PACKET_DEV_CPP                      12
+#define CAM_PACKET_DEV_CSID                     13
+#define CAM_PACKET_DEV_ISPIF                    14
+#define CAM_PACKET_DEV_IFE                      15
+#define CAM_PACKET_DEV_ICP                      16
+#define CAM_PACKET_DEV_LRME                     17
+#define CAM_PACKET_DEV_MAX                      18
+
+
+/* constants */
+#define CAM_PACKET_MAX_PLANES                   3
+
+/**
+ * struct cam_plane_cfg - plane configuration info
+ *
+ * @width:                      plane width in pixels
+ * @height:                     plane height in lines
+ * @plane_stride:               plane stride in pixel
+ * @slice_height:               slice height in line (not used by ISP)
+ *
+ */
+struct cam_plane_cfg {
+	uint32_t                width;
+	uint32_t                height;
+	uint32_t                plane_stride;
+	uint32_t                slice_height;
+};
+
+/**
+ * struct cam_buf_io_cfg - Buffer io configuration for buffers
+ *
+ * @mem_handle:                 mem_handle array for the buffers.
+ * @offsets:                    offsets for each planes in the buffer
+ * @planes:                     per plane information
+ * @width:                      main plane width in pixel
+ * @height:                     main plane height in lines
+ * @format:                     format of the buffer
+ * @color_space:                color space for the buffer
+ * @color_pattern:              color pattern in the buffer
+ * @bpp:                        bit per pixel
+ * @rotation:                   rotation information for the buffer
+ * @resource_type:              resource type associated with the buffer
+ * @fence:                      fence handle
+ * @cmd_buf_index:              command buffer index to patch the buffer info
+ * @cmd_buf_offset:             offset within the command buffer to patch
+ * @flag:                       flags for extra information
+ * @direction:                  buffer direction: input or output
+ * @padding:                    padding for the structure
+ *
+ */
+struct cam_buf_io_cfg {
+	int32_t                         mem_handle[CAM_PACKET_MAX_PLANES];
+	uint32_t                        offsets[CAM_PACKET_MAX_PLANES];
+	struct cam_plane_cfg            planes[CAM_PACKET_MAX_PLANES];
+	uint32_t                        width;
+	uint32_t                        height;
+	uint32_t                        format;
+	uint32_t                        color_space;
+	uint32_t                        color_pattern;
+	uint32_t                        bpp;
+	uint32_t                        rotation;
+	uint32_t                        resource_type;
+	int32_t                         fence;
+	uint32_t                        cmd_buf_index;
+	uint32_t                        cmd_buf_offset;
+	uint32_t                        flag;
+	uint32_t                        direction;
+	uint32_t                        padding;
+};
+
+/**
+ * struct cam_cmd_buf_desc - Command buffer descriptor
+ *
+ * @mem_handle:                 command buffer handle
+ * @offset:                     command start offset
+ * @size:                       size of the command buffer in bytes
+ * @length:                     used memory in command buffer in bytes
+ * @type:                       type of the command buffer
+ * @meta_data:                  data type for private command buffer
+ *                              between UMD and KMD
+ *
+ */
+struct cam_cmd_buf_desc {
+	int32_t                 mem_handle;
+	uint32_t                offset;
+	uint32_t                size;
+	uint32_t                length;
+	uint32_t                type;
+	uint32_t                meta_data;
+};
+
+/**
+ * struct cam_packet_header - camera packet header
+ *
+ * @op_code:                    camera packet opcode
+ * @dev_type:                   camera packet device type
+ * @size:                       size of the camera packet in bytes
+ * @flags:                      flags for the camera packet
+ * @request_id:                 request id for this camera packet
+ *
+ */
+struct cam_packet_header {
+	uint32_t                op_code;
+	uint32_t                dev_type;
+	uint32_t                size;
+	uint32_t                flags;
+	uint64_t                request_id;
+};
+
+/**
+ * struct cam_patch_desc - Patch structure
+ *
+ * @dst_buf_hdl:                memory handle for the dest buffer
+ * @dst_offset:                 offset byte in the dest buffer
+ * @src_buf_hdl:                memory handle for the source buffer
+ * @src_offset:                 offset byte in the source buffer
+ *
+ */
+struct cam_patch_desc {
+	int32_t                 dst_buf_hdl;
+	uint32_t                dst_offset;
+	int32_t                 src_buf_hdl;
+	uint32_t                src_offset;
+};
+
+/**
+ * struct cam_packet - cam packet structure
+ *
+ * @header:                     camera packet header
+ * @cmd_buf_offset:             command buffer start offset
+ * @num_cmd_buf:                number of the command buffer in the packet
+ * @io_config_offset:           buffer io configuration start offset
+ * @num_io_configs:             number of the buffer io configurations
+ * @patch_offset:               patch offset for the patch structure
+ * @num_patches:                number of the patch structure
+ * @kmd_cmd_buf_index:          command buffer index which contains extra
+ *                                      space for the KMD buffer
+ * @kmd_cmd_buf_offset:         offset from the beginning of the command
+ *                                      buffer for KMD usage.
+ * @payload:                    camera packet payload
+ *
+ */
+struct cam_packet {
+	struct cam_packet_header        header;
+	uint32_t                        cmd_buf_offset;
+	uint32_t                        num_cmd_buf;
+	uint32_t                        io_configs_offset;
+	uint32_t                        num_io_configs;
+	uint32_t                        patch_offset;
+	uint32_t                        num_patches;
+	uint32_t                        kmd_cmd_buf_index;
+	uint32_t                        kmd_cmd_buf_offset;
+	uint64_t                        payload[1];
+
+};
+
+/* Release Device */
+/**
+ * struct cam_release_dev_cmd - control payload for release devices
+ *
+ * @session_handle:             session handle for the release
+ * @dev_handle:                 device handle for the release
+ */
+struct cam_release_dev_cmd {
+	int32_t                 session_handle;
+	int32_t                 dev_handle;
+};
+
+/* Start/Stop device */
+/**
+ * struct cam_start_stop_dev_cmd - control payload for start/stop device
+ *
+ * @session_handle:             session handle for the start/stop command
+ * @dev_handle:                 device handle for the start/stop command
+ *
+ */
+struct cam_start_stop_dev_cmd {
+	int32_t                 session_handle;
+	int32_t                 dev_handle;
+};
+
+/* Configure Device */
+/**
+ * struct cam_config_dev_cmd - command payload for configure device
+ *
+ * @session_handle:             session handle for the command
+ * @dev_handle:                 device handle for the command
+ * @offset:                     offset byte in the packet handle.
+ * @packet_handle:              packet memory handle for the actual packet:
+ *                                      struct cam_packet.
+ *
+ */
+struct cam_config_dev_cmd {
+	int32_t                 session_handle;
+	int32_t                 dev_handle;
+	uint64_t                offset;
+	uint64_t                packet_handle;
+};
+
+#endif /* __UAPI_CAM_DEFS_H__ */
diff --git a/include/uapi/media/cam_isp.h b/include/uapi/media/cam_isp.h
new file mode 100644
index 0000000..dc8268c
--- /dev/null
+++ b/include/uapi/media/cam_isp.h
@@ -0,0 +1,235 @@
+#ifndef __UAPI_CAM_ISP_H__
+#define __UAPI_CAM_ISP_H__
+
+#include "cam_defs.h"
+#include "cam_isp_vfe.h"
+#include "cam_isp_ife.h"
+
+
+/* ISP driver name */
+#define CAM_ISP_DEV_NAME                        "cam-isp"
+
+/* HW type */
+#define CAM_ISP_HW_BASE                         0
+#define CAM_ISP_HW_CSID                         1
+#define CAM_ISP_HW_VFE                          2
+#define CAM_ISP_HW_IFE                          3
+#define CAM_ISP_HW_ISPIF                        4
+#define CAM_ISP_HW_MAX                          5
+
+/* Color Pattern */
+#define CAM_ISP_PATTERN_BAYER_RGRGRG            0
+#define CAM_ISP_PATTERN_BAYER_GRGRGR            1
+#define CAM_ISP_PATTERN_BAYER_BGBGBG            2
+#define CAM_ISP_PATTERN_BAYER_GBGBGB            3
+#define CAM_ISP_PATTERN_YUV_YCBYCR              4
+#define CAM_ISP_PATTERN_YUV_YCRYCB              5
+#define CAM_ISP_PATTERN_YUV_CBYCRY              6
+#define CAM_ISP_PATTERN_YUV_CRYCBY              7
+#define CAM_ISP_PATTERN_MAX                     8
+
+/* Usage Type */
+#define CAM_ISP_RES_USAGE_SINGLE                0
+#define CAM_ISP_RES_USAGE_DUAL                  1
+#define CAM_ISP_RES_USAGE_MAX                   2
+
+/* Resource ID */
+#define CAM_ISP_RES_ID_PORT                     0
+#define CAM_ISP_RES_ID_CLK                      1
+#define CAM_ISP_RES_ID_MAX                      2
+
+/* Resource Type - Type of resource for the resource id
+ * defined in cam_isp_vfe.h, cam_isp_ife.h
+ */
+
+/* Lane Type in input resource for Port */
+#define CAM_ISP_LANE_TYPE_DPHY                  0
+#define CAM_ISP_LANE_TYPE_CPHY                  1
+#define CAM_ISP_LANE_TYPE_MAX                   2
+
+/* ISP Resurce Composite Group ID */
+#define CAM_ISP_RES_COMP_GROUP_NONE             0
+#define CAM_ISP_RES_COMP_GROUP_ID_0             1
+#define CAM_ISP_RES_COMP_GROUP_ID_1             2
+#define CAM_ISP_RES_COMP_GROUP_ID_2             3
+#define CAM_ISP_RES_COMP_GROUP_ID_3             4
+#define CAM_ISP_RES_COMP_GROUP_ID_4             5
+#define CAM_ISP_RES_COMP_GROUP_ID_5             6
+#define CAM_ISP_RES_COMP_GROUP_ID_MAX           6
+
+/* ISP packet opcode for ISP */
+#define CAM_ISP_PACKET_OP_BASE                  0
+#define CAM_ISP_PACKET_INIT_DEV                 1
+#define CAM_ISP_PACKET_UPDATE_DEV               2
+#define CAM_ISP_PACKET_OP_MAX                   3
+
+/* ISP packet meta_data type for command buffer */
+#define CAM_ISP_PACKET_META_BASE                0
+#define CAM_ISP_PACKET_META_LEFT                1
+#define CAM_ISP_PACKET_META_RIGHT               2
+#define CAM_ISP_PACKET_META_COMMON              3
+#define CAM_ISP_PACKET_META_DMI_LEFT            4
+#define CAM_ISP_PACKET_META_DMI_RIGHT           5
+#define CAM_ISP_PACKET_META_DMI_COMMON          6
+#define CAM_ISP_PACKET_META_CLOCK               7
+#define CAM_ISP_PACKET_META_CSID                8
+#define CAM_ISP_PACKET_META_MAX                 9
+
+
+/* Query devices */
+/**
+ * struct cam_isp_dev_cap_info - A cap info for particular hw type
+ *
+ * @hw_type:            Hardware type for the cap info
+ * @hw_version:         Hardware version
+ *
+ */
+struct cam_isp_dev_cap_info {
+	uint32_t              hw_type;
+	struct cam_hw_version hw_version;
+};
+
+/**
+ * struct cam_isp_query_cap_cmd - ISP query device capability payload
+ *
+ * @device_iommu:               returned iommu handles for device
+ * @cdm_iommu:                  returned iommu handles for cdm
+ * @num_dev:                    returned number of device capabilities
+ * @reserved:                   reserved field for alignment
+ * @dev_caps:                   returned device capability array
+ *
+ */
+struct cam_isp_query_cap_cmd {
+	struct cam_iommu_handle       device_iommu;
+	struct cam_iommu_handle       cdm_iommu;
+	int32_t                       num_dev;
+	uint32_t                      reserved;
+	struct cam_isp_dev_cap_info   dev_caps[CAM_ISP_HW_MAX];
+};
+
+/* Acquire Device */
+/**
+ * struct cam_isp_out_port_info - An output port resource info
+ *
+ * @res_type:                   output resource type defined in file
+ *                              cam_isp_vfe.h or cam_isp_ife.h
+ * @format:                     output format of the resource
+ * @wdith:                      output width in pixels
+ * @height:                     output height in lines
+ * @comp_grp_id:                composite group id for the resource.
+ * @split_point:                split point in pixels for the dual VFE.
+ * @secure_mode:                flag to tell if output should be run in secure
+ *                              mode or not. See cam_defs.h for definition
+ * @reserved:                   reserved field for alignment
+ *
+ */
+struct cam_isp_out_port_info {
+	uint32_t                res_type;
+	uint32_t                format;
+	uint32_t                width;
+	uint32_t                height;
+	uint32_t                comp_grp_id;
+	uint32_t                split_point;
+	uint32_t                secure_mode;
+	uint32_t                reserved;
+};
+
+/**
+ * struct cam_isp_in_port_info - An input port resource info
+ *
+ * @res_type:                   input resource type define in file
+ *                              cam_isp_vfe.h or cam_isp_ife.h
+ * @lane_type:                  lane type: c-phy or d-phy.
+ * @lane_num:                   active lane number
+ * @lane_cfg:                   lane configurations: 4 bits per lane
+ * @vc:                         input virtual channel number
+ * @dt:                         input data type number
+ * @format:                     input format
+ * @test_pattern:               test pattern for the testgen
+ * @usage_type:                 whether dual vfe is required
+ * @left_start:                 left input start offset in pixels
+ * @left_stop:                  left input stop offset in pixels
+ * @left_width:                 left input width in pixels
+ * @right_start:                right input start offset in pixels.
+ *                              Only for Dual VFE
+ * @right_stop:                 right input stop offset in pixels.
+ *                              Only for Dual VFE
+ * @right_width:                right input width in pixels.
+ *                              Only for dual VFE
+ * @line_start:                 top of the line number
+ * @line_stop:                  bottome of the line number
+ * @height:                     input height in lines
+ * @pixel_clk;                  sensor output clock
+ * @batch_size:                 batch size for HFR mode
+ * @reserved:                   reserved field for alignment
+ * @num_out_res:                number of the output resource associated
+ * @data:                       payload that contains the output resources
+ *
+ */
+struct cam_isp_in_port_info {
+	uint32_t                        res_type;
+	uint32_t                        lane_type;
+	uint32_t                        lane_num;
+	uint32_t                        lane_cfg;
+	uint32_t                        vc;
+	uint32_t                        dt;
+	uint32_t                        format;
+	uint32_t                        test_pattern;
+	uint32_t                        usage_type;
+	uint32_t                        left_start;
+	uint32_t                        left_stop;
+	uint32_t                        left_width;
+	uint32_t                        right_start;
+	uint32_t                        right_stop;
+	uint32_t                        right_width;
+	uint32_t                        line_start;
+	uint32_t                        line_stop;
+	uint32_t                        height;
+	uint32_t                        pixel_clk;
+	uint32_t                        batch_size;
+	uint32_t                        reserved;
+	uint32_t                        num_out_res;
+	struct cam_isp_out_port_info    data[1];
+};
+
+/**
+ * struct cam_isp_resource - A resource bundle
+ *
+ * @resoruce_id:                resource id for the resource bundle
+ * @length:                     length of the while resource blob
+ * @handle_type:                type of the resource handle
+ * @reserved:                   reserved field for alignment
+ * @res_hdl:                    resource handle that points to the
+ *                                     resource array;
+ *
+ */
+struct cam_isp_resource {
+	uint32_t                       resource_id;
+	uint32_t                       length;
+	uint32_t                       handle_type;
+	uint32_t                       reserved;
+	uint64_t                       res_hdl;
+};
+
+/**
+ * struct cam_isp_acquire_dev_cmd - control payload for acquire devices
+ *
+ * @session_handle:             session handle for the acquire command
+ * @dev_handle:                 device handle to be returned
+ * @num_resources:              number of the resources to be acquired
+ * @handle_type:                resource handle type:
+ *                                      1 = user poniter, 2 = mem handle
+ * @resources_hdl:              resource handle that refers to the actual
+ *                                      resource array. Each item in this
+ *                                      array is struct cam_isp_resource
+ *
+ */
+struct cam_isp_acquire_dev_cmd {
+	int32_t                 session_handle;
+	int32_t                 dev_handle;
+	uint32_t                num_resources;
+	uint32_t                handle_type;
+	uint64_t                resource_hdl;
+};
+
+#endif /* __UAPI_CAM_ISP_H__ */
diff --git a/include/uapi/media/cam_isp_ife.h b/include/uapi/media/cam_isp_ife.h
new file mode 100644
index 0000000..f5e7281
--- /dev/null
+++ b/include/uapi/media/cam_isp_ife.h
@@ -0,0 +1,39 @@
+#ifndef __UAPI_CAM_ISP_IFE_H__
+#define __UAPI_CAM_ISP_IFE_H__
+
+/* IFE output port resource type (global unique)*/
+#define CAM_ISP_IFE_OUT_RES_BASE               0x3000
+
+#define CAM_ISP_IFE_OUT_RES_FULL               (CAM_ISP_IFE_OUT_RES_BASE + 0)
+#define CAM_ISP_IFE_OUT_RES_DS4                (CAM_ISP_IFE_OUT_RES_BASE + 1)
+#define CAM_ISP_IFE_OUT_RES_DS16               (CAM_ISP_IFE_OUT_RES_BASE + 2)
+#define CAM_ISP_IFE_OUT_RES_RAW_DUMP           (CAM_ISP_IFE_OUT_RES_BASE + 3)
+#define CAM_ISP_IFE_OUT_RES_FD                 (CAM_ISP_IFE_OUT_RES_BASE + 4)
+#define CAM_ISP_IFE_OUT_RES_PDAF               (CAM_ISP_IFE_OUT_RES_BASE + 5)
+#define CAM_ISP_IFE_OUT_RES_RDI_0              (CAM_ISP_IFE_OUT_RES_BASE + 6)
+#define CAM_ISP_IFE_OUT_RES_RDI_1              (CAM_ISP_IFE_OUT_RES_BASE + 7)
+#define CAM_ISP_IFE_OUT_RES_RDI_2              (CAM_ISP_IFE_OUT_RES_BASE + 8)
+#define CAM_ISP_IFE_OUT_RES_RDI_3              (CAM_ISP_IFE_OUT_RES_BASE + 9)
+#define CAM_ISP_IFE_OUT_RES_STATS_HDR_BE       (CAM_ISP_IFE_OUT_RES_BASE + 10)
+#define CAM_ISP_IFE_OUT_RES_STATS_HDR_BHIST    (CAM_ISP_IFE_OUT_RES_BASE + 11)
+#define CAM_ISP_IFE_OUT_RES_STATS_TL_BG        (CAM_ISP_IFE_OUT_RES_BASE + 12)
+#define CAM_ISP_IFE_OUT_RES_STATS_BF           (CAM_ISP_IFE_OUT_RES_BASE + 13)
+#define CAM_ISP_IFE_OUT_RES_STATS_AWB_BG       (CAM_ISP_IFE_OUT_RES_BASE + 14)
+#define CAM_ISP_IFE_OUT_RES_STATS_BHIST        (CAM_ISP_IFE_OUT_RES_BASE + 15)
+#define CAM_ISP_IFE_OUT_RES_STATS_RS           (CAM_ISP_IFE_OUT_RES_BASE + 16)
+#define CAM_ISP_IFE_OUT_RES_STATS_CS           (CAM_ISP_IFE_OUT_RES_BASE + 17)
+#define CAM_ISP_IFE_OUT_RES_STATS_IHIST        (CAM_ISP_IFE_OUT_RES_BASE + 18)
+#define CAM_ISP_IFE_OUT_RES_MAX                (CAM_ISP_IFE_OUT_RES_BASE + 19)
+
+
+/* IFE input port resource type (global unique) */
+#define CAM_ISP_IFE_IN_RES_BASE                 0x4000
+
+#define CAM_ISP_IFE_IN_RES_TPG                 (CAM_ISP_IFE_IN_RES_BASE + 0)
+#define CAM_ISP_IFE_IN_RES_PHY_0               (CAM_ISP_IFE_IN_RES_BASE + 1)
+#define CAM_ISP_IFE_IN_RES_PHY_1               (CAM_ISP_IFE_IN_RES_BASE + 2)
+#define CAM_ISP_IFE_IN_RES_PHY_2               (CAM_ISP_IFE_IN_RES_BASE + 3)
+#define CAM_ISP_IFE_IN_RES_PHY_3               (CAM_ISP_IFE_IN_RES_BASE + 4)
+#define CAM_ISP_IFE_IN_RES_MAX                 (CAM_ISP_IFE_IN_RES_BASE + 5)
+
+#endif /* __UAPI_CAM_ISP_IFE_H__ */
diff --git a/include/uapi/media/cam_isp_vfe.h b/include/uapi/media/cam_isp_vfe.h
new file mode 100644
index 0000000..e48db2f
--- /dev/null
+++ b/include/uapi/media/cam_isp_vfe.h
@@ -0,0 +1,44 @@
+#ifndef __UAPI_CAM_ISP_VFE_H__
+#define __UAPI_CAM_ISP_VFE_H__
+
+/* VFE output port resource type  (global unique)  */
+#define CAM_ISP_VFE_OUT_RES_BASE               0x1000
+
+#define CAM_ISP_VFE_OUT_RES_ENC                (CAM_ISP_VFE_OUT_RES_BASE + 0)
+#define CAM_ISP_VFE_OUT_RES_VIEW               (CAM_ISP_VFE_OUT_RES_BASE + 1)
+#define CAM_ISP_VFE_OUT_RES_VID                (CAM_ISP_VFE_OUT_RES_BASE + 2)
+#define CAM_ISP_VFE_OUT_RES_RDI_0              (CAM_ISP_VFE_OUT_RES_BASE + 3)
+#define CAM_ISP_VFE_OUT_RES_RDI_1              (CAM_ISP_VFE_OUT_RES_BASE + 4)
+#define CAM_ISP_VFE_OUT_RES_RDI_2              (CAM_ISP_VFE_OUT_RES_BASE + 5)
+#define CAM_ISP_VFE_OUT_RES_RDI_3              (CAM_ISP_VFE_OUT_RES_BASE + 6)
+#define CAM_ISP_VFE_OUT_RES_STATS_AEC          (CAM_ISP_VFE_OUT_RES_BASE + 7)
+#define CAM_ISP_VFE_OUT_RES_STATS_AF           (CAM_ISP_VFE_OUT_RES_BASE + 8)
+#define CAM_ISP_VFE_OUT_RES_STATS_AWB          (CAM_ISP_VFE_OUT_RES_BASE + 9)
+#define CAM_ISP_VFE_OUT_RES_STATS_RS           (CAM_ISP_VFE_OUT_RES_BASE + 10)
+#define CAM_ISP_VFE_OUT_RES_STATS_CS           (CAM_ISP_VFE_OUT_RES_BASE + 11)
+#define CAM_ISP_VFE_OUT_RES_STATS_IHIST        (CAM_ISP_VFE_OUT_RES_BASE + 12)
+#define CAM_ISP_VFE_OUT_RES_STATS_SKIN         (CAM_ISP_VFE_OUT_RES_BASE + 13)
+#define CAM_ISP_VFE_OUT_RES_STATS_BG           (CAM_ISP_VFE_OUT_RES_BASE + 14)
+#define CAM_ISP_VFE_OUT_RES_STATS_BF           (CAM_ISP_VFE_OUT_RES_BASE + 15)
+#define CAM_ISP_VFE_OUT_RES_STATS_BE           (CAM_ISP_VFE_OUT_RES_BASE + 16)
+#define CAM_ISP_VFE_OUT_RES_STATS_BHIST        (CAM_ISP_VFE_OUT_RES_BASE + 17)
+#define CAM_ISP_VFE_OUT_RES_STATS_BF_SCALE     (CAM_ISP_VFE_OUT_RES_BASE + 18)
+#define CAM_ISP_VFE_OUT_RES_STATS_HDR_BE       (CAM_ISP_VFE_OUT_RES_BASE + 19)
+#define CAM_ISP_VFE_OUT_RES_STATS_HDR_BHIST    (CAM_ISP_VFE_OUT_RES_BASE + 20)
+#define CAM_ISP_VFE_OUT_RES_STATS_AEC_BG       (CAM_ISP_VFE_OUT_RES_BASE + 21)
+#define CAM_ISP_VFE_OUT_RES_CAMIF_RAW          (CAM_ISP_VFE_OUT_RES_BASE + 22)
+#define CAM_ISP_VFE_OUT_RES_IDEAL_RAW          (CAM_ISP_VFE_OUT_RES_BASE + 23)
+#define CAM_ISP_VFE_OUT_RES_MAX                (CAM_ISP_VFE_OUT_RES_BASE + 24)
+
+/* VFE input port_ resource type (global unique) */
+#define CAM_ISP_VFE_IN_RES_BASE                0x2000
+
+#define CAM_ISP_VFE_IN_RES_TPG                 (CAM_ISP_VFE_IN_RES_BASE + 0)
+#define CAM_ISP_VFE_IN_RES_PHY_0               (CAM_ISP_VFE_IN_RES_BASE + 1)
+#define CAM_ISP_VFE_IN_RES_PHY_1               (CAM_ISP_VFE_IN_RES_BASE + 2)
+#define CAM_ISP_VFE_IN_RES_PHY_2               (CAM_ISP_VFE_IN_RES_BASE + 3)
+#define CAM_ISP_VFE_IN_RES_PHY_3               (CAM_ISP_VFE_IN_RES_BASE + 4)
+#define CAM_ISP_VFE_IN_RES_FE                  (CAM_ISP_VFE_IN_RES_BASE + 5)
+#define CAM_ISP_VFE_IN_RES_MAX                 (CAM_ISP_VFE_IN_RES_BASE + 6)
+
+#endif /* __UAPI_CAM_ISP_VFE_H__ */
diff --git a/include/uapi/media/cam_req_mgr.h b/include/uapi/media/cam_req_mgr.h
new file mode 100644
index 0000000..18bd04a
--- /dev/null
+++ b/include/uapi/media/cam_req_mgr.h
@@ -0,0 +1,327 @@
+#ifndef __UAPI_LINUX_CAM_REQ_MGR_H
+#define __UAPI_LINUX_CAM_REQ_MGR_H
+
+#include <linux/videodev2.h>
+#include <linux/types.h>
+#include <linux/ioctl.h>
+#include <linux/media.h>
+#include <media/cam_defs.h>
+
+#define CAM_REQ_MGR_VNODE_NAME "cam-req-mgr-devnode"
+
+#define CAM_DEVICE_TYPE_BASE    (MEDIA_ENT_F_OLD_BASE)
+#define CAM_VNODE_DEVICE_TYPE   (CAM_DEVICE_TYPE_BASE)
+#define CAM_SENSOR_DEVICE_TYPE  (CAM_DEVICE_TYPE_BASE + 1)
+#define CAM_IFE_DEVICE_TYPE     (CAM_DEVICE_TYPE_BASE + 2)
+#define CAM_ICP_DEVICE_TYPE     (CAM_DEVICE_TYPE_BASE + 3)
+#define CAM_LRME_DEVICE_TYPE    (CAM_DEVICE_TYPE_BASE + 4)
+#define CAM_JPEG_DEVICE_TYPE    (CAM_DEVICE_TYPE_BASE + 5)
+#define CAM_FD_DEVICE_TYPE      (CAM_DEVICE_TYPE_BASE + 6)
+#define CAM_CPAS_DEVICE_TYPE    (CAM_DEVICE_TYPE_BASE + 7)
+#define CAM_CSIPHY_DEVICE_TYPE  (CAM_DEVICE_TYPE_BASE + 8)
+
+/* cam_req_mgr hdl info */
+#define CAM_REQ_MGR_HDL_IDX_POS           8
+#define CAM_REQ_MGR_HDL_IDX_MASK          ((1 << CAM_REQ_MGR_HDL_IDX_POS) - 1)
+#define CAM_REQ_MGR_GET_HDL_IDX(hdl)      (hdl & CAM_REQ_MGR_HDL_IDX_MASK)
+
+/**
+ * Max handles supported by cam_req_mgr
+ * It includes both session and device handles
+ */
+#define CAM_REQ_MGR_MAX_HANDLES           64
+#define MAX_LINKS_PER_SESSION             2
+
+/* V4L event type which user space will subscribe to */
+#define V4L_EVENT_CAM_REQ_MGR_EVENT       (V4L2_EVENT_PRIVATE_START + 0)
+
+/* Specific event ids to get notified in user space */
+#define V4L_EVENT_CAM_REQ_MGR_SOF         0
+#define V4L_EVENT_CAM_REQ_MGR_ERROR       1
+#define V4L_EVENT_CAM_REQ_MGR_MAX         2
+
+/**
+ * Request Manager : flush_type
+ * @CAM_REQ_MGR_FLUSH_TYPE_ALL: Req mgr will remove all the pending
+ * requests from input/processing queue.
+ * @CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ: Req mgr will remove only particular
+ * request id from input/processing queue.
+ * @CAM_REQ_MGR_FLUSH_TYPE_MAX: Max number of the flush type
+ * @opcode: CAM_REQ_MGR_FLUSH_REQ
+ */
+#define CAM_REQ_MGR_FLUSH_TYPE_ALL          0
+#define CAM_REQ_MGR_FLUSH_TYPE_CANCEL_REQ   1
+#define CAM_REQ_MGR_FLUSH_TYPE_MAX          2
+
+/**
+ * struct cam_req_mgr_event_data
+ * @session_hdl: session handle
+ * @link_hdl: link handle
+ * @frame_id: frame id
+ * @reserved: reserved for 64 bit aligngment
+ * @req_id: request id
+ * @tv_sec: timestamp in seconds
+ * @tv_usec: timestamp in micro seconds
+ */
+struct cam_req_mgr_event_data {
+	int32_t   session_hdl;
+	int32_t   link_hdl;
+	int32_t   frame_id;
+	int32_t   reserved;
+	int64_t   req_id;
+	uint64_t  tv_sec;
+	uint64_t  tv_usec;
+};
+
+/**
+ * struct cam_req_mgr_session_info
+ * @session_hdl: In/Output param - session_handle
+ * @opcode1: CAM_REQ_MGR_CREATE_SESSION
+ * @opcode2: CAM_REQ_MGR_DESTROY_SESSION
+ */
+struct cam_req_mgr_session_info {
+	int32_t session_hdl;
+	int32_t reserved;
+};
+
+/**
+ * struct cam_req_mgr_link_info
+ * @session_hdl: Input param - Identifier for CSL session
+ * @num_devices: Input Param - Num of devices to be linked
+ * @dev_hdls: Input param - List of device handles to be linked
+ * @link_hdl: Output Param -Identifier for link
+ * @opcode: CAM_REQ_MGR_LINK
+ */
+struct cam_req_mgr_link_info {
+	int32_t session_hdl;
+	uint32_t num_devices;
+	int32_t dev_hdls[CAM_REQ_MGR_MAX_HANDLES];
+	int32_t link_hdl;
+};
+
+/**
+ * struct cam_req_mgr_unlink_info
+ * @session_hdl: input param - session handle
+ * @link_hdl: input param - link handle
+ * @opcode: CAM_REQ_MGR_UNLINK
+ */
+struct cam_req_mgr_unlink_info {
+	int32_t session_hdl;
+	int32_t link_hdl;
+};
+
+/**
+ * struct cam_req_mgr_flush_info
+ * @brief: User can tell drivers to flush a particular request id or
+ * flush all requests from its pending processing queue. Flush is a
+ * blocking call and driver shall ensure all requests are flushed
+ * before returning.
+ * @session_hdl: Input param - Identifier for CSL session
+ * @link_hdl: Input Param -Identifier for link
+ * @flush_type: User can cancel a particular req id or can flush
+ * all requests in queue
+ * @reserved: reserved for 64 bit aligngment
+ * @req_id: field is valid only if flush type is cancel request
+ * for flush all this field value is not considered.
+ * @opcode: CAM_REQ_MGR_FLUSH_REQ
+ */
+struct cam_req_mgr_flush_info {
+	int32_t session_hdl;
+	int32_t link_hdl;
+	uint32_t flush_type;
+	uint32_t reserved;
+	int64_t req_id;
+};
+
+/** struct cam_req_mgr_sched_info
+ * @session_hdl: Input param - Identifier for CSL session
+ * @link_hdl: Input Param -Identifier for link
+ * inluding itself.
+ * @bubble_enable: Input Param - Cam req mgr will do bubble recovery if this
+ * flag is set.
+ * @reserved: reserved field for alignment
+ * @req_id: Input Param - Request Id from which all requests will be flushed
+ */
+struct cam_req_mgr_sched_request {
+	int32_t session_hdl;
+	int32_t link_hdl;
+	int32_t bubble_enable;
+	int32_t reserved;
+	int64_t req_id;
+};
+
+/**
+ * struct cam_req_mgr_sync_mode
+ * @session_hdl:         Input param - Identifier for CSL session
+ * @sync_enable:         Input Param -Enable sync mode or disable
+ * @num_links:           Input Param - Num of links in sync mode (Valid only
+ *                             when sync_enable is TRUE)
+ * @link_hdls:           Input Param - Array of link handles to be in sync mode
+ *                             (Valid only when sync_enable is TRUE)
+ * @master_link_hdl:     Input Param - To dictate which link's SOF drives system
+ *                             (Valid only when sync_enable is TRUE)
+ *
+ * @opcode: CAM_REQ_MGR_SYNC_MODE
+ */
+struct cam_req_mgr_sync_mode {
+	int32_t session_hdl;
+	int32_t sync_enable;
+	int32_t num_links;
+	int32_t link_hdls[MAX_LINKS_PER_SESSION];
+	int32_t master_link_hdl;
+	int32_t reserved;
+};
+
+/**
+ * cam_req_mgr specific opcode ids
+ */
+#define CAM_REQ_MGR_CREATE_DEV_NODES            (CAM_COMMON_OPCODE_MAX + 1)
+#define CAM_REQ_MGR_CREATE_SESSION              (CAM_COMMON_OPCODE_MAX + 2)
+#define CAM_REQ_MGR_DESTROY_SESSION             (CAM_COMMON_OPCODE_MAX + 3)
+#define CAM_REQ_MGR_LINK                        (CAM_COMMON_OPCODE_MAX + 4)
+#define CAM_REQ_MGR_UNLINK                      (CAM_COMMON_OPCODE_MAX + 5)
+#define CAM_REQ_MGR_SCHED_REQ                   (CAM_COMMON_OPCODE_MAX + 6)
+#define CAM_REQ_MGR_FLUSH_REQ                   (CAM_COMMON_OPCODE_MAX + 7)
+#define CAM_REQ_MGR_SYNC_MODE                   (CAM_COMMON_OPCODE_MAX + 8)
+#define CAM_REQ_MGR_ALLOC_BUF                   (CAM_COMMON_OPCODE_MAX + 9)
+#define CAM_REQ_MGR_MAP_BUF                     (CAM_COMMON_OPCODE_MAX + 10)
+#define CAM_REQ_MGR_RELEASE_BUF                 (CAM_COMMON_OPCODE_MAX + 11)
+#define CAM_REQ_MGR_CACHE_OPS                   (CAM_COMMON_OPCODE_MAX + 12)
+#define CAM_REQ_MGR_GET_MMU_HDLS_DEBUG          (CAM_COMMON_OPCODE_MAX + 13)
+#define CAM_REQ_MGR_GET_IO_BUF_DEBUG            (CAM_COMMON_OPCODE_MAX + 14)
+#define CAM_REQ_MGR_GET_KMD_BUF_DEBUG           (CAM_COMMON_OPCODE_MAX + 15)
+/* end of cam_req_mgr opcodes */
+
+#define CAM_MEM_FLAG_HW_READ_WRITE              (1<<0)
+#define CAM_MEM_FLAG_HW_READ_ONLY               (1<<1)
+#define CAM_MEM_FLAG_HW_WRITE_ONLY              (1<<2)
+#define CAM_MEM_FLAG_KMD_ACCESS                 (1<<3)
+#define CAM_MEM_FLAG_UMD_ACCESS                 (1<<4)
+#define CAM_MEM_FLAG_PROTECTED_MODE             (1<<5)
+#define CAM_MEM_FLAG_CMD_BUF_TYPE               (1<<6)
+#define CAM_MEM_FLAG_PIXEL_BUF_TYPE             (1<<7)
+#define CAM_MEM_FLAG_STATS_BUF_TYPE             (1<<8)
+#define CAM_MEM_FLAG_PACKET_BUF_TYPE            (1<<9)
+#define CAM_MEM_FLAG_CACHE                      (1<<10)
+
+#define CAM_MEM_MMU_MAX_HANDLE                  16
+
+/* Maximum allowed buffers in existence */
+#define CAM_MEM_BUFQ_MAX                        1024
+
+#define CAM_MEM_MGR_HDL_IDX_SIZE                16
+#define CAM_MEM_MGR_HDL_FD_SIZE                 16
+#define CAM_MEM_MGR_HDL_IDX_END_POS             16
+#define CAM_MEM_MGR_HDL_FD_END_POS              32
+
+#define CAM_MEM_MGR_HDL_IDX_MASK      ((1 << CAM_MEM_MGR_HDL_IDX_SIZE) - 1)
+
+#define GET_MEM_HANDLE(idx, fd) \
+	((idx << (CAM_MEM_MGR_HDL_IDX_END_POS - CAM_MEM_MGR_HDL_IDX_SIZE)) | \
+	(fd << (CAM_MEM_MGR_HDL_FD_END_POS - CAM_MEM_MGR_HDL_FD_SIZE))) \
+
+#define CAM_MEM_MGR_GET_HDL_IDX(hdl) (hdl & CAM_MEM_MGR_HDL_IDX_MASK)
+
+/**
+ * memory allocation type
+ */
+#define CAM_MEM_DMA_NONE                        0
+#define CAM_MEM_DMA_BIDIRECTIONAL               1
+#define CAM_MEM_DMA_TO_DEVICE                   2
+#define CAM_MEM_DMA_FROM_DEVICE                 3
+
+
+/**
+ * memory cache operation
+ */
+#define CAM_MEM_CLEAN_CACHE                     1
+#define CAM_MEM_INV_CACHE                       2
+#define CAM_MEM_CLEAN_INV_CACHE                 3
+
+
+/**
+ * struct cam_mem_alloc_out_params
+ * @buf_handle: buffer handle
+ * @fd: output buffer file descriptor
+ * @vaddr: virtual address pointer
+ */
+struct cam_mem_alloc_out_params {
+	uint32_t buf_handle;
+	int32_t fd;
+	uint64_t vaddr;
+};
+
+/**
+ * struct cam_mem_map_out_params
+ * @buf_handle: buffer handle
+ * @reserved: reserved for future
+ * @vaddr: virtual address pointer
+ */
+struct cam_mem_map_out_params {
+	uint32_t buf_handle;
+	uint32_t reserved;
+	uint64_t vaddr;
+};
+
+/**
+ * struct cam_mem_mgr_alloc_cmd
+ * @len: size of buffer to allocate
+ * @align: alignment of the buffer
+ * @mmu_hdls: array of mmu handles
+ * @num_hdl: number of handles
+ * @flags: flags of the buffer
+ * @out: out params
+ */
+/* CAM_REQ_MGR_ALLOC_BUF */
+struct cam_mem_mgr_alloc_cmd {
+	uint64_t len;
+	uint64_t align;
+	int32_t mmu_hdls[CAM_MEM_MMU_MAX_HANDLE];
+	uint32_t num_hdl;
+	uint32_t flags;
+	struct cam_mem_alloc_out_params out;
+};
+
+/**
+ * struct cam_mem_mgr_map_cmd
+ * @mmu_hdls: array of mmu handles
+ * @num_hdl: number of handles
+ * @flags: flags of the buffer
+ * @fd: output buffer file descriptor
+ * @reserved: reserved field
+ * @out: out params
+ */
+
+/* CAM_REQ_MGR_MAP_BUF */
+struct cam_mem_mgr_map_cmd {
+	int32_t mmu_hdls[CAM_MEM_MMU_MAX_HANDLE];
+	uint32_t num_hdl;
+	uint32_t flags;
+	int32_t fd;
+	uint32_t reserved;
+	struct cam_mem_map_out_params out;
+};
+
+/**
+ * struct cam_mem_mgr_map_cmd
+ * @buf_handle: buffer handle
+ * @reserved: reserved field
+ */
+/* CAM_REQ_MGR_RELEASE_BUF */
+struct cam_mem_mgr_release_cmd {
+	int32_t buf_handle;
+	uint32_t reserved;
+};
+
+/**
+ * struct cam_mem_mgr_map_cmd
+ * @buf_handle: buffer handle
+ * @ops: cache operations
+ */
+/* CAM_REQ_MGR_CACHE_OPS */
+struct cam_mem_cache_ops_cmd {
+	int32_t buf_handle;
+	uint32_t mem_cache_ops;
+};
+
+#endif /* __UAPI_LINUX_CAM_REQ_MGR_H */
diff --git a/include/uapi/media/msm_media_info.h b/include/uapi/media/msm_media_info.h
new file mode 100644
index 0000000..746eee6
--- /dev/null
+++ b/include/uapi/media/msm_media_info.h
@@ -0,0 +1,1054 @@
+#ifndef __MEDIA_INFO_H__
+#define __MEDIA_INFO_H__
+
+#ifndef MSM_MEDIA_ALIGN
+#define MSM_MEDIA_ALIGN(__sz, __align) (((__align) & ((__align) - 1)) ?\
+	((((__sz) + (__align) - 1) / (__align)) * (__align)) :\
+	(((__sz) + (__align) - 1) & (~((__align) - 1))))
+#endif
+
+#ifndef MSM_MEDIA_ROUNDUP
+#define MSM_MEDIA_ROUNDUP(__sz, __r) (((__sz) + ((__r) - 1)) / (__r))
+#endif
+
+#ifndef MSM_MEDIA_MAX
+#define MSM_MEDIA_MAX(__a, __b) ((__a) > (__b)?(__a):(__b))
+#endif
+
+enum color_fmts {
+	/* Venus NV12:
+	 * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+	 * by an interleaved U/V plane containing 8 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * U V U V U V U V U V U V . . . .  ^
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Buffer size alignment
+	 *
+	 * Y_Stride : Width aligned to 128
+	 * UV_Stride : Width aligned to 128
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * Extradata: Arbitrary (software-imposed) padding
+	 * Total size = align((Y_Stride * Y_Scanlines
+	 *          + UV_Stride * UV_Scanlines
+	 *          + max(Extradata, Y_Stride * 8), 4096)
+	 */
+	COLOR_FMT_NV12,
+
+	/* Venus NV21:
+	 * YUV 4:2:0 image with a plane of 8 bit Y samples followed
+	 * by an interleaved V/U plane containing 8 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 * V U V U V U V U V U V U . . . .  ^
+	 * V U V U V U V U V U V U . . . .  |
+	 * V U V U V U V U V U V U . . . .  |
+	 * V U V U V U V U V U V U . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  --> Padding & Buffer size alignment
+	 *
+	 * Y_Stride : Width aligned to 128
+	 * UV_Stride : Width aligned to 128
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * Extradata: Arbitrary (software-imposed) padding
+	 * Total size = align((Y_Stride * Y_Scanlines
+	 *          + UV_Stride * UV_Scanlines
+	 *          + max(Extradata, Y_Stride * 8), 4096)
+	 */
+	COLOR_FMT_NV21,
+	/* Venus NV12_MVTB:
+	 * Two YUV 4:2:0 images/views one after the other
+	 * in a top-bottom layout, same as NV12
+	 * with a plane of 8 bit Y samples followed
+	 * by an interleaved U/V plane containing 8 bit 2x2 subsampled
+	 * colour difference samples.
+	 *
+	 *
+	 * <-------- Y/UV_Stride -------->
+	 * <------- Width ------->
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^               ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |               |
+	 * . . . . . . . . . . . . . . . .              |             View_1
+	 * . . . . . . . . . . . . . . . .              |               |
+	 * . . . . . . . . . . . . . . . .              |               |
+	 * . . . . . . . . . . . . . . . .              V               |
+	 * U V U V U V U V U V U V . . . .  ^                           |
+	 * U V U V U V U V U V U V . . . .  |                           |
+	 * U V U V U V U V U V U V . . . .  |                           |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines                |
+	 * . . . . . . . . . . . . . . . .  |                           |
+	 * . . . . . . . . . . . . . . . .  V                           V
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  ^           ^               ^
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  Height      |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |          Y_Scanlines      |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  |           |               |
+	 * Y Y Y Y Y Y Y Y Y Y Y Y . . . .  V           |               |
+	 * . . . . . . . . . . . . . . . .              |             View_2
+	 * . . . . . . . . . . . . . . . .              |               |
+	 * . . . . . . . . . . . . . . . .              |               |
+	 * . . . . . . . . . . . . . . . .              V               |
+	 * U V U V U V U V U V U V . . . .  ^                           |
+	 * U V U V U V U V U V U V . . . .  |                           |
+	 * U V U V U V U V U V U V . . . .  |                           |
+	 * U V U V U V U V U V U V . . . .  UV_Scanlines                |
+	 * . . . . . . . . . . . . . . . .  |                           |
+	 * . . . . . . . . . . . . . . . .  V                           V
+	 * . . . . . . . . . . . . . . . .  --> Buffer size alignment
+	 *
+	 * Y_Stride : Width aligned to 128
+	 * UV_Stride : Width aligned to 128
+	 * Y_Scanlines: Height aligned to 32
+	 * UV_Scanlines: Height/2 aligned to 16
+	 * View_1 begin at: 0 (zero)
+	 * View_2 begin at: Y_Stride * Y_Scanlines + UV_Stride * UV_Scanlines
+	 * Extradata: Arbitrary (software-imposed) padding
+	 * Total size = align((2*(Y_Stride * Y_Scanlines)
+	 *          + 2*(UV_Stride * UV_Scanlines) + Extradata), 4096)
+	 */
+	COLOR_FMT_NV12_MVTB,
+	/* Venus NV12 UBWC:
+	 * Compressed Macro-tile format for NV12.
+	 * Contains 4 planes in the following order -
+	 * (A) Y_Meta_Plane
+	 * (B) Y_UBWC_Plane
+	 * (C) UV_Meta_Plane
+	 * (D) UV_UBWC_Plane
+	 *
+	 * Y_Meta_Plane consists of meta information to decode compressed
+	 * tile data in Y_UBWC_Plane.
+	 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+	 * UBWC decoder block will use the Y_Meta_Plane data together with
+	 * Y_UBWC_Plane data to produce loss-less uncompressed 8 bit Y samples.
+	 *
+	 * UV_Meta_Plane consists of meta information to decode compressed
+	 * tile data in UV_UBWC_Plane.
+	 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+	 * UBWC decoder block will use UV_Meta_Plane data together with
+	 * UV_UBWC_Plane data to produce loss-less uncompressed 8 bit 2x2
+	 * subsampled color difference samples.
+	 *
+	 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+	 * and randomly accessible. There is no dependency between tiles.
+	 *
+	 * <----- Y_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <--Compressed tile Y Stride--->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----- UV_Meta_Stride ---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <--Compressed tile UV Stride--->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 * Y_Stride = align(Width, 128)
+	 * UV_Stride = align(Width, 128)
+	 * Y_Scanlines = align(Height, 32)
+	 * UV_Scanlines = align(Height/2, 16)
+	 * Y_UBWC_Plane_size = align(Y_Stride * Y_Scanlines, 4096)
+	 * UV_UBWC_Plane_size = align(UV_Stride * UV_Scanlines, 4096)
+	 * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16)
+	 * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096)
+	 * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
+	 * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align( Y_UBWC_Plane_size + UV_UBWC_Plane_size +
+	 *           Y_Meta_Plane_size + UV_Meta_Plane_size
+	 *           + max(Extradata, Y_Stride * 48), 4096)
+	 */
+	COLOR_FMT_NV12_UBWC,
+	/* Venus NV12 10-bit UBWC:
+	 * Compressed Macro-tile format for NV12.
+	 * Contains 4 planes in the following order -
+	 * (A) Y_Meta_Plane
+	 * (B) Y_UBWC_Plane
+	 * (C) UV_Meta_Plane
+	 * (D) UV_UBWC_Plane
+	 *
+	 * Y_Meta_Plane consists of meta information to decode compressed
+	 * tile data in Y_UBWC_Plane.
+	 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+	 * UBWC decoder block will use the Y_Meta_Plane data together with
+	 * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples.
+	 *
+	 * UV_Meta_Plane consists of meta information to decode compressed
+	 * tile data in UV_UBWC_Plane.
+	 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+	 * UBWC decoder block will use UV_Meta_Plane data together with
+	 * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2
+	 * subsampled color difference samples.
+	 *
+	 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+	 * and randomly accessible. There is no dependency between tiles.
+	 *
+	 * <----- Y_Meta_Stride ----->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <--Compressed tile Y Stride--->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----- UV_Meta_Stride ---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <--Compressed tile UV Stride--->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 *
+	 * Y_Stride = align(Width * 4/3, 128)
+	 * UV_Stride = align(Width * 4/3, 128)
+	 * Y_Scanlines = align(Height, 32)
+	 * UV_Scanlines = align(Height/2, 16)
+	 * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096)
+	 * UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096)
+	 * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16)
+	 * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096)
+	 * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
+	 * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size +
+	 *           Y_Meta_Plane_size + UV_Meta_Plane_size
+	 *           + max(Extradata, Y_Stride * 48), 4096)
+	 */
+	COLOR_FMT_NV12_BPP10_UBWC,
+	/* Venus RGBA8888 format:
+	 * Contains 1 plane in the following order -
+	 * (A) RGBA plane
+	 *
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 4, 128)
+	 * RGB_Scanlines = align(Height, 32)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align(RGB_Plane_size + Extradata, 4096)
+	 */
+	COLOR_FMT_RGBA8888,
+	/* Venus RGBA8888 UBWC format:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGBA plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 4, 128)
+	 * RGB_Scanlines = align(Height, 32)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *		RGB_Meta_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size +
+	 *		Extradata, 4096)
+	 */
+	COLOR_FMT_RGBA8888_UBWC,
+	/* Venus RGBA1010102 UBWC format:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGBA plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 4, 256)
+	 * RGB_Scanlines = align(Height, 16)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *		RGB_Meta_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size +
+	 *		Extradata, 4096)
+	 */
+	COLOR_FMT_RGBA1010102_UBWC,
+	/* Venus RGB565 UBWC format:
+	 * Contains 2 planes in the following order -
+	 * (A) Meta plane
+	 * (B) RGB plane
+	 *
+	 * <--- RGB_Meta_Stride ---->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |       Meta_RGB_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <-------- RGB_Stride -------->
+	 * <------- Width ------->
+	 * R R R R R R R R R R R R . . . .  ^           ^
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  Height      |
+	 * R R R R R R R R R R R R . . . .  |       RGB_Scanlines
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  |           |
+	 * R R R R R R R R R R R R . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .    -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 *
+	 * RGB_Stride = align(Width * 2, 128)
+	 * RGB_Scanlines = align(Height, 16)
+	 * RGB_Plane_size = align(RGB_Stride * RGB_Scanlines, 4096)
+	 * RGB_Meta_Stride = align(roundup(Width, RGB_TileWidth), 64)
+	 * RGB_Meta_Scanline = align(roundup(Height, RGB_TileHeight), 16)
+	 * RGB_Meta_Plane_size = align(RGB_Meta_Stride *
+	 *		RGB_Meta_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align(RGB_Meta_Plane_size + RGB_Plane_size +
+	 *		Extradata, 4096)
+	 */
+	COLOR_FMT_RGB565_UBWC,
+	/* P010 UBWC:
+	 * Compressed Macro-tile format for NV12.
+	 * Contains 4 planes in the following order -
+	 * (A) Y_Meta_Plane
+	 * (B) Y_UBWC_Plane
+	 * (C) UV_Meta_Plane
+	 * (D) UV_UBWC_Plane
+	 *
+	 * Y_Meta_Plane consists of meta information to decode compressed
+	 * tile data in Y_UBWC_Plane.
+	 * Y_UBWC_Plane consists of Y data in compressed macro-tile format.
+	 * UBWC decoder block will use the Y_Meta_Plane data together with
+	 * Y_UBWC_Plane data to produce loss-less uncompressed 10 bit Y samples.
+	 *
+	 * UV_Meta_Plane consists of meta information to decode compressed
+	 * tile data in UV_UBWC_Plane.
+	 * UV_UBWC_Plane consists of UV data in compressed macro-tile format.
+	 * UBWC decoder block will use UV_Meta_Plane data together with
+	 * UV_UBWC_Plane data to produce loss-less uncompressed 10 bit 2x2
+	 * subsampled color difference samples.
+	 *
+	 * Each tile in Y_UBWC_Plane/UV_UBWC_Plane is independently decodable
+	 * and randomly accessible. There is no dependency between tiles.
+	 *
+	 * <----- Y_Meta_Stride ----->
+	 * <-------- Width ------>
+	 * M M M M M M M M M M M M . .      ^           ^
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      Height      |
+	 * M M M M M M M M M M M M . .      |         Meta_Y_Scanlines
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      |           |
+	 * M M M M M M M M M M M M . .      V           |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .                  |
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . .                  V
+	 * <--Compressed tile Y Stride--->
+	 * <------- Width ------->
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  ^           ^
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  Height      |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |        Macro_tile_Y_Scanlines
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  |           |
+	 * Y* Y* Y* Y* Y* Y* Y* Y* . . . .  V           |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .              |
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 * . . . . . . . . . . . . . . . .              V
+	 * <----- UV_Meta_Stride ---->
+	 * M M M M M M M M M M M M . .      ^
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      |
+	 * M M M M M M M M M M M M . .      M_UV_Scanlines
+	 * . . . . . . . . . . . . . .      |
+	 * . . . . . . . . . . . . . .      V
+	 * . . . . . . . . . . . . . .      -------> Buffer size aligned to 4k
+	 * <--Compressed tile UV Stride--->
+	 * U* V* U* V* U* V* U* V* . . . .  ^
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  |
+	 * U* V* U* V* U* V* U* V* . . . .  UV_Scanlines
+	 * . . . . . . . . . . . . . . . .  |
+	 * . . . . . . . . . . . . . . . .  V
+	 * . . . . . . . . . . . . . . . .  -------> Buffer size aligned to 4k
+	 *
+	 *
+	 * Y_Stride = align(Width * 2, 256)
+	 * UV_Stride = align(Width * 2, 256)
+	 * Y_Scanlines = align(Height, 16)
+	 * UV_Scanlines = align(Height/2, 16)
+	 * Y_UBWC_Plane_Size = align(Y_Stride * Y_Scanlines, 4096)
+	 * UV_UBWC_Plane_Size = align(UV_Stride * UV_Scanlines, 4096)
+	 * Y_Meta_Stride = align(roundup(Width, Y_TileWidth), 64)
+	 * Y_Meta_Scanlines = align(roundup(Height, Y_TileHeight), 16)
+	 * Y_Meta_Plane_size = align(Y_Meta_Stride * Y_Meta_Scanlines, 4096)
+	 * UV_Meta_Stride = align(roundup(Width, UV_TileWidth), 64)
+	 * UV_Meta_Scanlines = align(roundup(Height, UV_TileHeight), 16)
+	 * UV_Meta_Plane_size = align(UV_Meta_Stride * UV_Meta_Scanlines, 4096)
+	 * Extradata = 8k
+	 *
+	 * Total size = align(Y_UBWC_Plane_size + UV_UBWC_Plane_size +
+	 *           Y_Meta_Plane_size + UV_Meta_Plane_size
+	 *           + max(Extradata, Y_Stride * 48), 4096)
+	 */
+	COLOR_FMT_P010_UBWC,
+};
+
+#define COLOR_FMT_RGBA1010102_UBWC	COLOR_FMT_RGBA1010102_UBWC
+#define COLOR_FMT_RGB565_UBWC		COLOR_FMT_RGB565_UBWC
+#define COLOR_FMT_P010_UBWC		COLOR_FMT_P010_UBWC
+
+static inline unsigned int VENUS_EXTRADATA_SIZE(int width, int height)
+{
+	(void)height;
+	(void)width;
+
+	/*
+	 * In the future, calculate the size based on the w/h but just
+	 * hardcode it for now since 16K satisfies all current usecases.
+	 */
+	return 16 * 1024;
+}
+
+static inline unsigned int VENUS_Y_STRIDE(int color_fmt, int width)
+{
+	unsigned int alignment, stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV21:
+	case COLOR_FMT_NV12:
+	case COLOR_FMT_NV12_MVTB:
+	case COLOR_FMT_NV12_UBWC:
+		alignment = 128;
+		stride = MSM_MEDIA_ALIGN(width, alignment);
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+		alignment = 256;
+		stride = MSM_MEDIA_ALIGN(width, 192);
+		stride = MSM_MEDIA_ALIGN(stride * 4/3, alignment);
+		break;
+	case COLOR_FMT_P010_UBWC:
+		alignment = 256;
+		stride = MSM_MEDIA_ALIGN(width * 2, alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return stride;
+}
+
+static inline unsigned int VENUS_UV_STRIDE(int color_fmt, int width)
+{
+	unsigned int alignment, stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV21:
+	case COLOR_FMT_NV12:
+	case COLOR_FMT_NV12_MVTB:
+	case COLOR_FMT_NV12_UBWC:
+		alignment = 128;
+		stride = MSM_MEDIA_ALIGN(width, alignment);
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+		alignment = 256;
+		stride = MSM_MEDIA_ALIGN(width, 192);
+		stride = MSM_MEDIA_ALIGN(stride * 4/3, alignment);
+		break;
+	case COLOR_FMT_P010_UBWC:
+		alignment = 256;
+		stride = MSM_MEDIA_ALIGN(width * 2, alignment);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return stride;
+}
+
+static inline unsigned int VENUS_Y_SCANLINES(int color_fmt, int height)
+{
+	unsigned int alignment, sclines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV21:
+	case COLOR_FMT_NV12:
+	case COLOR_FMT_NV12_MVTB:
+	case COLOR_FMT_NV12_UBWC:
+		alignment = 32;
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+	case COLOR_FMT_P010_UBWC:
+		alignment = 16;
+		break;
+	default:
+		return 0;
+	}
+	sclines = MSM_MEDIA_ALIGN(height, alignment);
+invalid_input:
+	return sclines;
+}
+
+static inline unsigned int VENUS_UV_SCANLINES(int color_fmt, int height)
+{
+	unsigned int alignment, sclines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV21:
+	case COLOR_FMT_NV12:
+	case COLOR_FMT_NV12_MVTB:
+	case COLOR_FMT_NV12_BPP10_UBWC:
+	case COLOR_FMT_P010_UBWC:
+		alignment = 16;
+		break;
+	case COLOR_FMT_NV12_UBWC:
+		alignment = 32;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	sclines = MSM_MEDIA_ALIGN(height / 2, alignment);
+
+invalid_input:
+	return sclines;
+}
+
+static inline unsigned int VENUS_Y_META_STRIDE(int color_fmt, int width)
+{
+	int y_tile_width = 0, y_meta_stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12_UBWC:
+	case COLOR_FMT_P010_UBWC:
+		y_tile_width = 32;
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+		y_tile_width = 48;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	y_meta_stride = MSM_MEDIA_ROUNDUP(width, y_tile_width);
+	y_meta_stride = MSM_MEDIA_ALIGN(y_meta_stride, 64);
+
+invalid_input:
+	return y_meta_stride;
+}
+
+static inline unsigned int VENUS_Y_META_SCANLINES(int color_fmt, int height)
+{
+	int y_tile_height = 0, y_meta_scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12_UBWC:
+		y_tile_height = 8;
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+	case COLOR_FMT_P010_UBWC:
+		y_tile_height = 4;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	y_meta_scanlines = MSM_MEDIA_ROUNDUP(height, y_tile_height);
+	y_meta_scanlines = MSM_MEDIA_ALIGN(y_meta_scanlines, 16);
+
+invalid_input:
+	return y_meta_scanlines;
+}
+
+static inline unsigned int VENUS_UV_META_STRIDE(int color_fmt, int width)
+{
+	int uv_tile_width = 0, uv_meta_stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12_UBWC:
+	case COLOR_FMT_P010_UBWC:
+		uv_tile_width = 16;
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+		uv_tile_width = 24;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	uv_meta_stride = MSM_MEDIA_ROUNDUP(width / 2, uv_tile_width);
+	uv_meta_stride = MSM_MEDIA_ALIGN(uv_meta_stride, 64);
+
+invalid_input:
+	return uv_meta_stride;
+}
+
+static inline unsigned int VENUS_UV_META_SCANLINES(int color_fmt, int height)
+{
+	int uv_tile_height = 0, uv_meta_scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV12_UBWC:
+		uv_tile_height = 8;
+		break;
+	case COLOR_FMT_NV12_BPP10_UBWC:
+	case COLOR_FMT_P010_UBWC:
+		uv_tile_height = 4;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	uv_meta_scanlines = MSM_MEDIA_ROUNDUP(height / 2, uv_tile_height);
+	uv_meta_scanlines = MSM_MEDIA_ALIGN(uv_meta_scanlines, 16);
+
+invalid_input:
+	return uv_meta_scanlines;
+}
+
+static inline unsigned int VENUS_RGB_STRIDE(int color_fmt, int width)
+{
+	unsigned int alignment = 0, stride = 0, bpp = 4;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_RGBA8888:
+		alignment = 128;
+		break;
+	case COLOR_FMT_RGB565_UBWC:
+		alignment = 128;
+		bpp = 2;
+		break;
+	case COLOR_FMT_RGBA8888_UBWC:
+	case COLOR_FMT_RGBA1010102_UBWC:
+		alignment = 256;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	stride = MSM_MEDIA_ALIGN(width * bpp, alignment);
+
+invalid_input:
+	return stride;
+}
+
+static inline unsigned int VENUS_RGB_SCANLINES(int color_fmt, int height)
+{
+	unsigned int alignment = 0, scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_RGBA8888:
+		alignment = 32;
+		break;
+	case COLOR_FMT_RGBA8888_UBWC:
+	case COLOR_FMT_RGBA1010102_UBWC:
+	case COLOR_FMT_RGB565_UBWC:
+		alignment = 16;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	scanlines = MSM_MEDIA_ALIGN(height, alignment);
+
+invalid_input:
+	return scanlines;
+}
+
+static inline unsigned int VENUS_RGB_META_STRIDE(int color_fmt, int width)
+{
+	int rgb_tile_width = 0, rgb_meta_stride = 0;
+
+	if (!width)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_RGBA8888_UBWC:
+	case COLOR_FMT_RGBA1010102_UBWC:
+	case COLOR_FMT_RGB565_UBWC:
+		rgb_tile_width = 16;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	rgb_meta_stride = MSM_MEDIA_ROUNDUP(width, rgb_tile_width);
+	rgb_meta_stride = MSM_MEDIA_ALIGN(rgb_meta_stride, 64);
+
+invalid_input:
+	return rgb_meta_stride;
+}
+
+static inline unsigned int VENUS_RGB_META_SCANLINES(int color_fmt, int height)
+{
+	int rgb_tile_height = 0, rgb_meta_scanlines = 0;
+
+	if (!height)
+		goto invalid_input;
+
+	switch (color_fmt) {
+	case COLOR_FMT_RGBA8888_UBWC:
+	case COLOR_FMT_RGBA1010102_UBWC:
+	case COLOR_FMT_RGB565_UBWC:
+		rgb_tile_height = 4;
+		break;
+	default:
+		goto invalid_input;
+	}
+
+	rgb_meta_scanlines = MSM_MEDIA_ROUNDUP(height, rgb_tile_height);
+	rgb_meta_scanlines = MSM_MEDIA_ALIGN(rgb_meta_scanlines, 16);
+
+invalid_input:
+	return rgb_meta_scanlines;
+}
+
+static inline unsigned int VENUS_BUFFER_SIZE(
+	int color_fmt, int width, int height)
+{
+	const unsigned int extra_size = VENUS_EXTRADATA_SIZE(width, height);
+	unsigned int uv_alignment = 0, size = 0;
+	unsigned int y_plane, uv_plane, y_stride,
+		uv_stride, y_sclines, uv_sclines;
+	unsigned int y_ubwc_plane = 0, uv_ubwc_plane = 0;
+	unsigned int y_meta_stride = 0, y_meta_scanlines = 0;
+	unsigned int uv_meta_stride = 0, uv_meta_scanlines = 0;
+	unsigned int y_meta_plane = 0, uv_meta_plane = 0;
+	unsigned int rgb_stride = 0, rgb_scanlines = 0;
+	unsigned int rgb_plane = 0, rgb_ubwc_plane = 0, rgb_meta_plane = 0;
+	unsigned int rgb_meta_stride = 0, rgb_meta_scanlines = 0;
+
+	if (!width || !height)
+		goto invalid_input;
+
+	y_stride = VENUS_Y_STRIDE(color_fmt, width);
+	uv_stride = VENUS_UV_STRIDE(color_fmt, width);
+	y_sclines = VENUS_Y_SCANLINES(color_fmt, height);
+	uv_sclines = VENUS_UV_SCANLINES(color_fmt, height);
+	rgb_stride = VENUS_RGB_STRIDE(color_fmt, width);
+	rgb_scanlines = VENUS_RGB_SCANLINES(color_fmt, height);
+
+	switch (color_fmt) {
+	case COLOR_FMT_NV21:
+	case COLOR_FMT_NV12:
+		uv_alignment = 4096;
+		y_plane = y_stride * y_sclines;
+		uv_plane = uv_stride * uv_sclines + uv_alignment;
+		size = y_plane + uv_plane +
+				MSM_MEDIA_MAX(extra_size, 8 * y_stride);
+		size = MSM_MEDIA_ALIGN(size, 4096);
+		break;
+	case COLOR_FMT_NV12_MVTB:
+		uv_alignment = 4096;
+		y_plane = y_stride * y_sclines;
+		uv_plane = uv_stride * uv_sclines + uv_alignment;
+		size = y_plane + uv_plane;
+		size = 2 * size + extra_size;
+		size = MSM_MEDIA_ALIGN(size, 4096);
+		break;
+	case COLOR_FMT_NV12_UBWC:
+	case COLOR_FMT_NV12_BPP10_UBWC:
+		y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096);
+		uv_ubwc_plane = MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096);
+		y_meta_stride = VENUS_Y_META_STRIDE(color_fmt, width);
+		y_meta_scanlines = VENUS_Y_META_SCANLINES(color_fmt, height);
+		y_meta_plane = MSM_MEDIA_ALIGN(
+				y_meta_stride * y_meta_scanlines, 4096);
+		uv_meta_stride = VENUS_UV_META_STRIDE(color_fmt, width);
+		uv_meta_scanlines = VENUS_UV_META_SCANLINES(color_fmt, height);
+		uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride *
+					uv_meta_scanlines, 4096);
+
+		size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+			uv_meta_plane +
+			MSM_MEDIA_MAX(extra_size + 8192, 48 * y_stride);
+		size = MSM_MEDIA_ALIGN(size, 4096);
+		break;
+	case COLOR_FMT_P010_UBWC:
+		y_ubwc_plane = MSM_MEDIA_ALIGN(y_stride * y_sclines, 4096);
+		uv_ubwc_plane = MSM_MEDIA_ALIGN(uv_stride * uv_sclines, 4096);
+		y_meta_stride = VENUS_Y_META_STRIDE(color_fmt, width);
+		y_meta_scanlines = VENUS_Y_META_SCANLINES(color_fmt, height);
+		y_meta_plane = MSM_MEDIA_ALIGN(
+				y_meta_stride * y_meta_scanlines, 4096);
+		uv_meta_stride = VENUS_UV_META_STRIDE(color_fmt, width);
+		uv_meta_scanlines = VENUS_UV_META_SCANLINES(color_fmt, height);
+		uv_meta_plane = MSM_MEDIA_ALIGN(uv_meta_stride *
+					uv_meta_scanlines, 4096);
+
+		size = y_ubwc_plane + uv_ubwc_plane + y_meta_plane +
+			uv_meta_plane;
+		size = MSM_MEDIA_ALIGN(size, 4096);
+		break;
+	case COLOR_FMT_RGBA8888:
+		rgb_plane = MSM_MEDIA_ALIGN(rgb_stride  * rgb_scanlines, 4096);
+		size = rgb_plane;
+		size =  MSM_MEDIA_ALIGN(size, 4096);
+		break;
+	case COLOR_FMT_RGBA8888_UBWC:
+		rgb_ubwc_plane = MSM_MEDIA_ALIGN(rgb_stride * rgb_scanlines,
+							4096);
+		rgb_meta_stride = VENUS_RGB_META_STRIDE(color_fmt, width);
+		rgb_meta_scanlines = VENUS_RGB_META_SCANLINES(color_fmt,
+					height);
+		rgb_meta_plane = MSM_MEDIA_ALIGN(rgb_meta_stride *
+					rgb_meta_scanlines, 4096);
+		size = rgb_ubwc_plane + rgb_meta_plane;
+		size = MSM_MEDIA_ALIGN(size, 4096);
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return size;
+}
+
+static inline unsigned int VENUS_VIEW2_OFFSET(
+	int color_fmt, int width, int height)
+{
+	unsigned int offset = 0;
+	unsigned int y_plane, uv_plane, y_stride,
+		uv_stride, y_sclines, uv_sclines;
+	if (!width || !height)
+		goto invalid_input;
+
+	y_stride = VENUS_Y_STRIDE(color_fmt, width);
+	uv_stride = VENUS_UV_STRIDE(color_fmt, width);
+	y_sclines = VENUS_Y_SCANLINES(color_fmt, height);
+	uv_sclines = VENUS_UV_SCANLINES(color_fmt, height);
+	switch (color_fmt) {
+	case COLOR_FMT_NV12_MVTB:
+		y_plane = y_stride * y_sclines;
+		uv_plane = uv_stride * uv_sclines;
+		offset = y_plane + uv_plane;
+		break;
+	default:
+		break;
+	}
+invalid_input:
+	return offset;
+}
+
+#endif
diff --git a/include/uapi/media/msm_vidc.h b/include/uapi/media/msm_vidc.h
new file mode 100644
index 0000000..d330e4d
--- /dev/null
+++ b/include/uapi/media/msm_vidc.h
@@ -0,0 +1,374 @@
+#ifndef __MSM_VIDC_H__
+#define __MSM_VIDC_H__
+
+#include <linux/types.h>
+
+#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12	0x2
+#define MSM_VIDC_HAL_INTERLACE_COLOR_FORMAT_NV12_UBWC	0x8002
+
+struct msm_vidc_extradata_header {
+	unsigned int size;
+	unsigned int:32; /** Keeping binary compatibility */
+	unsigned int:32; /* with firmware and OpenMAX IL **/
+	unsigned int type; /* msm_vidc_extradata_type */
+	unsigned int data_size;
+	unsigned char data[1];
+};
+
+struct msm_vidc_interlace_payload {
+	unsigned int format;
+	unsigned int color_format;
+};
+
+struct msm_vidc_framerate_payload {
+	unsigned int frame_rate;
+};
+
+struct msm_vidc_ts_payload {
+	unsigned int timestamp_lo;
+	unsigned int timestamp_hi;
+};
+
+struct msm_vidc_concealmb_payload {
+	unsigned int num_mbs;
+};
+
+struct msm_vidc_recoverysei_payload {
+	unsigned int flags;
+};
+
+struct msm_vidc_aspect_ratio_payload {
+	unsigned int size;
+	unsigned int version;
+	unsigned int port_index;
+	unsigned int aspect_width;
+	unsigned int aspect_height;
+};
+
+struct msm_vidc_mpeg2_seqdisp_payload {
+	unsigned int video_format;
+	unsigned int color_descp;
+	unsigned int color_primaries;
+	unsigned int transfer_char;
+	unsigned int matrix_coeffs;
+	unsigned int disp_width;
+	unsigned int disp_height;
+};
+
+struct msm_vidc_vc1_seqdisp_payload {
+	unsigned int prog_seg_format;
+	unsigned int uv_sampl_fmt;
+	unsigned int color_format;
+	unsigned int color_primaries;
+	unsigned int transfer_char;
+	unsigned int matrix_coeffs;
+	unsigned int aspect_ratio;
+	unsigned int aspect_horiz;
+	unsigned int aspect_vert;
+};
+
+struct msm_vidc_input_crop_payload {
+	unsigned int size;
+	unsigned int version;
+	unsigned int port_index;
+	unsigned int left;
+	unsigned int top;
+	unsigned int width;
+	unsigned int height;
+};
+
+struct msm_vidc_output_crop_payload {
+	unsigned int size;
+	unsigned int version;
+	unsigned int port_index;
+	unsigned int left;
+	unsigned int top;
+	unsigned int display_width;
+	unsigned int display_height;
+	unsigned int width;
+	unsigned int height;
+};
+
+
+struct msm_vidc_digital_zoom_payload {
+	unsigned int size;
+	unsigned int version;
+	unsigned int port_index;
+	unsigned int zoom_width;
+	unsigned int zoom_height;
+};
+
+struct msm_vidc_extradata_index {
+	unsigned int type;
+	union {
+		struct msm_vidc_input_crop_payload input_crop;
+		struct msm_vidc_digital_zoom_payload digital_zoom;
+		struct msm_vidc_aspect_ratio_payload aspect_ratio;
+	};
+};
+
+struct msm_vidc_panscan_window {
+	unsigned int panscan_height_offset;
+	unsigned int panscan_width_offset;
+	unsigned int panscan_window_width;
+	unsigned int panscan_window_height;
+};
+
+struct msm_vidc_panscan_window_payload {
+	unsigned int num_panscan_windows;
+	struct msm_vidc_panscan_window wnd[1];
+};
+
+struct msm_vidc_stream_userdata_payload {
+	unsigned int type;
+	unsigned int data[1];
+};
+
+struct msm_vidc_frame_qp_payload {
+	unsigned int frame_qp;
+};
+
+struct msm_vidc_frame_bits_info_payload {
+	unsigned int frame_bits;
+	unsigned int header_bits;
+};
+
+struct msm_vidc_s3d_frame_packing_payload {
+	unsigned int fpa_id;
+	unsigned int cancel_flag;
+	unsigned int fpa_type;
+	unsigned int quin_cunx_flag;
+	unsigned int content_interprtation_type;
+	unsigned int spatial_flipping_flag;
+	unsigned int frame0_flipped_flag;
+	unsigned int field_views_flag;
+	unsigned int current_frame_is_frame0_flag;
+	unsigned int frame0_self_contained_flag;
+	unsigned int frame1_self_contained_flag;
+	unsigned int frame0_graid_pos_x;
+	unsigned int frame0_graid_pos_y;
+	unsigned int frame1_graid_pos_x;
+	unsigned int frame1_graid_pos_y;
+	unsigned int fpa_reserved_byte;
+	unsigned int fpa_repetition_period;
+	unsigned int fpa_extension_flag;
+};
+
+struct msm_vidc_vqzip_sei_payload {
+	unsigned int size;
+	unsigned int data[1];
+};
+
+struct msm_vidc_yuv_stats_payload {
+	unsigned int frame_qp;
+	unsigned int texture;
+	unsigned int luma_in_q16;
+	unsigned int frame_difference;
+};
+
+struct msm_vidc_vpx_colorspace_payload {
+	unsigned int color_space;
+	unsigned int yuv_range_flag;
+	unsigned int sumsampling_x;
+	unsigned int sumsampling_y;
+};
+
+struct msm_vidc_roi_qp_payload {
+	int upper_qp_offset;
+	int lower_qp_offset;
+	unsigned int b_roi_info;
+	int mbi_info_size;
+	unsigned int data[1];
+};
+
+struct msm_vidc_mastering_display_colour_sei_payload {
+	unsigned int nDisplayPrimariesX[3];
+	unsigned int nDisplayPrimariesY[3];
+	unsigned int nWhitePointX;
+	unsigned int nWhitePointY;
+	unsigned int nMaxDisplayMasteringLuminance;
+	unsigned int nMinDisplayMasteringLuminance;
+};
+
+struct msm_vidc_content_light_level_sei_payload {
+	unsigned int nMaxContentLight;
+	unsigned int nMaxPicAverageLight;
+};
+
+struct msm_vidc_vui_display_info_payload {
+	unsigned int video_signal_present_flag;
+	unsigned int video_format;
+	unsigned int bit_depth_y;
+	unsigned int bit_depth_c;
+	unsigned int video_full_range_flag;
+	unsigned int color_description_present_flag;
+	unsigned int color_primaries;
+	unsigned int transfer_characteristics;
+	unsigned int matrix_coefficients;
+	unsigned int chroma_location_info_present_flag;
+	unsigned int chroma_format_idc;
+	unsigned int separate_color_plane_flag;
+	unsigned int chroma_sample_loc_type_top_field;
+	unsigned int chroma_sample_loc_type_bottom_field;
+};
+
+enum msm_vidc_extradata_type {
+	MSM_VIDC_EXTRADATA_NONE = 0x00000000,
+	MSM_VIDC_EXTRADATA_MB_QUANTIZATION = 0x00000001,
+	MSM_VIDC_EXTRADATA_INTERLACE_VIDEO = 0x00000002,
+	MSM_VIDC_EXTRADATA_TIMESTAMP = 0x00000005,
+	MSM_VIDC_EXTRADATA_S3D_FRAME_PACKING = 0x00000006,
+	MSM_VIDC_EXTRADATA_FRAME_RATE = 0x00000007,
+	MSM_VIDC_EXTRADATA_PANSCAN_WINDOW = 0x00000008,
+	MSM_VIDC_EXTRADATA_RECOVERY_POINT_SEI = 0x00000009,
+	MSM_VIDC_EXTRADATA_MPEG2_SEQDISP = 0x0000000D,
+	MSM_VIDC_EXTRADATA_STREAM_USERDATA = 0x0000000E,
+	MSM_VIDC_EXTRADATA_FRAME_QP = 0x0000000F,
+	MSM_VIDC_EXTRADATA_FRAME_BITS_INFO = 0x00000010,
+	MSM_VIDC_EXTRADATA_VQZIP_SEI = 0x00000011,
+	MSM_VIDC_EXTRADATA_ROI_QP = 0x00000013,
+#define MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI \
+	MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI
+	MSM_VIDC_EXTRADATA_MASTERING_DISPLAY_COLOUR_SEI = 0x00000015,
+#define MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI \
+	MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI
+	MSM_VIDC_EXTRADATA_CONTENT_LIGHT_LEVEL_SEI = 0x00000016,
+#define MSM_VIDC_EXTRADATA_PQ_INFO \
+	MSM_VIDC_EXTRADATA_PQ_INFO
+	MSM_VIDC_EXTRADATA_PQ_INFO = 0x00000017,
+	MSM_VIDC_EXTRADATA_INPUT_CROP = 0x0700000E,
+#define MSM_VIDC_EXTRADATA_OUTPUT_CROP \
+	MSM_VIDC_EXTRADATA_OUTPUT_CROP
+	MSM_VIDC_EXTRADATA_OUTPUT_CROP = 0x0700000F,
+	MSM_VIDC_EXTRADATA_DIGITAL_ZOOM = 0x07000010,
+#define MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO \
+	MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO
+	MSM_VIDC_EXTRADATA_VPX_COLORSPACE_INFO = 0x070000011,
+	MSM_VIDC_EXTRADATA_MULTISLICE_INFO = 0x7F100000,
+	MSM_VIDC_EXTRADATA_NUM_CONCEALED_MB = 0x7F100001,
+	MSM_VIDC_EXTRADATA_INDEX = 0x7F100002,
+	MSM_VIDC_EXTRADATA_ASPECT_RATIO = 0x7F100003,
+	MSM_VIDC_EXTRADATA_METADATA_LTR = 0x7F100004,
+	MSM_VIDC_EXTRADATA_METADATA_FILLER = 0x7FE00002,
+	MSM_VIDC_EXTRADATA_METADATA_MBI = 0x7F100005,
+#define MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO \
+	MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO
+	MSM_VIDC_EXTRADATA_VUI_DISPLAY_INFO = 0x7F100006,
+	MSM_VIDC_EXTRADATA_YUVSTATS_INFO = 0x7F100007,
+};
+enum msm_vidc_interlace_type {
+	MSM_VIDC_INTERLACE_FRAME_PROGRESSIVE = 0x01,
+	MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_TOPFIELDFIRST = 0x02,
+	MSM_VIDC_INTERLACE_INTERLEAVE_FRAME_BOTTOMFIELDFIRST = 0x04,
+	MSM_VIDC_INTERLACE_FRAME_TOPFIELDFIRST = 0x08,
+	MSM_VIDC_INTERLACE_FRAME_BOTTOMFIELDFIRST = 0x10,
+};
+
+/* enum msm_vidc_framepack_type */
+#define MSM_VIDC_FRAMEPACK_CHECKERBOARD 0x00
+#define MSM_VIDC_FRAMEPACK_COLUMN_INTERLEAVE 0x01
+#define MSM_VIDC_FRAMEPACK_ROW_INTERLEAVE 0x02
+#define MSM_VIDC_FRAMEPACK_SIDE_BY_SIDE 0x03
+#define MSM_VIDC_FRAMEPACK_TOP_BOTTOM 0x04
+#define MSM_VIDC_FRAMEPACK_TEMPORAL_INTERLEAVE 0x05
+
+enum msm_vidc_recovery_sei {
+	MSM_VIDC_FRAME_RECONSTRUCTION_INCORRECT = 0x0,
+	MSM_VIDC_FRAME_RECONSTRUCTION_CORRECT = 0x01,
+	MSM_VIDC_FRAME_RECONSTRUCTION_APPROXIMATELY_CORRECT = 0x02,
+};
+enum msm_vidc_userdata_type {
+	MSM_VIDC_USERDATA_TYPE_FRAME = 0x1,
+	MSM_VIDC_USERDATA_TYPE_TOP_FIELD = 0x2,
+	MSM_VIDC_USERDATA_TYPE_BOTTOM_FIELD = 0x3,
+};
+
+/* See colour_primaries of ISO/IEC 14496 for significance */
+enum msm_vidc_h264_color_primaries_values {
+	MSM_VIDC_RESERVED_1 = 0,
+	MSM_VIDC_BT709_5 = 1,
+	MSM_VIDC_UNSPECIFIED = 2,
+	MSM_VIDC_RESERVED_2 = 3,
+	MSM_VIDC_BT470_6_M = 4,
+	MSM_VIDC_BT601_6_625 = 5,
+	MSM_VIDC_BT470_6_BG = MSM_VIDC_BT601_6_625,
+	MSM_VIDC_BT601_6_525 = 6,
+	MSM_VIDC_SMPTE_240M = 7,
+	MSM_VIDC_GENERIC_FILM = 8,
+	MSM_VIDC_BT2020 = 9,
+};
+
+enum msm_vidc_vp9_color_primaries_values {
+	MSM_VIDC_CS_UNKNOWN,
+	MSM_VIDC_CS_BT_601,
+	MSM_VIDC_CS_BT_709,
+	MSM_VIDC_CS_SMPTE_170,
+	MSM_VIDC_CS_SMPTE_240,
+	MSM_VIDC_CS_BT_2020,
+	MSM_VIDC_CS_RESERVED,
+	MSM_VIDC_CS_RGB,
+};
+
+enum msm_vidc_h264_matrix_coeff_values {
+	MSM_VIDC_MATRIX_RGB = 0,
+	MSM_VIDC_MATRIX_BT_709_5 = 1,
+	MSM_VIDC_MATRIX_UNSPECIFIED = 2,
+	MSM_VIDC_MATRIX_RESERVED = 3,
+	MSM_VIDC_MATRIX_FCC_47 = 4,
+	MSM_VIDC_MATRIX_601_6_625 = 5,
+	MSM_VIDC_MATRIX_BT470_BG = MSM_VIDC_MATRIX_601_6_625,
+	MSM_VIDC_MATRIX_601_6_525 = 6,
+	MSM_VIDC_MATRIX_SMPTE_170M = MSM_VIDC_MATRIX_601_6_525,
+	MSM_VIDC_MATRIX_SMPTE_240M = 7,
+	MSM_VIDC_MATRIX_Y_CG_CO = 8,
+	MSM_VIDC_MATRIX_BT_2020 = 9,
+	MSM_VIDC_MATRIX_BT_2020_CONST = 10,
+};
+
+enum msm_vidc_h264_transfer_chars_values {
+	MSM_VIDC_TRANSFER_RESERVED_1 = 0,
+	MSM_VIDC_TRANSFER_BT709_5 = 1,
+	MSM_VIDC_TRANSFER_UNSPECIFIED = 2,
+	MSM_VIDC_TRANSFER_RESERVED_2 = 3,
+	MSM_VIDC_TRANSFER_BT_470_6_M = 4,
+	MSM_VIDC_TRANSFER_BT_470_6_BG = 5,
+	MSM_VIDC_TRANSFER_601_6_625 = 6,
+	MSM_VIDC_TRANSFER_601_6_525 = MSM_VIDC_TRANSFER_601_6_625,
+	MSM_VIDC_TRANSFER_SMPTE_240M = 7,
+	MSM_VIDC_TRANSFER_LINEAR = 8,
+	MSM_VIDC_TRANSFER_LOG_100_1 = 9,
+	MSM_VIDC_TRANSFER_LOG_100_SQRT10_1 = 10,
+	MSM_VIDC_TRANSFER_IEC_61966 = 11,
+	MSM_VIDC_TRANSFER_BT_1361 = 12,
+	MSM_VIDC_TRANSFER_SRGB = 13,
+	MSM_VIDC_TRANSFER_BT_2020_10 = 14,
+	MSM_VIDC_TRANSFER_BT_2020_12 = 15,
+};
+
+enum msm_vidc_pixel_depth {
+	MSM_VIDC_BIT_DEPTH_8,
+	MSM_VIDC_BIT_DEPTH_10,
+	MSM_VIDC_BIT_DEPTH_UNSUPPORTED = 0XFFFFFFFF,
+};
+
+enum msm_vidc_video_format {
+	MSM_VIDC_COMPONENT,
+	MSM_VIDC_PAL,
+	MSM_VIDC_NTSC,
+	MSM_VIDC_SECAM,
+	MSM_VIDC_MAC,
+	MSM_VIDC_UNSPECIFIED_FORMAT,
+	MSM_VIDC_RESERVED_1_FORMAT,
+	MSM_VIDC_RESERVED_2_FORMAT,
+};
+
+enum msm_vidc_color_desc_flag {
+	MSM_VIDC_COLOR_DESC_NOT_PRESENT,
+	MSM_VIDC_COLOR_DESC_PRESENT,
+};
+
+/*enum msm_vidc_pic_struct */
+#define MSM_VIDC_PIC_STRUCT_MAYBE_INTERLACED 0x0
+#define MSM_VIDC_PIC_STRUCT_PROGRESSIVE 0x1
+
+#endif
diff --git a/include/uapi/sound/Kbuild b/include/uapi/sound/Kbuild
index 9578d8b..b0350f0 100644
--- a/include/uapi/sound/Kbuild
+++ b/include/uapi/sound/Kbuild
@@ -14,3 +14,11 @@
 header-y += tlv.h
 header-y += usb_stream.h
 header-y += snd_sst_tokens.h
+header-y += lsm_params.h
+header-y += audio_slimslave.h
+header-y += voice_params.h
+header-y += audio_effects.h
+header-y += voice_svc.h
+header-y += devdep_params.h
+header-y += msmcal-hwdep.h
+header-y += wcd-dsp-glink.h
diff --git a/include/uapi/sound/audio_effects.h b/include/uapi/sound/audio_effects.h
new file mode 100644
index 0000000..063b5c1
--- /dev/null
+++ b/include/uapi/sound/audio_effects.h
@@ -0,0 +1,374 @@
+/*
+ * Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _AUDIO_EFFECTS_H
+#define _AUDIO_EFFECTS_H
+
+/** AUDIO EFFECTS **/
+
+
+/* CONFIG GET/SET */
+#define CONFIG_CACHE			0
+#define CONFIG_SET			1
+#define CONFIG_GET			2
+
+/* CONFIG HEADER */
+/*
+ * MODULE_ID,
+ * DEVICE,
+ * NUM_COMMANDS,
+ * COMMAND_ID_1,
+ * CONFIG_CACHE/SET/GET,
+ * OFFSET_1,
+ * LENGTH_1,
+ * VALUES_1,
+ * ...,
+ * ...,
+ * COMMAND_ID_2,
+ * CONFIG_CACHE/SET/GET,
+ * OFFSET_2,
+ * LENGTH_2,
+ * VALUES_2,
+ * ...,
+ * ...,
+ * COMMAND_ID_3,
+ * ...
+ */
+
+
+/* CONFIG PARAM IDs */
+#define VIRTUALIZER_MODULE		0x00001000
+#define VIRTUALIZER_ENABLE		0x00001001
+#define VIRTUALIZER_STRENGTH		0x00001002
+#define VIRTUALIZER_OUT_TYPE		0x00001003
+#define VIRTUALIZER_GAIN_ADJUST		0x00001004
+#define VIRTUALIZER_ENABLE_PARAM_LEN		1
+#define VIRTUALIZER_STRENGTH_PARAM_LEN		1
+#define VIRTUALIZER_OUT_TYPE_PARAM_LEN		1
+#define VIRTUALIZER_GAIN_ADJUST_PARAM_LEN	1
+
+#define REVERB_MODULE			0x00002000
+#define REVERB_ENABLE			0x00002001
+#define REVERB_MODE			0x00002002
+#define REVERB_PRESET			0x00002003
+#define REVERB_WET_MIX			0x00002004
+#define REVERB_GAIN_ADJUST		0x00002005
+#define REVERB_ROOM_LEVEL		0x00002006
+#define REVERB_ROOM_HF_LEVEL		0x00002007
+#define REVERB_DECAY_TIME		0x00002008
+#define REVERB_DECAY_HF_RATIO		0x00002009
+#define REVERB_REFLECTIONS_LEVEL	0x0000200a
+#define REVERB_REFLECTIONS_DELAY	0x0000200b
+#define REVERB_LEVEL			0x0000200c
+#define REVERB_DELAY			0x0000200d
+#define REVERB_DIFFUSION		0x0000200e
+#define REVERB_DENSITY			0x0000200f
+#define REVERB_ENABLE_PARAM_LEN			1
+#define REVERB_MODE_PARAM_LEN			1
+#define REVERB_PRESET_PARAM_LEN			1
+#define REVERB_WET_MIX_PARAM_LEN		1
+#define REVERB_GAIN_ADJUST_PARAM_LEN		1
+#define REVERB_ROOM_LEVEL_PARAM_LEN		1
+#define REVERB_ROOM_HF_LEVEL_PARAM_LEN		1
+#define REVERB_DECAY_TIME_PARAM_LEN		1
+#define REVERB_DECAY_HF_RATIO_PARAM_LEN		1
+#define REVERB_REFLECTIONS_LEVEL_PARAM_LEN	1
+#define REVERB_REFLECTIONS_DELAY_PARAM_LEN	1
+#define REVERB_LEVEL_PARAM_LEN			1
+#define REVERB_DELAY_PARAM_LEN			1
+#define REVERB_DIFFUSION_PARAM_LEN		1
+#define REVERB_DENSITY_PARAM_LEN		1
+
+#define BASS_BOOST_MODULE		0x00003000
+#define BASS_BOOST_ENABLE		0x00003001
+#define BASS_BOOST_MODE			0x00003002
+#define BASS_BOOST_STRENGTH		0x00003003
+#define BASS_BOOST_ENABLE_PARAM_LEN		1
+#define BASS_BOOST_MODE_PARAM_LEN		1
+#define BASS_BOOST_STRENGTH_PARAM_LEN		1
+
+#define EQ_MODULE			0x00004000
+#define EQ_ENABLE			0x00004001
+#define EQ_CONFIG			0x00004002
+#define EQ_NUM_BANDS			0x00004003
+#define EQ_BAND_LEVELS			0x00004004
+#define EQ_BAND_LEVEL_RANGE		0x00004005
+#define EQ_BAND_FREQS			0x00004006
+#define EQ_SINGLE_BAND_FREQ_RANGE	0x00004007
+#define EQ_SINGLE_BAND_FREQ		0x00004008
+#define EQ_BAND_INDEX			0x00004009
+#define EQ_PRESET_ID			0x0000400a
+#define EQ_NUM_PRESETS			0x0000400b
+#define EQ_PRESET_NAME			0x0000400c
+#define EQ_ENABLE_PARAM_LEN			1
+#define EQ_CONFIG_PARAM_LEN			3
+#define EQ_CONFIG_PER_BAND_PARAM_LEN		5
+#define EQ_NUM_BANDS_PARAM_LEN			1
+#define EQ_BAND_LEVELS_PARAM_LEN		13
+#define EQ_BAND_LEVEL_RANGE_PARAM_LEN		2
+#define EQ_BAND_FREQS_PARAM_LEN			13
+#define EQ_SINGLE_BAND_FREQ_RANGE_PARAM_LEN	2
+#define EQ_SINGLE_BAND_FREQ_PARAM_LEN		1
+#define EQ_BAND_INDEX_PARAM_LEN			1
+#define EQ_PRESET_ID_PARAM_LEN			1
+#define EQ_NUM_PRESETS_PARAM_LEN		1
+#define EQ_PRESET_NAME_PARAM_LEN		32
+
+#define EQ_TYPE_NONE	0
+#define EQ_BASS_BOOST	1
+#define EQ_BASS_CUT	2
+#define EQ_TREBLE_BOOST	3
+#define EQ_TREBLE_CUT	4
+#define EQ_BAND_BOOST	5
+#define EQ_BAND_CUT	6
+
+#define SOFT_VOLUME_MODULE		0x00006000
+#define SOFT_VOLUME_ENABLE		0x00006001
+#define SOFT_VOLUME_GAIN_2CH		0x00006002
+#define SOFT_VOLUME_GAIN_MASTER		0x00006003
+#define SOFT_VOLUME_ENABLE_PARAM_LEN		1
+#define SOFT_VOLUME_GAIN_2CH_PARAM_LEN		2
+#define SOFT_VOLUME_GAIN_MASTER_PARAM_LEN	1
+
+#define SOFT_VOLUME2_MODULE		0x00007000
+#define SOFT_VOLUME2_ENABLE		0x00007001
+#define SOFT_VOLUME2_GAIN_2CH		0x00007002
+#define SOFT_VOLUME2_GAIN_MASTER	0x00007003
+#define SOFT_VOLUME2_ENABLE_PARAM_LEN		SOFT_VOLUME_ENABLE_PARAM_LEN
+#define SOFT_VOLUME2_GAIN_2CH_PARAM_LEN		SOFT_VOLUME_GAIN_2CH_PARAM_LEN
+#define SOFT_VOLUME2_GAIN_MASTER_PARAM_LEN	\
+					SOFT_VOLUME_GAIN_MASTER_PARAM_LEN
+
+#define PBE_CONF_MODULE_ID	0x00010C2A
+#define PBE_CONF_PARAM_ID	0x00010C49
+
+#define PBE_MODULE		0x00008000
+#define PBE_ENABLE		0x00008001
+#define PBE_CONFIG		0x00008002
+#define PBE_ENABLE_PARAM_LEN		1
+#define PBE_CONFIG_PARAM_LEN		28
+
+#define COMMAND_PAYLOAD_LEN	3
+#define COMMAND_PAYLOAD_SZ	(COMMAND_PAYLOAD_LEN * sizeof(uint32_t))
+#define MAX_INBAND_PARAM_SZ	4096
+#define Q27_UNITY		(1 << 27)
+#define Q8_UNITY		(1 << 8)
+#define CUSTOM_OPENSL_PRESET	18
+
+#define VIRTUALIZER_ENABLE_PARAM_SZ	\
+			(VIRTUALIZER_ENABLE_PARAM_LEN*sizeof(uint32_t))
+#define VIRTUALIZER_STRENGTH_PARAM_SZ	\
+			(VIRTUALIZER_STRENGTH_PARAM_LEN*sizeof(uint32_t))
+#define VIRTUALIZER_OUT_TYPE_PARAM_SZ	\
+			(VIRTUALIZER_OUT_TYPE_PARAM_LEN*sizeof(uint32_t))
+#define VIRTUALIZER_GAIN_ADJUST_PARAM_SZ	\
+			(VIRTUALIZER_GAIN_ADJUST_PARAM_LEN*sizeof(uint32_t))
+struct virtualizer_params {
+	uint32_t device;
+	uint32_t enable_flag;
+	uint32_t strength;
+	uint32_t out_type;
+	int32_t gain_adjust;
+};
+
+#define NUM_OSL_REVERB_PRESETS_SUPPORTED	6
+#define REVERB_ENABLE_PARAM_SZ		\
+			(REVERB_ENABLE_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_MODE_PARAM_SZ		\
+			(REVERB_MODE_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_PRESET_PARAM_SZ		\
+			(REVERB_PRESET_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_WET_MIX_PARAM_SZ		\
+			(REVERB_WET_MIX_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_GAIN_ADJUST_PARAM_SZ	\
+			(REVERB_GAIN_ADJUST_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_ROOM_LEVEL_PARAM_SZ	\
+			(REVERB_ROOM_LEVEL_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_ROOM_HF_LEVEL_PARAM_SZ	\
+			(REVERB_ROOM_HF_LEVEL_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_DECAY_TIME_PARAM_SZ	\
+			(REVERB_DECAY_TIME_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_DECAY_HF_RATIO_PARAM_SZ	\
+			(REVERB_DECAY_HF_RATIO_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_REFLECTIONS_LEVEL_PARAM_SZ	\
+			(REVERB_REFLECTIONS_LEVEL_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_REFLECTIONS_DELAY_PARAM_SZ	\
+			(REVERB_REFLECTIONS_DELAY_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_LEVEL_PARAM_SZ		\
+			(REVERB_LEVEL_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_DELAY_PARAM_SZ		\
+			(REVERB_DELAY_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_DIFFUSION_PARAM_SZ	\
+			(REVERB_DIFFUSION_PARAM_LEN*sizeof(uint32_t))
+#define REVERB_DENSITY_PARAM_SZ		\
+			(REVERB_DENSITY_PARAM_LEN*sizeof(uint32_t))
+struct reverb_params {
+	uint32_t device;
+	uint32_t enable_flag;
+	uint32_t mode;
+	uint32_t preset;
+	uint32_t wet_mix;
+	int32_t  gain_adjust;
+	int32_t  room_level;
+	int32_t  room_hf_level;
+	uint32_t decay_time;
+	uint32_t decay_hf_ratio;
+	int32_t  reflections_level;
+	uint32_t reflections_delay;
+	int32_t  level;
+	uint32_t delay;
+	uint32_t diffusion;
+	uint32_t density;
+};
+
+#define BASS_BOOST_ENABLE_PARAM_SZ	\
+			(BASS_BOOST_ENABLE_PARAM_LEN*sizeof(uint32_t))
+#define BASS_BOOST_MODE_PARAM_SZ	\
+			(BASS_BOOST_MODE_PARAM_LEN*sizeof(uint32_t))
+#define BASS_BOOST_STRENGTH_PARAM_SZ	\
+			(BASS_BOOST_STRENGTH_PARAM_LEN*sizeof(uint32_t))
+struct bass_boost_params {
+	uint32_t device;
+	uint32_t enable_flag;
+	uint32_t mode;
+	uint32_t strength;
+};
+
+
+#define MAX_EQ_BANDS 12
+#define MAX_OSL_EQ_BANDS 5
+#define EQ_ENABLE_PARAM_SZ			\
+			(EQ_ENABLE_PARAM_LEN*sizeof(uint32_t))
+#define EQ_CONFIG_PARAM_SZ			\
+			(EQ_CONFIG_PARAM_LEN*sizeof(uint32_t))
+#define EQ_CONFIG_PER_BAND_PARAM_SZ		\
+			(EQ_CONFIG_PER_BAND_PARAM_LEN*sizeof(uint32_t))
+#define EQ_CONFIG_PARAM_MAX_LEN			(EQ_CONFIG_PARAM_LEN+\
+			MAX_EQ_BANDS*EQ_CONFIG_PER_BAND_PARAM_LEN)
+#define EQ_CONFIG_PARAM_MAX_SZ			\
+			(EQ_CONFIG_PARAM_MAX_LEN*sizeof(uint32_t))
+#define EQ_NUM_BANDS_PARAM_SZ			\
+			(EQ_NUM_BANDS_PARAM_LEN*sizeof(uint32_t))
+#define EQ_BAND_LEVELS_PARAM_SZ			\
+			(EQ_BAND_LEVELS_PARAM_LEN*sizeof(uint32_t))
+#define EQ_BAND_LEVEL_RANGE_PARAM_SZ		\
+			(EQ_BAND_LEVEL_RANGE_PARAM_LEN*sizeof(uint32_t))
+#define EQ_BAND_FREQS_PARAM_SZ			\
+			(EQ_BAND_FREQS_PARAM_LEN*sizeof(uint32_t))
+#define EQ_SINGLE_BAND_FREQ_RANGE_PARAM_SZ	\
+			(EQ_SINGLE_BAND_FREQ_RANGE_PARAM_LEN*sizeof(uint32_t))
+#define EQ_SINGLE_BAND_FREQ_PARAM_SZ		\
+			(EQ_SINGLE_BAND_FREQ_PARAM_LEN*sizeof(uint32_t))
+#define EQ_BAND_INDEX_PARAM_SZ			\
+			(EQ_BAND_INDEX_PARAM_LEN*sizeof(uint32_t))
+#define EQ_PRESET_ID_PARAM_SZ			\
+			(EQ_PRESET_ID_PARAM_LEN*sizeof(uint32_t))
+#define EQ_NUM_PRESETS_PARAM_SZ			\
+			(EQ_NUM_PRESETS_PARAM_LEN*sizeof(uint8_t))
+struct eq_config_t {
+	int32_t eq_pregain;
+	int32_t preset_id;
+	uint32_t num_bands;
+};
+struct eq_per_band_config_t {
+	int32_t band_idx;
+	uint32_t filter_type;
+	uint32_t freq_millihertz;
+	int32_t  gain_millibels;
+	uint32_t quality_factor;
+};
+struct eq_per_band_freq_range_t {
+	uint32_t band_index;
+	uint32_t min_freq_millihertz;
+	uint32_t max_freq_millihertz;
+};
+
+struct eq_params {
+	uint32_t device;
+	uint32_t enable_flag;
+	struct eq_config_t config;
+	struct eq_per_band_config_t per_band_cfg[MAX_EQ_BANDS];
+	struct eq_per_band_freq_range_t per_band_freq_range[MAX_EQ_BANDS];
+	uint32_t band_index;
+	uint32_t freq_millihertz;
+};
+
+#define PBE_ENABLE_PARAM_SZ	\
+			(PBE_ENABLE_PARAM_LEN*sizeof(uint32_t))
+#define PBE_CONFIG_PARAM_SZ	\
+			(PBE_CONFIG_PARAM_LEN*sizeof(uint16_t))
+struct pbe_config_t {
+	int16_t  real_bass_mix;
+	int16_t  bass_color_control;
+	uint16_t main_chain_delay;
+	uint16_t xover_filter_order;
+	uint16_t bandpass_filter_order;
+	int16_t  drc_delay;
+	uint16_t rms_tav;
+	int16_t exp_threshold;
+	uint16_t exp_slope;
+	int16_t comp_threshold;
+	uint16_t comp_slope;
+	uint16_t makeup_gain;
+	uint32_t comp_attack;
+	uint32_t comp_release;
+	uint32_t exp_attack;
+	uint32_t exp_release;
+	int16_t limiter_bass_threshold;
+	int16_t limiter_high_threshold;
+	int16_t limiter_bass_makeup_gain;
+	int16_t limiter_high_makeup_gain;
+	int16_t limiter_bass_gc;
+	int16_t limiter_high_gc;
+	int16_t  limiter_delay;
+	uint16_t reserved;
+	/* place holder for filter coeffs to be followed */
+	int32_t p1LowPassCoeffs[5*2];
+	int32_t p1HighPassCoeffs[5*2];
+	int32_t p1BandPassCoeffs[5*3];
+	int32_t p1BassShelfCoeffs[5];
+	int32_t p1TrebleShelfCoeffs[5];
+} __packed;
+
+struct pbe_params {
+	uint32_t device;
+	uint32_t enable_flag;
+	uint32_t cfg_len;
+	struct pbe_config_t config;
+};
+
+#define SOFT_VOLUME_ENABLE_PARAM_SZ		\
+			(SOFT_VOLUME_ENABLE_PARAM_LEN*sizeof(uint32_t))
+#define SOFT_VOLUME_GAIN_MASTER_PARAM_SZ	\
+			(SOFT_VOLUME_GAIN_MASTER_PARAM_LEN*sizeof(uint32_t))
+#define SOFT_VOLUME_GAIN_2CH_PARAM_SZ		\
+			(SOFT_VOLUME_GAIN_2CH_PARAM_LEN*sizeof(uint16_t))
+struct soft_volume_params {
+	uint32_t device;
+	uint32_t enable_flag;
+	uint32_t master_gain;
+	uint32_t left_gain;
+	uint32_t right_gain;
+};
+
+struct msm_nt_eff_all_config {
+	struct bass_boost_params bass_boost;
+	struct pbe_params pbe;
+	struct virtualizer_params virtualizer;
+	struct reverb_params reverb;
+	struct eq_params equalizer;
+	struct soft_volume_params saplus_vol;
+	struct soft_volume_params topo_switch_vol;
+};
+
+#endif /*_MSM_AUDIO_EFFECTS_H*/
diff --git a/include/uapi/sound/devdep_params.h b/include/uapi/sound/devdep_params.h
new file mode 100644
index 0000000..5061ec0
--- /dev/null
+++ b/include/uapi/sound/devdep_params.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DEV_DEP_H
+#define _DEV_DEP_H
+
+struct dolby_param_data {
+	int32_t version;
+	int32_t device_id;
+	int32_t be_id;
+	int32_t param_id;
+	int32_t length;
+	int32_t __user *data;
+};
+
+struct dolby_param_license {
+	int32_t dmid;
+	int32_t license_key;
+};
+
+#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM\
+		_IOWR('U', 0x10, struct dolby_param_data)
+#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM\
+		_IOR('U', 0x11, struct dolby_param_data)
+#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND\
+		_IOWR('U', 0x13, struct dolby_param_data)
+#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE\
+		_IOWR('U', 0x14, struct dolby_param_license)
+#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER\
+		_IOR('U', 0x15, struct dolby_param_data)
+
+#define DTS_EAGLE_MODULE			0x00005000
+#define DTS_EAGLE_MODULE_ENABLE			0x00005001
+#define EAGLE_DRIVER_ID				0xF2
+#define DTS_EAGLE_IOCTL_GET_CACHE_SIZE		_IOR(EAGLE_DRIVER_ID, 0, int)
+#define DTS_EAGLE_IOCTL_SET_CACHE_SIZE		_IOW(EAGLE_DRIVER_ID, 1, int)
+#define DTS_EAGLE_IOCTL_GET_PARAM		_IOR(EAGLE_DRIVER_ID, 2, void*)
+#define DTS_EAGLE_IOCTL_SET_PARAM		_IOW(EAGLE_DRIVER_ID, 3, void*)
+#define DTS_EAGLE_IOCTL_SET_CACHE_BLOCK		_IOW(EAGLE_DRIVER_ID, 4, void*)
+#define DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE	_IOW(EAGLE_DRIVER_ID, 5, void*)
+#define DTS_EAGLE_IOCTL_GET_LICENSE		_IOR(EAGLE_DRIVER_ID, 6, void*)
+#define DTS_EAGLE_IOCTL_SET_LICENSE		_IOW(EAGLE_DRIVER_ID, 7, void*)
+#define DTS_EAGLE_IOCTL_SEND_LICENSE		_IOW(EAGLE_DRIVER_ID, 8, int)
+#define DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS	_IOW(EAGLE_DRIVER_ID, 9, void*)
+#define DTS_EAGLE_FLAG_IOCTL_PRE		(1<<30)
+#define DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE	(1<<31)
+#define DTS_EAGLE_FLAG_IOCTL_GETFROMCORE       DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE
+#define DTS_EAGLE_FLAG_IOCTL_MASK		(~(DTS_EAGLE_FLAG_IOCTL_PRE | \
+					     DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE))
+#define DTS_EAGLE_FLAG_ALSA_GET			(1<<31)
+
+struct dts_eagle_param_desc {
+	uint32_t id;
+	uint32_t size;
+	int32_t offset;
+	uint32_t device;
+} __packed;
+
+#endif
diff --git a/include/uapi/sound/lsm_params.h b/include/uapi/sound/lsm_params.h
new file mode 100644
index 0000000..eafdc11
--- /dev/null
+++ b/include/uapi/sound/lsm_params.h
@@ -0,0 +1,175 @@
+#ifndef _UAPI_LSM_PARAMS_H__
+#define _UAPI_LSM_PARAMS_H__
+
+#include <linux/types.h>
+#include <sound/asound.h>
+
+#define SNDRV_LSM_VERSION SNDRV_PROTOCOL_VERSION(0, 1, 0)
+
+#define LSM_OUT_FORMAT_PCM (0)
+#define LSM_OUT_FORMAT_ADPCM (1 << 0)
+
+#define LSM_OUT_DATA_RAW (0)
+#define LSM_OUT_DATA_PACKED (1)
+
+#define LSM_OUT_DATA_EVENTS_DISABLED (0)
+#define LSM_OUT_DATA_EVENTS_ENABLED (1)
+
+#define LSM_OUT_TRANSFER_MODE_RT (0)
+#define LSM_OUT_TRANSFER_MODE_FTRT (1)
+
+enum lsm_app_id {
+	LSM_VOICE_WAKEUP_APP_ID = 1,
+	LSM_VOICE_WAKEUP_APP_ID_V2 = 2,
+};
+
+enum lsm_detection_mode {
+	LSM_MODE_KEYWORD_ONLY_DETECTION = 1,
+	LSM_MODE_USER_KEYWORD_DETECTION
+};
+
+enum lsm_vw_status {
+	LSM_VOICE_WAKEUP_STATUS_RUNNING = 1,
+	LSM_VOICE_WAKEUP_STATUS_DETECTED,
+	LSM_VOICE_WAKEUP_STATUS_END_SPEECH,
+	LSM_VOICE_WAKEUP_STATUS_REJECTED
+};
+
+enum LSM_PARAM_TYPE {
+	LSM_ENDPOINT_DETECT_THRESHOLD = 0,
+	LSM_OPERATION_MODE,
+	LSM_GAIN,
+	LSM_MIN_CONFIDENCE_LEVELS,
+	LSM_REG_SND_MODEL,
+	LSM_DEREG_SND_MODEL,
+	LSM_CUSTOM_PARAMS,
+	/* driver ioctl will parse only so many params */
+	LSM_PARAMS_MAX,
+};
+
+/*
+ * Data for LSM_ENDPOINT_DETECT_THRESHOLD param_type
+ * @epd_begin: Begin threshold
+ * @epd_end: End threshold
+ */
+struct snd_lsm_ep_det_thres {
+	__u32 epd_begin;
+	__u32 epd_end;
+};
+
+/*
+ * Data for LSM_OPERATION_MODE param_type
+ * @mode: The detection mode to be used
+ * @detect_failure: Setting to enable failure detections.
+ */
+struct snd_lsm_detect_mode {
+	enum lsm_detection_mode mode;
+	bool detect_failure;
+};
+
+/*
+ * Data for LSM_GAIN param_type
+ * @gain: The gain to be applied on LSM
+ */
+struct snd_lsm_gain {
+	__u16 gain;
+};
+
+
+struct snd_lsm_sound_model_v2 {
+	__u8 __user *data;
+	__u8 *confidence_level;
+	__u32 data_size;
+	enum lsm_detection_mode detection_mode;
+	__u8 num_confidence_levels;
+	bool detect_failure;
+};
+
+struct snd_lsm_session_data {
+	enum lsm_app_id app_id;
+};
+
+struct snd_lsm_event_status {
+	__u16 status;
+	__u16 payload_size;
+	__u8 payload[0];
+};
+
+struct snd_lsm_detection_params {
+	__u8 *conf_level;
+	enum lsm_detection_mode detect_mode;
+	__u8 num_confidence_levels;
+	bool detect_failure;
+};
+
+/*
+ * Param info for each parameter type
+ * @module_id: Module to which parameter is to be set
+ * @param_id: Parameter that is to be set
+ * @param_size: size (in number of bytes) for the data
+ *		in param_data.
+ *		For confidence levels, this is num_conf_levels
+ *		For REG_SND_MODEL, this is size of sound model
+ *		For CUSTOM_PARAMS, this is size of the entire blob of data
+ * @param_data: Data for the parameter.
+ *		For some param_types this is a structure defined, ex: LSM_GAIN
+ *		For CONFIDENCE_LEVELS, this is array of confidence levels
+ *		For REG_SND_MODEL, this is the sound model data
+ *		For CUSTOM_PARAMS, this is the blob of custom data.
+ */
+struct lsm_params_info {
+	__u32 module_id;
+	__u32 param_id;
+	__u32 param_size;
+	__u8 __user *param_data;
+	enum LSM_PARAM_TYPE param_type;
+};
+
+/*
+ * Data passed to the SET_PARAM_V2 IOCTL
+ * @num_params: Number of params that are to be set
+ *		should not be greater than LSM_PARAMS_MAX
+ * @params: Points to an array of lsm_params_info
+ *	    Each entry points to one parameter to set
+ * @data_size: size (in bytes) for params
+ *	       should be equal to
+ *	       num_params * sizeof(struct lsm_parms_info)
+ */
+struct snd_lsm_module_params {
+	__u8 __user *params;
+	__u32 num_params;
+	__u32 data_size;
+};
+
+/*
+ * Data passed to LSM_OUT_FORMAT_CFG IOCTL
+ * @format: The media format enum
+ * @packing: indicates the packing method used for data path
+ * @events: indicates whether data path events need to be enabled
+ * @transfer_mode: indicates whether FTRT mode or RT mode.
+ */
+struct snd_lsm_output_format_cfg {
+	__u8 format;
+	__u8 packing;
+	__u8 events;
+	__u8 mode;
+};
+
+#define SNDRV_LSM_DEREG_SND_MODEL _IOW('U', 0x01, int)
+#define SNDRV_LSM_EVENT_STATUS	_IOW('U', 0x02, struct snd_lsm_event_status)
+#define SNDRV_LSM_ABORT_EVENT	_IOW('U', 0x03, int)
+#define SNDRV_LSM_START		_IOW('U', 0x04, int)
+#define SNDRV_LSM_STOP		_IOW('U', 0x05, int)
+#define SNDRV_LSM_SET_SESSION_DATA _IOW('U', 0x06, struct snd_lsm_session_data)
+#define SNDRV_LSM_REG_SND_MODEL_V2 _IOW('U', 0x07,\
+					struct snd_lsm_sound_model_v2)
+#define SNDRV_LSM_LAB_CONTROL	_IOW('U', 0x08, uint32_t)
+#define SNDRV_LSM_STOP_LAB	_IO('U', 0x09)
+#define SNDRV_LSM_SET_PARAMS	_IOW('U', 0x0A, \
+					struct snd_lsm_detection_params)
+#define SNDRV_LSM_SET_MODULE_PARAMS	_IOW('U', 0x0B, \
+					struct snd_lsm_module_params)
+#define SNDRV_LSM_OUT_FORMAT_CFG _IOW('U', 0x0C, \
+				      struct snd_lsm_output_format_cfg)
+
+#endif
diff --git a/include/uapi/sound/msmcal-hwdep.h b/include/uapi/sound/msmcal-hwdep.h
new file mode 100644
index 0000000..2a29482
--- /dev/null
+++ b/include/uapi/sound/msmcal-hwdep.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _CALIB_HWDEP_H
+#define _CALIB_HWDEP_H
+
+#define WCD9XXX_CODEC_HWDEP_NODE    1000
+enum wcd_cal_type {
+	WCD9XXX_MIN_CAL,
+	WCD9XXX_ANC_CAL = WCD9XXX_MIN_CAL,
+	WCD9XXX_MAD_CAL,
+	WCD9XXX_MBHC_CAL,
+	WCD9XXX_VBAT_CAL,
+	WCD9XXX_MAX_CAL,
+};
+
+struct wcdcal_ioctl_buffer {
+	__u32 size;
+	__u8 __user *buffer;
+	enum wcd_cal_type cal_type;
+};
+
+#define SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE \
+	_IOW('U', 0x1, struct wcdcal_ioctl_buffer)
+
+#endif /*_CALIB_HWDEP_H*/
diff --git a/include/uapi/sound/wcd-dsp-glink.h b/include/uapi/sound/wcd-dsp-glink.h
new file mode 100644
index 0000000..39d128d
--- /dev/null
+++ b/include/uapi/sound/wcd-dsp-glink.h
@@ -0,0 +1,60 @@
+#ifndef _WCD_DSP_GLINK_H
+#define _WCD_DSP_GLINK_H
+
+#include <linux/types.h>
+
+#define WDSP_CH_NAME_MAX_LEN 50
+
+enum {
+	WDSP_REG_PKT = 1,
+	WDSP_CMD_PKT,
+	WDSP_READY_PKT,
+};
+#define WDSP_READY_PKT WDSP_READY_PKT
+
+/*
+ * struct wdsp_reg_pkt -  Glink channel information structure format
+ * @no_of_channels:   Number of glink channels to open
+ * @payload[0]:       Dynamic array contains all the glink channels information
+ */
+struct wdsp_reg_pkt {
+	__u8 no_of_channels;
+	__u8 payload[0];
+};
+
+/*
+ * struct wdsp_cmd_pkt - WDSP command packet format
+ * @ch_name:         Name of the glink channel
+ * @payload_size:    Size of the payload
+ * @payload[0]:      Actual data payload
+ */
+struct wdsp_cmd_pkt {
+	char ch_name[WDSP_CH_NAME_MAX_LEN];
+	__u32 payload_size;
+	__u8 payload[0];
+};
+
+/*
+ * struct wdsp_write_pkt - Format that userspace send the data to driver.
+ * @pkt_type:      Type of the packet(REG or CMD PKT)
+ * @payload[0]:    Payload is either cmd or reg pkt structure based on pkt type
+ */
+struct wdsp_write_pkt {
+	__u8 pkt_type;
+	__u8 payload[0];
+};
+
+/*
+ * struct wdsp_glink_ch_cfg - Defines the glink channel configuration.
+ * @ch_name:           Name of the glink channel
+ * @latency_in_us:     Latency specified in micro seconds for QOS
+ * @no_of_intents:     Number of intents prequeued
+ * @intents_size[0]:   Dynamic array to specify size of each intent
+ */
+struct wdsp_glink_ch_cfg {
+	char name[WDSP_CH_NAME_MAX_LEN];
+	__u32 latency_in_us;
+	__u32 no_of_intents;
+	__u32 intents_size[0];
+};
+#endif /* _WCD_DSP_GLINK_H */
diff --git a/init/Kconfig b/init/Kconfig
index f595c26..262fbd4 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -974,6 +974,23 @@
 config PAGE_COUNTER
        bool
 
+config CGROUP_SCHEDTUNE
+	bool "CFS tasks boosting cgroup subsystem (EXPERIMENTAL)"
+	depends on SCHED_TUNE
+	help
+	  This option provides the "schedtune" controller which improves the
+	  flexibility of the task boosting mechanism by introducing the support
+	  to define "per task" boost values.
+
+	  This new controller:
+	  1. allows only a two layers hierarchy, where the root defines the
+	     system-wide boost value and its direct childrens define each one a
+	     different "class of tasks" to be boosted with a different value
+	  2. supports up to 16 different task classes, each one which could be
+	     configured with a different boost value
+
+	  Say N if unsure.
+
 config MEMCG
 	bool "Memory controller"
 	select PAGE_COUNTER
@@ -1182,6 +1199,16 @@
 	  with CPUs C-state. If this is enabled, scheduler places tasks
 	  onto the shallowest C-state CPU among the most power efficient CPUs.
 
+config SCHED_CORE_CTL
+	bool "QTI Core Control"
+	depends on SMP
+	help
+	  This options enables the core control functionality in
+	  the scheduler. Core control automatically offline and
+	  online cores based on cpu load and utilization.
+
+	  If unsure, say N here.
+
 config CHECKPOINT_RESTORE
 	bool "Checkpoint/restore support" if EXPERT
 	select PROC_CHILDREN
@@ -1265,6 +1292,32 @@
 	  desktop applications.  Task group autogeneration is currently based
 	  upon task session.
 
+config SCHED_TUNE
+	bool "Boosting for CFS tasks (EXPERIMENTAL)"
+	help
+	  This option enables the system-wide support for task boosting.
+	  When this support is enabled a new sysctl interface is exposed to
+	  userspace via:
+	     /proc/sys/kernel/sched_cfs_boost
+	  which allows to set a system-wide boost value in range [0..100].
+
+	  The currently boosting strategy is implemented in such a way that:
+	  - a 0% boost value requires to operate in "standard" mode by
+	    scheduling all tasks at the minimum capacities required by their
+	    workload demand
+	  - a 100% boost value requires to push at maximum the task
+	    performances, "regardless" of the incurred energy consumption
+
+	  A boost value in between these two boundaries is used to bias the
+	  power/performance trade-off, the higher the boost value the more the
+	  scheduler is biased toward performance boosting instead of energy
+	  efficiency.
+
+	  Since this support exposes a single system-wide knob, the specified
+	  boost value is applied to all (CFS) tasks in the system.
+
+	  If unsure, say N.
+
 config SYSFS_DEPRECATED
 	bool "Enable deprecated sysfs features to support old userspace tools"
 	depends on SYSFS
diff --git a/init/main.c b/init/main.c
index 2858be7..c91ca2c 100644
--- a/init/main.c
+++ b/init/main.c
@@ -485,11 +485,6 @@ asmlinkage __visible void __init start_kernel(void)
 	smp_setup_processor_id();
 	debug_objects_early_init();
 
-	/*
-	 * Set up the the initial canary ASAP:
-	 */
-	boot_init_stack_canary();
-
 	cgroup_init_early();
 
 	local_irq_disable();
@@ -503,6 +498,10 @@ asmlinkage __visible void __init start_kernel(void)
 	page_address_init();
 	pr_notice("%s", linux_banner);
 	setup_arch(&command_line);
+	/*
+	 * Set up the the initial canary ASAP:
+	 */
+	boot_init_stack_canary();
 	mm_init_cpumask(&init_mm);
 	setup_command_line(command_line);
 	setup_nr_cpu_ids();
diff --git a/kernel/cpu.c b/kernel/cpu.c
index 19444fc..2918a9a 100644
--- a/kernel/cpu.c
+++ b/kernel/cpu.c
@@ -1903,6 +1903,9 @@ EXPORT_SYMBOL(__cpu_present_mask);
 struct cpumask __cpu_active_mask __read_mostly;
 EXPORT_SYMBOL(__cpu_active_mask);
 
+struct cpumask __cpu_isolated_mask __read_mostly;
+EXPORT_SYMBOL(__cpu_isolated_mask);
+
 void init_cpu_present(const struct cpumask *src)
 {
 	cpumask_copy(&__cpu_present_mask, src);
@@ -1918,6 +1921,11 @@ void init_cpu_online(const struct cpumask *src)
 	cpumask_copy(&__cpu_online_mask, src);
 }
 
+void init_cpu_isolated(const struct cpumask *src)
+{
+	cpumask_copy(&__cpu_isolated_mask, src);
+}
+
 /*
  * Activate the first processor.
  */
diff --git a/kernel/events/core.c b/kernel/events/core.c
index ede107c..9048830 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -3586,7 +3586,8 @@ static int perf_event_read(struct perf_event *event, bool group)
 	 * If event is enabled and currently active on a CPU, update the
 	 * value in the event structure:
 	 */
-	if (event->state == PERF_EVENT_STATE_ACTIVE) {
+	if (event->state == PERF_EVENT_STATE_ACTIVE &&
+						!cpu_isolated(event->oncpu)) {
 		struct perf_read_data data = {
 			.event = event,
 			.group = group,
diff --git a/kernel/fork.c b/kernel/fork.c
index 27e9af6..23f9d08 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1857,6 +1857,7 @@ static __latent_entropy struct task_struct *copy_process(
 bad_fork_cleanup_perf:
 	perf_event_free_task(p);
 bad_fork_cleanup_policy:
+	free_task_load_ptrs(p);
 #ifdef CONFIG_NUMA
 	mpol_put(p->mempolicy);
 bad_fork_cleanup_threadgroup_lock:
@@ -1890,7 +1891,7 @@ struct task_struct *fork_idle(int cpu)
 			    cpu_to_node(cpu));
 	if (!IS_ERR(task)) {
 		init_idle_pids(task->pids);
-		init_idle(task, cpu);
+		init_idle(task, cpu, false);
 	}
 
 	return task;
diff --git a/kernel/irq/cpuhotplug.c b/kernel/irq/cpuhotplug.c
index 011f8c4..104432f 100644
--- a/kernel/irq/cpuhotplug.c
+++ b/kernel/irq/cpuhotplug.c
@@ -11,6 +11,7 @@
 #include <linux/interrupt.h>
 #include <linux/ratelimit.h>
 #include <linux/irq.h>
+#include <linux/cpumask.h>
 
 #include "internals.h"
 
@@ -20,6 +21,7 @@ static bool migrate_one_irq(struct irq_desc *desc)
 	const struct cpumask *affinity = d->common->affinity;
 	struct irq_chip *c;
 	bool ret = false;
+	struct cpumask available_cpus;
 
 	/*
 	 * If this is a per-CPU interrupt, or the affinity does not
@@ -29,8 +31,15 @@ static bool migrate_one_irq(struct irq_desc *desc)
 	    !cpumask_test_cpu(smp_processor_id(), affinity))
 		return false;
 
+	cpumask_copy(&available_cpus, affinity);
+	cpumask_andnot(&available_cpus, &available_cpus, cpu_isolated_mask);
+	affinity = &available_cpus;
+
 	if (cpumask_any_and(affinity, cpu_online_mask) >= nr_cpu_ids) {
-		affinity = cpu_online_mask;
+		cpumask_andnot(&available_cpus, cpu_online_mask,
+							cpu_isolated_mask);
+		if (cpumask_empty(affinity))
+			affinity = cpu_online_mask;
 		ret = true;
 	}
 
diff --git a/kernel/power/qos.c b/kernel/power/qos.c
index 311c14f..22d67f0 100644
--- a/kernel/power/qos.c
+++ b/kernel/power/qos.c
@@ -45,6 +45,7 @@
 #include <linux/seq_file.h>
 #include <linux/irq.h>
 #include <linux/irqdesc.h>
+#include <linux/cpumask.h>
 
 #include <linux/uaccess.h>
 #include <linux/export.h>
@@ -438,6 +439,9 @@ EXPORT_SYMBOL_GPL(pm_qos_request);
 
 int pm_qos_request_for_cpu(int pm_qos_class, int cpu)
 {
+	if (cpu_isolated(cpu))
+		return INT_MAX;
+
 	return pm_qos_array[pm_qos_class]->constraints->target_per_cpu[cpu];
 }
 EXPORT_SYMBOL(pm_qos_request_for_cpu);
@@ -460,6 +464,9 @@ int pm_qos_request_for_cpumask(int pm_qos_class, struct cpumask *mask)
 	val = c->default_value;
 
 	for_each_cpu(cpu, mask) {
+		if (cpu_isolated(cpu))
+			continue;
+
 		switch (c->type) {
 		case PM_QOS_MIN:
 			if (c->target_per_cpu[cpu] < val)
diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile
index 1302fff..11cb1b2 100644
--- a/kernel/sched/Makefile
+++ b/kernel/sched/Makefile
@@ -19,10 +19,12 @@
 obj-y += idle_task.o fair.o rt.o deadline.o stop_task.o
 obj-y += wait.o swait.o completion.o idle.o sched_avg.o
 obj-$(CONFIG_SMP) += cpupri.o cpudeadline.o
-obj-$(CONFIG_SCHED_HMP) += hmp.o
+obj-$(CONFIG_SCHED_HMP) += hmp.o boost.o
 obj-$(CONFIG_SCHED_AUTOGROUP) += auto_group.o
 obj-$(CONFIG_SCHEDSTATS) += stats.o
 obj-$(CONFIG_SCHED_DEBUG) += debug.o
+obj-$(CONFIG_SCHED_TUNE) += tune.o
 obj-$(CONFIG_CGROUP_CPUACCT) += cpuacct.o
 obj-$(CONFIG_CPU_FREQ) += cpufreq.o
 obj-$(CONFIG_CPU_FREQ_GOV_SCHEDUTIL) += cpufreq_schedutil.o
+obj-$(CONFIG_SCHED_CORE_CTL) += core_ctl.o
diff --git a/kernel/sched/boost.c b/kernel/sched/boost.c
new file mode 100644
index 0000000..5bdd51b
--- /dev/null
+++ b/kernel/sched/boost.c
@@ -0,0 +1,217 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "sched.h"
+#include <linux/of.h>
+#include <linux/sched/core_ctl.h>
+#include <trace/events/sched.h>
+
+/*
+ * Scheduler boost is a mechanism to temporarily place tasks on CPUs
+ * with higher capacity than those where a task would have normally
+ * ended up with their load characteristics. Any entity enabling
+ * boost is responsible for disabling it as well.
+ */
+
+unsigned int sysctl_sched_boost;
+static enum sched_boost_policy boost_policy;
+static enum sched_boost_policy boost_policy_dt = SCHED_BOOST_NONE;
+static DEFINE_MUTEX(boost_mutex);
+static unsigned int freq_aggr_threshold_backup;
+
+static inline void boost_kick(int cpu)
+{
+	struct rq *rq = cpu_rq(cpu);
+
+	if (!test_and_set_bit(BOOST_KICK, &rq->hmp_flags))
+		smp_send_reschedule(cpu);
+}
+
+static void boost_kick_cpus(void)
+{
+	int i;
+	struct cpumask kick_mask;
+
+	if (boost_policy != SCHED_BOOST_ON_BIG)
+		return;
+
+	cpumask_andnot(&kick_mask, cpu_online_mask, cpu_isolated_mask);
+
+	for_each_cpu(i, &kick_mask) {
+		if (cpu_capacity(i) != max_capacity)
+			boost_kick(i);
+	}
+}
+
+int got_boost_kick(void)
+{
+	int cpu = smp_processor_id();
+	struct rq *rq = cpu_rq(cpu);
+
+	return test_bit(BOOST_KICK, &rq->hmp_flags);
+}
+
+void clear_boost_kick(int cpu)
+{
+	struct rq *rq = cpu_rq(cpu);
+
+	clear_bit(BOOST_KICK, &rq->hmp_flags);
+}
+
+/*
+ * Scheduler boost type and boost policy might at first seem unrelated,
+ * however, there exists a connection between them that will allow us
+ * to use them interchangeably during placement decisions. We'll explain
+ * the connection here in one possible way so that the implications are
+ * clear when looking at placement policies.
+ *
+ * When policy = SCHED_BOOST_NONE, type is either none or RESTRAINED
+ * When policy = SCHED_BOOST_ON_ALL or SCHED_BOOST_ON_BIG, type can
+ * neither be none nor RESTRAINED.
+ */
+static void set_boost_policy(int type)
+{
+	if (type == SCHED_BOOST_NONE || type == RESTRAINED_BOOST) {
+		boost_policy = SCHED_BOOST_NONE;
+		return;
+	}
+
+	if (boost_policy_dt) {
+		boost_policy = boost_policy_dt;
+		return;
+	}
+
+	if (min_possible_efficiency != max_possible_efficiency) {
+		boost_policy = SCHED_BOOST_ON_BIG;
+		return;
+	}
+
+	boost_policy = SCHED_BOOST_ON_ALL;
+}
+
+enum sched_boost_policy sched_boost_policy(void)
+{
+	return boost_policy;
+}
+
+static bool verify_boost_params(int old_val, int new_val)
+{
+	/*
+	 * Boost can only be turned on or off. There is no possiblity of
+	 * switching from one boost type to another or to set the same
+	 * kind of boost several times.
+	 */
+	return !(!!old_val == !!new_val);
+}
+
+static void _sched_set_boost(int old_val, int type)
+{
+	switch (type) {
+	case NO_BOOST:
+		if (old_val == FULL_THROTTLE_BOOST)
+			core_ctl_set_boost(false);
+		else if (old_val == CONSERVATIVE_BOOST)
+			restore_cgroup_boost_settings();
+		else
+			update_freq_aggregate_threshold(
+				freq_aggr_threshold_backup);
+		break;
+
+	case FULL_THROTTLE_BOOST:
+		core_ctl_set_boost(true);
+		boost_kick_cpus();
+		break;
+
+	case CONSERVATIVE_BOOST:
+		update_cgroup_boost_settings();
+		boost_kick_cpus();
+		break;
+
+	case RESTRAINED_BOOST:
+		freq_aggr_threshold_backup =
+			update_freq_aggregate_threshold(1);
+		break;
+
+	default:
+		WARN_ON(1);
+		return;
+	}
+
+	set_boost_policy(type);
+	sysctl_sched_boost = type;
+	trace_sched_set_boost(type);
+}
+
+void sched_boost_parse_dt(void)
+{
+	struct device_node *sn;
+	const char *boost_policy;
+
+	sn = of_find_node_by_path("/sched-hmp");
+	if (!sn)
+		return;
+
+	if (!of_property_read_string(sn, "boost-policy", &boost_policy)) {
+		if (!strcmp(boost_policy, "boost-on-big"))
+			boost_policy_dt = SCHED_BOOST_ON_BIG;
+		else if (!strcmp(boost_policy, "boost-on-all"))
+			boost_policy_dt = SCHED_BOOST_ON_ALL;
+	}
+}
+
+int sched_set_boost(int type)
+{
+	int ret = 0;
+
+	mutex_lock(&boost_mutex);
+
+	if (verify_boost_params(sysctl_sched_boost, type))
+		_sched_set_boost(sysctl_sched_boost, type);
+	else
+		ret = -EINVAL;
+
+	mutex_unlock(&boost_mutex);
+	return ret;
+}
+
+int sched_boost_handler(struct ctl_table *table, int write,
+		void __user *buffer, size_t *lenp,
+		loff_t *ppos)
+{
+	int ret;
+	unsigned int *data = (unsigned int *)table->data;
+	unsigned int old_val;
+
+	mutex_lock(&boost_mutex);
+
+	old_val = *data;
+	ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+	if (ret || !write)
+		goto done;
+
+	if (verify_boost_params(old_val, *data)) {
+		_sched_set_boost(old_val, *data);
+	} else {
+		*data = old_val;
+		ret = -EINVAL;
+	}
+
+done:
+	mutex_unlock(&boost_mutex);
+	return ret;
+}
+
+int sched_boost(void)
+{
+	return sysctl_sched_boost;
+}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index dc545a5..3c8d4d7f 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -75,6 +75,8 @@
 #include <linux/compiler.h>
 #include <linux/frame.h>
 #include <linux/prefetch.h>
+#include <linux/irq.h>
+#include <linux/sched/core_ctl.h>
 
 #include <asm/switch_to.h>
 #include <asm/tlb.h>
@@ -1152,6 +1154,7 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 	struct rq_flags rf;
 	struct rq *rq;
 	int ret = 0;
+	cpumask_t allowed_mask;
 
 	rq = task_rq_lock(p, &rf);
 
@@ -1174,10 +1177,17 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 	if (cpumask_equal(&p->cpus_allowed, new_mask))
 		goto out;
 
-	dest_cpu = cpumask_any_and(cpu_valid_mask, new_mask);
+	cpumask_andnot(&allowed_mask, new_mask, cpu_isolated_mask);
+	cpumask_and(&allowed_mask, &allowed_mask, cpu_valid_mask);
+
+	dest_cpu = cpumask_any(&allowed_mask);
 	if (dest_cpu >= nr_cpu_ids) {
-		ret = -EINVAL;
-		goto out;
+		cpumask_and(&allowed_mask, cpu_valid_mask, new_mask);
+		dest_cpu = cpumask_any(&allowed_mask);
+		if (dest_cpu >= nr_cpu_ids) {
+			ret = -EINVAL;
+			goto out;
+		}
 	}
 
 	do_set_cpus_allowed(p, new_mask);
@@ -1193,7 +1203,7 @@ static int __set_cpus_allowed_ptr(struct task_struct *p,
 	}
 
 	/* Can the task run on the task's current CPU? If so, we're done */
-	if (cpumask_test_cpu(task_cpu(p), new_mask))
+	if (cpumask_test_cpu(task_cpu(p), &allowed_mask))
 		goto out;
 
 	dest_cpu = cpumask_any_and(cpu_valid_mask, new_mask);
@@ -1537,12 +1547,13 @@ EXPORT_SYMBOL_GPL(kick_process);
  * select_task_rq() below may allow selection of !active CPUs in order
  * to satisfy the above rules.
  */
-static int select_fallback_rq(int cpu, struct task_struct *p)
+static int select_fallback_rq(int cpu, struct task_struct *p, bool allow_iso)
 {
 	int nid = cpu_to_node(cpu);
 	const struct cpumask *nodemask = NULL;
-	enum { cpuset, possible, fail } state = cpuset;
+	enum { cpuset, possible, fail, bug } state = cpuset;
 	int dest_cpu;
+	int isolated_candidate = -1;
 
 	/*
 	 * If the node that the cpu is on has been offlined, cpu_to_node()
@@ -1556,6 +1567,8 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 		for_each_cpu(dest_cpu, nodemask) {
 			if (!cpu_active(dest_cpu))
 				continue;
+			if (cpu_isolated(dest_cpu))
+				continue;
 			if (cpumask_test_cpu(dest_cpu, tsk_cpus_allowed(p)))
 				return dest_cpu;
 		}
@@ -1568,6 +1581,16 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 				continue;
 			if (!cpu_online(dest_cpu))
 				continue;
+			if (cpu_isolated(dest_cpu)) {
+				if (allow_iso)
+					isolated_candidate = dest_cpu;
+				continue;
+			}
+			goto out;
+		}
+
+		if (isolated_candidate != -1) {
+			dest_cpu = isolated_candidate;
 			goto out;
 		}
 
@@ -1586,6 +1609,11 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 			break;
 
 		case fail:
+			allow_iso = true;
+			state = bug;
+			break;
+
+		case bug:
 			BUG();
 			break;
 		}
@@ -1613,6 +1641,8 @@ static int select_fallback_rq(int cpu, struct task_struct *p)
 static inline
 int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags)
 {
+	bool allow_isolated = (p->flags & PF_KTHREAD);
+
 	lockdep_assert_held(&p->pi_lock);
 
 	if (tsk_nr_cpus_allowed(p) > 1)
@@ -1631,13 +1661,14 @@ int select_task_rq(struct task_struct *p, int cpu, int sd_flags, int wake_flags)
 	 *   not worry about this generic constraint ]
 	 */
 	if (unlikely(!cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) ||
-		     !cpu_online(cpu)))
-		cpu = select_fallback_rq(task_cpu(p), p);
+		     !cpu_online(cpu)) ||
+		     (cpu_isolated(cpu) && !allow_isolated))
+		cpu = select_fallback_rq(task_cpu(p), p, allow_isolated);
 
 	return cpu;
 }
 
-static void update_avg(u64 *avg, u64 sample)
+void update_avg(u64 *avg, u64 sample)
 {
 	s64 diff = sample - *avg;
 	*avg += diff >> 3;
@@ -1854,7 +1885,7 @@ void scheduler_ipi(void)
 	/*
 	 * Check if someone kicked us for doing the nohz idle load balance.
 	 */
-	if (unlikely(got_nohz_idle_kick())) {
+	if (unlikely(got_nohz_idle_kick()) && !cpu_isolated(cpu)) {
 		this_rq()->idle_balance = 1;
 		raise_softirq_irqoff(SCHED_SOFTIRQ);
 	}
@@ -2147,7 +2178,7 @@ try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
 		notif_required = true;
 	}
 
-	set_task_last_wake(p, wallclock);
+	note_task_waking(p, wallclock);
 #endif /* CONFIG_SMP */
 
 	ttwu_queue(p, cpu, wake_flags);
@@ -2220,7 +2251,7 @@ static void try_to_wake_up_local(struct task_struct *p, struct pin_cookie cookie
 		update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
 		update_task_ravg(p, rq, TASK_WAKE, wallclock, 0);
 		ttwu_activate(rq, p, ENQUEUE_WAKEUP);
-		set_task_last_wake(p, wallclock);
+		note_task_waking(p, wallclock);
 	}
 
 	ttwu_do_wakeup(rq, p, 0, cookie);
@@ -2304,14 +2335,14 @@ void __dl_clear_params(struct task_struct *p)
  */
 void sched_exit(struct task_struct *p)
 {
-	unsigned long flags;
-	int cpu = get_cpu();
-	struct rq *rq = cpu_rq(cpu);
+	struct rq_flags rf;
+	struct rq *rq;
 	u64 wallclock;
 
 	sched_set_group_id(p, 0);
 
-	raw_spin_lock_irqsave(&rq->lock, flags);
+	rq = task_rq_lock(p, &rf);
+
 	/* rq->curr == p */
 	wallclock = sched_ktime_clock();
 	update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
@@ -2319,11 +2350,11 @@ void sched_exit(struct task_struct *p)
 	reset_task_stats(p);
 	p->ravg.mark_start = wallclock;
 	p->ravg.sum_history[0] = EXITING_TASK_MARKER;
+	free_task_load_ptrs(p);
+
 	enqueue_task(rq, p, 0);
 	clear_ed_task(p, rq);
-	raw_spin_unlock_irqrestore(&rq->lock, flags);
-
-	put_cpu();
+	task_rq_unlock(rq, p, &rf);
 }
 #endif /* CONFIG_SCHED_HMP */
 
@@ -2509,7 +2540,10 @@ static inline void init_schedstats(void) {}
 int sched_fork(unsigned long clone_flags, struct task_struct *p)
 {
 	unsigned long flags;
-	int cpu = get_cpu();
+	int cpu;
+
+	init_new_task_load(p, false);
+	cpu = get_cpu();
 
 	__sched_fork(clone_flags, p);
 	/*
@@ -2702,9 +2736,8 @@ void wake_up_new_task(struct task_struct *p)
 	struct rq_flags rf;
 	struct rq *rq;
 
-	raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
-	init_new_task_load(p);
 	add_new_task_to_grp(p);
+	raw_spin_lock_irqsave(&p->pi_lock, rf.flags);
 	p->state = TASK_RUNNING;
 #ifdef CONFIG_SMP
 	/*
@@ -3130,7 +3163,7 @@ void sched_exec(void)
 	if (dest_cpu == smp_processor_id())
 		goto unlock;
 
-	if (likely(cpu_active(dest_cpu))) {
+	if (likely(cpu_active(dest_cpu) && likely(!cpu_isolated(dest_cpu)))) {
 		struct migration_arg arg = { p, dest_cpu };
 
 		raw_spin_unlock_irqrestore(&p->pi_lock, flags);
@@ -3261,6 +3294,8 @@ void scheduler_tick(void)
 
 	if (curr->sched_class == &fair_sched_class)
 		check_for_migration(rq, curr);
+
+	core_ctl_check(wallclock);
 }
 
 #ifdef CONFIG_NO_HZ_FULL
@@ -3561,15 +3596,17 @@ static void __sched notrace __schedule(bool preempt)
 
 	next = pick_next_task(rq, prev, cookie);
 
-	wallclock = sched_ktime_clock();
-	update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0);
-	update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0);
-
 	clear_tsk_need_resched(prev);
 	clear_preempt_need_resched();
 	rq->clock_skip_update = 0;
 
+	wallclock = sched_ktime_clock();
 	if (likely(prev != next)) {
+		update_task_ravg(prev, rq, PUT_PREV_TASK, wallclock, 0);
+		update_task_ravg(next, rq, PICK_NEXT_TASK, wallclock, 0);
+		if (!is_idle_task(prev) && !prev->on_rq)
+			update_avg_burst(prev);
+
 		rq->nr_switches++;
 		rq->curr = next;
 		++*switch_count;
@@ -3579,6 +3616,7 @@ static void __sched notrace __schedule(bool preempt)
 		trace_sched_switch(preempt, prev, next);
 		rq = context_switch(rq, prev, next, cookie); /* unlocks the rq */
 	} else {
+		update_task_ravg(prev, rq, TASK_UPDATE, wallclock, 0);
 		lockdep_unpin_lock(&rq->lock, cookie);
 		raw_spin_unlock_irq(&rq->lock);
 	}
@@ -4865,6 +4903,8 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
 	cpumask_var_t cpus_allowed, new_mask;
 	struct task_struct *p;
 	int retval;
+	int dest_cpu;
+	cpumask_t allowed_mask;
 
 	rcu_read_lock();
 
@@ -4926,20 +4966,26 @@ long sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
 	}
 #endif
 again:
-	retval = __set_cpus_allowed_ptr(p, new_mask, true);
-
-	if (!retval) {
-		cpuset_cpus_allowed(p, cpus_allowed);
-		if (!cpumask_subset(new_mask, cpus_allowed)) {
-			/*
-			 * We must have raced with a concurrent cpuset
-			 * update. Just reset the cpus_allowed to the
-			 * cpuset's cpus_allowed
-			 */
-			cpumask_copy(new_mask, cpus_allowed);
-			goto again;
+	cpumask_andnot(&allowed_mask, new_mask, cpu_isolated_mask);
+	dest_cpu = cpumask_any_and(cpu_active_mask, &allowed_mask);
+	if (dest_cpu < nr_cpu_ids) {
+		retval = __set_cpus_allowed_ptr(p, new_mask, true);
+		if (!retval) {
+			cpuset_cpus_allowed(p, cpus_allowed);
+			if (!cpumask_subset(new_mask, cpus_allowed)) {
+				/*
+				 * We must have raced with a concurrent cpuset
+				 * update. Just reset the cpus_allowed to the
+				 * cpuset's cpus_allowed
+				 */
+				cpumask_copy(new_mask, cpus_allowed);
+				goto again;
+			}
 		}
+	} else {
+		retval = -EINVAL;
 	}
+
 out_free_new_mask:
 	free_cpumask_var(new_mask);
 out_free_cpus_allowed:
@@ -5442,17 +5488,21 @@ void init_idle_bootup_task(struct task_struct *idle)
  * init_idle - set up an idle thread for a given CPU
  * @idle: task in question
  * @cpu: cpu the idle task belongs to
+ * @cpu_up: differentiate between initial boot vs hotplug
  *
  * NOTE: this function does not set the idle thread's NEED_RESCHED
  * flag, to make booting more robust.
  */
-void init_idle(struct task_struct *idle, int cpu)
+void init_idle(struct task_struct *idle, int cpu, bool cpu_up)
 {
 	struct rq *rq = cpu_rq(cpu);
 	unsigned long flags;
 
 	__sched_fork(0, idle);
 
+	if (!cpu_up)
+		init_new_task_load(idle, true);
+
 	raw_spin_lock_irqsave(&idle->pi_lock, flags);
 	raw_spin_lock(&rq->lock);
 
@@ -5687,19 +5737,55 @@ static struct task_struct fake_task = {
 };
 
 /*
- * Migrate all tasks from the rq, sleeping tasks will be migrated by
- * try_to_wake_up()->select_task_rq().
+ * Remove a task from the runqueue and pretend that it's migrating. This
+ * should prevent migrations for the detached task and disallow further
+ * changes to tsk_cpus_allowed.
+ */
+static void
+detach_one_task(struct task_struct *p, struct rq *rq, struct list_head *tasks)
+{
+	lockdep_assert_held(&rq->lock);
+
+	p->on_rq = TASK_ON_RQ_MIGRATING;
+	deactivate_task(rq, p, 0);
+	list_add(&p->se.group_node, tasks);
+}
+
+static void attach_tasks(struct list_head *tasks, struct rq *rq)
+{
+	struct task_struct *p;
+
+	lockdep_assert_held(&rq->lock);
+
+	while (!list_empty(tasks)) {
+		p = list_first_entry(tasks, struct task_struct, se.group_node);
+		list_del_init(&p->se.group_node);
+
+		BUG_ON(task_rq(p) != rq);
+		activate_task(rq, p, 0);
+		p->on_rq = TASK_ON_RQ_QUEUED;
+	}
+}
+
+/*
+ * Migrate all tasks (not pinned if pinned argument say so) from the rq,
+ * sleeping tasks will be migrated by try_to_wake_up()->select_task_rq().
  *
  * Called with rq->lock held even though we'er in stop_machine() and
  * there's no concurrency possible, we hold the required locks anyway
  * because of lock validation efforts.
  */
-static void migrate_tasks(struct rq *dead_rq)
+static void migrate_tasks(struct rq *dead_rq, bool migrate_pinned_tasks)
 {
 	struct rq *rq = dead_rq;
 	struct task_struct *next, *stop = rq->stop;
 	struct pin_cookie cookie;
 	int dest_cpu;
+	unsigned int num_pinned_kthreads = 1; /* this thread */
+	LIST_HEAD(tasks);
+	cpumask_t avail_cpus;
+
+	cpumask_andnot(&avail_cpus, cpu_online_mask, cpu_isolated_mask);
 
 	/*
 	 * Fudge the rq selection such that the below task selection loop
@@ -5735,6 +5821,14 @@ static void migrate_tasks(struct rq *dead_rq)
 		BUG_ON(!next);
 		next->sched_class->put_prev_task(rq, next);
 
+		if (!migrate_pinned_tasks && next->flags & PF_KTHREAD &&
+			!cpumask_intersects(&avail_cpus, &next->cpus_allowed)) {
+			detach_one_task(next, rq, &tasks);
+			num_pinned_kthreads += 1;
+			lockdep_unpin_lock(&rq->lock, cookie);
+			continue;
+		}
+
 		/*
 		 * Rules for changing task_struct::cpus_allowed are holding
 		 * both pi_lock and rq->lock, such that holding either
@@ -5753,14 +5847,18 @@ static void migrate_tasks(struct rq *dead_rq)
 		 * Since we're inside stop-machine, _nothing_ should have
 		 * changed the task, WARN if weird stuff happened, because in
 		 * that case the above rq->lock drop is a fail too.
+		 * However, during cpu isolation the load balancer might have
+		 * interferred since we don't stop all CPUs. Ignore warning for
+		 * this case.
 		 */
-		if (WARN_ON(task_rq(next) != rq || !task_on_rq_queued(next))) {
+		if (task_rq(next) != rq || !task_on_rq_queued(next)) {
+			WARN_ON(migrate_pinned_tasks);
 			raw_spin_unlock(&next->pi_lock);
 			continue;
 		}
 
 		/* Find suitable destination for @next, with force if needed. */
-		dest_cpu = select_fallback_rq(dead_rq->cpu, next);
+		dest_cpu = select_fallback_rq(dead_rq->cpu, next, false);
 
 		rq = __migrate_task(rq, next, dest_cpu);
 		if (rq != dead_rq) {
@@ -5775,7 +5873,245 @@ static void migrate_tasks(struct rq *dead_rq)
 	}
 
 	rq->stop = stop;
+
+	if (num_pinned_kthreads > 1)
+		attach_tasks(&tasks, rq);
 }
+
+static void set_rq_online(struct rq *rq);
+static void set_rq_offline(struct rq *rq);
+
+int do_isolation_work_cpu_stop(void *data)
+{
+	unsigned int cpu = smp_processor_id();
+	struct rq *rq = cpu_rq(cpu);
+
+	watchdog_disable(cpu);
+
+	irq_migrate_all_off_this_cpu();
+
+	local_irq_disable();
+
+	sched_ttwu_pending();
+
+	raw_spin_lock(&rq->lock);
+
+	/*
+	 * Temporarily mark the rq as offline. This will allow us to
+	 * move tasks off the CPU.
+	 */
+	if (rq->rd) {
+		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
+		set_rq_offline(rq);
+	}
+
+	migrate_tasks(rq, false);
+
+	if (rq->rd)
+		set_rq_online(rq);
+	raw_spin_unlock(&rq->lock);
+
+	/*
+	 * We might have been in tickless state. Clear NOHZ flags to avoid
+	 * us being kicked for helping out with balancing
+	 */
+	nohz_balance_clear_nohz_mask(cpu);
+
+	clear_hmp_request(cpu);
+	local_irq_enable();
+	return 0;
+}
+
+int do_unisolation_work_cpu_stop(void *data)
+{
+	watchdog_enable(smp_processor_id());
+	return 0;
+}
+
+static void init_sched_groups_capacity(int cpu, struct sched_domain *sd);
+
+static void sched_update_group_capacities(int cpu)
+{
+	struct sched_domain *sd;
+
+	mutex_lock(&sched_domains_mutex);
+	rcu_read_lock();
+
+	for_each_domain(cpu, sd) {
+		int balance_cpu = group_balance_cpu(sd->groups);
+
+		init_sched_groups_capacity(cpu, sd);
+		/*
+		 * Need to ensure this is also called with balancing
+		 * cpu.
+		*/
+		if (cpu != balance_cpu)
+			init_sched_groups_capacity(balance_cpu, sd);
+	}
+
+	rcu_read_unlock();
+	mutex_unlock(&sched_domains_mutex);
+}
+
+static unsigned int cpu_isolation_vote[NR_CPUS];
+
+int sched_isolate_count(const cpumask_t *mask, bool include_offline)
+{
+	cpumask_t count_mask = CPU_MASK_NONE;
+
+	if (include_offline) {
+		cpumask_complement(&count_mask, cpu_online_mask);
+		cpumask_or(&count_mask, &count_mask, cpu_isolated_mask);
+		cpumask_and(&count_mask, &count_mask, mask);
+	} else {
+		cpumask_and(&count_mask, mask, cpu_isolated_mask);
+	}
+
+	return cpumask_weight(&count_mask);
+}
+
+/*
+ * 1) CPU is isolated and cpu is offlined:
+ *	Unisolate the core.
+ * 2) CPU is not isolated and CPU is offlined:
+ *	No action taken.
+ * 3) CPU is offline and request to isolate
+ *	Request ignored.
+ * 4) CPU is offline and isolated:
+ *	Not a possible state.
+ * 5) CPU is online and request to isolate
+ *	Normal case: Isolate the CPU
+ * 6) CPU is not isolated and comes back online
+ *	Nothing to do
+ *
+ * Note: The client calling sched_isolate_cpu() is repsonsible for ONLY
+ * calling sched_unisolate_cpu() on a CPU that the client previously isolated.
+ * Client is also responsible for unisolating when a core goes offline
+ * (after CPU is marked offline).
+ */
+int sched_isolate_cpu(int cpu)
+{
+	struct rq *rq = cpu_rq(cpu);
+	cpumask_t avail_cpus;
+	int ret_code = 0;
+	u64 start_time = 0;
+
+	if (trace_sched_isolate_enabled())
+		start_time = sched_clock();
+
+	cpu_maps_update_begin();
+
+	cpumask_andnot(&avail_cpus, cpu_online_mask, cpu_isolated_mask);
+
+	/* We cannot isolate ALL cpus in the system */
+	if (cpumask_weight(&avail_cpus) == 1) {
+		ret_code = -EINVAL;
+		goto out;
+	}
+
+	if (!cpu_online(cpu)) {
+		ret_code = -EINVAL;
+		goto out;
+	}
+
+	if (++cpu_isolation_vote[cpu] > 1)
+		goto out;
+
+	/*
+	 * There is a race between watchdog being enabled by hotplug and
+	 * core isolation disabling the watchdog. When a CPU is hotplugged in
+	 * and the hotplug lock has been released the watchdog thread might
+	 * not have run yet to enable the watchdog.
+	 * We have to wait for the watchdog to be enabled before proceeding.
+	 */
+	if (!watchdog_configured(cpu)) {
+		msleep(20);
+		if (!watchdog_configured(cpu)) {
+			--cpu_isolation_vote[cpu];
+			ret_code = -EBUSY;
+			goto out;
+		}
+	}
+
+	set_cpu_isolated(cpu, true);
+	cpumask_clear_cpu(cpu, &avail_cpus);
+
+	/* Migrate timers */
+	smp_call_function_any(&avail_cpus, hrtimer_quiesce_cpu, &cpu, 1);
+	smp_call_function_any(&avail_cpus, timer_quiesce_cpu, &cpu, 1);
+
+	stop_cpus(cpumask_of(cpu), do_isolation_work_cpu_stop, 0);
+
+	calc_load_migrate(rq);
+	update_max_interval();
+	sched_update_group_capacities(cpu);
+
+out:
+	cpu_maps_update_done();
+	trace_sched_isolate(cpu, cpumask_bits(cpu_isolated_mask)[0],
+			    start_time, 1);
+	return ret_code;
+}
+
+/*
+ * Note: The client calling sched_isolate_cpu() is repsonsible for ONLY
+ * calling sched_unisolate_cpu() on a CPU that the client previously isolated.
+ * Client is also responsible for unisolating when a core goes offline
+ * (after CPU is marked offline).
+ */
+int sched_unisolate_cpu_unlocked(int cpu)
+{
+	int ret_code = 0;
+	struct rq *rq = cpu_rq(cpu);
+	u64 start_time = 0;
+
+	if (trace_sched_isolate_enabled())
+		start_time = sched_clock();
+
+	if (!cpu_isolation_vote[cpu]) {
+		ret_code = -EINVAL;
+		goto out;
+	}
+
+	if (--cpu_isolation_vote[cpu])
+		goto out;
+
+	if (cpu_online(cpu)) {
+		unsigned long flags;
+
+		raw_spin_lock_irqsave(&rq->lock, flags);
+		rq->age_stamp = sched_clock_cpu(cpu);
+		raw_spin_unlock_irqrestore(&rq->lock, flags);
+	}
+
+	set_cpu_isolated(cpu, false);
+	update_max_interval();
+	sched_update_group_capacities(cpu);
+
+	if (cpu_online(cpu)) {
+		stop_cpus(cpumask_of(cpu), do_unisolation_work_cpu_stop, 0);
+
+		/* Kick CPU to immediately do load balancing */
+		if (!test_and_set_bit(NOHZ_BALANCE_KICK, nohz_flags(cpu)))
+			smp_send_reschedule(cpu);
+	}
+
+out:
+	trace_sched_isolate(cpu, cpumask_bits(cpu_isolated_mask)[0],
+			    start_time, 0);
+	return ret_code;
+}
+
+int sched_unisolate_cpu(int cpu)
+{
+	int ret_code;
+
+	cpu_maps_update_begin();
+	ret_code = sched_unisolate_cpu_unlocked(cpu);
+	cpu_maps_update_done();
+	return ret_code;
+}
+
 #endif /* CONFIG_HOTPLUG_CPU */
 
 static void set_rq_online(struct rq *rq)
@@ -6491,11 +6827,14 @@ build_sched_groups(struct sched_domain *sd, int cpu)
 static void init_sched_groups_capacity(int cpu, struct sched_domain *sd)
 {
 	struct sched_group *sg = sd->groups;
+	cpumask_t avail_mask;
 
 	WARN_ON(!sg);
 
 	do {
-		sg->group_weight = cpumask_weight(sched_group_cpus(sg));
+		cpumask_andnot(&avail_mask, sched_group_cpus(sg),
+							cpu_isolated_mask);
+		sg->group_weight = cpumask_weight(&avail_mask);
 		sg = sg->next;
 	} while (sg != sd->groups);
 
@@ -7605,13 +7944,12 @@ int sched_cpu_dying(unsigned int cpu)
 	/* Handle pending wakeups and then migrate everything off */
 	sched_ttwu_pending();
 	raw_spin_lock_irqsave(&rq->lock, flags);
-	migrate_sync_cpu(cpu);
 
 	if (rq->rd) {
 		BUG_ON(!cpumask_test_cpu(cpu, rq->rd->span));
 		set_rq_offline(rq);
 	}
-	migrate_tasks(rq);
+	migrate_tasks(rq, true);
 	BUG_ON(rq->nr_running != 1);
 	raw_spin_unlock_irqrestore(&rq->lock, flags);
 
@@ -7737,8 +8075,9 @@ void __init sched_init(void)
 
 #ifdef CONFIG_SCHED_HMP
 	pr_info("HMP scheduling enabled.\n");
-	init_clusters();
 #endif
+	sched_boost_parse_dt();
+	init_clusters();
 
 #ifdef CONFIG_FAIR_GROUP_SCHED
 	alloc_size += 2 * nr_cpu_ids * sizeof(void **);
@@ -7881,10 +8220,27 @@ void __init sched_init(void)
 		rq->cluster = &init_cluster;
 		rq->curr_runnable_sum = rq->prev_runnable_sum = 0;
 		rq->nt_curr_runnable_sum = rq->nt_prev_runnable_sum = 0;
+		memset(&rq->grp_time, 0, sizeof(struct group_cpu_time));
 		rq->old_busy_time = 0;
 		rq->old_estimated_time = 0;
 		rq->old_busy_time_group = 0;
 		rq->hmp_stats.pred_demands_sum = 0;
+		rq->curr_table = 0;
+		rq->prev_top = 0;
+		rq->curr_top = 0;
+
+		for (j = 0; j < NUM_TRACKED_WINDOWS; j++) {
+			memset(&rq->load_subs[j], 0,
+					sizeof(struct load_subtractions));
+
+			rq->top_tasks[j] = kcalloc(NUM_LOAD_INDICES,
+						sizeof(u8), GFP_NOWAIT);
+
+			/* No other choice */
+			BUG_ON(!rq->top_tasks[j]);
+
+			clear_top_tasks_bitmap(rq->top_tasks_bitmap[j]);
+		}
 #endif
 		INIT_LIST_HEAD(&rq->cfs_tasks);
 
@@ -7901,6 +8257,9 @@ void __init sched_init(void)
 		atomic_set(&rq->nr_iowait, 0);
 	}
 
+	i = alloc_related_thread_groups();
+	BUG_ON(i);
+
 	set_hmp_defaults();
 
 	set_load_weight(&init_task);
@@ -7917,7 +8276,7 @@ void __init sched_init(void)
 	 * but because we are the idle thread, we just pick up running again
 	 * when this runqueue becomes "idle".
 	 */
-	init_idle(current, smp_processor_id());
+	init_idle(current, smp_processor_id(), false);
 
 	calc_load_update = jiffies + LOAD_FREQ;
 
diff --git a/kernel/sched/core_ctl.c b/kernel/sched/core_ctl.c
new file mode 100644
index 0000000..aac12bf
--- /dev/null
+++ b/kernel/sched/core_ctl.c
@@ -0,0 +1,1113 @@
+/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/notifier.h>
+#include <linux/cpu.h>
+#include <linux/cpumask.h>
+#include <linux/cpufreq.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/sched/rt.h>
+
+#include <trace/events/sched.h>
+
+#define MAX_CPUS_PER_CLUSTER 4
+#define MAX_CLUSTERS 2
+
+struct cluster_data {
+	bool inited;
+	unsigned int min_cpus;
+	unsigned int max_cpus;
+	unsigned int offline_delay_ms;
+	unsigned int busy_up_thres[MAX_CPUS_PER_CLUSTER];
+	unsigned int busy_down_thres[MAX_CPUS_PER_CLUSTER];
+	unsigned int active_cpus;
+	unsigned int num_cpus;
+	cpumask_t cpu_mask;
+	unsigned int need_cpus;
+	unsigned int task_thres;
+	s64 last_isolate_ts;
+	struct list_head lru;
+	bool pending;
+	spinlock_t pending_lock;
+	bool is_big_cluster;
+	int nrrun;
+	bool nrrun_changed;
+	struct task_struct *core_ctl_thread;
+	unsigned int first_cpu;
+	unsigned int boost;
+	struct kobject kobj;
+};
+
+struct cpu_data {
+	bool online;
+	bool is_busy;
+	unsigned int busy;
+	unsigned int cpu;
+	bool not_preferred;
+	struct cluster_data *cluster;
+	struct list_head sib;
+	bool isolated_by_us;
+};
+
+static DEFINE_PER_CPU(struct cpu_data, cpu_state);
+static struct cluster_data cluster_state[MAX_CLUSTERS];
+static unsigned int num_clusters;
+
+#define for_each_cluster(cluster, idx) \
+	for ((cluster) = &cluster_state[idx]; (idx) < num_clusters;\
+		(idx)++, (cluster) = &cluster_state[idx])
+
+static DEFINE_SPINLOCK(state_lock);
+static void apply_need(struct cluster_data *state);
+static void wake_up_core_ctl_thread(struct cluster_data *state);
+static bool initialized;
+
+static unsigned int get_active_cpu_count(const struct cluster_data *cluster);
+
+/* ========================= sysfs interface =========================== */
+
+static ssize_t store_min_cpus(struct cluster_data *state,
+				const char *buf, size_t count)
+{
+	unsigned int val;
+
+	if (sscanf(buf, "%u\n", &val) != 1)
+		return -EINVAL;
+
+	state->min_cpus = min(val, state->max_cpus);
+	wake_up_core_ctl_thread(state);
+
+	return count;
+}
+
+static ssize_t show_min_cpus(const struct cluster_data *state, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->min_cpus);
+}
+
+static ssize_t store_max_cpus(struct cluster_data *state,
+				const char *buf, size_t count)
+{
+	unsigned int val;
+
+	if (sscanf(buf, "%u\n", &val) != 1)
+		return -EINVAL;
+
+	val = min(val, state->num_cpus);
+	state->max_cpus = val;
+	state->min_cpus = min(state->min_cpus, state->max_cpus);
+	wake_up_core_ctl_thread(state);
+
+	return count;
+}
+
+static ssize_t show_max_cpus(const struct cluster_data *state, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->max_cpus);
+}
+
+static ssize_t store_offline_delay_ms(struct cluster_data *state,
+					const char *buf, size_t count)
+{
+	unsigned int val;
+
+	if (sscanf(buf, "%u\n", &val) != 1)
+		return -EINVAL;
+
+	state->offline_delay_ms = val;
+	apply_need(state);
+
+	return count;
+}
+
+static ssize_t show_task_thres(const struct cluster_data *state, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->task_thres);
+}
+
+static ssize_t store_task_thres(struct cluster_data *state,
+				const char *buf, size_t count)
+{
+	unsigned int val;
+
+	if (sscanf(buf, "%u\n", &val) != 1)
+		return -EINVAL;
+
+	if (val < state->num_cpus)
+		return -EINVAL;
+
+	state->task_thres = val;
+	apply_need(state);
+
+	return count;
+}
+
+static ssize_t show_offline_delay_ms(const struct cluster_data *state,
+				     char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->offline_delay_ms);
+}
+
+static ssize_t store_busy_up_thres(struct cluster_data *state,
+					const char *buf, size_t count)
+{
+	unsigned int val[MAX_CPUS_PER_CLUSTER];
+	int ret, i;
+
+	ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+	if (ret != 1 && ret != state->num_cpus)
+		return -EINVAL;
+
+	if (ret == 1) {
+		for (i = 0; i < state->num_cpus; i++)
+			state->busy_up_thres[i] = val[0];
+	} else {
+		for (i = 0; i < state->num_cpus; i++)
+			state->busy_up_thres[i] = val[i];
+	}
+	apply_need(state);
+	return count;
+}
+
+static ssize_t show_busy_up_thres(const struct cluster_data *state, char *buf)
+{
+	int i, count = 0;
+
+	for (i = 0; i < state->num_cpus; i++)
+		count += snprintf(buf + count, PAGE_SIZE - count, "%u ",
+				  state->busy_up_thres[i]);
+
+	count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+	return count;
+}
+
+static ssize_t store_busy_down_thres(struct cluster_data *state,
+					const char *buf, size_t count)
+{
+	unsigned int val[MAX_CPUS_PER_CLUSTER];
+	int ret, i;
+
+	ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+	if (ret != 1 && ret != state->num_cpus)
+		return -EINVAL;
+
+	if (ret == 1) {
+		for (i = 0; i < state->num_cpus; i++)
+			state->busy_down_thres[i] = val[0];
+	} else {
+		for (i = 0; i < state->num_cpus; i++)
+			state->busy_down_thres[i] = val[i];
+	}
+	apply_need(state);
+	return count;
+}
+
+static ssize_t show_busy_down_thres(const struct cluster_data *state, char *buf)
+{
+	int i, count = 0;
+
+	for (i = 0; i < state->num_cpus; i++)
+		count += snprintf(buf + count, PAGE_SIZE - count, "%u ",
+				  state->busy_down_thres[i]);
+
+	count += snprintf(buf + count, PAGE_SIZE - count, "\n");
+	return count;
+}
+
+static ssize_t store_is_big_cluster(struct cluster_data *state,
+				const char *buf, size_t count)
+{
+	unsigned int val;
+
+	if (sscanf(buf, "%u\n", &val) != 1)
+		return -EINVAL;
+
+	state->is_big_cluster = val ? 1 : 0;
+	return count;
+}
+
+static ssize_t show_is_big_cluster(const struct cluster_data *state, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->is_big_cluster);
+}
+
+static ssize_t show_cpus(const struct cluster_data *state, char *buf)
+{
+	struct cpu_data *c;
+	ssize_t count = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&state_lock, flags);
+	list_for_each_entry(c, &state->lru, sib) {
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				  "CPU%u (%s)\n", c->cpu,
+				  c->online ? "Online" : "Offline");
+	}
+	spin_unlock_irqrestore(&state_lock, flags);
+	return count;
+}
+
+static ssize_t show_need_cpus(const struct cluster_data *state, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->need_cpus);
+}
+
+static ssize_t show_active_cpus(const struct cluster_data *state, char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%u\n", state->active_cpus);
+}
+
+static ssize_t show_global_state(const struct cluster_data *state, char *buf)
+{
+	struct cpu_data *c;
+	struct cluster_data *cluster;
+	ssize_t count = 0;
+	unsigned int cpu;
+
+	for_each_possible_cpu(cpu) {
+		c = &per_cpu(cpu_state, cpu);
+		if (!c->cluster)
+			continue;
+
+		cluster = c->cluster;
+		if (!cluster || !cluster->inited)
+			continue;
+
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"CPU%u\n", cpu);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tCPU: %u\n", c->cpu);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tOnline: %u\n", c->online);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tActive: %u\n",
+					!cpu_isolated(c->cpu));
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tFirst CPU: %u\n",
+						cluster->first_cpu);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tBusy%%: %u\n", c->busy);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tIs busy: %u\n", c->is_busy);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+					"\tNr running: %u\n", cluster->nrrun);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+			"\tActive CPUs: %u\n", get_active_cpu_count(cluster));
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				"\tNeed CPUs: %u\n", cluster->need_cpus);
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				"\tBoost: %u\n", (unsigned int) cluster->boost);
+	}
+
+	return count;
+}
+
+static ssize_t store_not_preferred(struct cluster_data *state,
+				   const char *buf, size_t count)
+{
+	struct cpu_data *c;
+	unsigned int i;
+	unsigned int val[MAX_CPUS_PER_CLUSTER];
+	unsigned long flags;
+	int ret;
+
+	ret = sscanf(buf, "%u %u %u %u\n", &val[0], &val[1], &val[2], &val[3]);
+	if (ret != 1 && ret != state->num_cpus)
+		return -EINVAL;
+
+	i = 0;
+	spin_lock_irqsave(&state_lock, flags);
+	list_for_each_entry(c, &state->lru, sib)
+		c->not_preferred = val[i++];
+	spin_unlock_irqrestore(&state_lock, flags);
+
+	return count;
+}
+
+static ssize_t show_not_preferred(const struct cluster_data *state, char *buf)
+{
+	struct cpu_data *c;
+	ssize_t count = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&state_lock, flags);
+	list_for_each_entry(c, &state->lru, sib)
+		count += snprintf(buf + count, PAGE_SIZE - count,
+				"\tCPU:%d %u\n", c->cpu, c->not_preferred);
+	spin_unlock_irqrestore(&state_lock, flags);
+
+	return count;
+}
+
+
+struct core_ctl_attr {
+	struct attribute attr;
+	ssize_t (*show)(const struct cluster_data *, char *);
+	ssize_t (*store)(struct cluster_data *, const char *, size_t count);
+};
+
+#define core_ctl_attr_ro(_name)		\
+static struct core_ctl_attr _name =	\
+__ATTR(_name, 0444, show_##_name, NULL)
+
+#define core_ctl_attr_rw(_name)			\
+static struct core_ctl_attr _name =		\
+__ATTR(_name, 0644, show_##_name, store_##_name)
+
+core_ctl_attr_rw(min_cpus);
+core_ctl_attr_rw(max_cpus);
+core_ctl_attr_rw(offline_delay_ms);
+core_ctl_attr_rw(busy_up_thres);
+core_ctl_attr_rw(busy_down_thres);
+core_ctl_attr_rw(task_thres);
+core_ctl_attr_rw(is_big_cluster);
+core_ctl_attr_ro(cpus);
+core_ctl_attr_ro(need_cpus);
+core_ctl_attr_ro(active_cpus);
+core_ctl_attr_ro(global_state);
+core_ctl_attr_rw(not_preferred);
+
+static struct attribute *default_attrs[] = {
+	&min_cpus.attr,
+	&max_cpus.attr,
+	&offline_delay_ms.attr,
+	&busy_up_thres.attr,
+	&busy_down_thres.attr,
+	&task_thres.attr,
+	&is_big_cluster.attr,
+	&cpus.attr,
+	&need_cpus.attr,
+	&active_cpus.attr,
+	&global_state.attr,
+	&not_preferred.attr,
+	NULL
+};
+
+#define to_cluster_data(k) container_of(k, struct cluster_data, kobj)
+#define to_attr(a) container_of(a, struct core_ctl_attr, attr)
+static ssize_t show(struct kobject *kobj, struct attribute *attr, char *buf)
+{
+	struct cluster_data *data = to_cluster_data(kobj);
+	struct core_ctl_attr *cattr = to_attr(attr);
+	ssize_t ret = -EIO;
+
+	if (cattr->show)
+		ret = cattr->show(data, buf);
+
+	return ret;
+}
+
+static ssize_t store(struct kobject *kobj, struct attribute *attr,
+		     const char *buf, size_t count)
+{
+	struct cluster_data *data = to_cluster_data(kobj);
+	struct core_ctl_attr *cattr = to_attr(attr);
+	ssize_t ret = -EIO;
+
+	if (cattr->store)
+		ret = cattr->store(data, buf, count);
+
+	return ret;
+}
+
+static const struct sysfs_ops sysfs_ops = {
+	.show	= show,
+	.store	= store,
+};
+
+static struct kobj_type ktype_core_ctl = {
+	.sysfs_ops	= &sysfs_ops,
+	.default_attrs	= default_attrs,
+};
+
+/* ==================== runqueue based core count =================== */
+
+#define RQ_AVG_TOLERANCE 2
+#define RQ_AVG_DEFAULT_MS 20
+#define NR_RUNNING_TOLERANCE 5
+static unsigned int rq_avg_period_ms = RQ_AVG_DEFAULT_MS;
+
+static s64 rq_avg_timestamp_ms;
+
+static void update_running_avg(bool trigger_update)
+{
+	int avg, iowait_avg, big_avg, old_nrrun;
+	s64 now;
+	unsigned long flags;
+	struct cluster_data *cluster;
+	unsigned int index = 0;
+
+	spin_lock_irqsave(&state_lock, flags);
+
+	now = ktime_to_ms(ktime_get());
+	if (now - rq_avg_timestamp_ms < rq_avg_period_ms - RQ_AVG_TOLERANCE) {
+		spin_unlock_irqrestore(&state_lock, flags);
+		return;
+	}
+	rq_avg_timestamp_ms = now;
+	sched_get_nr_running_avg(&avg, &iowait_avg, &big_avg);
+
+	spin_unlock_irqrestore(&state_lock, flags);
+
+	/*
+	 * Round up to the next integer if the average nr running tasks
+	 * is within NR_RUNNING_TOLERANCE/100 of the next integer.
+	 * If normal rounding up is used, it will allow a transient task
+	 * to trigger online event. By the time core is onlined, the task
+	 * has finished.
+	 * Rounding to closest suffers same problem because scheduler
+	 * might only provide running stats per jiffy, and a transient
+	 * task could skew the number for one jiffy. If core control
+	 * samples every 2 jiffies, it will observe 0.5 additional running
+	 * average which rounds up to 1 task.
+	 */
+	avg = (avg + NR_RUNNING_TOLERANCE) / 100;
+	big_avg = (big_avg + NR_RUNNING_TOLERANCE) / 100;
+
+	for_each_cluster(cluster, index) {
+		if (!cluster->inited)
+			continue;
+		old_nrrun = cluster->nrrun;
+		/*
+		 * Big cluster only need to take care of big tasks, but if
+		 * there are not enough big cores, big tasks need to be run
+		 * on little as well. Thus for little's runqueue stat, it
+		 * has to use overall runqueue average, or derive what big
+		 * tasks would have to be run on little. The latter approach
+		 * is not easy to get given core control reacts much slower
+		 * than scheduler, and can't predict scheduler's behavior.
+		 */
+		cluster->nrrun = cluster->is_big_cluster ? big_avg : avg;
+		if (cluster->nrrun != old_nrrun) {
+			if (trigger_update)
+				apply_need(cluster);
+			else
+				cluster->nrrun_changed = true;
+		}
+	}
+	return;
+}
+
+/* adjust needed CPUs based on current runqueue information */
+static unsigned int apply_task_need(const struct cluster_data *cluster,
+				    unsigned int new_need)
+{
+	/* unisolate all cores if there are enough tasks */
+	if (cluster->nrrun >= cluster->task_thres)
+		return cluster->num_cpus;
+
+	/* only unisolate more cores if there are tasks to run */
+	if (cluster->nrrun > new_need)
+		return new_need + 1;
+
+	return new_need;
+}
+
+/* ======================= load based core count  ====================== */
+
+static unsigned int apply_limits(const struct cluster_data *cluster,
+				 unsigned int need_cpus)
+{
+	return min(max(cluster->min_cpus, need_cpus), cluster->max_cpus);
+}
+
+static unsigned int get_active_cpu_count(const struct cluster_data *cluster)
+{
+	return cluster->num_cpus -
+				sched_isolate_count(&cluster->cpu_mask, true);
+}
+
+static bool is_active(const struct cpu_data *state)
+{
+	return state->online && !cpu_isolated(state->cpu);
+}
+
+static bool adjustment_possible(const struct cluster_data *cluster,
+							unsigned int need)
+{
+	return (need < cluster->active_cpus || (need > cluster->active_cpus &&
+	    sched_isolate_count(&cluster->cpu_mask, false)));
+}
+
+static bool eval_need(struct cluster_data *cluster)
+{
+	unsigned long flags;
+	struct cpu_data *c;
+	unsigned int need_cpus = 0, last_need, thres_idx;
+	int ret = 0;
+	bool need_flag = false;
+	unsigned int active_cpus;
+	unsigned int new_need;
+
+	if (unlikely(!cluster->inited))
+		return 0;
+
+	spin_lock_irqsave(&state_lock, flags);
+
+	if (cluster->boost) {
+		need_cpus = cluster->max_cpus;
+	} else {
+		active_cpus = get_active_cpu_count(cluster);
+		thres_idx = active_cpus ? active_cpus - 1 : 0;
+		list_for_each_entry(c, &cluster->lru, sib) {
+			if (c->busy >= cluster->busy_up_thres[thres_idx])
+				c->is_busy = true;
+			else if (c->busy < cluster->busy_down_thres[thres_idx])
+				c->is_busy = false;
+			need_cpus += c->is_busy;
+		}
+		need_cpus = apply_task_need(cluster, need_cpus);
+	}
+	new_need = apply_limits(cluster, need_cpus);
+	need_flag = adjustment_possible(cluster, new_need);
+
+	last_need = cluster->need_cpus;
+	cluster->need_cpus = new_need;
+
+	if (!need_flag) {
+		spin_unlock_irqrestore(&state_lock, flags);
+		return 0;
+	}
+
+	if (need_cpus > cluster->active_cpus) {
+		ret = 1;
+	} else if (need_cpus < cluster->active_cpus) {
+		s64 now = ktime_to_ms(ktime_get());
+		s64 elapsed = now - cluster->last_isolate_ts;
+
+		ret = elapsed >= cluster->offline_delay_ms;
+	}
+
+	trace_core_ctl_eval_need(cluster->first_cpu, last_need, need_cpus,
+				 ret && need_flag);
+	spin_unlock_irqrestore(&state_lock, flags);
+
+	return ret && need_flag;
+}
+
+static void apply_need(struct cluster_data *cluster)
+{
+	if (eval_need(cluster))
+		wake_up_core_ctl_thread(cluster);
+}
+
+static int core_ctl_set_busy(unsigned int cpu, unsigned int busy)
+{
+	struct cpu_data *c = &per_cpu(cpu_state, cpu);
+	struct cluster_data *cluster = c->cluster;
+	unsigned int old_is_busy = c->is_busy;
+
+	if (!cluster || !cluster->inited)
+		return 0;
+
+	update_running_avg(false);
+	if (c->busy == busy && !cluster->nrrun_changed)
+		return 0;
+	c->busy = busy;
+	cluster->nrrun_changed = false;
+
+	apply_need(cluster);
+	trace_core_ctl_set_busy(cpu, busy, old_is_busy, c->is_busy);
+	return 0;
+}
+
+/* ========================= core count enforcement ==================== */
+
+static void wake_up_core_ctl_thread(struct cluster_data *cluster)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&cluster->pending_lock, flags);
+	cluster->pending = true;
+	spin_unlock_irqrestore(&cluster->pending_lock, flags);
+
+	wake_up_process_no_notif(cluster->core_ctl_thread);
+}
+
+static u64 core_ctl_check_timestamp;
+static u64 core_ctl_check_interval;
+
+static bool do_check(u64 wallclock)
+{
+	bool do_check = false;
+	unsigned long flags;
+
+	spin_lock_irqsave(&state_lock, flags);
+	if ((wallclock - core_ctl_check_timestamp) >= core_ctl_check_interval) {
+		core_ctl_check_timestamp = wallclock;
+		do_check = true;
+	}
+	spin_unlock_irqrestore(&state_lock, flags);
+	return do_check;
+}
+
+int core_ctl_set_boost(bool boost)
+{
+	unsigned int index = 0;
+	struct cluster_data *cluster;
+	unsigned long flags;
+	int ret = 0;
+	bool boost_state_changed = false;
+
+	spin_lock_irqsave(&state_lock, flags);
+	for_each_cluster(cluster, index) {
+		if (cluster->is_big_cluster) {
+			if (boost) {
+				boost_state_changed = !cluster->boost;
+				++cluster->boost;
+			} else {
+				if (!cluster->boost) {
+					pr_err("Error turning off boost. Boost already turned off\n");
+					ret = -EINVAL;
+				} else {
+					--cluster->boost;
+					boost_state_changed = !cluster->boost;
+				}
+			}
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&state_lock, flags);
+
+	if (boost_state_changed)
+		apply_need(cluster);
+
+	trace_core_ctl_set_boost(cluster->boost, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(core_ctl_set_boost);
+
+void core_ctl_check(u64 wallclock)
+{
+	if (unlikely(!initialized))
+		return;
+
+	if (do_check(wallclock)) {
+		unsigned int index = 0;
+		struct cluster_data *cluster;
+
+		update_running_avg(true);
+
+		for_each_cluster(cluster, index) {
+			if (eval_need(cluster))
+				wake_up_core_ctl_thread(cluster);
+		}
+	}
+}
+
+static void move_cpu_lru(struct cpu_data *cpu_data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&state_lock, flags);
+	list_del(&cpu_data->sib);
+	list_add_tail(&cpu_data->sib, &cpu_data->cluster->lru);
+	spin_unlock_irqrestore(&state_lock, flags);
+}
+
+static void try_to_isolate(struct cluster_data *cluster, unsigned int need)
+{
+	struct cpu_data *c, *tmp;
+	unsigned long flags;
+	unsigned int num_cpus = cluster->num_cpus;
+
+	/*
+	 * Protect against entry being removed (and added at tail) by other
+	 * thread (hotplug).
+	 */
+	spin_lock_irqsave(&state_lock, flags);
+	list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
+		if (!num_cpus--)
+			break;
+
+		if (!is_active(c))
+			continue;
+		if (cluster->active_cpus == need)
+			break;
+		/* Don't offline busy CPUs. */
+		if (c->is_busy)
+			continue;
+
+		spin_unlock_irqrestore(&state_lock, flags);
+
+		pr_debug("Trying to isolate CPU%u\n", c->cpu);
+		if (!sched_isolate_cpu(c->cpu)) {
+			c->isolated_by_us = true;
+			move_cpu_lru(c);
+			cluster->last_isolate_ts = ktime_to_ms(ktime_get());
+		} else {
+			pr_debug("Unable to isolate CPU%u\n", c->cpu);
+		}
+		cluster->active_cpus = get_active_cpu_count(cluster);
+		spin_lock_irqsave(&state_lock, flags);
+	}
+	spin_unlock_irqrestore(&state_lock, flags);
+
+	/*
+	 * If the number of active CPUs is within the limits, then
+	 * don't force isolation of any busy CPUs.
+	 */
+	if (cluster->active_cpus <= cluster->max_cpus)
+		return;
+
+	num_cpus = cluster->num_cpus;
+	spin_lock_irqsave(&state_lock, flags);
+	list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
+		if (!num_cpus--)
+			break;
+
+		if (!is_active(c))
+			continue;
+		if (cluster->active_cpus <= cluster->max_cpus)
+			break;
+
+		spin_unlock_irqrestore(&state_lock, flags);
+
+		pr_debug("Trying to isolate CPU%u\n", c->cpu);
+		if (!sched_isolate_cpu(c->cpu)) {
+			c->isolated_by_us = true;
+			move_cpu_lru(c);
+			cluster->last_isolate_ts = ktime_to_ms(ktime_get());
+		} else {
+			pr_debug("Unable to isolate CPU%u\n", c->cpu);
+		}
+		cluster->active_cpus = get_active_cpu_count(cluster);
+		spin_lock_irqsave(&state_lock, flags);
+	}
+	spin_unlock_irqrestore(&state_lock, flags);
+
+}
+
+static void __try_to_unisolate(struct cluster_data *cluster,
+			       unsigned int need, bool force)
+{
+	struct cpu_data *c, *tmp;
+	unsigned long flags;
+	unsigned int num_cpus = cluster->num_cpus;
+
+	/*
+	 * Protect against entry being removed (and added at tail) by other
+	 * thread (hotplug).
+	 */
+	spin_lock_irqsave(&state_lock, flags);
+	list_for_each_entry_safe(c, tmp, &cluster->lru, sib) {
+		if (!num_cpus--)
+			break;
+
+		if (!c->isolated_by_us)
+			continue;
+		if ((c->online && !cpu_isolated(c->cpu)) ||
+			(!force && c->not_preferred))
+			continue;
+		if (cluster->active_cpus == need)
+			break;
+
+		spin_unlock_irqrestore(&state_lock, flags);
+
+		pr_debug("Trying to unisolate CPU%u\n", c->cpu);
+		if (!sched_unisolate_cpu(c->cpu)) {
+			c->isolated_by_us = false;
+			move_cpu_lru(c);
+		} else {
+			pr_debug("Unable to unisolate CPU%u\n", c->cpu);
+		}
+		cluster->active_cpus = get_active_cpu_count(cluster);
+		spin_lock_irqsave(&state_lock, flags);
+	}
+	spin_unlock_irqrestore(&state_lock, flags);
+}
+
+static void try_to_unisolate(struct cluster_data *cluster, unsigned int need)
+{
+	bool force_use_non_preferred = false;
+
+	__try_to_unisolate(cluster, need, force_use_non_preferred);
+
+	if (cluster->active_cpus == need)
+		return;
+
+	force_use_non_preferred = true;
+	__try_to_unisolate(cluster, need, force_use_non_preferred);
+}
+
+static void __ref do_core_ctl(struct cluster_data *cluster)
+{
+	unsigned int need;
+
+	need = apply_limits(cluster, cluster->need_cpus);
+
+	if (adjustment_possible(cluster, need)) {
+		pr_debug("Trying to adjust group %u from %u to %u\n",
+				cluster->first_cpu, cluster->active_cpus, need);
+
+		if (cluster->active_cpus > need)
+			try_to_isolate(cluster, need);
+		else if (cluster->active_cpus < need)
+			try_to_unisolate(cluster, need);
+	}
+}
+
+static int __ref try_core_ctl(void *data)
+{
+	struct cluster_data *cluster = data;
+	unsigned long flags;
+
+	while (1) {
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&cluster->pending_lock, flags);
+		if (!cluster->pending) {
+			spin_unlock_irqrestore(&cluster->pending_lock, flags);
+			schedule();
+			if (kthread_should_stop())
+				break;
+			spin_lock_irqsave(&cluster->pending_lock, flags);
+		}
+		set_current_state(TASK_RUNNING);
+		cluster->pending = false;
+		spin_unlock_irqrestore(&cluster->pending_lock, flags);
+
+		do_core_ctl(cluster);
+	}
+
+	return 0;
+}
+
+static int __ref cpu_callback(struct notifier_block *nfb,
+				unsigned long action, void *hcpu)
+{
+	uint32_t cpu = (uintptr_t)hcpu;
+	struct cpu_data *state = &per_cpu(cpu_state, cpu);
+	struct cluster_data *cluster = state->cluster;
+	unsigned int need;
+	int ret = NOTIFY_OK;
+
+	if (unlikely(!cluster || !cluster->inited))
+		return NOTIFY_OK;
+
+	switch (action & ~CPU_TASKS_FROZEN) {
+	case CPU_UP_PREPARE:
+
+		/* If online state of CPU somehow got out of sync, fix it. */
+		if (state->online) {
+			state->online = false;
+			cluster->active_cpus = get_active_cpu_count(cluster);
+			pr_warn("CPU%d offline when state is online\n", cpu);
+		}
+		break;
+
+	case CPU_ONLINE:
+
+		state->online = true;
+		cluster->active_cpus = get_active_cpu_count(cluster);
+
+		/*
+		 * Moving to the end of the list should only happen in
+		 * CPU_ONLINE and not on CPU_UP_PREPARE to prevent an
+		 * infinite list traversal when thermal (or other entities)
+		 * reject trying to online CPUs.
+		 */
+		move_cpu_lru(state);
+		break;
+
+	case CPU_DEAD:
+		/*
+		 * We don't want to have a CPU both offline and isolated.
+		 * So unisolate a CPU that went down if it was isolated by us.
+		 */
+		if (state->isolated_by_us) {
+			sched_unisolate_cpu_unlocked(cpu);
+			state->isolated_by_us = false;
+		}
+
+		/* Move a CPU to the end of the LRU when it goes offline. */
+		move_cpu_lru(state);
+
+		/* Fall through */
+
+	case CPU_UP_CANCELED:
+
+		/* If online state of CPU somehow got out of sync, fix it. */
+		if (!state->online)
+			pr_warn("CPU%d online when state is offline\n", cpu);
+
+		state->online = false;
+		state->busy = 0;
+		cluster->active_cpus = get_active_cpu_count(cluster);
+		break;
+	}
+
+	need = apply_limits(cluster, cluster->need_cpus);
+	if (adjustment_possible(cluster, need))
+		wake_up_core_ctl_thread(cluster);
+
+	return ret;
+}
+
+static struct notifier_block __refdata cpu_notifier = {
+	.notifier_call = cpu_callback,
+};
+
+/* ============================ init code ============================== */
+
+static struct cluster_data *find_cluster_by_first_cpu(unsigned int first_cpu)
+{
+	unsigned int i;
+
+	for (i = 0; i < num_clusters; ++i) {
+		if (cluster_state[i].first_cpu == first_cpu)
+			return &cluster_state[i];
+	}
+
+	return NULL;
+}
+
+static int cluster_init(const struct cpumask *mask)
+{
+	struct device *dev;
+	unsigned int first_cpu = cpumask_first(mask);
+	struct cluster_data *cluster;
+	struct cpu_data *state;
+	unsigned int cpu;
+	struct sched_param param = { .sched_priority = MAX_RT_PRIO-1 };
+
+	if (find_cluster_by_first_cpu(first_cpu))
+		return 0;
+
+	dev = get_cpu_device(first_cpu);
+	if (!dev)
+		return -ENODEV;
+
+	pr_info("Creating CPU group %d\n", first_cpu);
+
+	if (num_clusters == MAX_CLUSTERS) {
+		pr_err("Unsupported number of clusters. Only %u supported\n",
+								MAX_CLUSTERS);
+		return -EINVAL;
+	}
+	cluster = &cluster_state[num_clusters];
+	++num_clusters;
+
+	cpumask_copy(&cluster->cpu_mask, mask);
+	cluster->num_cpus = cpumask_weight(mask);
+	if (cluster->num_cpus > MAX_CPUS_PER_CLUSTER) {
+		pr_err("HW configuration not supported\n");
+		return -EINVAL;
+	}
+	cluster->first_cpu = first_cpu;
+	cluster->min_cpus = 1;
+	cluster->max_cpus = cluster->num_cpus;
+	cluster->need_cpus = cluster->num_cpus;
+	cluster->offline_delay_ms = 100;
+	cluster->task_thres = UINT_MAX;
+	cluster->nrrun = cluster->num_cpus;
+	INIT_LIST_HEAD(&cluster->lru);
+	spin_lock_init(&cluster->pending_lock);
+
+	for_each_cpu(cpu, mask) {
+		pr_info("Init CPU%u state\n", cpu);
+
+		state = &per_cpu(cpu_state, cpu);
+		state->cluster = cluster;
+		state->cpu = cpu;
+		if (cpu_online(cpu))
+			state->online = true;
+		list_add_tail(&state->sib, &cluster->lru);
+	}
+	cluster->active_cpus = get_active_cpu_count(cluster);
+
+	cluster->core_ctl_thread = kthread_run(try_core_ctl, (void *) cluster,
+					"core_ctl/%d", first_cpu);
+	if (IS_ERR(cluster->core_ctl_thread))
+		return PTR_ERR(cluster->core_ctl_thread);
+
+	sched_setscheduler_nocheck(cluster->core_ctl_thread, SCHED_FIFO,
+				   &param);
+
+	cluster->inited = true;
+
+	kobject_init(&cluster->kobj, &ktype_core_ctl);
+	return kobject_add(&cluster->kobj, &dev->kobj, "core_ctl");
+}
+
+static int cpufreq_policy_cb(struct notifier_block *nb, unsigned long val,
+				void *data)
+{
+	struct cpufreq_policy *policy = data;
+	int ret;
+
+	switch (val) {
+	case CPUFREQ_CREATE_POLICY:
+		ret = cluster_init(policy->related_cpus);
+		if (ret)
+			pr_warn("unable to create core ctl group: %d\n", ret);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_pol_nb = {
+	.notifier_call = cpufreq_policy_cb,
+};
+
+static int cpufreq_gov_cb(struct notifier_block *nb, unsigned long val,
+				void *data)
+{
+	struct cpufreq_govinfo *info = data;
+
+	switch (val) {
+	case CPUFREQ_LOAD_CHANGE:
+		core_ctl_set_busy(info->cpu, info->load);
+		break;
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block cpufreq_gov_nb = {
+	.notifier_call = cpufreq_gov_cb,
+};
+
+static int __init core_ctl_init(void)
+{
+	unsigned int cpu;
+
+	core_ctl_check_interval = (rq_avg_period_ms - RQ_AVG_TOLERANCE)
+					* NSEC_PER_MSEC;
+
+	register_cpu_notifier(&cpu_notifier);
+	cpufreq_register_notifier(&cpufreq_pol_nb, CPUFREQ_POLICY_NOTIFIER);
+	cpufreq_register_notifier(&cpufreq_gov_nb, CPUFREQ_GOVINFO_NOTIFIER);
+
+	cpu_maps_update_begin();
+	for_each_online_cpu(cpu) {
+		struct cpufreq_policy *policy;
+		int ret;
+
+		policy = cpufreq_cpu_get(cpu);
+		if (policy) {
+			ret = cluster_init(policy->related_cpus);
+			if (ret)
+				pr_warn("unable to create core ctl group: %d\n"
+					, ret);
+			cpufreq_cpu_put(policy);
+		}
+	}
+	cpu_maps_update_done();
+	initialized = true;
+	return 0;
+}
+
+late_initcall(core_ctl_init);
diff --git a/kernel/sched/debug.c b/kernel/sched/debug.c
index 5671f26..8cc5256 100644
--- a/kernel/sched/debug.c
+++ b/kernel/sched/debug.c
@@ -732,6 +732,7 @@ static void sched_debug_header(struct seq_file *m)
 	P(min_capacity);
 	P(max_capacity);
 	P(sched_ravg_window);
+	P(sched_load_granule);
 #endif
 #undef PN
 #undef P
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 45a2b23..48f25e3 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -80,14 +80,6 @@ static unsigned int sched_nr_latency = 8;
 unsigned int sysctl_sched_child_runs_first __read_mostly;
 
 /*
- * Controls whether, when SD_SHARE_PKG_RESOURCES is on, if all
- * tasks go to idle CPUs when woken. If this is off, note that the
- * per-task flag PF_WAKE_UP_IDLE can still cause a task to go to an
- * idle CPU upon being woken.
- */
-unsigned int __read_mostly sysctl_sched_wake_to_idle;
-
-/*
  * SCHED_OTHER wake-up granularity.
  * (default: 1 msec * (1 + ilog(ncpus)), units: nanoseconds)
  *
@@ -2798,11 +2790,14 @@ static u32 __compute_runnable_contrib(u64 n)
 #define SBC_FLAG_COST_CSTATE_PREV_CPU_TIE_BREAKER	0x80
 #define SBC_FLAG_CSTATE_LOAD				0x100
 #define SBC_FLAG_BEST_SIBLING				0x200
+#define SBC_FLAG_WAKER_CPU				0x400
+#define SBC_FLAG_PACK_TASK				0x800
 
 /* Cluster selection flag */
 #define SBC_FLAG_COLOC_CLUSTER				0x10000
 #define SBC_FLAG_WAKER_CLUSTER				0x20000
 #define SBC_FLAG_BACKUP_CLUSTER				0x40000
+#define SBC_FLAG_BOOST_CLUSTER				0x80000
 
 struct cpu_select_env {
 	struct task_struct *p;
@@ -2812,7 +2807,8 @@ struct cpu_select_env {
 	u8 need_waker_cluster:1;
 	u8 sync:1;
 	u8 ignore_prev_cpu:1;
-	enum sched_boost_type boost_type;
+	enum sched_boost_policy boost_policy;
+	u8 pack_task:1;
 	int prev_cpu;
 	DECLARE_BITMAP(candidate_list, NR_CPUS);
 	DECLARE_BITMAP(backup_list, NR_CPUS);
@@ -2826,11 +2822,26 @@ struct cluster_cpu_stats {
 	int best_idle_cpu, least_loaded_cpu;
 	int best_capacity_cpu, best_cpu, best_sibling_cpu;
 	int min_cost, best_sibling_cpu_cost;
-	int best_cpu_cstate;
+	int best_cpu_wakeup_latency;
 	u64 min_load, best_load, best_sibling_cpu_load;
 	s64 highest_spare_capacity;
 };
 
+/*
+ * Should task be woken to any available idle cpu?
+ *
+ * Waking tasks to idle cpu has mixed implications on both performance and
+ * power. In many cases, scheduler can't estimate correctly impact of using idle
+ * cpus on either performance or power. PF_WAKE_UP_IDLE allows external kernel
+ * module to pass a strong hint to scheduler that the task in question should be
+ * woken to idle cpu, generally to improve performance.
+ */
+static inline int wake_to_idle(struct task_struct *p)
+{
+	return (current->flags & PF_WAKE_UP_IDLE) ||
+		 (p->flags & PF_WAKE_UP_IDLE);
+}
+
 static int spill_threshold_crossed(struct cpu_select_env *env, struct rq *rq)
 {
 	u64 total_load;
@@ -2912,10 +2923,38 @@ select_least_power_cluster(struct cpu_select_env *env)
 	struct sched_cluster *cluster;
 
 	if (env->rtg) {
-		env->task_load = scale_load_to_cpu(task_load(env->p),
-			cluster_first_cpu(env->rtg->preferred_cluster));
-		env->sbc_best_cluster_flag |= SBC_FLAG_COLOC_CLUSTER;
-		return env->rtg->preferred_cluster;
+		int cpu = cluster_first_cpu(env->rtg->preferred_cluster);
+
+		env->task_load = scale_load_to_cpu(task_load(env->p), cpu);
+
+		if (task_load_will_fit(env->p, env->task_load,
+					cpu, env->boost_policy)) {
+			env->sbc_best_cluster_flag |= SBC_FLAG_COLOC_CLUSTER;
+
+			if (env->boost_policy == SCHED_BOOST_NONE)
+				return env->rtg->preferred_cluster;
+
+			for_each_sched_cluster(cluster) {
+				if (cluster != env->rtg->preferred_cluster) {
+					__set_bit(cluster->id,
+						env->backup_list);
+					__clear_bit(cluster->id,
+						env->candidate_list);
+				}
+			}
+
+			return env->rtg->preferred_cluster;
+		}
+
+		/*
+		 * Since the task load does not fit on the preferred
+		 * cluster anymore, pretend that the task does not
+		 * have any preferred cluster. This allows the waking
+		 * task to get the appropriate CPU it needs as per the
+		 * non co-location placement policy without having to
+		 * wait until the preferred cluster is updated.
+		 */
+		env->rtg = NULL;
 	}
 
 	for_each_sched_cluster(cluster) {
@@ -2925,7 +2964,7 @@ select_least_power_cluster(struct cpu_select_env *env)
 			env->task_load = scale_load_to_cpu(task_load(env->p),
 									 cpu);
 			if (task_load_will_fit(env->p, env->task_load, cpu,
-					       env->boost_type))
+					       env->boost_policy))
 				return cluster;
 
 			__set_bit(cluster->id, env->backup_list);
@@ -3034,19 +3073,19 @@ next_best_cluster(struct sched_cluster *cluster, struct cpu_select_env *env,
 static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
 				   struct cpu_select_env *env, int cpu_cost)
 {
-	int cpu_cstate;
+	int wakeup_latency;
 	int prev_cpu = env->prev_cpu;
 
-	cpu_cstate = cpu_rq(cpu)->cstate;
+	wakeup_latency = cpu_rq(cpu)->wakeup_latency;
 
 	if (env->need_idle) {
 		stats->min_cost = cpu_cost;
 		if (idle_cpu(cpu)) {
-			if (cpu_cstate < stats->best_cpu_cstate ||
-				(cpu_cstate == stats->best_cpu_cstate &&
-							cpu == prev_cpu)) {
+			if (wakeup_latency < stats->best_cpu_wakeup_latency ||
+			    (wakeup_latency == stats->best_cpu_wakeup_latency &&
+			     cpu == prev_cpu)) {
 				stats->best_idle_cpu = cpu;
-				stats->best_cpu_cstate = cpu_cstate;
+				stats->best_cpu_wakeup_latency = wakeup_latency;
 			}
 		} else {
 			if (env->cpu_load < stats->min_load ||
@@ -3062,7 +3101,7 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
 
 	if (cpu_cost < stats->min_cost)  {
 		stats->min_cost = cpu_cost;
-		stats->best_cpu_cstate = cpu_cstate;
+		stats->best_cpu_wakeup_latency = wakeup_latency;
 		stats->best_load = env->cpu_load;
 		stats->best_cpu = cpu;
 		env->sbc_best_flag = SBC_FLAG_CPU_COST;
@@ -3071,11 +3110,11 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
 
 	/* CPU cost is the same. Start breaking the tie by C-state */
 
-	if (cpu_cstate > stats->best_cpu_cstate)
+	if (wakeup_latency > stats->best_cpu_wakeup_latency)
 		return;
 
-	if (cpu_cstate < stats->best_cpu_cstate) {
-		stats->best_cpu_cstate = cpu_cstate;
+	if (wakeup_latency < stats->best_cpu_wakeup_latency) {
+		stats->best_cpu_wakeup_latency = wakeup_latency;
 		stats->best_load = env->cpu_load;
 		stats->best_cpu = cpu;
 		env->sbc_best_flag = SBC_FLAG_COST_CSTATE_TIE_BREAKER;
@@ -3090,8 +3129,8 @@ static void __update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
 	}
 
 	if (stats->best_cpu != prev_cpu &&
-	    ((cpu_cstate == 0 && env->cpu_load < stats->best_load) ||
-	    (cpu_cstate > 0 && env->cpu_load > stats->best_load))) {
+	    ((wakeup_latency == 0 && env->cpu_load < stats->best_load) ||
+	    (wakeup_latency > 0 && env->cpu_load > stats->best_load))) {
 		stats->best_load = env->cpu_load;
 		stats->best_cpu = cpu;
 		env->sbc_best_flag = SBC_FLAG_CSTATE_LOAD;
@@ -3136,8 +3175,17 @@ static void update_cluster_stats(int cpu, struct cluster_cpu_stats *stats,
 {
 	int cpu_cost;
 
-	cpu_cost = power_cost(cpu, task_load(env->p) +
+	/*
+	 * We try to find the least loaded *busy* CPU irrespective
+	 * of the power cost.
+	 */
+	if (env->pack_task)
+		cpu_cost = cpu_min_power_cost(cpu);
+
+	else
+		cpu_cost = power_cost(cpu, task_load(env->p) +
 				cpu_cravg_sync(cpu, env->sync));
+
 	if (cpu_cost <= stats->min_cost)
 		__update_cluster_stats(cpu, stats, env, cpu_cost);
 }
@@ -3149,9 +3197,13 @@ static void find_best_cpu_in_cluster(struct sched_cluster *c,
 	struct cpumask search_cpus;
 
 	cpumask_and(&search_cpus, tsk_cpus_allowed(env->p), &c->cpus);
+	cpumask_andnot(&search_cpus, &search_cpus, cpu_isolated_mask);
+
 	if (env->ignore_prev_cpu)
 		cpumask_clear_cpu(env->prev_cpu, &search_cpus);
 
+	env->need_idle = wake_to_idle(env->p) || c->wake_up_idle;
+
 	for_each_cpu(i, &search_cpus) {
 		env->cpu_load = cpu_load_sync(i, env->sync);
 
@@ -3166,7 +3218,14 @@ static void find_best_cpu_in_cluster(struct sched_cluster *c,
 		update_spare_capacity(stats, env, i, c->capacity,
 				      env->cpu_load);
 
-		if (env->boost_type == SCHED_BOOST_ON_ALL ||
+		/*
+		 * need_idle takes precedence over sched boost but when both
+		 * are set, idlest CPU with in all the clusters is selected
+		 * when boost_policy = BOOST_ON_ALL whereas idlest CPU in the
+		 * big cluster is selected within boost_policy = BOOST_ON_BIG.
+		 */
+		if ((!env->need_idle &&
+		    env->boost_policy != SCHED_BOOST_NONE) ||
 		    env->need_waker_cluster ||
 		    sched_cpu_high_irqload(i) ||
 		    spill_threshold_crossed(env, cpu_rq(i)))
@@ -3184,23 +3243,17 @@ static inline void init_cluster_cpu_stats(struct cluster_cpu_stats *stats)
 	stats->min_load	= stats->best_sibling_cpu_load = ULLONG_MAX;
 	stats->highest_spare_capacity = 0;
 	stats->least_loaded_cpu = -1;
-	stats->best_cpu_cstate = INT_MAX;
+	stats->best_cpu_wakeup_latency = INT_MAX;
 	/* No need to initialize stats->best_load */
 }
 
-/*
- * Should task be woken to any available idle cpu?
- *
- * Waking tasks to idle cpu has mixed implications on both performance and
- * power. In many cases, scheduler can't estimate correctly impact of using idle
- * cpus on either performance or power. PF_WAKE_UP_IDLE allows external kernel
- * module to pass a strong hint to scheduler that the task in question should be
- * woken to idle cpu, generally to improve performance.
- */
-static inline int wake_to_idle(struct task_struct *p)
+static inline bool env_has_special_flags(struct cpu_select_env *env)
 {
-	return (current->flags & PF_WAKE_UP_IDLE) ||
-		 (p->flags & PF_WAKE_UP_IDLE) || sysctl_sched_wake_to_idle;
+	if (env->need_idle || env->boost_policy != SCHED_BOOST_NONE ||
+	    env->reason)
+		return true;
+
+	return false;
 }
 
 static inline bool
@@ -3210,14 +3263,13 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats)
 	struct task_struct *task = env->p;
 	struct sched_cluster *cluster;
 
-	if (env->boost_type != SCHED_BOOST_NONE || env->reason ||
-	    !task->ravg.mark_start ||
-	    env->need_idle || !sched_short_sleep_task_threshold)
+	if (!task->ravg.mark_start || !sched_short_sleep_task_threshold)
 		return false;
 
 	prev_cpu = env->prev_cpu;
 	if (!cpumask_test_cpu(prev_cpu, tsk_cpus_allowed(task)) ||
-					unlikely(!cpu_active(prev_cpu)))
+					unlikely(!cpu_active(prev_cpu)) ||
+					cpu_isolated(prev_cpu))
 		return false;
 
 	if (task->ravg.mark_start - task->last_cpu_selected_ts >=
@@ -3238,7 +3290,7 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats)
 	cluster = cpu_rq(prev_cpu)->cluster;
 
 	if (!task_load_will_fit(task, env->task_load, prev_cpu,
-				sched_boost_type())) {
+				sched_boost_policy())) {
 
 		__set_bit(cluster->id, env->backup_list);
 		__clear_bit(cluster->id, env->candidate_list);
@@ -3260,11 +3312,20 @@ bias_to_prev_cpu(struct cpu_select_env *env, struct cluster_cpu_stats *stats)
 static inline bool
 wake_to_waker_cluster(struct cpu_select_env *env)
 {
-	return !env->need_idle && !env->reason && env->sync &&
+	return env->sync &&
 	       task_load(current) > sched_big_waker_task_load &&
 	       task_load(env->p) < sched_small_wakee_task_load;
 }
 
+static inline bool
+bias_to_waker_cpu(struct task_struct *p, int cpu)
+{
+	return sysctl_sched_prefer_sync_wakee_to_waker &&
+	       cpu_rq(cpu)->nr_running == 1 &&
+	       cpumask_test_cpu(cpu, tsk_cpus_allowed(p)) &&
+	       cpu_active(cpu) && !cpu_isolated(cpu);
+}
+
 static inline int
 cluster_allowed(struct task_struct *p, struct sched_cluster *cluster)
 {
@@ -3276,7 +3337,6 @@ cluster_allowed(struct task_struct *p, struct sched_cluster *cluster)
 	return !cpumask_empty(&tmp_mask);
 }
 
-
 /* return cheapest cpu that can fit this task */
 static int select_best_cpu(struct task_struct *p, int target, int reason,
 			   int sync)
@@ -3285,25 +3345,31 @@ static int select_best_cpu(struct task_struct *p, int target, int reason,
 	struct cluster_cpu_stats stats;
 	struct related_thread_group *grp;
 	unsigned int sbc_flag = 0;
+	int cpu = raw_smp_processor_id();
+	bool special;
 
 	struct cpu_select_env env = {
 		.p			= p,
 		.reason			= reason,
 		.need_idle		= wake_to_idle(p),
 		.need_waker_cluster	= 0,
-		.boost_type		= sched_boost_type(),
 		.sync			= sync,
 		.prev_cpu		= target,
 		.ignore_prev_cpu	= 0,
 		.rtg			= NULL,
 		.sbc_best_flag		= 0,
 		.sbc_best_cluster_flag	= 0,
+		.pack_task              = false,
 	};
 
+	env.boost_policy = task_sched_boost(p) ?
+			sched_boost_policy() : SCHED_BOOST_NONE;
+
 	bitmap_copy(env.candidate_list, all_cluster_ids, NR_CPUS);
 	bitmap_zero(env.backup_list, NR_CPUS);
 
 	init_cluster_cpu_stats(&stats);
+	special = env_has_special_flags(&env);
 
 	rcu_read_lock();
 
@@ -3315,21 +3381,31 @@ static int select_best_cpu(struct task_struct *p, int target, int reason,
 			clear_bit(pref_cluster->id, env.candidate_list);
 		else
 			env.rtg = grp;
-	} else {
-		cluster = cpu_rq(smp_processor_id())->cluster;
-		if (wake_to_waker_cluster(&env) &&
-		    cluster_allowed(p, cluster)) {
-			env.need_waker_cluster = 1;
-			bitmap_zero(env.candidate_list, NR_CPUS);
-			__set_bit(cluster->id, env.candidate_list);
-			env.sbc_best_cluster_flag = SBC_FLAG_WAKER_CLUSTER;
-
+	} else if (!special) {
+		cluster = cpu_rq(cpu)->cluster;
+		if (wake_to_waker_cluster(&env)) {
+			if (bias_to_waker_cpu(p, cpu)) {
+				target = cpu;
+				sbc_flag = SBC_FLAG_WAKER_CLUSTER |
+					   SBC_FLAG_WAKER_CPU;
+				goto out;
+			} else if (cluster_allowed(p, cluster)) {
+				env.need_waker_cluster = 1;
+				bitmap_zero(env.candidate_list, NR_CPUS);
+				__set_bit(cluster->id, env.candidate_list);
+				env.sbc_best_cluster_flag =
+							SBC_FLAG_WAKER_CLUSTER;
+			}
 		} else if (bias_to_prev_cpu(&env, &stats)) {
 			sbc_flag = SBC_FLAG_PREV_CPU;
 			goto out;
 		}
 	}
 
+	if (!special && is_short_burst_task(p)) {
+		env.pack_task = true;
+		sbc_flag = SBC_FLAG_PACK_TASK;
+	}
 retry:
 	cluster = select_least_power_cluster(&env);
 
@@ -3365,23 +3441,34 @@ static int select_best_cpu(struct task_struct *p, int target, int reason,
 		sbc_flag |= env.sbc_best_flag;
 		target = stats.best_cpu;
 	} else {
-		if (env.rtg) {
+		if (env.rtg && env.boost_policy == SCHED_BOOST_NONE) {
 			env.rtg = NULL;
 			goto retry;
 		}
 
-		find_backup_cluster(&env, &stats);
+		/*
+		 * With boost_policy == SCHED_BOOST_ON_BIG, we reach here with
+		 * backup_list = little cluster, candidate_list = none and
+		 * stats->best_capacity_cpu points the best spare capacity
+		 * CPU among the CPUs in the big cluster.
+		 */
+		if (env.boost_policy == SCHED_BOOST_ON_BIG &&
+		    stats.best_capacity_cpu >= 0)
+			sbc_flag |= SBC_FLAG_BOOST_CLUSTER;
+		else
+			find_backup_cluster(&env, &stats);
+
 		if (stats.best_capacity_cpu >= 0) {
 			target = stats.best_capacity_cpu;
 			sbc_flag |= SBC_FLAG_BEST_CAP_CPU;
 		}
 	}
 	p->last_cpu_selected_ts = sched_ktime_clock();
-	sbc_flag |= env.sbc_best_cluster_flag;
 out:
+	sbc_flag |= env.sbc_best_cluster_flag;
 	rcu_read_unlock();
-	trace_sched_task_load(p, sched_boost(), env.reason, env.sync,
-					env.need_idle, sbc_flag, target);
+	trace_sched_task_load(p, sched_boost_policy() && task_sched_boost(p),
+		env.reason, env.sync, env.need_idle, sbc_flag, target);
 	return target;
 }
 
@@ -3542,11 +3629,9 @@ static inline int migration_needed(struct task_struct *p, int cpu)
 	if (task_will_be_throttled(p))
 		return 0;
 
-	if (sched_boost_type() == SCHED_BOOST_ON_BIG) {
-		if (cpu_capacity(cpu) != max_capacity)
-			return UP_MIGRATION;
-		return 0;
-	}
+	if (sched_boost_policy() == SCHED_BOOST_ON_BIG &&
+		 cpu_capacity(cpu) != max_capacity && task_sched_boost(p))
+		return UP_MIGRATION;
 
 	if (sched_cpu_high_irqload(cpu))
 		return IRQLOAD_MIGRATION;
@@ -3560,7 +3645,7 @@ static inline int migration_needed(struct task_struct *p, int cpu)
 		return DOWN_MIGRATION;
 	}
 
-	if (!grp && !task_will_fit(p, cpu)) {
+	if (!task_will_fit(p, cpu)) {
 		rcu_read_unlock();
 		return UP_MIGRATION;
 	}
@@ -3665,7 +3750,7 @@ static void dec_throttled_cfs_rq_hmp_stats(struct hmp_sched_stats *stats,
 
 	BUG_ON(stats->nr_big_tasks < 0 ||
 		(s64)stats->cumulative_runnable_avg < 0);
-	verify_pred_demands_sum(stats);
+	BUG_ON((s64)stats->pred_demands_sum < 0);
 }
 
 #else	/* CONFIG_CFS_BANDWIDTH */
@@ -3688,15 +3773,9 @@ static inline void inc_cfs_rq_hmp_stats(struct cfs_rq *cfs_rq,
 static inline void dec_cfs_rq_hmp_stats(struct cfs_rq *cfs_rq,
 	 struct task_struct *p, int change_cra) { }
 
-static inline void inc_throttled_cfs_rq_hmp_stats(struct hmp_sched_stats *stats,
-			 struct cfs_rq *cfs_rq)
-{
-}
+#define dec_throttled_cfs_rq_hmp_stats(...)
+#define inc_throttled_cfs_rq_hmp_stats(...)
 
-static inline void dec_throttled_cfs_rq_hmp_stats(struct hmp_sched_stats *stats,
-			 struct cfs_rq *cfs_rq)
-{
-}
 #endif	/* CONFIG_SCHED_HMP */
 
 #define cap_scale(v, s) ((v)*(s) >> SCHED_CAPACITY_SHIFT)
@@ -4833,6 +4912,7 @@ static inline int cfs_rq_throttled(struct cfs_rq *cfs_rq)
 	return cfs_bandwidth_used() && cfs_rq->throttled;
 }
 
+#ifdef CONFIG_SCHED_HMP
 /*
  * Check if task is part of a hierarchy where some cfs_rq does not have any
  * runtime left.
@@ -4859,6 +4939,7 @@ static int task_will_be_throttled(struct task_struct *p)
 
 	return 0;
 }
+#endif
 
 /* check whether cfs_rq, or any parent, is throttled */
 static inline int throttled_hierarchy(struct cfs_rq *cfs_rq)
@@ -4937,9 +5018,7 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
 		if (dequeue)
 			dequeue_entity(qcfs_rq, se, DEQUEUE_SLEEP);
 		qcfs_rq->h_nr_running -= task_delta;
-#ifdef CONFIG_SCHED_HMP
 		dec_throttled_cfs_rq_hmp_stats(&qcfs_rq->hmp_stats, cfs_rq);
-#endif
 
 		if (qcfs_rq->load.weight)
 			dequeue = 0;
@@ -4947,9 +5026,7 @@ static void throttle_cfs_rq(struct cfs_rq *cfs_rq)
 
 	if (!se) {
 		sub_nr_running(rq, task_delta);
-#ifdef CONFIG_SCHED_HMP
 		dec_throttled_cfs_rq_hmp_stats(&rq->hmp_stats, cfs_rq);
-#endif
 	}
 
 	cfs_rq->throttled = 1;
@@ -4986,7 +5063,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
 	struct sched_entity *se;
 	int enqueue = 1;
 	long task_delta;
-	struct cfs_rq *tcfs_rq = cfs_rq;
+	struct cfs_rq *tcfs_rq __maybe_unused = cfs_rq;
 
 	se = cfs_rq->tg->se[cpu_of(rq)];
 
@@ -5014,9 +5091,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
 		if (enqueue)
 			enqueue_entity(cfs_rq, se, ENQUEUE_WAKEUP);
 		cfs_rq->h_nr_running += task_delta;
-#ifdef CONFIG_SCHED_HMP
 		inc_throttled_cfs_rq_hmp_stats(&cfs_rq->hmp_stats, tcfs_rq);
-#endif
 
 		if (cfs_rq_throttled(cfs_rq))
 			break;
@@ -5024,9 +5099,7 @@ void unthrottle_cfs_rq(struct cfs_rq *cfs_rq)
 
 	if (!se) {
 		add_nr_running(rq, task_delta);
-#ifdef CONFIG_SCHED_HMP
 		inc_throttled_cfs_rq_hmp_stats(&rq->hmp_stats, tcfs_rq);
-#endif
 	}
 
 	/* determine whether we need to wake up potentially idle cpu */
@@ -7263,10 +7336,7 @@ enum fbq_type { regular, remote, all };
 #define LBF_NEED_BREAK	0x02
 #define LBF_DST_PINNED  0x04
 #define LBF_SOME_PINNED	0x08
-#define LBF_SCHED_BOOST_ACTIVE_BALANCE 0x40
 #define LBF_BIG_TASK_ACTIVE_BALANCE 0x80
-#define LBF_HMP_ACTIVE_BALANCE (LBF_SCHED_BOOST_ACTIVE_BALANCE | \
-				LBF_BIG_TASK_ACTIVE_BALANCE)
 #define LBF_IGNORE_BIG_TASKS 0x100
 #define LBF_IGNORE_PREFERRED_CLUSTER_TASKS 0x200
 #define LBF_MOVED_RELATED_THREAD_GROUP_TASK 0x400
@@ -7297,6 +7367,7 @@ struct lb_env {
 
 	enum fbq_type		fbq_type;
 	struct list_head	tasks;
+	enum sched_boost_policy	boost_policy;
 };
 
 /*
@@ -7441,9 +7512,14 @@ int can_migrate_task(struct task_struct *p, struct lb_env *env)
 	/* Record that we found atleast one task that could run on dst_cpu */
 	env->flags &= ~LBF_ALL_PINNED;
 
-	if (cpu_capacity(env->dst_cpu) > cpu_capacity(env->src_cpu) &&
-		nr_big_tasks(env->src_rq) && !is_big_task(p))
-		return 0;
+	if (cpu_capacity(env->dst_cpu) > cpu_capacity(env->src_cpu)) {
+		if (nr_big_tasks(env->src_rq) && !is_big_task(p))
+			return 0;
+
+		if (env->boost_policy == SCHED_BOOST_ON_BIG &&
+					!task_sched_boost(p))
+			return 0;
+	}
 
 	twf = task_will_fit(p, env->dst_cpu);
 
@@ -7565,12 +7641,12 @@ static int detach_tasks(struct lb_env *env)
 	if (env->imbalance <= 0)
 		return 0;
 
-	if (cpu_capacity(env->dst_cpu) < cpu_capacity(env->src_cpu) &&
-							!sched_boost())
-		env->flags |= LBF_IGNORE_BIG_TASKS;
-	else if (!same_cluster(env->dst_cpu, env->src_cpu))
+	if (!same_cluster(env->dst_cpu, env->src_cpu))
 		env->flags |= LBF_IGNORE_PREFERRED_CLUSTER_TASKS;
 
+	if (cpu_capacity(env->dst_cpu) < cpu_capacity(env->src_cpu))
+		env->flags |= LBF_IGNORE_BIG_TASKS;
+
 redo:
 	while (!list_empty(tasks)) {
 		/*
@@ -7869,8 +7945,10 @@ bail_inter_cluster_balance(struct lb_env *env, struct sd_lb_stats *sds)
 	int local_capacity, busiest_capacity;
 	int local_pwr_cost, busiest_pwr_cost;
 	int nr_cpus;
+	int boost = sched_boost();
 
-	if (!sysctl_sched_restrict_cluster_spill || sched_boost())
+	if (!sysctl_sched_restrict_cluster_spill ||
+		boost == FULL_THROTTLE_BOOST || boost == CONSERVATIVE_BOOST)
 		return 0;
 
 	local_cpu = group_first_cpu(sds->local);
@@ -7882,9 +7960,7 @@ bail_inter_cluster_balance(struct lb_env *env, struct sd_lb_stats *sds)
 	local_pwr_cost = cpu_max_power_cost(local_cpu);
 	busiest_pwr_cost = cpu_max_power_cost(busiest_cpu);
 
-	if (local_capacity < busiest_capacity ||
-			(local_capacity == busiest_capacity &&
-			local_pwr_cost <= busiest_pwr_cost))
+	if (local_pwr_cost <= busiest_pwr_cost)
 		return 0;
 
 	if (local_capacity > busiest_capacity &&
@@ -8010,6 +8086,8 @@ void update_group_capacity(struct sched_domain *sd, int cpu)
 			struct sched_group_capacity *sgc;
 			struct rq *rq = cpu_rq(cpu);
 
+			if (cpumask_test_cpu(cpu, cpu_isolated_mask))
+				continue;
 			/*
 			 * build_sched_domains() -> init_sched_groups_capacity()
 			 * gets here before we've attached the domains to the
@@ -8037,7 +8115,11 @@ void update_group_capacity(struct sched_domain *sd, int cpu)
 
 		group = child->groups;
 		do {
-			capacity += group->sgc->capacity;
+			cpumask_t *cpus = sched_group_cpus(group);
+
+			/* Revisit this later. This won't work for MT domain */
+			if (!cpu_isolated(cpumask_first(cpus)))
+				capacity += group->sgc->capacity;
 			group = group->next;
 		} while (group != child->groups);
 	}
@@ -8177,6 +8259,9 @@ static inline void update_sg_lb_stats(struct lb_env *env,
 				     power_cost(i, 0),
 				     cpu_temp(i));
 
+		if (cpu_isolated(i))
+			continue;
+
 		/* Bias balancing toward cpus of our domain */
 		if (local_group)
 			load = target_load(i, load_idx);
@@ -8208,17 +8293,27 @@ static inline void update_sg_lb_stats(struct lb_env *env,
 			sgs->idle_cpus++;
 	}
 
-	/* Adjust by relative CPU capacity of the group */
-	sgs->group_capacity = group->sgc->capacity;
-	sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) / sgs->group_capacity;
+	/* Isolated CPU has no weight */
+	if (!group->group_weight) {
+		sgs->group_capacity = 0;
+		sgs->avg_load = 0;
+		sgs->group_no_capacity = 1;
+		sgs->group_type = group_other;
+		sgs->group_weight = group->group_weight;
+	} else {
+		/* Adjust by relative CPU capacity of the group */
+		sgs->group_capacity = group->sgc->capacity;
+		sgs->avg_load = (sgs->group_load*SCHED_CAPACITY_SCALE) /
+							sgs->group_capacity;
+
+		sgs->group_weight = group->group_weight;
+
+		sgs->group_no_capacity = group_is_overloaded(env, sgs);
+		sgs->group_type = group_classify(group, sgs);
+	}
 
 	if (sgs->sum_nr_running)
 		sgs->load_per_task = sgs->sum_weighted_load / sgs->sum_nr_running;
-
-	sgs->group_weight = group->group_weight;
-
-	sgs->group_no_capacity = group_is_overloaded(env, sgs);
-	sgs->group_type = group_classify(group, sgs);
 }
 
 #ifdef CONFIG_SCHED_HMP
@@ -8229,11 +8324,6 @@ static bool update_sd_pick_busiest_active_balance(struct lb_env *env,
 {
 	if (env->idle != CPU_NOT_IDLE &&
 	    cpu_capacity(env->dst_cpu) > group_rq_capacity(sg)) {
-		if (sched_boost() && !sds->busiest && sgs->sum_nr_running) {
-			env->flags |= LBF_SCHED_BOOST_ACTIVE_BALANCE;
-			return true;
-		}
-
 		if (sgs->sum_nr_big_tasks >
 				sds->busiest_stat.sum_nr_big_tasks) {
 			env->flags |= LBF_BIG_TASK_ACTIVE_BALANCE;
@@ -8647,7 +8737,7 @@ static struct sched_group *find_busiest_group(struct lb_env *env)
 	if (!sds.busiest || busiest->sum_nr_running == 0)
 		goto out_balanced;
 
-	if (env->flags & LBF_HMP_ACTIVE_BALANCE)
+	if (env->flags & LBF_BIG_TASK_ACTIVE_BALANCE)
 		goto force_balance;
 
 	if (bail_inter_cluster_balance(env, &sds))
@@ -8723,8 +8813,11 @@ static struct rq *find_busiest_queue_hmp(struct lb_env *env,
 	int max_nr_big = 0, nr_big;
 	bool find_big = !!(env->flags & LBF_BIG_TASK_ACTIVE_BALANCE);
 	int i;
+	cpumask_t cpus;
 
-	for_each_cpu(i, sched_group_cpus(group)) {
+	cpumask_andnot(&cpus, sched_group_cpus(group), cpu_isolated_mask);
+
+	for_each_cpu(i, &cpus) {
 		struct rq *rq = cpu_rq(i);
 		u64 cumulative_runnable_avg =
 				rq->hmp_stats.cumulative_runnable_avg;
@@ -8853,7 +8946,7 @@ static int need_active_balance(struct lb_env *env)
 {
 	struct sched_domain *sd = env->sd;
 
-	if (env->flags & LBF_HMP_ACTIVE_BALANCE)
+	if (env->flags & LBF_BIG_TASK_ACTIVE_BALANCE)
 		return 1;
 
 	if (env->idle == CPU_NEWLY_IDLE) {
@@ -8884,6 +8977,15 @@ static int need_active_balance(struct lb_env *env)
 			sd->cache_nice_tries + NEED_ACTIVE_BALANCE_THRESHOLD);
 }
 
+static int group_balance_cpu_not_isolated(struct sched_group *sg)
+{
+	cpumask_t cpus;
+
+	cpumask_and(&cpus, sched_group_cpus(sg), sched_group_mask(sg));
+	cpumask_andnot(&cpus, &cpus, cpu_isolated_mask);
+	return cpumask_first(&cpus);
+}
+
 static int active_load_balance_cpu_stop(void *data);
 
 static int should_we_balance(struct lb_env *env)
@@ -8903,7 +9005,8 @@ static int should_we_balance(struct lb_env *env)
 	sg_mask = sched_group_mask(sg);
 	/* Try to find first idle cpu */
 	for_each_cpu_and(cpu, sg_cpus, env->cpus) {
-		if (!cpumask_test_cpu(cpu, sg_mask) || !idle_cpu(cpu))
+		if (!cpumask_test_cpu(cpu, sg_mask) || !idle_cpu(cpu) ||
+		    cpu_isolated(cpu))
 			continue;
 
 		balance_cpu = cpu;
@@ -8911,7 +9014,7 @@ static int should_we_balance(struct lb_env *env)
 	}
 
 	if (balance_cpu == -1)
-		balance_cpu = group_balance_cpu(sg);
+		balance_cpu = group_balance_cpu_not_isolated(sg);
 
 	/*
 	 * First idle cpu or the first cpu(busiest) in this sched group
@@ -8936,20 +9039,21 @@ static int load_balance(int this_cpu, struct rq *this_rq,
 	struct cpumask *cpus = this_cpu_cpumask_var_ptr(load_balance_mask);
 
 	struct lb_env env = {
-		.sd		= sd,
-		.dst_cpu	= this_cpu,
-		.dst_rq		= this_rq,
-		.dst_grpmask    = sched_group_cpus(sd->groups),
-		.idle		= idle,
-		.loop_break	= sched_nr_migrate_break,
-		.cpus		= cpus,
-		.fbq_type	= all,
-		.tasks		= LIST_HEAD_INIT(env.tasks),
-		.imbalance	= 0,
-		.flags		= 0,
-		.loop		= 0,
+		.sd			= sd,
+		.dst_cpu		= this_cpu,
+		.dst_rq			= this_rq,
+		.dst_grpmask		= sched_group_cpus(sd->groups),
+		.idle			= idle,
+		.loop_break		= sched_nr_migrate_break,
+		.cpus			= cpus,
+		.fbq_type		= all,
+		.tasks			= LIST_HEAD_INIT(env.tasks),
+		.imbalance		= 0,
+		.flags			= 0,
+		.loop			= 0,
 		.busiest_nr_running     = 0,
 		.busiest_grp_capacity   = 0,
+		.boost_policy		= sched_boost_policy(),
 	};
 
 	/*
@@ -9098,7 +9202,7 @@ static int load_balance(int this_cpu, struct rq *this_rq,
 
 no_move:
 	if (!ld_moved) {
-		if (!(env.flags & LBF_HMP_ACTIVE_BALANCE))
+		if (!(env.flags & LBF_BIG_TASK_ACTIVE_BALANCE))
 			schedstat_inc(sd->lb_failed[idle]);
 		/*
 		 * Increment the failure counter only on periodic balance.
@@ -9107,7 +9211,7 @@ static int load_balance(int this_cpu, struct rq *this_rq,
 		 * excessive cache_hot migrations and active balances.
 		 */
 		if (idle != CPU_NEWLY_IDLE &&
-		    !(env.flags & LBF_HMP_ACTIVE_BALANCE))
+		    !(env.flags & LBF_BIG_TASK_ACTIVE_BALANCE))
 			sd->nr_balance_failed++;
 
 		if (need_active_balance(&env)) {
@@ -9130,7 +9234,8 @@ static int load_balance(int this_cpu, struct rq *this_rq,
 			 * ->active_balance_work.  Once set, it's cleared
 			 * only after active load balance is finished.
 			 */
-			if (!busiest->active_balance) {
+			if (!busiest->active_balance &&
+			    !cpu_isolated(cpu_of(busiest))) {
 				busiest->active_balance = 1;
 				busiest->push_cpu = this_cpu;
 				active_balance = 1;
@@ -9258,6 +9363,9 @@ static int idle_balance(struct rq *this_rq)
 	int pulled_task = 0;
 	u64 curr_cost = 0;
 
+	if (cpu_isolated(this_cpu))
+		return 0;
+
 	/*
 	 * We must set idle_stamp _before_ calling idle_balance(), such that we
 	 * measure the duration of idle_balance() as idle time.
@@ -9374,6 +9482,7 @@ static int active_load_balance_cpu_stop(void *data)
 		.busiest_grp_capacity	= 0,
 		.flags			= 0,
 		.loop			= 0,
+		.boost_policy		= sched_boost_policy(),
 	};
 	bool moved = false;
 
@@ -9498,9 +9607,6 @@ static inline int find_new_hmp_ilb(int type)
 		for_each_cpu_and(ilb, nohz.idle_cpus_mask,
 						sched_domain_span(sd)) {
 			if (idle_cpu(ilb) && (type != NOHZ_KICK_RESTRICT ||
-					(hmp_capable() &&
-					 cpu_max_possible_capacity(ilb) <=
-					cpu_max_possible_capacity(call_cpu)) ||
 					cpu_max_power_cost(ilb) <=
 					cpu_max_power_cost(call_cpu))) {
 				rcu_read_unlock();
@@ -9557,16 +9663,21 @@ static void nohz_balancer_kick(int type)
 	return;
 }
 
+void nohz_balance_clear_nohz_mask(int cpu)
+{
+	if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) {
+		cpumask_clear_cpu(cpu, nohz.idle_cpus_mask);
+		atomic_dec(&nohz.nr_cpus);
+	}
+}
+
 void nohz_balance_exit_idle(unsigned int cpu)
 {
 	if (unlikely(test_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu)))) {
 		/*
 		 * Completely isolated CPUs don't ever set, so we must test.
 		 */
-		if (likely(cpumask_test_cpu(cpu, nohz.idle_cpus_mask))) {
-			cpumask_clear_cpu(cpu, nohz.idle_cpus_mask);
-			atomic_dec(&nohz.nr_cpus);
-		}
+		nohz_balance_clear_nohz_mask(cpu);
 		clear_bit(NOHZ_TICK_STOPPED, nohz_flags(cpu));
 	}
 }
@@ -9623,7 +9734,7 @@ void nohz_balance_enter_idle(int cpu)
 	/*
 	 * If we're a completely isolated CPU, we don't play.
 	 */
-	if (on_null_domain(cpu_rq(cpu)))
+	if (on_null_domain(cpu_rq(cpu)) || cpu_isolated(cpu))
 		return;
 
 	cpumask_set_cpu(cpu, nohz.idle_cpus_mask);
@@ -9640,7 +9751,13 @@ static DEFINE_SPINLOCK(balancing);
  */
 void update_max_interval(void)
 {
-	max_load_balance_interval = HZ*num_online_cpus()/10;
+	cpumask_t avail_mask;
+	unsigned int available_cpus;
+
+	cpumask_andnot(&avail_mask, cpu_online_mask, cpu_isolated_mask);
+	available_cpus = cpumask_weight(&avail_mask);
+
+	max_load_balance_interval = HZ*available_cpus/10;
 }
 
 /*
@@ -9765,12 +9882,15 @@ static void nohz_idle_balance(struct rq *this_rq, enum cpu_idle_type idle)
 	/* Earliest time when we have to do rebalance again */
 	unsigned long next_balance = jiffies + 60*HZ;
 	int update_next_balance = 0;
+	cpumask_t cpus;
 
 	if (idle != CPU_IDLE ||
 	    !test_bit(NOHZ_BALANCE_KICK, nohz_flags(this_cpu)))
 		goto end;
 
-	for_each_cpu(balance_cpu, nohz.idle_cpus_mask) {
+	cpumask_andnot(&cpus, nohz.idle_cpus_mask, cpu_isolated_mask);
+
+	for_each_cpu(balance_cpu, &cpus) {
 		if (balance_cpu == this_cpu || !idle_cpu(balance_cpu))
 			continue;
 
@@ -9822,11 +9942,11 @@ static inline int _nohz_kick_needed_hmp(struct rq *rq, int cpu, int *type)
 	if (rq->nr_running < 2)
 		return 0;
 
-	if (!sysctl_sched_restrict_cluster_spill || sched_boost())
+	if (!sysctl_sched_restrict_cluster_spill ||
+			sched_boost_policy() == SCHED_BOOST_ON_ALL)
 		return 1;
 
-	if (hmp_capable() && cpu_max_possible_capacity(cpu) ==
-			max_possible_capacity)
+	if (cpu_max_power_cost(cpu) == max_power_cost)
 		return 1;
 
 	rcu_read_lock();
@@ -9977,8 +10097,10 @@ void trigger_load_balance(struct rq *rq)
 {
 	int type = NOHZ_KICK_ANY;
 
-	/* Don't need to rebalance while attached to NULL domain */
-	if (unlikely(on_null_domain(rq)))
+	/* Don't need to rebalance while attached to NULL domain or
+	 * cpu is isolated.
+	 */
+	if (unlikely(on_null_domain(rq)) || cpu_isolated(cpu_of(rq)))
 		return;
 
 	if (time_after_eq(jiffies, rq->next_balance))
diff --git a/kernel/sched/hmp.c b/kernel/sched/hmp.c
index 9bc1bac..1de1fb1 100644
--- a/kernel/sched/hmp.c
+++ b/kernel/sched/hmp.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -22,12 +22,12 @@
 
 #include <trace/events/sched.h>
 
+#define CSTATE_LATENCY_GRANULARITY_SHIFT (6)
+
 const char *task_event_names[] = {"PUT_PREV_TASK", "PICK_NEXT_TASK",
 		"TASK_WAKE", "TASK_MIGRATE", "TASK_UPDATE", "IRQ_UPDATE"};
 
-const char *migrate_type_names[] = {"GROUP_TO_RQ", "RQ_TO_GROUP",
-				"RQ_TO_RQ", "GROUP_TO_GROUP"};
-
+const char *migrate_type_names[] = {"GROUP_TO_RQ", "RQ_TO_GROUP"};
 
 static ktime_t ktime_last;
 static bool sched_ktime_suspended;
@@ -72,11 +72,6 @@ inline void clear_ed_task(struct task_struct *p, struct rq *rq)
 		rq->ed_task = NULL;
 }
 
-inline void set_task_last_wake(struct task_struct *p, u64 wallclock)
-{
-	p->last_wake_ts = wallclock;
-}
-
 inline void set_task_last_switch_out(struct task_struct *p, u64 wallclock)
 {
 	p->last_switch_out_ts = wallclock;
@@ -97,7 +92,10 @@ sched_set_cpu_cstate(int cpu, int cstate, int wakeup_energy, int wakeup_latency)
 
 	rq->cstate = cstate; /* C1, C2 etc */
 	rq->wakeup_energy = wakeup_energy;
-	rq->wakeup_latency = wakeup_latency;
+	/* disregard small latency delta (64 us). */
+	rq->wakeup_latency = ((wakeup_latency >>
+			       CSTATE_LATENCY_GRANULARITY_SHIFT) <<
+			      CSTATE_LATENCY_GRANULARITY_SHIFT);
 }
 
 /*
@@ -194,7 +192,7 @@ int sched_update_freq_max_load(const cpumask_t *cpumask)
 			entry = &max_load->freqs[i];
 			freq = costs[i].freq;
 			hpct = get_freq_max_load(cpu, freq);
-			if (hpct <= 0 && hpct > 100)
+			if (hpct <= 0 || hpct > 100)
 				hpct = 100;
 			hfreq = div64_u64((u64)freq * hpct, 100);
 			entry->hdemand =
@@ -356,6 +354,8 @@ DECLARE_BITMAP(all_cluster_ids, NR_CPUS);
 struct sched_cluster *sched_cluster[NR_CPUS];
 int num_clusters;
 
+unsigned int max_power_cost = 1;
+
 struct sched_cluster init_cluster = {
 	.list			=	LIST_HEAD_INIT(init_cluster.list),
 	.id			=	0,
@@ -375,6 +375,7 @@ struct sched_cluster init_cluster = {
 	.dstate_wakeup_latency	=	0,
 	.exec_scale_factor	=	1024,
 	.notifier_sent		=	0,
+	.wake_up_idle		=	0,
 };
 
 static void update_all_clusters_stats(void)
@@ -465,6 +466,7 @@ static void sort_clusters(void)
 {
 	struct sched_cluster *cluster;
 	struct list_head new_head;
+	unsigned int tmp_max = 1;
 
 	INIT_LIST_HEAD(&new_head);
 
@@ -473,7 +475,11 @@ static void sort_clusters(void)
 							       max_task_load());
 		cluster->min_power_cost = power_cost(cluster_first_cpu(cluster),
 							       0);
+
+		if (cluster->max_power_cost > tmp_max)
+			tmp_max = cluster->max_power_cost;
 	}
+	max_power_cost = tmp_max;
 
 	move_list(&new_head, &cluster_head, true);
 
@@ -530,6 +536,7 @@ static struct sched_cluster *alloc_new_cluster(const struct cpumask *cpus)
 	cluster->dstate_wakeup_latency	=	0;
 	cluster->freq_init_done		=	false;
 
+	raw_spin_lock_init(&cluster->load_lock);
 	cluster->cpus = *cpus;
 	cluster->efficiency = arch_get_cpu_efficiency(cpumask_first(cpus));
 
@@ -581,12 +588,14 @@ void update_cluster_topology(void)
 	 * cluster_head visible.
 	 */
 	move_list(&cluster_head, &new_head, false);
+	update_all_clusters_stats();
 }
 
 void init_clusters(void)
 {
 	bitmap_clear(all_cluster_ids, 0, NR_CPUS);
 	init_cluster.cpus = *cpu_possible_mask;
+	raw_spin_lock_init(&init_cluster.load_lock);
 	INIT_LIST_HEAD(&cluster_head);
 }
 
@@ -605,29 +614,6 @@ int register_cpu_cycle_counter_cb(struct cpu_cycle_counter_cb *cb)
 	return 0;
 }
 
-int got_boost_kick(void)
-{
-	int cpu = smp_processor_id();
-	struct rq *rq = cpu_rq(cpu);
-
-	return test_bit(BOOST_KICK, &rq->hmp_flags);
-}
-
-inline void clear_boost_kick(int cpu)
-{
-	struct rq *rq = cpu_rq(cpu);
-
-	clear_bit(BOOST_KICK, &rq->hmp_flags);
-}
-
-inline void boost_kick(int cpu)
-{
-	struct rq *rq = cpu_rq(cpu);
-
-	if (!test_and_set_bit(BOOST_KICK, &rq->hmp_flags))
-		smp_send_reschedule(cpu);
-}
-
 /* Clear any HMP scheduler related requests pending from or on cpu */
 void clear_hmp_request(int cpu)
 {
@@ -637,14 +623,18 @@ void clear_hmp_request(int cpu)
 	clear_boost_kick(cpu);
 	clear_reserved(cpu);
 	if (rq->push_task) {
+		struct task_struct *push_task = NULL;
+
 		raw_spin_lock_irqsave(&rq->lock, flags);
 		if (rq->push_task) {
 			clear_reserved(rq->push_cpu);
-			put_task_struct(rq->push_task);
+			push_task = rq->push_task;
 			rq->push_task = NULL;
 		}
 		rq->active_balance = 0;
 		raw_spin_unlock_irqrestore(&rq->lock, flags);
+		if (push_task)
+			put_task_struct(push_task);
 	}
 }
 
@@ -674,6 +664,19 @@ unsigned int sched_get_static_cluster_pwr_cost(int cpu)
 	return cpu_rq(cpu)->cluster->static_cluster_pwr_cost;
 }
 
+int sched_set_cluster_wake_idle(int cpu, unsigned int wake_idle)
+{
+	struct sched_cluster *cluster = cpu_rq(cpu)->cluster;
+
+	cluster->wake_up_idle = !!wake_idle;
+	return 0;
+}
+
+unsigned int sched_get_cluster_wake_idle(int cpu)
+{
+	return cpu_rq(cpu)->cluster->wake_up_idle;
+}
+
 /*
  * sched_window_stats_policy and sched_ravg_hist_size have a 'sysctl' copy
  * associated with them. This is required for atomic update of those variables
@@ -701,8 +704,6 @@ __read_mostly unsigned int sysctl_sched_window_stats_policy =
 
 __read_mostly unsigned int sysctl_sched_cpu_high_irqload = (10 * NSEC_PER_MSEC);
 
-unsigned int __read_mostly sysctl_sched_enable_colocation = 1;
-
 /*
  * Enable colocation and frequency aggregation for all threads in a process.
  * The children inherits the group id from the parent.
@@ -715,6 +716,12 @@ __read_mostly unsigned int sysctl_sched_new_task_windows = 5;
 #define SCHED_FREQ_ACCOUNT_WAIT_TIME 0
 
 /*
+ * This governs what load needs to be used when reporting CPU busy time
+ * to the cpufreq governor.
+ */
+__read_mostly unsigned int sysctl_sched_freq_reporting_policy;
+
+/*
  * For increase, send notification if
  *      freq_required - cur_freq > sysctl_sched_freq_inc_notify
  */
@@ -750,32 +757,42 @@ unsigned int max_possible_capacity = 1024; /* max(rq->max_possible_capacity) */
 unsigned int
 min_max_possible_capacity = 1024; /* min(rq->max_possible_capacity) */
 
-/* Window size (in ns) */
-__read_mostly unsigned int sched_ravg_window = 10000000;
-
 /* Min window size (in ns) = 10ms */
 #define MIN_SCHED_RAVG_WINDOW 10000000
 
 /* Max window size (in ns) = 1s */
 #define MAX_SCHED_RAVG_WINDOW 1000000000
 
+/* Window size (in ns) */
+__read_mostly unsigned int sched_ravg_window = MIN_SCHED_RAVG_WINDOW;
+
+/* Maximum allowed threshold before freq aggregation must be enabled */
+#define MAX_FREQ_AGGR_THRESH 1000
+
 /* Temporarily disable window-stats activity on all cpus */
 unsigned int __read_mostly sched_disable_window_stats;
 
-/*
- * Major task runtime. If a task runs for more than sched_major_task_runtime
- * in a window, it's considered to be generating majority of workload
- * for this window. Prediction could be adjusted for such tasks.
- */
-__read_mostly unsigned int sched_major_task_runtime = 10000000;
-
-static unsigned int sync_cpu;
-
-static LIST_HEAD(related_thread_groups);
+struct related_thread_group *related_thread_groups[MAX_NUM_CGROUP_COLOC_ID];
+static LIST_HEAD(active_related_thread_groups);
 static DEFINE_RWLOCK(related_thread_group_lock);
 
 #define for_each_related_thread_group(grp) \
-	list_for_each_entry(grp, &related_thread_groups, list)
+	list_for_each_entry(grp, &active_related_thread_groups, list)
+
+/*
+ * Task load is categorized into buckets for the purpose of top task tracking.
+ * The entire range of load from 0 to sched_ravg_window needs to be covered
+ * in NUM_LOAD_INDICES number of buckets. Therefore the size of each bucket
+ * is given by sched_ravg_window / NUM_LOAD_INDICES. Since the default value
+ * of sched_ravg_window is MIN_SCHED_RAVG_WINDOW, use that to compute
+ * sched_load_granule.
+ */
+__read_mostly unsigned int sched_load_granule =
+			MIN_SCHED_RAVG_WINDOW / NUM_LOAD_INDICES;
+
+/* Size of bitmaps maintained to track top tasks */
+static const unsigned int top_tasks_bitmap_size =
+		BITS_TO_LONGS(NUM_LOAD_INDICES + 1) * sizeof(unsigned long);
 
 /*
  * Demand aggregation for frequency purpose:
@@ -823,8 +840,8 @@ static DEFINE_RWLOCK(related_thread_group_lock);
  *	C1 busy time = 5 + 5 + 6 = 16ms
  *
  */
-static __read_mostly unsigned int sched_freq_aggregate;
-__read_mostly unsigned int sysctl_sched_freq_aggregate;
+static __read_mostly unsigned int sched_freq_aggregate = 1;
+__read_mostly unsigned int sysctl_sched_freq_aggregate = 1;
 
 unsigned int __read_mostly sysctl_sched_freq_aggregate_threshold_pct;
 static unsigned int __read_mostly sched_freq_aggregate_threshold;
@@ -838,14 +855,6 @@ unsigned int max_task_load(void)
 	return sched_ravg_window;
 }
 
-/*
- * Scheduler boost is a mechanism to temporarily place tasks on CPUs
- * with higher capacity than those where a task would have normally
- * ended up with their load characteristics. Any entity enabling
- * boost is responsible for disabling it as well.
- */
-unsigned int sysctl_sched_boost;
-
 /* A cpu can no longer accommodate more tasks if:
  *
  *	rq->nr_running > sysctl_sched_spill_nr_run ||
@@ -873,6 +882,13 @@ unsigned int __read_mostly sched_spill_load;
 unsigned int __read_mostly sysctl_sched_spill_load_pct = 100;
 
 /*
+ * Prefer the waker CPU for sync wakee task, if the CPU has only 1 runnable
+ * task. This eliminates the LPM exit latency associated with the idle
+ * CPUs in the waker cluster.
+ */
+unsigned int __read_mostly sysctl_sched_prefer_sync_wakee_to_waker;
+
+/*
  * Tasks whose bandwidth consumption on a cpu is more than
  * sched_upmigrate are considered "big" tasks. Big tasks will be
  * considered for "up" migration, i.e migrating to a cpu with better
@@ -890,6 +906,21 @@ unsigned int __read_mostly sched_downmigrate;
 unsigned int __read_mostly sysctl_sched_downmigrate_pct = 60;
 
 /*
+ * Task groups whose aggregate demand on a cpu is more than
+ * sched_group_upmigrate need to be up-migrated if possible.
+ */
+unsigned int __read_mostly sched_group_upmigrate;
+unsigned int __read_mostly sysctl_sched_group_upmigrate_pct = 100;
+
+/*
+ * Task groups, once up-migrated, will need to drop their aggregate
+ * demand to less than sched_group_downmigrate before they are "down"
+ * migrated.
+ */
+unsigned int __read_mostly sched_group_downmigrate;
+unsigned int __read_mostly sysctl_sched_group_downmigrate_pct = 95;
+
+/*
  * The load scale factor of a CPU gets boosted when its max frequency
  * is restricted due to which the tasks are migrating to higher capacity
  * CPUs early. The sched_upmigrate threshold is auto-upgraded by
@@ -911,33 +942,56 @@ sched_long_cpu_selection_threshold = 100 * NSEC_PER_MSEC;
 
 unsigned int __read_mostly sysctl_sched_restrict_cluster_spill;
 
-void update_up_down_migrate(void)
+/*
+ * Scheduler tries to avoid waking up idle CPUs for tasks running
+ * in short bursts. If the task average burst is less than
+ * sysctl_sched_short_burst nanoseconds and it sleeps on an average
+ * for more than sysctl_sched_short_sleep nanoseconds, then the
+ * task is eligible for packing.
+ */
+unsigned int __read_mostly sysctl_sched_short_burst;
+unsigned int __read_mostly sysctl_sched_short_sleep = 1 * NSEC_PER_MSEC;
+
+static void
+_update_up_down_migrate(unsigned int *up_migrate, unsigned int *down_migrate)
 {
-	unsigned int up_migrate = pct_to_real(sysctl_sched_upmigrate_pct);
-	unsigned int down_migrate = pct_to_real(sysctl_sched_downmigrate_pct);
 	unsigned int delta;
 
 	if (up_down_migrate_scale_factor == 1024)
-		goto done;
+		return;
 
-	delta = up_migrate - down_migrate;
+	delta = *up_migrate - *down_migrate;
 
-	up_migrate /= NSEC_PER_USEC;
-	up_migrate *= up_down_migrate_scale_factor;
-	up_migrate >>= 10;
-	up_migrate *= NSEC_PER_USEC;
+	*up_migrate /= NSEC_PER_USEC;
+	*up_migrate *= up_down_migrate_scale_factor;
+	*up_migrate >>= 10;
+	*up_migrate *= NSEC_PER_USEC;
 
-	up_migrate = min(up_migrate, sched_ravg_window);
+	*up_migrate = min(*up_migrate, sched_ravg_window);
 
-	down_migrate /= NSEC_PER_USEC;
-	down_migrate *= up_down_migrate_scale_factor;
-	down_migrate >>= 10;
-	down_migrate *= NSEC_PER_USEC;
+	*down_migrate /= NSEC_PER_USEC;
+	*down_migrate *= up_down_migrate_scale_factor;
+	*down_migrate >>= 10;
+	*down_migrate *= NSEC_PER_USEC;
 
-	down_migrate = min(down_migrate, up_migrate - delta);
-done:
+	*down_migrate = min(*down_migrate, *up_migrate - delta);
+}
+
+static void update_up_down_migrate(void)
+{
+	unsigned int up_migrate = pct_to_real(sysctl_sched_upmigrate_pct);
+	unsigned int down_migrate = pct_to_real(sysctl_sched_downmigrate_pct);
+
+	_update_up_down_migrate(&up_migrate, &down_migrate);
 	sched_upmigrate = up_migrate;
 	sched_downmigrate = down_migrate;
+
+	up_migrate = pct_to_real(sysctl_sched_group_upmigrate_pct);
+	down_migrate = pct_to_real(sysctl_sched_group_downmigrate_pct);
+
+	_update_up_down_migrate(&up_migrate, &down_migrate);
+	sched_group_upmigrate = up_migrate;
+	sched_group_downmigrate = down_migrate;
 }
 
 void set_hmp_defaults(void)
@@ -947,9 +1001,6 @@ void set_hmp_defaults(void)
 
 	update_up_down_migrate();
 
-	sched_major_task_runtime =
-		mult_frac(sched_ravg_window, MAJOR_TASK_PCT, 100);
-
 	sched_init_task_load_windows =
 		div64_u64((u64)sysctl_sched_init_task_load_pct *
 			  (u64)sched_ravg_window, 100);
@@ -1028,77 +1079,6 @@ u64 cpu_load_sync(int cpu, int sync)
 	return scale_load_to_cpu(cpu_cravg_sync(cpu, sync), cpu);
 }
 
-static int boost_refcount;
-static DEFINE_SPINLOCK(boost_lock);
-static DEFINE_MUTEX(boost_mutex);
-
-static void boost_kick_cpus(void)
-{
-	int i;
-
-	for_each_online_cpu(i) {
-		if (cpu_capacity(i) != max_capacity)
-			boost_kick(i);
-	}
-}
-
-int sched_boost(void)
-{
-	return boost_refcount > 0;
-}
-
-int sched_set_boost(int enable)
-{
-	unsigned long flags;
-	int ret = 0;
-	int old_refcount;
-
-	spin_lock_irqsave(&boost_lock, flags);
-
-	old_refcount = boost_refcount;
-
-	if (enable == 1) {
-		boost_refcount++;
-	} else if (!enable) {
-		if (boost_refcount >= 1)
-			boost_refcount--;
-		else
-			ret = -EINVAL;
-	} else {
-		ret = -EINVAL;
-	}
-
-	if (!old_refcount && boost_refcount)
-		boost_kick_cpus();
-
-	trace_sched_set_boost(boost_refcount);
-	spin_unlock_irqrestore(&boost_lock, flags);
-
-	return ret;
-}
-
-int sched_boost_handler(struct ctl_table *table, int write,
-		void __user *buffer, size_t *lenp,
-		loff_t *ppos)
-{
-	int ret;
-
-	mutex_lock(&boost_mutex);
-	if (!write)
-		sysctl_sched_boost = sched_boost();
-
-	ret = proc_dointvec(table, write, buffer, lenp, ppos);
-	if (ret || !write)
-		goto done;
-
-	ret = (sysctl_sched_boost <= 1) ?
-		sched_set_boost(sysctl_sched_boost) : -EINVAL;
-
-done:
-	mutex_unlock(&boost_mutex);
-	return ret;
-}
-
 /*
  * Task will fit on a cpu if it's bandwidth consumption on that cpu
  * will be less than sched_upmigrate. A big task that was previously
@@ -1108,63 +1088,63 @@ int sched_boost_handler(struct ctl_table *table, int write,
  * tasks with load close to the upmigrate threshold
  */
 int task_load_will_fit(struct task_struct *p, u64 task_load, int cpu,
-			      enum sched_boost_type boost_type)
+			      enum sched_boost_policy boost_policy)
 {
-	int upmigrate;
+	int upmigrate = sched_upmigrate;
 
 	if (cpu_capacity(cpu) == max_capacity)
 		return 1;
 
-	if (boost_type != SCHED_BOOST_ON_BIG) {
+	if (cpu_capacity(task_cpu(p)) > cpu_capacity(cpu))
+		upmigrate = sched_downmigrate;
+
+	if (boost_policy != SCHED_BOOST_ON_BIG) {
 		if (task_nice(p) > SCHED_UPMIGRATE_MIN_NICE ||
 		    upmigrate_discouraged(p))
 			return 1;
 
-		upmigrate = sched_upmigrate;
-		if (cpu_capacity(task_cpu(p)) > cpu_capacity(cpu))
-			upmigrate = sched_downmigrate;
-
 		if (task_load < upmigrate)
 			return 1;
+	} else {
+		if (task_sched_boost(p) || task_load >= upmigrate)
+			return 0;
+
+		return 1;
 	}
 
 	return 0;
 }
 
-enum sched_boost_type sched_boost_type(void)
-{
-	if (sched_boost()) {
-		if (min_possible_efficiency != max_possible_efficiency)
-			return SCHED_BOOST_ON_BIG;
-		else
-			return SCHED_BOOST_ON_ALL;
-	}
-	return SCHED_BOOST_NONE;
-}
-
 int task_will_fit(struct task_struct *p, int cpu)
 {
 	u64 tload = scale_load_to_cpu(task_load(p), cpu);
 
-	return task_load_will_fit(p, tload, cpu, sched_boost_type());
+	return task_load_will_fit(p, tload, cpu, sched_boost_policy());
 }
 
-int group_will_fit(struct sched_cluster *cluster,
-		 struct related_thread_group *grp, u64 demand)
+static int
+group_will_fit(struct sched_cluster *cluster, struct related_thread_group *grp,
+						u64 demand, bool group_boost)
 {
 	int cpu = cluster_first_cpu(cluster);
 	int prev_capacity = 0;
-	unsigned int threshold = sched_upmigrate;
+	unsigned int threshold = sched_group_upmigrate;
 	u64 load;
 
 	if (cluster->capacity == max_capacity)
 		return 1;
 
+	if (group_boost)
+		return 0;
+
+	if (!demand)
+		return 1;
+
 	if (grp->preferred_cluster)
 		prev_capacity = grp->preferred_cluster->capacity;
 
 	if (cluster->capacity < prev_capacity)
-		threshold = sched_downmigrate;
+		threshold = sched_group_downmigrate;
 
 	load = scale_load_to_cpu(demand, cpu);
 	if (load < threshold)
@@ -1279,7 +1259,7 @@ void dec_rq_hmp_stats(struct rq *rq, struct task_struct *p, int change_cra)
 		dec_cumulative_runnable_avg(&rq->hmp_stats, p);
 }
 
-static void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra)
+void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra)
 {
 	stats->nr_big_tasks = 0;
 	if (reset_cra) {
@@ -1288,27 +1268,15 @@ static void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra)
 	}
 }
 
-/*
- * Invoked from three places:
- *	1) try_to_wake_up() -> ... -> select_best_cpu()
- *	2) scheduler_tick() -> ... -> migration_needed() -> select_best_cpu()
- *	3) can_migrate_task()
- *
- * Its safe to de-reference p->grp in first case (since p->pi_lock is held)
- * but not in other cases. p->grp is hence freed after a RCU grace period and
- * accessed under rcu_read_lock()
- */
 int preferred_cluster(struct sched_cluster *cluster, struct task_struct *p)
 {
 	struct related_thread_group *grp;
-	int rc = 0;
+	int rc = 1;
 
 	rcu_read_lock();
 
 	grp = task_related_thread_group(p);
-	if (!grp || !sysctl_sched_enable_colocation)
-		rc = 1;
-	else
+	if (grp)
 		rc = (grp->preferred_cluster == cluster);
 
 	rcu_read_unlock();
@@ -1397,6 +1365,23 @@ void post_big_task_count_change(const struct cpumask *cpus)
 
 DEFINE_MUTEX(policy_mutex);
 
+unsigned int update_freq_aggregate_threshold(unsigned int threshold)
+{
+	unsigned int old_threshold;
+
+	mutex_lock(&policy_mutex);
+
+	old_threshold = sysctl_sched_freq_aggregate_threshold_pct;
+
+	sysctl_sched_freq_aggregate_threshold_pct = threshold;
+	sched_freq_aggregate_threshold =
+		pct_to_real(sysctl_sched_freq_aggregate_threshold_pct);
+
+	mutex_unlock(&policy_mutex);
+
+	return old_threshold;
+}
+
 static inline int invalid_value_freq_input(unsigned int *data)
 {
 	if (data == &sysctl_sched_freq_aggregate)
@@ -1420,7 +1405,7 @@ static inline int invalid_value(unsigned int *data)
 
 /*
  * Handle "atomic" update of sysctl_sched_window_stats_policy,
- * sysctl_sched_ravg_hist_size and sched_freq_legacy_mode variables.
+ * sysctl_sched_ravg_hist_size variables.
  */
 int sched_window_update_handler(struct ctl_table *table, int write,
 		void __user *buffer, size_t *lenp,
@@ -1463,7 +1448,17 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
 	int ret;
 	unsigned int old_val;
 	unsigned int *data = (unsigned int *)table->data;
-	int update_min_nice = 0;
+	int update_task_count = 0;
+
+	/*
+	 * The policy mutex is acquired with cpu_hotplug.lock
+	 * held from cpu_up()->cpufreq_governor_interactive()->
+	 * sched_set_window(). So enforce the same order here.
+	 */
+	if (write && (data == &sysctl_sched_upmigrate_pct)) {
+		update_task_count = 1;
+		get_online_cpus();
+	}
 
 	mutex_lock(&policy_mutex);
 
@@ -1477,7 +1472,9 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
 	if (write && (old_val == *data))
 		goto done;
 
-	if (sysctl_sched_downmigrate_pct > sysctl_sched_upmigrate_pct) {
+	if (sysctl_sched_downmigrate_pct > sysctl_sched_upmigrate_pct ||
+				sysctl_sched_group_downmigrate_pct >
+				sysctl_sched_group_upmigrate_pct) {
 		*data = old_val;
 		ret = -EINVAL;
 		goto done;
@@ -1491,20 +1488,18 @@ int sched_hmp_proc_update_handler(struct ctl_table *table, int write,
 	 * includes taking runqueue lock of all online cpus and re-initiatizing
 	 * their big counter values based on changed criteria.
 	 */
-	if ((data == &sysctl_sched_upmigrate_pct || update_min_nice)) {
-		get_online_cpus();
+	if (update_task_count)
 		pre_big_task_count_change(cpu_online_mask);
-	}
 
 	set_hmp_defaults();
 
-	if ((data == &sysctl_sched_upmigrate_pct || update_min_nice)) {
+	if (update_task_count)
 		post_big_task_count_change(cpu_online_mask);
-		put_online_cpus();
-	}
 
 done:
 	mutex_unlock(&policy_mutex);
+	if (update_task_count)
+		put_online_cpus();
 	return ret;
 }
 
@@ -1523,7 +1518,21 @@ unsigned int cpu_temp(int cpu)
 		return 0;
 }
 
-void init_new_task_load(struct task_struct *p)
+void free_task_load_ptrs(struct task_struct *p)
+{
+	kfree(p->ravg.curr_window_cpu);
+	kfree(p->ravg.prev_window_cpu);
+
+	/*
+	 * update_task_ravg() can be called for exiting tasks. While the
+	 * function itself ensures correct behavior, the corresponding
+	 * trace event requires that these pointers be NULL.
+	 */
+	p->ravg.curr_window_cpu = NULL;
+	p->ravg.prev_window_cpu = NULL;
+}
+
+void init_new_task_load(struct task_struct *p, bool idle_task)
 {
 	int i;
 	u32 init_load_windows = sched_init_task_load_windows;
@@ -1534,6 +1543,24 @@ void init_new_task_load(struct task_struct *p)
 	INIT_LIST_HEAD(&p->grp_list);
 	memset(&p->ravg, 0, sizeof(struct ravg));
 	p->cpu_cycles = 0;
+	p->ravg.curr_burst = 0;
+	/*
+	 * Initialize the avg_burst to twice the threshold, so that
+	 * a task would not be classified as short burst right away
+	 * after fork. It takes at least 6 sleep-wakeup cycles for
+	 * the avg_burst to go below the threshold.
+	 */
+	p->ravg.avg_burst = 2 * (u64)sysctl_sched_short_burst;
+	p->ravg.avg_sleep_time = 0;
+
+	p->ravg.curr_window_cpu = kcalloc(nr_cpu_ids, sizeof(u32), GFP_KERNEL);
+	p->ravg.prev_window_cpu = kcalloc(nr_cpu_ids, sizeof(u32), GFP_KERNEL);
+
+	/* Don't have much choice. CPU frequency would be bogus */
+	BUG_ON(!p->ravg.curr_window_cpu || !p->ravg.prev_window_cpu);
+
+	if (idle_task)
+		return;
 
 	if (init_load_pct)
 		init_load_windows = div64_u64((u64)init_load_pct *
@@ -1662,48 +1689,23 @@ static inline unsigned int load_to_freq(struct rq *rq, u64 load)
 	return freq;
 }
 
-static inline struct group_cpu_time *
-_group_cpu_time(struct related_thread_group *grp, int cpu);
-
-/*
- * Return load from all related group in given cpu.
- * Caller must ensure that related_thread_group_lock is held.
- */
-static void _group_load_in_cpu(int cpu, u64 *grp_load, u64 *new_grp_load)
-{
-	struct related_thread_group *grp;
-
-	for_each_related_thread_group(grp) {
-		struct group_cpu_time *cpu_time;
-
-		cpu_time = _group_cpu_time(grp, cpu);
-		*grp_load += cpu_time->prev_runnable_sum;
-		if (new_grp_load)
-			*new_grp_load += cpu_time->nt_prev_runnable_sum;
-	}
-}
-
 /*
  * Return load from all related groups in given frequency domain.
- * Caller must ensure that related_thread_group_lock is held.
  */
 static void group_load_in_freq_domain(struct cpumask *cpus,
 				u64 *grp_load, u64 *new_grp_load)
 {
-	struct related_thread_group *grp;
 	int j;
 
-	for_each_related_thread_group(grp) {
-		for_each_cpu(j, cpus) {
-			struct group_cpu_time *cpu_time;
+	for_each_cpu(j, cpus) {
+		struct rq *rq = cpu_rq(j);
 
-			cpu_time = _group_cpu_time(grp, j);
-			*grp_load += cpu_time->prev_runnable_sum;
-			*new_grp_load += cpu_time->nt_prev_runnable_sum;
-		}
+		*grp_load += rq->grp_time.prev_runnable_sum;
+		*new_grp_load += rq->grp_time.nt_prev_runnable_sum;
 	}
 }
 
+static inline u64 freq_policy_load(struct rq *rq, u64 load);
 /*
  * Should scheduler alert governor for changing frequency?
  *
@@ -1741,19 +1743,18 @@ static int send_notification(struct rq *rq, int check_pred, int check_groups)
 		if (freq_required < cur_freq + sysctl_sched_pred_alert_freq)
 			return 0;
 	} else {
-		read_lock(&related_thread_group_lock);
 		/*
 		 * Protect from concurrent update of rq->prev_runnable_sum and
 		 * group cpu load
 		 */
 		raw_spin_lock_irqsave(&rq->lock, flags);
 		if (check_groups)
-			_group_load_in_cpu(cpu_of(rq), &group_load, NULL);
+			group_load = rq->grp_time.prev_runnable_sum;
 
 		new_load = rq->prev_runnable_sum + group_load;
+		new_load = freq_policy_load(rq, new_load);
 
 		raw_spin_unlock_irqrestore(&rq->lock, flags);
-		read_unlock(&related_thread_group_lock);
 
 		cur_freq = load_to_freq(rq, rq->old_busy_time);
 		freq_required = load_to_freq(rq, new_load);
@@ -1904,8 +1905,6 @@ scale_load_to_freq(u64 load, unsigned int src_freq, unsigned int dst_freq)
 	return div64_u64(load * (u64)src_freq, (u64)dst_freq);
 }
 
-#define HEAVY_TASK_SKIP 2
-#define HEAVY_TASK_SKIP_LIMIT 4
 /*
  * get_pred_busy - calculate predicted demand for a task on runqueue
  *
@@ -1933,7 +1932,7 @@ static u32 get_pred_busy(struct rq *rq, struct task_struct *p,
 	u32 *hist = p->ravg.sum_history;
 	u32 dmin, dmax;
 	u64 cur_freq_runtime = 0;
-	int first = NUM_BUSY_BUCKETS, final, skip_to;
+	int first = NUM_BUSY_BUCKETS, final;
 	u32 ret = runtime;
 
 	/* skip prediction for new tasks due to lack of history */
@@ -1953,36 +1952,6 @@ static u32 get_pred_busy(struct rq *rq, struct task_struct *p,
 
 	/* compute the bucket for prediction */
 	final = first;
-	if (first < HEAVY_TASK_SKIP_LIMIT) {
-		/* compute runtime at current CPU frequency */
-		cur_freq_runtime = mult_frac(runtime, max_possible_efficiency,
-					     rq->cluster->efficiency);
-		cur_freq_runtime = scale_load_to_freq(cur_freq_runtime,
-				max_possible_freq, rq->cluster->cur_freq);
-		/*
-		 * if the task runs for majority of the window, try to
-		 * pick higher buckets.
-		 */
-		if (cur_freq_runtime >= sched_major_task_runtime) {
-			int next = NUM_BUSY_BUCKETS;
-			/*
-			 * if there is a higher bucket that's consistently
-			 * hit, don't jump beyond that.
-			 */
-			for (i = start + 1; i <= HEAVY_TASK_SKIP_LIMIT &&
-			     i < NUM_BUSY_BUCKETS; i++) {
-				if (buckets[i] > CONSISTENT_THRES) {
-					next = i;
-					break;
-				}
-			}
-			skip_to = min(next, start + HEAVY_TASK_SKIP);
-			/* don't jump beyond HEAVY_TASK_SKIP_LIMIT */
-			skip_to = min(HEAVY_TASK_SKIP_LIMIT, skip_to);
-			/* don't go below first non-empty bucket, if any */
-			final = max(first, skip_to);
-		}
-	}
 
 	/* determine demand range for the predicted bucket */
 	if (final < 2) {
@@ -2070,6 +2039,220 @@ void update_task_pred_demand(struct rq *rq, struct task_struct *p, int event)
 	p->ravg.pred_demand = new;
 }
 
+void clear_top_tasks_bitmap(unsigned long *bitmap)
+{
+	memset(bitmap, 0, top_tasks_bitmap_size);
+	__set_bit(NUM_LOAD_INDICES, bitmap);
+}
+
+/*
+ * Special case the last index and provide a fast path for index = 0.
+ * Note that sched_load_granule can change underneath us if we are not
+ * holding any runqueue locks while calling the two functions below.
+ */
+static u32  top_task_load(struct rq *rq)
+{
+	int index = rq->prev_top;
+	u8 prev = 1 - rq->curr_table;
+
+	if (!index) {
+		int msb = NUM_LOAD_INDICES - 1;
+
+		if (!test_bit(msb, rq->top_tasks_bitmap[prev]))
+			return 0;
+		else
+			return sched_load_granule;
+	} else if (index == NUM_LOAD_INDICES - 1) {
+		return sched_ravg_window;
+	} else {
+		return (index + 1) * sched_load_granule;
+	}
+}
+
+static int load_to_index(u32 load)
+{
+	if (load < sched_load_granule)
+		return 0;
+	else if (load >= sched_ravg_window)
+		return NUM_LOAD_INDICES - 1;
+	else
+		return load / sched_load_granule;
+}
+
+static void update_top_tasks(struct task_struct *p, struct rq *rq,
+		u32 old_curr_window, int new_window, bool full_window)
+{
+	u8 curr = rq->curr_table;
+	u8 prev = 1 - curr;
+	u8 *curr_table = rq->top_tasks[curr];
+	u8 *prev_table = rq->top_tasks[prev];
+	int old_index, new_index, update_index;
+	u32 curr_window = p->ravg.curr_window;
+	u32 prev_window = p->ravg.prev_window;
+	bool zero_index_update;
+
+	if (old_curr_window == curr_window && !new_window)
+		return;
+
+	old_index = load_to_index(old_curr_window);
+	new_index = load_to_index(curr_window);
+
+	if (!new_window) {
+		zero_index_update = !old_curr_window && curr_window;
+		if (old_index != new_index || zero_index_update) {
+			if (old_curr_window)
+				curr_table[old_index] -= 1;
+			if (curr_window)
+				curr_table[new_index] += 1;
+			if (new_index > rq->curr_top)
+				rq->curr_top = new_index;
+		}
+
+		if (!curr_table[old_index])
+			__clear_bit(NUM_LOAD_INDICES - old_index - 1,
+				rq->top_tasks_bitmap[curr]);
+
+		if (curr_table[new_index] == 1)
+			__set_bit(NUM_LOAD_INDICES - new_index - 1,
+				rq->top_tasks_bitmap[curr]);
+
+		return;
+	}
+
+	/*
+	 * The window has rolled over for this task. By the time we get
+	 * here, curr/prev swaps would has already occurred. So we need
+	 * to use prev_window for the new index.
+	 */
+	update_index = load_to_index(prev_window);
+
+	if (full_window) {
+		/*
+		 * Two cases here. Either 'p' ran for the entire window or
+		 * it didn't run at all. In either case there is no entry
+		 * in the prev table. If 'p' ran the entire window, we just
+		 * need to create a new entry in the prev table. In this case
+		 * update_index will be correspond to sched_ravg_window
+		 * so we can unconditionally update the top index.
+		 */
+		if (prev_window) {
+			prev_table[update_index] += 1;
+			rq->prev_top = update_index;
+		}
+
+		if (prev_table[update_index] == 1)
+			__set_bit(NUM_LOAD_INDICES - update_index - 1,
+				rq->top_tasks_bitmap[prev]);
+	} else {
+		zero_index_update = !old_curr_window && prev_window;
+		if (old_index != update_index || zero_index_update) {
+			if (old_curr_window)
+				prev_table[old_index] -= 1;
+
+			prev_table[update_index] += 1;
+
+			if (update_index > rq->prev_top)
+				rq->prev_top = update_index;
+
+			if (!prev_table[old_index])
+				__clear_bit(NUM_LOAD_INDICES - old_index - 1,
+						rq->top_tasks_bitmap[prev]);
+
+			if (prev_table[update_index] == 1)
+				__set_bit(NUM_LOAD_INDICES - update_index - 1,
+						rq->top_tasks_bitmap[prev]);
+		}
+	}
+
+	if (curr_window) {
+		curr_table[new_index] += 1;
+
+		if (new_index > rq->curr_top)
+			rq->curr_top = new_index;
+
+		if (curr_table[new_index] == 1)
+			__set_bit(NUM_LOAD_INDICES - new_index - 1,
+				rq->top_tasks_bitmap[curr]);
+	}
+}
+
+static inline void clear_top_tasks_table(u8 *table)
+{
+	memset(table, 0, NUM_LOAD_INDICES * sizeof(u8));
+}
+
+static void rollover_top_tasks(struct rq *rq, bool full_window)
+{
+	u8 curr_table = rq->curr_table;
+	u8 prev_table = 1 - curr_table;
+	int curr_top = rq->curr_top;
+
+	clear_top_tasks_table(rq->top_tasks[prev_table]);
+	clear_top_tasks_bitmap(rq->top_tasks_bitmap[prev_table]);
+
+	if (full_window) {
+		curr_top = 0;
+		clear_top_tasks_table(rq->top_tasks[curr_table]);
+		clear_top_tasks_bitmap(
+				rq->top_tasks_bitmap[curr_table]);
+	}
+
+	rq->curr_table = prev_table;
+	rq->prev_top = curr_top;
+	rq->curr_top = 0;
+}
+
+static u32 empty_windows[NR_CPUS];
+
+static void rollover_task_window(struct task_struct *p, bool full_window)
+{
+	u32 *curr_cpu_windows = empty_windows;
+	u32 curr_window;
+	int i;
+
+	/* Rollover the sum */
+	curr_window = 0;
+
+	if (!full_window) {
+		curr_window = p->ravg.curr_window;
+		curr_cpu_windows = p->ravg.curr_window_cpu;
+	}
+
+	p->ravg.prev_window = curr_window;
+	p->ravg.curr_window = 0;
+
+	/* Roll over individual CPU contributions */
+	for (i = 0; i < nr_cpu_ids; i++) {
+		p->ravg.prev_window_cpu[i] = curr_cpu_windows[i];
+		p->ravg.curr_window_cpu[i] = 0;
+	}
+}
+
+static void rollover_cpu_window(struct rq *rq, bool full_window)
+{
+	u64 curr_sum = rq->curr_runnable_sum;
+	u64 nt_curr_sum = rq->nt_curr_runnable_sum;
+	u64 grp_curr_sum = rq->grp_time.curr_runnable_sum;
+	u64 grp_nt_curr_sum = rq->grp_time.nt_curr_runnable_sum;
+
+	if (unlikely(full_window)) {
+		curr_sum = 0;
+		nt_curr_sum = 0;
+		grp_curr_sum = 0;
+		grp_nt_curr_sum = 0;
+	}
+
+	rq->prev_runnable_sum = curr_sum;
+	rq->nt_prev_runnable_sum = nt_curr_sum;
+	rq->grp_time.prev_runnable_sum = grp_curr_sum;
+	rq->grp_time.nt_prev_runnable_sum = grp_nt_curr_sum;
+
+	rq->curr_runnable_sum = 0;
+	rq->nt_curr_runnable_sum = 0;
+	rq->grp_time.curr_runnable_sum = 0;
+	rq->grp_time.nt_curr_runnable_sum = 0;
+}
+
 /*
  * Account cpu activity in its busy time counters (rq->curr/prev_runnable_sum)
  */
@@ -2086,10 +2269,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 	u64 *prev_runnable_sum = &rq->prev_runnable_sum;
 	u64 *nt_curr_runnable_sum = &rq->nt_curr_runnable_sum;
 	u64 *nt_prev_runnable_sum = &rq->nt_prev_runnable_sum;
-	int flip_counters = 0;
-	int prev_sum_reset = 0;
 	bool new_task;
 	struct related_thread_group *grp;
+	int cpu = rq->cpu;
+	u32 old_curr_window = p->ravg.curr_window;
 
 	new_window = mark_start < window_start;
 	if (new_window) {
@@ -2100,105 +2283,32 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 
 	new_task = is_new_task(p);
 
+	/*
+	 * Handle per-task window rollover. We don't care about the idle
+	 * task or exiting tasks.
+	 */
+	if (!is_idle_task(p) && !exiting_task(p)) {
+		if (new_window)
+			rollover_task_window(p, full_window);
+	}
+
+	if (p_is_curr_task && new_window) {
+		rollover_cpu_window(rq, full_window);
+		rollover_top_tasks(rq, full_window);
+	}
+
+	if (!account_busy_for_cpu_time(rq, p, irqtime, event))
+		goto done;
+
 	grp = p->grp;
 	if (grp && sched_freq_aggregate) {
-		/* cpu_time protected by rq_lock */
-		struct group_cpu_time *cpu_time =
-			_group_cpu_time(grp, cpu_of(rq));
+		struct group_cpu_time *cpu_time = &rq->grp_time;
 
 		curr_runnable_sum = &cpu_time->curr_runnable_sum;
 		prev_runnable_sum = &cpu_time->prev_runnable_sum;
 
 		nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
 		nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
-
-		if (cpu_time->window_start != rq->window_start) {
-			int nr_windows;
-
-			delta = rq->window_start - cpu_time->window_start;
-			nr_windows = div64_u64(delta, window_size);
-			if (nr_windows > 1)
-				prev_sum_reset = 1;
-
-			cpu_time->window_start = rq->window_start;
-			flip_counters = 1;
-		}
-
-		if (p_is_curr_task && new_window) {
-			u64 curr_sum = rq->curr_runnable_sum;
-			u64 nt_curr_sum = rq->nt_curr_runnable_sum;
-
-			if (full_window)
-				curr_sum = nt_curr_sum = 0;
-
-			rq->prev_runnable_sum = curr_sum;
-			rq->nt_prev_runnable_sum = nt_curr_sum;
-
-			rq->curr_runnable_sum = 0;
-			rq->nt_curr_runnable_sum = 0;
-		}
-	} else {
-		if (p_is_curr_task && new_window) {
-			flip_counters = 1;
-			if (full_window)
-				prev_sum_reset = 1;
-		}
-	}
-
-	/*
-	 * Handle per-task window rollover. We don't care about the idle
-	 * task or exiting tasks.
-	 */
-	if (new_window && !is_idle_task(p) && !exiting_task(p)) {
-		u32 curr_window = 0;
-
-		if (!full_window)
-			curr_window = p->ravg.curr_window;
-
-		p->ravg.prev_window = curr_window;
-		p->ravg.curr_window = 0;
-	}
-
-	if (flip_counters) {
-		u64 curr_sum = *curr_runnable_sum;
-		u64 nt_curr_sum = *nt_curr_runnable_sum;
-
-		if (prev_sum_reset)
-			curr_sum = nt_curr_sum = 0;
-
-		*prev_runnable_sum = curr_sum;
-		*nt_prev_runnable_sum = nt_curr_sum;
-
-		*curr_runnable_sum = 0;
-		*nt_curr_runnable_sum = 0;
-	}
-
-	if (!account_busy_for_cpu_time(rq, p, irqtime, event)) {
-		/*
-		 * account_busy_for_cpu_time() = 0, so no update to the
-		 * task's current window needs to be made. This could be
-		 * for example
-		 *
-		 *   - a wakeup event on a task within the current
-		 *     window (!new_window below, no action required),
-		 *   - switching to a new task from idle (PICK_NEXT_TASK)
-		 *     in a new window where irqtime is 0 and we aren't
-		 *     waiting on IO
-		 */
-
-		if (!new_window)
-			return;
-
-		/*
-		 * A new window has started. The RQ demand must be rolled
-		 * over if p is the current task.
-		 */
-		if (p_is_curr_task) {
-			/* p is idle task */
-			BUG_ON(p != rq->idle);
-		}
-
-		return;
 	}
 
 	if (!new_window) {
@@ -2219,10 +2329,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 		if (new_task)
 			*nt_curr_runnable_sum += delta;
 
-		if (!is_idle_task(p) && !exiting_task(p))
+		if (!is_idle_task(p) && !exiting_task(p)) {
 			p->ravg.curr_window += delta;
+			p->ravg.curr_window_cpu[cpu] += delta;
+		}
 
-		return;
+		goto done;
 	}
 
 	if (!p_is_curr_task) {
@@ -2245,8 +2357,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 			 * contribution to previous completed window.
 			 */
 			delta = scale_exec_time(window_start - mark_start, rq);
-			if (!exiting_task(p))
+			if (!exiting_task(p)) {
 				p->ravg.prev_window += delta;
+				p->ravg.prev_window_cpu[cpu] += delta;
+			}
 		} else {
 			/*
 			 * Since at least one full window has elapsed,
@@ -2254,8 +2368,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 			 * full window (window_size).
 			 */
 			delta = scale_exec_time(window_size, rq);
-			if (!exiting_task(p))
+			if (!exiting_task(p)) {
 				p->ravg.prev_window = delta;
+				p->ravg.prev_window_cpu[cpu] = delta;
+			}
 		}
 
 		*prev_runnable_sum += delta;
@@ -2268,10 +2384,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 		if (new_task)
 			*nt_curr_runnable_sum += delta;
 
-		if (!exiting_task(p))
+		if (!exiting_task(p)) {
 			p->ravg.curr_window = delta;
+			p->ravg.curr_window_cpu[cpu] = delta;
+		}
 
-		return;
+		goto done;
 	}
 
 	if (!irqtime || !is_idle_task(p) || cpu_is_waiting_on_io(rq)) {
@@ -2295,8 +2413,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 			 * contribution to previous completed window.
 			 */
 			delta = scale_exec_time(window_start - mark_start, rq);
-			if (!is_idle_task(p) && !exiting_task(p))
+			if (!is_idle_task(p) && !exiting_task(p)) {
 				p->ravg.prev_window += delta;
+				p->ravg.prev_window_cpu[cpu] += delta;
+			}
 		} else {
 			/*
 			 * Since at least one full window has elapsed,
@@ -2304,8 +2424,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 			 * full window (window_size).
 			 */
 			delta = scale_exec_time(window_size, rq);
-			if (!is_idle_task(p) && !exiting_task(p))
+			if (!is_idle_task(p) && !exiting_task(p)) {
 				p->ravg.prev_window = delta;
+				p->ravg.prev_window_cpu[cpu] = delta;
+			}
 		}
 
 		/*
@@ -2322,10 +2444,12 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 		if (new_task)
 			*nt_curr_runnable_sum += delta;
 
-		if (!is_idle_task(p) && !exiting_task(p))
+		if (!is_idle_task(p) && !exiting_task(p)) {
 			p->ravg.curr_window = delta;
+			p->ravg.curr_window_cpu[cpu] = delta;
+		}
 
-		return;
+		goto done;
 	}
 
 	if (irqtime) {
@@ -2370,7 +2494,10 @@ static void update_cpu_busy_time(struct task_struct *p, struct rq *rq,
 		return;
 	}
 
-	BUG();
+done:
+	if (!is_idle_task(p) && !exiting_task(p))
+		update_top_tasks(p, rq, old_curr_window,
+					new_window, full_window);
 }
 
 static inline u32 predict_and_update_buckets(struct rq *rq,
@@ -2533,12 +2660,14 @@ static void update_history(struct rq *rq, struct task_struct *p,
 	trace_sched_update_history(rq, p, runtime, samples, event);
 }
 
-static void add_to_task_demand(struct rq *rq, struct task_struct *p, u64 delta)
+static u64 add_to_task_demand(struct rq *rq, struct task_struct *p, u64 delta)
 {
 	delta = scale_exec_time(delta, rq);
 	p->ravg.sum += delta;
 	if (unlikely(p->ravg.sum > sched_ravg_window))
 		p->ravg.sum = sched_ravg_window;
+
+	return delta;
 }
 
 /*
@@ -2591,13 +2720,14 @@ static void add_to_task_demand(struct rq *rq, struct task_struct *p, u64 delta)
  * IMPORTANT : Leave p->ravg.mark_start unchanged, as update_cpu_busy_time()
  * depends on it!
  */
-static void update_task_demand(struct task_struct *p, struct rq *rq,
+static u64 update_task_demand(struct task_struct *p, struct rq *rq,
 			       int event, u64 wallclock)
 {
 	u64 mark_start = p->ravg.mark_start;
 	u64 delta, window_start = rq->window_start;
 	int new_window, nr_full_windows;
 	u32 window_size = sched_ravg_window;
+	u64 runtime;
 
 	new_window = mark_start < window_start;
 	if (!account_busy_for_task_demand(p, event)) {
@@ -2611,7 +2741,7 @@ static void update_task_demand(struct task_struct *p, struct rq *rq,
 			 * it is not necessary to account those.
 			 */
 			update_history(rq, p, p->ravg.sum, 1, event);
-		return;
+		return 0;
 	}
 
 	if (!new_window) {
@@ -2619,8 +2749,7 @@ static void update_task_demand(struct task_struct *p, struct rq *rq,
 		 * The simple case - busy time contained within the existing
 		 * window.
 		 */
-		add_to_task_demand(rq, p, wallclock - mark_start);
-		return;
+		return add_to_task_demand(rq, p, wallclock - mark_start);
 	}
 
 	/*
@@ -2632,13 +2761,16 @@ static void update_task_demand(struct task_struct *p, struct rq *rq,
 	window_start -= (u64)nr_full_windows * (u64)window_size;
 
 	/* Process (window_start - mark_start) first */
-	add_to_task_demand(rq, p, window_start - mark_start);
+	runtime = add_to_task_demand(rq, p, window_start - mark_start);
 
 	/* Push new sample(s) into task's demand history */
 	update_history(rq, p, p->ravg.sum, 1, event);
-	if (nr_full_windows)
-		update_history(rq, p, scale_exec_time(window_size, rq),
-			       nr_full_windows, event);
+	if (nr_full_windows) {
+		u64 scaled_window = scale_exec_time(window_size, rq);
+
+		update_history(rq, p, scaled_window, nr_full_windows, event);
+		runtime += nr_full_windows * scaled_window;
+	}
 
 	/*
 	 * Roll window_start back to current to process any remainder
@@ -2648,14 +2780,33 @@ static void update_task_demand(struct task_struct *p, struct rq *rq,
 
 	/* Process (wallclock - window_start) next */
 	mark_start = window_start;
-	add_to_task_demand(rq, p, wallclock - mark_start);
+	runtime += add_to_task_demand(rq, p, wallclock - mark_start);
+
+	return runtime;
+}
+
+static inline void
+update_task_burst(struct task_struct *p, struct rq *rq, int event, u64 runtime)
+{
+	/*
+	 * update_task_demand() has checks for idle task and
+	 * exit task. The runtime may include the wait time,
+	 * so update the burst only for the cases where the
+	 * task is running.
+	 */
+	if (event == PUT_PREV_TASK || (event == TASK_UPDATE &&
+				rq->curr == p))
+		p->ravg.curr_burst += runtime;
 }
 
 /* Reflect task activity on its demand and cpu's busy time statistics */
 void update_task_ravg(struct task_struct *p, struct rq *rq, int event,
 						u64 wallclock, u64 irqtime)
 {
-	if (!rq->window_start || sched_disable_window_stats)
+	u64 runtime;
+
+	if (!rq->window_start || sched_disable_window_stats ||
+	    p->ravg.mark_start == wallclock)
 		return;
 
 	lockdep_assert_held(&rq->lock);
@@ -2668,13 +2819,15 @@ void update_task_ravg(struct task_struct *p, struct rq *rq, int event,
 	}
 
 	update_task_rq_cpu_cycles(p, rq, event, wallclock, irqtime);
-	update_task_demand(p, rq, event, wallclock);
+	runtime = update_task_demand(p, rq, event, wallclock);
+	if (runtime)
+		update_task_burst(p, rq, event, runtime);
 	update_cpu_busy_time(p, rq, event, wallclock, irqtime);
 	update_task_pred_demand(rq, p, event);
 done:
 	trace_sched_update_task_ravg(p, rq, event, wallclock, irqtime,
 				     rq->cc.cycles, rq->cc.time,
-				     _group_cpu_time(p->grp, cpu_of(rq)));
+				     p->grp ? &rq->grp_time : NULL);
 
 	p->ravg.mark_start = wallclock;
 }
@@ -2737,11 +2890,25 @@ void sched_account_irqstart(int cpu, struct task_struct *curr, u64 wallclock)
 void reset_task_stats(struct task_struct *p)
 {
 	u32 sum = 0;
+	u32 *curr_window_ptr = NULL;
+	u32 *prev_window_ptr = NULL;
 
-	if (exiting_task(p))
+	if (exiting_task(p)) {
 		sum = EXITING_TASK_MARKER;
+	} else {
+		curr_window_ptr =  p->ravg.curr_window_cpu;
+		prev_window_ptr = p->ravg.prev_window_cpu;
+		memset(curr_window_ptr, 0, sizeof(u32) * nr_cpu_ids);
+		memset(prev_window_ptr, 0, sizeof(u32) * nr_cpu_ids);
+	}
 
 	memset(&p->ravg, 0, sizeof(struct ravg));
+
+	p->ravg.curr_window_cpu = curr_window_ptr;
+	p->ravg.prev_window_cpu = prev_window_ptr;
+
+	p->ravg.avg_burst = 2 * (u64)sysctl_sched_short_burst;
+
 	/* Retain EXITING_TASK marker */
 	p->ravg.sum_history[0] = sum;
 }
@@ -2765,18 +2932,20 @@ void mark_task_starting(struct task_struct *p)
 
 void set_window_start(struct rq *rq)
 {
-	int cpu = cpu_of(rq);
-	struct rq *sync_rq = cpu_rq(sync_cpu);
+	static int sync_cpu_available;
 
 	if (rq->window_start)
 		return;
 
-	if (cpu == sync_cpu) {
+	if (!sync_cpu_available) {
 		rq->window_start = sched_ktime_clock();
+		sync_cpu_available = 1;
 	} else {
+		struct rq *sync_rq = cpu_rq(cpumask_any(cpu_online_mask));
+
 		raw_spin_unlock(&rq->lock);
 		double_rq_lock(rq, sync_rq);
-		rq->window_start = cpu_rq(sync_cpu)->window_start;
+		rq->window_start = sync_rq->window_start;
 		rq->curr_runnable_sum = rq->prev_runnable_sum = 0;
 		rq->nt_curr_runnable_sum = rq->nt_prev_runnable_sum = 0;
 		raw_spin_unlock(&sync_rq->lock);
@@ -2785,45 +2954,13 @@ void set_window_start(struct rq *rq)
 	rq->curr->ravg.mark_start = rq->window_start;
 }
 
-void migrate_sync_cpu(int cpu)
-{
-	if (cpu == sync_cpu)
-		sync_cpu = smp_processor_id();
-}
-
 static void reset_all_task_stats(void)
 {
 	struct task_struct *g, *p;
 
-	read_lock(&tasklist_lock);
 	do_each_thread(g, p) {
 		reset_task_stats(p);
 	}  while_each_thread(g, p);
-	read_unlock(&tasklist_lock);
-}
-
-static void disable_window_stats(void)
-{
-	unsigned long flags;
-	int i;
-
-	local_irq_save(flags);
-	for_each_possible_cpu(i)
-		raw_spin_lock(&cpu_rq(i)->lock);
-
-	sched_disable_window_stats = 1;
-
-	for_each_possible_cpu(i)
-		raw_spin_unlock(&cpu_rq(i)->lock);
-
-	local_irq_restore(flags);
-}
-
-/* Called with all cpu's rq->lock held */
-static void enable_window_stats(void)
-{
-	sched_disable_window_stats = 0;
-
 }
 
 enum reset_reason_code {
@@ -2842,43 +2979,35 @@ const char *sched_window_reset_reasons[] = {
 /* Called with IRQs enabled */
 void reset_all_window_stats(u64 window_start, unsigned int window_size)
 {
-	int cpu;
+	int cpu, i;
 	unsigned long flags;
 	u64 start_ts = sched_ktime_clock();
 	int reason = WINDOW_CHANGE;
 	unsigned int old = 0, new = 0;
-	struct related_thread_group *grp;
-
-	disable_window_stats();
-
-	reset_all_task_stats();
 
 	local_irq_save(flags);
 
+	read_lock(&tasklist_lock);
+
 	read_lock(&related_thread_group_lock);
 
+	/* Taking all runqueue locks prevents race with sched_exit(). */
 	for_each_possible_cpu(cpu)
 		raw_spin_lock(&cpu_rq(cpu)->lock);
 
-	list_for_each_entry(grp, &related_thread_groups, list) {
-		int j;
+	sched_disable_window_stats = 1;
 
-		for_each_possible_cpu(j) {
-			struct group_cpu_time *cpu_time;
-			/* Protected by rq lock */
-			cpu_time = _group_cpu_time(grp, j);
-			memset(cpu_time, 0, sizeof(struct group_cpu_time));
-			if (window_start)
-				cpu_time->window_start = window_start;
-		}
-	}
+	reset_all_task_stats();
+
+	read_unlock(&tasklist_lock);
 
 	if (window_size) {
 		sched_ravg_window = window_size * TICK_NSEC;
 		set_hmp_defaults();
+		sched_load_granule = sched_ravg_window / NUM_LOAD_INDICES;
 	}
 
-	enable_window_stats();
+	sched_disable_window_stats = 0;
 
 	for_each_possible_cpu(cpu) {
 		struct rq *rq = cpu_rq(cpu);
@@ -2887,6 +3016,17 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size)
 			rq->window_start = window_start;
 		rq->curr_runnable_sum = rq->prev_runnable_sum = 0;
 		rq->nt_curr_runnable_sum = rq->nt_prev_runnable_sum = 0;
+		memset(&rq->grp_time, 0, sizeof(struct group_cpu_time));
+		for (i = 0; i < NUM_TRACKED_WINDOWS; i++) {
+			memset(&rq->load_subs[i], 0,
+					sizeof(struct load_subtractions));
+			clear_top_tasks_table(rq->top_tasks[i]);
+			clear_top_tasks_bitmap(rq->top_tasks_bitmap[i]);
+		}
+
+		rq->curr_table = 0;
+		rq->curr_top = 0;
+		rq->prev_top = 0;
 		reset_cpu_hmp_stats(cpu, 1);
 	}
 
@@ -2919,8 +3059,58 @@ void reset_all_window_stats(u64 window_start, unsigned int window_size)
 		sched_ktime_clock() - start_ts, reason, old, new);
 }
 
-static inline void
-sync_window_start(struct rq *rq, struct group_cpu_time *cpu_time);
+/*
+ * In this function we match the accumulated subtractions with the current
+ * and previous windows we are operating with. Ignore any entries where
+ * the window start in the load_subtraction struct does not match either
+ * the curent or the previous window. This could happen whenever CPUs
+ * become idle or busy with interrupts disabled for an extended period.
+ */
+static inline void account_load_subtractions(struct rq *rq)
+{
+	u64 ws = rq->window_start;
+	u64 prev_ws = ws - sched_ravg_window;
+	struct load_subtractions *ls = rq->load_subs;
+	int i;
+
+	for (i = 0; i < NUM_TRACKED_WINDOWS; i++) {
+		if (ls[i].window_start == ws) {
+			rq->curr_runnable_sum -= ls[i].subs;
+			rq->nt_curr_runnable_sum -= ls[i].new_subs;
+		} else if (ls[i].window_start == prev_ws) {
+			rq->prev_runnable_sum -= ls[i].subs;
+			rq->nt_prev_runnable_sum -= ls[i].new_subs;
+		}
+
+		ls[i].subs = 0;
+		ls[i].new_subs = 0;
+	}
+
+	BUG_ON((s64)rq->prev_runnable_sum < 0);
+	BUG_ON((s64)rq->curr_runnable_sum < 0);
+	BUG_ON((s64)rq->nt_prev_runnable_sum < 0);
+	BUG_ON((s64)rq->nt_curr_runnable_sum < 0);
+}
+
+static inline u64 freq_policy_load(struct rq *rq, u64 load)
+{
+	unsigned int reporting_policy = sysctl_sched_freq_reporting_policy;
+
+	switch (reporting_policy) {
+	case FREQ_REPORT_MAX_CPU_LOAD_TOP_TASK:
+		load = max_t(u64, load, top_task_load(rq));
+		break;
+	case FREQ_REPORT_TOP_TASK:
+		load = top_task_load(rq);
+		break;
+	case FREQ_REPORT_CPU_LOAD:
+		break;
+	default:
+		break;
+	}
+
+	return load;
+}
 
 void sched_get_cpus_busy(struct sched_load *busy,
 			 const struct cpumask *query_cpus)
@@ -2931,41 +3121,48 @@ void sched_get_cpus_busy(struct sched_load *busy,
 	u64 load[cpus], group_load[cpus];
 	u64 nload[cpus], ngload[cpus];
 	u64 pload[cpus];
-	unsigned int cur_freq[cpus], max_freq[cpus];
+	unsigned int max_freq[cpus];
 	int notifier_sent = 0;
 	int early_detection[cpus];
 	int cpu, i = 0;
 	unsigned int window_size;
 	u64 max_prev_sum = 0;
 	int max_busy_cpu = cpumask_first(query_cpus);
-	struct related_thread_group *grp;
 	u64 total_group_load = 0, total_ngload = 0;
 	bool aggregate_load = false;
+	struct sched_cluster *cluster = cpu_cluster(cpumask_first(query_cpus));
 
 	if (unlikely(cpus == 0))
 		return;
 
+	local_irq_save(flags);
+
 	/*
 	 * This function could be called in timer context, and the
 	 * current task may have been executing for a long time. Ensure
 	 * that the window stats are current by doing an update.
 	 */
-	read_lock(&related_thread_group_lock);
 
-	local_irq_save(flags);
 	for_each_cpu(cpu, query_cpus)
 		raw_spin_lock(&cpu_rq(cpu)->lock);
 
 	window_size = sched_ravg_window;
 
+	/*
+	 * We don't really need the cluster lock for this entire for loop
+	 * block. However, there is no advantage in optimizing this as rq
+	 * locks are held regardless and would prevent migration anyways
+	 */
+	raw_spin_lock(&cluster->load_lock);
+
 	for_each_cpu(cpu, query_cpus) {
 		rq = cpu_rq(cpu);
 
 		update_task_ravg(rq->curr, rq, TASK_UPDATE, sched_ktime_clock(),
 				 0);
-		cur_freq[i] = cpu_cycles_to_freq(rq->cc.cycles, rq->cc.time);
 
-		load[i] = rq->old_busy_time = rq->prev_runnable_sum;
+		account_load_subtractions(rq);
+		load[i] = rq->prev_runnable_sum;
 		nload[i] = rq->nt_prev_runnable_sum;
 		pload[i] = rq->hmp_stats.pred_demands_sum;
 		rq->old_estimated_time = pload[i];
@@ -2986,19 +3183,11 @@ void sched_get_cpus_busy(struct sched_load *busy,
 			rq->cluster->notifier_sent = 0;
 		}
 		early_detection[i] = (rq->ed_task != NULL);
-		cur_freq[i] = cpu_cur_freq(cpu);
 		max_freq[i] = cpu_max_freq(cpu);
 		i++;
 	}
 
-	for_each_related_thread_group(grp) {
-		for_each_cpu(cpu, query_cpus) {
-			/* Protected by rq_lock */
-			struct group_cpu_time *cpu_time =
-						_group_cpu_time(grp, cpu);
-			sync_window_start(cpu_rq(cpu), cpu_time);
-		}
-	}
+	raw_spin_unlock(&cluster->load_lock);
 
 	group_load_in_freq_domain(
 			&cpu_rq(max_busy_cpu)->freq_domain_cpumask,
@@ -3020,11 +3209,16 @@ void sched_get_cpus_busy(struct sched_load *busy,
 				ngload[i] = total_ngload;
 			}
 		} else {
-			_group_load_in_cpu(cpu, &group_load[i], &ngload[i]);
+			group_load[i] = rq->grp_time.prev_runnable_sum;
+			ngload[i] = rq->grp_time.nt_prev_runnable_sum;
 		}
 
 		load[i] += group_load[i];
 		nload[i] += ngload[i];
+
+		load[i] = freq_policy_load(rq, load[i]);
+		rq->old_busy_time = load[i];
+
 		/*
 		 * Scale load in reference to cluster max_possible_freq.
 		 *
@@ -3040,9 +3234,8 @@ void sched_get_cpus_busy(struct sched_load *busy,
 
 	for_each_cpu(cpu, query_cpus)
 		raw_spin_unlock(&(cpu_rq(cpu))->lock);
-	local_irq_restore(flags);
 
-	read_unlock(&related_thread_group_lock);
+	local_irq_restore(flags);
 
 	i = 0;
 	for_each_cpu(cpu, query_cpus) {
@@ -3052,36 +3245,15 @@ void sched_get_cpus_busy(struct sched_load *busy,
 			busy[i].prev_load = div64_u64(sched_ravg_window,
 							NSEC_PER_USEC);
 			busy[i].new_task_load = 0;
+			busy[i].predicted_load = 0;
 			goto exit_early;
 		}
 
-		/*
-		 * When the load aggregation is controlled by
-		 * sched_freq_aggregate_threshold, allow reporting loads
-		 * greater than 100 @ Fcur to ramp up the frequency
-		 * faster.
-		 */
-		if (notifier_sent || (aggregate_load &&
-					sched_freq_aggregate_threshold)) {
-			load[i] = scale_load_to_freq(load[i], max_freq[i],
-						    cpu_max_possible_freq(cpu));
-			nload[i] = scale_load_to_freq(nload[i], max_freq[i],
-						    cpu_max_possible_freq(cpu));
-		} else {
-			load[i] = scale_load_to_freq(load[i], max_freq[i],
-						     cur_freq[i]);
-			nload[i] = scale_load_to_freq(nload[i], max_freq[i],
-						      cur_freq[i]);
-			if (load[i] > window_size)
-				load[i] = window_size;
-			if (nload[i] > window_size)
-				nload[i] = window_size;
+		load[i] = scale_load_to_freq(load[i], max_freq[i],
+				cpu_max_possible_freq(cpu));
+		nload[i] = scale_load_to_freq(nload[i], max_freq[i],
+				cpu_max_possible_freq(cpu));
 
-			load[i] = scale_load_to_freq(load[i], cur_freq[i],
-						    cpu_max_possible_freq(cpu));
-			nload[i] = scale_load_to_freq(nload[i], cur_freq[i],
-						    cpu_max_possible_freq(cpu));
-		}
 		pload[i] = scale_load_to_freq(pload[i], max_freq[i],
 					     rq->cluster->max_possible_freq);
 
@@ -3145,6 +3317,189 @@ int sched_set_window(u64 window_start, unsigned int window_size)
 	return 0;
 }
 
+static inline void create_subtraction_entry(struct rq *rq, u64 ws, int index)
+{
+	rq->load_subs[index].window_start = ws;
+	rq->load_subs[index].subs = 0;
+	rq->load_subs[index].new_subs = 0;
+}
+
+static bool get_subtraction_index(struct rq *rq, u64 ws)
+{
+	int i;
+	u64 oldest = ULLONG_MAX;
+	int oldest_index = 0;
+
+	for (i = 0; i < NUM_TRACKED_WINDOWS; i++) {
+		u64 entry_ws = rq->load_subs[i].window_start;
+
+		if (ws == entry_ws)
+			return i;
+
+		if (entry_ws < oldest) {
+			oldest = entry_ws;
+			oldest_index = i;
+		}
+	}
+
+	create_subtraction_entry(rq, ws, oldest_index);
+	return oldest_index;
+}
+
+static void update_rq_load_subtractions(int index, struct rq *rq,
+					u32 sub_load, bool new_task)
+{
+	rq->load_subs[index].subs +=  sub_load;
+	if (new_task)
+		rq->load_subs[index].new_subs += sub_load;
+}
+
+static void update_cluster_load_subtractions(struct task_struct *p,
+					int cpu, u64 ws, bool new_task)
+{
+	struct sched_cluster *cluster = cpu_cluster(cpu);
+	struct cpumask cluster_cpus = cluster->cpus;
+	u64 prev_ws = ws - sched_ravg_window;
+	int i;
+
+	cpumask_clear_cpu(cpu, &cluster_cpus);
+	raw_spin_lock(&cluster->load_lock);
+
+	for_each_cpu(i, &cluster_cpus) {
+		struct rq *rq = cpu_rq(i);
+		int index;
+
+		if (p->ravg.curr_window_cpu[i]) {
+			index = get_subtraction_index(rq, ws);
+			update_rq_load_subtractions(index, rq,
+				p->ravg.curr_window_cpu[i], new_task);
+			p->ravg.curr_window_cpu[i] = 0;
+		}
+
+		if (p->ravg.prev_window_cpu[i]) {
+			index = get_subtraction_index(rq, prev_ws);
+			update_rq_load_subtractions(index, rq,
+				p->ravg.prev_window_cpu[i], new_task);
+			p->ravg.prev_window_cpu[i] = 0;
+		}
+	}
+
+	raw_spin_unlock(&cluster->load_lock);
+}
+
+static inline void inter_cluster_migration_fixup
+	(struct task_struct *p, int new_cpu, int task_cpu, bool new_task)
+{
+	struct rq *dest_rq = cpu_rq(new_cpu);
+	struct rq *src_rq = cpu_rq(task_cpu);
+
+	if (same_freq_domain(new_cpu, task_cpu))
+		return;
+
+	p->ravg.curr_window_cpu[new_cpu] = p->ravg.curr_window;
+	p->ravg.prev_window_cpu[new_cpu] = p->ravg.prev_window;
+
+	dest_rq->curr_runnable_sum += p->ravg.curr_window;
+	dest_rq->prev_runnable_sum += p->ravg.prev_window;
+
+	src_rq->curr_runnable_sum -=  p->ravg.curr_window_cpu[task_cpu];
+	src_rq->prev_runnable_sum -=  p->ravg.prev_window_cpu[task_cpu];
+
+	if (new_task) {
+		dest_rq->nt_curr_runnable_sum += p->ravg.curr_window;
+		dest_rq->nt_prev_runnable_sum += p->ravg.prev_window;
+
+		src_rq->nt_curr_runnable_sum -=
+				p->ravg.curr_window_cpu[task_cpu];
+		src_rq->nt_prev_runnable_sum -=
+				p->ravg.prev_window_cpu[task_cpu];
+	}
+
+	p->ravg.curr_window_cpu[task_cpu] = 0;
+	p->ravg.prev_window_cpu[task_cpu] = 0;
+
+	update_cluster_load_subtractions(p, task_cpu,
+			src_rq->window_start, new_task);
+
+	BUG_ON((s64)src_rq->prev_runnable_sum < 0);
+	BUG_ON((s64)src_rq->curr_runnable_sum < 0);
+	BUG_ON((s64)src_rq->nt_prev_runnable_sum < 0);
+	BUG_ON((s64)src_rq->nt_curr_runnable_sum < 0);
+}
+
+static int get_top_index(unsigned long *bitmap, unsigned long old_top)
+{
+	int index = find_next_bit(bitmap, NUM_LOAD_INDICES, old_top);
+
+	if (index == NUM_LOAD_INDICES)
+		return 0;
+
+	return NUM_LOAD_INDICES - 1 - index;
+}
+
+static void
+migrate_top_tasks(struct task_struct *p, struct rq *src_rq, struct rq *dst_rq)
+{
+	int index;
+	int top_index;
+	u32 curr_window = p->ravg.curr_window;
+	u32 prev_window = p->ravg.prev_window;
+	u8 src = src_rq->curr_table;
+	u8 dst = dst_rq->curr_table;
+	u8 *src_table;
+	u8 *dst_table;
+
+	if (curr_window) {
+		src_table = src_rq->top_tasks[src];
+		dst_table = dst_rq->top_tasks[dst];
+		index = load_to_index(curr_window);
+		src_table[index] -= 1;
+		dst_table[index] += 1;
+
+		if (!src_table[index])
+			__clear_bit(NUM_LOAD_INDICES - index - 1,
+				src_rq->top_tasks_bitmap[src]);
+
+		if (dst_table[index] == 1)
+			__set_bit(NUM_LOAD_INDICES - index - 1,
+				dst_rq->top_tasks_bitmap[dst]);
+
+		if (index > dst_rq->curr_top)
+			dst_rq->curr_top = index;
+
+		top_index = src_rq->curr_top;
+		if (index == top_index && !src_table[index])
+			src_rq->curr_top = get_top_index(
+				src_rq->top_tasks_bitmap[src], top_index);
+	}
+
+	if (prev_window) {
+		src = 1 - src;
+		dst = 1 - dst;
+		src_table = src_rq->top_tasks[src];
+		dst_table = dst_rq->top_tasks[dst];
+		index = load_to_index(prev_window);
+		src_table[index] -= 1;
+		dst_table[index] += 1;
+
+		if (!src_table[index])
+			__clear_bit(NUM_LOAD_INDICES - index - 1,
+				src_rq->top_tasks_bitmap[src]);
+
+		if (dst_table[index] == 1)
+			__set_bit(NUM_LOAD_INDICES - index - 1,
+				dst_rq->top_tasks_bitmap[dst]);
+
+		if (index > dst_rq->prev_top)
+			dst_rq->prev_top = index;
+
+		top_index = src_rq->prev_top;
+		if (index == top_index && !src_table[index])
+			src_rq->prev_top = get_top_index(
+				src_rq->top_tasks_bitmap[src], top_index);
+	}
+}
+
 void fixup_busy_time(struct task_struct *p, int new_cpu)
 {
 	struct rq *src_rq = task_rq(p);
@@ -3154,8 +3509,6 @@ void fixup_busy_time(struct task_struct *p, int new_cpu)
 	u64 *src_prev_runnable_sum, *dst_prev_runnable_sum;
 	u64 *src_nt_curr_runnable_sum, *dst_nt_curr_runnable_sum;
 	u64 *src_nt_prev_runnable_sum, *dst_nt_prev_runnable_sum;
-	int migrate_type;
-	struct migration_sum_data d;
 	bool new_task;
 	struct related_thread_group *grp;
 
@@ -3189,62 +3542,54 @@ void fixup_busy_time(struct task_struct *p, int new_cpu)
 	new_task = is_new_task(p);
 	/* Protected by rq_lock */
 	grp = p->grp;
+
+	/*
+	 * For frequency aggregation, we continue to do migration fixups
+	 * even for intra cluster migrations. This is because, the aggregated
+	 * load has to reported on a single CPU regardless.
+	 */
 	if (grp && sched_freq_aggregate) {
 		struct group_cpu_time *cpu_time;
 
-		migrate_type = GROUP_TO_GROUP;
-		/* Protected by rq_lock */
-		cpu_time = _group_cpu_time(grp, cpu_of(src_rq));
-		d.src_rq = NULL;
-		d.src_cpu_time = cpu_time;
+		cpu_time = &src_rq->grp_time;
 		src_curr_runnable_sum = &cpu_time->curr_runnable_sum;
 		src_prev_runnable_sum = &cpu_time->prev_runnable_sum;
 		src_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
 		src_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
 
-		/* Protected by rq_lock */
-		cpu_time = _group_cpu_time(grp, cpu_of(dest_rq));
-		d.dst_rq = NULL;
-		d.dst_cpu_time = cpu_time;
+		cpu_time = &dest_rq->grp_time;
 		dst_curr_runnable_sum = &cpu_time->curr_runnable_sum;
 		dst_prev_runnable_sum = &cpu_time->prev_runnable_sum;
 		dst_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
 		dst_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
-		sync_window_start(dest_rq, cpu_time);
+
+		if (p->ravg.curr_window) {
+			*src_curr_runnable_sum -= p->ravg.curr_window;
+			*dst_curr_runnable_sum += p->ravg.curr_window;
+			if (new_task) {
+				*src_nt_curr_runnable_sum -=
+							p->ravg.curr_window;
+				*dst_nt_curr_runnable_sum +=
+							p->ravg.curr_window;
+			}
+		}
+
+		if (p->ravg.prev_window) {
+			*src_prev_runnable_sum -= p->ravg.prev_window;
+			*dst_prev_runnable_sum += p->ravg.prev_window;
+			if (new_task) {
+				*src_nt_prev_runnable_sum -=
+							p->ravg.prev_window;
+				*dst_nt_prev_runnable_sum +=
+							p->ravg.prev_window;
+			}
+		}
 	} else {
-		migrate_type = RQ_TO_RQ;
-		d.src_rq = src_rq;
-		d.src_cpu_time = NULL;
-		d.dst_rq = dest_rq;
-		d.dst_cpu_time = NULL;
-		src_curr_runnable_sum = &src_rq->curr_runnable_sum;
-		src_prev_runnable_sum = &src_rq->prev_runnable_sum;
-		src_nt_curr_runnable_sum = &src_rq->nt_curr_runnable_sum;
-		src_nt_prev_runnable_sum = &src_rq->nt_prev_runnable_sum;
-
-		dst_curr_runnable_sum = &dest_rq->curr_runnable_sum;
-		dst_prev_runnable_sum = &dest_rq->prev_runnable_sum;
-		dst_nt_curr_runnable_sum = &dest_rq->nt_curr_runnable_sum;
-		dst_nt_prev_runnable_sum = &dest_rq->nt_prev_runnable_sum;
+		inter_cluster_migration_fixup(p, new_cpu,
+						task_cpu(p), new_task);
 	}
 
-	if (p->ravg.curr_window) {
-		*src_curr_runnable_sum -= p->ravg.curr_window;
-		*dst_curr_runnable_sum += p->ravg.curr_window;
-		if (new_task) {
-			*src_nt_curr_runnable_sum -= p->ravg.curr_window;
-			*dst_nt_curr_runnable_sum += p->ravg.curr_window;
-		}
-	}
-
-	if (p->ravg.prev_window) {
-		*src_prev_runnable_sum -= p->ravg.prev_window;
-		*dst_prev_runnable_sum += p->ravg.prev_window;
-		if (new_task) {
-			*src_nt_prev_runnable_sum -= p->ravg.prev_window;
-			*dst_nt_prev_runnable_sum += p->ravg.prev_window;
-		}
-	}
+	migrate_top_tasks(p, src_rq, dest_rq);
 
 	if (p == src_rq->ed_task) {
 		src_rq->ed_task = NULL;
@@ -3252,12 +3597,6 @@ void fixup_busy_time(struct task_struct *p, int new_cpu)
 			dest_rq->ed_task = p;
 	}
 
-	trace_sched_migration_update_sum(p, migrate_type, &d);
-	BUG_ON((s64)*src_prev_runnable_sum < 0);
-	BUG_ON((s64)*src_curr_runnable_sum < 0);
-	BUG_ON((s64)*src_nt_prev_runnable_sum < 0);
-	BUG_ON((s64)*src_nt_curr_runnable_sum < 0);
-
 done:
 	if (p->state == TASK_WAKING)
 		double_rq_unlock(src_rq, dest_rq);
@@ -3284,29 +3623,31 @@ static void check_for_up_down_migrate_update(const struct cpumask *cpus)
 }
 
 /* Return cluster which can offer required capacity for group */
-static struct sched_cluster *
-best_cluster(struct related_thread_group *grp, u64 total_demand)
+static struct sched_cluster *best_cluster(struct related_thread_group *grp,
+					u64 total_demand, bool group_boost)
 {
 	struct sched_cluster *cluster = NULL;
 
 	for_each_sched_cluster(cluster) {
-		if (group_will_fit(cluster, grp, total_demand))
+		if (group_will_fit(cluster, grp, total_demand, group_boost))
 			return cluster;
 	}
 
-	return NULL;
+	return sched_cluster[0];
 }
 
 static void _set_preferred_cluster(struct related_thread_group *grp)
 {
 	struct task_struct *p;
 	u64 combined_demand = 0;
+	bool boost_on_big = sched_boost_policy() == SCHED_BOOST_ON_BIG;
+	bool group_boost = false;
+	u64 wallclock;
 
-	if (!sysctl_sched_enable_colocation) {
-		grp->last_update = sched_ktime_clock();
-		grp->preferred_cluster = NULL;
+	if (list_empty(&grp->tasks))
 		return;
-	}
+
+	wallclock = sched_ktime_clock();
 
 	/*
 	 * wakeup of two or more related tasks could race with each other and
@@ -3314,13 +3655,25 @@ static void _set_preferred_cluster(struct related_thread_group *grp)
 	 * at same time. Avoid overhead in such cases of rechecking preferred
 	 * cluster
 	 */
-	if (sched_ktime_clock() - grp->last_update < sched_ravg_window / 10)
+	if (wallclock - grp->last_update < sched_ravg_window / 10)
 		return;
 
-	list_for_each_entry(p, &grp->tasks, grp_list)
+	list_for_each_entry(p, &grp->tasks, grp_list) {
+		if (boost_on_big && task_sched_boost(p)) {
+			group_boost = true;
+			break;
+		}
+
+		if (p->ravg.mark_start < wallclock -
+		    (sched_ravg_window * sched_ravg_hist_size))
+			continue;
+
 		combined_demand += p->ravg.demand;
 
-	grp->preferred_cluster = best_cluster(grp, combined_demand);
+	}
+
+	grp->preferred_cluster = best_cluster(grp,
+			combined_demand, group_boost);
 	grp->last_update = sched_ktime_clock();
 	trace_sched_set_preferred_cluster(grp, combined_demand);
 }
@@ -3335,60 +3688,7 @@ void set_preferred_cluster(struct related_thread_group *grp)
 #define ADD_TASK	0
 #define REM_TASK	1
 
-static inline void free_group_cputime(struct related_thread_group *grp)
-{
-	free_percpu(grp->cpu_time);
-}
-
-static int alloc_group_cputime(struct related_thread_group *grp)
-{
-	int i;
-	struct group_cpu_time *cpu_time;
-	int cpu = raw_smp_processor_id();
-	struct rq *rq = cpu_rq(cpu);
-	u64 window_start = rq->window_start;
-
-	grp->cpu_time = alloc_percpu(struct group_cpu_time);
-	if (!grp->cpu_time)
-		return -ENOMEM;
-
-	for_each_possible_cpu(i) {
-		cpu_time = per_cpu_ptr(grp->cpu_time, i);
-		memset(cpu_time, 0, sizeof(struct group_cpu_time));
-		cpu_time->window_start = window_start;
-	}
-
-	return 0;
-}
-
-/*
- * A group's window_start may be behind. When moving it forward, flip prev/curr
- * counters. When moving forward > 1 window, prev counter is set to 0
- */
-static inline void
-sync_window_start(struct rq *rq, struct group_cpu_time *cpu_time)
-{
-	u64 delta;
-	int nr_windows;
-	u64 curr_sum = cpu_time->curr_runnable_sum;
-	u64 nt_curr_sum = cpu_time->nt_curr_runnable_sum;
-
-	delta = rq->window_start - cpu_time->window_start;
-	if (!delta)
-		return;
-
-	nr_windows = div64_u64(delta, sched_ravg_window);
-	if (nr_windows > 1)
-		curr_sum = nt_curr_sum = 0;
-
-	cpu_time->prev_runnable_sum  = curr_sum;
-	cpu_time->curr_runnable_sum  = 0;
-
-	cpu_time->nt_prev_runnable_sum = nt_curr_sum;
-	cpu_time->nt_curr_runnable_sum = 0;
-
-	cpu_time->window_start = rq->window_start;
-}
+#define DEFAULT_CGROUP_COLOC_ID 1
 
 /*
  * Task's cpu usage is accounted in:
@@ -3407,8 +3707,10 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
 	u64 *src_prev_runnable_sum, *dst_prev_runnable_sum;
 	u64 *src_nt_curr_runnable_sum, *dst_nt_curr_runnable_sum;
 	u64 *src_nt_prev_runnable_sum, *dst_nt_prev_runnable_sum;
-	struct migration_sum_data d;
 	int migrate_type;
+	int cpu = cpu_of(rq);
+	bool new_task;
+	int i;
 
 	if (!sched_freq_aggregate)
 		return;
@@ -3417,16 +3719,12 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
 
 	update_task_ravg(rq->curr, rq, TASK_UPDATE, wallclock, 0);
 	update_task_ravg(p, rq, TASK_UPDATE, wallclock, 0);
+	new_task = is_new_task(p);
 
-	/* cpu_time protected by related_thread_group_lock, grp->lock rq_lock */
-	cpu_time = _group_cpu_time(grp, cpu_of(rq));
+	cpu_time = &rq->grp_time;
 	if (event == ADD_TASK) {
-		sync_window_start(rq, cpu_time);
 		migrate_type = RQ_TO_GROUP;
-		d.src_rq = rq;
-		d.src_cpu_time = NULL;
-		d.dst_rq = NULL;
-		d.dst_cpu_time = cpu_time;
+
 		src_curr_runnable_sum = &rq->curr_runnable_sum;
 		dst_curr_runnable_sum = &cpu_time->curr_runnable_sum;
 		src_prev_runnable_sum = &rq->prev_runnable_sum;
@@ -3436,19 +3734,22 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
 		dst_nt_curr_runnable_sum = &cpu_time->nt_curr_runnable_sum;
 		src_nt_prev_runnable_sum = &rq->nt_prev_runnable_sum;
 		dst_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
+
+		*src_curr_runnable_sum -= p->ravg.curr_window_cpu[cpu];
+		*src_prev_runnable_sum -= p->ravg.prev_window_cpu[cpu];
+		if (new_task) {
+			*src_nt_curr_runnable_sum -=
+					p->ravg.curr_window_cpu[cpu];
+			*src_nt_prev_runnable_sum -=
+					p->ravg.prev_window_cpu[cpu];
+		}
+
+		update_cluster_load_subtractions(p, cpu,
+				rq->window_start, new_task);
+
 	} else {
 		migrate_type = GROUP_TO_RQ;
-		d.src_rq = NULL;
-		d.src_cpu_time = cpu_time;
-		d.dst_rq = rq;
-		d.dst_cpu_time = NULL;
 
-		/*
-		 * In case of REM_TASK, cpu_time->window_start would be
-		 * uptodate, because of the update_task_ravg() we called
-		 * above on the moving task. Hence no need for
-		 * sync_window_start()
-		 */
 		src_curr_runnable_sum = &cpu_time->curr_runnable_sum;
 		dst_curr_runnable_sum = &rq->curr_runnable_sum;
 		src_prev_runnable_sum = &cpu_time->prev_runnable_sum;
@@ -3458,80 +3759,91 @@ static void transfer_busy_time(struct rq *rq, struct related_thread_group *grp,
 		dst_nt_curr_runnable_sum = &rq->nt_curr_runnable_sum;
 		src_nt_prev_runnable_sum = &cpu_time->nt_prev_runnable_sum;
 		dst_nt_prev_runnable_sum = &rq->nt_prev_runnable_sum;
+
+		*src_curr_runnable_sum -= p->ravg.curr_window;
+		*src_prev_runnable_sum -= p->ravg.prev_window;
+		if (new_task) {
+			*src_nt_curr_runnable_sum -= p->ravg.curr_window;
+			*src_nt_prev_runnable_sum -= p->ravg.prev_window;
+		}
+
+		/*
+		 * Need to reset curr/prev windows for all CPUs, not just the
+		 * ones in the same cluster. Since inter cluster migrations
+		 * did not result in the appropriate book keeping, the values
+		 * per CPU would be inaccurate.
+		 */
+		for_each_possible_cpu(i) {
+			p->ravg.curr_window_cpu[i] = 0;
+			p->ravg.prev_window_cpu[i] = 0;
+		}
 	}
 
-	*src_curr_runnable_sum -= p->ravg.curr_window;
 	*dst_curr_runnable_sum += p->ravg.curr_window;
-
-	*src_prev_runnable_sum -= p->ravg.prev_window;
 	*dst_prev_runnable_sum += p->ravg.prev_window;
-
-	if (is_new_task(p)) {
-		*src_nt_curr_runnable_sum -= p->ravg.curr_window;
+	if (new_task) {
 		*dst_nt_curr_runnable_sum += p->ravg.curr_window;
-		*src_nt_prev_runnable_sum -= p->ravg.prev_window;
 		*dst_nt_prev_runnable_sum += p->ravg.prev_window;
 	}
 
-	trace_sched_migration_update_sum(p, migrate_type, &d);
+	/*
+	 * When a task enter or exits a group, it's curr and prev windows are
+	 * moved to a single CPU. This behavior might be sub-optimal in the
+	 * exit case, however, it saves us the overhead of handling inter
+	 * cluster migration fixups while the task is part of a related group.
+	 */
+	p->ravg.curr_window_cpu[cpu] = p->ravg.curr_window;
+	p->ravg.prev_window_cpu[cpu] = p->ravg.prev_window;
+
+	trace_sched_migration_update_sum(p, migrate_type, rq);
 
 	BUG_ON((s64)*src_curr_runnable_sum < 0);
 	BUG_ON((s64)*src_prev_runnable_sum < 0);
+	BUG_ON((s64)*src_nt_curr_runnable_sum < 0);
+	BUG_ON((s64)*src_nt_prev_runnable_sum < 0);
 }
 
-static inline struct group_cpu_time *
-task_group_cpu_time(struct task_struct *p, int cpu)
+static inline struct related_thread_group*
+lookup_related_thread_group(unsigned int group_id)
 {
-	return _group_cpu_time(rcu_dereference(p->grp), cpu);
+	return related_thread_groups[group_id];
 }
 
-static inline struct group_cpu_time *
-_group_cpu_time(struct related_thread_group *grp, int cpu)
+int alloc_related_thread_groups(void)
 {
-	return grp ? per_cpu_ptr(grp->cpu_time, cpu) : NULL;
-}
-
-struct related_thread_group *alloc_related_thread_group(int group_id)
-{
+	int i, ret;
 	struct related_thread_group *grp;
 
-	grp = kzalloc(sizeof(*grp), GFP_KERNEL);
-	if (!grp)
-		return ERR_PTR(-ENOMEM);
+	/* groupd_id = 0 is invalid as it's special id to remove group. */
+	for (i = 1; i < MAX_NUM_CGROUP_COLOC_ID; i++) {
+		grp = kzalloc(sizeof(*grp), GFP_NOWAIT);
+		if (!grp) {
+			ret = -ENOMEM;
+			goto err;
+		}
 
-	if (alloc_group_cputime(grp)) {
-		kfree(grp);
-		return ERR_PTR(-ENOMEM);
+		grp->id = i;
+		INIT_LIST_HEAD(&grp->tasks);
+		INIT_LIST_HEAD(&grp->list);
+		raw_spin_lock_init(&grp->lock);
+
+		related_thread_groups[i] = grp;
 	}
 
-	grp->id = group_id;
-	INIT_LIST_HEAD(&grp->tasks);
-	INIT_LIST_HEAD(&grp->list);
-	raw_spin_lock_init(&grp->lock);
+	return 0;
 
-	return grp;
-}
-
-struct related_thread_group *lookup_related_thread_group(unsigned int group_id)
-{
-	struct related_thread_group *grp;
-
-	list_for_each_entry(grp, &related_thread_groups, list) {
-		if (grp->id == group_id)
-			return grp;
+err:
+	for (i = 1; i < MAX_NUM_CGROUP_COLOC_ID; i++) {
+		grp = lookup_related_thread_group(i);
+		if (grp) {
+			kfree(grp);
+			related_thread_groups[i] = NULL;
+		} else {
+			break;
+		}
 	}
 
-	return NULL;
-}
-
-/* See comments before preferred_cluster() */
-static void free_related_thread_group(struct rcu_head *rcu)
-{
-	struct related_thread_group *grp = container_of(rcu, struct
-			related_thread_group, rcu);
-
-	free_group_cputime(grp);
-	kfree(grp);
+	return ret;
 }
 
 static void remove_task_from_group(struct task_struct *p)
@@ -3549,6 +3861,7 @@ static void remove_task_from_group(struct task_struct *p)
 	rcu_assign_pointer(p->grp, NULL);
 	__task_rq_unlock(rq, &rf);
 
+
 	if (!list_empty(&grp->tasks)) {
 		empty_group = 0;
 		_set_preferred_cluster(grp);
@@ -3556,10 +3869,13 @@ static void remove_task_from_group(struct task_struct *p)
 
 	raw_spin_unlock(&grp->lock);
 
-	if (empty_group) {
-		list_del(&grp->list);
-		call_rcu(&grp->rcu, free_related_thread_group);
-	}
+	/* Reserved groups cannot be destroyed */
+	if (empty_group && grp->id != DEFAULT_CGROUP_COLOC_ID)
+		 /*
+		  * We test whether grp->list is attached with list_empty()
+		  * hence re-init the list after deletion.
+		  */
+		list_del_init(&grp->list);
 }
 
 static int
@@ -3591,93 +3907,89 @@ void add_new_task_to_grp(struct task_struct *new)
 {
 	unsigned long flags;
 	struct related_thread_group *grp;
-	struct task_struct *parent;
+	struct task_struct *leader = new->group_leader;
+	unsigned int leader_grp_id = sched_get_group_id(leader);
 
-	if (!sysctl_sched_enable_thread_grouping)
+	if (!sysctl_sched_enable_thread_grouping &&
+	    leader_grp_id != DEFAULT_CGROUP_COLOC_ID)
 		return;
 
 	if (thread_group_leader(new))
 		return;
 
-	parent = new->group_leader;
+	if (leader_grp_id == DEFAULT_CGROUP_COLOC_ID) {
+		if (!same_schedtune(new, leader))
+			return;
+	}
+
+	write_lock_irqsave(&related_thread_group_lock, flags);
+
+	rcu_read_lock();
+	grp = task_related_thread_group(leader);
+	rcu_read_unlock();
 
 	/*
-	 * The parent's pi_lock is required here to protect race
-	 * against the parent task being removed from the
-	 * group.
+	 * It's possible that someone already added the new task to the
+	 * group. A leader's thread group is updated prior to calling
+	 * this function. It's also possible that the leader has exited
+	 * the group. In either case, there is nothing else to do.
 	 */
-	raw_spin_lock_irqsave(&parent->pi_lock, flags);
-
-	/* protected by pi_lock. */
-	grp = task_related_thread_group(parent);
-	if (!grp) {
-		raw_spin_unlock_irqrestore(&parent->pi_lock, flags);
+	if (!grp || new->grp) {
+		write_unlock_irqrestore(&related_thread_group_lock, flags);
 		return;
 	}
+
 	raw_spin_lock(&grp->lock);
 
 	rcu_assign_pointer(new->grp, grp);
 	list_add(&new->grp_list, &grp->tasks);
 
 	raw_spin_unlock(&grp->lock);
-	raw_spin_unlock_irqrestore(&parent->pi_lock, flags);
+	write_unlock_irqrestore(&related_thread_group_lock, flags);
+}
+
+static int __sched_set_group_id(struct task_struct *p, unsigned int group_id)
+{
+	int rc = 0;
+	unsigned long flags;
+	struct related_thread_group *grp = NULL;
+
+	if (group_id >= MAX_NUM_CGROUP_COLOC_ID)
+		return -EINVAL;
+
+	raw_spin_lock_irqsave(&p->pi_lock, flags);
+	write_lock(&related_thread_group_lock);
+
+	/* Switching from one group to another directly is not permitted */
+	if ((current != p && p->flags & PF_EXITING) ||
+			(!p->grp && !group_id) ||
+			(p->grp && group_id))
+		goto done;
+
+	if (!group_id) {
+		remove_task_from_group(p);
+		goto done;
+	}
+
+	grp = lookup_related_thread_group(group_id);
+	if (list_empty(&grp->list))
+		list_add(&grp->list, &active_related_thread_groups);
+
+	rc = add_task_to_group(p, grp);
+done:
+	write_unlock(&related_thread_group_lock);
+	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
+
+	return rc;
 }
 
 int sched_set_group_id(struct task_struct *p, unsigned int group_id)
 {
-	int rc = 0, destroy = 0;
-	unsigned long flags;
-	struct related_thread_group *grp = NULL, *new = NULL;
+	/* DEFAULT_CGROUP_COLOC_ID is a reserved id */
+	if (group_id == DEFAULT_CGROUP_COLOC_ID)
+		return -EINVAL;
 
-redo:
-	raw_spin_lock_irqsave(&p->pi_lock, flags);
-
-	if ((current != p && p->flags & PF_EXITING) ||
-			(!p->grp && !group_id) ||
-			(p->grp && p->grp->id == group_id))
-		goto done;
-
-	write_lock(&related_thread_group_lock);
-
-	if (!group_id) {
-		remove_task_from_group(p);
-		write_unlock(&related_thread_group_lock);
-		goto done;
-	}
-
-	if (p->grp && p->grp->id != group_id)
-		remove_task_from_group(p);
-
-	grp = lookup_related_thread_group(group_id);
-	if (!grp && !new) {
-		/* New group */
-		write_unlock(&related_thread_group_lock);
-		raw_spin_unlock_irqrestore(&p->pi_lock, flags);
-		new = alloc_related_thread_group(group_id);
-		if (IS_ERR(new))
-			return -ENOMEM;
-		destroy = 1;
-		/* Rerun checks (like task exiting), since we dropped pi_lock */
-		goto redo;
-	} else if (!grp && new) {
-		/* New group - use object allocated before */
-		destroy = 0;
-		list_add(&new->list, &related_thread_groups);
-		grp = new;
-	}
-
-	BUG_ON(!grp);
-	rc = add_task_to_group(p, grp);
-	write_unlock(&related_thread_group_lock);
-done:
-	raw_spin_unlock_irqrestore(&p->pi_lock, flags);
-
-	if (new && destroy) {
-		free_group_cputime(new);
-		kfree(new);
-	}
-
-	return rc;
+	return __sched_set_group_id(p, group_id);
 }
 
 unsigned int sched_get_group_id(struct task_struct *p)
@@ -3693,6 +4005,42 @@ unsigned int sched_get_group_id(struct task_struct *p)
 	return group_id;
 }
 
+#if defined(CONFIG_SCHED_TUNE) && defined(CONFIG_CGROUP_SCHEDTUNE)
+/*
+ * We create a default colocation group at boot. There is no need to
+ * synchronize tasks between cgroups at creation time because the
+ * correct cgroup hierarchy is not available at boot. Therefore cgroup
+ * colocation is turned off by default even though the colocation group
+ * itself has been allocated. Furthermore this colocation group cannot
+ * be destroyted once it has been created. All of this has been as part
+ * of runtime optimizations.
+ *
+ * The job of synchronizing tasks to the colocation group is done when
+ * the colocation flag in the cgroup is turned on.
+ */
+static int __init create_default_coloc_group(void)
+{
+	struct related_thread_group *grp = NULL;
+	unsigned long flags;
+
+	grp = lookup_related_thread_group(DEFAULT_CGROUP_COLOC_ID);
+	write_lock_irqsave(&related_thread_group_lock, flags);
+	list_add(&grp->list, &active_related_thread_groups);
+	write_unlock_irqrestore(&related_thread_group_lock, flags);
+
+	update_freq_aggregate_threshold(MAX_FREQ_AGGR_THRESH);
+	return 0;
+}
+late_initcall(create_default_coloc_group);
+
+int sync_cgroup_colocation(struct task_struct *p, bool insert)
+{
+	unsigned int grp_id = insert ? DEFAULT_CGROUP_COLOC_ID : 0;
+
+	return __sched_set_group_id(p, grp_id);
+}
+#endif
+
 static void update_cpu_cluster_capacity(const cpumask_t *cpus)
 {
 	int i;
@@ -3918,7 +4266,7 @@ bool early_detection_notify(struct rq *rq, u64 wallclock)
 	struct task_struct *p;
 	int loop_max = 10;
 
-	if (!sched_boost() || !rq->cfs.h_nr_running)
+	if (sched_boost_policy() == SCHED_BOOST_NONE || !rq->cfs.h_nr_running)
 		return 0;
 
 	rq->ed_task = NULL;
@@ -3937,6 +4285,20 @@ bool early_detection_notify(struct rq *rq, u64 wallclock)
 	return 0;
 }
 
+void update_avg_burst(struct task_struct *p)
+{
+	update_avg(&p->ravg.avg_burst, p->ravg.curr_burst);
+	p->ravg.curr_burst = 0;
+}
+
+void note_task_waking(struct task_struct *p, u64 wallclock)
+{
+	u64 sleep_time = wallclock - p->last_switch_out_ts;
+
+	p->last_wake_ts = wallclock;
+	update_avg(&p->ravg.avg_sleep_time, sleep_time);
+}
+
 #ifdef CONFIG_CGROUP_SCHED
 u64 cpu_upmigrate_discourage_read_u64(struct cgroup_subsys_state *css,
 					  struct cftype *cft)
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index a791486..189fc63 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -273,8 +273,12 @@ static void pull_rt_task(struct rq *this_rq);
 
 static inline bool need_pull_rt_task(struct rq *rq, struct task_struct *prev)
 {
-	/* Try to pull RT tasks here if we lower this rq's prio */
-	return rq->rt.highest_prio.curr > prev->prio;
+	/*
+	 * Try to pull RT tasks here if we lower this rq's prio and cpu is not
+	 * isolated
+	 */
+	return rq->rt.highest_prio.curr > prev->prio &&
+	       !cpu_isolated(cpu_of(rq));
 }
 
 static inline int rt_overloaded(struct rq *rq)
@@ -1736,8 +1740,14 @@ static int find_lowest_rq_hmp(struct task_struct *task)
 	int prev_cpu = task_cpu(task);
 	u64 cpu_load, min_load = ULLONG_MAX;
 	int i;
-	int restrict_cluster = sched_boost() ? 0 :
-				sysctl_sched_restrict_cluster_spill;
+	int restrict_cluster;
+	int boost_on_big;
+	int pack_task, wakeup_latency, least_wakeup_latency = INT_MAX;
+
+	boost_on_big = sched_boost() == FULL_THROTTLE_BOOST &&
+			sched_boost_policy() == SCHED_BOOST_ON_BIG;
+
+	restrict_cluster = sysctl_sched_restrict_cluster_spill;
 
 	/* Make sure the mask is initialized first */
 	if (unlikely(!lowest_mask))
@@ -1749,6 +1759,8 @@ static int find_lowest_rq_hmp(struct task_struct *task)
 	if (!cpupri_find(&task_rq(task)->rd->cpupri, task, lowest_mask))
 		return best_cpu; /* No targets found */
 
+	pack_task = is_short_burst_task(task);
+
 	/*
 	 * At this point we have built a mask of cpus representing the
 	 * lowest priority tasks in the system.  Now we want to elect
@@ -1756,7 +1768,12 @@ static int find_lowest_rq_hmp(struct task_struct *task)
 	 */
 
 	for_each_sched_cluster(cluster) {
+		if (boost_on_big && cluster->capacity != max_possible_capacity)
+			continue;
+
 		cpumask_and(&candidate_mask, &cluster->cpus, lowest_mask);
+		cpumask_andnot(&candidate_mask, &candidate_mask,
+			       cpu_isolated_mask);
 
 		if (cpumask_empty(&candidate_mask))
 			continue;
@@ -1769,6 +1786,20 @@ static int find_lowest_rq_hmp(struct task_struct *task)
 			if (!restrict_cluster)
 				cpu_load = scale_load_to_cpu(cpu_load, i);
 
+			if (pack_task) {
+				wakeup_latency = cpu_rq(i)->wakeup_latency;
+
+				if (wakeup_latency > least_wakeup_latency)
+					continue;
+
+				if (wakeup_latency < least_wakeup_latency) {
+					least_wakeup_latency = wakeup_latency;
+					min_load = cpu_load;
+					best_cpu = i;
+					continue;
+				}
+			}
+
 			if (cpu_load < min_load ||
 				(cpu_load == min_load &&
 				(i == prev_cpu || (best_cpu != prev_cpu &&
@@ -1777,6 +1808,7 @@ static int find_lowest_rq_hmp(struct task_struct *task)
 				best_cpu = i;
 			}
 		}
+
 		if (restrict_cluster && best_cpu != -1)
 			break;
 	}
@@ -2339,7 +2371,8 @@ static void switched_from_rt(struct rq *rq, struct task_struct *p)
 	 * we may need to handle the pulling of RT tasks
 	 * now.
 	 */
-	if (!task_on_rq_queued(p) || rq->rt.rt_nr_running)
+	if (!task_on_rq_queued(p) || rq->rt.rt_nr_running ||
+		cpu_isolated(cpu_of(rq)))
 		return;
 
 	queue_pull_task(rq);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index f6e2bf1..41a7039 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -379,13 +379,30 @@ struct cfs_bandwidth { };
 
 #ifdef CONFIG_SCHED_HMP
 
+#define NUM_TRACKED_WINDOWS 2
+#define NUM_LOAD_INDICES 1000
+
 struct hmp_sched_stats {
 	int nr_big_tasks;
 	u64 cumulative_runnable_avg;
 	u64 pred_demands_sum;
 };
 
+struct load_subtractions {
+	u64 window_start;
+	u64 subs;
+	u64 new_subs;
+};
+
+struct group_cpu_time {
+	u64 curr_runnable_sum;
+	u64 prev_runnable_sum;
+	u64 nt_curr_runnable_sum;
+	u64 nt_prev_runnable_sum;
+};
+
 struct sched_cluster {
+	raw_spinlock_t load_lock;
 	struct list_head list;
 	struct cpumask cpus;
 	int id;
@@ -407,6 +424,7 @@ struct sched_cluster {
 	int dstate, dstate_wakeup_latency, dstate_wakeup_energy;
 	unsigned int static_cluster_pwr_cost;
 	int notifier_sent;
+	bool wake_up_idle;
 };
 
 extern unsigned long all_cluster_ids[];
@@ -424,12 +442,6 @@ struct related_thread_group {
 	struct sched_cluster *preferred_cluster;
 	struct rcu_head rcu;
 	u64 last_update;
-	struct group_cpu_time __percpu *cpu_time;	/* one per cluster */
-};
-
-struct migration_sum_data {
-	struct rq *src_rq, *dst_rq;
-	struct group_cpu_time *src_cpu_time, *dst_cpu_time;
 };
 
 extern struct list_head cluster_head;
@@ -773,6 +785,14 @@ struct rq {
 	u64 prev_runnable_sum;
 	u64 nt_curr_runnable_sum;
 	u64 nt_prev_runnable_sum;
+	struct group_cpu_time grp_time;
+	struct load_subtractions load_subs[NUM_TRACKED_WINDOWS];
+	DECLARE_BITMAP_ARRAY(top_tasks_bitmap,
+			NUM_TRACKED_WINDOWS, NUM_LOAD_INDICES);
+	u8 *top_tasks[NUM_TRACKED_WINDOWS];
+	u8 curr_table;
+	int prev_top;
+	int curr_top;
 #endif
 
 #ifdef CONFIG_IRQ_TIME_ACCOUNTING
@@ -1065,6 +1085,12 @@ static inline void sched_ttwu_pending(void) { }
 #include "stats.h"
 #include "auto_group.h"
 
+enum sched_boost_policy {
+	SCHED_BOOST_NONE,
+	SCHED_BOOST_ON_BIG,
+	SCHED_BOOST_ON_ALL,
+};
+
 #ifdef CONFIG_SCHED_HMP
 
 #define WINDOW_STATS_RECENT		0
@@ -1073,7 +1099,6 @@ static inline void sched_ttwu_pending(void) { }
 #define WINDOW_STATS_AVG		3
 #define WINDOW_STATS_INVALID_POLICY	4
 
-#define MAJOR_TASK_PCT 85
 #define SCHED_UPMIGRATE_MIN_NICE 15
 #define EXITING_TASK_MARKER	0xdeaddead
 
@@ -1094,13 +1119,11 @@ extern unsigned int min_capacity;
 extern unsigned int max_load_scale_factor;
 extern unsigned int max_possible_capacity;
 extern unsigned int min_max_possible_capacity;
-extern unsigned int sched_upmigrate;
-extern unsigned int sched_downmigrate;
+extern unsigned int max_power_cost;
 extern unsigned int sched_init_task_load_windows;
 extern unsigned int up_down_migrate_scale_factor;
 extern unsigned int sysctl_sched_restrict_cluster_spill;
 extern unsigned int sched_pred_alert_load;
-extern unsigned int sched_major_task_runtime;
 extern struct sched_cluster init_cluster;
 extern unsigned int  __read_mostly sched_short_sleep_task_threshold;
 extern unsigned int  __read_mostly sched_long_cpu_selection_threshold;
@@ -1110,8 +1133,9 @@ extern unsigned int  __read_mostly sched_spill_load;
 extern unsigned int  __read_mostly sched_upmigrate;
 extern unsigned int  __read_mostly sched_downmigrate;
 extern unsigned int  __read_mostly sysctl_sched_spill_nr_run;
+extern unsigned int  __read_mostly sched_load_granule;
 
-extern void init_new_task_load(struct task_struct *p);
+extern void init_new_task_load(struct task_struct *p, bool idle_task);
 extern u64 sched_ktime_clock(void);
 extern int got_boost_kick(void);
 extern int register_cpu_cycle_counter_cb(struct cpu_cycle_counter_cb *cb);
@@ -1124,9 +1148,8 @@ extern void clear_boost_kick(int cpu);
 extern void clear_hmp_request(int cpu);
 extern void mark_task_starting(struct task_struct *p);
 extern void set_window_start(struct rq *rq);
-extern void migrate_sync_cpu(int cpu);
 extern void update_cluster_topology(void);
-extern void set_task_last_wake(struct task_struct *p, u64 wallclock);
+extern void note_task_waking(struct task_struct *p, u64 wallclock);
 extern void set_task_last_switch_out(struct task_struct *p, u64 wallclock);
 extern void init_clusters(void);
 extern void reset_cpu_hmp_stats(int cpu, int reset_cra);
@@ -1137,17 +1160,18 @@ extern void sched_account_irqstart(int cpu, struct task_struct *curr,
 				   u64 wallclock);
 extern unsigned int cpu_temp(int cpu);
 extern unsigned int nr_eligible_big_tasks(int cpu);
-extern void update_up_down_migrate(void);
 extern int update_preferred_cluster(struct related_thread_group *grp,
 			struct task_struct *p, u32 old_load);
 extern void set_preferred_cluster(struct related_thread_group *grp);
 extern void add_new_task_to_grp(struct task_struct *new);
+extern unsigned int update_freq_aggregate_threshold(unsigned int threshold);
+extern void update_avg_burst(struct task_struct *p);
+extern void update_avg(u64 *avg, u64 sample);
 
-enum sched_boost_type {
-	SCHED_BOOST_NONE,
-	SCHED_BOOST_ON_BIG,
-	SCHED_BOOST_ON_ALL,
-};
+#define NO_BOOST 0
+#define FULL_THROTTLE_BOOST 1
+#define CONSERVATIVE_BOOST 2
+#define RESTRAINED_BOOST 3
 
 static inline struct sched_cluster *cpu_cluster(int cpu)
 {
@@ -1214,6 +1238,11 @@ static inline int cpu_max_power_cost(int cpu)
 	return cpu_rq(cpu)->cluster->max_power_cost;
 }
 
+static inline int cpu_min_power_cost(int cpu)
+{
+	return cpu_rq(cpu)->cluster->min_power_cost;
+}
+
 static inline u32 cpu_cycles_to_freq(u64 cycles, u32 period)
 {
 	return div64_u64(cycles, period);
@@ -1345,14 +1374,6 @@ check_for_freq_change(struct rq *rq, bool check_pred, bool check_groups);
 extern void notify_migration(int src_cpu, int dest_cpu,
 			bool src_cpu_dead, struct task_struct *p);
 
-struct group_cpu_time {
-	u64 curr_runnable_sum;
-	u64 prev_runnable_sum;
-	u64 nt_curr_runnable_sum;
-	u64 nt_prev_runnable_sum;
-	u64 window_start;
-};
-
 /* Is frequency of two cpus synchronized with each other? */
 static inline int same_freq_domain(int src_cpu, int dst_cpu)
 {
@@ -1411,6 +1432,12 @@ static inline u64 cpu_cravg_sync(int cpu, int sync)
 	return load;
 }
 
+static inline bool is_short_burst_task(struct task_struct *p)
+{
+	return p->ravg.avg_burst < sysctl_sched_short_burst &&
+	       p->ravg.avg_sleep_time > sysctl_sched_short_sleep;
+}
+
 extern void check_for_migration(struct rq *rq, struct task_struct *p);
 extern void pre_big_task_count_change(const struct cpumask *cpus);
 extern void post_big_task_count_change(const struct cpumask *cpus);
@@ -1418,14 +1445,11 @@ extern void set_hmp_defaults(void);
 extern int power_delta_exceeded(unsigned int cpu_cost, unsigned int base_cost);
 extern unsigned int power_cost(int cpu, u64 demand);
 extern void reset_all_window_stats(u64 window_start, unsigned int window_size);
-extern void boost_kick(int cpu);
 extern int sched_boost(void);
 extern int task_load_will_fit(struct task_struct *p, u64 task_load, int cpu,
-					enum sched_boost_type boost_type);
-extern enum sched_boost_type sched_boost_type(void);
+					enum sched_boost_policy boost_policy);
+extern enum sched_boost_policy sched_boost_policy(void);
 extern int task_will_fit(struct task_struct *p, int cpu);
-extern int group_will_fit(struct sched_cluster *cluster,
-		 struct related_thread_group *grp, u64 demand);
 extern u64 cpu_load(int cpu);
 extern u64 cpu_load_sync(int cpu, int sync);
 extern int preferred_cluster(struct sched_cluster *cluster,
@@ -1438,6 +1462,7 @@ extern void inc_rq_hmp_stats(struct rq *rq,
 				struct task_struct *p, int change_cra);
 extern void dec_rq_hmp_stats(struct rq *rq,
 				struct task_struct *p, int change_cra);
+extern void reset_hmp_stats(struct hmp_sched_stats *stats, int reset_cra);
 extern int is_big_task(struct task_struct *p);
 extern int upmigrate_discouraged(struct task_struct *p);
 extern struct sched_cluster *rq_cluster(struct rq *rq);
@@ -1452,6 +1477,33 @@ extern u64 cpu_upmigrate_discourage_read_u64(struct cgroup_subsys_state *css,
 					struct cftype *cft);
 extern int cpu_upmigrate_discourage_write_u64(struct cgroup_subsys_state *css,
 				struct cftype *cft, u64 upmigrate_discourage);
+extern void sched_boost_parse_dt(void);
+extern void clear_top_tasks_bitmap(unsigned long *bitmap);
+
+#if defined(CONFIG_SCHED_TUNE) && defined(CONFIG_CGROUP_SCHEDTUNE)
+extern bool task_sched_boost(struct task_struct *p);
+extern int sync_cgroup_colocation(struct task_struct *p, bool insert);
+extern bool same_schedtune(struct task_struct *tsk1, struct task_struct *tsk2);
+extern void update_cgroup_boost_settings(void);
+extern void restore_cgroup_boost_settings(void);
+
+#else
+static inline bool
+same_schedtune(struct task_struct *tsk1, struct task_struct *tsk2)
+{
+	return true;
+}
+
+static inline bool task_sched_boost(struct task_struct *p)
+{
+	return true;
+}
+
+static inline void update_cgroup_boost_settings(void) { }
+static inline void restore_cgroup_boost_settings(void) { }
+#endif
+
+extern int alloc_related_thread_groups(void);
 
 #else	/* CONFIG_SCHED_HMP */
 
@@ -1459,6 +1511,16 @@ struct hmp_sched_stats;
 struct related_thread_group;
 struct sched_cluster;
 
+static inline enum sched_boost_policy sched_boost_policy(void)
+{
+	return SCHED_BOOST_NONE;
+}
+
+static inline bool task_sched_boost(struct task_struct *p)
+{
+	return true;
+}
+
 static inline int got_boost_kick(void)
 {
 	return 0;
@@ -1478,9 +1540,9 @@ static inline void clear_boost_kick(int cpu) { }
 static inline void clear_hmp_request(int cpu) { }
 static inline void mark_task_starting(struct task_struct *p) { }
 static inline void set_window_start(struct rq *rq) { }
-static inline void migrate_sync_cpu(int cpu) { }
+static inline void init_clusters(void) {}
 static inline void update_cluster_topology(void) { }
-static inline void set_task_last_wake(struct task_struct *p, u64 wallclock) { }
+static inline void note_task_waking(struct task_struct *p, u64 wallclock) { }
 static inline void set_task_last_switch_out(struct task_struct *p,
 					    u64 wallclock) { }
 
@@ -1541,7 +1603,9 @@ static inline struct sched_cluster *rq_cluster(struct rq *rq)
 	return NULL;
 }
 
-static inline void init_new_task_load(struct task_struct *p) { }
+static inline void init_new_task_load(struct task_struct *p, bool idle_task)
+{
+}
 
 static inline u64 scale_load_to_cpu(u64 load, int cpu)
 {
@@ -1607,8 +1671,6 @@ static inline int update_preferred_cluster(struct related_thread_group *grp,
 
 static inline void add_new_task_to_grp(struct task_struct *new) {}
 
-#define sched_freq_legacy_mode 1
-#define sched_migration_fixup	0
 #define PRED_DEMAND_DELTA (0)
 
 static inline void
@@ -1628,12 +1690,16 @@ static inline void post_big_task_count_change(void) { }
 static inline void set_hmp_defaults(void) { }
 
 static inline void clear_reserved(int cpu) { }
+static inline void sched_boost_parse_dt(void) {}
+static inline int alloc_related_thread_groups(void) { return 0; }
 
 #define trace_sched_cpu_load(...)
 #define trace_sched_cpu_load_lb(...)
 #define trace_sched_cpu_load_cgroup(...)
 #define trace_sched_cpu_load_wakeup(...)
 
+static inline void update_avg_burst(struct task_struct *p) {}
+
 #endif	/* CONFIG_SCHED_HMP */
 
 /*
@@ -1991,6 +2057,7 @@ extern const struct sched_class idle_sched_class;
 extern void update_group_capacity(struct sched_domain *sd, int cpu);
 
 extern void trigger_load_balance(struct rq *rq);
+extern void nohz_balance_clear_nohz_mask(int cpu);
 
 extern void set_cpus_allowed_common(struct task_struct *p, const struct cpumask *new_mask);
 
diff --git a/kernel/sched/sched_avg.c b/kernel/sched/sched_avg.c
index c70e046..29d8a26 100644
--- a/kernel/sched/sched_avg.c
+++ b/kernel/sched/sched_avg.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012, 2015, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012, 2015-2016, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -60,17 +60,17 @@ void sched_get_nr_running_avg(int *avg, int *iowait_avg, int *big_avg)
 
 		spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags);
 		curr_time = sched_clock();
+		diff = curr_time - per_cpu(last_time, cpu);
+		BUG_ON((s64)diff < 0);
+
 		tmp_avg += per_cpu(nr_prod_sum, cpu);
-		tmp_avg += per_cpu(nr, cpu) *
-			(curr_time - per_cpu(last_time, cpu));
+		tmp_avg += per_cpu(nr, cpu) * diff;
 
 		tmp_big_avg += per_cpu(nr_big_prod_sum, cpu);
-		tmp_big_avg += nr_eligible_big_tasks(cpu) *
-			(curr_time - per_cpu(last_time, cpu));
+		tmp_big_avg += nr_eligible_big_tasks(cpu) * diff;
 
 		tmp_iowait += per_cpu(iowait_prod_sum, cpu);
-		tmp_iowait +=  nr_iowait_cpu(cpu) *
-			(curr_time - per_cpu(last_time, cpu));
+		tmp_iowait +=  nr_iowait_cpu(cpu) * diff;
 
 		per_cpu(last_time, cpu) = curr_time;
 
@@ -107,14 +107,15 @@ EXPORT_SYMBOL(sched_get_nr_running_avg);
  */
 void sched_update_nr_prod(int cpu, long delta, bool inc)
 {
-	int diff;
-	s64 curr_time;
+	u64 diff;
+	u64 curr_time;
 	unsigned long flags, nr_running;
 
 	spin_lock_irqsave(&per_cpu(nr_lock, cpu), flags);
 	nr_running = per_cpu(nr, cpu);
 	curr_time = sched_clock();
 	diff = curr_time - per_cpu(last_time, cpu);
+	BUG_ON((s64)diff < 0);
 	per_cpu(last_time, cpu) = curr_time;
 	per_cpu(nr, cpu) = nr_running + (inc ? delta : -delta);
 
diff --git a/kernel/sched/tune.c b/kernel/sched/tune.c
new file mode 100644
index 0000000..ee2af8e
--- /dev/null
+++ b/kernel/sched/tune.c
@@ -0,0 +1,425 @@
+#include <linux/cgroup.h>
+#include <linux/err.h>
+#include <linux/percpu.h>
+#include <linux/printk.h>
+#include <linux/slab.h>
+
+#include "sched.h"
+
+unsigned int sysctl_sched_cfs_boost __read_mostly;
+
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+
+/*
+ * EAS scheduler tunables for task groups.
+ */
+
+/* SchdTune tunables for a group of tasks */
+struct schedtune {
+	/* SchedTune CGroup subsystem */
+	struct cgroup_subsys_state css;
+
+	/* Boost group allocated ID */
+	int idx;
+
+	/* Boost value for tasks on that SchedTune CGroup */
+	int boost;
+
+#ifdef CONFIG_SCHED_HMP
+	/* Toggle ability to override sched boost enabled */
+	bool sched_boost_no_override;
+
+	/*
+	 * Controls whether a cgroup is eligible for sched boost or not. This
+	 * can temporariliy be disabled by the kernel based on the no_override
+	 * flag above.
+	 */
+	bool sched_boost_enabled;
+
+	/*
+	 * This tracks the default value of sched_boost_enabled and is used
+	 * restore the value following any temporary changes to that flag.
+	 */
+	bool sched_boost_enabled_backup;
+
+	/*
+	 * Controls whether tasks of this cgroup should be colocated with each
+	 * other and tasks of other cgroups that have the same flag turned on.
+	 */
+	bool colocate;
+
+	/* Controls whether further updates are allowed to the colocate flag */
+	bool colocate_update_disabled;
+#endif
+
+};
+
+static inline struct schedtune *css_st(struct cgroup_subsys_state *css)
+{
+	return container_of(css, struct schedtune, css);
+}
+
+static inline struct schedtune *task_schedtune(struct task_struct *tsk)
+{
+	return css_st(task_css(tsk, schedtune_cgrp_id));
+}
+
+static inline struct schedtune *parent_st(struct schedtune *st)
+{
+	return css_st(st->css.parent);
+}
+
+/*
+ * SchedTune root control group
+ * The root control group is used to defined a system-wide boosting tuning,
+ * which is applied to all tasks in the system.
+ * Task specific boost tuning could be specified by creating and
+ * configuring a child control group under the root one.
+ * By default, system-wide boosting is disabled, i.e. no boosting is applied
+ * to tasks which are not into a child control group.
+ */
+static struct schedtune
+root_schedtune = {
+	.boost	= 0,
+#ifdef CONFIG_SCHED_HMP
+	.sched_boost_no_override = false,
+	.sched_boost_enabled = true,
+	.sched_boost_enabled_backup = true,
+	.colocate = false,
+	.colocate_update_disabled = false,
+#endif
+};
+
+/*
+ * Maximum number of boost groups to support
+ * When per-task boosting is used we still allow only limited number of
+ * boost groups for two main reasons:
+ * 1. on a real system we usually have only few classes of workloads which
+ *    make sense to boost with different values (e.g. background vs foreground
+ *    tasks, interactive vs low-priority tasks)
+ * 2. a limited number allows for a simpler and more memory/time efficient
+ *    implementation especially for the computation of the per-CPU boost
+ *    value
+ */
+#define BOOSTGROUPS_COUNT 5
+
+/* Array of configured boostgroups */
+static struct schedtune *allocated_group[BOOSTGROUPS_COUNT] = {
+	&root_schedtune,
+	NULL,
+};
+
+/* SchedTune boost groups
+ * Keep track of all the boost groups which impact on CPU, for example when a
+ * CPU has two RUNNABLE tasks belonging to two different boost groups and thus
+ * likely with different boost values.
+ * Since on each system we expect only a limited number of boost groups, here
+ * we use a simple array to keep track of the metrics required to compute the
+ * maximum per-CPU boosting value.
+ */
+struct boost_groups {
+	/* Maximum boost value for all RUNNABLE tasks on a CPU */
+	unsigned boost_max;
+	struct {
+		/* The boost for tasks on that boost group */
+		unsigned boost;
+		/* Count of RUNNABLE tasks on that boost group */
+		unsigned tasks;
+	} group[BOOSTGROUPS_COUNT];
+};
+
+/* Boost groups affecting each CPU in the system */
+DEFINE_PER_CPU(struct boost_groups, cpu_boost_groups);
+
+#ifdef CONFIG_SCHED_HMP
+static inline void init_sched_boost(struct schedtune *st)
+{
+	st->sched_boost_no_override = false;
+	st->sched_boost_enabled = true;
+	st->sched_boost_enabled_backup = st->sched_boost_enabled;
+	st->colocate = false;
+	st->colocate_update_disabled = false;
+}
+
+bool same_schedtune(struct task_struct *tsk1, struct task_struct *tsk2)
+{
+	return task_schedtune(tsk1) == task_schedtune(tsk2);
+}
+
+void update_cgroup_boost_settings(void)
+{
+	int i;
+
+	for (i = 0; i < BOOSTGROUPS_COUNT; i++) {
+		if (!allocated_group[i])
+			break;
+
+		if (allocated_group[i]->sched_boost_no_override)
+			continue;
+
+		allocated_group[i]->sched_boost_enabled = false;
+	}
+}
+
+void restore_cgroup_boost_settings(void)
+{
+	int i;
+
+	for (i = 0; i < BOOSTGROUPS_COUNT; i++) {
+		if (!allocated_group[i])
+			break;
+
+		allocated_group[i]->sched_boost_enabled =
+			allocated_group[i]->sched_boost_enabled_backup;
+	}
+}
+
+bool task_sched_boost(struct task_struct *p)
+{
+	struct schedtune *st = task_schedtune(p);
+
+	return st->sched_boost_enabled;
+}
+
+static u64
+sched_boost_override_read(struct cgroup_subsys_state *css,
+			struct cftype *cft)
+{
+	struct schedtune *st = css_st(css);
+
+	return st->sched_boost_no_override;
+}
+
+static int sched_boost_override_write(struct cgroup_subsys_state *css,
+			struct cftype *cft, u64 override)
+{
+	struct schedtune *st = css_st(css);
+
+	st->sched_boost_no_override = !!override;
+
+	return 0;
+}
+
+static u64 sched_boost_enabled_read(struct cgroup_subsys_state *css,
+			struct cftype *cft)
+{
+	struct schedtune *st = css_st(css);
+
+	return st->sched_boost_enabled;
+}
+
+static int sched_boost_enabled_write(struct cgroup_subsys_state *css,
+			struct cftype *cft, u64 enable)
+{
+	struct schedtune *st = css_st(css);
+
+	st->sched_boost_enabled = !!enable;
+	st->sched_boost_enabled_backup = st->sched_boost_enabled;
+
+	return 0;
+}
+
+static u64 sched_colocate_read(struct cgroup_subsys_state *css,
+			struct cftype *cft)
+{
+	struct schedtune *st = css_st(css);
+
+	return st->colocate;
+}
+
+static int sched_colocate_write(struct cgroup_subsys_state *css,
+			struct cftype *cft, u64 colocate)
+{
+	struct schedtune *st = css_st(css);
+
+	if (st->colocate_update_disabled)
+		return -EPERM;
+
+	st->colocate = !!colocate;
+	st->colocate_update_disabled = true;
+	return 0;
+}
+
+#else /* CONFIG_SCHED_HMP */
+
+static inline void init_sched_boost(struct schedtune *st) { }
+
+#endif /* CONFIG_SCHED_HMP */
+
+static u64
+boost_read(struct cgroup_subsys_state *css, struct cftype *cft)
+{
+	struct schedtune *st = css_st(css);
+
+	return st->boost;
+}
+
+static int
+boost_write(struct cgroup_subsys_state *css, struct cftype *cft,
+	    u64 boost)
+{
+	struct schedtune *st = css_st(css);
+
+	if (boost < 0 || boost > 100)
+		return -EINVAL;
+
+	st->boost = boost;
+	if (css == &root_schedtune.css)
+		sysctl_sched_cfs_boost = boost;
+
+	return 0;
+}
+
+static void schedtune_attach(struct cgroup_taskset *tset)
+{
+	struct task_struct *task;
+	struct cgroup_subsys_state *css;
+	struct schedtune *st;
+	bool colocate;
+
+	cgroup_taskset_first(tset, &css);
+	st = css_st(css);
+
+	colocate = st->colocate;
+
+	cgroup_taskset_for_each(task, css, tset)
+		sync_cgroup_colocation(task, colocate);
+}
+
+static struct cftype files[] = {
+	{
+		.name = "boost",
+		.read_u64 = boost_read,
+		.write_u64 = boost_write,
+	},
+#ifdef CONFIG_SCHED_HMP
+	{
+		.name = "sched_boost_no_override",
+		.read_u64 = sched_boost_override_read,
+		.write_u64 = sched_boost_override_write,
+	},
+	{
+		.name = "sched_boost_enabled",
+		.read_u64 = sched_boost_enabled_read,
+		.write_u64 = sched_boost_enabled_write,
+	},
+	{
+		.name = "colocate",
+		.read_u64 = sched_colocate_read,
+		.write_u64 = sched_colocate_write,
+	},
+#endif
+	{ }	/* terminate */
+};
+
+static int
+schedtune_boostgroup_init(struct schedtune *st)
+{
+	/* Keep track of allocated boost groups */
+	allocated_group[st->idx] = st;
+
+	return 0;
+}
+
+static int
+schedtune_init(void)
+{
+	struct boost_groups *bg;
+	int cpu;
+
+	/* Initialize the per CPU boost groups */
+	for_each_possible_cpu(cpu) {
+		bg = &per_cpu(cpu_boost_groups, cpu);
+		memset(bg, 0, sizeof(struct boost_groups));
+	}
+
+	pr_info("  schedtune configured to support %d boost groups\n",
+		BOOSTGROUPS_COUNT);
+	return 0;
+}
+
+static struct cgroup_subsys_state *
+schedtune_css_alloc(struct cgroup_subsys_state *parent_css)
+{
+	struct schedtune *st;
+	int idx;
+
+	if (!parent_css) {
+		schedtune_init();
+		return &root_schedtune.css;
+	}
+
+	/* Allow only single level hierachies */
+	if (parent_css != &root_schedtune.css) {
+		pr_err("Nested SchedTune boosting groups not allowed\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* Allow only a limited number of boosting groups */
+	for (idx = 1; idx < BOOSTGROUPS_COUNT; ++idx)
+		if (!allocated_group[idx])
+			break;
+	if (idx == BOOSTGROUPS_COUNT) {
+		pr_err("Trying to create more than %d SchedTune boosting groups\n",
+		       BOOSTGROUPS_COUNT);
+		return ERR_PTR(-ENOSPC);
+	}
+
+	st = kzalloc(sizeof(*st), GFP_KERNEL);
+	if (!st)
+		goto out;
+
+	/* Initialize per CPUs boost group support */
+	st->idx = idx;
+	init_sched_boost(st);
+	if (schedtune_boostgroup_init(st))
+		goto release;
+
+	return &st->css;
+
+release:
+	kfree(st);
+out:
+	return ERR_PTR(-ENOMEM);
+}
+
+static void
+schedtune_boostgroup_release(struct schedtune *st)
+{
+	/* Keep track of allocated boost groups */
+	allocated_group[st->idx] = NULL;
+}
+
+static void
+schedtune_css_free(struct cgroup_subsys_state *css)
+{
+	struct schedtune *st = css_st(css);
+
+	schedtune_boostgroup_release(st);
+	kfree(st);
+}
+
+struct cgroup_subsys schedtune_cgrp_subsys = {
+	.css_alloc	= schedtune_css_alloc,
+	.css_free	= schedtune_css_free,
+	.legacy_cftypes	= files,
+	.early_init	= 1,
+	.allow_attach	= subsys_cgroup_allow_attach,
+	.attach		= schedtune_attach,
+};
+
+#endif /* CONFIG_CGROUP_SCHEDTUNE */
+
+int
+sysctl_sched_cfs_boost_handler(struct ctl_table *table, int write,
+			       void __user *buffer, size_t *lenp,
+			       loff_t *ppos)
+{
+	int ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);
+
+	if (ret || !write)
+		return ret;
+
+	return 0;
+}
+
diff --git a/kernel/smp.c b/kernel/smp.c
index fa362c0..ee80cc8 100644
--- a/kernel/smp.c
+++ b/kernel/smp.c
@@ -751,8 +751,8 @@ void wake_up_all_idle_cpus(void)
 	for_each_online_cpu(cpu) {
 		if (cpu == smp_processor_id())
 			continue;
-
-		wake_up_if_idle(cpu);
+		if (!cpu_isolated(cpu))
+			wake_up_if_idle(cpu);
 	}
 	preempt_enable();
 }
diff --git a/kernel/smpboot.c b/kernel/smpboot.c
index 4a5c6e7..1650578 100644
--- a/kernel/smpboot.c
+++ b/kernel/smpboot.c
@@ -31,7 +31,7 @@ struct task_struct *idle_thread_get(unsigned int cpu)
 
 	if (!tsk)
 		return ERR_PTR(-ENOMEM);
-	init_idle(tsk, cpu);
+	init_idle(tsk, cpu, true);
 	return tsk;
 }
 
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 337cb1e..db14070 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -123,10 +123,14 @@ static int __maybe_unused neg_one = -1;
 static int zero;
 static int __maybe_unused one = 1;
 static int __maybe_unused two = 2;
+static int __maybe_unused three = 3;
 static int __maybe_unused four = 4;
 static unsigned long one_ul = 1;
 static int one_hundred = 100;
 static int one_thousand = 1000;
+#ifdef CONFIG_SCHED_HMP
+static int max_freq_reporting_policy = FREQ_REPORT_INVALID_POLICY - 1;
+#endif
 #ifdef CONFIG_PRINTK
 static int ten_thousand = 10000;
 #endif
@@ -288,14 +292,16 @@ static struct ctl_table kern_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dointvec,
 	},
+#ifdef CONFIG_SCHED_HMP
 	{
-		.procname	= "sched_wake_to_idle",
-		.data		= &sysctl_sched_wake_to_idle,
+		.procname	= "sched_freq_reporting_policy",
+		.data		= &sysctl_sched_freq_reporting_policy,
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &max_freq_reporting_policy,
 	},
-#ifdef CONFIG_SCHED_HMP
 	{
 		.procname	= "sched_freq_inc_notify",
 		.data		= &sysctl_sched_freq_inc_notify,
@@ -369,6 +375,22 @@ static struct ctl_table kern_table[] = {
 		.extra2		= &one_hundred,
 	},
 	{
+		.procname	= "sched_group_upmigrate",
+		.data		= &sysctl_sched_group_upmigrate_pct,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_hmp_proc_update_handler,
+		.extra1		= &zero,
+	},
+	{
+		.procname	= "sched_group_downmigrate",
+		.data		= &sysctl_sched_group_downmigrate_pct,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= sched_hmp_proc_update_handler,
+		.extra1		= &zero,
+	},
+	{
 		.procname	= "sched_init_task_load",
 		.data		= &sysctl_sched_init_task_load_pct,
 		.maxlen		= sizeof(unsigned int),
@@ -386,15 +408,6 @@ static struct ctl_table kern_table[] = {
 		.extra1		= &zero,
 	},
 	{
-		.procname       = "sched_enable_colocation",
-		.data           = &sysctl_sched_enable_colocation,
-		.maxlen         = sizeof(unsigned int),
-		.mode           = 0644,
-		.proc_handler   = proc_dointvec,
-		.extra1		= &zero,
-		.extra2		= &one,
-	},
-	{
 		.procname	= "sched_restrict_cluster_spill",
 		.data		= &sysctl_sched_restrict_cluster_spill,
 		.maxlen		= sizeof(unsigned int),
@@ -422,6 +435,15 @@ static struct ctl_table kern_table[] = {
 		.extra2		= &one_hundred,
 	},
 	{
+		.procname	= "sched_prefer_sync_wakee_to_waker",
+		.data		= &sysctl_sched_prefer_sync_wakee_to_waker,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &zero,
+		.extra2		= &one,
+	},
+	{
 		.procname       = "sched_enable_thread_grouping",
 		.data           = &sysctl_sched_enable_thread_grouping,
 		.maxlen         = sizeof(unsigned int),
@@ -470,6 +492,22 @@ static struct ctl_table kern_table[] = {
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
 		.proc_handler	= sched_boost_handler,
+		.extra1         = &zero,
+		.extra2		= &three,
+	},
+	{
+		.procname	= "sched_short_burst_ns",
+		.data		= &sysctl_sched_short_burst,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= proc_dointvec,
+	},
+	{
+		.procname       = "sched_short_sleep_ns",
+		.data           = &sysctl_sched_short_sleep,
+		.maxlen         = sizeof(unsigned int),
+		.mode           = 0644,
+		.proc_handler   = proc_dointvec,
 	},
 #endif	/* CONFIG_SCHED_HMP */
 #ifdef CONFIG_SCHED_DEBUG
@@ -529,7 +567,8 @@ static struct ctl_table kern_table[] = {
 		.data		= &sysctl_sched_time_avg,
 		.maxlen		= sizeof(unsigned int),
 		.mode		= 0644,
-		.proc_handler	= proc_dointvec,
+		.proc_handler	= proc_dointvec_minmax,
+		.extra1		= &one,
 	},
 	{
 		.procname	= "sched_shares_window_ns",
@@ -633,6 +672,21 @@ static struct ctl_table kern_table[] = {
 		.extra1		= &one,
 	},
 #endif
+#ifdef CONFIG_SCHED_TUNE
+	{
+		.procname	= "sched_cfs_boost",
+		.data		= &sysctl_sched_cfs_boost,
+		.maxlen		= sizeof(sysctl_sched_cfs_boost),
+#ifdef CONFIG_CGROUP_SCHEDTUNE
+		.mode		= 0444,
+#else
+		.mode		= 0644,
+#endif
+		.proc_handler	= &sysctl_sched_cfs_boost_handler,
+		.extra1		= &zero,
+		.extra2		= &one_hundred,
+	},
+#endif
 #ifdef CONFIG_PROVE_LOCKING
 	{
 		.procname	= "prove_locking",
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index 49eca0b..5819ca0 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -12,3 +12,5 @@
 obj-$(CONFIG_TIMER_STATS)			+= timer_stats.o
 obj-$(CONFIG_DEBUG_FS)				+= timekeeping_debug.o
 obj-$(CONFIG_TEST_UDELAY)			+= test_udelay.o
+
+ccflags-y += -Idrivers/cpuidle
diff --git a/kernel/time/alarmtimer.c b/kernel/time/alarmtimer.c
index 12dd190..fa80192 100644
--- a/kernel/time/alarmtimer.c
+++ b/kernel/time/alarmtimer.c
@@ -26,6 +26,11 @@
 #include <linux/workqueue.h>
 #include <linux/freezer.h>
 
+#ifdef CONFIG_MSM_PM
+#include "lpm-levels.h"
+#endif
+#include <linux/workqueue.h>
+
 /**
  * struct alarm_base - Alarm timer bases
  * @lock:		Lock for syncrhonized access to the base
@@ -45,14 +50,130 @@ static ktime_t freezer_delta;
 static DEFINE_SPINLOCK(freezer_delta_lock);
 
 static struct wakeup_source *ws;
+static struct delayed_work work;
+static struct workqueue_struct *power_off_alarm_workqueue;
 
 #ifdef CONFIG_RTC_CLASS
 /* rtc timer and device for setting alarm wakeups at suspend */
 static struct rtc_timer		rtctimer;
 static struct rtc_device	*rtcdev;
 static DEFINE_SPINLOCK(rtcdev_lock);
+static struct mutex power_on_alarm_lock;
+static struct alarm init_alarm;
 
 /**
+ * power_on_alarm_init - Init power on alarm value
+ *
+ * Read rtc alarm value after device booting up and add this alarm
+ * into alarm queue.
+ */
+void power_on_alarm_init(void)
+{
+	struct rtc_wkalrm rtc_alarm;
+	struct rtc_time rt;
+	unsigned long alarm_time;
+	struct rtc_device *rtc;
+	ktime_t alarm_ktime;
+
+	rtc = alarmtimer_get_rtcdev();
+
+	if (!rtc)
+		return;
+
+	rtc_read_alarm(rtc, &rtc_alarm);
+	rt = rtc_alarm.time;
+
+	rtc_tm_to_time(&rt, &alarm_time);
+
+	if (alarm_time) {
+		alarm_ktime = ktime_set(alarm_time, 0);
+		alarm_init(&init_alarm, ALARM_POWEROFF_REALTIME, NULL);
+		alarm_start(&init_alarm, alarm_ktime);
+	}
+}
+
+/**
+ * set_power_on_alarm - set power on alarm value into rtc register
+ *
+ * Get the soonest power off alarm timer and set the alarm value into rtc
+ * register.
+ */
+void set_power_on_alarm(void)
+{
+	int rc;
+	struct timespec wall_time, alarm_ts;
+	long alarm_secs = 0l;
+	long rtc_secs, alarm_time, alarm_delta;
+	struct rtc_time rtc_time;
+	struct rtc_wkalrm alarm;
+	struct rtc_device *rtc;
+	struct timerqueue_node *next;
+	unsigned long flags;
+	struct alarm_base *base = &alarm_bases[ALARM_POWEROFF_REALTIME];
+
+	rc = mutex_lock_interruptible(&power_on_alarm_lock);
+	if (rc != 0)
+		return;
+
+	spin_lock_irqsave(&base->lock, flags);
+	next = timerqueue_getnext(&base->timerqueue);
+	spin_unlock_irqrestore(&base->lock, flags);
+
+	if (next) {
+		alarm_ts = ktime_to_timespec(next->expires);
+		alarm_secs = alarm_ts.tv_sec;
+	}
+
+	if (!alarm_secs)
+		goto disable_alarm;
+
+	getnstimeofday(&wall_time);
+
+	/*
+	 * alarm_secs have to be bigger than "wall_time +1".
+	 * It is to make sure that alarm time will be always
+	 * bigger than wall time.
+	 */
+	if (alarm_secs <= wall_time.tv_sec + 1)
+		goto disable_alarm;
+
+	rtc = alarmtimer_get_rtcdev();
+	if (!rtc)
+		goto exit;
+
+	rtc_read_time(rtc, &rtc_time);
+	rtc_tm_to_time(&rtc_time, &rtc_secs);
+	alarm_delta = wall_time.tv_sec - rtc_secs;
+	alarm_time = alarm_secs - alarm_delta;
+
+	rtc_time_to_tm(alarm_time, &alarm.time);
+	alarm.enabled = 1;
+	rc = rtc_set_alarm(rtcdev, &alarm);
+	if (rc)
+		goto disable_alarm;
+
+	mutex_unlock(&power_on_alarm_lock);
+	return;
+
+disable_alarm:
+	rtc_alarm_irq_enable(rtcdev, 0);
+exit:
+	mutex_unlock(&power_on_alarm_lock);
+}
+
+static void alarmtimer_triggered_func(void *p)
+{
+	struct rtc_device *rtc = rtcdev;
+
+	if (!(rtc->irq_data & RTC_AF))
+		return;
+	__pm_wakeup_event(ws, 2 * MSEC_PER_SEC);
+}
+
+static struct rtc_task alarmtimer_rtc_task = {
+	.func = alarmtimer_triggered_func
+};
+/**
  * alarmtimer_get_rtcdev - Return selected rtcdevice
  *
  * This function returns the rtc device to use for wakealarms.
@@ -62,7 +183,7 @@ static DEFINE_SPINLOCK(rtcdev_lock);
 struct rtc_device *alarmtimer_get_rtcdev(void)
 {
 	unsigned long flags;
-	struct rtc_device *ret;
+	struct rtc_device *ret = NULL;
 
 	spin_lock_irqsave(&rtcdev_lock, flags);
 	ret = rtcdev;
@@ -76,33 +197,48 @@ static int alarmtimer_rtc_add_device(struct device *dev,
 				struct class_interface *class_intf)
 {
 	unsigned long flags;
+	int err = 0;
 	struct rtc_device *rtc = to_rtc_device(dev);
-
 	if (rtcdev)
 		return -EBUSY;
-
 	if (!rtc->ops->set_alarm)
 		return -1;
-	if (!device_may_wakeup(rtc->dev.parent))
-		return -1;
 
 	spin_lock_irqsave(&rtcdev_lock, flags);
 	if (!rtcdev) {
+		err = rtc_irq_register(rtc, &alarmtimer_rtc_task);
+		if (err)
+			goto rtc_irq_reg_err;
 		rtcdev = rtc;
 		/* hold a reference so it doesn't go away */
 		get_device(dev);
 	}
+
+rtc_irq_reg_err:
 	spin_unlock_irqrestore(&rtcdev_lock, flags);
-	return 0;
+	return err;
+
+}
+
+static void alarmtimer_rtc_remove_device(struct device *dev,
+				struct class_interface *class_intf)
+{
+	if (rtcdev && dev == &rtcdev->dev) {
+		rtc_irq_unregister(rtcdev, &alarmtimer_rtc_task);
+		rtcdev = NULL;
+	}
 }
 
 static inline void alarmtimer_rtc_timer_init(void)
 {
+	mutex_init(&power_on_alarm_lock);
+
 	rtc_timer_init(&rtctimer, NULL, NULL);
 }
 
 static struct class_interface alarmtimer_rtc_interface = {
 	.add_dev = &alarmtimer_rtc_add_device,
+	.remove_dev = &alarmtimer_rtc_remove_device,
 };
 
 static int alarmtimer_rtc_interface_setup(void)
@@ -123,8 +259,14 @@ struct rtc_device *alarmtimer_get_rtcdev(void)
 static inline int alarmtimer_rtc_interface_setup(void) { return 0; }
 static inline void alarmtimer_rtc_interface_remove(void) { }
 static inline void alarmtimer_rtc_timer_init(void) { }
+void set_power_on_alarm(void) { }
 #endif
 
+static void alarm_work_func(struct work_struct *unused)
+{
+	set_power_on_alarm();
+}
+
 /**
  * alarmtimer_enqueue - Adds an alarm timer to an alarm_base timerqueue
  * @base: pointer to the base where the timer is being run
@@ -194,6 +336,10 @@ static enum hrtimer_restart alarmtimer_fired(struct hrtimer *timer)
 	}
 	spin_unlock_irqrestore(&base->lock, flags);
 
+	/* set next power off alarm */
+	if (alarm->type == ALARM_POWEROFF_REALTIME)
+		queue_delayed_work(power_off_alarm_workqueue, &work, 0);
+
 	return ret;
 
 }
@@ -216,6 +362,70 @@ EXPORT_SYMBOL_GPL(alarm_expires_remaining);
  * set an rtc timer to fire that far into the future, which
  * will wake us from suspend.
  */
+#if defined(CONFIG_RTC_DRV_QPNP) && defined(CONFIG_MSM_PM)
+static int alarmtimer_suspend(struct device *dev)
+{
+	struct rtc_time tm;
+	ktime_t min, now;
+	unsigned long flags;
+	struct rtc_device *rtc;
+	int i;
+	int ret = 0;
+
+	spin_lock_irqsave(&freezer_delta_lock, flags);
+	min = freezer_delta;
+	freezer_delta = ktime_set(0, 0);
+	spin_unlock_irqrestore(&freezer_delta_lock, flags);
+
+	rtc = alarmtimer_get_rtcdev();
+	/* If we have no rtcdev, just return */
+	if (!rtc)
+		return 0;
+
+	/* Find the soonest timer to expire*/
+	for (i = 0; i < ALARM_NUMTYPE; i++) {
+		struct alarm_base *base = &alarm_bases[i];
+		struct timerqueue_node *next;
+		ktime_t delta;
+
+		spin_lock_irqsave(&base->lock, flags);
+		next = timerqueue_getnext(&base->timerqueue);
+		spin_unlock_irqrestore(&base->lock, flags);
+		if (!next)
+			continue;
+		delta = ktime_sub(next->expires, base->gettime());
+		if (!min.tv64 || (delta.tv64 < min.tv64))
+			min = delta;
+	}
+	if (min.tv64 == 0)
+		return 0;
+
+	if (ktime_to_ns(min) < 2 * NSEC_PER_SEC) {
+		__pm_wakeup_event(ws, 2 * MSEC_PER_SEC);
+		return -EBUSY;
+	}
+
+	/* Setup a timer to fire that far in the future */
+	rtc_timer_cancel(rtc, &rtctimer);
+	rtc_read_time(rtc, &tm);
+	now = rtc_tm_to_ktime(tm);
+	now = ktime_add(now, min);
+	if (poweron_alarm) {
+		struct rtc_time tm_val;
+		unsigned long secs;
+
+		tm_val = rtc_ktime_to_tm(min);
+		rtc_tm_to_time(&tm_val, &secs);
+		lpm_suspend_wake_time(secs);
+	} else {
+		/* Set alarm, if in the past reject suspend briefly to handle */
+		ret = rtc_timer_start(rtc, &rtctimer, now, ktime_set(0, 0));
+		if (ret < 0)
+			__pm_wakeup_event(ws, MSEC_PER_SEC);
+	}
+	return ret;
+}
+#else
 static int alarmtimer_suspend(struct device *dev)
 {
 	struct rtc_time tm;
@@ -225,6 +435,8 @@ static int alarmtimer_suspend(struct device *dev)
 	int i;
 	int ret;
 
+	cancel_delayed_work_sync(&work);
+
 	spin_lock_irqsave(&freezer_delta_lock, flags);
 	min = freezer_delta;
 	freezer_delta = ktime_set(0, 0);
@@ -270,7 +482,7 @@ static int alarmtimer_suspend(struct device *dev)
 		__pm_wakeup_event(ws, MSEC_PER_SEC);
 	return ret;
 }
-
+#endif
 static int alarmtimer_resume(struct device *dev)
 {
 	struct rtc_device *rtc;
@@ -278,6 +490,8 @@ static int alarmtimer_resume(struct device *dev)
 	rtc = alarmtimer_get_rtcdev();
 	if (rtc)
 		rtc_timer_cancel(rtc, &rtctimer);
+
+	queue_delayed_work(power_off_alarm_workqueue, &work, 0);
 	return 0;
 }
 
@@ -458,12 +672,14 @@ EXPORT_SYMBOL_GPL(alarm_forward_now);
  * clock2alarm - helper that converts from clockid to alarmtypes
  * @clockid: clockid.
  */
-static enum alarmtimer_type clock2alarm(clockid_t clockid)
+enum alarmtimer_type clock2alarm(clockid_t clockid)
 {
 	if (clockid == CLOCK_REALTIME_ALARM)
 		return ALARM_REALTIME;
 	if (clockid == CLOCK_BOOTTIME_ALARM)
 		return ALARM_BOOTTIME;
+	if (clockid == CLOCK_POWEROFF_ALARM)
+		return ALARM_POWEROFF_REALTIME;
 	return -1;
 }
 
@@ -848,10 +1064,13 @@ static int __init alarmtimer_init(void)
 
 	posix_timers_register_clock(CLOCK_REALTIME_ALARM, &alarm_clock);
 	posix_timers_register_clock(CLOCK_BOOTTIME_ALARM, &alarm_clock);
+	posix_timers_register_clock(CLOCK_POWEROFF_ALARM, &alarm_clock);
 
 	/* Initialize alarm bases */
 	alarm_bases[ALARM_REALTIME].base_clockid = CLOCK_REALTIME;
 	alarm_bases[ALARM_REALTIME].gettime = &ktime_get_real;
+	alarm_bases[ALARM_POWEROFF_REALTIME].base_clockid = CLOCK_REALTIME;
+	alarm_bases[ALARM_POWEROFF_REALTIME].gettime = &ktime_get_real;
 	alarm_bases[ALARM_BOOTTIME].base_clockid = CLOCK_BOOTTIME;
 	alarm_bases[ALARM_BOOTTIME].gettime = &ktime_get_boottime;
 	for (i = 0; i < ALARM_NUMTYPE; i++) {
@@ -873,8 +1092,24 @@ static int __init alarmtimer_init(void)
 		goto out_drv;
 	}
 	ws = wakeup_source_register("alarmtimer");
-	return 0;
+	if (!ws) {
+		error = -ENOMEM;
+		goto out_ws;
+	}
 
+	INIT_DELAYED_WORK(&work, alarm_work_func);
+	power_off_alarm_workqueue =
+		create_singlethread_workqueue("power_off_alarm");
+	if (!power_off_alarm_workqueue) {
+		error = -ENOMEM;
+		goto out_wq;
+	}
+
+	return 0;
+out_wq:
+	wakeup_source_unregister(ws);
+out_ws:
+	platform_device_unregister(pdev);
 out_drv:
 	platform_driver_unregister(&alarmtimer_driver);
 out_if:
diff --git a/kernel/time/hrtimer.c b/kernel/time/hrtimer.c
index bb5ec42..4223c4a 100644
--- a/kernel/time/hrtimer.c
+++ b/kernel/time/hrtimer.c
@@ -870,7 +870,7 @@ static int enqueue_hrtimer(struct hrtimer *timer,
 
 	base->cpu_base->active_bases |= 1 << base->index;
 
-	timer->state = HRTIMER_STATE_ENQUEUED;
+	timer->state |= HRTIMER_STATE_ENQUEUED;
 
 	return timerqueue_add(&base->active, &timer->node);
 }
@@ -890,11 +890,9 @@ static void __remove_hrtimer(struct hrtimer *timer,
 			     u8 newstate, int reprogram)
 {
 	struct hrtimer_cpu_base *cpu_base = base->cpu_base;
-	u8 state = timer->state;
 
-	timer->state = newstate;
-	if (!(state & HRTIMER_STATE_ENQUEUED))
-		return;
+	if (!(timer->state & HRTIMER_STATE_ENQUEUED))
+		goto out;
 
 	if (!timerqueue_del(&base->active, &timer->node))
 		cpu_base->active_bases &= ~(1 << base->index);
@@ -911,6 +909,13 @@ static void __remove_hrtimer(struct hrtimer *timer,
 	if (reprogram && timer == cpu_base->next_timer)
 		hrtimer_force_reprogram(cpu_base, 1);
 #endif
+
+out:
+	/*
+	* We need to preserve PINNED state here, otherwise we may end up
+	* migrating pinned hrtimers as well.
+	*/
+	timer->state = newstate | (timer->state & HRTIMER_STATE_PINNED);
 }
 
 /*
@@ -939,6 +944,7 @@ remove_hrtimer(struct hrtimer *timer, struct hrtimer_clock_base *base, bool rest
 			state = HRTIMER_STATE_INACTIVE;
 
 		__remove_hrtimer(timer, base, state, reprogram);
+		timer->state &= ~HRTIMER_STATE_PINNED;
 		return 1;
 	}
 	return 0;
@@ -992,6 +998,10 @@ void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
 
 	timer_stats_hrtimer_set_start_info(timer);
 
+	/* Update pinned state */
+	timer->state &= ~HRTIMER_STATE_PINNED;
+	timer->state |= (!!(mode & HRTIMER_MODE_PINNED)) << HRTIMER_PINNED_SHIFT;
+
 	leftmost = enqueue_hrtimer(timer, new_base);
 	if (!leftmost)
 		goto unlock;
@@ -1166,8 +1176,8 @@ bool hrtimer_active(const struct hrtimer *timer)
 		cpu_base = READ_ONCE(timer->base->cpu_base);
 		seq = raw_read_seqcount_begin(&cpu_base->seq);
 
-		if (timer->state != HRTIMER_STATE_INACTIVE ||
-		    cpu_base->running == timer)
+		if (((timer->state & ~HRTIMER_STATE_PINNED) !=
+		      HRTIMER_STATE_INACTIVE) || cpu_base->running == timer)
 			return true;
 
 	} while (read_seqcount_retry(&cpu_base->seq, seq) ||
@@ -1606,12 +1616,16 @@ int hrtimers_prepare_cpu(unsigned int cpu)
 }
 
 #ifdef CONFIG_HOTPLUG_CPU
-
 static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
-				struct hrtimer_clock_base *new_base)
+				 struct hrtimer_clock_base *new_base,
+				 bool remove_pinned)
 {
 	struct hrtimer *timer;
 	struct timerqueue_node *node;
+	struct timerqueue_head pinned;
+	int is_pinned;
+
+	timerqueue_init_head(&pinned);
 
 	while ((node = timerqueue_getnext(&old_base->active))) {
 		timer = container_of(node, struct hrtimer, node);
@@ -1624,6 +1638,13 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
 		 * under us on another CPU
 		 */
 		__remove_hrtimer(timer, old_base, HRTIMER_STATE_ENQUEUED, 0);
+
+		is_pinned = timer->state & HRTIMER_STATE_PINNED;
+		if (!remove_pinned && is_pinned) {
+			timerqueue_add(&pinned, &timer->node);
+			continue;
+		}
+
 		timer->base = new_base;
 		/*
 		 * Enqueue the timers on the new cpu. This does not
@@ -1635,17 +1656,23 @@ static void migrate_hrtimer_list(struct hrtimer_clock_base *old_base,
 		 */
 		enqueue_hrtimer(timer, new_base);
 	}
+
+	/* Re-queue pinned timers for non-hotplug usecase */
+	while ((node = timerqueue_getnext(&pinned))) {
+		timer = container_of(node, struct hrtimer, node);
+
+		timerqueue_del(&pinned, &timer->node);
+		enqueue_hrtimer(timer, old_base);
+	}
 }
 
-int hrtimers_dead_cpu(unsigned int scpu)
+static void __migrate_hrtimers(unsigned int scpu, bool remove_pinned)
 {
 	struct hrtimer_cpu_base *old_base, *new_base;
+	unsigned long flags;
 	int i;
 
-	BUG_ON(cpu_online(scpu));
-	tick_cancel_sched_timer(scpu);
-
-	local_irq_disable();
+	local_irq_save(flags);
 	old_base = &per_cpu(hrtimer_bases, scpu);
 	new_base = this_cpu_ptr(&hrtimer_bases);
 	/*
@@ -1657,7 +1684,7 @@ int hrtimers_dead_cpu(unsigned int scpu)
 
 	for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
 		migrate_hrtimer_list(&old_base->clock_base[i],
-				     &new_base->clock_base[i]);
+				     &new_base->clock_base[i], remove_pinned);
 	}
 
 	raw_spin_unlock(&old_base->lock);
@@ -1665,10 +1692,23 @@ int hrtimers_dead_cpu(unsigned int scpu)
 
 	/* Check, if we got expired work to do */
 	__hrtimer_peek_ahead_timers();
-	local_irq_enable();
+	local_irq_restore(flags);
+}
+
+int hrtimers_dead_cpu(unsigned int scpu)
+{
+	BUG_ON(cpu_online(scpu));
+	tick_cancel_sched_timer(scpu);
+
+	__migrate_hrtimers(scpu, true);
 	return 0;
 }
 
+void hrtimer_quiesce_cpu(void *cpup)
+{
+	__migrate_hrtimers(*(int *)cpup, false);
+}
+
 #endif /* CONFIG_HOTPLUG_CPU */
 
 void __init hrtimers_init(void)
diff --git a/kernel/time/timer.c b/kernel/time/timer.c
index c611c47..f605186 100644
--- a/kernel/time/timer.c
+++ b/kernel/time/timer.c
@@ -1808,27 +1808,32 @@ signed long __sched schedule_timeout_idle(signed long timeout)
 EXPORT_SYMBOL(schedule_timeout_idle);
 
 #ifdef CONFIG_HOTPLUG_CPU
-static void migrate_timer_list(struct timer_base *new_base, struct hlist_head *head)
+static void migrate_timer_list(struct timer_base *new_base,
+			       struct hlist_head *head, bool remove_pinned)
 {
 	struct timer_list *timer;
 	int cpu = new_base->cpu;
+	struct hlist_node *n;
+	int is_pinned;
 
-	while (!hlist_empty(head)) {
-		timer = hlist_entry(head->first, struct timer_list, entry);
-		detach_timer(timer, false);
+	hlist_for_each_entry_safe(timer, n, head, entry) {
+		is_pinned = timer->flags & TIMER_PINNED;
+		if (!remove_pinned && is_pinned)
+			continue;
+
+		detach_if_pending(timer, get_timer_base(timer->flags), false);
 		timer->flags = (timer->flags & ~TIMER_BASEMASK) | cpu;
 		internal_add_timer(new_base, timer);
 	}
 }
 
-int timers_dead_cpu(unsigned int cpu)
+static void __migrate_timers(unsigned int cpu, bool remove_pinned)
 {
 	struct timer_base *old_base;
 	struct timer_base *new_base;
+	unsigned long flags;
 	int b, i;
 
-	BUG_ON(cpu_online(cpu));
-
 	for (b = 0; b < NR_BASES; b++) {
 		old_base = per_cpu_ptr(&timer_bases[b], cpu);
 		new_base = get_cpu_ptr(&timer_bases[b]);
@@ -1836,21 +1841,33 @@ int timers_dead_cpu(unsigned int cpu)
 		 * The caller is globally serialized and nobody else
 		 * takes two locks at once, deadlock is not possible.
 		 */
-		spin_lock_irq(&new_base->lock);
+		spin_lock_irqsave(&new_base->lock, flags);
 		spin_lock_nested(&old_base->lock, SINGLE_DEPTH_NESTING);
 
 		BUG_ON(old_base->running_timer);
 
 		for (i = 0; i < WHEEL_SIZE; i++)
-			migrate_timer_list(new_base, old_base->vectors + i);
+			migrate_timer_list(new_base, old_base->vectors + i,
+					   remove_pinned);
 
 		spin_unlock(&old_base->lock);
-		spin_unlock_irq(&new_base->lock);
+		spin_unlock_irqrestore(&new_base->lock, flags);
 		put_cpu_ptr(&timer_bases);
 	}
+}
+
+int timers_dead_cpu(unsigned int cpu)
+{
+	BUG_ON(cpu_online(cpu));
+	__migrate_timers(cpu, true);
 	return 0;
 }
 
+void timer_quiesce_cpu(void *cpup)
+{
+	__migrate_timers(*(unsigned int *)cpup, false);
+}
+
 #endif /* CONFIG_HOTPLUG_CPU */
 
 static void __init init_timer_cpu(int cpu)
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index a1f78d4..f3631a3 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -13,6 +13,7 @@
 
 #include <linux/mm.h>
 #include <linux/cpu.h>
+#include <linux/device.h>
 #include <linux/nmi.h>
 #include <linux/init.h>
 #include <linux/module.h>
@@ -95,6 +96,7 @@ static u64 __read_mostly sample_period;
 static DEFINE_PER_CPU(unsigned long, watchdog_touch_ts);
 static DEFINE_PER_CPU(struct task_struct *, softlockup_watchdog);
 static DEFINE_PER_CPU(struct hrtimer, watchdog_hrtimer);
+static DEFINE_PER_CPU(unsigned int, watchdog_en);
 static DEFINE_PER_CPU(bool, softlockup_touch_sync);
 static DEFINE_PER_CPU(bool, soft_watchdog_warn);
 static DEFINE_PER_CPU(unsigned long, hrtimer_interrupts);
@@ -584,9 +586,13 @@ static void watchdog_set_prio(unsigned int policy, unsigned int prio)
 	sched_setscheduler(current, policy, &param);
 }
 
-static void watchdog_enable(unsigned int cpu)
+void watchdog_enable(unsigned int cpu)
 {
 	struct hrtimer *hrtimer = raw_cpu_ptr(&watchdog_hrtimer);
+	unsigned int *enabled = raw_cpu_ptr(&watchdog_en);
+
+	if (*enabled)
+		return;
 
 	/* kick off the timer for the hardlockup detector */
 	hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
@@ -602,16 +608,40 @@ static void watchdog_enable(unsigned int cpu)
 	/* initialize timestamp */
 	watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1);
 	__touch_watchdog();
+
+	/*
+	 * Need to ensure above operations are observed by other CPUs before
+	 * indicating that timer is enabled. This is to synchronize core
+	 * isolation and hotplug. Core isolation will wait for this flag to be
+	 * set.
+	 */
+	mb();
+	*enabled = 1;
 }
 
-static void watchdog_disable(unsigned int cpu)
+void watchdog_disable(unsigned int cpu)
 {
 	struct hrtimer *hrtimer = raw_cpu_ptr(&watchdog_hrtimer);
+	unsigned int *enabled = raw_cpu_ptr(&watchdog_en);
+
+	if (!*enabled)
+		return;
 
 	watchdog_set_prio(SCHED_NORMAL, 0);
 	hrtimer_cancel(hrtimer);
 	/* disable the perf event */
 	watchdog_nmi_disable(cpu);
+
+	/*
+	 * No need for barrier here since disabling the watchdog is
+	 * synchronized with hotplug lock
+	 */
+	*enabled = 0;
+}
+
+bool watchdog_configured(unsigned int cpu)
+{
+	return *per_cpu_ptr(&watchdog_en, cpu);
 }
 
 static void watchdog_cleanup(unsigned int cpu, bool online)
diff --git a/mm/vmstat.c b/mm/vmstat.c
index 604f26a..25a1f39 100644
--- a/mm/vmstat.c
+++ b/mm/vmstat.c
@@ -1612,7 +1612,7 @@ int vmstat_refresh(struct ctl_table *table, int write,
 
 static void vmstat_update(struct work_struct *w)
 {
-	if (refresh_cpu_vm_stats(true)) {
+	if (refresh_cpu_vm_stats(true) && !cpu_isolated(smp_processor_id())) {
 		/*
 		 * Counters were updated so we expect more updates
 		 * to occur in the future. Keep on running the
@@ -1696,7 +1696,8 @@ static void vmstat_shepherd(struct work_struct *w)
 	for_each_online_cpu(cpu) {
 		struct delayed_work *dw = &per_cpu(vmstat_work, cpu);
 
-		if (!delayed_work_pending(dw) && need_update(cpu))
+		if (!delayed_work_pending(dw) && need_update(cpu) &&
+							!cpu_isolated(cpu))
 			queue_delayed_work_on(cpu, vmstat_wq, dw, 0);
 	}
 	put_online_cpus();
diff --git a/net/rmnet_data/rmnet_data_config.c b/net/rmnet_data/rmnet_data_config.c
index 8eab5ae..f82676d 100644
--- a/net/rmnet_data/rmnet_data_config.c
+++ b/net/rmnet_data/rmnet_data_config.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -557,6 +557,11 @@ void rmnet_config_netlink_msg_handler(struct sk_buff *skb)
 	nlmsg_header = (struct nlmsghdr *)skb->data;
 	rmnet_header = (struct rmnet_nl_msg_s *)nlmsg_data(nlmsg_header);
 
+	if (!nlmsg_header->nlmsg_pid ||
+	    (nlmsg_header->nlmsg_len < sizeof(struct nlmsghdr) +
+				       sizeof(struct rmnet_nl_msg_s)))
+		return;
+
 	LOGL("Netlink message pid=%d, seq=%d, length=%d, rmnet_type=%d",
 	     nlmsg_header->nlmsg_pid,
 	     nlmsg_header->nlmsg_seq,
diff --git a/net/rmnet_data/rmnet_data_handlers.c b/net/rmnet_data/rmnet_data_handlers.c
index 825d57e..42955a2 100644
--- a/net/rmnet_data/rmnet_data_handlers.c
+++ b/net/rmnet_data/rmnet_data_handlers.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -489,6 +489,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
 		if (pskb_expand_head(skb, required_headroom, 0, GFP_KERNEL)) {
 			LOGD("Failed to add headroom of %d bytes",
 			     required_headroom);
+			kfree_skb(skb);
 			return 1;
 		}
 	}
@@ -512,6 +513,7 @@ static int rmnet_map_egress_handler(struct sk_buff *skb,
 
 	if (!map_header) {
 		LOGD("%s", "Failed to add MAP header to egress packet");
+		kfree_skb(skb);
 		return 1;
 	}
 
diff --git a/net/rmnet_data/rmnet_data_vnd.c b/net/rmnet_data/rmnet_data_vnd.c
index 166b445..64217bd 100644
--- a/net/rmnet_data/rmnet_data_vnd.c
+++ b/net/rmnet_data/rmnet_data_vnd.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -176,6 +176,7 @@ static netdev_tx_t rmnet_vnd_start_xmit(struct sk_buff *skb,
 			rmnet_vnd_add_qos_header(skb,
 						 dev,
 						 dev_conf->qos_version);
+		skb_orphan(skb);
 		rmnet_egress_handler(skb, &dev_conf->local_ep);
 	} else {
 		dev->stats.tx_dropped++;
@@ -497,6 +498,17 @@ static void rmnet_vnd_setup(struct net_device *dev)
 	INIT_LIST_HEAD(&dev_conf->flow_head);
 }
 
+/* rmnet_vnd_setup() - net_device initialization helper function
+ * @dev:      Virtual network device
+ *
+ * Called during device initialization. Disables GRO.
+ */
+static void rmnet_vnd_disable_offload(struct net_device *dev)
+{
+	dev->wanted_features &= ~NETIF_F_GRO;
+	__netdev_update_features(dev);
+}
+
 /* Exposed API */
 
 /* rmnet_vnd_exit() - Shutdown cleanup hook
@@ -607,6 +619,8 @@ int rmnet_vnd_create_dev(int id, struct net_device **new_device,
 		LOGM("Registered device %s", dev->name);
 	}
 
+	rmnet_vnd_disable_offload(dev);
+
 	return rc;
 }
 
diff --git a/net/rmnet_data/rmnet_map_command.c b/net/rmnet_data/rmnet_map_command.c
index 306b6fb..6618f32 100644
--- a/net/rmnet_data/rmnet_map_command.c
+++ b/net/rmnet_data/rmnet_map_command.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved.
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -118,6 +118,7 @@ static void rmnet_map_send_ack(struct sk_buff *skb,
 {
 	struct rmnet_map_control_command_s *cmd;
 	int xmit_status;
+	int rc;
 
 	if (unlikely(!skb))
 		return;
@@ -146,6 +147,15 @@ static void rmnet_map_send_ack(struct sk_buff *skb,
 	netif_tx_unlock(skb->dev);
 
 	LOGD("MAP command ACK=%hhu sent with rc: %d", type & 0x03, xmit_status);
+
+	if (xmit_status != NETDEV_TX_OK) {
+		rc = dev_queue_xmit(skb);
+		if (rc != 0) {
+			LOGD("Failed to queue packet for transmission on [%s]",
+			     skb->dev->name);
+		}
+	}
+
 }
 
 /* rmnet_map_command() - Entry point for handling MAP commands
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92e..22b3073 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -54,6 +54,7 @@
 source "sound/soc/img/Kconfig"
 source "sound/soc/intel/Kconfig"
 source "sound/soc/mediatek/Kconfig"
+source "sound/soc/msm/Kconfig"
 source "sound/soc/mxs/Kconfig"
 source "sound/soc/pxa/Kconfig"
 source "sound/soc/qcom/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21..1466075 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -31,6 +31,7 @@
 obj-$(CONFIG_SND_SOC)	+= img/
 obj-$(CONFIG_SND_SOC)	+= intel/
 obj-$(CONFIG_SND_SOC)	+= mediatek/
+obj-$(CONFIG_SND_SOC)	+= msm/
 obj-$(CONFIG_SND_SOC)	+= mxs/
 obj-$(CONFIG_SND_SOC)	+= nuc900/
 obj-$(CONFIG_SND_SOC)	+= omap/
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c67667b..9685b02 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -875,6 +875,71 @@
 config SND_SOC_UDA1380
         tristate
 
+config SND_SOC_WCD934X_DSD
+        tristate
+
+config SND_SOC_WCD9320
+        tristate
+
+config SND_SOC_WCD9330
+        tristate
+	depends on WCD9330_CODEC
+
+config SND_SOC_WCD9335
+        tristate
+	depends on WCD9335_CODEC
+
+config SND_SOC_WCD934X
+        tristate
+	depends on WCD934X_CODEC
+	select SND_SOC_WCD9XXX_V2
+	select AUDIO_EXT_CLK
+	select SND_SOC_WCD_DSP_MGR
+	select SND_SOC_WCD_SPI
+	select SND_SOC_WCD934X_MBHC
+        select SND_SOC_WCD934X_DSD
+
+config SND_SOC_WCD934X_MBHC
+        tristate
+	depends on SND_SOC_WCD934X
+	select SND_SOC_WCD_MBHC
+
+config SND_SOC_WSA881X
+        tristate
+	select MSM_CDC_PINCTRL
+	select REGMAP_SWR
+
+config SND_SOC_WSA881X_ANALOG
+        tristate
+	select REGMAP_I2C
+
+config SND_SOC_WCD9XXX
+	tristate
+	default y if SND_SOC_WCD9320=y || SND_SOC_WCD9330=y || SND_SOC_WCD9335=y
+
+config SND_SOC_WCD9XXX_V2
+	tristate
+	default y if SND_SOC_WCD9335=y
+
+config SND_SOC_WCD_CPE
+	tristate
+	default y if SND_SOC_WCD9330=y || SND_SOC_WCD9335=y
+
+config AUDIO_EXT_CLK
+	tristate
+	default y if SND_SOC_WCD9335=y || SND_SOC_WCD9330=y || SND_SOC_MSM8X16_WCD=y
+
+config SND_SOC_WCD_MBHC
+	tristate
+	default y if (SND_SOC_MSM8909_WCD=y || SND_SOC_MSM8X16_WCD=y || SND_SOC_WCD9335=y) && SND_SOC_MDMCALIFORNIUM!=y
+
+config SND_SOC_WCD_DSP_MGR
+	tristate
+
+config SND_SOC_WCD_SPI
+	depends on CONFIG_SPI
+	tristate
+
 config SND_SOC_WL1273
 	tristate
 
@@ -1089,4 +1154,16 @@
 	tristate "Texas Instruments TPA6130A2 headphone amplifier"
 	depends on I2C
 
+config SND_SOC_MSM_STUB
+	tristate
+
+config SND_SOC_MSM_HDMI_CODEC_RX
+	bool "HDMI Audio Playback"
+	depends on FB_MSM_MDSS_HDMI_PANEL && (SND_SOC_APQ8084 || SND_SOC_MSM8994 || SND_SOC_MSM8996 || SND_SOC_MSM8998)
+	help
+	HDMI audio drivers should be built only if the platform
+        supports hdmi panel.
+
+source "sound/soc/codecs/msm8x16/Kconfig"
+
 endmenu
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 958cd49..78db388 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -158,6 +158,27 @@
 snd-soc-twl6040-objs := twl6040.o
 snd-soc-uda134x-objs := uda134x.o
 snd-soc-uda1380-objs := uda1380.o
+snd-soc-wcd9320-objs := wcd9320.o wcd9320-tables.o
+snd-soc-wcd9330-objs := wcd9330.o wcd9330-tables.o
+snd-soc-wcd9335-objs := wcd9335.o
+snd-soc-wcd934x-objs := wcd934x.o
+snd-soc-wcd9xxx-objs := wcd9xxx-resmgr.o wcd9xxx-mbhc.o wcd9xxx-common.o wcdcal-hwdep.o
+snd-soc-wcd9xxx-v2-objs := wcd9xxx-common-v2.o wcd9xxx-resmgr-v2.o wcdcal-hwdep.o
+ifeq ($(CONFIG_COMMON_CLK_MSM), y)
+	audio-ext-clock-objs := audio-ext-clk.o
+endif
+
+ifeq ($(CONFIG_COMMON_CLK_QCOM), y)
+	audio-ext-clock-up-objs := audio-ext-clk-up.o
+endif
+snd-soc-wcd-cpe-objs := wcd_cpe_services.o wcd_cpe_core.o
+snd-soc-wsa881x-objs := wsa881x.o wsa881x-tables.o wsa881x-regmap.o wsa881x-temp-sensor.o
+snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
+snd-soc-wsa881x-analog-objs := wsa881x-analog.o wsa881x-tables-analog.o
+snd-soc-wsa881x-analog-objs += wsa881x-regmap-analog.o wsa881x-irq.o
+snd-soc-wcd-dsp-utils-objs := wcd-dsp-utils.o
+snd-soc-wcd-dsp-mgr-objs := wcd-dsp-mgr.o
+snd-soc-wcd-spi-objs := wcd-spi.o
 snd-soc-wl1273-objs := wl1273.o
 snd-soc-wm-adsp-objs := wm_adsp.o
 snd-soc-wm0010-objs := wm0010.o
@@ -214,6 +235,8 @@
 snd-soc-wm9712-objs := wm9712.o
 snd-soc-wm9713-objs := wm9713.o
 snd-soc-wm-hubs-objs := wm_hubs.o
+snd-soc-msm-stub-objs := msm_stub.o
+obj-$(CONFIG_SND_SOC_MSM_HDMI_CODEC_RX) := msm_hdmi_codec_rx.o
 
 # Amp
 snd-soc-max9877-objs := max9877.o
@@ -379,7 +402,25 @@
 obj-$(CONFIG_SND_SOC_TWL6040)	+= snd-soc-twl6040.o
 obj-$(CONFIG_SND_SOC_UDA134X)	+= snd-soc-uda134x.o
 obj-$(CONFIG_SND_SOC_UDA1380)	+= snd-soc-uda1380.o
+obj-$(CONFIG_SND_SOC_WCD9320)	+= snd-soc-wcd9320.o
+obj-$(CONFIG_SND_SOC_WCD9330)	+= snd-soc-wcd9330.o
+obj-$(CONFIG_SND_SOC_WCD9335)	+= snd-soc-wcd9335.o
+obj-$(CONFIG_SND_SOC_WCD934X)	+= wcd934x/
+ifeq ($(CONFIG_COMMON_CLK_MSM), y)
+	obj-$(CONFIG_AUDIO_EXT_CLK)	+= audio-ext-clock.o
+endif
+ifeq ($(CONFIG_COMMON_CLK_QCOM), y)
+	obj-$(CONFIG_AUDIO_EXT_CLK)     += audio-ext-clock-up.o
+endif
+obj-$(CONFIG_SND_SOC_WCD9XXX)   += snd-soc-wcd9xxx.o
+obj-$(CONFIG_SND_SOC_WCD9XXX_V2) += snd-soc-wcd9xxx-v2.o
+obj-$(CONFIG_SND_SOC_WCD_CPE)   += snd-soc-wcd-cpe.o
+obj-$(CONFIG_SND_SOC_WCD_MBHC)  += snd-soc-wcd-mbhc.o
+obj-$(CONFIG_SND_SOC_WSA881X)	+= snd-soc-wsa881x.o
+obj-$(CONFIG_SND_SOC_WSA881X_ANALOG)	+= snd-soc-wsa881x-analog.o
 obj-$(CONFIG_SND_SOC_WL1273)	+= snd-soc-wl1273.o
+obj-$(CONFIG_SND_SOC_WCD_DSP_MGR)	+= snd-soc-wcd-dsp-mgr.o snd-soc-wcd-dsp-utils.o
+obj-$(CONFIG_SND_SOC_WCD_SPI)  += snd-soc-wcd-spi.o
 obj-$(CONFIG_SND_SOC_WM0010)	+= snd-soc-wm0010.o
 obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
 obj-$(CONFIG_SND_SOC_WM2000)	+= snd-soc-wm2000.o
@@ -435,8 +476,10 @@
 obj-$(CONFIG_SND_SOC_WM9713)	+= snd-soc-wm9713.o
 obj-$(CONFIG_SND_SOC_WM_ADSP)	+= snd-soc-wm-adsp.o
 obj-$(CONFIG_SND_SOC_WM_HUBS)	+= snd-soc-wm-hubs.o
+obj-$(CONFIG_SND_SOC_MSM_STUB)  += snd-soc-msm-stub.o
 
 # Amp
 obj-$(CONFIG_SND_SOC_MAX9877)	+= snd-soc-max9877.o
 obj-$(CONFIG_SND_SOC_MAX98504)	+= snd-soc-max98504.o
 obj-$(CONFIG_SND_SOC_TPA6130A2)	+= snd-soc-tpa6130a2.o
+obj-y += msm8x16/
diff --git a/sound/soc/codecs/audio-ext-clk-up.c b/sound/soc/codecs/audio-ext-clk-up.c
new file mode 100644
index 0000000..626db69
--- /dev/null
+++ b/sound/soc/codecs/audio-ext-clk-up.c
@@ -0,0 +1,577 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include "../../../drivers/clk/qcom/common.h"
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <dt-bindings/clock/qcom,audio-ext-clk.h>
+#include <sound/q6afe-v2.h>
+
+enum audio_clk_mux {
+	AP_CLK2,
+	LPASS_MCLK,
+	LPASS_MCLK2,
+};
+
+struct pinctrl_info {
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *sleep;
+	struct pinctrl_state *active;
+};
+
+struct audio_ext_ap_clk {
+	bool enabled;
+	int gpio;
+	struct clk_fixed_factor fact;
+};
+
+struct audio_ext_pmi_clk {
+	int gpio;
+	struct clk_fixed_factor fact;
+};
+
+struct audio_ext_ap_clk2 {
+	bool enabled;
+	struct pinctrl_info pnctrl_info;
+	struct clk_fixed_factor fact;
+};
+
+struct audio_ext_lpass_mclk {
+	struct pinctrl_info pnctrl_info;
+	struct clk_fixed_factor fact;
+};
+
+static struct afe_clk_set clk2_config = {
+	Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+	Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
+	Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+static struct afe_clk_set lpass_default = {
+	Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+	Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
+	Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+static struct afe_clk_set lpass_mclk = {
+	Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+	Q6AFE_LPASS_CLK_ID_MCLK_1,
+	Q6AFE_LPASS_OSR_CLK_11_P2896_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct audio_ext_ap_clk, fact.hw);
+}
+
+static int audio_ext_clk_prepare(struct clk_hw *hw)
+{
+	struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
+
+	pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+	if (gpio_is_valid(audio_clk->gpio))
+		return gpio_direction_output(audio_clk->gpio, 1);
+	return 0;
+}
+
+static void audio_ext_clk_unprepare(struct clk_hw *hw)
+{
+	struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(hw);
+
+	pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+	if (gpio_is_valid(audio_clk->gpio))
+		gpio_direction_output(audio_clk->gpio, 0);
+}
+
+static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk_hw *hw)
+{
+	return container_of(hw, struct audio_ext_ap_clk2, fact.hw);
+}
+
+static int audio_ext_clk2_prepare(struct clk_hw *hw)
+{
+	struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
+	struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+	int ret;
+
+
+	if (!pnctrl_info->pinctrl || !pnctrl_info->active)
+		return 0;
+
+	ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				   pnctrl_info->active);
+	if (ret) {
+		pr_err("%s: active state select failed with %d\n",
+			__func__, ret);
+		return -EIO;
+	}
+
+	clk2_config.enable = 1;
+	ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+	if (ret < 0) {
+		pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void audio_ext_clk2_unprepare(struct clk_hw *hw)
+{
+	struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(hw);
+	struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+	int ret;
+
+	if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
+		return;
+
+	ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				   pnctrl_info->sleep);
+	if (ret)
+		pr_err("%s: sleep state select failed with %d\n",
+			__func__, ret);
+
+	clk2_config.enable = 0;
+	ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+	if (ret < 0)
+		pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
+}
+
+static inline struct audio_ext_lpass_mclk *to_audio_lpass_mclk(
+						struct clk_hw *hw)
+{
+	return container_of(hw, struct audio_ext_lpass_mclk, fact.hw);
+}
+
+static int audio_ext_lpass_mclk_prepare(struct clk_hw *hw)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
+	struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
+	int ret;
+
+	if (pnctrl_info->pinctrl) {
+		ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				pnctrl_info->active);
+		if (ret) {
+			pr_err("%s: active state select failed with %d\n",
+				__func__, ret);
+			return -EIO;
+		}
+	}
+
+	lpass_mclk.enable = 1;
+	ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
+				&lpass_mclk);
+	if (ret < 0) {
+		pr_err("%s afe_set_digital_codec_core_clock failed\n",
+			__func__);
+		return ret;
+		}
+
+	return 0;
+}
+
+static void audio_ext_lpass_mclk_unprepare(struct clk_hw *hw)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk = to_audio_lpass_mclk(hw);
+	struct pinctrl_info *pnctrl_info = &audio_lpass_mclk->pnctrl_info;
+	int ret;
+
+	if (pnctrl_info->pinctrl) {
+		ret = pinctrl_select_state(pnctrl_info->pinctrl,
+					   pnctrl_info->sleep);
+		if (ret) {
+			pr_err("%s: active state select failed with %d\n",
+				__func__, ret);
+			return;
+		}
+	}
+
+	lpass_mclk.enable = 0;
+	ret = afe_set_lpass_clock_v2(AFE_PORT_ID_PRIMARY_MI2S_RX,
+			&lpass_mclk);
+	if (ret < 0)
+		pr_err("%s: afe_set_digital_codec_core_clock failed, ret = %d\n",
+			__func__, ret);
+}
+
+static int audio_ext_lpass_mclk2_prepare(struct clk_hw *hw)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
+					to_audio_lpass_mclk(hw);
+	struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
+	int ret;
+
+	if (pnctrl_info->pinctrl) {
+		ret = pinctrl_select_state(pnctrl_info->pinctrl,
+					   pnctrl_info->active);
+		if (ret) {
+			pr_err("%s: active state select failed with %d\n",
+				__func__, ret);
+			return -EIO;
+		}
+	}
+
+	lpass_default.enable = 1;
+	ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
+	if (ret < 0) {
+		pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void audio_ext_lpass_mclk2_unprepare(struct clk_hw *hw)
+{
+	struct audio_ext_lpass_mclk *audio_lpass_mclk2 =
+					to_audio_lpass_mclk(hw);
+	struct pinctrl_info *pnctrl_info = &audio_lpass_mclk2->pnctrl_info;
+	int ret;
+
+	if (pnctrl_info->pinctrl) {
+		ret = pinctrl_select_state(pnctrl_info->pinctrl,
+					   pnctrl_info->sleep);
+		if (ret)
+			pr_err("%s: sleep state select failed with %d\n",
+				__func__, ret);
+	}
+
+	lpass_default.enable = 0;
+	ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &lpass_default);
+	if (ret < 0)
+		pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
+}
+
+static const struct clk_ops audio_ext_ap_clk_ops = {
+	.prepare = audio_ext_clk_prepare,
+	.unprepare = audio_ext_clk_unprepare,
+};
+
+static const struct clk_ops audio_ext_ap_clk2_ops = {
+	.prepare = audio_ext_clk2_prepare,
+	.unprepare = audio_ext_clk2_unprepare,
+};
+
+static const struct clk_ops audio_ext_lpass_mclk_ops = {
+	.prepare = audio_ext_lpass_mclk_prepare,
+	.unprepare = audio_ext_lpass_mclk_unprepare,
+};
+
+static const struct clk_ops audio_ext_lpass_mclk2_ops = {
+	.prepare = audio_ext_lpass_mclk2_prepare,
+	.unprepare = audio_ext_lpass_mclk2_unprepare,
+};
+
+static struct audio_ext_pmi_clk audio_pmi_clk = {
+	.gpio = -EINVAL,
+	.fact = {
+		.mult = 1,
+		.div = 1,
+		.hw.init = &(struct clk_init_data){
+			.name = "audio_ext_pmi_clk",
+			.ops = &clk_dummy_ops,
+		},
+	},
+};
+
+static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
+	.gpio = -EINVAL,
+	.fact = {
+		.mult = 1,
+		.div = 1,
+		.hw.init = &(struct clk_init_data){
+			.name = "audio_ext_pmi_lnbb_clk",
+			.ops = &clk_dummy_ops,
+		},
+	},
+};
+
+static struct audio_ext_ap_clk audio_ap_clk = {
+	.gpio = -EINVAL,
+	.fact = {
+		.mult = 1,
+		.div = 1,
+		.hw.init = &(struct clk_init_data){
+			.name = "audio_ap_clk",
+			.ops = &audio_ext_ap_clk_ops,
+		},
+	},
+};
+
+static struct audio_ext_ap_clk2 audio_ap_clk2 = {
+	.enabled = false,
+	.pnctrl_info = {NULL},
+	.fact = {
+		.mult = 1,
+		.div = 1,
+		.hw.init = &(struct clk_init_data){
+			.name = "audio_ap_clk2",
+			.ops = &audio_ext_ap_clk2_ops,
+		},
+	},
+};
+
+static struct audio_ext_lpass_mclk audio_lpass_mclk = {
+	.pnctrl_info = {NULL},
+	.fact = {
+		.mult = 1,
+		.div = 1,
+		.hw.init = &(struct clk_init_data){
+			.name = "audio_lpass_mclk",
+			.ops = &audio_ext_lpass_mclk_ops,
+		},
+	},
+};
+
+static struct audio_ext_lpass_mclk audio_lpass_mclk2 = {
+	.pnctrl_info = {NULL},
+	.fact = {
+		.mult = 1,
+		.div = 1,
+		.hw.init = &(struct clk_init_data){
+			.name = "audio_lpass_mclk2",
+			.ops = &audio_ext_lpass_mclk2_ops,
+		},
+	},
+};
+
+static struct clk_hw *audio_msm_hws[] = {
+	&audio_pmi_clk.fact.hw,
+	&audio_pmi_lnbb_clk.fact.hw,
+	&audio_ap_clk.fact.hw,
+	&audio_ap_clk2.fact.hw,
+	&audio_lpass_mclk.fact.hw,
+	&audio_lpass_mclk2.fact.hw,
+};
+
+static int audio_get_pinctrl(struct platform_device *pdev,
+			     enum audio_clk_mux mux)
+{
+	struct pinctrl_info *pnctrl_info;
+	struct pinctrl *pinctrl;
+	int ret;
+
+	switch (mux) {
+	case AP_CLK2:
+		pnctrl_info = &audio_ap_clk2.pnctrl_info;
+		break;
+	case LPASS_MCLK:
+		pnctrl_info = &audio_lpass_mclk.pnctrl_info;
+		break;
+	case LPASS_MCLK2:
+		pnctrl_info = &audio_lpass_mclk2.pnctrl_info;
+		break;
+	default:
+		dev_err(&pdev->dev, "%s Not a valid MUX ID: %d\n",
+			__func__, mux);
+		return -EINVAL;
+	}
+	pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+	if (pnctrl_info->pinctrl) {
+		dev_dbg(&pdev->dev, "%s: already requested before\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(pinctrl)) {
+		dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n",
+			__func__);
+		return -EINVAL;
+	}
+	pnctrl_info->pinctrl = pinctrl;
+	/* get all state handles from Device Tree */
+	pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
+	if (IS_ERR(pnctrl_info->sleep)) {
+		dev_err(&pdev->dev, "%s: could not get sleep pinstate\n",
+			__func__);
+		goto err;
+	}
+	pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
+	if (IS_ERR(pnctrl_info->active)) {
+		dev_err(&pdev->dev, "%s: could not get active pinstate\n",
+			__func__);
+		goto err;
+	}
+	/* Reset the TLMM pins to a default state */
+	ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				   pnctrl_info->sleep);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n",
+			__func__, ret);
+		goto err;
+	}
+	return 0;
+
+err:
+	devm_pinctrl_put(pnctrl_info->pinctrl);
+	return -EINVAL;
+}
+
+static int audio_ref_clk_probe(struct platform_device *pdev)
+{
+	int clk_gpio;
+	int ret;
+	u32 mclk_freq;
+	struct clk *audio_clk;
+	struct device *dev = &pdev->dev;
+	int i;
+	struct clk_onecell_data *clk_data;
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			"qcom,codec-mclk-clk-freq",
+			&mclk_freq);
+	if (!ret) {
+		lpass_mclk.clk_freq_in_hz = mclk_freq;
+
+		ret = audio_get_pinctrl(pdev, LPASS_MCLK);
+		if (ret)
+			dev_err(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
+				__func__, "LPASS_MCLK");
+		ret = audio_get_pinctrl(pdev, LPASS_MCLK2);
+		if (ret)
+			dev_dbg(&pdev->dev, "%s: Parsing pinctrl %s failed\n",
+				__func__, "LPASS_MCLK2");
+	}
+
+	clk_gpio = of_get_named_gpio(pdev->dev.of_node,
+				     "qcom,audio-ref-clk-gpio", 0);
+	if (clk_gpio > 0) {
+		ret = gpio_request(clk_gpio, "EXT_CLK");
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Request ext clk gpio failed %d, err:%d\n",
+				clk_gpio, ret);
+			goto err;
+		}
+		if (of_property_read_bool(pdev->dev.of_node,
+					"qcom,node_has_rpm_clock")) {
+			audio_pmi_clk.gpio = clk_gpio;
+		} else
+			audio_ap_clk.gpio = clk_gpio;
+
+	}
+
+	ret = audio_get_pinctrl(pdev, AP_CLK2);
+	if (ret)
+		dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
+			__func__);
+
+	clk_data = devm_kzalloc(&pdev->dev, sizeof(*clk_data), GFP_KERNEL);
+	if (!clk_data)
+		goto err_gpio;
+
+	clk_data->clk_num = ARRAY_SIZE(audio_msm_hws);
+	clk_data->clks = devm_kzalloc(&pdev->dev,
+				clk_data->clk_num * sizeof(struct clk *),
+				GFP_KERNEL);
+	if (!clk_data->clks)
+		goto err_clk;
+
+	for (i = 0; i < ARRAY_SIZE(audio_msm_hws); i++) {
+		audio_clk = devm_clk_register(dev, audio_msm_hws[i]);
+		if (IS_ERR(audio_clk)) {
+			dev_err(&pdev->dev,
+				"%s: audio ref clock i = %d register failed\n",
+				__func__, i);
+			return PTR_ERR(audio_clk);
+		}
+		clk_data->clks[i] = audio_clk;
+	}
+
+	ret = of_clk_add_provider(pdev->dev.of_node,
+				of_clk_src_onecell_get, clk_data);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
+			__func__);
+		goto err_gpio;
+	}
+
+	return 0;
+
+err_clk:
+	if (clk_data)
+		devm_kfree(&pdev->dev, clk_data->clks);
+	devm_kfree(&pdev->dev, clk_data);
+err_gpio:
+	gpio_free(clk_gpio);
+
+err:
+	return ret;
+}
+
+static int audio_ref_clk_remove(struct platform_device *pdev)
+{
+	struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+	if (audio_pmi_clk.gpio > 0)
+		gpio_free(audio_pmi_clk.gpio);
+	else if (audio_ap_clk.gpio > 0)
+		gpio_free(audio_ap_clk.gpio);
+
+	if (pnctrl_info->pinctrl) {
+		devm_pinctrl_put(pnctrl_info->pinctrl);
+		pnctrl_info->pinctrl = NULL;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id audio_ref_clk_match[] = {
+	{.compatible = "qcom,audio-ref-clk"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
+
+static struct platform_driver audio_ref_clk_driver = {
+	.driver = {
+		.name = "audio-ref-clk",
+		.owner = THIS_MODULE,
+		.of_match_table = audio_ref_clk_match,
+	},
+	.probe = audio_ref_clk_probe,
+	.remove = audio_ref_clk_remove,
+};
+
+static int __init audio_ref_clk_platform_init(void)
+{
+	return platform_driver_register(&audio_ref_clk_driver);
+}
+module_init(audio_ref_clk_platform_init);
+
+static void __exit audio_ref_clk_platform_exit(void)
+{
+	platform_driver_unregister(&audio_ref_clk_driver);
+}
+module_exit(audio_ref_clk_platform_exit);
+
+MODULE_DESCRIPTION("Audio Ref Up Clock module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/audio-ext-clk.c b/sound/soc/codecs/audio-ext-clk.c
new file mode 100644
index 0000000..ef795df
--- /dev/null
+++ b/sound/soc/codecs/audio-ext-clk.c
@@ -0,0 +1,349 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/clk/msm-clk-provider.h>
+#include <linux/clk/msm-clk.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <dt-bindings/clock/audio-ext-clk.h>
+#include <sound/q6afe-v2.h>
+
+struct pinctrl_info {
+	struct pinctrl *pinctrl;
+	struct pinctrl_state *sleep;
+	struct pinctrl_state *active;
+};
+
+struct audio_ext_ap_clk {
+	bool enabled;
+	int gpio;
+	struct clk c;
+};
+
+struct audio_ext_pmi_clk {
+	int gpio;
+	struct clk c;
+};
+
+struct audio_ext_ap_clk2 {
+	bool enabled;
+	struct pinctrl_info pnctrl_info;
+	struct clk c;
+};
+
+static struct afe_clk_set clk2_config = {
+	Q6AFE_LPASS_CLK_CONFIG_API_VERSION,
+	Q6AFE_LPASS_CLK_ID_SPEAKER_I2S_OSR,
+	Q6AFE_LPASS_IBIT_CLK_11_P2896_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+static inline struct audio_ext_ap_clk *to_audio_ap_clk(struct clk *clk)
+{
+	return container_of(clk, struct audio_ext_ap_clk, c);
+}
+
+static int audio_ext_clk_prepare(struct clk *clk)
+{
+	struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk);
+
+	pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+	if (gpio_is_valid(audio_clk->gpio))
+		return gpio_direction_output(audio_clk->gpio, 1);
+	return 0;
+}
+
+static void audio_ext_clk_unprepare(struct clk *clk)
+{
+	struct audio_ext_ap_clk *audio_clk = to_audio_ap_clk(clk);
+
+	pr_debug("%s: gpio: %d\n", __func__, audio_clk->gpio);
+	if (gpio_is_valid(audio_clk->gpio))
+		gpio_direction_output(audio_clk->gpio, 0);
+}
+
+static inline struct audio_ext_ap_clk2 *to_audio_ap_clk2(struct clk *clk)
+{
+	return container_of(clk, struct audio_ext_ap_clk2, c);
+}
+
+static int audio_ext_clk2_prepare(struct clk *clk)
+{
+	struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk);
+	struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+	int ret;
+
+
+	if (!pnctrl_info->pinctrl || !pnctrl_info->active)
+		return 0;
+
+	ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				   pnctrl_info->active);
+	if (ret) {
+		pr_err("%s: active state select failed with %d\n",
+			__func__, ret);
+		return -EIO;
+	}
+
+	clk2_config.enable = 1;
+	ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+	if (ret < 0) {
+		pr_err("%s: failed to set clock, ret = %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void audio_ext_clk2_unprepare(struct clk *clk)
+{
+	struct audio_ext_ap_clk2 *audio_clk2 = to_audio_ap_clk2(clk);
+	struct pinctrl_info *pnctrl_info = &audio_clk2->pnctrl_info;
+	int ret;
+
+	if (!pnctrl_info->pinctrl || !pnctrl_info->sleep)
+		return;
+
+	ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				   pnctrl_info->sleep);
+	if (ret)
+		pr_err("%s: sleep state select failed with %d\n",
+			__func__, ret);
+
+	clk2_config.enable = 0;
+	ret = afe_set_lpass_clk_cfg(IDX_RSVD_3, &clk2_config);
+	if (ret < 0)
+		pr_err("%s: failed to reset clock, ret = %d\n", __func__, ret);
+}
+
+static const struct clk_ops audio_ext_ap_clk_ops = {
+	.prepare = audio_ext_clk_prepare,
+	.unprepare = audio_ext_clk_unprepare,
+};
+
+static const struct clk_ops audio_ext_ap_clk2_ops = {
+	.prepare = audio_ext_clk2_prepare,
+	.unprepare = audio_ext_clk2_unprepare,
+};
+
+static struct audio_ext_pmi_clk audio_pmi_clk = {
+	.gpio = -EINVAL,
+	.c = {
+		.dbg_name = "audio_ext_pmi_clk",
+		.ops = &clk_ops_dummy,
+		CLK_INIT(audio_pmi_clk.c),
+	},
+};
+
+static struct audio_ext_pmi_clk audio_pmi_lnbb_clk = {
+	.gpio = -EINVAL,
+	.c = {
+		.dbg_name = "audio_ext_pmi_lnbb_clk",
+		.ops = &clk_ops_dummy,
+		CLK_INIT(audio_pmi_lnbb_clk.c),
+	},
+};
+
+static struct audio_ext_ap_clk audio_ap_clk = {
+	.gpio = -EINVAL,
+	.c = {
+		.dbg_name = "audio_ext_ap_clk",
+		.ops = &audio_ext_ap_clk_ops,
+		CLK_INIT(audio_ap_clk.c),
+	},
+};
+
+static struct audio_ext_ap_clk2 audio_ap_clk2 = {
+	.c = {
+		.dbg_name = "audio_ext_ap_clk2",
+		.ops = &audio_ext_ap_clk2_ops,
+		CLK_INIT(audio_ap_clk2.c),
+	},
+};
+
+static struct clk_lookup audio_ref_clock[] = {
+	CLK_LIST(audio_ap_clk),
+	CLK_LIST(audio_pmi_clk),
+	CLK_LIST(audio_pmi_lnbb_clk),
+	CLK_LIST(audio_ap_clk2),
+};
+
+static int audio_get_pinctrl(struct platform_device *pdev)
+{
+	struct pinctrl_info *pnctrl_info;
+	struct pinctrl *pinctrl;
+	int ret;
+
+	pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+	if (pnctrl_info->pinctrl) {
+		dev_dbg(&pdev->dev, "%s: already requested before\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	pinctrl = devm_pinctrl_get(&pdev->dev);
+	if (IS_ERR_OR_NULL(pinctrl)) {
+		dev_dbg(&pdev->dev, "%s: Unable to get pinctrl handle\n",
+			__func__);
+		return -EINVAL;
+	}
+	pnctrl_info->pinctrl = pinctrl;
+	/* get all state handles from Device Tree */
+	pnctrl_info->sleep = pinctrl_lookup_state(pinctrl, "sleep");
+	if (IS_ERR(pnctrl_info->sleep)) {
+		dev_err(&pdev->dev, "%s: could not get sleep pinstate\n",
+			__func__);
+		goto err;
+	}
+	pnctrl_info->active = pinctrl_lookup_state(pinctrl, "active");
+	if (IS_ERR(pnctrl_info->active)) {
+		dev_err(&pdev->dev, "%s: could not get active pinstate\n",
+			__func__);
+		goto err;
+	}
+	/* Reset the TLMM pins to a default state */
+	ret = pinctrl_select_state(pnctrl_info->pinctrl,
+				   pnctrl_info->sleep);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: Disable TLMM pins failed with %d\n",
+			__func__, ret);
+		goto err;
+	}
+	return 0;
+
+err:
+	devm_pinctrl_put(pnctrl_info->pinctrl);
+	return -EINVAL;
+}
+
+static int audio_ref_clk_probe(struct platform_device *pdev)
+{
+	int clk_gpio;
+	int ret;
+	struct clk *audio_clk;
+
+	clk_gpio = of_get_named_gpio(pdev->dev.of_node,
+				     "qcom,audio-ref-clk-gpio", 0);
+	if (clk_gpio > 0) {
+		ret = gpio_request(clk_gpio, "EXT_CLK");
+		if (ret) {
+			dev_err(&pdev->dev,
+				"Request ext clk gpio failed %d, err:%d\n",
+				clk_gpio, ret);
+			goto err;
+		}
+		if (of_property_read_bool(pdev->dev.of_node,
+					"qcom,node_has_rpm_clock")) {
+			audio_clk = clk_get(&pdev->dev, NULL);
+			if (IS_ERR(audio_clk)) {
+				dev_err(&pdev->dev, "Failed to get RPM div clk\n");
+				ret = PTR_ERR(audio_clk);
+				goto err_gpio;
+			}
+			audio_pmi_clk.c.parent = audio_clk;
+			audio_pmi_clk.gpio = clk_gpio;
+		} else
+			audio_ap_clk.gpio = clk_gpio;
+
+	} else {
+		if (of_property_read_bool(pdev->dev.of_node,
+					"qcom,node_has_rpm_clock")) {
+			audio_clk = clk_get(&pdev->dev, NULL);
+			if (IS_ERR(audio_clk)) {
+				dev_err(&pdev->dev, "Failed to get lnbbclk2\n");
+				ret = PTR_ERR(audio_clk);
+				goto err;
+			}
+			audio_pmi_lnbb_clk.c.parent = audio_clk;
+			audio_pmi_lnbb_clk.gpio = -EINVAL;
+		}
+	}
+
+	ret = audio_get_pinctrl(pdev);
+	if (ret)
+		dev_dbg(&pdev->dev, "%s: Parsing pinctrl failed\n",
+			__func__);
+
+	ret = of_msm_clock_register(pdev->dev.of_node, audio_ref_clock,
+			      ARRAY_SIZE(audio_ref_clock));
+	if (ret) {
+		dev_err(&pdev->dev, "%s: audio ref clock register failed\n",
+			__func__);
+		goto err_gpio;
+	}
+
+	return 0;
+
+err_gpio:
+	gpio_free(clk_gpio);
+
+err:
+	return ret;
+}
+
+static int audio_ref_clk_remove(struct platform_device *pdev)
+{
+	struct pinctrl_info *pnctrl_info = &audio_ap_clk2.pnctrl_info;
+
+	if (audio_pmi_clk.gpio > 0)
+		gpio_free(audio_pmi_clk.gpio);
+	else if (audio_ap_clk.gpio > 0)
+		gpio_free(audio_ap_clk.gpio);
+
+	if (pnctrl_info->pinctrl) {
+		devm_pinctrl_put(pnctrl_info->pinctrl);
+		pnctrl_info->pinctrl = NULL;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id audio_ref_clk_match[] = {
+	{.compatible = "qcom,audio-ref-clk"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, audio_ref_clk_match);
+
+static struct platform_driver audio_ref_clk_driver = {
+	.driver = {
+		.name = "audio-ref-clk",
+		.owner = THIS_MODULE,
+		.of_match_table = audio_ref_clk_match,
+	},
+	.probe = audio_ref_clk_probe,
+	.remove = audio_ref_clk_remove,
+};
+
+static int __init audio_ref_clk_platform_init(void)
+{
+	return platform_driver_register(&audio_ref_clk_driver);
+}
+module_init(audio_ref_clk_platform_init);
+
+static void __exit audio_ref_clk_platform_exit(void)
+{
+	platform_driver_unregister(&audio_ref_clk_driver);
+}
+module_exit(audio_ref_clk_platform_exit);
+
+MODULE_DESCRIPTION("Audio Ref Clock module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm8x16/Kconfig b/sound/soc/codecs/msm8x16/Kconfig
new file mode 100644
index 0000000..d225b7a
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/Kconfig
@@ -0,0 +1,3 @@
+
+config SND_SOC_MSM8X16_WCD
+	tristate "MSM Internal PMIC based codec"
diff --git a/sound/soc/codecs/msm8x16/Makefile b/sound/soc/codecs/msm8x16/Makefile
new file mode 100644
index 0000000..1e4522f
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/Makefile
@@ -0,0 +1,2 @@
+snd-soc-msm8952-wcd-objs := msm8x16-wcd.o msm8x16-wcd-tables.o msm89xx-regmap.o
+obj-$(CONFIG_SND_SOC_MSM8X16_WCD)      += snd-soc-msm8952-wcd.o msm8916-wcd-irq.o
diff --git a/sound/soc/codecs/msm8x16/msm8916-wcd-irq.c b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.c
new file mode 100644
index 0000000..a722842
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.c
@@ -0,0 +1,441 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/interrupt.h>
+#include <linux/pm_qos.h>
+#include <soc/qcom/pm.h>
+#include <sound/soc.h>
+#include "msm8x16-wcd.h"
+#include "msm8916-wcd-irq.h"
+#include "msm8x16_wcd_registers.h"
+
+#define MAX_NUM_IRQS 14
+#define NUM_IRQ_REGS 2
+#define WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS 700
+
+#define BYTE_BIT_MASK(nr) (1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr) ((nr) / BITS_PER_BYTE)
+
+static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data);
+
+char *irq_names[MAX_NUM_IRQS] = {
+	"spk_cnp_int",
+	"spk_clip_int",
+	"spk_ocp_int",
+	"ins_rem_det1",
+	"but_rel_det",
+	"but_press_det",
+	"ins_rem_det",
+	"mbhc_int",
+	"ear_ocp_int",
+	"hphr_ocp_int",
+	"hphl_ocp_det",
+	"ear_cnp_int",
+	"hphr_cnp_int",
+	"hphl_cnp_int"
+};
+
+int order[MAX_NUM_IRQS] = {
+	MSM89XX_IRQ_SPKR_CNP,
+	MSM89XX_IRQ_SPKR_CLIP,
+	MSM89XX_IRQ_SPKR_OCP,
+	MSM89XX_IRQ_MBHC_INSREM_DET1,
+	MSM89XX_IRQ_MBHC_RELEASE,
+	MSM89XX_IRQ_MBHC_PRESS,
+	MSM89XX_IRQ_MBHC_INSREM_DET,
+	MSM89XX_IRQ_MBHC_HS_DET,
+	MSM89XX_IRQ_EAR_OCP,
+	MSM89XX_IRQ_HPHR_OCP,
+	MSM89XX_IRQ_HPHL_OCP,
+	MSM89XX_IRQ_EAR_CNP,
+	MSM89XX_IRQ_HPHR_CNP,
+	MSM89XX_IRQ_HPHL_CNP,
+};
+
+enum wcd9xxx_spmi_pm_state {
+	WCD9XXX_PM_SLEEPABLE,
+	WCD9XXX_PM_AWAKE,
+	WCD9XXX_PM_ASLEEP,
+};
+
+struct wcd9xxx_spmi_map {
+	uint8_t handled[NUM_IRQ_REGS];
+	uint8_t mask[NUM_IRQ_REGS];
+	int linuxirq[MAX_NUM_IRQS];
+	irq_handler_t handler[MAX_NUM_IRQS];
+	struct spmi_device *spmi[NUM_IRQ_REGS];
+	struct snd_soc_codec *codec;
+
+	enum wcd9xxx_spmi_pm_state pm_state;
+	struct mutex pm_lock;
+	/* pm_wq notifies change of pm_state */
+	wait_queue_head_t pm_wq;
+	struct pm_qos_request pm_qos_req;
+	int wlock_holders;
+};
+
+struct wcd9xxx_spmi_map map;
+
+void wcd9xxx_spmi_enable_irq(int irq)
+{
+	pr_debug("%s: irqno =%d\n", __func__, irq);
+	if ((irq >= 0) && (irq <= 7)) {
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_DIGITAL_INT_EN_CLR,
+				(0x01 << irq), 0x00);
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_DIGITAL_INT_EN_SET,
+				(0x01 << irq), (0x01 << irq));
+	}
+	if ((irq > 7) && (irq <= 15)) {
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_ANALOG_INT_EN_CLR,
+				(0x01 << (irq - 8)), 0x00);
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_ANALOG_INT_EN_SET,
+				(0x01 << (irq - 8)), (0x01 << (irq - 8)));
+	}
+
+	if (!(map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq))))
+		return;
+
+	map.mask[BIT_BYTE(irq)] &=
+		~(BYTE_BIT_MASK(irq));
+
+	enable_irq(map.linuxirq[irq]);
+}
+
+void wcd9xxx_spmi_disable_irq(int irq)
+{
+	pr_debug("%s: irqno =%d\n", __func__, irq);
+	if ((irq >= 0) && (irq <= 7)) {
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_DIGITAL_INT_EN_SET,
+				(0x01 << (irq)), 0x00);
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_DIGITAL_INT_EN_CLR,
+				(0x01 << irq), (0x01 << irq));
+	}
+
+	if ((irq > 7) && (irq <= 15)) {
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_ANALOG_INT_EN_SET,
+				(0x01 << (irq - 8)), 0x00);
+		snd_soc_update_bits(map.codec,
+				MSM89XX_PMIC_ANALOG_INT_EN_CLR,
+				(0x01 << (irq - 8)), (0x01 << (irq - 8)));
+	}
+
+	if (map.mask[BIT_BYTE(irq)] & (BYTE_BIT_MASK(irq)))
+		return;
+
+	map.mask[BIT_BYTE(irq)] |=
+		(BYTE_BIT_MASK(irq));
+
+	disable_irq_nosync(map.linuxirq[irq]);
+}
+
+int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler,
+			const char *name, void *priv)
+{
+	int rc;
+	unsigned long irq_flags;
+
+	if (strcmp(name, "mbhc sw intr"))
+		irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			IRQF_ONESHOT;
+	else
+		irq_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+			IRQF_ONESHOT | IRQF_NO_SUSPEND;
+	pr_debug("%s: name:%s irq_flags = %lx\n", __func__, name, irq_flags);
+
+	rc = devm_request_threaded_irq(&map.spmi[BIT_BYTE(irq)]->dev,
+				map.linuxirq[irq], NULL,
+				wcd9xxx_spmi_irq_handler,
+				irq_flags,
+				name, priv);
+		if (rc < 0) {
+			dev_err(&map.spmi[BIT_BYTE(irq)]->dev,
+				"Can't request %d IRQ\n", irq);
+			return rc;
+		}
+
+	dev_dbg(&map.spmi[BIT_BYTE(irq)]->dev,
+			"irq %d linuxIRQ: %d\n", irq, map.linuxirq[irq]);
+	map.mask[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq);
+	map.handler[irq] = handler;
+	enable_irq_wake(map.linuxirq[irq]);
+	return 0;
+}
+
+int wcd9xxx_spmi_free_irq(int irq, void *priv)
+{
+	devm_free_irq(&map.spmi[BIT_BYTE(irq)]->dev, map.linuxirq[irq],
+						priv);
+	map.mask[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq);
+	return 0;
+}
+
+static int get_irq_bit(int linux_irq)
+{
+	int i = 0;
+
+	for (; i < MAX_NUM_IRQS; i++)
+		if (map.linuxirq[i] == linux_irq)
+			return i;
+
+	return i;
+}
+
+static int get_order_irq(int  i)
+{
+	return order[i];
+}
+
+static irqreturn_t wcd9xxx_spmi_irq_handler(int linux_irq, void *data)
+{
+	int irq, i, j;
+	unsigned long status[NUM_IRQ_REGS] = {0};
+
+	if (unlikely(wcd9xxx_spmi_lock_sleep() == false)) {
+		pr_err("Failed to hold suspend\n");
+		return IRQ_NONE;
+	}
+
+	irq = get_irq_bit(linux_irq);
+	if (irq == MAX_NUM_IRQS)
+		return IRQ_HANDLED;
+
+	status[BIT_BYTE(irq)] |= BYTE_BIT_MASK(irq);
+	for (i = 0; i < NUM_IRQ_REGS; i++) {
+		status[i] |= snd_soc_read(map.codec,
+				BIT_BYTE(irq) * 0x100 +
+			MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS);
+		status[i] &= ~map.mask[i];
+	}
+	for (i = 0; i < MAX_NUM_IRQS; i++) {
+		j = get_order_irq(i);
+		if ((status[BIT_BYTE(j)] & BYTE_BIT_MASK(j)) &&
+			((map.handled[BIT_BYTE(j)] &
+			BYTE_BIT_MASK(j)) == 0)) {
+			map.handler[j](irq, data);
+			map.handled[BIT_BYTE(j)] |=
+					BYTE_BIT_MASK(j);
+		}
+	}
+	map.handled[BIT_BYTE(irq)] &= ~BYTE_BIT_MASK(irq);
+	wcd9xxx_spmi_unlock_sleep();
+
+	return IRQ_HANDLED;
+}
+
+enum wcd9xxx_spmi_pm_state wcd9xxx_spmi_pm_cmpxchg(
+		enum wcd9xxx_spmi_pm_state o,
+		enum wcd9xxx_spmi_pm_state n)
+{
+	enum wcd9xxx_spmi_pm_state old;
+
+	mutex_lock(&map.pm_lock);
+	old = map.pm_state;
+	if (old == o)
+		map.pm_state = n;
+	pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
+	mutex_unlock(&map.pm_lock);
+	return old;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_pm_cmpxchg);
+
+int wcd9xxx_spmi_suspend(pm_message_t pmesg)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	/*
+	 * pm_qos_update_request() can be called after this suspend chain call
+	 * started. thus suspend can be called while lock is being held
+	 */
+	mutex_lock(&map.pm_lock);
+	if (map.pm_state == WCD9XXX_PM_SLEEPABLE) {
+		pr_debug("%s: suspending system, state %d, wlock %d\n",
+			 __func__, map.pm_state,
+			 map.wlock_holders);
+		map.pm_state = WCD9XXX_PM_ASLEEP;
+	} else if (map.pm_state == WCD9XXX_PM_AWAKE) {
+		/*
+		 * unlock to wait for pm_state == WCD9XXX_PM_SLEEPABLE
+		 * then set to WCD9XXX_PM_ASLEEP
+		 */
+		pr_debug("%s: waiting to suspend system, state %d, wlock %d\n",
+			 __func__, map.pm_state,
+			 map.wlock_holders);
+		mutex_unlock(&map.pm_lock);
+		if (!(wait_event_timeout(map.pm_wq,
+					 wcd9xxx_spmi_pm_cmpxchg(
+							WCD9XXX_PM_SLEEPABLE,
+							WCD9XXX_PM_ASLEEP) ==
+							WCD9XXX_PM_SLEEPABLE,
+							HZ))) {
+			pr_debug("%s: suspend failed state %d, wlock %d\n",
+				 __func__, map.pm_state,
+				 map.wlock_holders);
+			ret = -EBUSY;
+		} else {
+			pr_debug("%s: done, state %d, wlock %d\n", __func__,
+				 map.pm_state,
+				 map.wlock_holders);
+		}
+		mutex_lock(&map.pm_lock);
+	} else if (map.pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_warn("%s: system is already suspended, state %d, wlock %dn",
+			__func__, map.pm_state,
+			map.wlock_holders);
+	}
+	mutex_unlock(&map.pm_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_suspend);
+
+int wcd9xxx_spmi_resume(void)
+{
+	int ret = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	mutex_lock(&map.pm_lock);
+	if (map.pm_state == WCD9XXX_PM_ASLEEP) {
+		pr_debug("%s: resuming system, state %d, wlock %d\n", __func__,
+				map.pm_state,
+				map.wlock_holders);
+		map.pm_state = WCD9XXX_PM_SLEEPABLE;
+	} else {
+		pr_warn("%s: system is already awake, state %d wlock %d\n",
+				__func__, map.pm_state,
+				map.wlock_holders);
+	}
+	mutex_unlock(&map.pm_lock);
+	wake_up_all(&map.pm_wq);
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_resume);
+
+bool wcd9xxx_spmi_lock_sleep(void)
+{
+	/*
+	 * wcd9xxx_spmi_{lock/unlock}_sleep will be called by
+	 * wcd9xxx_spmi_irq_thread
+	 * and its subroutines only motly.
+	 * but btn0_lpress_fn is not wcd9xxx_spmi_irq_thread's subroutine and
+	 * It can race with wcd9xxx_spmi_irq_thread.
+	 * So need to embrace wlock_holders with mutex.
+	 */
+	mutex_lock(&map.pm_lock);
+	if (map.wlock_holders++ == 0) {
+		pr_debug("%s: holding wake lock\n", __func__);
+		pm_qos_update_request(&map.pm_qos_req,
+				      msm_cpuidle_get_deep_idle_latency());
+		pm_stay_awake(&map.spmi[0]->dev);
+	}
+	mutex_unlock(&map.pm_lock);
+	pr_debug("%s: wake lock counter %d\n", __func__,
+			map.wlock_holders);
+	pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
+
+	if (!wait_event_timeout(map.pm_wq,
+				((wcd9xxx_spmi_pm_cmpxchg(
+					WCD9XXX_PM_SLEEPABLE,
+					WCD9XXX_PM_AWAKE)) ==
+					WCD9XXX_PM_SLEEPABLE ||
+					(wcd9xxx_spmi_pm_cmpxchg(
+						 WCD9XXX_PM_SLEEPABLE,
+						 WCD9XXX_PM_AWAKE) ==
+						 WCD9XXX_PM_AWAKE)),
+					msecs_to_jiffies(
+					WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS))) {
+		pr_warn("%s: system didn't resume within %dms, s %d, w %d\n",
+			__func__,
+			WCD9XXX_SYSTEM_RESUME_TIMEOUT_MS, map.pm_state,
+			map.wlock_holders);
+		wcd9xxx_spmi_unlock_sleep();
+		return false;
+	}
+	wake_up_all(&map.pm_wq);
+	pr_debug("%s: leaving pm_state = %d\n", __func__, map.pm_state);
+	return true;
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_lock_sleep);
+
+void wcd9xxx_spmi_unlock_sleep(void)
+{
+	mutex_lock(&map.pm_lock);
+	if (--map.wlock_holders == 0) {
+		pr_debug("%s: releasing wake lock pm_state %d -> %d\n",
+			 __func__, map.pm_state, WCD9XXX_PM_SLEEPABLE);
+		/*
+		 * if wcd9xxx_spmi_lock_sleep failed, pm_state would be still
+		 * WCD9XXX_PM_ASLEEP, don't overwrite
+		 */
+		if (likely(map.pm_state == WCD9XXX_PM_AWAKE))
+			map.pm_state = WCD9XXX_PM_SLEEPABLE;
+		pm_qos_update_request(&map.pm_qos_req,
+				PM_QOS_DEFAULT_VALUE);
+		pm_relax(&map.spmi[0]->dev);
+	}
+	mutex_unlock(&map.pm_lock);
+	pr_debug("%s: wake lock counter %d\n", __func__,
+			map.wlock_holders);
+	pr_debug("%s: map.pm_state = %d\n", __func__, map.pm_state);
+	wake_up_all(&map.pm_wq);
+}
+EXPORT_SYMBOL(wcd9xxx_spmi_unlock_sleep);
+
+void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec)
+{
+	map.codec = codec;
+}
+
+void wcd9xxx_spmi_set_dev(struct spmi_device *spmi, int i)
+{
+	if (i < NUM_IRQ_REGS)
+		map.spmi[i] = spmi;
+}
+
+int wcd9xxx_spmi_irq_init(void)
+{
+	int i = 0;
+
+	for (; i < MAX_NUM_IRQS; i++)
+		map.mask[BIT_BYTE(i)] |= BYTE_BIT_MASK(i);
+	mutex_init(&map.pm_lock);
+	map.wlock_holders = 0;
+	map.pm_state = WCD9XXX_PM_SLEEPABLE;
+	init_waitqueue_head(&map.pm_wq);
+	pm_qos_add_request(&map.pm_qos_req,
+				PM_QOS_CPU_DMA_LATENCY,
+				PM_QOS_DEFAULT_VALUE);
+
+	return 0;
+}
+
+MODULE_DESCRIPTION("MSM8x16 SPMI IRQ driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm8x16/msm8916-wcd-irq.h b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.h
new file mode 100644
index 0000000..3862865
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm8916-wcd-irq.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_SPMI_IRQ_H__
+#define __WCD9XXX_SPMI_IRQ_H__
+
+#include <sound/soc.h>
+#include <linux/spmi.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/pm_qos.h>
+
+extern void wcd9xxx_spmi_enable_irq(int irq);
+extern void wcd9xxx_spmi_disable_irq(int irq);
+extern int wcd9xxx_spmi_request_irq(int irq, irq_handler_t handler,
+				const char *name, void *priv);
+extern int wcd9xxx_spmi_free_irq(int irq, void *priv);
+extern void wcd9xxx_spmi_set_codec(struct snd_soc_codec *codec);
+extern void wcd9xxx_spmi_set_dev(struct spmi_device *spmi, int i);
+extern int wcd9xxx_spmi_irq_init(void);
+extern int wcd9xxx_spmi_suspend(pm_message_t pmesg);
+extern int wcd9xxx_spmi_resume(void);
+bool wcd9xxx_spmi_lock_sleep(void);
+void wcd9xxx_spmi_unlock_sleep(void);
+
+#endif
diff --git a/sound/soc/codecs/msm8x16/msm89xx-regmap.c b/sound/soc/codecs/msm8x16/msm89xx-regmap.c
new file mode 100644
index 0000000..007b74c
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm89xx-regmap.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "msm8x16-wcd.h"
+
+/*
+ * Default register reset values that are common across different versions
+ * are defined here. If a register reset value is changed based on version
+ * then remove it from this structure and add it in version specific
+ * structures.
+ */
+static struct reg_default
+	msm89xx_cdc_core_defaults[MSM89XX_CDC_CORE_CACHE_SIZE] = {
+	{MSM89XX_CDC_CORE_CLK_RX_RESET_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x13},
+	{MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 0x13},
+	{MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_OTHR_CTL, 0x04},
+	{MSM89XX_CDC_CORE_CLK_RX_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_SD_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_B5_CTL, 0x68},
+	{MSM89XX_CDC_CORE_RX2_B5_CTL, 0x68},
+	{MSM89XX_CDC_CORE_RX3_B5_CTL, 0x68},
+	{MSM89XX_CDC_CORE_RX1_B6_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_B6_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_B6_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TOP_GAIN_UPDATE, 0x00},
+	{MSM89XX_CDC_CORE_TOP_CTL, 0x01},
+	{MSM89XX_CDC_CORE_COMP0_B1_CTL, 0x30},
+	{MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xB5},
+	{MSM89XX_CDC_CORE_COMP0_B3_CTL, 0x28},
+	{MSM89XX_CDC_CORE_COMP0_B4_CTL, 0x37},
+	{MSM89XX_CDC_CORE_COMP0_B5_CTL, 0x7F},
+	{MSM89XX_CDC_CORE_COMP0_B6_CTL, 0x00},
+	{MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS, 0x03},
+	{MSM89XX_CDC_CORE_COMP0_FS_CFG, 0x03},
+	{MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL, 0x02},
+	{MSM89XX_CDC_CORE_DEBUG_DESER1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_DEBUG_DESER2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_CTL, 0x40},
+	{MSM89XX_CDC_CORE_IIR2_CTL, 0x40},
+	{MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX1_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX1_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX1_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX2_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX2_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX2_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX3_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_RX3_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_TX_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL, 0x00},
+	{MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER, 0x00},
+	{MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER, 0x00},
+	{MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER, 0x00},
+	{MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER, 0x00},
+	{MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN, 0x00},
+	{MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN, 0x00},
+	{MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN, 0x00},
+	{MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN, 0x00},
+	{MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG, 0x00},
+	{MSM89XX_CDC_CORE_TX1_MUX_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX2_MUX_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX3_MUX_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX4_MUX_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX1_CLK_FS_CTL, 0x03},
+	{MSM89XX_CDC_CORE_TX2_CLK_FS_CTL, 0x03},
+	{MSM89XX_CDC_CORE_TX3_CLK_FS_CTL, 0x03},
+	{MSM89XX_CDC_CORE_TX4_CLK_FS_CTL, 0x03},
+	{MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX3_DMIC_CTL, 0x00},
+	{MSM89XX_CDC_CORE_TX4_DMIC_CTL, 0x00},
+};
+
+static struct reg_default
+	msm89xx_pmic_cdc_defaults[MSM89XX_PMIC_CDC_CACHE_SIZE] = {
+	{MSM89XX_PMIC_DIGITAL_REVISION1, 0x00},
+	{MSM89XX_PMIC_DIGITAL_REVISION2, 0x00},
+	{MSM89XX_PMIC_DIGITAL_PERPH_TYPE, 0x23},
+	{MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE, 0x01},
+	{MSM89XX_PMIC_DIGITAL_INT_RT_STS, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_SET_TYPE, 0xFF},
+	{MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH, 0xFF},
+	{MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_EN_SET, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_EN_CLR, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_PENDING_STS, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_MID_SEL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_PRIORITY, 0x00},
+	{MSM89XX_PMIC_DIGITAL_GPIO_MODE, 0x00},
+	{MSM89XX_PMIC_DIGITAL_PIN_CTL_OE, 0x01},
+	{MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA, 0x00},
+	{MSM89XX_PMIC_DIGITAL_PIN_STATUS, 0x00},
+	{MSM89XX_PMIC_DIGITAL_HDRIVE_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL, 0x02},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL, 0x02},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1, 0x7C},
+	{MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2, 0x7C},
+	{MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3, 0x7C},
+	{MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0, 0x00},
+	{MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1, 0x00},
+	{MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2, 0x00},
+	{MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3, 0x00},
+	{MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN, 0x00},
+	{MSM89XX_PMIC_DIGITAL_SPARE_0, 0x00},
+	{MSM89XX_PMIC_DIGITAL_SPARE_1, 0x00},
+	{MSM89XX_PMIC_DIGITAL_SPARE_2, 0x00},
+	{MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0x00},
+	{MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1, 0x00},
+	{MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2, 0x02},
+	{MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x05},
+	{MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_TEST1, 0x00},
+	{MSM89XX_PMIC_DIGITAL_INT_TEST_VAL, 0x00},
+	{MSM89XX_PMIC_DIGITAL_TRIM_NUM, 0x00},
+	{MSM89XX_PMIC_DIGITAL_TRIM_CTRL, 0x00},
+	{MSM89XX_PMIC_ANALOG_REVISION1, 0x00},
+	{MSM89XX_PMIC_ANALOG_REVISION2, 0x00},
+	{MSM89XX_PMIC_ANALOG_REVISION3, 0x00},
+	{MSM89XX_PMIC_ANALOG_REVISION4, 0x00},
+	{MSM89XX_PMIC_ANALOG_PERPH_TYPE, 0x23},
+	{MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x09},
+	{MSM89XX_PMIC_ANALOG_INT_RT_STS, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_SET_TYPE, 0x3F},
+	{MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH, 0x3F},
+	{MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_EN_SET, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_EN_CLR, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_LATCHED_STS, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_PENDING_STS, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_MID_SEL, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_PRIORITY, 0x00},
+	{MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x00},
+	{MSM89XX_PMIC_ANALOG_MICB_1_VAL, 0x20},
+	{MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS, 0x49},
+	{MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20},
+	{MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2, 0x00},
+	{MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x35},
+	{MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08},
+	{MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x98},
+	{MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL, 0x20},
+	{MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, 0x40},
+	{MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x61},
+	{MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL, 0x80},
+	{MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0x00},
+	{MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x00},
+	{MSM89XX_PMIC_ANALOG_TX_1_EN, 0x03},
+	{MSM89XX_PMIC_ANALOG_TX_2_EN, 0x03},
+	{MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1, 0xBF},
+	{MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2, 0x8C},
+	{MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x6B},
+	{MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0x51},
+	{MSM89XX_PMIC_ANALOG_TX_3_EN, 0x02},
+	{MSM89XX_PMIC_ANALOG_NCP_EN, 0x26},
+	{MSM89XX_PMIC_ANALOG_NCP_CLK, 0x23},
+	{MSM89XX_PMIC_ANALOG_NCP_DEGLITCH, 0x5B},
+	{MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x08},
+	{MSM89XX_PMIC_ANALOG_NCP_BIAS, 0x29},
+	{MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0x24},
+	{MSM89XX_PMIC_ANALOG_NCP_TEST, 0x00},
+	{MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR, 0xD5},
+	{MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER, 0xE8},
+	{MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xCF},
+	{MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0x6E},
+	{MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC, 0x18},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0x5A},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP, 0x69},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP, 0x29},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x80},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL, 0xDA},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0x16},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x00},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x00},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20},
+	{MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12},
+	{MSM89XX_PMIC_ANALOG_RX_ATEST, 0x00},
+	{MSM89XX_PMIC_ANALOG_RX_HPH_STATUS, 0x0C},
+	{MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x00},
+	{MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x00},
+	{MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x83},
+	{MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET, 0x91},
+	{MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x29},
+	{MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x4D},
+	{MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1},
+	{MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x1E},
+	{MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC, 0xCB},
+	{MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x00},
+	{MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x02},
+	{MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE, 0x14},
+	{MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x00},
+	{MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x1F},
+	{MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x8C},
+	{MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE, 0xC0},
+	{MSM89XX_PMIC_ANALOG_BOOST_TEST1_1, 0x00},
+	{MSM89XX_PMIC_ANALOG_BOOST_TEST_2, 0x00},
+	{MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS, 0x00},
+	{MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS, 0x00},
+	{MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR, 0x00},
+	{MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL, 0x00},
+	{MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0x00},
+	{MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1, 0x00},
+	{MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2, 0x01},
+	{MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x05},
+	{MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_TEST1, 0x00},
+	{MSM89XX_PMIC_ANALOG_INT_TEST_VAL, 0x00},
+	{MSM89XX_PMIC_ANALOG_TRIM_NUM, 0x04},
+	{MSM89XX_PMIC_ANALOG_TRIM_CTRL1, 0x00},
+	{MSM89XX_PMIC_ANALOG_TRIM_CTRL2, 0x00},
+	{MSM89XX_PMIC_ANALOG_TRIM_CTRL3, 0x00},
+	{MSM89XX_PMIC_ANALOG_TRIM_CTRL4, 0x00},
+};
+
+static bool msm89xx_cdc_core_readable_reg(struct device *dev, unsigned int reg)
+{
+	return msm89xx_cdc_core_reg_readable[reg];
+}
+
+static bool msm89xx_pmic_cdc_readable_reg(struct device *dev, unsigned int reg)
+{
+	return msm89xx_pmic_cdc_reg_readable[reg];
+}
+
+static bool msm89xx_cdc_core_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MSM89XX_CDC_CORE_RX1_B1_CTL:
+	case MSM89XX_CDC_CORE_RX2_B1_CTL:
+	case MSM89XX_CDC_CORE_RX3_B1_CTL:
+	case MSM89XX_CDC_CORE_RX1_B6_CTL:
+	case MSM89XX_CDC_CORE_RX2_B6_CTL:
+	case MSM89XX_CDC_CORE_RX3_B6_CTL:
+	case MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG:
+	case MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG:
+	case MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL:
+	case MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL:
+	case MSM89XX_CDC_CORE_CLK_MCLK_CTL:
+	case MSM89XX_CDC_CORE_CLK_PDM_CTL:
+	case MSM89XX_PMIC_ANALOG_BYPASS_MODE:
+	case MSM89XX_PMIC_ANALOG_BOOST_EN_CTL:
+	case MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL:
+	case MSM89XX_PMIC_ANALOG_CURRENT_LIMIT:
+	case MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL:
+	case MSM89XX_PMIC_ANALOG_NCP_FBCTRL:
+	case MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1:
+		return true;
+	default:
+		return false;
+	}
+}
+
+static bool msm89xx_pmic_cdc_volatile_reg(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case MSM89XX_PMIC_DIGITAL_REVISION1:
+	case MSM89XX_PMIC_DIGITAL_REVISION2:
+	case MSM89XX_PMIC_DIGITAL_PERPH_TYPE:
+	case MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE:
+	case MSM89XX_PMIC_DIGITAL_INT_RT_STS:
+	case MSM89XX_PMIC_DIGITAL_INT_SET_TYPE:
+	case MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH:
+	case MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW:
+	case MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS:
+	case MSM89XX_PMIC_DIGITAL_INT_PENDING_STS:
+	case MSM89XX_PMIC_DIGITAL_PIN_STATUS:
+	case MSM89XX_PMIC_DIGITAL_SEC_ACCESS:
+	case MSM89XX_PMIC_ANALOG_SEC_ACCESS:
+	case MSM89XX_PMIC_ANALOG_REVISION1:
+	case MSM89XX_PMIC_ANALOG_REVISION2:
+	case MSM89XX_PMIC_ANALOG_REVISION3:
+	case MSM89XX_PMIC_ANALOG_REVISION4:
+	case MSM89XX_PMIC_ANALOG_PERPH_TYPE:
+	case MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE:
+	case MSM89XX_PMIC_ANALOG_INT_RT_STS:
+	case MSM89XX_PMIC_ANALOG_INT_SET_TYPE:
+	case MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH:
+	case MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW:
+	case MSM89XX_PMIC_ANALOG_INT_LATCHED_STS:
+	case MSM89XX_PMIC_ANALOG_INT_PENDING_STS:
+	case MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT:
+	case MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT:
+	case MSM89XX_PMIC_ANALOG_RX_HPH_STATUS:
+	case MSM89XX_PMIC_ANALOG_RX_EAR_STATUS:
+	case MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS:
+	case MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS:
+		return true;
+	default:
+		return false;
+	}
+}
+
+struct regmap_config msm89xx_pmic_cdc_regmap_config = {
+	.reg_bits	= 16,
+	.val_bits	= 8,
+	.max_register	= MSM89XX_PMIC_CDC_CACHE_SIZE,
+	.fast_io	= true,
+	.reg_defaults = msm89xx_pmic_cdc_defaults,
+	.num_reg_defaults = ARRAY_SIZE(msm89xx_pmic_cdc_defaults),
+	.readable_reg = msm89xx_pmic_cdc_readable_reg,
+	.volatile_reg = msm89xx_pmic_cdc_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian = REGMAP_ENDIAN_NATIVE,
+	.can_multi_write = true,
+	.lock = enable_digital_callback,
+	.unlock = disable_digital_callback,
+
+};
+
+struct regmap_config msm89xx_cdc_core_regmap_config = {
+	.reg_bits = 32,
+	.reg_stride = 4,
+	.val_bits = 32,
+
+	.max_register = MSM89XX_CDC_CORE_CACHE_SIZE,
+	.reg_defaults = msm89xx_cdc_core_defaults,
+	.num_reg_defaults = ARRAY_SIZE(msm89xx_cdc_core_defaults),
+	.readable_reg = msm89xx_cdc_core_readable_reg,
+	.volatile_reg = msm89xx_cdc_core_volatile_reg,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian = REGMAP_ENDIAN_NATIVE,
+	.can_multi_write = true,
+};
diff --git a/sound/soc/codecs/msm8x16/msm8x16-wcd-tables.c b/sound/soc/codecs/msm8x16/msm8x16-wcd-tables.c
new file mode 100644
index 0000000..b969639
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm8x16-wcd-tables.c
@@ -0,0 +1,263 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include "msm8x16-wcd.h"
+
+const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE] = {
+		[MSM89XX_PMIC_DIGITAL_REVISION1] = 1,
+		[MSM89XX_PMIC_DIGITAL_REVISION2] = 1,
+		[MSM89XX_PMIC_DIGITAL_PERPH_TYPE] = 1,
+		[MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_RT_STS] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_SET_TYPE] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_EN_SET] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_EN_CLR] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_PENDING_STS] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_MID_SEL] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_PRIORITY] = 1,
+		[MSM89XX_PMIC_DIGITAL_GPIO_MODE] = 1,
+		[MSM89XX_PMIC_DIGITAL_PIN_CTL_OE] = 1,
+		[MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA] = 1,
+		[MSM89XX_PMIC_DIGITAL_PIN_STATUS] = 1,
+		[MSM89XX_PMIC_DIGITAL_HDRIVE_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_RST_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2] = 1,
+		[MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3] = 1,
+		[MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0] = 1,
+		[MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1] = 1,
+		[MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2] = 1,
+		[MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3] = 1,
+		[MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_SPARE_0] = 1,
+		[MSM89XX_PMIC_DIGITAL_SPARE_1] = 1,
+		[MSM89XX_PMIC_DIGITAL_SPARE_2] = 1,
+		[MSM89XX_PMIC_ANALOG_REVISION1] = 1,
+		[MSM89XX_PMIC_ANALOG_REVISION2] = 1,
+		[MSM89XX_PMIC_ANALOG_REVISION3] = 1,
+		[MSM89XX_PMIC_ANALOG_REVISION4] = 1,
+		[MSM89XX_PMIC_ANALOG_PERPH_TYPE] = 1,
+		[MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_RT_STS] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_SET_TYPE] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_EN_SET] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_EN_CLR] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_LATCHED_STS] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_PENDING_STS] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_MID_SEL] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_PRIORITY] = 1,
+		[MSM89XX_PMIC_ANALOG_MICB_1_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_MICB_1_VAL] = 1,
+		[MSM89XX_PMIC_ANALOG_MICB_1_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS] = 1,
+		[MSM89XX_PMIC_ANALOG_MICB_2_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT] = 1,
+		[MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_2_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV] = 1,
+		[MSM89XX_PMIC_ANALOG_TX_3_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_CLK] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_DEGLITCH] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_FBCTRL] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_BIAS] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_VCTRL] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_TEST] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_EAR_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_ATEST] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_HPH_STATUS] = 1,
+		[MSM89XX_PMIC_ANALOG_RX_EAR_STATUS] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG] = 1,
+		[MSM89XX_PMIC_ANALOG_CURRENT_LIMIT] = 1,
+		[MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE] = 1,
+		[MSM89XX_PMIC_ANALOG_BYPASS_MODE] = 1,
+		[MSM89XX_PMIC_ANALOG_BOOST_EN_CTL] = 1,
+		[MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO] = 1,
+		[MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE] = 1,
+		[MSM89XX_PMIC_ANALOG_BOOST_TEST1_1] = 1,
+		[MSM89XX_PMIC_ANALOG_BOOST_TEST_2] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS] = 1,
+		[MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS] = 1,
+		[MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR] = 1,
+		[MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL] = 1,
+		[MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL] = 1,
+		[MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR] = 1,
+		[MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR] = 1,
+		[MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR] = 1,
+		[MSM89XX_PMIC_DIGITAL_SEC_ACCESS] = 1,
+		[MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3] = 1,
+		[MSM89XX_PMIC_ANALOG_SEC_ACCESS] = 1,
+};
+
+const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE] = {
+		[MSM89XX_CDC_CORE_CLK_RX_RESET_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_RX_I2S_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_TX_I2S_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_OTHR_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_RX_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_MCLK_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_PDM_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_SD_CTL] = 1,
+		[MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_B5_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_B5_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_B5_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_B6_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_B6_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_B6_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_TOP_GAIN_UPDATE] = 1,
+		[MSM89XX_CDC_CORE_TOP_CTL] = 1,
+		[MSM89XX_CDC_CORE_DEBUG_DESER1_CTL] = 1,
+		[MSM89XX_CDC_CORE_DEBUG_DESER2_CTL] = 1,
+		[MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX1_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX1_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX1_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX2_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX2_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX2_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX3_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_RX3_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_TX_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL] = 1,
+		[MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER] = 1,
+		[MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER] = 1,
+		[MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER] = 1,
+		[MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER] = 1,
+		[MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN] = 1,
+		[MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN] = 1,
+		[MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN] = 1,
+		[MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN] = 1,
+		[MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG] = 1,
+		[MSM89XX_CDC_CORE_TX1_MUX_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX2_MUX_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX3_MUX_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX4_MUX_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX1_CLK_FS_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX2_CLK_FS_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX3_CLK_FS_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX4_CLK_FS_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX1_DMIC_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX2_DMIC_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX3_DMIC_CTL] = 1,
+		[MSM89XX_CDC_CORE_TX4_DMIC_CTL] = 1,
+};
diff --git a/sound/soc/codecs/msm8x16/msm8x16-wcd.c b/sound/soc/codecs/msm8x16/msm8x16-wcd.c
new file mode 100644
index 0000000..f76dde7
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm8x16-wcd.c
@@ -0,0 +1,6022 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/spmi.h>
+#include <linux/of_gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/qdsp6v2/apr.h>
+#include <linux/timer.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <sound/q6afe-v2.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/q6core.h>
+#include <soc/qcom/subsystem_notif.h>
+#include "../../msm/msmfalcon-common.h"
+#include "../wcd-mbhc-v2.h"
+#include "msm8916-wcd-irq.h"
+#include "msm8x16-wcd.h"
+
+#define DRV_NAME "msm-codec"
+#define MSM89XX_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000)
+#define MSM89XX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+		SNDRV_PCM_FMTBIT_S24_LE)
+
+#define NUM_INTERPOLATORS	3
+#define BITS_PER_REG		8
+#define MSM89XX_TX_PORT_NUMBER	4
+
+#define MSM89XX_I2S_MASTER_MODE_MASK	0x08
+#define MSM89XX_DIGITAL_CODEC_BASE_ADDR		0x771C000
+#define PMIC_SLAVE_ID_0		0
+#define PMIC_SLAVE_ID_1		1
+
+#define PMIC_MBG_OK		0x2C08
+#define PMIC_LDO7_EN_CTL	0x4646
+#define MASK_MSB_BIT		0x80
+
+#define CODEC_DT_MAX_PROP_SIZE			40
+#define MSM89XX_DIGITAL_CODEC_REG_SIZE		0x400
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH	64
+
+#define MCLK_RATE_9P6MHZ	9600000
+#define MCLK_RATE_12P288MHZ	12288000
+
+#define BUS_DOWN 1
+
+/*
+ *50 Milliseconds sufficient for DSP bring up in the modem
+ * after Sub System Restart
+ */
+#define ADSP_STATE_READY_TIMEOUT_MS 50
+
+#define HPHL_PA_DISABLE (0x01 << 1)
+#define HPHR_PA_DISABLE (0x01 << 2)
+#define EAR_PA_DISABLE (0x01 << 3)
+#define SPKR_PA_DISABLE (0x01 << 4)
+
+enum {
+	BOOST_SWITCH = 0,
+	BOOST_ALWAYS,
+	BYPASS_ALWAYS,
+	BOOST_ON_FOREVER,
+};
+
+#define EAR_PMD 0
+#define EAR_PMU 1
+#define SPK_PMD 2
+#define SPK_PMU 3
+
+#define MICBIAS_DEFAULT_VAL 1800000
+#define MICBIAS_MIN_VAL 1600000
+#define MICBIAS_STEP_SIZE 50000
+
+#define DEFAULT_BOOST_VOLTAGE 5000
+#define MIN_BOOST_VOLTAGE 4000
+#define MAX_BOOST_VOLTAGE 5550
+#define BOOST_VOLTAGE_STEP 50
+
+#define MSM89XX_MBHC_BTN_COARSE_ADJ  100 /* in mV */
+#define MSM89XX_MBHC_BTN_FINE_ADJ 12 /* in mV */
+
+#define VOLTAGE_CONVERTER(value, min_value, step_size)\
+	((value - min_value)/step_size)
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_VIFEED,
+	NUM_CODEC_DAIS,
+};
+
+enum {
+	RX_MIX1_INP_SEL_ZERO = 0,
+	RX_MIX1_INP_SEL_IIR1,
+	RX_MIX1_INP_SEL_IIR2,
+	RX_MIX1_INP_SEL_RX1,
+	RX_MIX1_INP_SEL_RX2,
+	RX_MIX1_INP_SEL_RX3,
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[];
+/* By default enable the internal speaker boost */
+static bool spkr_boost_en = true;
+
+#define MSM89XX_ACQUIRE_LOCK(x) \
+	mutex_lock_nested(&x, SINGLE_DEPTH_NESTING)
+
+#define MSM89XX_RELEASE_LOCK(x) mutex_unlock(&x)
+
+
+/* Codec supports 2 IIR filters */
+enum {
+	IIR1 = 0,
+	IIR2,
+	IIR_MAX,
+};
+
+/* Codec supports 5 bands */
+enum {
+	BAND1 = 0,
+	BAND2,
+	BAND3,
+	BAND4,
+	BAND5,
+	BAND_MAX,
+};
+
+struct hpf_work {
+	struct msm8x16_wcd_priv *msm8x16_wcd;
+	u32 decimator;
+	u8 tx_hpf_cut_of_freq;
+	struct delayed_work dwork;
+};
+
+static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+	"cdc-vdd-mic-bias",
+};
+
+static unsigned long rx_digital_gain_reg[] = {
+	MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL,
+	MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL,
+	MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL,
+};
+
+static unsigned long tx_digital_gain_reg[] = {
+	MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN,
+	MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN,
+};
+
+enum {
+	MSM89XX_SPMI_DIGITAL = 0,
+	MSM89XX_SPMI_ANALOG,
+	MSM89XX_CODEC_CORE,
+	MAX_MSM89XX_DEVICE
+};
+
+static struct wcd_mbhc_register
+	wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = {
+
+	WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x18, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+			  MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, 0x06, 1, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+			  MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+			  MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0xF0, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+			  MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, 0x0C, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+			  MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+			  MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x01,
+			  0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+			  MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x02,
+			  1, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+			  MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x08,
+			  3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+			  MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0x04,
+			  2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+			  MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+			  MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT, 0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+			  MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, 0x70, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+			  MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT, 0xFF,
+			  0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+			  MSM89XX_PMIC_ANALOG_MICB_2_EN, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+			  MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFC, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+			  MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+			  MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+			  MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN, 0x30, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+			  MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT,
+			  0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_PULLDOWN_CTRL",
+			  MSM89XX_PMIC_ANALOG_MICB_2_EN, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ANC_DET_EN",
+			  0, 0, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+			  0, 0, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+			  0, 0, 0, 0),
+};
+
+struct msm8x16_wcd_spmi {
+	struct spmi_device *spmi;
+	int base;
+};
+
+/* Multiply gain_adj and offset by 1000 and 100 to avoid float arithmetic */
+static const struct wcd_imped_i_ref imped_i_ref[] = {
+	{I_h4_UA, 8, 800, 9000, 10000},
+	{I_pt5_UA, 10, 100, 990, 4600},
+	{I_14_UA, 17, 14, 1050, 700},
+	{I_l4_UA, 10, 4, 1165, 110},
+	{I_1_UA, 0, 1, 1200, 65},
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+	.mbhc_sw_intr =  MSM89XX_IRQ_MBHC_HS_DET,
+	.mbhc_btn_press_intr = MSM89XX_IRQ_MBHC_PRESS,
+	.mbhc_btn_release_intr = MSM89XX_IRQ_MBHC_RELEASE,
+	.mbhc_hs_ins_intr = MSM89XX_IRQ_MBHC_INSREM_DET1,
+	.mbhc_hs_rem_intr = MSM89XX_IRQ_MBHC_INSREM_DET,
+	.hph_left_ocp = MSM89XX_IRQ_HPHL_OCP,
+	.hph_right_ocp = MSM89XX_IRQ_HPHR_OCP,
+};
+
+static int msm_digcdc_clock_control(bool flag);
+static int msm8x16_wcd_dt_parse_vreg_info(struct device *dev,
+	struct msm8x16_wcd_regulator *vreg,
+	const char *vreg_name, bool ondemand);
+static struct msm8x16_wcd_pdata *msm8x16_wcd_populate_dt_pdata(
+	struct device *dev);
+static int msm8x16_wcd_enable_ext_mb_source(struct wcd_mbhc *mbhc,
+					    bool turn_on);
+static void msm8x16_trim_btn_reg(struct snd_soc_codec *codec);
+static void msm8x16_wcd_set_micb_v(struct snd_soc_codec *codec);
+static void msm8x16_wcd_set_boost_v(struct snd_soc_codec *codec);
+static void msm8x16_wcd_set_auto_zeroing(struct snd_soc_codec *codec,
+		bool enable);
+static void msm8x16_wcd_configure_cap(struct snd_soc_codec *codec,
+		bool micbias1, bool micbias2);
+static bool msm8x16_wcd_use_mb(struct snd_soc_codec *codec);
+
+struct msm8x16_wcd_spmi msm8x16_wcd_modules[MAX_MSM89XX_DEVICE];
+
+static void *adsp_state_notifier;
+
+static struct snd_soc_codec *registered_codec;
+static struct snd_soc_codec *registered_digcodec;
+
+static int get_codec_version(struct msm8x16_wcd_priv *msm8x16_wcd)
+{
+	if (msm8x16_wcd->codec_version == DIANGU)
+		return DIANGU;
+	else if (msm8x16_wcd->codec_version == CAJON_2_0)
+		return CAJON_2_0;
+	else if (msm8x16_wcd->codec_version == CAJON)
+		return CAJON;
+	else if (msm8x16_wcd->codec_version == CONGA)
+		return CONGA;
+	else if (msm8x16_wcd->pmic_rev == TOMBAK_2_0)
+		return TOMBAK_2_0;
+	else if (msm8x16_wcd->pmic_rev == TOMBAK_1_0)
+		return TOMBAK_1_0;
+
+	pr_err("%s: unsupported codec version\n", __func__);
+	return UNSUPPORTED;
+}
+
+static int msm_digcdc_clock_control(bool flag)
+{
+	int ret = -EINVAL;
+	struct msm_asoc_mach_data *pdata = NULL;
+
+	pdata = snd_soc_card_get_drvdata(registered_codec->component.card);
+
+	if (flag) {
+		mutex_lock(&pdata->cdc_int_mclk0_mutex);
+		if (atomic_read(&pdata->int_mclk0_enabled) == false) {
+			pdata->digital_cdc_core_clk.enable = 1;
+			ret = afe_set_lpass_clock_v2(
+					AFE_PORT_ID_INT0_MI2S_RX,
+					&pdata->digital_cdc_core_clk);
+			if (ret < 0) {
+				pr_err("failed to enable the INT_MCLK0\n");
+				goto err_mclk;
+			}
+			pr_err("enabled digital codec core clk\n");
+			atomic_set(&pdata->int_mclk0_enabled, true);
+			schedule_delayed_work(&pdata->disable_int_mclk0_work,
+					      50);
+		}
+err_mclk:
+		mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+		return ret;
+	}
+	return 0;
+}
+
+void enable_digital_callback(void *flag)
+{
+	msm_digcdc_clock_control(true);
+}
+
+void disable_digital_callback(void *flag)
+{
+	msm_digcdc_clock_control(false);
+}
+
+static int snd_soc_read_wrapper(struct snd_soc_codec *codec, u16 reg)
+{
+	int ret = -EINVAL;
+	struct msm8x16_wcd *msm8x16_wcd = codec->control_data;
+
+	pr_err("%s reg = %x\n", __func__, reg);
+	mutex_lock(&msm8x16_wcd->io_lock);
+	if (MSM89XX_IS_PMIC_CDC_REG(reg))
+		ret = snd_soc_read(codec, reg);
+	else if (MSM89XX_IS_CDC_CORE_REG(reg))
+		ret = snd_soc_read(registered_digcodec, reg);
+	mutex_unlock(&msm8x16_wcd->io_lock);
+
+	return ret;
+}
+
+static int snd_soc_write_wrapper(struct snd_soc_codec *codec, u16 reg, u8 val)
+{
+	int ret = -EINVAL;
+	struct msm8x16_wcd *msm8x16_wcd = codec->control_data;
+
+	pr_err("%s reg = %x\n", __func__, reg);
+	mutex_lock(&msm8x16_wcd->io_lock);
+	if (MSM89XX_IS_PMIC_CDC_REG(reg))
+		ret = snd_soc_write(codec, reg, val);
+	else if (MSM89XX_IS_CDC_CORE_REG(reg))
+		ret = snd_soc_write(registered_digcodec, reg, val);
+	mutex_unlock(&msm8x16_wcd->io_lock);
+
+	return ret;
+}
+
+static int snd_soc_update_bits_wrapper(struct snd_soc_codec *codec,
+			u16 reg, u8 mask, u8 val)
+{
+	int ret = -EINVAL;
+	struct msm8x16_wcd *msm8x16_wcd = codec->control_data;
+
+	pr_err("%s reg = %x\n", __func__, reg);
+	mutex_lock(&msm8x16_wcd->io_lock);
+	if (MSM89XX_IS_PMIC_CDC_REG(reg))
+		ret = snd_soc_update_bits(codec, reg, mask, val);
+	else if (MSM89XX_IS_CDC_CORE_REG(reg))
+		ret = snd_soc_update_bits(registered_digcodec, reg, mask, val);
+	mutex_unlock(&msm8x16_wcd->io_lock);
+
+	return ret;
+}
+
+static void wcd_mbhc_meas_imped(struct snd_soc_codec *codec,
+				s16 *impedance_l, s16 *impedance_r)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if ((msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_BOTH) ||
+		(msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHL)) {
+		/* Enable ZDET_L_MEAS_EN */
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+				0x08, 0x08);
+		/* Wait for 2ms for measurement to complete */
+		usleep_range(2000, 2100);
+		/* Read Left impedance value from Result1 */
+		*impedance_l = snd_soc_read_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+		/* Enable ZDET_R_MEAS_EN */
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+				0x08, 0x00);
+	}
+	if ((msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_BOTH) ||
+		(msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR)) {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x04, 0x04);
+		/* Wait for 2ms for measurement to complete */
+		usleep_range(2000, 2100);
+		/* Read Right impedance value from Result1 */
+		*impedance_r = snd_soc_read_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+				0x04, 0x00);
+	}
+}
+
+static void msm8x16_set_ref_current(struct snd_soc_codec *codec,
+				enum wcd_curr_ref curr_ref)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	pr_err("%s: curr_ref: %d\n", __func__, curr_ref);
+
+	if (get_codec_version(msm8x16_wcd) < CAJON)
+		pr_err("%s: Setting ref current not required\n", __func__);
+
+	msm8x16_wcd->imped_i_ref = imped_i_ref[curr_ref];
+
+	switch (curr_ref) {
+	case I_h4_UA:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN,
+			0x07, 0x01);
+		break;
+	case I_pt5_UA:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN,
+			0x07, 0x04);
+		break;
+	case I_14_UA:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN,
+			0x07, 0x03);
+		break;
+	case I_l4_UA:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN,
+			0x07, 0x01);
+		break;
+	case I_1_UA:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN,
+			0x07, 0x00);
+		break;
+	default:
+		pr_err("%s: No ref current set\n", __func__);
+		break;
+	}
+}
+
+static bool msm8x16_adj_ref_current(struct snd_soc_codec *codec,
+					s16 *impedance_l, s16 *impedance_r)
+{
+	int i = 2;
+	s16 compare_imp = 0;
+
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR)
+		compare_imp = *impedance_r;
+	else
+		compare_imp = *impedance_l;
+
+	if (get_codec_version(msm8x16_wcd) < CAJON) {
+		pr_err("%s: Reference current adjustment not required\n",
+			 __func__);
+		return false;
+	}
+
+	while (compare_imp < imped_i_ref[i].min_val) {
+		msm8x16_set_ref_current(codec,
+					imped_i_ref[++i].curr_ref);
+		wcd_mbhc_meas_imped(codec,
+				impedance_l, impedance_r);
+		compare_imp = (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR)
+				? *impedance_r : *impedance_l;
+	}
+
+	return true;
+}
+
+void msm8x16_wcd_spk_ext_pa_cb(
+		int (*codec_spk_ext_pa)(struct snd_soc_codec *codec,
+			int enable), struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	pr_err("%s: Enter\n", __func__);
+	msm8x16_wcd->codec_spk_ext_pa_cb = codec_spk_ext_pa;
+}
+
+void msm8x16_wcd_hph_comp_cb(
+	int (*codec_hph_comp_gpio)(bool enable), struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	pr_err("%s: Enter\n", __func__);
+	msm8x16_wcd->codec_hph_comp_gpio = codec_hph_comp_gpio;
+}
+
+static void msm8x16_wcd_compute_impedance(struct snd_soc_codec *codec, s16 l,
+				s16 r, uint32_t *zl, uint32_t *zr, bool high)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	uint32_t rl = 0, rr = 0;
+	struct wcd_imped_i_ref R = msm8x16_wcd->imped_i_ref;
+	int codec_ver = get_codec_version(msm8x16_wcd);
+
+	switch (codec_ver) {
+	case TOMBAK_1_0:
+	case TOMBAK_2_0:
+	case CONGA:
+		if (high) {
+			pr_err("%s: This plug has high range impedance\n",
+				 __func__);
+			rl = (uint32_t)(((100 * (l * 400 - 200))/96) - 230);
+			rr = (uint32_t)(((100 * (r * 400 - 200))/96) - 230);
+		} else {
+			pr_err("%s: This plug has low range impedance\n",
+				 __func__);
+			rl = (uint32_t)(((1000 * (l * 2 - 1))/1165) - (13/10));
+			rr = (uint32_t)(((1000 * (r * 2 - 1))/1165) - (13/10));
+		}
+		break;
+	case CAJON:
+	case CAJON_2_0:
+	case DIANGU:
+		if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHL) {
+			rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) -
+			   (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+			rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5)))
+			      - R.offset * R.gain_adj)/(R.gain_adj * 100));
+		} else if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR) {
+			rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5)))
+			      - R.offset * R.gain_adj)/(R.gain_adj * 100));
+			rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))-
+			   (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+		} else if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_NONE) {
+			rr = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * r - 5)) -
+			   (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+			rl = (uint32_t)(((DEFAULT_MULTIPLIER * (10 * l - 5))-
+			   (DEFAULT_OFFSET * DEFAULT_GAIN))/DEFAULT_GAIN);
+		} else {
+			rr = (uint32_t)(((10000 * (R.multiplier * (10 * r - 5)))
+			      - R.offset * R.gain_adj)/(R.gain_adj * 100));
+			rl = (uint32_t)(((10000 * (R.multiplier * (10 * l - 5)))
+			      - R.offset * R.gain_adj)/(R.gain_adj * 100));
+		}
+		break;
+	default:
+		pr_err("%s: No codec mentioned\n", __func__);
+		break;
+	}
+	*zl = rl;
+	*zr = rr;
+}
+
+static struct firmware_cal *msm8x16_wcd_get_hwdep_fw_cal(
+		struct wcd_mbhc *mbhc,
+		enum wcd_cal_type type)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd;
+	struct firmware_cal *hwdep_cal;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer\n", __func__);
+		return NULL;
+	}
+	msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	hwdep_cal = wcdcal_get_fw_cal(msm8x16_wcd->fw_data, type);
+	if (!hwdep_cal) {
+		dev_err(codec->dev, "%s: cal not sent by %d\n",
+				__func__, type);
+		return NULL;
+	}
+	return hwdep_cal;
+}
+
+static void wcd9xxx_spmi_irq_control(struct snd_soc_codec *codec,
+				     int irq, bool enable)
+{
+	if (enable)
+		wcd9xxx_spmi_enable_irq(irq);
+	else
+		wcd9xxx_spmi_disable_irq(irq);
+}
+
+static void msm8x16_mbhc_clk_setup(struct snd_soc_codec *codec,
+				   bool enable)
+{
+	if (enable)
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+				0x08, 0x08);
+	else
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+				0x08, 0x00);
+}
+
+static int msm8x16_mbhc_map_btn_code_to_num(struct snd_soc_codec *codec)
+{
+	int btn_code;
+	int btn;
+
+	btn_code = snd_soc_read_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+
+	switch (btn_code) {
+	case 0:
+		btn = 0;
+		break;
+	case 1:
+		btn = 1;
+		break;
+	case 3:
+		btn = 2;
+		break;
+	case 7:
+		btn = 3;
+		break;
+	case 15:
+		btn = 4;
+		break;
+	default:
+		btn = -EINVAL;
+		break;
+	};
+
+	return btn;
+}
+
+static bool msm8x16_spmi_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+	if (lock)
+		return wcd9xxx_spmi_lock_sleep();
+	wcd9xxx_spmi_unlock_sleep();
+	return 0;
+}
+
+static bool msm8x16_wcd_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+	if (micb_num == MIC_BIAS_1)
+		return (snd_soc_read_wrapper(mbhc->codec,
+				     MSM89XX_PMIC_ANALOG_MICB_1_EN) &
+			0x80);
+	if (micb_num == MIC_BIAS_2)
+		return (snd_soc_read_wrapper(mbhc->codec,
+				     MSM89XX_PMIC_ANALOG_MICB_2_EN) &
+			0x80);
+	return false;
+}
+
+static void msm8x16_wcd_enable_master_bias(struct snd_soc_codec *codec,
+					   bool enable)
+{
+	if (enable)
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL,
+			0x30, 0x30);
+	else
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL,
+			0x30, 0x00);
+}
+
+static void msm8x16_wcd_mbhc_common_micb_ctrl(struct snd_soc_codec *codec,
+					      int event, bool enable)
+{
+	u16 reg;
+	u8 mask;
+	u8 val;
+
+	switch (event) {
+	case MBHC_COMMON_MICB_PRECHARGE:
+		reg = MSM89XX_PMIC_ANALOG_MICB_1_CTL;
+		mask = 0x60;
+		val = (enable ? 0x60 : 0x00);
+		break;
+	case MBHC_COMMON_MICB_SET_VAL:
+		reg = MSM89XX_PMIC_ANALOG_MICB_1_VAL;
+		mask = 0xFF;
+		val = (enable ? 0xC0 : 0x00);
+		break;
+	case MBHC_COMMON_MICB_TAIL_CURR:
+		reg = MSM89XX_PMIC_ANALOG_MICB_1_EN;
+		mask = 0x04;
+		val = (enable ? 0x04 : 0x00);
+		break;
+	};
+	snd_soc_update_bits_wrapper(codec, reg, mask, val);
+}
+
+static void msm8x16_wcd_mbhc_internal_micbias_ctrl(struct snd_soc_codec *codec,
+						   int micbias_num, bool enable)
+{
+	if (micbias_num == 1) {
+		if (enable)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS,
+				0x10, 0x10);
+		else
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS,
+				0x10, 0x00);
+	}
+}
+
+static bool msm8x16_wcd_mbhc_hph_pa_on_status(struct snd_soc_codec *codec)
+{
+	return (snd_soc_read_wrapper(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN) &
+		0x30) ? true : false;
+}
+
+static void msm8x16_wcd_mbhc_program_btn_thr(struct snd_soc_codec *codec,
+					     s16 *btn_low, s16 *btn_high,
+					     int num_btn, bool is_micbias)
+{
+	int i;
+	u32 course, fine, reg_val;
+	u16 reg_addr = MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL;
+	s16 *btn_voltage;
+
+	btn_voltage = ((is_micbias) ? btn_high : btn_low);
+
+	for (i = 0; i <  num_btn; i++) {
+		course = (btn_voltage[i] / MSM89XX_MBHC_BTN_COARSE_ADJ);
+		fine = ((btn_voltage[i] % MSM89XX_MBHC_BTN_COARSE_ADJ) /
+				MSM89XX_MBHC_BTN_FINE_ADJ);
+
+		reg_val = (course << 5) | (fine << 2);
+		snd_soc_update_bits_wrapper(codec, reg_addr, 0xFC, reg_val);
+		pr_err("%s: course: %d fine: %d reg_addr: %x reg_val: %x\n",
+			  __func__, course, fine, reg_addr, reg_val);
+		reg_addr++;
+	}
+}
+
+static void msm8x16_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+					    uint32_t *zr)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	s16 impedance_l, impedance_r;
+	s16 impedance_l_fixed;
+	s16 reg0, reg1, reg2, reg3, reg4;
+	bool high = false;
+	bool min_range_used =  false;
+
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+	reg0 = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER);
+	reg1 = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL);
+	reg2 = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2);
+	reg3 = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN);
+	reg4 = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL);
+
+	msm8x16_wcd->imped_det_pin = WCD_MBHC_DET_BOTH;
+	mbhc->hph_type = WCD_MBHC_HPH_NONE;
+
+	/* disable FSM and micbias and enable pullup*/
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x80, 0x00);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_2_EN,
+			0xA5, 0x25);
+	/*
+	 * Enable legacy electrical detection current sources
+	 * and disable fast ramp and enable manual switching
+	 * of extra capacitance
+	 */
+	pr_err("%s: Setup for impedance det\n", __func__);
+
+	msm8x16_set_ref_current(codec, I_h4_UA);
+
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2,
+			0x06, 0x02);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER,
+			0x02, 0x02);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL,
+			0x02, 0x00);
+
+	pr_err("%s: Start performing impedance detection\n",
+		 __func__);
+
+	wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r);
+
+	if (impedance_l > 2 || impedance_r > 2) {
+		high = true;
+		if (!mbhc->mbhc_cfg->mono_stero_detection) {
+			/* Set ZDET_CHG to 0  to discharge ramp */
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+					0x02, 0x00);
+			/* wait 40ms for the discharge ramp to complete */
+			usleep_range(40000, 40100);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+				0x03, 0x00);
+			msm8x16_wcd->imped_det_pin = (impedance_l > 2 &&
+						      impedance_r > 2) ?
+						      WCD_MBHC_DET_NONE :
+						      ((impedance_l > 2) ?
+						      WCD_MBHC_DET_HPHR :
+						      WCD_MBHC_DET_HPHL);
+			if (msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_NONE)
+				goto exit;
+		} else {
+			if (get_codec_version(msm8x16_wcd) >= CAJON) {
+				if (impedance_l == 63 && impedance_r == 63) {
+					pr_err("%s: HPHL and HPHR are floating\n",
+						 __func__);
+					msm8x16_wcd->imped_det_pin =
+							WCD_MBHC_DET_NONE;
+					mbhc->hph_type = WCD_MBHC_HPH_NONE;
+				} else if (impedance_l == 63
+					   && impedance_r < 63) {
+					pr_err("%s: Mono HS with HPHL floating\n",
+						 __func__);
+					msm8x16_wcd->imped_det_pin =
+							WCD_MBHC_DET_HPHR;
+					mbhc->hph_type = WCD_MBHC_HPH_MONO;
+				} else if (impedance_r == 63 &&
+					   impedance_l < 63) {
+					pr_err("%s: Mono HS with HPHR floating\n",
+						 __func__);
+					msm8x16_wcd->imped_det_pin =
+							WCD_MBHC_DET_HPHL;
+					mbhc->hph_type = WCD_MBHC_HPH_MONO;
+				} else if (impedance_l > 3 && impedance_r > 3 &&
+					(impedance_l == impedance_r)) {
+					snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2,
+					0x06, 0x06);
+					wcd_mbhc_meas_imped(codec, &impedance_l,
+							    &impedance_r);
+					if (impedance_r == impedance_l)
+						pr_err("%s: Mono Headset\n",
+							  __func__);
+						msm8x16_wcd->imped_det_pin =
+							WCD_MBHC_DET_NONE;
+						mbhc->hph_type =
+							WCD_MBHC_HPH_MONO;
+				} else {
+					pr_err("%s: STEREO headset is found\n",
+						 __func__);
+					msm8x16_wcd->imped_det_pin =
+							WCD_MBHC_DET_BOTH;
+					mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+				}
+			}
+		}
+	}
+
+	msm8x16_set_ref_current(codec, I_pt5_UA);
+	msm8x16_set_ref_current(codec, I_14_UA);
+
+	/* Enable RAMP_L, RAMP_R & ZDET_CHG*/
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+			0x03, 0x03);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x02, 0x02);
+	/* wait for 50msec for the HW to apply ramp on HPHL and HPHR */
+	usleep_range(50000, 50100);
+	/* Enable ZDET_DISCHG_CAP_CTL  to add extra capacitance */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x01, 0x01);
+	/* wait for 5msec for the voltage to get stable */
+	usleep_range(5000, 5100);
+
+
+	wcd_mbhc_meas_imped(codec, &impedance_l, &impedance_r);
+
+	min_range_used = msm8x16_adj_ref_current(codec,
+						&impedance_l, &impedance_r);
+	if (!mbhc->mbhc_cfg->mono_stero_detection) {
+		/* Set ZDET_CHG to 0  to discharge ramp */
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+				0x02, 0x00);
+		/* wait for 40msec for the capacitor to discharge */
+		usleep_range(40000, 40100);
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+				0x03, 0x00);
+		goto exit;
+	}
+
+	/* we are setting ref current to the minimun range or the measured
+	 * value larger than the minimum value, so min_range_used is true.
+	 * If the headset is mono headset with either HPHL or HPHR floating
+	 * then we have already done the mono stereo detection and do not
+	 * need to continue further.
+	 */
+
+	if (!min_range_used ||
+	    msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHL ||
+	    msm8x16_wcd->imped_det_pin == WCD_MBHC_DET_HPHR)
+		goto exit;
+
+
+	/* Disable Set ZDET_CONN_RAMP_L and enable ZDET_CONN_FIXED_L */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+			0x02, 0x00);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL,
+			0x02, 0x02);
+	/* Set ZDET_CHG to 0  */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x02, 0x00);
+	/* wait for 40msec for the capacitor to discharge */
+	usleep_range(40000, 40100);
+
+	/* Set ZDET_CONN_RAMP_R to 0  */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+			0x01, 0x00);
+	/* Enable ZDET_L_MEAS_EN */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x08, 0x08);
+	/* wait for 2msec for the HW to compute left inpedance value */
+	usleep_range(2000, 2100);
+	/* Read Left impedance value from Result1 */
+	impedance_l_fixed = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT);
+	/* Disable ZDET_L_MEAS_EN */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x08, 0x00);
+	/*
+	 * Assume impedance_l is L1, impedance_l_fixed is L2.
+	 * If the following condition is met, we can take this
+	 * headset as mono one with impedance of L2.
+	 * Otherwise, take it as stereo with impedance of L1.
+	 * Condition:
+	 * abs[(L2-0.5L1)/(L2+0.5L1)] < abs [(L2-L1)/(L2+L1)]
+	 */
+	if ((abs(impedance_l_fixed - impedance_l/2) *
+		(impedance_l_fixed + impedance_l)) >=
+		(abs(impedance_l_fixed - impedance_l) *
+		(impedance_l_fixed + impedance_l/2))) {
+		pr_err("%s: STEREO plug type detected\n",
+			 __func__);
+		mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+	} else {
+		pr_err("%s: MONO plug type detected\n",
+			__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_MONO;
+		impedance_l = impedance_l_fixed;
+	}
+	/* Enable ZDET_CHG  */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x02, 0x02);
+	/* wait for 10msec for the capacitor to charge */
+	usleep_range(10000, 10100);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+			0x02, 0x02);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL,
+			0x02, 0x00);
+	/* Set ZDET_CHG to 0  to discharge HPHL */
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL,
+			0x02, 0x00);
+	/* wait for 40msec for the capacitor to discharge */
+	usleep_range(40000, 40100);
+	snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL,
+			0x02, 0x00);
+
+exit:
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL, reg4);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MICB_2_EN, reg3);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL, reg1);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER, reg0);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2, reg2);
+	msm8x16_wcd_compute_impedance(codec, impedance_l, impedance_r,
+				      zl, zr, high);
+
+	pr_err("%s: RL %d ohm, RR %d ohm\n", __func__, *zl, *zr);
+	pr_err("%s: Impedance detection completed\n", __func__);
+}
+
+static int msm8x16_register_notifier(struct wcd_mbhc *mbhc,
+				     struct notifier_block *nblock,
+				     bool enable)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct msm8x16_wcd_priv *msm8x16_wcd =
+		snd_soc_codec_get_drvdata(codec);
+
+	if (enable)
+		return blocking_notifier_chain_register(&msm8x16_wcd->notifier,
+							nblock);
+	return blocking_notifier_chain_unregister(
+			&msm8x16_wcd->notifier,	nblock);
+}
+
+static int msm8x16_wcd_request_irq(struct snd_soc_codec *codec,
+				   int irq, irq_handler_t handler,
+				   const char *name, void *data)
+{
+	return wcd9xxx_spmi_request_irq(irq, handler, name, data);
+}
+
+static int msm8x16_wcd_free_irq(struct snd_soc_codec *codec,
+				int irq, void *data)
+{
+	return wcd9xxx_spmi_free_irq(irq, data);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+	.enable_mb_source = msm8x16_wcd_enable_ext_mb_source,
+	.trim_btn_reg = msm8x16_trim_btn_reg,
+	.compute_impedance = msm8x16_wcd_mbhc_calc_impedance,
+	.set_micbias_value = msm8x16_wcd_set_micb_v,
+	.set_auto_zeroing = msm8x16_wcd_set_auto_zeroing,
+	.get_hwdep_fw_cal = msm8x16_wcd_get_hwdep_fw_cal,
+	.set_cap_mode = msm8x16_wcd_configure_cap,
+	.register_notifier = msm8x16_register_notifier,
+	.request_irq = msm8x16_wcd_request_irq,
+	.irq_control = wcd9xxx_spmi_irq_control,
+	.free_irq = msm8x16_wcd_free_irq,
+	.clk_setup = msm8x16_mbhc_clk_setup,
+	.map_btn_code_to_num = msm8x16_mbhc_map_btn_code_to_num,
+	.lock_sleep = msm8x16_spmi_lock_sleep,
+	.micbias_enable_status = msm8x16_wcd_micb_en_status,
+	.mbhc_bias = msm8x16_wcd_enable_master_bias,
+	.mbhc_common_micb_ctrl = msm8x16_wcd_mbhc_common_micb_ctrl,
+	.micb_internal = msm8x16_wcd_mbhc_internal_micbias_ctrl,
+	.hph_pa_on_status = msm8x16_wcd_mbhc_hph_pa_on_status,
+	.set_btn_thr = msm8x16_wcd_mbhc_program_btn_thr,
+	.extn_use_mb = msm8x16_wcd_use_mb,
+};
+
+static const uint32_t wcd_imped_val[] = {4, 8, 12, 13, 16,
+					20, 24, 28, 32,
+					36, 40, 44, 48};
+
+void msm8x16_notifier_call(struct snd_soc_codec *codec,
+				  const enum wcd_notify_event event)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	pr_err("%s: notifier call event %d\n", __func__, event);
+	blocking_notifier_call_chain(&msm8x16_wcd->notifier, event,
+				     &msm8x16_wcd->mbhc);
+}
+
+static void msm8x16_wcd_boost_on(struct snd_soc_codec *codec)
+{
+	u8 dest = 0x00;
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+
+	if ((dest & MASK_MSB_BIT) == 0) {
+		pr_err("PMIC MBG not ON, enable codec hw_en MB bit again\n");
+		snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30);
+		/* Allow 1ms for PMIC MBG state to be updated */
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+	}
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3,
+		0x0F, 0x0F);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+		0xA5);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3,
+		0x0F);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL,
+		0x30);
+	if (get_codec_version(msm8x16_wcd) < CAJON_2_0) {
+		snd_soc_write_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_CURRENT_LIMIT,
+			0x82);
+	} else {
+		snd_soc_write_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_CURRENT_LIMIT,
+			0xA2);
+	}
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+		0x69, 0x69);
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG,
+		0x01, 0x01);
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO,
+		0x88, 0x88);
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+		0x03, 0x03);
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL,
+		0xE1, 0xE1);
+	if (get_codec_version(msm8x16_wcd) < CAJON_2_0) {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+			0x20, 0x20);
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+			0xDF, 0xDF);
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+	} else {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+			0x40, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+			0x20, 0x20);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+			0x80, 0x80);
+		usleep_range(500, 510);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+			0x40, 0x40);
+		usleep_range(500, 510);
+	}
+}
+
+static void msm8x16_wcd_boost_off(struct snd_soc_codec *codec)
+{
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+		0xDF, 0x5F);
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+		0x20, 0x00);
+}
+
+static void msm8x16_wcd_bypass_on(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (get_codec_version(msm8x16_wcd) < CAJON_2_0) {
+		snd_soc_write_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+			0xA5);
+		snd_soc_write_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3,
+			0x07);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x02, 0x02);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x01, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x40, 0x40);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x80, 0x80);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+			0xDF, 0xDF);
+	} else {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+			0x20, 0x20);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x20, 0x20);
+	}
+}
+
+static void msm8x16_wcd_bypass_off(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (get_codec_version(msm8x16_wcd) < CAJON_2_0) {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BOOST_EN_CTL,
+			0x80, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x80, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x02, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x40, 0x00);
+	} else {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_BYPASS_MODE,
+			0x20, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+			0x20, 0x00);
+	}
+}
+
+static void msm8x16_wcd_boost_mode_sequence(struct snd_soc_codec *codec,
+					int flag)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (flag == EAR_PMU) {
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (msm8x16_wcd->ear_pa_boost_set) {
+				msm8x16_wcd_boost_off(codec);
+				msm8x16_wcd_bypass_on(codec);
+			}
+			break;
+		case BOOST_ALWAYS:
+			msm8x16_wcd_boost_on(codec);
+			break;
+		case BYPASS_ALWAYS:
+			msm8x16_wcd_bypass_on(codec);
+			break;
+		case BOOST_ON_FOREVER:
+			msm8x16_wcd_boost_on(codec);
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+	} else if (flag == EAR_PMD) {
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (msm8x16_wcd->ear_pa_boost_set)
+				msm8x16_wcd_bypass_off(codec);
+			break;
+		case BOOST_ALWAYS:
+			msm8x16_wcd_boost_off(codec);
+			/* 80ms for EAR boost to settle down */
+			msleep(80);
+			break;
+		case BYPASS_ALWAYS:
+			/* nothing to do as bypass on always */
+			break;
+		case BOOST_ON_FOREVER:
+			/* nothing to do as boost on forever */
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+	} else if (flag == SPK_PMU) {
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (msm8x16_wcd->spk_boost_set) {
+				msm8x16_wcd_bypass_off(codec);
+				msm8x16_wcd_boost_on(codec);
+			}
+			break;
+		case BOOST_ALWAYS:
+			msm8x16_wcd_boost_on(codec);
+			break;
+		case BYPASS_ALWAYS:
+			msm8x16_wcd_bypass_on(codec);
+			break;
+		case BOOST_ON_FOREVER:
+			msm8x16_wcd_boost_on(codec);
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+	} else if (flag == SPK_PMD) {
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (msm8x16_wcd->spk_boost_set) {
+				msm8x16_wcd_boost_off(codec);
+				/*
+				 * Add 40 ms sleep for the spk
+				 * boost to settle down
+				 */
+				msleep(40);
+			}
+			break;
+		case BOOST_ALWAYS:
+			msm8x16_wcd_boost_off(codec);
+			/*
+			 * Add 40 ms sleep for the spk
+			 * boost to settle down
+			 */
+			msleep(40);
+			break;
+		case BYPASS_ALWAYS:
+			/* nothing to do as bypass on always */
+			break;
+		case BOOST_ON_FOREVER:
+			/* nothing to do as boost on forever */
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+	}
+}
+
+static int msm8x16_wcd_dt_parse_vreg_info(struct device *dev,
+	struct msm8x16_wcd_regulator *vreg, const char *vreg_name,
+	bool ondemand)
+{
+	int len, ret = 0;
+	const __be32 *prop;
+	char prop_name[CODEC_DT_MAX_PROP_SIZE];
+	struct device_node *regnode = NULL;
+	u32 prop_val;
+
+	snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE, "%s-supply",
+		vreg_name);
+	regnode = of_parse_phandle(dev->of_node, prop_name, 0);
+
+	if (!regnode) {
+		dev_err(dev, "Looking up %s property in node %s failed\n",
+			prop_name, dev->of_node->full_name);
+		return -ENODEV;
+	}
+
+	dev_err(dev, "Looking up %s property in node %s\n",
+		prop_name, dev->of_node->full_name);
+
+	vreg->name = vreg_name;
+	vreg->ondemand = ondemand;
+
+	snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
+		"qcom,%s-voltage", vreg_name);
+	prop = of_get_property(dev->of_node, prop_name, &len);
+
+	if (!prop || (len != (2 * sizeof(__be32)))) {
+		dev_err(dev, "%s %s property\n",
+			prop ? "invalid format" : "no", prop_name);
+		return -EINVAL;
+	}
+	vreg->min_uv = be32_to_cpup(&prop[0]);
+	vreg->max_uv = be32_to_cpup(&prop[1]);
+
+	snprintf(prop_name, CODEC_DT_MAX_PROP_SIZE,
+		"qcom,%s-current", vreg_name);
+
+	ret = of_property_read_u32(dev->of_node, prop_name, &prop_val);
+	if (ret) {
+		dev_err(dev, "Looking up %s property in node %s failed",
+			prop_name, dev->of_node->full_name);
+		return -EFAULT;
+	}
+	vreg->optimum_ua = prop_val;
+
+	dev_err(dev, "%s: vol=[%d %d]uV, curr=[%d]uA, ond %d\n\n", vreg->name,
+		 vreg->min_uv, vreg->max_uv, vreg->optimum_ua, vreg->ondemand);
+	return 0;
+}
+
+static void msm8x16_wcd_dt_parse_boost_info(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+		snd_soc_codec_get_drvdata(codec);
+	const char *prop_name = "qcom,cdc-boost-voltage";
+	int boost_voltage, ret;
+
+	ret = of_property_read_u32(codec->dev->of_node, prop_name,
+			&boost_voltage);
+	if (ret) {
+		dev_err(codec->dev, "Looking up %s property in node %s failed\n",
+			prop_name, codec->dev->of_node->full_name);
+		boost_voltage = DEFAULT_BOOST_VOLTAGE;
+	}
+	if (boost_voltage < MIN_BOOST_VOLTAGE ||
+			boost_voltage > MAX_BOOST_VOLTAGE) {
+		dev_err(codec->dev,
+				"Incorrect boost voltage. Reverting to default\n");
+		boost_voltage = DEFAULT_BOOST_VOLTAGE;
+	}
+
+	msm8x16_wcd_priv->boost_voltage =
+		VOLTAGE_CONVERTER(boost_voltage, MIN_BOOST_VOLTAGE,
+				BOOST_VOLTAGE_STEP);
+	dev_err(codec->dev, "Boost voltage value is: %d\n",
+			boost_voltage);
+}
+
+static void msm8x16_wcd_dt_parse_micbias_info(struct device *dev,
+			struct wcd9xxx_micbias_setting *micbias)
+{
+	const char *prop_name = "qcom,cdc-micbias-cfilt-mv";
+	int ret;
+
+	ret = of_property_read_u32(dev->of_node, prop_name,
+			&micbias->cfilt1_mv);
+	if (ret) {
+		dev_err(dev, "Looking up %s property in node %s failed",
+			prop_name, dev->of_node->full_name);
+		micbias->cfilt1_mv = MICBIAS_DEFAULT_VAL;
+	}
+}
+
+static struct msm8x16_wcd_pdata *msm8x16_wcd_populate_dt_pdata(
+						struct device *dev)
+{
+	struct msm8x16_wcd_pdata *pdata;
+	int ret, static_cnt, ond_cnt, idx, i;
+	const char *name = NULL;
+	const char *static_prop_name = "qcom,cdc-static-supplies";
+	const char *ond_prop_name = "qcom,cdc-on-demand-supplies";
+
+	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+	if (!pdata)
+		return NULL;
+
+	static_cnt = of_property_count_strings(dev->of_node, static_prop_name);
+	if (static_cnt < 0) {
+		dev_err(dev, "%s: Failed to get static supplies %d\n", __func__,
+			static_cnt);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/* On-demand supply list is an optional property */
+	ond_cnt = of_property_count_strings(dev->of_node, ond_prop_name);
+	if (ond_cnt < 0)
+		ond_cnt = 0;
+
+	WARN_ON(static_cnt <= 0 || ond_cnt < 0);
+	if ((static_cnt + ond_cnt) > ARRAY_SIZE(pdata->regulator)) {
+		dev_err(dev, "%s: Num of supplies %u > max supported %zd\n",
+				__func__, (static_cnt + ond_cnt),
+					ARRAY_SIZE(pdata->regulator));
+		ret = -EINVAL;
+		goto err;
+	}
+
+	for (idx = 0; idx < static_cnt; idx++) {
+		ret = of_property_read_string_index(dev->of_node,
+						    static_prop_name, idx,
+						    &name);
+		if (ret) {
+			dev_err(dev, "%s: of read string %s idx %d error %d\n",
+				__func__, static_prop_name, idx, ret);
+			goto err;
+		}
+
+		dev_err(dev, "%s: Found static cdc supply %s\n", __func__,
+			name);
+		ret = msm8x16_wcd_dt_parse_vreg_info(dev,
+						&pdata->regulator[idx],
+						name, false);
+		if (ret) {
+			dev_err(dev, "%s:err parsing vreg for %s idx %d\n",
+				__func__, name, idx);
+			goto err;
+		}
+	}
+
+	for (i = 0; i < ond_cnt; i++, idx++) {
+		ret = of_property_read_string_index(dev->of_node, ond_prop_name,
+						    i, &name);
+		if (ret) {
+			dev_err(dev, "%s: err parsing on_demand for %s idx %d\n",
+				__func__, ond_prop_name, i);
+			goto err;
+		}
+
+		dev_err(dev, "%s: Found on-demand cdc supply %s\n", __func__,
+			name);
+		ret = msm8x16_wcd_dt_parse_vreg_info(dev,
+						&pdata->regulator[idx],
+						name, true);
+		if (ret) {
+			dev_err(dev, "%s: err parsing vreg on_demand for %s idx %d\n",
+				__func__, name, idx);
+			goto err;
+		}
+	}
+	msm8x16_wcd_dt_parse_micbias_info(dev, &pdata->micbias);
+	return pdata;
+err:
+	devm_kfree(dev, pdata);
+	dev_err(dev, "%s: Failed to populate DT data ret = %d\n",
+		__func__, ret);
+	return NULL;
+}
+
+static int msm8x16_wcd_codec_enable_on_demand_supply(
+		struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	struct on_demand_supply *supply;
+
+	if (w->shift >= ON_DEMAND_SUPPLIES_MAX) {
+		dev_err(codec->dev, "%s: error index > MAX Demand supplies",
+			__func__);
+		ret = -EINVAL;
+		goto out;
+	}
+	dev_err(codec->dev, "%s: supply: %s event: %d ref: %d\n",
+		__func__, on_demand_supply_name[w->shift], event,
+		atomic_read(&msm8x16_wcd->on_demand_list[w->shift].ref));
+
+	supply = &msm8x16_wcd->on_demand_list[w->shift];
+	WARN_ONCE(!supply->supply, "%s isn't defined\n",
+		  on_demand_supply_name[w->shift]);
+	if (!supply->supply) {
+		dev_err(codec->dev, "%s: err supply not present ond for %d",
+			__func__, w->shift);
+		goto out;
+	}
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (atomic_inc_return(&supply->ref) == 1)
+			ret = regulator_enable(supply->supply);
+		if (ret)
+			dev_err(codec->dev, "%s: Failed to enable %s\n",
+				__func__,
+				on_demand_supply_name[w->shift]);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (atomic_read(&supply->ref) == 0) {
+			dev_err(codec->dev, "%s: %s supply has been disabled.\n",
+				 __func__, on_demand_supply_name[w->shift]);
+			goto out;
+		}
+		if (atomic_dec_return(&supply->ref) == 0)
+			ret = regulator_disable(supply->supply);
+			if (ret)
+				dev_err(codec->dev, "%s: Failed to disable %s\n",
+					__func__,
+					on_demand_supply_name[w->shift]);
+		break;
+	default:
+		break;
+	}
+out:
+	return ret;
+}
+
+static int msm8x16_wcd_codec_enable_clock_block(struct snd_soc_codec *codec,
+						int enable)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	if (enable) {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_CLK_MCLK_CTL, 0x01, 0x01);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30, 0x30);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x0C);
+		if (pdata->mclk_freq == MCLK_RATE_12P288MHZ)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x00);
+		else if (pdata->mclk_freq == MCLK_RATE_9P6MHZ)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_TOP_CTL, 0x01, 0x01);
+	} else {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL, 0x0C, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x00);
+
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_charge_pump(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: event = %d\n", __func__, event);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		msm8x16_wcd_codec_enable_clock_block(codec, 1);
+		if (!(strcmp(w->name, "EAR CP"))) {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0x80, 0x80);
+			msm8x16_wcd_boost_mode_sequence(codec, EAR_PMU);
+		} else if (get_codec_version(msm8x16_wcd) == DIANGU) {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0x80, 0x80);
+		} else {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0xC0, 0xC0);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		if (!(strcmp(w->name, "EAR CP"))) {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0x80, 0x00);
+			if (msm8x16_wcd->boost_option != BOOST_ALWAYS) {
+				dev_err(codec->dev,
+					"%s: boost_option:%d, tear down ear\n",
+					__func__, msm8x16_wcd->boost_option);
+				msm8x16_wcd_boost_mode_sequence(codec, EAR_PMD);
+			}
+			/*
+			 * Reset pa select bit from ear to hph after ear pa
+			 * is disabled and HPH DAC disable to reduce ear
+			 * turn off pop and avoid HPH pop in concurrency
+			 */
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x00);
+		} else {
+			if (get_codec_version(msm8x16_wcd) < DIANGU)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0x40, 0x00);
+			if (msm8x16_wcd->rx_bias_count == 0)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0x80, 0x00);
+			dev_err(codec->dev, "%s: rx_bias_count = %d\n",
+					__func__, msm8x16_wcd->rx_bias_count);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_ear_pa_boost_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] =
+		(msm8x16_wcd->ear_pa_boost_set ? 1 : 0);
+	dev_err(codec->dev, "%s: msm8x16_wcd->ear_pa_boost_set = %d\n",
+			__func__, msm8x16_wcd->ear_pa_boost_set);
+	return 0;
+}
+
+static int msm8x16_wcd_ear_pa_boost_set(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd =
+			snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+	msm8x16_wcd->ear_pa_boost_set =
+		(ucontrol->value.integer.value[0] ? true : false);
+	return 0;
+}
+
+static int msm8x16_wcd_pa_gain_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ear_pa_gain = snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_EAR_CTL);
+
+	ear_pa_gain = (ear_pa_gain >> 5) & 0x1;
+
+	if (ear_pa_gain == 0x00) {
+		ucontrol->value.integer.value[0] = 0;
+	} else if (ear_pa_gain == 0x01) {
+		ucontrol->value.integer.value[0] = 1;
+	} else  {
+		dev_err(codec->dev, "%s: ERROR: Unsupported Ear Gain = 0x%x\n",
+			__func__, ear_pa_gain);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = ear_pa_gain;
+	dev_err(codec->dev, "%s: ear_pa_gain = 0x%x\n",
+		__func__, ear_pa_gain);
+	return 0;
+}
+
+static int msm8x16_wcd_loopback_mode_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm_asoc_mach_data *pdata = NULL;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	return pdata->lb_mode;
+}
+
+static int msm8x16_wcd_loopback_mode_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm_asoc_mach_data *pdata = NULL;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		pdata->lb_mode = false;
+		break;
+	case 1:
+		pdata->lb_mode = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int msm8x16_wcd_pa_gain_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		ear_pa_gain = 0x00;
+		break;
+	case 1:
+		ear_pa_gain = 0x20;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+			    0x20, ear_pa_gain);
+	return 0;
+}
+
+static int msm8x16_wcd_hph_mode_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (msm8x16_wcd->hph_mode == NORMAL_MODE) {
+		ucontrol->value.integer.value[0] = 0;
+	} else if (msm8x16_wcd->hph_mode == HD2_MODE) {
+		ucontrol->value.integer.value[0] = 1;
+	} else  {
+		dev_err(codec->dev, "%s: ERROR: Default HPH Mode= %d\n",
+			__func__, msm8x16_wcd->hph_mode);
+	}
+
+	dev_err(codec->dev, "%s: msm8x16_wcd->hph_mode = %d\n", __func__,
+			msm8x16_wcd->hph_mode);
+	return 0;
+}
+
+static int msm8x16_wcd_hph_mode_set(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		msm8x16_wcd->hph_mode = NORMAL_MODE;
+		break;
+	case 1:
+		if (get_codec_version(msm8x16_wcd) >= DIANGU)
+			msm8x16_wcd->hph_mode = HD2_MODE;
+		break;
+	default:
+		msm8x16_wcd->hph_mode = NORMAL_MODE;
+		break;
+	}
+	dev_err(codec->dev, "%s: msm8x16_wcd->hph_mode_set = %d\n",
+		__func__, msm8x16_wcd->hph_mode);
+	return 0;
+}
+
+static int msm8x16_wcd_boost_option_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (msm8x16_wcd->boost_option == BOOST_SWITCH) {
+		ucontrol->value.integer.value[0] = 0;
+	} else if (msm8x16_wcd->boost_option == BOOST_ALWAYS) {
+		ucontrol->value.integer.value[0] = 1;
+	} else if (msm8x16_wcd->boost_option == BYPASS_ALWAYS) {
+		ucontrol->value.integer.value[0] = 2;
+	} else if (msm8x16_wcd->boost_option == BOOST_ON_FOREVER) {
+		ucontrol->value.integer.value[0] = 3;
+	} else  {
+		dev_err(codec->dev, "%s: ERROR: Unsupported Boost option= %d\n",
+			__func__, msm8x16_wcd->boost_option);
+		return -EINVAL;
+	}
+
+	dev_err(codec->dev, "%s: msm8x16_wcd->boost_option = %d\n", __func__,
+			msm8x16_wcd->boost_option);
+	return 0;
+}
+
+static int msm8x16_wcd_boost_option_set(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		msm8x16_wcd->boost_option = BOOST_SWITCH;
+		break;
+	case 1:
+		msm8x16_wcd->boost_option = BOOST_ALWAYS;
+		break;
+	case 2:
+		msm8x16_wcd->boost_option = BYPASS_ALWAYS;
+		msm8x16_wcd_bypass_on(codec);
+		break;
+	case 3:
+		msm8x16_wcd->boost_option = BOOST_ON_FOREVER;
+		msm8x16_wcd_boost_on(codec);
+		break;
+	default:
+		pr_err("%s: invalid boost option: %d\n", __func__,
+					msm8x16_wcd->boost_option);
+		return -EINVAL;
+	}
+	dev_err(codec->dev, "%s: msm8x16_wcd->boost_option_set = %d\n",
+		__func__, msm8x16_wcd->boost_option);
+	return 0;
+}
+
+static int msm8x16_wcd_ext_spk_boost_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (msm8x16_wcd->ext_spk_boost_set == false)
+		ucontrol->value.integer.value[0] = 0;
+	else
+		ucontrol->value.integer.value[0] = 1;
+
+	dev_err(codec->dev, "%s: msm8x16_wcd->ext_spk_boost_set = %d\n",
+				__func__, msm8x16_wcd->ext_spk_boost_set);
+	return 0;
+}
+
+static int msm8x16_wcd_ext_spk_boost_set(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		msm8x16_wcd->ext_spk_boost_set = false;
+		break;
+	case 1:
+		msm8x16_wcd->ext_spk_boost_set = true;
+		break;
+	default:
+		return -EINVAL;
+	}
+	dev_err(codec->dev, "%s: msm8x16_wcd->spk_boost_set = %d\n",
+		__func__, msm8x16_wcd->spk_boost_set);
+	return 0;
+}
+static int msm8x16_wcd_get_iir_enable_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read_wrapper(codec,
+			    (MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) &
+		(1 << band_idx)) != 0;
+
+	dev_err(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm8x16_wcd_put_iir_enable_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	int value = ucontrol->value.integer.value[0];
+
+	/* Mask first 5 bits, 6-8 are reserved */
+	snd_soc_update_bits_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx),
+			    (1 << band_idx), (value << band_idx));
+
+	dev_err(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+	  iir_idx, band_idx,
+		((snd_soc_read_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_CTL + 64 * iir_idx)) &
+	  (1 << band_idx)) != 0));
+
+	return 0;
+}
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+				   int iir_idx, int band_idx,
+				   int coeff_idx)
+{
+	uint32_t value = 0;
+
+	/* Address does not automatically update if reading */
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t)) & 0x7F);
+
+	value |= snd_soc_read_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx));
+
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 1) & 0x7F);
+
+	value |= (snd_soc_read_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 8);
+
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 2) & 0x7F);
+
+	value |= (snd_soc_read_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx)) << 16);
+
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 3) & 0x7F);
+
+	/* Mask bits top 2 bits since they are reserved */
+	value |= ((snd_soc_read_wrapper(codec,
+			(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL
+		+ 64 * iir_idx)) & 0x3f) << 24);
+
+	return value;
+
+}
+
+static int msm8x16_wcd_get_iir_band_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	ucontrol->value.integer.value[0] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+	ucontrol->value.integer.value[1] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+	ucontrol->value.integer.value[2] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+	ucontrol->value.integer.value[3] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+	ucontrol->value.integer.value[4] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+	dev_err(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[1],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[2],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[3],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[4]);
+	return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+				int iir_idx, int band_idx,
+				uint32_t value)
+{
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+		(value & 0xFF));
+
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+		(value >> 8) & 0xFF);
+
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+		(value >> 16) & 0xFF);
+
+	/* Mask top 2 bits, 7-8 are reserved */
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL + 64 * iir_idx),
+		(value >> 24) & 0x3F);
+
+}
+
+static int msm8x16_wcd_put_iir_band_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	/* Mask top bit it is reserved */
+	/* Updates addr automatically for each B2 write */
+	snd_soc_write_wrapper(codec,
+		(MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL + 64 * iir_idx),
+		(band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+			   ucontrol->value.integer.value[0]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+			   ucontrol->value.integer.value[1]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+			   ucontrol->value.integer.value[2]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+			   ucontrol->value.integer.value[3]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+			   ucontrol->value.integer.value[4]);
+
+	dev_err(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+	return 0;
+}
+
+static int msm8x16_wcd_compander_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	int comp_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int rx_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	dev_err(codec->dev, "%s: msm8x16_wcd->comp[%d]_enabled[%d] = %d\n",
+			__func__, comp_idx, rx_idx,
+			msm8x16_wcd->comp_enabled[rx_idx]);
+
+	ucontrol->value.integer.value[0] = msm8x16_wcd->comp_enabled[rx_idx];
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm8x16_wcd_compander_set(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	int comp_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int rx_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	int value = ucontrol->value.integer.value[0];
+
+	dev_err(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	if (get_codec_version(msm8x16_wcd) >= DIANGU) {
+		if (!value)
+			msm8x16_wcd->comp_enabled[rx_idx] = 0;
+		else
+			msm8x16_wcd->comp_enabled[rx_idx] = comp_idx;
+	}
+
+	dev_err(codec->dev, "%s: msm8x16_wcd->comp[%d]_enabled[%d] = %d\n",
+		__func__, comp_idx, rx_idx,
+		msm8x16_wcd->comp_enabled[rx_idx]);
+
+	return 0;
+}
+
+static const char * const msm8x16_wcd_loopback_mode_ctrl_text[] = {
+		"DISABLE", "ENABLE"};
+static const struct soc_enum msm8x16_wcd_loopback_mode_ctl_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_loopback_mode_ctrl_text),
+};
+
+static const char * const msm8x16_wcd_ear_pa_boost_ctrl_text[] = {
+		"DISABLE", "ENABLE"};
+static const struct soc_enum msm8x16_wcd_ear_pa_boost_ctl_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_boost_ctrl_text),
+};
+
+static const char * const msm8x16_wcd_ear_pa_gain_text[] = {
+		"POS_1P5_DB", "POS_6_DB"};
+static const struct soc_enum msm8x16_wcd_ear_pa_gain_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ear_pa_gain_text),
+};
+
+static const char * const msm8x16_wcd_boost_option_ctrl_text[] = {
+		"BOOST_SWITCH", "BOOST_ALWAYS", "BYPASS_ALWAYS",
+		"BOOST_ON_FOREVER"};
+static const struct soc_enum msm8x16_wcd_boost_option_ctl_enum[] = {
+		SOC_ENUM_SINGLE_EXT(4, msm8x16_wcd_boost_option_ctrl_text),
+};
+static const char * const msm8x16_wcd_spk_boost_ctrl_text[] = {
+		"DISABLE", "ENABLE"};
+static const struct soc_enum msm8x16_wcd_spk_boost_ctl_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_spk_boost_ctrl_text),
+};
+
+static const char * const msm8x16_wcd_ext_spk_boost_ctrl_text[] = {
+		"DISABLE", "ENABLE"};
+static const struct soc_enum msm8x16_wcd_ext_spk_boost_ctl_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, msm8x16_wcd_ext_spk_boost_ctrl_text),
+};
+
+static const char * const msm8x16_wcd_hph_mode_ctrl_text[] = {
+		"NORMAL", "HD2"};
+static const struct soc_enum msm8x16_wcd_hph_mode_ctl_enum[] = {
+		SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(msm8x16_wcd_hph_mode_ctrl_text),
+			msm8x16_wcd_hph_mode_ctrl_text),
+};
+
+/*cut of frequency for high pass filter*/
+static const char * const cf_text[] = {
+	"MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz"
+};
+
+static const struct soc_enum cf_dec1_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX1_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX2_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_rxmix1_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX1_B4_CTL, 0, 3, cf_text);
+
+static const struct soc_enum cf_rxmix2_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX2_B4_CTL, 0, 3, cf_text);
+
+static const struct soc_enum cf_rxmix3_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_RX3_B4_CTL, 0, 3, cf_text);
+
+static const struct snd_kcontrol_new msm8x16_wcd_snd_controls[] = {
+
+	SOC_ENUM_EXT("RX HPH Mode", msm8x16_wcd_hph_mode_ctl_enum[0],
+		msm8x16_wcd_hph_mode_get, msm8x16_wcd_hph_mode_set),
+
+	SOC_ENUM_EXT("Boost Option", msm8x16_wcd_boost_option_ctl_enum[0],
+		msm8x16_wcd_boost_option_get, msm8x16_wcd_boost_option_set),
+
+	SOC_ENUM_EXT("EAR PA Boost", msm8x16_wcd_ear_pa_boost_ctl_enum[0],
+		msm8x16_wcd_ear_pa_boost_get, msm8x16_wcd_ear_pa_boost_set),
+
+	SOC_ENUM_EXT("EAR PA Gain", msm8x16_wcd_ear_pa_gain_enum[0],
+		msm8x16_wcd_pa_gain_get, msm8x16_wcd_pa_gain_put),
+
+	SOC_ENUM_EXT("Ext Spk Boost", msm8x16_wcd_ext_spk_boost_ctl_enum[0],
+		msm8x16_wcd_ext_spk_boost_get, msm8x16_wcd_ext_spk_boost_set),
+
+	SOC_ENUM_EXT("LOOPBACK Mode", msm8x16_wcd_loopback_mode_ctl_enum[0],
+		msm8x16_wcd_loopback_mode_get, msm8x16_wcd_loopback_mode_put),
+
+	SOC_SINGLE_TLV("ADC1 Volume", MSM89XX_PMIC_ANALOG_TX_1_EN, 3,
+					8, 0, analog_gain),
+	SOC_SINGLE_TLV("ADC2 Volume", MSM89XX_PMIC_ANALOG_TX_2_EN, 3,
+					8, 0, analog_gain),
+	SOC_SINGLE_TLV("ADC3 Volume", MSM89XX_PMIC_ANALOG_TX_3_EN, 3,
+					8, 0, analog_gain),
+
+	SOC_SINGLE_SX_TLV("RX1 Digital Volume",
+			  MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL,
+			0,  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX2 Digital Volume",
+			  MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL,
+			0,  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX3 Digital Volume",
+			  MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL,
+			0,  -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("DEC1 Volume",
+			  MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN,
+			0,  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC2 Volume",
+			  MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN,
+			0,  -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("IIR1 INP1 Volume",
+			  MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL,
+			0,  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP2 Volume",
+			  MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL,
+			0,  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP3 Volume",
+			  MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL,
+			0,  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP4 Volume",
+			  MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL,
+			0,  -84,	40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR2 INP1 Volume",
+			  MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL,
+			0,  -84, 40, digital_gain),
+
+	SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+	SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+
+	SOC_SINGLE("TX1 HPF Switch",
+		MSM89XX_CDC_CORE_TX1_MUX_CTL, 3, 1, 0),
+	SOC_SINGLE("TX2 HPF Switch",
+		MSM89XX_CDC_CORE_TX2_MUX_CTL, 3, 1, 0),
+
+	SOC_SINGLE("RX1 HPF Switch",
+		MSM89XX_CDC_CORE_RX1_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX2 HPF Switch",
+		MSM89XX_CDC_CORE_RX2_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX3 HPF Switch",
+		MSM89XX_CDC_CORE_RX3_B5_CTL, 2, 1, 0),
+
+	SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum),
+	SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum),
+	SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum),
+
+	SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0,
+	msm8x16_wcd_get_iir_enable_audio_mixer,
+	msm8x16_wcd_put_iir_enable_audio_mixer),
+
+	SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
+	msm8x16_wcd_get_iir_band_audio_mixer,
+	msm8x16_wcd_put_iir_band_audio_mixer),
+
+	SOC_SINGLE_EXT("COMP0 RX1", COMPANDER_1, MSM89XX_RX1, 1, 0,
+	msm8x16_wcd_compander_get, msm8x16_wcd_compander_set),
+
+	SOC_SINGLE_EXT("COMP0 RX2", COMPANDER_1, MSM89XX_RX2, 1, 0,
+	msm8x16_wcd_compander_get, msm8x16_wcd_compander_set),
+};
+
+static int tombak_hph_impedance_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int ret;
+	uint32_t zl, zr;
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+
+	hphr = mc->shift;
+	ret = wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+	if (ret)
+		pr_err("%s: Failed to get mbhc imped", __func__);
+	pr_err("%s: zl %u, zr %u\n", __func__, zl, zr);
+	ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+			tombak_hph_impedance_get, NULL),
+	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+			tombak_hph_impedance_get, NULL),
+};
+
+static int tombak_get_hph_type(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm8x16_wcd_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct wcd_mbhc *mbhc;
+
+	if (!priv) {
+		pr_err("%s: msm8x16-wcd private data is NULL\n",
+			 __func__);
+		return -EINVAL;
+	}
+
+	mbhc = &priv->mbhc;
+	if (!mbhc) {
+		pr_err("%s: mbhc not initialized\n", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+	pr_err("%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+	SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+	tombak_get_hph_type, NULL),
+};
+
+static const char * const rx_mix1_text[] = {
+	"ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3"
+};
+
+static const char * const rx_mix2_text[] = {
+	"ZERO", "IIR1", "IIR2"
+};
+
+static const char * const dec_mux_text[] = {
+	"ZERO", "ADC1", "ADC2", "ADC3", "DMIC1", "DMIC2"
+};
+
+static const char * const dec3_mux_text[] = {
+	"ZERO", "DMIC3"
+};
+
+static const char * const dec4_mux_text[] = {
+	"ZERO", "DMIC4"
+};
+
+static const char * const adc2_mux_text[] = {
+	"ZERO", "INP2", "INP3"
+};
+
+static const char * const ext_spk_text[] = {
+	"Off", "On"
+};
+
+static const char * const wsa_spk_text[] = {
+	"ZERO", "WSA"
+};
+
+static const char * const rdac2_mux_text[] = {
+	"ZERO", "RX2", "RX1"
+};
+
+static const char * const iir_inp1_text[] = {
+	"ZERO", "DEC1", "DEC2", "RX1", "RX2", "RX3"
+};
+
+static const struct soc_enum adc2_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+		ARRAY_SIZE(adc2_mux_text), adc2_mux_text);
+
+static const struct soc_enum ext_spk_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+		ARRAY_SIZE(ext_spk_text), ext_spk_text);
+
+static const struct soc_enum wsa_spk_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0,
+		ARRAY_SIZE(wsa_spk_text), wsa_spk_text);
+
+/* RX1 MIX1 */
+static const struct soc_enum rx_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL,
+		0, 6, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B1_CTL,
+		3, 6, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp3_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B2_CTL,
+		0, 6, rx_mix1_text);
+
+/* RX1 MIX2 */
+static const struct soc_enum rx_mix2_inp1_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX1_B3_CTL,
+		0, 3, rx_mix2_text);
+
+/* RX2 MIX1 */
+static const struct soc_enum rx2_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL,
+		0, 6, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL,
+		3, 6, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp3_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B1_CTL,
+		0, 6, rx_mix1_text);
+
+/* RX2 MIX2 */
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX2_B3_CTL,
+		0, 3, rx_mix2_text);
+
+/* RX3 MIX1 */
+static const struct soc_enum rx3_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL,
+		0, 6, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL,
+		3, 6, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp3_chain_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_RX3_B1_CTL,
+		0, 6, rx_mix1_text);
+
+/* DEC */
+static const struct soc_enum dec1_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL,
+		0, 6, dec_mux_text);
+
+static const struct soc_enum dec2_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_TX_B1_CTL,
+		3, 6, dec_mux_text);
+
+static const struct soc_enum dec3_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX3_MUX_CTL, 0,
+				ARRAY_SIZE(dec3_mux_text), dec3_mux_text);
+
+static const struct soc_enum dec4_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_TX4_MUX_CTL, 0,
+				ARRAY_SIZE(dec4_mux_text), dec4_mux_text);
+
+static const struct soc_enum rdac2_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL,
+		0, 3, rdac2_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL,
+		0, 6, iir_inp1_text);
+
+static const struct soc_enum iir2_inp1_mux_enum =
+	SOC_ENUM_SINGLE(MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL,
+		0, 6, iir_inp1_text);
+
+static const struct snd_kcontrol_new ext_spk_mux =
+	SOC_DAPM_ENUM("Ext Spk Switch Mux", ext_spk_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp3_mux =
+	SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp3_mux =
+	SOC_DAPM_ENUM("RX2 MIX1 INP3 Mux", rx2_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp3_mux =
+	SOC_DAPM_ENUM("RX3 MIX1 INP3 Mux", rx3_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux =
+	SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux =
+	SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc2_mux =
+	SOC_DAPM_ENUM("ADC2 MUX Mux", adc2_enum);
+
+static int msm8x16_wcd_put_dec_enum(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+			dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *w = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int dec_mux, decimator;
+	char *dec_name = NULL;
+	char *widget_name = NULL;
+	char *temp;
+	u16 tx_mux_ctl_reg;
+	u8 adc_dmic_sel = 0x0;
+	int ret = 0;
+	char *dec_num;
+
+	if (ucontrol->value.enumerated.item[0] > e->items) {
+		dev_err(codec->dev, "%s: Invalid enum value: %d\n",
+			__func__, ucontrol->value.enumerated.item[0]);
+		return -EINVAL;
+	}
+	dec_mux = ucontrol->value.enumerated.item[0];
+
+	widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+	if (!widget_name) {
+		dev_err(codec->dev, "%s: failed to copy string\n",
+			__func__);
+		return -ENOMEM;
+	}
+	temp = widget_name;
+
+	dec_name = strsep(&widget_name, " ");
+	widget_name = temp;
+	if (!dec_name) {
+		dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+			__func__, w->name);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	dec_num = strpbrk(dec_name, "12");
+	if (dec_num == NULL) {
+		dev_err(codec->dev, "%s: Invalid DEC selected\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = kstrtouint(dec_num, 10, &decimator);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+			__func__, dec_name);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	dev_err(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n"
+		, __func__, w->name, decimator, dec_mux);
+
+	switch (decimator) {
+	case 1:
+	case 2:
+		if ((dec_mux == 4) || (dec_mux == 5))
+			adc_dmic_sel = 0x1;
+		else
+			adc_dmic_sel = 0x0;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid Decimator = %u\n",
+			__func__, decimator);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tx_mux_ctl_reg =
+		MSM89XX_CDC_CORE_TX1_MUX_CTL + 32 * (decimator - 1);
+
+	snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel);
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+out:
+	kfree(widget_name);
+	return ret;
+}
+
+#define MSM89XX_DEC_ENUM(xname, xenum) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_double, \
+	.put = msm8x16_wcd_put_dec_enum, \
+	.private_value = (unsigned long)&xenum }
+
+static const struct snd_kcontrol_new dec1_mux =
+	MSM89XX_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum);
+
+static const struct snd_kcontrol_new dec2_mux =
+	MSM89XX_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum);
+
+static const struct snd_kcontrol_new dec3_mux =
+	SOC_DAPM_ENUM("DEC3 MUX Mux", dec3_mux_enum);
+
+static const struct snd_kcontrol_new dec4_mux =
+	SOC_DAPM_ENUM("DEC4 MUX Mux", dec4_mux_enum);
+
+static const struct snd_kcontrol_new rdac2_mux =
+	SOC_DAPM_ENUM("RDAC2 MUX Mux", rdac2_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+	SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const char * const ear_text[] = {
+	"ZERO", "Switch",
+};
+
+static const struct soc_enum ear_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(ear_text), ear_text);
+
+static const struct snd_kcontrol_new ear_pa_mux[] = {
+	SOC_DAPM_ENUM("EAR_S", ear_enum)
+};
+
+static const struct snd_kcontrol_new wsa_spk_mux[] = {
+	SOC_DAPM_ENUM("WSA Spk Switch", wsa_spk_enum)
+};
+
+static const struct snd_kcontrol_new iir2_inp1_mux =
+	SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum);
+
+static const char * const hph_text[] = {
+	"ZERO", "Switch",
+};
+
+static const struct soc_enum hph_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text);
+
+static const struct snd_kcontrol_new hphl_mux[] = {
+	SOC_DAPM_ENUM("HPHL", hph_enum)
+};
+
+static const struct snd_kcontrol_new hphr_mux[] = {
+	SOC_DAPM_ENUM("HPHR", hph_enum)
+};
+
+static const struct snd_kcontrol_new spkr_mux[] = {
+	SOC_DAPM_ENUM("SPK", hph_enum)
+};
+
+static const char * const lo_text[] = {
+	"ZERO", "Switch",
+};
+
+static const struct soc_enum lo_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(hph_text), hph_text);
+
+static const struct snd_kcontrol_new lo_mux[] = {
+	SOC_DAPM_ENUM("LINE_OUT", lo_enum)
+};
+
+static void msm8x16_wcd_codec_enable_adc_block(struct snd_soc_codec *codec,
+					 int enable)
+{
+	struct msm8x16_wcd_priv *wcd8x16 = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s %d\n", __func__, enable);
+
+	if (enable) {
+		wcd8x16->adc_count++;
+		snd_soc_update_bits_wrapper(codec,
+				    MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL,
+				    0x20, 0x20);
+		snd_soc_update_bits_wrapper(codec,
+				    MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+				    0x10, 0x10);
+	} else {
+		wcd8x16->adc_count--;
+		if (!wcd8x16->adc_count) {
+			snd_soc_update_bits_wrapper(codec,
+				    MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+				    0x10, 0x00);
+			snd_soc_update_bits_wrapper(codec,
+				    MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL,
+					    0x20, 0x0);
+		}
+	}
+}
+
+static int msm8x16_wcd_codec_enable_adc(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u16 adc_reg;
+	u8 init_bit_shift;
+
+	dev_err(codec->dev, "%s %d\n", __func__, event);
+
+	adc_reg = MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2;
+
+	if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN)
+		init_bit_shift = 5;
+	else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) ||
+		 (w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN))
+		init_bit_shift = 4;
+	else {
+		dev_err(codec->dev, "%s: Error, invalid adc register\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		msm8x16_wcd_codec_enable_adc_block(codec, 1);
+		if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN)
+			snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x02);
+		/*
+		 * Add delay of 10 ms to give sufficient time for the voltage
+		 * to shoot up and settle so that the txfe init does not
+		 * happen when the input voltage is changing too much.
+		 */
+		usleep_range(10000, 10010);
+		snd_soc_update_bits_wrapper(codec,
+			adc_reg, 1 << init_bit_shift,
+			1 << init_bit_shift);
+		if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL,
+				0x03, 0x00);
+		else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) ||
+			(w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN))
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL,
+				0x03, 0x00);
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * Add delay of 12 ms before deasserting the init
+		 * to reduce the tx pop
+		 */
+	usleep_range(12000, 12010);
+		snd_soc_update_bits_wrapper(codec,
+			adc_reg, 1 << init_bit_shift, 0x00);
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		msm8x16_wcd_codec_enable_adc_block(codec, 0);
+		if (w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN)
+			snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_MICB_1_CTL, 0x02, 0x00);
+		if (w->reg == MSM89XX_PMIC_ANALOG_TX_1_EN)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL,
+				0x03, 0x02);
+		else if ((w->reg == MSM89XX_PMIC_ANALOG_TX_2_EN) ||
+			(w->reg == MSM89XX_PMIC_ANALOG_TX_3_EN))
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL,
+				0x03, 0x02);
+
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_spk_pa(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s %d %s\n", __func__, event, w->name);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x01);
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (!msm8x16_wcd->spk_boost_set)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+					0x10, 0x10);
+			break;
+		case BOOST_ALWAYS:
+		case BOOST_ON_FOREVER:
+			break;
+		case BYPASS_ALWAYS:
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+				0x10, 0x10);
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0xE0);
+		if (get_codec_version(msm8x16_wcd) != TOMBAK_1_0)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x01);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (msm8x16_wcd->spk_boost_set)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+					0xEF, 0xEF);
+			else
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL,
+					0x10, 0x00);
+			break;
+		case BOOST_ALWAYS:
+		case BOOST_ON_FOREVER:
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+				0xEF, 0xEF);
+			break;
+		case BYPASS_ALWAYS:
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00);
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00);
+		snd_soc_update_bits_wrapper(codec, w->reg, 0x80, 0x80);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x01);
+		msm8x16_wcd->mute_mask |= SPKR_PA_DISABLE;
+		/*
+		 * Add 1 ms sleep for the mute to take effect
+		 */
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x10);
+		if (get_codec_version(msm8x16_wcd) < CAJON_2_0)
+			msm8x16_wcd_boost_mode_sequence(codec, SPK_PMD);
+		snd_soc_update_bits_wrapper(codec, w->reg, 0x80, 0x00);
+		switch (msm8x16_wcd->boost_option) {
+		case BOOST_SWITCH:
+			if (msm8x16_wcd->spk_boost_set)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+					0xEF, 0x69);
+			break;
+		case BOOST_ALWAYS:
+		case BOOST_ON_FOREVER:
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+				0xEF, 0x69);
+			break;
+		case BYPASS_ALWAYS:
+			break;
+		default:
+			pr_err("%s: invalid boost option: %d\n", __func__,
+						msm8x16_wcd->boost_option);
+			break;
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0xE0, 0x00);
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL, 0x01, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x10, 0x00);
+		if (get_codec_version(msm8x16_wcd) != TOMBAK_1_0)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x01, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00);
+		if (get_codec_version(msm8x16_wcd) >= CAJON_2_0)
+			msm8x16_wcd_boost_mode_sequence(codec, SPK_PMD);
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_dig_clk(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	struct msm_asoc_mach_data *pdata = NULL;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+
+	dev_err(codec->dev, "%s event %d w->name %s\n", __func__,
+			event, w->name);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		msm8x16_wcd_codec_enable_clock_block(codec, 1);
+		snd_soc_update_bits_wrapper(codec, w->reg, 0x80, 0x80);
+		msm8x16_wcd_boost_mode_sequence(codec, SPK_PMU);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (msm8x16_wcd->rx_bias_count == 0)
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+					0x80, 0x00);
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	u8  dmic_clk_en;
+	u16 dmic_clk_reg;
+	s32 *dmic_clk_cnt;
+	unsigned int dmic;
+	int ret;
+	char *dec_num = strpbrk(w->name, "12");
+
+	if (dec_num == NULL) {
+		dev_err(codec->dev, "%s: Invalid DMIC\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = kstrtouint(dec_num, 10, &dmic);
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Invalid DMIC line on the codec\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (dmic) {
+	case 1:
+	case 2:
+		dmic_clk_en = 0x01;
+		dmic_clk_cnt = &(msm8x16_wcd->dmic_1_2_clk_cnt);
+		dmic_clk_reg = MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL;
+		dev_err(codec->dev,
+			"%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
+			__func__, event,  dmic, *dmic_clk_cnt);
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid DMIC Selection\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		(*dmic_clk_cnt)++;
+		if (*dmic_clk_cnt == 1) {
+			snd_soc_update_bits_wrapper(codec, dmic_clk_reg,
+					0x0E, 0x02);
+			snd_soc_update_bits_wrapper(codec, dmic_clk_reg,
+					dmic_clk_en, dmic_clk_en);
+		}
+		if (dmic == 1)
+			snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_TX1_DMIC_CTL, 0x07, 0x01);
+		if (dmic == 2)
+			snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_TX2_DMIC_CTL, 0x07, 0x01);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		(*dmic_clk_cnt)--;
+		if (*dmic_clk_cnt  == 0)
+			snd_soc_update_bits_wrapper(codec, dmic_clk_reg,
+					dmic_clk_en, 0);
+		break;
+	}
+	return 0;
+}
+
+static bool msm8x16_wcd_use_mb(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (get_codec_version(msm8x16_wcd) < CAJON)
+		return true;
+	else
+		return false;
+}
+
+static void msm8x16_wcd_set_auto_zeroing(struct snd_soc_codec *codec,
+					bool enable)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (get_codec_version(msm8x16_wcd) < CONGA) {
+		if (enable)
+			/*
+			 * Set autozeroing for special headset detection and
+			 * buttons to work.
+			 */
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_2_EN,
+				0x18, 0x10);
+		else
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_2_EN,
+				0x18, 0x00);
+
+	} else {
+		pr_err("%s: Auto Zeroing is not required from CONGA\n",
+				__func__);
+	}
+}
+
+static void msm8x16_trim_btn_reg(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if (get_codec_version(msm8x16_wcd) == TOMBAK_1_0) {
+		pr_err("%s: This device needs to be trimmed\n", __func__);
+		/*
+		 * Calculate the trim value for each device used
+		 * till is comes in production by hardware team
+		 */
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+				0xA5, 0xA5);
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_TRIM_CTRL2,
+				0xFF, 0x30);
+	} else {
+		pr_err("%s: This device is trimmed at ATE\n", __func__);
+	}
+}
+static int msm8x16_wcd_enable_ext_mb_source(struct wcd_mbhc *mbhc,
+					    bool turn_on)
+{
+	int ret = 0;
+	static int count;
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	dev_err(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on,
+			count);
+	if (turn_on) {
+		if (!count) {
+			ret = snd_soc_dapm_force_enable_pin(dapm,
+				"MICBIAS_REGULATOR");
+			snd_soc_dapm_sync(dapm);
+		}
+		count++;
+	} else {
+		if (count > 0)
+			count--;
+		if (!count) {
+			ret = snd_soc_dapm_disable_pin(dapm,
+				"MICBIAS_REGULATOR");
+			snd_soc_dapm_sync(dapm);
+		}
+	}
+
+	if (ret)
+		dev_err(codec->dev, "%s: Failed to %s external micbias source\n",
+			__func__, turn_on ? "enable" : "disabled");
+	else
+		dev_err(codec->dev, "%s: %s external micbias source\n",
+			 __func__, turn_on ? "Enabled" : "Disabled");
+
+	return ret;
+}
+
+static int msm8x16_wcd_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd =
+				snd_soc_codec_get_drvdata(codec);
+	u16 micb_int_reg;
+	char *internal1_text = "Internal1";
+	char *internal2_text = "Internal2";
+	char *internal3_text = "Internal3";
+	char *external2_text = "External2";
+	char *external_text = "External";
+	bool micbias2;
+
+	dev_err(codec->dev, "%s %d\n", __func__, event);
+	switch (w->reg) {
+	case MSM89XX_PMIC_ANALOG_MICB_1_EN:
+	case MSM89XX_PMIC_ANALOG_MICB_2_EN:
+		micb_int_reg = MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: Error, invalid micbias register 0x%x\n",
+			__func__, w->reg);
+		return -EINVAL;
+	}
+
+	micbias2 = (snd_soc_read_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_MICB_2_EN) & 0x80);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (strnstr(w->name, internal1_text, strlen(w->name))) {
+			if (get_codec_version(msm8x16_wcd) >= CAJON)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2,
+					0x02, 0x02);
+			snd_soc_update_bits_wrapper(codec,
+				micb_int_reg, 0x80, 0x80);
+		} else if (strnstr(w->name, internal2_text, strlen(w->name))) {
+			snd_soc_update_bits_wrapper(codec,
+				micb_int_reg, 0x10, 0x10);
+			snd_soc_update_bits_wrapper(codec,
+				w->reg, 0x60, 0x00);
+		} else if (strnstr(w->name, internal3_text, strlen(w->name))) {
+			snd_soc_update_bits_wrapper(codec,
+				micb_int_reg, 0x2, 0x2);
+		/*
+		 * update MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2
+		 * for external bias only, not for external2.
+		 */
+		} else if (!strnstr(w->name, external2_text, strlen(w->name)) &&
+					strnstr(w->name, external_text,
+						strlen(w->name))) {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2,
+					0x02, 0x02);
+		}
+		if (!strnstr(w->name, external_text, strlen(w->name)))
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_EN, 0x05, 0x04);
+		if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN)
+			msm8x16_wcd_configure_cap(codec, true, micbias2);
+
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (get_codec_version(msm8x16_wcd) <= TOMBAK_2_0)
+			usleep_range(20000, 20100);
+		if (strnstr(w->name, internal1_text, strlen(w->name))) {
+			snd_soc_update_bits_wrapper(codec,
+				micb_int_reg, 0x40, 0x40);
+		} else if (strnstr(w->name, internal2_text,  strlen(w->name))) {
+			snd_soc_update_bits_wrapper(codec,
+				 micb_int_reg, 0x08, 0x08);
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_POST_MICBIAS_2_ON);
+		} else if (strnstr(w->name, internal3_text, 30)) {
+			snd_soc_update_bits_wrapper(codec,
+				 micb_int_reg, 0x01, 0x01);
+		} else if (strnstr(w->name, external2_text, strlen(w->name))) {
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_POST_MICBIAS_2_ON);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (strnstr(w->name, internal1_text, strlen(w->name))) {
+			snd_soc_update_bits_wrapper(codec,
+				micb_int_reg, 0xC0, 0x40);
+		} else if (strnstr(w->name, internal2_text, strlen(w->name))) {
+			msm8x16_notifier_call(codec,
+				WCD_EVENT_POST_MICBIAS_2_OFF);
+		} else if (strnstr(w->name, internal3_text, 30)) {
+			snd_soc_update_bits_wrapper(codec,
+				micb_int_reg, 0x2, 0x0);
+		} else if (strnstr(w->name, external2_text, strlen(w->name))) {
+			/*
+			 * send micbias turn off event to mbhc driver and then
+			 * break, as no need to set MICB_1_EN register.
+			 */
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_POST_MICBIAS_2_OFF);
+			break;
+		}
+		if (w->reg == MSM89XX_PMIC_ANALOG_MICB_1_EN)
+			msm8x16_wcd_configure_cap(codec, false, micbias2);
+		break;
+	}
+	return 0;
+}
+
+static void tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+	struct delayed_work *hpf_delayed_work;
+	struct hpf_work *hpf_work;
+	struct msm8x16_wcd_priv *msm8x16_wcd;
+	struct snd_soc_codec *codec;
+	u16 tx_mux_ctl_reg;
+	u8 hpf_cut_of_freq;
+
+	hpf_delayed_work = to_delayed_work(work);
+	hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+	msm8x16_wcd = hpf_work->msm8x16_wcd;
+	codec = hpf_work->msm8x16_wcd->codec;
+	hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq;
+
+	tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL +
+			(hpf_work->decimator - 1) * 32;
+
+	dev_err(codec->dev, "%s(): decimator %u hpf_cut_of_freq 0x%x\n",
+		 __func__, hpf_work->decimator, (unsigned int)hpf_cut_of_freq);
+	snd_soc_update_bits_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV, 0xFF, 0x51);
+
+	snd_soc_update_bits_wrapper(codec,
+		tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4);
+}
+
+
+#define  TX_MUX_CTL_CUT_OFF_FREQ_MASK	0x30
+#define  CF_MIN_3DB_4HZ			0x0
+#define  CF_MIN_3DB_75HZ		0x1
+#define  CF_MIN_3DB_150HZ		0x2
+
+static int msm8x16_wcd_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int value = 0, reg;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (w->shift == 0)
+			reg = MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL;
+		else if (w->shift == 1)
+			reg = MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL;
+		value = snd_soc_read_wrapper(codec, reg);
+		snd_soc_write_wrapper(codec, reg, value);
+		break;
+	default:
+		pr_err("%s: event = %d not expected\n", __func__, event);
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_dec(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm_asoc_mach_data *pdata = NULL;
+	unsigned int decimator;
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	char *dec_name = NULL;
+	char *widget_name = NULL;
+	char *temp;
+	int ret = 0, i;
+	u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
+	u8 dec_hpf_cut_of_freq;
+	int offset;
+	char *dec_num;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	dev_err(codec->dev, "%s %d\n", __func__, event);
+
+	widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+	if (!widget_name)
+		return -ENOMEM;
+	temp = widget_name;
+
+	dec_name = strsep(&widget_name, " ");
+	widget_name = temp;
+	if (!dec_name) {
+		dev_err(codec->dev,
+			"%s: Invalid decimator = %s\n", __func__, w->name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	dec_num = strpbrk(dec_name, "1234");
+	if (dec_num == NULL) {
+		dev_err(codec->dev, "%s: Invalid Decimator\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = kstrtouint(dec_num, 10, &decimator);
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Invalid decimator = %s\n", __func__, dec_name);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	dev_err(codec->dev,
+		"%s(): widget = %s dec_name = %s decimator = %u\n", __func__,
+		w->name, dec_name, decimator);
+
+	if (w->reg == MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL) {
+		dec_reset_reg = MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL;
+		offset = 0;
+	} else {
+		dev_err(codec->dev, "%s: Error, incorrect dec\n", __func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG +
+			 32 * (decimator - 1);
+	tx_mux_ctl_reg = MSM89XX_CDC_CORE_TX1_MUX_CTL +
+			  32 * (decimator - 1);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (decimator == 3 || decimator == 4) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL,
+				0xFF, 0x5);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_TX1_DMIC_CTL +
+					(decimator - 1) * 0x20, 0x7, 0x2);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_TX1_DMIC_CTL +
+					(decimator - 1) * 0x20, 0x7, 0x2);
+		}
+		/* Enableable TX digital mute */
+		snd_soc_update_bits_wrapper(codec, tx_vol_ctl_reg, 0x01, 0x01);
+		for (i = 0; i < NUM_DECIMATORS; i++) {
+			if (decimator == i + 1)
+				msm8x16_wcd->dec_active[i] = true;
+		}
+
+		dec_hpf_cut_of_freq =
+			snd_soc_read_wrapper(codec, tx_mux_ctl_reg);
+
+		dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4;
+
+		tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq =
+			dec_hpf_cut_of_freq;
+
+		if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) {
+
+			/* set cut of freq to CF_MIN_3DB_150HZ (0x1); */
+			snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x30,
+					    CF_MIN_3DB_150HZ << 4);
+		}
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV,
+				0xFF, 0x42);
+
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* enable HPF */
+		snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x08, 0x00);
+
+		if (tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq !=
+				CF_MIN_3DB_150HZ) {
+
+			schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork,
+					msecs_to_jiffies(300));
+		}
+		/* apply the digital gain after the decimator is enabled*/
+		if ((w->shift) < ARRAY_SIZE(tx_digital_gain_reg))
+			snd_soc_write_wrapper(codec,
+				  tx_digital_gain_reg[w->shift + offset],
+				  snd_soc_read_wrapper(codec,
+				  tx_digital_gain_reg[w->shift + offset])
+				  );
+		if (pdata->lb_mode) {
+			pr_err("%s: loopback mode unmute the DEC\n",
+							__func__);
+			snd_soc_update_bits_wrapper(codec,
+				tx_vol_ctl_reg, 0x01, 0x00);
+		}
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits_wrapper(codec, tx_vol_ctl_reg, 0x01, 0x01);
+		msleep(20);
+		snd_soc_update_bits_wrapper(codec, tx_mux_ctl_reg, 0x08, 0x08);
+		cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits_wrapper(codec,
+			dec_reset_reg, 1 << w->shift, 1 << w->shift);
+		snd_soc_update_bits_wrapper(codec,
+			dec_reset_reg, 1 << w->shift, 0x0);
+		snd_soc_update_bits_wrapper(codec,
+			tx_mux_ctl_reg, 0x08, 0x08);
+		snd_soc_update_bits_wrapper(codec,
+			tx_mux_ctl_reg, 0x30,
+			(tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4);
+		snd_soc_update_bits_wrapper(codec,
+			tx_vol_ctl_reg, 0x01, 0x00);
+		for (i = 0; i < NUM_DECIMATORS; i++) {
+			if (decimator == i + 1)
+				msm8x16_wcd->dec_active[i] = false;
+		}
+		if (decimator == 3 || decimator == 4) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL,
+				0xFF, 0x0);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_TX1_DMIC_CTL +
+					(decimator - 1) * 0x20, 0x7, 0x0);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_TX1_DMIC_CTL +
+					(decimator - 1) * 0x20, 0x7, 0x0);
+		}
+		break;
+	}
+out:
+	kfree(widget_name);
+	return ret;
+}
+
+static int msm89xx_wcd_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (!msm8x16_wcd->ext_spk_boost_set) {
+		dev_err(codec->dev, "%s: ext_boost not supported/disabled\n",
+								__func__);
+		return 0;
+	}
+	dev_err(codec->dev, "%s: %s %d\n", __func__, w->name, event);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (msm8x16_wcd->spkdrv_reg) {
+			ret = regulator_enable(msm8x16_wcd->spkdrv_reg);
+			if (ret)
+				dev_err(codec->dev,
+					"%s Failed to enable spkdrv reg %s\n",
+					__func__, MSM89XX_VDD_SPKDRV_NAME);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (msm8x16_wcd->spkdrv_reg) {
+			ret = regulator_disable(msm8x16_wcd->spkdrv_reg);
+			if (ret)
+				dev_err(codec->dev,
+					"%s: Failed to disable spkdrv_reg %s\n",
+					__func__, MSM89XX_VDD_SPKDRV_NAME);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_config_compander(struct snd_soc_codec *codec,
+					int interp_n, int event)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: event %d shift %d, enabled %d\n",
+		__func__, event, interp_n,
+		msm8x16_wcd->comp_enabled[interp_n]);
+
+	/* compander is not enabled */
+	if (!msm8x16_wcd->comp_enabled[interp_n])
+		return 0;
+
+	switch (msm8x16_wcd->comp_enabled[interp_n]) {
+	case COMPANDER_1:
+		if (SND_SOC_DAPM_EVENT_ON(event)) {
+			/* Enable Compander Clock */
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x09);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x01);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B1_CTL,
+				1 << interp_n, 1 << interp_n);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x01);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0x50);
+			/* add sleep for compander to settle */
+			usleep_range(1000, 1100);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B3_CTL, 0xFF, 0x28);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B2_CTL, 0xF0, 0xB0);
+
+			/* Enable Compander GPIO */
+			if (msm8x16_wcd->codec_hph_comp_gpio)
+				msm8x16_wcd->codec_hph_comp_gpio(1);
+		} else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+			/* Disable Compander GPIO */
+			if (msm8x16_wcd->codec_hph_comp_gpio)
+				msm8x16_wcd->codec_hph_comp_gpio(0);
+
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B2_CTL, 0x0F, 0x05);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_COMP0_B1_CTL,
+				1 << interp_n, 0);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_RX_B2_CTL, 0x01, 0x00);
+		}
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid compander %d\n", __func__,
+				msm8x16_wcd->comp_enabled[interp_n]);
+		break;
+	};
+
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+						 struct snd_kcontrol *kcontrol,
+						 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		msm8x16_wcd_codec_config_compander(codec, w->shift, event);
+		/* apply the digital gain after the interpolator is enabled*/
+		if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+			snd_soc_write_wrapper(codec,
+				  rx_digital_gain_reg[w->shift],
+				  snd_soc_read_wrapper(codec,
+				  rx_digital_gain_reg[w->shift])
+				  );
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		msm8x16_wcd_codec_config_compander(codec, w->shift, event);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_CLK_RX_RESET_CTL,
+			1 << w->shift, 1 << w->shift);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_CLK_RX_RESET_CTL,
+			1 << w->shift, 0x0);
+		/*
+		 * disable the mute enabled during the PMD of this device
+		 */
+		if ((w->shift == 0) &&
+			(msm8x16_wcd->mute_mask & HPHL_PA_DISABLE)) {
+			pr_err("disabling HPHL mute\n");
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00);
+			if (get_codec_version(msm8x16_wcd) >= CAJON)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP,
+					0xF0, 0x20);
+			msm8x16_wcd->mute_mask &= ~(HPHL_PA_DISABLE);
+		} else if ((w->shift == 1) &&
+				(msm8x16_wcd->mute_mask & HPHR_PA_DISABLE)) {
+			pr_err("disabling HPHR mute\n");
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00);
+			if (get_codec_version(msm8x16_wcd) >= CAJON)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP,
+					0xF0, 0x20);
+			msm8x16_wcd->mute_mask &= ~(HPHR_PA_DISABLE);
+		} else if ((w->shift == 2) &&
+				(msm8x16_wcd->mute_mask & SPKR_PA_DISABLE)) {
+			pr_err("disabling SPKR mute\n");
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00);
+			msm8x16_wcd->mute_mask &= ~(SPKR_PA_DISABLE);
+		} else if ((w->shift == 0) &&
+				(msm8x16_wcd->mute_mask & EAR_PA_DISABLE)) {
+			pr_err("disabling EAR mute\n");
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00);
+			msm8x16_wcd->mute_mask &= ~(EAR_PA_DISABLE);
+		}
+	}
+	return 0;
+}
+
+
+/* The register address is the same as other codec so it can use resmgr */
+static int msm8x16_wcd_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		msm8x16_wcd->rx_bias_count++;
+		if (msm8x16_wcd->rx_bias_count == 1) {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+					0x80, 0x80);
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+					0x01, 0x01);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		msm8x16_wcd->rx_bias_count--;
+		if (msm8x16_wcd->rx_bias_count == 0) {
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+					0x01, 0x00);
+			snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+					0x80, 0x00);
+		}
+		break;
+	}
+	dev_err(codec->dev, "%s rx_bias_count = %d\n",
+			__func__, msm8x16_wcd->rx_bias_count);
+	return 0;
+}
+
+static uint32_t wcd_get_impedance_value(uint32_t imped)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(wcd_imped_val) - 1; i++) {
+		if (imped >= wcd_imped_val[i] &&
+			imped < wcd_imped_val[i + 1])
+			break;
+	}
+
+	pr_err("%s: selected impedance value = %d\n",
+		 __func__, wcd_imped_val[i]);
+	return wcd_imped_val[i];
+}
+
+void wcd_imped_config(struct snd_soc_codec *codec,
+			uint32_t imped, bool set_gain)
+{
+	uint32_t value;
+	int codec_version;
+	struct msm8x16_wcd_priv *msm8x16_wcd =
+				snd_soc_codec_get_drvdata(codec);
+
+	value = wcd_get_impedance_value(imped);
+
+	if (value < wcd_imped_val[0]) {
+		pr_err("%s, detected impedance is less than 4 Ohm\n",
+			 __func__);
+		return;
+	}
+	if (value >= wcd_imped_val[ARRAY_SIZE(wcd_imped_val) - 1]) {
+		pr_err("%s, invalid imped, greater than 48 Ohm\n = %d\n",
+			__func__, value);
+		return;
+	}
+
+	codec_version = get_codec_version(msm8x16_wcd);
+
+	if (set_gain) {
+		switch (codec_version) {
+		case TOMBAK_1_0:
+		case TOMBAK_2_0:
+		case CONGA:
+			/*
+			 * For 32Ohm load and higher loads, Set 0x19E
+			 * bit 5 to 1 (POS_6_DB_DI). For loads lower
+			 * than 32Ohm (such as 16Ohm load), Set 0x19E
+			 * bit 5 to 0 (POS_1P5_DB_DI)
+			 */
+			if (value >= 32)
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+					0x20, 0x20);
+			else
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+					0x20, 0x00);
+			break;
+		case CAJON:
+		case CAJON_2_0:
+		case DIANGU:
+			if (value >= 13) {
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+					0x20, 0x20);
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_NCP_VCTRL,
+					0x07, 0x07);
+			} else {
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+					0x20, 0x00);
+				snd_soc_update_bits_wrapper(codec,
+					MSM89XX_PMIC_ANALOG_NCP_VCTRL,
+					0x07, 0x04);
+			}
+			break;
+		}
+	} else {
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+			0x20, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_NCP_VCTRL,
+			0x07, 0x04);
+	}
+
+	pr_err("%s: Exit\n", __func__);
+}
+
+static int msm8x16_wcd_hphl_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	uint32_t impedl, impedr;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+	int ret;
+
+	dev_err(codec->dev, "%s %s %d\n", __func__, w->name, event);
+	ret = wcd_mbhc_get_impedance(&msm8x16_wcd->mbhc,
+			&impedl, &impedr);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (get_codec_version(msm8x16_wcd) > CAJON)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+				0x08, 0x08);
+		if (get_codec_version(msm8x16_wcd) == CAJON ||
+			get_codec_version(msm8x16_wcd) == CAJON_2_0) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST,
+				0x80, 0x80);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST,
+				0x80, 0x80);
+		}
+		if (get_codec_version(msm8x16_wcd) > CAJON)
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+				0x08, 0x00);
+		if (msm8x16_wcd->hph_mode == HD2_MODE) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x14);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0x10);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x80);
+		}
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x02);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x01);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x02);
+		if (!ret)
+			wcd_imped_config(codec, impedl, true);
+		else
+			dev_err(codec->dev, "Failed to get mbhc impedance %d\n",
+				ret);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x02, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd_imped_config(codec, impedl, false);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x02, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x01, 0x00);
+		if (msm8x16_wcd->hph_mode == HD2_MODE) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B3_CTL, 0x1C, 0x00);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B4_CTL, 0x18, 0xFF);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B3_CTL, 0x80, 0x00);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_lo_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_err(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x10);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x20);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x80);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x08);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x40);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x80);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x40);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		usleep_range(20000, 20100);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x80, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x40, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL, 0x08, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x80, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x40, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL, 0x20, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x10, 0x00);
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_hphr_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (msm8x16_wcd->hph_mode == HD2_MODE) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x14);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0x10);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x80);
+		}
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x02);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x02);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x01);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x02, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL, 0x01, 0x00);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL, 0x02, 0x00);
+		if (msm8x16_wcd->hph_mode == HD2_MODE) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B3_CTL, 0x1C, 0x00);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B4_CTL, 0x18, 0xFF);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B3_CTL, 0x80, 0x00);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_hph_pa_event(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: %s event = %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (w->shift == 5)
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_PRE_HPHL_PA_ON);
+		else if (w->shift == 4)
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_PRE_HPHR_PA_ON);
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x20, 0x20);
+		break;
+
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(7000, 7100);
+		if (w->shift == 5) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x04);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00);
+		} else if (w->shift == 4) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x04);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x00);
+		}
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		if (w->shift == 5) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01);
+			msleep(20);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST, 0x04, 0x00);
+			msm8x16_wcd->mute_mask |= HPHL_PA_DISABLE;
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_PRE_HPHL_PA_OFF);
+		} else if (w->shift == 4) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_RX2_B6_CTL, 0x01, 0x01);
+			msleep(20);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST, 0x04, 0x00);
+			msm8x16_wcd->mute_mask |= HPHR_PA_DISABLE;
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_PRE_HPHR_PA_OFF);
+		}
+		if (get_codec_version(msm8x16_wcd) >= CAJON) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP,
+				0xF0, 0x30);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (w->shift == 5) {
+			clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK,
+				&msm8x16_wcd->mbhc.hph_pa_dac_state);
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_POST_HPHL_PA_OFF);
+		} else if (w->shift == 4) {
+			clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK,
+				&msm8x16_wcd->mbhc.hph_pa_dac_state);
+			msm8x16_notifier_call(codec,
+					WCD_EVENT_POST_HPHR_PA_OFF);
+		}
+		usleep_range(4000, 4100);
+		usleep_range(CODEC_DELAY_1_MS, CODEC_DELAY_1_1_MS);
+
+		dev_err(codec->dev,
+			"%s: sleep 10 ms after %s PA disable.\n", __func__,
+			w->name);
+		usleep_range(10000, 10100);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	{"RX_I2S_CLK", NULL, "CDC_CONN"},
+	{"I2S RX1", NULL, "RX_I2S_CLK"},
+	{"I2S RX2", NULL, "RX_I2S_CLK"},
+	{"I2S RX3", NULL, "RX_I2S_CLK"},
+
+	{"I2S TX1", NULL, "TX_I2S_CLK"},
+	{"I2S TX2", NULL, "TX_I2S_CLK"},
+	{"AIF2 VI", NULL, "TX_I2S_CLK"},
+
+	{"I2S TX1", NULL, "DEC1 MUX"},
+	{"I2S TX2", NULL, "DEC2 MUX"},
+	{"AIF2 VI", NULL, "DEC3 MUX"},
+	{"AIF2 VI", NULL, "DEC4 MUX"},
+
+	/* RDAC Connections */
+	{"HPHR DAC", NULL, "RDAC2 MUX"},
+	{"RDAC2 MUX", "RX1", "RX1 CHAIN"},
+	{"RDAC2 MUX", "RX2", "RX2 CHAIN"},
+
+	/* WSA */
+	{"WSA_SPK OUT", NULL, "WSA Spk Switch"},
+	{"WSA Spk Switch", "WSA", "EAR PA"},
+
+	/* Earpiece (RX MIX1) */
+	{"EAR", NULL, "EAR_S"},
+	{"EAR_S", "Switch", "EAR PA"},
+	{"EAR PA", NULL, "RX_BIAS"},
+	{"EAR PA", NULL, "HPHL DAC"},
+	{"EAR PA", NULL, "HPHR DAC"},
+	{"EAR PA", NULL, "EAR CP"},
+
+	/* Headset (RX MIX1 and RX MIX2) */
+	{"HEADPHONE", NULL, "HPHL PA"},
+	{"HEADPHONE", NULL, "HPHR PA"},
+
+	{"Ext Spk", NULL, "Ext Spk Switch"},
+	{"Ext Spk Switch", "On", "HPHL PA"},
+	{"Ext Spk Switch", "On", "HPHR PA"},
+
+	{"HPHL PA", NULL, "HPHL"},
+	{"HPHR PA", NULL, "HPHR"},
+	{"HPHL", "Switch", "HPHL DAC"},
+	{"HPHR", "Switch", "HPHR DAC"},
+	{"HPHL PA", NULL, "CP"},
+	{"HPHL PA", NULL, "RX_BIAS"},
+	{"HPHR PA", NULL, "CP"},
+	{"HPHR PA", NULL, "RX_BIAS"},
+	{"HPHL DAC", NULL, "RX1 CHAIN"},
+
+	{"SPK_OUT", NULL, "SPK PA"},
+	{"SPK PA", NULL, "SPK_RX_BIAS"},
+	{"SPK PA", NULL, "SPK"},
+	{"SPK", "Switch", "SPK DAC"},
+	{"SPK DAC", NULL, "RX3 CHAIN"},
+	{"SPK DAC", NULL, "VDD_SPKDRV"},
+
+	/* lineout */
+	{"LINEOUT", NULL, "LINEOUT PA"},
+	{"LINEOUT PA", NULL, "SPK_RX_BIAS"},
+	{"LINEOUT PA", NULL, "LINE_OUT"},
+	{"LINE_OUT", "Switch", "LINEOUT DAC"},
+	{"LINEOUT DAC", NULL, "RX3 CHAIN"},
+
+	/* lineout to WSA */
+	{"WSA_SPK OUT", NULL, "LINEOUT PA"},
+
+	{"RX1 CHAIN", NULL, "RX1 CLK"},
+	{"RX2 CHAIN", NULL, "RX2 CLK"},
+	{"RX3 CHAIN", NULL, "RX3 CLK"},
+	{"RX1 CHAIN", NULL, "RX1 MIX2"},
+	{"RX2 CHAIN", NULL, "RX2 MIX2"},
+	{"RX3 CHAIN", NULL, "RX3 MIX1"},
+
+	{"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
+	{"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+	{"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
+	{"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
+	{"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
+	{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+	{"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+	{"RX1 MIX2", NULL, "RX1 MIX1"},
+	{"RX1 MIX2", NULL, "RX1 MIX2 INP1"},
+	{"RX2 MIX2", NULL, "RX2 MIX1"},
+	{"RX2 MIX2", NULL, "RX2 MIX2 INP1"},
+
+	{"RX1 MIX1 INP1", "RX1", "I2S RX1"},
+	{"RX1 MIX1 INP1", "RX2", "I2S RX2"},
+	{"RX1 MIX1 INP1", "RX3", "I2S RX3"},
+	{"RX1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX1 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX1 MIX1 INP2", "RX1", "I2S RX1"},
+	{"RX1 MIX1 INP2", "RX2", "I2S RX2"},
+	{"RX1 MIX1 INP2", "RX3", "I2S RX3"},
+	{"RX1 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX1 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX1 MIX1 INP3", "RX1", "I2S RX1"},
+	{"RX1 MIX1 INP3", "RX2", "I2S RX2"},
+	{"RX1 MIX1 INP3", "RX3", "I2S RX3"},
+
+	{"RX2 MIX1 INP1", "RX1", "I2S RX1"},
+	{"RX2 MIX1 INP1", "RX2", "I2S RX2"},
+	{"RX2 MIX1 INP1", "RX3", "I2S RX3"},
+	{"RX2 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX2 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX2 MIX1 INP2", "RX1", "I2S RX1"},
+	{"RX2 MIX1 INP2", "RX2", "I2S RX2"},
+	{"RX2 MIX1 INP2", "RX3", "I2S RX3"},
+	{"RX2 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX2 MIX1 INP2", "IIR2", "IIR2"},
+
+	{"RX3 MIX1 INP1", "RX1", "I2S RX1"},
+	{"RX3 MIX1 INP1", "RX2", "I2S RX2"},
+	{"RX3 MIX1 INP1", "RX3", "I2S RX3"},
+	{"RX3 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX3 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX3 MIX1 INP2", "RX1", "I2S RX1"},
+	{"RX3 MIX1 INP2", "RX2", "I2S RX2"},
+	{"RX3 MIX1 INP2", "RX3", "I2S RX3"},
+	{"RX3 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX3 MIX1 INP2", "IIR2", "IIR2"},
+
+	{"RX1 MIX2 INP1", "IIR1", "IIR1"},
+	{"RX2 MIX2 INP1", "IIR1", "IIR1"},
+	{"RX1 MIX2 INP1", "IIR2", "IIR2"},
+	{"RX2 MIX2 INP1", "IIR2", "IIR2"},
+
+	/* Decimator Inputs */
+	{"DEC1 MUX", "DMIC1", "DMIC1"},
+	{"DEC1 MUX", "DMIC2", "DMIC2"},
+	{"DEC1 MUX", "ADC1", "ADC1"},
+	{"DEC1 MUX", "ADC2", "ADC2"},
+	{"DEC1 MUX", "ADC3", "ADC3"},
+	{"DEC1 MUX", NULL, "CDC_CONN"},
+
+	{"DEC2 MUX", "DMIC1", "DMIC1"},
+	{"DEC2 MUX", "DMIC2", "DMIC2"},
+	{"DEC2 MUX", "ADC1", "ADC1"},
+	{"DEC2 MUX", "ADC2", "ADC2"},
+	{"DEC2 MUX", "ADC3", "ADC3"},
+	{"DEC2 MUX", NULL, "CDC_CONN"},
+
+	{"DEC3 MUX", "DMIC3", "DMIC3"},
+	{"DEC4 MUX", "DMIC4", "DMIC4"},
+	{"DEC3 MUX", NULL, "CDC_CONN"},
+	{"DEC4 MUX", NULL, "CDC_CONN"},
+	/* ADC Connections */
+	{"ADC2", NULL, "ADC2 MUX"},
+	{"ADC3", NULL, "ADC2 MUX"},
+	{"ADC2 MUX", "INP2", "ADC2_INP2"},
+	{"ADC2 MUX", "INP3", "ADC2_INP3"},
+
+	{"ADC1", NULL, "AMIC1"},
+	{"ADC2_INP2", NULL, "AMIC2"},
+	{"ADC2_INP3", NULL, "AMIC3"},
+
+	/* TODO: Fix this */
+	{"IIR1", NULL, "IIR1 INP1 MUX"},
+	{"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR2", NULL, "IIR2 INP1 MUX"},
+	{"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"},
+	{"MIC BIAS Internal1", NULL, "INT_LDO_H"},
+	{"MIC BIAS Internal2", NULL, "INT_LDO_H"},
+	{"MIC BIAS External", NULL, "INT_LDO_H"},
+	{"MIC BIAS External2", NULL, "INT_LDO_H"},
+	{"MIC BIAS Internal1", NULL, "MICBIAS_REGULATOR"},
+	{"MIC BIAS Internal2", NULL, "MICBIAS_REGULATOR"},
+	{"MIC BIAS External", NULL, "MICBIAS_REGULATOR"},
+	{"MIC BIAS External2", NULL, "MICBIAS_REGULATOR"},
+};
+
+static int msm8x16_wcd_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd =
+		snd_soc_codec_get_drvdata(dai->codec);
+
+	dev_err(dai->codec->dev, "%s(): substream = %s  stream = %d\n",
+		__func__,
+		substream->name, substream->stream);
+	/*
+	 * If status_mask is BU_DOWN it means SSR is not complete.
+	 * So retun error.
+	 */
+	if (test_bit(BUS_DOWN, &msm8x16_wcd->status_mask)) {
+		dev_err(dai->codec->dev, "Error, Device is not up post SSR\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void msm8x16_wcd_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	dev_err(dai->codec->dev,
+		"%s(): substream = %s  stream = %d\n", __func__,
+		substream->name, substream->stream);
+}
+
+int msm8x16_wcd_mclk_enable(struct snd_soc_codec *codec,
+			    int mclk_enable, bool dapm)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: mclk_enable = %u, dapm = %d\n",
+		__func__, mclk_enable, dapm);
+	if (mclk_enable) {
+		msm8x16_wcd->int_mclk0_enabled = true;
+		msm8x16_wcd_codec_enable_clock_block(codec, 1);
+	} else {
+		if (!msm8x16_wcd->int_mclk0_enabled) {
+			dev_err(codec->dev, "Error, MCLK already diabled\n");
+			return -EINVAL;
+		}
+		msm8x16_wcd->int_mclk0_enabled = false;
+		msm8x16_wcd_codec_enable_clock_block(codec, 0);
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_set_dai_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	dev_err(dai->codec->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int msm8x16_wcd_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	dev_err(dai->codec->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int msm8x16_wcd_set_channel_map(struct snd_soc_dai *dai,
+				unsigned int tx_num, unsigned int *tx_slot,
+				unsigned int rx_num, unsigned int *rx_slot)
+
+{
+	dev_err(dai->codec->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int msm8x16_wcd_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+	dev_err(dai->codec->dev, "%s\n", __func__);
+	return 0;
+}
+
+static int msm8x16_wcd_set_interpolator_rate(struct snd_soc_dai *dai,
+	u8 rx_fs_rate_reg_val, u32 sample_rate)
+{
+	snd_soc_update_bits_wrapper(dai->codec,
+			MSM89XX_CDC_CORE_RX1_B5_CTL, 0xF0, rx_fs_rate_reg_val);
+	snd_soc_update_bits_wrapper(dai->codec,
+			MSM89XX_CDC_CORE_RX2_B5_CTL, 0xF0, rx_fs_rate_reg_val);
+	return 0;
+}
+
+static int msm8x16_wcd_set_decimator_rate(struct snd_soc_dai *dai,
+	u8 tx_fs_rate_reg_val, u32 sample_rate)
+{
+	return 0;
+}
+
+static int msm8x16_wcd_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	u8 tx_fs_rate, rx_fs_rate, rx_clk_fs_rate;
+	int ret;
+
+	dev_err(dai->codec->dev,
+		"%s: dai_name = %s DAI-ID %x rate %d num_ch %d format %d\n",
+		__func__, dai->name, dai->id, params_rate(params),
+		params_channels(params), params_format(params));
+
+	switch (params_rate(params)) {
+	case 8000:
+		tx_fs_rate = 0x00;
+		rx_fs_rate = 0x00;
+		rx_clk_fs_rate = 0x00;
+		break;
+	case 16000:
+		tx_fs_rate = 0x20;
+		rx_fs_rate = 0x20;
+		rx_clk_fs_rate = 0x01;
+		break;
+	case 32000:
+		tx_fs_rate = 0x40;
+		rx_fs_rate = 0x40;
+		rx_clk_fs_rate = 0x02;
+		break;
+	case 48000:
+		tx_fs_rate = 0x60;
+		rx_fs_rate = 0x60;
+		rx_clk_fs_rate = 0x03;
+		break;
+	case 96000:
+		tx_fs_rate = 0x80;
+		rx_fs_rate = 0x80;
+		rx_clk_fs_rate = 0x04;
+		break;
+	case 192000:
+		tx_fs_rate = 0xA0;
+		rx_fs_rate = 0xA0;
+		rx_clk_fs_rate = 0x05;
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: Invalid sampling rate %d\n", __func__,
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits_wrapper(dai->codec,
+			MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x0F, rx_clk_fs_rate);
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		ret = msm8x16_wcd_set_decimator_rate(dai, tx_fs_rate,
+					       params_rate(params));
+		if (ret < 0) {
+			dev_err(dai->codec->dev,
+				"%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
+		}
+		break;
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ret = msm8x16_wcd_set_interpolator_rate(dai, rx_fs_rate,
+						  params_rate(params));
+		if (ret < 0) {
+			dev_err(dai->codec->dev,
+				"%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
+		}
+		break;
+	default:
+		dev_err(dai->codec->dev,
+			"%s: Invalid stream type %d\n", __func__,
+			substream->stream);
+		return -EINVAL;
+	}
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		snd_soc_update_bits_wrapper(dai->codec,
+				MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x20);
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		snd_soc_update_bits_wrapper(dai->codec,
+				MSM89XX_CDC_CORE_CLK_RX_I2S_CTL, 0x20, 0x00);
+		break;
+	default:
+		dev_err(dai->codec->dev, "%s: wrong format selected\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int msm8x16_wcd_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+	struct snd_soc_codec *codec = NULL;
+	u16 tx_vol_ctl_reg = 0;
+	u8 decimator = 0, i;
+	struct msm8x16_wcd_priv *msm8x16_wcd;
+
+	pr_err("%s: Digital Mute val = %d\n", __func__, mute);
+
+	if (!dai || !dai->codec) {
+		pr_err("%s: Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	codec = dai->codec;
+	msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	if ((dai->id != AIF1_CAP) && (dai->id != AIF2_VIFEED)) {
+		dev_err(codec->dev, "%s: Not capture use case skip\n",
+		__func__);
+		return 0;
+	}
+
+	mute = (mute) ? 1 : 0;
+	if (!mute) {
+		/*
+		 * 15 ms is an emperical value for the mute time
+		 * that was arrived by checking the pop level
+		 * to be inaudible
+		 */
+		usleep_range(15000, 15010);
+	}
+
+	for (i = 0; i < NUM_DECIMATORS; i++) {
+		if (msm8x16_wcd->dec_active[i])
+			decimator = i + 1;
+		if (decimator && decimator <= NUM_DECIMATORS) {
+			pr_err("%s: Mute = %d Decimator = %d", __func__,
+					mute, decimator);
+			tx_vol_ctl_reg = MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG +
+				32 * (decimator - 1);
+			snd_soc_update_bits_wrapper(codec,
+				tx_vol_ctl_reg, 0x01, mute);
+		}
+		decimator = 0;
+	}
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm8x16_wcd_dai_ops = {
+	.startup = msm8x16_wcd_startup,
+	.shutdown = msm8x16_wcd_shutdown,
+	.hw_params = msm8x16_wcd_hw_params,
+	.set_sysclk = msm8x16_wcd_set_dai_sysclk,
+	.set_fmt = msm8x16_wcd_set_dai_fmt,
+	.set_channel_map = msm8x16_wcd_set_channel_map,
+	.get_channel_map = msm8x16_wcd_get_channel_map,
+	.digital_mute = msm8x16_wcd_digital_mute,
+};
+
+static struct snd_soc_dai_driver msm8x16_wcd_i2s_dai[] = {
+	{
+		.name = "msm8x16_wcd_i2s_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = MSM89XX_RATES,
+			.formats = MSM89XX_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 3,
+		},
+		.ops = &msm8x16_wcd_dai_ops,
+	},
+	{
+		.name = "msm8x16_wcd_i2s_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = MSM89XX_RATES,
+			.formats = MSM89XX_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &msm8x16_wcd_dai_ops,
+	},
+	{
+		.name = "cajon_vifeedback",
+		.id = AIF2_VIFEED,
+		.capture = {
+			.stream_name = "VIfeed",
+			.rates = MSM89XX_RATES,
+			.formats = MSM89XX_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 48000,
+			.channels_min = 2,
+			.channels_max = 2,
+		},
+		.ops = &msm8x16_wcd_dai_ops,
+	},
+};
+
+static int msm8x16_wcd_codec_enable_rx_chain(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dev_err(codec->dev,
+			"%s: PMU:Sleeping 20ms after disabling mute\n",
+			__func__);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		dev_err(codec->dev,
+			"%s: PMD:Sleeping 20ms after disabling mute\n",
+			__func__);
+		snd_soc_update_bits_wrapper(codec, w->reg,
+			    1 << w->shift, 0x00);
+		msleep(20);
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_lo_pa(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_err(codec->dev, "%s: %d %s\n", __func__, event, w->name);
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_RX3_B6_CTL, 0x01, 0x00);
+		break;
+	}
+
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_spk_ext_pa(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	dev_err(codec->dev, "%s: %s event = %d\n", __func__, w->name, event);
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dev_err(codec->dev,
+			"%s: enable external speaker PA\n", __func__);
+		if (msm8x16_wcd->codec_spk_ext_pa_cb)
+			msm8x16_wcd->codec_spk_ext_pa_cb(codec, 1);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		dev_err(codec->dev,
+			"%s: enable external speaker PA\n", __func__);
+		if (msm8x16_wcd->codec_spk_ext_pa_cb)
+			msm8x16_wcd->codec_spk_ext_pa_cb(codec, 0);
+		break;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		dev_err(codec->dev,
+			"%s: Sleeping 20ms after select EAR PA\n",
+			__func__);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x80, 0x80);
+		if (get_codec_version(msm8x16_wcd) < CONGA)
+			snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x2A);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		dev_err(codec->dev,
+			"%s: Sleeping 20ms after enabling EAR PA\n",
+			__func__);
+		snd_soc_update_bits_wrapper(codec,
+			 MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x40, 0x40);
+		usleep_range(7000, 7100);
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x00);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits_wrapper(codec,
+			MSM89XX_CDC_CORE_RX1_B6_CTL, 0x01, 0x01);
+		msleep(20);
+		msm8x16_wcd->mute_mask |= EAR_PA_DISABLE;
+		if (msm8x16_wcd->boost_option == BOOST_ALWAYS) {
+			dev_err(codec->dev,
+				"%s: boost_option:%d, tear down ear\n",
+				__func__, msm8x16_wcd->boost_option);
+			msm8x16_wcd_boost_mode_sequence(codec, EAR_PMD);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		dev_err(codec->dev,
+			"%s: Sleeping 7ms after disabling EAR PA\n",
+			__func__);
+		snd_soc_update_bits_wrapper(codec,
+			 MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x40, 0x00);
+		usleep_range(7000, 7100);
+		if (get_codec_version(msm8x16_wcd) < CONGA)
+			snd_soc_update_bits_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME, 0xFF, 0x16);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget msm8x16_wcd_dapm_widgets[] = {
+	/*RX stuff */
+	SND_SOC_DAPM_OUTPUT("EAR"),
+	SND_SOC_DAPM_OUTPUT("WSA_SPK OUT"),
+
+	SND_SOC_DAPM_PGA_E("EAR PA", SND_SOC_NOPM,
+			0, 0, NULL, 0, msm8x16_wcd_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_MUX("EAR_S", SND_SOC_NOPM, 0, 0,
+		ear_pa_mux),
+
+	SND_SOC_DAPM_MUX("WSA Spk Switch", SND_SOC_NOPM, 0, 0,
+		wsa_spk_mux),
+
+	SND_SOC_DAPM_AIF_IN("I2S RX1", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_AIF_IN("I2S RX2", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_AIF_IN("I2S RX3", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_SUPPLY("INT_LDO_H", SND_SOC_NOPM, 1, 0, NULL, 0),
+
+	SND_SOC_DAPM_SPK("Ext Spk", msm8x16_wcd_codec_enable_spk_ext_pa),
+
+	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+	SND_SOC_DAPM_PGA_E("HPHL PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+		5, 0, NULL, 0,
+		msm8x16_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("HPHL", SND_SOC_NOPM, 0, 0,
+		hphl_mux),
+
+	SND_SOC_DAPM_MIXER_E("HPHL DAC",
+		MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 3, 0, NULL,
+		0, msm8x16_wcd_hphl_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PGA_E("HPHR PA", MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+		4, 0, NULL, 0,
+		msm8x16_wcd_hph_pa_event, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("HPHR", SND_SOC_NOPM, 0, 0,
+		hphr_mux),
+
+	SND_SOC_DAPM_MIXER_E("HPHR DAC",
+		MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 3, 0, NULL,
+		0, msm8x16_wcd_hphr_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SPK", SND_SOC_NOPM, 0, 0,
+		spkr_mux),
+
+	SND_SOC_DAPM_DAC("SPK DAC", NULL,
+		MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 7, 0),
+
+	SND_SOC_DAPM_MUX("LINE_OUT",
+		SND_SOC_NOPM, 0, 0, lo_mux),
+
+	SND_SOC_DAPM_DAC_E("LINEOUT DAC", NULL,
+		SND_SOC_NOPM, 0, 0, msm8x16_wcd_lo_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	/* Speaker */
+	SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+
+	/* Lineout */
+	SND_SOC_DAPM_OUTPUT("LINEOUT"),
+
+	SND_SOC_DAPM_PGA_E("SPK PA", MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+			6, 0, NULL, 0, msm8x16_wcd_codec_enable_spk_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("LINEOUT PA", MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL,
+			5, 0, NULL, 0, msm8x16_wcd_codec_enable_lo_pa,
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0,
+			    msm89xx_wcd_codec_enable_vdd_spkr,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("Ext Spk Switch", SND_SOC_NOPM, 0, 0,
+		&ext_spk_mux),
+
+	SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER_E("RX1 MIX2",
+		MSM89XX_CDC_CORE_CLK_RX_B1_CTL, MSM89XX_RX1, 0, NULL,
+		0, msm8x16_wcd_codec_enable_interpolator,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX2 MIX2",
+		MSM89XX_CDC_CORE_CLK_RX_B1_CTL, MSM89XX_RX2, 0, NULL,
+		0, msm8x16_wcd_codec_enable_interpolator,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX3 MIX1",
+		MSM89XX_CDC_CORE_CLK_RX_B1_CTL, MSM89XX_RX3, 0, NULL,
+		0, msm8x16_wcd_codec_enable_interpolator,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("RX1 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+		0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("RX2 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+		1, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("RX3 CLK", MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+		2, 0, msm8x16_wcd_codec_enable_dig_clk, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX1 CHAIN", MSM89XX_CDC_CORE_RX1_B6_CTL, 0, 0,
+		NULL, 0,
+		msm8x16_wcd_codec_enable_rx_chain,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX2 CHAIN", MSM89XX_CDC_CORE_RX2_B6_CTL, 0, 0,
+		NULL, 0,
+		msm8x16_wcd_codec_enable_rx_chain,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX3 CHAIN", MSM89XX_CDC_CORE_RX3_B6_CTL, 0, 0,
+		NULL, 0,
+		msm8x16_wcd_codec_enable_rx_chain,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+		&rx_mix1_inp3_mux),
+
+	SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx2_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx2_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+		&rx2_mix1_inp3_mux),
+
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp3_mux),
+
+	SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+		&rx1_mix2_inp1_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+		&rx2_mix2_inp1_mux),
+
+	SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM,
+		ON_DEMAND_MICBIAS, 0,
+		msm8x16_wcd_codec_enable_on_demand_supply,
+		SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("CP", MSM89XX_PMIC_ANALOG_NCP_EN, 0, 0,
+		msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU |	SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("EAR CP", MSM89XX_PMIC_ANALOG_NCP_EN, 4, 0,
+		msm8x16_wcd_codec_enable_charge_pump, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY_S("RX_BIAS", 1, SND_SOC_NOPM,
+		0, 0, msm8x16_wcd_codec_enable_rx_bias,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY_S("SPK_RX_BIAS", 1, SND_SOC_NOPM, 0, 0,
+		msm8x16_wcd_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	/* TX */
+
+	SND_SOC_DAPM_SUPPLY_S("CDC_CONN", -2, MSM89XX_CDC_CORE_CLK_OTHR_CTL,
+		2, 0, NULL, 0),
+
+
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal1",
+		MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0,
+		msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal2",
+		MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0,
+		msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS Internal3",
+		MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0,
+		msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC1", NULL, MSM89XX_PMIC_ANALOG_TX_1_EN, 7, 0,
+		msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC2_INP2",
+		NULL, MSM89XX_PMIC_ANALOG_TX_2_EN, 7, 0,
+		msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC2_INP3",
+		NULL, MSM89XX_PMIC_ANALOG_TX_3_EN, 7, 0,
+		msm8x16_wcd_codec_enable_adc, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MUX("ADC2 MUX", SND_SOC_NOPM, 0, 0,
+		&tx_adc2_mux),
+
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS External",
+		MSM89XX_PMIC_ANALOG_MICB_1_EN, 7, 0,
+		msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS External2",
+		MSM89XX_PMIC_ANALOG_MICB_2_EN, 7, 0,
+		msm8x16_wcd_codec_enable_micbias, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+
+	SND_SOC_DAPM_INPUT("AMIC3"),
+
+	SND_SOC_DAPM_MUX_E("DEC1 MUX",
+		MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+		&dec1_mux, msm8x16_wcd_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC2 MUX",
+		MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 1, 0,
+		&dec2_mux, msm8x16_wcd_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC3 MUX",
+		MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 2, 0,
+		&dec3_mux, msm8x16_wcd_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC4 MUX",
+		MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL, 3, 0,
+		&dec4_mux, msm8x16_wcd_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("RDAC2 MUX", SND_SOC_NOPM, 0, 0, &rdac2_mux),
+
+	SND_SOC_DAPM_INPUT("AMIC2"),
+
+	SND_SOC_DAPM_AIF_OUT("I2S TX1", "AIF1 Capture", 0, SND_SOC_NOPM,
+		0, 0),
+	SND_SOC_DAPM_AIF_OUT("I2S TX2", "AIF1 Capture", 0, SND_SOC_NOPM,
+		0, 0),
+	SND_SOC_DAPM_AIF_OUT("I2S TX3", "AIF1 Capture", 0, SND_SOC_NOPM,
+		0, 0),
+
+	SND_SOC_DAPM_AIF_OUT("AIF2 VI", "VIfeed", 0, SND_SOC_NOPM,
+		0, 0),
+	/* Digital Mic Inputs */
+	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+		msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+		msm8x16_wcd_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_INPUT("DMIC3"),
+
+	SND_SOC_DAPM_INPUT("DMIC4"),
+
+	/* Sidetone */
+	SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+	SND_SOC_DAPM_PGA_E("IIR1", MSM89XX_CDC_CORE_CLK_SD_CTL, 0, 0, NULL, 0,
+		msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
+	SND_SOC_DAPM_PGA_E("IIR2", MSM89XX_CDC_CORE_CLK_SD_CTL, 1, 0, NULL, 0,
+		msm8x16_wcd_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_SUPPLY("RX_I2S_CLK",
+		MSM89XX_CDC_CORE_CLK_RX_I2S_CTL,	4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("TX_I2S_CLK",
+		MSM89XX_CDC_CORE_CLK_TX_I2S_CTL, 4, 0,
+		NULL, 0),
+};
+
+static const struct msm8x16_wcd_reg_mask_val msm8x16_wcd_reg_defaults[] = {
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+};
+
+static const struct msm8x16_wcd_reg_mask_val msm8x16_wcd_reg_defaults_2_0[] = {
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BOOST_EN_CTL, 0x5F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO, 0x88),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static const struct msm8x16_wcd_reg_mask_val msm8909_wcd_reg_defaults[] = {
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0x28),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE, 0x0A),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static const struct msm8x16_wcd_reg_mask_val cajon_wcd_reg_defaults[] = {
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0x82),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static const struct msm8x16_wcd_reg_mask_val cajon2p0_wcd_reg_defaults[] = {
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SEC_ACCESS, 0xA5),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3, 0x0F),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS, 0x4C),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_CURRENT_LIMIT, 0xA2),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_FBCTRL, 0xA8),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_NCP_VCTRL, 0xA4),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET, 0x41),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL, 0x69),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG, 0x01),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL, 0xE1),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x03),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_EAR_STATUS, 0x10),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_BYPASS_MODE, 0x18),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA, 0xFA),
+	MSM89XX_REG_VAL(MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80),
+};
+
+static void msm8x16_wcd_update_reg_defaults(struct snd_soc_codec *codec)
+{
+	u32 i, version;
+	struct msm8x16_wcd_priv *msm8x16_wcd = snd_soc_codec_get_drvdata(codec);
+
+	version = get_codec_version(msm8x16_wcd);
+	if (version == TOMBAK_1_0) {
+		for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_reg_defaults); i++)
+			snd_soc_write_wrapper(codec,
+				msm8x16_wcd_reg_defaults[i].reg,
+				msm8x16_wcd_reg_defaults[i].val);
+	} else if (version == TOMBAK_2_0) {
+		for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_reg_defaults_2_0); i++)
+			snd_soc_write_wrapper(codec,
+				msm8x16_wcd_reg_defaults_2_0[i].reg,
+				msm8x16_wcd_reg_defaults_2_0[i].val);
+	} else if (version == CONGA) {
+		for (i = 0; i < ARRAY_SIZE(msm8909_wcd_reg_defaults); i++)
+			snd_soc_write_wrapper(codec,
+				msm8909_wcd_reg_defaults[i].reg,
+				msm8909_wcd_reg_defaults[i].val);
+	} else if (version == CAJON) {
+		for (i = 0; i < ARRAY_SIZE(cajon_wcd_reg_defaults); i++)
+			snd_soc_write_wrapper(codec,
+				cajon_wcd_reg_defaults[i].reg,
+				cajon_wcd_reg_defaults[i].val);
+	} else if (version == CAJON_2_0 || version == DIANGU) {
+		for (i = 0; i < ARRAY_SIZE(cajon2p0_wcd_reg_defaults); i++)
+			snd_soc_write_wrapper(codec,
+				cajon2p0_wcd_reg_defaults[i].reg,
+				cajon2p0_wcd_reg_defaults[i].val);
+	}
+}
+
+static const struct msm8x16_wcd_reg_mask_val
+	msm8x16_wcd_codec_reg_init_val[] = {
+
+	/* Initialize current threshold to 350MA
+	 * number of wait and run cycles to 4096
+	 */
+	{MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL, 0xFF, 0x12},
+	{MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT, 0xFF, 0xFF},
+};
+
+static void msm8x16_wcd_codec_init_reg(struct snd_soc_codec *codec)
+{
+	u32 i;
+
+	for (i = 0; i < ARRAY_SIZE(msm8x16_wcd_codec_reg_init_val); i++)
+		snd_soc_update_bits_wrapper(codec,
+			    msm8x16_wcd_codec_reg_init_val[i].reg,
+			    msm8x16_wcd_codec_reg_init_val[i].mask,
+			    msm8x16_wcd_codec_reg_init_val[i].val);
+}
+
+static int msm8x16_wcd_bringup(struct snd_soc_codec *codec)
+{
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_DIGITAL_SEC_ACCESS,
+		0xA5);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x01);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+		0xA5);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x01);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_DIGITAL_SEC_ACCESS,
+		0xA5);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4, 0x00);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SEC_ACCESS,
+		0xA5);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4, 0x00);
+	return 0;
+}
+
+static struct regulator *wcd8x16_wcd_codec_find_regulator(
+				const struct msm8x16_wcd *msm8x16,
+				const char *name)
+{
+	int i;
+
+	for (i = 0; i < msm8x16->num_of_supplies; i++) {
+		if (msm8x16->supplies[i].supply &&
+		    !strcmp(msm8x16->supplies[i].supply, name))
+			return msm8x16->supplies[i].consumer;
+	}
+
+	dev_err(msm8x16->dev, "Error: regulator not found:%s\n"
+				, name);
+	return NULL;
+}
+
+static int msm8x16_wcd_device_down(struct snd_soc_codec *codec)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+		snd_soc_codec_get_drvdata(codec);
+	int i;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	dev_err(codec->dev, "%s: device down!\n", __func__);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_TX_1_EN, 0x3);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_TX_2_EN, 0x3);
+	if (msm8x16_wcd_priv->boost_option == BOOST_ON_FOREVER) {
+		if ((snd_soc_read_wrapper(codec,
+			MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL)
+		& 0x80) == 0) {
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_MCLK_CTL,	0x01, 0x01);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_CDC_CORE_CLK_PDM_CTL, 0x03, 0x03);
+			snd_soc_write_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL, 0x30);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_RST_CTL, 0x80, 0x80);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL,
+				0x0C, 0x0C);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL,
+				0x84, 0x84);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL,
+				0x10, 0x10);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL,
+				0x1F, 0x1F);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC,
+				0x90, 0x90);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_RX_EAR_CTL,
+				0xFF, 0xFF);
+			usleep_range(20, 21);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL,
+				0xFF, 0xFF);
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+				0xE9, 0xE9);
+		}
+	}
+	msm8x16_wcd_boost_off(codec);
+	msm8x16_wcd_priv->hph_mode = NORMAL_MODE;
+	for (i = 0; i < MSM89XX_RX_MAX; i++)
+		msm8x16_wcd_priv->comp_enabled[i] = COMPANDER_NONE;
+
+	/* 40ms to allow boost to discharge */
+	msleep(40);
+	/* Disable PA to avoid pop during codec bring up */
+	snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN,
+			0x30, 0x00);
+	snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL,
+			0x80, 0x00);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL, 0x20);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL, 0x20);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_RX_EAR_CTL, 0x12);
+	snd_soc_write_wrapper(codec,
+		MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL, 0x93);
+
+	msm8x16_wcd_bringup(codec);
+	atomic_set(&pdata->int_mclk0_enabled, false);
+	set_bit(BUS_DOWN, &msm8x16_wcd_priv->status_mask);
+	snd_soc_card_change_online_state(codec->component.card, 0);
+	return 0;
+}
+
+static int msm8x16_wcd_device_up(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+		snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	dev_err(codec->dev, "%s: device up!\n", __func__);
+
+	clear_bit(BUS_DOWN, &msm8x16_wcd_priv->status_mask);
+
+	snd_soc_card_change_online_state(codec->component.card, 1);
+	/* delay is required to make sure sound card state updated */
+	usleep_range(5000, 5100);
+
+	msm8x16_wcd_codec_init_reg(codec);
+	msm8x16_wcd_update_reg_defaults(codec);
+
+	snd_soc_write_wrapper(codec, MSM89XX_PMIC_DIGITAL_INT_EN_SET,
+				MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR);
+	snd_soc_write_wrapper(codec, MSM89XX_PMIC_DIGITAL_INT_EN_CLR,
+				MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR);
+
+	msm8x16_wcd_set_boost_v(codec);
+
+	msm8x16_wcd_set_micb_v(codec);
+	if (msm8x16_wcd_priv->boost_option == BOOST_ON_FOREVER)
+		msm8x16_wcd_boost_on(codec);
+	else if (msm8x16_wcd_priv->boost_option == BYPASS_ALWAYS)
+		msm8x16_wcd_bypass_on(codec);
+
+	msm8x16_wcd_configure_cap(codec, false, false);
+	wcd_mbhc_stop(&msm8x16_wcd_priv->mbhc);
+	wcd_mbhc_deinit(&msm8x16_wcd_priv->mbhc);
+	ret = wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids,
+			wcd_mbhc_registers, true);
+	if (ret)
+		dev_err(codec->dev, "%s: mbhc initialization failed\n",
+			__func__);
+	else
+		wcd_mbhc_start(&msm8x16_wcd_priv->mbhc,
+			msm8x16_wcd_priv->mbhc.mbhc_cfg);
+
+
+	return 0;
+}
+
+static int adsp_state_callback(struct notifier_block *nb, unsigned long value,
+			       void *priv)
+{
+	bool timedout;
+	unsigned long timeout;
+
+	if (value == SUBSYS_BEFORE_SHUTDOWN)
+		msm8x16_wcd_device_down(registered_codec);
+	else if (value == SUBSYS_AFTER_POWERUP) {
+
+		dev_err(registered_codec->dev,
+			"ADSP is about to power up. bring up codec\n");
+
+		if (!q6core_is_adsp_ready()) {
+			dev_err(registered_codec->dev,
+				"ADSP isn't ready\n");
+			timeout = jiffies +
+				  msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+			while (!(timedout = time_after(jiffies, timeout))) {
+				if (!q6core_is_adsp_ready()) {
+					dev_err(registered_codec->dev,
+						"ADSP isn't ready\n");
+				} else {
+					dev_err(registered_codec->dev,
+						"ADSP is ready\n");
+					break;
+				}
+			}
+		} else {
+			dev_err(registered_codec->dev,
+				"%s: DSP is ready\n", __func__);
+		}
+
+		msm8x16_wcd_device_up(registered_codec);
+	}
+	return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+	.notifier_call = adsp_state_callback,
+	.priority = -INT_MAX,
+};
+
+int msm8x16_wcd_hs_detect(struct snd_soc_codec *codec,
+		    struct wcd_mbhc_config *mbhc_cfg)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+		snd_soc_codec_get_drvdata(codec);
+
+	return wcd_mbhc_start(&msm8x16_wcd_priv->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(msm8x16_wcd_hs_detect);
+
+void msm8x16_wcd_hs_detect_exit(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+		snd_soc_codec_get_drvdata(codec);
+
+	wcd_mbhc_stop(&msm8x16_wcd_priv->mbhc);
+}
+EXPORT_SYMBOL(msm8x16_wcd_hs_detect_exit);
+
+void msm8x16_update_int_spk_boost(bool enable)
+{
+	pr_err("%s: enable = %d\n", __func__, enable);
+	spkr_boost_en = enable;
+}
+EXPORT_SYMBOL(msm8x16_update_int_spk_boost);
+
+static void msm8x16_wcd_set_micb_v(struct snd_soc_codec *codec)
+{
+
+	struct msm8x16_wcd *msm8x16 = codec->control_data;
+	struct msm8x16_wcd_pdata *pdata = msm8x16->dev->platform_data;
+	u8 reg_val;
+
+	reg_val = VOLTAGE_CONVERTER(pdata->micbias.cfilt1_mv, MICBIAS_MIN_VAL,
+			MICBIAS_STEP_SIZE);
+	dev_err(codec->dev, "cfilt1_mv %d reg_val %x\n",
+			(u32)pdata->micbias.cfilt1_mv, reg_val);
+	snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_MICB_1_VAL,
+			0xF8, (reg_val << 3));
+}
+
+static void msm8x16_wcd_set_boost_v(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+				snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_update_bits_wrapper(codec, MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE,
+			0x1F, msm8x16_wcd_priv->boost_voltage);
+}
+
+static void msm8x16_wcd_configure_cap(struct snd_soc_codec *codec,
+		bool micbias1, bool micbias2)
+{
+
+	struct msm_asoc_mach_data *pdata = NULL;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+
+	pr_err("\n %s: micbias1 %x micbias2 = %d\n", __func__, micbias1,
+			micbias2);
+	if (micbias1 && micbias2) {
+		if ((pdata->micbias1_cap_mode
+		     == MICBIAS_EXT_BYP_CAP) ||
+		    (pdata->micbias2_cap_mode
+		     == MICBIAS_EXT_BYP_CAP))
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_EN,
+				0x40, (MICBIAS_EXT_BYP_CAP << 6));
+		else
+			snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_EN,
+				0x40, (MICBIAS_NO_EXT_BYP_CAP << 6));
+	} else if (micbias2) {
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_EN,
+				0x40, (pdata->micbias2_cap_mode << 6));
+	} else if (micbias1) {
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_EN,
+				0x40, (pdata->micbias1_cap_mode << 6));
+	} else {
+		snd_soc_update_bits_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_MICB_1_EN,
+				0x40, 0x00);
+	}
+}
+
+static int msm89xx_digcodec_probe(struct snd_soc_codec *codec)
+{
+	registered_digcodec = codec;
+
+	return 0;
+}
+
+
+static int msm89xx_digcodec_remove(struct snd_soc_codec *codec)
+{
+	return 0;
+}
+
+static int msm8x16_wcd_codec_probe(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv;
+	int i, ret;
+
+	dev_err(codec->dev, "%s()\n", __func__);
+
+	msm8x16_wcd_priv = kzalloc(sizeof(struct msm8x16_wcd_priv), GFP_KERNEL);
+	if (!msm8x16_wcd_priv)
+		return -ENOMEM;
+
+	for (i = 0; i < NUM_DECIMATORS; i++) {
+		tx_hpf_work[i].msm8x16_wcd = msm8x16_wcd_priv;
+		tx_hpf_work[i].decimator = i + 1;
+		INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
+			tx_hpf_corner_freq_callback);
+	}
+
+	codec->control_data = dev_get_drvdata(codec->dev);
+	snd_soc_codec_set_drvdata(codec, msm8x16_wcd_priv);
+	msm8x16_wcd_priv->codec = codec;
+
+	msm8x16_wcd_priv->spkdrv_reg =
+		wcd8x16_wcd_codec_find_regulator(codec->control_data,
+					MSM89XX_VDD_SPKDRV_NAME);
+	msm8x16_wcd_priv->pmic_rev = snd_soc_read_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_REVISION1);
+	msm8x16_wcd_priv->codec_version = snd_soc_read_wrapper(codec,
+				MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE);
+	if (msm8x16_wcd_priv->codec_version == CONGA) {
+		dev_err(codec->dev, "%s :Conga REV: %d\n", __func__,
+				msm8x16_wcd_priv->codec_version);
+		msm8x16_wcd_priv->ext_spk_boost_set = true;
+	} else {
+		dev_err(codec->dev, "%s :PMIC REV: %d\n", __func__,
+					msm8x16_wcd_priv->pmic_rev);
+		if (msm8x16_wcd_priv->pmic_rev == TOMBAK_1_0 &&
+			msm8x16_wcd_priv->codec_version == CAJON_2_0) {
+			msm8x16_wcd_priv->codec_version = DIANGU;
+			dev_err(codec->dev, "%s : Diangu detected\n",
+						__func__);
+		} else if (msm8x16_wcd_priv->pmic_rev == TOMBAK_1_0 &&
+			(snd_soc_read_wrapper(codec,
+				 MSM89XX_PMIC_ANALOG_NCP_FBCTRL)
+			 & 0x80)) {
+			msm8x16_wcd_priv->codec_version = CAJON;
+			dev_err(codec->dev, "%s : Cajon detected\n", __func__);
+		} else if (msm8x16_wcd_priv->pmic_rev == TOMBAK_2_0 &&
+			(snd_soc_read_wrapper(codec,
+				MSM89XX_PMIC_ANALOG_NCP_FBCTRL)
+			 & 0x80)) {
+			msm8x16_wcd_priv->codec_version = CAJON_2_0;
+			dev_err(codec->dev, "%s : Cajon 2.0 detected\n",
+						__func__);
+		}
+	}
+	/*
+	 * set to default boost option BOOST_SWITCH, user mixer path can change
+	 * it to BOOST_ALWAYS or BOOST_BYPASS based on solution chosen.
+	 */
+	msm8x16_wcd_priv->boost_option = BOOST_SWITCH;
+	msm8x16_wcd_priv->hph_mode = NORMAL_MODE;
+
+	for (i = 0; i < MSM89XX_RX_MAX; i++)
+		msm8x16_wcd_priv->comp_enabled[i] = COMPANDER_NONE;
+
+	msm8x16_wcd_dt_parse_boost_info(codec);
+	msm8x16_wcd_set_boost_v(codec);
+
+	snd_soc_add_codec_controls(codec, impedance_detect_controls,
+				   ARRAY_SIZE(impedance_detect_controls));
+	snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+				  ARRAY_SIZE(hph_type_detect_controls));
+
+	msm8x16_wcd_bringup(codec);
+	msm8x16_wcd_codec_init_reg(codec);
+	msm8x16_wcd_update_reg_defaults(codec);
+
+	wcd9xxx_spmi_set_codec(codec);
+
+	msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply =
+				wcd8x16_wcd_codec_find_regulator(
+				codec->control_data,
+				on_demand_supply_name[ON_DEMAND_MICBIAS]);
+	atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&msm8x16_wcd_priv->notifier);
+
+	msm8x16_wcd_priv->fw_data = kzalloc(sizeof(*(msm8x16_wcd_priv->fw_data))
+			, GFP_KERNEL);
+	if (!msm8x16_wcd_priv->fw_data) {
+		kfree(msm8x16_wcd_priv);
+		return -ENOMEM;
+	}
+
+	set_bit(WCD9XXX_MBHC_CAL, msm8x16_wcd_priv->fw_data->cal_bit);
+	ret = wcd_cal_create_hwdep(msm8x16_wcd_priv->fw_data,
+			WCD9XXX_CODEC_HWDEP_NODE, codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+		kfree(msm8x16_wcd_priv->fw_data);
+		kfree(msm8x16_wcd_priv);
+		return ret;
+	}
+
+	wcd_mbhc_init(&msm8x16_wcd_priv->mbhc, codec, &mbhc_cb, &intr_ids,
+		      wcd_mbhc_registers, true);
+
+	msm8x16_wcd_priv->int_mclk0_enabled = false;
+	msm8x16_wcd_priv->clock_active = false;
+	msm8x16_wcd_priv->config_mode_active = false;
+
+	/*Update speaker boost configuration*/
+	msm8x16_wcd_priv->spk_boost_set = spkr_boost_en;
+	pr_err("%s: speaker boost configured = %d\n",
+			__func__, msm8x16_wcd_priv->spk_boost_set);
+
+	/* Set initial MICBIAS voltage level */
+	msm8x16_wcd_set_micb_v(codec);
+
+	/* Set initial cap mode */
+	msm8x16_wcd_configure_cap(codec, false, false);
+	registered_codec = codec;
+	adsp_state_notifier =
+	    subsys_notif_register_notifier("adsp",
+					   &adsp_state_notifier_block);
+	if (!adsp_state_notifier) {
+		dev_err(codec->dev, "Failed to register adsp state notifier\n");
+		kfree(msm8x16_wcd_priv->fw_data);
+		kfree(msm8x16_wcd_priv);
+		registered_codec = NULL;
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static int msm8x16_wcd_codec_remove(struct snd_soc_codec *codec)
+{
+	struct msm8x16_wcd_priv *msm8x16_wcd_priv =
+					snd_soc_codec_get_drvdata(codec);
+	struct msm8x16_wcd *msm8x16_wcd;
+
+	msm8x16_wcd = codec->control_data;
+	msm8x16_wcd_priv->spkdrv_reg = NULL;
+	msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].supply = NULL;
+	atomic_set(&msm8x16_wcd_priv->on_demand_list[ON_DEMAND_MICBIAS].ref, 0);
+	kfree(msm8x16_wcd_priv->fw_data);
+	kfree(msm8x16_wcd_priv);
+
+	return 0;
+}
+
+static int msm8x16_wcd_enable_static_supplies_to_optimum(
+				struct msm8x16_wcd *msm8x16,
+				struct msm8x16_wcd_pdata *pdata)
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < msm8x16->num_of_supplies; i++) {
+		if (pdata->regulator[i].ondemand)
+			continue;
+		if (regulator_count_voltages(msm8x16->supplies[i].consumer) <=
+			0)
+			continue;
+
+		ret = regulator_set_voltage(msm8x16->supplies[i].consumer,
+			pdata->regulator[i].min_uv,
+			pdata->regulator[i].max_uv);
+		if (ret) {
+			dev_err(msm8x16->dev,
+				"Setting volt failed for regulator %s err %d\n",
+				msm8x16->supplies[i].supply, ret);
+		}
+
+		ret = regulator_set_load(msm8x16->supplies[i].consumer,
+			pdata->regulator[i].optimum_ua);
+		dev_err(msm8x16->dev, "Regulator %s set optimum mode\n",
+			 msm8x16->supplies[i].supply);
+	}
+
+	return ret;
+}
+
+static int msm8x16_wcd_disable_static_supplies_to_optimum(
+			struct msm8x16_wcd *msm8x16,
+			struct msm8x16_wcd_pdata *pdata)
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < msm8x16->num_of_supplies; i++) {
+		if (pdata->regulator[i].ondemand)
+			continue;
+		if (regulator_count_voltages(msm8x16->supplies[i].consumer) <=
+			0)
+			continue;
+		regulator_set_voltage(msm8x16->supplies[i].consumer, 0,
+			pdata->regulator[i].max_uv);
+		regulator_set_load(msm8x16->supplies[i].consumer, 0);
+		dev_err(msm8x16->dev, "Regulator %s set optimum mode\n",
+				 msm8x16->supplies[i].supply);
+	}
+
+	return ret;
+}
+
+int msm8x16_wcd_suspend(struct snd_soc_codec *codec)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct msm8x16_wcd *msm8x16 = codec->control_data;
+	struct msm8x16_wcd_pdata *msm8x16_pdata = msm8x16->dev->platform_data;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	pr_err("%s: mclk cnt = %d, mclk_enabled = %d\n",
+			__func__, atomic_read(&pdata->int_mclk0_rsc_ref),
+			atomic_read(&pdata->int_mclk0_enabled));
+	if (atomic_read(&pdata->int_mclk0_enabled) == true) {
+		cancel_delayed_work_sync(
+				&pdata->disable_int_mclk0_work);
+		mutex_lock(&pdata->cdc_int_mclk0_mutex);
+		pdata->digital_cdc_core_clk.enable = 0;
+		afe_set_lpass_clock_v2(AFE_PORT_ID_INT0_MI2S_RX,
+				       &pdata->digital_cdc_core_clk);
+		atomic_set(&pdata->int_mclk0_enabled, false);
+		mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+	}
+	msm8x16_wcd_disable_static_supplies_to_optimum(msm8x16, msm8x16_pdata);
+	return 0;
+}
+
+int msm8x16_wcd_resume(struct snd_soc_codec *codec)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct msm8x16_wcd *msm8x16 = codec->control_data;
+	struct msm8x16_wcd_pdata *msm8x16_pdata = msm8x16->dev->platform_data;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	msm8x16_wcd_enable_static_supplies_to_optimum(msm8x16, msm8x16_pdata);
+	return 0;
+}
+
+static struct regmap *msm89xx_pmic_cdc_regmap;
+static struct regmap *msm89xx_pmic_cdc_get_regmap(struct device *dev)
+{
+	return msm89xx_pmic_cdc_regmap;
+}
+
+static const struct snd_soc_codec_driver soc_codec_dev_msm8x16_wcd = {
+	.probe	= msm8x16_wcd_codec_probe,
+	.remove	= msm8x16_wcd_codec_remove,
+
+	.suspend = msm8x16_wcd_suspend,
+	.resume = msm8x16_wcd_resume,
+
+	.controls = msm8x16_wcd_snd_controls,
+	.num_controls = ARRAY_SIZE(msm8x16_wcd_snd_controls),
+	.dapm_widgets = msm8x16_wcd_dapm_widgets,
+	.num_dapm_widgets = ARRAY_SIZE(msm8x16_wcd_dapm_widgets),
+	.dapm_routes = audio_map,
+	.num_dapm_routes = ARRAY_SIZE(audio_map),
+	.get_regmap = msm89xx_pmic_cdc_get_regmap,
+};
+
+static int msm8x16_wcd_init_supplies(struct msm8x16_wcd *msm8x16,
+				struct msm8x16_wcd_pdata *pdata)
+{
+	int ret;
+	int i;
+
+	msm8x16->supplies = kzalloc(sizeof(struct regulator_bulk_data) *
+				   ARRAY_SIZE(pdata->regulator),
+				   GFP_KERNEL);
+	if (!msm8x16->supplies) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	msm8x16->num_of_supplies = 0;
+
+	if (ARRAY_SIZE(pdata->regulator) > MAX_REGULATOR) {
+		dev_err(msm8x16->dev, "%s: Array Size out of bound\n",
+			__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		if (pdata->regulator[i].name) {
+			msm8x16->supplies[i].supply = pdata->regulator[i].name;
+			msm8x16->num_of_supplies++;
+		}
+	}
+
+	ret = regulator_bulk_get(msm8x16->dev, msm8x16->num_of_supplies,
+				 msm8x16->supplies);
+	if (ret != 0) {
+		dev_err(msm8x16->dev, "Failed to get supplies: err = %d\n",
+							ret);
+		goto err_supplies;
+	}
+
+	for (i = 0; i < msm8x16->num_of_supplies; i++) {
+		if (regulator_count_voltages(msm8x16->supplies[i].consumer) <=
+			0)
+			continue;
+
+		ret = regulator_set_voltage(msm8x16->supplies[i].consumer,
+			pdata->regulator[i].min_uv,
+			pdata->regulator[i].max_uv);
+		if (ret) {
+			dev_err(msm8x16->dev, "Setting regulator voltage failed for regulator %s err = %d\n",
+				msm8x16->supplies[i].supply, ret);
+			goto err_get;
+		}
+
+		ret = regulator_set_load(msm8x16->supplies[i].consumer,
+			pdata->regulator[i].optimum_ua);
+		if (ret < 0) {
+			dev_err(msm8x16->dev, "Setting regulator optimum mode failed for regulator %s err = %d\n",
+				msm8x16->supplies[i].supply, ret);
+			goto err_get;
+		} else {
+			ret = 0;
+		}
+	}
+
+	return ret;
+
+err_get:
+	regulator_bulk_free(msm8x16->num_of_supplies, msm8x16->supplies);
+err_supplies:
+	kfree(msm8x16->supplies);
+err:
+	return ret;
+}
+
+static int msm8x16_wcd_enable_static_supplies(struct msm8x16_wcd *msm8x16,
+					  struct msm8x16_wcd_pdata *pdata)
+{
+	int i;
+	int ret = 0;
+
+	for (i = 0; i < msm8x16->num_of_supplies; i++) {
+		if (pdata->regulator[i].ondemand)
+			continue;
+		ret = regulator_enable(msm8x16->supplies[i].consumer);
+		if (ret) {
+			dev_err(msm8x16->dev, "Failed to enable %s\n",
+			       msm8x16->supplies[i].supply);
+			break;
+		}
+		dev_err(msm8x16->dev, "Enabled regulator %s\n",
+				 msm8x16->supplies[i].supply);
+	}
+
+	while (ret && --i)
+		if (!pdata->regulator[i].ondemand)
+			regulator_disable(msm8x16->supplies[i].consumer);
+
+	return ret;
+}
+
+
+
+static void msm8x16_wcd_disable_supplies(struct msm8x16_wcd *msm8x16,
+				     struct msm8x16_wcd_pdata *pdata)
+{
+	int i;
+
+	regulator_bulk_disable(msm8x16->num_of_supplies,
+				    msm8x16->supplies);
+	for (i = 0; i < msm8x16->num_of_supplies; i++) {
+		if (regulator_count_voltages(msm8x16->supplies[i].consumer) <=
+			0)
+			continue;
+		regulator_set_voltage(msm8x16->supplies[i].consumer, 0,
+			pdata->regulator[i].max_uv);
+		regulator_set_load(msm8x16->supplies[i].consumer, 0);
+	}
+	regulator_bulk_free(msm8x16->num_of_supplies, msm8x16->supplies);
+	kfree(msm8x16->supplies);
+}
+
+static struct snd_soc_dai_driver msm_codec_dais[] = {
+	{
+		.name = "msm-codec-rx",
+		.playback = { /* Support maximum range */
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 2,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+	{
+		.name = "msm-codec-tx",
+		.capture = { /* Support maximum range */
+			.stream_name = "Record",
+			.channels_min = 1,
+			.channels_max = 4,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+};
+
+static struct regmap *msm89xx_codec_regmap;
+static struct regmap *msm89xx_codec_get_regmap(struct device *dev)
+{
+	return msm89xx_codec_regmap;
+}
+
+static struct snd_soc_codec_driver soc_msm89xx_codec = {
+	.probe	= msm89xx_digcodec_probe,
+	.remove	= msm89xx_digcodec_remove,
+	.get_regmap = msm89xx_codec_get_regmap,
+};
+
+static const struct of_device_id msm89xx_codec_of_match[] = {
+	{ .compatible = "qcom,msm-codec-core",
+	  .data = "msm_codec"},
+	{ .compatible = "qcom,pmic-codec-digital",
+	  .data = "pmic-digital-codec"},
+	{ .compatible = "qcom,pmic-codec-analog",
+	  .data = "pmic-analog-codec"},
+	{},
+};
+MODULE_DEVICE_TABLE(of, msm89xx_codec_of_match);
+
+static struct msm8x16_wcd *temp_89xx;
+static int msm89xx_codec_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct msm8x16_wcd *msm8x16 = NULL;
+	struct msm8x16_wcd_pdata *pdata;
+	int adsp_state;
+	static int dev_registered_cnt;
+	const struct of_device_id *match;
+	const char *addr_prop_name = "qcom,dig-cdc-base-addr";
+	u32 dig_cdc_addr;
+	char __iomem *dig_base;
+
+	adsp_state = apr_get_subsys_state();
+	if (adsp_state != APR_SUBSYS_LOADED) {
+		dev_err(&pdev->dev, "Adsp is not loaded yet %d\n",
+				adsp_state);
+		return -EPROBE_DEFER;
+	}
+
+	match = of_match_node(msm89xx_codec_of_match,
+			pdev->dev.of_node);
+
+	dev_dbg(&pdev->dev, "%s(%d):%s\n",
+		__func__, __LINE__, (char *)match->data);
+
+	if (!strcmp(match->data, "pmic-digital-codec")) {
+		device_init_wakeup(&pdev->dev, true);
+
+		if (pdev->dev.of_node) {
+			dev_err(&pdev->dev, "%s:Platform data from device tree\n",
+				__func__);
+			pdata = msm8x16_wcd_populate_dt_pdata(&pdev->dev);
+			pdev->dev.platform_data = pdata;
+		} else {
+			dev_err(&pdev->dev, "%s:Platform data from board file\n",
+				__func__);
+			pdata = pdev->dev.platform_data;
+		}
+		if (pdata == NULL) {
+			dev_err(&pdev->dev, "%s:Platform data failed to populate\n",
+				__func__);
+			goto rtn;
+		}
+		msm8x16 = kzalloc(sizeof(struct msm8x16_wcd), GFP_KERNEL);
+		if (msm8x16 == NULL) {
+			ret = -ENOMEM;
+			goto rtn;
+		}
+
+		msm8x16->dev = &pdev->dev;
+		ret = msm8x16_wcd_init_supplies(msm8x16, pdata);
+		if (ret) {
+			dev_err(&pdev->dev, "%s: Fail to enable Codec supplies\n",
+				__func__);
+			goto err_codec;
+		}
+
+		ret = msm8x16_wcd_enable_static_supplies(msm8x16, pdata);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: Fail to enable Codec pre-reset supplies\n",
+				   __func__);
+			goto err_codec;
+		}
+		usleep_range(5, 6);
+
+		mutex_init(&msm8x16->io_lock);
+		dev_set_drvdata(&pdev->dev, msm8x16);
+		temp_89xx = msm8x16;
+		dev_registered_cnt++;
+	} else if (!strcmp(match->data, "pmic-analog-codec")) {
+		if (wcd9xxx_spmi_irq_init()) {
+			dev_err(&pdev->dev,
+				"%s: irq initialization failed\n", __func__);
+		} else {
+			dev_err(&pdev->dev,
+				"%s: irq initialization passed\n", __func__);
+		}
+		dev_registered_cnt++;
+	} else if (!strcmp(match->data, "msm-codec")) {
+		ret = of_property_read_u32(pdev->dev.of_node, addr_prop_name,
+							&dig_cdc_addr);
+		if (ret) {
+			dev_err(&pdev->dev, "%s: could not find %s entry in dt\n",
+					__func__, addr_prop_name);
+			dig_cdc_addr = MSM89XX_DIGITAL_CODEC_BASE_ADDR;
+		}
+		dig_base = ioremap(dig_cdc_addr,
+				 MSM89XX_DIGITAL_CODEC_REG_SIZE);
+		if (dig_base == NULL) {
+			dev_err(&pdev->dev, "%s ioremap failed\n", __func__);
+			return -ENOMEM;
+		}
+		msm89xx_codec_regmap =
+			devm_regmap_init_mmio_clk(&pdev->dev, NULL,
+				dig_base, &msm89xx_cdc_core_regmap_config);
+		snd_soc_register_codec(&pdev->dev, &soc_msm89xx_codec,
+				msm_codec_dais, ARRAY_SIZE(msm_codec_dais));
+		dev_registered_cnt++;
+	}
+
+	if ((dev_registered_cnt == MAX_MSM89XX_DEVICE) && (!ret)) {
+		msm89xx_pmic_cdc_regmap =
+			devm_regmap_init_spmi_ext(
+				(struct spmi_device *) &pdev->dev.parent,
+				&msm89xx_pmic_cdc_regmap_config);
+		ret = snd_soc_register_codec(temp_89xx->dev,
+				&soc_codec_dev_msm8x16_wcd,
+				msm8x16_wcd_i2s_dai,
+				ARRAY_SIZE(msm8x16_wcd_i2s_dai));
+		if (ret) {
+			dev_err(&pdev->dev,
+			"%s:snd_soc_register_codec failed with error %d\n",
+			__func__, ret);
+			goto err_supplies;
+		}
+	}
+	return ret;
+err_supplies:
+	msm8x16_wcd_disable_supplies(msm8x16, pdata);
+err_codec:
+	kfree(msm8x16);
+rtn:
+	return ret;
+}
+
+static int msm89xx_codec_remove(struct platform_device *pdev)
+{
+	struct msm8x16_wcd *msm8x16 = dev_get_drvdata(&pdev->dev);
+
+	mutex_destroy(&msm8x16->io_lock);
+	kfree(msm8x16);
+
+	return 0;
+}
+
+static struct platform_driver msm_codec_driver = {
+	.driver                 = {
+		.owner          = THIS_MODULE,
+		.name           = DRV_NAME,
+		.of_match_table = of_match_ptr(msm89xx_codec_of_match)
+	},
+	.probe                  = msm89xx_codec_probe,
+	.remove                 = msm89xx_codec_remove,
+};
+module_platform_driver(msm_codec_driver);
+
+MODULE_DESCRIPTION("MSM89xx Audio codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm8x16/msm8x16-wcd.h b/sound/soc/codecs/msm8x16/msm8x16-wcd.h
new file mode 100644
index 0000000..45ebab2
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm8x16-wcd.h
@@ -0,0 +1,315 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM8X16_WCD_H
+#define MSM8X16_WCD_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include "../wcd-mbhc-v2.h"
+#include "../wcdcal-hwdep.h"
+#include "msm8x16_wcd_registers.h"
+
+#define MICBIAS_EXT_BYP_CAP 0x00
+#define MICBIAS_NO_EXT_BYP_CAP 0x01
+
+#define MSM89XX_NUM_IRQ_REGS	2
+#define MAX_REGULATOR				7
+#define MSM89XX_REG_VAL(reg, val)		{reg, 0, val}
+#define MSM8X16_TOMBAK_LPASS_AUDIO_CORE_DIG_CODEC_CLK_SEL	0xFE03B004
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CMD_RCGR			0x0181C09C
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CFG_RCGR			0x0181C0A0
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_M				0x0181C0A4
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_N				0x0181C0A8
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_D				0x0181C0AC
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_CBCR			0x0181C0B0
+#define MSM8X16_TOMBAK_LPASS_DIGCODEC_AHB_CBCR			0x0181C0B4
+
+#define MSM8X16_CODEC_NAME "msm8x16_wcd_codec"
+
+#define MSM89XX_IS_CDC_CORE_REG(reg) \
+	(((reg >= 0x00) && (reg <= 0x3FF)) ? 1 : 0)
+#define MSM89XX_IS_PMIC_CDC_REG(reg) \
+	(((reg >= 0xF000) && (reg <= 0xF1FF)) ? 1 : 0)
+/*
+ * MCLK activity indicators during suspend and resume call
+ */
+#define MCLK_SUS_DIS	1
+#define MCLK_SUS_RSC	2
+#define MCLK_SUS_NO_ACT	3
+
+#define NUM_DECIMATORS	4
+#define MSM89XX_VDD_SPKDRV_NAME "cdc-vdd-spkdrv"
+
+#define DEFAULT_MULTIPLIER 800
+#define DEFAULT_GAIN 9
+#define DEFAULT_OFFSET 100
+
+extern const u8 msm89xx_pmic_cdc_reg_readable[MSM89XX_PMIC_CDC_CACHE_SIZE];
+extern const u8 msm89xx_cdc_core_reg_readable[MSM89XX_CDC_CORE_CACHE_SIZE];
+extern struct regmap_config msm89xx_cdc_core_regmap_config;
+extern struct regmap_config msm89xx_pmic_cdc_regmap_config;
+
+enum codec_versions {
+	TOMBAK_1_0,
+	TOMBAK_2_0,
+	CONGA,
+	CAJON,
+	CAJON_2_0,
+	DIANGU,
+	UNSUPPORTED,
+};
+
+/* Support different hph modes */
+enum {
+	NORMAL_MODE = 0,
+	HD2_MODE,
+};
+
+/* Codec supports 1 compander */
+enum {
+	COMPANDER_NONE = 0,
+	COMPANDER_1, /* HPHL/R */
+	COMPANDER_MAX,
+};
+
+enum wcd_curr_ref {
+	I_h4_UA = 0,
+	I_pt5_UA,
+	I_14_UA,
+	I_l4_UA,
+	I_1_UA,
+};
+
+enum wcd_mbhc_imp_det_pin {
+	WCD_MBHC_DET_NONE = 0,
+	WCD_MBHC_DET_HPHL,
+	WCD_MBHC_DET_HPHR,
+	WCD_MBHC_DET_BOTH,
+};
+
+
+/* Each micbias can be assigned to one of three cfilters
+ * Vbatt_min >= .15V + ldoh_v
+ * ldoh_v >= .15v + cfiltx_mv
+ * If ldoh_v = 1.95 160 mv < cfiltx_mv < 1800 mv
+ * If ldoh_v = 2.35 200 mv < cfiltx_mv < 2200 mv
+ * If ldoh_v = 2.75 240 mv < cfiltx_mv < 2600 mv
+ * If ldoh_v = 2.85 250 mv < cfiltx_mv < 2700 mv
+ */
+
+struct wcd9xxx_micbias_setting {
+	u8 ldoh_v;
+	u32 cfilt1_mv; /* in mv */
+	u32 cfilt2_mv; /* in mv */
+	u32 cfilt3_mv; /* in mv */
+	/* Different WCD9xxx series codecs may not
+	 * have 4 mic biases. If a codec has fewer
+	 * mic biases, some of these properties will
+	 * not be used.
+	 */
+	u8 bias1_cfilt_sel;
+	u8 bias2_cfilt_sel;
+	u8 bias3_cfilt_sel;
+	u8 bias4_cfilt_sel;
+	u8 bias1_cap_mode;
+	u8 bias2_cap_mode;
+	u8 bias3_cap_mode;
+	u8 bias4_cap_mode;
+	bool bias2_is_headset_only;
+};
+
+enum msm8x16_wcd_pid_current {
+	MSM89XX_PID_MIC_2P5_UA,
+	MSM89XX_PID_MIC_5_UA,
+	MSM89XX_PID_MIC_10_UA,
+	MSM89XX_PID_MIC_20_UA,
+};
+
+struct msm8x16_wcd_reg_mask_val {
+	u16	reg;
+	u8	mask;
+	u8	val;
+};
+
+enum msm8x16_wcd_mbhc_analog_pwr_cfg {
+	MSM89XX_ANALOG_PWR_COLLAPSED = 0,
+	MSM89XX_ANALOG_PWR_ON,
+	MSM89XX_NUM_ANALOG_PWR_CONFIGS,
+};
+
+/* Number of input and output I2S port */
+enum {
+	MSM89XX_RX1 = 0,
+	MSM89XX_RX2,
+	MSM89XX_RX3,
+	MSM89XX_RX_MAX,
+};
+
+enum {
+	MSM89XX_TX1 = 0,
+	MSM89XX_TX2,
+	MSM89XX_TX3,
+	MSM89XX_TX4,
+	MSM89XX_TX_MAX,
+};
+
+enum {
+	/* INTR_REG 0 - Digital Periph */
+	MSM89XX_IRQ_SPKR_CNP = 0,
+	MSM89XX_IRQ_SPKR_CLIP,
+	MSM89XX_IRQ_SPKR_OCP,
+	MSM89XX_IRQ_MBHC_INSREM_DET1,
+	MSM89XX_IRQ_MBHC_RELEASE,
+	MSM89XX_IRQ_MBHC_PRESS,
+	MSM89XX_IRQ_MBHC_INSREM_DET,
+	MSM89XX_IRQ_MBHC_HS_DET,
+	/* INTR_REG 1 - Analog Periph */
+	MSM89XX_IRQ_EAR_OCP,
+	MSM89XX_IRQ_HPHR_OCP,
+	MSM89XX_IRQ_HPHL_OCP,
+	MSM89XX_IRQ_EAR_CNP,
+	MSM89XX_IRQ_HPHR_CNP,
+	MSM89XX_IRQ_HPHL_CNP,
+	MSM89XX_NUM_IRQS,
+};
+
+enum {
+	ON_DEMAND_MICBIAS = 0,
+	ON_DEMAND_SPKDRV,
+	ON_DEMAND_SUPPLIES_MAX,
+};
+
+/*
+ * The delay list is per codec HW specification.
+ * Please add delay in the list in the future instead
+ * of magic number
+ */
+enum {
+	CODEC_DELAY_1_MS = 1000,
+	CODEC_DELAY_1_1_MS  = 1100,
+};
+
+struct msm8x16_wcd_regulator {
+	const char *name;
+	int min_uv;
+	int max_uv;
+	int optimum_ua;
+	bool ondemand;
+	struct regulator *regulator;
+};
+
+struct on_demand_supply {
+	struct regulator *supply;
+	atomic_t ref;
+};
+
+struct wcd_imped_i_ref {
+	enum wcd_curr_ref curr_ref;
+	int min_val;
+	int multiplier;
+	int gain_adj;
+	int offset;
+};
+
+struct msm8x16_wcd_pdata {
+	int irq;
+	int irq_base;
+	int num_irqs;
+	int reset_gpio;
+	void *msm8x16_wcd_ahb_base_vaddr;
+	struct wcd9xxx_micbias_setting micbias;
+	struct msm8x16_wcd_regulator regulator[MAX_REGULATOR];
+	u32 mclk_rate;
+	u32 is_lpass;
+};
+
+enum msm8x16_wcd_micbias_num {
+	MSM89XX_MICBIAS1 = 0,
+};
+
+struct msm8x16_wcd {
+	struct device *dev;
+	struct mutex io_lock;
+	u8 version;
+
+	int reset_gpio;
+	int (*read_dev)(struct snd_soc_codec *codec,
+			unsigned short reg);
+	int (*write_dev)(struct snd_soc_codec *codec,
+			 unsigned short reg, u8 val);
+
+	u32 num_of_supplies;
+	struct regulator_bulk_data *supplies;
+
+	u8 idbyte[4];
+
+	int num_irqs;
+	u32 mclk_rate;
+};
+
+struct msm8x16_wcd_priv {
+	struct snd_soc_codec *codec;
+	u16 pmic_rev;
+	u16 codec_version;
+	u32 boost_voltage;
+	u32 adc_count;
+	u32 rx_bias_count;
+	s32 dmic_1_2_clk_cnt;
+	u32 mute_mask;
+	bool int_mclk0_enabled;
+	bool clock_active;
+	bool config_mode_active;
+	u16 boost_option;
+	/* mode to select hd2 */
+	u32 hph_mode;
+	/* compander used for each rx chain */
+	u32 comp_enabled[MSM89XX_RX_MAX];
+	bool spk_boost_set;
+	bool ear_pa_boost_set;
+	bool ext_spk_boost_set;
+	bool dec_active[NUM_DECIMATORS];
+	struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
+	struct regulator *spkdrv_reg;
+	/* mbhc module */
+	struct wcd_mbhc mbhc;
+	/* cal info for codec */
+	struct fw_info *fw_data;
+	struct blocking_notifier_head notifier;
+	int (*codec_spk_ext_pa_cb)(struct snd_soc_codec *codec, int enable);
+	int (*codec_hph_comp_gpio)(bool enable);
+	unsigned long status_mask;
+	struct wcd_imped_i_ref imped_i_ref;
+	enum wcd_mbhc_imp_det_pin imped_det_pin;
+};
+
+extern int msm8x16_wcd_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
+			     bool dapm);
+
+extern int msm8x16_wcd_hs_detect(struct snd_soc_codec *codec,
+		    struct wcd_mbhc_config *mbhc_cfg);
+
+extern void msm8x16_wcd_hs_detect_exit(struct snd_soc_codec *codec);
+
+extern void msm8x16_update_int_spk_boost(bool enable);
+
+extern void msm8x16_wcd_spk_ext_pa_cb(
+		int (*codec_spk_ext_pa)(struct snd_soc_codec *codec,
+		int enable), struct snd_soc_codec *codec);
+
+extern void msm8x16_wcd_hph_comp_cb(
+		int (*codec_hph_comp_gpio)(bool enable),
+		struct snd_soc_codec *codec);
+void enable_digital_callback(void *flag);
+void disable_digital_callback(void *flag);
+
+#endif
diff --git a/sound/soc/codecs/msm8x16/msm8x16_wcd_registers.h b/sound/soc/codecs/msm8x16/msm8x16_wcd_registers.h
new file mode 100644
index 0000000..ec26ef39
--- /dev/null
+++ b/sound/soc/codecs/msm8x16/msm8x16_wcd_registers.h
@@ -0,0 +1,585 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef MSM8X16_WCD_REGISTERS_H
+#define MSM8X16_WCD_REGISTERS_H
+
+#define CDC_DIG_BASE		0xF000
+#define CDC_ANA_BASE		0xF100
+
+#define MSM89XX_PMIC_DIGITAL_REVISION1		(CDC_DIG_BASE+0x000)
+#define MSM89XX_PMIC_DIGITAL_REVISION1__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_REVISION2		(CDC_DIG_BASE+0x001)
+#define MSM89XX_PMIC_DIGITAL_REVISION2__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE		(CDC_DIG_BASE+0x004)
+#define MSM89XX_PMIC_DIGITAL_PERPH_TYPE__POR		(0x23)
+#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE		(CDC_DIG_BASE+0x005)
+#define MSM89XX_PMIC_DIGITAL_PERPH_SUBTYPE__POR		(0x01)
+#define MSM89XX_PMIC_DIGITAL_INT_RT_STS		(CDC_DIG_BASE+0x010)
+#define MSM89XX_PMIC_DIGITAL_INT_RT_STS__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE		(CDC_DIG_BASE+0x011)
+#define MSM89XX_PMIC_DIGITAL_INT_SET_TYPE__POR		(0xFF)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH		(CDC_DIG_BASE+0x012)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_HIGH__POR		(0xFF)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW		(CDC_DIG_BASE+0x013)
+#define MSM89XX_PMIC_DIGITAL_INT_POLARITY_LOW__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR		(CDC_DIG_BASE+0x014)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_CLR__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_SET		(CDC_DIG_BASE+0x015)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_SET__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR		(CDC_DIG_BASE+0x016)
+#define MSM89XX_PMIC_DIGITAL_INT_EN_CLR__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS		(CDC_DIG_BASE+0x018)
+#define MSM89XX_PMIC_DIGITAL_INT_LATCHED_STS__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS		(CDC_DIG_BASE+0x019)
+#define MSM89XX_PMIC_DIGITAL_INT_PENDING_STS__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL		(CDC_DIG_BASE+0x01A)
+#define MSM89XX_PMIC_DIGITAL_INT_MID_SEL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY		(CDC_DIG_BASE+0x01B)
+#define MSM89XX_PMIC_DIGITAL_INT_PRIORITY__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_GPIO_MODE		(CDC_DIG_BASE+0x040)
+#define MSM89XX_PMIC_DIGITAL_GPIO_MODE__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE		(CDC_DIG_BASE+0x041)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_OE__POR		(0x01)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA		(CDC_DIG_BASE+0x042)
+#define MSM89XX_PMIC_DIGITAL_PIN_CTL_DATA__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_PIN_STATUS		(CDC_DIG_BASE+0x043)
+#define MSM89XX_PMIC_DIGITAL_PIN_STATUS__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL		(CDC_DIG_BASE+0x044)
+#define MSM89XX_PMIC_DIGITAL_HDRIVE_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL		(CDC_DIG_BASE+0x046)
+#define MSM89XX_PMIC_DIGITAL_CDC_RST_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL		(CDC_DIG_BASE+0x048)
+#define MSM89XX_PMIC_DIGITAL_CDC_TOP_CLK_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL		(CDC_DIG_BASE+0x049)
+#define MSM89XX_PMIC_DIGITAL_CDC_ANA_CLK_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL		(CDC_DIG_BASE+0x04A)
+#define MSM89XX_PMIC_DIGITAL_CDC_DIG_CLK_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL		(CDC_DIG_BASE+0x050)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX1_CTL__POR		(0x02)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL		(CDC_DIG_BASE+0x051)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_TX2_CTL__POR		(0x02)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL	(CDC_DIG_BASE+0x052)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_HPHR_DAC_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL		(CDC_DIG_BASE+0x053)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX1_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL		(CDC_DIG_BASE+0x054)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX2_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL		(CDC_DIG_BASE+0x055)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX3_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL	(CDC_DIG_BASE+0x056)
+#define MSM89XX_PMIC_DIGITAL_CDC_CONN_RX_LB_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1		(CDC_DIG_BASE+0x058)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL1__POR		(0x7C)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2		(CDC_DIG_BASE+0x059)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL2__POR		(0x7C)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3		(CDC_DIG_BASE+0x05A)
+#define MSM89XX_PMIC_DIGITAL_CDC_RX_CTL3__POR		(0x7C)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0		(CDC_DIG_BASE+0x05B)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA0__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1		(CDC_DIG_BASE+0x05C)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA1__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2		(CDC_DIG_BASE+0x05D)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA2__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3		(CDC_DIG_BASE+0x05E)
+#define MSM89XX_PMIC_DIGITAL_DEM_BYPASS_DATA3__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL		(CDC_DIG_BASE+0x068)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_CTL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN		(CDC_DIG_BASE+0x069)
+#define MSM89XX_PMIC_DIGITAL_DIG_DEBUG_EN__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_SPARE_0		(CDC_DIG_BASE+0x070)
+#define MSM89XX_PMIC_DIGITAL_SPARE_0__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_SPARE_1		(CDC_DIG_BASE+0x071)
+#define MSM89XX_PMIC_DIGITAL_SPARE_1__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_SPARE_2		(CDC_DIG_BASE+0x072)
+#define MSM89XX_PMIC_DIGITAL_SPARE_2__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS		(CDC_DIG_BASE+0x0D0)
+#define MSM89XX_PMIC_DIGITAL_SEC_ACCESS__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1		(CDC_DIG_BASE+0x0D8)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL1__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2		(CDC_DIG_BASE+0x0D9)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL2__POR		(0x01)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3		(CDC_DIG_BASE+0x0DA)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL3__POR		(0x05)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4		(CDC_DIG_BASE+0x0DB)
+#define MSM89XX_PMIC_DIGITAL_PERPH_RESET_CTL4__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST1		(CDC_DIG_BASE+0x0E0)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST1__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL		(CDC_DIG_BASE+0x0E1)
+#define MSM89XX_PMIC_DIGITAL_INT_TEST_VAL__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_TRIM_NUM		(CDC_DIG_BASE+0x0F0)
+#define MSM89XX_PMIC_DIGITAL_TRIM_NUM__POR		(0x00)
+#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL		(CDC_DIG_BASE+0x0F1)
+#define MSM89XX_PMIC_DIGITAL_TRIM_CTRL__POR		(0x00)
+
+#define MSM89XX_PMIC_ANALOG_REVISION1		(CDC_ANA_BASE+0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION1__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION2		(CDC_ANA_BASE+0x01)
+#define MSM89XX_PMIC_ANALOG_REVISION2__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION3		(CDC_ANA_BASE+0x02)
+#define MSM89XX_PMIC_ANALOG_REVISION3__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_REVISION4		(CDC_ANA_BASE+0x03)
+#define MSM89XX_PMIC_ANALOG_REVISION4__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_PERPH_TYPE		(CDC_ANA_BASE+0x04)
+#define MSM89XX_PMIC_ANALOG_PERPH_TYPE__POR		(0x23)
+#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE		(CDC_ANA_BASE+0x05)
+#define MSM89XX_PMIC_ANALOG_PERPH_SUBTYPE__POR		(0x09)
+#define MSM89XX_PMIC_ANALOG_INT_RT_STS		(CDC_ANA_BASE+0x10)
+#define MSM89XX_PMIC_ANALOG_INT_RT_STS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE		(CDC_ANA_BASE+0x11)
+#define MSM89XX_PMIC_ANALOG_INT_SET_TYPE__POR		(0x3F)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH		(CDC_ANA_BASE+0x12)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_HIGH__POR		(0x3F)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW		(CDC_ANA_BASE+0x13)
+#define MSM89XX_PMIC_ANALOG_INT_POLARITY_LOW__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR		(CDC_ANA_BASE+0x14)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_CLR__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_EN_SET		(CDC_ANA_BASE+0x15)
+#define MSM89XX_PMIC_ANALOG_INT_EN_SET__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_EN_CLR		(CDC_ANA_BASE+0x16)
+#define MSM89XX_PMIC_ANALOG_INT_EN_CLR__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS		(CDC_ANA_BASE+0x18)
+#define MSM89XX_PMIC_ANALOG_INT_LATCHED_STS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS		(CDC_ANA_BASE+0x19)
+#define MSM89XX_PMIC_ANALOG_INT_PENDING_STS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_MID_SEL		(CDC_ANA_BASE+0x1A)
+#define MSM89XX_PMIC_ANALOG_INT_MID_SEL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_PRIORITY		(CDC_ANA_BASE+0x1B)
+#define MSM89XX_PMIC_ANALOG_INT_PRIORITY__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MICB_1_EN		(CDC_ANA_BASE+0x40)
+#define MSM89XX_PMIC_ANALOG_MICB_1_EN__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MICB_1_VAL		(CDC_ANA_BASE+0x41)
+#define MSM89XX_PMIC_ANALOG_MICB_1_VAL__POR		(0x20)
+#define MSM89XX_PMIC_ANALOG_MICB_1_CTL		(CDC_ANA_BASE+0x42)
+#define MSM89XX_PMIC_ANALOG_MICB_1_CTL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS		(CDC_ANA_BASE+0x43)
+#define MSM89XX_PMIC_ANALOG_MICB_1_INT_RBIAS__POR		(0x49)
+#define MSM89XX_PMIC_ANALOG_MICB_2_EN		(CDC_ANA_BASE+0x44)
+#define MSM89XX_PMIC_ANALOG_MICB_2_EN__POR		(0x20)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2		(CDC_ANA_BASE+0x45)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL_2__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL		(CDC_ANA_BASE+0x46)
+#define MSM89XX_PMIC_ANALOG_MASTER_BIAS_CTL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1		(CDC_ANA_BASE+0x47)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_1__POR		(0x35)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2		(CDC_ANA_BASE+0x50)
+#define MSM89XX_PMIC_ANALOG_MBHC_DET_CTL_2__POR		(0x08)
+#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL		(CDC_ANA_BASE+0x51)
+#define MSM89XX_PMIC_ANALOG_MBHC_FSM_CTL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER		(CDC_ANA_BASE+0x52)
+#define MSM89XX_PMIC_ANALOG_MBHC_DBNC_TIMER__POR		(0x98)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL	(CDC_ANA_BASE+0x53)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN0_ZDETL_CTL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL	(CDC_ANA_BASE+0x54)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN1_ZDETM_CTL__POR		(0x20)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL	(CDC_ANA_BASE+0x55)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN2_ZDETH_CTL__POR		(0x40)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL		(CDC_ANA_BASE+0x56)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN3_CTL__POR		(0x61)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL		(CDC_ANA_BASE+0x57)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN4_CTL__POR		(0x80)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT		(CDC_ANA_BASE+0x58)
+#define MSM89XX_PMIC_ANALOG_MBHC_BTN_RESULT__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT	(CDC_ANA_BASE+0x59)
+#define MSM89XX_PMIC_ANALOG_MBHC_ZDET_ELECT_RESULT__POR	(0x00)
+#define MSM89XX_PMIC_ANALOG_TX_1_EN		(CDC_ANA_BASE+0x60)
+#define MSM89XX_PMIC_ANALOG_TX_1_EN__POR		(0x03)
+#define MSM89XX_PMIC_ANALOG_TX_2_EN		(CDC_ANA_BASE+0x61)
+#define MSM89XX_PMIC_ANALOG_TX_2_EN__POR		(0x03)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1		(CDC_ANA_BASE+0x62)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_1__POR		(0xBF)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2		(CDC_ANA_BASE+0x63)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TEST_CTL_2__POR		(0x8C)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL		(CDC_ANA_BASE+0x64)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_ATEST_CTL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS		(CDC_ANA_BASE+0x65)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_OPAMP_BIAS__POR		(0x6B)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV		(CDC_ANA_BASE+0x66)
+#define MSM89XX_PMIC_ANALOG_TX_1_2_TXFE_CLKDIV__POR		(0x51)
+#define MSM89XX_PMIC_ANALOG_TX_3_EN		(CDC_ANA_BASE+0x67)
+#define MSM89XX_PMIC_ANALOG_TX_3_EN__POR		(0x02)
+#define MSM89XX_PMIC_ANALOG_NCP_EN		(CDC_ANA_BASE+0x80)
+#define MSM89XX_PMIC_ANALOG_NCP_EN__POR		(0x26)
+#define MSM89XX_PMIC_ANALOG_NCP_CLK		(CDC_ANA_BASE+0x81)
+#define MSM89XX_PMIC_ANALOG_NCP_CLK__POR		(0x23)
+#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH		(CDC_ANA_BASE+0x82)
+#define MSM89XX_PMIC_ANALOG_NCP_DEGLITCH__POR		(0x5B)
+#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL		(CDC_ANA_BASE+0x83)
+#define MSM89XX_PMIC_ANALOG_NCP_FBCTRL__POR		(0x08)
+#define MSM89XX_PMIC_ANALOG_NCP_BIAS		(CDC_ANA_BASE+0x84)
+#define MSM89XX_PMIC_ANALOG_NCP_BIAS__POR		(0x29)
+#define MSM89XX_PMIC_ANALOG_NCP_VCTRL		(CDC_ANA_BASE+0x85)
+#define MSM89XX_PMIC_ANALOG_NCP_VCTRL__POR		(0x24)
+#define MSM89XX_PMIC_ANALOG_NCP_TEST		(CDC_ANA_BASE+0x86)
+#define MSM89XX_PMIC_ANALOG_NCP_TEST__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR		(CDC_ANA_BASE+0x87)
+#define MSM89XX_PMIC_ANALOG_NCP_CLIM_ADDR__POR		(0xD5)
+#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER		(CDC_ANA_BASE+0x90)
+#define MSM89XX_PMIC_ANALOG_RX_CLOCK_DIVIDER__POR		(0xE8)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL		(CDC_ANA_BASE+0x91)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_CTL__POR		(0xCF)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT		(CDC_ANA_BASE+0x92)
+#define MSM89XX_PMIC_ANALOG_RX_COM_OCP_COUNT__POR		(0x6E)
+#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC		(CDC_ANA_BASE+0x93)
+#define MSM89XX_PMIC_ANALOG_RX_COM_BIAS_DAC__POR		(0x18)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA		(CDC_ANA_BASE+0x94)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_PA__POR		(0x5A)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP	(CDC_ANA_BASE+0x95)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_LDO_OCP__POR		(0x69)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP		(CDC_ANA_BASE+0x96)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_BIAS_CNP__POR		(0x29)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN		(CDC_ANA_BASE+0x97)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_EN__POR		(0x80)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL		(CDC_ANA_BASE+0x98)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_CTL__POR		(0xDA)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME		(CDC_ANA_BASE+0x99)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_CNP_WG_TIME__POR		(0x16)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST		(CDC_ANA_BASE+0x9A)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_TEST__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL	(CDC_ANA_BASE+0x9B)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_L_PA_DAC_CTL__POR		(0x20)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST		(CDC_ANA_BASE+0x9C)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_TEST__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL	(CDC_ANA_BASE+0x9D)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_R_PA_DAC_CTL__POR		(0x20)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL		(CDC_ANA_BASE+0x9E)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_CTL___POR		(0x12)
+#define MSM89XX_PMIC_ANALOG_RX_ATEST		(CDC_ANA_BASE+0x9F)
+#define MSM89XX_PMIC_ANALOG_RX_ATEST__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS		(CDC_ANA_BASE+0xA0)
+#define MSM89XX_PMIC_ANALOG_RX_HPH_STATUS__POR		(0x0C)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS		(CDC_ANA_BASE+0xA1)
+#define MSM89XX_PMIC_ANALOG_RX_EAR_STATUS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL		(CDC_ANA_BASE+0xAC)
+#define MSM89XX_PMIC_ANALOG_RX_LO_DAC_CTL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_RX_LO_EN_CTL		(CDC_ANA_BASE+0xAD)
+#define MSM89XX_PMIC_ANALOG_RX_RX_LO_EN_CTL__POR	(0x00)
+#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL		(CDC_ANA_BASE+0xB0)
+#define MSM89XX_PMIC_ANALOG_SPKR_DAC_CTL__POR		(0x83)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET		(CDC_ANA_BASE+0xB1)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CLIP_DET__POR		(0x91)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL		(CDC_ANA_BASE+0xB2)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_CTL__POR		(0x29)
+#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET		(CDC_ANA_BASE+0xB3)
+#define MSM89XX_PMIC_ANALOG_SPKR_ANA_BIAS_SET__POR		(0x4D)
+#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL		(CDC_ANA_BASE+0xB4)
+#define MSM89XX_PMIC_ANALOG_SPKR_OCP_CTL__POR		(0xE1)
+#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL		(CDC_ANA_BASE+0xB5)
+#define MSM89XX_PMIC_ANALOG_SPKR_PWRSTG_CTL__POR		(0x1E)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC		(CDC_ANA_BASE+0xB6)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_MISC__POR		(0xCB)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG		(CDC_ANA_BASE+0xB7)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_DBG__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT		(CDC_ANA_BASE+0xC0)
+#define MSM89XX_PMIC_ANALOG_CURRENT_LIMIT__POR		(0x02)
+#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE		(CDC_ANA_BASE+0xC1)
+#define MSM89XX_PMIC_ANALOG_OUTPUT_VOLTAGE__POR		(0x14)
+#define MSM89XX_PMIC_ANALOG_BYPASS_MODE		(CDC_ANA_BASE+0xC2)
+#define MSM89XX_PMIC_ANALOG_BYPASS_MODE__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL		(CDC_ANA_BASE+0xC3)
+#define MSM89XX_PMIC_ANALOG_BOOST_EN_CTL__POR		(0x1F)
+#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO		(CDC_ANA_BASE+0xC4)
+#define MSM89XX_PMIC_ANALOG_SLOPE_COMP_IP_ZERO__POR		(0x8C)
+#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE	(CDC_ANA_BASE+0xC5)
+#define MSM89XX_PMIC_ANALOG_RDSON_MAX_DUTY_CYCLE__POR		(0xC0)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1		(CDC_ANA_BASE+0xC6)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST1_1__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2		(CDC_ANA_BASE+0xC7)
+#define MSM89XX_PMIC_ANALOG_BOOST_TEST_2__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS		(CDC_ANA_BASE+0xC8)
+#define MSM89XX_PMIC_ANALOG_SPKR_SAR_STATUS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS		(CDC_ANA_BASE+0xC9)
+#define MSM89XX_PMIC_ANALOG_SPKR_DRV_STATUS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR		(CDC_ANA_BASE+0xCE)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_CSR__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL		(CDC_ANA_BASE+0xCF)
+#define MSM89XX_PMIC_ANALOG_PBUS_ADD_SEL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_SEC_ACCESS		(CDC_ANA_BASE+0xD0)
+#define MSM89XX_PMIC_ANALOG_SEC_ACCESS__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1		(CDC_ANA_BASE+0xD8)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL1__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2		(CDC_ANA_BASE+0xD9)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL2__POR		(0x01)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3		(CDC_ANA_BASE+0xDA)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL3__POR		(0x05)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4		(CDC_ANA_BASE+0xDB)
+#define MSM89XX_PMIC_ANALOG_PERPH_RESET_CTL4__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_TEST1		(CDC_ANA_BASE+0xE0)
+#define MSM89XX_PMIC_ANALOG_INT_TEST1__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL		(CDC_ANA_BASE+0xE1)
+#define MSM89XX_PMIC_ANALOG_INT_TEST_VAL__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_NUM		(CDC_ANA_BASE+0xF0)
+#define MSM89XX_PMIC_ANALOG_TRIM_NUM__POR		(0x04)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1		(CDC_ANA_BASE+0xF1)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL1__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2		(CDC_ANA_BASE+0xF2)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL2__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3		(CDC_ANA_BASE+0xF3)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL3__POR		(0x00)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4		(CDC_ANA_BASE+0xF4)
+#define MSM89XX_PMIC_ANALOG_TRIM_CTRL4__POR		(0x00)
+
+#define MSM89XX_PMIC_CDC_NUM_REGISTERS \
+		(MSM89XX_PMIC_ANALOG_TRIM_CTRL4+1)
+#define MSM89XX_PMIC_CDC_MAX_REGISTER \
+		(MSM89XX_PMIC_CDC_NUM_REGISTERS-1)
+#define MSM89XX_PMIC_CDC_CACHE_SIZE \
+		MSM89XX_PMIC_CDC_NUM_REGISTERS
+
+
+#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL		(0x00)
+#define MSM89XX_CDC_CORE_CLK_RX_RESET_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL		(0x04)
+#define MSM89XX_CDC_CORE_CLK_TX_RESET_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL		(0x08)
+#define MSM89XX_CDC_CORE_CLK_DMIC_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL		(0x0C)
+#define MSM89XX_CDC_CORE_CLK_RX_I2S_CTL__POR		(0x13)
+#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL		(0x10)
+#define MSM89XX_CDC_CORE_CLK_TX_I2S_CTL__POR		(0x13)
+#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL		(0x14)
+#define MSM89XX_CDC_CORE_CLK_OTHR_RESET_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL		(0x18)
+#define MSM89XX_CDC_CORE_CLK_TX_CLK_EN_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_OTHR_CTL		(0x1C)
+#define MSM89XX_CDC_CORE_CLK_OTHR_CTL__POR		(0x04)
+#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL		(0x20)
+#define MSM89XX_CDC_CORE_CLK_RX_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_MCLK_CTL		(0x24)
+#define MSM89XX_CDC_CORE_CLK_MCLK_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_PDM_CTL		(0x28)
+#define MSM89XX_CDC_CORE_CLK_PDM_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_SD_CTL		(0x2C)
+#define MSM89XX_CDC_CORE_CLK_SD_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL		(0x30)
+#define MSM89XX_CDC_CORE_CLK_WSA_VI_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL		(0x34)
+#define MSM89XX_CDC_CORE_CLK_RX_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_B1_CTL		(0x40)
+#define MSM89XX_CDC_CORE_RX1_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_B1_CTL		(0x60)
+#define MSM89XX_CDC_CORE_RX2_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_B1_CTL		(0x80)
+#define MSM89XX_CDC_CORE_RX3_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_B2_CTL		(0x44)
+#define MSM89XX_CDC_CORE_RX1_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_B2_CTL		(0x64)
+#define MSM89XX_CDC_CORE_RX2_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_B2_CTL		(0x84)
+#define MSM89XX_CDC_CORE_RX3_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_B3_CTL		(0x48)
+#define MSM89XX_CDC_CORE_RX1_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_B3_CTL		(0x68)
+#define MSM89XX_CDC_CORE_RX2_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_B3_CTL		(0x88)
+#define MSM89XX_CDC_CORE_RX3_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_B4_CTL		(0x4C)
+#define MSM89XX_CDC_CORE_RX1_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_B4_CTL		(0x6C)
+#define MSM89XX_CDC_CORE_RX2_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_B4_CTL		(0x8C)
+#define MSM89XX_CDC_CORE_RX3_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_B5_CTL		(0x50)
+#define MSM89XX_CDC_CORE_RX1_B5_CTL__POR		(0x68)
+#define MSM89XX_CDC_CORE_RX2_B5_CTL		(0x70)
+#define MSM89XX_CDC_CORE_RX2_B5_CTL__POR		(0x68)
+#define MSM89XX_CDC_CORE_RX3_B5_CTL		(0x90)
+#define MSM89XX_CDC_CORE_RX3_B5_CTL__POR		(0x68)
+#define MSM89XX_CDC_CORE_RX1_B6_CTL		(0x54)
+#define MSM89XX_CDC_CORE_RX1_B6_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_B6_CTL		(0x74)
+#define MSM89XX_CDC_CORE_RX2_B6_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_B6_CTL		(0x94)
+#define MSM89XX_CDC_CORE_RX3_B6_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL		(0x58)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL		(0x78)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL		(0x98)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL		(0x5C)
+#define MSM89XX_CDC_CORE_RX1_VOL_CTL_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL		(0x7C)
+#define MSM89XX_CDC_CORE_RX2_VOL_CTL_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL		(0x9C)
+#define MSM89XX_CDC_CORE_RX3_VOL_CTL_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE		(0xA0)
+#define MSM89XX_CDC_CORE_TOP_GAIN_UPDATE__POR		(0x00)
+#define MSM89XX_CDC_CORE_TOP_CTL		(0xA4)
+#define MSM89XX_CDC_CORE_TOP_CTL__POR			(0x01)
+#define MSM89XX_CDC_CORE_COMP0_B1_CTL		(0xB0)
+#define MSM89XX_CDC_CORE_COMP0_B1_CTL__POR		(0x30)
+#define MSM89XX_CDC_CORE_COMP0_B2_CTL		(0xB4)
+#define MSM89XX_CDC_CORE_COMP0_B2_CTL__POR		(0xB5)
+#define MSM89XX_CDC_CORE_COMP0_B3_CTL		(0xB8)
+#define MSM89XX_CDC_CORE_COMP0_B3_CTL__POR		(0x28)
+#define MSM89XX_CDC_CORE_COMP0_B4_CTL		(0xBC)
+#define MSM89XX_CDC_CORE_COMP0_B4_CTL__POR		(0x37)
+#define MSM89XX_CDC_CORE_COMP0_B5_CTL		(0xC0)
+#define MSM89XX_CDC_CORE_COMP0_B5_CTL__POR		(0x7F)
+#define MSM89XX_CDC_CORE_COMP0_B6_CTL		(0xC4)
+#define MSM89XX_CDC_CORE_COMP0_B6_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS		(0xC8)
+#define MSM89XX_CDC_CORE_COMP0_SHUT_DOWN_STATUS__POR		(0x03)
+#define MSM89XX_CDC_CORE_COMP0_FS_CFG		(0xCC)
+#define MSM89XX_CDC_CORE_COMP0_FS_CFG__POR		(0x03)
+#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL		(0xD0)
+#define MSM89XX_CDC_CORE_COMP0_DELAY_BUF_CTL__POR		(0x02)
+#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL		(0xE0)
+#define MSM89XX_CDC_CORE_DEBUG_DESER1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL		(0xE4)
+#define MSM89XX_CDC_CORE_DEBUG_DESER2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_DEBUG_B1_CTL_CFG		(0xE8)
+#define MSM89XX_CDC_CORE_DEBUG_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_DEBUG_B2_CTL_CFG		(0xEC)
+#define MSM89XX_CDC_CORE_DEBUG_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_DEBUG_B3_CTL_CFG		(0xF0)
+#define MSM89XX_CDC_CORE_DEBUG_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL		(0x100)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL		(0x140)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL		(0x104)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL		(0x144)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL		(0x108)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL		(0x148)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL		(0x10C)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL		(0x14C)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL		(0x110)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B5_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL		(0x150)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B5_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL		(0x114)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B6_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL		(0x154)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B6_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL		(0x118)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B7_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL		(0x158)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B7_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL		(0x11C)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_B8_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL		(0x15C)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_B8_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_CTL		(0x120)
+#define MSM89XX_CDC_CORE_IIR1_CTL__POR		(0x40)
+#define MSM89XX_CDC_CORE_IIR2_CTL		(0x160)
+#define MSM89XX_CDC_CORE_IIR2_CTL__POR		(0x40)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL		(0x124)
+#define MSM89XX_CDC_CORE_IIR1_GAIN_TIMER_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL		(0x164)
+#define MSM89XX_CDC_CORE_IIR2_GAIN_TIMER_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL		(0x128)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL		(0x168)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL		(0x12C)
+#define MSM89XX_CDC_CORE_IIR1_COEF_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL		(0x16C)
+#define MSM89XX_CDC_CORE_IIR2_COEF_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL		(0x180)
+#define MSM89XX_CDC_CORE_CONN_RX1_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL		(0x184)
+#define MSM89XX_CDC_CORE_CONN_RX1_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL		(0x188)
+#define MSM89XX_CDC_CORE_CONN_RX1_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL		(0x18C)
+#define MSM89XX_CDC_CORE_CONN_RX2_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL		(0x190)
+#define MSM89XX_CDC_CORE_CONN_RX2_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL		(0x194)
+#define MSM89XX_CDC_CORE_CONN_RX2_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL		(0x198)
+#define MSM89XX_CDC_CORE_CONN_RX3_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL		(0x19C)
+#define MSM89XX_CDC_CORE_CONN_RX3_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL		(0x1A0)
+#define MSM89XX_CDC_CORE_CONN_TX_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL		(0x1A8)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL		(0x1AC)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL		(0x1B0)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL		(0x1B4)
+#define MSM89XX_CDC_CORE_CONN_EQ1_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL		(0x1B8)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL		(0x1BC)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B2_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL		(0x1C0)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B3_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL		(0x1C4)
+#define MSM89XX_CDC_CORE_CONN_EQ2_B4_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL		(0x1C8)
+#define MSM89XX_CDC_CORE_CONN_TX_I2S_SD1_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER		(0x280)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_TIMER__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER		(0x2A0)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_TIMER__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER		(0x2C0)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_TIMER__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER		(0x2E0)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_TIMER__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN		(0x284)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_GAIN__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN		(0x2A4)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_GAIN__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN		(0x2C4)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_GAIN__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN		(0x2E4)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_GAIN__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG		(0x288)
+#define MSM89XX_CDC_CORE_TX1_VOL_CTL_CFG__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG		(0x2A8)
+#define MSM89XX_CDC_CORE_TX2_VOL_CTL_CFG__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG		(0x2C8)
+#define MSM89XX_CDC_CORE_TX3_VOL_CTL_CFG__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG		(0x2E8)
+#define MSM89XX_CDC_CORE_TX4_VOL_CTL_CFG__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX1_MUX_CTL		(0x28C)
+#define MSM89XX_CDC_CORE_TX1_MUX_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX2_MUX_CTL		(0x2AC)
+#define MSM89XX_CDC_CORE_TX2_MUX_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX3_MUX_CTL		(0x2CC)
+#define MSM89XX_CDC_CORE_TX3_MUX_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX4_MUX_CTL		(0x2EC)
+#define MSM89XX_CDC_CORE_TX4_MUX_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL		(0x290)
+#define MSM89XX_CDC_CORE_TX1_CLK_FS_CTL__POR		(0x03)
+#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL		(0x2B0)
+#define MSM89XX_CDC_CORE_TX2_CLK_FS_CTL__POR		(0x03)
+#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL		(0x2D0)
+#define MSM89XX_CDC_CORE_TX3_CLK_FS_CTL__POR		(0x03)
+#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL		(0x2F0)
+#define MSM89XX_CDC_CORE_TX4_CLK_FS_CTL__POR		(0x03)
+#define MSM89XX_CDC_CORE_TX1_DMIC_CTL		(0x294)
+#define MSM89XX_CDC_CORE_TX1_DMIC_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX2_DMIC_CTL		(0x2B4)
+#define MSM89XX_CDC_CORE_TX2_DMIC_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX3_DMIC_CTL		(0x2D4)
+#define MSM89XX_CDC_CORE_TX3_DMIC_CTL__POR		(0x00)
+#define MSM89XX_CDC_CORE_TX4_DMIC_CTL		(0x2F4)
+#define MSM89XX_CDC_CORE_TX4_DMIC_CTL__POR		(0x00)
+
+#define MSM89XX_CDC_CORE_NUM_REGISTERS \
+		(MSM89XX_CDC_CORE_TX4_DMIC_CTL+1)
+#define MSM89XX_CDC_CORE_MAX_REGISTER \
+		(MSM89XX_CDC_CORE_NUM_REGISTERS-1)
+#define MSM89XX_CDC_CORE_CACHE_SIZE \
+		MSM89XX_CDC_CORE_NUM_REGISTERS
+#endif
diff --git a/sound/soc/codecs/msm_hdmi_codec_rx.c b/sound/soc/codecs/msm_hdmi_codec_rx.c
new file mode 100644
index 0000000..cd5e707
--- /dev/null
+++ b/sound/soc/codecs/msm_hdmi_codec_rx.c
@@ -0,0 +1,495 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <linux/msm_ext_display.h>
+
+#define MSM_EXT_DISP_PCM_RATES	SNDRV_PCM_RATE_48000
+
+static const char *const ext_disp_audio_type_text[] = {"None", "HDMI", "DP"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_audio_type, ext_disp_audio_type_text);
+
+struct msm_ext_disp_audio_codec_rx_data {
+	struct platform_device *ext_disp_core_pdev;
+	struct msm_ext_disp_audio_codec_ops ext_disp_ops;
+	int cable_status;
+};
+
+static int msm_ext_disp_edid_ctl_info(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_info *uinfo)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm_ext_disp_audio_codec_rx_data *codec_data;
+	struct msm_ext_disp_audio_edid_blk edid_blk;
+	int rc;
+
+	codec_data = snd_soc_codec_get_drvdata(codec);
+
+	if (!codec_data) {
+		dev_err(codec->dev, "%s: codec_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!codec_data->ext_disp_ops.get_audio_edid_blk) {
+		dev_dbg(codec->dev, "%s: get_audio_edid_blk() is NULL\n",
+			__func__);
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+		uinfo->count = 0;
+		return 0;
+	}
+
+	rc = codec_data->ext_disp_ops.get_audio_edid_blk(
+				codec_data->ext_disp_core_pdev, &edid_blk);
+	if (rc >= 0) {
+		uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+		uinfo->count = edid_blk.audio_data_blk_size +
+			edid_blk.spk_alloc_data_blk_size;
+	}
+
+	dev_dbg(codec->dev, "%s: count: %d\n", __func__, uinfo->count);
+
+	return rc;
+}
+
+static int msm_ext_disp_edid_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol) {
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm_ext_disp_audio_codec_rx_data *codec_data;
+	struct msm_ext_disp_audio_edid_blk edid_blk;
+	int rc;
+
+	codec_data = snd_soc_codec_get_drvdata(codec);
+	if (!codec_data || !codec_data->ext_disp_ops.get_audio_edid_blk) {
+		dev_err(codec->dev, "%s: codec_data or get_audio_edid_blk() is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	rc = codec_data->ext_disp_ops.get_audio_edid_blk(
+			codec_data->ext_disp_core_pdev, &edid_blk);
+	if (rc >= 0) {
+		memcpy(ucontrol->value.bytes.data,
+		       edid_blk.audio_data_blk,
+		       edid_blk.audio_data_blk_size);
+		memcpy((ucontrol->value.bytes.data +
+		       edid_blk.audio_data_blk_size),
+		       edid_blk.spk_alloc_data_blk,
+		       edid_blk.spk_alloc_data_blk_size);
+
+		dev_dbg(codec->dev, "%s: data_blk_size:%d, spk_alloc_data_blk_size:%d\n",
+			__func__, edid_blk.audio_data_blk_size,
+			edid_blk.spk_alloc_data_blk_size);
+	}
+
+	return rc;
+}
+
+static int msm_ext_disp_audio_type_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct msm_ext_disp_audio_codec_rx_data *codec_data;
+	enum msm_ext_disp_cable_state cable_state;
+	enum msm_ext_disp_type disp_type;
+	int rc;
+
+	codec_data = snd_soc_codec_get_drvdata(codec);
+	if (!codec_data ||
+	    !codec_data->ext_disp_ops.get_audio_edid_blk ||
+	    !codec_data->ext_disp_ops.get_intf_id) {
+		dev_err(codec->dev, "%s: codec_data, get_audio_edid_blk() or get_intf_id is NULL\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	cable_state = codec_data->ext_disp_ops.cable_status(
+				   codec_data->ext_disp_core_pdev, 1);
+	if (cable_state < 0) {
+		dev_err(codec->dev, "%s: Error retrieving cable state from ext_disp, err:%d\n",
+			__func__, cable_state);
+		rc = cable_state;
+		goto done;
+	}
+
+	codec_data->cable_status = cable_state;
+	if (cable_state == EXT_DISPLAY_CABLE_DISCONNECT) {
+		dev_err(codec->dev, "%s: Display cable disconnected\n",
+			__func__);
+		ucontrol->value.integer.value[0] = 0;
+		rc = 0;
+		goto done;
+	}
+
+	disp_type = codec_data->ext_disp_ops.get_intf_id(
+						codec_data->ext_disp_core_pdev);
+	if (disp_type >= 0) {
+		switch (disp_type) {
+		case EXT_DISPLAY_TYPE_DP:
+			ucontrol->value.integer.value[0] = 2;
+			rc = 0;
+			break;
+		case EXT_DISPLAY_TYPE_HDMI:
+			ucontrol->value.integer.value[0] = 1;
+			rc = 0;
+			break;
+		default:
+			rc = -EINVAL;
+			dev_err(codec->dev, "%s: Invalid disp_type:%d\n",
+			       __func__, disp_type);
+			goto done;
+		}
+		dev_dbg(codec->dev, "%s: Display type: %d\n",
+			__func__, disp_type);
+	} else {
+		dev_err(codec->dev, "%s: Error retrieving disp_type from ext_disp, err:%d\n",
+			__func__, disp_type);
+		rc = disp_type;
+	}
+
+done:
+	return rc;
+}
+
+static const struct snd_kcontrol_new msm_ext_disp_codec_rx_controls[] = {
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface  = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name   = "HDMI EDID",
+		.info   = msm_ext_disp_edid_ctl_info,
+		.get    = msm_ext_disp_edid_get,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ |
+			  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+		.iface  = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name   = "Display Port EDID",
+		.info   = msm_ext_disp_edid_ctl_info,
+		.get    = msm_ext_disp_edid_get,
+	},
+	SOC_ENUM_EXT("External Display Type", ext_disp_audio_type,
+		     msm_ext_disp_audio_type_get, NULL),
+};
+
+static int msm_ext_disp_audio_codec_rx_dai_startup(
+		struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	int ret = 0;
+	struct msm_ext_disp_audio_codec_rx_data *codec_data =
+			dev_get_drvdata(dai->codec->dev);
+
+	if (!codec_data || !codec_data->ext_disp_ops.cable_status) {
+		dev_err(dai->dev, "%s() codec_data or cable_status is null\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	codec_data->cable_status =
+		codec_data->ext_disp_ops.cable_status(
+		codec_data->ext_disp_core_pdev, 1);
+	if (codec_data->cable_status < 0) {
+		dev_err(dai->dev,
+			"%s() ext disp core is not ready (ret val = %d)\n",
+			__func__, codec_data->cable_status);
+		ret = codec_data->cable_status;
+	} else if (!codec_data->cable_status) {
+		dev_err(dai->dev,
+			"%s() ext disp cable is not connected (ret val = %d)\n",
+			__func__, codec_data->cable_status);
+		ret = -ENODEV;
+	}
+
+	return ret;
+}
+
+static int msm_ext_disp_audio_codec_rx_dai_hw_params(
+		struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	u32 channel_allocation = 0;
+	u32 level_shift  = 0; /* 0dB */
+	bool down_mix = 0;
+	u32 num_channels = params_channels(params);
+	int rc = 0;
+	struct msm_ext_disp_audio_setup_params audio_setup_params = {0};
+
+	struct msm_ext_disp_audio_codec_rx_data *codec_data =
+			dev_get_drvdata(dai->codec->dev);
+
+	if (!codec_data || !codec_data->ext_disp_ops.audio_info_setup) {
+		dev_err(dai->dev, "%s: codec_data or audio_info_setup is null\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (codec_data->cable_status < 0) {
+		dev_err_ratelimited(dai->dev,
+			"%s() ext disp core is not ready (ret val = %d)\n",
+			__func__, codec_data->cable_status);
+		return codec_data->cable_status;
+	} else if (!codec_data->cable_status) {
+		dev_err_ratelimited(dai->dev,
+			"%s() ext disp cable is not connected (ret val = %d)\n",
+			__func__, codec_data->cable_status);
+		return -ENODEV;
+	}
+
+	/*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
+	switch (num_channels) {
+	case 2:
+		channel_allocation  = 0;
+		break;
+	case 3:
+		channel_allocation  = 0x02;/*default to FL/FR/FC*/
+		audio_setup_params.sample_present = 0x3;
+		break;
+	case 4:
+		channel_allocation  = 0x06;/*default to FL/FR/FC/RC*/
+		audio_setup_params.sample_present = 0x7;
+		break;
+	case 5:
+		channel_allocation  = 0x0A;/*default to FL/FR/FC/RR/RL*/
+		audio_setup_params.sample_present = 0x7;
+		break;
+	case 6:
+		channel_allocation  = 0x0B;
+		audio_setup_params.sample_present = 0x7;
+		break;
+	case 7:
+		channel_allocation  = 0x12;/*default to FL/FR/FC/RL/RR/RRC/RLC*/
+		audio_setup_params.sample_present = 0xf;
+		break;
+	case 8:
+		channel_allocation  = 0x13;
+		audio_setup_params.sample_present = 0xf;
+		break;
+	default:
+		dev_err(dai->dev, "invalid Channels = %u\n", num_channels);
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->dev,
+		"%s() num_ch %u  samplerate %u channel_allocation = %u\n",
+		__func__, num_channels, params_rate(params),
+		channel_allocation);
+
+	audio_setup_params.sample_rate_hz = params_rate(params);
+	audio_setup_params.num_of_channels = num_channels;
+	audio_setup_params.channel_allocation = channel_allocation;
+	audio_setup_params.level_shift = level_shift;
+	audio_setup_params.down_mix = down_mix;
+
+	rc = codec_data->ext_disp_ops.audio_info_setup(
+			codec_data->ext_disp_core_pdev, &audio_setup_params);
+	if (rc < 0) {
+		dev_err_ratelimited(dai->dev,
+			"%s() ext disp core is not ready, rc: %d\n",
+			__func__, rc);
+	}
+
+	return rc;
+}
+
+static void msm_ext_disp_audio_codec_rx_dai_shutdown(
+		struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	int rc;
+
+	struct msm_ext_disp_audio_codec_rx_data *codec_data =
+			dev_get_drvdata(dai->codec->dev);
+
+	if (!codec_data || !codec_data->ext_disp_ops.teardown_done ||
+	    !codec_data->ext_disp_ops.cable_status) {
+		dev_err(dai->dev, "%s: codec data or teardown_done or cable_status is null\n",
+			__func__);
+		return;
+	}
+
+	rc = codec_data->ext_disp_ops.cable_status(
+			codec_data->ext_disp_core_pdev, 0);
+	if (rc < 0) {
+		dev_err(dai->dev,
+			"%s: ext disp core had problems releasing audio flag\n",
+			__func__);
+	}
+
+	codec_data->ext_disp_ops.teardown_done(
+		codec_data->ext_disp_core_pdev);
+}
+
+static int msm_ext_disp_audio_codec_rx_probe(struct snd_soc_codec *codec)
+{
+	struct msm_ext_disp_audio_codec_rx_data *codec_data;
+	struct device_node *of_node_parent = NULL;
+
+	codec_data = kzalloc(sizeof(struct msm_ext_disp_audio_codec_rx_data),
+		GFP_KERNEL);
+
+	if (!codec_data) {
+		dev_err(codec->dev, "%s(): fail to allocate dai data\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	of_node_parent = of_get_parent(codec->dev->of_node);
+	if (!of_node_parent) {
+		dev_err(codec->dev, "%s(): Parent device tree node not found\n",
+				__func__);
+		kfree(codec_data);
+		return -ENODEV;
+	}
+
+	codec_data->ext_disp_core_pdev = of_find_device_by_node(of_node_parent);
+	if (!codec_data->ext_disp_core_pdev) {
+		dev_err(codec->dev, "%s(): can't get parent pdev\n", __func__);
+		kfree(codec_data);
+		return -ENODEV;
+	}
+
+	if (msm_ext_disp_register_audio_codec(codec_data->ext_disp_core_pdev,
+				&codec_data->ext_disp_ops)) {
+		dev_err(codec->dev, "%s(): can't register with ext disp core",
+				__func__);
+		kfree(codec_data);
+		return -ENODEV;
+	}
+
+	dev_set_drvdata(codec->dev, codec_data);
+
+	dev_dbg(codec->dev, "%s(): registered %s with ext disp core\n",
+		__func__, codec->component.name);
+
+	return 0;
+}
+
+static int msm_ext_disp_audio_codec_rx_remove(struct snd_soc_codec *codec)
+{
+	struct msm_ext_disp_audio_codec_rx_data *codec_data;
+
+	codec_data = dev_get_drvdata(codec->dev);
+	kfree(codec_data);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm_ext_disp_audio_codec_rx_dai_ops = {
+	.startup   = msm_ext_disp_audio_codec_rx_dai_startup,
+	.hw_params = msm_ext_disp_audio_codec_rx_dai_hw_params,
+	.shutdown  = msm_ext_disp_audio_codec_rx_dai_shutdown
+};
+
+static struct snd_soc_dai_driver msm_ext_disp_audio_codec_rx_dais[] = {
+	{
+		.name = "msm_hdmi_audio_codec_rx_dai",
+		.playback = {
+			.stream_name = "HDMI Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 48000,
+			.rate_max = 48000,
+			.rates = MSM_EXT_DISP_PCM_RATES,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+		.ops = &msm_ext_disp_audio_codec_rx_dai_ops,
+	},
+	{
+		.name = "msm_dp_audio_codec_rx_dai",
+		.playback = {
+			.stream_name = "Display Port Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 48000,
+			.rate_max = 192000,
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE,
+		},
+		.ops = &msm_ext_disp_audio_codec_rx_dai_ops,
+	},
+};
+
+static struct snd_soc_codec_driver msm_ext_disp_audio_codec_rx_soc_driver = {
+	.probe = msm_ext_disp_audio_codec_rx_probe,
+	.remove =  msm_ext_disp_audio_codec_rx_remove,
+	.component_driver = {
+		.controls = msm_ext_disp_codec_rx_controls,
+		.num_controls = ARRAY_SIZE(msm_ext_disp_codec_rx_controls),
+	},
+};
+
+static int msm_ext_disp_audio_codec_rx_plat_probe(
+		struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "%s(): dev name %s\n", __func__,
+		dev_name(&pdev->dev));
+
+	return snd_soc_register_codec(&pdev->dev,
+		&msm_ext_disp_audio_codec_rx_soc_driver,
+		msm_ext_disp_audio_codec_rx_dais,
+		ARRAY_SIZE(msm_ext_disp_audio_codec_rx_dais));
+}
+
+static int msm_ext_disp_audio_codec_rx_plat_remove(
+		struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+static const struct of_device_id msm_ext_disp_audio_codec_rx_dt_match[] = {
+	{ .compatible = "qcom,msm-ext-disp-audio-codec-rx", },
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_ext_disp_audio_codec_rx_dt_match);
+
+static struct platform_driver msm_ext_disp_audio_codec_rx_driver = {
+	.driver = {
+		.name = "msm-ext-disp-audio-codec-rx",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_ext_disp_audio_codec_rx_dt_match,
+	},
+	.probe = msm_ext_disp_audio_codec_rx_plat_probe,
+	.remove = msm_ext_disp_audio_codec_rx_plat_remove,
+};
+
+static int __init msm_ext_disp_audio_codec_rx_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&msm_ext_disp_audio_codec_rx_driver);
+	if (rc) {
+		pr_err("%s: failed to register ext disp codec driver err:%d\n",
+		       __func__, rc);
+	}
+
+	return rc;
+}
+module_init(msm_ext_disp_audio_codec_rx_init);
+
+static void __exit msm_ext_disp_audio_codec_rx_exit(void)
+{
+	platform_driver_unregister(&msm_ext_disp_audio_codec_rx_driver);
+}
+module_exit(msm_ext_disp_audio_codec_rx_exit);
+
+MODULE_DESCRIPTION("MSM External Display Audio CODEC Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/msm_stub.c b/sound/soc/codecs/msm_stub.c
new file mode 100644
index 0000000..68e55ae
--- /dev/null
+++ b/sound/soc/codecs/msm_stub.c
@@ -0,0 +1,88 @@
+/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+/* A dummy driver useful only to advertise hardware parameters */
+static struct snd_soc_dai_driver msm_stub_dais[] = {
+	{
+		.name = "msm-stub-rx",
+		.playback = { /* Support maximum range */
+			.stream_name = "Playback",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		},
+	},
+	{
+		.name = "msm-stub-tx",
+		.capture = { /* Support maximum range */
+			.stream_name = "Record",
+			.channels_min = 1,
+			.channels_max = 8,
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+		},
+	},
+};
+
+static struct snd_soc_codec_driver soc_msm_stub = {};
+
+static int msm_stub_dev_probe(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+	return snd_soc_register_codec(&pdev->dev,
+	&soc_msm_stub, msm_stub_dais, ARRAY_SIZE(msm_stub_dais));
+}
+
+static int msm_stub_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+static const struct of_device_id msm_stub_codec_dt_match[] = {
+	{ .compatible = "qcom,msm-stub-codec", },
+	{}
+};
+
+static struct platform_driver msm_stub_driver = {
+	.driver = {
+		.name = "msm-stub-codec",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_stub_codec_dt_match,
+	},
+	.probe = msm_stub_dev_probe,
+	.remove = msm_stub_dev_remove,
+};
+
+static int __init msm_stub_init(void)
+{
+	return platform_driver_register(&msm_stub_driver);
+}
+module_init(msm_stub_init);
+
+static void __exit msm_stub_exit(void)
+{
+	platform_driver_unregister(&msm_stub_driver);
+}
+module_exit(msm_stub_exit);
+
+MODULE_DESCRIPTION("Generic MSM CODEC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-dsp-mgr.c b/sound/soc/codecs/wcd-dsp-mgr.c
new file mode 100644
index 0000000..5c27f10
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-mgr.c
@@ -0,0 +1,1155 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/stringify.h>
+#include <linux/of.h>
+#include <linux/component.h>
+#include <linux/dma-mapping.h>
+#include <soc/qcom/ramdump.h>
+#include <sound/wcd-dsp-mgr.h>
+#include "wcd-dsp-utils.h"
+
+/* Forward declarations */
+static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type);
+
+/* Component related macros */
+#define WDSP_GET_COMPONENT(wdsp, x) (&(wdsp->cmpnts[x]))
+#define WDSP_GET_CMPNT_TYPE_STR(x) wdsp_get_cmpnt_type_string(x)
+
+/*
+ * These #defines indicate the bit number in status field
+ * for each of the status. If bit is set, it indicates
+ * the status as done, else if bit is not set, it indicates
+ * the status is either failed or not done.
+ */
+#define WDSP_STATUS_INITIALIZED   BIT(0)
+#define WDSP_STATUS_CODE_DLOADED  BIT(1)
+#define WDSP_STATUS_DATA_DLOADED  BIT(2)
+#define WDSP_STATUS_BOOTED        BIT(3)
+
+/* Helper macros for printing wdsp messages */
+#define WDSP_ERR(wdsp, fmt, ...)		\
+	dev_err(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+#define WDSP_DBG(wdsp, fmt, ...)	\
+	dev_dbg(wdsp->mdev, "%s: " fmt "\n", __func__, ##__VA_ARGS__)
+
+/* Helper macros for locking */
+#define WDSP_MGR_MUTEX_LOCK(wdsp, lock)         \
+{                                               \
+	WDSP_DBG(wdsp, "mutex_lock(%s)",        \
+		 __stringify_1(lock));          \
+	mutex_lock(&lock);                      \
+}
+
+#define WDSP_MGR_MUTEX_UNLOCK(wdsp, lock)       \
+{                                               \
+	WDSP_DBG(wdsp, "mutex_unlock(%s)",      \
+		 __stringify_1(lock));          \
+	mutex_unlock(&lock);                    \
+}
+
+/* Helper macros for using status mask */
+#define WDSP_SET_STATUS(wdsp, state)                  \
+{                                                     \
+	wdsp->status |= state;                        \
+	WDSP_DBG(wdsp, "set 0x%lx, new_state = 0x%x", \
+		 state, wdsp->status);                \
+}
+
+#define WDSP_CLEAR_STATUS(wdsp, state)                  \
+{                                                       \
+	wdsp->status &= (~state);                       \
+	WDSP_DBG(wdsp, "clear 0x%lx, new_state = 0x%x", \
+		 state, wdsp->status);                  \
+}
+
+#define WDSP_STATUS_IS_SET(wdsp, state) (wdsp->status & state)
+
+/* SSR relate status macros */
+#define WDSP_SSR_STATUS_WDSP_READY    BIT(0)
+#define WDSP_SSR_STATUS_CDC_READY     BIT(1)
+#define WDSP_SSR_STATUS_READY         \
+	(WDSP_SSR_STATUS_WDSP_READY | WDSP_SSR_STATUS_CDC_READY)
+#define WDSP_SSR_READY_WAIT_TIMEOUT   (10 * HZ)
+
+enum wdsp_ssr_type {
+
+	/* Init value, indicates there is no SSR in progress */
+	WDSP_SSR_TYPE_NO_SSR = 0,
+
+	/*
+	 * Indicates WDSP crashed. The manager driver internally
+	 * decides when to perform WDSP restart based on the
+	 * users of wdsp. Hence there is no explicit WDSP_UP.
+	 */
+	WDSP_SSR_TYPE_WDSP_DOWN,
+
+	/* Indicates codec hardware is down */
+	WDSP_SSR_TYPE_CDC_DOWN,
+
+	/* Indicates codec hardware is up, trigger to restart WDSP */
+	WDSP_SSR_TYPE_CDC_UP,
+};
+
+struct wdsp_cmpnt {
+
+	/* OF node of the phandle */
+	struct device_node *np;
+
+	/*
+	 * Child component's dev_name, should be set in DT for the child's
+	 * phandle if child's dev->of_node does not match the phandle->of_node
+	 */
+	const char *cdev_name;
+
+	/* Child component's device node */
+	struct device *cdev;
+
+	/* Private data that component may want back on callbacks */
+	void *priv_data;
+
+	/* Child ops */
+	struct wdsp_cmpnt_ops *ops;
+};
+
+struct wdsp_ramdump_data {
+
+	/* Ramdump device */
+	void *rd_dev;
+
+	/* DMA address of the dump */
+	dma_addr_t rd_addr;
+
+	/* Virtual address of the dump */
+	void *rd_v_addr;
+
+	/* Data provided through error interrupt */
+	struct wdsp_err_signal_arg err_data;
+};
+
+struct wdsp_mgr_priv {
+
+	/* Manager driver's struct device pointer */
+	struct device *mdev;
+
+	/* Match struct for component framework */
+	struct component_match *match;
+
+	/* Manager's ops/function callbacks */
+	struct wdsp_mgr_ops *ops;
+
+	/* Array to store information for all expected components */
+	struct wdsp_cmpnt cmpnts[WDSP_CMPNT_TYPE_MAX];
+
+	/* The filename of image to be downloaded */
+	const char *img_fname;
+
+	/* Keeps track of current state of manager driver */
+	u32 status;
+
+	/* Work to load the firmware image after component binding */
+	struct work_struct load_fw_work;
+
+	/* List of segments in image to be downloaded */
+	struct list_head *seg_list;
+
+	/* Base address of the image in memory */
+	u32 base_addr;
+
+	/* Instances using dsp */
+	int dsp_users;
+
+	/* Lock for serializing ops called by components */
+	struct mutex api_mutex;
+
+	struct wdsp_ramdump_data dump_data;
+
+	/* SSR related */
+	enum wdsp_ssr_type ssr_type;
+	struct mutex ssr_mutex;
+	struct work_struct ssr_work;
+	u16 ready_status;
+	struct completion ready_compl;
+};
+
+static char *wdsp_get_ssr_type_string(enum wdsp_ssr_type type)
+{
+	switch (type) {
+	case WDSP_SSR_TYPE_NO_SSR:
+		return "NO_SSR";
+	case WDSP_SSR_TYPE_WDSP_DOWN:
+		return "WDSP_DOWN";
+	case WDSP_SSR_TYPE_CDC_DOWN:
+		return "CDC_DOWN";
+	case WDSP_SSR_TYPE_CDC_UP:
+		return "CDC_UP";
+	default:
+		pr_err("%s: Invalid ssr_type %d\n",
+			__func__, type);
+		return "Invalid";
+	}
+}
+
+static char *wdsp_get_cmpnt_type_string(enum wdsp_cmpnt_type type)
+{
+	switch (type) {
+	case WDSP_CMPNT_CONTROL:
+		return "control";
+	case WDSP_CMPNT_IPC:
+		return "ipc";
+	case WDSP_CMPNT_TRANSPORT:
+		return "transport";
+	default:
+		pr_err("%s: Invalid component type %d\n",
+			__func__, type);
+		return "Invalid";
+	}
+}
+
+static void __wdsp_clr_ready_locked(struct wdsp_mgr_priv *wdsp,
+				    u16 value)
+{
+	wdsp->ready_status &= ~(value);
+	WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
+}
+
+static void __wdsp_set_ready_locked(struct wdsp_mgr_priv *wdsp,
+				    u16 value, bool mark_complete)
+{
+	wdsp->ready_status |= value;
+	WDSP_DBG(wdsp, "ready_status = 0x%x", wdsp->ready_status);
+
+	if (mark_complete &&
+	    wdsp->ready_status == WDSP_SSR_STATUS_READY) {
+		WDSP_DBG(wdsp, "marking ready completion");
+		complete(&wdsp->ready_compl);
+	}
+}
+
+static void wdsp_broadcast_event_upseq(struct wdsp_mgr_priv *wdsp,
+				       enum wdsp_event_type event,
+				       void *data)
+{
+	struct wdsp_cmpnt *cmpnt;
+	int i;
+
+	for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+		cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+		if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
+			cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+						  event, data);
+	}
+}
+
+static void wdsp_broadcast_event_downseq(struct wdsp_mgr_priv *wdsp,
+					 enum wdsp_event_type event,
+					 void *data)
+{
+	struct wdsp_cmpnt *cmpnt;
+	int i;
+
+	for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+		cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+		if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler)
+			cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+						  event, data);
+	}
+}
+
+static int wdsp_unicast_event(struct wdsp_mgr_priv *wdsp,
+			      enum wdsp_cmpnt_type type,
+			      enum wdsp_event_type event,
+			      void *data)
+{
+	struct wdsp_cmpnt *cmpnt;
+	int ret;
+
+	cmpnt = WDSP_GET_COMPONENT(wdsp, type);
+	if (cmpnt && cmpnt->ops && cmpnt->ops->event_handler) {
+		ret = cmpnt->ops->event_handler(cmpnt->cdev, cmpnt->priv_data,
+						event, data);
+	} else {
+		WDSP_ERR(wdsp, "not valid event_handler for %s",
+			 WDSP_GET_CMPNT_TYPE_STR(type));
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void wdsp_deinit_components(struct wdsp_mgr_priv *wdsp)
+{
+	struct wdsp_cmpnt *cmpnt;
+	int i;
+
+	for (i = WDSP_CMPNT_TYPE_MAX - 1; i >= 0; i--) {
+		cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+		if (cmpnt && cmpnt->ops && cmpnt->ops->deinit)
+			cmpnt->ops->deinit(cmpnt->cdev, cmpnt->priv_data);
+	}
+}
+
+static int wdsp_init_components(struct wdsp_mgr_priv *wdsp)
+{
+	struct wdsp_cmpnt *cmpnt;
+	int fail_idx = WDSP_CMPNT_TYPE_MAX;
+	int i, ret = 0;
+
+	for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+
+		cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+
+		/* Init is allowed to be NULL */
+		if (!cmpnt->ops || !cmpnt->ops->init)
+			continue;
+		ret = cmpnt->ops->init(cmpnt->cdev, cmpnt->priv_data);
+		if (ret) {
+			WDSP_ERR(wdsp, "Init failed (%d) for component %s",
+				 ret, WDSP_GET_CMPNT_TYPE_STR(i));
+				fail_idx = i;
+				break;
+		}
+	}
+
+	if (fail_idx < WDSP_CMPNT_TYPE_MAX) {
+		/* Undo init for already initialized components */
+		for (i = fail_idx - 1; i >= 0; i--) {
+			struct wdsp_cmpnt *cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+
+			if (cmpnt->ops && cmpnt->ops->deinit)
+				cmpnt->ops->deinit(cmpnt->cdev,
+						   cmpnt->priv_data);
+		}
+	} else {
+		wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_INIT, NULL);
+	}
+
+	return ret;
+}
+
+static int wdsp_load_each_segment(struct wdsp_mgr_priv *wdsp,
+				  struct wdsp_img_segment *seg)
+{
+	struct wdsp_img_section img_section;
+	int ret;
+
+	WDSP_DBG(wdsp,
+		 "base_addr 0x%x, split_fname %s, load_addr 0x%x, size 0x%zx",
+		 wdsp->base_addr, seg->split_fname, seg->load_addr, seg->size);
+
+	if (seg->load_addr < wdsp->base_addr) {
+		WDSP_ERR(wdsp, "Invalid addr 0x%x, base_addr = 0x%x",
+			 seg->load_addr, wdsp->base_addr);
+		return -EINVAL;
+	}
+
+	img_section.addr = seg->load_addr - wdsp->base_addr;
+	img_section.size = seg->size;
+	img_section.data = seg->data;
+
+	ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
+				 WDSP_EVENT_DLOAD_SECTION,
+				 &img_section);
+	if (ret < 0)
+		WDSP_ERR(wdsp,
+			 "Failed, err = %d for base_addr = 0x%x split_fname = %s, load_addr = 0x%x, size = 0x%zx",
+			 ret, wdsp->base_addr, seg->split_fname,
+			 seg->load_addr, seg->size);
+	return ret;
+}
+
+static int wdsp_download_segments(struct wdsp_mgr_priv *wdsp,
+				  unsigned int type)
+{
+	struct wdsp_cmpnt *ctl;
+	struct wdsp_img_segment *seg = NULL;
+	enum wdsp_event_type pre, post;
+	long status;
+	int ret;
+
+	ctl = WDSP_GET_COMPONENT(wdsp, WDSP_CMPNT_CONTROL);
+
+	if (type == WDSP_ELF_FLAG_RE) {
+		pre = WDSP_EVENT_PRE_DLOAD_CODE;
+		post = WDSP_EVENT_POST_DLOAD_CODE;
+		status = WDSP_STATUS_CODE_DLOADED;
+	} else if (type == WDSP_ELF_FLAG_WRITE) {
+		pre = WDSP_EVENT_PRE_DLOAD_DATA;
+		post = WDSP_EVENT_POST_DLOAD_DATA;
+		status = WDSP_STATUS_DATA_DLOADED;
+	} else {
+		WDSP_ERR(wdsp, "Invalid type %u", type);
+		return -EINVAL;
+	}
+
+	ret = wdsp_get_segment_list(ctl->cdev, wdsp->img_fname,
+				    type, wdsp->seg_list, &wdsp->base_addr);
+	if (ret < 0 ||
+	    list_empty(wdsp->seg_list)) {
+		WDSP_ERR(wdsp, "Error %d to get image segments for type %d",
+			 ret, type);
+		wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_DLOAD_FAILED,
+					     NULL);
+		goto done;
+	}
+
+	/* Notify all components that image is about to be downloaded */
+	wdsp_broadcast_event_upseq(wdsp, pre, NULL);
+
+	/* Go through the list of segments and download one by one */
+	list_for_each_entry(seg, wdsp->seg_list, list) {
+		ret = wdsp_load_each_segment(wdsp, seg);
+		if (ret < 0) {
+			wdsp_broadcast_event_downseq(wdsp,
+						     WDSP_EVENT_DLOAD_FAILED,
+						     NULL);
+			goto dload_error;
+		}
+	}
+
+	WDSP_SET_STATUS(wdsp, status);
+
+	/* Notify all components that image is downloaded */
+	wdsp_broadcast_event_downseq(wdsp, post, NULL);
+
+dload_error:
+	wdsp_flush_segment_list(wdsp->seg_list);
+done:
+	return ret;
+}
+
+static int wdsp_init_and_dload_code_sections(struct wdsp_mgr_priv *wdsp)
+{
+	int ret;
+	bool is_initialized;
+
+	is_initialized = WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_INITIALIZED);
+
+	if (!is_initialized) {
+		/* Components are not initialized yet, initialize them */
+		ret = wdsp_init_components(wdsp);
+		if (ret < 0) {
+			WDSP_ERR(wdsp, "INIT failed, err = %d", ret);
+			goto done;
+		}
+		WDSP_SET_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+	}
+
+	/* Download the read-execute sections of image */
+	ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_RE);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Error %d to download code sections", ret);
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static void wdsp_load_fw_image(struct work_struct *work)
+{
+	struct wdsp_mgr_priv *wdsp;
+	int ret;
+
+	wdsp = container_of(work, struct wdsp_mgr_priv, load_fw_work);
+	if (!wdsp) {
+		pr_err("%s: Invalid private_data\n", __func__);
+		return;
+	}
+
+	ret = wdsp_init_and_dload_code_sections(wdsp);
+	if (ret < 0)
+		WDSP_ERR(wdsp, "dload code sections failed, err = %d", ret);
+}
+
+static int wdsp_enable_dsp(struct wdsp_mgr_priv *wdsp)
+{
+	int ret;
+
+	/* Make sure wdsp is in good state */
+	if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_CODE_DLOADED)) {
+		WDSP_ERR(wdsp, "WDSP in invalid state 0x%x", wdsp->status);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Download the read-write sections of image */
+	ret = wdsp_download_segments(wdsp, WDSP_ELF_FLAG_WRITE);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Data section download failed, err = %d", ret);
+		goto done;
+	}
+
+	wdsp_broadcast_event_upseq(wdsp, WDSP_EVENT_PRE_BOOTUP, NULL);
+
+	ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+				 WDSP_EVENT_DO_BOOT, NULL);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Failed to boot dsp, err = %d", ret);
+		WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+		goto done;
+	}
+
+	wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_BOOTUP, NULL);
+	WDSP_SET_STATUS(wdsp, WDSP_STATUS_BOOTED);
+done:
+	return ret;
+}
+
+static int wdsp_disable_dsp(struct wdsp_mgr_priv *wdsp)
+{
+	int ret;
+
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+	/*
+	 * If Disable happened while SSR is in progress, then set the SSR
+	 * ready status indicating WDSP is now ready. Ignore the disable
+	 * event here and let the SSR handler go through shutdown.
+	 */
+	if (wdsp->ssr_type != WDSP_SSR_TYPE_NO_SSR) {
+		__wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY, true);
+		WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+		return 0;
+	}
+
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+
+	/* Make sure wdsp is in good state */
+	if (!WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+		WDSP_ERR(wdsp, "wdsp in invalid state 0x%x", wdsp->status);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN, NULL);
+	ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+				 WDSP_EVENT_DO_SHUTDOWN, NULL);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Failed to shutdown dsp, err = %d", ret);
+		goto done;
+	}
+
+	wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN, NULL);
+	WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
+
+	/* Data sections are to be downloaded per boot */
+	WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+done:
+	return ret;
+}
+
+static int wdsp_register_cmpnt_ops(struct device *wdsp_dev,
+				   struct device *cdev,
+				   void *priv_data,
+				   struct wdsp_cmpnt_ops *ops)
+{
+	struct wdsp_mgr_priv *wdsp;
+	struct wdsp_cmpnt *cmpnt;
+	int i, ret;
+
+	if (!wdsp_dev || !cdev || !ops)
+		return -EINVAL;
+
+	wdsp = dev_get_drvdata(wdsp_dev);
+
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+
+	for (i = 0; i < WDSP_CMPNT_TYPE_MAX; i++) {
+		cmpnt = WDSP_GET_COMPONENT(wdsp, i);
+		if ((cdev->of_node && cdev->of_node == cmpnt->np) ||
+		    (cmpnt->cdev_name &&
+		     !strcmp(dev_name(cdev), cmpnt->cdev_name))) {
+			break;
+		}
+	}
+
+	if (i == WDSP_CMPNT_TYPE_MAX) {
+		WDSP_ERR(wdsp, "Failed to register component dev %s",
+			 dev_name(cdev));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cmpnt->cdev = cdev;
+	cmpnt->ops = ops;
+	cmpnt->priv_data = priv_data;
+done:
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+	return 0;
+}
+
+static struct device *wdsp_get_dev_for_cmpnt(struct device *wdsp_dev,
+					     enum wdsp_cmpnt_type type)
+{
+	struct wdsp_mgr_priv *wdsp;
+	struct wdsp_cmpnt *cmpnt;
+
+	if (!wdsp_dev || type >= WDSP_CMPNT_TYPE_MAX)
+		return NULL;
+
+	wdsp = dev_get_drvdata(wdsp_dev);
+	cmpnt = WDSP_GET_COMPONENT(wdsp, type);
+
+	return cmpnt->cdev;
+}
+
+static void wdsp_collect_ramdumps(struct wdsp_mgr_priv *wdsp)
+{
+	struct wdsp_img_section img_section;
+	struct wdsp_err_signal_arg *data = &wdsp->dump_data.err_data;
+	struct ramdump_segment rd_seg;
+	int ret = 0;
+
+	if (wdsp->ssr_type != WDSP_SSR_TYPE_WDSP_DOWN ||
+	    !data->mem_dumps_enabled) {
+		WDSP_DBG(wdsp, "cannot dump memory, ssr_type %s, dumps %s",
+			 wdsp_get_ssr_type_string(wdsp->ssr_type),
+			 !(data->mem_dumps_enabled) ? "disabled" : "enabled");
+		goto done;
+	}
+
+	if (data->dump_size == 0 ||
+	    data->remote_start_addr < wdsp->base_addr) {
+		WDSP_ERR(wdsp, "Invalid start addr 0x%x or dump_size 0x%zx",
+			 data->remote_start_addr, data->dump_size);
+		goto done;
+	}
+
+	if (!wdsp->dump_data.rd_dev) {
+		WDSP_ERR(wdsp, "Ramdump device is not setup");
+		goto done;
+	}
+
+	WDSP_DBG(wdsp, "base_addr 0x%x, dump_start_addr 0x%x, dump_size 0x%zx",
+		 wdsp->base_addr, data->remote_start_addr, data->dump_size);
+
+	/* Allocate memory for dumps */
+	wdsp->dump_data.rd_v_addr = dma_alloc_coherent(wdsp->mdev,
+						       data->dump_size,
+						       &wdsp->dump_data.rd_addr,
+						       GFP_KERNEL);
+	if (!wdsp->dump_data.rd_v_addr)
+		goto done;
+
+	img_section.addr = data->remote_start_addr - wdsp->base_addr;
+	img_section.size = data->dump_size;
+	img_section.data = wdsp->dump_data.rd_v_addr;
+
+	ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_TRANSPORT,
+				 WDSP_EVENT_READ_SECTION,
+				 &img_section);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Failed to read dumps, size 0x%zx at addr 0x%x",
+			 img_section.size, img_section.addr);
+		goto err_read_dumps;
+	}
+
+	rd_seg.address = (unsigned long) wdsp->dump_data.rd_v_addr;
+	rd_seg.size = img_section.size;
+	rd_seg.v_address = wdsp->dump_data.rd_v_addr;
+
+	ret = do_ramdump(wdsp->dump_data.rd_dev, &rd_seg, 1);
+	if (ret < 0)
+		WDSP_ERR(wdsp, "do_ramdump failed with error %d", ret);
+
+err_read_dumps:
+	dma_free_coherent(wdsp->mdev, data->dump_size,
+			  wdsp->dump_data.rd_v_addr, wdsp->dump_data.rd_addr);
+done:
+	return;
+}
+
+static void wdsp_ssr_work_fn(struct work_struct *work)
+{
+	struct wdsp_mgr_priv *wdsp;
+	int ret;
+
+	wdsp = container_of(work, struct wdsp_mgr_priv, ssr_work);
+	if (!wdsp) {
+		pr_err("%s: Invalid private_data\n", __func__);
+		return;
+	}
+
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+	/* Issue ramdumps and shutdown only if DSP is currently booted */
+	if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+		wdsp_collect_ramdumps(wdsp);
+		ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_CONTROL,
+					 WDSP_EVENT_DO_SHUTDOWN, NULL);
+		if (ret < 0)
+			WDSP_ERR(wdsp, "Failed WDSP shutdown, err = %d", ret);
+
+		wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_POST_SHUTDOWN,
+					     NULL);
+		WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_BOOTED);
+	}
+
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+	ret = wait_for_completion_timeout(&wdsp->ready_compl,
+					  WDSP_SSR_READY_WAIT_TIMEOUT);
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+	if (ret == 0) {
+		WDSP_ERR(wdsp, "wait_for_ready timed out, status = 0x%x",
+			 wdsp->ready_status);
+		goto done;
+	}
+
+	/* Data sections are to downloaded per WDSP boot */
+	WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_DATA_DLOADED);
+
+	/*
+	 * Even though code section could possible be retained on DSP
+	 * crash, go ahead and still re-download just to avoid any
+	 * memory corruption from previous crash.
+	 */
+	WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_CODE_DLOADED);
+
+	/* If codec restarted, then all components must be re-initialized */
+	if (wdsp->ssr_type == WDSP_SSR_TYPE_CDC_UP) {
+		wdsp_deinit_components(wdsp);
+		WDSP_CLEAR_STATUS(wdsp, WDSP_STATUS_INITIALIZED);
+	}
+
+	ret = wdsp_init_and_dload_code_sections(wdsp);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Failed to dload code sections err = %d",
+			 ret);
+		goto done;
+	}
+
+	/* SSR handling is finished, mark SSR type as NO_SSR */
+	wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
+done:
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+}
+
+static int wdsp_ssr_handler(struct wdsp_mgr_priv *wdsp, void *arg,
+			    enum wdsp_ssr_type ssr_type)
+{
+	enum wdsp_ssr_type current_ssr_type;
+	struct wdsp_err_signal_arg *err_data;
+
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->ssr_mutex);
+
+	current_ssr_type = wdsp->ssr_type;
+	WDSP_DBG(wdsp, "Current ssr_type %s, handling ssr_type %s",
+		 wdsp_get_ssr_type_string(current_ssr_type),
+		 wdsp_get_ssr_type_string(ssr_type));
+	wdsp->ssr_type = ssr_type;
+
+	if (arg) {
+		err_data = (struct wdsp_err_signal_arg *) arg;
+		memcpy(&wdsp->dump_data.err_data, err_data,
+		       sizeof(*err_data));
+	} else {
+		memset(&wdsp->dump_data.err_data, 0,
+		       sizeof(wdsp->dump_data.err_data));
+	}
+
+	switch (ssr_type) {
+
+	case WDSP_SSR_TYPE_WDSP_DOWN:
+		__wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_WDSP_READY);
+		wdsp_broadcast_event_downseq(wdsp, WDSP_EVENT_PRE_SHUTDOWN,
+					     NULL);
+		schedule_work(&wdsp->ssr_work);
+		break;
+
+	case WDSP_SSR_TYPE_CDC_DOWN:
+		__wdsp_clr_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY);
+		/*
+		 * If DSP is booted when CDC_DOWN is received, it needs
+		 * to be shutdown.
+		 */
+		if (WDSP_STATUS_IS_SET(wdsp, WDSP_STATUS_BOOTED)) {
+			__wdsp_clr_ready_locked(wdsp,
+						WDSP_SSR_STATUS_WDSP_READY);
+			wdsp_broadcast_event_downseq(wdsp,
+						     WDSP_EVENT_PRE_SHUTDOWN,
+						     NULL);
+		}
+
+		schedule_work(&wdsp->ssr_work);
+		break;
+
+	case WDSP_SSR_TYPE_CDC_UP:
+		__wdsp_set_ready_locked(wdsp, WDSP_SSR_STATUS_CDC_READY, true);
+		break;
+
+	default:
+		WDSP_ERR(wdsp, "undefined ssr_type %d\n", ssr_type);
+		/* Revert back the ssr_type for undefined events */
+		wdsp->ssr_type = current_ssr_type;
+		break;
+	}
+
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->ssr_mutex);
+
+	return 0;
+}
+
+static int wdsp_signal_handler(struct device *wdsp_dev,
+			       enum wdsp_signal signal, void *arg)
+{
+	struct wdsp_mgr_priv *wdsp;
+	int ret;
+
+	if (!wdsp_dev)
+		return -EINVAL;
+
+	wdsp = dev_get_drvdata(wdsp_dev);
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+
+	WDSP_DBG(wdsp, "Raised signal %d", signal);
+
+	switch (signal) {
+	case WDSP_IPC1_INTR:
+		ret = wdsp_unicast_event(wdsp, WDSP_CMPNT_IPC,
+					 WDSP_EVENT_IPC1_INTR, NULL);
+		break;
+	case WDSP_ERR_INTR:
+		ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_WDSP_DOWN);
+		break;
+	case WDSP_CDC_DOWN_SIGNAL:
+		ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_DOWN);
+		break;
+	case WDSP_CDC_UP_SIGNAL:
+		ret = wdsp_ssr_handler(wdsp, arg, WDSP_SSR_TYPE_CDC_UP);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	if (ret < 0)
+		WDSP_ERR(wdsp, "handling signal %d failed with error %d",
+			 signal, ret);
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+
+	return ret;
+}
+
+static int wdsp_vote_for_dsp(struct device *wdsp_dev,
+			     bool vote)
+{
+	struct wdsp_mgr_priv *wdsp;
+	int ret = 0;
+
+	if (!wdsp_dev)
+		return -EINVAL;
+
+	wdsp = dev_get_drvdata(wdsp_dev);
+
+	WDSP_MGR_MUTEX_LOCK(wdsp, wdsp->api_mutex);
+	WDSP_DBG(wdsp, "request %s, current users = %d",
+		 vote ? "enable" : "disable", wdsp->dsp_users);
+
+	if (vote) {
+		wdsp->dsp_users++;
+		if (wdsp->dsp_users == 1)
+			ret = wdsp_enable_dsp(wdsp);
+	} else {
+		if (wdsp->dsp_users == 0)
+			goto done;
+
+		wdsp->dsp_users--;
+		if (wdsp->dsp_users == 0)
+			ret = wdsp_disable_dsp(wdsp);
+	}
+
+	if (ret < 0)
+		WDSP_DBG(wdsp, "wdsp %s failed, err = %d",
+			 vote ? "enable" : "disable", ret);
+
+done:
+	WDSP_MGR_MUTEX_UNLOCK(wdsp, wdsp->api_mutex);
+	return ret;
+}
+
+static int wdsp_suspend(struct device *wdsp_dev)
+{
+	return 0;
+}
+
+static int wdsp_resume(struct device *wdsp_dev)
+{
+	return 0;
+}
+
+static struct wdsp_mgr_ops wdsp_ops = {
+	.register_cmpnt_ops = wdsp_register_cmpnt_ops,
+	.get_dev_for_cmpnt = wdsp_get_dev_for_cmpnt,
+	.signal_handler = wdsp_signal_handler,
+	.vote_for_dsp = wdsp_vote_for_dsp,
+	.suspend = wdsp_suspend,
+	.resume = wdsp_resume,
+};
+
+static int wdsp_mgr_compare_of(struct device *dev, void *data)
+{
+	struct wdsp_cmpnt *cmpnt = data;
+
+	/*
+	 * First try to match based on of_node, if of_node is not
+	 * present, try to match on the dev_name
+	 */
+	return ((dev->of_node && dev->of_node == cmpnt->np) ||
+		(cmpnt->cdev_name &&
+		 !strcmp(dev_name(dev), cmpnt->cdev_name)));
+}
+
+static int wdsp_mgr_bind(struct device *dev)
+{
+	struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
+	struct wdsp_cmpnt *cmpnt;
+	int ret, idx;
+
+	wdsp->ops = &wdsp_ops;
+
+	/* Setup ramdump device */
+	wdsp->dump_data.rd_dev = create_ramdump_device("wdsp", dev);
+	if (!wdsp->dump_data.rd_dev)
+		dev_info(dev, "%s: create_ramdump_device failed\n", __func__);
+
+	ret = component_bind_all(dev, wdsp->ops);
+	if (ret < 0)
+		WDSP_ERR(wdsp, "component_bind_all failed %d\n", ret);
+
+	/* Make sure all components registered ops */
+	for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+		cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+		if (!cmpnt->cdev || !cmpnt->ops) {
+			WDSP_ERR(wdsp, "%s did not register ops\n",
+				 WDSP_GET_CMPNT_TYPE_STR(idx));
+			ret = -EINVAL;
+			component_unbind_all(dev, wdsp->ops);
+			break;
+		}
+	}
+
+	/* Schedule the work to download image if binding was successful. */
+	if (!ret)
+		schedule_work(&wdsp->load_fw_work);
+
+	return ret;
+}
+
+static void wdsp_mgr_unbind(struct device *dev)
+{
+	struct wdsp_mgr_priv *wdsp = dev_get_drvdata(dev);
+	struct wdsp_cmpnt *cmpnt;
+	int idx;
+
+	component_unbind_all(dev, wdsp->ops);
+
+	if (wdsp->dump_data.rd_dev) {
+		destroy_ramdump_device(wdsp->dump_data.rd_dev);
+		wdsp->dump_data.rd_dev = NULL;
+	}
+
+	/* Clear all status bits */
+	wdsp->status = 0x00;
+
+	/* clean up the components */
+	for (idx = 0; idx < WDSP_CMPNT_TYPE_MAX; idx++) {
+		cmpnt = WDSP_GET_COMPONENT(wdsp, idx);
+		cmpnt->cdev = NULL;
+		cmpnt->ops = NULL;
+		cmpnt->priv_data = NULL;
+	}
+}
+
+static const struct component_master_ops wdsp_master_ops = {
+	.bind = wdsp_mgr_bind,
+	.unbind = wdsp_mgr_unbind,
+};
+
+static void *wdsp_mgr_parse_phandle(struct wdsp_mgr_priv *wdsp,
+				    int index)
+{
+	struct device *mdev = wdsp->mdev;
+	struct device_node *np;
+	struct wdsp_cmpnt *cmpnt = NULL;
+	struct of_phandle_args pargs;
+	u32 value;
+	int ret;
+
+	ret = of_parse_phandle_with_fixed_args(mdev->of_node,
+					      "qcom,wdsp-components", 1,
+					      index, &pargs);
+	if (ret) {
+		WDSP_ERR(wdsp, "parse_phandle at index %d failed %d",
+			 index, ret);
+		return NULL;
+	}
+
+	np = pargs.np;
+	value = pargs.args[0];
+
+	if (value >= WDSP_CMPNT_TYPE_MAX) {
+		WDSP_ERR(wdsp, "invalid phandle_arg to of_node %s", np->name);
+		goto done;
+	}
+
+	cmpnt = WDSP_GET_COMPONENT(wdsp, value);
+	if (cmpnt->np || cmpnt->cdev_name) {
+		WDSP_ERR(wdsp, "cmpnt %d already added", value);
+		cmpnt = NULL;
+		goto done;
+	}
+
+	cmpnt->np = np;
+	of_property_read_string(np, "qcom,wdsp-cmpnt-dev-name",
+				&cmpnt->cdev_name);
+done:
+	of_node_put(np);
+	return cmpnt;
+}
+
+static int wdsp_mgr_parse_dt_entries(struct wdsp_mgr_priv *wdsp)
+{
+	struct device *dev = wdsp->mdev;
+	void *match_data;
+	int ph_idx, ret;
+
+	ret = of_property_read_string(dev->of_node, "qcom,img-filename",
+				      &wdsp->img_fname);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Reading property %s failed, error = %d",
+			 "qcom,img-filename", ret);
+		return ret;
+	}
+
+	ret = of_count_phandle_with_args(dev->of_node,
+					 "qcom,wdsp-components",
+					 NULL);
+	if (ret == -ENOENT) {
+		WDSP_ERR(wdsp, "Property %s not defined in DT",
+			 "qcom,wdsp-components");
+		goto done;
+	} else if (ret != WDSP_CMPNT_TYPE_MAX * 2) {
+		WDSP_ERR(wdsp, "Invalid phandle + arg count %d, expected %d",
+			 ret, WDSP_CMPNT_TYPE_MAX * 2);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = 0;
+
+	for (ph_idx = 0; ph_idx < WDSP_CMPNT_TYPE_MAX; ph_idx++) {
+
+		match_data = wdsp_mgr_parse_phandle(wdsp, ph_idx);
+		if (!match_data) {
+			WDSP_ERR(wdsp, "component not found at idx %d", ph_idx);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		component_match_add(dev, &wdsp->match,
+				    wdsp_mgr_compare_of, match_data);
+	}
+
+done:
+	return ret;
+}
+
+static int wdsp_mgr_probe(struct platform_device *pdev)
+{
+	struct wdsp_mgr_priv *wdsp;
+	struct device *mdev = &pdev->dev;
+	int ret;
+
+	wdsp = devm_kzalloc(mdev, sizeof(*wdsp), GFP_KERNEL);
+	if (!wdsp)
+		return -ENOMEM;
+	wdsp->mdev = mdev;
+	wdsp->seg_list = devm_kzalloc(mdev, sizeof(struct list_head),
+				      GFP_KERNEL);
+	if (!wdsp->seg_list) {
+		devm_kfree(mdev, wdsp);
+		return -ENOMEM;
+	}
+
+	ret = wdsp_mgr_parse_dt_entries(wdsp);
+	if (ret)
+		goto err_dt_parse;
+
+	INIT_WORK(&wdsp->load_fw_work, wdsp_load_fw_image);
+	INIT_LIST_HEAD(wdsp->seg_list);
+	mutex_init(&wdsp->api_mutex);
+	mutex_init(&wdsp->ssr_mutex);
+	wdsp->ssr_type = WDSP_SSR_TYPE_NO_SSR;
+	wdsp->ready_status = WDSP_SSR_STATUS_READY;
+	INIT_WORK(&wdsp->ssr_work, wdsp_ssr_work_fn);
+	init_completion(&wdsp->ready_compl);
+	arch_setup_dma_ops(wdsp->mdev, 0, 0, NULL, 0);
+	dev_set_drvdata(mdev, wdsp);
+
+	ret = component_master_add_with_match(mdev, &wdsp_master_ops,
+					      wdsp->match);
+	if (ret < 0) {
+		WDSP_ERR(wdsp, "Failed to add master, err = %d", ret);
+		goto err_master_add;
+	}
+
+	return 0;
+
+err_master_add:
+	mutex_destroy(&wdsp->api_mutex);
+	mutex_destroy(&wdsp->ssr_mutex);
+err_dt_parse:
+	devm_kfree(mdev, wdsp->seg_list);
+	devm_kfree(mdev, wdsp);
+	dev_set_drvdata(mdev, NULL);
+
+	return ret;
+}
+
+static int wdsp_mgr_remove(struct platform_device *pdev)
+{
+	struct device *mdev = &pdev->dev;
+	struct wdsp_mgr_priv *wdsp = dev_get_drvdata(mdev);
+
+	component_master_del(mdev, &wdsp_master_ops);
+
+	mutex_destroy(&wdsp->api_mutex);
+	mutex_destroy(&wdsp->ssr_mutex);
+	devm_kfree(mdev, wdsp->seg_list);
+	devm_kfree(mdev, wdsp);
+	dev_set_drvdata(mdev, NULL);
+
+	return 0;
+};
+
+static const struct of_device_id wdsp_mgr_dt_match[] = {
+	{.compatible = "qcom,wcd-dsp-mgr" },
+	{ }
+};
+
+static struct platform_driver wdsp_mgr_driver = {
+	.driver = {
+		.name = "wcd-dsp-mgr",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(wdsp_mgr_dt_match),
+	},
+	.probe = wdsp_mgr_probe,
+	.remove = wdsp_mgr_remove,
+};
+module_platform_driver(wdsp_mgr_driver);
+
+MODULE_DESCRIPTION("WCD DSP manager driver");
+MODULE_DEVICE_TABLE(of, wdsp_mgr_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-dsp-utils.c b/sound/soc/codecs/wcd-dsp-utils.c
new file mode 100644
index 0000000..4eafd55
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-utils.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/elf.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include "wcd-dsp-utils.h"
+
+static bool wdsp_is_valid_elf_hdr(const struct elf32_hdr *ehdr,
+				  size_t fw_size)
+{
+	if (fw_size < sizeof(*ehdr)) {
+		pr_err("%s: Firmware too small\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+		pr_err("%s: Not an ELF file\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+		pr_err("%s: Not an executable image\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (ehdr->e_phnum == 0) {
+		pr_err("%s: no segments to load\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
+	    sizeof(struct elf32_hdr) > fw_size) {
+		pr_err("%s: Too small MDT file\n", __func__);
+		goto elf_check_fail;
+	}
+
+	return true;
+
+elf_check_fail:
+	return false;
+}
+
+static int wdsp_add_segment_to_list(struct device *dev,
+				    const char *img_fname,
+				    const struct elf32_phdr *phdr,
+				    int phdr_idx,
+				    struct list_head *seg_list)
+{
+	struct wdsp_img_segment *seg;
+	int ret = 0;
+
+	/* Do not load segments with zero size */
+	if (phdr->p_filesz == 0 || phdr->p_memsz == 0)
+		goto done;
+
+	seg = kzalloc(sizeof(*seg), GFP_KERNEL);
+	if (!seg) {
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	snprintf(seg->split_fname, sizeof(seg->split_fname),
+		 "%s.b%02d", img_fname, phdr_idx);
+	ret = request_firmware(&seg->split_fw, seg->split_fname, dev);
+	if (ret < 0) {
+		dev_err(dev, "%s: firmware %s not found\n",
+			__func__, seg->split_fname);
+		goto bad_seg;
+	}
+
+	seg->load_addr = phdr->p_paddr;
+	seg->size = phdr->p_filesz;
+	seg->data = (u8 *) seg->split_fw->data;
+
+	list_add_tail(&seg->list, seg_list);
+done:
+	return ret;
+bad_seg:
+	kfree(seg);
+	return ret;
+}
+
+/*
+ * wdsp_flush_segment_list: Flush the list of segments
+ * @seg_list: List of segments to be flushed
+ * This API will traverse through the list of segments provided in
+ * seg_list, release the firmware for each segment and delete the
+ * segment from the list.
+ */
+void wdsp_flush_segment_list(struct list_head *seg_list)
+{
+	struct wdsp_img_segment *seg, *next;
+
+	list_for_each_entry_safe(seg, next, seg_list, list) {
+		release_firmware(seg->split_fw);
+		list_del(&seg->list);
+		kfree(seg);
+	}
+}
+EXPORT_SYMBOL(wdsp_flush_segment_list);
+
+/*
+ * wdsp_get_segment_list: Get the list of requested segments
+ * @dev: struct device pointer of caller
+ * @img_fname: Image name for the mdt and split firmware files
+ * @segment_type: Requested segment type, should be either
+ *		  WDSP_ELF_FLAG_RE or WDSP_ELF_FLAG_WRITE
+ * @seg_list: An initialized head for list of segmented to be returned
+ * @entry_point: Pointer to return the entry point of the image
+ * This API will parse the mdt file for img_fname and create
+ * an struct wdsp_img_segment for each segment that matches segment_type
+ * and add this structure to list pointed by seg_list
+ */
+int wdsp_get_segment_list(struct device *dev,
+			  const char *img_fname,
+			  unsigned int segment_type,
+			  struct list_head *seg_list,
+			  u32 *entry_point)
+{
+	const struct firmware *fw;
+	const struct elf32_hdr *ehdr;
+	const struct elf32_phdr *phdr;
+	const u8 *elf_ptr;
+	char mdt_name[WDSP_IMG_NAME_LEN_MAX];
+	int ret, phdr_idx;
+	bool segment_match;
+
+	if (!dev) {
+		ret = -EINVAL;
+		pr_err("%s: Invalid device handle\n", __func__);
+		goto done;
+	}
+
+	if (!img_fname || !seg_list || !entry_point) {
+		ret = -EINVAL;
+		dev_err(dev, "%s: Invalid input params\n",
+			__func__);
+		goto done;
+	}
+
+	if (segment_type != WDSP_ELF_FLAG_RE &&
+	    segment_type != WDSP_ELF_FLAG_WRITE) {
+		dev_err(dev, "%s: Invalid request for segment_type %d\n",
+			__func__, segment_type);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", img_fname);
+	ret = request_firmware(&fw, mdt_name, dev);
+	if (ret < 0) {
+		dev_err(dev, "%s: firmware %s not found\n",
+			__func__, mdt_name);
+		goto done;
+	}
+
+	ehdr = (struct elf32_hdr *) fw->data;
+	*entry_point = ehdr->e_entry;
+	if (!wdsp_is_valid_elf_hdr(ehdr, fw->size)) {
+		dev_err(dev, "%s: fw mdt %s is invalid\n",
+			__func__, mdt_name);
+		ret = -EINVAL;
+		goto bad_elf;
+	}
+
+	elf_ptr = fw->data + sizeof(*ehdr);
+	for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
+		phdr = (struct elf32_phdr *) elf_ptr;
+		segment_match = false;
+
+		switch (segment_type) {
+		case WDSP_ELF_FLAG_RE:
+			/*
+			 * Flag can be READ or EXECUTE or both but
+			 * WRITE flag should not be set.
+			 */
+			if ((phdr->p_flags & segment_type) &&
+			    !(phdr->p_flags & WDSP_ELF_FLAG_WRITE))
+				segment_match = true;
+			break;
+		case WDSP_ELF_FLAG_WRITE:
+			/*
+			 * If WRITE flag is set, other flags do not
+			 * matter.
+			 */
+			if (phdr->p_flags & segment_type)
+				segment_match = true;
+			break;
+		}
+
+		if (segment_match) {
+			ret = wdsp_add_segment_to_list(dev, img_fname, phdr,
+						       phdr_idx, seg_list);
+			if (ret < 0) {
+				wdsp_flush_segment_list(seg_list);
+				goto bad_elf;
+			}
+		}
+		elf_ptr = elf_ptr + sizeof(*phdr);
+	}
+
+bad_elf:
+	release_firmware(fw);
+done:
+	return ret;
+}
+EXPORT_SYMBOL(wdsp_get_segment_list);
diff --git a/sound/soc/codecs/wcd-dsp-utils.h b/sound/soc/codecs/wcd-dsp-utils.h
new file mode 100644
index 0000000..a530a1c
--- /dev/null
+++ b/sound/soc/codecs/wcd-dsp-utils.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_DSP_UTILS_H__
+#define __WCD_DSP_UTILS_H__
+
+#define WDSP_IMG_NAME_LEN_MAX    64
+
+#define WDSP_ELF_FLAG_EXECUTE    (1 << 0)
+#define WDSP_ELF_FLAG_WRITE      (1 << 1)
+#define WDSP_ELF_FLAG_READ       (1 << 2)
+
+#define WDSP_ELF_FLAG_RE (WDSP_ELF_FLAG_READ | WDSP_ELF_FLAG_EXECUTE)
+
+struct wdsp_img_segment {
+
+	/* Firmware for the slit image */
+	const struct firmware *split_fw;
+
+	/* Name of the split firmware file */
+	char split_fname[WDSP_IMG_NAME_LEN_MAX];
+
+	/* Address where the segment is to be loaded */
+	u32 load_addr;
+
+	/* Buffer to hold the data to be loaded */
+	u8 *data;
+
+	/* Size of the data to be loaded */
+	size_t size;
+
+	/* List node pointing to next segment */
+	struct list_head list;
+};
+
+int wdsp_get_segment_list(struct device *dev, const char *img_fname,
+			  unsigned int segment_type, struct list_head *seg_list,
+			  u32 *entry_point);
+void wdsp_flush_segment_list(struct list_head *seg_list);
+
+#endif /* __WCD_DSP_UTILS_H__ */
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
new file mode 100644
index 0000000..f4c68ff
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -0,0 +1,2603 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/list.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/input.h>
+#include <linux/firmware.h>
+#include <linux/completion.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "wcd-mbhc-v2.h"
+#include "wcdcal-hwdep.h"
+
+#define WCD_MBHC_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+			   SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+			   SND_JACK_MECHANICAL | SND_JACK_MICROPHONE2 | \
+			   SND_JACK_UNSUPPORTED)
+
+#define WCD_MBHC_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+				  SND_JACK_BTN_4 | SND_JACK_BTN_5)
+#define OCP_ATTEMPT 20
+#define HS_DETECT_PLUG_TIME_MS (3 * 1000)
+#define SPECIAL_HS_DETECT_TIME_MS (2 * 1000)
+#define MBHC_BUTTON_PRESS_THRESHOLD_MIN 250
+#define GND_MIC_SWAP_THRESHOLD 4
+#define WCD_FAKE_REMOVAL_MIN_PERIOD_MS 100
+#define HS_VREF_MIN_VAL 1400
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 4000000
+#define FAKE_REM_RETRY_ATTEMPTS 3
+#define MAX_IMPED 60000
+
+#define WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS  50
+#define ANC_DETECT_RETRY_CNT 7
+#define WCD_MBHC_SPL_HS_CNT  1
+
+static int det_extn_cable_en;
+module_param(det_extn_cable_en, int, 0664);
+MODULE_PARM_DESC(det_extn_cable_en, "enable/disable extn cable detect");
+
+enum wcd_mbhc_cs_mb_en_flag {
+	WCD_MBHC_EN_CS = 0,
+	WCD_MBHC_EN_MB,
+	WCD_MBHC_EN_PULLUP,
+	WCD_MBHC_EN_NONE,
+};
+
+static void wcd_mbhc_jack_report(struct wcd_mbhc *mbhc,
+				struct snd_soc_jack *jack, int status, int mask)
+{
+	snd_soc_jack_report(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status,
+				int irq)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	dev_dbg(codec->dev, "%s: clear ocp status %x\n",
+		__func__, jack_status);
+
+	if (mbhc->hph_status & jack_status) {
+		mbhc->hph_status &= ~jack_status;
+		wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+				     mbhc->hph_status, WCD_MBHC_JACK_MASK);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
+		/*
+		 * reset retry counter as PA is turned off signifying
+		 * start of new OCP detection session
+		 */
+		if (mbhc->intr_ids->hph_left_ocp)
+			mbhc->hphlocp_cnt = 0;
+		else
+			mbhc->hphrocp_cnt = 0;
+		mbhc->mbhc_cb->irq_control(codec, irq, true);
+	}
+}
+
+static void hphrocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+			    mbhc->intr_ids->hph_right_ocp);
+}
+
+static void hphlocp_off_report(struct wcd_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+			    mbhc->intr_ids->hph_left_ocp);
+}
+
+static void wcd_program_hs_vref(struct wcd_mbhc *mbhc)
+{
+	struct wcd_mbhc_plug_type_cfg *plug_type_cfg;
+	struct snd_soc_codec *codec = mbhc->codec;
+	u32 reg_val;
+
+	plug_type_cfg = WCD_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	reg_val = ((plug_type_cfg->v_hs_max - HS_VREF_MIN_VAL) / 100);
+
+	dev_dbg(codec->dev, "%s: reg_val  = %x\n", __func__, reg_val);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_VREF, reg_val);
+}
+
+static void wcd_program_btn_threshold(const struct wcd_mbhc *mbhc, bool micbias)
+{
+	struct wcd_mbhc_btn_detect_cfg *btn_det;
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct snd_soc_card *card = codec->component.card;
+	s16 *btn_low, *btn_high;
+
+	if (mbhc->mbhc_cfg->calibration == NULL) {
+		dev_err(card->dev, "%s: calibration data is NULL\n", __func__);
+		return;
+	}
+
+	btn_det = WCD_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	btn_low = btn_det->_v_btn_low;
+	btn_high = ((void *)&btn_det->_v_btn_low) +
+			(sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn);
+
+	mbhc->mbhc_cb->set_btn_thr(codec, btn_low, btn_high, btn_det->num_btn,
+				   micbias);
+}
+
+static void wcd_enable_curr_micbias(const struct wcd_mbhc *mbhc,
+				const enum wcd_mbhc_cs_mb_en_flag cs_mb_en)
+{
+
+	/*
+	 * Some codecs handle micbias/pullup enablement in codec
+	 * drivers itself and micbias is not needed for regular
+	 * plug type detection. So if micbias_control callback function
+	 * is defined, just return.
+	 */
+	if (mbhc->mbhc_cb->mbhc_micbias_control)
+		return;
+
+	pr_debug("%s: enter, cs_mb_en: %d\n", __func__, cs_mb_en);
+
+	switch (cs_mb_en) {
+	case WCD_MBHC_EN_CS:
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+		/* Program Button threshold registers as per CS */
+		wcd_program_btn_threshold(mbhc, false);
+		break;
+	case WCD_MBHC_EN_MB:
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+
+		/* Disable PULL_UP_EN & enable MICBIAS */
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 2);
+		/* Program Button threshold registers as per MICBIAS */
+		wcd_program_btn_threshold(mbhc, true);
+		break;
+	case WCD_MBHC_EN_PULLUP:
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 1);
+		/* Program Button threshold registers as per MICBIAS */
+		wcd_program_btn_threshold(mbhc, true);
+		break;
+	case WCD_MBHC_EN_NONE:
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+		break;
+	default:
+		pr_debug("%s: Invalid parameter", __func__);
+		break;
+	}
+
+	pr_debug("%s: exit\n", __func__);
+}
+
+static const char *wcd_mbhc_get_event_string(int event)
+{
+	switch (event) {
+	case WCD_EVENT_PRE_MICBIAS_2_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_OFF);
+	case WCD_EVENT_POST_MICBIAS_2_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_OFF);
+	case WCD_EVENT_PRE_MICBIAS_2_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_MICBIAS_2_ON);
+	case WCD_EVENT_POST_MICBIAS_2_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_MICBIAS_2_ON);
+	case WCD_EVENT_PRE_HPHL_PA_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_ON);
+	case WCD_EVENT_POST_HPHL_PA_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHL_PA_OFF);
+	case WCD_EVENT_PRE_HPHR_PA_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_ON);
+	case WCD_EVENT_POST_HPHR_PA_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_HPHR_PA_OFF);
+	case WCD_EVENT_PRE_HPHR_PA_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHR_PA_OFF);
+	case WCD_EVENT_PRE_HPHL_PA_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_HPHL_PA_OFF);
+	case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_ON);
+	case WCD_EVENT_PRE_DAPM_MICBIAS_2_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_ON);
+	case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_POST_DAPM_MICBIAS_2_OFF);
+	case WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF);
+	case WCD_EVENT_OCP_OFF:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_OFF);
+	case WCD_EVENT_OCP_ON:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_OCP_ON);
+	case WCD_EVENT_INVALID:
+	default:
+		return WCD_MBHC_STRINGIFY(WCD_EVENT_INVALID);
+	}
+}
+
+static int wcd_event_notify(struct notifier_block *self, unsigned long val,
+			    void *data)
+{
+	struct wcd_mbhc *mbhc = (struct wcd_mbhc *)data;
+	enum wcd_notify_event event = (enum wcd_notify_event)val;
+	struct snd_soc_codec *codec = mbhc->codec;
+	bool micbias2 = false;
+	bool micbias1 = false;
+	u8 fsm_en = 0;
+
+	pr_debug("%s: event %s (%d)\n", __func__,
+		 wcd_mbhc_get_event_string(event), event);
+	if (mbhc->mbhc_cb->micbias_enable_status) {
+		micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+								MIC_BIAS_2);
+		micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+								MIC_BIAS_1);
+	}
+	switch (event) {
+	/* MICBIAS usage change */
+	case WCD_EVENT_POST_DAPM_MICBIAS_2_ON:
+		mbhc->is_hs_recording = true;
+		pr_debug("%s: is_capture: %d\n", __func__,
+			  mbhc->is_hs_recording);
+		break;
+	case WCD_EVENT_POST_MICBIAS_2_ON:
+		if (!mbhc->micbias_enable)
+			goto out_micb_en;
+		if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) {
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_PRECHARGE,
+					true);
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_SET_VAL,
+					true);
+			/*
+			 * Special headset needs MICBIAS as 2.7V so wait for
+			 * 50 msec for the MICBIAS to reach 2.7 volts.
+			 */
+			msleep(50);
+		}
+		if (mbhc->mbhc_cb->set_auto_zeroing)
+			mbhc->mbhc_cb->set_auto_zeroing(codec, true);
+		if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_PRECHARGE,
+					false);
+out_micb_en:
+		/* Disable current source if micbias enabled */
+		if (mbhc->mbhc_cb->mbhc_micbias_control) {
+			WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
+			if (fsm_en)
+				WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL,
+							 0);
+		} else {
+			mbhc->is_hs_recording = true;
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+		}
+		/* configure cap settings properly when micbias is enabled */
+		if (mbhc->mbhc_cb->set_cap_mode)
+			mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true);
+		break;
+	case WCD_EVENT_PRE_MICBIAS_2_OFF:
+		/*
+		 * Before MICBIAS_2 is turned off, if FSM is enabled,
+		 * make sure current source is enabled so as to detect
+		 * button press/release events
+		 */
+		if (mbhc->mbhc_cb->mbhc_micbias_control &&
+		    !mbhc->micbias_enable) {
+			WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, fsm_en);
+			if (fsm_en)
+				WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL,
+							 3);
+		}
+		break;
+	/* MICBIAS usage change */
+	case WCD_EVENT_POST_DAPM_MICBIAS_2_OFF:
+		mbhc->is_hs_recording = false;
+		pr_debug("%s: is_capture: %d\n", __func__,
+			  mbhc->is_hs_recording);
+		break;
+	case WCD_EVENT_POST_MICBIAS_2_OFF:
+		if (!mbhc->mbhc_cb->mbhc_micbias_control)
+			mbhc->is_hs_recording = false;
+		if (mbhc->micbias_enable) {
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+			break;
+		}
+
+		if (mbhc->mbhc_cb->set_auto_zeroing)
+			mbhc->mbhc_cb->set_auto_zeroing(codec, false);
+		if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable)
+			mbhc->mbhc_cb->set_micbias_value(codec);
+		/* Enable PULL UP if PA's are enabled */
+		if ((test_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state)) ||
+				(test_bit(WCD_MBHC_EVENT_PA_HPHR,
+					  &mbhc->event_state)))
+			/* enable pullup and cs, disable mb */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
+		else
+			/* enable current source and disable mb, pullup*/
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+
+		/* configure cap settings properly when micbias is disabled */
+		if (mbhc->mbhc_cb->set_cap_mode)
+			mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
+		break;
+	case WCD_EVENT_PRE_HPHL_PA_OFF:
+		mutex_lock(&mbhc->hphl_pa_lock);
+		break;
+	case WCD_EVENT_POST_HPHL_PA_OFF:
+		clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHL)
+			hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		clear_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+		/* check if micbias is enabled */
+		if (micbias2)
+			/* Disable cs, pullup & enable micbias */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+		else
+			/* Disable micbias, pullup & enable cs */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+		mutex_unlock(&mbhc->hphl_pa_lock);
+		break;
+	case WCD_EVENT_PRE_HPHR_PA_OFF:
+		mutex_lock(&mbhc->hphr_pa_lock);
+		break;
+	case WCD_EVENT_POST_HPHR_PA_OFF:
+		clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHR)
+			hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+		clear_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+		/* check if micbias is enabled */
+		if (micbias2)
+			/* Disable cs, pullup & enable micbias */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+		else
+			/* Disable micbias, pullup & enable cs */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+		mutex_unlock(&mbhc->hphr_pa_lock);
+		break;
+	case WCD_EVENT_PRE_HPHL_PA_ON:
+		set_bit(WCD_MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+		/* check if micbias is enabled */
+		if (micbias2)
+			/* Disable cs, pullup & enable micbias */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+		else
+			/* Disable micbias, enable pullup & cs */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
+		break;
+	case WCD_EVENT_PRE_HPHR_PA_ON:
+		set_bit(WCD_MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+		/* check if micbias is enabled */
+		if (micbias2)
+			/* Disable cs, pullup & enable micbias */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+		else
+			/* Disable micbias, enable pullup & cs */
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_PULLUP);
+		break;
+	case WCD_EVENT_OCP_OFF:
+		mbhc->mbhc_cb->irq_control(mbhc->codec,
+					   mbhc->intr_ids->hph_left_ocp,
+					   false);
+		break;
+	case WCD_EVENT_OCP_ON:
+		mbhc->mbhc_cb->irq_control(mbhc->codec,
+					   mbhc->intr_ids->hph_left_ocp,
+					   true);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int wcd_cancel_btn_work(struct wcd_mbhc *mbhc)
+{
+	int r;
+
+	r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+	/*
+	 * if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+	 * we have to unlock from here instead btn_work
+	 */
+	if (r)
+		mbhc->mbhc_cb->lock_sleep(mbhc, false);
+	return r;
+}
+
+static bool wcd_swch_level_remove(struct wcd_mbhc *mbhc)
+{
+	u16 result2 = 0;
+
+	WCD_MBHC_REG_READ(WCD_MBHC_SWCH_LEVEL_REMOVE, result2);
+	return (result2) ? true : false;
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd_schedule_hs_detect_plug(struct wcd_mbhc *mbhc,
+					    struct work_struct *work)
+{
+	pr_debug("%s: scheduling correct_swch_plug\n", __func__);
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+	mbhc->hs_detect_work_stop = false;
+	mbhc->mbhc_cb->lock_sleep(mbhc, true);
+	schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd_cancel_hs_detect_plug(struct wcd_mbhc *mbhc,
+					 struct work_struct *work)
+{
+	pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+	mbhc->hs_detect_work_stop = true;
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	if (cancel_work_sync(work)) {
+		pr_debug("%s: correct_plug_swch is canceled\n",
+			 __func__);
+		mbhc->mbhc_cb->lock_sleep(mbhc, false);
+	}
+	WCD_MBHC_RSC_LOCK(mbhc);
+}
+
+static void wcd_mbhc_clr_and_turnon_hph_padac(struct wcd_mbhc *mbhc)
+{
+	bool pa_turned_on = false;
+	u8 wg_time = 0;
+
+	WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time);
+	wg_time += 1;
+
+	mutex_lock(&mbhc->hphr_pa_lock);
+	if (test_and_clear_bit(WCD_MBHC_HPHR_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_PA_EN, 1);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 1);
+		pa_turned_on = true;
+	}
+	mutex_unlock(&mbhc->hphr_pa_lock);
+	mutex_lock(&mbhc->hphl_pa_lock);
+	if (test_and_clear_bit(WCD_MBHC_HPHL_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PA_EN, 1);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 1);
+		pa_turned_on = true;
+	}
+	mutex_unlock(&mbhc->hphl_pa_lock);
+
+	if (pa_turned_on) {
+		pr_debug("%s: PA was turned on by MBHC and not by DAPM\n",
+			 __func__);
+		usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+	}
+}
+
+static bool wcd_mbhc_is_hph_pa_on(struct wcd_mbhc *mbhc)
+{
+	bool hph_pa_on = false;
+
+	WCD_MBHC_REG_READ(WCD_MBHC_HPH_PA_EN, hph_pa_on);
+
+	return (hph_pa_on) ? true : false;
+}
+
+static void wcd_mbhc_set_and_turnoff_hph_padac(struct wcd_mbhc *mbhc)
+{
+	u8 wg_time = 0;
+
+	WCD_MBHC_REG_READ(WCD_MBHC_HPH_CNP_WG_TIME, wg_time);
+	wg_time += 1;
+
+	/* If headphone PA is on, check if userspace receives
+	 * removal event to sync-up PA's state
+	 */
+	if (wcd_mbhc_is_hph_pa_on(mbhc)) {
+		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+		set_bit(WCD_MBHC_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		set_bit(WCD_MBHC_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_OCP_DET_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHR_OCP_DET_EN, 0);
+	} else {
+		pr_debug("%s PA is off\n", __func__);
+	}
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPH_PA_EN, 0);
+	usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+}
+
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+			uint32_t *zr)
+{
+	*zl = mbhc->zl;
+	*zr = mbhc->zr;
+
+	if (*zl && *zr)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+static void wcd_mbhc_hs_elec_irq(struct wcd_mbhc *mbhc, int irq_type,
+				 bool enable)
+{
+	int irq;
+
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+	if (irq_type == WCD_MBHC_ELEC_HS_INS)
+		irq = mbhc->intr_ids->mbhc_hs_ins_intr;
+	else if (irq_type == WCD_MBHC_ELEC_HS_REM)
+		irq = mbhc->intr_ids->mbhc_hs_rem_intr;
+	else {
+		pr_debug("%s: irq_type: %d, enable: %d\n",
+			__func__, irq_type, enable);
+		return;
+	}
+
+	pr_debug("%s: irq: %d, enable: %d, intr_status:%lu\n",
+		 __func__, irq, enable, mbhc->intr_status);
+	if ((test_bit(irq_type, &mbhc->intr_status)) != enable) {
+		mbhc->mbhc_cb->irq_control(mbhc->codec, irq, enable);
+		if (enable)
+			set_bit(irq_type, &mbhc->intr_status);
+		else
+			clear_bit(irq_type, &mbhc->intr_status);
+	}
+}
+
+static void wcd_mbhc_report_plug(struct wcd_mbhc *mbhc, int insertion,
+				enum snd_jack_types jack_type)
+{
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+	pr_debug("%s: enter insertion %d hph_status %x\n",
+		 __func__, insertion, mbhc->hph_status);
+	if (!insertion) {
+		/* Report removal */
+		mbhc->hph_status &= ~jack_type;
+		/*
+		 * cancel possibly scheduled btn work and
+		 * report release if we reported button press
+		 */
+		if (wcd_cancel_btn_work(mbhc)) {
+			pr_debug("%s: button press is canceled\n", __func__);
+		} else if (mbhc->buttons_pressed) {
+			pr_debug("%s: release of button press%d\n",
+				 __func__, jack_type);
+			wcd_mbhc_jack_report(mbhc, &mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+			mbhc->buttons_pressed &=
+				~WCD_MBHC_JACK_BUTTON_MASK;
+		}
+
+		if (mbhc->micbias_enable) {
+			if (mbhc->mbhc_cb->mbhc_micbias_control)
+				mbhc->mbhc_cb->mbhc_micbias_control(
+						mbhc->codec, MIC_BIAS_2,
+						MICB_DISABLE);
+			if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+				mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+						mbhc->codec,
+						MIC_BIAS_2, false);
+			if (mbhc->mbhc_cb->set_micbias_value) {
+				mbhc->mbhc_cb->set_micbias_value(mbhc->codec);
+				WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MICB_CTRL, 0);
+			}
+			mbhc->micbias_enable = false;
+		}
+
+		mbhc->hph_type = WCD_MBHC_HPH_NONE;
+		mbhc->zl = mbhc->zr = 0;
+		pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+				mbhc->hph_status, WCD_MBHC_JACK_MASK);
+		wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
+		hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+		hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+	} else {
+		/*
+		 * Report removal of current jack type.
+		 * Headphone to headset shouldn't report headphone
+		 * removal.
+		 */
+		if (mbhc->mbhc_cfg->detect_extn_cable &&
+		    (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH ||
+		    jack_type == SND_JACK_LINEOUT) &&
+		    (mbhc->hph_status && mbhc->hph_status != jack_type)) {
+
+			if (mbhc->micbias_enable) {
+				if (mbhc->mbhc_cb->mbhc_micbias_control)
+					mbhc->mbhc_cb->mbhc_micbias_control(
+						mbhc->codec, MIC_BIAS_2,
+						MICB_DISABLE);
+				if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+					mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+						mbhc->codec,
+						MIC_BIAS_2, false);
+				if (mbhc->mbhc_cb->set_micbias_value) {
+					mbhc->mbhc_cb->set_micbias_value(
+							mbhc->codec);
+					WCD_MBHC_REG_UPDATE_BITS(
+							WCD_MBHC_MICB_CTRL, 0);
+				}
+				mbhc->micbias_enable = false;
+			}
+			mbhc->hph_type = WCD_MBHC_HPH_NONE;
+			mbhc->zl = mbhc->zr = 0;
+			pr_debug("%s: Reporting removal (%x)\n",
+				 __func__, mbhc->hph_status);
+			wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+					    0, WCD_MBHC_JACK_MASK);
+
+			if (mbhc->hph_status == SND_JACK_LINEOUT) {
+
+				pr_debug("%s: Enable micbias\n", __func__);
+				/* Disable current source and enable micbias */
+				wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+				pr_debug("%s: set up elec removal detection\n",
+					  __func__);
+				WCD_MBHC_REG_UPDATE_BITS(
+						WCD_MBHC_ELECT_DETECTION_TYPE,
+						0);
+				usleep_range(200, 210);
+				wcd_mbhc_hs_elec_irq(mbhc,
+						     WCD_MBHC_ELEC_HS_REM,
+						     true);
+			}
+			mbhc->hph_status &= ~(SND_JACK_HEADSET |
+						SND_JACK_LINEOUT |
+						SND_JACK_ANC_HEADPHONE |
+						SND_JACK_UNSUPPORTED);
+		}
+
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET &&
+			jack_type == SND_JACK_HEADPHONE)
+			mbhc->hph_status &= ~SND_JACK_HEADSET;
+
+		/* Report insertion */
+		if (jack_type == SND_JACK_HEADPHONE)
+			mbhc->current_plug = MBHC_PLUG_TYPE_HEADPHONE;
+		else if (jack_type == SND_JACK_UNSUPPORTED)
+			mbhc->current_plug = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+		else if (jack_type == SND_JACK_HEADSET) {
+			mbhc->current_plug = MBHC_PLUG_TYPE_HEADSET;
+			mbhc->jiffies_atreport = jiffies;
+		} else if (jack_type == SND_JACK_LINEOUT) {
+			mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+		} else if (jack_type == SND_JACK_ANC_HEADPHONE)
+			mbhc->current_plug = MBHC_PLUG_TYPE_ANC_HEADPHONE;
+
+		if (mbhc->impedance_detect &&
+			mbhc->mbhc_cb->compute_impedance &&
+			(mbhc->mbhc_cfg->linein_th != 0)) {
+			mbhc->mbhc_cb->compute_impedance(mbhc,
+					&mbhc->zl, &mbhc->zr);
+			if ((mbhc->zl > mbhc->mbhc_cfg->linein_th &&
+				mbhc->zl < MAX_IMPED) &&
+				(mbhc->zr > mbhc->mbhc_cfg->linein_th &&
+				 mbhc->zr < MAX_IMPED) &&
+				(jack_type == SND_JACK_HEADPHONE)) {
+				jack_type = SND_JACK_LINEOUT;
+				mbhc->current_plug = MBHC_PLUG_TYPE_HIGH_HPH;
+				if (mbhc->hph_status) {
+					mbhc->hph_status &= ~(SND_JACK_HEADSET |
+							SND_JACK_LINEOUT |
+							SND_JACK_UNSUPPORTED);
+					wcd_mbhc_jack_report(mbhc,
+							&mbhc->headset_jack,
+							mbhc->hph_status,
+							WCD_MBHC_JACK_MASK);
+				}
+				pr_debug("%s: Marking jack type as SND_JACK_LINEOUT\n",
+				__func__);
+			}
+		}
+
+		mbhc->hph_status |= jack_type;
+
+		pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+				    (mbhc->hph_status | SND_JACK_MECHANICAL),
+				    WCD_MBHC_JACK_MASK);
+		wcd_mbhc_clr_and_turnon_hph_padac(mbhc);
+	}
+	pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+static bool wcd_mbhc_detect_anc_plug_type(struct wcd_mbhc *mbhc)
+{
+	bool anc_mic_found = false;
+	u16 val, hs_comp_res, btn_status = 0;
+	unsigned long retry = 0;
+	int valid_plug_cnt = 0, invalid_plug_cnt = 0;
+	int btn_status_cnt = 0;
+	bool is_check_btn_press = false;
+
+
+	if (mbhc->mbhc_cfg->anc_micbias < MIC_BIAS_1 ||
+	    mbhc->mbhc_cfg->anc_micbias > MIC_BIAS_4)
+		return false;
+
+	if (!mbhc->mbhc_cb->mbhc_micbias_control)
+		return false;
+
+	WCD_MBHC_REG_READ(WCD_MBHC_FSM_EN, val);
+
+	if (val)
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+
+	mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
+					    mbhc->mbhc_cfg->anc_micbias,
+					    MICB_ENABLE);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x2);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 1);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+	/*
+	 * wait for button debounce time 20ms. If 4-pole plug is inserted
+	 * into 5-pole jack, then there will be a button press interrupt
+	 * during anc plug detection. In that case though Hs_comp_res is 0,
+	 * it should not be declared as ANC plug type
+	 */
+	usleep_range(20000, 20100);
+
+	/*
+	 * After enabling FSM, to handle slow insertion scenarios,
+	 * check hs_comp_result for few times to see if the IN3 voltage
+	 * is below the Vref
+	 */
+	do {
+		if (wcd_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch level is low\n", __func__);
+			goto exit;
+		}
+		pr_debug("%s: Retry attempt %lu\n", __func__, retry + 1);
+		WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+		if (!hs_comp_res) {
+			valid_plug_cnt++;
+			is_check_btn_press = true;
+		} else
+			invalid_plug_cnt++;
+		/* Wait 1ms before taking another reading */
+		usleep_range(1000, 1100);
+
+		WCD_MBHC_REG_READ(WCD_MBHC_FSM_STATUS, btn_status);
+		if (btn_status)
+			btn_status_cnt++;
+
+		retry++;
+	} while (retry < ANC_DETECT_RETRY_CNT);
+
+	pr_debug("%s: valid: %d, invalid: %d, btn_status_cnt: %d\n",
+		 __func__, valid_plug_cnt, invalid_plug_cnt, btn_status_cnt);
+
+	/* decision logic */
+	if ((valid_plug_cnt > invalid_plug_cnt) && is_check_btn_press &&
+	    (btn_status_cnt == 0))
+		anc_mic_found = true;
+exit:
+	if (!val)
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ANC_DET_EN, 0);
+
+	mbhc->mbhc_cb->mbhc_micbias_control(mbhc->codec,
+					    mbhc->mbhc_cfg->anc_micbias,
+					    MICB_DISABLE);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MUX_CTL, 0x0);
+	pr_debug("%s: anc mic %sfound\n", __func__,
+		 anc_mic_found ? "" : "not ");
+	return anc_mic_found;
+}
+
+static void wcd_mbhc_find_plug_and_report(struct wcd_mbhc *mbhc,
+					 enum wcd_mbhc_plug_type plug_type)
+{
+	bool anc_mic_found = false;
+	enum snd_jack_types jack_type;
+
+	pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+		 __func__, mbhc->current_plug, plug_type);
+
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+	if (mbhc->current_plug == plug_type) {
+		pr_debug("%s: cable already reported, exit\n", __func__);
+		goto exit;
+	}
+
+	if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
+		/*
+		 * Nothing was reported previously
+		 * report a headphone or unsupported
+		 */
+		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+	} else if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE)
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET)
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+		wcd_mbhc_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+	} else if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
+		if (mbhc->mbhc_cfg->enable_anc_mic_detect)
+			anc_mic_found = wcd_mbhc_detect_anc_plug_type(mbhc);
+
+		jack_type = SND_JACK_HEADSET;
+		if (anc_mic_found)
+			jack_type = SND_JACK_ANC_HEADPHONE;
+
+		/*
+		 * If Headphone was reported previously, this will
+		 * only report the mic line
+		 */
+		wcd_mbhc_report_plug(mbhc, 1, jack_type);
+	} else if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH) {
+		if (mbhc->mbhc_cfg->detect_extn_cable) {
+			/* High impedance device found. Report as LINEOUT */
+			wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+			pr_debug("%s: setup mic trigger for further detection\n",
+				 __func__);
+
+			/* Disable HW FSM and current source */
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+			/* Setup for insertion detection */
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+						 1);
+			/*
+			 * Enable HPHL trigger and MIC Schmitt triggers
+			 * and request for elec insertion interrupts
+			 */
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC,
+						 3);
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+					     true);
+		} else {
+			wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+		}
+	} else {
+		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+		     mbhc->current_plug, plug_type);
+	}
+exit:
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* To determine if cross connection occurred */
+static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
+{
+	u16 swap_res = 0;
+	enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_NONE;
+	s16 reg1 = 0;
+	bool hphl_sch_res = 0, hphr_sch_res = 0;
+
+	if (wcd_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low\n", __func__);
+		return -EINVAL;
+	}
+
+	/* If PA is enabled, dont check for cross-connection */
+	if (mbhc->mbhc_cb->hph_pa_on_status)
+		if (mbhc->mbhc_cb->hph_pa_on_status(mbhc->codec))
+			return false;
+
+	WCD_MBHC_REG_READ(WCD_MBHC_ELECT_SCHMT_ISRC, reg1);
+	/*
+	 * Check if there is any cross connection,
+	 * Micbias and schmitt trigger (HPHL-HPHR)
+	 * needs to be enabled. For some codecs like wcd9335,
+	 * pull-up will already be enabled when this function
+	 * is called for cross-connection identification. No
+	 * need to enable micbias in that case.
+	 */
+	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 2);
+
+	WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, swap_res);
+	pr_debug("%s: swap_res%x\n", __func__, swap_res);
+
+	/*
+	 * Read reg hphl and hphr schmitt result with cross connection
+	 * bit. These bits will both be "0" in case of cross connection
+	 * otherwise, they stay at 1
+	 */
+	WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch_res);
+	WCD_MBHC_REG_READ(WCD_MBHC_HPHR_SCHMT_RESULT, hphr_sch_res);
+	if (!(hphl_sch_res || hphr_sch_res)) {
+		plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+		pr_debug("%s: Cross connection identified\n", __func__);
+	} else {
+		pr_debug("%s: No Cross connection found\n", __func__);
+	}
+
+	/* Disable schmitt trigger and restore micbias */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, reg1);
+	pr_debug("%s: leave, plug type: %d\n", __func__,  plug_type);
+
+	return (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP) ? true : false;
+}
+
+static bool wcd_is_special_headset(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int delay = 0, rc;
+	bool ret = false;
+	u16 hs_comp_res;
+	bool is_spl_hs = false;
+
+	/*
+	 * Increase micbias to 2.7V to detect headsets with
+	 * threshold on microphone
+	 */
+	if (mbhc->mbhc_cb->mbhc_micbias_control &&
+	    !mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
+		pr_debug("%s: callback fn micb_ctrl_thr_mic not defined\n",
+			 __func__);
+		return false;
+	} else if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic) {
+		rc = mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec,
+							MIC_BIAS_2, true);
+		if (rc) {
+			pr_err("%s: Micbias control for thr mic failed, rc: %d\n",
+				__func__, rc);
+			return false;
+		}
+	}
+
+	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+
+	pr_debug("%s: special headset, start register writes\n", __func__);
+
+	WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+	while (!is_spl_hs)  {
+		if (mbhc->hs_detect_work_stop) {
+			pr_debug("%s: stop requested: %d\n", __func__,
+					mbhc->hs_detect_work_stop);
+			break;
+		}
+		delay = delay + 50;
+		if (mbhc->mbhc_cb->mbhc_common_micb_ctrl) {
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_PRECHARGE,
+					true);
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_SET_VAL,
+					true);
+		}
+		/* Wait for 50msec for MICBIAS to settle down */
+		msleep(50);
+		if (mbhc->mbhc_cb->set_auto_zeroing)
+			mbhc->mbhc_cb->set_auto_zeroing(codec, true);
+		/* Wait for 50msec for FSM to update result values */
+		msleep(50);
+		WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+		if (!(hs_comp_res)) {
+			pr_debug("%s: Special headset detected in %d msecs\n",
+					__func__, (delay * 2));
+			is_spl_hs = true;
+		}
+		if (delay == SPECIAL_HS_DETECT_TIME_MS) {
+			pr_debug("%s: Spl headset did not get detect in 4 sec\n",
+					__func__);
+			break;
+		}
+	}
+	if (is_spl_hs) {
+		pr_debug("%s: Headset with threshold found\n",  __func__);
+		mbhc->micbias_enable = true;
+		ret = true;
+	}
+	if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+		mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+				MBHC_COMMON_MICB_PRECHARGE,
+				false);
+	if (mbhc->mbhc_cb->set_micbias_value && !mbhc->micbias_enable)
+		mbhc->mbhc_cb->set_micbias_value(codec);
+	if (mbhc->mbhc_cb->set_auto_zeroing)
+		mbhc->mbhc_cb->set_auto_zeroing(codec, false);
+
+	if (mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic &&
+	    !mbhc->micbias_enable)
+		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(codec, MIC_BIAS_2,
+						      false);
+
+	pr_debug("%s: leave, micb_enable: %d\n", __func__,
+		  mbhc->micbias_enable);
+	return ret;
+}
+
+static void wcd_mbhc_update_fsm_source(struct wcd_mbhc *mbhc,
+				       enum wcd_mbhc_plug_type plug_type)
+{
+	bool micbias2;
+
+	micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+							MIC_BIAS_2);
+	switch (plug_type) {
+	case MBHC_PLUG_TYPE_HEADPHONE:
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+		break;
+	case MBHC_PLUG_TYPE_HEADSET:
+	case MBHC_PLUG_TYPE_ANC_HEADPHONE:
+		if (!mbhc->is_hs_recording && !micbias2)
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 3);
+		break;
+	default:
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+		break;
+
+	};
+}
+
+static void wcd_enable_mbhc_supply(struct wcd_mbhc *mbhc,
+			enum wcd_mbhc_plug_type plug_type)
+{
+
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	/*
+	 * Do not disable micbias if recording is going on or
+	 * headset is inserted on the other side of the extn
+	 * cable. If headset has been detected current source
+	 * needs to be kept enabled for button detection to work.
+	 * If the accessory type is invalid or unsupported, we
+	 * dont need to enable either of them.
+	 */
+	if (det_extn_cable_en && mbhc->is_extn_cable &&
+		mbhc->mbhc_cb && mbhc->mbhc_cb->extn_use_mb &&
+		mbhc->mbhc_cb->extn_use_mb(codec)) {
+		if (plug_type == MBHC_PLUG_TYPE_HEADPHONE ||
+		    plug_type == MBHC_PLUG_TYPE_HEADSET)
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+	} else {
+		if (plug_type == MBHC_PLUG_TYPE_HEADSET) {
+			if (mbhc->is_hs_recording || mbhc->micbias_enable)
+				wcd_enable_curr_micbias(mbhc,
+							WCD_MBHC_EN_MB);
+			else if ((test_bit(WCD_MBHC_EVENT_PA_HPHL,
+						&mbhc->event_state)) ||
+				 (test_bit(WCD_MBHC_EVENT_PA_HPHR,
+						&mbhc->event_state)))
+				wcd_enable_curr_micbias(mbhc,
+							WCD_MBHC_EN_PULLUP);
+			else
+				wcd_enable_curr_micbias(mbhc,
+							WCD_MBHC_EN_CS);
+		} else if (plug_type == MBHC_PLUG_TYPE_HEADPHONE) {
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_CS);
+		} else {
+			wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
+		}
+	}
+}
+
+static bool wcd_mbhc_check_for_spl_headset(struct wcd_mbhc *mbhc,
+					   int *spl_hs_cnt)
+{
+	u16 hs_comp_res_1_8v = 0, hs_comp_res_2_7v = 0;
+	bool spl_hs = false;
+
+	if (!mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic)
+		goto exit;
+
+	/* Read back hs_comp_res @ 1.8v Micbias */
+	WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_1_8v);
+	if (!hs_comp_res_1_8v) {
+		spl_hs = false;
+		goto exit;
+	}
+
+	/* Bump up MB2 to 2.7v */
+	mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
+				mbhc->mbhc_cfg->mbhc_micbias, true);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+	usleep_range(10000, 10100);
+
+	/* Read back HS_COMP_RESULT */
+	WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res_2_7v);
+	if (!hs_comp_res_2_7v && hs_comp_res_1_8v)
+		spl_hs = true;
+
+	if (spl_hs && spl_hs_cnt)
+		*spl_hs_cnt += 1;
+
+	/* MB2 back to 1.8v */
+	if (*spl_hs_cnt != WCD_MBHC_SPL_HS_CNT) {
+		mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(mbhc->codec,
+				mbhc->mbhc_cfg->mbhc_micbias, false);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+		usleep_range(10000, 10100);
+	}
+
+	if (spl_hs)
+		pr_debug("%s: Detected special HS (%d)\n", __func__, spl_hs);
+
+exit:
+	return spl_hs;
+}
+
+static void wcd_correct_swch_plug(struct work_struct *work)
+{
+	struct wcd_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	enum wcd_mbhc_plug_type plug_type = MBHC_PLUG_TYPE_INVALID;
+	unsigned long timeout;
+	u16 hs_comp_res = 0, hphl_sch = 0, mic_sch = 0, btn_result = 0;
+	bool wrk_complete = false;
+	int pt_gnd_mic_swap_cnt = 0;
+	int no_gnd_mic_swap_cnt = 0;
+	bool is_pa_on = false, spl_hs = false;
+	bool micbias2 = false;
+	bool micbias1 = false;
+	int ret = 0;
+	int rc, spl_hs_count = 0;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc = container_of(work, struct wcd_mbhc, correct_plug_swch);
+	codec = mbhc->codec;
+
+	/*
+	 * Enable micbias/pullup for detection in correct work.
+	 * This work will get scheduled from detect_plug_type which
+	 * will already request for pullup/micbias. If the pullup/micbias
+	 * is handled with ref-counts by individual codec drivers, there is
+	 * no need to enabale micbias/pullup here
+	 */
+
+	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+
+
+	if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
+		mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+		goto correct_plug_type;
+	}
+
+	/* Enable HW FSM */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+	/*
+	 * Check for any button press interrupts before starting 3-sec
+	 * loop.
+	 */
+	rc = wait_for_completion_timeout(&mbhc->btn_press_compl,
+			msecs_to_jiffies(WCD_MBHC_BTN_PRESS_COMPL_TIMEOUT_MS));
+
+	WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
+	WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+	if (!rc) {
+		pr_debug("%s No btn press interrupt\n", __func__);
+		if (!btn_result && !hs_comp_res)
+			plug_type = MBHC_PLUG_TYPE_HEADSET;
+		else if (!btn_result && hs_comp_res)
+			plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+		else
+			plug_type = MBHC_PLUG_TYPE_INVALID;
+	} else {
+		if (!btn_result && !hs_comp_res)
+			plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+		else
+			plug_type = MBHC_PLUG_TYPE_INVALID;
+	}
+
+	pr_debug("%s: Valid plug found, plug type is %d\n",
+			 __func__, plug_type);
+	if ((plug_type == MBHC_PLUG_TYPE_HEADSET ||
+	     plug_type == MBHC_PLUG_TYPE_HEADPHONE) &&
+	    (!wcd_swch_level_remove(mbhc))) {
+		WCD_MBHC_RSC_LOCK(mbhc);
+		wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+		WCD_MBHC_RSC_UNLOCK(mbhc);
+	}
+
+correct_plug_type:
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!time_after(jiffies, timeout)) {
+		if (mbhc->hs_detect_work_stop) {
+			pr_debug("%s: stop requested: %d\n", __func__,
+					mbhc->hs_detect_work_stop);
+			wcd_enable_curr_micbias(mbhc,
+						WCD_MBHC_EN_NONE);
+			if (mbhc->micbias_enable) {
+				mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+					mbhc->codec, MIC_BIAS_2, false);
+				if (mbhc->mbhc_cb->set_micbias_value)
+					mbhc->mbhc_cb->set_micbias_value(
+							mbhc->codec);
+				mbhc->micbias_enable = false;
+			}
+			goto exit;
+		}
+		if (mbhc->btn_press_intr) {
+			wcd_cancel_btn_work(mbhc);
+			mbhc->btn_press_intr = false;
+		}
+		/* Toggle FSM */
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 1);
+
+		/* allow sometime and re-check stop requested again */
+		msleep(20);
+		if (mbhc->hs_detect_work_stop) {
+			pr_debug("%s: stop requested: %d\n", __func__,
+					mbhc->hs_detect_work_stop);
+			wcd_enable_curr_micbias(mbhc,
+						WCD_MBHC_EN_NONE);
+			if (mbhc->micbias_enable) {
+				mbhc->mbhc_cb->mbhc_micb_ctrl_thr_mic(
+					mbhc->codec, MIC_BIAS_2, false);
+				if (mbhc->mbhc_cb->set_micbias_value)
+					mbhc->mbhc_cb->set_micbias_value(
+							mbhc->codec);
+				mbhc->micbias_enable = false;
+			}
+			goto exit;
+		}
+		WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_res);
+
+		pr_debug("%s: hs_comp_res: %x\n", __func__, hs_comp_res);
+		if (mbhc->mbhc_cb->hph_pa_on_status)
+			is_pa_on = mbhc->mbhc_cb->hph_pa_on_status(codec);
+
+		/*
+		 * instead of hogging system by contineous polling, wait for
+		 * sometime and re-check stop request again.
+		 */
+		msleep(180);
+		if (hs_comp_res && (spl_hs_count < WCD_MBHC_SPL_HS_CNT)) {
+			spl_hs = wcd_mbhc_check_for_spl_headset(mbhc,
+								&spl_hs_count);
+
+			if (spl_hs_count == WCD_MBHC_SPL_HS_CNT) {
+				hs_comp_res = 0;
+				spl_hs = true;
+				mbhc->micbias_enable = true;
+			}
+		}
+
+		if ((!hs_comp_res) && (!is_pa_on)) {
+			/* Check for cross connection*/
+			ret = wcd_check_cross_conn(mbhc);
+			if (ret < 0) {
+				continue;
+			} else if (ret > 0) {
+				pt_gnd_mic_swap_cnt++;
+				no_gnd_mic_swap_cnt = 0;
+				if (pt_gnd_mic_swap_cnt <
+						GND_MIC_SWAP_THRESHOLD) {
+					continue;
+				} else if (pt_gnd_mic_swap_cnt >
+						GND_MIC_SWAP_THRESHOLD) {
+					/*
+					 * This is due to GND/MIC switch didn't
+					 * work,  Report unsupported plug.
+					 */
+					pr_debug("%s: switch did not work\n",
+						  __func__);
+					plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+					goto report;
+				} else {
+					plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+				}
+			} else {
+				no_gnd_mic_swap_cnt++;
+				pt_gnd_mic_swap_cnt = 0;
+				plug_type = MBHC_PLUG_TYPE_HEADSET;
+				if ((no_gnd_mic_swap_cnt <
+				    GND_MIC_SWAP_THRESHOLD) &&
+				    (spl_hs_count != WCD_MBHC_SPL_HS_CNT)) {
+					continue;
+				} else {
+					no_gnd_mic_swap_cnt = 0;
+				}
+			}
+			if ((pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD) &&
+				(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
+				/*
+				 * if switch is toggled, check again,
+				 * otherwise report unsupported plug
+				 */
+				if (mbhc->mbhc_cfg->swap_gnd_mic &&
+					mbhc->mbhc_cfg->swap_gnd_mic(codec)) {
+					pr_debug("%s: US_EU gpio present,flip switch\n"
+						, __func__);
+					continue;
+				}
+			}
+		}
+
+		WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
+		WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
+		if (hs_comp_res && !(hphl_sch || mic_sch)) {
+			pr_debug("%s: cable is extension cable\n", __func__);
+			plug_type = MBHC_PLUG_TYPE_HIGH_HPH;
+			wrk_complete = true;
+		} else {
+			pr_debug("%s: cable might be headset: %d\n", __func__,
+					plug_type);
+			if (!(plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP)) {
+				plug_type = MBHC_PLUG_TYPE_HEADSET;
+				/*
+				 * Report headset only if not already reported
+				 * and if there is not button press without
+				 * release
+				 */
+				if (((mbhc->current_plug !=
+				      MBHC_PLUG_TYPE_HEADSET) &&
+				     (mbhc->current_plug !=
+				      MBHC_PLUG_TYPE_ANC_HEADPHONE)) &&
+				    !wcd_swch_level_remove(mbhc) &&
+				    !mbhc->btn_press_intr) {
+					pr_debug("%s: cable is %sheadset\n",
+						__func__,
+						((spl_hs_count ==
+							WCD_MBHC_SPL_HS_CNT) ?
+							"special ":""));
+					goto report;
+				}
+			}
+			wrk_complete = false;
+		}
+	}
+	if (!wrk_complete && mbhc->btn_press_intr) {
+		pr_debug("%s: Can be slow insertion of headphone\n", __func__);
+		wcd_cancel_btn_work(mbhc);
+		plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+	}
+	/*
+	 * If plug_tye is headset, we might have already reported either in
+	 * detect_plug-type or in above while loop, no need to report again
+	 */
+	if (!wrk_complete && ((plug_type == MBHC_PLUG_TYPE_HEADSET) ||
+	    (plug_type == MBHC_PLUG_TYPE_ANC_HEADPHONE))) {
+		pr_debug("%s: plug_type:0x%x already reported\n",
+			 __func__, mbhc->current_plug);
+		goto enable_supply;
+	}
+
+	if (plug_type == MBHC_PLUG_TYPE_HIGH_HPH &&
+		(!det_extn_cable_en)) {
+		if (wcd_is_special_headset(mbhc)) {
+			pr_debug("%s: Special headset found %d\n",
+					__func__, plug_type);
+			plug_type = MBHC_PLUG_TYPE_HEADSET;
+			goto report;
+		}
+	}
+
+report:
+	if (wcd_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low\n", __func__);
+		goto exit;
+	}
+	if (plug_type == MBHC_PLUG_TYPE_GND_MIC_SWAP && mbhc->btn_press_intr) {
+		pr_debug("%s: insertion of headphone with swap\n", __func__);
+		wcd_cancel_btn_work(mbhc);
+		plug_type = MBHC_PLUG_TYPE_HEADPHONE;
+	}
+	pr_debug("%s: Valid plug found, plug type %d wrk_cmpt %d btn_intr %d\n",
+			__func__, plug_type, wrk_complete,
+			mbhc->btn_press_intr);
+	WCD_MBHC_RSC_LOCK(mbhc);
+	wcd_mbhc_find_plug_and_report(mbhc, plug_type);
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+enable_supply:
+	if (mbhc->mbhc_cb->mbhc_micbias_control)
+		wcd_mbhc_update_fsm_source(mbhc, plug_type);
+	else
+		wcd_enable_mbhc_supply(mbhc, plug_type);
+exit:
+	if (mbhc->mbhc_cb->mbhc_micbias_control &&
+	    !mbhc->micbias_enable)
+		mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
+						    MICB_DISABLE);
+	if (mbhc->mbhc_cb->micbias_enable_status) {
+		micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+								MIC_BIAS_1);
+		micbias2 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+								MIC_BIAS_2);
+	}
+
+	if (mbhc->mbhc_cfg->detect_extn_cable &&
+	    ((plug_type == MBHC_PLUG_TYPE_HEADPHONE) ||
+	     (plug_type == MBHC_PLUG_TYPE_HEADSET)) &&
+	    !mbhc->hs_detect_work_stop) {
+		WCD_MBHC_RSC_LOCK(mbhc);
+		wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM, true);
+		WCD_MBHC_RSC_UNLOCK(mbhc);
+	}
+	if (mbhc->mbhc_cb->set_cap_mode)
+		mbhc->mbhc_cb->set_cap_mode(codec, micbias1, micbias2);
+
+	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+		mbhc->mbhc_cb->hph_pull_down_ctrl(codec, true);
+
+	mbhc->mbhc_cb->lock_sleep(mbhc, false);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd_mbhc_detect_plug_type(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	enum wcd_mbhc_plug_type plug_type;
+	bool micbias1 = false;
+	int cross_conn;
+	int try = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+	if (mbhc->mbhc_cb->hph_pull_down_ctrl)
+		mbhc->mbhc_cb->hph_pull_down_ctrl(codec, false);
+
+	if (mbhc->mbhc_cb->micbias_enable_status)
+		micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+								MIC_BIAS_1);
+
+	if (mbhc->mbhc_cb->set_cap_mode)
+		mbhc->mbhc_cb->set_cap_mode(codec, micbias1, true);
+
+	if (mbhc->mbhc_cb->mbhc_micbias_control)
+		mbhc->mbhc_cb->mbhc_micbias_control(codec, MIC_BIAS_2,
+						    MICB_ENABLE);
+	else
+		wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_MB);
+
+	do {
+		cross_conn = wcd_check_cross_conn(mbhc);
+		try++;
+	} while (try < GND_MIC_SWAP_THRESHOLD);
+
+	if (cross_conn > 0) {
+		pr_debug("%s: cross con found, start polling\n",
+			 __func__);
+		plug_type = MBHC_PLUG_TYPE_GND_MIC_SWAP;
+		if (!mbhc->current_plug)
+			mbhc->current_plug = plug_type;
+		pr_debug("%s: Plug found, plug type is %d\n",
+			 __func__, plug_type);
+	}
+
+	/* Re-initialize button press completion object */
+	reinit_completion(&mbhc->btn_press_compl);
+	wcd_schedule_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd_mbhc_swch_irq_handler(struct wcd_mbhc *mbhc)
+{
+	bool detection_type = 0;
+	bool micbias1 = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	dev_dbg(codec->dev, "%s: enter\n", __func__);
+
+	WCD_MBHC_RSC_LOCK(mbhc);
+
+	mbhc->in_swch_irq_handler = true;
+
+	/* cancel pending button press */
+	if (wcd_cancel_btn_work(mbhc))
+		pr_debug("%s: button press is canceled\n", __func__);
+
+	WCD_MBHC_REG_READ(WCD_MBHC_MECH_DETECTION_TYPE, detection_type);
+
+	/* Set the detection type appropriately */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_MECH_DETECTION_TYPE,
+				 !detection_type);
+
+	pr_debug("%s: mbhc->current_plug: %d detection_type: %d\n", __func__,
+			mbhc->current_plug, detection_type);
+	wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+	if (mbhc->mbhc_cb->micbias_enable_status)
+		micbias1 = mbhc->mbhc_cb->micbias_enable_status(mbhc,
+						MIC_BIAS_1);
+
+	if ((mbhc->current_plug == MBHC_PLUG_TYPE_NONE) &&
+	    detection_type) {
+		/* Make sure MASTER_BIAS_CTL is enabled */
+		mbhc->mbhc_cb->mbhc_bias(codec, true);
+
+		if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_TAIL_CURR, true);
+
+		if (!mbhc->mbhc_cfg->hs_ext_micbias &&
+		     mbhc->mbhc_cb->micb_internal)
+			/*
+			 * Enable Tx2 RBias if the headset
+			 * is using internal micbias
+			 */
+			mbhc->mbhc_cb->micb_internal(codec, 1, true);
+
+		/* Remove micbias pulldown */
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 0);
+		/* Apply trim if needed on the device */
+		if (mbhc->mbhc_cb->trim_btn_reg)
+			mbhc->mbhc_cb->trim_btn_reg(codec);
+		/* Enable external voltage source to micbias if present */
+		if (mbhc->mbhc_cb->enable_mb_source)
+			mbhc->mbhc_cb->enable_mb_source(mbhc, true);
+		mbhc->btn_press_intr = false;
+		wcd_mbhc_detect_plug_type(mbhc);
+	} else if ((mbhc->current_plug != MBHC_PLUG_TYPE_NONE)
+			&& !detection_type) {
+		/* Disable external voltage source to micbias if present */
+		if (mbhc->mbhc_cb->enable_mb_source)
+			mbhc->mbhc_cb->enable_mb_source(mbhc, false);
+		/* Disable HW FSM */
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+		if (mbhc->mbhc_cb->mbhc_common_micb_ctrl)
+			mbhc->mbhc_cb->mbhc_common_micb_ctrl(codec,
+					MBHC_COMMON_MICB_TAIL_CURR, false);
+
+		if (mbhc->mbhc_cb->set_cap_mode)
+			mbhc->mbhc_cb->set_cap_mode(codec, micbias1, false);
+
+		mbhc->btn_press_intr = false;
+		if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+					     false);
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+					     false);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+						 1);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_GND_MIC_SWAP) {
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
+			/* make sure to turn off Rbias */
+			if (mbhc->mbhc_cb->micb_internal)
+				mbhc->mbhc_cb->micb_internal(codec, 1, false);
+
+			/* Pulldown micbias */
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_PULLDOWN_CTRL, 1);
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+					     false);
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+					     false);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+						 1);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_HEADSET);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_HIGH_HPH) {
+			mbhc->is_extn_cable = false;
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+					     false);
+			wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+					     false);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+						 1);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+		} else if (mbhc->current_plug == MBHC_PLUG_TYPE_ANC_HEADPHONE) {
+			mbhc->mbhc_cb->irq_control(codec,
+					mbhc->intr_ids->mbhc_hs_rem_intr,
+					false);
+			mbhc->mbhc_cb->irq_control(codec,
+					mbhc->intr_ids->mbhc_hs_ins_intr,
+					false);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE,
+						 0);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+			wcd_mbhc_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
+		}
+	} else if (!detection_type) {
+		/* Disable external voltage source to micbias if present */
+		if (mbhc->mbhc_cb->enable_mb_source)
+			mbhc->mbhc_cb->enable_mb_source(mbhc, false);
+		/* Disable HW FSM */
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_ISRC_CTL, 0);
+	}
+
+	mbhc->in_swch_irq_handler = false;
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd_mbhc_mech_plug_detect_irq(int irq, void *data)
+{
+	int r = IRQ_HANDLED;
+	struct wcd_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	if (unlikely((mbhc->mbhc_cb->lock_sleep(mbhc, true)) == false)) {
+		pr_warn("%s: failed to hold suspend\n", __func__);
+		r = IRQ_NONE;
+	} else {
+		/* Call handler */
+		wcd_mbhc_swch_irq_handler(mbhc);
+		mbhc->mbhc_cb->lock_sleep(mbhc, false);
+	}
+	pr_debug("%s: leave %d\n", __func__, r);
+	return r;
+}
+
+static int wcd_mbhc_get_button_mask(struct wcd_mbhc *mbhc)
+{
+	int mask = 0;
+	int btn;
+
+	btn = mbhc->mbhc_cb->map_btn_code_to_num(mbhc->codec);
+
+	switch (btn) {
+	case 0:
+		mask = SND_JACK_BTN_0;
+		break;
+	case 1:
+		mask = SND_JACK_BTN_1;
+		break;
+	case 2:
+		mask = SND_JACK_BTN_2;
+		break;
+	case 3:
+		mask = SND_JACK_BTN_3;
+		break;
+	case 4:
+		mask = SND_JACK_BTN_4;
+		break;
+	case 5:
+		mask = SND_JACK_BTN_5;
+		break;
+	default:
+		break;
+	}
+
+	return mask;
+}
+
+static irqreturn_t wcd_mbhc_hs_ins_irq(int irq, void *data)
+{
+	struct wcd_mbhc *mbhc = data;
+	bool detection_type = 0, hphl_sch = 0, mic_sch = 0;
+	u16 elect_result = 0;
+	static u16 hphl_trigerred;
+	static u16 mic_trigerred;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->mbhc_cfg->detect_extn_cable) {
+		pr_debug("%s: Returning as Extension cable feature not enabled\n",
+			__func__);
+		return IRQ_HANDLED;
+	}
+	WCD_MBHC_RSC_LOCK(mbhc);
+
+	WCD_MBHC_REG_READ(WCD_MBHC_ELECT_DETECTION_TYPE, detection_type);
+	WCD_MBHC_REG_READ(WCD_MBHC_ELECT_RESULT, elect_result);
+
+	pr_debug("%s: detection_type %d, elect_result %x\n", __func__,
+				detection_type, elect_result);
+	if (detection_type) {
+		/* check if both Left and MIC Schmitt triggers are triggered */
+		WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
+		WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
+		if (hphl_sch && mic_sch) {
+			/* Go for plug type determination */
+			pr_debug("%s: Go for plug type determination\n",
+				  __func__);
+			goto determine_plug;
+
+		} else {
+			if (mic_sch) {
+				mic_trigerred++;
+				pr_debug("%s: Insertion MIC trigerred %d\n",
+					 __func__, mic_trigerred);
+				WCD_MBHC_REG_UPDATE_BITS(
+						WCD_MBHC_ELECT_SCHMT_ISRC,
+						0);
+				msleep(20);
+				WCD_MBHC_REG_UPDATE_BITS(
+						WCD_MBHC_ELECT_SCHMT_ISRC,
+						1);
+			}
+			if (hphl_sch) {
+				hphl_trigerred++;
+				pr_debug("%s: Insertion HPHL trigerred %d\n",
+					 __func__, hphl_trigerred);
+			}
+			if (mic_trigerred && hphl_trigerred) {
+				/* Go for plug type determination */
+				pr_debug("%s: Go for plug type determination\n",
+					 __func__);
+				goto determine_plug;
+			}
+		}
+	}
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	pr_debug("%s: leave\n", __func__);
+	return IRQ_HANDLED;
+
+determine_plug:
+	/*
+	 * Disable HPHL trigger and MIC Schmitt triggers.
+	 * Setup for insertion detection.
+	 */
+	pr_debug("%s: Disable insertion interrupt\n", __func__);
+	wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+			     false);
+
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 0);
+	hphl_trigerred = 0;
+	mic_trigerred = 0;
+	mbhc->is_extn_cable = true;
+	mbhc->btn_press_intr = false;
+	wcd_mbhc_detect_plug_type(mbhc);
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	pr_debug("%s: leave\n", __func__);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hs_rem_irq(int irq, void *data)
+{
+	struct wcd_mbhc *mbhc = data;
+	u8 hs_comp_result = 0, hphl_sch = 0, mic_sch = 0;
+	static u16 hphl_trigerred;
+	static u16 mic_trigerred;
+	unsigned long timeout;
+	bool removed = true;
+	int retry = 0;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD_MBHC_RSC_LOCK(mbhc);
+
+	timeout = jiffies +
+		  msecs_to_jiffies(WCD_FAKE_REMOVAL_MIN_PERIOD_MS);
+	do {
+		retry++;
+		/*
+		 * read the result register every 10ms to look for
+		 * any change in HS_COMP_RESULT bit
+		 */
+		usleep_range(10000, 10100);
+		WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
+		pr_debug("%s: Check result reg for fake removal: hs_comp_res %x\n",
+			 __func__, hs_comp_result);
+		if ((!hs_comp_result) &&
+		    retry > FAKE_REM_RETRY_ATTEMPTS) {
+			removed = false;
+			break;
+		}
+	} while (!time_after(jiffies, timeout));
+
+	if (wcd_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low ", __func__);
+		goto exit;
+	}
+	pr_debug("%s: headset %s actually removed\n", __func__,
+		removed ? "" : "not ");
+
+	WCD_MBHC_REG_READ(WCD_MBHC_HPHL_SCHMT_RESULT, hphl_sch);
+	WCD_MBHC_REG_READ(WCD_MBHC_MIC_SCHMT_RESULT, mic_sch);
+	WCD_MBHC_REG_READ(WCD_MBHC_HS_COMP_RESULT, hs_comp_result);
+
+	if (removed) {
+		if (!(hphl_sch && mic_sch && hs_comp_result)) {
+			/*
+			 * extension cable is still plugged in
+			 * report it as LINEOUT device
+			 */
+			goto report_unplug;
+		} else {
+			if (!mic_sch) {
+				mic_trigerred++;
+				pr_debug("%s: Removal MIC trigerred %d\n",
+					 __func__, mic_trigerred);
+			}
+			if (!hphl_sch) {
+				hphl_trigerred++;
+				pr_debug("%s: Removal HPHL trigerred %d\n",
+					 __func__, hphl_trigerred);
+			}
+			if (mic_trigerred && hphl_trigerred) {
+				/*
+				 * extension cable is still plugged in
+				 * report it as LINEOUT device
+				 */
+				goto report_unplug;
+			}
+		}
+	}
+exit:
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	pr_debug("%s: leave\n", __func__);
+	return IRQ_HANDLED;
+
+report_unplug:
+
+	/* cancel pending button press */
+	if (wcd_cancel_btn_work(mbhc))
+		pr_debug("%s: button press is canceled\n", __func__);
+	/* cancel correct work function */
+	wcd_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+	pr_debug("%s: Report extension cable\n", __func__);
+	wcd_mbhc_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+	/*
+	 * If PA is enabled HPHL schmitt trigger can
+	 * be unreliable, make sure to disable it
+	 */
+	if (test_bit(WCD_MBHC_EVENT_PA_HPHL,
+		&mbhc->event_state))
+		wcd_mbhc_set_and_turnoff_hph_padac(mbhc);
+	/*
+	 * Disable HPHL trigger and MIC Schmitt triggers.
+	 * Setup for insertion detection.
+	 */
+	wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_REM,
+			     false);
+	wcd_enable_curr_micbias(mbhc, WCD_MBHC_EN_NONE);
+	/* Disable HW FSM */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_FSM_EN, 0);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_SCHMT_ISRC, 3);
+
+	/* Set the detection type appropriately */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_ELECT_DETECTION_TYPE, 1);
+	wcd_mbhc_hs_elec_irq(mbhc, WCD_MBHC_ELEC_HS_INS,
+			     true);
+	hphl_trigerred = 0;
+	mic_trigerred = 0;
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	pr_debug("%s: leave\n", __func__);
+	return IRQ_HANDLED;
+}
+
+static void wcd_btn_lpress_fn(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd_mbhc *mbhc;
+	s16 btn_result = 0;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd_mbhc, mbhc_btn_dwork);
+
+	WCD_MBHC_REG_READ(WCD_MBHC_BTN_RESULT, btn_result);
+	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADSET) {
+		pr_debug("%s: Reporting long button press event, btn_result: %d\n",
+			 __func__, btn_result);
+		wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
+				mbhc->buttons_pressed, mbhc->buttons_pressed);
+	}
+	pr_debug("%s: leave\n", __func__);
+	mbhc->mbhc_cb->lock_sleep(mbhc, false);
+}
+
+static bool wcd_mbhc_fw_validate(const void *data, size_t size)
+{
+	u32 cfg_offset;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	struct firmware_cal fw;
+
+	fw.data = (void *)data;
+	fw.size = size;
+
+	if (fw.size < WCD_MBHC_CAL_MIN_SIZE)
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to num_btn
+	 */
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(fw.data);
+	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
+	if (fw.size < (cfg_offset + WCD_MBHC_CAL_BTN_SZ(btn_cfg)))
+		return false;
+
+	return true;
+}
+
+static irqreturn_t wcd_mbhc_btn_press_handler(int irq, void *data)
+{
+	struct wcd_mbhc *mbhc = data;
+	int mask;
+	unsigned long msec_val;
+
+	pr_debug("%s: enter\n", __func__);
+	complete(&mbhc->btn_press_compl);
+	WCD_MBHC_RSC_LOCK(mbhc);
+	/* send event to sw intr handler*/
+	mbhc->is_btn_press = true;
+	wcd_cancel_btn_work(mbhc);
+	if (wcd_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low ", __func__);
+		goto done;
+	}
+	mbhc->btn_press_intr = true;
+
+	msec_val = jiffies_to_msecs(jiffies - mbhc->jiffies_atreport);
+	pr_debug("%s: msec_val = %ld\n", __func__, msec_val);
+	if (msec_val < MBHC_BUTTON_PRESS_THRESHOLD_MIN) {
+		pr_debug("%s: Too short, ignore button press\n", __func__);
+		goto done;
+	}
+
+	/* If switch interrupt already kicked in, ignore button press */
+	if (mbhc->in_swch_irq_handler) {
+		pr_debug("%s: Swtich level changed, ignore button press\n",
+			 __func__);
+		goto done;
+	}
+	if (mbhc->current_plug != MBHC_PLUG_TYPE_HEADSET) {
+		pr_debug("%s: Plug isn't headset, ignore button press\n",
+				__func__);
+		goto done;
+	}
+	mask = wcd_mbhc_get_button_mask(mbhc);
+	mbhc->buttons_pressed |= mask;
+	mbhc->mbhc_cb->lock_sleep(mbhc, true);
+	if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+				msecs_to_jiffies(400)) == 0) {
+		WARN(1, "Button pressed twice without release event\n");
+		mbhc->mbhc_cb->lock_sleep(mbhc, false);
+	}
+done:
+	pr_debug("%s: leave\n", __func__);
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_release_handler(int irq, void *data)
+{
+	struct wcd_mbhc *mbhc = data;
+	int ret;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD_MBHC_RSC_LOCK(mbhc);
+	if (wcd_swch_level_remove(mbhc)) {
+		pr_debug("%s: Switch level is low ", __func__);
+		goto exit;
+	}
+
+	if (mbhc->btn_press_intr) {
+		mbhc->btn_press_intr = false;
+	} else {
+		pr_debug("%s: This release is for fake btn press\n", __func__);
+		goto exit;
+	}
+
+	/*
+	 * If current plug is headphone then there is no chance to
+	 * get btn release interrupt, so connected cable should be
+	 * headset not headphone.
+	 */
+	if (mbhc->current_plug == MBHC_PLUG_TYPE_HEADPHONE) {
+		wcd_mbhc_find_plug_and_report(mbhc, MBHC_PLUG_TYPE_HEADSET);
+		goto exit;
+
+	}
+	if (mbhc->buttons_pressed & WCD_MBHC_JACK_BUTTON_MASK) {
+		ret = wcd_cancel_btn_work(mbhc);
+		if (ret == 0) {
+			pr_debug("%s: Reporting long button release event\n",
+				 __func__);
+			wcd_mbhc_jack_report(mbhc, &mbhc->button_jack,
+					0, mbhc->buttons_pressed);
+		} else {
+			if (mbhc->in_swch_irq_handler) {
+				pr_debug("%s: Switch irq kicked in, ignore\n",
+					__func__);
+			} else {
+				pr_debug("%s: Reporting btn press\n",
+					 __func__);
+				wcd_mbhc_jack_report(mbhc,
+						     &mbhc->button_jack,
+						     mbhc->buttons_pressed,
+						     mbhc->buttons_pressed);
+				pr_debug("%s: Reporting btn release\n",
+					 __func__);
+				wcd_mbhc_jack_report(mbhc,
+						&mbhc->button_jack,
+						0, mbhc->buttons_pressed);
+			}
+		}
+		mbhc->buttons_pressed &= ~WCD_MBHC_JACK_BUTTON_MASK;
+	}
+exit:
+	pr_debug("%s: leave\n", __func__);
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hphl_ocp_irq(int irq, void *data)
+{
+	struct wcd_mbhc *mbhc = data;
+	int val;
+
+	pr_debug("%s: received HPHL OCP irq\n", __func__);
+	if (mbhc) {
+		if (mbhc->mbhc_cb->hph_register_recovery) {
+			if (mbhc->mbhc_cb->hph_register_recovery(mbhc)) {
+				WCD_MBHC_REG_READ(WCD_MBHC_HPHR_OCP_STATUS,
+						  val);
+				if ((val != -EINVAL) && val)
+					mbhc->is_hph_ocp_pending = true;
+				goto done;
+			}
+		}
+
+		if (mbhc->hphlocp_cnt < OCP_ATTEMPT) {
+			mbhc->hphlocp_cnt++;
+			pr_debug("%s: retry, hphlocp_cnt: %d\n", __func__,
+				 mbhc->hphlocp_cnt);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
+			WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
+		} else {
+			mbhc->mbhc_cb->irq_control(mbhc->codec,
+						   mbhc->intr_ids->hph_left_ocp,
+						   false);
+			mbhc->hph_status |= SND_JACK_OC_HPHL;
+			wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+					    mbhc->hph_status,
+					    WCD_MBHC_JACK_MASK);
+		}
+	} else {
+		pr_err("%s: Bad wcd9xxx_spmi private data\n", __func__);
+	}
+done:
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_mbhc_hphr_ocp_irq(int irq, void *data)
+{
+	struct wcd_mbhc *mbhc = data;
+
+	pr_debug("%s: received HPHR OCP irq\n", __func__);
+
+	if (!mbhc) {
+		pr_err("%s: Bad mbhc private data\n", __func__);
+		goto done;
+	}
+
+	if (mbhc->is_hph_ocp_pending) {
+		mbhc->is_hph_ocp_pending = false;
+		goto done;
+	}
+
+	if (mbhc->mbhc_cb->hph_register_recovery) {
+		if (mbhc->mbhc_cb->hph_register_recovery(mbhc))
+			/* register corruption, hence reset registers */
+			goto done;
+	}
+	if (mbhc->hphrocp_cnt < OCP_ATTEMPT) {
+		mbhc->hphrocp_cnt++;
+		pr_debug("%s: retry, hphrocp_cnt: %d\n", __func__,
+			 mbhc->hphrocp_cnt);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 0);
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_OCP_FSM_EN, 1);
+	} else {
+		mbhc->mbhc_cb->irq_control(mbhc->codec,
+					   mbhc->intr_ids->hph_right_ocp,
+					   false);
+		mbhc->hph_status |= SND_JACK_OC_HPHR;
+		wcd_mbhc_jack_report(mbhc, &mbhc->headset_jack,
+				    mbhc->hph_status, WCD_MBHC_JACK_MASK);
+	}
+done:
+	return IRQ_HANDLED;
+}
+
+static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD_MBHC_RSC_LOCK(mbhc);
+
+	/* enable HS detection */
+	if (mbhc->mbhc_cb->hph_pull_up_control)
+		mbhc->mbhc_cb->hph_pull_up_control(codec, I_DEFAULT);
+	else
+		WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_CTRL, 3);
+
+	if (mbhc->mbhc_cfg->moisture_en && mbhc->mbhc_cb->mbhc_moisture_config)
+		mbhc->mbhc_cb->mbhc_moisture_config(mbhc);
+
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HPHL_PLUG_TYPE, mbhc->hphl_swh);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_GND_PLUG_TYPE, mbhc->gnd_swh);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_SW_HPH_LP_100K_TO_GND, 1);
+	if (mbhc->mbhc_cfg->gnd_det_en && mbhc->mbhc_cb->mbhc_gnd_det_ctrl)
+		mbhc->mbhc_cb->mbhc_gnd_det_ctrl(codec, true);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL, 1);
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_L_DET_EN, 1);
+
+	/* Insertion debounce set to 96ms */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_INSREM_DBNC, 6);
+	/* Button Debounce set to 16ms */
+	WCD_MBHC_REG_UPDATE_BITS(WCD_MBHC_BTN_DBNC, 2);
+
+	/* Enable micbias ramp */
+	if (mbhc->mbhc_cb->mbhc_micb_ramp_control)
+		mbhc->mbhc_cb->mbhc_micb_ramp_control(codec, true);
+	/* enable bias */
+	mbhc->mbhc_cb->mbhc_bias(codec, true);
+	/* enable MBHC clock */
+	if (mbhc->mbhc_cb->clk_setup)
+		mbhc->mbhc_cb->clk_setup(codec, true);
+
+	/* program HS_VREF value */
+	wcd_program_hs_vref(mbhc);
+
+	wcd_program_btn_threshold(mbhc, false);
+
+	INIT_WORK(&mbhc->correct_plug_swch, wcd_correct_swch_plug);
+
+	init_completion(&mbhc->btn_press_compl);
+
+	WCD_MBHC_RSC_UNLOCK(mbhc);
+	pr_debug("%s: leave\n", __func__);
+	return ret;
+}
+
+static void wcd_mbhc_fw_read(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	const struct firmware *fw;
+	struct firmware_cal *fw_data = NULL;
+	int ret = -1, retry = 0;
+	bool use_default_cal = false;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd_mbhc, mbhc_firmware_dwork);
+	codec = mbhc->codec;
+
+	while (retry < FW_READ_ATTEMPTS) {
+		retry++;
+		pr_debug("%s:Attempt %d to request MBHC firmware\n",
+			__func__, retry);
+		if (mbhc->mbhc_cb->get_hwdep_fw_cal)
+			fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(mbhc,
+					WCD9XXX_MBHC_CAL);
+		if (!fw_data)
+			ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+				       codec->dev);
+		/*
+		 * if request_firmware and hwdep cal both fail then
+		 * sleep for 4sec for the userspace to send data to kernel
+		 * retry for few times before bailing out
+		 */
+		if ((ret != 0) && !fw_data) {
+			usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT +
+					WCD_MBHC_USLEEP_RANGE_MARGIN_US);
+		} else {
+			pr_debug("%s: MBHC Firmware read successful\n",
+					__func__);
+			break;
+		}
+	}
+	if (!fw_data)
+		pr_debug("%s: using request_firmware\n", __func__);
+	else
+		pr_debug("%s: using hwdep cal\n", __func__);
+
+	if (ret != 0 && !fw_data) {
+		pr_err("%s: Cannot load MBHC firmware use default cal\n",
+		       __func__);
+		use_default_cal = true;
+	}
+	if (!use_default_cal) {
+		const void *data;
+		size_t size;
+
+		if (fw_data) {
+			data = fw_data->data;
+			size = fw_data->size;
+		} else {
+			data = fw->data;
+			size = fw->size;
+		}
+		if (wcd_mbhc_fw_validate(data, size) == false) {
+			pr_err("%s: Invalid MBHC cal data size use default cal\n",
+				__func__);
+			if (!fw_data)
+				release_firmware(fw);
+		} else {
+			if (fw_data) {
+				mbhc->mbhc_cfg->calibration =
+					(void *)fw_data->data;
+				mbhc->mbhc_cal = fw_data;
+			} else {
+				mbhc->mbhc_cfg->calibration =
+					(void *)fw->data;
+				mbhc->mbhc_fw = fw;
+			}
+		}
+
+	}
+
+	(void) wcd_mbhc_initialise(mbhc);
+}
+
+int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc)
+{
+	enum snd_jack_types type;
+	int i, ret, result = 0;
+	int *btn_key_code;
+
+	btn_key_code = mbhc->mbhc_cfg->key_code;
+
+	for (i = 0 ; i < WCD_MBHC_KEYCODE_NUM ; i++) {
+		if (btn_key_code[i] != 0) {
+			switch (i) {
+			case 0:
+				type = SND_JACK_BTN_0;
+				break;
+			case 1:
+				type = SND_JACK_BTN_1;
+				break;
+			case 2:
+				type = SND_JACK_BTN_2;
+				break;
+			case 3:
+				type = SND_JACK_BTN_3;
+				break;
+			case 4:
+				type = SND_JACK_BTN_4;
+				break;
+			case 5:
+				type = SND_JACK_BTN_5;
+				break;
+			default:
+				WARN_ONCE(1, "Wrong button number:%d\n", i);
+				result = -1;
+				return result;
+			}
+			ret = snd_jack_set_key(mbhc->button_jack.jack,
+							type,
+							btn_key_code[i]);
+			if (ret) {
+				pr_err("%s: Failed to set code for %d\n",
+					__func__, btn_key_code[i]);
+				result = -1;
+				return result;
+			}
+			input_set_capability(
+				mbhc->button_jack.jack->input_dev,
+				EV_KEY, btn_key_code[i]);
+			pr_debug("%s: set btn%d key code:%d\n", __func__,
+				i, btn_key_code[i]);
+		}
+	}
+	if (btn_key_code[0])
+		mbhc->is_btn_already_regd = true;
+	return result;
+}
+
+int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+		       struct wcd_mbhc_config *mbhc_cfg)
+{
+	int rc = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	/* update the mbhc config */
+	mbhc->mbhc_cfg = mbhc_cfg;
+
+	/* Set btn key code */
+	if ((!mbhc->is_btn_already_regd) && wcd_mbhc_set_keycode(mbhc))
+		pr_err("Set btn key code error!!!\n");
+
+	if (!mbhc->mbhc_cfg->read_fw_bin ||
+	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
+	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
+		rc = wcd_mbhc_initialise(mbhc);
+	} else {
+		if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
+			schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+				      usecs_to_jiffies(FW_READ_TIMEOUT));
+		else
+			pr_err("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
+				 __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
+	}
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+EXPORT_SYMBOL(wcd_mbhc_start);
+
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+	pr_debug("%s: enter\n", __func__);
+	if (mbhc->current_plug != MBHC_PLUG_TYPE_NONE) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->skip_imped_detect)
+			mbhc->mbhc_cb->skip_imped_detect(mbhc->codec);
+	}
+	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+	mbhc->hph_status = 0;
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->irq_control) {
+		mbhc->mbhc_cb->irq_control(mbhc->codec,
+				mbhc->intr_ids->hph_left_ocp,
+				false);
+		mbhc->mbhc_cb->irq_control(mbhc->codec,
+				mbhc->intr_ids->hph_right_ocp,
+				false);
+	}
+	if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
+		cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
+		if (!mbhc->mbhc_cal)
+			release_firmware(mbhc->mbhc_fw);
+		mbhc->mbhc_fw = NULL;
+		mbhc->mbhc_cal = NULL;
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+EXPORT_SYMBOL(wcd_mbhc_stop);
+
+/*
+ * wcd_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
+		      const struct wcd_mbhc_cb *mbhc_cb,
+		      const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+		      struct wcd_mbhc_register *wcd_mbhc_regs,
+		      bool impedance_det_en)
+{
+	int ret = 0;
+	int hph_swh = 0;
+	int gnd_swh = 0;
+	struct snd_soc_card *card = codec->component.card;
+	const char *hph_switch = "qcom,msm-mbhc-hphl-swh";
+	const char *gnd_switch = "qcom,msm-mbhc-gnd-swh";
+
+	pr_debug("%s: enter\n", __func__);
+
+	ret = of_property_read_u32(card->dev->of_node, hph_switch, &hph_swh);
+	if (ret) {
+		dev_err(card->dev,
+			"%s: missing %s in dt node\n", __func__, hph_switch);
+		goto err;
+	}
+
+	ret = of_property_read_u32(card->dev->of_node, gnd_switch, &gnd_swh);
+	if (ret) {
+		dev_err(card->dev,
+			"%s: missing %s in dt node\n", __func__, gnd_switch);
+		goto err;
+	}
+
+	mbhc->in_swch_irq_handler = false;
+	mbhc->current_plug = MBHC_PLUG_TYPE_NONE;
+	mbhc->is_btn_press = false;
+	mbhc->codec = codec;
+	mbhc->intr_ids = mbhc_cdc_intr_ids;
+	mbhc->impedance_detect = impedance_det_en;
+	mbhc->hphl_swh = hph_swh;
+	mbhc->gnd_swh = gnd_swh;
+	mbhc->micbias_enable = false;
+	mbhc->mbhc_cb = mbhc_cb;
+	mbhc->btn_press_intr = false;
+	mbhc->is_hs_recording = false;
+	mbhc->is_extn_cable = false;
+	mbhc->hph_type = WCD_MBHC_HPH_NONE;
+	mbhc->wcd_mbhc_regs = wcd_mbhc_regs;
+
+	if (mbhc->intr_ids == NULL) {
+		pr_err("%s: Interrupt mapping not provided\n", __func__);
+		return -EINVAL;
+	}
+	if (!mbhc->wcd_mbhc_regs) {
+		dev_err(codec->dev, "%s: mbhc registers are not defined\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/* Check if IRQ and other required callbacks are defined or not */
+	if (!mbhc_cb || !mbhc_cb->request_irq || !mbhc_cb->irq_control ||
+	    !mbhc_cb->free_irq || !mbhc_cb->map_btn_code_to_num ||
+	    !mbhc_cb->lock_sleep || !mbhc_cb->mbhc_bias ||
+	    !mbhc_cb->set_btn_thr) {
+		dev_err(codec->dev, "%s: required mbhc callbacks are not defined\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (mbhc->headset_jack.jack == NULL) {
+		ret = snd_soc_card_jack_new(codec->component.card,
+					    "Headset Jack", WCD_MBHC_JACK_MASK,
+					    &mbhc->headset_jack, NULL, 0);
+		if (ret) {
+			pr_err("%s: Failed to create new jack\n", __func__);
+			return ret;
+		}
+
+		ret = snd_soc_card_jack_new(codec->component.card,
+					    "Button Jack",
+					    WCD_MBHC_JACK_BUTTON_MASK,
+					    &mbhc->button_jack, NULL, 0);
+		if (ret) {
+			pr_err("Failed to create new jack\n");
+			return ret;
+		}
+
+		ret = snd_jack_set_key(mbhc->button_jack.jack,
+				       SND_JACK_BTN_0,
+				       KEY_MEDIA);
+		if (ret) {
+			pr_err("%s: Failed to set code for btn-0\n",
+				__func__);
+			return ret;
+		}
+
+		INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
+				  wcd_mbhc_fw_read);
+		INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd_btn_lpress_fn);
+	}
+	mutex_init(&mbhc->hphl_pa_lock);
+	mutex_init(&mbhc->hphr_pa_lock);
+
+	/* Register event notifier */
+	mbhc->nblock.notifier_call = wcd_event_notify;
+	if (mbhc->mbhc_cb->register_notifier) {
+		ret = mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock,
+						       true);
+		if (ret) {
+			pr_err("%s: Failed to register notifier %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	init_waitqueue_head(&mbhc->wait_btn_press);
+	mutex_init(&mbhc->codec_resource_lock);
+
+	ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->mbhc_sw_intr,
+				  wcd_mbhc_mech_plug_detect_irq,
+				  "mbhc sw intr", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
+		       mbhc->intr_ids->mbhc_sw_intr, ret);
+		goto err_mbhc_sw_irq;
+	}
+
+	ret = mbhc->mbhc_cb->request_irq(codec,
+					 mbhc->intr_ids->mbhc_btn_press_intr,
+					 wcd_mbhc_btn_press_handler,
+					 "Button Press detect",
+					 mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->mbhc_btn_press_intr);
+		goto err_btn_press_irq;
+	}
+
+	ret = mbhc->mbhc_cb->request_irq(codec,
+					 mbhc->intr_ids->mbhc_btn_release_intr,
+					 wcd_mbhc_release_handler,
+					 "Button Release detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			mbhc->intr_ids->mbhc_btn_release_intr);
+		goto err_btn_release_irq;
+	}
+
+	ret = mbhc->mbhc_cb->request_irq(codec,
+					 mbhc->intr_ids->mbhc_hs_ins_intr,
+					 wcd_mbhc_hs_ins_irq,
+					 "Elect Insert", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->mbhc_hs_ins_intr);
+		goto err_mbhc_hs_ins_irq;
+	}
+	mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_ins_intr,
+				   false);
+	clear_bit(WCD_MBHC_ELEC_HS_INS, &mbhc->intr_status);
+
+	ret = mbhc->mbhc_cb->request_irq(codec,
+					 mbhc->intr_ids->mbhc_hs_rem_intr,
+					 wcd_mbhc_hs_rem_irq,
+					 "Elect Remove", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->mbhc_hs_rem_intr);
+		goto err_mbhc_hs_rem_irq;
+	}
+	mbhc->mbhc_cb->irq_control(codec, mbhc->intr_ids->mbhc_hs_rem_intr,
+				   false);
+	clear_bit(WCD_MBHC_ELEC_HS_REM, &mbhc->intr_status);
+
+	ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_left_ocp,
+				  wcd_mbhc_hphl_ocp_irq, "HPH_L OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->hph_left_ocp);
+		goto err_hphl_ocp_irq;
+	}
+
+	ret = mbhc->mbhc_cb->request_irq(codec, mbhc->intr_ids->hph_right_ocp,
+				  wcd_mbhc_hphr_ocp_irq, "HPH_R OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->hph_right_ocp);
+		goto err_hphr_ocp_irq;
+	}
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+
+err_hphr_ocp_irq:
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc);
+err_hphl_ocp_irq:
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+err_mbhc_hs_rem_irq:
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+err_mbhc_hs_ins_irq:
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr,
+				mbhc);
+err_btn_release_irq:
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr,
+				mbhc);
+err_btn_press_irq:
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc);
+err_mbhc_sw_irq:
+	if (mbhc->mbhc_cb->register_notifier)
+		mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
+	mutex_destroy(&mbhc->codec_resource_lock);
+err:
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+}
+EXPORT_SYMBOL(wcd_mbhc_init);
+
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_sw_intr, mbhc);
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_press_intr,
+				mbhc);
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_btn_release_intr,
+				mbhc);
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_ins_intr, mbhc);
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->mbhc_hs_rem_intr, mbhc);
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_left_ocp, mbhc);
+	mbhc->mbhc_cb->free_irq(codec, mbhc->intr_ids->hph_right_ocp, mbhc);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->register_notifier)
+		mbhc->mbhc_cb->register_notifier(mbhc, &mbhc->nblock, false);
+	mutex_destroy(&mbhc->codec_resource_lock);
+	mutex_destroy(&mbhc->hphl_pa_lock);
+	mutex_destroy(&mbhc->hphr_pa_lock);
+}
+EXPORT_SYMBOL(wcd_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd MBHC v2 module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
new file mode 100644
index 0000000..c9bd4fe
--- /dev/null
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -0,0 +1,540 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD_MBHC_V2_H__
+#define __WCD_MBHC_V2_H__
+
+#include <linux/wait.h>
+#include <linux/stringify.h>
+#include "wcdcal-hwdep.h"
+
+#define TOMBAK_MBHC_NC	0
+#define TOMBAK_MBHC_NO	1
+#define WCD_MBHC_DEF_BUTTONS 8
+#define WCD_MBHC_KEYCODE_NUM 8
+#define WCD_MBHC_USLEEP_RANGE_MARGIN_US 100
+#define WCD_MBHC_THR_HS_MICB_MV  2700
+/* z value defined in Ohms */
+#define WCD_MONO_HS_MIN_THR	2
+#define WCD_MBHC_STRINGIFY(s)  __stringify(s)
+
+enum {
+	WCD_MBHC_ELEC_HS_INS,
+	WCD_MBHC_ELEC_HS_REM,
+};
+
+struct wcd_mbhc;
+enum wcd_mbhc_register_function {
+	WCD_MBHC_L_DET_EN,
+	WCD_MBHC_GND_DET_EN,
+	WCD_MBHC_MECH_DETECTION_TYPE,
+	WCD_MBHC_MIC_CLAMP_CTL,
+	WCD_MBHC_ELECT_DETECTION_TYPE,
+	WCD_MBHC_HS_L_DET_PULL_UP_CTRL,
+	WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL,
+	WCD_MBHC_HPHL_PLUG_TYPE,
+	WCD_MBHC_GND_PLUG_TYPE,
+	WCD_MBHC_SW_HPH_LP_100K_TO_GND,
+	WCD_MBHC_ELECT_SCHMT_ISRC,
+	WCD_MBHC_FSM_EN,
+	WCD_MBHC_INSREM_DBNC,
+	WCD_MBHC_BTN_DBNC,
+	WCD_MBHC_HS_VREF,
+	WCD_MBHC_HS_COMP_RESULT,
+	WCD_MBHC_MIC_SCHMT_RESULT,
+	WCD_MBHC_HPHL_SCHMT_RESULT,
+	WCD_MBHC_HPHR_SCHMT_RESULT,
+	WCD_MBHC_OCP_FSM_EN,
+	WCD_MBHC_BTN_RESULT,
+	WCD_MBHC_BTN_ISRC_CTL,
+	WCD_MBHC_ELECT_RESULT,
+	WCD_MBHC_MICB_CTRL,    /* Pull-up and micb control */
+	WCD_MBHC_HPH_CNP_WG_TIME,
+	WCD_MBHC_HPHR_PA_EN,
+	WCD_MBHC_HPHL_PA_EN,
+	WCD_MBHC_HPH_PA_EN,
+	WCD_MBHC_SWCH_LEVEL_REMOVE,
+	WCD_MBHC_PULLDOWN_CTRL,
+	WCD_MBHC_ANC_DET_EN,
+	WCD_MBHC_FSM_STATUS,
+	WCD_MBHC_MUX_CTL,
+	WCD_MBHC_HPHL_OCP_DET_EN,
+	WCD_MBHC_HPHR_OCP_DET_EN,
+	WCD_MBHC_HPHL_OCP_STATUS,
+	WCD_MBHC_HPHR_OCP_STATUS,
+	WCD_MBHC_REG_FUNC_MAX,
+};
+
+enum wcd_mbhc_plug_type {
+	MBHC_PLUG_TYPE_INVALID = -1,
+	MBHC_PLUG_TYPE_NONE,
+	MBHC_PLUG_TYPE_HEADSET,
+	MBHC_PLUG_TYPE_HEADPHONE,
+	MBHC_PLUG_TYPE_HIGH_HPH,
+	MBHC_PLUG_TYPE_GND_MIC_SWAP,
+	MBHC_PLUG_TYPE_ANC_HEADPHONE,
+};
+
+enum pa_dac_ack_flags {
+	WCD_MBHC_HPHL_PA_OFF_ACK = 0,
+	WCD_MBHC_HPHR_PA_OFF_ACK,
+};
+
+enum wcd_mbhc_btn_det_mem {
+	WCD_MBHC_BTN_DET_V_BTN_LOW,
+	WCD_MBHC_BTN_DET_V_BTN_HIGH
+};
+
+enum {
+	MIC_BIAS_1 = 1,
+	MIC_BIAS_2,
+	MIC_BIAS_3,
+	MIC_BIAS_4
+};
+
+enum {
+	MICB_PULLUP_ENABLE,
+	MICB_PULLUP_DISABLE,
+	MICB_ENABLE,
+	MICB_DISABLE,
+};
+
+enum {
+	MBHC_COMMON_MICB_PRECHARGE,
+	MBHC_COMMON_MICB_SET_VAL,
+	MBHC_COMMON_MICB_TAIL_CURR,
+};
+
+enum wcd_notify_event {
+	WCD_EVENT_INVALID,
+	/* events for micbias ON and OFF */
+	WCD_EVENT_PRE_MICBIAS_2_OFF,
+	WCD_EVENT_POST_MICBIAS_2_OFF,
+	WCD_EVENT_PRE_MICBIAS_2_ON,
+	WCD_EVENT_POST_MICBIAS_2_ON,
+	WCD_EVENT_PRE_DAPM_MICBIAS_2_OFF,
+	WCD_EVENT_POST_DAPM_MICBIAS_2_OFF,
+	WCD_EVENT_PRE_DAPM_MICBIAS_2_ON,
+	WCD_EVENT_POST_DAPM_MICBIAS_2_ON,
+	/* events for PA ON and OFF */
+	WCD_EVENT_PRE_HPHL_PA_ON,
+	WCD_EVENT_POST_HPHL_PA_OFF,
+	WCD_EVENT_PRE_HPHR_PA_ON,
+	WCD_EVENT_POST_HPHR_PA_OFF,
+	WCD_EVENT_PRE_HPHL_PA_OFF,
+	WCD_EVENT_PRE_HPHR_PA_OFF,
+	WCD_EVENT_OCP_OFF,
+	WCD_EVENT_OCP_ON,
+	WCD_EVENT_LAST,
+};
+
+enum wcd_mbhc_event_state {
+	WCD_MBHC_EVENT_PA_HPHL,
+	WCD_MBHC_EVENT_PA_HPHR,
+};
+struct wcd_mbhc_general_cfg {
+	u8 t_ldoh;
+	u8 t_bg_fast_settle;
+	u8 t_shutdown_plug_rem;
+	u8 mbhc_nsa;
+	u8 mbhc_navg;
+	u8 v_micbias_l;
+	u8 v_micbias;
+	u8 mbhc_reserved;
+	u16 settle_wait;
+	u16 t_micbias_rampup;
+	u16 t_micbias_rampdown;
+	u16 t_supply_bringup;
+} __packed;
+
+struct wcd_mbhc_plug_detect_cfg {
+	u32 mic_current;
+	u32 hph_current;
+	u16 t_mic_pid;
+	u16 t_ins_complete;
+	u16 t_ins_retry;
+	u16 v_removal_delta;
+	u8 micbias_slow_ramp;
+	u8 reserved0;
+	u8 reserved1;
+	u8 reserved2;
+} __packed;
+
+struct wcd_mbhc_plug_type_cfg {
+	u8 av_detect;
+	u8 mono_detect;
+	u8 num_ins_tries;
+	u8 reserved0;
+	s16 v_no_mic;
+	s16 v_av_min;
+	s16 v_av_max;
+	s16 v_hs_min;
+	s16 v_hs_max;
+	u16 reserved1;
+} __packed;
+
+struct wcd_mbhc_btn_detect_cfg {
+	s8 c[8];
+	u8 nc;
+	u8 n_meas;
+	u8 mbhc_nsc;
+	u8 n_btn_meas;
+	u8 n_btn_con;
+	u8 num_btn;
+	u8 reserved0;
+	u8 reserved1;
+	u16 t_poll;
+	u16 t_bounce_wait;
+	u16 t_rel_timeout;
+	s16 v_btn_press_delta_sta;
+	s16 v_btn_press_delta_cic;
+	u16 t_btn0_timeout;
+	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+	u8 _n_ready[2];
+	u8 _n_cic[2];
+	u8 _gain[2];
+} __packed;
+
+struct wcd_mbhc_imped_detect_cfg {
+	u8 _hs_imped_detect;
+	u8 _n_rload;
+	u8 _hph_keep_on;
+	u8 _repeat_rload_calc;
+	u16 _t_dac_ramp_time;
+	u16 _rhph_high;
+	u16 _rhph_low;
+	u16 _rload[0]; /* rload[n_rload] */
+	u16 _alpha[0]; /* alpha[n_rload] */
+	u16 _beta[3];
+} __packed;
+
+enum wcd_mbhc_hph_type {
+	WCD_MBHC_HPH_NONE = 0,
+	WCD_MBHC_HPH_MONO,
+	WCD_MBHC_HPH_STEREO,
+};
+
+/*
+ * These enum definitions are directly mapped to the register
+ * definitions
+ */
+enum mbhc_moisture_vref {
+	V_OFF,
+	V_45_MV,
+	V_100_MV,
+	V_225_MV,
+};
+
+enum mbhc_hs_pullup_iref {
+	I_DEFAULT = -1,
+	I_OFF = 0,
+	I_1P0_UA,
+	I_2P0_UA,
+	I_3P0_UA,
+};
+
+enum mbhc_moisture_rref {
+	R_OFF,
+	R_24_KOHM,
+	R_84_KOHM,
+	R_184_KOHM,
+};
+
+struct wcd_mbhc_config {
+	bool read_fw_bin;
+	void *calibration;
+	bool detect_extn_cable;
+	bool mono_stero_detection;
+	bool (*swap_gnd_mic)(struct snd_soc_codec *codec);
+	bool hs_ext_micbias;
+	bool gnd_det_en;
+	int key_code[WCD_MBHC_KEYCODE_NUM];
+	uint32_t linein_th;
+	bool moisture_en;
+	int mbhc_micbias;
+	int anc_micbias;
+	bool enable_anc_mic_detect;
+};
+
+struct wcd_mbhc_intr {
+	int mbhc_sw_intr;
+	int mbhc_btn_press_intr;
+	int mbhc_btn_release_intr;
+	int mbhc_hs_ins_intr;
+	int mbhc_hs_rem_intr;
+	int hph_left_ocp;
+	int hph_right_ocp;
+};
+
+struct wcd_mbhc_register {
+	const char *id;
+	u16 reg;
+	u8 mask;
+	u8 offset;
+	u8 invert;
+};
+
+#define WCD_MBHC_REGISTER(rid, rreg, rmask, rshift, rinvert) \
+{ .id = rid, .reg = rreg, .mask = rmask, .offset = rshift, .invert = rinvert }
+
+#define WCD_MBHC_RSC_LOCK(mbhc)			\
+{							\
+	pr_debug("%s: Acquiring BCL\n", __func__);	\
+	mutex_lock(&mbhc->codec_resource_lock);		\
+	pr_debug("%s: Acquiring BCL done\n", __func__);	\
+}
+
+#define WCD_MBHC_RSC_UNLOCK(mbhc)			\
+{							\
+	pr_debug("%s: Release BCL\n", __func__);	\
+	mutex_unlock(&mbhc->codec_resource_lock);	\
+}
+
+#define WCD_MBHC_RSC_ASSERT_LOCKED(mbhc)		\
+{							\
+	WARN_ONCE(!mutex_is_locked(&mbhc->codec_resource_lock), \
+		  "%s: BCL should have acquired\n", __func__); \
+}
+
+/*
+ * Macros to update and read mbhc register bits. Check for
+ * "0" before updating or reading the register, because it
+ * is possible that one codec wants to write to that bit and
+ * other codec does not.
+ */
+#define WCD_MBHC_REG_UPDATE_BITS(function, val)         \
+do {                                                    \
+	if (mbhc->wcd_mbhc_regs[function].reg) {        \
+		snd_soc_update_bits(mbhc->codec,	\
+		mbhc->wcd_mbhc_regs[function].reg,	\
+		mbhc->wcd_mbhc_regs[function].mask,	\
+		val << (mbhc->wcd_mbhc_regs[function].offset)); \
+	}                                               \
+} while (0)
+
+#define WCD_MBHC_REG_READ(function, val)	        \
+do {                                                    \
+	if (mbhc->wcd_mbhc_regs[function].reg) {        \
+		val = (((snd_soc_read(mbhc->codec,	\
+		mbhc->wcd_mbhc_regs[function].reg)) &	\
+		(mbhc->wcd_mbhc_regs[function].mask)) >> \
+		(mbhc->wcd_mbhc_regs[function].offset)); \
+	} else {                                         \
+		val = -EINVAL;                           \
+	}                                                \
+} while (0)
+
+struct wcd_mbhc_cb {
+	int (*enable_mb_source)(struct wcd_mbhc *, bool);
+	void (*trim_btn_reg)(struct snd_soc_codec *);
+	void (*compute_impedance)(struct wcd_mbhc *, uint32_t *, uint32_t *);
+	void (*set_micbias_value)(struct snd_soc_codec *);
+	void (*set_auto_zeroing)(struct snd_soc_codec *, bool);
+	struct firmware_cal * (*get_hwdep_fw_cal)(struct wcd_mbhc *,
+			enum wcd_cal_type);
+	void (*set_cap_mode)(struct snd_soc_codec *, bool, bool);
+	int (*register_notifier)(struct wcd_mbhc *,
+				 struct notifier_block *nblock,
+				 bool enable);
+	int (*request_irq)(struct snd_soc_codec *,
+			int, irq_handler_t, const char *, void *);
+	void (*irq_control)(struct snd_soc_codec *,
+			int irq, bool enable);
+	int (*free_irq)(struct snd_soc_codec *,
+			int irq, void *);
+	void (*clk_setup)(struct snd_soc_codec *, bool);
+	int (*map_btn_code_to_num)(struct snd_soc_codec *);
+	bool (*lock_sleep)(struct wcd_mbhc *, bool);
+	bool (*micbias_enable_status)(struct wcd_mbhc *, int);
+	void (*mbhc_bias)(struct snd_soc_codec *, bool);
+	void (*mbhc_common_micb_ctrl)(struct snd_soc_codec *,
+				      int event, bool);
+	void (*micb_internal)(struct snd_soc_codec *,
+			int micb_num, bool);
+	bool (*hph_pa_on_status)(struct snd_soc_codec *);
+	void (*set_btn_thr)(struct snd_soc_codec *, s16 *, s16 *,
+			    int num_btn, bool);
+	void (*hph_pull_up_control)(struct snd_soc_codec *,
+				    enum mbhc_hs_pullup_iref);
+	int (*mbhc_micbias_control)(struct snd_soc_codec *, int, int req);
+	void (*mbhc_micb_ramp_control)(struct snd_soc_codec *, bool);
+	void (*skip_imped_detect)(struct snd_soc_codec *);
+	bool (*extn_use_mb)(struct snd_soc_codec *);
+	int (*mbhc_micb_ctrl_thr_mic)(struct snd_soc_codec *, int, bool);
+	void (*mbhc_gnd_det_ctrl)(struct snd_soc_codec *, bool);
+	void (*hph_pull_down_ctrl)(struct snd_soc_codec *, bool);
+	void (*mbhc_moisture_config)(struct wcd_mbhc *);
+	bool (*hph_register_recovery)(struct wcd_mbhc *);
+};
+
+struct wcd_mbhc {
+	/* Delayed work to report long button press */
+	struct delayed_work mbhc_btn_dwork;
+	int buttons_pressed;
+	struct wcd_mbhc_config *mbhc_cfg;
+	const struct wcd_mbhc_cb *mbhc_cb;
+
+	u32 hph_status; /* track headhpone status */
+	u8 hphlocp_cnt; /* headphone left ocp retry */
+	u8 hphrocp_cnt; /* headphone right ocp retry */
+
+	wait_queue_head_t wait_btn_press;
+	bool is_btn_press;
+	u8 current_plug;
+	bool in_swch_irq_handler;
+	bool hphl_swh; /*track HPHL switch NC / NO */
+	bool gnd_swh; /*track GND switch NC / NO */
+	u8 micbias1_cap_mode; /* track ext cap setting */
+	u8 micbias2_cap_mode; /* track ext cap setting */
+	bool hs_detect_work_stop;
+	bool micbias_enable;
+	bool btn_press_intr;
+	bool is_hs_recording;
+	bool is_extn_cable;
+	bool skip_imped_detection;
+	bool is_btn_already_regd;
+
+	struct snd_soc_codec *codec;
+	/* Work to perform MBHC Firmware Read */
+	struct delayed_work mbhc_firmware_dwork;
+	const struct firmware *mbhc_fw;
+	struct firmware_cal *mbhc_cal;
+
+	/* track PA/DAC state to sync with userspace */
+	unsigned long hph_pa_dac_state;
+	unsigned long event_state;
+	unsigned long jiffies_atreport;
+
+	/* impedance of hphl and hphr */
+	uint32_t zl, zr;
+	bool impedance_detect;
+
+	/* Holds type of Headset - Mono/Stereo */
+	enum wcd_mbhc_hph_type hph_type;
+
+	struct snd_soc_jack headset_jack;
+	struct snd_soc_jack button_jack;
+	struct mutex codec_resource_lock;
+
+	/* Holds codec specific interrupt mapping */
+	const struct wcd_mbhc_intr *intr_ids;
+
+	/* Work to correct accessory type */
+	struct work_struct correct_plug_swch;
+	struct notifier_block nblock;
+
+	struct wcd_mbhc_register *wcd_mbhc_regs;
+
+	struct completion btn_press_compl;
+	struct mutex hphl_pa_lock;
+	struct mutex hphr_pa_lock;
+
+	unsigned long intr_status;
+	bool is_hph_ocp_pending;
+};
+#define WCD_MBHC_CAL_SIZE(buttons, rload) ( \
+	sizeof(struct wcd_mbhc_general_cfg) + \
+	sizeof(struct wcd_mbhc_plug_detect_cfg) + \
+	((sizeof(s16) + sizeof(s16)) * buttons) + \
+	    sizeof(struct wcd_mbhc_plug_type_cfg) + \
+	sizeof(struct wcd_mbhc_btn_detect_cfg) + \
+	sizeof(struct wcd_mbhc_imped_detect_cfg) + \
+		((sizeof(u16) + sizeof(u16)) * rload) \
+	)
+
+
+#define WCD_MBHC_CAL_GENERAL_PTR(cali) ( \
+	(struct wcd_mbhc_general_cfg *) cali)
+#define WCD_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+	(struct wcd_mbhc_plug_detect_cfg *) \
+	&(WCD_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+	(struct wcd_mbhc_plug_type_cfg *) \
+	&(WCD_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD_MBHC_CAL_BTN_DET_PTR(cali) ( \
+	    (struct wcd_mbhc_btn_detect_cfg *) \
+	&(WCD_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+	(struct wcd_mbhc_imped_detect_cfg *) \
+	(((void *)&WCD_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+	(WCD_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+	(sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+	sizeof(WCD_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+	)
+
+#define WCD_MBHC_CAL_MIN_SIZE ( \
+	sizeof(struct wcd_mbhc_general_cfg) + \
+	sizeof(struct wcd_mbhc_plug_detect_cfg) + \
+	sizeof(struct wcd_mbhc_plug_type_cfg) + \
+	sizeof(struct wcd_mbhc_btn_detect_cfg) + \
+	sizeof(struct wcd_mbhc_imped_detect_cfg) + \
+	(sizeof(u16)*2)  \
+	)
+
+#define WCD_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+	sizeof(struct wcd_mbhc_btn_detect_cfg) + \
+	(cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+			sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD_MBHC_CAL_IMPED_MIN_SZ ( \
+	sizeof(struct wcd_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+	sizeof(struct wcd_mbhc_imped_detect_cfg) + \
+	(cfg_ptr->_n_rload * \
+	(sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+#ifdef CONFIG_SND_SOC_WCD_MBHC
+int wcd_mbhc_set_keycode(struct wcd_mbhc *mbhc);
+int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+		       struct wcd_mbhc_config *mbhc_cfg);
+void wcd_mbhc_stop(struct wcd_mbhc *mbhc);
+int wcd_mbhc_init(struct wcd_mbhc *mbhc, struct snd_soc_codec *codec,
+		      const struct wcd_mbhc_cb *mbhc_cb,
+		      const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+		      struct wcd_mbhc_register *mbhc_reg,
+		      bool impedance_det_en);
+int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+			   uint32_t *zr);
+void wcd_mbhc_deinit(struct wcd_mbhc *mbhc);
+#else
+static inline void wcd_mbhc_stop(struct wcd_mbhc *mbhc)
+{
+}
+static inline int wcd_mbhc_init(struct wcd_mbhc *mbhc,
+				struct snd_soc_codec *codec,
+				const struct wcd_mbhc_cb *mbhc_cb,
+				const struct wcd_mbhc_intr *mbhc_cdc_intr_ids,
+				struct wcd_mbhc_register *mbhc_reg,
+				bool impedance_det_en)
+{
+	return 0;
+}
+static inline int wcd_mbhc_start(struct wcd_mbhc *mbhc,
+				 struct wcd_mbhc_config *mbhc_cfg)
+{
+	return 0;
+}
+static inline int wcd_mbhc_get_impedance(struct wcd_mbhc *mbhc,
+					 uint32_t *zl,
+					 uint32_t *zr)
+{
+	*zl = 0;
+	*zr = 0;
+	return -EINVAL;
+}
+static inline void wcd_mbhc_deinit(struct wcd_mbhc *mbhc)
+{
+}
+#endif
+
+#endif /* __WCD_MBHC_V2_H__ */
diff --git a/sound/soc/codecs/wcd-spi-registers.h b/sound/soc/codecs/wcd-spi-registers.h
new file mode 100644
index 0000000..4e57969
--- /dev/null
+++ b/sound/soc/codecs/wcd-spi-registers.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD_SPI_REGISTERS_H__
+#define __WCD_SPI_REGISTERS_H__
+
+#include <linux/regmap.h>
+
+#define WCD_SPI_SLAVE_SANITY         (0x00)
+#define WCD_SPI_SLAVE_DEVICE_ID      (0x04)
+#define WCD_SPI_SLAVE_STATUS         (0x08)
+#define WCD_SPI_SLAVE_CONFIG         (0x0c)
+#define WCD_SPI_SLAVE_SW_RESET       (0x10)
+#define WCD_SPI_SLAVE_IRQ_STATUS     (0x14)
+#define WCD_SPI_SLAVE_IRQ_EN         (0x18)
+#define WCD_SPI_SLAVE_IRQ_CLR        (0x1c)
+#define WCD_SPI_SLAVE_IRQ_FORCE      (0x20)
+#define WCD_SPI_SLAVE_TX             (0x24)
+#define WCD_SPI_SLAVE_TEST_BUS_DATA  (0x2c)
+#define WCD_SPI_SLAVE_TEST_BUS_CTRL  (0x30)
+#define WCD_SPI_SLAVE_SW_RST_IRQ     (0x34)
+#define WCD_SPI_SLAVE_CHAR_CFG       (0x38)
+#define WCD_SPI_SLAVE_CHAR_DATA_MOSI (0x3c)
+#define WCD_SPI_SLAVE_CHAR_DATA_CS_N (0x40)
+#define WCD_SPI_SLAVE_CHAR_DATA_MISO (0x44)
+#define WCD_SPI_SLAVE_TRNS_BYTE_CNT  (0x4c)
+#define WCD_SPI_SLAVE_TRNS_LEN       (0x50)
+#define WCD_SPI_SLAVE_FIFO_LEVEL     (0x54)
+#define WCD_SPI_SLAVE_GENERICS       (0x58)
+#define WCD_SPI_SLAVE_EXT_BASE_ADDR  (0x5c)
+#define WCD_SPI_MAX_REGISTER         (0x5F)
+
+#endif /* End __WCD_SPI_REGISTERS_H__ */
diff --git a/sound/soc/codecs/wcd-spi.c b/sound/soc/codecs/wcd-spi.c
new file mode 100644
index 0000000..c08e746
--- /dev/null
+++ b/sound/soc/codecs/wcd-spi.c
@@ -0,0 +1,1351 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+#include <linux/component.h>
+#include <linux/ratelimit.h>
+#include <sound/wcd-dsp-mgr.h>
+#include <sound/wcd-spi.h>
+#include "wcd-spi-registers.h"
+
+/* Byte manipulations */
+#define SHIFT_1_BYTES    (8)
+#define SHIFT_2_BYTES    (16)
+#define SHIFT_3_BYTES    (24)
+
+/* Command opcodes */
+#define WCD_SPI_CMD_NOP     (0x00)
+#define WCD_SPI_CMD_WREN    (0x06)
+#define WCD_SPI_CMD_CLKREQ  (0xDA)
+#define WCD_SPI_CMD_RDSR    (0x05)
+#define WCD_SPI_CMD_IRR     (0x81)
+#define WCD_SPI_CMD_IRW     (0x82)
+#define WCD_SPI_CMD_MIOR    (0x83)
+#define WCD_SPI_CMD_FREAD   (0x0B)
+#define WCD_SPI_CMD_MIOW    (0x02)
+#define WCD_SPI_WRITE_FRAME_OPCODE \
+	(WCD_SPI_CMD_MIOW << SHIFT_3_BYTES)
+#define WCD_SPI_READ_FRAME_OPCODE \
+	(WCD_SPI_CMD_MIOR << SHIFT_3_BYTES)
+#define WCD_SPI_FREAD_FRAME_OPCODE \
+	(WCD_SPI_CMD_FREAD << SHIFT_3_BYTES)
+
+/* Command lengths */
+#define WCD_SPI_OPCODE_LEN       (0x01)
+#define WCD_SPI_CMD_NOP_LEN      (0x01)
+#define WCD_SPI_CMD_WREN_LEN     (0x01)
+#define WCD_SPI_CMD_CLKREQ_LEN   (0x04)
+#define WCD_SPI_CMD_IRR_LEN      (0x04)
+#define WCD_SPI_CMD_IRW_LEN      (0x06)
+#define WCD_SPI_WRITE_SINGLE_LEN (0x08)
+#define WCD_SPI_READ_SINGLE_LEN  (0x13)
+#define WCD_SPI_CMD_FREAD_LEN    (0x13)
+
+/* Command delays */
+#define WCD_SPI_CLKREQ_DELAY_USECS (500)
+#define WCD_SPI_CLK_OFF_TIMER_MS   (3000)
+
+/* Command masks */
+#define WCD_CMD_ADDR_MASK            \
+	(0xFF |                      \
+	 (0xFF << SHIFT_1_BYTES) |   \
+	 (0xFF << SHIFT_2_BYTES))
+
+/* Clock ctrl request related */
+#define WCD_SPI_CLK_ENABLE true
+#define WCD_SPI_CLK_DISABLE false
+#define WCD_SPI_CLK_FLAG_DELAYED    (1 << 0)
+#define WCD_SPI_CLK_FLAG_IMMEDIATE  (1 << 1)
+
+/* Internal addresses */
+#define WCD_SPI_ADDR_IPC_CTL_HOST (0x012014)
+
+/* Word sizes and min/max lengths */
+#define WCD_SPI_WORD_BYTE_CNT (4)
+#define WCD_SPI_RW_MULTI_MIN_LEN (16)
+
+/* Max size is closest multiple of 16 less than 64Kbytes */
+#define WCD_SPI_RW_MULTI_MAX_LEN ((64 * 1024) - 16)
+
+/* Alignment requirements */
+#define WCD_SPI_RW_MIN_ALIGN    WCD_SPI_WORD_BYTE_CNT
+#define WCD_SPI_RW_MULTI_ALIGN  (16)
+
+/* Status mask bits */
+#define WCD_SPI_CLK_STATE_ENABLED BIT(0)
+
+/* Locking related */
+#define WCD_SPI_MUTEX_LOCK(spi, lock)              \
+{                                                  \
+	dev_vdbg(&spi->dev, "%s: mutex_lock(%s)\n", \
+		 __func__, __stringify_1(lock));    \
+	mutex_lock(&lock);                         \
+}
+
+#define WCD_SPI_MUTEX_UNLOCK(spi, lock)              \
+{                                                    \
+	dev_vdbg(&spi->dev, "%s: mutex_unlock(%s)\n", \
+		 __func__, __stringify_1(lock));      \
+	mutex_unlock(&lock);                         \
+}
+
+struct wcd_spi_debug_data {
+	struct dentry *dir;
+	u32 addr;
+	u32 size;
+};
+
+struct wcd_spi_priv {
+	struct spi_device *spi;
+	u32 mem_base_addr;
+
+	struct regmap *regmap;
+
+	/* Message for single transfer */
+	struct spi_message msg1;
+	struct spi_transfer xfer1;
+
+	/* Message for two transfers */
+	struct spi_message msg2;
+	struct spi_transfer xfer2[2];
+
+	/* Register access related */
+	u32 reg_bytes;
+	u32 val_bytes;
+
+	/* Clock requests related */
+	struct mutex clk_mutex;
+	int clk_users;
+	unsigned long status_mask;
+	struct delayed_work clk_dwork;
+
+	/* Transaction related */
+	struct mutex xfer_mutex;
+
+	struct device *m_dev;
+	struct wdsp_mgr_ops *m_ops;
+
+	/* Debugfs related information */
+	struct wcd_spi_debug_data debug_data;
+};
+
+enum xfer_request {
+	WCD_SPI_XFER_WRITE,
+	WCD_SPI_XFER_READ,
+};
+
+
+static char *wcd_spi_xfer_req_str(enum xfer_request req)
+{
+	if (req == WCD_SPI_XFER_WRITE)
+		return "xfer_write";
+	else if (req == WCD_SPI_XFER_READ)
+		return "xfer_read";
+	else
+		return "xfer_invalid";
+}
+
+static void wcd_spi_reinit_xfer(struct spi_transfer *xfer)
+{
+	xfer->tx_buf = NULL;
+	xfer->rx_buf = NULL;
+	xfer->delay_usecs = 0;
+	xfer->len = 0;
+}
+
+static int wcd_spi_read_single(struct spi_device *spi,
+			       u32 remote_addr, u32 *val)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+	struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+	u8 *tx_buf;
+	u32 frame = 0;
+	int ret;
+
+	dev_dbg(&spi->dev, "%s: remote_addr = 0x%x\n",
+		__func__, remote_addr);
+
+	tx_buf = kzalloc(WCD_SPI_READ_SINGLE_LEN,
+			 GFP_KERNEL | GFP_DMA);
+	if (!tx_buf)
+		return -ENOMEM;
+
+	frame |= WCD_SPI_READ_FRAME_OPCODE;
+	frame |= remote_addr & WCD_CMD_ADDR_MASK;
+
+	wcd_spi_reinit_xfer(tx_xfer);
+	frame = cpu_to_be32(frame);
+	memcpy(tx_buf, &frame, sizeof(frame));
+	tx_xfer->tx_buf = tx_buf;
+	tx_xfer->len = WCD_SPI_READ_SINGLE_LEN;
+
+	wcd_spi_reinit_xfer(rx_xfer);
+	rx_xfer->rx_buf = val;
+	rx_xfer->len = sizeof(*val);
+
+	ret = spi_sync(spi, &wcd_spi->msg2);
+	kfree(tx_buf);
+
+	return ret;
+}
+
+static int wcd_spi_read_multi(struct spi_device *spi,
+			      u32 remote_addr, u8 *data,
+			      size_t len)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *xfer = &wcd_spi->xfer1;
+	u8 *tx_buf;
+	u8 *rx_buf;
+	u32 frame = 0;
+	int ret;
+
+	dev_dbg(&spi->dev,  "%s: addr 0x%x, len = %zd\n",
+		__func__, remote_addr, len);
+
+	frame |= WCD_SPI_FREAD_FRAME_OPCODE;
+	frame |= remote_addr & WCD_CMD_ADDR_MASK;
+
+	tx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len,
+			 GFP_KERNEL | GFP_DMA);
+	if (!tx_buf)
+		return -ENOMEM;
+
+	rx_buf = kzalloc(WCD_SPI_CMD_FREAD_LEN + len,
+			 GFP_KERNEL | GFP_DMA);
+	if (!rx_buf) {
+		kfree(tx_buf);
+		return -ENOMEM;
+	}
+
+	wcd_spi_reinit_xfer(xfer);
+	frame = cpu_to_be32(frame);
+	memcpy(tx_buf, &frame, sizeof(frame));
+	xfer->tx_buf = tx_buf;
+	xfer->rx_buf = rx_buf;
+	xfer->len = WCD_SPI_CMD_FREAD_LEN + len;
+
+	ret = spi_sync(spi, &wcd_spi->msg1);
+	if (ret) {
+		dev_err(&spi->dev, "%s: failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	memcpy(data, rx_buf + WCD_SPI_CMD_FREAD_LEN, len);
+done:
+	kfree(tx_buf);
+	kfree(rx_buf);
+	return ret;
+}
+
+static int wcd_spi_write_single(struct spi_device *spi,
+				u32 remote_addr, u32 val)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *xfer = &wcd_spi->xfer1;
+	u8 buf[WCD_SPI_WRITE_SINGLE_LEN];
+	u32 frame = 0;
+
+	dev_dbg(&spi->dev, "%s: remote_addr = 0x%x, val = 0x%x\n",
+		__func__, remote_addr, val);
+
+	memset(buf, 0, WCD_SPI_WRITE_SINGLE_LEN);
+	frame |= WCD_SPI_WRITE_FRAME_OPCODE;
+	frame |= (remote_addr & WCD_CMD_ADDR_MASK);
+
+	frame = cpu_to_be32(frame);
+	memcpy(buf, &frame, sizeof(frame));
+	memcpy(buf + sizeof(frame), &val, sizeof(val));
+
+	wcd_spi_reinit_xfer(xfer);
+	xfer->tx_buf = buf;
+	xfer->len = WCD_SPI_WRITE_SINGLE_LEN;
+
+	return spi_sync(spi, &wcd_spi->msg1);
+}
+
+static int wcd_spi_write_multi(struct spi_device *spi,
+			       u32 remote_addr, u8 *data,
+			       size_t len)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *xfer = &wcd_spi->xfer1;
+	u32 frame = 0;
+	u8 *tx_buf;
+	int xfer_len, ret;
+
+	dev_dbg(&spi->dev, "%s: addr = 0x%x len = %zd\n",
+		__func__, remote_addr, len);
+
+	frame |= WCD_SPI_WRITE_FRAME_OPCODE;
+	frame |= (remote_addr & WCD_CMD_ADDR_MASK);
+
+	frame = cpu_to_be32(frame);
+	xfer_len = len + sizeof(frame);
+
+	tx_buf = kzalloc(xfer_len, GFP_KERNEL);
+	if (!tx_buf)
+		return -ENOMEM;
+
+	memcpy(tx_buf, &frame, sizeof(frame));
+	memcpy(tx_buf + sizeof(frame), data, len);
+
+	wcd_spi_reinit_xfer(xfer);
+	xfer->tx_buf = tx_buf;
+	xfer->len = xfer_len;
+
+	ret = spi_sync(spi, &wcd_spi->msg1);
+	if (ret < 0)
+		dev_err(&spi->dev,
+			"%s: Failed, addr = 0x%x, len = %zd\n",
+			__func__, remote_addr, len);
+	kfree(tx_buf);
+
+	return ret;
+}
+
+static int wcd_spi_transfer_split(struct spi_device *spi,
+				  struct wcd_spi_msg *data_msg,
+				  enum xfer_request xfer_req)
+{
+	u32 addr = data_msg->remote_addr;
+	u8 *data = data_msg->data;
+	int remain_size = data_msg->len;
+	int to_xfer, loop_cnt, ret = 0;
+
+	/* Perform single writes until multi word alignment is met */
+	loop_cnt = 1;
+	while (remain_size &&
+	       !IS_ALIGNED(addr, WCD_SPI_RW_MULTI_ALIGN)) {
+		if (xfer_req == WCD_SPI_XFER_WRITE)
+			ret = wcd_spi_write_single(spi, addr,
+						   (*(u32 *)data));
+		else
+			ret = wcd_spi_read_single(spi, addr,
+						  (u32 *)data);
+		if (ret < 0) {
+			dev_err(&spi->dev,
+				"%s: %s fail iter(%d) start-word addr (0x%x)\n",
+				__func__, wcd_spi_xfer_req_str(xfer_req),
+				loop_cnt, addr);
+			goto done;
+		}
+
+		addr += WCD_SPI_WORD_BYTE_CNT;
+		data += WCD_SPI_WORD_BYTE_CNT;
+		remain_size -= WCD_SPI_WORD_BYTE_CNT;
+		loop_cnt++;
+	}
+
+	/* Perform multi writes for max allowed multi writes */
+	loop_cnt = 1;
+	while (remain_size >= WCD_SPI_RW_MULTI_MAX_LEN) {
+		if (xfer_req == WCD_SPI_XFER_WRITE)
+			ret = wcd_spi_write_multi(spi, addr, data,
+						  WCD_SPI_RW_MULTI_MAX_LEN);
+		else
+			ret = wcd_spi_read_multi(spi, addr, data,
+						 WCD_SPI_RW_MULTI_MAX_LEN);
+		if (ret < 0) {
+			dev_err(&spi->dev,
+				"%s: %s fail iter(%d) max-write addr (0x%x)\n",
+				__func__, wcd_spi_xfer_req_str(xfer_req),
+				loop_cnt, addr);
+			goto done;
+		}
+
+		addr += WCD_SPI_RW_MULTI_MAX_LEN;
+		data += WCD_SPI_RW_MULTI_MAX_LEN;
+		remain_size -= WCD_SPI_RW_MULTI_MAX_LEN;
+		loop_cnt++;
+	}
+
+	/*
+	 * Perform write for max possible data that is multiple
+	 * of the minimum size for multi-write commands.
+	 */
+	to_xfer = remain_size - (remain_size % WCD_SPI_RW_MULTI_MIN_LEN);
+	if (remain_size >= WCD_SPI_RW_MULTI_MIN_LEN &&
+	    to_xfer > 0) {
+		if (xfer_req == WCD_SPI_XFER_WRITE)
+			ret = wcd_spi_write_multi(spi, addr, data, to_xfer);
+		else
+			ret = wcd_spi_read_multi(spi, addr, data, to_xfer);
+		if (ret < 0) {
+			dev_err(&spi->dev,
+				"%s: %s fail write addr (0x%x), size (0x%x)\n",
+				__func__, wcd_spi_xfer_req_str(xfer_req),
+				addr, to_xfer);
+			goto done;
+		}
+
+		addr += to_xfer;
+		data += to_xfer;
+		remain_size -= to_xfer;
+	}
+
+	/* Perform single writes for the last remaining data */
+	loop_cnt = 1;
+	while (remain_size > 0) {
+		if (xfer_req == WCD_SPI_XFER_WRITE)
+			ret = wcd_spi_write_single(spi, addr, (*((u32 *)data)));
+		else
+			ret = wcd_spi_read_single(spi, addr,  (u32 *) data);
+		if (ret < 0) {
+			dev_err(&spi->dev,
+				"%s: %s fail iter(%d) end-write addr (0x%x)\n",
+				__func__, wcd_spi_xfer_req_str(xfer_req),
+				loop_cnt, addr);
+			goto done;
+		}
+
+		addr += WCD_SPI_WORD_BYTE_CNT;
+		data += WCD_SPI_WORD_BYTE_CNT;
+		remain_size -= WCD_SPI_WORD_BYTE_CNT;
+		loop_cnt++;
+	}
+
+done:
+	return ret;
+}
+
+static int wcd_spi_cmd_nop(struct spi_device *spi)
+{
+	u8 nop = WCD_SPI_CMD_NOP;
+
+	return spi_write(spi, &nop, WCD_SPI_CMD_NOP_LEN);
+}
+
+static int wcd_spi_cmd_clkreq(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *xfer = &wcd_spi->xfer1;
+	u8 cmd[WCD_SPI_CMD_CLKREQ_LEN] = {
+		WCD_SPI_CMD_CLKREQ,
+		0xBA, 0x80, 0x00};
+
+	wcd_spi_reinit_xfer(xfer);
+	xfer->tx_buf = cmd;
+	xfer->len = WCD_SPI_CMD_CLKREQ_LEN;
+	xfer->delay_usecs = WCD_SPI_CLKREQ_DELAY_USECS;
+
+	return spi_sync(spi, &wcd_spi->msg1);
+}
+
+static int wcd_spi_cmd_wr_en(struct spi_device *spi)
+{
+	u8 wr_en = WCD_SPI_CMD_WREN;
+
+	return spi_write(spi, &wr_en, WCD_SPI_CMD_WREN_LEN);
+}
+
+static int wcd_spi_cmd_rdsr(struct spi_device *spi,
+			    u32 *rdsr_status)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+	struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+	u8 rdsr_cmd;
+	u32 status;
+	int ret;
+
+	rdsr_cmd = WCD_SPI_CMD_RDSR;
+	wcd_spi_reinit_xfer(tx_xfer);
+	tx_xfer->tx_buf = &rdsr_cmd;
+	tx_xfer->len = sizeof(rdsr_cmd);
+
+
+	wcd_spi_reinit_xfer(rx_xfer);
+	rx_xfer->rx_buf = &status;
+	rx_xfer->len = sizeof(status);
+
+	ret = spi_sync(spi, &wcd_spi->msg2);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: RDSR failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	*rdsr_status = be32_to_cpu(status);
+
+	dev_dbg(&spi->dev, "%s: RDSR success, value = 0x%x\n",
+		 __func__, *rdsr_status);
+done:
+	return ret;
+}
+
+static int wcd_spi_clk_enable(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	int ret;
+	u32 rd_status;
+
+	ret = wcd_spi_cmd_nop(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: NOP1 failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ret = wcd_spi_cmd_clkreq(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: CLK_REQ failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ret = wcd_spi_cmd_nop(spi);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: NOP2 failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+	wcd_spi_cmd_rdsr(spi, &rd_status);
+	/*
+	 * Read status zero means reads are not
+	 * happenning on the bus, possibly because
+	 * clock request failed.
+	 */
+	if (rd_status) {
+		set_bit(WCD_SPI_CLK_STATE_ENABLED,
+			&wcd_spi->status_mask);
+	} else {
+		dev_err(&spi->dev, "%s: RDSR status is zero\n",
+			__func__);
+		ret = -EIO;
+	}
+done:
+	return ret;
+}
+
+static int wcd_spi_clk_disable(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	int ret;
+
+	ret = wcd_spi_write_single(spi, WCD_SPI_ADDR_IPC_CTL_HOST, 0x01);
+	if (ret < 0)
+		dev_err(&spi->dev, "%s: Failed, err = %d\n",
+			__func__, ret);
+	else
+		clear_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask);
+
+	return ret;
+}
+
+static int wcd_spi_clk_ctrl(struct spi_device *spi,
+			    bool request, u32 flags)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	int ret = 0;
+	const char *delay_str;
+
+	delay_str = (flags == WCD_SPI_CLK_FLAG_DELAYED) ?
+		    "delayed" : "immediate";
+
+	WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+
+	/* Reject any unbalanced disable request */
+	if (wcd_spi->clk_users < 0 ||
+	    (!request && wcd_spi->clk_users == 0)) {
+		dev_err(&spi->dev, "%s: Unbalanced clk_users %d for %s\n",
+			 __func__, wcd_spi->clk_users,
+			request ? "enable" : "disable");
+		ret = -EINVAL;
+
+		/* Reset the clk_users to 0 */
+		wcd_spi->clk_users = 0;
+
+		goto done;
+	}
+
+	if (request == WCD_SPI_CLK_ENABLE) {
+		/* Cancel the disable clk work */
+		WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+		cancel_delayed_work_sync(&wcd_spi->clk_dwork);
+		WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+
+		wcd_spi->clk_users++;
+
+		/*
+		 * If clk state is already set,
+		 * then clk wasnt really disabled
+		 */
+		if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+			goto done;
+		else if (wcd_spi->clk_users == 1)
+			ret = wcd_spi_clk_enable(spi);
+
+	} else {
+		wcd_spi->clk_users--;
+
+		/* Clock is still voted for */
+		if (wcd_spi->clk_users > 0)
+			goto done;
+
+		/*
+		 * If we are here, clk_users must be 0 and needs
+		 * to be disabled. Call the disable based on the
+		 * flags.
+		 */
+		if (flags == WCD_SPI_CLK_FLAG_DELAYED) {
+			schedule_delayed_work(&wcd_spi->clk_dwork,
+				msecs_to_jiffies(WCD_SPI_CLK_OFF_TIMER_MS));
+		} else {
+			ret = wcd_spi_clk_disable(spi);
+			if (ret < 0)
+				dev_err(&spi->dev,
+					"%s: Failed to disable clk err = %d\n",
+					__func__, ret);
+		}
+	}
+
+done:
+	dev_dbg(&spi->dev, "%s: updated clk_users = %d, request_%s %s\n",
+		__func__, wcd_spi->clk_users, request ? "enable" : "disable",
+		request ? "" : delay_str);
+	WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+
+	return ret;
+}
+
+static int wcd_spi_init(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	int ret;
+
+	ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+			       WCD_SPI_CLK_FLAG_IMMEDIATE);
+	if (ret < 0)
+		goto done;
+
+	ret = wcd_spi_cmd_wr_en(spi);
+	if (ret < 0)
+		goto err_wr_en;
+
+	/*
+	 * In case spi_init is called after component deinit,
+	 * it is possible hardware register state is also reset.
+	 * Sync the regcache here so hardware state is updated
+	 * to reflect the cache.
+	 */
+	regcache_sync(wcd_spi->regmap);
+
+	regmap_write(wcd_spi->regmap, WCD_SPI_SLAVE_CONFIG,
+		     0x0F3D0800);
+
+	/* Write the MTU to max allowed size */
+	regmap_update_bits(wcd_spi->regmap,
+			   WCD_SPI_SLAVE_TRNS_LEN,
+			   0xFFFF0000, 0xFFFF0000);
+err_wr_en:
+	wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+			 WCD_SPI_CLK_FLAG_IMMEDIATE);
+done:
+	return ret;
+}
+
+static void wcd_spi_clk_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd_spi_priv *wcd_spi;
+	struct spi_device *spi;
+	int ret;
+
+	dwork = to_delayed_work(work);
+	wcd_spi = container_of(dwork, struct wcd_spi_priv, clk_dwork);
+	spi = wcd_spi->spi;
+
+	WCD_SPI_MUTEX_LOCK(spi, wcd_spi->clk_mutex);
+	ret = wcd_spi_clk_disable(spi);
+	if (ret < 0)
+		dev_err(&spi->dev,
+			"%s: Failed to disable clk, err = %d\n",
+			__func__, ret);
+	WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->clk_mutex);
+}
+
+static int __wcd_spi_data_xfer(struct spi_device *spi,
+			       struct wcd_spi_msg *msg,
+			       enum xfer_request xfer_req)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	int ret;
+
+	/* Check for minimum alignment requirements */
+	if (!IS_ALIGNED(msg->remote_addr, WCD_SPI_RW_MIN_ALIGN)) {
+		dev_err(&spi->dev,
+			"%s addr 0x%x is not aligned to 0x%x\n",
+			__func__, msg->remote_addr, WCD_SPI_RW_MIN_ALIGN);
+		return -EINVAL;
+	} else if (msg->len % WCD_SPI_WORD_BYTE_CNT) {
+		dev_err(&spi->dev,
+			"%s len 0x%zx is not multiple of %d\n",
+			__func__, msg->len, WCD_SPI_WORD_BYTE_CNT);
+		return -EINVAL;
+	}
+
+	WCD_SPI_MUTEX_LOCK(spi, wcd_spi->xfer_mutex);
+	if (msg->len == WCD_SPI_WORD_BYTE_CNT) {
+		if (xfer_req == WCD_SPI_XFER_WRITE)
+			ret = wcd_spi_write_single(spi, msg->remote_addr,
+						   (*((u32 *)msg->data)));
+		else
+			ret = wcd_spi_read_single(spi, msg->remote_addr,
+						  (u32 *) msg->data);
+	} else {
+		ret = wcd_spi_transfer_split(spi, msg, xfer_req);
+	}
+	WCD_SPI_MUTEX_UNLOCK(spi, wcd_spi->xfer_mutex);
+
+	return ret;
+}
+
+static int wcd_spi_data_xfer(struct spi_device *spi,
+			     struct wcd_spi_msg *msg,
+			     enum xfer_request req)
+{
+	int ret, ret1;
+
+	if (msg->len <= 0) {
+		dev_err(&spi->dev, "%s: Invalid size %zd\n",
+			__func__, msg->len);
+		return -EINVAL;
+	}
+
+	/* Request for clock */
+	ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+			       WCD_SPI_CLK_FLAG_IMMEDIATE);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: clk enable failed %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	/* Perform the transaction */
+	ret = __wcd_spi_data_xfer(spi, msg, req);
+	if (ret < 0)
+		dev_err(&spi->dev,
+			"%s: Failed %s, addr = 0x%x, size = 0x%zx, err = %d\n",
+			__func__, wcd_spi_xfer_req_str(req),
+			msg->remote_addr, msg->len, ret);
+
+	/* Release the clock even if xfer failed */
+	ret1 = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+				WCD_SPI_CLK_FLAG_DELAYED);
+	if (ret1 < 0)
+		dev_err(&spi->dev, "%s: clk disable failed %d\n",
+			__func__, ret1);
+done:
+	return ret;
+}
+
+/*
+ * wcd_spi_data_write: Write data to WCD SPI
+ * @spi: spi_device struct
+ * @msg: msg that needs to be written to WCD
+ *
+ * This API writes length of data to address specified. These details
+ * about the write are encapsulated in @msg. Write size should be multiple
+ * of 4 bytes and write address should be 4-byte aligned.
+ */
+int wcd_spi_data_write(struct spi_device *spi,
+		       struct wcd_spi_msg *msg)
+{
+	if (!spi || !msg) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!spi) ? "spi device" : "msg");
+		return -EINVAL;
+	}
+
+	dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x, len = %zu\n",
+			    __func__, msg->remote_addr, msg->len);
+	return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_WRITE);
+}
+EXPORT_SYMBOL(wcd_spi_data_write);
+
+/*
+ * wcd_spi_data_read: Read data from WCD SPI
+ * @spi: spi_device struct
+ * @msg: msg that needs to be read from WCD
+ *
+ * This API reads length of data from address specified. These details
+ * about the read are encapsulated in @msg. Read size should be multiple
+ * of 4 bytes and read address should be 4-byte aligned.
+ */
+int wcd_spi_data_read(struct spi_device *spi,
+		      struct wcd_spi_msg *msg)
+{
+	if (!spi || !msg) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!spi) ? "spi device" : "msg");
+		return -EINVAL;
+	}
+
+	dev_dbg_ratelimited(&spi->dev, "%s: addr = 0x%x,len = %zu\n",
+			    __func__, msg->remote_addr, msg->len);
+	return wcd_spi_data_xfer(spi, msg, WCD_SPI_XFER_READ);
+}
+EXPORT_SYMBOL(wcd_spi_data_read);
+
+static int wdsp_spi_dload_section(struct spi_device *spi,
+				  void *data)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct wdsp_img_section *sec = data;
+	struct wcd_spi_msg msg;
+	int ret;
+
+	dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
+		__func__, sec->addr, sec->size);
+
+	msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
+	msg.data = sec->data;
+	msg.len = sec->size;
+
+	ret = __wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_WRITE);
+	if (ret < 0)
+		dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
+			__func__, msg.remote_addr, msg.len);
+	return ret;
+}
+
+static int wdsp_spi_read_section(struct spi_device *spi, void *data)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct wdsp_img_section *sec = data;
+	struct wcd_spi_msg msg;
+	int ret;
+
+	msg.remote_addr = sec->addr + wcd_spi->mem_base_addr;
+	msg.data = sec->data;
+	msg.len = sec->size;
+
+	dev_dbg(&spi->dev, "%s: addr = 0x%x, size = 0x%zx\n",
+		__func__, msg.remote_addr, msg.len);
+
+	ret = wcd_spi_data_xfer(spi, &msg, WCD_SPI_XFER_READ);
+	if (ret < 0)
+		dev_err(&spi->dev, "%s: fail addr (0x%x) size (0x%zx)\n",
+			__func__, msg.remote_addr, msg.len);
+	return ret;
+}
+
+static int wdsp_spi_event_handler(struct device *dev, void *priv_data,
+				  enum wdsp_event_type event,
+				  void *data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret = 0;
+
+	dev_dbg(&spi->dev, "%s: event type %d\n",
+		__func__, event);
+
+	switch (event) {
+	case WDSP_EVENT_PRE_DLOAD_CODE:
+	case WDSP_EVENT_PRE_DLOAD_DATA:
+		ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_ENABLE,
+				       WCD_SPI_CLK_FLAG_IMMEDIATE);
+		if (ret < 0)
+			dev_err(&spi->dev, "%s: clk_req failed %d\n",
+				__func__, ret);
+		break;
+
+	case WDSP_EVENT_POST_DLOAD_CODE:
+	case WDSP_EVENT_POST_DLOAD_DATA:
+	case WDSP_EVENT_DLOAD_FAILED:
+
+		ret = wcd_spi_clk_ctrl(spi, WCD_SPI_CLK_DISABLE,
+				       WCD_SPI_CLK_FLAG_IMMEDIATE);
+		if (ret < 0)
+			dev_err(&spi->dev, "%s: clk unvote failed %d\n",
+				__func__, ret);
+		break;
+
+	case WDSP_EVENT_DLOAD_SECTION:
+		ret = wdsp_spi_dload_section(spi, data);
+		break;
+
+	case WDSP_EVENT_READ_SECTION:
+		ret = wdsp_spi_read_section(spi, data);
+		break;
+
+	default:
+		dev_dbg(&spi->dev, "%s: Unhandled event %d\n",
+			__func__, event);
+		break;
+	}
+
+	return ret;
+}
+
+static int wcd_spi_bus_gwrite(void *context, const void *reg,
+			      size_t reg_len, const void *val,
+			      size_t val_len)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	u8 tx_buf[WCD_SPI_CMD_IRW_LEN];
+
+	if (!reg || !val || reg_len != wcd_spi->reg_bytes ||
+	    val_len != wcd_spi->val_bytes) {
+		dev_err(&spi->dev,
+			"%s: Invalid input, reg_len = %zd, val_len = %zd",
+			__func__, reg_len, val_len);
+		return -EINVAL;
+	}
+
+	tx_buf[0] = WCD_SPI_CMD_IRW;
+	tx_buf[1] = *((u8 *)reg);
+	memcpy(&tx_buf[WCD_SPI_OPCODE_LEN + reg_len],
+	       val, val_len);
+
+	return spi_write(spi, tx_buf, WCD_SPI_CMD_IRW_LEN);
+}
+
+static int wcd_spi_bus_write(void *context, const void *data,
+			     size_t count)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+	if (count < (wcd_spi->reg_bytes + wcd_spi->val_bytes)) {
+		dev_err(&spi->dev, "%s: Invalid size %zd\n",
+			__func__, count);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	return wcd_spi_bus_gwrite(context, data, wcd_spi->reg_bytes,
+				  data + wcd_spi->reg_bytes,
+				  count - wcd_spi->reg_bytes);
+}
+
+static int wcd_spi_bus_read(void *context, const void *reg,
+			    size_t reg_len, void *val,
+			    size_t val_len)
+{
+	struct device *dev = context;
+	struct spi_device *spi = to_spi_device(dev);
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct spi_transfer *tx_xfer = &wcd_spi->xfer2[0];
+	struct spi_transfer *rx_xfer = &wcd_spi->xfer2[1];
+	u8 tx_buf[WCD_SPI_CMD_IRR_LEN];
+
+	if (!reg || !val || reg_len != wcd_spi->reg_bytes ||
+	    val_len != wcd_spi->val_bytes) {
+		dev_err(&spi->dev,
+			"%s: Invalid input, reg_len = %zd, val_len = %zd",
+			__func__, reg_len, val_len);
+		return -EINVAL;
+	}
+
+	memset(tx_buf, 0, WCD_SPI_OPCODE_LEN);
+	tx_buf[0] = WCD_SPI_CMD_IRR;
+	tx_buf[1] = *((u8 *)reg);
+
+	wcd_spi_reinit_xfer(tx_xfer);
+	tx_xfer->tx_buf = tx_buf;
+	tx_xfer->rx_buf = NULL;
+	tx_xfer->len = WCD_SPI_CMD_IRR_LEN;
+
+	wcd_spi_reinit_xfer(rx_xfer);
+	rx_xfer->tx_buf = NULL;
+	rx_xfer->rx_buf = val;
+	rx_xfer->len = val_len;
+
+	return spi_sync(spi, &wcd_spi->msg2);
+}
+
+static struct regmap_bus wcd_spi_regmap_bus = {
+	.write = wcd_spi_bus_write,
+	.gather_write = wcd_spi_bus_gwrite,
+	.read = wcd_spi_bus_read,
+	.reg_format_endian_default = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int wcd_spi_state_show(struct seq_file *f, void *ptr)
+{
+	struct spi_device *spi = f->private;
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	const char *clk_state, *clk_mutex, *xfer_mutex;
+
+	if (test_bit(WCD_SPI_CLK_STATE_ENABLED, &wcd_spi->status_mask))
+		clk_state = "enabled";
+	else
+		clk_state = "disabled";
+
+	clk_mutex = mutex_is_locked(&wcd_spi->clk_mutex) ?
+		    "locked" : "unlocked";
+
+	xfer_mutex = mutex_is_locked(&wcd_spi->xfer_mutex) ?
+		     "locked" : "unlocked";
+
+	seq_printf(f, "clk_state = %s\nclk_users = %d\n"
+		   "clk_mutex = %s\nxfer_mutex = %s\n",
+		   clk_state, wcd_spi->clk_users, clk_mutex,
+		   xfer_mutex);
+	return 0;
+}
+
+static int wcd_spi_state_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, wcd_spi_state_show, inode->i_private);
+}
+
+static const struct file_operations state_fops = {
+	.open = wcd_spi_state_open,
+	.read = seq_read,
+	.llseek = seq_lseek,
+	.release = single_release,
+};
+
+static ssize_t wcd_spi_debugfs_mem_read(struct file *file, char __user *ubuf,
+					size_t count, loff_t *ppos)
+{
+	struct spi_device *spi = file->private_data;
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data;
+	struct wcd_spi_msg msg;
+	ssize_t buf_size, read_count = 0;
+	char *buf;
+	int ret;
+
+	if (*ppos < 0 || !count)
+		return -EINVAL;
+
+	if (dbg_data->size == 0 || dbg_data->addr == 0) {
+		dev_err(&spi->dev,
+			"%s: Invalid request, size = %u, addr = 0x%x\n",
+			__func__, dbg_data->size, dbg_data->addr);
+		return 0;
+	}
+
+	buf_size = count < dbg_data->size ? count : dbg_data->size;
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	msg.data = buf;
+	msg.remote_addr = dbg_data->addr;
+	msg.len = buf_size;
+	msg.flags = 0;
+
+	ret = wcd_spi_data_read(spi, &msg);
+	if (ret < 0) {
+		dev_err(&spi->dev,
+			"%s: Failed to read %zu bytes from addr 0x%x\n",
+			__func__, buf_size, msg.remote_addr);
+		goto done;
+	}
+
+	read_count = simple_read_from_buffer(ubuf, count, ppos, buf, buf_size);
+
+done:
+	kfree(buf);
+	if (ret < 0)
+		return ret;
+	else
+		return read_count;
+}
+
+static const struct file_operations mem_read_fops = {
+	.open = simple_open,
+	.read = wcd_spi_debugfs_mem_read,
+};
+
+static int wcd_spi_debugfs_init(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	struct wcd_spi_debug_data *dbg_data = &wcd_spi->debug_data;
+	int rc = 0;
+
+	dbg_data->dir = debugfs_create_dir("wcd_spi", NULL);
+	if (IS_ERR_OR_NULL(dbg_data->dir)) {
+		dbg_data->dir = NULL;
+		rc = -ENODEV;
+		goto done;
+	}
+
+	debugfs_create_file("state", 0444, dbg_data->dir, spi, &state_fops);
+	debugfs_create_u32("addr", 0644, dbg_data->dir,
+			   &dbg_data->addr);
+	debugfs_create_u32("size", 0644, dbg_data->dir,
+			   &dbg_data->size);
+
+	debugfs_create_file("mem_read", 0444, dbg_data->dir,
+			    spi, &mem_read_fops);
+done:
+	return rc;
+}
+
+
+static const struct reg_default wcd_spi_defaults[] = {
+	{WCD_SPI_SLAVE_SANITY, 0xDEADBEEF},
+	{WCD_SPI_SLAVE_DEVICE_ID, 0x00500000},
+	{WCD_SPI_SLAVE_STATUS, 0x80100000},
+	{WCD_SPI_SLAVE_CONFIG, 0x0F200808},
+	{WCD_SPI_SLAVE_SW_RESET, 0x00000000},
+	{WCD_SPI_SLAVE_IRQ_STATUS, 0x00000000},
+	{WCD_SPI_SLAVE_IRQ_EN, 0x00000000},
+	{WCD_SPI_SLAVE_IRQ_CLR, 0x00000000},
+	{WCD_SPI_SLAVE_IRQ_FORCE, 0x00000000},
+	{WCD_SPI_SLAVE_TX, 0x00000000},
+	{WCD_SPI_SLAVE_TEST_BUS_DATA, 0x00000000},
+	{WCD_SPI_SLAVE_TEST_BUS_CTRL, 0x00000000},
+	{WCD_SPI_SLAVE_SW_RST_IRQ, 0x00000000},
+	{WCD_SPI_SLAVE_CHAR_CFG, 0x00000000},
+	{WCD_SPI_SLAVE_CHAR_DATA_MOSI, 0x00000000},
+	{WCD_SPI_SLAVE_CHAR_DATA_CS_N, 0x00000000},
+	{WCD_SPI_SLAVE_CHAR_DATA_MISO, 0x00000000},
+	{WCD_SPI_SLAVE_TRNS_BYTE_CNT, 0x00000000},
+	{WCD_SPI_SLAVE_TRNS_LEN, 0x00000000},
+	{WCD_SPI_SLAVE_FIFO_LEVEL, 0x00000000},
+	{WCD_SPI_SLAVE_GENERICS, 0x80000000},
+	{WCD_SPI_SLAVE_EXT_BASE_ADDR, 0x00000000},
+};
+
+static bool wcd_spi_is_volatile_reg(struct device *dev,
+				    unsigned int reg)
+{
+	switch (reg) {
+	case WCD_SPI_SLAVE_SANITY:
+	case WCD_SPI_SLAVE_STATUS:
+	case WCD_SPI_SLAVE_IRQ_STATUS:
+	case WCD_SPI_SLAVE_TX:
+	case WCD_SPI_SLAVE_SW_RST_IRQ:
+	case WCD_SPI_SLAVE_TRNS_BYTE_CNT:
+	case WCD_SPI_SLAVE_FIFO_LEVEL:
+	case WCD_SPI_SLAVE_GENERICS:
+		return true;
+	}
+
+	return false;
+}
+
+static bool wcd_spi_is_readable_reg(struct device *dev,
+				    unsigned int reg)
+{
+	switch (reg) {
+	case WCD_SPI_SLAVE_SW_RESET:
+	case WCD_SPI_SLAVE_IRQ_CLR:
+	case WCD_SPI_SLAVE_IRQ_FORCE:
+		return false;
+	}
+
+	return true;
+}
+
+static struct regmap_config wcd_spi_regmap_cfg = {
+	.reg_bits = 8,
+	.val_bits = 32,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = wcd_spi_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wcd_spi_defaults),
+	.max_register = WCD_SPI_MAX_REGISTER,
+	.volatile_reg = wcd_spi_is_volatile_reg,
+	.readable_reg = wcd_spi_is_readable_reg,
+};
+
+static int wdsp_spi_init(struct device *dev, void *priv_data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	int ret;
+
+	ret = wcd_spi_init(spi);
+	if (ret < 0)
+		dev_err(&spi->dev, "%s: Init failed, err = %d\n",
+			__func__, ret);
+	return ret;
+}
+
+static int wdsp_spi_deinit(struct device *dev, void *priv_data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+	/*
+	 * Deinit means the hardware is reset. Mark the cache
+	 * as dirty here, so init will sync the cache
+	 */
+	regcache_mark_dirty(wcd_spi->regmap);
+
+	return 0;
+}
+
+static struct wdsp_cmpnt_ops wdsp_spi_ops = {
+	.init = wdsp_spi_init,
+	.deinit = wdsp_spi_deinit,
+	.event_handler = wdsp_spi_event_handler,
+};
+
+static int wcd_spi_component_bind(struct device *dev,
+				  struct device *master,
+				  void *data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+	int ret = 0;
+
+	wcd_spi->m_dev = master;
+	wcd_spi->m_ops = data;
+
+	if (wcd_spi->m_ops &&
+	    wcd_spi->m_ops->register_cmpnt_ops)
+		ret = wcd_spi->m_ops->register_cmpnt_ops(master, dev,
+							 wcd_spi,
+							 &wdsp_spi_ops);
+	if (ret) {
+		dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	wcd_spi->reg_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.reg_bits, 8);
+	wcd_spi->val_bytes = DIV_ROUND_UP(wcd_spi_regmap_cfg.val_bits, 8);
+
+	wcd_spi->regmap = devm_regmap_init(&spi->dev, &wcd_spi_regmap_bus,
+					   &spi->dev, &wcd_spi_regmap_cfg);
+	if (IS_ERR(wcd_spi->regmap)) {
+		ret = PTR_ERR(wcd_spi->regmap);
+		dev_err(&spi->dev, "%s: Failed to allocate regmap, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	if (wcd_spi_debugfs_init(spi))
+		dev_err(&spi->dev, "%s: Failed debugfs init\n", __func__);
+
+	spi_message_init(&wcd_spi->msg1);
+	spi_message_add_tail(&wcd_spi->xfer1, &wcd_spi->msg1);
+
+	spi_message_init(&wcd_spi->msg2);
+	spi_message_add_tail(&wcd_spi->xfer2[0], &wcd_spi->msg2);
+	spi_message_add_tail(&wcd_spi->xfer2[1], &wcd_spi->msg2);
+done:
+	return ret;
+}
+
+static void wcd_spi_component_unbind(struct device *dev,
+				     struct device *master,
+				     void *data)
+{
+	struct spi_device *spi = to_spi_device(dev);
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+	wcd_spi->m_dev = NULL;
+	wcd_spi->m_ops = NULL;
+
+	spi_transfer_del(&wcd_spi->xfer1);
+	spi_transfer_del(&wcd_spi->xfer2[0]);
+	spi_transfer_del(&wcd_spi->xfer2[1]);
+}
+
+static const struct component_ops wcd_spi_component_ops = {
+	.bind = wcd_spi_component_bind,
+	.unbind = wcd_spi_component_unbind,
+};
+
+static int wcd_spi_probe(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi;
+	int ret = 0;
+
+	wcd_spi = devm_kzalloc(&spi->dev, sizeof(*wcd_spi),
+			       GFP_KERNEL);
+	if (!wcd_spi)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(spi->dev.of_node,
+				   "qcom,mem-base-addr",
+				   &wcd_spi->mem_base_addr);
+	if (ret < 0) {
+		dev_err(&spi->dev, "%s: Missing %s DT entry",
+			__func__, "qcom,mem-base-addr");
+		goto err_ret;
+	}
+
+	dev_dbg(&spi->dev,
+		"%s: mem_base_addr 0x%x\n", __func__, wcd_spi->mem_base_addr);
+
+	mutex_init(&wcd_spi->clk_mutex);
+	mutex_init(&wcd_spi->xfer_mutex);
+	INIT_DELAYED_WORK(&wcd_spi->clk_dwork, wcd_spi_clk_work);
+
+	wcd_spi->spi = spi;
+	spi_set_drvdata(spi, wcd_spi);
+
+	ret = component_add(&spi->dev, &wcd_spi_component_ops);
+	if (ret) {
+		dev_err(&spi->dev, "%s: component_add failed err = %d\n",
+			__func__, ret);
+		goto err_component_add;
+	}
+
+	return ret;
+
+err_component_add:
+	mutex_destroy(&wcd_spi->clk_mutex);
+	mutex_destroy(&wcd_spi->xfer_mutex);
+err_ret:
+	devm_kfree(&spi->dev, wcd_spi);
+	spi_set_drvdata(spi, NULL);
+	return ret;
+}
+
+static int wcd_spi_remove(struct spi_device *spi)
+{
+	struct wcd_spi_priv *wcd_spi = spi_get_drvdata(spi);
+
+	component_del(&spi->dev, &wcd_spi_component_ops);
+
+	mutex_destroy(&wcd_spi->clk_mutex);
+	mutex_destroy(&wcd_spi->xfer_mutex);
+
+	devm_kfree(&spi->dev, wcd_spi);
+	spi_set_drvdata(spi, NULL);
+
+	return 0;
+}
+
+static const struct of_device_id wcd_spi_of_match[] = {
+	{ .compatible = "qcom,wcd-spi-v2", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, wcd_spi_of_match);
+
+static struct spi_driver wcd_spi_driver = {
+	.driver = {
+		.name = "wcd-spi-v2",
+		.of_match_table = wcd_spi_of_match,
+	},
+	.probe = wcd_spi_probe,
+	.remove = wcd_spi_remove,
+};
+
+module_spi_driver(wcd_spi_driver);
+
+MODULE_DESCRIPTION("WCD SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9330-tables.c b/sound/soc/codecs/wcd9330-tables.c
new file mode 100644
index 0000000..1866fb3
--- /dev/null
+++ b/sound/soc/codecs/wcd9330-tables.c
@@ -0,0 +1,1675 @@
+/* Copyright (c) 2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include "wcd9330.h"
+
+const u8 tomtom_reg_readable[WCD9330_MAX_REGISTER + 1] = {
+	[TOMTOM_A_CHIP_CTL] = 1,
+	[TOMTOM_A_CHIP_STATUS] = 1,
+	[TOMTOM_A_CHIP_ID_BYTE_0] = 1,
+	[TOMTOM_A_CHIP_ID_BYTE_1] = 1,
+	[TOMTOM_A_CHIP_ID_BYTE_2] = 1,
+	[TOMTOM_A_CHIP_ID_BYTE_3] = 1,
+	[TOMTOM_A_CHIP_I2C_SLAVE_ID] = 1,
+	[TOMTOM_A_SLAVE_ID_1] = 1,
+	[TOMTOM_A_SLAVE_ID_2] = 1,
+	[TOMTOM_A_SLAVE_ID_3] = 1,
+	[TOMTOM_A_PIN_CTL_OE0] = 1,
+	[TOMTOM_A_PIN_CTL_OE1] = 1,
+	[TOMTOM_A_PIN_CTL_OE2] = 1,
+	[TOMTOM_A_PIN_CTL_DATA0] = 1,
+	[TOMTOM_A_PIN_CTL_DATA1] = 1,
+	[TOMTOM_A_PIN_CTL_DATA2] = 1,
+	[TOMTOM_A_HDRIVE_GENERIC] = 1,
+	[TOMTOM_A_HDRIVE_OVERRIDE] = 1,
+	[TOMTOM_A_ANA_CSR_WAIT_STATE] = 1,
+	[TOMTOM_A_PROCESS_MONITOR_CTL0] = 1,
+	[TOMTOM_A_PROCESS_MONITOR_CTL1] = 1,
+	[TOMTOM_A_PROCESS_MONITOR_CTL2] = 1,
+	[TOMTOM_A_PROCESS_MONITOR_CTL3] = 1,
+	[TOMTOM_A_QFUSE_CTL] = 1,
+	[TOMTOM_A_QFUSE_STATUS] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT0] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT1] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT2] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT3] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT4] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT5] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT6] = 1,
+	[TOMTOM_A_QFUSE_DATA_OUT7] = 1,
+	[TOMTOM_A_CDC_CTL] = 1,
+	[TOMTOM_A_LEAKAGE_CTL] = 1,
+	[TOMTOM_A_SVASS_MEM_PTR0] = 1,
+	[TOMTOM_A_SVASS_MEM_PTR1] = 1,
+	[TOMTOM_A_SVASS_MEM_PTR2] = 1,
+	[TOMTOM_A_SVASS_MEM_CTL] = 1,
+	[TOMTOM_A_SVASS_MEM_BANK] = 1,
+	[TOMTOM_A_DMIC_B1_CTL] = 1,
+	[TOMTOM_A_DMIC_B2_CTL] = 1,
+	[TOMTOM_A_SVASS_CLKRST_CTL] = 1,
+	[TOMTOM_A_SVASS_CPAR_CFG] = 1,
+	[TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD] = 1,
+	[TOMTOM_A_SVASS_CPAR_WDOG_CFG] = 1,
+	[TOMTOM_A_SVASS_CFG] = 1,
+	[TOMTOM_A_SVASS_SPE_CFG] = 1,
+	[TOMTOM_A_SVASS_STATUS] = 1,
+	[TOMTOM_A_SVASS_INT_MASK] = 1,
+	[TOMTOM_A_SVASS_INT_STATUS] = 1,
+	[TOMTOM_A_SVASS_INT_CLR] = 0,
+	[TOMTOM_A_SVASS_DEBUG] = 1,
+	[TOMTOM_A_SVASS_SPE_BKUP_INT] = 0,
+	[TOMTOM_A_SVASS_MEM_ACC] = 1,
+	[TOMTOM_A_MEM_LEAKAGE_CTL] = 1,
+	[TOMTOM_A_SVASS_SPE_INBOX_TRG] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_0] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_1] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_2] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_3] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_4] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_5] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_6] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_7] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_8] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_9] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_10] = 0,
+	[TOMTOM_A_SVASS_SPE_INBOX_11] = 0,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_0] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_1] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_2] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_3] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_4] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_5] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_6] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_7] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_8] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_9] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_10] = 1,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_11] = 1,
+	[TOMTOM_A_INTR_MODE] = 1,
+	[TOMTOM_A_INTR1_MASK0] = 1,
+	[TOMTOM_A_INTR1_MASK1] = 1,
+	[TOMTOM_A_INTR1_MASK2] = 1,
+	[TOMTOM_A_INTR1_MASK3] = 1,
+	[TOMTOM_A_INTR1_STATUS0] = 1,
+	[TOMTOM_A_INTR1_STATUS1] = 1,
+	[TOMTOM_A_INTR1_STATUS2] = 1,
+	[TOMTOM_A_INTR1_STATUS3] = 1,
+	[TOMTOM_A_INTR1_CLEAR0] = 0,
+	[TOMTOM_A_INTR1_CLEAR1] = 0,
+	[TOMTOM_A_INTR1_CLEAR2] = 0,
+	[TOMTOM_A_INTR1_CLEAR3] = 0,
+	[TOMTOM_A_INTR1_LEVEL0] = 1,
+	[TOMTOM_A_INTR1_LEVEL1] = 1,
+	[TOMTOM_A_INTR1_LEVEL2] = 1,
+	[TOMTOM_A_INTR1_LEVEL3] = 1,
+	[TOMTOM_A_INTR1_TEST0] = 1,
+	[TOMTOM_A_INTR1_TEST1] = 1,
+	[TOMTOM_A_INTR1_TEST2] = 1,
+	[TOMTOM_A_INTR1_TEST3] = 1,
+	[TOMTOM_A_INTR1_SET0] = 1,
+	[TOMTOM_A_INTR1_SET1] = 1,
+	[TOMTOM_A_INTR1_SET2] = 1,
+	[TOMTOM_A_INTR1_SET3] = 1,
+	[TOMTOM_A_INTR2_MASK0] = 1,
+	[TOMTOM_A_INTR2_STATUS0] = 1,
+	[TOMTOM_A_INTR2_CLEAR0] = 0,
+	[TOMTOM_A_INTR2_LEVEL0] = 1,
+	[TOMTOM_A_INTR2_TEST0] = 1,
+	[TOMTOM_A_INTR2_SET0] = 1,
+	[TOMTOM_A_CDC_TX_I2S_SCK_MODE] = 1,
+	[TOMTOM_A_CDC_TX_I2S_WS_MODE] = 1,
+	[TOMTOM_A_CDC_DMIC_DATA0_MODE] = 1,
+	[TOMTOM_A_CDC_DMIC_CLK0_MODE] = 1,
+	[TOMTOM_A_CDC_DMIC_DATA1_MODE] = 1,
+	[TOMTOM_A_CDC_DMIC_CLK1_MODE] = 1,
+	[TOMTOM_A_CDC_RX_I2S_SCK_MODE] = 1,
+	[TOMTOM_A_CDC_RX_I2S_WS_MODE] = 1,
+	[TOMTOM_A_CDC_DMIC_DATA2_MODE] = 1,
+	[TOMTOM_A_CDC_DMIC_CLK2_MODE] = 1,
+	[TOMTOM_A_CDC_INTR1_MODE] = 1,
+	[TOMTOM_A_CDC_SB_NRZ_SEL_MODE] = 1,
+	[TOMTOM_A_CDC_INTR2_MODE] = 1,
+	[TOMTOM_A_CDC_RF_PA_ON_MODE] = 1,
+	[TOMTOM_A_CDC_BOOST_MODE] = 1,
+	[TOMTOM_A_CDC_JTCK_MODE] = 1,
+	[TOMTOM_A_CDC_JTDI_MODE] = 1,
+	[TOMTOM_A_CDC_JTMS_MODE] = 1,
+	[TOMTOM_A_CDC_JTDO_MODE] = 1,
+	[TOMTOM_A_CDC_JTRST_MODE] = 1,
+	[TOMTOM_A_CDC_BIST_MODE_MODE] = 1,
+	[TOMTOM_A_CDC_MAD_MAIN_CTL_1] = 1,
+	[TOMTOM_A_CDC_MAD_MAIN_CTL_2] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_1] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_2] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_3] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_4] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_5] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_6] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_7] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_8] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR] = 1,
+	[TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_1] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_2] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_3] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_4] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_5] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_6] = 1,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_7] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_1] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_2] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_3] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_4] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_5] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_6] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_7] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_8] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR] = 1,
+	[TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL] = 1,
+	[TOMTOM_A_CDC_MAD_INP_SEL] = 1,
+	[TOMTOM_A_BIAS_REF_CTL] = 1,
+	[TOMTOM_A_BIAS_CENTRAL_BG_CTL] = 1,
+	[TOMTOM_A_BIAS_PRECHRG_CTL] = 1,
+	[TOMTOM_A_BIAS_CURR_CTL_1] = 1,
+	[TOMTOM_A_BIAS_CURR_CTL_2] = 1,
+	[TOMTOM_A_BIAS_OSC_BG_CTL] = 1,
+	[TOMTOM_A_CLK_BUFF_EN1] = 1,
+	[TOMTOM_A_CLK_BUFF_EN2] = 1,
+	[TOMTOM_A_LDO_L_MODE_1] = 1,
+	[TOMTOM_A_LDO_L_MODE_2] = 1,
+	[TOMTOM_A_LDO_L_CTRL_1] = 1,
+	[TOMTOM_A_LDO_L_CTRL_2] = 1,
+	[TOMTOM_A_LDO_L_CTRL_3] = 1,
+	[TOMTOM_A_LDO_L_CTRL_4] = 1,
+	[TOMTOM_A_LDO_H_MODE_1] = 1,
+	[TOMTOM_A_LDO_H_MODE_2] = 1,
+	[TOMTOM_A_LDO_H_LOOP_CTL] = 1,
+	[TOMTOM_A_LDO_H_COMP_1] = 1,
+	[TOMTOM_A_LDO_H_COMP_2] = 1,
+	[TOMTOM_A_LDO_H_BIAS_1] = 1,
+	[TOMTOM_A_LDO_H_BIAS_2] = 1,
+	[TOMTOM_A_LDO_H_BIAS_3] = 1,
+	[TOMTOM_A_VBAT_CLK] = 1,
+	[TOMTOM_A_VBAT_LOOP] = 1,
+	[TOMTOM_A_VBAT_REF] = 1,
+	[TOMTOM_A_VBAT_ADC_TEST] = 1,
+	[TOMTOM_A_VBAT_FE] = 1,
+	[TOMTOM_A_VBAT_BIAS_1] = 1,
+	[TOMTOM_A_VBAT_BIAS_2] = 1,
+	[TOMTOM_A_VBAT_ADC_DATA_MSB] = 1,
+	[TOMTOM_A_VBAT_ADC_DATA_LSB] = 1,
+	[TOMTOM_A_FLL_NREF] = 1,
+	[TOMTOM_A_FLL_KDCO_TUNE] = 1,
+	[TOMTOM_A_FLL_LOCK_THRESH] = 1,
+	[TOMTOM_A_FLL_LOCK_DET_COUNT] = 1,
+	[TOMTOM_A_FLL_DAC_THRESHOLD] = 1,
+	[TOMTOM_A_FLL_TEST_DCO_FREERUN] = 1,
+	[TOMTOM_A_FLL_TEST_ENABLE] = 1,
+	[TOMTOM_A_MICB_CFILT_1_CTL] = 1,
+	[TOMTOM_A_MICB_CFILT_1_VAL] = 1,
+	[TOMTOM_A_MICB_CFILT_1_PRECHRG] = 1,
+	[TOMTOM_A_MICB_1_CTL] = 1,
+	[TOMTOM_A_MICB_1_INT_RBIAS] = 1,
+	[TOMTOM_A_MICB_1_MBHC] = 1,
+	[TOMTOM_A_MICB_CFILT_2_CTL] = 1,
+	[TOMTOM_A_MICB_CFILT_2_VAL] = 1,
+	[TOMTOM_A_MICB_CFILT_2_PRECHRG] = 1,
+	[TOMTOM_A_MICB_2_CTL] = 1,
+	[TOMTOM_A_MICB_2_INT_RBIAS] = 1,
+	[TOMTOM_A_MICB_2_MBHC] = 1,
+	[TOMTOM_A_MICB_CFILT_3_CTL] = 1,
+	[TOMTOM_A_MICB_CFILT_3_VAL] = 1,
+	[TOMTOM_A_MICB_CFILT_3_PRECHRG] = 1,
+	[TOMTOM_A_MICB_3_CTL] = 1,
+	[TOMTOM_A_MICB_3_INT_RBIAS] = 1,
+	[TOMTOM_A_MICB_3_MBHC] = 1,
+	[TOMTOM_A_MICB_4_CTL] = 1,
+	[TOMTOM_A_MICB_4_INT_RBIAS] = 1,
+	[TOMTOM_A_MICB_4_MBHC] = 1,
+	[TOMTOM_A_SPKR_DRV2_EN] = 1,
+	[TOMTOM_A_SPKR_DRV2_GAIN] = 1,
+	[TOMTOM_A_SPKR_DRV2_DAC_CTL] = 1,
+	[TOMTOM_A_SPKR_DRV2_OCP_CTL] = 1,
+	[TOMTOM_A_SPKR_DRV2_CLIP_DET] = 1,
+	[TOMTOM_A_SPKR_DRV2_DBG_DAC] = 1,
+	[TOMTOM_A_SPKR_DRV2_DBG_PA] = 1,
+	[TOMTOM_A_SPKR_DRV2_DBG_PWRSTG] = 1,
+	[TOMTOM_A_SPKR_DRV2_BIAS_LDO] = 1,
+	[TOMTOM_A_SPKR_DRV2_BIAS_INT] = 1,
+	[TOMTOM_A_SPKR_DRV2_BIAS_PA] = 1,
+	[TOMTOM_A_SPKR_DRV2_STATUS_OCP] = 1,
+	[TOMTOM_A_SPKR_DRV2_STATUS_PA] = 1,
+	[TOMTOM_A_MBHC_INSERT_DETECT] = 1,
+	[TOMTOM_A_MBHC_INSERT_DET_STATUS] = 1,
+	[TOMTOM_A_TX_COM_BIAS] = 1,
+	[TOMTOM_A_MBHC_INSERT_DETECT2] = 1,
+	[TOMTOM_A_MBHC_SCALING_MUX_1] = 1,
+	[TOMTOM_A_MBHC_SCALING_MUX_2] = 1,
+	[TOMTOM_A_MAD_ANA_CTRL] = 1,
+	[TOMTOM_A_TX_SUP_SWITCH_CTRL_1] = 1,
+	[TOMTOM_A_TX_SUP_SWITCH_CTRL_2] = 1,
+	[TOMTOM_A_TX_1_GAIN] = 1,
+	[TOMTOM_A_TX_1_2_TEST_EN] = 1,
+	[TOMTOM_A_TX_2_GAIN] = 1,
+	[TOMTOM_A_TX_1_2_ADC_IB] = 1,
+	[TOMTOM_A_TX_1_2_ATEST_REFCTRL] = 1,
+	[TOMTOM_A_TX_1_2_TEST_CTL] = 1,
+	[TOMTOM_A_TX_1_2_TEST_BLOCK_EN] = 1,
+	[TOMTOM_A_TX_1_2_TXFE_CLKDIV] = 1,
+	[TOMTOM_A_TX_1_2_SAR_ERR_CH1] = 1,
+	[TOMTOM_A_TX_1_2_SAR_ERR_CH2] = 1,
+	[TOMTOM_A_TX_3_GAIN] = 1,
+	[TOMTOM_A_TX_3_4_TEST_EN] = 1,
+	[TOMTOM_A_TX_4_GAIN] = 1,
+	[TOMTOM_A_TX_3_4_ADC_IB] = 1,
+	[TOMTOM_A_TX_3_4_ATEST_REFCTRL] = 1,
+	[TOMTOM_A_TX_3_4_TEST_CTL] = 1,
+	[TOMTOM_A_TX_3_4_TEST_BLOCK_EN] = 1,
+	[TOMTOM_A_TX_3_4_TXFE_CKDIV] = 1,
+	[TOMTOM_A_TX_3_4_SAR_ERR_CH3] = 1,
+	[TOMTOM_A_TX_3_4_SAR_ERR_CH4] = 1,
+	[TOMTOM_A_TX_5_GAIN] = 1,
+	[TOMTOM_A_TX_5_6_TEST_EN] = 1,
+	[TOMTOM_A_TX_6_GAIN] = 1,
+	[TOMTOM_A_TX_5_6_ADC_IB] = 1,
+	[TOMTOM_A_TX_5_6_ATEST_REFCTRL] = 1,
+	[TOMTOM_A_TX_5_6_TEST_CTL] = 1,
+	[TOMTOM_A_TX_5_6_TEST_BLOCK_EN] = 1,
+	[TOMTOM_A_TX_5_6_TXFE_CKDIV] = 1,
+	[TOMTOM_A_TX_5_6_SAR_ERR_CH5] = 1,
+	[TOMTOM_A_TX_5_6_SAR_ERR_CH6] = 1,
+	[TOMTOM_A_TX_7_MBHC_EN] = 1,
+	[TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL] = 1,
+	[TOMTOM_A_TX_7_MBHC_ADC] = 1,
+	[TOMTOM_A_TX_7_MBHC_TEST_CTL] = 1,
+	[TOMTOM_A_TX_7_MBHC_SAR_ERR] = 1,
+	[TOMTOM_A_TX_7_TXFE_CLKDIV] = 1,
+	[TOMTOM_A_RCO_CTRL] = 1,
+	[TOMTOM_A_RCO_CALIBRATION_CTRL1] = 1,
+	[TOMTOM_A_RCO_CALIBRATION_CTRL2] = 1,
+	[TOMTOM_A_RCO_CALIBRATION_CTRL3] = 1,
+	[TOMTOM_A_RCO_TEST_CTRL] = 1,
+	[TOMTOM_A_RCO_CALIBRATION_RESULT1] = 1,
+	[TOMTOM_A_RCO_CALIBRATION_RESULT2] = 1,
+	[TOMTOM_A_BUCK_MODE_1] = 1,
+	[TOMTOM_A_BUCK_MODE_2] = 1,
+	[TOMTOM_A_BUCK_MODE_3] = 1,
+	[TOMTOM_A_BUCK_MODE_4] = 1,
+	[TOMTOM_A_BUCK_MODE_5] = 1,
+	[TOMTOM_A_BUCK_CTRL_VCL_1] = 1,
+	[TOMTOM_A_BUCK_CTRL_VCL_2] = 1,
+	[TOMTOM_A_BUCK_CTRL_VCL_3] = 1,
+	[TOMTOM_A_BUCK_CTRL_CCL_1] = 1,
+	[TOMTOM_A_BUCK_CTRL_CCL_2] = 1,
+	[TOMTOM_A_BUCK_CTRL_CCL_3] = 1,
+	[TOMTOM_A_BUCK_CTRL_CCL_4] = 1,
+	[TOMTOM_A_BUCK_CTRL_PWM_DRVR_1] = 1,
+	[TOMTOM_A_BUCK_CTRL_PWM_DRVR_2] = 1,
+	[TOMTOM_A_BUCK_CTRL_PWM_DRVR_3] = 1,
+	[TOMTOM_A_BUCK_TMUX_A_D] = 1,
+	[TOMTOM_A_NCP_BUCKREF] = 1,
+	[TOMTOM_A_NCP_EN] = 1,
+	[TOMTOM_A_NCP_CLK] = 1,
+	[TOMTOM_A_NCP_STATIC] = 1,
+	[TOMTOM_A_NCP_VTH_LOW] = 1,
+	[TOMTOM_A_NCP_VTH_HIGH] = 1,
+	[TOMTOM_A_NCP_ATEST] = 1,
+	[TOMTOM_A_NCP_DTEST] = 1,
+	[TOMTOM_A_NCP_DLY1] = 1,
+	[TOMTOM_A_NCP_DLY2] = 1,
+	[TOMTOM_A_RX_AUX_SW_CTL] = 1,
+	[TOMTOM_A_RX_PA_AUX_IN_CONN] = 1,
+	[TOMTOM_A_RX_COM_TIMER_DIV] = 1,
+	[TOMTOM_A_RX_COM_OCP_CTL] = 1,
+	[TOMTOM_A_RX_COM_OCP_COUNT] = 1,
+	[TOMTOM_A_RX_COM_DAC_CTL] = 1,
+	[TOMTOM_A_RX_COM_BIAS] = 1,
+	[TOMTOM_A_RX_HPH_AUTO_CHOP] = 1,
+	[TOMTOM_A_RX_HPH_CHOP_CTL] = 1,
+	[TOMTOM_A_RX_HPH_BIAS_PA] = 1,
+	[TOMTOM_A_RX_HPH_BIAS_LDO] = 1,
+	[TOMTOM_A_RX_HPH_BIAS_CNP] = 1,
+	[TOMTOM_A_RX_HPH_BIAS_WG_OCP] = 1,
+	[TOMTOM_A_RX_HPH_OCP_CTL] = 1,
+	[TOMTOM_A_RX_HPH_CNP_EN] = 1,
+	[TOMTOM_A_RX_HPH_CNP_WG_CTL] = 1,
+	[TOMTOM_A_RX_HPH_CNP_WG_TIME] = 1,
+	[TOMTOM_A_RX_HPH_L_GAIN] = 1,
+	[TOMTOM_A_RX_HPH_L_TEST] = 1,
+	[TOMTOM_A_RX_HPH_L_PA_CTL] = 1,
+	[TOMTOM_A_RX_HPH_L_DAC_CTL] = 1,
+	[TOMTOM_A_RX_HPH_L_ATEST] = 1,
+	[TOMTOM_A_RX_HPH_L_STATUS] = 1,
+	[TOMTOM_A_RX_HPH_R_GAIN] = 1,
+	[TOMTOM_A_RX_HPH_R_TEST] = 1,
+	[TOMTOM_A_RX_HPH_R_PA_CTL] = 1,
+	[TOMTOM_A_RX_HPH_R_DAC_CTL] = 1,
+	[TOMTOM_A_RX_HPH_R_ATEST] = 1,
+	[TOMTOM_A_RX_HPH_R_STATUS] = 1,
+	[TOMTOM_A_RX_EAR_BIAS_PA] = 1,
+	[TOMTOM_A_RX_EAR_BIAS_CMBUFF] = 1,
+	[TOMTOM_A_RX_EAR_EN] = 1,
+	[TOMTOM_A_RX_EAR_GAIN] = 1,
+	[TOMTOM_A_RX_EAR_CMBUFF] = 1,
+	[TOMTOM_A_RX_EAR_ICTL] = 1,
+	[TOMTOM_A_RX_EAR_CCOMP] = 1,
+	[TOMTOM_A_RX_EAR_VCM] = 1,
+	[TOMTOM_A_RX_EAR_CNP] = 1,
+	[TOMTOM_A_RX_EAR_DAC_CTL_ATEST] = 1,
+	[TOMTOM_A_RX_EAR_STATUS] = 1,
+	[TOMTOM_A_RX_LINE_BIAS_PA] = 1,
+	[TOMTOM_A_RX_BUCK_BIAS1] = 1,
+	[TOMTOM_A_RX_BUCK_BIAS2] = 1,
+	[TOMTOM_A_RX_LINE_COM] = 1,
+	[TOMTOM_A_RX_LINE_CNP_EN] = 1,
+	[TOMTOM_A_RX_LINE_CNP_WG_CTL] = 1,
+	[TOMTOM_A_RX_LINE_CNP_WG_TIME] = 1,
+	[TOMTOM_A_RX_LINE_1_GAIN] = 1,
+	[TOMTOM_A_RX_LINE_1_TEST] = 1,
+	[TOMTOM_A_RX_LINE_1_DAC_CTL] = 1,
+	[TOMTOM_A_RX_LINE_1_STATUS] = 1,
+	[TOMTOM_A_RX_LINE_2_GAIN] = 1,
+	[TOMTOM_A_RX_LINE_2_TEST] = 1,
+	[TOMTOM_A_RX_LINE_2_DAC_CTL] = 1,
+	[TOMTOM_A_RX_LINE_2_STATUS] = 1,
+	[TOMTOM_A_RX_LINE_3_GAIN] = 1,
+	[TOMTOM_A_RX_LINE_3_TEST] = 1,
+	[TOMTOM_A_RX_LINE_3_DAC_CTL] = 1,
+	[TOMTOM_A_RX_LINE_3_STATUS] = 1,
+	[TOMTOM_A_RX_LINE_4_GAIN] = 1,
+	[TOMTOM_A_RX_LINE_4_TEST] = 1,
+	[TOMTOM_A_RX_LINE_4_DAC_CTL] = 1,
+	[TOMTOM_A_RX_LINE_4_STATUS] = 1,
+	[TOMTOM_A_RX_LINE_CNP_DBG] = 1,
+	[TOMTOM_A_SPKR_DRV1_EN] = 1,
+	[TOMTOM_A_SPKR_DRV1_GAIN] = 1,
+	[TOMTOM_A_SPKR_DRV1_DAC_CTL] = 1,
+	[TOMTOM_A_SPKR_DRV1_OCP_CTL] = 1,
+	[TOMTOM_A_SPKR_DRV1_CLIP_DET] = 1,
+	[TOMTOM_A_SPKR_DRV1_IEC] = 1,
+	[TOMTOM_A_SPKR_DRV1_DBG_DAC] = 1,
+	[TOMTOM_A_SPKR_DRV1_DBG_PA] = 1,
+	[TOMTOM_A_SPKR_DRV1_DBG_PWRSTG] = 1,
+	[TOMTOM_A_SPKR_DRV1_BIAS_LDO] = 1,
+	[TOMTOM_A_SPKR_DRV1_BIAS_INT] = 1,
+	[TOMTOM_A_SPKR_DRV1_BIAS_PA] = 1,
+	[TOMTOM_A_SPKR_DRV1_STATUS_OCP] = 1,
+	[TOMTOM_A_SPKR_DRV1_STATUS_PA] = 1,
+	[TOMTOM_A_SPKR1_PROT_EN] = 1,
+	[TOMTOM_A_SPKR1_PROT_ADC_TEST_EN] = 1,
+	[TOMTOM_A_SPKR1_PROT_ATEST] = 1,
+	[TOMTOM_A_SPKR1_PROT_LDO_CTRL] = 1,
+	[TOMTOM_A_SPKR1_PROT_ISENSE_CTRL] = 1,
+	[TOMTOM_A_SPKR1_PROT_VSENSE_CTRL] = 1,
+	[TOMTOM_A_SPKR2_PROT_EN] = 1,
+	[TOMTOM_A_SPKR2_PROT_ADC_TEST_EN] = 1,
+	[TOMTOM_A_SPKR2_PROT_ATEST] = 1,
+	[TOMTOM_A_SPKR2_PROT_LDO_CTRL] = 1,
+	[TOMTOM_A_SPKR2_PROT_ISENSE_CTRL] = 1,
+	[TOMTOM_A_SPKR2_PROT_VSENSE_CTRL] = 1,
+	[TOMTOM_A_MBHC_HPH] = 1,
+	[TOMTOM_A_CDC_ANC1_B1_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_SHIFT] = 1,
+	[TOMTOM_A_CDC_ANC2_SHIFT] = 1,
+	[TOMTOM_A_CDC_ANC1_IIR_B1_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_IIR_B1_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_IIR_B2_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_IIR_B2_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_IIR_B3_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_IIR_B3_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_LPF_B1_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_LPF_B1_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_LPF_B2_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_LPF_B2_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_SPARE] = 1,
+	[TOMTOM_A_CDC_ANC2_SPARE] = 1,
+	[TOMTOM_A_CDC_ANC1_SMLPF_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_SMLPF_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_DCFLT_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_DCFLT_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_GAIN_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_GAIN_CTL] = 1,
+	[TOMTOM_A_CDC_ANC1_B2_CTL] = 1,
+	[TOMTOM_A_CDC_ANC2_B2_CTL] = 1,
+	[TOMTOM_A_CDC_TX1_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX2_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX3_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX4_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX5_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX6_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX7_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX8_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX9_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX10_VOL_CTL_TIMER] = 1,
+	[TOMTOM_A_CDC_TX1_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX2_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX3_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX4_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX5_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX6_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX7_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX8_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX9_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX10_VOL_CTL_GAIN] = 1,
+	[TOMTOM_A_CDC_TX1_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX2_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX3_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX4_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX5_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX6_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX7_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX8_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX9_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX10_VOL_CTL_CFG] = 1,
+	[TOMTOM_A_CDC_TX1_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX2_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX3_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX4_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX5_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX6_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX7_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX8_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX9_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX10_MUX_CTL] = 1,
+	[TOMTOM_A_CDC_TX1_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX2_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX3_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX4_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX5_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX6_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX7_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX8_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX9_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX10_CLK_FS_CTL] = 1,
+	[TOMTOM_A_CDC_TX1_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX2_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX3_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX4_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX5_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX6_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX7_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX8_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX9_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_TX10_DMIC_CTL] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL0] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL1] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL2] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL3] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL4] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL5] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL6] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL7] = 1,
+	[TOMTOM_A_CDC_DEBUG_B1_CTL] = 1,
+	[TOMTOM_A_CDC_DEBUG_B2_CTL] = 1,
+	[TOMTOM_A_CDC_DEBUG_B3_CTL] = 1,
+	[TOMTOM_A_CDC_DEBUG_B4_CTL] = 1,
+	[TOMTOM_A_CDC_DEBUG_B5_CTL] = 1,
+	[TOMTOM_A_CDC_DEBUG_B6_CTL] = 1,
+	[TOMTOM_A_CDC_DEBUG_B7_CTL] = 1,
+	[TOMTOM_A_CDC_SRC1_PDA_CFG] = 1,
+	[TOMTOM_A_CDC_SRC2_PDA_CFG] = 1,
+	[TOMTOM_A_CDC_SRC1_FS_CTL] = 1,
+	[TOMTOM_A_CDC_SRC2_FS_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_VBAT_CFG] = 1,
+	[TOMTOM_A_CDC_VBAT_ADC_CAL1] = 1,
+	[TOMTOM_A_CDC_VBAT_ADC_CAL2] = 1,
+	[TOMTOM_A_CDC_VBAT_ADC_CAL3] = 1,
+	[TOMTOM_A_CDC_VBAT_PK_EST1] = 1,
+	[TOMTOM_A_CDC_VBAT_PK_EST2] = 1,
+	[TOMTOM_A_CDC_VBAT_PK_EST3] = 1,
+	[TOMTOM_A_CDC_VBAT_RF_PROC1] = 1,
+	[TOMTOM_A_CDC_VBAT_RF_PROC2] = 1,
+	[TOMTOM_A_CDC_VBAT_TAC1] = 1,
+	[TOMTOM_A_CDC_VBAT_TAC2] = 1,
+	[TOMTOM_A_CDC_VBAT_TAC3] = 1,
+	[TOMTOM_A_CDC_VBAT_TAC4] = 1,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD1] = 1,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD2] = 1,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD3] = 1,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD4] = 1,
+	[TOMTOM_A_CDC_VBAT_DEBUG1] = 1,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD_MON] = 0,
+	[TOMTOM_A_CDC_VBAT_GAIN_MON_VAL] = 1,
+	[TOMTOM_A_CDC_CLK_ANC_RESET_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_RX_RESET_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_RX_I2S_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_TX_I2S_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_OTHR_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_RX_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_RX_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_MCLK_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_PDM_CTL] = 1,
+	[TOMTOM_A_CDC_CLK_SD_CTL] = 1,
+	[TOMTOM_A_CDC_CLSH_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLSH_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CLSH_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS] = 1,
+	[TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD] = 1,
+	[TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD] = 1,
+	[TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD] = 1,
+	[TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD] = 1,
+	[TOMTOM_A_CDC_CLSH_K_ADDR] = 1,
+	[TOMTOM_A_CDC_CLSH_K_DATA] = 1,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L] = 1,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U] = 1,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L] = 1,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U] = 1,
+	[TOMTOM_A_CDC_CLSH_V_PA_HD_EAR] = 1,
+	[TOMTOM_A_CDC_CLSH_V_PA_HD_HPH] = 1,
+	[TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR] = 1,
+	[TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B1_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B1_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B2_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B2_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B3_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B3_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B4_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B4_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B5_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B5_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B6_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B6_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B7_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B7_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_B8_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_B8_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_COEF_B1_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_COEF_B1_CTL] = 1,
+	[TOMTOM_A_CDC_IIR1_COEF_B2_CTL] = 1,
+	[TOMTOM_A_CDC_IIR2_COEF_B2_CTL] = 1,
+	[TOMTOM_A_CDC_TOP_GAIN_UPDATE] = 1,
+	[TOMTOM_A_CDC_PA_RAMP_B1_CTL] = 1,
+	[TOMTOM_A_CDC_PA_RAMP_B2_CTL] = 1,
+	[TOMTOM_A_CDC_PA_RAMP_B3_CTL] = 1,
+	[TOMTOM_A_CDC_PA_RAMP_B4_CTL] = 1,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_B1_CTL] = 1,
+	[TOMTOM_A_CDC_COMP1_B1_CTL] = 1,
+	[TOMTOM_A_CDC_COMP2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_B2_CTL] = 1,
+	[TOMTOM_A_CDC_COMP1_B2_CTL] = 1,
+	[TOMTOM_A_CDC_COMP2_B2_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_B3_CTL] = 1,
+	[TOMTOM_A_CDC_COMP1_B3_CTL] = 1,
+	[TOMTOM_A_CDC_COMP2_B3_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_B4_CTL] = 1,
+	[TOMTOM_A_CDC_COMP1_B4_CTL] = 1,
+	[TOMTOM_A_CDC_COMP2_B4_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_B5_CTL] = 1,
+	[TOMTOM_A_CDC_COMP1_B5_CTL] = 1,
+	[TOMTOM_A_CDC_COMP2_B5_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_B6_CTL] = 1,
+	[TOMTOM_A_CDC_COMP1_B6_CTL] = 1,
+	[TOMTOM_A_CDC_COMP2_B6_CTL] = 1,
+	[TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS] = 1,
+	[TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS] = 1,
+	[TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS] = 1,
+	[TOMTOM_A_CDC_COMP0_FS_CFG] = 1,
+	[TOMTOM_A_CDC_COMP1_FS_CFG] = 1,
+	[TOMTOM_A_CDC_COMP2_FS_CFG] = 1,
+	[TOMTOM_A_CDC_CONN_RX1_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX1_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX1_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX2_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX2_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX3_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX3_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX4_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX4_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX5_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX5_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX6_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX6_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX7_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX7_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX7_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_ANC_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_ANC_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_B4_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ1_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ1_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ1_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ1_B4_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ2_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ2_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_EQ2_B4_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_SRC1_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_SRC1_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_SRC2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_SRC2_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B3_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B4_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B5_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B6_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B7_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B8_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B9_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B10_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_TX_SB_B11_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX_SB_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_RX_SB_B2_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_CLSH_CTL] = 1,
+	[TOMTOM_A_CDC_CONN_MISC] = 1,
+	[TOMTOM_A_CDC_CONN_RX8_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK] = 1,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING] = 1,
+	[TOMTOM_A_CDC_MBHC_EN_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_FIR_B1_CFG] = 1,
+	[TOMTOM_A_CDC_MBHC_FIR_B2_CFG] = 1,
+	[TOMTOM_A_CDC_MBHC_TIMER_B1_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_TIMER_B2_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_TIMER_B3_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_TIMER_B4_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_TIMER_B5_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_TIMER_B6_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_B1_STATUS] = 1,
+	[TOMTOM_A_CDC_MBHC_B2_STATUS] = 1,
+	[TOMTOM_A_CDC_MBHC_B3_STATUS] = 1,
+	[TOMTOM_A_CDC_MBHC_B4_STATUS] = 1,
+	[TOMTOM_A_CDC_MBHC_B5_STATUS] = 1,
+	[TOMTOM_A_CDC_MBHC_B1_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_B2_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B1_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B2_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B3_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B4_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B5_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B6_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B7_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B8_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B9_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B10_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B11_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_VOLT_B12_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_CLK_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_INT_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_DEBUG_CTL] = 1,
+	[TOMTOM_A_CDC_MBHC_SPARE] = 1,
+	[TOMTOM_A_CDC_RX8_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_B2_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_B3_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_B4_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_B5_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_B6_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL] = 1,
+	[TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6] = 1,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7] = 1,
+	[TOMTOM_A_CDC_BOOST_MODE_CTL] = 1,
+	[TOMTOM_A_CDC_BOOST_THRESHOLD] = 1,
+	[TOMTOM_A_CDC_BOOST_TAP_SEL] = 1,
+	[TOMTOM_A_CDC_BOOST_HOLD_TIME] = 1,
+	[TOMTOM_A_CDC_BOOST_TRGR_EN] = 1,
+};
+
+const u8 tomtom_reset_reg_defaults[TOMTOM_CACHE_SIZE] = {
+	[TOMTOM_A_CHIP_CTL] = TOMTOM_A_CHIP_CTL__POR,
+	[TOMTOM_A_CHIP_STATUS] = TOMTOM_A_CHIP_STATUS__POR,
+	[TOMTOM_A_CHIP_ID_BYTE_0] = TOMTOM_A_CHIP_ID_BYTE_0__POR,
+	[TOMTOM_A_CHIP_ID_BYTE_1] = TOMTOM_A_CHIP_ID_BYTE_1__POR,
+	[TOMTOM_A_CHIP_ID_BYTE_2] = TOMTOM_A_CHIP_ID_BYTE_2__POR,
+	[TOMTOM_A_CHIP_ID_BYTE_3] = TOMTOM_A_CHIP_ID_BYTE_3__POR,
+	[TOMTOM_A_CHIP_I2C_SLAVE_ID] = TOMTOM_A_CHIP_I2C_SLAVE_ID__POR,
+	[TOMTOM_A_SLAVE_ID_1] = TOMTOM_A_SLAVE_ID_1__POR,
+	[TOMTOM_A_SLAVE_ID_2] = TOMTOM_A_SLAVE_ID_2__POR,
+	[TOMTOM_A_SLAVE_ID_3] = TOMTOM_A_SLAVE_ID_3__POR,
+	[TOMTOM_A_PIN_CTL_OE0] = TOMTOM_A_PIN_CTL_OE0__POR,
+	[TOMTOM_A_PIN_CTL_OE1] = TOMTOM_A_PIN_CTL_OE1__POR,
+	[TOMTOM_A_PIN_CTL_OE2] = TOMTOM_A_PIN_CTL_OE2__POR,
+	[TOMTOM_A_PIN_CTL_DATA0] = TOMTOM_A_PIN_CTL_DATA0__POR,
+	[TOMTOM_A_PIN_CTL_DATA1] = TOMTOM_A_PIN_CTL_DATA1__POR,
+	[TOMTOM_A_PIN_CTL_DATA2] = TOMTOM_A_PIN_CTL_DATA2__POR,
+	[TOMTOM_A_HDRIVE_GENERIC] = TOMTOM_A_HDRIVE_GENERIC__POR,
+	[TOMTOM_A_HDRIVE_OVERRIDE] = TOMTOM_A_HDRIVE_OVERRIDE__POR,
+	[TOMTOM_A_ANA_CSR_WAIT_STATE] = TOMTOM_A_ANA_CSR_WAIT_STATE__POR,
+	[TOMTOM_A_PROCESS_MONITOR_CTL0] = TOMTOM_A_PROCESS_MONITOR_CTL0__POR,
+	[TOMTOM_A_PROCESS_MONITOR_CTL1] = TOMTOM_A_PROCESS_MONITOR_CTL1__POR,
+	[TOMTOM_A_PROCESS_MONITOR_CTL2] = TOMTOM_A_PROCESS_MONITOR_CTL2__POR,
+	[TOMTOM_A_PROCESS_MONITOR_CTL3] = TOMTOM_A_PROCESS_MONITOR_CTL3__POR,
+	[TOMTOM_A_QFUSE_CTL] = TOMTOM_A_QFUSE_CTL__POR,
+	[TOMTOM_A_QFUSE_STATUS] = TOMTOM_A_QFUSE_STATUS__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT0] = TOMTOM_A_QFUSE_DATA_OUT0__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT1] = TOMTOM_A_QFUSE_DATA_OUT1__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT2] = TOMTOM_A_QFUSE_DATA_OUT2__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT3] = TOMTOM_A_QFUSE_DATA_OUT3__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT4] = TOMTOM_A_QFUSE_DATA_OUT4__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT5] = TOMTOM_A_QFUSE_DATA_OUT5__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT6] = TOMTOM_A_QFUSE_DATA_OUT6__POR,
+	[TOMTOM_A_QFUSE_DATA_OUT7] = TOMTOM_A_QFUSE_DATA_OUT7__POR,
+	[TOMTOM_A_CDC_CTL] = TOMTOM_A_CDC_CTL__POR,
+	[TOMTOM_A_LEAKAGE_CTL] = TOMTOM_A_LEAKAGE_CTL__POR,
+	[TOMTOM_A_SVASS_MEM_PTR0] = TOMTOM_A_SVASS_MEM_PTR0__POR,
+	[TOMTOM_A_SVASS_MEM_PTR1] = TOMTOM_A_SVASS_MEM_PTR1__POR,
+	[TOMTOM_A_SVASS_MEM_PTR2] = TOMTOM_A_SVASS_MEM_PTR2__POR,
+	[TOMTOM_A_SVASS_MEM_CTL] = TOMTOM_A_SVASS_MEM_CTL__POR,
+	[TOMTOM_A_SVASS_MEM_BANK] = TOMTOM_A_SVASS_MEM_BANK__POR,
+	[TOMTOM_A_DMIC_B1_CTL] = TOMTOM_A_DMIC_B1_CTL__POR,
+	[TOMTOM_A_DMIC_B2_CTL] = TOMTOM_A_DMIC_B2_CTL__POR,
+	[TOMTOM_A_SVASS_CLKRST_CTL] = TOMTOM_A_SVASS_CLKRST_CTL__POR,
+	[TOMTOM_A_SVASS_CPAR_CFG] = TOMTOM_A_SVASS_CPAR_CFG__POR,
+	[TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD] =
+				TOMTOM_A_SVASS_BUF_RDY_INT_PERIOD__POR,
+	[TOMTOM_A_SVASS_CPAR_WDOG_CFG] = TOMTOM_A_SVASS_CPAR_WDOG_CFG__POR,
+	[TOMTOM_A_SVASS_CFG] = TOMTOM_A_SVASS_CFG__POR,
+	[TOMTOM_A_SVASS_SPE_CFG] = TOMTOM_A_SVASS_SPE_CFG__POR,
+	[TOMTOM_A_SVASS_STATUS] = TOMTOM_A_SVASS_STATUS__POR,
+	[TOMTOM_A_SVASS_INT_MASK] = TOMTOM_A_SVASS_INT_MASK__POR,
+	[TOMTOM_A_SVASS_INT_STATUS] = TOMTOM_A_SVASS_INT_STATUS__POR,
+	[TOMTOM_A_SVASS_INT_CLR] = TOMTOM_A_SVASS_INT_CLR__POR,
+	[TOMTOM_A_SVASS_DEBUG] = TOMTOM_A_SVASS_DEBUG__POR,
+	[TOMTOM_A_SVASS_SPE_BKUP_INT] = TOMTOM_A_SVASS_SPE_BKUP_INT__POR,
+	[TOMTOM_A_SVASS_MEM_ACC] = TOMTOM_A_SVASS_MEM_ACC__POR,
+	[TOMTOM_A_MEM_LEAKAGE_CTL] = TOMTOM_A_MEM_LEAKAGE_CTL__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_TRG] = TOMTOM_A_SVASS_SPE_INBOX_TRG__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_0] = TOMTOM_A_SVASS_SPE_INBOX_0__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_1] = TOMTOM_A_SVASS_SPE_INBOX_1__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_2] = TOMTOM_A_SVASS_SPE_INBOX_2__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_3] = TOMTOM_A_SVASS_SPE_INBOX_3__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_4] = TOMTOM_A_SVASS_SPE_INBOX_4__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_5] = TOMTOM_A_SVASS_SPE_INBOX_5__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_6] = TOMTOM_A_SVASS_SPE_INBOX_6__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_7] = TOMTOM_A_SVASS_SPE_INBOX_7__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_8] = TOMTOM_A_SVASS_SPE_INBOX_8__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_9] = TOMTOM_A_SVASS_SPE_INBOX_9__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_10] = TOMTOM_A_SVASS_SPE_INBOX_10__POR,
+	[TOMTOM_A_SVASS_SPE_INBOX_11] = TOMTOM_A_SVASS_SPE_INBOX_11__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_0] = TOMTOM_A_SVASS_SPE_OUTBOX_0__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_1] = TOMTOM_A_SVASS_SPE_OUTBOX_1__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_2] = TOMTOM_A_SVASS_SPE_OUTBOX_2__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_3] = TOMTOM_A_SVASS_SPE_OUTBOX_3__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_4] = TOMTOM_A_SVASS_SPE_OUTBOX_4__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_5] = TOMTOM_A_SVASS_SPE_OUTBOX_5__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_6] = TOMTOM_A_SVASS_SPE_OUTBOX_6__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_7] = TOMTOM_A_SVASS_SPE_OUTBOX_7__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_8] = TOMTOM_A_SVASS_SPE_OUTBOX_8__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_9] = TOMTOM_A_SVASS_SPE_OUTBOX_9__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_10] = TOMTOM_A_SVASS_SPE_OUTBOX_10__POR,
+	[TOMTOM_A_SVASS_SPE_OUTBOX_11] = TOMTOM_A_SVASS_SPE_OUTBOX_11__POR,
+	[TOMTOM_A_INTR_MODE] = TOMTOM_A_INTR_MODE__POR,
+	[TOMTOM_A_INTR1_MASK0] = TOMTOM_A_INTR1_MASK0__POR,
+	[TOMTOM_A_INTR1_MASK1] = TOMTOM_A_INTR1_MASK1__POR,
+	[TOMTOM_A_INTR1_MASK2] = TOMTOM_A_INTR1_MASK2__POR,
+	[TOMTOM_A_INTR1_MASK3] = TOMTOM_A_INTR1_MASK3__POR,
+	[TOMTOM_A_INTR1_STATUS0] = TOMTOM_A_INTR1_STATUS0__POR,
+	[TOMTOM_A_INTR1_STATUS1] = TOMTOM_A_INTR1_STATUS1__POR,
+	[TOMTOM_A_INTR1_STATUS2] = TOMTOM_A_INTR1_STATUS2__POR,
+	[TOMTOM_A_INTR1_STATUS3] = TOMTOM_A_INTR1_STATUS3__POR,
+	[TOMTOM_A_INTR1_CLEAR0] = TOMTOM_A_INTR1_CLEAR0__POR,
+	[TOMTOM_A_INTR1_CLEAR1] = TOMTOM_A_INTR1_CLEAR1__POR,
+	[TOMTOM_A_INTR1_CLEAR2] = TOMTOM_A_INTR1_CLEAR2__POR,
+	[TOMTOM_A_INTR1_CLEAR3] = TOMTOM_A_INTR1_CLEAR3__POR,
+	[TOMTOM_A_INTR1_LEVEL0] = TOMTOM_A_INTR1_LEVEL0__POR,
+	[TOMTOM_A_INTR1_LEVEL1] = TOMTOM_A_INTR1_LEVEL1__POR,
+	[TOMTOM_A_INTR1_LEVEL2] = TOMTOM_A_INTR1_LEVEL2__POR,
+	[TOMTOM_A_INTR1_LEVEL3] = TOMTOM_A_INTR1_LEVEL3__POR,
+	[TOMTOM_A_INTR1_TEST0] = TOMTOM_A_INTR1_TEST0__POR,
+	[TOMTOM_A_INTR1_TEST1] = TOMTOM_A_INTR1_TEST1__POR,
+	[TOMTOM_A_INTR1_TEST2] = TOMTOM_A_INTR1_TEST2__POR,
+	[TOMTOM_A_INTR1_TEST3] = TOMTOM_A_INTR1_TEST3__POR,
+	[TOMTOM_A_INTR1_SET0] = TOMTOM_A_INTR1_SET0__POR,
+	[TOMTOM_A_INTR1_SET1] = TOMTOM_A_INTR1_SET1__POR,
+	[TOMTOM_A_INTR1_SET2] = TOMTOM_A_INTR1_SET2__POR,
+	[TOMTOM_A_INTR1_SET3] = TOMTOM_A_INTR1_SET3__POR,
+	[TOMTOM_A_INTR2_MASK0] = TOMTOM_A_INTR2_MASK0__POR,
+	[TOMTOM_A_INTR2_STATUS0] = TOMTOM_A_INTR2_STATUS0__POR,
+	[TOMTOM_A_INTR2_CLEAR0] = TOMTOM_A_INTR2_CLEAR0__POR,
+	[TOMTOM_A_INTR2_LEVEL0] = TOMTOM_A_INTR2_LEVEL0__POR,
+	[TOMTOM_A_INTR2_TEST0] = TOMTOM_A_INTR2_TEST0__POR,
+	[TOMTOM_A_INTR2_SET0] = TOMTOM_A_INTR2_SET0__POR,
+	[TOMTOM_A_CDC_TX_I2S_SCK_MODE] = TOMTOM_A_CDC_TX_I2S_SCK_MODE__POR,
+	[TOMTOM_A_CDC_TX_I2S_WS_MODE] = TOMTOM_A_CDC_TX_I2S_WS_MODE__POR,
+	[TOMTOM_A_CDC_DMIC_DATA0_MODE] = TOMTOM_A_CDC_DMIC_DATA0_MODE__POR,
+	[TOMTOM_A_CDC_DMIC_CLK0_MODE] = TOMTOM_A_CDC_DMIC_CLK0_MODE__POR,
+	[TOMTOM_A_CDC_DMIC_DATA1_MODE] = TOMTOM_A_CDC_DMIC_DATA1_MODE__POR,
+	[TOMTOM_A_CDC_DMIC_CLK1_MODE] = TOMTOM_A_CDC_DMIC_CLK1_MODE__POR,
+	[TOMTOM_A_CDC_RX_I2S_SCK_MODE] = TOMTOM_A_CDC_RX_I2S_SCK_MODE__POR,
+	[TOMTOM_A_CDC_RX_I2S_WS_MODE] = TOMTOM_A_CDC_RX_I2S_WS_MODE__POR,
+	[TOMTOM_A_CDC_DMIC_DATA2_MODE] = TOMTOM_A_CDC_DMIC_DATA2_MODE__POR,
+	[TOMTOM_A_CDC_DMIC_CLK2_MODE] = TOMTOM_A_CDC_DMIC_CLK2_MODE__POR,
+	[TOMTOM_A_CDC_INTR1_MODE] = TOMTOM_A_CDC_INTR1_MODE__POR,
+	[TOMTOM_A_CDC_SB_NRZ_SEL_MODE] = TOMTOM_A_CDC_SB_NRZ_SEL_MODE__POR,
+	[TOMTOM_A_CDC_INTR2_MODE] = TOMTOM_A_CDC_INTR2_MODE__POR,
+	[TOMTOM_A_CDC_RF_PA_ON_MODE] = TOMTOM_A_CDC_RF_PA_ON_MODE__POR,
+	[TOMTOM_A_CDC_BOOST_MODE] = TOMTOM_A_CDC_BOOST_MODE__POR,
+	[TOMTOM_A_CDC_JTCK_MODE] = TOMTOM_A_CDC_JTCK_MODE__POR,
+	[TOMTOM_A_CDC_JTDI_MODE] = TOMTOM_A_CDC_JTDI_MODE__POR,
+	[TOMTOM_A_CDC_JTMS_MODE] = TOMTOM_A_CDC_JTMS_MODE__POR,
+	[TOMTOM_A_CDC_JTDO_MODE] = TOMTOM_A_CDC_JTDO_MODE__POR,
+	[TOMTOM_A_CDC_JTRST_MODE] = TOMTOM_A_CDC_JTRST_MODE__POR,
+	[TOMTOM_A_CDC_BIST_MODE_MODE] = TOMTOM_A_CDC_BIST_MODE_MODE__POR,
+	[TOMTOM_A_CDC_MAD_MAIN_CTL_1] = TOMTOM_A_CDC_MAD_MAIN_CTL_1__POR,
+	[TOMTOM_A_CDC_MAD_MAIN_CTL_2] = TOMTOM_A_CDC_MAD_MAIN_CTL_2__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_1] = TOMTOM_A_CDC_MAD_AUDIO_CTL_1__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_2] = TOMTOM_A_CDC_MAD_AUDIO_CTL_2__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_3] = TOMTOM_A_CDC_MAD_AUDIO_CTL_3__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_4] = TOMTOM_A_CDC_MAD_AUDIO_CTL_4__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_5] = TOMTOM_A_CDC_MAD_AUDIO_CTL_5__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_6] = TOMTOM_A_CDC_MAD_AUDIO_CTL_6__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_7] = TOMTOM_A_CDC_MAD_AUDIO_CTL_7__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_CTL_8] = TOMTOM_A_CDC_MAD_AUDIO_CTL_8__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR] =
+				TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR__POR,
+	[TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL] =
+				TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_1] = TOMTOM_A_CDC_MAD_ULTR_CTL_1__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_2] = TOMTOM_A_CDC_MAD_ULTR_CTL_2__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_3] = TOMTOM_A_CDC_MAD_ULTR_CTL_3__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_4] = TOMTOM_A_CDC_MAD_ULTR_CTL_4__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_5] = TOMTOM_A_CDC_MAD_ULTR_CTL_5__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_6] = TOMTOM_A_CDC_MAD_ULTR_CTL_6__POR,
+	[TOMTOM_A_CDC_MAD_ULTR_CTL_7] = TOMTOM_A_CDC_MAD_ULTR_CTL_7__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_1] = TOMTOM_A_CDC_MAD_BEACON_CTL_1__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_2] = TOMTOM_A_CDC_MAD_BEACON_CTL_2__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_3] = TOMTOM_A_CDC_MAD_BEACON_CTL_3__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_4] = TOMTOM_A_CDC_MAD_BEACON_CTL_4__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_5] = TOMTOM_A_CDC_MAD_BEACON_CTL_5__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_6] = TOMTOM_A_CDC_MAD_BEACON_CTL_6__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_7] = TOMTOM_A_CDC_MAD_BEACON_CTL_7__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_CTL_8] = TOMTOM_A_CDC_MAD_BEACON_CTL_8__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR] =
+				TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_PTR__POR,
+	[TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL] =
+				TOMTOM_A_CDC_MAD_BEACON_IIR_CTL_VAL__POR,
+	[TOMTOM_A_CDC_MAD_INP_SEL] = TOMTOM_A_CDC_MAD_INP_SEL__POR,
+	[TOMTOM_A_BIAS_REF_CTL] = TOMTOM_A_BIAS_REF_CTL__POR,
+	[TOMTOM_A_BIAS_CENTRAL_BG_CTL] = TOMTOM_A_BIAS_CENTRAL_BG_CTL__POR,
+	[TOMTOM_A_BIAS_PRECHRG_CTL] = TOMTOM_A_BIAS_PRECHRG_CTL__POR,
+	[TOMTOM_A_BIAS_CURR_CTL_1] = TOMTOM_A_BIAS_CURR_CTL_1__POR,
+	[TOMTOM_A_BIAS_CURR_CTL_2] = TOMTOM_A_BIAS_CURR_CTL_2__POR,
+	[TOMTOM_A_BIAS_OSC_BG_CTL] = TOMTOM_A_BIAS_OSC_BG_CTL__POR,
+	[TOMTOM_A_CLK_BUFF_EN1] = TOMTOM_A_CLK_BUFF_EN1__POR,
+	[TOMTOM_A_CLK_BUFF_EN2] = TOMTOM_A_CLK_BUFF_EN2__POR,
+	[TOMTOM_A_LDO_L_MODE_1] = TOMTOM_A_LDO_L_MODE_1__POR,
+	[TOMTOM_A_LDO_L_MODE_2] = TOMTOM_A_LDO_L_MODE_2__POR,
+	[TOMTOM_A_LDO_L_CTRL_1] = TOMTOM_A_LDO_L_CTRL_1__POR,
+	[TOMTOM_A_LDO_L_CTRL_2] = TOMTOM_A_LDO_L_CTRL_2__POR,
+	[TOMTOM_A_LDO_L_CTRL_3] = TOMTOM_A_LDO_L_CTRL_3__POR,
+	[TOMTOM_A_LDO_L_CTRL_4] = TOMTOM_A_LDO_L_CTRL_4__POR,
+	[TOMTOM_A_LDO_H_MODE_1] = TOMTOM_A_LDO_H_MODE_1__POR,
+	[TOMTOM_A_LDO_H_MODE_2] = TOMTOM_A_LDO_H_MODE_2__POR,
+	[TOMTOM_A_LDO_H_LOOP_CTL] = TOMTOM_A_LDO_H_LOOP_CTL__POR,
+	[TOMTOM_A_LDO_H_COMP_1] = TOMTOM_A_LDO_H_COMP_1__POR,
+	[TOMTOM_A_LDO_H_COMP_2] = TOMTOM_A_LDO_H_COMP_2__POR,
+	[TOMTOM_A_LDO_H_BIAS_1] = TOMTOM_A_LDO_H_BIAS_1__POR,
+	[TOMTOM_A_LDO_H_BIAS_2] = TOMTOM_A_LDO_H_BIAS_2__POR,
+	[TOMTOM_A_LDO_H_BIAS_3] = TOMTOM_A_LDO_H_BIAS_3__POR,
+	[TOMTOM_A_VBAT_CLK] = TOMTOM_A_VBAT_CLK__POR,
+	[TOMTOM_A_VBAT_LOOP] = TOMTOM_A_VBAT_LOOP__POR,
+	[TOMTOM_A_VBAT_REF] = TOMTOM_A_VBAT_REF__POR,
+	[TOMTOM_A_VBAT_ADC_TEST] = TOMTOM_A_VBAT_ADC_TEST__POR,
+	[TOMTOM_A_VBAT_FE] = TOMTOM_A_VBAT_FE__POR,
+	[TOMTOM_A_VBAT_BIAS_1] = TOMTOM_A_VBAT_BIAS_1__POR,
+	[TOMTOM_A_VBAT_BIAS_2] = TOMTOM_A_VBAT_BIAS_2__POR,
+	[TOMTOM_A_VBAT_ADC_DATA_MSB] = TOMTOM_A_VBAT_ADC_DATA_MSB__POR,
+	[TOMTOM_A_VBAT_ADC_DATA_LSB] = TOMTOM_A_VBAT_ADC_DATA_LSB__POR,
+	[TOMTOM_A_FLL_NREF] = TOMTOM_A_FLL_NREF__POR,
+	[TOMTOM_A_FLL_KDCO_TUNE] = TOMTOM_A_FLL_KDCO_TUNE__POR,
+	[TOMTOM_A_FLL_LOCK_THRESH] = TOMTOM_A_FLL_LOCK_THRESH__POR,
+	[TOMTOM_A_FLL_LOCK_DET_COUNT] = TOMTOM_A_FLL_LOCK_DET_COUNT__POR,
+	[TOMTOM_A_FLL_DAC_THRESHOLD] = TOMTOM_A_FLL_DAC_THRESHOLD__POR,
+	[TOMTOM_A_FLL_TEST_DCO_FREERUN] = TOMTOM_A_FLL_TEST_DCO_FREERUN__POR,
+	[TOMTOM_A_FLL_TEST_ENABLE] = TOMTOM_A_FLL_TEST_ENABLE__POR,
+	[TOMTOM_A_MICB_CFILT_1_CTL] = TOMTOM_A_MICB_CFILT_1_CTL__POR,
+	[TOMTOM_A_MICB_CFILT_1_VAL] = TOMTOM_A_MICB_CFILT_1_VAL__POR,
+	[TOMTOM_A_MICB_CFILT_1_PRECHRG] = TOMTOM_A_MICB_CFILT_1_PRECHRG__POR,
+	[TOMTOM_A_MICB_1_CTL] = TOMTOM_A_MICB_1_CTL__POR,
+	[TOMTOM_A_MICB_1_INT_RBIAS] = TOMTOM_A_MICB_1_INT_RBIAS__POR,
+	[TOMTOM_A_MICB_1_MBHC] = TOMTOM_A_MICB_1_MBHC__POR,
+	[TOMTOM_A_MICB_CFILT_2_CTL] = TOMTOM_A_MICB_CFILT_2_CTL__POR,
+	[TOMTOM_A_MICB_CFILT_2_VAL] = TOMTOM_A_MICB_CFILT_2_VAL__POR,
+	[TOMTOM_A_MICB_CFILT_2_PRECHRG] = TOMTOM_A_MICB_CFILT_2_PRECHRG__POR,
+	[TOMTOM_A_MICB_2_CTL] = TOMTOM_A_MICB_2_CTL__POR,
+	[TOMTOM_A_MICB_2_INT_RBIAS] = TOMTOM_A_MICB_2_INT_RBIAS__POR,
+	[TOMTOM_A_MICB_2_MBHC] = TOMTOM_A_MICB_2_MBHC__POR,
+	[TOMTOM_A_MICB_CFILT_3_CTL] = TOMTOM_A_MICB_CFILT_3_CTL__POR,
+	[TOMTOM_A_MICB_CFILT_3_VAL] = TOMTOM_A_MICB_CFILT_3_VAL__POR,
+	[TOMTOM_A_MICB_CFILT_3_PRECHRG] = TOMTOM_A_MICB_CFILT_3_PRECHRG__POR,
+	[TOMTOM_A_MICB_3_CTL] = TOMTOM_A_MICB_3_CTL__POR,
+	[TOMTOM_A_MICB_3_INT_RBIAS] = TOMTOM_A_MICB_3_INT_RBIAS__POR,
+	[TOMTOM_A_MICB_3_MBHC] = TOMTOM_A_MICB_3_MBHC__POR,
+	[TOMTOM_A_MICB_4_CTL] = TOMTOM_A_MICB_4_CTL__POR,
+	[TOMTOM_A_MICB_4_INT_RBIAS] = TOMTOM_A_MICB_4_INT_RBIAS__POR,
+	[TOMTOM_A_MICB_4_MBHC] = TOMTOM_A_MICB_4_MBHC__POR,
+	[TOMTOM_A_SPKR_DRV2_EN] = TOMTOM_A_SPKR_DRV2_EN__POR,
+	[TOMTOM_A_SPKR_DRV2_GAIN] = TOMTOM_A_SPKR_DRV2_GAIN__POR,
+	[TOMTOM_A_SPKR_DRV2_DAC_CTL] = TOMTOM_A_SPKR_DRV2_DAC_CTL__POR,
+	[TOMTOM_A_SPKR_DRV2_OCP_CTL] = TOMTOM_A_SPKR_DRV2_OCP_CTL__POR,
+	[TOMTOM_A_SPKR_DRV2_CLIP_DET] = TOMTOM_A_SPKR_DRV2_CLIP_DET__POR,
+	[TOMTOM_A_SPKR_DRV2_DBG_DAC] = TOMTOM_A_SPKR_DRV2_DBG_DAC__POR,
+	[TOMTOM_A_SPKR_DRV2_DBG_PA] = TOMTOM_A_SPKR_DRV2_DBG_PA__POR,
+	[TOMTOM_A_SPKR_DRV2_DBG_PWRSTG] = TOMTOM_A_SPKR_DRV2_DBG_PWRSTG__POR,
+	[TOMTOM_A_SPKR_DRV2_BIAS_LDO] = TOMTOM_A_SPKR_DRV2_BIAS_LDO__POR,
+	[TOMTOM_A_SPKR_DRV2_BIAS_INT] = TOMTOM_A_SPKR_DRV2_BIAS_INT__POR,
+	[TOMTOM_A_SPKR_DRV2_BIAS_PA] = TOMTOM_A_SPKR_DRV2_BIAS_PA__POR,
+	[TOMTOM_A_SPKR_DRV2_STATUS_OCP] = TOMTOM_A_SPKR_DRV2_STATUS_OCP__POR,
+	[TOMTOM_A_SPKR_DRV2_STATUS_PA] = TOMTOM_A_SPKR_DRV2_STATUS_PA__POR,
+	[TOMTOM_A_MBHC_INSERT_DETECT] = TOMTOM_A_MBHC_INSERT_DETECT__POR,
+	[TOMTOM_A_MBHC_INSERT_DET_STATUS] =
+				TOMTOM_A_MBHC_INSERT_DET_STATUS__POR,
+	[TOMTOM_A_TX_COM_BIAS] = TOMTOM_A_TX_COM_BIAS__POR,
+	[TOMTOM_A_MBHC_INSERT_DETECT2] = TOMTOM_A_MBHC_INSERT_DETECT2__POR,
+	[TOMTOM_A_MBHC_SCALING_MUX_1] = TOMTOM_A_MBHC_SCALING_MUX_1__POR,
+	[TOMTOM_A_MBHC_SCALING_MUX_2] = TOMTOM_A_MBHC_SCALING_MUX_2__POR,
+	[TOMTOM_A_MAD_ANA_CTRL] = TOMTOM_A_MAD_ANA_CTRL__POR,
+	[TOMTOM_A_TX_SUP_SWITCH_CTRL_1] = TOMTOM_A_TX_SUP_SWITCH_CTRL_1__POR,
+	[TOMTOM_A_TX_SUP_SWITCH_CTRL_2] = TOMTOM_A_TX_SUP_SWITCH_CTRL_2__POR,
+	[TOMTOM_A_TX_1_GAIN] = TOMTOM_A_TX_1_GAIN__POR,
+	[TOMTOM_A_TX_1_2_TEST_EN] = TOMTOM_A_TX_1_2_TEST_EN__POR,
+	[TOMTOM_A_TX_2_GAIN] = TOMTOM_A_TX_2_GAIN__POR,
+	[TOMTOM_A_TX_1_2_ADC_IB] = TOMTOM_A_TX_1_2_ADC_IB__POR,
+	[TOMTOM_A_TX_1_2_ATEST_REFCTRL] = TOMTOM_A_TX_1_2_ATEST_REFCTRL__POR,
+	[TOMTOM_A_TX_1_2_TEST_CTL] = TOMTOM_A_TX_1_2_TEST_CTL__POR,
+	[TOMTOM_A_TX_1_2_TEST_BLOCK_EN] = TOMTOM_A_TX_1_2_TEST_BLOCK_EN__POR,
+	[TOMTOM_A_TX_1_2_TXFE_CLKDIV] = TOMTOM_A_TX_1_2_TXFE_CLKDIV__POR,
+	[TOMTOM_A_TX_1_2_SAR_ERR_CH1] = TOMTOM_A_TX_1_2_SAR_ERR_CH1__POR,
+	[TOMTOM_A_TX_1_2_SAR_ERR_CH2] = TOMTOM_A_TX_1_2_SAR_ERR_CH2__POR,
+	[TOMTOM_A_TX_3_GAIN] = TOMTOM_A_TX_3_GAIN__POR,
+	[TOMTOM_A_TX_3_4_TEST_EN] = TOMTOM_A_TX_3_4_TEST_EN__POR,
+	[TOMTOM_A_TX_4_GAIN] = TOMTOM_A_TX_4_GAIN__POR,
+	[TOMTOM_A_TX_3_4_ADC_IB] = TOMTOM_A_TX_3_4_ADC_IB__POR,
+	[TOMTOM_A_TX_3_4_ATEST_REFCTRL] = TOMTOM_A_TX_3_4_ATEST_REFCTRL__POR,
+	[TOMTOM_A_TX_3_4_TEST_CTL] = TOMTOM_A_TX_3_4_TEST_CTL__POR,
+	[TOMTOM_A_TX_3_4_TEST_BLOCK_EN] = TOMTOM_A_TX_3_4_TEST_BLOCK_EN__POR,
+	[TOMTOM_A_TX_3_4_TXFE_CKDIV] = TOMTOM_A_TX_3_4_TXFE_CKDIV__POR,
+	[TOMTOM_A_TX_3_4_SAR_ERR_CH3] = TOMTOM_A_TX_3_4_SAR_ERR_CH3__POR,
+	[TOMTOM_A_TX_3_4_SAR_ERR_CH4] = TOMTOM_A_TX_3_4_SAR_ERR_CH4__POR,
+	[TOMTOM_A_TX_5_GAIN] = TOMTOM_A_TX_5_GAIN__POR,
+	[TOMTOM_A_TX_5_6_TEST_EN] = TOMTOM_A_TX_5_6_TEST_EN__POR,
+	[TOMTOM_A_TX_6_GAIN] = TOMTOM_A_TX_6_GAIN__POR,
+	[TOMTOM_A_TX_5_6_ADC_IB] = TOMTOM_A_TX_5_6_ADC_IB__POR,
+	[TOMTOM_A_TX_5_6_ATEST_REFCTRL] = TOMTOM_A_TX_5_6_ATEST_REFCTRL__POR,
+	[TOMTOM_A_TX_5_6_TEST_CTL] = TOMTOM_A_TX_5_6_TEST_CTL__POR,
+	[TOMTOM_A_TX_5_6_TEST_BLOCK_EN] = TOMTOM_A_TX_5_6_TEST_BLOCK_EN__POR,
+	[TOMTOM_A_TX_5_6_TXFE_CKDIV] = TOMTOM_A_TX_5_6_TXFE_CKDIV__POR,
+	[TOMTOM_A_TX_5_6_SAR_ERR_CH5] = TOMTOM_A_TX_5_6_SAR_ERR_CH5__POR,
+	[TOMTOM_A_TX_5_6_SAR_ERR_CH6] = TOMTOM_A_TX_5_6_SAR_ERR_CH6__POR,
+	[TOMTOM_A_TX_7_MBHC_EN] = TOMTOM_A_TX_7_MBHC_EN__POR,
+	[TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL] =
+				TOMTOM_A_TX_7_MBHC_ATEST_REFCTRL__POR,
+	[TOMTOM_A_TX_7_MBHC_ADC] = TOMTOM_A_TX_7_MBHC_ADC__POR,
+	[TOMTOM_A_TX_7_MBHC_TEST_CTL] = TOMTOM_A_TX_7_MBHC_TEST_CTL__POR,
+	[TOMTOM_A_TX_7_MBHC_SAR_ERR] = TOMTOM_A_TX_7_MBHC_SAR_ERR__POR,
+	[TOMTOM_A_TX_7_TXFE_CLKDIV] = TOMTOM_A_TX_7_TXFE_CLKDIV__POR,
+	[TOMTOM_A_RCO_CTRL] = TOMTOM_A_RCO_CTRL__POR,
+	[TOMTOM_A_RCO_CALIBRATION_CTRL1] = TOMTOM_A_RCO_CALIBRATION_CTRL1__POR,
+	[TOMTOM_A_RCO_CALIBRATION_CTRL2] = TOMTOM_A_RCO_CALIBRATION_CTRL2__POR,
+	[TOMTOM_A_RCO_CALIBRATION_CTRL3] = TOMTOM_A_RCO_CALIBRATION_CTRL3__POR,
+	[TOMTOM_A_RCO_TEST_CTRL] = TOMTOM_A_RCO_TEST_CTRL__POR,
+	[TOMTOM_A_RCO_CALIBRATION_RESULT1] =
+				TOMTOM_A_RCO_CALIBRATION_RESULT1__POR,
+	[TOMTOM_A_RCO_CALIBRATION_RESULT2] =
+				TOMTOM_A_RCO_CALIBRATION_RESULT2__POR,
+	[TOMTOM_A_BUCK_MODE_1] = TOMTOM_A_BUCK_MODE_1__POR,
+	[TOMTOM_A_BUCK_MODE_2] = TOMTOM_A_BUCK_MODE_2__POR,
+	[TOMTOM_A_BUCK_MODE_3] = TOMTOM_A_BUCK_MODE_3__POR,
+	[TOMTOM_A_BUCK_MODE_4] = TOMTOM_A_BUCK_MODE_4__POR,
+	[TOMTOM_A_BUCK_MODE_5] = TOMTOM_A_BUCK_MODE_5__POR,
+	[TOMTOM_A_BUCK_CTRL_VCL_1] = TOMTOM_A_BUCK_CTRL_VCL_1__POR,
+	[TOMTOM_A_BUCK_CTRL_VCL_2] = TOMTOM_A_BUCK_CTRL_VCL_2__POR,
+	[TOMTOM_A_BUCK_CTRL_VCL_3] = TOMTOM_A_BUCK_CTRL_VCL_3__POR,
+	[TOMTOM_A_BUCK_CTRL_CCL_1] = TOMTOM_A_BUCK_CTRL_CCL_1__POR,
+	[TOMTOM_A_BUCK_CTRL_CCL_2] = TOMTOM_A_BUCK_CTRL_CCL_2__POR,
+	[TOMTOM_A_BUCK_CTRL_CCL_3] = TOMTOM_A_BUCK_CTRL_CCL_3__POR,
+	[TOMTOM_A_BUCK_CTRL_CCL_4] = TOMTOM_A_BUCK_CTRL_CCL_4__POR,
+	[TOMTOM_A_BUCK_CTRL_PWM_DRVR_1] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_1__POR,
+	[TOMTOM_A_BUCK_CTRL_PWM_DRVR_2] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_2__POR,
+	[TOMTOM_A_BUCK_CTRL_PWM_DRVR_3] = TOMTOM_A_BUCK_CTRL_PWM_DRVR_3__POR,
+	[TOMTOM_A_BUCK_TMUX_A_D] = TOMTOM_A_BUCK_TMUX_A_D__POR,
+	[TOMTOM_A_NCP_BUCKREF] = TOMTOM_A_NCP_BUCKREF__POR,
+	[TOMTOM_A_NCP_EN] = TOMTOM_A_NCP_EN__POR,
+	[TOMTOM_A_NCP_CLK] = TOMTOM_A_NCP_CLK__POR,
+	[TOMTOM_A_NCP_STATIC] = TOMTOM_A_NCP_STATIC__POR,
+	[TOMTOM_A_NCP_VTH_LOW] = TOMTOM_A_NCP_VTH_LOW__POR,
+	[TOMTOM_A_NCP_VTH_HIGH] = TOMTOM_A_NCP_VTH_HIGH__POR,
+	[TOMTOM_A_NCP_ATEST] = TOMTOM_A_NCP_ATEST__POR,
+	[TOMTOM_A_NCP_DTEST] = TOMTOM_A_NCP_DTEST__POR,
+	[TOMTOM_A_NCP_DLY1] = TOMTOM_A_NCP_DLY1__POR,
+	[TOMTOM_A_NCP_DLY2] = TOMTOM_A_NCP_DLY2__POR,
+	[TOMTOM_A_RX_AUX_SW_CTL] = TOMTOM_A_RX_AUX_SW_CTL__POR,
+	[TOMTOM_A_RX_PA_AUX_IN_CONN] = TOMTOM_A_RX_PA_AUX_IN_CONN__POR,
+	[TOMTOM_A_RX_COM_TIMER_DIV] = TOMTOM_A_RX_COM_TIMER_DIV__POR,
+	[TOMTOM_A_RX_COM_OCP_CTL] = TOMTOM_A_RX_COM_OCP_CTL__POR,
+	[TOMTOM_A_RX_COM_OCP_COUNT] = TOMTOM_A_RX_COM_OCP_COUNT__POR,
+	[TOMTOM_A_RX_COM_DAC_CTL] = TOMTOM_A_RX_COM_DAC_CTL__POR,
+	[TOMTOM_A_RX_COM_BIAS] = TOMTOM_A_RX_COM_BIAS__POR,
+	[TOMTOM_A_RX_HPH_AUTO_CHOP] = TOMTOM_A_RX_HPH_AUTO_CHOP__POR,
+	[TOMTOM_A_RX_HPH_CHOP_CTL] = TOMTOM_A_RX_HPH_CHOP_CTL__POR,
+	[TOMTOM_A_RX_HPH_BIAS_PA] = TOMTOM_A_RX_HPH_BIAS_PA__POR,
+	[TOMTOM_A_RX_HPH_BIAS_LDO] = TOMTOM_A_RX_HPH_BIAS_LDO__POR,
+	[TOMTOM_A_RX_HPH_BIAS_CNP] = TOMTOM_A_RX_HPH_BIAS_CNP__POR,
+	[TOMTOM_A_RX_HPH_BIAS_WG_OCP] = TOMTOM_A_RX_HPH_BIAS_WG_OCP__POR,
+	[TOMTOM_A_RX_HPH_OCP_CTL] = TOMTOM_A_RX_HPH_OCP_CTL__POR,
+	[TOMTOM_A_RX_HPH_CNP_EN] = TOMTOM_A_RX_HPH_CNP_EN__POR,
+	[TOMTOM_A_RX_HPH_CNP_WG_CTL] = TOMTOM_A_RX_HPH_CNP_WG_CTL__POR,
+	[TOMTOM_A_RX_HPH_CNP_WG_TIME] = TOMTOM_A_RX_HPH_CNP_WG_TIME__POR,
+	[TOMTOM_A_RX_HPH_L_GAIN] = TOMTOM_A_RX_HPH_L_GAIN__POR,
+	[TOMTOM_A_RX_HPH_L_TEST] = TOMTOM_A_RX_HPH_L_TEST__POR,
+	[TOMTOM_A_RX_HPH_L_PA_CTL] = TOMTOM_A_RX_HPH_L_PA_CTL__POR,
+	[TOMTOM_A_RX_HPH_L_DAC_CTL] = TOMTOM_A_RX_HPH_L_DAC_CTL__POR,
+	[TOMTOM_A_RX_HPH_L_ATEST] = TOMTOM_A_RX_HPH_L_ATEST__POR,
+	[TOMTOM_A_RX_HPH_L_STATUS] = TOMTOM_A_RX_HPH_L_STATUS__POR,
+	[TOMTOM_A_RX_HPH_R_GAIN] = TOMTOM_A_RX_HPH_R_GAIN__POR,
+	[TOMTOM_A_RX_HPH_R_TEST] = TOMTOM_A_RX_HPH_R_TEST__POR,
+	[TOMTOM_A_RX_HPH_R_PA_CTL] = TOMTOM_A_RX_HPH_R_PA_CTL__POR,
+	[TOMTOM_A_RX_HPH_R_DAC_CTL] = TOMTOM_A_RX_HPH_R_DAC_CTL__POR,
+	[TOMTOM_A_RX_HPH_R_ATEST] = TOMTOM_A_RX_HPH_R_ATEST__POR,
+	[TOMTOM_A_RX_HPH_R_STATUS] = TOMTOM_A_RX_HPH_R_STATUS__POR,
+	[TOMTOM_A_RX_EAR_BIAS_PA] = TOMTOM_A_RX_EAR_BIAS_PA__POR,
+	[TOMTOM_A_RX_EAR_BIAS_CMBUFF] = TOMTOM_A_RX_EAR_BIAS_CMBUFF__POR,
+	[TOMTOM_A_RX_EAR_EN] = TOMTOM_A_RX_EAR_EN__POR,
+	[TOMTOM_A_RX_EAR_GAIN] = TOMTOM_A_RX_EAR_GAIN__POR,
+	[TOMTOM_A_RX_EAR_CMBUFF] = TOMTOM_A_RX_EAR_CMBUFF__POR,
+	[TOMTOM_A_RX_EAR_ICTL] = TOMTOM_A_RX_EAR_ICTL__POR,
+	[TOMTOM_A_RX_EAR_CCOMP] = TOMTOM_A_RX_EAR_CCOMP__POR,
+	[TOMTOM_A_RX_EAR_VCM] = TOMTOM_A_RX_EAR_VCM__POR,
+	[TOMTOM_A_RX_EAR_CNP] = TOMTOM_A_RX_EAR_CNP__POR,
+	[TOMTOM_A_RX_EAR_DAC_CTL_ATEST] = TOMTOM_A_RX_EAR_DAC_CTL_ATEST__POR,
+	[TOMTOM_A_RX_EAR_STATUS] = TOMTOM_A_RX_EAR_STATUS__POR,
+	[TOMTOM_A_RX_LINE_BIAS_PA] = TOMTOM_A_RX_LINE_BIAS_PA__POR,
+	[TOMTOM_A_RX_BUCK_BIAS1] = TOMTOM_A_RX_BUCK_BIAS1__POR,
+	[TOMTOM_A_RX_BUCK_BIAS2] = TOMTOM_A_RX_BUCK_BIAS2__POR,
+	[TOMTOM_A_RX_LINE_COM] = TOMTOM_A_RX_LINE_COM__POR,
+	[TOMTOM_A_RX_LINE_CNP_EN] = TOMTOM_A_RX_LINE_CNP_EN__POR,
+	[TOMTOM_A_RX_LINE_CNP_WG_CTL] = TOMTOM_A_RX_LINE_CNP_WG_CTL__POR,
+	[TOMTOM_A_RX_LINE_CNP_WG_TIME] = TOMTOM_A_RX_LINE_CNP_WG_TIME__POR,
+	[TOMTOM_A_RX_LINE_1_GAIN] = TOMTOM_A_RX_LINE_1_GAIN__POR,
+	[TOMTOM_A_RX_LINE_1_TEST] = TOMTOM_A_RX_LINE_1_TEST__POR,
+	[TOMTOM_A_RX_LINE_1_DAC_CTL] = TOMTOM_A_RX_LINE_1_DAC_CTL__POR,
+	[TOMTOM_A_RX_LINE_1_STATUS] = TOMTOM_A_RX_LINE_1_STATUS__POR,
+	[TOMTOM_A_RX_LINE_2_GAIN] = TOMTOM_A_RX_LINE_2_GAIN__POR,
+	[TOMTOM_A_RX_LINE_2_TEST] = TOMTOM_A_RX_LINE_2_TEST__POR,
+	[TOMTOM_A_RX_LINE_2_DAC_CTL] = TOMTOM_A_RX_LINE_2_DAC_CTL__POR,
+	[TOMTOM_A_RX_LINE_2_STATUS] = TOMTOM_A_RX_LINE_2_STATUS__POR,
+	[TOMTOM_A_RX_LINE_3_GAIN] = TOMTOM_A_RX_LINE_3_GAIN__POR,
+	[TOMTOM_A_RX_LINE_3_TEST] = TOMTOM_A_RX_LINE_3_TEST__POR,
+	[TOMTOM_A_RX_LINE_3_DAC_CTL] = TOMTOM_A_RX_LINE_3_DAC_CTL__POR,
+	[TOMTOM_A_RX_LINE_3_STATUS] = TOMTOM_A_RX_LINE_3_STATUS__POR,
+	[TOMTOM_A_RX_LINE_4_GAIN] = TOMTOM_A_RX_LINE_4_GAIN__POR,
+	[TOMTOM_A_RX_LINE_4_TEST] = TOMTOM_A_RX_LINE_4_TEST__POR,
+	[TOMTOM_A_RX_LINE_4_DAC_CTL] = TOMTOM_A_RX_LINE_4_DAC_CTL__POR,
+	[TOMTOM_A_RX_LINE_4_STATUS] = TOMTOM_A_RX_LINE_4_STATUS__POR,
+	[TOMTOM_A_RX_LINE_CNP_DBG] = TOMTOM_A_RX_LINE_CNP_DBG__POR,
+	[TOMTOM_A_SPKR_DRV1_EN] = TOMTOM_A_SPKR_DRV1_EN__POR,
+	[TOMTOM_A_SPKR_DRV1_GAIN] = TOMTOM_A_SPKR_DRV1_GAIN__POR,
+	[TOMTOM_A_SPKR_DRV1_DAC_CTL] = TOMTOM_A_SPKR_DRV1_DAC_CTL__POR,
+	[TOMTOM_A_SPKR_DRV1_OCP_CTL] = TOMTOM_A_SPKR_DRV1_OCP_CTL__POR,
+	[TOMTOM_A_SPKR_DRV1_CLIP_DET] = TOMTOM_A_SPKR_DRV1_CLIP_DET__POR,
+	[TOMTOM_A_SPKR_DRV1_IEC] = TOMTOM_A_SPKR_DRV1_IEC__POR,
+	[TOMTOM_A_SPKR_DRV1_DBG_DAC] = TOMTOM_A_SPKR_DRV1_DBG_DAC__POR,
+	[TOMTOM_A_SPKR_DRV1_DBG_PA] = TOMTOM_A_SPKR_DRV1_DBG_PA__POR,
+	[TOMTOM_A_SPKR_DRV1_DBG_PWRSTG] = TOMTOM_A_SPKR_DRV1_DBG_PWRSTG__POR,
+	[TOMTOM_A_SPKR_DRV1_BIAS_LDO] = TOMTOM_A_SPKR_DRV1_BIAS_LDO__POR,
+	[TOMTOM_A_SPKR_DRV1_BIAS_INT] = TOMTOM_A_SPKR_DRV1_BIAS_INT__POR,
+	[TOMTOM_A_SPKR_DRV1_BIAS_PA] = TOMTOM_A_SPKR_DRV1_BIAS_PA__POR,
+	[TOMTOM_A_SPKR_DRV1_STATUS_OCP] = TOMTOM_A_SPKR_DRV1_STATUS_OCP__POR,
+	[TOMTOM_A_SPKR_DRV1_STATUS_PA] = TOMTOM_A_SPKR_DRV1_STATUS_PA__POR,
+	[TOMTOM_A_SPKR1_PROT_EN] = TOMTOM_A_SPKR1_PROT_EN__POR,
+	[TOMTOM_A_SPKR1_PROT_ADC_TEST_EN] =
+				TOMTOM_A_SPKR1_PROT_ADC_TEST_EN__POR,
+	[TOMTOM_A_SPKR1_PROT_ATEST] = TOMTOM_A_SPKR1_PROT_ATEST__POR,
+	[TOMTOM_A_SPKR1_PROT_LDO_CTRL] = TOMTOM_A_SPKR1_PROT_LDO_CTRL__POR,
+	[TOMTOM_A_SPKR1_PROT_ISENSE_CTRL] =
+				TOMTOM_A_SPKR1_PROT_ISENSE_CTRL__POR,
+	[TOMTOM_A_SPKR1_PROT_VSENSE_CTRL] =
+				TOMTOM_A_SPKR1_PROT_VSENSE_CTRL__POR,
+	[TOMTOM_A_SPKR2_PROT_EN] = TOMTOM_A_SPKR2_PROT_EN__POR,
+	[TOMTOM_A_SPKR2_PROT_ADC_TEST_EN] =
+				TOMTOM_A_SPKR2_PROT_ADC_TEST_EN__POR,
+	[TOMTOM_A_SPKR2_PROT_ATEST] = TOMTOM_A_SPKR2_PROT_ATEST__POR,
+	[TOMTOM_A_SPKR2_PROT_LDO_CTRL] = TOMTOM_A_SPKR2_PROT_LDO_CTRL__POR,
+	[TOMTOM_A_SPKR2_PROT_ISENSE_CTRL] =
+				TOMTOM_A_SPKR2_PROT_ISENSE_CTRL__POR,
+	[TOMTOM_A_SPKR2_PROT_VSENSE_CTRL] =
+				TOMTOM_A_SPKR2_PROT_VSENSE_CTRL__POR,
+	[TOMTOM_A_MBHC_HPH] = TOMTOM_A_MBHC_HPH__POR,
+	[TOMTOM_A_CDC_ANC1_B1_CTL] = TOMTOM_A_CDC_ANC1_B1_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_B1_CTL] = TOMTOM_A_CDC_ANC2_B1_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_SHIFT] = TOMTOM_A_CDC_ANC1_SHIFT__POR,
+	[TOMTOM_A_CDC_ANC2_SHIFT] = TOMTOM_A_CDC_ANC2_SHIFT__POR,
+	[TOMTOM_A_CDC_ANC1_IIR_B1_CTL] = TOMTOM_A_CDC_ANC1_IIR_B1_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_IIR_B1_CTL] = TOMTOM_A_CDC_ANC2_IIR_B1_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_IIR_B2_CTL] = TOMTOM_A_CDC_ANC1_IIR_B2_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_IIR_B2_CTL] = TOMTOM_A_CDC_ANC2_IIR_B2_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_IIR_B3_CTL] = TOMTOM_A_CDC_ANC1_IIR_B3_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_IIR_B3_CTL] = TOMTOM_A_CDC_ANC2_IIR_B3_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_LPF_B1_CTL] = TOMTOM_A_CDC_ANC1_LPF_B1_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_LPF_B1_CTL] = TOMTOM_A_CDC_ANC2_LPF_B1_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_LPF_B2_CTL] = TOMTOM_A_CDC_ANC1_LPF_B2_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_LPF_B2_CTL] = TOMTOM_A_CDC_ANC2_LPF_B2_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_SPARE] = TOMTOM_A_CDC_ANC1_SPARE__POR,
+	[TOMTOM_A_CDC_ANC2_SPARE] = TOMTOM_A_CDC_ANC2_SPARE__POR,
+	[TOMTOM_A_CDC_ANC1_SMLPF_CTL] = TOMTOM_A_CDC_ANC1_SMLPF_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_SMLPF_CTL] = TOMTOM_A_CDC_ANC2_SMLPF_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_DCFLT_CTL] = TOMTOM_A_CDC_ANC1_DCFLT_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_DCFLT_CTL] = TOMTOM_A_CDC_ANC2_DCFLT_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_GAIN_CTL] = TOMTOM_A_CDC_ANC1_GAIN_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_GAIN_CTL] = TOMTOM_A_CDC_ANC2_GAIN_CTL__POR,
+	[TOMTOM_A_CDC_ANC1_B2_CTL] = TOMTOM_A_CDC_ANC1_B2_CTL__POR,
+	[TOMTOM_A_CDC_ANC2_B2_CTL] = TOMTOM_A_CDC_ANC2_B2_CTL__POR,
+	[TOMTOM_A_CDC_TX1_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX1_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX2_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX2_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX3_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX3_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX4_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX4_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX5_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX5_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX6_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX6_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX7_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX7_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX8_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX8_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX9_VOL_CTL_TIMER] = TOMTOM_A_CDC_TX9_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX10_VOL_CTL_TIMER] =
+				TOMTOM_A_CDC_TX10_VOL_CTL_TIMER__POR,
+	[TOMTOM_A_CDC_TX1_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX1_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX2_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX2_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX3_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX3_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX4_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX4_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX5_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX5_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX6_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX6_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX7_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX7_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX8_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX8_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX9_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX9_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX10_VOL_CTL_GAIN] = TOMTOM_A_CDC_TX10_VOL_CTL_GAIN__POR,
+	[TOMTOM_A_CDC_TX1_VOL_CTL_CFG] = TOMTOM_A_CDC_TX1_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX2_VOL_CTL_CFG] = TOMTOM_A_CDC_TX2_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX3_VOL_CTL_CFG] = TOMTOM_A_CDC_TX3_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX4_VOL_CTL_CFG] = TOMTOM_A_CDC_TX4_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX5_VOL_CTL_CFG] = TOMTOM_A_CDC_TX5_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX6_VOL_CTL_CFG] = TOMTOM_A_CDC_TX6_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX7_VOL_CTL_CFG] = TOMTOM_A_CDC_TX7_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX8_VOL_CTL_CFG] = TOMTOM_A_CDC_TX8_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX9_VOL_CTL_CFG] = TOMTOM_A_CDC_TX9_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX10_VOL_CTL_CFG] = TOMTOM_A_CDC_TX10_VOL_CTL_CFG__POR,
+	[TOMTOM_A_CDC_TX1_MUX_CTL] = TOMTOM_A_CDC_TX1_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX2_MUX_CTL] = TOMTOM_A_CDC_TX2_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX3_MUX_CTL] = TOMTOM_A_CDC_TX3_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX4_MUX_CTL] = TOMTOM_A_CDC_TX4_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX5_MUX_CTL] = TOMTOM_A_CDC_TX5_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX6_MUX_CTL] = TOMTOM_A_CDC_TX6_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX7_MUX_CTL] = TOMTOM_A_CDC_TX7_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX8_MUX_CTL] = TOMTOM_A_CDC_TX8_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX9_MUX_CTL] = TOMTOM_A_CDC_TX9_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX10_MUX_CTL] = TOMTOM_A_CDC_TX10_MUX_CTL__POR,
+	[TOMTOM_A_CDC_TX1_CLK_FS_CTL] = TOMTOM_A_CDC_TX1_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX2_CLK_FS_CTL] = TOMTOM_A_CDC_TX2_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX3_CLK_FS_CTL] = TOMTOM_A_CDC_TX3_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX4_CLK_FS_CTL] = TOMTOM_A_CDC_TX4_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX5_CLK_FS_CTL] = TOMTOM_A_CDC_TX5_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX6_CLK_FS_CTL] = TOMTOM_A_CDC_TX6_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX7_CLK_FS_CTL] = TOMTOM_A_CDC_TX7_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX8_CLK_FS_CTL] = TOMTOM_A_CDC_TX8_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX9_CLK_FS_CTL] = TOMTOM_A_CDC_TX9_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX10_CLK_FS_CTL] = TOMTOM_A_CDC_TX10_CLK_FS_CTL__POR,
+	[TOMTOM_A_CDC_TX1_DMIC_CTL] = TOMTOM_A_CDC_TX1_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX2_DMIC_CTL] = TOMTOM_A_CDC_TX2_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX3_DMIC_CTL] = TOMTOM_A_CDC_TX3_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX4_DMIC_CTL] = TOMTOM_A_CDC_TX4_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX5_DMIC_CTL] = TOMTOM_A_CDC_TX5_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX6_DMIC_CTL] = TOMTOM_A_CDC_TX6_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX7_DMIC_CTL] = TOMTOM_A_CDC_TX7_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX8_DMIC_CTL] = TOMTOM_A_CDC_TX8_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX9_DMIC_CTL] = TOMTOM_A_CDC_TX9_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_TX10_DMIC_CTL] = TOMTOM_A_CDC_TX10_DMIC_CTL__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL0] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL0__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL1] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL1__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL2] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL2__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL3] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL3__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL4] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL4__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL5] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL5__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL6] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL6__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_VAL7] = TOMTOM_A_CDC_SPKR_CLIPDET_VAL7__POR,
+	[TOMTOM_A_CDC_DEBUG_B1_CTL] = TOMTOM_A_CDC_DEBUG_B1_CTL__POR,
+	[TOMTOM_A_CDC_DEBUG_B2_CTL] = TOMTOM_A_CDC_DEBUG_B2_CTL__POR,
+	[TOMTOM_A_CDC_DEBUG_B3_CTL] = TOMTOM_A_CDC_DEBUG_B3_CTL__POR,
+	[TOMTOM_A_CDC_DEBUG_B4_CTL] = TOMTOM_A_CDC_DEBUG_B4_CTL__POR,
+	[TOMTOM_A_CDC_DEBUG_B5_CTL] = TOMTOM_A_CDC_DEBUG_B5_CTL__POR,
+	[TOMTOM_A_CDC_DEBUG_B6_CTL] = TOMTOM_A_CDC_DEBUG_B6_CTL__POR,
+	[TOMTOM_A_CDC_DEBUG_B7_CTL] = TOMTOM_A_CDC_DEBUG_B7_CTL__POR,
+	[TOMTOM_A_CDC_SRC1_PDA_CFG] = TOMTOM_A_CDC_SRC1_PDA_CFG__POR,
+	[TOMTOM_A_CDC_SRC2_PDA_CFG] = TOMTOM_A_CDC_SRC2_PDA_CFG__POR,
+	[TOMTOM_A_CDC_SRC1_FS_CTL] = TOMTOM_A_CDC_SRC1_FS_CTL__POR,
+	[TOMTOM_A_CDC_SRC2_FS_CTL] = TOMTOM_A_CDC_SRC2_FS_CTL__POR,
+	[TOMTOM_A_CDC_RX1_B1_CTL] = TOMTOM_A_CDC_RX1_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX2_B1_CTL] = TOMTOM_A_CDC_RX2_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX3_B1_CTL] = TOMTOM_A_CDC_RX3_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX4_B1_CTL] = TOMTOM_A_CDC_RX4_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX5_B1_CTL] = TOMTOM_A_CDC_RX5_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX6_B1_CTL] = TOMTOM_A_CDC_RX6_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX7_B1_CTL] = TOMTOM_A_CDC_RX7_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX1_B2_CTL] = TOMTOM_A_CDC_RX1_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX2_B2_CTL] = TOMTOM_A_CDC_RX2_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX3_B2_CTL] = TOMTOM_A_CDC_RX3_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX4_B2_CTL] = TOMTOM_A_CDC_RX4_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX5_B2_CTL] = TOMTOM_A_CDC_RX5_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX6_B2_CTL] = TOMTOM_A_CDC_RX6_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX7_B2_CTL] = TOMTOM_A_CDC_RX7_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX1_B3_CTL] = TOMTOM_A_CDC_RX1_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX2_B3_CTL] = TOMTOM_A_CDC_RX2_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX3_B3_CTL] = TOMTOM_A_CDC_RX3_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX4_B3_CTL] = TOMTOM_A_CDC_RX4_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX5_B3_CTL] = TOMTOM_A_CDC_RX5_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX6_B3_CTL] = TOMTOM_A_CDC_RX6_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX7_B3_CTL] = TOMTOM_A_CDC_RX7_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX1_B4_CTL] = TOMTOM_A_CDC_RX1_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX2_B4_CTL] = TOMTOM_A_CDC_RX2_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX3_B4_CTL] = TOMTOM_A_CDC_RX3_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX4_B4_CTL] = TOMTOM_A_CDC_RX4_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX5_B4_CTL] = TOMTOM_A_CDC_RX5_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX6_B4_CTL] = TOMTOM_A_CDC_RX6_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX7_B4_CTL] = TOMTOM_A_CDC_RX7_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX1_B5_CTL] = TOMTOM_A_CDC_RX1_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX2_B5_CTL] = TOMTOM_A_CDC_RX2_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX3_B5_CTL] = TOMTOM_A_CDC_RX3_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX4_B5_CTL] = TOMTOM_A_CDC_RX4_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX5_B5_CTL] = TOMTOM_A_CDC_RX5_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX6_B5_CTL] = TOMTOM_A_CDC_RX6_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX7_B5_CTL] = TOMTOM_A_CDC_RX7_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX1_B6_CTL] = TOMTOM_A_CDC_RX1_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX2_B6_CTL] = TOMTOM_A_CDC_RX2_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX3_B6_CTL] = TOMTOM_A_CDC_RX3_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX4_B6_CTL] = TOMTOM_A_CDC_RX4_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX5_B6_CTL] = TOMTOM_A_CDC_RX5_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX6_B6_CTL] = TOMTOM_A_CDC_RX6_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX7_B6_CTL] = TOMTOM_A_CDC_RX7_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX1_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX2_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX3_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX4_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX5_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX6_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX7_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_VBAT_CFG] = TOMTOM_A_CDC_VBAT_CFG__POR,
+	[TOMTOM_A_CDC_VBAT_ADC_CAL1] = TOMTOM_A_CDC_VBAT_ADC_CAL1__POR,
+	[TOMTOM_A_CDC_VBAT_ADC_CAL2] = TOMTOM_A_CDC_VBAT_ADC_CAL2__POR,
+	[TOMTOM_A_CDC_VBAT_ADC_CAL3] = TOMTOM_A_CDC_VBAT_ADC_CAL3__POR,
+	[TOMTOM_A_CDC_VBAT_PK_EST1] = TOMTOM_A_CDC_VBAT_PK_EST1__POR,
+	[TOMTOM_A_CDC_VBAT_PK_EST2] = TOMTOM_A_CDC_VBAT_PK_EST2__POR,
+	[TOMTOM_A_CDC_VBAT_PK_EST3] = TOMTOM_A_CDC_VBAT_PK_EST3__POR,
+	[TOMTOM_A_CDC_VBAT_RF_PROC1] = TOMTOM_A_CDC_VBAT_RF_PROC1__POR,
+	[TOMTOM_A_CDC_VBAT_RF_PROC2] = TOMTOM_A_CDC_VBAT_RF_PROC2__POR,
+	[TOMTOM_A_CDC_VBAT_TAC1] = TOMTOM_A_CDC_VBAT_TAC1__POR,
+	[TOMTOM_A_CDC_VBAT_TAC2] = TOMTOM_A_CDC_VBAT_TAC2__POR,
+	[TOMTOM_A_CDC_VBAT_TAC3] = TOMTOM_A_CDC_VBAT_TAC3__POR,
+	[TOMTOM_A_CDC_VBAT_TAC4] = TOMTOM_A_CDC_VBAT_TAC4__POR,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD1] = TOMTOM_A_CDC_VBAT_GAIN_UPD1__POR,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD2] = TOMTOM_A_CDC_VBAT_GAIN_UPD2__POR,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD3] = TOMTOM_A_CDC_VBAT_GAIN_UPD3__POR,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD4] = TOMTOM_A_CDC_VBAT_GAIN_UPD4__POR,
+	[TOMTOM_A_CDC_VBAT_DEBUG1] = TOMTOM_A_CDC_VBAT_DEBUG1__POR,
+	[TOMTOM_A_CDC_VBAT_GAIN_UPD_MON] = TOMTOM_A_CDC_VBAT_GAIN_UPD_MON__POR,
+	[TOMTOM_A_CDC_VBAT_GAIN_MON_VAL] = TOMTOM_A_CDC_VBAT_GAIN_MON_VAL__POR,
+	[TOMTOM_A_CDC_CLK_ANC_RESET_CTL] = TOMTOM_A_CDC_CLK_ANC_RESET_CTL__POR,
+	[TOMTOM_A_CDC_CLK_RX_RESET_CTL] = TOMTOM_A_CDC_CLK_RX_RESET_CTL__POR,
+	[TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL] =
+				TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL] =
+				TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL__POR,
+	[TOMTOM_A_CDC_CLK_RX_I2S_CTL] = TOMTOM_A_CDC_CLK_RX_I2S_CTL__POR,
+	[TOMTOM_A_CDC_CLK_TX_I2S_CTL] = TOMTOM_A_CDC_CLK_TX_I2S_CTL__POR,
+	[TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL] =
+				TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL] =
+				TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL__POR,
+	[TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL] =
+				TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL] =
+				TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL__POR,
+	[TOMTOM_A_CDC_CLK_OTHR_CTL] = TOMTOM_A_CDC_CLK_OTHR_CTL__POR,
+	[TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL] =
+				TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL__POR,
+	[TOMTOM_A_CDC_CLK_RX_B1_CTL] = TOMTOM_A_CDC_CLK_RX_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLK_RX_B2_CTL] = TOMTOM_A_CDC_CLK_RX_B2_CTL__POR,
+	[TOMTOM_A_CDC_CLK_MCLK_CTL] = TOMTOM_A_CDC_CLK_MCLK_CTL__POR,
+	[TOMTOM_A_CDC_CLK_PDM_CTL] = TOMTOM_A_CDC_CLK_PDM_CTL__POR,
+	[TOMTOM_A_CDC_CLK_SD_CTL] = TOMTOM_A_CDC_CLK_SD_CTL__POR,
+	[TOMTOM_A_CDC_CLSH_B1_CTL] = TOMTOM_A_CDC_CLSH_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLSH_B2_CTL] = TOMTOM_A_CDC_CLSH_B2_CTL__POR,
+	[TOMTOM_A_CDC_CLSH_B3_CTL] = TOMTOM_A_CDC_CLSH_B3_CTL__POR,
+	[TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS] =
+				TOMTOM_A_CDC_CLSH_BUCK_NCP_VARS__POR,
+	[TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD] =
+				TOMTOM_A_CDC_CLSH_IDLE_HPH_THSD__POR,
+	[TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD] =
+				TOMTOM_A_CDC_CLSH_IDLE_EAR_THSD__POR,
+	[TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD] =
+				TOMTOM_A_CDC_CLSH_FCLKONLY_HPH_THSD__POR,
+	[TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD] =
+				TOMTOM_A_CDC_CLSH_FCLKONLY_EAR_THSD__POR,
+	[TOMTOM_A_CDC_CLSH_K_ADDR] = TOMTOM_A_CDC_CLSH_K_ADDR__POR,
+	[TOMTOM_A_CDC_CLSH_K_DATA] = TOMTOM_A_CDC_CLSH_K_DATA__POR,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L] =
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_L__POR,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U] =
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_HPH_U__POR,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L] =
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_L__POR,
+	[TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U] =
+				TOMTOM_A_CDC_CLSH_I_PA_FACT_EAR_U__POR,
+	[TOMTOM_A_CDC_CLSH_V_PA_HD_EAR] = TOMTOM_A_CDC_CLSH_V_PA_HD_EAR__POR,
+	[TOMTOM_A_CDC_CLSH_V_PA_HD_HPH] = TOMTOM_A_CDC_CLSH_V_PA_HD_HPH__POR,
+	[TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR] = TOMTOM_A_CDC_CLSH_V_PA_MIN_EAR__POR,
+	[TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH] = TOMTOM_A_CDC_CLSH_V_PA_MIN_HPH__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B1_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B1_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B1_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B1_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B2_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B2_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B2_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B2_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B3_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B3_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B3_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B3_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B4_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B4_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B4_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B4_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B5_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B5_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B5_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B5_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B6_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B6_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B6_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B6_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B7_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B7_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B7_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B7_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_B8_CTL] = TOMTOM_A_CDC_IIR1_GAIN_B8_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_B8_CTL] = TOMTOM_A_CDC_IIR2_GAIN_B8_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_CTL] = TOMTOM_A_CDC_IIR1_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_CTL] = TOMTOM_A_CDC_IIR2_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL] =
+				TOMTOM_A_CDC_IIR1_GAIN_TIMER_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL] =
+				TOMTOM_A_CDC_IIR2_GAIN_TIMER_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_COEF_B1_CTL] = TOMTOM_A_CDC_IIR1_COEF_B1_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_COEF_B1_CTL] = TOMTOM_A_CDC_IIR2_COEF_B1_CTL__POR,
+	[TOMTOM_A_CDC_IIR1_COEF_B2_CTL] = TOMTOM_A_CDC_IIR1_COEF_B2_CTL__POR,
+	[TOMTOM_A_CDC_IIR2_COEF_B2_CTL] = TOMTOM_A_CDC_IIR2_COEF_B2_CTL__POR,
+	[TOMTOM_A_CDC_TOP_GAIN_UPDATE] = TOMTOM_A_CDC_TOP_GAIN_UPDATE__POR,
+	[TOMTOM_A_CDC_PA_RAMP_B1_CTL] = TOMTOM_A_CDC_PA_RAMP_B1_CTL__POR,
+	[TOMTOM_A_CDC_PA_RAMP_B2_CTL] = TOMTOM_A_CDC_PA_RAMP_B2_CTL__POR,
+	[TOMTOM_A_CDC_PA_RAMP_B3_CTL] = TOMTOM_A_CDC_PA_RAMP_B3_CTL__POR,
+	[TOMTOM_A_CDC_PA_RAMP_B4_CTL] = TOMTOM_A_CDC_PA_RAMP_B4_CTL__POR,
+	[TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL] =
+				TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_B1_CTL] = TOMTOM_A_CDC_COMP0_B1_CTL__POR,
+	[TOMTOM_A_CDC_COMP1_B1_CTL] = TOMTOM_A_CDC_COMP1_B1_CTL__POR,
+	[TOMTOM_A_CDC_COMP2_B1_CTL] = TOMTOM_A_CDC_COMP2_B1_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_B2_CTL] = TOMTOM_A_CDC_COMP0_B2_CTL__POR,
+	[TOMTOM_A_CDC_COMP1_B2_CTL] = TOMTOM_A_CDC_COMP1_B2_CTL__POR,
+	[TOMTOM_A_CDC_COMP2_B2_CTL] = TOMTOM_A_CDC_COMP2_B2_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_B3_CTL] = TOMTOM_A_CDC_COMP0_B3_CTL__POR,
+	[TOMTOM_A_CDC_COMP1_B3_CTL] = TOMTOM_A_CDC_COMP1_B3_CTL__POR,
+	[TOMTOM_A_CDC_COMP2_B3_CTL] = TOMTOM_A_CDC_COMP2_B3_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_B4_CTL] = TOMTOM_A_CDC_COMP0_B4_CTL__POR,
+	[TOMTOM_A_CDC_COMP1_B4_CTL] = TOMTOM_A_CDC_COMP1_B4_CTL__POR,
+	[TOMTOM_A_CDC_COMP2_B4_CTL] = TOMTOM_A_CDC_COMP2_B4_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_B5_CTL] = TOMTOM_A_CDC_COMP0_B5_CTL__POR,
+	[TOMTOM_A_CDC_COMP1_B5_CTL] = TOMTOM_A_CDC_COMP1_B5_CTL__POR,
+	[TOMTOM_A_CDC_COMP2_B5_CTL] = TOMTOM_A_CDC_COMP2_B5_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_B6_CTL] = TOMTOM_A_CDC_COMP0_B6_CTL__POR,
+	[TOMTOM_A_CDC_COMP1_B6_CTL] = TOMTOM_A_CDC_COMP1_B6_CTL__POR,
+	[TOMTOM_A_CDC_COMP2_B6_CTL] = TOMTOM_A_CDC_COMP2_B6_CTL__POR,
+	[TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS] =
+				TOMTOM_A_CDC_COMP0_SHUT_DOWN_STATUS__POR,
+	[TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS] =
+				TOMTOM_A_CDC_COMP1_SHUT_DOWN_STATUS__POR,
+	[TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS] =
+				TOMTOM_A_CDC_COMP2_SHUT_DOWN_STATUS__POR,
+	[TOMTOM_A_CDC_COMP0_FS_CFG] = TOMTOM_A_CDC_COMP0_FS_CFG__POR,
+	[TOMTOM_A_CDC_COMP1_FS_CFG] = TOMTOM_A_CDC_COMP1_FS_CFG__POR,
+	[TOMTOM_A_CDC_COMP2_FS_CFG] = TOMTOM_A_CDC_COMP2_FS_CFG__POR,
+	[TOMTOM_A_CDC_CONN_RX1_B1_CTL] = TOMTOM_A_CDC_CONN_RX1_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX1_B2_CTL] = TOMTOM_A_CDC_CONN_RX1_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX1_B3_CTL] = TOMTOM_A_CDC_CONN_RX1_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX2_B1_CTL] = TOMTOM_A_CDC_CONN_RX2_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX2_B2_CTL] = TOMTOM_A_CDC_CONN_RX2_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX2_B3_CTL] = TOMTOM_A_CDC_CONN_RX2_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX3_B1_CTL] = TOMTOM_A_CDC_CONN_RX3_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX3_B2_CTL] = TOMTOM_A_CDC_CONN_RX3_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX4_B1_CTL] = TOMTOM_A_CDC_CONN_RX4_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX4_B2_CTL] = TOMTOM_A_CDC_CONN_RX4_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX5_B1_CTL] = TOMTOM_A_CDC_CONN_RX5_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX5_B2_CTL] = TOMTOM_A_CDC_CONN_RX5_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX6_B1_CTL] = TOMTOM_A_CDC_CONN_RX6_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX6_B2_CTL] = TOMTOM_A_CDC_CONN_RX6_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX7_B1_CTL] = TOMTOM_A_CDC_CONN_RX7_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX7_B2_CTL] = TOMTOM_A_CDC_CONN_RX7_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX7_B3_CTL] = TOMTOM_A_CDC_CONN_RX7_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_ANC_B1_CTL] = TOMTOM_A_CDC_CONN_ANC_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_ANC_B2_CTL] = TOMTOM_A_CDC_CONN_ANC_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_B1_CTL] = TOMTOM_A_CDC_CONN_TX_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_B2_CTL] = TOMTOM_A_CDC_CONN_TX_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_B3_CTL] = TOMTOM_A_CDC_CONN_TX_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_B4_CTL] = TOMTOM_A_CDC_CONN_TX_B4_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ1_B1_CTL] = TOMTOM_A_CDC_CONN_EQ1_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ1_B2_CTL] = TOMTOM_A_CDC_CONN_EQ1_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ1_B3_CTL] = TOMTOM_A_CDC_CONN_EQ1_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ1_B4_CTL] = TOMTOM_A_CDC_CONN_EQ1_B4_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ2_B1_CTL] = TOMTOM_A_CDC_CONN_EQ2_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ2_B2_CTL] = TOMTOM_A_CDC_CONN_EQ2_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ2_B3_CTL] = TOMTOM_A_CDC_CONN_EQ2_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_EQ2_B4_CTL] = TOMTOM_A_CDC_CONN_EQ2_B4_CTL__POR,
+	[TOMTOM_A_CDC_CONN_SRC1_B1_CTL] = TOMTOM_A_CDC_CONN_SRC1_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_SRC1_B2_CTL] = TOMTOM_A_CDC_CONN_SRC1_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_SRC2_B1_CTL] = TOMTOM_A_CDC_CONN_SRC2_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_SRC2_B2_CTL] = TOMTOM_A_CDC_CONN_SRC2_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B1_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B2_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B3_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B3_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B4_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B4_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B5_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B5_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B6_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B6_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B7_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B7_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B8_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B8_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B9_CTL] = TOMTOM_A_CDC_CONN_TX_SB_B9_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B10_CTL] =
+				TOMTOM_A_CDC_CONN_TX_SB_B10_CTL__POR,
+	[TOMTOM_A_CDC_CONN_TX_SB_B11_CTL] =
+				TOMTOM_A_CDC_CONN_TX_SB_B11_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX_SB_B1_CTL] = TOMTOM_A_CDC_CONN_RX_SB_B1_CTL__POR,
+	[TOMTOM_A_CDC_CONN_RX_SB_B2_CTL] = TOMTOM_A_CDC_CONN_RX_SB_B2_CTL__POR,
+	[TOMTOM_A_CDC_CONN_CLSH_CTL] = TOMTOM_A_CDC_CONN_CLSH_CTL__POR,
+	[TOMTOM_A_CDC_CONN_MISC] = TOMTOM_A_CDC_CONN_MISC__POR,
+	[TOMTOM_A_CDC_CONN_RX8_B1_CTL] = TOMTOM_A_CDC_CONN_RX8_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL] =
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_CLIP_LEVEL_ADJUST__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_THRESHOLD_STATUS__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_SAMPLE_MARK__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL] =
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR2_CLIP_LEVEL_ADJUST__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS] =
+			TOMTOM_A_CDC_CLIP_ADJ_SPKR2_THRESHOLD_STATUS__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK] =
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR2_SAMPLE_MARK__POR,
+	[TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING] =
+				TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING__POR,
+	[TOMTOM_A_CDC_MBHC_EN_CTL] = TOMTOM_A_CDC_MBHC_EN_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_FIR_B1_CFG] = TOMTOM_A_CDC_MBHC_FIR_B1_CFG__POR,
+	[TOMTOM_A_CDC_MBHC_FIR_B2_CFG] = TOMTOM_A_CDC_MBHC_FIR_B2_CFG__POR,
+	[TOMTOM_A_CDC_MBHC_TIMER_B1_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B1_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_TIMER_B2_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B2_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_TIMER_B3_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B3_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_TIMER_B4_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B4_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_TIMER_B5_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B5_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_TIMER_B6_CTL] = TOMTOM_A_CDC_MBHC_TIMER_B6_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_B1_STATUS] = TOMTOM_A_CDC_MBHC_B1_STATUS__POR,
+	[TOMTOM_A_CDC_MBHC_B2_STATUS] = TOMTOM_A_CDC_MBHC_B2_STATUS__POR,
+	[TOMTOM_A_CDC_MBHC_B3_STATUS] = TOMTOM_A_CDC_MBHC_B3_STATUS__POR,
+	[TOMTOM_A_CDC_MBHC_B4_STATUS] = TOMTOM_A_CDC_MBHC_B4_STATUS__POR,
+	[TOMTOM_A_CDC_MBHC_B5_STATUS] = TOMTOM_A_CDC_MBHC_B5_STATUS__POR,
+	[TOMTOM_A_CDC_MBHC_B1_CTL] = TOMTOM_A_CDC_MBHC_B1_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_B2_CTL] = TOMTOM_A_CDC_MBHC_B2_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B1_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B1_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B2_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B2_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B3_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B3_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B4_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B4_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B5_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B5_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B6_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B6_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B7_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B7_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B8_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B8_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B9_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B9_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B10_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B10_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B11_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B11_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_VOLT_B12_CTL] = TOMTOM_A_CDC_MBHC_VOLT_B12_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_CLK_CTL] = TOMTOM_A_CDC_MBHC_CLK_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_INT_CTL] = TOMTOM_A_CDC_MBHC_INT_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_DEBUG_CTL] = TOMTOM_A_CDC_MBHC_DEBUG_CTL__POR,
+	[TOMTOM_A_CDC_MBHC_SPARE] = TOMTOM_A_CDC_MBHC_SPARE__POR,
+	[TOMTOM_A_CDC_RX8_B1_CTL] = TOMTOM_A_CDC_RX8_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX8_B2_CTL] = TOMTOM_A_CDC_RX8_B2_CTL__POR,
+	[TOMTOM_A_CDC_RX8_B3_CTL] = TOMTOM_A_CDC_RX8_B3_CTL__POR,
+	[TOMTOM_A_CDC_RX8_B4_CTL] = TOMTOM_A_CDC_RX8_B4_CTL__POR,
+	[TOMTOM_A_CDC_RX8_B5_CTL] = TOMTOM_A_CDC_RX8_B5_CTL__POR,
+	[TOMTOM_A_CDC_RX8_B6_CTL] = TOMTOM_A_CDC_RX8_B6_CTL__POR,
+	[TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL] =
+				TOMTOM_A_CDC_RX8_VOL_CTL_B1_CTL__POR,
+	[TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL] =
+				TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6__POR,
+	[TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7] =
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7__POR,
+	[TOMTOM_A_CDC_BOOST_MODE_CTL] = TOMTOM_A_CDC_BOOST_MODE_CTL__POR,
+	[TOMTOM_A_CDC_BOOST_THRESHOLD] = TOMTOM_A_CDC_BOOST_THRESHOLD__POR,
+	[TOMTOM_A_CDC_BOOST_TAP_SEL] = TOMTOM_A_CDC_BOOST_TAP_SEL__POR,
+	[TOMTOM_A_CDC_BOOST_HOLD_TIME] = TOMTOM_A_CDC_BOOST_HOLD_TIME__POR,
+	[TOMTOM_A_CDC_BOOST_TRGR_EN] = TOMTOM_A_CDC_BOOST_TRGR_EN__POR,
+};
diff --git a/sound/soc/codecs/wcd9330.c b/sound/soc/codecs/wcd9330.c
new file mode 100644
index 0000000..0b07393
--- /dev/null
+++ b/sound/soc/codecs/wcd9330.c
@@ -0,0 +1,9106 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include "wcd9330.h"
+#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
+#include "wcdcal-hwdep.h"
+#include "wcd_cpe_core.h"
+
+enum {
+	BUS_DOWN,
+	ADC1_TXFE,
+	ADC2_TXFE,
+	ADC3_TXFE,
+	ADC4_TXFE,
+	ADC5_TXFE,
+	ADC6_TXFE,
+	HPH_DELAY,
+};
+
+#define TOMTOM_MAD_SLIMBUS_TX_PORT 12
+#define TOMTOM_MAD_AUDIO_FIRMWARE_PATH "wcd9320/wcd9320_mad_audio.bin"
+#define TOMTOM_VALIDATE_RX_SBPORT_RANGE(port) ((port >= 16) && (port <= 23))
+#define TOMTOM_VALIDATE_TX_SBPORT_RANGE(port) ((port >= 0) && (port <= 15))
+#define TOMTOM_CONVERT_RX_SBPORT_ID(port) (port - 16) /* RX1 port ID = 0 */
+#define TOMTOM_BIT_ADJ_SHIFT_PORT1_6 4
+#define TOMTOM_BIT_ADJ_SHIFT_PORT7_10 5
+
+#define TOMTOM_HPH_PA_SETTLE_COMP_ON 10000
+#define TOMTOM_HPH_PA_SETTLE_COMP_OFF 13000
+#define TOMTOM_HPH_PA_RAMP_DELAY 30000
+
+#define TOMTOM_SVASS_INT_STATUS_RCO_WDOG 0x20
+#define TOMTOM_SVASS_INT_STATUS_WDOG_BITE 0x02
+
+/* Add any SVA IRQs that are to be treated as FATAL */
+#define TOMTOM_CPE_FATAL_IRQS \
+	(TOMTOM_SVASS_INT_STATUS_RCO_WDOG | \
+	 TOMTOM_SVASS_INT_STATUS_WDOG_BITE)
+
+#define DAPM_MICBIAS2_EXTERNAL_STANDALONE "MIC BIAS2 External Standalone"
+
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define TOMTOM_WG_TIME_FACTOR_US	240
+
+#define RX8_PATH 8
+#define HPH_PA_ENABLE true
+#define HPH_PA_DISABLE false
+
+#define SLIM_BW_CLK_GEAR_9 6200000
+#define SLIM_BW_UNVOTE 0
+
+static int cpe_debug_mode;
+module_param(cpe_debug_mode, int, 0664);
+MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode");
+
+static atomic_t kp_tomtom_priv;
+
+static int high_perf_mode;
+module_param(high_perf_mode, int, 0664);
+MODULE_PARM_DESC(high_perf_mode, "enable/disable class AB config for hph");
+
+static struct afe_param_slimbus_slave_port_cfg tomtom_slimbus_slave_port_cfg = {
+	.minor_version = 1,
+	.slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+	.slave_dev_pgd_la = 0,
+	.slave_dev_intfdev_la = 0,
+	.bit_width = 16,
+	.data_format = 0,
+	.num_channels = 1
+};
+
+static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_MAIN_CTL_1),
+		HW_MAD_AUDIO_ENABLE, 0x1, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_AUDIO_CTL_3),
+		HW_MAD_AUDIO_SLEEP_TIME, 0xF, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_MAD_AUDIO_CTL_4),
+		HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR_MODE),
+		MAD_AUDIO_INT_DEST_SELECT_REG, 0x4, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0),
+		MAD_AUDIO_INT_MASK_REG, 0x2, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0),
+		MAD_AUDIO_INT_STATUS_REG, 0x2, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0),
+		MAD_AUDIO_INT_CLEAR_REG, 0x2, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_TX_BASE),
+		SB_PGD_PORT_TX_WATERMARK_N, 0x1E, 8, 0x1
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_TX_BASE),
+		SB_PGD_PORT_TX_ENABLE_N, 0x1, 8, 0x1
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_RX_BASE),
+		SB_PGD_PORT_RX_WATERMARK_N, 0x1E, 8, 0x1
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_SB_PGD_PORT_RX_BASE),
+		SB_PGD_PORT_RX_ENABLE_N, 0x1, 8, 0x1
+	},
+	{	1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_IIR_B1_CTL),
+		AANC_FF_GAIN_ADAPTIVE, 0x4, 8, 0
+	},
+	{	1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_IIR_B1_CTL),
+		AANC_FFGAIN_ADAPTIVE_EN, 0x8, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_CDC_ANC1_GAIN_CTL),
+		AANC_GAIN_CONTROL, 0xFF, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0),
+		MAD_CLIP_INT_MASK_REG, 0x10, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_MASK0),
+		MAD2_CLIP_INT_MASK_REG, 0x20, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0),
+		MAD_CLIP_INT_STATUS_REG, 0x10, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_STATUS0),
+		MAD2_CLIP_INT_STATUS_REG, 0x20, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0),
+		MAD_CLIP_INT_CLEAR_REG, 0x10, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET + TOMTOM_A_INTR2_CLEAR0),
+		MAD2_CLIP_INT_CLEAR_REG, 0x20, 8, 0
+	},
+};
+
+static struct afe_param_cdc_reg_cfg clip_reg_cfg[] = {
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				 TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL),
+		SPKR_CLIP_PIPE_BANK_SEL, 0x3, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL0),
+		SPKR_CLIPDET_VAL0, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL1),
+		SPKR_CLIPDET_VAL1, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL2),
+		SPKR_CLIPDET_VAL2, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL3),
+		SPKR_CLIPDET_VAL3, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL4),
+		SPKR_CLIPDET_VAL4, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL5),
+		SPKR_CLIPDET_VAL5, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL6),
+		SPKR_CLIPDET_VAL6, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR_CLIPDET_VAL7),
+		SPKR_CLIPDET_VAL7, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				 TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL),
+		SPKR2_CLIP_PIPE_BANK_SEL, 0x3, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL0),
+		SPKR2_CLIPDET_VAL0, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL1),
+		SPKR2_CLIPDET_VAL1, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL2),
+		SPKR2_CLIPDET_VAL2, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL3),
+		SPKR2_CLIPDET_VAL3, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL4),
+		SPKR2_CLIPDET_VAL4, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL5),
+		SPKR2_CLIPDET_VAL5, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL6),
+		SPKR2_CLIPDET_VAL6, 0xff, 8, 0
+	},
+	{
+		1,
+		(TOMTOM_REGISTER_START_OFFSET +
+				TOMTOM_A_CDC_SPKR2_CLIPDET_VAL7),
+		SPKR2_CLIPDET_VAL7, 0xff, 8, 0
+	},
+};
+
+static struct afe_param_cdc_reg_cfg_data tomtom_audio_reg_cfg = {
+	.num_registers = ARRAY_SIZE(audio_reg_cfg),
+	.reg_data = audio_reg_cfg,
+};
+
+static struct afe_param_cdc_reg_cfg_data tomtom_clip_reg_cfg = {
+	.num_registers = ARRAY_SIZE(clip_reg_cfg),
+	.reg_data = clip_reg_cfg,
+};
+
+static struct afe_param_id_cdc_aanc_version tomtom_cdc_aanc_version = {
+	.cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+	.aanc_hw_version        = AANC_HW_BLOCK_VERSION_2,
+};
+
+static struct afe_param_id_clip_bank_sel clip_bank_sel = {
+	.minor_version = AFE_API_VERSION_CLIP_BANK_SEL_CFG,
+	.num_banks = AFE_CLIP_MAX_BANKS,
+	.bank_map = {0, 1, 2, 3},
+};
+
+#define WCD9330_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+			SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define NUM_DECIMATORS 10
+#define NUM_INTERPOLATORS 8
+#define BITS_PER_REG 8
+#define TOMTOM_TX_PORT_NUMBER	16
+#define TOMTOM_RX_PORT_START_NUMBER	16
+
+#define TOMTOM_I2S_MASTER_MODE_MASK 0x08
+
+#define TOMTOM_SLIM_CLOSE_TIMEOUT 1000
+#define TOMTOM_SLIM_IRQ_OVERFLOW (1 << 0)
+#define TOMTOM_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define TOMTOM_SLIM_IRQ_PORT_CLOSED (1 << 2)
+#define TOMTOM_MCLK_CLK_12P288MHZ 12288000
+#define TOMTOM_MCLK_CLK_9P6MHZ 9600000
+
+#define TOMTOM_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+			SNDRV_PCM_FORMAT_S24_LE)
+
+#define TOMTOM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+#define TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 (TOMTOM_SLIM_PGD_PORT_INT_EN0 + 2)
+#define TOMTOM_ZDET_BOX_CAR_AVG_LOOP_COUNT 1
+#define TOMTOM_ZDET_MUL_FACTOR_1X 7218
+#define TOMTOM_ZDET_MUL_FACTOR_10X (TOMTOM_ZDET_MUL_FACTOR_1X * 10)
+#define TOMTOM_ZDET_MUL_FACTOR_100X (TOMTOM_ZDET_MUL_FACTOR_1X * 100)
+#define TOMTOM_ZDET_ERROR_APPROX_MUL_FACTOR 655
+#define TOMTOM_ZDET_ERROR_APPROX_SHIFT 16
+#define TOMTOM_ZDET_ZONE_3_DEFAULT_VAL 1000000
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	AIF4_VIFEED,
+	AIF4_MAD_TX,
+	NUM_CODEC_DAIS,
+};
+
+enum {
+	RX_MIX1_INP_SEL_ZERO = 0,
+	RX_MIX1_INP_SEL_SRC1,
+	RX_MIX1_INP_SEL_SRC2,
+	RX_MIX1_INP_SEL_IIR1,
+	RX_MIX1_INP_SEL_IIR2,
+	RX_MIX1_INP_SEL_RX1,
+	RX_MIX1_INP_SEL_RX2,
+	RX_MIX1_INP_SEL_RX3,
+	RX_MIX1_INP_SEL_RX4,
+	RX_MIX1_INP_SEL_RX5,
+	RX_MIX1_INP_SEL_RX6,
+	RX_MIX1_INP_SEL_RX7,
+	RX_MIX1_INP_SEL_AUXRX,
+};
+enum {
+	RX8_MIX1_INP_SEL_ZERO = 0,
+	RX8_MIX1_INP_SEL_IIR1,
+	RX8_MIX1_INP_SEL_IIR2,
+	RX8_MIX1_INP_SEL_RX1,
+	RX8_MIX1_INP_SEL_RX2,
+	RX8_MIX1_INP_SEL_RX3,
+	RX8_MIX1_INP_SEL_RX4,
+	RX8_MIX1_INP_SEL_RX5,
+	RX8_MIX1_INP_SEL_RX6,
+	RX8_MIX1_INP_SEL_RX7,
+	RX8_MIX1_INP_SEL_RX8,
+};
+
+#define TOMTOM_COMP_DIGITAL_GAIN_OFFSET 3
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+static struct snd_soc_dai_driver tomtom_dai[];
+static const DECLARE_TLV_DB_SCALE(aux_pga_gain, 0, 2, 0);
+
+/* Codec supports 2 IIR filters */
+enum {
+	IIR1 = 0,
+	IIR2,
+	IIR_MAX,
+};
+/* Codec supports 5 bands */
+enum {
+	BAND1 = 0,
+	BAND2,
+	BAND3,
+	BAND4,
+	BAND5,
+	BAND_MAX,
+};
+
+enum {
+	COMPANDER_0,
+	COMPANDER_1,
+	COMPANDER_2,
+	COMPANDER_MAX,
+};
+
+enum {
+	COMPANDER_FS_8KHZ = 0,
+	COMPANDER_FS_16KHZ,
+	COMPANDER_FS_32KHZ,
+	COMPANDER_FS_48KHZ,
+	COMPANDER_FS_96KHZ,
+	COMPANDER_FS_192KHZ,
+	COMPANDER_FS_MAX,
+};
+
+struct comp_sample_dependent_params {
+	u32 peak_det_timeout;
+	u32 rms_meter_div_fact;
+	u32 rms_meter_resamp_fact;
+};
+
+struct hpf_work {
+	struct tomtom_priv *tomtom;
+	u32 decimator;
+	u8 tx_hpf_cut_of_freq;
+	bool tx_hpf_bypass;
+	struct delayed_work dwork;
+};
+
+static struct hpf_work tx_hpf_work[NUM_DECIMATORS];
+
+static const struct wcd9xxx_ch tomtom_rx_chs[TOMTOM_RX_MAX] = {
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER, 0),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 1, 1),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 2, 2),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 3, 3),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 4, 4),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 5, 5),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 6, 6),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 7, 7),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 8, 8),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 9, 9),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 10, 10),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 11, 11),
+	WCD9XXX_CH(TOMTOM_RX_PORT_START_NUMBER + 12, 12),
+};
+
+static const struct wcd9xxx_ch tomtom_tx_chs[TOMTOM_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9),
+	WCD9XXX_CH(10, 10),
+	WCD9XXX_CH(11, 11),
+	WCD9XXX_CH(12, 12),
+	WCD9XXX_CH(13, 13),
+	WCD9XXX_CH(14, 14),
+	WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_check_table[NUM_CODEC_DAIS] = {
+	0,					/* AIF1_PB */
+	(1 << AIF2_CAP) | (1 << AIF3_CAP),	/* AIF1_CAP */
+	0,					/* AIF2_PB */
+	(1 << AIF1_CAP) | (1 << AIF3_CAP),	/* AIF2_CAP */
+	0,					/* AIF3_PB */
+	(1 << AIF1_CAP) | (1 << AIF2_CAP),	/* AIF3_CAP */
+};
+
+static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = {
+	0,	/* AIF1_PB */
+	0,	/* AIF1_CAP */
+	0,	/* AIF2_PB */
+	0,	/* AIF2_CAP */
+};
+
+/*
+ * Interrupt table for v3 corresponds to newer version
+ * codecs (wcd9330)
+ */
+static const struct intr_data wcd9330_intr_tbl[] = {
+	{WCD9XXX_IRQ_SLIMBUS, false},
+	{WCD9XXX_IRQ_MBHC_INSERTION, true},
+	{WCD9XXX_IRQ_MBHC_POTENTIAL, true},
+	{WCD9XXX_IRQ_MBHC_RELEASE, true},
+	{WCD9XXX_IRQ_MBHC_PRESS, true},
+	{WCD9XXX_IRQ_MBHC_SHORT_TERM, true},
+	{WCD9XXX_IRQ_MBHC_REMOVAL, true},
+	{WCD9330_IRQ_MBHC_JACK_SWITCH, true},
+	{WCD9XXX_IRQ_BG_PRECHARGE, false},
+	{WCD9XXX_IRQ_PA1_STARTUP, false},
+	{WCD9XXX_IRQ_PA2_STARTUP, false},
+	{WCD9XXX_IRQ_PA3_STARTUP, false},
+	{WCD9XXX_IRQ_PA4_STARTUP, false},
+	{WCD9XXX_IRQ_PA5_STARTUP, false},
+	{WCD9XXX_IRQ_MICBIAS1_PRECHARGE, false},
+	{WCD9XXX_IRQ_MICBIAS2_PRECHARGE, false},
+	{WCD9XXX_IRQ_MICBIAS3_PRECHARGE, false},
+	{WCD9XXX_IRQ_HPH_PA_OCPL_FAULT, false},
+	{WCD9XXX_IRQ_HPH_PA_OCPR_FAULT, false},
+	{WCD9XXX_IRQ_EAR_PA_OCPL_FAULT, false},
+	{WCD9XXX_IRQ_HPH_L_PA_STARTUP, false},
+	{WCD9XXX_IRQ_HPH_R_PA_STARTUP, false},
+	{WCD9320_IRQ_EAR_PA_STARTUP, false},
+	{WCD9330_IRQ_SVASS_ERR_EXCEPTION, false},
+	{WCD9330_IRQ_SVASS_ENGINE, true},
+	{WCD9330_IRQ_MAD_AUDIO, false},
+	{WCD9330_IRQ_MAD_BEACON, false},
+	{WCD9330_IRQ_MAD_ULTRASOUND, false},
+	{WCD9330_IRQ_SPEAKER1_CLIPPING, false},
+	{WCD9330_IRQ_SPEAKER2_CLIPPING, false},
+	{WCD9330_IRQ_VBAT_MONITOR_ATTACK, false},
+	{WCD9330_IRQ_VBAT_MONITOR_RELEASE, false},
+};
+
+struct tomtom_priv {
+	struct snd_soc_codec *codec;
+	u32 adc_count;
+	u32 rx_bias_count;
+	s32 dmic_1_2_clk_cnt;
+	s32 dmic_3_4_clk_cnt;
+	s32 dmic_5_6_clk_cnt;
+	s32 ldo_h_users;
+	s32 micb_2_users;
+
+	u32 anc_slot;
+	bool anc_func;
+
+	/* cal info for codec */
+	struct fw_info *fw_data;
+
+	/*track tomtom interface type*/
+	u8 intf_type;
+
+	/* num of slim ports required */
+	struct wcd9xxx_codec_dai_data  dai[NUM_CODEC_DAIS];
+
+	/*compander*/
+	int comp_enabled[COMPANDER_MAX];
+	u32 comp_fs[COMPANDER_MAX];
+
+	/* Maintain the status of AUX PGA */
+	int aux_pga_cnt;
+	u8 aux_l_gain;
+	u8 aux_r_gain;
+
+	bool spkr_pa_widget_on;
+	struct regulator *spkdrv_reg;
+	struct regulator *spkdrv2_reg;
+
+	bool mbhc_started;
+
+	struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
+	/* resmgr module */
+	struct wcd9xxx_resmgr resmgr;
+	/* mbhc module */
+	struct wcd9xxx_mbhc mbhc;
+
+	/* class h specific data */
+	struct wcd9xxx_clsh_cdc_data clsh_d;
+
+	int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
+			enum wcd9xxx_codec_event);
+	int (*codec_ext_clk_en_cb)(struct snd_soc_codec *codec,
+			int enable, bool dapm);
+	int (*codec_get_ext_clk_cnt)(void);
+	/*
+	 * list used to save/restore registers at start and
+	 * end of impedance measurement
+	 */
+	struct list_head reg_save_restore;
+
+	/* handle to cpe core */
+	struct wcd_cpe_core *cpe_core;
+
+	/* UHQA (class AB) mode */
+	u8 uhqa_mode;
+
+	/* Multiplication factor used for impedance detection */
+	int zdet_gain_mul_fact;
+
+	/* to track the status */
+	unsigned long status_mask;
+
+	int ext_clk_users;
+	struct clk *wcd_ext_clk;
+
+	/* Port values for Rx and Tx codec_dai */
+	unsigned int rx_port_value;
+	unsigned int tx_port_value;
+
+	struct mutex codec_mutex;
+};
+
+static const u32 comp_shift[] = {
+	4, /* Compander 0's clock source is on interpolator 7 */
+	0,
+	2,
+};
+
+static const int comp_rx_path[] = {
+	COMPANDER_1,
+	COMPANDER_1,
+	COMPANDER_2,
+	COMPANDER_2,
+	COMPANDER_2,
+	COMPANDER_2,
+	COMPANDER_0,
+	COMPANDER_0,
+	COMPANDER_MAX,
+};
+
+static const struct comp_sample_dependent_params comp_samp_params[] = {
+	{
+		/* 8 Khz */
+		.peak_det_timeout = 0x06,
+		.rms_meter_div_fact = 0x09,
+		.rms_meter_resamp_fact = 0x06,
+	},
+	{
+		/* 16 Khz */
+		.peak_det_timeout = 0x07,
+		.rms_meter_div_fact = 0x0A,
+		.rms_meter_resamp_fact = 0x0C,
+	},
+	{
+		/* 32 Khz */
+		.peak_det_timeout = 0x08,
+		.rms_meter_div_fact = 0x0B,
+		.rms_meter_resamp_fact = 0x1E,
+	},
+	{
+		/* 48 Khz */
+		.peak_det_timeout = 0x09,
+		.rms_meter_div_fact = 0x0B,
+		.rms_meter_resamp_fact = 0x28,
+	},
+	{
+		/* 96 Khz */
+		.peak_det_timeout = 0x0A,
+		.rms_meter_div_fact = 0x0C,
+		.rms_meter_resamp_fact = 0x50,
+	},
+	{
+		/* 192 Khz */
+		.peak_det_timeout = 0x0B,
+		.rms_meter_div_fact = 0xC,
+		.rms_meter_resamp_fact = 0xA0,
+	},
+};
+
+static unsigned short rx_digital_gain_reg[] = {
+	TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL,
+	TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL,
+};
+
+
+static unsigned short tx_digital_gain_reg[] = {
+	TOMTOM_A_CDC_TX1_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX2_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX3_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX4_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX5_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX6_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX7_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX8_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX9_VOL_CTL_GAIN,
+	TOMTOM_A_CDC_TX10_VOL_CTL_GAIN,
+};
+
+/*
+ * wcd9330_get_codec_info: Get codec specific information
+ *
+ * @wcd9xxx: pointer to wcd9xxx structure
+ * @wcd_type: pointer to wcd9xxx_codec_type structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9330_get_codec_info(struct wcd9xxx *wcd9xxx,
+			   struct wcd9xxx_codec_type *wcd_type)
+{
+	u16 id_minor, id_major;
+	struct regmap *wcd_regmap;
+	int rc, val, version = 0;
+
+	if (!wcd9xxx || !wcd_type)
+		return -EINVAL;
+
+	if (!wcd9xxx->regmap) {
+		dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+			__func__);
+		return -EINVAL;
+	}
+	wcd_regmap = wcd9xxx->regmap;
+	rc = regmap_bulk_read(wcd_regmap, TOMTOM_A_CHIP_ID_BYTE_0,
+			      (u8 *)&id_minor, sizeof(u16));
+	if (rc)
+		return -EINVAL;
+
+	rc = regmap_bulk_read(wcd_regmap, TOMTOM_A_CHIP_ID_BYTE_2,
+			      (u8 *)&id_major, sizeof(u16));
+	if (rc)
+		return -EINVAL;
+
+	dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
+		 __func__, id_major, id_minor);
+
+	if (id_minor == cpu_to_le16(0x1))
+		version = 2;
+	else if (id_minor == cpu_to_le16(0x0))
+		version = 1;
+	else
+		dev_err(wcd9xxx->dev, "%s: wcd9330 version unknown (major 0x%x, minor 0x%x)\n",
+			__func__, id_major, id_minor);
+
+	/* Fill codec type info */
+	wcd_type->id_major = id_major;
+	wcd_type->id_minor = id_minor;
+	wcd_type->num_irqs = WCD9330_NUM_IRQS;
+	wcd_type->version = version;
+	wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
+	wcd_type->i2c_chip_status = 0x01;
+	wcd_type->intr_tbl = wcd9330_intr_tbl;
+	wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9330_intr_tbl);
+
+	wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
+						TOMTOM_A_INTR1_STATUS0;
+	wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
+						TOMTOM_A_INTR1_CLEAR0;
+	wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
+						TOMTOM_A_INTR1_MASK0;
+	wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
+						TOMTOM_A_INTR1_LEVEL0;
+	wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
+						TOMTOM_A_INTR_MODE;
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9330_get_codec_info);
+
+/*
+ * wcd9330_bringdown: Bringdown WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9330_bringdown(struct wcd9xxx *wcd9xxx)
+{
+	if (!wcd9xxx || !wcd9xxx->regmap)
+		return -EINVAL;
+
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x7);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x6);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0xe);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x8);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9330_bringdown);
+
+/*
+ * wcd9330_bringup: Bring up WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9330_bringup(struct wcd9xxx *wcd9xxx)
+{
+	if (!wcd9xxx || !wcd9xxx->regmap)
+		return -EINVAL;
+
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x4);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x0);
+	/* wait for 5ms after codec reset for it to complete */
+	usleep_range(5000, 5100);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x1);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_LEAKAGE_CTL, 0x3);
+	regmap_write(wcd9xxx->regmap, TOMTOM_A_CDC_CTL, 0x3);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9330_bringup);
+
+int tomtom_enable_qfuse_sensing(struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	if (tomtom->wcd_ext_clk)
+		tomtom_codec_mclk_enable(codec, true, false);
+
+	snd_soc_write(codec, TOMTOM_A_QFUSE_CTL, 0x03);
+	/*
+	 * 5ms sleep required after enabling qfuse control
+	 * before checking the status.
+	 */
+	usleep_range(5000, 5500);
+	if ((snd_soc_read(codec, TOMTOM_A_QFUSE_STATUS) & (0x03)) != 0x03)
+		WARN(1, "%s: Qfuse sense is not complete\n", __func__);
+
+	if (tomtom->wcd_ext_clk)
+		tomtom_codec_mclk_enable(codec, false, false);
+	return 0;
+}
+EXPORT_SYMBOL(tomtom_enable_qfuse_sensing);
+
+static int tomtom_get_sample_rate(struct snd_soc_codec *codec, int path)
+{
+	if (path == RX8_PATH)
+		return snd_soc_read(codec, TOMTOM_A_CDC_RX8_B5_CTL);
+	else
+		return snd_soc_read(codec,
+			(TOMTOM_A_CDC_RX1_B5_CTL + 8 * (path - 1)));
+}
+
+static int tomtom_compare_bit_format(struct snd_soc_codec *codec,
+				int bit_format)
+{
+	int i = 0;
+	int ret = 0;
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	for (i = 0; i < NUM_CODEC_DAIS; i++) {
+		if (tomtom_p->dai[i].bit_width == bit_format) {
+			ret = 1;
+			break;
+		}
+	}
+	return ret;
+}
+
+static int tomtom_update_uhqa_mode(struct snd_soc_codec *codec, int path)
+{
+	int ret = 0;
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	/* UHQA path has fs=192KHz & bit=24 bit */
+	if (((tomtom_get_sample_rate(codec, path) & 0xE0) == 0xA0) &&
+		(tomtom_compare_bit_format(codec, 24))) {
+		tomtom_p->uhqa_mode = 1;
+	} else {
+		tomtom_p->uhqa_mode = 0;
+	}
+	dev_dbg(codec->dev, "%s: uhqa_mode=%d", __func__, tomtom_p->uhqa_mode);
+	return ret;
+}
+
+static int tomtom_get_anc_slot(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tomtom->anc_slot;
+	return 0;
+}
+
+static int tomtom_put_anc_slot(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	tomtom->anc_slot = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int tomtom_get_anc_func(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = (tomtom->anc_func == true ? 1 : 0);
+	return 0;
+}
+
+static int tomtom_put_anc_func(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm =
+				snd_soc_codec_get_dapm(codec);
+
+	mutex_lock(&tomtom->codec_mutex);
+	tomtom->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+
+	dev_dbg(codec->dev, "%s: anc_func %x", __func__, tomtom->anc_func);
+
+	if (tomtom->anc_func == true) {
+		snd_soc_dapm_enable_pin(dapm, "ANC HPHR");
+		snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
+		snd_soc_dapm_enable_pin(dapm, "ANC HEADPHONE");
+		snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+		snd_soc_dapm_disable_pin(dapm, "HPHR");
+		snd_soc_dapm_disable_pin(dapm, "HPHL");
+		snd_soc_dapm_disable_pin(dapm, "HEADPHONE");
+		snd_soc_dapm_disable_pin(dapm, "EAR PA");
+		snd_soc_dapm_disable_pin(dapm, "EAR");
+	} else {
+		snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+		snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+		snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE");
+		snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+		snd_soc_dapm_enable_pin(dapm, "HPHR");
+		snd_soc_dapm_enable_pin(dapm, "HPHL");
+		snd_soc_dapm_enable_pin(dapm, "HEADPHONE");
+		snd_soc_dapm_enable_pin(dapm, "EAR PA");
+		snd_soc_dapm_enable_pin(dapm, "EAR");
+	}
+	mutex_unlock(&tomtom->codec_mutex);
+	snd_soc_dapm_sync(dapm);
+	return 0;
+}
+
+static int tomtom_get_iir_enable_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+		(1 << band_idx)) != 0;
+
+	pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int tomtom_put_iir_enable_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	int value = ucontrol->value.integer.value[0];
+
+	/* Mask first 5 bits, 6-8 are reserved */
+	snd_soc_update_bits(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx),
+		(1 << band_idx), (value << band_idx));
+
+	pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx,
+		((snd_soc_read(codec, (TOMTOM_A_CDC_IIR1_CTL + 16 * iir_idx)) &
+		(1 << band_idx)) != 0));
+	return 0;
+}
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+				int iir_idx, int band_idx,
+				int coeff_idx)
+{
+	uint32_t value = 0;
+
+	/* Address does not automatically update if reading */
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t)) & 0x7F);
+
+	value |= snd_soc_read(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx));
+
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 1) & 0x7F);
+
+	value |= (snd_soc_read(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 8);
+
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 2) & 0x7F);
+
+	value |= (snd_soc_read(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) << 16);
+
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 3) & 0x7F);
+
+	/* Mask bits top 2 bits since they are reserved */
+	value |= ((snd_soc_read(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx)) & 0x3F) << 24);
+
+	return value;
+}
+
+static int tomtom_get_iir_band_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	ucontrol->value.integer.value[0] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+	ucontrol->value.integer.value[1] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+	ucontrol->value.integer.value[2] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+	ucontrol->value.integer.value[3] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+	ucontrol->value.integer.value[4] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+	pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[1],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[2],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[3],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[4]);
+	return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+				int iir_idx, int band_idx,
+				uint32_t value)
+{
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+		(value & 0xFF));
+
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 8) & 0xFF);
+
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 16) & 0xFF);
+
+	/* Mask top 2 bits, 7-8 are reserved */
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 24) & 0x3F);
+}
+
+static int tomtom_put_iir_band_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	/* Mask top bit it is reserved */
+	/* Updates addr automatically for each B2 write */
+	snd_soc_write(codec,
+		(TOMTOM_A_CDC_IIR1_COEF_B1_CTL + 16 * iir_idx),
+		(band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[0]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[1]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[2]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[3]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[4]);
+
+	pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+	return 0;
+}
+
+static int tomtom_get_compander(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int comp = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tomtom->comp_enabled[comp];
+	return 0;
+}
+
+static int tomtom_set_compander(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	int comp = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+	int value = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: Compander %d enable current %d, new %d\n",
+		 __func__, comp, tomtom->comp_enabled[comp], value);
+	tomtom->comp_enabled[comp] = value;
+
+	if (comp == COMPANDER_1 &&
+			tomtom->comp_enabled[comp] == 1) {
+		/* Wavegen to 5 msec */
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDB);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x2A);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0x2A);
+
+		/* Enable Chopper */
+		snd_soc_update_bits(codec,
+			TOMTOM_A_RX_HPH_CHOP_CTL, 0x80, 0x80);
+
+		snd_soc_write(codec, TOMTOM_A_NCP_DTEST, 0x20);
+		pr_debug("%s: Enabled Chopper and set wavegen to 5 msec\n",
+				__func__);
+	} else if (comp == COMPANDER_1 &&
+			tomtom->comp_enabled[comp] == 0) {
+		/* Wavegen to 20 msec */
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDB);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x58);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0x1A);
+
+		/* Disable CHOPPER block */
+		snd_soc_update_bits(codec,
+			TOMTOM_A_RX_HPH_CHOP_CTL, 0x80, 0x00);
+
+		snd_soc_write(codec, TOMTOM_A_NCP_DTEST, 0x10);
+		pr_debug("%s: Disabled Chopper and set wavegen to 20 msec\n",
+				__func__);
+	}
+	return 0;
+}
+
+static int tomtom_config_gain_compander(struct snd_soc_codec *codec,
+				       int comp, bool enable)
+{
+	int ret = 0;
+
+	switch (comp) {
+	case COMPANDER_0:
+		snd_soc_update_bits(codec, TOMTOM_A_SPKR_DRV1_GAIN,
+				    1 << 2, !enable << 2);
+		snd_soc_update_bits(codec, TOMTOM_A_SPKR_DRV2_GAIN,
+				    1 << 2, !enable << 2);
+		break;
+	case COMPANDER_1:
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_GAIN,
+				    1 << 5, !enable << 5);
+		break;
+	case COMPANDER_2:
+		snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_1_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_3_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_2_GAIN,
+				    1 << 5, !enable << 5);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_LINE_4_GAIN,
+				    1 << 5, !enable << 5);
+		break;
+	default:
+		WARN_ON(1);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static void tomtom_discharge_comp(struct snd_soc_codec *codec, int comp)
+{
+	/* Level meter DIV Factor to 5*/
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8), 0xF0,
+			    0x05 << 4);
+	/* RMS meter Sampling to 0x01 */
+	snd_soc_write(codec, TOMTOM_A_CDC_COMP0_B3_CTL + (comp * 8), 0x01);
+
+	/* Worst case timeout for compander CnP sleep timeout */
+	usleep_range(3000, 3100);
+}
+
+static enum wcd9xxx_buck_volt tomtom_codec_get_buck_mv(
+	struct snd_soc_codec *codec)
+{
+	int buck_volt = WCD9XXX_CDC_BUCK_UNSUPPORTED;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		if (!strcmp(pdata->regulator[i].name,
+					 WCD9XXX_SUPPLY_BUCK_NAME)) {
+			if ((pdata->regulator[i].min_uV ==
+					WCD9XXX_CDC_BUCK_MV_1P8) ||
+				(pdata->regulator[i].min_uV ==
+					WCD9XXX_CDC_BUCK_MV_2P15))
+				buck_volt = pdata->regulator[i].min_uV;
+			break;
+		}
+	}
+	return buck_volt;
+}
+
+static int tomtom_config_compander(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	int mask, enable_mask;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	const int comp = w->shift;
+	const u32 rate = tomtom->comp_fs[comp];
+	const struct comp_sample_dependent_params *comp_params =
+	    &comp_samp_params[rate];
+	enum wcd9xxx_buck_volt buck_mv;
+
+	pr_debug("%s: %s event %d compander %d, enabled %d", __func__,
+		 w->name, event, comp, tomtom->comp_enabled[comp]);
+
+	if (!tomtom->comp_enabled[comp])
+		return 0;
+
+	/* Compander 0 has two channels */
+	mask = enable_mask = 0x03;
+	buck_mv = tomtom_codec_get_buck_mv(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Set compander Sample rate */
+		snd_soc_update_bits(codec,
+				    TOMTOM_A_CDC_COMP0_FS_CFG + (comp * 8),
+				    0x07, rate);
+		/* Set the static gain offset for HPH Path */
+		if (comp == COMPANDER_1) {
+			if (buck_mv == WCD9XXX_CDC_BUCK_MV_2P15) {
+				snd_soc_update_bits(codec,
+					TOMTOM_A_CDC_COMP0_B4_CTL + (comp * 8),
+					0x80, 0x00);
+			} else {
+				snd_soc_update_bits(codec,
+					TOMTOM_A_CDC_COMP0_B4_CTL + (comp * 8),
+					0x80, 0x80);
+			}
+		}
+		/* Enable RX interpolation path compander clocks */
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_B2_CTL,
+				    mask << comp_shift[comp],
+				    mask << comp_shift[comp]);
+		/* Toggle compander reset bits */
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				    mask << comp_shift[comp],
+				    mask << comp_shift[comp]);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				    mask << comp_shift[comp], 0);
+
+		/* Set gain source to compander */
+		tomtom_config_gain_compander(codec, comp, true);
+
+		/* Compander enable */
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_COMP0_B1_CTL +
+				    (comp * 8), enable_mask, enable_mask);
+
+		tomtom_discharge_comp(codec, comp);
+
+		/* Set sample rate dependent parameter */
+		snd_soc_write(codec, TOMTOM_A_CDC_COMP0_B3_CTL + (comp * 8),
+			      comp_params->rms_meter_resamp_fact);
+		snd_soc_update_bits(codec,
+				    TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8),
+				    0xF0, comp_params->rms_meter_div_fact << 4);
+		snd_soc_update_bits(codec,
+					TOMTOM_A_CDC_COMP0_B2_CTL + (comp * 8),
+					0x0F, comp_params->peak_det_timeout);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		/* Disable compander */
+		snd_soc_update_bits(codec,
+				    TOMTOM_A_CDC_COMP0_B1_CTL + (comp * 8),
+				    enable_mask, 0x00);
+
+		/* Toggle compander reset bits */
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				    mask << comp_shift[comp],
+				    mask << comp_shift[comp]);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_OTHR_RESET_B2_CTL,
+				    mask << comp_shift[comp], 0);
+
+		/* Turn off the clock for compander in pair */
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_B2_CTL,
+				    mask << comp_shift[comp], 0);
+
+		/* Set gain source to register */
+		tomtom_config_gain_compander(codec, comp, false);
+		break;
+	}
+	return 0;
+}
+
+
+
+static const char *const tomtom_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tomtom_anc_func_enum =
+		SOC_ENUM_SINGLE_EXT(2, tomtom_anc_func_text);
+
+static const char *const tabla_ear_pa_gain_text[] = {"POS_6_DB", "POS_2_DB"};
+static const struct soc_enum tabla_ear_pa_gain_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, tabla_ear_pa_gain_text),
+};
+
+/*cut of frequency for high pass filter*/
+static const char * const cf_text[] = {
+	"MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz"
+};
+
+static const char * const rx_cf_text[] = {
+	"MIN_3DB_4Hz", "MIN_3DB_75Hz", "MIN_3DB_150Hz",
+	"MIN_3DB_0P48Hz"
+};
+
+static const struct soc_enum cf_dec1_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX1_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX2_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX3_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX4_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec5_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX5_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec6_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX6_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec7_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX7_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec8_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX8_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec9_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX9_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_dec10_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_TX10_MUX_CTL, 4, 3, cf_text);
+
+static const struct soc_enum cf_rxmix1_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX1_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix2_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX2_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix3_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX3_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix4_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX4_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix5_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX5_B4_CTL, 0, 4, rx_cf_text)
+;
+static const struct soc_enum cf_rxmix6_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX6_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix7_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX7_B4_CTL, 0, 4, rx_cf_text);
+
+static const struct soc_enum cf_rxmix8_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_RX8_B4_CTL, 0, 4, rx_cf_text);
+
+static const char * const class_h_dsm_text[] = {
+	"ZERO", "DSM_HPHL_RX1", "DSM_SPKR_RX7"
+};
+
+static const struct soc_enum class_h_dsm_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_CLSH_CTL, 4, 3, class_h_dsm_text);
+
+static const struct snd_kcontrol_new class_h_dsm_mux =
+	SOC_DAPM_ENUM("CLASS_H_DSM MUX Mux", class_h_dsm_enum);
+
+static const char * const rx1_interp_text[] = {
+	"ZERO", "RX1 MIX2"
+};
+
+static const struct soc_enum rx1_interp_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CLK_RX_B1_CTL, 0, 2, rx1_interp_text);
+
+static const struct snd_kcontrol_new rx1_interp_mux =
+	SOC_DAPM_ENUM("RX1 INTERP MUX Mux", rx1_interp_enum);
+
+static const char * const rx2_interp_text[] = {
+	"ZERO", "RX2 MIX2"
+};
+
+static const struct soc_enum rx2_interp_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CLK_RX_B1_CTL, 1, 2, rx2_interp_text);
+
+static const struct snd_kcontrol_new rx2_interp_mux =
+	SOC_DAPM_ENUM("RX2 INTERP MUX Mux", rx2_interp_enum);
+
+static const char *const tomtom_conn_mad_text[] = {
+	"ADC_MB", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "NOTUSED1",
+	"DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6", "NOTUSED2",
+	"NOTUSED3"};
+
+static const struct soc_enum tomtom_conn_mad_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tomtom_conn_mad_text),
+			tomtom_conn_mad_text);
+
+
+static int tomtom_mad_input_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 tomtom_mad_input;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	tomtom_mad_input = snd_soc_read(codec, TOMTOM_A_CDC_MAD_INP_SEL);
+
+	tomtom_mad_input = tomtom_mad_input & 0x0F;
+
+	ucontrol->value.integer.value[0] = tomtom_mad_input;
+
+	pr_debug("%s: tomtom_mad_input = %s\n", __func__,
+			tomtom_conn_mad_text[tomtom_mad_input]);
+
+	return 0;
+}
+
+static int tomtom_mad_input_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 tomtom_mad_input;
+	u16 micb_int_reg, micb_4_int_reg;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct snd_soc_card *card = codec->component.card;
+	char mad_amic_input_widget[6];
+	u32 adc;
+	const char *mad_input_widget;
+	const char *source_widget = NULL;
+	u32  mic_bias_found = 0;
+	u32 i;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	char *mad_input;
+
+	tomtom_mad_input = ucontrol->value.integer.value[0];
+	micb_4_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias;
+
+	pr_debug("%s: tomtom_mad_input = %s\n", __func__,
+			tomtom_conn_mad_text[tomtom_mad_input]);
+
+	if (!strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED1") ||
+		!strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED2") ||
+		!strcmp(tomtom_conn_mad_text[tomtom_mad_input], "NOTUSED3") ||
+		!strcmp(tomtom_conn_mad_text[tomtom_mad_input], "ADC_MB")) {
+		pr_info("%s: tomtom mad input is set to unsupported input = %s\n",
+			__func__, tomtom_conn_mad_text[tomtom_mad_input]);
+		return -EINVAL;
+	}
+
+	if (strnstr(tomtom_conn_mad_text[tomtom_mad_input],
+				"ADC", sizeof("ADC"))) {
+		mad_input = strpbrk(tomtom_conn_mad_text[tomtom_mad_input],
+				"123456");
+		if (!mad_input) {
+			dev_err(codec->dev, "%s: Invalid MAD input %s\n",
+			__func__, tomtom_conn_mad_text[tomtom_mad_input]);
+			return -EINVAL;
+		}
+		ret = kstrtouint(mad_input, 10, &adc);
+		if ((ret < 0) || (adc > 6)) {
+			pr_err("%s: Invalid ADC = %s\n", __func__,
+				tomtom_conn_mad_text[tomtom_mad_input]);
+			ret =  -EINVAL;
+		}
+
+		snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
+
+		mad_input_widget = mad_amic_input_widget;
+		pr_debug("%s: tomtom amic input widget = %s\n", __func__,
+			  mad_amic_input_widget);
+	} else {
+		/* DMIC type input widget*/
+		mad_input_widget = tomtom_conn_mad_text[tomtom_mad_input];
+	}
+
+	pr_debug("%s: tomtom input widget = %s\n", __func__, mad_input_widget);
+
+	for (i = 0; i < card->num_dapm_routes; i++) {
+
+		if (!strcmp(card->dapm_routes[i].sink, mad_input_widget)) {
+
+			source_widget = card->dapm_routes[i].source;
+			if (!source_widget) {
+				dev_err(codec->dev,
+					"%s: invalid source widget\n",
+					__func__);
+				return -EINVAL;
+			}
+
+			if (strnstr(source_widget,
+				"MIC BIAS1", sizeof("MIC BIAS1"))) {
+				mic_bias_found = 1;
+				micb_int_reg = TOMTOM_A_MICB_1_INT_RBIAS;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS2", sizeof("MIC BIAS2"))) {
+				mic_bias_found = 2;
+				micb_int_reg = TOMTOM_A_MICB_2_INT_RBIAS;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS3", sizeof("MIC BIAS3"))) {
+				mic_bias_found = 3;
+				micb_int_reg = TOMTOM_A_MICB_3_INT_RBIAS;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS4", sizeof("MIC BIAS4"))) {
+				mic_bias_found = 4;
+				micb_int_reg = micb_4_int_reg;
+				break;
+			}
+		}
+	}
+
+	if (mic_bias_found) {
+		pr_debug("%s: source mic bias = %s. sink = %s\n", __func__,
+				card->dapm_routes[i].source,
+				card->dapm_routes[i].sink);
+
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_INP_SEL,
+					0x0F, tomtom_mad_input);
+		snd_soc_update_bits(codec, TOMTOM_A_MAD_ANA_CTRL,
+					0x07, mic_bias_found);
+
+		/* Setup internal micbias */
+
+		if (strnstr(source_widget, "Internal1", strlen(source_widget)))
+			snd_soc_update_bits(codec,
+					micb_int_reg,
+					0xE0, 0xE0);
+		else if (strnstr(source_widget, "Internal2",
+				strlen(source_widget)))
+			snd_soc_update_bits(codec,
+					micb_int_reg,
+					0x1C, 0x1C);
+		else if (strnstr(source_widget, "Internal3",
+				strlen(source_widget)))
+			snd_soc_update_bits(codec,
+					micb_int_reg,
+					0x3, 0x3);
+		else
+			/*
+			 *  If not internal, make sure to write the
+			 *  register to default value
+			 */
+			snd_soc_write(codec, micb_int_reg, 0x24);
+		return 0;
+	}
+
+	pr_err("%s: mic bias source not found for input = %s\n",
+		__func__, mad_input_widget);
+	return -EINVAL;
+}
+
+static int tomtom_tx_hpf_bypass_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u32	tx_index;
+
+	tx_index = (u32)kcontrol->private_value;
+
+	if (tx_index > NUM_DECIMATORS) {
+		pr_err("%s: Invalid TX decimator %d\n", __func__,
+			   tx_index);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] =
+		tx_hpf_work[tx_index-1].tx_hpf_bypass;
+
+	return 0;
+}
+
+static int tomtom_tx_hpf_bypass_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	bool tx_hpf_bypass_cfg;
+	u32	tx_index;
+
+	tx_hpf_bypass_cfg = (bool)ucontrol->value.integer.value[0];
+
+	pr_debug("%s: tx_hpf_bypass = %d\n", __func__,
+			tx_hpf_bypass_cfg);
+
+	tx_index = (u32)kcontrol->private_value;
+
+	if (tx_index > NUM_DECIMATORS) {
+		pr_err("%s: Invalid TX decimator %d\n", __func__,
+			   tx_index);
+		return -EINVAL;
+	}
+	if (tx_hpf_work[tx_index-1].tx_hpf_bypass != tx_hpf_bypass_cfg)
+		tx_hpf_work[tx_index-1].tx_hpf_bypass = tx_hpf_bypass_cfg;
+
+	pr_debug("%s: Set TX%d HPF bypass configuration %d",
+			 __func__, tx_index,
+			 tx_hpf_work[tx_index-1].tx_hpf_bypass);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new tomtom_snd_controls[] = {
+
+	SOC_SINGLE_SX_TLV("RX1 Digital Volume", TOMTOM_A_CDC_RX1_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX2 Digital Volume", TOMTOM_A_CDC_RX2_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX3 Digital Volume", TOMTOM_A_CDC_RX3_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX4 Digital Volume", TOMTOM_A_CDC_RX4_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX5 Digital Volume", TOMTOM_A_CDC_RX5_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX6 Digital Volume", TOMTOM_A_CDC_RX6_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX7 Digital Volume", TOMTOM_A_CDC_RX7_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX8 Digital Volume", TOMTOM_A_CDC_RX8_VOL_CTL_B2_CTL,
+		0, -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("DEC1 Volume", TOMTOM_A_CDC_TX1_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC2 Volume", TOMTOM_A_CDC_TX2_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC3 Volume", TOMTOM_A_CDC_TX3_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC4 Volume", TOMTOM_A_CDC_TX4_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC5 Volume", TOMTOM_A_CDC_TX5_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC6 Volume", TOMTOM_A_CDC_TX6_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC7 Volume", TOMTOM_A_CDC_TX7_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC8 Volume", TOMTOM_A_CDC_TX8_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC9 Volume", TOMTOM_A_CDC_TX9_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC10 Volume", TOMTOM_A_CDC_TX10_VOL_CTL_GAIN, 0,
+					  -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("IIR1 INP1 Volume", TOMTOM_A_CDC_IIR1_GAIN_B1_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP2 Volume", TOMTOM_A_CDC_IIR1_GAIN_B2_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP3 Volume", TOMTOM_A_CDC_IIR1_GAIN_B3_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP4 Volume", TOMTOM_A_CDC_IIR1_GAIN_B4_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR2 INP1 Volume", TOMTOM_A_CDC_IIR2_GAIN_B1_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR2 INP2 Volume", TOMTOM_A_CDC_IIR2_GAIN_B2_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR2 INP3 Volume", TOMTOM_A_CDC_IIR2_GAIN_B3_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR2 INP4 Volume", TOMTOM_A_CDC_IIR2_GAIN_B4_CTL, 0,
+					  -84, 40, digital_gain),
+
+	SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tomtom_get_anc_slot,
+		tomtom_put_anc_slot),
+	SOC_ENUM_EXT("ANC Function", tomtom_anc_func_enum, tomtom_get_anc_func,
+		tomtom_put_anc_func),
+
+	SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+	SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+	SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+	SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+	SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+	SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+	SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+	SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+	SOC_ENUM("TX9 HPF cut off", cf_dec9_enum),
+	SOC_ENUM("TX10 HPF cut off", cf_dec10_enum),
+
+	SOC_SINGLE_BOOL_EXT("TX1 HPF Switch", 1,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX2 HPF Switch", 2,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX3 HPF Switch", 3,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX4 HPF Switch", 4,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX5 HPF Switch", 5,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX6 HPF Switch", 6,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX7 HPF Switch", 7,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX8 HPF Switch", 8,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX9 HPF Switch", 9,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+	SOC_SINGLE_BOOL_EXT("TX10 HPF Switch", 10,
+				tomtom_tx_hpf_bypass_get,
+				tomtom_tx_hpf_bypass_put),
+
+	SOC_SINGLE("RX1 HPF Switch", TOMTOM_A_CDC_RX1_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX2 HPF Switch", TOMTOM_A_CDC_RX2_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX3 HPF Switch", TOMTOM_A_CDC_RX3_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX4 HPF Switch", TOMTOM_A_CDC_RX4_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX5 HPF Switch", TOMTOM_A_CDC_RX5_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX6 HPF Switch", TOMTOM_A_CDC_RX6_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX7 HPF Switch", TOMTOM_A_CDC_RX7_B5_CTL, 2, 1, 0),
+	SOC_SINGLE("RX8 HPF Switch", TOMTOM_A_CDC_RX8_B5_CTL, 2, 1, 0),
+
+	SOC_ENUM("RX1 HPF cut off", cf_rxmix1_enum),
+	SOC_ENUM("RX2 HPF cut off", cf_rxmix2_enum),
+	SOC_ENUM("RX3 HPF cut off", cf_rxmix3_enum),
+	SOC_ENUM("RX4 HPF cut off", cf_rxmix4_enum),
+	SOC_ENUM("RX5 HPF cut off", cf_rxmix5_enum),
+	SOC_ENUM("RX6 HPF cut off", cf_rxmix6_enum),
+	SOC_ENUM("RX7 HPF cut off", cf_rxmix7_enum),
+	SOC_ENUM("RX8 HPF cut off", cf_rxmix8_enum),
+
+	SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band1", IIR2, BAND1, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band2", IIR2, BAND2, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band3", IIR2, BAND3, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band4", IIR2, BAND4, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR2 Enable Band5", IIR2, BAND5, 1, 0,
+	tomtom_get_iir_enable_audio_mixer, tomtom_put_iir_enable_audio_mixer),
+
+	SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band1", IIR2, BAND1, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band2", IIR2, BAND2, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band3", IIR2, BAND3, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band4", IIR2, BAND4, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR2 Band5", IIR2, BAND5, 255, 0, 5,
+	tomtom_get_iir_band_audio_mixer, tomtom_put_iir_band_audio_mixer),
+
+	SOC_SINGLE_EXT("COMP0 Switch", SND_SOC_NOPM, COMPANDER_0, 1, 0,
+		       tomtom_get_compander, tomtom_set_compander),
+	SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+		       tomtom_get_compander, tomtom_set_compander),
+	SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+		       tomtom_get_compander, tomtom_set_compander),
+
+	SOC_ENUM_EXT("MAD Input", tomtom_conn_mad_enum,
+			tomtom_mad_input_get, tomtom_mad_input_put),
+
+};
+
+static int tomtom_pa_gain_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ear_pa_gain = snd_soc_read(codec, TOMTOM_A_RX_EAR_GAIN);
+
+	ear_pa_gain = ear_pa_gain >> 5;
+
+	ucontrol->value.integer.value[0] = ear_pa_gain;
+
+	pr_debug("%s: ear_pa_gain = 0x%x\n", __func__, ear_pa_gain);
+
+	return 0;
+}
+
+static int tomtom_pa_gain_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	pr_debug("%s: ucontrol->value.integer.value[0]  = %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+
+	ear_pa_gain =  ucontrol->value.integer.value[0] << 5;
+
+	snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_GAIN, 0xE0, ear_pa_gain);
+	return 0;
+}
+
+static const char * const tomtom_1_x_ear_pa_gain_text[] = {
+	"POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB",
+	"POS_0_DB", "NEG_2P5_DB", "UNDEFINED", "NEG_12_DB"
+};
+
+static const struct soc_enum tomtom_1_x_ear_pa_gain_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tomtom_1_x_ear_pa_gain_text),
+			tomtom_1_x_ear_pa_gain_text);
+
+static const struct snd_kcontrol_new tomtom_1_x_analog_gain_controls[] = {
+
+	SOC_ENUM_EXT("EAR PA Gain", tomtom_1_x_ear_pa_gain_enum,
+		tomtom_pa_gain_get, tomtom_pa_gain_put),
+
+	SOC_SINGLE_TLV("HPHL Volume", TOMTOM_A_RX_HPH_L_GAIN, 0, 20, 1,
+		line_gain),
+	SOC_SINGLE_TLV("HPHR Volume", TOMTOM_A_RX_HPH_R_GAIN, 0, 20, 1,
+		line_gain),
+
+	SOC_SINGLE_TLV("LINEOUT1 Volume", TOMTOM_A_RX_LINE_1_GAIN, 0, 20, 1,
+		line_gain),
+	SOC_SINGLE_TLV("LINEOUT2 Volume", TOMTOM_A_RX_LINE_2_GAIN, 0, 20, 1,
+		line_gain),
+	SOC_SINGLE_TLV("LINEOUT3 Volume", TOMTOM_A_RX_LINE_3_GAIN, 0, 20, 1,
+		line_gain),
+	SOC_SINGLE_TLV("LINEOUT4 Volume", TOMTOM_A_RX_LINE_4_GAIN, 0, 20, 1,
+		line_gain),
+
+	SOC_SINGLE_TLV("SPK DRV Volume", TOMTOM_A_SPKR_DRV1_GAIN, 3, 8, 1,
+		line_gain),
+	SOC_SINGLE_TLV("SPK DRV2 Volume", TOMTOM_A_SPKR_DRV2_GAIN, 3, 8, 1,
+		line_gain),
+
+	SOC_SINGLE_TLV("ADC1 Volume", TOMTOM_A_TX_1_GAIN, 2, 19, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC2 Volume", TOMTOM_A_TX_2_GAIN, 2, 19, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC3 Volume", TOMTOM_A_TX_3_GAIN, 2, 19, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC4 Volume", TOMTOM_A_TX_4_GAIN, 2, 19, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC5 Volume", TOMTOM_A_TX_5_GAIN, 2, 19, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC6 Volume", TOMTOM_A_TX_6_GAIN, 2, 19, 0,
+			analog_gain),
+};
+
+static int tomtom_hph_impedance_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	uint32_t zl, zr;
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+
+	hphr = mc->shift;
+	wcd9xxx_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+	pr_debug("%s: zl %u, zr %u\n", __func__, zl, zr);
+	ucontrol->value.integer.value[0] = hphr ? zr : zl;
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+		       tomtom_hph_impedance_get, NULL),
+	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+		       tomtom_hph_impedance_get, NULL),
+};
+
+static int tomtom_get_hph_type(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_mbhc *mbhc;
+
+	if (!priv) {
+		pr_debug("%s: wcd9330 private data is NULL\n", __func__);
+		return 0;
+	}
+
+	mbhc = &priv->mbhc;
+	if (!mbhc) {
+		pr_debug("%s: mbhc not initialized\n", __func__);
+		return 0;
+	}
+
+	ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+	pr_debug("%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+	SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+		       tomtom_get_hph_type, NULL),
+};
+
+static const char * const rx_mix1_text[] = {
+	"ZERO", "SRC1", "SRC2", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4",
+		"RX5", "RX6", "RX7"
+};
+
+static const char * const rx8_mix1_text[] = {
+	"ZERO", "IIR1", "IIR2", "RX1", "RX2", "RX3", "RX4",
+		"RX5", "RX6", "RX7", "RX8"
+};
+
+static const char * const rx_mix2_text[] = {
+	"ZERO", "SRC1", "SRC2", "IIR1", "IIR2"
+};
+
+static const char * const rx_rdac5_text[] = {
+	"DEM4", "DEM3_INV"
+};
+
+static const char * const rx_rdac7_text[] = {
+	"DEM6", "DEM5_INV"
+};
+
+static const char * const mad_sel_text[] = {
+	"SPE", "MSM"
+};
+
+static const char * const sb_tx1_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC1", "RMIX8"
+};
+
+static const char * const sb_tx2_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC2", "RMIX8"
+};
+
+static const char * const sb_tx3_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC3", "RMIX8"
+};
+
+static const char * const sb_tx4_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC4", "RMIX8"
+};
+
+static const char * const sb_tx5_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC5", "RMIX8"
+};
+
+static const char * const sb_tx6_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC6", "RMIX8"
+};
+
+static const char * const sb_tx7_to_tx10_mux_text[] = {
+	"ZERO", "RMIX1", "RMIX2", "RMIX3", "RMIX4", "RMIX5", "RMIX6", "RMIX7",
+		"DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+		"DEC9", "DEC10"
+};
+
+static const char * const dec1_mux_text[] = {
+	"ZERO", "DMIC1", "ADC6",
+};
+
+static const char * const dec2_mux_text[] = {
+	"ZERO", "DMIC2", "ADC5",
+};
+
+static const char * const dec3_mux_text[] = {
+	"ZERO", "DMIC3", "ADC4",
+};
+
+static const char * const dec4_mux_text[] = {
+	"ZERO", "DMIC4", "ADC3",
+};
+
+static const char * const dec5_mux_text[] = {
+	"ZERO", "DMIC5", "ADC2",
+};
+
+static const char * const dec6_mux_text[] = {
+	"ZERO", "DMIC6", "ADC1",
+};
+
+static const char * const dec7_mux_text[] = {
+	"ZERO", "DMIC1", "DMIC6", "ADC1", "ADC6", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const dec8_mux_text[] = {
+	"ZERO", "DMIC2", "DMIC5", "ADC2", "ADC5", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const dec9_mux_text[] = {
+	"ZERO", "DMIC4", "DMIC5", "ADC2", "ADC3", "ADCMB", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const dec10_mux_text[] = {
+	"ZERO", "DMIC3", "DMIC6", "ADC1", "ADC4", "ADCMB", "ANC1_FB", "ANC2_FB",
+};
+
+static const char * const anc_mux_text[] = {
+	"ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6", "ADC_MB",
+		"RSVD_1", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5", "DMIC6"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+	"ZERO", "EAR_HPH_L", "EAR_LINE_1",
+};
+
+static const char * const iir_inp1_text[] = {
+	"ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+	"DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const iir_inp2_text[] = {
+	"ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+	"DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const iir_inp3_text[] = {
+	"ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+	"DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const iir_inp4_text[] = {
+	"ZERO", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6", "DEC7", "DEC8",
+	"DEC9", "DEC10", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const struct soc_enum rx_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx_mix1_inp3_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B2_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx2_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX3_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx3_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX3_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx4_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX4_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx4_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX4_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx5_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX5_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx5_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX5_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx6_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX6_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx6_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX6_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx7_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B1_CTL, 0, 12, rx_mix1_text);
+
+static const struct soc_enum rx7_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B1_CTL, 4, 12, rx_mix1_text);
+
+static const struct soc_enum rx8_mix1_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX8_B1_CTL, 0, 11, rx8_mix1_text);
+
+static const struct soc_enum rx8_mix1_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX8_B1_CTL, 4, 11, rx8_mix1_text);
+
+static const struct soc_enum rx1_mix2_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B3_CTL, 0, 5, rx_mix2_text);
+
+static const struct soc_enum rx1_mix2_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX1_B3_CTL, 3, 5, rx_mix2_text);
+
+static const struct soc_enum rx2_mix2_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B3_CTL, 0, 5, rx_mix2_text);
+
+static const struct soc_enum rx2_mix2_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX2_B3_CTL, 3, 5, rx_mix2_text);
+
+static const struct soc_enum rx7_mix2_inp1_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B3_CTL, 0, 5, rx_mix2_text);
+
+static const struct soc_enum rx7_mix2_inp2_chain_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_RX7_B3_CTL, 3, 5, rx_mix2_text);
+
+static const struct soc_enum rx_rdac5_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_MISC, 2, 2, rx_rdac5_text);
+
+static const struct soc_enum rx_rdac7_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_MISC, 1, 2, rx_rdac7_text);
+
+static const struct soc_enum mad_sel_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_SVASS_CFG, 0, 2, mad_sel_text);
+
+static const struct soc_enum sb_tx1_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, 0, 10, sb_tx1_mux_text);
+
+static const struct soc_enum sb_tx2_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, 0, 10, sb_tx2_mux_text);
+
+static const struct soc_enum sb_tx3_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, 0, 10, sb_tx3_mux_text);
+
+static const struct soc_enum sb_tx4_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, 0, 10, sb_tx4_mux_text);
+
+static const struct soc_enum sb_tx5_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, 0, 10, sb_tx5_mux_text);
+
+static const struct soc_enum sb_tx6_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, 0, 10, sb_tx6_mux_text);
+
+static const struct soc_enum sb_tx7_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, 0, 18,
+			sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx8_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, 0, 18,
+			sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx9_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, 0, 18,
+			sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum sb_tx10_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, 0, 18,
+			sb_tx7_to_tx10_mux_text);
+
+static const struct soc_enum dec1_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 0, 3, dec1_mux_text);
+
+static const struct soc_enum dec2_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 2, 3, dec2_mux_text);
+
+static const struct soc_enum dec3_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 4, 3, dec3_mux_text);
+
+static const struct soc_enum dec4_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B1_CTL, 6, 3, dec4_mux_text);
+
+static const struct soc_enum dec5_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 0, 3, dec5_mux_text);
+
+static const struct soc_enum dec6_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 2, 3, dec6_mux_text);
+
+static const struct soc_enum dec7_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B2_CTL, 4, 7, dec7_mux_text);
+
+static const struct soc_enum dec8_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B3_CTL, 0, 7, dec8_mux_text);
+
+static const struct soc_enum dec9_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B3_CTL, 3, 8, dec9_mux_text);
+
+static const struct soc_enum dec10_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_TX_B4_CTL, 0, 8, dec10_mux_text);
+
+static const struct soc_enum anc1_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B1_CTL, 0, 15, anc_mux_text);
+
+static const struct soc_enum anc2_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B1_CTL, 4, 15, anc_mux_text);
+
+static const struct soc_enum anc1_fb_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_ANC_B2_CTL, 0, 3, anc1_fb_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B1_CTL, 0, 18, iir_inp1_text);
+
+static const struct soc_enum iir2_inp1_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B1_CTL, 0, 18, iir_inp1_text);
+
+static const struct soc_enum iir1_inp2_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B2_CTL, 0, 18, iir_inp2_text);
+
+static const struct soc_enum iir2_inp2_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B2_CTL, 0, 18, iir_inp2_text);
+
+static const struct soc_enum iir1_inp3_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B3_CTL, 0, 18, iir_inp3_text);
+
+static const struct soc_enum iir2_inp3_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B3_CTL, 0, 18, iir_inp3_text);
+
+static const struct soc_enum iir1_inp4_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ1_B4_CTL, 0, 18, iir_inp4_text);
+
+static const struct soc_enum iir2_inp4_mux_enum =
+	SOC_ENUM_SINGLE(TOMTOM_A_CDC_CONN_EQ2_B4_CTL, 0, 18, iir_inp4_text);
+
+static const struct snd_kcontrol_new rx_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX1 MIX1 INP1 Mux", rx_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX1 MIX1 INP2 Mux", rx_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_mix1_inp3_mux =
+	SOC_DAPM_ENUM("RX1 MIX1 INP3 Mux", rx_mix1_inp3_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX2 MIX1 INP1 Mux", rx2_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX2 MIX1 INP2 Mux", rx2_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX3 MIX1 INP1 Mux", rx3_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx3_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX3 MIX1 INP2 Mux", rx3_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx4_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX4 MIX1 INP1 Mux", rx4_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx4_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX4 MIX1 INP2 Mux", rx4_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX5 MIX1 INP1 Mux", rx5_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx5_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX5 MIX1 INP2 Mux", rx5_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx6_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX6 MIX1 INP1 Mux", rx6_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx6_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX6 MIX1 INP2 Mux", rx6_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX7 MIX1 INP1 Mux", rx7_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX7 MIX1 INP2 Mux", rx7_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx8_mix1_inp1_mux =
+	SOC_DAPM_ENUM("RX8 MIX1 INP1 Mux", rx8_mix1_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx8_mix1_inp2_mux =
+	SOC_DAPM_ENUM("RX8 MIX1 INP2 Mux", rx8_mix1_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix2_inp1_mux =
+	SOC_DAPM_ENUM("RX1 MIX2 INP1 Mux", rx1_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx1_mix2_inp2_mux =
+	SOC_DAPM_ENUM("RX1 MIX2 INP2 Mux", rx1_mix2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix2_inp1_mux =
+	SOC_DAPM_ENUM("RX2 MIX2 INP1 Mux", rx2_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx2_mix2_inp2_mux =
+	SOC_DAPM_ENUM("RX2 MIX2 INP2 Mux", rx2_mix2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix2_inp1_mux =
+	SOC_DAPM_ENUM("RX7 MIX2 INP1 Mux", rx7_mix2_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx7_mix2_inp2_mux =
+	SOC_DAPM_ENUM("RX7 MIX2 INP2 Mux", rx7_mix2_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_dac5_mux =
+	SOC_DAPM_ENUM("RDAC5 MUX Mux", rx_rdac5_enum);
+
+static const struct snd_kcontrol_new rx_dac7_mux =
+	SOC_DAPM_ENUM("RDAC7 MUX Mux", rx_rdac7_enum);
+
+static const struct snd_kcontrol_new mad_sel_mux =
+	SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum);
+
+static const struct snd_kcontrol_new sb_tx1_mux =
+	SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx2_mux =
+	SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx3_mux =
+	SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx4_mux =
+	SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx5_mux =
+	SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx6_mux =
+	SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx7_mux =
+	SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx8_mux =
+	SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx9_mux =
+	SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx10_mux =
+	SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum);
+
+
+static int wcd9330_put_dec_enum(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *w = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int dec_mux, decimator;
+	char *dec_name = NULL;
+	char *widget_name = NULL;
+	char *temp;
+	u16 tx_mux_ctl_reg;
+	u8 adc_dmic_sel = 0x0;
+	int ret = 0;
+	char *dec;
+
+	if (ucontrol->value.enumerated.item[0] >= e->items)
+		return -EINVAL;
+
+	dec_mux = ucontrol->value.enumerated.item[0];
+
+	widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+	if (!widget_name)
+		return -ENOMEM;
+	temp = widget_name;
+
+	dec_name = strsep(&widget_name, " ");
+	widget_name = temp;
+	if (!dec_name) {
+		pr_err("%s: Invalid decimator = %s\n", __func__, w->name);
+		ret =  -EINVAL;
+		goto out;
+	}
+	dec = strpbrk(dec_name, "123456789");
+	if (!dec) {
+		dev_err(w->dapm->dev, "%s: decimator index not found\n",
+			__func__);
+		ret =  -EINVAL;
+		goto out;
+	}
+	ret = kstrtouint(dec, 10, &decimator);
+	if (ret < 0) {
+		pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	dev_dbg(w->dapm->dev, "%s(): widget = %s decimator = %u dec_mux = %u\n"
+		, __func__, w->name, decimator, dec_mux);
+
+
+	switch (decimator) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+		if (dec_mux == 1)
+			adc_dmic_sel = 0x1;
+		else
+			adc_dmic_sel = 0x0;
+		break;
+	case 7:
+	case 8:
+	case 9:
+	case 10:
+		if ((dec_mux == 1) || (dec_mux == 2))
+			adc_dmic_sel = 0x1;
+		else
+			adc_dmic_sel = 0x0;
+		break;
+	default:
+		pr_err("%s: Invalid Decimator = %u\n", __func__, decimator);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + 8 * (decimator - 1);
+
+	snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x1, adc_dmic_sel);
+
+	ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+
+out:
+	kfree(widget_name);
+	return ret;
+}
+
+#define WCD9330_DEC_ENUM(xname, xenum) \
+{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+	.info = snd_soc_info_enum_double, \
+	.get = snd_soc_dapm_get_enum_double, \
+	.put = wcd9330_put_dec_enum, \
+	.private_value = (unsigned long)&xenum }
+
+static const struct snd_kcontrol_new dec1_mux =
+	WCD9330_DEC_ENUM("DEC1 MUX Mux", dec1_mux_enum);
+
+static const struct snd_kcontrol_new dec2_mux =
+	WCD9330_DEC_ENUM("DEC2 MUX Mux", dec2_mux_enum);
+
+static const struct snd_kcontrol_new dec3_mux =
+	WCD9330_DEC_ENUM("DEC3 MUX Mux", dec3_mux_enum);
+
+static const struct snd_kcontrol_new dec4_mux =
+	WCD9330_DEC_ENUM("DEC4 MUX Mux", dec4_mux_enum);
+
+static const struct snd_kcontrol_new dec5_mux =
+	WCD9330_DEC_ENUM("DEC5 MUX Mux", dec5_mux_enum);
+
+static const struct snd_kcontrol_new dec6_mux =
+	WCD9330_DEC_ENUM("DEC6 MUX Mux", dec6_mux_enum);
+
+static const struct snd_kcontrol_new dec7_mux =
+	WCD9330_DEC_ENUM("DEC7 MUX Mux", dec7_mux_enum);
+
+static const struct snd_kcontrol_new dec8_mux =
+	WCD9330_DEC_ENUM("DEC8 MUX Mux", dec8_mux_enum);
+
+static const struct snd_kcontrol_new dec9_mux =
+	WCD9330_DEC_ENUM("DEC9 MUX Mux", dec9_mux_enum);
+
+static const struct snd_kcontrol_new dec10_mux =
+	WCD9330_DEC_ENUM("DEC10 MUX Mux", dec10_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+	SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp1_mux =
+	SOC_DAPM_ENUM("IIR2 INP1 Mux", iir2_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp2_mux =
+	SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp2_mux =
+	SOC_DAPM_ENUM("IIR2 INP2 Mux", iir2_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp3_mux =
+	SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp3_mux =
+	SOC_DAPM_ENUM("IIR2 INP3 Mux", iir2_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp4_mux =
+	SOC_DAPM_ENUM("IIR1 INP4 Mux", iir1_inp4_mux_enum);
+
+static const struct snd_kcontrol_new iir2_inp4_mux =
+	SOC_DAPM_ENUM("IIR2 INP4 Mux", iir2_inp4_mux_enum);
+
+static const struct snd_kcontrol_new anc1_mux =
+	SOC_DAPM_ENUM("ANC1 MUX Mux", anc1_mux_enum);
+
+static const struct snd_kcontrol_new anc2_mux =
+	SOC_DAPM_ENUM("ANC2 MUX Mux", anc2_mux_enum);
+
+static const struct snd_kcontrol_new anc1_fb_mux =
+	SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+
+static const struct snd_kcontrol_new dac1_switch[] = {
+	SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_EAR_EN, 5, 1, 0)
+};
+static const struct snd_kcontrol_new hphl_switch[] = {
+	SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_HPH_L_DAC_CTL, 6, 1, 0)
+};
+
+static const struct snd_kcontrol_new hphl_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					7, 1, 0),
+};
+
+static const struct snd_kcontrol_new hphr_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					6, 1, 0),
+};
+
+static const struct snd_kcontrol_new ear_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					5, 1, 0),
+};
+static const struct snd_kcontrol_new lineout1_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					4, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout2_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					3, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout3_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_L Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					2, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout4_pa_mix[] = {
+	SOC_DAPM_SINGLE("AUX_PGA_R Switch", TOMTOM_A_RX_PA_AUX_IN_CONN,
+					1, 1, 0),
+};
+
+static const struct snd_kcontrol_new lineout3_ground_switch =
+	SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_LINE_3_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new lineout4_ground_switch =
+	SOC_DAPM_SINGLE("Switch", TOMTOM_A_RX_LINE_4_DAC_CTL, 6, 1, 0);
+
+static const struct snd_kcontrol_new aif4_mad_switch =
+	SOC_DAPM_SINGLE("Switch", TOMTOM_A_SVASS_CLKRST_CTL, 0, 1, 0);
+
+static const struct snd_kcontrol_new aif4_vi_switch =
+	SOC_DAPM_SINGLE("Switch", TOMTOM_A_SPKR1_PROT_EN, 3, 1, 0);
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tomtom_p->tx_port_value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct snd_soc_dapm_update *update = NULL;
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+	u32 vtable = vport_check_table[dai_id];
+
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, tomtom_p->tx_port_value,
+		widget->shift, ucontrol->value.integer.value[0]);
+
+	mutex_lock(&tomtom_p->codec_mutex);
+
+	if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&tomtom_p->codec_mutex);
+			return -EINVAL;
+		}
+	}
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set
+		 */
+		if (enable && !(tomtom_p->tx_port_value & 1 << port_id)) {
+
+			if (tomtom_p->intf_type ==
+				WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+				vtable = vport_check_table[dai_id];
+			if (tomtom_p->intf_type ==
+				WCD9XXX_INTERFACE_TYPE_I2C)
+				vtable = vport_i2s_check_table[dai_id];
+
+			if (wcd9xxx_tx_vport_validation(
+					vtable,
+					port_id,
+					tomtom_p->dai, NUM_CODEC_DAIS)) {
+				dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
+					__func__, port_id + 1);
+				mutex_unlock(&tomtom_p->codec_mutex);
+				return 0;
+			}
+			tomtom_p->tx_port_value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+			      &tomtom_p->dai[dai_id].wcd9xxx_ch_list
+					      );
+		} else if (!enable && (tomtom_p->tx_port_value &
+					1 << port_id)) {
+			tomtom_p->tx_port_value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				dev_dbg(codec->dev, "%s: TX%u port is used by\n"
+					"this virtual port\n",
+					__func__, port_id + 1);
+			else
+				dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
+					"this virtual port\n",
+					__func__, port_id + 1);
+			/* avoid update power function */
+			mutex_unlock(&tomtom_p->codec_mutex);
+			return 0;
+		}
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&tomtom_p->codec_mutex);
+		return -EINVAL;
+	}
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, tomtom_p->tx_port_value,
+		widget->shift);
+
+	mutex_unlock(&tomtom_p->codec_mutex);
+	snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] = tomtom_p->rx_port_value;
+	return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+	u32 port_id = widget->shift;
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, tomtom_p->rx_port_value,
+		widget->shift, ucontrol->value.integer.value[0]);
+
+	tomtom_p->rx_port_value = ucontrol->value.enumerated.item[0];
+
+	mutex_lock(&tomtom_p->codec_mutex);
+
+	if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (tomtom_p->rx_port_value > 2) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			goto err;
+		}
+	}
+	/* value need to match the Virtual port and AIF number
+	 */
+	switch (tomtom_p->rx_port_value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+		break;
+	case 1:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TOMTOM_RX_PORT_START_NUMBER,
+			&tomtom_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id + 1);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tomtom_p->dai[AIF1_PB].wcd9xxx_ch_list);
+		break;
+	case 2:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TOMTOM_RX_PORT_START_NUMBER,
+			&tomtom_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id + 1);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tomtom_p->dai[AIF2_PB].wcd9xxx_ch_list);
+		break;
+	case 3:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TOMTOM_RX_PORT_START_NUMBER,
+			&tomtom_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id + 1);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tomtom_p->dai[AIF3_PB].wcd9xxx_ch_list);
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", tomtom_p->rx_port_value);
+		goto err;
+	}
+rtn:
+	mutex_unlock(&tomtom_p->codec_mutex);
+	snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+					tomtom_p->rx_port_value, e, update);
+
+	return 0;
+err:
+	mutex_unlock(&tomtom_p->codec_mutex);
+	return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TOMTOM_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX8 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TOMTOM_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TOMTOM_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TOMTOM_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TOMTOM_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TOMTOM_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TOMTOM_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TOMTOM_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TOMTOM_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TOMTOM_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TOMTOM_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static void tomtom_codec_enable_adc_block(struct snd_soc_codec *codec,
+					 int enable)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s %d\n", __func__, enable);
+
+	if (enable) {
+		tomtom->adc_count++;
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+						0x2, 0x2);
+	} else {
+		tomtom->adc_count--;
+		if (!tomtom->adc_count)
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+					    0x2, 0x0);
+	}
+}
+
+static int tomtom_codec_enable_adc(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+	u16 adc_reg;
+	u16 tx_fe_clkdiv_reg;
+	u8 tx_fe_clkdiv_mask;
+	u8 init_bit_shift;
+	u8 bit_pos;
+
+	pr_debug("%s %d\n", __func__, event);
+
+	switch (w->reg) {
+	case TOMTOM_A_TX_1_GAIN:
+		adc_reg = TOMTOM_A_TX_1_2_TEST_CTL;
+		tx_fe_clkdiv_reg = TOMTOM_A_TX_1_2_TXFE_CLKDIV;
+		tx_fe_clkdiv_mask = 0x0F;
+		init_bit_shift = 7;
+		bit_pos = ADC1_TXFE;
+		break;
+	case TOMTOM_A_TX_2_GAIN:
+		adc_reg = TOMTOM_A_TX_1_2_TEST_CTL;
+		tx_fe_clkdiv_reg = TOMTOM_A_TX_1_2_TXFE_CLKDIV;
+		tx_fe_clkdiv_mask = 0xF0;
+		init_bit_shift = 6;
+		bit_pos = ADC2_TXFE;
+		break;
+	case TOMTOM_A_TX_3_GAIN:
+		adc_reg = TOMTOM_A_TX_3_4_TEST_CTL;
+		init_bit_shift = 7;
+		tx_fe_clkdiv_reg = TOMTOM_A_TX_3_4_TXFE_CKDIV;
+		tx_fe_clkdiv_mask = 0x0F;
+		bit_pos = ADC3_TXFE;
+		break;
+	case TOMTOM_A_TX_4_GAIN:
+		adc_reg = TOMTOM_A_TX_3_4_TEST_CTL;
+		init_bit_shift = 6;
+		tx_fe_clkdiv_reg = TOMTOM_A_TX_3_4_TXFE_CKDIV;
+		tx_fe_clkdiv_mask = 0xF0;
+		bit_pos = ADC4_TXFE;
+		break;
+	case TOMTOM_A_TX_5_GAIN:
+		adc_reg = TOMTOM_A_TX_5_6_TEST_CTL;
+		init_bit_shift = 7;
+		tx_fe_clkdiv_reg = TOMTOM_A_TX_5_6_TXFE_CKDIV;
+		tx_fe_clkdiv_mask = 0x0F;
+		bit_pos = ADC5_TXFE;
+		break;
+	case TOMTOM_A_TX_6_GAIN:
+		adc_reg = TOMTOM_A_TX_5_6_TEST_CTL;
+		init_bit_shift = 6;
+		tx_fe_clkdiv_reg = TOMTOM_A_TX_5_6_TXFE_CKDIV;
+		tx_fe_clkdiv_mask = 0xF0;
+		bit_pos = ADC6_TXFE;
+		break;
+	default:
+		pr_err("%s: Error, invalid adc register\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, tx_fe_clkdiv_reg, tx_fe_clkdiv_mask,
+				    0x0);
+		set_bit(bit_pos, &priv->status_mask);
+		tomtom_codec_enable_adc_block(codec, 1);
+		snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift,
+				1 << init_bit_shift);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, adc_reg, 1 << init_bit_shift, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tomtom_codec_enable_adc_block(codec, 0);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_ext_clk_en(struct snd_soc_codec *codec,
+		int enable, bool dapm)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	if (!tomtom->codec_ext_clk_en_cb) {
+		dev_err(codec->dev,
+			"%s: Invalid ext_clk_callback\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return tomtom->codec_ext_clk_en_cb(codec, enable, dapm);
+}
+
+static int __tomtom_mclk_enable(struct tomtom_priv *tomtom, int mclk_enable)
+{
+	int ret = 0;
+
+	WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+	if (mclk_enable) {
+		tomtom->ext_clk_users++;
+		if (tomtom->ext_clk_users > 1)
+			goto bg_clk_unlock;
+		ret = clk_prepare_enable(tomtom->wcd_ext_clk);
+		if (ret) {
+			pr_err("%s: ext clk enable failed\n",
+				__func__);
+			tomtom->ext_clk_users--;
+			goto bg_clk_unlock;
+		}
+		wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK);
+	} else {
+		tomtom->ext_clk_users--;
+		if (tomtom->ext_clk_users == 0) {
+			/* Put clock and BG */
+			wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr,
+						     WCD9XXX_CLK_MCLK);
+			wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
+					WCD9XXX_BANDGAP_AUDIO_MODE);
+			clk_disable_unprepare(tomtom->wcd_ext_clk);
+		}
+	}
+bg_clk_unlock:
+	WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+
+	return ret;
+}
+
+int tomtom_codec_mclk_enable(struct snd_soc_codec *codec,
+			     int enable, bool dapm)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	if (tomtom->wcd_ext_clk) {
+		dev_dbg(codec->dev, "%s: mclk_enable = %u, dapm = %d\n",
+			__func__, enable, dapm);
+		return __tomtom_mclk_enable(tomtom, enable);
+	} else if (tomtom->codec_ext_clk_en_cb)
+		return tomtom_codec_ext_clk_en(codec, enable, dapm);
+	else {
+		dev_err(codec->dev,
+			"%s: Cannot turn on MCLK\n",
+			__func__);
+		return -EINVAL;
+	}
+}
+EXPORT_SYMBOL(tomtom_codec_mclk_enable);
+
+static int tomtom_codec_get_ext_clk_users(struct tomtom_priv *tomtom)
+{
+	if (tomtom->wcd_ext_clk)
+		return tomtom->ext_clk_users;
+	else if (tomtom->codec_get_ext_clk_cnt)
+		return tomtom->codec_get_ext_clk_cnt();
+	else
+		return 0;
+}
+
+/* tomtom_codec_internal_rco_ctrl( )
+ * Make sure that BG_CLK_LOCK is not acquired. Exit if acquired to avoid
+ * potential deadlock as ext_clk_en_cb() also tries to acquire the same
+ * lock to enable MCLK for RCO calibration
+ */
+static int tomtom_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+					  bool enable)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (enable) {
+		if (wcd9xxx_resmgr_get_clk_type(&tomtom->resmgr) ==
+		    WCD9XXX_CLK_RCO) {
+			WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+			wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr,
+						     WCD9XXX_CLK_RCO);
+			WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+		} else {
+			tomtom_codec_mclk_enable(codec, true, false);
+			WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+			tomtom->resmgr.ext_clk_users =
+					tomtom_codec_get_ext_clk_users(tomtom);
+			wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr,
+						     WCD9XXX_CLK_RCO);
+			WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+			tomtom_codec_mclk_enable(codec, false, false);
+		}
+
+	} else {
+		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+		wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr,
+					     WCD9XXX_CLK_RCO);
+		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+	}
+
+	return ret;
+}
+
+static int tomtom_codec_enable_aux_pga(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+		wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+		/* AUX PGA requires RCO or MCLK */
+		tomtom_codec_internal_rco_ctrl(codec, true);
+		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+		wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1);
+		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+		wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0);
+		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+		tomtom_codec_internal_rco_ctrl(codec, false);
+		WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+		wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_enable_lineout(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	u16 lineout_gain_reg;
+
+	pr_debug("%s %d %s\n", __func__, event, w->name);
+
+	switch (w->shift) {
+	case 0:
+		lineout_gain_reg = TOMTOM_A_RX_LINE_1_GAIN;
+		break;
+	case 1:
+		lineout_gain_reg = TOMTOM_A_RX_LINE_2_GAIN;
+		break;
+	case 2:
+		lineout_gain_reg = TOMTOM_A_RX_LINE_3_GAIN;
+		break;
+	case 3:
+		lineout_gain_reg = TOMTOM_A_RX_LINE_4_GAIN;
+		break;
+	default:
+		pr_err("%s: Error, incorrect lineout register value\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x40);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+						 WCD9XXX_CLSH_STATE_LO,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+		pr_debug("%s: sleeping 5 ms after %s PA turn on\n",
+				__func__, w->name);
+		/* Wait for CnP time after PA enable */
+		usleep_range(5000, 5100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, lineout_gain_reg, 0x40, 0x00);
+		pr_debug("%s: sleeping 5 ms after %s PA turn off\n",
+			 __func__, w->name);
+		/* Wait for CnP time after PA disable */
+		usleep_range(5000, 5100);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_enable_spk_pa(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	u16 spk_drv_reg;
+
+	pr_debug("%s: %d %s\n", __func__, event, w->name);
+	if (strnstr(w->name, "SPK2 PA", sizeof("SPK2 PA")))
+		spk_drv_reg = TOMTOM_A_SPKR_DRV2_EN;
+	else
+		spk_drv_reg = TOMTOM_A_SPKR_DRV1_EN;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tomtom->spkr_pa_widget_on = true;
+		snd_soc_update_bits(codec, spk_drv_reg, 0x80, 0x80);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tomtom->spkr_pa_widget_on = false;
+		snd_soc_update_bits(codec, spk_drv_reg, 0x80, 0x00);
+		break;
+	}
+	return 0;
+}
+
+static u8 tomtom_get_dmic_clk_val(struct snd_soc_codec *codec,
+	u32 mclk_rate, u32 dmic_clk_rate)
+{
+	u32 div_factor;
+	u8 dmic_ctl_val;
+
+	dev_dbg(codec->dev,
+		"%s: mclk_rate = %d, dmic_sample_rate = %d\n",
+		__func__, mclk_rate, dmic_clk_rate);
+
+	/* Default value to return in case of error */
+	if (mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ)
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_2;
+	else
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_3;
+
+	if (dmic_clk_rate == 0) {
+		dev_err(codec->dev,
+			"%s: dmic_sample_rate cannot be 0\n",
+			__func__);
+		goto done;
+	}
+
+	div_factor = mclk_rate / dmic_clk_rate;
+	switch (div_factor) {
+	case 2:
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_2;
+		break;
+	case 3:
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_3;
+		break;
+	case 4:
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_4;
+		break;
+	case 6:
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_6;
+		break;
+	case 16:
+		dmic_ctl_val = WCD9330_DMIC_CLK_DIV_16;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+			__func__, div_factor, mclk_rate, dmic_clk_rate);
+		break;
+	}
+
+done:
+	return dmic_ctl_val;
+}
+
+static int tomtom_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata;
+	u8  dmic_clk_en;
+	u16 dmic_clk_reg;
+	s32 *dmic_clk_cnt;
+	u8 dmic_rate_val, dmic_rate_shift;
+	unsigned int dmic;
+	int ret;
+	char *wname;
+
+	wname = strpbrk(w->name, "123456");
+	if (!wname) {
+		dev_err(codec->dev, "%s: widget not found\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = kstrtouint(wname, 10, &dmic);
+	if (ret < 0) {
+		pr_err("%s: Invalid DMIC line on the codec\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (dmic) {
+	case 1:
+	case 2:
+		dmic_clk_en = 0x01;
+		dmic_clk_cnt = &(tomtom->dmic_1_2_clk_cnt);
+		dmic_clk_reg = TOMTOM_A_DMIC_B1_CTL;
+		dmic_rate_shift = 5;
+		pr_debug("%s() event %d DMIC%d dmic_1_2_clk_cnt %d\n",
+			__func__, event,  dmic, *dmic_clk_cnt);
+
+		break;
+
+	case 3:
+	case 4:
+		dmic_clk_en = 0x02;
+		dmic_clk_cnt = &(tomtom->dmic_3_4_clk_cnt);
+		dmic_clk_reg = TOMTOM_A_DMIC_B2_CTL;
+		dmic_rate_shift = 1;
+		pr_debug("%s() event %d DMIC%d dmic_3_4_clk_cnt %d\n",
+			__func__, event,  dmic, *dmic_clk_cnt);
+		break;
+
+	case 5:
+	case 6:
+		dmic_clk_en = 0x04;
+		dmic_clk_cnt = &(tomtom->dmic_5_6_clk_cnt);
+		dmic_clk_reg = TOMTOM_A_DMIC_B2_CTL;
+		dmic_rate_shift = 4;
+		pr_debug("%s() event %d DMIC%d dmic_5_6_clk_cnt %d\n",
+			__func__, event,  dmic, *dmic_clk_cnt);
+
+		break;
+
+	default:
+		pr_err("%s: Invalid DMIC Selection\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+
+		dmic_rate_val =
+			tomtom_get_dmic_clk_val(codec,
+					pdata->mclk_rate,
+					pdata->dmic_sample_rate);
+
+		(*dmic_clk_cnt)++;
+		if (*dmic_clk_cnt == 1) {
+			snd_soc_update_bits(codec, dmic_clk_reg,
+				0x07 << dmic_rate_shift,
+				dmic_rate_val << dmic_rate_shift);
+			snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL,
+					dmic_clk_en, dmic_clk_en);
+		}
+
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+
+		dmic_rate_val =
+			tomtom_get_dmic_clk_val(codec,
+					pdata->mclk_rate,
+					pdata->mad_dmic_sample_rate);
+		(*dmic_clk_cnt)--;
+		if (*dmic_clk_cnt  == 0) {
+			snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL,
+					dmic_clk_en, 0);
+			snd_soc_update_bits(codec, dmic_clk_reg,
+				0x07 << dmic_rate_shift,
+				dmic_rate_val << dmic_rate_shift);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_config_mad(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	const struct firmware *fw;
+	struct firmware_cal *hwdep_cal = NULL;
+	struct mad_audio_cal *mad_cal;
+	const void *data;
+	const char *filename = TOMTOM_MAD_AUDIO_FIRMWARE_PATH;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	size_t cal_size;
+	int idx;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (!tomtom->fw_data) {
+		dev_err(codec->dev, "%s: invalid cal data\n",
+				__func__);
+		return -ENODEV;
+	}
+
+	hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, WCD9XXX_MAD_CAL);
+	if (hwdep_cal) {
+		data = hwdep_cal->data;
+		cal_size = hwdep_cal->size;
+		dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+				__func__);
+	} else {
+		ret = request_firmware(&fw, filename, codec->dev);
+		if (ret != 0) {
+			pr_err("Failed to acquire MAD firwmare data %s: %d\n",
+				filename, ret);
+			return -ENODEV;
+		}
+		if (!fw) {
+			dev_err(codec->dev, "failed to get mad fw");
+			return -ENODEV;
+		}
+		data = fw->data;
+		cal_size = fw->size;
+		dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+				__func__);
+	}
+	if (cal_size < sizeof(struct mad_audio_cal)) {
+		pr_err("%s: incorrect hwdep cal size %zu\n",
+			__func__, cal_size);
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	mad_cal = (struct mad_audio_cal *)(data);
+	if (!mad_cal) {
+		dev_err(codec->dev, "%s: Invalid calibration data\n",
+				__func__);
+		ret =  -EINVAL;
+		goto err;
+	}
+
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_MAIN_CTL_2,
+		      mad_cal->microphone_info.cycle_time);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_MAIN_CTL_1, 0xFF << 3,
+			    ((uint16_t)mad_cal->microphone_info.settle_time)
+			    << 3);
+
+	/* Audio */
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_8,
+		      mad_cal->audio_info.rms_omit_samples);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_1,
+			    0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_2, 0x03 << 2,
+			    mad_cal->audio_info.detection_mechanism << 2);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_7,
+		      mad_cal->audio_info.rms_diff_threshold & 0x3F);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_5,
+		      mad_cal->audio_info.rms_threshold_lsb);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_CTL_6,
+		      mad_cal->audio_info.rms_threshold_msb);
+
+	for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients);
+	     idx++) {
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR,
+				    0x3F, idx);
+		snd_soc_write(codec, TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL,
+			      mad_cal->audio_info.iir_coefficients[idx]);
+		dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x",
+			__func__, idx,
+			mad_cal->audio_info.iir_coefficients[idx]);
+	}
+
+	/* Beacon */
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_8,
+		      mad_cal->beacon_info.rms_omit_samples);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_1,
+			    0x07 << 4, mad_cal->beacon_info.rms_comp_time);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_2, 0x03 << 2,
+			    mad_cal->beacon_info.detection_mechanism << 2);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_7,
+		      mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_5,
+		      mad_cal->beacon_info.rms_threshold_lsb);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_6,
+		      mad_cal->beacon_info.rms_threshold_msb);
+
+	/* Ultrasound */
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_BEACON_CTL_1,
+			    0x07 << 4, mad_cal->beacon_info.rms_comp_time);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_2, 0x03 << 2,
+			    mad_cal->ultrasound_info.detection_mechanism);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_7,
+		      mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_5,
+		      mad_cal->ultrasound_info.rms_threshold_lsb);
+	snd_soc_write(codec, TOMTOM_A_CDC_MAD_ULTR_CTL_6,
+		      mad_cal->ultrasound_info.rms_threshold_msb);
+
+	/* Set MAD intr time to 20 msec */
+	snd_soc_update_bits(codec, 0x4E, 0x01F, 0x13);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+err:
+	if (!hwdep_cal)
+		release_firmware(fw);
+	return ret;
+}
+
+static int tomtom_codec_enable_mad(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	u8 mad_micb, mad_cfilt;
+	u16 mad_cfilt_reg;
+
+	mad_micb = snd_soc_read(codec, TOMTOM_A_MAD_ANA_CTRL) & 0x07;
+	switch (mad_micb) {
+	case 1:
+		mad_cfilt = tomtom->resmgr.pdata->micbias.bias1_cfilt_sel;
+		break;
+	case 2:
+		mad_cfilt = tomtom->resmgr.pdata->micbias.bias2_cfilt_sel;
+		break;
+	case 3:
+		mad_cfilt = tomtom->resmgr.pdata->micbias.bias3_cfilt_sel;
+		break;
+	case 4:
+		mad_cfilt = tomtom->resmgr.pdata->micbias.bias4_cfilt_sel;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: Invalid micbias selection 0x%x\n",
+			__func__, mad_micb);
+		return -EINVAL;
+	}
+
+	switch (mad_cfilt) {
+	case WCD9XXX_CFILT1_SEL:
+		mad_cfilt_reg = TOMTOM_A_MICB_CFILT_1_VAL;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		mad_cfilt_reg = TOMTOM_A_MICB_CFILT_2_VAL;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		mad_cfilt_reg = TOMTOM_A_MICB_CFILT_3_VAL;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: invalid cfilt 0x%x for micb 0x%x\n",
+			__func__, mad_cfilt, mad_micb);
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev,
+		"%s event = %d, mad_cfilt_reg = 0x%x\n",
+		__func__, event, mad_cfilt_reg);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Undo reset for MAD */
+		snd_soc_update_bits(codec, TOMTOM_A_SVASS_CLKRST_CTL,
+				    0x02, 0x00);
+
+		ret = tomtom_codec_config_mad(codec);
+		if (ret) {
+			pr_err("%s: Failed to config MAD\n", __func__);
+			break;
+		}
+
+		/* setup MAD micbias to VDDIO */
+		snd_soc_update_bits(codec, mad_cfilt_reg,
+				    0x02, 0x02);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* Reset the MAD block */
+		snd_soc_update_bits(codec, TOMTOM_A_SVASS_CLKRST_CTL,
+				    0x02, 0x02);
+
+		/* Undo setup of MAD micbias to VDDIO */
+		snd_soc_update_bits(codec, mad_cfilt_reg,
+				    0x02, 0x00);
+	}
+	return ret;
+}
+
+static int tomtom_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	u16 micb_int_reg = 0, micb_ctl_reg = 0;
+	u8 cfilt_sel_val = 0;
+	char *internal1_text = "Internal1";
+	char *internal2_text = "Internal2";
+	char *internal3_text = "Internal3";
+	enum wcd9xxx_notify_event e_post_off, e_pre_on, e_post_on;
+
+	pr_debug("%s: w->name %s event %d\n", __func__, w->name, event);
+	if (strnstr(w->name, "MIC BIAS1", sizeof("MIC BIAS1"))) {
+		micb_ctl_reg = TOMTOM_A_MICB_1_CTL;
+		micb_int_reg = TOMTOM_A_MICB_1_INT_RBIAS;
+		cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias1_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_1_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_1_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_1_OFF;
+	} else if (strnstr(w->name, "MIC BIAS2", sizeof("MIC BIAS2"))) {
+		micb_ctl_reg = TOMTOM_A_MICB_2_CTL;
+		micb_int_reg = TOMTOM_A_MICB_2_INT_RBIAS;
+		cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias2_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_2_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_2_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_2_OFF;
+	} else if (strnstr(w->name, "MIC BIAS3", sizeof("MIC BIAS3"))) {
+		micb_ctl_reg = TOMTOM_A_MICB_3_CTL;
+		micb_int_reg = TOMTOM_A_MICB_3_INT_RBIAS;
+		cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias3_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_3_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_3_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_3_OFF;
+	} else if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4"))) {
+		micb_ctl_reg = TOMTOM_A_MICB_4_CTL;
+		micb_int_reg = tomtom->resmgr.reg_addr->micb_4_int_rbias;
+		cfilt_sel_val = tomtom->resmgr.pdata->micbias.bias4_cfilt_sel;
+		e_pre_on = WCD9XXX_EVENT_PRE_MICBIAS_4_ON;
+		e_post_on = WCD9XXX_EVENT_POST_MICBIAS_4_ON;
+		e_post_off = WCD9XXX_EVENT_POST_MICBIAS_4_OFF;
+	} else {
+		pr_err("%s: Error, invalid micbias %s\n", __func__, w->name);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Let MBHC module know so micbias switch to be off */
+		wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_pre_on);
+
+		/* Get cfilt */
+		wcd9xxx_resmgr_cfilt_get(&tomtom->resmgr, cfilt_sel_val);
+
+		if (strnstr(w->name, internal1_text, 30))
+			snd_soc_update_bits(codec, micb_int_reg, 0xE0, 0xE0);
+		else if (strnstr(w->name, internal2_text, 30))
+			snd_soc_update_bits(codec, micb_int_reg, 0x1C, 0x1C);
+		else if (strnstr(w->name, internal3_text, 30))
+			snd_soc_update_bits(codec, micb_int_reg, 0x3, 0x3);
+		else
+			/*
+			 *  If not internal, make sure to write the
+			 *  register to default value
+			 */
+			snd_soc_write(codec, micb_int_reg, 0x24);
+		if (tomtom->mbhc_started && micb_ctl_reg ==
+		    TOMTOM_A_MICB_2_CTL) {
+			if (++tomtom->micb_2_users == 1) {
+				if (tomtom->resmgr.pdata->
+				    micbias.bias2_is_headset_only)
+					wcd9xxx_resmgr_add_cond_update_bits(
+							 &tomtom->resmgr,
+							 WCD9XXX_COND_HPH_MIC,
+							 micb_ctl_reg, w->shift,
+							 false);
+				else
+					snd_soc_update_bits(codec, micb_ctl_reg,
+							    1 << w->shift,
+							    1 << w->shift);
+			}
+			pr_debug("%s: micb_2_users %d\n", __func__,
+				 tomtom->micb_2_users);
+		} else {
+			snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
+					    1 << w->shift);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		usleep_range(5000, 5100);
+		/* Let MBHC module know so micbias is on */
+		wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_on);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (tomtom->mbhc_started && micb_ctl_reg ==
+		    TOMTOM_A_MICB_2_CTL) {
+			if (--tomtom->micb_2_users == 0) {
+				if (tomtom->resmgr.pdata->
+				    micbias.bias2_is_headset_only)
+					wcd9xxx_resmgr_rm_cond_update_bits(
+							&tomtom->resmgr,
+							WCD9XXX_COND_HPH_MIC,
+							micb_ctl_reg, 7, false);
+				else
+					snd_soc_update_bits(codec, micb_ctl_reg,
+							    1 << w->shift, 0);
+			}
+			pr_debug("%s: micb_2_users %d\n", __func__,
+				 tomtom->micb_2_users);
+			WARN(tomtom->micb_2_users < 0,
+			     "Unexpected micbias users %d\n",
+			     tomtom->micb_2_users);
+		} else {
+			snd_soc_update_bits(codec, micb_ctl_reg, 1 << w->shift,
+					    0);
+		}
+
+		/* Let MBHC module know so micbias switch to be off */
+		wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_off);
+
+		if (strnstr(w->name, internal1_text, 30))
+			snd_soc_update_bits(codec, micb_int_reg, 0x80, 0x00);
+		else if (strnstr(w->name, internal2_text, 30))
+			snd_soc_update_bits(codec, micb_int_reg, 0x10, 0x00);
+		else if (strnstr(w->name, internal3_text, 30))
+			snd_soc_update_bits(codec, micb_int_reg, 0x2, 0x0);
+
+		/* Put cfilt */
+		wcd9xxx_resmgr_cfilt_put(&tomtom->resmgr, cfilt_sel_val);
+		break;
+	}
+
+	return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static int tomtom_enable_mbhc_micbias(struct snd_soc_codec *codec, bool enable,
+				enum wcd9xxx_micbias_num micb_num)
+{
+	int rc;
+
+	if (micb_num != MBHC_MICBIAS2) {
+		dev_err(codec->dev, "%s: Unsupported micbias, micb_num=%d\n",
+			__func__, micb_num);
+		return -EINVAL;
+	}
+
+	if (enable)
+		rc = snd_soc_dapm_force_enable_pin(
+					     snd_soc_codec_get_dapm(codec),
+					     DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+	else
+		rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+					     DAPM_MICBIAS2_EXTERNAL_STANDALONE);
+	if (!rc)
+		snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+	pr_debug("%s: leave ret %d\n", __func__, rc);
+	return rc;
+}
+
+static void txfe_clkdiv_update(struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (test_bit(ADC1_TXFE, &priv->status_mask)) {
+		snd_soc_update_bits(codec, TOMTOM_A_TX_1_2_TXFE_CLKDIV,
+				    0x0F, 0x05);
+		clear_bit(ADC1_TXFE, &priv->status_mask);
+	}
+	if (test_bit(ADC2_TXFE, &priv->status_mask)) {
+		snd_soc_update_bits(codec, TOMTOM_A_TX_1_2_TXFE_CLKDIV,
+				    0xF0, 0x50);
+		clear_bit(ADC2_TXFE, &priv->status_mask);
+	}
+	if (test_bit(ADC3_TXFE, &priv->status_mask)) {
+		snd_soc_update_bits(codec, TOMTOM_A_TX_3_4_TXFE_CKDIV,
+				    0x0F, 0x05);
+		clear_bit(ADC3_TXFE, &priv->status_mask);
+	}
+	if (test_bit(ADC4_TXFE, &priv->status_mask)) {
+		snd_soc_update_bits(codec, TOMTOM_A_TX_3_4_TXFE_CKDIV,
+				    0xF0, 0x50);
+		clear_bit(ADC4_TXFE, &priv->status_mask);
+	}
+	if (test_bit(ADC5_TXFE, &priv->status_mask)) {
+		snd_soc_update_bits(codec, TOMTOM_A_TX_5_6_TXFE_CKDIV,
+				    0x0F, 0x05);
+		clear_bit(ADC5_TXFE, &priv->status_mask);
+	}
+	if (test_bit(ADC6_TXFE, &priv->status_mask)) {
+		snd_soc_update_bits(codec, TOMTOM_A_TX_5_6_TXFE_CKDIV,
+				    0xF0, 0x50);
+		clear_bit(ADC6_TXFE, &priv->status_mask);
+	}
+}
+
+static void tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+	struct delayed_work *hpf_delayed_work;
+	struct hpf_work *hpf_work;
+	struct tomtom_priv *tomtom;
+	struct snd_soc_codec *codec;
+	u16 tx_mux_ctl_reg;
+	u8 hpf_cut_of_freq;
+
+	hpf_delayed_work = to_delayed_work(work);
+	hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+	tomtom = hpf_work->tomtom;
+	codec = hpf_work->tomtom->codec;
+	hpf_cut_of_freq = hpf_work->tx_hpf_cut_of_freq;
+
+	tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL +
+			(hpf_work->decimator - 1) * 8;
+
+	pr_debug("%s(): decimator %u hpf_cut_of_freq 0x%x\n", __func__,
+		hpf_work->decimator, (unsigned int)hpf_cut_of_freq);
+
+	/*
+	 * Restore TXFE ClkDiv registers to default.
+	 * If any of these registers are modified during analog
+	 * front-end enablement, they will be restored back to the
+	 * default
+	 */
+	txfe_clkdiv_update(codec);
+
+	snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30, hpf_cut_of_freq << 4);
+}
+
+#define  TX_MUX_CTL_CUT_OFF_FREQ_MASK	0x30
+#define  CF_MIN_3DB_4HZ			0x0
+#define  CF_MIN_3DB_75HZ		0x1
+#define  CF_MIN_3DB_150HZ		0x2
+
+static int tomtom_codec_enable_dec(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	unsigned int decimator;
+	char *dec_name = NULL;
+	char *widget_name = NULL;
+	char *temp;
+	int ret = 0;
+	u16 dec_reset_reg, tx_vol_ctl_reg, tx_mux_ctl_reg;
+	u8 dec_hpf_cut_of_freq;
+	int offset;
+	char *dec;
+
+	pr_debug("%s %d\n", __func__, event);
+
+	widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+	if (!widget_name)
+		return -ENOMEM;
+	temp = widget_name;
+
+	dec_name = strsep(&widget_name, " ");
+	widget_name = temp;
+	if (!dec_name) {
+		pr_err("%s: Invalid decimator = %s\n", __func__, w->name);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	dec = strpbrk(dec_name, "123456789");
+	if (!dec) {
+		dev_err(codec->dev, "%s: decimator index not found\n",
+			__func__);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	ret = kstrtouint(dec, 10, &decimator);
+	if (ret < 0) {
+		pr_err("%s: Invalid decimator = %s\n", __func__, dec_name);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	pr_debug("%s(): widget = %s dec_name = %s decimator = %u\n", __func__,
+			w->name, dec_name, decimator);
+
+	if (w->reg == TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL) {
+		dec_reset_reg = TOMTOM_A_CDC_CLK_TX_RESET_B1_CTL;
+		offset = 0;
+	} else if (w->reg == TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL) {
+		dec_reset_reg = TOMTOM_A_CDC_CLK_TX_RESET_B2_CTL;
+		offset = 8;
+	} else {
+		pr_err("%s: Error, incorrect dec\n", __func__);
+		return -EINVAL;
+	}
+
+	tx_vol_ctl_reg = TOMTOM_A_CDC_TX1_VOL_CTL_CFG + 8 * (decimator - 1);
+	tx_mux_ctl_reg = TOMTOM_A_CDC_TX1_MUX_CTL + 8 * (decimator - 1);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+
+		/* Enableable TX digital mute */
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
+
+		snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift,
+			1 << w->shift);
+		snd_soc_update_bits(codec, dec_reset_reg, 1 << w->shift, 0x0);
+
+		pr_debug("%s: decimator = %u, bypass = %d\n", __func__,
+			decimator, tx_hpf_work[decimator - 1].tx_hpf_bypass);
+		if (tx_hpf_work[decimator - 1].tx_hpf_bypass != true) {
+			dec_hpf_cut_of_freq = snd_soc_read(codec,
+							tx_mux_ctl_reg);
+
+			dec_hpf_cut_of_freq = (dec_hpf_cut_of_freq & 0x30) >> 4;
+
+			tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq =
+				dec_hpf_cut_of_freq;
+
+			if (dec_hpf_cut_of_freq != CF_MIN_3DB_150HZ) {
+
+				/* set cut of freq to CF_MIN_3DB_150HZ (0x1); */
+				snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30,
+							CF_MIN_3DB_150HZ << 4);
+			}
+
+			/* enable HPF */
+			snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x00);
+		} else
+			/* bypass HPF */
+			snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08);
+
+		break;
+
+	case SND_SOC_DAPM_POST_PMU:
+
+		/* Disable TX digital mute */
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x00);
+
+		if ((tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq !=
+				CF_MIN_3DB_150HZ) &&
+			(tx_hpf_work[decimator - 1].tx_hpf_bypass != true)) {
+
+			schedule_delayed_work(&tx_hpf_work[decimator - 1].dwork,
+					msecs_to_jiffies(300));
+		}
+		/* apply the digital gain after the decimator is enabled*/
+		if ((w->shift + offset) < ARRAY_SIZE(tx_digital_gain_reg))
+			snd_soc_write(codec,
+				  tx_digital_gain_reg[w->shift + offset],
+				  snd_soc_read(codec,
+				  tx_digital_gain_reg[w->shift + offset])
+				  );
+
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x01, 0x01);
+		cancel_delayed_work_sync(&tx_hpf_work[decimator - 1].dwork);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+
+		snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x08, 0x08);
+		snd_soc_update_bits(codec, tx_mux_ctl_reg, 0x30,
+			(tx_hpf_work[decimator - 1].tx_hpf_cut_of_freq) << 4);
+
+		break;
+	}
+out:
+	kfree(widget_name);
+	return ret;
+}
+
+static int tomtom_codec_enable_vdd_spkr(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: %d %s\n", __func__, event, w->name);
+
+	WARN_ONCE(!priv->spkdrv_reg, "SPKDRV supply %s isn't defined\n",
+		  WCD9XXX_VDD_SPKDRV_NAME);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (priv->spkdrv_reg) {
+			ret = regulator_enable(priv->spkdrv_reg);
+			if (ret)
+				pr_err("%s: Failed to enable spkdrv_reg %s\n",
+				       __func__, WCD9XXX_VDD_SPKDRV_NAME);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (priv->spkdrv_reg) {
+			ret = regulator_disable(priv->spkdrv_reg);
+			if (ret)
+				pr_err("%s: Failed to disable spkdrv_reg %s\n",
+				       __func__, WCD9XXX_VDD_SPKDRV_NAME);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int tomtom_codec_enable_vdd_spkr2(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol, int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: %d %s\n", __func__, event, w->name);
+
+	/*
+	 * If on-demand voltage regulators of spkr1 and spkr2 has been derived
+	 * from same power rail then same on-demand voltage regulator can be
+	 * used by both spkr1 and spkr2, if a separate device tree entry has
+	 * not been defined for on-demand voltage regulator for spkr2.
+	 */
+	if (!priv->spkdrv2_reg) {
+		if (priv->spkdrv_reg) {
+			priv->spkdrv2_reg = priv->spkdrv_reg;
+		} else {
+			WARN_ONCE(!priv->spkdrv2_reg,
+					"SPKDRV2 supply %s isn't defined\n",
+					WCD9XXX_VDD_SPKDRV2_NAME);
+			return 0;
+		}
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (priv->spkdrv2_reg) {
+			ret = regulator_enable(priv->spkdrv2_reg);
+			if (ret)
+				pr_err("%s: Failed to enable spkdrv2_reg %s ret:%d\n",
+				       __func__, WCD9XXX_VDD_SPKDRV2_NAME, ret);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (priv->spkdrv2_reg) {
+			ret = regulator_disable(priv->spkdrv2_reg);
+			if (ret)
+				pr_err("%s: Failed to disable spkdrv2_reg %s ret:%d\n",
+				       __func__, WCD9XXX_VDD_SPKDRV2_NAME, ret);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+static int tomtom_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s %d %s\n", __func__, event, w->name);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_RESET_CTL,
+			1 << w->shift, 1 << w->shift);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_RESET_CTL,
+			1 << w->shift, 0x0);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* apply the digital gain after the interpolator is enabled*/
+		if ((w->shift) < ARRAY_SIZE(rx_digital_gain_reg))
+			snd_soc_write(codec,
+				  rx_digital_gain_reg[w->shift],
+				  snd_soc_read(codec,
+				  rx_digital_gain_reg[w->shift])
+				  );
+		/* Check for Rx1 and Rx2 paths for uhqa mode update */
+		if (w->shift == 0 || w->shift == 1)
+			tomtom_update_uhqa_mode(codec, (1 << w->shift));
+
+		break;
+	}
+	return 0;
+}
+
+/* called under codec_resource_lock acquisition */
+static int __tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: enter\n", __func__);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/*
+		 * ldo_h_users is protected by tomtom->codec_mutex, don't need
+		 * additional mutex
+		 */
+		if (++priv->ldo_h_users == 1) {
+			WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
+			wcd9xxx_resmgr_get_bandgap(&priv->resmgr,
+						   WCD9XXX_BANDGAP_AUDIO_MODE);
+			WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
+			tomtom_codec_internal_rco_ctrl(codec, true);
+			snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1,
+					    1 << 7, 1 << 7);
+			tomtom_codec_internal_rco_ctrl(codec, false);
+			pr_debug("%s: ldo_h_users %d\n", __func__,
+				 priv->ldo_h_users);
+			/* LDO enable requires 1ms to settle down */
+			usleep_range(1000, 1100);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (--priv->ldo_h_users == 0) {
+			tomtom_codec_internal_rco_ctrl(codec, true);
+			snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1,
+					    1 << 7, 0);
+			tomtom_codec_internal_rco_ctrl(codec, false);
+			WCD9XXX_BG_CLK_LOCK(&priv->resmgr);
+			wcd9xxx_resmgr_put_bandgap(&priv->resmgr,
+						   WCD9XXX_BANDGAP_AUDIO_MODE);
+			WCD9XXX_BG_CLK_UNLOCK(&priv->resmgr);
+			pr_debug("%s: ldo_h_users %d\n", __func__,
+				 priv->ldo_h_users);
+		}
+		WARN(priv->ldo_h_users < 0, "Unexpected ldo_h users %d\n",
+		     priv->ldo_h_users);
+		break;
+	}
+	pr_debug("%s: leave\n", __func__);
+	return 0;
+}
+
+static int tomtom_codec_enable_ldo_h(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *kcontrol, int event)
+{
+	int rc;
+
+	rc = __tomtom_codec_enable_ldo_h(w, kcontrol, event);
+	return rc;
+}
+
+static int tomtom_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 1);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd9xxx_resmgr_enable_rx_bias(&tomtom->resmgr, 0);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_enable_anc(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	const char *filename;
+	const struct firmware *fw;
+	int i;
+	int ret = 0;
+	int num_anc_slots;
+	struct wcd9xxx_anc_header *anc_head;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	struct firmware_cal *hwdep_cal = NULL;
+	u32 anc_writes_size = 0;
+	u32 anc_cal_size = 0;
+	int anc_size_remaining;
+	u32 *anc_ptr;
+	u16 reg;
+	u8 mask, val, old_val;
+	size_t cal_size;
+	const void *data;
+
+	if (tomtom->anc_func == 0)
+		return 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		filename = "wcd9320/wcd9320_anc.bin";
+
+		hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, WCD9XXX_ANC_CAL);
+		if (hwdep_cal) {
+			data = hwdep_cal->data;
+			cal_size = hwdep_cal->size;
+			dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+				__func__);
+		} else {
+			ret = request_firmware(&fw, filename, codec->dev);
+			if (ret != 0) {
+				dev_err(codec->dev, "Failed to acquire ANC data: %d\n",
+					ret);
+				return -ENODEV;
+			}
+			if (!fw) {
+				dev_err(codec->dev, "failed to get anc fw");
+				return -ENODEV;
+			}
+			data = fw->data;
+			cal_size = fw->size;
+			dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+				__func__);
+		}
+		if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+			dev_err(codec->dev, "Not enough data\n");
+			ret = -ENOMEM;
+			goto err;
+		}
+		/* First number is the number of register writes */
+		anc_head = (struct wcd9xxx_anc_header *)(data);
+		anc_ptr = (u32 *)(data +
+				  sizeof(struct wcd9xxx_anc_header));
+		anc_size_remaining = cal_size -
+				     sizeof(struct wcd9xxx_anc_header);
+		num_anc_slots = anc_head->num_anc_slots;
+
+		if (tomtom->anc_slot >= num_anc_slots) {
+			dev_err(codec->dev, "Invalid ANC slot selected\n");
+			ret = -EINVAL;
+			goto err;
+		}
+		for (i = 0; i < num_anc_slots; i++) {
+			if (anc_size_remaining < TOMTOM_PACKED_REG_SIZE) {
+				dev_err(codec->dev, "Invalid register format\n");
+				ret = -EINVAL;
+				goto err;
+			}
+			anc_writes_size = (u32)(*anc_ptr);
+			anc_size_remaining -= sizeof(u32);
+			anc_ptr += 1;
+
+			if (anc_writes_size * TOMTOM_PACKED_REG_SIZE
+				> anc_size_remaining) {
+				dev_err(codec->dev, "Invalid register format\n");
+				ret = -EINVAL;
+				goto err;
+			}
+
+			if (tomtom->anc_slot == i)
+				break;
+
+			anc_size_remaining -= (anc_writes_size *
+				TOMTOM_PACKED_REG_SIZE);
+			anc_ptr += anc_writes_size;
+		}
+		if (i == num_anc_slots) {
+			dev_err(codec->dev, "Selected ANC slot not present\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		i = 0;
+		anc_cal_size = anc_writes_size;
+		if (w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) {
+			snd_soc_update_bits(codec,
+				TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x03, 0x03);
+			anc_writes_size = (anc_cal_size/2);
+		}
+
+		if (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL) {
+			snd_soc_update_bits(codec,
+				TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0C, 0x0C);
+			i = (anc_cal_size/2);
+			anc_writes_size = anc_cal_size;
+		}
+
+		for (; i < anc_writes_size; i++) {
+			TOMTOM_CODEC_UNPACK_ENTRY(anc_ptr[i], reg,
+				mask, val);
+			/*
+			 * ANC Soft reset register is ignored from ACDB
+			 * because ANC left soft reset bits will be called
+			 * while enabling ANC HPH Right DAC.
+			 */
+			if ((reg == TOMTOM_A_CDC_CLK_ANC_RESET_CTL) &&
+				((w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL) ||
+				(w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL))) {
+				continue;
+			}
+			old_val = snd_soc_read(codec, reg);
+			snd_soc_write(codec, reg, (old_val & ~mask) |
+				(val & mask));
+		}
+		if (w->reg == TOMTOM_A_RX_HPH_L_DAC_CTL)
+			snd_soc_update_bits(codec,
+				TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x03, 0x00);
+
+		if (w->reg == TOMTOM_A_RX_HPH_R_DAC_CTL)
+			snd_soc_update_bits(codec,
+				TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0C, 0x00);
+		if (!hwdep_cal)
+			release_firmware(fw);
+		txfe_clkdiv_update(codec);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		msleep(40);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC1_B1_CTL, 0x01,
+				    0x00);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC2_B1_CTL, 0x02,
+				    0x00);
+		msleep(20);
+		snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x0F);
+		snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_CLK_EN_CTL, 0);
+		snd_soc_write(codec, TOMTOM_A_CDC_CLK_ANC_RESET_CTL, 0x00);
+		break;
+	}
+	return 0;
+err:
+	if (!hwdep_cal)
+		release_firmware(fw);
+	return ret;
+}
+
+static int tomtom_hphl_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	uint32_t impedl, impedr;
+	int ret = 0;
+
+	pr_debug("%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tomtom_p->anc_func) {
+			tomtom_codec_enable_anc(w, kcontrol, event);
+			msleep(50);
+		}
+
+		if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+			wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_HPHL,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_PRE_DAC);
+		} else {
+			wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+						tomtom_p->uhqa_mode,
+						WCD9XXX_CLSAB_STATE_HPHL,
+						WCD9XXX_CLSAB_REQ_ENABLE);
+		}
+		ret = wcd9xxx_mbhc_get_impedance(&tomtom_p->mbhc,
+					&impedl, &impedr);
+		if (!ret)
+			wcd9xxx_clsh_imped_config(codec, impedl);
+		else
+			dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+						__func__, ret);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B3_CTL, 0xBC, 0x94);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B4_CTL, 0x30, 0x10);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B3_CTL, 0xBC, 0x00);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX1_B4_CTL, 0x30, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+			wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_HPHL,
+						 WCD9XXX_CLSH_REQ_DISABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+		} else {
+			wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+						tomtom_p->uhqa_mode,
+						WCD9XXX_CLSAB_STATE_HPHL,
+						WCD9XXX_CLSAB_REQ_DISABLE);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_hphr_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tomtom_p->anc_func) {
+			tomtom_codec_enable_anc(w, kcontrol, event);
+			msleep(50);
+		}
+
+		snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+		if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+			wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_HPHR,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_PRE_DAC);
+		} else {
+			wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+						tomtom_p->uhqa_mode,
+						WCD9XXX_CLSAB_STATE_HPHR,
+						WCD9XXX_CLSAB_REQ_ENABLE);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B3_CTL, 0xBC, 0x94);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B4_CTL, 0x30, 0x10);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B3_CTL, 0xBC, 0x00);
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_RX2_B4_CTL, 0x30, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
+		if (!high_perf_mode && !tomtom_p->uhqa_mode) {
+			wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_HPHR,
+						 WCD9XXX_CLSH_REQ_DISABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+		} else {
+			wcd9xxx_enable_high_perf_mode(codec, &tomtom_p->clsh_d,
+						tomtom_p->uhqa_mode,
+						WCD9XXX_CLSAB_STATE_HPHR,
+						WCD9XXX_CLSAB_REQ_DISABLE);
+		}
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_hph_pa_event(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	enum wcd9xxx_notify_event e_pre_on, e_post_off;
+	u8 req_clsh_state;
+	u32 pa_settle_time = TOMTOM_HPH_PA_SETTLE_COMP_OFF;
+
+	pr_debug("%s: %s event = %d\n", __func__, w->name, event);
+	if (w->shift == 5) {
+		e_pre_on = WCD9XXX_EVENT_PRE_HPHL_PA_ON;
+		e_post_off = WCD9XXX_EVENT_POST_HPHL_PA_OFF;
+		req_clsh_state = WCD9XXX_CLSH_STATE_HPHL;
+	} else if (w->shift == 4) {
+		e_pre_on = WCD9XXX_EVENT_PRE_HPHR_PA_ON;
+		e_post_off = WCD9XXX_EVENT_POST_HPHR_PA_OFF;
+		req_clsh_state = WCD9XXX_CLSH_STATE_HPHR;
+	} else {
+		pr_err("%s: Invalid w->shift %d\n", __func__, w->shift);
+		return -EINVAL;
+	}
+
+	if (tomtom->comp_enabled[COMPANDER_1])
+		pa_settle_time = TOMTOM_HPH_PA_SETTLE_COMP_ON;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		set_bit(HPH_DELAY, &tomtom->status_mask);
+		/* Let MBHC module know PA is turning on */
+		wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_pre_on);
+		break;
+
+	case SND_SOC_DAPM_POST_PMU:
+		if (test_bit(HPH_DELAY, &tomtom->status_mask)) {
+			/*
+			 * Make sure to wait 10ms after enabling HPHR_HPHL
+			 * in register 0x1AB
+			 */
+			usleep_range(pa_settle_time, pa_settle_time + 1000);
+			clear_bit(HPH_DELAY, &tomtom->status_mask);
+			pr_debug("%s: sleep %d us after %s PA enable\n",
+				__func__, pa_settle_time, w->name);
+		}
+		if (!high_perf_mode && !tomtom->uhqa_mode) {
+			wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+						 req_clsh_state,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+		}
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		set_bit(HPH_DELAY, &tomtom->status_mask);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		/* Let MBHC module know PA turned off */
+		wcd9xxx_resmgr_notifier_call(&tomtom->resmgr, e_post_off);
+		if (test_bit(HPH_DELAY, &tomtom->status_mask)) {
+			/*
+			 * Make sure to wait 10ms after disabling HPHR_HPHL
+			 * in register 0x1AB
+			 */
+			usleep_range(pa_settle_time, pa_settle_time + 1000);
+			clear_bit(HPH_DELAY, &tomtom->status_mask);
+			pr_debug("%s: sleep %d us after %s PA disable\n",
+				__func__, pa_settle_time, w->name);
+		}
+
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_enable_anc_hph(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = tomtom_hph_pa_event(w, kcontrol, event);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if ((snd_soc_read(codec, TOMTOM_A_RX_HPH_L_DAC_CTL) & 0x80) &&
+			(snd_soc_read(codec, TOMTOM_A_RX_HPH_R_DAC_CTL)
+								& 0x80)) {
+			snd_soc_update_bits(codec,
+					TOMTOM_A_RX_HPH_CNP_EN, 0x30, 0x30);
+			msleep(30);
+		}
+		ret = tomtom_hph_pa_event(w, kcontrol, event);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		if (w->shift == 5) {
+			snd_soc_update_bits(codec,
+					TOMTOM_A_RX_HPH_CNP_EN, 0x30, 0x00);
+			msleep(40);
+			snd_soc_update_bits(codec,
+					TOMTOM_A_TX_7_MBHC_EN, 0x80, 00);
+			ret |= tomtom_codec_enable_anc(w, kcontrol, event);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = tomtom_hph_pa_event(w, kcontrol, event);
+		break;
+	}
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget tomtom_dapm_i2s_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("RX_I2S_CLK", TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+	4, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("TX_I2S_CLK", TOMTOM_A_CDC_CLK_TX_I2S_CTL, 4,
+	0, NULL, 0),
+};
+
+static int tomtom_lineout_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+						 WCD9XXX_CLSH_STATE_LO,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_PRE_DAC);
+		snd_soc_update_bits(codec, w->reg, 0x40, 0x40);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, w->reg, 0x40, 0x00);
+		wcd9xxx_clsh_fsm(codec, &tomtom->clsh_d,
+						 WCD9XXX_CLSH_STATE_LO,
+						 WCD9XXX_CLSH_REQ_DISABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_spk_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+							0x80, 0x80);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if ((snd_soc_read(codec, w->reg) & 0x03) == 0)
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+							0x80, 0x00);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_route audio_i2s_map[] = {
+	{"SLIM RX1", NULL, "RX_I2S_CLK"},
+	{"SLIM RX2", NULL, "RX_I2S_CLK"},
+	{"SLIM RX3", NULL, "RX_I2S_CLK"},
+	{"SLIM RX4", NULL, "RX_I2S_CLK"},
+
+	{"SLIM TX7 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX8 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX9 MUX", NULL, "TX_I2S_CLK"},
+	{"SLIM TX10 MUX", NULL, "TX_I2S_CLK"},
+
+	{"RX_I2S_CLK", NULL, "CDC_I2S_RX_CONN"},
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+	/* SLIMBUS Connections */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+
+	/* VI Feedback */
+	{"AIF4 VI", NULL, "VIONOFF"},
+	{"VIONOFF", "Switch", "VIINPUT"},
+
+	/* MAD */
+	{"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"},
+	{"MAD_SEL MUX", "MSM", "MADINPUT"},
+	{"MADONOFF", "Switch", "MAD_SEL MUX"},
+	{"AIF4 MAD", NULL, "MADONOFF"},
+
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF2_CAP Mixer"),*/
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	/* SLIM_MIXER("AIF3_CAP Mixer"),*/
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+
+	{"SLIM TX1 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX1 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX1 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX1 MUX", "RMIX3", "RX3 MIX1"},
+	{"SLIM TX1 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX1 MUX", "RMIX5", "RX5 MIX1"},
+	{"SLIM TX1 MUX", "RMIX6", "RX6 MIX1"},
+	{"SLIM TX1 MUX", "RMIX7", "RX7 MIX1"},
+	{"SLIM TX1 MUX", "RMIX8", "RX8 MIX1"},
+
+	{"SLIM TX2 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX2 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX2 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX2 MUX", "RMIX3", "RX3 MIX1"},
+	{"SLIM TX2 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX2 MUX", "RMIX5", "RX5 MIX1"},
+	{"SLIM TX2 MUX", "RMIX6", "RX6 MIX1"},
+	{"SLIM TX2 MUX", "RMIX7", "RX7 MIX1"},
+	{"SLIM TX2 MUX", "RMIX8", "RX8 MIX1"},
+
+	{"SLIM TX3 MUX", "DEC3", "DEC3 MUX"},
+	{"SLIM TX3 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX3 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX3 MUX", "RMIX3", "RX3 MIX1"},
+	{"SLIM TX3 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX3 MUX", "RMIX5", "RX5 MIX1"},
+	{"SLIM TX3 MUX", "RMIX6", "RX6 MIX1"},
+	{"SLIM TX3 MUX", "RMIX7", "RX7 MIX1"},
+	{"SLIM TX3 MUX", "RMIX8", "RX8 MIX1"},
+
+	{"SLIM TX4 MUX", "DEC4", "DEC4 MUX"},
+	{"SLIM TX4 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX4 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX4 MUX", "RMIX3", "RX3 MIX1"},
+	{"SLIM TX4 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX4 MUX", "RMIX5", "RX5 MIX1"},
+	{"SLIM TX4 MUX", "RMIX6", "RX6 MIX1"},
+	{"SLIM TX4 MUX", "RMIX7", "RX7 MIX1"},
+	{"SLIM TX4 MUX", "RMIX8", "RX8 MIX1"},
+
+	{"SLIM TX5 MUX", "DEC5", "DEC5 MUX"},
+	{"SLIM TX5 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX5 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX5 MUX", "RMIX3", "RX3 MIX1"},
+	{"SLIM TX5 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX5 MUX", "RMIX5", "RX5 MIX1"},
+	{"SLIM TX5 MUX", "RMIX6", "RX6 MIX1"},
+	{"SLIM TX5 MUX", "RMIX7", "RX7 MIX1"},
+	{"SLIM TX5 MUX", "RMIX8", "RX8 MIX1"},
+
+	{"SLIM TX6 MUX", "DEC6", "DEC6 MUX"},
+
+	{"SLIM TX7 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX7 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX7 MUX", "DEC3", "DEC3 MUX"},
+	{"SLIM TX7 MUX", "DEC4", "DEC4 MUX"},
+	{"SLIM TX7 MUX", "DEC5", "DEC5 MUX"},
+	{"SLIM TX7 MUX", "DEC6", "DEC6 MUX"},
+	{"SLIM TX7 MUX", "DEC7", "DEC7 MUX"},
+	{"SLIM TX7 MUX", "DEC8", "DEC8 MUX"},
+	{"SLIM TX7 MUX", "DEC9", "DEC9 MUX"},
+	{"SLIM TX7 MUX", "DEC10", "DEC10 MUX"},
+	{"SLIM TX7 MUX", "RMIX1", "RX1 MIX1"},
+	{"SLIM TX7 MUX", "RMIX2", "RX2 MIX1"},
+	{"SLIM TX7 MUX", "RMIX3", "RX3 MIX1"},
+	{"SLIM TX7 MUX", "RMIX4", "RX4 MIX1"},
+	{"SLIM TX7 MUX", "RMIX5", "RX5 MIX1"},
+	{"SLIM TX7 MUX", "RMIX6", "RX6 MIX1"},
+	{"SLIM TX7 MUX", "RMIX7", "RX7 MIX1"},
+
+	{"SLIM TX8 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX8 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX8 MUX", "DEC3", "DEC3 MUX"},
+	{"SLIM TX8 MUX", "DEC4", "DEC4 MUX"},
+	{"SLIM TX8 MUX", "DEC5", "DEC5 MUX"},
+	{"SLIM TX8 MUX", "DEC6", "DEC6 MUX"},
+	{"SLIM TX8 MUX", "DEC7", "DEC7 MUX"},
+	{"SLIM TX8 MUX", "DEC8", "DEC8 MUX"},
+	{"SLIM TX8 MUX", "DEC9", "DEC9 MUX"},
+	{"SLIM TX8 MUX", "DEC10", "DEC10 MUX"},
+
+	{"SLIM TX9 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX9 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX9 MUX", "DEC3", "DEC3 MUX"},
+	{"SLIM TX9 MUX", "DEC4", "DEC4 MUX"},
+	{"SLIM TX9 MUX", "DEC5", "DEC5 MUX"},
+	{"SLIM TX9 MUX", "DEC6", "DEC6 MUX"},
+	{"SLIM TX9 MUX", "DEC7", "DEC7 MUX"},
+	{"SLIM TX9 MUX", "DEC8", "DEC8 MUX"},
+	{"SLIM TX9 MUX", "DEC9", "DEC9 MUX"},
+	{"SLIM TX9 MUX", "DEC10", "DEC10 MUX"},
+
+	{"SLIM TX10 MUX", "DEC1", "DEC1 MUX"},
+	{"SLIM TX10 MUX", "DEC2", "DEC2 MUX"},
+	{"SLIM TX10 MUX", "DEC3", "DEC3 MUX"},
+	{"SLIM TX10 MUX", "DEC4", "DEC4 MUX"},
+	{"SLIM TX10 MUX", "DEC5", "DEC5 MUX"},
+	{"SLIM TX10 MUX", "DEC6", "DEC6 MUX"},
+	{"SLIM TX10 MUX", "DEC7", "DEC7 MUX"},
+	{"SLIM TX10 MUX", "DEC8", "DEC8 MUX"},
+	{"SLIM TX10 MUX", "DEC9", "DEC9 MUX"},
+	{"SLIM TX10 MUX", "DEC10", "DEC10 MUX"},
+
+	/* Earpiece (RX MIX1) */
+	{"EAR", NULL, "EAR PA"},
+	{"EAR PA", NULL, "EAR_PA_MIXER"},
+	{"EAR_PA_MIXER", NULL, "DAC1"},
+	{"DAC1", NULL, "RX_BIAS"},
+
+	{"ANC EAR", NULL, "ANC EAR PA"},
+	{"ANC EAR PA", NULL, "EAR_PA_MIXER"},
+	{"ANC1 FB MUX", "EAR_HPH_L", "RX1 MIX2"},
+	{"ANC1 FB MUX", "EAR_LINE_1", "RX2 MIX2"},
+
+	/* Headset (RX MIX1 and RX MIX2) */
+	{"HEADPHONE", NULL, "HPHL"},
+	{"HEADPHONE", NULL, "HPHR"},
+
+	{"HPHL", NULL, "HPHL_PA_MIXER"},
+	{"HPHL_PA_MIXER", NULL, "HPHL DAC"},
+	{"HPHL DAC", NULL, "RX_BIAS"},
+
+	{"HPHR", NULL, "HPHR_PA_MIXER"},
+	{"HPHR_PA_MIXER", NULL, "HPHR DAC"},
+	{"HPHR DAC", NULL, "RX_BIAS"},
+
+	{"ANC HEADPHONE", NULL, "ANC HPHL"},
+	{"ANC HEADPHONE", NULL, "ANC HPHR"},
+
+	{"ANC HPHL", NULL, "HPHL_PA_MIXER"},
+	{"ANC HPHR", NULL, "HPHR_PA_MIXER"},
+
+	{"ANC1 MUX", "ADC1", "ADC1"},
+	{"ANC1 MUX", "ADC2", "ADC2"},
+	{"ANC1 MUX", "ADC3", "ADC3"},
+	{"ANC1 MUX", "ADC4", "ADC4"},
+	{"ANC1 MUX", "ADC5", "ADC5"},
+	{"ANC1 MUX", "ADC6", "ADC6"},
+	{"ANC1 MUX", "DMIC1", "DMIC1"},
+	{"ANC1 MUX", "DMIC2", "DMIC2"},
+	{"ANC1 MUX", "DMIC3", "DMIC3"},
+	{"ANC1 MUX", "DMIC4", "DMIC4"},
+	{"ANC1 MUX", "DMIC5", "DMIC5"},
+	{"ANC1 MUX", "DMIC6", "DMIC6"},
+	{"ANC2 MUX", "ADC1", "ADC1"},
+	{"ANC2 MUX", "ADC2", "ADC2"},
+	{"ANC2 MUX", "ADC3", "ADC3"},
+	{"ANC2 MUX", "ADC4", "ADC4"},
+	{"ANC2 MUX", "ADC5", "ADC5"},
+	{"ANC2 MUX", "ADC6", "ADC6"},
+	{"ANC2 MUX", "DMIC1", "DMIC1"},
+	{"ANC2 MUX", "DMIC2", "DMIC2"},
+	{"ANC2 MUX", "DMIC3", "DMIC3"},
+	{"ANC2 MUX", "DMIC4", "DMIC4"},
+	{"ANC2 MUX", "DMIC5", "DMIC5"},
+	{"ANC2 MUX", "DMIC6", "DMIC6"},
+
+	{"ANC HPHR", NULL, "CDC_CONN"},
+
+	{"DAC1", "Switch", "CLASS_H_DSM MUX"},
+	{"HPHL DAC", "Switch", "CLASS_H_DSM MUX"},
+	{"HPHR DAC", NULL, "RX2 CHAIN"},
+
+	{"LINEOUT1", NULL, "LINEOUT1 PA"},
+	{"LINEOUT2", NULL, "LINEOUT2 PA"},
+	{"LINEOUT3", NULL, "LINEOUT3 PA"},
+	{"LINEOUT4", NULL, "LINEOUT4 PA"},
+	{"SPK_OUT", NULL, "SPK PA"},
+	{"SPK_OUT", NULL, "SPK2 PA"},
+
+	{"LINEOUT1 PA", NULL, "LINEOUT1_PA_MIXER"},
+	{"LINEOUT1_PA_MIXER", NULL, "LINEOUT1 DAC"},
+
+	{"LINEOUT2 PA", NULL, "LINEOUT2_PA_MIXER"},
+	{"LINEOUT2_PA_MIXER", NULL, "LINEOUT2 DAC"},
+
+	{"LINEOUT3 PA", NULL, "LINEOUT3_PA_MIXER"},
+	{"LINEOUT3_PA_MIXER", NULL, "LINEOUT3 DAC"},
+
+	{"LINEOUT4 PA", NULL, "LINEOUT4_PA_MIXER"},
+	{"LINEOUT4_PA_MIXER", NULL, "LINEOUT4 DAC"},
+
+	{"LINEOUT1 DAC", NULL, "RX3 MIX1"},
+
+	{"RDAC5 MUX", "DEM3_INV", "RX3 MIX1"},
+	{"RDAC5 MUX", "DEM4", "RX4 MIX1"},
+
+	{"LINEOUT3 DAC", NULL, "RDAC5 MUX"},
+
+	{"LINEOUT2 DAC", NULL, "RX5 MIX1"},
+
+	{"RDAC7 MUX", "DEM5_INV", "RX5 MIX1"},
+	{"RDAC7 MUX", "DEM6", "RX6 MIX1"},
+
+	{"LINEOUT4 DAC", NULL, "RDAC7 MUX"},
+
+	{"SPK PA", NULL, "SPK DAC"},
+	{"SPK DAC", NULL, "RX7 MIX2"},
+	{"SPK DAC", NULL, "VDD_SPKDRV"},
+
+	{"SPK2 PA", NULL, "SPK2 DAC"},
+	{"SPK2 DAC", NULL, "RX8 MIX1"},
+	{"SPK2 DAC", NULL, "VDD_SPKDRV2"},
+
+	{"CLASS_H_DSM MUX", "DSM_HPHL_RX1", "RX1 CHAIN"},
+
+	{"RX1 INTERP", NULL, "RX1 MIX2"},
+	{"RX1 CHAIN", NULL, "RX1 INTERP"},
+	{"RX2 INTERP", NULL, "RX2 MIX2"},
+	{"RX2 CHAIN", NULL, "RX2 INTERP"},
+	{"RX1 MIX2", NULL, "ANC1 MUX"},
+	{"RX2 MIX2", NULL, "ANC2 MUX"},
+
+	{"LINEOUT1 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT2 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT3 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT4 DAC", NULL, "RX_BIAS"},
+	{"SPK DAC", NULL, "RX_BIAS"},
+	{"SPK2 DAC", NULL, "RX_BIAS"},
+
+	{"RX7 MIX1", NULL, "COMP0_CLK"},
+	{"RX8 MIX1", NULL, "COMP0_CLK"},
+	{"RX1 MIX1", NULL, "COMP1_CLK"},
+	{"RX2 MIX1", NULL, "COMP1_CLK"},
+	{"RX3 MIX1", NULL, "COMP2_CLK"},
+	{"RX5 MIX1", NULL, "COMP2_CLK"},
+
+	{"RX1 MIX1", NULL, "RX1 MIX1 INP1"},
+	{"RX1 MIX1", NULL, "RX1 MIX1 INP2"},
+	{"RX1 MIX1", NULL, "RX1 MIX1 INP3"},
+	{"RX2 MIX1", NULL, "RX2 MIX1 INP1"},
+	{"RX2 MIX1", NULL, "RX2 MIX1 INP2"},
+	{"RX3 MIX1", NULL, "RX3 MIX1 INP1"},
+	{"RX3 MIX1", NULL, "RX3 MIX1 INP2"},
+	{"RX4 MIX1", NULL, "RX4 MIX1 INP1"},
+	{"RX4 MIX1", NULL, "RX4 MIX1 INP2"},
+	{"RX5 MIX1", NULL, "RX5 MIX1 INP1"},
+	{"RX5 MIX1", NULL, "RX5 MIX1 INP2"},
+	{"RX6 MIX1", NULL, "RX6 MIX1 INP1"},
+	{"RX6 MIX1", NULL, "RX6 MIX1 INP2"},
+	{"RX7 MIX1", NULL, "RX7 MIX1 INP1"},
+	{"RX7 MIX1", NULL, "RX7 MIX1 INP2"},
+	{"RX8 MIX1", NULL, "RX8 MIX1 INP1"},
+	{"RX8 MIX1", NULL, "RX8 MIX1 INP2"},
+	{"RX1 MIX2", NULL, "RX1 MIX1"},
+	{"RX1 MIX2", NULL, "RX1 MIX2 INP1"},
+	{"RX1 MIX2", NULL, "RX1 MIX2 INP2"},
+	{"RX2 MIX2", NULL, "RX2 MIX1"},
+	{"RX2 MIX2", NULL, "RX2 MIX2 INP1"},
+	{"RX2 MIX2", NULL, "RX2 MIX2 INP2"},
+	{"RX7 MIX2", NULL, "RX7 MIX1"},
+	{"RX7 MIX2", NULL, "RX7 MIX2 INP1"},
+	{"RX7 MIX2", NULL, "RX7 MIX2 INP2"},
+
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX8 MUX", "AIF1_PB", "AIF1 PB"},
+	/* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX8 MUX", "AIF2_PB", "AIF2 PB"},
+	/* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX8 MUX", "AIF3_PB", "AIF3 PB"},
+
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+	{"SLIM RX8", NULL, "SLIM RX8 MUX"},
+
+	{"RX1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX1 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX1 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX1 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX1 MIX1 INP3", "RX1", "SLIM RX1"},
+	{"RX1 MIX1 INP3", "RX2", "SLIM RX2"},
+	{"RX1 MIX1 INP3", "RX3", "SLIM RX3"},
+	{"RX1 MIX1 INP3", "RX4", "SLIM RX4"},
+	{"RX1 MIX1 INP3", "RX5", "SLIM RX5"},
+	{"RX1 MIX1 INP3", "RX6", "SLIM RX6"},
+	{"RX1 MIX1 INP3", "RX7", "SLIM RX7"},
+	{"RX2 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX2 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX2 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX2 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX2 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX2 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX2 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX2 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX2 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX2 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX2 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX2 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX2 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX2 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX2 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX2 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX2 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX2 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX3 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX3 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX3 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX3 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX3 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX3 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX3 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX3 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX3 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX3 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX3 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX3 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX3 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX3 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX3 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX3 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX3 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX3 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX4 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX4 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX4 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX4 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX4 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX4 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX4 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX4 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX4 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX4 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX4 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX4 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX4 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX4 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX4 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX4 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX4 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX4 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX5 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX5 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX5 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX5 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX5 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX5 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX5 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX5 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX5 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX5 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX5 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX5 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX5 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX5 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX5 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX5 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX5 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX5 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX6 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX6 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX6 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX6 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX6 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX6 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX6 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX6 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX6 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX6 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX6 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX6 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX6 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX6 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX6 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX6 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX6 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX6 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX7 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX7 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX7 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX7 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX7 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX7 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX7 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX7 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX7 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX7 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX7 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX7 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX7 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX7 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX7 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX7 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX7 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX7 MIX1 INP2", "IIR2", "IIR2"},
+	{"RX8 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX8 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX8 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX8 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX8 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX8 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX8 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX8 MIX1 INP1", "RX8", "SLIM RX8"},
+	{"RX8 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX8 MIX1 INP1", "IIR2", "IIR2"},
+	{"RX8 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX8 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX8 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX8 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX8 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX8 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX8 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX8 MIX1 INP2", "RX8", "SLIM RX8"},
+	{"RX8 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX8 MIX1 INP2", "IIR2", "IIR2"},
+
+	/* IIR1, IIR2 inputs to Second RX Mixer on RX1, RX2 and RX7 chains. */
+	{"RX1 MIX2 INP1", "IIR1", "IIR1"},
+	{"RX1 MIX2 INP2", "IIR1", "IIR1"},
+	{"RX2 MIX2 INP1", "IIR1", "IIR1"},
+	{"RX2 MIX2 INP2", "IIR1", "IIR1"},
+	{"RX7 MIX2 INP1", "IIR1", "IIR1"},
+	{"RX7 MIX2 INP2", "IIR1", "IIR1"},
+	{"RX1 MIX2 INP1", "IIR2", "IIR2"},
+	{"RX1 MIX2 INP2", "IIR2", "IIR2"},
+	{"RX2 MIX2 INP1", "IIR2", "IIR2"},
+	{"RX2 MIX2 INP2", "IIR2", "IIR2"},
+	{"RX7 MIX2 INP1", "IIR2", "IIR2"},
+	{"RX7 MIX2 INP2", "IIR2", "IIR2"},
+
+	/* Decimator Inputs */
+	{"DEC1 MUX", "DMIC1", "DMIC1"},
+	{"DEC1 MUX", "ADC6", "ADC6"},
+	{"DEC1 MUX", NULL, "CDC_CONN"},
+	{"DEC2 MUX", "DMIC2", "DMIC2"},
+	{"DEC2 MUX", "ADC5", "ADC5"},
+	{"DEC2 MUX", NULL, "CDC_CONN"},
+	{"DEC3 MUX", "DMIC3", "DMIC3"},
+	{"DEC3 MUX", "ADC4", "ADC4"},
+	{"DEC3 MUX", NULL, "CDC_CONN"},
+	{"DEC4 MUX", "DMIC4", "DMIC4"},
+	{"DEC4 MUX", "ADC3", "ADC3"},
+	{"DEC4 MUX", NULL, "CDC_CONN"},
+	{"DEC5 MUX", "DMIC5", "DMIC5"},
+	{"DEC5 MUX", "ADC2", "ADC2"},
+	{"DEC5 MUX", NULL, "CDC_CONN"},
+	{"DEC6 MUX", "DMIC6", "DMIC6"},
+	{"DEC6 MUX", "ADC1", "ADC1"},
+	{"DEC6 MUX", NULL, "CDC_CONN"},
+	{"DEC7 MUX", "DMIC1", "DMIC1"},
+	{"DEC7 MUX", "DMIC6", "DMIC6"},
+	{"DEC7 MUX", "ADC1", "ADC1"},
+	{"DEC7 MUX", "ADC6", "ADC6"},
+	{"DEC7 MUX", "ANC1_FB", "ANC1 MUX"},
+	{"DEC7 MUX", "ANC2_FB", "ANC2 MUX"},
+	{"DEC7 MUX", NULL, "CDC_CONN"},
+	{"DEC8 MUX", "DMIC2", "DMIC2"},
+	{"DEC8 MUX", "DMIC5", "DMIC5"},
+	{"DEC8 MUX", "ADC2", "ADC2"},
+	{"DEC8 MUX", "ADC5", "ADC5"},
+	{"DEC8 MUX", "ANC1_FB", "ANC1 MUX"},
+	{"DEC8 MUX", "ANC2_FB", "ANC2 MUX"},
+	{"DEC8 MUX", NULL, "CDC_CONN"},
+	{"DEC9 MUX", "DMIC4", "DMIC4"},
+	{"DEC9 MUX", "DMIC5", "DMIC5"},
+	{"DEC9 MUX", "ADC2", "ADC2"},
+	{"DEC9 MUX", "ADC3", "ADC3"},
+	{"DEC9 MUX", "ANC1_FB", "ANC1 MUX"},
+	{"DEC9 MUX", "ANC2_FB", "ANC2 MUX"},
+	{"DEC9 MUX", NULL, "CDC_CONN"},
+	{"DEC10 MUX", "DMIC3", "DMIC3"},
+	{"DEC10 MUX", "DMIC6", "DMIC6"},
+	{"DEC10 MUX", "ADC1", "ADC1"},
+	{"DEC10 MUX", "ADC4", "ADC4"},
+	{"DEC10 MUX", "ANC1_FB", "ANC1 MUX"},
+	{"DEC10 MUX", "ANC2_FB", "ANC2 MUX"},
+	{"DEC10 MUX", NULL, "CDC_CONN"},
+
+	/* ADC Connections */
+	{"ADC1", NULL, "AMIC1"},
+	{"ADC2", NULL, "AMIC2"},
+	{"ADC3", NULL, "AMIC3"},
+	{"ADC4", NULL, "AMIC4"},
+	{"ADC5", NULL, "AMIC5"},
+	{"ADC6", NULL, "AMIC6"},
+
+	/* AUX PGA Connections */
+	{"EAR_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+	{"HPHL_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+	{"HPHR_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
+	{"LINEOUT1_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+	{"LINEOUT2_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
+	{"LINEOUT3_PA_MIXER", "AUX_PGA_L Switch", "AUX_PGA_Left"},
+	{"LINEOUT4_PA_MIXER", "AUX_PGA_R Switch", "AUX_PGA_Right"},
+	{"AUX_PGA_Left", NULL, "AMIC5"},
+	{"AUX_PGA_Right", NULL, "AMIC6"},
+
+	{"IIR1", NULL, "IIR1 INP1 MUX"},
+	{"IIR1 INP1 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR1 INP1 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR1 INP1 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR1 INP1 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR1 INP1 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR1 INP1 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR1 INP1 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR1 INP1 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR1 INP1 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR1 INP1 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR1 INP1 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP1 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP1 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP1 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP1 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP1 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP1 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR2", NULL, "IIR2 INP1 MUX"},
+	{"IIR2 INP1 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR2 INP1 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR2 INP1 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR2 INP1 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR2 INP1 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR2 INP1 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR2 INP1 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR2 INP1 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR2 INP1 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR2 INP1 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR2 INP1 MUX", "RX1", "SLIM RX1"},
+	{"IIR2 INP1 MUX", "RX2", "SLIM RX2"},
+	{"IIR2 INP1 MUX", "RX3", "SLIM RX3"},
+	{"IIR2 INP1 MUX", "RX4", "SLIM RX4"},
+	{"IIR2 INP1 MUX", "RX5", "SLIM RX5"},
+	{"IIR2 INP1 MUX", "RX6", "SLIM RX6"},
+	{"IIR2 INP1 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR1", NULL, "IIR1 INP2 MUX"},
+	{"IIR1 INP2 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR1 INP2 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR1 INP2 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR1 INP2 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR1 INP2 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR1 INP2 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR1 INP2 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR1 INP2 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR1 INP2 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR1 INP2 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR1 INP2 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP2 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP2 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP2 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP2 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP2 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP2 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR2", NULL, "IIR2 INP2 MUX"},
+	{"IIR2 INP2 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR2 INP2 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR2 INP2 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR2 INP2 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR2 INP2 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR2 INP2 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR2 INP2 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR2 INP2 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR2 INP2 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR2 INP2 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR2 INP2 MUX", "RX1", "SLIM RX1"},
+	{"IIR2 INP2 MUX", "RX2", "SLIM RX2"},
+	{"IIR2 INP2 MUX", "RX3", "SLIM RX3"},
+	{"IIR2 INP2 MUX", "RX4", "SLIM RX4"},
+	{"IIR2 INP2 MUX", "RX5", "SLIM RX5"},
+	{"IIR2 INP2 MUX", "RX6", "SLIM RX6"},
+	{"IIR2 INP2 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR1", NULL, "IIR1 INP3 MUX"},
+	{"IIR1 INP3 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR1 INP3 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR1 INP3 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR1 INP3 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR1 INP3 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR1 INP3 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR1 INP3 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR1 INP3 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR1 INP3 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR1 INP3 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR1 INP3 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP3 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP3 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP3 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP3 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP3 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP3 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR2", NULL, "IIR2 INP3 MUX"},
+	{"IIR2 INP3 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR2 INP3 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR2 INP3 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR2 INP3 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR2 INP3 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR2 INP3 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR2 INP3 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR2 INP3 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR2 INP3 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR2 INP3 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR2 INP3 MUX", "RX1", "SLIM RX1"},
+	{"IIR2 INP3 MUX", "RX2", "SLIM RX2"},
+	{"IIR2 INP3 MUX", "RX3", "SLIM RX3"},
+	{"IIR2 INP3 MUX", "RX4", "SLIM RX4"},
+	{"IIR2 INP3 MUX", "RX5", "SLIM RX5"},
+	{"IIR2 INP3 MUX", "RX6", "SLIM RX6"},
+	{"IIR2 INP3 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR1", NULL, "IIR1 INP4 MUX"},
+	{"IIR1 INP4 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR1 INP4 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR1 INP4 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR1 INP4 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR1 INP4 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR1 INP4 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR1 INP4 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR1 INP4 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR1 INP4 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR1 INP4 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR1 INP4 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP4 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP4 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP4 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP4 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP4 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP4 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR2", NULL, "IIR2 INP4 MUX"},
+	{"IIR2 INP4 MUX", "DEC1", "DEC1 MUX"},
+	{"IIR2 INP4 MUX", "DEC2", "DEC2 MUX"},
+	{"IIR2 INP4 MUX", "DEC3", "DEC3 MUX"},
+	{"IIR2 INP4 MUX", "DEC4", "DEC4 MUX"},
+	{"IIR2 INP4 MUX", "DEC5", "DEC5 MUX"},
+	{"IIR2 INP4 MUX", "DEC6", "DEC6 MUX"},
+	{"IIR2 INP4 MUX", "DEC7", "DEC7 MUX"},
+	{"IIR2 INP4 MUX", "DEC8", "DEC8 MUX"},
+	{"IIR2 INP4 MUX", "DEC9", "DEC9 MUX"},
+	{"IIR2 INP4 MUX", "DEC10", "DEC10 MUX"},
+	{"IIR2 INP4 MUX", "RX1", "SLIM RX1"},
+	{"IIR2 INP4 MUX", "RX2", "SLIM RX2"},
+	{"IIR2 INP4 MUX", "RX3", "SLIM RX3"},
+	{"IIR2 INP4 MUX", "RX4", "SLIM RX4"},
+	{"IIR2 INP4 MUX", "RX5", "SLIM RX5"},
+	{"IIR2 INP4 MUX", "RX6", "SLIM RX6"},
+	{"IIR2 INP4 MUX", "RX7", "SLIM RX7"},
+
+	{"MIC BIAS1 Internal1", NULL, "LDO_H"},
+	{"MIC BIAS1 Internal2", NULL, "LDO_H"},
+	{"MIC BIAS1 External", NULL, "LDO_H"},
+	{"MIC BIAS2 Internal1", NULL, "LDO_H"},
+	{"MIC BIAS2 Internal2", NULL, "LDO_H"},
+	{"MIC BIAS2 Internal3", NULL, "LDO_H"},
+	{"MIC BIAS2 External", NULL, "LDO_H"},
+	{"MIC BIAS3 Internal1", NULL, "LDO_H"},
+	{"MIC BIAS3 Internal2", NULL, "LDO_H"},
+	{"MIC BIAS3 External", NULL, "LDO_H"},
+	{"MIC BIAS4 External", NULL, "LDO_H"},
+	{DAPM_MICBIAS2_EXTERNAL_STANDALONE, NULL, "LDO_H Standalone"},
+};
+
+static int tomtom_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	return 0;
+}
+
+static void tomtom_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+}
+
+int tomtom_mclk_enable(struct snd_soc_codec *codec, int mclk_enable, bool dapm)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: mclk_enable = %u, dapm = %d\n", __func__, mclk_enable,
+		 dapm);
+
+	WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+	if (mclk_enable) {
+		wcd9xxx_resmgr_get_bandgap(&tomtom->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+		wcd9xxx_resmgr_get_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK);
+	} else {
+		/* Put clock and BG */
+		wcd9xxx_resmgr_put_clk_block(&tomtom->resmgr, WCD9XXX_CLK_MCLK);
+		wcd9xxx_resmgr_put_bandgap(&tomtom->resmgr,
+					   WCD9XXX_BANDGAP_AUDIO_MODE);
+	}
+	WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+
+	return 0;
+}
+
+static int tomtom_set_dai_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+
+static int tomtom_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	u8 val = 0;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
+
+	pr_debug("%s\n", __func__);
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* CPU is master */
+		if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			if (dai->id == AIF1_CAP)
+				snd_soc_update_bits(dai->codec,
+					TOMTOM_A_CDC_CLK_TX_I2S_CTL,
+					TOMTOM_I2S_MASTER_MODE_MASK, 0);
+			else if (dai->id == AIF1_PB)
+				snd_soc_update_bits(dai->codec,
+					TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+					TOMTOM_I2S_MASTER_MODE_MASK, 0);
+		}
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+	/* CPU is slave */
+		if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			val = TOMTOM_I2S_MASTER_MODE_MASK;
+			if (dai->id == AIF1_CAP)
+				snd_soc_update_bits(dai->codec,
+					TOMTOM_A_CDC_CLK_TX_I2S_CTL, val, val);
+			else if (dai->id == AIF1_PB)
+				snd_soc_update_bits(dai->codec,
+					TOMTOM_A_CDC_CLK_RX_I2S_CTL, val, val);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int tomtom_set_channel_map(struct snd_soc_dai *dai,
+				unsigned int tx_num, unsigned int *tx_slot,
+				unsigned int rx_num, unsigned int *rx_slot)
+
+{
+	struct wcd9xxx_codec_dai_data *dai_data = NULL;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
+	struct wcd9xxx *core = dev_get_drvdata(dai->codec->dev->parent);
+
+	if (!tx_slot || !rx_slot) {
+		pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n",
+			__func__, tx_slot, rx_slot);
+		return -EINVAL;
+	}
+	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+		 "tomtom->intf_type %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num,
+		 tomtom->intf_type);
+
+	if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+					   tx_num, tx_slot, rx_num, rx_slot);
+		/*Reserve tx11 and tx12 for VI feedback path*/
+		dai_data = &tomtom->dai[AIF4_VIFEED];
+		if (dai_data) {
+			list_add_tail(&core->tx_chs[TOMTOM_TX11].list,
+			&dai_data->wcd9xxx_ch_list);
+			list_add_tail(&core->tx_chs[TOMTOM_TX12].list,
+			&dai_data->wcd9xxx_ch_list);
+		}
+
+		/* Reserve TX13 for MAD data channel */
+		dai_data = &tomtom->dai[AIF4_MAD_TX];
+		if (dai_data)
+			list_add_tail(&core->tx_chs[TOMTOM_TX13].list,
+				      &dai_data->wcd9xxx_ch_list);
+	}
+
+	return 0;
+}
+
+static int tomtom_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+
+{
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
+
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n",
+				 __func__, rx_slot, rx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tomtom_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: slot_num %u ch->ch_num %d\n",
+				 __func__, i, ch->ch_num);
+			rx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: rx_num %d\n", __func__, i);
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+	case AIF4_VIFEED:
+	case AIF4_MAD_TX:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n",
+				 __func__, tx_slot, tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tomtom_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: slot_num %u ch->ch_num %d\n",
+				 __func__, i,  ch->ch_num);
+			tx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: tx_num %d\n", __func__, i);
+		*tx_num = i;
+		break;
+
+	default:
+		pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+		break;
+	}
+
+	return 0;
+}
+
+static int tomtom_set_interpolator_rate(struct snd_soc_dai *dai,
+	u8 rx_fs_rate_reg_val, u32 compander_fs, u32 sample_rate)
+{
+	u32 j;
+	u8 rx_mix1_inp, rx8_mix1_inp;
+	u16 rx_mix_1_reg_1, rx_mix_1_reg_2;
+	u16 rx_fs_reg;
+	u8 rx_mix_1_reg_1_val, rx_mix_1_reg_2_val;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	int port_rx_8 = TOMTOM_RX_PORT_START_NUMBER + NUM_INTERPOLATORS - 1;
+
+	list_for_each_entry(ch, &tomtom->dai[dai->id].wcd9xxx_ch_list, list) {
+		/* for RX port starting from 16 instead of 10 like tabla */
+		rx_mix1_inp = ch->port + RX_MIX1_INP_SEL_RX1 -
+			      TOMTOM_TX_PORT_NUMBER;
+		rx8_mix1_inp = ch->port + RX8_MIX1_INP_SEL_RX1 -
+			       TOMTOM_RX_PORT_START_NUMBER;
+		if (((ch->port < port_rx_8) &&
+		     ((rx_mix1_inp < RX_MIX1_INP_SEL_RX1) ||
+		      (rx_mix1_inp > RX_MIX1_INP_SEL_RX7))) ||
+		    ((rx8_mix1_inp < RX8_MIX1_INP_SEL_RX1) ||
+		     (rx8_mix1_inp > RX8_MIX1_INP_SEL_RX8))) {
+			pr_err("%s: Invalid TOMTOM_RX%u port. Dai ID is %d\n",
+					__func__,  rx8_mix1_inp - 2,
+					dai->id);
+			return -EINVAL;
+		}
+
+		rx_mix_1_reg_1 = TOMTOM_A_CDC_CONN_RX1_B1_CTL;
+
+		for (j = 0; j < NUM_INTERPOLATORS - 1; j++) {
+			rx_mix_1_reg_2 = rx_mix_1_reg_1 + 1;
+
+			rx_mix_1_reg_1_val = snd_soc_read(codec,
+							  rx_mix_1_reg_1);
+			rx_mix_1_reg_2_val = snd_soc_read(codec,
+							  rx_mix_1_reg_2);
+
+			if (((rx_mix_1_reg_1_val & 0x0F) == rx_mix1_inp) ||
+			    (((rx_mix_1_reg_1_val >> 4) & 0x0F)
+				== rx_mix1_inp) ||
+			    ((rx_mix_1_reg_2_val & 0x0F) == rx_mix1_inp)) {
+
+				rx_fs_reg = TOMTOM_A_CDC_RX1_B5_CTL + 8 * j;
+
+				pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+					__func__, dai->id, j + 1);
+
+				pr_debug("%s: set RX%u sample rate to %u\n",
+					__func__, j + 1, sample_rate);
+
+				snd_soc_update_bits(codec, rx_fs_reg,
+						0xE0, rx_fs_rate_reg_val);
+
+				if (comp_rx_path[j] < COMPANDER_MAX)
+					tomtom->comp_fs[comp_rx_path[j]]
+					= compander_fs;
+			}
+			if (j < 2)
+				rx_mix_1_reg_1 += 3;
+			else
+				rx_mix_1_reg_1 += 2;
+		}
+
+		/* RX8 interpolator path */
+		rx_mix_1_reg_1_val = snd_soc_read(codec,
+						TOMTOM_A_CDC_CONN_RX8_B1_CTL);
+		if (((rx_mix_1_reg_1_val & 0x0F) == rx8_mix1_inp) ||
+		    (((rx_mix_1_reg_1_val >> 4) & 0x0F) == rx8_mix1_inp)) {
+			snd_soc_update_bits(codec, TOMTOM_A_CDC_RX8_B5_CTL,
+					    0xE0, rx_fs_rate_reg_val);
+			pr_debug("%s: AIF_PB DAI(%d) connected to RX%u\n",
+					__func__, dai->id, NUM_INTERPOLATORS);
+
+			pr_debug("%s: set RX%u sample rate to %u\n",
+					__func__, NUM_INTERPOLATORS,
+					sample_rate);
+			if (comp_rx_path[NUM_INTERPOLATORS - 1] < COMPANDER_MAX)
+				tomtom->comp_fs[comp_rx_path[j]] =
+							compander_fs;
+		}
+	}
+	return 0;
+}
+
+static int tomtom_set_decimator_rate(struct snd_soc_dai *dai,
+	u8 tx_fs_rate_reg_val, u32 sample_rate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port;
+	u16 tx_port_reg, tx_fs_reg;
+	u8 tx_port_reg_val;
+	s8 decimator;
+
+	list_for_each_entry(ch, &tomtom->dai[dai->id].wcd9xxx_ch_list, list) {
+
+		tx_port = ch->port + 1;
+		pr_debug("%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
+
+		if ((tx_port < 1) || (tx_port > NUM_DECIMATORS)) {
+			pr_err("%s: Invalid SLIM TX%u port. DAI ID is %d\n",
+				__func__, tx_port, dai->id);
+			return -EINVAL;
+		}
+
+		tx_port_reg = TOMTOM_A_CDC_CONN_TX_SB_B1_CTL + (tx_port - 1);
+		tx_port_reg_val =  snd_soc_read(codec, tx_port_reg);
+
+		decimator = 0;
+
+		if ((tx_port >= 1) && (tx_port <= 6)) {
+
+			tx_port_reg_val =  tx_port_reg_val & 0x0F;
+			if (tx_port_reg_val == 0x8)
+				decimator = tx_port;
+
+		} else if ((tx_port >= 7) && (tx_port <= NUM_DECIMATORS)) {
+
+			tx_port_reg_val =  tx_port_reg_val & 0x1F;
+
+			if ((tx_port_reg_val >= 0x8) &&
+			    (tx_port_reg_val <= 0x11)) {
+
+				decimator = (tx_port_reg_val - 0x8) + 1;
+			}
+		}
+
+		if (decimator) { /* SLIM_TX port has a DEC as input */
+
+			tx_fs_reg = TOMTOM_A_CDC_TX1_CLK_FS_CTL +
+				    8 * (decimator - 1);
+
+			pr_debug("%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+				__func__, decimator, tx_port, sample_rate);
+
+			snd_soc_update_bits(codec, tx_fs_reg, 0x07,
+					    tx_fs_rate_reg_val);
+
+		} else {
+			if ((tx_port_reg_val >= 0x1) &&
+			    (tx_port_reg_val <= 0x7)) {
+
+				pr_debug("%s: RMIX%u going to SLIM TX%u\n",
+					__func__, tx_port_reg_val, tx_port);
+
+			} else if  ((tx_port_reg_val >= 0x8) &&
+				    (tx_port_reg_val <= 0x11)) {
+
+				pr_err("%s: ERROR: Should not be here\n",
+				       __func__);
+				pr_err("%s: ERROR: DEC connected to SLIM TX%u\n",
+					__func__, tx_port);
+				return -EINVAL;
+
+			} else if (tx_port_reg_val == 0) {
+				pr_debug("%s: no signal to SLIM TX%u\n",
+					__func__, tx_port);
+			} else {
+				pr_err("%s: ERROR: wrong signal to SLIM TX%u\n",
+					__func__, tx_port);
+				pr_err("%s: ERROR: wrong signal = %u\n",
+					__func__, tx_port_reg_val);
+				return -EINVAL;
+			}
+		}
+	}
+	return 0;
+}
+
+static void tomtom_set_rxsb_port_format(struct snd_pcm_hw_params *params,
+				       struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *cdc_dai;
+	struct wcd9xxx_ch *ch;
+	int port;
+	u8 bit_sel;
+	u16 sb_ctl_reg, field_shift;
+
+	switch (params_width(params)) {
+	case 16:
+		bit_sel = 0x2;
+		tomtom_p->dai[dai->id].bit_width = 16;
+		break;
+	case 24:
+		bit_sel = 0x0;
+		tomtom_p->dai[dai->id].bit_width = 24;
+		break;
+	default:
+		dev_err(codec->dev, "Invalid format\n");
+		return;
+	}
+
+	cdc_dai = &tomtom_p->dai[dai->id];
+
+	list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) {
+		port = wcd9xxx_get_slave_port(ch->ch_num);
+		if (port < 0 ||
+		    !TOMTOM_VALIDATE_RX_SBPORT_RANGE(port)) {
+			dev_warn(codec->dev,
+				 "%s: invalid port ID %d returned for RX DAI\n",
+				 __func__, port);
+			return;
+		}
+
+		port = TOMTOM_CONVERT_RX_SBPORT_ID(port);
+
+		if (port <= 3) {
+			sb_ctl_reg = TOMTOM_A_CDC_CONN_RX_SB_B1_CTL;
+			field_shift = port << 1;
+		} else if (port <= 7) {
+			sb_ctl_reg = TOMTOM_A_CDC_CONN_RX_SB_B2_CTL;
+			field_shift = (port - 4) << 1;
+		} else { /* should not happen */
+			dev_warn(codec->dev,
+				 "%s: bad port ID %d\n", __func__, port);
+			return;
+		}
+
+		dev_dbg(codec->dev, "%s: sb_ctl_reg %x field_shift %x\n",
+			__func__, sb_ctl_reg, field_shift);
+		snd_soc_update_bits(codec, sb_ctl_reg, 0x3 << field_shift,
+				    bit_sel << field_shift);
+	}
+}
+
+static void tomtom_set_tx_sb_port_format(struct snd_pcm_hw_params *params,
+					 struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *cdc_dai;
+	struct wcd9xxx_ch *ch;
+	int port;
+	u8 bit_sel, bit_shift;
+	u16 sb_ctl_reg;
+
+	switch (params_width(params)) {
+	case 16:
+		bit_sel = 0x2;
+		tomtom_p->dai[dai->id].bit_width = 16;
+		break;
+	case 24:
+		bit_sel = 0x0;
+		tomtom_p->dai[dai->id].bit_width = 24;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid format %d\n", __func__,
+			params_width(params));
+		return;
+	}
+
+	cdc_dai = &tomtom_p->dai[dai->id];
+
+	list_for_each_entry(ch, &cdc_dai->wcd9xxx_ch_list, list) {
+		port = wcd9xxx_get_slave_port(ch->ch_num);
+		if (port < 0 ||
+		    !TOMTOM_VALIDATE_TX_SBPORT_RANGE(port)) {
+			dev_warn(codec->dev,
+				 "%s: invalid port ID %d returned for TX DAI\n",
+				 __func__, port);
+			return;
+		}
+
+		if (port < 6) /* 6 = SLIMBUS TX7 */
+			bit_shift = TOMTOM_BIT_ADJ_SHIFT_PORT1_6;
+		else if (port < 10)
+			bit_shift = TOMTOM_BIT_ADJ_SHIFT_PORT7_10;
+		else {
+			dev_warn(codec->dev,
+				 "%s: port ID %d bitwidth is fixed\n",
+				 __func__, port);
+			return;
+		}
+
+		sb_ctl_reg = (TOMTOM_A_CDC_CONN_TX_SB_B1_CTL + port);
+
+		dev_dbg(codec->dev, "%s: reg %x bit_sel %x bit_shift %x\n",
+			__func__, sb_ctl_reg, bit_sel, bit_shift);
+		snd_soc_update_bits(codec, sb_ctl_reg, 0x3 <<
+				    bit_shift, bit_sel << bit_shift);
+	}
+}
+
+static int tomtom_hw_params(struct snd_pcm_substream *substream,
+			    struct snd_pcm_hw_params *params,
+			    struct snd_soc_dai *dai)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(dai->codec);
+	u8 tx_fs_rate, rx_fs_rate, i2s_bit_mode;
+	u32 compander_fs;
+	int ret;
+
+	pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
+		 dai->name, dai->id, params_rate(params),
+		 params_channels(params));
+
+	switch (params_rate(params)) {
+	case 8000:
+		tx_fs_rate = 0x00;
+		rx_fs_rate = 0x00;
+		compander_fs = COMPANDER_FS_8KHZ;
+		break;
+	case 16000:
+		tx_fs_rate = 0x01;
+		rx_fs_rate = 0x20;
+		compander_fs = COMPANDER_FS_16KHZ;
+		break;
+	case 32000:
+		tx_fs_rate = 0x02;
+		rx_fs_rate = 0x40;
+		compander_fs = COMPANDER_FS_32KHZ;
+		break;
+	case 48000:
+		tx_fs_rate = 0x03;
+		rx_fs_rate = 0x60;
+		compander_fs = COMPANDER_FS_48KHZ;
+		break;
+	case 96000:
+		tx_fs_rate = 0x04;
+		rx_fs_rate = 0x80;
+		compander_fs = COMPANDER_FS_96KHZ;
+		break;
+	case 192000:
+		tx_fs_rate = 0x05;
+		rx_fs_rate = 0xA0;
+		compander_fs = COMPANDER_FS_192KHZ;
+		break;
+	default:
+		pr_err("%s: Invalid sampling rate %d\n", __func__,
+			params_rate(params));
+		return -EINVAL;
+	}
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_CAPTURE:
+		if (dai->id != AIF4_VIFEED &&
+		    dai->id != AIF4_MAD_TX) {
+			ret = tomtom_set_decimator_rate(dai, tx_fs_rate,
+							   params_rate(params));
+			if (ret < 0) {
+				pr_err("%s: set decimator rate failed %d\n",
+					__func__, ret);
+				return ret;
+			}
+		}
+
+		tomtom->dai[dai->id].rate   = params_rate(params);
+
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S16_LE:
+			i2s_bit_mode = 0x01;
+			tomtom->dai[dai->id].bit_width = 16;
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
+			tomtom->dai[dai->id].bit_width = 24;
+			i2s_bit_mode = 0x00;
+			break;
+		case SNDRV_PCM_FORMAT_S32_LE:
+			tomtom->dai[dai->id].bit_width = 32;
+			i2s_bit_mode = 0x00;
+			break;
+		default:
+			dev_err(codec->dev,
+				"%s: Invalid format 0x%x\n",
+				 __func__, params_format(params));
+			return -EINVAL;
+		}
+
+		if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_I2S_CTL,
+					    0x20, i2s_bit_mode << 5);
+			snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_I2S_CTL,
+					    0x07, tx_fs_rate);
+		} else {
+			/* only generic ports can have sample bit adjustment */
+			if (dai->id != AIF4_VIFEED &&
+			    dai->id != AIF4_MAD_TX)
+				tomtom_set_tx_sb_port_format(params, dai);
+		}
+
+		break;
+
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ret = tomtom_set_interpolator_rate(dai, rx_fs_rate,
+						  compander_fs,
+						  params_rate(params));
+		if (ret < 0) {
+			pr_err("%s: set decimator rate failed %d\n", __func__,
+				ret);
+			return ret;
+		}
+		if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			switch (params_format(params)) {
+			case SNDRV_PCM_FORMAT_S16_LE:
+				snd_soc_update_bits(codec,
+					TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+					0x20, 0x20);
+				break;
+			case SNDRV_PCM_FORMAT_S32_LE:
+				snd_soc_update_bits(codec,
+					TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+					0x20, 0x00);
+				break;
+			default:
+				pr_err("invalid format\n");
+				break;
+			}
+			snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_RX_I2S_CTL,
+					    0x03, (rx_fs_rate >> 0x05));
+		} else {
+			tomtom_set_rxsb_port_format(params, dai);
+			tomtom->dai[dai->id].rate   = params_rate(params);
+		}
+		break;
+	default:
+		pr_err("%s: Invalid stream type %d\n", __func__,
+			substream->stream);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tomtom_dai_ops = {
+	.startup = tomtom_startup,
+	.shutdown = tomtom_shutdown,
+	.hw_params = tomtom_hw_params,
+	.set_sysclk = tomtom_set_dai_sysclk,
+	.set_fmt = tomtom_set_dai_fmt,
+	.set_channel_map = tomtom_set_channel_map,
+	.get_channel_map = tomtom_get_channel_map,
+};
+
+static struct snd_soc_dai_driver tomtom_dai[] = {
+	{
+		.name = "tomtom_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_rx2",
+		.id = AIF2_PB,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_tx2",
+		.id = AIF2_CAP,
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 8,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_rx3",
+		.id = AIF3_PB,
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_tx3",
+		.id = AIF3_CAP,
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_vifeedback",
+		.id = AIF4_VIFEED,
+		.capture = {
+			.stream_name = "VIfeed",
+			.rates = SNDRV_PCM_RATE_48000,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 48000,
+			.rate_min = 48000,
+			.channels_min = 2,
+			.channels_max = 2,
+	 },
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_mad1",
+		.id = AIF4_MAD_TX,
+		.capture = {
+			.stream_name = "AIF4 MAD TX",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = TOMTOM_FORMATS_S16_S24_LE,
+			.rate_min = 16000,
+			.rate_max = 16000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+};
+
+static struct snd_soc_dai_driver tomtom_i2s_dai[] = {
+	{
+		.name = "tomtom_i2s_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_i2s_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_i2s_rx2",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+	{
+		.name = "tomtom_i2s_tx2",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.rates = WCD9330_RATES,
+			.formats = TOMTOM_FORMATS,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tomtom_dai_ops,
+	},
+};
+
+static int tomtom_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+					  bool up)
+{
+	int ret = 0;
+	struct wcd9xxx_ch *ch;
+
+	if (up) {
+		list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
+			if (ret < 0) {
+				pr_err("%s: Invalid slave port ID: %d\n",
+				       __func__, ret);
+				ret = -EINVAL;
+			} else {
+				set_bit(ret, &dai->ch_mask);
+			}
+		}
+	} else {
+		ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+					 msecs_to_jiffies(
+						TOMTOM_SLIM_CLOSE_TIMEOUT));
+		if (!ret) {
+			pr_err("%s: Slim close tx/rx wait timeout\n", __func__);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
+static void tomtom_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai,
+					  struct snd_soc_codec *codec)
+{
+	struct wcd9xxx_ch *ch;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int port_num = 0;
+	unsigned short reg = 0;
+	u8 val = 0;
+
+	if (!dai || !codec) {
+		pr_err("%s: Invalid params\n", __func__);
+		return;
+	}
+	list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+		if (ch->port >= TOMTOM_RX_PORT_START_NUMBER) {
+			port_num = ch->port - TOMTOM_RX_PORT_START_NUMBER;
+			reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 + (port_num / 8);
+			val = wcd9xxx_interface_reg_read(wcd9xxx,
+				reg);
+			if (!(val & (1 << (port_num % 8)))) {
+				val |= (1 << (port_num % 8));
+				wcd9xxx_interface_reg_write(
+					wcd9xxx, reg, val);
+				val = wcd9xxx_interface_reg_read(
+					wcd9xxx, reg);
+			}
+		} else {
+			port_num = ch->port;
+			reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+			val = wcd9xxx_interface_reg_read(wcd9xxx,
+				reg);
+			if (!(val & (1 << (port_num % 8)))) {
+				val |= (1 << (port_num % 8));
+				wcd9xxx_interface_reg_write(wcd9xxx,
+					reg, val);
+				val = wcd9xxx_interface_reg_read(
+					wcd9xxx, reg);
+			}
+		}
+	}
+}
+
+static int tomtom_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct wcd9xxx *core;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, codec->component.name,
+		codec->component.num_dai, w->sname, event);
+
+	/* Execute the callback only if interface type is slimbus */
+	if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		return 0;
+
+	dai = &tomtom_p->dai[w->shift];
+	pr_debug("%s: w->name %s w->shift %d event %d\n",
+		 __func__, w->name, w->shift, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dai->bus_down_in_recovery = false;
+		tomtom_codec_enable_int_port(dai, codec);
+		(void) tomtom_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		if (!dai->bus_down_in_recovery)
+			ret = tomtom_codec_enable_slim_chmask(dai, false);
+		else
+			pr_debug("%s: bus in recovery skip enable slim_chmask",
+				__func__);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_debug("%s: Disconnect RX port, ret = %d\n",
+				 __func__, ret);
+		}
+		break;
+	}
+	return ret;
+}
+
+static int tomtom_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol,
+				int event)
+{
+	struct wcd9xxx *core = NULL;
+	struct snd_soc_codec *codec = NULL;
+	struct tomtom_priv *tomtom_p = NULL;
+	u32 ret = 0;
+	struct wcd9xxx_codec_dai_data *dai = NULL;
+
+	if (!w) {
+		pr_err("%s invalid params\n", __func__);
+		return -EINVAL;
+	}
+	codec = snd_soc_dapm_to_codec(w->dapm);
+	tomtom_p = snd_soc_codec_get_drvdata(codec);
+	core = dev_get_drvdata(codec->dev->parent);
+
+	pr_debug("%s: event called! codec name %s num_dai %d stream name %s\n",
+		__func__, codec->component.name,
+		codec->component.num_dai, w->sname);
+
+	/* Execute the callback only if interface type is slimbus */
+	if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		pr_err("%s Interface is not correct", __func__);
+		return 0;
+	}
+
+	pr_debug("%s(): w->name %s event %d w->shift %d\n",
+		__func__, w->name, event, w->shift);
+	if (w->shift != AIF4_VIFEED) {
+		pr_err("%s Error in enabling the tx path\n", __func__);
+		ret = -EINVAL;
+		goto out_vi;
+	}
+	dai = &tomtom_p->dai[w->shift];
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/*Enable V&I sensing*/
+		snd_soc_update_bits(codec, TOMTOM_A_SPKR1_PROT_EN,
+				0x88, 0x88);
+		/*Enable spkr VI clocks*/
+		snd_soc_update_bits(codec,
+		TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0xC, 0xC);
+		dai->bus_down_in_recovery = false;
+		tomtom_codec_enable_int_port(dai, codec);
+		(void) tomtom_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					dai->rate, dai->bit_width,
+					&dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		if (ret)
+			pr_err("%s error in close_slim_sch_tx %d\n",
+				__func__, ret);
+		if (!dai->bus_down_in_recovery)
+			ret = tomtom_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+				&dai->wcd9xxx_ch_list,
+				dai->grph);
+			pr_debug("%s: Disconnect TX port, ret = %d\n",
+				__func__, ret);
+		}
+
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL,
+				0xC, 0x0);
+		/*Disable V&I sensing*/
+		snd_soc_update_bits(codec, TOMTOM_A_SPKR1_PROT_EN,
+				0x88, 0x00);
+		break;
+	}
+out_vi:
+	return ret;
+}
+
+/* __tomtom_codec_enable_slimtx: Enable the slimbus slave port
+ *				 for TX path
+ * @codec: Handle to the codec for which the slave port is to be
+ *	   enabled.
+ * @dai_data: The dai specific data for dai which is enabled.
+ */
+static int __tomtom_codec_enable_slimtx(struct snd_soc_codec *codec,
+		int event, struct wcd9xxx_codec_dai_data *dai_data)
+{
+	struct wcd9xxx *core;
+	int ret = 0;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dai_data->bus_down_in_recovery = false;
+		tomtom_codec_enable_int_port(dai_data, codec);
+		(void) tomtom_codec_enable_slim_chmask(dai_data, true);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai_data->wcd9xxx_ch_list,
+					      dai_data->rate,
+					      dai_data->bit_width,
+					      &dai_data->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_tx(core,
+						&dai_data->wcd9xxx_ch_list,
+						dai_data->grph);
+		if (!dai_data->bus_down_in_recovery)
+			ret = tomtom_codec_enable_slim_chmask(dai_data, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+					&dai_data->wcd9xxx_ch_list,
+					dai_data->grph);
+			dev_dbg(codec->dev,
+				"%s: Disconnect TX port, ret = %d\n",
+				 __func__, ret);
+		}
+		break;
+	}
+
+	return ret;
+}
+
+/*
+ * tomtom_codec_enable_slimtx_mad: Callback function that will be invoked
+ *	to setup the slave port for MAD.
+ * @codec: Handle to the codec
+ * @event: Indicates whether to enable or disable the slave port
+ */
+static int tomtom_codec_enable_slimtx_mad(struct snd_soc_codec *codec,
+					  u8 event)
+{
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *dai;
+	int dapm_event = SND_SOC_DAPM_POST_PMU;
+
+	dai = &tomtom_p->dai[AIF4_MAD_TX];
+
+	if (event == 0)
+		dapm_event = SND_SOC_DAPM_POST_PMD;
+
+	dev_dbg(codec->dev,
+		"%s: mad_channel, event = 0x%x\n",
+		 __func__, event);
+	return __tomtom_codec_enable_slimtx(codec, dapm_event, dai);
+}
+
+/*
+ * tomtom_codec_enable_slimtx: DAPM widget allback for TX widgets
+ * @w: widget for which this callback is invoked
+ * @kcontrol: kcontrol associated with this widget
+ * @event: DAPM supplied event indicating enable/disable
+ */
+static int tomtom_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *dai;
+
+	dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d stream name %s\n",
+		__func__, codec->component.name,
+		codec->component.num_dai, w->sname);
+
+	/* Execute the callback only if interface type is slimbus */
+	if (tomtom_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		return 0;
+
+	dev_dbg(codec->dev,
+		"%s(): w->name %s event %d w->shift %d\n",
+		__func__, w->name, event, w->shift);
+
+	dai = &tomtom_p->dai[w->shift];
+	return __tomtom_codec_enable_slimtx(codec, event, dai);
+}
+
+static int tomtom_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_EAR,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+
+		usleep_range(5000, 5100);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tomtom_priv *tomtom_p = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_EAR,
+						 WCD9XXX_CLSH_REQ_ENABLE,
+						 WCD9XXX_CLSH_EVENT_PRE_DAC);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd9xxx_clsh_fsm(codec, &tomtom_p->clsh_d,
+						 WCD9XXX_CLSH_STATE_EAR,
+						 WCD9XXX_CLSH_REQ_DISABLE,
+						 WCD9XXX_CLSH_EVENT_POST_PA);
+		usleep_range(5000, 5100);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU: /* fall through */
+	case SND_SOC_DAPM_PRE_PMD:
+		if (strnstr(w->name, "IIR1", sizeof("IIR1"))) {
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B1_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR1_GAIN_B1_CTL));
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B2_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR1_GAIN_B2_CTL));
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B3_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR1_GAIN_B3_CTL));
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR1_GAIN_B4_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR1_GAIN_B4_CTL));
+		} else {
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B1_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR2_GAIN_B1_CTL));
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B2_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR2_GAIN_B2_CTL));
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B3_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR2_GAIN_B3_CTL));
+			snd_soc_write(codec, TOMTOM_A_CDC_IIR2_GAIN_B4_CTL,
+				      snd_soc_read(codec,
+					      TOMTOM_A_CDC_IIR2_GAIN_B4_CTL));
+		}
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_dsm_mux_event(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u8 reg_val, zoh_mux_val = 0x00;
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		reg_val = snd_soc_read(codec, TOMTOM_A_CDC_CONN_CLSH_CTL);
+
+		if ((reg_val & 0x30) == 0x10)
+			zoh_mux_val = 0x04;
+		else if ((reg_val & 0x30) == 0x20)
+			zoh_mux_val = 0x08;
+
+		if (zoh_mux_val != 0x00)
+			snd_soc_update_bits(codec,
+					TOMTOM_A_CDC_CONN_CLSH_CTL,
+					0x0C, zoh_mux_val);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, TOMTOM_A_CDC_CONN_CLSH_CTL,
+							0x0C, 0x00);
+		break;
+	}
+	return 0;
+}
+
+static int tomtom_codec_enable_anc_ear(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = tomtom_codec_enable_anc(w, kcontrol, event);
+		msleep(50);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_EN, 0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		ret = tomtom_codec_enable_ear_pa(w, kcontrol, event);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, TOMTOM_A_RX_EAR_EN, 0x10, 0x00);
+		msleep(40);
+		ret |= tomtom_codec_enable_anc(w, kcontrol, event);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = tomtom_codec_enable_ear_pa(w, kcontrol, event);
+		break;
+	}
+	return ret;
+}
+
+/* Todo: Have separate dapm widgets for I2S and Slimbus.
+ * Might Need to have callbacks registered only for slimbus
+ */
+static const struct snd_soc_dapm_widget tomtom_dapm_widgets[] = {
+	/*RX stuff */
+	SND_SOC_DAPM_OUTPUT("EAR"),
+
+	SND_SOC_DAPM_PGA_E("EAR PA", TOMTOM_A_RX_EAR_EN, 4, 0, NULL, 0,
+			tomtom_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MIXER_E("DAC1", TOMTOM_A_RX_EAR_EN, 6, 0, dac1_switch,
+		ARRAY_SIZE(dac1_switch), tomtom_codec_ear_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+				AIF1_PB, 0, tomtom_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+				AIF2_PB, 0, tomtom_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+				AIF3_PB, 0, tomtom_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TOMTOM_RX1, 0,
+				&slim_rx_mux[TOMTOM_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TOMTOM_RX2, 0,
+				&slim_rx_mux[TOMTOM_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TOMTOM_RX3, 0,
+				&slim_rx_mux[TOMTOM_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TOMTOM_RX4, 0,
+				&slim_rx_mux[TOMTOM_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TOMTOM_RX5, 0,
+				&slim_rx_mux[TOMTOM_RX5]),
+	SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TOMTOM_RX6, 0,
+				&slim_rx_mux[TOMTOM_RX6]),
+	SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TOMTOM_RX7, 0,
+				&slim_rx_mux[TOMTOM_RX7]),
+	SND_SOC_DAPM_MUX("SLIM RX8 MUX", SND_SOC_NOPM, TOMTOM_RX8, 0,
+				&slim_rx_mux[TOMTOM_RX8]),
+
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX8", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Headphone */
+	SND_SOC_DAPM_OUTPUT("HEADPHONE"),
+	SND_SOC_DAPM_PGA_E("HPHL", TOMTOM_A_RX_HPH_CNP_EN, 5, 0, NULL, 0,
+		tomtom_hph_pa_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("HPHL DAC", TOMTOM_A_RX_HPH_L_DAC_CTL, 7, 0,
+		hphl_switch, ARRAY_SIZE(hphl_switch), tomtom_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_PGA_E("HPHR", TOMTOM_A_RX_HPH_CNP_EN, 4, 0, NULL, 0,
+		tomtom_hph_pa_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("HPHR DAC", NULL, TOMTOM_A_RX_HPH_R_DAC_CTL, 7, 0,
+		tomtom_hphr_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	/* Speaker */
+	SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT3"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT4"),
+	SND_SOC_DAPM_OUTPUT("SPK_OUT"),
+
+	SND_SOC_DAPM_PGA_E("LINEOUT1 PA", TOMTOM_A_RX_LINE_CNP_EN, 0, 0, NULL,
+			0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT2 PA", TOMTOM_A_RX_LINE_CNP_EN, 1, 0, NULL,
+			0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT3 PA", TOMTOM_A_RX_LINE_CNP_EN, 2, 0, NULL,
+			0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT4 PA", TOMTOM_A_RX_LINE_CNP_EN, 3, 0, NULL,
+			0, tomtom_codec_enable_lineout, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("SPK PA", SND_SOC_NOPM, 0, 0, NULL,
+			   0, tomtom_codec_enable_spk_pa,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("SPK2 PA", SND_SOC_NOPM, 0, 0, NULL,
+			   0, tomtom_codec_enable_spk_pa,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_DAC_E("LINEOUT1 DAC", NULL, TOMTOM_A_RX_LINE_1_DAC_CTL, 7,
+		0, tomtom_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("LINEOUT2 DAC", NULL, TOMTOM_A_RX_LINE_2_DAC_CTL, 7,
+		0, tomtom_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("LINEOUT3 DAC", NULL, TOMTOM_A_RX_LINE_3_DAC_CTL, 7,
+		0, tomtom_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SWITCH("LINEOUT3 DAC GROUND", SND_SOC_NOPM, 0, 0,
+				&lineout3_ground_switch),
+	SND_SOC_DAPM_DAC_E("LINEOUT4 DAC", NULL, TOMTOM_A_RX_LINE_4_DAC_CTL, 7,
+		0, tomtom_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SWITCH("LINEOUT4 DAC GROUND", SND_SOC_NOPM, 0, 0,
+				&lineout4_ground_switch),
+
+	SND_SOC_DAPM_DAC_E("SPK DAC", NULL, TOMTOM_A_CDC_BOOST_TRGR_EN, 0, 0,
+			   tomtom_spk_dac_event,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("SPK2 DAC", NULL, TOMTOM_A_CDC_BOOST_TRGR_EN, 1, 0,
+			   tomtom_spk_dac_event,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("VDD_SPKDRV", SND_SOC_NOPM, 0, 0,
+			    tomtom_codec_enable_vdd_spkr,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("VDD_SPKDRV2", SND_SOC_NOPM, 0, 0,
+			    tomtom_codec_enable_vdd_spkr2,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("RX1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX2 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX7 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("RX1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER_E("RX3 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 2, 0, NULL,
+		0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER_E("RX4 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 3, 0, NULL,
+		0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER_E("RX5 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 4, 0, NULL,
+		0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER_E("RX6 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 5, 0, NULL,
+		0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER_E("RX7 MIX2", TOMTOM_A_CDC_CLK_RX_B1_CTL, 6, 0, NULL,
+		0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MIXER_E("RX8 MIX1", TOMTOM_A_CDC_CLK_RX_B1_CTL, 7, 0, NULL,
+		0, tomtom_codec_enable_interpolator, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("RX1 INTERP", TOMTOM_A_CDC_CLK_RX_B1_CTL, 0, 0,
+			&rx1_interp_mux, tomtom_codec_enable_interpolator,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX2 INTERP", TOMTOM_A_CDC_CLK_RX_B1_CTL, 1, 0,
+			&rx2_interp_mux, tomtom_codec_enable_interpolator,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+
+	SND_SOC_DAPM_MIXER("RX1 CHAIN", TOMTOM_A_CDC_RX1_B6_CTL, 5, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX2 CHAIN", TOMTOM_A_CDC_RX2_B6_CTL, 5, 0, NULL, 0),
+
+	SND_SOC_DAPM_MUX("RX1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX1 MIX1 INP3", SND_SOC_NOPM, 0, 0,
+		&rx_mix1_inp3_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx2_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx2_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX3 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx3_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX4 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx4_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX5 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx5_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX5 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx5_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX6 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx6_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX6 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx6_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX7 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx7_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX7 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx7_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX8 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx8_mix1_inp1_mux),
+	SND_SOC_DAPM_MUX("RX8 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx8_mix1_inp2_mux),
+	SND_SOC_DAPM_MUX("RX1 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+		&rx1_mix2_inp1_mux),
+	SND_SOC_DAPM_MUX("RX1 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+		&rx1_mix2_inp2_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+		&rx2_mix2_inp1_mux),
+	SND_SOC_DAPM_MUX("RX2 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+		&rx2_mix2_inp2_mux),
+	SND_SOC_DAPM_MUX("RX7 MIX2 INP1", SND_SOC_NOPM, 0, 0,
+		&rx7_mix2_inp1_mux),
+	SND_SOC_DAPM_MUX("RX7 MIX2 INP2", SND_SOC_NOPM, 0, 0,
+		&rx7_mix2_inp2_mux),
+
+	SND_SOC_DAPM_MUX("RDAC5 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_dac5_mux),
+	SND_SOC_DAPM_MUX("RDAC7 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_dac7_mux),
+
+	SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0,
+		&mad_sel_mux),
+
+	SND_SOC_DAPM_MUX_E("CLASS_H_DSM MUX", SND_SOC_NOPM, 0, 0,
+		&class_h_dsm_mux, tomtom_codec_dsm_mux_event,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("CDC_I2S_RX_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 5, 0,
+			    NULL, 0),
+
+	/* TX */
+
+	SND_SOC_DAPM_SUPPLY("CDC_CONN", WCD9XXX_A_CDC_CLK_OTHR_CTL, 2, 0, NULL,
+		0),
+
+	SND_SOC_DAPM_SUPPLY("LDO_H", SND_SOC_NOPM, 7, 0,
+			    tomtom_codec_enable_ldo_h,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	/*
+	 * DAPM 'LDO_H Standalone' is to be powered by mbhc driver after
+	 * acquring codec_resource lock.
+	 * So call __tomtom_codec_enable_ldo_h instead and avoid deadlock.
+	 */
+	SND_SOC_DAPM_SUPPLY("LDO_H Standalone", SND_SOC_NOPM, 7, 0,
+			    __tomtom_codec_enable_ldo_h,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("COMP0_CLK", SND_SOC_NOPM, 0, 0,
+		tomtom_config_compander, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("COMP1_CLK", SND_SOC_NOPM, 1, 0,
+		tomtom_config_compander, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("COMP2_CLK", SND_SOC_NOPM, 2, 0,
+		tomtom_config_compander, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+
+
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 External", SND_SOC_NOPM, 7, 0,
+			       tomtom_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			       SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal1", SND_SOC_NOPM, 7, 0,
+			       tomtom_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			       SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1 Internal2", SND_SOC_NOPM, 7, 0,
+			       tomtom_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			       SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_INPUT("AMIC3"),
+
+	SND_SOC_DAPM_INPUT("AMIC4"),
+
+	SND_SOC_DAPM_INPUT("AMIC5"),
+
+	SND_SOC_DAPM_INPUT("AMIC6"),
+
+	SND_SOC_DAPM_MUX_E("DEC1 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 0, 0,
+		&dec1_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC2 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 1, 0,
+		&dec2_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC3 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 2, 0,
+		&dec3_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC4 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 3, 0,
+		&dec4_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC5 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 4, 0,
+		&dec5_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC6 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 5, 0,
+		&dec6_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC7 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 6, 0,
+		&dec7_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC8 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B1_CTL, 7, 0,
+		&dec8_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC9 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 0, 0,
+		&dec9_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("DEC10 MUX", TOMTOM_A_CDC_CLK_TX_CLK_EN_B2_CTL, 1, 0,
+		&dec10_mux, tomtom_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("ANC1 MUX", SND_SOC_NOPM, 0, 0, &anc1_mux),
+	SND_SOC_DAPM_MUX("ANC2 MUX", SND_SOC_NOPM, 0, 0, &anc2_mux),
+
+	SND_SOC_DAPM_OUTPUT("ANC HEADPHONE"),
+	SND_SOC_DAPM_PGA_E("ANC HPHL", SND_SOC_NOPM, 5, 0, NULL, 0,
+		tomtom_codec_enable_anc_hph,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_PGA_E("ANC HPHR", SND_SOC_NOPM, 4, 0, NULL, 0,
+		tomtom_codec_enable_anc_hph, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+		SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_OUTPUT("ANC EAR"),
+	SND_SOC_DAPM_PGA_E("ANC EAR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+		tomtom_codec_enable_anc_ear,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
+
+	SND_SOC_DAPM_INPUT("AMIC2"),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_EXTERNAL_STANDALONE, SND_SOC_NOPM,
+			       7, 0, tomtom_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 External", SND_SOC_NOPM, 7, 0,
+			       tomtom_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 Internal1", SND_SOC_NOPM, 7, 0,
+			       tomtom_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 Internal2", SND_SOC_NOPM, 7, 0,
+			       tomtom_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 Internal3", SND_SOC_NOPM, 7, 0,
+			       tomtom_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 External", SND_SOC_NOPM, 7, 0,
+			       tomtom_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 Internal1", SND_SOC_NOPM, 7, 0,
+			       tomtom_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 Internal2", SND_SOC_NOPM, 7, 0,
+			       tomtom_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU |
+			       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS4 External", SND_SOC_NOPM, 7,
+			       0, tomtom_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			       SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, tomtom_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, tomtom_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, tomtom_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
+		AIF4_VIFEED, 0, tomtom_codec_enable_slimvi_feedback,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0,
+			       SND_SOC_NOPM, 0, 0,
+			       tomtom_codec_enable_mad,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0,
+			    &aif4_mad_switch),
+	SND_SOC_DAPM_INPUT("MADINPUT"),
+	SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"),
+
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TOMTOM_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TOMTOM_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TOMTOM_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TOMTOM_TX4, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TOMTOM_TX5, 0,
+		&sb_tx5_mux),
+	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TOMTOM_TX6, 0,
+		&sb_tx6_mux),
+	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TOMTOM_TX7, 0,
+		&sb_tx7_mux),
+	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TOMTOM_TX8, 0,
+		&sb_tx8_mux),
+	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TOMTOM_TX9, 0,
+		&sb_tx9_mux),
+	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TOMTOM_TX10, 0,
+		&sb_tx10_mux),
+
+	/* Digital Mic Inputs */
+	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC6", NULL, SND_SOC_NOPM, 0, 0,
+		tomtom_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	/* Sidetone */
+	SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+
+	SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+
+	SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+	SND_SOC_DAPM_MUX("IIR1 INP4 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp4_mux),
+
+	SND_SOC_DAPM_MIXER_E("IIR1", TOMTOM_A_CDC_CLK_SD_CTL, 0, 0, NULL, 0,
+		tomtom_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_MUX("IIR2 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp1_mux),
+
+	SND_SOC_DAPM_MUX("IIR2 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp2_mux),
+
+	SND_SOC_DAPM_MUX("IIR2 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp3_mux),
+
+	SND_SOC_DAPM_MUX("IIR2 INP4 MUX", SND_SOC_NOPM, 0, 0, &iir2_inp4_mux),
+
+	SND_SOC_DAPM_MIXER_E("IIR2", TOMTOM_A_CDC_CLK_SD_CTL, 1, 0, NULL, 0,
+		tomtom_codec_set_iir_gain, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD),
+
+	/* AUX PGA */
+	SND_SOC_DAPM_ADC_E("AUX_PGA_Left", NULL, TOMTOM_A_RX_AUX_SW_CTL, 7, 0,
+		tomtom_codec_enable_aux_pga, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("AUX_PGA_Right", NULL, TOMTOM_A_RX_AUX_SW_CTL, 6, 0,
+		tomtom_codec_enable_aux_pga, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	/* Lineout, ear and HPH PA Mixers */
+
+	SND_SOC_DAPM_MIXER("EAR_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		ear_pa_mix, ARRAY_SIZE(ear_pa_mix)),
+
+	SND_SOC_DAPM_MIXER("HPHL_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		hphl_pa_mix, ARRAY_SIZE(hphl_pa_mix)),
+
+	SND_SOC_DAPM_MIXER("HPHR_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		hphr_pa_mix, ARRAY_SIZE(hphr_pa_mix)),
+
+	SND_SOC_DAPM_MIXER("LINEOUT1_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		lineout1_pa_mix, ARRAY_SIZE(lineout1_pa_mix)),
+
+	SND_SOC_DAPM_MIXER("LINEOUT2_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		lineout2_pa_mix, ARRAY_SIZE(lineout2_pa_mix)),
+
+	SND_SOC_DAPM_MIXER("LINEOUT3_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		lineout3_pa_mix, ARRAY_SIZE(lineout3_pa_mix)),
+
+	SND_SOC_DAPM_MIXER("LINEOUT4_PA_MIXER", SND_SOC_NOPM, 0, 0,
+		lineout4_pa_mix, ARRAY_SIZE(lineout4_pa_mix)),
+
+	SND_SOC_DAPM_SWITCH("VIONOFF", SND_SOC_NOPM, 0, 0,
+			    &aif4_vi_switch),
+
+	SND_SOC_DAPM_INPUT("VIINPUT"),
+};
+
+static irqreturn_t tomtom_slimbus_irq(int irq, void *data)
+{
+	struct tomtom_priv *priv = data;
+	struct snd_soc_codec *codec = priv->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	unsigned long status = 0;
+	int i, j, port_id, k;
+	u32 bit;
+	u8 val, int_val = 0;
+	bool tx, cleared;
+	unsigned short reg = 0;
+
+	for (i = TOMTOM_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+	     i <= TOMTOM_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+		val = wcd9xxx_interface_reg_read(wcd9xxx, i);
+		status |= ((u32)val << (8 * j));
+	}
+
+	for_each_set_bit(j, &status, 32) {
+		tx = (j >= 16 ? true : false);
+		port_id = (tx ? j - 16 : j);
+		val = wcd9xxx_interface_reg_read(wcd9xxx,
+				TOMTOM_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+		if (val) {
+			if (!tx)
+				reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 +
+					(port_id / 8);
+			else
+				reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 +
+					(port_id / 8);
+			int_val = wcd9xxx_interface_reg_read(
+				wcd9xxx, reg);
+			/*
+			 * Ignore interrupts for ports for which the
+			 * interrupts are not specifically enabled.
+			 */
+			if (!(int_val & (1 << (port_id % 8))))
+				continue;
+		}
+		if (val & TOMTOM_SLIM_IRQ_OVERFLOW)
+			pr_err_ratelimited(
+			   "%s: overflow error on %s port %d, value %x\n",
+			   __func__, (tx ? "TX" : "RX"), port_id, val);
+		if (val & TOMTOM_SLIM_IRQ_UNDERFLOW)
+			pr_err_ratelimited(
+			   "%s: underflow error on %s port %d, value %x\n",
+			   __func__, (tx ? "TX" : "RX"), port_id, val);
+		if ((val & TOMTOM_SLIM_IRQ_OVERFLOW) ||
+			(val & TOMTOM_SLIM_IRQ_UNDERFLOW)) {
+			if (!tx)
+				reg = TOMTOM_SLIM_PGD_PORT_INT_EN0 +
+					(port_id / 8);
+			else
+				reg = TOMTOM_SLIM_PGD_PORT_INT_TX_EN0 +
+					(port_id / 8);
+			int_val = wcd9xxx_interface_reg_read(wcd9xxx, reg);
+			if (int_val & (1 << (port_id % 8))) {
+				int_val = int_val ^ (1 << (port_id % 8));
+				wcd9xxx_interface_reg_write(wcd9xxx, reg,
+							    int_val);
+			}
+		}
+		if (val & TOMTOM_SLIM_IRQ_PORT_CLOSED) {
+			/*
+			 * INT SOURCE register starts from RX to TX
+			 * but port number in the ch_mask is in opposite way
+			 */
+			bit = (tx ? j - 16 : j + 16);
+			pr_debug("%s: %s port %d closed value %x, bit %u\n",
+				 __func__, (tx ? "TX" : "RX"), port_id, val,
+				 bit);
+			for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+				pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n",
+					 __func__, k, priv->dai[k].ch_mask);
+				if (test_and_clear_bit(bit,
+						       &priv->dai[k].ch_mask)) {
+					cleared = true;
+					if (!priv->dai[k].ch_mask)
+						wake_up(&priv->dai[k].dai_wait);
+					/*
+					 * There are cases when multiple DAIs
+					 * might be using the same slimbus
+					 * channel. Hence don't break here.
+					 */
+				}
+			}
+			WARN(!cleared,
+			     "Couldn't find slimbus %s port %d for closing\n",
+			     (tx ? "TX" : "RX"), port_id);
+		}
+		wcd9xxx_interface_reg_write(wcd9xxx,
+					    TOMTOM_SLIM_PGD_PORT_INT_CLR_RX_0 +
+					    (j / 8),
+					    1 << (j % 8));
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tomtom_handle_pdata(struct tomtom_priv *tomtom)
+{
+	struct snd_soc_codec *codec = tomtom->codec;
+	struct wcd9xxx_pdata *pdata = tomtom->resmgr.pdata;
+	int k1, k2, k3, dec, rc = 0;
+	u8 leg_mode, txfe_bypass, txfe_buff, flag;
+	u8 i = 0, j = 0;
+	u8 val_txfe = 0, value = 0;
+	u8 dmic_ctl_val, mad_dmic_ctl_val;
+	u8 anc_ctl_value = 0;
+	u32 def_dmic_rate;
+	u16 tx_dmic_ctl_reg;
+
+	if (!pdata) {
+		pr_err("%s: NULL pdata\n", __func__);
+		rc = -ENODEV;
+		goto done;
+	}
+
+	leg_mode = pdata->amic_settings.legacy_mode;
+	txfe_bypass = pdata->amic_settings.txfe_enable;
+	txfe_buff = pdata->amic_settings.txfe_buff;
+	flag = pdata->amic_settings.use_pdata;
+
+	/* Make sure settings are correct */
+	if ((pdata->micbias.ldoh_v > WCD9XXX_LDOH_3P0_V) ||
+	    (pdata->micbias.bias1_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias2_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias3_cfilt_sel > WCD9XXX_CFILT3_SEL) ||
+	    (pdata->micbias.bias4_cfilt_sel > WCD9XXX_CFILT3_SEL)) {
+		rc = -EINVAL;
+		goto done;
+	}
+	/* figure out k value */
+	k1 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr,
+				      pdata->micbias.cfilt1_mv);
+	k2 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr,
+				      pdata->micbias.cfilt2_mv);
+	k3 = wcd9xxx_resmgr_get_k_val(&tomtom->resmgr,
+				      pdata->micbias.cfilt3_mv);
+	if (k1 < 0 || k2 < 0 || k3 < 0) {
+		rc = -EINVAL;
+		goto done;
+	}
+	/* Set voltage level and always use LDO */
+	snd_soc_update_bits(codec, TOMTOM_A_LDO_H_MODE_1, 0x0C,
+			    (pdata->micbias.ldoh_v << 2));
+
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_1_VAL, 0xFC, (k1 << 2));
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_2_VAL, 0xFC, (k2 << 2));
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_3_VAL, 0xFC, (k3 << 2));
+
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_1_CTL, 0x60,
+			    (pdata->micbias.bias1_cfilt_sel << 5));
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_2_CTL, 0x60,
+			    (pdata->micbias.bias2_cfilt_sel << 5));
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_3_CTL, 0x60,
+			    (pdata->micbias.bias3_cfilt_sel << 5));
+	snd_soc_update_bits(codec, tomtom->resmgr.reg_addr->micb_4_ctl, 0x60,
+			    (pdata->micbias.bias4_cfilt_sel << 5));
+
+	for (i = 0; i < 6; j++, i += 2) {
+		if (flag & (0x01 << i)) {
+			val_txfe = (txfe_bypass & (0x01 << i)) ? 0x20 : 0x00;
+			val_txfe = val_txfe |
+				((txfe_buff & (0x01 << i)) ? 0x10 : 0x00);
+			snd_soc_update_bits(codec,
+				TOMTOM_A_TX_1_2_TEST_EN + j * 10,
+				0x30, val_txfe);
+		}
+		if (flag & (0x01 << (i + 1))) {
+			val_txfe = (txfe_bypass &
+					(0x01 << (i + 1))) ? 0x02 : 0x00;
+			val_txfe |= (txfe_buff &
+					(0x01 << (i + 1))) ? 0x01 : 0x00;
+			snd_soc_update_bits(codec,
+				TOMTOM_A_TX_1_2_TEST_EN + j * 10,
+				0x03, val_txfe);
+		}
+	}
+	if (flag & 0x40) {
+		value = (leg_mode & 0x40) ? 0x10 : 0x00;
+		value = value | ((txfe_bypass & 0x40) ? 0x02 : 0x00);
+		value = value | ((txfe_buff & 0x40) ? 0x01 : 0x00);
+		snd_soc_update_bits(codec, TOMTOM_A_TX_7_MBHC_EN,
+			0x13, value);
+	}
+
+	if (pdata->ocp.use_pdata) {
+		/* not defined in CODEC specification */
+		if (pdata->ocp.hph_ocp_limit == 1 ||
+			pdata->ocp.hph_ocp_limit == 5) {
+			rc = -EINVAL;
+			goto done;
+		}
+		snd_soc_update_bits(codec, TOMTOM_A_RX_COM_OCP_CTL,
+			0x0F, pdata->ocp.num_attempts);
+		snd_soc_write(codec, TOMTOM_A_RX_COM_OCP_COUNT,
+			((pdata->ocp.run_time << 4) | pdata->ocp.wait_time));
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_OCP_CTL,
+			0xE0, (pdata->ocp.hph_ocp_limit << 5));
+	}
+
+	for (i = 0; i < ARRAY_SIZE(pdata->regulator); i++) {
+		if (pdata->regulator[i].name &&
+		    !strcmp(pdata->regulator[i].name, "CDC_VDDA_RX")) {
+			if (pdata->regulator[i].min_uV == 1800000 &&
+			    pdata->regulator[i].max_uV == 1800000) {
+				snd_soc_write(codec, TOMTOM_A_BIAS_REF_CTL,
+					      0x1C);
+			} else if (pdata->regulator[i].min_uV == 2200000 &&
+				   pdata->regulator[i].max_uV == 2200000) {
+				snd_soc_write(codec, TOMTOM_A_BIAS_REF_CTL,
+					      0x1E);
+			} else {
+				pr_err("%s: unsupported CDC_VDDA_RX voltage\n"
+				       "min %d, max %d\n", __func__,
+				       pdata->regulator[i].min_uV,
+				       pdata->regulator[i].max_uV);
+				rc = -EINVAL;
+			}
+			break;
+		}
+	}
+
+	/* Set micbias capless mode with tail current */
+	value = (pdata->micbias.bias1_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_1_CTL, 0x1E, value);
+	value = (pdata->micbias.bias2_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_2_CTL, 0x1E, value);
+	value = (pdata->micbias.bias3_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_3_CTL, 0x1E, value);
+	value = (pdata->micbias.bias4_cap_mode == MICBIAS_EXT_BYP_CAP ?
+		 0x00 : 0x16);
+	snd_soc_update_bits(codec, TOMTOM_A_MICB_4_CTL, 0x1E, value);
+
+	/* Set the DMIC sample rate */
+	switch (pdata->mclk_rate) {
+	case TOMTOM_MCLK_CLK_9P6MHZ:
+		def_dmic_rate =
+			WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+		break;
+	case TOMTOM_MCLK_CLK_12P288MHZ:
+		def_dmic_rate =
+			WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+		break;
+	default:
+		/* should never happen */
+		pr_err("%s: Invalid mclk_rate %d\n",
+			__func__, pdata->mclk_rate);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (pdata->dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		pr_info("%s: dmic_rate invalid default = %d\n",
+			__func__, def_dmic_rate);
+		pdata->dmic_sample_rate = def_dmic_rate;
+	}
+
+	if (pdata->mad_dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		pr_info("%s: mad_dmic_rate invalid default = %d\n",
+			__func__, def_dmic_rate);
+		/*
+		 * use dmic_sample_rate as the default for MAD
+		 * if mad dmic sample rate is undefined
+		 */
+		pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
+	}
+
+	/*
+	 * Default the DMIC clk rates to mad_dmic_sample_rate,
+	 * whereas, the anc/txfe dmic rates to dmic_sample_rate
+	 * since the anc/txfe are independent of mad block.
+	 */
+	mad_dmic_ctl_val = tomtom_get_dmic_clk_val(tomtom->codec,
+				pdata->mclk_rate,
+				pdata->mad_dmic_sample_rate);
+	snd_soc_update_bits(codec, TOMTOM_A_DMIC_B1_CTL,
+		0xE0, mad_dmic_ctl_val << 5);
+	snd_soc_update_bits(codec, TOMTOM_A_DMIC_B2_CTL,
+		0x70, mad_dmic_ctl_val << 4);
+	snd_soc_update_bits(codec, TOMTOM_A_DMIC_B2_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+
+	dmic_ctl_val = tomtom_get_dmic_clk_val(tomtom->codec,
+				pdata->mclk_rate,
+				pdata->dmic_sample_rate);
+
+	if (dmic_ctl_val == WCD9330_DMIC_CLK_DIV_2)
+		anc_ctl_value = WCD9XXX_ANC_DMIC_X2_ON;
+	else
+		anc_ctl_value = WCD9XXX_ANC_DMIC_X2_OFF;
+
+	for (dec = 0; dec < NUM_DECIMATORS; dec++) {
+		tx_dmic_ctl_reg =
+			TOMTOM_A_CDC_TX1_DMIC_CTL + (8 * dec);
+		snd_soc_update_bits(codec, tx_dmic_ctl_reg,
+				    0x07, dmic_ctl_val);
+	}
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC1_B2_CTL,
+		0x1, anc_ctl_value);
+	snd_soc_update_bits(codec, TOMTOM_A_CDC_ANC2_B2_CTL,
+		0x1, anc_ctl_value);
+done:
+	return rc;
+}
+
+static const struct wcd9xxx_reg_mask_val tomtom_reg_defaults[] = {
+
+	/* set MCLk to 9.6 */
+	TOMTOM_REG_VAL(TOMTOM_A_CHIP_CTL, 0x02),
+
+	/* EAR PA deafults  */
+	TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_CMBUFF, 0x05),
+
+	/* RX deafults */
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B5_CTL, 0x79),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B5_CTL, 0x79),
+
+	/* RX1 and RX2 defaults */
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B6_CTL, 0xA0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B6_CTL, 0xA0),
+
+	/* RX3 to RX7 defaults */
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B6_CTL, 0x80),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B6_CTL, 0x80),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B6_CTL, 0x80),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B6_CTL, 0x80),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B6_CTL, 0x80),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B6_CTL, 0x80),
+
+	/* MAD registers */
+	TOMTOM_REG_VAL(TOMTOM_A_MAD_ANA_CTRL, 0xF1),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_1, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_2, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_1, 0x00),
+	/* Set SAMPLE_TX_EN bit */
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_2, 0x03),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_3, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_4, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_5, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_6, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_7, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_CTL_8, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_PTR, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_AUDIO_IIR_CTL_VAL, 0x40),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_DEBUG_B7_CTL, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_CLK_OTHR_RESET_B1_CTL, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_CLK_OTHR_CTL, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_INP_SEL, 0x01),
+
+	/* Set HPH Path to low power mode */
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_BIAS_PA, 0x57),
+
+	/* BUCK default */
+	TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_4, 0x51),
+	TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_1, 0x5B),
+};
+
+/*
+ * Don't update TOMTOM_A_CHIP_CTL, TOMTOM_A_BUCK_CTRL_CCL_1 and
+ * TOMTOM_A_RX_EAR_CMBUFF as those are updated in tomtom_reg_defaults
+ */
+static const struct wcd9xxx_reg_mask_val tomtom_1_0_reg_defaults[] = {
+	TOMTOM_REG_VAL(TOMTOM_A_TX_1_GAIN, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_2_GAIN, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_1_2_ADC_IB, 0x44),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_3_GAIN, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_4_GAIN, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_3_4_ADC_IB, 0x44),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_5_GAIN, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_6_GAIN, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_TX_5_6_ADC_IB, 0x44),
+	TOMTOM_REG_VAL(WCD9XXX_A_BUCK_MODE_3, 0xCE),
+	TOMTOM_REG_VAL(WCD9XXX_A_BUCK_CTRL_VCL_1, 0x8),
+	TOMTOM_REG_VAL(TOMTOM_A_BUCK_CTRL_CCL_4, 0x51),
+	TOMTOM_REG_VAL(TOMTOM_A_NCP_DTEST, 0x10),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CHOP_CTL, 0xA4),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_OCP_CTL, 0x69),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xDA),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_CNP_WG_TIME, 0x15),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_BIAS_PA, 0x76),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_EAR_CNP, 0xC0),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_BIAS_PA, 0x78),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_1_TEST, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_2_TEST, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_3_TEST, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_LINE_4_TEST, 0x2),
+	TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_OCP_CTL, 0x97),
+	TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_CLIP_DET, 0x1),
+	TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV1_IEC, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV2_OCP_CTL, 0x97),
+	TOMTOM_REG_VAL(TOMTOM_A_SPKR_DRV2_CLIP_DET, 0x1),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX1_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX2_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX3_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX4_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX5_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX6_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX7_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX8_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX9_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX10_MUX_CTL, 0x4A),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX1_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX2_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX3_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX4_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX5_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX6_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX7_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX8_B4_CTL, 0xB),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_VBAT_GAIN_UPD_MON, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B1_CTL, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B2_CTL, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B3_CTL, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_PA_RAMP_B4_CTL, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_SPKR_CLIPDET_B1_CTL, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_SPKR2_CLIPDET_B1_CTL, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B4_CTL, 0x37),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B5_CTL, 0x7f),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_COMP0_B5_CTL, 0x7f),
+};
+
+static const struct wcd9xxx_reg_mask_val tomtom_2_0_reg_defaults[] = {
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_MAD_MAIN_CTL_2, 0x32),
+	TOMTOM_REG_VAL(TOMTOM_A_RCO_CTRL, 0x10),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_L_TEST, 0x0A),
+	TOMTOM_REG_VAL(TOMTOM_A_RX_HPH_R_TEST, 0x0A),
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE0, 0xC3),
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_DATA0, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_SCK_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_WS_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_SCK_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_WS_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE1, 0xE0),
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE2, 0x03),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_JTCK_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_JTDI_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_JTMS_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_JTDO_MODE, 0x04),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_JTRST_MODE, 0x04),
+};
+
+static const struct wcd9xxx_reg_mask_val tomtom_2_0_reg_i2c_defaults[] = {
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE0, 0x00),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_SCK_MODE, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_TX_I2S_WS_MODE, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_SCK_MODE, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_CDC_RX_I2S_WS_MODE, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE1, 0x0),
+	TOMTOM_REG_VAL(TOMTOM_A_PIN_CTL_OE2, 0x0),
+};
+
+static void tomtom_update_reg_defaults(struct snd_soc_codec *codec)
+{
+	u32 i;
+	struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent);
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	for (i = 0; i < ARRAY_SIZE(tomtom_reg_defaults); i++)
+		snd_soc_write(codec, tomtom_reg_defaults[i].reg,
+			      tomtom_reg_defaults[i].val);
+
+	for (i = 0; i < ARRAY_SIZE(tomtom_1_0_reg_defaults); i++)
+		snd_soc_write(codec, tomtom_1_0_reg_defaults[i].reg,
+				tomtom_1_0_reg_defaults[i].val);
+
+	if (!TOMTOM_IS_1_0(tomtom_core->version)) {
+		for (i = 0; i < ARRAY_SIZE(tomtom_2_0_reg_defaults); i++)
+			snd_soc_write(codec, tomtom_2_0_reg_defaults[i].reg,
+				      tomtom_2_0_reg_defaults[i].val);
+
+		if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			for (i = 0; i < ARRAY_SIZE(tomtom_2_0_reg_i2c_defaults);
+			     i++)
+				snd_soc_write(codec,
+					tomtom_2_0_reg_i2c_defaults[i].reg,
+					tomtom_2_0_reg_i2c_defaults[i].val);
+		}
+	}
+}
+
+static const struct wcd9xxx_reg_mask_val tomtom_codec_reg_init_val[] = {
+	/* Initialize current threshold to 350MA
+	 * number of wait and run cycles to 4096
+	 */
+	{TOMTOM_A_RX_HPH_OCP_CTL, 0xE1, 0x61},
+	{TOMTOM_A_RX_COM_OCP_COUNT, 0xFF, 0xFF},
+	{TOMTOM_A_RX_HPH_L_TEST, 0x01, 0x01},
+	{TOMTOM_A_RX_HPH_R_TEST, 0x01, 0x01},
+
+	/* Initialize gain registers to use register gain */
+	{TOMTOM_A_RX_HPH_L_GAIN, 0x20, 0x20},
+	{TOMTOM_A_RX_HPH_R_GAIN, 0x20, 0x20},
+	{TOMTOM_A_RX_LINE_1_GAIN, 0x20, 0x20},
+	{TOMTOM_A_RX_LINE_2_GAIN, 0x20, 0x20},
+	{TOMTOM_A_RX_LINE_3_GAIN, 0x20, 0x20},
+	{TOMTOM_A_RX_LINE_4_GAIN, 0x20, 0x20},
+	{TOMTOM_A_SPKR_DRV1_GAIN, 0x04, 0x04},
+	{TOMTOM_A_SPKR_DRV2_GAIN, 0x04, 0x04},
+
+	/* Use 16 bit sample size for TX1 to TX6 */
+	{TOMTOM_A_CDC_CONN_TX_SB_B1_CTL, 0x30, 0x20},
+	{TOMTOM_A_CDC_CONN_TX_SB_B2_CTL, 0x30, 0x20},
+	{TOMTOM_A_CDC_CONN_TX_SB_B3_CTL, 0x30, 0x20},
+	{TOMTOM_A_CDC_CONN_TX_SB_B4_CTL, 0x30, 0x20},
+	{TOMTOM_A_CDC_CONN_TX_SB_B5_CTL, 0x30, 0x20},
+	{TOMTOM_A_CDC_CONN_TX_SB_B6_CTL, 0x30, 0x20},
+
+	/* Use 16 bit sample size for TX7 to TX10 */
+	{TOMTOM_A_CDC_CONN_TX_SB_B7_CTL, 0x60, 0x40},
+	{TOMTOM_A_CDC_CONN_TX_SB_B8_CTL, 0x60, 0x40},
+	{TOMTOM_A_CDC_CONN_TX_SB_B9_CTL, 0x60, 0x40},
+	{TOMTOM_A_CDC_CONN_TX_SB_B10_CTL, 0x60, 0x40},
+
+	/*enable HPF filter for TX paths */
+	{TOMTOM_A_CDC_TX1_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX2_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX3_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX4_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX5_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX6_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX7_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX8_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX9_MUX_CTL, 0x8, 0x0},
+	{TOMTOM_A_CDC_TX10_MUX_CTL, 0x8, 0x0},
+
+	/* Compander zone selection */
+	{TOMTOM_A_CDC_COMP0_B4_CTL, 0x3F, 0x37},
+	{TOMTOM_A_CDC_COMP1_B4_CTL, 0x3F, 0x37},
+	{TOMTOM_A_CDC_COMP2_B4_CTL, 0x3F, 0x37},
+	{TOMTOM_A_CDC_COMP0_B5_CTL, 0x7F, 0x7F},
+	{TOMTOM_A_CDC_COMP1_B5_CTL, 0x7F, 0x7F},
+	{TOMTOM_A_CDC_COMP2_B5_CTL, 0x7F, 0x7F},
+
+	/*
+	 * Setup wavegen timer to 20msec and disable chopper
+	 * as default. This corresponds to Compander OFF
+	 */
+	{TOMTOM_A_RX_HPH_CNP_WG_CTL, 0xFF, 0xDB},
+	{TOMTOM_A_RX_HPH_CNP_WG_TIME, 0xFF, 0x58},
+	{TOMTOM_A_RX_HPH_BIAS_WG_OCP, 0xFF, 0x1A},
+	{TOMTOM_A_RX_HPH_CHOP_CTL, 0xFF, 0x24},
+
+	/* Choose max non-overlap time for NCP */
+	{TOMTOM_A_NCP_CLK, 0xFF, 0xFC},
+
+	/* Program the 0.85 volt VBG_REFERENCE */
+	{TOMTOM_A_BIAS_CURR_CTL_2, 0xFF, 0x04},
+
+	/* set MAD input MIC to DMIC1 */
+	{TOMTOM_A_CDC_MAD_INP_SEL, 0x0F, 0x08},
+
+	{TOMTOM_A_INTR_MODE, 0x04, 0x04},
+};
+
+static const struct wcd9xxx_reg_mask_val tomtom_codec_2_0_reg_init_val[] = {
+	{TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x00},
+	{TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x00},
+	{TOMTOM_A_CDC_CLIP_ADJ_SPKR_MIN_CLIP_THRESHOLD, 0xFF, 0x00},
+	{TOMTOM_A_CDC_CLIP_ADJ_SPKR2_MIN_CLIP_THRESHOLD, 0xFF, 0x00},
+	{TOMTOM_A_CDC_CLIP_ADJ_SPKR_BOOST_GATING, 0x01, 0x01},
+	{TOMTOM_A_CDC_CLIP_ADJ_SPKR2_BOOST_GATING, 0x01, 0x01},
+	{TOMTOM_A_CDC_CLIP_ADJ_SPKR_B1_CTL, 0x01, 0x00},
+	{TOMTOM_A_CDC_CLIP_ADJ_SPKR2_B1_CTL, 0x01, 0x00},
+};
+
+static void tomtom_codec_init_reg(struct snd_soc_codec *codec)
+{
+	u32 i;
+	struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent);
+
+	for (i = 0; i < ARRAY_SIZE(tomtom_codec_reg_init_val); i++)
+		snd_soc_update_bits(codec, tomtom_codec_reg_init_val[i].reg,
+				tomtom_codec_reg_init_val[i].mask,
+				tomtom_codec_reg_init_val[i].val);
+
+	if (!TOMTOM_IS_1_0(tomtom_core->version)) {
+		for (i = 0; i < ARRAY_SIZE(tomtom_codec_2_0_reg_init_val); i++)
+			snd_soc_update_bits(codec,
+				tomtom_codec_2_0_reg_init_val[i].reg,
+				tomtom_codec_2_0_reg_init_val[i].mask,
+				tomtom_codec_2_0_reg_init_val[i].val);
+	}
+
+}
+
+static void tomtom_slim_interface_init_reg(struct snd_soc_codec *codec)
+{
+	int i;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
+		wcd9xxx_interface_reg_write(wcd9xxx,
+					    TOMTOM_SLIM_PGD_PORT_INT_EN0 + i,
+					    0xFF);
+}
+
+static int tomtom_setup_irqs(struct tomtom_priv *tomtom)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = tomtom->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
+				  tomtom_slimbus_irq, "SLIMBUS Slave", tomtom);
+	if (ret)
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_SLIMBUS);
+	else
+		tomtom_slim_interface_init_reg(codec);
+
+	return ret;
+}
+
+static void tomtom_cleanup_irqs(struct tomtom_priv *tomtom)
+{
+	struct snd_soc_codec *codec = tomtom->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tomtom);
+}
+
+static
+struct firmware_cal *tomtom_get_hwdep_fw_cal(struct snd_soc_codec *codec,
+			enum wcd_cal_type type)
+{
+	struct tomtom_priv *tomtom;
+	struct firmware_cal *hwdep_cal;
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer\n", __func__);
+		return NULL;
+	}
+	tomtom = snd_soc_codec_get_drvdata(codec);
+	hwdep_cal = wcdcal_get_fw_cal(tomtom->fw_data, type);
+	if (!hwdep_cal) {
+		dev_err(codec->dev, "%s: cal not sent by %d\n",
+				__func__, type);
+		return NULL;
+	} else {
+		return hwdep_cal;
+	}
+}
+
+int tomtom_hs_detect(struct snd_soc_codec *codec,
+		    struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	int rc;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	if (mbhc_cfg->insert_detect) {
+		rc = wcd9xxx_mbhc_start(&tomtom->mbhc, mbhc_cfg);
+		if (!rc)
+			tomtom->mbhc_started = true;
+	} else {
+		/* MBHC is disabled, so disable Auto pulldown */
+		snd_soc_update_bits(codec, TOMTOM_A_MBHC_INSERT_DETECT2, 0xC0,
+				    0x00);
+		snd_soc_update_bits(codec, TOMTOM_A_MICB_CFILT_2_CTL, 0x01,
+				    0x00);
+		tomtom->mbhc.mbhc_cfg = NULL;
+		rc = 0;
+	}
+	return rc;
+}
+EXPORT_SYMBOL(tomtom_hs_detect);
+
+void tomtom_hs_detect_exit(struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	wcd9xxx_mbhc_stop(&tomtom->mbhc);
+	tomtom->mbhc_started = false;
+}
+EXPORT_SYMBOL(tomtom_hs_detect_exit);
+
+void tomtom_event_register(
+	int (*machine_event_cb)(struct snd_soc_codec *codec,
+				enum wcd9xxx_codec_event),
+	struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	tomtom->machine_codec_event_cb = machine_event_cb;
+}
+EXPORT_SYMBOL(tomtom_event_register);
+
+void tomtom_register_ext_clk_cb(
+	int (*codec_ext_clk_en)(struct snd_soc_codec *codec,
+				int enable, bool dapm),
+	int (*get_ext_clk_cnt)(void),
+	struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+	tomtom->codec_ext_clk_en_cb =  codec_ext_clk_en;
+	tomtom->codec_get_ext_clk_cnt = get_ext_clk_cnt;
+}
+EXPORT_SYMBOL(tomtom_register_ext_clk_cb);
+
+static void tomtom_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct afe_param_cdc_slimbus_slave_cfg *cfg;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	uint64_t eaddr = 0;
+
+	cfg = &priv->slimbus_slave_cfg;
+	cfg->minor_version = 1;
+	cfg->tx_slave_port_offset = 0;
+	cfg->rx_slave_port_offset = 16;
+
+	memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+	WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+	cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+	cfg->device_enum_addr_msw = eaddr >> 32;
+
+	pr_debug("%s: slimbus logical address 0x%llx\n", __func__, eaddr);
+}
+
+static int tomtom_device_down(struct wcd9xxx *wcd9xxx)
+{
+	int count;
+	struct snd_soc_codec *codec;
+	struct tomtom_priv *priv;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	priv = snd_soc_codec_get_drvdata(codec);
+	wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT);
+	snd_soc_card_change_online_state(codec->component.card, 0);
+	set_bit(BUS_DOWN, &priv->status_mask);
+
+	for (count = 0; count < NUM_CODEC_DAIS; count++)
+		priv->dai[count].bus_down_in_recovery = true;
+	return 0;
+}
+
+static int wcd9xxx_prepare_static_pa(struct wcd9xxx_mbhc *mbhc,
+				     struct list_head *lh)
+{
+	int i;
+	struct snd_soc_codec *codec = mbhc->codec;
+	u32 delay;
+
+	const struct wcd9xxx_reg_mask_val reg_set_paon[] = {
+		{TOMTOM_A_TX_COM_BIAS, 0xff, 0xF0},
+		{WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x81},
+		{WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x01, 0x01},
+		{WCD9XXX_A_BUCK_MODE_2, 0xff, 0xEF},
+		{WCD9XXX_A_BUCK_MODE_2, 0xff, 0xEE},
+		{TOMTOM_A_NCP_DTEST, 0xff, 0x20},
+		{WCD9XXX_A_CDC_CLK_OTHR_CTL, 0xff, 0x21},
+		{WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x81},
+		{WCD9XXX_A_CDC_CLK_RX_B1_CTL, 0x02, 0x02},
+
+		{WCD9XXX_A_BUCK_MODE_2, 0xff, 0xAE},
+		{WCD9XXX_A_BUCK_MODE_2, 0xff, 0xAA},
+		{WCD9XXX_A_NCP_CLK, 0xff, 0x9C},
+		{WCD9XXX_A_NCP_CLK, 0xff, 0xFC},
+		{WCD9XXX_A_RX_COM_BIAS, 0xff, 0xA0},
+		{WCD9XXX_A_BUCK_MODE_3, 0xff, 0xC6},
+		{WCD9XXX_A_BUCK_MODE_4, 0xff, 0xE6},
+		{WCD9XXX_A_BUCK_MODE_5, 0xff, 0x02},
+		{WCD9XXX_A_BUCK_MODE_1, 0xff, 0xA1},
+		/* Add a delay of 1ms after this reg write */
+
+		{WCD9XXX_A_NCP_STATIC, 0xff, 0x28},
+		{WCD9XXX_A_NCP_EN, 0xff, 0xFF},
+		/* Add a delay of 1ms after this reg write */
+
+		/* set HPHL */
+		{WCD9XXX_A_RX_HPH_L_TEST, 0xff, 0x00},
+		{TOMTOM_A_RX_HPH_L_PA_CTL, 0xff, 0x42},
+		{TOMTOM_A_RX_HPH_BIAS_LDO, 0xff, 0x8C},
+		{TOMTOM_A_RX_HPH_CHOP_CTL, 0xff, 0xA4},
+		{WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0xE0},
+		{WCD9XXX_A_RX_HPH_L_GAIN, 0xff, 0xEC},
+
+		/* set HPHR */
+		{WCD9XXX_A_RX_HPH_R_TEST, 0xff, 0x00},
+		{TOMTOM_A_RX_HPH_R_PA_CTL, 0xff, 0x42},
+		{WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x20},
+		{WCD9XXX_A_RX_HPH_R_GAIN, 0xff, 0x2C},
+
+		/* set HPH PAs */
+		{WCD9XXX_A_RX_HPH_BIAS_WG_OCP, 0xff, 0x2A},
+		{WCD9XXX_A_RX_HPH_CNP_WG_CTL, 0xff, 0xDA},
+		{WCD9XXX_A_RX_HPH_CNP_WG_TIME, 0xff, 0x15},
+		{WCD9XXX_A_CDC_CLSH_B1_CTL, 0xff, 0xE6},
+		{WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0x40},
+		{WCD9XXX_A_RX_HPH_L_DAC_CTL, 0xff, 0xC0},
+		{WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0x40},
+		{WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xff, 0xC0},
+
+		{TOMTOM_A_RX_HPH_L_ATEST, 0xff, 0x00},
+		{TOMTOM_A_RX_HPH_R_ATEST, 0xff, 0x00},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(reg_set_paon); i++) {
+		/*
+		 * Some of the codec registers like BUCK_MODE_1
+		 * and NCP_EN requires 1ms wait time for them
+		 * to take effect. Other register writes for
+		 * PA configuration do not require any wait time.
+		 */
+		if (reg_set_paon[i].reg == WCD9XXX_A_BUCK_MODE_1 ||
+		    reg_set_paon[i].reg == WCD9XXX_A_NCP_EN)
+			delay = 1000;
+		else
+			delay = 0;
+		wcd9xxx_soc_update_bits_push(codec, lh,
+					     reg_set_paon[i].reg,
+					     reg_set_paon[i].mask,
+					     reg_set_paon[i].val, delay);
+	}
+	pr_debug("%s: PAs are prepared\n", __func__);
+
+	return 0;
+}
+
+static int wcd9xxx_enable_static_pa(struct wcd9xxx_mbhc *mbhc, bool enable,
+				    u8 hph_pa)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const int wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME) *
+			    TOMTOM_WG_TIME_FACTOR_US;
+	u8 mask = (hph_pa << 4);
+	u8 pa_en = enable ? mask : ~mask;
+
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, mask, pa_en);
+	/* Wait for wave gen time to avoid pop noise */
+	usleep_range(wg_time, wg_time + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+	pr_debug("%s: PAs are %s as static mode (wg_time %d)\n", __func__,
+		 enable ? "enabled" : "disabled", wg_time);
+	return 0;
+}
+
+static int tomtom_setup_zdet(struct wcd9xxx_mbhc *mbhc,
+			    enum mbhc_impedance_detect_stages stage)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+
+#define __wr(reg, mask, value)						  \
+	do {								  \
+		ret = wcd9xxx_soc_update_bits_push(codec,		  \
+						   &tomtom->reg_save_restore, \
+						   reg, mask, value, 0);  \
+		if (ret < 0)						  \
+			return ret;					  \
+	} while (0)
+
+	switch (stage) {
+
+	case MBHC_ZDET_PRE_MEASURE:
+		INIT_LIST_HEAD(&tomtom->reg_save_restore);
+		wcd9xxx_prepare_static_pa(mbhc, &tomtom->reg_save_restore);
+		/* Set HPH_MBHC for zdet */
+		__wr(WCD9XXX_A_MBHC_HPH, 0xff, 0xC4);
+		usleep_range(10, 10 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		wcd9xxx_enable_static_pa(mbhc, HPH_PA_ENABLE, HPH_PA_L_R);
+
+		/* save old value of registers and write the new value */
+		__wr(WCD9XXX_A_RX_HPH_OCP_CTL, 0xff, 0x69);
+		__wr(WCD9XXX_A_CDC_RX1_B6_CTL, 0xff, 0x80);
+		__wr(WCD9XXX_A_CDC_RX2_B6_CTL, 0xff, 0x80);
+		/* Enable MBHC MUX, Set MUX current to 37.5uA and ADC7 */
+		__wr(WCD9XXX_A_MBHC_SCALING_MUX_1, 0xff, 0xC0);
+		__wr(WCD9XXX_A_MBHC_SCALING_MUX_2, 0xff, 0xF0);
+		__wr(TOMTOM_A_TX_7_TXFE_CLKDIV, 0xff, 0x8B);
+		__wr(WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0xff, 0x78);
+		__wr(WCD9XXX_A_TX_7_MBHC_EN, 0xff, 0x8C);
+		__wr(WCD9XXX_A_CDC_MBHC_B1_CTL, 0xff, 0xDC);
+		/* Reset MBHC and set it up for STA */
+		__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xff, 0x0A);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x00);
+		__wr(WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xff, 0x02);
+		__wr(WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL, 0xff, 0x80);
+		__wr(WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0xff, 0x25);
+		/* Wait for ~50us to let MBHC hardware settle down */
+		usleep_range(50, 50 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		break;
+	case MBHC_ZDET_POST_MEASURE:
+		/* 0x69 for 105 number of samples for PA RAMP */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+		/* Program the PA Ramp to FS_16K, L shift 1 */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+			      0x1 << 4 | 0x6);
+		/* Reset the PA Ramp */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1C);
+		/*
+		 * Connect the PA Ramp to PA chain and release reset with
+		 * keep it connected.
+		 */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x1F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+
+		/* Start the PA ramp on HPH L and R */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x05);
+		/* Ramp generator takes ~30ms */
+		usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+			     TOMTOM_HPH_PA_RAMP_DELAY +
+			     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+		/*
+		 * Set the multiplication factor for zdet calculation
+		 * based on the Ramp voltage and Gain used
+		 */
+		tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_1X;
+		break;
+	case MBHC_ZDET_GAIN_0:
+		/* Set Gain at 1x */
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x00);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x00);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x42);
+		/* Allow 100us for gain registers to settle */
+		usleep_range(100,
+			     100 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		break;
+	case MBHC_ZDET_GAIN_UPDATE_1X:
+		/*
+		 * Set the multiplication factor for zdet calculation
+		 * based on the Gain value used
+		 */
+		tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_1X;
+		break;
+	case MBHC_ZDET_GAIN_1:
+		/* Set Gain at 10x */
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x10);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x00);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x42);
+		/* Allow 100us for gain registers to settle */
+		usleep_range(100,
+			     100 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+		/*
+		 * Set the multiplication factor for zdet calculation
+		 * based on the Gain value used
+		 */
+		tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_10X;
+		break;
+	case MBHC_ZDET_GAIN_2:
+		/* Set Gain at 100x */
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_L_ATEST, 0x00);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_R_ATEST, 0x10);
+		snd_soc_write(codec, TOMTOM_A_RX_HPH_L_PA_CTL, 0x43);
+		/* Allow 100us for gain registers to settle */
+		usleep_range(100,
+			     100 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+		/*
+		 * Set the multiplication factor for zdet calculation
+		 * based on the Gain value used
+		 */
+		tomtom->zdet_gain_mul_fact = TOMTOM_ZDET_MUL_FACTOR_100X;
+		break;
+	case MBHC_ZDET_RAMP_DISABLE:
+		/* Ramp HPH L & R back to Zero */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+		/* 0x69 for 105 number of samples for PA RAMP */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+		/* Program the PA Ramp to FS_16K, L shift 1 */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+			      0x1 << 4 | 0x6);
+		/* Reset the PA Ramp */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17);
+		/*
+		 * Connect the PA Ramp to PA chain and release reset with
+		 * keep it connected.
+		 */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+		/* Start the PA ramp on HPH L and R */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x0A);
+		/* Ramp generator takes ~30ms to settle down */
+		usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+			     TOMTOM_HPH_PA_RAMP_DELAY +
+			     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		break;
+	case MBHC_ZDET_HPHR_RAMP_DISABLE:
+		/* Ramp HPHR back to Zero */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+			      0x1 << 4 | 0x6);
+		/* Reset the PA Ramp */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17);
+		/*
+		 * Connect the PA Ramp to PA chain and release reset with
+		 * keep it connected.
+		 */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x08);
+		/* Ramp generator takes ~30ms to settle down */
+		usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+			     TOMTOM_HPH_PA_RAMP_DELAY +
+			     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		break;
+	case MBHC_ZDET_HPHL_RAMP_DISABLE:
+		/* Ramp back to Zero */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x69);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL,
+			      0x1 << 4 | 0x6);
+		/* Reset the PA Ramp */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x17);
+		/*
+		 * Connect the PA Ramp to PA chain and release reset with
+		 * keep it connected.
+		 */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x03);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x02);
+		/* Ramp generator takes ~30ms to settle down */
+		usleep_range(TOMTOM_HPH_PA_RAMP_DELAY,
+			     TOMTOM_HPH_PA_RAMP_DELAY +
+			     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		break;
+	case MBHC_ZDET_HPHR_PA_DISABLE:
+		/* Disable PA */
+		wcd9xxx_enable_static_pa(mbhc, HPH_PA_DISABLE, HPH_PA_R);
+		break;
+	case MBHC_ZDET_PA_DISABLE:
+		/* Disable PA */
+		if (!mbhc->hph_pa_dac_state &&
+			(!(test_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state) ||
+			test_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state))))
+			wcd9xxx_enable_static_pa(mbhc, HPH_PA_DISABLE,
+						 HPH_PA_L_R);
+		else if (!(snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN) & 0x10))
+			wcd9xxx_enable_static_pa(mbhc, HPH_PA_ENABLE, HPH_PA_R);
+
+		/* Turn off PA ramp generator */
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B1_CTL, 0x00);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B2_CTL, 0x00);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B3_CTL, 0x00);
+		snd_soc_write(codec, WCD9XXX_A_CDC_PA_RAMP_B4_CTL, 0x00);
+
+		/* Restore registers */
+		wcd9xxx_restore_registers(codec, &tomtom->reg_save_restore);
+		break;
+	}
+#undef __wr
+
+	return ret;
+}
+
+/* Calculate final impedance values for HPH left and right based on formulae */
+static void tomtom_compute_impedance(struct wcd9xxx_mbhc *mbhc, s16 *l, s16 *r,
+				     uint32_t *zl, uint32_t *zr)
+{
+	s64 zln, zrn;
+	int zld, zrd;
+	s64 rl = 0, rr = 0;
+	struct snd_soc_codec *codec;
+	struct tomtom_priv *tomtom;
+
+	if (!mbhc) {
+		pr_err("%s: Invalid parameters mbhc = %pK\n",
+			__func__,  mbhc);
+		return;
+	}
+	codec = mbhc->codec;
+	tomtom = snd_soc_codec_get_drvdata(codec);
+
+	if (l && zl) {
+		zln = (s64) (l[1] - l[0]) * tomtom->zdet_gain_mul_fact;
+		zld = (l[2] - l[0]);
+		if (zld)
+			rl = div_s64(zln, zld);
+		else
+			/* If L0 and L2 are same, Z has to be on Zone 3.
+			 * Assign a default value so that atleast the value
+			 * is read again with Ramp-up
+			 */
+			rl = TOMTOM_ZDET_ZONE_3_DEFAULT_VAL;
+
+		/* 32-bit LSBs are enough to hold Impedance values */
+		*zl = (u32) rl;
+	}
+	if (r && zr) {
+		zrn = (s64) (r[1] - r[0]) * tomtom->zdet_gain_mul_fact;
+		zrd = (r[2] - r[0]);
+		if (zrd)
+			rr = div_s64(zrn, zrd);
+		else
+			/* If R0 and R2 are same, Z has to be on Zone 3.
+			 * Assign a default value so that atleast the value
+			 * is read again with Ramp-up
+			 */
+			rr = TOMTOM_ZDET_ZONE_3_DEFAULT_VAL;
+
+		/* 32-bit LSBs are enough to hold Impedance values */
+		*zr = (u32) rr;
+	}
+}
+
+/*
+ * Calculate error approximation of impedance values for HPH left
+ * and HPH right based on QFuse values
+ */
+static void tomtom_zdet_error_approx(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+				     uint32_t *zr)
+{
+	struct snd_soc_codec *codec;
+	struct tomtom_priv *tomtom;
+	s8 q1_t, q2_t;
+	s8 q1_m, q2_m;
+	s8 q1, q2;
+	u8 div_shift;
+	int rl_alpha = 0, rr_alpha = 0;
+	int rl_beta = 0, rr_beta = 0;
+	u64 rl = 0, rr = 0;
+	const int mult_factor = TOMTOM_ZDET_ERROR_APPROX_MUL_FACTOR;
+	const int shift = TOMTOM_ZDET_ERROR_APPROX_SHIFT;
+
+	if (!zl || !zr || !mbhc) {
+		pr_err("%s: Invalid parameters zl = %pK zr = %pK, mbhc = %pK\n",
+			__func__, zl, zr, mbhc);
+		return;
+	}
+	codec = mbhc->codec;
+	tomtom = snd_soc_codec_get_drvdata(codec);
+
+	if ((tomtom->zdet_gain_mul_fact == TOMTOM_ZDET_MUL_FACTOR_1X) ||
+	    (tomtom->zdet_gain_mul_fact == TOMTOM_ZDET_MUL_FACTOR_10X)) {
+		q1_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT0) &
+			0x3) << 0x5);
+		q1_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT1) &
+			0xF8) >> 0x3);
+		q2_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT1) &
+			0x7) << 0x4);
+		q2_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT2) &
+			0xF0) >> 0x4);
+		/* Take out the numeric part of the Qfuse value */
+		q1_m = q1_t & 0x3F;
+		q2_m = q2_t & 0x3F;
+		/* Check the sign part of the Qfuse and adjust value */
+		q1 = (q1_t & 0x40) ? -q1_m : q1_m;
+		q2 = (q2_t & 0x40) ? -q2_m : q2_m;
+		div_shift = 1;
+	} else {
+		q1_t = ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT2) &
+			0xF) << 0x2);
+		q1_t |= ((snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT3) &
+			0xC0) >> 0x6);
+		q2_t = (snd_soc_read(codec, TOMTOM_A_QFUSE_DATA_OUT3) & 0x3F);
+		/* Take out the numeric part of the Qfuse value */
+		q1_m = q1_t & 0x1F;
+		q2_m = q2_t & 0x1F;
+		/* Check the sign part of the Qfuse and adjust value */
+		q1 = (q1_t & 0x20) ? -q1_m : q1_m;
+		q2 = (q2_t & 0x20) ? -q2_m : q2_m;
+		div_shift = 0;
+	}
+
+	dev_dbg(codec->dev, "%s: qfuse1 = %d, qfuse2 = %d\n",
+		__func__, q1, q2);
+	if (!q1 && !q2) {
+		dev_dbg(codec->dev, "%s: qfuse1 and qfuse2 are 0. Exiting\n",
+			__func__);
+		return;
+	}
+
+	/*
+	 * Use multiplication and shift to avoid floating point math
+	 * The Z value is calculated with the below formulae using
+	 * the Qfuse value-
+	 * zl = zl * [1 - {(Q1 / div) / 100}] (Include sign for Q1)
+	 * zr = zr * [1 - {(Q2 / div) / 100}] (Include sign for Q2)
+	 * We multiply by 65536 and shift 16 times to get the approx result
+	 * div = 4 for 1x gain, div = 2 for 10x/100x gain
+	 */
+	/* Q1/4 */
+	rl_alpha = q1 >> div_shift;
+	rl_alpha = 100 - rl_alpha;
+	/* {rl_alpha/100} * 65536 */
+	rl_beta = rl_alpha * mult_factor;
+	rl = (u64) *zl * rl_beta;
+	/* rl/65536 */
+	rl = (u64) rl >> shift;
+
+	rr_alpha = q2 >> div_shift;
+	rr_alpha = 100 - rr_alpha;
+	rr_beta = rr_alpha * mult_factor;
+	rr = (u64) *zr * rr_beta;
+	rr = (u64) rr >> shift;
+
+	dev_dbg(codec->dev, "%s: rl = 0x%llx (%lld) \t rr = 0x%llx (%lld)\n",
+		__func__, rl, rl, rr, rr);
+
+	*zl = (u32) rl;
+	*zr = (u32) rr;
+}
+
+static enum wcd9xxx_cdc_type tomtom_get_cdc_type(void)
+{
+	return WCD9XXX_CDC_TYPE_TOMTOM;
+}
+
+static bool tomtom_mbhc_ins_rem_status(struct snd_soc_codec *codec)
+{
+	return !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+			    (1 << 4));
+}
+
+static void tomtom_mbhc_micb_pulldown_ctrl(struct wcd9xxx_mbhc *mbhc,
+					   bool enable)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	if (!enable) {
+		/* Remove automatic pulldown on micbias */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+				    0x01, 0x00);
+	} else {
+		/* Enable automatic pulldown on micbias */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+				    0x01, 0x01);
+	}
+}
+
+static void tomtom_codec_hph_auto_pull_down(struct snd_soc_codec *codec,
+					    bool enable)
+{
+	struct wcd9xxx *tomtom_core = dev_get_drvdata(codec->dev->parent);
+
+	if (TOMTOM_IS_1_0(tomtom_core->version))
+		return;
+
+	dev_dbg(codec->dev, "%s: %s auto pull down\n", __func__,
+			enable ? "enable" : "disable");
+	if (enable) {
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x08);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x08);
+	} else {
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_L_TEST, 0x08, 0x00);
+		snd_soc_update_bits(codec, TOMTOM_A_RX_HPH_R_TEST, 0x08, 0x00);
+	}
+}
+
+static const struct wcd9xxx_mbhc_cb mbhc_cb = {
+	.get_cdc_type = tomtom_get_cdc_type,
+	.setup_zdet = tomtom_setup_zdet,
+	.compute_impedance = tomtom_compute_impedance,
+	.zdet_error_approx = tomtom_zdet_error_approx,
+	.insert_rem_status = tomtom_mbhc_ins_rem_status,
+	.micbias_pulldown_ctrl = tomtom_mbhc_micb_pulldown_ctrl,
+	.codec_rco_ctrl = tomtom_codec_internal_rco_ctrl,
+	.hph_auto_pulldown_ctrl = tomtom_codec_hph_auto_pull_down,
+	.get_hwdep_fw_cal = tomtom_get_hwdep_fw_cal,
+};
+
+static const struct wcd9xxx_mbhc_intr cdc_intr_ids = {
+	.poll_plug_rem = WCD9XXX_IRQ_MBHC_REMOVAL,
+	.shortavg_complete = WCD9XXX_IRQ_MBHC_SHORT_TERM,
+	.potential_button_press = WCD9XXX_IRQ_MBHC_PRESS,
+	.button_release = WCD9XXX_IRQ_MBHC_RELEASE,
+	.dce_est_complete = WCD9XXX_IRQ_MBHC_POTENTIAL,
+	.insertion = WCD9XXX_IRQ_MBHC_INSERTION,
+	.hph_left_ocp = WCD9XXX_IRQ_HPH_PA_OCPL_FAULT,
+	.hph_right_ocp = WCD9XXX_IRQ_HPH_PA_OCPR_FAULT,
+	.hs_jack_switch = WCD9330_IRQ_MBHC_JACK_SWITCH,
+};
+
+static int tomtom_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec;
+	struct tomtom_priv *tomtom;
+	int rco_clk_rate;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	tomtom = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_card_change_online_state(codec->component.card, 1);
+	clear_bit(BUS_DOWN, &tomtom->status_mask);
+
+	mutex_lock(&tomtom->codec_mutex);
+
+	tomtom_update_reg_defaults(codec);
+	if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+		snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x0);
+	else if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ)
+		snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x2);
+	tomtom_codec_init_reg(codec);
+
+	snd_soc_cache_sync(codec);
+
+	ret = tomtom_handle_pdata(tomtom);
+	if (ret < 0)
+		pr_err("%s: bad pdata\n", __func__);
+
+	tomtom_init_slim_slave_cfg(codec);
+	tomtom_slim_interface_init_reg(codec);
+	wcd_cpe_ssr_event(tomtom->cpe_core, WCD_CPE_BUS_UP_EVENT);
+	wcd9xxx_resmgr_post_ssr(&tomtom->resmgr);
+
+	if (tomtom->mbhc_started) {
+		wcd9xxx_mbhc_deinit(&tomtom->mbhc);
+		tomtom->mbhc_started = false;
+
+		if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+			rco_clk_rate = TOMTOM_MCLK_CLK_12P288MHZ;
+		else
+			rco_clk_rate = TOMTOM_MCLK_CLK_9P6MHZ;
+
+		ret = wcd9xxx_mbhc_init(&tomtom->mbhc, &tomtom->resmgr, codec,
+					tomtom_enable_mbhc_micbias,
+					&mbhc_cb, &cdc_intr_ids,
+					rco_clk_rate, TOMTOM_ZDET_SUPPORTED);
+		if (ret)
+			pr_err("%s: mbhc init failed %d\n", __func__, ret);
+		else
+			tomtom_hs_detect(codec, tomtom->mbhc.mbhc_cfg);
+	}
+
+	if (tomtom->machine_codec_event_cb)
+		tomtom->machine_codec_event_cb(codec,
+				       WCD9XXX_CODEC_EVENT_CODEC_UP);
+
+	tomtom_cleanup_irqs(tomtom);
+	ret = tomtom_setup_irqs(tomtom);
+	if (ret)
+		pr_err("%s: Failed to setup irq: %d\n", __func__, ret);
+
+	/*
+	 * After SSR, the qfuse sensing is lost.
+	 * Perform qfuse sensing again after SSR
+	 * handling is finished.
+	 */
+	tomtom_enable_qfuse_sensing(codec);
+	mutex_unlock(&tomtom->codec_mutex);
+	return ret;
+}
+
+void *tomtom_get_afe_config(struct snd_soc_codec *codec,
+			   enum afe_config_type config_type)
+{
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (config_type) {
+	case AFE_SLIMBUS_SLAVE_CONFIG:
+		return &priv->slimbus_slave_cfg;
+	case AFE_CDC_REGISTERS_CONFIG:
+		return &tomtom_audio_reg_cfg;
+	case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+		return &tomtom_slimbus_slave_port_cfg;
+	case AFE_AANC_VERSION:
+		return &tomtom_cdc_aanc_version;
+	case AFE_CLIP_BANK_SEL:
+		return &clip_bank_sel;
+	case AFE_CDC_CLIP_REGISTERS_CONFIG:
+		return &tomtom_clip_reg_cfg;
+	default:
+		pr_err("%s: Unknown config_type 0x%x\n", __func__, config_type);
+		return NULL;
+	}
+}
+
+static struct wcd9xxx_reg_address tomtom_reg_address = {
+	.micb_4_mbhc = TOMTOM_A_MICB_4_MBHC,
+	.micb_4_int_rbias = TOMTOM_A_MICB_4_INT_RBIAS,
+	.micb_4_ctl = TOMTOM_A_MICB_4_CTL,
+};
+
+static int wcd9xxx_ssr_register(struct wcd9xxx *control,
+				int (*device_down_cb)(struct wcd9xxx *wcd9xxx),
+				int (*device_up_cb)(struct wcd9xxx *wcd9xxx),
+				void *priv)
+{
+	control->dev_down = device_down_cb;
+	control->post_reset = device_up_cb;
+	control->ssr_priv = priv;
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget tomtom_1_dapm_widgets[] = {
+	SND_SOC_DAPM_ADC_E("ADC1", NULL, TOMTOM_A_TX_1_GAIN, 7, 0,
+			   tomtom_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU |
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC2", NULL, TOMTOM_A_TX_2_GAIN, 7, 0,
+			   tomtom_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU |
+			   SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC3", NULL, TOMTOM_A_TX_3_GAIN, 7, 0,
+			   tomtom_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC4", NULL, TOMTOM_A_TX_4_GAIN, 7, 0,
+			   tomtom_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC5", NULL, TOMTOM_A_TX_5_GAIN, 7, 0,
+			   tomtom_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("ADC6", NULL, TOMTOM_A_TX_6_GAIN, 7, 0,
+			   tomtom_codec_enable_adc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+};
+
+static struct regulator *tomtom_codec_find_regulator(struct snd_soc_codec *cdc,
+						    const char *name)
+{
+	int i;
+	struct wcd9xxx *core = dev_get_drvdata(cdc->dev->parent);
+
+	for (i = 0; i < core->num_of_supplies; i++) {
+		if (core->supplies[i].supply &&
+		    !strcmp(core->supplies[i].supply, name))
+			return core->supplies[i].consumer;
+	}
+
+	return NULL;
+}
+
+static struct wcd_cpe_core *tomtom_codec_get_cpe_core(
+		struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	return priv->cpe_core;
+}
+
+static int tomtom_codec_fll_enable(struct snd_soc_codec *codec,
+				   bool enable)
+{
+	struct wcd9xxx *wcd9xxx;
+
+	if (!codec || !codec->control_data) {
+		pr_err("%s: Invalid codec handle, %pK\n",
+		       __func__, codec);
+		return -EINVAL;
+	}
+
+	wcd9xxx = codec->control_data;
+
+	dev_dbg(codec->dev, "%s: %s, mclk_rate = %d\n",
+		__func__, (enable ? "enable" : "disable"),
+		wcd9xxx->mclk_rate);
+
+	switch (wcd9xxx->mclk_rate) {
+	case TOMTOM_MCLK_CLK_9P6MHZ:
+		snd_soc_update_bits(codec, TOMTOM_A_FLL_NREF,
+				    0x1F, 0x15);
+		snd_soc_update_bits(codec, TOMTOM_A_FLL_KDCO_TUNE,
+				    0x07, 0x06);
+		snd_soc_write(codec, TOMTOM_A_FLL_LOCK_THRESH, 0xD1);
+		snd_soc_write(codec, TOMTOM_A_FLL_LOCK_DET_COUNT,
+			      0x40);
+		break;
+	case TOMTOM_MCLK_CLK_12P288MHZ:
+		snd_soc_update_bits(codec, TOMTOM_A_FLL_NREF,
+				    0x1F, 0x11);
+		snd_soc_update_bits(codec, TOMTOM_A_FLL_KDCO_TUNE,
+				    0x07, 0x05);
+		snd_soc_write(codec, TOMTOM_A_FLL_LOCK_THRESH, 0xB1);
+		snd_soc_write(codec, TOMTOM_A_FLL_LOCK_DET_COUNT,
+			      0x40);
+		break;
+	}
+
+	return 0;
+}
+
+static int tomtom_codec_slim_reserve_bw(struct snd_soc_codec *codec,
+		u32 bw_ops, bool commit)
+{
+	struct wcd9xxx *wcd9xxx;
+
+	if (!codec) {
+		pr_err("%s: Invalid handle to codec\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!wcd9xxx) {
+		dev_err(codec->dev, "%s: Invalid parent drv_data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit);
+}
+
+static int tomtom_codec_vote_max_bw(struct snd_soc_codec *codec,
+			bool vote)
+{
+	u32 bw_ops;
+
+	if (vote)
+		bw_ops = SLIM_BW_CLK_GEAR_9;
+	else
+		bw_ops = SLIM_BW_UNVOTE;
+
+	return tomtom_codec_slim_reserve_bw(codec,
+			bw_ops, true);
+}
+
+static const struct wcd9xxx_resmgr_cb resmgr_cb = {
+	.cdc_rco_ctrl = tomtom_codec_internal_rco_ctrl,
+};
+
+static int tomtom_cpe_err_irq_control(struct snd_soc_codec *codec,
+	enum cpe_err_irq_cntl_type cntl_type, u8 *status)
+{
+	switch (cntl_type) {
+	case CPE_ERR_IRQ_MASK:
+		snd_soc_update_bits(codec,
+				    TOMTOM_A_SVASS_INT_MASK,
+				    0x3F, 0x3F);
+		break;
+	case CPE_ERR_IRQ_UNMASK:
+		snd_soc_update_bits(codec,
+				    TOMTOM_A_SVASS_INT_MASK,
+				    0x3F, 0x0C);
+		break;
+	case CPE_ERR_IRQ_CLEAR:
+		snd_soc_update_bits(codec,
+				    TOMTOM_A_SVASS_INT_CLR,
+				    0x3F, 0x3F);
+		break;
+	case CPE_ERR_IRQ_STATUS:
+		if (!status)
+			return -EINVAL;
+		*status = snd_soc_read(codec,
+				       TOMTOM_A_SVASS_INT_STATUS);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct wcd_cpe_cdc_cb cpe_cb = {
+	.cdc_clk_en = tomtom_codec_internal_rco_ctrl,
+	.cpe_clk_en = tomtom_codec_fll_enable,
+	.lab_cdc_ch_ctl = tomtom_codec_enable_slimtx_mad,
+	.cdc_ext_clk = tomtom_codec_ext_clk_en,
+	.bus_vote_bw = tomtom_codec_vote_max_bw,
+	.cpe_err_irq_control = tomtom_cpe_err_irq_control,
+};
+
+static struct cpe_svc_init_param cpe_svc_params = {
+	.version = 0,
+	.query_freq_plans_cb = NULL,
+	.change_freq_plan_cb = NULL,
+};
+
+static int tomtom_cpe_initialize(struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	struct wcd_cpe_params cpe_params;
+
+	memset(&cpe_params, 0,
+	       sizeof(struct wcd_cpe_params));
+	cpe_params.codec = codec;
+	cpe_params.get_cpe_core = tomtom_codec_get_cpe_core;
+	cpe_params.cdc_cb = &cpe_cb;
+	cpe_params.dbg_mode = cpe_debug_mode;
+	cpe_params.cdc_major_ver = CPE_SVC_CODEC_TOMTOM;
+	cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0;
+	cpe_params.cdc_id = CPE_SVC_CODEC_TOMTOM;
+
+	cpe_params.cdc_irq_info.cpe_engine_irq =
+			WCD9330_IRQ_SVASS_ENGINE;
+	cpe_params.cdc_irq_info.cpe_err_irq =
+			WCD9330_IRQ_SVASS_ERR_EXCEPTION;
+	cpe_params.cdc_irq_info.cpe_fatal_irqs =
+			TOMTOM_CPE_FATAL_IRQS;
+
+	cpe_svc_params.context = codec;
+	cpe_params.cpe_svc_params = &cpe_svc_params;
+
+	tomtom->cpe_core = wcd_cpe_init("cpe", codec,
+					&cpe_params);
+	if (IS_ERR_OR_NULL(tomtom->cpe_core)) {
+		dev_err(codec->dev,
+			"%s: Failed to enable CPE\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int tomtom_codec_probe(struct snd_soc_codec *codec)
+{
+	struct wcd9xxx *control;
+	struct tomtom_priv *tomtom;
+	struct wcd9xxx_pdata *pdata;
+	struct wcd9xxx *wcd9xxx;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	int ret = 0;
+	int i, rco_clk_rate;
+	void *ptr = NULL;
+	struct wcd9xxx_core_resource *core_res;
+	struct clk *wcd_ext_clk = NULL;
+
+	dev_info(codec->dev, "%s()\n", __func__);
+
+	control = dev_get_drvdata(codec->dev->parent);
+
+	tomtom = snd_soc_codec_get_drvdata(codec);
+
+	wcd9xxx_ssr_register(control, tomtom_device_down,
+			     tomtom_post_reset_cb, (void *)codec);
+
+	for (i = 0; i < NUM_DECIMATORS; i++) {
+		tx_hpf_work[i].tomtom = tomtom;
+		tx_hpf_work[i].decimator = i + 1;
+		tx_hpf_work[i].tx_hpf_bypass = false;
+		INIT_DELAYED_WORK(&tx_hpf_work[i].dwork,
+			tx_hpf_corner_freq_callback);
+	}
+
+	wcd9xxx = control;
+	if (!of_find_property(wcd9xxx->dev->of_node, "clock-names", NULL)) {
+		dev_dbg(wcd9xxx->dev, "%s: codec not using audio-ext-clk driver\n",
+			__func__);
+	} else {
+		wcd_ext_clk = clk_get(wcd9xxx->dev, "wcd_clk");
+		if (IS_ERR(wcd_ext_clk)) {
+			dev_err(codec->dev, "%s: clk get %s failed\n",
+					__func__, "wcd_ext_clk");
+			goto err_nomem_slimch;
+		}
+	}
+	tomtom->wcd_ext_clk = wcd_ext_clk;
+	core_res = &wcd9xxx->core_res;
+	pdata = dev_get_platdata(codec->dev->parent);
+	/* codec resmgr module init */
+	ret = wcd9xxx_resmgr_init(&tomtom->resmgr, codec, core_res, pdata,
+				  &pdata->micbias, &tomtom_reg_address,
+				  &resmgr_cb, WCD9XXX_CDC_TYPE_TOMTOM);
+	if (ret) {
+		pr_err("%s: wcd9xxx init failed %d\n", __func__, ret);
+		goto err_nomem_slimch;
+	}
+
+	tomtom->clsh_d.buck_mv = tomtom_codec_get_buck_mv(codec);
+	/* TomTom does not support dynamic switching of vdd_cp */
+	tomtom->clsh_d.is_dynamic_vdd_cp = false;
+	wcd9xxx_clsh_init(&tomtom->clsh_d, &tomtom->resmgr);
+
+	if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+		rco_clk_rate = TOMTOM_MCLK_CLK_12P288MHZ;
+	else
+		rco_clk_rate = TOMTOM_MCLK_CLK_9P6MHZ;
+
+	tomtom->fw_data = kzalloc(sizeof(*(tomtom->fw_data)), GFP_KERNEL);
+	if (!tomtom->fw_data)
+		goto err_nomem_slimch;
+	set_bit(WCD9XXX_ANC_CAL, tomtom->fw_data->cal_bit);
+	set_bit(WCD9XXX_MAD_CAL, tomtom->fw_data->cal_bit);
+	set_bit(WCD9XXX_MBHC_CAL, tomtom->fw_data->cal_bit);
+	ret = wcd_cal_create_hwdep(tomtom->fw_data,
+				WCD9XXX_CODEC_HWDEP_NODE, codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+		goto err_hwdep;
+	}
+
+	/* init and start mbhc */
+	ret = wcd9xxx_mbhc_init(&tomtom->mbhc, &tomtom->resmgr, codec,
+				tomtom_enable_mbhc_micbias,
+				&mbhc_cb, &cdc_intr_ids,
+				rco_clk_rate, TOMTOM_ZDET_SUPPORTED);
+	if (ret) {
+		pr_err("%s: mbhc init failed %d\n", __func__, ret);
+		goto err_hwdep;
+	}
+
+	tomtom->codec = codec;
+	for (i = 0; i < COMPANDER_MAX; i++) {
+		tomtom->comp_enabled[i] = 0;
+		tomtom->comp_fs[i] = COMPANDER_FS_48KHZ;
+	}
+	tomtom->intf_type = wcd9xxx_get_intf_type();
+	tomtom->aux_pga_cnt = 0;
+	tomtom->aux_l_gain = 0x1F;
+	tomtom->aux_r_gain = 0x1F;
+	tomtom->ldo_h_users = 0;
+	tomtom->micb_2_users = 0;
+	tomtom_update_reg_defaults(codec);
+	pr_debug("%s: MCLK Rate = %x\n", __func__, wcd9xxx->mclk_rate);
+	if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_12P288MHZ)
+		snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x0);
+	else if (wcd9xxx->mclk_rate == TOMTOM_MCLK_CLK_9P6MHZ)
+		snd_soc_update_bits(codec, TOMTOM_A_CHIP_CTL, 0x06, 0x2);
+	tomtom_codec_init_reg(codec);
+
+	ret = tomtom_handle_pdata(tomtom);
+	if (ret < 0) {
+		pr_err("%s: bad pdata\n", __func__);
+		goto err_hwdep;
+	}
+
+	tomtom->spkdrv_reg = tomtom_codec_find_regulator(codec,
+					       WCD9XXX_VDD_SPKDRV_NAME);
+	tomtom->spkdrv2_reg = tomtom_codec_find_regulator(codec,
+					       WCD9XXX_VDD_SPKDRV2_NAME);
+
+	ptr = kmalloc((sizeof(tomtom_rx_chs) +
+		       sizeof(tomtom_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto err_hwdep;
+	}
+
+	if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+		snd_soc_dapm_new_controls(dapm, tomtom_dapm_i2s_widgets,
+			ARRAY_SIZE(tomtom_dapm_i2s_widgets));
+		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
+			ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(tomtom_i2s_dai); i++)
+			INIT_LIST_HEAD(&tomtom->dai[i].wcd9xxx_ch_list);
+	} else if (tomtom->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&tomtom->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&tomtom->dai[i].dai_wait);
+		}
+		tomtom_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+		    control->slim_slave->laddr;
+		tomtom_slimbus_slave_port_cfg.slave_dev_pgd_la =
+		    control->slim->laddr;
+		tomtom_slimbus_slave_port_cfg.slave_port_mapping[0] =
+		    TOMTOM_MAD_SLIMBUS_TX_PORT;
+
+		tomtom_init_slim_slave_cfg(codec);
+	}
+
+	snd_soc_dapm_new_controls(dapm, tomtom_1_dapm_widgets,
+			ARRAY_SIZE(tomtom_1_dapm_widgets));
+	snd_soc_add_codec_controls(codec,
+			tomtom_1_x_analog_gain_controls,
+			ARRAY_SIZE(tomtom_1_x_analog_gain_controls));
+
+	snd_soc_add_codec_controls(codec, impedance_detect_controls,
+				   ARRAY_SIZE(impedance_detect_controls));
+	snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+				   ARRAY_SIZE(hph_type_detect_controls));
+
+	control->num_rx_port = TOMTOM_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, tomtom_rx_chs, sizeof(tomtom_rx_chs));
+	control->num_tx_port = TOMTOM_TX_MAX;
+	control->tx_chs = ptr + sizeof(tomtom_rx_chs);
+	memcpy(control->tx_chs, tomtom_tx_chs, sizeof(tomtom_tx_chs));
+
+	snd_soc_dapm_sync(dapm);
+
+	ret = tomtom_setup_irqs(tomtom);
+	if (ret) {
+		pr_err("%s: tomtom irq setup failed %d\n", __func__, ret);
+		goto err_pdata;
+	}
+
+	atomic_set(&kp_tomtom_priv, (unsigned long)tomtom);
+	mutex_lock(&tomtom->codec_mutex);
+	snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+	snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+	snd_soc_dapm_disable_pin(dapm, "ANC HEADPHONE");
+	snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+	mutex_unlock(&tomtom->codec_mutex);
+	snd_soc_dapm_sync(dapm);
+
+	codec->component.ignore_pmdown_time = 1;
+	ret = tomtom_cpe_initialize(codec);
+	if (ret) {
+		dev_info(codec->dev,
+			"%s: cpe initialization failed, ret = %d\n",
+			__func__, ret);
+		/* Do not fail probe if CPE failed */
+		ret = 0;
+	}
+	return ret;
+
+err_pdata:
+	kfree(ptr);
+	control->rx_chs = NULL;
+	control->tx_chs = NULL;
+err_hwdep:
+	kfree(tomtom->fw_data);
+	tomtom->fw_data = NULL;
+err_nomem_slimch:
+	devm_kfree(codec->dev, tomtom);
+	return ret;
+}
+static int tomtom_codec_remove(struct snd_soc_codec *codec)
+{
+	struct tomtom_priv *tomtom = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *control;
+
+	WCD9XXX_BG_CLK_LOCK(&tomtom->resmgr);
+	atomic_set(&kp_tomtom_priv, 0);
+
+	WCD9XXX_BG_CLK_UNLOCK(&tomtom->resmgr);
+
+	control = dev_get_drvdata(codec->dev->parent);
+	control->rx_chs = NULL;
+	control->tx_chs = NULL;
+
+	if (tomtom->wcd_ext_clk)
+		clk_put(tomtom->wcd_ext_clk);
+	tomtom_cleanup_irqs(tomtom);
+
+	/* cleanup MBHC */
+	wcd9xxx_mbhc_deinit(&tomtom->mbhc);
+	/* cleanup resmgr */
+	wcd9xxx_resmgr_deinit(&tomtom->resmgr);
+
+	tomtom->spkdrv_reg = NULL;
+	tomtom->spkdrv2_reg = NULL;
+
+	devm_kfree(codec->dev, tomtom);
+	return 0;
+}
+
+static struct regmap *tomtom_get_regmap(struct device *dev)
+{
+	struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+	return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tomtom = {
+	.probe = tomtom_codec_probe,
+	.remove = tomtom_codec_remove,
+	.get_regmap = tomtom_get_regmap,
+	.component_driver = {
+		.controls = tomtom_snd_controls,
+		.num_controls = ARRAY_SIZE(tomtom_snd_controls),
+		.dapm_widgets = tomtom_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(tomtom_dapm_widgets),
+		.dapm_routes = audio_map,
+		.num_dapm_routes = ARRAY_SIZE(audio_map),
+	},
+};
+
+#ifdef CONFIG_PM
+static int tomtom_suspend(struct device *dev)
+{
+	dev_dbg(dev, "%s: system suspend\n", __func__);
+	return 0;
+}
+
+static int tomtom_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tomtom_priv *tomtom = platform_get_drvdata(pdev);
+
+	if (!tomtom) {
+		dev_err(dev, "%s: tomtom private data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(dev, "%s: system resume\n", __func__);
+	/* Notify */
+	wcd9xxx_resmgr_notifier_call(&tomtom->resmgr,
+				     WCD9XXX_EVENT_POST_RESUME);
+	return 0;
+}
+
+static const struct dev_pm_ops tomtom_pm_ops = {
+	.suspend	= tomtom_suspend,
+	.resume		= tomtom_resume,
+};
+#endif
+
+static int tomtom_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct tomtom_priv *tomtom;
+
+	tomtom = devm_kzalloc(&pdev->dev, sizeof(struct tomtom_priv),
+			      GFP_KERNEL);
+	if (!tomtom)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, tomtom);
+
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tomtom,
+			tomtom_dai, ARRAY_SIZE(tomtom_dai));
+	else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tomtom,
+			tomtom_i2s_dai, ARRAY_SIZE(tomtom_i2s_dai));
+	mutex_init(&tomtom->codec_mutex);
+	return ret;
+}
+static int tomtom_remove(struct platform_device *pdev)
+{
+	struct tomtom_priv *tomtom = platform_get_drvdata(pdev);
+
+	mutex_destroy(&tomtom->codec_mutex);
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+static struct platform_driver tomtom_codec_driver = {
+	.probe = tomtom_probe,
+	.remove = tomtom_remove,
+	.driver = {
+		.name = "tomtom_codec",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &tomtom_pm_ops,
+#endif
+	},
+};
+
+static int __init tomtom_codec_init(void)
+{
+	return platform_driver_register(&tomtom_codec_driver);
+}
+
+static void __exit tomtom_codec_exit(void)
+{
+	platform_driver_unregister(&tomtom_codec_driver);
+}
+
+module_init(tomtom_codec_init);
+module_exit(tomtom_codec_exit);
+
+MODULE_DESCRIPTION("TomTom codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9330.h b/sound/soc/codecs/wcd9330.h
new file mode 100644
index 0000000..8679d01
--- /dev/null
+++ b/sound/soc/codecs/wcd9330.h
@@ -0,0 +1,128 @@
+/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD9330_H
+#define WCD9330_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
+
+#define TOMTOM_NUM_REGISTERS 0x400
+#define TOMTOM_MAX_REGISTER (TOMTOM_NUM_REGISTERS-1)
+#define TOMTOM_CACHE_SIZE TOMTOM_NUM_REGISTERS
+
+#define TOMTOM_REG_VAL(reg, val)		{reg, 0, val}
+#define TOMTOM_MCLK_ID 0
+
+#define TOMTOM_REGISTER_START_OFFSET 0x800
+#define TOMTOM_SB_PGD_PORT_RX_BASE   0x40
+#define TOMTOM_SB_PGD_PORT_TX_BASE   0x50
+
+#define WCD9330_DMIC_CLK_DIV_2 0x00
+#define WCD9330_DMIC_CLK_DIV_3 0x01
+#define WCD9330_DMIC_CLK_DIV_4 0x02
+#define WCD9330_DMIC_CLK_DIV_6 0x03
+#define WCD9330_DMIC_CLK_DIV_16 0x04
+
+#define TOMTOM_ZDET_SUPPORTED true
+
+extern const u8 tomtom_reset_reg_defaults[TOMTOM_CACHE_SIZE];
+struct tomtom_codec_dai_data {
+	u32 rate;
+	u32 *ch_num;
+	u32 ch_act;
+	u32 ch_tot;
+};
+
+enum tomtom_pid_current {
+	TOMTOM_PID_MIC_2P5_UA,
+	TOMTOM_PID_MIC_5_UA,
+	TOMTOM_PID_MIC_10_UA,
+	TOMTOM_PID_MIC_20_UA,
+};
+
+enum tomtom_mbhc_analog_pwr_cfg {
+	TOMTOM_ANALOG_PWR_COLLAPSED = 0,
+	TOMTOM_ANALOG_PWR_ON,
+	TOMTOM_NUM_ANALOG_PWR_CONFIGS,
+};
+
+enum {
+	HPH_PA_NONE = 0,
+	HPH_PA_R,
+	HPH_PA_L,
+	HPH_PA_L_R,
+};
+
+/* Number of input and output Slimbus port */
+enum {
+	TOMTOM_RX1 = 0,
+	TOMTOM_RX2,
+	TOMTOM_RX3,
+	TOMTOM_RX4,
+	TOMTOM_RX5,
+	TOMTOM_RX6,
+	TOMTOM_RX7,
+	TOMTOM_RX8,
+	TOMTOM_RX9,
+	TOMTOM_RX10,
+	TOMTOM_RX11,
+	TOMTOM_RX12,
+	TOMTOM_RX13,
+	TOMTOM_RX_MAX,
+};
+
+enum {
+	TOMTOM_TX1 = 0,
+	TOMTOM_TX2,
+	TOMTOM_TX3,
+	TOMTOM_TX4,
+	TOMTOM_TX5,
+	TOMTOM_TX6,
+	TOMTOM_TX7,
+	TOMTOM_TX8,
+	TOMTOM_TX9,
+	TOMTOM_TX10,
+	TOMTOM_TX11,
+	TOMTOM_TX12,
+	TOMTOM_TX13,
+	TOMTOM_TX14,
+	TOMTOM_TX15,
+	TOMTOM_TX16,
+	TOMTOM_TX_MAX,
+};
+
+extern int tomtom_mclk_enable(struct snd_soc_codec *codec, int mclk_enable,
+			     bool dapm);
+extern int tomtom_codec_mclk_enable(struct snd_soc_codec *codec,
+				    int mclk_enable, bool dapm);
+extern int tomtom_hs_detect(struct snd_soc_codec *codec,
+			   struct wcd9xxx_mbhc_config *mbhc_cfg);
+extern void tomtom_hs_detect_exit(struct snd_soc_codec *codec);
+extern void *tomtom_get_afe_config(struct snd_soc_codec *codec,
+				  enum afe_config_type config_type);
+
+extern void tomtom_event_register(
+	int (*machine_event_cb)(struct snd_soc_codec *codec,
+				enum wcd9xxx_codec_event),
+	struct snd_soc_codec *codec);
+extern void tomtom_register_ext_clk_cb(
+	int (*codec_ext_clk_en)(struct snd_soc_codec *codec,
+				int enable, bool dapm),
+	int (*get_ext_clk_cnt)(void),
+	struct snd_soc_codec *codec);
+extern int tomtom_enable_qfuse_sensing(struct snd_soc_codec *codec);
+#endif
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
new file mode 100644
index 0000000..2185f4b
--- /dev/null
+++ b/sound/soc/codecs/wcd9335.c
@@ -0,0 +1,14378 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/regulator/consumer.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/soundwire/swr-wcd.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+#include "wcd9335.h"
+#include "wcd-mbhc-v2.h"
+#include "wcd9xxx-common-v2.h"
+#include "wcd9xxx-resmgr-v2.h"
+#include "wcd_cpe_core.h"
+#include "wcdcal-hwdep.h"
+
+#define TASHA_RX_PORT_START_NUMBER  16
+
+#define WCD9335_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			    SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+			    SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+/* Fractional Rates */
+#define WCD9335_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100)
+
+#define WCD9335_MIX_RATES_MASK (SNDRV_PCM_RATE_48000 |\
+				SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define TASHA_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+				  SNDRV_PCM_FMTBIT_S24_LE | \
+				  SNDRV_PCM_FMTBIT_S24_3LE)
+
+#define TASHA_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+				  SNDRV_PCM_FMTBIT_S24_LE | \
+				  SNDRV_PCM_FMTBIT_S24_3LE | \
+				  SNDRV_PCM_FMTBIT_S32_LE)
+
+#define TASHA_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ * Timeout in milli seconds and it is the wait time for
+ * slim channel removal interrupt to receive.
+ */
+#define TASHA_SLIM_CLOSE_TIMEOUT 1000
+#define TASHA_SLIM_IRQ_OVERFLOW (1 << 0)
+#define TASHA_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define TASHA_SLIM_IRQ_PORT_CLOSED (1 << 2)
+#define TASHA_MCLK_CLK_12P288MHZ 12288000
+#define TASHA_MCLK_CLK_9P6MHZ 9600000
+
+#define TASHA_SLIM_PGD_PORT_INT_TX_EN0 (TASHA_SLIM_PGD_PORT_INT_EN0 + 2)
+
+#define TASHA_NUM_INTERPOLATORS 9
+#define TASHA_NUM_DECIMATORS 9
+
+#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE))
+#define TASHA_MAD_AUDIO_FIRMWARE_PATH "wcd9335/wcd9335_mad_audio.bin"
+#define TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS (1 << 0)
+#define TASHA_CPE_SS_ERR_STATUS_WDOG_BITE (1 << 1)
+
+#define TASHA_CPE_FATAL_IRQS \
+	(TASHA_CPE_SS_ERR_STATUS_WDOG_BITE | \
+	 TASHA_CPE_SS_ERR_STATUS_MEM_ACCESS)
+
+#define SLIM_BW_CLK_GEAR_9 6200000
+#define SLIM_BW_UNVOTE 0
+
+#define CPE_FLL_CLK_75MHZ 75000000
+#define CPE_FLL_CLK_150MHZ 150000000
+#define WCD9335_REG_BITS 8
+
+#define WCD9335_MAX_VALID_ADC_MUX  13
+#define WCD9335_INVALID_ADC_MUX 9
+
+#define TASHA_DIG_CORE_REG_MIN  WCD9335_CDC_ANC0_CLK_RESET_CTL
+#define TASHA_DIG_CORE_REG_MAX  0xDFF
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define WCD_VOUT_CTL_TO_MICB(v) (1000 + v * 50)
+
+#define TASHA_ZDET_NUM_MEASUREMENTS 150
+#define TASHA_MBHC_GET_C1(c)  ((c & 0xC000) >> 14)
+#define TASHA_MBHC_GET_X1(x)  (x & 0x3FFF)
+/* z value compared in milliOhm */
+#define TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define TASHA_MBHC_ZDET_CONST  (86 * 16384)
+#define TASHA_MBHC_MOISTURE_VREF  V_45_MV
+#define TASHA_MBHC_MOISTURE_IREF  I_3P0_UA
+
+#define TASHA_VERSION_ENTRY_SIZE 17
+
+#define WCD9335_AMIC_PWR_LEVEL_LP 0
+#define WCD9335_AMIC_PWR_LEVEL_DEFAULT 1
+#define WCD9335_AMIC_PWR_LEVEL_HP 2
+#define WCD9335_AMIC_PWR_LVL_MASK 0x60
+#define WCD9335_AMIC_PWR_LVL_SHIFT 0x5
+
+#define WCD9335_DEC_PWR_LVL_MASK 0x06
+#define WCD9335_DEC_PWR_LVL_LP 0x02
+#define WCD9335_DEC_PWR_LVL_HP 0x04
+#define WCD9335_DEC_PWR_LVL_DF 0x00
+#define WCD9335_STRING_LEN 100
+
+#define CALCULATE_VOUT_D(req_mv) (((req_mv - 650) * 10) / 25)
+
+static int cpe_debug_mode;
+
+#define TASHA_MAX_MICBIAS 4
+#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone"
+#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone"
+#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone"
+#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone"
+
+#define DAPM_LDO_H_STANDALONE "LDO_H"
+module_param(cpe_debug_mode, int, 0664);
+MODULE_PARM_DESC(cpe_debug_mode, "boot cpe in debug mode");
+
+#define TASHA_DIG_CORE_COLLAPSE_TIMER_MS  (5 * 1000)
+
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH    64
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+	"cdc-vdd-mic-bias",
+};
+
+enum {
+	POWER_COLLAPSE,
+	POWER_RESUME,
+};
+
+enum tasha_sido_voltage {
+	SIDO_VOLTAGE_SVS_MV = 950,
+	SIDO_VOLTAGE_NOMINAL_MV = 1100,
+};
+
+static enum codec_variant codec_ver;
+
+static int dig_core_collapse_enable = 1;
+module_param(dig_core_collapse_enable, int, 0664);
+MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating");
+
+/* dig_core_collapse timer in seconds */
+static int dig_core_collapse_timer = (TASHA_DIG_CORE_COLLAPSE_TIMER_MS/1000);
+module_param(dig_core_collapse_timer, int, 0664);
+MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating");
+
+/* SVS Scaling enable/disable */
+static int svs_scaling_enabled = 1;
+module_param(svs_scaling_enabled, int, 0664);
+MODULE_PARM_DESC(svs_scaling_enabled, "enable/disable svs scaling");
+
+/* SVS buck setting */
+static int sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV;
+module_param(sido_buck_svs_voltage, int, 0664);
+MODULE_PARM_DESC(sido_buck_svs_voltage,
+			"setting for SVS voltage for SIDO BUCK");
+
+#define TASHA_TX_UNMUTE_DELAY_MS	25
+
+static int tx_unmute_delay = TASHA_TX_UNMUTE_DELAY_MS;
+module_param(tx_unmute_delay, int, 0664);
+MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path");
+
+static struct afe_param_slimbus_slave_port_cfg tasha_slimbus_slave_port_cfg = {
+	.minor_version = 1,
+	.slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+	.slave_dev_pgd_la = 0,
+	.slave_dev_intfdev_la = 0,
+	.bit_width = 16,
+	.data_format = 0,
+	.num_channels = 1
+};
+
+struct tasha_mbhc_zdet_param {
+	u16 ldo_ctl;
+	u16 noff;
+	u16 nshift;
+	u16 btn5;
+	u16 btn6;
+	u16 btn7;
+};
+
+static struct afe_param_cdc_reg_page_cfg tasha_cdc_reg_page_cfg = {
+	.minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG,
+	.enable = 1,
+	.proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1,
+};
+
+static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_MAIN_CTL_1),
+		HW_MAD_AUDIO_ENABLE, 0x1, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_3),
+		HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_SOC_MAD_AUDIO_CTL_4),
+		HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
+		MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
+		MAD_AUDIO_INT_MASK_REG, 0x1, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
+		MAD_AUDIO_INT_STATUS_REG, 0x1, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
+		MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
+		VBAT_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
+		VBAT_INT_MASK_REG, 0x08, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
+		VBAT_INT_STATUS_REG, 0x08, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
+		VBAT_INT_CLEAR_REG, 0x08, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_CFG),
+		VBAT_RELEASE_INT_DEST_SELECT_REG, 0x2, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_MASK3),
+		VBAT_RELEASE_INT_MASK_REG, 0x10, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_STATUS3),
+		VBAT_RELEASE_INT_STATUS_REG, 0x10, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_INTR_PIN2_CLEAR3),
+		VBAT_RELEASE_INT_CLEAR_REG, 0x10, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE),
+		SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_TX_BASE),
+		SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE),
+		SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD9335_REG_BITS, 0x1
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + TASHA_SB_PGD_PORT_RX_BASE),
+		SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD9335_REG_BITS, 0x1
+	},
+	{	1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL),
+		AANC_FF_GAIN_ADAPTIVE, 0x4, WCD9335_REG_BITS, 0
+	},
+	{	1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_IIR_ADAPT_CTL),
+		AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD9335_REG_BITS, 0
+	},
+	{
+		1,
+		(TASHA_REGISTER_START_OFFSET + WCD9335_CDC_ANC0_FF_A_GAIN_CTL),
+		AANC_GAIN_CONTROL, 0xFF, WCD9335_REG_BITS, 0
+	},
+};
+
+static struct afe_param_cdc_reg_cfg_data tasha_audio_reg_cfg = {
+	.num_registers = ARRAY_SIZE(audio_reg_cfg),
+	.reg_data = audio_reg_cfg,
+};
+
+static struct afe_param_id_cdc_aanc_version tasha_cdc_aanc_version = {
+	.cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+	.aanc_hw_version        = AANC_HW_BLOCK_VERSION_2,
+};
+
+enum {
+	VI_SENSE_1,
+	VI_SENSE_2,
+	AIF4_SWITCH_VALUE,
+	AUDIO_NOMINAL,
+	CPE_NOMINAL,
+	HPH_PA_DELAY,
+	SB_CLK_GEAR,
+	ANC_MIC_AMIC1,
+	ANC_MIC_AMIC2,
+	ANC_MIC_AMIC3,
+	ANC_MIC_AMIC4,
+	ANC_MIC_AMIC5,
+	ANC_MIC_AMIC6,
+	CLASSH_CONFIG,
+};
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	AIF4_PB,
+	AIF_MIX1_PB,
+	AIF4_MAD_TX,
+	AIF4_VIFEED,
+	AIF5_CPE_TX,
+	NUM_CODEC_DAIS,
+};
+
+enum {
+	INTn_1_MIX_INP_SEL_ZERO = 0,
+	INTn_1_MIX_INP_SEL_DEC0,
+	INTn_1_MIX_INP_SEL_DEC1,
+	INTn_1_MIX_INP_SEL_IIR0,
+	INTn_1_MIX_INP_SEL_IIR1,
+	INTn_1_MIX_INP_SEL_RX0,
+	INTn_1_MIX_INP_SEL_RX1,
+	INTn_1_MIX_INP_SEL_RX2,
+	INTn_1_MIX_INP_SEL_RX3,
+	INTn_1_MIX_INP_SEL_RX4,
+	INTn_1_MIX_INP_SEL_RX5,
+	INTn_1_MIX_INP_SEL_RX6,
+	INTn_1_MIX_INP_SEL_RX7,
+
+};
+
+#define IS_VALID_NATIVE_FIFO_PORT(inp) \
+	((inp >= INTn_1_MIX_INP_SEL_RX0) && \
+	 (inp <= INTn_1_MIX_INP_SEL_RX3))
+
+enum {
+	INTn_2_INP_SEL_ZERO = 0,
+	INTn_2_INP_SEL_RX0,
+	INTn_2_INP_SEL_RX1,
+	INTn_2_INP_SEL_RX2,
+	INTn_2_INP_SEL_RX3,
+	INTn_2_INP_SEL_RX4,
+	INTn_2_INP_SEL_RX5,
+	INTn_2_INP_SEL_RX6,
+	INTn_2_INP_SEL_RX7,
+	INTn_2_INP_SEL_PROXIMITY,
+};
+
+enum {
+	INTERP_EAR = 0,
+	INTERP_HPHL,
+	INTERP_HPHR,
+	INTERP_LO1,
+	INTERP_LO2,
+	INTERP_LO3,
+	INTERP_LO4,
+	INTERP_SPKR1,
+	INTERP_SPKR2,
+};
+
+struct interp_sample_rate {
+	int sample_rate;
+	int rate_val;
+};
+
+static struct interp_sample_rate int_prim_sample_rate_val[] = {
+	{8000, 0x0},	/* 8K */
+	{16000, 0x1},	/* 16K */
+	{24000, -EINVAL},/* 24K */
+	{32000, 0x3},	/* 32K */
+	{48000, 0x4},	/* 48K */
+	{96000, 0x5},	/* 96K */
+	{192000, 0x6},	/* 192K */
+	{384000, 0x7},	/* 384K */
+	{44100, 0x8}, /* 44.1K */
+};
+
+static struct interp_sample_rate int_mix_sample_rate_val[] = {
+	{48000, 0x4},	/* 48K */
+	{96000, 0x5},	/* 96K */
+	{192000, 0x6},	/* 192K */
+};
+
+static const struct wcd9xxx_ch tasha_rx_chs[TASHA_RX_MAX] = {
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER, 0),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 1, 1),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 2, 2),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 3, 3),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 4, 4),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 5, 5),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 6, 6),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 7, 7),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 8, 8),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 9, 9),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 10, 10),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 11, 11),
+	WCD9XXX_CH(TASHA_RX_PORT_START_NUMBER + 12, 12),
+};
+
+static const struct wcd9xxx_ch tasha_tx_chs[TASHA_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9),
+	WCD9XXX_CH(10, 10),
+	WCD9XXX_CH(11, 11),
+	WCD9XXX_CH(12, 12),
+	WCD9XXX_CH(13, 13),
+	WCD9XXX_CH(14, 14),
+	WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
+	/* Needs to define in the same order of DAI enum definitions */
+	0,
+	BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+	0,
+	BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+	0,
+	BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX) | BIT(AIF5_CPE_TX),
+	0,
+	0,
+	BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF5_CPE_TX),
+	0,
+	BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX),
+};
+
+static const u32 vport_i2s_check_table[NUM_CODEC_DAIS] = {
+	0,			/* AIF1_PB */
+	BIT(AIF2_CAP),		/* AIF1_CAP */
+	0,			/* AIF2_PB */
+	BIT(AIF1_CAP),		/* AIF2_CAP */
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+	IIR0 = 0,
+	IIR1,
+	IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+	BAND1 = 0,
+	BAND2,
+	BAND3,
+	BAND4,
+	BAND5,
+	BAND_MAX,
+};
+
+enum {
+	COMPANDER_1, /* HPH_L */
+	COMPANDER_2, /* HPH_R */
+	COMPANDER_3, /* LO1_DIFF */
+	COMPANDER_4, /* LO2_DIFF */
+	COMPANDER_5, /* LO3_SE */
+	COMPANDER_6, /* LO4_SE */
+	COMPANDER_7, /* SWR SPK CH1 */
+	COMPANDER_8, /* SWR SPK CH2 */
+	COMPANDER_MAX,
+};
+
+enum {
+	SRC_IN_HPHL,
+	SRC_IN_LO1,
+	SRC_IN_HPHR,
+	SRC_IN_LO2,
+	SRC_IN_SPKRL,
+	SRC_IN_LO3,
+	SRC_IN_SPKRR,
+	SRC_IN_LO4,
+};
+
+enum {
+	SPLINE_SRC0,
+	SPLINE_SRC1,
+	SPLINE_SRC2,
+	SPLINE_SRC3,
+	SPLINE_SRC_MAX,
+};
+
+/* wcd9335 interrupt table  */
+static const struct intr_data wcd9335_intr_table[] = {
+	{WCD9XXX_IRQ_SLIMBUS, false},
+	{WCD9335_IRQ_MBHC_SW_DET, true},
+	{WCD9335_IRQ_MBHC_BUTTON_PRESS_DET, true},
+	{WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET, true},
+	{WCD9335_IRQ_MBHC_ELECT_INS_REM_DET, true},
+	{WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
+	{WCD9335_IRQ_FLL_LOCK_LOSS, false},
+	{WCD9335_IRQ_HPH_PA_CNPL_COMPLETE, false},
+	{WCD9335_IRQ_HPH_PA_CNPR_COMPLETE, false},
+	{WCD9335_IRQ_EAR_PA_CNP_COMPLETE, false},
+	{WCD9335_IRQ_LINE_PA1_CNP_COMPLETE, false},
+	{WCD9335_IRQ_LINE_PA2_CNP_COMPLETE, false},
+	{WCD9335_IRQ_LINE_PA3_CNP_COMPLETE, false},
+	{WCD9335_IRQ_LINE_PA4_CNP_COMPLETE, false},
+	{WCD9335_IRQ_HPH_PA_OCPL_FAULT, false},
+	{WCD9335_IRQ_HPH_PA_OCPR_FAULT, false},
+	{WCD9335_IRQ_EAR_PA_OCP_FAULT, false},
+	{WCD9335_IRQ_SOUNDWIRE, false},
+	{WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE, false},
+	{WCD9335_IRQ_RCO_ERROR, false},
+	{WCD9335_IRQ_SVA_ERROR, false},
+	{WCD9335_IRQ_MAD_AUDIO, false},
+	{WCD9335_IRQ_MAD_BEACON, false},
+	{WCD9335_IRQ_SVA_OUTBOX1, true},
+	{WCD9335_IRQ_SVA_OUTBOX2, true},
+	{WCD9335_IRQ_MAD_ULTRASOUND, false},
+	{WCD9335_IRQ_VBAT_ATTACK, false},
+	{WCD9335_IRQ_VBAT_RESTORE, false},
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+static struct snd_soc_dai_driver tasha_dai[];
+static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv);
+
+static int tasha_config_compander(struct snd_soc_codec *, int, int);
+static void tasha_codec_set_tx_hold(struct snd_soc_codec *, u16, bool);
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+				  bool enable);
+
+/* Hold instance to soundwire platform device */
+struct tasha_swr_ctrl_data {
+	struct platform_device *swr_pdev;
+	struct ida swr_ida;
+};
+
+struct wcd_swr_ctrl_platform_data {
+	void *handle; /* holds codec private data */
+	int (*read)(void *handle, int reg);
+	int (*write)(void *handle, int reg, int val);
+	int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+	int (*clk)(void *handle, bool enable);
+	int (*handle_irq)(void *handle,
+			  irqreturn_t (*swrm_irq_handler)(int irq,
+							  void *data),
+			  void *swrm_handle,
+			  int action);
+};
+
+static struct wcd_mbhc_register
+	wcd_mbhc_registers[WCD_MBHC_REG_FUNC_MAX] = {
+	WCD_MBHC_REGISTER("WCD_MBHC_L_DET_EN",
+			  WCD9335_ANA_MBHC_MECH, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+			  WCD9335_ANA_MBHC_MECH, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+			  WCD9335_ANA_MBHC_MECH, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+			  WCD9335_MBHC_PLUG_DETECT_CTL, 0x30, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+			  WCD9335_ANA_MBHC_ELECT, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+			  WCD9335_MBHC_PLUG_DETECT_CTL, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+			  WCD9335_ANA_MBHC_MECH, 0x04, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+			  WCD9335_ANA_MBHC_MECH, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+			  WCD9335_ANA_MBHC_MECH, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+			  WCD9335_ANA_MBHC_MECH, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+			  WCD9335_ANA_MBHC_ELECT, 0x06, 1, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+			  WCD9335_ANA_MBHC_ELECT, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+			  WCD9335_MBHC_PLUG_DETECT_CTL, 0x0F, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+			  WCD9335_MBHC_CTL_1, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+			  WCD9335_MBHC_CTL_2, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+			  WCD9335_ANA_MBHC_RESULT_3, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+			  WCD9335_ANA_MBHC_RESULT_3, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+			  WCD9335_ANA_MBHC_RESULT_3, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+			  WCD9335_ANA_MBHC_RESULT_3, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+			  WCD9335_HPH_OCP_CTL, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+			  WCD9335_ANA_MBHC_RESULT_3, 0x07, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+			  WCD9335_ANA_MBHC_ELECT, 0x70, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+			  WCD9335_ANA_MBHC_RESULT_3, 0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+			  WCD9335_ANA_MICB2, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+			  WCD9335_HPH_CNP_WG_TIME, 0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+			  WCD9335_ANA_HPH, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+			  WCD9335_ANA_HPH, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+			  WCD9335_ANA_HPH, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+			  WCD9335_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",
+			  WCD9335_ANA_MBHC_ZDET, 0x01, 0, 0),
+	/*
+	 * MBHC FSM status register is only available in Tasha 2.0.
+	 * So, init with 0 later once the version is known, then values
+	 * will be updated.
+	 */
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+			  0, 0, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+			  WCD9335_MBHC_CTL_2, 0x70, 4, 0),
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+	.mbhc_sw_intr =  WCD9335_IRQ_MBHC_SW_DET,
+	.mbhc_btn_press_intr = WCD9335_IRQ_MBHC_BUTTON_PRESS_DET,
+	.mbhc_btn_release_intr = WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET,
+	.mbhc_hs_ins_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	.mbhc_hs_rem_intr = WCD9335_IRQ_MBHC_ELECT_INS_REM_DET,
+	.hph_left_ocp = WCD9335_IRQ_HPH_PA_OCPL_FAULT,
+	.hph_right_ocp = WCD9335_IRQ_HPH_PA_OCPR_FAULT,
+};
+
+struct wcd_vbat {
+	bool is_enabled;
+	bool adc_config;
+	/* Variables to cache Vbat ADC output values */
+	u16 dcp1;
+	u16 dcp2;
+};
+
+struct hpf_work {
+	struct tasha_priv *tasha;
+	u8 decimator;
+	u8 hpf_cut_off_freq;
+	struct delayed_work dwork;
+};
+
+#define WCD9335_SPK_ANC_EN_DELAY_MS 350
+static int spk_anc_en_delay = WCD9335_SPK_ANC_EN_DELAY_MS;
+module_param(spk_anc_en_delay, int, 0664);
+MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path");
+
+struct spk_anc_work {
+	struct tasha_priv *tasha;
+	struct delayed_work dwork;
+};
+
+struct tx_mute_work {
+	struct tasha_priv *tasha;
+	u8 decimator;
+	struct delayed_work dwork;
+};
+
+struct tasha_priv {
+	struct device *dev;
+	struct wcd9xxx *wcd9xxx;
+
+	struct snd_soc_codec *codec;
+	u32 adc_count;
+	u32 rx_bias_count;
+	s32 dmic_0_1_clk_cnt;
+	s32 dmic_2_3_clk_cnt;
+	s32 dmic_4_5_clk_cnt;
+	s32 ldo_h_users;
+	s32 micb_ref[TASHA_MAX_MICBIAS];
+	s32 pullup_ref[TASHA_MAX_MICBIAS];
+
+	u32 anc_slot;
+	bool anc_func;
+
+	/* Vbat module */
+	struct wcd_vbat vbat;
+
+	/* cal info for codec */
+	struct fw_info *fw_data;
+
+	/*track tasha interface type*/
+	u8 intf_type;
+
+	/* num of slim ports required */
+	struct wcd9xxx_codec_dai_data  dai[NUM_CODEC_DAIS];
+
+	/* SoundWire data structure */
+	struct tasha_swr_ctrl_data *swr_ctrl_data;
+	int nr;
+
+	/*compander*/
+	int comp_enabled[COMPANDER_MAX];
+
+	/* Maintain the status of AUX PGA */
+	int aux_pga_cnt;
+	u8 aux_l_gain;
+	u8 aux_r_gain;
+
+	bool spkr_pa_widget_on;
+	struct regulator *spkdrv_reg;
+	struct regulator *spkdrv2_reg;
+
+	bool mbhc_started;
+	/* class h specific data */
+	struct wcd_clsh_cdc_data clsh_d;
+
+	struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
+	/*
+	 * list used to save/restore registers at start and
+	 * end of impedance measurement
+	 */
+	struct list_head reg_save_restore;
+
+	/* handle to cpe core */
+	struct wcd_cpe_core *cpe_core;
+	u32 current_cpe_clk_freq;
+	enum tasha_sido_voltage sido_voltage;
+	int sido_ccl_cnt;
+
+	u32 ana_rx_supplies;
+	/* Multiplication factor used for impedance detection */
+	int zdet_gain_mul_fact;
+
+	/* to track the status */
+	unsigned long status_mask;
+
+	struct work_struct tasha_add_child_devices_work;
+	struct wcd_swr_ctrl_platform_data swr_plat_data;
+
+	/* Port values for Rx and Tx codec_dai */
+	unsigned int rx_port_value[TASHA_RX_MAX];
+	unsigned int tx_port_value;
+
+	unsigned int vi_feed_value;
+	/* Tasha Interpolator Mode Select for EAR, HPH_L and HPH_R */
+	u32 hph_mode;
+
+	u16 prim_int_users[TASHA_NUM_INTERPOLATORS];
+	int spl_src_users[SPLINE_SRC_MAX];
+
+	struct wcd9xxx_resmgr_v2 *resmgr;
+	struct delayed_work power_gate_work;
+	struct mutex power_lock;
+	struct mutex sido_lock;
+
+	/* mbhc module */
+	struct wcd_mbhc mbhc;
+	struct blocking_notifier_head notifier;
+	struct mutex micb_lock;
+
+	struct clk *wcd_ext_clk;
+	struct clk *wcd_native_clk;
+	struct mutex swr_read_lock;
+	struct mutex swr_write_lock;
+	struct mutex swr_clk_lock;
+	int swr_clk_users;
+	int native_clk_users;
+	int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high);
+
+	struct snd_info_entry *entry;
+	struct snd_info_entry *version_entry;
+	int power_active_ref;
+
+	struct on_demand_supply on_demand_list[ON_DEMAND_SUPPLIES_MAX];
+
+	int (*machine_codec_event_cb)(struct snd_soc_codec *codec,
+				      enum wcd9335_codec_event);
+	int spkr_gain_offset;
+	int spkr_mode;
+	int ear_spkr_gain;
+	struct hpf_work tx_hpf_work[TASHA_NUM_DECIMATORS];
+	struct tx_mute_work tx_mute_dwork[TASHA_NUM_DECIMATORS];
+	struct spk_anc_work spk_anc_dwork;
+	struct mutex codec_mutex;
+	int hph_l_gain;
+	int hph_r_gain;
+	int rx_7_count;
+	int rx_8_count;
+	bool clk_mode;
+	bool clk_internal;
+
+	/* Lock to protect mclk enablement */
+	struct mutex mclk_lock;
+};
+
+static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
+				   bool vote);
+
+static const struct tasha_reg_mask_val tasha_spkr_default[] = {
+	{WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+	{WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+	{WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+	{WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+	{WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50},
+	{WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50},
+};
+
+static const struct tasha_reg_mask_val tasha_spkr_mode1[] = {
+	{WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x00},
+	{WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x00},
+	{WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x00},
+	{WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x00},
+	{WCD9335_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44},
+	{WCD9335_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44},
+};
+
+/*
+ * wcd9335_get_codec_info: Get codec specific information
+ *
+ * @wcd9xxx: pointer to wcd9xxx structure
+ * @wcd_type: pointer to wcd9xxx_codec_type structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9335_get_codec_info(struct wcd9xxx *wcd9xxx,
+			   struct wcd9xxx_codec_type *wcd_type)
+{
+	u16 id_minor, id_major;
+	struct regmap *wcd_regmap;
+	int rc, val, version = 0;
+
+	if (!wcd9xxx || !wcd_type)
+		return -EINVAL;
+
+	if (!wcd9xxx->regmap) {
+		dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+			__func__);
+		return -EINVAL;
+	}
+	wcd_regmap = wcd9xxx->regmap;
+
+	rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
+			(u8 *)&id_minor, sizeof(u16));
+	if (rc)
+		return -EINVAL;
+
+	rc = regmap_bulk_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
+			      (u8 *)&id_major, sizeof(u16));
+	if (rc)
+		return -EINVAL;
+
+	dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
+		 __func__, id_major, id_minor);
+
+	/* Version detection */
+	if (id_major == TASHA_MAJOR) {
+		regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0,
+			    &val);
+		version = ((u8)val & 0x80) >> 7;
+	} else if (id_major == TASHA2P0_MAJOR)
+		version = 2;
+	else
+		dev_err(wcd9xxx->dev, "%s: wcd9335 version unknown (major 0x%x, minor 0x%x)\n",
+			__func__, id_major, id_minor);
+
+	/* Fill codec type info */
+	wcd_type->id_major = id_major;
+	wcd_type->id_minor = id_minor;
+	wcd_type->num_irqs = WCD9335_NUM_IRQS;
+	wcd_type->version = version;
+	wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
+	wcd_type->i2c_chip_status = 0x01;
+	wcd_type->intr_tbl = wcd9335_intr_table;
+	wcd_type->intr_tbl_size = ARRAY_SIZE(wcd9335_intr_table);
+
+	wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
+						WCD9335_INTR_PIN1_STATUS0;
+	wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
+						WCD9335_INTR_PIN1_CLEAR0;
+	wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
+						WCD9335_INTR_PIN1_MASK0;
+	wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
+						WCD9335_INTR_LEVEL0;
+	wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
+						WCD9335_INTR_CLR_COMMIT;
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd9335_get_codec_info);
+
+/*
+ * wcd9335_bringdown: Bringdown WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9335_bringdown(struct wcd9xxx *wcd9xxx)
+{
+	if (!wcd9xxx || !wcd9xxx->regmap)
+		return -EINVAL;
+
+	regmap_write(wcd9xxx->regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+		     0x04);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd9335_bringdown);
+
+/*
+ * wcd9335_bringup: Bringup WCD Codec
+ *
+ * @wcd9xxx: Pointer to the wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd9335_bringup(struct wcd9xxx *wcd9xxx)
+{
+	int ret = 0;
+	int val, byte0;
+	struct regmap *wcd_regmap;
+
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	if (!wcd9xxx->regmap) {
+		dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+			__func__);
+		return -EINVAL;
+	}
+	wcd_regmap = wcd9xxx->regmap;
+
+	regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0, &val);
+	regmap_read(wcd_regmap, WCD9335_CHIP_TIER_CTRL_CHIP_ID_BYTE0, &byte0);
+
+	if ((val < 0) || (byte0 < 0)) {
+		dev_err(wcd9xxx->dev, "%s: tasha codec version detection fail!\n",
+			__func__);
+		return -EINVAL;
+	}
+	if ((val & 0x80) && (byte0 == 0x0)) {
+		dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.1\n",
+			 __func__);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+		regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC);
+		regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x5);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x7);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x3);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+	} else if (byte0 == 0x1) {
+		dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v2.0\n",
+			 __func__);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+		regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_TEST_2, 0x00);
+		regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_8, 0x6F);
+		regmap_write(wcd_regmap, WCD9335_BIAS_VBG_FINE_ADJ, 0x65);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x5);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x7);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x3);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+	} else if ((byte0 == 0) && (!(val & 0x80))) {
+		dev_info(wcd9xxx->dev, "%s: wcd9335 codec version is v1.0\n",
+			 __func__);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x01);
+		regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_2, 0xFC);
+		regmap_write(wcd_regmap, WCD9335_SIDO_SIDO_CCL_4, 0x21);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			     0x3);
+		regmap_write(wcd_regmap, WCD9335_CODEC_RPM_RST_CTL, 0x3);
+	} else {
+		dev_err(wcd9xxx->dev, "%s: tasha codec version unknown\n",
+			__func__);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9335_bringup);
+
+/**
+ * tasha_set_spkr_gain_offset - offset the speaker path
+ * gain with the given offset value.
+ *
+ * @codec: codec instance
+ * @offset: Indicates speaker path gain offset value.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset)
+{
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (!priv)
+		return -EINVAL;
+
+	priv->spkr_gain_offset = offset;
+	return 0;
+}
+EXPORT_SYMBOL(tasha_set_spkr_gain_offset);
+
+/**
+ * tasha_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @codec: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode)
+{
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+	int i;
+	const struct tasha_reg_mask_val *regs;
+	int size;
+
+	if (!priv)
+		return -EINVAL;
+
+	switch (mode) {
+	case SPKR_MODE_1:
+		regs = tasha_spkr_mode1;
+		size = ARRAY_SIZE(tasha_spkr_mode1);
+		break;
+	default:
+		regs = tasha_spkr_default;
+		size = ARRAY_SIZE(tasha_spkr_default);
+		break;
+	}
+
+	priv->spkr_mode = mode;
+	for (i = 0; i < size; i++)
+		snd_soc_update_bits(codec, regs[i].reg,
+				    regs[i].mask, regs[i].val);
+	return 0;
+}
+EXPORT_SYMBOL(tasha_set_spkr_mode);
+
+static void tasha_enable_sido_buck(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_update_bits(codec, WCD9335_ANA_RCO, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x02, 0x02);
+	/* 100us sleep needed after IREF settings */
+	usleep_range(100, 110);
+	snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x04, 0x04);
+	/* 100us sleep needed after VREF settings */
+	usleep_range(100, 110);
+	tasha->resmgr->sido_input_src = SIDO_SOURCE_RCO_BG;
+}
+
+static void tasha_cdc_sido_ccl_enable(struct tasha_priv *tasha, bool ccl_flag)
+{
+	struct snd_soc_codec *codec = tasha->codec;
+
+	if (!codec)
+		return;
+
+	if (!TASHA_IS_2_0(tasha->wcd9xxx)) {
+		dev_dbg(codec->dev, "%s: tasha version < 2p0, return\n",
+			__func__);
+		return;
+	}
+	dev_dbg(codec->dev, "%s: sido_ccl_cnt=%d, ccl_flag:%d\n",
+			__func__, tasha->sido_ccl_cnt, ccl_flag);
+	if (ccl_flag) {
+		if (++tasha->sido_ccl_cnt == 1)
+			snd_soc_update_bits(codec,
+				WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x6E);
+	} else {
+		if (tasha->sido_ccl_cnt == 0) {
+			dev_dbg(codec->dev, "%s: sido_ccl already disabled\n",
+				__func__);
+			return;
+		}
+		if (--tasha->sido_ccl_cnt == 0)
+			snd_soc_update_bits(codec,
+				WCD9335_SIDO_SIDO_CCL_10, 0xFF, 0x02);
+	}
+}
+
+static bool tasha_cdc_is_svs_enabled(struct tasha_priv *tasha)
+{
+	if (TASHA_IS_2_0(tasha->wcd9xxx) &&
+		svs_scaling_enabled)
+		return true;
+
+	return false;
+}
+
+static int tasha_cdc_req_mclk_enable(struct tasha_priv *tasha,
+				     bool enable)
+{
+	int ret = 0;
+
+	mutex_lock(&tasha->mclk_lock);
+	if (enable) {
+		tasha_cdc_sido_ccl_enable(tasha, true);
+		ret = clk_prepare_enable(tasha->wcd_ext_clk);
+		if (ret) {
+			dev_err(tasha->dev, "%s: ext clk enable failed\n",
+				__func__);
+			goto unlock_mutex;
+		}
+		/* get BG */
+		wcd_resmgr_enable_master_bias(tasha->resmgr);
+		/* get MCLK */
+		wcd_resmgr_enable_clk_block(tasha->resmgr, WCD_CLK_MCLK);
+	} else {
+		/* put MCLK */
+		wcd_resmgr_disable_clk_block(tasha->resmgr, WCD_CLK_MCLK);
+		/* put BG */
+		wcd_resmgr_disable_master_bias(tasha->resmgr);
+		clk_disable_unprepare(tasha->wcd_ext_clk);
+		tasha_cdc_sido_ccl_enable(tasha, false);
+	}
+unlock_mutex:
+	mutex_unlock(&tasha->mclk_lock);
+	return ret;
+}
+
+static int tasha_cdc_check_sido_value(enum tasha_sido_voltage req_mv)
+{
+	if ((req_mv != SIDO_VOLTAGE_SVS_MV) &&
+		(req_mv != SIDO_VOLTAGE_NOMINAL_MV))
+		return -EINVAL;
+
+	return 0;
+}
+
+static void tasha_codec_apply_sido_voltage(
+				struct tasha_priv *tasha,
+				enum tasha_sido_voltage req_mv)
+{
+	u32 vout_d_val;
+	struct snd_soc_codec *codec = tasha->codec;
+	int ret;
+
+	if (!codec)
+		return;
+
+	if (!tasha_cdc_is_svs_enabled(tasha))
+		return;
+
+	if ((sido_buck_svs_voltage != SIDO_VOLTAGE_SVS_MV) &&
+		(sido_buck_svs_voltage != SIDO_VOLTAGE_NOMINAL_MV))
+		sido_buck_svs_voltage = SIDO_VOLTAGE_SVS_MV;
+
+	ret = tasha_cdc_check_sido_value(req_mv);
+	if (ret < 0) {
+		dev_dbg(codec->dev, "%s: requested mv=%d not in range\n",
+			__func__, req_mv);
+		return;
+	}
+	if (req_mv == tasha->sido_voltage) {
+		dev_dbg(codec->dev, "%s: Already at requested mv=%d\n",
+			__func__, req_mv);
+		return;
+	}
+	if (req_mv == sido_buck_svs_voltage) {
+		if (test_bit(AUDIO_NOMINAL, &tasha->status_mask) ||
+			test_bit(CPE_NOMINAL, &tasha->status_mask)) {
+			dev_dbg(codec->dev,
+				"%s: nominal client running, status_mask=%lu\n",
+				__func__, tasha->status_mask);
+			return;
+		}
+	}
+	/* compute the vout_d step value */
+	vout_d_val = CALCULATE_VOUT_D(req_mv);
+	snd_soc_write(codec, WCD9335_ANA_BUCK_VOUT_D, vout_d_val & 0xFF);
+	snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL, 0x80, 0x80);
+
+	/* 1 msec sleep required after SIDO Vout_D voltage change */
+	usleep_range(1000, 1100);
+	tasha->sido_voltage = req_mv;
+	dev_dbg(codec->dev,
+		"%s: updated SIDO buck Vout_D to %d, vout_d step = %u\n",
+		__func__, tasha->sido_voltage, vout_d_val);
+
+	snd_soc_update_bits(codec, WCD9335_ANA_BUCK_CTL,
+				0x80, 0x00);
+}
+
+static int tasha_codec_update_sido_voltage(
+				struct tasha_priv *tasha,
+				enum tasha_sido_voltage req_mv)
+{
+	int ret = 0;
+
+	if (!tasha_cdc_is_svs_enabled(tasha))
+		return ret;
+
+	mutex_lock(&tasha->sido_lock);
+	/* enable mclk before setting SIDO voltage */
+	ret = tasha_cdc_req_mclk_enable(tasha, true);
+	if (ret) {
+		dev_err(tasha->dev, "%s: ext clk enable failed\n",
+			__func__);
+		goto err;
+	}
+	tasha_codec_apply_sido_voltage(tasha, req_mv);
+	tasha_cdc_req_mclk_enable(tasha, false);
+
+err:
+	mutex_unlock(&tasha->sido_lock);
+	return ret;
+}
+
+int tasha_enable_efuse_sensing(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	tasha_cdc_mclk_enable(codec, true, false);
+
+	if (!TASHA_IS_2_0(priv->wcd9xxx))
+		snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
+				    0x1E, 0x02);
+	snd_soc_update_bits(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_CTL,
+			    0x01, 0x01);
+	/*
+	 * 5ms sleep required after enabling efuse control
+	 * before checking the status.
+	 */
+	usleep_range(5000, 5500);
+	if (!(snd_soc_read(codec, WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS) & 0x01))
+		WARN(1, "%s: Efuse sense is not complete\n", __func__);
+
+	if (TASHA_IS_2_0(priv->wcd9xxx)) {
+		if (!(snd_soc_read(codec,
+			WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT0) & 0x40))
+			snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST,
+					    0x04, 0x00);
+		tasha_enable_sido_buck(codec);
+	}
+
+	tasha_cdc_mclk_enable(codec, false, false);
+
+	return 0;
+}
+EXPORT_SYMBOL(tasha_enable_efuse_sensing);
+
+void *tasha_get_afe_config(struct snd_soc_codec *codec,
+			   enum afe_config_type config_type)
+{
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (config_type) {
+	case AFE_SLIMBUS_SLAVE_CONFIG:
+		return &priv->slimbus_slave_cfg;
+	case AFE_CDC_REGISTERS_CONFIG:
+		return &tasha_audio_reg_cfg;
+	case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+		return &tasha_slimbus_slave_port_cfg;
+	case AFE_AANC_VERSION:
+		return &tasha_cdc_aanc_version;
+	case AFE_CLIP_BANK_SEL:
+		return NULL;
+	case AFE_CDC_CLIP_REGISTERS_CONFIG:
+		return NULL;
+	case AFE_CDC_REGISTER_PAGE_CONFIG:
+		return &tasha_cdc_reg_page_cfg;
+	default:
+		dev_err(codec->dev, "%s: Unknown config_type 0x%x\n",
+			__func__, config_type);
+		return NULL;
+	}
+}
+EXPORT_SYMBOL(tasha_get_afe_config);
+
+/*
+ * tasha_event_register: Registers a machine driver callback
+ * function with codec private data for post ADSP sub-system
+ * restart (SSR). This callback function will be called from
+ * codec driver once codec comes out of reset after ADSP SSR.
+ *
+ * @machine_event_cb: callback function from machine driver
+ * @codec: Codec instance
+ *
+ * Return: none
+ */
+void tasha_event_register(
+	int (*machine_event_cb)(struct snd_soc_codec *codec,
+				enum wcd9335_codec_event),
+	struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (tasha)
+		tasha->machine_codec_event_cb = machine_event_cb;
+	else
+		dev_dbg(codec->dev, "%s: Invalid tasha_priv data\n", __func__);
+}
+EXPORT_SYMBOL(tasha_event_register);
+
+static int tasha_mbhc_request_irq(struct snd_soc_codec *codec,
+				   int irq, irq_handler_t handler,
+				   const char *name, void *data)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	return wcd9xxx_request_irq(core_res, irq, handler, name, data);
+}
+
+static void tasha_mbhc_irq_control(struct snd_soc_codec *codec,
+				   int irq, bool enable)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+	if (enable)
+		wcd9xxx_enable_irq(core_res, irq);
+	else
+		wcd9xxx_disable_irq(core_res, irq);
+}
+
+static int tasha_mbhc_free_irq(struct snd_soc_codec *codec,
+			       int irq, void *data)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	wcd9xxx_free_irq(core_res, irq, data);
+	return 0;
+}
+
+static void tasha_mbhc_clk_setup(struct snd_soc_codec *codec,
+				 bool enable)
+{
+	if (enable)
+		snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1,
+				    0x80, 0x80);
+	else
+		snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1,
+				    0x80, 0x00);
+}
+
+static int tasha_mbhc_btn_to_num(struct snd_soc_codec *codec)
+{
+	return snd_soc_read(codec, WCD9335_ANA_MBHC_RESULT_3) & 0x7;
+}
+
+static void tasha_mbhc_mbhc_bias_control(struct snd_soc_codec *codec,
+					 bool enable)
+{
+	if (enable)
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT,
+				    0x01, 0x01);
+	else
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_ELECT,
+				    0x01, 0x00);
+}
+
+static void tasha_mbhc_program_btn_thr(struct snd_soc_codec *codec,
+				       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(codec->dev, "%s: invalid number of buttons: %d\n",
+			__func__, num_btn);
+		return;
+	}
+	/*
+	 * Tasha just needs one set of thresholds for button detection
+	 * due to micbias voltage ramp to pullup upon button press. So
+	 * btn_low and is_micbias are ignored and always program button
+	 * thresholds using btn_high.
+	 */
+	for (i = 0; i < num_btn; i++) {
+		vth = ((btn_high[i] * 2) / 25) & 0x3F;
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN0 + i,
+				    0xFC, vth << 2);
+		dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+			__func__, i, btn_high[i], vth);
+	}
+}
+
+static bool tasha_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+	if (lock)
+		return wcd9xxx_lock_sleep(core_res);
+	else {
+		wcd9xxx_unlock_sleep(core_res);
+		return 0;
+	}
+}
+
+static int tasha_mbhc_register_notifier(struct wcd_mbhc *mbhc,
+					struct notifier_block *nblock,
+					bool enable)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (enable)
+		return blocking_notifier_chain_register(&tasha->notifier,
+							nblock);
+	else
+		return blocking_notifier_chain_unregister(&tasha->notifier,
+							  nblock);
+}
+
+static bool tasha_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+	u8 val;
+
+	if (micb_num == MIC_BIAS_2) {
+		val = (snd_soc_read(mbhc->codec, WCD9335_ANA_MICB2) >> 6);
+		if (val == 0x01)
+			return true;
+	}
+	return false;
+}
+
+static bool tasha_mbhc_hph_pa_on_status(struct snd_soc_codec *codec)
+{
+	return (snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0) ? true : false;
+}
+
+static void tasha_mbhc_hph_l_pull_up_control(struct snd_soc_codec *codec,
+					enum mbhc_hs_pullup_iref pull_up_cur)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (!tasha)
+		return;
+
+	/* 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(codec->dev, "%s: HS pull up current:%d\n",
+		__func__, pull_up_cur);
+
+	if (TASHA_IS_2_0(tasha->wcd9xxx))
+		snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL,
+			    0xC0, pull_up_cur << 6);
+	else
+		snd_soc_update_bits(codec, WCD9335_MBHC_PLUG_DETECT_CTL,
+			    0xC0, 0x40);
+}
+
+static int tasha_enable_ext_mb_source(struct wcd_mbhc *mbhc,
+		bool turn_on)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	struct on_demand_supply *supply;
+
+	if (!tasha)
+		return -EINVAL;
+
+	supply =  &tasha->on_demand_list[ON_DEMAND_MICBIAS];
+	if (!supply->supply) {
+		dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n",
+				__func__, "onDemand Micbias");
+		return ret;
+	}
+
+	dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on,
+		supply->ondemand_supply_count);
+
+	if (turn_on) {
+		if (!(supply->ondemand_supply_count)) {
+			ret = snd_soc_dapm_force_enable_pin(
+				snd_soc_codec_get_dapm(codec),
+				"MICBIAS_REGULATOR");
+			snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+		}
+		supply->ondemand_supply_count++;
+	} else {
+		if (supply->ondemand_supply_count > 0)
+			supply->ondemand_supply_count--;
+		if (!(supply->ondemand_supply_count)) {
+			ret = snd_soc_dapm_disable_pin(
+				snd_soc_codec_get_dapm(codec),
+				"MICBIAS_REGULATOR");
+		snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+		}
+	}
+
+	if (ret)
+		dev_err(codec->dev, "%s: Failed to %s external micbias source\n",
+			__func__, turn_on ? "enable" : "disabled");
+	else
+		dev_dbg(codec->dev, "%s: %s external micbias source\n",
+			__func__, turn_on ? "Enabled" : "Disabled");
+
+	return ret;
+}
+
+static int tasha_micbias_control(struct snd_soc_codec *codec,
+				 int micb_num,
+				 int req, bool is_dapm)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	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;
+
+	if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) {
+		dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+			__func__, micb_index);
+		return -EINVAL;
+	}
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_reg = WCD9335_ANA_MICB1;
+		break;
+	case MIC_BIAS_2:
+		micb_reg = WCD9335_ANA_MICB2;
+		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 = WCD9335_ANA_MICB3;
+		break;
+	case MIC_BIAS_4:
+		micb_reg = WCD9335_ANA_MICB4;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid micbias number: %d\n",
+			__func__, micb_num);
+		return -EINVAL;
+	}
+	mutex_lock(&tasha->micb_lock);
+
+	switch (req) {
+	case MICB_PULLUP_ENABLE:
+		tasha->pullup_ref[micb_index]++;
+		if ((tasha->pullup_ref[micb_index] == 1) &&
+		    (tasha->micb_ref[micb_index] == 0))
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+		break;
+	case MICB_PULLUP_DISABLE:
+		if (tasha->pullup_ref[micb_index] > 0)
+			tasha->pullup_ref[micb_index]--;
+		if ((tasha->pullup_ref[micb_index] == 0) &&
+		    (tasha->micb_ref[micb_index] == 0))
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+		break;
+	case MICB_ENABLE:
+		tasha->micb_ref[micb_index]++;
+		if (tasha->micb_ref[micb_index] == 1) {
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+			if (post_on_event)
+				blocking_notifier_call_chain(&tasha->notifier,
+						post_on_event, &tasha->mbhc);
+		}
+		if (is_dapm && post_dapm_on)
+			blocking_notifier_call_chain(&tasha->notifier,
+					post_dapm_on, &tasha->mbhc);
+		break;
+	case MICB_DISABLE:
+		if (tasha->micb_ref[micb_index] > 0)
+			tasha->micb_ref[micb_index]--;
+		if ((tasha->micb_ref[micb_index] == 0) &&
+		    (tasha->pullup_ref[micb_index] > 0))
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+		else if ((tasha->micb_ref[micb_index] == 0) &&
+			 (tasha->pullup_ref[micb_index] == 0)) {
+			if (pre_off_event)
+				blocking_notifier_call_chain(&tasha->notifier,
+						pre_off_event, &tasha->mbhc);
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+			if (post_off_event)
+				blocking_notifier_call_chain(&tasha->notifier,
+						post_off_event, &tasha->mbhc);
+		}
+		if (is_dapm && post_dapm_off)
+			blocking_notifier_call_chain(&tasha->notifier,
+					post_dapm_off, &tasha->mbhc);
+		break;
+	};
+
+	dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n",
+		__func__, micb_num, tasha->micb_ref[micb_index],
+		tasha->pullup_ref[micb_index]);
+
+	mutex_unlock(&tasha->micb_lock);
+
+	return 0;
+}
+
+static int tasha_mbhc_request_micbias(struct snd_soc_codec *codec,
+				      int micb_num, int req)
+{
+	int ret;
+
+	/*
+	 * If micbias is requested, make sure that there
+	 * is vote to enable mclk
+	 */
+	if (req == MICB_ENABLE)
+		tasha_cdc_mclk_enable(codec, true, false);
+
+	ret = tasha_micbias_control(codec, micb_num, req, false);
+
+	/*
+	 * Release vote for mclk while requesting for
+	 * micbias disable
+	 */
+	if (req == MICB_DISABLE)
+		tasha_cdc_mclk_enable(codec, false, false);
+
+	return ret;
+}
+
+static void tasha_mbhc_micb_ramp_control(struct snd_soc_codec *codec,
+					bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+				    0x1C, 0x0C);
+		snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+				    0x80, 0x80);
+	} else {
+		snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+				    0x80, 0x00);
+		snd_soc_update_bits(codec, WCD9335_ANA_MICB2_RAMP,
+				    0x1C, 0x00);
+	}
+}
+
+static struct firmware_cal *tasha_get_hwdep_fw_cal(struct wcd_mbhc *mbhc,
+						   enum wcd_cal_type type)
+{
+	struct tasha_priv *tasha;
+	struct firmware_cal *hwdep_cal;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer\n", __func__);
+		return NULL;
+	}
+	tasha = snd_soc_codec_get_drvdata(codec);
+	hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, type);
+	if (!hwdep_cal)
+		dev_err(codec->dev, "%s: cal not sent by %d\n",
+			__func__, type);
+
+	return hwdep_cal;
+}
+
+static int tasha_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
+					  int req_volt,
+					  int micb_num)
+{
+	int cur_vout_ctl, req_vout_ctl;
+	int micb_reg, micb_val, micb_en;
+
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_reg = WCD9335_ANA_MICB1;
+		break;
+	case MIC_BIAS_2:
+		micb_reg = WCD9335_ANA_MICB2;
+		break;
+	case MIC_BIAS_3:
+		micb_reg = WCD9335_ANA_MICB3;
+		break;
+	case MIC_BIAS_4:
+		micb_reg = WCD9335_ANA_MICB4;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/*
+	 * 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.
+	 */
+	micb_val = snd_soc_read(codec, micb_reg);
+	micb_en = (micb_val & 0xC0) >> 6;
+	cur_vout_ctl = micb_val & 0x3F;
+
+	req_vout_ctl = wcd9335_get_micb_vout_ctl_val(req_volt);
+	if (req_vout_ctl < 0)
+		return -EINVAL;
+	if (cur_vout_ctl == req_vout_ctl)
+		return 0;
+
+	dev_dbg(codec->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_update_bits(codec, micb_reg, 0xC0, 0x80);
+
+	snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl);
+
+	if (micb_en == 0x1) {
+		snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+		/*
+		 * Add 2ms delay as per HW requirement after enabling
+		 * micbias
+		 */
+		usleep_range(2000, 2100);
+	}
+
+	return 0;
+}
+
+static int tasha_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec,
+					      int micb_num, bool req_en)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+	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;
+
+	mutex_lock(&tasha->micb_lock);
+	rc = tasha_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2);
+	mutex_unlock(&tasha->micb_lock);
+
+	return rc;
+}
+
+static inline void tasha_mbhc_get_result_params(struct wcd9xxx *wcd9xxx,
+						s16 *d1_a, u16 noff,
+						int32_t *zdet)
+{
+	int i;
+	int val, val1;
+	s16 c1;
+	s32 x1, d1;
+	int32_t denom;
+	int minCode_param[] = {
+			3277, 1639, 820, 410, 205, 103, 52, 26
+	};
+
+	regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x20);
+	for (i = 0; i < TASHA_ZDET_NUM_MEASUREMENTS; i++) {
+		regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_2, &val);
+		if (val & 0x80)
+			break;
+	}
+	val = val << 0x8;
+	regmap_read(wcd9xxx->regmap, WCD9335_ANA_MBHC_RESULT_1, &val1);
+	val |= val1;
+	regmap_update_bits(wcd9xxx->regmap, WCD9335_ANA_MBHC_ZDET, 0x20, 0x00);
+	x1 = TASHA_MBHC_GET_X1(val);
+	c1 = TASHA_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(wcd9xxx->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 = (TASHA_MBHC_ZDET_CONST * 1000) / denom;
+	else if (x1 < minCode_param[noff])
+		*zdet = TASHA_ZDET_FLOATING_IMPEDANCE;
+
+	dev_dbg(wcd9xxx->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_bulk_read(wcd9xxx->regmap,
+				 WCD9335_ANA_MBHC_RESULT_1, (u8 *)&val, 2);
+		x1 = TASHA_MBHC_GET_X1(val);
+		i++;
+		if (i == TASHA_ZDET_NUM_MEASUREMENTS)
+			break;
+	}
+}
+
+/*
+ * tasha_mbhc_zdet_gpio_ctrl: Register callback function for
+ * controlling the switch on hifi amps. Default switch state
+ * will put a 51ohm load in parallel to the hph load. So,
+ * impedance detection function will pull the gpio high
+ * to make the switch open.
+ *
+ * @zdet_gpio_cb: callback function from machine driver
+ * @codec: Codec instance
+ *
+ * Return: none
+ */
+void tasha_mbhc_zdet_gpio_ctrl(
+		int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high),
+		struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	tasha->zdet_gpio_cb = zdet_gpio_cb;
+}
+EXPORT_SYMBOL(tasha_mbhc_zdet_gpio_ctrl);
+
+static void tasha_mbhc_zdet_ramp(struct snd_soc_codec *codec,
+				 struct tasha_mbhc_zdet_param *zdet_param,
+				 int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int32_t zdet = 0;
+
+	snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x70,
+			    zdet_param->ldo_ctl << 4);
+	snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN5, 0xFC,
+			    zdet_param->btn5);
+	snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN6, 0xFC,
+			    zdet_param->btn6);
+	snd_soc_update_bits(codec, WCD9335_ANA_MBHC_BTN7, 0xFC,
+			    zdet_param->btn7);
+	snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_ANA_CTL, 0x0F,
+			    zdet_param->noff);
+	snd_soc_update_bits(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x0F,
+			    zdet_param->nshift);
+
+	if (!zl)
+		goto z_right;
+	/* Start impedance measurement for HPH_L */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD9335_ANA_MBHC_ZDET, 0x80, 0x80);
+	dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n",
+					__func__, zdet_param->noff);
+	tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD9335_ANA_MBHC_ZDET, 0x80, 0x00);
+
+	*zl = zdet;
+
+z_right:
+	if (!zr)
+		return;
+	/* Start impedance measurement for HPH_R */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD9335_ANA_MBHC_ZDET, 0x40, 0x40);
+	dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n",
+					__func__, zdet_param->noff);
+	tasha_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD9335_ANA_MBHC_ZDET, 0x40, 0x00);
+
+	*zr = zdet;
+}
+
+static inline void tasha_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec,
+					int32_t *z_val, int flag_l_r)
+{
+	s16 q1;
+	int q1_cal;
+
+	if (*z_val < (TASHA_ZDET_VAL_400/1000))
+		q1 = snd_soc_read(codec,
+			WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r));
+	else
+		q1 = snd_soc_read(codec,
+			WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (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 tasha_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+					  uint32_t *zr)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	s16 reg0, reg1, reg2, reg3, reg4;
+	int32_t z1L, z1R, z1Ls;
+	int zMono, z_diff1, z_diff2;
+	bool is_fsm_disable = false;
+	bool is_change = false;
+	struct tasha_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 tasha_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;
+
+	if (!TASHA_IS_2_0(wcd9xxx)) {
+		dev_dbg(codec->dev, "%s: Z-det is not supported for this codec version\n",
+					__func__);
+		*zl = 0;
+		*zr = 0;
+		return;
+	}
+	WCD_MBHC_RSC_ASSERT_LOCKED(mbhc);
+
+	if (tasha->zdet_gpio_cb)
+		is_change = tasha->zdet_gpio_cb(codec, true);
+
+	reg0 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN5);
+	reg1 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN6);
+	reg2 = snd_soc_read(codec, WCD9335_ANA_MBHC_BTN7);
+	reg3 = snd_soc_read(codec, WCD9335_MBHC_CTL_1);
+	reg4 = snd_soc_read(codec, WCD9335_MBHC_ZDET_ANA_CTL);
+
+	if (snd_soc_read(codec, WCD9335_ANA_MBHC_ELECT) & 0x80) {
+		is_fsm_disable = true;
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD9335_ANA_MBHC_ELECT, 0x80, 0x00);
+	}
+
+	/* For NO-jack, disable L_DET_EN before Z-det measurements */
+	if (mbhc->hphl_swh)
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD9335_ANA_MBHC_MECH, 0x80, 0x00);
+
+	/* Enable AZ */
+	snd_soc_update_bits(codec, WCD9335_MBHC_CTL_1, 0x0C, 0x04);
+	/* Turn off 100k pull down on HPHL */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD9335_ANA_MBHC_MECH, 0x01, 0x00);
+
+	/* First get impedance on Left */
+	d1 = d1_a[1];
+	zdet_param_ptr = &zdet_param[1];
+	tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+	if (!TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+		goto left_ch_impedance;
+
+	/* second ramp for left ch */
+	if (z1L < TASHA_ZDET_VAL_32) {
+		zdet_param_ptr = &zdet_param[0];
+		d1 = d1_a[0];
+	} else if ((z1L > TASHA_ZDET_VAL_400) && (z1L <= TASHA_ZDET_VAL_1200)) {
+		zdet_param_ptr = &zdet_param[2];
+		d1 = d1_a[2];
+	} else if (z1L > TASHA_ZDET_VAL_1200) {
+		zdet_param_ptr = &zdet_param[3];
+		d1 = d1_a[3];
+	}
+	tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+	if ((z1L == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+		(z1L > TASHA_ZDET_VAL_100K)) {
+		*zl = TASHA_ZDET_FLOATING_IMPEDANCE;
+		zdet_param_ptr = &zdet_param[1];
+		d1 = d1_a[1];
+	} else {
+		*zl = z1L/1000;
+		tasha_wcd_mbhc_qfuse_cal(codec, zl, 0);
+	}
+	dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+				__func__, *zl);
+
+	/* start of right impedance ramp and calculation */
+	tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+	if (TASHA_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+		if (((z1R > TASHA_ZDET_VAL_1200) &&
+			(zdet_param_ptr->noff == 0x6)) ||
+			((*zl) != TASHA_ZDET_FLOATING_IMPEDANCE))
+			goto right_ch_impedance;
+		/* second ramp for right ch */
+		if (z1R < TASHA_ZDET_VAL_32) {
+			zdet_param_ptr = &zdet_param[0];
+			d1 = d1_a[0];
+		} else if ((z1R > TASHA_ZDET_VAL_400) &&
+			(z1R <= TASHA_ZDET_VAL_1200)) {
+			zdet_param_ptr = &zdet_param[2];
+			d1 = d1_a[2];
+		} else if (z1R > TASHA_ZDET_VAL_1200) {
+			zdet_param_ptr = &zdet_param[3];
+			d1 = d1_a[3];
+		}
+		tasha_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+	}
+right_ch_impedance:
+	if ((z1R == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+		(z1R > TASHA_ZDET_VAL_100K)) {
+		*zr = TASHA_ZDET_FLOATING_IMPEDANCE;
+	} else {
+		*zr = z1R/1000;
+		tasha_wcd_mbhc_qfuse_cal(codec, zr, 1);
+	}
+	dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+				__func__, *zr);
+
+	/* mono/stereo detection */
+	if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) &&
+		(*zr == TASHA_ZDET_FLOATING_IMPEDANCE)) {
+		dev_dbg(codec->dev,
+			"%s: plug type is invalid or extension cable\n",
+			__func__);
+		goto zdet_complete;
+	}
+	if ((*zl == TASHA_ZDET_FLOATING_IMPEDANCE) ||
+	    (*zr == TASHA_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(codec->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_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x02);
+	snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x01);
+	if (*zl < (TASHA_ZDET_VAL_32/1000))
+		tasha_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1);
+	else
+		tasha_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1);
+	snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x40, 0x00);
+	snd_soc_update_bits(codec, WCD9335_HPH_R_ATEST, 0x02, 0x00);
+	z1Ls /= 1000;
+	tasha_wcd_mbhc_qfuse_cal(codec, &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(codec->dev, "%s: stereo plug type detected\n",
+				__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+	} else {
+		dev_dbg(codec->dev, "%s: MONO plug type detected\n",
+			 __func__);
+		mbhc->hph_type = WCD_MBHC_HPH_MONO;
+	}
+
+zdet_complete:
+	snd_soc_write(codec, WCD9335_ANA_MBHC_BTN5, reg0);
+	snd_soc_write(codec, WCD9335_ANA_MBHC_BTN6, reg1);
+	snd_soc_write(codec, WCD9335_ANA_MBHC_BTN7, reg2);
+	/* Turn on 100k pull down on HPHL */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD9335_ANA_MBHC_MECH, 0x01, 0x01);
+
+	/* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+	if (mbhc->hphl_swh)
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD9335_ANA_MBHC_MECH, 0x80, 0x80);
+
+	snd_soc_write(codec, WCD9335_MBHC_ZDET_ANA_CTL, reg4);
+	snd_soc_write(codec, WCD9335_MBHC_CTL_1, reg3);
+	if (is_fsm_disable)
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD9335_ANA_MBHC_ELECT, 0x80, 0x80);
+	if (tasha->zdet_gpio_cb && is_change)
+		tasha->zdet_gpio_cb(codec, false);
+}
+
+static void tasha_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+				    0x02, 0x02);
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+				    0x40, 0x40);
+	} else {
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+				    0x40, 0x00);
+		snd_soc_update_bits(codec, WCD9335_ANA_MBHC_MECH,
+				    0x02, 0x00);
+	}
+}
+
+static void tasha_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec,
+					  bool enable)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+				    0x40, 0x40);
+		if (TASHA_IS_2_0(tasha->wcd9xxx))
+			snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+					    0x10, 0x10);
+	} else {
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+				    0x40, 0x00);
+		if (TASHA_IS_2_0(tasha->wcd9xxx))
+			snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2,
+					    0x10, 0x00);
+	}
+}
+
+static void tasha_mbhc_moisture_config(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	if (TASHA_MBHC_MOISTURE_VREF == V_OFF)
+		return;
+
+	/* Donot enable moisture detection if jack type is NC */
+	if (!mbhc->hphl_swh) {
+		dev_dbg(codec->dev, "%s: disable moisture detection for NC\n",
+			__func__);
+		return;
+	}
+
+	snd_soc_update_bits(codec, WCD9335_MBHC_CTL_2,
+			    0x0C, TASHA_MBHC_MOISTURE_VREF << 2);
+	tasha_mbhc_hph_l_pull_up_control(codec, TASHA_MBHC_MOISTURE_IREF);
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+	.request_irq = tasha_mbhc_request_irq,
+	.irq_control = tasha_mbhc_irq_control,
+	.free_irq = tasha_mbhc_free_irq,
+	.clk_setup = tasha_mbhc_clk_setup,
+	.map_btn_code_to_num = tasha_mbhc_btn_to_num,
+	.enable_mb_source = tasha_enable_ext_mb_source,
+	.mbhc_bias = tasha_mbhc_mbhc_bias_control,
+	.set_btn_thr = tasha_mbhc_program_btn_thr,
+	.lock_sleep = tasha_mbhc_lock_sleep,
+	.register_notifier = tasha_mbhc_register_notifier,
+	.micbias_enable_status = tasha_mbhc_micb_en_status,
+	.hph_pa_on_status = tasha_mbhc_hph_pa_on_status,
+	.hph_pull_up_control = tasha_mbhc_hph_l_pull_up_control,
+	.mbhc_micbias_control = tasha_mbhc_request_micbias,
+	.mbhc_micb_ramp_control = tasha_mbhc_micb_ramp_control,
+	.get_hwdep_fw_cal = tasha_get_hwdep_fw_cal,
+	.mbhc_micb_ctrl_thr_mic = tasha_mbhc_micb_ctrl_threshold_mic,
+	.compute_impedance = tasha_wcd_mbhc_calc_impedance,
+	.mbhc_gnd_det_ctrl = tasha_mbhc_gnd_det_ctrl,
+	.hph_pull_down_ctrl = tasha_mbhc_hph_pull_down_ctrl,
+	.mbhc_moisture_config = tasha_mbhc_moisture_config,
+};
+
+static int tasha_get_anc_slot(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tasha->anc_slot;
+	return 0;
+}
+
+static int tasha_put_anc_slot(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	tasha->anc_slot = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int tasha_get_anc_func(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = (tasha->anc_func == true ? 1 : 0);
+	return 0;
+}
+
+static int tasha_put_anc_func(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	mutex_lock(&tasha->codec_mutex);
+	tasha->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+
+	dev_dbg(codec->dev, "%s: anc_func %x", __func__, tasha->anc_func);
+
+	if (tasha->anc_func == true) {
+		snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2 PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT2");
+		snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1 PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC LINEOUT1");
+		snd_soc_dapm_enable_pin(dapm, "ANC HPHR PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC HPHR");
+		snd_soc_dapm_enable_pin(dapm, "ANC HPHL PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC HPHL");
+		snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+		snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+		snd_soc_dapm_disable_pin(dapm, "LINEOUT2");
+		snd_soc_dapm_disable_pin(dapm, "LINEOUT2 PA");
+		snd_soc_dapm_disable_pin(dapm, "LINEOUT1");
+		snd_soc_dapm_disable_pin(dapm, "LINEOUT1 PA");
+		snd_soc_dapm_disable_pin(dapm, "HPHR");
+		snd_soc_dapm_disable_pin(dapm, "HPHL");
+		snd_soc_dapm_disable_pin(dapm, "HPHR PA");
+		snd_soc_dapm_disable_pin(dapm, "HPHL PA");
+		snd_soc_dapm_disable_pin(dapm, "EAR PA");
+		snd_soc_dapm_disable_pin(dapm, "EAR");
+	} else {
+		snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2");
+		snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1");
+		snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+		snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+		snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+		snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+		snd_soc_dapm_enable_pin(dapm, "LINEOUT2");
+		snd_soc_dapm_enable_pin(dapm, "LINEOUT2 PA");
+		snd_soc_dapm_enable_pin(dapm, "LINEOUT1");
+		snd_soc_dapm_enable_pin(dapm, "LINEOUT1 PA");
+		snd_soc_dapm_enable_pin(dapm, "HPHR");
+		snd_soc_dapm_enable_pin(dapm, "HPHL");
+		snd_soc_dapm_enable_pin(dapm, "HPHR PA");
+		snd_soc_dapm_enable_pin(dapm, "HPHL PA");
+		snd_soc_dapm_enable_pin(dapm, "EAR PA");
+		snd_soc_dapm_enable_pin(dapm, "EAR");
+	}
+	mutex_unlock(&tasha->codec_mutex);
+	snd_soc_dapm_sync(dapm);
+	return 0;
+}
+
+static int tasha_get_clkmode(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] = tasha->clk_mode;
+	dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+	return 0;
+}
+
+static int tasha_put_clkmode(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	tasha->clk_mode = ucontrol->value.enumerated.item[0];
+	dev_dbg(codec->dev, "%s: clk_mode: %d\n", __func__, tasha->clk_mode);
+
+	return 0;
+}
+
+static int tasha_get_iir_enable_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	/* IIR filter band registers are at integer multiples of 16 */
+	u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+	ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) &
+					    (1 << band_idx)) != 0;
+
+	dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int tasha_hph_impedance_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	uint32_t zl, zr;
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	mc = (struct soc_multi_mixer_control *)(kcontrol->private_value);
+	hphr = mc->shift;
+	wcd_mbhc_get_impedance(&priv->mbhc, &zl, &zr);
+	dev_dbg(codec->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 impedance_detect_controls[] = {
+	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+		       tasha_hph_impedance_get, NULL),
+	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+		       tasha_hph_impedance_get, NULL),
+};
+
+static int tasha_get_hph_type(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct wcd_mbhc *mbhc;
+
+	if (!priv) {
+		dev_dbg(codec->dev, "%s: wcd9335 private data is NULL\n",
+				__func__);
+		return 0;
+	}
+
+	mbhc = &priv->mbhc;
+	if (!mbhc) {
+		dev_dbg(codec->dev, "%s: mbhc not initialized\n", __func__);
+		return 0;
+	}
+
+	ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+	dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new hph_type_detect_controls[] = {
+	SOC_SINGLE_EXT("HPH Type", 0, 0, UINT_MAX, 0,
+		       tasha_get_hph_type, NULL),
+};
+
+static int tasha_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tasha_p->vi_feed_value;
+
+	return 0;
+}
+
+static int tasha_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = tasha_p->wcd9xxx;
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n",
+		__func__, enable, port_id, dai_id);
+
+	tasha_p->vi_feed_value = ucontrol->value.integer.value[0];
+
+	mutex_lock(&tasha_p->codec_mutex);
+	if (enable) {
+		if (port_id == TASHA_TX14 && !test_bit(VI_SENSE_1,
+						&tasha_p->status_mask)) {
+			list_add_tail(&core->tx_chs[TASHA_TX14].list,
+					&tasha_p->dai[dai_id].wcd9xxx_ch_list);
+			set_bit(VI_SENSE_1, &tasha_p->status_mask);
+		}
+		if (port_id == TASHA_TX15 && !test_bit(VI_SENSE_2,
+						&tasha_p->status_mask)) {
+			list_add_tail(&core->tx_chs[TASHA_TX15].list,
+					&tasha_p->dai[dai_id].wcd9xxx_ch_list);
+			set_bit(VI_SENSE_2, &tasha_p->status_mask);
+		}
+	} else {
+		if (port_id == TASHA_TX14 && test_bit(VI_SENSE_1,
+					&tasha_p->status_mask)) {
+			list_del_init(&core->tx_chs[TASHA_TX14].list);
+			clear_bit(VI_SENSE_1, &tasha_p->status_mask);
+		}
+		if (port_id == TASHA_TX15 && test_bit(VI_SENSE_2,
+					&tasha_p->status_mask)) {
+			list_del_init(&core->tx_chs[TASHA_TX15].list);
+			clear_bit(VI_SENSE_2, &tasha_p->status_mask);
+		}
+	}
+	mutex_unlock(&tasha_p->codec_mutex);
+	snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+	return 0;
+}
+
+/* virtual port entries */
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tasha_p->tx_port_value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct snd_soc_dapm_update *update = NULL;
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+	u32 vtable;
+
+
+	dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
+		  __func__,
+		widget->name, ucontrol->id.name, tasha_p->tx_port_value,
+		widget->shift, ucontrol->value.integer.value[0]);
+
+	mutex_lock(&tasha_p->codec_mutex);
+
+	if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (dai_id != AIF1_CAP) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			mutex_unlock(&tasha_p->codec_mutex);
+			return -EINVAL;
+		}
+		vtable = vport_slim_check_table[dai_id];
+	} else {
+		if (dai_id >= ARRAY_SIZE(vport_i2s_check_table)) {
+			dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n",
+				__func__, dai_id);
+			return -EINVAL;
+		}
+		vtable = vport_i2s_check_table[dai_id];
+	}
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set */
+		if (enable && !(tasha_p->tx_port_value & 1 << port_id)) {
+
+			if (wcd9xxx_tx_vport_validation(vtable, port_id,
+					tasha_p->dai, NUM_CODEC_DAIS)) {
+				dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
+					__func__, port_id);
+				mutex_unlock(&tasha_p->codec_mutex);
+				return 0;
+			}
+			tasha_p->tx_port_value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+			      &tasha_p->dai[dai_id].wcd9xxx_ch_list
+					      );
+		} else if (!enable && (tasha_p->tx_port_value &
+					1 << port_id)) {
+			tasha_p->tx_port_value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				dev_dbg(codec->dev, "%s: TX%u port is used by\n"
+					"this virtual port\n",
+					__func__, port_id);
+			else
+				dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
+					"this virtual port\n",
+					__func__, port_id);
+			/* avoid update power function */
+			mutex_unlock(&tasha_p->codec_mutex);
+			return 0;
+		}
+		break;
+	case AIF4_MAD_TX:
+	case AIF5_CPE_TX:
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", dai_id);
+		mutex_unlock(&tasha_p->codec_mutex);
+		return -EINVAL;
+	}
+	pr_debug("%s: name %s sname %s updated value %u shift %d\n", __func__,
+		widget->name, widget->sname, tasha_p->tx_port_value,
+		widget->shift);
+
+	mutex_unlock(&tasha_p->codec_mutex);
+	snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] =
+			tasha_p->rx_port_value[widget->shift];
+	return 0;
+}
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB", "AIF_MIX1_PB"
+};
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+	unsigned int rx_port_value;
+	u32 port_id = widget->shift;
+
+	tasha_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+	rx_port_value = tasha_p->rx_port_value[port_id];
+
+	pr_debug("%s: wname %s cname %s value %u shift %d item %ld\n", __func__,
+		widget->name, ucontrol->id.name, rx_port_value,
+		widget->shift, ucontrol->value.integer.value[0]);
+
+	mutex_lock(&tasha_p->codec_mutex);
+
+	if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		if (rx_port_value > 2) {
+			dev_err(codec->dev, "%s: invalid AIF for I2C mode\n",
+				__func__);
+			goto err;
+		}
+	}
+	/* value need to match the Virtual port and AIF number */
+	switch (rx_port_value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+		break;
+	case 1:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TASHA_RX_PORT_START_NUMBER,
+			&tasha_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tasha_p->dai[AIF1_PB].wcd9xxx_ch_list);
+		break;
+	case 2:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TASHA_RX_PORT_START_NUMBER,
+			&tasha_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tasha_p->dai[AIF2_PB].wcd9xxx_ch_list);
+		break;
+	case 3:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TASHA_RX_PORT_START_NUMBER,
+			&tasha_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tasha_p->dai[AIF3_PB].wcd9xxx_ch_list);
+		break;
+	case 4:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TASHA_RX_PORT_START_NUMBER,
+			&tasha_p->dai[AIF4_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tasha_p->dai[AIF4_PB].wcd9xxx_ch_list);
+		break;
+	case 5:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			TASHA_RX_PORT_START_NUMBER,
+			&tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tasha_p->dai[AIF_MIX1_PB].wcd9xxx_ch_list);
+		break;
+	default:
+		pr_err("Unknown AIF %d\n", rx_port_value);
+		goto err;
+	}
+rtn:
+	mutex_unlock(&tasha_p->codec_mutex);
+	snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+					rx_port_value, e, update);
+
+	return 0;
+err:
+	mutex_unlock(&tasha_p->codec_mutex);
+	return -EINVAL;
+}
+
+static const struct soc_enum slim_rx_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim_rx_mux_text), slim_rx_mux_text);
+
+static const struct snd_kcontrol_new slim_rx_mux[TASHA_RX_MAX] = {
+	SOC_DAPM_ENUM_EXT("SLIM RX0 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX1 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX2 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX3 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX4 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX5 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX6 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+	SOC_DAPM_ENUM_EXT("SLIM RX7 Mux", slim_rx_mux_enum,
+			  slim_rx_mux_get, slim_rx_mux_put),
+};
+
+static const struct snd_kcontrol_new aif4_vi_mixer[] = {
+	SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, TASHA_TX14, 1, 0,
+			tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put),
+	SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, TASHA_TX15, 1, 0,
+			tasha_vi_feed_mixer_get, tasha_vi_feed_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, TASHA_TX0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, TASHA_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, TASHA_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, TASHA_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, TASHA_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, TASHA_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, TASHA_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, TASHA_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, TASHA_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, TASHA_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, TASHA_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, TASHA_TX11, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif4_mad_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX12", SND_SOC_NOPM, TASHA_TX12, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, TASHA_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, 0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+
+};
+
+static const struct snd_kcontrol_new rx_int1_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int2_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int3_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int4_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int5_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("LO3 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int6_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("LO4 Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int7_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("SPKRL Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int8_spline_mix_switch[] = {
+	SOC_DAPM_SINGLE("SPKRR Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int5_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("LO3 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int6_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("LO4 VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int7_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("SPKRL VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new rx_int8_vbat_mix_switch[] = {
+	SOC_DAPM_SINGLE("SPKRR VBAT Enable", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static const struct snd_kcontrol_new cpe_in_mix_switch[] = {
+	SOC_DAPM_SINGLE("MAD_BYPASS", SND_SOC_NOPM, 0, 1, 0)
+};
+
+
+
+static int tasha_put_iir_enable_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	bool iir_band_en_status;
+	int value = ucontrol->value.integer.value[0];
+	u16 iir_reg = WCD9335_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+	/* Mask first 5 bits, 6-8 are reserved */
+	snd_soc_update_bits(codec, iir_reg, (1 << band_idx),
+			    (value << band_idx));
+
+	iir_band_en_status = ((snd_soc_read(codec, iir_reg) &
+			      (1 << band_idx)) != 0);
+	pr_debug("%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx, iir_band_en_status);
+	return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+				int iir_idx, int band_idx,
+				int coeff_idx)
+{
+	uint32_t value = 0;
+
+	/* Address does not automatically update if reading */
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t)) & 0x7F);
+
+	value |= snd_soc_read(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx));
+
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 1) & 0x7F);
+
+	value |= (snd_soc_read(codec,
+			       (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+				16 * iir_idx)) << 8);
+
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 2) & 0x7F);
+
+	value |= (snd_soc_read(codec,
+			       (WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+				16 * iir_idx)) << 16);
+
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 3) & 0x7F);
+
+	/* Mask bits top 2 bits since they are reserved */
+	value |= ((snd_soc_read(codec,
+				(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+				 16 * iir_idx)) & 0x3F) << 24);
+
+	return value;
+}
+
+static int tasha_get_iir_band_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	ucontrol->value.integer.value[0] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+	ucontrol->value.integer.value[1] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+	ucontrol->value.integer.value[2] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+	ucontrol->value.integer.value[3] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+	ucontrol->value.integer.value[4] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+	pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[1],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[2],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[3],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[4]);
+	return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+				int iir_idx, int band_idx,
+				uint32_t value)
+{
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value & 0xFF));
+
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 8) & 0xFF);
+
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 16) & 0xFF);
+
+	/* Mask top 2 bits, 7-8 are reserved */
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 24) & 0x3F);
+}
+
+static void tasha_codec_enable_int_port(struct wcd9xxx_codec_dai_data *dai,
+					struct snd_soc_codec *codec)
+{
+	struct wcd9xxx_ch *ch;
+	int port_num = 0;
+	unsigned short reg = 0;
+	u8 val = 0;
+	struct tasha_priv *tasha_p;
+
+	if (!dai || !codec) {
+		pr_err("%s: Invalid params\n", __func__);
+		return;
+	}
+
+	tasha_p = snd_soc_codec_get_drvdata(codec);
+	list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+		if (ch->port >= TASHA_RX_PORT_START_NUMBER) {
+			port_num = ch->port - TASHA_RX_PORT_START_NUMBER;
+			reg = TASHA_SLIM_PGD_PORT_INT_EN0 + (port_num / 8);
+			val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx,
+				reg);
+			if (!(val & BYTE_BIT_MASK(port_num))) {
+				val |= BYTE_BIT_MASK(port_num);
+				wcd9xxx_interface_reg_write(
+					tasha_p->wcd9xxx, reg, val);
+				val = wcd9xxx_interface_reg_read(
+					tasha_p->wcd9xxx, reg);
+			}
+		} else {
+			port_num = ch->port;
+			reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+			val = wcd9xxx_interface_reg_read(tasha_p->wcd9xxx,
+				reg);
+			if (!(val & BYTE_BIT_MASK(port_num))) {
+				val |= BYTE_BIT_MASK(port_num);
+				wcd9xxx_interface_reg_write(tasha_p->wcd9xxx,
+					reg, val);
+				val = wcd9xxx_interface_reg_read(
+					tasha_p->wcd9xxx, reg);
+			}
+		}
+	}
+}
+
+static int tasha_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+					  bool up)
+{
+	int ret = 0;
+	struct wcd9xxx_ch *ch;
+
+	if (up) {
+		list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
+			if (ret < 0) {
+				pr_err("%s: Invalid slave port ID: %d\n",
+				       __func__, ret);
+				ret = -EINVAL;
+			} else {
+				set_bit(ret, &dai->ch_mask);
+			}
+		}
+	} else {
+		ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+					 msecs_to_jiffies(
+						TASHA_SLIM_CLOSE_TIMEOUT));
+		if (!ret) {
+			pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n",
+				__func__, dai->ch_mask);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
+static int tasha_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct wcd9xxx *core;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, codec->component.name,
+		codec->component.num_dai, w->sname, event);
+
+	/* Execute the callback only if interface type is slimbus */
+	if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		return 0;
+
+	dai = &tasha_p->dai[w->shift];
+	dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n",
+		 __func__, w->name, w->shift, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dai->bus_down_in_recovery = false;
+		tasha_codec_enable_int_port(dai, codec);
+		(void) tasha_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		if (!test_bit(SB_CLK_GEAR, &tasha_p->status_mask)) {
+			tasha_codec_vote_max_bw(codec, true);
+			set_bit(SB_CLK_GEAR, &tasha_p->status_mask);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list,
+					      dai->grph);
+		dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
+			__func__, ret);
+
+		if (!dai->bus_down_in_recovery)
+			ret = tasha_codec_enable_slim_chmask(dai, false);
+		else
+			dev_dbg(codec->dev,
+				"%s: bus in recovery skip enable slim_chmask",
+				__func__);
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		break;
+	}
+	return ret;
+}
+
+static int tasha_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
+					      struct snd_kcontrol *kcontrol,
+					      int event)
+{
+	struct wcd9xxx *core = NULL;
+	struct snd_soc_codec *codec = NULL;
+	struct tasha_priv *tasha_p = NULL;
+	int ret = 0;
+	struct wcd9xxx_codec_dai_data *dai = NULL;
+
+	if (!w) {
+		pr_err("%s invalid params\n", __func__);
+		return -EINVAL;
+	}
+	codec = snd_soc_dapm_to_codec(w->dapm);
+	tasha_p = snd_soc_codec_get_drvdata(codec);
+	core = tasha_p->wcd9xxx;
+
+	dev_dbg(codec->dev, "%s: num_dai %d stream name %s\n",
+		__func__, codec->component.num_dai, w->sname);
+
+	/* Execute the callback only if interface type is slimbus */
+	if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		dev_err(codec->dev, "%s Interface is not correct", __func__);
+		return 0;
+	}
+
+	dev_dbg(codec->dev, "%s(): w->name %s event %d w->shift %d\n",
+		__func__, w->name, event, w->shift);
+	if (w->shift != AIF4_VIFEED) {
+		pr_err("%s Error in enabling the tx path\n", __func__);
+		ret = -EINVAL;
+		goto out_vi;
+	}
+	dai = &tasha_p->dai[w->shift];
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) {
+			dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__);
+			/* Enable V&I sensing */
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+				0x10);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+				0x00);
+		}
+		if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) {
+			pr_debug("%s: spkr2 enabled\n", __func__);
+			/* Enable V&I sensing */
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+				0x10);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+				0x10);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+				0x00);
+		}
+		dai->bus_down_in_recovery = false;
+		tasha_codec_enable_int_port(dai, codec);
+		(void) tasha_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		if (ret)
+			dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n",
+				__func__, ret);
+		if (!dai->bus_down_in_recovery)
+			ret = tasha_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+				&dai->wcd9xxx_ch_list,
+				dai->grph);
+			dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n",
+				__func__, ret);
+		}
+		if (test_bit(VI_SENSE_1, &tasha_p->status_mask)) {
+			/* Disable V&I sensing */
+			dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+				0x00);
+		}
+		if (test_bit(VI_SENSE_2, &tasha_p->status_mask)) {
+			/* Disable V&I sensing */
+			dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+				0x00);
+		}
+		break;
+	}
+out_vi:
+	return ret;
+}
+
+/*
+ * __tasha_codec_enable_slimtx: Enable the slimbus slave port
+ *				 for TX path
+ * @codec: Handle to the codec for which the slave port is to be
+ *	   enabled.
+ * @dai_data: The dai specific data for dai which is enabled.
+ */
+static int __tasha_codec_enable_slimtx(struct snd_soc_codec *codec,
+		int event, struct wcd9xxx_codec_dai_data *dai)
+{
+	struct wcd9xxx *core;
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	/* Execute the callback only if interface type is slimbus */
+	if (tasha_p->intf_type != WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		return 0;
+
+	dev_dbg(codec->dev,
+		"%s: event = %d\n", __func__, event);
+	core = dev_get_drvdata(codec->dev->parent);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dai->bus_down_in_recovery = false;
+		tasha_codec_enable_int_port(dai, codec);
+		(void) tasha_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		if (!dai->bus_down_in_recovery)
+			ret = tasha_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			pr_debug("%s: Disconnect TX port, ret = %d\n",
+				 __func__, ret);
+		}
+
+		break;
+	}
+
+	return ret;
+}
+
+static int tasha_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *dai;
+
+	dev_dbg(codec->dev,
+		"%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n",
+		__func__, w->name, w->shift,
+		codec->component.num_dai, w->sname);
+
+	dai = &tasha_p->dai[w->shift];
+	return __tasha_codec_enable_slimtx(codec, event, dai);
+}
+
+static void tasha_codec_cpe_pp_set_cfg(struct snd_soc_codec *codec, int event)
+{
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *dai;
+	u8 bit_width, rate, buf_period;
+
+	dai = &tasha_p->dai[AIF4_MAD_TX];
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		switch (dai->bit_width) {
+		case 32:
+			bit_width = 0xF;
+			break;
+		case 24:
+			bit_width = 0xE;
+			break;
+		case 20:
+			bit_width = 0xD;
+			break;
+		case 16:
+		default:
+			bit_width = 0x0;
+			break;
+		}
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x0F,
+				    bit_width);
+
+		switch (dai->rate) {
+		case 384000:
+			rate = 0x30;
+			break;
+		case 192000:
+			rate = 0x20;
+			break;
+		case 48000:
+			rate = 0x10;
+			break;
+		case 16000:
+		default:
+			rate = 0x00;
+			break;
+		}
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x70,
+				    rate);
+
+		buf_period = (dai->rate * (dai->bit_width/8)) / (16*1000);
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD,
+				    0xFF, buf_period);
+		dev_dbg(codec->dev, "%s: PP buffer period= 0x%x\n",
+			__func__, buf_period);
+		break;
+
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_CFG, 0x3C);
+		snd_soc_write(codec, WCD9335_CPE_SS_TX_PP_BUF_INT_PERIOD, 0x60);
+		break;
+
+	default:
+		break;
+	}
+}
+
+/*
+ * tasha_codec_get_mad_port_id: Callback function that will be invoked
+ *	to get the port ID for MAD.
+ * @codec: Handle to the codec
+ * @port_id: cpe port_id needs to enable
+ */
+static int tasha_codec_get_mad_port_id(struct snd_soc_codec *codec,
+				       u16 *port_id)
+{
+	struct tasha_priv *tasha_p;
+	struct wcd9xxx_codec_dai_data *dai;
+	struct wcd9xxx_ch *ch;
+
+	if (!port_id || !codec)
+		return -EINVAL;
+
+	tasha_p = snd_soc_codec_get_drvdata(codec);
+	if (!tasha_p)
+		return -EINVAL;
+
+	dai = &tasha_p->dai[AIF4_MAD_TX];
+	list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+		if (ch->port == TASHA_TX12)
+			*port_id = WCD_CPE_AFE_OUT_PORT_2;
+		else if (ch->port == TASHA_TX13)
+			*port_id = WCD_CPE_AFE_OUT_PORT_4;
+		else {
+			dev_err(codec->dev, "%s: invalid mad_port = %d\n",
+					__func__, ch->port);
+			return -EINVAL;
+		}
+	}
+	dev_dbg(codec->dev, "%s: port_id = %d\n", __func__, *port_id);
+
+	return 0;
+}
+
+/*
+ * tasha_codec_enable_slimtx_mad: Callback function that will be invoked
+ *	to setup the slave port for MAD.
+ * @codec: Handle to the codec
+ * @event: Indicates whether to enable or disable the slave port
+ */
+static int tasha_codec_enable_slimtx_mad(struct snd_soc_codec *codec,
+					 u8 event)
+{
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *dai;
+	struct wcd9xxx_ch *ch;
+	int dapm_event = SND_SOC_DAPM_POST_PMU;
+	u16 port = 0;
+	int ret = 0;
+
+	dai = &tasha_p->dai[AIF4_MAD_TX];
+
+	if (event == 0)
+		dapm_event = SND_SOC_DAPM_POST_PMD;
+
+	dev_dbg(codec->dev,
+		"%s: mad_channel, event = 0x%x\n",
+		 __func__, event);
+
+	list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+		dev_dbg(codec->dev, "%s: mad_port = %d, event = 0x%x\n",
+			__func__, ch->port, event);
+		if (ch->port == TASHA_TX13) {
+			tasha_codec_cpe_pp_set_cfg(codec, dapm_event);
+			port = TASHA_TX13;
+			break;
+		}
+	}
+
+	ret = __tasha_codec_enable_slimtx(codec, dapm_event, dai);
+
+	if (port == TASHA_TX13) {
+		switch (dapm_event) {
+		case SND_SOC_DAPM_POST_PMU:
+			snd_soc_update_bits(codec,
+				WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN,
+				0x20, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG,
+				0x03, 0x02);
+			snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG,
+					    0x80, 0x80);
+			break;
+		case SND_SOC_DAPM_POST_PMD:
+			snd_soc_update_bits(codec,
+				WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN,
+				0x20, 0x20);
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG,
+				0x03, 0x00);
+			snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG,
+					    0x80, 0x00);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int tasha_put_iir_band_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	/*
+	 * Mask top bit it is reserved
+	 * Updates addr automatically for each B2 write
+	 */
+	snd_soc_write(codec,
+		(WCD9335_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		(band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[0]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[1]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[2]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[3]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[4]);
+
+	pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+	return 0;
+}
+
+static int tasha_get_compander(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int comp = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tasha->comp_enabled[comp];
+	return 0;
+}
+
+static int tasha_set_compander(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int comp = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+	int value = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: Compander %d enable current %d, new %d\n",
+		 __func__, comp + 1, tasha->comp_enabled[comp], value);
+	tasha->comp_enabled[comp] = value;
+
+	/* Any specific register configuration for compander */
+	switch (comp) {
+	case COMPANDER_1:
+		/* Set Gain Source Select based on compander enable/disable */
+		snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0x20,
+				(value ? 0x00:0x20));
+		break;
+	case COMPANDER_2:
+		snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0x20,
+				(value ? 0x00:0x20));
+		break;
+	case COMPANDER_3:
+		break;
+	case COMPANDER_4:
+		break;
+	case COMPANDER_5:
+		snd_soc_update_bits(codec, WCD9335_SE_LO_LO3_GAIN, 0x20,
+				(value ? 0x00:0x20));
+		break;
+	case COMPANDER_6:
+		snd_soc_update_bits(codec, WCD9335_SE_LO_LO4_GAIN, 0x20,
+				(value ? 0x00:0x20));
+		break;
+	case COMPANDER_7:
+		break;
+	case COMPANDER_8:
+		break;
+	default:
+		/*
+		 * if compander is not enabled for any interpolator,
+		 * it does not cause any audio failure, so do not
+		 * return error in this case, but just print a log
+		 */
+		dev_warn(codec->dev, "%s: unknown compander: %d\n",
+			__func__, comp);
+	};
+	return 0;
+}
+
+static void tasha_codec_init_flyback(struct snd_soc_codec *codec)
+{
+	snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x00);
+	snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x00);
+	snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0x0F, 0x00);
+	snd_soc_update_bits(codec, WCD9335_RX_BIAS_FLYB_BUFF, 0xF0, 0x00);
+}
+
+static int tasha_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tasha->rx_bias_count++;
+		if (tasha->rx_bias_count == 1) {
+			if (TASHA_IS_2_0(tasha->wcd9xxx))
+				tasha_codec_init_flyback(codec);
+			snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES,
+					    0x01, 0x01);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tasha->rx_bias_count--;
+		if (!tasha->rx_bias_count)
+			snd_soc_update_bits(codec, WCD9335_ANA_RX_SUPPLIES,
+					    0x01, 0x00);
+		break;
+	};
+	dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__,
+		tasha->rx_bias_count);
+
+	return 0;
+}
+
+static void tasha_realign_anc_coeff(struct snd_soc_codec *codec,
+				    u16 reg1, u16 reg2)
+{
+	u8 val1, val2, tmpval1, tmpval2;
+
+	snd_soc_write(codec, reg1, 0x00);
+	tmpval1 = snd_soc_read(codec, reg2);
+	tmpval2 = snd_soc_read(codec, reg2);
+	snd_soc_write(codec, reg1, 0x00);
+	snd_soc_write(codec, reg2, 0xFF);
+	snd_soc_write(codec, reg1, 0x01);
+	snd_soc_write(codec, reg2, 0xFF);
+
+	snd_soc_write(codec, reg1, 0x00);
+	val1 = snd_soc_read(codec, reg2);
+	val2 = snd_soc_read(codec, reg2);
+
+	if (val1 == 0x0F && val2 == 0xFF) {
+		dev_dbg(codec->dev, "%s: ANC0 co-eff index re-aligned\n",
+			__func__);
+		snd_soc_read(codec, reg2);
+		snd_soc_write(codec, reg1, 0x00);
+		snd_soc_write(codec, reg2, tmpval2);
+		snd_soc_write(codec, reg1, 0x01);
+		snd_soc_write(codec, reg2, tmpval1);
+	} else if (val1 == 0xFF && val2 == 0x0F) {
+		dev_dbg(codec->dev, "%s: ANC1 co-eff index already aligned\n",
+			__func__);
+		snd_soc_write(codec, reg1, 0x00);
+		snd_soc_write(codec, reg2, tmpval1);
+		snd_soc_write(codec, reg1, 0x01);
+		snd_soc_write(codec, reg2, tmpval2);
+	} else {
+		dev_err(codec->dev, "%s: ANC0 co-eff index not aligned\n",
+			__func__);
+	}
+}
+
+static int tasha_codec_enable_anc(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	const char *filename;
+	const struct firmware *fw;
+	int i;
+	int ret = 0;
+	int num_anc_slots;
+	struct wcd9xxx_anc_header *anc_head;
+	struct firmware_cal *hwdep_cal = NULL;
+	u32 anc_writes_size = 0;
+	u32 anc_cal_size = 0;
+	int anc_size_remaining;
+	u32 *anc_ptr;
+	u16 reg;
+	u8 mask, val;
+	size_t cal_size;
+	const void *data;
+
+	if (!tasha->anc_func)
+		return 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_ANC_CAL);
+		if (hwdep_cal) {
+			data = hwdep_cal->data;
+			cal_size = hwdep_cal->size;
+			dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+				__func__);
+		} else {
+			filename = "wcd9335/wcd9335_anc.bin";
+			ret = request_firmware(&fw, filename, codec->dev);
+			if (ret != 0) {
+				dev_err(codec->dev,
+				"Failed to acquire ANC data: %d\n", ret);
+				return -ENODEV;
+			}
+			if (!fw) {
+				dev_err(codec->dev, "failed to get anc fw");
+				return -ENODEV;
+			}
+			data = fw->data;
+			cal_size = fw->size;
+			dev_dbg(codec->dev,
+			"%s: using request_firmware calibration\n", __func__);
+		}
+		if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+			dev_err(codec->dev, "Not enough data\n");
+			ret = -ENOMEM;
+			goto err;
+		}
+		/* First number is the number of register writes */
+		anc_head = (struct wcd9xxx_anc_header *)(data);
+		anc_ptr = (u32 *)(data +
+				  sizeof(struct wcd9xxx_anc_header));
+		anc_size_remaining = cal_size -
+				     sizeof(struct wcd9xxx_anc_header);
+		num_anc_slots = anc_head->num_anc_slots;
+
+		if (tasha->anc_slot >= num_anc_slots) {
+			dev_err(codec->dev, "Invalid ANC slot selected\n");
+			ret = -EINVAL;
+			goto err;
+		}
+		for (i = 0; i < num_anc_slots; i++) {
+			if (anc_size_remaining < TASHA_PACKED_REG_SIZE) {
+				dev_err(codec->dev,
+					"Invalid register format\n");
+				ret = -EINVAL;
+				goto err;
+			}
+			anc_writes_size = (u32)(*anc_ptr);
+			anc_size_remaining -= sizeof(u32);
+			anc_ptr += 1;
+
+			if (anc_writes_size * TASHA_PACKED_REG_SIZE
+				> anc_size_remaining) {
+				dev_err(codec->dev,
+					"Invalid register format\n");
+				ret = -EINVAL;
+				goto err;
+			}
+
+			if (tasha->anc_slot == i)
+				break;
+
+			anc_size_remaining -= (anc_writes_size *
+				TASHA_PACKED_REG_SIZE);
+			anc_ptr += anc_writes_size;
+		}
+		if (i == num_anc_slots) {
+			dev_err(codec->dev, "Selected ANC slot not present\n");
+			ret = -EINVAL;
+			goto err;
+		}
+
+		i = 0;
+		anc_cal_size = anc_writes_size;
+
+		if (!strcmp(w->name, "RX INT0 DAC") ||
+		    !strcmp(w->name, "ANC SPK1 PA"))
+			tasha_realign_anc_coeff(codec,
+					WCD9335_CDC_ANC0_IIR_COEFF_1_CTL,
+					WCD9335_CDC_ANC0_IIR_COEFF_2_CTL);
+
+		if (!strcmp(w->name, "RX INT1 DAC") ||
+			!strcmp(w->name, "RX INT3 DAC")) {
+			tasha_realign_anc_coeff(codec,
+					WCD9335_CDC_ANC0_IIR_COEFF_1_CTL,
+					WCD9335_CDC_ANC0_IIR_COEFF_2_CTL);
+			anc_writes_size = anc_cal_size / 2;
+			snd_soc_update_bits(codec,
+			WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x39, 0x39);
+		} else if (!strcmp(w->name, "RX INT2 DAC") ||
+				!strcmp(w->name, "RX INT4 DAC")) {
+			tasha_realign_anc_coeff(codec,
+					WCD9335_CDC_ANC1_IIR_COEFF_1_CTL,
+					WCD9335_CDC_ANC1_IIR_COEFF_2_CTL);
+			i = anc_cal_size / 2;
+			snd_soc_update_bits(codec,
+			WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x39, 0x39);
+		}
+
+		for (; i < anc_writes_size; i++) {
+			TASHA_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val);
+			snd_soc_write(codec, reg, (val & mask));
+		}
+		if (!strcmp(w->name, "RX INT1 DAC") ||
+			!strcmp(w->name, "RX INT3 DAC")) {
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x08, 0x08);
+		} else if (!strcmp(w->name, "RX INT2 DAC") ||
+				!strcmp(w->name, "RX INT4 DAC")) {
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x08, 0x08);
+		}
+
+		if (!hwdep_cal)
+			release_firmware(fw);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* Remove ANC Rx from reset */
+		snd_soc_update_bits(codec, WCD9335_CDC_ANC0_CLK_RESET_CTL,
+				    0x08, 0x00);
+		snd_soc_update_bits(codec, WCD9335_CDC_ANC1_CLK_RESET_CTL,
+				    0x08, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (!strcmp(w->name, "ANC HPHL PA") ||
+		    !strcmp(w->name, "ANC EAR PA") ||
+		    !strcmp(w->name, "ANC SPK1 PA") ||
+		    !strcmp(w->name, "ANC LINEOUT1 PA")) {
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC0_MODE_1_CTL, 0x30, 0x00);
+			msleep(50);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC0_MODE_1_CTL, 0x01, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x38);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x07, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC0_CLK_RESET_CTL, 0x38, 0x00);
+		} else if (!strcmp(w->name, "ANC HPHR PA") ||
+			   !strcmp(w->name, "ANC LINEOUT2 PA")) {
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC1_MODE_1_CTL, 0x30, 0x00);
+			msleep(50);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC1_MODE_1_CTL, 0x01, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x38);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x07, 0x00);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_ANC1_CLK_RESET_CTL, 0x38, 0x00);
+		}
+		break;
+	}
+
+	return 0;
+err:
+	if (!hwdep_cal)
+		release_firmware(fw);
+	return ret;
+}
+
+static void tasha_codec_clear_anc_tx_hold(struct tasha_priv *tasha)
+{
+	if (test_and_clear_bit(ANC_MIC_AMIC1, &tasha->status_mask))
+		tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC1, false);
+	if (test_and_clear_bit(ANC_MIC_AMIC2, &tasha->status_mask))
+		tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC2, false);
+	if (test_and_clear_bit(ANC_MIC_AMIC3, &tasha->status_mask))
+		tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC3, false);
+	if (test_and_clear_bit(ANC_MIC_AMIC4, &tasha->status_mask))
+		tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC4, false);
+	if (test_and_clear_bit(ANC_MIC_AMIC5, &tasha->status_mask))
+		tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC5, false);
+	if (test_and_clear_bit(ANC_MIC_AMIC6, &tasha->status_mask))
+		tasha_codec_set_tx_hold(tasha->codec, WCD9335_ANA_AMIC6, false);
+}
+
+static void tasha_codec_hph_post_pa_config(struct tasha_priv *tasha,
+					   int mode, int event)
+{
+	u8 scale_val = 0;
+
+	if (!TASHA_IS_2_0(tasha->wcd9xxx))
+		return;
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		switch (mode) {
+		case CLS_H_HIFI:
+			scale_val = 0x3;
+			break;
+		case CLS_H_LOHIFI:
+			scale_val = 0x1;
+			break;
+		}
+		if (tasha->anc_func) {
+			/* Clear Tx FE HOLD if both PAs are enabled */
+			if ((snd_soc_read(tasha->codec, WCD9335_ANA_HPH) &
+			     0xC0) == 0xC0) {
+				tasha_codec_clear_anc_tx_hold(tasha);
+			}
+		}
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		scale_val = 0x6;
+		break;
+	}
+
+	if (scale_val)
+		snd_soc_update_bits(tasha->codec, WCD9335_HPH_PA_CTL1, 0x0E,
+				    scale_val << 1);
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		if (tasha->comp_enabled[COMPANDER_1] ||
+		    tasha->comp_enabled[COMPANDER_2]) {
+			snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN,
+					    0x20, 0x00);
+			snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN,
+					    0x20, 0x00);
+			snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP,
+					    0x20, 0x20);
+		}
+		snd_soc_update_bits(tasha->codec, WCD9335_HPH_L_EN, 0x1F,
+				    tasha->hph_l_gain);
+		snd_soc_update_bits(tasha->codec, WCD9335_HPH_R_EN, 0x1F,
+				    tasha->hph_r_gain);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(tasha->codec, WCD9335_HPH_AUTO_CHOP, 0x20,
+				    0x00);
+	}
+}
+
+static void tasha_codec_override(struct snd_soc_codec *codec,
+				 int mode,
+				 int event)
+{
+	if (mode == CLS_AB) {
+		switch (event) {
+		case SND_SOC_DAPM_POST_PMU:
+			if (!(snd_soc_read(codec,
+					WCD9335_CDC_RX2_RX_PATH_CTL) & 0x10) &&
+				(!(snd_soc_read(codec,
+					WCD9335_CDC_RX1_RX_PATH_CTL) & 0x10)))
+				snd_soc_update_bits(codec,
+					WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02);
+		break;
+		case SND_SOC_DAPM_POST_PMD:
+			snd_soc_update_bits(codec,
+				WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00);
+		break;
+		}
+	}
+}
+
+static int tasha_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = tasha->hph_mode;
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if ((!(strcmp(w->name, "ANC HPHR PA"))) &&
+		    (test_bit(HPH_PA_DELAY, &tasha->status_mask))) {
+			snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0);
+		}
+		set_bit(HPH_PA_DELAY, &tasha->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (!(strcmp(w->name, "ANC HPHR PA"))) {
+			if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+							!= 0xC0)
+				/*
+				 * If PA_EN is not set (potentially in ANC case)
+				 * then do nothing for POST_PMU and let left
+				 * channel handle everything.
+				 */
+				break;
+		}
+		/*
+		 * 7ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) {
+			usleep_range(7000, 7100);
+			clear_bit(HPH_PA_DELAY, &tasha->status_mask);
+		}
+		tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+		snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL,
+				    0x10, 0x00);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) &
+				  0x10)
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+					    0x10, 0x00);
+
+		if (!(strcmp(w->name, "ANC HPHR PA"))) {
+			/* Do everything needed for left channel */
+			snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL,
+					    0x10, 0x00);
+			/* Remove mix path mute if it is enabled */
+			if ((snd_soc_read(codec,
+					  WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) &
+					  0x10)
+				snd_soc_update_bits(codec,
+						WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+						0x10, 0x00);
+			/* Remove ANC Rx from reset */
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+		}
+		tasha_codec_override(codec, hph_mode, event);
+		break;
+
+	case SND_SOC_DAPM_PRE_PMD:
+		blocking_notifier_call_chain(&tasha->notifier,
+					WCD_EVENT_PRE_HPHR_PA_OFF,
+					&tasha->mbhc);
+		tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+		if (!(strcmp(w->name, "ANC HPHR PA")))
+			snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x40, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 5ms sleep is required after PA is disabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		tasha_codec_override(codec, hph_mode, event);
+		blocking_notifier_call_chain(&tasha->notifier,
+					WCD_EVENT_POST_HPHR_PA_OFF,
+					&tasha->mbhc);
+
+		if (!(strcmp(w->name, "ANC HPHR PA"))) {
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x00);
+		}
+		break;
+	};
+
+	return ret;
+}
+
+static int tasha_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = tasha->hph_mode;
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if ((!(strcmp(w->name, "ANC HPHL PA"))) &&
+		    (test_bit(HPH_PA_DELAY, &tasha->status_mask))) {
+			snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0xC0, 0xC0);
+		}
+		set_bit(HPH_PA_DELAY, &tasha->status_mask);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (!(strcmp(w->name, "ANC HPHL PA"))) {
+			if ((snd_soc_read(codec, WCD9335_ANA_HPH) & 0xC0)
+								!= 0xC0)
+				/*
+				 * If PA_EN is not set (potentially in ANC case)
+				 * then do nothing for POST_PMU and let right
+				 * channel handle everything.
+				 */
+				break;
+		}
+		/*
+		 * 7ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		if (test_bit(HPH_PA_DELAY, &tasha->status_mask)) {
+			usleep_range(7000, 7100);
+			clear_bit(HPH_PA_DELAY, &tasha->status_mask);
+		}
+
+		tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+		snd_soc_update_bits(codec, WCD9335_CDC_RX1_RX_PATH_CTL,
+				    0x10, 0x00);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_MIX_CTL)) &
+				  0x10)
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+					    0x10, 0x00);
+
+		if (!(strcmp(w->name, "ANC HPHL PA"))) {
+			/* Do everything needed for right channel */
+			snd_soc_update_bits(codec, WCD9335_CDC_RX2_RX_PATH_CTL,
+					    0x10, 0x00);
+			/* Remove mix path mute if it is enabled */
+			if ((snd_soc_read(codec,
+					  WCD9335_CDC_RX2_RX_PATH_MIX_CTL)) &
+					  0x10)
+				snd_soc_update_bits(codec,
+						WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+						0x10, 0x00);
+
+			/* Remove ANC Rx from reset */
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+		}
+		tasha_codec_override(codec, hph_mode, event);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		blocking_notifier_call_chain(&tasha->notifier,
+					WCD_EVENT_PRE_HPHL_PA_OFF,
+					&tasha->mbhc);
+		tasha_codec_hph_post_pa_config(tasha, hph_mode, event);
+		if (!(strcmp(w->name, "ANC HPHL PA")))
+			snd_soc_update_bits(codec, WCD9335_ANA_HPH, 0x80, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 5ms sleep is required after PA is disabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		tasha_codec_override(codec, hph_mode, event);
+		blocking_notifier_call_chain(&tasha->notifier,
+					WCD_EVENT_POST_HPHL_PA_OFF,
+					&tasha->mbhc);
+
+		if (!(strcmp(w->name, "ANC HPHL PA"))) {
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x00);
+		}
+		break;
+	};
+
+	return ret;
+}
+
+static int tasha_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol,
+					 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0;
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	if (w->reg == WCD9335_ANA_LO_1_2) {
+		if (w->shift == 7) {
+			lineout_vol_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+			lineout_mix_vol_reg = WCD9335_CDC_RX3_RX_PATH_MIX_CTL;
+		} else if (w->shift == 6) {
+			lineout_vol_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+			lineout_mix_vol_reg = WCD9335_CDC_RX4_RX_PATH_MIX_CTL;
+		}
+	} else if (w->reg == WCD9335_ANA_LO_3_4) {
+		if (w->shift == 7) {
+			lineout_vol_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+			lineout_mix_vol_reg = WCD9335_CDC_RX5_RX_PATH_MIX_CTL;
+		} else if (w->shift == 6) {
+			lineout_vol_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+			lineout_mix_vol_reg = WCD9335_CDC_RX6_RX_PATH_MIX_CTL;
+		}
+	} else {
+		dev_err(codec->dev, "%s: Error enabling lineout PA\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* 5ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		snd_soc_update_bits(codec, lineout_vol_reg,
+				    0x10, 0x00);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10)
+			snd_soc_update_bits(codec,
+					    lineout_mix_vol_reg,
+					    0x10, 0x00);
+		if (!(strcmp(w->name, "ANC LINEOUT1 PA")) ||
+		    !(strcmp(w->name, "ANC LINEOUT2 PA")))
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+		tasha_codec_override(codec, CLS_AB, event);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 5ms sleep is required after PA is disabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		tasha_codec_override(codec, CLS_AB, event);
+		if (!(strcmp(w->name, "ANC LINEOUT1 PA")) ||
+			!(strcmp(w->name, "ANC LINEOUT2 PA"))) {
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+			if (!(strcmp(w->name, "ANC LINEOUT1 PA")))
+				snd_soc_update_bits(codec,
+				WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10);
+			else
+				snd_soc_update_bits(codec,
+				WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10);
+		}
+		break;
+	};
+
+	return ret;
+}
+
+static void tasha_spk_anc_update_callback(struct work_struct *work)
+{
+	struct spk_anc_work *spk_anc_dwork;
+	struct tasha_priv *tasha;
+	struct delayed_work *delayed_work;
+	struct snd_soc_codec *codec;
+
+	delayed_work = to_delayed_work(work);
+	spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork);
+	tasha = spk_anc_dwork->tasha;
+	codec = tasha->codec;
+
+	snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10);
+}
+
+static int tasha_codec_enable_spk_anc(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s %s %d %d\n", __func__, w->name, event,
+		tasha->anc_func);
+
+	if (!tasha->anc_func)
+		return 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = tasha_codec_enable_anc(w, kcontrol, event);
+		schedule_delayed_work(&tasha->spk_anc_dwork.dwork,
+				      msecs_to_jiffies(spk_anc_en_delay));
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		cancel_delayed_work_sync(&tasha->spk_anc_dwork.dwork);
+		snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_CFG0,
+				    0x10, 0x00);
+		ret = tasha_codec_enable_anc(w, kcontrol, event);
+		break;
+	}
+	return ret;
+}
+
+static int tasha_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* 5ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		snd_soc_update_bits(codec, WCD9335_CDC_RX0_RX_PATH_CTL,
+				    0x10, 0x00);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, WCD9335_CDC_RX0_RX_PATH_MIX_CTL)) &
+		     0x10)
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX0_RX_PATH_MIX_CTL,
+					    0x10, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 5ms sleep is required after PA is disabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+
+		if (!(strcmp(w->name, "ANC EAR PA"))) {
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x00);
+		}
+		break;
+	};
+
+	return ret;
+}
+
+static void tasha_codec_hph_mode_gain_opt(struct snd_soc_codec *codec,
+					  u8 gain)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u8 hph_l_en, hph_r_en;
+	u8 l_val, r_val;
+	u8 hph_pa_status;
+	bool is_hphl_pa, is_hphr_pa;
+
+	hph_pa_status = snd_soc_read(codec, WCD9335_ANA_HPH);
+	is_hphl_pa = hph_pa_status >> 7;
+	is_hphr_pa = (hph_pa_status & 0x40) >> 6;
+
+	hph_l_en = snd_soc_read(codec, WCD9335_HPH_L_EN);
+	hph_r_en = snd_soc_read(codec, WCD9335_HPH_R_EN);
+
+	l_val = (hph_l_en & 0xC0) | 0x20 | gain;
+	r_val = (hph_r_en & 0xC0) | 0x20 | gain;
+
+	/*
+	 * Set HPH_L & HPH_R gain source selection to REGISTER
+	 * for better click and pop only if corresponding PAs are
+	 * not enabled. Also cache the values of the HPHL/R
+	 * PA gains to be applied after PAs are enabled
+	 */
+	if ((l_val != hph_l_en) && !is_hphl_pa) {
+		snd_soc_write(codec, WCD9335_HPH_L_EN, l_val);
+		tasha->hph_l_gain = hph_l_en & 0x1F;
+	}
+
+	if ((r_val != hph_r_en) && !is_hphr_pa) {
+		snd_soc_write(codec, WCD9335_HPH_R_EN, r_val);
+		tasha->hph_r_gain = hph_r_en & 0x1F;
+	}
+}
+
+static void tasha_codec_hph_lohifi_config(struct snd_soc_codec *codec,
+					  int event)
+{
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x06);
+		snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2,
+				    0xF0, 0x40);
+		snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C);
+		tasha_codec_hph_mode_gain_opt(codec, 0x11);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00);
+		snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02);
+		snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDACBUFF_CNP2, 0x8A);
+		snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_PA, 0x0F, 0x0A);
+	}
+}
+
+static void tasha_codec_hph_lp_config(struct snd_soc_codec *codec,
+				      int event)
+{
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C);
+		tasha_codec_hph_mode_gain_opt(codec, 0x10);
+		snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x04);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x20);
+		snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x07,
+				    0x01);
+		snd_soc_update_bits(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x70,
+				    0x10);
+		snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO,
+				    0x0F, 0x01);
+		snd_soc_update_bits(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO,
+				    0xF0, 0x10);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_write(codec, WCD9335_RX_BIAS_HPH_RDAC_LDO, 0x88);
+		snd_soc_write(codec, WCD9335_HPH_RDAC_LDO_CTL, 0x33);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x20, 0x00);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x04, 0x00);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00);
+		snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02);
+		snd_soc_update_bits(codec, WCD9335_HPH_R_EN, 0xC0, 0x80);
+		snd_soc_update_bits(codec, WCD9335_HPH_L_EN, 0xC0, 0x80);
+	}
+}
+
+static void tasha_codec_hph_hifi_config(struct snd_soc_codec *codec,
+					int event)
+{
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x03);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x08);
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL1, 0x0E, 0x0C);
+		tasha_codec_hph_mode_gain_opt(codec, 0x11);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, WCD9335_HPH_PA_CTL2, 0x08, 0x00);
+		snd_soc_update_bits(codec, WCD9335_HPH_CNP_WG_CTL, 0x07, 0x02);
+	}
+}
+
+static void tasha_codec_hph_mode_config(struct snd_soc_codec *codec,
+					int event, int mode)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (!TASHA_IS_2_0(tasha->wcd9xxx))
+		return;
+
+	switch (mode) {
+	case CLS_H_LP:
+		tasha_codec_hph_lp_config(codec, event);
+		break;
+	case CLS_H_LOHIFI:
+		tasha_codec_hph_lohifi_config(codec, event);
+		break;
+	case CLS_H_HIFI:
+		tasha_codec_hph_hifi_config(codec, event);
+		break;
+	}
+}
+
+static int tasha_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int hph_mode = tasha->hph_mode;
+	u8 dem_inp;
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+		w->name, event, hph_mode);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tasha->anc_func) {
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+			/* 40 msec delay is needed to avoid click and pop */
+			msleep(40);
+		}
+
+		/* Read DEM INP Select */
+		dem_inp = snd_soc_read(codec, WCD9335_CDC_RX2_RX_PATH_SEC0) &
+			  0x03;
+		if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+		     (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+			dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+					__func__, hph_mode);
+			return -EINVAL;
+		}
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_HPHR,
+			     ((hph_mode == CLS_H_LOHIFI) ?
+			       CLS_H_HIFI : hph_mode));
+
+		tasha_codec_hph_mode_config(codec, event, hph_mode);
+
+		if (tasha->anc_func)
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_RX2_RX_PATH_CFG0, 0x10, 0x10);
+
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* 1000us required as per HW requirement */
+		usleep_range(1000, 1100);
+		if ((hph_mode == CLS_H_LP) &&
+		   (TASHA_IS_1_1(wcd9xxx))) {
+			snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+					    0x03, 0x03);
+		}
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		if ((hph_mode == CLS_H_LP) &&
+		   (TASHA_IS_1_1(wcd9xxx))) {
+			snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+					    0x03, 0x00);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 1000us required as per HW requirement */
+		usleep_range(1000, 1100);
+
+		if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) &
+		     WCD_CLSH_STATE_HPHL))
+			tasha_codec_hph_mode_config(codec, event, hph_mode);
+
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_HPHR,
+			     ((hph_mode == CLS_H_LOHIFI) ?
+			       CLS_H_HIFI : hph_mode));
+		break;
+	};
+
+	return ret;
+}
+
+static int tasha_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int hph_mode = tasha->hph_mode;
+	u8 dem_inp;
+	int ret = 0;
+	uint32_t impedl = 0, impedr = 0;
+
+	dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+		w->name, event, hph_mode);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tasha->anc_func) {
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+			/* 40 msec delay is needed to avoid click and pop */
+			msleep(40);
+		}
+
+		/* Read DEM INP Select */
+		dem_inp = snd_soc_read(codec, WCD9335_CDC_RX1_RX_PATH_SEC0) &
+			  0x03;
+		if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+		     (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+			dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+					__func__, hph_mode);
+			return -EINVAL;
+		}
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_HPHL,
+			     ((hph_mode == CLS_H_LOHIFI) ?
+			       CLS_H_HIFI : hph_mode));
+
+		tasha_codec_hph_mode_config(codec, event, hph_mode);
+
+		if (tasha->anc_func)
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_RX1_RX_PATH_CFG0, 0x10, 0x10);
+
+		ret = wcd_mbhc_get_impedance(&tasha->mbhc,
+					&impedl, &impedr);
+		if (!ret) {
+			wcd_clsh_imped_config(codec, impedl, false);
+			set_bit(CLASSH_CONFIG, &tasha->status_mask);
+		} else {
+			dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+						__func__, ret);
+			ret = 0;
+		}
+
+
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* 1000us required as per HW requirement */
+		usleep_range(1000, 1100);
+		if ((hph_mode == CLS_H_LP) &&
+		   (TASHA_IS_1_1(wcd9xxx))) {
+			snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+					    0x03, 0x03);
+		}
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		if ((hph_mode == CLS_H_LP) &&
+		   (TASHA_IS_1_1(wcd9xxx))) {
+			snd_soc_update_bits(codec, WCD9335_HPH_L_DAC_CTL,
+					    0x03, 0x00);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 1000us required as per HW requirement */
+		usleep_range(1000, 1100);
+
+		if (!(wcd_clsh_get_clsh_state(&tasha->clsh_d) &
+		     WCD_CLSH_STATE_HPHR))
+			tasha_codec_hph_mode_config(codec, event, hph_mode);
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_HPHL,
+			     ((hph_mode == CLS_H_LOHIFI) ?
+			       CLS_H_HIFI : hph_mode));
+
+		if (test_bit(CLASSH_CONFIG, &tasha->status_mask)) {
+			wcd_clsh_imped_config(codec, impedl, true);
+			clear_bit(CLASSH_CONFIG, &tasha->status_mask);
+		} else
+			dev_dbg(codec->dev, "%s: Failed to get mbhc impedance %d\n",
+						__func__, ret);
+
+
+		break;
+	};
+
+	return ret;
+}
+
+static int tasha_codec_lineout_dac_event(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol,
+					 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tasha->anc_func &&
+			(!strcmp(w->name, "RX INT3 DAC") ||
+				!strcmp(w->name, "RX INT4 DAC")))
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_LO,
+			     CLS_AB);
+
+		if (tasha->anc_func) {
+			if (!strcmp(w->name, "RX INT3 DAC"))
+				snd_soc_update_bits(codec,
+				WCD9335_CDC_RX3_RX_PATH_CFG0, 0x10, 0x10);
+			else if (!strcmp(w->name, "RX INT4 DAC"))
+				snd_soc_update_bits(codec,
+				WCD9335_CDC_RX4_RX_PATH_CFG0, 0x10, 0x10);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_LO,
+			     CLS_AB);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget tasha_dapm_i2s_widgets[] = {
+	SND_SOC_DAPM_SUPPLY("RX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+	0, 0, NULL, 0),
+	SND_SOC_DAPM_SUPPLY("TX_I2S_CTL", WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+	0, 0, NULL, 0),
+};
+
+static int tasha_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tasha->anc_func)
+			ret = tasha_codec_enable_anc(w, kcontrol, event);
+
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_EAR,
+			     CLS_H_NORMAL);
+		if (tasha->anc_func)
+			snd_soc_update_bits(codec,
+				WCD9335_CDC_RX0_RX_PATH_CFG0, 0x10, 0x10);
+
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd_clsh_fsm(codec, &tasha->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_EAR,
+			     CLS_H_NORMAL);
+		break;
+	};
+
+	return ret;
+}
+
+static int tasha_codec_spk_boost_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol,
+				int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u16 boost_path_ctl, boost_path_cfg1;
+	u16 reg, reg_mix;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	if (!strcmp(w->name, "RX INT7 CHAIN")) {
+		boost_path_ctl = WCD9335_CDC_BOOST0_BOOST_PATH_CTL;
+		boost_path_cfg1 = WCD9335_CDC_RX7_RX_PATH_CFG1;
+		reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+		reg_mix = WCD9335_CDC_RX7_RX_PATH_MIX_CTL;
+	} else if (!strcmp(w->name, "RX INT8 CHAIN")) {
+		boost_path_ctl = WCD9335_CDC_BOOST1_BOOST_PATH_CTL;
+		boost_path_cfg1 = WCD9335_CDC_RX8_RX_PATH_CFG1;
+		reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+		reg_mix = WCD9335_CDC_RX8_RX_PATH_MIX_CTL;
+	} else {
+		dev_err(codec->dev, "%s: unknown widget: %s\n",
+			__func__, w->name);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10);
+		snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01);
+		snd_soc_update_bits(codec, reg, 0x10, 0x00);
+		if ((snd_soc_read(codec, reg_mix)) & 0x10)
+			snd_soc_update_bits(codec, reg_mix, 0x10, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00);
+		snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00);
+		break;
+	};
+
+	return 0;
+}
+
+static u16 tasha_interp_get_primary_reg(u16 reg, u16 *ind)
+{
+	u16 prim_int_reg = 0;
+
+	switch (reg) {
+	case WCD9335_CDC_RX0_RX_PATH_CTL:
+	case WCD9335_CDC_RX0_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+		*ind = 0;
+		break;
+	case WCD9335_CDC_RX1_RX_PATH_CTL:
+	case WCD9335_CDC_RX1_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+		*ind = 1;
+		break;
+	case WCD9335_CDC_RX2_RX_PATH_CTL:
+	case WCD9335_CDC_RX2_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+		*ind = 2;
+		break;
+	case WCD9335_CDC_RX3_RX_PATH_CTL:
+	case WCD9335_CDC_RX3_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+		*ind = 3;
+		break;
+	case WCD9335_CDC_RX4_RX_PATH_CTL:
+	case WCD9335_CDC_RX4_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+		*ind = 4;
+		break;
+	case WCD9335_CDC_RX5_RX_PATH_CTL:
+	case WCD9335_CDC_RX5_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+		*ind = 5;
+		break;
+	case WCD9335_CDC_RX6_RX_PATH_CTL:
+	case WCD9335_CDC_RX6_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+		*ind = 6;
+		break;
+	case WCD9335_CDC_RX7_RX_PATH_CTL:
+	case WCD9335_CDC_RX7_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+		*ind = 7;
+		break;
+	case WCD9335_CDC_RX8_RX_PATH_CTL:
+	case WCD9335_CDC_RX8_RX_PATH_MIX_CTL:
+		prim_int_reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+		*ind = 8;
+		break;
+	};
+
+	return prim_int_reg;
+}
+
+static void tasha_codec_hd2_control(struct snd_soc_codec *codec,
+				    u16 prim_int_reg, int event)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 hd2_scale_reg;
+	u16 hd2_enable_reg = 0;
+
+	if (!TASHA_IS_2_0(tasha->wcd9xxx))
+		return;
+
+	if (prim_int_reg == WCD9335_CDC_RX1_RX_PATH_CTL) {
+		hd2_scale_reg = WCD9335_CDC_RX1_RX_PATH_SEC3;
+		hd2_enable_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+	}
+	if (prim_int_reg == WCD9335_CDC_RX2_RX_PATH_CTL) {
+		hd2_scale_reg = WCD9335_CDC_RX2_RX_PATH_SEC3;
+		hd2_enable_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+	}
+
+	if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x10);
+		snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x01);
+		snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04);
+	}
+
+	if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00);
+		snd_soc_update_bits(codec, hd2_scale_reg, 0x03, 0x00);
+		snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00);
+	}
+}
+
+static int tasha_codec_enable_prim_interpolator(
+				struct snd_soc_codec *codec,
+				u16 reg, int event)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 prim_int_reg;
+	u16 ind = 0;
+
+	prim_int_reg = tasha_interp_get_primary_reg(reg, &ind);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tasha->prim_int_users[ind]++;
+		if (tasha->prim_int_users[ind] == 1) {
+			snd_soc_update_bits(codec, prim_int_reg,
+					    0x10, 0x10);
+			tasha_codec_hd2_control(codec, prim_int_reg, event);
+			snd_soc_update_bits(codec, prim_int_reg,
+					    1 << 0x5, 1 << 0x5);
+		}
+		if ((reg != prim_int_reg) &&
+		    ((snd_soc_read(codec, prim_int_reg)) & 0x10))
+			snd_soc_update_bits(codec, reg, 0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tasha->prim_int_users[ind]--;
+		if (tasha->prim_int_users[ind] == 0) {
+			snd_soc_update_bits(codec, prim_int_reg,
+					1 << 0x5, 0 << 0x5);
+			snd_soc_update_bits(codec, prim_int_reg,
+					0x40, 0x40);
+			snd_soc_update_bits(codec, prim_int_reg,
+					0x40, 0x00);
+			tasha_codec_hd2_control(codec, prim_int_reg, event);
+		}
+		break;
+	};
+
+	dev_dbg(codec->dev, "%s: primary interpolator: INT%d, users: %d\n",
+		__func__, ind, tasha->prim_int_users[ind]);
+	return 0;
+}
+
+static int tasha_codec_enable_spline_src(struct snd_soc_codec *codec,
+					 int src_num,
+					 int event)
+{
+	u16 src_paired_reg;
+	struct tasha_priv *tasha;
+	u16 rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+	u16 rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+	int *src_users, count, spl_src = SPLINE_SRC0;
+	u16 src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+
+	tasha = snd_soc_codec_get_drvdata(codec);
+
+	switch (src_num) {
+	case SRC_IN_HPHL:
+		rx_path_cfg_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+		spl_src = SPLINE_SRC0;
+		break;
+	case SRC_IN_LO1:
+		rx_path_cfg_reg = WCD9335_CDC_RX3_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+		spl_src = SPLINE_SRC0;
+		break;
+	case SRC_IN_HPHR:
+		rx_path_cfg_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+		spl_src = SPLINE_SRC1;
+		break;
+	case SRC_IN_LO2:
+		rx_path_cfg_reg = WCD9335_CDC_RX4_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC1_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC0_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+		spl_src = SPLINE_SRC1;
+		break;
+	case SRC_IN_SPKRL:
+		rx_path_cfg_reg = WCD9335_CDC_RX7_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+		spl_src = SPLINE_SRC2;
+		break;
+	case SRC_IN_LO3:
+		rx_path_cfg_reg = WCD9335_CDC_RX5_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+		spl_src = SPLINE_SRC2;
+		break;
+	case SRC_IN_SPKRR:
+		rx_path_cfg_reg = WCD9335_CDC_RX8_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+		spl_src = SPLINE_SRC3;
+		break;
+	case SRC_IN_LO4:
+		rx_path_cfg_reg = WCD9335_CDC_RX6_RX_PATH_CFG0;
+		src_clk_reg = WCD9335_SPLINE_SRC3_CLK_RST_CTL_0;
+		src_paired_reg = WCD9335_SPLINE_SRC2_CLK_RST_CTL_0;
+		rx_path_ctl_reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+		spl_src = SPLINE_SRC3;
+		break;
+	};
+
+	src_users = &tasha->spl_src_users[spl_src];
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		count = *src_users;
+		count++;
+		if (count == 1) {
+			if ((snd_soc_read(codec, src_clk_reg) & 0x02) ||
+			    (snd_soc_read(codec, src_paired_reg) & 0x02)) {
+				snd_soc_update_bits(codec, src_clk_reg, 0x02,
+						    0x00);
+				snd_soc_update_bits(codec, src_paired_reg,
+						    0x02, 0x00);
+			}
+			snd_soc_update_bits(codec, src_clk_reg,	0x01, 0x01);
+			snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80,
+					    0x80);
+		}
+		*src_users = count;
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		count = *src_users;
+		count--;
+		if (count == 0) {
+			snd_soc_update_bits(codec, rx_path_cfg_reg, 0x80,
+					    0x00);
+			snd_soc_update_bits(codec, src_clk_reg, 0x03, 0x02);
+			/* default sample rate */
+			snd_soc_update_bits(codec, rx_path_ctl_reg, 0x0f,
+					    0x04);
+		}
+		*src_users = count;
+		break;
+	};
+
+	dev_dbg(codec->dev, "%s: Spline SRC%d, users: %d\n",
+		__func__, spl_src, *src_users);
+	return 0;
+}
+
+static int tasha_codec_enable_spline_resampler(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol,
+				int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+	u8 src_in;
+
+	src_in = snd_soc_read(codec, WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0);
+	if (!(src_in & 0xFF)) {
+		dev_err(codec->dev, "%s: Spline SRC%u input not selected\n",
+			__func__, w->shift);
+		return -EINVAL;
+	}
+
+	switch (w->shift) {
+	case SPLINE_SRC0:
+		ret = tasha_codec_enable_spline_src(codec,
+			((src_in & 0x03) == 1) ? SRC_IN_HPHL : SRC_IN_LO1,
+			event);
+		break;
+	case SPLINE_SRC1:
+		ret = tasha_codec_enable_spline_src(codec,
+			((src_in & 0x0C) == 4) ? SRC_IN_HPHR : SRC_IN_LO2,
+			event);
+		break;
+	case SPLINE_SRC2:
+		ret = tasha_codec_enable_spline_src(codec,
+			((src_in & 0x30) == 0x10) ? SRC_IN_LO3 : SRC_IN_SPKRL,
+			event);
+		break;
+	case SPLINE_SRC3:
+		ret = tasha_codec_enable_spline_src(codec,
+			((src_in & 0xC0) == 0x40) ? SRC_IN_LO4 : SRC_IN_SPKRR,
+			event);
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid spline src:%u\n", __func__,
+			w->shift);
+		ret = -EINVAL;
+	};
+
+	return ret;
+}
+
+static int tasha_codec_enable_swr(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha;
+	int i, ch_cnt;
+
+	tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (!tasha->nr)
+		return 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) &&
+		    !tasha->rx_7_count)
+			tasha->rx_7_count++;
+		if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+		    !tasha->rx_8_count)
+			tasha->rx_8_count++;
+		ch_cnt = tasha->rx_7_count + tasha->rx_8_count;
+
+		for (i = 0; i < tasha->nr; i++) {
+			swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev,
+					SWR_DEVICE_UP, NULL);
+			swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev,
+					SWR_SET_NUM_RX_CH, &ch_cnt);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if ((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) &&
+		    tasha->rx_7_count)
+			tasha->rx_7_count--;
+		if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+		    tasha->rx_8_count)
+			tasha->rx_8_count--;
+		ch_cnt = tasha->rx_7_count + tasha->rx_8_count;
+
+		for (i = 0; i < tasha->nr; i++)
+			swrm_wcd_notify(tasha->swr_ctrl_data[i].swr_pdev,
+					SWR_SET_NUM_RX_CH, &ch_cnt);
+
+		break;
+	}
+	dev_dbg(tasha->dev, "%s: current swr ch cnt: %d\n",
+		__func__, tasha->rx_7_count + tasha->rx_8_count);
+
+	return 0;
+}
+
+static int tasha_codec_config_ear_spkr_gain(struct snd_soc_codec *codec,
+					    int event, int gain_reg)
+{
+	int comp_gain_offset, val;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	switch (tasha->spkr_mode) {
+	/* Compander gain in SPKR_MODE1 case is 12 dB */
+	case SPKR_MODE_1:
+		comp_gain_offset = -12;
+		break;
+	/* Default case compander gain is 15 dB */
+	default:
+		comp_gain_offset = -15;
+		break;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* Apply ear spkr gain only if compander is enabled */
+		if (tasha->comp_enabled[COMPANDER_7] &&
+		    (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) &&
+		    (tasha->ear_spkr_gain != 0)) {
+			/* For example, val is -8(-12+5-1) for 4dB of gain */
+			val = comp_gain_offset + tasha->ear_spkr_gain - 1;
+			snd_soc_write(codec, gain_reg, val);
+
+			dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n",
+				__func__, val);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/*
+		 * Reset RX7 volume to 0 dB if compander is enabled and
+		 * ear_spkr_gain is non-zero.
+		 */
+		if (tasha->comp_enabled[COMPANDER_7] &&
+		    (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL) &&
+		    (tasha->ear_spkr_gain != 0)) {
+			snd_soc_write(codec, gain_reg, 0x0);
+
+			dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n",
+				__func__);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int tasha_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 gain_reg;
+	int offset_val = 0;
+	int val = 0;
+
+	dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+	switch (w->reg) {
+	case WCD9335_CDC_RX0_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX0_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX1_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX1_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX2_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX2_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX3_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX3_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX4_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX4_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX5_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX5_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX6_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX6_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX7_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX7_RX_VOL_MIX_CTL;
+		break;
+	case WCD9335_CDC_RX8_RX_PATH_MIX_CTL:
+		gain_reg = WCD9335_CDC_RX8_RX_VOL_MIX_CTL;
+		break;
+	default:
+		dev_err(codec->dev, "%s: No gain register avail for %s\n",
+			__func__, w->name);
+		return 0;
+	};
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tasha->comp_enabled[COMPANDER_7] ||
+		     tasha->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL ||
+		     gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) {
+			snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			offset_val = -2;
+		}
+		val = snd_soc_read(codec, gain_reg);
+		val += offset_val;
+		snd_soc_write(codec, gain_reg, val);
+		tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tasha->comp_enabled[COMPANDER_7] ||
+		     tasha->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD9335_CDC_RX7_RX_VOL_MIX_CTL ||
+		     gain_reg == WCD9335_CDC_RX8_RX_VOL_MIX_CTL)) {
+			snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			offset_val = 2;
+			val = snd_soc_read(codec, gain_reg);
+			val += offset_val;
+			snd_soc_write(codec, gain_reg, val);
+		}
+		tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	};
+
+	return 0;
+}
+
+static int __tasha_cdc_native_clk_enable(struct tasha_priv *tasha,
+					 bool enable)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = tasha->codec;
+
+	if (!tasha->wcd_native_clk) {
+		dev_err(tasha->dev, "%s: wcd native clock is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(tasha->dev, "%s: native_clk_enable = %u\n", __func__, enable);
+
+	if (enable) {
+		ret = clk_prepare_enable(tasha->wcd_native_clk);
+		if (ret) {
+			dev_err(tasha->dev, "%s: native clk enable failed\n",
+				__func__);
+			goto err;
+		}
+		if (++tasha->native_clk_users == 1) {
+			snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+					    0x10, 0x10);
+			snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+					    0x80, 0x80);
+			snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE,
+					    0x04, 0x00);
+			snd_soc_update_bits(codec,
+					WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x02, 0x02);
+		}
+	} else {
+		if (tasha->native_clk_users &&
+		    (--tasha->native_clk_users == 0)) {
+			snd_soc_update_bits(codec,
+					WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x02, 0x00);
+			snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_GATE,
+					    0x04, 0x04);
+			snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+					    0x80, 0x00);
+			snd_soc_update_bits(codec, WCD9335_CLOCK_TEST_CTL,
+					    0x10, 0x00);
+		}
+		clk_disable_unprepare(tasha->wcd_native_clk);
+	}
+
+	dev_dbg(codec->dev, "%s: native_clk_users: %d\n", __func__,
+		tasha->native_clk_users);
+err:
+	return ret;
+}
+
+static int tasha_codec_get_native_fifo_sync_mask(struct snd_soc_codec *codec,
+						 int interp_n)
+{
+	int mask = 0;
+	u16 reg;
+	u8 val1, val2, inp0 = 0;
+	u8 inp1 = 0, inp2 = 0;
+
+	reg = WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0 + (2 * interp_n) - 2;
+
+	val1 = snd_soc_read(codec, reg);
+	val2 = snd_soc_read(codec, reg + 1);
+
+	inp0 = val1 & 0x0F;
+	inp1 = (val1 >> 4) & 0x0F;
+	inp2 = (val2 >> 4) & 0x0F;
+
+	if (IS_VALID_NATIVE_FIFO_PORT(inp0))
+		mask |= (1 << (inp0 - 5));
+	if (IS_VALID_NATIVE_FIFO_PORT(inp1))
+		mask |= (1 << (inp1 - 5));
+	if (IS_VALID_NATIVE_FIFO_PORT(inp2))
+		mask |= (1 << (inp2 - 5));
+
+	dev_dbg(codec->dev, "%s: native fifo mask: 0x%x\n", __func__, mask);
+	if (!mask)
+		dev_err(codec->dev, "native fifo err,int:%d,inp0:%d,inp1:%d,inp2:%d\n",
+			interp_n, inp0, inp1, inp2);
+	return mask;
+}
+
+static int tasha_enable_native_supply(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol, int event)
+{
+	int mask;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 interp_reg;
+
+	dev_dbg(codec->dev, "%s: event: %d, shift:%d\n", __func__, event,
+		w->shift);
+
+	if (w->shift < INTERP_HPHL || w->shift > INTERP_LO2)
+		return -EINVAL;
+
+	interp_reg = WCD9335_CDC_RX1_RX_PATH_CTL + 20 * (w->shift - 1);
+
+	mask = tasha_codec_get_native_fifo_sync_mask(codec, w->shift);
+	if (!mask)
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Adjust interpolator rate to 44P1_NATIVE */
+		snd_soc_update_bits(codec, interp_reg, 0x0F, 0x09);
+		__tasha_cdc_native_clk_enable(tasha, true);
+		snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC,
+				    mask, mask);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, WCD9335_DATA_HUB_NATIVE_FIFO_SYNC,
+				    mask, 0x0);
+		__tasha_cdc_native_clk_enable(tasha, false);
+		/* Adjust interpolator rate to default */
+		snd_soc_update_bits(codec, interp_reg, 0x0F, 0x04);
+		break;
+	}
+
+	return 0;
+}
+
+static int tasha_codec_enable_interpolator(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 gain_reg;
+	u16 reg;
+	int val;
+	int offset_val = 0;
+
+	dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+	if (!(strcmp(w->name, "RX INT0 INTERP"))) {
+		reg = WCD9335_CDC_RX0_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX0_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT1 INTERP"))) {
+		reg = WCD9335_CDC_RX1_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX1_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT2 INTERP"))) {
+		reg = WCD9335_CDC_RX2_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX2_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT3 INTERP"))) {
+		reg = WCD9335_CDC_RX3_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX3_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT4 INTERP"))) {
+		reg = WCD9335_CDC_RX4_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX4_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT5 INTERP"))) {
+		reg = WCD9335_CDC_RX5_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX5_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT6 INTERP"))) {
+		reg = WCD9335_CDC_RX6_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX6_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT7 INTERP"))) {
+		reg = WCD9335_CDC_RX7_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX7_RX_VOL_CTL;
+	} else if (!(strcmp(w->name, "RX INT8 INTERP"))) {
+		reg = WCD9335_CDC_RX8_RX_PATH_CTL;
+		gain_reg = WCD9335_CDC_RX8_RX_VOL_CTL;
+	} else {
+		dev_err(codec->dev, "%s: Interpolator reg not found\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (!test_bit(SB_CLK_GEAR, &tasha->status_mask)) {
+			tasha_codec_vote_max_bw(codec, true);
+			set_bit(SB_CLK_GEAR, &tasha->status_mask);
+		}
+		/* Reset if needed */
+		tasha_codec_enable_prim_interpolator(codec, reg, event);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		tasha_config_compander(codec, w->shift, event);
+		/* apply gain after int clk is enabled */
+		if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tasha->comp_enabled[COMPANDER_7] ||
+		     tasha->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) {
+			snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			offset_val = -2;
+		}
+		val = snd_soc_read(codec, gain_reg);
+		val += offset_val;
+		snd_soc_write(codec, gain_reg, val);
+		tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tasha_config_compander(codec, w->shift, event);
+		tasha_codec_enable_prim_interpolator(codec, reg, event);
+		if ((tasha->spkr_gain_offset == RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tasha->comp_enabled[COMPANDER_7] ||
+		     tasha->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD9335_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD9335_CDC_RX8_RX_VOL_CTL)) {
+			snd_soc_update_bits(codec, WCD9335_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec, WCD9335_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD9335_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			offset_val = 2;
+			val = snd_soc_read(codec, gain_reg);
+			val += offset_val;
+			snd_soc_write(codec, gain_reg, val);
+		}
+		tasha_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	};
+
+	return 0;
+}
+
+static int tasha_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU: /* fall through */
+	case SND_SOC_DAPM_PRE_PMD:
+		if (strnstr(w->name, "IIR0", sizeof("IIR0"))) {
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL));
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL));
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL));
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL));
+		} else {
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL));
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL));
+			snd_soc_write(codec,
+				WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL,
+				snd_soc_read(codec,
+				WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL));
+		}
+		break;
+	}
+	return 0;
+}
+
+static int tasha_codec_enable_on_demand_supply(
+	struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct on_demand_supply *supply;
+
+	if (w->shift >= ON_DEMAND_SUPPLIES_MAX) {
+		dev_err(codec->dev, "%s: error index > MAX Demand supplies",
+			__func__);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	dev_dbg(codec->dev, "%s: supply: %s event: %d\n",
+		__func__, on_demand_supply_name[w->shift], event);
+
+	supply = &tasha->on_demand_list[w->shift];
+	WARN_ONCE(!supply->supply, "%s isn't defined\n",
+		on_demand_supply_name[w->shift]);
+	if (!supply->supply) {
+		dev_err(codec->dev, "%s: err supply not present ond for %d",
+			__func__, w->shift);
+		goto out;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = regulator_enable(supply->supply);
+		if (ret)
+			dev_err(codec->dev, "%s: Failed to enable %s\n",
+				__func__,
+				on_demand_supply_name[w->shift]);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = regulator_disable(supply->supply);
+		if (ret)
+			dev_err(codec->dev, "%s: Failed to disable %s\n",
+				__func__,
+				on_demand_supply_name[w->shift]);
+		break;
+	default:
+		break;
+	};
+
+out:
+	return ret;
+}
+
+static int tasha_codec_find_amic_input(struct snd_soc_codec *codec,
+				       int adc_mux_n)
+{
+	u16 mask, shift, adc_mux_in_reg;
+	u16 amic_mux_sel_reg;
+	bool is_amic;
+
+	if (adc_mux_n < 0 || adc_mux_n > WCD9335_MAX_VALID_ADC_MUX ||
+	    adc_mux_n == WCD9335_INVALID_ADC_MUX)
+		return 0;
+
+	/* Check whether adc mux input is AMIC or DMIC */
+	if (adc_mux_n < 4) {
+		adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+				 2 * adc_mux_n;
+		amic_mux_sel_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+				   2 * adc_mux_n;
+		mask = 0x03;
+		shift = 0;
+	} else {
+		adc_mux_in_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+				 adc_mux_n - 4;
+		amic_mux_sel_reg = adc_mux_in_reg;
+		mask = 0xC0;
+		shift = 6;
+	}
+	is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift)
+		    == 1);
+	if (!is_amic)
+		return 0;
+
+	return snd_soc_read(codec, amic_mux_sel_reg) & 0x07;
+}
+
+static void tasha_codec_set_tx_hold(struct snd_soc_codec *codec,
+				    u16 amic_reg, bool set)
+{
+	u8 mask = 0x20;
+	u8 val;
+
+	if (amic_reg == WCD9335_ANA_AMIC1 ||
+	    amic_reg == WCD9335_ANA_AMIC3 ||
+	    amic_reg == WCD9335_ANA_AMIC5)
+		mask = 0x40;
+
+	val = set ? mask : 0x00;
+
+	switch (amic_reg) {
+	case WCD9335_ANA_AMIC1:
+	case WCD9335_ANA_AMIC2:
+		snd_soc_update_bits(codec, WCD9335_ANA_AMIC2, mask, val);
+		break;
+	case WCD9335_ANA_AMIC3:
+	case WCD9335_ANA_AMIC4:
+		snd_soc_update_bits(codec, WCD9335_ANA_AMIC4, mask, val);
+		break;
+	case WCD9335_ANA_AMIC5:
+	case WCD9335_ANA_AMIC6:
+		snd_soc_update_bits(codec, WCD9335_ANA_AMIC6, mask, val);
+		break;
+	default:
+		dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+			__func__, amic_reg);
+		break;
+	}
+}
+
+static int tasha_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	int adc_mux_n = w->shift;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int amic_n;
+
+	dev_dbg(codec->dev, "%s: event: %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		amic_n = tasha_codec_find_amic_input(codec, adc_mux_n);
+		if (amic_n) {
+			/*
+			 * Prevent ANC Rx pop by leaving Tx FE in HOLD
+			 * state until PA is up. Track AMIC being used
+			 * so we can release the HOLD later.
+			 */
+			set_bit(ANC_MIC_AMIC1 + amic_n - 1,
+				&tasha->status_mask);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static u16 tasha_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic)
+{
+	u16 pwr_level_reg = 0;
+
+	switch (amic) {
+	case 1:
+	case 2:
+		pwr_level_reg = WCD9335_ANA_AMIC1;
+		break;
+
+	case 3:
+	case 4:
+		pwr_level_reg = WCD9335_ANA_AMIC3;
+		break;
+
+	case 5:
+	case 6:
+		pwr_level_reg = WCD9335_ANA_AMIC5;
+		break;
+	default:
+		dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+			__func__, amic);
+		break;
+	}
+
+	return pwr_level_reg;
+}
+
+#define  TX_HPF_CUT_OFF_FREQ_MASK	0x60
+#define  CF_MIN_3DB_4HZ			0x0
+#define  CF_MIN_3DB_75HZ		0x1
+#define  CF_MIN_3DB_150HZ		0x2
+
+static void tasha_tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+	struct delayed_work *hpf_delayed_work;
+	struct hpf_work *hpf_work;
+	struct tasha_priv *tasha;
+	struct snd_soc_codec *codec;
+	u16 dec_cfg_reg, amic_reg;
+	u8 hpf_cut_off_freq;
+	int amic_n;
+
+	hpf_delayed_work = to_delayed_work(work);
+	hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+	tasha = hpf_work->tasha;
+	codec = tasha->codec;
+	hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
+
+	dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator;
+
+	dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n",
+		__func__, hpf_work->decimator, hpf_cut_off_freq);
+
+	amic_n = tasha_codec_find_amic_input(codec, hpf_work->decimator);
+	if (amic_n) {
+		amic_reg = WCD9335_ANA_AMIC1 + amic_n - 1;
+		tasha_codec_set_tx_hold(codec, amic_reg, false);
+	}
+	tasha_codec_vote_max_bw(codec, true);
+	snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK,
+			    hpf_cut_off_freq << 5);
+	tasha_codec_vote_max_bw(codec, false);
+}
+
+static void tasha_tx_mute_update_callback(struct work_struct *work)
+{
+	struct tx_mute_work *tx_mute_dwork;
+	struct tasha_priv *tasha;
+	struct delayed_work *delayed_work;
+	struct snd_soc_codec *codec;
+	u16 tx_vol_ctl_reg, hpf_gate_reg;
+
+	delayed_work = to_delayed_work(work);
+	tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+	tasha = tx_mute_dwork->tasha;
+	codec = tasha->codec;
+
+	tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL +
+					16 * tx_mute_dwork->decimator;
+	hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 +
+					16 * tx_mute_dwork->decimator;
+	snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+}
+
+static int tasha_codec_enable_dec(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	unsigned int decimator;
+	char *dec_adc_mux_name = NULL;
+	char *widget_name = NULL;
+	char *wname;
+	int ret = 0, amic_n;
+	u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg;
+	u16 tx_gain_ctl_reg;
+	char *dec;
+	u8 hpf_cut_off_freq;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+	widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+	if (!widget_name)
+		return -ENOMEM;
+
+	wname = widget_name;
+	dec_adc_mux_name = strsep(&widget_name, " ");
+	if (!dec_adc_mux_name) {
+		dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+			__func__, w->name);
+		ret =  -EINVAL;
+		goto out;
+	}
+	dec_adc_mux_name = widget_name;
+
+	dec = strpbrk(dec_adc_mux_name, "012345678");
+	if (!dec) {
+		dev_err(codec->dev, "%s: decimator index not found\n",
+			__func__);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	ret = kstrtouint(dec, 10, &decimator);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+			__func__, wname);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__,
+			w->name, decimator);
+
+	tx_vol_ctl_reg = WCD9335_CDC_TX0_TX_PATH_CTL + 16 * decimator;
+	hpf_gate_reg = WCD9335_CDC_TX0_TX_PATH_SEC2 + 16 * decimator;
+	dec_cfg_reg = WCD9335_CDC_TX0_TX_PATH_CFG0 + 16 * decimator;
+	tx_gain_ctl_reg = WCD9335_CDC_TX0_TX_VOL_CTL + 16 * decimator;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		amic_n = tasha_codec_find_amic_input(codec, decimator);
+		if (amic_n)
+			pwr_level_reg = tasha_codec_get_amic_pwlvl_reg(codec,
+								       amic_n);
+
+		if (pwr_level_reg) {
+			switch ((snd_soc_read(codec, pwr_level_reg) &
+					      WCD9335_AMIC_PWR_LVL_MASK) >>
+					      WCD9335_AMIC_PWR_LVL_SHIFT) {
+			case WCD9335_AMIC_PWR_LEVEL_LP:
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    WCD9335_DEC_PWR_LVL_MASK,
+						    WCD9335_DEC_PWR_LVL_LP);
+				break;
+
+			case WCD9335_AMIC_PWR_LEVEL_HP:
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    WCD9335_DEC_PWR_LVL_MASK,
+						    WCD9335_DEC_PWR_LVL_HP);
+				break;
+			case WCD9335_AMIC_PWR_LEVEL_DEFAULT:
+			default:
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    WCD9335_DEC_PWR_LVL_MASK,
+						    WCD9335_DEC_PWR_LVL_DF);
+				break;
+			}
+		}
+		hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) &
+				   TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+		tasha->tx_hpf_work[decimator].hpf_cut_off_freq =
+							hpf_cut_off_freq;
+
+		if (hpf_cut_off_freq != CF_MIN_3DB_150HZ)
+			snd_soc_update_bits(codec, dec_cfg_reg,
+					    TX_HPF_CUT_OFF_FREQ_MASK,
+					    CF_MIN_3DB_150HZ << 5);
+		/* Enable TX PGA Mute */
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+		/* Enable APC */
+		snd_soc_update_bits(codec, dec_cfg_reg, 0x08, 0x08);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_update_bits(codec, hpf_gate_reg, 0x01, 0x00);
+
+		if (decimator == 0) {
+			snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
+			snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0xA3);
+			snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x83);
+			snd_soc_write(codec, WCD9335_MBHC_ZDET_RAMP_CTL, 0x03);
+		}
+		/* schedule work queue to Remove Mute */
+		schedule_delayed_work(&tasha->tx_mute_dwork[decimator].dwork,
+				      msecs_to_jiffies(tx_unmute_delay));
+		if (tasha->tx_hpf_work[decimator].hpf_cut_off_freq !=
+							CF_MIN_3DB_150HZ)
+			schedule_delayed_work(
+					&tasha->tx_hpf_work[decimator].dwork,
+					msecs_to_jiffies(300));
+		/* apply gain after decimator is enabled */
+		snd_soc_write(codec, tx_gain_ctl_reg,
+			      snd_soc_read(codec, tx_gain_ctl_reg));
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		hpf_cut_off_freq =
+			tasha->tx_hpf_work[decimator].hpf_cut_off_freq;
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+		snd_soc_update_bits(codec, dec_cfg_reg, 0x08, 0x00);
+		if (cancel_delayed_work_sync(
+		    &tasha->tx_hpf_work[decimator].dwork)) {
+			if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+				tasha_codec_vote_max_bw(codec, true);
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    TX_HPF_CUT_OFF_FREQ_MASK,
+						    hpf_cut_off_freq << 5);
+				tasha_codec_vote_max_bw(codec, false);
+			}
+		}
+		cancel_delayed_work_sync(
+				&tasha->tx_mute_dwork[decimator].dwork);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+		break;
+	};
+out:
+	kfree(wname);
+	return ret;
+}
+
+static u32 tasha_get_dmic_sample_rate(struct snd_soc_codec *codec,
+				unsigned int dmic, struct wcd9xxx_pdata *pdata)
+{
+	u8 tx_stream_fs;
+	u8 adc_mux_index = 0, adc_mux_sel = 0;
+	bool dec_found = false;
+	u16 adc_mux_ctl_reg, tx_fs_reg;
+	u32 dmic_fs;
+
+	while (dec_found == 0 && adc_mux_index < WCD9335_MAX_VALID_ADC_MUX) {
+		if (adc_mux_index < 4) {
+			adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+						(adc_mux_index * 2);
+			adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+						0x78) >> 3) - 1;
+		} else if (adc_mux_index < 9) {
+			adc_mux_ctl_reg = WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+						((adc_mux_index - 4) * 1);
+			adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+						0x38) >> 3) - 1;
+		} else if (adc_mux_index == 9) {
+			++adc_mux_index;
+			continue;
+		}
+		if (adc_mux_sel == dmic)
+			dec_found = true;
+		else
+			++adc_mux_index;
+	}
+
+	if (dec_found == true && adc_mux_index <= 8) {
+		tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+		tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
+		dmic_fs = tx_stream_fs <= 4 ? WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ :
+					WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+
+		/*
+		 * Check for ECPP path selection and DEC1 not connected to
+		 * any other audio path to apply ECPP DMIC sample rate
+		 */
+		if ((adc_mux_index == 1) &&
+		    ((snd_soc_read(codec, WCD9335_CPE_SS_US_EC_MUX_CFG)
+				   & 0x0F) == 0x0A) &&
+		    ((snd_soc_read(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0)
+				   & 0x0C) == 0x00)) {
+			dmic_fs = pdata->ecpp_dmic_sample_rate;
+		}
+	} else {
+		dmic_fs = pdata->dmic_sample_rate;
+	}
+
+	return dmic_fs;
+}
+
+static u8 tasha_get_dmic_clk_val(struct snd_soc_codec *codec,
+				 u32 mclk_rate, u32 dmic_clk_rate)
+{
+	u32 div_factor;
+	u8 dmic_ctl_val;
+
+	dev_dbg(codec->dev,
+		"%s: mclk_rate = %d, dmic_sample_rate = %d\n",
+		__func__, mclk_rate, dmic_clk_rate);
+
+	/* Default value to return in case of error */
+	if (mclk_rate == TASHA_MCLK_CLK_9P6MHZ)
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2;
+	else
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3;
+
+	if (dmic_clk_rate == 0) {
+		dev_err(codec->dev,
+			"%s: dmic_sample_rate cannot be 0\n",
+			__func__);
+		goto done;
+	}
+
+	div_factor = mclk_rate / dmic_clk_rate;
+	switch (div_factor) {
+	case 2:
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_2;
+		break;
+	case 3:
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_3;
+		break;
+	case 4:
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_4;
+		break;
+	case 6:
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_6;
+		break;
+	case 8:
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_8;
+		break;
+	case 16:
+		dmic_ctl_val = WCD9335_DMIC_CLK_DIV_16;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+			__func__, div_factor, mclk_rate, dmic_clk_rate);
+		break;
+	}
+
+done:
+	return dmic_ctl_val;
+}
+
+static int tasha_codec_enable_adc(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev, "%s: event:%d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tasha_codec_set_tx_hold(codec, w->reg, true);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int tasha_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+	u8  dmic_clk_en = 0x01;
+	u16 dmic_clk_reg;
+	s32 *dmic_clk_cnt;
+	u8 dmic_rate_val, dmic_rate_shift = 1;
+	unsigned int dmic;
+	u32 dmic_sample_rate;
+	int ret;
+	char *wname;
+
+	wname = strpbrk(w->name, "012345");
+	if (!wname) {
+		dev_err(codec->dev, "%s: widget not found\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = kstrtouint(wname, 10, &dmic);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (dmic) {
+	case 0:
+	case 1:
+		dmic_clk_cnt = &(tasha->dmic_0_1_clk_cnt);
+		dmic_clk_reg = WCD9335_CPE_SS_DMIC0_CTL;
+		break;
+	case 2:
+	case 3:
+		dmic_clk_cnt = &(tasha->dmic_2_3_clk_cnt);
+		dmic_clk_reg = WCD9335_CPE_SS_DMIC1_CTL;
+		break;
+	case 4:
+	case 5:
+		dmic_clk_cnt = &(tasha->dmic_4_5_clk_cnt);
+		dmic_clk_reg = WCD9335_CPE_SS_DMIC2_CTL;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid DMIC Selection\n",
+			__func__);
+		return -EINVAL;
+	};
+	dev_dbg(codec->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:
+		dmic_sample_rate = tasha_get_dmic_sample_rate(codec, dmic,
+						pdata);
+		dmic_rate_val =
+			tasha_get_dmic_clk_val(codec,
+					pdata->mclk_rate,
+					dmic_sample_rate);
+
+		(*dmic_clk_cnt)++;
+		if (*dmic_clk_cnt == 1) {
+			snd_soc_update_bits(codec, dmic_clk_reg,
+				0x07 << dmic_rate_shift,
+				dmic_rate_val << dmic_rate_shift);
+			snd_soc_update_bits(codec, dmic_clk_reg,
+					dmic_clk_en, dmic_clk_en);
+		}
+
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		dmic_rate_val =
+			tasha_get_dmic_clk_val(codec,
+					pdata->mclk_rate,
+					pdata->mad_dmic_sample_rate);
+		(*dmic_clk_cnt)--;
+		if (*dmic_clk_cnt  == 0) {
+			snd_soc_update_bits(codec, dmic_clk_reg,
+					dmic_clk_en, 0);
+			snd_soc_update_bits(codec, dmic_clk_reg,
+				0x07 << dmic_rate_shift,
+				dmic_rate_val << dmic_rate_shift);
+		}
+		break;
+	};
+
+	return 0;
+}
+
+static int __tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+					int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int micb_num;
+
+	dev_dbg(codec->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 if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4")))
+		micb_num = MIC_BIAS_4;
+	else
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/*
+		 * MIC BIAS can also be requested by MBHC,
+		 * so use ref count to handle micbias pullup
+		 * and enable requests
+		 */
+		tasha_micbias_control(codec, micb_num, MICB_ENABLE, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* wait for cnp time */
+		usleep_range(1000, 1100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tasha_micbias_control(codec, micb_num, MICB_DISABLE, true);
+		break;
+	};
+
+	return 0;
+}
+
+static int tasha_codec_ldo_h_control(struct snd_soc_dapm_widget *w,
+				     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		tasha->ldo_h_users++;
+
+		if (tasha->ldo_h_users == 1)
+			snd_soc_update_bits(codec, WCD9335_LDOH_MODE,
+					    0x80, 0x80);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		tasha->ldo_h_users--;
+
+		if (tasha->ldo_h_users < 0)
+			tasha->ldo_h_users = 0;
+
+		if (tasha->ldo_h_users == 0)
+			snd_soc_update_bits(codec, WCD9335_LDOH_MODE,
+					    0x80, 0x00);
+	}
+
+	return 0;
+}
+
+static int tasha_codec_force_enable_ldo_h(struct snd_soc_dapm_widget *w,
+					  struct snd_kcontrol *kcontrol,
+					  int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd_resmgr_enable_master_bias(tasha->resmgr);
+		tasha_codec_ldo_h_control(w, event);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tasha_codec_ldo_h_control(w, event);
+		wcd_resmgr_disable_master_bias(tasha->resmgr);
+		break;
+	}
+
+	return 0;
+}
+
+static int tasha_codec_force_enable_micbias(struct snd_soc_dapm_widget *w,
+					    struct snd_kcontrol *kcontrol,
+					    int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd_resmgr_enable_master_bias(tasha->resmgr);
+		tasha_cdc_mclk_enable(codec, true, true);
+		ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU);
+		/* Wait for 1ms for better cnp */
+		usleep_range(1000, 1100);
+		tasha_cdc_mclk_enable(codec, false, true);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = __tasha_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD);
+		wcd_resmgr_disable_master_bias(tasha->resmgr);
+		break;
+	}
+
+	return ret;
+}
+
+static int tasha_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+		struct snd_kcontrol *kcontrol, int event)
+{
+	return __tasha_codec_enable_micbias(w, event);
+}
+
+static int tasha_codec_enable_standalone_ldo_h(struct snd_soc_codec *codec,
+					       bool enable)
+{
+	int rc;
+
+	if (enable)
+		rc = snd_soc_dapm_force_enable_pin(
+					snd_soc_codec_get_dapm(codec),
+					DAPM_LDO_H_STANDALONE);
+	else
+		rc = snd_soc_dapm_disable_pin(
+					snd_soc_codec_get_dapm(codec),
+					DAPM_LDO_H_STANDALONE);
+
+	if (!rc)
+		snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+	else
+		dev_err(codec->dev, "%s: ldo_h force %s pin failed\n",
+			__func__, (enable ? "enable" : "disable"));
+
+	return rc;
+}
+
+/*
+ * tasha_codec_enable_standalone_micbias - enable micbias standalone
+ * @codec: pointer to codec instance
+ * @micb_num: number of micbias to be enabled
+ * @enable: true to enable micbias or false to disable
+ *
+ * This function is used to enable micbias (1, 2, 3 or 4) during
+ * standalone independent of whether TX use-case is running or not
+ *
+ * Return: error code in case of failure or 0 for success
+ */
+int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+					  int micb_num,
+					  bool enable)
+{
+	const char * const micb_names[] = {
+		DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE,
+		DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE
+	};
+	int micb_index = micb_num - 1;
+	int rc;
+
+	if (!codec) {
+		pr_err("%s: Codec memory is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((micb_index < 0) || (micb_index > TASHA_MAX_MICBIAS - 1)) {
+		dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+			__func__, micb_index);
+		return -EINVAL;
+	}
+
+	if (enable)
+		rc = snd_soc_dapm_force_enable_pin(
+						 snd_soc_codec_get_dapm(codec),
+						   micb_names[micb_index]);
+	else
+		rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+					      micb_names[micb_index]);
+
+	if (!rc)
+		snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+	else
+		dev_err(codec->dev, "%s: micbias%d force %s pin failed\n",
+			__func__, micb_num, (enable ? "enable" : "disable"));
+
+	return rc;
+}
+EXPORT_SYMBOL(tasha_codec_enable_standalone_micbias);
+
+static const char *const tasha_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tasha_anc_func_enum =
+		SOC_ENUM_SINGLE_EXT(2, tasha_anc_func_text);
+
+static const char *const tasha_clkmode_text[] = {"EXTERNAL", "INTERNAL"};
+static SOC_ENUM_SINGLE_EXT_DECL(tasha_clkmode_enum, tasha_clkmode_text);
+
+/* Cutoff frequency for high pass filter */
+static const char * const cf_text[] = {
+	"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
+};
+
+static const char * const rx_cf_text[] = {
+	"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ",
+	"CF_NEG_3DB_0P48HZ"
+};
+
+static const struct soc_enum cf_dec0_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX0_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX1_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec2_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX2_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec3_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX3_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec4_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX4_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec5_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX5_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec6_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX6_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec7_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX7_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_dec8_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX8_TX_PATH_CFG0, 5, 3, cf_text);
+
+static const struct soc_enum cf_int0_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int1_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int2_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int3_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int4_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int5_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int5_2_enum, WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int6_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int6_2_enum, WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int7_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct soc_enum cf_int8_1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CFG2, 0, 4, rx_cf_text);
+
+static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 2,
+		     rx_cf_text);
+
+static const struct snd_soc_dapm_route audio_i2s_map[] = {
+	{"SLIM RX0 MUX", NULL, "RX_I2S_CTL"},
+	{"SLIM RX1 MUX", NULL, "RX_I2S_CTL"},
+	{"SLIM RX2 MUX", NULL, "RX_I2S_CTL"},
+	{"SLIM RX3 MUX", NULL, "RX_I2S_CTL"},
+
+	{"SLIM TX6 MUX", NULL, "TX_I2S_CTL"},
+	{"SLIM TX7 MUX", NULL, "TX_I2S_CTL"},
+	{"SLIM TX8 MUX", NULL, "TX_I2S_CTL"},
+	{"SLIM TX11 MUX", NULL, "TX_I2S_CTL"},
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+
+	/* MAD */
+	{"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"},
+	{"MAD_SEL MUX", "MSM", "MADINPUT"},
+	{"MADONOFF", "Switch", "MAD_SEL MUX"},
+	{"MAD_BROADCAST", "Switch", "MAD_SEL MUX"},
+	{"TX13 INP MUX", "CPE_TX_PP", "MADONOFF"},
+
+	/* CPE HW MAD bypass */
+	{"CPE IN Mixer", "MAD_BYPASS", "SLIM TX1 MUX"},
+
+	{"AIF4_MAD Mixer", "SLIM TX1", "CPE IN Mixer"},
+	{"AIF4_MAD Mixer", "SLIM TX12", "MADONOFF"},
+	{"AIF4_MAD Mixer", "SLIM TX13", "TX13 INP MUX"},
+	{"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
+	{"AIF4 MAD", NULL, "AIF4"},
+
+	{"EC BUF MUX INP", "DEC1", "ADC MUX1"},
+	{"AIF5 CPE", NULL, "EC BUF MUX INP"},
+
+	/* SLIMBUS Connections */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+
+	/* VI Feedback */
+	{"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"},
+	{"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"},
+	{"AIF4 VI", NULL, "AIF4_VI Mixer"},
+
+	/* SLIM_MIXER("AIF1_CAP Mixer"),*/
+	{"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"},
+	{"AIF1_CAP Mixer", "SLIM TX13", "TX13 INP MUX"},
+	/* SLIM_MIXER("AIF2_CAP Mixer"),*/
+	{"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"},
+	{"AIF2_CAP Mixer", "SLIM TX13", "TX13 INP MUX"},
+	/* SLIM_MIXER("AIF3_CAP Mixer"),*/
+	{"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11 MUX"},
+	{"AIF3_CAP Mixer", "SLIM TX13", "TX13 INP MUX"},
+
+	{"SLIM TX0 MUX", "DEC0", "ADC MUX0"},
+	{"SLIM TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"},
+	{"SLIM TX0 MUX", "DEC0_192", "ADC US MUX0"},
+
+	{"SLIM TX1 MUX", "DEC1", "ADC MUX1"},
+	{"SLIM TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"},
+	{"SLIM TX1 MUX", "DEC1_192", "ADC US MUX1"},
+
+	{"SLIM TX2 MUX", "DEC2", "ADC MUX2"},
+	{"SLIM TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"},
+	{"SLIM TX2 MUX", "DEC2_192", "ADC US MUX2"},
+
+	{"SLIM TX3 MUX", "DEC3", "ADC MUX3"},
+	{"SLIM TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"},
+	{"SLIM TX3 MUX", "DEC3_192", "ADC US MUX3"},
+
+	{"SLIM TX4 MUX", "DEC4", "ADC MUX4"},
+	{"SLIM TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"},
+	{"SLIM TX4 MUX", "DEC4_192", "ADC US MUX4"},
+
+	{"SLIM TX5 MUX", "DEC5", "ADC MUX5"},
+	{"SLIM TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+	{"SLIM TX5 MUX", "DEC5_192", "ADC US MUX5"},
+
+	{"SLIM TX6 MUX", "DEC6", "ADC MUX6"},
+	{"SLIM TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"},
+	{"SLIM TX6 MUX", "DEC6_192", "ADC US MUX6"},
+
+	{"SLIM TX7 MUX", "DEC7", "ADC MUX7"},
+	{"SLIM TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"},
+	{"SLIM TX7 MUX", "DEC7_192", "ADC US MUX7"},
+
+	{"SLIM TX8 MUX", "DEC8", "ADC MUX8"},
+	{"SLIM TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"},
+	{"SLIM TX8 MUX", "DEC8_192", "ADC US MUX8"},
+
+	{"SLIM TX9 MUX", "DEC7", "ADC MUX7"},
+	{"SLIM TX9 MUX", "DEC7_192", "ADC US MUX7"},
+	{"SLIM TX10 MUX", "DEC6", "ADC MUX6"},
+	{"SLIM TX10 MUX", "DEC6_192", "ADC US MUX6"},
+
+	{"SLIM TX11 MUX", "DEC_0_5", "SLIM TX11 INP1 MUX"},
+	{"SLIM TX11 MUX", "DEC_9_12", "SLIM TX11 INP1 MUX"},
+	{"SLIM TX11 INP1 MUX", "DEC0", "ADC MUX0"},
+	{"SLIM TX11 INP1 MUX", "DEC1", "ADC MUX1"},
+	{"SLIM TX11 INP1 MUX", "DEC2", "ADC MUX2"},
+	{"SLIM TX11 INP1 MUX", "DEC3", "ADC MUX3"},
+	{"SLIM TX11 INP1 MUX", "DEC4", "ADC MUX4"},
+	{"SLIM TX11 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"SLIM TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+
+	{"TX13 INP MUX", "MAD_BRDCST", "MAD_BROADCAST"},
+	{"TX13 INP MUX", "CDC_DEC_5", "SLIM TX13 MUX"},
+	{"SLIM TX13 MUX", "DEC5", "ADC MUX5"},
+
+	{"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX0 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX0 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX0 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX1 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX1 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX1 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX2 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX2 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX2 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX3 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX3 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX3 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX4 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX4 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX4 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX5 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX5 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX5 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX6 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX6 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX6 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX7 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX7 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX7 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX5", "RX INT5 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX6", "RX INT6 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX_VBAT5", "RX INT5 VBAT"},
+	{"RX MIX TX8 MUX", "RX_MIX_VBAT6", "RX INT6 VBAT"},
+	{"RX MIX TX8 MUX", "RX_MIX_VBAT7", "RX INT7 VBAT"},
+	{"RX MIX TX8 MUX", "RX_MIX_VBAT8", "RX INT8 VBAT"},
+
+	{"ADC US MUX0", "US_Switch", "ADC MUX0"},
+	{"ADC US MUX1", "US_Switch", "ADC MUX1"},
+	{"ADC US MUX2", "US_Switch", "ADC MUX2"},
+	{"ADC US MUX3", "US_Switch", "ADC MUX3"},
+	{"ADC US MUX4", "US_Switch", "ADC MUX4"},
+	{"ADC US MUX5", "US_Switch", "ADC MUX5"},
+	{"ADC US MUX6", "US_Switch", "ADC MUX6"},
+	{"ADC US MUX7", "US_Switch", "ADC MUX7"},
+	{"ADC US MUX8", "US_Switch", "ADC MUX8"},
+	{"ADC MUX0", "DMIC", "DMIC MUX0"},
+	{"ADC MUX0", "AMIC", "AMIC MUX0"},
+	{"ADC MUX1", "DMIC", "DMIC MUX1"},
+	{"ADC MUX1", "AMIC", "AMIC MUX1"},
+	{"ADC MUX2", "DMIC", "DMIC MUX2"},
+	{"ADC MUX2", "AMIC", "AMIC MUX2"},
+	{"ADC MUX3", "DMIC", "DMIC MUX3"},
+	{"ADC MUX3", "AMIC", "AMIC MUX3"},
+	{"ADC MUX4", "DMIC", "DMIC MUX4"},
+	{"ADC MUX4", "AMIC", "AMIC MUX4"},
+	{"ADC MUX5", "DMIC", "DMIC MUX5"},
+	{"ADC MUX5", "AMIC", "AMIC MUX5"},
+	{"ADC MUX6", "DMIC", "DMIC MUX6"},
+	{"ADC MUX6", "AMIC", "AMIC MUX6"},
+	{"ADC MUX7", "DMIC", "DMIC MUX7"},
+	{"ADC MUX7", "AMIC", "AMIC MUX7"},
+	{"ADC MUX8", "DMIC", "DMIC MUX8"},
+	{"ADC MUX8", "AMIC", "AMIC MUX8"},
+	{"ADC MUX10", "DMIC", "DMIC MUX10"},
+	{"ADC MUX10", "AMIC", "AMIC MUX10"},
+	{"ADC MUX11", "DMIC", "DMIC MUX11"},
+	{"ADC MUX11", "AMIC", "AMIC MUX11"},
+	{"ADC MUX12", "DMIC", "DMIC MUX12"},
+	{"ADC MUX12", "AMIC", "AMIC MUX12"},
+	{"ADC MUX13", "DMIC", "DMIC MUX13"},
+	{"ADC MUX13", "AMIC", "AMIC MUX13"},
+
+	{"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"},
+
+	{"DMIC MUX0", "DMIC0", "DMIC0"},
+	{"DMIC MUX0", "DMIC1", "DMIC1"},
+	{"DMIC MUX0", "DMIC2", "DMIC2"},
+	{"DMIC MUX0", "DMIC3", "DMIC3"},
+	{"DMIC MUX0", "DMIC4", "DMIC4"},
+	{"DMIC MUX0", "DMIC5", "DMIC5"},
+	{"AMIC MUX0", "ADC1", "ADC1"},
+	{"AMIC MUX0", "ADC2", "ADC2"},
+	{"AMIC MUX0", "ADC3", "ADC3"},
+	{"AMIC MUX0", "ADC4", "ADC4"},
+	{"AMIC MUX0", "ADC5", "ADC5"},
+	{"AMIC MUX0", "ADC6", "ADC6"},
+
+	{"DMIC MUX1", "DMIC0", "DMIC0"},
+	{"DMIC MUX1", "DMIC1", "DMIC1"},
+	{"DMIC MUX1", "DMIC2", "DMIC2"},
+	{"DMIC MUX1", "DMIC3", "DMIC3"},
+	{"DMIC MUX1", "DMIC4", "DMIC4"},
+	{"DMIC MUX1", "DMIC5", "DMIC5"},
+	{"AMIC MUX1", "ADC1", "ADC1"},
+	{"AMIC MUX1", "ADC2", "ADC2"},
+	{"AMIC MUX1", "ADC3", "ADC3"},
+	{"AMIC MUX1", "ADC4", "ADC4"},
+	{"AMIC MUX1", "ADC5", "ADC5"},
+	{"AMIC MUX1", "ADC6", "ADC6"},
+
+	{"DMIC MUX2", "DMIC0", "DMIC0"},
+	{"DMIC MUX2", "DMIC1", "DMIC1"},
+	{"DMIC MUX2", "DMIC2", "DMIC2"},
+	{"DMIC MUX2", "DMIC3", "DMIC3"},
+	{"DMIC MUX2", "DMIC4", "DMIC4"},
+	{"DMIC MUX2", "DMIC5", "DMIC5"},
+	{"AMIC MUX2", "ADC1", "ADC1"},
+	{"AMIC MUX2", "ADC2", "ADC2"},
+	{"AMIC MUX2", "ADC3", "ADC3"},
+	{"AMIC MUX2", "ADC4", "ADC4"},
+	{"AMIC MUX2", "ADC5", "ADC5"},
+	{"AMIC MUX2", "ADC6", "ADC6"},
+
+	{"DMIC MUX3", "DMIC0", "DMIC0"},
+	{"DMIC MUX3", "DMIC1", "DMIC1"},
+	{"DMIC MUX3", "DMIC2", "DMIC2"},
+	{"DMIC MUX3", "DMIC3", "DMIC3"},
+	{"DMIC MUX3", "DMIC4", "DMIC4"},
+	{"DMIC MUX3", "DMIC5", "DMIC5"},
+	{"AMIC MUX3", "ADC1", "ADC1"},
+	{"AMIC MUX3", "ADC2", "ADC2"},
+	{"AMIC MUX3", "ADC3", "ADC3"},
+	{"AMIC MUX3", "ADC4", "ADC4"},
+	{"AMIC MUX3", "ADC5", "ADC5"},
+	{"AMIC MUX3", "ADC6", "ADC6"},
+
+	{"DMIC MUX4", "DMIC0", "DMIC0"},
+	{"DMIC MUX4", "DMIC1", "DMIC1"},
+	{"DMIC MUX4", "DMIC2", "DMIC2"},
+	{"DMIC MUX4", "DMIC3", "DMIC3"},
+	{"DMIC MUX4", "DMIC4", "DMIC4"},
+	{"DMIC MUX4", "DMIC5", "DMIC5"},
+	{"AMIC MUX4", "ADC1", "ADC1"},
+	{"AMIC MUX4", "ADC2", "ADC2"},
+	{"AMIC MUX4", "ADC3", "ADC3"},
+	{"AMIC MUX4", "ADC4", "ADC4"},
+	{"AMIC MUX4", "ADC5", "ADC5"},
+	{"AMIC MUX4", "ADC6", "ADC6"},
+
+	{"DMIC MUX5", "DMIC0", "DMIC0"},
+	{"DMIC MUX5", "DMIC1", "DMIC1"},
+	{"DMIC MUX5", "DMIC2", "DMIC2"},
+	{"DMIC MUX5", "DMIC3", "DMIC3"},
+	{"DMIC MUX5", "DMIC4", "DMIC4"},
+	{"DMIC MUX5", "DMIC5", "DMIC5"},
+	{"AMIC MUX5", "ADC1", "ADC1"},
+	{"AMIC MUX5", "ADC2", "ADC2"},
+	{"AMIC MUX5", "ADC3", "ADC3"},
+	{"AMIC MUX5", "ADC4", "ADC4"},
+	{"AMIC MUX5", "ADC5", "ADC5"},
+	{"AMIC MUX5", "ADC6", "ADC6"},
+
+	{"DMIC MUX6", "DMIC0", "DMIC0"},
+	{"DMIC MUX6", "DMIC1", "DMIC1"},
+	{"DMIC MUX6", "DMIC2", "DMIC2"},
+	{"DMIC MUX6", "DMIC3", "DMIC3"},
+	{"DMIC MUX6", "DMIC4", "DMIC4"},
+	{"DMIC MUX6", "DMIC5", "DMIC5"},
+	{"AMIC MUX6", "ADC1", "ADC1"},
+	{"AMIC MUX6", "ADC2", "ADC2"},
+	{"AMIC MUX6", "ADC3", "ADC3"},
+	{"AMIC MUX6", "ADC4", "ADC4"},
+	{"AMIC MUX6", "ADC5", "ADC5"},
+	{"AMIC MUX6", "ADC6", "ADC6"},
+
+	{"DMIC MUX7", "DMIC0", "DMIC0"},
+	{"DMIC MUX7", "DMIC1", "DMIC1"},
+	{"DMIC MUX7", "DMIC2", "DMIC2"},
+	{"DMIC MUX7", "DMIC3", "DMIC3"},
+	{"DMIC MUX7", "DMIC4", "DMIC4"},
+	{"DMIC MUX7", "DMIC5", "DMIC5"},
+	{"AMIC MUX7", "ADC1", "ADC1"},
+	{"AMIC MUX7", "ADC2", "ADC2"},
+	{"AMIC MUX7", "ADC3", "ADC3"},
+	{"AMIC MUX7", "ADC4", "ADC4"},
+	{"AMIC MUX7", "ADC5", "ADC5"},
+	{"AMIC MUX7", "ADC6", "ADC6"},
+
+	{"DMIC MUX8", "DMIC0", "DMIC0"},
+	{"DMIC MUX8", "DMIC1", "DMIC1"},
+	{"DMIC MUX8", "DMIC2", "DMIC2"},
+	{"DMIC MUX8", "DMIC3", "DMIC3"},
+	{"DMIC MUX8", "DMIC4", "DMIC4"},
+	{"DMIC MUX8", "DMIC5", "DMIC5"},
+	{"AMIC MUX8", "ADC1", "ADC1"},
+	{"AMIC MUX8", "ADC2", "ADC2"},
+	{"AMIC MUX8", "ADC3", "ADC3"},
+	{"AMIC MUX8", "ADC4", "ADC4"},
+	{"AMIC MUX8", "ADC5", "ADC5"},
+	{"AMIC MUX8", "ADC6", "ADC6"},
+
+	{"DMIC MUX10", "DMIC0", "DMIC0"},
+	{"DMIC MUX10", "DMIC1", "DMIC1"},
+	{"DMIC MUX10", "DMIC2", "DMIC2"},
+	{"DMIC MUX10", "DMIC3", "DMIC3"},
+	{"DMIC MUX10", "DMIC4", "DMIC4"},
+	{"DMIC MUX10", "DMIC5", "DMIC5"},
+	{"AMIC MUX10", "ADC1", "ADC1"},
+	{"AMIC MUX10", "ADC2", "ADC2"},
+	{"AMIC MUX10", "ADC3", "ADC3"},
+	{"AMIC MUX10", "ADC4", "ADC4"},
+	{"AMIC MUX10", "ADC5", "ADC5"},
+	{"AMIC MUX10", "ADC6", "ADC6"},
+
+	{"DMIC MUX11", "DMIC0", "DMIC0"},
+	{"DMIC MUX11", "DMIC1", "DMIC1"},
+	{"DMIC MUX11", "DMIC2", "DMIC2"},
+	{"DMIC MUX11", "DMIC3", "DMIC3"},
+	{"DMIC MUX11", "DMIC4", "DMIC4"},
+	{"DMIC MUX11", "DMIC5", "DMIC5"},
+	{"AMIC MUX11", "ADC1", "ADC1"},
+	{"AMIC MUX11", "ADC2", "ADC2"},
+	{"AMIC MUX11", "ADC3", "ADC3"},
+	{"AMIC MUX11", "ADC4", "ADC4"},
+	{"AMIC MUX11", "ADC5", "ADC5"},
+	{"AMIC MUX11", "ADC6", "ADC6"},
+
+	{"DMIC MUX12", "DMIC0", "DMIC0"},
+	{"DMIC MUX12", "DMIC1", "DMIC1"},
+	{"DMIC MUX12", "DMIC2", "DMIC2"},
+	{"DMIC MUX12", "DMIC3", "DMIC3"},
+	{"DMIC MUX12", "DMIC4", "DMIC4"},
+	{"DMIC MUX12", "DMIC5", "DMIC5"},
+	{"AMIC MUX12", "ADC1", "ADC1"},
+	{"AMIC MUX12", "ADC2", "ADC2"},
+	{"AMIC MUX12", "ADC3", "ADC3"},
+	{"AMIC MUX12", "ADC4", "ADC4"},
+	{"AMIC MUX12", "ADC5", "ADC5"},
+	{"AMIC MUX12", "ADC6", "ADC6"},
+
+	{"DMIC MUX13", "DMIC0", "DMIC0"},
+	{"DMIC MUX13", "DMIC1", "DMIC1"},
+	{"DMIC MUX13", "DMIC2", "DMIC2"},
+	{"DMIC MUX13", "DMIC3", "DMIC3"},
+	{"DMIC MUX13", "DMIC4", "DMIC4"},
+	{"DMIC MUX13", "DMIC5", "DMIC5"},
+	{"AMIC MUX13", "ADC1", "ADC1"},
+	{"AMIC MUX13", "ADC2", "ADC2"},
+	{"AMIC MUX13", "ADC3", "ADC3"},
+	{"AMIC MUX13", "ADC4", "ADC4"},
+	{"AMIC MUX13", "ADC5", "ADC5"},
+	{"AMIC MUX13", "ADC6", "ADC6"},
+	/* ADC Connections */
+	{"ADC1", NULL, "AMIC1"},
+	{"ADC2", NULL, "AMIC2"},
+	{"ADC3", NULL, "AMIC3"},
+	{"ADC4", NULL, "AMIC4"},
+	{"ADC5", NULL, "AMIC5"},
+	{"ADC6", NULL, "AMIC6"},
+
+	{"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"},
+	{"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"},
+	{"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"},
+	{"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"},
+	{"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"},
+	{"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"},
+	{"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"},
+	{"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"},
+	{"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"},
+	{"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"},
+	{"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"},
+	{"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"},
+	{"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"},
+	{"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"},
+	{"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"},
+	{"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP0"},
+	{"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP1"},
+	{"RX INT5_1 MIX1", NULL, "RX INT5_1 MIX1 INP2"},
+	{"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP0"},
+	{"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP1"},
+	{"RX INT6_1 MIX1", NULL, "RX INT6_1 MIX1 INP2"},
+	{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"},
+	{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"},
+	{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"},
+	{"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"},
+	{"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"},
+	{"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"},
+
+	{"RX INT0 SEC MIX", NULL, "RX INT0_1 MIX1"},
+	{"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"},
+	{"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"},
+	{"RX INT0 INTERP", NULL, "RX INT0 MIX2"},
+	{"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 INTERP"},
+	{"RX INT0 DAC", NULL, "RX INT0 DEM MUX"},
+	{"RX INT0 DAC", NULL, "RX_BIAS"},
+	{"EAR PA", NULL, "RX INT0 DAC"},
+	{"EAR", NULL, "EAR PA"},
+
+	{"SPL SRC0 MUX", "SRC_IN_HPHL", "RX INT1_1 MIX1"},
+	{"RX INT1 SPLINE MIX", NULL, "RX INT1_1 MIX1"},
+	{"RX INT1 SPLINE MIX", "HPHL Switch", "SPL SRC0 MUX"},
+	{"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"},
+	{"RX INT1 SPLINE MIX", NULL, "RX INT1_1 NATIVE MUX"},
+	{"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
+	{"RX INT1 SEC MIX", NULL, "RX INT1 SPLINE MIX"},
+	{"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
+	{"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
+	{"RX INT1 INTERP", NULL, "RX INT1 MIX2"},
+	{"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 INTERP"},
+	{"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
+	{"RX INT1 DAC", NULL, "RX_BIAS"},
+	{"HPHL PA", NULL, "RX INT1 DAC"},
+	{"HPHL", NULL, "HPHL PA"},
+
+	{"SPL SRC1 MUX", "SRC_IN_HPHR", "RX INT2_1 MIX1"},
+	{"RX INT2 SPLINE MIX", NULL, "RX INT2_1 MIX1"},
+	{"RX INT2 SPLINE MIX", "HPHR Switch", "SPL SRC1 MUX"},
+	{"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"},
+	{"RX INT2 SPLINE MIX", NULL, "RX INT2_1 NATIVE MUX"},
+	{"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
+	{"RX INT2 SEC MIX", NULL, "RX INT2 SPLINE MIX"},
+	{"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
+	{"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
+	{"RX INT2 INTERP", NULL, "RX INT2 MIX2"},
+	{"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 INTERP"},
+	{"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
+	{"RX INT2 DAC", NULL, "RX_BIAS"},
+	{"HPHR PA", NULL, "RX INT2 DAC"},
+	{"HPHR", NULL, "HPHR PA"},
+
+	{"SPL SRC0 MUX", "SRC_IN_LO1", "RX INT3_1 MIX1"},
+	{"RX INT3 SPLINE MIX", NULL, "RX INT3_1 MIX1"},
+	{"RX INT3 SPLINE MIX", "LO1 Switch", "SPL SRC0 MUX"},
+	{"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"},
+	{"RX INT3 SPLINE MIX", NULL, "RX INT3_1 NATIVE MUX"},
+	{"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
+	{"RX INT3 SEC MIX", NULL, "RX INT3 SPLINE MIX"},
+	{"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"},
+	{"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"},
+	{"RX INT3 INTERP", NULL, "RX INT3 MIX2"},
+	{"RX INT3 DAC", NULL, "RX INT3 INTERP"},
+	{"RX INT3 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT1 PA", NULL, "RX INT3 DAC"},
+	{"LINEOUT1", NULL, "LINEOUT1 PA"},
+
+	{"SPL SRC1 MUX", "SRC_IN_LO2", "RX INT4_1 MIX1"},
+	{"RX INT4 SPLINE MIX", NULL, "RX INT4_1 MIX1"},
+	{"RX INT4 SPLINE MIX", "LO2 Switch", "SPL SRC1 MUX"},
+	{"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"},
+	{"RX INT4 SPLINE MIX", NULL, "RX INT4_1 NATIVE MUX"},
+	{"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
+	{"RX INT4 SEC MIX", NULL, "RX INT4 SPLINE MIX"},
+	{"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"},
+	{"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"},
+	{"RX INT4 INTERP", NULL, "RX INT4 MIX2"},
+	{"RX INT4 DAC", NULL, "RX INT4 INTERP"},
+	{"RX INT4 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT2 PA", NULL, "RX INT4 DAC"},
+	{"LINEOUT2", NULL, "LINEOUT2 PA"},
+
+	{"SPL SRC2 MUX", "SRC_IN_LO3", "RX INT5_1 MIX1"},
+	{"RX INT5 SPLINE MIX", NULL, "RX INT5_1 MIX1"},
+	{"RX INT5 SPLINE MIX", "LO3 Switch", "SPL SRC2 MUX"},
+	{"RX INT5 SEC MIX", NULL, "RX INT5 SPLINE MIX"},
+	{"RX INT5 MIX2", NULL, "RX INT5 SEC MIX"},
+	{"RX INT5 INTERP", NULL, "RX INT5 MIX2"},
+
+	{"RX INT5 VBAT", "LO3 VBAT Enable", "RX INT5 INTERP"},
+	{"RX INT5 DAC", NULL, "RX INT5 VBAT"},
+
+	{"RX INT5 DAC", NULL, "RX INT5 INTERP"},
+	{"RX INT5 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT3 PA", NULL, "RX INT5 DAC"},
+	{"LINEOUT3", NULL, "LINEOUT3 PA"},
+
+	{"SPL SRC3 MUX", "SRC_IN_LO4", "RX INT6_1 MIX1"},
+	{"RX INT6 SPLINE MIX", NULL, "RX INT6_1 MIX1"},
+	{"RX INT6 SPLINE MIX", "LO4 Switch", "SPL SRC3 MUX"},
+	{"RX INT6 SEC MIX", NULL, "RX INT6 SPLINE MIX"},
+	{"RX INT6 MIX2", NULL, "RX INT6 SEC MIX"},
+	{"RX INT6 INTERP", NULL, "RX INT6 MIX2"},
+
+	{"RX INT6 VBAT", "LO4 VBAT Enable", "RX INT6 INTERP"},
+	{"RX INT6 DAC", NULL, "RX INT6 VBAT"},
+
+	{"RX INT6 DAC", NULL, "RX INT6 INTERP"},
+	{"RX INT6 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT4 PA", NULL, "RX INT6 DAC"},
+	{"LINEOUT4", NULL, "LINEOUT4 PA"},
+
+	{"SPL SRC2 MUX", "SRC_IN_SPKRL", "RX INT7_1 MIX1"},
+	{"RX INT7 SPLINE MIX", NULL, "RX INT7_1 MIX1"},
+	{"RX INT7 SPLINE MIX", "SPKRL Switch", "SPL SRC2 MUX"},
+	{"RX INT7 SEC MIX", NULL, "RX INT7 SPLINE MIX"},
+	{"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"},
+	{"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"},
+
+	{"RX INT7 INTERP", NULL, "RX INT7 MIX2"},
+
+	{"RX INT7 VBAT", "SPKRL VBAT Enable", "RX INT7 INTERP"},
+	{"RX INT7 CHAIN", NULL, "RX INT7 VBAT"},
+
+	{"RX INT7 CHAIN", NULL, "RX INT7 INTERP"},
+	{"RX INT7 CHAIN", NULL, "RX_BIAS"},
+	{"SPK1 OUT", NULL, "RX INT7 CHAIN"},
+
+	{"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"},
+	{"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"},
+	{"SPK1 OUT", NULL, "ANC SPK1 PA"},
+
+	{"SPL SRC3 MUX", "SRC_IN_SPKRR", "RX INT8_1 MIX1"},
+	{"RX INT8 SPLINE MIX", NULL, "RX INT8_1 MIX1"},
+	{"RX INT8 SPLINE MIX", "SPKRR Switch", "SPL SRC3 MUX"},
+	{"RX INT8 SEC MIX", NULL, "RX INT8 SPLINE MIX"},
+	{"RX INT8 INTERP", NULL, "RX INT8 SEC MIX"},
+
+	{"RX INT8 VBAT", "SPKRR VBAT Enable", "RX INT8 INTERP"},
+	{"RX INT8 CHAIN", NULL, "RX INT8 VBAT"},
+
+	{"RX INT8 CHAIN", NULL, "RX INT8 INTERP"},
+	{"RX INT8 CHAIN", NULL, "RX_BIAS"},
+	{"SPK2 OUT", NULL, "RX INT8 CHAIN"},
+
+	{"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"},
+	{"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"},
+	{"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"},
+	{"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"},
+	{"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"},
+	{"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"},
+
+	{"ANC HPHL Enable", "Switch", "ADC MUX10"},
+	{"ANC HPHL Enable", "Switch", "ADC MUX11"},
+	{"RX INT1 MIX2", NULL, "ANC HPHL Enable"},
+
+	{"ANC HPHR Enable", "Switch", "ADC MUX12"},
+	{"ANC HPHR Enable", "Switch", "ADC MUX13"},
+	{"RX INT2 MIX2", NULL, "ANC HPHR Enable"},
+
+	{"ANC EAR Enable", "Switch", "ADC MUX10"},
+	{"ANC EAR Enable", "Switch", "ADC MUX11"},
+	{"RX INT0 MIX2", NULL, "ANC EAR Enable"},
+
+	{"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"},
+	{"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"},
+	{"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"},
+
+	{"ANC LINEOUT1 Enable", "Switch", "ADC MUX10"},
+	{"ANC LINEOUT1 Enable", "Switch", "ADC MUX11"},
+	{"RX INT3 MIX2", NULL, "ANC LINEOUT1 Enable"},
+
+	{"ANC LINEOUT2 Enable", "Switch", "ADC MUX12"},
+	{"ANC LINEOUT2 Enable", "Switch", "ADC MUX13"},
+	{"RX INT4 MIX2", NULL, "ANC LINEOUT2 Enable"},
+
+	{"ANC EAR PA", NULL, "RX INT0 DAC"},
+	{"ANC EAR", NULL, "ANC EAR PA"},
+	{"ANC HPHL PA", NULL, "RX INT1 DAC"},
+	{"ANC HPHL", NULL, "ANC HPHL PA"},
+	{"ANC HPHR PA", NULL, "RX INT2 DAC"},
+	{"ANC HPHR", NULL, "ANC HPHR PA"},
+	{"ANC LINEOUT1 PA", NULL, "RX INT3 DAC"},
+	{"ANC LINEOUT1", NULL, "ANC LINEOUT1 PA"},
+	{"ANC LINEOUT2 PA", NULL, "RX INT4 DAC"},
+	{"ANC LINEOUT2", NULL, "ANC LINEOUT2 PA"},
+
+	/* SLIM_MUX("AIF1_PB", "AIF1 PB"),*/
+	{"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+	/* SLIM_MUX("AIF2_PB", "AIF2 PB"),*/
+	{"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+	/* SLIM_MUX("AIF3_PB", "AIF3 PB"),*/
+	{"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+	/* SLIM_MUX("AIF4_PB", "AIF4 PB"),*/
+	{"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"},
+
+	/* SLIM_MUX("AIF_MIX1_PB", "AIF MIX1 PB"),*/
+	{"SLIM RX0 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX1 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX2 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX3 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX4 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX5 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX6 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+	{"SLIM RX7 MUX", "AIF_MIX1_PB", "AIF MIX1 PB"},
+
+	{"SLIM RX0", NULL, "SLIM RX0 MUX"},
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+	{"RX INT0_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT0_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT0_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT0_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT0_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT0_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT0_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT0_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT0_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT0_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT0_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT0_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT0_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT0_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT0_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT0_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT0_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT0_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT0_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT0_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT0_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT0_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT0_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT0_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	/* MIXing path INT0 */
+	{"RX INT0_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT0_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT0_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT0_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT0_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT0_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT0_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT0_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"},
+
+	/* MIXing path INT1 */
+	{"RX INT1_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT1_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT1_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT1_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT1_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT1_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT1_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT1_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT1 SEC MIX", NULL, "RX INT1_2 MUX"},
+
+	/* MIXing path INT2 */
+	{"RX INT2_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT2_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT2_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT2_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT2_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT2_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT2_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT2_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT2 SEC MIX", NULL, "RX INT2_2 MUX"},
+
+	/* MIXing path INT3 */
+	{"RX INT3_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT3_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT3_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT3_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT3_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT3_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT3_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT3_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT3 SEC MIX", NULL, "RX INT3_2 MUX"},
+
+	/* MIXing path INT4 */
+	{"RX INT4_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT4_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT4_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT4_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT4_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT4_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT4_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT4_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT4 SEC MIX", NULL, "RX INT4_2 MUX"},
+
+	/* MIXing path INT5 */
+	{"RX INT5_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT5_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT5_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT5_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT5_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT5_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT5_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT5_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT5 SEC MIX", NULL, "RX INT5_2 MUX"},
+
+	/* MIXing path INT6 */
+	{"RX INT6_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT6_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT6_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT6_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT6_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT6_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT6_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT6_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT6 SEC MIX", NULL, "RX INT6_2 MUX"},
+
+	/* MIXing path INT7 */
+	{"RX INT7_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT7_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT7_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT7_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT7_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT7_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT7_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT7_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT7 SEC MIX", NULL, "RX INT7_2 MUX"},
+
+	/* MIXing path INT8 */
+	{"RX INT8_2 MUX", "RX0", "SLIM RX0"},
+	{"RX INT8_2 MUX", "RX1", "SLIM RX1"},
+	{"RX INT8_2 MUX", "RX2", "SLIM RX2"},
+	{"RX INT8_2 MUX", "RX3", "SLIM RX3"},
+	{"RX INT8_2 MUX", "RX4", "SLIM RX4"},
+	{"RX INT8_2 MUX", "RX5", "SLIM RX5"},
+	{"RX INT8_2 MUX", "RX6", "SLIM RX6"},
+	{"RX INT8_2 MUX", "RX7", "SLIM RX7"},
+	{"RX INT8 SEC MIX", NULL, "RX INT8_2 MUX"},
+
+	{"RX INT1_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT1_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT1_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT1_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT1_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT1_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT1_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT1_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT1_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT1_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT1_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT1_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT1_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT1_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT1_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT1_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT1_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT1_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT1_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT1_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT1_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT1_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT1_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT1_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX INT2_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT2_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT2_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT2_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT2_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT2_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT2_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT2_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT2_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT2_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT2_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT2_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT2_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT2_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT2_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT2_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT2_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT2_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT2_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT2_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT2_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT2_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT2_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT2_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT3_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT3_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT3_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT3_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT3_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT3_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT3_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT3_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT3_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT3_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT3_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT3_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT3_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT3_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT3_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT3_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT3_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT3_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT3_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT3_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT3_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT3_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT3_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT3_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT4_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT4_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT4_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT4_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT4_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT4_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT4_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT4_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT4_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT4_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT4_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT4_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT4_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT4_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT4_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT4_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT4_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT4_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT4_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT4_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT4_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT4_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT4_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT4_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT5_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT5_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT5_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT5_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT5_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT5_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT5_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT5_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT5_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT5_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT5_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT5_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT5_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT5_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT5_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT5_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT5_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT5_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT5_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT5_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT5_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT5_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT5_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT5_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT5_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT5_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT5_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT5_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT5_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT5_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT6_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT6_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT6_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT6_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT6_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT6_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT6_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT6_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT6_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT6_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT6_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT6_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT6_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT6_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT6_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT6_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT6_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT6_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT6_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT6_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT6_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT6_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT6_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT6_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT6_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT6_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT6_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT6_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT6_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT6_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT7_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT7_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT7_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT7_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT7_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT7_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT7_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT7_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT7_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT7_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT7_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT7_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT7_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT7_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT7_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT7_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT7_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT7_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT7_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT7_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT7_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT7_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT7_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT7_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT8_1 MIX1 INP0", "RX0", "SLIM RX0"},
+	{"RX INT8_1 MIX1 INP0", "RX1", "SLIM RX1"},
+	{"RX INT8_1 MIX1 INP0", "RX2", "SLIM RX2"},
+	{"RX INT8_1 MIX1 INP0", "RX3", "SLIM RX3"},
+	{"RX INT8_1 MIX1 INP0", "RX4", "SLIM RX4"},
+	{"RX INT8_1 MIX1 INP0", "RX5", "SLIM RX5"},
+	{"RX INT8_1 MIX1 INP0", "RX6", "SLIM RX6"},
+	{"RX INT8_1 MIX1 INP0", "RX7", "SLIM RX7"},
+	{"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT8_1 MIX1 INP1", "RX0", "SLIM RX0"},
+	{"RX INT8_1 MIX1 INP1", "RX1", "SLIM RX1"},
+	{"RX INT8_1 MIX1 INP1", "RX2", "SLIM RX2"},
+	{"RX INT8_1 MIX1 INP1", "RX3", "SLIM RX3"},
+	{"RX INT8_1 MIX1 INP1", "RX4", "SLIM RX4"},
+	{"RX INT8_1 MIX1 INP1", "RX5", "SLIM RX5"},
+	{"RX INT8_1 MIX1 INP1", "RX6", "SLIM RX6"},
+	{"RX INT8_1 MIX1 INP1", "RX7", "SLIM RX7"},
+	{"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT8_1 MIX1 INP2", "RX0", "SLIM RX0"},
+	{"RX INT8_1 MIX1 INP2", "RX1", "SLIM RX1"},
+	{"RX INT8_1 MIX1 INP2", "RX2", "SLIM RX2"},
+	{"RX INT8_1 MIX1 INP2", "RX3", "SLIM RX3"},
+	{"RX INT8_1 MIX1 INP2", "RX4", "SLIM RX4"},
+	{"RX INT8_1 MIX1 INP2", "RX5", "SLIM RX5"},
+	{"RX INT8_1 MIX1 INP2", "RX6", "SLIM RX6"},
+	{"RX INT8_1 MIX1 INP2", "RX7", "SLIM RX7"},
+	{"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	/* SRC0, SRC1 inputs to Sidetone RX Mixer
+	 * on RX0, RX1, RX2, RX3, RX4 and RX7 chains
+	 */
+	{"IIR0", NULL, "IIR0 INP0 MUX"},
+	{"IIR0 INP0 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP0 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP0 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP0 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP0 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP0 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP0 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP0 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP0 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP0 MUX", "RX0", "SLIM RX0"},
+	{"IIR0 INP0 MUX", "RX1", "SLIM RX1"},
+	{"IIR0 INP0 MUX", "RX2", "SLIM RX2"},
+	{"IIR0 INP0 MUX", "RX3", "SLIM RX3"},
+	{"IIR0 INP0 MUX", "RX4", "SLIM RX4"},
+	{"IIR0 INP0 MUX", "RX5", "SLIM RX5"},
+	{"IIR0 INP0 MUX", "RX6", "SLIM RX6"},
+	{"IIR0 INP0 MUX", "RX7", "SLIM RX7"},
+	{"IIR0", NULL, "IIR0 INP1 MUX"},
+	{"IIR0 INP1 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP1 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP1 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP1 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP1 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP1 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP1 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP1 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP1 MUX", "RX0", "SLIM RX0"},
+	{"IIR0 INP1 MUX", "RX1", "SLIM RX1"},
+	{"IIR0 INP1 MUX", "RX2", "SLIM RX2"},
+	{"IIR0 INP1 MUX", "RX3", "SLIM RX3"},
+	{"IIR0 INP1 MUX", "RX4", "SLIM RX4"},
+	{"IIR0 INP1 MUX", "RX5", "SLIM RX5"},
+	{"IIR0 INP1 MUX", "RX6", "SLIM RX6"},
+	{"IIR0 INP1 MUX", "RX7", "SLIM RX7"},
+	{"IIR0", NULL, "IIR0 INP2 MUX"},
+	{"IIR0 INP2 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP2 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP2 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP2 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP2 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP2 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP2 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP2 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP2 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP2 MUX", "RX0", "SLIM RX0"},
+	{"IIR0 INP2 MUX", "RX1", "SLIM RX1"},
+	{"IIR0 INP2 MUX", "RX2", "SLIM RX2"},
+	{"IIR0 INP2 MUX", "RX3", "SLIM RX3"},
+	{"IIR0 INP2 MUX", "RX4", "SLIM RX4"},
+	{"IIR0 INP2 MUX", "RX5", "SLIM RX5"},
+	{"IIR0 INP2 MUX", "RX6", "SLIM RX6"},
+	{"IIR0 INP2 MUX", "RX7", "SLIM RX7"},
+	{"IIR0", NULL, "IIR0 INP3 MUX"},
+	{"IIR0 INP3 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP3 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP3 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP3 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP3 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP3 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP3 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP3 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP3 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP3 MUX", "RX0", "SLIM RX0"},
+	{"IIR0 INP3 MUX", "RX1", "SLIM RX1"},
+	{"IIR0 INP3 MUX", "RX2", "SLIM RX2"},
+	{"IIR0 INP3 MUX", "RX3", "SLIM RX3"},
+	{"IIR0 INP3 MUX", "RX4", "SLIM RX4"},
+	{"IIR0 INP3 MUX", "RX5", "SLIM RX5"},
+	{"IIR0 INP3 MUX", "RX6", "SLIM RX6"},
+	{"IIR0 INP3 MUX", "RX7", "SLIM RX7"},
+
+	{"IIR1", NULL, "IIR1 INP0 MUX"},
+	{"IIR1 INP0 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP0 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP0 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP0 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP0 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP0 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP0 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP0 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP0 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP0 MUX", "RX0", "SLIM RX0"},
+	{"IIR1 INP0 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP0 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP0 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP0 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP0 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP0 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP0 MUX", "RX7", "SLIM RX7"},
+	{"IIR1", NULL, "IIR1 INP1 MUX"},
+	{"IIR1 INP1 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP1 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP1 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP1 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP1 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP1 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP1 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP1 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP1 MUX", "RX0", "SLIM RX0"},
+	{"IIR1 INP1 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP1 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP1 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP1 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP1 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP1 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP1 MUX", "RX7", "SLIM RX7"},
+	{"IIR1", NULL, "IIR1 INP2 MUX"},
+	{"IIR1 INP2 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP2 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP2 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP2 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP2 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP2 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP2 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP2 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP2 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP2 MUX", "RX0", "SLIM RX0"},
+	{"IIR1 INP2 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP2 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP2 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP2 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP2 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP2 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP2 MUX", "RX7", "SLIM RX7"},
+	{"IIR1", NULL, "IIR1 INP3 MUX"},
+	{"IIR1 INP3 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP3 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP3 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP3 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP3 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP3 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP3 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP3 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP3 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP3 MUX", "RX0", "SLIM RX0"},
+	{"IIR1 INP3 MUX", "RX1", "SLIM RX1"},
+	{"IIR1 INP3 MUX", "RX2", "SLIM RX2"},
+	{"IIR1 INP3 MUX", "RX3", "SLIM RX3"},
+	{"IIR1 INP3 MUX", "RX4", "SLIM RX4"},
+	{"IIR1 INP3 MUX", "RX5", "SLIM RX5"},
+	{"IIR1 INP3 MUX", "RX6", "SLIM RX6"},
+	{"IIR1 INP3 MUX", "RX7", "SLIM RX7"},
+
+	{"SRC0", NULL, "IIR0"},
+	{"SRC1", NULL, "IIR1"},
+	{"RX INT0 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT0 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT1 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT1 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT2 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT2 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT3 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT3 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT4 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT4 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT7 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT7 MIX2 INP", "SRC1", "SRC1"},
+};
+
+static int tasha_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u16 amic_reg;
+
+	if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+		amic_reg = WCD9335_ANA_AMIC1;
+	if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+		amic_reg = WCD9335_ANA_AMIC3;
+	if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE"))
+		amic_reg = WCD9335_ANA_AMIC5;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read(codec, amic_reg) & WCD9335_AMIC_PWR_LVL_MASK) >>
+			     WCD9335_AMIC_PWR_LVL_SHIFT;
+
+	return 0;
+}
+
+static int tasha_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u32 mode_val;
+	u16 amic_reg;
+
+	mode_val = ucontrol->value.enumerated.item[0];
+
+	dev_dbg(codec->dev, "%s: mode: %d\n",
+		__func__, mode_val);
+
+	if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+		amic_reg = WCD9335_ANA_AMIC1;
+	if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+		amic_reg = WCD9335_ANA_AMIC3;
+	if (!strcmp(kcontrol->id.name, "AMIC_5_6 PWR MODE"))
+		amic_reg = WCD9335_ANA_AMIC5;
+
+	snd_soc_update_bits(codec, amic_reg, WCD9335_AMIC_PWR_LVL_MASK,
+			    mode_val << WCD9335_AMIC_PWR_LVL_SHIFT);
+
+	return 0;
+}
+
+static int tasha_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tasha->hph_mode;
+	return 0;
+}
+
+static int tasha_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u32 mode_val;
+
+	mode_val = ucontrol->value.enumerated.item[0];
+
+	dev_dbg(codec->dev, "%s: mode: %d\n",
+		__func__, mode_val);
+
+	if (mode_val == 0) {
+		dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H HiFi\n",
+			__func__);
+		mode_val = CLS_H_HIFI;
+	}
+	tasha->hph_mode = mode_val;
+	return 0;
+}
+
+static const char *const tasha_conn_mad_text[] = {
+	"NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6",
+	"NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4",
+	"DMIC5", "NOTUSED3", "NOTUSED4"
+};
+
+static const struct soc_enum tasha_conn_mad_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_conn_mad_text),
+			    tasha_conn_mad_text);
+
+static int tasha_enable_ldo_h_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u8 val = 0;
+
+	if (codec)
+		val = snd_soc_read(codec, WCD9335_LDOH_MODE) & 0x80;
+
+	ucontrol->value.integer.value[0] = !!val;
+
+	return 0;
+}
+
+static int tasha_enable_ldo_h_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int value = ucontrol->value.integer.value[0];
+	bool enable;
+
+	enable = !!value;
+	if (codec)
+		tasha_codec_enable_standalone_ldo_h(codec, enable);
+
+	return 0;
+}
+
+static int tasha_mad_input_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 tasha_mad_input;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	tasha_mad_input = snd_soc_read(codec,
+				WCD9335_SOC_MAD_INP_SEL) & 0x0F;
+	ucontrol->value.integer.value[0] = tasha_mad_input;
+
+	dev_dbg(codec->dev,
+		"%s: tasha_mad_input = %s\n", __func__,
+		tasha_conn_mad_text[tasha_mad_input]);
+	return 0;
+}
+
+static int tasha_mad_input_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	u8 tasha_mad_input;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct snd_soc_card *card = codec->component.card;
+	char mad_amic_input_widget[6];
+	const char *mad_input_widget;
+	const char *source_widget = NULL;
+	u32 adc, i, mic_bias_found = 0;
+	int ret = 0;
+	char *mad_input;
+
+	tasha_mad_input = ucontrol->value.integer.value[0];
+
+	if (!strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED1") ||
+	    !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED2") ||
+	    !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED3") ||
+	    !strcmp(tasha_conn_mad_text[tasha_mad_input], "NOTUSED4")) {
+		dev_err(codec->dev,
+			"%s: Unsupported tasha_mad_input = %s\n",
+			__func__, tasha_conn_mad_text[tasha_mad_input]);
+		return -EINVAL;
+	}
+
+	if (strnstr(tasha_conn_mad_text[tasha_mad_input],
+		    "ADC", sizeof("ADC"))) {
+		mad_input = strpbrk(tasha_conn_mad_text[tasha_mad_input],
+				    "123456");
+		if (!mad_input) {
+			dev_err(codec->dev, "%s: Invalid MAD input %s\n",
+				__func__,
+				tasha_conn_mad_text[tasha_mad_input]);
+			return -EINVAL;
+		}
+		ret = kstrtouint(mad_input, 10, &adc);
+		if ((ret < 0) || (adc > 6)) {
+			dev_err(codec->dev,
+				"%s: Invalid ADC = %s\n", __func__,
+				tasha_conn_mad_text[tasha_mad_input]);
+			ret =  -EINVAL;
+		}
+
+		snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
+
+		mad_input_widget = mad_amic_input_widget;
+	} else {
+		/* DMIC type input widget*/
+		mad_input_widget = tasha_conn_mad_text[tasha_mad_input];
+	}
+
+	dev_dbg(codec->dev,
+		"%s: tasha input widget = %s\n", __func__,
+		mad_input_widget);
+
+	for (i = 0; i < card->num_of_dapm_routes; i++) {
+		if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) {
+			source_widget = card->of_dapm_routes[i].source;
+			if (!source_widget) {
+				dev_err(codec->dev,
+					"%s: invalid source widget\n",
+					__func__);
+				return -EINVAL;
+			}
+
+			if (strnstr(source_widget,
+				"MIC BIAS1", sizeof("MIC BIAS1"))) {
+				mic_bias_found = 1;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS2", sizeof("MIC BIAS2"))) {
+				mic_bias_found = 2;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS3", sizeof("MIC BIAS3"))) {
+				mic_bias_found = 3;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS4", sizeof("MIC BIAS4"))) {
+				mic_bias_found = 4;
+				break;
+			}
+		}
+	}
+
+	if (!mic_bias_found) {
+		dev_err(codec->dev,
+			"%s: mic bias source not found for input = %s\n",
+			__func__, mad_input_widget);
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev,
+		"%s: mic_bias found = %d\n", __func__,
+		mic_bias_found);
+
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_INP_SEL,
+			    0x0F, tasha_mad_input);
+	snd_soc_update_bits(codec, WCD9335_ANA_MAD_SETUP,
+			    0x07, mic_bias_found);
+
+	return 0;
+}
+
+static int tasha_pinctl_mode_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u16 ctl_reg;
+	u8 reg_val, pinctl_position;
+
+	pinctl_position = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	switch (pinctl_position >> 3) {
+	case 0:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0;
+		break;
+	case 1:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1;
+		break;
+	case 2:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2;
+		break;
+	case 3:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
+			__func__, pinctl_position);
+		return -EINVAL;
+	}
+
+	reg_val = snd_soc_read(codec, ctl_reg);
+	reg_val = (reg_val >> (pinctl_position & 0x07)) & 0x1;
+	ucontrol->value.integer.value[0] = reg_val;
+
+	return 0;
+}
+
+static int tasha_pinctl_mode_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 ctl_reg, cfg_reg;
+	u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask;
+
+	/* 1- high or low; 0- high Z */
+	pinctl_mode = ucontrol->value.integer.value[0];
+	pinctl_position = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	switch (pinctl_position >> 3) {
+	case 0:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_0;
+		break;
+	case 1:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_1;
+		break;
+	case 2:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_2;
+		break;
+	case 3:
+		ctl_reg = WCD9335_TEST_DEBUG_PIN_CTL_OE_3;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
+			__func__, pinctl_position);
+		return -EINVAL;
+	}
+
+	ctl_val = pinctl_mode << (pinctl_position & 0x07);
+	mask = 1 << (pinctl_position & 0x07);
+	snd_soc_update_bits(codec, ctl_reg, mask, ctl_val);
+
+	cfg_reg = WCD9335_TLMM_BIST_MODE_PINCFG + pinctl_position;
+	if (!pinctl_mode) {
+		if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+			cfg_val = 0x4;
+		else
+			cfg_val = 0xC;
+	} else {
+		cfg_val = 0;
+	}
+	snd_soc_update_bits(codec, cfg_reg, 0x07, cfg_val);
+
+	dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n",
+			__func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val);
+
+	return 0;
+}
+
+static void wcd_vbat_adc_out_config_2_0(struct wcd_vbat *vbat,
+					struct snd_soc_codec *codec)
+{
+	u8 val1, val2;
+
+	/*
+	 * Measure dcp1 by using "ALT" branch of band gap
+	 * voltage(Vbg) and use it in FAST mode
+	 */
+	snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x82, 0x82);
+	snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10);
+	snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x00);
+
+	snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x20);
+	/* Wait 100 usec after calibration select as Vbg */
+	usleep_range(100, 110);
+
+	snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40);
+	val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+	val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+	snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00);
+
+	vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+	snd_soc_update_bits(codec, WCD9335_BIAS_CTL, 0x40, 0x40);
+	/* Wait 100 usec after selecting Vbg as 1.05V */
+	usleep_range(100, 110);
+
+	snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x40);
+	val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+	val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+	snd_soc_update_bits(codec, WCD9335_VBADC_ADC_IO, 0x40, 0x00);
+
+	vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+	dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n",
+		__func__, vbat->dcp1, vbat->dcp2);
+
+	snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
+	/* Wait 100 usec after selecting Vbg as 0.85V */
+	usleep_range(100, 110);
+
+	snd_soc_update_bits(codec, WCD9335_VBADC_FE_CTRL, 0x20, 0x00);
+	snd_soc_update_bits(codec, WCD9335_VBADC_SUBBLOCK_EN, 0x20, 0x20);
+	snd_soc_update_bits(codec, WCD9335_ANA_VBADC, 0x80, 0x00);
+
+	snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x00);
+	snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01, 0x00);
+}
+
+static void wcd_vbat_adc_out_config_1_x(struct wcd_vbat *vbat,
+					struct snd_soc_codec *codec)
+{
+	u8 val1, val2;
+
+	/*
+	 * Measure dcp1 by applying band gap voltage(Vbg)
+	 * of 0.85V
+	 */
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0x20);
+	snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
+	snd_soc_write(codec, WCD9335_BIAS_VBG_FINE_ADJ, 0x05);
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0);
+	/* Wait 2 sec after enabling band gap bias */
+	usleep_range(2000000, 2000100);
+
+	snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x82);
+	snd_soc_write(codec, WCD9335_ANA_CLK_TOP, 0x87);
+	snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x10, 0x10);
+	snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0D);
+	snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x01);
+
+	snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80);
+	snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xDE);
+	snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x3C);
+	/* Wait 1 msec after calibration select as Vbg */
+	usleep_range(1000, 1100);
+
+	snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0);
+	val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+	val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+	snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
+
+	vbat->dcp1 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+	/*
+	 * Measure dcp2 by applying band gap voltage(Vbg)
+	 * of 1.05V
+	 */
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0);
+	snd_soc_write(codec, WCD9335_BIAS_CTL, 0x68);
+	/* Wait 2 msec after selecting Vbg as 1.05V */
+	usleep_range(2000, 2100);
+
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
+	/* Wait 1 sec after enabling band gap bias */
+	usleep_range(1000000, 1000100);
+
+	snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0xC0);
+	val1 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTMSB);
+	val2 = snd_soc_read(codec, WCD9335_VBADC_ADC_DOUTLSB);
+	snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
+
+	vbat->dcp2 = (((val1 & 0xFF) << 3) | (val2 & 0x07));
+
+	dev_dbg(codec->dev, "%s: dcp1:0x%x, dcp2:0x%x\n",
+		__func__, vbat->dcp1, vbat->dcp2);
+
+	/* Reset the Vbat ADC configuration */
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0x80);
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0xC0);
+
+	snd_soc_write(codec, WCD9335_BIAS_CTL, 0x28);
+	/* Wait 2 msec after selecting Vbg as 0.85V */
+	usleep_range(2000, 2100);
+
+	snd_soc_write(codec, WCD9335_ANA_BIAS, 0xA0);
+	/* Wait 1 sec after enabling band gap bias */
+	usleep_range(1000000, 1000100);
+
+	snd_soc_write(codec, WCD9335_VBADC_FE_CTRL, 0x1C);
+	snd_soc_write(codec, WCD9335_VBADC_SUBBLOCK_EN, 0xFE);
+	snd_soc_write(codec, WCD9335_VBADC_ADC_IO, 0x80);
+	snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00);
+
+	snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_DEBUG1, 0x00);
+	snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_PATH_CTL, 0x00);
+	snd_soc_write(codec, WCD9335_CDC_VBAT_VBAT_CFG, 0x0A);
+}
+
+static void wcd_vbat_adc_out_config(struct wcd_vbat *vbat,
+				struct snd_soc_codec *codec)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!vbat->adc_config) {
+		tasha_cdc_mclk_enable(codec, true, false);
+
+		if (TASHA_IS_2_0(wcd9xxx))
+			wcd_vbat_adc_out_config_2_0(vbat, codec);
+		else
+			wcd_vbat_adc_out_config_1_x(vbat, codec);
+
+		tasha_cdc_mclk_enable(codec, false, false);
+		vbat->adc_config = true;
+	}
+}
+
+static int tasha_update_vbat_reg_config(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct firmware_cal *hwdep_cal = NULL;
+	struct vbat_monitor_reg *vbat_reg_ptr = NULL;
+	const void *data;
+	size_t cal_size, vbat_size_remaining;
+	int ret = 0, i;
+	u32 vbat_writes_size = 0;
+	u16 reg;
+	u8 mask, val, old_val;
+
+	hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_VBAT_CAL);
+	if (hwdep_cal) {
+		data = hwdep_cal->data;
+		cal_size = hwdep_cal->size;
+		dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+			__func__);
+	} else {
+		dev_err(codec->dev, "%s: Vbat cal not received\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_size < sizeof(*vbat_reg_ptr)) {
+		dev_err(codec->dev,
+			"%s: Incorrect size %zd for Vbat Cal, expected %zd\n",
+			__func__, cal_size, sizeof(*vbat_reg_ptr));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	vbat_reg_ptr = (struct vbat_monitor_reg *) (data);
+
+	if (!vbat_reg_ptr) {
+		dev_err(codec->dev,
+			"%s: Invalid calibration data for Vbat\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	vbat_writes_size = vbat_reg_ptr->size;
+	vbat_size_remaining = cal_size - sizeof(u32);
+	dev_dbg(codec->dev, "%s: vbat_writes_sz: %d, vbat_sz_remaining: %zd\n",
+			__func__, vbat_writes_size, vbat_size_remaining);
+
+	if ((vbat_writes_size * TASHA_PACKED_REG_SIZE)
+					> vbat_size_remaining) {
+		pr_err("%s: Incorrect Vbat calibration data\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	for (i = 0 ; i < vbat_writes_size; i++) {
+		TASHA_CODEC_UNPACK_ENTRY(vbat_reg_ptr->writes[i],
+					reg, mask, val);
+		old_val = snd_soc_read(codec, reg);
+		snd_soc_write(codec, reg, (old_val & ~mask) | (val & mask));
+	}
+
+done:
+	return ret;
+}
+
+static int tasha_vbat_adc_data_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	wcd_vbat_adc_out_config(&tasha->vbat, codec);
+
+	ucontrol->value.integer.value[0] = tasha->vbat.dcp1;
+	ucontrol->value.integer.value[1] = tasha->vbat.dcp2;
+
+	dev_dbg(codec->dev,
+		"%s: Vbat ADC output values, Dcp1 : %lu, Dcp2: %lu\n",
+		__func__, ucontrol->value.integer.value[0],
+		ucontrol->value.integer.value[1]);
+
+	return 0;
+}
+
+static const char * const tasha_vbat_gsm_mode_text[] = {
+	"OFF", "ON"};
+
+static const struct soc_enum tasha_vbat_gsm_mode_enum =
+	SOC_ENUM_SINGLE_EXT(2, tasha_vbat_gsm_mode_text);
+
+static int tasha_vbat_gsm_mode_func_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ucontrol->value.integer.value[0] =
+		((snd_soc_read(codec, WCD9335_CDC_VBAT_VBAT_CFG) & 0x04) ?
+		  1 : 0);
+
+	dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int tasha_vbat_gsm_mode_func_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	dev_dbg(codec->dev, "%s: value: %lu\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	/* Set Vbat register configuration for GSM mode bit based on value */
+	if (ucontrol->value.integer.value[0])
+		snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG,
+						0x04, 0x04);
+	else
+		snd_soc_update_bits(codec, WCD9335_CDC_VBAT_VBAT_CFG,
+						0x04, 0x00);
+
+	return 0;
+}
+
+static int tasha_codec_vbat_enable_event(struct snd_soc_dapm_widget *w,
+				struct snd_kcontrol *kcontrol,
+				int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u16 vbat_path_ctl, vbat_cfg, vbat_path_cfg;
+
+	vbat_path_ctl = WCD9335_CDC_VBAT_VBAT_PATH_CTL;
+	vbat_cfg = WCD9335_CDC_VBAT_VBAT_CFG;
+	vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1;
+
+	if (!strcmp(w->name, "RX INT8 VBAT"))
+		vbat_path_cfg = WCD9335_CDC_RX8_RX_PATH_CFG1;
+	else if (!strcmp(w->name, "RX INT7 VBAT"))
+		vbat_path_cfg = WCD9335_CDC_RX7_RX_PATH_CFG1;
+	else if (!strcmp(w->name, "RX INT6 VBAT"))
+		vbat_path_cfg = WCD9335_CDC_RX6_RX_PATH_CFG1;
+	else if (!strcmp(w->name, "RX INT5 VBAT"))
+		vbat_path_cfg = WCD9335_CDC_RX5_RX_PATH_CFG1;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = tasha_update_vbat_reg_config(codec);
+		if (ret) {
+			dev_dbg(codec->dev,
+				"%s : VBAT isn't calibrated, So not enabling it\n",
+				__func__);
+			return 0;
+		}
+		snd_soc_write(codec, WCD9335_ANA_VBADC, 0x80);
+		snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x02);
+		snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x10);
+		snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x01);
+		tasha->vbat.is_enabled = true;
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (tasha->vbat.is_enabled) {
+			snd_soc_update_bits(codec, vbat_cfg, 0x01, 0x00);
+			snd_soc_update_bits(codec, vbat_path_ctl, 0x10, 0x00);
+			snd_soc_update_bits(codec, vbat_path_cfg, 0x02, 0x00);
+			snd_soc_write(codec, WCD9335_ANA_VBADC, 0x00);
+			tasha->vbat.is_enabled = false;
+		}
+		break;
+	};
+
+	return ret;
+}
+
+static const char * const rx_hph_mode_mux_text[] = {
+	"CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI"
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+			    rx_hph_mode_mux_text);
+
+static const char * const amic_pwr_lvl_text[] = {
+	"LOW_PWR", "DEFAULT", "HIGH_PERF"
+};
+
+static const struct soc_enum amic_pwr_lvl_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(amic_pwr_lvl_text),
+			    amic_pwr_lvl_text);
+
+static const struct snd_kcontrol_new tasha_snd_controls[] = {
+	SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD9335_CDC_RX0_RX_VOL_CTL,
+		0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD9335_CDC_RX1_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD9335_CDC_RX2_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD9335_CDC_RX3_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD9335_CDC_RX4_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX5 Digital Volume", WCD9335_CDC_RX5_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX6 Digital Volume", WCD9335_CDC_RX6_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD9335_CDC_RX7_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD9335_CDC_RX8_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume",
+			  WCD9335_CDC_RX0_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume",
+			  WCD9335_CDC_RX1_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume",
+			  WCD9335_CDC_RX2_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume",
+			  WCD9335_CDC_RX3_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume",
+			  WCD9335_CDC_RX4_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX5 Mix Digital Volume",
+			  WCD9335_CDC_RX5_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX6 Mix Digital Volume",
+			  WCD9335_CDC_RX6_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume",
+			  WCD9335_CDC_RX7_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume",
+			  WCD9335_CDC_RX8_RX_VOL_MIX_CTL,
+			  0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+
+	SOC_SINGLE_SX_TLV("DEC0 Volume", WCD9335_CDC_TX0_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC1 Volume", WCD9335_CDC_TX1_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC2 Volume", WCD9335_CDC_TX2_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC3 Volume", WCD9335_CDC_TX3_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC4 Volume", WCD9335_CDC_TX4_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC5 Volume", WCD9335_CDC_TX5_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC6 Volume", WCD9335_CDC_TX6_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC7 Volume", WCD9335_CDC_TX7_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC8 Volume", WCD9335_CDC_TX8_TX_VOL_CTL, 0,
+					  -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("IIR0 INP0 Volume",
+			  WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR0 INP1 Volume",
+			  WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR0 INP2 Volume",
+			  WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR0 INP3 Volume",
+			  WCD9335_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP0 Volume",
+			  WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP1 Volume",
+			  WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP2 Volume",
+			  WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84,
+			  40, digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP3 Volume",
+			  WCD9335_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84,
+			  40, digital_gain),
+
+	SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tasha_get_anc_slot,
+		       tasha_put_anc_slot),
+	SOC_ENUM_EXT("ANC Function", tasha_anc_func_enum, tasha_get_anc_func,
+		     tasha_put_anc_func),
+
+	SOC_ENUM_EXT("CLK MODE", tasha_clkmode_enum, tasha_get_clkmode,
+		     tasha_put_clkmode),
+
+	SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
+	SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+	SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+	SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+	SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+	SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+	SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+	SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+	SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+
+	SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum),
+	SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum),
+	SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum),
+	SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum),
+	SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum),
+	SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum),
+	SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum),
+	SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum),
+	SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum),
+	SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum),
+	SOC_ENUM("RX INT5_1 HPF cut off", cf_int5_1_enum),
+	SOC_ENUM("RX INT5_2 HPF cut off", cf_int5_2_enum),
+	SOC_ENUM("RX INT6_1 HPF cut off", cf_int6_1_enum),
+	SOC_ENUM("RX INT6_2 HPF cut off", cf_int6_2_enum),
+	SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum),
+	SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum),
+	SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
+	SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+
+	SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+	SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+	tasha_get_iir_enable_audio_mixer, tasha_put_iir_enable_audio_mixer),
+
+	SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+	tasha_get_iir_band_audio_mixer, tasha_put_iir_band_audio_mixer),
+
+	SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP5 Switch", SND_SOC_NOPM, COMPANDER_5, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP6 Switch", SND_SOC_NOPM, COMPANDER_6, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+	SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
+		       tasha_get_compander, tasha_set_compander),
+
+	SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+		       tasha_rx_hph_mode_get, tasha_rx_hph_mode_put),
+
+	SOC_ENUM_EXT("MAD Input", tasha_conn_mad_enum,
+		     tasha_mad_input_get, tasha_mad_input_put),
+	SOC_SINGLE_EXT("LDO_H Enable", SND_SOC_NOPM, 0, 1, 0,
+			tasha_enable_ldo_h_get, tasha_enable_ldo_h_put),
+
+	SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0,
+		       tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+	SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0,
+		       tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+	SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0,
+		       tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+	SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0,
+		       tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+	SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0,
+		       tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+
+	SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0,
+		       tasha_pinctl_mode_get, tasha_pinctl_mode_put),
+	SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum,
+		       tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put),
+	SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum,
+		       tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put),
+	SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum,
+		       tasha_amic_pwr_lvl_get, tasha_amic_pwr_lvl_put),
+
+	SOC_SINGLE_MULTI_EXT("Vbat ADC data", SND_SOC_NOPM, 0, 0xFFFF, 0, 2,
+			tasha_vbat_adc_data_get, NULL),
+
+	SOC_ENUM_EXT("GSM mode Enable", tasha_vbat_gsm_mode_enum,
+			tasha_vbat_gsm_mode_func_get,
+			tasha_vbat_gsm_mode_func_put),
+};
+
+static int tasha_put_dec_enum(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val;
+	u16 mic_sel_reg;
+	u8 mic_sel;
+
+	val = ucontrol->value.enumerated.item[0];
+	if (val > e->items - 1)
+		return -EINVAL;
+
+	dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+		widget->name, val);
+
+	switch (e->reg) {
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+		mic_sel_reg = WCD9335_CDC_TX0_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+		mic_sel_reg = WCD9335_CDC_TX1_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+		mic_sel_reg = WCD9335_CDC_TX2_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+		mic_sel_reg = WCD9335_CDC_TX3_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0:
+		mic_sel_reg = WCD9335_CDC_TX4_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0:
+		mic_sel_reg = WCD9335_CDC_TX5_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0:
+		mic_sel_reg = WCD9335_CDC_TX6_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0:
+		mic_sel_reg = WCD9335_CDC_TX7_TX_PATH_CFG0;
+		break;
+	case WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0:
+		mic_sel_reg = WCD9335_CDC_TX8_TX_PATH_CFG0;
+		break;
+	default:
+		dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n",
+			__func__, e->reg);
+		return -EINVAL;
+	}
+
+	/* ADC: 0, DMIC: 1 */
+	mic_sel = val ? 0x0 : 0x1;
+	snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7);
+
+	return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tasha_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val;
+	unsigned short look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0;
+
+	val = ucontrol->value.enumerated.item[0];
+	if (val >= e->items)
+		return -EINVAL;
+
+	dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+		widget->name, val);
+
+	if (e->reg == WCD9335_CDC_RX0_RX_PATH_SEC0)
+		look_ahead_dly_reg = WCD9335_CDC_RX0_RX_PATH_CFG0;
+	else if (e->reg == WCD9335_CDC_RX1_RX_PATH_SEC0)
+		look_ahead_dly_reg = WCD9335_CDC_RX1_RX_PATH_CFG0;
+	else if (e->reg == WCD9335_CDC_RX2_RX_PATH_SEC0)
+		look_ahead_dly_reg = WCD9335_CDC_RX2_RX_PATH_CFG0;
+
+	/* Set Look Ahead Delay */
+	snd_soc_update_bits(codec, look_ahead_dly_reg,
+			    0x08, (val ? 0x08 : 0x00));
+	/* Set DEM INP Select */
+	return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tasha_ear_pa_gain_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ear_pa_gain = snd_soc_read(codec, WCD9335_ANA_EAR);
+
+	ear_pa_gain = (ear_pa_gain & 0x70) >> 4;
+
+	ucontrol->value.integer.value[0] = ear_pa_gain;
+
+	dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__,
+		ear_pa_gain);
+
+	return 0;
+}
+
+static int tasha_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0]  = %ld\n",
+			__func__, ucontrol->value.integer.value[0]);
+
+	ear_pa_gain =  ucontrol->value.integer.value[0] << 4;
+
+	snd_soc_update_bits(codec, WCD9335_ANA_EAR, 0x70, ear_pa_gain);
+	return 0;
+}
+
+static int tasha_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tasha->ear_spkr_gain;
+
+	dev_dbg(codec->dev, "%s: ear_spkr_gain = %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int tasha_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0]  = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	tasha->ear_spkr_gain =  ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int tasha_config_compander(struct snd_soc_codec *codec, int interp_n,
+				  int event)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int comp;
+	u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+	/* EAR does not have compander */
+	if (!interp_n)
+		return 0;
+
+	comp = interp_n - 1;
+	dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n",
+		__func__, event, comp + 1, tasha->comp_enabled[comp]);
+
+	if (!tasha->comp_enabled[comp])
+		return 0;
+
+	comp_ctl0_reg = WCD9335_CDC_COMPANDER1_CTL0 + (comp * 8);
+	rx_path_cfg0_reg = WCD9335_CDC_RX1_RX_PATH_CFG0 + (comp * 20);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		/* Enable Compander Clock */
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+		snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04);
+		snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00);
+	}
+
+	return 0;
+}
+
+static int tasha_codec_config_mad(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	int idx;
+	const struct firmware *fw;
+	struct firmware_cal *hwdep_cal = NULL;
+	struct wcd_mad_audio_cal *mad_cal = NULL;
+	const void *data;
+	const char *filename = TASHA_MAD_AUDIO_FIRMWARE_PATH;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	size_t cal_size;
+
+	hwdep_cal = wcdcal_get_fw_cal(tasha->fw_data, WCD9XXX_MAD_CAL);
+	if (hwdep_cal) {
+		data = hwdep_cal->data;
+		cal_size = hwdep_cal->size;
+		dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+			__func__);
+	} else {
+		ret = request_firmware(&fw, filename, codec->dev);
+		if (ret || !fw) {
+			dev_err(codec->dev,
+				"%s: MAD firmware acquire failed, err = %d\n",
+				__func__, ret);
+			return -ENODEV;
+		}
+		data = fw->data;
+		cal_size = fw->size;
+		dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+			__func__);
+	}
+
+	if (cal_size < sizeof(*mad_cal)) {
+		dev_err(codec->dev,
+			"%s: Incorrect size %zd for MAD Cal, expected %zd\n",
+			__func__, cal_size, sizeof(*mad_cal));
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	mad_cal = (struct wcd_mad_audio_cal *) (data);
+	if (!mad_cal) {
+		dev_err(codec->dev,
+			"%s: Invalid calibration data\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	snd_soc_write(codec, WCD9335_SOC_MAD_MAIN_CTL_2,
+		      mad_cal->microphone_info.cycle_time);
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_MAIN_CTL_1, 0xFF << 3,
+			    ((uint16_t)mad_cal->microphone_info.settle_time)
+			    << 3);
+
+	/* Audio */
+	snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_8,
+		      mad_cal->audio_info.rms_omit_samples);
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_1,
+			    0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03 << 2,
+			    mad_cal->audio_info.detection_mechanism << 2);
+	snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_7,
+		      mad_cal->audio_info.rms_diff_threshold & 0x3F);
+	snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_5,
+		      mad_cal->audio_info.rms_threshold_lsb);
+	snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_CTL_6,
+		      mad_cal->audio_info.rms_threshold_msb);
+
+	for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients);
+	     idx++) {
+		snd_soc_update_bits(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_PTR,
+				    0x3F, idx);
+		snd_soc_write(codec, WCD9335_SOC_MAD_AUDIO_IIR_CTL_VAL,
+			      mad_cal->audio_info.iir_coefficients[idx]);
+		dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x",
+			__func__, idx,
+			mad_cal->audio_info.iir_coefficients[idx]);
+	}
+
+	/* Beacon */
+	snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_8,
+		      mad_cal->beacon_info.rms_omit_samples);
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_1,
+			    0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_CTL_2, 0x03 << 2,
+			    mad_cal->beacon_info.detection_mechanism << 2);
+	snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_7,
+		      mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+	snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_5,
+		      mad_cal->beacon_info.rms_threshold_lsb);
+	snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_CTL_6,
+		      mad_cal->beacon_info.rms_threshold_msb);
+
+	for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients);
+	     idx++) {
+		snd_soc_update_bits(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_PTR,
+				    0x3F, idx);
+		snd_soc_write(codec, WCD9335_SOC_MAD_BEACON_IIR_CTL_VAL,
+			      mad_cal->beacon_info.iir_coefficients[idx]);
+		dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x",
+			__func__, idx,
+			mad_cal->beacon_info.iir_coefficients[idx]);
+	}
+
+	/* Ultrasound */
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_1,
+			    0x07 << 4,
+			    mad_cal->ultrasound_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, WCD9335_SOC_MAD_ULTR_CTL_2, 0x03 << 2,
+			    mad_cal->ultrasound_info.detection_mechanism << 2);
+	snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_7,
+		      mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+	snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_5,
+		      mad_cal->ultrasound_info.rms_threshold_lsb);
+	snd_soc_write(codec, WCD9335_SOC_MAD_ULTR_CTL_6,
+		      mad_cal->ultrasound_info.rms_threshold_msb);
+
+done:
+	if (!hwdep_cal)
+		release_firmware(fw);
+
+	return ret;
+}
+
+static int tasha_codec_enable_mad(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+
+	dev_dbg(codec->dev,
+		"%s: event = %d\n", __func__, event);
+
+	/* Return if CPE INPUT is DEC1 */
+	if (snd_soc_read(codec, WCD9335_CPE_SS_SVA_CFG) & 0x01)
+		return ret;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+
+		/* Turn on MAD clk */
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+				    0x01, 0x01);
+
+		/* Undo reset for MAD */
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+				    0x02, 0x00);
+		ret = tasha_codec_config_mad(codec);
+		if (ret)
+			dev_err(codec->dev,
+				"%s: Failed to config MAD, err = %d\n",
+				__func__, ret);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* Reset the MAD block */
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+				    0x02, 0x02);
+		/* Turn off MAD clk */
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_MAD_CTL,
+				    0x01, 0x00);
+		break;
+	}
+
+	return ret;
+}
+
+static int tasha_codec_configure_cpe_input(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev,
+		"%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Configure CPE input as DEC1 */
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG,
+				    0x01, 0x01);
+
+		/* Configure DEC1 Tx out with sample rate as 16K */
+		snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL,
+				    0x0F, 0x01);
+
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* Reset DEC1 Tx out sample rate */
+		snd_soc_update_bits(codec, WCD9335_CDC_TX1_TX_PATH_CTL,
+				    0x0F, 0x04);
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_SVA_CFG,
+				    0x01, 0x00);
+
+		break;
+	}
+
+	return 0;
+}
+
+
+static int tasha_codec_aif4_mixer_switch_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+			dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+	if (test_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask))
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int tasha_codec_aif4_mixer_switch_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+			dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: AIF4 switch value = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	if (ucontrol->value.integer.value[0]) {
+		snd_soc_dapm_mixer_update_power(widget->dapm,
+						kcontrol, 1, update);
+		set_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask);
+	} else {
+		snd_soc_dapm_mixer_update_power(widget->dapm,
+						kcontrol, 0, update);
+		clear_bit(AIF4_SWITCH_VALUE, &tasha_p->status_mask);
+	}
+
+	return 1;
+}
+
+static const char * const tasha_ear_pa_gain_text[] = {
+	"G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+	"G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB"
+};
+
+static const char * const tasha_ear_spkr_pa_gain_text[] = {
+	"G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB", "G_4_DB",
+	"G_5_DB", "G_6_DB"
+};
+
+static const struct soc_enum tasha_ear_pa_gain_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_pa_gain_text),
+			tasha_ear_pa_gain_text);
+
+static const struct soc_enum tasha_ear_spkr_pa_gain_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tasha_ear_spkr_pa_gain_text),
+			    tasha_ear_spkr_pa_gain_text);
+
+static const struct snd_kcontrol_new tasha_analog_gain_controls[] = {
+	SOC_ENUM_EXT("EAR PA Gain", tasha_ear_pa_gain_enum,
+		tasha_ear_pa_gain_get, tasha_ear_pa_gain_put),
+
+	SOC_ENUM_EXT("EAR SPKR PA Gain", tasha_ear_spkr_pa_gain_enum,
+		     tasha_ear_spkr_pa_gain_get, tasha_ear_spkr_pa_gain_put),
+
+	SOC_SINGLE_TLV("HPHL Volume", WCD9335_HPH_L_EN, 0, 20, 1,
+		line_gain),
+	SOC_SINGLE_TLV("HPHR Volume", WCD9335_HPH_R_EN, 0, 20, 1,
+		line_gain),
+	SOC_SINGLE_TLV("LINEOUT1 Volume", WCD9335_DIFF_LO_LO1_COMPANDER,
+			3, 16, 1, line_gain),
+	SOC_SINGLE_TLV("LINEOUT2 Volume", WCD9335_DIFF_LO_LO2_COMPANDER,
+			3, 16, 1, line_gain),
+	SOC_SINGLE_TLV("LINEOUT3 Volume", WCD9335_SE_LO_LO3_GAIN, 0, 20, 1,
+			line_gain),
+	SOC_SINGLE_TLV("LINEOUT4 Volume", WCD9335_SE_LO_LO4_GAIN, 0, 20, 1,
+			line_gain),
+
+	SOC_SINGLE_TLV("ADC1 Volume", WCD9335_ANA_AMIC1, 0, 20, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC2 Volume", WCD9335_ANA_AMIC2, 0, 20, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC3 Volume", WCD9335_ANA_AMIC3, 0, 20, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC4 Volume", WCD9335_ANA_AMIC4, 0, 20, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC5 Volume", WCD9335_ANA_AMIC5, 0, 20, 0,
+			analog_gain),
+	SOC_SINGLE_TLV("ADC6 Volume", WCD9335_ANA_AMIC6, 0, 20, 0,
+			analog_gain),
+};
+
+static const char * const spl_src0_mux_text[] = {
+	"ZERO", "SRC_IN_HPHL", "SRC_IN_LO1",
+};
+
+static const char * const spl_src1_mux_text[] = {
+	"ZERO", "SRC_IN_HPHR", "SRC_IN_LO2",
+};
+
+static const char * const spl_src2_mux_text[] = {
+	"ZERO", "SRC_IN_LO3", "SRC_IN_SPKRL",
+};
+
+static const char * const spl_src3_mux_text[] = {
+	"ZERO", "SRC_IN_LO4", "SRC_IN_SPKRR",
+};
+
+static const char * const rx_int0_7_mix_mux_text[] = {
+	"ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+	"RX6", "RX7", "PROXIMITY"
+};
+
+static const char * const rx_int_mix_mux_text[] = {
+	"ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+	"RX6", "RX7"
+};
+
+static const char * const rx_prim_mix_text[] = {
+	"ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+	"RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+	"ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const sb_tx0_mux_text[] = {
+	"ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192"
+};
+
+static const char * const sb_tx1_mux_text[] = {
+	"ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192"
+};
+
+static const char * const sb_tx2_mux_text[] = {
+	"ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192"
+};
+
+static const char * const sb_tx3_mux_text[] = {
+	"ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192"
+};
+
+static const char * const sb_tx4_mux_text[] = {
+	"ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192"
+};
+
+static const char * const sb_tx5_mux_text[] = {
+	"ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192"
+};
+
+static const char * const sb_tx6_mux_text[] = {
+	"ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192"
+};
+
+static const char * const sb_tx7_mux_text[] = {
+	"ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192"
+};
+
+static const char * const sb_tx8_mux_text[] = {
+	"ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
+};
+
+static const char * const sb_tx9_mux_text[] = {
+	"ZERO", "DEC7", "DEC7_192"
+};
+
+static const char * const sb_tx10_mux_text[] = {
+	"ZERO", "DEC6", "DEC6_192"
+};
+
+static const char * const sb_tx11_mux_text[] = {
+	"DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST"
+};
+
+static const char * const sb_tx11_inp1_mux_text[] = {
+	"ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4",
+	"DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12"
+};
+
+static const char * const sb_tx13_mux_text[] = {
+	"ZERO", "DEC5", "DEC5_192"
+};
+
+static const char * const tx13_inp_mux_text[] = {
+	"CDC_DEC_5", "MAD_BRDCST", "CPE_TX_PP"
+};
+
+static const char * const iir_inp_mux_text[] = {
+	"ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6",
+	"DEC7", "DEC8",	"RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+	"NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_interp_mux_text[] = {
+	"ZERO", "RX INT0 MIX2",
+};
+
+static const char * const rx_int1_interp_mux_text[] = {
+	"ZERO", "RX INT1 MIX2",
+};
+
+static const char * const rx_int2_interp_mux_text[] = {
+	"ZERO", "RX INT2 MIX2",
+};
+
+static const char * const rx_int3_interp_mux_text[] = {
+	"ZERO", "RX INT3 MIX2",
+};
+
+static const char * const rx_int4_interp_mux_text[] = {
+	"ZERO", "RX INT4 MIX2",
+};
+
+static const char * const rx_int5_interp_mux_text[] = {
+	"ZERO", "RX INT5 MIX2",
+};
+
+static const char * const rx_int6_interp_mux_text[] = {
+	"ZERO", "RX INT6 MIX2",
+};
+
+static const char * const rx_int7_interp_mux_text[] = {
+	"ZERO", "RX INT7 MIX2",
+};
+
+static const char * const rx_int8_interp_mux_text[] = {
+	"ZERO", "RX INT8 SEC MIX"
+};
+
+static const char * const mad_sel_text[] = {
+	"SPE", "MSM"
+};
+
+static const char * const adc_mux_text[] = {
+	"DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2"
+};
+
+static const char * const dmic_mux_text[] = {
+	"ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5",
+	"SMIC0", "SMIC1", "SMIC2", "SMIC3"
+};
+
+static const char * const dmic_mux_alt_text[] = {
+	"ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5",
+};
+
+static const char * const amic_mux_text[] = {
+	"ZERO", "ADC1", "ADC2", "ADC3", "ADC4", "ADC5", "ADC6"
+};
+
+static const char * const rx_echo_mux_text[] = {
+	"ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4",
+	"RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8", "RX_MIX_VBAT5",
+	"RX_MIX_VBAT6",	"RX_MIX_VBAT7", "RX_MIX_VBAT8"
+};
+
+static const char * const anc0_fb_mux_text[] = {
+	"ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR",
+	"ANC_IN_LO1"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+	"ZERO", "ANC_IN_HPHR", "ANC_IN_LO2"
+};
+
+static const char * const native_mux_text[] = {
+	"OFF", "ON",
+};
+
+static const struct soc_enum spl_src0_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 0, 3,
+			spl_src0_mux_text);
+
+static const struct soc_enum spl_src1_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 2, 3,
+			spl_src1_mux_text);
+
+static const struct soc_enum spl_src2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 4, 3,
+			spl_src2_mux_text);
+
+static const struct soc_enum spl_src3_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SPLINE_SRC_CFG0, 6, 3,
+			spl_src3_mux_text);
+
+static const struct soc_enum rx_int0_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 0, 10,
+			rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int1_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int2_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int3_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int4_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int5_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int6_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum rx_int7_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 0, 10,
+			rx_int0_7_mix_mux_text);
+
+static const struct soc_enum rx_int8_2_mux_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 0, 9,
+			rx_int_mix_mux_text);
+
+static const struct soc_enum int1_1_native_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+			native_mux_text);
+
+static const struct soc_enum int2_1_native_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+			native_mux_text);
+
+static const struct soc_enum int3_1_native_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+			native_mux_text);
+
+static const struct soc_enum int4_1_native_enum =
+	SOC_ENUM_SINGLE(SND_SOC_NOPM, 0, ARRAY_SIZE(native_mux_text),
+			native_mux_text);
+
+static const struct soc_enum rx_int0_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int1_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT1_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int2_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT2_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int3_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT3_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int4_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT4_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int5_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT5_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int6_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT6_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int7_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT7_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 0, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG0, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int8_1_mix_inp2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_INT8_CFG1, 4, 13,
+			rx_prim_mix_text);
+
+static const struct soc_enum rx_int0_sidetone_mix_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0, 4,
+			rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int1_sidetone_mix_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2, 4,
+			rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int2_sidetone_mix_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4, 4,
+			rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int3_sidetone_mix_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6, 4,
+			rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int4_sidetone_mix_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0, 4,
+			rx_sidetone_mix_text);
+
+static const struct soc_enum rx_int7_sidetone_mix_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2, 4,
+			rx_sidetone_mix_text);
+
+static const struct soc_enum tx_adc_mux0_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux1_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux2_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux3_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux4_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux5_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux6_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux7_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux8_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux10_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux11_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux12_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_adc_mux13_chain_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 6, 4,
+			adc_mux_text);
+
+static const struct soc_enum tx_dmic_mux0_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3, 11,
+			dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3, 11,
+			dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux2_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3, 11,
+			dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux3_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3, 11,
+			dmic_mux_text);
+
+static const struct soc_enum tx_dmic_mux4_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux5_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux6_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux7_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux8_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux10_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux11_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux12_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_dmic_mux13_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3, 7,
+			dmic_mux_alt_text);
+
+static const struct soc_enum tx_amic_mux0_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux1_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux2_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux3_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux4_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux5_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux6_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux7_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux8_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux10_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux11_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux12_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum tx_amic_mux13_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0, 7,
+			amic_mux_text);
+
+static const struct soc_enum sb_tx0_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 0, 4,
+			sb_tx0_mux_text);
+
+static const struct soc_enum sb_tx1_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 2, 4,
+			sb_tx1_mux_text);
+
+static const struct soc_enum sb_tx2_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 4, 4,
+			sb_tx2_mux_text);
+
+static const struct soc_enum sb_tx3_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0, 6, 4,
+			sb_tx3_mux_text);
+
+static const struct soc_enum sb_tx4_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 0, 4,
+			sb_tx4_mux_text);
+
+static const struct soc_enum sb_tx5_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 2, 4,
+			sb_tx5_mux_text);
+
+static const struct soc_enum sb_tx6_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 4, 4,
+			sb_tx6_mux_text);
+
+static const struct soc_enum sb_tx7_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1, 6, 4,
+			sb_tx7_mux_text);
+
+static const struct soc_enum sb_tx8_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 0, 4,
+			sb_tx8_mux_text);
+
+static const struct soc_enum sb_tx9_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 2, 3,
+			sb_tx9_mux_text);
+
+static const struct soc_enum sb_tx10_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2, 4, 3,
+			sb_tx10_mux_text);
+
+static const struct soc_enum sb_tx11_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX11_INP_CFG, 0, 4,
+			sb_tx11_mux_text);
+
+static const struct soc_enum sb_tx11_inp1_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 0, 10,
+			sb_tx11_inp1_mux_text);
+
+static const struct soc_enum sb_tx13_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3, 4, 3,
+			sb_tx13_mux_text);
+
+static const struct soc_enum tx13_inp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_DATA_HUB_DATA_HUB_SB_TX13_INP_CFG, 0, 3,
+			tx13_inp_mux_text);
+
+static const struct soc_enum rx_mix_tx0_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 0, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx1_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG0, 4, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx2_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 0, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx3_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG1, 4, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx4_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 0, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx5_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG2, 4, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx6_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 0, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx7_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG3, 4, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum rx_mix_tx8_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_RX_MIX_CFG4, 0, 14,
+			rx_echo_mux_text);
+
+static const struct soc_enum iir0_inp0_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp1_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp2_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir0_inp3_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp0_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp1_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp2_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum iir1_inp3_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0, 18,
+			iir_inp_mux_text);
+
+static const struct soc_enum rx_int0_dem_inp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_SEC0, 0,
+			ARRAY_SIZE(rx_int_dem_inp_mux_text),
+			rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int1_dem_inp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_SEC0, 0,
+			ARRAY_SIZE(rx_int_dem_inp_mux_text),
+			rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int2_dem_inp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_SEC0, 0,
+			ARRAY_SIZE(rx_int_dem_inp_mux_text),
+			rx_int_dem_inp_mux_text);
+
+static const struct soc_enum rx_int0_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX0_RX_PATH_CTL, 5, 2,
+			rx_int0_interp_mux_text);
+
+static const struct soc_enum rx_int1_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX1_RX_PATH_CTL, 5, 2,
+			rx_int1_interp_mux_text);
+
+static const struct soc_enum rx_int2_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX2_RX_PATH_CTL, 5, 2,
+			rx_int2_interp_mux_text);
+
+static const struct soc_enum rx_int3_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX3_RX_PATH_CTL, 5, 2,
+			rx_int3_interp_mux_text);
+
+static const struct soc_enum rx_int4_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX4_RX_PATH_CTL, 5, 2,
+			rx_int4_interp_mux_text);
+
+static const struct soc_enum rx_int5_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX5_RX_PATH_CTL, 5, 2,
+			rx_int5_interp_mux_text);
+
+static const struct soc_enum rx_int6_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX6_RX_PATH_CTL, 5, 2,
+			rx_int6_interp_mux_text);
+
+static const struct soc_enum rx_int7_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX7_RX_PATH_CTL, 5, 2,
+			rx_int7_interp_mux_text);
+
+static const struct soc_enum rx_int8_interp_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX8_RX_PATH_CTL, 5, 2,
+			rx_int8_interp_mux_text);
+
+static const struct soc_enum mad_sel_enum =
+	SOC_ENUM_SINGLE(WCD9335_CPE_SS_CFG, 0, 2, mad_sel_text);
+
+static const struct soc_enum anc0_fb_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 0, 5,
+			anc0_fb_mux_text);
+
+static const struct soc_enum anc1_fb_mux_enum =
+	SOC_ENUM_SINGLE(WCD9335_CDC_RX_INP_MUX_ANC_CFG0, 3, 3,
+			anc1_fb_mux_text);
+
+static const struct snd_kcontrol_new rx_int0_dem_inp_mux =
+	SOC_DAPM_ENUM_EXT("RX INT0 DEM MUX Mux", rx_int0_dem_inp_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int1_dem_inp_mux =
+	SOC_DAPM_ENUM_EXT("RX INT1 DEM MUX Mux", rx_int1_dem_inp_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new rx_int2_dem_inp_mux =
+	SOC_DAPM_ENUM_EXT("RX INT2 DEM MUX Mux", rx_int2_dem_inp_mux_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_int_dem_inp_mux_put);
+
+static const struct snd_kcontrol_new spl_src0_mux =
+	SOC_DAPM_ENUM("SPL SRC0 MUX Mux", spl_src0_mux_chain_enum);
+
+static const struct snd_kcontrol_new spl_src1_mux =
+	SOC_DAPM_ENUM("SPL SRC1 MUX Mux", spl_src1_mux_chain_enum);
+
+static const struct snd_kcontrol_new spl_src2_mux =
+	SOC_DAPM_ENUM("SPL SRC2 MUX Mux", spl_src2_mux_chain_enum);
+
+static const struct snd_kcontrol_new spl_src3_mux =
+	SOC_DAPM_ENUM("SPL SRC3 MUX Mux", spl_src3_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_2_mux =
+	SOC_DAPM_ENUM("RX INT0_2 MUX Mux", rx_int0_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_2_mux =
+	SOC_DAPM_ENUM("RX INT1_2 MUX Mux", rx_int1_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_2_mux =
+	SOC_DAPM_ENUM("RX INT2_2 MUX Mux", rx_int2_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_2_mux =
+	SOC_DAPM_ENUM("RX INT3_2 MUX Mux", rx_int3_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_2_mux =
+	SOC_DAPM_ENUM("RX INT4_2 MUX Mux", rx_int4_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_2_mux =
+	SOC_DAPM_ENUM("RX INT5_2 MUX Mux", rx_int5_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_2_mux =
+	SOC_DAPM_ENUM("RX INT6_2 MUX Mux", rx_int6_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_2_mux =
+	SOC_DAPM_ENUM("RX INT7_2 MUX Mux", rx_int7_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_2_mux =
+	SOC_DAPM_ENUM("RX INT8_2 MUX Mux", rx_int8_2_mux_chain_enum);
+
+static const struct snd_kcontrol_new int1_1_native_mux =
+	SOC_DAPM_ENUM("RX INT1_1 NATIVE MUX Mux", int1_1_native_enum);
+
+static const struct snd_kcontrol_new int2_1_native_mux =
+	SOC_DAPM_ENUM("RX INT2_1 NATIVE MUX Mux", int2_1_native_enum);
+
+static const struct snd_kcontrol_new int3_1_native_mux =
+	SOC_DAPM_ENUM("RX INT3_1 NATIVE MUX Mux", int3_1_native_enum);
+
+static const struct snd_kcontrol_new int4_1_native_mux =
+	SOC_DAPM_ENUM("RX INT4_1 NATIVE MUX Mux", int4_1_native_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT0_1 MIX1 INP0 Mux", rx_int0_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT0_1 MIX1 INP1 Mux", rx_int0_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT0_1 MIX1 INP2 Mux", rx_int0_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT1_1 MIX1 INP0 Mux", rx_int1_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT1_1 MIX1 INP1 Mux", rx_int1_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT1_1 MIX1 INP2 Mux", rx_int1_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT2_1 MIX1 INP0 Mux", rx_int2_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT2_1 MIX1 INP1 Mux", rx_int2_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT2_1 MIX1 INP2 Mux", rx_int2_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT3_1 MIX1 INP0 Mux", rx_int3_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT3_1 MIX1 INP1 Mux", rx_int3_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT3_1 MIX1 INP2 Mux", rx_int3_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT4_1 MIX1 INP0 Mux", rx_int4_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT4_1 MIX1 INP1 Mux", rx_int4_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT4_1 MIX1 INP2 Mux", rx_int4_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT5_1 MIX1 INP0 Mux", rx_int5_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT5_1 MIX1 INP1 Mux", rx_int5_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int5_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT5_1 MIX1 INP2 Mux", rx_int5_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT6_1 MIX1 INP0 Mux", rx_int6_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT6_1 MIX1 INP1 Mux", rx_int6_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int6_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT6_1 MIX1 INP2 Mux", rx_int6_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT7_1 MIX1 INP0 Mux", rx_int7_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT7_1 MIX1 INP1 Mux", rx_int7_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT7_1 MIX1 INP2 Mux", rx_int7_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp0_mux =
+	SOC_DAPM_ENUM("RX INT8_1 MIX1 INP0 Mux", rx_int8_1_mix_inp0_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp1_mux =
+	SOC_DAPM_ENUM("RX INT8_1 MIX1 INP1 Mux", rx_int8_1_mix_inp1_chain_enum);
+
+static const struct snd_kcontrol_new rx_int8_1_mix_inp2_mux =
+	SOC_DAPM_ENUM("RX INT8_1 MIX1 INP2 Mux", rx_int8_1_mix_inp2_chain_enum);
+
+static const struct snd_kcontrol_new rx_int0_mix2_inp_mux =
+	SOC_DAPM_ENUM("RX INT0 MIX2 INP Mux", rx_int0_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int1_mix2_inp_mux =
+	SOC_DAPM_ENUM("RX INT1 MIX2 INP Mux", rx_int1_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int2_mix2_inp_mux =
+	SOC_DAPM_ENUM("RX INT2 MIX2 INP Mux", rx_int2_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int3_mix2_inp_mux =
+	SOC_DAPM_ENUM("RX INT3 MIX2 INP Mux", rx_int3_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int4_mix2_inp_mux =
+	SOC_DAPM_ENUM("RX INT4 MIX2 INP Mux", rx_int4_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new rx_int7_mix2_inp_mux =
+	SOC_DAPM_ENUM("RX INT7 MIX2 INP Mux", rx_int7_sidetone_mix_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux0 =
+	SOC_DAPM_ENUM_EXT("ADC MUX0 Mux", tx_adc_mux0_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux1 =
+	SOC_DAPM_ENUM_EXT("ADC MUX1 Mux", tx_adc_mux1_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux2 =
+	SOC_DAPM_ENUM_EXT("ADC MUX2 Mux", tx_adc_mux2_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux3 =
+	SOC_DAPM_ENUM_EXT("ADC MUX3 Mux", tx_adc_mux3_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux4 =
+	SOC_DAPM_ENUM_EXT("ADC MUX4 Mux", tx_adc_mux4_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux5 =
+	SOC_DAPM_ENUM_EXT("ADC MUX5 Mux", tx_adc_mux5_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux6 =
+	SOC_DAPM_ENUM_EXT("ADC MUX6 Mux", tx_adc_mux6_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux7 =
+	SOC_DAPM_ENUM_EXT("ADC MUX7 Mux", tx_adc_mux7_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux8 =
+	SOC_DAPM_ENUM_EXT("ADC MUX8 Mux", tx_adc_mux8_chain_enum,
+			  snd_soc_dapm_get_enum_double,
+			  tasha_put_dec_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux10 =
+	SOC_DAPM_ENUM("ADC MUX10 Mux", tx_adc_mux10_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux11 =
+	SOC_DAPM_ENUM("ADC MUX11 Mux", tx_adc_mux11_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux12 =
+	SOC_DAPM_ENUM("ADC MUX12 Mux", tx_adc_mux12_chain_enum);
+
+static const struct snd_kcontrol_new tx_adc_mux13 =
+	SOC_DAPM_ENUM("ADC MUX13 Mux", tx_adc_mux13_chain_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux0 =
+	SOC_DAPM_ENUM("DMIC MUX0 Mux", tx_dmic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux1 =
+	SOC_DAPM_ENUM("DMIC MUX1 Mux", tx_dmic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux2 =
+	SOC_DAPM_ENUM("DMIC MUX2 Mux", tx_dmic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux3 =
+	SOC_DAPM_ENUM("DMIC MUX3 Mux", tx_dmic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux4 =
+	SOC_DAPM_ENUM("DMIC MUX4 Mux", tx_dmic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux5 =
+	SOC_DAPM_ENUM("DMIC MUX5 Mux", tx_dmic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux6 =
+	SOC_DAPM_ENUM("DMIC MUX6 Mux", tx_dmic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux7 =
+	SOC_DAPM_ENUM("DMIC MUX7 Mux", tx_dmic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux8 =
+	SOC_DAPM_ENUM("DMIC MUX8 Mux", tx_dmic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux10 =
+	SOC_DAPM_ENUM("DMIC MUX10 Mux", tx_dmic_mux10_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux11 =
+	SOC_DAPM_ENUM("DMIC MUX11 Mux", tx_dmic_mux11_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux12 =
+	SOC_DAPM_ENUM("DMIC MUX12 Mux", tx_dmic_mux12_enum);
+
+static const struct snd_kcontrol_new tx_dmic_mux13 =
+	SOC_DAPM_ENUM("DMIC MUX13 Mux", tx_dmic_mux13_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux0 =
+	SOC_DAPM_ENUM("AMIC MUX0 Mux", tx_amic_mux0_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux1 =
+	SOC_DAPM_ENUM("AMIC MUX1 Mux", tx_amic_mux1_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux2 =
+	SOC_DAPM_ENUM("AMIC MUX2 Mux", tx_amic_mux2_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux3 =
+	SOC_DAPM_ENUM("AMIC MUX3 Mux", tx_amic_mux3_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux4 =
+	SOC_DAPM_ENUM("AMIC MUX4 Mux", tx_amic_mux4_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux5 =
+	SOC_DAPM_ENUM("AMIC MUX5 Mux", tx_amic_mux5_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux6 =
+	SOC_DAPM_ENUM("AMIC MUX6 Mux", tx_amic_mux6_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux7 =
+	SOC_DAPM_ENUM("AMIC MUX7 Mux", tx_amic_mux7_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux8 =
+	SOC_DAPM_ENUM("AMIC MUX8 Mux", tx_amic_mux8_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux10 =
+	SOC_DAPM_ENUM("AMIC MUX10 Mux", tx_amic_mux10_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux11 =
+	SOC_DAPM_ENUM("AMIC MUX11 Mux", tx_amic_mux11_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux12 =
+	SOC_DAPM_ENUM("AMIC MUX12 Mux", tx_amic_mux12_enum);
+
+static const struct snd_kcontrol_new tx_amic_mux13 =
+	SOC_DAPM_ENUM("AMIC MUX13 Mux", tx_amic_mux13_enum);
+
+static const struct snd_kcontrol_new sb_tx0_mux =
+	SOC_DAPM_ENUM("SLIM TX0 MUX Mux", sb_tx0_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx1_mux =
+	SOC_DAPM_ENUM("SLIM TX1 MUX Mux", sb_tx1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx2_mux =
+	SOC_DAPM_ENUM("SLIM TX2 MUX Mux", sb_tx2_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx3_mux =
+	SOC_DAPM_ENUM("SLIM TX3 MUX Mux", sb_tx3_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx4_mux =
+	SOC_DAPM_ENUM("SLIM TX4 MUX Mux", sb_tx4_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx5_mux =
+	SOC_DAPM_ENUM("SLIM TX5 MUX Mux", sb_tx5_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx6_mux =
+	SOC_DAPM_ENUM("SLIM TX6 MUX Mux", sb_tx6_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx7_mux =
+	SOC_DAPM_ENUM("SLIM TX7 MUX Mux", sb_tx7_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx8_mux =
+	SOC_DAPM_ENUM("SLIM TX8 MUX Mux", sb_tx8_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx9_mux =
+	SOC_DAPM_ENUM("SLIM TX9 MUX Mux", sb_tx9_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx10_mux =
+	SOC_DAPM_ENUM("SLIM TX10 MUX Mux", sb_tx10_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx11_mux =
+	SOC_DAPM_ENUM("SLIM TX11 MUX Mux", sb_tx11_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx11_inp1_mux =
+	SOC_DAPM_ENUM("SLIM TX11 INP1 MUX Mux", sb_tx11_inp1_mux_enum);
+
+static const struct snd_kcontrol_new sb_tx13_mux =
+	SOC_DAPM_ENUM("SLIM TX13 MUX Mux", sb_tx13_mux_enum);
+
+static const struct snd_kcontrol_new tx13_inp_mux =
+	SOC_DAPM_ENUM("TX13 INP MUX Mux", tx13_inp_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx0_mux =
+	SOC_DAPM_ENUM("RX MIX TX0 MUX Mux", rx_mix_tx0_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx1_mux =
+	SOC_DAPM_ENUM("RX MIX TX1 MUX Mux", rx_mix_tx1_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx2_mux =
+	SOC_DAPM_ENUM("RX MIX TX2 MUX Mux", rx_mix_tx2_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx3_mux =
+	SOC_DAPM_ENUM("RX MIX TX3 MUX Mux", rx_mix_tx3_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx4_mux =
+	SOC_DAPM_ENUM("RX MIX TX4 MUX Mux", rx_mix_tx4_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx5_mux =
+	SOC_DAPM_ENUM("RX MIX TX5 MUX Mux", rx_mix_tx5_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx6_mux =
+	SOC_DAPM_ENUM("RX MIX TX6 MUX Mux", rx_mix_tx6_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx7_mux =
+	SOC_DAPM_ENUM("RX MIX TX7 MUX Mux", rx_mix_tx7_mux_enum);
+
+static const struct snd_kcontrol_new rx_mix_tx8_mux =
+	SOC_DAPM_ENUM("RX MIX TX8 MUX Mux", rx_mix_tx8_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp0_mux =
+	SOC_DAPM_ENUM("IIR0 INP0 Mux", iir0_inp0_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp1_mux =
+	SOC_DAPM_ENUM("IIR0 INP1 Mux", iir0_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp2_mux =
+	SOC_DAPM_ENUM("IIR0 INP2 Mux", iir0_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir0_inp3_mux =
+	SOC_DAPM_ENUM("IIR0 INP3 Mux", iir0_inp3_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp0_mux =
+	SOC_DAPM_ENUM("IIR1 INP0 Mux", iir1_inp0_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp1_mux =
+	SOC_DAPM_ENUM("IIR1 INP1 Mux", iir1_inp1_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp2_mux =
+	SOC_DAPM_ENUM("IIR1 INP2 Mux", iir1_inp2_mux_enum);
+
+static const struct snd_kcontrol_new iir1_inp3_mux =
+	SOC_DAPM_ENUM("IIR1 INP3 Mux", iir1_inp3_mux_enum);
+
+static const struct snd_kcontrol_new rx_int0_interp_mux =
+	SOC_DAPM_ENUM("RX INT0 INTERP Mux", rx_int0_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int1_interp_mux =
+	SOC_DAPM_ENUM("RX INT1 INTERP Mux", rx_int1_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int2_interp_mux =
+	SOC_DAPM_ENUM("RX INT2 INTERP Mux", rx_int2_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int3_interp_mux =
+	SOC_DAPM_ENUM("RX INT3 INTERP Mux", rx_int3_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int4_interp_mux =
+	SOC_DAPM_ENUM("RX INT4 INTERP Mux", rx_int4_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int5_interp_mux =
+	SOC_DAPM_ENUM("RX INT5 INTERP Mux", rx_int5_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int6_interp_mux =
+	SOC_DAPM_ENUM("RX INT6 INTERP Mux", rx_int6_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int7_interp_mux =
+	SOC_DAPM_ENUM("RX INT7 INTERP Mux", rx_int7_interp_mux_enum);
+
+static const struct snd_kcontrol_new rx_int8_interp_mux =
+	SOC_DAPM_ENUM("RX INT8 INTERP Mux", rx_int8_interp_mux_enum);
+
+static const struct snd_kcontrol_new mad_sel_mux =
+	SOC_DAPM_ENUM("MAD_SEL MUX Mux", mad_sel_enum);
+
+static const struct snd_kcontrol_new aif4_mad_switch =
+	SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 5, 1, 0);
+
+static const struct snd_kcontrol_new mad_brdcst_switch =
+	SOC_DAPM_SINGLE("Switch", WCD9335_CPE_SS_CFG, 6, 1, 0);
+
+static const struct snd_kcontrol_new aif4_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+			0, 1, 0, tasha_codec_aif4_mixer_switch_get,
+			tasha_codec_aif4_mixer_switch_put);
+
+static const struct snd_kcontrol_new anc_hphl_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_hphr_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_spkr_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_lineout1_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_lineout2_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_spkr_pa_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux0_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux1_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux2_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux3_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux4_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux5_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux6_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux7_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux8_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc0_fb_mux =
+	SOC_DAPM_ENUM("ANC0 FB MUX Mux", anc0_fb_mux_enum);
+
+static const struct snd_kcontrol_new anc1_fb_mux =
+	SOC_DAPM_ENUM("ANC1 FB MUX Mux", anc1_fb_mux_enum);
+
+static int tasha_codec_ec_buf_mux_enable(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol,
+					 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev, "%s: event = %d name = %s\n",
+		__func__, event, w->name);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x3B);
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x08);
+		snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+				    0x08, 0x08);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0,
+				    0x08, 0x00);
+		snd_soc_update_bits(codec, WCD9335_CPE_SS_CFG, 0x08, 0x00);
+		snd_soc_write(codec, WCD9335_CPE_SS_EC_BUF_INT_PERIOD, 0x00);
+		break;
+	}
+
+	return 0;
+};
+
+static const char * const ec_buf_mux_text[] = {
+	"ZERO", "RXMIXEC", "SB_RX0", "SB_RX1", "SB_RX2", "SB_RX3",
+	"I2S_RX_SD0_L", "I2S_RX_SD0_R", "I2S_RX_SD1_L", "I2S_RX_SD1_R",
+	"DEC1"
+};
+
+static SOC_ENUM_SINGLE_DECL(ec_buf_mux_enum, WCD9335_CPE_SS_US_EC_MUX_CFG,
+			    0, ec_buf_mux_text);
+
+static const struct snd_kcontrol_new ec_buf_mux =
+	SOC_DAPM_ENUM("EC BUF Mux", ec_buf_mux_enum);
+
+static const struct snd_soc_dapm_widget tasha_dapm_widgets[] = {
+	SND_SOC_DAPM_OUTPUT("EAR"),
+	SND_SOC_DAPM_OUTPUT("ANC EAR"),
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+				AIF1_PB, 0, tasha_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+				AIF2_PB, 0, tasha_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+				AIF3_PB, 0, tasha_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
+				AIF4_PB, 0, tasha_codec_enable_slimrx,
+				SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+				SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF MIX1 PB", "AIF Mix Playback", 0,
+			       SND_SOC_NOPM, AIF_MIX1_PB, 0,
+			       tasha_codec_enable_slimrx,
+			       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+			       SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("SLIM RX0 MUX", SND_SOC_NOPM, TASHA_RX0, 0,
+				&slim_rx_mux[TASHA_RX0]),
+	SND_SOC_DAPM_MUX("SLIM RX1 MUX", SND_SOC_NOPM, TASHA_RX1, 0,
+				&slim_rx_mux[TASHA_RX1]),
+	SND_SOC_DAPM_MUX("SLIM RX2 MUX", SND_SOC_NOPM, TASHA_RX2, 0,
+				&slim_rx_mux[TASHA_RX2]),
+	SND_SOC_DAPM_MUX("SLIM RX3 MUX", SND_SOC_NOPM, TASHA_RX3, 0,
+				&slim_rx_mux[TASHA_RX3]),
+	SND_SOC_DAPM_MUX("SLIM RX4 MUX", SND_SOC_NOPM, TASHA_RX4, 0,
+				&slim_rx_mux[TASHA_RX4]),
+	SND_SOC_DAPM_MUX("SLIM RX5 MUX", SND_SOC_NOPM, TASHA_RX5, 0,
+				&slim_rx_mux[TASHA_RX5]),
+	SND_SOC_DAPM_MUX("SLIM RX6 MUX", SND_SOC_NOPM, TASHA_RX6, 0,
+				&slim_rx_mux[TASHA_RX6]),
+	SND_SOC_DAPM_MUX("SLIM RX7 MUX", SND_SOC_NOPM, TASHA_RX7, 0,
+				&slim_rx_mux[TASHA_RX7]),
+
+	SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MUX_E("SPL SRC0 MUX", SND_SOC_NOPM, SPLINE_SRC0, 0,
+			 &spl_src0_mux, tasha_codec_enable_spline_resampler,
+			 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("SPL SRC1 MUX", SND_SOC_NOPM, SPLINE_SRC1, 0,
+			 &spl_src1_mux, tasha_codec_enable_spline_resampler,
+			 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("SPL SRC2 MUX", SND_SOC_NOPM, SPLINE_SRC2, 0,
+			 &spl_src2_mux, tasha_codec_enable_spline_resampler,
+			 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("SPL SRC3 MUX", SND_SOC_NOPM, SPLINE_SRC3, 0,
+			 &spl_src3_mux, tasha_codec_enable_spline_resampler,
+			 SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", WCD9335_CDC_RX0_RX_PATH_MIX_CTL,
+			5, 0, &rx_int0_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", WCD9335_CDC_RX1_RX_PATH_MIX_CTL,
+			5, 0, &rx_int1_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", WCD9335_CDC_RX2_RX_PATH_MIX_CTL,
+			5, 0, &rx_int2_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", WCD9335_CDC_RX3_RX_PATH_MIX_CTL,
+			5, 0, &rx_int3_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", WCD9335_CDC_RX4_RX_PATH_MIX_CTL,
+			5, 0, &rx_int4_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT5_2 MUX", WCD9335_CDC_RX5_RX_PATH_MIX_CTL,
+			5, 0, &rx_int5_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT6_2 MUX", WCD9335_CDC_RX6_RX_PATH_MIX_CTL,
+			5, 0, &rx_int6_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", WCD9335_CDC_RX7_RX_PATH_MIX_CTL,
+			5, 0, &rx_int7_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+	SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", WCD9335_CDC_RX8_RX_PATH_MIX_CTL,
+			5, 0, &rx_int8_2_mux, tasha_codec_enable_mix_path,
+			SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int0_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int0_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT0_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int0_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int1_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int1_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT1_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int1_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int2_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int2_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT2_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int2_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int3_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int3_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT3_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int3_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int4_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int4_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT4_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int4_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int5_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int5_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT5_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int5_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int6_1_mix_inp0_mux),
+	SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int6_1_mix_inp1_mux),
+	SND_SOC_DAPM_MUX("RX INT6_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int6_1_mix_inp2_mux),
+	SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int7_1_mix_inp0_mux, tasha_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int7_1_mix_inp1_mux, tasha_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int7_1_mix_inp2_mux, tasha_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int8_1_mix_inp0_mux, tasha_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int8_1_mix_inp1_mux, tasha_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int8_1_mix_inp2_mux, tasha_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int1_spline_mix_switch,
+			ARRAY_SIZE(rx_int1_spline_mix_switch)),
+	SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT2 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int2_spline_mix_switch,
+			ARRAY_SIZE(rx_int2_spline_mix_switch)),
+	SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT3 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int3_spline_mix_switch,
+			ARRAY_SIZE(rx_int3_spline_mix_switch)),
+	SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT4 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int4_spline_mix_switch,
+			ARRAY_SIZE(rx_int4_spline_mix_switch)),
+	SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT5_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT5 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int5_spline_mix_switch,
+			ARRAY_SIZE(rx_int5_spline_mix_switch)),
+	SND_SOC_DAPM_MIXER("RX INT5 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("RX INT6_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT6 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int6_spline_mix_switch,
+			ARRAY_SIZE(rx_int6_spline_mix_switch)),
+	SND_SOC_DAPM_MIXER("RX INT6 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT7 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int7_spline_mix_switch,
+			ARRAY_SIZE(rx_int7_spline_mix_switch)),
+
+	SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT8 SPLINE MIX", SND_SOC_NOPM, 0, 0,
+			rx_int8_spline_mix_switch,
+			ARRAY_SIZE(rx_int8_spline_mix_switch)),
+
+	SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT5 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT6 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0,
+			NULL, 0, tasha_codec_spk_boost_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0,
+			NULL, 0, tasha_codec_spk_boost_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER_E("RX INT5 VBAT", SND_SOC_NOPM, 0, 0,
+			rx_int5_vbat_mix_switch,
+			ARRAY_SIZE(rx_int5_vbat_mix_switch),
+			tasha_codec_vbat_enable_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX INT6 VBAT", SND_SOC_NOPM, 0, 0,
+			rx_int6_vbat_mix_switch,
+			ARRAY_SIZE(rx_int6_vbat_mix_switch),
+			tasha_codec_vbat_enable_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX INT7 VBAT", SND_SOC_NOPM, 0, 0,
+			rx_int7_vbat_mix_switch,
+			ARRAY_SIZE(rx_int7_vbat_mix_switch),
+			tasha_codec_vbat_enable_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX INT8 VBAT", SND_SOC_NOPM, 0, 0,
+			rx_int8_vbat_mix_switch,
+			ARRAY_SIZE(rx_int8_vbat_mix_switch),
+			tasha_codec_vbat_enable_event,
+			SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("RX INT0 MIX2 INP", WCD9335_CDC_RX0_RX_PATH_CFG1, 4,
+			   0, &rx_int0_mix2_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT1 MIX2 INP", WCD9335_CDC_RX1_RX_PATH_CFG1, 4,
+			   0, &rx_int1_mix2_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT2 MIX2 INP", WCD9335_CDC_RX2_RX_PATH_CFG1, 4,
+			   0, &rx_int2_mix2_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT3 MIX2 INP", WCD9335_CDC_RX3_RX_PATH_CFG1, 4,
+			   0, &rx_int3_mix2_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT4 MIX2 INP", WCD9335_CDC_RX4_RX_PATH_CFG1, 4,
+			   0, &rx_int4_mix2_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT7 MIX2 INP", WCD9335_CDC_RX7_RX_PATH_CFG1, 4,
+			   0, &rx_int7_mix2_inp_mux),
+
+	SND_SOC_DAPM_MUX("SLIM TX0 MUX", SND_SOC_NOPM, TASHA_TX0, 0,
+		&sb_tx0_mux),
+	SND_SOC_DAPM_MUX("SLIM TX1 MUX", SND_SOC_NOPM, TASHA_TX1, 0,
+		&sb_tx1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX2 MUX", SND_SOC_NOPM, TASHA_TX2, 0,
+		&sb_tx2_mux),
+	SND_SOC_DAPM_MUX("SLIM TX3 MUX", SND_SOC_NOPM, TASHA_TX3, 0,
+		&sb_tx3_mux),
+	SND_SOC_DAPM_MUX("SLIM TX4 MUX", SND_SOC_NOPM, TASHA_TX4, 0,
+		&sb_tx4_mux),
+	SND_SOC_DAPM_MUX("SLIM TX5 MUX", SND_SOC_NOPM, TASHA_TX5, 0,
+		&sb_tx5_mux),
+	SND_SOC_DAPM_MUX("SLIM TX6 MUX", SND_SOC_NOPM, TASHA_TX6, 0,
+		&sb_tx6_mux),
+	SND_SOC_DAPM_MUX("SLIM TX7 MUX", SND_SOC_NOPM, TASHA_TX7, 0,
+		&sb_tx7_mux),
+	SND_SOC_DAPM_MUX("SLIM TX8 MUX", SND_SOC_NOPM, TASHA_TX8, 0,
+		&sb_tx8_mux),
+	SND_SOC_DAPM_MUX("SLIM TX9 MUX", SND_SOC_NOPM, TASHA_TX9, 0,
+		&sb_tx9_mux),
+	SND_SOC_DAPM_MUX("SLIM TX10 MUX", SND_SOC_NOPM, TASHA_TX10, 0,
+		&sb_tx10_mux),
+	SND_SOC_DAPM_MUX("SLIM TX11 MUX", SND_SOC_NOPM, TASHA_TX11, 0,
+		&sb_tx11_mux),
+	SND_SOC_DAPM_MUX("SLIM TX11 INP1 MUX", SND_SOC_NOPM, TASHA_TX11, 0,
+		&sb_tx11_inp1_mux),
+	SND_SOC_DAPM_MUX("SLIM TX13 MUX", SND_SOC_NOPM, TASHA_TX13, 0,
+		&sb_tx13_mux),
+	SND_SOC_DAPM_MUX("TX13 INP MUX", SND_SOC_NOPM, 0, 0,
+			 &tx13_inp_mux),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX0", WCD9335_CDC_TX0_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux0, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX1", WCD9335_CDC_TX1_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux1, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX2", WCD9335_CDC_TX2_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux2, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX3", WCD9335_CDC_TX3_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux3, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX4", WCD9335_CDC_TX4_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux4, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX5", WCD9335_CDC_TX5_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux5, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX6", WCD9335_CDC_TX6_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux6, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX7", WCD9335_CDC_TX7_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux7, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX8", WCD9335_CDC_TX8_TX_PATH_CTL, 5, 0,
+			   &tx_adc_mux8, tasha_codec_enable_dec,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0,
+			 &tx_adc_mux10, tasha_codec_tx_adc_cfg,
+			 SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0,
+			 &tx_adc_mux11, tasha_codec_tx_adc_cfg,
+			 SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0,
+			 &tx_adc_mux12, tasha_codec_tx_adc_cfg,
+			 SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0,
+			 &tx_adc_mux13, tasha_codec_tx_adc_cfg,
+			 SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX("DMIC MUX0", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux0),
+	SND_SOC_DAPM_MUX("DMIC MUX1", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux1),
+	SND_SOC_DAPM_MUX("DMIC MUX2", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux2),
+	SND_SOC_DAPM_MUX("DMIC MUX3", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux3),
+	SND_SOC_DAPM_MUX("DMIC MUX4", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux4),
+	SND_SOC_DAPM_MUX("DMIC MUX5", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux5),
+	SND_SOC_DAPM_MUX("DMIC MUX6", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux6),
+	SND_SOC_DAPM_MUX("DMIC MUX7", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux7),
+	SND_SOC_DAPM_MUX("DMIC MUX8", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux8),
+	SND_SOC_DAPM_MUX("DMIC MUX10", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux10),
+	SND_SOC_DAPM_MUX("DMIC MUX11", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux11),
+	SND_SOC_DAPM_MUX("DMIC MUX12", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux12),
+	SND_SOC_DAPM_MUX("DMIC MUX13", SND_SOC_NOPM, 0, 0,
+		&tx_dmic_mux13),
+
+	SND_SOC_DAPM_MUX("AMIC MUX0", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux0),
+	SND_SOC_DAPM_MUX("AMIC MUX1", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux1),
+	SND_SOC_DAPM_MUX("AMIC MUX2", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux2),
+	SND_SOC_DAPM_MUX("AMIC MUX3", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux3),
+	SND_SOC_DAPM_MUX("AMIC MUX4", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux4),
+	SND_SOC_DAPM_MUX("AMIC MUX5", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux5),
+	SND_SOC_DAPM_MUX("AMIC MUX6", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux6),
+	SND_SOC_DAPM_MUX("AMIC MUX7", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux7),
+	SND_SOC_DAPM_MUX("AMIC MUX8", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux8),
+	SND_SOC_DAPM_MUX("AMIC MUX10", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux10),
+	SND_SOC_DAPM_MUX("AMIC MUX11", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux11),
+	SND_SOC_DAPM_MUX("AMIC MUX12", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux12),
+	SND_SOC_DAPM_MUX("AMIC MUX13", SND_SOC_NOPM, 0, 0,
+		&tx_amic_mux13),
+
+	SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD9335_ANA_AMIC1, 7, 0,
+			   tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD9335_ANA_AMIC2, 7, 0,
+			   tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD9335_ANA_AMIC3, 7, 0,
+			   tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD9335_ANA_AMIC4, 7, 0,
+			   tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC5", NULL, WCD9335_ANA_AMIC5, 7, 0,
+			   tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC6", NULL, WCD9335_ANA_AMIC6, 7, 0,
+			   tasha_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+
+	SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM,
+			    INTERP_HPHL, 0, tasha_enable_native_supply,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM,
+			    INTERP_HPHR, 0, tasha_enable_native_supply,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM,
+			    INTERP_LO1, 0, tasha_enable_native_supply,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM,
+			    INTERP_LO2, 0, tasha_enable_native_supply,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0,
+			       tasha_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,
+			       tasha_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,
+			       tasha_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			       SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0,
+			       tasha_codec_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+			       SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0,
+			       tasha_codec_force_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0,
+			       tasha_codec_force_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0,
+			       tasha_codec_force_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0,
+			       tasha_codec_force_enable_micbias,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_SUPPLY(DAPM_LDO_H_STANDALONE, SND_SOC_NOPM, 0, 0,
+			    tasha_codec_force_enable_ldo_h,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("ANC0 FB MUX", SND_SOC_NOPM, 0, 0, &anc0_fb_mux),
+	SND_SOC_DAPM_MUX("ANC1 FB MUX", SND_SOC_NOPM, 0, 0, &anc1_fb_mux),
+
+	SND_SOC_DAPM_INPUT("AMIC2"),
+	SND_SOC_DAPM_INPUT("AMIC3"),
+	SND_SOC_DAPM_INPUT("AMIC4"),
+	SND_SOC_DAPM_INPUT("AMIC5"),
+	SND_SOC_DAPM_INPUT("AMIC6"),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, tasha_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, tasha_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, tasha_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
+		AIF4_VIFEED, 0, tasha_codec_enable_slimvi_feedback,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0,
+		aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+
+	SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0,
+		aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
+
+	SND_SOC_DAPM_INPUT("VIINPUT"),
+
+	SND_SOC_DAPM_AIF_OUT("AIF5 CPE", "AIF5 CPE TX", 0, SND_SOC_NOPM,
+			     AIF5_CPE_TX, 0),
+
+	SND_SOC_DAPM_MUX_E("EC BUF MUX INP", SND_SOC_NOPM, 0, 0, &ec_buf_mux,
+		tasha_codec_ec_buf_mux_enable,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/* Digital Mic Inputs */
+	SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_dmic, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("IIR0 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp0_mux),
+	SND_SOC_DAPM_MUX("IIR0 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp1_mux),
+	SND_SOC_DAPM_MUX("IIR0 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp2_mux),
+	SND_SOC_DAPM_MUX("IIR0 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir0_inp3_mux),
+	SND_SOC_DAPM_MUX("IIR1 INP0 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp0_mux),
+	SND_SOC_DAPM_MUX("IIR1 INP1 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp1_mux),
+	SND_SOC_DAPM_MUX("IIR1 INP2 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp2_mux),
+	SND_SOC_DAPM_MUX("IIR1 INP3 MUX", SND_SOC_NOPM, 0, 0, &iir1_inp3_mux),
+
+	SND_SOC_DAPM_MIXER_E("IIR0", WCD9335_CDC_SIDETONE_IIR0_IIR_PATH_CTL,
+			     4, 0, NULL, 0, tasha_codec_set_iir_gain,
+			     SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER_E("IIR1", WCD9335_CDC_SIDETONE_IIR1_IIR_PATH_CTL,
+			     4, 0, NULL, 0, tasha_codec_set_iir_gain,
+			     SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER("SRC0", WCD9335_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+			     4, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SRC1", WCD9335_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+			     4, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER_E("CPE IN Mixer", SND_SOC_NOPM, 0, 0,
+				cpe_in_mix_switch,
+				ARRAY_SIZE(cpe_in_mix_switch),
+				tasha_codec_configure_cpe_input,
+				SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("RX INT1_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+		&int1_1_native_mux),
+	SND_SOC_DAPM_MUX("RX INT2_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+		&int2_1_native_mux),
+	SND_SOC_DAPM_MUX("RX INT3_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+		&int3_1_native_mux),
+	SND_SOC_DAPM_MUX("RX INT4_1 NATIVE MUX", SND_SOC_NOPM, 0, 0,
+		&int4_1_native_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX0 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx0_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX1 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx1_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX2 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx2_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX3 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx3_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX4 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx4_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX5 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx5_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX6 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx6_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX7 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx7_mux),
+	SND_SOC_DAPM_MUX("RX MIX TX8 MUX", SND_SOC_NOPM, 0, 0,
+		&rx_mix_tx8_mux),
+
+	SND_SOC_DAPM_MUX("RX INT0 DEM MUX", SND_SOC_NOPM, 0, 0,
+		&rx_int0_dem_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT1 DEM MUX", SND_SOC_NOPM, 0, 0,
+		&rx_int1_dem_inp_mux),
+	SND_SOC_DAPM_MUX("RX INT2 DEM MUX", SND_SOC_NOPM, 0, 0,
+		&rx_int2_dem_inp_mux),
+
+	SND_SOC_DAPM_MUX_E("RX INT0 INTERP", SND_SOC_NOPM,
+		INTERP_EAR, 0, &rx_int0_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT1 INTERP", SND_SOC_NOPM,
+		INTERP_HPHL, 0, &rx_int1_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT2 INTERP", SND_SOC_NOPM,
+		INTERP_HPHR, 0, &rx_int2_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT3 INTERP", SND_SOC_NOPM,
+		INTERP_LO1, 0, &rx_int3_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT4 INTERP", SND_SOC_NOPM,
+		INTERP_LO2, 0, &rx_int4_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT5 INTERP", SND_SOC_NOPM,
+		INTERP_LO3, 0, &rx_int5_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT6 INTERP", SND_SOC_NOPM,
+		INTERP_LO4, 0, &rx_int6_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7 INTERP", SND_SOC_NOPM,
+		INTERP_SPKR1, 0, &rx_int7_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8 INTERP", SND_SOC_NOPM,
+		INTERP_SPKR2, 0, &rx_int8_interp_mux,
+		tasha_codec_enable_interpolator,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tasha_codec_ear_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("RX INT1 DAC", NULL, WCD9335_ANA_HPH,
+		5, 0, tasha_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("RX INT2 DAC", NULL, WCD9335_ANA_HPH,
+		4, 0, tasha_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("RX INT3 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tasha_codec_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tasha_codec_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RX INT5 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tasha_codec_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RX INT6 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tasha_codec_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HPHL PA", WCD9335_ANA_HPH, 7, 0, NULL, 0,
+			   tasha_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 PA", WCD9335_ANA_HPH, 6, 0, NULL, 0,
+			   tasha_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_PGA_E("EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0,
+			   tasha_codec_enable_ear_pa,
+			   SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT1 PA", WCD9335_ANA_LO_1_2, 7, 0, NULL, 0,
+			   tasha_codec_enable_lineout_pa,
+			   SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD9335_ANA_LO_1_2, 6, 0, NULL, 0,
+			   tasha_codec_enable_lineout_pa,
+			   SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT3 PA", WCD9335_ANA_LO_3_4, 7, 0, NULL, 0,
+			   tasha_codec_enable_lineout_pa,
+			   SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT4 PA", WCD9335_ANA_LO_3_4, 6, 0, NULL, 0,
+			   tasha_codec_enable_lineout_pa,
+			   SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD9335_ANA_EAR, 7, 0, NULL, 0,
+			   tasha_codec_enable_ear_pa,
+			   SND_SOC_DAPM_POST_PMU |
+			   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("ANC HPHL PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+			   tasha_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("ANC HPHR PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+			   tasha_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_PGA_E("ANC LINEOUT1 PA", WCD9335_ANA_LO_1_2,
+				7, 0, NULL, 0,
+				tasha_codec_enable_lineout_pa,
+				SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("ANC LINEOUT2 PA", WCD9335_ANA_LO_1_2,
+				6, 0, NULL, 0,
+				tasha_codec_enable_lineout_pa,
+				SND_SOC_DAPM_POST_PMU |
+				SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+			   tasha_codec_enable_spk_anc,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_OUTPUT("HPHL"),
+	SND_SOC_DAPM_OUTPUT("HPHR"),
+	SND_SOC_DAPM_OUTPUT("ANC HPHL"),
+	SND_SOC_DAPM_OUTPUT("ANC HPHR"),
+	SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+		tasha_codec_enable_rx_bias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
+	SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT3"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT4"),
+	SND_SOC_DAPM_OUTPUT("ANC LINEOUT1"),
+	SND_SOC_DAPM_OUTPUT("ANC LINEOUT2"),
+	SND_SOC_DAPM_SUPPLY("MICBIAS_REGULATOR", SND_SOC_NOPM,
+		ON_DEMAND_MICBIAS, 0,
+		tasha_codec_enable_on_demand_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD9335_CDC_TX0_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux0_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD9335_CDC_TX1_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux1_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD9335_CDC_TX2_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux2_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD9335_CDC_TX3_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux3_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD9335_CDC_TX4_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux4_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD9335_CDC_TX5_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux5_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD9335_CDC_TX6_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux6_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD9335_CDC_TX7_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux7_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD9335_CDC_TX8_TX_PATH_192_CTL, 0,
+			    0, &adc_us_mux8_switch),
+	/* MAD related widgets */
+	SND_SOC_DAPM_AIF_OUT_E("AIF4 MAD", "AIF4 MAD TX", 0,
+			       SND_SOC_NOPM, 0, 0,
+			       tasha_codec_enable_mad,
+			       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("MAD_SEL MUX", SND_SOC_NOPM, 0, 0,
+			 &mad_sel_mux),
+	SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"),
+	SND_SOC_DAPM_INPUT("MADINPUT"),
+	SND_SOC_DAPM_SWITCH("MADONOFF", SND_SOC_NOPM, 0, 0,
+			    &aif4_mad_switch),
+	SND_SOC_DAPM_SWITCH("MAD_BROADCAST", SND_SOC_NOPM, 0, 0,
+			    &mad_brdcst_switch),
+	SND_SOC_DAPM_SWITCH("AIF4", SND_SOC_NOPM, 0, 0,
+			    &aif4_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("ANC HPHL Enable", SND_SOC_NOPM, 0, 0,
+			&anc_hphl_switch),
+	SND_SOC_DAPM_SWITCH("ANC HPHR Enable", SND_SOC_NOPM, 0, 0,
+			&anc_hphr_switch),
+	SND_SOC_DAPM_SWITCH("ANC EAR Enable", SND_SOC_NOPM, 0, 0,
+			&anc_ear_switch),
+	SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0,
+			    &anc_ear_spkr_switch),
+	SND_SOC_DAPM_SWITCH("ANC LINEOUT1 Enable", SND_SOC_NOPM, 0, 0,
+			&anc_lineout1_switch),
+	SND_SOC_DAPM_SWITCH("ANC LINEOUT2 Enable", SND_SOC_NOPM, 0, 0,
+			&anc_lineout2_switch),
+	SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0,
+			    &anc_spkr_pa_switch),
+};
+
+static int tasha_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+{
+	struct tasha_priv *tasha_p = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
+
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+	case AIF4_PB:
+	case AIF_MIX1_PB:
+		if (!rx_slot || !rx_num) {
+			pr_err("%s: Invalid rx_slot %pK or rx_num %pK\n",
+				 __func__, rx_slot, rx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: slot_num %u ch->ch_num %d\n",
+				 __func__, i, ch->ch_num);
+			rx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: rx_num %d\n", __func__, i);
+		*rx_num = i;
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+	case AIF4_MAD_TX:
+	case AIF4_VIFEED:
+		if (!tx_slot || !tx_num) {
+			pr_err("%s: Invalid tx_slot %pK or tx_num %pK\n",
+				 __func__, tx_slot, tx_num);
+			return -EINVAL;
+		}
+		list_for_each_entry(ch, &tasha_p->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			pr_debug("%s: slot_num %u ch->ch_num %d\n",
+				 __func__, i,  ch->ch_num);
+			tx_slot[i++] = ch->ch_num;
+		}
+		pr_debug("%s: tx_num %d\n", __func__, i);
+		*tx_num = i;
+		break;
+
+	default:
+		pr_err("%s: Invalid DAI ID %x\n", __func__, dai->id);
+		break;
+	}
+
+	return 0;
+}
+
+static int tasha_set_channel_map(struct snd_soc_dai *dai,
+				 unsigned int tx_num, unsigned int *tx_slot,
+				 unsigned int rx_num, unsigned int *rx_slot)
+{
+	struct tasha_priv *tasha;
+	struct wcd9xxx *core;
+	struct wcd9xxx_codec_dai_data *dai_data = NULL;
+
+	if (!dai) {
+		pr_err("%s: dai is empty\n", __func__);
+		return -EINVAL;
+	}
+	tasha = snd_soc_codec_get_drvdata(dai->codec);
+	core = dev_get_drvdata(dai->codec->dev->parent);
+
+	if (!tx_slot || !rx_slot) {
+		pr_err("%s: Invalid tx_slot=%pK, rx_slot=%pK\n",
+			__func__, tx_slot, rx_slot);
+		return -EINVAL;
+	}
+	pr_debug("%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n"
+		 "tasha->intf_type %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num,
+		 tasha->intf_type);
+
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		wcd9xxx_init_slimslave(core, core->slim->laddr,
+					   tx_num, tx_slot, rx_num, rx_slot);
+		/* Reserve TX12/TX13 for MAD data channel */
+		dai_data = &tasha->dai[AIF4_MAD_TX];
+		if (dai_data) {
+			if (TASHA_IS_2_0(tasha->wcd9xxx))
+				list_add_tail(&core->tx_chs[TASHA_TX13].list,
+					      &dai_data->wcd9xxx_ch_list);
+			else
+				list_add_tail(&core->tx_chs[TASHA_TX12].list,
+					      &dai_data->wcd9xxx_ch_list);
+		}
+	}
+	return 0;
+}
+
+static int tasha_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	return 0;
+}
+
+static void tasha_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+		return;
+
+	if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+	    test_bit(SB_CLK_GEAR, &tasha->status_mask)) {
+		tasha_codec_vote_max_bw(dai->codec, false);
+		clear_bit(SB_CLK_GEAR, &tasha->status_mask);
+	}
+}
+
+static int tasha_set_decimator_rate(struct snd_soc_dai *dai,
+				    u8 tx_fs_rate_reg_val, u32 sample_rate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port = 0;
+	u8 shift = 0, shift_val = 0, tx_mux_sel = 0;
+	int decimator = -1;
+	u16 tx_port_reg = 0, tx_fs_reg = 0;
+
+	list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
+		tx_port = ch->port;
+		dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
+
+		if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) {
+			dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n",
+				__func__, tx_port, dai->id);
+			return -EINVAL;
+		}
+		/* Find the SB TX MUX input - which decimator is connected */
+		if (tx_port < 4) {
+			tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG0;
+			shift = (tx_port << 1);
+			shift_val = 0x03;
+		} else if ((tx_port >= 4) && (tx_port < 8)) {
+			tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG1;
+			shift = ((tx_port - 4) << 1);
+			shift_val = 0x03;
+		} else if ((tx_port >= 8) && (tx_port < 11)) {
+			tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG2;
+			shift = ((tx_port - 8) << 1);
+			shift_val = 0x03;
+		} else if (tx_port == 11) {
+			tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
+			shift = 0;
+			shift_val = 0x0F;
+		} else if (tx_port == 13) {
+			tx_port_reg = WCD9335_CDC_IF_ROUTER_TX_MUX_CFG3;
+			shift = 4;
+			shift_val = 0x03;
+		}
+		tx_mux_sel = snd_soc_read(codec, tx_port_reg) &
+					  (shift_val << shift);
+		tx_mux_sel = tx_mux_sel >> shift;
+
+		if (tx_port <= 8) {
+			if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3))
+				decimator = tx_port;
+		} else if (tx_port <= 10) {
+			if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+				decimator = ((tx_port == 9) ? 7 : 6);
+		} else if (tx_port == 11) {
+			if ((tx_mux_sel >= 1) && (tx_mux_sel < 7))
+				decimator = tx_mux_sel - 1;
+		} else if (tx_port == 13) {
+			if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+				decimator = 5;
+		}
+
+		if (decimator >= 0) {
+			tx_fs_reg = WCD9335_CDC_TX0_TX_PATH_CTL +
+				    16 * decimator;
+			dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+				__func__, decimator, tx_port, sample_rate);
+			snd_soc_update_bits(codec, tx_fs_reg, 0x0F,
+					    tx_fs_rate_reg_val);
+		} else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) {
+			/* Check if the TX Mux input is RX MIX TXn */
+			dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to SLIM TX%u\n",
+					__func__, tx_port, tx_port);
+		} else {
+			dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n",
+				__func__, decimator);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int tasha_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+					   u8 int_mix_fs_rate_reg_val,
+					   u32 sample_rate)
+{
+	u8 int_2_inp;
+	u32 j;
+	u16 int_mux_cfg1, int_fs_reg;
+	u8 int_mux_cfg1_val;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
+		int_2_inp = ch->port + INTn_2_INP_SEL_RX0 -
+				  TASHA_RX_PORT_START_NUMBER;
+		if ((int_2_inp < INTn_2_INP_SEL_RX0) ||
+		   (int_2_inp > INTn_2_INP_SEL_RX7)) {
+			pr_err("%s: Invalid RX%u port, Dai ID is %d\n",
+				__func__,
+				(ch->port - TASHA_RX_PORT_START_NUMBER),
+				dai->id);
+			return -EINVAL;
+		}
+
+		int_mux_cfg1 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG1;
+		for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) {
+			int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) &
+						0x0F;
+			if (int_mux_cfg1_val == int_2_inp) {
+				int_fs_reg = WCD9335_CDC_RX0_RX_PATH_MIX_CTL +
+						20 * j;
+				pr_debug("%s: AIF_MIX_PB DAI(%d) connected to INT%u_2\n",
+					  __func__, dai->id, j);
+				pr_debug("%s: set INT%u_2 sample rate to %u\n",
+					__func__, j, sample_rate);
+				snd_soc_update_bits(codec, int_fs_reg,
+						0x0F, int_mix_fs_rate_reg_val);
+			}
+			int_mux_cfg1 += 2;
+		}
+	}
+	return 0;
+}
+
+static int tasha_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+					    u8 int_prim_fs_rate_reg_val,
+					    u32 sample_rate)
+{
+	u8 int_1_mix1_inp;
+	u32 j;
+	u16 int_mux_cfg0, int_mux_cfg1;
+	u16 int_fs_reg;
+	u8 int_mux_cfg0_val, int_mux_cfg1_val;
+	u8 inp0_sel, inp1_sel, inp2_sel;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	list_for_each_entry(ch, &tasha->dai[dai->id].wcd9xxx_ch_list, list) {
+		int_1_mix1_inp = ch->port + INTn_1_MIX_INP_SEL_RX0 -
+				  TASHA_RX_PORT_START_NUMBER;
+		if ((int_1_mix1_inp < INTn_1_MIX_INP_SEL_RX0) ||
+		   (int_1_mix1_inp > INTn_1_MIX_INP_SEL_RX7)) {
+			pr_err("%s: Invalid RX%u port, Dai ID is %d\n",
+				__func__,
+				(ch->port - TASHA_RX_PORT_START_NUMBER),
+				dai->id);
+			return -EINVAL;
+		}
+
+		int_mux_cfg0 = WCD9335_CDC_RX_INP_MUX_RX_INT0_CFG0;
+
+		/*
+		 * Loop through all interpolator MUX inputs and find out
+		 * to which interpolator input, the slim rx port
+		 * is connected
+		 */
+		for (j = 0; j < TASHA_NUM_INTERPOLATORS; j++) {
+			int_mux_cfg1 = int_mux_cfg0 + 1;
+
+			int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0);
+			int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1);
+			inp0_sel = int_mux_cfg0_val & 0x0F;
+			inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F;
+			inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F;
+			if ((inp0_sel == int_1_mix1_inp) ||
+			    (inp1_sel == int_1_mix1_inp) ||
+			    (inp2_sel == int_1_mix1_inp)) {
+				int_fs_reg = WCD9335_CDC_RX0_RX_PATH_CTL +
+					     20 * j;
+				pr_debug("%s: AIF_PB DAI(%d) connected to INT%u_1\n",
+					  __func__, dai->id, j);
+				pr_debug("%s: set INT%u_1 sample rate to %u\n",
+					__func__, j, sample_rate);
+				/* sample_rate is in Hz */
+				if ((j == 0) && (sample_rate == 44100)) {
+					pr_info("%s: Cannot set 44.1KHz on INT0\n",
+						__func__);
+				} else
+					snd_soc_update_bits(codec, int_fs_reg,
+						0x0F, int_prim_fs_rate_reg_val);
+			}
+			int_mux_cfg0 += 2;
+		}
+	}
+
+	return 0;
+}
+
+
+static int tasha_set_interpolator_rate(struct snd_soc_dai *dai,
+				       u32 sample_rate)
+{
+	int rate_val = 0;
+	int i, ret;
+
+	/* set mixing path rate */
+	for (i = 0; i < ARRAY_SIZE(int_mix_sample_rate_val); i++) {
+		if (sample_rate ==
+				int_mix_sample_rate_val[i].sample_rate) {
+			rate_val =
+				int_mix_sample_rate_val[i].rate_val;
+			break;
+		}
+	}
+	if ((i == ARRAY_SIZE(int_mix_sample_rate_val)) ||
+			(rate_val < 0))
+		goto prim_rate;
+	ret = tasha_set_mix_interpolator_rate(dai,
+			(u8) rate_val, sample_rate);
+prim_rate:
+	/* set primary path sample rate */
+	for (i = 0; i < ARRAY_SIZE(int_prim_sample_rate_val); i++) {
+		if (sample_rate ==
+				int_prim_sample_rate_val[i].sample_rate) {
+			rate_val =
+				int_prim_sample_rate_val[i].rate_val;
+			break;
+		}
+	}
+	if ((i == ARRAY_SIZE(int_prim_sample_rate_val)) ||
+			(rate_val < 0))
+		return -EINVAL;
+	ret = tasha_set_prim_interpolator_rate(dai,
+			(u8) rate_val, sample_rate);
+	return ret;
+}
+
+static int tasha_prepare(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+	if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK) &&
+	    test_bit(SB_CLK_GEAR, &tasha->status_mask)) {
+		tasha_codec_vote_max_bw(dai->codec, false);
+		clear_bit(SB_CLK_GEAR, &tasha->status_mask);
+	}
+	return 0;
+}
+
+static int tasha_hw_params(struct snd_pcm_substream *substream,
+			   struct snd_pcm_hw_params *params,
+			   struct snd_soc_dai *dai)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+	int ret;
+	int tx_fs_rate = -EINVAL;
+	int rx_fs_rate = -EINVAL;
+	int i2s_bit_mode;
+	struct snd_soc_codec *codec = dai->codec;
+
+	pr_debug("%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n", __func__,
+		 dai->name, dai->id, params_rate(params),
+		 params_channels(params));
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ret = tasha_set_interpolator_rate(dai, params_rate(params));
+		if (ret) {
+			pr_err("%s: cannot set sample rate: %u\n",
+				__func__, params_rate(params));
+			return ret;
+		}
+		switch (params_width(params)) {
+		case 16:
+			tasha->dai[dai->id].bit_width = 16;
+			i2s_bit_mode = 0x01;
+			break;
+		case 24:
+			tasha->dai[dai->id].bit_width = 24;
+			i2s_bit_mode = 0x00;
+			break;
+		default:
+			return -EINVAL;
+		}
+		tasha->dai[dai->id].rate = params_rate(params);
+		if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			switch (params_rate(params)) {
+			case 8000:
+				rx_fs_rate = 0;
+				break;
+			case 16000:
+				rx_fs_rate = 1;
+				break;
+			case 32000:
+				rx_fs_rate = 2;
+				break;
+			case 48000:
+				rx_fs_rate = 3;
+				break;
+			case 96000:
+				rx_fs_rate = 4;
+				break;
+			case 192000:
+				rx_fs_rate = 5;
+				break;
+			default:
+				dev_err(tasha->dev,
+				"%s: Invalid RX sample rate: %d\n",
+				__func__, params_rate(params));
+				return -EINVAL;
+			};
+			snd_soc_update_bits(codec,
+					WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+					0x20, i2s_bit_mode << 5);
+			snd_soc_update_bits(codec,
+					WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+					0x1c, (rx_fs_rate << 2));
+		}
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		switch (params_rate(params)) {
+		case 8000:
+			tx_fs_rate = 0;
+			break;
+		case 16000:
+			tx_fs_rate = 1;
+			break;
+		case 32000:
+			tx_fs_rate = 3;
+			break;
+		case 48000:
+			tx_fs_rate = 4;
+			break;
+		case 96000:
+			tx_fs_rate = 5;
+			break;
+		case 192000:
+			tx_fs_rate = 6;
+			break;
+		case 384000:
+			tx_fs_rate = 7;
+			break;
+		default:
+			dev_err(tasha->dev, "%s: Invalid TX sample rate: %d\n",
+				__func__, params_rate(params));
+			return -EINVAL;
+
+		};
+		if (dai->id != AIF4_VIFEED &&
+		    dai->id != AIF4_MAD_TX) {
+			ret = tasha_set_decimator_rate(dai, tx_fs_rate,
+					params_rate(params));
+			if (ret < 0) {
+				dev_err(tasha->dev, "%s: cannot set TX Decimator rate: %d\n",
+					__func__, tx_fs_rate);
+				return ret;
+			}
+		}
+		tasha->dai[dai->id].rate = params_rate(params);
+		switch (params_width(params)) {
+		case 16:
+			tasha->dai[dai->id].bit_width = 16;
+			i2s_bit_mode = 0x01;
+			break;
+		case 24:
+			tasha->dai[dai->id].bit_width = 24;
+			i2s_bit_mode = 0x00;
+			break;
+		case 32:
+			tasha->dai[dai->id].bit_width = 32;
+			i2s_bit_mode = 0x00;
+			break;
+		default:
+			dev_err(tasha->dev, "%s: Invalid format 0x%x\n",
+				__func__, params_width(params));
+			return -EINVAL;
+		};
+		if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+				0x20, i2s_bit_mode << 5);
+			if (tx_fs_rate > 1)
+				tx_fs_rate--;
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+				0x1c, tx_fs_rate << 2);
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG,
+				0x05, 0x05);
+
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG,
+				0x05, 0x05);
+
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG,
+				0x05, 0x05);
+
+			snd_soc_update_bits(codec,
+				WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG,
+				0x05, 0x05);
+		}
+		break;
+	default:
+		pr_err("%s: Invalid stream type %d\n", __func__,
+			substream->stream);
+		return -EINVAL;
+	};
+	if (dai->id == AIF4_VIFEED)
+		tasha->dai[dai->id].bit_width = 32;
+
+	return 0;
+}
+
+static int tasha_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(dai->codec);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		/* CPU is master */
+		if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			if (dai->id == AIF1_CAP)
+				snd_soc_update_bits(dai->codec,
+					WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+					0x2, 0);
+			else if (dai->id == AIF1_PB)
+				snd_soc_update_bits(dai->codec,
+					WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+					0x2, 0);
+		}
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		/* CPU is slave */
+		if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+			if (dai->id == AIF1_CAP)
+				snd_soc_update_bits(dai->codec,
+					WCD9335_DATA_HUB_DATA_HUB_TX_I2S_CTL,
+					0x2, 0x2);
+			else if (dai->id == AIF1_PB)
+				snd_soc_update_bits(dai->codec,
+					WCD9335_DATA_HUB_DATA_HUB_RX_I2S_CTL,
+					0x2, 0x2);
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int tasha_set_dai_sysclk(struct snd_soc_dai *dai,
+		int clk_id, unsigned int freq, int dir)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+
+static struct snd_soc_dai_ops tasha_dai_ops = {
+	.startup = tasha_startup,
+	.shutdown = tasha_shutdown,
+	.hw_params = tasha_hw_params,
+	.prepare = tasha_prepare,
+	.set_sysclk = tasha_set_dai_sysclk,
+	.set_fmt = tasha_set_dai_fmt,
+	.set_channel_map = tasha_set_channel_map,
+	.get_channel_map = tasha_get_channel_map,
+};
+
+static struct snd_soc_dai_driver tasha_dai[] = {
+	{
+		.name = "tasha_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_rx2",
+		.id = AIF2_PB,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_tx2",
+		.id = AIF2_CAP,
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 8,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_rx3",
+		.id = AIF3_PB,
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_tx3",
+		.id = AIF3_CAP,
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_rx4",
+		.id = AIF4_PB,
+		.playback = {
+			.stream_name = "AIF4 Playback",
+			.rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_mix_rx1",
+		.id = AIF_MIX1_PB,
+		.playback = {
+			.stream_name = "AIF Mix Playback",
+			.rates = WCD9335_RATES_MASK | WCD9335_FRAC_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 8,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_mad1",
+		.id = AIF4_MAD_TX,
+		.capture = {
+			.stream_name = "AIF4 MAD TX",
+			.rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 |
+				 SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000,
+			.formats = TASHA_FORMATS_S16_S24_S32_LE,
+			.rate_min = 16000,
+			.rate_max = 384000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_vifeedback",
+		.id = AIF4_VIFEED,
+		.capture = {
+			.stream_name = "VIfeed",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+			.formats = TASHA_FORMATS_S16_S24_S32_LE,
+			.rate_max = 48000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		 },
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_cpe",
+		.id = AIF5_CPE_TX,
+		.capture = {
+			.stream_name = "AIF5 CPE TX",
+			.rates = SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000,
+			.formats = TASHA_FORMATS_S16_S24_S32_LE,
+			.rate_min = 16000,
+			.rate_max = 48000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+	},
+};
+
+static struct snd_soc_dai_driver tasha_i2s_dai[] = {
+	{
+		.name = "tasha_i2s_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_i2s_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_i2s_rx2",
+		.id = AIF2_PB,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tasha_dai_ops,
+	},
+	{
+		.name = "tasha_i2s_tx2",
+		.id = AIF2_CAP,
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.rates = WCD9335_RATES_MASK,
+			.formats = TASHA_FORMATS_S16_S24_LE,
+			.rate_max = 192000,
+			.rate_min = 8000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tasha_dai_ops,
+	},
+};
+
+static void tasha_codec_power_gate_digital_core(struct tasha_priv *tasha)
+{
+	struct snd_soc_codec *codec = tasha->codec;
+
+	if (!codec)
+		return;
+
+	mutex_lock(&tasha->power_lock);
+	dev_dbg(codec->dev, "%s: Entering power gating function, %d\n",
+		__func__, tasha->power_active_ref);
+
+	if (tasha->power_active_ref > 0)
+		goto exit;
+
+	wcd9xxx_set_power_state(tasha->wcd9xxx,
+			WCD_REGION_POWER_COLLAPSE_BEGIN,
+			WCD9XXX_DIG_CORE_REGION_1);
+	snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			0x01, 0x00);
+	snd_soc_update_bits(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			0x02, 0x00);
+	clear_bit(AUDIO_NOMINAL, &tasha->status_mask);
+	tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage);
+	wcd9xxx_set_power_state(tasha->wcd9xxx, WCD_REGION_POWER_DOWN,
+				WCD9XXX_DIG_CORE_REGION_1);
+exit:
+	dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n",
+		__func__, tasha->power_active_ref);
+	mutex_unlock(&tasha->power_lock);
+}
+
+static void tasha_codec_power_gate_work(struct work_struct *work)
+{
+	struct tasha_priv *tasha;
+	struct delayed_work *dwork;
+	struct snd_soc_codec *codec;
+
+	dwork = to_delayed_work(work);
+	tasha = container_of(dwork, struct tasha_priv, power_gate_work);
+	codec = tasha->codec;
+
+	if (!codec)
+		return;
+
+	tasha_codec_power_gate_digital_core(tasha);
+}
+
+/* called under power_lock acquisition */
+static int tasha_dig_core_remove_power_collapse(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	tasha_codec_vote_max_bw(codec, true);
+	snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+	snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+	snd_soc_write(codec, WCD9335_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+	snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x00);
+	snd_soc_update_bits(codec, WCD9335_CODEC_RPM_RST_CTL, 0x02, 0x02);
+
+	wcd9xxx_set_power_state(tasha->wcd9xxx,
+			WCD_REGION_POWER_COLLAPSE_REMOVE,
+			WCD9XXX_DIG_CORE_REGION_1);
+	regcache_mark_dirty(codec->component.regmap);
+	regcache_sync_region(codec->component.regmap,
+			     TASHA_DIG_CORE_REG_MIN, TASHA_DIG_CORE_REG_MAX);
+	tasha_codec_vote_max_bw(codec, false);
+
+	return 0;
+}
+
+static int tasha_dig_core_power_collapse(struct tasha_priv *tasha,
+					 int req_state)
+{
+	struct snd_soc_codec *codec;
+	int cur_state;
+
+	/* Exit if feature is disabled */
+	if (!dig_core_collapse_enable)
+		return 0;
+
+	mutex_lock(&tasha->power_lock);
+	if (req_state == POWER_COLLAPSE)
+		tasha->power_active_ref--;
+	else if (req_state == POWER_RESUME)
+		tasha->power_active_ref++;
+	else
+		goto unlock_mutex;
+
+	if (tasha->power_active_ref < 0) {
+		dev_dbg(tasha->dev, "%s: power_active_ref is negative\n",
+			__func__);
+		goto unlock_mutex;
+	}
+
+	codec = tasha->codec;
+	if (!codec)
+		goto unlock_mutex;
+
+	if (req_state == POWER_COLLAPSE) {
+		if (tasha->power_active_ref == 0) {
+			schedule_delayed_work(&tasha->power_gate_work,
+			msecs_to_jiffies(dig_core_collapse_timer * 1000));
+		}
+	} else if (req_state == POWER_RESUME) {
+		if (tasha->power_active_ref == 1) {
+			/*
+			 * At this point, there can be two cases:
+			 * 1. Core already in power collapse state
+			 * 2. Timer kicked in and still did not expire or
+			 * waiting for the power_lock
+			 */
+			cur_state = wcd9xxx_get_current_power_state(
+						tasha->wcd9xxx,
+						WCD9XXX_DIG_CORE_REGION_1);
+			if (cur_state == WCD_REGION_POWER_DOWN)
+				tasha_dig_core_remove_power_collapse(codec);
+			else {
+				mutex_unlock(&tasha->power_lock);
+				cancel_delayed_work_sync(
+						&tasha->power_gate_work);
+				mutex_lock(&tasha->power_lock);
+			}
+		}
+	}
+
+unlock_mutex:
+	mutex_unlock(&tasha->power_lock);
+
+	return 0;
+}
+
+static int __tasha_cdc_mclk_enable_locked(struct tasha_priv *tasha,
+					  bool enable)
+{
+	int ret = 0;
+
+	if (!tasha->wcd_ext_clk) {
+		dev_err(tasha->dev, "%s: wcd ext clock is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(tasha->dev, "%s: mclk_enable = %u\n", __func__, enable);
+
+	if (enable) {
+		tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+		ret = tasha_cdc_req_mclk_enable(tasha, true);
+		if (ret)
+			goto err;
+
+		set_bit(AUDIO_NOMINAL, &tasha->status_mask);
+		tasha_codec_apply_sido_voltage(tasha,
+				SIDO_VOLTAGE_NOMINAL_MV);
+	} else {
+		if (!dig_core_collapse_enable) {
+			clear_bit(AUDIO_NOMINAL, &tasha->status_mask);
+			tasha_codec_update_sido_voltage(tasha,
+						sido_buck_svs_voltage);
+		}
+		tasha_cdc_req_mclk_enable(tasha, false);
+		tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+	}
+
+err:
+	return ret;
+}
+
+static int __tasha_cdc_mclk_enable(struct tasha_priv *tasha,
+				   bool enable)
+{
+	int ret;
+
+	WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr);
+	ret = __tasha_cdc_mclk_enable_locked(tasha, enable);
+	WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr);
+
+	return ret;
+}
+
+int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	return __tasha_cdc_mclk_enable(tasha, enable);
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_enable);
+
+int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable, bool dapm)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	dev_dbg(tasha->dev, "%s: clk_mode: %d, enable: %d, clk_internal: %d\n",
+		__func__, tasha->clk_mode, enable, tasha->clk_internal);
+	if (tasha->clk_mode || tasha->clk_internal) {
+		if (enable) {
+			tasha_cdc_sido_ccl_enable(tasha, true);
+			wcd_resmgr_enable_master_bias(tasha->resmgr);
+			tasha_dig_core_power_collapse(tasha, POWER_RESUME);
+			snd_soc_update_bits(codec,
+					WCD9335_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+					0x01, 0x01);
+			snd_soc_update_bits(codec,
+					WCD9335_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x01, 0x01);
+			set_bit(CPE_NOMINAL, &tasha->status_mask);
+			tasha_codec_update_sido_voltage(tasha,
+						SIDO_VOLTAGE_NOMINAL_MV);
+			tasha->clk_internal = true;
+		} else {
+			tasha->clk_internal = false;
+			clear_bit(CPE_NOMINAL, &tasha->status_mask);
+			tasha_codec_update_sido_voltage(tasha,
+						sido_buck_svs_voltage);
+			tasha_dig_core_power_collapse(tasha, POWER_COLLAPSE);
+			wcd_resmgr_disable_master_bias(tasha->resmgr);
+			tasha_cdc_sido_ccl_enable(tasha, false);
+		}
+	} else {
+		ret = __tasha_cdc_mclk_enable(tasha, enable);
+	}
+	return ret;
+}
+EXPORT_SYMBOL(tasha_cdc_mclk_tx_enable);
+
+static ssize_t tasha_codec_version_read(struct snd_info_entry *entry,
+			       void *file_private_data, struct file *file,
+			       char __user *buf, size_t count, loff_t pos)
+{
+	struct tasha_priv *tasha;
+	struct wcd9xxx *wcd9xxx;
+	char buffer[TASHA_VERSION_ENTRY_SIZE];
+	int len = 0;
+
+	tasha = (struct tasha_priv *) entry->private_data;
+	if (!tasha) {
+		pr_err("%s: tasha priv is null\n", __func__);
+		return -EINVAL;
+	}
+
+	wcd9xxx = tasha->wcd9xxx;
+
+	if (wcd9xxx->codec_type->id_major == TASHA_MAJOR) {
+		if (TASHA_IS_1_0(wcd9xxx))
+			len = snprintf(buffer, sizeof(buffer), "WCD9335_1_0\n");
+		else if (TASHA_IS_1_1(wcd9xxx))
+			len = snprintf(buffer, sizeof(buffer), "WCD9335_1_1\n");
+		else
+			snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+	} else if (wcd9xxx->codec_type->id_major == TASHA2P0_MAJOR) {
+		len = snprintf(buffer, sizeof(buffer), "WCD9335_2_0\n");
+	} else
+		len = snprintf(buffer, sizeof(buffer), "VER_UNDEFINED\n");
+
+	return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops tasha_codec_info_ops = {
+	.read = tasha_codec_version_read,
+};
+
+/*
+ * tasha_codec_info_create_codec_entry - creates wcd9335 module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wcd9335 module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int tasha_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+					struct snd_soc_codec *codec)
+{
+	struct snd_info_entry *version_entry;
+	struct tasha_priv *tasha;
+	struct snd_soc_card *card;
+
+	if (!codec_root || !codec)
+		return -EINVAL;
+
+	tasha = snd_soc_codec_get_drvdata(codec);
+	card = codec->component.card;
+	tasha->entry = snd_info_create_subdir(codec_root->module,
+					      "tasha", codec_root);
+	if (!tasha->entry) {
+		dev_dbg(codec->dev, "%s: failed to create wcd9335 entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry = snd_info_create_card_entry(card->snd_card,
+						   "version",
+						   tasha->entry);
+	if (!version_entry) {
+		dev_dbg(codec->dev, "%s: failed to create wcd9335 version entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry->private_data = tasha;
+	version_entry->size = TASHA_VERSION_ENTRY_SIZE;
+	version_entry->content = SNDRV_INFO_CONTENT_DATA;
+	version_entry->c.ops = &tasha_codec_info_ops;
+
+	if (snd_info_register(version_entry) < 0) {
+		snd_info_free_entry(version_entry);
+		return -ENOMEM;
+	}
+	tasha->version_entry = version_entry;
+
+	return 0;
+}
+EXPORT_SYMBOL(tasha_codec_info_create_codec_entry);
+
+static int __tasha_codec_internal_rco_ctrl(
+	struct snd_soc_codec *codec, bool enable)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (enable) {
+		tasha_cdc_sido_ccl_enable(tasha, true);
+		if (wcd_resmgr_get_clk_type(tasha->resmgr) ==
+		    WCD_CLK_RCO) {
+			ret = wcd_resmgr_enable_clk_block(tasha->resmgr,
+							  WCD_CLK_RCO);
+		} else {
+			ret = tasha_cdc_req_mclk_enable(tasha, true);
+			ret |= wcd_resmgr_enable_clk_block(tasha->resmgr,
+							   WCD_CLK_RCO);
+			ret |= tasha_cdc_req_mclk_enable(tasha, false);
+		}
+
+	} else {
+		ret = wcd_resmgr_disable_clk_block(tasha->resmgr,
+						   WCD_CLK_RCO);
+		tasha_cdc_sido_ccl_enable(tasha, false);
+	}
+
+	if (ret) {
+		dev_err(codec->dev, "%s: Error in %s RCO\n",
+			__func__, (enable ? "enabling" : "disabling"));
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * tasha_codec_internal_rco_ctrl()
+ * Make sure that the caller does not acquire
+ * BG_CLK_LOCK.
+ */
+static int tasha_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+				  bool enable)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	WCD9XXX_V2_BG_CLK_LOCK(tasha->resmgr);
+	ret = __tasha_codec_internal_rco_ctrl(codec, enable);
+	WCD9XXX_V2_BG_CLK_UNLOCK(tasha->resmgr);
+	return ret;
+}
+
+/*
+ * tasha_mbhc_hs_detect: starts mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ * @mbhc_cfg: handle to mbhc configuration structure
+ * return 0 if mbhc_start is success or error code in case of failure
+ */
+int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
+			 struct wcd_mbhc_config *mbhc_cfg)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	return wcd_mbhc_start(&tasha->mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(tasha_mbhc_hs_detect);
+
+/*
+ * tasha_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ */
+void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	wcd_mbhc_stop(&tasha->mbhc);
+}
+EXPORT_SYMBOL(tasha_mbhc_hs_detect_exit);
+
+static int wcd9335_get_micb_vout_ctl_val(u32 micb_mv)
+{
+	/* min micbias voltage is 1V and maximum is 2.85V */
+	if (micb_mv < 1000 || micb_mv > 2850) {
+		pr_err("%s: unsupported micbias voltage\n", __func__);
+		return -EINVAL;
+	}
+
+	return (micb_mv - 1000) / 50;
+}
+
+static const struct tasha_reg_mask_val tasha_reg_update_reset_val_1_1[] = {
+	{WCD9335_RCO_CTRL_2, 0xFF, 0x47},
+	{WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_1[] = {
+	{WCD9335_FLYBACK_VNEG_DAC_CTRL_1, 0xFF, 0x65},
+	{WCD9335_FLYBACK_VNEG_DAC_CTRL_2, 0xFF, 0x52},
+	{WCD9335_FLYBACK_VNEG_DAC_CTRL_3, 0xFF, 0xAF},
+	{WCD9335_FLYBACK_VNEG_DAC_CTRL_4, 0xFF, 0x60},
+	{WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0xF4},
+	{WCD9335_FLYBACK_VNEG_CTRL_9, 0xFF, 0x40},
+	{WCD9335_FLYBACK_VNEG_CTRL_2, 0xFF, 0x4F},
+	{WCD9335_FLYBACK_EN, 0xFF, 0x6E},
+	{WCD9335_CDC_RX2_RX_PATH_SEC0, 0xF8, 0xF8},
+	{WCD9335_CDC_RX1_RX_PATH_SEC0, 0xF8, 0xF8},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_val_1_0[] = {
+	{WCD9335_FLYBACK_VNEG_CTRL_3, 0xFF, 0x54},
+	{WCD9335_CDC_RX2_RX_PATH_SEC0, 0xFC, 0xFC},
+	{WCD9335_CDC_RX1_RX_PATH_SEC0, 0xFC, 0xFC},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_val_2_0[] = {
+	{WCD9335_RCO_CTRL_2, 0x0F, 0x08},
+	{WCD9335_RX_BIAS_FLYB_MID_RST, 0xF0, 0x10},
+	{WCD9335_FLYBACK_CTRL_1, 0x20, 0x20},
+	{WCD9335_HPH_OCP_CTL, 0xFF, 0x7A},
+	{WCD9335_HPH_L_TEST, 0x01, 0x01},
+	{WCD9335_HPH_R_TEST, 0x01, 0x01},
+	{WCD9335_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+	{WCD9335_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+	{WCD9335_CDC_COMPANDER7_CTL7, 0x1E, 0x18},
+	{WCD9335_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+	{WCD9335_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
+	{WCD9335_CDC_COMPANDER8_CTL7, 0x1E, 0x18},
+	{WCD9335_CDC_TX0_TX_PATH_SEC7, 0xFF, 0x45},
+	{WCD9335_CDC_RX0_RX_PATH_SEC0, 0xFC, 0xF4},
+	{WCD9335_HPH_REFBUFF_LP_CTL, 0x08, 0x08},
+	{WCD9335_HPH_REFBUFF_LP_CTL, 0x06, 0x02},
+	{WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xA0},
+	{WCD9335_SE_LO_COM1, 0xFF, 0xC0},
+	{WCD9335_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4},
+	{WCD9335_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4},
+	{WCD9335_CDC_RX5_RX_PATH_SEC0, 0xFC, 0xF8},
+	{WCD9335_CDC_RX6_RX_PATH_SEC0, 0xFC, 0xF8},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_defaults[] = {
+	{WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x00},
+	{WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x01},
+	{WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x04, 0x04},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_i2c_defaults[] = {
+	{WCD9335_ANA_CLK_TOP, 0x20, 0x20},
+	{WCD9335_CODEC_RPM_CLK_GATE, 0x03, 0x01},
+	{WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x03, 0x00},
+	{WCD9335_CODEC_RPM_CLK_MCLK_CFG, 0x05, 0x05},
+	{WCD9335_DATA_HUB_DATA_HUB_RX0_INP_CFG, 0x01, 0x01},
+	{WCD9335_DATA_HUB_DATA_HUB_RX1_INP_CFG, 0x01, 0x01},
+	{WCD9335_DATA_HUB_DATA_HUB_RX2_INP_CFG, 0x01, 0x01},
+	{WCD9335_DATA_HUB_DATA_HUB_RX3_INP_CFG, 0x01, 0x01},
+	{WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_L_CFG, 0x05, 0x05},
+	{WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD0_R_CFG, 0x05, 0x05},
+	{WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_L_CFG, 0x05, 0x05},
+	{WCD9335_DATA_HUB_DATA_HUB_TX_I2S_SD1_R_CFG, 0x05, 0x05},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_common_val[] = {
+	/* Rbuckfly/R_EAR(32) */
+	{WCD9335_CDC_CLSH_K2_MSB, 0x0F, 0x00},
+	{WCD9335_CDC_CLSH_K2_LSB, 0xFF, 0x60},
+	{WCD9335_CPE_SS_DMIC_CFG, 0x80, 0x00},
+	{WCD9335_CDC_BOOST0_BOOST_CTL, 0x70, 0x50},
+	{WCD9335_CDC_BOOST1_BOOST_CTL, 0x70, 0x50},
+	{WCD9335_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08},
+	{WCD9335_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08},
+	{WCD9335_ANA_LO_1_2, 0x3C, 0X3C},
+	{WCD9335_DIFF_LO_COM_SWCAP_REFBUF_FREQ, 0x70, 0x00},
+	{WCD9335_SOC_MAD_AUDIO_CTL_2, 0x03, 0x03},
+	{WCD9335_CDC_TOP_TOP_CFG1, 0x02, 0x02},
+	{WCD9335_CDC_TOP_TOP_CFG1, 0x01, 0x01},
+	{WCD9335_EAR_CMBUFF, 0x08, 0x00},
+	{WCD9335_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+	{WCD9335_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+	{WCD9335_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+	{WCD9335_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+	{WCD9335_CDC_RX0_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX1_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX2_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX3_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX4_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX5_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX6_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX7_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX8_RX_PATH_CFG0, 0x01, 0x01},
+	{WCD9335_CDC_RX0_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX1_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX2_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX3_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX4_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX5_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX6_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX7_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_CDC_RX8_RX_PATH_MIX_CFG, 0x01, 0x01},
+	{WCD9335_VBADC_IBIAS_FE, 0x0C, 0x08},
+};
+
+static const struct tasha_reg_mask_val tasha_codec_reg_init_1_x_val[] = {
+	/* Enable TX HPF Filter & Linear Phase */
+	{WCD9335_CDC_TX0_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX1_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX2_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX3_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX4_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX5_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX6_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX7_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_TX8_TX_PATH_CFG0, 0x11, 0x11},
+	{WCD9335_CDC_RX0_RX_PATH_SEC0, 0xF8, 0xF8},
+	{WCD9335_CDC_RX0_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX1_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX2_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX3_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX4_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX5_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX6_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX7_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX8_RX_PATH_SEC1, 0x08, 0x08},
+	{WCD9335_CDC_RX0_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX1_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX2_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX3_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX4_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX5_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX6_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX7_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_RX8_RX_PATH_MIX_SEC0, 0x08, 0x08},
+	{WCD9335_CDC_TX0_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX1_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX2_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX3_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX4_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX5_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX6_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX7_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_TX8_TX_PATH_SEC2, 0x01, 0x01},
+	{WCD9335_CDC_RX3_RX_PATH_SEC0, 0xF8, 0xF0},
+	{WCD9335_CDC_RX4_RX_PATH_SEC0, 0xF8, 0xF0},
+	{WCD9335_CDC_RX5_RX_PATH_SEC0, 0xF8, 0xF8},
+	{WCD9335_CDC_RX6_RX_PATH_SEC0, 0xF8, 0xF8},
+	{WCD9335_RX_OCP_COUNT, 0xFF, 0xFF},
+	{WCD9335_HPH_OCP_CTL, 0xF0, 0x70},
+	{WCD9335_CPE_SS_CPAR_CFG, 0xFF, 0x00},
+	{WCD9335_FLYBACK_VNEG_CTRL_1, 0xFF, 0x63},
+	{WCD9335_FLYBACK_VNEG_CTRL_4, 0xFF, 0x7F},
+	{WCD9335_CLASSH_CTRL_VCL_1, 0xFF, 0x60},
+	{WCD9335_CLASSH_CTRL_CCL_5, 0xFF, 0x40},
+	{WCD9335_RX_TIMER_DIV, 0xFF, 0x32},
+	{WCD9335_SE_LO_COM2, 0xFF, 0x01},
+	{WCD9335_MBHC_ZDET_ANA_CTL, 0x0F, 0x07},
+	{WCD9335_RX_BIAS_HPH_PA, 0xF0, 0x60},
+	{WCD9335_HPH_RDAC_LDO_CTL, 0x88, 0x88},
+	{WCD9335_HPH_L_EN, 0x20, 0x20},
+	{WCD9335_HPH_R_EN, 0x20, 0x20},
+	{WCD9335_DIFF_LO_CORE_OUT_PROG, 0xFC, 0xD8},
+	{WCD9335_CDC_RX5_RX_PATH_SEC3, 0xBD, 0xBD},
+	{WCD9335_CDC_RX6_RX_PATH_SEC3, 0xBD, 0xBD},
+	{WCD9335_DIFF_LO_COM_PA_FREQ, 0x70, 0x40},
+};
+
+static void tasha_update_reg_reset_values(struct snd_soc_codec *codec)
+{
+	u32 i;
+	struct wcd9xxx *tasha_core = dev_get_drvdata(codec->dev->parent);
+
+	if (TASHA_IS_1_1(tasha_core)) {
+		for (i = 0; i < ARRAY_SIZE(tasha_reg_update_reset_val_1_1);
+		     i++)
+			snd_soc_write(codec,
+				      tasha_reg_update_reset_val_1_1[i].reg,
+				      tasha_reg_update_reset_val_1_1[i].val);
+	}
+}
+
+static void tasha_codec_init_reg(struct snd_soc_codec *codec)
+{
+	u32 i;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_common_val); i++)
+		snd_soc_update_bits(codec,
+				tasha_codec_reg_init_common_val[i].reg,
+				tasha_codec_reg_init_common_val[i].mask,
+				tasha_codec_reg_init_common_val[i].val);
+
+	if (TASHA_IS_1_1(wcd9xxx) ||
+	    TASHA_IS_1_0(wcd9xxx))
+		for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_1_x_val); i++)
+			snd_soc_update_bits(codec,
+					tasha_codec_reg_init_1_x_val[i].reg,
+					tasha_codec_reg_init_1_x_val[i].mask,
+					tasha_codec_reg_init_1_x_val[i].val);
+
+	if (TASHA_IS_1_1(wcd9xxx)) {
+		for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_1); i++)
+			snd_soc_update_bits(codec,
+					tasha_codec_reg_init_val_1_1[i].reg,
+					tasha_codec_reg_init_val_1_1[i].mask,
+					tasha_codec_reg_init_val_1_1[i].val);
+	} else if (TASHA_IS_1_0(wcd9xxx)) {
+		for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_1_0); i++)
+			snd_soc_update_bits(codec,
+					tasha_codec_reg_init_val_1_0[i].reg,
+					tasha_codec_reg_init_val_1_0[i].mask,
+					tasha_codec_reg_init_val_1_0[i].val);
+	} else if (TASHA_IS_2_0(wcd9xxx)) {
+		for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_init_val_2_0); i++)
+			snd_soc_update_bits(codec,
+					tasha_codec_reg_init_val_2_0[i].reg,
+					tasha_codec_reg_init_val_2_0[i].mask,
+					tasha_codec_reg_init_val_2_0[i].val);
+	}
+}
+
+static void tasha_update_reg_defaults(struct tasha_priv *tasha)
+{
+	u32 i;
+	struct wcd9xxx *wcd9xxx;
+
+	wcd9xxx = tasha->wcd9xxx;
+	for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_defaults); i++)
+		regmap_update_bits(wcd9xxx->regmap,
+				   tasha_codec_reg_defaults[i].reg,
+				   tasha_codec_reg_defaults[i].mask,
+				   tasha_codec_reg_defaults[i].val);
+
+	tasha->intf_type = wcd9xxx_get_intf_type();
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+		for (i = 0; i < ARRAY_SIZE(tasha_codec_reg_i2c_defaults); i++)
+			regmap_update_bits(wcd9xxx->regmap,
+					   tasha_codec_reg_i2c_defaults[i].reg,
+					   tasha_codec_reg_i2c_defaults[i].mask,
+					   tasha_codec_reg_i2c_defaults[i].val);
+
+}
+
+static void tasha_slim_interface_init_reg(struct snd_soc_codec *codec)
+{
+	int i;
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
+		wcd9xxx_interface_reg_write(priv->wcd9xxx,
+					    TASHA_SLIM_PGD_PORT_INT_EN0 + i,
+					    0xFF);
+}
+
+static irqreturn_t tasha_slimbus_irq(int irq, void *data)
+{
+	struct tasha_priv *priv = data;
+	unsigned long status = 0;
+	int i, j, port_id, k;
+	u32 bit;
+	u8 val, int_val = 0;
+	bool tx, cleared;
+	unsigned short reg = 0;
+
+	for (i = TASHA_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+	     i <= TASHA_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+		val = wcd9xxx_interface_reg_read(priv->wcd9xxx, i);
+		status |= ((u32)val << (8 * j));
+	}
+
+	for_each_set_bit(j, &status, 32) {
+		tx = (j >= 16 ? true : false);
+		port_id = (tx ? j - 16 : j);
+		val = wcd9xxx_interface_reg_read(priv->wcd9xxx,
+				TASHA_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+		if (val) {
+			if (!tx)
+				reg = TASHA_SLIM_PGD_PORT_INT_EN0 +
+					(port_id / 8);
+			else
+				reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 +
+					(port_id / 8);
+			int_val = wcd9xxx_interface_reg_read(
+				priv->wcd9xxx, reg);
+			/*
+			 * Ignore interrupts for ports for which the
+			 * interrupts are not specifically enabled.
+			 */
+			if (!(int_val & (1 << (port_id % 8))))
+				continue;
+		}
+		if (val & TASHA_SLIM_IRQ_OVERFLOW)
+			pr_err_ratelimited(
+			   "%s: overflow error on %s port %d, value %x\n",
+			   __func__, (tx ? "TX" : "RX"), port_id, val);
+		if (val & TASHA_SLIM_IRQ_UNDERFLOW)
+			pr_err_ratelimited(
+			   "%s: underflow error on %s port %d, value %x\n",
+			   __func__, (tx ? "TX" : "RX"), port_id, val);
+		if ((val & TASHA_SLIM_IRQ_OVERFLOW) ||
+			(val & TASHA_SLIM_IRQ_UNDERFLOW)) {
+			if (!tx)
+				reg = TASHA_SLIM_PGD_PORT_INT_EN0 +
+					(port_id / 8);
+			else
+				reg = TASHA_SLIM_PGD_PORT_INT_TX_EN0 +
+					(port_id / 8);
+			int_val = wcd9xxx_interface_reg_read(
+				priv->wcd9xxx, reg);
+			if (int_val & (1 << (port_id % 8))) {
+				int_val = int_val ^ (1 << (port_id % 8));
+				wcd9xxx_interface_reg_write(priv->wcd9xxx,
+					reg, int_val);
+			}
+		}
+		if (val & TASHA_SLIM_IRQ_PORT_CLOSED) {
+			/*
+			 * INT SOURCE register starts from RX to TX
+			 * but port number in the ch_mask is in opposite way
+			 */
+			bit = (tx ? j - 16 : j + 16);
+			pr_debug("%s: %s port %d closed value %x, bit %u\n",
+				 __func__, (tx ? "TX" : "RX"), port_id, val,
+				 bit);
+			for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+				pr_debug("%s: priv->dai[%d].ch_mask = 0x%lx\n",
+					 __func__, k, priv->dai[k].ch_mask);
+				if (test_and_clear_bit(bit,
+						       &priv->dai[k].ch_mask)) {
+					cleared = true;
+					if (!priv->dai[k].ch_mask)
+						wake_up(&priv->dai[k].dai_wait);
+					/*
+					 * There are cases when multiple DAIs
+					 * might be using the same slimbus
+					 * channel. Hence don't break here.
+					 */
+				}
+			}
+			WARN(!cleared,
+			     "Couldn't find slimbus %s port %d for closing\n",
+			     (tx ? "TX" : "RX"), port_id);
+		}
+		wcd9xxx_interface_reg_write(priv->wcd9xxx,
+					    TASHA_SLIM_PGD_PORT_INT_CLR_RX_0 +
+					    (j / 8),
+					    1 << (j % 8));
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tasha_setup_irqs(struct tasha_priv *tasha)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = tasha->codec;
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
+				  tasha_slimbus_irq, "SLIMBUS Slave", tasha);
+	if (ret)
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_SLIMBUS);
+	else
+		tasha_slim_interface_init_reg(codec);
+
+	return ret;
+}
+
+static void tasha_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct afe_param_cdc_slimbus_slave_cfg *cfg;
+	struct wcd9xxx *wcd9xxx = priv->wcd9xxx;
+	uint64_t eaddr = 0;
+
+	cfg = &priv->slimbus_slave_cfg;
+	cfg->minor_version = 1;
+	cfg->tx_slave_port_offset = 0;
+	cfg->rx_slave_port_offset = 16;
+
+	memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+	WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+	cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+	cfg->device_enum_addr_msw = eaddr >> 32;
+
+	dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n",
+		__func__, eaddr);
+}
+
+static void tasha_cleanup_irqs(struct tasha_priv *tasha)
+{
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tasha);
+}
+
+static int tasha_handle_pdata(struct tasha_priv *tasha,
+			      struct wcd9xxx_pdata *pdata)
+{
+	struct snd_soc_codec *codec = tasha->codec;
+	u8 dmic_ctl_val, mad_dmic_ctl_val;
+	u8 anc_ctl_value;
+	u32 def_dmic_rate, dmic_clk_drv;
+	int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(codec->dev, "%s: NULL pdata\n", __func__);
+		return -ENODEV;
+	}
+
+	/* set micbias voltage */
+	vout_ctl_1 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb1_mv);
+	vout_ctl_2 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb2_mv);
+	vout_ctl_3 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb3_mv);
+	vout_ctl_4 = wcd9335_get_micb_vout_ctl_val(pdata->micbias.micb4_mv);
+	if (vout_ctl_1 < 0 || vout_ctl_2 < 0 ||
+	    vout_ctl_3 < 0 || vout_ctl_4 < 0) {
+		rc = -EINVAL;
+		goto done;
+	}
+	snd_soc_update_bits(codec, WCD9335_ANA_MICB1, 0x3F, vout_ctl_1);
+	snd_soc_update_bits(codec, WCD9335_ANA_MICB2, 0x3F, vout_ctl_2);
+	snd_soc_update_bits(codec, WCD9335_ANA_MICB3, 0x3F, vout_ctl_3);
+	snd_soc_update_bits(codec, WCD9335_ANA_MICB4, 0x3F, vout_ctl_4);
+
+	/* Set the DMIC sample rate */
+	switch (pdata->mclk_rate) {
+	case TASHA_MCLK_CLK_9P6MHZ:
+		def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+		break;
+	case TASHA_MCLK_CLK_12P288MHZ:
+		def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+		break;
+	default:
+		/* should never happen */
+		dev_err(codec->dev, "%s: Invalid mclk_rate %d\n",
+			__func__, pdata->mclk_rate);
+		rc = -EINVAL;
+		goto done;
+	};
+
+	if (pdata->dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n",
+			__func__, def_dmic_rate);
+		pdata->dmic_sample_rate = def_dmic_rate;
+	}
+	if (pdata->mad_dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n",
+			__func__, def_dmic_rate);
+		/*
+		 * use dmic_sample_rate as the default for MAD
+		 * if mad dmic sample rate is undefined
+		 */
+		pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
+	}
+	if (pdata->ecpp_dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		dev_info(codec->dev,
+			 "%s: ecpp_dmic_rate invalid default = %d\n",
+			 __func__, def_dmic_rate);
+		/*
+		 * use dmic_sample_rate as the default for ECPP DMIC
+		 * if ecpp dmic sample rate is undefined
+		 */
+		pdata->ecpp_dmic_sample_rate = pdata->dmic_sample_rate;
+	}
+
+	if (pdata->dmic_clk_drv ==
+	    WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) {
+		pdata->dmic_clk_drv = WCD9335_DMIC_CLK_DRIVE_DEFAULT;
+		dev_info(codec->dev,
+			 "%s: dmic_clk_strength invalid, default = %d\n",
+			 __func__, pdata->dmic_clk_drv);
+	}
+
+	switch (pdata->dmic_clk_drv) {
+	case 2:
+		dmic_clk_drv = 0;
+		break;
+	case 4:
+		dmic_clk_drv = 1;
+		break;
+	case 8:
+		dmic_clk_drv = 2;
+		break;
+	case 16:
+		dmic_clk_drv = 3;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: invalid dmic_clk_drv %d, using default\n",
+			__func__, pdata->dmic_clk_drv);
+		dmic_clk_drv = 0;
+		break;
+	}
+
+	snd_soc_update_bits(codec, WCD9335_TEST_DEBUG_PAD_DRVCTL,
+			    0x0C, dmic_clk_drv << 2);
+
+	/*
+	 * Default the DMIC clk rates to mad_dmic_sample_rate,
+	 * whereas, the anc/txfe dmic rates to dmic_sample_rate
+	 * since the anc/txfe are independent of mad block.
+	 */
+	mad_dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec,
+				pdata->mclk_rate,
+				pdata->mad_dmic_sample_rate);
+	snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC0_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+	snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC1_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+	snd_soc_update_bits(codec, WCD9335_CPE_SS_DMIC2_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+
+	dmic_ctl_val = tasha_get_dmic_clk_val(tasha->codec,
+				pdata->mclk_rate,
+				pdata->dmic_sample_rate);
+
+	if (dmic_ctl_val == WCD9335_DMIC_CLK_DIV_2)
+		anc_ctl_value = WCD9335_ANC_DMIC_X2_FULL_RATE;
+	else
+		anc_ctl_value = WCD9335_ANC_DMIC_X2_HALF_RATE;
+
+	snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL,
+			    0x40, anc_ctl_value << 6);
+	snd_soc_update_bits(codec, WCD9335_CDC_ANC0_MODE_2_CTL,
+			    0x20, anc_ctl_value << 5);
+	snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL,
+			    0x40, anc_ctl_value << 6);
+	snd_soc_update_bits(codec, WCD9335_CDC_ANC1_MODE_2_CTL,
+			    0x20, anc_ctl_value << 5);
+done:
+	return rc;
+}
+
+static struct wcd_cpe_core *tasha_codec_get_cpe_core(
+		struct snd_soc_codec *codec)
+{
+	struct tasha_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	return priv->cpe_core;
+}
+
+static int tasha_codec_cpe_fll_update_divider(
+	struct snd_soc_codec *codec, u32 cpe_fll_rate)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	u32 div_val = 0, l_val = 0;
+	u32 computed_cpe_fll;
+
+	if (cpe_fll_rate != CPE_FLL_CLK_75MHZ &&
+	    cpe_fll_rate != CPE_FLL_CLK_150MHZ) {
+		dev_err(codec->dev,
+			"%s: Invalid CPE fll rate request %u\n",
+			__func__, cpe_fll_rate);
+		return -EINVAL;
+	}
+
+	if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_12P288MHZ) {
+		/* update divider to 10 and enable 5x divider */
+		snd_soc_write(codec, WCD9335_CPE_FLL_USER_CTL_1,
+			      0x55);
+		div_val = 10;
+	} else if (wcd9xxx->mclk_rate == TASHA_MCLK_CLK_9P6MHZ) {
+		/* update divider to 8 and enable 2x divider */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
+				    0x7C, 0x70);
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_1,
+				    0xE0, 0x20);
+		div_val = 8;
+	} else {
+		dev_err(codec->dev,
+			"%s: Invalid MCLK rate %u\n",
+			__func__, wcd9xxx->mclk_rate);
+		return -EINVAL;
+	}
+
+	l_val = ((cpe_fll_rate / 1000) * div_val) /
+		 (wcd9xxx->mclk_rate / 1000);
+
+	/* If l_val was integer truncated, increment l_val once */
+	computed_cpe_fll = (wcd9xxx->mclk_rate / div_val) * l_val;
+	if (computed_cpe_fll < cpe_fll_rate)
+		l_val++;
+
+
+	/* update L value LSB and MSB */
+	snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_0,
+		      (l_val & 0xFF));
+	snd_soc_write(codec, WCD9335_CPE_FLL_L_VAL_CTL_1,
+		      ((l_val >> 8) & 0xFF));
+
+	tasha->current_cpe_clk_freq = cpe_fll_rate;
+	dev_dbg(codec->dev,
+		"%s: updated l_val to %u for cpe_clk %u and mclk %u\n",
+		__func__, l_val, cpe_fll_rate, wcd9xxx->mclk_rate);
+
+	return 0;
+}
+
+static int __tasha_cdc_change_cpe_clk(struct snd_soc_codec *codec,
+		u32 clk_freq)
+{
+	int ret = 0;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (!tasha_cdc_is_svs_enabled(tasha)) {
+		dev_dbg(codec->dev,
+			"%s: SVS not enabled or tasha is not 2p0, return\n",
+			__func__);
+		return 0;
+	}
+	dev_dbg(codec->dev, "%s: clk_freq = %u\n", __func__, clk_freq);
+
+	if (clk_freq == CPE_FLL_CLK_75MHZ) {
+		/* Change to SVS */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+				    0x08, 0x08);
+		if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) {
+			ret = -EINVAL;
+			goto done;
+		}
+
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+				    0x10, 0x10);
+
+		clear_bit(CPE_NOMINAL, &tasha->status_mask);
+		tasha_codec_update_sido_voltage(tasha, sido_buck_svs_voltage);
+
+	} else if (clk_freq == CPE_FLL_CLK_150MHZ) {
+		/* change to nominal */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+				    0x08, 0x08);
+
+		set_bit(CPE_NOMINAL, &tasha->status_mask);
+		tasha_codec_update_sido_voltage(tasha, SIDO_VOLTAGE_NOMINAL_MV);
+
+		if (tasha_codec_cpe_fll_update_divider(codec, clk_freq)) {
+			ret = -EINVAL;
+			goto done;
+		}
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+				    0x10, 0x10);
+	} else {
+		dev_err(codec->dev,
+			"%s: Invalid clk_freq request %d for CPE FLL\n",
+			__func__, clk_freq);
+		ret = -EINVAL;
+	}
+
+done:
+	snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+			    0x10, 0x00);
+	snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+			    0x08, 0x00);
+	return ret;
+}
+
+
+static int tasha_codec_cpe_fll_enable(struct snd_soc_codec *codec,
+				   bool enable)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u8 clk_sel_reg_val = 0x00;
+
+	dev_dbg(codec->dev, "%s: enable = %s\n",
+			__func__, enable ? "true" : "false");
+
+	if (enable) {
+		if (tasha_cdc_is_svs_enabled(tasha)) {
+			/* FLL enable is always at SVS */
+			if (__tasha_cdc_change_cpe_clk(codec,
+					CPE_FLL_CLK_75MHZ)) {
+				dev_err(codec->dev,
+					"%s: clk change to %d failed\n",
+					__func__, CPE_FLL_CLK_75MHZ);
+				return -EINVAL;
+			}
+		} else {
+			if (tasha_codec_cpe_fll_update_divider(codec,
+							CPE_FLL_CLK_75MHZ)) {
+				dev_err(codec->dev,
+					"%s: clk change to %d failed\n",
+					__func__, CPE_FLL_CLK_75MHZ);
+				return -EINVAL;
+			}
+		}
+
+		if (TASHA_IS_1_0(wcd9xxx)) {
+			tasha_cdc_mclk_enable(codec, true, false);
+			clk_sel_reg_val = 0x02;
+		}
+
+		/* Setup CPE reference clk */
+		snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP,
+				    0x02, clk_sel_reg_val);
+
+		/* enable CPE FLL reference clk */
+		snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP,
+				    0x01, 0x01);
+
+		/* program the PLL */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
+				    0x01, 0x01);
+
+		/* TEST clk setting */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0,
+				    0x80, 0x80);
+		/* set FLL mode to HW controlled */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+				    0x60, 0x00);
+		snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x80);
+	} else {
+		/* disable CPE FLL reference clk */
+		snd_soc_update_bits(codec, WCD9335_ANA_CLK_TOP,
+				    0x01, 0x00);
+		/* undo TEST clk setting */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_TEST_CTL_0,
+				    0x80, 0x00);
+		/* undo FLL mode to HW control */
+		snd_soc_write(codec, WCD9335_CPE_FLL_FLL_MODE, 0x00);
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_FLL_MODE,
+				    0x60, 0x20);
+		/* undo the PLL */
+		snd_soc_update_bits(codec, WCD9335_CPE_FLL_USER_CTL_0,
+				    0x01, 0x00);
+
+		if (TASHA_IS_1_0(wcd9xxx))
+			tasha_cdc_mclk_enable(codec, false, false);
+
+		/*
+		 * FLL could get disabled while at nominal,
+		 * scale it back to SVS
+		 */
+		if (tasha_cdc_is_svs_enabled(tasha))
+			__tasha_cdc_change_cpe_clk(codec,
+						CPE_FLL_CLK_75MHZ);
+	}
+
+	return 0;
+
+}
+
+static void tasha_cdc_query_cpe_clk_plan(void *data,
+		struct cpe_svc_cfg_clk_plan *clk_freq)
+{
+	struct snd_soc_codec *codec = data;
+	struct tasha_priv *tasha;
+	u32 cpe_clk_khz;
+
+	if (!codec) {
+		pr_err("%s: Invalid codec handle\n",
+			__func__);
+		return;
+	}
+
+	tasha = snd_soc_codec_get_drvdata(codec);
+	cpe_clk_khz = tasha->current_cpe_clk_freq / 1000;
+
+	dev_dbg(codec->dev,
+		"%s: current_clk_freq = %u\n",
+		__func__, tasha->current_cpe_clk_freq);
+
+	clk_freq->current_clk_feq = cpe_clk_khz;
+	clk_freq->num_clk_freqs = 2;
+
+	if (tasha_cdc_is_svs_enabled(tasha)) {
+		clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ / 1000;
+		clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ / 1000;
+	} else {
+		clk_freq->clk_freqs[0] = CPE_FLL_CLK_75MHZ;
+		clk_freq->clk_freqs[1] = CPE_FLL_CLK_150MHZ;
+	}
+}
+
+static void tasha_cdc_change_cpe_clk(void *data,
+		u32 clk_freq)
+{
+	struct snd_soc_codec *codec = data;
+	struct tasha_priv *tasha;
+	u32 cpe_clk_khz, req_freq = 0;
+
+	if (!codec) {
+		pr_err("%s: Invalid codec handle\n",
+			__func__);
+		return;
+	}
+
+	tasha = snd_soc_codec_get_drvdata(codec);
+	cpe_clk_khz = tasha->current_cpe_clk_freq / 1000;
+
+	if (tasha_cdc_is_svs_enabled(tasha)) {
+		if ((clk_freq * 1000) <= CPE_FLL_CLK_75MHZ)
+			req_freq = CPE_FLL_CLK_75MHZ;
+		else
+			req_freq = CPE_FLL_CLK_150MHZ;
+	}
+
+	dev_dbg(codec->dev,
+		"%s: requested clk_freq = %u, current clk_freq = %u\n",
+		__func__, clk_freq * 1000,
+		tasha->current_cpe_clk_freq);
+
+	if (tasha_cdc_is_svs_enabled(tasha)) {
+		if (__tasha_cdc_change_cpe_clk(codec, req_freq))
+			dev_err(codec->dev,
+				"%s: clock/voltage scaling failed\n",
+				__func__);
+	}
+}
+
+static int tasha_codec_slim_reserve_bw(struct snd_soc_codec *codec,
+		u32 bw_ops, bool commit)
+{
+	struct wcd9xxx *wcd9xxx;
+
+	if (!codec) {
+		pr_err("%s: Invalid handle to codec\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!wcd9xxx) {
+		dev_err(codec->dev, "%s: Invalid parent drv_data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return wcd9xxx_slim_reserve_bw(wcd9xxx, bw_ops, commit);
+}
+
+static int tasha_codec_vote_max_bw(struct snd_soc_codec *codec,
+			bool vote)
+{
+	u32 bw_ops;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C)
+		return 0;
+
+	if (vote)
+		bw_ops = SLIM_BW_CLK_GEAR_9;
+	else
+		bw_ops = SLIM_BW_UNVOTE;
+
+	return tasha_codec_slim_reserve_bw(codec,
+			bw_ops, true);
+}
+
+static int tasha_cpe_err_irq_control(struct snd_soc_codec *codec,
+	enum cpe_err_irq_cntl_type cntl_type, u8 *status)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	u8 irq_bits;
+
+	if (TASHA_IS_2_0(tasha->wcd9xxx))
+		irq_bits = 0xFF;
+	else
+		irq_bits = 0x3F;
+
+	if (status)
+		irq_bits = (*status) & irq_bits;
+
+	switch (cntl_type) {
+	case CPE_ERR_IRQ_MASK:
+		snd_soc_update_bits(codec,
+				    WCD9335_CPE_SS_SS_ERROR_INT_MASK,
+				    irq_bits, irq_bits);
+		break;
+	case CPE_ERR_IRQ_UNMASK:
+		snd_soc_update_bits(codec,
+				    WCD9335_CPE_SS_SS_ERROR_INT_MASK,
+				    irq_bits, 0x00);
+		break;
+	case CPE_ERR_IRQ_CLEAR:
+		snd_soc_write(codec, WCD9335_CPE_SS_SS_ERROR_INT_CLEAR,
+			      irq_bits);
+		break;
+	case CPE_ERR_IRQ_STATUS:
+		if (!status)
+			return -EINVAL;
+		*status = snd_soc_read(codec,
+				       WCD9335_CPE_SS_SS_ERROR_INT_STATUS);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct wcd_cpe_cdc_cb cpe_cb = {
+	.cdc_clk_en = tasha_codec_internal_rco_ctrl,
+	.cpe_clk_en = tasha_codec_cpe_fll_enable,
+	.get_afe_out_port_id = tasha_codec_get_mad_port_id,
+	.lab_cdc_ch_ctl = tasha_codec_enable_slimtx_mad,
+	.cdc_ext_clk = tasha_cdc_mclk_enable,
+	.bus_vote_bw = tasha_codec_vote_max_bw,
+	.cpe_err_irq_control = tasha_cpe_err_irq_control,
+};
+
+static struct cpe_svc_init_param cpe_svc_params = {
+	.version = CPE_SVC_INIT_PARAM_V1,
+	.query_freq_plans_cb = tasha_cdc_query_cpe_clk_plan,
+	.change_freq_plan_cb = tasha_cdc_change_cpe_clk,
+};
+
+static int tasha_cpe_initialize(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd_cpe_params cpe_params;
+
+	memset(&cpe_params, 0,
+	       sizeof(struct wcd_cpe_params));
+	cpe_params.codec = codec;
+	cpe_params.get_cpe_core = tasha_codec_get_cpe_core;
+	cpe_params.cdc_cb = &cpe_cb;
+	cpe_params.dbg_mode = cpe_debug_mode;
+	cpe_params.cdc_major_ver = CPE_SVC_CODEC_WCD9335;
+	cpe_params.cdc_minor_ver = CPE_SVC_CODEC_V1P0;
+	cpe_params.cdc_id = CPE_SVC_CODEC_WCD9335;
+
+	cpe_params.cdc_irq_info.cpe_engine_irq =
+			WCD9335_IRQ_SVA_OUTBOX1;
+	cpe_params.cdc_irq_info.cpe_err_irq =
+			WCD9335_IRQ_SVA_ERROR;
+	cpe_params.cdc_irq_info.cpe_fatal_irqs =
+			TASHA_CPE_FATAL_IRQS;
+
+	cpe_svc_params.context = codec;
+	cpe_params.cpe_svc_params = &cpe_svc_params;
+
+	tasha->cpe_core = wcd_cpe_init("cpe_9335", codec,
+					&cpe_params);
+	if (IS_ERR_OR_NULL(tasha->cpe_core)) {
+		dev_err(codec->dev,
+			"%s: Failed to enable CPE\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct wcd_resmgr_cb tasha_resmgr_cb = {
+	.cdc_rco_ctrl = __tasha_codec_internal_rco_ctrl,
+};
+
+static int tasha_device_down(struct wcd9xxx *wcd9xxx)
+{
+	struct snd_soc_codec *codec;
+	struct tasha_priv *priv;
+	int count;
+	int i = 0;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	priv = snd_soc_codec_get_drvdata(codec);
+	wcd_cpe_ssr_event(priv->cpe_core, WCD_CPE_BUS_DOWN_EVENT);
+	for (i = 0; i < priv->nr; i++)
+		swrm_wcd_notify(priv->swr_ctrl_data[i].swr_pdev,
+				SWR_DEVICE_DOWN, NULL);
+	snd_soc_card_change_online_state(codec->component.card, 0);
+	for (count = 0; count < NUM_CODEC_DAIS; count++)
+		priv->dai[count].bus_down_in_recovery = true;
+
+	priv->resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
+
+	return 0;
+}
+
+static int tasha_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+	int i, ret = 0;
+	struct wcd9xxx *control;
+	struct snd_soc_codec *codec;
+	struct tasha_priv *tasha;
+	struct wcd9xxx_pdata *pdata;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	tasha = snd_soc_codec_get_drvdata(codec);
+	control = dev_get_drvdata(codec->dev->parent);
+
+	wcd9xxx_set_power_state(tasha->wcd9xxx,
+				WCD_REGION_POWER_COLLAPSE_REMOVE,
+				WCD9XXX_DIG_CORE_REGION_1);
+
+	mutex_lock(&tasha->codec_mutex);
+
+	tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+		control->slim_slave->laddr;
+	tasha_slimbus_slave_port_cfg.slave_dev_pgd_la =
+		control->slim->laddr;
+	tasha_init_slim_slave_cfg(codec);
+	if (tasha->machine_codec_event_cb)
+		tasha->machine_codec_event_cb(codec,
+				WCD9335_CODEC_EVENT_CODEC_UP);
+	snd_soc_card_change_online_state(codec->component.card, 1);
+
+	/* Class-H Init*/
+	wcd_clsh_init(&tasha->clsh_d);
+	/* Default HPH Mode to Class-H HiFi */
+	tasha->hph_mode = CLS_H_HIFI;
+
+	for (i = 0; i < TASHA_MAX_MICBIAS; i++)
+		tasha->micb_ref[i] = 0;
+
+	tasha_update_reg_defaults(tasha);
+
+	tasha->codec = codec;
+	for (i = 0; i < COMPANDER_MAX; i++)
+		tasha->comp_enabled[i] = 0;
+
+	dev_dbg(codec->dev, "%s: MCLK Rate = %x\n",
+		__func__, control->mclk_rate);
+
+	if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ)
+		snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+				    0x03, 0x00);
+	else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ)
+		snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+				    0x03, 0x01);
+	tasha_codec_init_reg(codec);
+
+	wcd_resmgr_post_ssr_v2(tasha->resmgr);
+
+	tasha_enable_efuse_sensing(codec);
+
+	regcache_mark_dirty(codec->component.regmap);
+	regcache_sync(codec->component.regmap);
+
+	pdata = dev_get_platdata(codec->dev->parent);
+	ret = tasha_handle_pdata(tasha, pdata);
+	if (ret < 0)
+		dev_err(codec->dev, "%s: invalid pdata\n", __func__);
+
+	/* MBHC Init */
+	wcd_mbhc_deinit(&tasha->mbhc);
+	tasha->mbhc_started = false;
+
+	/* Initialize MBHC module */
+	ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids,
+		      wcd_mbhc_registers, TASHA_ZDET_SUPPORTED);
+	if (ret)
+		dev_err(codec->dev, "%s: mbhc initialization failed\n",
+			__func__);
+	else
+		tasha_mbhc_hs_detect(codec, tasha->mbhc.mbhc_cfg);
+
+	tasha_cleanup_irqs(tasha);
+	ret = tasha_setup_irqs(tasha);
+	if (ret) {
+		dev_err(codec->dev, "%s: tasha irq setup failed %d\n",
+			__func__, ret);
+		goto err;
+	}
+
+	tasha_set_spkr_mode(codec, tasha->spkr_mode);
+	wcd_cpe_ssr_event(tasha->cpe_core, WCD_CPE_BUS_UP_EVENT);
+
+err:
+	mutex_unlock(&tasha->codec_mutex);
+	return ret;
+}
+
+static struct regulator *tasha_codec_find_ondemand_regulator(
+		struct snd_soc_codec *codec, const char *name)
+{
+	int i;
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = tasha->wcd9xxx;
+	struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+
+	for (i = 0; i < wcd9xxx->num_of_supplies; ++i) {
+		if (pdata->regulator[i].ondemand &&
+			wcd9xxx->supplies[i].supply &&
+			!strcmp(wcd9xxx->supplies[i].supply, name))
+			return wcd9xxx->supplies[i].consumer;
+	}
+
+	dev_dbg(tasha->dev, "Warning: regulator not found:%s\n",
+		name);
+	return NULL;
+}
+
+static int tasha_codec_probe(struct snd_soc_codec *codec)
+{
+	struct wcd9xxx *control;
+	struct tasha_priv *tasha;
+	struct wcd9xxx_pdata *pdata;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	int i, ret;
+	void *ptr = NULL;
+	struct regulator *supply;
+
+	control = dev_get_drvdata(codec->dev->parent);
+
+	dev_info(codec->dev, "%s()\n", __func__);
+	tasha = snd_soc_codec_get_drvdata(codec);
+	tasha->intf_type = wcd9xxx_get_intf_type();
+
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		control->dev_down = tasha_device_down;
+		control->post_reset = tasha_post_reset_cb;
+		control->ssr_priv = (void *)codec;
+	}
+
+	/* Resource Manager post Init */
+	ret = wcd_resmgr_post_init(tasha->resmgr, &tasha_resmgr_cb, codec);
+	if (ret) {
+		dev_err(codec->dev, "%s: wcd resmgr post init failed\n",
+			__func__);
+		goto err;
+	}
+	/* Class-H Init*/
+	wcd_clsh_init(&tasha->clsh_d);
+	/* Default HPH Mode to Class-H HiFi */
+	tasha->hph_mode = CLS_H_HIFI;
+
+	tasha->codec = codec;
+	for (i = 0; i < COMPANDER_MAX; i++)
+		tasha->comp_enabled[i] = 0;
+
+	tasha->spkr_gain_offset = RX_GAIN_OFFSET_0_DB;
+	tasha->intf_type = wcd9xxx_get_intf_type();
+	tasha_update_reg_reset_values(codec);
+	pr_debug("%s: MCLK Rate = %x\n", __func__, control->mclk_rate);
+	if (control->mclk_rate == TASHA_MCLK_CLK_12P288MHZ)
+		snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+				    0x03, 0x00);
+	else if (control->mclk_rate == TASHA_MCLK_CLK_9P6MHZ)
+		snd_soc_update_bits(codec, WCD9335_CODEC_RPM_CLK_MCLK_CFG,
+				    0x03, 0x01);
+	tasha_codec_init_reg(codec);
+
+	tasha_enable_efuse_sensing(codec);
+
+	pdata = dev_get_platdata(codec->dev->parent);
+	ret = tasha_handle_pdata(tasha, pdata);
+	if (ret < 0) {
+		pr_err("%s: bad pdata\n", __func__);
+		goto err;
+	}
+
+	supply = tasha_codec_find_ondemand_regulator(codec,
+		on_demand_supply_name[ON_DEMAND_MICBIAS]);
+	if (supply) {
+		tasha->on_demand_list[ON_DEMAND_MICBIAS].supply = supply;
+		tasha->on_demand_list[ON_DEMAND_MICBIAS].ondemand_supply_count =
+				0;
+	}
+
+	tasha->fw_data = devm_kzalloc(codec->dev,
+				      sizeof(*(tasha->fw_data)), GFP_KERNEL);
+	if (!tasha->fw_data)
+		goto err;
+	set_bit(WCD9XXX_ANC_CAL, tasha->fw_data->cal_bit);
+	set_bit(WCD9XXX_MBHC_CAL, tasha->fw_data->cal_bit);
+	set_bit(WCD9XXX_MAD_CAL, tasha->fw_data->cal_bit);
+	set_bit(WCD9XXX_VBAT_CAL, tasha->fw_data->cal_bit);
+
+	ret = wcd_cal_create_hwdep(tasha->fw_data,
+				   WCD9XXX_CODEC_HWDEP_NODE, codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+		goto err_hwdep;
+	}
+
+	/* Initialize MBHC module */
+	if (TASHA_IS_2_0(tasha->wcd9xxx)) {
+		wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].reg =
+			WCD9335_MBHC_FSM_STATUS;
+		wcd_mbhc_registers[WCD_MBHC_FSM_STATUS].mask = 0x01;
+	}
+	ret = wcd_mbhc_init(&tasha->mbhc, codec, &mbhc_cb, &intr_ids,
+		      wcd_mbhc_registers, TASHA_ZDET_SUPPORTED);
+	if (ret) {
+		pr_err("%s: mbhc initialization failed\n", __func__);
+		goto err_hwdep;
+	}
+
+	ptr = devm_kzalloc(codec->dev, (sizeof(tasha_rx_chs) +
+			   sizeof(tasha_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto err_hwdep;
+	}
+
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_I2C) {
+		snd_soc_dapm_new_controls(dapm, tasha_dapm_i2s_widgets,
+			ARRAY_SIZE(tasha_dapm_i2s_widgets));
+		snd_soc_dapm_add_routes(dapm, audio_i2s_map,
+			ARRAY_SIZE(audio_i2s_map));
+		for (i = 0; i < ARRAY_SIZE(tasha_i2s_dai); i++) {
+			INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&tasha->dai[i].dai_wait);
+		}
+	} else if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		for (i = 0; i < NUM_CODEC_DAIS; i++) {
+			INIT_LIST_HEAD(&tasha->dai[i].wcd9xxx_ch_list);
+			init_waitqueue_head(&tasha->dai[i].dai_wait);
+		}
+		tasha_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+					control->slim_slave->laddr;
+		tasha_slimbus_slave_port_cfg.slave_dev_pgd_la =
+					control->slim->laddr;
+		tasha_slimbus_slave_port_cfg.slave_port_mapping[0] =
+					TASHA_TX13;
+		tasha_init_slim_slave_cfg(codec);
+	}
+
+	snd_soc_add_codec_controls(codec, impedance_detect_controls,
+				   ARRAY_SIZE(impedance_detect_controls));
+	snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+				   ARRAY_SIZE(hph_type_detect_controls));
+
+	snd_soc_add_codec_controls(codec,
+			tasha_analog_gain_controls,
+			ARRAY_SIZE(tasha_analog_gain_controls));
+	control->num_rx_port = TASHA_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, tasha_rx_chs, sizeof(tasha_rx_chs));
+	control->num_tx_port = TASHA_TX_MAX;
+	control->tx_chs = ptr + sizeof(tasha_rx_chs);
+	memcpy(control->tx_chs, tasha_tx_chs, sizeof(tasha_tx_chs));
+
+	snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
+
+	if (tasha->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS) {
+		snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
+		snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+		snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+		snd_soc_dapm_ignore_suspend(dapm, "AIF Mix Playback");
+		snd_soc_dapm_ignore_suspend(dapm, "AIF4 MAD TX");
+		snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
+		snd_soc_dapm_ignore_suspend(dapm, "AIF5 CPE TX");
+	}
+
+	snd_soc_dapm_sync(dapm);
+
+	ret = tasha_setup_irqs(tasha);
+	if (ret) {
+		pr_err("%s: tasha irq setup failed %d\n", __func__, ret);
+		goto err_pdata;
+	}
+
+	ret = tasha_cpe_initialize(codec);
+	if (ret) {
+		dev_err(codec->dev,
+			"%s: cpe initialization failed, err = %d\n",
+			__func__, ret);
+		/* Do not fail probe if CPE failed */
+		ret = 0;
+	}
+
+	for (i = 0; i < TASHA_NUM_DECIMATORS; i++) {
+		tasha->tx_hpf_work[i].tasha = tasha;
+		tasha->tx_hpf_work[i].decimator = i;
+		INIT_DELAYED_WORK(&tasha->tx_hpf_work[i].dwork,
+			tasha_tx_hpf_corner_freq_callback);
+	}
+
+	for (i = 0; i < TASHA_NUM_DECIMATORS; i++) {
+		tasha->tx_mute_dwork[i].tasha = tasha;
+		tasha->tx_mute_dwork[i].decimator = i;
+		INIT_DELAYED_WORK(&tasha->tx_mute_dwork[i].dwork,
+			  tasha_tx_mute_update_callback);
+	}
+
+	tasha->spk_anc_dwork.tasha = tasha;
+	INIT_DELAYED_WORK(&tasha->spk_anc_dwork.dwork,
+			  tasha_spk_anc_update_callback);
+
+	mutex_lock(&tasha->codec_mutex);
+	snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1");
+	snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2");
+	snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT1 PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC LINEOUT2 PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC HPHL");
+	snd_soc_dapm_disable_pin(dapm, "ANC HPHR");
+	snd_soc_dapm_disable_pin(dapm, "ANC HPHL PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC HPHR PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+	snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+	mutex_unlock(&tasha->codec_mutex);
+	snd_soc_dapm_sync(dapm);
+
+	return ret;
+
+err_pdata:
+	devm_kfree(codec->dev, ptr);
+	control->rx_chs = NULL;
+	control->tx_chs = NULL;
+err_hwdep:
+	devm_kfree(codec->dev, tasha->fw_data);
+	tasha->fw_data = NULL;
+err:
+	return ret;
+}
+
+static int tasha_codec_remove(struct snd_soc_codec *codec)
+{
+	struct tasha_priv *tasha = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *control;
+
+	control = dev_get_drvdata(codec->dev->parent);
+	control->rx_chs = NULL;
+	control->tx_chs = NULL;
+
+	tasha_cleanup_irqs(tasha);
+	/* Cleanup MBHC */
+	/* Cleanup resmgr */
+
+	return 0;
+}
+
+static struct regmap *tasha_get_regmap(struct device *dev)
+{
+	struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+	return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tasha = {
+	.probe = tasha_codec_probe,
+	.remove = tasha_codec_remove,
+	.get_regmap = tasha_get_regmap,
+	.component_driver = {
+		.controls = tasha_snd_controls,
+		.num_controls = ARRAY_SIZE(tasha_snd_controls),
+		.dapm_widgets = tasha_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(tasha_dapm_widgets),
+		.dapm_routes = audio_map,
+		.num_dapm_routes = ARRAY_SIZE(audio_map),
+	},
+};
+
+#ifdef CONFIG_PM
+static int tasha_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tasha_priv *tasha = platform_get_drvdata(pdev);
+
+	dev_dbg(dev, "%s: system suspend\n", __func__);
+	if (cancel_delayed_work_sync(&tasha->power_gate_work))
+		tasha_codec_power_gate_digital_core(tasha);
+
+	return 0;
+}
+
+static int tasha_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tasha_priv *tasha = platform_get_drvdata(pdev);
+
+	if (!tasha) {
+		dev_err(dev, "%s: tasha private data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(dev, "%s: system resume\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops tasha_pm_ops = {
+	.suspend = tasha_suspend,
+	.resume = tasha_resume,
+};
+#endif
+
+static int tasha_swrm_read(void *handle, int reg)
+{
+	struct tasha_priv *tasha;
+	struct wcd9xxx *wcd9xxx;
+	unsigned short swr_rd_addr_base;
+	unsigned short swr_rd_data_base;
+	int val, ret;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	tasha = (struct tasha_priv *)handle;
+	wcd9xxx = tasha->wcd9xxx;
+
+	dev_dbg(tasha->dev, "%s: Reading soundwire register, 0x%x\n",
+		__func__, reg);
+	swr_rd_addr_base = WCD9335_SWR_AHB_BRIDGE_RD_ADDR_0;
+	swr_rd_data_base = WCD9335_SWR_AHB_BRIDGE_RD_DATA_0;
+	/* read_lock */
+	mutex_lock(&tasha->swr_read_lock);
+	ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base,
+				(u8 *)&reg, 4);
+	if (ret < 0) {
+		pr_err("%s: RD Addr Failure\n", __func__);
+		goto err;
+	}
+	/* Check for RD status */
+	ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base,
+			       (u8 *)&val, 4);
+	if (ret < 0) {
+		pr_err("%s: RD Data Failure\n", __func__);
+		goto err;
+	}
+	ret = val;
+err:
+	/* read_unlock */
+	mutex_unlock(&tasha->swr_read_lock);
+	return ret;
+}
+
+static int tasha_swrm_i2s_bulk_write(struct wcd9xxx *wcd9xxx,
+				struct wcd9xxx_reg_val *bulk_reg,
+				size_t len)
+{
+	int i, ret = 0;
+	unsigned short swr_wr_addr_base;
+	unsigned short swr_wr_data_base;
+
+	swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0;
+	swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0;
+
+	for (i = 0; i < (len * 2); i += 2) {
+		/* First Write the Data to register */
+		ret = regmap_bulk_write(wcd9xxx->regmap,
+			swr_wr_data_base, bulk_reg[i].buf, 4);
+		if (ret < 0) {
+			dev_err(wcd9xxx->dev, "%s: WR Data Failure\n",
+				__func__);
+			break;
+		}
+		/* Next Write Address */
+		ret = regmap_bulk_write(wcd9xxx->regmap,
+			swr_wr_addr_base, bulk_reg[i+1].buf, 4);
+		if (ret < 0) {
+			dev_err(wcd9xxx->dev, "%s: WR Addr Failure\n",
+				__func__);
+			break;
+		}
+	}
+	return ret;
+}
+
+static int tasha_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len)
+{
+	struct tasha_priv *tasha;
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_reg_val *bulk_reg;
+	unsigned short swr_wr_addr_base;
+	unsigned short swr_wr_data_base;
+	int i, j, ret;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	if (len <= 0) {
+		pr_err("%s: Invalid size: %zu\n", __func__, len);
+		return -EINVAL;
+	}
+	tasha = (struct tasha_priv *)handle;
+	wcd9xxx = tasha->wcd9xxx;
+
+	swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0;
+	swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0;
+
+	bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)),
+			   GFP_KERNEL);
+	if (!bulk_reg)
+		return -ENOMEM;
+
+	for (i = 0, j = 0; i < (len * 2); i += 2, j++) {
+		bulk_reg[i].reg = swr_wr_data_base;
+		bulk_reg[i].buf = (u8 *)(&val[j]);
+		bulk_reg[i].bytes = 4;
+		bulk_reg[i+1].reg = swr_wr_addr_base;
+		bulk_reg[i+1].buf = (u8 *)(&reg[j]);
+		bulk_reg[i+1].bytes = 4;
+	}
+	mutex_lock(&tasha->swr_write_lock);
+
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) {
+		ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, len);
+		if (ret) {
+			dev_err(tasha->dev, "%s: i2s bulk write failed, ret: %d\n",
+				__func__, ret);
+		}
+	} else {
+		ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg,
+				 (len * 2), false);
+		if (ret) {
+			dev_err(tasha->dev, "%s: swrm bulk write failed, ret: %d\n",
+				__func__, ret);
+		}
+	}
+
+	mutex_unlock(&tasha->swr_write_lock);
+	kfree(bulk_reg);
+
+	return ret;
+}
+
+static int tasha_swrm_write(void *handle, int reg, int val)
+{
+	struct tasha_priv *tasha;
+	struct wcd9xxx *wcd9xxx;
+	unsigned short swr_wr_addr_base;
+	unsigned short swr_wr_data_base;
+	struct wcd9xxx_reg_val bulk_reg[2];
+	int ret;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	tasha = (struct tasha_priv *)handle;
+	wcd9xxx = tasha->wcd9xxx;
+
+	swr_wr_addr_base = WCD9335_SWR_AHB_BRIDGE_WR_ADDR_0;
+	swr_wr_data_base = WCD9335_SWR_AHB_BRIDGE_WR_DATA_0;
+
+	/* First Write the Data to register */
+	bulk_reg[0].reg = swr_wr_data_base;
+	bulk_reg[0].buf = (u8 *)(&val);
+	bulk_reg[0].bytes = 4;
+	bulk_reg[1].reg = swr_wr_addr_base;
+	bulk_reg[1].buf = (u8 *)(&reg);
+	bulk_reg[1].bytes = 4;
+
+	mutex_lock(&tasha->swr_write_lock);
+
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) {
+		ret = tasha_swrm_i2s_bulk_write(wcd9xxx, bulk_reg, 1);
+		if (ret) {
+			dev_err(tasha->dev, "%s: i2s swrm write failed, ret: %d\n",
+				__func__, ret);
+		}
+	} else {
+		ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false);
+		if (ret < 0)
+			pr_err("%s: WR Data Failure\n", __func__);
+	}
+
+	mutex_unlock(&tasha->swr_write_lock);
+	return ret;
+}
+
+static int tasha_swrm_clock(void *handle, bool enable)
+{
+	struct tasha_priv *tasha = (struct tasha_priv *) handle;
+
+	mutex_lock(&tasha->swr_clk_lock);
+
+	dev_dbg(tasha->dev, "%s: swrm clock %s\n",
+		__func__, (enable?"enable" : "disable"));
+	if (enable) {
+		tasha->swr_clk_users++;
+		if (tasha->swr_clk_users == 1) {
+			if (TASHA_IS_2_0(tasha->wcd9xxx))
+				regmap_update_bits(
+					tasha->wcd9xxx->regmap,
+					WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,
+					0x10, 0x00);
+			__tasha_cdc_mclk_enable(tasha, true);
+			regmap_update_bits(tasha->wcd9xxx->regmap,
+				WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL,
+				0x01, 0x01);
+		}
+	} else {
+		tasha->swr_clk_users--;
+		if (tasha->swr_clk_users == 0) {
+			regmap_update_bits(tasha->wcd9xxx->regmap,
+				WCD9335_CDC_CLK_RST_CTRL_SWR_CONTROL,
+				0x01, 0x00);
+			__tasha_cdc_mclk_enable(tasha, false);
+			if (TASHA_IS_2_0(tasha->wcd9xxx))
+				regmap_update_bits(
+					tasha->wcd9xxx->regmap,
+					WCD9335_TEST_DEBUG_NPL_DLY_TEST_1,
+					0x10, 0x10);
+		}
+	}
+	dev_dbg(tasha->dev, "%s: swrm clock users %d\n",
+		__func__, tasha->swr_clk_users);
+	mutex_unlock(&tasha->swr_clk_lock);
+	return 0;
+}
+
+static int tasha_swrm_handle_irq(void *handle,
+				   irqreturn_t (*swrm_irq_handler)(int irq,
+								   void *data),
+				    void *swrm_handle,
+				    int action)
+{
+	struct tasha_priv *tasha;
+	int ret = 0;
+	struct wcd9xxx *wcd9xxx;
+
+	if (!handle) {
+		pr_err("%s: null handle received\n", __func__);
+		return -EINVAL;
+	}
+	tasha = (struct tasha_priv *) handle;
+	wcd9xxx = tasha->wcd9xxx;
+
+	if (action) {
+		ret = wcd9xxx_request_irq(&wcd9xxx->core_res,
+					  WCD9335_IRQ_SOUNDWIRE,
+					  swrm_irq_handler,
+					  "Tasha SWR Master", swrm_handle);
+		if (ret)
+			dev_err(tasha->dev, "%s: Failed to request irq %d\n",
+				__func__, WCD9335_IRQ_SOUNDWIRE);
+	} else
+		wcd9xxx_free_irq(&wcd9xxx->core_res, WCD9335_IRQ_SOUNDWIRE,
+				 swrm_handle);
+
+	return ret;
+}
+
+static void tasha_add_child_devices(struct work_struct *work)
+{
+	struct tasha_priv *tasha;
+	struct platform_device *pdev;
+	struct device_node *node;
+	struct wcd9xxx *wcd9xxx;
+	struct tasha_swr_ctrl_data *swr_ctrl_data = NULL, *temp;
+	int ret, ctrl_num = 0;
+	struct wcd_swr_ctrl_platform_data *platdata;
+	char plat_dev_name[WCD9335_STRING_LEN];
+
+	tasha = container_of(work, struct tasha_priv,
+			     tasha_add_child_devices_work);
+	if (!tasha) {
+		pr_err("%s: Memory for WCD9335 does not exist\n",
+			__func__);
+		return;
+	}
+	wcd9xxx = tasha->wcd9xxx;
+	if (!wcd9xxx) {
+		pr_err("%s: Memory for WCD9XXX does not exist\n",
+			__func__);
+		return;
+	}
+	if (!wcd9xxx->dev->of_node) {
+		pr_err("%s: DT node for wcd9xxx does not exist\n",
+			__func__);
+		return;
+	}
+
+	platdata = &tasha->swr_plat_data;
+
+	for_each_child_of_node(wcd9xxx->dev->of_node, node) {
+		if (!strcmp(node->name, "swr_master"))
+			strlcpy(plat_dev_name, "tasha_swr_ctrl",
+				(WCD9335_STRING_LEN - 1));
+		else if (strnstr(node->name, "msm_cdc_pinctrl",
+				 strlen("msm_cdc_pinctrl")) != NULL)
+			strlcpy(plat_dev_name, node->name,
+				(WCD9335_STRING_LEN - 1));
+		else
+			continue;
+
+		pdev = platform_device_alloc(plat_dev_name, -1);
+		if (!pdev) {
+			dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n",
+				__func__);
+			ret = -ENOMEM;
+			goto err;
+		}
+		pdev->dev.parent = tasha->dev;
+		pdev->dev.of_node = node;
+
+		if (!strcmp(node->name, "swr_master")) {
+			ret = platform_device_add_data(pdev, platdata,
+						       sizeof(*platdata));
+			if (ret) {
+				dev_err(&pdev->dev,
+					"%s: cannot add plat data ctrl:%d\n",
+					__func__, ctrl_num);
+				goto fail_pdev_add;
+			}
+		}
+
+		ret = platform_device_add(pdev);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: Cannot add platform device\n",
+				__func__);
+			goto fail_pdev_add;
+		}
+
+		if (!strcmp(node->name, "swr_master")) {
+			temp = krealloc(swr_ctrl_data,
+					(ctrl_num + 1) * sizeof(
+					struct tasha_swr_ctrl_data),
+					GFP_KERNEL);
+			if (!temp) {
+				dev_err(wcd9xxx->dev, "out of memory\n");
+				ret = -ENOMEM;
+				goto err;
+			}
+			swr_ctrl_data = temp;
+			swr_ctrl_data[ctrl_num].swr_pdev = pdev;
+			ctrl_num++;
+			dev_dbg(&pdev->dev,
+				"%s: Added soundwire ctrl device(s)\n",
+				__func__);
+			tasha->nr = ctrl_num;
+			tasha->swr_ctrl_data = swr_ctrl_data;
+		}
+	}
+
+	return;
+fail_pdev_add:
+	platform_device_put(pdev);
+err:
+	return;
+}
+
+/*
+ * tasha_codec_ver: to get tasha codec version
+ * @codec: handle to snd_soc_codec *
+ * return enum codec_variant - version
+ */
+enum codec_variant tasha_codec_ver(void)
+{
+	return codec_ver;
+}
+EXPORT_SYMBOL(tasha_codec_ver);
+
+static int __tasha_enable_efuse_sensing(struct tasha_priv *tasha)
+{
+	int val, rc;
+
+	__tasha_cdc_mclk_enable(tasha, true);
+
+	regmap_update_bits(tasha->wcd9xxx->regmap,
+			   WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x20);
+	regmap_update_bits(tasha->wcd9xxx->regmap,
+			   WCD9335_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01);
+
+	/*
+	 * 5ms sleep required after enabling efuse control
+	 * before checking the status.
+	 */
+	usleep_range(5000, 5500);
+	rc = regmap_read(tasha->wcd9xxx->regmap,
+			 WCD9335_CHIP_TIER_CTRL_EFUSE_STATUS, &val);
+
+	if (rc || (!(val & 0x01)))
+		WARN(1, "%s: Efuse sense is not complete\n", __func__);
+
+	__tasha_cdc_mclk_enable(tasha, false);
+
+	return rc;
+}
+
+void tasha_get_codec_ver(struct tasha_priv *tasha)
+{
+	int i;
+	int val;
+	struct tasha_reg_mask_val codec_reg[] = {
+		{WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT10, 0xFF, 0xFF},
+		{WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT11, 0xFF, 0x83},
+		{WCD9335_CHIP_TIER_CTRL_EFUSE_VAL_OUT12, 0xFF, 0x0A},
+	};
+
+	__tasha_enable_efuse_sensing(tasha);
+	for (i = 0; i < ARRAY_SIZE(codec_reg); i++) {
+		regmap_read(tasha->wcd9xxx->regmap, codec_reg[i].reg, &val);
+		if (!(val && codec_reg[i].val)) {
+			codec_ver = WCD9335;
+			goto ret;
+		}
+	}
+	codec_ver = WCD9326;
+ret:
+	pr_debug("%s: codec is %d\n", __func__, codec_ver);
+}
+EXPORT_SYMBOL(tasha_get_codec_ver);
+
+static int tasha_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct tasha_priv *tasha;
+	struct clk *wcd_ext_clk, *wcd_native_clk;
+	struct wcd9xxx_resmgr_v2 *resmgr;
+	struct wcd9xxx_power_region *cdc_pwr;
+
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C) {
+		if (apr_get_subsys_state() == APR_SUBSYS_DOWN) {
+			dev_err(&pdev->dev, "%s: dsp down\n", __func__);
+			return -EPROBE_DEFER;
+		}
+	}
+
+	tasha = devm_kzalloc(&pdev->dev, sizeof(struct tasha_priv),
+			    GFP_KERNEL);
+	if (!tasha)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, tasha);
+
+	tasha->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
+	tasha->dev = &pdev->dev;
+	INIT_DELAYED_WORK(&tasha->power_gate_work, tasha_codec_power_gate_work);
+	mutex_init(&tasha->power_lock);
+	mutex_init(&tasha->sido_lock);
+	INIT_WORK(&tasha->tasha_add_child_devices_work,
+		  tasha_add_child_devices);
+	BLOCKING_INIT_NOTIFIER_HEAD(&tasha->notifier);
+	mutex_init(&tasha->micb_lock);
+	mutex_init(&tasha->swr_read_lock);
+	mutex_init(&tasha->swr_write_lock);
+	mutex_init(&tasha->swr_clk_lock);
+	mutex_init(&tasha->mclk_lock);
+
+	cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
+			       GFP_KERNEL);
+	if (!cdc_pwr) {
+		ret = -ENOMEM;
+		goto err_cdc_pwr;
+	}
+	tasha->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr;
+	cdc_pwr->pwr_collapse_reg_min = TASHA_DIG_CORE_REG_MIN;
+	cdc_pwr->pwr_collapse_reg_max = TASHA_DIG_CORE_REG_MAX;
+	wcd9xxx_set_power_state(tasha->wcd9xxx,
+				WCD_REGION_POWER_COLLAPSE_REMOVE,
+				WCD9XXX_DIG_CORE_REGION_1);
+
+	mutex_init(&tasha->codec_mutex);
+	/*
+	 * Init resource manager so that if child nodes such as SoundWire
+	 * requests for clock, resource manager can honor the request
+	 */
+	resmgr = wcd_resmgr_init(&tasha->wcd9xxx->core_res, NULL);
+	if (IS_ERR(resmgr)) {
+		ret = PTR_ERR(resmgr);
+		dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n",
+			__func__);
+		goto err_resmgr;
+	}
+	tasha->resmgr = resmgr;
+	tasha->swr_plat_data.handle = (void *) tasha;
+	tasha->swr_plat_data.read = tasha_swrm_read;
+	tasha->swr_plat_data.write = tasha_swrm_write;
+	tasha->swr_plat_data.bulk_write = tasha_swrm_bulk_write;
+	tasha->swr_plat_data.clk = tasha_swrm_clock;
+	tasha->swr_plat_data.handle_irq = tasha_swrm_handle_irq;
+
+	/* Register for Clock */
+	wcd_ext_clk = clk_get(tasha->wcd9xxx->dev, "wcd_clk");
+	if (IS_ERR(wcd_ext_clk)) {
+		dev_err(tasha->wcd9xxx->dev, "%s: clk get %s failed\n",
+			__func__, "wcd_ext_clk");
+		goto err_clk;
+	}
+	tasha->wcd_ext_clk = wcd_ext_clk;
+	tasha->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV;
+	set_bit(AUDIO_NOMINAL, &tasha->status_mask);
+	tasha->sido_ccl_cnt = 0;
+
+	/* Register native clk for 44.1 playback */
+	wcd_native_clk = clk_get(tasha->wcd9xxx->dev, "wcd_native_clk");
+	if (IS_ERR(wcd_native_clk))
+		dev_dbg(tasha->wcd9xxx->dev, "%s: clk get %s failed\n",
+			__func__, "wcd_native_clk");
+	else
+		tasha->wcd_native_clk = wcd_native_clk;
+
+	if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha,
+					     tasha_dai, ARRAY_SIZE(tasha_dai));
+	else if (wcd9xxx_get_intf_type() == WCD9XXX_INTERFACE_TYPE_I2C)
+		ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tasha,
+					     tasha_i2s_dai,
+					     ARRAY_SIZE(tasha_i2s_dai));
+	else
+		ret = -EINVAL;
+	if (ret) {
+		dev_err(&pdev->dev, "%s: Codec registration failed, ret = %d\n",
+			__func__, ret);
+		goto err_cdc_reg;
+	}
+	/* Update codec register default values */
+	tasha_update_reg_defaults(tasha);
+	schedule_work(&tasha->tasha_add_child_devices_work);
+	tasha_get_codec_ver(tasha);
+
+	dev_info(&pdev->dev, "%s: Tasha driver probe done\n", __func__);
+	return ret;
+
+err_cdc_reg:
+	clk_put(tasha->wcd_ext_clk);
+	if (tasha->wcd_native_clk)
+		clk_put(tasha->wcd_native_clk);
+err_clk:
+	wcd_resmgr_remove(tasha->resmgr);
+err_resmgr:
+	devm_kfree(&pdev->dev, cdc_pwr);
+err_cdc_pwr:
+	mutex_destroy(&tasha->mclk_lock);
+	devm_kfree(&pdev->dev, tasha);
+	return ret;
+}
+
+static int tasha_remove(struct platform_device *pdev)
+{
+	struct tasha_priv *tasha;
+
+	tasha = platform_get_drvdata(pdev);
+
+	mutex_destroy(&tasha->codec_mutex);
+	clk_put(tasha->wcd_ext_clk);
+	if (tasha->wcd_native_clk)
+		clk_put(tasha->wcd_native_clk);
+	mutex_destroy(&tasha->mclk_lock);
+	devm_kfree(&pdev->dev, tasha);
+	snd_soc_unregister_codec(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver tasha_codec_driver = {
+	.probe = tasha_probe,
+	.remove = tasha_remove,
+	.driver = {
+		.name = "tasha_codec",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &tasha_pm_ops,
+#endif
+	},
+};
+
+module_platform_driver(tasha_codec_driver);
+
+MODULE_DESCRIPTION("Tasha Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9335.h b/sound/soc/codecs/wcd9335.h
new file mode 100644
index 0000000..d27bb96
--- /dev/null
+++ b/sound/soc/codecs/wcd9335.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD9335_H
+#define WCD9335_H
+
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/apr_audio-v2.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd-mbhc-v2.h"
+
+#define TASHA_REG_VAL(reg, val)      {reg, 0, val}
+
+#define TASHA_REGISTER_START_OFFSET  0x800
+#define TASHA_SB_PGD_PORT_RX_BASE   0x40
+#define TASHA_SB_PGD_PORT_TX_BASE   0x50
+
+#define TASHA_ZDET_SUPPORTED true
+/* z value defined in milliohm */
+#define TASHA_ZDET_VAL_32	32000
+#define TASHA_ZDET_VAL_400	400000
+#define TASHA_ZDET_VAL_1200	1200000
+#define TASHA_ZDET_VAL_100K	100000000
+/* z floating defined in ohms */
+#define TASHA_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define WCD9335_DMIC_CLK_DIV_2  0x0
+#define WCD9335_DMIC_CLK_DIV_3  0x1
+#define WCD9335_DMIC_CLK_DIV_4  0x2
+#define WCD9335_DMIC_CLK_DIV_6  0x3
+#define WCD9335_DMIC_CLK_DIV_8  0x4
+#define WCD9335_DMIC_CLK_DIV_16  0x5
+#define WCD9335_DMIC_CLK_DRIVE_DEFAULT 0x02
+
+#define WCD9335_ANC_DMIC_X2_FULL_RATE 1
+#define WCD9335_ANC_DMIC_X2_HALF_RATE 0
+
+/* Number of input and output Slimbus port */
+enum {
+	TASHA_RX0 = 0,
+	TASHA_RX1,
+	TASHA_RX2,
+	TASHA_RX3,
+	TASHA_RX4,
+	TASHA_RX5,
+	TASHA_RX6,
+	TASHA_RX7,
+	TASHA_RX8,
+	TASHA_RX9,
+	TASHA_RX10,
+	TASHA_RX11,
+	TASHA_RX12,
+	TASHA_RX_MAX,
+};
+
+enum {
+	TASHA_TX0 = 0,
+	TASHA_TX1,
+	TASHA_TX2,
+	TASHA_TX3,
+	TASHA_TX4,
+	TASHA_TX5,
+	TASHA_TX6,
+	TASHA_TX7,
+	TASHA_TX8,
+	TASHA_TX9,
+	TASHA_TX10,
+	TASHA_TX11,
+	TASHA_TX12,
+	TASHA_TX13,
+	TASHA_TX14,
+	TASHA_TX15,
+	TASHA_TX_MAX,
+};
+
+enum {
+	/* INTR_REG 0 */
+	WCD9335_IRQ_FLL_LOCK_LOSS = 1,
+	WCD9335_IRQ_HPH_PA_OCPL_FAULT,
+	WCD9335_IRQ_HPH_PA_OCPR_FAULT,
+	WCD9335_IRQ_EAR_PA_OCP_FAULT,
+	WCD9335_IRQ_HPH_PA_CNPL_COMPLETE,
+	WCD9335_IRQ_HPH_PA_CNPR_COMPLETE,
+	WCD9335_IRQ_EAR_PA_CNP_COMPLETE,
+	/* INTR_REG 1 */
+	WCD9335_IRQ_MBHC_SW_DET,
+	WCD9335_IRQ_MBHC_ELECT_INS_REM_DET,
+	WCD9335_IRQ_MBHC_BUTTON_PRESS_DET,
+	WCD9335_IRQ_MBHC_BUTTON_RELEASE_DET,
+	WCD9335_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	WCD9335_IRQ_RESERVED_0,
+	WCD9335_IRQ_RESERVED_1,
+	WCD9335_IRQ_RESERVED_2,
+	/* INTR_REG 2 */
+	WCD9335_IRQ_LINE_PA1_CNP_COMPLETE,
+	WCD9335_IRQ_LINE_PA2_CNP_COMPLETE,
+	WCD9335_IRQ_LINE_PA3_CNP_COMPLETE,
+	WCD9335_IRQ_LINE_PA4_CNP_COMPLETE,
+	WCD9335_IRQ_SOUNDWIRE,
+	WCD9335_IRQ_VDD_DIG_RAMP_COMPLETE,
+	WCD9335_IRQ_RCO_ERROR,
+	WCD9335_IRQ_SVA_ERROR,
+	/* INTR_REG 3 */
+	WCD9335_IRQ_MAD_AUDIO,
+	WCD9335_IRQ_MAD_BEACON,
+	WCD9335_IRQ_MAD_ULTRASOUND,
+	WCD9335_IRQ_VBAT_ATTACK,
+	WCD9335_IRQ_VBAT_RESTORE,
+	WCD9335_IRQ_SVA_OUTBOX1,
+	WCD9335_IRQ_SVA_OUTBOX2,
+	WCD9335_NUM_IRQS,
+};
+
+enum wcd9335_codec_event {
+	WCD9335_CODEC_EVENT_CODEC_UP = 0,
+};
+
+enum tasha_on_demand_supply {
+	ON_DEMAND_MICBIAS = 0,
+	ON_DEMAND_SUPPLIES_MAX,
+};
+
+/* structure used to put the defined
+ * ondemand supply for codec
+ * and count being used.
+ */
+struct on_demand_supply {
+	struct regulator *supply;
+	int ondemand_supply_count;
+};
+
+/* Dai data structure holds the
+ * dai specific info like rate,
+ * channel number etc.
+ */
+struct tasha_codec_dai_data {
+	u32 rate;
+	u32 *ch_num;
+	u32 ch_act;
+	u32 ch_tot;
+};
+
+/* Structure used to update codec
+ * register defaults after reset
+ */
+struct tasha_reg_mask_val {
+	u16 reg;
+	u8 mask;
+	u8 val;
+};
+
+/* Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+	SPKR_MODE_DEFAULT,
+	SPKR_MODE_1,          /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+/*
+ * Rx path gain offsets
+ */
+enum {
+	RX_GAIN_OFFSET_M1P5_DB,
+	RX_GAIN_OFFSET_0_DB,
+};
+
+extern void *tasha_get_afe_config(struct snd_soc_codec *codec,
+				  enum afe_config_type config_type);
+extern int tasha_cdc_mclk_enable(struct snd_soc_codec *codec, int enable,
+				 bool dapm);
+extern int tasha_cdc_mclk_tx_enable(struct snd_soc_codec *codec, int enable,
+				    bool dapm);
+extern int tasha_enable_efuse_sensing(struct snd_soc_codec *codec);
+extern int tasha_mbhc_hs_detect(struct snd_soc_codec *codec,
+				struct wcd_mbhc_config *mbhc_cfg);
+extern void tasha_mbhc_hs_detect_exit(struct snd_soc_codec *codec);
+extern void tasha_mbhc_zdet_gpio_ctrl(
+		int (*zdet_gpio_cb)(struct snd_soc_codec *codec, bool high),
+		struct snd_soc_codec *codec);
+extern int tasha_codec_info_create_codec_entry(
+		struct snd_info_entry *codec_root,
+		struct snd_soc_codec *codec);
+extern void tasha_event_register(
+	int (*machine_event_cb)(struct snd_soc_codec *codec,
+				enum wcd9335_codec_event),
+	struct snd_soc_codec *codec);
+extern int tasha_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+						 int micb_num,
+						 bool enable);
+extern int tasha_set_spkr_mode(struct snd_soc_codec *codec, int mode);
+extern int tasha_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset);
+extern enum codec_variant tasha_codec_ver(void);
+#endif
diff --git a/sound/soc/codecs/wcd934x/Makefile b/sound/soc/codecs/wcd934x/Makefile
new file mode 100644
index 0000000..2843fa1
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for wcd934x codec driver.
+#
+snd-soc-wcd934x-objs := wcd934x.o wcd934x-dsp-cntl.o
+obj-$(CONFIG_SND_SOC_WCD934X) += snd-soc-wcd934x.o
+snd-soc-wcd934x-mbhc-objs := wcd934x-mbhc.o
+obj-$(CONFIG_SND_SOC_WCD934X_MBHC) += snd-soc-wcd934x-mbhc.o
+snd-soc-wcd934x-dsd-objs := wcd934x-dsd.o
+obj-$(CONFIG_SND_SOC_WCD934X_DSD) += snd-soc-wcd934x-dsd.o
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.c b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
new file mode 100644
index 0000000..580591a
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.c
@@ -0,0 +1,767 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/tlv.h>
+#include <sound/control.h>
+#include "wcd934x-dsd.h"
+
+#define DSD_VOLUME_MAX_0dB      0
+#define DSD_VOLUME_MIN_M110dB   -110
+
+#define DSD_VOLUME_RANGE_CHECK(x)   ((x >= DSD_VOLUME_MIN_M110dB) &&\
+				     (x <= DSD_VOLUME_MAX_0dB))
+#define DSD_VOLUME_STEPS            3
+#define DSD_VOLUME_UPDATE_DELAY_MS  30
+#define DSD_VOLUME_USLEEP_MARGIN_US 100
+#define DSD_VOLUME_STEP_DELAY_US    ((1000 * DSD_VOLUME_UPDATE_DELAY_MS) / \
+				     (2 * DSD_VOLUME_STEPS))
+
+#define TAVIL_VERSION_1_0  0
+#define TAVIL_VERSION_1_1  1
+
+static const DECLARE_TLV_DB_MINMAX(tavil_dsd_db_scale, DSD_VOLUME_MIN_M110dB,
+				   DSD_VOLUME_MAX_0dB);
+
+static const char *const dsd_if_text[] = {
+	"ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7",
+	"DSD_DATA_PAD"
+};
+
+static const char * const dsd_filt0_mux_text[] = {
+	"ZERO", "DSD_L IF MUX",
+};
+
+static const char * const dsd_filt1_mux_text[] = {
+	"ZERO", "DSD_R IF MUX",
+};
+
+static const struct soc_enum dsd_filt0_mux_enum =
+	SOC_ENUM_SINGLE(WCD934X_CDC_DSD0_PATH_CTL, 0,
+			ARRAY_SIZE(dsd_filt0_mux_text), dsd_filt0_mux_text);
+
+static const struct soc_enum dsd_filt1_mux_enum =
+	SOC_ENUM_SINGLE(WCD934X_CDC_DSD1_PATH_CTL, 0,
+			ARRAY_SIZE(dsd_filt1_mux_text), dsd_filt1_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(dsd_l_if_enum, WCD934X_CDC_DSD0_CFG0,
+			    2, dsd_if_text);
+static SOC_ENUM_SINGLE_DECL(dsd_r_if_enum, WCD934X_CDC_DSD1_CFG0,
+			    2, dsd_if_text);
+
+static const struct snd_kcontrol_new dsd_filt0_mux =
+		SOC_DAPM_ENUM("DSD Filt0 Mux", dsd_filt0_mux_enum);
+
+static const struct snd_kcontrol_new dsd_filt1_mux =
+		SOC_DAPM_ENUM("DSD Filt1 Mux", dsd_filt1_mux_enum);
+
+static const struct snd_kcontrol_new dsd_l_if_mux =
+		SOC_DAPM_ENUM("DSD Left If Mux", dsd_l_if_enum);
+static const struct snd_kcontrol_new dsd_r_if_mux =
+		SOC_DAPM_ENUM("DSD Right If Mux", dsd_r_if_enum);
+
+static const struct snd_soc_dapm_route tavil_dsd_audio_map[] = {
+	{"DSD_L IF MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"DSD_L IF MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"DSD_L IF MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"DSD_L IF MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"DSD_L IF MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"DSD_L IF MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"DSD_L IF MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"DSD_L IF MUX", "RX7", "CDC_IF RX7 MUX"},
+
+	{"DSD_FILTER_0", NULL, "DSD_L IF MUX"},
+	{"DSD_FILTER_0", NULL, "RX INT1 NATIVE SUPPLY"},
+	{"RX INT1 MIX3", "DSD HPHL Switch", "DSD_FILTER_0"},
+
+	{"DSD_R IF MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"DSD_R IF MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"DSD_R IF MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"DSD_R IF MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"DSD_R IF MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"DSD_R IF MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"DSD_R IF MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"DSD_R IF MUX", "RX7", "CDC_IF RX7 MUX"},
+
+	{"DSD_FILTER_1", NULL, "DSD_R IF MUX"},
+	{"DSD_FILTER_1", NULL, "RX INT2 NATIVE SUPPLY"},
+	{"RX INT2 MIX3", "DSD HPHR Switch", "DSD_FILTER_1"},
+};
+
+static bool is_valid_dsd_interpolator(int interp_num)
+{
+	if ((interp_num == INTERP_HPHL) || (interp_num == INTERP_HPHR) ||
+	    (interp_num == INTERP_LO1) || (interp_num == INTERP_LO2))
+		return true;
+
+	return false;
+}
+
+/**
+ * tavil_dsd_set_mixer_value - Set DSD HPH/LO mixer value
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ * @sw_value: Mixer switch value
+ *
+ * Returns 0 on success or -EINVAL on failure
+ */
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+			      int interp_num, int sw_value)
+{
+	if (!dsd_conf)
+		return -EINVAL;
+
+	if (!is_valid_dsd_interpolator(interp_num))
+		return -EINVAL;
+
+	dsd_conf->dsd_interp_mixer[interp_num] = !!sw_value;
+
+	return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_set_mixer_value);
+
+/**
+ * tavil_dsd_get_current_mixer_value - Get DSD HPH/LO mixer value
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ *
+ * Returns current mixer val for success or -EINVAL for failure
+ */
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+				      int interp_num)
+{
+	if (!dsd_conf)
+		return -EINVAL;
+
+	if (!is_valid_dsd_interpolator(interp_num))
+		return -EINVAL;
+
+	return dsd_conf->dsd_interp_mixer[interp_num];
+}
+EXPORT_SYMBOL(tavil_dsd_get_current_mixer_value);
+
+/**
+ * tavil_dsd_set_out_select - DSD0/1 out select to HPH or LO
+ *
+ * @dsd_conf: pointer to dsd config
+ * @interp_num: Interpolator number (HPHL/R, LO1/2)
+ *
+ * Returns 0 for success or -EINVAL for failure
+ */
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+			     int interp_num)
+{
+	unsigned int reg, val;
+	struct snd_soc_codec *codec;
+
+	if (!dsd_conf || !dsd_conf->codec)
+		return -EINVAL;
+
+	codec = dsd_conf->codec;
+
+	if (!is_valid_dsd_interpolator(interp_num)) {
+		dev_err(codec->dev, "%s: Invalid Interpolator: %d for DSD\n",
+			__func__, interp_num);
+		return -EINVAL;
+	}
+
+	switch (interp_num) {
+	case INTERP_HPHL:
+		reg = WCD934X_CDC_DSD0_CFG0;
+		val = 0x00;
+		break;
+	case INTERP_HPHR:
+		reg = WCD934X_CDC_DSD1_CFG0;
+		val = 0x00;
+		break;
+	case INTERP_LO1:
+		reg = WCD934X_CDC_DSD0_CFG0;
+		val = 0x02;
+		break;
+	case INTERP_LO2:
+		reg = WCD934X_CDC_DSD1_CFG0;
+		val = 0x02;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, reg, 0x02, val);
+
+	return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_set_out_select);
+
+/**
+ * tavil_dsd_reset - Reset DSD block
+ *
+ * @dsd_conf: pointer to dsd config
+ *
+ */
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf)
+{
+	if (!dsd_conf || !dsd_conf->codec)
+		return;
+
+	snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL,
+			    0x02, 0x02);
+	snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD0_PATH_CTL,
+			    0x01, 0x00);
+	snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL,
+			    0x02, 0x02);
+	snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_DSD1_PATH_CTL,
+			    0x01, 0x00);
+}
+EXPORT_SYMBOL(tavil_dsd_reset);
+
+/**
+ * tavil_dsd_set_interp_rate - Set interpolator rate for DSD
+ *
+ * @dsd_conf: pointer to dsd config
+ * @rx_port: RX port number
+ * @sample_rate: Sample rate of the RX interpolator
+ * @sample_rate_val: Interpolator rate value
+ */
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+			       u32 sample_rate, u8 sample_rate_val)
+{
+	u8 dsd_inp_sel;
+	u8 dsd0_inp, dsd1_inp;
+	u8 val0, val1;
+	u8 dsd0_out_sel, dsd1_out_sel;
+	u16 int_fs_reg, interp_num = 0;
+	struct snd_soc_codec *codec;
+
+	if (!dsd_conf || !dsd_conf->codec)
+		return;
+
+	codec = dsd_conf->codec;
+
+	dsd_inp_sel = DSD_INP_SEL_RX0 + rx_port - WCD934X_RX_PORT_START_NUMBER;
+
+	val0 = snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0);
+	val1 = snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0);
+	dsd0_inp = (val0 & 0x3C) >> 2;
+	dsd1_inp = (val1 & 0x3C) >> 2;
+	dsd0_out_sel = (val0 & 0x02) >> 1;
+	dsd1_out_sel = (val1 & 0x02) >> 1;
+
+	/* Set HPHL or LO1 interp rate based on out select */
+	if (dsd_inp_sel == dsd0_inp) {
+		interp_num = dsd0_out_sel ? INTERP_LO1 : INTERP_HPHL;
+		dsd_conf->base_sample_rate[DSD0] = sample_rate;
+	}
+
+	/* Set HPHR or LO2 interp rate based on out select */
+	if (dsd_inp_sel == dsd1_inp) {
+		interp_num = dsd1_out_sel ? INTERP_LO2 : INTERP_HPHR;
+		dsd_conf->base_sample_rate[DSD1] = sample_rate;
+	}
+
+	if (interp_num) {
+		int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL + 20 * interp_num;
+		if ((snd_soc_read(codec, int_fs_reg) & 0x0f) < 0x09) {
+			dev_dbg(codec->dev, "%s: Set Interp %d to sample_rate val 0x%x\n",
+				__func__, interp_num, sample_rate_val);
+			snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+					    sample_rate_val);
+		}
+	}
+}
+EXPORT_SYMBOL(tavil_dsd_set_interp_rate);
+
+static int tavil_set_dsd_mode(struct snd_soc_codec *codec, int dsd_num,
+			      u8 *pcm_rate_val)
+{
+	unsigned int dsd_out_sel_reg;
+	u8 dsd_mode;
+	u32 sample_rate;
+	struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+
+	if (!dsd_conf)
+		return -EINVAL;
+
+	if ((dsd_num < 0) || (dsd_num > 1))
+		return -EINVAL;
+
+	sample_rate = dsd_conf->base_sample_rate[dsd_num];
+	dsd_out_sel_reg = WCD934X_CDC_DSD0_CFG0 + dsd_num * 16;
+
+	switch (sample_rate) {
+	case 176400:
+		dsd_mode = 0; /* DSD_64 */
+		*pcm_rate_val = 0xb;
+		break;
+	case 352800:
+		dsd_mode = 1; /* DSD_128 */
+		*pcm_rate_val = 0xc;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid DSD rate: %d\n",
+			__func__, sample_rate);
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, dsd_out_sel_reg, 0x01, dsd_mode);
+
+	return 0;
+}
+
+static void tavil_dsd_data_pull(struct snd_soc_codec *codec, int dsd_num,
+				u8 pcm_rate_val, bool enable)
+{
+	u8 clk_en, mute_en;
+	u8 dsd_inp_sel;
+
+	if (enable) {
+		clk_en = 0x20;
+		mute_en = 0x10;
+	} else {
+		clk_en = 0x00;
+		mute_en = 0x00;
+	}
+
+	if (dsd_num & 0x01) {
+		snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+				    0x20, clk_en);
+		dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) &
+				0x3C) >> 2;
+		dsd_inp_sel = (enable) ? dsd_inp_sel : 0;
+		if (dsd_inp_sel < 9) {
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1,
+					0x0F, dsd_inp_sel);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+					0x0F, pcm_rate_val);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_RX7_RX_PATH_MIX_CTL,
+					0x10, mute_en);
+		}
+	}
+	if (dsd_num & 0x02) {
+		snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+				    0x20, clk_en);
+		dsd_inp_sel = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) &
+				0x3C) >> 2;
+		dsd_inp_sel = (enable) ? dsd_inp_sel : 0;
+		if (dsd_inp_sel < 9) {
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1,
+					0x0F, dsd_inp_sel);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+					0x0F, pcm_rate_val);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_RX8_RX_PATH_MIX_CTL,
+					0x10, mute_en);
+		}
+	}
+}
+
+static void tavil_dsd_update_volume(struct tavil_dsd_config *dsd_conf)
+{
+	snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0,
+			    0x01, 0x01);
+	snd_soc_update_bits(dsd_conf->codec, WCD934X_CDC_TOP_TOP_CFG0,
+			    0x01, 0x00);
+}
+
+static int tavil_enable_dsd(struct snd_soc_dapm_widget *w,
+			    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+	int rc, clk_users;
+	int interp_idx;
+	u8 pcm_rate_val;
+
+	if (!dsd_conf) {
+		dev_err(codec->dev, "%s: null dsd_config pointer\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "%s: DSD%d, event: %d\n", __func__,
+		w->shift, event);
+
+	if (w->shift == DSD0) {
+		/* Read out select */
+		if (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x02)
+			interp_idx = INTERP_LO1;
+		else
+			interp_idx = INTERP_HPHL;
+	} else if (w->shift == DSD1) {
+		/* Read out select */
+		if (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x02)
+			interp_idx = INTERP_LO2;
+		else
+			interp_idx = INTERP_HPHR;
+	} else {
+		dev_err(codec->dev, "%s: Unsupported DSD:%d\n",
+			__func__, w->shift);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		clk_users = tavil_codec_enable_interp_clk(codec, event,
+							  interp_idx);
+
+		rc = tavil_set_dsd_mode(codec, w->shift, &pcm_rate_val);
+		if (rc)
+			return rc;
+
+		tavil_dsd_data_pull(codec, (1 << w->shift), pcm_rate_val,
+				    true);
+
+		snd_soc_update_bits(codec,
+				    WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL, 0x01,
+				    0x01);
+		if (w->shift == DSD0) {
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+					    0x02, 0x02);
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+					    0x02, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+					    0x01, 0x01);
+			/* Apply Gain */
+			snd_soc_write(codec, WCD934X_CDC_DSD0_CFG1,
+				      dsd_conf->volume[DSD0]);
+			if (dsd_conf->version == TAVIL_VERSION_1_1)
+				tavil_dsd_update_volume(dsd_conf);
+
+		} else if (w->shift == DSD1) {
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+					    0x02, 0x02);
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+					    0x02, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+					    0x01, 0x01);
+			/* Apply Gain */
+			snd_soc_write(codec, WCD934X_CDC_DSD1_CFG1,
+				      dsd_conf->volume[DSD1]);
+			if (dsd_conf->version == TAVIL_VERSION_1_1)
+				tavil_dsd_update_volume(dsd_conf);
+		}
+		/* 10msec sleep required after DSD clock is set */
+		usleep_range(10000, 10100);
+
+		if (clk_users > 1) {
+			snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
+			if (w->shift == DSD0)
+				snd_soc_update_bits(codec,
+						    WCD934X_CDC_DSD0_CFG2,
+						    0x04, 0x00);
+			if (w->shift == DSD1)
+				snd_soc_update_bits(codec,
+						    WCD934X_CDC_DSD1_CFG2,
+						    0x04, 0x00);
+
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (w->shift == DSD0) {
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+					    0x04, 0x04);
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_PATH_CTL,
+					    0x01, 0x00);
+		} else if (w->shift == DSD1) {
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+					    0x04, 0x04);
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_PATH_CTL,
+					    0x01, 0x00);
+		}
+
+		tavil_codec_enable_interp_clk(codec, event, interp_idx);
+
+		if (!(snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01) &&
+		    !(snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_CLK_RST_CTRL_DSD_CONTROL,
+					0x01, 0x00);
+			tavil_dsd_data_pull(codec, 0x03, 0x04, false);
+			tavil_dsd_reset(dsd_conf);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int tavil_dsd_vol_info(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+	uinfo->count = 2;
+	uinfo->value.integer.min = DSD_VOLUME_MIN_M110dB;
+	uinfo->value.integer.max = DSD_VOLUME_MAX_0dB;
+
+	return 0;
+}
+
+static int tavil_dsd_vol_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+	int nv[DSD_MAX], cv[DSD_MAX];
+	int step_size, nv1;
+	int i, dsd_idx;
+
+	if (!dsd_conf)
+		return 0;
+
+	mutex_lock(&dsd_conf->vol_mutex);
+
+	for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) {
+		cv[dsd_idx] = dsd_conf->volume[dsd_idx];
+		nv[dsd_idx] = ucontrol->value.integer.value[dsd_idx];
+	}
+
+	if ((!DSD_VOLUME_RANGE_CHECK(nv[DSD0])) ||
+	    (!DSD_VOLUME_RANGE_CHECK(nv[DSD1])))
+		goto done;
+
+	for (dsd_idx = DSD0; dsd_idx < DSD_MAX; dsd_idx++) {
+		if (cv[dsd_idx] == nv[dsd_idx])
+			continue;
+
+		dev_dbg(codec->dev, "%s: DSD%d cur.vol: %d, new vol: %d\n",
+			__func__, dsd_idx, cv[dsd_idx], nv[dsd_idx]);
+
+		step_size =  (nv[dsd_idx] - cv[dsd_idx]) /
+			      DSD_VOLUME_STEPS;
+
+		nv1 = cv[dsd_idx];
+
+		for (i = 0; i < DSD_VOLUME_STEPS; i++) {
+			nv1 += step_size;
+			snd_soc_write(codec,
+				      WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
+				      nv1);
+			if (dsd_conf->version == TAVIL_VERSION_1_1)
+				tavil_dsd_update_volume(dsd_conf);
+
+			/* sleep required after each volume step */
+			usleep_range(DSD_VOLUME_STEP_DELAY_US,
+				     (DSD_VOLUME_STEP_DELAY_US +
+				      DSD_VOLUME_USLEEP_MARGIN_US));
+		}
+		if (nv1 != nv[dsd_idx]) {
+			snd_soc_write(codec,
+				      WCD934X_CDC_DSD0_CFG1 + 16 * dsd_idx,
+				      nv[dsd_idx]);
+
+			if (dsd_conf->version == TAVIL_VERSION_1_1)
+				tavil_dsd_update_volume(dsd_conf);
+		}
+
+		dsd_conf->volume[dsd_idx] = nv[dsd_idx];
+	}
+
+done:
+	mutex_unlock(&dsd_conf->vol_mutex);
+
+	return 0;
+}
+
+static int tavil_dsd_vol_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_dsd_config *dsd_conf = tavil_get_dsd_config(codec);
+
+	if (dsd_conf) {
+		ucontrol->value.integer.value[0] = dsd_conf->volume[DSD0];
+		ucontrol->value.integer.value[1] = dsd_conf->volume[DSD1];
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new tavil_dsd_vol_controls[] = {
+	{
+	   .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	   .access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+		      SNDRV_CTL_ELEM_ACCESS_TLV_READ),
+	   .name = "DSD Volume",
+	   .info = tavil_dsd_vol_info,
+	   .get = tavil_dsd_vol_get,
+	   .put = tavil_dsd_vol_put,
+	   .tlv = { .p = tavil_dsd_db_scale },
+	},
+};
+
+static const struct snd_soc_dapm_widget tavil_dsd_widgets[] = {
+	SND_SOC_DAPM_MUX("DSD_L IF MUX", SND_SOC_NOPM, 0, 0, &dsd_l_if_mux),
+	SND_SOC_DAPM_MUX_E("DSD_FILTER_0", SND_SOC_NOPM, 0, 0, &dsd_filt0_mux,
+			   tavil_enable_dsd,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("DSD_R IF MUX", SND_SOC_NOPM, 0, 0, &dsd_r_if_mux),
+	SND_SOC_DAPM_MUX_E("DSD_FILTER_1", SND_SOC_NOPM, 1, 0, &dsd_filt1_mux,
+			   tavil_enable_dsd,
+			   SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+/**
+ * tavil_dsd_post_ssr_init - DSD intialization after subsystem restart
+ *
+ * @codec: pointer to snd_soc_codec
+ *
+ * Returns 0 on success or error on failure
+ */
+int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_conf)
+{
+	struct snd_soc_codec *codec;
+
+	if (!dsd_conf || !dsd_conf->codec)
+		return -EINVAL;
+
+	codec = dsd_conf->codec;
+	/* Disable DSD Interrupts */
+	snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08);
+
+	/* DSD registers init */
+	if (dsd_conf->version == TAVIL_VERSION_1_0) {
+		snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
+		snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+	}
+	/* DSD0: Mute EN */
+	snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04);
+	/* DSD1: Mute EN */
+	snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10,
+			    0x10);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10,
+			    0x10);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E,
+			    0x0A);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E,
+			    0x0A);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07,
+			    0x04);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07,
+			    0x04);
+
+	/* Enable DSD Interrupts */
+	snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00);
+
+	return 0;
+}
+EXPORT_SYMBOL(tavil_dsd_post_ssr_init);
+
+/**
+ * tavil_dsd_init - DSD intialization
+ *
+ * @codec: pointer to snd_soc_codec
+ *
+ * Returns pointer to tavil_dsd_config for success or NULL for failure
+ */
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm;
+	struct tavil_dsd_config *dsd_conf;
+	u8 val;
+
+	if (!codec)
+		return NULL;
+
+	dapm = snd_soc_codec_get_dapm(codec);
+
+	/* Read efuse register to check if DSD is supported */
+	val = snd_soc_read(codec, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14);
+	if (val & 0x80) {
+		dev_info(codec->dev, "%s: DSD unsupported for this codec version\n",
+			 __func__);
+		return NULL;
+	}
+
+	dsd_conf = devm_kzalloc(codec->dev, sizeof(struct tavil_dsd_config),
+				GFP_KERNEL);
+	if (!dsd_conf)
+		return NULL;
+
+	dsd_conf->codec = codec;
+
+	/* Read version */
+	dsd_conf->version = snd_soc_read(codec,
+					 WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0);
+	/* DSD registers init */
+	if (dsd_conf->version == TAVIL_VERSION_1_0) {
+		snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x02, 0x00);
+		snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x02, 0x00);
+	}
+	/* DSD0: Mute EN */
+	snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2, 0x04, 0x04);
+	/* DSD1: Mute EN */
+	snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG3, 0x10,
+			    0x10);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG3, 0x10,
+			    0x10);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG0, 0x0E,
+			    0x0A);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG0, 0x0E,
+			    0x0A);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD0_DEBUG_CFG1, 0x07,
+			    0x04);
+	snd_soc_update_bits(codec, WCD934X_CDC_DEBUG_DSD1_DEBUG_CFG1, 0x07,
+			    0x04);
+
+	snd_soc_dapm_new_controls(dapm, tavil_dsd_widgets,
+				  ARRAY_SIZE(tavil_dsd_widgets));
+
+	snd_soc_dapm_add_routes(dapm, tavil_dsd_audio_map,
+				ARRAY_SIZE(tavil_dsd_audio_map));
+
+	mutex_init(&dsd_conf->vol_mutex);
+	dsd_conf->volume[DSD0] = DSD_VOLUME_MAX_0dB;
+	dsd_conf->volume[DSD1] = DSD_VOLUME_MAX_0dB;
+
+	snd_soc_add_codec_controls(codec, tavil_dsd_vol_controls,
+				   ARRAY_SIZE(tavil_dsd_vol_controls));
+
+	/* Enable DSD Interrupts */
+	snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x00);
+
+	return dsd_conf;
+}
+EXPORT_SYMBOL(tavil_dsd_init);
+
+/**
+ * tavil_dsd_deinit - DSD de-intialization
+ *
+ * @dsd_conf: pointer to tavil_dsd_config
+ */
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_conf)
+{
+	struct snd_soc_codec *codec;
+
+	if (!dsd_conf)
+		return;
+
+	codec = dsd_conf->codec;
+
+	mutex_destroy(&dsd_conf->vol_mutex);
+
+	/* Disable DSD Interrupts */
+	snd_soc_update_bits(codec, WCD934X_INTR_CODEC_MISC_MASK, 0x08, 0x08);
+
+	devm_kfree(codec->dev, dsd_conf);
+}
+EXPORT_SYMBOL(tavil_dsd_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsd.h b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
new file mode 100644
index 0000000..4982883
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsd.h
@@ -0,0 +1,97 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD934X_DSD_H__
+#define __WCD934X_DSD_H__
+
+#include <sound/soc.h>
+#include "wcd934x.h"
+
+enum {
+	DSD0,
+	DSD1,
+	DSD_MAX,
+};
+
+enum {
+	DSD_INP_SEL_ZERO = 0,
+	DSD_INP_SEL_RX0,
+	DSD_INP_SEL_RX1,
+	DSD_INP_SEL_RX2,
+	DSD_INP_SEL_RX3,
+	DSD_INP_SEL_RX4,
+	DSD_INP_SEL_RX5,
+	DSD_INP_SEL_RX6,
+	DSD_INP_SEL_RX7,
+};
+
+struct tavil_dsd_config {
+	struct snd_soc_codec *codec;
+	unsigned int dsd_interp_mixer[INTERP_MAX];
+	u32 base_sample_rate[DSD_MAX];
+	int volume[DSD_MAX];
+	struct mutex vol_mutex;
+	int version;
+};
+
+#ifdef CONFIG_SND_SOC_WCD934X_DSD
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+			      int interp_num, int sw_value);
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+				      int interp_num);
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+			     int interp_num);
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf);
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+			       u32 sample_rate, u8 sample_rate_val);
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec);
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config);
+int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config);
+#else
+int tavil_dsd_set_mixer_value(struct tavil_dsd_config *dsd_conf,
+			      int interp_num, int sw_value)
+{
+	return 0;
+}
+
+int tavil_dsd_get_current_mixer_value(struct tavil_dsd_config *dsd_conf,
+				      int interp_num)
+{
+	return 0;
+}
+
+int tavil_dsd_set_out_select(struct tavil_dsd_config *dsd_conf,
+			     int interp_num)
+{
+	return 0;
+}
+
+void tavil_dsd_reset(struct tavil_dsd_config *dsd_conf)
+{  }
+
+void tavil_dsd_set_interp_rate(struct tavil_dsd_config *dsd_conf, u16 rx_port,
+			       u32 sample_rate, u8 sample_rate_val)
+{  }
+
+struct tavil_dsd_config *tavil_dsd_init(struct snd_soc_codec *codec)
+{
+	return NULL;
+}
+
+void tavil_dsd_deinit(struct tavil_dsd_config *dsd_config)
+{  }
+int tavil_dsd_post_ssr_init(struct tavil_dsd_config *dsd_config)
+{
+	return 0;
+}
+#endif
+#endif
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
new file mode 100644
index 0000000..9bc5d5f
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.c
@@ -0,0 +1,1369 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/component.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/soc.h>
+#include <sound/wcd-dsp-mgr.h>
+#include "wcd934x.h"
+#include "wcd934x-dsp-cntl.h"
+
+#define WCD_CNTL_DIR_NAME_LEN_MAX 32
+#define WCD_CPE_FLL_MAX_RETRIES 5
+#define WCD_MEM_ENABLE_MAX_RETRIES 20
+#define WCD_DSP_BOOT_TIMEOUT_MS 3000
+#define WCD_SYSFS_ENTRY_MAX_LEN 8
+#define WCD_PROCFS_ENTRY_MAX_LEN 16
+#define WCD_934X_RAMDUMP_START_ADDR 0x20100000
+#define WCD_934X_RAMDUMP_SIZE ((1024 * 1024) - 128)
+
+#define WCD_CNTL_MUTEX_LOCK(codec, lock)             \
+{                                                    \
+	dev_dbg(codec->dev, "%s: mutex_lock(%s)\n",  \
+		__func__, __stringify_1(lock));      \
+	mutex_lock(&lock);                           \
+}
+
+#define WCD_CNTL_MUTEX_UNLOCK(codec, lock)            \
+{                                                     \
+	dev_dbg(codec->dev, "%s: mutex_unlock(%s)\n", \
+		__func__, __stringify_1(lock));       \
+	mutex_unlock(&lock);                          \
+}
+
+enum wcd_mem_type {
+	WCD_MEM_TYPE_ALWAYS_ON,
+	WCD_MEM_TYPE_SWITCHABLE,
+};
+
+struct wcd_cntl_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct wcd_dsp_cntl *cntl, char *buf);
+	ssize_t (*store)(struct wcd_dsp_cntl *cntl, const char *buf,
+			 ssize_t count);
+};
+
+#define WCD_CNTL_ATTR(_name, _mode, _show, _store) \
+static struct wcd_cntl_attribute cntl_attr_##_name = {	\
+	.attr = {.name = __stringify(_name), .mode = _mode},	\
+	.show = _show,	\
+	.store = _store,	\
+}
+
+#define to_wcd_cntl_attr(a) \
+	container_of((a), struct wcd_cntl_attribute, attr)
+
+#define to_wcd_cntl(kobj) \
+	container_of((kobj), struct wcd_dsp_cntl, wcd_kobj)
+
+static u8 mem_enable_values[] = {
+	0xFE, 0xFC, 0xF8, 0xF0,
+	0xE0, 0xC0, 0x80, 0x00,
+};
+
+static ssize_t wdsp_boot_show(struct wcd_dsp_cntl *cntl, char *buf)
+{
+	return snprintf(buf, WCD_SYSFS_ENTRY_MAX_LEN,
+			"%u", cntl->boot_reqs);
+}
+
+static ssize_t wdsp_boot_store(struct wcd_dsp_cntl *cntl,
+			       const char *buf, ssize_t count)
+{
+	u32 val;
+	bool vote;
+	int ret;
+
+	ret = kstrtou32(buf, 10, &val);
+	if (ret) {
+		dev_err(cntl->codec->dev,
+			"%s: Invalid entry, ret = %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	if (val > 0) {
+		cntl->boot_reqs++;
+		vote = true;
+	} else {
+		cntl->boot_reqs--;
+		vote = false;
+	}
+
+	if (cntl->m_dev && cntl->m_ops &&
+	    cntl->m_ops->vote_for_dsp)
+		ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote);
+	else
+		ret = -EINVAL;
+
+	if (ret < 0)
+		dev_err(cntl->codec->dev,
+			"%s: failed to %s dsp\n", __func__,
+			vote ? "enable" : "disable");
+	return count;
+}
+
+WCD_CNTL_ATTR(boot, 0660, wdsp_boot_show, wdsp_boot_store);
+
+static ssize_t wcd_cntl_sysfs_show(struct kobject *kobj,
+				   struct attribute *attr, char *buf)
+{
+	struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr);
+	struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj);
+	ssize_t ret = -EINVAL;
+
+	if (cntl && wcd_attr->show)
+		ret = wcd_attr->show(cntl, buf);
+
+	return ret;
+}
+
+static ssize_t wcd_cntl_sysfs_store(struct kobject *kobj,
+				    struct attribute *attr, const char *buf,
+				    size_t count)
+{
+	struct wcd_cntl_attribute *wcd_attr = to_wcd_cntl_attr(attr);
+	struct wcd_dsp_cntl *cntl = to_wcd_cntl(kobj);
+	ssize_t ret = -EINVAL;
+
+	if (cntl && wcd_attr->store)
+		ret = wcd_attr->store(cntl, buf, count);
+
+	return ret;
+}
+
+static const struct sysfs_ops wcd_cntl_sysfs_ops = {
+	.show = wcd_cntl_sysfs_show,
+	.store = wcd_cntl_sysfs_store,
+};
+
+static struct kobj_type wcd_cntl_ktype = {
+	.sysfs_ops = &wcd_cntl_sysfs_ops,
+};
+
+static void wcd_cntl_change_online_state(struct wcd_dsp_cntl *cntl,
+					 u8 online)
+{
+	struct wdsp_ssr_entry *ssr_entry = &cntl->ssr_entry;
+	unsigned long ret;
+
+	WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+	ssr_entry->offline = !online;
+	/* Make sure the write is complete */
+	wmb();
+	ret = xchg(&ssr_entry->offline_change, 1);
+	wake_up_interruptible(&ssr_entry->offline_poll_wait);
+	dev_dbg(cntl->codec->dev,
+		"%s: requested %u, offline %u offline_change %u, ret = %ldn",
+		__func__, online, ssr_entry->offline,
+		ssr_entry->offline_change, ret);
+	WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+}
+
+static ssize_t wdsp_ssr_entry_read(struct snd_info_entry *entry,
+				   void *file_priv_data, struct file *file,
+				   char __user *buf, size_t count, loff_t pos)
+{
+	int len = 0;
+	char buffer[WCD_PROCFS_ENTRY_MAX_LEN];
+	struct wcd_dsp_cntl *cntl;
+	struct wdsp_ssr_entry *ssr_entry;
+	ssize_t ret;
+	u8 offline;
+
+	cntl = (struct wcd_dsp_cntl *) entry->private_data;
+	if (!cntl) {
+		pr_err("%s: Invalid private data for SSR procfs entry\n",
+		       __func__);
+		return -EINVAL;
+	}
+
+	ssr_entry = &cntl->ssr_entry;
+
+	WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+	offline = ssr_entry->offline;
+	/* Make sure the read is complete */
+	rmb();
+	dev_dbg(cntl->codec->dev, "%s: offline = %s\n", __func__,
+		offline ? "true" : "false");
+	len = snprintf(buffer, sizeof(buffer), "%s\n",
+		       offline ? "OFFLINE" : "ONLINE");
+	ret = simple_read_from_buffer(buf, count, &pos, buffer, len);
+	WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+
+	return ret;
+}
+
+static unsigned int wdsp_ssr_entry_poll(struct snd_info_entry *entry,
+					void *private_data, struct file *file,
+					poll_table *wait)
+{
+	struct wcd_dsp_cntl *cntl;
+	struct wdsp_ssr_entry *ssr_entry;
+	unsigned int ret = 0;
+
+	if (!entry || !entry->private_data) {
+		pr_err("%s: %s is NULL\n", __func__,
+		       (!entry) ? "entry" : "private_data");
+		return -EINVAL;
+	}
+
+	cntl = (struct wcd_dsp_cntl *) entry->private_data;
+	ssr_entry = &cntl->ssr_entry;
+
+	dev_dbg(cntl->codec->dev, "%s: Poll wait, offline = %u\n",
+		__func__, ssr_entry->offline);
+	poll_wait(file, &ssr_entry->offline_poll_wait, wait);
+	dev_dbg(cntl->codec->dev, "%s: Woken up Poll wait, offline = %u\n",
+		__func__, ssr_entry->offline);
+
+	WCD_CNTL_MUTEX_LOCK(cntl->codec, cntl->ssr_mutex);
+	if (xchg(&ssr_entry->offline_change, 0))
+		ret = POLLIN | POLLPRI | POLLRDNORM;
+	dev_dbg(cntl->codec->dev, "%s: ret (%d) from poll_wait\n",
+		__func__, ret);
+	WCD_CNTL_MUTEX_UNLOCK(cntl->codec, cntl->ssr_mutex);
+
+	return ret;
+}
+
+static struct snd_info_entry_ops wdsp_ssr_entry_ops = {
+	.read = wdsp_ssr_entry_read,
+	.poll = wdsp_ssr_entry_poll,
+};
+
+static int wcd_cntl_cpe_fll_calibrate(struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret = 0, retry = 0;
+	u8 cal_lsb, cal_msb;
+	u8 lock_det;
+
+	/* Make sure clocks are gated */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+			    0x05, 0x00);
+
+	/* Enable CPE FLL reference clock */
+	snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+			    0x80, 0x80);
+
+	snd_soc_update_bits(codec, WCD934X_CPE_FLL_USER_CTL_5,
+			    0xF3, 0x13);
+	snd_soc_write(codec, WCD934X_CPE_FLL_L_VAL_CTL_0, 0x50);
+
+	/* Disable CPAR reset and Enable CPAR clk */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL,
+			    0x02, 0x02);
+
+	/* Write calibration l-value based on cdc clk rate */
+	if (cntl->clk_rate == 9600000) {
+		cal_lsb = 0x6d;
+		cal_msb = 0x00;
+	} else {
+		cal_lsb = 0x56;
+		cal_msb = 0x00;
+	}
+	snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_6, cal_lsb);
+	snd_soc_write(codec, WCD934X_CPE_FLL_USER_CTL_7, cal_msb);
+
+	/* FLL mode to follow power up sequence */
+	snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+			    0x60, 0x00);
+
+	/* HW controlled CPE FLL */
+	snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+			    0x80, 0x80);
+
+	/* Force on CPE FLL */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+			    0x04, 0x04);
+
+	do {
+		/* Time for FLL calibration to complete */
+		usleep_range(1000, 1100);
+		lock_det = snd_soc_read(codec, WCD934X_CPE_FLL_STATUS_3);
+		retry++;
+	} while (!(lock_det & 0x01) &&
+		 retry <= WCD_CPE_FLL_MAX_RETRIES);
+
+	if (!(lock_det & 0x01)) {
+		dev_err(codec->dev, "%s: lock detect not set, 0x%02x\n",
+			__func__, lock_det);
+		ret = -EIO;
+		goto err_lock_det;
+	}
+
+	snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+			    0x60, 0x20);
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+			    0x04, 0x00);
+	return ret;
+
+err_lock_det:
+	/* Undo the register settings */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+			    0x04, 0x00);
+	snd_soc_update_bits(codec, WCD934X_CPE_FLL_FLL_MODE,
+			    0x80, 0x00);
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL,
+			    0x02, 0x00);
+	return ret;
+}
+
+static void wcd_cntl_config_cpar(struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	u8 nom_lo, nom_hi, svs2_lo, svs2_hi;
+
+	/* Configure CPAR */
+	nom_hi = svs2_hi = 0;
+	if (cntl->clk_rate == 9600000) {
+		nom_lo = 0x90;
+		svs2_lo = 0x50;
+	} else {
+		nom_lo = 0x70;
+		svs2_lo = 0x3e;
+	}
+
+	snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_LOW, nom_lo);
+	snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_NOM_HIGH, nom_hi);
+	snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_LOW, svs2_lo);
+	snd_soc_write(codec, WCD934X_TEST_DEBUG_LVAL_SVS_SVS2_HIGH, svs2_hi);
+
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_PWR_CPEFLL_CTL,
+			    0x03, 0x03);
+}
+
+static int wcd_cntl_cpe_fll_ctrl(struct wcd_dsp_cntl *cntl,
+				 bool enable)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret = 0;
+
+	if (enable) {
+		ret = wcd_cntl_cpe_fll_calibrate(cntl);
+		if (ret < 0) {
+			dev_err(codec->dev,
+				"%s: cpe_fll_cal failed, err = %d\n",
+				__func__, ret);
+			goto done;
+		}
+
+		wcd_cntl_config_cpar(cntl);
+
+		/* Enable AHB CLK and CPE CLK*/
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+				    0x05, 0x05);
+	} else {
+		/* Disable AHB CLK and CPE CLK */
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+				    0x05, 0x00);
+		/* Reset the CPAR mode for CPE FLL */
+		snd_soc_write(codec, WCD934X_CPE_FLL_FLL_MODE, 0x20);
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CFG,
+				    0x04, 0x00);
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_CPAR_CTL,
+				    0x02, 0x00);
+	}
+done:
+	return ret;
+}
+
+static int wcd_cntl_clocks_enable(struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret;
+
+	WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex);
+	/* Enable codec clock */
+	if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en)
+		ret = cntl->cdc_cb->cdc_clk_en(codec, true);
+	else
+		ret = -EINVAL;
+
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Failed to enable cdc clk, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	/* Configure and Enable CPE FLL clock */
+	ret = wcd_cntl_cpe_fll_ctrl(cntl, true);
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Failed to enable cpe clk, err = %d\n",
+			__func__, ret);
+		goto err_cpe_clk;
+	}
+	cntl->is_clk_enabled = true;
+
+	/* Ungate the CPR clock  */
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x00);
+done:
+	WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex);
+	return ret;
+
+err_cpe_clk:
+	if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en)
+		cntl->cdc_cb->cdc_clk_en(codec, false);
+
+	WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex);
+	return ret;
+}
+
+static int wcd_cntl_clocks_disable(struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret = 0;
+
+	WCD_CNTL_MUTEX_LOCK(codec, cntl->clk_mutex);
+	if (!cntl->is_clk_enabled) {
+		dev_info(codec->dev, "%s: clocks already disabled\n",
+			__func__);
+		goto done;
+	}
+
+	/* Gate the CPR clock  */
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE, 0x10, 0x10);
+
+	/* Disable CPE FLL clock */
+	ret = wcd_cntl_cpe_fll_ctrl(cntl, false);
+	if (ret < 0)
+		dev_err(codec->dev,
+			"%s: Failed to disable cpe clk, err = %d\n",
+			__func__, ret);
+
+	/*
+	 * Even if CPE FLL disable failed, go ahead and disable
+	 * the codec clock
+	 */
+	if (cntl->cdc_cb && cntl->cdc_cb->cdc_clk_en)
+		ret = cntl->cdc_cb->cdc_clk_en(codec, false);
+	else
+		ret = -EINVAL;
+
+	cntl->is_clk_enabled = false;
+done:
+	WCD_CNTL_MUTEX_UNLOCK(codec, cntl->clk_mutex);
+	return ret;
+}
+
+static void wcd_cntl_cpar_ctrl(struct wcd_dsp_cntl *cntl,
+			       bool enable)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+
+	if (enable)
+		snd_soc_write(codec, WCD934X_CPE_SS_CPAR_CTL, 0x03);
+	else
+		snd_soc_write(codec, WCD934X_CPE_SS_CPAR_CTL, 0x00);
+}
+
+static int wcd_cntl_enable_memory(struct wcd_dsp_cntl *cntl,
+				  enum wcd_mem_type mem_type)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int loop_cnt = 0;
+	u8 status;
+	int ret = 0;
+
+
+	switch (mem_type) {
+
+	case WCD_MEM_TYPE_ALWAYS_ON:
+
+		/* 512KB of always on region */
+		wcd9xxx_slim_write_repeat(wcd9xxx,
+				WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0,
+				ARRAY_SIZE(mem_enable_values),
+				mem_enable_values);
+		wcd9xxx_slim_write_repeat(wcd9xxx,
+				WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1,
+				ARRAY_SIZE(mem_enable_values),
+				mem_enable_values);
+		break;
+
+	case WCD_MEM_TYPE_SWITCHABLE:
+
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,
+				    0x04, 0x00);
+		snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL,
+				    0x80, 0x80);
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,
+				    0x01, 0x01);
+		do {
+			loop_cnt++;
+			/* Time to enable the power domain for memory */
+			usleep_range(100, 150);
+			status = snd_soc_read(codec,
+					WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL);
+		} while ((status & 0x02) != 0x02 &&
+			  loop_cnt != WCD_MEM_ENABLE_MAX_RETRIES);
+
+		if ((status & 0x02) != 0x02) {
+			dev_err(cntl->codec->dev,
+				"%s: power domain not enabled, status = 0x%02x\n",
+				__func__, status);
+			ret = -EIO;
+			goto done;
+		}
+
+		/* Rest of the memory */
+		wcd9xxx_slim_write_repeat(wcd9xxx,
+				WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2,
+				ARRAY_SIZE(mem_enable_values),
+				mem_enable_values);
+		wcd9xxx_slim_write_repeat(wcd9xxx,
+				WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3,
+				ARRAY_SIZE(mem_enable_values),
+				mem_enable_values);
+
+		snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN,
+			      0x05);
+		break;
+
+	default:
+		dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n",
+			__func__, mem_type);
+		ret = -EINVAL;
+		break;
+	}
+done:
+	/* Make sure Deep sleep of memories is enabled for all banks */
+	snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF);
+	snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F);
+
+	return ret;
+}
+
+static void wcd_cntl_disable_memory(struct wcd_dsp_cntl *cntl,
+				    enum wcd_mem_type mem_type)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	u8 val;
+
+	switch (mem_type) {
+	case WCD_MEM_TYPE_ALWAYS_ON:
+		snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_1,
+			      0xFF);
+		snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_0,
+			      0xFF);
+		break;
+	case WCD_MEM_TYPE_SWITCHABLE:
+		snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_3,
+			      0xFF);
+		snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_SHUTDOWN_2,
+			      0xFF);
+		snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_DRAM1_SHUTDOWN,
+			      0x07);
+
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL,
+				    0x01, 0x00);
+		val = snd_soc_read(codec, WCD934X_CPE_SS_SOC_SW_COLLAPSE_CTL);
+		if (val & 0x02)
+			dev_err(codec->dev,
+				"%s: Disable switchable failed, val = 0x%02x",
+				__func__, val);
+
+		snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_MEM_CTRL,
+				    0x80, 0x00);
+		break;
+	default:
+		dev_err(cntl->codec->dev, "%s: Invalid mem_type %d\n",
+			__func__, mem_type);
+		break;
+	}
+
+	snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_0, 0xFF);
+	snd_soc_write(codec, WCD934X_CPE_SS_PWR_CPE_SYSMEM_DEEPSLP_1, 0x0F);
+}
+
+static void wcd_cntl_do_shutdown(struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+
+	/* Disable WDOG */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+			    0x3F, 0x01);
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+			    0x04, 0x00);
+
+	/* Put WDSP in reset state */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+			    0x02, 0x00);
+
+	/* If DSP transitions from boot to shutdown, then vote for SVS */
+	if (cntl->is_wdsp_booted)
+		cntl->cdc_cb->cdc_vote_svs(codec, true);
+	cntl->is_wdsp_booted = false;
+}
+
+static int wcd_cntl_do_boot(struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret = 0;
+
+	/*
+	 * Debug mode is set from debugfs file node. If debug_mode
+	 * is set, then do not configure the watchdog timer. This
+	 * will be required for debugging the DSP firmware.
+	 */
+	if (cntl->debug_mode) {
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+				    0x3F, 0x01);
+		snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+				    0x04, 0x00);
+	} else {
+		snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+				    0x04, 0x04);
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+				    0x3F, 0x21);
+	}
+
+	/* Make sure all the error interrupts are cleared */
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0A, 0xFF);
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_CLEAR_0B, 0xFF);
+
+	reinit_completion(&cntl->boot_complete);
+
+	/* Remove WDSP out of reset */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_CPE_CTL,
+			    0x02, 0x02);
+
+	/*
+	 * In debug mode, DSP may not boot up normally,
+	 * wait indefinitely for DSP to boot.
+	 */
+	if (cntl->debug_mode) {
+		wait_for_completion(&cntl->boot_complete);
+		dev_dbg(codec->dev, "%s: WDSP booted in dbg mode\n", __func__);
+		cntl->is_wdsp_booted = true;
+		goto done;
+	}
+
+	/* Boot in normal mode */
+	ret = wait_for_completion_timeout(&cntl->boot_complete,
+				msecs_to_jiffies(WCD_DSP_BOOT_TIMEOUT_MS));
+	if (!ret) {
+		dev_err(codec->dev, "%s: WDSP boot timed out\n",
+			__func__);
+		ret = -ETIMEDOUT;
+		goto err_boot;
+	} else {
+		/*
+		 * Re-initialize the return code to 0, as in success case,
+		 * it will hold the remaining time for completion timeout
+		 */
+		ret = 0;
+	}
+
+	dev_dbg(codec->dev, "%s: WDSP booted in normal mode\n", __func__);
+	cntl->is_wdsp_booted = true;
+
+	/* Enable WDOG */
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_WDOG_CFG,
+			    0x10, 0x10);
+done:
+	/* If dsp booted up, then remove vote on SVS */
+	if (cntl->is_wdsp_booted)
+		cntl->cdc_cb->cdc_vote_svs(codec, false);
+
+	return ret;
+err_boot:
+	/* call shutdown to perform cleanup */
+	wcd_cntl_do_shutdown(cntl);
+	return ret;
+}
+
+static irqreturn_t wcd_cntl_ipc_irq(int irq, void *data)
+{
+	struct wcd_dsp_cntl *cntl = data;
+	int ret;
+
+	complete(&cntl->boot_complete);
+
+	if (cntl->m_dev && cntl->m_ops &&
+	    cntl->m_ops->signal_handler)
+		ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_IPC1_INTR,
+						  NULL);
+	else
+		ret = -EINVAL;
+
+	if (ret < 0)
+		dev_err(cntl->codec->dev,
+			"%s: Failed to handle irq %d\n", __func__, irq);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd_cntl_err_irq(int irq, void *data)
+{
+	struct wcd_dsp_cntl *cntl = data;
+	struct snd_soc_codec *codec = cntl->codec;
+	struct wdsp_err_signal_arg arg;
+	u16 status = 0;
+	u8 reg_val;
+	int ret = 0;
+
+	reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0A);
+	status = status | reg_val;
+
+	reg_val = snd_soc_read(codec, WCD934X_CPE_SS_SS_ERROR_INT_STATUS_0B);
+	status = status | (reg_val << 8);
+
+	dev_info(codec->dev, "%s: error interrupt status = 0x%x\n",
+		__func__, status);
+
+	if ((status & cntl->irqs.fatal_irqs) &&
+	    (cntl->m_dev && cntl->m_ops && cntl->m_ops->signal_handler)) {
+		arg.mem_dumps_enabled = cntl->ramdump_enable;
+		arg.remote_start_addr = WCD_934X_RAMDUMP_START_ADDR;
+		arg.dump_size = WCD_934X_RAMDUMP_SIZE;
+		ret = cntl->m_ops->signal_handler(cntl->m_dev, WDSP_ERR_INTR,
+						  &arg);
+		if (ret < 0)
+			dev_err(cntl->codec->dev,
+				"%s: Failed to handle fatal irq 0x%x\n",
+				__func__, status & cntl->irqs.fatal_irqs);
+		wcd_cntl_change_online_state(cntl, 0);
+	} else {
+		dev_err(cntl->codec->dev, "%s: Invalid signal_handler\n",
+			__func__);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int wcd_control_handler(struct device *dev, void *priv_data,
+			       enum wdsp_event_type event, void *data)
+{
+	struct wcd_dsp_cntl *cntl = priv_data;
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret = 0;
+
+	switch (event) {
+	case WDSP_EVENT_POST_INIT:
+	case WDSP_EVENT_POST_DLOAD_CODE:
+	case WDSP_EVENT_DLOAD_FAILED:
+	case WDSP_EVENT_POST_SHUTDOWN:
+
+		if (event == WDSP_EVENT_POST_DLOAD_CODE)
+			/* Mark DSP online since code download is complete */
+			wcd_cntl_change_online_state(cntl, 1);
+
+		/* Disable CPAR */
+		wcd_cntl_cpar_ctrl(cntl, false);
+		/* Disable all the clocks */
+		ret = wcd_cntl_clocks_disable(cntl);
+		if (ret < 0)
+			dev_err(codec->dev,
+				"%s: Failed to disable clocks, err = %d\n",
+				__func__, ret);
+		break;
+
+	case WDSP_EVENT_PRE_DLOAD_DATA:
+	case WDSP_EVENT_PRE_DLOAD_CODE:
+
+		/* Enable all the clocks */
+		ret = wcd_cntl_clocks_enable(cntl);
+		if (ret < 0) {
+			dev_err(codec->dev,
+				"%s: Failed to enable clocks, err = %d\n",
+				__func__, ret);
+			goto done;
+		}
+
+		/* Enable CPAR */
+		wcd_cntl_cpar_ctrl(cntl, true);
+
+		if (event == WDSP_EVENT_PRE_DLOAD_CODE)
+			wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_ALWAYS_ON);
+		else if (event == WDSP_EVENT_PRE_DLOAD_DATA)
+			wcd_cntl_enable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE);
+		break;
+
+	case WDSP_EVENT_DO_BOOT:
+
+		ret = wcd_cntl_do_boot(cntl);
+		if (ret < 0)
+			dev_err(codec->dev,
+				"%s: WDSP boot failed, err = %d\n",
+				__func__, ret);
+		break;
+
+	case WDSP_EVENT_DO_SHUTDOWN:
+
+		wcd_cntl_do_shutdown(cntl);
+		wcd_cntl_disable_memory(cntl, WCD_MEM_TYPE_SWITCHABLE);
+		break;
+
+	default:
+		dev_dbg(codec->dev, "%s: unhandled event %d\n",
+			__func__, event);
+	}
+
+done:
+	return ret;
+}
+
+static int wcd_cntl_sysfs_init(char *dir, struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+	int ret = 0;
+
+	ret = kobject_init_and_add(&cntl->wcd_kobj, &wcd_cntl_ktype,
+				   kernel_kobj, dir);
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Failed to add kobject %s, err = %d\n",
+			__func__, dir, ret);
+		goto done;
+	}
+
+	ret = sysfs_create_file(&cntl->wcd_kobj, &cntl_attr_boot.attr);
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Failed to add wdsp_boot sysfs entry to %s\n",
+			__func__, dir);
+		goto fail_create_file;
+	}
+
+	return ret;
+
+fail_create_file:
+	kobject_put(&cntl->wcd_kobj);
+done:
+	return ret;
+}
+
+static void wcd_cntl_sysfs_remove(struct wcd_dsp_cntl *cntl)
+{
+	sysfs_remove_file(&cntl->wcd_kobj, &cntl_attr_boot.attr);
+	kobject_put(&cntl->wcd_kobj);
+}
+
+static void wcd_cntl_debugfs_init(char *dir, struct wcd_dsp_cntl *cntl)
+{
+	struct snd_soc_codec *codec = cntl->codec;
+
+	cntl->entry = debugfs_create_dir(dir, NULL);
+	if (IS_ERR_OR_NULL(dir)) {
+		dev_err(codec->dev, "%s debugfs_create_dir failed for %s\n",
+			__func__, dir);
+		goto done;
+	}
+
+	debugfs_create_u32("debug_mode", 0644,
+			   cntl->entry, &cntl->debug_mode);
+	debugfs_create_bool("ramdump_enable", 0644,
+			    cntl->entry, &cntl->ramdump_enable);
+done:
+	return;
+}
+
+static void wcd_cntl_debugfs_remove(struct wcd_dsp_cntl *cntl)
+{
+	if (cntl)
+		debugfs_remove(cntl->entry);
+}
+
+static int wcd_miscdev_release(struct inode *inode, struct file *filep)
+{
+	struct wcd_dsp_cntl *cntl = container_of(filep->private_data,
+						 struct wcd_dsp_cntl, miscdev);
+	if (!cntl->m_dev || !cntl->m_ops ||
+	    !cntl->m_ops->vote_for_dsp) {
+		dev_err(cntl->codec->dev,
+			"%s: DSP not ready to boot\n", __func__);
+		return -EINVAL;
+	}
+
+	/* Make sure the DSP users goes to zero upon closing dev node */
+	while (cntl->boot_reqs > 0) {
+		cntl->m_ops->vote_for_dsp(cntl->m_dev, false);
+		cntl->boot_reqs--;
+	}
+
+	return 0;
+}
+
+static ssize_t wcd_miscdev_write(struct file *filep, const char __user *ubuf,
+				 size_t count, loff_t *pos)
+{
+	struct wcd_dsp_cntl *cntl = container_of(filep->private_data,
+						 struct wcd_dsp_cntl, miscdev);
+	char val[count];
+	bool vote;
+	int ret = 0;
+
+	if (count == 0 || count > 2) {
+		pr_err("%s: Invalid count = %zd\n", __func__, count);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = copy_from_user(val, ubuf, count);
+	if (ret < 0) {
+		dev_err(cntl->codec->dev,
+			"%s: copy_from_user failed, err = %d\n",
+			__func__, ret);
+		ret = -EFAULT;
+		goto done;
+	}
+
+	if (val[0] == '1') {
+		cntl->boot_reqs++;
+		vote = true;
+	} else if (val[0] == '0') {
+		if (cntl->boot_reqs == 0) {
+			dev_err(cntl->codec->dev,
+				"%s: WDSP already disabled\n", __func__);
+			ret = -EINVAL;
+			goto done;
+		}
+		cntl->boot_reqs--;
+		vote = false;
+	} else {
+		dev_err(cntl->codec->dev, "%s: Invalid value %s\n",
+			__func__, val);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	dev_dbg(cntl->codec->dev,
+		"%s: booted = %s, ref_cnt = %d, vote = %s\n",
+		__func__, cntl->is_wdsp_booted ? "true" : "false",
+		cntl->boot_reqs, vote ? "true" : "false");
+
+	if (cntl->m_dev && cntl->m_ops &&
+	    cntl->m_ops->vote_for_dsp)
+		ret = cntl->m_ops->vote_for_dsp(cntl->m_dev, vote);
+	else
+		ret = -EINVAL;
+done:
+	if (ret)
+		return ret;
+	else
+		return count;
+}
+
+static const struct file_operations wcd_miscdev_fops = {
+	.write = wcd_miscdev_write,
+	.release = wcd_miscdev_release,
+};
+
+static int wcd_cntl_miscdev_create(struct wcd_dsp_cntl *cntl)
+{
+	snprintf(cntl->miscdev_name, ARRAY_SIZE(cntl->miscdev_name),
+		"wcd_dsp%u_control", cntl->dsp_instance);
+	cntl->miscdev.minor = MISC_DYNAMIC_MINOR;
+	cntl->miscdev.name = cntl->miscdev_name;
+	cntl->miscdev.fops = &wcd_miscdev_fops;
+	cntl->miscdev.parent = cntl->codec->dev;
+
+	return misc_register(&cntl->miscdev);
+}
+
+static void wcd_cntl_miscdev_destroy(struct wcd_dsp_cntl *cntl)
+{
+	misc_deregister(&cntl->miscdev);
+}
+
+static int wcd_control_init(struct device *dev, void *priv_data)
+{
+	struct wcd_dsp_cntl *cntl = priv_data;
+	struct snd_soc_codec *codec = cntl->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+	int ret;
+	bool err_irq_requested = false;
+
+	ret = wcd9xxx_request_irq(core_res,
+				  cntl->irqs.cpe_ipc1_irq,
+				  wcd_cntl_ipc_irq, "CPE IPC1",
+				  cntl);
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: Failed to request cpe ipc irq, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	/* Unmask the fatal irqs */
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A,
+		      ~(cntl->irqs.fatal_irqs & 0xFF));
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B,
+		      ~((cntl->irqs.fatal_irqs >> 8) & 0xFF));
+
+	/*
+	 * CPE ERR irq is used only for error reporting from WCD DSP,
+	 * even if this request fails, DSP can be function normally.
+	 * Continuing with init even if the CPE ERR irq request fails.
+	 */
+	if (wcd9xxx_request_irq(core_res, cntl->irqs.cpe_err_irq,
+				wcd_cntl_err_irq, "CPE ERR", cntl))
+		dev_info(codec->dev, "%s: Failed request_irq(cpe_err_irq)",
+			__func__);
+	else
+		err_irq_requested = true;
+
+
+	/* Enable all the clocks */
+	ret = wcd_cntl_clocks_enable(cntl);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: Failed to enable clocks, err = %d\n",
+			__func__, ret);
+		goto err_clk_enable;
+	}
+	wcd_cntl_cpar_ctrl(cntl, true);
+
+	return 0;
+
+err_clk_enable:
+	/* Mask all error interrupts */
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF);
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF);
+
+	/* Free the irq's requested */
+	wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl);
+
+	if (err_irq_requested)
+		wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl);
+done:
+	return ret;
+}
+
+static int wcd_control_deinit(struct device *dev, void *priv_data)
+{
+	struct wcd_dsp_cntl *cntl = priv_data;
+	struct snd_soc_codec *codec = cntl->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+
+	wcd_cntl_clocks_disable(cntl);
+	wcd_cntl_cpar_ctrl(cntl, false);
+
+	/* Mask all error interrupts */
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0A, 0xFF);
+	snd_soc_write(codec, WCD934X_CPE_SS_SS_ERROR_INT_MASK_0B, 0xFF);
+
+	/* Free the irq's requested */
+	wcd9xxx_free_irq(core_res, cntl->irqs.cpe_err_irq, cntl);
+	wcd9xxx_free_irq(core_res, cntl->irqs.cpe_ipc1_irq, cntl);
+
+	return 0;
+}
+
+static struct wdsp_cmpnt_ops control_ops = {
+	.init = wcd_control_init,
+	.deinit = wcd_control_deinit,
+	.event_handler = wcd_control_handler,
+};
+
+static int wcd_ctrl_component_bind(struct device *dev,
+				   struct device *master,
+				   void *data)
+{
+	struct wcd_dsp_cntl *cntl;
+	struct snd_soc_codec *codec;
+	struct snd_card *card;
+	struct snd_info_entry *entry;
+	char proc_name[WCD_PROCFS_ENTRY_MAX_LEN];
+	char wcd_cntl_dir_name[WCD_CNTL_DIR_NAME_LEN_MAX];
+	int ret = 0;
+
+	if (!dev || !master || !data) {
+		pr_err("%s: Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	cntl = tavil_get_wcd_dsp_cntl(dev);
+	if (!cntl) {
+		dev_err(dev, "%s: Failed to get cntl reference\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	cntl->m_dev = master;
+	cntl->m_ops = data;
+
+	if (!cntl->m_ops->register_cmpnt_ops) {
+		dev_err(dev, "%s: invalid master callback register_cmpnt_ops\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cntl->m_ops->register_cmpnt_ops(master, dev, cntl, &control_ops);
+	if (ret) {
+		dev_err(dev, "%s: register_cmpnt_ops failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ret = wcd_cntl_miscdev_create(cntl);
+	if (ret < 0) {
+		dev_err(dev, "%s: misc dev register failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	snprintf(wcd_cntl_dir_name, WCD_CNTL_DIR_NAME_LEN_MAX,
+		 "%s%d", "wdsp", cntl->dsp_instance);
+	ret = wcd_cntl_sysfs_init(wcd_cntl_dir_name, cntl);
+	if (ret < 0) {
+		dev_err(dev, "%s: sysfs_init failed, err = %d\n",
+			__func__, ret);
+		goto err_sysfs_init;
+	}
+
+	wcd_cntl_debugfs_init(wcd_cntl_dir_name, cntl);
+
+	codec = cntl->codec;
+	card = codec->component.card->snd_card;
+	snprintf(proc_name, WCD_PROCFS_ENTRY_MAX_LEN, "%s%d%s", "cpe",
+		 cntl->dsp_instance, "_state");
+	entry = snd_info_create_card_entry(card, proc_name, card->proc_root);
+	if (!entry) {
+		/* Do not treat this as Fatal error */
+		dev_err(dev, "%s: Failed to create procfs entry %s\n",
+			__func__, proc_name);
+		goto err_sysfs_init;
+	}
+
+	cntl->ssr_entry.entry = entry;
+	cntl->ssr_entry.offline = 1;
+	entry->size = WCD_PROCFS_ENTRY_MAX_LEN;
+	entry->content = SNDRV_INFO_CONTENT_DATA;
+	entry->c.ops = &wdsp_ssr_entry_ops;
+	entry->private_data = cntl;
+	ret = snd_info_register(entry);
+	if (ret < 0) {
+		dev_err(dev, "%s: Failed to register entry %s, err = %d\n",
+			__func__, proc_name, ret);
+		snd_info_free_entry(entry);
+		/* Let bind still happen even if creating the entry failed */
+		ret = 0;
+	}
+done:
+	return ret;
+
+err_sysfs_init:
+	wcd_cntl_miscdev_destroy(cntl);
+	return ret;
+}
+
+static void wcd_ctrl_component_unbind(struct device *dev,
+				      struct device *master,
+				      void *data)
+{
+	struct wcd_dsp_cntl *cntl;
+
+	if (!dev) {
+		pr_err("%s: Invalid device\n", __func__);
+		return;
+	}
+
+	cntl = tavil_get_wcd_dsp_cntl(dev);
+	if (!cntl) {
+		dev_err(dev, "%s: Failed to get cntl reference\n",
+			__func__);
+		return;
+	}
+
+	cntl->m_dev = NULL;
+	cntl->m_ops = NULL;
+
+	/* Remove the sysfs entries */
+	wcd_cntl_sysfs_remove(cntl);
+
+	/* Remove the debugfs entries */
+	wcd_cntl_debugfs_remove(cntl);
+
+	/* Remove the misc device */
+	wcd_cntl_miscdev_destroy(cntl);
+}
+
+static const struct component_ops wcd_ctrl_component_ops = {
+	.bind = wcd_ctrl_component_bind,
+	.unbind = wcd_ctrl_component_unbind,
+};
+
+/*
+ * wcd_dsp_ssr_event: handle the SSR event raised by caller.
+ * @cntl: Handle to the wcd_dsp_cntl structure
+ * @event: The SSR event to be handled
+ *
+ * Notifies the manager driver about the SSR event.
+ * Returns 0 on success and negative error code on error.
+ */
+int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event)
+{
+	int ret = 0;
+
+	if (!cntl) {
+		pr_err("%s: Invalid handle to control\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!cntl->m_dev || !cntl->m_ops || !cntl->m_ops->signal_handler) {
+		dev_err(cntl->codec->dev,
+			"%s: Invalid signal_handler callback\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case WCD_CDC_DOWN_EVENT:
+		ret = cntl->m_ops->signal_handler(cntl->m_dev,
+						  WDSP_CDC_DOWN_SIGNAL,
+						  NULL);
+		if (ret < 0)
+			dev_err(cntl->codec->dev,
+				"%s: WDSP_CDC_DOWN_SIGNAL failed, err = %d\n",
+				__func__, ret);
+		wcd_cntl_change_online_state(cntl, 0);
+		break;
+	case WCD_CDC_UP_EVENT:
+		ret = cntl->m_ops->signal_handler(cntl->m_dev,
+						  WDSP_CDC_UP_SIGNAL,
+						  NULL);
+		if (ret < 0)
+			dev_err(cntl->codec->dev,
+				"%s: WDSP_CDC_UP_SIGNAL failed, err = %d\n",
+				__func__, ret);
+		break;
+	default:
+		dev_err(cntl->codec->dev, "%s: Invalid event %d\n",
+			__func__, event);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd_dsp_ssr_event);
+
+/*
+ * wcd_dsp_cntl_init: Initialize the wcd-dsp control
+ * @codec: pointer to the codec handle
+ * @params: Parameters required to initialize wcd-dsp control
+ *
+ * This API is expected to be invoked by the codec driver and
+ * provide information essential for the wcd dsp control to
+ * configure and initialize the dsp
+ */
+void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
+		       struct wcd_dsp_params *params,
+		       struct wcd_dsp_cntl **cntl)
+{
+	struct wcd_dsp_cntl *control;
+	int ret;
+
+	if (!codec || !params) {
+		pr_err("%s: Invalid handle to %s\n", __func__,
+		       (!codec) ? "codec" : "params");
+		*cntl = NULL;
+		return;
+	}
+
+	if (*cntl) {
+		pr_err("%s: cntl is non NULL, maybe already initialized ?\n",
+			__func__);
+		return;
+	}
+
+	if (!params->cb || !params->cb->cdc_clk_en ||
+	    !params->cb->cdc_vote_svs) {
+		dev_err(codec->dev,
+			"%s: clk_en and vote_svs callbacks must be provided\n",
+			__func__);
+		return;
+	}
+
+	control = kzalloc(sizeof(*control), GFP_KERNEL);
+	if (!(control))
+		return;
+
+	control->codec = codec;
+	control->clk_rate = params->clk_rate;
+	control->cdc_cb = params->cb;
+	control->dsp_instance = params->dsp_instance;
+	memcpy(&control->irqs, &params->irqs, sizeof(control->irqs));
+	init_completion(&control->boot_complete);
+	mutex_init(&control->clk_mutex);
+	mutex_init(&control->ssr_mutex);
+	init_waitqueue_head(&control->ssr_entry.offline_poll_wait);
+
+	/*
+	 * The default state of WDSP is in SVS mode.
+	 * Vote for SVS now, the vote will be removed only
+	 * after DSP is booted up.
+	 */
+	control->cdc_cb->cdc_vote_svs(codec, true);
+
+	/*
+	 * If this is the last component needed by master to be ready,
+	 * then component_bind will be called within the component_add.
+	 * Hence, the data pointer should be assigned before component_add,
+	 * so that we can access it during this component's bind call.
+	 */
+	*cntl = control;
+	ret = component_add(codec->dev, &wcd_ctrl_component_ops);
+	if (ret) {
+		dev_err(codec->dev, "%s: component_add failed, err = %d\n",
+			__func__, ret);
+		kfree(*cntl);
+		*cntl = NULL;
+	}
+}
+EXPORT_SYMBOL(wcd_dsp_cntl_init);
+
+/*
+ * wcd_dsp_cntl_deinit: De-initialize the wcd-dsp control
+ * @cntl: The struct wcd_dsp_cntl to de-initialize
+ *
+ * This API is intended to be invoked by the codec driver
+ * to de-initialize the wcd dsp control
+ */
+void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl)
+{
+	struct wcd_dsp_cntl *control = *cntl;
+	struct snd_soc_codec *codec;
+
+	/* If control is NULL, there is nothing to de-initialize */
+	if (!control)
+		return;
+	codec = control->codec;
+
+	/*
+	 * Calling shutdown will cleanup all register states,
+	 * irrespective of DSP was booted up or not.
+	 */
+	wcd_cntl_do_shutdown(control);
+	wcd_cntl_disable_memory(control, WCD_MEM_TYPE_SWITCHABLE);
+	wcd_cntl_disable_memory(control, WCD_MEM_TYPE_ALWAYS_ON);
+
+	component_del(codec->dev, &wcd_ctrl_component_ops);
+
+	mutex_destroy(&control->clk_mutex);
+	mutex_destroy(&control->ssr_mutex);
+	kfree(*cntl);
+	*cntl = NULL;
+}
+EXPORT_SYMBOL(wcd_dsp_cntl_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
new file mode 100644
index 0000000..e934638
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-dsp-cntl.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __WCD934X_DSP_CNTL_H__
+#define __WCD934X_DSP_CNTL_H__
+
+#include <sound/soc.h>
+#include <sound/wcd-dsp-mgr.h>
+
+enum cdc_ssr_event {
+	WCD_CDC_DOWN_EVENT,
+	WCD_CDC_UP_EVENT,
+};
+
+struct wcd_dsp_cdc_cb {
+	/* Callback to enable codec clock */
+	int (*cdc_clk_en)(struct snd_soc_codec *, bool);
+	/* Callback to vote and unvote for SVS2 mode */
+	void (*cdc_vote_svs)(struct snd_soc_codec *, bool);
+};
+
+struct wcd_dsp_irq_info {
+	/* IPC interrupt */
+	int cpe_ipc1_irq;
+
+	/* CPE error summary interrupt */
+	int cpe_err_irq;
+
+	/*
+	 * Bit mask to indicate which of the
+	 * error interrupts are to be considered
+	 * as fatal.
+	 */
+	u16 fatal_irqs;
+};
+
+struct wcd_dsp_params {
+	struct wcd_dsp_cdc_cb *cb;
+	struct wcd_dsp_irq_info irqs;
+
+	/* Rate at which the codec clock operates */
+	u32 clk_rate;
+
+	/*
+	 * Represents the dsp instance, will be used
+	 * to create sysfs and debugfs entries with
+	 * directory wdsp<dsp-instance>
+	 */
+	u32 dsp_instance;
+};
+
+struct wdsp_ssr_entry {
+	u8 offline;
+	u8 offline_change;
+	wait_queue_head_t offline_poll_wait;
+	struct snd_info_entry *entry;
+};
+
+struct wcd_dsp_cntl {
+	/* Handle to codec */
+	struct snd_soc_codec *codec;
+
+	/* Clk rate of the codec clock */
+	u32 clk_rate;
+
+	/* Callbacks to codec driver */
+	const struct wcd_dsp_cdc_cb *cdc_cb;
+
+	/* Completion to indicate WDSP boot done */
+	struct completion boot_complete;
+
+	struct wcd_dsp_irq_info irqs;
+	u32 dsp_instance;
+
+	/* Sysfs entries related */
+	int boot_reqs;
+	struct kobject wcd_kobj;
+
+	/* Debugfs related */
+	struct dentry *entry;
+	u32 debug_mode;
+	bool ramdump_enable;
+
+	/* WDSP manager drivers data */
+	struct device *m_dev;
+	struct wdsp_mgr_ops *m_ops;
+
+	/* clk related */
+	struct mutex clk_mutex;
+	bool is_clk_enabled;
+
+	/* Keep track of WDSP boot status */
+	bool is_wdsp_booted;
+
+	/* SSR related */
+	struct wdsp_ssr_entry ssr_entry;
+	struct mutex ssr_mutex;
+
+	/* Misc device related */
+	char miscdev_name[256];
+	struct miscdevice miscdev;
+};
+
+void wcd_dsp_cntl_init(struct snd_soc_codec *codec,
+		       struct wcd_dsp_params *params,
+		       struct wcd_dsp_cntl **cntl);
+void wcd_dsp_cntl_deinit(struct wcd_dsp_cntl **cntl);
+int wcd_dsp_ssr_event(struct wcd_dsp_cntl *cntl, enum cdc_ssr_event event);
+#endif /* end __WCD_DSP_CONTROL_H__ */
diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.c b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
new file mode 100644
index 0000000..5dbdb9a
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/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 <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include "wcd934x.h"
+#include "wcd934x-mbhc.h"
+#include "../wcdcal-hwdep.h"
+
+#define TAVIL_ZDET_SUPPORTED          true
+/* Z value defined in milliohm */
+#define TAVIL_ZDET_VAL_32             32000
+#define TAVIL_ZDET_VAL_400            400000
+#define TAVIL_ZDET_VAL_1200           1200000
+#define TAVIL_ZDET_VAL_100K           100000000
+/* Z floating defined in ohms */
+#define TAVIL_ZDET_FLOATING_IMPEDANCE 0x0FFFFFFE
+
+#define TAVIL_ZDET_NUM_MEASUREMENTS   150
+#define TAVIL_MBHC_GET_C1(c)          ((c & 0xC000) >> 14)
+#define TAVIL_MBHC_GET_X1(x)          (x & 0x3FFF)
+/* Z value compared in milliOhm */
+#define TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z) ((z > 400000) || (z < 32000))
+#define TAVIL_MBHC_ZDET_CONST         (86 * 16384)
+#define TAVIL_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",
+			  WCD934X_ANA_MBHC_MECH, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_DET_EN",
+			  WCD934X_ANA_MBHC_MECH, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MECH_DETECTION_TYPE",
+			  WCD934X_ANA_MBHC_MECH, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_CLAMP_CTL",
+			  WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x30, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_DETECTION_TYPE",
+			  WCD934X_ANA_MBHC_ELECT, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_CTRL",
+			  WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_L_DET_PULL_UP_COMP_CTRL",
+			  WCD934X_ANA_MBHC_MECH, 0x04, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PLUG_TYPE",
+			  WCD934X_ANA_MBHC_MECH, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_GND_PLUG_TYPE",
+			  WCD934X_ANA_MBHC_MECH, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SW_HPH_LP_100K_TO_GND",
+			  WCD934X_ANA_MBHC_MECH, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_SCHMT_ISRC",
+			  WCD934X_ANA_MBHC_ELECT, 0x06, 1, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_EN",
+			  WCD934X_ANA_MBHC_ELECT, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_INSREM_DBNC",
+			  WCD934X_MBHC_NEW_PLUG_DETECT_CTL, 0x0F, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_DBNC",
+			  WCD934X_MBHC_NEW_CTL_1, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_VREF",
+			  WCD934X_MBHC_NEW_CTL_2, 0x03, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HS_COMP_RESULT",
+			  WCD934X_ANA_MBHC_RESULT_3, 0x08, 3, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MIC_SCHMT_RESULT",
+			  WCD934X_ANA_MBHC_RESULT_3, 0x20, 5, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_SCHMT_RESULT",
+			  WCD934X_ANA_MBHC_RESULT_3, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_SCHMT_RESULT",
+			  WCD934X_ANA_MBHC_RESULT_3, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_OCP_FSM_EN",
+			  WCD934X_HPH_OCP_CTL, 0x10, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_RESULT",
+			  WCD934X_ANA_MBHC_RESULT_3, 0x07, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_BTN_ISRC_CTL",
+			  WCD934X_ANA_MBHC_ELECT, 0x70, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_ELECT_RESULT",
+			  WCD934X_ANA_MBHC_RESULT_3, 0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MICB_CTRL",
+			  WCD934X_ANA_MICB2, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_CNP_WG_TIME",
+			  WCD934X_HPH_CNP_WG_TIME, 0xFF, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_PA_EN",
+			  WCD934X_ANA_HPH, 0x40, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_PA_EN",
+			  WCD934X_ANA_HPH, 0x80, 7, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPH_PA_EN",
+			  WCD934X_ANA_HPH, 0xC0, 6, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_SWCH_LEVEL_REMOVE",
+			  WCD934X_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",
+			  WCD934X_ANA_MBHC_ZDET, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_FSM_STATUS",
+			  WCD934X_MBHC_STATUS_SPARE_1, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_MUX_CTL",
+			  WCD934X_MBHC_NEW_CTL_2, 0x70, 4, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_DET_EN",
+			  WCD934X_HPH_L_TEST, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_DET_EN",
+			  WCD934X_HPH_R_TEST, 0x01, 0, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHL_OCP_STATUS",
+			  WCD934X_INTR_PIN1_STATUS0, 0x04, 2, 0),
+	WCD_MBHC_REGISTER("WCD_MBHC_HPHR_OCP_STATUS",
+			  WCD934X_INTR_PIN1_STATUS0, 0x08, 3, 0),
+};
+
+static const struct wcd_mbhc_intr intr_ids = {
+	.mbhc_sw_intr =  WCD934X_IRQ_MBHC_SW_DET,
+	.mbhc_btn_press_intr = WCD934X_IRQ_MBHC_BUTTON_PRESS_DET,
+	.mbhc_btn_release_intr = WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET,
+	.mbhc_hs_ins_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	.mbhc_hs_rem_intr = WCD934X_IRQ_MBHC_ELECT_INS_REM_DET,
+	.hph_left_ocp = WCD934X_IRQ_HPH_PA_OCPL_FAULT,
+	.hph_right_ocp = WCD934X_IRQ_HPH_PA_OCPR_FAULT,
+};
+
+
+static char on_demand_supply_name[][MAX_ON_DEMAND_SUPPLY_NAME_LENGTH] = {
+	"cdc-vdd-mic-bias",
+};
+
+struct tavil_mbhc_zdet_param {
+	u16 ldo_ctl;
+	u16 noff;
+	u16 nshift;
+	u16 btn5;
+	u16 btn6;
+	u16 btn7;
+};
+
+static int tavil_mbhc_request_irq(struct snd_soc_codec *codec,
+				  int irq, irq_handler_t handler,
+				  const char *name, void *data)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	return wcd9xxx_request_irq(core_res, irq, handler, name, data);
+}
+
+static void tavil_mbhc_irq_control(struct snd_soc_codec *codec,
+				   int irq, bool enable)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+	if (enable)
+		wcd9xxx_enable_irq(core_res, irq);
+	else
+		wcd9xxx_disable_irq(core_res, irq);
+}
+
+static int tavil_mbhc_free_irq(struct snd_soc_codec *codec,
+			       int irq, void *data)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	wcd9xxx_free_irq(core_res, irq, data);
+	return 0;
+}
+
+static void tavil_mbhc_clk_setup(struct snd_soc_codec *codec,
+				 bool enable)
+{
+	if (enable)
+		snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1,
+				    0x80, 0x80);
+	else
+		snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1,
+				    0x80, 0x00);
+}
+
+static int tavil_mbhc_btn_to_num(struct snd_soc_codec *codec)
+{
+	return snd_soc_read(codec, WCD934X_ANA_MBHC_RESULT_3) & 0x7;
+}
+
+static int tavil_enable_ext_mb_source(struct wcd_mbhc *mbhc,
+				      bool turn_on)
+{
+	struct wcd934x_mbhc *wcd934x_mbhc;
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd934x_on_demand_supply *supply;
+	int ret = 0;
+
+	wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc);
+
+	supply =  &wcd934x_mbhc->on_demand_list[WCD934X_ON_DEMAND_MICBIAS];
+	if (!supply->supply) {
+		dev_dbg(codec->dev, "%s: warning supply not present ond for %s\n",
+				__func__, "onDemand Micbias");
+		return ret;
+	}
+
+	dev_dbg(codec->dev, "%s turn_on: %d count: %d\n", __func__, turn_on,
+		supply->ondemand_supply_count);
+
+	if (turn_on) {
+		if (!(supply->ondemand_supply_count)) {
+			ret = snd_soc_dapm_force_enable_pin(
+				snd_soc_codec_get_dapm(codec),
+				"MICBIAS_REGULATOR");
+			snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+		}
+		supply->ondemand_supply_count++;
+	} else {
+		if (supply->ondemand_supply_count > 0)
+			supply->ondemand_supply_count--;
+		if (!(supply->ondemand_supply_count)) {
+			ret = snd_soc_dapm_disable_pin(
+				snd_soc_codec_get_dapm(codec),
+				"MICBIAS_REGULATOR");
+		snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+		}
+	}
+
+	if (ret)
+		dev_err(codec->dev, "%s: Failed to %s external micbias source\n",
+			__func__, turn_on ? "enable" : "disabled");
+	else
+		dev_dbg(codec->dev, "%s: %s external micbias source\n",
+			__func__, turn_on ? "Enabled" : "Disabled");
+
+	return ret;
+}
+
+static void tavil_mbhc_mbhc_bias_control(struct snd_soc_codec *codec,
+					 bool enable)
+{
+	if (enable)
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT,
+				    0x01, 0x01);
+	else
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_ELECT,
+				    0x01, 0x00);
+}
+
+static void tavil_mbhc_program_btn_thr(struct snd_soc_codec *codec,
+				       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(codec->dev, "%s: invalid number of buttons: %d\n",
+			__func__, num_btn);
+		return;
+	}
+	/*
+	 * Tavil just needs one set of thresholds for button detection
+	 * due to micbias voltage ramp to pullup upon button press. So
+	 * btn_low and is_micbias are ignored and always program button
+	 * thresholds using btn_high.
+	 */
+	for (i = 0; i < num_btn; i++) {
+		vth = ((btn_high[i] * 2) / 25) & 0x3F;
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN0 + i,
+				    0xFC, vth << 2);
+		dev_dbg(codec->dev, "%s: btn_high[%d]: %d, vth: %d\n",
+			__func__, i, btn_high[i], vth);
+	}
+}
+
+static bool tavil_mbhc_lock_sleep(struct wcd_mbhc *mbhc, bool lock)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+	bool ret = 0;
+
+	if (lock)
+		ret = wcd9xxx_lock_sleep(core_res);
+	else
+		wcd9xxx_unlock_sleep(core_res);
+
+	return ret;
+}
+
+static int tavil_mbhc_register_notifier(struct wcd_mbhc *mbhc,
+					struct notifier_block *nblock,
+					bool enable)
+{
+	struct wcd934x_mbhc *wcd934x_mbhc;
+
+	wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc);
+
+	if (enable)
+		return blocking_notifier_chain_register(&wcd934x_mbhc->notifier,
+							nblock);
+	else
+		return blocking_notifier_chain_unregister(
+				&wcd934x_mbhc->notifier, nblock);
+}
+
+static bool tavil_mbhc_micb_en_status(struct wcd_mbhc *mbhc, int micb_num)
+{
+	u8 val;
+
+	if (micb_num == MIC_BIAS_2) {
+		val = (snd_soc_read(mbhc->codec, WCD934X_ANA_MICB2) >> 6);
+		if (val == 0x01)
+			return true;
+	}
+	return false;
+}
+
+static bool tavil_mbhc_hph_pa_on_status(struct snd_soc_codec *codec)
+{
+	return (snd_soc_read(codec, WCD934X_ANA_HPH) & 0xC0) ? true : false;
+}
+
+static void tavil_mbhc_hph_l_pull_up_control(
+		struct snd_soc_codec *codec,
+		enum mbhc_hs_pullup_iref 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(codec->dev, "%s: HS pull up current:%d\n",
+		__func__, pull_up_cur);
+
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_PLUG_DETECT_CTL,
+			    0xC0, pull_up_cur << 6);
+}
+
+static int tavil_mbhc_request_micbias(struct snd_soc_codec *codec,
+				      int micb_num, int req)
+{
+	int ret;
+
+	/*
+	 * If micbias is requested, make sure that there
+	 * is vote to enable mclk
+	 */
+	if (req == MICB_ENABLE)
+		tavil_cdc_mclk_enable(codec, true);
+
+	ret = tavil_micbias_control(codec, micb_num, req, false);
+
+	/*
+	 * Release vote for mclk while requesting for
+	 * micbias disable
+	 */
+	if (req == MICB_DISABLE)
+		tavil_cdc_mclk_enable(codec, false);
+
+	return ret;
+}
+
+static void tavil_mbhc_micb_ramp_control(struct snd_soc_codec *codec,
+					 bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+				    0x1C, 0x0C);
+		snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+				    0x80, 0x80);
+	} else {
+		snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+				    0x80, 0x00);
+		snd_soc_update_bits(codec, WCD934X_ANA_MICB2_RAMP,
+				    0x1C, 0x00);
+	}
+}
+
+static struct firmware_cal *tavil_get_hwdep_fw_cal(struct wcd_mbhc *mbhc,
+						   enum wcd_cal_type type)
+{
+	struct wcd934x_mbhc *wcd934x_mbhc;
+	struct firmware_cal *hwdep_cal;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wcd934x_mbhc = container_of(mbhc, struct wcd934x_mbhc, wcd_mbhc);
+
+	if (!codec) {
+		pr_err("%s: NULL codec pointer\n", __func__);
+		return NULL;
+	}
+	hwdep_cal = wcdcal_get_fw_cal(wcd934x_mbhc->fw_data, type);
+	if (!hwdep_cal)
+		dev_err(codec->dev, "%s: cal not sent by %d\n",
+			__func__, type);
+
+	return hwdep_cal;
+}
+
+static int tavil_mbhc_micb_ctrl_threshold_mic(struct snd_soc_codec *codec,
+					      int micb_num, bool req_en)
+{
+	struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+	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 = tavil_mbhc_micb_adjust_voltage(codec, micb_mv, MIC_BIAS_2);
+
+	return rc;
+}
+
+static inline void tavil_mbhc_get_result_params(struct wcd9xxx *wcd9xxx,
+						s16 *d1_a, u16 noff,
+						int32_t *zdet)
+{
+	int i;
+	int val, val1;
+	s16 c1;
+	s32 x1, d1;
+	int32_t denom;
+	int minCode_param[] = {
+			3277, 1639, 820, 410, 205, 103, 52, 26
+	};
+
+	regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x20);
+	for (i = 0; i < TAVIL_ZDET_NUM_MEASUREMENTS; i++) {
+		regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_2, &val);
+		if (val & 0x80)
+			break;
+	}
+	val = val << 0x8;
+	regmap_read(wcd9xxx->regmap, WCD934X_ANA_MBHC_RESULT_1, &val1);
+	val |= val1;
+	regmap_update_bits(wcd9xxx->regmap, WCD934X_ANA_MBHC_ZDET, 0x20, 0x00);
+	x1 = TAVIL_MBHC_GET_X1(val);
+	c1 = TAVIL_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(wcd9xxx->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 = (TAVIL_MBHC_ZDET_CONST * 1000) / denom;
+	else if (x1 < minCode_param[noff])
+		*zdet = TAVIL_ZDET_FLOATING_IMPEDANCE;
+
+	dev_dbg(wcd9xxx->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_bulk_read(wcd9xxx->regmap,
+				 WCD934X_ANA_MBHC_RESULT_1, (u8 *)&val, 2);
+		x1 = TAVIL_MBHC_GET_X1(val);
+		i++;
+		if (i == TAVIL_ZDET_NUM_MEASUREMENTS)
+			break;
+	}
+}
+
+static void tavil_mbhc_zdet_ramp(struct snd_soc_codec *codec,
+				 struct tavil_mbhc_zdet_param *zdet_param,
+				 int32_t *zl, int32_t *zr, s16 *d1_a)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int32_t zdet = 0;
+
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x70,
+			    zdet_param->ldo_ctl << 4);
+	snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN5, 0xFC,
+			    zdet_param->btn5);
+	snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN6, 0xFC,
+			    zdet_param->btn6);
+	snd_soc_update_bits(codec, WCD934X_ANA_MBHC_BTN7, 0xFC,
+			    zdet_param->btn7);
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, 0x0F,
+			    zdet_param->noff);
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_ZDET_RAMP_CTL, 0x0F,
+			    zdet_param->nshift);
+
+	if (!zl)
+		goto z_right;
+	/* Start impedance measurement for HPH_L */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD934X_ANA_MBHC_ZDET, 0x80, 0x80);
+	dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_L, noff = %d\n",
+		__func__, zdet_param->noff);
+	tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD934X_ANA_MBHC_ZDET, 0x80, 0x00);
+
+	*zl = zdet;
+
+z_right:
+	if (!zr)
+		return;
+	/* Start impedance measurement for HPH_R */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD934X_ANA_MBHC_ZDET, 0x40, 0x40);
+	dev_dbg(wcd9xxx->dev, "%s: ramp for HPH_R, noff = %d\n",
+		__func__, zdet_param->noff);
+	tavil_mbhc_get_result_params(wcd9xxx, d1_a, zdet_param->noff, &zdet);
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD934X_ANA_MBHC_ZDET, 0x40, 0x00);
+
+	*zr = zdet;
+}
+
+static inline void tavil_wcd_mbhc_qfuse_cal(struct snd_soc_codec *codec,
+					    int32_t *z_val, int flag_l_r)
+{
+	s16 q1;
+	int q1_cal;
+
+	if (*z_val < (TAVIL_ZDET_VAL_400/1000))
+		q1 = snd_soc_read(codec,
+			WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT1 + (2 * flag_l_r));
+	else
+		q1 = snd_soc_read(codec,
+			WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT2 + (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 tavil_wcd_mbhc_calc_impedance(struct wcd_mbhc *mbhc, uint32_t *zl,
+					  uint32_t *zr)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	s16 reg0, reg1, reg2, reg3, reg4;
+	int32_t z1L, z1R, z1Ls;
+	int zMono, z_diff1, z_diff2;
+	bool is_fsm_disable = false;
+	struct tavil_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 tavil_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_read(codec, WCD934X_ANA_MBHC_BTN5);
+	reg1 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN6);
+	reg2 = snd_soc_read(codec, WCD934X_ANA_MBHC_BTN7);
+	reg3 = snd_soc_read(codec, WCD934X_MBHC_CTL_CLK);
+	reg4 = snd_soc_read(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL);
+
+	if (snd_soc_read(codec, WCD934X_ANA_MBHC_ELECT) & 0x80) {
+		is_fsm_disable = true;
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD934X_ANA_MBHC_ELECT, 0x80, 0x00);
+	}
+
+	/* For NO-jack, disable L_DET_EN before Z-det measurements */
+	if (mbhc->hphl_swh)
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD934X_ANA_MBHC_MECH, 0x80, 0x00);
+
+	/* Turn off 100k pull down on HPHL */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD934X_ANA_MBHC_MECH, 0x01, 0x00);
+
+	/* First get impedance on Left */
+	d1 = d1_a[1];
+	zdet_param_ptr = &zdet_param[1];
+	tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+	if (!TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1L))
+		goto left_ch_impedance;
+
+	/* Second ramp for left ch */
+	if (z1L < TAVIL_ZDET_VAL_32) {
+		zdet_param_ptr = &zdet_param[0];
+		d1 = d1_a[0];
+	} else if ((z1L > TAVIL_ZDET_VAL_400) && (z1L <= TAVIL_ZDET_VAL_1200)) {
+		zdet_param_ptr = &zdet_param[2];
+		d1 = d1_a[2];
+	} else if (z1L > TAVIL_ZDET_VAL_1200) {
+		zdet_param_ptr = &zdet_param[3];
+		d1 = d1_a[3];
+	}
+	tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, &z1L, NULL, d1);
+
+left_ch_impedance:
+	if ((z1L == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+		(z1L > TAVIL_ZDET_VAL_100K)) {
+		*zl = TAVIL_ZDET_FLOATING_IMPEDANCE;
+		zdet_param_ptr = &zdet_param[1];
+		d1 = d1_a[1];
+	} else {
+		*zl = z1L/1000;
+		tavil_wcd_mbhc_qfuse_cal(codec, zl, 0);
+	}
+	dev_dbg(codec->dev, "%s: impedance on HPH_L = %d(ohms)\n",
+		__func__, *zl);
+
+	/* Start of right impedance ramp and calculation */
+	tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+	if (TAVIL_MBHC_IS_SECOND_RAMP_REQUIRED(z1R)) {
+		if (((z1R > TAVIL_ZDET_VAL_1200) &&
+			(zdet_param_ptr->noff == 0x6)) ||
+			((*zl) != TAVIL_ZDET_FLOATING_IMPEDANCE))
+			goto right_ch_impedance;
+		/* Second ramp for right ch */
+		if (z1R < TAVIL_ZDET_VAL_32) {
+			zdet_param_ptr = &zdet_param[0];
+			d1 = d1_a[0];
+		} else if ((z1R > TAVIL_ZDET_VAL_400) &&
+			(z1R <= TAVIL_ZDET_VAL_1200)) {
+			zdet_param_ptr = &zdet_param[2];
+			d1 = d1_a[2];
+		} else if (z1R > TAVIL_ZDET_VAL_1200) {
+			zdet_param_ptr = &zdet_param[3];
+			d1 = d1_a[3];
+		}
+		tavil_mbhc_zdet_ramp(codec, zdet_param_ptr, NULL, &z1R, d1);
+	}
+right_ch_impedance:
+	if ((z1R == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+		(z1R > TAVIL_ZDET_VAL_100K)) {
+		*zr = TAVIL_ZDET_FLOATING_IMPEDANCE;
+	} else {
+		*zr = z1R/1000;
+		tavil_wcd_mbhc_qfuse_cal(codec, zr, 1);
+	}
+	dev_dbg(codec->dev, "%s: impedance on HPH_R = %d(ohms)\n",
+		__func__, *zr);
+
+	/* Mono/stereo detection */
+	if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) &&
+		(*zr == TAVIL_ZDET_FLOATING_IMPEDANCE)) {
+		dev_dbg(codec->dev,
+			"%s: plug type is invalid or extension cable\n",
+			__func__);
+		goto zdet_complete;
+	}
+	if ((*zl == TAVIL_ZDET_FLOATING_IMPEDANCE) ||
+	    (*zr == TAVIL_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(codec->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_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x02);
+	snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x01);
+	if (*zl < (TAVIL_ZDET_VAL_32/1000))
+		tavil_mbhc_zdet_ramp(codec, &zdet_param[0], &z1Ls, NULL, d1);
+	else
+		tavil_mbhc_zdet_ramp(codec, &zdet_param[1], &z1Ls, NULL, d1);
+	snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2, 0x40, 0x00);
+	snd_soc_update_bits(codec, WCD934X_HPH_R_ATEST, 0x02, 0x00);
+	z1Ls /= 1000;
+	tavil_wcd_mbhc_qfuse_cal(codec, &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(codec->dev, "%s: stereo plug type detected\n",
+			__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_STEREO;
+	} else {
+		dev_dbg(codec->dev, "%s: MONO plug type detected\n",
+			__func__);
+		mbhc->hph_type = WCD_MBHC_HPH_MONO;
+	}
+
+zdet_complete:
+	snd_soc_write(codec, WCD934X_ANA_MBHC_BTN5, reg0);
+	snd_soc_write(codec, WCD934X_ANA_MBHC_BTN6, reg1);
+	snd_soc_write(codec, WCD934X_ANA_MBHC_BTN7, reg2);
+	/* Turn on 100k pull down on HPHL */
+	regmap_update_bits(wcd9xxx->regmap,
+			   WCD934X_ANA_MBHC_MECH, 0x01, 0x01);
+
+	/* For NO-jack, re-enable L_DET_EN after Z-det measurements */
+	if (mbhc->hphl_swh)
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD934X_ANA_MBHC_MECH, 0x80, 0x80);
+
+	snd_soc_write(codec, WCD934X_MBHC_NEW_ZDET_ANA_CTL, reg4);
+	snd_soc_write(codec, WCD934X_MBHC_CTL_CLK, reg3);
+	if (is_fsm_disable)
+		regmap_update_bits(wcd9xxx->regmap,
+				   WCD934X_ANA_MBHC_ELECT, 0x80, 0x80);
+}
+
+static void tavil_mbhc_gnd_det_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+				    0x02, 0x02);
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+				    0x40, 0x40);
+	} else {
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+				    0x40, 0x00);
+		snd_soc_update_bits(codec, WCD934X_ANA_MBHC_MECH,
+				    0x02, 0x00);
+	}
+}
+
+static void tavil_mbhc_hph_pull_down_ctrl(struct snd_soc_codec *codec,
+					  bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+				    0x40, 0x40);
+		snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+				    0x10, 0x10);
+	} else {
+		snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+				    0x40, 0x00);
+		snd_soc_update_bits(codec, WCD934X_HPH_PA_CTL2,
+				    0x10, 0x00);
+	}
+}
+static void tavil_mbhc_moisture_config(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	if (TAVIL_MBHC_MOISTURE_RREF == R_OFF)
+		return;
+
+	/* Donot enable moisture detection if jack type is NC */
+	if (!mbhc->hphl_swh) {
+		dev_dbg(codec->dev, "%s: disable moisture detection for NC\n",
+			__func__);
+		return;
+	}
+
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_2,
+			    0x0C, TAVIL_MBHC_MOISTURE_RREF << 2);
+}
+
+static bool tavil_hph_register_recovery(struct wcd_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+	if (!wcd934x_mbhc)
+		return false;
+
+	wcd934x_mbhc->is_hph_recover = false;
+	snd_soc_dapm_force_enable_pin(snd_soc_codec_get_dapm(codec),
+				      "RESET_HPH_REGISTERS");
+	snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+	snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+				 "RESET_HPH_REGISTERS");
+	snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+
+	return wcd934x_mbhc->is_hph_recover;
+}
+
+static const struct wcd_mbhc_cb mbhc_cb = {
+	.request_irq = tavil_mbhc_request_irq,
+	.irq_control = tavil_mbhc_irq_control,
+	.free_irq = tavil_mbhc_free_irq,
+	.clk_setup = tavil_mbhc_clk_setup,
+	.map_btn_code_to_num = tavil_mbhc_btn_to_num,
+	.enable_mb_source = tavil_enable_ext_mb_source,
+	.mbhc_bias = tavil_mbhc_mbhc_bias_control,
+	.set_btn_thr = tavil_mbhc_program_btn_thr,
+	.lock_sleep = tavil_mbhc_lock_sleep,
+	.register_notifier = tavil_mbhc_register_notifier,
+	.micbias_enable_status = tavil_mbhc_micb_en_status,
+	.hph_pa_on_status = tavil_mbhc_hph_pa_on_status,
+	.hph_pull_up_control = tavil_mbhc_hph_l_pull_up_control,
+	.mbhc_micbias_control = tavil_mbhc_request_micbias,
+	.mbhc_micb_ramp_control = tavil_mbhc_micb_ramp_control,
+	.get_hwdep_fw_cal = tavil_get_hwdep_fw_cal,
+	.mbhc_micb_ctrl_thr_mic = tavil_mbhc_micb_ctrl_threshold_mic,
+	.compute_impedance = tavil_wcd_mbhc_calc_impedance,
+	.mbhc_gnd_det_ctrl = tavil_mbhc_gnd_det_ctrl,
+	.hph_pull_down_ctrl = tavil_mbhc_hph_pull_down_ctrl,
+	.mbhc_moisture_config = tavil_mbhc_moisture_config,
+	.hph_register_recovery = tavil_hph_register_recovery,
+};
+
+static struct regulator *tavil_codec_find_ondemand_regulator(
+		struct snd_soc_codec *codec, const char *name)
+{
+	int i;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+
+	for (i = 0; i < wcd9xxx->num_of_supplies; ++i) {
+		if (pdata->regulator[i].ondemand &&
+		    wcd9xxx->supplies[i].supply &&
+		    !strcmp(wcd9xxx->supplies[i].supply, name))
+			return wcd9xxx->supplies[i].consumer;
+	}
+
+	dev_dbg(codec->dev, "Warning: regulator not found:%s\n",
+		name);
+	return NULL;
+}
+
+static int tavil_get_hph_type(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+	struct wcd_mbhc *mbhc;
+
+	if (!wcd934x_mbhc) {
+		dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+		return -EINVAL;
+	}
+
+	mbhc = &wcd934x_mbhc->wcd_mbhc;
+
+	ucontrol->value.integer.value[0] = (u32) mbhc->hph_type;
+	dev_dbg(codec->dev, "%s: hph_type = %u\n", __func__, mbhc->hph_type);
+
+	return 0;
+}
+
+static int tavil_hph_impedance_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	uint32_t zl, zr;
+	bool hphr;
+	struct soc_multi_mixer_control *mc;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+	if (!wcd934x_mbhc) {
+		dev_err(codec->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(&wcd934x_mbhc->wcd_mbhc, &zl, &zr);
+	dev_dbg(codec->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,
+		       tavil_get_hph_type, NULL),
+};
+
+static const struct snd_kcontrol_new impedance_detect_controls[] = {
+	SOC_SINGLE_EXT("HPHL Impedance", 0, 0, UINT_MAX, 0,
+		       tavil_hph_impedance_get, NULL),
+	SOC_SINGLE_EXT("HPHR Impedance", 0, 1, UINT_MAX, 0,
+		       tavil_hph_impedance_get, NULL),
+};
+
+/*
+ * tavil_mbhc_hs_detect: starts mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ * @mbhc_cfg: handle to mbhc configuration structure
+ * return 0 if mbhc_start is success or error code in case of failure
+ */
+int tavil_mbhc_hs_detect(struct snd_soc_codec *codec,
+			 struct wcd_mbhc_config *mbhc_cfg)
+{
+	struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+	if (!wcd934x_mbhc) {
+		dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+		return -EINVAL;
+	}
+
+	return wcd_mbhc_start(&wcd934x_mbhc->wcd_mbhc, mbhc_cfg);
+}
+EXPORT_SYMBOL(tavil_mbhc_hs_detect);
+
+/*
+ * tavil_mbhc_hs_detect_exit: stop mbhc insertion/removal functionality
+ * @codec: handle to snd_soc_codec *
+ */
+void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec)
+{
+	struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+	if (!wcd934x_mbhc) {
+		dev_err(codec->dev, "%s: mbhc not initialized!\n", __func__);
+		return;
+	}
+	wcd_mbhc_stop(&wcd934x_mbhc->wcd_mbhc);
+}
+EXPORT_SYMBOL(tavil_mbhc_hs_detect_exit);
+
+/*
+ * tavil_mbhc_post_ssr_init: initialize mbhc for tavil post subsystem restart
+ * @mbhc: poniter to wcd934x_mbhc structure
+ * @codec: handle to snd_soc_codec *
+ *
+ * return 0 if mbhc_init is success or error code in case of failure
+ */
+int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
+			     struct snd_soc_codec *codec)
+{
+	int ret;
+
+	if (!mbhc || !codec)
+		return -EINVAL;
+
+	wcd_mbhc_deinit(&mbhc->wcd_mbhc);
+	ret = wcd_mbhc_init(&mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids,
+			    wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED);
+	if (ret) {
+		dev_err(codec->dev, "%s: mbhc initialization failed\n",
+			__func__);
+		goto done;
+	}
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL(tavil_mbhc_post_ssr_init);
+
+/*
+ * tavil_mbhc_init: initialize mbhc for tavil
+ * @mbhc: poniter to wcd934x_mbhc struct pointer to store the configs
+ * @codec: handle to snd_soc_codec *
+ * @fw_data: handle to firmware data
+ *
+ * return 0 if mbhc_init is success or error code in case of failure
+ */
+int tavil_mbhc_init(struct wcd934x_mbhc **mbhc, struct snd_soc_codec *codec,
+		    struct fw_info *fw_data)
+{
+	struct regulator *supply;
+	struct wcd934x_mbhc *wcd934x_mbhc;
+	int ret;
+
+	wcd934x_mbhc = devm_kzalloc(codec->dev, sizeof(struct wcd934x_mbhc),
+				    GFP_KERNEL);
+	if (!wcd934x_mbhc)
+		return -ENOMEM;
+
+	wcd934x_mbhc->wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	wcd934x_mbhc->fw_data = fw_data;
+	BLOCKING_INIT_NOTIFIER_HEAD(&wcd934x_mbhc->notifier);
+
+	ret = wcd_mbhc_init(&wcd934x_mbhc->wcd_mbhc, codec, &mbhc_cb, &intr_ids,
+			    wcd_mbhc_registers, TAVIL_ZDET_SUPPORTED);
+	if (ret) {
+		dev_err(codec->dev, "%s: mbhc initialization failed\n",
+			__func__);
+		goto err;
+	}
+
+	supply = tavil_codec_find_ondemand_regulator(codec,
+			on_demand_supply_name[WCD934X_ON_DEMAND_MICBIAS]);
+	if (supply) {
+		wcd934x_mbhc->on_demand_list[
+			WCD934X_ON_DEMAND_MICBIAS].supply =
+				supply;
+		wcd934x_mbhc->on_demand_list[
+			WCD934X_ON_DEMAND_MICBIAS].ondemand_supply_count =
+				0;
+	}
+
+	(*mbhc) = wcd934x_mbhc;
+	snd_soc_add_codec_controls(codec, impedance_detect_controls,
+				   ARRAY_SIZE(impedance_detect_controls));
+	snd_soc_add_codec_controls(codec, hph_type_detect_controls,
+				   ARRAY_SIZE(hph_type_detect_controls));
+
+	snd_soc_update_bits(codec, WCD934X_MBHC_NEW_CTL_1, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD934X_MBHC_CTL_BCS, 0x01, 0x01);
+
+	return 0;
+err:
+	devm_kfree(codec->dev, wcd934x_mbhc);
+	return ret;
+}
+EXPORT_SYMBOL(tavil_mbhc_init);
+
+/*
+ * tavil_mbhc_deinit: deinitialize mbhc for tavil
+ * @codec: handle to snd_soc_codec *
+ */
+void tavil_mbhc_deinit(struct snd_soc_codec *codec)
+{
+	struct wcd934x_mbhc *wcd934x_mbhc = tavil_soc_get_mbhc(codec);
+
+	if (wcd934x_mbhc) {
+		wcd_mbhc_deinit(&wcd934x_mbhc->wcd_mbhc);
+		devm_kfree(codec->dev, wcd934x_mbhc);
+	}
+}
+EXPORT_SYMBOL(tavil_mbhc_deinit);
diff --git a/sound/soc/codecs/wcd934x/wcd934x-mbhc.h b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
new file mode 100644
index 0000000..95d8e3d
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-mbhc.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD934X_MBHC_H__
+#define __WCD934X_MBHC_H__
+#include "../wcd-mbhc-v2.h"
+
+enum wcd934x_on_demand_supply_name {
+	WCD934X_ON_DEMAND_MICBIAS = 0,
+	WCD934X_ON_DEMAND_SUPPLIES_MAX,
+};
+
+struct wcd934x_on_demand_supply {
+	struct regulator *supply;
+	int ondemand_supply_count;
+};
+
+struct wcd934x_mbhc {
+	struct wcd_mbhc wcd_mbhc;
+	struct blocking_notifier_head notifier;
+	struct wcd934x_on_demand_supply on_demand_list[
+			WCD934X_ON_DEMAND_SUPPLIES_MAX];
+	struct wcd9xxx *wcd9xxx;
+	struct fw_info *fw_data;
+	bool mbhc_started;
+	bool is_hph_recover;
+};
+
+extern int tavil_mbhc_init(struct wcd934x_mbhc **mbhc,
+			   struct snd_soc_codec *codec,
+			   struct fw_info *fw_data);
+extern void tavil_mbhc_hs_detect_exit(struct snd_soc_codec *codec);
+extern int tavil_mbhc_hs_detect(struct snd_soc_codec *codec,
+				struct wcd_mbhc_config *mbhc_cfg);
+extern void tavil_mbhc_deinit(struct snd_soc_codec *codec);
+extern int tavil_mbhc_post_ssr_init(struct wcd934x_mbhc *mbhc,
+				    struct snd_soc_codec *codec);
+#endif /* __WCD934X_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd934x/wcd934x-routing.h b/sound/soc/codecs/wcd934x/wcd934x-routing.h
new file mode 100644
index 0000000..940fdf8
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x-routing.h
@@ -0,0 +1,1154 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD934X_ROUTING_H
+#define WCD934X_ROUTING_H
+
+#include <sound/soc-dapm.h>
+
+const struct snd_soc_dapm_route tavil_slim_audio_map[] = {
+
+	/* Virtual input widgets */
+	{"AIF1 CAP", NULL, "AIF1_CAP Mixer"},
+	{"AIF2 CAP", NULL, "AIF2_CAP Mixer"},
+	{"AIF3 CAP", NULL, "AIF3_CAP Mixer"},
+	{"AIF4 MAD", NULL, "AIF4_MAD Mixer"},
+
+	/* Virtual input widget Mixer */
+	{"AIF1_CAP Mixer", "SLIM TX0", "SLIM TX0"},
+	{"AIF1_CAP Mixer", "SLIM TX1", "SLIM TX1"},
+	{"AIF1_CAP Mixer", "SLIM TX2", "SLIM TX2"},
+	{"AIF1_CAP Mixer", "SLIM TX3", "SLIM TX3"},
+	{"AIF1_CAP Mixer", "SLIM TX4", "SLIM TX4"},
+	{"AIF1_CAP Mixer", "SLIM TX5", "SLIM TX5"},
+	{"AIF1_CAP Mixer", "SLIM TX6", "SLIM TX6"},
+	{"AIF1_CAP Mixer", "SLIM TX7", "SLIM TX7"},
+	{"AIF1_CAP Mixer", "SLIM TX8", "SLIM TX8"},
+	{"AIF1_CAP Mixer", "SLIM TX9", "SLIM TX9"},
+	{"AIF1_CAP Mixer", "SLIM TX10", "SLIM TX10"},
+	{"AIF1_CAP Mixer", "SLIM TX11", "SLIM TX11"},
+	{"AIF1_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+
+	{"AIF2_CAP Mixer", "SLIM TX0", "SLIM TX0"},
+	{"AIF2_CAP Mixer", "SLIM TX1", "SLIM TX1"},
+	{"AIF2_CAP Mixer", "SLIM TX2", "SLIM TX2"},
+	{"AIF2_CAP Mixer", "SLIM TX3", "SLIM TX3"},
+	{"AIF2_CAP Mixer", "SLIM TX4", "SLIM TX4"},
+	{"AIF2_CAP Mixer", "SLIM TX5", "SLIM TX5"},
+	{"AIF2_CAP Mixer", "SLIM TX6", "SLIM TX6"},
+	{"AIF2_CAP Mixer", "SLIM TX7", "SLIM TX7"},
+	{"AIF2_CAP Mixer", "SLIM TX8", "SLIM TX8"},
+	{"AIF2_CAP Mixer", "SLIM TX9", "SLIM TX9"},
+	{"AIF2_CAP Mixer", "SLIM TX10", "SLIM TX10"},
+	{"AIF2_CAP Mixer", "SLIM TX11", "SLIM TX11"},
+	{"AIF2_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+
+	{"AIF3_CAP Mixer", "SLIM TX0", "SLIM TX0"},
+	{"AIF3_CAP Mixer", "SLIM TX1", "SLIM TX1"},
+	{"AIF3_CAP Mixer", "SLIM TX2", "SLIM TX2"},
+	{"AIF3_CAP Mixer", "SLIM TX3", "SLIM TX3"},
+	{"AIF3_CAP Mixer", "SLIM TX4", "SLIM TX4"},
+	{"AIF3_CAP Mixer", "SLIM TX5", "SLIM TX5"},
+	{"AIF3_CAP Mixer", "SLIM TX6", "SLIM TX6"},
+	{"AIF3_CAP Mixer", "SLIM TX7", "SLIM TX7"},
+	{"AIF3_CAP Mixer", "SLIM TX8", "SLIM TX8"},
+	{"AIF3_CAP Mixer", "SLIM TX9", "SLIM TX9"},
+	{"AIF3_CAP Mixer", "SLIM TX10", "SLIM TX10"},
+	{"AIF3_CAP Mixer", "SLIM TX11", "SLIM TX11"},
+	{"AIF3_CAP Mixer", "SLIM TX13", "SLIM TX13"},
+
+	{"AIF4_MAD Mixer", "SLIM TX13", "SLIM TX13"},
+
+	{"SLIM RX0 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX1 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX2 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX3 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX4 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX5 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX6 MUX", "AIF1_PB", "AIF1 PB"},
+	{"SLIM RX7 MUX", "AIF1_PB", "AIF1 PB"},
+
+	{"SLIM RX0 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX1 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX2 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX3 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX4 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX5 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX6 MUX", "AIF2_PB", "AIF2 PB"},
+	{"SLIM RX7 MUX", "AIF2_PB", "AIF2 PB"},
+
+	{"SLIM RX0 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX1 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX2 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX3 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX4 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX5 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX6 MUX", "AIF3_PB", "AIF3 PB"},
+	{"SLIM RX7 MUX", "AIF3_PB", "AIF3 PB"},
+
+	{"SLIM RX0 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX1 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX2 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX3 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX4 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX5 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX6 MUX", "AIF4_PB", "AIF4 PB"},
+	{"SLIM RX7 MUX", "AIF4_PB", "AIF4 PB"},
+
+	{"SLIM RX0", NULL, "SLIM RX0 MUX"},
+	{"SLIM RX1", NULL, "SLIM RX1 MUX"},
+	{"SLIM RX2", NULL, "SLIM RX2 MUX"},
+	{"SLIM RX3", NULL, "SLIM RX3 MUX"},
+	{"SLIM RX4", NULL, "SLIM RX4 MUX"},
+	{"SLIM RX5", NULL, "SLIM RX5 MUX"},
+	{"SLIM RX6", NULL, "SLIM RX6 MUX"},
+	{"SLIM RX7", NULL, "SLIM RX7 MUX"},
+
+};
+
+const struct snd_soc_dapm_route tavil_audio_map[] = {
+
+	/* MAD */
+	{"MAD_SEL MUX", "SPE", "MAD_CPE_INPUT"},
+	{"MAD_SEL MUX", "MSM", "MADINPUT"},
+
+	{"MAD_INP MUX", "MAD", "MAD_SEL MUX"},
+	{"MAD_INP MUX", "DEC1", "ADC MUX1"},
+
+	{"MAD_BROADCAST", "Switch", "MAD_INP MUX"},
+	{"MAD_CPE1", "Switch", "MAD_INP MUX"},
+	{"MAD_CPE2", "Switch", "MAD_INP MUX"},
+
+	{"MAD_CPE_OUT1", NULL, "MAD_CPE1"},
+	{"MAD_CPE_OUT2", NULL, "MAD_CPE2"},
+
+	/* VI Feedback */
+	{"AIF4_VI Mixer", "SPKR_VI_1", "VIINPUT"},
+	{"AIF4_VI Mixer", "SPKR_VI_2", "VIINPUT"},
+	{"AIF4 VI", NULL, "AIF4_VI Mixer"},
+
+	/* CDC Tx interface with SLIMBUS */
+	{"SLIM TX0", NULL, "CDC_IF TX0 MUX"},
+	{"SLIM TX1", NULL, "CDC_IF TX1 MUX"},
+	{"SLIM TX2", NULL, "CDC_IF TX2 MUX"},
+	{"SLIM TX3", NULL, "CDC_IF TX3 MUX"},
+	{"SLIM TX4", NULL, "CDC_IF TX4 MUX"},
+	{"SLIM TX5", NULL, "CDC_IF TX5 MUX"},
+	{"SLIM TX6", NULL, "CDC_IF TX6 MUX"},
+	{"SLIM TX7", NULL, "CDC_IF TX7 MUX"},
+	{"SLIM TX8", NULL, "CDC_IF TX8 MUX"},
+	{"SLIM TX9", NULL, "CDC_IF TX9 MUX"},
+	{"SLIM TX10", NULL, "CDC_IF TX10 MUX"},
+	{"SLIM TX11", NULL, "CDC_IF TX11 MUX"},
+	{"SLIM TX13", NULL, "CDC_IF TX13 MUX"},
+
+	{"CDC_IF TX0 MUX", "DEC0", "ADC MUX0"},
+	{"CDC_IF TX0 MUX", "RX_MIX_TX0", "RX MIX TX0 MUX"},
+	{"CDC_IF TX0 MUX", "DEC0_192", "ADC US MUX0"},
+
+	{"CDC_IF TX1 MUX", "DEC1", "ADC MUX1"},
+	{"CDC_IF TX1 MUX", "RX_MIX_TX1", "RX MIX TX1 MUX"},
+	{"CDC_IF TX1 MUX", "DEC1_192", "ADC US MUX1"},
+
+	{"CDC_IF TX2 MUX", "DEC2", "ADC MUX2"},
+	{"CDC_IF TX2 MUX", "RX_MIX_TX2", "RX MIX TX2 MUX"},
+	{"CDC_IF TX2 MUX", "DEC2_192", "ADC US MUX2"},
+
+	{"CDC_IF TX3 MUX", "DEC3", "ADC MUX3"},
+	{"CDC_IF TX3 MUX", "RX_MIX_TX3", "RX MIX TX3 MUX"},
+	{"CDC_IF TX3 MUX", "DEC3_192", "ADC US MUX3"},
+
+	{"CDC_IF TX4 MUX", "DEC4", "ADC MUX4"},
+	{"CDC_IF TX4 MUX", "RX_MIX_TX4", "RX MIX TX4 MUX"},
+	{"CDC_IF TX4 MUX", "DEC4_192", "ADC US MUX4"},
+
+	{"CDC_IF TX5 MUX", "DEC5", "ADC MUX5"},
+	{"CDC_IF TX5 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+	{"CDC_IF TX5 MUX", "DEC5_192", "ADC US MUX5"},
+
+	{"CDC_IF TX6 MUX", "DEC6", "ADC MUX6"},
+	{"CDC_IF TX6 MUX", "RX_MIX_TX6", "RX MIX TX6 MUX"},
+	{"CDC_IF TX6 MUX", "DEC6_192", "ADC US MUX6"},
+
+	{"CDC_IF TX7 MUX", "DEC7", "ADC MUX7"},
+	{"CDC_IF TX7 MUX", "RX_MIX_TX7", "RX MIX TX7 MUX"},
+	{"CDC_IF TX7 MUX", "DEC7_192", "ADC US MUX7"},
+
+	{"CDC_IF TX8 MUX", "DEC8", "ADC MUX8"},
+	{"CDC_IF TX8 MUX", "RX_MIX_TX8", "RX MIX TX8 MUX"},
+	{"CDC_IF TX8 MUX", "DEC8_192", "ADC US MUX8"},
+
+	{"CDC_IF TX9 MUX", "DEC7", "ADC MUX7"},
+	{"CDC_IF TX9 MUX", "DEC7_192", "ADC US MUX7"},
+	{"CDC_IF TX10 MUX", "DEC6", "ADC MUX6"},
+	{"CDC_IF TX10 MUX", "DEC6_192", "ADC US MUX6"},
+
+	{"CDC_IF TX11 MUX", "DEC_0_5", "CDC_IF TX11 INP1 MUX"},
+	{"CDC_IF TX11 MUX", "DEC_9_12", "CDC_IF TX11 INP1 MUX"},
+	{"CDC_IF TX11 INP1 MUX", "DEC0", "ADC MUX0"},
+	{"CDC_IF TX11 INP1 MUX", "DEC1", "ADC MUX1"},
+	{"CDC_IF TX11 INP1 MUX", "DEC2", "ADC MUX2"},
+	{"CDC_IF TX11 INP1 MUX", "DEC3", "ADC MUX3"},
+	{"CDC_IF TX11 INP1 MUX", "DEC4", "ADC MUX4"},
+	{"CDC_IF TX11 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"CDC_IF TX11 INP1 MUX", "RX_MIX_TX5", "RX MIX TX5 MUX"},
+
+	{"CDC_IF TX13 MUX", "MAD_BRDCST", "MAD_BROADCAST"},
+	{"CDC_IF TX13 MUX", "CDC_DEC_5", "CDC_IF TX13 INP1 MUX"},
+	{"CDC_IF TX13 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"CDC_IF TX13 INP1 MUX", "DEC5_192", "ADC US MUX5"},
+
+	{"RX MIX TX0 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX0 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX1 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX1 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX2 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX2 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX3 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX3 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX4 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX4 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX5 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX5 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX6 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX6 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX7 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX7 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"RX MIX TX8 MUX", "RX_MIX0", "RX INT0 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX1", "RX INT1 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX2", "RX INT2 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX3", "RX INT3 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX4", "RX INT4 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX7", "RX INT7 SEC MIX"},
+	{"RX MIX TX8 MUX", "RX_MIX8", "RX INT8 SEC MIX"},
+
+	{"ADC US MUX0", "US_Switch", "ADC MUX0"},
+	{"ADC US MUX1", "US_Switch", "ADC MUX1"},
+	{"ADC US MUX2", "US_Switch", "ADC MUX2"},
+	{"ADC US MUX3", "US_Switch", "ADC MUX3"},
+	{"ADC US MUX4", "US_Switch", "ADC MUX4"},
+	{"ADC US MUX5", "US_Switch", "ADC MUX5"},
+	{"ADC US MUX6", "US_Switch", "ADC MUX6"},
+	{"ADC US MUX7", "US_Switch", "ADC MUX7"},
+	{"ADC US MUX8", "US_Switch", "ADC MUX8"},
+
+	{"ADC MUX0", "DMIC", "DMIC MUX0"},
+	{"ADC MUX0", "AMIC", "AMIC MUX0"},
+	{"ADC MUX1", "DMIC", "DMIC MUX1"},
+	{"ADC MUX1", "AMIC", "AMIC MUX1"},
+	{"ADC MUX2", "DMIC", "DMIC MUX2"},
+	{"ADC MUX2", "AMIC", "AMIC MUX2"},
+	{"ADC MUX3", "DMIC", "DMIC MUX3"},
+	{"ADC MUX3", "AMIC", "AMIC MUX3"},
+	{"ADC MUX4", "DMIC", "DMIC MUX4"},
+	{"ADC MUX4", "AMIC", "AMIC MUX4"},
+	{"ADC MUX5", "DMIC", "DMIC MUX5"},
+	{"ADC MUX5", "AMIC", "AMIC MUX5"},
+	{"ADC MUX6", "DMIC", "DMIC MUX6"},
+	{"ADC MUX6", "AMIC", "AMIC MUX6"},
+	{"ADC MUX7", "DMIC", "DMIC MUX7"},
+	{"ADC MUX7", "AMIC", "AMIC MUX7"},
+	{"ADC MUX8", "DMIC", "DMIC MUX8"},
+	{"ADC MUX8", "AMIC", "AMIC MUX8"},
+	{"ADC MUX10", "DMIC", "DMIC MUX10"},
+	{"ADC MUX10", "AMIC", "AMIC MUX10"},
+	{"ADC MUX11", "DMIC", "DMIC MUX11"},
+	{"ADC MUX11", "AMIC", "AMIC MUX11"},
+	{"ADC MUX12", "DMIC", "DMIC MUX12"},
+	{"ADC MUX12", "AMIC", "AMIC MUX12"},
+	{"ADC MUX13", "DMIC", "DMIC MUX13"},
+	{"ADC MUX13", "AMIC", "AMIC MUX13"},
+
+	{"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX0", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX0", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX1", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX1", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX2", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX2", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX3", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX3", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX4", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX4", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX5", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX5", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX6", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX6", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX7", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX7", "ANC_FB_TUNE2", "ADC MUX13"},
+	{"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX10"},
+	{"ADC MUX8", "ANC_FB_TUNE1", "ADC MUX11"},
+	{"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX12"},
+	{"ADC MUX8", "ANC_FB_TUNE2", "ADC MUX13"},
+
+	{"DMIC MUX0", "DMIC0", "DMIC0"},
+	{"DMIC MUX0", "DMIC1", "DMIC1"},
+	{"DMIC MUX0", "DMIC2", "DMIC2"},
+	{"DMIC MUX0", "DMIC3", "DMIC3"},
+	{"DMIC MUX0", "DMIC4", "DMIC4"},
+	{"DMIC MUX0", "DMIC5", "DMIC5"},
+	{"AMIC MUX0", "ADC1", "ADC1"},
+	{"AMIC MUX0", "ADC2", "ADC2"},
+	{"AMIC MUX0", "ADC3", "ADC3"},
+	{"AMIC MUX0", "ADC4", "ADC4"},
+
+	{"DMIC MUX1", "DMIC0", "DMIC0"},
+	{"DMIC MUX1", "DMIC1", "DMIC1"},
+	{"DMIC MUX1", "DMIC2", "DMIC2"},
+	{"DMIC MUX1", "DMIC3", "DMIC3"},
+	{"DMIC MUX1", "DMIC4", "DMIC4"},
+	{"DMIC MUX1", "DMIC5", "DMIC5"},
+	{"AMIC MUX1", "ADC1", "ADC1"},
+	{"AMIC MUX1", "ADC2", "ADC2"},
+	{"AMIC MUX1", "ADC3", "ADC3"},
+	{"AMIC MUX1", "ADC4", "ADC4"},
+
+	{"DMIC MUX2", "DMIC0", "DMIC0"},
+	{"DMIC MUX2", "DMIC1", "DMIC1"},
+	{"DMIC MUX2", "DMIC2", "DMIC2"},
+	{"DMIC MUX2", "DMIC3", "DMIC3"},
+	{"DMIC MUX2", "DMIC4", "DMIC4"},
+	{"DMIC MUX2", "DMIC5", "DMIC5"},
+	{"AMIC MUX2", "ADC1", "ADC1"},
+	{"AMIC MUX2", "ADC2", "ADC2"},
+	{"AMIC MUX2", "ADC3", "ADC3"},
+	{"AMIC MUX2", "ADC4", "ADC4"},
+
+	{"DMIC MUX3", "DMIC0", "DMIC0"},
+	{"DMIC MUX3", "DMIC1", "DMIC1"},
+	{"DMIC MUX3", "DMIC2", "DMIC2"},
+	{"DMIC MUX3", "DMIC3", "DMIC3"},
+	{"DMIC MUX3", "DMIC4", "DMIC4"},
+	{"DMIC MUX3", "DMIC5", "DMIC5"},
+	{"AMIC MUX3", "ADC1", "ADC1"},
+	{"AMIC MUX3", "ADC2", "ADC2"},
+	{"AMIC MUX3", "ADC3", "ADC3"},
+	{"AMIC MUX3", "ADC4", "ADC4"},
+
+	{"DMIC MUX4", "DMIC0", "DMIC0"},
+	{"DMIC MUX4", "DMIC1", "DMIC1"},
+	{"DMIC MUX4", "DMIC2", "DMIC2"},
+	{"DMIC MUX4", "DMIC3", "DMIC3"},
+	{"DMIC MUX4", "DMIC4", "DMIC4"},
+	{"DMIC MUX4", "DMIC5", "DMIC5"},
+	{"AMIC MUX4", "ADC1", "ADC1"},
+	{"AMIC MUX4", "ADC2", "ADC2"},
+	{"AMIC MUX4", "ADC3", "ADC3"},
+	{"AMIC MUX4", "ADC4", "ADC4"},
+
+	{"DMIC MUX5", "DMIC0", "DMIC0"},
+	{"DMIC MUX5", "DMIC1", "DMIC1"},
+	{"DMIC MUX5", "DMIC2", "DMIC2"},
+	{"DMIC MUX5", "DMIC3", "DMIC3"},
+	{"DMIC MUX5", "DMIC4", "DMIC4"},
+	{"DMIC MUX5", "DMIC5", "DMIC5"},
+	{"AMIC MUX5", "ADC1", "ADC1"},
+	{"AMIC MUX5", "ADC2", "ADC2"},
+	{"AMIC MUX5", "ADC3", "ADC3"},
+	{"AMIC MUX5", "ADC4", "ADC4"},
+
+	{"DMIC MUX6", "DMIC0", "DMIC0"},
+	{"DMIC MUX6", "DMIC1", "DMIC1"},
+	{"DMIC MUX6", "DMIC2", "DMIC2"},
+	{"DMIC MUX6", "DMIC3", "DMIC3"},
+	{"DMIC MUX6", "DMIC4", "DMIC4"},
+	{"DMIC MUX6", "DMIC5", "DMIC5"},
+	{"AMIC MUX6", "ADC1", "ADC1"},
+	{"AMIC MUX6", "ADC2", "ADC2"},
+	{"AMIC MUX6", "ADC3", "ADC3"},
+	{"AMIC MUX6", "ADC4", "ADC4"},
+
+	{"DMIC MUX7", "DMIC0", "DMIC0"},
+	{"DMIC MUX7", "DMIC1", "DMIC1"},
+	{"DMIC MUX7", "DMIC2", "DMIC2"},
+	{"DMIC MUX7", "DMIC3", "DMIC3"},
+	{"DMIC MUX7", "DMIC4", "DMIC4"},
+	{"DMIC MUX7", "DMIC5", "DMIC5"},
+	{"AMIC MUX7", "ADC1", "ADC1"},
+	{"AMIC MUX7", "ADC2", "ADC2"},
+	{"AMIC MUX7", "ADC3", "ADC3"},
+	{"AMIC MUX7", "ADC4", "ADC4"},
+
+	{"DMIC MUX8", "DMIC0", "DMIC0"},
+	{"DMIC MUX8", "DMIC1", "DMIC1"},
+	{"DMIC MUX8", "DMIC2", "DMIC2"},
+	{"DMIC MUX8", "DMIC3", "DMIC3"},
+	{"DMIC MUX8", "DMIC4", "DMIC4"},
+	{"DMIC MUX8", "DMIC5", "DMIC5"},
+	{"AMIC MUX8", "ADC1", "ADC1"},
+	{"AMIC MUX8", "ADC2", "ADC2"},
+	{"AMIC MUX8", "ADC3", "ADC3"},
+	{"AMIC MUX8", "ADC4", "ADC4"},
+
+	{"DMIC MUX10", "DMIC0", "DMIC0"},
+	{"DMIC MUX10", "DMIC1", "DMIC1"},
+	{"DMIC MUX10", "DMIC2", "DMIC2"},
+	{"DMIC MUX10", "DMIC3", "DMIC3"},
+	{"DMIC MUX10", "DMIC4", "DMIC4"},
+	{"DMIC MUX10", "DMIC5", "DMIC5"},
+	{"AMIC MUX10", "ADC1", "ADC1"},
+	{"AMIC MUX10", "ADC2", "ADC2"},
+	{"AMIC MUX10", "ADC3", "ADC3"},
+	{"AMIC MUX10", "ADC4", "ADC4"},
+
+	{"DMIC MUX11", "DMIC0", "DMIC0"},
+	{"DMIC MUX11", "DMIC1", "DMIC1"},
+	{"DMIC MUX11", "DMIC2", "DMIC2"},
+	{"DMIC MUX11", "DMIC3", "DMIC3"},
+	{"DMIC MUX11", "DMIC4", "DMIC4"},
+	{"DMIC MUX11", "DMIC5", "DMIC5"},
+	{"AMIC MUX11", "ADC1", "ADC1"},
+	{"AMIC MUX11", "ADC2", "ADC2"},
+	{"AMIC MUX11", "ADC3", "ADC3"},
+	{"AMIC MUX11", "ADC4", "ADC4"},
+
+	{"DMIC MUX12", "DMIC0", "DMIC0"},
+	{"DMIC MUX12", "DMIC1", "DMIC1"},
+	{"DMIC MUX12", "DMIC2", "DMIC2"},
+	{"DMIC MUX12", "DMIC3", "DMIC3"},
+	{"DMIC MUX12", "DMIC4", "DMIC4"},
+	{"DMIC MUX12", "DMIC5", "DMIC5"},
+	{"AMIC MUX12", "ADC1", "ADC1"},
+	{"AMIC MUX12", "ADC2", "ADC2"},
+	{"AMIC MUX12", "ADC3", "ADC3"},
+	{"AMIC MUX12", "ADC4", "ADC4"},
+
+	{"DMIC MUX13", "DMIC0", "DMIC0"},
+	{"DMIC MUX13", "DMIC1", "DMIC1"},
+	{"DMIC MUX13", "DMIC2", "DMIC2"},
+	{"DMIC MUX13", "DMIC3", "DMIC3"},
+	{"DMIC MUX13", "DMIC4", "DMIC4"},
+	{"DMIC MUX13", "DMIC5", "DMIC5"},
+	{"AMIC MUX13", "ADC1", "ADC1"},
+	{"AMIC MUX13", "ADC2", "ADC2"},
+	{"AMIC MUX13", "ADC3", "ADC3"},
+	{"AMIC MUX13", "ADC4", "ADC4"},
+
+	{"AMIC4_5 SEL", "AMIC4", "AMIC4"},
+	{"AMIC4_5 SEL", "AMIC5", "AMIC5"},
+
+	{"ADC1", NULL, "AMIC1"},
+	{"ADC2", NULL, "AMIC2"},
+	{"ADC3", NULL, "AMIC3"},
+	{"ADC4", NULL, "AMIC4_5 SEL"},
+
+	/* CDC Rx interface with SLIMBUS */
+	{"CDC_IF RX0 MUX", "SLIM RX0", "SLIM RX0"},
+	{"CDC_IF RX1 MUX", "SLIM RX1", "SLIM RX1"},
+	{"CDC_IF RX2 MUX", "SLIM RX2", "SLIM RX2"},
+	{"CDC_IF RX3 MUX", "SLIM RX3", "SLIM RX3"},
+	{"CDC_IF RX4 MUX", "SLIM RX4", "SLIM RX4"},
+	{"CDC_IF RX5 MUX", "SLIM RX5", "SLIM RX5"},
+	{"CDC_IF RX6 MUX", "SLIM RX6", "SLIM RX6"},
+	{"CDC_IF RX7 MUX", "SLIM RX7", "SLIM RX7"},
+
+	{"RX INT0_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT0_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT0_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT0_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT0_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT0_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT0_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT0_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT0_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT0_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT0_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT0_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT1_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT1_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT1_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT1_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT1_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT1_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT1_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT1_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT1_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT1_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT1_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT1_1 MIX1 INP2", "IIR1", "IIR1"},
+	{"RX INT2_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT2_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT2_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT2_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT2_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT2_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT2_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT2_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT2_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT2_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT2_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT2_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT3_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT3_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT3_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT3_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT3_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT3_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT3_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT3_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT3_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT3_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT3_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT3_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT4_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT4_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT4_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT4_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT4_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT4_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT4_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT4_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT4_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT4_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT4_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT4_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT7_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT7_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT7_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT7_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT7_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT7_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT7_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT7_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT7_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT7_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT7_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT7_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT8_1 MIX1 INP0", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT8_1 MIX1 INP0", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT8_1 MIX1 INP0", "IIR0", "IIR0"},
+	{"RX INT8_1 MIX1 INP0", "IIR1", "IIR1"},
+	{"RX INT8_1 MIX1 INP1", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT8_1 MIX1 INP1", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT8_1 MIX1 INP1", "IIR0", "IIR0"},
+	{"RX INT8_1 MIX1 INP1", "IIR1", "IIR1"},
+	{"RX INT8_1 MIX1 INP2", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT8_1 MIX1 INP2", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT8_1 MIX1 INP2", "IIR0", "IIR0"},
+	{"RX INT8_1 MIX1 INP2", "IIR1", "IIR1"},
+
+	{"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP0"},
+	{"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP1"},
+	{"RX INT0_1 MIX1", NULL, "RX INT0_1 MIX1 INP2"},
+	{"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP0"},
+	{"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP1"},
+	{"RX INT1_1 MIX1", NULL, "RX INT1_1 MIX1 INP2"},
+	{"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP0"},
+	{"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP1"},
+	{"RX INT2_1 MIX1", NULL, "RX INT2_1 MIX1 INP2"},
+	{"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP0"},
+	{"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP1"},
+	{"RX INT3_1 MIX1", NULL, "RX INT3_1 MIX1 INP2"},
+	{"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP0"},
+	{"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP1"},
+	{"RX INT4_1 MIX1", NULL, "RX INT4_1 MIX1 INP2"},
+	{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP0"},
+	{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP1"},
+	{"RX INT7_1 MIX1", NULL, "RX INT7_1 MIX1 INP2"},
+	{"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP0"},
+	{"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP1"},
+	{"RX INT8_1 MIX1", NULL, "RX INT8_1 MIX1 INP2"},
+
+	/* Mixing path INT0 */
+	{"RX INT0_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT0_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT0_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT0_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT0_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT0_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT0_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT0_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT0 SEC MIX", NULL, "RX INT0_2 MUX"},
+
+	/* Mixing path INT1 */
+	{"RX INT1_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT1_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT1_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT1_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT1_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT1_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT1_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT1_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT1_2 INTERP", NULL, "RX INT1_2 MUX"},
+	{"RX INT1 SEC MIX", NULL, "RX INT1_2 INTERP"},
+
+	/* Mixing path INT2 */
+	{"RX INT2_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT2_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT2_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT2_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT2_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT2_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT2_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT2_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT2_2 INTERP", NULL, "RX INT2_2 MUX"},
+	{"RX INT2 SEC MIX", NULL, "RX INT2_2 INTERP"},
+
+	/* Mixing path INT3 */
+	{"RX INT3_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT3_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT3_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT3_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT3_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT3_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT3_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT3_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT3_2 INTERP", NULL, "RX INT3_2 MUX"},
+	{"RX INT3 SEC MIX", NULL, "RX INT3_2 INTERP"},
+
+	/* Mixing path INT4 */
+	{"RX INT4_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT4_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT4_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT4_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT4_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT4_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT4_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT4_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT4_2 INTERP", NULL, "RX INT4_2 MUX"},
+	{"RX INT4 SEC MIX", NULL, "RX INT4_2 INTERP"},
+
+	/* Mixing path INT7 */
+	{"RX INT7_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT7_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT7_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT7_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT7_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT7_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT7_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT7_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT7_2 INTERP", NULL, "RX INT7_2 MUX"},
+	{"RX INT7 SEC MIX", NULL, "RX INT7_2 INTERP"},
+
+	/* Mixing path INT8 */
+	{"RX INT8_2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"RX INT8_2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"RX INT8_2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"RX INT8_2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"RX INT8_2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"RX INT8_2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"RX INT8_2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"RX INT8_2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"RX INT8_2 INTERP", NULL, "RX INT8_2 MUX"},
+	{"RX INT8 SEC MIX", NULL, "RX INT8_2 INTERP"},
+
+	{"RX INT0_1 INTERP", NULL, "RX INT0_1 MIX1"},
+	{"RX INT0 SEC MIX", NULL, "RX INT0_1 INTERP"},
+	{"RX INT0 MIX2", NULL, "RX INT0 SEC MIX"},
+	{"RX INT0 MIX2", NULL, "RX INT0 MIX2 INP"},
+	{"RX INT0 DEM MUX", "CLSH_DSM_OUT", "RX INT0 MIX2"},
+	{"RX INT0 DAC", NULL, "RX INT0 DEM MUX"},
+	{"RX INT0 DAC", NULL, "RX_BIAS"},
+	{"EAR PA", NULL, "RX INT0 DAC"},
+	{"EAR", NULL, "EAR PA"},
+
+	{"RX INT1_1 INTERP", NULL, "RX INT1_1 MIX1"},
+	{"RX INT1 SEC MIX", NULL, "RX INT1_1 INTERP"},
+	{"RX INT1 MIX2", NULL, "RX INT1 SEC MIX"},
+	{"RX INT1 MIX2", NULL, "RX INT1 MIX2 INP"},
+	{"RX INT1 MIX3", NULL, "RX INT1 MIX2"},
+	{"RX INT1 DEM MUX", "CLSH_DSM_OUT", "RX INT1 MIX3"},
+	{"RX INT1 DAC", NULL, "RX INT1 DEM MUX"},
+	{"RX INT1 DAC", NULL, "RX_BIAS"},
+	{"HPHL PA", NULL, "RX INT1 DAC"},
+	{"HPHL", NULL, "HPHL PA"},
+
+	{"RX INT2_1 INTERP", NULL, "RX INT2_1 MIX1"},
+	{"RX INT2 SEC MIX", NULL, "RX INT2_1 INTERP"},
+	{"RX INT2 MIX2", NULL, "RX INT2 SEC MIX"},
+	{"RX INT2 MIX2", NULL, "RX INT2 MIX2 INP"},
+	{"RX INT2 MIX3", NULL, "RX INT2 MIX2"},
+	{"RX INT2 DEM MUX", "CLSH_DSM_OUT", "RX INT2 MIX3"},
+	{"RX INT2 DAC", NULL, "RX INT2 DEM MUX"},
+	{"RX INT2 DAC", NULL, "RX_BIAS"},
+	{"HPHR PA", NULL, "RX INT2 DAC"},
+	{"HPHR", NULL, "HPHR PA"},
+
+	{"RX INT3_1 INTERP", NULL, "RX INT3_1 MIX1"},
+	{"RX INT3 SEC MIX", NULL, "RX INT3_1 INTERP"},
+	{"RX INT3 MIX2", NULL, "RX INT3 SEC MIX"},
+	{"RX INT3 MIX2", NULL, "RX INT3 MIX2 INP"},
+	{"RX INT3 DAC", NULL, "RX INT3 MIX2"},
+	{"RX INT3 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT1 PA", NULL, "RX INT3 DAC"},
+	{"LINEOUT1", NULL, "LINEOUT1 PA"},
+
+	{"RX INT4_1 INTERP", NULL, "RX INT4_1 MIX1"},
+	{"RX INT4 SEC MIX", NULL, "RX INT4_1 INTERP"},
+	{"RX INT4 SEC MIX", NULL, "RX INT4_1 MIX1"},
+	{"RX INT4 MIX2", NULL, "RX INT4 SEC MIX"},
+	{"RX INT4 MIX2", NULL, "RX INT4 MIX2 INP"},
+	{"RX INT4 DAC", NULL, "RX INT4 MIX2"},
+	{"RX INT4 DAC", NULL, "RX_BIAS"},
+	{"LINEOUT2 PA", NULL, "RX INT4 DAC"},
+	{"LINEOUT2", NULL, "LINEOUT2 PA"},
+
+	{"RX INT7_1 INTERP", NULL, "RX INT7_1 MIX1"},
+	{"RX INT7 SEC MIX", NULL, "RX INT7_1 INTERP"},
+	{"RX INT7 MIX2", NULL, "RX INT7 SEC MIX"},
+	{"RX INT7 MIX2", NULL, "RX INT7 MIX2 INP"},
+	{"RX INT7 CHAIN", NULL, "RX INT7 MIX2"},
+	{"RX INT7 CHAIN", NULL, "RX_BIAS"},
+	{"SPK1 OUT", NULL, "RX INT7 CHAIN"},
+
+	{"RX INT8_1 INTERP", NULL, "RX INT8_1 MIX1"},
+	{"RX INT8 SEC MIX", NULL, "RX INT8_1 INTERP"},
+	{"RX INT8 SEC MIX", NULL, "RX INT8_1 MIX1"},
+	{"RX INT8 CHAIN", NULL, "RX INT8 SEC MIX"},
+	{"RX INT8 CHAIN", NULL, "RX_BIAS"},
+	{"SPK2 OUT", NULL, "RX INT8 CHAIN"},
+
+	/* ANC Routing */
+	{"ANC0 FB MUX", "ANC_IN_EAR", "RX INT0 MIX2"},
+	{"ANC0 FB MUX", "ANC_IN_HPHL", "RX INT1 MIX2"},
+	{"ANC0 FB MUX", "ANC_IN_LO1", "RX INT3 MIX2"},
+	{"ANC0 FB MUX", "ANC_IN_EAR_SPKR", "RX INT7 MIX2"},
+	{"ANC1 FB MUX", "ANC_IN_HPHR", "RX INT2 MIX2"},
+	{"ANC1 FB MUX", "ANC_IN_LO2", "RX INT4 MIX2"},
+
+	{"ANC OUT EAR Enable", "Switch", "ADC MUX10"},
+	{"ANC OUT EAR Enable", "Switch", "ADC MUX11"},
+	{"RX INT0 MIX2", NULL, "ANC OUT EAR Enable"},
+
+	{"ANC EAR PA", NULL, "RX INT0 DAC"},
+	{"ANC EAR", NULL, "ANC EAR PA"},
+
+	{"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX10"},
+	{"ANC OUT EAR SPKR Enable", "Switch", "ADC MUX11"},
+	{"RX INT7 MIX2", NULL, "ANC OUT EAR SPKR Enable"},
+
+	{"ANC SPKR PA Enable", "Switch", "RX INT7 CHAIN"},
+	{"ANC SPK1 PA", NULL, "ANC SPKR PA Enable"},
+	{"SPK1 OUT", NULL, "ANC SPK1 PA"},
+
+	/*
+	 * SRC0, SRC1 inputs to Sidetone RX Mixer
+	 * on RX0, RX1, RX2, RX3, RX4 and RX7 chains
+	 */
+	{"IIR0", NULL, "IIR0 INP0 MUX"},
+	{"IIR0 INP0 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP0 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP0 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP0 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP0 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP0 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP0 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP0 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP0 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP0 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR0 INP0 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR0 INP0 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR0 INP0 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR0 INP0 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR0 INP0 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR0 INP0 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR0 INP0 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"IIR0", NULL, "IIR0 INP1 MUX"},
+	{"IIR0 INP1 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP1 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP1 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP1 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP1 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP1 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP1 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP1 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP1 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR0 INP1 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR0 INP1 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR0 INP1 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR0 INP1 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR0 INP1 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR0 INP1 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR0 INP1 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"IIR0", NULL, "IIR0 INP2 MUX"},
+	{"IIR0 INP2 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP2 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP2 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP2 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP2 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP2 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP2 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP2 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP2 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR0 INP2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR0 INP2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR0 INP2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR0 INP2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR0 INP2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR0 INP2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR0 INP2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"IIR0", NULL, "IIR0 INP3 MUX"},
+	{"IIR0 INP3 MUX", "DEC0", "ADC MUX0"},
+	{"IIR0 INP3 MUX", "DEC1", "ADC MUX1"},
+	{"IIR0 INP3 MUX", "DEC2", "ADC MUX2"},
+	{"IIR0 INP3 MUX", "DEC3", "ADC MUX3"},
+	{"IIR0 INP3 MUX", "DEC4", "ADC MUX4"},
+	{"IIR0 INP3 MUX", "DEC5", "ADC MUX5"},
+	{"IIR0 INP3 MUX", "DEC6", "ADC MUX6"},
+	{"IIR0 INP3 MUX", "DEC7", "ADC MUX7"},
+	{"IIR0 INP3 MUX", "DEC8", "ADC MUX8"},
+	{"IIR0 INP3 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR0 INP3 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR0 INP3 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR0 INP3 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR0 INP3 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR0 INP3 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR0 INP3 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR0 INP3 MUX", "RX7", "CDC_IF RX7 MUX"},
+
+	{"IIR1", NULL, "IIR1 INP0 MUX"},
+	{"IIR1 INP0 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP0 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP0 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP0 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP0 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP0 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP0 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP0 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP0 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP0 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR1 INP0 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR1 INP0 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR1 INP0 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR1 INP0 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR1 INP0 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR1 INP0 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR1 INP0 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"IIR1", NULL, "IIR1 INP1 MUX"},
+	{"IIR1 INP1 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP1 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP1 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP1 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP1 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP1 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP1 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP1 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP1 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP1 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR1 INP1 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR1 INP1 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR1 INP1 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR1 INP1 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR1 INP1 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR1 INP1 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR1 INP1 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"IIR1", NULL, "IIR1 INP2 MUX"},
+	{"IIR1 INP2 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP2 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP2 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP2 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP2 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP2 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP2 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP2 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP2 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP2 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR1 INP2 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR1 INP2 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR1 INP2 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR1 INP2 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR1 INP2 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR1 INP2 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR1 INP2 MUX", "RX7", "CDC_IF RX7 MUX"},
+	{"IIR1", NULL, "IIR1 INP3 MUX"},
+	{"IIR1 INP3 MUX", "DEC0", "ADC MUX0"},
+	{"IIR1 INP3 MUX", "DEC1", "ADC MUX1"},
+	{"IIR1 INP3 MUX", "DEC2", "ADC MUX2"},
+	{"IIR1 INP3 MUX", "DEC3", "ADC MUX3"},
+	{"IIR1 INP3 MUX", "DEC4", "ADC MUX4"},
+	{"IIR1 INP3 MUX", "DEC5", "ADC MUX5"},
+	{"IIR1 INP3 MUX", "DEC6", "ADC MUX6"},
+	{"IIR1 INP3 MUX", "DEC7", "ADC MUX7"},
+	{"IIR1 INP3 MUX", "DEC8", "ADC MUX8"},
+	{"IIR1 INP3 MUX", "RX0", "CDC_IF RX0 MUX"},
+	{"IIR1 INP3 MUX", "RX1", "CDC_IF RX1 MUX"},
+	{"IIR1 INP3 MUX", "RX2", "CDC_IF RX2 MUX"},
+	{"IIR1 INP3 MUX", "RX3", "CDC_IF RX3 MUX"},
+	{"IIR1 INP3 MUX", "RX4", "CDC_IF RX4 MUX"},
+	{"IIR1 INP3 MUX", "RX5", "CDC_IF RX5 MUX"},
+	{"IIR1 INP3 MUX", "RX6", "CDC_IF RX6 MUX"},
+	{"IIR1 INP3 MUX", "RX7", "CDC_IF RX7 MUX"},
+
+	{"SRC0", NULL, "IIR0"},
+	{"SRC1", NULL, "IIR1"},
+	{"RX INT0 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT0 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT1 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT1 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT2 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT2 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT3 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT3 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT4 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT4 MIX2 INP", "SRC1", "SRC1"},
+	{"RX INT7 MIX2 INP", "SRC0", "SRC0"},
+	{"RX INT7 MIX2 INP", "SRC1", "SRC1"},
+
+	/* Native clk main path routing */
+	{"RX INT1_1 NATIVE MUX", "ON", "RX INT1_1 MIX1"},
+	{"RX INT1_1 INTERP", NULL, "RX INT1_1 NATIVE MUX"},
+	{"RX INT1_1 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
+
+	{"RX INT2_1 NATIVE MUX", "ON", "RX INT2_1 MIX1"},
+	{"RX INT2_1 INTERP", NULL, "RX INT2_1 NATIVE MUX"},
+	{"RX INT2_1 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
+
+	{"RX INT3_1 NATIVE MUX", "ON", "RX INT3_1 MIX1"},
+	{"RX INT3_1 INTERP", NULL, "RX INT3_1 NATIVE MUX"},
+	{"RX INT3_1 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
+
+	{"RX INT4_1 NATIVE MUX", "ON", "RX INT4_1 MIX1"},
+	{"RX INT4_1 INTERP", NULL, "RX INT4_1 NATIVE MUX"},
+	{"RX INT4_1 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
+
+	/* Native clk mix path routing */
+	{"RX INT1_2 NATIVE MUX", "ON", "RX INT1_2 MUX"},
+	{"RX INT1_2 INTERP", NULL, "RX INT1_2 NATIVE MUX"},
+	{"RX INT1_2 NATIVE MUX", NULL, "RX INT1 NATIVE SUPPLY"},
+
+	{"RX INT2_2 NATIVE MUX", "ON", "RX INT2_2 MUX"},
+	{"RX INT2_2 INTERP", NULL, "RX INT2_2 NATIVE MUX"},
+	{"RX INT2_2 NATIVE MUX", NULL, "RX INT2 NATIVE SUPPLY"},
+
+	{"RX INT3_2 NATIVE MUX", "ON", "RX INT3_2 MUX"},
+	{"RX INT3_2 INTERP", NULL, "RX INT3_2 NATIVE MUX"},
+	{"RX INT3_2 NATIVE MUX", NULL, "RX INT3 NATIVE SUPPLY"},
+
+	{"RX INT4_2 NATIVE MUX", "ON", "RX INT4_2 MUX"},
+	{"RX INT4_2 INTERP", NULL, "RX INT4_2 NATIVE MUX"},
+	{"RX INT4_2 NATIVE MUX", NULL, "RX INT4 NATIVE SUPPLY"},
+
+	{"RX INT7_2 NATIVE MUX", "ON", "RX INT7_2 MUX"},
+	{"RX INT7_2 INTERP", NULL, "RX INT7_2 NATIVE MUX"},
+	{"RX INT7_2 NATIVE MUX", NULL, "RX INT7 NATIVE SUPPLY"},
+
+	{"RX INT8_2 NATIVE MUX", "ON", "RX INT8_2 MUX"},
+	{"RX INT8_2 INTERP", NULL, "RX INT8_2 NATIVE MUX"},
+	{"RX INT8_2 NATIVE MUX", NULL, "RX INT8 NATIVE SUPPLY"},
+
+	/* ASRC Routing */
+	{"ASRC0 MUX", "ASRC_IN_HPHL", "RX INT1_2 INTERP"},
+	{"RX INT1 SEC MIX", "HPHL Switch", "ASRC0 MUX"},
+
+	{"ASRC1 MUX", "ASRC_IN_HPHR", "RX INT2_2 INTERP"},
+	{"RX INT2 SEC MIX", "HPHR Switch", "ASRC1 MUX"},
+
+	{"ASRC0 MUX", "ASRC_IN_LO1", "RX INT3_2 INTERP"},
+	{"RX INT3 SEC MIX", "LO1 Switch", "ASRC0 MUX"},
+
+	{"ASRC1 MUX", "ASRC_IN_LO2", "RX INT4_2 INTERP"},
+	{"RX INT4 SEC MIX", "LO2 Switch", "ASRC1 MUX"},
+
+	{"ASRC2 MUX", "ASRC_IN_SPKR1", "RX INT7_2 INTERP"},
+	{"RX INT7 SEC MIX", NULL, "ASRC2 MUX"},
+
+	{"ASRC3 MUX", "ASRC_IN_SPKR2", "RX INT8_2 INTERP"},
+	{"RX INT8 SEC MIX", NULL, "ASRC3 MUX"},
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd934x/wcd934x.c b/sound/soc/codecs/wcd934x/wcd934x.c
new file mode 100644
index 0000000..58b08c9
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x.c
@@ -0,0 +1,9747 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/wait.h>
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <linux/regulator/consumer.h>
+#include <linux/soundwire/swr-wcd.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/info.h>
+#include "wcd934x.h"
+#include "wcd934x-mbhc.h"
+#include "wcd934x-routing.h"
+#include "wcd934x-dsp-cntl.h"
+#include "../wcd9xxx-common-v2.h"
+#include "../wcd9xxx-resmgr-v2.h"
+#include "../wcdcal-hwdep.h"
+#include "wcd934x-dsd.h"
+
+#define WCD934X_RATES_MASK (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+			    SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\
+			    SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\
+			    SNDRV_PCM_RATE_384000)
+/* Fractional Rates */
+#define WCD934X_FRAC_RATES_MASK (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\
+				 SNDRV_PCM_RATE_176400)
+
+#define WCD934X_FORMATS_S16_S24_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+				    SNDRV_PCM_FMTBIT_S24_LE)
+
+#define WCD934X_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+					SNDRV_PCM_FMTBIT_S24_LE | \
+					SNDRV_PCM_FMTBIT_S32_LE)
+
+#define WCD934X_FORMATS_S16_LE (SNDRV_PCM_FMTBIT_S16_LE)
+
+/* Macros for packing register writes into a U32 */
+#define WCD934X_PACKED_REG_SIZE sizeof(u32)
+#define WCD934X_CODEC_UNPACK_ENTRY(packed, reg, mask, val) \
+	do { \
+		((reg) = ((packed >> 16) & (0xffff))); \
+		((mask) = ((packed >> 8) & (0xff))); \
+		((val) = ((packed) & (0xff))); \
+	} while (0)
+
+#define STRING(name) #name
+#define WCD_DAPM_ENUM(name, reg, offset, text) \
+static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \
+static const struct snd_kcontrol_new name##_mux = \
+		SOC_DAPM_ENUM(STRING(name), name##_enum)
+
+#define WCD_DAPM_ENUM_EXT(name, reg, offset, text, getname, putname) \
+static SOC_ENUM_SINGLE_DECL(name##_enum, reg, offset, text); \
+static const struct snd_kcontrol_new name##_mux = \
+		SOC_DAPM_ENUM_EXT(STRING(name), name##_enum, getname, putname)
+
+#define WCD_DAPM_MUX(name, shift, kctl) \
+		SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, shift, 0, &kctl##_mux)
+
+/*
+ * Timeout in milli seconds and it is the wait time for
+ * slim channel removal interrupt to receive.
+ */
+#define WCD934X_SLIM_CLOSE_TIMEOUT 1000
+#define WCD934X_SLIM_IRQ_OVERFLOW (1 << 0)
+#define WCD934X_SLIM_IRQ_UNDERFLOW (1 << 1)
+#define WCD934X_SLIM_IRQ_PORT_CLOSED (1 << 2)
+#define WCD934X_MCLK_CLK_12P288MHZ 12288000
+#define WCD934X_MCLK_CLK_9P6MHZ 9600000
+
+#define WCD934X_INTERP_MUX_NUM_INPUTS 3
+#define WCD934X_NUM_INTERPOLATORS 9
+#define WCD934X_NUM_DECIMATORS 9
+#define WCD934X_RX_PATH_CTL_OFFSET 20
+
+#define BYTE_BIT_MASK(nr) (1 << ((nr) % BITS_PER_BYTE))
+
+#define WCD934X_REG_BITS 8
+#define WCD934X_MAX_VALID_ADC_MUX  13
+#define WCD934X_INVALID_ADC_MUX 9
+
+#define WCD934X_AMIC_PWR_LEVEL_LP 0
+#define WCD934X_AMIC_PWR_LEVEL_DEFAULT 1
+#define WCD934X_AMIC_PWR_LEVEL_HP 2
+#define WCD934X_AMIC_PWR_LVL_MASK 0x60
+#define WCD934X_AMIC_PWR_LVL_SHIFT 0x5
+
+#define WCD934X_DEC_PWR_LVL_MASK 0x06
+#define WCD934X_DEC_PWR_LVL_LP 0x02
+#define WCD934X_DEC_PWR_LVL_HP 0x04
+#define WCD934X_DEC_PWR_LVL_DF 0x00
+#define WCD934X_STRING_LEN 100
+
+#define WCD934X_DIG_CORE_REG_MIN  WCD934X_CDC_ANC0_CLK_RESET_CTL
+#define WCD934X_DIG_CORE_REG_MAX  0xFFF
+
+#define WCD934X_MAX_MICBIAS 4
+#define DAPM_MICBIAS1_STANDALONE "MIC BIAS1 Standalone"
+#define DAPM_MICBIAS2_STANDALONE "MIC BIAS2 Standalone"
+#define DAPM_MICBIAS3_STANDALONE "MIC BIAS3 Standalone"
+#define DAPM_MICBIAS4_STANDALONE "MIC BIAS4 Standalone"
+
+#define  TX_HPF_CUT_OFF_FREQ_MASK	0x60
+#define  CF_MIN_3DB_4HZ			0x0
+#define  CF_MIN_3DB_75HZ		0x1
+#define  CF_MIN_3DB_150HZ		0x2
+
+#define CPE_ERR_WDOG_BITE BIT(0)
+#define CPE_FATAL_IRQS CPE_ERR_WDOG_BITE
+
+#define WCD934X_MAD_AUDIO_FIRMWARE_PATH "wcd934x/wcd934x_mad_audio.bin"
+
+#define TAVIL_VERSION_ENTRY_SIZE 17
+
+#define WCD934X_DIG_CORE_COLLAPSE_TIMER_MS  (5 * 1000)
+
+enum {
+	POWER_COLLAPSE,
+	POWER_RESUME,
+};
+
+static int dig_core_collapse_enable = 1;
+module_param(dig_core_collapse_enable, int, 0664);
+MODULE_PARM_DESC(dig_core_collapse_enable, "enable/disable power gating");
+
+/* dig_core_collapse timer in seconds */
+static int dig_core_collapse_timer = (WCD934X_DIG_CORE_COLLAPSE_TIMER_MS/1000);
+module_param(dig_core_collapse_timer, int, 0664);
+MODULE_PARM_DESC(dig_core_collapse_timer, "timer for power gating");
+
+#define TAVIL_HPH_REG_RANGE_1  (WCD934X_HPH_R_DAC_CTL - WCD934X_HPH_CNP_EN + 1)
+#define TAVIL_HPH_REG_RANGE_2  (WCD934X_HPH_NEW_ANA_HPH3 -\
+				WCD934X_HPH_NEW_ANA_HPH2 + 1)
+#define TAVIL_HPH_REG_RANGE_3  (WCD934X_HPH_NEW_INT_PA_RDAC_MISC3 -\
+				WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL + 1)
+#define TAVIL_HPH_TOTAL_REG    (TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2 +\
+				TAVIL_HPH_REG_RANGE_3)
+
+enum {
+	VI_SENSE_1,
+	VI_SENSE_2,
+	AUDIO_NOMINAL,
+	HPH_PA_DELAY,
+};
+
+enum {
+	AIF1_PB = 0,
+	AIF1_CAP,
+	AIF2_PB,
+	AIF2_CAP,
+	AIF3_PB,
+	AIF3_CAP,
+	AIF4_PB,
+	AIF4_VIFEED,
+	AIF4_MAD_TX,
+	NUM_CODEC_DAIS,
+};
+
+enum {
+	INTn_1_INP_SEL_ZERO = 0,
+	INTn_1_INP_SEL_DEC0,
+	INTn_1_INP_SEL_DEC1,
+	INTn_1_INP_SEL_IIR0,
+	INTn_1_INP_SEL_IIR1,
+	INTn_1_INP_SEL_RX0,
+	INTn_1_INP_SEL_RX1,
+	INTn_1_INP_SEL_RX2,
+	INTn_1_INP_SEL_RX3,
+	INTn_1_INP_SEL_RX4,
+	INTn_1_INP_SEL_RX5,
+	INTn_1_INP_SEL_RX6,
+	INTn_1_INP_SEL_RX7,
+};
+
+enum {
+	INTn_2_INP_SEL_ZERO = 0,
+	INTn_2_INP_SEL_RX0,
+	INTn_2_INP_SEL_RX1,
+	INTn_2_INP_SEL_RX2,
+	INTn_2_INP_SEL_RX3,
+	INTn_2_INP_SEL_RX4,
+	INTn_2_INP_SEL_RX5,
+	INTn_2_INP_SEL_RX6,
+	INTn_2_INP_SEL_RX7,
+	INTn_2_INP_SEL_PROXIMITY,
+};
+
+enum {
+	INTERP_MAIN_PATH,
+	INTERP_MIX_PATH,
+};
+
+struct tavil_idle_detect_config {
+	u8 hph_idle_thr;
+	u8 hph_idle_detect_en;
+};
+
+static const struct intr_data wcd934x_intr_table[] = {
+	{WCD9XXX_IRQ_SLIMBUS, false},
+	{WCD934X_IRQ_MBHC_SW_DET, true},
+	{WCD934X_IRQ_MBHC_BUTTON_PRESS_DET, true},
+	{WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET, true},
+	{WCD934X_IRQ_MBHC_ELECT_INS_REM_DET, true},
+	{WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET, true},
+	{WCD934X_IRQ_MISC, false},
+	{WCD934X_IRQ_HPH_PA_CNPL_COMPLETE, false},
+	{WCD934X_IRQ_HPH_PA_CNPR_COMPLETE, false},
+	{WCD934X_IRQ_EAR_PA_CNP_COMPLETE, false},
+	{WCD934X_IRQ_LINE_PA1_CNP_COMPLETE, false},
+	{WCD934X_IRQ_LINE_PA2_CNP_COMPLETE, false},
+	{WCD934X_IRQ_SLNQ_ANALOG_ERROR, false},
+	{WCD934X_IRQ_RESERVED_3, false},
+	{WCD934X_IRQ_HPH_PA_OCPL_FAULT, false},
+	{WCD934X_IRQ_HPH_PA_OCPR_FAULT, false},
+	{WCD934X_IRQ_EAR_PA_OCP_FAULT, false},
+	{WCD934X_IRQ_SOUNDWIRE, false},
+	{WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE, false},
+	{WCD934X_IRQ_RCO_ERROR, false},
+	{WCD934X_IRQ_CPE_ERROR, false},
+	{WCD934X_IRQ_MAD_AUDIO, false},
+	{WCD934X_IRQ_MAD_BEACON, false},
+	{WCD934X_IRQ_CPE1_INTR, true},
+	{WCD934X_IRQ_RESERVED_4, false},
+	{WCD934X_IRQ_MAD_ULTRASOUND, false},
+	{WCD934X_IRQ_VBAT_ATTACK, false},
+	{WCD934X_IRQ_VBAT_RESTORE, false},
+};
+
+struct tavil_cpr_reg_defaults {
+	int wr_data;
+	int wr_addr;
+};
+
+struct interp_sample_rate {
+	int sample_rate;
+	int rate_val;
+};
+
+static struct interp_sample_rate sr_val_tbl[] = {
+	{8000, 0x0}, {16000, 0x1}, {32000, 0x3}, {48000, 0x4}, {96000, 0x5},
+	{192000, 0x6}, {384000, 0x7}, {44100, 0x9}, {88200, 0xA},
+	{176400, 0xB}, {352800, 0xC},
+};
+
+static const struct wcd9xxx_ch tavil_rx_chs[WCD934X_RX_MAX] = {
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER, 0),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 1, 1),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 2, 2),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 3, 3),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 4, 4),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 5, 5),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 6, 6),
+	WCD9XXX_CH(WCD934X_RX_PORT_START_NUMBER + 7, 7),
+};
+
+static const struct wcd9xxx_ch tavil_tx_chs[WCD934X_TX_MAX] = {
+	WCD9XXX_CH(0, 0),
+	WCD9XXX_CH(1, 1),
+	WCD9XXX_CH(2, 2),
+	WCD9XXX_CH(3, 3),
+	WCD9XXX_CH(4, 4),
+	WCD9XXX_CH(5, 5),
+	WCD9XXX_CH(6, 6),
+	WCD9XXX_CH(7, 7),
+	WCD9XXX_CH(8, 8),
+	WCD9XXX_CH(9, 9),
+	WCD9XXX_CH(10, 10),
+	WCD9XXX_CH(11, 11),
+	WCD9XXX_CH(12, 12),
+	WCD9XXX_CH(13, 13),
+	WCD9XXX_CH(14, 14),
+	WCD9XXX_CH(15, 15),
+};
+
+static const u32 vport_slim_check_table[NUM_CODEC_DAIS] = {
+	0,							/* AIF1_PB */
+	BIT(AIF2_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX),	/* AIF1_CAP */
+	0,							/* AIF2_PB */
+	BIT(AIF1_CAP) | BIT(AIF3_CAP) | BIT(AIF4_MAD_TX),	/* AIF2_CAP */
+	0,							/* AIF3_PB */
+	BIT(AIF1_CAP) | BIT(AIF2_CAP) | BIT(AIF4_MAD_TX),	/* AIF3_CAP */
+	0,							/* AIF4_PB */
+};
+
+/* Codec supports 2 IIR filters */
+enum {
+	IIR0 = 0,
+	IIR1,
+	IIR_MAX,
+};
+
+/* Each IIR has 5 Filter Stages */
+enum {
+	BAND1 = 0,
+	BAND2,
+	BAND3,
+	BAND4,
+	BAND5,
+	BAND_MAX,
+};
+
+enum {
+	COMPANDER_1, /* HPH_L */
+	COMPANDER_2, /* HPH_R */
+	COMPANDER_3, /* LO1_DIFF */
+	COMPANDER_4, /* LO2_DIFF */
+	COMPANDER_5, /* LO3_SE - not used in Tavil */
+	COMPANDER_6, /* LO4_SE - not used in Tavil */
+	COMPANDER_7, /* SWR SPK CH1 */
+	COMPANDER_8, /* SWR SPK CH2 */
+	COMPANDER_MAX,
+};
+
+enum {
+	ASRC_IN_HPHL,
+	ASRC_IN_LO1,
+	ASRC_IN_HPHR,
+	ASRC_IN_LO2,
+	ASRC_IN_SPKR1,
+	ASRC_IN_SPKR2,
+	ASRC_INVALID,
+};
+
+enum {
+	ASRC0,
+	ASRC1,
+	ASRC2,
+	ASRC3,
+	ASRC_MAX,
+};
+
+enum {
+	CONV_88P2K_TO_384K,
+	CONV_96K_TO_352P8K,
+	CONV_352P8K_TO_384K,
+	CONV_384K_TO_352P8K,
+	CONV_384K_TO_384K,
+	CONV_96K_TO_384K,
+};
+
+static struct afe_param_slimbus_slave_port_cfg tavil_slimbus_slave_port_cfg = {
+	.minor_version = 1,
+	.slimbus_dev_id = AFE_SLIMBUS_DEVICE_1,
+	.slave_dev_pgd_la = 0,
+	.slave_dev_intfdev_la = 0,
+	.bit_width = 16,
+	.data_format = 0,
+	.num_channels = 1
+};
+
+static struct afe_param_cdc_reg_page_cfg tavil_cdc_reg_page_cfg = {
+	.minor_version = AFE_API_VERSION_CDC_REG_PAGE_CFG,
+	.enable = 1,
+	.proc_id = AFE_CDC_REG_PAGE_ASSIGN_PROC_ID_1,
+};
+
+static struct afe_param_cdc_reg_cfg audio_reg_cfg[] = {
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_MAIN_CTL_1),
+		HW_MAD_AUDIO_ENABLE, 0x1, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_3),
+		HW_MAD_AUDIO_SLEEP_TIME, 0xF, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SOC_MAD_AUDIO_CTL_4),
+		HW_MAD_TX_AUDIO_SWITCH_OFF, 0x1, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_CFG),
+		MAD_AUDIO_INT_DEST_SELECT_REG, 0x2, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_MASK3),
+		MAD_AUDIO_INT_MASK_REG, 0x1, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_STATUS3),
+		MAD_AUDIO_INT_STATUS_REG, 0x1, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_INTR_PIN2_CLEAR3),
+		MAD_AUDIO_INT_CLEAR_REG, 0x1, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE),
+		SB_PGD_PORT_TX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_TX_BASE),
+		SB_PGD_PORT_TX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE),
+		SB_PGD_PORT_RX_WATERMARK_N, 0x1E, WCD934X_REG_BITS, 0x1
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET + WCD934X_SB_PGD_PORT_RX_BASE),
+		SB_PGD_PORT_RX_ENABLE_N, 0x1, WCD934X_REG_BITS, 0x1
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 WCD934X_CDC_ANC0_IIR_ADAPT_CTL),
+		AANC_FF_GAIN_ADAPTIVE, 0x4, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 WCD934X_CDC_ANC0_IIR_ADAPT_CTL),
+		AANC_FFGAIN_ADAPTIVE_EN, 0x8, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 WCD934X_CDC_ANC0_FF_A_GAIN_CTL),
+		AANC_GAIN_CONTROL, 0xFF, WCD934X_REG_BITS, 0
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 SB_PGD_TX_PORT_MULTI_CHANNEL_0(0)),
+		SB_PGD_TX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 SB_PGD_TX_PORT_MULTI_CHANNEL_1(0)),
+		SB_PGD_TX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x180, 0)),
+		SB_PGD_RX_PORTn_MULTI_CHNL_0, 0xFF, WCD934X_REG_BITS, 0x4
+	},
+	{
+		1,
+		(WCD934X_REGISTER_START_OFFSET +
+		 SB_PGD_RX_PORT_MULTI_CHANNEL_0(0x181, 0)),
+		SB_PGD_RX_PORTn_MULTI_CHNL_1, 0xFF, WCD934X_REG_BITS, 0x4
+	},
+};
+
+static struct afe_param_cdc_reg_cfg_data tavil_audio_reg_cfg = {
+	.num_registers = ARRAY_SIZE(audio_reg_cfg),
+	.reg_data = audio_reg_cfg,
+};
+
+static struct afe_param_id_cdc_aanc_version tavil_cdc_aanc_version = {
+	.cdc_aanc_minor_version = AFE_API_VERSION_CDC_AANC_VERSION,
+	.aanc_hw_version        = AANC_HW_BLOCK_VERSION_2,
+};
+
+static const DECLARE_TLV_DB_SCALE(digital_gain, 0, 1, 0);
+static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
+static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
+
+#define WCD934X_TX_UNMUTE_DELAY_MS 25
+
+static int tx_unmute_delay = WCD934X_TX_UNMUTE_DELAY_MS;
+module_param(tx_unmute_delay, int, 0664);
+MODULE_PARM_DESC(tx_unmute_delay, "delay to unmute the tx path");
+
+
+/* Hold instance to soundwire platform device */
+struct tavil_swr_ctrl_data {
+	struct platform_device *swr_pdev;
+};
+
+struct wcd_swr_ctrl_platform_data {
+	void *handle; /* holds codec private data */
+	int (*read)(void *handle, int reg);
+	int (*write)(void *handle, int reg, int val);
+	int (*bulk_write)(void *handle, u32 *reg, u32 *val, size_t len);
+	int (*clk)(void *handle, bool enable);
+	int (*handle_irq)(void *handle,
+			  irqreturn_t (*swrm_irq_handler)(int irq, void *data),
+			  void *swrm_handle, int action);
+};
+
+/* Holds all Soundwire and speaker related information */
+struct wcd934x_swr {
+	struct tavil_swr_ctrl_data *ctrl_data;
+	struct wcd_swr_ctrl_platform_data plat_data;
+	struct mutex read_mutex;
+	struct mutex write_mutex;
+	struct mutex clk_mutex;
+	int spkr_gain_offset;
+	int spkr_mode;
+	int clk_users;
+	int rx_7_count;
+	int rx_8_count;
+};
+
+struct tx_mute_work {
+	struct tavil_priv *tavil;
+	u8 decimator;
+	struct delayed_work dwork;
+};
+
+#define WCD934X_SPK_ANC_EN_DELAY_MS 350
+static int spk_anc_en_delay = WCD934X_SPK_ANC_EN_DELAY_MS;
+module_param(spk_anc_en_delay, int, 0664);
+MODULE_PARM_DESC(spk_anc_en_delay, "delay to enable anc in speaker path");
+
+struct spk_anc_work {
+	struct tavil_priv *tavil;
+	struct delayed_work dwork;
+};
+
+struct hpf_work {
+	struct tavil_priv *tavil;
+	u8 decimator;
+	u8 hpf_cut_off_freq;
+	struct delayed_work dwork;
+};
+
+struct tavil_priv {
+	struct device *dev;
+	struct wcd9xxx *wcd9xxx;
+	struct snd_soc_codec *codec;
+	u32 rx_bias_count;
+	s32 dmic_0_1_clk_cnt;
+	s32 dmic_2_3_clk_cnt;
+	s32 dmic_4_5_clk_cnt;
+	s32 micb_ref[TAVIL_MAX_MICBIAS];
+	s32 pullup_ref[TAVIL_MAX_MICBIAS];
+
+	/* ANC related */
+	u32 anc_slot;
+	bool anc_func;
+
+	/* compander */
+	int comp_enabled[COMPANDER_MAX];
+	int ear_spkr_gain;
+
+	/* class h specific data */
+	struct wcd_clsh_cdc_data clsh_d;
+	/* Tavil Interpolator Mode Select for EAR, HPH_L and HPH_R */
+	u32 hph_mode;
+
+	/* Mad switch reference count */
+	int mad_switch_cnt;
+
+	/* track tavil interface type */
+	u8 intf_type;
+
+	/* to track the status */
+	unsigned long status_mask;
+
+	struct afe_param_cdc_slimbus_slave_cfg slimbus_slave_cfg;
+
+	/* num of slim ports required */
+	struct wcd9xxx_codec_dai_data  dai[NUM_CODEC_DAIS];
+	/* Port values for Rx and Tx codec_dai */
+	unsigned int rx_port_value[WCD934X_RX_MAX];
+	unsigned int tx_port_value;
+
+	struct wcd9xxx_resmgr_v2 *resmgr;
+	struct wcd934x_swr swr;
+	struct mutex micb_lock;
+
+	struct delayed_work power_gate_work;
+	struct mutex power_lock;
+
+	struct clk *wcd_ext_clk;
+
+	/* mbhc module */
+	struct wcd934x_mbhc *mbhc;
+
+	struct mutex codec_mutex;
+	struct work_struct tavil_add_child_devices_work;
+	struct hpf_work tx_hpf_work[WCD934X_NUM_DECIMATORS];
+	struct tx_mute_work tx_mute_dwork[WCD934X_NUM_DECIMATORS];
+	struct spk_anc_work spk_anc_dwork;
+
+	unsigned int vi_feed_value;
+
+	/* DSP control */
+	struct wcd_dsp_cntl *wdsp_cntl;
+
+	/* cal info for codec */
+	struct fw_info *fw_data;
+
+	/* Entry for version info */
+	struct snd_info_entry *entry;
+	struct snd_info_entry *version_entry;
+
+	/* SVS voting related */
+	struct mutex svs_mutex;
+	int svs_ref_cnt;
+
+	int native_clk_users;
+	/* ASRC users count */
+	int asrc_users[ASRC_MAX];
+	int asrc_output_mode[ASRC_MAX];
+	/* Main path clock users count */
+	int main_clk_users[WCD934X_NUM_INTERPOLATORS];
+	struct tavil_dsd_config *dsd_config;
+	struct tavil_idle_detect_config idle_det_cfg;
+
+	int power_active_ref;
+};
+
+static const struct tavil_reg_mask_val tavil_spkr_default[] = {
+	{WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+	{WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+	{WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+	{WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+	{WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x50},
+	{WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x50},
+};
+
+static const struct tavil_reg_mask_val tavil_spkr_mode1[] = {
+	{WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x00},
+	{WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x00},
+	{WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x00},
+	{WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x00},
+	{WCD934X_CDC_BOOST0_BOOST_CTL, 0x7C, 0x44},
+	{WCD934X_CDC_BOOST1_BOOST_CTL, 0x7C, 0x44},
+};
+
+static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil);
+
+/*
+ * wcd934x_get_codec_info: Get codec specific information
+ *
+ * @wcd9xxx: pointer to wcd9xxx structure
+ * @wcd_type: pointer to wcd9xxx_codec_type structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd934x_get_codec_info(struct wcd9xxx *wcd9xxx,
+			   struct wcd9xxx_codec_type *wcd_type)
+{
+	u16 id_minor, id_major;
+	struct regmap *wcd_regmap;
+	int rc, version = -1;
+
+	if (!wcd9xxx || !wcd_type)
+		return -EINVAL;
+
+	if (!wcd9xxx->regmap) {
+		dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null\n", __func__);
+		return -EINVAL;
+	}
+	wcd_regmap = wcd9xxx->regmap;
+
+	rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE0,
+			      (u8 *)&id_minor, sizeof(u16));
+	if (rc)
+		return -EINVAL;
+
+	rc = regmap_bulk_read(wcd_regmap, WCD934X_CHIP_TIER_CTRL_CHIP_ID_BYTE2,
+			      (u8 *)&id_major, sizeof(u16));
+	if (rc)
+		return -EINVAL;
+
+	dev_info(wcd9xxx->dev, "%s: wcd9xxx chip id major 0x%x, minor 0x%x\n",
+		 __func__, id_major, id_minor);
+
+	if (id_major != TAVIL_MAJOR)
+		goto version_unknown;
+
+	/*
+	 * As fine version info cannot be retrieved before tavil probe.
+	 * Assign coarse versions for possible future use before tavil probe.
+	 */
+	if (id_minor == cpu_to_le16(0))
+		version = TAVIL_VERSION_1_0;
+	else if (id_minor == cpu_to_le16(0x01))
+		version = TAVIL_VERSION_1_1;
+
+version_unknown:
+	if (version < 0)
+		dev_err(wcd9xxx->dev, "%s: wcd934x version unknown\n",
+			__func__);
+
+	/* Fill codec type info */
+	wcd_type->id_major = id_major;
+	wcd_type->id_minor = id_minor;
+	wcd_type->num_irqs = WCD934X_NUM_IRQS;
+	wcd_type->version = version;
+	wcd_type->slim_slave_type = WCD9XXX_SLIM_SLAVE_ADDR_TYPE_1;
+	wcd_type->i2c_chip_status = 0x01;
+	wcd_type->intr_tbl = wcd934x_intr_table;
+	wcd_type->intr_tbl_size = ARRAY_SIZE(wcd934x_intr_table);
+
+	wcd_type->intr_reg[WCD9XXX_INTR_STATUS_BASE] =
+						WCD934X_INTR_PIN1_STATUS0;
+	wcd_type->intr_reg[WCD9XXX_INTR_CLEAR_BASE] =
+						WCD934X_INTR_PIN1_CLEAR0;
+	wcd_type->intr_reg[WCD9XXX_INTR_MASK_BASE] =
+						WCD934X_INTR_PIN1_MASK0;
+	wcd_type->intr_reg[WCD9XXX_INTR_LEVEL_BASE] =
+						WCD934X_INTR_LEVEL0;
+	wcd_type->intr_reg[WCD9XXX_INTR_CLR_COMMIT] =
+						WCD934X_INTR_CLR_COMMIT;
+
+	return rc;
+}
+EXPORT_SYMBOL(wcd934x_get_codec_info);
+
+/*
+ * wcd934x_bringdown: Bringdown WCD Codec
+ *
+ * @wcd9xxx: Pointer to wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd934x_bringdown(struct wcd9xxx *wcd9xxx)
+{
+	if (!wcd9xxx || !wcd9xxx->regmap)
+		return -EINVAL;
+
+	regmap_write(wcd9xxx->regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+		     0x04);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd934x_bringdown);
+
+/*
+ * wcd934x_bringup: Bringup WCD Codec
+ *
+ * @wcd9xxx: Pointer to the wcd9xxx structure
+ *
+ * Returns 0 for success or negative error code for failure
+ */
+int wcd934x_bringup(struct wcd9xxx *wcd9xxx)
+{
+	struct regmap *wcd_regmap;
+
+	if (!wcd9xxx)
+		return -EINVAL;
+
+	if (!wcd9xxx->regmap) {
+		dev_err(wcd9xxx->dev, "%s: wcd9xxx regmap is null!\n",
+			__func__);
+		return -EINVAL;
+	}
+	wcd_regmap = wcd9xxx->regmap;
+
+	regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x01);
+	regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_A_STARTUP, 0x19);
+	regmap_write(wcd_regmap, WCD934X_SIDO_NEW_VOUT_D_STARTUP, 0x15);
+	regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+	regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+	regmap_write(wcd_regmap, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+	regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x3);
+	regmap_write(wcd_regmap, WCD934X_CODEC_RPM_RST_CTL, 0x7);
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd934x_bringup);
+
+/**
+ * tavil_set_spkr_gain_offset - offset the speaker path
+ * gain with the given offset value.
+ *
+ * @codec: codec instance
+ * @offset: Indicates speaker path gain offset value.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset)
+{
+	struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	if (!priv)
+		return -EINVAL;
+
+	priv->swr.spkr_gain_offset = offset;
+	return 0;
+}
+EXPORT_SYMBOL(tavil_set_spkr_gain_offset);
+
+/**
+ * tavil_set_spkr_mode - Configures speaker compander and smartboost
+ * settings based on speaker mode.
+ *
+ * @codec: codec instance
+ * @mode: Indicates speaker configuration mode.
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode)
+{
+	struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+	int i;
+	const struct tavil_reg_mask_val *regs;
+	int size;
+
+	if (!priv)
+		return -EINVAL;
+
+	switch (mode) {
+	case WCD934X_SPKR_MODE_1:
+		regs = tavil_spkr_mode1;
+		size = ARRAY_SIZE(tavil_spkr_mode1);
+		break;
+	default:
+		regs = tavil_spkr_default;
+		size = ARRAY_SIZE(tavil_spkr_default);
+		break;
+	}
+
+	priv->swr.spkr_mode = mode;
+	for (i = 0; i < size; i++)
+		snd_soc_update_bits(codec, regs[i].reg,
+				    regs[i].mask, regs[i].val);
+	return 0;
+}
+EXPORT_SYMBOL(tavil_set_spkr_mode);
+
+/**
+ * tavil_get_afe_config - returns specific codec configuration to afe to write
+ *
+ * @codec: codec instance
+ * @config_type: Indicates type of configuration to write.
+ */
+void *tavil_get_afe_config(struct snd_soc_codec *codec,
+			   enum afe_config_type config_type)
+{
+	struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	switch (config_type) {
+	case AFE_SLIMBUS_SLAVE_CONFIG:
+		return &priv->slimbus_slave_cfg;
+	case AFE_CDC_REGISTERS_CONFIG:
+		return &tavil_audio_reg_cfg;
+	case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+		return &tavil_slimbus_slave_port_cfg;
+	case AFE_AANC_VERSION:
+		return &tavil_cdc_aanc_version;
+	case AFE_CDC_REGISTER_PAGE_CONFIG:
+		return &tavil_cdc_reg_page_cfg;
+	default:
+		dev_info(codec->dev, "%s: Unknown config_type 0x%x\n",
+			__func__, config_type);
+		return NULL;
+	}
+}
+EXPORT_SYMBOL(tavil_get_afe_config);
+
+static bool is_tavil_playback_dai(int dai_id)
+{
+	if ((dai_id == AIF1_PB) || (dai_id == AIF2_PB) ||
+	    (dai_id == AIF3_PB) || (dai_id == AIF4_PB))
+		return true;
+
+	return false;
+}
+
+static int tavil_find_playback_dai_id_for_port(int port_id,
+					       struct tavil_priv *tavil)
+{
+	struct wcd9xxx_codec_dai_data *dai;
+	struct wcd9xxx_ch *ch;
+	int i, slv_port_id;
+
+	for (i = AIF1_PB; i < NUM_CODEC_DAIS; i++) {
+		if (!is_tavil_playback_dai(i))
+			continue;
+
+		dai = &tavil->dai[i];
+		list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+			slv_port_id = wcd9xxx_get_slave_port(ch->ch_num);
+			if ((slv_port_id > 0) && (slv_port_id == port_id))
+				return i;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void tavil_vote_svs(struct tavil_priv *tavil, bool vote)
+{
+	struct wcd9xxx *wcd9xxx;
+
+	wcd9xxx = tavil->wcd9xxx;
+
+	mutex_lock(&tavil->svs_mutex);
+	if (vote) {
+		tavil->svs_ref_cnt++;
+		if (tavil->svs_ref_cnt == 1)
+			regmap_update_bits(wcd9xxx->regmap,
+					   WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0,
+					   0x01, 0x01);
+	} else {
+		/* Do not decrement ref count if it is already 0 */
+		if (tavil->svs_ref_cnt == 0)
+			goto done;
+
+		tavil->svs_ref_cnt--;
+		if (tavil->svs_ref_cnt == 0)
+			regmap_update_bits(wcd9xxx->regmap,
+					   WCD934X_CPE_SS_PWR_SYS_PSTATE_CTL_0,
+					   0x01, 0x00);
+	}
+done:
+	dev_dbg(tavil->dev, "%s: vote = %s, updated ref cnt = %u\n", __func__,
+		vote ? "vote" : "Unvote", tavil->svs_ref_cnt);
+	mutex_unlock(&tavil->svs_mutex);
+}
+
+static int tavil_get_anc_slot(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tavil->anc_slot;
+	return 0;
+}
+
+static int tavil_put_anc_slot(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	tavil->anc_slot = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int tavil_get_anc_func(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = (tavil->anc_func == true ? 1 : 0);
+	return 0;
+}
+
+static int tavil_put_anc_func(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	mutex_lock(&tavil->codec_mutex);
+	tavil->anc_func = (!ucontrol->value.integer.value[0] ? false : true);
+	dev_dbg(codec->dev, "%s: anc_func %x", __func__, tavil->anc_func);
+
+	if (tavil->anc_func == true) {
+		snd_soc_dapm_enable_pin(dapm, "ANC EAR PA");
+		snd_soc_dapm_enable_pin(dapm, "ANC EAR");
+		snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+		snd_soc_dapm_disable_pin(dapm, "EAR PA");
+		snd_soc_dapm_disable_pin(dapm, "EAR");
+	} else {
+		snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+		snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+		snd_soc_dapm_disable_pin(dapm, "ANC SPK1 PA");
+		snd_soc_dapm_enable_pin(dapm, "EAR PA");
+		snd_soc_dapm_enable_pin(dapm, "EAR");
+	}
+	mutex_unlock(&tavil->codec_mutex);
+
+	snd_soc_dapm_sync(dapm);
+	return 0;
+}
+
+static int tavil_codec_enable_anc(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	const char *filename;
+	const struct firmware *fw;
+	int i;
+	int ret = 0;
+	int num_anc_slots;
+	struct wcd9xxx_anc_header *anc_head;
+	struct firmware_cal *hwdep_cal = NULL;
+	u32 anc_writes_size = 0;
+	u32 anc_cal_size = 0;
+	int anc_size_remaining;
+	u32 *anc_ptr;
+	u16 reg;
+	u8 mask, val;
+	size_t cal_size;
+	const void *data;
+
+	if (!tavil->anc_func)
+		return 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_ANC_CAL);
+		if (hwdep_cal) {
+			data = hwdep_cal->data;
+			cal_size = hwdep_cal->size;
+			dev_dbg(codec->dev, "%s: using hwdep calibration, cal_size %zd",
+				__func__, cal_size);
+		} else {
+			filename = "WCD934X/WCD934X_anc.bin";
+			ret = request_firmware(&fw, filename, codec->dev);
+			if (ret < 0) {
+				dev_err(codec->dev, "%s: Failed to acquire ANC data: %d\n",
+					__func__, ret);
+				return ret;
+			}
+			if (!fw) {
+				dev_err(codec->dev, "%s: Failed to get anc fw\n",
+					__func__);
+				return -ENODEV;
+			}
+			data = fw->data;
+			cal_size = fw->size;
+			dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+				__func__);
+		}
+		if (cal_size < sizeof(struct wcd9xxx_anc_header)) {
+			dev_err(codec->dev, "%s: Invalid cal_size %zd\n",
+				__func__, cal_size);
+			ret = -EINVAL;
+			goto err;
+		}
+		/* First number is the number of register writes */
+		anc_head = (struct wcd9xxx_anc_header *)(data);
+		anc_ptr = (u32 *)(data + sizeof(struct wcd9xxx_anc_header));
+		anc_size_remaining = cal_size -
+				     sizeof(struct wcd9xxx_anc_header);
+		num_anc_slots = anc_head->num_anc_slots;
+
+		if (tavil->anc_slot >= num_anc_slots) {
+			dev_err(codec->dev, "%s: Invalid ANC slot selected\n",
+				__func__);
+			ret = -EINVAL;
+			goto err;
+		}
+		for (i = 0; i < num_anc_slots; i++) {
+			if (anc_size_remaining < WCD934X_PACKED_REG_SIZE) {
+				dev_err(codec->dev, "%s: Invalid register format\n",
+					__func__);
+				ret = -EINVAL;
+				goto err;
+			}
+			anc_writes_size = (u32)(*anc_ptr);
+			anc_size_remaining -= sizeof(u32);
+			anc_ptr += 1;
+
+			if ((anc_writes_size * WCD934X_PACKED_REG_SIZE) >
+			    anc_size_remaining) {
+				dev_err(codec->dev, "%s: Invalid register format\n",
+					__func__);
+				ret = -EINVAL;
+				goto err;
+			}
+
+			if (tavil->anc_slot == i)
+				break;
+
+			anc_size_remaining -= (anc_writes_size *
+				WCD934X_PACKED_REG_SIZE);
+			anc_ptr += anc_writes_size;
+		}
+		if (i == num_anc_slots) {
+			dev_err(codec->dev, "%s: Selected ANC slot not present\n",
+				__func__);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		anc_cal_size = anc_writes_size;
+		for (i = 0; i < anc_writes_size; i++) {
+			WCD934X_CODEC_UNPACK_ENTRY(anc_ptr[i], reg, mask, val);
+			snd_soc_write(codec, reg, (val & mask));
+		}
+
+		/* Rate converter clk enable and set bypass mode */
+		snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL,
+				    0x05, 0x05);
+		if (!hwdep_cal)
+			release_firmware(fw);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, WCD934X_CDC_ANC0_RC_COMMON_CTL,
+				    0x05, 0x00);
+		if (!strcmp(w->name, "ANC EAR PA") ||
+		    !strcmp(w->name, "ANC SPK1 PA")) {
+			snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
+					    0x30, 0x00);
+			msleep(50);
+			snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_1_CTL,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_ANC0_CLK_RESET_CTL,
+					    0x38, 0x38);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_ANC0_CLK_RESET_CTL,
+					    0x07, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_ANC0_CLK_RESET_CTL,
+					    0x38, 0x00);
+		}
+		break;
+	}
+
+	return 0;
+err:
+	if (!hwdep_cal)
+		release_firmware(fw);
+	return ret;
+}
+
+static int tavil_vi_feed_mixer_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tavil_p->vi_feed_value;
+
+	return 0;
+}
+
+static int tavil_vi_feed_mixer_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: enable: %d, port_id:%d, dai_id: %d\n",
+		__func__, enable, port_id, dai_id);
+
+	tavil_p->vi_feed_value = ucontrol->value.integer.value[0];
+
+	mutex_lock(&tavil_p->codec_mutex);
+	if (enable) {
+		if (port_id == WCD934X_TX14 && !test_bit(VI_SENSE_1,
+						&tavil_p->status_mask)) {
+			list_add_tail(&core->tx_chs[WCD934X_TX14].list,
+					&tavil_p->dai[dai_id].wcd9xxx_ch_list);
+			set_bit(VI_SENSE_1, &tavil_p->status_mask);
+		}
+		if (port_id == WCD934X_TX15 && !test_bit(VI_SENSE_2,
+						&tavil_p->status_mask)) {
+			list_add_tail(&core->tx_chs[WCD934X_TX15].list,
+					&tavil_p->dai[dai_id].wcd9xxx_ch_list);
+			set_bit(VI_SENSE_2, &tavil_p->status_mask);
+		}
+	} else {
+		if (port_id == WCD934X_TX14 && test_bit(VI_SENSE_1,
+					&tavil_p->status_mask)) {
+			list_del_init(&core->tx_chs[WCD934X_TX14].list);
+			clear_bit(VI_SENSE_1, &tavil_p->status_mask);
+		}
+		if (port_id == WCD934X_TX15 && test_bit(VI_SENSE_2,
+					&tavil_p->status_mask)) {
+			list_del_init(&core->tx_chs[WCD934X_TX15].list);
+			clear_bit(VI_SENSE_2, &tavil_p->status_mask);
+		}
+	}
+	mutex_unlock(&tavil_p->codec_mutex);
+	snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, NULL);
+
+	return 0;
+}
+
+static int slim_tx_mixer_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tavil_p->tx_port_value;
+	return 0;
+}
+
+static int slim_tx_mixer_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct snd_soc_dapm_update *update = NULL;
+	struct soc_multi_mixer_control *mixer =
+		((struct soc_multi_mixer_control *)kcontrol->private_value);
+	u32 dai_id = widget->shift;
+	u32 port_id = mixer->shift;
+	u32 enable = ucontrol->value.integer.value[0];
+	u32 vtable;
+
+	dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
+		  __func__,
+		widget->name, ucontrol->id.name, tavil_p->tx_port_value,
+		widget->shift, ucontrol->value.integer.value[0]);
+
+	mutex_lock(&tavil_p->codec_mutex);
+	if (dai_id >= ARRAY_SIZE(vport_slim_check_table)) {
+		dev_err(codec->dev, "%s: dai_id: %d, out of bounds\n",
+			__func__, dai_id);
+		mutex_unlock(&tavil_p->codec_mutex);
+		return -EINVAL;
+	}
+	vtable = vport_slim_check_table[dai_id];
+
+	switch (dai_id) {
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+		/* only add to the list if value not set */
+		if (enable && !(tavil_p->tx_port_value & 1 << port_id)) {
+			if (wcd9xxx_tx_vport_validation(vtable, port_id,
+			    tavil_p->dai, NUM_CODEC_DAIS)) {
+				dev_dbg(codec->dev, "%s: TX%u is used by other virtual port\n",
+					__func__, port_id);
+				mutex_unlock(&tavil_p->codec_mutex);
+				return 0;
+			}
+			tavil_p->tx_port_value |= 1 << port_id;
+			list_add_tail(&core->tx_chs[port_id].list,
+				      &tavil_p->dai[dai_id].wcd9xxx_ch_list);
+		} else if (!enable && (tavil_p->tx_port_value &
+			   1 << port_id)) {
+			tavil_p->tx_port_value &= ~(1 << port_id);
+			list_del_init(&core->tx_chs[port_id].list);
+		} else {
+			if (enable)
+				dev_dbg(codec->dev, "%s: TX%u port is used by\n"
+					"this virtual port\n",
+					__func__, port_id);
+			else
+				dev_dbg(codec->dev, "%s: TX%u port is not used by\n"
+					"this virtual port\n",
+					__func__, port_id);
+			/* avoid update power function */
+			mutex_unlock(&tavil_p->codec_mutex);
+			return 0;
+		}
+		break;
+	case AIF4_MAD_TX:
+		break;
+	default:
+		dev_err(codec->dev, "Unknown AIF %d\n", dai_id);
+		mutex_unlock(&tavil_p->codec_mutex);
+		return -EINVAL;
+	}
+	dev_dbg(codec->dev, "%s: name %s sname %s updated value %u shift %d\n",
+		__func__, widget->name, widget->sname, tavil_p->tx_port_value,
+		widget->shift);
+
+	mutex_unlock(&tavil_p->codec_mutex);
+	snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, enable, update);
+
+	return 0;
+}
+
+static int slim_rx_mux_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.enumerated.item[0] =
+				tavil_p->rx_port_value[widget->shift];
+	return 0;
+}
+
+static int slim_rx_mux_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *core = dev_get_drvdata(codec->dev->parent);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+	unsigned int rx_port_value;
+	u32 port_id = widget->shift;
+
+	tavil_p->rx_port_value[port_id] = ucontrol->value.enumerated.item[0];
+	rx_port_value = tavil_p->rx_port_value[port_id];
+
+	mutex_lock(&tavil_p->codec_mutex);
+	dev_dbg(codec->dev, "%s: wname %s cname %s value %u shift %d item %ld\n",
+		__func__, widget->name, ucontrol->id.name,
+		rx_port_value, widget->shift,
+		ucontrol->value.integer.value[0]);
+
+	/* value need to match the Virtual port and AIF number */
+	switch (rx_port_value) {
+	case 0:
+		list_del_init(&core->rx_chs[port_id].list);
+		break;
+	case 1:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			WCD934X_RX_PORT_START_NUMBER,
+			&tavil_p->dai[AIF1_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tavil_p->dai[AIF1_PB].wcd9xxx_ch_list);
+		break;
+	case 2:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			WCD934X_RX_PORT_START_NUMBER,
+			&tavil_p->dai[AIF2_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tavil_p->dai[AIF2_PB].wcd9xxx_ch_list);
+		break;
+	case 3:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			WCD934X_RX_PORT_START_NUMBER,
+			&tavil_p->dai[AIF3_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tavil_p->dai[AIF3_PB].wcd9xxx_ch_list);
+		break;
+	case 4:
+		if (wcd9xxx_rx_vport_validation(port_id +
+			WCD934X_RX_PORT_START_NUMBER,
+			&tavil_p->dai[AIF4_PB].wcd9xxx_ch_list)) {
+			dev_dbg(codec->dev, "%s: RX%u is used by current requesting AIF_PB itself\n",
+				__func__, port_id);
+			goto rtn;
+		}
+		list_add_tail(&core->rx_chs[port_id].list,
+			      &tavil_p->dai[AIF4_PB].wcd9xxx_ch_list);
+		break;
+	default:
+		dev_err(codec->dev, "Unknown AIF %d\n", rx_port_value);
+		goto err;
+	}
+rtn:
+	mutex_unlock(&tavil_p->codec_mutex);
+	snd_soc_dapm_mux_update_power(widget->dapm, kcontrol,
+				      rx_port_value, e, update);
+
+	return 0;
+err:
+	mutex_unlock(&tavil_p->codec_mutex);
+	return -EINVAL;
+}
+
+static void tavil_codec_enable_slim_port_intr(
+					struct wcd9xxx_codec_dai_data *dai,
+					struct snd_soc_codec *codec)
+{
+	struct wcd9xxx_ch *ch;
+	int port_num = 0;
+	unsigned short reg = 0;
+	u8 val = 0;
+	struct tavil_priv *tavil_p;
+
+	if (!dai || !codec) {
+		pr_err("%s: Invalid params\n", __func__);
+		return;
+	}
+
+	tavil_p = snd_soc_codec_get_drvdata(codec);
+	list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+		if (ch->port >= WCD934X_RX_PORT_START_NUMBER) {
+			port_num = ch->port - WCD934X_RX_PORT_START_NUMBER;
+			reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + (port_num / 8);
+			val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx,
+				reg);
+			if (!(val & BYTE_BIT_MASK(port_num))) {
+				val |= BYTE_BIT_MASK(port_num);
+				wcd9xxx_interface_reg_write(
+					tavil_p->wcd9xxx, reg, val);
+				val = wcd9xxx_interface_reg_read(
+					tavil_p->wcd9xxx, reg);
+			}
+		} else {
+			port_num = ch->port;
+			reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 + (port_num / 8);
+			val = wcd9xxx_interface_reg_read(tavil_p->wcd9xxx,
+				reg);
+			if (!(val & BYTE_BIT_MASK(port_num))) {
+				val |= BYTE_BIT_MASK(port_num);
+				wcd9xxx_interface_reg_write(tavil_p->wcd9xxx,
+					reg, val);
+				val = wcd9xxx_interface_reg_read(
+					tavil_p->wcd9xxx, reg);
+			}
+		}
+	}
+}
+
+static int tavil_codec_enable_slim_chmask(struct wcd9xxx_codec_dai_data *dai,
+					  bool up)
+{
+	int ret = 0;
+	struct wcd9xxx_ch *ch;
+
+	if (up) {
+		list_for_each_entry(ch, &dai->wcd9xxx_ch_list, list) {
+			ret = wcd9xxx_get_slave_port(ch->ch_num);
+			if (ret < 0) {
+				pr_err("%s: Invalid slave port ID: %d\n",
+				       __func__, ret);
+				ret = -EINVAL;
+			} else {
+				set_bit(ret, &dai->ch_mask);
+			}
+		}
+	} else {
+		ret = wait_event_timeout(dai->dai_wait, (dai->ch_mask == 0),
+					 msecs_to_jiffies(
+						WCD934X_SLIM_CLOSE_TIMEOUT));
+		if (!ret) {
+			pr_err("%s: Slim close tx/rx wait timeout, ch_mask:0x%lx\n",
+				__func__, dai->ch_mask);
+			ret = -ETIMEDOUT;
+		} else {
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
+static void tavil_codec_mute_dsd(struct snd_soc_codec *codec,
+				 struct list_head *ch_list)
+{
+	u8 dsd0_in;
+	u8 dsd1_in;
+	struct wcd9xxx_ch *ch;
+
+	/* Read DSD Input Ports */
+	dsd0_in = (snd_soc_read(codec, WCD934X_CDC_DSD0_CFG0) & 0x3C) >> 2;
+	dsd1_in = (snd_soc_read(codec, WCD934X_CDC_DSD1_CFG0) & 0x3C) >> 2;
+
+	if ((dsd0_in == 0) && (dsd1_in == 0))
+		return;
+
+	/*
+	 * Check if the ports getting disabled are connected to DSD inputs.
+	 * If connected, enable DSD mute to avoid DC entering into DSD Filter
+	 */
+	list_for_each_entry(ch, ch_list, list) {
+		if (ch->port == (dsd0_in + WCD934X_RX_PORT_START_NUMBER - 1))
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+					    0x04, 0x04);
+		if (ch->port == (dsd1_in + WCD934X_RX_PORT_START_NUMBER - 1))
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+					    0x04, 0x04);
+	}
+}
+
+static int tavil_codec_enable_slimrx(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct wcd9xxx *core;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+	struct wcd9xxx_codec_dai_data *dai;
+	struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+
+	core = dev_get_drvdata(codec->dev->parent);
+
+	dev_dbg(codec->dev, "%s: event called! codec name %s num_dai %d\n"
+		"stream name %s event %d\n",
+		__func__, codec->component.name,
+		codec->component.num_dai, w->sname, event);
+
+	dai = &tavil_p->dai[w->shift];
+	dev_dbg(codec->dev, "%s: w->name %s w->shift %d event %d\n",
+		 __func__, w->name, w->shift, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dai->bus_down_in_recovery = false;
+		tavil_codec_enable_slim_port_intr(dai, codec);
+		(void) tavil_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (dsd_conf)
+			tavil_codec_mute_dsd(codec, &dai->wcd9xxx_ch_list);
+
+		ret = wcd9xxx_disconnect_port(core, &dai->wcd9xxx_ch_list,
+					      dai->grph);
+		dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
+			__func__, ret);
+
+		if (!dai->bus_down_in_recovery)
+			ret = tavil_codec_enable_slim_chmask(dai, false);
+		else
+			dev_dbg(codec->dev,
+				"%s: bus in recovery skip enable slim_chmask",
+				__func__);
+		ret = wcd9xxx_close_slim_sch_rx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		break;
+	}
+	return ret;
+}
+
+static int tavil_codec_enable_slimtx(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_codec_dai_data *dai;
+	struct wcd9xxx *core;
+	int ret = 0;
+
+	dev_dbg(codec->dev,
+		"%s: w->name %s, w->shift = %d, num_dai %d stream name %s\n",
+		__func__, w->name, w->shift,
+		codec->component.num_dai, w->sname);
+
+	dai = &tavil_p->dai[w->shift];
+	core = dev_get_drvdata(codec->dev->parent);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		dai->bus_down_in_recovery = false;
+		tavil_codec_enable_slim_port_intr(dai, codec);
+		(void) tavil_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		if (!dai->bus_down_in_recovery)
+			ret = tavil_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+						      &dai->wcd9xxx_ch_list,
+						      dai->grph);
+			dev_dbg(codec->dev, "%s: Disconnect RX port, ret = %d\n",
+				 __func__, ret);
+		}
+		break;
+	}
+	return ret;
+}
+
+static int tavil_codec_enable_slimvi_feedback(struct snd_soc_dapm_widget *w,
+					      struct snd_kcontrol *kcontrol,
+					      int event)
+{
+	struct wcd9xxx *core = NULL;
+	struct snd_soc_codec *codec = NULL;
+	struct tavil_priv *tavil_p = NULL;
+	int ret = 0;
+	struct wcd9xxx_codec_dai_data *dai = NULL;
+
+	codec = snd_soc_dapm_to_codec(w->dapm);
+	tavil_p = snd_soc_codec_get_drvdata(codec);
+	core = dev_get_drvdata(codec->dev->parent);
+
+	dev_dbg(codec->dev,
+		"%s: num_dai %d stream name %s w->name %s event %d shift %d\n",
+		__func__, codec->component.num_dai, w->sname,
+		w->name, event, w->shift);
+
+	if (w->shift != AIF4_VIFEED) {
+		pr_err("%s Error in enabling the tx path\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	dai = &tavil_p->dai[w->shift];
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) {
+			dev_dbg(codec->dev, "%s: spkr1 enabled\n", __func__);
+			/* Enable V&I sensing */
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x0F, 0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x0F,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x10);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+				0x10);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+				0x00);
+		}
+		if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) {
+			pr_debug("%s: spkr2 enabled\n", __func__);
+			/* Enable V&I sensing */
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x0F,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x0F,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+				0x10);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+				0x10);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+				0x00);
+		}
+		dai->bus_down_in_recovery = false;
+		tavil_codec_enable_slim_port_intr(dai, codec);
+		(void) tavil_codec_enable_slim_chmask(dai, true);
+		ret = wcd9xxx_cfg_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+					      dai->rate, dai->bit_width,
+					      &dai->grph);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = wcd9xxx_close_slim_sch_tx(core, &dai->wcd9xxx_ch_list,
+						dai->grph);
+		if (ret)
+			dev_err(codec->dev, "%s error in close_slim_sch_tx %d\n",
+				__func__, ret);
+		if (!dai->bus_down_in_recovery)
+			ret = tavil_codec_enable_slim_chmask(dai, false);
+		if (ret < 0) {
+			ret = wcd9xxx_disconnect_port(core,
+				&dai->wcd9xxx_ch_list,
+				dai->grph);
+			dev_dbg(codec->dev, "%s: Disconnect TX port, ret = %d\n",
+				__func__, ret);
+		}
+		if (test_bit(VI_SENSE_1, &tavil_p->status_mask)) {
+			/* Disable V&I sensing */
+			dev_dbg(codec->dev, "%s: spkr1 disabled\n", __func__);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x20, 0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX9_SPKR_PROT_PATH_CTL, 0x10, 0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX10_SPKR_PROT_PATH_CTL, 0x10,
+				0x00);
+		}
+		if (test_bit(VI_SENSE_2, &tavil_p->status_mask)) {
+			/* Disable V&I sensing */
+			dev_dbg(codec->dev, "%s: spkr2 disabled\n", __func__);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x20,
+				0x20);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX11_SPKR_PROT_PATH_CTL, 0x10,
+				0x00);
+			snd_soc_update_bits(codec,
+				WCD934X_CDC_TX12_SPKR_PROT_PATH_CTL, 0x10,
+				0x00);
+		}
+		break;
+	}
+done:
+	return ret;
+}
+
+static int tavil_codec_enable_rx_bias(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tavil->rx_bias_count++;
+		if (tavil->rx_bias_count == 1) {
+			snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+					    0x01, 0x01);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tavil->rx_bias_count--;
+		if (!tavil->rx_bias_count)
+			snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+					    0x01, 0x00);
+		break;
+	};
+	dev_dbg(codec->dev, "%s: Current RX BIAS user count: %d\n", __func__,
+		tavil->rx_bias_count);
+
+	return 0;
+}
+
+static void tavil_spk_anc_update_callback(struct work_struct *work)
+{
+	struct spk_anc_work *spk_anc_dwork;
+	struct tavil_priv *tavil;
+	struct delayed_work *delayed_work;
+	struct snd_soc_codec *codec;
+
+	delayed_work = to_delayed_work(work);
+	spk_anc_dwork = container_of(delayed_work, struct spk_anc_work, dwork);
+	tavil = spk_anc_dwork->tavil;
+	codec = tavil->codec;
+
+	snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0, 0x10, 0x10);
+}
+
+static int tavil_codec_enable_spkr_anc(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	if (!tavil->anc_func)
+		return 0;
+
+	dev_dbg(codec->dev, "%s: w: %s event: %d anc: %d\n", __func__,
+		w->name, event, tavil->anc_func);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = tavil_codec_enable_anc(w, kcontrol, event);
+		schedule_delayed_work(&tavil->spk_anc_dwork.dwork,
+				      msecs_to_jiffies(spk_anc_en_delay));
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		cancel_delayed_work_sync(&tavil->spk_anc_dwork.dwork);
+		snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_CFG0,
+				    0x10, 0x00);
+		ret = tavil_codec_enable_anc(w, kcontrol, event);
+		break;
+	}
+	return ret;
+}
+
+static int tavil_codec_enable_ear_pa(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * 5ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CTL,
+				    0x10, 0x00);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, WCD934X_CDC_RX0_RX_PATH_MIX_CTL)) &
+		     0x10)
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX0_RX_PATH_MIX_CTL,
+					    0x10, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/*
+		 * 5ms sleep is required after PA is disabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+
+		if (!(strcmp(w->name, "ANC EAR PA"))) {
+			ret = tavil_codec_enable_anc(w, kcontrol, event);
+			snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0,
+					    0x10, 0x00);
+		}
+		break;
+	};
+
+	return ret;
+}
+
+static void tavil_codec_override(struct snd_soc_codec *codec, int mode,
+				 int event)
+{
+	if (mode == CLS_AB || mode == CLS_AB_HIFI) {
+		switch (event) {
+		case SND_SOC_DAPM_PRE_PMU:
+		case SND_SOC_DAPM_POST_PMU:
+			if (!(snd_soc_read(codec,
+					WCD934X_CDC_RX2_RX_PATH_CTL) & 0x10) &&
+				(!(snd_soc_read(codec,
+					WCD934X_CDC_RX1_RX_PATH_CTL) & 0x10)))
+				snd_soc_update_bits(codec,
+					WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x02);
+		break;
+		case SND_SOC_DAPM_POST_PMD:
+			snd_soc_update_bits(codec,
+				WCD9XXX_A_ANA_RX_SUPPLIES, 0x02, 0x00);
+		break;
+		}
+	}
+}
+
+static int tavil_codec_enable_hphr_pa(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+					    0x06, (0x03 << 1));
+		set_bit(HPH_PA_DELAY, &tavil->status_mask);
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01)) {
+			/* Set regulator mode to AB if DSD is enabled */
+			snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * 7ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) {
+			usleep_range(7000, 7100);
+			clear_bit(HPH_PA_DELAY, &tavil->status_mask);
+		}
+
+		snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x01);
+
+		/* Remove mute */
+		snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
+				    0x10, 0x00);
+		/* Enable GM3 boost */
+		snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL,
+				    0x80, 0x80);
+		/* Enable AutoChop timer at the end of power up */
+		snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+				    0x02, 0x02);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_MIX_CTL)) &
+				  0x10)
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX2_RX_PATH_MIX_CTL,
+					    0x10, 0x00);
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+					    0x04, 0x00);
+		tavil_codec_override(codec, tavil->hph_mode, event);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		blocking_notifier_call_chain(&tavil->mbhc->notifier,
+					     WCD_EVENT_PRE_HPHR_PA_OFF,
+					     &tavil->mbhc->wcd_mbhc);
+		/* Enable DSD Mute before PA disable */
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD1_CFG2,
+					    0x04, 0x04);
+		snd_soc_update_bits(codec, WCD934X_HPH_R_TEST, 0x01, 0x00);
+		snd_soc_update_bits(codec, WCD934X_CDC_RX2_RX_PATH_CTL,
+				    0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 5ms sleep is required after PA disable */
+		usleep_range(5000, 5100);
+		tavil_codec_override(codec, tavil->hph_mode, event);
+		blocking_notifier_call_chain(&tavil->mbhc->notifier,
+					     WCD_EVENT_POST_HPHR_PA_OFF,
+					     &tavil->mbhc->wcd_mbhc);
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+					    0x06, 0x0);
+		break;
+	};
+
+	return 0;
+}
+
+static int tavil_codec_enable_hphl_pa(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+					    0x06, (0x03 << 1));
+		set_bit(HPH_PA_DELAY, &tavil->status_mask);
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01)) {
+			/* Set regulator mode to AB if DSD is enabled */
+			snd_soc_update_bits(codec, WCD934X_ANA_RX_SUPPLIES,
+					    0x02, 0x02);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * 7ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		if (test_bit(HPH_PA_DELAY, &tavil->status_mask)) {
+			usleep_range(7000, 7100);
+			clear_bit(HPH_PA_DELAY, &tavil->status_mask);
+		}
+		snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x01);
+		/* Remove Mute on primary path */
+		snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
+				    0x10, 0x00);
+		/* Enable GM3 boost */
+		snd_soc_update_bits(codec, WCD934X_HPH_CNP_WG_CTL,
+				    0x80, 0x80);
+		/* Enable AutoChop timer at the end of power up */
+		snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+				    0x02, 0x02);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_MIX_CTL)) &
+				  0x10)
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX1_RX_PATH_MIX_CTL,
+					    0x10, 0x00);
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+					    0x04, 0x00);
+		tavil_codec_override(codec, tavil->hph_mode, event);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		blocking_notifier_call_chain(&tavil->mbhc->notifier,
+					     WCD_EVENT_PRE_HPHL_PA_OFF,
+					     &tavil->mbhc->wcd_mbhc);
+		/* Enable DSD Mute before PA disable */
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+			snd_soc_update_bits(codec, WCD934X_CDC_DSD0_CFG2,
+					    0x04, 0x04);
+
+		snd_soc_update_bits(codec, WCD934X_HPH_L_TEST, 0x01, 0x00);
+		snd_soc_update_bits(codec, WCD934X_CDC_RX1_RX_PATH_CTL,
+				    0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 5ms sleep is required after PA disable */
+		usleep_range(5000, 5100);
+		tavil_codec_override(codec, tavil->hph_mode, event);
+		blocking_notifier_call_chain(&tavil->mbhc->notifier,
+					     WCD_EVENT_POST_HPHL_PA_OFF,
+					     &tavil->mbhc->wcd_mbhc);
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec, WCD934X_HPH_REFBUFF_LP_CTL,
+					    0x06, 0x0);
+		break;
+	};
+
+	return 0;
+}
+
+static int tavil_codec_enable_lineout_pa(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol,
+					 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u16 lineout_vol_reg = 0, lineout_mix_vol_reg = 0;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	if (w->reg == WCD934X_ANA_LO_1_2) {
+		if (w->shift == 7) {
+			lineout_vol_reg = WCD934X_CDC_RX3_RX_PATH_CTL;
+			lineout_mix_vol_reg = WCD934X_CDC_RX3_RX_PATH_MIX_CTL;
+		} else if (w->shift == 6) {
+			lineout_vol_reg = WCD934X_CDC_RX4_RX_PATH_CTL;
+			lineout_mix_vol_reg = WCD934X_CDC_RX4_RX_PATH_MIX_CTL;
+		}
+	} else {
+		dev_err(codec->dev, "%s: Error enabling lineout PA\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tavil_codec_override(codec, CLS_AB, event);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/*
+		 * 5ms sleep is required after PA is enabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		snd_soc_update_bits(codec, lineout_vol_reg,
+				    0x10, 0x00);
+		/* Remove mix path mute if it is enabled */
+		if ((snd_soc_read(codec, lineout_mix_vol_reg)) & 0x10)
+			snd_soc_update_bits(codec,
+					    lineout_mix_vol_reg,
+					    0x10, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/*
+		 * 5ms sleep is required after PA is disabled as per
+		 * HW requirement
+		 */
+		usleep_range(5000, 5500);
+		tavil_codec_override(codec, CLS_AB, event);
+	default:
+		break;
+	};
+
+	return 0;
+}
+
+static int tavil_codec_ear_dac_event(struct snd_soc_dapm_widget *w,
+				     struct snd_kcontrol *kcontrol,
+				     int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Disable AutoChop timer during power up */
+		snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+				    0x02, 0x00);
+
+		if (tavil->anc_func)
+			ret = tavil_codec_enable_anc(w, kcontrol, event);
+
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_EAR,
+			     CLS_H_NORMAL);
+		if (tavil->anc_func)
+			snd_soc_update_bits(codec, WCD934X_CDC_RX0_RX_PATH_CFG0,
+					    0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_EAR,
+			     CLS_H_NORMAL);
+		break;
+	default:
+		break;
+	};
+
+	return ret;
+}
+
+static int tavil_codec_hphr_dac_event(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = tavil->hph_mode;
+	u8 dem_inp;
+	struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+	dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+		w->name, event, hph_mode);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Read DEM INP Select */
+		dem_inp = snd_soc_read(codec, WCD934X_CDC_RX2_RX_PATH_SEC0) &
+			  0x03;
+		if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+		     (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+			dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+					__func__, hph_mode);
+			return -EINVAL;
+		}
+		if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+			/* Ripple freq control enable */
+			snd_soc_update_bits(codec,
+					     WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+					     0x01, 0x01);
+		/* Disable AutoChop timer during power up */
+		snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+				    0x02, 0x00);
+		/* Set RDAC gain */
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec,
+					    WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+					    0xF0, 0x40);
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD1_PATH_CTL) & 0x01))
+			hph_mode = CLS_H_HIFI;
+
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_HPHR,
+			     hph_mode);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 1000us required as per HW requirement */
+		usleep_range(1000, 1100);
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_HPHR,
+			     hph_mode);
+		if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+			/* Ripple freq control disable */
+			snd_soc_update_bits(codec,
+					    WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+					    0x01, 0x0);
+		/* Re-set RDAC gain */
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec,
+					    WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+					    0xF0, 0x0);
+		break;
+	default:
+		break;
+	};
+
+	return 0;
+}
+
+static int tavil_codec_hphl_dac_event(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int hph_mode = tavil->hph_mode;
+	u8 dem_inp;
+	int ret = 0;
+	struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+	dev_dbg(codec->dev, "%s wname: %s event: %d hph_mode: %d\n", __func__,
+		w->name, event, hph_mode);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/* Read DEM INP Select */
+		dem_inp = snd_soc_read(codec, WCD934X_CDC_RX1_RX_PATH_SEC0) &
+			  0x03;
+		if (((hph_mode == CLS_H_HIFI) || (hph_mode == CLS_H_LOHIFI) ||
+		     (hph_mode == CLS_H_LP)) && (dem_inp != 0x01)) {
+			dev_err(codec->dev, "%s: DEM Input not set correctly, hph_mode: %d\n",
+					__func__, hph_mode);
+			return -EINVAL;
+		}
+		if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+			/* Ripple freq control enable */
+			snd_soc_update_bits(codec,
+					     WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+					     0x01, 0x01);
+		/* Disable AutoChop timer during power up */
+		snd_soc_update_bits(codec, WCD934X_HPH_NEW_INT_HPH_TIMER1,
+				    0x02, 0x00);
+		/* Set RDAC gain */
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec,
+					    WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+					    0xF0, 0x40);
+		if (dsd_conf &&
+		    (snd_soc_read(codec, WCD934X_CDC_DSD0_PATH_CTL) & 0x01))
+			hph_mode = CLS_H_HIFI;
+
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_HPHL,
+			     hph_mode);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* 1000us required as per HW requirement */
+		usleep_range(1000, 1100);
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_HPHL,
+			     hph_mode);
+		if ((hph_mode != CLS_H_LP) && (hph_mode != CLS_H_ULP))
+			/* Ripple freq control disable */
+			snd_soc_update_bits(codec,
+					    WCD934X_SIDO_NEW_VOUT_D_FREQ2,
+					    0x01, 0x0);
+		/* Re-set RDAC gain */
+		if (TAVIL_IS_1_0(tavil->wcd9xxx))
+			snd_soc_update_bits(codec,
+					    WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+					    0xF0, 0x0);
+		break;
+	default:
+		break;
+	};
+
+	return ret;
+}
+
+static int tavil_codec_lineout_dac_event(struct snd_soc_dapm_widget *w,
+					 struct snd_kcontrol *kcontrol,
+					 int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_PRE_DAC,
+			     WCD_CLSH_STATE_LO,
+			     CLS_AB);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		wcd_clsh_fsm(codec, &tavil->clsh_d,
+			     WCD_CLSH_EVENT_POST_PA,
+			     WCD_CLSH_STATE_LO,
+			     CLS_AB);
+		break;
+	}
+
+	return 0;
+}
+
+static int tavil_codec_spk_boost_event(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u16 boost_path_ctl, boost_path_cfg1;
+	u16 reg, reg_mix;
+
+	dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event);
+
+	if (!strcmp(w->name, "RX INT7 CHAIN")) {
+		boost_path_ctl = WCD934X_CDC_BOOST0_BOOST_PATH_CTL;
+		boost_path_cfg1 = WCD934X_CDC_RX7_RX_PATH_CFG1;
+		reg = WCD934X_CDC_RX7_RX_PATH_CTL;
+		reg_mix = WCD934X_CDC_RX7_RX_PATH_MIX_CTL;
+	} else if (!strcmp(w->name, "RX INT8 CHAIN")) {
+		boost_path_ctl = WCD934X_CDC_BOOST1_BOOST_PATH_CTL;
+		boost_path_cfg1 = WCD934X_CDC_RX8_RX_PATH_CFG1;
+		reg = WCD934X_CDC_RX8_RX_PATH_CTL;
+		reg_mix = WCD934X_CDC_RX8_RX_PATH_MIX_CTL;
+	} else {
+		dev_err(codec->dev, "%s: unknown widget: %s\n",
+			__func__, w->name);
+		return -EINVAL;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x01);
+		snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x10);
+		snd_soc_update_bits(codec, reg, 0x10, 0x00);
+		if ((snd_soc_read(codec, reg_mix)) & 0x10)
+			snd_soc_update_bits(codec, reg_mix, 0x10, 0x00);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, boost_path_ctl, 0x10, 0x00);
+		snd_soc_update_bits(codec, boost_path_cfg1, 0x01, 0x00);
+		break;
+	};
+
+	return 0;
+}
+
+static int __tavil_codec_enable_swr(struct snd_soc_dapm_widget *w, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil;
+	int ch_cnt = 0;
+
+	tavil = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (((strnstr(w->name, "INT7_", sizeof("RX INT7_"))) ||
+			(strnstr(w->name, "INT7 MIX2",
+						sizeof("RX INT7 MIX2")))))
+			tavil->swr.rx_7_count++;
+		if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+		    !tavil->swr.rx_8_count)
+			tavil->swr.rx_8_count++;
+		ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count;
+
+		swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev,
+				SWR_DEVICE_UP, NULL);
+		swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev,
+				SWR_SET_NUM_RX_CH, &ch_cnt);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if ((strnstr(w->name, "INT7_", sizeof("RX INT7_")))  ||
+			(strnstr(w->name, "INT7 MIX2",
+			sizeof("RX INT7 MIX2"))))
+			tavil->swr.rx_7_count--;
+		if ((strnstr(w->name, "INT8_", sizeof("RX INT8_"))) &&
+		    tavil->swr.rx_8_count)
+			tavil->swr.rx_8_count--;
+		ch_cnt = !!(tavil->swr.rx_7_count) + tavil->swr.rx_8_count;
+
+		swrm_wcd_notify(tavil->swr.ctrl_data[0].swr_pdev,
+				SWR_SET_NUM_RX_CH, &ch_cnt);
+
+		break;
+	}
+	dev_dbg(tavil->dev, "%s: %s: current swr ch cnt: %d\n",
+		__func__, w->name, ch_cnt);
+
+	return 0;
+}
+
+static int tavil_codec_enable_swr(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	return __tavil_codec_enable_swr(w, event);
+}
+
+static int tavil_codec_config_mad(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	int idx;
+	const struct firmware *fw;
+	struct firmware_cal *hwdep_cal = NULL;
+	struct wcd_mad_audio_cal *mad_cal = NULL;
+	const void *data;
+	const char *filename = WCD934X_MAD_AUDIO_FIRMWARE_PATH;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	size_t cal_size;
+
+	hwdep_cal = wcdcal_get_fw_cal(tavil->fw_data, WCD9XXX_MAD_CAL);
+	if (hwdep_cal) {
+		data = hwdep_cal->data;
+		cal_size = hwdep_cal->size;
+		dev_dbg(codec->dev, "%s: using hwdep calibration\n",
+			__func__);
+	} else {
+		ret = request_firmware(&fw, filename, codec->dev);
+		if (ret || !fw) {
+			dev_err(codec->dev,
+				"%s: MAD firmware acquire failed, err = %d\n",
+				__func__, ret);
+			return -ENODEV;
+		}
+		data = fw->data;
+		cal_size = fw->size;
+		dev_dbg(codec->dev, "%s: using request_firmware calibration\n",
+			__func__);
+	}
+
+	if (cal_size < sizeof(*mad_cal)) {
+		dev_err(codec->dev,
+			"%s: Incorrect size %zd for MAD Cal, expected %zd\n",
+			__func__, cal_size, sizeof(*mad_cal));
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	mad_cal = (struct wcd_mad_audio_cal *) (data);
+	if (!mad_cal) {
+		dev_err(codec->dev,
+			"%s: Invalid calibration data\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	snd_soc_write(codec, WCD934X_SOC_MAD_MAIN_CTL_2,
+		      mad_cal->microphone_info.cycle_time);
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_MAIN_CTL_1, 0xFF << 3,
+			    ((uint16_t)mad_cal->microphone_info.settle_time)
+			    << 3);
+
+	/* Audio */
+	snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_8,
+		      mad_cal->audio_info.rms_omit_samples);
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_1,
+			    0x07 << 4, mad_cal->audio_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2, 0x03 << 2,
+			    mad_cal->audio_info.detection_mechanism << 2);
+	snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_7,
+		      mad_cal->audio_info.rms_diff_threshold & 0x3F);
+	snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_5,
+		      mad_cal->audio_info.rms_threshold_lsb);
+	snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_CTL_6,
+		      mad_cal->audio_info.rms_threshold_msb);
+
+	for (idx = 0; idx < ARRAY_SIZE(mad_cal->audio_info.iir_coefficients);
+	     idx++) {
+		snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_PTR,
+				    0x3F, idx);
+		snd_soc_write(codec, WCD934X_SOC_MAD_AUDIO_IIR_CTL_VAL,
+			      mad_cal->audio_info.iir_coefficients[idx]);
+		dev_dbg(codec->dev, "%s:MAD Audio IIR Coef[%d] = 0X%x",
+			__func__, idx,
+			mad_cal->audio_info.iir_coefficients[idx]);
+	}
+
+	/* Beacon */
+	snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_8,
+		      mad_cal->beacon_info.rms_omit_samples);
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_1,
+			    0x07 << 4, mad_cal->beacon_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_CTL_2, 0x03 << 2,
+			    mad_cal->beacon_info.detection_mechanism << 2);
+	snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_7,
+		      mad_cal->beacon_info.rms_diff_threshold & 0x1F);
+	snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_5,
+		      mad_cal->beacon_info.rms_threshold_lsb);
+	snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_CTL_6,
+		      mad_cal->beacon_info.rms_threshold_msb);
+
+	for (idx = 0; idx < ARRAY_SIZE(mad_cal->beacon_info.iir_coefficients);
+	     idx++) {
+		snd_soc_update_bits(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_PTR,
+				    0x3F, idx);
+		snd_soc_write(codec, WCD934X_SOC_MAD_BEACON_IIR_CTL_VAL,
+			      mad_cal->beacon_info.iir_coefficients[idx]);
+		dev_dbg(codec->dev, "%s:MAD Beacon IIR Coef[%d] = 0X%x",
+			__func__, idx,
+			mad_cal->beacon_info.iir_coefficients[idx]);
+	}
+
+	/* Ultrasound */
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_1,
+			    0x07 << 4,
+			    mad_cal->ultrasound_info.rms_comp_time << 4);
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_ULTR_CTL_2, 0x03 << 2,
+			    mad_cal->ultrasound_info.detection_mechanism << 2);
+	snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_7,
+		      mad_cal->ultrasound_info.rms_diff_threshold & 0x1F);
+	snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_5,
+		      mad_cal->ultrasound_info.rms_threshold_lsb);
+	snd_soc_write(codec, WCD934X_SOC_MAD_ULTR_CTL_6,
+		      mad_cal->ultrasound_info.rms_threshold_msb);
+
+done:
+	if (!hwdep_cal)
+		release_firmware(fw);
+
+	return ret;
+}
+
+static int __tavil_codec_enable_mad(struct snd_soc_codec *codec, bool enable)
+{
+	int rc = 0;
+
+	/* Return if CPE INPUT is DEC1 */
+	if (snd_soc_read(codec, WCD934X_CPE_SS_SVA_CFG) & 0x04) {
+		dev_dbg(codec->dev, "%s: MAD is bypassed, skip mad %s\n",
+			__func__, enable ? "enable" : "disable");
+		return rc;
+	}
+
+	dev_dbg(codec->dev, "%s: enable = %s\n", __func__,
+		enable ? "enable" : "disable");
+
+	if (enable) {
+		snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2,
+				    0x03, 0x03);
+		rc = tavil_codec_config_mad(codec);
+		if (rc < 0) {
+			snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2,
+					    0x03, 0x00);
+			goto done;
+		}
+
+		/* Turn on MAD clk */
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+				    0x01, 0x01);
+
+		/* Undo reset for MAD */
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+				    0x02, 0x00);
+	} else {
+		snd_soc_update_bits(codec, WCD934X_SOC_MAD_AUDIO_CTL_2,
+				    0x03, 0x00);
+		/* Reset the MAD block */
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+				    0x02, 0x02);
+		/* Turn off MAD clk */
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_MAD_CTL,
+				    0x01, 0x00);
+	}
+done:
+	return rc;
+}
+
+static int tavil_codec_ape_enable_mad(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol,
+				      int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int rc = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x40);
+		rc = __tavil_codec_enable_mad(codec, true);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x40, 0x00);
+		__tavil_codec_enable_mad(codec, false);
+		break;
+	}
+
+	dev_dbg(tavil->dev, "%s: event = %d\n", __func__, event);
+	return rc;
+}
+
+static int tavil_codec_cpe_mad_ctl(struct snd_soc_dapm_widget *w,
+				   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int rc = 0;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tavil->mad_switch_cnt++;
+		if (tavil->mad_switch_cnt != 1)
+			goto done;
+
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x20);
+		rc = __tavil_codec_enable_mad(codec, true);
+		if (rc < 0) {
+			tavil->mad_switch_cnt--;
+			goto done;
+		}
+
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		tavil->mad_switch_cnt--;
+		if (tavil->mad_switch_cnt != 0)
+			goto done;
+
+		snd_soc_update_bits(codec, WCD934X_CPE_SS_SVA_CFG, 0x20, 0x00);
+		__tavil_codec_enable_mad(codec, false);
+		break;
+	}
+done:
+	dev_dbg(tavil->dev, "%s: event = %d, mad_switch_cnt = %d\n",
+		__func__, event, tavil->mad_switch_cnt);
+	return rc;
+}
+
+static int tavil_get_asrc_mode(struct tavil_priv *tavil, int asrc,
+			       u8 main_sr, u8 mix_sr)
+{
+	u8 asrc_output_mode;
+	int asrc_mode = CONV_88P2K_TO_384K;
+
+	if ((asrc < 0) || (asrc >= ASRC_MAX))
+		return 0;
+
+	asrc_output_mode = tavil->asrc_output_mode[asrc];
+
+	if (asrc_output_mode) {
+		/*
+		 * If Mix sample rate is < 96KHz, use 96K to 352.8K
+		 * conversion, or else use 384K to 352.8K conversion
+		 */
+		if (mix_sr < 5)
+			asrc_mode = CONV_96K_TO_352P8K;
+		else
+			asrc_mode = CONV_384K_TO_352P8K;
+	} else {
+		/* Integer main and Fractional mix path */
+		if (main_sr < 8 && mix_sr > 9) {
+			asrc_mode = CONV_352P8K_TO_384K;
+		} else if (main_sr > 8 && mix_sr < 8) {
+			/* Fractional main and Integer mix path */
+			if (mix_sr < 5)
+				asrc_mode = CONV_96K_TO_352P8K;
+			else
+				asrc_mode = CONV_384K_TO_352P8K;
+		} else if (main_sr < 8 && mix_sr < 8) {
+			/* Integer main and Integer mix path */
+			asrc_mode = CONV_96K_TO_384K;
+		}
+	}
+
+	return asrc_mode;
+}
+
+static int tavil_codec_enable_asrc(struct snd_soc_codec *codec,
+				   int asrc_in, int event)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u16 cfg_reg, ctl_reg, clk_reg, asrc_ctl, mix_ctl_reg;
+	int asrc, ret = 0;
+	u8 main_sr, mix_sr, asrc_mode = 0;
+
+	switch (asrc_in) {
+	case ASRC_IN_HPHL:
+		cfg_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+		ctl_reg = WCD934X_CDC_RX1_RX_PATH_CTL;
+		clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL;
+		asrc_ctl = WCD934X_MIXING_ASRC0_CTL1;
+		asrc = ASRC0;
+		break;
+	case ASRC_IN_LO1:
+		cfg_reg = WCD934X_CDC_RX3_RX_PATH_CFG0;
+		ctl_reg = WCD934X_CDC_RX3_RX_PATH_CTL;
+		clk_reg = WCD934X_MIXING_ASRC0_CLK_RST_CTL;
+		asrc_ctl = WCD934X_MIXING_ASRC0_CTL1;
+		asrc = ASRC0;
+		break;
+	case ASRC_IN_HPHR:
+		cfg_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+		ctl_reg = WCD934X_CDC_RX2_RX_PATH_CTL;
+		clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL;
+		asrc_ctl = WCD934X_MIXING_ASRC1_CTL1;
+		asrc = ASRC1;
+		break;
+	case ASRC_IN_LO2:
+		cfg_reg = WCD934X_CDC_RX4_RX_PATH_CFG0;
+		ctl_reg = WCD934X_CDC_RX4_RX_PATH_CTL;
+		clk_reg = WCD934X_MIXING_ASRC1_CLK_RST_CTL;
+		asrc_ctl = WCD934X_MIXING_ASRC1_CTL1;
+		asrc = ASRC1;
+		break;
+	case ASRC_IN_SPKR1:
+		cfg_reg = WCD934X_CDC_RX7_RX_PATH_CFG0;
+		ctl_reg = WCD934X_CDC_RX7_RX_PATH_CTL;
+		clk_reg = WCD934X_MIXING_ASRC2_CLK_RST_CTL;
+		asrc_ctl = WCD934X_MIXING_ASRC2_CTL1;
+		asrc = ASRC2;
+		break;
+	case ASRC_IN_SPKR2:
+		cfg_reg = WCD934X_CDC_RX8_RX_PATH_CFG0;
+		ctl_reg = WCD934X_CDC_RX8_RX_PATH_CTL;
+		clk_reg = WCD934X_MIXING_ASRC3_CLK_RST_CTL;
+		asrc_ctl = WCD934X_MIXING_ASRC3_CTL1;
+		asrc = ASRC3;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid asrc input :%d\n", __func__,
+			asrc_in);
+		ret = -EINVAL;
+		goto done;
+	};
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (tavil->asrc_users[asrc] == 0) {
+			snd_soc_update_bits(codec, cfg_reg, 0x80, 0x80);
+			snd_soc_update_bits(codec, clk_reg, 0x01, 0x01);
+			main_sr = snd_soc_read(codec, ctl_reg) & 0x0F;
+			mix_ctl_reg = ctl_reg + 5;
+			mix_sr = snd_soc_read(codec, mix_ctl_reg) & 0x0F;
+			asrc_mode = tavil_get_asrc_mode(tavil, asrc,
+							main_sr, mix_sr);
+			dev_dbg(codec->dev, "%s: main_sr:%d mix_sr:%d asrc_mode %d\n",
+				__func__, main_sr, mix_sr, asrc_mode);
+			snd_soc_update_bits(codec, asrc_ctl, 0x07, asrc_mode);
+		}
+		tavil->asrc_users[asrc]++;
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tavil->asrc_users[asrc]--;
+		if (tavil->asrc_users[asrc] <= 0) {
+			tavil->asrc_users[asrc] = 0;
+			snd_soc_update_bits(codec, asrc_ctl, 0x07, 0x00);
+			snd_soc_update_bits(codec, cfg_reg, 0x80, 0x00);
+			snd_soc_update_bits(codec, clk_reg, 0x01, 0x00);
+		}
+		break;
+	};
+
+	dev_dbg(codec->dev, "%s: ASRC%d, users: %d\n",
+		__func__, asrc, tavil->asrc_users[asrc]);
+
+done:
+	return ret;
+}
+
+static int tavil_codec_enable_asrc_resampler(struct snd_soc_dapm_widget *w,
+					     struct snd_kcontrol *kcontrol,
+					     int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+	u8 cfg, asrc_in;
+
+	cfg = snd_soc_read(codec, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0);
+	if (!(cfg & 0xFF)) {
+		dev_err(codec->dev, "%s: ASRC%u input not selected\n",
+			__func__, w->shift);
+		return -EINVAL;
+	}
+
+	switch (w->shift) {
+	case ASRC0:
+		asrc_in = ((cfg & 0x03) == 1) ? ASRC_IN_HPHL : ASRC_IN_LO1;
+		ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+		break;
+	case ASRC1:
+		asrc_in = ((cfg & 0x0C) == 4) ? ASRC_IN_HPHR : ASRC_IN_LO2;
+		ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+		break;
+	case ASRC2:
+		asrc_in = ((cfg & 0x30) == 0x20) ? ASRC_IN_SPKR1 : ASRC_INVALID;
+		ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+		break;
+	case ASRC3:
+		asrc_in = ((cfg & 0xC0) == 0x80) ? ASRC_IN_SPKR2 : ASRC_INVALID;
+		ret = tavil_codec_enable_asrc(codec, asrc_in, event);
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid asrc:%u\n", __func__,
+			w->shift);
+		ret = -EINVAL;
+		break;
+	};
+
+	return ret;
+}
+
+static int tavil_enable_native_supply(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (++tavil->native_clk_users == 1) {
+			snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES,
+					    0x01, 0x01);
+			usleep_range(100, 120);
+			snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+					    0x06, 0x02);
+			snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE,
+					    0x04, 0x00);
+			usleep_range(30, 50);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x02, 0x02);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+					0x10, 0x10);
+		}
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		if (tavil->native_clk_users &&
+		    (--tavil->native_clk_users == 0)) {
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+					0x10, 0x00);
+			snd_soc_update_bits(codec,
+					WCD934X_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x02, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_GATE,
+					    0x04, 0x04);
+			snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CLK_SYS_MCLK2_PRG1,
+					    0x06, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CLK_SYS_PLL_ENABLES,
+					    0x01, 0x00);
+		}
+		break;
+	}
+
+	dev_dbg(codec->dev, "%s: native_clk_users: %d, event: %d\n",
+		__func__, tavil->native_clk_users, event);
+
+	return 0;
+}
+
+static void tavil_codec_hphdelay_lutbypass(struct snd_soc_codec *codec,
+				    u16 interp_idx, int event)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u8 hph_dly_mask;
+	u16 hph_lut_bypass_reg = 0;
+	u16 hph_comp_ctrl7 = 0;
+
+
+	switch (interp_idx) {
+	case INTERP_HPHL:
+		hph_dly_mask = 1;
+		hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHL_COMP_LUT;
+		hph_comp_ctrl7 = WCD934X_CDC_COMPANDER1_CTL7;
+		break;
+	case INTERP_HPHR:
+		hph_dly_mask = 2;
+		hph_lut_bypass_reg = WCD934X_CDC_TOP_HPHR_COMP_LUT;
+		hph_comp_ctrl7 = WCD934X_CDC_COMPANDER2_CTL7;
+		break;
+	default:
+		break;
+	}
+
+	if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0,
+				    hph_dly_mask, 0x0);
+		snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x80);
+		if (tavil->hph_mode == CLS_H_ULP)
+			snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x20);
+	}
+
+	if (hph_lut_bypass_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, WCD934X_CDC_CLSH_TEST0,
+				    hph_dly_mask, hph_dly_mask);
+		snd_soc_update_bits(codec, hph_lut_bypass_reg, 0x80, 0x00);
+		snd_soc_update_bits(codec, hph_comp_ctrl7, 0x20, 0x0);
+	}
+}
+
+static void tavil_codec_hd2_control(struct tavil_priv *priv,
+				    u16 interp_idx, int event)
+{
+	u16 hd2_scale_reg;
+	u16 hd2_enable_reg = 0;
+	struct snd_soc_codec *codec = priv->codec;
+
+	if (TAVIL_IS_1_1(priv->wcd9xxx))
+		return;
+
+	switch (interp_idx) {
+	case INTERP_HPHL:
+		hd2_scale_reg = WCD934X_CDC_RX1_RX_PATH_SEC3;
+		hd2_enable_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+		break;
+	case INTERP_HPHR:
+		hd2_scale_reg = WCD934X_CDC_RX2_RX_PATH_SEC3;
+		hd2_enable_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+		break;
+	}
+
+	if (hd2_enable_reg && SND_SOC_DAPM_EVENT_ON(event)) {
+		snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x14);
+		snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x04);
+	}
+
+	if (hd2_enable_reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, hd2_enable_reg, 0x04, 0x00);
+		snd_soc_update_bits(codec, hd2_scale_reg, 0x3C, 0x00);
+	}
+}
+
+static int tavil_codec_config_ear_spkr_gain(struct snd_soc_codec *codec,
+					    int event, int gain_reg)
+{
+	int comp_gain_offset, val;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	switch (tavil->swr.spkr_mode) {
+	/* Compander gain in SPKR_MODE1 case is 12 dB */
+	case WCD934X_SPKR_MODE_1:
+		comp_gain_offset = -12;
+		break;
+	/* Default case compander gain is 15 dB */
+	default:
+		comp_gain_offset = -15;
+		break;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		/* Apply ear spkr gain only if compander is enabled */
+		if (tavil->comp_enabled[COMPANDER_7] &&
+		    (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) &&
+		    (tavil->ear_spkr_gain != 0)) {
+			/* For example, val is -8(-12+5-1) for 4dB of gain */
+			val = comp_gain_offset + tavil->ear_spkr_gain - 1;
+			snd_soc_write(codec, gain_reg, val);
+
+			dev_dbg(codec->dev, "%s: RX7 Volume %d dB\n",
+				__func__, val);
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/*
+		 * Reset RX7 volume to 0 dB if compander is enabled and
+		 * ear_spkr_gain is non-zero.
+		 */
+		if (tavil->comp_enabled[COMPANDER_7] &&
+		    (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL) &&
+		    (tavil->ear_spkr_gain != 0)) {
+			snd_soc_write(codec, gain_reg, 0x0);
+
+			dev_dbg(codec->dev, "%s: Reset RX7 Volume to 0 dB\n",
+				__func__);
+		}
+		break;
+	}
+
+	return 0;
+}
+
+static int tavil_config_compander(struct snd_soc_codec *codec, int interp_n,
+				  int event)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int comp;
+	u16 comp_ctl0_reg, rx_path_cfg0_reg;
+
+	/* EAR does not have compander */
+	if (!interp_n)
+		return 0;
+
+	comp = interp_n - 1;
+	dev_dbg(codec->dev, "%s: event %d compander %d, enabled %d\n",
+		__func__, event, comp + 1, tavil->comp_enabled[comp]);
+
+	if (!tavil->comp_enabled[comp])
+		return 0;
+
+	comp_ctl0_reg = WCD934X_CDC_COMPANDER1_CTL0 + (comp * 8);
+	rx_path_cfg0_reg = WCD934X_CDC_RX1_RX_PATH_CFG0 + (comp * 20);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		/* Enable Compander Clock */
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x01);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+		snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x02);
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, rx_path_cfg0_reg, 0x02, 0x00);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x04);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x02);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x02, 0x00);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x01, 0x00);
+		snd_soc_update_bits(codec, comp_ctl0_reg, 0x04, 0x00);
+	}
+
+	return 0;
+}
+
+static void tavil_codec_idle_detect_control(struct snd_soc_codec *codec,
+					    int interp, int event)
+{
+	int reg = 0, mask, val;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	if (!tavil->idle_det_cfg.hph_idle_detect_en)
+		return;
+
+	if (interp == INTERP_HPHL) {
+		reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL;
+		mask = 0x01;
+		val = 0x01;
+	}
+	if (interp == INTERP_HPHR) {
+		reg = WCD934X_CDC_RX_IDLE_DET_PATH_CTL;
+		mask = 0x02;
+		val = 0x02;
+	}
+
+	if (reg && SND_SOC_DAPM_EVENT_ON(event))
+		snd_soc_update_bits(codec, reg, mask, val);
+
+	if (reg && SND_SOC_DAPM_EVENT_OFF(event)) {
+		snd_soc_update_bits(codec, reg, mask, 0x00);
+		tavil->idle_det_cfg.hph_idle_thr = 0;
+		snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, 0x0);
+	}
+}
+
+/**
+ * tavil_codec_enable_interp_clk - Enable main path Interpolator
+ * clock.
+ *
+ * @codec:    Codec instance
+ * @event:    Indicates speaker path gain offset value
+ * @intp_idx: Interpolator index
+ * Returns number of main clock users
+ */
+int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
+				  int event, int interp_idx)
+{
+	struct tavil_priv *tavil;
+	u16 main_reg;
+
+	if (!codec) {
+		pr_err("%s: codec is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	tavil  = snd_soc_codec_get_drvdata(codec);
+	main_reg = WCD934X_CDC_RX0_RX_PATH_CTL + (interp_idx * 20);
+
+	if (SND_SOC_DAPM_EVENT_ON(event)) {
+		if (tavil->main_clk_users[interp_idx] == 0) {
+			/* Main path PGA mute enable */
+			snd_soc_update_bits(codec, main_reg, 0x10, 0x10);
+			/* Clk enable */
+			snd_soc_update_bits(codec, main_reg, 0x20, 0x20);
+			tavil_codec_idle_detect_control(codec, interp_idx,
+							event);
+			tavil_codec_hd2_control(tavil, interp_idx, event);
+			tavil_codec_hphdelay_lutbypass(codec, interp_idx,
+						       event);
+			tavil_config_compander(codec, interp_idx, event);
+		}
+		tavil->main_clk_users[interp_idx]++;
+	}
+
+	if (SND_SOC_DAPM_EVENT_OFF(event)) {
+		tavil->main_clk_users[interp_idx]--;
+		if (tavil->main_clk_users[interp_idx] <= 0) {
+			tavil->main_clk_users[interp_idx] = 0;
+			tavil_config_compander(codec, interp_idx, event);
+			tavil_codec_hphdelay_lutbypass(codec, interp_idx,
+						       event);
+			tavil_codec_hd2_control(tavil, interp_idx, event);
+			tavil_codec_idle_detect_control(codec, interp_idx,
+							event);
+			/* Clk Disable */
+			snd_soc_update_bits(codec, main_reg, 0x20, 0x00);
+			/* Reset enable and disable */
+			snd_soc_update_bits(codec, main_reg, 0x40, 0x40);
+			snd_soc_update_bits(codec, main_reg, 0x40, 0x00);
+			/* Reset rate to 48K*/
+			snd_soc_update_bits(codec, main_reg, 0x0F, 0x04);
+		}
+	}
+
+	dev_dbg(codec->dev, "%s event %d main_clk_users %d\n",
+		__func__,  event, tavil->main_clk_users[interp_idx]);
+
+	return tavil->main_clk_users[interp_idx];
+}
+EXPORT_SYMBOL(tavil_codec_enable_interp_clk);
+
+static int tavil_codec_set_idle_detect_thr(struct snd_soc_codec *codec,
+					   int interp, int path_type)
+{
+	int port_id[4] = { 0, 0, 0, 0 };
+	int *port_ptr, num_ports;
+	int bit_width = 0, i;
+	int mux_reg, mux_reg_val;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int dai_id, idle_thr;
+
+	if ((interp != INTERP_HPHL) && (interp != INTERP_HPHR))
+		return 0;
+
+	if (!tavil->idle_det_cfg.hph_idle_detect_en)
+		return 0;
+
+	port_ptr = &port_id[0];
+	num_ports = 0;
+
+	/*
+	 * Read interpolator MUX input registers and find
+	 * which slimbus port is connected and store the port
+	 * numbers in port_id array.
+	 */
+	if (path_type == INTERP_MIX_PATH) {
+		mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1 +
+			  2 * (interp - 1);
+		mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f;
+
+		if ((mux_reg_val >= INTn_2_INP_SEL_RX0) &&
+		   (mux_reg_val < INTn_2_INP_SEL_PROXIMITY)) {
+			*port_ptr++ = mux_reg_val +
+				      WCD934X_RX_PORT_START_NUMBER - 1;
+			num_ports++;
+		}
+	}
+
+	if (path_type == INTERP_MAIN_PATH) {
+		mux_reg = WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0 +
+			  2 * (interp - 1);
+		mux_reg_val = snd_soc_read(codec, mux_reg) & 0x0f;
+		i = WCD934X_INTERP_MUX_NUM_INPUTS;
+
+		while (i) {
+			if ((mux_reg_val >= INTn_1_INP_SEL_RX0) &&
+			    (mux_reg_val <= INTn_1_INP_SEL_RX7)) {
+				*port_ptr++ = mux_reg_val +
+					WCD934X_RX_PORT_START_NUMBER -
+					INTn_1_INP_SEL_RX0;
+				num_ports++;
+			}
+			mux_reg_val = (snd_soc_read(codec, mux_reg) &
+						    0xf0) >> 4;
+			mux_reg += 1;
+			i--;
+		}
+	}
+
+	dev_dbg(codec->dev, "%s: num_ports: %d, ports[%d %d %d %d]\n",
+		__func__, num_ports, port_id[0], port_id[1],
+		port_id[2], port_id[3]);
+
+	i = 0;
+	while (num_ports) {
+		dai_id = tavil_find_playback_dai_id_for_port(port_id[i++],
+							     tavil);
+
+		if ((dai_id >= 0) && (dai_id < NUM_CODEC_DAIS)) {
+			dev_dbg(codec->dev, "%s: dai_id: %d bit_width: %d\n",
+				__func__, dai_id,
+				tavil->dai[dai_id].bit_width);
+
+			if (tavil->dai[dai_id].bit_width > bit_width)
+				bit_width = tavil->dai[dai_id].bit_width;
+		}
+
+		num_ports--;
+	}
+
+	switch (bit_width) {
+	case 16:
+		idle_thr = 0xff; /* F16 */
+		break;
+	case 24:
+	case 32:
+		idle_thr = 0x03; /* F22 */
+		break;
+	default:
+		idle_thr = 0x00;
+		break;
+	}
+
+	dev_dbg(codec->dev, "%s: (new) idle_thr: %d, (cur) idle_thr: %d\n",
+		__func__, idle_thr, tavil->idle_det_cfg.hph_idle_thr);
+
+	if ((tavil->idle_det_cfg.hph_idle_thr == 0) ||
+	    (idle_thr < tavil->idle_det_cfg.hph_idle_thr)) {
+		snd_soc_write(codec, WCD934X_CDC_RX_IDLE_DET_CFG3, idle_thr);
+		tavil->idle_det_cfg.hph_idle_thr = idle_thr;
+	}
+
+	return 0;
+}
+
+static int tavil_codec_enable_mix_path(struct snd_soc_dapm_widget *w,
+				       struct snd_kcontrol *kcontrol,
+				       int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u16 gain_reg, mix_reg;
+	int offset_val = 0;
+	int val = 0;
+
+	if (w->shift >= WCD934X_NUM_INTERPOLATORS ||
+	    w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) {
+		dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n",
+			__func__, w->shift, w->name);
+		return -EINVAL;
+	};
+
+	gain_reg = WCD934X_CDC_RX0_RX_VOL_MIX_CTL +
+					(w->shift * WCD934X_RX_PATH_CTL_OFFSET);
+	mix_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL +
+					(w->shift * WCD934X_RX_PATH_CTL_OFFSET);
+
+	if (w->shift == INTERP_SPKR1 ||  w->shift == INTERP_SPKR2)
+		__tavil_codec_enable_swr(w, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tavil_codec_set_idle_detect_thr(codec, w->shift,
+						INTERP_MIX_PATH);
+		tavil_codec_enable_interp_clk(codec, event, w->shift);
+		/* Clk enable */
+		snd_soc_update_bits(codec, mix_reg, 0x20, 0x20);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if ((tavil->swr.spkr_gain_offset ==
+		     WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tavil->comp_enabled[COMPANDER_7] ||
+		     tavil->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL ||
+		     gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) {
+			snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			offset_val = -2;
+		}
+		val = snd_soc_read(codec, gain_reg);
+		val += offset_val;
+		snd_soc_write(codec, gain_reg, val);
+		tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		/* Clk Disable */
+		snd_soc_update_bits(codec, mix_reg, 0x20, 0x00);
+		tavil_codec_enable_interp_clk(codec, event, w->shift);
+		/* Reset enable and disable */
+		snd_soc_update_bits(codec, mix_reg, 0x40, 0x40);
+		snd_soc_update_bits(codec, mix_reg, 0x40, 0x00);
+
+		if ((tavil->swr.spkr_gain_offset ==
+		     WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tavil->comp_enabled[COMPANDER_7] ||
+		     tavil->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD934X_CDC_RX7_RX_VOL_MIX_CTL ||
+		     gain_reg == WCD934X_CDC_RX8_RX_VOL_MIX_CTL)) {
+			snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			offset_val = 2;
+			val = snd_soc_read(codec, gain_reg);
+			val += offset_val;
+			snd_soc_write(codec, gain_reg, val);
+		}
+		tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	};
+	dev_dbg(codec->dev, "%s event %d name %s\n", __func__, event, w->name);
+
+	return 0;
+}
+
+/**
+ * tavil_get_dsd_config - Get pointer to dsd config structure
+ *
+ * @codec: pointer to snd_soc_codec structure
+ *
+ * Returns pointer to tavil_dsd_config structure
+ */
+struct tavil_dsd_config *tavil_get_dsd_config(struct snd_soc_codec *codec)
+{
+	struct tavil_priv *tavil;
+
+	if (!codec)
+		return NULL;
+
+	tavil = snd_soc_codec_get_drvdata(codec);
+
+	if (!tavil)
+		return NULL;
+
+	return tavil->dsd_config;
+}
+EXPORT_SYMBOL(tavil_get_dsd_config);
+
+static int tavil_codec_enable_main_path(struct snd_soc_dapm_widget *w,
+					struct snd_kcontrol *kcontrol,
+					int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u16 gain_reg;
+	u16 reg;
+	int val;
+	int offset_val = 0;
+
+	dev_dbg(codec->dev, "%s %d %s\n", __func__, event, w->name);
+
+	if (w->shift >= WCD934X_NUM_INTERPOLATORS ||
+	    w->shift == INTERP_LO3_NA || w->shift == INTERP_LO4_NA) {
+		dev_err(codec->dev, "%s: Invalid Interpolator value %d for name %s\n",
+			__func__, w->shift, w->name);
+		return -EINVAL;
+	};
+
+	reg = WCD934X_CDC_RX0_RX_PATH_CTL + (w->shift *
+					     WCD934X_RX_PATH_CTL_OFFSET);
+	gain_reg = WCD934X_CDC_RX0_RX_VOL_CTL + (w->shift *
+						 WCD934X_RX_PATH_CTL_OFFSET);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tavil_codec_set_idle_detect_thr(codec, w->shift,
+						INTERP_MAIN_PATH);
+		tavil_codec_enable_interp_clk(codec, event, w->shift);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* apply gain after int clk is enabled */
+		if ((tavil->swr.spkr_gain_offset ==
+					WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tavil->comp_enabled[COMPANDER_7] ||
+		     tavil->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) {
+			snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x01);
+			offset_val = -2;
+		}
+		val = snd_soc_read(codec, gain_reg);
+		val += offset_val;
+		snd_soc_write(codec, gain_reg, val);
+		tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tavil_codec_enable_interp_clk(codec, event, w->shift);
+
+		if ((tavil->swr.spkr_gain_offset ==
+					WCD934X_RX_GAIN_OFFSET_M1P5_DB) &&
+		    (tavil->comp_enabled[COMPANDER_7] ||
+		     tavil->comp_enabled[COMPANDER_8]) &&
+		    (gain_reg == WCD934X_CDC_RX7_RX_VOL_CTL ||
+		     gain_reg == WCD934X_CDC_RX8_RX_VOL_CTL)) {
+			snd_soc_update_bits(codec, WCD934X_CDC_RX7_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX7_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec, WCD934X_CDC_RX8_RX_PATH_SEC1,
+					    0x01, 0x00);
+			snd_soc_update_bits(codec,
+					    WCD934X_CDC_RX8_RX_PATH_MIX_SEC0,
+					    0x01, 0x00);
+			offset_val = 2;
+			val = snd_soc_read(codec, gain_reg);
+			val += offset_val;
+			snd_soc_write(codec, gain_reg, val);
+		}
+		tavil_codec_config_ear_spkr_gain(codec, event, gain_reg);
+		break;
+	};
+
+	return 0;
+}
+
+static int tavil_codec_set_iir_gain(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev, "%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU: /* fall through */
+	case SND_SOC_DAPM_PRE_PMD:
+		if (strnstr(w->name, "IIR0", sizeof("IIR0"))) {
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL));
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL));
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL));
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL));
+		} else {
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL));
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL));
+			snd_soc_write(codec,
+				WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL,
+			snd_soc_read(codec,
+				WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL));
+		}
+		break;
+	}
+	return 0;
+}
+
+static int tavil_codec_find_amic_input(struct snd_soc_codec *codec,
+				       int adc_mux_n)
+{
+	u16 mask, shift, adc_mux_in_reg;
+	u16 amic_mux_sel_reg;
+	bool is_amic;
+
+	if (adc_mux_n < 0 || adc_mux_n > WCD934X_MAX_VALID_ADC_MUX ||
+	    adc_mux_n == WCD934X_INVALID_ADC_MUX)
+		return 0;
+
+	if (adc_mux_n < 3) {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+				 adc_mux_n;
+		mask = 0x03;
+		shift = 0;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+				   2 * adc_mux_n;
+	} else if (adc_mux_n < 4) {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+		mask = 0x03;
+		shift = 0;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+				   2 * adc_mux_n;
+	} else if (adc_mux_n < 7) {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+				 (adc_mux_n - 4);
+		mask = 0x0C;
+		shift = 2;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+				   adc_mux_n - 4;
+	} else if (adc_mux_n < 8) {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+		mask = 0x0C;
+		shift = 2;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+				   adc_mux_n - 4;
+	} else if (adc_mux_n < 12) {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1 +
+				 ((adc_mux_n == 8) ? (adc_mux_n - 8) :
+				  (adc_mux_n - 9));
+		mask = 0x30;
+		shift = 4;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+				   adc_mux_n - 4;
+	} else if (adc_mux_n < 13) {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1;
+		mask = 0x30;
+		shift = 4;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+				   adc_mux_n - 4;
+	} else {
+		adc_mux_in_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1;
+		mask = 0xC0;
+		shift = 6;
+		amic_mux_sel_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+				   adc_mux_n - 4;
+	}
+
+	is_amic = (((snd_soc_read(codec, adc_mux_in_reg) & mask) >> shift)
+		    == 1);
+	if (!is_amic)
+		return 0;
+
+	return snd_soc_read(codec, amic_mux_sel_reg) & 0x07;
+}
+
+static void tavil_codec_set_tx_hold(struct snd_soc_codec *codec,
+				    u16 amic_reg, bool set)
+{
+	u8 mask = 0x20;
+	u8 val;
+
+	if (amic_reg == WCD934X_ANA_AMIC1 ||
+	    amic_reg == WCD934X_ANA_AMIC3)
+		mask = 0x40;
+
+	val = set ? mask : 0x00;
+
+	switch (amic_reg) {
+	case WCD934X_ANA_AMIC1:
+	case WCD934X_ANA_AMIC2:
+		snd_soc_update_bits(codec, WCD934X_ANA_AMIC2, mask, val);
+		break;
+	case WCD934X_ANA_AMIC3:
+	case WCD934X_ANA_AMIC4:
+		snd_soc_update_bits(codec, WCD934X_ANA_AMIC4, mask, val);
+		break;
+	default:
+		dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+			__func__, amic_reg);
+		break;
+	}
+}
+
+static int tavil_codec_tx_adc_cfg(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	int adc_mux_n = w->shift;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int amic_n;
+	u16 amic_reg;
+
+	dev_dbg(codec->dev, "%s: event: %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		amic_n = tavil_codec_find_amic_input(codec, adc_mux_n);
+		if (amic_n) {
+			amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1;
+			tavil_codec_set_tx_hold(codec, amic_reg, false);
+		}
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static u16 tavil_codec_get_amic_pwlvl_reg(struct snd_soc_codec *codec, int amic)
+{
+	u16 pwr_level_reg = 0;
+
+	switch (amic) {
+	case 1:
+	case 2:
+		pwr_level_reg = WCD934X_ANA_AMIC1;
+		break;
+
+	case 3:
+	case 4:
+		pwr_level_reg = WCD934X_ANA_AMIC3;
+		break;
+	default:
+		dev_dbg(codec->dev, "%s: invalid amic: %d\n",
+			__func__, amic);
+		break;
+	}
+
+	return pwr_level_reg;
+}
+
+#define  TX_HPF_CUT_OFF_FREQ_MASK 0x60
+#define  CF_MIN_3DB_4HZ     0x0
+#define  CF_MIN_3DB_75HZ    0x1
+#define  CF_MIN_3DB_150HZ   0x2
+
+static void tavil_tx_hpf_corner_freq_callback(struct work_struct *work)
+{
+	struct delayed_work *hpf_delayed_work;
+	struct hpf_work *hpf_work;
+	struct tavil_priv *tavil;
+	struct snd_soc_codec *codec;
+	u16 dec_cfg_reg, amic_reg, go_bit_reg;
+	u8 hpf_cut_off_freq;
+	int amic_n;
+
+	hpf_delayed_work = to_delayed_work(work);
+	hpf_work = container_of(hpf_delayed_work, struct hpf_work, dwork);
+	tavil = hpf_work->tavil;
+	codec = tavil->codec;
+	hpf_cut_off_freq = hpf_work->hpf_cut_off_freq;
+
+	dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * hpf_work->decimator;
+	go_bit_reg = dec_cfg_reg + 7;
+
+	dev_dbg(codec->dev, "%s: decimator %u hpf_cut_of_freq 0x%x\n",
+		__func__, hpf_work->decimator, hpf_cut_off_freq);
+
+	amic_n = tavil_codec_find_amic_input(codec, hpf_work->decimator);
+	if (amic_n) {
+		amic_reg = WCD934X_ANA_AMIC1 + amic_n - 1;
+		tavil_codec_set_tx_hold(codec, amic_reg, false);
+	}
+	snd_soc_update_bits(codec, dec_cfg_reg, TX_HPF_CUT_OFF_FREQ_MASK,
+			    hpf_cut_off_freq << 5);
+	snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x02);
+	/* Minimum 1 clk cycle delay is required as per HW spec */
+	usleep_range(1000, 1010);
+	snd_soc_update_bits(codec, go_bit_reg, 0x02, 0x00);
+}
+
+static void tavil_tx_mute_update_callback(struct work_struct *work)
+{
+	struct tx_mute_work *tx_mute_dwork;
+	struct tavil_priv *tavil;
+	struct delayed_work *delayed_work;
+	struct snd_soc_codec *codec;
+	u16 tx_vol_ctl_reg, hpf_gate_reg;
+
+	delayed_work = to_delayed_work(work);
+	tx_mute_dwork = container_of(delayed_work, struct tx_mute_work, dwork);
+	tavil = tx_mute_dwork->tavil;
+	codec = tavil->codec;
+
+	tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL +
+			 16 * tx_mute_dwork->decimator;
+	hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 +
+		       16 * tx_mute_dwork->decimator;
+	snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+}
+
+static int tavil_codec_enable_rx_path_clk(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	u16 sidetone_reg;
+
+	dev_dbg(codec->dev, "%s %d %d\n", __func__, event, w->shift);
+	sidetone_reg = WCD934X_CDC_RX0_RX_PATH_CFG1 + 0x14*(w->shift);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		if (!strcmp(w->name, "RX INT7 MIX2 INP"))
+			__tavil_codec_enable_swr(w, event);
+		tavil_codec_enable_interp_clk(codec, event, w->shift);
+		snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, sidetone_reg, 0x10, 0x00);
+		tavil_codec_enable_interp_clk(codec, event, w->shift);
+		if (!strcmp(w->name, "RX INT7 MIX2 INP"))
+			__tavil_codec_enable_swr(w, event);
+		break;
+	default:
+		break;
+	};
+	return 0;
+}
+
+static int tavil_codec_enable_dec(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	unsigned int decimator;
+	char *dec_adc_mux_name = NULL;
+	char *widget_name = NULL;
+	char *wname;
+	int ret = 0, amic_n;
+	u16 tx_vol_ctl_reg, pwr_level_reg = 0, dec_cfg_reg, hpf_gate_reg;
+	u16 tx_gain_ctl_reg;
+	char *dec;
+	u8 hpf_cut_off_freq;
+
+	dev_dbg(codec->dev, "%s %d\n", __func__, event);
+
+	widget_name = kstrndup(w->name, 15, GFP_KERNEL);
+	if (!widget_name)
+		return -ENOMEM;
+
+	wname = widget_name;
+	dec_adc_mux_name = strsep(&widget_name, " ");
+	if (!dec_adc_mux_name) {
+		dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+			__func__, w->name);
+		ret =  -EINVAL;
+		goto out;
+	}
+	dec_adc_mux_name = widget_name;
+
+	dec = strpbrk(dec_adc_mux_name, "012345678");
+	if (!dec) {
+		dev_err(codec->dev, "%s: decimator index not found\n",
+			__func__);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	ret = kstrtouint(dec, 10, &decimator);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: Invalid decimator = %s\n",
+			__func__, wname);
+		ret =  -EINVAL;
+		goto out;
+	}
+
+	dev_dbg(codec->dev, "%s(): widget = %s decimator = %u\n", __func__,
+			w->name, decimator);
+
+	tx_vol_ctl_reg = WCD934X_CDC_TX0_TX_PATH_CTL + 16 * decimator;
+	hpf_gate_reg = WCD934X_CDC_TX0_TX_PATH_SEC2 + 16 * decimator;
+	dec_cfg_reg = WCD934X_CDC_TX0_TX_PATH_CFG0 + 16 * decimator;
+	tx_gain_ctl_reg = WCD934X_CDC_TX0_TX_VOL_CTL + 16 * decimator;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		amic_n = tavil_codec_find_amic_input(codec, decimator);
+		if (amic_n)
+			pwr_level_reg = tavil_codec_get_amic_pwlvl_reg(codec,
+								       amic_n);
+
+		if (pwr_level_reg) {
+			switch ((snd_soc_read(codec, pwr_level_reg) &
+					      WCD934X_AMIC_PWR_LVL_MASK) >>
+					      WCD934X_AMIC_PWR_LVL_SHIFT) {
+			case WCD934X_AMIC_PWR_LEVEL_LP:
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    WCD934X_DEC_PWR_LVL_MASK,
+						    WCD934X_DEC_PWR_LVL_LP);
+				break;
+
+			case WCD934X_AMIC_PWR_LEVEL_HP:
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    WCD934X_DEC_PWR_LVL_MASK,
+						    WCD934X_DEC_PWR_LVL_HP);
+				break;
+			case WCD934X_AMIC_PWR_LEVEL_DEFAULT:
+			default:
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    WCD934X_DEC_PWR_LVL_MASK,
+						    WCD934X_DEC_PWR_LVL_DF);
+				break;
+			}
+		}
+		/* Enable TX PGA Mute */
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		hpf_cut_off_freq = (snd_soc_read(codec, dec_cfg_reg) &
+				   TX_HPF_CUT_OFF_FREQ_MASK) >> 5;
+
+		tavil->tx_hpf_work[decimator].hpf_cut_off_freq =
+							hpf_cut_off_freq;
+		if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+			snd_soc_update_bits(codec, dec_cfg_reg,
+					    TX_HPF_CUT_OFF_FREQ_MASK,
+					    CF_MIN_3DB_150HZ << 5);
+			snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x02);
+			/*
+			 * Minimum 1 clk cycle delay is required as per
+			 * HW spec.
+			 */
+			usleep_range(1000, 1010);
+			snd_soc_update_bits(codec, hpf_gate_reg, 0x02, 0x00);
+		}
+		/* schedule work queue to Remove Mute */
+		schedule_delayed_work(&tavil->tx_mute_dwork[decimator].dwork,
+				      msecs_to_jiffies(tx_unmute_delay));
+		if (tavil->tx_hpf_work[decimator].hpf_cut_off_freq !=
+							CF_MIN_3DB_150HZ)
+			schedule_delayed_work(
+					&tavil->tx_hpf_work[decimator].dwork,
+					msecs_to_jiffies(300));
+		/* apply gain after decimator is enabled */
+		snd_soc_write(codec, tx_gain_ctl_reg,
+			      snd_soc_read(codec, tx_gain_ctl_reg));
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		hpf_cut_off_freq =
+			tavil->tx_hpf_work[decimator].hpf_cut_off_freq;
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x10);
+		if (cancel_delayed_work_sync(
+		    &tavil->tx_hpf_work[decimator].dwork)) {
+			if (hpf_cut_off_freq != CF_MIN_3DB_150HZ) {
+				snd_soc_update_bits(codec, dec_cfg_reg,
+						    TX_HPF_CUT_OFF_FREQ_MASK,
+						    hpf_cut_off_freq << 5);
+				snd_soc_update_bits(codec, hpf_gate_reg,
+						    0x02, 0x02);
+				/*
+				 * Minimum 1 clk cycle delay is required as per
+				 * HW spec.
+				 */
+				usleep_range(1000, 1010);
+				snd_soc_update_bits(codec, hpf_gate_reg,
+						    0x02, 0x00);
+			}
+		}
+		cancel_delayed_work_sync(
+				&tavil->tx_mute_dwork[decimator].dwork);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		snd_soc_update_bits(codec, tx_vol_ctl_reg, 0x10, 0x00);
+		snd_soc_update_bits(codec, dec_cfg_reg,
+				    WCD934X_DEC_PWR_LVL_MASK,
+				    WCD934X_DEC_PWR_LVL_DF);
+		break;
+	};
+out:
+	kfree(wname);
+	return ret;
+}
+
+static u32 tavil_get_dmic_sample_rate(struct snd_soc_codec *codec,
+				      unsigned int dmic,
+				      struct wcd9xxx_pdata *pdata)
+{
+	u8 tx_stream_fs;
+	u8 adc_mux_index = 0, adc_mux_sel = 0;
+	bool dec_found = false;
+	u16 adc_mux_ctl_reg, tx_fs_reg;
+	u32 dmic_fs;
+
+	while (dec_found == 0 && adc_mux_index < WCD934X_MAX_VALID_ADC_MUX) {
+		if (adc_mux_index < 4) {
+			adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0 +
+						(adc_mux_index * 2);
+		} else if (adc_mux_index < WCD934X_INVALID_ADC_MUX) {
+			adc_mux_ctl_reg = WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0 +
+						adc_mux_index - 4;
+		} else if (adc_mux_index == WCD934X_INVALID_ADC_MUX) {
+			++adc_mux_index;
+			continue;
+		}
+		adc_mux_sel = ((snd_soc_read(codec, adc_mux_ctl_reg) &
+					0xF8) >> 3) - 1;
+
+		if (adc_mux_sel == dmic) {
+			dec_found = true;
+			break;
+		}
+
+		++adc_mux_index;
+	}
+
+	if (dec_found && adc_mux_index <= 8) {
+		tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL + (16 * adc_mux_index);
+		tx_stream_fs = snd_soc_read(codec, tx_fs_reg) & 0x0F;
+		if (tx_stream_fs <= 4)  {
+			if (pdata->dmic_sample_rate <=
+					WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ)
+				dmic_fs = pdata->dmic_sample_rate;
+			else
+				dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_2P4MHZ;
+		} else
+			dmic_fs = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+	} else {
+		dmic_fs = pdata->dmic_sample_rate;
+	}
+
+	return dmic_fs;
+}
+
+static u8 tavil_get_dmic_clk_val(struct snd_soc_codec *codec,
+				 u32 mclk_rate, u32 dmic_clk_rate)
+{
+	u32 div_factor;
+	u8 dmic_ctl_val;
+
+	dev_dbg(codec->dev,
+		"%s: mclk_rate = %d, dmic_sample_rate = %d\n",
+		__func__, mclk_rate, dmic_clk_rate);
+
+	/* Default value to return in case of error */
+	if (mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2;
+	else
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3;
+
+	if (dmic_clk_rate == 0) {
+		dev_err(codec->dev,
+			"%s: dmic_sample_rate cannot be 0\n",
+			__func__);
+		goto done;
+	}
+
+	div_factor = mclk_rate / dmic_clk_rate;
+	switch (div_factor) {
+	case 2:
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_2;
+		break;
+	case 3:
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_3;
+		break;
+	case 4:
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_4;
+		break;
+	case 6:
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_6;
+		break;
+	case 8:
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_8;
+		break;
+	case 16:
+		dmic_ctl_val = WCD934X_DMIC_CLK_DIV_16;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: Invalid div_factor %u, clk_rate(%u), dmic_rate(%u)\n",
+			__func__, div_factor, mclk_rate, dmic_clk_rate);
+		break;
+	}
+
+done:
+	return dmic_ctl_val;
+}
+
+static int tavil_codec_enable_adc(struct snd_soc_dapm_widget *w,
+				  struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	dev_dbg(codec->dev, "%s: event:%d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		tavil_codec_set_tx_hold(codec, w->reg, true);
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int tavil_codec_enable_dmic(struct snd_soc_dapm_widget *w,
+				   struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx_pdata *pdata = dev_get_platdata(codec->dev->parent);
+	u8  dmic_clk_en = 0x01;
+	u16 dmic_clk_reg;
+	s32 *dmic_clk_cnt;
+	u8 dmic_rate_val, dmic_rate_shift = 1;
+	unsigned int dmic;
+	u32 dmic_sample_rate;
+	int ret;
+	char *wname;
+
+	wname = strpbrk(w->name, "012345");
+	if (!wname) {
+		dev_err(codec->dev, "%s: widget not found\n", __func__);
+		return -EINVAL;
+	}
+
+	ret = kstrtouint(wname, 10, &dmic);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: Invalid DMIC line on the codec\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	switch (dmic) {
+	case 0:
+	case 1:
+		dmic_clk_cnt = &(tavil->dmic_0_1_clk_cnt);
+		dmic_clk_reg = WCD934X_CPE_SS_DMIC0_CTL;
+		break;
+	case 2:
+	case 3:
+		dmic_clk_cnt = &(tavil->dmic_2_3_clk_cnt);
+		dmic_clk_reg = WCD934X_CPE_SS_DMIC1_CTL;
+		break;
+	case 4:
+	case 5:
+		dmic_clk_cnt = &(tavil->dmic_4_5_clk_cnt);
+		dmic_clk_reg = WCD934X_CPE_SS_DMIC2_CTL;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid DMIC Selection\n",
+			__func__);
+		return -EINVAL;
+	};
+	dev_dbg(codec->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:
+		dmic_sample_rate = tavil_get_dmic_sample_rate(codec, dmic,
+							      pdata);
+		dmic_rate_val =
+			tavil_get_dmic_clk_val(codec,
+					       pdata->mclk_rate,
+					       dmic_sample_rate);
+
+		(*dmic_clk_cnt)++;
+		if (*dmic_clk_cnt == 1) {
+			snd_soc_update_bits(codec, dmic_clk_reg,
+					    0x07 << dmic_rate_shift,
+					    dmic_rate_val << dmic_rate_shift);
+			snd_soc_update_bits(codec, dmic_clk_reg,
+					    dmic_clk_en, dmic_clk_en);
+		}
+
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		dmic_rate_val =
+			tavil_get_dmic_clk_val(codec,
+					       pdata->mclk_rate,
+					       pdata->mad_dmic_sample_rate);
+		(*dmic_clk_cnt)--;
+		if (*dmic_clk_cnt  == 0) {
+			snd_soc_update_bits(codec, dmic_clk_reg,
+					    dmic_clk_en, 0);
+			snd_soc_update_bits(codec, dmic_clk_reg,
+					    0x07 << dmic_rate_shift,
+					    dmic_rate_val << dmic_rate_shift);
+		}
+		break;
+	};
+
+	return 0;
+}
+
+/*
+ * tavil_mbhc_micb_adjust_voltage: adjust specific micbias voltage
+ * @codec: handle to snd_soc_codec *
+ * @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 tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
+				   int req_volt, int micb_num)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int cur_vout_ctl, req_vout_ctl;
+	int micb_reg, micb_val, micb_en;
+	int ret = 0;
+
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_reg = WCD934X_ANA_MICB1;
+		break;
+	case MIC_BIAS_2:
+		micb_reg = WCD934X_ANA_MICB2;
+		break;
+	case MIC_BIAS_3:
+		micb_reg = WCD934X_ANA_MICB3;
+		break;
+	case MIC_BIAS_4:
+		micb_reg = WCD934X_ANA_MICB4;
+		break;
+	default:
+		return -EINVAL;
+	}
+	mutex_lock(&tavil->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.
+	 */
+	micb_val = snd_soc_read(codec, micb_reg);
+	micb_en = (micb_val & 0xC0) >> 6;
+	cur_vout_ctl = micb_val & 0x3F;
+
+	req_vout_ctl = wcd934x_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(codec->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_update_bits(codec, micb_reg, 0xC0, 0x80);
+
+	snd_soc_update_bits(codec, micb_reg, 0x3F, req_vout_ctl);
+
+	if (micb_en == 0x1) {
+		snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+		/*
+		 * Add 2ms delay as per HW requirement after enabling
+		 * micbias
+		 */
+		usleep_range(2000, 2100);
+	}
+exit:
+	mutex_unlock(&tavil->micb_lock);
+	return ret;
+}
+EXPORT_SYMBOL(tavil_mbhc_micb_adjust_voltage);
+
+/*
+ * tavil_micbias_control: enable/disable micbias
+ * @codec: handle to snd_soc_codec *
+ * @micb_num: micbias to be enabled/disabled, e.g. micbias1 or micbias2
+ * @req: control requested, enable/disable or pullup enable/disable
+ * @is_dapm: triggered by dapm or not
+ *
+ * return 0 if control is success or error code in case of failure
+ */
+int tavil_micbias_control(struct snd_soc_codec *codec,
+			  int micb_num, int req, bool is_dapm)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	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;
+
+	if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) {
+		dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+			__func__, micb_index);
+		return -EINVAL;
+	}
+
+	switch (micb_num) {
+	case MIC_BIAS_1:
+		micb_reg = WCD934X_ANA_MICB1;
+		break;
+	case MIC_BIAS_2:
+		micb_reg = WCD934X_ANA_MICB2;
+		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 = WCD934X_ANA_MICB3;
+		break;
+	case MIC_BIAS_4:
+		micb_reg = WCD934X_ANA_MICB4;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid micbias number: %d\n",
+			__func__, micb_num);
+		return -EINVAL;
+	}
+	mutex_lock(&tavil->micb_lock);
+
+	switch (req) {
+	case MICB_PULLUP_ENABLE:
+		tavil->pullup_ref[micb_index]++;
+		if ((tavil->pullup_ref[micb_index] == 1) &&
+		    (tavil->micb_ref[micb_index] == 0))
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+		break;
+	case MICB_PULLUP_DISABLE:
+		if (tavil->pullup_ref[micb_index] > 0)
+			tavil->pullup_ref[micb_index]--;
+		if ((tavil->pullup_ref[micb_index] == 0) &&
+		    (tavil->micb_ref[micb_index] == 0))
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+		break;
+	case MICB_ENABLE:
+		tavil->micb_ref[micb_index]++;
+		if (tavil->micb_ref[micb_index] == 1) {
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x40);
+			if (post_on_event && tavil->mbhc)
+				blocking_notifier_call_chain(
+						&tavil->mbhc->notifier,
+						post_on_event,
+						&tavil->mbhc->wcd_mbhc);
+		}
+		if (is_dapm && post_dapm_on && tavil->mbhc)
+			blocking_notifier_call_chain(&tavil->mbhc->notifier,
+					post_dapm_on, &tavil->mbhc->wcd_mbhc);
+		break;
+	case MICB_DISABLE:
+		if (tavil->micb_ref[micb_index] > 0)
+			tavil->micb_ref[micb_index]--;
+		if ((tavil->micb_ref[micb_index] == 0) &&
+		    (tavil->pullup_ref[micb_index] > 0))
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x80);
+		else if ((tavil->micb_ref[micb_index] == 0) &&
+			 (tavil->pullup_ref[micb_index] == 0)) {
+			if (pre_off_event && tavil->mbhc)
+				blocking_notifier_call_chain(
+						&tavil->mbhc->notifier,
+						pre_off_event,
+						&tavil->mbhc->wcd_mbhc);
+			snd_soc_update_bits(codec, micb_reg, 0xC0, 0x00);
+			if (post_off_event && tavil->mbhc)
+				blocking_notifier_call_chain(
+						&tavil->mbhc->notifier,
+						post_off_event,
+						&tavil->mbhc->wcd_mbhc);
+		}
+		if (is_dapm && post_dapm_off && tavil->mbhc)
+			blocking_notifier_call_chain(&tavil->mbhc->notifier,
+					post_dapm_off, &tavil->mbhc->wcd_mbhc);
+		break;
+	};
+
+	dev_dbg(codec->dev, "%s: micb_num:%d, micb_ref: %d, pullup_ref: %d\n",
+		__func__, micb_num, tavil->micb_ref[micb_index],
+		tavil->pullup_ref[micb_index]);
+
+	mutex_unlock(&tavil->micb_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(tavil_micbias_control);
+
+static int __tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+					int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int micb_num;
+
+	dev_dbg(codec->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 if (strnstr(w->name, "MIC BIAS4", sizeof("MIC BIAS4")))
+		micb_num = MIC_BIAS_4;
+	else
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		/*
+		 * MIC BIAS can also be requested by MBHC,
+		 * so use ref count to handle micbias pullup
+		 * and enable requests
+		 */
+		tavil_micbias_control(codec, micb_num, MICB_ENABLE, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		/* wait for cnp time */
+		usleep_range(1000, 1100);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		tavil_micbias_control(codec, micb_num, MICB_DISABLE, true);
+		break;
+	};
+
+	return 0;
+}
+
+/*
+ * tavil_codec_enable_standalone_micbias - enable micbias standalone
+ * @codec: pointer to codec instance
+ * @micb_num: number of micbias to be enabled
+ * @enable: true to enable micbias or false to disable
+ *
+ * This function is used to enable micbias (1, 2, 3 or 4) during
+ * standalone independent of whether TX use-case is running or not
+ *
+ * Return: error code in case of failure or 0 for success
+ */
+int tavil_codec_enable_standalone_micbias(struct snd_soc_codec *codec,
+					  int micb_num,
+					  bool enable)
+{
+	const char * const micb_names[] = {
+		DAPM_MICBIAS1_STANDALONE, DAPM_MICBIAS2_STANDALONE,
+		DAPM_MICBIAS3_STANDALONE, DAPM_MICBIAS4_STANDALONE
+	};
+	int micb_index = micb_num - 1;
+	int rc;
+
+	if (!codec) {
+		pr_err("%s: Codec memory is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if ((micb_index < 0) || (micb_index > TAVIL_MAX_MICBIAS - 1)) {
+		dev_err(codec->dev, "%s: Invalid micbias index, micb_ind:%d\n",
+			__func__, micb_index);
+		return -EINVAL;
+	}
+
+	if (enable)
+		rc = snd_soc_dapm_force_enable_pin(
+						snd_soc_codec_get_dapm(codec),
+						micb_names[micb_index]);
+	else
+		rc = snd_soc_dapm_disable_pin(snd_soc_codec_get_dapm(codec),
+					      micb_names[micb_index]);
+
+	if (!rc)
+		snd_soc_dapm_sync(snd_soc_codec_get_dapm(codec));
+	else
+		dev_err(codec->dev, "%s: micbias%d force %s pin failed\n",
+			__func__, micb_num, (enable ? "enable" : "disable"));
+
+	return rc;
+}
+EXPORT_SYMBOL(tavil_codec_enable_standalone_micbias);
+
+static int tavil_codec_force_enable_micbias(struct snd_soc_dapm_widget *w,
+					    struct snd_kcontrol *kcontrol,
+					    int event)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wcd_resmgr_enable_master_bias(tavil->resmgr);
+		tavil_cdc_mclk_enable(codec, true);
+		ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_PRE_PMU);
+		/* Wait for 1ms for better cnp */
+		usleep_range(1000, 1100);
+		tavil_cdc_mclk_enable(codec, false);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = __tavil_codec_enable_micbias(w, SND_SOC_DAPM_POST_PMD);
+		wcd_resmgr_disable_master_bias(tavil->resmgr);
+		break;
+	}
+
+	return ret;
+}
+
+static int tavil_codec_enable_micbias(struct snd_soc_dapm_widget *w,
+				      struct snd_kcontrol *kcontrol, int event)
+{
+	return __tavil_codec_enable_micbias(w, event);
+}
+
+
+static const struct reg_sequence tavil_hph_reset_tbl[] = {
+	{ WCD934X_HPH_CNP_EN, 0x80 },
+	{ WCD934X_HPH_CNP_WG_CTL, 0x9A },
+	{ WCD934X_HPH_CNP_WG_TIME, 0x14 },
+	{ WCD934X_HPH_OCP_CTL, 0x28 },
+	{ WCD934X_HPH_AUTO_CHOP, 0x16 },
+	{ WCD934X_HPH_CHOP_CTL, 0x83 },
+	{ WCD934X_HPH_PA_CTL1, 0x46 },
+	{ WCD934X_HPH_PA_CTL2, 0x50 },
+	{ WCD934X_HPH_L_EN, 0x80 },
+	{ WCD934X_HPH_L_TEST, 0xE0 },
+	{ WCD934X_HPH_L_ATEST, 0x50 },
+	{ WCD934X_HPH_R_EN, 0x80 },
+	{ WCD934X_HPH_R_TEST, 0xE0 },
+	{ WCD934X_HPH_R_ATEST, 0x54 },
+	{ WCD934X_HPH_RDAC_CLK_CTL1, 0x99 },
+	{ WCD934X_HPH_RDAC_CLK_CTL2, 0x9B },
+	{ WCD934X_HPH_RDAC_LDO_CTL, 0x33 },
+	{ WCD934X_HPH_RDAC_CHOP_CLK_LP_CTL, 0x00 },
+	{ WCD934X_HPH_REFBUFF_UHQA_CTL, 0xA8 },
+};
+
+static const struct reg_sequence tavil_hph_reset_tbl_1_0[] = {
+	{ WCD934X_HPH_REFBUFF_LP_CTL, 0x0A },
+	{ WCD934X_HPH_L_DAC_CTL, 0x00 },
+	{ WCD934X_HPH_R_DAC_CTL, 0x00 },
+	{ WCD934X_HPH_NEW_ANA_HPH2, 0x00 },
+	{ WCD934X_HPH_NEW_ANA_HPH3, 0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0xA0 },
+	{ WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
+	{ WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e},
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+};
+
+static const struct reg_sequence tavil_hph_reset_tbl_1_1[] = {
+	{ WCD934X_HPH_REFBUFF_LP_CTL, 0x0E },
+	{ WCD934X_HPH_L_DAC_CTL, 0x00 },
+	{ WCD934X_HPH_R_DAC_CTL, 0x00 },
+	{ WCD934X_HPH_NEW_ANA_HPH2, 0x00 },
+	{ WCD934X_HPH_NEW_ANA_HPH3, 0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0x40 },
+	{ WCD934X_HPH_NEW_INT_RDAC_HD2_CTL, 0x81 },
+	{ WCD934X_HPH_NEW_INT_RDAC_VREF_CTL, 0x10 },
+	{ WCD934X_HPH_NEW_INT_RDAC_OVERRIDE_CTL, 0x00 },
+	{ WCD934X_HPH_NEW_INT_RDAC_MISC1, 0x81 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC1, 0x22 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC, 0x00 },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0xFE },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER2, 0x2 },
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER3, 0x4e},
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER4, 0x54 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC2, 0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_RDAC_MISC3, 0x00 },
+};
+
+static const struct tavil_reg_mask_val tavil_pa_disable[] = {
+	{ WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x10 }, /* RX1 mute enable */
+	{ WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x10 }, /* RX2 mute enable */
+	{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 }, /* GM3 boost disable */
+	{ WCD934X_ANA_HPH, 0x80, 0x00 }, /* HPHL PA disable */
+	{ WCD934X_ANA_HPH, 0x40, 0x00 }, /* HPHR PA disable */
+	{ WCD934X_ANA_HPH, 0x20, 0x00 }, /* HPHL REF dsable */
+	{ WCD934X_ANA_HPH, 0x10, 0x00 }, /* HPHR REF disable */
+};
+
+static const struct tavil_reg_mask_val tavil_ocp_en_seq[] = {
+	{ WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */
+	{ WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
+	{ WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
+	{ WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
+};
+
+static const struct tavil_reg_mask_val tavil_ocp_en_seq_1[] = {
+	{ WCD934X_RX_OCP_CTL, 0x0F, 0x02 }, /* OCP number of attempts is 2 */
+	{ WCD934X_HPH_OCP_CTL, 0xFA, 0x3A }, /* OCP current limit */
+};
+
+/* LO-HIFI */
+static const struct tavil_reg_mask_val tavil_pre_pa_en_lohifi[] = {
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
+	{ WCD934X_FLYBACK_VNEG_CTRL_4, 0xf0, 0x80 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x20 },
+	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
+	{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
+	{ WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0xc0 },
+	{ WCD934X_HPH_PA_CTL1, 0x0e, 0x02 },
+	{ WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
+};
+
+static const struct tavil_reg_mask_val tavil_pre_pa_en[] = {
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x00 },
+	{ WCD934X_HPH_NEW_INT_PA_MISC2, 0x20, 0x0 },
+	{ WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL, 0xf0, 0x40 },
+	{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x00 },
+	{ WCD934X_RX_BIAS_HPH_LOWPOWER, 0xf0, 0x80 },
+	{ WCD934X_HPH_PA_CTL1, 0x0e, 0x06 },
+	{ WCD934X_HPH_REFBUFF_LP_CTL, 0x06, 0x06 },
+};
+
+static const struct tavil_reg_mask_val tavil_post_pa_en[] = {
+	{ WCD934X_HPH_L_TEST, 0x01, 0x01 }, /* Enable HPHL OCP */
+	{ WCD934X_HPH_R_TEST, 0x01, 0x01 }, /* Enable HPHR OCP */
+	{ WCD934X_CDC_RX1_RX_PATH_CTL, 0x30, 0x20 }, /* RX1 mute disable */
+	{ WCD934X_CDC_RX2_RX_PATH_CTL, 0x30, 0x20 }, /* RX2 mute disable */
+	{ WCD934X_HPH_CNP_WG_CTL, 0x80, 0x80 }, /* GM3 boost enable */
+	{ WCD934X_HPH_NEW_INT_HPH_TIMER1, 0x02, 0x02 },
+};
+
+static void tavil_codec_hph_reg_range_read(struct regmap *map, u8 *buf)
+{
+	regmap_bulk_read(map, WCD934X_HPH_CNP_EN, buf, TAVIL_HPH_REG_RANGE_1);
+	regmap_bulk_read(map, WCD934X_HPH_NEW_ANA_HPH2,
+			 buf + TAVIL_HPH_REG_RANGE_1, TAVIL_HPH_REG_RANGE_2);
+	regmap_bulk_read(map, WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL,
+			 buf + TAVIL_HPH_REG_RANGE_1 + TAVIL_HPH_REG_RANGE_2,
+			 TAVIL_HPH_REG_RANGE_3);
+}
+
+static void tavil_codec_hph_reg_recover(struct tavil_priv *tavil,
+					struct regmap *map, int pa_status)
+{
+	int i;
+	unsigned int reg;
+
+	blocking_notifier_call_chain(&tavil->mbhc->notifier,
+				     WCD_EVENT_OCP_OFF,
+				     &tavil->mbhc->wcd_mbhc);
+
+	if (pa_status & 0xC0)
+		goto pa_en_restore;
+
+	dev_dbg(tavil->dev, "%s: HPH PA in disable state (0x%x)\n",
+		__func__, pa_status);
+
+	regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x10);
+	regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x10);
+	regmap_write_bits(map, WCD934X_ANA_HPH, 0xC0, 0x00);
+	regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x00);
+	regmap_write_bits(map, WCD934X_CDC_RX1_RX_PATH_CTL, 0x10, 0x00);
+	regmap_write_bits(map, WCD934X_CDC_RX2_RX_PATH_CTL, 0x10, 0x00);
+
+	/* Restore to HW defaults */
+	regmap_multi_reg_write(map, tavil_hph_reset_tbl,
+			       ARRAY_SIZE(tavil_hph_reset_tbl));
+	if (TAVIL_IS_1_1(tavil->wcd9xxx))
+		regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1,
+				ARRAY_SIZE(tavil_hph_reset_tbl_1_1));
+	if (TAVIL_IS_1_0(tavil->wcd9xxx))
+		regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0,
+				ARRAY_SIZE(tavil_hph_reset_tbl_1_0));
+
+	for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq); i++)
+		regmap_write_bits(map, tavil_ocp_en_seq[i].reg,
+				  tavil_ocp_en_seq[i].mask,
+				  tavil_ocp_en_seq[i].val);
+	goto end;
+
+
+pa_en_restore:
+	dev_dbg(tavil->dev, "%s: HPH PA in enable state (0x%x)\n",
+		__func__, pa_status);
+
+	/* Disable PA and other registers before restoring */
+	for (i = 0; i < ARRAY_SIZE(tavil_pa_disable); i++) {
+		if (TAVIL_IS_1_1(tavil->wcd9xxx) &&
+		    (tavil_pa_disable[i].reg == WCD934X_HPH_CNP_WG_CTL))
+			continue;
+		regmap_write_bits(map, tavil_pa_disable[i].reg,
+				  tavil_pa_disable[i].mask,
+				  tavil_pa_disable[i].val);
+	}
+
+	regmap_multi_reg_write(map, tavil_hph_reset_tbl,
+			       ARRAY_SIZE(tavil_hph_reset_tbl));
+	if (TAVIL_IS_1_1(tavil->wcd9xxx))
+		regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_1,
+				ARRAY_SIZE(tavil_hph_reset_tbl_1_1));
+	if (TAVIL_IS_1_0(tavil->wcd9xxx))
+		regmap_multi_reg_write(map, tavil_hph_reset_tbl_1_0,
+				ARRAY_SIZE(tavil_hph_reset_tbl_1_0));
+
+	for (i = 0; i < ARRAY_SIZE(tavil_ocp_en_seq_1); i++)
+		regmap_write_bits(map, tavil_ocp_en_seq_1[i].reg,
+				  tavil_ocp_en_seq_1[i].mask,
+				  tavil_ocp_en_seq_1[i].val);
+
+	if (tavil->hph_mode == CLS_H_LOHIFI) {
+		for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en_lohifi); i++) {
+			reg = tavil_pre_pa_en_lohifi[i].reg;
+			if ((TAVIL_IS_1_1(tavil->wcd9xxx)) &&
+			    ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) ||
+			     (reg == WCD934X_HPH_CNP_WG_CTL) ||
+			     (reg == WCD934X_HPH_REFBUFF_LP_CTL)))
+				continue;
+			regmap_write_bits(map,
+					  tavil_pre_pa_en_lohifi[i].reg,
+					  tavil_pre_pa_en_lohifi[i].mask,
+					  tavil_pre_pa_en_lohifi[i].val);
+		}
+	} else {
+		for (i = 0; i < ARRAY_SIZE(tavil_pre_pa_en); i++) {
+			reg = tavil_pre_pa_en[i].reg;
+			if ((TAVIL_IS_1_1(tavil->wcd9xxx)) &&
+			    ((reg == WCD934X_HPH_NEW_INT_RDAC_GAIN_CTL) ||
+			     (reg == WCD934X_HPH_CNP_WG_CTL) ||
+			     (reg == WCD934X_HPH_REFBUFF_LP_CTL)))
+				continue;
+			regmap_write_bits(map, tavil_pre_pa_en[i].reg,
+					  tavil_pre_pa_en[i].mask,
+					  tavil_pre_pa_en[i].val);
+		}
+	}
+
+	if (TAVIL_IS_1_1(tavil->wcd9xxx)) {
+		regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0x84);
+		regmap_write(map, WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0x84);
+	}
+
+	regmap_write_bits(map, WCD934X_ANA_HPH, 0x0C, pa_status & 0x0C);
+	regmap_write_bits(map, WCD934X_ANA_HPH, 0x30, 0x30);
+	/* wait for 100usec after HPH DAC is enabled */
+	usleep_range(100, 110);
+	regmap_write(map, WCD934X_ANA_HPH, pa_status);
+	/* Sleep for 7msec after PA is enabled */
+	usleep_range(7000, 7100);
+
+	for (i = 0; i < ARRAY_SIZE(tavil_post_pa_en); i++) {
+		if ((TAVIL_IS_1_1(tavil->wcd9xxx)) &&
+		    (tavil_post_pa_en[i].reg == WCD934X_HPH_CNP_WG_CTL))
+			continue;
+		regmap_write_bits(map, tavil_post_pa_en[i].reg,
+				  tavil_post_pa_en[i].mask,
+				  tavil_post_pa_en[i].val);
+	}
+
+end:
+	tavil->mbhc->is_hph_recover = true;
+	blocking_notifier_call_chain(
+			&tavil->mbhc->notifier,
+			WCD_EVENT_OCP_ON,
+			&tavil->mbhc->wcd_mbhc);
+}
+
+static int tavil_codec_reset_hph_registers(struct snd_soc_dapm_widget *w,
+					   struct snd_kcontrol *kcontrol,
+					   int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	u8 cache_val[TAVIL_HPH_TOTAL_REG];
+	u8 hw_val[TAVIL_HPH_TOTAL_REG];
+	int pa_status;
+	int ret;
+
+	dev_dbg(wcd9xxx->dev, "%s: event: %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		memset(cache_val, 0, TAVIL_HPH_TOTAL_REG);
+		memset(hw_val, 0, TAVIL_HPH_TOTAL_REG);
+
+		regmap_read(wcd9xxx->regmap, WCD934X_ANA_HPH, &pa_status);
+
+		tavil_codec_hph_reg_range_read(wcd9xxx->regmap, cache_val);
+
+		/* Read register values from HW directly */
+		regcache_cache_bypass(wcd9xxx->regmap, true);
+		tavil_codec_hph_reg_range_read(wcd9xxx->regmap, hw_val);
+		regcache_cache_bypass(wcd9xxx->regmap, false);
+
+		/* compare both the registers to know if there is corruption */
+		ret = memcmp(cache_val, hw_val, TAVIL_HPH_TOTAL_REG);
+
+		/* If both the values are same, it means no corruption */
+		if (ret) {
+			dev_dbg(codec->dev, "%s: cache and hw reg are not same\n",
+				__func__);
+			tavil_codec_hph_reg_recover(tavil, wcd9xxx->regmap,
+						    pa_status);
+		} else {
+			dev_dbg(codec->dev, "%s: cache and hw reg are same\n",
+				__func__);
+			tavil->mbhc->is_hph_recover = false;
+		}
+		break;
+	default:
+		break;
+	};
+
+	return 0;
+}
+
+static int tavil_iir_enable_audio_mixer_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	/* IIR filter band registers are at integer multiples of 16 */
+	u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+	ucontrol->value.integer.value[0] = (snd_soc_read(codec, iir_reg) &
+					    (1 << band_idx)) != 0;
+
+	dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int tavil_iir_enable_audio_mixer_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+	bool iir_band_en_status;
+	int value = ucontrol->value.integer.value[0];
+	u16 iir_reg = WCD934X_CDC_SIDETONE_IIR0_IIR_CTL + 16 * iir_idx;
+
+	/* Mask first 5 bits, 6-8 are reserved */
+	snd_soc_update_bits(codec, iir_reg, (1 << band_idx),
+			    (value << band_idx));
+
+	iir_band_en_status = ((snd_soc_read(codec, iir_reg) &
+			      (1 << band_idx)) != 0);
+	dev_dbg(codec->dev, "%s: IIR #%d band #%d enable %d\n", __func__,
+		iir_idx, band_idx, iir_band_en_status);
+	return 0;
+}
+
+static uint32_t get_iir_band_coeff(struct snd_soc_codec *codec,
+				   int iir_idx, int band_idx,
+				   int coeff_idx)
+{
+	uint32_t value = 0;
+
+	/* Address does not automatically update if reading */
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t)) & 0x7F);
+
+	value |= snd_soc_read(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx));
+
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 1) & 0x7F);
+
+	value |= (snd_soc_read(codec,
+			       (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+				16 * iir_idx)) << 8);
+
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 2) & 0x7F);
+
+	value |= (snd_soc_read(codec,
+			       (WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+				16 * iir_idx)) << 16);
+
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		((band_idx * BAND_MAX + coeff_idx)
+		* sizeof(uint32_t) + 3) & 0x7F);
+
+	/* Mask bits top 2 bits since they are reserved */
+	value |= ((snd_soc_read(codec,
+				(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL +
+				 16 * iir_idx)) & 0x3F) << 24);
+
+	return value;
+}
+
+static int tavil_iir_band_audio_mixer_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	ucontrol->value.integer.value[0] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0);
+	ucontrol->value.integer.value[1] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1);
+	ucontrol->value.integer.value[2] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2);
+	ucontrol->value.integer.value[3] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3);
+	ucontrol->value.integer.value[4] =
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4);
+
+	dev_dbg(codec->dev, "%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[0],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[1],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[2],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[3],
+		__func__, iir_idx, band_idx,
+		(uint32_t)ucontrol->value.integer.value[4]);
+	return 0;
+}
+
+static void set_iir_band_coeff(struct snd_soc_codec *codec,
+			       int iir_idx, int band_idx,
+			       uint32_t value)
+{
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value & 0xFF));
+
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 8) & 0xFF);
+
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 16) & 0xFF);
+
+	/* Mask top 2 bits, 7-8 are reserved */
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B2_CTL + 16 * iir_idx),
+		(value >> 24) & 0x3F);
+}
+
+static int tavil_iir_band_audio_mixer_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int iir_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	/*
+	 * Mask top bit it is reserved
+	 * Updates addr automatically for each B2 write
+	 */
+	snd_soc_write(codec,
+		(WCD934X_CDC_SIDETONE_IIR0_IIR_COEF_B1_CTL + 16 * iir_idx),
+		(band_idx * BAND_MAX * sizeof(uint32_t)) & 0x7F);
+
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[0]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[1]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[2]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[3]);
+	set_iir_band_coeff(codec, iir_idx, band_idx,
+				ucontrol->value.integer.value[4]);
+
+	pr_debug("%s: IIR #%d band #%d b0 = 0x%x\n"
+		"%s: IIR #%d band #%d b1 = 0x%x\n"
+		"%s: IIR #%d band #%d b2 = 0x%x\n"
+		"%s: IIR #%d band #%d a1 = 0x%x\n"
+		"%s: IIR #%d band #%d a2 = 0x%x\n",
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 0),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 1),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 2),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 3),
+		__func__, iir_idx, band_idx,
+		get_iir_band_coeff(codec, iir_idx, band_idx, 4));
+	return 0;
+}
+
+static int tavil_compander_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	int comp = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tavil->comp_enabled[comp];
+	return 0;
+}
+
+static int tavil_compander_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int comp = ((struct soc_multi_mixer_control *)
+		    kcontrol->private_value)->shift;
+	int value = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: Compander %d enable current %d, new %d\n",
+		 __func__, comp + 1, tavil->comp_enabled[comp], value);
+	tavil->comp_enabled[comp] = value;
+
+	/* Any specific register configuration for compander */
+	switch (comp) {
+	case COMPANDER_1:
+		/* Set Gain Source Select based on compander enable/disable */
+		snd_soc_update_bits(codec, WCD934X_HPH_L_EN, 0x20,
+				(value ? 0x00:0x20));
+		break;
+	case COMPANDER_2:
+		snd_soc_update_bits(codec, WCD934X_HPH_R_EN, 0x20,
+				(value ? 0x00:0x20));
+		break;
+	case COMPANDER_3:
+	case COMPANDER_4:
+	case COMPANDER_7:
+	case COMPANDER_8:
+		break;
+	default:
+		/*
+		 * if compander is not enabled for any interpolator,
+		 * it does not cause any audio failure, so do not
+		 * return error in this case, but just print a log
+		 */
+		dev_warn(codec->dev, "%s: unknown compander: %d\n",
+			__func__, comp);
+	};
+	return 0;
+}
+
+static int tavil_hph_asrc_mode_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int index = -EINVAL;
+
+	if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode"))
+		index = ASRC0;
+	if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode"))
+		index = ASRC1;
+
+	if (tavil && (index >= 0) && (index < ASRC_MAX))
+		tavil->asrc_output_mode[index] =
+			ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int tavil_hph_asrc_mode_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int val = 0;
+	int index = -EINVAL;
+
+	if (!strcmp(kcontrol->id.name, "ASRC0 Output Mode"))
+		index = ASRC0;
+	if (!strcmp(kcontrol->id.name, "ASRC1 Output Mode"))
+		index = ASRC1;
+
+	if (tavil && (index >= 0) && (index < ASRC_MAX))
+		val = tavil->asrc_output_mode[index];
+
+	ucontrol->value.integer.value[0] = val;
+
+	return 0;
+}
+
+static int tavil_hph_idle_detect_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int val = 0;
+
+	if (tavil)
+		val = tavil->idle_det_cfg.hph_idle_detect_en;
+
+	ucontrol->value.integer.value[0] = val;
+
+	return 0;
+}
+
+static int tavil_hph_idle_detect_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	if (tavil)
+		tavil->idle_det_cfg.hph_idle_detect_en =
+			ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int tavil_dmic_pin_mode_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u16 dmic_pin;
+	u8 reg_val, pinctl_position;
+
+	pinctl_position = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	dmic_pin = pinctl_position & 0x07;
+	reg_val = snd_soc_read(codec,
+			WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1);
+
+	ucontrol->value.integer.value[0] = !!reg_val;
+
+	return 0;
+}
+
+static int tavil_dmic_pin_mode_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u16 ctl_reg, cfg_reg, dmic_pin;
+	u8 ctl_val, cfg_val, pinctl_position, pinctl_mode, mask;
+
+	/* 0- high or low; 1- high Z */
+	pinctl_mode = ucontrol->value.integer.value[0];
+	pinctl_position = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	switch (pinctl_position >> 3) {
+	case 0:
+		ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_0;
+		break;
+	case 1:
+		ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_1;
+		break;
+	case 2:
+		ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_2;
+		break;
+	case 3:
+		ctl_reg = WCD934X_TEST_DEBUG_PIN_CTL_OE_3;
+		break;
+	default:
+		dev_err(codec->dev, "%s: Invalid pinctl position = %d\n",
+			__func__, pinctl_position);
+		return -EINVAL;
+	}
+
+	ctl_val = ~(pinctl_mode << (pinctl_position & 0x07));
+	mask = 1 << (pinctl_position & 0x07);
+	snd_soc_update_bits(codec, ctl_reg, mask, ctl_val);
+
+	dmic_pin = pinctl_position & 0x07;
+	cfg_reg = WCD934X_TLMM_DMIC1_CLK_PINCFG + dmic_pin - 1;
+	if (pinctl_mode) {
+		if (tavil->intf_type == WCD9XXX_INTERFACE_TYPE_SLIMBUS)
+			cfg_val = 0x6;
+		else
+			cfg_val = 0xD;
+	} else
+		cfg_val = 0;
+	snd_soc_update_bits(codec, cfg_reg, 0x1F, cfg_val);
+
+	dev_dbg(codec->dev, "%s: reg=0x%x mask=0x%x val=%d reg=0x%x val=%d\n",
+			__func__, ctl_reg, mask, ctl_val, cfg_reg, cfg_val);
+
+	return 0;
+}
+
+static int tavil_amic_pwr_lvl_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u16 amic_reg;
+
+	if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+		amic_reg = WCD934X_ANA_AMIC1;
+	if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+		amic_reg = WCD934X_ANA_AMIC3;
+	else
+		goto ret;
+
+	ucontrol->value.integer.value[0] =
+		(snd_soc_read(codec, amic_reg) & WCD934X_AMIC_PWR_LVL_MASK) >>
+			     WCD934X_AMIC_PWR_LVL_SHIFT;
+ret:
+	return 0;
+}
+
+static int tavil_amic_pwr_lvl_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u32 mode_val;
+	u16 amic_reg;
+
+	mode_val = ucontrol->value.enumerated.item[0];
+
+	dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val);
+
+	if (!strcmp(kcontrol->id.name, "AMIC_1_2 PWR MODE"))
+		amic_reg = WCD934X_ANA_AMIC1;
+	if (!strcmp(kcontrol->id.name, "AMIC_3_4 PWR MODE"))
+		amic_reg = WCD934X_ANA_AMIC3;
+	else
+		goto ret;
+
+	snd_soc_update_bits(codec, amic_reg, WCD934X_AMIC_PWR_LVL_MASK,
+			    mode_val << WCD934X_AMIC_PWR_LVL_SHIFT);
+ret:
+	return 0;
+}
+
+static const char *const tavil_conn_mad_text[] = {
+	"NOTUSED1", "ADC1", "ADC2", "ADC3", "ADC4", "NOTUSED5",
+	"NOTUSED6", "NOTUSED2", "DMIC0", "DMIC1", "DMIC2", "DMIC3",
+	"DMIC4", "DMIC5", "NOTUSED3", "NOTUSED4"
+};
+
+static const struct soc_enum tavil_conn_mad_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(tavil_conn_mad_text),
+			    tavil_conn_mad_text);
+
+static int tavil_mad_input_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	u8 tavil_mad_input;
+
+	tavil_mad_input = snd_soc_read(codec, WCD934X_SOC_MAD_INP_SEL) & 0x0F;
+	ucontrol->value.integer.value[0] = tavil_mad_input;
+
+	dev_dbg(codec->dev, "%s: tavil_mad_input = %s\n", __func__,
+		tavil_conn_mad_text[tavil_mad_input]);
+
+	return 0;
+}
+
+static int tavil_mad_input_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct snd_soc_card *card = codec->component.card;
+	u8 tavil_mad_input;
+	char mad_amic_input_widget[6];
+	const char *mad_input_widget;
+	const char *source_widget = NULL;
+	u32 adc, i, mic_bias_found = 0;
+	int ret = 0;
+	char *mad_input;
+	bool is_adc2_input = false;
+
+	tavil_mad_input = ucontrol->value.integer.value[0];
+
+	if (strnstr(tavil_conn_mad_text[tavil_mad_input], "NOTUSED",
+				sizeof("NOTUSED"))) {
+		dev_dbg(codec->dev,
+			"%s: Unsupported tavil_mad_input = %s\n",
+			__func__, tavil_conn_mad_text[tavil_mad_input]);
+		/* Make sure the MAD register is updated */
+		snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+				    0x88, 0x00);
+		return -EINVAL;
+	}
+
+	if (strnstr(tavil_conn_mad_text[tavil_mad_input],
+		    "ADC", sizeof("ADC"))) {
+		mad_input = strpbrk(tavil_conn_mad_text[tavil_mad_input],
+				    "1234");
+		if (!mad_input) {
+			dev_err(codec->dev, "%s: Invalid MAD input %s\n",
+				__func__, tavil_conn_mad_text[tavil_mad_input]);
+			return -EINVAL;
+		}
+
+		ret = kstrtouint(mad_input, 10, &adc);
+		if ((ret < 0) || (adc > 4)) {
+			dev_err(codec->dev, "%s: Invalid ADC = %s\n", __func__,
+				tavil_conn_mad_text[tavil_mad_input]);
+			return -EINVAL;
+		}
+
+		/*AMIC4 and AMIC5 share ADC4*/
+		if ((adc == 4) &&
+		    (snd_soc_read(codec, WCD934X_TX_NEW_AMIC_4_5_SEL) & 0x10))
+			adc = 5;
+
+		snprintf(mad_amic_input_widget, 6, "%s%u", "AMIC", adc);
+
+		mad_input_widget = mad_amic_input_widget;
+		if (adc == 2)
+			is_adc2_input = true;
+	} else {
+		/* DMIC type input widget*/
+		mad_input_widget = tavil_conn_mad_text[tavil_mad_input];
+	}
+
+	dev_dbg(codec->dev,
+		"%s: tavil input widget = %s, adc_input = %s\n", __func__,
+		mad_input_widget, is_adc2_input ? "true" : "false");
+
+	for (i = 0; i < card->num_of_dapm_routes; i++) {
+		if (!strcmp(card->of_dapm_routes[i].sink, mad_input_widget)) {
+			source_widget = card->of_dapm_routes[i].source;
+			if (!source_widget) {
+				dev_err(codec->dev,
+					"%s: invalid source widget\n",
+					__func__);
+				return -EINVAL;
+			}
+
+			if (strnstr(source_widget,
+				"MIC BIAS1", sizeof("MIC BIAS1"))) {
+				mic_bias_found = 1;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS2", sizeof("MIC BIAS2"))) {
+				mic_bias_found = 2;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS3", sizeof("MIC BIAS3"))) {
+				mic_bias_found = 3;
+				break;
+			} else if (strnstr(source_widget,
+				"MIC BIAS4", sizeof("MIC BIAS4"))) {
+				mic_bias_found = 4;
+				break;
+			}
+		}
+	}
+
+	if (!mic_bias_found) {
+		dev_err(codec->dev, "%s: mic bias not found for input %s\n",
+			__func__, mad_input_widget);
+		return -EINVAL;
+	}
+
+	dev_dbg(codec->dev, "%s: mic_bias found = %d\n", __func__,
+		mic_bias_found);
+
+	snd_soc_update_bits(codec, WCD934X_SOC_MAD_INP_SEL,
+			    0x0F, tavil_mad_input);
+	snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+			    0x07, mic_bias_found);
+	/* for adc2 input, mad should be in micbias mode with BG enabled */
+	if (is_adc2_input)
+		snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+				    0x88, 0x88);
+	else
+		snd_soc_update_bits(codec, WCD934X_ANA_MAD_SETUP,
+				    0x88, 0x00);
+	return 0;
+}
+
+static int tavil_ear_pa_gain_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	ear_pa_gain = snd_soc_read(codec, WCD934X_ANA_EAR);
+
+	ear_pa_gain = (ear_pa_gain & 0x70) >> 4;
+
+	ucontrol->value.integer.value[0] = ear_pa_gain;
+
+	dev_dbg(codec->dev, "%s: ear_pa_gain = 0x%x\n", __func__,
+		ear_pa_gain);
+
+	return 0;
+}
+
+static int tavil_ear_pa_gain_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	u8 ear_pa_gain;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0]  = %ld\n",
+			__func__, ucontrol->value.integer.value[0]);
+
+	ear_pa_gain =  ucontrol->value.integer.value[0] << 4;
+
+	snd_soc_update_bits(codec, WCD934X_ANA_EAR, 0x70, ear_pa_gain);
+	return 0;
+}
+
+static int tavil_ear_spkr_pa_gain_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tavil->ear_spkr_gain;
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int tavil_ear_spkr_pa_gain_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	tavil->ear_spkr_gain =  ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: gain = %d\n", __func__, tavil->ear_spkr_gain);
+
+	return 0;
+}
+
+static int tavil_rx_hph_mode_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = tavil->hph_mode;
+	return 0;
+}
+
+static int tavil_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u32 mode_val;
+
+	mode_val = ucontrol->value.enumerated.item[0];
+
+	dev_dbg(codec->dev, "%s: mode: %d\n", __func__, mode_val);
+
+	if (mode_val == 0) {
+		dev_warn(codec->dev, "%s:Invalid HPH Mode, default to Cls-H LOHiFi\n",
+			__func__);
+		mode_val = CLS_H_LOHIFI;
+	}
+	tavil->hph_mode = mode_val;
+	return 0;
+}
+
+static const char * const rx_hph_mode_mux_text[] = {
+	"CLS_H_INVALID", "CLS_H_HIFI", "CLS_H_LP", "CLS_AB", "CLS_H_LOHIFI",
+	"CLS_H_ULP", "CLS_AB_HIFI",
+};
+
+static const struct soc_enum rx_hph_mode_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_hph_mode_mux_text),
+			    rx_hph_mode_mux_text);
+
+static const char *const tavil_anc_func_text[] = {"OFF", "ON"};
+static const struct soc_enum tavil_anc_func_enum =
+	SOC_ENUM_SINGLE_EXT(2, tavil_anc_func_text);
+
+/* Cutoff frequency for high pass filter */
+static const char * const cf_text[] = {
+	"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ"
+};
+
+static const char * const rx_cf_text[] = {
+	"CF_NEG_3DB_4HZ", "CF_NEG_3DB_75HZ", "CF_NEG_3DB_150HZ",
+	"CF_NEG_3DB_0P48HZ"
+};
+
+static const char * const amic_pwr_lvl_text[] = {
+	"LOW_PWR", "DEFAULT", "HIGH_PERF"
+};
+
+static const char * const hph_idle_detect_text[] = {
+	"OFF", "ON"
+};
+
+static const char * const asrc_mode_text[] = {
+	"INT", "FRAC"
+};
+
+static const char * const tavil_ear_pa_gain_text[] = {
+	"G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+	"G_0_DB", "G_M2P5_DB", "UNDEFINED", "G_M12_DB"
+};
+
+static const char * const tavil_ear_spkr_pa_gain_text[] = {
+	"G_DEFAULT", "G_0_DB", "G_1_DB", "G_2_DB", "G_3_DB",
+	"G_4_DB", "G_5_DB", "G_6_DB"
+};
+
+static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_pa_gain_enum, tavil_ear_pa_gain_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tavil_ear_spkr_pa_gain_enum,
+				tavil_ear_spkr_pa_gain_text);
+static SOC_ENUM_SINGLE_EXT_DECL(amic_pwr_lvl_enum, amic_pwr_lvl_text);
+static SOC_ENUM_SINGLE_EXT_DECL(hph_idle_detect_enum, hph_idle_detect_text);
+static SOC_ENUM_SINGLE_EXT_DECL(asrc_mode_enum, asrc_mode_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec0_enum, WCD934X_CDC_TX0_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec1_enum, WCD934X_CDC_TX1_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec2_enum, WCD934X_CDC_TX2_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec3_enum, WCD934X_CDC_TX3_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec4_enum, WCD934X_CDC_TX4_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec5_enum, WCD934X_CDC_TX5_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec6_enum, WCD934X_CDC_TX6_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec7_enum, WCD934X_CDC_TX7_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_dec8_enum, WCD934X_CDC_TX8_TX_PATH_CFG0, 5,
+							cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int0_1_enum, WCD934X_CDC_RX0_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int0_2_enum, WCD934X_CDC_RX0_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int1_1_enum, WCD934X_CDC_RX1_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int1_2_enum, WCD934X_CDC_RX1_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int2_1_enum, WCD934X_CDC_RX2_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int2_2_enum, WCD934X_CDC_RX2_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int3_1_enum, WCD934X_CDC_RX3_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int3_2_enum, WCD934X_CDC_RX3_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int4_1_enum, WCD934X_CDC_RX4_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int4_2_enum, WCD934X_CDC_RX4_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int7_1_enum, WCD934X_CDC_RX7_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int7_2_enum, WCD934X_CDC_RX7_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int8_1_enum, WCD934X_CDC_RX8_RX_PATH_CFG2, 0,
+							rx_cf_text);
+static SOC_ENUM_SINGLE_DECL(cf_int8_2_enum, WCD934X_CDC_RX8_RX_PATH_MIX_CFG, 2,
+							rx_cf_text);
+
+static const struct snd_kcontrol_new tavil_snd_controls[] = {
+	SOC_ENUM_EXT("EAR PA Gain", tavil_ear_pa_gain_enum,
+		tavil_ear_pa_gain_get, tavil_ear_pa_gain_put),
+	SOC_ENUM_EXT("EAR SPKR PA Gain", tavil_ear_spkr_pa_gain_enum,
+		     tavil_ear_spkr_pa_gain_get, tavil_ear_spkr_pa_gain_put),
+	SOC_SINGLE_TLV("HPHL Volume", WCD934X_HPH_L_EN, 0, 20, 1, line_gain),
+	SOC_SINGLE_TLV("HPHR Volume", WCD934X_HPH_R_EN, 0, 20, 1, line_gain),
+	SOC_SINGLE_TLV("LINEOUT1 Volume", WCD934X_DIFF_LO_LO1_COMPANDER,
+		3, 16, 1, line_gain),
+	SOC_SINGLE_TLV("LINEOUT2 Volume", WCD934X_DIFF_LO_LO2_COMPANDER,
+		3, 16, 1, line_gain),
+	SOC_SINGLE_TLV("ADC1 Volume", WCD934X_ANA_AMIC1, 0, 20, 0, analog_gain),
+	SOC_SINGLE_TLV("ADC2 Volume", WCD934X_ANA_AMIC2, 0, 20, 0, analog_gain),
+	SOC_SINGLE_TLV("ADC3 Volume", WCD934X_ANA_AMIC3, 0, 20, 0, analog_gain),
+	SOC_SINGLE_TLV("ADC4 Volume", WCD934X_ANA_AMIC4, 0, 20, 0, analog_gain),
+
+	SOC_SINGLE_SX_TLV("RX0 Digital Volume", WCD934X_CDC_RX0_RX_VOL_CTL,
+		0, -84, 40, digital_gain), /* -84dB min - 40dB max */
+	SOC_SINGLE_SX_TLV("RX1 Digital Volume", WCD934X_CDC_RX1_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX2 Digital Volume", WCD934X_CDC_RX2_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX3 Digital Volume", WCD934X_CDC_RX3_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX4 Digital Volume", WCD934X_CDC_RX4_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX7 Digital Volume", WCD934X_CDC_RX7_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX8 Digital Volume", WCD934X_CDC_RX8_RX_VOL_CTL,
+		0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX0 Mix Digital Volume",
+		WCD934X_CDC_RX0_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX1 Mix Digital Volume",
+		WCD934X_CDC_RX1_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX2 Mix Digital Volume",
+		WCD934X_CDC_RX2_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX3 Mix Digital Volume",
+		WCD934X_CDC_RX3_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX4 Mix Digital Volume",
+		WCD934X_CDC_RX4_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX7 Mix Digital Volume",
+		WCD934X_CDC_RX7_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("RX8 Mix Digital Volume",
+		WCD934X_CDC_RX8_RX_VOL_MIX_CTL, 0, -84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("DEC0 Volume", WCD934X_CDC_TX0_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC1 Volume", WCD934X_CDC_TX1_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC2 Volume", WCD934X_CDC_TX2_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC3 Volume", WCD934X_CDC_TX3_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC4 Volume", WCD934X_CDC_TX4_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC5 Volume", WCD934X_CDC_TX5_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC6 Volume", WCD934X_CDC_TX6_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC7 Volume", WCD934X_CDC_TX7_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+	SOC_SINGLE_SX_TLV("DEC8 Volume", WCD934X_CDC_TX8_TX_VOL_CTL, 0,
+		-84, 40, digital_gain),
+
+	SOC_SINGLE_SX_TLV("IIR0 INP0 Volume",
+		WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B1_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR0 INP1 Volume",
+		WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B2_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR0 INP2 Volume",
+		WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B3_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR0 INP3 Volume",
+		WCD934X_CDC_SIDETONE_IIR0_IIR_GAIN_B4_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP0 Volume",
+		WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B1_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP1 Volume",
+		WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B2_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP2 Volume",
+		WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B3_CTL, 0, -84, 40,
+		digital_gain),
+	SOC_SINGLE_SX_TLV("IIR1 INP3 Volume",
+		WCD934X_CDC_SIDETONE_IIR1_IIR_GAIN_B4_CTL, 0, -84, 40,
+		digital_gain),
+
+	SOC_SINGLE_EXT("ANC Slot", SND_SOC_NOPM, 0, 100, 0, tavil_get_anc_slot,
+		tavil_put_anc_slot),
+	SOC_ENUM_EXT("ANC Function", tavil_anc_func_enum, tavil_get_anc_func,
+		tavil_put_anc_func),
+
+	SOC_ENUM("TX0 HPF cut off", cf_dec0_enum),
+	SOC_ENUM("TX1 HPF cut off", cf_dec1_enum),
+	SOC_ENUM("TX2 HPF cut off", cf_dec2_enum),
+	SOC_ENUM("TX3 HPF cut off", cf_dec3_enum),
+	SOC_ENUM("TX4 HPF cut off", cf_dec4_enum),
+	SOC_ENUM("TX5 HPF cut off", cf_dec5_enum),
+	SOC_ENUM("TX6 HPF cut off", cf_dec6_enum),
+	SOC_ENUM("TX7 HPF cut off", cf_dec7_enum),
+	SOC_ENUM("TX8 HPF cut off", cf_dec8_enum),
+
+	SOC_ENUM("RX INT0_1 HPF cut off", cf_int0_1_enum),
+	SOC_ENUM("RX INT0_2 HPF cut off", cf_int0_2_enum),
+	SOC_ENUM("RX INT1_1 HPF cut off", cf_int1_1_enum),
+	SOC_ENUM("RX INT1_2 HPF cut off", cf_int1_2_enum),
+	SOC_ENUM("RX INT2_1 HPF cut off", cf_int2_1_enum),
+	SOC_ENUM("RX INT2_2 HPF cut off", cf_int2_2_enum),
+	SOC_ENUM("RX INT3_1 HPF cut off", cf_int3_1_enum),
+	SOC_ENUM("RX INT3_2 HPF cut off", cf_int3_2_enum),
+	SOC_ENUM("RX INT4_1 HPF cut off", cf_int4_1_enum),
+	SOC_ENUM("RX INT4_2 HPF cut off", cf_int4_2_enum),
+	SOC_ENUM("RX INT7_1 HPF cut off", cf_int7_1_enum),
+	SOC_ENUM("RX INT7_2 HPF cut off", cf_int7_2_enum),
+	SOC_ENUM("RX INT8_1 HPF cut off", cf_int8_1_enum),
+	SOC_ENUM("RX INT8_2 HPF cut off", cf_int8_2_enum),
+
+	SOC_ENUM_EXT("RX HPH Mode", rx_hph_mode_mux_enum,
+		tavil_rx_hph_mode_get, tavil_rx_hph_mode_put),
+
+	SOC_SINGLE_EXT("IIR0 Enable Band1", IIR0, BAND1, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR0 Enable Band2", IIR0, BAND2, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR0 Enable Band3", IIR0, BAND3, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR0 Enable Band4", IIR0, BAND4, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR0 Enable Band5", IIR0, BAND5, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR1 Enable Band1", IIR1, BAND1, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR1 Enable Band2", IIR1, BAND2, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR1 Enable Band3", IIR1, BAND3, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR1 Enable Band4", IIR1, BAND4, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+	SOC_SINGLE_EXT("IIR1 Enable Band5", IIR1, BAND5, 1, 0,
+		tavil_iir_enable_audio_mixer_get,
+		tavil_iir_enable_audio_mixer_put),
+
+	SOC_SINGLE_MULTI_EXT("IIR0 Band1", IIR0, BAND1, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band2", IIR0, BAND2, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band3", IIR0, BAND3, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band4", IIR0, BAND4, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR0 Band5", IIR0, BAND5, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band1", IIR1, BAND1, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band2", IIR1, BAND2, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band3", IIR1, BAND3, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band4", IIR1, BAND4, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+	SOC_SINGLE_MULTI_EXT("IIR1 Band5", IIR1, BAND5, 255, 0, 5,
+		tavil_iir_band_audio_mixer_get, tavil_iir_band_audio_mixer_put),
+
+	SOC_SINGLE_EXT("COMP1 Switch", SND_SOC_NOPM, COMPANDER_1, 1, 0,
+		tavil_compander_get, tavil_compander_put),
+	SOC_SINGLE_EXT("COMP2 Switch", SND_SOC_NOPM, COMPANDER_2, 1, 0,
+		tavil_compander_get, tavil_compander_put),
+	SOC_SINGLE_EXT("COMP3 Switch", SND_SOC_NOPM, COMPANDER_3, 1, 0,
+		tavil_compander_get, tavil_compander_put),
+	SOC_SINGLE_EXT("COMP4 Switch", SND_SOC_NOPM, COMPANDER_4, 1, 0,
+		tavil_compander_get, tavil_compander_put),
+	SOC_SINGLE_EXT("COMP7 Switch", SND_SOC_NOPM, COMPANDER_7, 1, 0,
+		tavil_compander_get, tavil_compander_put),
+	SOC_SINGLE_EXT("COMP8 Switch", SND_SOC_NOPM, COMPANDER_8, 1, 0,
+		tavil_compander_get, tavil_compander_put),
+
+	SOC_ENUM_EXT("ASRC0 Output Mode", asrc_mode_enum,
+		tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put),
+	SOC_ENUM_EXT("ASRC1 Output Mode", asrc_mode_enum,
+		tavil_hph_asrc_mode_get, tavil_hph_asrc_mode_put),
+
+	SOC_ENUM_EXT("HPH Idle Detect", hph_idle_detect_enum,
+		tavil_hph_idle_detect_get, tavil_hph_idle_detect_put),
+
+	SOC_ENUM_EXT("MAD Input", tavil_conn_mad_enum,
+		     tavil_mad_input_get, tavil_mad_input_put),
+
+	SOC_SINGLE_EXT("DMIC1_CLK_PIN_MODE", SND_SOC_NOPM, 17, 1, 0,
+		tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+	SOC_SINGLE_EXT("DMIC1_DATA_PIN_MODE", SND_SOC_NOPM, 18, 1, 0,
+		tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+	SOC_SINGLE_EXT("DMIC2_CLK_PIN_MODE", SND_SOC_NOPM, 19, 1, 0,
+		tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+	SOC_SINGLE_EXT("DMIC2_DATA_PIN_MODE", SND_SOC_NOPM, 20, 1, 0,
+		tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+	SOC_SINGLE_EXT("DMIC3_CLK_PIN_MODE", SND_SOC_NOPM, 21, 1, 0,
+		tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+
+	SOC_SINGLE_EXT("DMIC3_DATA_PIN_MODE", SND_SOC_NOPM, 22, 1, 0,
+		tavil_dmic_pin_mode_get, tavil_dmic_pin_mode_put),
+	SOC_ENUM_EXT("AMIC_1_2 PWR MODE", amic_pwr_lvl_enum,
+		tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put),
+	SOC_ENUM_EXT("AMIC_3_4 PWR MODE", amic_pwr_lvl_enum,
+		tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put),
+	SOC_ENUM_EXT("AMIC_5_6 PWR MODE", amic_pwr_lvl_enum,
+		tavil_amic_pwr_lvl_get, tavil_amic_pwr_lvl_put),
+};
+
+static int tavil_dec_enum_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val;
+	u16 mic_sel_reg = 0;
+	u8 mic_sel;
+
+	val = ucontrol->value.enumerated.item[0];
+	if (val > e->items - 1)
+		return -EINVAL;
+
+	dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+		widget->name, val);
+
+	switch (e->reg) {
+	case WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1:
+		if (e->shift_l == 0)
+			mic_sel_reg = WCD934X_CDC_TX0_TX_PATH_CFG0;
+		else if (e->shift_l == 2)
+			mic_sel_reg = WCD934X_CDC_TX4_TX_PATH_CFG0;
+		else if (e->shift_l == 4)
+			mic_sel_reg = WCD934X_CDC_TX8_TX_PATH_CFG0;
+		break;
+	case WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1:
+		if (e->shift_l == 0)
+			mic_sel_reg = WCD934X_CDC_TX1_TX_PATH_CFG0;
+		else if (e->shift_l == 2)
+			mic_sel_reg = WCD934X_CDC_TX5_TX_PATH_CFG0;
+		break;
+	case WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1:
+		if (e->shift_l == 0)
+			mic_sel_reg = WCD934X_CDC_TX2_TX_PATH_CFG0;
+		else if (e->shift_l == 2)
+			mic_sel_reg = WCD934X_CDC_TX6_TX_PATH_CFG0;
+		break;
+	case WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1:
+		if (e->shift_l == 0)
+			mic_sel_reg = WCD934X_CDC_TX3_TX_PATH_CFG0;
+		else if (e->shift_l == 2)
+			mic_sel_reg = WCD934X_CDC_TX7_TX_PATH_CFG0;
+		break;
+	default:
+		dev_err(codec->dev, "%s: e->reg: 0x%x not expected\n",
+			__func__, e->reg);
+		return -EINVAL;
+	}
+
+	/* ADC: 0, DMIC: 1 */
+	mic_sel = val ? 0x0 : 0x1;
+	if (mic_sel_reg)
+		snd_soc_update_bits(codec, mic_sel_reg, 1 << 7, mic_sel << 7);
+
+	return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static int tavil_int_dem_inp_mux_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(widget->dapm);
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	unsigned int val;
+	unsigned short look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0;
+
+	val = ucontrol->value.enumerated.item[0];
+	if (val >= e->items)
+		return -EINVAL;
+
+	dev_dbg(codec->dev, "%s: wname: %s, val: 0x%x\n", __func__,
+		widget->name, val);
+
+	if (e->reg == WCD934X_CDC_RX0_RX_PATH_SEC0)
+		look_ahead_dly_reg = WCD934X_CDC_RX0_RX_PATH_CFG0;
+	else if (e->reg == WCD934X_CDC_RX1_RX_PATH_SEC0)
+		look_ahead_dly_reg = WCD934X_CDC_RX1_RX_PATH_CFG0;
+	else if (e->reg == WCD934X_CDC_RX2_RX_PATH_SEC0)
+		look_ahead_dly_reg = WCD934X_CDC_RX2_RX_PATH_CFG0;
+
+	/* Set Look Ahead Delay */
+	snd_soc_update_bits(codec, look_ahead_dly_reg,
+			    0x08, (val ? 0x08 : 0x00));
+	/* Set DEM INP Select */
+	return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+}
+
+static const char * const rx_int0_7_mix_mux_text[] = {
+	"ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+	"RX6", "RX7", "PROXIMITY"
+};
+
+static const char * const rx_int_mix_mux_text[] = {
+	"ZERO", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5",
+	"RX6", "RX7"
+};
+
+static const char * const rx_prim_mix_text[] = {
+	"ZERO", "DEC0", "DEC1", "IIR0", "IIR1", "RX0", "RX1", "RX2",
+	"RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_sidetone_mix_text[] = {
+	"ZERO", "SRC0", "SRC1", "SRC_SUM"
+};
+
+static const char * const cdc_if_tx0_mux_text[] = {
+	"ZERO", "RX_MIX_TX0", "DEC0", "DEC0_192"
+};
+static const char * const cdc_if_tx1_mux_text[] = {
+	"ZERO", "RX_MIX_TX1", "DEC1", "DEC1_192"
+};
+static const char * const cdc_if_tx2_mux_text[] = {
+	"ZERO", "RX_MIX_TX2", "DEC2", "DEC2_192"
+};
+static const char * const cdc_if_tx3_mux_text[] = {
+	"ZERO", "RX_MIX_TX3", "DEC3", "DEC3_192"
+};
+static const char * const cdc_if_tx4_mux_text[] = {
+	"ZERO", "RX_MIX_TX4", "DEC4", "DEC4_192"
+};
+static const char * const cdc_if_tx5_mux_text[] = {
+	"ZERO", "RX_MIX_TX5", "DEC5", "DEC5_192"
+};
+static const char * const cdc_if_tx6_mux_text[] = {
+	"ZERO", "RX_MIX_TX6", "DEC6", "DEC6_192"
+};
+static const char * const cdc_if_tx7_mux_text[] = {
+	"ZERO", "RX_MIX_TX7", "DEC7", "DEC7_192"
+};
+static const char * const cdc_if_tx8_mux_text[] = {
+	"ZERO", "RX_MIX_TX8", "DEC8", "DEC8_192"
+};
+static const char * const cdc_if_tx9_mux_text[] = {
+	"ZERO", "DEC7", "DEC7_192"
+};
+static const char * const cdc_if_tx10_mux_text[] = {
+	"ZERO", "DEC6", "DEC6_192"
+};
+static const char * const cdc_if_tx11_mux_text[] = {
+	"DEC_0_5", "DEC_9_12", "MAD_AUDIO", "MAD_BRDCST"
+};
+static const char * const cdc_if_tx11_inp1_mux_text[] = {
+	"ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4",
+	"DEC5", "RX_MIX_TX5", "DEC9_10", "DEC11_12"
+};
+static const char * const cdc_if_tx13_mux_text[] = {
+	"CDC_DEC_5", "MAD_BRDCST"
+};
+static const char * const cdc_if_tx13_inp1_mux_text[] = {
+	"ZERO", "DEC5", "DEC5_192"
+};
+
+static const char * const iir_inp_mux_text[] = {
+	"ZERO", "DEC0", "DEC1", "DEC2", "DEC3", "DEC4", "DEC5", "DEC6",
+	"DEC7", "DEC8", "RX0", "RX1", "RX2", "RX3", "RX4", "RX5", "RX6", "RX7"
+};
+
+static const char * const rx_int_dem_inp_mux_text[] = {
+	"NORMAL_DSM_OUT", "CLSH_DSM_OUT",
+};
+
+static const char * const rx_int0_1_interp_mux_text[] = {
+	"ZERO", "RX INT0_1 MIX1",
+};
+
+static const char * const rx_int1_1_interp_mux_text[] = {
+	"ZERO", "RX INT1_1 MIX1",
+};
+
+static const char * const rx_int2_1_interp_mux_text[] = {
+	"ZERO", "RX INT2_1 MIX1",
+};
+
+static const char * const rx_int3_1_interp_mux_text[] = {
+	"ZERO", "RX INT3_1 MIX1",
+};
+
+static const char * const rx_int4_1_interp_mux_text[] = {
+	"ZERO", "RX INT4_1 MIX1",
+};
+
+static const char * const rx_int7_1_interp_mux_text[] = {
+	"ZERO", "RX INT7_1 MIX1",
+};
+
+static const char * const rx_int8_1_interp_mux_text[] = {
+	"ZERO", "RX INT8_1 MIX1",
+};
+
+static const char * const rx_int0_2_interp_mux_text[] = {
+	"ZERO", "RX INT0_2 MUX",
+};
+
+static const char * const rx_int1_2_interp_mux_text[] = {
+	"ZERO", "RX INT1_2 MUX",
+};
+
+static const char * const rx_int2_2_interp_mux_text[] = {
+	"ZERO", "RX INT2_2 MUX",
+};
+
+static const char * const rx_int3_2_interp_mux_text[] = {
+	"ZERO", "RX INT3_2 MUX",
+};
+
+static const char * const rx_int4_2_interp_mux_text[] = {
+	"ZERO", "RX INT4_2 MUX",
+};
+
+static const char * const rx_int7_2_interp_mux_text[] = {
+	"ZERO", "RX INT7_2 MUX",
+};
+
+static const char * const rx_int8_2_interp_mux_text[] = {
+	"ZERO", "RX INT8_2 MUX",
+};
+
+static const char * const mad_sel_txt[] = {
+	"SPE", "MSM"
+};
+
+static const char * const mad_inp_mux_txt[] = {
+	"MAD", "DEC1"
+};
+
+static const char * const adc_mux_text[] = {
+	"DMIC", "AMIC", "ANC_FB_TUNE1", "ANC_FB_TUNE2"
+};
+
+static const char * const dmic_mux_text[] = {
+	"ZERO", "DMIC0", "DMIC1", "DMIC2", "DMIC3", "DMIC4", "DMIC5"
+};
+
+static const char * const amic_mux_text[] = {
+	"ZERO", "ADC1", "ADC2", "ADC3", "ADC4"
+};
+
+static const char * const amic4_5_sel_text[] = {
+	"AMIC4", "AMIC5"
+};
+
+static const char * const anc0_fb_mux_text[] = {
+	"ZERO", "ANC_IN_HPHL", "ANC_IN_EAR", "ANC_IN_EAR_SPKR",
+	"ANC_IN_LO1"
+};
+
+static const char * const anc1_fb_mux_text[] = {
+	"ZERO", "ANC_IN_HPHR", "ANC_IN_LO2"
+};
+
+static const char * const rx_echo_mux_text[] = {
+	"ZERO", "RX_MIX0", "RX_MIX1", "RX_MIX2", "RX_MIX3", "RX_MIX4",
+	"RX_MIX5", "RX_MIX6", "RX_MIX7", "RX_MIX8"
+};
+
+static const char *const slim_rx_mux_text[] = {
+	"ZERO", "AIF1_PB", "AIF2_PB", "AIF3_PB", "AIF4_PB"
+};
+
+static const char *const cdc_if_rx0_mux_text[] = {
+	"SLIM RX0", "I2S_0 RX0"
+};
+static const char *const cdc_if_rx1_mux_text[] = {
+	"SLIM RX1", "I2S_0 RX1"
+};
+static const char *const cdc_if_rx2_mux_text[] = {
+	"SLIM RX2", "I2S_0 RX2"
+};
+static const char *const cdc_if_rx3_mux_text[] = {
+	"SLIM RX3", "I2S_0 RX3"
+};
+static const char *const cdc_if_rx4_mux_text[] = {
+	"SLIM RX4", "I2S_0 RX4"
+};
+static const char *const cdc_if_rx5_mux_text[] = {
+	"SLIM RX5", "I2S_0 RX5"
+};
+static const char *const cdc_if_rx6_mux_text[] = {
+	"SLIM RX6", "I2S_0 RX6"
+};
+static const char *const cdc_if_rx7_mux_text[] = {
+	"SLIM RX7", "I2S_0 RX7"
+};
+
+static const char * const asrc0_mux_text[] = {
+	"ZERO", "ASRC_IN_HPHL", "ASRC_IN_LO1",
+};
+
+static const char * const asrc1_mux_text[] = {
+	"ZERO", "ASRC_IN_HPHR", "ASRC_IN_LO2",
+};
+
+static const char * const asrc2_mux_text[] = {
+	"ZERO", "ASRC_IN_SPKR1",
+};
+
+static const char * const asrc3_mux_text[] = {
+	"ZERO", "ASRC_IN_SPKR2",
+};
+
+static const char * const native_mux_text[] = {
+	"OFF", "ON",
+};
+
+static const struct snd_kcontrol_new aif4_vi_mixer[] = {
+	SOC_SINGLE_EXT("SPKR_VI_1", SND_SOC_NOPM, WCD934X_TX14, 1, 0,
+			tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put),
+	SOC_SINGLE_EXT("SPKR_VI_2", SND_SOC_NOPM, WCD934X_TX15, 1, 0,
+			tavil_vi_feed_mixer_get, tavil_vi_feed_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif1_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif2_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif3_cap_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX0", SND_SOC_NOPM, WCD934X_TX0, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX1", SND_SOC_NOPM, WCD934X_TX1, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX2", SND_SOC_NOPM, WCD934X_TX2, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX3", SND_SOC_NOPM, WCD934X_TX3, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX4", SND_SOC_NOPM, WCD934X_TX4, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX5", SND_SOC_NOPM, WCD934X_TX5, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX6", SND_SOC_NOPM, WCD934X_TX6, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX7", SND_SOC_NOPM, WCD934X_TX7, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX8", SND_SOC_NOPM, WCD934X_TX8, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX9", SND_SOC_NOPM, WCD934X_TX9, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX10", SND_SOC_NOPM, WCD934X_TX10, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX11", SND_SOC_NOPM, WCD934X_TX11, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+static const struct snd_kcontrol_new aif4_mad_mixer[] = {
+	SOC_SINGLE_EXT("SLIM TX13", SND_SOC_NOPM, WCD934X_TX13, 1, 0,
+			slim_tx_mixer_get, slim_tx_mixer_put),
+};
+
+WCD_DAPM_ENUM_EXT(slim_rx0, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx1, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx2, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx3, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx4, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx5, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx6, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+WCD_DAPM_ENUM_EXT(slim_rx7, SND_SOC_NOPM, 0, slim_rx_mux_text,
+	slim_rx_mux_get, slim_rx_mux_put);
+
+WCD_DAPM_ENUM(cdc_if_rx0, SND_SOC_NOPM, 0, cdc_if_rx0_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx1, SND_SOC_NOPM, 0, cdc_if_rx1_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx2, SND_SOC_NOPM, 0, cdc_if_rx2_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx3, SND_SOC_NOPM, 0, cdc_if_rx3_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx4, SND_SOC_NOPM, 0, cdc_if_rx4_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx5, SND_SOC_NOPM, 0, cdc_if_rx5_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx6, SND_SOC_NOPM, 0, cdc_if_rx6_mux_text);
+WCD_DAPM_ENUM(cdc_if_rx7, SND_SOC_NOPM, 0, cdc_if_rx7_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 0,
+	rx_int0_7_mix_mux_text);
+WCD_DAPM_ENUM(rx_int1_2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 0,
+	rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int2_2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 0,
+	rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int3_2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 0,
+	rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int4_2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 0,
+	rx_int_mix_mux_text);
+WCD_DAPM_ENUM(rx_int7_2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 0,
+	rx_int0_7_mix_mux_text);
+WCD_DAPM_ENUM(rx_int8_2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 0,
+	rx_int_mix_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int0_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int0_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int1_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int1_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int1_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT1_CFG1, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int2_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int2_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int2_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT2_CFG1, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int3_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int3_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int3_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT3_CFG1, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int4_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int4_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int4_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT4_CFG1, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int7_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int7_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int7_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT7_CFG1, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int8_1_mix_inp0, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 0,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int8_1_mix_inp1, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG0, 4,
+	rx_prim_mix_text);
+WCD_DAPM_ENUM(rx_int8_1_mix_inp2, WCD934X_CDC_RX_INP_MUX_RX_INT8_CFG1, 4,
+	rx_prim_mix_text);
+
+WCD_DAPM_ENUM(rx_int0_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 0,
+	rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int1_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 2,
+	rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int2_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 4,
+	rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int3_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG0, 6,
+	rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int4_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 0,
+	rx_sidetone_mix_text);
+WCD_DAPM_ENUM(rx_int7_mix2_inp, WCD934X_CDC_RX_INP_MUX_SIDETONE_SRC_CFG1, 2,
+	rx_sidetone_mix_text);
+
+WCD_DAPM_ENUM(tx_adc_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 4,
+	adc_mux_text);
+WCD_DAPM_ENUM(tx_adc_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 4,
+	adc_mux_text);
+WCD_DAPM_ENUM(tx_adc_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 4,
+	adc_mux_text);
+WCD_DAPM_ENUM(tx_adc_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 6,
+	adc_mux_text);
+
+
+WCD_DAPM_ENUM(tx_dmic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 3,
+	dmic_mux_text);
+WCD_DAPM_ENUM(tx_dmic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 3,
+	dmic_mux_text);
+
+
+WCD_DAPM_ENUM(tx_amic_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX4_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX5_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX6_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX7_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX8_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux10, WCD934X_CDC_TX_INP_MUX_ADC_MUX10_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux11, WCD934X_CDC_TX_INP_MUX_ADC_MUX11_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux12, WCD934X_CDC_TX_INP_MUX_ADC_MUX12_CFG0, 0,
+	amic_mux_text);
+WCD_DAPM_ENUM(tx_amic_mux13, WCD934X_CDC_TX_INP_MUX_ADC_MUX13_CFG0, 0,
+	amic_mux_text);
+
+WCD_DAPM_ENUM(tx_amic4_5, WCD934X_TX_NEW_AMIC_4_5_SEL, 7, amic4_5_sel_text);
+
+WCD_DAPM_ENUM(cdc_if_tx0, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 0,
+	cdc_if_tx0_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 2,
+	cdc_if_tx1_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx2, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 4,
+	cdc_if_tx2_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx3, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0, 6,
+	cdc_if_tx3_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx4, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 0,
+	cdc_if_tx4_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx5, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 2,
+	cdc_if_tx5_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx6, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 4,
+	cdc_if_tx6_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx7, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1, 6,
+	cdc_if_tx7_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx8, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 0,
+	cdc_if_tx8_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx9, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 2,
+	cdc_if_tx9_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx10, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2, 4,
+	cdc_if_tx10_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx11_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 0,
+	cdc_if_tx11_inp1_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx11, WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0,
+	cdc_if_tx11_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx13_inp1, WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3, 4,
+	cdc_if_tx13_inp1_mux_text);
+WCD_DAPM_ENUM(cdc_if_tx13, WCD934X_DATA_HUB_SB_TX13_INP_CFG, 0,
+	cdc_if_tx13_mux_text);
+
+WCD_DAPM_ENUM(rx_mix_tx0, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 0,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx1, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG0, 4,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx2, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 0,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx3, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG1, 4,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx4, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 0,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx5, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG2, 4,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx6, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 0,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx7, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG3, 4,
+	rx_echo_mux_text);
+WCD_DAPM_ENUM(rx_mix_tx8, WCD934X_CDC_RX_INP_MUX_RX_MIX_CFG4, 0,
+	rx_echo_mux_text);
+
+WCD_DAPM_ENUM(iir0_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG0, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir0_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG1, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir0_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG2, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir0_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR0_MIX_CFG3, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp0, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG0, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp1, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG1, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp2, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG2, 0,
+	iir_inp_mux_text);
+WCD_DAPM_ENUM(iir1_inp3, WCD934X_CDC_SIDETONE_IIR_INP_MUX_IIR1_MIX_CFG3, 0,
+	iir_inp_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_1_interp, SND_SOC_NOPM, 0, rx_int0_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int1_1_interp, SND_SOC_NOPM, 0, rx_int1_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int2_1_interp, SND_SOC_NOPM, 0, rx_int2_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int3_1_interp, SND_SOC_NOPM, 0, rx_int3_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int4_1_interp, SND_SOC_NOPM, 0, rx_int4_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int7_1_interp, SND_SOC_NOPM, 0, rx_int7_1_interp_mux_text);
+WCD_DAPM_ENUM(rx_int8_1_interp, SND_SOC_NOPM, 0, rx_int8_1_interp_mux_text);
+
+WCD_DAPM_ENUM(rx_int0_2_interp, SND_SOC_NOPM, 0, rx_int0_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int1_2_interp, SND_SOC_NOPM, 0, rx_int1_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int2_2_interp, SND_SOC_NOPM, 0, rx_int2_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int3_2_interp, SND_SOC_NOPM, 0, rx_int3_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int4_2_interp, SND_SOC_NOPM, 0, rx_int4_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int7_2_interp, SND_SOC_NOPM, 0, rx_int7_2_interp_mux_text);
+WCD_DAPM_ENUM(rx_int8_2_interp, SND_SOC_NOPM, 0, rx_int8_2_interp_mux_text);
+
+WCD_DAPM_ENUM(mad_sel, WCD934X_CPE_SS_SVA_CFG, 0,
+	mad_sel_txt);
+
+WCD_DAPM_ENUM(mad_inp_mux, WCD934X_CPE_SS_SVA_CFG, 2,
+	mad_inp_mux_txt);
+
+WCD_DAPM_ENUM_EXT(rx_int0_dem_inp, WCD934X_CDC_RX0_RX_PATH_SEC0, 0,
+	rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double,
+	tavil_int_dem_inp_mux_put);
+WCD_DAPM_ENUM_EXT(rx_int1_dem_inp, WCD934X_CDC_RX1_RX_PATH_SEC0, 0,
+	rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double,
+	tavil_int_dem_inp_mux_put);
+WCD_DAPM_ENUM_EXT(rx_int2_dem_inp, WCD934X_CDC_RX2_RX_PATH_SEC0, 0,
+	rx_int_dem_inp_mux_text, snd_soc_dapm_get_enum_double,
+	tavil_int_dem_inp_mux_put);
+
+WCD_DAPM_ENUM_EXT(tx_adc_mux0, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 0,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux1, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 0,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux2, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 0,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux3, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 0,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux4, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 2,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux5, WCD934X_CDC_TX_INP_MUX_ADC_MUX1_CFG1, 2,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux6, WCD934X_CDC_TX_INP_MUX_ADC_MUX2_CFG1, 2,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux7, WCD934X_CDC_TX_INP_MUX_ADC_MUX3_CFG1, 2,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+WCD_DAPM_ENUM_EXT(tx_adc_mux8, WCD934X_CDC_TX_INP_MUX_ADC_MUX0_CFG1, 4,
+	adc_mux_text, snd_soc_dapm_get_enum_double, tavil_dec_enum_put);
+
+WCD_DAPM_ENUM(asrc0, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 0,
+	asrc0_mux_text);
+WCD_DAPM_ENUM(asrc1, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 2,
+	asrc1_mux_text);
+WCD_DAPM_ENUM(asrc2, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 4,
+	asrc2_mux_text);
+WCD_DAPM_ENUM(asrc3, WCD934X_CDC_RX_INP_MUX_SPLINE_ASRC_CFG0, 6,
+	asrc3_mux_text);
+
+WCD_DAPM_ENUM(int1_1_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int2_1_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int3_1_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int4_1_native, SND_SOC_NOPM, 0, native_mux_text);
+
+WCD_DAPM_ENUM(int1_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int2_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int3_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int4_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int7_2_native, SND_SOC_NOPM, 0, native_mux_text);
+WCD_DAPM_ENUM(int8_2_native, SND_SOC_NOPM, 0, native_mux_text);
+
+WCD_DAPM_ENUM(anc0_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 0, anc0_fb_mux_text);
+WCD_DAPM_ENUM(anc1_fb, WCD934X_CDC_RX_INP_MUX_ANC_CFG0, 3, anc1_fb_mux_text);
+
+static const struct snd_kcontrol_new anc_ear_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_ear_spkr_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new anc_spkr_pa_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new mad_cpe1_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new mad_cpe2_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new mad_brdcst_switch =
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux0_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux1_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux2_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux3_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux4_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux5_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux6_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux7_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new adc_us_mux8_switch =
+	SOC_DAPM_SINGLE("US_Switch", SND_SOC_NOPM, 0, 1, 0);
+
+static const struct snd_kcontrol_new rx_int1_asrc_switch[] = {
+	SOC_DAPM_SINGLE("HPHL Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int2_asrc_switch[] = {
+	SOC_DAPM_SINGLE("HPHR Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int3_asrc_switch[] = {
+	SOC_DAPM_SINGLE("LO1 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new rx_int4_asrc_switch[] = {
+	SOC_DAPM_SINGLE("LO2 Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static int tavil_dsd_mixer_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_context *dapm =
+				snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+	int val;
+
+	val = tavil_dsd_get_current_mixer_value(dsd_conf, mc->shift);
+
+	ucontrol->value.integer.value[0] = ((val < 0) ? 0 : val);
+
+	return 0;
+}
+
+static int tavil_dsd_mixer_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_dapm_context *dapm =
+		snd_soc_dapm_kcontrol_dapm(kcontrol);
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+	struct tavil_priv *tavil_p = snd_soc_codec_get_drvdata(codec);
+	unsigned int wval = ucontrol->value.integer.value[0];
+	struct tavil_dsd_config *dsd_conf = tavil_p->dsd_config;
+
+	if (!dsd_conf)
+		return 0;
+
+	mutex_lock(&tavil_p->codec_mutex);
+
+	tavil_dsd_set_out_select(dsd_conf, mc->shift);
+	tavil_dsd_set_mixer_value(dsd_conf, mc->shift, wval);
+
+	mutex_unlock(&tavil_p->codec_mutex);
+	snd_soc_dapm_mixer_update_power(dapm, kcontrol, wval, NULL);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new hphl_mixer[] = {
+	SOC_SINGLE_EXT("DSD HPHL Switch", SND_SOC_NOPM, INTERP_HPHL, 1, 0,
+			tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_kcontrol_new hphr_mixer[] = {
+	SOC_SINGLE_EXT("DSD HPHR Switch", SND_SOC_NOPM, INTERP_HPHR, 1, 0,
+			tavil_dsd_mixer_get, tavil_dsd_mixer_put),
+};
+
+static const struct snd_soc_dapm_widget tavil_dapm_widgets[] = {
+	SND_SOC_DAPM_AIF_IN_E("AIF1 PB", "AIF1 Playback", 0, SND_SOC_NOPM,
+		AIF1_PB, 0, tavil_codec_enable_slimrx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF2 PB", "AIF2 Playback", 0, SND_SOC_NOPM,
+		AIF2_PB, 0, tavil_codec_enable_slimrx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF3 PB", "AIF3 Playback", 0, SND_SOC_NOPM,
+		AIF3_PB, 0, tavil_codec_enable_slimrx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_IN_E("AIF4 PB", "AIF4 Playback", 0, SND_SOC_NOPM,
+		AIF4_PB, 0, tavil_codec_enable_slimrx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	WCD_DAPM_MUX("SLIM RX0 MUX", WCD934X_RX0, slim_rx0),
+	WCD_DAPM_MUX("SLIM RX1 MUX", WCD934X_RX1, slim_rx1),
+	WCD_DAPM_MUX("SLIM RX2 MUX", WCD934X_RX2, slim_rx2),
+	WCD_DAPM_MUX("SLIM RX3 MUX", WCD934X_RX3, slim_rx3),
+	WCD_DAPM_MUX("SLIM RX4 MUX", WCD934X_RX4, slim_rx4),
+	WCD_DAPM_MUX("SLIM RX5 MUX", WCD934X_RX5, slim_rx5),
+	WCD_DAPM_MUX("SLIM RX6 MUX", WCD934X_RX6, slim_rx6),
+	WCD_DAPM_MUX("SLIM RX7 MUX", WCD934X_RX7, slim_rx7),
+
+	SND_SOC_DAPM_MIXER("SLIM RX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM RX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	WCD_DAPM_MUX("CDC_IF RX0 MUX", WCD934X_RX0, cdc_if_rx0),
+	WCD_DAPM_MUX("CDC_IF RX1 MUX", WCD934X_RX1, cdc_if_rx1),
+	WCD_DAPM_MUX("CDC_IF RX2 MUX", WCD934X_RX2, cdc_if_rx2),
+	WCD_DAPM_MUX("CDC_IF RX3 MUX", WCD934X_RX3, cdc_if_rx3),
+	WCD_DAPM_MUX("CDC_IF RX4 MUX", WCD934X_RX4, cdc_if_rx4),
+	WCD_DAPM_MUX("CDC_IF RX5 MUX", WCD934X_RX5, cdc_if_rx5),
+	WCD_DAPM_MUX("CDC_IF RX6 MUX", WCD934X_RX6, cdc_if_rx6),
+	WCD_DAPM_MUX("CDC_IF RX7 MUX", WCD934X_RX7, cdc_if_rx7),
+
+	SND_SOC_DAPM_MUX_E("RX INT0_2 MUX", SND_SOC_NOPM, INTERP_EAR, 0,
+		&rx_int0_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT1_2 MUX", SND_SOC_NOPM, INTERP_HPHL, 0,
+		&rx_int1_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT2_2 MUX", SND_SOC_NOPM, INTERP_HPHR, 0,
+		&rx_int2_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT3_2 MUX", SND_SOC_NOPM, INTERP_LO1, 0,
+		&rx_int3_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT4_2 MUX", SND_SOC_NOPM, INTERP_LO2, 0,
+		&rx_int4_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7_2 MUX", SND_SOC_NOPM, INTERP_SPKR1, 0,
+		&rx_int7_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_2 MUX", SND_SOC_NOPM, INTERP_SPKR2, 0,
+		&rx_int8_2_mux, tavil_codec_enable_mix_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	WCD_DAPM_MUX("RX INT0_1 MIX1 INP0", 0, rx_int0_1_mix_inp0),
+	WCD_DAPM_MUX("RX INT0_1 MIX1 INP1", 0, rx_int0_1_mix_inp1),
+	WCD_DAPM_MUX("RX INT0_1 MIX1 INP2", 0, rx_int0_1_mix_inp2),
+	WCD_DAPM_MUX("RX INT1_1 MIX1 INP0", 0, rx_int1_1_mix_inp0),
+	WCD_DAPM_MUX("RX INT1_1 MIX1 INP1", 0, rx_int1_1_mix_inp1),
+	WCD_DAPM_MUX("RX INT1_1 MIX1 INP2", 0, rx_int1_1_mix_inp2),
+	WCD_DAPM_MUX("RX INT2_1 MIX1 INP0", 0, rx_int2_1_mix_inp0),
+	WCD_DAPM_MUX("RX INT2_1 MIX1 INP1", 0, rx_int2_1_mix_inp1),
+	WCD_DAPM_MUX("RX INT2_1 MIX1 INP2", 0, rx_int2_1_mix_inp2),
+	WCD_DAPM_MUX("RX INT3_1 MIX1 INP0", 0, rx_int3_1_mix_inp0),
+	WCD_DAPM_MUX("RX INT3_1 MIX1 INP1", 0, rx_int3_1_mix_inp1),
+	WCD_DAPM_MUX("RX INT3_1 MIX1 INP2", 0, rx_int3_1_mix_inp2),
+	WCD_DAPM_MUX("RX INT4_1 MIX1 INP0", 0, rx_int4_1_mix_inp0),
+	WCD_DAPM_MUX("RX INT4_1 MIX1 INP1", 0, rx_int4_1_mix_inp1),
+	WCD_DAPM_MUX("RX INT4_1 MIX1 INP2", 0, rx_int4_1_mix_inp2),
+
+	SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int7_1_mix_inp0_mux, tavil_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int7_1_mix_inp1_mux, tavil_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int7_1_mix_inp2_mux, tavil_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP0", SND_SOC_NOPM, 0, 0,
+		&rx_int8_1_mix_inp0_mux, tavil_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP1", SND_SOC_NOPM, 0, 0,
+		&rx_int8_1_mix_inp1_mux, tavil_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 MIX1 INP2", SND_SOC_NOPM, 0, 0,
+		&rx_int8_1_mix_inp2_mux, tavil_codec_enable_swr,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MIXER("RX INT0_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT0 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1 SEC MIX", SND_SOC_NOPM, 0, 0,
+		rx_int1_asrc_switch, ARRAY_SIZE(rx_int1_asrc_switch)),
+	SND_SOC_DAPM_MIXER("RX INT2_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT2 SEC MIX", SND_SOC_NOPM, 0, 0,
+		rx_int2_asrc_switch, ARRAY_SIZE(rx_int2_asrc_switch)),
+	SND_SOC_DAPM_MIXER("RX INT3_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT3 SEC MIX", SND_SOC_NOPM, 0, 0,
+		rx_int3_asrc_switch, ARRAY_SIZE(rx_int3_asrc_switch)),
+	SND_SOC_DAPM_MIXER("RX INT4_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT4 SEC MIX", SND_SOC_NOPM, 0, 0,
+		rx_int4_asrc_switch, ARRAY_SIZE(rx_int4_asrc_switch)),
+	SND_SOC_DAPM_MIXER("RX INT7_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT7 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT8_1 MIX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT8 SEC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	SND_SOC_DAPM_MIXER("RX INT0 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT1 MIX3", SND_SOC_NOPM, 0, 0, hphl_mixer,
+			   ARRAY_SIZE(hphl_mixer)),
+	SND_SOC_DAPM_MIXER("RX INT2 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT2 MIX3", SND_SOC_NOPM, 0, 0, hphr_mixer,
+			   ARRAY_SIZE(hphr_mixer)),
+	SND_SOC_DAPM_MIXER("RX INT3 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT4 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("RX INT7 MIX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER_E("RX INT7 CHAIN", SND_SOC_NOPM, 0, 0,
+		NULL, 0, tavil_codec_spk_boost_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER_E("RX INT8 CHAIN", SND_SOC_NOPM, 0, 0,
+		NULL, 0, tavil_codec_spk_boost_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("RX INT0 MIX2 INP", SND_SOC_NOPM, INTERP_EAR,
+		0, &rx_int0_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT1 MIX2 INP", SND_SOC_NOPM, INTERP_HPHL,
+		0, &rx_int1_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT2 MIX2 INP", SND_SOC_NOPM, INTERP_HPHR,
+		0, &rx_int2_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT3 MIX2 INP", SND_SOC_NOPM, INTERP_LO1,
+		0, &rx_int3_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT4 MIX2 INP", SND_SOC_NOPM, INTERP_LO2,
+		0, &rx_int4_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7 MIX2 INP", SND_SOC_NOPM, INTERP_SPKR1,
+		0, &rx_int7_mix2_inp_mux, tavil_codec_enable_rx_path_clk,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	WCD_DAPM_MUX("CDC_IF TX0 MUX", WCD934X_TX0, cdc_if_tx0),
+	WCD_DAPM_MUX("CDC_IF TX1 MUX", WCD934X_TX1, cdc_if_tx1),
+	WCD_DAPM_MUX("CDC_IF TX2 MUX", WCD934X_TX2, cdc_if_tx2),
+	WCD_DAPM_MUX("CDC_IF TX3 MUX", WCD934X_TX3, cdc_if_tx3),
+	WCD_DAPM_MUX("CDC_IF TX4 MUX", WCD934X_TX4, cdc_if_tx4),
+	WCD_DAPM_MUX("CDC_IF TX5 MUX", WCD934X_TX5, cdc_if_tx5),
+	WCD_DAPM_MUX("CDC_IF TX6 MUX", WCD934X_TX6, cdc_if_tx6),
+	WCD_DAPM_MUX("CDC_IF TX7 MUX", WCD934X_TX7, cdc_if_tx7),
+	WCD_DAPM_MUX("CDC_IF TX8 MUX", WCD934X_TX8, cdc_if_tx8),
+	WCD_DAPM_MUX("CDC_IF TX9 MUX", WCD934X_TX9, cdc_if_tx9),
+	WCD_DAPM_MUX("CDC_IF TX10 MUX", WCD934X_TX10, cdc_if_tx10),
+	WCD_DAPM_MUX("CDC_IF TX11 MUX", WCD934X_TX11, cdc_if_tx11),
+	WCD_DAPM_MUX("CDC_IF TX11 INP1 MUX", WCD934X_TX11, cdc_if_tx11_inp1),
+	WCD_DAPM_MUX("CDC_IF TX13 MUX", WCD934X_TX13, cdc_if_tx13),
+	WCD_DAPM_MUX("CDC_IF TX13 INP1 MUX", WCD934X_TX13, cdc_if_tx13_inp1),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX0", WCD934X_CDC_TX0_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux0_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX1", WCD934X_CDC_TX1_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux1_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX2", WCD934X_CDC_TX2_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux2_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX3", WCD934X_CDC_TX3_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux3_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX4", WCD934X_CDC_TX4_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux4_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX5", WCD934X_CDC_TX5_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux5_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX6", WCD934X_CDC_TX6_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux6_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX7", WCD934X_CDC_TX7_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux7_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX8", WCD934X_CDC_TX8_TX_PATH_CTL, 5, 0,
+		&tx_adc_mux8_mux, tavil_codec_enable_dec,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX10", SND_SOC_NOPM, 10, 0, &tx_adc_mux10_mux,
+		tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX11", SND_SOC_NOPM, 11, 0, &tx_adc_mux11_mux,
+		tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX12", SND_SOC_NOPM, 12, 0, &tx_adc_mux12_mux,
+		tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+	SND_SOC_DAPM_MUX_E("ADC MUX13", SND_SOC_NOPM, 13, 0, &tx_adc_mux13_mux,
+		tavil_codec_tx_adc_cfg, SND_SOC_DAPM_POST_PMU),
+
+	WCD_DAPM_MUX("DMIC MUX0", 0, tx_dmic_mux0),
+	WCD_DAPM_MUX("DMIC MUX1", 0, tx_dmic_mux1),
+	WCD_DAPM_MUX("DMIC MUX2", 0, tx_dmic_mux2),
+	WCD_DAPM_MUX("DMIC MUX3", 0, tx_dmic_mux3),
+	WCD_DAPM_MUX("DMIC MUX4", 0, tx_dmic_mux4),
+	WCD_DAPM_MUX("DMIC MUX5", 0, tx_dmic_mux5),
+	WCD_DAPM_MUX("DMIC MUX6", 0, tx_dmic_mux6),
+	WCD_DAPM_MUX("DMIC MUX7", 0, tx_dmic_mux7),
+	WCD_DAPM_MUX("DMIC MUX8", 0, tx_dmic_mux8),
+	WCD_DAPM_MUX("DMIC MUX10", 0, tx_dmic_mux10),
+	WCD_DAPM_MUX("DMIC MUX11", 0, tx_dmic_mux11),
+	WCD_DAPM_MUX("DMIC MUX12", 0, tx_dmic_mux12),
+	WCD_DAPM_MUX("DMIC MUX13", 0, tx_dmic_mux13),
+
+	WCD_DAPM_MUX("AMIC MUX0", 0, tx_amic_mux0),
+	WCD_DAPM_MUX("AMIC MUX1", 0, tx_amic_mux1),
+	WCD_DAPM_MUX("AMIC MUX2", 0, tx_amic_mux2),
+	WCD_DAPM_MUX("AMIC MUX3", 0, tx_amic_mux3),
+	WCD_DAPM_MUX("AMIC MUX4", 0, tx_amic_mux4),
+	WCD_DAPM_MUX("AMIC MUX5", 0, tx_amic_mux5),
+	WCD_DAPM_MUX("AMIC MUX6", 0, tx_amic_mux6),
+	WCD_DAPM_MUX("AMIC MUX7", 0, tx_amic_mux7),
+	WCD_DAPM_MUX("AMIC MUX8", 0, tx_amic_mux8),
+	WCD_DAPM_MUX("AMIC MUX10", 0, tx_amic_mux10),
+	WCD_DAPM_MUX("AMIC MUX11", 0, tx_amic_mux11),
+	WCD_DAPM_MUX("AMIC MUX12", 0, tx_amic_mux12),
+	WCD_DAPM_MUX("AMIC MUX13", 0, tx_amic_mux13),
+
+	SND_SOC_DAPM_ADC_E("ADC1", NULL, WCD934X_ANA_AMIC1, 7, 0,
+		tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC2", NULL, WCD934X_ANA_AMIC2, 7, 0,
+		tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC3", NULL, WCD934X_ANA_AMIC3, 7, 0,
+		tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+	SND_SOC_DAPM_ADC_E("ADC4", NULL, WCD934X_ANA_AMIC4, 7, 0,
+		tavil_codec_enable_adc, SND_SOC_DAPM_PRE_PMU),
+
+	WCD_DAPM_MUX("AMIC4_5 SEL", 0, tx_amic4_5),
+
+	WCD_DAPM_MUX("ANC0 FB MUX", 0, anc0_fb),
+	WCD_DAPM_MUX("ANC1 FB MUX", 0, anc1_fb),
+
+	SND_SOC_DAPM_INPUT("AMIC1"),
+	SND_SOC_DAPM_INPUT("AMIC2"),
+	SND_SOC_DAPM_INPUT("AMIC3"),
+	SND_SOC_DAPM_INPUT("AMIC4"),
+	SND_SOC_DAPM_INPUT("AMIC5"),
+
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS1", SND_SOC_NOPM, 0, 0,
+		tavil_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,
+		tavil_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,
+		tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E("MIC BIAS4", SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_micbias, SND_SOC_DAPM_PRE_PMU |
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	/*
+	 * Not supply widget, this is used to recover HPH registers.
+	 * It is not connected to any other widgets
+	 */
+	SND_SOC_DAPM_SUPPLY("RESET_HPH_REGISTERS", SND_SOC_NOPM,
+		0, 0, tavil_codec_reset_hph_registers,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS1_STANDALONE, SND_SOC_NOPM, 0, 0,
+		tavil_codec_force_enable_micbias,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS2_STANDALONE, SND_SOC_NOPM, 0, 0,
+		tavil_codec_force_enable_micbias,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS3_STANDALONE, SND_SOC_NOPM, 0, 0,
+		tavil_codec_force_enable_micbias,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MICBIAS_E(DAPM_MICBIAS4_STANDALONE, SND_SOC_NOPM, 0, 0,
+		tavil_codec_force_enable_micbias,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF1 CAP", "AIF1 Capture", 0, SND_SOC_NOPM,
+		AIF1_CAP, 0, tavil_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF2 CAP", "AIF2 Capture", 0, SND_SOC_NOPM,
+		AIF2_CAP, 0, tavil_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_AIF_OUT_E("AIF3 CAP", "AIF3 Capture", 0, SND_SOC_NOPM,
+		AIF3_CAP, 0, tavil_codec_enable_slimtx,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIXER("AIF1_CAP Mixer", SND_SOC_NOPM, AIF1_CAP, 0,
+		aif1_cap_mixer, ARRAY_SIZE(aif1_cap_mixer)),
+	SND_SOC_DAPM_MIXER("AIF2_CAP Mixer", SND_SOC_NOPM, AIF2_CAP, 0,
+		aif2_cap_mixer, ARRAY_SIZE(aif2_cap_mixer)),
+	SND_SOC_DAPM_MIXER("AIF3_CAP Mixer", SND_SOC_NOPM, AIF3_CAP, 0,
+		aif3_cap_mixer, ARRAY_SIZE(aif3_cap_mixer)),
+	SND_SOC_DAPM_MIXER("AIF4_MAD Mixer", SND_SOC_NOPM, AIF4_MAD_TX, 0,
+		aif4_mad_mixer, ARRAY_SIZE(aif4_mad_mixer)),
+
+	SND_SOC_DAPM_AIF_OUT_E("AIF4 VI", "VIfeed", 0, SND_SOC_NOPM,
+		AIF4_VIFEED, 0, tavil_codec_enable_slimvi_feedback,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_AIF_OUT("AIF4 MAD", "AIF4 MAD TX", 0,
+		SND_SOC_NOPM, 0, 0),
+
+	SND_SOC_DAPM_MIXER("AIF4_VI Mixer", SND_SOC_NOPM, AIF4_VIFEED, 0,
+		aif4_vi_mixer, ARRAY_SIZE(aif4_vi_mixer)),
+	SND_SOC_DAPM_INPUT("VIINPUT"),
+
+	SND_SOC_DAPM_MIXER("SLIM TX0", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX1", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX2", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX3", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX4", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX5", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX6", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX7", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX8", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX9", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX10", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX11", SND_SOC_NOPM, 0, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SLIM TX13", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+	/* Digital Mic Inputs */
+	SND_SOC_DAPM_ADC_E("DMIC0", NULL, SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_dmic,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC1", NULL, SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_dmic,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC2", NULL, SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_dmic,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC3", NULL, SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_dmic,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC4", NULL, SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_dmic,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_ADC_E("DMIC5", NULL, SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_dmic,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	WCD_DAPM_MUX("IIR0 INP0 MUX", 0, iir0_inp0),
+	WCD_DAPM_MUX("IIR0 INP1 MUX", 0, iir0_inp1),
+	WCD_DAPM_MUX("IIR0 INP2 MUX", 0, iir0_inp2),
+	WCD_DAPM_MUX("IIR0 INP3 MUX", 0, iir0_inp3),
+	WCD_DAPM_MUX("IIR1 INP0 MUX", 0, iir1_inp0),
+	WCD_DAPM_MUX("IIR1 INP1 MUX", 0, iir1_inp1),
+	WCD_DAPM_MUX("IIR1 INP2 MUX", 0, iir1_inp2),
+	WCD_DAPM_MUX("IIR1 INP3 MUX", 0, iir1_inp3),
+
+	SND_SOC_DAPM_MIXER_E("IIR0", WCD934X_CDC_SIDETONE_IIR0_IIR_PATH_CTL,
+		4, 0, NULL, 0, tavil_codec_set_iir_gain,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER_E("IIR1", WCD934X_CDC_SIDETONE_IIR1_IIR_PATH_CTL,
+		4, 0, NULL, 0, tavil_codec_set_iir_gain,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_MIXER("SRC0", WCD934X_CDC_SIDETONE_SRC0_ST_SRC_PATH_CTL,
+		4, 0, NULL, 0),
+	SND_SOC_DAPM_MIXER("SRC1", WCD934X_CDC_SIDETONE_SRC1_ST_SRC_PATH_CTL,
+		4, 0, NULL, 0),
+
+	WCD_DAPM_MUX("RX MIX TX0 MUX", 0, rx_mix_tx0),
+	WCD_DAPM_MUX("RX MIX TX1 MUX", 0, rx_mix_tx1),
+	WCD_DAPM_MUX("RX MIX TX2 MUX", 0, rx_mix_tx2),
+	WCD_DAPM_MUX("RX MIX TX3 MUX", 0, rx_mix_tx3),
+	WCD_DAPM_MUX("RX MIX TX4 MUX", 0, rx_mix_tx4),
+	WCD_DAPM_MUX("RX MIX TX5 MUX", 0, rx_mix_tx5),
+	WCD_DAPM_MUX("RX MIX TX6 MUX", 0, rx_mix_tx6),
+	WCD_DAPM_MUX("RX MIX TX7 MUX", 0, rx_mix_tx7),
+	WCD_DAPM_MUX("RX MIX TX8 MUX", 0, rx_mix_tx8),
+	WCD_DAPM_MUX("RX INT0 DEM MUX", 0, rx_int0_dem_inp),
+	WCD_DAPM_MUX("RX INT1 DEM MUX", 0, rx_int1_dem_inp),
+	WCD_DAPM_MUX("RX INT2 DEM MUX", 0, rx_int2_dem_inp),
+
+	SND_SOC_DAPM_MUX_E("RX INT0_1 INTERP", SND_SOC_NOPM, INTERP_EAR, 0,
+		&rx_int0_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT1_1 INTERP", SND_SOC_NOPM, INTERP_HPHL, 0,
+		&rx_int1_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT2_1 INTERP", SND_SOC_NOPM, INTERP_HPHR, 0,
+		&rx_int2_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT3_1 INTERP", SND_SOC_NOPM, INTERP_LO1, 0,
+		&rx_int3_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT4_1 INTERP", SND_SOC_NOPM, INTERP_LO2, 0,
+		&rx_int4_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT7_1 INTERP", SND_SOC_NOPM, INTERP_SPKR1, 0,
+		&rx_int7_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("RX INT8_1 INTERP", SND_SOC_NOPM, INTERP_SPKR2, 0,
+		&rx_int8_1_interp_mux, tavil_codec_enable_main_path,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+
+	WCD_DAPM_MUX("RX INT0_2 INTERP", 0, rx_int0_2_interp),
+	WCD_DAPM_MUX("RX INT1_2 INTERP", 0, rx_int1_2_interp),
+	WCD_DAPM_MUX("RX INT2_2 INTERP", 0, rx_int2_2_interp),
+	WCD_DAPM_MUX("RX INT3_2 INTERP", 0, rx_int3_2_interp),
+	WCD_DAPM_MUX("RX INT4_2 INTERP", 0, rx_int4_2_interp),
+	WCD_DAPM_MUX("RX INT7_2 INTERP", 0, rx_int7_2_interp),
+	WCD_DAPM_MUX("RX INT8_2 INTERP", 0, rx_int8_2_interp),
+
+	SND_SOC_DAPM_SWITCH("ADC US MUX0", WCD934X_CDC_TX0_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux0_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX1", WCD934X_CDC_TX1_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux1_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX2", WCD934X_CDC_TX2_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux2_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX3", WCD934X_CDC_TX3_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux3_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX4", WCD934X_CDC_TX4_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux4_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX5", WCD934X_CDC_TX5_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux5_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX6", WCD934X_CDC_TX6_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux6_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX7", WCD934X_CDC_TX7_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux7_switch),
+	SND_SOC_DAPM_SWITCH("ADC US MUX8", WCD934X_CDC_TX8_TX_PATH_192_CTL, 0,
+		0, &adc_us_mux8_switch),
+
+	/* MAD related widgets */
+	SND_SOC_DAPM_INPUT("MAD_CPE_INPUT"),
+	SND_SOC_DAPM_INPUT("MADINPUT"),
+
+	WCD_DAPM_MUX("MAD_SEL MUX", 0, mad_sel),
+	WCD_DAPM_MUX("MAD_INP MUX", 0, mad_inp_mux),
+
+	SND_SOC_DAPM_SWITCH_E("MAD_BROADCAST", SND_SOC_NOPM, 0, 0,
+			      &mad_brdcst_switch, tavil_codec_ape_enable_mad,
+			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_SWITCH_E("MAD_CPE1", SND_SOC_NOPM, 0, 0,
+			      &mad_cpe1_switch, tavil_codec_cpe_mad_ctl,
+			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SWITCH_E("MAD_CPE2", SND_SOC_NOPM, 0, 0,
+			      &mad_cpe2_switch, tavil_codec_cpe_mad_ctl,
+			      SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT1"),
+	SND_SOC_DAPM_OUTPUT("MAD_CPE_OUT2"),
+
+	SND_SOC_DAPM_DAC_E("RX INT0 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tavil_codec_ear_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("RX INT1 DAC", NULL, WCD934X_ANA_HPH,
+		5, 0, tavil_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("RX INT2 DAC", NULL, WCD934X_ANA_HPH,
+		4, 0, tavil_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("RX INT3 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tavil_codec_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_DAC_E("RX INT4 DAC", NULL, SND_SOC_NOPM,
+		0, 0, tavil_codec_lineout_dac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PGA_E("EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0,
+		tavil_codec_enable_ear_pa,
+		SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("HPHL PA", WCD934X_ANA_HPH, 7, 0, NULL, 0,
+		tavil_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 PA", WCD934X_ANA_HPH, 6, 0, NULL, 0,
+		tavil_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_PGA_E("LINEOUT1 PA", WCD934X_ANA_LO_1_2, 7, 0, NULL, 0,
+		tavil_codec_enable_lineout_pa,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("LINEOUT2 PA", WCD934X_ANA_LO_1_2, 6, 0, NULL, 0,
+		tavil_codec_enable_lineout_pa,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("ANC EAR PA", WCD934X_ANA_EAR, 7, 0, NULL, 0,
+		tavil_codec_enable_ear_pa, SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_PGA_E("ANC SPK1 PA", SND_SOC_NOPM, 0, 0, NULL, 0,
+		tavil_codec_enable_spkr_anc,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_OUTPUT("EAR"),
+	SND_SOC_DAPM_OUTPUT("HPHL"),
+	SND_SOC_DAPM_OUTPUT("HPHR"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT1"),
+	SND_SOC_DAPM_OUTPUT("LINEOUT2"),
+	SND_SOC_DAPM_OUTPUT("SPK1 OUT"),
+	SND_SOC_DAPM_OUTPUT("SPK2 OUT"),
+	SND_SOC_DAPM_OUTPUT("ANC EAR"),
+
+	SND_SOC_DAPM_SWITCH("ANC OUT EAR Enable", SND_SOC_NOPM, 0, 0,
+		&anc_ear_switch),
+	SND_SOC_DAPM_SWITCH("ANC OUT EAR SPKR Enable", SND_SOC_NOPM, 0, 0,
+		&anc_ear_spkr_switch),
+	SND_SOC_DAPM_SWITCH("ANC SPKR PA Enable", SND_SOC_NOPM, 0, 0,
+		&anc_spkr_pa_switch),
+
+	SND_SOC_DAPM_SUPPLY("RX_BIAS", SND_SOC_NOPM, 0, 0,
+		tavil_codec_enable_rx_bias,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("RX INT1 NATIVE SUPPLY", SND_SOC_NOPM,
+		INTERP_HPHL, 0, tavil_enable_native_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("RX INT2 NATIVE SUPPLY", SND_SOC_NOPM,
+		INTERP_HPHR, 0, tavil_enable_native_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("RX INT3 NATIVE SUPPLY", SND_SOC_NOPM,
+		INTERP_LO1, 0, tavil_enable_native_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("RX INT4 NATIVE SUPPLY", SND_SOC_NOPM,
+		INTERP_LO2, 0, tavil_enable_native_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("RX INT7 NATIVE SUPPLY", SND_SOC_NOPM,
+		INTERP_SPKR1, 0, tavil_enable_native_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+	SND_SOC_DAPM_SUPPLY("RX INT8 NATIVE SUPPLY", SND_SOC_NOPM,
+		INTERP_SPKR2, 0, tavil_enable_native_supply,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD),
+
+	WCD_DAPM_MUX("RX INT1_1 NATIVE MUX", 0, int1_1_native),
+	WCD_DAPM_MUX("RX INT2_1 NATIVE MUX", 0, int2_1_native),
+	WCD_DAPM_MUX("RX INT3_1 NATIVE MUX", 0, int3_1_native),
+	WCD_DAPM_MUX("RX INT4_1 NATIVE MUX", 0, int4_1_native),
+
+	WCD_DAPM_MUX("RX INT1_2 NATIVE MUX", 0, int1_2_native),
+	WCD_DAPM_MUX("RX INT2_2 NATIVE MUX", 0, int2_2_native),
+	WCD_DAPM_MUX("RX INT3_2 NATIVE MUX", 0, int3_2_native),
+	WCD_DAPM_MUX("RX INT4_2 NATIVE MUX", 0, int4_2_native),
+	WCD_DAPM_MUX("RX INT7_2 NATIVE MUX", 0, int7_2_native),
+	WCD_DAPM_MUX("RX INT8_2 NATIVE MUX", 0, int8_2_native),
+
+	SND_SOC_DAPM_MUX_E("ASRC0 MUX", SND_SOC_NOPM, ASRC0, 0,
+		&asrc0_mux, tavil_codec_enable_asrc_resampler,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("ASRC1 MUX", SND_SOC_NOPM, ASRC1, 0,
+		&asrc1_mux, tavil_codec_enable_asrc_resampler,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("ASRC2 MUX", SND_SOC_NOPM, ASRC2, 0,
+		&asrc2_mux, tavil_codec_enable_asrc_resampler,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MUX_E("ASRC3 MUX", SND_SOC_NOPM, ASRC3, 0,
+		&asrc3_mux, tavil_codec_enable_asrc_resampler,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static int tavil_get_channel_map(struct snd_soc_dai *dai,
+				 unsigned int *tx_num, unsigned int *tx_slot,
+				 unsigned int *rx_num, unsigned int *rx_slot)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
+	u32 i = 0;
+	struct wcd9xxx_ch *ch;
+	int ret = 0;
+
+	switch (dai->id) {
+	case AIF1_PB:
+	case AIF2_PB:
+	case AIF3_PB:
+	case AIF4_PB:
+		if (!rx_slot || !rx_num) {
+			dev_err(tavil->dev, "%s: Invalid rx_slot 0x%pK or rx_num 0x%pK\n",
+				 __func__, rx_slot, rx_num);
+			ret = -EINVAL;
+			break;
+		}
+		list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n",
+				 __func__, i, ch->ch_num);
+			rx_slot[i++] = ch->ch_num;
+		}
+		*rx_num = i;
+		dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x  rx_num = %d\n",
+			__func__, dai->name, dai->id, i);
+		if (*rx_num == 0) {
+			dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n",
+				__func__, dai->name, dai->id);
+			ret = -EINVAL;
+		}
+		break;
+	case AIF1_CAP:
+	case AIF2_CAP:
+	case AIF3_CAP:
+	case AIF4_MAD_TX:
+	case AIF4_VIFEED:
+		if (!tx_slot || !tx_num) {
+			dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK or tx_num 0x%pK\n",
+				 __func__, tx_slot, tx_num);
+			ret = -EINVAL;
+			break;
+		}
+		list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list,
+				    list) {
+			dev_dbg(tavil->dev, "%s: slot_num %u ch->ch_num %d\n",
+				 __func__, i,  ch->ch_num);
+			tx_slot[i++] = ch->ch_num;
+		}
+		*tx_num = i;
+		dev_dbg(tavil->dev, "%s: dai_name = %s dai_id = %x  tx_num = %d\n",
+			 __func__, dai->name, dai->id, i);
+		if (*tx_num == 0) {
+			dev_err(tavil->dev, "%s: Channel list empty for dai_name = %s dai_id = %x\n",
+				 __func__, dai->name, dai->id);
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		dev_err(tavil->dev, "%s: Invalid DAI ID %x\n",
+			__func__, dai->id);
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int tavil_set_channel_map(struct snd_soc_dai *dai,
+				 unsigned int tx_num, unsigned int *tx_slot,
+				 unsigned int rx_num, unsigned int *rx_slot)
+{
+	struct tavil_priv *tavil;
+	struct wcd9xxx *core;
+	struct wcd9xxx_codec_dai_data *dai_data = NULL;
+
+	tavil = snd_soc_codec_get_drvdata(dai->codec);
+	core = dev_get_drvdata(dai->codec->dev->parent);
+
+	if (!tx_slot || !rx_slot) {
+		dev_err(tavil->dev, "%s: Invalid tx_slot 0x%pK, rx_slot 0x%pK\n",
+			__func__, tx_slot, rx_slot);
+		return -EINVAL;
+	}
+	dev_dbg(tavil->dev, "%s(): dai_name = %s DAI-ID %x tx_ch %d rx_ch %d\n",
+		 __func__, dai->name, dai->id, tx_num, rx_num);
+
+	wcd9xxx_init_slimslave(core, core->slim->laddr,
+				tx_num, tx_slot, rx_num, rx_slot);
+	/* Reserve TX13 for MAD data channel */
+	dai_data = &tavil->dai[AIF4_MAD_TX];
+	if (dai_data)
+		list_add_tail(&core->tx_chs[WCD934X_TX13].list,
+			      &dai_data->wcd9xxx_ch_list);
+
+	return 0;
+}
+
+static int tavil_startup(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	return 0;
+}
+
+static void tavil_shutdown(struct snd_pcm_substream *substream,
+			   struct snd_soc_dai *dai)
+{
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+}
+
+static int tavil_set_decimator_rate(struct snd_soc_dai *dai,
+				    u32 sample_rate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	u32 tx_port = 0, tx_fs_rate = 0;
+	u8 shift = 0, shift_val = 0, tx_mux_sel = 0;
+	int decimator = -1;
+	u16 tx_port_reg = 0, tx_fs_reg = 0;
+
+	switch (sample_rate) {
+	case 8000:
+		tx_fs_rate = 0;
+		break;
+	case 16000:
+		tx_fs_rate = 1;
+		break;
+	case 32000:
+		tx_fs_rate = 3;
+		break;
+	case 48000:
+		tx_fs_rate = 4;
+		break;
+	case 96000:
+		tx_fs_rate = 5;
+		break;
+	case 192000:
+		tx_fs_rate = 6;
+		break;
+	default:
+		dev_err(tavil->dev, "%s: Invalid TX sample rate: %d\n",
+			__func__, sample_rate);
+		return -EINVAL;
+
+	};
+
+	list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
+		tx_port = ch->port;
+		dev_dbg(codec->dev, "%s: dai->id = %d, tx_port = %d",
+			__func__, dai->id, tx_port);
+
+		if ((tx_port < 0) || (tx_port == 12) || (tx_port >= 14)) {
+			dev_err(codec->dev, "%s: Invalid SLIM TX%u port. DAI ID: %d\n",
+				__func__, tx_port, dai->id);
+			return -EINVAL;
+		}
+		/* Find the SB TX MUX input - which decimator is connected */
+		if (tx_port < 4) {
+			tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG0;
+			shift = (tx_port << 1);
+			shift_val = 0x03;
+		} else if ((tx_port >= 4) && (tx_port < 8)) {
+			tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG1;
+			shift = ((tx_port - 4) << 1);
+			shift_val = 0x03;
+		} else if ((tx_port >= 8) && (tx_port < 11)) {
+			tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG2;
+			shift = ((tx_port - 8) << 1);
+			shift_val = 0x03;
+		} else if (tx_port == 11) {
+			tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3;
+			shift = 0;
+			shift_val = 0x0F;
+		} else if (tx_port == 13) {
+			tx_port_reg = WCD934X_CDC_IF_ROUTER_TX_MUX_CFG3;
+			shift = 4;
+			shift_val = 0x03;
+		}
+		tx_mux_sel = snd_soc_read(codec, tx_port_reg) &
+					  (shift_val << shift);
+		tx_mux_sel = tx_mux_sel >> shift;
+
+		if (tx_port <= 8) {
+			if ((tx_mux_sel == 0x2) || (tx_mux_sel == 0x3))
+				decimator = tx_port;
+		} else if (tx_port <= 10) {
+			if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+				decimator = ((tx_port == 9) ? 7 : 6);
+		} else if (tx_port == 11) {
+			if ((tx_mux_sel >= 1) && (tx_mux_sel < 7))
+				decimator = tx_mux_sel - 1;
+		} else if (tx_port == 13) {
+			if ((tx_mux_sel == 0x1) || (tx_mux_sel == 0x2))
+				decimator = 5;
+		}
+
+		if (decimator >= 0) {
+			tx_fs_reg = WCD934X_CDC_TX0_TX_PATH_CTL +
+				    16 * decimator;
+			dev_dbg(codec->dev, "%s: set DEC%u (-> SLIM_TX%u) rate to %u\n",
+				__func__, decimator, tx_port, sample_rate);
+			snd_soc_update_bits(codec, tx_fs_reg, 0x0F, tx_fs_rate);
+		} else if ((tx_port <= 8) && (tx_mux_sel == 0x01)) {
+			/* Check if the TX Mux input is RX MIX TXn */
+			dev_dbg(codec->dev, "%s: RX_MIX_TX%u going to CDC_IF TX%u\n",
+					__func__, tx_port, tx_port);
+		} else {
+			dev_err(codec->dev, "%s: ERROR: Invalid decimator: %d\n",
+				__func__, decimator);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int tavil_set_mix_interpolator_rate(struct snd_soc_dai *dai,
+					   u8 rate_reg_val,
+					   u32 sample_rate)
+{
+	u8 int_2_inp;
+	u32 j;
+	u16 int_mux_cfg1, int_fs_reg;
+	u8 int_mux_cfg1_val;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
+		int_2_inp = INTn_2_INP_SEL_RX0 + ch->port -
+						WCD934X_RX_PORT_START_NUMBER;
+		if ((int_2_inp < INTn_2_INP_SEL_RX0) ||
+		    (int_2_inp > INTn_2_INP_SEL_RX7)) {
+			dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n",
+				__func__,
+				(ch->port - WCD934X_RX_PORT_START_NUMBER),
+				dai->id);
+			return -EINVAL;
+		}
+
+		int_mux_cfg1 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG1;
+		for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) {
+			/* Interpolators 5 and 6 are not aviliable in Tavil */
+			if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) {
+				int_mux_cfg1 += 2;
+				continue;
+			}
+			int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1) &
+									0x0F;
+			if (int_mux_cfg1_val == int_2_inp) {
+				/*
+				 * Ear mix path supports only 48, 96, 192,
+				 * 384KHz only
+				 */
+				if ((j == INTERP_EAR) &&
+				    (rate_reg_val < 0x4 ||
+				     rate_reg_val > 0x7)) {
+					dev_err_ratelimited(codec->dev,
+					"%s: Invalid rate for AIF_PB DAI(%d)\n",
+					  __func__, dai->id);
+					return -EINVAL;
+				}
+
+				int_fs_reg = WCD934X_CDC_RX0_RX_PATH_MIX_CTL +
+									20 * j;
+				dev_dbg(codec->dev, "%s: AIF_PB DAI(%d) connected to INT%u_2\n",
+					  __func__, dai->id, j);
+				dev_dbg(codec->dev, "%s: set INT%u_2 sample rate to %u\n",
+					__func__, j, sample_rate);
+				snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+						    rate_reg_val);
+			}
+			int_mux_cfg1 += 2;
+		}
+	}
+	return 0;
+}
+
+static int tavil_set_prim_interpolator_rate(struct snd_soc_dai *dai,
+					    u8 rate_reg_val,
+					    u32 sample_rate)
+{
+	u8 int_1_mix1_inp;
+	u32 j;
+	u16 int_mux_cfg0, int_mux_cfg1;
+	u16 int_fs_reg;
+	u8 int_mux_cfg0_val, int_mux_cfg1_val;
+	u8 inp0_sel, inp1_sel, inp2_sel;
+	struct snd_soc_codec *codec = dai->codec;
+	struct wcd9xxx_ch *ch;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	struct tavil_dsd_config *dsd_conf = tavil->dsd_config;
+
+	list_for_each_entry(ch, &tavil->dai[dai->id].wcd9xxx_ch_list, list) {
+		int_1_mix1_inp = INTn_1_INP_SEL_RX0 + ch->port -
+						WCD934X_RX_PORT_START_NUMBER;
+		if ((int_1_mix1_inp < INTn_1_INP_SEL_RX0) ||
+		    (int_1_mix1_inp > INTn_1_INP_SEL_RX7)) {
+			dev_err(codec->dev, "%s: Invalid RX%u port, Dai ID is %d\n",
+				__func__,
+				(ch->port - WCD934X_RX_PORT_START_NUMBER),
+				dai->id);
+			return -EINVAL;
+		}
+
+		int_mux_cfg0 = WCD934X_CDC_RX_INP_MUX_RX_INT0_CFG0;
+
+		/*
+		 * Loop through all interpolator MUX inputs and find out
+		 * to which interpolator input, the slim rx port
+		 * is connected
+		 */
+		for (j = 0; j < WCD934X_NUM_INTERPOLATORS; j++) {
+			/* Interpolators 5 and 6 are not aviliable in Tavil */
+			if (j == INTERP_LO3_NA || j == INTERP_LO4_NA) {
+				int_mux_cfg0 += 2;
+				continue;
+			}
+			int_mux_cfg1 = int_mux_cfg0 + 1;
+
+			int_mux_cfg0_val = snd_soc_read(codec, int_mux_cfg0);
+			int_mux_cfg1_val = snd_soc_read(codec, int_mux_cfg1);
+			inp0_sel = int_mux_cfg0_val & 0x0F;
+			inp1_sel = (int_mux_cfg0_val >> 4) & 0x0F;
+			inp2_sel = (int_mux_cfg1_val >> 4) & 0x0F;
+			if ((inp0_sel == int_1_mix1_inp) ||
+			    (inp1_sel == int_1_mix1_inp) ||
+			    (inp2_sel == int_1_mix1_inp)) {
+				/*
+				 * Ear and speaker primary path does not support
+				 * native sample rates
+				 */
+				if ((j == INTERP_EAR || j == INTERP_SPKR1 ||
+					j == INTERP_SPKR2) &&
+					(rate_reg_val > 0x7)) {
+					dev_err_ratelimited(codec->dev,
+					"%s: Invalid rate for AIF_PB DAI(%d)\n",
+					  __func__, dai->id);
+					return -EINVAL;
+				}
+
+				int_fs_reg = WCD934X_CDC_RX0_RX_PATH_CTL +
+									20 * j;
+				dev_dbg(codec->dev,
+				"%s: AIF_PB DAI(%d) connected to INT%u_1\n",
+				  __func__, dai->id, j);
+				dev_dbg(codec->dev,
+					"%s: set INT%u_1 sample rate to %u\n",
+					__func__, j, sample_rate);
+				snd_soc_update_bits(codec, int_fs_reg, 0x0F,
+						    rate_reg_val);
+			}
+			int_mux_cfg0 += 2;
+		}
+		if (dsd_conf)
+			tavil_dsd_set_interp_rate(dsd_conf, ch->port,
+						  sample_rate, rate_reg_val);
+	}
+
+	return 0;
+}
+
+
+static int tavil_set_interpolator_rate(struct snd_soc_dai *dai,
+				       u32 sample_rate)
+{
+	struct snd_soc_codec *codec = dai->codec;
+	int rate_val = 0;
+	int i, ret;
+
+	for (i = 0; i < ARRAY_SIZE(sr_val_tbl); i++) {
+		if (sample_rate == sr_val_tbl[i].sample_rate) {
+			rate_val = sr_val_tbl[i].rate_val;
+			break;
+		}
+	}
+	if ((i == ARRAY_SIZE(sr_val_tbl)) || (rate_val < 0)) {
+		dev_err(codec->dev, "%s: Unsupported sample rate: %d\n",
+			__func__, sample_rate);
+		return -EINVAL;
+	}
+
+	ret = tavil_set_prim_interpolator_rate(dai, (u8)rate_val, sample_rate);
+	if (ret)
+		return ret;
+	ret = tavil_set_mix_interpolator_rate(dai, (u8)rate_val, sample_rate);
+	if (ret)
+		return ret;
+
+	return ret;
+}
+
+static int tavil_prepare(struct snd_pcm_substream *substream,
+			 struct snd_soc_dai *dai)
+{
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+	return 0;
+}
+
+static int tavil_vi_hw_params(struct snd_pcm_substream *substream,
+			      struct snd_pcm_hw_params *params,
+			      struct snd_soc_dai *dai)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
+
+	dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n",
+		 __func__, dai->name, dai->id, params_rate(params),
+		 params_channels(params));
+
+	tavil->dai[dai->id].rate = params_rate(params);
+	tavil->dai[dai->id].bit_width = 32;
+
+	return 0;
+}
+
+static int tavil_hw_params(struct snd_pcm_substream *substream,
+			   struct snd_pcm_hw_params *params,
+			   struct snd_soc_dai *dai)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(dai->codec);
+	int ret = 0;
+
+	dev_dbg(tavil->dev, "%s: dai_name = %s DAI-ID %x rate %d num_ch %d\n",
+		 __func__, dai->name, dai->id, params_rate(params),
+		 params_channels(params));
+
+	switch (substream->stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		ret = tavil_set_interpolator_rate(dai, params_rate(params));
+		if (ret) {
+			dev_err(tavil->dev, "%s: cannot set sample rate: %u\n",
+				__func__, params_rate(params));
+			return ret;
+		}
+		switch (params_width(params)) {
+		case 16:
+			tavil->dai[dai->id].bit_width = 16;
+			break;
+		case 24:
+			tavil->dai[dai->id].bit_width = 24;
+			break;
+		case 32:
+			tavil->dai[dai->id].bit_width = 32;
+			break;
+		default:
+			return -EINVAL;
+		}
+		tavil->dai[dai->id].rate = params_rate(params);
+		break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		if (dai->id != AIF4_MAD_TX)
+			ret = tavil_set_decimator_rate(dai,
+						       params_rate(params));
+		if (ret) {
+			dev_err(tavil->dev, "%s: cannot set TX Decimator rate: %d\n",
+				__func__, ret);
+			return ret;
+		}
+		switch (params_width(params)) {
+		case 16:
+			tavil->dai[dai->id].bit_width = 16;
+			break;
+		case 24:
+			tavil->dai[dai->id].bit_width = 24;
+			break;
+		default:
+			dev_err(tavil->dev, "%s: Invalid format 0x%x\n",
+				__func__, params_width(params));
+			return -EINVAL;
+		};
+		tavil->dai[dai->id].rate = params_rate(params);
+		break;
+	default:
+		dev_err(tavil->dev, "%s: Invalid stream type %d\n", __func__,
+			substream->stream);
+		return -EINVAL;
+	};
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops tavil_dai_ops = {
+	.startup = tavil_startup,
+	.shutdown = tavil_shutdown,
+	.hw_params = tavil_hw_params,
+	.prepare = tavil_prepare,
+	.set_channel_map = tavil_set_channel_map,
+	.get_channel_map = tavil_get_channel_map,
+};
+
+static struct snd_soc_dai_ops tavil_vi_dai_ops = {
+	.hw_params = tavil_vi_hw_params,
+	.set_channel_map = tavil_set_channel_map,
+	.get_channel_map = tavil_get_channel_map,
+};
+
+static struct snd_soc_dai_driver tavil_dai[] = {
+	{
+		.name = "tavil_rx1",
+		.id = AIF1_PB,
+		.playback = {
+			.stream_name = "AIF1 Playback",
+			.rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_S32_LE,
+			.rate_min = 8000,
+			.rate_max = 384000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_tx1",
+		.id = AIF1_CAP,
+		.capture = {
+			.stream_name = "AIF1 Capture",
+			.rates = WCD934X_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_rx2",
+		.id = AIF2_PB,
+		.playback = {
+			.stream_name = "AIF2 Playback",
+			.rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_S32_LE,
+			.rate_min = 8000,
+			.rate_max = 384000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_tx2",
+		.id = AIF2_CAP,
+		.capture = {
+			.stream_name = "AIF2 Capture",
+			.rates = WCD934X_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_rx3",
+		.id = AIF3_PB,
+		.playback = {
+			.stream_name = "AIF3 Playback",
+			.rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_S32_LE,
+			.rate_min = 8000,
+			.rate_max = 384000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_tx3",
+		.id = AIF3_CAP,
+		.capture = {
+			.stream_name = "AIF3 Capture",
+			.rates = WCD934X_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_LE,
+			.rate_min = 8000,
+			.rate_max = 192000,
+			.channels_min = 1,
+			.channels_max = 4,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_rx4",
+		.id = AIF4_PB,
+		.playback = {
+			.stream_name = "AIF4 Playback",
+			.rates = WCD934X_RATES_MASK | WCD934X_FRAC_RATES_MASK,
+			.formats = WCD934X_FORMATS_S16_S24_S32_LE,
+			.rate_min = 8000,
+			.rate_max = 384000,
+			.channels_min = 1,
+			.channels_max = 2,
+		},
+		.ops = &tavil_dai_ops,
+	},
+	{
+		.name = "tavil_vifeedback",
+		.id = AIF4_VIFEED,
+		.capture = {
+			.stream_name = "VIfeed",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_48000,
+			.formats = WCD934X_FORMATS_S16_S24_S32_LE,
+			.rate_min = 8000,
+			.rate_max = 48000,
+			.channels_min = 1,
+			.channels_max = 4,
+		 },
+		.ops = &tavil_vi_dai_ops,
+	},
+	{
+		.name = "tavil_mad1",
+		.id = AIF4_MAD_TX,
+		.capture = {
+			.stream_name = "AIF4 MAD TX",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = WCD934X_FORMATS_S16_LE,
+			.rate_min = 16000,
+			.rate_max = 16000,
+			.channels_min = 1,
+			.channels_max = 1,
+		},
+		.ops = &tavil_dai_ops,
+	},
+};
+
+static void tavil_codec_power_gate_digital_core(struct tavil_priv *tavil)
+{
+	struct snd_soc_codec *codec = tavil->codec;
+
+	if (!codec)
+		return;
+
+	mutex_lock(&tavil->power_lock);
+	dev_dbg(codec->dev, "%s: Entering power gating function, %d\n",
+		__func__, tavil->power_active_ref);
+
+	if (tavil->power_active_ref > 0)
+		goto exit;
+
+	wcd9xxx_set_power_state(tavil->wcd9xxx,
+			WCD_REGION_POWER_COLLAPSE_BEGIN,
+			WCD9XXX_DIG_CORE_REGION_1);
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			0x04, 0x04);
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			0x01, 0x00);
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL,
+			0x02, 0x00);
+	wcd9xxx_set_power_state(tavil->wcd9xxx, WCD_REGION_POWER_DOWN,
+				WCD9XXX_DIG_CORE_REGION_1);
+exit:
+	dev_dbg(codec->dev, "%s: Exiting power gating function, %d\n",
+		__func__, tavil->power_active_ref);
+	mutex_unlock(&tavil->power_lock);
+}
+
+static void tavil_codec_power_gate_work(struct work_struct *work)
+{
+	struct tavil_priv *tavil;
+	struct delayed_work *dwork;
+	struct snd_soc_codec *codec;
+
+	dwork = to_delayed_work(work);
+	tavil = container_of(dwork, struct tavil_priv, power_gate_work);
+	codec = tavil->codec;
+
+	if (!codec)
+		return;
+
+	tavil_codec_power_gate_digital_core(tavil);
+}
+
+/* called under power_lock acquisition */
+static int tavil_dig_core_remove_power_collapse(struct snd_soc_codec *codec)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x5);
+	snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x7);
+	snd_soc_write(codec, WCD934X_CODEC_RPM_PWR_CDC_DIG_HM_CTL, 0x3);
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x00);
+	snd_soc_update_bits(codec, WCD934X_CODEC_RPM_RST_CTL, 0x02, 0x02);
+
+	wcd9xxx_set_power_state(tavil->wcd9xxx,
+			WCD_REGION_POWER_COLLAPSE_REMOVE,
+			WCD9XXX_DIG_CORE_REGION_1);
+	regcache_mark_dirty(codec->component.regmap);
+	regcache_sync_region(codec->component.regmap,
+			     WCD934X_DIG_CORE_REG_MIN,
+			     WCD934X_DIG_CORE_REG_MAX);
+
+	return 0;
+}
+
+static int tavil_dig_core_power_collapse(struct tavil_priv *tavil,
+					 int req_state)
+{
+	struct snd_soc_codec *codec;
+	int cur_state;
+
+	/* Exit if feature is disabled */
+	if (!dig_core_collapse_enable)
+		return 0;
+
+	mutex_lock(&tavil->power_lock);
+	if (req_state == POWER_COLLAPSE)
+		tavil->power_active_ref--;
+	else if (req_state == POWER_RESUME)
+		tavil->power_active_ref++;
+	else
+		goto unlock_mutex;
+
+	if (tavil->power_active_ref < 0) {
+		dev_dbg(tavil->dev, "%s: power_active_ref is negative\n",
+			__func__);
+		goto unlock_mutex;
+	}
+
+	codec = tavil->codec;
+	if (!codec)
+		goto unlock_mutex;
+
+	if (req_state == POWER_COLLAPSE) {
+		if (tavil->power_active_ref == 0) {
+			schedule_delayed_work(&tavil->power_gate_work,
+			msecs_to_jiffies(dig_core_collapse_timer * 1000));
+		}
+	} else if (req_state == POWER_RESUME) {
+		if (tavil->power_active_ref == 1) {
+			/*
+			 * At this point, there can be two cases:
+			 * 1. Core already in power collapse state
+			 * 2. Timer kicked in and still did not expire or
+			 * waiting for the power_lock
+			 */
+			cur_state = wcd9xxx_get_current_power_state(
+						tavil->wcd9xxx,
+						WCD9XXX_DIG_CORE_REGION_1);
+			if (cur_state == WCD_REGION_POWER_DOWN) {
+				tavil_dig_core_remove_power_collapse(codec);
+			} else {
+				mutex_unlock(&tavil->power_lock);
+				cancel_delayed_work_sync(
+						&tavil->power_gate_work);
+				mutex_lock(&tavil->power_lock);
+			}
+		}
+	}
+
+unlock_mutex:
+	mutex_unlock(&tavil->power_lock);
+
+	return 0;
+}
+
+static int tavil_cdc_req_mclk_enable(struct tavil_priv *tavil,
+				     bool enable)
+{
+	int ret = 0;
+
+	if (enable) {
+		ret = clk_prepare_enable(tavil->wcd_ext_clk);
+		if (ret) {
+			dev_err(tavil->dev, "%s: ext clk enable failed\n",
+				__func__);
+			goto done;
+		}
+		/* get BG */
+		wcd_resmgr_enable_master_bias(tavil->resmgr);
+		/* get MCLK */
+		wcd_resmgr_enable_clk_block(tavil->resmgr, WCD_CLK_MCLK);
+	} else {
+		/* put MCLK */
+		wcd_resmgr_disable_clk_block(tavil->resmgr, WCD_CLK_MCLK);
+		/* put BG */
+		wcd_resmgr_disable_master_bias(tavil->resmgr);
+		clk_disable_unprepare(tavil->wcd_ext_clk);
+	}
+
+done:
+	return ret;
+}
+
+static int __tavil_cdc_mclk_enable_locked(struct tavil_priv *tavil,
+					  bool enable)
+{
+	int ret = 0;
+
+	if (!tavil->wcd_ext_clk) {
+		dev_err(tavil->dev, "%s: wcd ext clock is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(tavil->dev, "%s: mclk_enable = %u\n", __func__, enable);
+
+	if (enable) {
+		tavil_dig_core_power_collapse(tavil, POWER_RESUME);
+		tavil_vote_svs(tavil, true);
+		ret = tavil_cdc_req_mclk_enable(tavil, true);
+		if (ret)
+			goto done;
+	} else {
+		tavil_cdc_req_mclk_enable(tavil, false);
+		tavil_vote_svs(tavil, false);
+		tavil_dig_core_power_collapse(tavil, POWER_COLLAPSE);
+	}
+
+done:
+	return ret;
+}
+
+static int __tavil_cdc_mclk_enable(struct tavil_priv *tavil,
+				   bool enable)
+{
+	int ret;
+
+	WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr);
+	ret = __tavil_cdc_mclk_enable_locked(tavil, enable);
+	WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr);
+
+	return ret;
+}
+
+static ssize_t tavil_codec_version_read(struct snd_info_entry *entry,
+					void *file_private_data,
+					struct file *file,
+					char __user *buf, size_t count,
+					loff_t pos)
+{
+	struct tavil_priv *tavil;
+	struct wcd9xxx *wcd9xxx;
+	char buffer[TAVIL_VERSION_ENTRY_SIZE];
+	int len = 0;
+
+	tavil = (struct tavil_priv *) entry->private_data;
+	if (!tavil) {
+		pr_err("%s: tavil priv is null\n", __func__);
+		return -EINVAL;
+	}
+
+	wcd9xxx = tavil->wcd9xxx;
+
+	switch (wcd9xxx->version) {
+	case TAVIL_VERSION_WCD9340_1_0:
+		len = snprintf(buffer, sizeof(buffer), "WCD9340_1_0\n");
+		break;
+	case TAVIL_VERSION_WCD9341_1_0:
+		len = snprintf(buffer, sizeof(buffer), "WCD9341_1_0\n");
+		break;
+	case TAVIL_VERSION_WCD9340_1_1:
+		len = snprintf(buffer, sizeof(buffer), "WCD9340_1_1\n");
+		break;
+	case TAVIL_VERSION_WCD9341_1_1:
+		len = snprintf(buffer, sizeof(buffer), "WCD9341_1_1\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 tavil_codec_info_ops = {
+	.read = tavil_codec_version_read,
+};
+
+/*
+ * tavil_codec_info_create_codec_entry - creates wcd934x module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wcd934x module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int tavil_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+					struct snd_soc_codec *codec)
+{
+	struct snd_info_entry *version_entry;
+	struct tavil_priv *tavil;
+	struct snd_soc_card *card;
+
+	if (!codec_root || !codec)
+		return -EINVAL;
+
+	tavil = snd_soc_codec_get_drvdata(codec);
+	card = codec->component.card;
+	tavil->entry = snd_info_create_subdir(codec_root->module,
+					      "tavil", codec_root);
+	if (!tavil->entry) {
+		dev_dbg(codec->dev, "%s: failed to create wcd934x entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry = snd_info_create_card_entry(card->snd_card,
+						   "version",
+						   tavil->entry);
+	if (!version_entry) {
+		dev_dbg(codec->dev, "%s: failed to create wcd934x version entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry->private_data = tavil;
+	version_entry->size = TAVIL_VERSION_ENTRY_SIZE;
+	version_entry->content = SNDRV_INFO_CONTENT_DATA;
+	version_entry->c.ops = &tavil_codec_info_ops;
+
+	if (snd_info_register(version_entry) < 0) {
+		snd_info_free_entry(version_entry);
+		return -ENOMEM;
+	}
+	tavil->version_entry = version_entry;
+
+	return 0;
+}
+EXPORT_SYMBOL(tavil_codec_info_create_codec_entry);
+
+/**
+ * tavil_cdc_mclk_enable - Enable/disable codec mclk
+ *
+ * @codec: codec instance
+ * @enable: Indicates clk enable or disable
+ *
+ * Returns 0 on Success and error on failure
+ */
+int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	return __tavil_cdc_mclk_enable(tavil, enable);
+}
+EXPORT_SYMBOL(tavil_cdc_mclk_enable);
+
+static int __tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+					   bool enable)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (enable) {
+		if (wcd_resmgr_get_clk_type(tavil->resmgr) ==
+		    WCD_CLK_RCO) {
+			ret = wcd_resmgr_enable_clk_block(tavil->resmgr,
+							  WCD_CLK_RCO);
+		} else {
+			ret = tavil_cdc_req_mclk_enable(tavil, true);
+			if (ret) {
+				dev_err(codec->dev,
+					"%s: mclk_enable failed, err = %d\n",
+					__func__, ret);
+				goto done;
+			}
+			ret = wcd_resmgr_enable_clk_block(tavil->resmgr,
+							   WCD_CLK_RCO);
+			ret |= tavil_cdc_req_mclk_enable(tavil, false);
+		}
+
+	} else {
+		ret = wcd_resmgr_disable_clk_block(tavil->resmgr,
+						   WCD_CLK_RCO);
+	}
+
+	if (ret) {
+		dev_err(codec->dev, "%s: Error in %s RCO\n",
+			__func__, (enable ? "enabling" : "disabling"));
+		ret = -EINVAL;
+	}
+
+done:
+	return ret;
+}
+
+/*
+ * tavil_codec_internal_rco_ctrl: Enable/Disable codec's RCO clock
+ * @codec: Handle to the codec
+ * @enable: Indicates whether clock should be enabled or disabled
+ */
+static int tavil_codec_internal_rco_ctrl(struct snd_soc_codec *codec,
+					 bool enable)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	WCD9XXX_V2_BG_CLK_LOCK(tavil->resmgr);
+	ret = __tavil_codec_internal_rco_ctrl(codec, enable);
+	WCD9XXX_V2_BG_CLK_UNLOCK(tavil->resmgr);
+	return ret;
+}
+
+static const struct wcd_resmgr_cb tavil_resmgr_cb = {
+	.cdc_rco_ctrl = __tavil_codec_internal_rco_ctrl,
+};
+
+static const struct tavil_reg_mask_val tavil_codec_mclk2_1_1_defaults[] = {
+	{WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_mclk2_1_0_defaults[] = {
+	/*
+	 * PLL Settings:
+	 * Clock Root: MCLK2,
+	 * Clock Source: EXT_CLK,
+	 * Clock Destination: MCLK2
+	 * Clock Freq In: 19.2MHz,
+	 * Clock Freq Out: 11.2896MHz
+	 */
+	{WCD934X_CLK_SYS_MCLK2_PRG1, 0x60, 0x20},
+	{WCD934X_CLK_SYS_INT_POST_DIV_REG0, 0xFF, 0x5E},
+	{WCD934X_CLK_SYS_INT_POST_DIV_REG1, 0x1F, 0x1F},
+	{WCD934X_CLK_SYS_INT_REF_DIV_REG0, 0xFF, 0x54},
+	{WCD934X_CLK_SYS_INT_REF_DIV_REG1, 0xFF, 0x01},
+	{WCD934X_CLK_SYS_INT_FILTER_REG1, 0x07, 0x04},
+	{WCD934X_CLK_SYS_INT_PLL_L_VAL, 0xFF, 0x93},
+	{WCD934X_CLK_SYS_INT_PLL_N_VAL, 0xFF, 0xFA},
+	{WCD934X_CLK_SYS_INT_TEST_REG0, 0xFF, 0x90},
+	{WCD934X_CLK_SYS_INT_PFD_CP_DSM_PROG, 0xFF, 0x7E},
+	{WCD934X_CLK_SYS_INT_VCO_PROG, 0xFF, 0xF8},
+	{WCD934X_CLK_SYS_INT_TEST_REG1, 0xFF, 0x68},
+	{WCD934X_CLK_SYS_INT_LDO_LOCK_CFG, 0xFF, 0x40},
+	{WCD934X_CLK_SYS_INT_DIG_LOCK_DET_CFG, 0xFF, 0x32},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_reg_defaults[] = {
+	{WCD934X_BIAS_VBG_FINE_ADJ, 0xFF, 0x75},
+	{WCD934X_CODEC_CPR_SVS_CX_VDD, 0xFF, 0x7C}, /* value in svs mode */
+	{WCD934X_CODEC_CPR_SVS2_CX_VDD, 0xFF, 0x58}, /* value in svs2 mode */
+	{WCD934X_CDC_RX0_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_RX1_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_RX2_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_RX3_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_RX4_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_RX7_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_RX8_RX_PATH_DSMDEM_CTL, 0x01, 0x01},
+	{WCD934X_CDC_COMPANDER8_CTL7, 0x1E, 0x18},
+	{WCD934X_CDC_COMPANDER7_CTL7, 0x1E, 0x18},
+	{WCD934X_CDC_RX0_RX_PATH_SEC0, 0x08, 0x0},
+	{WCD934X_CDC_CLSH_DECAY_CTRL, 0x03, 0x0},
+	{WCD934X_MICB1_TEST_CTL_2, 0x07, 0x01},
+	{WCD934X_CDC_BOOST0_BOOST_CFG1, 0x3F, 0x12},
+	{WCD934X_CDC_BOOST0_BOOST_CFG2, 0x1C, 0x08},
+	{WCD934X_CDC_BOOST1_BOOST_CFG1, 0x3F, 0x12},
+	{WCD934X_CDC_BOOST1_BOOST_CFG2, 0x1C, 0x08},
+	{WCD934X_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 0x1F, 0x09},
+	{WCD934X_CDC_TX0_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX1_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX2_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX3_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX4_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX5_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX6_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX7_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_CDC_TX8_TX_PATH_CFG1, 0x01, 0x00},
+	{WCD934X_RX_OCP_CTL, 0x0F, 0x02}, /* OCP number of attempts is 2 */
+	{WCD934X_HPH_OCP_CTL, 0xFF, 0x3A}, /* OCP current limit */
+	{WCD934X_HPH_L_TEST, 0x01, 0x01},
+	{WCD934X_HPH_R_TEST, 0x01, 0x01},
+	{WCD934X_CPE_FLL_CONFIG_CTL_2, 0xFF, 0x20},
+};
+
+static const struct tavil_reg_mask_val tavil_codec_reg_init_1_1_val[] = {
+	{WCD934X_CDC_COMPANDER1_CTL7, 0x1E, 0x06},
+	{WCD934X_CDC_COMPANDER2_CTL7, 0x1E, 0x06},
+	{WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_L, 0xFF, 0x84},
+	{WCD934X_HPH_NEW_INT_RDAC_HD2_CTL_R, 0xFF, 0x84},
+	{WCD934X_CDC_RX3_RX_PATH_SEC0, 0xFC, 0xF4},
+	{WCD934X_CDC_RX4_RX_PATH_SEC0, 0xFC, 0xF4},
+};
+
+static const struct tavil_cpr_reg_defaults cpr_defaults[] = {
+	{ 0x00000820, 0x00000094 },
+	{ 0x00000fC0, 0x00000048 },
+	{ 0x0000f000, 0x00000044 },
+	{ 0x0000bb80, 0xC0000178 },
+	{ 0x00000000, 0x00000160 },
+	{ 0x10854522, 0x00000060 },
+	{ 0x10854509, 0x00000064 },
+	{ 0x108544dd, 0x00000068 },
+	{ 0x108544ad, 0x0000006C },
+	{ 0x0000077E, 0x00000070 },
+	{ 0x000007da, 0x00000074 },
+	{ 0x00000000, 0x00000078 },
+	{ 0x00000000, 0x0000007C },
+	{ 0x00042029, 0x00000080 },
+	{ 0x4002002A, 0x00000090 },
+	{ 0x4002002B, 0x00000090 },
+};
+
+static const struct tavil_reg_mask_val tavil_codec_reg_init_common_val[] = {
+	{WCD934X_CDC_CLSH_K2_MSB, 0x0F, 0x00},
+	{WCD934X_CDC_CLSH_K2_LSB, 0xFF, 0x60},
+	{WCD934X_CPE_SS_DMIC_CFG, 0x80, 0x00},
+	{WCD934X_CDC_BOOST0_BOOST_CTL, 0x70, 0x50},
+	{WCD934X_CDC_BOOST1_BOOST_CTL, 0x70, 0x50},
+	{WCD934X_CDC_RX7_RX_PATH_CFG1, 0x08, 0x08},
+	{WCD934X_CDC_RX8_RX_PATH_CFG1, 0x08, 0x08},
+	{WCD934X_CDC_TOP_TOP_CFG1, 0x02, 0x02},
+	{WCD934X_CDC_TOP_TOP_CFG1, 0x01, 0x01},
+	{WCD934X_CDC_TX9_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD934X_CDC_TX10_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD934X_CDC_TX11_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD934X_CDC_TX12_SPKR_PROT_PATH_CFG0, 0x01, 0x01},
+	{WCD934X_DATA_HUB_SB_TX11_INP_CFG, 0x01, 0x01},
+	{WCD934X_CDC_CLK_RST_CTRL_FS_CNT_CONTROL, 0x01, 0x01},
+	{WCD934X_CDC_COMPANDER7_CTL3, 0x80, 0x80},
+	{WCD934X_CDC_COMPANDER8_CTL3, 0x80, 0x80},
+	{WCD934X_CDC_COMPANDER7_CTL7, 0x01, 0x01},
+	{WCD934X_CDC_COMPANDER8_CTL7, 0x01, 0x01},
+	{WCD934X_CODEC_RPM_CLK_GATE, 0x08, 0x00},
+	{WCD934X_TLMM_DMIC3_CLK_PINCFG, 0xFF, 0x0a},
+	{WCD934X_TLMM_DMIC3_DATA_PINCFG, 0xFF, 0x0a},
+	{WCD934X_CPE_SS_SVA_CFG, 0x60, 0x00},
+};
+
+static void tavil_codec_init_reg(struct tavil_priv *priv)
+{
+	struct snd_soc_codec *codec = priv->codec;
+	u32 i;
+
+	for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_common_val); i++)
+		snd_soc_update_bits(codec,
+				    tavil_codec_reg_init_common_val[i].reg,
+				    tavil_codec_reg_init_common_val[i].mask,
+				    tavil_codec_reg_init_common_val[i].val);
+
+	if (TAVIL_IS_1_1(priv->wcd9xxx)) {
+		for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_init_1_1_val); i++)
+			snd_soc_update_bits(codec,
+					tavil_codec_reg_init_1_1_val[i].reg,
+					tavil_codec_reg_init_1_1_val[i].mask,
+					tavil_codec_reg_init_1_1_val[i].val);
+	}
+}
+
+static void tavil_update_reg_defaults(struct tavil_priv *tavil)
+{
+	u32 i;
+	struct wcd9xxx *wcd9xxx;
+
+	wcd9xxx = tavil->wcd9xxx;
+	for (i = 0; i < ARRAY_SIZE(tavil_codec_reg_defaults); i++)
+		regmap_update_bits(wcd9xxx->regmap,
+				   tavil_codec_reg_defaults[i].reg,
+				   tavil_codec_reg_defaults[i].mask,
+				   tavil_codec_reg_defaults[i].val);
+}
+
+static void tavil_update_cpr_defaults(struct tavil_priv *tavil)
+{
+	int i;
+	struct wcd9xxx *wcd9xxx;
+
+	wcd9xxx = tavil->wcd9xxx;
+	if (!TAVIL_IS_1_1(wcd9xxx))
+		return;
+
+	__tavil_cdc_mclk_enable(tavil, true);
+
+	regmap_write(wcd9xxx->regmap, WCD934X_CODEC_CPR_SVS2_MIN_CX_VDD, 0x2C);
+	regmap_update_bits(wcd9xxx->regmap, WCD934X_CODEC_RPM_CLK_GATE,
+			   0x10, 0x00);
+
+	for (i = 0; i < ARRAY_SIZE(cpr_defaults); i++) {
+		regmap_bulk_write(wcd9xxx->regmap,
+				WCD934X_CODEC_CPR_WR_DATA_0,
+				(u8 *)&cpr_defaults[i].wr_data, 4);
+		regmap_bulk_write(wcd9xxx->regmap,
+				WCD934X_CODEC_CPR_WR_ADDR_0,
+				(u8 *)&cpr_defaults[i].wr_addr, 4);
+	}
+
+	__tavil_cdc_mclk_enable(tavil, false);
+}
+
+static void tavil_slim_interface_init_reg(struct snd_soc_codec *codec)
+{
+	int i;
+	struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+	for (i = 0; i < WCD9XXX_SLIM_NUM_PORT_REG; i++)
+		wcd9xxx_interface_reg_write(priv->wcd9xxx,
+				    WCD934X_SLIM_PGD_PORT_INT_RX_EN0 + i,
+				    0xFF);
+}
+
+static irqreturn_t tavil_misc_irq(int irq, void *data)
+{
+	struct tavil_priv *tavil = data;
+	int misc_val;
+
+	/* Find source of interrupt */
+	regmap_read(tavil->wcd9xxx->regmap, WCD934X_INTR_CODEC_MISC_STATUS,
+		    &misc_val);
+
+	if (misc_val & 0x08) {
+		dev_info(tavil->dev, "%s: irq: %d, DSD DC detected!\n",
+			 __func__, irq);
+		/* DSD DC interrupt, reset DSD path */
+		tavil_dsd_reset(tavil->dsd_config);
+	} else {
+		dev_err(tavil->dev, "%s: Codec misc irq: %d, val: 0x%x\n",
+			__func__, irq, misc_val);
+	}
+
+	/* Clear interrupt status */
+	regmap_update_bits(tavil->wcd9xxx->regmap,
+			   WCD934X_INTR_CODEC_MISC_CLEAR, misc_val, 0x00);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t tavil_slimbus_irq(int irq, void *data)
+{
+	struct tavil_priv *tavil = data;
+	unsigned long status = 0;
+	int i, j, port_id, k;
+	u32 bit;
+	u8 val, int_val = 0;
+	bool tx, cleared;
+	unsigned short reg = 0;
+
+	for (i = WCD934X_SLIM_PGD_PORT_INT_STATUS_RX_0, j = 0;
+	     i <= WCD934X_SLIM_PGD_PORT_INT_STATUS_TX_1; i++, j++) {
+		val = wcd9xxx_interface_reg_read(tavil->wcd9xxx, i);
+		status |= ((u32)val << (8 * j));
+	}
+
+	for_each_set_bit(j, &status, 32) {
+		tx = (j >= 16 ? true : false);
+		port_id = (tx ? j - 16 : j);
+		val = wcd9xxx_interface_reg_read(tavil->wcd9xxx,
+				WCD934X_SLIM_PGD_PORT_INT_RX_SOURCE0 + j);
+		if (val) {
+			if (!tx)
+				reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 +
+					(port_id / 8);
+			else
+				reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 +
+					(port_id / 8);
+			int_val = wcd9xxx_interface_reg_read(
+				tavil->wcd9xxx, reg);
+			/*
+			 * Ignore interrupts for ports for which the
+			 * interrupts are not specifically enabled.
+			 */
+			if (!(int_val & (1 << (port_id % 8))))
+				continue;
+		}
+		if (val & WCD934X_SLIM_IRQ_OVERFLOW)
+			dev_err_ratelimited(tavil->dev, "%s: overflow error on %s port %d, value %x\n",
+			   __func__, (tx ? "TX" : "RX"), port_id, val);
+		if (val & WCD934X_SLIM_IRQ_UNDERFLOW)
+			dev_err_ratelimited(tavil->dev, "%s: underflow error on %s port %d, value %x\n",
+			   __func__, (tx ? "TX" : "RX"), port_id, val);
+		if ((val & WCD934X_SLIM_IRQ_OVERFLOW) ||
+			(val & WCD934X_SLIM_IRQ_UNDERFLOW)) {
+			if (!tx)
+				reg = WCD934X_SLIM_PGD_PORT_INT_RX_EN0 +
+					(port_id / 8);
+			else
+				reg = WCD934X_SLIM_PGD_PORT_INT_TX_EN0 +
+					(port_id / 8);
+			int_val = wcd9xxx_interface_reg_read(
+				tavil->wcd9xxx, reg);
+			if (int_val & (1 << (port_id % 8))) {
+				int_val = int_val ^ (1 << (port_id % 8));
+				wcd9xxx_interface_reg_write(tavil->wcd9xxx,
+					reg, int_val);
+			}
+		}
+		if (val & WCD934X_SLIM_IRQ_PORT_CLOSED) {
+			/*
+			 * INT SOURCE register starts from RX to TX
+			 * but port number in the ch_mask is in opposite way
+			 */
+			bit = (tx ? j - 16 : j + 16);
+			dev_dbg(tavil->dev, "%s: %s port %d closed value %x, bit %u\n",
+				 __func__, (tx ? "TX" : "RX"), port_id, val,
+				 bit);
+			for (k = 0, cleared = false; k < NUM_CODEC_DAIS; k++) {
+				dev_dbg(tavil->dev, "%s: tavil->dai[%d].ch_mask = 0x%lx\n",
+					 __func__, k, tavil->dai[k].ch_mask);
+				if (test_and_clear_bit(bit,
+						&tavil->dai[k].ch_mask)) {
+					cleared = true;
+					if (!tavil->dai[k].ch_mask)
+						wake_up(
+						      &tavil->dai[k].dai_wait);
+					/*
+					 * There are cases when multiple DAIs
+					 * might be using the same slimbus
+					 * channel. Hence don't break here.
+					 */
+				}
+			}
+			WARN(!cleared,
+			     "Couldn't find slimbus %s port %d for closing\n",
+			     (tx ? "TX" : "RX"), port_id);
+		}
+		wcd9xxx_interface_reg_write(tavil->wcd9xxx,
+					    WCD934X_SLIM_PGD_PORT_INT_CLR_RX_0 +
+					    (j / 8),
+					    1 << (j % 8));
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int tavil_setup_irqs(struct tavil_priv *tavil)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = tavil->codec;
+	struct wcd9xxx *wcd9xxx = tavil->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	ret = wcd9xxx_request_irq(core_res, WCD9XXX_IRQ_SLIMBUS,
+				  tavil_slimbus_irq, "SLIMBUS Slave", tavil);
+	if (ret)
+		dev_err(codec->dev, "%s: Failed to request irq %d\n", __func__,
+		       WCD9XXX_IRQ_SLIMBUS);
+	else
+		tavil_slim_interface_init_reg(codec);
+
+	/* Register for misc interrupts as well */
+	ret = wcd9xxx_request_irq(core_res, WCD934X_IRQ_MISC,
+				  tavil_misc_irq, "CDC MISC Irq", tavil);
+	if (ret)
+		dev_err(codec->dev, "%s: Failed to request cdc misc irq\n",
+			__func__);
+
+	return ret;
+}
+
+static void tavil_init_slim_slave_cfg(struct snd_soc_codec *codec)
+{
+	struct tavil_priv *priv = snd_soc_codec_get_drvdata(codec);
+	struct afe_param_cdc_slimbus_slave_cfg *cfg;
+	struct wcd9xxx *wcd9xxx = priv->wcd9xxx;
+	uint64_t eaddr = 0;
+
+	cfg = &priv->slimbus_slave_cfg;
+	cfg->minor_version = 1;
+	cfg->tx_slave_port_offset = 0;
+	cfg->rx_slave_port_offset = 16;
+
+	memcpy(&eaddr, &wcd9xxx->slim->e_addr, sizeof(wcd9xxx->slim->e_addr));
+	WARN_ON(sizeof(wcd9xxx->slim->e_addr) != 6);
+	cfg->device_enum_addr_lsw = eaddr & 0xFFFFFFFF;
+	cfg->device_enum_addr_msw = eaddr >> 32;
+
+	dev_dbg(codec->dev, "%s: slimbus logical address 0x%llx\n",
+		__func__, eaddr);
+}
+
+static void tavil_cleanup_irqs(struct tavil_priv *tavil)
+{
+	struct wcd9xxx *wcd9xxx = tavil->wcd9xxx;
+	struct wcd9xxx_core_resource *core_res =
+				&wcd9xxx->core_res;
+
+	wcd9xxx_free_irq(core_res, WCD9XXX_IRQ_SLIMBUS, tavil);
+	wcd9xxx_free_irq(core_res, WCD934X_IRQ_MISC, tavil);
+}
+
+/*
+ * wcd934x_get_micb_vout_ctl_val: converts micbias from volts to register value
+ * @micb_mv: micbias in mv
+ *
+ * return register value converted
+ */
+int wcd934x_get_micb_vout_ctl_val(u32 micb_mv)
+{
+	/* min micbias voltage is 1V and maximum is 2.85V */
+	if (micb_mv < 1000 || micb_mv > 2850) {
+		pr_err("%s: unsupported micbias voltage\n", __func__);
+		return -EINVAL;
+	}
+
+	return (micb_mv - 1000) / 50;
+}
+EXPORT_SYMBOL(wcd934x_get_micb_vout_ctl_val);
+
+static int tavil_handle_pdata(struct tavil_priv *tavil,
+			      struct wcd9xxx_pdata *pdata)
+{
+	struct snd_soc_codec *codec = tavil->codec;
+	u8 mad_dmic_ctl_val;
+	u8 anc_ctl_value;
+	u32 def_dmic_rate, dmic_clk_drv;
+	int vout_ctl_1, vout_ctl_2, vout_ctl_3, vout_ctl_4;
+	int rc = 0;
+
+	if (!pdata) {
+		dev_err(codec->dev, "%s: NULL pdata\n", __func__);
+		return -ENODEV;
+	}
+
+	/* set micbias voltage */
+	vout_ctl_1 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb1_mv);
+	vout_ctl_2 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb2_mv);
+	vout_ctl_3 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb3_mv);
+	vout_ctl_4 = wcd934x_get_micb_vout_ctl_val(pdata->micbias.micb4_mv);
+	if (vout_ctl_1 < 0 || vout_ctl_2 < 0 ||
+	    vout_ctl_3 < 0 || vout_ctl_4 < 0) {
+		rc = -EINVAL;
+		goto done;
+	}
+	snd_soc_update_bits(codec, WCD934X_ANA_MICB1, 0x3F, vout_ctl_1);
+	snd_soc_update_bits(codec, WCD934X_ANA_MICB2, 0x3F, vout_ctl_2);
+	snd_soc_update_bits(codec, WCD934X_ANA_MICB3, 0x3F, vout_ctl_3);
+	snd_soc_update_bits(codec, WCD934X_ANA_MICB4, 0x3F, vout_ctl_4);
+
+	/* Set the DMIC sample rate */
+	switch (pdata->mclk_rate) {
+	case WCD934X_MCLK_CLK_9P6MHZ:
+		def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P8MHZ;
+		break;
+	case WCD934X_MCLK_CLK_12P288MHZ:
+		def_dmic_rate = WCD9XXX_DMIC_SAMPLE_RATE_4P096MHZ;
+		break;
+	default:
+		/* should never happen */
+		dev_err(codec->dev, "%s: Invalid mclk_rate %d\n",
+			__func__, pdata->mclk_rate);
+		rc = -EINVAL;
+		goto done;
+	};
+
+	if (pdata->dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		dev_info(codec->dev, "%s: dmic_rate invalid default = %d\n",
+			__func__, def_dmic_rate);
+		pdata->dmic_sample_rate = def_dmic_rate;
+	}
+	if (pdata->mad_dmic_sample_rate ==
+	    WCD9XXX_DMIC_SAMPLE_RATE_UNDEFINED) {
+		dev_info(codec->dev, "%s: mad_dmic_rate invalid default = %d\n",
+			__func__, def_dmic_rate);
+		/*
+		 * use dmic_sample_rate as the default for MAD
+		 * if mad dmic sample rate is undefined
+		 */
+		pdata->mad_dmic_sample_rate = pdata->dmic_sample_rate;
+	}
+
+	if (pdata->dmic_clk_drv ==
+	    WCD9XXX_DMIC_CLK_DRIVE_UNDEFINED) {
+		pdata->dmic_clk_drv = WCD934X_DMIC_CLK_DRIVE_DEFAULT;
+		dev_info(codec->dev,
+			 "%s: dmic_clk_strength invalid, default = %d\n",
+			 __func__, pdata->dmic_clk_drv);
+	}
+
+	switch (pdata->dmic_clk_drv) {
+	case 2:
+		dmic_clk_drv = 0;
+		break;
+	case 4:
+		dmic_clk_drv = 1;
+		break;
+	case 8:
+		dmic_clk_drv = 2;
+		break;
+	case 16:
+		dmic_clk_drv = 3;
+		break;
+	default:
+		dev_err(codec->dev,
+			"%s: invalid dmic_clk_drv %d, using default\n",
+			__func__, pdata->dmic_clk_drv);
+		dmic_clk_drv = 0;
+		break;
+	}
+
+	snd_soc_update_bits(codec, WCD934X_TEST_DEBUG_PAD_DRVCTL_0,
+			    0x0C, dmic_clk_drv << 2);
+
+	/*
+	 * Default the DMIC clk rates to mad_dmic_sample_rate,
+	 * whereas, the anc/txfe dmic rates to dmic_sample_rate
+	 * since the anc/txfe are independent of mad block.
+	 */
+	mad_dmic_ctl_val = tavil_get_dmic_clk_val(tavil->codec,
+				pdata->mclk_rate,
+				pdata->mad_dmic_sample_rate);
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC0_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC1_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+	snd_soc_update_bits(codec, WCD934X_CPE_SS_DMIC2_CTL,
+		0x0E, mad_dmic_ctl_val << 1);
+
+	if (dmic_clk_drv == WCD934X_DMIC_CLK_DIV_2)
+		anc_ctl_value = WCD934X_ANC_DMIC_X2_FULL_RATE;
+	else
+		anc_ctl_value = WCD934X_ANC_DMIC_X2_HALF_RATE;
+
+	snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL,
+			    0x40, anc_ctl_value << 6);
+	snd_soc_update_bits(codec, WCD934X_CDC_ANC0_MODE_2_CTL,
+			    0x20, anc_ctl_value << 5);
+	snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL,
+			    0x40, anc_ctl_value << 6);
+	snd_soc_update_bits(codec, WCD934X_CDC_ANC1_MODE_2_CTL,
+			    0x20, anc_ctl_value << 5);
+
+done:
+	return rc;
+}
+
+static void tavil_cdc_vote_svs(struct snd_soc_codec *codec, bool vote)
+{
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	return tavil_vote_svs(tavil, vote);
+}
+
+struct wcd_dsp_cdc_cb cdc_cb = {
+	.cdc_clk_en = tavil_codec_internal_rco_ctrl,
+	.cdc_vote_svs = tavil_cdc_vote_svs,
+};
+
+static int tavil_wdsp_initialize(struct snd_soc_codec *codec)
+{
+	struct wcd9xxx *control;
+	struct tavil_priv *tavil;
+	struct wcd_dsp_params params;
+	int ret = 0;
+
+	control = dev_get_drvdata(codec->dev->parent);
+	tavil = snd_soc_codec_get_drvdata(codec);
+
+	params.cb = &cdc_cb;
+	params.irqs.cpe_ipc1_irq = WCD934X_IRQ_CPE1_INTR;
+	params.irqs.cpe_err_irq = WCD934X_IRQ_CPE_ERROR;
+	params.irqs.fatal_irqs = CPE_FATAL_IRQS;
+	params.clk_rate = control->mclk_rate;
+	params.dsp_instance = 0;
+
+	wcd_dsp_cntl_init(codec, &params, &tavil->wdsp_cntl);
+	if (!tavil->wdsp_cntl) {
+		dev_err(tavil->dev, "%s: wcd-dsp-control init failed\n",
+			__func__);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+/*
+ * tavil_soc_get_mbhc: get wcd934x_mbhc handle of corresponding codec
+ * @codec: handle to snd_soc_codec *
+ *
+ * return wcd934x_mbhc handle or error code in case of failure
+ */
+struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec)
+{
+	struct tavil_priv *tavil;
+
+	if (!codec) {
+		pr_err("%s: Invalid params, NULL codec\n", __func__);
+		return NULL;
+	}
+	tavil = snd_soc_codec_get_drvdata(codec);
+
+	if (!tavil) {
+		pr_err("%s: Invalid params, NULL tavil\n", __func__);
+		return NULL;
+	}
+
+	return tavil->mbhc;
+}
+EXPORT_SYMBOL(tavil_soc_get_mbhc);
+
+static void tavil_mclk2_reg_defaults(struct tavil_priv *tavil)
+{
+	int i;
+	struct snd_soc_codec *codec = tavil->codec;
+
+	if (TAVIL_IS_1_0(tavil->wcd9xxx)) {
+		/* MCLK2 configuration */
+		for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_0_defaults); i++)
+			snd_soc_update_bits(codec,
+					tavil_codec_mclk2_1_0_defaults[i].reg,
+					tavil_codec_mclk2_1_0_defaults[i].mask,
+					tavil_codec_mclk2_1_0_defaults[i].val);
+	}
+	if (TAVIL_IS_1_1(tavil->wcd9xxx)) {
+		/* MCLK2 configuration */
+		for (i = 0; i < ARRAY_SIZE(tavil_codec_mclk2_1_1_defaults); i++)
+			snd_soc_update_bits(codec,
+					tavil_codec_mclk2_1_1_defaults[i].reg,
+					tavil_codec_mclk2_1_1_defaults[i].mask,
+					tavil_codec_mclk2_1_1_defaults[i].val);
+	}
+}
+
+static int tavil_device_down(struct wcd9xxx *wcd9xxx)
+{
+	struct snd_soc_codec *codec;
+	struct tavil_priv *priv;
+	int count;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	priv = snd_soc_codec_get_drvdata(codec);
+	swrm_wcd_notify(priv->swr.ctrl_data[0].swr_pdev,
+			SWR_DEVICE_DOWN, NULL);
+	tavil_dsd_reset(priv->dsd_config);
+	snd_soc_card_change_online_state(codec->component.card, 0);
+	for (count = 0; count < NUM_CODEC_DAIS; count++)
+		priv->dai[count].bus_down_in_recovery = true;
+	wcd_dsp_ssr_event(priv->wdsp_cntl, WCD_CDC_DOWN_EVENT);
+	wcd_resmgr_set_sido_input_src_locked(priv->resmgr,
+					     SIDO_SOURCE_INTERNAL);
+
+	return 0;
+}
+
+static int tavil_post_reset_cb(struct wcd9xxx *wcd9xxx)
+{
+	int i, ret = 0;
+	struct wcd9xxx *control;
+	struct snd_soc_codec *codec;
+	struct tavil_priv *tavil;
+	struct wcd9xxx_pdata *pdata;
+	struct wcd_mbhc *mbhc;
+
+	codec = (struct snd_soc_codec *)(wcd9xxx->ssr_priv);
+	tavil = snd_soc_codec_get_drvdata(codec);
+	control = dev_get_drvdata(codec->dev->parent);
+
+	wcd9xxx_set_power_state(tavil->wcd9xxx,
+				WCD_REGION_POWER_COLLAPSE_REMOVE,
+				WCD9XXX_DIG_CORE_REGION_1);
+
+	mutex_lock(&tavil->codec_mutex);
+	/*
+	 * Codec hardware by default comes up in SVS mode.
+	 * Initialize the svs_ref_cnt to 1 to reflect the hardware
+	 * state in the driver.
+	 */
+	tavil->svs_ref_cnt = 1;
+
+	tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+				control->slim_slave->laddr;
+	tavil_slimbus_slave_port_cfg.slave_dev_pgd_la =
+					control->slim->laddr;
+	tavil_init_slim_slave_cfg(codec);
+	snd_soc_card_change_online_state(codec->component.card, 1);
+
+	/* Class-H Init */
+	wcd_clsh_init(&tavil->clsh_d);
+	/* Default HPH Mode to Class-H LOHiFi */
+	tavil->hph_mode = CLS_H_LOHIFI;
+
+	for (i = 0; i < TAVIL_MAX_MICBIAS; i++)
+		tavil->micb_ref[i] = 0;
+
+	for (i = 0; i < COMPANDER_MAX; i++)
+		tavil->comp_enabled[i] = 0;
+
+	dev_dbg(codec->dev, "%s: MCLK Rate = %x\n",
+		__func__, control->mclk_rate);
+
+	if (control->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ)
+		snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+				    0x03, 0x00);
+	else if (control->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+		snd_soc_update_bits(codec, WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+				    0x03, 0x01);
+	wcd_resmgr_post_ssr_v2(tavil->resmgr);
+	tavil_update_reg_defaults(tavil);
+	tavil_codec_init_reg(tavil);
+	__tavil_enable_efuse_sensing(tavil);
+	tavil_mclk2_reg_defaults(tavil);
+
+	__tavil_cdc_mclk_enable(tavil, true);
+	regcache_mark_dirty(codec->component.regmap);
+	regcache_sync(codec->component.regmap);
+	__tavil_cdc_mclk_enable(tavil, false);
+
+	tavil_update_cpr_defaults(tavil);
+
+	pdata = dev_get_platdata(codec->dev->parent);
+	ret = tavil_handle_pdata(tavil, pdata);
+	if (ret < 0)
+		dev_err(codec->dev, "%s: invalid pdata\n", __func__);
+
+	/* Initialize MBHC module */
+	mbhc = &tavil->mbhc->wcd_mbhc;
+	ret = tavil_mbhc_post_ssr_init(tavil->mbhc, codec);
+	if (ret) {
+		dev_err(codec->dev, "%s: mbhc initialization failed\n",
+			__func__);
+		goto done;
+	} else {
+		tavil_mbhc_hs_detect(codec, mbhc->mbhc_cfg);
+	}
+
+	/* DSD initialization */
+	ret = tavil_dsd_post_ssr_init(tavil->dsd_config);
+	if (ret)
+		dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__);
+
+	tavil_cleanup_irqs(tavil);
+	ret = tavil_setup_irqs(tavil);
+	if (ret) {
+		dev_err(codec->dev, "%s: tavil irq setup failed %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	tavil_set_spkr_mode(codec, tavil->swr.spkr_mode);
+	/*
+	 * Once the codec initialization is completed, the svs vote
+	 * can be released allowing the codec to go to SVS2.
+	 */
+	tavil_vote_svs(tavil, false);
+	wcd_dsp_ssr_event(tavil->wdsp_cntl, WCD_CDC_UP_EVENT);
+
+done:
+	mutex_unlock(&tavil->codec_mutex);
+	return ret;
+}
+
+static int tavil_soc_codec_probe(struct snd_soc_codec *codec)
+{
+	struct wcd9xxx *control;
+	struct tavil_priv *tavil;
+	struct wcd9xxx_pdata *pdata;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	int i, ret;
+	void *ptr = NULL;
+
+	control = dev_get_drvdata(codec->dev->parent);
+
+	dev_info(codec->dev, "%s()\n", __func__);
+	tavil = snd_soc_codec_get_drvdata(codec);
+	tavil->intf_type = wcd9xxx_get_intf_type();
+
+	control->dev_down = tavil_device_down;
+	control->post_reset = tavil_post_reset_cb;
+	control->ssr_priv = (void *)codec;
+
+	/* Resource Manager post Init */
+	ret = wcd_resmgr_post_init(tavil->resmgr, &tavil_resmgr_cb, codec);
+	if (ret) {
+		dev_err(codec->dev, "%s: wcd resmgr post init failed\n",
+			__func__);
+		goto err;
+	}
+	/* Class-H Init */
+	wcd_clsh_init(&tavil->clsh_d);
+	/* Default HPH Mode to Class-H Low HiFi */
+	tavil->hph_mode = CLS_H_LOHIFI;
+
+	tavil->fw_data = devm_kzalloc(codec->dev, sizeof(*(tavil->fw_data)),
+				      GFP_KERNEL);
+	if (!tavil->fw_data)
+		goto err;
+
+	set_bit(WCD9XXX_ANC_CAL, tavil->fw_data->cal_bit);
+	set_bit(WCD9XXX_MBHC_CAL, tavil->fw_data->cal_bit);
+	set_bit(WCD9XXX_MAD_CAL, tavil->fw_data->cal_bit);
+	set_bit(WCD9XXX_VBAT_CAL, tavil->fw_data->cal_bit);
+
+	ret = wcd_cal_create_hwdep(tavil->fw_data,
+				   WCD9XXX_CODEC_HWDEP_NODE, codec);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s hwdep failed %d\n", __func__, ret);
+		goto err_hwdep;
+	}
+
+	/* Initialize MBHC module */
+	ret = tavil_mbhc_init(&tavil->mbhc, codec, tavil->fw_data);
+	if (ret) {
+		pr_err("%s: mbhc initialization failed\n", __func__);
+		goto err_hwdep;
+	}
+
+	tavil->codec = codec;
+	for (i = 0; i < COMPANDER_MAX; i++)
+		tavil->comp_enabled[i] = 0;
+
+	tavil_codec_init_reg(tavil);
+
+	pdata = dev_get_platdata(codec->dev->parent);
+	ret = tavil_handle_pdata(tavil, pdata);
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: bad pdata\n", __func__);
+		goto err_hwdep;
+	}
+
+	ptr = devm_kzalloc(codec->dev, (sizeof(tavil_rx_chs) +
+			   sizeof(tavil_tx_chs)), GFP_KERNEL);
+	if (!ptr) {
+		ret = -ENOMEM;
+		goto err_hwdep;
+	}
+
+	snd_soc_dapm_add_routes(dapm, tavil_slim_audio_map,
+			ARRAY_SIZE(tavil_slim_audio_map));
+	for (i = 0; i < NUM_CODEC_DAIS; i++) {
+		INIT_LIST_HEAD(&tavil->dai[i].wcd9xxx_ch_list);
+		init_waitqueue_head(&tavil->dai[i].dai_wait);
+	}
+	tavil_slimbus_slave_port_cfg.slave_dev_intfdev_la =
+				control->slim_slave->laddr;
+	tavil_slimbus_slave_port_cfg.slave_dev_pgd_la =
+				control->slim->laddr;
+	tavil_slimbus_slave_port_cfg.slave_port_mapping[0] =
+				WCD934X_TX13;
+	tavil_init_slim_slave_cfg(codec);
+
+	control->num_rx_port = WCD934X_RX_MAX;
+	control->rx_chs = ptr;
+	memcpy(control->rx_chs, tavil_rx_chs, sizeof(tavil_rx_chs));
+	control->num_tx_port = WCD934X_TX_MAX;
+	control->tx_chs = ptr + sizeof(tavil_rx_chs);
+	memcpy(control->tx_chs, tavil_tx_chs, sizeof(tavil_tx_chs));
+
+	ret = tavil_setup_irqs(tavil);
+	if (ret) {
+		dev_err(tavil->dev, "%s: tavil irq setup failed %d\n",
+			__func__, ret);
+		goto err_pdata;
+	}
+
+	for (i = 0; i < WCD934X_NUM_DECIMATORS; i++) {
+		tavil->tx_hpf_work[i].tavil = tavil;
+		tavil->tx_hpf_work[i].decimator = i;
+		INIT_DELAYED_WORK(&tavil->tx_hpf_work[i].dwork,
+				  tavil_tx_hpf_corner_freq_callback);
+
+		tavil->tx_mute_dwork[i].tavil = tavil;
+		tavil->tx_mute_dwork[i].decimator = i;
+		INIT_DELAYED_WORK(&tavil->tx_mute_dwork[i].dwork,
+				  tavil_tx_mute_update_callback);
+	}
+
+	tavil->spk_anc_dwork.tavil = tavil;
+	INIT_DELAYED_WORK(&tavil->spk_anc_dwork.dwork,
+			  tavil_spk_anc_update_callback);
+
+	tavil_mclk2_reg_defaults(tavil);
+
+	/* DSD initialization */
+	tavil->dsd_config = tavil_dsd_init(codec);
+	if (IS_ERR_OR_NULL(tavil->dsd_config))
+		dev_dbg(tavil->dev, "%s: DSD init failed\n", __func__);
+
+	mutex_lock(&tavil->codec_mutex);
+	snd_soc_dapm_disable_pin(dapm, "ANC EAR PA");
+	snd_soc_dapm_disable_pin(dapm, "ANC EAR");
+	snd_soc_dapm_enable_pin(dapm, "ANC SPK1 PA");
+	mutex_unlock(&tavil->codec_mutex);
+
+	snd_soc_dapm_ignore_suspend(dapm, "AIF1 Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF1 Capture");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF2 Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF2 Capture");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF3 Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF3 Capture");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF4 Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "VIfeed");
+
+	snd_soc_dapm_sync(dapm);
+
+	tavil_wdsp_initialize(codec);
+
+	/*
+	 * Once the codec initialization is completed, the svs vote
+	 * can be released allowing the codec to go to SVS2.
+	 */
+	tavil_vote_svs(tavil, false);
+
+	return ret;
+
+err_pdata:
+	devm_kfree(codec->dev, ptr);
+	control->rx_chs = NULL;
+	control->tx_chs = NULL;
+err_hwdep:
+	devm_kfree(codec->dev, tavil->fw_data);
+	tavil->fw_data = NULL;
+err:
+	return ret;
+}
+
+static int tavil_soc_codec_remove(struct snd_soc_codec *codec)
+{
+	struct wcd9xxx *control;
+	struct tavil_priv *tavil = snd_soc_codec_get_drvdata(codec);
+
+	control = dev_get_drvdata(codec->dev->parent);
+	devm_kfree(codec->dev, control->rx_chs);
+	control->rx_chs = NULL;
+	control->tx_chs = NULL;
+	tavil_cleanup_irqs(tavil);
+
+	if (tavil->wdsp_cntl)
+		wcd_dsp_cntl_deinit(&tavil->wdsp_cntl);
+
+	/* Deinitialize MBHC module */
+	tavil_mbhc_deinit(codec);
+	tavil->mbhc = NULL;
+
+	return 0;
+}
+
+static struct regmap *tavil_get_regmap(struct device *dev)
+{
+	struct wcd9xxx *control = dev_get_drvdata(dev->parent);
+
+	return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_tavil = {
+	.probe = tavil_soc_codec_probe,
+	.remove = tavil_soc_codec_remove,
+	.get_regmap = tavil_get_regmap,
+	.component_driver = {
+		.controls = tavil_snd_controls,
+		.num_controls = ARRAY_SIZE(tavil_snd_controls),
+		.dapm_widgets = tavil_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(tavil_dapm_widgets),
+		.dapm_routes = tavil_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(tavil_audio_map),
+	},
+};
+
+#ifdef CONFIG_PM
+static int tavil_suspend(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tavil_priv *tavil = platform_get_drvdata(pdev);
+
+	if (!tavil) {
+		dev_err(dev, "%s: tavil private data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(dev, "%s: system suspend\n", __func__);
+	if (delayed_work_pending(&tavil->power_gate_work) &&
+	    cancel_delayed_work_sync(&tavil->power_gate_work))
+		tavil_codec_power_gate_digital_core(tavil);
+	return 0;
+}
+
+static int tavil_resume(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct tavil_priv *tavil = platform_get_drvdata(pdev);
+
+	if (!tavil) {
+		dev_err(dev, "%s: tavil private data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(dev, "%s: system resume\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops tavil_pm_ops = {
+	.suspend = tavil_suspend,
+	.resume = tavil_resume,
+};
+#endif
+
+static int tavil_swrm_read(void *handle, int reg)
+{
+	struct tavil_priv *tavil;
+	struct wcd9xxx *wcd9xxx;
+	unsigned short swr_rd_addr_base;
+	unsigned short swr_rd_data_base;
+	int val, ret;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	tavil = (struct tavil_priv *)handle;
+	wcd9xxx = tavil->wcd9xxx;
+
+	dev_dbg(tavil->dev, "%s: Reading soundwire register, 0x%x\n",
+		__func__, reg);
+	swr_rd_addr_base = WCD934X_SWR_AHB_BRIDGE_RD_ADDR_0;
+	swr_rd_data_base = WCD934X_SWR_AHB_BRIDGE_RD_DATA_0;
+
+	mutex_lock(&tavil->swr.read_mutex);
+	ret = regmap_bulk_write(wcd9xxx->regmap, swr_rd_addr_base,
+				 (u8 *)&reg, 4);
+	if (ret < 0) {
+		dev_err(tavil->dev, "%s: RD Addr Failure\n", __func__);
+		goto done;
+	}
+	ret = regmap_bulk_read(wcd9xxx->regmap, swr_rd_data_base,
+				(u8 *)&val, 4);
+	if (ret < 0) {
+		dev_err(tavil->dev, "%s: RD Data Failure\n", __func__);
+		goto done;
+	}
+	ret = val;
+done:
+	mutex_unlock(&tavil->swr.read_mutex);
+
+	return ret;
+}
+
+static int tavil_swrm_bulk_write(void *handle, u32 *reg, u32 *val, size_t len)
+{
+	struct tavil_priv *tavil;
+	struct wcd9xxx *wcd9xxx;
+	struct wcd9xxx_reg_val *bulk_reg;
+	unsigned short swr_wr_addr_base;
+	unsigned short swr_wr_data_base;
+	int i, j, ret;
+
+	if (!handle || !reg || !val) {
+		pr_err("%s: NULL parameter\n", __func__);
+		return -EINVAL;
+	}
+	if (len <= 0) {
+		pr_err("%s: Invalid size: %zu\n", __func__, len);
+		return -EINVAL;
+	}
+	tavil = (struct tavil_priv *)handle;
+	wcd9xxx = tavil->wcd9xxx;
+
+	swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0;
+	swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0;
+
+	bulk_reg = kzalloc((2 * len * sizeof(struct wcd9xxx_reg_val)),
+			   GFP_KERNEL);
+	if (!bulk_reg)
+		return -ENOMEM;
+
+	for (i = 0, j = 0; i < (len * 2); i += 2, j++) {
+		bulk_reg[i].reg = swr_wr_data_base;
+		bulk_reg[i].buf = (u8 *)(&val[j]);
+		bulk_reg[i].bytes = 4;
+		bulk_reg[i+1].reg = swr_wr_addr_base;
+		bulk_reg[i+1].buf = (u8 *)(&reg[j]);
+		bulk_reg[i+1].bytes = 4;
+	}
+
+	mutex_lock(&tavil->swr.write_mutex);
+	ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg,
+			 (len * 2), false);
+	if (ret) {
+		dev_err(tavil->dev, "%s: swrm bulk write failed, ret: %d\n",
+			__func__, ret);
+	}
+	mutex_unlock(&tavil->swr.write_mutex);
+
+	kfree(bulk_reg);
+	return ret;
+}
+
+static int tavil_swrm_write(void *handle, int reg, int val)
+{
+	struct tavil_priv *tavil;
+	struct wcd9xxx *wcd9xxx;
+	unsigned short swr_wr_addr_base;
+	unsigned short swr_wr_data_base;
+	struct wcd9xxx_reg_val bulk_reg[2];
+	int ret;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	tavil = (struct tavil_priv *)handle;
+	wcd9xxx = tavil->wcd9xxx;
+
+	swr_wr_addr_base = WCD934X_SWR_AHB_BRIDGE_WR_ADDR_0;
+	swr_wr_data_base = WCD934X_SWR_AHB_BRIDGE_WR_DATA_0;
+
+	/* First Write the Data to register */
+	bulk_reg[0].reg = swr_wr_data_base;
+	bulk_reg[0].buf = (u8 *)(&val);
+	bulk_reg[0].bytes = 4;
+	bulk_reg[1].reg = swr_wr_addr_base;
+	bulk_reg[1].buf = (u8 *)(&reg);
+	bulk_reg[1].bytes = 4;
+
+	mutex_lock(&tavil->swr.write_mutex);
+	ret = wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2, false);
+	if (ret < 0)
+		dev_err(tavil->dev, "%s: WR Data Failure\n", __func__);
+	mutex_unlock(&tavil->swr.write_mutex);
+
+	return ret;
+}
+
+static int tavil_swrm_clock(void *handle, bool enable)
+{
+	struct tavil_priv *tavil;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	tavil = (struct tavil_priv *)handle;
+
+	mutex_lock(&tavil->swr.clk_mutex);
+	dev_dbg(tavil->dev, "%s: swrm clock %s\n",
+		__func__, (enable?"enable" : "disable"));
+	if (enable) {
+		tavil->swr.clk_users++;
+		if (tavil->swr.clk_users == 1) {
+			regmap_update_bits(tavil->wcd9xxx->regmap,
+					WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,
+					0x10, 0x00);
+			__tavil_cdc_mclk_enable(tavil, true);
+			regmap_update_bits(tavil->wcd9xxx->regmap,
+				WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+				0x01, 0x01);
+		}
+	} else {
+		tavil->swr.clk_users--;
+		if (tavil->swr.clk_users == 0) {
+			regmap_update_bits(tavil->wcd9xxx->regmap,
+				WCD934X_CDC_CLK_RST_CTRL_SWR_CONTROL,
+				0x01, 0x00);
+			__tavil_cdc_mclk_enable(tavil, false);
+			regmap_update_bits(tavil->wcd9xxx->regmap,
+					WCD934X_TEST_DEBUG_NPL_DLY_TEST_1,
+					0x10, 0x10);
+		}
+	}
+	dev_dbg(tavil->dev, "%s: swrm clock users %d\n",
+		__func__, tavil->swr.clk_users);
+	mutex_unlock(&tavil->swr.clk_mutex);
+
+	return 0;
+}
+
+static int tavil_swrm_handle_irq(void *handle,
+				 irqreturn_t (*swrm_irq_handler)(int irq,
+								 void *data),
+				 void *swrm_handle,
+				 int action)
+{
+	struct tavil_priv *tavil;
+	int ret = 0;
+	struct wcd9xxx *wcd9xxx;
+
+	if (!handle) {
+		pr_err("%s: NULL handle\n", __func__);
+		return -EINVAL;
+	}
+	tavil = (struct tavil_priv *) handle;
+	wcd9xxx = tavil->wcd9xxx;
+
+	if (action) {
+		ret = wcd9xxx_request_irq(&wcd9xxx->core_res,
+					  WCD934X_IRQ_SOUNDWIRE,
+					  swrm_irq_handler,
+					  "Tavil SWR Master", swrm_handle);
+		if (ret)
+			dev_err(tavil->dev, "%s: Failed to request irq %d\n",
+				__func__, WCD934X_IRQ_SOUNDWIRE);
+	} else
+		wcd9xxx_free_irq(&wcd9xxx->core_res, WCD934X_IRQ_SOUNDWIRE,
+				 swrm_handle);
+
+	return ret;
+}
+
+static void tavil_codec_add_spi_device(struct tavil_priv *tavil,
+				       struct device_node *node)
+{
+	struct spi_master *master;
+	struct spi_device *spi;
+	u32 prop_value;
+	int rc;
+
+	/* Read the master bus num from DT node */
+	rc = of_property_read_u32(node, "qcom,master-bus-num",
+				  &prop_value);
+	if (rc < 0) {
+		dev_err(tavil->dev, "%s: prop %s not found in node %s",
+			__func__, "qcom,master-bus-num", node->full_name);
+		goto done;
+	}
+
+	/* Get the reference to SPI master */
+	master = spi_busnum_to_master(prop_value);
+	if (!master) {
+		dev_err(tavil->dev, "%s: Invalid spi_master for bus_num %u\n",
+			__func__, prop_value);
+		goto done;
+	}
+
+	/* Allocate the spi device */
+	spi = spi_alloc_device(master);
+	if (!spi) {
+		dev_err(tavil->dev, "%s: spi_alloc_device failed\n",
+			__func__);
+		goto err_spi_alloc_dev;
+	}
+
+	/* Initialize device properties */
+	if (of_modalias_node(node, spi->modalias,
+			     sizeof(spi->modalias)) < 0) {
+		dev_err(tavil->dev, "%s: cannot find modalias for %s\n",
+			__func__, node->full_name);
+		goto err_dt_parse;
+	}
+
+	rc = of_property_read_u32(node, "qcom,chip-select",
+				  &prop_value);
+	if (rc < 0) {
+		dev_err(tavil->dev, "%s: prop %s not found in node %s",
+			__func__, "qcom,chip-select", node->full_name);
+		goto err_dt_parse;
+	}
+	spi->chip_select = prop_value;
+
+	rc = of_property_read_u32(node, "qcom,max-frequency",
+				  &prop_value);
+	if (rc < 0) {
+		dev_err(tavil->dev, "%s: prop %s not found in node %s",
+			__func__, "qcom,max-frequency", node->full_name);
+		goto err_dt_parse;
+	}
+	spi->max_speed_hz = prop_value;
+
+	spi->dev.of_node = node;
+
+	rc = spi_add_device(spi);
+	if (rc < 0) {
+		dev_err(tavil->dev, "%s: spi_add_device failed\n", __func__);
+		goto err_dt_parse;
+	}
+
+	/* Put the reference to SPI master */
+	put_device(&master->dev);
+
+	return;
+
+err_dt_parse:
+	spi_dev_put(spi);
+
+err_spi_alloc_dev:
+	/* Put the reference to SPI master */
+	put_device(&master->dev);
+done:
+	return;
+}
+
+static void tavil_add_child_devices(struct work_struct *work)
+{
+	struct tavil_priv *tavil;
+	struct platform_device *pdev;
+	struct device_node *node;
+	struct wcd9xxx *wcd9xxx;
+	struct tavil_swr_ctrl_data *swr_ctrl_data = NULL, *temp;
+	int ret, ctrl_num = 0;
+	struct wcd_swr_ctrl_platform_data *platdata;
+	char plat_dev_name[WCD934X_STRING_LEN];
+
+	tavil = container_of(work, struct tavil_priv,
+			     tavil_add_child_devices_work);
+	if (!tavil) {
+		pr_err("%s: Memory for WCD934X does not exist\n",
+			__func__);
+		return;
+	}
+	wcd9xxx = tavil->wcd9xxx;
+	if (!wcd9xxx) {
+		pr_err("%s: Memory for WCD9XXX does not exist\n",
+			__func__);
+		return;
+	}
+	if (!wcd9xxx->dev->of_node) {
+		dev_err(wcd9xxx->dev, "%s: DT node for wcd9xxx does not exist\n",
+			__func__);
+		return;
+	}
+
+	platdata = &tavil->swr.plat_data;
+
+	for_each_child_of_node(wcd9xxx->dev->of_node, node) {
+
+		/* Parse and add the SPI device node */
+		if (!strcmp(node->name, "wcd_spi")) {
+			tavil_codec_add_spi_device(tavil, node);
+			continue;
+		}
+
+		/* Parse other child device nodes and add platform device */
+		if (!strcmp(node->name, "swr_master"))
+			strlcpy(plat_dev_name, "tavil_swr_ctrl",
+				(WCD934X_STRING_LEN - 1));
+		else if (strnstr(node->name, "msm_cdc_pinctrl",
+				 strlen("msm_cdc_pinctrl")) != NULL)
+			strlcpy(plat_dev_name, node->name,
+				(WCD934X_STRING_LEN - 1));
+		else
+			continue;
+
+		pdev = platform_device_alloc(plat_dev_name, -1);
+		if (!pdev) {
+			dev_err(wcd9xxx->dev, "%s: pdev memory alloc failed\n",
+				__func__);
+			ret = -ENOMEM;
+			goto err_mem;
+		}
+		pdev->dev.parent = tavil->dev;
+		pdev->dev.of_node = node;
+
+		if (strcmp(node->name, "swr_master") == 0) {
+			ret = platform_device_add_data(pdev, platdata,
+						       sizeof(*platdata));
+			if (ret) {
+				dev_err(&pdev->dev,
+					"%s: cannot add plat data ctrl:%d\n",
+					__func__, ctrl_num);
+				goto err_pdev_add;
+			}
+		}
+
+		ret = platform_device_add(pdev);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: Cannot add platform device\n",
+				__func__);
+			goto err_pdev_add;
+		}
+
+		if (strcmp(node->name, "swr_master") == 0) {
+			temp = krealloc(swr_ctrl_data,
+					(ctrl_num + 1) * sizeof(
+					struct tavil_swr_ctrl_data),
+					GFP_KERNEL);
+			if (!temp) {
+				dev_err(wcd9xxx->dev, "out of memory\n");
+				ret = -ENOMEM;
+				goto err_pdev_add;
+			}
+			swr_ctrl_data = temp;
+			swr_ctrl_data[ctrl_num].swr_pdev = pdev;
+			ctrl_num++;
+			dev_dbg(&pdev->dev,
+				"%s: Added soundwire ctrl device(s)\n",
+				__func__);
+			tavil->swr.ctrl_data = swr_ctrl_data;
+		}
+	}
+
+	return;
+
+err_pdev_add:
+	platform_device_put(pdev);
+err_mem:
+	return;
+}
+
+static int __tavil_enable_efuse_sensing(struct tavil_priv *tavil)
+{
+	int val, rc;
+
+	__tavil_cdc_mclk_enable(tavil, true);
+
+	regmap_update_bits(tavil->wcd9xxx->regmap,
+			WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x1E, 0x10);
+	regmap_update_bits(tavil->wcd9xxx->regmap,
+			WCD934X_CHIP_TIER_CTRL_EFUSE_CTL, 0x01, 0x01);
+
+	/*
+	 * 5ms sleep required after enabling efuse control
+	 * before checking the status.
+	 */
+	usleep_range(5000, 5500);
+	rc = regmap_read(tavil->wcd9xxx->regmap,
+			 WCD934X_CHIP_TIER_CTRL_EFUSE_STATUS, &val);
+	if (rc || (!(val & 0x01)))
+		WARN(1, "%s: Efuse sense is not complete val=%x, ret=%d\n",
+			__func__, val, rc);
+
+	__tavil_cdc_mclk_enable(tavil, false);
+
+	return rc;
+}
+
+static void ___tavil_get_codec_fine_version(struct tavil_priv *tavil)
+{
+	int val1, val2, version;
+	struct regmap *regmap;
+	u16 id_minor;
+	u32 version_mask = 0;
+
+	regmap = tavil->wcd9xxx->regmap;
+	version = tavil->wcd9xxx->version;
+	id_minor = tavil->wcd9xxx->codec_type->id_minor;
+
+	regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT14, &val1);
+	regmap_read(regmap, WCD934X_CHIP_TIER_CTRL_EFUSE_VAL_OUT15, &val2);
+
+	dev_dbg(tavil->dev, "%s: chip version :0x%x 0x:%x\n",
+		__func__, val1, val2);
+
+	version_mask |= (!!((u8)val1 & 0x80)) << DSD_DISABLED_MASK;
+	version_mask |= (!!((u8)val2 & 0x01)) << SLNQ_DISABLED_MASK;
+
+	switch (version_mask) {
+	case DSD_DISABLED | SLNQ_DISABLED:
+		if (id_minor == cpu_to_le16(0))
+			version = TAVIL_VERSION_WCD9340_1_0;
+		else if (id_minor == cpu_to_le16(0x01))
+			version = TAVIL_VERSION_WCD9340_1_1;
+		break;
+	case SLNQ_DISABLED:
+		if (id_minor == cpu_to_le16(0))
+			version = TAVIL_VERSION_WCD9341_1_0;
+		else if (id_minor == cpu_to_le16(0x01))
+			version = TAVIL_VERSION_WCD9341_1_1;
+		break;
+	}
+
+	tavil->wcd9xxx->version = version;
+	tavil->wcd9xxx->codec_type->version = version;
+}
+
+/*
+ * tavil_get_wcd_dsp_cntl: Get the reference to wcd_dsp_cntl
+ * @dev: Device pointer for codec device
+ *
+ * This API gets the reference to codec's struct wcd_dsp_cntl
+ */
+struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct tavil_priv *tavil;
+
+	if (!dev) {
+		pr_err("%s: Invalid device\n", __func__);
+		return NULL;
+	}
+
+	pdev = to_platform_device(dev);
+	tavil = platform_get_drvdata(pdev);
+
+	return tavil->wdsp_cntl;
+}
+EXPORT_SYMBOL(tavil_get_wcd_dsp_cntl);
+
+static int tavil_probe(struct platform_device *pdev)
+{
+	int ret = 0;
+	struct tavil_priv *tavil;
+	struct clk *wcd_ext_clk;
+	struct wcd9xxx_resmgr_v2 *resmgr;
+	struct wcd9xxx_power_region *cdc_pwr;
+
+	tavil = devm_kzalloc(&pdev->dev, sizeof(struct tavil_priv),
+			    GFP_KERNEL);
+	if (!tavil)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, tavil);
+
+	tavil->wcd9xxx = dev_get_drvdata(pdev->dev.parent);
+	tavil->dev = &pdev->dev;
+	INIT_DELAYED_WORK(&tavil->power_gate_work, tavil_codec_power_gate_work);
+	mutex_init(&tavil->power_lock);
+	INIT_WORK(&tavil->tavil_add_child_devices_work,
+		  tavil_add_child_devices);
+	mutex_init(&tavil->micb_lock);
+	mutex_init(&tavil->swr.read_mutex);
+	mutex_init(&tavil->swr.write_mutex);
+	mutex_init(&tavil->swr.clk_mutex);
+	mutex_init(&tavil->codec_mutex);
+	mutex_init(&tavil->svs_mutex);
+
+	/*
+	 * Codec hardware by default comes up in SVS mode.
+	 * Initialize the svs_ref_cnt to 1 to reflect the hardware
+	 * state in the driver.
+	 */
+	tavil->svs_ref_cnt = 1;
+
+	cdc_pwr = devm_kzalloc(&pdev->dev, sizeof(struct wcd9xxx_power_region),
+				GFP_KERNEL);
+	if (!cdc_pwr) {
+		ret = -ENOMEM;
+		goto err_resmgr;
+	}
+	tavil->wcd9xxx->wcd9xxx_pwr[WCD9XXX_DIG_CORE_REGION_1] = cdc_pwr;
+	cdc_pwr->pwr_collapse_reg_min = WCD934X_DIG_CORE_REG_MIN;
+	cdc_pwr->pwr_collapse_reg_max = WCD934X_DIG_CORE_REG_MAX;
+	wcd9xxx_set_power_state(tavil->wcd9xxx,
+				WCD_REGION_POWER_COLLAPSE_REMOVE,
+				WCD9XXX_DIG_CORE_REGION_1);
+	/*
+	 * Init resource manager so that if child nodes such as SoundWire
+	 * requests for clock, resource manager can honor the request
+	 */
+	resmgr = wcd_resmgr_init(&tavil->wcd9xxx->core_res, NULL);
+	if (IS_ERR(resmgr)) {
+		ret = PTR_ERR(resmgr);
+		dev_err(&pdev->dev, "%s: Failed to initialize wcd resmgr\n",
+			__func__);
+		goto err_resmgr;
+	}
+	tavil->resmgr = resmgr;
+	tavil->swr.plat_data.handle = (void *) tavil;
+	tavil->swr.plat_data.read = tavil_swrm_read;
+	tavil->swr.plat_data.write = tavil_swrm_write;
+	tavil->swr.plat_data.bulk_write = tavil_swrm_bulk_write;
+	tavil->swr.plat_data.clk = tavil_swrm_clock;
+	tavil->swr.plat_data.handle_irq = tavil_swrm_handle_irq;
+	tavil->swr.spkr_gain_offset = WCD934X_RX_GAIN_OFFSET_0_DB;
+
+	/* Register for Clock */
+	wcd_ext_clk = clk_get(tavil->wcd9xxx->dev, "wcd_clk");
+	if (IS_ERR(wcd_ext_clk)) {
+		dev_err(tavil->wcd9xxx->dev, "%s: clk get %s failed\n",
+			__func__, "wcd_ext_clk");
+		goto err_clk;
+	}
+	tavil->wcd_ext_clk = wcd_ext_clk;
+	set_bit(AUDIO_NOMINAL, &tavil->status_mask);
+	/* Update codec register default values */
+	dev_dbg(&pdev->dev, "%s: MCLK Rate = %x\n", __func__,
+		tavil->wcd9xxx->mclk_rate);
+	if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_12P288MHZ)
+		regmap_update_bits(tavil->wcd9xxx->regmap,
+				   WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+				   0x03, 0x00);
+	else if (tavil->wcd9xxx->mclk_rate == WCD934X_MCLK_CLK_9P6MHZ)
+		regmap_update_bits(tavil->wcd9xxx->regmap,
+				   WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+				   0x03, 0x01);
+	tavil_update_reg_defaults(tavil);
+	__tavil_enable_efuse_sensing(tavil);
+	___tavil_get_codec_fine_version(tavil);
+	tavil_update_cpr_defaults(tavil);
+
+	/* Register with soc framework */
+	ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_tavil,
+				  tavil_dai, ARRAY_SIZE(tavil_dai));
+	if (ret) {
+		dev_err(&pdev->dev, "%s: Codec registration failed\n",
+		 __func__);
+		goto err_cdc_reg;
+	}
+	schedule_work(&tavil->tavil_add_child_devices_work);
+
+	return ret;
+
+err_cdc_reg:
+	clk_put(tavil->wcd_ext_clk);
+err_clk:
+	wcd_resmgr_remove(tavil->resmgr);
+err_resmgr:
+	mutex_destroy(&tavil->micb_lock);
+	mutex_destroy(&tavil->svs_mutex);
+	mutex_destroy(&tavil->codec_mutex);
+	mutex_destroy(&tavil->swr.read_mutex);
+	mutex_destroy(&tavil->swr.write_mutex);
+	mutex_destroy(&tavil->swr.clk_mutex);
+	devm_kfree(&pdev->dev, tavil);
+
+	return ret;
+}
+
+static int tavil_remove(struct platform_device *pdev)
+{
+	struct tavil_priv *tavil;
+
+	tavil = platform_get_drvdata(pdev);
+	if (!tavil)
+		return -EINVAL;
+
+	mutex_destroy(&tavil->micb_lock);
+	mutex_destroy(&tavil->svs_mutex);
+	mutex_destroy(&tavil->codec_mutex);
+	mutex_destroy(&tavil->swr.read_mutex);
+	mutex_destroy(&tavil->swr.write_mutex);
+	mutex_destroy(&tavil->swr.clk_mutex);
+
+	snd_soc_unregister_codec(&pdev->dev);
+	clk_put(tavil->wcd_ext_clk);
+	wcd_resmgr_remove(tavil->resmgr);
+	if (tavil->dsd_config) {
+		tavil_dsd_deinit(tavil->dsd_config);
+		tavil->dsd_config = NULL;
+	}
+	devm_kfree(&pdev->dev, tavil);
+	return 0;
+}
+
+static struct platform_driver tavil_codec_driver = {
+	.probe = tavil_probe,
+	.remove = tavil_remove,
+	.driver = {
+		.name = "tavil_codec",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM
+		.pm = &tavil_pm_ops,
+#endif
+	},
+};
+
+module_platform_driver(tavil_codec_driver);
+
+MODULE_DESCRIPTION("Tavil Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd934x/wcd934x.h b/sound/soc/codecs/wcd934x/wcd934x.h
new file mode 100644
index 0000000..ae70175
--- /dev/null
+++ b/sound/soc/codecs/wcd934x/wcd934x.h
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef WCD934X_H
+#define WCD934X_H
+
+#include <sound/apr_audio-v2.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-slimslave.h>
+#include "wcd934x-dsp-cntl.h"
+#include "../wcd9xxx-common-v2.h"
+#include "../wcd-mbhc-v2.h"
+
+#define WCD934X_REGISTER_START_OFFSET  0x800
+#define WCD934X_SB_PGD_PORT_RX_BASE   0x40
+#define WCD934X_SB_PGD_PORT_TX_BASE   0x50
+#define WCD934X_RX_PORT_START_NUMBER  16
+
+#define WCD934X_DMIC_CLK_DIV_2  0x0
+#define WCD934X_DMIC_CLK_DIV_3  0x1
+#define WCD934X_DMIC_CLK_DIV_4  0x2
+#define WCD934X_DMIC_CLK_DIV_6  0x3
+#define WCD934X_DMIC_CLK_DIV_8  0x4
+#define WCD934X_DMIC_CLK_DIV_16  0x5
+#define WCD934X_DMIC_CLK_DRIVE_DEFAULT 0x02
+
+#define WCD934X_ANC_DMIC_X2_FULL_RATE 1
+#define WCD934X_ANC_DMIC_X2_HALF_RATE 0
+
+#define TAVIL_MAX_MICBIAS 4
+#define TAVIL_NUM_INTERPOLATORS 9
+#define MAX_ON_DEMAND_SUPPLY_NAME_LENGTH    64
+
+/* Convert from vout ctl to micbias voltage in mV */
+#define  WCD_VOUT_CTL_TO_MICB(v)  (1000 + v * 50)
+
+/* Feature masks to distinguish codec version */
+#define DSD_DISABLED_MASK   0
+#define SLNQ_DISABLED_MASK  1
+
+#define DSD_DISABLED   (1 << DSD_DISABLED_MASK)
+#define SLNQ_DISABLED  (1 << SLNQ_DISABLED_MASK)
+
+/* Number of input and output Slimbus port */
+enum {
+	WCD934X_RX0 = 0,
+	WCD934X_RX1,
+	WCD934X_RX2,
+	WCD934X_RX3,
+	WCD934X_RX4,
+	WCD934X_RX5,
+	WCD934X_RX6,
+	WCD934X_RX7,
+	WCD934X_RX_MAX,
+};
+
+enum {
+	WCD934X_TX0 = 0,
+	WCD934X_TX1,
+	WCD934X_TX2,
+	WCD934X_TX3,
+	WCD934X_TX4,
+	WCD934X_TX5,
+	WCD934X_TX6,
+	WCD934X_TX7,
+	WCD934X_TX8,
+	WCD934X_TX9,
+	WCD934X_TX10,
+	WCD934X_TX11,
+	WCD934X_TX12,
+	WCD934X_TX13,
+	WCD934X_TX14,
+	WCD934X_TX15,
+	WCD934X_TX_MAX,
+};
+
+enum {
+	INTERP_EAR = 0,
+	INTERP_HPHL,
+	INTERP_HPHR,
+	INTERP_LO1,
+	INTERP_LO2,
+	INTERP_LO3_NA, /* LO3 not avalible in Tavil*/
+	INTERP_LO4_NA,
+	INTERP_SPKR1,
+	INTERP_SPKR2,
+	INTERP_MAX,
+};
+
+enum {
+	/* INTR_REG 0 */
+	WCD934X_IRQ_MISC = 1,
+	WCD934X_IRQ_HPH_PA_OCPL_FAULT,
+	WCD934X_IRQ_HPH_PA_OCPR_FAULT,
+	WCD934X_IRQ_EAR_PA_OCP_FAULT,
+	WCD934X_IRQ_HPH_PA_CNPL_COMPLETE,
+	WCD934X_IRQ_HPH_PA_CNPR_COMPLETE,
+	WCD934X_IRQ_EAR_PA_CNP_COMPLETE,
+	/* INTR_REG 1 */
+	WCD934X_IRQ_MBHC_SW_DET,
+	WCD934X_IRQ_MBHC_ELECT_INS_REM_DET,
+	WCD934X_IRQ_MBHC_BUTTON_PRESS_DET,
+	WCD934X_IRQ_MBHC_BUTTON_RELEASE_DET,
+	WCD934X_IRQ_MBHC_ELECT_INS_REM_LEG_DET,
+	WCD934X_IRQ_RESERVED_0,
+	WCD934X_IRQ_RESERVED_1,
+	WCD934X_IRQ_RESERVED_2,
+	/* INTR_REG 2 */
+	WCD934X_IRQ_LINE_PA1_CNP_COMPLETE,
+	WCD934X_IRQ_LINE_PA2_CNP_COMPLETE,
+	WCD934X_IRQ_SLNQ_ANALOG_ERROR,
+	WCD934X_IRQ_RESERVED_3,
+	WCD934X_IRQ_SOUNDWIRE,
+	WCD934X_IRQ_VDD_DIG_RAMP_COMPLETE,
+	WCD934X_IRQ_RCO_ERROR,
+	WCD934X_IRQ_CPE_ERROR,
+	/* INTR_REG 3 */
+	WCD934X_IRQ_MAD_AUDIO,
+	WCD934X_IRQ_MAD_BEACON,
+	WCD934X_IRQ_MAD_ULTRASOUND,
+	WCD934X_IRQ_VBAT_ATTACK,
+	WCD934X_IRQ_VBAT_RESTORE,
+	WCD934X_IRQ_CPE1_INTR,
+	WCD934X_IRQ_RESERVED_4,
+	WCD934X_IRQ_SLNQ_DIGITAL,
+	WCD934X_NUM_IRQS,
+};
+
+/*
+ * Selects compander and smart boost settings
+ * for a given speaker mode
+ */
+enum {
+	WCD934X_SPKR_MODE_DEFAULT,
+	WCD934X_SPKR_MODE_1, /* COMP Gain = 12dB, Smartboost Max = 5.5V */
+};
+
+/*
+ * Rx path gain offsets
+ */
+enum {
+	WCD934X_RX_GAIN_OFFSET_M1P5_DB,
+	WCD934X_RX_GAIN_OFFSET_0_DB,
+};
+
+/*
+ * Dai data structure holds the
+ * dai specific info like rate,
+ * channel number etc.
+ */
+struct tavil_codec_dai_data {
+	u32 rate;
+	u32 *ch_num;
+	u32 ch_act;
+	u32 ch_tot;
+};
+
+/*
+ * Structure used to update codec
+ * register defaults after reset
+ */
+struct tavil_reg_mask_val {
+	u16 reg;
+	u8 mask;
+	u8 val;
+};
+
+extern void *tavil_get_afe_config(struct snd_soc_codec *codec,
+				  enum afe_config_type config_type);
+extern int tavil_cdc_mclk_enable(struct snd_soc_codec *codec, bool enable);
+extern int tavil_set_spkr_mode(struct snd_soc_codec *codec, int mode);
+extern int tavil_set_spkr_gain_offset(struct snd_soc_codec *codec, int offset);
+extern struct wcd_dsp_cntl *tavil_get_wcd_dsp_cntl(struct device *dev);
+extern int wcd934x_get_micb_vout_ctl_val(u32 micb_mv);
+extern int tavil_micbias_control(struct snd_soc_codec *codec,
+				 int micb_num,
+				 int req, bool is_dapm);
+extern int tavil_mbhc_micb_adjust_voltage(struct snd_soc_codec *codec,
+					  int req_volt,
+					  int micb_num);
+extern struct wcd934x_mbhc *tavil_soc_get_mbhc(struct snd_soc_codec *codec);
+extern int tavil_codec_enable_interp_clk(struct snd_soc_codec *codec,
+					 int event, int intp_idx);
+extern struct tavil_dsd_config *tavil_get_dsd_config(
+				struct snd_soc_codec *codec);
+extern int tavil_codec_info_create_codec_entry(
+				struct snd_info_entry *codec_root,
+				struct snd_soc_codec *codec);
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.c b/sound/soc/codecs/wcd9xxx-common-v2.c
new file mode 100644
index 0000000..ad62d18
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common-v2.c
@@ -0,0 +1,1270 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include "wcd9xxx-common-v2.h"
+
+#define WCD_USLEEP_RANGE 50
+#define MAX_IMPED_PARAMS 6
+
+enum {
+	DAC_GAIN_0DB = 0,
+	DAC_GAIN_0P2DB,
+	DAC_GAIN_0P4DB,
+	DAC_GAIN_0P6DB,
+	DAC_GAIN_0P8DB,
+	DAC_GAIN_M0P2DB,
+	DAC_GAIN_M0P4DB,
+	DAC_GAIN_M0P6DB,
+};
+
+enum {
+	VREF_FILT_R_0OHM = 0,
+	VREF_FILT_R_25KOHM,
+	VREF_FILT_R_50KOHM,
+	VREF_FILT_R_100KOHM,
+};
+
+enum {
+	DELTA_I_0MA,
+	DELTA_I_10MA,
+	DELTA_I_20MA,
+	DELTA_I_30MA,
+	DELTA_I_40MA,
+	DELTA_I_50MA,
+};
+
+struct wcd_imped_val {
+	u32 imped_val;
+	u8 index;
+};
+
+static const struct wcd_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = {
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf5},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf5},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf5},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf5},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf7},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf7},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf7},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf7},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xf9},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xf9},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x0},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xf9},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xf9},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x0},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfa},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfa},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfa},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfa},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfb},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfb},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfb},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfb},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfc},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfc},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfc},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfc},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfd},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfd},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfd},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfd},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xfe},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xfe},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x01},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xfe},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xfe},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x01},
+	},
+	{
+		{WCD9XXX_CDC_RX1_RX_VOL_CTL, 0xff, 0xff},
+		{WCD9XXX_CDC_RX1_RX_VOL_MIX_CTL, 0xff, 0xff},
+		{WCD9XXX_CDC_RX1_RX_PATH_SEC1, 0x01, 0x00},
+		{WCD9XXX_CDC_RX2_RX_VOL_CTL, 0xff, 0xff},
+		{WCD9XXX_CDC_RX2_RX_VOL_MIX_CTL, 0xff, 0xff},
+		{WCD9XXX_CDC_RX2_RX_PATH_SEC1, 0x01, 0x00},
+	},
+};
+
+static const struct wcd_imped_val imped_index[] = {
+	{4, 0},
+	{5, 1},
+	{6, 2},
+	{7, 3},
+	{8, 4},
+	{9, 5},
+	{10, 6},
+	{11, 7},
+	{12, 8},
+	{13, 9},
+};
+
+static void (*clsh_state_fp[NUM_CLSH_STATES_V2])(struct snd_soc_codec *,
+					      struct wcd_clsh_cdc_data *,
+					      u8 req_state, bool en, int mode);
+
+static int get_impedance_index(int imped)
+{
+	int i = 0;
+
+	if (imped < imped_index[i].imped_val) {
+		pr_debug("%s, detected impedance is less than 4 Ohm\n",
+				__func__);
+		i = 0;
+		goto ret;
+	}
+	if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) {
+		pr_debug("%s, detected impedance is greater than 12 Ohm\n",
+				__func__);
+		i = ARRAY_SIZE(imped_index) - 1;
+		goto ret;
+	}
+	for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) {
+		if (imped >= imped_index[i].imped_val &&
+			imped < imped_index[i + 1].imped_val)
+			break;
+	}
+ret:
+	pr_debug("%s: selected impedance index = %d\n",
+			__func__, imped_index[i].index);
+	return imped_index[i].index;
+}
+
+/*
+ * Function: wcd_clsh_imped_config
+ * Params: codec, imped, reset
+ * Description:
+ * This function updates HPHL and HPHR gain settings
+ * according to the impedance value.
+ */
+void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped, bool reset)
+{
+	int i;
+	int index = 0;
+
+	/* reset = 1, which means request is to reset the register values */
+	if (reset) {
+		for (i = 0; i < MAX_IMPED_PARAMS; i++)
+			snd_soc_update_bits(codec, imped_table[index][i].reg,
+				imped_table[index][i].mask, 0);
+		return;
+	}
+	index = get_impedance_index(imped);
+	if (index >= (ARRAY_SIZE(imped_index) - 1)) {
+		pr_debug("%s, impedance not in range = %d\n", __func__, imped);
+		return;
+	}
+	if (index >= ARRAY_SIZE(imped_table)) {
+		pr_debug("%s, impedance index not in range = %d\n", __func__,
+			index);
+		return;
+	}
+	for (i = 0; i < MAX_IMPED_PARAMS; i++)
+		snd_soc_update_bits(codec, imped_table[index][i].reg,
+				imped_table[index][i].mask,
+				imped_table[index][i].val);
+}
+EXPORT_SYMBOL(wcd_clsh_imped_config);
+
+static bool is_native_44_1_active(struct snd_soc_codec *codec)
+{
+	bool native_active = false;
+	u8 native_clk, rx1_rate, rx2_rate;
+
+	native_clk = snd_soc_read(codec,
+				 WCD9XXX_CDC_CLK_RST_CTRL_MCLK_CONTROL);
+	rx1_rate = snd_soc_read(codec, WCD9XXX_CDC_RX1_RX_PATH_CTL);
+	rx2_rate = snd_soc_read(codec, WCD9XXX_CDC_RX2_RX_PATH_CTL);
+
+	dev_dbg(codec->dev, "%s: native_clk %x rx1_rate= %x rx2_rate= %x",
+		__func__, native_clk, rx1_rate, rx2_rate);
+
+	if ((native_clk & 0x2) &&
+	    ((rx1_rate & 0x0F) == 0x9 || (rx2_rate & 0x0F) == 0x9))
+		native_active = true;
+
+	return native_active;
+}
+
+static const char *mode_to_str(int mode)
+{
+	switch (mode) {
+	case CLS_H_NORMAL:
+		return "CLS_H_NORMAL";
+	case CLS_H_HIFI:
+		return "CLS_H_HIFI";
+	case CLS_H_LOHIFI:
+		return "CLS_H_LOHIFI";
+	case CLS_H_LP:
+		return "CLS_H_LP";
+	case CLS_H_ULP:
+		return "CLS_H_ULP";
+	case CLS_AB:
+		return "CLS_AB";
+	case CLS_AB_HIFI:
+		return "CLS_AB_HIFI";
+	default:
+		return "CLS_H_INVALID";
+	};
+}
+
+static const char *state_to_str(u8 state, char *buf, size_t buflen)
+{
+	int i;
+	int cnt = 0;
+	/*
+	 * This array of strings should match with enum wcd_clsh_state_bit.
+	 */
+	static const char *const states[] = {
+		"STATE_EAR",
+		"STATE_HPH_L",
+		"STATE_HPH_R",
+		"STATE_LO",
+	};
+
+	if (state == WCD_CLSH_STATE_IDLE) {
+		snprintf(buf, buflen, "[STATE_IDLE]");
+		goto done;
+	}
+
+	buf[0] = '\0';
+	for (i = 0; i < ARRAY_SIZE(states); i++) {
+		if (!(state & (1 << i)))
+			continue;
+		cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
+			       buf[0] == '\0' ? "[" : "|",
+			       states[i]);
+	}
+	if (cnt > 0)
+		strlcat(buf + cnt, "]", buflen);
+
+done:
+	if (buf[0] == '\0')
+		snprintf(buf, buflen, "[STATE_UNKNOWN]");
+	return buf;
+}
+
+static inline void
+wcd_enable_clsh_block(struct snd_soc_codec *codec,
+		      struct wcd_clsh_cdc_data *clsh_d, bool enable)
+{
+	if ((enable && ++clsh_d->clsh_users == 1) ||
+	    (!enable && --clsh_d->clsh_users == 0))
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_CRC, 0x01,
+				    (u8) enable);
+	if (clsh_d->clsh_users < 0)
+		clsh_d->clsh_users = 0;
+	dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__,
+		clsh_d->clsh_users, enable);
+}
+
+static inline bool wcd_clsh_enable_status(struct snd_soc_codec *codec)
+{
+	return snd_soc_read(codec, WCD9XXX_A_CDC_CLSH_CRC) & 0x01;
+}
+
+static inline int wcd_clsh_get_int_mode(struct wcd_clsh_cdc_data *clsh_d,
+					int clsh_state)
+{
+	int mode;
+
+	if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHL) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHR) &&
+	    (clsh_state != WCD_CLSH_STATE_LO))
+		mode = CLS_NONE;
+	else
+		mode = clsh_d->interpolator_modes[ffs(clsh_state)];
+
+	return mode;
+}
+
+static inline void wcd_clsh_set_int_mode(struct wcd_clsh_cdc_data *clsh_d,
+					int clsh_state, int mode)
+{
+	if ((clsh_state != WCD_CLSH_STATE_EAR) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHL) &&
+	    (clsh_state != WCD_CLSH_STATE_HPHR) &&
+	    (clsh_state != WCD_CLSH_STATE_LO))
+		return;
+
+	clsh_d->interpolator_modes[ffs(clsh_state)] = mode;
+}
+
+static inline void wcd_clsh_set_buck_mode(struct snd_soc_codec *codec,
+					  int mode)
+{
+	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+	    mode == CLS_AB_HIFI || mode == CLS_AB)
+		snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+				    0x08, 0x08); /* set to HIFI */
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+				    0x08, 0x00); /* set to default */
+}
+
+static inline void wcd_clsh_set_flyback_mode(struct snd_soc_codec *codec,
+					     int mode)
+{
+	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+	    mode == CLS_AB_HIFI || mode == CLS_AB)
+		snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+				    0x04, 0x04); /* set to HIFI */
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+				    0x04, 0x00); /* set to Default */
+}
+
+static inline void wcd_clsh_gm3_boost_disable(struct snd_soc_codec *codec,
+					      int mode)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!IS_CODEC_TYPE(wcd9xxx, WCD934X))
+		return;
+
+	if (mode == CLS_H_HIFI || mode == CLS_H_LOHIFI ||
+	    mode == CLS_AB_HIFI || mode == CLS_AB) {
+		if (TAVIL_IS_1_0(wcd9xxx))
+			snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL,
+					    0x80, 0x0); /* disable GM3 Boost */
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+				    0xF0, 0x80);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_HPH_CNP_WG_CTL,
+				    0x80, 0x80); /* set to Default */
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_4,
+				    0xF0, 0x70);
+	}
+}
+
+
+static inline void wcd_clsh_force_iq_ctl(struct snd_soc_codec *codec,
+					 int mode)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!IS_CODEC_TYPE(wcd9xxx, WCD934X))
+		return;
+
+	if (mode == CLS_H_LOHIFI || mode == CLS_AB) {
+		snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+				    0x20, 0x20);
+		snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+				    0xF0, 0xC0);
+		snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+				    0x0E, 0x02);
+	} else {
+
+		snd_soc_update_bits(codec, WCD9XXX_HPH_NEW_INT_PA_MISC2,
+				    0x20, 0x0);
+		snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_HPH_LOWPOWER,
+				    0xF0, 0x80);
+		snd_soc_update_bits(codec, WCD9XXX_HPH_PA_CTL1,
+				    0x0E, 0x06);
+	}
+}
+
+static void wcd_clsh_buck_ctrl(struct snd_soc_codec *codec,
+			       struct wcd_clsh_cdc_data *clsh_d,
+			       int mode,
+			       bool enable)
+{
+	/* enable/disable buck */
+	if ((enable && (++clsh_d->buck_users == 1)) ||
+	   (!enable && (--clsh_d->buck_users == 0)))
+		snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+				    (1 << 7), (enable << 7));
+	dev_dbg(codec->dev, "%s: buck_users %d, enable %d, mode: %s",
+		__func__, clsh_d->buck_users, enable, mode_to_str(mode));
+	/*
+	 * 500us sleep is required after buck enable/disable
+	 * as per HW requirement
+	 */
+	usleep_range(500, 500 + WCD_USLEEP_RANGE);
+}
+
+static void wcd_clsh_flyback_ctrl(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_data *clsh_d,
+				  int mode,
+				  bool enable)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_reg_val bulk_reg[2];
+	u8 vneg[] = {0x00, 0x40};
+
+	/* enable/disable flyback */
+	if ((enable && (++clsh_d->flyback_users == 1)) ||
+	   (!enable && (--clsh_d->flyback_users == 0))) {
+		snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+				    (1 << 6), (enable << 6));
+		/* 100usec delay is needed as per HW requirement */
+		usleep_range(100, 110);
+		if (enable && (TASHA_IS_1_1(wcd9xxx))) {
+			wcd_clsh_set_flyback_mode(codec, CLS_H_HIFI);
+			snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
+					    0x60, 0x40);
+			snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
+					    0x10, 0x10);
+			vneg[0] = snd_soc_read(codec,
+					       WCD9XXX_A_ANA_RX_SUPPLIES);
+			vneg[0] &= ~(0x40);
+			vneg[1] = vneg[0] | 0x40;
+			bulk_reg[0].reg = WCD9XXX_A_ANA_RX_SUPPLIES;
+			bulk_reg[0].buf = &vneg[0];
+			bulk_reg[0].bytes = 1;
+			bulk_reg[1].reg = WCD9XXX_A_ANA_RX_SUPPLIES;
+			bulk_reg[1].buf = &vneg[1];
+			bulk_reg[1].bytes = 1;
+			/* 500usec delay is needed as per HW requirement */
+			usleep_range(500, 510);
+			wcd9xxx_slim_bulk_write(wcd9xxx, bulk_reg, 2,
+						false);
+			snd_soc_update_bits(codec, WCD9XXX_FLYBACK_EN,
+					    0x10, 0x00);
+			wcd_clsh_set_flyback_mode(codec, mode);
+		}
+
+	}
+	dev_dbg(codec->dev, "%s: flyback_users %d, enable %d, mode: %s",
+		__func__, clsh_d->flyback_users, enable, mode_to_str(mode));
+	/*
+	 * 500us sleep is required after flyback enable/disable
+	 * as per HW requirement
+	 */
+	usleep_range(500, 500 + WCD_USLEEP_RANGE);
+}
+
+static void wcd_clsh_set_gain_path(struct snd_soc_codec *codec,
+				   int mode)
+{
+	u8 val = 0;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!TASHA_IS_2_0(wcd9xxx))
+		return;
+
+	switch (mode) {
+	case CLS_H_NORMAL:
+	case CLS_AB:
+		val = 0x00;
+		break;
+	case CLS_H_HIFI:
+		val = 0x02;
+		break;
+	case CLS_H_LP:
+		val = 0x01;
+		break;
+	default:
+		return;
+	};
+	snd_soc_update_bits(codec, WCD9XXX_HPH_L_EN, 0xC0, (val << 6));
+	snd_soc_update_bits(codec, WCD9XXX_HPH_R_EN, 0xC0, (val << 6));
+}
+
+static void wcd_clsh_set_hph_mode(struct snd_soc_codec *codec,
+				  int mode)
+{
+	u8 val = 0;
+	u8 gain = 0;
+	u8 res_val = VREF_FILT_R_0OHM;
+	u8 ipeak = DELTA_I_50MA;
+
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	switch (mode) {
+	case CLS_H_NORMAL:
+		res_val = VREF_FILT_R_50KOHM;
+		val = 0x00;
+		gain = DAC_GAIN_0DB;
+		ipeak = DELTA_I_50MA;
+		break;
+	case CLS_AB:
+		val = 0x00;
+		gain = DAC_GAIN_0DB;
+		ipeak = DELTA_I_50MA;
+		break;
+	case CLS_AB_HIFI:
+		val = 0x08;
+		break;
+	case CLS_H_HIFI:
+		val = 0x08;
+		gain = DAC_GAIN_M0P2DB;
+		ipeak = DELTA_I_50MA;
+		break;
+	case CLS_H_LOHIFI:
+		val = 0x00;
+		if ((IS_CODEC_TYPE(wcd9xxx, WCD9335)) ||
+		    (IS_CODEC_TYPE(wcd9xxx, WCD9326))) {
+			val = 0x08;
+			gain = DAC_GAIN_M0P2DB;
+			ipeak = DELTA_I_50MA;
+		}
+		break;
+	case CLS_H_ULP:
+		val = 0x0C;
+		break;
+	case CLS_H_LP:
+		val = 0x04;
+		ipeak = DELTA_I_30MA;
+		break;
+	default:
+		return;
+	};
+
+	/*
+	 * For tavil set mode to Lower_power for
+	 * CLS_H_LOHIFI and CLS_AB
+	 */
+	if ((IS_CODEC_TYPE(wcd9xxx, WCD934X)) &&
+	    (mode == CLS_H_LOHIFI || mode == CLS_AB))
+		val = 0x04;
+
+	snd_soc_update_bits(codec, WCD9XXX_A_ANA_HPH, 0x0C, val);
+	if (TASHA_IS_2_0(wcd9xxx)) {
+		snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_VCL_2,
+				    0x30, (res_val << 4));
+		if (mode != CLS_H_LP)
+			snd_soc_update_bits(codec, WCD9XXX_HPH_REFBUFF_UHQA_CTL,
+					    0x07, gain);
+		snd_soc_update_bits(codec, WCD9XXX_CLASSH_CTRL_CCL_1,
+				    0xF0, (ipeak << 4));
+	}
+}
+
+static void wcd_clsh_set_flyback_vneg_ctl(struct snd_soc_codec *codec,
+					  bool enable)
+{
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+				    0xE0, (0x07 << 5));
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEG_CTRL_1, 0xE0,
+				    (0x07 << 5));
+		snd_soc_update_bits(codec, WCD9XXX_FLYBACK_VNEGDAC_CTRL_2,
+				    0xE0, (0x02 << 5));
+	}
+}
+
+static void wcd_clsh_set_flyback_current(struct snd_soc_codec *codec, int mode)
+{
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+
+	if (!TASHA_IS_2_0(wcd9xxx))
+		return;
+
+	snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0x0F, 0x0A);
+	snd_soc_update_bits(codec, WCD9XXX_RX_BIAS_FLYB_BUFF, 0xF0, 0xA0);
+	/* Sleep needed to avoid click and pop as per HW requirement */
+	usleep_range(100, 110);
+}
+
+static void wcd_clsh_set_buck_regulator_mode(struct snd_soc_codec *codec,
+					     int mode)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_ANA_RX_SUPPLIES,
+			    0x02, 0x00);
+}
+
+static void wcd_clsh_state_lo(struct snd_soc_codec *codec,
+			      struct wcd_clsh_cdc_data *clsh_d,
+			      u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+		dev_err(codec->dev, "%s: LO cannot be in this mode: %d\n",
+			__func__, mode);
+		return;
+	}
+
+	if (is_enable) {
+		wcd_clsh_set_buck_regulator_mode(codec, mode);
+		wcd_clsh_set_flyback_vneg_ctl(codec, true);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+	} else {
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_flyback_vneg_ctl(codec, false);
+		wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_hph_ear(struct snd_soc_codec *codec,
+				   struct wcd_clsh_cdc_data *clsh_d,
+				   u8 req_state, bool is_enable, int mode)
+{
+	int hph_mode = 0;
+
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		if (req_state == WCD_CLSH_STATE_EAR) {
+			/* If HPH is running in CLS-AB when
+			 * EAR comes, let it continue to run
+			 * in Class-AB, no need to enable Class-H
+			 * for EAR.
+			 */
+			if (clsh_d->state & WCD_CLSH_STATE_HPHL)
+				hph_mode = wcd_clsh_get_int_mode(clsh_d,
+						WCD_CLSH_STATE_HPHL);
+			else if (clsh_d->state & WCD_CLSH_STATE_HPHR)
+				hph_mode = wcd_clsh_get_int_mode(clsh_d,
+						WCD_CLSH_STATE_HPHR);
+			else
+				return;
+			if (hph_mode != CLS_AB && hph_mode != CLS_AB_HIFI
+			    && !is_native_44_1_active(codec))
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+						0x40, 0x40);
+		}
+
+		if (is_native_44_1_active(codec)) {
+			snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x39);
+			snd_soc_update_bits(codec,
+					WCD9XXX_CDC_RX0_RX_PATH_SEC0,
+					0x03, 0x00);
+			if ((req_state == WCD_CLSH_STATE_HPHL) ||
+			    (req_state == WCD_CLSH_STATE_HPHR))
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+						0x40, 0x00);
+		}
+
+		if (req_state == WCD_CLSH_STATE_HPHL)
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					    0x40, 0x40);
+		if (req_state == WCD_CLSH_STATE_HPHR)
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					    0x40, 0x40);
+		if ((req_state == WCD_CLSH_STATE_HPHL) ||
+		    (req_state == WCD_CLSH_STATE_HPHR)) {
+			wcd_clsh_set_gain_path(codec, mode);
+			wcd_clsh_set_flyback_mode(codec, mode);
+			wcd_clsh_set_buck_mode(codec, mode);
+		}
+	} else {
+		if (req_state == WCD_CLSH_STATE_EAR) {
+			/*
+			 * If EAR goes away, disable EAR Channel Enable
+			 * if HPH running in Class-H otherwise
+			 * and if HPH requested mode is CLS_AB then
+			 * no need to disable EAR channel enable bit.
+			 */
+			if (wcd_clsh_enable_status(codec))
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+						0x40, 0x00);
+		}
+
+		if (is_native_44_1_active(codec)) {
+			snd_soc_write(codec, WCD9XXX_CDC_CLSH_HPH_V_PA, 0x1C);
+			snd_soc_update_bits(codec,
+					WCD9XXX_CDC_RX0_RX_PATH_SEC0,
+					0x03, 0x01);
+			if (((clsh_d->state & WCD_CLSH_STATE_HPH_ST)
+				  != WCD_CLSH_STATE_HPH_ST) &&
+			    ((req_state == WCD_CLSH_STATE_HPHL) ||
+			     (req_state == WCD_CLSH_STATE_HPHR)))
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+						0x40, 0x40);
+		}
+
+		if (req_state == WCD_CLSH_STATE_HPHL)
+			snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					0x40, 0x00);
+		if (req_state == WCD_CLSH_STATE_HPHR)
+			snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					0x40, 0x00);
+		if ((req_state & WCD_CLSH_STATE_HPH_ST) &&
+		    !wcd_clsh_enable_status(codec)) {
+			/* If Class-H is not enabled when HPH is turned
+			 * off, enable it as EAR is in progress
+			 */
+			wcd_enable_clsh_block(codec, clsh_d, true);
+			snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+					0x40, 0x40);
+			wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+			wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+		}
+	}
+}
+
+static void wcd_clsh_state_ear_lo(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_data *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (is_enable && (req_state == WCD_CLSH_STATE_LO)) {
+		wcd_clsh_set_buck_regulator_mode(codec, CLS_AB);
+	} else {
+		if (req_state == WCD_CLSH_STATE_EAR)
+			goto end;
+
+		/* LO powerdown.
+		 * If EAR Class-H is already enabled, just
+		 * turn on regulator other enable Class-H
+		 * configuration
+		 */
+		if (wcd_clsh_enable_status(codec)) {
+			wcd_clsh_set_buck_regulator_mode(codec,
+					CLS_H_NORMAL);
+			goto end;
+		}
+		wcd_enable_clsh_block(codec, clsh_d, true);
+		snd_soc_update_bits(codec,
+				WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+				0x40, 0x40);
+		wcd_clsh_set_buck_regulator_mode(codec,
+				CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+	}
+end:
+	return;
+}
+
+static void wcd_clsh_state_hph_lo(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_data *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	int hph_mode = 0;
+
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		/*
+		 * If requested state is LO, put regulator
+		 * in class-AB or if requested state is HPH,
+		 * which means LO is already enabled, keep
+		 * the regulator config the same at class-AB
+		 * and just set the power modes for flyback
+		 * and buck.
+		 */
+		if (req_state == WCD_CLSH_STATE_LO)
+			wcd_clsh_set_buck_regulator_mode(codec, CLS_AB);
+		else {
+			if (!wcd_clsh_enable_status(codec)) {
+				wcd_enable_clsh_block(codec, clsh_d, true);
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_CLSH_K1_MSB,
+						0x0F, 0x00);
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_CLSH_K1_LSB,
+						0xFF, 0xC0);
+				wcd_clsh_set_flyback_mode(codec, mode);
+				wcd_clsh_set_flyback_vneg_ctl(codec, false);
+				wcd_clsh_set_buck_mode(codec, mode);
+				wcd_clsh_set_hph_mode(codec, mode);
+				wcd_clsh_set_gain_path(codec, mode);
+			} else {
+				dev_dbg(codec->dev, "%s:clsh is already enabled\n",
+					__func__);
+			}
+			if (req_state == WCD_CLSH_STATE_HPHL)
+				snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					0x40, 0x40);
+			if (req_state == WCD_CLSH_STATE_HPHR)
+				snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					0x40, 0x40);
+		}
+	} else {
+		if ((req_state == WCD_CLSH_STATE_HPHL) ||
+		    (req_state == WCD_CLSH_STATE_HPHR)) {
+			if (req_state == WCD_CLSH_STATE_HPHL)
+				snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					    0x40, 0x00);
+			if (req_state == WCD_CLSH_STATE_HPHR)
+				snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					    0x40, 0x00);
+			/*
+			 * If HPH is powering down first, then disable clsh,
+			 * set the buck/flyback mode to default and keep the
+			 * regulator at Class-AB
+			 */
+			if ((clsh_d->state & WCD_CLSH_STATE_HPH_ST)
+				!= WCD_CLSH_STATE_HPH_ST) {
+				wcd_enable_clsh_block(codec, clsh_d, false);
+				wcd_clsh_set_flyback_vneg_ctl(codec, true);
+				wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+				wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+			}
+		} else {
+			/* LO powerdown.
+			 * If HPH mode also is CLS-AB, no need
+			 * to turn-on class-H, otherwise enable
+			 * Class-H configuration.
+			 */
+			if (clsh_d->state & WCD_CLSH_STATE_HPHL)
+				hph_mode = wcd_clsh_get_int_mode(clsh_d,
+						WCD_CLSH_STATE_HPHL);
+			else if (clsh_d->state & WCD_CLSH_STATE_HPHR)
+				hph_mode = wcd_clsh_get_int_mode(clsh_d,
+						WCD_CLSH_STATE_HPHR);
+			else
+				return;
+			dev_dbg(codec->dev, "%s: hph_mode = %d\n", __func__,
+				hph_mode);
+
+			if ((hph_mode == CLS_AB) ||
+			   (hph_mode == CLS_AB_HIFI) ||
+			   (hph_mode == CLS_NONE))
+				goto end;
+
+			/*
+			 * If Class-H is already enabled (HPH ON and then
+			 * LO ON), no need to turn on again, just set the
+			 * regulator mode.
+			 */
+			if (wcd_clsh_enable_status(codec)) {
+				wcd_clsh_set_buck_regulator_mode(codec,
+								 hph_mode);
+				goto end;
+			} else {
+				dev_dbg(codec->dev, "%s: clsh is not enabled\n",
+					__func__);
+			}
+
+			wcd_enable_clsh_block(codec, clsh_d, true);
+			snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_CLSH_K1_MSB,
+					0x0F, 0x00);
+			snd_soc_update_bits(codec,
+					WCD9XXX_A_CDC_CLSH_K1_LSB,
+					0xFF, 0xC0);
+			wcd_clsh_set_buck_regulator_mode(codec,
+							 hph_mode);
+			if (clsh_d->state & WCD_CLSH_STATE_HPHL)
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+						0x40, 0x40);
+			if (clsh_d->state & WCD_CLSH_STATE_HPHR)
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+						0x40, 0x40);
+			wcd_clsh_set_hph_mode(codec, hph_mode);
+		}
+	}
+end:
+	return;
+}
+
+static void wcd_clsh_state_hph_st(struct snd_soc_codec *codec,
+				  struct wcd_clsh_cdc_data *clsh_d,
+				  u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode == CLS_AB || mode == CLS_AB_HIFI)
+		return;
+
+	if (is_enable) {
+		if (req_state == WCD_CLSH_STATE_HPHL)
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					    0x40, 0x40);
+		if (req_state == WCD_CLSH_STATE_HPHR)
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					    0x40, 0x40);
+	} else {
+		if (req_state == WCD_CLSH_STATE_HPHL)
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					    0x40, 0x00);
+		if (req_state == WCD_CLSH_STATE_HPHR)
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					    0x40, 0x00);
+	}
+}
+
+static void wcd_clsh_state_hph_r(struct snd_soc_codec *codec,
+				 struct wcd_clsh_cdc_data *clsh_d,
+				 u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode == CLS_H_NORMAL) {
+		dev_err(codec->dev, "%s: Normal mode not applicable for hph_r\n",
+			__func__);
+		return;
+	}
+
+	if (is_enable) {
+		if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+			wcd_enable_clsh_block(codec, clsh_d, true);
+			/*
+			 * These K1 values depend on the Headphone Impedance
+			 * For now it is assumed to be 16 ohm
+			 */
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB,
+					    0x0F, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB,
+					    0xFF, 0xC0);
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					    0x40, 0x40);
+		}
+		wcd_clsh_set_buck_regulator_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_gm3_boost_disable(codec, mode);
+		wcd_clsh_force_iq_ctl(codec, mode);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_hph_mode(codec, mode);
+		wcd_clsh_set_gain_path(codec, mode);
+	} else {
+		wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+		if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX2_RX_PATH_CFG0,
+					    0x40, 0x00);
+			wcd_enable_clsh_block(codec, clsh_d, false);
+		}
+		/* buck and flyback set to default mode and disable */
+		wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL);
+		wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_hph_l(struct snd_soc_codec *codec,
+				 struct wcd_clsh_cdc_data *clsh_d,
+				 u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode == CLS_H_NORMAL) {
+		dev_err(codec->dev, "%s: Normal mode not applicable for hph_l\n",
+			__func__);
+		return;
+	}
+
+	if (is_enable) {
+		if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+			wcd_enable_clsh_block(codec, clsh_d, true);
+			/*
+			 * These K1 values depend on the Headphone Impedance
+			 * For now it is assumed to be 16 ohm
+			 */
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_MSB,
+					    0x0F, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_K1_LSB,
+					    0xFF, 0xC0);
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					    0x40, 0x40);
+		}
+		wcd_clsh_set_buck_regulator_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_gm3_boost_disable(codec, mode);
+		wcd_clsh_force_iq_ctl(codec, mode);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_hph_mode(codec, mode);
+		wcd_clsh_set_gain_path(codec, mode);
+	} else {
+		wcd_clsh_set_hph_mode(codec, CLS_H_NORMAL);
+
+		if (mode != CLS_AB && mode != CLS_AB_HIFI) {
+			snd_soc_update_bits(codec,
+					    WCD9XXX_A_CDC_RX1_RX_PATH_CFG0,
+					    0x40, 0x00);
+			wcd_enable_clsh_block(codec, clsh_d, false);
+		}
+		/* set buck and flyback to Default Mode */
+		wcd_clsh_buck_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, CLS_H_NORMAL, false);
+		wcd_clsh_force_iq_ctl(codec, CLS_H_NORMAL);
+		wcd_clsh_gm3_boost_disable(codec, CLS_H_NORMAL);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_regulator_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_ear(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable, int mode)
+{
+	dev_dbg(codec->dev, "%s: mode: %s, %s\n", __func__, mode_to_str(mode),
+		is_enable ? "enable" : "disable");
+
+	if (mode != CLS_H_NORMAL) {
+		dev_err(codec->dev, "%s: mode: %s cannot be used for EAR\n",
+			__func__, mode_to_str(mode));
+		return;
+	}
+
+	if (is_enable) {
+		wcd_enable_clsh_block(codec, clsh_d, true);
+		snd_soc_update_bits(codec,
+				    WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+				    0x40, 0x40);
+		wcd_clsh_set_buck_mode(codec, mode);
+		wcd_clsh_set_flyback_mode(codec, mode);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, true);
+		wcd_clsh_set_flyback_current(codec, mode);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, true);
+	} else {
+		snd_soc_update_bits(codec,
+				    WCD9XXX_A_CDC_RX0_RX_PATH_CFG0,
+				    0x40, 0x00);
+		wcd_enable_clsh_block(codec, clsh_d, false);
+		wcd_clsh_buck_ctrl(codec, clsh_d, mode, false);
+		wcd_clsh_flyback_ctrl(codec, clsh_d, mode, false);
+		wcd_clsh_set_flyback_mode(codec, CLS_H_NORMAL);
+		wcd_clsh_set_buck_mode(codec, CLS_H_NORMAL);
+	}
+}
+
+static void wcd_clsh_state_err(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable, int mode)
+{
+	char msg[128];
+
+	dev_err(codec->dev,
+		"%s Wrong request for class H state machine requested to %s %s",
+		__func__, is_enable ? "enable" : "disable",
+		state_to_str(req_state, msg, sizeof(msg)));
+	WARN_ON(1);
+}
+
+/*
+ * Function: wcd_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static bool wcd_clsh_is_state_valid(u8 state)
+{
+	switch (state) {
+	case WCD_CLSH_STATE_IDLE:
+	case WCD_CLSH_STATE_EAR:
+	case WCD_CLSH_STATE_HPHL:
+	case WCD_CLSH_STATE_HPHR:
+	case WCD_CLSH_STATE_HPH_ST:
+	case WCD_CLSH_STATE_LO:
+	case WCD_CLSH_STATE_HPHL_EAR:
+	case WCD_CLSH_STATE_HPHR_EAR:
+	case WCD_CLSH_STATE_HPH_ST_EAR:
+	case WCD_CLSH_STATE_HPHL_LO:
+	case WCD_CLSH_STATE_HPHR_LO:
+	case WCD_CLSH_STATE_HPH_ST_LO:
+		return true;
+	default:
+		return false;
+	};
+}
+
+/*
+ * Function: wcd_clsh_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
+void wcd_clsh_fsm(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_data *cdc_clsh_d,
+		u8 clsh_event, u8 req_state,
+		int int_mode)
+{
+	u8 old_state, new_state;
+	char msg0[128], msg1[128];
+
+	switch (clsh_event) {
+	case WCD_CLSH_EVENT_PRE_DAC:
+		old_state = cdc_clsh_d->state;
+		new_state = old_state | req_state;
+
+		if (!wcd_clsh_is_state_valid(new_state)) {
+			dev_err(codec->dev,
+				"%s: Class-H not a valid new state: %s\n",
+				__func__,
+				state_to_str(new_state, msg0, sizeof(msg0)));
+			return;
+		}
+		if (new_state == old_state) {
+			dev_err(codec->dev,
+				"%s: Class-H already in requested state: %s\n",
+				__func__,
+				state_to_str(new_state, msg0, sizeof(msg0)));
+			return;
+		}
+		cdc_clsh_d->state = new_state;
+		wcd_clsh_set_int_mode(cdc_clsh_d, req_state, int_mode);
+		(*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
+					     CLSH_REQ_ENABLE, int_mode);
+		dev_dbg(codec->dev,
+			"%s: ClassH state transition from %s to %s\n",
+			__func__, state_to_str(old_state, msg0, sizeof(msg0)),
+			state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
+		break;
+	case WCD_CLSH_EVENT_POST_PA:
+		old_state = cdc_clsh_d->state;
+		new_state = old_state & (~req_state);
+		if (new_state < NUM_CLSH_STATES_V2) {
+			if (!wcd_clsh_is_state_valid(old_state)) {
+				dev_err(codec->dev,
+					"%s:Invalid old state:%s\n",
+					__func__,
+					state_to_str(old_state, msg0,
+						     sizeof(msg0)));
+				return;
+			}
+			if (new_state == old_state) {
+				dev_err(codec->dev,
+					"%s: Class-H already in requested state: %s\n",
+					__func__,
+					state_to_str(new_state, msg0,
+						     sizeof(msg0)));
+				return;
+			}
+			(*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
+					req_state, CLSH_REQ_DISABLE,
+					int_mode);
+			cdc_clsh_d->state = new_state;
+			wcd_clsh_set_int_mode(cdc_clsh_d, req_state, CLS_NONE);
+			dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+				__func__, state_to_str(old_state, msg0,
+						       sizeof(msg0)),
+				state_to_str(cdc_clsh_d->state, msg1,
+					     sizeof(msg1)));
+		}
+		break;
+	};
+}
+
+int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh)
+{
+	return clsh->state;
+}
+EXPORT_SYMBOL(wcd_clsh_get_clsh_state);
+
+void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh)
+{
+	int i;
+
+	clsh->state = WCD_CLSH_STATE_IDLE;
+
+	for (i = 0; i < NUM_CLSH_STATES_V2; i++)
+		clsh_state_fp[i] = wcd_clsh_state_err;
+
+	clsh_state_fp[WCD_CLSH_STATE_EAR] = wcd_clsh_state_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPHL] =
+						wcd_clsh_state_hph_l;
+	clsh_state_fp[WCD_CLSH_STATE_HPHR] =
+						wcd_clsh_state_hph_r;
+	clsh_state_fp[WCD_CLSH_STATE_HPH_ST] =
+						wcd_clsh_state_hph_st;
+	clsh_state_fp[WCD_CLSH_STATE_LO] = wcd_clsh_state_lo;
+	clsh_state_fp[WCD_CLSH_STATE_HPHL_EAR] =
+						wcd_clsh_state_hph_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPHR_EAR] =
+						wcd_clsh_state_hph_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPH_ST_EAR] =
+						wcd_clsh_state_hph_ear;
+	clsh_state_fp[WCD_CLSH_STATE_HPHL_LO] = wcd_clsh_state_hph_lo;
+	clsh_state_fp[WCD_CLSH_STATE_HPHR_LO] = wcd_clsh_state_hph_lo;
+	clsh_state_fp[WCD_CLSH_STATE_HPH_ST_LO] =
+						wcd_clsh_state_hph_lo;
+	clsh_state_fp[WCD_CLSH_STATE_EAR_LO] = wcd_clsh_state_ear_lo;
+	/* Set interpolaotr modes to NONE */
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_EAR, CLS_NONE);
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHL, CLS_NONE);
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_HPHR, CLS_NONE);
+	wcd_clsh_set_int_mode(clsh, WCD_CLSH_STATE_LO, CLS_NONE);
+	clsh->flyback_users = 0;
+	clsh->buck_users = 0;
+	clsh->clsh_users = 0;
+}
+EXPORT_SYMBOL(wcd_clsh_init);
+
+MODULE_DESCRIPTION("WCD9XXX Common Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-common-v2.h b/sound/soc/codecs/wcd9xxx-common-v2.h
new file mode 100644
index 0000000..ee7e587
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common-v2.h
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCD9XXX_COMMON_V2
+
+#define _WCD9XXX_COMMON_V2
+
+#define CLSH_REQ_ENABLE true
+#define CLSH_REQ_DISABLE false
+
+#define WCD_CLSH_EVENT_PRE_DAC 0x01
+#define WCD_CLSH_EVENT_POST_PA 0x02
+#define MAX_VBAT_MONITOR_WRITES 17
+/*
+ * Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: Lineout mode
+ */
+#define	WCD_CLSH_STATE_IDLE 0x00
+#define	WCD_CLSH_STATE_EAR (0x01 << 0)
+#define	WCD_CLSH_STATE_HPHL (0x01 << 1)
+#define	WCD_CLSH_STATE_HPHR (0x01 << 2)
+#define	WCD_CLSH_STATE_LO (0x01 << 3)
+#define WCD_CLSH_STATE_MAX 4
+#define NUM_CLSH_STATES_V2 (0x01 << WCD_CLSH_STATE_MAX)
+
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD_CLSH_STATE_HPH_ST (WCD_CLSH_STATE_HPHL | \
+			       WCD_CLSH_STATE_HPHR)
+
+#define WCD_CLSH_STATE_HPHL_LO (WCD_CLSH_STATE_HPHL | \
+				    WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_HPHR_LO (WCD_CLSH_STATE_HPHR | \
+				    WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_HPH_ST_LO (WCD_CLSH_STATE_HPH_ST | \
+				      WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_EAR_LO (WCD_CLSH_STATE_EAR | \
+				   WCD_CLSH_STATE_LO)
+#define WCD_CLSH_STATE_HPHL_EAR (WCD_CLSH_STATE_HPHL | \
+				     WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPHR_EAR (WCD_CLSH_STATE_HPHR | \
+				     WCD_CLSH_STATE_EAR)
+#define WCD_CLSH_STATE_HPH_ST_EAR (WCD_CLSH_STATE_HPH_ST | \
+				       WCD_CLSH_STATE_EAR)
+
+enum {
+	CLS_H_NORMAL = 0, /* Class-H Default */
+	CLS_H_HIFI, /* Class-H HiFi */
+	CLS_H_LP, /* Class-H Low Power */
+	CLS_AB, /* Class-AB Low HIFI*/
+	CLS_H_LOHIFI, /* LoHIFI */
+	CLS_H_ULP, /* Ultra Low power */
+	CLS_AB_HIFI, /* Class-AB */
+	CLS_NONE, /* None of the above modes */
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd_clsh_cdc_data {
+	u8 state;
+	int flyback_users;
+	int buck_users;
+	int clsh_users;
+	int interpolator_modes[WCD_CLSH_STATE_MAX];
+};
+
+struct wcd_mad_audio_header {
+	u32 reserved[3];
+	u32 num_reg_cfg;
+};
+
+struct wcd_mad_microphone_info {
+	uint8_t input_microphone;
+	uint8_t cycle_time;
+	uint8_t settle_time;
+	uint8_t padding;
+} __packed;
+
+struct wcd_mad_micbias_info {
+	uint8_t micbias;
+	uint8_t k_factor;
+	uint8_t external_bypass_capacitor;
+	uint8_t internal_biasing;
+	uint8_t cfilter;
+	uint8_t padding[3];
+} __packed;
+
+struct wcd_mad_rms_audio_beacon_info {
+	uint8_t rms_omit_samples;
+	uint8_t rms_comp_time;
+	uint8_t detection_mechanism;
+	uint8_t rms_diff_threshold;
+	uint8_t rms_threshold_lsb;
+	uint8_t rms_threshold_msb;
+	uint8_t padding[2];
+	uint8_t iir_coefficients[36];
+} __packed;
+
+struct wcd_mad_rms_ultrasound_info {
+	uint8_t rms_comp_time;
+	uint8_t detection_mechanism;
+	uint8_t rms_diff_threshold;
+	uint8_t rms_threshold_lsb;
+	uint8_t rms_threshold_msb;
+	uint8_t padding[3];
+	uint8_t iir_coefficients[36];
+} __packed;
+
+struct wcd_mad_audio_cal {
+	uint32_t version;
+	struct wcd_mad_microphone_info microphone_info;
+	struct wcd_mad_micbias_info micbias_info;
+	struct wcd_mad_rms_audio_beacon_info audio_info;
+	struct wcd_mad_rms_audio_beacon_info beacon_info;
+	struct wcd_mad_rms_ultrasound_info ultrasound_info;
+} __packed;
+
+struct wcd9xxx_anc_header {
+	u32 reserved[3];
+	u32 num_anc_slots;
+};
+
+struct vbat_monitor_reg {
+	u32 size;
+	u32 writes[MAX_VBAT_MONITOR_WRITES];
+} __packed;
+
+struct wcd_reg_mask_val {
+	u16	reg;
+	u8	mask;
+	u8	val;
+};
+
+extern void wcd_clsh_fsm(struct snd_soc_codec *codec,
+		struct wcd_clsh_cdc_data *cdc_clsh_d,
+		u8 clsh_event, u8 req_state,
+		int int_mode);
+
+extern void wcd_clsh_init(struct wcd_clsh_cdc_data *clsh);
+extern int wcd_clsh_get_clsh_state(struct wcd_clsh_cdc_data *clsh);
+extern void wcd_clsh_imped_config(struct snd_soc_codec *codec, int imped,
+		bool reset);
+
+enum {
+	RESERVED = 0,
+	AANC_LPF_FF_FB = 1,
+	AANC_LPF_COEFF_MSB,
+	AANC_LPF_COEFF_LSB,
+	HW_MAD_AUDIO_ENABLE,
+	HW_MAD_ULTR_ENABLE,
+	HW_MAD_BEACON_ENABLE,
+	HW_MAD_AUDIO_SLEEP_TIME,
+	HW_MAD_ULTR_SLEEP_TIME,
+	HW_MAD_BEACON_SLEEP_TIME,
+	HW_MAD_TX_AUDIO_SWITCH_OFF,
+	HW_MAD_TX_ULTR_SWITCH_OFF,
+	HW_MAD_TX_BEACON_SWITCH_OFF,
+	MAD_AUDIO_INT_DEST_SELECT_REG,
+	MAD_ULT_INT_DEST_SELECT_REG,
+	MAD_BEACON_INT_DEST_SELECT_REG,
+	MAD_CLIP_INT_DEST_SELECT_REG,
+	VBAT_INT_DEST_SELECT_REG,
+	MAD_AUDIO_INT_MASK_REG,
+	MAD_ULT_INT_MASK_REG,
+	MAD_BEACON_INT_MASK_REG,
+	MAD_CLIP_INT_MASK_REG,
+	VBAT_INT_MASK_REG,
+	MAD_AUDIO_INT_STATUS_REG,
+	MAD_ULT_INT_STATUS_REG,
+	MAD_BEACON_INT_STATUS_REG,
+	MAD_CLIP_INT_STATUS_REG,
+	VBAT_INT_STATUS_REG,
+	MAD_AUDIO_INT_CLEAR_REG,
+	MAD_ULT_INT_CLEAR_REG,
+	MAD_BEACON_INT_CLEAR_REG,
+	MAD_CLIP_INT_CLEAR_REG,
+	VBAT_INT_CLEAR_REG,
+	SB_PGD_PORT_TX_WATERMARK_N,
+	SB_PGD_PORT_TX_ENABLE_N,
+	SB_PGD_PORT_RX_WATERMARK_N,
+	SB_PGD_PORT_RX_ENABLE_N,
+	SB_PGD_TX_PORTn_MULTI_CHNL_0,
+	SB_PGD_TX_PORTn_MULTI_CHNL_1,
+	SB_PGD_RX_PORTn_MULTI_CHNL_0,
+	SB_PGD_RX_PORTn_MULTI_CHNL_1,
+	AANC_FF_GAIN_ADAPTIVE,
+	AANC_FFGAIN_ADAPTIVE_EN,
+	AANC_GAIN_CONTROL,
+	SPKR_CLIP_PIPE_BANK_SEL,
+	SPKR_CLIPDET_VAL0,
+	SPKR_CLIPDET_VAL1,
+	SPKR_CLIPDET_VAL2,
+	SPKR_CLIPDET_VAL3,
+	SPKR_CLIPDET_VAL4,
+	SPKR_CLIPDET_VAL5,
+	SPKR_CLIPDET_VAL6,
+	SPKR_CLIPDET_VAL7,
+	VBAT_RELEASE_INT_DEST_SELECT_REG,
+	VBAT_RELEASE_INT_MASK_REG,
+	VBAT_RELEASE_INT_STATUS_REG,
+	VBAT_RELEASE_INT_CLEAR_REG,
+	MAD2_CLIP_INT_DEST_SELECT_REG,
+	MAD2_CLIP_INT_MASK_REG,
+	MAD2_CLIP_INT_STATUS_REG,
+	MAD2_CLIP_INT_CLEAR_REG,
+	SPKR2_CLIP_PIPE_BANK_SEL,
+	SPKR2_CLIPDET_VAL0,
+	SPKR2_CLIPDET_VAL1,
+	SPKR2_CLIPDET_VAL2,
+	SPKR2_CLIPDET_VAL3,
+	SPKR2_CLIPDET_VAL4,
+	SPKR2_CLIPDET_VAL5,
+	SPKR2_CLIPDET_VAL6,
+	SPKR2_CLIPDET_VAL7,
+	MAX_CFG_REGISTERS,
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-common.c b/sound/soc/codecs/wcd9xxx-common.c
new file mode 100644
index 0000000..7b2e68a
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common.c
@@ -0,0 +1,1480 @@
+/* Copyright (c) 2013-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/soc.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include "wcd9xxx-common.h"
+
+#define CLSH_COMPUTE_EAR 0x01
+#define CLSH_COMPUTE_HPH_L 0x02
+#define CLSH_COMPUTE_HPH_R 0x03
+
+#define BUCK_VREF_0P494V 0x3F
+#define BUCK_VREF_2V 0xFF
+#define BUCK_VREF_0P494V 0x3F
+#define BUCK_VREF_1P8V 0xE6
+
+#define BUCK_SETTLE_TIME_US 50
+#define NCP_SETTLE_TIME_US 50
+
+#define MAX_IMPED_PARAMS 13
+
+#define USLEEP_RANGE_MARGIN_US 100
+
+struct wcd9xxx_imped_val {
+	u32 imped_val;
+	u8 index;
+};
+
+static const struct wcd9xxx_reg_mask_val imped_table[][MAX_IMPED_PARAMS] = {
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x46},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x04},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x47},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x15},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x05},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0C},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0E},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x49},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x17},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x5F},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCF},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x06},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x0F},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x59},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x15},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xCE},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x10},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x66},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x9A},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x02},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xBD},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x07},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x11},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA6},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x08},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x76},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x04},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x11},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x4E},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAD},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x09},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x12},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x12},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0A},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x13},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x7A},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0B},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x60},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x09},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0C},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x14},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x79},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1F},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0D},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x15},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x78},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x16},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x0E},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x89},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x40},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x10},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xD0},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x12},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xB7},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x13},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x17},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8A},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xA4},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x15},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x08},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x18},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x8B},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x18},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAC},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x20},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1A},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x9A},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x17},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x13},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1B},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2E},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1D},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x1A},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x14},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2D},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x1F},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x19},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xB9},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x06},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x10},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x23},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x18},
+	},
+	{
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0xA9},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x07},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1D},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x27},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x35},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0xff, 0x26},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0xff, 0x16},
+	},
+};
+
+static const struct wcd9xxx_imped_val imped_index[] = {
+	{4000, 0},
+	{4500, 1},
+	{5000, 2},
+	{5500, 3},
+	{6000, 4},
+	{6500, 5},
+	{7000, 6},
+	{7700, 7},
+	{8470, 8},
+	{9317, 9},
+	{10248, 10},
+	{11273, 11},
+	{12400, 12},
+	{13641, 13},
+	{15005, 14},
+	{16505, 15},
+	{18156, 16},
+	{19971, 17},
+	{21969, 18},
+	{24165, 19},
+	{26582, 20},
+	{29240, 21},
+	{32164, 22},
+};
+
+static inline void
+wcd9xxx_enable_clsh_block(struct snd_soc_codec *codec,
+			  struct wcd9xxx_clsh_cdc_data *clsh_d, bool enable)
+{
+	if ((enable && ++clsh_d->clsh_users == 1) ||
+	    (!enable && --clsh_d->clsh_users == 0))
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+				    0x01, enable ? 0x01 : 0x00);
+	dev_dbg(codec->dev, "%s: clsh_users %d, enable %d", __func__,
+		clsh_d->clsh_users, enable);
+}
+
+static inline void wcd9xxx_enable_anc_delay(
+	struct snd_soc_codec *codec,
+	bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL,
+		0x02, on ? 0x02 : 0x00);
+}
+
+static inline void
+wcd9xxx_enable_buck(struct snd_soc_codec *codec,
+		    struct wcd9xxx_clsh_cdc_data *clsh_d, bool enable)
+{
+	if ((enable && ++clsh_d->buck_users == 1) ||
+	    (!enable && --clsh_d->buck_users == 0))
+		snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+				    0x80, enable ? 0x80 : 0x00);
+	dev_dbg(codec->dev, "%s: buck_users %d, enable %d", __func__,
+		clsh_d->buck_users, enable);
+}
+
+static void (*clsh_state_fp[NUM_CLSH_STATES])(struct snd_soc_codec *,
+					      struct wcd9xxx_clsh_cdc_data *,
+					      u8 req_state, bool req_type);
+
+static const char *state_to_str(u8 state, char *buf, size_t buflen)
+{
+	int i;
+	int cnt = 0;
+	/*
+	 * This array of strings should match with enum wcd9xxx_clsh_state_bit.
+	 */
+	static const char *const states[] = {
+		"STATE_EAR",
+		"STATE_HPH_L",
+		"STATE_HPH_R",
+		"STATE_LO",
+	};
+
+	if (state == WCD9XXX_CLSH_STATE_IDLE) {
+		snprintf(buf, buflen, "[STATE_IDLE]");
+		goto done;
+	}
+
+	buf[0] = '\0';
+	for (i = 0; i < ARRAY_SIZE(states); i++) {
+		if (!(state & (1 << i)))
+			continue;
+		cnt = snprintf(buf, buflen - cnt - 1, "%s%s%s", buf,
+			       buf[0] == '\0' ? "[" : "|",
+			       states[i]);
+	}
+	if (cnt > 0)
+		strlcat(buf + cnt, "]", buflen);
+
+done:
+	if (buf[0] == '\0')
+		snprintf(buf, buflen, "[STATE_UNKNOWN]");
+	return buf;
+}
+
+static void wcd9xxx_cfg_clsh_param_common(
+		struct snd_soc_codec *codec)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 0, 0},
+		{WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, 0x3 << 2, 1 << 2},
+		{WCD9XXX_A_CDC_CLSH_BUCK_NCP_VARS, (0x1 << 4), 0},
+		{WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 0), 0x01},
+		{WCD9XXX_A_CDC_CLSH_B2_CTL, (0x3 << 2), (0x01 << 2)},
+		{WCD9XXX_A_CDC_CLSH_B2_CTL, (0xf << 4), (0x03 << 4)},
+		{WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 4), (0x03 << 4)},
+		{WCD9XXX_A_CDC_CLSH_B3_CTL, (0xf << 0), (0x0B)},
+		{WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 5), (0x01 << 5)},
+		{WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 1), (0x01 << 1)},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+						    reg_set[i].val);
+
+	dev_dbg(codec->dev, "%s: Programmed class H controller common parameters",
+			 __func__);
+}
+
+static void wcd9xxx_chargepump_request(struct snd_soc_codec *codec, bool on)
+{
+	static int cp_count;
+
+	if (on && (++cp_count == 1)) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+				    0x01, 0x01);
+		dev_dbg(codec->dev, "%s: Charge Pump enabled, count = %d\n",
+			__func__, cp_count);
+	} else if (!on) {
+		if (--cp_count < 0) {
+			dev_dbg(codec->dev,
+				"%s: Unbalanced disable for charge pump\n",
+				__func__);
+			if (snd_soc_read(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL) &
+			    0x01) {
+				dev_dbg(codec->dev,
+					"%s: Actual chargepump is ON\n",
+					__func__);
+			}
+			cp_count = 0;
+			WARN_ON(1);
+		}
+
+		if (cp_count == 0) {
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_OTHR_CTL,
+					    0x01, 0x00);
+			dev_dbg(codec->dev,
+				"%s: Charge pump disabled, count = %d\n",
+				__func__, cp_count);
+		}
+	}
+}
+
+void wcd9xxx_enable_high_perf_mode(struct snd_soc_codec *codec,
+				struct wcd9xxx_clsh_cdc_data *clsh_d,
+				u8 uhqa_mode, u8 req_state, bool req_type)
+{
+	dev_dbg(codec->dev, "%s: users fclk8 %d, fclk5 %d", __func__,
+			clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+			clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+
+	if (req_type == WCD9XXX_CLSAB_REQ_ENABLE) {
+		clsh_d->ncp_users[NCP_FCLK_LEVEL_8]++;
+		snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_PA,
+					WCD9XXX_A_RX_HPH_BIAS_PA__POR);
+		snd_soc_write(codec, WCD9XXX_A_RX_HPH_L_PA_CTL, 0x48);
+		snd_soc_write(codec, WCD9XXX_A_RX_HPH_R_PA_CTL, 0x48);
+		if (uhqa_mode)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CHOP_CTL,
+						0x20, 0x00);
+		wcd9xxx_chargepump_request(codec, true);
+		wcd9xxx_enable_anc_delay(codec, true);
+		wcd9xxx_enable_buck(codec, clsh_d, false);
+		if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] > 0)
+			snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+						0x0F, 0x08);
+		snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x30, 0x30);
+
+		/* Enable NCP and wait until settles down */
+		if (snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x01))
+			usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US+10);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CHOP_CTL,
+					0x20, 0x20);
+		snd_soc_write(codec, WCD9XXX_A_RX_HPH_L_PA_CTL,
+					WCD9XXX_A_RX_HPH_L_PA_CTL__POR);
+		snd_soc_write(codec, WCD9XXX_A_RX_HPH_R_PA_CTL,
+					WCD9XXX_A_RX_HPH_R_PA_CTL__POR);
+		snd_soc_write(codec, WCD9XXX_A_RX_HPH_BIAS_PA, 0x57);
+		wcd9xxx_enable_buck(codec, clsh_d, true);
+		wcd9xxx_chargepump_request(codec, false);
+		wcd9xxx_enable_anc_delay(codec, false);
+		clsh_d->ncp_users[NCP_FCLK_LEVEL_8]--;
+		if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0 &&
+		    clsh_d->ncp_users[NCP_FCLK_LEVEL_5] == 0)
+			snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN,
+						0x01, 0x00);
+		else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0)
+			snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+						0x0F, 0x05);
+	}
+	dev_dbg(codec->dev, "%s: leave\n", __func__);
+}
+EXPORT_SYMBOL(wcd9xxx_enable_high_perf_mode);
+
+static int get_impedance_index(u32 imped)
+{
+	int i = 0;
+
+	if (imped < imped_index[i].imped_val) {
+		pr_debug("%s, detected impedance is less than 4 Ohm\n",
+				__func__);
+		goto ret;
+	}
+	if (imped >= imped_index[ARRAY_SIZE(imped_index) - 1].imped_val) {
+		pr_debug("%s, detected impedance is greater than 32164 Ohm\n",
+				__func__);
+		i = ARRAY_SIZE(imped_index) - 1;
+		goto ret;
+	}
+	for (i = 0; i < ARRAY_SIZE(imped_index) - 1; i++) {
+		if (imped >= imped_index[i].imped_val &&
+			imped < imped_index[i + 1].imped_val)
+			break;
+	}
+ret:
+	pr_debug("%s: selected impedance index = %d\n",
+			__func__, imped_index[i].index);
+	return imped_index[i].index;
+}
+
+void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec,
+				  int imped)
+{
+	int i  = 0;
+	int index = 0;
+
+	index = get_impedance_index(imped);
+	if (index >= ARRAY_SIZE(imped_index)) {
+		pr_err("%s, invalid imped = %d\n", __func__, imped);
+		return;
+	}
+	for (i = 0; i < MAX_IMPED_PARAMS; i++)
+		snd_soc_write(codec, imped_table[index][i].reg,
+					imped_table[index][i].val);
+}
+
+static void wcd9xxx_clsh_comp_req(struct snd_soc_codec *codec,
+				  struct wcd9xxx_clsh_cdc_data *clsh_d,
+				  int compute_pa, bool on)
+{
+	u8 shift;
+
+	if (compute_pa == CLSH_COMPUTE_EAR) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLSH_B1_CTL, 0x10,
+				    (on ? 0x10 : 0));
+	} else {
+		if (compute_pa == CLSH_COMPUTE_HPH_L) {
+			shift = 3;
+		} else if (compute_pa == CLSH_COMPUTE_HPH_R) {
+			shift = 2;
+		} else {
+			dev_dbg(codec->dev,
+				"%s: classh computation request is incorrect\n",
+				__func__);
+			return;
+		}
+
+		if (on)
+			wcd9xxx_resmgr_add_cond_update_bits(clsh_d->resmgr,
+						  WCD9XXX_COND_HPH,
+						  WCD9XXX_A_CDC_CLSH_B1_CTL,
+						  shift, false);
+		else
+			wcd9xxx_resmgr_rm_cond_update_bits(clsh_d->resmgr,
+						  WCD9XXX_COND_HPH,
+						  WCD9XXX_A_CDC_CLSH_B1_CTL,
+						  shift, false);
+	}
+}
+
+int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+					struct list_head *list,
+					uint16_t reg, uint8_t mask,
+					uint8_t value, int delay)
+{
+	int rc;
+	struct wcd9xxx_register_save_node *node;
+
+	node = kmalloc(sizeof(*node), GFP_KERNEL);
+	if (unlikely(!node)) {
+		pr_err("%s: Not enough memory\n", __func__);
+		return -ENOMEM;
+	}
+	node->reg = reg;
+	node->value = snd_soc_read(codec, reg);
+	list_add(&node->lh, list);
+	if (mask == 0xFF)
+		rc = snd_soc_write(codec, reg, value);
+	else
+		rc = snd_soc_update_bits(codec, reg, mask, value);
+	if (delay)
+		usleep_range(delay, delay + USLEEP_RANGE_MARGIN_US);
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_soc_update_bits_push);
+
+void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
+			       struct list_head *lh)
+{
+	struct wcd9xxx_register_save_node *node, *nodetmp;
+
+	list_for_each_entry_safe(node, nodetmp, lh, lh) {
+		snd_soc_write(codec, node->reg, node->value);
+		list_del(&node->lh);
+		kfree(node);
+	}
+}
+EXPORT_SYMBOL(wcd9xxx_restore_registers);
+
+static void wcd9xxx_dynamic_bypass_buck_ctrl_lo(struct snd_soc_codec *cdc,
+						bool enable)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)},
+		{WCD9XXX_A_BUCK_MODE_5, enable ? 0xFF : 0x02, 0x02},
+		{WCD9XXX_A_BUCK_MODE_5, 0x1, 0x01}
+	};
+
+	if (!enable) {
+		snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1,
+					(0x1 << 3), 0x00);
+		snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4,
+					0xFF, BUCK_VREF_2V);
+	}
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask,
+							reg_set[i].val);
+
+	/* 50us sleep is reqd. as per the class H HW design sequence */
+	usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_dynamic_bypass_buck_ctrl(struct snd_soc_codec *cdc,
+						bool enable)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_BUCK_MODE_3, (0x1 << 3), (enable << 3)},
+		{WCD9XXX_A_BUCK_MODE_5, (0x1 << 1), ((!enable) << 1)},
+		{WCD9XXX_A_BUCK_MODE_5, 0x1, !enable}
+	};
+	if (!enable) {
+		snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_1,
+					(0x1 << 3), 0x00);
+		snd_soc_update_bits(cdc, WCD9XXX_A_BUCK_MODE_4,
+					0xFF, BUCK_VREF_2V);
+	}
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(cdc, reg_set[i].reg, reg_set[i].mask,
+							reg_set[i].val);
+
+	/* 50us sleep is reqd. as per the class H HW design sequence */
+	usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_set_buck_mode(struct snd_soc_codec *codec, u8 buck_vref)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_BUCK_MODE_5, 0x02, 0x02},
+		{WCD9XXX_A_BUCK_MODE_4, 0xFF, buck_vref},
+		{WCD9XXX_A_BUCK_MODE_1, 0x04, 0x04},
+		{WCD9XXX_A_BUCK_MODE_3, 0x04, 0x00},
+		{WCD9XXX_A_BUCK_MODE_3, 0x08, 0x00},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(codec, reg_set[i].reg,
+					reg_set[i].mask, reg_set[i].val);
+
+	dev_dbg(codec->dev, "%s: Done\n", __func__);
+	usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US + 10);
+}
+
+
+/* This will be called for all states except Lineout */
+static void wcd9xxx_clsh_enable_post_pa(struct snd_soc_codec *codec,
+	struct wcd9xxx_clsh_cdc_data *cdc_clsh_d)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_BUCK_MODE_5, 0x02, 0x00},
+		{WCD9XXX_A_NCP_STATIC, 0x20, 0x00},
+		{WCD9XXX_A_BUCK_MODE_3, 0x04, 0x04},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(codec, reg_set[i].reg,
+					reg_set[i].mask, reg_set[i].val);
+
+	if (!cdc_clsh_d->is_dynamic_vdd_cp)
+		snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_3,
+							0x08, 0x08);
+
+	dev_dbg(codec->dev, "%s: completed clsh mode settings after PA enable\n",
+		   __func__);
+
+}
+
+static void wcd9xxx_set_fclk_get_ncp(struct snd_soc_codec *codec,
+				     struct wcd9xxx_clsh_cdc_data *clsh_d,
+				     enum ncp_fclk_level fclk_level)
+{
+	clsh_d->ncp_users[fclk_level]++;
+
+	pr_debug("%s: enter ncp type %d users fclk8 %d, fclk5 %d\n", __func__,
+		 fclk_level, clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+		 clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x10, 0x00);
+	/* fclk level 8 dominates level 5 */
+	if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] > 0)
+		snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x08);
+	else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_5] > 0)
+		snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x05);
+	else
+		WARN_ONCE(1, "Unexpected users %d,%d\n",
+			  clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+			  clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+	snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x20, 0x20);
+
+	/* enable NCP and wait until settles down */
+	if (snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x01))
+		usleep_range(NCP_SETTLE_TIME_US, NCP_SETTLE_TIME_US + 50);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_set_fclk_put_ncp(struct snd_soc_codec *codec,
+				     struct wcd9xxx_clsh_cdc_data *clsh_d,
+				     enum ncp_fclk_level fclk_level)
+{
+	clsh_d->ncp_users[fclk_level]--;
+
+	pr_debug("%s: enter ncp type %d users fclk8 %d, fclk5 %d\n", __func__,
+		 fclk_level, clsh_d->ncp_users[NCP_FCLK_LEVEL_8],
+		 clsh_d->ncp_users[NCP_FCLK_LEVEL_5]);
+
+	if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0 &&
+	    clsh_d->ncp_users[NCP_FCLK_LEVEL_5] == 0)
+		snd_soc_update_bits(codec, WCD9XXX_A_NCP_EN, 0x01, 0x00);
+	else if (clsh_d->ncp_users[NCP_FCLK_LEVEL_8] == 0)
+		/* if dominating level 8 has gone, switch to 5 */
+		snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC, 0x0F, 0x05);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_ear(struct snd_soc_codec *codec)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 7), 0},
+		{WCD9XXX_A_CDC_CLSH_V_PA_HD_EAR, (0x3f << 0), 0x0D},
+		{WCD9XXX_A_CDC_CLSH_V_PA_MIN_EAR, (0x3f << 0), 0x3A},
+
+		/* Under assumption that EAR load is 10.7ohm */
+		{WCD9XXX_A_CDC_CLSH_IDLE_EAR_THSD, (0x3f << 0), 0x26},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_EAR_THSD, (0x3f << 0), 0x2C},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_L, 0xff, 0xA9},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_EAR_U, 0xff, 0x07},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, (0xf << 0), 0x08},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1b},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x2d},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x36},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x37},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(codec, reg_set[i].reg,
+					reg_set[i].mask, reg_set[i].val);
+
+	dev_dbg(codec->dev, "%s: Programmed Class H controller EAR specific params\n",
+			 __func__);
+}
+
+static void wcd9xxx_cfg_clsh_param_hph(struct snd_soc_codec *codec)
+{
+	int i;
+	const struct wcd9xxx_reg_mask_val reg_set[] = {
+		{WCD9XXX_A_CDC_CLSH_B1_CTL, (0x1 << 6), 0},
+		{WCD9XXX_A_CDC_CLSH_V_PA_HD_HPH, 0x3f, 0x0D},
+		{WCD9XXX_A_CDC_CLSH_V_PA_MIN_HPH, 0x3f, 0x1D},
+
+		/* Under assumption that HPH load is 16ohm per channel */
+		{WCD9XXX_A_CDC_CLSH_IDLE_HPH_THSD, 0x3f, 0x13},
+		{WCD9XXX_A_CDC_CLSH_FCLKONLY_HPH_THSD, 0x1f, 0x19},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_L, 0xff, 0x97},
+		{WCD9XXX_A_CDC_CLSH_I_PA_FACT_HPH_U, 0xff, 0x05},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, (0x1 << 7), 0},
+		{WCD9XXX_A_CDC_CLSH_K_ADDR, 0x0f, 0},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0xAE},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x01},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x1C},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x24},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x25},
+		{WCD9XXX_A_CDC_CLSH_K_DATA, 0xff, 0x00},
+	};
+
+	for (i = 0; i < ARRAY_SIZE(reg_set); i++)
+		snd_soc_update_bits(codec, reg_set[i].reg, reg_set[i].mask,
+							reg_set[i].val);
+	dev_dbg(codec->dev, "%s: Programmed Class H controller HPH specific params\n",
+			 __func__);
+}
+
+static void wcd9xxx_ncp_bypass_enable(struct snd_soc_codec *cdc, bool enable)
+{
+	snd_soc_update_bits(cdc, WCD9XXX_A_NCP_STATIC, 0x10, (enable << 4));
+	/* 50us sleep is reqd. as per the class H HW design sequence */
+	usleep_range(BUCK_SETTLE_TIME_US, BUCK_SETTLE_TIME_US+10);
+}
+
+static void wcd9xxx_clsh_set_Iest(struct snd_soc_codec *codec,
+		u8 value)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+				    0x01, (0x01 & 0x03));
+	snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+				    0xFC, (value << 2));
+}
+
+static void wcd9xxx_clsh_state_hph_ear(struct snd_soc_codec *codec,
+			struct wcd9xxx_clsh_cdc_data *clsh_d,
+			u8 req_state, bool is_enable)
+{
+	int compute_pa = 0;
+
+	dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+			is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		/*
+		 * The below check condition is required to make sure
+		 * functions inside if condition will execute only once.
+		 */
+		if ((clsh_d->state == WCD9XXX_CLSH_STATE_EAR) ||
+			(req_state == WCD9XXX_CLSH_STATE_EAR)) {
+			wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+			wcd9xxx_ncp_bypass_enable(codec, true);
+		}
+		switch (req_state) {
+		case WCD9XXX_CLSH_STATE_HPHL:
+			compute_pa = CLSH_COMPUTE_HPH_L;
+			break;
+		case WCD9XXX_CLSH_STATE_HPHR:
+			compute_pa = CLSH_COMPUTE_HPH_R;
+			break;
+		case WCD9XXX_CLSH_STATE_EAR:
+			compute_pa = CLSH_COMPUTE_EAR;
+			break;
+		default:
+			dev_dbg(codec->dev,
+				"%s:Invalid state:0x%x,enable:0x%x\n",
+				__func__, req_state, is_enable);
+			break;
+		}
+		wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, true);
+
+		dev_dbg(codec->dev, "%s: Enabled hph+ear mode clsh\n",
+				__func__);
+	} else {
+		switch (req_state) {
+		case WCD9XXX_CLSH_STATE_HPHL:
+			compute_pa = CLSH_COMPUTE_HPH_L;
+			break;
+		case WCD9XXX_CLSH_STATE_HPHR:
+			compute_pa = CLSH_COMPUTE_HPH_R;
+			break;
+		case WCD9XXX_CLSH_STATE_EAR:
+			compute_pa = CLSH_COMPUTE_EAR;
+			break;
+		default:
+			dev_dbg(codec->dev,
+				"%s:Invalid state:0x%x,enable:0x%x\n",
+				__func__, req_state, is_enable);
+			break;
+		}
+		wcd9xxx_clsh_comp_req(codec, clsh_d, compute_pa, false);
+
+		if (((clsh_d->state & (~req_state)) ==
+				WCD9XXX_CLSH_STATE_EAR) ||
+			(req_state == WCD9XXX_CLSH_STATE_EAR)) {
+			wcd9xxx_ncp_bypass_enable(codec, false);
+			wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+		}
+	}
+}
+
+static void wcd9xxx_clsh_state_hph_lo(struct snd_soc_codec *codec,
+			struct wcd9xxx_clsh_cdc_data *clsh_d,
+			u8 req_state, bool is_enable)
+{
+
+	dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+			is_enable ? "enable" : "disable");
+	if (is_enable) {
+		if ((clsh_d->state == WCD9XXX_CLSH_STATE_LO) ||
+			(req_state == WCD9XXX_CLSH_STATE_LO)) {
+			wcd9xxx_dynamic_bypass_buck_ctrl_lo(codec, false);
+			wcd9xxx_enable_buck(codec, clsh_d, true);
+			wcd9xxx_ncp_bypass_enable(codec, true);
+			if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+				wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+							NCP_FCLK_LEVEL_8);
+				wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+							NCP_FCLK_LEVEL_5);
+				wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+				wcd9xxx_chargepump_request(codec, true);
+				wcd9xxx_enable_anc_delay(codec, true);
+			}
+		}
+		if (req_state == WCD9XXX_CLSH_STATE_HPHL)
+			wcd9xxx_clsh_comp_req(codec, clsh_d,
+						CLSH_COMPUTE_HPH_L, true);
+		if (req_state == WCD9XXX_CLSH_STATE_HPHR)
+			wcd9xxx_clsh_comp_req(codec, clsh_d,
+						CLSH_COMPUTE_HPH_R, true);
+	} else {
+		switch (req_state) {
+		case WCD9XXX_CLSH_STATE_LO:
+			snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+						0x20, 0x00);
+			wcd9xxx_dynamic_bypass_buck_ctrl_lo(codec, true);
+			break;
+		case WCD9XXX_CLSH_STATE_HPHL:
+			wcd9xxx_clsh_comp_req(codec, clsh_d,
+						CLSH_COMPUTE_HPH_L, false);
+			break;
+		case WCD9XXX_CLSH_STATE_HPHR:
+			wcd9xxx_clsh_comp_req(codec, clsh_d,
+						CLSH_COMPUTE_HPH_R, false);
+			break;
+		default:
+			dev_dbg(codec->dev,
+				 "%s:Invalid state:0x%x,enable:0x%x\n",
+				__func__, req_state, is_enable);
+			break;
+		}
+		if ((req_state == WCD9XXX_CLSH_STATE_LO) ||
+		((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO)) {
+			wcd9xxx_ncp_bypass_enable(codec, false);
+
+			if ((clsh_d->state & (~req_state)) ==
+						WCD9XXX_CLSH_STATE_LO) {
+				wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+							NCP_FCLK_LEVEL_5);
+				wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+							NCP_FCLK_LEVEL_8);
+			}
+
+			if (req_state & WCD9XXX_CLSH_STATE_HPH_ST) {
+				usleep_range(BUCK_SETTLE_TIME_US,
+						BUCK_SETTLE_TIME_US + 10);
+				if (clsh_d->buck_mv ==
+						WCD9XXX_CDC_BUCK_MV_1P8) {
+					wcd9xxx_enable_buck(codec, clsh_d,
+								false);
+					wcd9xxx_ncp_bypass_enable(codec, true);
+				} else {
+					/*
+					 *NCP settle time recommended by codec
+					 *specification
+					 */
+					usleep_range(NCP_SETTLE_TIME_US,
+						NCP_SETTLE_TIME_US + 10);
+					wcd9xxx_clsh_set_Iest(codec, 0x02);
+				}
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_BUCK_MODE_1,
+						0x04, 0x00);
+				snd_soc_update_bits(codec,
+						 WCD9XXX_A_BUCK_MODE_4,
+						0xFF, BUCK_VREF_1P8V);
+			}
+		}
+	}
+}
+
+static void wcd9xxx_clsh_state_ear_lo(struct snd_soc_codec *codec,
+			struct wcd9xxx_clsh_cdc_data *clsh_d,
+			u8 req_state, bool is_enable)
+{
+
+	dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+			is_enable ? "enable" : "disable");
+	if (is_enable) {
+		wcd9xxx_dynamic_bypass_buck_ctrl(codec, false);
+		wcd9xxx_enable_buck(codec, clsh_d, true);
+		wcd9xxx_ncp_bypass_enable(codec, true);
+		if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+			wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+						NCP_FCLK_LEVEL_8);
+			wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+						NCP_FCLK_LEVEL_5);
+			wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+			wcd9xxx_chargepump_request(codec, true);
+			wcd9xxx_enable_anc_delay(codec, true);
+			wcd9xxx_clsh_comp_req(codec, clsh_d,
+						CLSH_COMPUTE_EAR, true);
+		}
+	} else {
+		wcd9xxx_ncp_bypass_enable(codec, false);
+
+		if ((clsh_d->state & (~req_state)) == WCD9XXX_CLSH_STATE_LO) {
+			wcd9xxx_set_fclk_get_ncp(codec, clsh_d,
+						NCP_FCLK_LEVEL_5);
+			wcd9xxx_set_fclk_put_ncp(codec, clsh_d,
+						NCP_FCLK_LEVEL_8);
+		}
+
+		if (req_state & WCD9XXX_CLSH_STATE_LO) {
+			snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+						0x20, 0x00);
+			wcd9xxx_dynamic_bypass_buck_ctrl(codec, true);
+		} else if (req_state & WCD9XXX_CLSH_STATE_EAR) {
+			wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+						false);
+			/*sleep 5ms*/
+			if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+				wcd9xxx_enable_buck(codec, clsh_d, false);
+				wcd9xxx_ncp_bypass_enable(codec, true);
+			} else {
+				/* NCP settle time recommended by codec	spec */
+				usleep_range(NCP_SETTLE_TIME_US,
+					     NCP_SETTLE_TIME_US + 10);
+				wcd9xxx_clsh_set_Iest(codec, 0x02);
+			}
+			snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1,
+						0x04, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_4,
+						0xFF, BUCK_VREF_1P8V);
+		}
+	}
+}
+
+static void wcd9xxx_clsh_state_hph_ear_lo(struct snd_soc_codec *codec,
+			struct wcd9xxx_clsh_cdc_data *clsh_d,
+			u8 req_state, bool is_enable)
+{
+	dev_dbg(codec->dev, "%s: enter %s\n", __func__,
+			is_enable ? "enable" : "disable");
+
+	if (req_state & WCD9XXX_CLSH_STATE_HPHL)
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L,
+					is_enable);
+
+	if (req_state & WCD9XXX_CLSH_STATE_HPHR)
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R,
+					is_enable);
+
+	if (req_state & WCD9XXX_CLSH_STATE_EAR)
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR,
+					is_enable);
+}
+
+static void wcd9xxx_clsh_state_ear(struct snd_soc_codec *codec,
+			struct wcd9xxx_clsh_cdc_data *clsh_d,
+			u8 req_state, bool is_enable)
+{
+	pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+	if (is_enable) {
+		wcd9xxx_cfg_clsh_param_common(codec);
+		wcd9xxx_cfg_clsh_param_ear(codec);
+		wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+		wcd9xxx_chargepump_request(codec, true);
+		wcd9xxx_enable_anc_delay(codec, true);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, true);
+		wcd9xxx_set_buck_mode(codec, BUCK_VREF_2V);
+		wcd9xxx_enable_buck(codec, clsh_d, true);
+		wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+
+		dev_dbg(codec->dev, "%s: Enabled ear mode class h\n", __func__);
+	} else {
+		dev_dbg(codec->dev, "%s: stub fallback to ear\n", __func__);
+		wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+		wcd9xxx_enable_buck(codec, clsh_d, false);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_EAR, false);
+		wcd9xxx_chargepump_request(codec, false);
+		wcd9xxx_enable_clsh_block(codec, clsh_d, false);
+	}
+}
+
+static void wcd9xxx_clsh_state_hph_l(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable)
+{
+	pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		wcd9xxx_cfg_clsh_param_common(codec);
+		wcd9xxx_cfg_clsh_param_hph(codec);
+		wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+		wcd9xxx_chargepump_request(codec, true);
+		wcd9xxx_enable_anc_delay(codec, true);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
+		wcd9xxx_set_buck_mode(codec, BUCK_VREF_0P494V);
+		wcd9xxx_enable_buck(codec, clsh_d, true);
+		wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+
+		dev_dbg(codec->dev, "%s: Done\n", __func__);
+	} else {
+		wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+		wcd9xxx_enable_buck(codec, clsh_d, false);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, false);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, false);
+		wcd9xxx_enable_clsh_block(codec, clsh_d, false);
+		wcd9xxx_chargepump_request(codec, false);
+	}
+}
+
+static void wcd9xxx_clsh_state_hph_r(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable)
+{
+	pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+
+	if (is_enable) {
+		wcd9xxx_cfg_clsh_param_common(codec);
+		wcd9xxx_cfg_clsh_param_hph(codec);
+		wcd9xxx_enable_clsh_block(codec, clsh_d, true);
+		wcd9xxx_chargepump_request(codec, true);
+		wcd9xxx_enable_anc_delay(codec, true);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, true);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, true);
+		wcd9xxx_set_buck_mode(codec, BUCK_VREF_0P494V);
+		wcd9xxx_enable_buck(codec, clsh_d, true);
+		wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+
+		dev_dbg(codec->dev, "%s: Done\n", __func__);
+	} else {
+		wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_8);
+		wcd9xxx_enable_buck(codec, clsh_d, false);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_L, false);
+		wcd9xxx_clsh_comp_req(codec, clsh_d, CLSH_COMPUTE_HPH_R, false);
+		wcd9xxx_enable_clsh_block(codec, clsh_d, false);
+		wcd9xxx_chargepump_request(codec, false);
+	}
+}
+
+static void wcd9xxx_clsh_state_hph_st(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable)
+{
+	pr_debug("%s: enter %s\n", __func__, is_enable ? "enable" : "disable");
+
+	if (is_enable)
+		dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+	else
+		dev_dbg(codec->dev, "%s: stub fallback to hph_st\n", __func__);
+}
+
+static void wcd9xxx_clsh_state_lo(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable)
+{
+	pr_debug("%s: enter %s, buck_mv %d\n", __func__,
+		 is_enable ? "enable" : "disable", clsh_d->buck_mv);
+
+	if (is_enable) {
+		wcd9xxx_set_buck_mode(codec, BUCK_VREF_1P8V);
+		wcd9xxx_enable_buck(codec, clsh_d, true);
+		wcd9xxx_set_fclk_get_ncp(codec, clsh_d, NCP_FCLK_LEVEL_5);
+
+		if (clsh_d->buck_mv == WCD9XXX_CDC_BUCK_MV_1P8) {
+			wcd9xxx_enable_buck(codec, clsh_d, false);
+			snd_soc_update_bits(codec, WCD9XXX_A_NCP_STATIC,
+					    1 << 4, 1 << 4);
+			/* NCP settle time recommended by codec specification */
+			usleep_range(NCP_SETTLE_TIME_US,
+				     NCP_SETTLE_TIME_US + 10);
+		} else {
+			/* NCP settle time recommended by codec specification */
+			usleep_range(NCP_SETTLE_TIME_US,
+				     NCP_SETTLE_TIME_US + 10);
+			snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+					    0x01, (0x01 & 0x03));
+			snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_5,
+					    0xFC, (0xFC & 0xB));
+		}
+		snd_soc_update_bits(codec, WCD9XXX_A_BUCK_MODE_1, 0x04, 0x00);
+	} else {
+		dev_dbg(codec->dev, "%s: stub fallback to lineout\n", __func__);
+		wcd9xxx_set_fclk_put_ncp(codec, clsh_d, NCP_FCLK_LEVEL_5);
+		if (clsh_d->buck_mv != WCD9XXX_CDC_BUCK_MV_1P8)
+			wcd9xxx_enable_buck(codec, clsh_d, false);
+	}
+}
+
+static void wcd9xxx_clsh_state_err(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *clsh_d,
+		u8 req_state, bool is_enable)
+{
+	char msg[128];
+
+	dev_dbg(codec->dev,
+		"%s Wrong request for class H state machine requested to %s %s",
+		__func__, is_enable ? "enable" : "disable",
+		state_to_str(req_state, msg, sizeof(msg)));
+	WARN_ON(1);
+}
+
+/*
+ * Function: wcd9xxx_clsh_is_state_valid
+ * Params: state
+ * Description:
+ * Provides information on valid states of Class H configuration
+ */
+static int wcd9xxx_clsh_is_state_valid(u8 state)
+{
+	switch (state) {
+	case WCD9XXX_CLSH_STATE_IDLE:
+	case WCD9XXX_CLSH_STATE_EAR:
+	case WCD9XXX_CLSH_STATE_HPHL:
+	case WCD9XXX_CLSH_STATE_HPHR:
+	case WCD9XXX_CLSH_STATE_HPH_ST:
+	case WCD9XXX_CLSH_STATE_LO:
+	case WCD9XXX_CLSH_STATE_HPHL_EAR:
+	case WCD9XXX_CLSH_STATE_HPHR_EAR:
+	case WCD9XXX_CLSH_STATE_HPH_ST_EAR:
+	case WCD9XXX_CLSH_STATE_HPHL_LO:
+	case WCD9XXX_CLSH_STATE_HPHR_LO:
+	case WCD9XXX_CLSH_STATE_HPH_ST_LO:
+	case WCD9XXX_CLSH_STATE_EAR_LO:
+	case WCD9XXX_CLSH_STATE_HPHL_EAR_LO:
+	case WCD9XXX_CLSH_STATE_HPHR_EAR_LO:
+	case WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO:
+		return 1;
+	default:
+		break;
+	}
+	return 0;
+}
+
+/*
+ * Function: wcd9xxx_clsh_fsm
+ * Params: codec, cdc_clsh_d, req_state, req_type, clsh_event
+ * Description:
+ * This function handles PRE DAC and POST DAC conditions of different devices
+ * and updates class H configuration of different combination of devices
+ * based on validity of their states. cdc_clsh_d will contain current
+ * class h state information
+ */
+void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
+		u8 req_state, bool req_type, u8 clsh_event)
+{
+	u8 old_state, new_state;
+	char msg0[128], msg1[128];
+
+	switch (clsh_event) {
+	case WCD9XXX_CLSH_EVENT_PRE_DAC:
+		/* PRE_DAC event should be used only for Enable */
+		BUG_ON(req_type != WCD9XXX_CLSH_REQ_ENABLE);
+
+		old_state = cdc_clsh_d->state;
+		new_state = old_state | req_state;
+
+		if (!wcd9xxx_clsh_is_state_valid(new_state)) {
+			dev_dbg(codec->dev,
+				"%s: classH not a valid new state: %s\n",
+				__func__,
+				state_to_str(new_state, msg0, sizeof(msg0)));
+			return;
+		}
+		if (new_state == old_state) {
+			dev_dbg(codec->dev,
+				"%s: classH already in requested state: %s\n",
+				__func__,
+				state_to_str(new_state, msg0, sizeof(msg0)));
+			return;
+		}
+		(*clsh_state_fp[new_state]) (codec, cdc_clsh_d, req_state,
+					     req_type);
+		cdc_clsh_d->state = new_state;
+		dev_dbg(codec->dev,
+			"%s: ClassH state transition from %s to %s\n",
+			__func__, state_to_str(old_state, msg0, sizeof(msg0)),
+			state_to_str(cdc_clsh_d->state, msg1, sizeof(msg1)));
+
+		break;
+	case WCD9XXX_CLSH_EVENT_POST_PA:
+		if (req_type == WCD9XXX_CLSH_REQ_DISABLE) {
+			old_state = cdc_clsh_d->state;
+			new_state = old_state & (~req_state);
+
+			if (new_state < NUM_CLSH_STATES) {
+				if (!wcd9xxx_clsh_is_state_valid(old_state)) {
+					dev_dbg(codec->dev,
+						"%s:Invalid old state:%s\n",
+						__func__,
+						state_to_str(old_state, msg0,
+						sizeof(msg0)));
+					return;
+				}
+				if (new_state == old_state) {
+					dev_dbg(codec->dev,
+					"%s: clsH already in old state: %s\n",
+					__func__,
+					state_to_str(new_state, msg0,
+					sizeof(msg0)));
+					return;
+				}
+				(*clsh_state_fp[old_state]) (codec, cdc_clsh_d,
+							     req_state,
+							     req_type);
+				cdc_clsh_d->state = new_state;
+				dev_dbg(codec->dev, "%s: ClassH state transition from %s to %s\n",
+					__func__, state_to_str(old_state, msg0,
+							       sizeof(msg0)),
+					state_to_str(cdc_clsh_d->state, msg1,
+						     sizeof(msg1)));
+
+			} else {
+				dev_dbg(codec->dev, "%s:wrong new state=0x%x\n",
+						__func__, new_state);
+			}
+		} else if (!(cdc_clsh_d->state & WCD9XXX_CLSH_STATE_LO)) {
+			wcd9xxx_clsh_enable_post_pa(codec, cdc_clsh_d);
+		}
+
+		break;
+	}
+
+}
+EXPORT_SYMBOL(wcd9xxx_clsh_fsm);
+
+void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
+		       struct wcd9xxx_resmgr *resmgr)
+{
+	int i;
+
+	clsh->state = WCD9XXX_CLSH_STATE_IDLE;
+	clsh->resmgr = resmgr;
+
+	for (i = 0; i < NUM_CLSH_STATES; i++)
+		clsh_state_fp[i] = wcd9xxx_clsh_state_err;
+
+	clsh_state_fp[WCD9XXX_CLSH_STATE_EAR] = wcd9xxx_clsh_state_ear;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL] =
+						wcd9xxx_clsh_state_hph_l;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR] =
+						wcd9xxx_clsh_state_hph_r;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST] =
+						wcd9xxx_clsh_state_hph_st;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_LO] = wcd9xxx_clsh_state_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR] =
+						wcd9xxx_clsh_state_hph_ear;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR] =
+						wcd9xxx_clsh_state_hph_ear;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR] =
+						wcd9xxx_clsh_state_hph_ear;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_LO] = wcd9xxx_clsh_state_hph_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_LO] = wcd9xxx_clsh_state_hph_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_LO] =
+						wcd9xxx_clsh_state_hph_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_EAR_LO] = wcd9xxx_clsh_state_ear_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHL_EAR_LO] =
+						wcd9xxx_clsh_state_hph_ear_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPHR_EAR_LO] =
+						wcd9xxx_clsh_state_hph_ear_lo;
+	clsh_state_fp[WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO] =
+						wcd9xxx_clsh_state_hph_ear_lo;
+
+}
+EXPORT_SYMBOL(wcd9xxx_clsh_init);
+
+MODULE_DESCRIPTION("WCD9XXX Common");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-common.h b/sound/soc/codecs/wcd9xxx-common.h
new file mode 100644
index 0000000..5c0c4a9
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-common.h
@@ -0,0 +1,286 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WCD9XXX_CODEC_COMMON
+
+#define WCD9XXX_CODEC_COMMON
+
+#include "wcd9xxx-resmgr.h"
+
+#define WCD9XXX_CLSH_REQ_ENABLE true
+#define WCD9XXX_CLSH_REQ_DISABLE false
+
+#define WCD9XXX_CLSH_EVENT_PRE_DAC 0x01
+#define WCD9XXX_CLSH_EVENT_POST_PA 0x02
+
+/* Basic states for Class H state machine.
+ * represented as a bit mask within a u8 data type
+ * bit 0: EAR mode
+ * bit 1: HPH Left mode
+ * bit 2: HPH Right mode
+ * bit 3: Lineout mode
+ * bit 4: Ultrasound mode
+ */
+#define	WCD9XXX_CLSH_STATE_IDLE 0x00
+#define	WCD9XXX_CLSH_STATE_EAR (0x01 << 0)
+#define	WCD9XXX_CLSH_STATE_HPHL (0x01 << 1)
+#define	WCD9XXX_CLSH_STATE_HPHR (0x01 << 2)
+#define	WCD9XXX_CLSH_STATE_LO (0x01 << 3)
+#define NUM_CLSH_STATES (0x01 << 4)
+
+#define	WCD9XXX_CLSAB_STATE_IDLE  0x00
+#define WCD9XXX_CLSAB_STATE_HPHL (0x01 << 1)
+#define WCD9XXX_CLSAB_STATE_HPHR (0x01 << 2)
+
+#define WCD9XXX_CLSAB_REQ_ENABLE  true
+#define WCD9XXX_CLSAB_REQ_DISABLE false
+
+#define WCD9XXX_NON_UHQA_MODE	0
+
+#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_2    0x0
+#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_3    0x1
+#define WCD9XXX_DMIC_SAMPLE_RATE_DIV_4    0x2
+
+#define WCD9XXX_DMIC_B1_CTL_DIV_2 0x00
+#define WCD9XXX_DMIC_B1_CTL_DIV_3 0x22
+#define WCD9XXX_DMIC_B1_CTL_DIV_4 0x44
+
+#define WCD9XXX_DMIC_B2_CTL_DIV_2 0x00
+#define WCD9XXX_DMIC_B2_CTL_DIV_3 0x02
+#define WCD9XXX_DMIC_B2_CTL_DIV_4 0x04
+
+#define WCD9XXX_ANC_DMIC_X2_ON    0x1
+#define WCD9XXX_ANC_DMIC_X2_OFF   0x0
+
+/* Derived State: Bits 1 and 2 should be set for Headphone stereo */
+#define WCD9XXX_CLSH_STATE_HPH_ST (WCD9XXX_CLSH_STATE_HPHL | \
+						WCD9XXX_CLSH_STATE_HPHR)
+
+#define WCD9XXX_CLSH_STATE_HPHL_EAR (WCD9XXX_CLSH_STATE_HPHL | \
+						WCD9XXX_CLSH_STATE_EAR)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR (WCD9XXX_CLSH_STATE_HPHR | \
+						WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR (WCD9XXX_CLSH_STATE_HPH_ST | \
+						WCD9XXX_CLSH_STATE_EAR)
+
+#define WCD9XXX_CLSH_STATE_HPHL_LO (WCD9XXX_CLSH_STATE_HPHL | \
+						WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+						WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPH_ST_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+						WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_EAR_LO (WCD9XXX_CLSH_STATE_EAR | \
+						WCD9XXX_CLSH_STATE_LO)
+
+#define WCD9XXX_CLSH_STATE_HPHL_EAR_LO (WCD9XXX_CLSH_STATE_HPHL | \
+						WCD9XXX_CLSH_STATE_EAR | \
+						WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPHR_EAR_LO (WCD9XXX_CLSH_STATE_HPHR | \
+						WCD9XXX_CLSH_STATE_EAR | \
+						WCD9XXX_CLSH_STATE_LO)
+#define WCD9XXX_CLSH_STATE_HPH_ST_EAR_LO (WCD9XXX_CLSH_STATE_HPH_ST | \
+						WCD9XXX_CLSH_STATE_EAR | \
+						WCD9XXX_CLSH_STATE_LO)
+
+struct wcd9xxx_reg_mask_val {
+	u16	reg;
+	u8	mask;
+	u8	val;
+};
+
+enum ncp_fclk_level {
+	NCP_FCLK_LEVEL_8,
+	NCP_FCLK_LEVEL_5,
+	NCP_FCLK_LEVEL_MAX,
+};
+
+/* Class H data that the codec driver will maintain */
+struct wcd9xxx_clsh_cdc_data {
+	u8 state;
+	int buck_mv;
+	bool is_dynamic_vdd_cp;
+	int clsh_users;
+	int buck_users;
+	int ncp_users[NCP_FCLK_LEVEL_MAX];
+	struct wcd9xxx_resmgr *resmgr;
+};
+
+struct wcd9xxx_anc_header {
+	u32 reserved[3];
+	u32 num_anc_slots;
+};
+
+enum wcd9xxx_buck_volt {
+	WCD9XXX_CDC_BUCK_UNSUPPORTED = 0,
+	WCD9XXX_CDC_BUCK_MV_1P8 = 1800000,
+	WCD9XXX_CDC_BUCK_MV_2P15 = 2150000,
+};
+
+struct mad_audio_header {
+	u32 reserved[3];
+	u32 num_reg_cfg;
+};
+
+struct mad_microphone_info {
+	uint8_t input_microphone;
+	uint8_t cycle_time;
+	uint8_t settle_time;
+	uint8_t padding;
+} __packed;
+
+struct mad_micbias_info {
+	uint8_t micbias;
+	uint8_t k_factor;
+	uint8_t external_bypass_capacitor;
+	uint8_t internal_biasing;
+	uint8_t cfilter;
+	uint8_t padding[3];
+} __packed;
+
+struct mad_rms_audio_beacon_info {
+	uint8_t rms_omit_samples;
+	uint8_t rms_comp_time;
+	uint8_t detection_mechanism;
+	uint8_t rms_diff_threshold;
+	uint8_t rms_threshold_lsb;
+	uint8_t rms_threshold_msb;
+	uint8_t padding[2];
+	uint8_t iir_coefficients[36];
+} __packed;
+
+struct mad_rms_ultrasound_info {
+	uint8_t rms_comp_time;
+	uint8_t detection_mechanism;
+	uint8_t rms_diff_threshold;
+	uint8_t rms_threshold_lsb;
+	uint8_t rms_threshold_msb;
+	uint8_t padding[3];
+	uint8_t iir_coefficients[36];
+} __packed;
+
+struct mad_audio_cal {
+	uint32_t version;
+	struct mad_microphone_info microphone_info;
+	struct mad_micbias_info micbias_info;
+	struct mad_rms_audio_beacon_info audio_info;
+	struct mad_rms_audio_beacon_info beacon_info;
+	struct mad_rms_ultrasound_info ultrasound_info;
+} __packed;
+
+extern void wcd9xxx_clsh_fsm(struct snd_soc_codec *codec,
+		struct wcd9xxx_clsh_cdc_data *cdc_clsh_d,
+		u8 req_state, bool req_type, u8 clsh_event);
+
+extern void wcd9xxx_enable_high_perf_mode(struct snd_soc_codec *codec,
+				struct wcd9xxx_clsh_cdc_data *clsh_d,
+				u8 uhqa_mode, u8 req_state, bool req_type);
+
+extern void wcd9xxx_clsh_init(struct wcd9xxx_clsh_cdc_data *clsh,
+			      struct wcd9xxx_resmgr *resmgr);
+
+extern void wcd9xxx_clsh_imped_config(struct snd_soc_codec *codec,
+				  int imped);
+
+enum wcd9xxx_codec_event {
+	WCD9XXX_CODEC_EVENT_CODEC_UP = 0,
+};
+
+struct wcd9xxx_register_save_node {
+	struct list_head lh;
+	u16 reg;
+	u16 value;
+};
+
+extern int wcd9xxx_soc_update_bits_push(struct snd_soc_codec *codec,
+					struct list_head *lh,
+					uint16_t reg, uint8_t mask,
+					uint8_t value, int delay);
+extern void wcd9xxx_restore_registers(struct snd_soc_codec *codec,
+				      struct list_head *lh);
+enum {
+	RESERVED = 0,
+	AANC_LPF_FF_FB = 1,
+	AANC_LPF_COEFF_MSB,
+	AANC_LPF_COEFF_LSB,
+	HW_MAD_AUDIO_ENABLE,
+	HW_MAD_ULTR_ENABLE,
+	HW_MAD_BEACON_ENABLE,
+	HW_MAD_AUDIO_SLEEP_TIME,
+	HW_MAD_ULTR_SLEEP_TIME,
+	HW_MAD_BEACON_SLEEP_TIME,
+	HW_MAD_TX_AUDIO_SWITCH_OFF,
+	HW_MAD_TX_ULTR_SWITCH_OFF,
+	HW_MAD_TX_BEACON_SWITCH_OFF,
+	MAD_AUDIO_INT_DEST_SELECT_REG,
+	MAD_ULT_INT_DEST_SELECT_REG,
+	MAD_BEACON_INT_DEST_SELECT_REG,
+	MAD_CLIP_INT_DEST_SELECT_REG,
+	MAD_VBAT_INT_DEST_SELECT_REG,
+	MAD_AUDIO_INT_MASK_REG,
+	MAD_ULT_INT_MASK_REG,
+	MAD_BEACON_INT_MASK_REG,
+	MAD_CLIP_INT_MASK_REG,
+	MAD_VBAT_INT_MASK_REG,
+	MAD_AUDIO_INT_STATUS_REG,
+	MAD_ULT_INT_STATUS_REG,
+	MAD_BEACON_INT_STATUS_REG,
+	MAD_CLIP_INT_STATUS_REG,
+	MAD_VBAT_INT_STATUS_REG,
+	MAD_AUDIO_INT_CLEAR_REG,
+	MAD_ULT_INT_CLEAR_REG,
+	MAD_BEACON_INT_CLEAR_REG,
+	MAD_CLIP_INT_CLEAR_REG,
+	MAD_VBAT_INT_CLEAR_REG,
+	SB_PGD_PORT_TX_WATERMARK_N,
+	SB_PGD_PORT_TX_ENABLE_N,
+	SB_PGD_PORT_RX_WATERMARK_N,
+	SB_PGD_PORT_RX_ENABLE_N,
+	SB_PGD_TX_PORTn_MULTI_CHNL_0,
+	SB_PGD_TX_PORTn_MULTI_CHNL_1,
+	SB_PGD_RX_PORTn_MULTI_CHNL_0,
+	SB_PGD_RX_PORTn_MULTI_CHNL_1,
+	AANC_FF_GAIN_ADAPTIVE,
+	AANC_FFGAIN_ADAPTIVE_EN,
+	AANC_GAIN_CONTROL,
+	SPKR_CLIP_PIPE_BANK_SEL,
+	SPKR_CLIPDET_VAL0,
+	SPKR_CLIPDET_VAL1,
+	SPKR_CLIPDET_VAL2,
+	SPKR_CLIPDET_VAL3,
+	SPKR_CLIPDET_VAL4,
+	SPKR_CLIPDET_VAL5,
+	SPKR_CLIPDET_VAL6,
+	SPKR_CLIPDET_VAL7,
+	VBAT_RELEASE_INT_DEST_SELECT_REG,
+	VBAT_RELEASE_INT_MASK_REG,
+	VBAT_RELEASE_INT_STATUS_REG,
+	VBAT_RELEASE_INT_CLEAR_REG,
+	MAD2_CLIP_INT_DEST_SELECT_REG,
+	MAD2_CLIP_INT_MASK_REG,
+	MAD2_CLIP_INT_STATUS_REG,
+	MAD2_CLIP_INT_CLEAR_REG,
+	SPKR2_CLIP_PIPE_BANK_SEL,
+	SPKR2_CLIPDET_VAL0,
+	SPKR2_CLIPDET_VAL1,
+	SPKR2_CLIPDET_VAL2,
+	SPKR2_CLIPDET_VAL3,
+	SPKR2_CLIPDET_VAL4,
+	SPKR2_CLIPDET_VAL5,
+	SPKR2_CLIPDET_VAL6,
+	SPKR2_CLIPDET_VAL7,
+	MAX_CFG_REGISTERS,
+};
+
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.c b/sound/soc/codecs/wcd9xxx-mbhc.c
new file mode 100644
index 0000000..3754b57
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.c
@@ -0,0 +1,5671 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include "wcd9xxx-mbhc.h"
+#include "wcdcal-hwdep.h"
+#include "wcd9xxx-resmgr.h"
+#include "wcd9xxx-common.h"
+
+#define WCD9XXX_JACK_MASK (SND_JACK_HEADSET | SND_JACK_OC_HPHL | \
+			   SND_JACK_OC_HPHR | SND_JACK_LINEOUT | \
+			   SND_JACK_UNSUPPORTED | SND_JACK_MICROPHONE2 | \
+			   SND_JACK_MECHANICAL)
+#define WCD9XXX_JACK_BUTTON_MASK (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+				  SND_JACK_BTN_2 | SND_JACK_BTN_3 | \
+				  SND_JACK_BTN_4 | SND_JACK_BTN_5)
+
+#define NUM_DCE_PLUG_DETECT 3
+#define NUM_DCE_PLUG_INS_DETECT 5
+#define NUM_ATTEMPTS_INSERT_DETECT 25
+#define NUM_ATTEMPTS_TO_REPORT 5
+
+#define FAKE_INS_LOW 10
+#define FAKE_INS_HIGH 80
+#define FAKE_INS_HIGH_NO_SWCH 150
+#define FAKE_REMOVAL_MIN_PERIOD_MS 50
+#define FAKE_INS_DELTA_SCALED_MV 300
+
+#define BUTTON_MIN 0x8000
+#define STATUS_REL_DETECTION 0x0C
+
+#define HS_DETECT_PLUG_TIME_MS (5 * 1000)
+#define ANC_HPH_DETECT_PLUG_TIME_MS (5 * 1000)
+#define HS_DETECT_PLUG_INERVAL_MS 100
+#define SWCH_REL_DEBOUNCE_TIME_MS 50
+#define SWCH_IRQ_DEBOUNCE_TIME_US 5000
+#define BTN_RELEASE_DEBOUNCE_TIME_MS 25
+
+#define GND_MIC_SWAP_THRESHOLD 2
+#define OCP_ATTEMPT 1
+
+#define FW_READ_ATTEMPTS 15
+#define FW_READ_TIMEOUT 4000000
+
+#define BUTTON_POLLING_SUPPORTED true
+
+#define MCLK_RATE_12288KHZ 12288000
+#define MCLK_RATE_9600KHZ 9600000
+
+#define DEFAULT_DCE_STA_WAIT 55
+#define DEFAULT_DCE_WAIT 60000
+#define DEFAULT_STA_WAIT 5000
+
+#define VDDIO_MICBIAS_MV 1800
+
+#define WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US 5000
+
+#define WCD9XXX_HPHL_STATUS_READY_WAIT_US 1000
+#define WCD9XXX_MUX_SWITCH_READY_WAIT_MS 50
+#define WCD9XXX_MEAS_DELTA_MAX_MV 120
+#define WCD9XXX_MEAS_INVALD_RANGE_LOW_MV 20
+#define WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV 80
+
+/* Threshold in milliohm used for mono/stereo
+ * plug classification
+ */
+#define WCD9XXX_MONO_HS_DIFF_THR 20000000
+#define WCD9XXX_MONO_HS_MIN_THR 2000
+
+/*
+ * Invalid voltage range for the detection
+ * of plug type with current source
+ */
+#define WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV 160
+#define WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV 265
+
+/*
+ * Threshold used to detect euro headset
+ * with current source
+ */
+#define WCD9XXX_CS_GM_SWAP_THRES_MIN_MV 10
+#define WCD9XXX_CS_GM_SWAP_THRES_MAX_MV 40
+
+#define WCD9XXX_MBHC_NSC_CS 9
+#define WCD9XXX_GM_SWAP_THRES_MIN_MV 150
+#define WCD9XXX_GM_SWAP_THRES_MAX_MV 650
+#define WCD9XXX_THRESHOLD_MIC_THRESHOLD 200
+
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+
+/* RX_HPH_CNP_WG_TIME increases by 0.24ms */
+#define WCD9XXX_WG_TIME_FACTOR_US	240
+
+#define WCD9XXX_V_CS_HS_MAX 500
+#define WCD9XXX_V_CS_NO_MIC 5
+#define WCD9XXX_MB_MEAS_DELTA_MAX_MV 80
+#define WCD9XXX_CS_MEAS_DELTA_MAX_MV 12
+
+#define WCD9XXX_ZDET_ZONE_1 80000
+#define WCD9XXX_ZDET_ZONE_2 800000
+
+#define WCD9XXX_IS_IN_ZDET_ZONE_1(x) (x < WCD9XXX_ZDET_ZONE_1 ? 1 : 0)
+#define WCD9XXX_IS_IN_ZDET_ZONE_2(x) ((x > WCD9XXX_ZDET_ZONE_1 && \
+				x < WCD9XXX_ZDET_ZONE_2) ? 1 : 0)
+#define WCD9XXX_IS_IN_ZDET_ZONE_3(x) (x > WCD9XXX_ZDET_ZONE_2 ? 1 : 0)
+#define WCD9XXX_BOX_CAR_AVRG_MIN 1
+#define WCD9XXX_BOX_CAR_AVRG_MAX 10
+
+/*
+ * Need to report LINEIN if H/L impedance
+ * is larger than 5K ohm
+ */
+#define WCD9XXX_LINEIN_THRESHOLD 5000000
+
+static int impedance_detect_en;
+module_param(impedance_detect_en, int, 0664);
+MODULE_PARM_DESC(impedance_detect_en, "enable/disable impedance detect");
+static unsigned int z_det_box_car_avg = 1;
+module_param(z_det_box_car_avg, int, 0664);
+MODULE_PARM_DESC(z_det_box_car_avg,
+		 "Number of samples for impedance detection");
+
+static bool detect_use_vddio_switch;
+
+struct wcd9xxx_mbhc_detect {
+	u16 dce;
+	u16 sta;
+	u16 hphl_status;
+	bool swap_gnd;
+	bool vddio;
+	bool hwvalue;
+	bool mic_bias;
+	/* internal purpose from here */
+	bool _above_no_mic;
+	bool _below_v_hs_max;
+	s16 _vdces;
+	enum wcd9xxx_mbhc_plug_type _type;
+};
+
+enum meas_type {
+	STA = 0,
+	DCE,
+};
+
+enum {
+	MBHC_USE_HPHL_TRIGGER = 1,
+	MBHC_USE_MB_TRIGGER = 2
+};
+
+/*
+ * Flags to track of PA and DAC state.
+ * PA and DAC should be tracked separately as AUXPGA loopback requires
+ * only PA to be turned on without DAC being on.
+ */
+enum pa_dac_ack_flags {
+	WCD9XXX_HPHL_PA_OFF_ACK = 0,
+	WCD9XXX_HPHR_PA_OFF_ACK,
+	WCD9XXX_HPHL_DAC_OFF_ACK,
+	WCD9XXX_HPHR_DAC_OFF_ACK
+};
+
+enum wcd9xxx_current_v_idx {
+	WCD9XXX_CURRENT_V_INS_H,
+	WCD9XXX_CURRENT_V_INS_HU,
+	WCD9XXX_CURRENT_V_B1_H,
+	WCD9XXX_CURRENT_V_B1_HU,
+	WCD9XXX_CURRENT_V_BR_H,
+};
+
+static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+				    uint32_t *zr);
+static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
+				 const enum wcd9xxx_current_v_idx idx);
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
+			  struct mbhc_micbias_regs *micb_regs,
+			  bool norel);
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc);
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+				   enum meas_type dce, s16 vin_mv,
+				   bool cs_enable);
+
+static bool wcd9xxx_mbhc_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	return snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_EN_CTL) & 0x1;
+}
+
+static void wcd9xxx_turn_onoff_override(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+			    0x04, on ? 0x04 : 0x00);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_pause_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->polling_active) {
+		pr_debug("polling not active, nothing to pause\n");
+		return;
+	}
+
+	/* Soft reset MBHC block */
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_start_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int mbhc_state = mbhc->mbhc_state;
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->polling_active) {
+		pr_debug("Polling is not active, do not start polling\n");
+		return;
+	}
+
+	/*
+	 * setup internal micbias if codec uses internal micbias for
+	 * headset detection
+	 */
+	if (mbhc->mbhc_cfg->use_int_rbias) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
+			mbhc->mbhc_cb->setup_int_rbias(codec, true);
+		else
+			pr_err("%s: internal bias requested but codec did not provide callback\n",
+				__func__);
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+		mbhc->mbhc_cb->enable_mux_bias_block(codec);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0x80, 0x80);
+
+	if (!mbhc->no_mic_headset_override &&
+	    mbhc_state == MBHC_STATE_POTENTIAL) {
+		pr_debug("%s recovering MBHC state machine\n", __func__);
+		mbhc->mbhc_state = MBHC_STATE_POTENTIAL_RECOVERY;
+		/* set to max button press threshold */
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, 0xFF);
+		/* set to max */
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL, 0x7F);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, 0xFF);
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x0);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x1);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static int __wcd9xxx_resmgr_get_k_val(struct wcd9xxx_mbhc *mbhc,
+		unsigned int cfilt_mv)
+{
+	return wcd9xxx_resmgr_get_k_val(mbhc->resmgr, cfilt_mv);
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * return old status
+ */
+static bool __wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc,
+				     int vddio_switch, bool restartpolling,
+				     bool checkpolling)
+{
+	bool ret;
+	int cfilt_k_val;
+	bool override;
+	struct snd_soc_codec *codec;
+	struct mbhc_internal_cal_data *d = &mbhc->mbhc_data;
+
+	codec = mbhc->codec;
+
+	if (mbhc->micbias_enable) {
+		pr_debug("%s: micbias is already on\n", __func__);
+		ret = mbhc->mbhc_micbias_switched;
+		return ret;
+	}
+
+	ret = mbhc->mbhc_micbias_switched;
+	if (vddio_switch && !mbhc->mbhc_micbias_switched &&
+	    (!checkpolling || mbhc->polling_active)) {
+		if (restartpolling)
+			wcd9xxx_pause_hs_polling(mbhc);
+		override = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+			   0x04;
+		if (!override)
+			wcd9xxx_turn_onoff_override(mbhc, true);
+
+		snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    0x10, 0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1,
+				    0x20, 0x00);
+		/* Adjust threshold if Mic Bias voltage changes */
+		if (d->micb_mv != VDDIO_MICBIAS_MV) {
+			cfilt_k_val = __wcd9xxx_resmgr_get_k_val(mbhc,
+							      VDDIO_MICBIAS_MV);
+			usleep_range(10000, 10100);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.cfilt_val,
+					0xFC, (cfilt_k_val << 2));
+			usleep_range(10000, 10100);
+			/* Threshods for insertion/removal */
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+				      d->v_ins_hu[MBHC_V_IDX_VDDIO] & 0xFF);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+				      (d->v_ins_hu[MBHC_V_IDX_VDDIO] >> 8) &
+				      0xFF);
+
+			if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+				/* Threshods for button press */
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+					d->v_b1_hu[MBHC_V_IDX_VDDIO] & 0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+					(d->v_b1_hu[MBHC_V_IDX_VDDIO] >> 8) &
+					0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+					d->v_b1_h[MBHC_V_IDX_VDDIO] & 0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+					(d->v_b1_h[MBHC_V_IDX_VDDIO] >> 8) &
+					0xFF);
+				/* Threshods for button release */
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+					d->v_brh[MBHC_V_IDX_VDDIO] & 0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+					(d->v_brh[MBHC_V_IDX_VDDIO] >> 8) &
+					0xFF);
+			}
+			pr_debug("%s: Programmed MBHC thresholds to VDDIO\n",
+				 __func__);
+		}
+
+		/* Enable MIC BIAS Switch to VDDIO */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x80, 0x80);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x10, 0x00);
+		if (!override)
+			wcd9xxx_turn_onoff_override(mbhc, false);
+		if (restartpolling)
+			wcd9xxx_start_hs_polling(mbhc);
+
+		mbhc->mbhc_micbias_switched = true;
+		pr_debug("%s: VDDIO switch enabled\n", __func__);
+	} else if (!vddio_switch && mbhc->mbhc_micbias_switched) {
+		if ((!checkpolling || mbhc->polling_active) &&
+		    restartpolling)
+			wcd9xxx_pause_hs_polling(mbhc);
+
+			snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL,
+					    0x10, 0x10);
+			snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1,
+					    0x20, 0x20);
+		/* Reprogram thresholds */
+		if (d->micb_mv != VDDIO_MICBIAS_MV) {
+			cfilt_k_val =
+			    __wcd9xxx_resmgr_get_k_val(mbhc,
+						     d->micb_mv);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.cfilt_val,
+					0xFC, (cfilt_k_val << 2));
+			usleep_range(10000, 10100);
+			/* Revert threshods for insertion/removal */
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL,
+					d->v_ins_hu[MBHC_V_IDX_CFILT] & 0xFF);
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+					(d->v_ins_hu[MBHC_V_IDX_CFILT] >> 8) &
+					0xFF);
+			if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+				/* Revert threshods for button press */
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL,
+					d->v_b1_hu[MBHC_V_IDX_CFILT] & 0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+					(d->v_b1_hu[MBHC_V_IDX_CFILT] >> 8) &
+					0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL,
+					d->v_b1_h[MBHC_V_IDX_CFILT] & 0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+					(d->v_b1_h[MBHC_V_IDX_CFILT] >> 8) &
+					0xFF);
+				/* Revert threshods for button release */
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL,
+					d->v_brh[MBHC_V_IDX_CFILT] & 0xFF);
+				snd_soc_write(codec,
+					WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+					(d->v_brh[MBHC_V_IDX_CFILT] >> 8) &
+					0xFF);
+			}
+			pr_debug("%s: Programmed MBHC thresholds to MICBIAS\n",
+					__func__);
+		}
+
+		/* Disable MIC BIAS Switch to VDDIO */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x80,
+				    0x00);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x10,
+				    0x00);
+
+		if ((!checkpolling || mbhc->polling_active) && restartpolling)
+			wcd9xxx_start_hs_polling(mbhc);
+
+		mbhc->mbhc_micbias_switched = false;
+		pr_debug("%s: VDDIO switch disabled\n", __func__);
+	}
+
+	return ret;
+}
+
+static void wcd9xxx_switch_micbias(struct wcd9xxx_mbhc *mbhc, int vddio_switch)
+{
+	__wcd9xxx_switch_micbias(mbhc, vddio_switch, true, true);
+}
+
+static s16 wcd9xxx_get_current_v(struct wcd9xxx_mbhc *mbhc,
+				 const enum wcd9xxx_current_v_idx idx)
+{
+	enum mbhc_v_index vidx;
+	s16 ret = -EINVAL;
+
+	if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+	    mbhc->mbhc_micbias_switched)
+		vidx = MBHC_V_IDX_VDDIO;
+	else
+		vidx = MBHC_V_IDX_CFILT;
+
+	switch (idx) {
+	case WCD9XXX_CURRENT_V_INS_H:
+		ret = (s16)mbhc->mbhc_data.v_ins_h[vidx];
+		break;
+	case WCD9XXX_CURRENT_V_INS_HU:
+		ret = (s16)mbhc->mbhc_data.v_ins_hu[vidx];
+		break;
+	case WCD9XXX_CURRENT_V_B1_H:
+		ret = (s16)mbhc->mbhc_data.v_b1_h[vidx];
+		break;
+	case WCD9XXX_CURRENT_V_B1_HU:
+		ret = (s16)mbhc->mbhc_data.v_b1_hu[vidx];
+		break;
+	case WCD9XXX_CURRENT_V_BR_H:
+		ret = (s16)mbhc->mbhc_data.v_brh[vidx];
+		break;
+	}
+
+	return ret;
+}
+
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+			    const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+			    const enum wcd9xxx_mbhc_btn_det_mem mem)
+{
+	void *ret = (void *)&btn_det->_v_btn_low;
+
+	switch (mem) {
+	case MBHC_BTN_DET_GAIN:
+		ret += sizeof(btn_det->_n_cic);
+		/* fallthrough */
+	case MBHC_BTN_DET_N_CIC:
+		ret += sizeof(btn_det->_n_ready);
+		/* fallthrough */
+	case MBHC_BTN_DET_N_READY:
+		ret += sizeof(btn_det->_v_btn_high[0]) * btn_det->num_btn;
+		/* fallthrough */
+	case MBHC_BTN_DET_V_BTN_HIGH:
+		ret += sizeof(btn_det->_v_btn_low[0]) * btn_det->num_btn;
+		/* fallthrough */
+	case MBHC_BTN_DET_V_BTN_LOW:
+		/* do nothing */
+		break;
+	default:
+		ret = NULL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_cal_btn_det_mp);
+
+static void wcd9xxx_calibrate_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const s16 v_ins_hu = wcd9xxx_get_current_v(mbhc,
+						   WCD9XXX_CURRENT_V_INS_HU);
+	const s16 v_b1_hu = wcd9xxx_get_current_v(mbhc,
+						  WCD9XXX_CURRENT_V_B1_HU);
+	const s16 v_b1_h = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+	const s16 v_brh = wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B1_CTL, v_ins_hu & 0xFF);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B2_CTL,
+		      (v_ins_hu >> 8) & 0xFF);
+
+	if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
+				0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+				(v_b1_hu >> 8) & 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B5_CTL, v_b1_h &
+				0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B6_CTL,
+				(v_b1_h >> 8) & 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
+				0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+				(v_brh >> 8) & 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B11_CTL,
+				mbhc->mbhc_data.v_brl & 0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B12_CTL,
+				(mbhc->mbhc_data.v_brl >> 8) & 0xFF);
+	}
+}
+
+static void wcd9xxx_codec_switch_cfilt_mode(struct wcd9xxx_mbhc *mbhc,
+					    bool fast)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx_cfilt_mode cfilt_mode;
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->switch_cfilt_mode) {
+		cfilt_mode = mbhc->mbhc_cb->switch_cfilt_mode(mbhc, fast);
+	} else {
+		if (fast)
+			cfilt_mode.reg_mode_val = WCD9XXX_CFILT_FAST_MODE;
+		else
+			cfilt_mode.reg_mode_val = WCD9XXX_CFILT_SLOW_MODE;
+
+		cfilt_mode.reg_mask = 0x40;
+		cfilt_mode.cur_mode_val =
+		    snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl) & 0x40;
+	}
+
+	if (cfilt_mode.cur_mode_val
+			!= cfilt_mode.reg_mode_val) {
+		if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		snd_soc_update_bits(codec,
+				    mbhc->mbhc_bias_regs.cfilt_ctl,
+					cfilt_mode.reg_mask,
+					cfilt_mode.reg_mode_val);
+		if (mbhc->polling_active && wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_start_hs_polling(mbhc);
+		pr_debug("%s: CFILT mode change (%x to %x)\n", __func__,
+			cfilt_mode.cur_mode_val,
+			cfilt_mode.reg_mode_val);
+	} else {
+		pr_debug("%s: CFILT Value is already %x\n",
+			 __func__, cfilt_mode.cur_mode_val);
+	}
+}
+
+static void wcd9xxx_jack_report(struct wcd9xxx_mbhc *mbhc,
+				struct snd_soc_jack *jack, int status, int mask)
+{
+	if (jack == &mbhc->headset_jack) {
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH_MIC,
+						status & SND_JACK_MICROPHONE);
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH,
+						status & SND_JACK_HEADPHONE);
+	}
+
+	snd_soc_jack_report(jack, status, mask);
+}
+
+static void __hphocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status,
+				int irq)
+{
+	struct snd_soc_codec *codec;
+
+	pr_debug("%s: clear ocp status %x\n", __func__, jack_status);
+	codec = mbhc->codec;
+	if (mbhc->hph_status & jack_status) {
+		mbhc->hph_status &= ~jack_status;
+		wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+		/*
+		 * reset retry counter as PA is turned off signifying
+		 * start of new OCP detection session
+		 */
+		if (mbhc->intr_ids->hph_left_ocp)
+			mbhc->hphlocp_cnt = 0;
+		else
+			mbhc->hphrocp_cnt = 0;
+		wcd9xxx_enable_irq(mbhc->resmgr->core_res, irq);
+	}
+}
+
+static void hphrocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHR,
+			    mbhc->intr_ids->hph_right_ocp);
+}
+
+static void hphlocp_off_report(struct wcd9xxx_mbhc *mbhc, u32 jack_status)
+{
+	__hphocp_off_report(mbhc, SND_JACK_OC_HPHL,
+			    mbhc->intr_ids->hph_left_ocp);
+}
+
+static void wcd9xxx_get_mbhc_micbias_regs(struct wcd9xxx_mbhc *mbhc,
+				enum wcd9xxx_mbhc_micbias_type mb_type)
+{
+	unsigned int cfilt;
+	struct wcd9xxx_micbias_setting *micbias_pdata =
+		mbhc->resmgr->micbias_pdata;
+	struct mbhc_micbias_regs *micbias_regs;
+	enum wcd9xxx_micbias_num mb_num;
+
+	if (mb_type == MBHC_ANC_MIC_MB) {
+		micbias_regs = &mbhc->mbhc_anc_bias_regs;
+		mb_num = mbhc->mbhc_cfg->anc_micbias;
+	} else {
+		micbias_regs = &mbhc->mbhc_bias_regs;
+		mb_num = mbhc->mbhc_cfg->micbias;
+	}
+
+	switch (mb_num) {
+	case MBHC_MICBIAS1:
+		cfilt = micbias_pdata->bias1_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_1_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_1_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_1_CTL;
+		break;
+	case MBHC_MICBIAS2:
+		cfilt = micbias_pdata->bias2_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_2_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_2_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_2_CTL;
+		break;
+	case MBHC_MICBIAS3:
+		cfilt = micbias_pdata->bias3_cfilt_sel;
+		micbias_regs->mbhc_reg = WCD9XXX_A_MICB_3_MBHC;
+		micbias_regs->int_rbias = WCD9XXX_A_MICB_3_INT_RBIAS;
+		micbias_regs->ctl_reg = WCD9XXX_A_MICB_3_CTL;
+		break;
+	case MBHC_MICBIAS4:
+		cfilt = micbias_pdata->bias4_cfilt_sel;
+		micbias_regs->mbhc_reg = mbhc->resmgr->reg_addr->micb_4_mbhc;
+		micbias_regs->int_rbias =
+		    mbhc->resmgr->reg_addr->micb_4_int_rbias;
+		micbias_regs->ctl_reg = mbhc->resmgr->reg_addr->micb_4_ctl;
+		break;
+	default:
+		/* Should never reach here */
+		pr_err("%s: Invalid MIC BIAS for MBHC\n", __func__);
+		return;
+	}
+
+	micbias_regs->cfilt_sel = cfilt;
+
+	switch (cfilt) {
+	case WCD9XXX_CFILT1_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_1_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_1_CTL;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_2_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_2_CTL;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		micbias_regs->cfilt_val = WCD9XXX_A_MICB_CFILT_3_VAL;
+		micbias_regs->cfilt_ctl = WCD9XXX_A_MICB_CFILT_3_CTL;
+		break;
+	}
+
+	if (mb_type == MBHC_PRIMARY_MIC_MB) {
+		switch (cfilt) {
+		case WCD9XXX_CFILT1_SEL:
+			mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt1_mv;
+			break;
+		case WCD9XXX_CFILT2_SEL:
+			mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt2_mv;
+			break;
+		case WCD9XXX_CFILT3_SEL:
+			mbhc->mbhc_data.micb_mv = micbias_pdata->cfilt3_mv;
+			break;
+		}
+	}
+
+}
+
+static void wcd9xxx_clr_and_turnon_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+	bool pa_turned_on = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+	u8 wg_time;
+
+	wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+	wg_time += 1;
+
+	if (test_and_clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL,
+				    0xC0, 0xC0);
+	}
+	if (test_and_clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK,
+				&mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable DAC\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL,
+				    0x80, 0x80);
+	}
+
+	if (test_and_clear_bit(WCD9XXX_HPHR_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHR clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x10,
+				    1 << 4);
+		pa_turned_on = true;
+	}
+	if (test_and_clear_bit(WCD9XXX_HPHL_PA_OFF_ACK,
+			       &mbhc->hph_pa_dac_state)) {
+		pr_debug("%s: HPHL clear flag and enable PA\n", __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x20, 1
+				    << 5);
+		pa_turned_on = true;
+	}
+
+	if (pa_turned_on) {
+		pr_debug("%s: PA was turned on by MBHC and not by DAPM\n",
+			 __func__);
+		usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+	}
+}
+
+static int wcd9xxx_cancel_btn_work(struct wcd9xxx_mbhc *mbhc)
+{
+	int r;
+
+	r = cancel_delayed_work_sync(&mbhc->mbhc_btn_dwork);
+	if (r)
+		/* if scheduled mbhc.mbhc_btn_dwork is canceled from here,
+		 * we have to unlock from here instead btn_work
+		 */
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+	return r;
+}
+
+static bool wcd9xxx_is_hph_dac_on(struct snd_soc_codec *codec, int left)
+{
+	u8 hph_reg_val = 0;
+
+	if (left)
+		hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL);
+	else
+		hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL);
+
+	return (hph_reg_val & 0xC0) ? true : false;
+}
+
+static bool wcd9xxx_is_hph_pa_on(struct snd_soc_codec *codec)
+{
+	u8 hph_reg_val = 0;
+
+	hph_reg_val = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_EN);
+
+	return (hph_reg_val & 0x30) ? true : false;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_set_and_turnoff_hph_padac(struct wcd9xxx_mbhc *mbhc)
+{
+	u8 wg_time;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wg_time = snd_soc_read(codec, WCD9XXX_A_RX_HPH_CNP_WG_TIME);
+	wg_time += 1;
+
+	/* If headphone PA is on, check if userspace receives
+	 * removal event to sync-up PA's state
+	 */
+	if (wcd9xxx_is_hph_pa_on(codec)) {
+		pr_debug("%s PA is on, setting PA_OFF_ACK\n", __func__);
+		set_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		set_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+	} else {
+		pr_debug("%s PA is off\n", __func__);
+	}
+
+	if (wcd9xxx_is_hph_dac_on(codec, 1))
+		set_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+	if (wcd9xxx_is_hph_dac_on(codec, 0))
+		set_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_CNP_EN, 0x30, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_L_DAC_CTL, 0x80, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_R_DAC_CTL, 0xC0, 0x00);
+	usleep_range(wg_time * 1000, wg_time * 1000 + 50);
+}
+
+static void wcd9xxx_insert_detect_setup(struct wcd9xxx_mbhc *mbhc, bool ins)
+{
+	if (!mbhc->mbhc_cfg->insert_detect)
+		return;
+	pr_debug("%s: Setting up %s detection\n", __func__,
+		 ins ? "insert" : "removal");
+	/* Disable detection to avoid glitch */
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 0);
+	if (mbhc->mbhc_cfg->gpio_level_insert)
+		snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+			      (0x68 | (ins ? (1 << 1) : 0)));
+	else
+		snd_soc_write(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT,
+			      (0x6C | (ins ? (1 << 1) : 0)));
+	/* Re-enable detection */
+	snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MBHC_INSERT_DETECT, 1, 1);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_report_plug(struct wcd9xxx_mbhc *mbhc, int insertion,
+				enum snd_jack_types jack_type)
+{
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	pr_debug("%s: enter insertion %d hph_status %x\n",
+		 __func__, insertion, mbhc->hph_status);
+	if (!insertion) {
+		/* Report removal */
+		mbhc->hph_status &= ~jack_type;
+		/*
+		 * cancel possibly scheduled btn work and
+		 * report release if we reported button press
+		 */
+		if (wcd9xxx_cancel_btn_work(mbhc))
+			pr_debug("%s: button press is canceled\n", __func__);
+		else if (mbhc->buttons_pressed) {
+			pr_debug("%s: release of button press%d\n",
+				 __func__, jack_type);
+			wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+			mbhc->buttons_pressed &=
+				~WCD9XXX_JACK_BUTTON_MASK;
+		}
+
+		if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
+			pr_debug("%s: Disabling micbias\n", __func__);
+			mbhc->micbias_enable = false;
+			mbhc->micbias_enable_cb(mbhc->codec, false,
+						mbhc->mbhc_cfg->micbias);
+		}
+		mbhc->zl = mbhc->zr = 0;
+		mbhc->hph_type = MBHC_HPH_NONE;
+		pr_debug("%s: Reporting removal %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd9xxx_jack_report(mbhc, &mbhc->headset_jack, mbhc->hph_status,
+				    WCD9XXX_JACK_MASK);
+		wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+		hphrocp_off_report(mbhc, SND_JACK_OC_HPHR);
+		hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		mbhc->current_plug = PLUG_TYPE_NONE;
+		mbhc->polling_active = false;
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->hph_auto_pulldown_ctrl)
+			mbhc->mbhc_cb->hph_auto_pulldown_ctrl(mbhc->codec,
+								false);
+	} else {
+		/*
+		 * Report removal of current jack type.
+		 * Headphone to headset shouldn't report headphone
+		 * removal.
+		 */
+		if (mbhc->mbhc_cfg->detect_extn_cable &&
+		    !(mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
+		      jack_type == SND_JACK_HEADSET) &&
+		    (mbhc->hph_status && mbhc->hph_status != jack_type)) {
+			if (mbhc->micbias_enable && mbhc->micbias_enable_cb &&
+			    mbhc->hph_status == SND_JACK_HEADSET) {
+				pr_debug("%s: Disabling micbias\n", __func__);
+				mbhc->micbias_enable = false;
+				mbhc->micbias_enable_cb(mbhc->codec, false,
+						mbhc->mbhc_cfg->micbias);
+			}
+
+			pr_debug("%s: Reporting removal (%x)\n",
+				 __func__, mbhc->hph_status);
+			mbhc->zl = mbhc->zr = 0;
+			wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+					    0, WCD9XXX_JACK_MASK);
+			mbhc->hph_status &= ~(SND_JACK_HEADSET |
+						SND_JACK_LINEOUT |
+						SND_JACK_ANC_HEADPHONE |
+						SND_JACK_UNSUPPORTED);
+			if (mbhc->mbhc_cb &&
+				 mbhc->mbhc_cb->hph_auto_pulldown_ctrl)
+				mbhc->mbhc_cb->hph_auto_pulldown_ctrl(
+								mbhc->codec,
+								false);
+		}
+
+		/* Report insertion */
+		if (jack_type == SND_JACK_HEADPHONE) {
+			mbhc->current_plug = PLUG_TYPE_HEADPHONE;
+		} else if (jack_type == SND_JACK_UNSUPPORTED) {
+			mbhc->current_plug = PLUG_TYPE_GND_MIC_SWAP;
+		} else if (jack_type == SND_JACK_HEADSET) {
+			mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+			mbhc->current_plug = PLUG_TYPE_HEADSET;
+			mbhc->update_z = true;
+		} else if (jack_type == SND_JACK_LINEOUT) {
+			mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+		} else if (jack_type == SND_JACK_ANC_HEADPHONE) {
+			mbhc->polling_active = BUTTON_POLLING_SUPPORTED;
+			mbhc->current_plug = PLUG_TYPE_ANC_HEADPHONE;
+		}
+
+		if (mbhc->impedance_detect && impedance_detect_en) {
+			wcd9xxx_detect_impedance(mbhc,
+					&mbhc->zl, &mbhc->zr);
+			if ((mbhc->zl > WCD9XXX_LINEIN_THRESHOLD) &&
+				(mbhc->zr > WCD9XXX_LINEIN_THRESHOLD)) {
+				jack_type = SND_JACK_LINEOUT;
+				mbhc->current_plug = PLUG_TYPE_HIGH_HPH;
+				pr_debug("%s: Replace with SND_JACK_LINEOUT\n",
+				__func__);
+			}
+		}
+
+		mbhc->hph_status |= jack_type;
+
+		if (mbhc->micbias_enable && mbhc->micbias_enable_cb) {
+			pr_debug("%s: Enabling micbias\n", __func__);
+			mbhc->micbias_enable_cb(mbhc->codec, true,
+						mbhc->mbhc_cfg->micbias);
+		}
+
+		pr_debug("%s: Reporting insertion %d(%x)\n", __func__,
+			 jack_type, mbhc->hph_status);
+		wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+				    (mbhc->hph_status | SND_JACK_MECHANICAL),
+				    WCD9XXX_JACK_MASK);
+		/*
+		 * if PA is already on, switch micbias
+		 * source to VDDIO
+		 */
+		if (((mbhc->current_plug == PLUG_TYPE_HEADSET) ||
+		     (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE)) &&
+		    ((mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
+			1 << MBHC_EVENT_PA_HPHR))))
+			__wcd9xxx_switch_micbias(mbhc, 1, false,
+						 false);
+		wcd9xxx_clr_and_turnon_hph_padac(mbhc);
+	}
+	/* Setup insert detect */
+	wcd9xxx_insert_detect_setup(mbhc, !insertion);
+
+	pr_debug("%s: leave hph_status %x\n", __func__, mbhc->hph_status);
+}
+
+/* should be called under interrupt context that hold suspend */
+static void wcd9xxx_schedule_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+					    struct work_struct *work)
+{
+	pr_debug("%s: scheduling wcd9xxx_correct_swch_plug\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	mbhc->hs_detect_work_stop = false;
+	wcd9xxx_lock_sleep(mbhc->resmgr->core_res);
+	schedule_work(work);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_cancel_hs_detect_plug(struct wcd9xxx_mbhc *mbhc,
+					 struct work_struct *work)
+{
+	pr_debug("%s: Canceling correct_plug_swch\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	mbhc->hs_detect_work_stop = true;
+
+	/* Make sure mbhc state update complete before unlocking. */
+	wmb();
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	if (cancel_work_sync(work)) {
+		pr_debug("%s: correct_plug_swch is canceled\n",
+			 __func__);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+	}
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+}
+
+static s16 scale_v_micb_vddio(struct wcd9xxx_mbhc *mbhc, int v, bool tovddio)
+{
+	int r;
+	int vddio_k, mb_k;
+
+	vddio_k = __wcd9xxx_resmgr_get_k_val(mbhc, VDDIO_MICBIAS_MV);
+	mb_k = __wcd9xxx_resmgr_get_k_val(mbhc, mbhc->mbhc_data.micb_mv);
+	if (tovddio)
+		r = v * (vddio_k + 4) / (mb_k + 4);
+	else
+		r = v * (mb_k + 4) / (vddio_k + 4);
+	return r;
+}
+
+static s16 wcd9xxx_get_current_v_hs_max(struct wcd9xxx_mbhc *mbhc)
+{
+	s16 v_hs_max;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+
+	plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	if ((mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) &&
+	    mbhc->mbhc_micbias_switched)
+		v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max, true);
+	else
+		v_hs_max = plug_type->v_hs_max;
+	return v_hs_max;
+}
+
+static short wcd9xxx_read_sta_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B3_STATUS);
+	bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B2_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static short wcd9xxx_read_dce_result(struct snd_soc_codec *codec)
+{
+	u8 bias_msb, bias_lsb;
+	short bias_value;
+
+	bias_msb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B5_STATUS);
+	bias_lsb = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B4_STATUS);
+	bias_value = (bias_msb << 8) | bias_lsb;
+	return bias_value;
+}
+
+static void wcd9xxx_turn_onoff_rel_detection(struct snd_soc_codec *codec,
+					     bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, on << 1);
+}
+
+static short __wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+				     bool override_bypass, bool noreldetection)
+{
+	short bias_value;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+			    mbhc->intr_ids->dce_est_complete);
+	if (noreldetection)
+		wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+	if (mbhc->mbhc_cfg->do_recalibration)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2,
+				    0x0);
+	/* Turn on the override */
+	if (!override_bypass)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x4, 0x4);
+	if (dce) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x0);
+		if (mbhc->mbhc_cfg->do_recalibration)
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x2, 0x2);
+		usleep_range(mbhc->mbhc_data.t_sta_dce,
+			     mbhc->mbhc_data.t_sta_dce + 50);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x4);
+		usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce + 50);
+		bias_value = wcd9xxx_read_dce_result(codec);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x0);
+		if (mbhc->mbhc_cfg->do_recalibration)
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x2, 0x2);
+		usleep_range(mbhc->mbhc_data.t_sta_dce,
+			     mbhc->mbhc_data.t_sta_dce + 50);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		usleep_range(mbhc->mbhc_data.t_sta,
+			     mbhc->mbhc_data.t_sta + 50);
+		bias_value = wcd9xxx_read_sta_result(codec);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8,
+				    0x8);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x0);
+	}
+	/* Turn off the override after measuring mic voltage */
+	if (!override_bypass)
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04,
+				    0x00);
+
+	if (noreldetection)
+		wcd9xxx_turn_onoff_rel_detection(codec, true);
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+			   mbhc->intr_ids->dce_est_complete);
+
+	return bias_value;
+}
+
+static short wcd9xxx_codec_sta_dce(struct wcd9xxx_mbhc *mbhc, int dce,
+				   bool norel)
+{
+	bool override_bypass;
+
+	/* Bypass override if it is already enabled */
+	override_bypass = (snd_soc_read(mbhc->codec,
+					WCD9XXX_A_CDC_MBHC_B1_CTL) &
+			   0x04) ? true : false;
+
+	return __wcd9xxx_codec_sta_dce(mbhc, dce, override_bypass, norel);
+}
+
+static s32 __wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+				     u16 bias_value, s16 z, u32 micb_mv)
+{
+	s16 value, mb;
+	s32 mv = 0;
+
+	value = bias_value;
+	if (dce) {
+		mb = (mbhc->mbhc_data.dce_mb);
+		if (mb - z)
+			mv = (value - z) * (s32)micb_mv / (mb - z);
+	} else {
+		mb = (mbhc->mbhc_data.sta_mb);
+		if (mb - z)
+			mv = (value - z) * (s32)micb_mv / (mb - z);
+	}
+
+	return mv;
+}
+
+static s32 wcd9xxx_codec_sta_dce_v(struct wcd9xxx_mbhc *mbhc, s8 dce,
+				   u16 bias_value)
+{
+	s16 z;
+
+	z = dce ? (s16)mbhc->mbhc_data.dce_z : (s16)mbhc->mbhc_data.sta_z;
+	return __wcd9xxx_codec_sta_dce_v(mbhc, dce, bias_value, z,
+					 mbhc->mbhc_data.micb_mv);
+}
+
+/* To enable/disable bandgap and RC oscillator */
+static void wcd9xxx_mbhc_ctrl_clk_bandgap(struct wcd9xxx_mbhc *mbhc,
+		bool enable)
+{
+	if (enable) {
+		WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+		wcd9xxx_resmgr_get_bandgap(mbhc->resmgr,
+				WCD9XXX_BANDGAP_AUDIO_MODE);
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) {
+			WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+			mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, true);
+		} else {
+			wcd9xxx_resmgr_get_clk_block(mbhc->resmgr,
+					WCD9XXX_CLK_RCO);
+			WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+		}
+	} else {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl) {
+			mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, false);
+			WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+		} else {
+			WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+			wcd9xxx_resmgr_put_clk_block(mbhc->resmgr,
+					WCD9XXX_CLK_RCO);
+		}
+		wcd9xxx_resmgr_put_bandgap(mbhc->resmgr,
+				WCD9XXX_BANDGAP_AUDIO_MODE);
+		WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+	}
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static short wcd9xxx_mbhc_setup_hs_polling(struct wcd9xxx_mbhc *mbhc,
+				struct mbhc_micbias_regs *mbhc_micb_regs,
+				bool is_cs_enable)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	short bias_value;
+	u8 cfilt_mode;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	pr_debug("%s: enter\n", __func__);
+	if (!mbhc->mbhc_cfg->calibration) {
+		pr_err("%s: Error, no calibration exists\n", __func__);
+		return -ENODEV;
+	}
+
+	/* Enable external voltage source to micbias if present */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+		mbhc->mbhc_cb->enable_mb_source(codec, true, true);
+
+	/*
+	 * setup internal micbias if codec uses internal micbias for
+	 * headset detection
+	 */
+	if (mbhc->mbhc_cfg->use_int_rbias) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
+			mbhc->mbhc_cb->setup_int_rbias(codec, true);
+	else
+		pr_err("%s: internal bias requested but codec did not provide callback\n",
+			__func__);
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x01);
+
+	/* Make sure CFILT is in fast mode, save current mode */
+	cfilt_mode = snd_soc_read(codec, mbhc_micb_regs->cfilt_ctl);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+		mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+	else
+		snd_soc_update_bits(codec, mbhc_micb_regs->cfilt_ctl,
+				    0x70, 0x00);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+		      mbhc->scaling_mux_in);
+	pr_debug("%s:  scaling_mux_input: %d\n", __func__,
+						 mbhc->scaling_mux_in);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+		mbhc->mbhc_cb->enable_mux_bias_block(codec);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0x80, 0x80);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x1F, 0x1C);
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x40, 0x40);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_TX_7_MBHC_EN, 0x80, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x00);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x8, 0x8);
+
+	if (!mbhc->mbhc_cfg->do_recalibration) {
+		if (!is_cs_enable)
+			wcd9xxx_calibrate_hs_polling(mbhc);
+	}
+
+	/* don't flip override */
+	bias_value = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+	snd_soc_write(codec, mbhc_micb_regs->cfilt_ctl, cfilt_mode);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+
+	return bias_value;
+}
+
+static void wcd9xxx_recalibrate(struct wcd9xxx_mbhc *mbhc,
+				struct mbhc_micbias_regs *mbhc_micb_regs,
+				bool is_cs_enable)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	s16 reg;
+	int change;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	s16 sta_z = 0, dce_z = 0;
+
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	if (mbhc->mbhc_cfg->do_recalibration) {
+		/* recalibrate dce_z and sta_z */
+		reg = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
+		change = snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					     0x78, btn_det->mbhc_nsc << 3);
+		wcd9xxx_get_z(mbhc, &dce_z, &sta_z, mbhc_micb_regs, true);
+		if (change)
+			snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg);
+		if (dce_z && sta_z) {
+			pr_debug("%s: sta_z 0x%x -> 0x%x, dce_z 0x%x -> 0x%x\n",
+				 __func__,
+				 mbhc->mbhc_data.sta_z, sta_z & 0xffff,
+				 mbhc->mbhc_data.dce_z, dce_z & 0xffff);
+			mbhc->mbhc_data.dce_z = dce_z;
+			mbhc->mbhc_data.sta_z = sta_z;
+			wcd9xxx_mbhc_calc_thres(mbhc);
+			wcd9xxx_calibrate_hs_polling(mbhc);
+		} else {
+			pr_warn("%s: failed get new dce_z/sta_z 0x%x/0x%x\n",
+				__func__, dce_z, sta_z);
+		}
+
+		if (is_cs_enable) {
+			/* recalibrate dce_nsc_cs_z */
+			reg = snd_soc_read(mbhc->codec,
+					   WCD9XXX_A_CDC_MBHC_B1_CTL);
+			snd_soc_update_bits(mbhc->codec,
+					    WCD9XXX_A_CDC_MBHC_B1_CTL,
+					    0x78, WCD9XXX_MBHC_NSC_CS << 3);
+			wcd9xxx_get_z(mbhc, &dce_z, NULL, mbhc_micb_regs,
+				      true);
+			snd_soc_write(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+				      reg);
+			if (dce_z) {
+				mbhc->mbhc_data.dce_nsc_cs_z = dce_z;
+				/* update v_cs_ins_h with new dce_nsc_cs_z */
+				mbhc->mbhc_data.v_cs_ins_h =
+						wcd9xxx_codec_v_sta_dce(
+							mbhc, DCE,
+							WCD9XXX_V_CS_HS_MAX,
+							is_cs_enable);
+				pr_debug("%s: dce_nsc_cs_z 0x%x -> 0x%x, v_cs_ins_h 0x%x\n",
+					  __func__,
+					  mbhc->mbhc_data.dce_nsc_cs_z,
+					  dce_z & 0xffff,
+					  mbhc->mbhc_data.v_cs_ins_h);
+			} else {
+				pr_debug("%s: failed get new dce_nsc_cs_z\n",
+					 __func__);
+			}
+		}
+	}
+}
+
+static void wcd9xxx_shutdown_hs_removal_detect(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+	    WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+
+	/* Need MBHC clock */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl)
+		mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, true);
+	else {
+		WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+		wcd9xxx_resmgr_get_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+		WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x2, 0x2);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x6, 0x0);
+	__wcd9xxx_switch_micbias(mbhc, 0, false, false);
+
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem + 50);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0xA, 0x8);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_rco_ctrl)
+		mbhc->mbhc_cb->codec_rco_ctrl(mbhc->codec, false);
+	else {
+		WCD9XXX_BG_CLK_LOCK(mbhc->resmgr);
+		/* Put requested CLK back */
+		wcd9xxx_resmgr_put_clk_block(mbhc->resmgr, WCD9XXX_CLK_RCO);
+		WCD9XXX_BG_CLK_UNLOCK(mbhc->resmgr);
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x00);
+}
+
+static void wcd9xxx_cleanup_hs_polling(struct wcd9xxx_mbhc *mbhc)
+{
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	wcd9xxx_shutdown_hs_removal_detect(mbhc);
+
+
+	/* Disable external voltage source to micbias if present */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+		mbhc->mbhc_cb->enable_mb_source(mbhc->codec, false, true);
+
+	mbhc->polling_active = false;
+	mbhc->mbhc_state = MBHC_STATE_NONE;
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_codec_hphr_gnd_switch(struct snd_soc_codec *codec, bool on)
+{
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, on);
+	if (on)
+		usleep_range(5000, 5100);
+}
+
+static void wcd9xxx_onoff_vddio_switch(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	pr_debug("%s: vddio %d\n", __func__, on);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->pull_mb_to_vddio) {
+		mbhc->mbhc_cb->pull_mb_to_vddio(mbhc->codec, on);
+		goto exit;
+	}
+
+	if (on) {
+		snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    1 << 7, 1 << 7);
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    1 << 4, 0);
+	} else {
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    1 << 4, 1 << 4);
+		snd_soc_update_bits(mbhc->codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    1 << 7, 0);
+	}
+
+exit:
+	/*
+	 * Wait for the micbias to settle down to vddio
+	 * when the micbias to vddio switch is enabled.
+	 */
+	if (on)
+		usleep_range(10000, 10100);
+}
+
+static int wcd9xxx_hphl_status(struct wcd9xxx_mbhc *mbhc)
+{
+	u16 hph, status;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	hph = snd_soc_read(codec, WCD9XXX_A_MBHC_HPH);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x02);
+	usleep_range(WCD9XXX_HPHL_STATUS_READY_WAIT_US,
+		     WCD9XXX_HPHL_STATUS_READY_WAIT_US +
+		     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+	status = snd_soc_read(codec, WCD9XXX_A_RX_HPH_L_STATUS);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_HPH, hph);
+	return status;
+}
+
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_cs_find_plug_type(struct wcd9xxx_mbhc *mbhc,
+			  struct wcd9xxx_mbhc_detect *dt, const int size,
+			  bool highhph,
+			  unsigned long event_state)
+{
+	int i;
+	int vdce, mb_mv;
+	int ch, sz, delta_thr;
+	int minv = 0, maxv = INT_MIN;
+	struct wcd9xxx_mbhc_detect *d = dt;
+	struct wcd9xxx_mbhc_detect *dprev = d, *dmicbias = NULL, *dgnd = NULL;
+	enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
+
+	const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	s16 hs_max, no_mic, dce_z;
+	int highhph_cnt = 0;
+
+	pr_debug("%s: enter\n", __func__);
+	pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
+
+	sz = size - 1;
+	for (i = 0, d = dt, ch = 0; i < sz; i++, d++) {
+		if (d->mic_bias) {
+			dce_z = mbhc->mbhc_data.dce_z;
+			mb_mv = mbhc->mbhc_data.micb_mv;
+			hs_max = plug_type->v_hs_max;
+			no_mic = plug_type->v_no_mic;
+		} else {
+			dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+			mb_mv = VDDIO_MICBIAS_MV;
+			hs_max = WCD9XXX_V_CS_HS_MAX;
+			no_mic = WCD9XXX_V_CS_NO_MIC;
+		}
+
+		vdce = __wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce,
+						 dce_z, (u32)mb_mv);
+		d->_vdces = vdce;
+		if (d->_vdces < no_mic)
+			d->_type = PLUG_TYPE_HEADPHONE;
+		else if (d->_vdces >= hs_max) {
+			d->_type = PLUG_TYPE_HIGH_HPH;
+			highhph_cnt++;
+		} else
+			d->_type = PLUG_TYPE_HEADSET;
+
+		pr_debug("%s: DCE #%d, %04x, V %04d(%04d), HPHL %d TYPE %d\n",
+			 __func__, i, d->dce, vdce, d->_vdces,
+			 d->hphl_status & 0x01,
+			 d->_type);
+
+		ch += d->hphl_status & 0x01;
+		if (!d->swap_gnd && !d->mic_bias) {
+			if (maxv < d->_vdces)
+				maxv = d->_vdces;
+			if (!minv || minv > d->_vdces)
+				minv = d->_vdces;
+		}
+		if ((!d->mic_bias &&
+		    (d->_vdces >= WCD9XXX_CS_MEAS_INVALD_RANGE_LOW_MV &&
+		     d->_vdces <= WCD9XXX_CS_MEAS_INVALD_RANGE_HIGH_MV)) ||
+		    (d->mic_bias &&
+		    (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+		     d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV))) {
+			pr_debug("%s: within invalid range\n", __func__);
+			type = PLUG_TYPE_INVALID;
+			goto exit;
+		}
+	}
+
+	delta_thr = ((highhph_cnt == sz) || highhph) ?
+			      WCD9XXX_MB_MEAS_DELTA_MAX_MV :
+			      WCD9XXX_CS_MEAS_DELTA_MAX_MV;
+
+	for (i = 0, d = dt; i < sz; i++, d++) {
+		if ((i > 0) && !d->mic_bias && !d->swap_gnd &&
+		    (d->_type != dprev->_type)) {
+			pr_debug("%s: Invalid, inconsistent types\n", __func__);
+			type = PLUG_TYPE_INVALID;
+			goto exit;
+		}
+
+		if (!d->swap_gnd && !d->mic_bias &&
+		    (abs(minv - d->_vdces) > delta_thr ||
+		     abs(maxv - d->_vdces) > delta_thr)) {
+			pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
+				 __func__, d->_vdces, minv, maxv);
+			type = PLUG_TYPE_INVALID;
+			goto exit;
+		} else if (d->swap_gnd) {
+			dgnd = d;
+		}
+
+		if (!d->mic_bias && !d->swap_gnd)
+			dprev = d;
+		else if (d->mic_bias)
+			dmicbias = d;
+	}
+	if (dgnd && dt->_type != PLUG_TYPE_HEADSET &&
+	    dt->_type != dgnd->_type) {
+		pr_debug("%s: Invalid, inconsistent types\n", __func__);
+		type = PLUG_TYPE_INVALID;
+		goto exit;
+	}
+
+	type = dt->_type;
+	if (dmicbias) {
+		if (dmicbias->_type == PLUG_TYPE_HEADSET &&
+		    (dt->_type == PLUG_TYPE_HIGH_HPH ||
+		     dt->_type == PLUG_TYPE_HEADSET)) {
+			type = PLUG_TYPE_HEADSET;
+			if (dt->_type == PLUG_TYPE_HIGH_HPH) {
+				pr_debug("%s: Headset with threshold on MIC detected\n",
+					 __func__);
+				if (mbhc->mbhc_cfg->micbias_enable_flags &
+				 (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
+					mbhc->micbias_enable = true;
+			}
+		}
+	}
+
+	if (type == PLUG_TYPE_HEADSET && dgnd && !dgnd->mic_bias) {
+		/* if plug type is Headphone report as GND_MIC_SWAP */
+		if (dgnd->_type == PLUG_TYPE_HEADPHONE) {
+			pr_debug("%s: GND_MIC_SWAP\n", __func__);
+			type = PLUG_TYPE_GND_MIC_SWAP;
+			/*
+			 * if type is GND_MIC_SWAP we should not check
+			 * HPHL status hence goto exit
+			 */
+			goto exit;
+		} else if (dgnd->_type != PLUG_TYPE_HEADSET && !dmicbias) {
+			pr_debug("%s: Invalid, inconsistent types\n", __func__);
+			type = PLUG_TYPE_INVALID;
+		}
+	}
+
+	if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
+		pr_debug("%s: HPHL PA was ON\n", __func__);
+	} else if (ch != sz && ch > 0) {
+		pr_debug("%s: Invalid, inconsistent HPHL..\n", __func__);
+		type = PLUG_TYPE_INVALID;
+		goto exit;
+	}
+
+	if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
+		if (((type == PLUG_TYPE_HEADSET ||
+		      type == PLUG_TYPE_HEADPHONE) && ch != sz)) {
+			pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
+				 __func__, type);
+			type = PLUG_TYPE_INVALID;
+		}
+	}
+
+	if (type == PLUG_TYPE_HEADSET &&
+	    (mbhc->mbhc_cfg->micbias_enable_flags &
+	    (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET)))
+		mbhc->micbias_enable = true;
+
+exit:
+	pr_debug("%s: Plug type %d detected\n", __func__, type);
+	return type;
+}
+
+/*
+ * wcd9xxx_find_plug_type : Find out and return the best plug type with given
+ *			    list of wcd9xxx_mbhc_detect structure.
+ * param mbhc wcd9xxx_mbhc structure
+ * param dt collected measurements
+ * param size array size of dt
+ * param event_state mbhc->event_state when dt is collected
+ */
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_find_plug_type(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_detect *dt, const int size,
+		       unsigned long event_state)
+{
+	int i;
+	int ch;
+	enum wcd9xxx_mbhc_plug_type type;
+	int vdce;
+	struct wcd9xxx_mbhc_detect *d, *dprev, *dgnd = NULL, *dvddio = NULL;
+	int maxv = 0, minv = 0;
+	const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	const s16 hs_max = plug_type->v_hs_max;
+	const s16 no_mic = plug_type->v_no_mic;
+
+	pr_debug("%s: event_state 0x%lx\n", __func__, event_state);
+
+	for (i = 0, d = dt, ch = 0; i < size; i++, d++) {
+		vdce = wcd9xxx_codec_sta_dce_v(mbhc, true, d->dce);
+		if (d->vddio)
+			d->_vdces = scale_v_micb_vddio(mbhc, vdce, false);
+		else
+			d->_vdces = vdce;
+
+		if (d->_vdces >= no_mic && d->_vdces < hs_max)
+			d->_type = PLUG_TYPE_HEADSET;
+		else if (d->_vdces < no_mic)
+			d->_type = PLUG_TYPE_HEADPHONE;
+		else
+			d->_type = PLUG_TYPE_HIGH_HPH;
+
+		ch += d->hphl_status & 0x01;
+		if (!d->swap_gnd && !d->hwvalue && !d->vddio) {
+			if (maxv < d->_vdces)
+				maxv = d->_vdces;
+			if (!minv || minv > d->_vdces)
+				minv = d->_vdces;
+		}
+
+		pr_debug("%s: DCE #%d, %04x, V %04d(%04d), GND %d, VDDIO %d, HPHL %d TYPE %d\n",
+			 __func__, i, d->dce, vdce, d->_vdces,
+			 d->swap_gnd, d->vddio, d->hphl_status & 0x01,
+			 d->_type);
+
+
+		/*
+		 * If GND and MIC prongs are aligned to HPHR and GND of
+		 * headphone, codec measures the voltage based on
+		 * impedance between HPHR and GND which results in ~80mv.
+		 * Avoid this.
+		 */
+		if (d->_vdces >= WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+		    d->_vdces <= WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) {
+			pr_debug("%s: within invalid range\n", __func__);
+			type = PLUG_TYPE_INVALID;
+			goto exit;
+		}
+	}
+
+	if (event_state & (1 << MBHC_EVENT_PA_HPHL)) {
+		pr_debug("%s: HPHL PA was ON\n", __func__);
+	} else if (ch != size && ch > 0) {
+		pr_debug("%s: Invalid, inconsistent HPHL\n", __func__);
+		type = PLUG_TYPE_INVALID;
+		goto exit;
+	}
+
+	for (i = 0, dprev = NULL, d = dt; i < size; i++, d++) {
+		if (d->vddio) {
+			dvddio = d;
+			continue;
+		}
+
+		if ((i > 0) && (dprev != NULL) && (d->_type != dprev->_type)) {
+			pr_debug("%s: Invalid, inconsistent types\n", __func__);
+			type = PLUG_TYPE_INVALID;
+			goto exit;
+		}
+
+		if (!d->swap_gnd && !d->hwvalue &&
+		    (abs(minv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV ||
+		     abs(maxv - d->_vdces) > WCD9XXX_MEAS_DELTA_MAX_MV)) {
+			pr_debug("%s: Invalid, delta %dmv, %dmv and %dmv\n",
+				 __func__, d->_vdces, minv, maxv);
+			type = PLUG_TYPE_INVALID;
+			goto exit;
+		} else if (d->swap_gnd) {
+			dgnd = d;
+		}
+		dprev = d;
+	}
+
+	WARN_ON(i != size);
+	type = dt->_type;
+	if (type == PLUG_TYPE_HEADSET && dgnd) {
+		if ((dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MIN_MV <
+		     minv) &&
+		    (dgnd->_vdces + WCD9XXX_GM_SWAP_THRES_MAX_MV >
+		     maxv))
+			type = PLUG_TYPE_GND_MIC_SWAP;
+	}
+
+	/* if HPHL PA was on, we cannot use hphl status */
+	if (!(event_state & (1UL << MBHC_EVENT_PA_HPHL))) {
+		if (((type == PLUG_TYPE_HEADSET ||
+		      type == PLUG_TYPE_HEADPHONE) && ch != size) ||
+		    (type == PLUG_TYPE_GND_MIC_SWAP && ch)) {
+			pr_debug("%s: Invalid, not fully inserted, TYPE %d\n",
+				 __func__, type);
+			type = PLUG_TYPE_INVALID;
+		}
+	}
+
+	if (type == PLUG_TYPE_HEADSET) {
+		if (dvddio && ((dvddio->_vdces > hs_max) ||
+		   (dvddio->_vdces > minv + WCD9XXX_THRESHOLD_MIC_THRESHOLD))) {
+			pr_debug("%s: Headset with threshold on MIC detected\n",
+				 __func__);
+			if (mbhc->mbhc_cfg->micbias_enable_flags &
+			    (1 << MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET))
+				mbhc->micbias_enable = true;
+		} else {
+			pr_debug("%s: Headset with regular MIC detected\n",
+				 __func__);
+			if (mbhc->mbhc_cfg->micbias_enable_flags &
+			    (1 << MBHC_MICBIAS_ENABLE_REGULAR_HEADSET))
+				mbhc->micbias_enable = true;
+		}
+	}
+exit:
+	pr_debug("%s: Plug type %d detected, micbias_enable %d\n", __func__,
+		 type, mbhc->micbias_enable);
+	return type;
+}
+
+/*
+ * Pull down MBHC micbias for provided duration in microsecond.
+ */
+static int wcd9xxx_pull_down_micbias(struct wcd9xxx_mbhc *mbhc, int us)
+{
+	bool micbiasconn = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const u16 ctlreg = mbhc->mbhc_bias_regs.ctl_reg;
+
+	/*
+	 * Disable MBHC to micbias connection to pull down
+	 * micbias and pull down micbias for a moment.
+	 */
+	if ((snd_soc_read(mbhc->codec, ctlreg) & 0x01)) {
+		WARN_ONCE(1, "MBHC micbias is already pulled down unexpectedly\n");
+		return -EFAULT;
+	}
+
+	if ((snd_soc_read(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL) & 1 << 4)) {
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    1 << 4, 0);
+		micbiasconn = true;
+	}
+
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+
+	/*
+	 * Pull down for 1ms to discharge bias. Give small margin (10us) to be
+	 * able to get consistent result across DCEs.
+	 */
+	usleep_range(1000, 1000 + 10);
+
+	if (micbiasconn)
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_MAD_ANA_CTRL,
+				    1 << 4, 1 << 4);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	usleep_range(us, us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+	return 0;
+}
+
+/* Called under codec resource lock acquisition */
+void wcd9xxx_turn_onoff_current_source(struct wcd9xxx_mbhc *mbhc,
+				       struct mbhc_micbias_regs *mbhc_micb_regs,
+				       bool on, bool highhph)
+{
+	struct snd_soc_codec *codec;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+	    WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	codec = mbhc->codec;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if ((on && mbhc->is_cs_enabled) ||
+	    (!on && !mbhc->is_cs_enabled)) {
+		pr_debug("%s: Current source is already %s\n",
+			__func__, on ? "ON" : "OFF");
+		return;
+	}
+
+	if (on) {
+		pr_debug("%s: enabling current source\n", __func__);
+		/* Nsc to 9 */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+				    0x78, 0x48);
+		/* pull down diode bit to 0 */
+		snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
+				    0x01, 0x00);
+		/*
+		 * Keep the low power insertion/removal
+		 * detection (reg 0x3DD) disabled
+		 */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL,
+				    0x01, 0x00);
+		/*
+		 * Enable the Mic Bias current source
+		 * Write bits[6:5] of register MICB_2_MBHC to 0x3 (V_20_UA)
+		 * Write bit[7] of register MICB_2_MBHC to 1
+		 * (INS_DET_ISRC_EN__ENABLE)
+		 * MICB_2_MBHC__SCHT_TRIG_EN to 1
+		 */
+		snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
+				    0xF0, 0xF0);
+		/* Disconnect MBHC Override from MicBias and LDOH */
+		snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x00);
+		mbhc->is_cs_enabled = true;
+	} else {
+		pr_debug("%s: disabling current source\n", __func__);
+		/* Connect MBHC Override from MicBias and LDOH */
+		snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 0x10, 0x10);
+		/* INS_DET_ISRC_CTL to acdb value */
+		snd_soc_update_bits(codec, mbhc_micb_regs->mbhc_reg,
+				    0x60, plug_det->mic_current << 5);
+		if (!highhph) {
+			/* INS_DET_ISRC_EN__ENABLE to 0 */
+			snd_soc_update_bits(codec,
+					    mbhc_micb_regs->mbhc_reg,
+					    0x80, 0x00);
+			/* MICB_2_MBHC__SCHT_TRIG_EN  to 0 */
+			snd_soc_update_bits(codec,
+					    mbhc_micb_regs->mbhc_reg,
+					    0x10, 0x00);
+		}
+		/* Nsc to acdb value */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+				    btn_det->mbhc_nsc << 3);
+		mbhc->is_cs_enabled = false;
+	}
+}
+
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_cs_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
+	enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
+	int i;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	BUG_ON(NUM_DCE_PLUG_INS_DETECT < 4);
+
+	wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+	rt[0].swap_gnd = false;
+	rt[0].vddio = false;
+	rt[0].hwvalue = true;
+	rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+	rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
+						  true);
+	rt[0].mic_bias = false;
+
+	for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+		rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 3);
+		rt[i].mic_bias = ((i == NUM_DCE_PLUG_INS_DETECT - 4) &&
+				   highhph);
+		rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+		if (rt[i].swap_gnd)
+			wcd9xxx_codec_hphr_gnd_switch(codec, true);
+
+		if (rt[i].mic_bias)
+			wcd9xxx_turn_onoff_current_source(mbhc,
+							  &mbhc->mbhc_bias_regs,
+							  false, false);
+
+		rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, !highhph, true);
+		if (rt[i].mic_bias)
+			wcd9xxx_turn_onoff_current_source(mbhc,
+							  &mbhc->mbhc_bias_regs,
+							  true, false);
+		if (rt[i].swap_gnd)
+			wcd9xxx_codec_hphr_gnd_switch(codec, false);
+	}
+
+	/* recalibrate DCE/STA GND voltages */
+	wcd9xxx_recalibrate(mbhc, &mbhc->mbhc_bias_regs, true);
+
+	type = wcd9xxx_cs_find_plug_type(mbhc, rt, ARRAY_SIZE(rt), highhph,
+					 mbhc->event_state);
+
+	wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+	pr_debug("%s: plug_type:%d\n", __func__, type);
+
+	return type;
+}
+
+static enum wcd9xxx_mbhc_plug_type
+wcd9xxx_codec_get_plug_type(struct wcd9xxx_mbhc *mbhc, bool highhph)
+{
+	int i;
+	bool vddioon;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type_ptr;
+	struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT];
+	enum wcd9xxx_mbhc_plug_type type = PLUG_TYPE_INVALID;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	/* make sure override is on */
+	WARN_ON(!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04));
+
+	/* GND and MIC swap detection requires at least 2 rounds of DCE */
+	BUG_ON(NUM_DCE_PLUG_INS_DETECT < 2);
+	detect_use_vddio_switch = mbhc->mbhc_cfg->use_vddio_meas;
+
+	/*
+	 * There are chances vddio switch is on and cfilt voltage is adjusted
+	 * to vddio voltage even after plug type removal reported.
+	 */
+	vddioon = __wcd9xxx_switch_micbias(mbhc, 0, false, false);
+	pr_debug("%s: vddio switch was %s\n", __func__, vddioon ? "on" : "off");
+
+	plug_type_ptr =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+	/*
+	 * cfilter in fast mode requires 1ms to charge up and down micbias
+	 * fully.
+	 */
+	(void) wcd9xxx_pull_down_micbias(mbhc,
+					 WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
+
+	wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+	rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+	rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc, &mbhc->mbhc_bias_regs,
+						  false);
+	rt[0].swap_gnd = false;
+	rt[0].vddio = false;
+	rt[0].hwvalue = true;
+	for (i = 1; i < NUM_DCE_PLUG_INS_DETECT; i++) {
+		rt[i].swap_gnd = (i == NUM_DCE_PLUG_INS_DETECT - 2);
+		if (detect_use_vddio_switch)
+			rt[i].vddio = (i == 1);
+		else
+			rt[i].vddio = false;
+		rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+		rt[i].hwvalue = false;
+		if (rt[i].swap_gnd)
+			wcd9xxx_codec_hphr_gnd_switch(codec, true);
+		if (rt[i].vddio)
+			wcd9xxx_onoff_vddio_switch(mbhc, true);
+		/*
+		 * Pull down micbias to detect headset with mic which has
+		 * threshold and to have more consistent voltage measurements.
+		 *
+		 * cfilter in fast mode requires 1ms to charge up and down
+		 * micbias fully.
+		 */
+		(void) wcd9xxx_pull_down_micbias(mbhc,
+					    WCD9XXX_MICBIAS_PULLDOWN_SETTLE_US);
+		rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1, true, true);
+		if (rt[i].vddio)
+			wcd9xxx_onoff_vddio_switch(mbhc, false);
+		if (rt[i].swap_gnd)
+			wcd9xxx_codec_hphr_gnd_switch(codec, false);
+	}
+	/* recalibrate DCE/STA GND voltages */
+	wcd9xxx_recalibrate(mbhc, &mbhc->mbhc_bias_regs, false);
+
+	if (vddioon)
+		__wcd9xxx_switch_micbias(mbhc, 1, false, false);
+
+	type = wcd9xxx_find_plug_type(mbhc, rt, ARRAY_SIZE(rt),
+				      mbhc->event_state);
+
+	wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+	pr_debug("%s: leave\n", __func__);
+	return type;
+}
+
+static bool wcd9xxx_swch_level_remove(struct wcd9xxx_mbhc *mbhc)
+{
+	if (mbhc->mbhc_cfg->gpio)
+		return (gpio_get_value_cansleep(mbhc->mbhc_cfg->gpio) !=
+			mbhc->mbhc_cfg->gpio_level_insert);
+	else if (mbhc->mbhc_cfg->insert_detect) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->insert_rem_status)
+			return mbhc->mbhc_cb->insert_rem_status(mbhc->codec);
+		else
+			return snd_soc_read(mbhc->codec,
+				    WCD9XXX_A_MBHC_INSERT_DET_STATUS) &
+				    (1 << 2);
+	} else
+		WARN(1, "Invalid jack detection configuration\n");
+
+	return true;
+}
+
+static bool is_clk_active(struct snd_soc_codec *codec)
+{
+	return !!(snd_soc_read(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL) & 0x05);
+}
+
+static int wcd9xxx_enable_hs_detect(struct wcd9xxx_mbhc *mbhc,
+				    int insertion, int trigger, bool padac_off)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	int central_bias_enabled = 0;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+	    WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	const struct wcd9xxx_mbhc_plug_detect_cfg *plug_det =
+	    WCD9XXX_MBHC_CAL_PLUG_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	pr_debug("%s: enter insertion(%d) trigger(0x%x)\n",
+		 __func__, insertion, trigger);
+
+	if (!mbhc->mbhc_cfg->calibration) {
+		pr_err("Error, no wcd9xxx calibration\n");
+		return -EINVAL;
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0);
+
+	/*
+	 * Make sure mic bias and Mic line schmitt trigger
+	 * are turned OFF
+	 */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+
+	if (insertion) {
+		wcd9xxx_switch_micbias(mbhc, 0);
+
+		/* DAPM can manipulate PA/DAC bits concurrently */
+		if (padac_off == true)
+			wcd9xxx_set_and_turnoff_hph_padac(mbhc);
+
+		if (trigger & MBHC_USE_HPHL_TRIGGER) {
+			/* Enable HPH Schmitt Trigger */
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x11,
+					0x11);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x0C,
+					plug_det->hph_current << 2);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x02,
+					0x02);
+		}
+		if (trigger & MBHC_USE_MB_TRIGGER) {
+			/* enable the mic line schmitt trigger */
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x60, plug_det->mic_current << 5);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x80, 0x80);
+			usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid +
+						WCD9XXX_USLEEP_RANGE_MARGIN_US);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+					0x00);
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.mbhc_reg,
+					0x10, 0x10);
+		}
+
+		/* setup for insetion detection */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2, 0);
+	} else {
+		pr_debug("setup for removal detection\n");
+		/* Make sure the HPH schmitt trigger is OFF */
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x12, 0x00);
+
+		/* enable the mic line schmitt trigger */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+				    0x01, 0x00);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x60,
+				    plug_det->mic_current << 5);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x80, 0x80);
+		usleep_range(plug_det->t_mic_pid, plug_det->t_mic_pid +
+					WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg,
+				    0x10, 0x10);
+
+		/* Setup for low power removal detection */
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x2,
+				    0x2);
+	}
+
+	if (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x4) {
+		/* called by interrupt */
+		if (!is_clk_active(codec)) {
+			wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 1);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					0x06, 0);
+			usleep_range(generic->t_shutdown_plug_rem,
+					generic->t_shutdown_plug_rem +
+					WCD9XXX_USLEEP_RANGE_MARGIN_US);
+			wcd9xxx_resmgr_enable_config_mode(mbhc->resmgr, 0);
+		} else
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					0x06, 0);
+	}
+
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.int_rbias, 0x80, 0);
+
+	/* If central bandgap disabled */
+	if (!(snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE1) & 1)) {
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x3, 0x3);
+		usleep_range(generic->t_bg_fast_settle,
+			     generic->t_bg_fast_settle +
+			     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		central_bias_enabled = 1;
+	}
+
+	/* If LDO_H disabled */
+	if (snd_soc_read(codec, WCD9XXX_A_PIN_CTL_OE0) & 0x80) {
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x10, 0);
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0x80);
+		usleep_range(generic->t_ldoh, generic->t_ldoh +
+					      WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE0, 0x80, 0);
+
+		if (central_bias_enabled)
+			snd_soc_update_bits(codec, WCD9XXX_A_PIN_CTL_OE1, 0x1,
+					    0);
+	}
+
+	if (mbhc->resmgr->reg_addr && mbhc->resmgr->reg_addr->micb_4_mbhc)
+		snd_soc_update_bits(codec, mbhc->resmgr->reg_addr->micb_4_mbhc,
+				    0x3, mbhc->mbhc_cfg->micbias);
+
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x1, 0x1);
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
+/*
+ * Function to determine whether anc microphone is preset or not.
+ * Return true if anc microphone is detected or false if not detected.
+ */
+static bool wcd9xxx_detect_anc_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+	struct wcd9xxx_mbhc_detect rt[NUM_DCE_PLUG_INS_DETECT - 1];
+	bool anc_mic_found = true;
+	int i, mb_mv;
+	const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	s16 hs_max, dce_z;
+	s16 no_mic;
+	bool override_en;
+	bool timedout;
+	unsigned long timeout, retry = 0;
+	enum wcd9xxx_mbhc_plug_type type;
+	bool cs_enable;
+
+	if (mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS3 &&
+	    mbhc->mbhc_cfg->anc_micbias != MBHC_MICBIAS2)
+		return false;
+
+	pr_debug("%s: enter\n", __func__);
+
+	override_en = (snd_soc_read(mbhc->codec, WCD9XXX_A_CDC_MBHC_B1_CTL) &
+		       0x04) ? true : false;
+	cs_enable = ((mbhc->mbhc_cfg->cs_enable_flags &
+		    (1 << MBHC_CS_ENABLE_DET_ANC)) != 0) &&
+		    (!(snd_soc_read(mbhc->codec,
+		       mbhc->mbhc_anc_bias_regs.ctl_reg) & 0x80)) &&
+		     (mbhc->mbhc_cfg->micbias != mbhc->mbhc_cfg->anc_micbias);
+
+	if (cs_enable) {
+		wcd9xxx_turn_onoff_current_source(mbhc,
+						  &mbhc->mbhc_anc_bias_regs,
+						  true, false);
+	} else {
+		if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
+			if (mbhc->micbias_enable_cb)
+				mbhc->micbias_enable_cb(mbhc->codec, true,
+						mbhc->mbhc_cfg->anc_micbias);
+			else
+				return false;
+		} else {
+			/* Enable override */
+			if (!override_en)
+				wcd9xxx_turn_onoff_override(mbhc, true);
+		}
+	}
+
+	if (!cs_enable) {
+		hs_max = plug_type->v_hs_max;
+		no_mic = plug_type->v_no_mic;
+		dce_z = mbhc->mbhc_data.dce_z;
+		mb_mv = mbhc->mbhc_data.micb_mv;
+	} else {
+		hs_max = WCD9XXX_V_CS_HS_MAX;
+		no_mic = WCD9XXX_V_CS_NO_MIC;
+		mb_mv = VDDIO_MICBIAS_MV;
+		dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+	}
+
+	wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+
+	timeout = jiffies + msecs_to_jiffies(ANC_HPH_DETECT_PLUG_TIME_MS);
+	anc_mic_found = true;
+
+	while (!(timedout = time_after(jiffies, timeout))) {
+		retry++;
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch level is low\n", __func__);
+			anc_mic_found = false;
+			break;
+		}
+
+		pr_debug("%s: Retry attempt %lu", __func__, retry - 1);
+
+		rt[0].hphl_status = wcd9xxx_hphl_status(mbhc);
+		rt[0].dce = wcd9xxx_mbhc_setup_hs_polling(mbhc,
+						  &mbhc->mbhc_anc_bias_regs,
+						  cs_enable);
+		rt[0]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true, rt[0].dce,
+							 dce_z, (u32)mb_mv);
+
+		if (rt[0]._vdces >= no_mic && rt[0]._vdces < hs_max)
+			rt[0]._type = PLUG_TYPE_HEADSET;
+		else if (rt[0]._vdces < no_mic)
+			rt[0]._type = PLUG_TYPE_HEADPHONE;
+		else
+			rt[0]._type = PLUG_TYPE_HIGH_HPH;
+
+		pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
+				__func__, 0, rt[0]._vdces,
+				rt[0].hphl_status & 0x01,
+				rt[0]._type);
+
+		for (i = 1; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+			rt[i].dce = __wcd9xxx_codec_sta_dce(mbhc, 1,
+							    true, true);
+			rt[i]._vdces = __wcd9xxx_codec_sta_dce_v(mbhc, true,
+							 rt[i].dce, dce_z,
+							 (u32) mb_mv);
+
+			if (rt[i]._vdces >= no_mic && rt[i]._vdces < hs_max)
+				rt[i]._type = PLUG_TYPE_HEADSET;
+			else if (rt[i]._vdces < no_mic)
+				rt[i]._type = PLUG_TYPE_HEADPHONE;
+			else
+				rt[i]._type = PLUG_TYPE_HIGH_HPH;
+
+			rt[i].hphl_status = wcd9xxx_hphl_status(mbhc);
+
+			pr_debug("%s: DCE #%d, V %04d, HPHL %d TYPE %d\n",
+					__func__, i, rt[i]._vdces,
+					rt[i].hphl_status & 0x01,
+					rt[i]._type);
+		}
+
+		/*
+		 * Check for the "type" of all the 4 measurements
+		 * If all 4 measurements have the Type as PLUG_TYPE_HEADSET
+		 * then it is proper mic and declare that the plug has two mics
+		 */
+		for (i = 0; i < NUM_DCE_PLUG_INS_DETECT - 1; i++) {
+			if (i > 0 && (rt[i - 1]._type != rt[i]._type)) {
+				type = PLUG_TYPE_INVALID;
+				break;
+			} else {
+				type = rt[0]._type;
+			}
+		}
+
+		pr_debug("%s: Plug type found in ANC detection :%d",
+			__func__, type);
+
+		if (type != PLUG_TYPE_HEADSET)
+			anc_mic_found = false;
+		if (anc_mic_found || (type == PLUG_TYPE_HEADPHONE &&
+		    mbhc->mbhc_cfg->hw_jack_type == FIVE_POLE_JACK) ||
+		    (type == PLUG_TYPE_HIGH_HPH &&
+		    mbhc->mbhc_cfg->hw_jack_type == SIX_POLE_JACK))
+			break;
+	}
+
+	wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+	if (cs_enable) {
+		wcd9xxx_turn_onoff_current_source(mbhc,
+						  &mbhc->mbhc_anc_bias_regs,
+						  false, false);
+	} else {
+		if (mbhc->mbhc_cfg->anc_micbias == MBHC_MICBIAS3) {
+			if (mbhc->micbias_enable_cb)
+				mbhc->micbias_enable_cb(mbhc->codec, false,
+						mbhc->mbhc_cfg->anc_micbias);
+		} else {
+			/* Disable override */
+			if (!override_en)
+				wcd9xxx_turn_onoff_override(mbhc, false);
+		}
+	}
+	pr_debug("%s: leave\n", __func__);
+	return anc_mic_found;
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_find_plug_and_report(struct wcd9xxx_mbhc *mbhc,
+					 enum wcd9xxx_mbhc_plug_type plug_type)
+{
+	bool anc_mic_found = false;
+
+	pr_debug("%s: enter current_plug(%d) new_plug(%d)\n",
+		 __func__, mbhc->current_plug, plug_type);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if (plug_type == PLUG_TYPE_HEADPHONE &&
+	    mbhc->current_plug == PLUG_TYPE_NONE) {
+		/*
+		 * Nothing was reported previously
+		 * report a headphone or unsupported
+		 */
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+		if (!mbhc->mbhc_cfg->detect_extn_cable) {
+			if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+				wcd9xxx_report_plug(mbhc, 0,
+							 SND_JACK_HEADSET);
+			else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+				wcd9xxx_report_plug(mbhc, 0,
+							 SND_JACK_HEADPHONE);
+		}
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_UNSUPPORTED);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_HEADSET) {
+
+		if (mbhc->mbhc_cfg->enable_anc_mic_detect) {
+			/*
+			 * Do not report Headset, because at this point
+			 * it could be a ANC headphone having two mics.
+			 * So, proceed further to detect if there is a
+			 * second mic.
+			 */
+			mbhc->scaling_mux_in = 0x08;
+			anc_mic_found = wcd9xxx_detect_anc_plug_type(mbhc);
+		}
+
+		if (anc_mic_found) {
+			/* Report ANC headphone */
+			wcd9xxx_report_plug(mbhc, 1, SND_JACK_ANC_HEADPHONE);
+		} else {
+			/*
+			 * If Headphone was reported previously, this will
+			 * only report the mic line
+			 */
+			wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADSET);
+		}
+		/* Button detection required RC oscillator */
+		wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, true);
+		/*
+		 * sleep so that audio path completely tears down
+		 * before report plug insertion to the user space
+		 */
+		msleep(100);
+
+		wcd9xxx_start_hs_polling(mbhc);
+	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+		if (mbhc->mbhc_cfg->detect_extn_cable) {
+			/* High impedance device found. Report as LINEOUT*/
+			if (mbhc->current_plug == PLUG_TYPE_NONE)
+				wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			pr_debug("%s: setup mic trigger for further detection\n",
+				 __func__);
+			mbhc->lpi_enabled = true;
+			/*
+			 * Do not enable HPHL trigger. If playback is active,
+			 * it might lead to continuous false HPHL triggers
+			 */
+			wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER,
+						 false);
+		} else {
+			if (mbhc->current_plug == PLUG_TYPE_NONE)
+				wcd9xxx_report_plug(mbhc, 1,
+							 SND_JACK_HEADPHONE);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			pr_debug("setup mic trigger for further detection\n");
+			mbhc->lpi_enabled = true;
+			wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+							  MBHC_USE_HPHL_TRIGGER,
+						 false);
+		}
+	} else {
+		WARN(1, "Unexpected current plug_type %d, plug_type %d\n",
+		     mbhc->current_plug, plug_type);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_decide_swch_plug(struct wcd9xxx_mbhc *mbhc)
+{
+	enum wcd9xxx_mbhc_plug_type plug_type;
+	bool current_source_enable;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+		      (1 << MBHC_CS_ENABLE_INSERTION)) != 0) &&
+		     (!(snd_soc_read(mbhc->codec,
+				     mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+
+	mbhc->scaling_mux_in = 0x04;
+
+	if (current_source_enable) {
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  true, false);
+		plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc, false);
+		/*
+		 * For other plug types, the current source disable
+		 * will be done from wcd9xxx_correct_swch_plug
+		 */
+		if (plug_type == PLUG_TYPE_HEADSET)
+			wcd9xxx_turn_onoff_current_source(mbhc,
+						&mbhc->mbhc_bias_regs,
+						false, false);
+	} else {
+		wcd9xxx_turn_onoff_override(mbhc, true);
+		plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+		wcd9xxx_turn_onoff_override(mbhc, false);
+	}
+
+	if (wcd9xxx_swch_level_remove(mbhc)) {
+		if (current_source_enable && mbhc->is_cs_enabled) {
+			wcd9xxx_turn_onoff_current_source(mbhc,
+					&mbhc->mbhc_bias_regs,
+					false, false);
+		}
+		pr_debug("%s: Switch level is low when determining plug\n",
+			 __func__);
+		return;
+	}
+
+	if (plug_type == PLUG_TYPE_INVALID ||
+	    plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+		wcd9xxx_cleanup_hs_polling(mbhc);
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else if (plug_type == PLUG_TYPE_HEADPHONE) {
+		wcd9xxx_report_plug(mbhc, 1, SND_JACK_HEADPHONE);
+		wcd9xxx_cleanup_hs_polling(mbhc);
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+		wcd9xxx_cleanup_hs_polling(mbhc);
+		wcd9xxx_schedule_hs_detect_plug(mbhc,
+						&mbhc->correct_plug_swch);
+	} else {
+		pr_debug("%s: Valid plug found, determine plug type %d\n",
+			 __func__, plug_type);
+		wcd9xxx_find_plug_and_report(mbhc, plug_type);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called under codec_resource_lock acquisition */
+static void wcd9xxx_mbhc_detect_plug_type(struct wcd9xxx_mbhc *mbhc)
+{
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if (wcd9xxx_swch_level_remove(mbhc))
+		pr_debug("%s: Switch level low when determining plug\n",
+			 __func__);
+	else
+		wcd9xxx_mbhc_decide_swch_plug(mbhc);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_swch(struct wcd9xxx_mbhc *mbhc,
+				       bool is_removal)
+{
+	if (!is_removal) {
+		pr_debug("%s: MIC trigger insertion interrupt\n", __func__);
+
+		/* Make sure memory read is completed before reading
+		 * lpi_enabled.
+		 */
+		rmb();
+		if (mbhc->lpi_enabled)
+			msleep(100);
+
+		/* Make sure memory read is completed before reading
+		 * lpi_enabled.
+		 */
+		rmb();
+		if (!mbhc->lpi_enabled) {
+			pr_debug("%s: lpi is disabled\n", __func__);
+		} else if (!wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Valid insertion, detect plug type\n",
+				 __func__);
+			wcd9xxx_mbhc_decide_swch_plug(mbhc);
+		} else {
+			pr_debug("%s: Invalid insertion stop plug detection\n",
+				 __func__);
+		}
+	} else if (mbhc->mbhc_cfg->detect_extn_cable) {
+		pr_debug("%s: Removal\n", __func__);
+		if (!wcd9xxx_swch_level_remove(mbhc)) {
+			/*
+			 * Switch indicates, something is still inserted.
+			 * This could be extension cable i.e. headset is
+			 * removed from extension cable.
+			 */
+			/* cancel detect plug */
+			wcd9xxx_cancel_hs_detect_plug(mbhc,
+						      &mbhc->correct_plug_swch);
+			wcd9xxx_mbhc_decide_swch_plug(mbhc);
+		}
+	} else {
+		pr_err("%s: Switch IRQ used, invalid MBHC Removal\n", __func__);
+	}
+}
+
+static bool is_valid_mic_voltage(struct wcd9xxx_mbhc *mbhc, s32 mic_mv,
+				 bool cs_enable)
+{
+	const struct wcd9xxx_mbhc_plug_type_cfg *plug_type =
+	    WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+	const s16 v_hs_max = wcd9xxx_get_current_v_hs_max(mbhc);
+
+	if (cs_enable)
+		return ((mic_mv > WCD9XXX_V_CS_NO_MIC) &&
+			 (mic_mv < WCD9XXX_V_CS_HS_MAX)) ? true : false;
+	else
+		return (!(mic_mv > WCD9XXX_MEAS_INVALD_RANGE_LOW_MV &&
+			  mic_mv < WCD9XXX_MEAS_INVALD_RANGE_HIGH_MV) &&
+			(mic_mv > plug_type->v_no_mic) &&
+			(mic_mv < v_hs_max)) ? true : false;
+}
+
+/*
+ * called under codec_resource_lock acquisition
+ * returns true if mic voltage range is back to normal insertion
+ * returns false either if timedout or removed
+ */
+static bool wcd9xxx_hs_remove_settle(struct wcd9xxx_mbhc *mbhc)
+{
+	int i;
+	bool timedout, settled = false;
+	s32 mic_mv[NUM_DCE_PLUG_DETECT];
+	short mb_v[NUM_DCE_PLUG_DETECT];
+	unsigned long retry = 0, timeout;
+	bool cs_enable;
+
+	cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+		      (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
+		     (!(snd_soc_read(mbhc->codec,
+				     mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+	if (cs_enable)
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  true, false);
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!(timedout = time_after(jiffies, timeout))) {
+		retry++;
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch indicates removal\n", __func__);
+			break;
+		}
+
+		if (retry > 1)
+			msleep(250);
+		else
+			msleep(50);
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switch indicates removal\n", __func__);
+			break;
+		}
+
+		if (cs_enable) {
+			for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+				mb_v[i] = __wcd9xxx_codec_sta_dce(mbhc, 1,
+								  true, true);
+				mic_mv[i] = __wcd9xxx_codec_sta_dce_v(mbhc,
+								      true,
+								      mb_v[i],
+						mbhc->mbhc_data.dce_nsc_cs_z,
+						(u32)VDDIO_MICBIAS_MV);
+				pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+					 __func__, retry, mic_mv[i], mb_v[i]);
+			}
+		} else {
+			for (i = 0; i < NUM_DCE_PLUG_DETECT; i++) {
+				mb_v[i] = wcd9xxx_codec_sta_dce(mbhc, 1,
+								true);
+				mic_mv[i] = wcd9xxx_codec_sta_dce_v(mbhc, 1,
+								mb_v[i]);
+				pr_debug("%s : DCE run %lu, mic_mv = %d(%x)\n",
+					 __func__, retry, mic_mv[i],
+								mb_v[i]);
+			}
+		}
+
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			pr_debug("%s: Switcn indicates removal\n", __func__);
+			break;
+		}
+
+		if (mbhc->current_plug == PLUG_TYPE_NONE) {
+			pr_debug("%s : headset/headphone is removed\n",
+				 __func__);
+			break;
+		}
+
+		for (i = 0; i < NUM_DCE_PLUG_DETECT; i++)
+			if (!is_valid_mic_voltage(mbhc, mic_mv[i], cs_enable))
+				break;
+
+		if (i == NUM_DCE_PLUG_DETECT) {
+			pr_debug("%s: MIC voltage settled\n", __func__);
+			settled = true;
+			msleep(200);
+			break;
+		}
+	}
+
+	if (cs_enable)
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  false, false);
+
+	if (timedout)
+		pr_debug("%s: Microphone did not settle in %d seconds\n",
+			 __func__, HS_DETECT_PLUG_TIME_MS);
+	return settled;
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_swch(struct wcd9xxx_mbhc *mbhc)
+{
+	pr_debug("%s: enter\n", __func__);
+	if (wcd9xxx_hs_remove_settle(mbhc))
+		wcd9xxx_start_hs_polling(mbhc);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_remove_irq_noswch(struct wcd9xxx_mbhc *mbhc)
+{
+	s16 dce, dcez;
+	unsigned long timeout;
+	bool removed = true;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const struct wcd9xxx_mbhc_general_cfg *generic =
+		WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	bool cs_enable;
+	s16 cur_v_ins_h;
+	u32 mb_mv;
+
+	pr_debug("%s: enter\n", __func__);
+	if (mbhc->current_plug != PLUG_TYPE_HEADSET &&
+		mbhc->current_plug != PLUG_TYPE_ANC_HEADPHONE) {
+		pr_debug("%s(): Headset is not inserted, ignore removal\n",
+			 __func__);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+				0x08, 0x08);
+		return;
+	}
+
+	usleep_range(generic->t_shutdown_plug_rem,
+		     generic->t_shutdown_plug_rem +
+		     WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+	/* If micbias is enabled, don't enable current source */
+	cs_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+		      (1 << MBHC_CS_ENABLE_REMOVAL)) != 0) &&
+		     (!(snd_soc_read(codec,
+				     mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+	if (cs_enable)
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  true, false);
+
+	timeout = jiffies + msecs_to_jiffies(FAKE_REMOVAL_MIN_PERIOD_MS);
+	do {
+		if (cs_enable) {
+			dce = __wcd9xxx_codec_sta_dce(mbhc, 1,  true, true);
+			dcez = mbhc->mbhc_data.dce_nsc_cs_z;
+			mb_mv = VDDIO_MICBIAS_MV;
+		} else {
+			dce = wcd9xxx_codec_sta_dce(mbhc, 1,  true);
+			dcez = mbhc->mbhc_data.dce_z;
+			mb_mv = mbhc->mbhc_data.micb_mv;
+		}
+
+		pr_debug("%s: DCE 0x%x,%d\n", __func__, dce,
+			  __wcd9xxx_codec_sta_dce_v(mbhc, true, dce,
+						    dcez, mb_mv));
+
+		cur_v_ins_h = cs_enable ? (s16) mbhc->mbhc_data.v_cs_ins_h :
+					  (wcd9xxx_get_current_v(mbhc,
+					   WCD9XXX_CURRENT_V_INS_H));
+
+		if (dce < cur_v_ins_h) {
+			removed = false;
+			break;
+		}
+	} while (!time_after(jiffies, timeout));
+	pr_debug("%s: headset %sactually removed\n", __func__,
+		  removed ? "" : "not ");
+
+	if (cs_enable)
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  false, false);
+
+	if (removed) {
+		if (mbhc->mbhc_cfg->detect_extn_cable) {
+			if (!wcd9xxx_swch_level_remove(mbhc)) {
+				/*
+				 * extension cable is still plugged in
+				 * report it as LINEOUT device
+				 */
+				if (mbhc->hph_status == SND_JACK_HEADSET)
+					wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc,
+							false);
+				wcd9xxx_report_plug(mbhc, 1, SND_JACK_LINEOUT);
+				wcd9xxx_cleanup_hs_polling(mbhc);
+				wcd9xxx_enable_hs_detect(mbhc, 1,
+							 MBHC_USE_MB_TRIGGER,
+							 false);
+			}
+		} else {
+			/* Cancel possibly running hs_detect_work */
+			wcd9xxx_cancel_hs_detect_plug(mbhc,
+						    &mbhc->correct_plug_noswch);
+			/*
+			 * If this removal is not false, first check the micbias
+			 * switch status and switch it to LDOH if it is already
+			 * switched to VDDIO.
+			 */
+			wcd9xxx_switch_micbias(mbhc, 0);
+
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+			wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_MB_TRIGGER |
+							  MBHC_USE_HPHL_TRIGGER,
+						 true);
+		}
+	} else {
+		wcd9xxx_start_hs_polling(mbhc);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/* called only from interrupt which is under codec_resource_lock acquisition */
+static void wcd9xxx_hs_insert_irq_extn(struct wcd9xxx_mbhc *mbhc,
+				       bool is_mb_trigger)
+{
+	/* Cancel possibly running hs_detect_work */
+	wcd9xxx_cancel_hs_detect_plug(mbhc, &mbhc->correct_plug_swch);
+
+	if (is_mb_trigger) {
+		pr_debug("%s: Waiting for Headphone left trigger\n", __func__);
+		wcd9xxx_enable_hs_detect(mbhc, 1, MBHC_USE_HPHL_TRIGGER, false);
+	} else  {
+		pr_debug("%s: HPHL trigger received, detecting plug type\n",
+			 __func__);
+		wcd9xxx_mbhc_detect_plug_type(mbhc);
+	}
+}
+
+static irqreturn_t wcd9xxx_hs_remove_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter, removal interrupt\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	/*
+	 * While we don't know whether MIC is there or not, let the resmgr know
+	 * so micbias can be disabled temporarily
+	 */
+	if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH_MIC, false);
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH, false);
+	} else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH, false);
+	}
+
+	if (mbhc->mbhc_cfg->detect_extn_cable &&
+	    !wcd9xxx_swch_level_remove(mbhc))
+		wcd9xxx_hs_remove_irq_noswch(mbhc);
+	else
+		wcd9xxx_hs_remove_irq_swch(mbhc);
+
+	if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH, true);
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH_MIC, true);
+	} else if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+		wcd9xxx_resmgr_cond_update_cond(mbhc->resmgr,
+						WCD9XXX_COND_HPH, true);
+	}
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hs_insert_irq(int irq, void *data)
+{
+	bool is_mb_trigger, is_removal;
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	wcd9xxx_disable_irq(mbhc->resmgr->core_res, mbhc->intr_ids->insertion);
+
+	is_mb_trigger = !!(snd_soc_read(codec, mbhc->mbhc_bias_regs.mbhc_reg) &
+			   0x10);
+	is_removal = !!(snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_INT_CTL) & 0x02);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_INT_CTL, 0x03, 0x00);
+
+	/* Turn off both HPH and MIC line schmitt triggers */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+
+	if (mbhc->mbhc_cfg->detect_extn_cable &&
+	    mbhc->current_plug == PLUG_TYPE_HIGH_HPH)
+		wcd9xxx_hs_insert_irq_extn(mbhc, is_mb_trigger);
+	else
+		wcd9xxx_hs_insert_irq_swch(mbhc, is_removal);
+
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static void wcd9xxx_btn_lpress_fn(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	short bias_value;
+	int dce_mv, sta_mv;
+	struct wcd9xxx_mbhc *mbhc;
+
+	pr_debug("%s:\n", __func__);
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_btn_dwork);
+
+	bias_value = wcd9xxx_read_sta_result(mbhc->codec);
+	sta_mv = wcd9xxx_codec_sta_dce_v(mbhc, 0, bias_value);
+
+	bias_value = wcd9xxx_read_dce_result(mbhc->codec);
+	dce_mv = wcd9xxx_codec_sta_dce_v(mbhc, 1, bias_value);
+	pr_debug("%s: STA: %d, DCE: %d\n", __func__, sta_mv, dce_mv);
+
+	pr_debug("%s: Reporting long button press event\n", __func__);
+	wcd9xxx_jack_report(mbhc, &mbhc->button_jack, mbhc->buttons_pressed,
+			    mbhc->buttons_pressed);
+
+	pr_debug("%s: leave\n", __func__);
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+}
+
+static void wcd9xxx_mbhc_insert_work(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	struct wcd9xxx_core_resource *core_res;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_insert_dwork);
+	codec = mbhc->codec;
+	core_res = mbhc->resmgr->core_res;
+
+	pr_debug("%s:\n", __func__);
+
+	/* Turn off both HPH and MIC line schmitt triggers */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.mbhc_reg, 0x90, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x13, 0x00);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	wcd9xxx_disable_irq_sync(core_res, mbhc->intr_ids->insertion);
+	wcd9xxx_mbhc_detect_plug_type(mbhc);
+	wcd9xxx_unlock_sleep(core_res);
+}
+
+static bool wcd9xxx_mbhc_fw_validate(const void *data, size_t size)
+{
+	u32 cfg_offset;
+	struct wcd9xxx_mbhc_imped_detect_cfg *imped_cfg;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_cfg;
+	struct firmware_cal fw;
+
+	fw.data = (void *)data;
+	fw.size = size;
+
+	if (fw.size < WCD9XXX_MBHC_CAL_MIN_SIZE)
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to num_btn
+	 */
+	btn_cfg = WCD9XXX_MBHC_CAL_BTN_DET_PTR(fw.data);
+	cfg_offset = (u32) ((void *) btn_cfg - (void *) fw.data);
+	if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_BTN_SZ(btn_cfg)))
+		return false;
+
+	/*
+	 * Previous check guarantees that there is enough fw data up
+	 * to start of impedance detection configuration
+	 */
+	imped_cfg = WCD9XXX_MBHC_CAL_IMPED_DET_PTR(fw.data);
+	cfg_offset = (u32) ((void *) imped_cfg - (void *) fw.data);
+
+	if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_MIN_SZ))
+		return false;
+
+	if (fw.size < (cfg_offset + WCD9XXX_MBHC_CAL_IMPED_SZ(imped_cfg)))
+		return false;
+
+	return true;
+}
+
+static u16 wcd9xxx_codec_v_sta_dce(struct wcd9xxx_mbhc *mbhc,
+				   enum meas_type dce, s16 vin_mv,
+				   bool cs_enable)
+{
+	s16 diff, zero;
+	u32 mb_mv, in;
+	u16 value;
+	s16 dce_z;
+
+	mb_mv = mbhc->mbhc_data.micb_mv;
+	dce_z = mbhc->mbhc_data.dce_z;
+
+	if (mb_mv == 0) {
+		pr_err("%s: Mic Bias voltage is set to zero\n", __func__);
+		return -EINVAL;
+	}
+	if (cs_enable) {
+		mb_mv = VDDIO_MICBIAS_MV;
+		dce_z = mbhc->mbhc_data.dce_nsc_cs_z;
+	}
+
+	if (dce) {
+		diff = (mbhc->mbhc_data.dce_mb) - (dce_z);
+		zero = (dce_z);
+	} else {
+		diff = (mbhc->mbhc_data.sta_mb) - (mbhc->mbhc_data.sta_z);
+		zero = (mbhc->mbhc_data.sta_z);
+	}
+	in = (u32) diff * vin_mv;
+
+	value = (u16) (in / mb_mv) + zero;
+	return value;
+}
+
+static void wcd9xxx_mbhc_calc_thres(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec;
+	s16 adj_v_hs_max;
+	s16 btn_mv = 0, btn_mv_sta[MBHC_V_IDX_NUM], btn_mv_dce[MBHC_V_IDX_NUM];
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	struct wcd9xxx_mbhc_plug_type_cfg *plug_type;
+	u16 *btn_high;
+	int i;
+
+	pr_debug("%s: enter\n", __func__);
+	codec = mbhc->codec;
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	plug_type = WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(mbhc->mbhc_cfg->calibration);
+
+	mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_CFILT] =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_hs_max, false);
+	mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_CFILT] =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, plug_type->v_hs_max, false);
+
+	mbhc->mbhc_data.v_inval_ins_low = FAKE_INS_LOW;
+	mbhc->mbhc_data.v_inval_ins_high = FAKE_INS_HIGH;
+
+	if (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV) {
+		adj_v_hs_max = scale_v_micb_vddio(mbhc, plug_type->v_hs_max,
+						  true);
+		mbhc->mbhc_data.v_ins_hu[MBHC_V_IDX_VDDIO] =
+		    wcd9xxx_codec_v_sta_dce(mbhc, STA, adj_v_hs_max, false);
+		mbhc->mbhc_data.v_ins_h[MBHC_V_IDX_VDDIO] =
+		    wcd9xxx_codec_v_sta_dce(mbhc, DCE, adj_v_hs_max, false);
+		mbhc->mbhc_data.v_inval_ins_low =
+		    scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_low,
+				       false);
+		mbhc->mbhc_data.v_inval_ins_high =
+		    scale_v_micb_vddio(mbhc, mbhc->mbhc_data.v_inval_ins_high,
+				       false);
+	}
+	mbhc->mbhc_data.v_cs_ins_h = wcd9xxx_codec_v_sta_dce(mbhc, DCE,
+							WCD9XXX_V_CS_HS_MAX,
+							true);
+	pr_debug("%s: v_ins_h for current source: 0x%x\n", __func__,
+		  mbhc->mbhc_data.v_cs_ins_h);
+
+	btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+					       MBHC_BTN_DET_V_BTN_HIGH);
+	for (i = 0; i < btn_det->num_btn; i++)
+		btn_mv = btn_high[i] > btn_mv ? btn_high[i] : btn_mv;
+
+	btn_mv_sta[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_sta;
+	btn_mv_dce[MBHC_V_IDX_CFILT] = btn_mv + btn_det->v_btn_press_delta_cic;
+	btn_mv_sta[MBHC_V_IDX_VDDIO] =
+	    scale_v_micb_vddio(mbhc, btn_mv_sta[MBHC_V_IDX_CFILT], true);
+	btn_mv_dce[MBHC_V_IDX_VDDIO] =
+	    scale_v_micb_vddio(mbhc, btn_mv_dce[MBHC_V_IDX_CFILT], true);
+
+	mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_CFILT] =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_CFILT],
+				    false);
+	mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT] =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_CFILT],
+				    false);
+	mbhc->mbhc_data.v_b1_hu[MBHC_V_IDX_VDDIO] =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, btn_mv_sta[MBHC_V_IDX_VDDIO],
+				    false);
+	mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO] =
+	    wcd9xxx_codec_v_sta_dce(mbhc, DCE, btn_mv_dce[MBHC_V_IDX_VDDIO],
+				    false);
+
+	mbhc->mbhc_data.v_brh[MBHC_V_IDX_CFILT] =
+	    mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_CFILT];
+	mbhc->mbhc_data.v_brh[MBHC_V_IDX_VDDIO] =
+	    mbhc->mbhc_data.v_b1_h[MBHC_V_IDX_VDDIO];
+
+	mbhc->mbhc_data.v_brl = BUTTON_MIN;
+
+	mbhc->mbhc_data.v_no_mic =
+	    wcd9xxx_codec_v_sta_dce(mbhc, STA, plug_type->v_no_mic, false);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_onoff_ext_mclk(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	/*
+	 * XXX: {codec}_mclk_enable holds WCD9XXX_BCL_LOCK,
+	 * therefore wcd9xxx_onoff_ext_mclk caller SHOULDN'T hold
+	 * WCD9XXX_BCL_LOCK when it calls wcd9xxx_onoff_ext_mclk()
+	 */
+	if (mbhc && mbhc->mbhc_cfg && mbhc->mbhc_cfg->mclk_cb_fn)
+		mbhc->mbhc_cfg->mclk_cb_fn(mbhc->codec, on, false);
+}
+
+/*
+ * Mic Bias Enable Decision
+ * Return true if high_hph_cnt is a power of 2 (!= 2)
+ * otherwise return false
+ */
+static bool wcd9xxx_mbhc_enable_mb_decision(int high_hph_cnt)
+{
+	return (high_hph_cnt > 2) && !(high_hph_cnt & (high_hph_cnt - 1));
+}
+
+static inline void wcd9xxx_handle_gnd_mic_swap(struct wcd9xxx_mbhc *mbhc,
+					int pt_gnd_mic_swap_cnt,
+					enum wcd9xxx_mbhc_plug_type plug_type)
+{
+	if (mbhc->mbhc_cfg->swap_gnd_mic &&
+	    (pt_gnd_mic_swap_cnt == GND_MIC_SWAP_THRESHOLD)) {
+		/*
+		 * if switch is toggled, check again,
+		 * otherwise report unsupported plug
+		 */
+		mbhc->mbhc_cfg->swap_gnd_mic(mbhc->codec);
+	} else if (pt_gnd_mic_swap_cnt >= GND_MIC_SWAP_THRESHOLD) {
+		/* Report UNSUPPORTED plug
+		 * and continue polling
+		 */
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		if (!mbhc->mbhc_cfg->detect_extn_cable) {
+			if (mbhc->current_plug == PLUG_TYPE_HEADPHONE)
+				wcd9xxx_report_plug(mbhc, 0,
+						    SND_JACK_HEADPHONE);
+			else if (mbhc->current_plug == PLUG_TYPE_HEADSET)
+				wcd9xxx_report_plug(mbhc, 0,
+						    SND_JACK_HEADSET);
+		}
+		if (mbhc->current_plug != plug_type)
+			wcd9xxx_report_plug(mbhc, 1,
+					SND_JACK_UNSUPPORTED);
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	}
+}
+
+static void wcd9xxx_correct_swch_plug(struct work_struct *work)
+{
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	enum wcd9xxx_mbhc_plug_type plug_type = PLUG_TYPE_INVALID;
+	unsigned long timeout;
+	int retry = 0, pt_gnd_mic_swap_cnt = 0;
+	int highhph_cnt = 0;
+	bool correction = false;
+	bool current_source_enable;
+	bool wrk_complete = true, highhph = false;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc = container_of(work, struct wcd9xxx_mbhc, correct_plug_swch);
+	codec = mbhc->codec;
+
+	current_source_enable = (((mbhc->mbhc_cfg->cs_enable_flags &
+		      (1 << MBHC_CS_ENABLE_POLLING)) != 0) &&
+		     (!(snd_soc_read(codec,
+				     mbhc->mbhc_bias_regs.ctl_reg) & 0x80)));
+
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+	/*
+	 * Keep override on during entire plug type correction work.
+	 *
+	 * This is okay under the assumption that any switch irqs which use
+	 * MBHC block cancel and sync this work so override is off again
+	 * prior to switch interrupt handler's MBHC block usage.
+	 * Also while this correction work is running, we can guarantee
+	 * DAPM doesn't use any MBHC block as this work only runs with
+	 * headphone detection.
+	 */
+	if (current_source_enable) {
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  true, false);
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	} else {
+		wcd9xxx_turn_onoff_override(mbhc, true);
+	}
+
+	timeout = jiffies + msecs_to_jiffies(HS_DETECT_PLUG_TIME_MS);
+	while (!time_after(jiffies, timeout)) {
+		++retry;
+
+		/* Make sure any pending memory read is completed, before
+		 * hs_detect_work_stop value is read.
+		 */
+		rmb();
+		if (mbhc->hs_detect_work_stop) {
+			wrk_complete = false;
+			pr_debug("%s: stop requested\n", __func__);
+			break;
+		}
+
+		msleep(HS_DETECT_PLUG_INERVAL_MS);
+		if (wcd9xxx_swch_level_remove(mbhc)) {
+			wrk_complete = false;
+			pr_debug("%s: Switch level is low\n", __func__);
+			break;
+		}
+
+		/* can race with removal interrupt */
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		if (current_source_enable)
+			plug_type = wcd9xxx_codec_cs_get_plug_type(mbhc,
+								   highhph);
+		else
+			plug_type = wcd9xxx_codec_get_plug_type(mbhc, true);
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+
+		pr_debug("%s: attempt(%d) current_plug(%d) new_plug(%d)\n",
+			 __func__, retry, mbhc->current_plug, plug_type);
+
+		highhph_cnt = (plug_type == PLUG_TYPE_HIGH_HPH) ?
+					(highhph_cnt + 1) :
+					0;
+		highhph = wcd9xxx_mbhc_enable_mb_decision(highhph_cnt);
+		if (plug_type == PLUG_TYPE_INVALID) {
+			pr_debug("Invalid plug in attempt # %d\n", retry);
+			if (!mbhc->mbhc_cfg->detect_extn_cable &&
+			    retry == NUM_ATTEMPTS_TO_REPORT &&
+			    mbhc->current_plug == PLUG_TYPE_NONE) {
+				WCD9XXX_BCL_LOCK(mbhc->resmgr);
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+				WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+			}
+		} else if (plug_type == PLUG_TYPE_HEADPHONE) {
+			pr_debug("Good headphone detected, continue polling\n");
+			WCD9XXX_BCL_LOCK(mbhc->resmgr);
+			if (mbhc->mbhc_cfg->detect_extn_cable) {
+				if (mbhc->current_plug != plug_type)
+					wcd9xxx_report_plug(mbhc, 1,
+							    SND_JACK_HEADPHONE);
+			} else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+			}
+			WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+		} else if (plug_type == PLUG_TYPE_HIGH_HPH) {
+			pr_debug("%s: High HPH detected, continue polling\n",
+				  __func__);
+			WCD9XXX_BCL_LOCK(mbhc->resmgr);
+			if (mbhc->mbhc_cfg->detect_extn_cable) {
+				if (mbhc->current_plug != plug_type)
+					wcd9xxx_report_plug(mbhc, 1,
+							    SND_JACK_LINEOUT);
+			} else if (mbhc->current_plug == PLUG_TYPE_NONE) {
+				wcd9xxx_report_plug(mbhc, 1,
+						    SND_JACK_HEADPHONE);
+			}
+			WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+		} else {
+			if (plug_type == PLUG_TYPE_GND_MIC_SWAP) {
+				pt_gnd_mic_swap_cnt++;
+				if (pt_gnd_mic_swap_cnt >=
+						GND_MIC_SWAP_THRESHOLD)
+					wcd9xxx_handle_gnd_mic_swap(mbhc,
+							pt_gnd_mic_swap_cnt,
+							plug_type);
+				pr_debug("%s: unsupported HS detected, continue polling\n",
+					 __func__);
+				continue;
+			} else {
+				pt_gnd_mic_swap_cnt = 0;
+
+				WCD9XXX_BCL_LOCK(mbhc->resmgr);
+				/* Turn off override/current source */
+				if (current_source_enable)
+					wcd9xxx_turn_onoff_current_source(mbhc,
+							&mbhc->mbhc_bias_regs,
+							false, false);
+				else
+					wcd9xxx_turn_onoff_override(mbhc,
+								    false);
+				/*
+				 * The valid plug also includes
+				 * PLUG_TYPE_GND_MIC_SWAP
+				 */
+				wcd9xxx_find_plug_and_report(mbhc, plug_type);
+				WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+				pr_debug("Attempt %d found correct plug %d\n",
+						retry,
+						plug_type);
+				correction = true;
+			}
+			break;
+		}
+	}
+
+	highhph = false;
+	if (wrk_complete && plug_type == PLUG_TYPE_HIGH_HPH) {
+		pr_debug("%s: polling is done, still HPH, so enabling MIC trigger\n",
+			 __func__);
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		wcd9xxx_find_plug_and_report(mbhc, plug_type);
+		highhph = true;
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	}
+
+	if (plug_type == PLUG_TYPE_HEADPHONE) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->hph_auto_pulldown_ctrl)
+			mbhc->mbhc_cb->hph_auto_pulldown_ctrl(codec, true);
+	}
+
+	if (!correction && current_source_enable) {
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		wcd9xxx_turn_onoff_current_source(mbhc, &mbhc->mbhc_bias_regs,
+						  false, highhph);
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	} else if (!correction) {
+		wcd9xxx_turn_onoff_override(mbhc, false);
+	}
+
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+	if (mbhc->mbhc_cfg->detect_extn_cable) {
+		WCD9XXX_BCL_LOCK(mbhc->resmgr);
+		if ((mbhc->current_plug == PLUG_TYPE_HEADPHONE &&
+		    wrk_complete) ||
+		    mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP ||
+		    mbhc->current_plug == PLUG_TYPE_INVALID ||
+		    (plug_type == PLUG_TYPE_INVALID && wrk_complete)) {
+			/* Enable removal detection */
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_enable_hs_detect(mbhc, 0, 0, false);
+		}
+		WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	}
+	pr_debug("%s: leave current_plug(%d)\n", __func__, mbhc->current_plug);
+	/* unlock sleep */
+	wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+}
+
+static void wcd9xxx_swch_irq_handler(struct wcd9xxx_mbhc *mbhc)
+{
+	bool insert;
+	bool is_removed = false;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	mbhc->in_swch_irq_handler = true;
+	/* Wait here for debounce time */
+	usleep_range(SWCH_IRQ_DEBOUNCE_TIME_US, SWCH_IRQ_DEBOUNCE_TIME_US +
+					WCD9XXX_USLEEP_RANGE_MARGIN_US);
+
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+
+	/* cancel pending button press */
+	if (wcd9xxx_cancel_btn_work(mbhc))
+		pr_debug("%s: button press is canceled\n", __func__);
+
+	insert = !wcd9xxx_swch_level_remove(mbhc);
+	pr_debug("%s: Current plug type %d, insert %d\n", __func__,
+		 mbhc->current_plug, insert);
+	if ((mbhc->current_plug == PLUG_TYPE_NONE) && insert) {
+
+		mbhc->lpi_enabled = false;
+
+		/* Make sure mbhc state update complete before cancel detect
+		 * plug.
+		 */
+		wmb();
+		/* cancel detect plug */
+		wcd9xxx_cancel_hs_detect_plug(mbhc,
+				      &mbhc->correct_plug_swch);
+
+		if ((mbhc->current_plug != PLUG_TYPE_NONE) &&
+		    (mbhc->current_plug != PLUG_TYPE_HIGH_HPH) &&
+		    !(snd_soc_read(codec, WCD9XXX_A_MBHC_INSERT_DETECT) &
+				   (1 << 1))) {
+			pr_debug("%s: current plug: %d\n", __func__,
+				mbhc->current_plug);
+			goto exit;
+		}
+
+		/* Disable Mic Bias pull down and HPH Switch to GND */
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x00);
+		wcd9xxx_mbhc_detect_plug_type(mbhc);
+	} else if ((mbhc->current_plug != PLUG_TYPE_NONE) && !insert) {
+		mbhc->lpi_enabled = false;
+
+		/* Make sure mbhc state update complete before cancel detect
+		 * plug.
+		 */
+		wmb();
+		/* cancel detect plug */
+		wcd9xxx_cancel_hs_detect_plug(mbhc,
+				      &mbhc->correct_plug_swch);
+
+		if (mbhc->current_plug == PLUG_TYPE_HEADPHONE) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADPHONE);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_GND_MIC_SWAP) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_UNSUPPORTED);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_HEADSET) {
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_HEADSET);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_HIGH_HPH) {
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_LINEOUT);
+			is_removed = true;
+		} else if (mbhc->current_plug == PLUG_TYPE_ANC_HEADPHONE) {
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_mbhc_ctrl_clk_bandgap(mbhc, false);
+			wcd9xxx_cleanup_hs_polling(mbhc);
+			wcd9xxx_report_plug(mbhc, 0, SND_JACK_ANC_HEADPHONE);
+			is_removed = true;
+		}
+
+		if (is_removed) {
+			snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				      0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+					    0x02, 0x00);
+
+			/* Enable Mic Bias pull down and HPH Switch to GND */
+			snd_soc_update_bits(codec,
+					mbhc->mbhc_bias_regs.ctl_reg, 0x01,
+					0x01);
+			snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01,
+					0x01);
+			/* Make sure mic trigger is turned off */
+			snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+					    0x01, 0x01);
+			snd_soc_update_bits(codec,
+					    mbhc->mbhc_bias_regs.mbhc_reg,
+					    0x90, 0x00);
+			/* Reset MBHC State Machine */
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x08);
+			snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL,
+					    0x08, 0x00);
+			/* Turn off override */
+			wcd9xxx_turn_onoff_override(mbhc, false);
+		}
+	}
+exit:
+	mbhc->in_swch_irq_handler = false;
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static irqreturn_t wcd9xxx_mech_plug_detect_irq(int irq, void *data)
+{
+	int r = IRQ_HANDLED;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	if (unlikely(wcd9xxx_lock_sleep(mbhc->resmgr->core_res) == false)) {
+		pr_warn("%s: failed to hold suspend\n", __func__);
+		r = IRQ_NONE;
+	} else {
+		/* Call handler */
+		wcd9xxx_swch_irq_handler(mbhc);
+		wcd9xxx_unlock_sleep(mbhc->resmgr->core_res);
+	}
+
+	pr_debug("%s: leave %d\n", __func__, r);
+	return r;
+}
+
+static int wcd9xxx_is_false_press(struct wcd9xxx_mbhc *mbhc)
+{
+	s16 mb_v;
+	int i = 0;
+	int r = 0;
+	const s16 v_ins_hu =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
+	const s16 v_ins_h =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
+	const s16 v_b1_hu =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
+	const s16 v_b1_h =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+	const unsigned long timeout =
+	    jiffies + msecs_to_jiffies(BTN_RELEASE_DEBOUNCE_TIME_MS);
+
+	while (time_before(jiffies, timeout)) {
+		/*
+		 * This function needs to run measurements just few times during
+		 * release debounce time.  Make 1ms interval to avoid
+		 * unnecessary excessive measurements.
+		 */
+		usleep_range(1000, 1000 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		if (i == 0) {
+			mb_v = wcd9xxx_codec_sta_dce(mbhc, 0, true);
+			pr_debug("%s: STA[0]: %d,%d\n", __func__, mb_v,
+				 wcd9xxx_codec_sta_dce_v(mbhc, 0, mb_v));
+			if (mb_v < v_b1_hu || mb_v > v_ins_hu) {
+				r = 1;
+				break;
+			}
+		} else {
+			mb_v = wcd9xxx_codec_sta_dce(mbhc, 1, true);
+			pr_debug("%s: DCE[%d]: %d,%d\n", __func__, i, mb_v,
+				 wcd9xxx_codec_sta_dce_v(mbhc, 1, mb_v));
+			if (mb_v < v_b1_h || mb_v > v_ins_h) {
+				r = 1;
+				break;
+			}
+		}
+		i++;
+	}
+
+	return r;
+}
+
+/* called under codec_resource_lock acquisition */
+static int wcd9xxx_determine_button(const struct wcd9xxx_mbhc *mbhc,
+				  const s32 micmv)
+{
+	s16 *v_btn_low, *v_btn_high;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	int i, btn = -1;
+
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	v_btn_low = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						MBHC_BTN_DET_V_BTN_LOW);
+	v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						 MBHC_BTN_DET_V_BTN_HIGH);
+
+	for (i = 0; i < btn_det->num_btn; i++) {
+		if ((v_btn_low[i] <= micmv) && (v_btn_high[i] >= micmv)) {
+			btn = i;
+			break;
+		}
+	}
+
+	if (btn == -1)
+		pr_debug("%s: couldn't find button number for mic mv %d\n",
+			 __func__, micmv);
+
+	return btn;
+}
+
+static int wcd9xxx_get_button_mask(const int btn)
+{
+	int mask = 0;
+
+	switch (btn) {
+	case 0:
+		mask = SND_JACK_BTN_0;
+		break;
+	case 1:
+		mask = SND_JACK_BTN_1;
+		break;
+	case 2:
+		mask = SND_JACK_BTN_2;
+		break;
+	case 3:
+		mask = SND_JACK_BTN_3;
+		break;
+	case 4:
+		mask = SND_JACK_BTN_4;
+		break;
+	case 5:
+		mask = SND_JACK_BTN_5;
+		break;
+	}
+	return mask;
+}
+
+static void wcd9xxx_get_z(struct wcd9xxx_mbhc *mbhc, s16 *dce_z, s16 *sta_z,
+			  struct mbhc_micbias_regs *micb_regs,
+			  bool norel_detection)
+{
+	s16 reg0, reg1;
+	int change;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+	/* Pull down micbias to ground and disconnect vddio switch */
+	reg0 = snd_soc_read(codec, micb_regs->ctl_reg);
+	snd_soc_update_bits(codec, micb_regs->ctl_reg, 0x81, 0x1);
+	reg1 = snd_soc_read(codec, micb_regs->mbhc_reg);
+	snd_soc_update_bits(codec, micb_regs->mbhc_reg, 1 << 7, 0);
+
+	/* Disconnect override from micbias */
+	change = snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
+				     1 << 0);
+	usleep_range(1000, 1000 + 1000);
+	if (sta_z) {
+		*sta_z = wcd9xxx_codec_sta_dce(mbhc, 0, norel_detection);
+		pr_debug("%s: sta_z 0x%x\n", __func__, *sta_z & 0xFFFF);
+	}
+	if (dce_z) {
+		*dce_z = wcd9xxx_codec_sta_dce(mbhc, 1, norel_detection);
+		pr_debug("%s: dce_z 0x%x\n", __func__, *dce_z & 0xFFFF);
+	}
+
+	/* Connect override from micbias */
+	if (change)
+		snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4,
+				    1 << 4);
+	/* Disable pull down micbias to ground */
+	snd_soc_write(codec, micb_regs->mbhc_reg, reg1);
+	snd_soc_write(codec, micb_regs->ctl_reg, reg0);
+}
+
+/*
+ * This function recalibrates dce_z and sta_z parameters.
+ * No release detection will be false when this function is
+ * used.
+ */
+void wcd9xxx_update_z(struct wcd9xxx_mbhc *mbhc)
+{
+	const u16 sta_z = mbhc->mbhc_data.sta_z;
+	const u16 dce_z = mbhc->mbhc_data.dce_z;
+
+	wcd9xxx_get_z(mbhc, &mbhc->mbhc_data.dce_z, &mbhc->mbhc_data.sta_z,
+		      &mbhc->mbhc_bias_regs, false);
+	pr_debug("%s: sta_z 0x%x,dce_z 0x%x -> sta_z 0x%x,dce_z 0x%x\n",
+		 __func__, sta_z & 0xFFFF, dce_z & 0xFFFF,
+		 mbhc->mbhc_data.sta_z & 0xFFFF,
+		 mbhc->mbhc_data.dce_z & 0xFFFF);
+
+	wcd9xxx_mbhc_calc_thres(mbhc);
+	wcd9xxx_calibrate_hs_polling(mbhc);
+}
+
+/*
+ * wcd9xxx_update_rel_threshold : update mbhc release upper bound threshold
+ *				  to ceilmv + buffer
+ */
+static int wcd9xxx_update_rel_threshold(struct wcd9xxx_mbhc *mbhc, int ceilmv,
+					bool vddio)
+{
+	u16 v_brh, v_b1_hu;
+	int mv;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	void *calibration = mbhc->mbhc_cfg->calibration;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+	mv = ceilmv + btn_det->v_btn_press_delta_cic;
+	if (vddio)
+		mv = scale_v_micb_vddio(mbhc, mv, true);
+	pr_debug("%s: reprogram vb1hu/vbrh to %dmv\n", __func__, mv);
+
+	if (mbhc->mbhc_state != MBHC_STATE_POTENTIAL_RECOVERY) {
+		/*
+		 * update LSB first so mbhc hardware block
+		 * doesn't see too low value.
+		 */
+		v_b1_hu = wcd9xxx_codec_v_sta_dce(mbhc, STA, mv, false);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B3_CTL, v_b1_hu &
+				0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B4_CTL,
+				(v_b1_hu >> 8) & 0xFF);
+		v_brh = wcd9xxx_codec_v_sta_dce(mbhc, DCE, mv, false);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B9_CTL, v_brh &
+				0xFF);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_VOLT_B10_CTL,
+				(v_brh >> 8) & 0xFF);
+	}
+	return 0;
+}
+
+irqreturn_t wcd9xxx_dce_handler(int irq, void *data)
+{
+	int i, mask;
+	bool vddio;
+	u8 mbhc_status;
+	s16 dce_z, sta_z;
+	s32 stamv, stamv_s;
+	s16 *v_btn_high;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	int btn = -1, meas = 0;
+	struct wcd9xxx_mbhc *mbhc = data;
+	const struct wcd9xxx_mbhc_btn_detect_cfg *d =
+	    WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+	short btnmeas[d->n_btn_meas + 1];
+	short dce[d->n_btn_meas + 1], sta;
+	s32 mv[d->n_btn_meas + 1], mv_s[d->n_btn_meas + 1];
+	struct snd_soc_codec *codec = mbhc->codec;
+	struct wcd9xxx_core_resource *core_res = mbhc->resmgr->core_res;
+	int n_btn_meas = d->n_btn_meas;
+	void *calibration = mbhc->mbhc_cfg->calibration;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	mutex_lock(&mbhc->mbhc_lock);
+	mbhc_status = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_STATUS) & 0x3E;
+
+	if (mbhc->mbhc_state == MBHC_STATE_POTENTIAL_RECOVERY) {
+		pr_debug("%s: mbhc is being recovered, skip button press\n",
+			 __func__);
+		goto done;
+	}
+
+	mbhc->mbhc_state = MBHC_STATE_POTENTIAL;
+
+	if (!mbhc->polling_active) {
+		pr_warn("%s: mbhc polling is not active, skip button press\n",
+			__func__);
+		goto done;
+	}
+
+	/* If switch nterrupt already kicked in, ignore button press */
+	if (mbhc->in_swch_irq_handler) {
+		pr_debug("%s: Swtich level changed, ignore button press\n",
+			 __func__);
+		btn = -1;
+		goto done;
+	}
+
+	/*
+	 * setup internal micbias if codec uses internal micbias for
+	 * headset detection
+	 */
+	if (mbhc->mbhc_cfg->use_int_rbias) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias)
+			mbhc->mbhc_cb->setup_int_rbias(codec, true);
+		else
+			pr_err("%s: internal bias requested but codec did not provide callback\n",
+				__func__);
+	}
+
+
+	/* Measure scaled HW DCE */
+	vddio = (mbhc->mbhc_data.micb_mv != VDDIO_MICBIAS_MV &&
+		 mbhc->mbhc_micbias_switched);
+
+	dce_z = mbhc->mbhc_data.dce_z;
+	sta_z = mbhc->mbhc_data.sta_z;
+
+	/* Measure scaled HW STA */
+	dce[0] = wcd9xxx_read_dce_result(codec);
+	sta = wcd9xxx_read_sta_result(codec);
+	if (mbhc_status != STATUS_REL_DETECTION) {
+		if (mbhc->mbhc_last_resume &&
+		    !time_after(jiffies, mbhc->mbhc_last_resume + HZ)) {
+			pr_debug("%s: Button is released after resume\n",
+				__func__);
+			n_btn_meas = 0;
+		} else {
+			pr_debug("%s: Button is released without resume",
+				 __func__);
+			if (mbhc->update_z) {
+				wcd9xxx_update_z(mbhc);
+				dce_z = mbhc->mbhc_data.dce_z;
+				sta_z = mbhc->mbhc_data.sta_z;
+				mbhc->update_z = true;
+			}
+			stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
+						mbhc->mbhc_data.micb_mv);
+			if (vddio)
+				stamv_s = scale_v_micb_vddio(mbhc, stamv,
+							     false);
+			else
+				stamv_s = stamv;
+			mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0],
+					  dce_z, mbhc->mbhc_data.micb_mv);
+			mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0],
+							     false) : mv[0];
+			btn = wcd9xxx_determine_button(mbhc, mv_s[0]);
+			if (btn != wcd9xxx_determine_button(mbhc, stamv_s))
+				btn = -1;
+			goto done;
+		}
+	}
+
+	for (meas = 1; ((d->n_btn_meas) && (meas < (d->n_btn_meas + 1)));
+	     meas++)
+		dce[meas] = wcd9xxx_codec_sta_dce(mbhc, 1, false);
+
+	if (mbhc->update_z) {
+		wcd9xxx_update_z(mbhc);
+		dce_z = mbhc->mbhc_data.dce_z;
+		sta_z = mbhc->mbhc_data.sta_z;
+		mbhc->update_z = true;
+	}
+
+	stamv = __wcd9xxx_codec_sta_dce_v(mbhc, 0, sta, sta_z,
+					  mbhc->mbhc_data.micb_mv);
+	if (vddio)
+		stamv_s = scale_v_micb_vddio(mbhc, stamv, false);
+	else
+		stamv_s = stamv;
+	pr_debug("%s: Meas HW - STA 0x%x,%d,%d\n", __func__,
+		 sta & 0xFFFF, stamv, stamv_s);
+
+	/* determine pressed button */
+	mv[0] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[0], dce_z,
+					  mbhc->mbhc_data.micb_mv);
+	mv_s[0] = vddio ? scale_v_micb_vddio(mbhc, mv[0], false) : mv[0];
+	btnmeas[0] = wcd9xxx_determine_button(mbhc, mv_s[0]);
+	pr_debug("%s: Meas HW - DCE 0x%x,%d,%d button %d\n", __func__,
+		 dce[0] & 0xFFFF, mv[0], mv_s[0], btnmeas[0]);
+	if (n_btn_meas == 0)
+		btn = btnmeas[0];
+	for (meas = 1; (n_btn_meas && d->n_btn_meas &&
+			(meas < (d->n_btn_meas + 1))); meas++) {
+		mv[meas] = __wcd9xxx_codec_sta_dce_v(mbhc, 1, dce[meas], dce_z,
+						     mbhc->mbhc_data.micb_mv);
+		mv_s[meas] = vddio ? scale_v_micb_vddio(mbhc, mv[meas], false) :
+				     mv[meas];
+		btnmeas[meas] = wcd9xxx_determine_button(mbhc, mv_s[meas]);
+		pr_debug("%s: Meas %d - DCE 0x%x,%d,%d button %d\n",
+			 __func__, meas, dce[meas] & 0xFFFF, mv[meas],
+			 mv_s[meas], btnmeas[meas]);
+		/*
+		 * if large enough measurements are collected,
+		 * start to check if last all n_btn_con measurements were
+		 * in same button low/high range
+		 */
+		if (meas + 1 >= d->n_btn_con) {
+			for (i = 0; i < d->n_btn_con; i++)
+				if ((btnmeas[meas] < 0) ||
+				    (btnmeas[meas] != btnmeas[meas - i]))
+					break;
+			if (i == d->n_btn_con) {
+				/* button pressed */
+				btn = btnmeas[meas];
+				break;
+			} else if ((n_btn_meas - meas) < (d->n_btn_con - 1)) {
+				/*
+				 * if left measurements are less than n_btn_con,
+				 * it's impossible to find button number
+				 */
+				break;
+			}
+		}
+	}
+
+	if (btn >= 0) {
+		if (mbhc->in_swch_irq_handler) {
+			pr_debug(
+			"%s: Switch irq triggered, ignore button press\n",
+			__func__);
+			goto done;
+		}
+		btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+		v_btn_high = wcd9xxx_mbhc_cal_btn_det_mp(btn_det,
+						       MBHC_BTN_DET_V_BTN_HIGH);
+		WARN_ON(btn >= btn_det->num_btn);
+		/* reprogram release threshold to catch voltage ramp up early */
+		wcd9xxx_update_rel_threshold(mbhc, v_btn_high[btn], vddio);
+
+		mask = wcd9xxx_get_button_mask(btn);
+		mbhc->buttons_pressed |= mask;
+		wcd9xxx_lock_sleep(core_res);
+		if (schedule_delayed_work(&mbhc->mbhc_btn_dwork,
+					  msecs_to_jiffies(400)) == 0) {
+			WARN(1, "Button pressed twice without release event\n");
+			wcd9xxx_unlock_sleep(core_res);
+		}
+	} else {
+		pr_debug("%s: bogus button press, too short press?\n",
+			 __func__);
+	}
+
+ done:
+	pr_debug("%s: leave\n", __func__);
+	mutex_unlock(&mbhc->mbhc_lock);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_release_handler(int irq, void *data)
+{
+	int ret;
+	bool waitdebounce = true;
+	struct wcd9xxx_mbhc *mbhc = data;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_LOCK(mbhc->resmgr);
+	mbhc->mbhc_state = MBHC_STATE_RELEASE;
+
+	if (mbhc->buttons_pressed & WCD9XXX_JACK_BUTTON_MASK) {
+		ret = wcd9xxx_cancel_btn_work(mbhc);
+		if (ret == 0) {
+			pr_debug("%s: Reporting long button release event\n",
+				 __func__);
+			wcd9xxx_jack_report(mbhc, &mbhc->button_jack, 0,
+					    mbhc->buttons_pressed);
+		} else {
+			if (wcd9xxx_is_false_press(mbhc)) {
+				pr_debug("%s: Fake button press interrupt\n",
+					 __func__);
+			} else {
+				if (mbhc->in_swch_irq_handler) {
+					pr_debug("%s: Switch irq kicked in, ignore\n",
+						 __func__);
+				} else {
+					pr_debug("%s: Reporting btn press\n",
+						 __func__);
+					wcd9xxx_jack_report(mbhc,
+							 &mbhc->button_jack,
+							 mbhc->buttons_pressed,
+							 mbhc->buttons_pressed);
+					pr_debug("%s: Reporting btn release\n",
+						 __func__);
+					wcd9xxx_jack_report(mbhc,
+						      &mbhc->button_jack,
+						      0, mbhc->buttons_pressed);
+					waitdebounce = false;
+				}
+			}
+		}
+
+		mbhc->buttons_pressed &= ~WCD9XXX_JACK_BUTTON_MASK;
+	}
+
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	if (waitdebounce)
+		msleep(SWCH_REL_DEBOUNCE_TIME_MS);
+	wcd9xxx_start_hs_polling(mbhc);
+
+	pr_debug("%s: leave\n", __func__);
+	WCD9XXX_BCL_UNLOCK(mbhc->resmgr);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphl_ocp_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec;
+
+	pr_info("%s: received HPHL OCP irq\n", __func__);
+
+	if (mbhc) {
+		codec = mbhc->codec;
+		if ((mbhc->hphlocp_cnt < OCP_ATTEMPT) &&
+		    (!mbhc->hphrocp_cnt)) {
+			pr_info("%s: retry\n", __func__);
+			mbhc->hphlocp_cnt++;
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+					    0x10, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+					    0x10, 0x10);
+		} else {
+			wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+					  mbhc->intr_ids->hph_left_ocp);
+			mbhc->hph_status |= SND_JACK_OC_HPHL;
+			wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+					    mbhc->hph_status,
+					    WCD9XXX_JACK_MASK);
+		}
+	} else {
+		pr_err("%s: Bad wcd9xxx private data\n", __func__);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t wcd9xxx_hphr_ocp_irq(int irq, void *data)
+{
+	struct wcd9xxx_mbhc *mbhc = data;
+	struct snd_soc_codec *codec;
+
+	pr_info("%s: received HPHR OCP irq\n", __func__);
+	codec = mbhc->codec;
+	if ((mbhc->hphrocp_cnt < OCP_ATTEMPT) &&
+	    (!mbhc->hphlocp_cnt)) {
+		pr_info("%s: retry\n", __func__);
+		mbhc->hphrocp_cnt++;
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+				    0x10);
+	} else {
+		wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+				    mbhc->intr_ids->hph_right_ocp);
+		mbhc->hph_status |= SND_JACK_OC_HPHR;
+		wcd9xxx_jack_report(mbhc, &mbhc->headset_jack,
+				    mbhc->hph_status, WCD9XXX_JACK_MASK);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int wcd9xxx_acdb_mclk_index(const int rate)
+{
+	if (rate == MCLK_RATE_12288KHZ)
+		return 0;
+	else if (rate == MCLK_RATE_9600KHZ)
+		return 1;
+	else {
+		BUG_ON(1);
+		return -EINVAL;
+	}
+}
+
+static void wcd9xxx_update_mbhc_clk_rate(struct wcd9xxx_mbhc *mbhc, u32 rate)
+{
+	u32 dce_wait, sta_wait;
+	u8 ncic, nmeas, navg;
+	void *calibration;
+	u8 *n_cic, *n_ready;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	u8 npoll = 4, nbounce_wait = 30;
+	struct snd_soc_codec *codec = mbhc->codec;
+	int idx = wcd9xxx_acdb_mclk_index(rate);
+	int idxmclk = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+	pr_debug("%s: Updating clock rate dependents, rate = %u\n", __func__,
+		 rate);
+	calibration = mbhc->mbhc_cfg->calibration;
+
+	/*
+	 * First compute the DCE / STA wait times depending on tunable
+	 * parameters. The value is computed in microseconds
+	 */
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration);
+	n_ready = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_READY);
+	n_cic = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_N_CIC);
+	nmeas = WCD9XXX_MBHC_CAL_BTN_DET_PTR(calibration)->n_meas;
+	navg = WCD9XXX_MBHC_CAL_GENERAL_PTR(calibration)->mbhc_navg;
+
+	/* ncic stays with the same what we had during calibration */
+	ncic = n_cic[idxmclk];
+	dce_wait = (1000 * 512 * ncic * (nmeas + 1)) / (rate / 1000);
+	sta_wait = (1000 * 128 * (navg + 1)) / (rate / 1000);
+	mbhc->mbhc_data.t_dce = dce_wait;
+	/* give extra margin to sta for safety */
+	mbhc->mbhc_data.t_sta = sta_wait + 250;
+	mbhc->mbhc_data.t_sta_dce = ((1000 * 256) / (rate / 1000) *
+				     n_ready[idx]) + 10;
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B1_CTL, n_ready[idx]);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B6_CTL, ncic);
+
+	if (rate == MCLK_RATE_12288KHZ) {
+		npoll = 4;
+		nbounce_wait = 30;
+	} else if (rate == MCLK_RATE_9600KHZ) {
+		npoll = 3;
+		nbounce_wait = 23;
+	}
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B2_CTL, npoll);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B3_CTL, nbounce_wait);
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_cal(struct wcd9xxx_mbhc *mbhc)
+{
+	u8 cfilt_mode;
+	u16 reg0, reg1, reg2;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	wcd9xxx_disable_irq(mbhc->resmgr->core_res,
+			    mbhc->intr_ids->dce_est_complete);
+	wcd9xxx_turn_onoff_rel_detection(codec, false);
+
+	/* t_dce and t_sta are updated by wcd9xxx_update_mbhc_clk_rate() */
+	WARN_ON(!mbhc->mbhc_data.t_dce);
+	WARN_ON(!mbhc->mbhc_data.t_sta);
+
+	/*
+	 * LDOH and CFILT are already configured during pdata handling.
+	 * Only need to make sure CFILT and bandgap are in Fast mode.
+	 * Need to restore defaults once calculation is done.
+	 *
+	 * In case when Micbias is powered by external source, request
+	 * turn on the external voltage source for Calibration.
+	 */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+		mbhc->mbhc_cb->enable_mb_source(codec, true, false);
+
+	cfilt_mode = snd_soc_read(codec, mbhc->mbhc_bias_regs.cfilt_ctl);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+		mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+	else
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+				    0x40, 0x00);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->micbias_pulldown_ctrl)
+		mbhc->mbhc_cb->micbias_pulldown_ctrl(mbhc, false);
+
+	/*
+	 * Micbias, CFILT, LDOH, MBHC MUX mode settings
+	 * to perform ADC calibration
+	 */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->select_cfilt)
+		mbhc->mbhc_cb->select_cfilt(codec, mbhc);
+	else
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x60,
+			    mbhc->mbhc_cfg->micbias << 5);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x00);
+	snd_soc_update_bits(codec, WCD9XXX_A_LDO_H_MODE_1, 0x60, 0x60);
+	snd_soc_write(codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL, 0x78);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->codec_specific_cal)
+		mbhc->mbhc_cb->codec_specific_cal(codec, mbhc);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL,
+				    0x04, 0x04);
+
+	/* Pull down micbias to ground */
+	reg0 = snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg);
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 1, 1);
+	/* Disconnect override from micbias */
+	reg1 = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
+	snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL, 1 << 4, 1 << 0);
+	/* Connect the MUX to micbias */
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+		mbhc->mbhc_cb->enable_mux_bias_block(codec);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0x80, 0x80);
+	/*
+	 * Hardware that has external cap can delay mic bias ramping down up
+	 * to 50ms.
+	 */
+	msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
+	/* DCE measurement for 0 voltage */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	mbhc->mbhc_data.dce_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true, false);
+
+	/* compute dce_z for current source */
+	reg2 = snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+			    WCD9XXX_MBHC_NSC_CS << 3);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	mbhc->mbhc_data.dce_nsc_cs_z = __wcd9xxx_codec_sta_dce(mbhc, 1, true,
+							       false);
+	pr_debug("%s: dce_z with nsc cs: 0x%x\n", __func__,
+						 mbhc->mbhc_data.dce_nsc_cs_z);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, reg2);
+
+	/* STA measurement for 0 voltage */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	mbhc->mbhc_data.sta_z = __wcd9xxx_codec_sta_dce(mbhc, 0, true, false);
+
+	/* Restore registers */
+	snd_soc_write(codec, mbhc->mbhc_bias_regs.ctl_reg, reg0);
+	snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, reg1);
+
+	/* DCE measurment for MB voltage */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+		mbhc->mbhc_cb->enable_mux_bias_block(codec);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0x80, 0x80);
+	/*
+	 * Hardware that has external cap can delay mic bias ramping down up
+	 * to 50ms.
+	 */
+	msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x04);
+	usleep_range(mbhc->mbhc_data.t_dce, mbhc->mbhc_data.t_dce +
+					WCD9XXX_USLEEP_RANGE_MARGIN_US);
+	mbhc->mbhc_data.dce_mb = wcd9xxx_read_dce_result(codec);
+
+	/* STA Measurement for MB Voltage */
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x0A);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_CLK_CTL, 0x02);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x02);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+		mbhc->mbhc_cb->enable_mux_bias_block(codec);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0x80, 0x80);
+	/*
+	 * Hardware that has external cap can delay mic bias ramping down up
+	 * to 50ms.
+	 */
+	msleep(WCD9XXX_MUX_SWITCH_READY_WAIT_MS);
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x02);
+	usleep_range(mbhc->mbhc_data.t_sta, mbhc->mbhc_data.t_sta +
+					WCD9XXX_USLEEP_RANGE_MARGIN_US);
+	mbhc->mbhc_data.sta_mb = wcd9xxx_read_sta_result(codec);
+
+	/* Restore default settings. */
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x04, 0x00);
+	snd_soc_write(codec, mbhc->mbhc_bias_regs.cfilt_ctl, cfilt_mode);
+	snd_soc_write(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0x04);
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mux_bias_block)
+		mbhc->mbhc_cb->enable_mux_bias_block(codec);
+	else
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0x80, 0x80);
+	usleep_range(100, 110);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mb_source)
+		mbhc->mbhc_cb->enable_mb_source(codec, false, false);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->micbias_pulldown_ctrl)
+		mbhc->mbhc_cb->micbias_pulldown_ctrl(mbhc, true);
+
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+			   mbhc->intr_ids->dce_est_complete);
+	wcd9xxx_turn_onoff_rel_detection(codec, true);
+
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_mbhc_setup(struct wcd9xxx_mbhc *mbhc)
+{
+	int n;
+	u8 *gain;
+	struct wcd9xxx_mbhc_general_cfg *generic;
+	struct wcd9xxx_mbhc_btn_detect_cfg *btn_det;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const int idx = wcd9xxx_acdb_mclk_index(mbhc->mbhc_cfg->mclk_rate);
+
+	pr_debug("%s: enter\n", __func__);
+	generic = WCD9XXX_MBHC_CAL_GENERAL_PTR(mbhc->mbhc_cfg->calibration);
+	btn_det = WCD9XXX_MBHC_CAL_BTN_DET_PTR(mbhc->mbhc_cfg->calibration);
+
+	for (n = 0; n < 8; n++) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_FIR_B1_CFG,
+				    0x07, n);
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_FIR_B2_CFG,
+			      btn_det->c[n]);
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x07,
+			    btn_det->nc);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x70,
+			    generic->mbhc_nsa << 4);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_TIMER_B4_CTL, 0x0F,
+			    btn_det->n_meas);
+
+	snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_TIMER_B5_CTL,
+		      generic->mbhc_navg);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x80, 0x80);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x78,
+			    btn_det->mbhc_nsc << 3);
+
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->get_cdc_type &&
+			mbhc->mbhc_cb->get_cdc_type() !=
+					WCD9XXX_CDC_TYPE_HELICON) {
+		if (mbhc->resmgr->reg_addr->micb_4_mbhc)
+			snd_soc_update_bits(codec,
+					mbhc->resmgr->reg_addr->micb_4_mbhc,
+					0x03, MBHC_MICBIAS2);
+	}
+
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B1_CTL, 0x02, 0x02);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xF0, 0xF0);
+
+	gain = wcd9xxx_mbhc_cal_btn_det_mp(btn_det, MBHC_BTN_DET_GAIN);
+	snd_soc_update_bits(codec, WCD9XXX_A_CDC_MBHC_B2_CTL, 0x78,
+			    gain[idx] << 3);
+	snd_soc_update_bits(codec, WCD9XXX_A_MICB_2_MBHC, 0x04, 0x04);
+
+	pr_debug("%s: leave\n", __func__);
+}
+
+static int wcd9xxx_setup_jack_detect_irq(struct wcd9xxx_mbhc *mbhc)
+{
+	int ret = 0;
+	void *core_res = mbhc->resmgr->core_res;
+
+	if (mbhc->mbhc_cfg->gpio) {
+		ret = request_threaded_irq(mbhc->mbhc_cfg->gpio_irq, NULL,
+					   wcd9xxx_mech_plug_detect_irq,
+					   (IRQF_TRIGGER_RISING |
+					    IRQF_TRIGGER_FALLING),
+					   "headset detect", mbhc);
+		if (ret) {
+			pr_err("%s: Failed to request gpio irq %d\n", __func__,
+			       mbhc->mbhc_cfg->gpio_irq);
+		} else {
+			ret = enable_irq_wake(mbhc->mbhc_cfg->gpio_irq);
+			if (ret)
+				pr_err("%s: Failed to enable wake up irq %d\n",
+				       __func__, mbhc->mbhc_cfg->gpio_irq);
+		}
+	} else if (mbhc->mbhc_cfg->insert_detect) {
+		/* Enable HPHL_10K_SW */
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_RX_HPH_OCP_CTL,
+				    1 << 1, 1 << 1);
+
+		ret = wcd9xxx_request_irq(core_res,
+					  mbhc->intr_ids->hs_jack_switch,
+					  wcd9xxx_mech_plug_detect_irq,
+					  "Jack Detect",
+					  mbhc);
+		if (ret)
+			pr_err("%s: Failed to request insert detect irq %d\n",
+				__func__, mbhc->intr_ids->hs_jack_switch);
+	}
+
+	return ret;
+}
+
+static int wcd9xxx_init_and_calibrate(struct wcd9xxx_mbhc *mbhc)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	/* Enable MCLK during calibration */
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+	wcd9xxx_mbhc_setup(mbhc);
+	wcd9xxx_mbhc_cal(mbhc);
+	wcd9xxx_mbhc_calc_thres(mbhc);
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+	wcd9xxx_calibrate_hs_polling(mbhc);
+
+	/* Enable Mic Bias pull down and HPH Switch to GND */
+	snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg, 0x01, 0x01);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_HPH, 0x01, 0x01);
+	INIT_WORK(&mbhc->correct_plug_swch, wcd9xxx_correct_swch_plug);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_RX_HPH_OCP_CTL, 0x10,
+			    0x10);
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+			   mbhc->intr_ids->hph_left_ocp);
+	wcd9xxx_enable_irq(mbhc->resmgr->core_res,
+			   mbhc->intr_ids->hph_right_ocp);
+
+	/* Initialize mechanical mbhc */
+	ret = wcd9xxx_setup_jack_detect_irq(mbhc);
+
+	if (!ret && mbhc->mbhc_cfg->gpio) {
+		/* Requested with IRQF_DISABLED */
+		enable_irq(mbhc->mbhc_cfg->gpio_irq);
+
+		/* Bootup time detection */
+		wcd9xxx_swch_irq_handler(mbhc);
+	} else if (!ret && mbhc->mbhc_cfg->insert_detect) {
+		pr_debug("%s: Setting up codec own insert detection\n",
+			 __func__);
+		/* Setup for insertion detection */
+		wcd9xxx_insert_detect_setup(mbhc, true);
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return ret;
+}
+
+static void wcd9xxx_mbhc_fw_read(struct work_struct *work)
+{
+	struct delayed_work *dwork;
+	struct wcd9xxx_mbhc *mbhc;
+	struct snd_soc_codec *codec;
+	const struct firmware *fw;
+	struct firmware_cal *fw_data = NULL;
+	int ret = -1, retry = 0;
+	bool use_default_cal = false;
+
+	dwork = to_delayed_work(work);
+	mbhc = container_of(dwork, struct wcd9xxx_mbhc, mbhc_firmware_dwork);
+	codec = mbhc->codec;
+
+	while (retry < FW_READ_ATTEMPTS) {
+		retry++;
+		pr_info("%s:Attempt %d to request MBHC firmware\n",
+				__func__, retry);
+		if (mbhc->mbhc_cb->get_hwdep_fw_cal)
+			fw_data = mbhc->mbhc_cb->get_hwdep_fw_cal(codec,
+					WCD9XXX_MBHC_CAL);
+		if (!fw_data)
+			ret = request_firmware(&fw, "wcd9320/wcd9320_mbhc.bin",
+					codec->dev);
+		/*
+		 * if request_firmware and hwdep cal both fail then
+		 * retry for few times before bailing out
+		 */
+		if ((ret != 0) && !fw_data) {
+			usleep_range(FW_READ_TIMEOUT, FW_READ_TIMEOUT +
+					WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		} else {
+			pr_info("%s: MBHC Firmware read successful\n",
+					__func__);
+			break;
+		}
+	}
+	if (!fw_data)
+		pr_info("%s: using request_firmware\n", __func__);
+	else
+		pr_info("%s: using hwdep cal\n", __func__);
+	if (ret != 0 && !fw_data) {
+		pr_err("%s: Cannot load MBHC firmware use default cal\n",
+				__func__);
+		use_default_cal = true;
+	}
+	if (!use_default_cal) {
+		const void *data;
+		size_t size;
+
+		if (fw_data) {
+			data = fw_data->data;
+			size = fw_data->size;
+		} else {
+			data = fw->data;
+			size = fw->size;
+		}
+		if (wcd9xxx_mbhc_fw_validate(data, size) == false) {
+			pr_err("%s: Invalid MBHC cal data size use default cal\n",
+			       __func__);
+			if (!fw_data)
+				release_firmware(fw);
+		} else {
+			if (fw_data) {
+				mbhc->mbhc_cfg->calibration =
+						(void *)fw_data->data;
+				mbhc->mbhc_cal = fw_data;
+			} else {
+				mbhc->mbhc_cfg->calibration =
+						(void *)fw->data;
+				mbhc->mbhc_fw = fw;
+			}
+		}
+	}
+
+	(void) wcd9xxx_init_and_calibrate(mbhc);
+}
+
+#ifdef CONFIG_DEBUG_FS
+ssize_t codec_mbhc_debug_read(struct file *file, char __user *buf,
+			      size_t count, loff_t *pos)
+{
+	const int size = 768;
+	char buffer[size];
+	int n = 0;
+	struct wcd9xxx_mbhc *mbhc = file->private_data;
+	const struct mbhc_internal_cal_data *p = &mbhc->mbhc_data;
+	const s16 v_ins_hu =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_HU);
+	const s16 v_ins_h =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_INS_H);
+	const s16 v_b1_hu =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_HU);
+	const s16 v_b1_h =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_B1_H);
+	const s16 v_br_h =
+	    wcd9xxx_get_current_v(mbhc, WCD9XXX_CURRENT_V_BR_H);
+
+	n = scnprintf(buffer, size - n, "dce_z = %x(%dmv)\n",
+		      p->dce_z, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_z));
+	n += scnprintf(buffer + n, size - n, "dce_mb = %x(%dmv)\n",
+		       p->dce_mb, wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_mb));
+	n += scnprintf(buffer + n, size - n, "dce_nsc_cs_z = %x(%dmv)\n",
+		      p->dce_nsc_cs_z,
+		      __wcd9xxx_codec_sta_dce_v(mbhc, 1, p->dce_nsc_cs_z,
+						p->dce_nsc_cs_z,
+						VDDIO_MICBIAS_MV));
+	n += scnprintf(buffer + n, size - n, "sta_z = %x(%dmv)\n",
+		       p->sta_z, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_z));
+	n += scnprintf(buffer + n, size - n, "sta_mb = %x(%dmv)\n",
+		       p->sta_mb, wcd9xxx_codec_sta_dce_v(mbhc, 0, p->sta_mb));
+	n += scnprintf(buffer + n, size - n, "t_dce = %d\n",  p->t_dce);
+	n += scnprintf(buffer + n, size - n, "t_sta = %d\n",  p->t_sta);
+	n += scnprintf(buffer + n, size - n, "micb_mv = %dmv\n", p->micb_mv);
+	n += scnprintf(buffer + n, size - n, "v_ins_hu = %x(%dmv)\n",
+		       v_ins_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_ins_hu));
+	n += scnprintf(buffer + n, size - n, "v_ins_h = %x(%dmv)\n",
+		       v_ins_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_ins_h));
+	n += scnprintf(buffer + n, size - n, "v_b1_hu = %x(%dmv)\n",
+		       v_b1_hu, wcd9xxx_codec_sta_dce_v(mbhc, 0, v_b1_hu));
+	n += scnprintf(buffer + n, size - n, "v_b1_h = %x(%dmv)\n",
+		       v_b1_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_b1_h));
+	n += scnprintf(buffer + n, size - n, "v_brh = %x(%dmv)\n",
+		       v_br_h, wcd9xxx_codec_sta_dce_v(mbhc, 1, v_br_h));
+	n += scnprintf(buffer + n, size - n, "v_brl = %x(%dmv)\n",  p->v_brl,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_brl));
+	n += scnprintf(buffer + n, size - n, "v_no_mic = %x(%dmv)\n",
+		       p->v_no_mic,
+		       wcd9xxx_codec_sta_dce_v(mbhc, 0, p->v_no_mic));
+	n += scnprintf(buffer + n, size - n, "v_inval_ins_low = %d\n",
+		       p->v_inval_ins_low);
+	n += scnprintf(buffer + n, size - n, "v_inval_ins_high = %d\n",
+		       p->v_inval_ins_high);
+	n += scnprintf(buffer + n, size - n, "Insert detect insert = %d\n",
+		       !wcd9xxx_swch_level_remove(mbhc));
+	buffer[n] = 0;
+
+	return simple_read_from_buffer(buf, count, pos, buffer, n);
+}
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+				 const char __user *ubuf, size_t cnt,
+				 loff_t *ppos)
+{
+	char lbuf[32];
+	char *buf;
+	int rc;
+	struct wcd9xxx_mbhc *mbhc = filp->private_data;
+
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+	buf = (char *)lbuf;
+	mbhc->no_mic_headset_override = (*strsep(&buf, " ") == '0') ?
+					     false : true;
+	return rc;
+}
+
+static const struct file_operations mbhc_trrs_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+};
+
+static const struct file_operations mbhc_debug_ops = {
+	.open = codec_debug_open,
+	.read = codec_mbhc_debug_read,
+};
+
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+	mbhc->debugfs_poke =
+	    debugfs_create_file("TRRS", S_IFREG | 0444, NULL, mbhc,
+				&mbhc_trrs_debug_ops);
+	mbhc->debugfs_mbhc =
+	    debugfs_create_file("wcd9xxx_mbhc", S_IFREG | 0444,
+				NULL, mbhc, &mbhc_debug_ops);
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+	debugfs_remove(mbhc->debugfs_poke);
+	debugfs_remove(mbhc->debugfs_mbhc);
+}
+#else
+static void wcd9xxx_init_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+
+static void wcd9xxx_cleanup_debugfs(struct wcd9xxx_mbhc *mbhc)
+{
+}
+#endif
+
+int wcd9xxx_mbhc_set_keycode(struct wcd9xxx_mbhc *mbhc)
+{
+	enum snd_jack_types type = SND_JACK_BTN_0;
+	int i, ret, result = 0;
+	int *btn_key_code;
+
+	btn_key_code = mbhc->mbhc_cfg->key_code;
+
+	for (i = 0 ; i < 8 ; i++) {
+		if (btn_key_code[i] != 0) {
+			switch (i) {
+			case 0:
+				type = SND_JACK_BTN_0;
+				break;
+			case 1:
+				type = SND_JACK_BTN_1;
+				break;
+			case 2:
+				type = SND_JACK_BTN_2;
+				break;
+			case 3:
+				type = SND_JACK_BTN_3;
+				break;
+			case 4:
+				type = SND_JACK_BTN_4;
+				break;
+			case 5:
+				type = SND_JACK_BTN_5;
+				break;
+			default:
+				WARN_ONCE(1, "Wrong button number:%d\n", i);
+				result = -1;
+				break;
+			}
+			ret = snd_jack_set_key(mbhc->button_jack.jack,
+					       type,
+					       btn_key_code[i]);
+			if (ret) {
+				pr_err("%s: Failed to set code for %d\n",
+					__func__, btn_key_code[i]);
+				result = -1;
+			}
+			input_set_capability(
+				mbhc->button_jack.jack->input_dev,
+				EV_KEY, btn_key_code[i]);
+			pr_debug("%s: set btn%d key code:%d\n", __func__,
+				i, btn_key_code[i]);
+		}
+	}
+	return result;
+}
+
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_config *mbhc_cfg)
+{
+	int rc = 0;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (!codec) {
+		pr_err("%s: no codec\n", __func__);
+		return -EINVAL;
+	}
+
+	if (mbhc_cfg->mclk_rate != MCLK_RATE_12288KHZ &&
+	    mbhc_cfg->mclk_rate != MCLK_RATE_9600KHZ) {
+		pr_err("Error: unsupported clock rate %d\n",
+		       mbhc_cfg->mclk_rate);
+		return -EINVAL;
+	}
+
+	/* Save mbhc config */
+	mbhc->mbhc_cfg = mbhc_cfg;
+
+	/* Set btn key code */
+	if (wcd9xxx_mbhc_set_keycode(mbhc))
+		pr_err("Set btn key code error!!!\n");
+
+	/* Get HW specific mbhc registers' address */
+	wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_PRIMARY_MIC_MB);
+
+	/* Get HW specific mbhc registers' address for anc */
+	wcd9xxx_get_mbhc_micbias_regs(mbhc, MBHC_ANC_MIC_MB);
+
+	/* Put CFILT in fast mode by default */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->cfilt_fast_mode)
+		mbhc->mbhc_cb->cfilt_fast_mode(codec, mbhc);
+	else
+		snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.cfilt_ctl,
+		0x40, WCD9XXX_CFILT_FAST_MODE);
+
+	/*
+	 * setup internal micbias if codec uses internal micbias for
+	 * headset detection
+	 */
+	if (mbhc->mbhc_cfg->use_int_rbias) {
+		if (mbhc->mbhc_cb && mbhc->mbhc_cb->setup_int_rbias) {
+			mbhc->mbhc_cb->setup_int_rbias(codec, true);
+		} else {
+			pr_info("%s: internal bias requested but codec did not provide callback\n",
+				__func__);
+		}
+	}
+
+	/*
+	 * If codec has specific clock gating for MBHC,
+	 * remove the clock gate
+	 */
+	if (mbhc->mbhc_cb &&
+			mbhc->mbhc_cb->enable_clock_gate)
+		mbhc->mbhc_cb->enable_clock_gate(mbhc->codec, true);
+
+	if (!mbhc->mbhc_cfg->read_fw_bin ||
+	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_fw) ||
+	    (mbhc->mbhc_cfg->read_fw_bin && mbhc->mbhc_cal)) {
+		rc = wcd9xxx_init_and_calibrate(mbhc);
+	} else {
+		if (!mbhc->mbhc_fw || !mbhc->mbhc_cal)
+			schedule_delayed_work(&mbhc->mbhc_firmware_dwork,
+					     usecs_to_jiffies(FW_READ_TIMEOUT));
+		else
+			pr_debug("%s: Skipping to read mbhc fw, 0x%pK %pK\n",
+				 __func__, mbhc->mbhc_fw, mbhc->mbhc_cal);
+	}
+
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_start);
+
+void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc)
+{
+	if (mbhc->mbhc_fw || mbhc->mbhc_cal) {
+		cancel_delayed_work_sync(&mbhc->mbhc_firmware_dwork);
+		if (!mbhc->mbhc_cal)
+			release_firmware(mbhc->mbhc_fw);
+		mbhc->mbhc_fw = NULL;
+		mbhc->mbhc_cal = NULL;
+	}
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_stop);
+
+static enum wcd9xxx_micbias_num
+wcd9xxx_event_to_micbias(const enum wcd9xxx_notify_event event)
+{
+	enum wcd9xxx_micbias_num ret;
+
+	switch (event) {
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+		ret = MBHC_MICBIAS1;
+		break;
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+		ret = MBHC_MICBIAS2;
+		break;
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+		ret = MBHC_MICBIAS3;
+		break;
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+		ret = MBHC_MICBIAS4;
+		break;
+	default:
+		WARN_ONCE(1, "Cannot convert event %d to micbias\n", event);
+		ret = MBHC_MICBIAS_INVALID;
+		break;
+	}
+	return ret;
+}
+
+static int wcd9xxx_event_to_cfilt(const enum wcd9xxx_notify_event event)
+{
+	int ret;
+
+	switch (event) {
+	case WCD9XXX_EVENT_PRE_CFILT_1_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+	case WCD9XXX_EVENT_POST_CFILT_1_ON:
+		ret = WCD9XXX_CFILT1_SEL;
+		break;
+	case WCD9XXX_EVENT_PRE_CFILT_2_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+	case WCD9XXX_EVENT_POST_CFILT_2_ON:
+		ret = WCD9XXX_CFILT2_SEL;
+		break;
+	case WCD9XXX_EVENT_PRE_CFILT_3_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+	case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+	case WCD9XXX_EVENT_POST_CFILT_3_ON:
+		ret = WCD9XXX_CFILT3_SEL;
+		break;
+	default:
+		ret = -1;
+	}
+	return ret;
+}
+
+static int wcd9xxx_get_mbhc_cfilt_sel(struct wcd9xxx_mbhc *mbhc)
+{
+	int cfilt;
+	const struct wcd9xxx_micbias_setting *mb_pdata =
+		mbhc->resmgr->micbias_pdata;
+
+	switch (mbhc->mbhc_cfg->micbias) {
+	case MBHC_MICBIAS1:
+		cfilt = mb_pdata->bias1_cfilt_sel;
+		break;
+	case MBHC_MICBIAS2:
+		cfilt = mb_pdata->bias2_cfilt_sel;
+		break;
+	case MBHC_MICBIAS3:
+		cfilt = mb_pdata->bias3_cfilt_sel;
+		break;
+	case MBHC_MICBIAS4:
+		cfilt = mb_pdata->bias4_cfilt_sel;
+		break;
+	default:
+		cfilt = MBHC_MICBIAS_INVALID;
+		break;
+	}
+	return cfilt;
+}
+
+static void wcd9xxx_enable_mbhc_txfe(struct wcd9xxx_mbhc *mbhc, bool on)
+{
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->enable_mbhc_txfe)
+		mbhc->mbhc_cb->enable_mbhc_txfe(mbhc->codec, on);
+	else
+		snd_soc_update_bits(mbhc->codec, WCD9XXX_A_TX_7_MBHC_TEST_CTL,
+				    0x40, on ? 0x40 : 0x00);
+}
+
+static int wcd9xxx_event_notify(struct notifier_block *self, unsigned long val,
+				void *data)
+{
+	int ret = 0;
+	struct wcd9xxx_mbhc *mbhc = ((struct wcd9xxx_resmgr *)data)->mbhc;
+	struct snd_soc_codec *codec;
+	enum wcd9xxx_notify_event event = (enum wcd9xxx_notify_event)val;
+
+	pr_debug("%s: enter event %s(%d)\n", __func__,
+		 wcd9xxx_get_event_string(event), event);
+
+	if (!mbhc || !mbhc->mbhc_cfg) {
+		pr_debug("mbhc not initialized\n");
+		return 0;
+	}
+	codec = mbhc->codec;
+	mutex_lock(&mbhc->mbhc_lock);
+	switch (event) {
+	/* MICBIAS usage change */
+	case WCD9XXX_EVENT_PRE_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_PRE_MICBIAS_4_ON:
+		if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event)) {
+			wcd9xxx_switch_micbias(mbhc, 0);
+			/*
+			 * Enable MBHC TxFE whenever  micbias is
+			 * turned ON and polling is active
+			 */
+			if (mbhc->polling_active)
+				wcd9xxx_enable_mbhc_txfe(mbhc, true);
+		}
+		break;
+	case WCD9XXX_EVENT_POST_MICBIAS_1_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_ON:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_ON:
+		if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event) &&
+		    wcd9xxx_mbhc_polling(mbhc)) {
+			/* if polling is on, restart it */
+			wcd9xxx_pause_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_POST_MICBIAS_1_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_2_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_3_OFF:
+	case WCD9XXX_EVENT_POST_MICBIAS_4_OFF:
+		if (mbhc->mbhc_cfg && mbhc->mbhc_cfg->micbias ==
+		    wcd9xxx_event_to_micbias(event)) {
+			if (mbhc->event_state &
+			   (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR))
+				wcd9xxx_switch_micbias(mbhc, 1);
+			/*
+			 * Disable MBHC TxFE, in case it was enabled earlier
+			 * when micbias was enabled and polling is not active.
+			 */
+			if (!mbhc->polling_active)
+				wcd9xxx_enable_mbhc_txfe(mbhc, false);
+		}
+		if (mbhc->micbias_enable && mbhc->polling_active &&
+		    !(snd_soc_read(mbhc->codec, mbhc->mbhc_bias_regs.ctl_reg)
+		    & 0x80)) {
+			pr_debug("%s:Micbias turned off by recording, set up again",
+				 __func__);
+			snd_soc_update_bits(codec, mbhc->mbhc_bias_regs.ctl_reg,
+					    0x80, 0x80);
+		}
+		break;
+	/* PA usage change */
+	case WCD9XXX_EVENT_PRE_HPHL_PA_ON:
+		set_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+		if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg) & 0x80))
+			/* if micbias is not enabled, switch to vddio */
+			wcd9xxx_switch_micbias(mbhc, 1);
+		break;
+	case WCD9XXX_EVENT_PRE_HPHR_PA_ON:
+		set_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+		break;
+	case WCD9XXX_EVENT_POST_HPHL_PA_OFF:
+		clear_bit(MBHC_EVENT_PA_HPHL, &mbhc->event_state);
+		/* if HPH PAs are off, report OCP and switch back to CFILT */
+		clear_bit(WCD9XXX_HPHL_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		clear_bit(WCD9XXX_HPHL_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHL)
+			hphlocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		if (!(mbhc->event_state &
+		      (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
+		       1 << MBHC_EVENT_PRE_TX_3_ON)))
+			wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	case WCD9XXX_EVENT_POST_HPHR_PA_OFF:
+		clear_bit(MBHC_EVENT_PA_HPHR, &mbhc->event_state);
+		/* if HPH PAs are off, report OCP and switch back to CFILT */
+		clear_bit(WCD9XXX_HPHR_PA_OFF_ACK, &mbhc->hph_pa_dac_state);
+		clear_bit(WCD9XXX_HPHR_DAC_OFF_ACK, &mbhc->hph_pa_dac_state);
+		if (mbhc->hph_status & SND_JACK_OC_HPHR)
+			hphrocp_off_report(mbhc, SND_JACK_OC_HPHL);
+		if (!(mbhc->event_state &
+		      (1 << MBHC_EVENT_PA_HPHL | 1 << MBHC_EVENT_PA_HPHR |
+		       1 << MBHC_EVENT_PRE_TX_3_ON)))
+			wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	/* Clock usage change */
+	case WCD9XXX_EVENT_PRE_MCLK_ON:
+		break;
+	case WCD9XXX_EVENT_POST_MCLK_ON:
+		/* Change to lower TxAAF frequency */
+		snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+				    1 << 4);
+		/* Re-calibrate clock rate dependent values */
+		wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->mbhc_cfg->mclk_rate);
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc)) {
+			wcd9xxx_calibrate_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_PRE_MCLK_OFF:
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		break;
+	case WCD9XXX_EVENT_POST_MCLK_OFF:
+		break;
+	case WCD9XXX_EVENT_PRE_RCO_ON:
+		break;
+	case WCD9XXX_EVENT_POST_RCO_ON:
+		/* Change to higher TxAAF frequency */
+		snd_soc_update_bits(codec, WCD9XXX_A_TX_COM_BIAS, 1 << 4,
+				    0 << 4);
+		/* Re-calibrate clock rate dependent values */
+		wcd9xxx_update_mbhc_clk_rate(mbhc, mbhc->rco_clk_rate);
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc)) {
+			wcd9xxx_calibrate_hs_polling(mbhc);
+			wcd9xxx_start_hs_polling(mbhc);
+		}
+		break;
+	case WCD9XXX_EVENT_PRE_RCO_OFF:
+		/* If clock source changes, stop and restart polling */
+		if (wcd9xxx_mbhc_polling(mbhc))
+			wcd9xxx_pause_hs_polling(mbhc);
+		break;
+	case WCD9XXX_EVENT_POST_RCO_OFF:
+		break;
+	/* CFILT usage change */
+	case WCD9XXX_EVENT_PRE_CFILT_1_ON:
+	case WCD9XXX_EVENT_PRE_CFILT_2_ON:
+	case WCD9XXX_EVENT_PRE_CFILT_3_ON:
+		if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+		    wcd9xxx_event_to_cfilt(event))
+			/*
+			 * Switch CFILT to slow mode if MBHC CFILT is being
+			 * used.
+			 */
+			wcd9xxx_codec_switch_cfilt_mode(mbhc, false);
+		break;
+	case WCD9XXX_EVENT_POST_CFILT_1_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_2_OFF:
+	case WCD9XXX_EVENT_POST_CFILT_3_OFF:
+		if (wcd9xxx_get_mbhc_cfilt_sel(mbhc) ==
+		    wcd9xxx_event_to_cfilt(event))
+			/*
+			 * Switch CFILT to fast mode if MBHC CFILT is not
+			 * used anymore.
+			 */
+			wcd9xxx_codec_switch_cfilt_mode(mbhc, true);
+		break;
+	/* System resume */
+	case WCD9XXX_EVENT_POST_RESUME:
+		mbhc->mbhc_last_resume = jiffies;
+		break;
+	/* BG mode chage */
+	case WCD9XXX_EVENT_PRE_BG_OFF:
+	case WCD9XXX_EVENT_POST_BG_OFF:
+	case WCD9XXX_EVENT_PRE_BG_AUDIO_ON:
+	case WCD9XXX_EVENT_POST_BG_AUDIO_ON:
+	case WCD9XXX_EVENT_PRE_BG_MBHC_ON:
+	case WCD9XXX_EVENT_POST_BG_MBHC_ON:
+		/* Not used for now */
+		break;
+	case WCD9XXX_EVENT_PRE_TX_3_ON:
+		/*
+		 * if polling is ON, mbhc micbias not enabled
+		 *  switch micbias source to VDDIO
+		 */
+		set_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
+		if (!(snd_soc_read(codec, mbhc->mbhc_bias_regs.ctl_reg)
+		      & 0x80) &&
+		    mbhc->polling_active && !mbhc->mbhc_micbias_switched)
+			wcd9xxx_switch_micbias(mbhc, 1);
+		break;
+	case WCD9XXX_EVENT_POST_TX_3_OFF:
+		/*
+		 * Switch back to micbias if HPH PA or TX3 path
+		 * is disabled
+		 */
+		clear_bit(MBHC_EVENT_PRE_TX_3_ON, &mbhc->event_state);
+		if (mbhc->polling_active && mbhc->mbhc_micbias_switched &&
+		    !(mbhc->event_state & (1 << MBHC_EVENT_PA_HPHL |
+		      1 << MBHC_EVENT_PA_HPHR)))
+			wcd9xxx_switch_micbias(mbhc, 0);
+		break;
+	default:
+		WARN(1, "Unknown event %d\n", event);
+		ret = -EINVAL;
+	}
+	mutex_unlock(&mbhc->mbhc_lock);
+
+	pr_debug("%s: leave\n", __func__);
+
+	return ret;
+}
+
+static s16 wcd9xxx_read_impedance_regs(struct wcd9xxx_mbhc *mbhc)
+{
+	struct snd_soc_codec *codec = mbhc->codec;
+	short bias_value;
+	int i;
+	s32 z_t = 0;
+	s32 z_loop = z_det_box_car_avg;
+
+	/* Box Car avrg of less than a particular loop count will not be
+	 * accomodated. Similarly if the count is more than a particular number
+	 * it will not be counted. Set z_loop counter to a limit, if its more
+	 * or less than the value in WCD9XXX_BOX_CAR_AVRG_MAX or
+	 * WCD9XXX_BOX_CAR_AVRG_MIN
+	 */
+	if (z_loop < WCD9XXX_BOX_CAR_AVRG_MIN) {
+		dev_dbg(codec->dev,
+			"%s: Box Car avrg counter < %d. Limiting it to %d\n",
+			__func__, WCD9XXX_BOX_CAR_AVRG_MIN,
+			WCD9XXX_BOX_CAR_AVRG_MIN);
+		z_loop = WCD9XXX_BOX_CAR_AVRG_MIN;
+	} else if (z_loop > WCD9XXX_BOX_CAR_AVRG_MAX) {
+		dev_dbg(codec->dev,
+			"%s: Box Car avrg counter > %d. Limiting it to %d\n",
+			__func__, WCD9XXX_BOX_CAR_AVRG_MAX,
+			WCD9XXX_BOX_CAR_AVRG_MAX);
+		z_loop = WCD9XXX_BOX_CAR_AVRG_MAX;
+	}
+
+	/* Take box car average if needed */
+	for (i = 0; i < z_loop; i++) {
+		snd_soc_write(codec, WCD9XXX_A_CDC_MBHC_EN_CTL, 0x2);
+		/* Wait for atleast 1800uS to let register write to settle */
+		usleep_range(1800, 1800 + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+		z_t += wcd9xxx_read_sta_result(codec);
+	}
+	/* Take average of the Z values read */
+	bias_value = (s16) (z_t / z_loop);
+	return bias_value;
+}
+
+static int wcd9xxx_remeasure_z_values(struct wcd9xxx_mbhc *mbhc,
+					s16 l[3], s16 r[3],
+					uint32_t *zl, uint32_t *zr,
+					u32 *zl_stereo, u32 *zl_mono)
+{
+	s16 l_t[3] = {0}, r_t[3] = {0};
+	s16 l2_stereo, l2_mono;
+	bool left, right;
+	struct snd_soc_codec *codec = mbhc->codec;
+
+	if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
+		!mbhc->mbhc_cb->compute_impedance) {
+		dev_err(codec->dev, "%s: Invalid parameters\n", __func__);
+		return -EINVAL;
+	}
+
+	left = !!(l);
+	right = !!(r);
+
+	dev_dbg(codec->dev, "%s: Remeasuring impedance values\n", __func__);
+	dev_dbg(codec->dev, "%s: l: %pK, r: %pK, left=%d, right=%d\n", __func__,
+		 l, r, left, right);
+
+	/* Remeasure V2 values */
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0);
+	if (right)
+		r_t[2] = wcd9xxx_read_impedance_regs(mbhc);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
+	if (left)
+		l_t[2] = wcd9xxx_read_impedance_regs(mbhc);
+
+	/* Ramp down HPHR */
+	mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHR_RAMP_DISABLE);
+
+	if (right) {
+		/* Take R0'/R1' */
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2,
+				    0xFF, 0xF8);
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1,
+				    0xFF, 0xA0);
+		r_t[1] = wcd9xxx_read_impedance_regs(mbhc);
+		snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2,
+				    0xFF, 0xF0);
+		r_t[0] = wcd9xxx_read_impedance_regs(mbhc);
+	}
+
+	/* Put back gain to 1x */
+	if (!left && right)
+		mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_GAIN_0);
+
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0);
+	/* Take L2'' measurement */
+	l2_stereo = wcd9xxx_read_impedance_regs(mbhc);
+
+	/* Turn off HPHR PA and take L2''' */
+	mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHR_PA_DISABLE);
+	l2_mono = wcd9xxx_read_impedance_regs(mbhc);
+
+	/* Ramp HPHL from -15mV to 0V */
+	mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_HPHL_RAMP_DISABLE);
+
+	/* Take L0' and L1' with iCal */
+	l_t[0] = wcd9xxx_read_impedance_regs(mbhc);
+	snd_soc_update_bits(codec, WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8);
+	l_t[1] = wcd9xxx_read_impedance_regs(mbhc);
+
+	if (left) {
+		l[0] = l_t[0];
+		l[1] = l_t[1];
+		l[2] = l_t[2];
+	}
+	if (right) {
+		r[0] = r_t[0];
+		r[1] = r_t[1];
+		r[2] = r_t[2];
+	}
+
+	/* compute the new impedance values */
+	mbhc->mbhc_cb->compute_impedance(mbhc, l, r, zl, zr);
+
+	if (!left && right)
+		mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_GAIN_UPDATE_1X);
+	/* compute the new ZL'' value */
+	l_t[2] = l2_stereo;
+	mbhc->mbhc_cb->compute_impedance(mbhc, l_t, NULL, zl_stereo, NULL);
+	/* compute the new ZL''' value */
+	l_t[2] = l2_mono;
+	mbhc->mbhc_cb->compute_impedance(mbhc, l_t, NULL, zl_mono, NULL);
+
+	pr_debug("%s: L0': 0x%x, L1': 0x%x L2_stereo: 0x%x, L2_mono: 0x%x\n",
+		 __func__, l_t[0] & 0xffff, l_t[1] & 0xffff,
+		 l2_stereo & 0xffff, l2_mono & 0xffff);
+	pr_debug("%s: ZL_stereo = %u, ZL_mono = %u\n",
+		 __func__, *zl_stereo, *zl_mono);
+
+	return 0;
+}
+
+static enum mbhc_zdet_zones wcd9xxx_assign_zdet_zone(uint32_t zl, uint32_t zr,
+						     int32_t *gain)
+{
+	enum mbhc_zdet_zones zdet_zone;
+
+	if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) {
+		zdet_zone = ZL_ZONE1__ZR_ZONE1;
+		*gain = 0;
+	} else if (WCD9XXX_IS_IN_ZDET_ZONE_2(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_2(zr)) {
+		zdet_zone = ZL_ZONE2__ZR_ZONE2;
+		*gain = MBHC_ZDET_GAIN_1;
+	} else if (WCD9XXX_IS_IN_ZDET_ZONE_3(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_3(zr)) {
+		zdet_zone = ZL_ZONE3__ZR_ZONE3;
+		*gain = MBHC_ZDET_GAIN_2;
+	} else if (WCD9XXX_IS_IN_ZDET_ZONE_2(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) {
+		zdet_zone = ZL_ZONE2__ZR_ZONE1;
+		*gain = MBHC_ZDET_GAIN_1;
+	} else if (WCD9XXX_IS_IN_ZDET_ZONE_3(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_1(zr)) {
+		zdet_zone = ZL_ZONE3__ZR_ZONE1;
+		*gain = MBHC_ZDET_GAIN_2;
+	} else if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_2(zr)) {
+		zdet_zone = ZL_ZONE1__ZR_ZONE2;
+		*gain = MBHC_ZDET_GAIN_1;
+	} else if (WCD9XXX_IS_IN_ZDET_ZONE_1(zl) &&
+		 WCD9XXX_IS_IN_ZDET_ZONE_3(zr)) {
+		zdet_zone = ZL_ZONE1__ZR_ZONE3;
+		*gain = MBHC_ZDET_GAIN_2;
+	} else {
+		zdet_zone = ZL_ZR_NOT_IN_ZONE1;
+		*gain = MBHC_ZDET_GAIN_1;
+	}
+
+	return zdet_zone;
+}
+
+static int wcd9xxx_detect_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+				    uint32_t *zr)
+{
+	int i;
+	int ret = 0;
+	u8 micb_mbhc_val;
+	s16 l[3], r[3];
+	s16 *z[] = {
+		&l[0], &r[0], &r[1], &l[1], &l[2], &r[2],
+	};
+	u32 zl_stereo, zl_mono;
+	u32 zl_diff_1, zl_diff_2;
+	bool override_en;
+	struct snd_soc_codec *codec = mbhc->codec;
+	const int mux_wait_us = 25;
+	const struct wcd9xxx_reg_mask_val reg_set_mux[] = {
+		/* Phase 1 */
+		/* Set MBHC_MUX for HPHL without ical */
+		{WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
+		/* Set MBHC_MUX for HPHR without ical */
+		{WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
+		/* Set MBHC_MUX for HPHR with ical */
+		{WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF8},
+		/* Set MBHC_MUX for HPHL with ical */
+		{WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xC0},
+
+		/* Phase 2 */
+		{WCD9XXX_A_MBHC_SCALING_MUX_2, 0xFF, 0xF0},
+		/* Set MBHC_MUX for HPHR without ical and wait for 25us */
+		{WCD9XXX_A_MBHC_SCALING_MUX_1, 0xFF, 0xA0},
+	};
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BCL_ASSERT_LOCKED(mbhc->resmgr);
+
+	if (!mbhc->mbhc_cb || !mbhc->mbhc_cb->setup_zdet ||
+	    !mbhc->mbhc_cb->compute_impedance || !zl || !zr) {
+		return -EINVAL;
+	}
+
+	/*
+	 * Impedance detection is an intrusive function as it mutes RX paths,
+	 * enable PAs and etc.  Therefore codec drvier including ALSA
+	 * shouldn't read and write hardware registers during detection.
+	 */
+	wcd9xxx_onoff_ext_mclk(mbhc, true);
+
+	/*
+	 * For impedance detection, make sure to disable micbias from
+	 * override signal so that override does not cause micbias
+	 * to be enabled. This setting will be undone after completing
+	 * impedance measurement.
+	 */
+	micb_mbhc_val = snd_soc_read(codec, WCD9XXX_A_MAD_ANA_CTRL);
+	snd_soc_update_bits(codec, WCD9XXX_A_MAD_ANA_CTRL,
+			    0x10, 0x00);
+
+	override_en = (snd_soc_read(codec, WCD9XXX_A_CDC_MBHC_B1_CTL) & 0x04) ?
+					true : false;
+	if (!override_en)
+		wcd9xxx_turn_onoff_override(mbhc, true);
+	pr_debug("%s: Setting impedance detection\n", __func__);
+
+	/* Codec specific setup for L0, R0, L1 and R1 measurements */
+	mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_PRE_MEASURE);
+
+	pr_debug("%s: Performing impedance detection\n", __func__);
+	for (i = 0; i < ARRAY_SIZE(reg_set_mux) - 2; i++) {
+		snd_soc_update_bits(codec, reg_set_mux[i].reg,
+				    reg_set_mux[i].mask,
+				    reg_set_mux[i].val);
+		if (mbhc->mbhc_cb->get_cdc_type &&
+		    mbhc->mbhc_cb->get_cdc_type() ==
+				WCD9XXX_CDC_TYPE_TOMTOM) {
+			*(z[i]) = wcd9xxx_read_impedance_regs(mbhc);
+		} else {
+			if (mbhc->mbhc_cb->enable_mux_bias_block)
+				mbhc->mbhc_cb->enable_mux_bias_block(codec);
+			else
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_MBHC_SCALING_MUX_1,
+						0x80, 0x80);
+			/* 25us is required after mux change to settle down */
+			usleep_range(mux_wait_us,
+				 mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+			*(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0,
+							  true, false);
+		}
+	}
+
+	/* Codec specific setup for L2 and R2 measurements */
+	mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_POST_MEASURE);
+
+	for (; i < ARRAY_SIZE(reg_set_mux); i++) {
+		snd_soc_update_bits(codec, reg_set_mux[i].reg,
+				    reg_set_mux[i].mask,
+				    reg_set_mux[i].val);
+		if (mbhc->mbhc_cb->get_cdc_type &&
+		    mbhc->mbhc_cb->get_cdc_type() ==
+				WCD9XXX_CDC_TYPE_TOMTOM) {
+			*(z[i]) = wcd9xxx_read_impedance_regs(mbhc);
+		} else {
+			if (mbhc->mbhc_cb->enable_mux_bias_block)
+				mbhc->mbhc_cb->enable_mux_bias_block(codec);
+			else
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_MBHC_SCALING_MUX_1,
+						0x80, 0x80);
+			/* 25us is required after mux change to settle down */
+			usleep_range(mux_wait_us,
+				mux_wait_us + WCD9XXX_USLEEP_RANGE_MARGIN_US);
+			*(z[i]) = __wcd9xxx_codec_sta_dce(mbhc, 0,
+							  true, false);
+		}
+	}
+
+	mbhc->mbhc_cb->compute_impedance(mbhc, l, r, zl, zr);
+
+	/*
+	 * For some codecs, an additional step of zdet is needed
+	 * to overcome effects of noise and for better accuracy of
+	 * z values
+	 */
+	if (mbhc->mbhc_cb->get_cdc_type &&
+	     mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_TOMTOM) {
+		uint32_t zl_t = 0, zr_t = 0;
+		s16 *l_p, *r_p;
+		enum mbhc_zdet_zones zdet_zone;
+		int32_t gain;
+
+		zdet_zone = wcd9xxx_assign_zdet_zone(*zl, *zr, &gain);
+		switch (zdet_zone) {
+		case ZL_ZONE1__ZR_ZONE1:
+			l_p = NULL;
+			r_p = NULL;
+			break;
+		case ZL_ZONE2__ZR_ZONE2:
+		case ZL_ZONE3__ZR_ZONE3:
+		case ZL_ZR_NOT_IN_ZONE1:
+			l_p = l;
+			r_p = r;
+			break;
+		case ZL_ZONE2__ZR_ZONE1:
+		case ZL_ZONE3__ZR_ZONE1:
+		/* If ZR falls in Zone 1, further computations with
+		 * gain update are not required
+		 */
+			l_p = l;
+			r_p = NULL;
+			break;
+		case ZL_ZONE1__ZR_ZONE2:
+		case ZL_ZONE1__ZR_ZONE3:
+		/* If ZL falls in Zone 1, further computations with
+		 * gain update are not required
+		 */
+			l_p = NULL;
+			r_p = r;
+			break;
+		}
+		pr_debug("%s:zdet_zone = %d, gain = %d\n", __func__,
+			 zdet_zone, gain);
+		if (gain)
+			mbhc->mbhc_cb->setup_zdet(mbhc, gain);
+
+		wcd9xxx_remeasure_z_values(mbhc, l_p, r_p, &zl_t, &zr_t,
+					   &zl_stereo, &zl_mono);
+
+		*zl = (zl_t) ? zl_t : *zl;
+		*zr = (zr_t) ? zr_t : *zr;
+
+		/* Check for Mono/Stereo Type
+		 * Conditions to classify Mono/Stereo
+		 * i. Difference of zl_stereo and zl_mono > (1/2) of zl_mono
+		 * ii. Absolute difference of zl and zr above a threshold
+		 */
+		zl_diff_1 = (zl_mono > zl_stereo) ? (zl_mono - zl_stereo) :
+						  (zl_stereo - zl_mono);
+		zl_diff_2 = (*zl > *zr) ? (*zl - *zr) : (*zr - *zl);
+
+		mbhc->hph_type = MBHC_HPH_NONE;
+		if (mbhc->current_plug != PLUG_TYPE_HIGH_HPH) {
+			if ((zl_diff_1 > (zl_mono >> 1)) ||
+			    (zl_diff_2 > WCD9XXX_MONO_HS_DIFF_THR) ||
+			    ((*zl < WCD9XXX_MONO_HS_MIN_THR) &&
+			     (*zr > WCD9XXX_MONO_HS_MIN_THR)) ||
+			    ((*zr < WCD9XXX_MONO_HS_MIN_THR) &&
+			     (*zl > WCD9XXX_MONO_HS_MIN_THR))) {
+				pr_debug("%s: MONO plug type detected\n",
+					 __func__);
+				mbhc->hph_type = MBHC_HPH_MONO;
+				*zl = zl_mono;
+			} else {
+				pr_debug("%s: STEREO plug type detected\n",
+					 __func__);
+				mbhc->hph_type = MBHC_HPH_STEREO;
+			}
+		}
+	}
+
+	mbhc->mbhc_cb->setup_zdet(mbhc, MBHC_ZDET_PA_DISABLE);
+
+	/* Calculate z values based on the Q-fuse registers, if used */
+	if (mbhc->mbhc_cb->zdet_error_approx)
+		mbhc->mbhc_cb->zdet_error_approx(mbhc, zl, zr);
+
+	wcd9xxx_onoff_ext_mclk(mbhc, false);
+
+	if (!override_en)
+		wcd9xxx_turn_onoff_override(mbhc, false);
+
+	/* Undo the micbias disable for override */
+	snd_soc_write(codec, WCD9XXX_A_MAD_ANA_CTRL, micb_mbhc_val);
+
+	pr_debug("%s: L0: 0x%x(%d), L1: 0x%x(%d), L2: 0x%x(%d)\n",
+		 __func__,
+		 l[0] & 0xffff, l[0], l[1] & 0xffff, l[1], l[2] & 0xffff, l[2]);
+	pr_debug("%s: R0: 0x%x(%d), R1: 0x%x(%d), R2: 0x%x(%d)\n",
+		 __func__,
+		 r[0] & 0xffff, r[0], r[1] & 0xffff, r[1], r[2] & 0xffff, r[2]);
+	pr_debug("%s: RL %u milliohm, RR %u milliohm\n", __func__, *zl, *zr);
+	pr_debug("%s: Impedance detection completed\n", __func__);
+
+	return ret;
+}
+
+int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+			       uint32_t *zr)
+{
+	*zl = mbhc->zl;
+	*zr = mbhc->zr;
+
+	if (*zl && *zr)
+		return 0;
+	else
+		return -EINVAL;
+}
+
+/*
+ * wcd9xxx_mbhc_init : initialize MBHC internal structures.
+ *
+ * NOTE: mbhc->mbhc_cfg is not YET configure so shouldn't be used
+ */
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+		      struct snd_soc_codec *codec,
+		      int (*micbias_enable_cb)(struct snd_soc_codec*,  bool,
+						enum wcd9xxx_micbias_num),
+		      const struct wcd9xxx_mbhc_cb *mbhc_cb,
+		      const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
+		      int rco_clk_rate,
+		      bool impedance_det_en)
+{
+	int ret;
+	void *core_res;
+
+	pr_debug("%s: enter\n", __func__);
+	memset(&mbhc->mbhc_bias_regs, 0, sizeof(struct mbhc_micbias_regs));
+	memset(&mbhc->mbhc_data, 0, sizeof(struct mbhc_internal_cal_data));
+
+	mbhc->mbhc_data.t_sta_dce = DEFAULT_DCE_STA_WAIT;
+	mbhc->mbhc_data.t_dce = DEFAULT_DCE_WAIT;
+	mbhc->mbhc_data.t_sta = DEFAULT_STA_WAIT;
+	mbhc->mbhc_micbias_switched = false;
+	mbhc->polling_active = false;
+	mbhc->mbhc_state = MBHC_STATE_NONE;
+	mbhc->in_swch_irq_handler = false;
+	mbhc->current_plug = PLUG_TYPE_NONE;
+	mbhc->lpi_enabled = false;
+	mbhc->no_mic_headset_override = false;
+	mbhc->mbhc_last_resume = 0;
+	mbhc->codec = codec;
+	mbhc->resmgr = resmgr;
+	mbhc->resmgr->mbhc = mbhc;
+	mbhc->micbias_enable_cb = micbias_enable_cb;
+	mbhc->rco_clk_rate = rco_clk_rate;
+	mbhc->mbhc_cb = mbhc_cb;
+	mbhc->intr_ids = mbhc_cdc_intr_ids;
+	mbhc->impedance_detect = impedance_det_en;
+	mbhc->hph_type = MBHC_HPH_NONE;
+
+	if (mbhc->intr_ids == NULL) {
+		pr_err("%s: Interrupt mapping not provided\n", __func__);
+		return -EINVAL;
+	}
+
+	if (mbhc->headset_jack.jack == NULL) {
+		ret = snd_soc_card_jack_new(codec->component.card,
+					    "Headset Jack", WCD9XXX_JACK_MASK,
+					    &mbhc->headset_jack, NULL, 0);
+		if (ret) {
+			pr_err("%s: Failed to create new jack\n", __func__);
+			return ret;
+		}
+
+		ret = snd_soc_card_jack_new(codec->component.card,
+					    "Button Jack",
+					    WCD9XXX_JACK_BUTTON_MASK,
+					    &mbhc->button_jack, NULL, 0);
+		if (ret) {
+			pr_err("Failed to create new jack\n");
+			return ret;
+		}
+
+		ret = snd_jack_set_key(mbhc->button_jack.jack,
+				       SND_JACK_BTN_0,
+				       KEY_MEDIA);
+		if (ret) {
+			pr_err("%s: Failed to set code for btn-0\n",
+				__func__);
+			return ret;
+		}
+
+		INIT_DELAYED_WORK(&mbhc->mbhc_firmware_dwork,
+				  wcd9xxx_mbhc_fw_read);
+		INIT_DELAYED_WORK(&mbhc->mbhc_btn_dwork, wcd9xxx_btn_lpress_fn);
+		INIT_DELAYED_WORK(&mbhc->mbhc_insert_dwork,
+				  wcd9xxx_mbhc_insert_work);
+	}
+
+	mutex_init(&mbhc->mbhc_lock);
+
+	/* Register event notifier */
+	mbhc->nblock.notifier_call = wcd9xxx_event_notify;
+	ret = wcd9xxx_resmgr_register_notifier(mbhc->resmgr, &mbhc->nblock);
+	if (ret) {
+		pr_err("%s: Failed to register notifier %d\n", __func__, ret);
+		mutex_destroy(&mbhc->mbhc_lock);
+		return ret;
+	}
+
+	wcd9xxx_init_debugfs(mbhc);
+
+	/* Disable Impedance detection by default for certain codec types */
+	if (mbhc->mbhc_cb && mbhc->mbhc_cb->get_cdc_type &&
+	    (mbhc->mbhc_cb->get_cdc_type() == WCD9XXX_CDC_TYPE_HELICON))
+		impedance_detect_en = 0;
+	else
+		impedance_detect_en = impedance_det_en ? 1 : 0;
+
+	core_res = mbhc->resmgr->core_res;
+	ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->insertion,
+				  wcd9xxx_hs_insert_irq,
+				  "Headset insert detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d, ret = %d\n", __func__,
+		       mbhc->intr_ids->insertion, ret);
+		goto err_insert_irq;
+	}
+	wcd9xxx_disable_irq(core_res, mbhc->intr_ids->insertion);
+
+	ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->poll_plug_rem,
+				  wcd9xxx_hs_remove_irq,
+				  "Headset remove detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			mbhc->intr_ids->poll_plug_rem);
+		goto err_remove_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->dce_est_complete,
+				  wcd9xxx_dce_handler, "DC Estimation detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->dce_est_complete);
+		goto err_potential_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->button_release,
+				  wcd9xxx_release_handler,
+				  "Button Release detect", mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+			mbhc->intr_ids->button_release);
+		goto err_release_irq;
+	}
+
+	ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_left_ocp,
+				  wcd9xxx_hphl_ocp_irq, "HPH_L OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->hph_left_ocp);
+		goto err_hphl_ocp_irq;
+	}
+	wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_left_ocp);
+
+	ret = wcd9xxx_request_irq(core_res, mbhc->intr_ids->hph_right_ocp,
+				  wcd9xxx_hphr_ocp_irq, "HPH_R OCP detect",
+				  mbhc);
+	if (ret) {
+		pr_err("%s: Failed to request irq %d\n", __func__,
+		       mbhc->intr_ids->hph_right_ocp);
+		goto err_hphr_ocp_irq;
+	}
+	wcd9xxx_disable_irq(core_res, mbhc->intr_ids->hph_right_ocp);
+
+	wcd9xxx_regmgr_cond_register(resmgr, 1 << WCD9XXX_COND_HPH_MIC |
+					     1 << WCD9XXX_COND_HPH);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+
+err_hphr_ocp_irq:
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
+err_hphl_ocp_irq:
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
+err_release_irq:
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
+err_potential_irq:
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
+err_remove_irq:
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
+err_insert_irq:
+	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+
+	mutex_destroy(&mbhc->mbhc_lock);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_init);
+
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc)
+{
+	struct wcd9xxx_core_resource *core_res =
+				mbhc->resmgr->core_res;
+
+	wcd9xxx_regmgr_cond_deregister(mbhc->resmgr, 1 << WCD9XXX_COND_HPH_MIC |
+						     1 << WCD9XXX_COND_HPH);
+
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->button_release, mbhc);
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->dce_est_complete, mbhc);
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->poll_plug_rem, mbhc);
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->insertion, mbhc);
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->hs_jack_switch, mbhc);
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_left_ocp, mbhc);
+	wcd9xxx_free_irq(core_res, mbhc->intr_ids->hph_right_ocp, mbhc);
+
+	mutex_destroy(&mbhc->mbhc_lock);
+	wcd9xxx_resmgr_unregister_notifier(mbhc->resmgr, &mbhc->nblock);
+	wcd9xxx_cleanup_debugfs(mbhc);
+}
+EXPORT_SYMBOL(wcd9xxx_mbhc_deinit);
+
+MODULE_DESCRIPTION("wcd9xxx MBHC module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-mbhc.h b/sound/soc/codecs/wcd9xxx-mbhc.h
new file mode 100644
index 0000000..e35f7d4
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-mbhc.h
@@ -0,0 +1,492 @@
+/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_MBHC_H__
+#define __WCD9XXX_MBHC_H__
+
+#include "wcd9xxx-resmgr.h"
+#include "wcdcal-hwdep.h"
+
+#define WCD9XXX_CFILT_FAST_MODE 0x00
+#define WCD9XXX_CFILT_SLOW_MODE 0x40
+#define WCD9XXX_CFILT_EXT_PRCHG_EN 0x30
+#define WCD9XXX_CFILT_EXT_PRCHG_DSBL 0x00
+
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+
+struct mbhc_micbias_regs {
+	u16 cfilt_val;
+	u16 cfilt_ctl;
+	u16 mbhc_reg;
+	u16 int_rbias;
+	u16 ctl_reg;
+	u8 cfilt_sel;
+};
+
+enum mbhc_v_index {
+	MBHC_V_IDX_CFILT,
+	MBHC_V_IDX_VDDIO,
+	MBHC_V_IDX_NUM,
+};
+
+enum mbhc_cal_type {
+	MBHC_CAL_MCLK,
+	MBHC_CAL_RCO,
+	MBHC_CAL_NUM,
+};
+
+enum mbhc_impedance_detect_stages {
+	MBHC_ZDET_PRE_MEASURE,
+	MBHC_ZDET_POST_MEASURE,
+	MBHC_ZDET_GAIN_0,
+	MBHC_ZDET_GAIN_1,
+	MBHC_ZDET_GAIN_2,
+	MBHC_ZDET_HPHR_RAMP_DISABLE,
+	MBHC_ZDET_HPHL_RAMP_DISABLE,
+	MBHC_ZDET_RAMP_DISABLE,
+	MBHC_ZDET_HPHR_PA_DISABLE,
+	MBHC_ZDET_PA_DISABLE,
+	MBHC_ZDET_GAIN_UPDATE_1X,
+};
+
+/* Zone assignments used in WCD9330 for Zdet */
+enum mbhc_zdet_zones {
+	ZL_ZONE1__ZR_ZONE1,
+	ZL_ZONE2__ZR_ZONE2,
+	ZL_ZONE3__ZR_ZONE3,
+	ZL_ZONE2__ZR_ZONE1,
+	ZL_ZONE3__ZR_ZONE1,
+	ZL_ZONE1__ZR_ZONE2,
+	ZL_ZONE1__ZR_ZONE3,
+	ZL_ZR_NOT_IN_ZONE1,
+};
+
+/* Data used by MBHC */
+struct mbhc_internal_cal_data {
+	u16 dce_z;
+	u16 dce_nsc_cs_z;
+	u16 dce_mb;
+	u16 sta_z;
+	u16 sta_mb;
+	u32 t_sta_dce;
+	u32 t_dce;
+	u32 t_sta;
+	u32 micb_mv;
+	u16 v_ins_hu[MBHC_V_IDX_NUM];
+	u16 v_ins_h[MBHC_V_IDX_NUM];
+	u16 v_b1_hu[MBHC_V_IDX_NUM];
+	u16 v_b1_h[MBHC_V_IDX_NUM];
+	u16 v_brh[MBHC_V_IDX_NUM];
+	u16 v_brl;
+	u16 v_no_mic;
+	s16 v_inval_ins_low;
+	s16 v_inval_ins_high;
+	u16 v_cs_ins_h;
+};
+
+enum wcd9xxx_mbhc_plug_type {
+	PLUG_TYPE_INVALID = -1,
+	PLUG_TYPE_NONE,
+	PLUG_TYPE_HEADSET,
+	PLUG_TYPE_HEADPHONE,
+	PLUG_TYPE_HIGH_HPH,
+	PLUG_TYPE_GND_MIC_SWAP,
+	PLUG_TYPE_ANC_HEADPHONE,
+};
+
+enum wcd9xxx_mbhc_micbias_type {
+	MBHC_PRIMARY_MIC_MB,
+	MBHC_ANC_MIC_MB,
+};
+
+enum wcd9xxx_micbias_num {
+	MBHC_MICBIAS_INVALID = -1,
+	MBHC_MICBIAS1,
+	MBHC_MICBIAS2,
+	MBHC_MICBIAS3,
+	MBHC_MICBIAS4,
+};
+
+enum hw_jack_type {
+	FOUR_POLE_JACK = 0,
+	FIVE_POLE_JACK,
+	SIX_POLE_JACK,
+};
+
+enum wcd9xx_mbhc_micbias_enable_bits {
+	MBHC_MICBIAS_ENABLE_THRESHOLD_HEADSET,
+	MBHC_MICBIAS_ENABLE_REGULAR_HEADSET,
+};
+
+enum wcd9xx_mbhc_cs_enable_bits {
+	MBHC_CS_ENABLE_POLLING,
+	MBHC_CS_ENABLE_INSERTION,
+	MBHC_CS_ENABLE_REMOVAL,
+	MBHC_CS_ENABLE_DET_ANC,
+};
+
+enum wcd9xxx_mbhc_state {
+	MBHC_STATE_NONE = -1,
+	MBHC_STATE_POTENTIAL,
+	MBHC_STATE_POTENTIAL_RECOVERY,
+	MBHC_STATE_RELEASE,
+};
+
+enum wcd9xxx_mbhc_btn_det_mem {
+	MBHC_BTN_DET_V_BTN_LOW,
+	MBHC_BTN_DET_V_BTN_HIGH,
+	MBHC_BTN_DET_N_READY,
+	MBHC_BTN_DET_N_CIC,
+	MBHC_BTN_DET_GAIN
+};
+
+enum wcd9xxx_mbhc_clk_freq {
+	TAIKO_MCLK_12P2MHZ = 0,
+	TAIKO_MCLK_9P6MHZ,
+	TAIKO_NUM_CLK_FREQS,
+};
+
+enum wcd9xxx_mbhc_event_state {
+	MBHC_EVENT_PA_HPHL,
+	MBHC_EVENT_PA_HPHR,
+	MBHC_EVENT_PRE_TX_3_ON,
+	MBHC_EVENT_POST_TX_3_OFF,
+};
+
+enum mbhc_hph_type {
+	MBHC_HPH_NONE = 0,
+	MBHC_HPH_MONO,
+	MBHC_HPH_STEREO,
+};
+
+struct wcd9xxx_mbhc_general_cfg {
+	u8 t_ldoh;
+	u8 t_bg_fast_settle;
+	u8 t_shutdown_plug_rem;
+	u8 mbhc_nsa;
+	u8 mbhc_navg;
+	u8 v_micbias_l;
+	u8 v_micbias;
+	u8 mbhc_reserved;
+	u16 settle_wait;
+	u16 t_micbias_rampup;
+	u16 t_micbias_rampdown;
+	u16 t_supply_bringup;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_detect_cfg {
+	u32 mic_current;
+	u32 hph_current;
+	u16 t_mic_pid;
+	u16 t_ins_complete;
+	u16 t_ins_retry;
+	u16 v_removal_delta;
+	u8 micbias_slow_ramp;
+	u8 reserved0;
+	u8 reserved1;
+	u8 reserved2;
+} __packed;
+
+struct wcd9xxx_mbhc_plug_type_cfg {
+	u8 av_detect;
+	u8 mono_detect;
+	u8 num_ins_tries;
+	u8 reserved0;
+	s16 v_no_mic;
+	s16 v_av_min;
+	s16 v_av_max;
+	s16 v_hs_min;
+	s16 v_hs_max;
+	u16 reserved1;
+} __packed;
+
+struct wcd9xxx_mbhc_btn_detect_cfg {
+	s8 c[8];
+	u8 nc;
+	u8 n_meas;
+	u8 mbhc_nsc;
+	u8 n_btn_meas;
+	u8 n_btn_con;
+	u8 num_btn;
+	u8 reserved0;
+	u8 reserved1;
+	u16 t_poll;
+	u16 t_bounce_wait;
+	u16 t_rel_timeout;
+	s16 v_btn_press_delta_sta;
+	s16 v_btn_press_delta_cic;
+	u16 t_btn0_timeout;
+	s16 _v_btn_low[0]; /* v_btn_low[num_btn] */
+	s16 _v_btn_high[0]; /* v_btn_high[num_btn] */
+	u8 _n_ready[TAIKO_NUM_CLK_FREQS];
+	u8 _n_cic[TAIKO_NUM_CLK_FREQS];
+	u8 _gain[TAIKO_NUM_CLK_FREQS];
+} __packed;
+
+struct wcd9xxx_mbhc_imped_detect_cfg {
+	u8 _hs_imped_detect;
+	u8 _n_rload;
+	u8 _hph_keep_on;
+	u8 _repeat_rload_calc;
+	u16 _t_dac_ramp_time;
+	u16 _rhph_high;
+	u16 _rhph_low;
+	u16 _rload[0]; /* rload[n_rload] */
+	u16 _alpha[0]; /* alpha[n_rload] */
+	u16 _beta[3];
+} __packed;
+
+struct wcd9xxx_mbhc_config {
+	bool read_fw_bin;
+	/*
+	 * void* calibration contains:
+	 *  struct wcd9xxx_mbhc_general_cfg generic;
+	 *  struct wcd9xxx_mbhc_plug_detect_cfg plug_det;
+	 *  struct wcd9xxx_mbhc_plug_type_cfg plug_type;
+	 *  struct wcd9xxx_mbhc_btn_detect_cfg btn_det;
+	 *  struct wcd9xxx_mbhc_imped_detect_cfg imped_det;
+	 * Note: various size depends on btn_det->num_btn
+	 */
+	void *calibration;
+	enum wcd9xxx_micbias_num micbias;
+	enum wcd9xxx_micbias_num anc_micbias;
+	int (*mclk_cb_fn)(struct snd_soc_codec*, int, bool);
+	unsigned int mclk_rate;
+	unsigned int gpio;
+	unsigned int gpio_irq;
+	int gpio_level_insert;
+	bool insert_detect; /* codec has own MBHC_INSERT_DETECT */
+	bool detect_extn_cable;
+	/* bit mask of enum wcd9xx_mbhc_micbias_enable_bits */
+	unsigned long micbias_enable_flags;
+	/* swap_gnd_mic returns true if extern GND/MIC swap switch toggled */
+	bool (*swap_gnd_mic)(struct snd_soc_codec *);
+	unsigned long cs_enable_flags;
+	bool use_int_rbias;
+	bool do_recalibration;
+	bool use_vddio_meas;
+	bool enable_anc_mic_detect;
+	enum hw_jack_type hw_jack_type;
+	int key_code[8];
+};
+
+struct wcd9xxx_cfilt_mode {
+	u8 reg_mode_val;
+	u8 cur_mode_val;
+	u8 reg_mask;
+};
+
+struct wcd9xxx_mbhc_intr {
+	int poll_plug_rem;
+	int shortavg_complete;
+	int potential_button_press;
+	int button_release;
+	int dce_est_complete;
+	int insertion;
+	int hph_left_ocp;
+	int hph_right_ocp;
+	int hs_jack_switch;
+};
+
+struct wcd9xxx_mbhc_cb {
+	void (*enable_mux_bias_block)(struct snd_soc_codec *);
+	void (*cfilt_fast_mode)(struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+	void (*codec_specific_cal)(struct snd_soc_codec *,
+				    struct wcd9xxx_mbhc *);
+	struct wcd9xxx_cfilt_mode (*switch_cfilt_mode)(struct wcd9xxx_mbhc *,
+							bool);
+	void (*select_cfilt)(struct snd_soc_codec *, struct wcd9xxx_mbhc *);
+	enum wcd9xxx_cdc_type (*get_cdc_type)(void);
+	void (*enable_clock_gate)(struct snd_soc_codec *, bool);
+	int (*setup_zdet)(struct wcd9xxx_mbhc *,
+			   enum mbhc_impedance_detect_stages stage);
+	void (*compute_impedance)(struct wcd9xxx_mbhc *, s16 *, s16 *,
+				   uint32_t *, uint32_t *);
+	void (*zdet_error_approx)(struct wcd9xxx_mbhc *, uint32_t *,
+				    uint32_t *);
+	void (*enable_mbhc_txfe)(struct snd_soc_codec *, bool);
+	int (*enable_mb_source)(struct snd_soc_codec *, bool, bool);
+	void (*setup_int_rbias)(struct snd_soc_codec *, bool);
+	void (*pull_mb_to_vddio)(struct snd_soc_codec *, bool);
+	bool (*insert_rem_status)(struct snd_soc_codec *);
+	void (*micbias_pulldown_ctrl)(struct wcd9xxx_mbhc *, bool);
+	int (*codec_rco_ctrl)(struct snd_soc_codec *, bool);
+	void (*hph_auto_pulldown_ctrl)(struct snd_soc_codec *, bool);
+	struct firmware_cal * (*get_hwdep_fw_cal)(struct snd_soc_codec *,
+				enum wcd_cal_type);
+};
+
+struct wcd9xxx_mbhc {
+	bool polling_active;
+	/* Delayed work to report long button press */
+	struct delayed_work mbhc_btn_dwork;
+	int buttons_pressed;
+	enum wcd9xxx_mbhc_state mbhc_state;
+	struct wcd9xxx_mbhc_config *mbhc_cfg;
+	const struct wcd9xxx_mbhc_cb *mbhc_cb;
+
+	struct mbhc_internal_cal_data mbhc_data;
+
+	struct mbhc_micbias_regs mbhc_bias_regs;
+	struct mbhc_micbias_regs mbhc_anc_bias_regs;
+
+	bool mbhc_micbias_switched;
+
+	u32 hph_status; /* track headhpone status */
+	u8 hphlocp_cnt; /* headphone left ocp retry */
+	u8 hphrocp_cnt; /* headphone right ocp retry */
+
+	/* Work to perform MBHC Firmware Read */
+	struct delayed_work mbhc_firmware_dwork;
+	const struct firmware *mbhc_fw;
+	struct firmware_cal *mbhc_cal;
+
+	struct delayed_work mbhc_insert_dwork;
+
+	u8 current_plug;
+	struct work_struct correct_plug_swch;
+	/*
+	 * Work to perform polling on microphone voltage
+	 * in order to correct plug type once plug type
+	 * is detected as headphone
+	 */
+	struct work_struct correct_plug_noswch;
+	bool hs_detect_work_stop;
+
+	bool lpi_enabled; /* low power insertion detection */
+	bool in_swch_irq_handler;
+
+	struct wcd9xxx_resmgr *resmgr;
+	struct snd_soc_codec *codec;
+
+	bool no_mic_headset_override;
+
+	/* track PA/DAC state to sync with userspace */
+	unsigned long hph_pa_dac_state;
+	/*
+	 * save codec's state with resmgr event notification
+	 * bit flags of enum wcd9xxx_mbhc_event_state
+	 */
+	unsigned long event_state;
+
+	unsigned long mbhc_last_resume; /* in jiffies */
+
+	bool insert_detect_level_insert;
+
+	struct snd_soc_jack headset_jack;
+	struct snd_soc_jack button_jack;
+
+	struct notifier_block nblock;
+
+	bool micbias_enable;
+	int (*micbias_enable_cb)(struct snd_soc_codec*,  bool,
+				  enum wcd9xxx_micbias_num);
+
+	bool impedance_detect;
+	/* impedance of hphl and hphr */
+	uint32_t zl, zr;
+
+	u32 rco_clk_rate;
+
+	bool update_z;
+
+	u8   scaling_mux_in;
+	/* Holds codec specific interrupt mapping */
+	const struct wcd9xxx_mbhc_intr *intr_ids;
+
+	/* Indicates status of current source switch */
+	bool is_cs_enabled;
+
+	/* Holds type of Headset - Mono/Stereo */
+	enum mbhc_hph_type hph_type;
+
+#ifdef CONFIG_DEBUG_FS
+	struct dentry *debugfs_poke;
+	struct dentry *debugfs_mbhc;
+#endif
+
+	struct mutex mbhc_lock;
+};
+
+#define WCD9XXX_MBHC_CAL_SIZE(buttons, rload) ( \
+	sizeof(enum wcd9xxx_micbias_num) + \
+	sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+	    ((sizeof(s16) + sizeof(s16)) * buttons) + \
+	sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    ((sizeof(u16) + sizeof(u16)) * rload) \
+	)
+
+#define WCD9XXX_MBHC_CAL_GENERAL_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_general_cfg *) cali)
+#define WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_plug_detect_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_GENERAL_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_plug_type_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_PLUG_DET_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_btn_detect_cfg *) \
+	    &(WCD9XXX_MBHC_CAL_PLUG_TYPE_PTR(cali)[1]))
+#define WCD9XXX_MBHC_CAL_IMPED_DET_PTR(cali) ( \
+	    (struct wcd9xxx_mbhc_imped_detect_cfg *) \
+	    (((void *)&WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)[1]) + \
+	     (WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->num_btn * \
+	      (sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_low[0]) + \
+	       sizeof(WCD9XXX_MBHC_CAL_BTN_DET_PTR(cali)->_v_btn_high[0])))) \
+	)
+
+/* minimum size of calibration data assuming there is only one button and
+ * one rload.
+ */
+#define WCD9XXX_MBHC_CAL_MIN_SIZE ( \
+	    sizeof(struct wcd9xxx_mbhc_general_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_plug_detect_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_plug_type_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    (sizeof(u16) * 2) \
+	)
+
+#define WCD9XXX_MBHC_CAL_BTN_SZ(cfg_ptr) ( \
+	    sizeof(struct wcd9xxx_mbhc_btn_detect_cfg) + \
+	    (cfg_ptr->num_btn * (sizeof(cfg_ptr->_v_btn_low[0]) + \
+				 sizeof(cfg_ptr->_v_btn_high[0]))))
+
+#define WCD9XXX_MBHC_CAL_IMPED_MIN_SZ ( \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + sizeof(u16) * 2)
+
+#define WCD9XXX_MBHC_CAL_IMPED_SZ(cfg_ptr) ( \
+	    sizeof(struct wcd9xxx_mbhc_imped_detect_cfg) + \
+	    (cfg_ptr->_n_rload * \
+	     (sizeof(cfg_ptr->_rload[0]) + sizeof(cfg_ptr->_alpha[0]))))
+
+int wcd9xxx_mbhc_set_keycode(struct wcd9xxx_mbhc *mbhc);
+int wcd9xxx_mbhc_start(struct wcd9xxx_mbhc *mbhc,
+		       struct wcd9xxx_mbhc_config *mbhc_cfg);
+void wcd9xxx_mbhc_stop(struct wcd9xxx_mbhc *mbhc);
+int wcd9xxx_mbhc_init(struct wcd9xxx_mbhc *mbhc, struct wcd9xxx_resmgr *resmgr,
+		      struct snd_soc_codec *codec,
+		      int (*micbias_enable_cb)(struct snd_soc_codec*,  bool,
+						enum wcd9xxx_micbias_num),
+		      const struct wcd9xxx_mbhc_cb *mbhc_cb,
+		      const struct wcd9xxx_mbhc_intr *mbhc_cdc_intr_ids,
+		      int rco_clk_rate,
+		      bool impedance_det_en);
+void wcd9xxx_mbhc_deinit(struct wcd9xxx_mbhc *mbhc);
+void *wcd9xxx_mbhc_cal_btn_det_mp(
+			    const struct wcd9xxx_mbhc_btn_detect_cfg *btn_det,
+			    const enum wcd9xxx_mbhc_btn_det_mem mem);
+int wcd9xxx_mbhc_get_impedance(struct wcd9xxx_mbhc *mbhc, uint32_t *zl,
+			       uint32_t *zr);
+#endif /* __WCD9XXX_MBHC_H__ */
diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.c b/sound/soc/codecs/wcd9xxx-resmgr-v2.c
new file mode 100644
index 0000000..fde13d2
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include <linux/mfd/wcd934x/registers.h>
+#include <sound/soc.h>
+#include "wcd9xxx-resmgr-v2.h"
+
+#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 5000
+#define WCD93XX_ANA_BIAS 0x0601
+#define WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL 0x0d41
+#define WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL 0x0d42
+
+static void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr,
+					  int sido_src);
+static const char *wcd_resmgr_clk_type_to_str(enum wcd_clock_type clk_type)
+{
+	if (clk_type == WCD_CLK_OFF)
+		return "WCD_CLK_OFF";
+	else if (clk_type == WCD_CLK_RCO)
+		return "WCD_CLK_RCO";
+	else if (clk_type == WCD_CLK_MCLK)
+		return "WCD_CLK_MCLK";
+	else
+		return "WCD_CLK_UNDEFINED";
+}
+
+static int wcd_resmgr_codec_reg_update_bits(struct wcd9xxx_resmgr_v2 *resmgr,
+					    u16 reg, u8 mask, u8 val)
+{
+	bool change;
+	int ret;
+
+	if (resmgr->codec_type == WCD934X) {
+		/* Tavil does not support ANA_CLK_TOP register */
+		if (reg == WCD9335_ANA_CLK_TOP)
+			return 0;
+	} else {
+		/* Tasha does not support CLK_SYS_MCLK_PRG register */
+		if (reg == WCD934X_CLK_SYS_MCLK_PRG)
+			return 0;
+	}
+	if (resmgr->codec) {
+		ret = snd_soc_update_bits(resmgr->codec, reg, mask, val);
+	} else if (resmgr->core_res->wcd_core_regmap) {
+		ret = regmap_update_bits_check(
+				resmgr->core_res->wcd_core_regmap,
+				reg, mask, val, &change);
+		if (!ret)
+			ret = change;
+	} else {
+		pr_err("%s: codec/regmap not defined\n", __func__);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int wcd_resmgr_codec_reg_read(struct wcd9xxx_resmgr_v2 *resmgr,
+				     unsigned int reg)
+{
+	int val, ret;
+
+	if (resmgr->codec_type == WCD934X) {
+		if (reg == WCD9335_ANA_CLK_TOP)
+			return 0;
+	} else {
+		if (reg == WCD934X_CLK_SYS_MCLK_PRG)
+			return 0;
+	}
+	if (resmgr->codec) {
+		val = snd_soc_read(resmgr->codec, reg);
+	} else if (resmgr->core_res->wcd_core_regmap) {
+		ret = regmap_read(resmgr->core_res->wcd_core_regmap,
+				  reg, &val);
+		if (ret)
+			val = ret;
+	} else {
+		pr_err("%s: wcd regmap is null\n", __func__);
+		return -EINVAL;
+	}
+
+	return val;
+}
+
+/*
+ * wcd_resmgr_get_clk_type()
+ * Returns clk type that is currently enabled
+ */
+int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	if (!resmgr) {
+		pr_err("%s: resmgr not initialized\n", __func__);
+		return -EINVAL;
+	}
+	return resmgr->clk_type;
+}
+
+static void wcd_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr_v2 *resmgr,
+						int clk_users)
+{
+	/* Caller of this function should have acquired BG_CLK lock */
+	if (clk_users) {
+		if (resmgr->resmgr_cb &&
+		    resmgr->resmgr_cb->cdc_rco_ctrl) {
+			while (clk_users--)
+				resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec,
+								true);
+		}
+	}
+}
+
+void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	int old_bg_audio_users;
+	int old_clk_rco_users, old_clk_mclk_users;
+
+	WCD9XXX_V2_BG_CLK_LOCK(resmgr);
+
+	old_bg_audio_users = resmgr->master_bias_users;
+	old_clk_mclk_users = resmgr->clk_mclk_users;
+	old_clk_rco_users = resmgr->clk_rco_users;
+	resmgr->master_bias_users = 0;
+	resmgr->clk_mclk_users = 0;
+	resmgr->clk_rco_users = 0;
+	resmgr->clk_type = WCD_CLK_OFF;
+
+	pr_debug("%s: old_bg_audio_users=%d old_clk_mclk_users=%d old_clk_rco_users=%d\n",
+		 __func__, old_bg_audio_users,
+		 old_clk_mclk_users, old_clk_rco_users);
+
+	if (old_bg_audio_users) {
+		while (old_bg_audio_users--)
+			wcd_resmgr_enable_master_bias(resmgr);
+	}
+
+	if (old_clk_mclk_users) {
+		while (old_clk_mclk_users--)
+			wcd_resmgr_enable_clk_block(resmgr, WCD_CLK_MCLK);
+	}
+
+	if (old_clk_rco_users)
+		wcd_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users);
+
+	WCD9XXX_V2_BG_CLK_UNLOCK(resmgr);
+}
+
+
+/*
+ * wcd_resmgr_enable_master_bias: enable codec master bias
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ */
+int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	mutex_lock(&resmgr->master_bias_lock);
+
+	resmgr->master_bias_users++;
+	if (resmgr->master_bias_users == 1) {
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+						 0x80, 0x80);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+						 0x40, 0x40);
+		/*
+		 * 1ms delay is required after pre-charge is enabled
+		 * as per HW requirement
+		 */
+		usleep_range(1000, 1100);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+						 0x40, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+						WCD93XX_ANA_BIAS, 0x20, 0x00);
+	}
+
+	pr_debug("%s: current master bias users: %d\n", __func__,
+		 resmgr->master_bias_users);
+
+	mutex_unlock(&resmgr->master_bias_lock);
+	return 0;
+}
+
+/*
+ * wcd_resmgr_disable_master_bias: disable codec master bias
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ */
+int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	mutex_lock(&resmgr->master_bias_lock);
+	if (resmgr->master_bias_users <= 0) {
+		mutex_unlock(&resmgr->master_bias_lock);
+		return -EINVAL;
+	}
+
+	resmgr->master_bias_users--;
+	if (resmgr->master_bias_users == 0) {
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD93XX_ANA_BIAS,
+						 0x80, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+						WCD93XX_ANA_BIAS, 0x20, 0x00);
+	}
+	mutex_unlock(&resmgr->master_bias_lock);
+	return 0;
+}
+
+static int wcd_resmgr_enable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	/* Enable mclk requires master bias to be enabled first */
+	if (resmgr->master_bias_users <= 0) {
+		pr_err("%s: Cannot turn on MCLK, BG is not enabled\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (((resmgr->clk_mclk_users == 0) &&
+	     (resmgr->clk_type == WCD_CLK_MCLK)) ||
+	    ((resmgr->clk_mclk_users > 0) &&
+	    (resmgr->clk_type != WCD_CLK_MCLK))) {
+		pr_err("%s: Error enabling MCLK, clk_type: %s\n",
+			__func__,
+			wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+		return -EINVAL;
+	}
+
+	if (++resmgr->clk_mclk_users == 1) {
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+				WCD9335_ANA_CLK_TOP, 0x80, 0x80);
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+				WCD9335_ANA_CLK_TOP, 0x08, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+				WCD9335_ANA_CLK_TOP, 0x04, 0x04);
+		if (resmgr->codec_type == WCD934X) {
+			/*
+			 * In tavil clock contrl register is changed
+			 * to CLK_SYS_MCLK_PRG
+			 */
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x00);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD934X_CLK_SYS_MCLK_PRG, 0x91, 0x91);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+					0x01, 0x01);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x01, 0x01);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD934X_CODEC_RPM_CLK_MCLK_CFG,
+					0x04, 0x04);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x01, 0x01);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD934X_CODEC_RPM_CLK_GATE, 0x03, 0x00);
+			wcd_resmgr_set_sido_input_src(resmgr,
+						      SIDO_SOURCE_RCO_BG);
+		} else {
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD93XX_CDC_CLK_RST_CTRL_FS_CNT_CONTROL,
+					0x01, 0x01);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD93XX_CDC_CLK_RST_CTRL_MCLK_CONTROL,
+					0x01, 0x01);
+		}
+		/*
+		 * 10us sleep is required after clock is enabled
+		 * as per HW requirement
+		 */
+		usleep_range(10, 15);
+	}
+
+	resmgr->clk_type = WCD_CLK_MCLK;
+
+	pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__,
+		 resmgr->clk_mclk_users,
+		 wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+	return 0;
+}
+
+static int wcd_resmgr_disable_clk_mclk(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	if (resmgr->clk_mclk_users <= 0) {
+		pr_err("%s: No mclk users, cannot disable mclk\n", __func__);
+		return -EINVAL;
+	}
+
+	if (--resmgr->clk_mclk_users == 0) {
+		if (resmgr->clk_rco_users > 0) {
+			/* MCLK to RCO switch */
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD9335_ANA_CLK_TOP,
+					0x08, 0x08);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD934X_CLK_SYS_MCLK_PRG, 0x02, 0x02);
+			resmgr->clk_type = WCD_CLK_RCO;
+		} else {
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD9335_ANA_CLK_TOP,
+					0x04, 0x00);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+					WCD934X_CLK_SYS_MCLK_PRG, 0x81, 0x00);
+			resmgr->clk_type = WCD_CLK_OFF;
+		}
+
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP,
+						 0x80, 0x00);
+	}
+
+	if ((resmgr->codec_type == WCD934X) &&
+	    (resmgr->clk_type == WCD_CLK_OFF))
+		wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL);
+
+	pr_debug("%s: mclk_users: %d, clk_type: %s\n", __func__,
+		 resmgr->clk_mclk_users,
+		 wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+	return 0;
+}
+
+static void wcd_resmgr_set_buck_accuracy(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+					 0x02, 0x02);
+	/* 100us sleep needed after HIGH_ACCURACY_PRE_EN1 */
+	usleep_range(100, 110);
+	wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+					 0x01, 0x01);
+	/* 100us sleep needed after HIGH_ACCURACY_PRE_EN2 */
+	usleep_range(100, 110);
+	wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+					 0x04, 0x04);
+	/* 100us sleep needed after HIGH_ACCURACY_EN */
+	usleep_range(100, 110);
+}
+
+static int wcd_resmgr_enable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	bool rco_cal_done = true;
+
+	resmgr->clk_rco_users++;
+	if ((resmgr->clk_rco_users == 1) &&
+	    ((resmgr->clk_type == WCD_CLK_OFF) ||
+	     (resmgr->clk_mclk_users == 0))) {
+		pr_warn("%s: RCO enable requires MCLK to be ON first\n",
+			__func__);
+		resmgr->clk_rco_users--;
+		return -EINVAL;
+	} else if ((resmgr->clk_rco_users == 1) &&
+		   (resmgr->clk_mclk_users)) {
+		/* RCO Enable */
+		if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL) {
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+							 WCD9335_ANA_RCO,
+							 0x80, 0x80);
+			if (resmgr->codec_type == WCD934X)
+				wcd_resmgr_set_buck_accuracy(resmgr);
+		}
+
+		/*
+		 * 20us required after RCO BG is enabled as per HW
+		 * requirements
+		 */
+		usleep_range(20, 25);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+						 0x40, 0x40);
+		/*
+		 * 20us required after RCO is enabled as per HW
+		 * requirements
+		 */
+		usleep_range(20, 25);
+		/* RCO Calibration */
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+						 0x04, 0x04);
+		if (resmgr->codec_type == WCD934X)
+			/*
+			 * For wcd934x codec, 20us sleep is needed
+			 * after enabling RCO calibration
+			 */
+			usleep_range(20, 25);
+
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+						 0x04, 0x00);
+		if (resmgr->codec_type == WCD934X)
+			/*
+			 * For wcd934x codec, 20us sleep is needed
+			 * after disabling RCO calibration
+			 */
+			usleep_range(20, 25);
+
+		/* RCO calibration takes app. 5ms to complete */
+		usleep_range(WCD9XXX_RCO_CALIBRATION_DELAY_INC_US,
+		       WCD9XXX_RCO_CALIBRATION_DELAY_INC_US + 100);
+		if (wcd_resmgr_codec_reg_read(resmgr, WCD9335_ANA_RCO) & 0x02)
+			rco_cal_done = false;
+
+		WARN((!rco_cal_done), "RCO Calibration failed\n");
+
+		/* Switch MUX to RCO */
+		if (resmgr->clk_mclk_users == 1) {
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+							WCD9335_ANA_CLK_TOP,
+							0x08, 0x08);
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+						 WCD934X_CLK_SYS_MCLK_PRG,
+						 0x02, 0x02);
+			resmgr->clk_type = WCD_CLK_RCO;
+		}
+	}
+	pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__,
+		 resmgr->clk_rco_users,
+		 wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+	return 0;
+}
+
+static int wcd_resmgr_disable_clk_rco(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	if ((resmgr->clk_rco_users <= 0) ||
+	    (resmgr->clk_type == WCD_CLK_OFF)) {
+		pr_err("%s: rco_clk_users = %d, clk_type = %d, cannot disable\n",
+			__func__, resmgr->clk_rco_users, resmgr->clk_type);
+		return -EINVAL;
+	}
+
+	resmgr->clk_rco_users--;
+
+	if ((resmgr->clk_rco_users == 0) &&
+	    (resmgr->clk_type == WCD_CLK_RCO)) {
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP,
+						 0x08, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+						 WCD934X_CLK_SYS_MCLK_PRG,
+						 0x02, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_CLK_TOP,
+						 0x04, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+						 0x40, 0x00);
+		if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL)
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+							 WCD9335_ANA_RCO,
+							 0x80, 0x00);
+		wcd_resmgr_codec_reg_update_bits(resmgr,
+						 WCD934X_CLK_SYS_MCLK_PRG,
+						 0x01, 0x00);
+		resmgr->clk_type = WCD_CLK_OFF;
+	} else if ((resmgr->clk_rco_users == 0) &&
+	      (resmgr->clk_mclk_users)) {
+		/* Disable RCO while MCLK is ON */
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD9335_ANA_RCO,
+						 0x40, 0x00);
+		if (resmgr->sido_input_src == SIDO_SOURCE_INTERNAL)
+			wcd_resmgr_codec_reg_update_bits(resmgr,
+							 WCD9335_ANA_RCO,
+							 0x80, 0x00);
+	}
+
+	if ((resmgr->codec_type == WCD934X) &&
+	    (resmgr->clk_type == WCD_CLK_OFF))
+		wcd_resmgr_set_sido_input_src(resmgr, SIDO_SOURCE_INTERNAL);
+
+	pr_debug("%s: rco clk users: %d, clk_type: %s\n", __func__,
+		 resmgr->clk_rco_users,
+		 wcd_resmgr_clk_type_to_str(resmgr->clk_type));
+
+	return 0;
+}
+
+/*
+ * wcd_resmgr_enable_clk_block: enable MCLK or RCO
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ * @type: Clock type to enable
+ */
+int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+				enum wcd_clock_type type)
+{
+	int ret;
+
+	switch (type) {
+	case WCD_CLK_MCLK:
+		ret = wcd_resmgr_enable_clk_mclk(resmgr);
+		break;
+	case WCD_CLK_RCO:
+		ret = wcd_resmgr_enable_clk_rco(resmgr);
+		break;
+	default:
+		pr_err("%s: Unknown Clock type: %s\n", __func__,
+			wcd_resmgr_clk_type_to_str(type));
+		ret = -EINVAL;
+		break;
+	};
+
+	if (ret)
+		pr_err("%s: Enable clock %s failed\n", __func__,
+			wcd_resmgr_clk_type_to_str(type));
+
+	return ret;
+}
+
+static void wcd_resmgr_set_sido_input_src(struct wcd9xxx_resmgr_v2 *resmgr,
+					  int sido_src)
+{
+	if (!resmgr)
+		return;
+
+	if (sido_src == resmgr->sido_input_src)
+		return;
+
+	if (sido_src == SIDO_SOURCE_INTERNAL) {
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+						 0x04, 0x00);
+		usleep_range(100, 110);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+						 0x03, 0x00);
+		usleep_range(100, 110);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO,
+						 0x80, 0x00);
+		usleep_range(100, 110);
+		resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
+		pr_debug("%s: sido input src to internal\n", __func__);
+	} else if (sido_src == SIDO_SOURCE_RCO_BG) {
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_RCO,
+						 0x80, 0x80);
+		usleep_range(100, 110);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+						 0x02, 0x02);
+		usleep_range(100, 110);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+						 0x01, 0x01);
+		usleep_range(100, 110);
+		wcd_resmgr_codec_reg_update_bits(resmgr, WCD934X_ANA_BUCK_CTL,
+						 0x04, 0x04);
+		usleep_range(100, 110);
+		resmgr->sido_input_src = SIDO_SOURCE_RCO_BG;
+		pr_debug("%s: sido input src to external\n", __func__);
+	}
+}
+
+/*
+ * wcd_resmgr_set_sido_input_src_locked:
+ *   Set SIDO input in BG_CLK locked context
+ *
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ * @sido_src: Select the SIDO input source
+ */
+void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr,
+					  int sido_src)
+{
+	if (!resmgr)
+		return;
+
+	WCD9XXX_V2_BG_CLK_LOCK(resmgr);
+	wcd_resmgr_set_sido_input_src(resmgr, sido_src);
+	WCD9XXX_V2_BG_CLK_UNLOCK(resmgr);
+}
+EXPORT_SYMBOL(wcd_resmgr_set_sido_input_src_locked);
+
+/*
+ * wcd_resmgr_disable_clk_block: disable MCLK or RCO
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ * @type: Clock type to disable
+ */
+int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+				enum wcd_clock_type type)
+{
+	int ret;
+
+	switch (type) {
+	case WCD_CLK_MCLK:
+		ret = wcd_resmgr_disable_clk_mclk(resmgr);
+		break;
+	case WCD_CLK_RCO:
+		ret = wcd_resmgr_disable_clk_rco(resmgr);
+		break;
+	default:
+		pr_err("%s: Unknown Clock type: %s\n", __func__,
+			wcd_resmgr_clk_type_to_str(type));
+		ret = -EINVAL;
+		break;
+	};
+
+	if (ret)
+		pr_err("%s: Disable clock %s failed\n", __func__,
+			wcd_resmgr_clk_type_to_str(type));
+
+	return ret;
+}
+
+/*
+ * wcd_resmgr_init: initialize wcd resource manager
+ * @core_res: handle to struct wcd9xxx_core_resource
+ *
+ * Early init call without a handle to snd_soc_codec *
+ */
+struct wcd9xxx_resmgr_v2 *wcd_resmgr_init(
+		struct wcd9xxx_core_resource *core_res,
+		struct snd_soc_codec *codec)
+{
+	struct wcd9xxx_resmgr_v2 *resmgr;
+	struct wcd9xxx *wcd9xxx;
+
+	resmgr = kzalloc(sizeof(struct wcd9xxx_resmgr_v2), GFP_KERNEL);
+	if (!resmgr)
+		return ERR_PTR(-ENOMEM);
+
+	wcd9xxx = container_of(core_res, struct wcd9xxx, core_res);
+	if (!wcd9xxx) {
+		kfree(resmgr);
+		pr_err("%s: Cannot get wcd9xx pointer\n", __func__);
+		return ERR_PTR(-EINVAL);
+	}
+
+	mutex_init(&resmgr->codec_bg_clk_lock);
+	mutex_init(&resmgr->master_bias_lock);
+	resmgr->master_bias_users = 0;
+	resmgr->clk_mclk_users = 0;
+	resmgr->clk_rco_users = 0;
+	resmgr->master_bias_users = 0;
+	resmgr->codec = codec;
+	resmgr->core_res = core_res;
+	resmgr->sido_input_src = SIDO_SOURCE_INTERNAL;
+	resmgr->codec_type = wcd9xxx->type;
+
+	return resmgr;
+}
+
+/*
+ * wcd_resmgr_remove: Clean-up wcd resource manager
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2
+ */
+void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr)
+{
+	mutex_destroy(&resmgr->master_bias_lock);
+	kfree(resmgr);
+}
+
+/*
+ * wcd_resmgr_post_init: post init call to assign codec handle
+ * @resmgr: handle to struct wcd9xxx_resmgr_v2 created during early init
+ * @resmgr_cb: codec callback function for resmgr
+ * @codec: handle to struct snd_soc_codec
+ */
+int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr,
+			 const struct wcd_resmgr_cb *resmgr_cb,
+			 struct snd_soc_codec *codec)
+{
+	if (!resmgr) {
+		pr_err("%s: resmgr not allocated\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!codec) {
+		pr_err("%s: Codec memory is NULL, nothing to post init\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	resmgr->codec = codec;
+	resmgr->resmgr_cb = resmgr_cb;
+
+	return 0;
+}
+MODULE_DESCRIPTION("wcd9xxx resmgr v2 module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr-v2.h b/sound/soc/codecs/wcd9xxx-resmgr-v2.h
new file mode 100644
index 0000000..f605a24
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr-v2.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_V2_H__
+#define __WCD9XXX_COMMON_V2_H__
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd_clock_type {
+	WCD_CLK_OFF,
+	WCD_CLK_RCO,
+	WCD_CLK_MCLK,
+};
+
+enum {
+	SIDO_SOURCE_INTERNAL,
+	SIDO_SOURCE_RCO_BG,
+};
+
+struct wcd_resmgr_cb {
+	int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool);
+};
+
+struct wcd9xxx_resmgr_v2 {
+	struct snd_soc_codec *codec;
+	struct wcd9xxx_core_resource *core_res;
+
+	int master_bias_users;
+	int clk_mclk_users;
+	int clk_rco_users;
+
+	struct mutex codec_bg_clk_lock;
+	struct mutex master_bias_lock;
+
+	enum codec_variant codec_type;
+	enum wcd_clock_type clk_type;
+
+	const struct wcd_resmgr_cb *resmgr_cb;
+	int sido_input_src;
+};
+
+#define WCD9XXX_V2_BG_CLK_LOCK(resmgr)			\
+{							\
+	struct wcd9xxx_resmgr_v2 *__resmgr = resmgr;	\
+	pr_debug("%s: Acquiring BG_CLK\n", __func__);	\
+	mutex_lock(&__resmgr->codec_bg_clk_lock);	\
+	pr_debug("%s: Acquiring BG_CLK done\n", __func__);	\
+}
+
+#define WCD9XXX_V2_BG_CLK_UNLOCK(resmgr)			\
+{							\
+	struct wcd9xxx_resmgr_v2 *__resmgr = resmgr;	\
+	pr_debug("%s: Releasing BG_CLK\n", __func__);	\
+	mutex_unlock(&__resmgr->codec_bg_clk_lock);	\
+}
+
+#define WCD9XXX_V2_BG_CLK_ASSERT_LOCKED(resmgr)		\
+{							\
+	WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \
+		  "%s: BG_CLK lock should have acquired\n", __func__); \
+}
+
+int wcd_resmgr_enable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr);
+int wcd_resmgr_disable_master_bias(struct wcd9xxx_resmgr_v2 *resmgr);
+struct wcd9xxx_resmgr_v2 *wcd_resmgr_init(
+		struct wcd9xxx_core_resource *core_res,
+		struct snd_soc_codec *codec);
+void wcd_resmgr_remove(struct wcd9xxx_resmgr_v2 *resmgr);
+int wcd_resmgr_post_init(struct wcd9xxx_resmgr_v2 *resmgr,
+			 const struct wcd_resmgr_cb *resmgr_cb,
+			 struct snd_soc_codec *codec);
+int wcd_resmgr_enable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+				enum wcd_clock_type type);
+int wcd_resmgr_disable_clk_block(struct wcd9xxx_resmgr_v2 *resmgr,
+				enum wcd_clock_type type);
+int wcd_resmgr_get_clk_type(struct wcd9xxx_resmgr_v2 *resmgr);
+void wcd_resmgr_post_ssr_v2(struct wcd9xxx_resmgr_v2 *resmgr);
+void wcd_resmgr_set_sido_input_src_locked(struct wcd9xxx_resmgr_v2 *resmgr,
+					  int sido_src);
+#endif
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.c b/sound/soc/codecs/wcd9xxx-resmgr.c
new file mode 100644
index 0000000..4b02652
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.c
@@ -0,0 +1,1099 @@
+/* Copyright (c) 2012-2014, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/ratelimit.h>
+#include <linux/debugfs.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+#include <uapi/linux/mfd/wcd9xxx/wcd9320_registers.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/mfd/wcd9xxx/pdata.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include "wcd9xxx-resmgr.h"
+
+static char wcd9xxx_event_string[][64] = {
+	"WCD9XXX_EVENT_INVALID",
+
+	"WCD9XXX_EVENT_PRE_RCO_ON",
+	"WCD9XXX_EVENT_POST_RCO_ON",
+	"WCD9XXX_EVENT_PRE_RCO_OFF",
+	"WCD9XXX_EVENT_POST_RCO_OFF",
+
+	"WCD9XXX_EVENT_PRE_MCLK_ON",
+	"WCD9XXX_EVENT_POST_MCLK_ON",
+	"WCD9XXX_EVENT_PRE_MCLK_OFF",
+	"WCD9XXX_EVENT_POST_MCLK_OFF",
+
+	"WCD9XXX_EVENT_PRE_BG_OFF",
+	"WCD9XXX_EVENT_POST_BG_OFF",
+	"WCD9XXX_EVENT_PRE_BG_AUDIO_ON",
+	"WCD9XXX_EVENT_POST_BG_AUDIO_ON",
+	"WCD9XXX_EVENT_PRE_BG_MBHC_ON",
+	"WCD9XXX_EVENT_POST_BG_MBHC_ON",
+
+	"WCD9XXX_EVENT_PRE_MICBIAS_1_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_1_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_2_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_2_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_3_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_3_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_4_OFF",
+	"WCD9XXX_EVENT_POST_MICBIAS_4_OFF",
+	"WCD9XXX_EVENT_PRE_MICBIAS_1_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_1_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_2_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_2_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_3_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_3_ON",
+	"WCD9XXX_EVENT_PRE_MICBIAS_4_ON",
+	"WCD9XXX_EVENT_POST_MICBIAS_4_ON",
+
+	"WCD9XXX_EVENT_PRE_CFILT_1_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_1_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_2_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_2_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_3_OFF",
+	"WCD9XXX_EVENT_POST_CFILT_3_OFF",
+	"WCD9XXX_EVENT_PRE_CFILT_1_ON",
+	"WCD9XXX_EVENT_POST_CFILT_1_ON",
+	"WCD9XXX_EVENT_PRE_CFILT_2_ON",
+	"WCD9XXX_EVENT_POST_CFILT_2_ON",
+	"WCD9XXX_EVENT_PRE_CFILT_3_ON",
+	"WCD9XXX_EVENT_POST_CFILT_3_ON",
+
+	"WCD9XXX_EVENT_PRE_HPHL_PA_ON",
+	"WCD9XXX_EVENT_POST_HPHL_PA_OFF",
+	"WCD9XXX_EVENT_PRE_HPHR_PA_ON",
+	"WCD9XXX_EVENT_POST_HPHR_PA_OFF",
+
+	"WCD9XXX_EVENT_POST_RESUME",
+
+	"WCD9XXX_EVENT_PRE_TX_3_ON",
+	"WCD9XXX_EVENT_POST_TX_3_OFF",
+
+	"WCD9XXX_EVENT_LAST",
+};
+
+#define WCD9XXX_RCO_CALIBRATION_RETRY_COUNT 5
+#define WCD9XXX_RCO_CALIBRATION_DELAY_US 5000
+#define WCD9XXX_USLEEP_RANGE_MARGIN_US 100
+#define WCD9XXX_RCO_CALIBRATION_DELAY_INC_US 1000
+
+struct wcd9xxx_resmgr_cond_entry {
+	unsigned short reg;
+	int shift;
+	bool invert;
+	enum wcd9xxx_resmgr_cond cond;
+	struct list_head list;
+};
+
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr
+						  *resmgr);
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type)
+{
+	return wcd9xxx_event_string[type];
+}
+
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+				  const enum wcd9xxx_notify_event e)
+{
+	pr_debug("%s: notifier call event %d\n", __func__, e);
+	blocking_notifier_call_chain(&resmgr->notifier, e, resmgr);
+}
+
+static void wcd9xxx_disable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+	/* Notify bg mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_OFF);
+	/* Disable bg */
+	snd_soc_update_bits(resmgr->codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+			    0x03, 0x00);
+	usleep_range(100, 110);
+	/* Notify bg mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_OFF);
+}
+
+/*
+ * BG enablement should always enable in slow mode.
+ * The fast mode doesn't need to be enabled as fast mode BG is to be driven
+ * by MBHC override.
+ */
+static void wcd9xxx_enable_bg(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	/* Enable BG in slow mode and precharge */
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x80);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x04, 0x04);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x01, 0x01);
+	usleep_range(1000, 1100);
+	snd_soc_update_bits(codec, WCD9XXX_A_BIAS_CENTRAL_BG_CTL, 0x80, 0x00);
+}
+
+static void wcd9xxx_enable_bg_audio(struct wcd9xxx_resmgr *resmgr)
+{
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_AUDIO_ON);
+	wcd9xxx_enable_bg(resmgr);
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_AUDIO_ON);
+}
+
+static void wcd9xxx_enable_bg_mbhc(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_BG_MBHC_ON);
+
+	/*
+	 * mclk should be off or clk buff source souldn't be VBG
+	 * Let's turn off mclk always
+	 */
+	WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+
+	wcd9xxx_enable_bg(resmgr);
+	/* Notify bandgap mode change */
+	wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_POST_BG_MBHC_ON);
+}
+
+static void wcd9xxx_disable_clock_block(struct wcd9xxx_resmgr *resmgr)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: enter\n", __func__);
+	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+
+	/* Notify */
+	if (resmgr->clk_type == WCD9XXX_CLK_RCO)
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_OFF);
+	else
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_PRE_MCLK_OFF);
+
+	switch (resmgr->codec_type) {
+	case WCD9XXX_CDC_TYPE_TOMTOM:
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+		usleep_range(50, 55);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x40);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x40, 0x00);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x01, 0x00);
+		break;
+	default:
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x04, 0x00);
+		usleep_range(50, 55);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02, 0x02);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1, 0x05, 0x00);
+		break;
+	}
+	usleep_range(50, 55);
+	/* Notify */
+	if (resmgr->clk_type == WCD9XXX_CLK_RCO) {
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_RCO_OFF);
+	} else {
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_MCLK_OFF);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+static void wcd9xxx_resmgr_cdc_specific_get_clk(struct wcd9xxx_resmgr *resmgr,
+						int clk_users)
+{
+	/* Caller of this function should have acquired
+	 *  BG_CLK lock
+	 */
+	WCD9XXX_BG_CLK_UNLOCK(resmgr);
+	if (clk_users) {
+		if (resmgr->resmgr_cb &&
+		    resmgr->resmgr_cb->cdc_rco_ctrl) {
+			while (clk_users--)
+				resmgr->resmgr_cb->cdc_rco_ctrl(resmgr->codec,
+								true);
+		}
+	}
+	/* Acquire BG_CLK lock before return */
+	WCD9XXX_BG_CLK_LOCK(resmgr);
+}
+
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr)
+{
+	int old_bg_audio_users, old_bg_mbhc_users;
+	int old_clk_rco_users, old_clk_mclk_users;
+
+	pr_debug("%s: enter\n", __func__);
+
+	WCD9XXX_BG_CLK_LOCK(resmgr);
+	old_bg_audio_users = resmgr->bg_audio_users;
+	old_bg_mbhc_users = resmgr->bg_mbhc_users;
+	old_clk_rco_users = resmgr->clk_rco_users;
+	old_clk_mclk_users = resmgr->clk_mclk_users;
+	resmgr->bg_audio_users = 0;
+	resmgr->bg_mbhc_users = 0;
+	resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+	resmgr->clk_rco_users = 0;
+	resmgr->clk_mclk_users = 0;
+	resmgr->clk_type = WCD9XXX_CLK_OFF;
+
+	if (old_bg_audio_users) {
+		while (old_bg_audio_users--)
+			wcd9xxx_resmgr_get_bandgap(resmgr,
+						  WCD9XXX_BANDGAP_AUDIO_MODE);
+	}
+
+	if (old_bg_mbhc_users) {
+		while (old_bg_mbhc_users--)
+			wcd9xxx_resmgr_get_bandgap(resmgr,
+						  WCD9XXX_BANDGAP_MBHC_MODE);
+	}
+
+	if (old_clk_mclk_users) {
+		while (old_clk_mclk_users--)
+			wcd9xxx_resmgr_get_clk_block(resmgr, WCD9XXX_CLK_MCLK);
+	}
+
+	if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+		wcd9xxx_resmgr_cdc_specific_get_clk(resmgr, old_clk_rco_users);
+	} else if (old_clk_rco_users) {
+		while (old_clk_rco_users--)
+			wcd9xxx_resmgr_get_clk_block(resmgr,
+					WCD9XXX_CLK_RCO);
+	}
+	WCD9XXX_BG_CLK_UNLOCK(resmgr);
+	pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_resmgr_get_bandgap : Vote for bandgap ref
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+				const enum wcd9xxx_bandgap_type choice)
+{
+	enum wcd9xxx_clock_type clock_save = WCD9XXX_CLK_OFF;
+
+	pr_debug("%s: enter, wants %d\n", __func__, choice);
+
+	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+	switch (choice) {
+	case WCD9XXX_BANDGAP_AUDIO_MODE:
+		resmgr->bg_audio_users++;
+		if (resmgr->bg_audio_users == 1 && resmgr->bg_mbhc_users) {
+			/*
+			 * Current bg is MBHC mode, about to switch to
+			 * audio mode.
+			 */
+			WARN_ON(resmgr->bandgap_type !=
+				WCD9XXX_BANDGAP_MBHC_MODE);
+
+			/* BG mode can be changed only with clock off */
+			if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
+				clock_save = wcd9xxx_save_clock(resmgr);
+			/* Swtich BG mode */
+			wcd9xxx_disable_bg(resmgr);
+			wcd9xxx_enable_bg_audio(resmgr);
+			/* restore clock */
+			if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
+				wcd9xxx_restore_clock(resmgr, clock_save);
+		} else if (resmgr->bg_audio_users == 1) {
+			/* currently off, just enable it */
+			WARN_ON(resmgr->bandgap_type != WCD9XXX_BANDGAP_OFF);
+			wcd9xxx_enable_bg_audio(resmgr);
+		}
+		resmgr->bandgap_type = WCD9XXX_BANDGAP_AUDIO_MODE;
+		break;
+	case WCD9XXX_BANDGAP_MBHC_MODE:
+		resmgr->bg_mbhc_users++;
+		if (resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE ||
+		    resmgr->bandgap_type == WCD9XXX_BANDGAP_AUDIO_MODE)
+			/* do nothing */
+			break;
+
+		/* bg mode can be changed only with clock off */
+		clock_save = wcd9xxx_save_clock(resmgr);
+		/* enable bg with MBHC mode */
+		wcd9xxx_enable_bg_mbhc(resmgr);
+		/* restore clock */
+		wcd9xxx_restore_clock(resmgr, clock_save);
+		/* save current mode */
+		resmgr->bandgap_type = WCD9XXX_BANDGAP_MBHC_MODE;
+		break;
+	default:
+		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+		 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+/*
+ * wcd9xxx_resmgr_put_bandgap : Unvote bandgap ref that has been voted
+ * choice : WCD9XXX_BANDGAP_AUDIO_MODE, WCD9XXX_BANDGAP_MBHC_MODE
+ */
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_bandgap_type choice)
+{
+	enum wcd9xxx_clock_type clock_save;
+
+	pr_debug("%s: enter choice %d\n", __func__, choice);
+
+	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+	switch (choice) {
+	case WCD9XXX_BANDGAP_AUDIO_MODE:
+		if (--resmgr->bg_audio_users == 0) {
+			if (resmgr->bg_mbhc_users) {
+				/* bg mode can be changed only with clock off */
+				clock_save = wcd9xxx_save_clock(resmgr);
+				/* switch to MBHC mode */
+				wcd9xxx_enable_bg_mbhc(resmgr);
+				/* restore clock */
+				wcd9xxx_restore_clock(resmgr, clock_save);
+				resmgr->bandgap_type =
+				    WCD9XXX_BANDGAP_MBHC_MODE;
+			} else {
+				/* turn off */
+				wcd9xxx_disable_bg(resmgr);
+				resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+			}
+		}
+		break;
+	case WCD9XXX_BANDGAP_MBHC_MODE:
+		WARN(resmgr->bandgap_type == WCD9XXX_BANDGAP_OFF,
+		     "Unexpected bandgap type %d\n", resmgr->bandgap_type);
+		if (--resmgr->bg_mbhc_users == 0 &&
+		    resmgr->bandgap_type == WCD9XXX_BANDGAP_MBHC_MODE) {
+			wcd9xxx_disable_bg(resmgr);
+			resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid bandgap settings\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: bg users audio %d, mbhc %d\n", __func__,
+		 resmgr->bg_audio_users, resmgr->bg_mbhc_users);
+}
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	if (enable) {
+		resmgr->rx_bias_count++;
+		if (resmgr->rx_bias_count == 1)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+					    0x80, 0x80);
+	} else {
+		resmgr->rx_bias_count--;
+		if (!resmgr->rx_bias_count)
+			snd_soc_update_bits(codec, WCD9XXX_A_RX_COM_BIAS,
+					    0x80, 0x00);
+	}
+}
+
+int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr, int enable)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: enable = %d\n", __func__, enable);
+	if (enable) {
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x10, 0);
+		/* bandgap mode to fast */
+		if (resmgr->pdata->mclk_rate == WCD9XXX_MCLK_CLK_12P288MHZ)
+			/* Set current value to 200nA for 12.288MHz clock */
+			snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x37);
+		else
+			snd_soc_write(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x17);
+
+		usleep_range(5, 10);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0x80);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0x80);
+		usleep_range(10, 20);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_TEST, 0x80, 0);
+		usleep_range(10000, 10100);
+
+		if (resmgr->pdata->mclk_rate != WCD9XXX_MCLK_CLK_12P288MHZ)
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+							0x08, 0x08);
+	} else {
+		snd_soc_update_bits(codec, WCD9XXX_A_BIAS_OSC_BG_CTL, 0x1, 0);
+		snd_soc_update_bits(codec, WCD9XXX_A_RC_OSC_FREQ, 0x80, 0);
+	}
+
+	return 0;
+}
+
+static void wcd9xxx_enable_clock_block(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_clock_config_mode config_mode)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+	unsigned long delay = WCD9XXX_RCO_CALIBRATION_DELAY_US;
+	int num_retry = 0;
+	unsigned int valr;
+	unsigned int valr1;
+	unsigned int valw[] = {0x01, 0x01, 0x10, 0x00};
+
+	pr_debug("%s: config_mode = %d\n", __func__, config_mode);
+
+	/* transit to RCO requires mclk off */
+	if (resmgr->codec_type != WCD9XXX_CDC_TYPE_TOMTOM)
+		WARN_ON(snd_soc_read(codec, WCD9XXX_A_CLK_BUFF_EN2) & (1 << 2));
+
+	if (config_mode == WCD9XXX_CFG_RCO) {
+		/* Notify */
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_RCO_ON);
+		/* enable RCO and switch to it */
+		wcd9xxx_resmgr_enable_config_mode(resmgr, 1);
+		snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2, 0x02);
+		usleep_range(1000, 1100);
+	} else if (config_mode == WCD9XXX_CFG_CAL_RCO) {
+		snd_soc_update_bits(codec, TOMTOM_A_BIAS_OSC_BG_CTL,
+				    0x01, 0x01);
+		/* 1ms sleep required after BG enabled */
+		usleep_range(1000, 1100);
+
+		if (resmgr->pdata->mclk_rate == WCD9XXX_MCLK_CLK_12P288MHZ) {
+			/*
+			 * Set RCO clock rate as 12.288MHz rate explicitly
+			 * as the Qfuse values are incorrect for this rate
+			 */
+			snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+					0x50, 0x50);
+		} else {
+			snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+					0x18, 0x10);
+			valr = snd_soc_read(codec,
+					TOMTOM_A_QFUSE_DATA_OUT0) & (0x04);
+			valr1 = snd_soc_read(codec,
+					TOMTOM_A_QFUSE_DATA_OUT1) & (0x08);
+			valr = (valr >> 1) | (valr1 >> 3);
+			snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x60,
+					valw[valr] << 5);
+		}
+		snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL, 0x80, 0x80);
+
+		do {
+			snd_soc_update_bits(codec,
+					    TOMTOM_A_RCO_CALIBRATION_CTRL1,
+					    0x80, 0x80);
+			snd_soc_update_bits(codec,
+					    TOMTOM_A_RCO_CALIBRATION_CTRL1,
+					    0x80, 0x00);
+			/* RCO calibration takes approx. 5ms */
+			usleep_range(delay, delay +
+					    WCD9XXX_USLEEP_RANGE_MARGIN_US);
+			if (!(snd_soc_read(codec,
+				TOMTOM_A_RCO_CALIBRATION_RESULT1) & 0x10))
+				break;
+			if (num_retry >= 3) {
+				delay = delay +
+					WCD9XXX_RCO_CALIBRATION_DELAY_INC_US;
+			}
+		} while (num_retry++ < WCD9XXX_RCO_CALIBRATION_RETRY_COUNT);
+	} else {
+		/* Notify */
+		wcd9xxx_resmgr_notifier_call(resmgr, WCD9XXX_EVENT_PRE_MCLK_ON);
+		/* switch to MCLK */
+
+		switch (resmgr->codec_type) {
+		case WCD9XXX_CDC_TYPE_TOMTOM:
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					    0x08, 0x00);
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					    0x40, 0x40);
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					    0x40, 0x00);
+			/* clk source to ext clk and clk buff ref to VBG */
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					    0x0C, 0x04);
+			break;
+		default:
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					    0x08, 0x00);
+			/* if RCO is enabled, switch from it */
+			if (snd_soc_read(codec, WCD9XXX_A_RC_OSC_FREQ) & 0x80) {
+				snd_soc_write(codec, WCD9XXX_A_CLK_BUFF_EN2,
+					      0x02);
+				wcd9xxx_resmgr_enable_config_mode(resmgr, 0);
+			}
+			/* clk source to ext clk and clk buff ref to VBG */
+			snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+					    0x0C, 0x04);
+			break;
+		}
+	}
+
+	if (config_mode != WCD9XXX_CFG_CAL_RCO) {
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN1,
+				    0x01, 0x01);
+		/*
+		 * sleep required by codec hardware to
+		 * enable clock buffer
+		 */
+		usleep_range(1000, 1200);
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2,
+				    0x02, 0x00);
+		/* on MCLK */
+		snd_soc_update_bits(codec, WCD9XXX_A_CLK_BUFF_EN2,
+				    0x04, 0x04);
+		snd_soc_update_bits(codec, WCD9XXX_A_CDC_CLK_MCLK_CTL,
+				    0x01, 0x01);
+	}
+	usleep_range(50, 55);
+
+	/* Notify */
+	if (config_mode == WCD9XXX_CFG_RCO)
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_RCO_ON);
+	else if (config_mode == WCD9XXX_CFG_MCLK)
+		wcd9xxx_resmgr_notifier_call(resmgr,
+					     WCD9XXX_EVENT_POST_MCLK_ON);
+}
+
+/*
+ * disable clock and return previous clock state
+ */
+static enum wcd9xxx_clock_type wcd9xxx_save_clock(struct wcd9xxx_resmgr *resmgr)
+{
+	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+	if (resmgr->clk_type != WCD9XXX_CLK_OFF)
+		wcd9xxx_disable_clock_block(resmgr);
+	return resmgr->clk_type != WCD9XXX_CLK_OFF;
+}
+
+static void wcd9xxx_restore_clock(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	if (type != WCD9XXX_CLK_OFF)
+		wcd9xxx_enable_clock_block(resmgr, type == WCD9XXX_CLK_RCO);
+}
+
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: current %d, requested %d, rco_users %d, mclk_users %d\n",
+		 __func__, resmgr->clk_type, type,
+		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
+	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+	switch (type) {
+	case WCD9XXX_CLK_RCO:
+		if (++resmgr->clk_rco_users == 1 &&
+		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
+			/* enable RCO and switch to it */
+			wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_RCO);
+			resmgr->clk_type = WCD9XXX_CLK_RCO;
+		} else if (resmgr->clk_rco_users == 1 &&
+			   resmgr->clk_type == WCD9XXX_CLK_MCLK &&
+			   resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+			/*
+			 * Enable RCO but do not switch CLK MUX to RCO
+			 * unless ext_clk_users is 1, which indicates
+			 * EXT CLK is enabled for RCO calibration
+			 */
+			wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_CAL_RCO);
+			if (resmgr->ext_clk_users == 1) {
+				/* Notify */
+				wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_PRE_RCO_ON);
+				/* CLK MUX to RCO */
+				if (resmgr->pdata->mclk_rate !=
+						WCD9XXX_MCLK_CLK_12P288MHZ)
+					snd_soc_update_bits(codec,
+						WCD9XXX_A_CLK_BUFF_EN1,
+						0x08, 0x08);
+				resmgr->clk_type = WCD9XXX_CLK_RCO;
+				wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_POST_RCO_ON);
+			}
+		}
+		break;
+	case WCD9XXX_CLK_MCLK:
+		if (++resmgr->clk_mclk_users == 1 &&
+		    resmgr->clk_type == WCD9XXX_CLK_OFF) {
+			/* switch to MCLK */
+			wcd9xxx_enable_clock_block(resmgr, WCD9XXX_CFG_MCLK);
+			resmgr->clk_type = WCD9XXX_CLK_MCLK;
+		} else if (resmgr->clk_mclk_users == 1 &&
+			   resmgr->clk_type == WCD9XXX_CLK_RCO) {
+			/* RCO to MCLK switch, with RCO still powered on */
+			if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+				wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_PRE_MCLK_ON);
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_BIAS_CENTRAL_BG_CTL,
+						0x40, 0x00);
+				/* Enable clock buffer */
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CLK_BUFF_EN1,
+						0x01, 0x01);
+				snd_soc_update_bits(codec,
+						WCD9XXX_A_CLK_BUFF_EN1,
+						0x08, 0x00);
+				wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_POST_MCLK_ON);
+			} else {
+				/* if RCO is enabled, switch from it */
+				WARN_ON(!(snd_soc_read(resmgr->codec,
+					WCD9XXX_A_RC_OSC_FREQ) & 0x80));
+				/* disable clock block */
+				wcd9xxx_disable_clock_block(resmgr);
+				/* switch to MCLK */
+				wcd9xxx_enable_clock_block(resmgr,
+							   WCD9XXX_CFG_MCLK);
+			}
+			resmgr->clk_type = WCD9XXX_CLK_MCLK;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+		       type);
+		break;
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type)
+{
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	pr_debug("%s: current %d, put %d\n", __func__, resmgr->clk_type, type);
+
+	WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr);
+	switch (type) {
+	case WCD9XXX_CLK_RCO:
+		if (--resmgr->clk_rco_users == 0 &&
+		    resmgr->clk_type == WCD9XXX_CLK_RCO) {
+			wcd9xxx_disable_clock_block(resmgr);
+			if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+				/* Powerdown RCO */
+				snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+						    0x80, 0x00);
+				snd_soc_update_bits(codec,
+						    TOMTOM_A_BIAS_OSC_BG_CTL,
+						    0x01, 0x00);
+			} else {
+				/* if RCO is enabled, switch from it */
+				if (snd_soc_read(resmgr->codec,
+						 WCD9XXX_A_RC_OSC_FREQ)
+						 & 0x80) {
+					snd_soc_write(resmgr->codec,
+						WCD9XXX_A_CLK_BUFF_EN2,
+						0x02);
+					wcd9xxx_resmgr_enable_config_mode(
+								resmgr,	0);
+				}
+			}
+			resmgr->clk_type = WCD9XXX_CLK_OFF;
+		}
+		break;
+	case WCD9XXX_CLK_MCLK:
+		if (--resmgr->clk_mclk_users == 0 &&
+		    resmgr->clk_rco_users == 0) {
+			wcd9xxx_disable_clock_block(resmgr);
+
+			if ((resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) &&
+			    (snd_soc_read(codec, TOMTOM_A_RCO_CTRL) & 0x80)) {
+				/* powerdown RCO*/
+				snd_soc_update_bits(codec, TOMTOM_A_RCO_CTRL,
+						    0x80, 0x00);
+				snd_soc_update_bits(codec,
+						    TOMTOM_A_BIAS_OSC_BG_CTL,
+						    0x01, 0x00);
+			}
+			resmgr->clk_type = WCD9XXX_CLK_OFF;
+		} else if (resmgr->clk_mclk_users == 0 &&
+			   resmgr->clk_rco_users) {
+			if (resmgr->codec_type == WCD9XXX_CDC_TYPE_TOMTOM) {
+				if (!(snd_soc_read(codec, TOMTOM_A_RCO_CTRL) &
+				      0x80)) {
+					dev_dbg(codec->dev, "%s: Enabling RCO\n",
+						__func__);
+					wcd9xxx_enable_clock_block(resmgr,
+							WCD9XXX_CFG_CAL_RCO);
+					snd_soc_update_bits(codec,
+							WCD9XXX_A_CLK_BUFF_EN1,
+							0x01, 0x00);
+				} else {
+					wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_PRE_MCLK_OFF);
+					snd_soc_update_bits(codec,
+							WCD9XXX_A_CLK_BUFF_EN1,
+							0x08, 0x08);
+					snd_soc_update_bits(codec,
+							WCD9XXX_A_CLK_BUFF_EN1,
+							0x01, 0x00);
+					wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_POST_MCLK_OFF);
+					/* CLK Mux changed to RCO, notify that
+					 * RCO is ON
+					 */
+					wcd9xxx_resmgr_notifier_call(resmgr,
+						WCD9XXX_EVENT_POST_RCO_ON);
+				}
+			} else {
+				/* disable clock */
+				wcd9xxx_disable_clock_block(resmgr);
+				/* switch to RCO */
+				wcd9xxx_enable_clock_block(resmgr,
+							WCD9XXX_CFG_RCO);
+			}
+			resmgr->clk_type = WCD9XXX_CLK_RCO;
+		}
+		break;
+	default:
+		pr_err("%s: Error, Invalid clock get request %d\n", __func__,
+		       type);
+		break;
+	}
+	WARN_ON(resmgr->clk_rco_users < 0);
+	WARN_ON(resmgr->clk_mclk_users < 0);
+
+	pr_debug("%s: new rco_users %d, mclk_users %d\n", __func__,
+		 resmgr->clk_rco_users, resmgr->clk_mclk_users);
+}
+
+/*
+ * wcd9xxx_resmgr_get_clk_type()
+ * Returns clk type that is currently enabled
+ */
+int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr)
+{
+	return resmgr->clk_type;
+}
+
+static void wcd9xxx_resmgr_update_cfilt_usage(struct wcd9xxx_resmgr *resmgr,
+					      enum wcd9xxx_cfilt_sel cfilt_sel,
+					      bool inc)
+{
+	u16 micb_cfilt_reg;
+	enum wcd9xxx_notify_event e_pre_on, e_post_off;
+	struct snd_soc_codec *codec = resmgr->codec;
+
+	switch (cfilt_sel) {
+	case WCD9XXX_CFILT1_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_1_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_1_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_1_OFF;
+		break;
+	case WCD9XXX_CFILT2_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_2_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_2_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_2_OFF;
+		break;
+	case WCD9XXX_CFILT3_SEL:
+		micb_cfilt_reg = WCD9XXX_A_MICB_CFILT_3_CTL;
+		e_pre_on = WCD9XXX_EVENT_PRE_CFILT_3_ON;
+		e_post_off = WCD9XXX_EVENT_POST_CFILT_3_OFF;
+		break;
+	default:
+		WARN(1, "Invalid CFILT selection %d\n", cfilt_sel);
+		return; /* should not happen */
+	}
+
+	if (inc) {
+		if ((resmgr->cfilt_users[cfilt_sel]++) == 0) {
+			/* Notify */
+			wcd9xxx_resmgr_notifier_call(resmgr, e_pre_on);
+			/* Enable CFILT */
+			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0x80);
+		}
+	} else {
+		/*
+		 * Check if count not zero, decrease
+		 * then check if zero, go ahead disable cfilter
+		 */
+		WARN(resmgr->cfilt_users[cfilt_sel] == 0,
+		     "Invalid CFILT use count 0\n");
+		if ((--resmgr->cfilt_users[cfilt_sel]) == 0) {
+			/* Disable CFILT */
+			snd_soc_update_bits(codec, micb_cfilt_reg, 0x80, 0);
+			/* Notify MBHC so MBHC can switch CFILT to fast mode */
+			wcd9xxx_resmgr_notifier_call(resmgr, e_post_off);
+		}
+	}
+}
+
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+	return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, true);
+}
+
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel)
+{
+	return wcd9xxx_resmgr_update_cfilt_usage(resmgr, cfilt_sel, false);
+}
+
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+			     unsigned int cfilt_mv)
+{
+	int rc = -EINVAL;
+	unsigned int ldoh_v = resmgr->micbias_pdata->ldoh_v;
+	unsigned int min_mv, max_mv;
+
+	switch (ldoh_v) {
+	case WCD9XXX_LDOH_1P95_V:
+		min_mv = 160;
+		max_mv = 1800;
+		break;
+	case WCD9XXX_LDOH_2P35_V:
+		min_mv = 200;
+		max_mv = 2200;
+		break;
+	case WCD9XXX_LDOH_2P75_V:
+		min_mv = 240;
+		max_mv = 2600;
+		break;
+	case WCD9XXX_LDOH_3P0_V:
+		min_mv = 260;
+		max_mv = 2875;
+		break;
+	default:
+		goto done;
+	}
+
+	if (cfilt_mv < min_mv || cfilt_mv > max_mv)
+		goto done;
+
+	for (rc = 4; rc <= 44; rc++) {
+		min_mv = max_mv * (rc) / 44;
+		if (min_mv >= cfilt_mv) {
+			rc -= 4;
+			break;
+		}
+	}
+done:
+	return rc;
+}
+
+static void wcd9xxx_resmgr_cond_trigger_cond(struct wcd9xxx_resmgr *resmgr,
+					     enum wcd9xxx_resmgr_cond cond)
+{
+	struct list_head *l;
+	struct wcd9xxx_resmgr_cond_entry *e;
+	bool set;
+
+	pr_debug("%s: enter\n", __func__);
+	/* update bit if cond isn't available or cond is set */
+	set = !test_bit(cond, &resmgr->cond_avail_flags) ||
+	      !!test_bit(cond, &resmgr->cond_flags);
+	list_for_each(l, &resmgr->update_bit_cond_h) {
+		e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
+		if (e->cond == cond)
+			snd_soc_update_bits(resmgr->codec, e->reg,
+					    1 << e->shift,
+					    (set ? !e->invert : e->invert)
+					    << e->shift);
+	}
+	pr_debug("%s: leave\n", __func__);
+}
+
+/*
+ * wcd9xxx_regmgr_cond_register : notify resmgr conditions in the condbits are
+ *				  available and notified.
+ * condbits : contains bitmask of enum wcd9xxx_resmgr_cond
+ */
+void wcd9xxx_regmgr_cond_register(struct wcd9xxx_resmgr *resmgr,
+				  unsigned long condbits)
+{
+	unsigned int cond;
+
+	for_each_set_bit(cond, &condbits, BITS_PER_BYTE * sizeof(condbits)) {
+		mutex_lock(&resmgr->update_bit_cond_lock);
+		WARN(test_bit(cond, &resmgr->cond_avail_flags),
+		     "Condition 0x%0x is already registered\n", cond);
+		set_bit(cond, &resmgr->cond_avail_flags);
+		wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+		mutex_unlock(&resmgr->update_bit_cond_lock);
+		pr_debug("%s: Condition 0x%x is registered\n", __func__, cond);
+	}
+}
+
+void wcd9xxx_regmgr_cond_deregister(struct wcd9xxx_resmgr *resmgr,
+				    unsigned long condbits)
+{
+	unsigned int cond;
+
+	for_each_set_bit(cond, &condbits, BITS_PER_BYTE * sizeof(condbits)) {
+		mutex_lock(&resmgr->update_bit_cond_lock);
+		WARN(!test_bit(cond, &resmgr->cond_avail_flags),
+		     "Condition 0x%0x isn't registered\n", cond);
+		clear_bit(cond, &resmgr->cond_avail_flags);
+		wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+		mutex_unlock(&resmgr->update_bit_cond_lock);
+		pr_debug("%s: Condition 0x%x is deregistered\n", __func__,
+			 cond);
+	}
+}
+
+void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr,
+				     enum wcd9xxx_resmgr_cond cond, bool set)
+{
+	mutex_lock(&resmgr->update_bit_cond_lock);
+	if ((set && !test_and_set_bit(cond, &resmgr->cond_flags)) ||
+	    (!set && test_and_clear_bit(cond, &resmgr->cond_flags))) {
+		pr_debug("%s: Resource %d condition changed to %s\n", __func__,
+			 cond, set ? "set" : "clear");
+		wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+	}
+	mutex_unlock(&resmgr->update_bit_cond_lock);
+}
+
+int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+					enum wcd9xxx_resmgr_cond cond,
+					unsigned short reg, int shift,
+					bool invert)
+{
+	struct wcd9xxx_resmgr_cond_entry *entry;
+
+	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->cond = cond;
+	entry->reg = reg;
+	entry->shift = shift;
+	entry->invert = invert;
+
+	mutex_lock(&resmgr->update_bit_cond_lock);
+	list_add_tail(&entry->list, &resmgr->update_bit_cond_h);
+
+	wcd9xxx_resmgr_cond_trigger_cond(resmgr, cond);
+	mutex_unlock(&resmgr->update_bit_cond_lock);
+
+	return 0;
+}
+
+/*
+ * wcd9xxx_resmgr_rm_cond_update_bits :
+ * Clear bit and remove from the conditional bit update list
+ */
+int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+				       enum wcd9xxx_resmgr_cond cond,
+				       unsigned short reg, int shift,
+				       bool invert)
+{
+	struct list_head *l, *next;
+	struct wcd9xxx_resmgr_cond_entry *e = NULL;
+
+	pr_debug("%s: enter\n", __func__);
+	mutex_lock(&resmgr->update_bit_cond_lock);
+	list_for_each_safe(l, next, &resmgr->update_bit_cond_h) {
+		e = list_entry(l, struct wcd9xxx_resmgr_cond_entry, list);
+		if (e->reg == reg && e->shift == shift && e->invert == invert) {
+			snd_soc_update_bits(resmgr->codec, e->reg,
+					    1 << e->shift,
+					    e->invert << e->shift);
+			list_del(&e->list);
+			mutex_unlock(&resmgr->update_bit_cond_lock);
+			kfree(e);
+			return 0;
+		}
+	}
+	mutex_unlock(&resmgr->update_bit_cond_lock);
+	pr_err("%s: Cannot find update bit entry reg 0x%x, shift %d\n",
+	       __func__, e ? e->reg : 0, e ? e->shift : 0);
+
+	return -EINVAL;
+}
+
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+				     struct notifier_block *nblock)
+{
+	return blocking_notifier_chain_register(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+				       struct notifier_block *nblock)
+{
+	return blocking_notifier_chain_unregister(&resmgr->notifier, nblock);
+}
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+			struct snd_soc_codec *codec,
+			struct wcd9xxx_core_resource *core_res,
+			struct wcd9xxx_pdata *pdata,
+			struct wcd9xxx_micbias_setting *micbias_pdata,
+			struct wcd9xxx_reg_address *reg_addr,
+			const struct wcd9xxx_resmgr_cb *resmgr_cb,
+			enum wcd9xxx_cdc_type cdc_type)
+{
+	WARN(ARRAY_SIZE(wcd9xxx_event_string) != WCD9XXX_EVENT_LAST + 1,
+	     "Event string table isn't up to date!, %zd != %d\n",
+	     ARRAY_SIZE(wcd9xxx_event_string), WCD9XXX_EVENT_LAST + 1);
+
+	resmgr->bandgap_type = WCD9XXX_BANDGAP_OFF;
+	resmgr->codec = codec;
+	resmgr->codec_type = cdc_type;
+	/* This gives access of core handle to lock/unlock suspend */
+	resmgr->core_res = core_res;
+	resmgr->pdata = pdata;
+	resmgr->micbias_pdata = micbias_pdata;
+	resmgr->reg_addr = reg_addr;
+	resmgr->resmgr_cb = resmgr_cb;
+
+	INIT_LIST_HEAD(&resmgr->update_bit_cond_h);
+
+	BLOCKING_INIT_NOTIFIER_HEAD(&resmgr->notifier);
+
+	mutex_init(&resmgr->codec_resource_lock);
+	mutex_init(&resmgr->codec_bg_clk_lock);
+	mutex_init(&resmgr->update_bit_cond_lock);
+
+	return 0;
+}
+
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_destroy(&resmgr->update_bit_cond_lock);
+	mutex_destroy(&resmgr->codec_bg_clk_lock);
+	mutex_destroy(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_lock(&resmgr->codec_resource_lock);
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr)
+{
+	mutex_unlock(&resmgr->codec_resource_lock);
+}
+
+MODULE_DESCRIPTION("wcd9xxx resmgr module");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd9xxx-resmgr.h b/sound/soc/codecs/wcd9xxx-resmgr.h
new file mode 100644
index 0000000..e35d616
--- /dev/null
+++ b/sound/soc/codecs/wcd9xxx-resmgr.h
@@ -0,0 +1,280 @@
+/* Copyright (c) 2012-2014, 2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_COMMON_H__
+#define __WCD9XXX_COMMON_H__
+
+#include <linux/notifier.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx_registers.h>
+
+enum wcd9xxx_bandgap_type {
+	WCD9XXX_BANDGAP_OFF,
+	WCD9XXX_BANDGAP_AUDIO_MODE,
+	WCD9XXX_BANDGAP_MBHC_MODE,
+};
+
+enum wcd9xxx_cdc_type {
+	WCD9XXX_CDC_TYPE_INVALID = 0,
+	WCD9XXX_CDC_TYPE_TAIKO,
+	WCD9XXX_CDC_TYPE_TAPAN,
+	WCD9XXX_CDC_TYPE_HELICON,
+	WCD9XXX_CDC_TYPE_TOMTOM,
+};
+
+enum wcd9xxx_clock_type {
+	WCD9XXX_CLK_OFF,
+	WCD9XXX_CLK_RCO,
+	WCD9XXX_CLK_MCLK,
+};
+
+enum wcd9xxx_clock_config_mode {
+	WCD9XXX_CFG_MCLK = 0,
+	WCD9XXX_CFG_RCO,
+	WCD9XXX_CFG_CAL_RCO,
+};
+
+enum wcd9xxx_cfilt_sel {
+	WCD9XXX_CFILT1_SEL,
+	WCD9XXX_CFILT2_SEL,
+	WCD9XXX_CFILT3_SEL,
+	WCD9XXX_NUM_OF_CFILT,
+};
+
+struct wcd9xxx_reg_address {
+	u16 micb_4_ctl;
+	u16 micb_4_int_rbias;
+	u16 micb_4_mbhc;
+};
+
+enum wcd9xxx_notify_event {
+	WCD9XXX_EVENT_INVALID,
+
+	WCD9XXX_EVENT_PRE_RCO_ON,
+	WCD9XXX_EVENT_POST_RCO_ON,
+	WCD9XXX_EVENT_PRE_RCO_OFF,
+	WCD9XXX_EVENT_POST_RCO_OFF,
+
+	WCD9XXX_EVENT_PRE_MCLK_ON,
+	WCD9XXX_EVENT_POST_MCLK_ON,
+	WCD9XXX_EVENT_PRE_MCLK_OFF,
+	WCD9XXX_EVENT_POST_MCLK_OFF,
+
+	WCD9XXX_EVENT_PRE_BG_OFF,
+	WCD9XXX_EVENT_POST_BG_OFF,
+	WCD9XXX_EVENT_PRE_BG_AUDIO_ON,
+	WCD9XXX_EVENT_POST_BG_AUDIO_ON,
+	WCD9XXX_EVENT_PRE_BG_MBHC_ON,
+	WCD9XXX_EVENT_POST_BG_MBHC_ON,
+
+	WCD9XXX_EVENT_PRE_MICBIAS_1_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_1_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_2_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_2_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_3_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_3_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_4_OFF,
+	WCD9XXX_EVENT_POST_MICBIAS_4_OFF,
+	WCD9XXX_EVENT_PRE_MICBIAS_1_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_1_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_2_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_2_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_3_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_3_ON,
+	WCD9XXX_EVENT_PRE_MICBIAS_4_ON,
+	WCD9XXX_EVENT_POST_MICBIAS_4_ON,
+
+	WCD9XXX_EVENT_PRE_CFILT_1_OFF,
+	WCD9XXX_EVENT_POST_CFILT_1_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_2_OFF,
+	WCD9XXX_EVENT_POST_CFILT_2_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_3_OFF,
+	WCD9XXX_EVENT_POST_CFILT_3_OFF,
+	WCD9XXX_EVENT_PRE_CFILT_1_ON,
+	WCD9XXX_EVENT_POST_CFILT_1_ON,
+	WCD9XXX_EVENT_PRE_CFILT_2_ON,
+	WCD9XXX_EVENT_POST_CFILT_2_ON,
+	WCD9XXX_EVENT_PRE_CFILT_3_ON,
+	WCD9XXX_EVENT_POST_CFILT_3_ON,
+
+	WCD9XXX_EVENT_PRE_HPHL_PA_ON,
+	WCD9XXX_EVENT_POST_HPHL_PA_OFF,
+	WCD9XXX_EVENT_PRE_HPHR_PA_ON,
+	WCD9XXX_EVENT_POST_HPHR_PA_OFF,
+
+	WCD9XXX_EVENT_POST_RESUME,
+
+	WCD9XXX_EVENT_PRE_TX_3_ON,
+	WCD9XXX_EVENT_POST_TX_3_OFF,
+
+	WCD9XXX_EVENT_LAST,
+};
+
+struct wcd9xxx_resmgr_cb {
+	int (*cdc_rco_ctrl)(struct snd_soc_codec *, bool);
+};
+
+struct wcd9xxx_resmgr {
+	struct snd_soc_codec *codec;
+	struct wcd9xxx_core_resource *core_res;
+
+	u32 rx_bias_count;
+
+	/*
+	 * bandgap_type, bg_audio_users and bg_mbhc_users have to be
+	 * referred/manipulated after acquiring codec_bg_clk_lock mutex
+	 */
+	enum wcd9xxx_bandgap_type bandgap_type;
+	u16 bg_audio_users;
+	u16 bg_mbhc_users;
+
+	/*
+	 * clk_type, clk_rco_users and clk_mclk_users have to be
+	 * referred/manipulated after acquiring codec_bg_clk_lock mutex
+	 */
+	enum wcd9xxx_clock_type clk_type;
+	u16 clk_rco_users;
+	u16 clk_mclk_users;
+	u16 ext_clk_users;
+
+	/* cfilt users per cfilts */
+	u16 cfilt_users[WCD9XXX_NUM_OF_CFILT];
+
+	struct wcd9xxx_reg_address *reg_addr;
+
+	struct wcd9xxx_pdata *pdata;
+
+	struct wcd9xxx_micbias_setting *micbias_pdata;
+
+	struct blocking_notifier_head notifier;
+	/* Notifier needs mbhc pointer with resmgr */
+	struct wcd9xxx_mbhc *mbhc;
+
+	unsigned long cond_flags;
+	unsigned long cond_avail_flags;
+	struct list_head update_bit_cond_h;
+	struct mutex update_bit_cond_lock;
+
+	/*
+	 * Currently, only used for mbhc purpose, to protect
+	 * concurrent execution of mbhc threaded irq handlers and
+	 * kill race between DAPM and MBHC. But can serve as a
+	 * general lock to protect codec resource
+	 */
+	struct mutex codec_resource_lock;
+	struct mutex codec_bg_clk_lock;
+
+	enum wcd9xxx_cdc_type codec_type;
+
+	const struct wcd9xxx_resmgr_cb *resmgr_cb;
+};
+
+int wcd9xxx_resmgr_init(struct wcd9xxx_resmgr *resmgr,
+			struct snd_soc_codec *codec,
+			struct wcd9xxx_core_resource *core_res,
+			struct wcd9xxx_pdata *pdata,
+			struct wcd9xxx_micbias_setting *micbias_pdata,
+			struct wcd9xxx_reg_address *reg_addr,
+			const struct wcd9xxx_resmgr_cb *resmgr_cb,
+			enum wcd9xxx_cdc_type cdc_type);
+void wcd9xxx_resmgr_deinit(struct wcd9xxx_resmgr *resmgr);
+
+int wcd9xxx_resmgr_enable_config_mode(struct wcd9xxx_resmgr *resmgr,
+				int enable);
+
+void wcd9xxx_resmgr_enable_rx_bias(struct wcd9xxx_resmgr *resmgr, u32 enable);
+void wcd9xxx_resmgr_get_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_put_clk_block(struct wcd9xxx_resmgr *resmgr,
+				  enum wcd9xxx_clock_type type);
+void wcd9xxx_resmgr_get_bandgap(struct wcd9xxx_resmgr *resmgr,
+				const enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_put_bandgap(struct wcd9xxx_resmgr *resmgr,
+				enum wcd9xxx_bandgap_type choice);
+void wcd9xxx_resmgr_cfilt_get(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel);
+void wcd9xxx_resmgr_cfilt_put(struct wcd9xxx_resmgr *resmgr,
+			      enum wcd9xxx_cfilt_sel cfilt_sel);
+int wcd9xxx_resmgr_get_clk_type(struct wcd9xxx_resmgr *resmgr);
+
+void wcd9xxx_resmgr_bcl_lock(struct wcd9xxx_resmgr *resmgr);
+void wcd9xxx_resmgr_post_ssr(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_LOCK(resmgr)			\
+{							\
+	pr_debug("%s: Acquiring BCL\n", __func__);	\
+	wcd9xxx_resmgr_bcl_lock(resmgr);			\
+	pr_debug("%s: Acquiring BCL done\n", __func__);	\
+}
+
+void wcd9xxx_resmgr_bcl_unlock(struct wcd9xxx_resmgr *resmgr);
+#define WCD9XXX_BCL_UNLOCK(resmgr)			\
+{							\
+	pr_debug("%s: Release BCL\n", __func__);	\
+	wcd9xxx_resmgr_bcl_unlock(resmgr);			\
+}
+
+#define WCD9XXX_BCL_ASSERT_LOCKED(resmgr)		\
+{							\
+	WARN_ONCE(!mutex_is_locked(&resmgr->codec_resource_lock), \
+		  "%s: BCL should have acquired\n", __func__); \
+}
+
+#define WCD9XXX_BG_CLK_LOCK(resmgr)			\
+{							\
+	struct wcd9xxx_resmgr *__resmgr = resmgr;	\
+	pr_debug("%s: Acquiring BG_CLK\n", __func__);	\
+	mutex_lock(&__resmgr->codec_bg_clk_lock);	\
+	pr_debug("%s: Acquiring BG_CLK done\n", __func__);	\
+}
+
+#define WCD9XXX_BG_CLK_UNLOCK(resmgr)			\
+{							\
+	struct wcd9xxx_resmgr *__resmgr = resmgr;	\
+	pr_debug("%s: Releasing BG_CLK\n", __func__);	\
+	mutex_unlock(&__resmgr->codec_bg_clk_lock);	\
+}
+
+#define WCD9XXX_BG_CLK_ASSERT_LOCKED(resmgr)		\
+{							\
+	WARN_ONCE(!mutex_is_locked(&resmgr->codec_bg_clk_lock), \
+		  "%s: BG_CLK lock should have acquired\n", __func__); \
+}
+
+const char *wcd9xxx_get_event_string(enum wcd9xxx_notify_event type);
+int wcd9xxx_resmgr_get_k_val(struct wcd9xxx_resmgr *resmgr,
+			     unsigned int cfilt_mv);
+int wcd9xxx_resmgr_register_notifier(struct wcd9xxx_resmgr *resmgr,
+				     struct notifier_block *nblock);
+int wcd9xxx_resmgr_unregister_notifier(struct wcd9xxx_resmgr *resmgr,
+				       struct notifier_block *nblock);
+void wcd9xxx_resmgr_notifier_call(struct wcd9xxx_resmgr *resmgr,
+				  const enum wcd9xxx_notify_event e);
+
+enum wcd9xxx_resmgr_cond {
+	WCD9XXX_COND_HPH = 0x01, /* Headphone */
+	WCD9XXX_COND_HPH_MIC = 0x02, /* Microphone on the headset */
+};
+void wcd9xxx_regmgr_cond_register(struct wcd9xxx_resmgr *resmgr,
+				  unsigned long condbits);
+void wcd9xxx_regmgr_cond_deregister(struct wcd9xxx_resmgr *resmgr,
+				    unsigned long condbits);
+int wcd9xxx_resmgr_rm_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+				       enum wcd9xxx_resmgr_cond cond,
+				       unsigned short reg, int shift,
+				       bool invert);
+int wcd9xxx_resmgr_add_cond_update_bits(struct wcd9xxx_resmgr *resmgr,
+					enum wcd9xxx_resmgr_cond cond,
+					unsigned short reg, int shift,
+					bool invert);
+void wcd9xxx_resmgr_cond_update_cond(struct wcd9xxx_resmgr *resmgr,
+				     enum wcd9xxx_resmgr_cond cond, bool set);
+
+#endif /* __WCD9XXX_COMMON_H__ */
diff --git a/sound/soc/codecs/wcd_cmi_api.h b/sound/soc/codecs/wcd_cmi_api.h
new file mode 100644
index 0000000..39be641
--- /dev/null
+++ b/sound/soc/codecs/wcd_cmi_api.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CMI_API__
+#define __CMI_API__
+
+enum cmi_api_result {
+	CMI_API_FAILED = 1,
+	CMI_API_BUSY,
+	CMI_API_NO_MEMORY,
+	CMI_API_NOT_READY,
+};
+
+enum cmi_api_event {
+	CMI_API_MSG = 1,
+	CMI_API_OFFLINE,
+	CMI_API_ONLINE,
+	CMI_API_DEINITIALIZED,
+};
+
+struct cmi_api_notification {
+	enum cmi_api_event event;
+	enum cmi_api_result result;
+	void *message;
+};
+
+void *cmi_register(
+	void notification_callback
+		(const struct cmi_api_notification *parameter),
+	u32 service);
+enum cmi_api_result cmi_deregister(void *reg_handle);
+enum cmi_api_result cmi_send_msg(void *message);
+
+#endif /*__CMI_API__*/
diff --git a/sound/soc/codecs/wcd_cpe_core.c b/sound/soc/codecs/wcd_cpe_core.c
new file mode 100644
index 0000000..c98fdc9
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_core.c
@@ -0,0 +1,4600 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/elf.h>
+#include <linux/wait.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/pm_qos.h>
+#include <linux/dma-mapping.h>
+#include <sound/soc.h>
+#include <sound/info.h>
+#include <sound/lsm_params.h>
+#include <sound/cpe_core.h>
+#include <sound/cpe_cmi.h>
+#include <sound/cpe_err.h>
+#include <soc/qcom/pm.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/mfd/wcd9xxx/wcd9xxx-irq.h>
+#include <sound/audio_cal_utils.h>
+#include "wcd_cpe_core.h"
+#include "wcd_cpe_services.h"
+#include "wcd_cmi_api.h"
+
+#define CMI_CMD_TIMEOUT (10 * HZ)
+#define WCD_CPE_LSM_MAX_SESSIONS 2
+#define WCD_CPE_AFE_MAX_PORTS 4
+#define AFE_SVC_EXPLICIT_PORT_START 1
+#define WCD_CPE_EC_PP_BUF_SIZE	480 /* 5 msec buffer */
+
+#define ELF_FLAG_EXECUTE (1 << 0)
+#define ELF_FLAG_WRITE (1 << 1)
+#define ELF_FLAG_READ (1 << 2)
+
+#define ELF_FLAG_RW (ELF_FLAG_READ | ELF_FLAG_WRITE)
+
+#define WCD_CPE_GRAB_LOCK(lock, name)		\
+{						\
+	pr_debug("%s: %s lock acquire\n",	\
+		 __func__, name);		\
+	mutex_lock(lock);			\
+}
+
+#define WCD_CPE_REL_LOCK(lock, name)		\
+{						\
+	pr_debug("%s: %s lock release\n",	\
+		 __func__, name);		\
+	mutex_unlock(lock);			\
+}
+
+#define WCD_CPE_STATE_MAX_LEN 11
+#define CPE_OFFLINE_WAIT_TIMEOUT (2 * HZ)
+#define CPE_READY_WAIT_TIMEOUT (3 * HZ)
+#define WCD_CPE_SYSFS_DIR_MAX_LENGTH 32
+
+#define CPE_ERR_IRQ_CB(core) \
+	(core->cpe_cdc_cb->cpe_err_irq_control)
+
+/*
+ * AFE output buffer size is always
+ * (sample_rate * number of bytes per sample/2*1000)
+ */
+#define AFE_OUT_BUF_SIZE(bit_width, sample_rate) \
+	(((sample_rate) * (bit_width / BITS_PER_BYTE))/(2*1000))
+
+enum afe_port_state {
+	AFE_PORT_STATE_DEINIT = 0,
+	AFE_PORT_STATE_INIT,
+	AFE_PORT_STATE_CONFIG,
+	AFE_PORT_STATE_STARTED,
+	AFE_PORT_STATE_SUSPENDED,
+};
+
+struct wcd_cmi_afe_port_data {
+	u8 port_id;
+	struct mutex afe_lock;
+	struct completion afe_cmd_complete;
+	enum afe_port_state port_state;
+	u8 cmd_result;
+	u32 mem_handle;
+};
+
+struct cpe_lsm_ids {
+	u32 module_id;
+	u32 param_id;
+};
+
+static struct wcd_cpe_core *core_d;
+static struct cpe_lsm_session
+		*lsm_sessions[WCD_CPE_LSM_MAX_SESSIONS + 1];
+struct wcd_cpe_core * (*wcd_get_cpe_core)(struct snd_soc_codec *);
+static struct wcd_cmi_afe_port_data afe_ports[WCD_CPE_AFE_MAX_PORTS + 1];
+static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param);
+static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core);
+static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core);
+static ssize_t cpe_ftm_test_trigger(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos);
+static u32 ramdump_enable;
+static u32 cpe_ftm_test_status;
+static const struct file_operations cpe_ftm_test_trigger_fops = {
+	.open = simple_open,
+	.write = cpe_ftm_test_trigger,
+};
+
+static int wcd_cpe_afe_svc_cmd_mode(void *core_handle,
+				    u8 mode);
+struct wcd_cpe_attribute {
+	struct attribute attr;
+	ssize_t (*show)(struct wcd_cpe_core *core, char *buf);
+	ssize_t (*store)(struct wcd_cpe_core *core, const char *buf,
+			 ssize_t count);
+};
+
+#define WCD_CPE_ATTR(_name, _mode, _show, _store) \
+static struct wcd_cpe_attribute cpe_attr_##_name = { \
+	.attr = {.name = __stringify(_name), .mode = _mode}, \
+	.show = _show, \
+	.store = _store, \
+}
+
+#define to_wcd_cpe_attr(a) \
+	container_of((a), struct wcd_cpe_attribute, attr)
+
+#define kobj_to_cpe_core(kobj) \
+	container_of((kobj), struct wcd_cpe_core, cpe_kobj)
+
+/* wcd_cpe_lsm_session_active: check if any session is active
+ * return true if any session is active.
+ */
+static bool wcd_cpe_lsm_session_active(void)
+{
+	int index = 1;
+	bool lsm_active = false;
+
+	/* session starts from index 1 */
+	for (; index <= WCD_CPE_LSM_MAX_SESSIONS; index++) {
+		if (lsm_sessions[index] != NULL) {
+			lsm_active = true;
+			break;
+		} else {
+			lsm_active = false;
+		}
+	}
+	return lsm_active;
+}
+
+static int wcd_cpe_get_sfr_dump(struct wcd_cpe_core *core)
+{
+	struct cpe_svc_mem_segment dump_seg;
+	int rc;
+	u8 *sfr_dump;
+
+	sfr_dump = kzalloc(core->sfr_buf_size, GFP_KERNEL);
+	if (!sfr_dump)
+		goto done;
+
+	dump_seg.type = CPE_SVC_DATA_MEM;
+	dump_seg.cpe_addr = core->sfr_buf_addr;
+	dump_seg.size = core->sfr_buf_size;
+	dump_seg.data = sfr_dump;
+	dev_dbg(core->dev,
+		"%s: reading SFR from CPE, size = %zu\n",
+		__func__, core->sfr_buf_size);
+
+	rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg);
+	if (rc < 0) {
+		dev_err(core->dev,
+			"%s: Failed to read cpe sfr_dump, err = %d\n",
+			__func__, rc);
+		goto free_sfr_dump;
+	}
+
+	dev_info(core->dev,
+		 "%s: cpe_sfr = %s\n", __func__, sfr_dump);
+
+free_sfr_dump:
+	kfree(sfr_dump);
+done:
+	/* Even if SFR dump failed, do not return error */
+	return 0;
+}
+
+static int wcd_cpe_collect_ramdump(struct wcd_cpe_core *core)
+{
+	struct cpe_svc_mem_segment dump_seg;
+	int rc;
+
+	if (!core->cpe_ramdump_dev || !core->cpe_dump_v_addr ||
+	    core->hw_info.dram_size == 0) {
+		dev_err(core->dev,
+			"%s: Ramdump devices not set up, size = %zu\n",
+			__func__, core->hw_info.dram_size);
+		return -EINVAL;
+	}
+
+	dump_seg.type = CPE_SVC_DATA_MEM;
+	dump_seg.cpe_addr = core->hw_info.dram_offset;
+	dump_seg.size = core->hw_info.dram_size;
+	dump_seg.data = core->cpe_dump_v_addr;
+
+	dev_dbg(core->dev,
+		"%s: Reading ramdump from CPE\n",
+		__func__);
+
+	rc = cpe_svc_ramdump(core->cpe_handle, &dump_seg);
+	if (rc < 0) {
+		dev_err(core->dev,
+			"%s: Failed to read CPE ramdump, err = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	dev_dbg(core->dev,
+		"%s: completed reading ramdump from CPE\n",
+		__func__);
+
+	core->cpe_ramdump_seg.address = (unsigned long) core->cpe_dump_addr;
+	core->cpe_ramdump_seg.size = core->hw_info.dram_size;
+	core->cpe_ramdump_seg.v_address = core->cpe_dump_v_addr;
+
+	rc = do_ramdump(core->cpe_ramdump_dev,
+			&core->cpe_ramdump_seg, 1);
+	if (rc)
+		dev_err(core->dev,
+			"%s: fail to dump cpe ram to device, err = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+/* wcd_cpe_is_valid_elf_hdr: check if the ELF header is valid
+ * @core: handle to wcd_cpe_core
+ * @fw_size: size of firmware from request_firmware
+ * @ehdr: the elf header to be checked for
+ * return true if all checks pass, true if any elf check fails
+ */
+static bool wcd_cpe_is_valid_elf_hdr(struct wcd_cpe_core *core, size_t fw_size,
+				     const struct elf32_hdr *ehdr)
+{
+	if (fw_size < sizeof(*ehdr)) {
+		dev_err(core->dev, "%s:Firmware too small\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+		dev_err(core->dev, "%s: Not an ELF file\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN) {
+		dev_err(core->dev, "%s: Not a executable image\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (ehdr->e_phnum == 0) {
+		dev_err(core->dev, "%s: no segments to load\n", __func__);
+		goto elf_check_fail;
+	}
+
+	if (sizeof(struct elf32_phdr) * ehdr->e_phnum +
+	    sizeof(struct elf32_hdr) > fw_size) {
+		dev_err(core->dev, "%s: Too small MDT file\n", __func__);
+		goto elf_check_fail;
+	}
+
+	return true;
+
+elf_check_fail:
+	return false;
+}
+
+/*
+ * wcd_cpe_load_each_segment: download segment to CPE
+ * @core: handle to struct wcd_cpe_core
+ * @file_idx: index of split firmware image file name
+ * @phdr: program header from metadata
+ */
+static int wcd_cpe_load_each_segment(struct wcd_cpe_core *core,
+			  int file_idx, const struct elf32_phdr *phdr)
+{
+	const struct firmware *split_fw;
+	char split_fname[32];
+	int ret = 0;
+	struct cpe_svc_mem_segment *segment;
+
+	if (!core || !phdr) {
+		pr_err("%s: Invalid params\n", __func__);
+		return -EINVAL;
+	}
+
+	/* file size can be 0 for bss segments */
+	if (phdr->p_filesz == 0 || phdr->p_memsz == 0)
+		return 0;
+
+	segment = kzalloc(sizeof(struct cpe_svc_mem_segment), GFP_KERNEL);
+	if (!segment)
+		return -ENOMEM;
+
+	snprintf(split_fname, sizeof(split_fname), "%s.b%02d",
+		 core->fname, file_idx);
+
+	ret = request_firmware(&split_fw, split_fname, core->dev);
+	if (ret) {
+		dev_err(core->dev, "firmware %s not found\n",
+			split_fname);
+		ret = -EIO;
+		goto fw_req_fail;
+	}
+
+	if (phdr->p_flags & ELF_FLAG_EXECUTE)
+		segment->type = CPE_SVC_INSTRUCTION_MEM;
+	else if (phdr->p_flags & ELF_FLAG_RW)
+		segment->type = CPE_SVC_DATA_MEM;
+	else {
+		dev_err(core->dev, "%s invalid flags 0x%x\n",
+			__func__, phdr->p_flags);
+		goto done;
+	}
+
+	segment->cpe_addr = phdr->p_paddr;
+	segment->size = phdr->p_filesz;
+	segment->data = (u8 *) split_fw->data;
+
+	dev_dbg(core->dev,
+		"%s: cpe segment type %s read from firmware\n", __func__,
+		(segment->type == CPE_SVC_INSTRUCTION_MEM) ?
+			"INSTRUCTION" : "DATA");
+
+	ret = cpe_svc_download_segment(core->cpe_handle, segment);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: Failed to download %s, error = %d\n",
+			__func__, split_fname, ret);
+		goto done;
+	}
+
+done:
+	release_firmware(split_fw);
+
+fw_req_fail:
+	kfree(segment);
+	return ret;
+}
+
+/*
+ * wcd_cpe_enable_cpe_clks: enable the clocks for CPE
+ * @core: handle to wcd_cpe_core
+ * @enable: flag indicating whether to enable/disable cpe clocks
+ */
+static int wcd_cpe_enable_cpe_clks(struct wcd_cpe_core *core, bool enable)
+{
+	int ret, ret1;
+
+	if (!core || !core->cpe_cdc_cb ||
+	    !core->cpe_cdc_cb->cpe_clk_en) {
+		pr_err("%s: invalid handle\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	ret = core->cpe_cdc_cb->cdc_clk_en(core->codec, enable);
+	if (ret) {
+		dev_err(core->dev, "%s: Failed to enable RCO\n",
+			__func__);
+		return ret;
+	}
+
+	if (!enable && core->cpe_clk_ref > 0)
+		core->cpe_clk_ref--;
+
+	/*
+	 * CPE clk will be enabled at the first time
+	 * and be disabled at the last time.
+	 */
+	if (core->cpe_clk_ref == 0) {
+		ret = core->cpe_cdc_cb->cpe_clk_en(core->codec, enable);
+		if (ret) {
+			dev_err(core->dev,
+				"%s: cpe_clk_en() failed, err = %d\n",
+				__func__, ret);
+			goto cpe_clk_fail;
+		}
+	}
+
+	if (enable)
+		core->cpe_clk_ref++;
+
+	return 0;
+
+cpe_clk_fail:
+	/* Release the codec clk if CPE clk enable failed */
+	if (enable) {
+		ret1 = core->cpe_cdc_cb->cdc_clk_en(core->codec, !enable);
+		if (ret1)
+			dev_err(core->dev,
+				"%s: Fail to release codec clk, err = %d\n",
+				__func__, ret1);
+	}
+
+	return ret;
+}
+
+/*
+ * wcd_cpe_bus_vote_max_bw: Function to vote for max bandwidth on codec bus
+ * @core: handle to core for cpe
+ * @vote: flag to indicate enable/disable of vote
+ *
+ * This function will try to use the codec provided callback to
+ * vote/unvote for the max bandwidth of the bus that is used by
+ * the codec for register reads/writes.
+ */
+static int wcd_cpe_bus_vote_max_bw(struct wcd_cpe_core *core,
+		bool vote)
+{
+	if (!core || !core->cpe_cdc_cb) {
+		pr_err("%s: Invalid handle to %s\n",
+			__func__,
+			(!core) ? "core" : "codec callbacks");
+		return -EINVAL;
+	}
+
+	if (core->cpe_cdc_cb->bus_vote_bw) {
+		dev_dbg(core->dev, "%s: %s cdc bus max bandwidth\n",
+			 __func__, vote ? "Vote" : "Unvote");
+		core->cpe_cdc_cb->bus_vote_bw(core->codec, vote);
+	}
+
+	return 0;
+}
+
+/*
+ * wcd_cpe_load_fw: Function to load the fw image
+ * @core: cpe core pointer
+ * @load_type: indicates whether to load to data section
+ *	       or the instruction section
+ *
+ * Parse the mdt file to look for program headers, load each
+ * split file corresponding to the program headers.
+ */
+static int wcd_cpe_load_fw(struct wcd_cpe_core *core,
+	unsigned int load_type)
+{
+
+	int ret, phdr_idx;
+	struct snd_soc_codec *codec = NULL;
+	struct wcd9xxx *wcd9xxx = NULL;
+	const struct elf32_hdr *ehdr;
+	const struct elf32_phdr *phdr;
+	const struct firmware *fw;
+	const u8 *elf_ptr;
+	char mdt_name[64];
+	bool img_dload_fail = false;
+	bool load_segment;
+
+	if (!core || !core->cpe_handle) {
+		pr_err("%s: Error CPE core %pK\n", __func__,
+		       core);
+		return -EINVAL;
+	}
+	codec = core->codec;
+	wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	snprintf(mdt_name, sizeof(mdt_name), "%s.mdt", core->fname);
+	ret = request_firmware(&fw, mdt_name, core->dev);
+	if (ret < 0) {
+		dev_err(core->dev, "firmware %s not found\n", mdt_name);
+		return ret;
+	}
+
+	ehdr = (struct elf32_hdr *) fw->data;
+	if (!wcd_cpe_is_valid_elf_hdr(core, fw->size, ehdr)) {
+		dev_err(core->dev, "%s: fw mdt %s is invalid\n",
+			__func__, mdt_name);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	elf_ptr = fw->data + sizeof(*ehdr);
+
+	if (load_type == ELF_FLAG_EXECUTE) {
+		/* Reset CPE first */
+		ret = cpe_svc_reset(core->cpe_handle);
+		if (ret < 0) {
+			dev_err(core->dev,
+				"%s: Failed to reset CPE with error %d\n",
+				__func__, ret);
+			goto done;
+		}
+	}
+
+	dev_dbg(core->dev, "%s: start image dload, name = %s, load_type = 0x%x\n",
+		__func__, core->fname, load_type);
+
+	wcd_cpe_bus_vote_max_bw(core, true);
+
+	/* parse every program header and request corresponding firmware */
+	for (phdr_idx = 0; phdr_idx < ehdr->e_phnum; phdr_idx++) {
+		phdr = (struct elf32_phdr *)elf_ptr;
+		load_segment = false;
+
+		dev_dbg(core->dev,
+			"index = %d, vaddr = 0x%x, paddr = 0x%x, filesz = 0x%x, memsz = 0x%x, flags = 0x%x\n"
+			, phdr_idx, phdr->p_vaddr, phdr->p_paddr,
+			phdr->p_filesz, phdr->p_memsz, phdr->p_flags);
+
+		switch (load_type) {
+		case ELF_FLAG_EXECUTE:
+			if (phdr->p_flags & load_type)
+				load_segment = true;
+			break;
+		case ELF_FLAG_RW:
+			if (!(phdr->p_flags & ELF_FLAG_EXECUTE) &&
+			    (phdr->p_flags & load_type))
+				load_segment = true;
+			break;
+		default:
+			pr_err("%s: Invalid load_type 0x%x\n",
+				__func__, load_type);
+			ret = -EINVAL;
+			goto rel_bus_vote;
+		}
+
+		if (load_segment) {
+			ret = wcd_cpe_load_each_segment(core,
+						phdr_idx, phdr);
+			if (ret < 0) {
+				dev_err(core->dev,
+					"Failed to load segment %d, aborting img dload\n",
+					phdr_idx);
+				img_dload_fail = true;
+				goto rel_bus_vote;
+			}
+		} else {
+			dev_dbg(core->dev,
+				"%s: skipped segment with index %d\n",
+				__func__, phdr_idx);
+		}
+
+		elf_ptr = elf_ptr + sizeof(*phdr);
+	}
+	if (load_type == ELF_FLAG_EXECUTE)
+		core->ssr_type = WCD_CPE_IMEM_DOWNLOADED;
+
+rel_bus_vote:
+	wcd_cpe_bus_vote_max_bw(core, false);
+
+done:
+	release_firmware(fw);
+	return ret;
+}
+
+/*
+ * wcd_cpe_change_online_state - mark cpe online/offline state
+ * @core: core session to mark
+ * @online: whether online of offline
+ *
+ */
+static void wcd_cpe_change_online_state(struct wcd_cpe_core *core,
+			int online)
+{
+	struct wcd_cpe_ssr_entry *ssr_entry = NULL;
+	unsigned long ret;
+
+	if (!core) {
+		pr_err("%s: Invalid core handle\n",
+			__func__);
+		return;
+	}
+
+	ssr_entry = &core->ssr_entry;
+	WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+	ssr_entry->offline = !online;
+
+	/* Make sure write to offline state is completed. */
+	wmb();
+	ret = xchg(&ssr_entry->offline_change, 1);
+	wake_up_interruptible(&ssr_entry->offline_poll_wait);
+	WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+	pr_debug("%s: change state 0x%x offline_change 0x%x\n"
+		 " core->offline 0x%x, ret = %ld\n",
+		 __func__, online,
+		 ssr_entry->offline_change,
+		 core->ssr_entry.offline, ret);
+}
+
+/*
+ * wcd_cpe_load_fw_image: work function to load the fw image
+ * @work: work that is scheduled to perform the image loading
+ *
+ * Parse the mdt file to look for program headers, load each
+ * split file corresponding to the program headers.
+ */
+static void wcd_cpe_load_fw_image(struct work_struct *work)
+{
+	struct wcd_cpe_core *core;
+	int ret = 0;
+
+	core = container_of(work, struct wcd_cpe_core, load_fw_work);
+	ret = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+	if (!ret)
+		wcd_cpe_change_online_state(core, 1);
+	else
+		pr_err("%s: failed to load instruction section, err = %d\n",
+			__func__, ret);
+}
+
+/*
+ * wcd_cpe_get_core_handle: get the handle to wcd_cpe_core
+ * @codec: codec from which this handle is to be obtained
+ * Codec driver should provide a callback function to obtain
+ * handle to wcd_cpe_core during initialization of wcd_cpe_core
+ */
+void *wcd_cpe_get_core_handle(
+	struct snd_soc_codec *codec)
+{
+	struct wcd_cpe_core *core = NULL;
+
+	if (!codec) {
+		pr_err("%s: Invalid codec handle\n",
+			__func__);
+		goto done;
+	}
+
+	if (!wcd_get_cpe_core) {
+		dev_err(codec->dev,
+			"%s: codec callback not available\n",
+			__func__);
+		goto done;
+	}
+
+	core = wcd_get_cpe_core(codec);
+
+	if (!core)
+		dev_err(codec->dev,
+			"%s: handle to core not available\n",
+			__func__);
+done:
+	return core;
+}
+
+/*
+ * svass_engine_irq: threaded interrupt handler for svass engine irq
+ * @irq: interrupt number
+ * @data: data pointer passed during irq registration
+ */
+static irqreturn_t svass_engine_irq(int irq, void *data)
+{
+	struct wcd_cpe_core *core = data;
+	int ret = 0;
+
+	if (!core) {
+		pr_err("%s: Invalid data for interrupt handler\n",
+			__func__);
+		goto done;
+	}
+
+	ret = cpe_svc_process_irq(core->cpe_handle, CPE_IRQ_OUTBOX_IRQ);
+	if (ret < 0)
+		dev_err(core->dev,
+			"%s: Error processing irq from cpe_Services\n",
+			__func__);
+done:
+	return IRQ_HANDLED;
+}
+
+/*
+ * wcd_cpe_state_read - update read status in procfs
+ * @entry: snd_info_entry
+ * @buf: buffer where the read status is updated.
+ *
+ */
+static ssize_t wcd_cpe_state_read(struct snd_info_entry *entry,
+			       void *file_private_data, struct file *file,
+			       char __user *buf, size_t count, loff_t pos)
+{
+	int len = 0;
+	char buffer[WCD_CPE_STATE_MAX_LEN];
+	struct wcd_cpe_core *core = NULL;
+	struct wcd_cpe_ssr_entry *ssr_entry = NULL;
+
+	core = (struct wcd_cpe_core *) entry->private_data;
+	if (!core) {
+		pr_err("%s: CPE core NULL\n", __func__);
+		return -EINVAL;
+	}
+	ssr_entry = &core->ssr_entry;
+
+	/* Make sure read from ssr_entry is completed. */
+	rmb();
+	dev_dbg(core->dev,
+		"%s: Offline 0x%x\n", __func__,
+		 ssr_entry->offline);
+
+	WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+	len = snprintf(buffer, sizeof(buffer), "%s\n",
+		       ssr_entry->offline ? "OFFLINE" : "ONLINE");
+	WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+
+	return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+/*
+ * wcd_cpe_state_poll - polls for change state
+ * @entry: snd_info_entry
+ * @wait: wait for duration for poll wait
+ *
+ */
+static unsigned int wcd_cpe_state_poll(struct snd_info_entry *entry,
+					void *private_data, struct file *file,
+					poll_table *wait)
+{
+	struct wcd_cpe_core *core = NULL;
+	struct wcd_cpe_ssr_entry *ssr_entry = NULL;
+	int ret = 0;
+
+	core = (struct wcd_cpe_core *) entry->private_data;
+	if (!core) {
+		pr_err("%s: CPE core NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ssr_entry = &core->ssr_entry;
+
+	dev_dbg(core->dev, "%s: CPE Poll wait\n",
+	       __func__);
+	poll_wait(file, &ssr_entry->offline_poll_wait, wait);
+	dev_dbg(core->dev, "%s: Wake-up Poll wait\n",
+	       __func__);
+	WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+
+	if (xchg(&ssr_entry->offline_change, 0))
+		ret = POLLIN | POLLPRI | POLLRDNORM;
+
+	WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+
+	dev_dbg(core->dev, "%s: ret (%d) from poll_wait\n",
+		__func__, ret);
+	return ret;
+}
+
+/*
+ * wcd_cpe_is_online_state - return true if card is online state
+ * @core: core offline to query
+ */
+static bool wcd_cpe_is_online_state(void *core_handle)
+{
+	struct wcd_cpe_core *core = core_handle;
+
+	if (core_handle) {
+		return !core->ssr_entry.offline;
+	} else {
+		pr_err("%s: Core handle NULL\n", __func__);
+		/* still return 1- offline if core ptr null */
+		return false;
+	}
+}
+
+static struct snd_info_entry_ops wcd_cpe_state_proc_ops = {
+	.read = wcd_cpe_state_read,
+	.poll = wcd_cpe_state_poll,
+};
+
+static int wcd_cpe_check_new_image(struct wcd_cpe_core *core)
+{
+	int rc = 0;
+	char temp_img_name[WCD_CPE_IMAGE_FNAME_MAX];
+
+	if (!strcmp(core->fname, core->dyn_fname) &&
+	    core->ssr_type != WCD_CPE_INITIALIZED) {
+		dev_dbg(core->dev,
+			"%s: Firmware unchanged, fname = %s, ssr_type 0x%x\n",
+			__func__, core->fname, core->ssr_type);
+		goto done;
+	}
+
+	/*
+	 * Different firmware name requested,
+	 * Re-load the instruction section
+	 */
+	strlcpy(temp_img_name, core->fname,
+		WCD_CPE_IMAGE_FNAME_MAX);
+	strlcpy(core->fname, core->dyn_fname,
+		WCD_CPE_IMAGE_FNAME_MAX);
+
+	rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+	if (rc) {
+		dev_err(core->dev,
+			"%s: Failed to dload new image %s, err = %d\n",
+			__func__, core->fname, rc);
+		/* If new image download failed, revert back to old image */
+		strlcpy(core->fname, temp_img_name,
+			WCD_CPE_IMAGE_FNAME_MAX);
+		rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+		if (rc)
+			dev_err(core->dev,
+				"%s: Failed to re-dload image %s, err = %d\n",
+				__func__, core->fname, rc);
+	} else {
+		dev_info(core->dev, "%s: fw changed to %s\n",
+			 __func__, core->fname);
+	}
+done:
+	return rc;
+}
+
+static int wcd_cpe_enable(struct wcd_cpe_core *core,
+		bool enable)
+{
+	int ret = 0;
+
+	if (enable) {
+		/* Reset CPE first */
+		ret = cpe_svc_reset(core->cpe_handle);
+		if (ret < 0) {
+			dev_err(core->dev,
+				"%s: CPE Reset failed, error = %d\n",
+				__func__, ret);
+			goto done;
+		}
+
+		ret = wcd_cpe_setup_irqs(core);
+		if (ret) {
+			dev_err(core->dev,
+				"%s: CPE IRQs setup failed, error = %d\n",
+				__func__, ret);
+			goto done;
+		}
+		ret = wcd_cpe_check_new_image(core);
+		if (ret)
+			goto fail_boot;
+
+		/* Dload data section */
+		ret = wcd_cpe_load_fw(core, ELF_FLAG_RW);
+		if (ret) {
+			dev_err(core->dev,
+				"%s: Failed to dload data section, err = %d\n",
+				__func__, ret);
+			goto fail_boot;
+		}
+
+		ret = wcd_cpe_enable_cpe_clks(core, true);
+		if (ret < 0) {
+			dev_err(core->dev,
+				"%s: CPE clk enable failed, err = %d\n",
+				__func__, ret);
+			goto fail_boot;
+		}
+
+		ret = cpe_svc_boot(core->cpe_handle,
+				   core->cpe_debug_mode);
+		if (ret < 0) {
+			dev_err(core->dev,
+				"%s: Failed to boot CPE\n",
+				__func__);
+			goto fail_boot;
+		}
+
+		/* wait for CPE to be online */
+		dev_dbg(core->dev,
+			"%s: waiting for CPE bootup\n",
+			__func__);
+
+		wait_for_completion(&core->online_compl);
+
+		dev_dbg(core->dev,
+			"%s: CPE bootup done\n",
+			__func__);
+
+		core->ssr_type = WCD_CPE_ENABLED;
+	} else {
+		if (core->ssr_type == WCD_CPE_BUS_DOWN_EVENT ||
+		    core->ssr_type == WCD_CPE_SSR_EVENT) {
+			/*
+			 * If this disable vote is when
+			 * SSR is in progress, do not disable CPE here,
+			 * instead SSR handler will control CPE.
+			 */
+			wcd_cpe_enable_cpe_clks(core, false);
+			/*
+			 * During BUS_DOWN event, possibly the
+			 * irq driver is under cleanup, do not request
+			 * cleanup of irqs here, rather cleanup irqs
+			 * once BUS_UP event is received.
+			 */
+			if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT)
+				wcd_cpe_cleanup_irqs(core);
+			goto done;
+		}
+
+		ret = cpe_svc_shutdown(core->cpe_handle);
+		if (ret < 0) {
+			dev_err(core->dev,
+				"%s: CPE shutdown failed, error %d\n",
+				__func__, ret);
+			goto done;
+		}
+
+		wcd_cpe_enable_cpe_clks(core, false);
+		wcd_cpe_cleanup_irqs(core);
+		core->ssr_type = WCD_CPE_IMEM_DOWNLOADED;
+	}
+
+	return ret;
+
+fail_boot:
+	wcd_cpe_cleanup_irqs(core);
+
+done:
+	return ret;
+}
+
+/*
+ * wcd_cpe_boot_ssr: Load the images to CPE after ssr and bootup cpe
+ * @core: handle to the core
+ */
+static int wcd_cpe_boot_ssr(struct wcd_cpe_core *core)
+{
+	int rc = 0;
+
+	if (!core || !core->cpe_handle) {
+		pr_err("%s: Invalid handle\n", __func__);
+		rc = -EINVAL;
+		goto fail;
+	}
+	/* Load the instruction section and mark CPE as online */
+	rc = wcd_cpe_load_fw(core, ELF_FLAG_EXECUTE);
+	if (rc) {
+		dev_err(core->dev,
+			"%s: Failed to load instruction, err = %d\n",
+			__func__, rc);
+		goto fail;
+	} else {
+		wcd_cpe_change_online_state(core, 1);
+	}
+
+fail:
+	return rc;
+}
+
+/*
+ * wcd_cpe_clr_ready_status:
+ *	Clear the value from the ready status for CPE
+ * @core: handle to the core
+ * @value: flag/bitmask that is to be cleared
+ *
+ * This function should not be invoked with ssr_lock acquired
+ */
+static void wcd_cpe_clr_ready_status(struct wcd_cpe_core *core,
+			       u8 value)
+{
+	WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+	core->ready_status &= ~(value);
+	dev_dbg(core->dev,
+		"%s: ready_status = 0x%x\n",
+		__func__, core->ready_status);
+	WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+}
+
+/*
+ * wcd_cpe_set_and_complete:
+ *	Set the ready status with the provided value and
+ *	flag the completion object if ready status moves
+ *	to ready to download
+ * @core: handle to the core
+ * @value: flag/bitmask that is to be set
+ */
+static void wcd_cpe_set_and_complete(struct wcd_cpe_core *core,
+				u8 value)
+{
+	WCD_CPE_GRAB_LOCK(&core->ssr_lock, "SSR");
+	core->ready_status |= value;
+	if ((core->ready_status & WCD_CPE_READY_TO_DLOAD) ==
+	    WCD_CPE_READY_TO_DLOAD) {
+		dev_dbg(core->dev,
+			"%s: marking ready, status = 0x%x\n",
+			__func__, core->ready_status);
+		complete(&core->ready_compl);
+	}
+	WCD_CPE_REL_LOCK(&core->ssr_lock, "SSR");
+}
+
+
+/*
+ * wcd_cpe_ssr_work: work function to handle CPE SSR
+ * @work: work that is scheduled to perform CPE shutdown
+ *	and restart
+ */
+static void wcd_cpe_ssr_work(struct work_struct *work)
+{
+
+	int rc = 0;
+	u32 irq = 0;
+	struct wcd_cpe_core *core = NULL;
+	u8 status = 0;
+
+	core = container_of(work, struct wcd_cpe_core, ssr_work);
+	if (!core) {
+		pr_err("%s: Core handle NULL\n", __func__);
+		return;
+	}
+
+	/* Obtain pm request up in case of suspend mode */
+	pm_qos_add_request(&core->pm_qos_req,
+			   PM_QOS_CPU_DMA_LATENCY,
+			   PM_QOS_DEFAULT_VALUE);
+	pm_qos_update_request(&core->pm_qos_req,
+			msm_cpuidle_get_deep_idle_latency());
+
+	dev_dbg(core->dev,
+		"%s: CPE SSR with event %d\n",
+		__func__, core->ssr_type);
+
+	if (core->ssr_type == WCD_CPE_SSR_EVENT) {
+		if (CPE_ERR_IRQ_CB(core))
+			core->cpe_cdc_cb->cpe_err_irq_control(
+					core->codec,
+					CPE_ERR_IRQ_STATUS,
+					&status);
+		if (status & core->irq_info.cpe_fatal_irqs)
+			irq = CPE_IRQ_WDOG_BITE;
+	} else {
+		/* If bus is down, cdc reg cannot be read */
+		irq = CPE_IRQ_WDOG_BITE;
+	}
+
+	if (core->cpe_users > 0) {
+		rc = cpe_svc_process_irq(core->cpe_handle, irq);
+		if (rc < 0)
+			/*
+			 * Even if process_irq fails,
+			 * wait for cpe to move to offline state
+			 */
+			dev_err(core->dev,
+				"%s: irq processing failed, error = %d\n",
+				__func__, rc);
+
+		rc = wait_for_completion_timeout(&core->offline_compl,
+						 CPE_OFFLINE_WAIT_TIMEOUT);
+		if (!rc) {
+			dev_err(core->dev,
+				"%s: wait for cpe offline timed out\n",
+				__func__);
+			goto err_ret;
+		}
+		if (core->ssr_type != WCD_CPE_BUS_DOWN_EVENT) {
+			wcd_cpe_get_sfr_dump(core);
+
+			/*
+			 * Ramdump has to be explicitly enabled
+			 * through debugfs and cannot be collected
+			 * when bus is down.
+			 */
+			if (ramdump_enable)
+				wcd_cpe_collect_ramdump(core);
+		}
+	} else {
+		pr_err("%s: no cpe users, mark as offline\n", __func__);
+		wcd_cpe_change_online_state(core, 0);
+		wcd_cpe_set_and_complete(core,
+					 WCD_CPE_BLK_READY);
+	}
+
+	rc = wait_for_completion_timeout(&core->ready_compl,
+					 CPE_READY_WAIT_TIMEOUT);
+	if (!rc) {
+		dev_err(core->dev,
+			"%s: ready to online timed out, status = %u\n",
+			__func__, core->ready_status);
+		goto err_ret;
+	}
+
+	rc = wcd_cpe_boot_ssr(core);
+
+	/* Once image are downloaded make sure all
+	 * error interrupts are cleared
+	 */
+	if (CPE_ERR_IRQ_CB(core))
+		core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+					CPE_ERR_IRQ_CLEAR, NULL);
+
+err_ret:
+	/* remove after default pm qos */
+	pm_qos_update_request(&core->pm_qos_req,
+			      PM_QOS_DEFAULT_VALUE);
+	pm_qos_remove_request(&core->pm_qos_req);
+}
+
+/*
+ * wcd_cpe_ssr_handle: handle SSR events here.
+ * @core_handle: handle to the cpe core
+ * @event: indicates ADSP or CDSP SSR.
+ */
+int wcd_cpe_ssr_event(void *core_handle,
+		      enum wcd_cpe_ssr_state_event event)
+{
+	struct wcd_cpe_core *core = core_handle;
+
+	if (!core) {
+		pr_err("%s: Invalid handle to core\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * If CPE is not even enabled, the SSR event for
+	 * CPE needs to be ignored
+	 */
+	if (core->ssr_type == WCD_CPE_INITIALIZED) {
+		dev_info(core->dev,
+			"%s: CPE initialized but not enabled, skip CPE ssr\n",
+			 __func__);
+		return 0;
+	}
+
+	dev_dbg(core->dev,
+		"%s: Schedule ssr work, event = %d\n",
+		__func__, core->ssr_type);
+
+	switch (event) {
+	case WCD_CPE_BUS_DOWN_EVENT:
+		/*
+		 * If bus down, then CPE block is also
+		 * treated to be down
+		 */
+		wcd_cpe_clr_ready_status(core, WCD_CPE_READY_TO_DLOAD);
+		core->ssr_type = event;
+		schedule_work(&core->ssr_work);
+		break;
+
+	case WCD_CPE_SSR_EVENT:
+		wcd_cpe_clr_ready_status(core, WCD_CPE_BLK_READY);
+		core->ssr_type = event;
+		schedule_work(&core->ssr_work);
+		break;
+
+	case WCD_CPE_BUS_UP_EVENT:
+		wcd_cpe_cleanup_irqs(core);
+		wcd_cpe_set_and_complete(core, WCD_CPE_BUS_READY);
+		/*
+		 * In case of bus up event ssr_type will be changed
+		 * to WCD_CPE_ACTIVE once CPE is online
+		 */
+		break;
+
+	default:
+		dev_err(core->dev,
+			"%s: unhandled SSR event %d\n",
+			__func__, event);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd_cpe_ssr_event);
+
+/*
+ * svass_exception_irq: threaded irq handler for sva error interrupts
+ * @irq: interrupt number
+ * @data: data pointer passed during irq registration
+ *
+ * Once a error interrupt is received, it is not cleared, since
+ * clearing this interrupt will raise spurious interrupts unless
+ * CPE is reset.
+ */
+static irqreturn_t svass_exception_irq(int irq, void *data)
+{
+	struct wcd_cpe_core *core = data;
+	u8 status = 0;
+
+	if (!core || !CPE_ERR_IRQ_CB(core)) {
+		pr_err("%s: Invalid %s\n",
+		       __func__,
+		       (!core) ? "core" : "cdc control");
+		return IRQ_HANDLED;
+	}
+
+	core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+			CPE_ERR_IRQ_STATUS, &status);
+
+	while (status != 0) {
+		if (status & core->irq_info.cpe_fatal_irqs) {
+			dev_err(core->dev,
+				"%s: CPE SSR event,err_status = 0x%02x\n",
+				__func__, status);
+			wcd_cpe_ssr_event(core, WCD_CPE_SSR_EVENT);
+			/*
+			 * If fatal interrupt is received,
+			 * trigger SSR and stop processing
+			 * further interrupts
+			 */
+			break;
+		}
+		/*
+		 * Mask the interrupt that was raised to
+		 * avoid spurious interrupts
+		 */
+		core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+					CPE_ERR_IRQ_MASK, &status);
+
+		/* Clear only the interrupt that was raised */
+		core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+					CPE_ERR_IRQ_CLEAR, &status);
+		dev_err(core->dev,
+			"%s: err_interrupt status = 0x%x\n",
+			__func__, status);
+
+		/* Read status for pending interrupts */
+		core->cpe_cdc_cb->cpe_err_irq_control(core->codec,
+					CPE_ERR_IRQ_STATUS, &status);
+	}
+
+	return IRQ_HANDLED;
+}
+
+/*
+ * wcd_cpe_cmi_afe_cb: callback called on response to afe commands
+ * @param: parameter containing the response code, etc
+ *
+ * Process the request to the command sent to CPE and wakeup the
+ * command send wait.
+ */
+static void wcd_cpe_cmi_afe_cb(const struct cmi_api_notification *param)
+{
+	struct cmi_hdr *hdr;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	u8 port_id;
+
+	if (!param) {
+		pr_err("%s: param is null\n", __func__);
+		return;
+	}
+
+	if (param->event != CMI_API_MSG) {
+		pr_err("%s: unhandled event 0x%x\n",
+			__func__, param->event);
+		return;
+	}
+
+	pr_debug("%s: param->result = %d\n",
+		 __func__, param->result);
+
+	hdr = (struct cmi_hdr *) param->message;
+
+	/*
+	 * for AFE cmd response, port id is
+	 * stored at session id field of header
+	 */
+	port_id = CMI_HDR_GET_SESSION_ID(hdr);
+	if (port_id > WCD_CPE_AFE_MAX_PORTS) {
+		pr_err("%s: invalid port_id %d\n",
+			__func__, port_id);
+		return;
+	}
+
+	afe_port_d = &(afe_ports[port_id]);
+
+	if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) {
+
+		u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr));
+		u8 result = payload[0];
+
+		afe_port_d->cmd_result = result;
+		complete(&afe_port_d->afe_cmd_complete);
+
+	} else if (hdr->opcode == CPE_AFE_PORT_CMDRSP_SHARED_MEM_ALLOC) {
+
+		struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc =
+			(struct cpe_cmdrsp_shmem_alloc *) param->message;
+
+		if (cmdrsp_shmem_alloc->addr == 0) {
+			pr_err("%s: Failed AFE shared mem alloc\n", __func__);
+			afe_port_d->cmd_result = CMI_SHMEM_ALLOC_FAILED;
+		} else {
+			pr_debug("%s AFE shared mem addr = 0x%x\n",
+				 __func__, cmdrsp_shmem_alloc->addr);
+			afe_port_d->mem_handle = cmdrsp_shmem_alloc->addr;
+			afe_port_d->cmd_result = 0;
+		}
+		complete(&afe_port_d->afe_cmd_complete);
+	}
+}
+
+/*
+ * wcd_cpe_initialize_afe_port_data: Initialize all AFE ports
+ *
+ * Initialize the data for all the afe ports. Assign the
+ * afe port state to INIT state.
+ */
+static void wcd_cpe_initialize_afe_port_data(void)
+{
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int i;
+
+	for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) {
+		afe_port_d = &afe_ports[i];
+		afe_port_d->port_id = i;
+		init_completion(&afe_port_d->afe_cmd_complete);
+		afe_port_d->port_state = AFE_PORT_STATE_INIT;
+		mutex_init(&afe_port_d->afe_lock);
+	}
+}
+
+/*
+ * wcd_cpe_deinitialize_afe_port_data: De-initialize all AFE ports
+ *
+ * De-Initialize the data for all the afe ports. Assign the
+ * afe port state to DEINIT state.
+ */
+static void wcd_cpe_deinitialize_afe_port_data(void)
+{
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int i;
+
+	for (i = 0; i <= WCD_CPE_AFE_MAX_PORTS; i++) {
+		afe_port_d = &afe_ports[i];
+		afe_port_d->port_state = AFE_PORT_STATE_DEINIT;
+		mutex_destroy(&afe_port_d->afe_lock);
+	}
+}
+
+/*
+ * wcd_cpe_svc_event_cb: callback from cpe services, indicating
+ * CPE is online or offline.
+ * @param: parameter / payload for event to be notified
+ */
+static void wcd_cpe_svc_event_cb(const struct cpe_svc_notification *param)
+{
+	struct snd_soc_codec *codec;
+	struct wcd_cpe_core *core;
+	struct cpe_svc_boot_event *boot_data;
+	bool active_sessions;
+
+	if (!param) {
+		pr_err("%s: Invalid event\n", __func__);
+		return;
+	}
+
+	codec = param->private_data;
+	if (!codec) {
+		pr_err("%s: Invalid handle to codec\n",
+			__func__);
+		return;
+	}
+
+	core = wcd_cpe_get_core_handle(codec);
+	if (!core) {
+		pr_err("%s: Invalid handle to core\n",
+			__func__);
+		return;
+	}
+
+	dev_dbg(core->dev,
+		"%s: event = 0x%x, ssr_type = 0x%x\n",
+		__func__, param->event, core->ssr_type);
+
+	switch (param->event) {
+	case CPE_SVC_BOOT:
+		boot_data = (struct cpe_svc_boot_event *)
+				param->payload;
+		core->sfr_buf_addr = boot_data->debug_address;
+		core->sfr_buf_size = boot_data->debug_buffer_size;
+		dev_dbg(core->dev,
+			"%s: CPE booted, sfr_addr = %d, sfr_size = %zu\n",
+			__func__, core->sfr_buf_addr,
+			core->sfr_buf_size);
+		break;
+	case CPE_SVC_ONLINE:
+		core->ssr_type = WCD_CPE_ACTIVE;
+		dev_dbg(core->dev, "%s CPE is now online\n",
+			 __func__);
+		complete(&core->online_compl);
+		break;
+	case CPE_SVC_OFFLINE:
+		/*
+		 * offline can happen during normal shutdown,
+		 * but we are interested in offline only during
+		 * SSR.
+		 */
+		if (core->ssr_type != WCD_CPE_SSR_EVENT &&
+		    core->ssr_type != WCD_CPE_BUS_DOWN_EVENT)
+			break;
+
+		active_sessions = wcd_cpe_lsm_session_active();
+		wcd_cpe_change_online_state(core, 0);
+		complete(&core->offline_compl);
+		dev_err(core->dev, "%s: CPE is now offline\n",
+			 __func__);
+		break;
+	case CPE_SVC_CMI_CLIENTS_DEREG:
+
+		/*
+		 * Only when either CPE SSR is in progress,
+		 * or the bus is down, we need to mark the CPE
+		 * as ready. In all other cases, this event is
+		 * ignored
+		 */
+		if (core->ssr_type == WCD_CPE_SSR_EVENT ||
+		    core->ssr_type == WCD_CPE_BUS_DOWN_EVENT)
+			wcd_cpe_set_and_complete(core,
+						 WCD_CPE_BLK_READY);
+		break;
+	default:
+		dev_err(core->dev,
+			"%s: unhandled notification\n",
+			__func__);
+		break;
+	}
+}
+
+/*
+ * wcd_cpe_cleanup_irqs: free the irq resources required by cpe
+ * @core: handle the cpe core
+ *
+ * This API will free the IRQs for CPE but does not mask the
+ * CPE interrupts. If masking is needed, it has to be done
+ * explicity by caller.
+ */
+static void wcd_cpe_cleanup_irqs(struct wcd_cpe_core *core)
+{
+
+	struct snd_soc_codec *codec = core->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+
+	wcd9xxx_free_irq(core_res,
+			 core->irq_info.cpe_engine_irq,
+			 core);
+	wcd9xxx_free_irq(core_res,
+			 core->irq_info.cpe_err_irq,
+			 core);
+
+}
+
+/*
+ * wcd_cpe_setup_sva_err_intr: setup the irqs for CPE
+ * @core: handle to wcd_cpe_core
+ * All interrupts needed for CPE are acquired. If any
+ * request_irq fails, then all irqs are free'd
+ */
+static int wcd_cpe_setup_irqs(struct wcd_cpe_core *core)
+{
+	int ret;
+	struct snd_soc_codec *codec = core->codec;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	struct wcd9xxx_core_resource *core_res = &wcd9xxx->core_res;
+
+	ret = wcd9xxx_request_irq(core_res,
+				  core->irq_info.cpe_engine_irq,
+				  svass_engine_irq, "SVASS_Engine", core);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: Failed to request svass engine irq\n",
+			__func__);
+		goto fail_engine_irq;
+	}
+
+	/* Make sure all error interrupts are cleared */
+	if (CPE_ERR_IRQ_CB(core))
+		core->cpe_cdc_cb->cpe_err_irq_control(
+					core->codec,
+					CPE_ERR_IRQ_CLEAR,
+					NULL);
+
+	/* Enable required error interrupts */
+	if (CPE_ERR_IRQ_CB(core))
+		core->cpe_cdc_cb->cpe_err_irq_control(
+					core->codec,
+					CPE_ERR_IRQ_UNMASK,
+					NULL);
+
+	ret = wcd9xxx_request_irq(core_res,
+				  core->irq_info.cpe_err_irq,
+				  svass_exception_irq, "SVASS_Exception", core);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: Failed to request svass err irq\n",
+			__func__);
+		goto fail_exception_irq;
+	}
+
+	return 0;
+
+fail_exception_irq:
+	wcd9xxx_free_irq(core_res,
+			 core->irq_info.cpe_engine_irq, core);
+
+fail_engine_irq:
+	return ret;
+}
+
+static int wcd_cpe_get_cal_index(int32_t cal_type)
+{
+	int cal_index = -EINVAL;
+
+	if (cal_type == ULP_AFE_CAL_TYPE)
+		cal_index = WCD_CPE_LSM_CAL_AFE;
+	else if (cal_type == ULP_LSM_CAL_TYPE)
+		cal_index = WCD_CPE_LSM_CAL_LSM;
+	else if (cal_type == ULP_LSM_TOPOLOGY_ID_CAL_TYPE)
+		cal_index = WCD_CPE_LSM_CAL_TOPOLOGY_ID;
+	else
+		pr_err("%s: invalid cal_type %d\n",
+			__func__, cal_type);
+
+	return cal_index;
+}
+
+static int wcd_cpe_alloc_cal(int32_t cal_type, size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	cal_index = wcd_cpe_get_cal_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: invalid caltype %d\n",
+			__func__, cal_type);
+		return -EINVAL;
+	}
+
+	ret = cal_utils_alloc_cal(data_size, data,
+				  core_d->cal_data[cal_index],
+				  0, NULL);
+	if (ret < 0)
+		pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+	return ret;
+}
+
+static int wcd_cpe_dealloc_cal(int32_t cal_type, size_t data_size,
+			   void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	cal_index = wcd_cpe_get_cal_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: invalid caltype %d\n",
+			__func__, cal_type);
+		return -EINVAL;
+	}
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+				    core_d->cal_data[cal_index]);
+	if (ret < 0)
+		pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+	return ret;
+}
+
+static int wcd_cpe_set_cal(int32_t cal_type, size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	cal_index = wcd_cpe_get_cal_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: invalid caltype %d\n",
+			__func__, cal_type);
+		return -EINVAL;
+	}
+
+	ret = cal_utils_set_cal(data_size, data,
+				core_d->cal_data[cal_index],
+				0, NULL);
+	if (ret < 0)
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+	return ret;
+}
+
+static int wcd_cpe_cal_init(struct wcd_cpe_core *core)
+{
+	int ret = 0;
+
+	struct cal_type_info cal_type_info[] = {
+		{{ULP_AFE_CAL_TYPE,
+		 {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL,
+		  wcd_cpe_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{ULP_LSM_CAL_TYPE,
+		 {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL,
+		  wcd_cpe_set_cal, NULL, NULL} },
+		 {NULL, NULL, cal_utils_match_buf_num} },
+
+		{{ULP_LSM_TOPOLOGY_ID_CAL_TYPE,
+		 {wcd_cpe_alloc_cal, wcd_cpe_dealloc_cal, NULL,
+		  wcd_cpe_set_cal, NULL, NULL} },
+		 {NULL, NULL, cal_utils_match_buf_num} },
+	};
+
+	ret = cal_utils_create_cal_types(WCD_CPE_LSM_CAL_MAX,
+					 core->cal_data,
+					 cal_type_info);
+	if (ret < 0)
+		pr_err("%s: could not create cal type!\n",
+		       __func__);
+	return ret;
+}
+
+/*
+ * wcd_cpe_enable: setup the cpe interrupts and schedule
+ *	the work to download image and bootup the CPE.
+ * core: handle to cpe core structure
+ */
+static int wcd_cpe_vote(struct wcd_cpe_core *core,
+		bool enable)
+{
+	int ret = 0;
+
+	if (!core) {
+		pr_err("%s: Invalid handle to core\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	dev_dbg(core->dev,
+		"%s: enter, enable = %s, cpe_users = %u\n",
+		__func__, (enable ? "true" : "false"),
+		core->cpe_users);
+
+	if (enable) {
+		core->cpe_users++;
+		if (core->cpe_users == 1) {
+			ret = wcd_cpe_enable(core, enable);
+			if (ret) {
+				dev_err(core->dev,
+					"%s: CPE enable failed, err = %d\n",
+					__func__, ret);
+				goto done;
+			}
+		} else {
+			dev_dbg(core->dev,
+				"%s: cpe already enabled, users = %u\n",
+				__func__, core->cpe_users);
+			goto done;
+		}
+	} else {
+		core->cpe_users--;
+		if (core->cpe_users == 0) {
+			ret = wcd_cpe_enable(core, enable);
+			if (ret) {
+				dev_err(core->dev,
+					"%s: CPE disable failed, err = %d\n",
+					__func__, ret);
+				goto done;
+			}
+		} else {
+			dev_dbg(core->dev,
+				"%s: %u valid users on cpe\n",
+				__func__, core->cpe_users);
+			goto done;
+		}
+	}
+
+	dev_dbg(core->dev,
+		"%s: leave, enable = %s, cpe_users = %u\n",
+		__func__, (enable ? "true" : "false"),
+		core->cpe_users);
+
+done:
+	return ret;
+}
+
+static int wcd_cpe_debugfs_init(struct wcd_cpe_core *core)
+{
+	int rc = 0;
+
+	struct dentry *dir = debugfs_create_dir("wcd_cpe", NULL);
+
+	if (IS_ERR_OR_NULL(dir)) {
+		dir = NULL;
+		rc = -ENODEV;
+		goto err_create_dir;
+	}
+
+	if (!debugfs_create_u32("ramdump_enable", 0644,
+				dir, &ramdump_enable)) {
+		dev_err(core->dev, "%s: Failed to create debugfs node %s\n",
+			__func__, "ramdump_enable");
+		rc = -ENODEV;
+		goto err_create_entry;
+	}
+
+	if (!debugfs_create_file("cpe_ftm_test_trigger", 0200,
+				dir, core, &cpe_ftm_test_trigger_fops)) {
+		dev_err(core->dev, "%s: Failed to create debugfs node %s\n",
+			__func__, "cpe_ftm_test_trigger");
+		rc = -ENODEV;
+		goto err_create_entry;
+	}
+
+	if (!debugfs_create_u32("cpe_ftm_test_status", 0444,
+				dir, &cpe_ftm_test_status)) {
+		dev_err(core->dev, "%s: Failed to create debugfs node %s\n",
+			__func__, "cpe_ftm_test_status");
+		rc = -ENODEV;
+		goto err_create_entry;
+	}
+
+err_create_entry:
+	debugfs_remove(dir);
+
+err_create_dir:
+	return rc;
+}
+
+static ssize_t fw_name_show(struct wcd_cpe_core *core, char *buf)
+{
+	return snprintf(buf, WCD_CPE_IMAGE_FNAME_MAX, "%s",
+			core->dyn_fname);
+}
+
+static ssize_t fw_name_store(struct wcd_cpe_core *core,
+		const char *buf, ssize_t count)
+{
+	int copy_count = count;
+	const char *pos;
+
+	pos = memchr(buf, '\n', count);
+	if (pos)
+		copy_count = pos - buf;
+
+	if (copy_count > WCD_CPE_IMAGE_FNAME_MAX) {
+		dev_err(core->dev,
+			"%s: Invalid length %d, max allowed %d\n",
+			__func__, copy_count, WCD_CPE_IMAGE_FNAME_MAX);
+		return -EINVAL;
+	}
+
+	strlcpy(core->dyn_fname, buf, copy_count + 1);
+
+	return count;
+}
+
+WCD_CPE_ATTR(fw_name, 0660, fw_name_show, fw_name_store);
+
+static ssize_t wcd_cpe_sysfs_show(struct kobject *kobj,
+		struct attribute *attr, char *buf)
+{
+	struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr);
+	struct wcd_cpe_core *core = kobj_to_cpe_core(kobj);
+	ssize_t ret = -EINVAL;
+
+	if (core && cpe_attr->show)
+		ret = cpe_attr->show(core, buf);
+
+	return ret;
+}
+
+static ssize_t wcd_cpe_sysfs_store(struct kobject *kobj,
+		struct attribute *attr, const char *buf,
+		size_t count)
+{
+	struct wcd_cpe_attribute *cpe_attr = to_wcd_cpe_attr(attr);
+	struct wcd_cpe_core *core = kobj_to_cpe_core(kobj);
+	ssize_t ret = -EINVAL;
+
+	if (core && cpe_attr->store)
+		ret = cpe_attr->store(core, buf, count);
+
+	return ret;
+}
+
+static const struct sysfs_ops wcd_cpe_sysfs_ops = {
+	.show = wcd_cpe_sysfs_show,
+	.store = wcd_cpe_sysfs_store,
+};
+
+static struct kobj_type wcd_cpe_ktype = {
+	.sysfs_ops = &wcd_cpe_sysfs_ops,
+};
+
+static int wcd_cpe_sysfs_init(struct wcd_cpe_core *core, int id)
+{
+	char sysfs_dir_name[WCD_CPE_SYSFS_DIR_MAX_LENGTH];
+	int rc = 0;
+
+	snprintf(sysfs_dir_name, WCD_CPE_SYSFS_DIR_MAX_LENGTH,
+		 "%s%d", "wcd_cpe", id);
+
+	rc = kobject_init_and_add(&core->cpe_kobj, &wcd_cpe_ktype,
+				  kernel_kobj,
+				  sysfs_dir_name);
+	if (unlikely(rc)) {
+		dev_err(core->dev,
+			"%s: Failed to add kobject %s, err = %d\n",
+			__func__, sysfs_dir_name, rc);
+		goto done;
+	}
+
+	rc = sysfs_create_file(&core->cpe_kobj, &cpe_attr_fw_name.attr);
+	if (rc) {
+		dev_err(core->dev,
+			"%s: Failed to fw_name sysfs entry to %s\n",
+			__func__, sysfs_dir_name);
+		goto fail_create_file;
+	}
+
+	return 0;
+
+fail_create_file:
+	kobject_put(&core->cpe_kobj);
+done:
+	return rc;
+}
+
+static ssize_t cpe_ftm_test_trigger(struct file *file,
+				     const char __user *user_buf,
+				     size_t count, loff_t *ppos)
+{
+	struct wcd_cpe_core *core = file->private_data;
+	int ret = 0;
+
+	/* Enable the clks for cpe */
+	ret = wcd_cpe_enable_cpe_clks(core, true);
+	if (ret < 0) {
+		dev_err(core->dev,
+			"%s: CPE clk enable failed, err = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	/* Get the CPE_STATUS */
+	ret = cpe_svc_ftm_test(core->cpe_handle, &cpe_ftm_test_status);
+	if (ret < 0) {
+		dev_err(core->dev,
+			"%s: CPE FTM test failed, err = %d\n",
+			__func__, ret);
+		if (ret == CPE_SVC_BUSY) {
+			cpe_ftm_test_status = 1;
+			ret = 0;
+		}
+	}
+
+	/* Disable the clks for cpe */
+	ret = wcd_cpe_enable_cpe_clks(core, false);
+	if (ret < 0) {
+		dev_err(core->dev,
+			"%s: CPE clk disable failed, err = %d\n",
+			__func__, ret);
+	}
+
+done:
+	if (ret < 0)
+		return ret;
+	else
+		return count;
+}
+
+static int wcd_cpe_validate_params(
+	struct snd_soc_codec *codec,
+	struct wcd_cpe_params *params)
+{
+
+	if (!codec) {
+		pr_err("%s: Invalid codec\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!params) {
+		dev_err(codec->dev,
+			"%s: No params supplied for codec %s\n",
+			__func__, codec->component.name);
+		return -EINVAL;
+	}
+
+	if (!params->codec || !params->get_cpe_core ||
+	    !params->cdc_cb) {
+		dev_err(codec->dev,
+			"%s: Invalid params for codec %s\n",
+			__func__, codec->component.name);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ * wcd_cpe_init: Initialize CPE related structures
+ * @img_fname: filename for firmware image
+ * @codec: handle to codec requesting for image download
+ * @params: parameter structure passed from caller
+ *
+ * This API will initialize the cpe core but will not
+ * download the image or boot the cpe core.
+ */
+struct wcd_cpe_core *wcd_cpe_init(const char *img_fname,
+	struct snd_soc_codec *codec,
+	struct wcd_cpe_params *params)
+{
+	struct wcd_cpe_core *core;
+	int ret = 0;
+	struct snd_card *card = NULL;
+	struct snd_info_entry *entry = NULL;
+	char proc_name[WCD_CPE_STATE_MAX_LEN];
+	const char *cpe_name = "cpe";
+	const char *state_name = "_state";
+	const struct cpe_svc_hw_cfg *hw_info;
+	int id = 0;
+
+	if (wcd_cpe_validate_params(codec, params))
+		return NULL;
+
+	core = kzalloc(sizeof(struct wcd_cpe_core), GFP_KERNEL);
+	if (!core)
+		return NULL;
+
+	snprintf(core->fname, sizeof(core->fname), "%s", img_fname);
+	strlcpy(core->dyn_fname, core->fname, WCD_CPE_IMAGE_FNAME_MAX);
+
+	wcd_get_cpe_core = params->get_cpe_core;
+
+	core->codec = params->codec;
+	core->dev = params->codec->dev;
+	core->cpe_debug_mode = params->dbg_mode;
+
+	core->cdc_info.major_version = params->cdc_major_ver;
+	core->cdc_info.minor_version = params->cdc_minor_ver;
+	core->cdc_info.id = params->cdc_id;
+
+	core->cpe_cdc_cb = params->cdc_cb;
+
+	memcpy(&core->irq_info, &params->cdc_irq_info,
+	       sizeof(core->irq_info));
+
+	INIT_WORK(&core->load_fw_work, wcd_cpe_load_fw_image);
+	INIT_WORK(&core->ssr_work, wcd_cpe_ssr_work);
+	init_completion(&core->offline_compl);
+	init_completion(&core->ready_compl);
+	init_completion(&core->online_compl);
+	init_waitqueue_head(&core->ssr_entry.offline_poll_wait);
+	mutex_init(&core->ssr_lock);
+	core->cpe_users = 0;
+	core->cpe_clk_ref = 0;
+
+	/*
+	 * By default, during probe, it is assumed that
+	 * both CPE hardware block and underlying bus to codec
+	 * are ready
+	 */
+	core->ready_status = WCD_CPE_READY_TO_DLOAD;
+
+	core->cpe_handle = cpe_svc_initialize(NULL, &core->cdc_info,
+					      params->cpe_svc_params);
+	if (!core->cpe_handle) {
+		dev_err(core->dev,
+			"%s: failed to initialize cpe services\n",
+			__func__);
+		goto fail_cpe_initialize;
+	}
+
+	core->cpe_reg_handle = cpe_svc_register(core->cpe_handle,
+					wcd_cpe_svc_event_cb,
+					CPE_SVC_ONLINE | CPE_SVC_OFFLINE |
+					CPE_SVC_BOOT |
+					CPE_SVC_CMI_CLIENTS_DEREG,
+					"codec cpe handler");
+	if (!core->cpe_reg_handle) {
+		dev_err(core->dev,
+			"%s: failed to register cpe service\n",
+			__func__);
+		goto fail_cpe_register;
+	}
+
+	card = codec->component.card->snd_card;
+	snprintf(proc_name, (sizeof("cpe") + sizeof("_state") +
+		 sizeof(id) - 2), "%s%d%s", cpe_name, id, state_name);
+	entry = snd_info_create_card_entry(card, proc_name,
+					   card->proc_root);
+	if (entry) {
+		core->ssr_entry.entry = entry;
+		core->ssr_entry.offline = 1;
+		entry->size = WCD_CPE_STATE_MAX_LEN;
+		entry->content = SNDRV_INFO_CONTENT_DATA;
+		entry->c.ops = &wcd_cpe_state_proc_ops;
+		entry->private_data = core;
+		ret = snd_info_register(entry);
+		if (ret < 0) {
+			dev_err(core->dev,
+				"%s: snd_info_register failed (%d)\n",
+				 __func__, ret);
+			snd_info_free_entry(entry);
+			entry = NULL;
+		}
+	} else {
+		dev_err(core->dev,
+			"%s: Failed to create CPE SSR status entry\n",
+			__func__);
+		/*
+		 * Even if SSR entry creation fails, continue
+		 * with image download
+		 */
+	}
+
+	core_d = core;
+	ret = wcd_cpe_cal_init(core);
+	if (ret < 0) {
+		dev_err(core->dev,
+			"%s: CPE calibration init failed, err = %d\n",
+			__func__, ret);
+		goto fail_cpe_reset;
+	}
+
+	wcd_cpe_debugfs_init(core);
+
+	wcd_cpe_sysfs_init(core, id);
+
+	hw_info = cpe_svc_get_hw_cfg(core->cpe_handle);
+	if (!hw_info) {
+		dev_err(core->dev,
+			"%s: hw info not available\n",
+			__func__);
+		goto schedule_dload_work;
+	} else {
+		core->hw_info.dram_offset = hw_info->DRAM_offset;
+		core->hw_info.dram_size = hw_info->DRAM_size;
+		core->hw_info.iram_offset = hw_info->IRAM_offset;
+		core->hw_info.iram_size = hw_info->IRAM_size;
+	}
+
+	/* Setup the ramdump device and buffer */
+	core->cpe_ramdump_dev = create_ramdump_device("cpe",
+						      core->dev);
+	if (!core->cpe_ramdump_dev) {
+		dev_err(core->dev,
+			"%s: Failed to create ramdump device\n",
+			__func__);
+		goto schedule_dload_work;
+	}
+
+	arch_setup_dma_ops(core->dev, 0, 0, NULL, 0);
+	core->cpe_dump_v_addr = dma_alloc_coherent(core->dev,
+						   core->hw_info.dram_size,
+						   &core->cpe_dump_addr,
+						   GFP_KERNEL);
+	if (!core->cpe_dump_v_addr) {
+		dev_err(core->dev,
+			"%s: Failed to alloc memory for cpe dump, size = %zd\n",
+			__func__, core->hw_info.dram_size);
+		goto schedule_dload_work;
+	} else {
+		memset(core->cpe_dump_v_addr, 0, core->hw_info.dram_size);
+	}
+
+schedule_dload_work:
+	core->ssr_type = WCD_CPE_INITIALIZED;
+	schedule_work(&core->load_fw_work);
+	return core;
+
+fail_cpe_reset:
+	cpe_svc_deregister(core->cpe_handle, core->cpe_reg_handle);
+
+fail_cpe_register:
+	cpe_svc_deinitialize(core->cpe_handle);
+
+fail_cpe_initialize:
+	kfree(core);
+	return NULL;
+}
+EXPORT_SYMBOL(wcd_cpe_init);
+
+/*
+ * wcd_cpe_cmi_lsm_callback: callback called from cpe services
+ *			     to notify command response for lsm
+ *			     service
+ * @param: param containing the response code and status
+ *
+ * This callback is registered with cpe services while registering
+ * the LSM service
+ */
+static void wcd_cpe_cmi_lsm_callback(const struct cmi_api_notification *param)
+{
+	struct cmi_hdr *hdr;
+	struct cpe_lsm_session *lsm_session;
+	u8 session_id;
+
+	if (!param) {
+		pr_err("%s: param is null\n", __func__);
+		return;
+	}
+
+	if (param->event != CMI_API_MSG) {
+		pr_err("%s: unhandled event 0x%x\n", __func__, param->event);
+		return;
+	}
+
+	hdr = (struct cmi_hdr *) param->message;
+	session_id = CMI_HDR_GET_SESSION_ID(hdr);
+
+	if (session_id > WCD_CPE_LSM_MAX_SESSIONS) {
+		pr_err("%s: invalid lsm session id = %d\n",
+			__func__, session_id);
+		return;
+	}
+
+	lsm_session = lsm_sessions[session_id];
+
+	if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) {
+
+		u8 *payload = ((u8 *)param->message) + (sizeof(struct cmi_hdr));
+		u8 result = payload[0];
+
+		lsm_session->cmd_err_code = result;
+		complete(&lsm_session->cmd_comp);
+
+	} else if (hdr->opcode == CPE_LSM_SESSION_CMDRSP_SHARED_MEM_ALLOC) {
+
+		struct cpe_cmdrsp_shmem_alloc *cmdrsp_shmem_alloc =
+			(struct cpe_cmdrsp_shmem_alloc *) param->message;
+
+		if (cmdrsp_shmem_alloc->addr == 0) {
+			pr_err("%s: Failed LSM shared mem alloc\n", __func__);
+			lsm_session->cmd_err_code = CMI_SHMEM_ALLOC_FAILED;
+
+		} else {
+
+			pr_debug("%s LSM shared mem addr = 0x%x\n",
+				__func__, cmdrsp_shmem_alloc->addr);
+			lsm_session->lsm_mem_handle = cmdrsp_shmem_alloc->addr;
+			lsm_session->cmd_err_code = 0;
+		}
+
+		complete(&lsm_session->cmd_comp);
+
+	} else if (hdr->opcode == CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) {
+
+		struct cpe_lsm_event_detect_v2 *event_detect_v2 =
+			(struct cpe_lsm_event_detect_v2 *) param->message;
+
+		if (!lsm_session->priv_d) {
+			pr_err("%s: private data is not present\n",
+				__func__);
+			return;
+		}
+
+		pr_debug("%s: event payload, status = %u, size = %u\n",
+			__func__, event_detect_v2->detection_status,
+			event_detect_v2->size);
+
+		if (lsm_session->event_cb)
+			lsm_session->event_cb(
+				lsm_session->priv_d,
+				event_detect_v2->detection_status,
+				event_detect_v2->size,
+				event_detect_v2->payload);
+	}
+}
+
+/*
+ * wcd_cpe_cmi_send_lsm_msg: send a message to lsm service
+ * @core: handle to cpe core
+ * @session: session on which to send the message
+ * @message: actual message containing header and payload
+ *
+ * Sends message to lsm service for specified session and wait
+ * for response back on the message.
+ * should be called after acquiring session specific mutex
+ */
+static int wcd_cpe_cmi_send_lsm_msg(
+			struct wcd_cpe_core *core,
+			struct cpe_lsm_session *session,
+			void *message)
+{
+	int ret = 0;
+	struct cmi_hdr *hdr = message;
+
+	pr_debug("%s: sending message with opcode 0x%x\n",
+		 __func__, hdr->opcode);
+
+	if (unlikely(!wcd_cpe_is_online_state(core))) {
+		dev_err(core->dev,
+			"%s: MSG not sent, CPE offline\n",
+			 __func__);
+		goto done;
+	}
+
+	if (CMI_HDR_GET_OBM_FLAG(hdr))
+		wcd_cpe_bus_vote_max_bw(core, true);
+
+	reinit_completion(&session->cmd_comp);
+	ret = cmi_send_msg(message);
+	if (ret) {
+		pr_err("%s: msg opcode (0x%x) send failed (%d)\n",
+			__func__, hdr->opcode, ret);
+		goto rel_bus_vote;
+	}
+
+	ret = wait_for_completion_timeout(&session->cmd_comp,
+					  CMI_CMD_TIMEOUT);
+	if (ret > 0) {
+		pr_debug("%s: command 0x%x, received response 0x%x\n",
+			__func__, hdr->opcode, session->cmd_err_code);
+		if (session->cmd_err_code == CMI_SHMEM_ALLOC_FAILED)
+			session->cmd_err_code = CPE_ENOMEMORY;
+		if (session->cmd_err_code > 0)
+			pr_err("%s: CPE returned error[%s]\n",
+				__func__, cpe_err_get_err_str(
+				session->cmd_err_code));
+		ret = cpe_err_get_lnx_err_code(session->cmd_err_code);
+		goto rel_bus_vote;
+	} else {
+		pr_err("%s: command (0x%x) send timed out\n",
+			__func__, hdr->opcode);
+		ret = -ETIMEDOUT;
+		goto rel_bus_vote;
+	}
+
+
+rel_bus_vote:
+
+	if (CMI_HDR_GET_OBM_FLAG(hdr))
+		wcd_cpe_bus_vote_max_bw(core, false);
+
+done:
+	return ret;
+}
+
+
+/*
+ * fill_cmi_header: fill the cmi header with specified values
+ *
+ * @hdr: header to be updated with values
+ * @session_id: session id of the header,
+ *		in case of AFE service it is port_id
+ * @service_id: afe/lsm, etc
+ * @version: update the version field in header
+ * @payload_size: size of the payload following after header
+ * @opcode: opcode of the message
+ * @obm_flag: indicates if this header is for obm message
+ *
+ */
+static int fill_cmi_header(struct cmi_hdr *hdr,
+			   u8 session_id, u8 service_id,
+			   bool version, u8 payload_size,
+			   u16 opcode, bool obm_flag)
+{
+	/* sanitize the data */
+	if (!IS_VALID_SESSION_ID(session_id) ||
+	    !IS_VALID_SERVICE_ID(service_id) ||
+	    !IS_VALID_PLD_SIZE(payload_size)) {
+		pr_err("Invalid header creation request\n");
+		return -EINVAL;
+	}
+
+	CMI_HDR_SET_SESSION(hdr, session_id);
+	CMI_HDR_SET_SERVICE(hdr, service_id);
+	if (version)
+		CMI_HDR_SET_VERSION(hdr, 1);
+	else
+		CMI_HDR_SET_VERSION(hdr, 0);
+
+	CMI_HDR_SET_PAYLOAD_SIZE(hdr, payload_size);
+
+	hdr->opcode = opcode;
+
+	if (obm_flag)
+		CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND);
+	else
+		CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND);
+
+	return 0;
+}
+
+/*
+ * fill_lsm_cmd_header_v0_inband:
+ *	Given the header, fill the header with information
+ *	for lsm service, version 0 and inband message
+ * @hdr: the cmi header to be filled.
+ * @session_id: ID for the lsm session
+ * @payload_size: size for cmi message payload
+ * @opcode: opcode for cmi message
+ */
+static int fill_lsm_cmd_header_v0_inband(struct cmi_hdr *hdr,
+		u8 session_id, u8 payload_size, u16 opcode)
+{
+	return fill_cmi_header(hdr, session_id,
+			       CMI_CPE_LSM_SERVICE_ID, false,
+			       payload_size, opcode, false);
+}
+
+/*
+ * wcd_cpe_is_valid_lsm_session:
+ *	Check session parameters to identify validity for the sesion
+ * @core: handle to cpe core
+ * @session: handle to the lsm session
+ * @func: invoking function to be printed in error logs
+ */
+static int wcd_cpe_is_valid_lsm_session(struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		const char *func)
+{
+	if (unlikely(IS_ERR_OR_NULL(core))) {
+		pr_err("%s: invalid handle to core\n",
+			func);
+		return -EINVAL;
+	}
+
+	if (unlikely(IS_ERR_OR_NULL(session))) {
+		dev_err(core->dev, "%s: invalid session\n",
+			func);
+		return -EINVAL;
+	}
+
+	if (session->id > WCD_CPE_LSM_MAX_SESSIONS) {
+		dev_err(core->dev, "%s: invalid session id (%u)\n",
+			func, session->id);
+		return -EINVAL;
+	}
+
+	dev_dbg(core->dev, "%s: session_id = %u\n",
+		func, session->id);
+	return 0;
+}
+
+static int wcd_cpe_cmd_lsm_open_tx_v2(
+	struct wcd_cpe_core *core,
+	struct cpe_lsm_session *session)
+{
+	struct cpe_lsm_cmd_open_tx_v2 cmd_open_tx_v2;
+	struct cal_block_data *top_cal = NULL;
+	struct audio_cal_info_lsm_top *lsm_top;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	if (core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID] == NULL) {
+		dev_err(core->dev,
+			"%s: LSM_TOPOLOGY cal not allocated!\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock);
+	top_cal = cal_utils_get_only_cal_block(
+			core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]);
+	if (!top_cal) {
+		dev_err(core->dev,
+			"%s: Failed to get LSM TOPOLOGY cal block\n",
+			__func__);
+		ret = -EINVAL;
+		goto unlock_cal_mutex;
+	}
+
+	lsm_top = (struct audio_cal_info_lsm_top *)
+			top_cal->cal_info;
+
+	if (!lsm_top) {
+		dev_err(core->dev,
+			"%s: cal_info for LSM_TOPOLOGY not found\n",
+			__func__);
+		ret = -EINVAL;
+		goto unlock_cal_mutex;
+	}
+
+	dev_dbg(core->dev,
+		"%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n",
+		__func__, lsm_top->topology, lsm_top->acdb_id,
+		lsm_top->app_type);
+
+	if (lsm_top->topology == 0) {
+		dev_err(core->dev,
+			"%s: topology id not sent for app_type 0x%x\n",
+			__func__, lsm_top->app_type);
+		ret = -EINVAL;
+		goto unlock_cal_mutex;
+	}
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_open_tx_v2, 0, sizeof(struct cpe_lsm_cmd_open_tx_v2));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx_v2.hdr,
+				session->id, OPEN_V2_CMD_PAYLOAD_SIZE,
+				CPE_LSM_SESSION_CMD_OPEN_TX_V2)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	cmd_open_tx_v2.topology_id = lsm_top->topology;
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx_v2);
+	if (ret)
+		dev_err(core->dev,
+			"%s: failed to send open_tx_v2 cmd, err = %d\n",
+			__func__, ret);
+	else
+		session->is_topology_used = true;
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+unlock_cal_mutex:
+	mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_TOPOLOGY_ID]->lock);
+	return ret;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_open_tx: compose and send lsm open command
+ * @core_handle: handle to cpe core
+ * @session: session for which the command needs to be sent
+ * @app_id: application id part of the command
+ * @sample_rate: sample rate for this session
+ */
+static int wcd_cpe_cmd_lsm_open_tx(void *core_handle,
+		struct cpe_lsm_session *session,
+		u16 app_id, u16 sample_rate)
+{
+	struct cpe_lsm_cmd_open_tx cmd_open_tx;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	/* Try to open with topology first */
+	ret = wcd_cpe_cmd_lsm_open_tx_v2(core, session);
+	if (!ret)
+		goto done;
+
+	dev_dbg(core->dev, "%s: Try open_tx without topology\n",
+		__func__);
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_open_tx, 0, sizeof(struct cpe_lsm_cmd_open_tx));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_open_tx.hdr,
+				session->id, OPEN_CMD_PAYLOAD_SIZE,
+				CPE_LSM_SESSION_CMD_OPEN_TX)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	cmd_open_tx.app_id = app_id;
+	cmd_open_tx.sampling_rate = sample_rate;
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_open_tx);
+	if (ret)
+		dev_err(core->dev,
+			"%s: failed to send open_tx cmd, err = %d\n",
+			__func__, ret);
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+	return ret;
+}
+
+/*
+ * wcd_cpe_cmd_close_tx: compose and send lsm close command
+ * @core_handle: handle to cpe core
+ * @session: session for which the command needs to be sent
+ */
+static int wcd_cpe_cmd_lsm_close_tx(void *core_handle,
+			struct cpe_lsm_session *session)
+{
+	struct cmi_hdr cmd_close_tx;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_close_tx, 0, sizeof(cmd_close_tx));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_close_tx, session->id,
+			    0, CPE_LSM_SESSION_CMD_CLOSE_TX)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_close_tx);
+	if (ret)
+		dev_err(core->dev,
+			"%s: lsm close_tx cmd failed, err = %d\n",
+			__func__, ret);
+	else
+		session->is_topology_used = false;
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_cmd_shmem_alloc: compose and send lsm shared
+ *			    memory allocation command
+ * @core_handle: handle to cpe core
+ * @session: session for which the command needs to be sent
+ * @size: size of memory to be allocated
+ */
+static int wcd_cpe_cmd_lsm_shmem_alloc(void *core_handle,
+			struct cpe_lsm_session *session,
+			u32 size)
+{
+	struct cpe_cmd_shmem_alloc cmd_shmem_alloc;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_shmem_alloc.hdr, session->id,
+			    SHMEM_ALLOC_CMD_PLD_SIZE,
+			    CPE_LSM_SESSION_CMD_SHARED_MEM_ALLOC)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	cmd_shmem_alloc.size = size;
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_shmem_alloc);
+	if (ret)
+		dev_err(core->dev,
+			"%s: lsm_shmem_alloc cmd send fail, %d\n",
+			__func__, ret);
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_shmem_dealloc: deallocate the shared memory
+ *				  for the specified session
+ * @core_handle: handle to cpe core
+ * @session: session for which memory needs to be deallocated.
+ */
+static int wcd_cpe_cmd_lsm_shmem_dealloc(void *core_handle,
+		struct cpe_lsm_session *session)
+{
+	struct cpe_cmd_shmem_dealloc cmd_dealloc;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_dealloc, 0, sizeof(cmd_dealloc));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_dealloc.hdr, session->id,
+			    SHMEM_DEALLOC_CMD_PLD_SIZE,
+			    CPE_LSM_SESSION_CMD_SHARED_MEM_DEALLOC)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	cmd_dealloc.addr = session->lsm_mem_handle;
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dealloc);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: lsm_shmem_dealloc cmd failed, rc %d\n",
+			__func__, ret);
+		goto end_ret;
+	}
+
+	memset(&session->lsm_mem_handle, 0,
+	       sizeof(session->lsm_mem_handle));
+
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_send_lsm_cal: send the calibration for lsm service
+ *			      from acdb to the cpe
+ * @core: handle to cpe core
+ * @session: session for which the calibration needs to be set.
+ */
+static int wcd_cpe_send_lsm_cal(
+			struct wcd_cpe_core *core,
+			struct cpe_lsm_session *session)
+{
+
+	u8 *msg_pld;
+	struct cmi_hdr *hdr;
+	struct cal_block_data *lsm_cal = NULL;
+	void *inb_msg;
+	int rc = 0;
+
+	if (core->cal_data[WCD_CPE_LSM_CAL_LSM] == NULL) {
+		pr_err("%s: LSM cal not allocated!\n", __func__);
+		return -EINVAL;
+	}
+
+	mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock);
+	lsm_cal = cal_utils_get_only_cal_block(
+			core->cal_data[WCD_CPE_LSM_CAL_LSM]);
+	if (!lsm_cal) {
+		pr_err("%s: failed to get lsm cal block\n", __func__);
+		rc = -EINVAL;
+		goto unlock_cal_mutex;
+	}
+
+	if (lsm_cal->cal_data.size == 0) {
+		dev_dbg(core->dev, "%s: No LSM cal to send\n",
+			__func__);
+		rc = 0;
+		goto unlock_cal_mutex;
+	}
+
+	inb_msg = kzalloc(sizeof(struct cmi_hdr) + lsm_cal->cal_data.size,
+			  GFP_KERNEL);
+	if (!inb_msg) {
+		rc = -ENOMEM;
+		goto unlock_cal_mutex;
+	}
+
+	hdr = (struct cmi_hdr *) inb_msg;
+
+	rc = fill_lsm_cmd_header_v0_inband(hdr, session->id,
+			lsm_cal->cal_data.size,
+			CPE_LSM_SESSION_CMD_SET_PARAMS);
+	if (rc) {
+		pr_err("%s: invalid params for header, err = %d\n",
+			__func__, rc);
+		goto free_msg;
+	}
+
+	msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr);
+	memcpy(msg_pld, lsm_cal->cal_data.kvaddr,
+	       lsm_cal->cal_data.size);
+
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, inb_msg);
+	if (rc)
+		pr_err("%s: acdb lsm_params send failed, err = %d\n",
+			__func__, rc);
+
+free_msg:
+	kfree(inb_msg);
+
+unlock_cal_mutex:
+	mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_LSM]->lock);
+	return rc;
+
+}
+
+static void wcd_cpe_set_param_data(struct cpe_param_data *param_d,
+		struct cpe_lsm_ids *ids, u32 p_size,
+		u32 set_param_cmd)
+{
+	param_d->module_id = ids->module_id;
+	param_d->param_id = ids->param_id;
+
+	switch (set_param_cmd) {
+	case CPE_LSM_SESSION_CMD_SET_PARAMS_V2:
+		param_d->p_size.param_size = p_size;
+		break;
+	case CPE_LSM_SESSION_CMD_SET_PARAMS:
+	default:
+		param_d->p_size.sr.param_size =
+			(u16) p_size;
+		param_d->p_size.sr.reserved = 0;
+		break;
+	}
+}
+
+static int wcd_cpe_send_param_epd_thres(struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		void *data, struct cpe_lsm_ids *ids)
+{
+	struct snd_lsm_ep_det_thres *ep_det_data;
+	struct cpe_lsm_param_epd_thres epd_cmd;
+	struct cmi_hdr *msg_hdr = &epd_cmd.hdr;
+	struct cpe_param_data *param_d =
+				&epd_cmd.param;
+	int rc;
+
+	memset(&epd_cmd, 0, sizeof(epd_cmd));
+	ep_det_data = (struct snd_lsm_ep_det_thres *) data;
+	if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+				session->id,
+				CPE_CMD_EPD_THRES_PLD_SIZE,
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		rc = -EINVAL;
+		goto err_ret;
+	}
+
+	wcd_cpe_set_param_data(param_d, ids,
+			       CPE_EPD_THRES_PARAM_SIZE,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	epd_cmd.minor_version = 1;
+	epd_cmd.epd_begin = ep_det_data->epd_begin;
+	epd_cmd.epd_end = ep_det_data->epd_end;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, &epd_cmd);
+	if (unlikely(rc))
+		dev_err(core->dev,
+			"%s: set_param(EPD Threshold) failed, rc %dn",
+			__func__, rc);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+	return rc;
+}
+
+static int wcd_cpe_send_param_opmode(struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		void *data, struct cpe_lsm_ids *ids)
+{
+	struct snd_lsm_detect_mode *opmode_d;
+	struct cpe_lsm_param_opmode opmode_cmd;
+	struct cmi_hdr *msg_hdr = &opmode_cmd.hdr;
+	struct cpe_param_data *param_d =
+				&opmode_cmd.param;
+	int rc;
+
+	memset(&opmode_cmd, 0, sizeof(opmode_cmd));
+	opmode_d = (struct snd_lsm_detect_mode *) data;
+	if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+				session->id,
+				CPE_CMD_OPMODE_PLD_SIZE,
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		rc = -EINVAL;
+		goto err_ret;
+	}
+
+	wcd_cpe_set_param_data(param_d, ids,
+			       CPE_OPMODE_PARAM_SIZE,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	opmode_cmd.minor_version = 1;
+	if (opmode_d->mode == LSM_MODE_KEYWORD_ONLY_DETECTION)
+		opmode_cmd.mode = 1;
+	else
+		opmode_cmd.mode = 3;
+
+	if (opmode_d->detect_failure)
+		opmode_cmd.mode |= 0x04;
+
+	opmode_cmd.reserved = 0;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, &opmode_cmd);
+	if (unlikely(rc))
+		dev_err(core->dev,
+			"%s: set_param(operation_mode) failed, rc %dn",
+			__func__, rc);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+	return rc;
+}
+
+static int wcd_cpe_send_param_gain(struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		void *data, struct cpe_lsm_ids *ids)
+{
+	struct snd_lsm_gain *gain_d;
+	struct cpe_lsm_param_gain gain_cmd;
+	struct cmi_hdr *msg_hdr = &gain_cmd.hdr;
+	struct cpe_param_data *param_d =
+				&gain_cmd.param;
+	int rc;
+
+	memset(&gain_cmd, 0, sizeof(gain_cmd));
+	gain_d = (struct snd_lsm_gain *) data;
+	if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+				session->id,
+				CPE_CMD_GAIN_PLD_SIZE,
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		rc = -EINVAL;
+		goto err_ret;
+	}
+
+	wcd_cpe_set_param_data(param_d, ids,
+			       CPE_GAIN_PARAM_SIZE,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	gain_cmd.minor_version = 1;
+	gain_cmd.gain = gain_d->gain;
+	gain_cmd.reserved = 0;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, &gain_cmd);
+	if (unlikely(rc))
+		dev_err(core->dev,
+			"%s: set_param(lsm_gain) failed, rc %dn",
+			__func__, rc);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+	return rc;
+}
+
+static int wcd_cpe_send_param_connectport(struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		void *data, struct cpe_lsm_ids *ids, u16 port_id)
+{
+	struct cpe_lsm_param_connectport con_port_cmd;
+	struct cmi_hdr *msg_hdr = &con_port_cmd.hdr;
+	struct cpe_param_data *param_d =
+				&con_port_cmd.param;
+	int rc;
+
+	memset(&con_port_cmd, 0, sizeof(con_port_cmd));
+	if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+				session->id,
+				CPE_CMD_CONNECTPORT_PLD_SIZE,
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		rc = -EINVAL;
+		goto err_ret;
+	}
+
+	wcd_cpe_set_param_data(param_d, ids,
+			       CPE_CONNECTPORT_PARAM_SIZE,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	con_port_cmd.minor_version = 1;
+	con_port_cmd.afe_port_id = port_id;
+	con_port_cmd.reserved = 0;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, &con_port_cmd);
+	if (unlikely(rc))
+		dev_err(core->dev,
+			"%s: set_param(connect_port) failed, rc %dn",
+			__func__, rc);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+	return rc;
+}
+
+static int wcd_cpe_send_param_conf_levels(
+		struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		struct cpe_lsm_ids *ids)
+{
+	struct cpe_lsm_conf_level conf_level_data;
+	struct cmi_hdr *hdr = &(conf_level_data.hdr);
+	struct cpe_param_data *param_d = &(conf_level_data.param);
+	u8 pld_size = 0;
+	u8 pad_bytes = 0;
+	void *message;
+	int ret = 0;
+
+	memset(&conf_level_data, 0, sizeof(conf_level_data));
+
+	pld_size = (sizeof(struct cpe_lsm_conf_level) - sizeof(struct cmi_hdr));
+	pld_size += session->num_confidence_levels;
+	pad_bytes = ((4 - (pld_size % 4)) % 4);
+	pld_size += pad_bytes;
+
+	fill_cmi_header(hdr, session->id, CMI_CPE_LSM_SERVICE_ID,
+			false, pld_size,
+			CPE_LSM_SESSION_CMD_SET_PARAMS_V2, false);
+
+	wcd_cpe_set_param_data(param_d, ids,
+			       pld_size - sizeof(struct cpe_param_data),
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	conf_level_data.num_active_models = session->num_confidence_levels;
+
+	message = kzalloc(sizeof(struct cpe_lsm_conf_level) +
+			   conf_level_data.num_active_models + pad_bytes,
+			   GFP_KERNEL);
+	if (!message) {
+		pr_err("%s: no memory for conf_level\n", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(message, &conf_level_data,
+	       sizeof(struct cpe_lsm_conf_level));
+	memcpy(((u8 *) message) + sizeof(struct cpe_lsm_conf_level),
+		session->conf_levels, conf_level_data.num_active_models);
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, message);
+	if (ret)
+		pr_err("%s: lsm_set_conf_levels failed, err = %d\n",
+			__func__, ret);
+	kfree(message);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+static int wcd_cpe_send_param_snd_model(struct wcd_cpe_core *core,
+	struct cpe_lsm_session *session, struct cpe_lsm_ids *ids)
+{
+	int ret = 0;
+	struct cmi_obm_msg obm_msg;
+	struct cpe_param_data *param_d;
+
+
+	ret = fill_cmi_header(&obm_msg.hdr, session->id,
+			CMI_CPE_LSM_SERVICE_ID, 0, 20,
+			CPE_LSM_SESSION_CMD_SET_PARAMS_V2, true);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: Invalid parameters, rc = %d\n",
+			__func__, ret);
+		goto err_ret;
+	}
+
+	obm_msg.pld.version = 0;
+	obm_msg.pld.size = session->snd_model_size;
+	obm_msg.pld.data_ptr.kvaddr = session->snd_model_data;
+	obm_msg.pld.mem_handle = session->lsm_mem_handle;
+
+	param_d = (struct cpe_param_data *) session->snd_model_data;
+	wcd_cpe_set_param_data(param_d, ids,
+			(session->snd_model_size - sizeof(*param_d)),
+			CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg);
+	if (ret)
+		dev_err(core->dev,
+			"%s: snd_model_register failed, %d\n",
+			__func__, ret);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+err_ret:
+	return ret;
+}
+
+static int wcd_cpe_send_param_dereg_model(
+	struct wcd_cpe_core *core,
+	struct cpe_lsm_session *session,
+	struct cpe_lsm_ids *ids)
+{
+	struct cmi_hdr *hdr;
+	struct cpe_param_data *param_d;
+	u8 *message;
+	u32 pld_size;
+	int rc = 0;
+
+	pld_size = sizeof(*hdr) + sizeof(*param_d);
+
+	message = kzalloc(pld_size, GFP_KERNEL);
+	if (!message)
+		return -ENOMEM;
+
+	hdr = (struct cmi_hdr *) message;
+	param_d = (struct cpe_param_data *)
+			(((u8 *) message) + sizeof(*hdr));
+
+	if (fill_lsm_cmd_header_v0_inband(hdr,
+				session->id,
+				sizeof(*param_d),
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		rc = -EINVAL;
+		goto err_ret;
+	}
+	wcd_cpe_set_param_data(param_d, ids, 0,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, message);
+	if (rc)
+		dev_err(core->dev,
+			"%s: snd_model_deregister failed, %d\n",
+			__func__, rc);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+	kfree(message);
+	return rc;
+}
+
+static int wcd_cpe_send_custom_param(
+	struct wcd_cpe_core *core,
+	struct cpe_lsm_session *session,
+	void *data, u32 msg_size)
+{
+	u8 *msg;
+	struct cmi_hdr *hdr;
+	u8 *msg_pld;
+	int rc;
+
+	if (msg_size > CMI_INBAND_MESSAGE_SIZE) {
+		dev_err(core->dev,
+			"%s: out of band custom params not supported\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	msg = kzalloc(sizeof(*hdr) + msg_size, GFP_KERNEL);
+	if (!msg)
+		return -ENOMEM;
+
+	hdr = (struct cmi_hdr *) msg;
+	msg_pld = msg + sizeof(struct cmi_hdr);
+
+	if (fill_lsm_cmd_header_v0_inband(hdr,
+				session->id,
+				msg_size,
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		rc = -EINVAL;
+		goto err_ret;
+	}
+
+	memcpy(msg_pld, data, msg_size);
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	rc = wcd_cpe_cmi_send_lsm_msg(core, session, msg);
+	if (rc)
+		dev_err(core->dev,
+			"%s: custom params send failed, err = %d\n",
+			 __func__, rc);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+err_ret:
+	kfree(msg);
+	return rc;
+}
+
+static int wcd_cpe_set_one_param(void *core_handle,
+	struct cpe_lsm_session *session, struct lsm_params_info *p_info,
+	void *data, enum LSM_PARAM_TYPE param_type)
+{
+	struct wcd_cpe_core *core = core_handle;
+	int rc = 0;
+	struct cpe_lsm_ids ids;
+
+	memset(&ids, 0, sizeof(ids));
+	ids.module_id = p_info->module_id;
+	ids.param_id = p_info->param_id;
+
+	switch (param_type) {
+	case LSM_ENDPOINT_DETECT_THRESHOLD:
+		rc = wcd_cpe_send_param_epd_thres(core, session,
+						data, &ids);
+		break;
+	case LSM_OPERATION_MODE: {
+		struct cpe_lsm_ids connectport_ids;
+
+		rc = wcd_cpe_send_param_opmode(core, session,
+					data, &ids);
+		if (rc)
+			break;
+
+		connectport_ids.module_id = LSM_MODULE_ID_FRAMEWORK;
+		connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+		rc = wcd_cpe_send_param_connectport(core, session, NULL,
+				       &connectport_ids, CPE_AFE_PORT_1_TX);
+		if (rc)
+			dev_err(core->dev,
+				"%s: send_param_connectport failed, err %d\n",
+				__func__, rc);
+		break;
+	}
+	case LSM_GAIN:
+		rc = wcd_cpe_send_param_gain(core, session, data, &ids);
+		break;
+	case LSM_MIN_CONFIDENCE_LEVELS:
+		rc = wcd_cpe_send_param_conf_levels(core, session, &ids);
+		break;
+	case LSM_REG_SND_MODEL:
+		rc = wcd_cpe_send_param_snd_model(core, session, &ids);
+		break;
+	case LSM_DEREG_SND_MODEL:
+		rc = wcd_cpe_send_param_dereg_model(core, session, &ids);
+		break;
+	case LSM_CUSTOM_PARAMS:
+		rc = wcd_cpe_send_custom_param(core, session,
+					       data, p_info->param_size);
+		break;
+	default:
+		pr_err("%s: wrong param_type 0x%x\n",
+			__func__, p_info->param_type);
+	}
+
+	if (rc)
+		dev_err(core->dev,
+			"%s: send_param(%d) failed, err %d\n",
+			 __func__, p_info->param_type, rc);
+	return rc;
+}
+
+/*
+ * wcd_cpe_lsm_set_params: set the parameters for lsm service
+ * @core: handle to cpe core
+ * @session: session for which the parameters are to be set
+ * @detect_mode: mode for detection
+ * @detect_failure: flag indicating failure detection enabled/disabled
+ *
+ */
+static int wcd_cpe_lsm_set_params(struct wcd_cpe_core *core,
+	struct cpe_lsm_session *session,
+	enum lsm_detection_mode detect_mode, bool detect_failure)
+{
+	struct cpe_lsm_ids ids;
+	struct snd_lsm_detect_mode det_mode;
+
+	int ret = 0;
+
+	/* Send lsm calibration */
+	ret = wcd_cpe_send_lsm_cal(core, session);
+	if (ret) {
+		pr_err("%s: fail to sent acdb cal, err = %d",
+			__func__, ret);
+		goto err_ret;
+	}
+
+	/* Send operation mode */
+	ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP;
+	ids.param_id = CPE_LSM_PARAM_ID_OPERATION_MODE;
+	det_mode.mode = detect_mode;
+	det_mode.detect_failure = detect_failure;
+	ret = wcd_cpe_send_param_opmode(core, session,
+					&det_mode, &ids);
+	if (ret)
+		dev_err(core->dev,
+			"%s: Failed to set opmode, err=%d\n",
+			__func__, ret);
+
+err_ret:
+	return ret;
+}
+
+static int wcd_cpe_lsm_set_data(void *core_handle,
+				struct cpe_lsm_session *session,
+				enum lsm_detection_mode detect_mode,
+				bool detect_failure)
+{
+	struct wcd_cpe_core *core = core_handle;
+	struct cpe_lsm_ids ids;
+	int ret = 0;
+
+	if (session->num_confidence_levels > 0) {
+		ret = wcd_cpe_lsm_set_params(core, session, detect_mode,
+				       detect_failure);
+		if (ret) {
+			dev_err(core->dev,
+				"%s: lsm set params failed, rc = %d\n",
+				__func__, ret);
+			goto err_ret;
+		}
+
+		ids.module_id = CPE_LSM_MODULE_ID_VOICE_WAKEUP;
+		ids.param_id = CPE_LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS;
+		ret = wcd_cpe_send_param_conf_levels(core, session, &ids);
+		if (ret) {
+			dev_err(core->dev,
+				"%s: lsm confidence levels failed, rc = %d\n",
+				__func__, ret);
+			goto err_ret;
+		}
+	} else {
+		dev_dbg(core->dev,
+			"%s: no conf levels to set\n",
+			__func__);
+	}
+
+err_ret:
+	return ret;
+}
+
+/*
+ * wcd_cpe_lsm_reg_snd_model: register the sound model for listen
+ * @session: session for which to register the sound model
+ * @detect_mode: detection mode, user dependent/independent
+ * @detect_failure: flag to indicate if failure detection is enabled
+ *
+ * The memory required for sound model should be pre-allocated on CPE
+ * before this function is invoked.
+ */
+static int wcd_cpe_lsm_reg_snd_model(void *core_handle,
+				 struct cpe_lsm_session *session,
+				 enum lsm_detection_mode detect_mode,
+				 bool detect_failure)
+{
+	int ret = 0;
+	struct cmi_obm_msg obm_msg;
+	struct wcd_cpe_core *core = core_handle;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	ret = wcd_cpe_lsm_set_data(core_handle, session,
+				   detect_mode, detect_failure);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: fail to set lsm data, err = %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	ret = fill_cmi_header(&obm_msg.hdr, session->id,
+			CMI_CPE_LSM_SERVICE_ID, 0, 20,
+			CPE_LSM_SESSION_CMD_REGISTER_SOUND_MODEL, true);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: Invalid parameters, rc = %d\n",
+			__func__, ret);
+		goto err_ret;
+	}
+
+	obm_msg.pld.version = 0;
+	obm_msg.pld.size = session->snd_model_size;
+	obm_msg.pld.data_ptr.kvaddr = session->snd_model_data;
+	obm_msg.pld.mem_handle = session->lsm_mem_handle;
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &obm_msg);
+	if (ret)
+		dev_err(core->dev,
+			"%s: snd_model_register failed, %d\n",
+			__func__, ret);
+err_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_lsm_dereg_snd_model: deregister the sound model for listen
+ * @core_handle: handle to cpe core
+ * @session: session for which to deregister the sound model
+ *
+ */
+static int wcd_cpe_lsm_dereg_snd_model(void *core_handle,
+				struct cpe_lsm_session *session)
+{
+	struct cmi_hdr cmd_dereg_snd_model;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_dereg_snd_model, 0, sizeof(cmd_dereg_snd_model));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_dereg_snd_model, session->id,
+			    0, CPE_LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_dereg_snd_model);
+	if (ret)
+		dev_err(core->dev,
+			"%s: failed to send dereg_snd_model cmd\n",
+			__func__);
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_lsm_get_afe_out_port_id: get afe output port id
+ * @core_handle: handle to the CPE core
+ * @session: session for which port id needs to get
+ */
+static int wcd_cpe_lsm_get_afe_out_port_id(void *core_handle,
+					   struct cpe_lsm_session *session)
+{
+	struct wcd_cpe_core *core = core_handle;
+	struct snd_soc_codec *codec;
+	int rc = 0;
+
+	if (!core || !core->codec) {
+		pr_err("%s: Invalid handle to %s\n",
+			__func__,
+			(!core) ? "core" : "codec");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (!session) {
+		dev_err(core->dev, "%s: Invalid session\n",
+			__func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (!core->cpe_cdc_cb ||
+		!core->cpe_cdc_cb->get_afe_out_port_id) {
+		session->afe_out_port_id = WCD_CPE_AFE_OUT_PORT_2;
+		dev_dbg(core->dev,
+			"%s: callback not defined, default port_id = %d\n",
+			__func__, session->afe_out_port_id);
+		goto done;
+	}
+
+	codec = core->codec;
+	rc = core->cpe_cdc_cb->get_afe_out_port_id(codec,
+						   &session->afe_out_port_id);
+	if (rc) {
+		dev_err(core->dev,
+			"%s: failed to get port id, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+	dev_dbg(core->dev, "%s: port_id: %d\n", __func__,
+		session->afe_out_port_id);
+
+done:
+	return rc;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_start: send the start command to lsm
+ * @core_handle: handle to the CPE core
+ * @session: session for which start command to be sent
+ *
+ */
+static int wcd_cpe_cmd_lsm_start(void *core_handle,
+			struct cpe_lsm_session *session)
+{
+	struct cmi_hdr cmd_lsm_start;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_lsm_start, 0, sizeof(struct cmi_hdr));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_start, session->id, 0,
+					  CPE_LSM_SESSION_CMD_START)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_start);
+	if (ret)
+		dev_err(core->dev, "failed to send lsm_start cmd\n");
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_cmd_lsm_stop: send the stop command for LSM service
+ * @core_handle: handle to the cpe core
+ * @session: session for which stop command to be sent
+ *
+ */
+static int wcd_cpe_cmd_lsm_stop(void *core_handle,
+		struct cpe_lsm_session *session)
+{
+	struct cmi_hdr cmd_lsm_stop;
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session,
+					   __func__);
+	if (ret)
+		return ret;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&cmd_lsm_stop, 0, sizeof(struct cmi_hdr));
+	if (fill_lsm_cmd_header_v0_inband(&cmd_lsm_stop, session->id, 0,
+					  CPE_LSM_SESSION_CMD_STOP)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cmd_lsm_stop);
+	if (ret)
+		dev_err(core->dev,
+			"%s: failed to send lsm_stop cmd\n",
+			__func__);
+end_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+
+}
+
+/*
+ * wcd_cpe_alloc_lsm_session: allocate a lsm session
+ * @core: handle to wcd_cpe_core
+ * @lsm_priv_d: lsm private data
+ */
+static struct cpe_lsm_session *wcd_cpe_alloc_lsm_session(
+	void *core_handle, void *client_data,
+	void (*event_cb)(void *, u8, u8, u8 *))
+{
+	struct cpe_lsm_session *session;
+	int i, session_id = -1;
+	struct wcd_cpe_core *core = core_handle;
+	bool afe_register_service = false;
+	int ret = 0;
+
+	/*
+	 * Even if multiple listen sessions can be
+	 * allocated, the AFE service registration
+	 * should be done only once as CPE can only
+	 * have one instance of AFE service.
+	 *
+	 * If this is the first session to be allocated,
+	 * only then register the afe service.
+	 */
+	if (!wcd_cpe_lsm_session_active())
+		afe_register_service = true;
+
+	for (i = 1; i <= WCD_CPE_LSM_MAX_SESSIONS; i++) {
+		if (!lsm_sessions[i]) {
+			session_id = i;
+			break;
+		}
+	}
+
+	if (session_id < 0) {
+		dev_err(core->dev,
+			"%s: max allowed sessions already allocated\n",
+			__func__);
+		return NULL;
+	}
+
+	ret = wcd_cpe_vote(core, true);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: Failed to enable cpe, err = %d\n",
+			__func__, ret);
+		return NULL;
+	}
+
+	session = kzalloc(sizeof(struct cpe_lsm_session), GFP_KERNEL);
+	if (!session)
+		goto err_session_alloc;
+
+	session->id = session_id;
+	session->event_cb = event_cb;
+	session->cmi_reg_handle = cmi_register(wcd_cpe_cmi_lsm_callback,
+						CMI_CPE_LSM_SERVICE_ID);
+	if (!session->cmi_reg_handle) {
+		dev_err(core->dev,
+			"%s: Failed to register LSM service with CMI\n",
+			__func__);
+		goto err_ret;
+	}
+	session->priv_d = client_data;
+	mutex_init(&session->lsm_lock);
+	if (afe_register_service) {
+		/* Register for AFE Service */
+		core->cmi_afe_handle = cmi_register(wcd_cpe_cmi_afe_cb,
+						CMI_CPE_AFE_SERVICE_ID);
+		wcd_cpe_initialize_afe_port_data();
+		if (!core->cmi_afe_handle) {
+			dev_err(core->dev,
+				"%s: Failed to register AFE service with CMI\n",
+				__func__);
+			goto err_afe_svc_reg;
+		}
+
+		/* Once AFE service is registered, send the mode command */
+		ret = wcd_cpe_afe_svc_cmd_mode(core,
+				AFE_SVC_EXPLICIT_PORT_START);
+		if (ret)
+			goto err_afe_mode_cmd;
+	}
+
+	session->lsm_mem_handle = 0;
+	init_completion(&session->cmd_comp);
+
+	lsm_sessions[session_id] = session;
+	return session;
+
+err_afe_mode_cmd:
+	cmi_deregister(core->cmi_afe_handle);
+
+err_afe_svc_reg:
+	cmi_deregister(session->cmi_reg_handle);
+	mutex_destroy(&session->lsm_lock);
+
+err_ret:
+	kfree(session);
+
+err_session_alloc:
+	wcd_cpe_vote(core, false);
+	return NULL;
+}
+
+/*
+ * wcd_cpe_lsm_config_lab_latency: send lab latency value
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session
+ * @latency: the value of latency for lab setup in msec
+ */
+static int wcd_cpe_lsm_config_lab_latency(
+		struct wcd_cpe_core *core,
+		struct cpe_lsm_session *session,
+		u32 latency)
+{
+	int ret = 0, pld_size = CPE_PARAM_LSM_LAB_LATENCY_SIZE;
+	struct cpe_lsm_lab_latency_config cpe_lab_latency;
+	struct cpe_lsm_lab_config *lab_lat = &cpe_lab_latency.latency_cfg;
+	struct cpe_param_data *param_d = &lab_lat->param;
+	struct cpe_lsm_ids ids;
+
+	if (fill_lsm_cmd_header_v0_inband(&cpe_lab_latency.hdr, session->id,
+		(u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		pr_err("%s: Failed to create header\n", __func__);
+		return -EINVAL;
+	}
+	if (latency == 0x00 || latency > WCD_CPE_LAB_MAX_LATENCY) {
+		pr_err("%s: Invalid latency %u\n",
+			__func__, latency);
+		return -EINVAL;
+	}
+
+	lab_lat->latency = latency;
+	lab_lat->minor_ver = 1;
+	ids.module_id = CPE_LSM_MODULE_ID_LAB;
+	ids.param_id = CPE_LSM_PARAM_ID_LAB_CONFIG;
+	wcd_cpe_set_param_data(param_d, &ids,
+			       PARAM_SIZE_LSM_LATENCY_SIZE,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	pr_debug("%s: Module 0x%x Param 0x%x size %zu pld_size 0x%x\n",
+		  __func__, lab_lat->param.module_id,
+		 lab_lat->param.param_id, PARAM_SIZE_LSM_LATENCY_SIZE,
+		 pld_size);
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_latency);
+	if (ret != 0)
+		pr_err("%s: lsm_set_params failed, error = %d\n",
+		       __func__, ret);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+	return ret;
+}
+
+/*
+ * wcd_cpe_lsm_lab_control: enable/disable lab
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session
+ * @enable: Indicates whether to enable / disable lab
+ */
+static int wcd_cpe_lsm_lab_control(
+		void *core_handle,
+		struct cpe_lsm_session *session,
+		bool enable)
+{
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0, pld_size = CPE_PARAM_SIZE_LSM_LAB_CONTROL;
+	struct cpe_lsm_control_lab cpe_lab_enable;
+	struct cpe_lsm_lab_enable *lab_enable = &cpe_lab_enable.lab_enable;
+	struct cpe_param_data *param_d = &lab_enable->param;
+	struct cpe_lsm_ids ids;
+
+	pr_debug("%s: enter payload_size = %d Enable %d\n",
+		 __func__, pld_size, enable);
+
+	if (fill_lsm_cmd_header_v0_inband(&cpe_lab_enable.hdr, session->id,
+		(u8) pld_size, CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		return -EINVAL;
+	}
+	if (enable == true)
+		lab_enable->enable = 1;
+	else
+		lab_enable->enable = 0;
+
+	ids.module_id = CPE_LSM_MODULE_ID_LAB;
+	ids.param_id = CPE_LSM_PARAM_ID_LAB_ENABLE;
+	wcd_cpe_set_param_data(param_d, &ids,
+			PARAM_SIZE_LSM_CONTROL_SIZE,
+			CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	pr_debug("%s: Module 0x%x, Param 0x%x size %zu pld_size 0x%x\n",
+		 __func__, lab_enable->param.module_id,
+		 lab_enable->param.param_id, PARAM_SIZE_LSM_CONTROL_SIZE,
+		 pld_size);
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &cpe_lab_enable);
+	if (ret != 0) {
+		pr_err("%s: lsm_set_params failed, error = %d\n",
+			__func__, ret);
+		WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+		goto done;
+	}
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+	if (lab_enable->enable)
+		ret = wcd_cpe_lsm_config_lab_latency(core, session,
+					       WCD_CPE_LAB_MAX_LATENCY);
+done:
+	return ret;
+}
+
+/*
+ * wcd_cpe_lsm_eob: stop lab
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session to be deallocated
+ */
+static int wcd_cpe_lsm_eob(
+			struct wcd_cpe_core *core,
+			struct cpe_lsm_session *session)
+{
+	int ret = 0;
+	struct cmi_hdr lab_eob;
+
+	if (fill_lsm_cmd_header_v0_inband(&lab_eob, session->id,
+		0, CPE_LSM_SESSION_CMD_EOB)) {
+		return -EINVAL;
+	}
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &lab_eob);
+	if (ret != 0)
+		pr_err("%s: lsm_set_params failed\n", __func__);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+
+	return ret;
+}
+
+/*
+ * wcd_cpe_dealloc_lsm_session: deallocate lsm session
+ * @core: handle to wcd_cpe_core
+ * @session: lsm session to be deallocated
+ */
+static int wcd_cpe_dealloc_lsm_session(void *core_handle,
+			struct cpe_lsm_session *session)
+{
+	struct wcd_cpe_core *core = core_handle;
+	int ret = 0;
+
+	if (!session) {
+		dev_err(core->dev,
+			"%s: Invalid lsm session\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_dbg(core->dev, "%s: session %d being deallocated\n",
+		__func__, session->id);
+	if (session->id > WCD_CPE_LSM_MAX_SESSIONS) {
+		dev_err(core->dev,
+			"%s: Wrong session id %d max allowed = %d\n",
+			__func__, session->id,
+			WCD_CPE_LSM_MAX_SESSIONS);
+		return -EINVAL;
+	}
+
+	cmi_deregister(session->cmi_reg_handle);
+	mutex_destroy(&session->lsm_lock);
+	lsm_sessions[session->id] = NULL;
+	kfree(session);
+
+	if (!wcd_cpe_lsm_session_active()) {
+		cmi_deregister(core->cmi_afe_handle);
+		core->cmi_afe_handle = NULL;
+		wcd_cpe_deinitialize_afe_port_data();
+	}
+
+	ret = wcd_cpe_vote(core, false);
+	if (ret)
+		dev_dbg(core->dev,
+			"%s: Failed to un-vote cpe, err = %d\n",
+			__func__, ret);
+
+	return ret;
+}
+
+static int wcd_cpe_lab_ch_setup(void *core_handle,
+		struct cpe_lsm_session *session,
+		enum wcd_cpe_event event)
+{
+	struct wcd_cpe_core *core = core_handle;
+	struct snd_soc_codec *codec;
+	int rc = 0;
+	u8 cpe_intr_bits;
+
+	if (!core || !core->codec) {
+		pr_err("%s: Invalid handle to %s\n",
+			__func__,
+			(!core) ? "core" : "codec");
+		rc = EINVAL;
+		goto done;
+	}
+
+	if (!core->cpe_cdc_cb ||
+	    !core->cpe_cdc_cb->cdc_ext_clk ||
+	    !core->cpe_cdc_cb->lab_cdc_ch_ctl) {
+		dev_err(core->dev,
+			"%s: Invalid codec callbacks\n",
+			__func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	codec = core->codec;
+	dev_dbg(core->dev,
+		"%s: event = 0x%x\n",
+		__func__, event);
+
+	switch (event) {
+	case WCD_CPE_PRE_ENABLE:
+		rc = core->cpe_cdc_cb->cdc_ext_clk(codec, true, false);
+		if (rc) {
+			dev_err(core->dev,
+				"%s: failed to enable cdc clk, err = %d\n",
+				__func__, rc);
+			goto done;
+		}
+
+		rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec,
+						      true);
+		if (rc) {
+			dev_err(core->dev,
+				"%s: failed to enable cdc port, err = %d\n",
+				__func__, rc);
+			rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false);
+			goto done;
+		}
+
+		break;
+
+	case WCD_CPE_POST_ENABLE:
+		rc = cpe_svc_toggle_lab(core->cpe_handle, true);
+		if (rc)
+			dev_err(core->dev,
+			"%s: Failed to enable lab\n", __func__);
+		break;
+
+	case WCD_CPE_PRE_DISABLE:
+		/*
+		 * Mask the non-fatal interrupts in CPE as they will
+		 * be generated during lab teardown and may flood.
+		 */
+		cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF);
+		if (CPE_ERR_IRQ_CB(core))
+			core->cpe_cdc_cb->cpe_err_irq_control(
+						core->codec,
+						CPE_ERR_IRQ_MASK,
+						&cpe_intr_bits);
+
+		rc = core->cpe_cdc_cb->lab_cdc_ch_ctl(codec,
+						      false);
+		if (rc)
+			dev_err(core->dev,
+				"%s: failed to disable cdc port, err = %d\n",
+				__func__, rc);
+		break;
+
+	case WCD_CPE_POST_DISABLE:
+		rc = wcd_cpe_lsm_eob(core, session);
+		if (rc)
+			dev_err(core->dev,
+				"%s: eob send failed, err = %d\n",
+				__func__, rc);
+
+		/* Continue teardown even if eob failed */
+		rc = cpe_svc_toggle_lab(core->cpe_handle, false);
+		if (rc)
+			dev_err(core->dev,
+			"%s: Failed to disable lab\n", __func__);
+
+		/* Continue with disabling even if toggle lab fails */
+		rc = core->cpe_cdc_cb->cdc_ext_clk(codec, false, false);
+		if (rc)
+			dev_err(core->dev,
+				"%s: failed to disable cdc clk, err = %d\n",
+				__func__, rc);
+
+		/* Unmask non-fatal CPE interrupts */
+		cpe_intr_bits = ~(core->irq_info.cpe_fatal_irqs & 0xFF);
+		if (CPE_ERR_IRQ_CB(core))
+			core->cpe_cdc_cb->cpe_err_irq_control(
+						core->codec,
+						CPE_ERR_IRQ_UNMASK,
+						&cpe_intr_bits);
+		break;
+
+	default:
+		dev_err(core->dev,
+			"%s: Invalid event 0x%x\n",
+			__func__, event);
+		rc = -EINVAL;
+		break;
+	}
+
+done:
+	return rc;
+}
+
+static int wcd_cpe_lsm_set_fmt_cfg(void *core_handle,
+			struct cpe_lsm_session *session)
+{
+	int ret;
+	struct cpe_lsm_output_format_cfg out_fmt_cfg;
+	struct wcd_cpe_core *core = core_handle;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session, __func__);
+	if (ret)
+		goto done;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+
+	memset(&out_fmt_cfg, 0, sizeof(out_fmt_cfg));
+	if (fill_lsm_cmd_header_v0_inband(&out_fmt_cfg.hdr,
+			session->id, OUT_FMT_CFG_CMD_PAYLOAD_SIZE,
+			CPE_LSM_SESSION_CMD_TX_BUFF_OUTPUT_CONFIG)) {
+		ret = -EINVAL;
+		goto err_ret;
+	}
+
+	out_fmt_cfg.format = session->out_fmt_cfg.format;
+	out_fmt_cfg.packing = session->out_fmt_cfg.pack_mode;
+	out_fmt_cfg.data_path_events = session->out_fmt_cfg.data_path_events;
+
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &out_fmt_cfg);
+	if (ret)
+		dev_err(core->dev,
+			"%s: lsm_set_output_format_cfg failed, err = %d\n",
+			__func__, ret);
+
+err_ret:
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+	return ret;
+}
+
+static void wcd_cpe_snd_model_offset(void *core_handle,
+		struct cpe_lsm_session *session, size_t *offset)
+{
+	*offset = sizeof(struct cpe_param_data);
+}
+
+static int wcd_cpe_lsm_set_media_fmt_params(void *core_handle,
+					  struct cpe_lsm_session *session,
+					  struct lsm_hw_params *param)
+{
+	struct cpe_lsm_media_fmt_param media_fmt;
+	struct cmi_hdr *msg_hdr = &media_fmt.hdr;
+	struct wcd_cpe_core *core = core_handle;
+	struct cpe_param_data *param_d = &media_fmt.param;
+	struct cpe_lsm_ids ids;
+	int ret;
+
+	memset(&media_fmt, 0, sizeof(media_fmt));
+	if (fill_lsm_cmd_header_v0_inband(msg_hdr,
+				session->id,
+				CPE_MEDIA_FMT_PLD_SIZE,
+				CPE_LSM_SESSION_CMD_SET_PARAMS_V2)) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	memset(&ids, 0, sizeof(ids));
+	ids.module_id = CPE_LSM_MODULE_FRAMEWORK;
+	ids.param_id = CPE_LSM_PARAM_ID_MEDIA_FMT;
+
+	wcd_cpe_set_param_data(param_d, &ids, CPE_MEDIA_FMT_PARAM_SIZE,
+			       CPE_LSM_SESSION_CMD_SET_PARAMS_V2);
+
+	media_fmt.minor_version = 1;
+	media_fmt.sample_rate = param->sample_rate;
+	media_fmt.num_channels = param->num_chs;
+	media_fmt.bit_width = param->bit_width;
+
+	WCD_CPE_GRAB_LOCK(&session->lsm_lock, "lsm");
+	ret = wcd_cpe_cmi_send_lsm_msg(core, session, &media_fmt);
+	if (ret)
+		dev_err(core->dev,
+			"%s: Set_param(media_format) failed, err=%d\n",
+			__func__, ret);
+	WCD_CPE_REL_LOCK(&session->lsm_lock, "lsm");
+done:
+	return ret;
+}
+
+static int wcd_cpe_lsm_set_port(void *core_handle,
+				struct cpe_lsm_session *session, void *data)
+{
+	u32 port_id;
+	int ret;
+	struct cpe_lsm_ids ids;
+	struct wcd_cpe_core *core = core_handle;
+
+	ret = wcd_cpe_is_valid_lsm_session(core, session, __func__);
+	if (ret)
+		goto done;
+
+	if (!data) {
+		dev_err(core->dev, "%s: data is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	port_id = *(u32 *)data;
+	dev_dbg(core->dev, "%s: port_id: %d\n", __func__, port_id);
+
+	memset(&ids, 0, sizeof(ids));
+	ids.module_id = LSM_MODULE_ID_FRAMEWORK;
+	ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+	ret = wcd_cpe_send_param_connectport(core, session, NULL,
+					     &ids, port_id);
+	if (ret)
+		dev_err(core->dev,
+			"%s: send_param_connectport failed, err %d\n",
+			__func__, ret);
+done:
+	return ret;
+}
+
+/*
+ * wcd_cpe_get_lsm_ops: register lsm driver to codec
+ * @lsm_ops: structure with lsm callbacks
+ * @codec: codec to which this lsm driver is registered to
+ */
+int wcd_cpe_get_lsm_ops(struct wcd_cpe_lsm_ops *lsm_ops)
+{
+	lsm_ops->lsm_alloc_session = wcd_cpe_alloc_lsm_session;
+	lsm_ops->lsm_dealloc_session = wcd_cpe_dealloc_lsm_session;
+	lsm_ops->lsm_open_tx = wcd_cpe_cmd_lsm_open_tx;
+	lsm_ops->lsm_close_tx = wcd_cpe_cmd_lsm_close_tx;
+	lsm_ops->lsm_shmem_alloc = wcd_cpe_cmd_lsm_shmem_alloc;
+	lsm_ops->lsm_shmem_dealloc = wcd_cpe_cmd_lsm_shmem_dealloc;
+	lsm_ops->lsm_register_snd_model = wcd_cpe_lsm_reg_snd_model;
+	lsm_ops->lsm_deregister_snd_model = wcd_cpe_lsm_dereg_snd_model;
+	lsm_ops->lsm_get_afe_out_port_id = wcd_cpe_lsm_get_afe_out_port_id;
+	lsm_ops->lsm_start = wcd_cpe_cmd_lsm_start;
+	lsm_ops->lsm_stop = wcd_cpe_cmd_lsm_stop;
+	lsm_ops->lsm_lab_control = wcd_cpe_lsm_lab_control;
+	lsm_ops->lab_ch_setup = wcd_cpe_lab_ch_setup;
+	lsm_ops->lsm_set_data = wcd_cpe_lsm_set_data;
+	lsm_ops->lsm_set_fmt_cfg = wcd_cpe_lsm_set_fmt_cfg;
+	lsm_ops->lsm_set_one_param = wcd_cpe_set_one_param;
+	lsm_ops->lsm_get_snd_model_offset = wcd_cpe_snd_model_offset;
+	lsm_ops->lsm_set_media_fmt_params = wcd_cpe_lsm_set_media_fmt_params;
+	lsm_ops->lsm_set_port = wcd_cpe_lsm_set_port;
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd_cpe_get_lsm_ops);
+
+static int fill_afe_cmd_header(struct cmi_hdr *hdr, u8 port_id,
+				u16 opcode, u8 pld_size,
+				bool obm_flag)
+{
+	CMI_HDR_SET_SESSION(hdr, port_id);
+	CMI_HDR_SET_SERVICE(hdr, CMI_CPE_AFE_SERVICE_ID);
+
+	CMI_HDR_SET_PAYLOAD_SIZE(hdr, pld_size);
+
+	hdr->opcode = opcode;
+
+	if (obm_flag)
+		CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_OUT_BAND);
+	else
+		CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND);
+
+	return 0;
+}
+
+/*
+ * wcd_cpe_cmi_send_afe_msg: send message to AFE service
+ * @core: wcd cpe core handle
+ * @port_cfg: configuration data for the afe port
+ *	      for which this message is to be sent
+ * @message: actual message with header and payload
+ *
+ * Port specific lock needs to be acquired before this
+ * function can be invoked
+ */
+static int wcd_cpe_cmi_send_afe_msg(
+	struct wcd_cpe_core *core,
+	struct wcd_cmi_afe_port_data *port_d,
+	void *message)
+{
+	int ret = 0;
+	struct cmi_hdr *hdr = message;
+
+	pr_debug("%s: sending message with opcode 0x%x\n",
+		__func__, hdr->opcode);
+
+	if (unlikely(!wcd_cpe_is_online_state(core))) {
+		dev_err(core->dev, "%s: CPE offline\n", __func__);
+		return 0;
+	}
+
+	if (CMI_HDR_GET_OBM_FLAG(hdr))
+		wcd_cpe_bus_vote_max_bw(core, true);
+
+	ret = cmi_send_msg(message);
+	if (ret) {
+		pr_err("%s: cmd 0x%x send failed, err = %d\n",
+			__func__, hdr->opcode, ret);
+		goto rel_bus_vote;
+	}
+
+	ret = wait_for_completion_timeout(&port_d->afe_cmd_complete,
+					  CMI_CMD_TIMEOUT);
+	if (ret > 0) {
+		pr_debug("%s: command 0x%x, received response 0x%x\n",
+			 __func__, hdr->opcode, port_d->cmd_result);
+		if (port_d->cmd_result == CMI_SHMEM_ALLOC_FAILED)
+			port_d->cmd_result = CPE_ENOMEMORY;
+		if (port_d->cmd_result > 0)
+			pr_err("%s: CPE returned error[%s]\n",
+				__func__, cpe_err_get_err_str(
+				port_d->cmd_result));
+		ret = cpe_err_get_lnx_err_code(port_d->cmd_result);
+		goto rel_bus_vote;
+	} else {
+		pr_err("%s: command 0x%x send timed out\n",
+			__func__, hdr->opcode);
+		ret = -ETIMEDOUT;
+		goto rel_bus_vote;
+	}
+
+rel_bus_vote:
+	reinit_completion(&port_d->afe_cmd_complete);
+
+	if (CMI_HDR_GET_OBM_FLAG(hdr))
+		wcd_cpe_bus_vote_max_bw(core, false);
+
+	return ret;
+}
+
+
+
+/*
+ * wcd_cpe_afe_shmem_alloc: allocate the cpe memory for afe service
+ * @core: handle to cpe core
+ * @port_cfg: configuration data for the port which needs
+ *	      memory to be allocated on CPE
+ * @size: size of the memory to be allocated
+ */
+static int wcd_cpe_afe_shmem_alloc(
+	struct wcd_cpe_core *core,
+	struct wcd_cmi_afe_port_data *port_d,
+	u32 size)
+{
+	struct cpe_cmd_shmem_alloc cmd_shmem_alloc;
+	int ret = 0;
+
+	pr_debug("%s: enter: size = %d\n", __func__, size);
+
+	memset(&cmd_shmem_alloc, 0, sizeof(cmd_shmem_alloc));
+	if (fill_afe_cmd_header(&cmd_shmem_alloc.hdr, port_d->port_id,
+			    CPE_AFE_PORT_CMD_SHARED_MEM_ALLOC,
+			    SHMEM_ALLOC_CMD_PLD_SIZE, false)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	cmd_shmem_alloc.size = size;
+
+	ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_shmem_alloc);
+	if (ret) {
+		pr_err("%s: afe_shmem_alloc fail,ret = %d\n",
+			__func__, ret);
+		goto end_ret;
+	}
+
+	pr_debug("%s: completed %s, mem_handle = 0x%x\n",
+		__func__, "CPE_AFE_CMD_SHARED_MEM_ALLOC",
+		port_d->mem_handle);
+
+end_ret:
+	return ret;
+}
+
+/*
+ * wcd_cpe_afe_shmem_dealloc: deallocate the cpe memory for
+ *			      afe service
+ * @core: handle to cpe core
+ * @port_d: configuration data for the port which needs
+ *	      memory to be deallocated on CPE
+ * The memory handle to be de-allocated is saved in the
+ * port configuration data
+ */
+static int wcd_cpe_afe_shmem_dealloc(
+	struct wcd_cpe_core *core,
+	struct wcd_cmi_afe_port_data *port_d)
+{
+	struct cpe_cmd_shmem_dealloc cmd_dealloc;
+	int ret = 0;
+
+	pr_debug("%s: enter, port_id = %d\n",
+		 __func__, port_d->port_id);
+
+	memset(&cmd_dealloc, 0, sizeof(cmd_dealloc));
+	if (fill_afe_cmd_header(&cmd_dealloc.hdr, port_d->port_id,
+				CPE_AFE_PORT_CMD_SHARED_MEM_DEALLOC,
+				SHMEM_DEALLOC_CMD_PLD_SIZE, false)) {
+		ret = -EINVAL;
+		goto end_ret;
+	}
+
+	cmd_dealloc.addr = port_d->mem_handle;
+	ret = wcd_cpe_cmi_send_afe_msg(core, port_d, &cmd_dealloc);
+	if (ret) {
+		pr_err("failed to send shmem_dealloc cmd\n");
+		goto end_ret;
+	}
+	memset(&port_d->mem_handle, 0,
+	       sizeof(port_d->mem_handle));
+
+end_ret:
+	return ret;
+}
+
+/*
+ * wcd_cpe_send_afe_cal: send the acdb calibration to AFE port
+ * @core: handle to cpe core
+ * @port_d: configuration data for the port for which the
+ *	      calibration needs to be appplied
+ */
+static int wcd_cpe_send_afe_cal(void *core_handle,
+		struct wcd_cmi_afe_port_data *port_d)
+{
+
+	struct cal_block_data *afe_cal = NULL;
+	struct wcd_cpe_core *core = core_handle;
+	struct cmi_obm_msg obm_msg;
+	void *inb_msg = NULL;
+	void *msg;
+	int rc = 0;
+	bool is_obm_msg;
+
+	if (core->cal_data[WCD_CPE_LSM_CAL_AFE] == NULL) {
+		pr_err("%s: LSM cal not allocated!\n",
+			__func__);
+		rc = -EINVAL;
+		goto rel_cal_mutex;
+	}
+
+	mutex_lock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock);
+	afe_cal = cal_utils_get_only_cal_block(
+			core->cal_data[WCD_CPE_LSM_CAL_AFE]);
+	if (!afe_cal) {
+		pr_err("%s: failed to get afe cal block\n",
+			__func__);
+		rc = -EINVAL;
+		goto rel_cal_mutex;
+	}
+
+	if (afe_cal->cal_data.size == 0) {
+		dev_dbg(core->dev, "%s: No AFE cal to send\n",
+			__func__);
+		rc = 0;
+		goto rel_cal_mutex;
+	}
+
+	is_obm_msg = (afe_cal->cal_data.size >
+		      CMI_INBAND_MESSAGE_SIZE) ? true : false;
+
+	if (is_obm_msg) {
+		struct cmi_hdr *hdr = &(obm_msg.hdr);
+		struct cmi_obm *pld = &(obm_msg.pld);
+
+		rc = wcd_cpe_afe_shmem_alloc(core, port_d,
+					afe_cal->cal_data.size);
+		if (rc) {
+			dev_err(core->dev,
+				"%s: AFE shmem alloc fail %d\n",
+				__func__, rc);
+			goto rel_cal_mutex;
+		}
+
+		rc = fill_afe_cmd_header(hdr, port_d->port_id,
+					 CPE_AFE_CMD_SET_PARAM,
+					 CPE_AFE_PARAM_PAYLOAD_SIZE,
+					 true);
+		if (rc) {
+			dev_err(core->dev,
+				"%s: invalid params for header, err = %d\n",
+				__func__, rc);
+			wcd_cpe_afe_shmem_dealloc(core, port_d);
+			goto rel_cal_mutex;
+		}
+
+		pld->version = 0;
+		pld->size = afe_cal->cal_data.size;
+		pld->data_ptr.kvaddr = afe_cal->cal_data.kvaddr;
+		pld->mem_handle = port_d->mem_handle;
+		msg = &obm_msg;
+
+	} else {
+		u8 *msg_pld;
+		struct cmi_hdr *hdr;
+
+		inb_msg = kzalloc(sizeof(struct cmi_hdr) +
+					afe_cal->cal_data.size,
+				  GFP_KERNEL);
+		if (!inb_msg) {
+			dev_err(core->dev,
+				"%s: no memory for afe cal inband\n",
+				__func__);
+			rc = -ENOMEM;
+			goto rel_cal_mutex;
+		}
+
+		hdr = (struct cmi_hdr *) inb_msg;
+
+		rc = fill_afe_cmd_header(hdr, port_d->port_id,
+					 CPE_AFE_CMD_SET_PARAM,
+					 CPE_AFE_PARAM_PAYLOAD_SIZE,
+					 false);
+		if (rc) {
+			dev_err(core->dev,
+				"%s: invalid params for header, err = %d\n",
+				__func__, rc);
+			kfree(inb_msg);
+			inb_msg = NULL;
+			goto rel_cal_mutex;
+		}
+
+		msg_pld = ((u8 *) inb_msg) + sizeof(struct cmi_hdr);
+		memcpy(msg_pld, afe_cal->cal_data.kvaddr,
+		       afe_cal->cal_data.size);
+
+		msg = inb_msg;
+	}
+
+	rc = wcd_cpe_cmi_send_afe_msg(core, port_d, msg);
+	if (rc)
+		pr_err("%s: afe cal for listen failed, rc = %d\n",
+			__func__, rc);
+
+	if (is_obm_msg) {
+		wcd_cpe_afe_shmem_dealloc(core, port_d);
+		port_d->mem_handle = 0;
+	} else {
+		kfree(inb_msg);
+		inb_msg = NULL;
+	}
+
+rel_cal_mutex:
+	mutex_unlock(&core->cal_data[WCD_CPE_LSM_CAL_AFE]->lock);
+	return rc;
+}
+
+/*
+ * wcd_cpe_is_valid_port: check validity of afe port id
+ * @core: handle to core to check for validity
+ * @afe_cfg: client provided afe configuration
+ * @func: function name invoking this validity check,
+ *	  used for logging purpose only.
+ */
+static int wcd_cpe_is_valid_port(struct wcd_cpe_core *core,
+		struct wcd_cpe_afe_port_cfg *afe_cfg,
+		const char *func)
+{
+	if (unlikely(IS_ERR_OR_NULL(core))) {
+		pr_err("%s: Invalid core handle\n", func);
+		return -EINVAL;
+	}
+
+	if (afe_cfg->port_id > WCD_CPE_AFE_MAX_PORTS) {
+		dev_err(core->dev,
+			"%s: invalid afe port (%u)\n",
+			func, afe_cfg->port_id);
+		return -EINVAL;
+	}
+
+	dev_dbg(core->dev,
+		"%s: port_id = %u\n",
+		func, afe_cfg->port_id);
+
+	return 0;
+}
+
+static int wcd_cpe_afe_svc_cmd_mode(void *core_handle,
+				    u8 mode)
+{
+	struct cpe_afe_svc_cmd_mode afe_mode;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret;
+
+	afe_port_d = &afe_ports[0];
+	/*
+	 * AFE SVC mode command is for the service and not port
+	 * specific, hence use AFE port as 0 so the command will
+	 * be applied to all AFE ports on CPE.
+	 */
+	afe_port_d->port_id = 0;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+	memset(&afe_mode, 0, sizeof(afe_mode));
+	if (fill_afe_cmd_header(&afe_mode.hdr, afe_port_d->port_id,
+				CPE_AFE_SVC_CMD_LAB_MODE,
+				CPE_AFE_CMD_MODE_PAYLOAD_SIZE,
+				false)) {
+		ret = -EINVAL;
+		goto err_ret;
+	}
+
+	afe_mode.mode = mode;
+
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_mode);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_svc_mode cmd failed, err = %d\n",
+			__func__, ret);
+
+err_ret:
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+	return ret;
+}
+
+static int wcd_cpe_afe_cmd_port_cfg(void *core_handle,
+		struct wcd_cpe_afe_port_cfg *afe_cfg)
+{
+	struct cpe_afe_cmd_port_cfg port_cfg_cmd;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret;
+
+	ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__);
+	if (ret)
+		goto done;
+
+	afe_port_d = &afe_ports[afe_cfg->port_id];
+	afe_port_d->port_id = afe_cfg->port_id;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+	memset(&port_cfg_cmd, 0, sizeof(port_cfg_cmd));
+	if (fill_afe_cmd_header(&port_cfg_cmd.hdr,
+			afe_cfg->port_id,
+			CPE_AFE_PORT_CMD_GENERIC_CONFIG,
+			CPE_AFE_CMD_PORT_CFG_PAYLOAD_SIZE,
+			false)) {
+		ret = -EINVAL;
+		goto err_ret;
+	}
+
+	port_cfg_cmd.bit_width = afe_cfg->bit_width;
+	port_cfg_cmd.num_channels = afe_cfg->num_channels;
+	port_cfg_cmd.sample_rate = afe_cfg->sample_rate;
+
+	if (afe_port_d->port_id == CPE_AFE_PORT_3_TX)
+		port_cfg_cmd.buffer_size = WCD_CPE_EC_PP_BUF_SIZE;
+	else
+		port_cfg_cmd.buffer_size = AFE_OUT_BUF_SIZE(afe_cfg->bit_width,
+							afe_cfg->sample_rate);
+
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &port_cfg_cmd);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_port_config failed, err = %d\n",
+			__func__, ret);
+
+err_ret:
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+done:
+	return ret;
+}
+
+/*
+ * wcd_cpe_afe_set_params: set the parameters for afe port
+ * @afe_cfg: configuration data for the port for which the
+ *	      parameters are to be set
+ */
+static int wcd_cpe_afe_set_params(void *core_handle,
+		struct wcd_cpe_afe_port_cfg *afe_cfg, bool afe_mad_ctl)
+{
+	struct cpe_afe_params afe_params;
+	struct cpe_afe_hw_mad_ctrl *hw_mad_ctrl = &afe_params.hw_mad_ctrl;
+	struct cpe_afe_port_cfg *port_cfg = &afe_params.port_cfg;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret = 0, pld_size = 0;
+
+	ret = wcd_cpe_is_valid_port(core, afe_cfg, __func__);
+	if (ret)
+		return ret;
+
+	afe_port_d = &afe_ports[afe_cfg->port_id];
+	afe_port_d->port_id = afe_cfg->port_id;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+	ret = wcd_cpe_send_afe_cal(core, afe_port_d);
+	if (ret) {
+		dev_err(core->dev,
+			"%s: afe acdb cal send failed, err = %d\n",
+			__func__, ret);
+		goto err_ret;
+	}
+
+	pld_size = CPE_AFE_PARAM_PAYLOAD_SIZE;
+	memset(&afe_params, 0, sizeof(afe_params));
+
+	if (fill_afe_cmd_header(&afe_params.hdr,
+				afe_cfg->port_id,
+				CPE_AFE_CMD_SET_PARAM,
+				(u8) pld_size, false)) {
+		ret = -EINVAL;
+		goto err_ret;
+	}
+
+	hw_mad_ctrl->param.module_id = CPE_AFE_MODULE_HW_MAD;
+	hw_mad_ctrl->param.param_id = CPE_AFE_PARAM_ID_HW_MAD_CTL;
+	hw_mad_ctrl->param.p_size.sr.param_size = PARAM_SIZE_AFE_HW_MAD_CTRL;
+	hw_mad_ctrl->param.p_size.sr.reserved = 0;
+	hw_mad_ctrl->minor_version = 1;
+	hw_mad_ctrl->mad_type = MAD_TYPE_AUDIO;
+	hw_mad_ctrl->mad_enable = afe_mad_ctl;
+
+	port_cfg->param.module_id = CPE_AFE_MODULE_AUDIO_DEV_INTERFACE;
+	port_cfg->param.param_id = CPE_AFE_PARAM_ID_GENERIC_PORT_CONFIG;
+	port_cfg->param.p_size.sr.param_size = PARAM_SIZE_AFE_PORT_CFG;
+	port_cfg->param.p_size.sr.reserved = 0;
+	port_cfg->minor_version = 1;
+	port_cfg->bit_width = afe_cfg->bit_width;
+	port_cfg->num_channels = afe_cfg->num_channels;
+	port_cfg->sample_rate = afe_cfg->sample_rate;
+
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &afe_params);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_port_config failed, err = %d\n",
+			__func__, ret);
+err_ret:
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+	return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_start: send the start command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ *	      to be started.
+ */
+static int wcd_cpe_afe_port_start(void *core_handle,
+			struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+
+	struct cmi_hdr hdr;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+	if (ret)
+		return ret;
+
+	afe_port_d = &afe_ports[port_cfg->port_id];
+	afe_port_d->port_id = port_cfg->port_id;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+	memset(&hdr, 0, sizeof(struct cmi_hdr));
+	fill_afe_cmd_header(&hdr, port_cfg->port_id,
+			    CPE_AFE_PORT_CMD_START,
+			    0, false);
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_port_start cmd failed, err = %d\n",
+			__func__, ret);
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+	return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_stop: send stop command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ *	      to be stopped.
+ */
+static int wcd_cpe_afe_port_stop(void *core_handle,
+	struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+	struct cmi_hdr hdr;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+	if (ret)
+		return ret;
+
+	afe_port_d = &afe_ports[port_cfg->port_id];
+	afe_port_d->port_id = port_cfg->port_id;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+	memset(&hdr, 0, sizeof(hdr));
+	fill_afe_cmd_header(&hdr, port_cfg->port_id,
+			    CPE_AFE_PORT_CMD_STOP,
+			    0, false);
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_stop cmd failed, err = %d\n",
+			__func__, ret);
+
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+	return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_suspend: send suspend command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ *	      to be suspended.
+ */
+static int wcd_cpe_afe_port_suspend(void *core_handle,
+		struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+	struct cmi_hdr hdr;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+	if (ret)
+		return ret;
+
+	afe_port_d = &afe_ports[port_cfg->port_id];
+	afe_port_d->port_id = port_cfg->port_id;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+	memset(&hdr, 0, sizeof(struct cmi_hdr));
+	fill_afe_cmd_header(&hdr, port_cfg->port_id,
+			    CPE_AFE_PORT_CMD_SUSPEND,
+			    0, false);
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_suspend cmd failed, err = %d\n",
+			__func__, ret);
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+	return ret;
+}
+
+/*
+ * wcd_cpe_afe_port_resume: send the resume command to afe service
+ * @core_handle: handle to the cpe core
+ * @port_cfg: configuration data for the afe port which needs
+ *	      to be resumed.
+ */
+static int wcd_cpe_afe_port_resume(void *core_handle,
+		struct wcd_cpe_afe_port_cfg *port_cfg)
+{
+	struct cmi_hdr hdr;
+	struct wcd_cpe_core *core = core_handle;
+	struct wcd_cmi_afe_port_data *afe_port_d;
+	int ret = 0;
+
+	ret = wcd_cpe_is_valid_port(core, port_cfg, __func__);
+	if (ret)
+		return ret;
+
+	afe_port_d = &afe_ports[port_cfg->port_id];
+	afe_port_d->port_id = port_cfg->port_id;
+
+	WCD_CPE_GRAB_LOCK(&afe_port_d->afe_lock, "afe");
+
+	memset(&hdr, 0, sizeof(hdr));
+	fill_afe_cmd_header(&hdr, port_cfg->port_id,
+			    CPE_AFE_PORT_CMD_RESUME,
+			    0, false);
+	ret = wcd_cpe_cmi_send_afe_msg(core, afe_port_d, &hdr);
+	if (ret)
+		dev_err(core->dev,
+			"%s: afe_resume cmd failed, err = %d\n",
+			__func__, ret);
+	WCD_CPE_REL_LOCK(&afe_port_d->afe_lock, "afe");
+	return ret;
+
+}
+
+/*
+ * wcd_cpe_register_afe_driver: register lsm driver to codec
+ * @cpe_ops: structure with lsm callbacks
+ * @codec: codec to which this lsm driver is registered to
+ */
+int wcd_cpe_get_afe_ops(struct wcd_cpe_afe_ops *afe_ops)
+{
+	afe_ops->afe_set_params = wcd_cpe_afe_set_params;
+	afe_ops->afe_port_start = wcd_cpe_afe_port_start;
+	afe_ops->afe_port_stop = wcd_cpe_afe_port_stop;
+	afe_ops->afe_port_suspend = wcd_cpe_afe_port_suspend;
+	afe_ops->afe_port_resume = wcd_cpe_afe_port_resume;
+	afe_ops->afe_port_cmd_cfg = wcd_cpe_afe_cmd_port_cfg;
+
+	return 0;
+}
+EXPORT_SYMBOL(wcd_cpe_get_afe_ops);
+
+MODULE_DESCRIPTION("WCD CPE Core");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd_cpe_core.h b/sound/soc/codecs/wcd_cpe_core.h
new file mode 100644
index 0000000..3d672b8
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_core.h
@@ -0,0 +1,226 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WCD_CPE_CORE_H
+#define WCD_CPE_CORE_H
+
+#include <soc/qcom/ramdump.h>
+#include <linux/dma-mapping.h>
+#include "wcd_cpe_services.h"
+
+#define WCD_CPE_LAB_MAX_LATENCY 250
+#define WCD_CPE_MAD_SLIM_CHANNEL 140
+
+/* Indicates CPE block is ready for image re-download */
+#define WCD_CPE_BLK_READY  (1 << 0)
+/* Indicates the underlying bus is ready */
+#define WCD_CPE_BUS_READY (1 << 1)
+
+/*
+ * only when the underlying bus and CPE block both are ready,
+ * the state will be ready to download
+ */
+#define WCD_CPE_READY_TO_DLOAD	\
+	(WCD_CPE_BLK_READY | WCD_CPE_BUS_READY)
+
+#define WCD_CPE_LOAD_IMEM (1 << 0)
+#define WCD_CPE_LOAD_DATA (1 << 1)
+#define WCD_CPE_LOAD_ALL \
+	(WCD_CPE_LOAD_IMEM | WCD_CPE_LOAD_DATA)
+
+#define WCD_CPE_IMAGE_FNAME_MAX 64
+
+#define WCD_CPE_AFE_OUT_PORT_2 2
+#define WCD_CPE_AFE_OUT_PORT_4 4
+
+enum {
+	WCD_CPE_LSM_CAL_AFE = 0,
+	WCD_CPE_LSM_CAL_LSM,
+	WCD_CPE_LSM_CAL_TOPOLOGY_ID,
+	WCD_CPE_LSM_CAL_MAX,
+};
+
+enum cpe_err_irq_cntl_type {
+	CPE_ERR_IRQ_MASK = 0,
+	CPE_ERR_IRQ_UNMASK,
+	CPE_ERR_IRQ_CLEAR,
+	CPE_ERR_IRQ_STATUS,
+};
+
+struct wcd_cpe_cdc_cb {
+	/* codec provided callback to enable RCO */
+	int (*cdc_clk_en)(struct snd_soc_codec *, bool);
+
+	/* callback for FLL setup for codec */
+	int (*cpe_clk_en)(struct snd_soc_codec *, bool);
+	int (*cdc_ext_clk)(struct snd_soc_codec *codec, int enable, bool dapm);
+	int (*lab_cdc_ch_ctl)(struct snd_soc_codec *codec, u8 event);
+	int (*get_afe_out_port_id)(struct snd_soc_codec *codec, u16 *port_id);
+	int (*bus_vote_bw)(struct snd_soc_codec *codec,
+			   bool vote);
+
+	/* Callback to control the cpe error interrupt mask/status/clear */
+	int (*cpe_err_irq_control)(struct snd_soc_codec *codec,
+				    enum cpe_err_irq_cntl_type cntl_type,
+				    u8 *status);
+};
+
+enum wcd_cpe_ssr_state_event {
+	/* Indicates CPE is initialized */
+	WCD_CPE_INITIALIZED = 0,
+	/* Indicates that IMEM is downloaded to CPE */
+	WCD_CPE_IMEM_DOWNLOADED,
+	/* Indicates CPE is enabled */
+	WCD_CPE_ENABLED,
+	/* Indicates that CPE is currently active */
+	WCD_CPE_ACTIVE,
+	/* Event from underlying bus notifying bus is down */
+	WCD_CPE_BUS_DOWN_EVENT,
+	/* Event from CPE block, notifying CPE is down */
+	WCD_CPE_SSR_EVENT,
+	/* Event from underlying bus notifying bus is up */
+	WCD_CPE_BUS_UP_EVENT,
+};
+
+struct wcd_cpe_ssr_entry {
+	int offline;
+	u32 offline_change;
+	wait_queue_head_t offline_poll_wait;
+	struct snd_info_entry *entry;
+};
+
+struct wcd_cpe_irq_info {
+	int cpe_engine_irq;
+	int cpe_err_irq;
+	u8 cpe_fatal_irqs;
+};
+
+struct wcd_cpe_hw_info {
+	u32 dram_offset;
+	size_t dram_size;
+	u32 iram_offset;
+	size_t iram_size;
+};
+
+struct wcd_cpe_core {
+	/* handle to cpe services */
+	void *cpe_handle;
+
+	/* registration handle to cpe services */
+	void *cpe_reg_handle;
+
+	/* cmi registration handle for afe service */
+	void *cmi_afe_handle;
+
+	/* handle to codec */
+	struct snd_soc_codec *codec;
+
+	/* codec device */
+	struct device *dev;
+
+	/* firmware image file name */
+	char fname[WCD_CPE_IMAGE_FNAME_MAX];
+
+	/* firmware image file name from sysfs */
+	char dyn_fname[WCD_CPE_IMAGE_FNAME_MAX];
+
+	/* codec information needed by cpe services */
+	struct cpe_svc_codec_info_v1 cdc_info;
+
+	/* work to perform image download */
+	struct work_struct load_fw_work;
+
+	/* flag to indicate mode in which cpe needs to be booted */
+	int cpe_debug_mode;
+
+	/* callbacks for codec specific implementation */
+	const struct wcd_cpe_cdc_cb *cpe_cdc_cb;
+
+	/* work to handle CPE SSR*/
+	struct work_struct ssr_work;
+
+	/* PM handle for suspend mode during SSR */
+	struct pm_qos_request pm_qos_req;
+
+	/* completion event indicating CPE OFFLINE */
+	struct completion offline_compl;
+
+	/* entry into snd card procfs indicating cpe status */
+	struct wcd_cpe_ssr_entry ssr_entry;
+
+	/*
+	 * completion event to signal CPE is
+	 * ready for image re-download
+	 */
+	struct completion ready_compl;
+
+	/* maintains the status for cpe ssr */
+	u8 ready_status;
+
+	/* Indicate SSR type */
+	enum wcd_cpe_ssr_state_event ssr_type;
+
+	/* mutex to protect cpe ssr status variables */
+	struct mutex ssr_lock;
+
+	/* Store the calibration data needed for cpe */
+	struct cal_type_data *cal_data[WCD_CPE_LSM_CAL_MAX];
+
+	/* completion event to signal CPE is online */
+	struct completion online_compl;
+
+	/* reference counter for cpe usage */
+	u8 cpe_users;
+
+	/* Ramdump support */
+	void *cpe_ramdump_dev;
+	struct ramdump_segment cpe_ramdump_seg;
+	dma_addr_t cpe_dump_addr;
+	void *cpe_dump_v_addr;
+
+	/* SFR support */
+	u32 sfr_buf_addr;
+	size_t sfr_buf_size;
+
+	/* IRQ information for CPE interrupts */
+	struct wcd_cpe_irq_info irq_info;
+
+	/* Kobject for sysfs entry */
+	struct kobject cpe_kobj;
+
+	/* Reference count for cpe clk*/
+	int cpe_clk_ref;
+
+	/* codec based hardware info */
+	struct wcd_cpe_hw_info hw_info;
+};
+
+struct wcd_cpe_params {
+	struct snd_soc_codec *codec;
+	struct wcd_cpe_core * (*get_cpe_core)(
+				struct snd_soc_codec *);
+	const struct wcd_cpe_cdc_cb *cdc_cb;
+	int dbg_mode;
+	u16 cdc_major_ver;
+	u16 cdc_minor_ver;
+	u32 cdc_id;
+
+	struct wcd_cpe_irq_info cdc_irq_info;
+
+	struct cpe_svc_init_param *cpe_svc_params;
+};
+
+int wcd_cpe_ssr_event(void *core_handle,
+		      enum wcd_cpe_ssr_state_event event);
+struct wcd_cpe_core *wcd_cpe_init(const char *img_fname,
+struct snd_soc_codec *codec, struct wcd_cpe_params *params);
+#endif
diff --git a/sound/soc/codecs/wcd_cpe_services.c b/sound/soc/codecs/wcd_cpe_services.c
new file mode 100644
index 0000000..0028ebc
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_services.c
@@ -0,0 +1,2990 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <sound/cpe_cmi.h>
+#include <sound/soc.h>
+#include <linux/mfd/wcd9xxx/wcd9330_registers.h>
+#include <linux/mfd/wcd9335/registers.h>
+#include "wcd_cpe_services.h"
+#include "wcd_cmi_api.h"
+
+#define CPE_MSG_BUFFER_SIZE 132
+#define CPE_NO_SERVICE 0
+
+#define CMI_DRIVER_SUPPORTED_VERSION 0
+#define CMI_API_SUCCESS 0
+#define CMI_MSG_TRANSPORT (0x0002)
+#define CPE_SVC_INACTIVE_STATE_RETRIES_MAX 10
+
+#define TOMTOM_A_SVASS_SPE_DRAM_OFFSET				0x50000
+#define TOMTOM_A_SVASS_SPE_DRAM_SIZE				0x30000
+#define TOMTOM_A_SVASS_SPE_IRAM_OFFSET				0x80000
+#define TOMTOM_A_SVASS_SPE_IRAM_SIZE				0xC000
+#define TOMTOM_A_SVASS_SPE_INBOX_SIZE				12
+#define TOMTOM_A_SVASS_SPE_OUTBOX_SIZE				12
+
+#define MEM_ACCESS_NONE_VAL			0x0
+#define MEM_ACCESS_IRAM_VAL			0x1
+#define MEM_ACCESS_DRAM_VAL			0x2
+#define LISTEN_CTL_SPE_VAL			0x0
+#define LISTEN_CTL_MSM_VAL			0x1
+
+#define TOMTOM_A_SVASS_SPE_INBOX(N)	(TOMTOM_A_SVASS_SPE_INBOX_0 + (N))
+#define TOMTOM_A_SVASS_SPE_OUTBOX(N)	(TOMTOM_A_SVASS_SPE_OUTBOX_0 + (N))
+
+#define WCD9335_CPE_SS_SPE_DRAM_OFFSET		0x48000
+#define WCD9335_CPE_SS_SPE_DRAM_SIZE		0x34000
+#define WCD9335_CPE_SS_SPE_IRAM_OFFSET		0x80000
+#define WCD9335_CPE_SS_SPE_IRAM_SIZE		0x20000
+
+#define WCD9335_CPE_SS_SPE_INBOX_SIZE		16
+#define WCD9335_CPE_SS_SPE_OUTBOX_SIZE		16
+#define WCD9335_CPE_SS_SPE_MEM_BANK_SIZ		16
+
+#define WCD9335_CPE_SS_SPE_INBOX1(N)	(WCD9335_CPE_SS_INBOX1_0 + (N))
+#define WCD9335_CPE_SS_SPE_OUTBOX1(N)	(WCD9335_CPE_SS_OUTBOX1_0 + (N))
+#define WCD9335_CPE_SS_MEM_BANK(N)	(WCD9335_CPE_SS_MEM_BANK_0 + (N))
+
+#define CHUNK_SIZE 16
+
+#define CPE_SVC_GRAB_LOCK(lock, name)		\
+{						\
+	pr_debug("%s: %s lock acquire\n",	\
+		 __func__, name);		\
+	mutex_lock(lock);			\
+}
+
+#define CPE_SVC_REL_LOCK(lock, name)		\
+{						\
+	pr_debug("%s: %s lock release\n",	\
+		 __func__, name);		\
+	mutex_unlock(lock);			\
+}
+
+static const struct cpe_svc_hw_cfg cpe_svc_tomtom_info = {
+	TOMTOM_A_SVASS_SPE_DRAM_SIZE,
+	TOMTOM_A_SVASS_SPE_DRAM_OFFSET,
+	TOMTOM_A_SVASS_SPE_IRAM_SIZE,
+	TOMTOM_A_SVASS_SPE_IRAM_OFFSET,
+	TOMTOM_A_SVASS_SPE_INBOX_SIZE,
+	TOMTOM_A_SVASS_SPE_OUTBOX_SIZE
+};
+
+static const struct cpe_svc_hw_cfg cpe_svc_wcd9335_info = {
+	WCD9335_CPE_SS_SPE_DRAM_SIZE,
+	WCD9335_CPE_SS_SPE_DRAM_OFFSET,
+	WCD9335_CPE_SS_SPE_IRAM_SIZE,
+	WCD9335_CPE_SS_SPE_IRAM_OFFSET,
+	WCD9335_CPE_SS_SPE_INBOX_SIZE,
+	WCD9335_CPE_SS_SPE_OUTBOX_SIZE
+};
+
+enum cpe_state {
+	CPE_STATE_UNINITIALIZED = 0,
+	CPE_STATE_INITIALIZED,
+	CPE_STATE_IDLE,
+	CPE_STATE_DOWNLOADING,
+	CPE_STATE_BOOTING,
+	CPE_STATE_SENDING_MSG,
+	CPE_STATE_OFFLINE,
+	CPE_STATE_BUFFERING,
+	CPE_STATE_BUFFERING_CANCELLED
+};
+
+enum cpe_substate {
+	CPE_SS_IDLE = 0,
+	CPE_SS_MSG_REQUEST_ACCESS,
+	CPE_SS_MSG_SEND_INBOX,
+	CPE_SS_MSG_SENT,
+	CPE_SS_DL_DOWNLOADING,
+	CPE_SS_DL_COMPLETED,
+	CPE_SS_BOOT,
+	CPE_SS_BOOT_INIT,
+	CPE_SS_ONLINE
+};
+
+enum cpe_command {
+	CPE_CMD_KILL_THREAD = 0,
+	CPE_CMD_BOOT,
+	CPE_CMD_BOOT_INITIALIZE,
+	CPE_CMD_BOOT_COMPLETE,
+	CPE_CMD_SEND_MSG,
+	CPE_CMD_SEND_TRANS_MSG,
+	CPE_CMD_SEND_MSG_COMPLETE,
+	CPE_CMD_PROCESS_IRQ,
+	CPE_CMD_RAMDUMP,
+	CPE_CMD_DL_SEGMENT,
+	CPE_CMD_SHUTDOWN,
+	CPE_CMD_RESET,
+	CPE_CMD_DEINITIALIZE,
+	CPE_CMD_READ,
+	CPE_CMD_ENABLE_LAB,
+	CPE_CMD_DISABLE_LAB,
+	CPE_CMD_SWAP_BUFFER,
+	CPE_LAB_CFG_SB,
+	CPE_CMD_CANCEL_MEMACCESS,
+	CPE_CMD_PROC_INCOMING_MSG,
+	CPE_CMD_FTM_TEST,
+};
+
+enum cpe_process_result {
+	CPE_PROC_SUCCESS = 0,
+	CPE_PROC_FAILED,
+	CPE_PROC_KILLED,
+	CPE_PROC_QUEUED,
+};
+
+struct cpe_command_node {
+	enum cpe_command command;
+	enum cpe_svc_result result;
+	void *data;
+	struct list_head list;
+};
+
+struct cpe_info {
+	struct list_head main_queue;
+	struct completion cmd_complete;
+	struct completion thread_comp;
+	void *thread_handler;
+	bool stop_thread;
+	struct mutex msg_lock;
+	enum cpe_state state;
+	enum cpe_substate substate;
+	struct list_head client_list;
+	enum cpe_process_result (*cpe_process_command)
+			(struct cpe_command_node *command_node);
+	enum cpe_svc_result (*cpe_cmd_validate)
+				(const struct cpe_info *i,
+				 enum cpe_command command);
+	enum cpe_svc_result (*cpe_start_notification)
+			     (struct cpe_info *i);
+	u32 initialized;
+	struct cpe_svc_tgt_abstraction *tgt;
+	void *pending;
+	void *data;
+	void *client_context;
+	u32 codec_id;
+	struct work_struct clk_plan_work;
+	struct completion core_svc_cmd_compl;
+};
+
+struct cpe_tgt_waiti_info {
+	u8 tgt_waiti_size;
+	u8 *tgt_waiti_data;
+};
+
+struct cpe_svc_tgt_abstraction {
+	enum cpe_svc_result (*tgt_boot)(int debug_mode);
+
+	u32 (*tgt_cpar_init_done)(void);
+
+	u32 (*tgt_is_active)(void);
+
+	enum cpe_svc_result (*tgt_reset)(void);
+
+	enum cpe_svc_result (*tgt_stop)(void);
+
+	enum cpe_svc_result (*tgt_read_mailbox)
+				(u8 *buffer, size_t size);
+
+	enum cpe_svc_result (*tgt_write_mailbox)
+				(u8 *buffer, size_t size);
+
+	enum cpe_svc_result (*tgt_read_ram)
+				(struct cpe_info *c,
+				 struct cpe_svc_mem_segment *data);
+
+	enum cpe_svc_result (*tgt_write_ram)
+				(struct cpe_info *c,
+				const struct cpe_svc_mem_segment *data);
+
+	enum cpe_svc_result (*tgt_route_notification)
+				(enum cpe_svc_module module,
+				 enum cpe_svc_route_dest dest);
+
+	enum cpe_svc_result (*tgt_set_debug_mode)(u32 enable);
+	const struct cpe_svc_hw_cfg *(*tgt_get_cpe_info)(void);
+	enum cpe_svc_result (*tgt_deinit)
+				(struct cpe_svc_tgt_abstraction *param);
+	enum cpe_svc_result (*tgt_voice_tx_lab)
+				(bool);
+	u8 *inbox;
+	u8 *outbox;
+	struct cpe_tgt_waiti_info *tgt_waiti_info;
+};
+
+static enum cpe_svc_result cpe_tgt_tomtom_init(
+	struct cpe_svc_codec_info_v1 *codec_info,
+	struct cpe_svc_tgt_abstraction *param);
+
+static enum cpe_svc_result cpe_tgt_wcd9335_init(
+	struct cpe_svc_codec_info_v1 *codec_info,
+	struct cpe_svc_tgt_abstraction *param);
+
+struct cpe_send_msg {
+	u8 *payload;
+	u32 isobm;
+	u32 address;
+	size_t size;
+};
+
+struct cpe_read_handle {
+	void *registration;
+	struct cpe_info t_info;
+	struct list_head buffers;
+	void *config;
+};
+
+struct generic_notification {
+	void (*notification)
+		(const struct cpe_svc_notification *parameter);
+	void (*cmi_notification)
+		(const struct cmi_api_notification *parameter);
+};
+
+struct cpe_notif_node {
+	struct generic_notification notif;
+	u32 mask;
+	u32 service;
+	const struct cpe_info *context;
+	const char *name;
+	u32 disabled;
+	struct list_head list;
+};
+
+struct cpe_priv {
+	struct cpe_info *cpe_default_handle;
+	void (*cpe_irq_control_callback)(u32 enable);
+	void (*cpe_query_freq_plans_cb)
+		(void *cdc_priv,
+		 struct cpe_svc_cfg_clk_plan *clk_freq);
+	void (*cpe_change_freq_plan_cb)(void *cdc_priv,
+			u32 clk_freq);
+	u32 cpe_msg_buffer;
+	void *cpe_cmi_handle;
+	struct mutex cpe_api_mutex;
+	struct mutex cpe_svc_lock;
+	struct cpe_svc_boot_event cpe_debug_vector;
+	void *cdc_priv;
+};
+
+static struct cpe_priv cpe_d;
+
+static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle);
+
+static enum cpe_svc_result cpe_is_command_valid(
+		const struct cpe_info *t_info,
+		enum cpe_command command);
+
+static int cpe_register_read(u32 reg, u8 *val)
+{
+	*(val) = snd_soc_read(cpe_d.cdc_priv, reg);
+	return 0;
+}
+
+static enum cpe_svc_result cpe_update_bits(u32 reg,
+		u32 mask, u32 value)
+{
+	int ret = 0;
+
+	ret = snd_soc_update_bits(cpe_d.cdc_priv, reg,
+				  mask, value);
+	if (ret < 0)
+		return CPE_SVC_FAILED;
+
+	return CPE_SVC_SUCCESS;
+}
+
+static int cpe_register_write(u32 reg, u32 val)
+{
+	int ret = 0;
+
+	if (reg != TOMTOM_A_SVASS_MEM_BANK &&
+	    reg != WCD9335_CPE_SS_MEM_BANK_0)
+		pr_debug("%s: reg = 0x%x, value = 0x%x\n",
+			  __func__, reg, val);
+
+	ret = snd_soc_write(cpe_d.cdc_priv, reg, val);
+	if (ret < 0)
+		return CPE_SVC_FAILED;
+
+	return CPE_SVC_SUCCESS;
+}
+
+static int cpe_register_write_repeat(u32 reg, u8 *ptr, u32 to_write)
+{
+	struct snd_soc_codec *codec = cpe_d.cdc_priv;
+	struct wcd9xxx *wcd9xxx = dev_get_drvdata(codec->dev->parent);
+	int ret = 0;
+
+	ret = wcd9xxx_slim_write_repeat(wcd9xxx, reg, to_write, ptr);
+	if (ret != 0)
+		pr_err("%s: slim_write_repeat failed\n", __func__);
+
+	if (ret < 0)
+		return CPE_SVC_FAILED;
+
+	return CPE_SVC_SUCCESS;
+}
+
+static bool cpe_register_read_autoinc_supported(void)
+{
+	return true;
+}
+
+
+/* Called under msgq locked context */
+static void cpe_cmd_received(struct cpe_info *t_info)
+{
+	struct cpe_command_node *node = NULL;
+	enum cpe_process_result proc_rc = CPE_PROC_SUCCESS;
+
+	if (!t_info) {
+		pr_err("%s: Invalid thread info\n",
+			__func__);
+		return;
+	}
+
+	while (!list_empty(&t_info->main_queue)) {
+		if (proc_rc != CPE_PROC_SUCCESS)
+			break;
+		node = list_first_entry(&t_info->main_queue,
+					struct cpe_command_node, list);
+		if (!node)
+			break;
+		list_del(&node->list);
+		proc_rc = t_info->cpe_process_command(node);
+		pr_debug("%s: process command return %d\n",
+			 __func__, proc_rc);
+
+		switch (proc_rc) {
+		case CPE_PROC_SUCCESS:
+			kfree(node);
+			break;
+		case CPE_PROC_FAILED:
+			kfree(node);
+			pr_err("%s: cmd failed\n", __func__);
+			break;
+		case CPE_PROC_KILLED:
+			break;
+		default:
+			list_add(&node->list, &(t_info->main_queue));
+
+		}
+	}
+}
+
+static int cpe_worker_thread(void *context)
+{
+	struct cpe_info *t_info = (struct cpe_info *)context;
+
+	/*
+	 * Thread will run until requested to stop explicitly
+	 * by setting the t_info->stop_thread flag
+	 */
+	while (1) {
+		/* Wait for command to be processed */
+		wait_for_completion(&t_info->cmd_complete);
+
+		CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+		cpe_cmd_received(t_info);
+		reinit_completion(&t_info->cmd_complete);
+		/* Check if thread needs to be stopped */
+		if (t_info->stop_thread)
+			goto unlock_and_exit;
+		CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+	};
+
+unlock_and_exit:
+	pr_debug("%s: thread stopped\n", __func__);
+	CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+	complete_and_exit(&t_info->thread_comp, 0);
+}
+
+static void cpe_create_worker_thread(struct cpe_info *t_info)
+{
+	INIT_LIST_HEAD(&t_info->main_queue);
+	init_completion(&t_info->cmd_complete);
+	init_completion(&t_info->thread_comp);
+	t_info->stop_thread = false;
+	t_info->thread_handler = kthread_run(cpe_worker_thread,
+		(void *)t_info, "cpe-worker-thread");
+	pr_debug("%s: Created new worker thread\n",
+		 __func__);
+}
+
+static void cpe_cleanup_worker_thread(struct cpe_info *t_info)
+{
+	if (!t_info->thread_handler) {
+		pr_err("%s: thread not created\n", __func__);
+		return;
+	}
+
+	/*
+	 * Wake up the command handler in case
+	 * it is waiting for an command to be processed.
+	 */
+	CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+	t_info->stop_thread = true;
+	complete(&t_info->cmd_complete);
+	CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
+	/* Wait for the thread to exit */
+	wait_for_completion(&t_info->thread_comp);
+	t_info->thread_handler = NULL;
+
+	pr_debug("%s: Thread cleaned up successfully\n",
+		 __func__);
+}
+
+static enum cpe_svc_result
+cpe_send_cmd_to_thread(struct cpe_info *t_info,
+	enum cpe_command command, void *data,
+	bool high_prio)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_command_node *cmd = NULL;
+
+	rc = cpe_is_command_valid(t_info, command);
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Invalid command %d\n",
+			__func__, command);
+		return rc;
+	}
+
+	cmd = kzalloc(sizeof(struct cpe_command_node),
+		      GFP_ATOMIC);
+	if (!cmd)
+		return CPE_SVC_NO_MEMORY;
+
+	cmd->command = command;
+	cmd->data = data;
+
+	CPE_SVC_GRAB_LOCK(&t_info->msg_lock, "msg_lock");
+	if (high_prio)
+		list_add(&(cmd->list),
+			 &(t_info->main_queue));
+	else
+		list_add_tail(&(cmd->list),
+			      &(t_info->main_queue));
+	complete(&t_info->cmd_complete);
+	CPE_SVC_REL_LOCK(&t_info->msg_lock, "msg_lock");
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_change_state(
+	struct cpe_info *t_info,
+	enum cpe_state state, enum cpe_substate ss)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	t_info->state = state;
+	t_info->substate = ss;
+
+	pr_debug("%s: current state: %d,%d, new_state: %d,%d\n",
+		 __func__, t_info->state, t_info->substate,
+		 state, ss);
+
+	return rc;
+}
+
+static enum cpe_svc_result
+cpe_is_command_valid(const struct cpe_info *t_info,
+		enum cpe_command command)
+{
+	enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE;
+
+	if (t_info && t_info->cpe_cmd_validate)
+		rc = t_info->cpe_cmd_validate(t_info, command);
+	else
+		pr_err("%s: invalid handle or callback\n",
+			__func__);
+	return rc;
+}
+
+static void cpe_notify_client(struct cpe_notif_node *client,
+		struct cpe_svc_notification *payload)
+{
+	if (!client || !payload) {
+		pr_err("%s: invalid client or payload\n",
+			__func__);
+		return;
+	}
+
+	if (!(client->mask & payload->event)) {
+		pr_debug("%s: client mask 0x%x not registered for event 0x%x\n",
+			 __func__, client->mask, payload->event);
+		return;
+	}
+
+	if (client->notif.notification && !client->disabled)
+		client->notif.notification(payload);
+
+	if ((client->mask & CPE_SVC_CMI_MSG) &&
+	     client->notif.cmi_notification)
+		client->notif.cmi_notification(
+			(const struct cmi_api_notification *)payload);
+}
+
+static void cpe_broadcast_notification(const struct cpe_info *t_info,
+		struct cpe_svc_notification *payload)
+{
+	struct cpe_notif_node *n = NULL;
+
+	if (!t_info || !payload) {
+		pr_err("%s: invalid handle\n", __func__);
+		return;
+	}
+
+	pr_debug("%s: notify clients, event = %d\n",
+		 __func__, payload->event);
+	payload->private_data = cpe_d.cdc_priv;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+	list_for_each_entry(n, &t_info->client_list, list) {
+		if (!(n->mask & CPE_SVC_CMI_MSG))
+			cpe_notify_client(n, payload);
+	}
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+}
+
+static void *cpe_register_generic(struct cpe_info *t_info,
+		void notification_callback(
+			const struct cpe_svc_notification *parameter),
+		void cmi_callback(
+			const struct cmi_api_notification *parameter),
+		u32 mask, u32 service, const char *name)
+{
+	struct cpe_notif_node *n = NULL;
+
+	n = kzalloc(sizeof(struct cpe_notif_node),
+		    GFP_KERNEL);
+	if (!n)
+		return NULL;
+	n->mask = mask;
+	n->service = service;
+	n->notif.notification = notification_callback;
+	n->notif.cmi_notification = cmi_callback;
+	n->context = t_info;
+	n->disabled = false;
+	n->name = name;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+	/* Make sure CPE core service is first */
+	if (service == CMI_CPE_CORE_SERVICE_ID)
+		list_add(&n->list, &t_info->client_list);
+	else
+		list_add_tail(&n->list, &t_info->client_list);
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+
+	return n;
+}
+
+static enum cpe_svc_result cpe_deregister_generic(struct cpe_info *t_info,
+		void *reg_handle)
+{
+	struct cpe_notif_node *n = (struct cpe_notif_node *)reg_handle;
+
+	if (!t_info || !reg_handle) {
+		pr_err("%s: invalid handle\n", __func__);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	list_del(&(n->list));
+	kfree(reg_handle);
+
+	return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_svc_result cpe_svc_tgt_init(struct cpe_svc_codec_info_v1 *i,
+		struct cpe_svc_tgt_abstraction *abs)
+{
+	if (!i || !abs) {
+		pr_err("%s: Incorrect information provided\n",
+			__func__);
+		return CPE_SVC_FAILED;
+	}
+
+	switch (i->id) {
+	case CPE_SVC_CODEC_TOMTOM:
+		return cpe_tgt_tomtom_init(i, abs);
+	case CPE_SVC_CODEC_WCD9335:
+		return cpe_tgt_wcd9335_init(i, abs);
+	default:
+		pr_err("%s: Codec type %d not supported\n",
+			__func__, i->id);
+		return CPE_SVC_FAILED;
+	}
+
+	return CPE_SVC_SUCCESS;
+}
+
+static void cpe_notify_cmi_client(struct cpe_info *t_info, u8 *payload,
+		enum cpe_svc_result result)
+{
+	struct cpe_notif_node *n = NULL;
+	struct cmi_api_notification notif;
+	struct cmi_hdr *hdr;
+	u8 service = 0;
+
+	if (!t_info || !payload) {
+		pr_err("%s: invalid payload/handle\n",
+			__func__);
+		return;
+	}
+
+	hdr = CMI_GET_HEADER(payload);
+	service = CMI_HDR_GET_SERVICE(hdr);
+
+	notif.event = CPE_SVC_CMI_MSG;
+	notif.result = result;
+	notif.message = payload;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+	list_for_each_entry(n, &t_info->client_list, list) {
+
+		if ((n->mask & CPE_SVC_CMI_MSG) &&
+		    n->service == service &&
+		    n->notif.cmi_notification) {
+			n->notif.cmi_notification(&notif);
+			break;
+		}
+	}
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+}
+
+static void cpe_toggle_irq_notification(struct cpe_info *t_info, u32 value)
+{
+	if (cpe_d.cpe_irq_control_callback)
+		cpe_d.cpe_irq_control_callback(value);
+}
+
+static void cpe_command_cleanup(struct cpe_command_node *command_node)
+{
+	switch (command_node->command) {
+	case CPE_CMD_SEND_MSG:
+	case CPE_CMD_SEND_TRANS_MSG:
+	case CPE_CMD_SEND_MSG_COMPLETE:
+	case CPE_CMD_SHUTDOWN:
+	case CPE_CMD_READ:
+		kfree(command_node->data);
+		command_node->data = NULL;
+		break;
+	default:
+		pr_err("%s: unhandled command\n",
+			__func__);
+		break;
+	}
+}
+
+static enum cpe_svc_result cpe_send_msg_to_inbox(
+		struct cpe_info *t_info, u32 opcode,
+		struct cpe_send_msg *msg)
+{
+	size_t bytes = 0;
+	size_t inbox_size =
+		t_info->tgt->tgt_get_cpe_info()->inbox_size;
+	struct cmi_hdr *hdr;
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	memset(t_info->tgt->inbox, 0, inbox_size);
+	hdr = CMI_GET_HEADER(t_info->tgt->inbox);
+	CMI_HDR_SET_SESSION(hdr, 1);
+	CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID);
+	CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION);
+	CMI_HDR_SET_OBM(hdr, CMI_OBM_FLAG_IN_BAND);
+
+	switch (opcode) {
+	case CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC: {
+		struct cmi_core_svc_cmd_shared_mem_alloc *m;
+
+		CMI_HDR_SET_OPCODE(hdr,
+			CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC);
+		CMI_HDR_SET_PAYLOAD_SIZE(hdr,
+			sizeof(struct cmi_core_svc_cmd_shared_mem_alloc));
+		m = (struct cmi_core_svc_cmd_shared_mem_alloc *)
+			CMI_GET_PAYLOAD(t_info->tgt->inbox);
+		m->size = CPE_MSG_BUFFER_SIZE;
+		pr_debug("send shared mem alloc msg to cpe inbox\n");
+		}
+		break;
+	case CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ:
+		CMI_HDR_SET_OPCODE(hdr,
+			CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ);
+		CMI_HDR_SET_PAYLOAD_SIZE(hdr, 0);
+		pr_debug("%s: Creating DRAM acces request msg\n",
+			 __func__);
+		break;
+
+	case CPE_CMI_BASIC_RSP_OPCODE: {
+		struct cmi_basic_rsp_result *rsp;
+
+		CMI_HDR_SET_OPCODE(hdr,
+			       CPE_CMI_BASIC_RSP_OPCODE);
+		CMI_HDR_SET_PAYLOAD_SIZE(hdr,
+			sizeof(struct cmi_basic_rsp_result));
+		rsp = (struct cmi_basic_rsp_result *)
+				CMI_GET_PAYLOAD(t_info->tgt->inbox);
+		rsp->status = 0;
+		pr_debug("%s: send basic response\n", __func__);
+		}
+		break;
+
+	default:
+		if (msg->address != 0) {
+			struct cmi_msg_transport *m = NULL;
+			struct cpe_svc_mem_segment mem_seg;
+
+			mem_seg.type = CPE_SVC_DATA_MEM;
+			if (msg->isobm) {
+				struct cmi_obm *obm = (struct cmi_obm *)
+
+				CMI_GET_PAYLOAD(msg->payload);
+				mem_seg.cpe_addr = obm->mem_handle;
+				mem_seg.data = (u8 *)obm->data_ptr.kvaddr;
+				mem_seg.size = obm->size;
+				t_info->tgt->tgt_write_ram(t_info, &mem_seg);
+			}
+
+			mem_seg.cpe_addr = msg->address;
+			mem_seg.data = msg->payload;
+			mem_seg.size = msg->size;
+			t_info->tgt->tgt_write_ram(t_info, &mem_seg);
+
+			hdr = CMI_GET_HEADER(t_info->tgt->inbox);
+			CMI_HDR_SET_OPCODE(hdr, CMI_MSG_TRANSPORT);
+			m = (struct cmi_msg_transport *)
+				CMI_GET_PAYLOAD(t_info->tgt->inbox);
+			m->addr = msg->address;
+			m->size = msg->size;
+			CMI_HDR_SET_PAYLOAD_SIZE(hdr,
+				sizeof(struct cmi_msg_transport));
+		} else {
+			memcpy(t_info->tgt->inbox, msg->payload,
+			       msg->size);
+		}
+
+		break;
+	}
+
+	pr_debug("%s: sending message to cpe inbox\n",
+		  __func__);
+	bytes = sizeof(struct cmi_hdr);
+	hdr = CMI_GET_HEADER(t_info->tgt->inbox);
+	bytes += CMI_HDR_GET_PAYLOAD_SIZE(hdr);
+	rc = t_info->tgt->tgt_write_mailbox(t_info->tgt->inbox, bytes);
+
+	return rc;
+}
+
+static bool cpe_is_cmd_clk_req(void *cmd)
+{
+	struct cmi_hdr *hdr;
+
+	hdr = CMI_GET_HEADER(cmd);
+
+	if ((CMI_HDR_GET_SERVICE(hdr) ==
+	    CMI_CPE_CORE_SERVICE_ID)) {
+		if (CMI_GET_OPCODE(cmd) ==
+		    CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST)
+			return true;
+	}
+
+	return false;
+}
+
+static enum cpe_svc_result cpe_process_clk_change_req(
+		struct cpe_info *t_info)
+{
+	struct cmi_core_svc_cmd_clk_freq_request *req;
+
+	req = (struct cmi_core_svc_cmd_clk_freq_request *)
+			CMI_GET_PAYLOAD(t_info->tgt->outbox);
+
+	if (!cpe_d.cpe_change_freq_plan_cb) {
+		pr_err("%s: No support for clk freq change\n",
+			__func__);
+		return CPE_SVC_FAILED;
+	}
+
+	cpe_d.cpe_change_freq_plan_cb(cpe_d.cdc_priv,
+				      req->clk_freq);
+
+	/*send a basic response*/
+	cpe_send_msg_to_inbox(t_info,
+		CPE_CMI_BASIC_RSP_OPCODE, NULL);
+
+	return CPE_SVC_SUCCESS;
+}
+
+static void cpe_process_irq_int(u32 irq,
+		struct cpe_info *t_info)
+{
+	struct cpe_command_node temp_node;
+	struct cpe_send_msg *m;
+	u8 size = 0;
+	bool err_irq = false;
+	struct cmi_hdr *hdr;
+
+	pr_debug("%s: irq = %u\n", __func__, irq);
+
+	if (!t_info) {
+		pr_err("%s: Invalid handle\n",
+			__func__);
+		return;
+	}
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	switch (irq) {
+	case CPE_IRQ_OUTBOX_IRQ:
+		size = t_info->tgt->tgt_get_cpe_info()->outbox_size;
+		t_info->tgt->tgt_read_mailbox(t_info->tgt->outbox, size);
+		break;
+
+	case CPE_IRQ_MEM_ACCESS_ERROR:
+		err_irq = true;
+		cpe_change_state(t_info, CPE_STATE_OFFLINE, CPE_SS_IDLE);
+		break;
+
+	case CPE_IRQ_WDOG_BITE:
+	case CPE_IRQ_RCO_WDOG_INT:
+		err_irq = true;
+		__cpe_svc_shutdown(t_info);
+		break;
+
+	case CPE_IRQ_FLL_LOCK_LOST:
+	default:
+		err_irq = true;
+		break;
+	}
+
+	if (err_irq) {
+		pr_err("%s: CPE error IRQ %u occurred\n",
+			__func__, irq);
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return;
+	}
+
+	switch (t_info->state) {
+	case CPE_STATE_BOOTING:
+
+		switch (t_info->substate) {
+		case CPE_SS_BOOT:
+			temp_node.command = CPE_CMD_BOOT_INITIALIZE;
+			temp_node.result = CPE_SVC_SUCCESS;
+			t_info->substate = CPE_SS_BOOT_INIT;
+			t_info->cpe_process_command(&temp_node);
+			break;
+
+		case CPE_SS_BOOT_INIT:
+			temp_node.command = CPE_CMD_BOOT_COMPLETE;
+			temp_node.result = CPE_SVC_SUCCESS;
+			t_info->substate = CPE_SS_ONLINE;
+			t_info->cpe_process_command(&temp_node);
+			break;
+
+		default:
+			pr_debug("%s: unhandled substate %d for state %d\n",
+				 __func__, t_info->state, t_info->substate);
+			break;
+		}
+		break;
+
+	case CPE_STATE_SENDING_MSG:
+		hdr = CMI_GET_HEADER(t_info->tgt->outbox);
+		if (CMI_GET_OPCODE(t_info->tgt->outbox) ==
+		    CPE_LSM_SESSION_EVENT_DETECTION_STATUS_V2) {
+			pr_debug("%s: session_id: %u, state: %d,%d, event received\n",
+				 __func__, CMI_HDR_GET_SESSION_ID(hdr),
+				t_info->state, t_info->substate);
+			temp_node.command = CPE_CMD_PROC_INCOMING_MSG;
+			temp_node.data = NULL;
+			t_info->cpe_process_command(&temp_node);
+			break;
+		}
+
+		m = (struct cpe_send_msg *)t_info->pending;
+
+		switch (t_info->substate) {
+		case CPE_SS_MSG_REQUEST_ACCESS:
+			cpe_send_cmd_to_thread(t_info,
+				CPE_CMD_SEND_TRANS_MSG, m, true);
+			break;
+
+		case CPE_SS_MSG_SEND_INBOX:
+			if (cpe_is_cmd_clk_req(t_info->tgt->outbox))
+				cpe_process_clk_change_req(t_info);
+			else
+				cpe_send_cmd_to_thread(t_info,
+					CPE_CMD_SEND_MSG_COMPLETE, m, true);
+			break;
+
+		default:
+			pr_debug("%s: unhandled substate %d for state %d\n",
+				 __func__, t_info->state, t_info->substate);
+			break;
+		}
+		break;
+
+	case CPE_STATE_IDLE:
+		pr_debug("%s: Message received, notifying client\n",
+			 __func__);
+		temp_node.command = CPE_CMD_PROC_INCOMING_MSG;
+		temp_node.data = NULL;
+		t_info->cpe_process_command(&temp_node);
+		break;
+
+	default:
+		pr_debug("%s: unhandled state %d\n",
+			 __func__, t_info->state);
+		break;
+	}
+
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+}
+
+
+static void broacast_boot_failed(void)
+{
+	struct cpe_info *t_info = cpe_d.cpe_default_handle;
+	struct cpe_svc_notification payload;
+
+	payload.event = CPE_SVC_BOOT_FAILED;
+	payload.result = CPE_SVC_FAILED;
+	payload.payload = NULL;
+	if (t_info)
+		payload.private_data =
+			t_info->client_context;
+	cpe_broadcast_notification(t_info, &payload);
+}
+
+static enum cpe_svc_result broadcast_boot_event(
+		struct cpe_info *t_info)
+{
+	struct cpe_svc_notification payload;
+
+	payload.event = CPE_SVC_ONLINE;
+	payload.result = CPE_SVC_SUCCESS;
+	payload.payload = NULL;
+	if (t_info)
+		payload.private_data =
+			t_info->client_context;
+	cpe_broadcast_notification(t_info, &payload);
+
+	return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_process_result cpe_boot_initialize(struct cpe_info *t_info,
+	enum cpe_svc_result *cpe_rc)
+{
+	enum cpe_process_result rc = CPE_SVC_FAILED;
+	struct cpe_svc_notification payload;
+	struct cmi_core_svc_event_system_boot *p = NULL;
+
+	if (CMI_GET_OPCODE(t_info->tgt->outbox) !=
+		CPE_CORE_SVC_EVENT_SYSTEM_BOOT) {
+		broacast_boot_failed();
+		return rc;
+	}
+
+	p = (struct cmi_core_svc_event_system_boot *)
+		CMI_GET_PAYLOAD(t_info->tgt->outbox);
+	if (p->status != CPE_BOOT_SUCCESS) {
+		pr_err("%s: cpe boot failed, status = %d\n",
+			__func__, p->status);
+		broacast_boot_failed();
+		return rc;
+	}
+
+	/* boot was successful */
+	if (p->version ==
+	    CPE_CORE_VERSION_SYSTEM_BOOT_EVENT) {
+		cpe_d.cpe_debug_vector.debug_address =
+				p->sfr_buff_address;
+		cpe_d.cpe_debug_vector.debug_buffer_size =
+				p->sfr_buff_size;
+		cpe_d.cpe_debug_vector.status = p->status;
+		payload.event = CPE_SVC_BOOT;
+		payload.result = CPE_SVC_SUCCESS;
+		payload.payload = (void *)&cpe_d.cpe_debug_vector;
+		payload.private_data = t_info->client_context;
+		cpe_broadcast_notification(t_info, &payload);
+	}
+	cpe_change_state(t_info, CPE_STATE_BOOTING,
+			 CPE_SS_BOOT_INIT);
+	(*cpe_rc) = cpe_send_msg_to_inbox(t_info,
+			CPE_CORE_SVC_CMD_SHARED_MEM_ALLOC, NULL);
+	rc = CPE_PROC_SUCCESS;
+	return rc;
+}
+
+static void cpe_svc_core_cmi_handler(
+		const struct cmi_api_notification *parameter)
+{
+	struct cmi_hdr *hdr;
+
+	if (!parameter)
+		return;
+
+	pr_debug("%s: event = %d\n",
+		 __func__, parameter->event);
+
+	if (parameter->event != CMI_API_MSG)
+		return;
+
+	hdr = (struct cmi_hdr *) parameter->message;
+
+	if (hdr->opcode == CPE_CMI_BASIC_RSP_OPCODE) {
+		struct cmi_basic_rsp_result *result;
+
+		result = (struct cmi_basic_rsp_result *)
+			((u8 *)parameter->message) + (sizeof(*hdr));
+		if (result->status)
+			pr_err("%s: error response, error code = %u\n",
+				__func__, result->status);
+		complete(&cpe_d.cpe_default_handle->core_svc_cmd_compl);
+	}
+}
+
+static void cpe_clk_plan_work(struct work_struct *work)
+{
+	struct cpe_info *t_info = NULL;
+	size_t size = 0;
+	struct cpe_svc_cfg_clk_plan plan;
+	u8 *cmi_msg;
+	struct cmi_hdr *hdr;
+	int rc;
+
+	t_info = container_of(work, struct cpe_info, clk_plan_work);
+	if (!t_info) {
+		pr_err("%s: Invalid handle for cpe_info\n",
+			__func__);
+		return;
+	}
+
+	/* Register the core service */
+	cpe_d.cpe_cmi_handle = cmi_register(
+					cpe_svc_core_cmi_handler,
+					CMI_CPE_CORE_SERVICE_ID);
+
+	/* send the clk plan command */
+	if (!cpe_d.cpe_query_freq_plans_cb) {
+		pr_err("%s: No support for querying clk plans\n",
+			__func__);
+		return;
+	}
+
+	cpe_d.cpe_query_freq_plans_cb(cpe_d.cdc_priv, &plan);
+	size = sizeof(plan.current_clk_feq) +
+		sizeof(plan.num_clk_freqs);
+	size += plan.num_clk_freqs *
+		  sizeof(plan.clk_freqs[0]);
+	cmi_msg = kzalloc(size + sizeof(struct cmi_hdr),
+			  GFP_KERNEL);
+	if (!cmi_msg)
+		return;
+
+	hdr = (struct cmi_hdr *) cmi_msg;
+	CMI_HDR_SET_OPCODE(hdr,
+			   CPE_CORE_SVC_CMD_CFG_CLK_PLAN);
+	CMI_HDR_SET_SERVICE(hdr, CMI_CPE_CORE_SERVICE_ID);
+		CMI_HDR_SET_SESSION(hdr, 1);
+	CMI_HDR_SET_VERSION(hdr, CMI_DRIVER_SUPPORTED_VERSION);
+	CMI_HDR_SET_PAYLOAD_SIZE(hdr, size);
+	memcpy(CMI_GET_PAYLOAD(cmi_msg), &plan,
+	       size);
+	cmi_send_msg(cmi_msg);
+
+	/* Wait for clk plan command to complete */
+	rc = wait_for_completion_timeout(&t_info->core_svc_cmd_compl,
+					 (10 * HZ));
+	if (!rc) {
+		pr_err("%s: clk plan cmd timed out\n",
+			__func__);
+		goto cmd_fail;
+	}
+
+	/* clk plan cmd is successful, send start notification */
+	if (t_info->cpe_start_notification)
+		t_info->cpe_start_notification(t_info);
+	else
+		pr_err("%s: no start notification\n",
+			 __func__);
+
+cmd_fail:
+	kfree(cmi_msg);
+	cmi_deregister(cpe_d.cpe_cmi_handle);
+}
+
+static enum cpe_process_result cpe_boot_complete(
+		struct cpe_info *t_info)
+{
+	struct cmi_core_svc_cmdrsp_shared_mem_alloc *p = NULL;
+
+	if (CMI_GET_OPCODE(t_info->tgt->outbox) !=
+		CPE_CORE_SVC_CMDRSP_SHARED_MEM_ALLOC) {
+		broacast_boot_failed();
+		return CPE_PROC_FAILED;
+	}
+
+	p = (struct cmi_core_svc_cmdrsp_shared_mem_alloc *)
+		CMI_GET_PAYLOAD(t_info->tgt->outbox);
+	cpe_d.cpe_msg_buffer = p->addr;
+
+	if (cpe_d.cpe_msg_buffer == 0) {
+		pr_err("%s: Invalid cpe buffer for message\n",
+			__func__);
+		broacast_boot_failed();
+		return CPE_PROC_FAILED;
+	}
+
+	cpe_change_state(t_info, CPE_STATE_IDLE, CPE_SS_IDLE);
+	cpe_create_worker_thread(t_info);
+
+	if (t_info->codec_id != CPE_SVC_CODEC_TOMTOM) {
+		schedule_work(&t_info->clk_plan_work);
+	} else {
+		if (t_info->cpe_start_notification)
+			t_info->cpe_start_notification(t_info);
+		else
+			pr_err("%s: no start notification\n",
+				__func__);
+	}
+
+	pr_debug("%s: boot complete\n", __func__);
+	return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_process_result cpe_process_send_msg(
+	struct cpe_info *t_info,
+	enum cpe_svc_result *cpe_rc,
+	struct cpe_command_node *command_node)
+{
+	enum cpe_process_result rc = CPE_PROC_SUCCESS;
+	struct cpe_send_msg *m =
+		(struct cpe_send_msg *)command_node->data;
+	u32 size = m->size;
+
+	if (t_info->pending) {
+		pr_debug("%s: message queued\n", __func__);
+		*cpe_rc = CPE_SVC_SUCCESS;
+		return CPE_PROC_QUEUED;
+	}
+
+	pr_debug("%s: Send CMI message, size = %u\n",
+		 __func__, size);
+
+	if (size <= t_info->tgt->tgt_get_cpe_info()->inbox_size) {
+		pr_debug("%s: Msg fits mailbox, size %u\n",
+			 __func__, size);
+		cpe_change_state(t_info, CPE_STATE_SENDING_MSG,
+			CPE_SS_MSG_SEND_INBOX);
+		t_info->pending = m;
+		*cpe_rc = cpe_send_msg_to_inbox(t_info, 0, m);
+	} else if (size < CPE_MSG_BUFFER_SIZE) {
+		m->address = cpe_d.cpe_msg_buffer;
+		pr_debug("%s: Message req CMI mem access\n",
+			 __func__);
+		t_info->pending = m;
+		cpe_change_state(t_info, CPE_STATE_SENDING_MSG,
+			CPE_SS_MSG_REQUEST_ACCESS);
+		*cpe_rc = cpe_send_msg_to_inbox(t_info,
+			CPE_CORE_SVC_CMD_DRAM_ACCESS_REQ, m);
+	} else {
+		pr_debug("%s: Invalid msg size %u\n",
+			 __func__, size);
+		cpe_command_cleanup(command_node);
+		rc = CPE_PROC_FAILED;
+		cpe_change_state(t_info, CPE_STATE_IDLE,
+			CPE_SS_IDLE);
+	}
+
+	return rc;
+}
+
+static enum cpe_process_result cpe_process_incoming(
+		struct cpe_info *t_info)
+{
+	enum cpe_process_result rc = CPE_PROC_FAILED;
+	struct cmi_hdr *hdr;
+
+	hdr = CMI_GET_HEADER(t_info->tgt->outbox);
+
+	if (CMI_HDR_GET_SERVICE(hdr) ==
+	    CMI_CPE_CORE_SERVICE_ID) {
+		pr_debug("%s: core service message received\n",
+			 __func__);
+
+		switch (CMI_GET_OPCODE(t_info->tgt->outbox)) {
+		case CPE_CORE_SVC_CMD_CLK_FREQ_REQUEST:
+			cpe_process_clk_change_req(t_info);
+			rc = CPE_PROC_SUCCESS;
+			break;
+		case CMI_MSG_TRANSPORT:
+			pr_debug("%s: transport msg received\n",
+				 __func__);
+			rc = CPE_PROC_SUCCESS;
+			break;
+		case CPE_CMI_BASIC_RSP_OPCODE:
+			pr_debug("%s: received basic rsp\n",
+				 __func__);
+			rc = CPE_PROC_SUCCESS;
+			break;
+		default:
+			pr_debug("%s: unknown message received\n",
+				 __func__);
+			break;
+		}
+	} else {
+		/* if service id if for a CMI client, notify client */
+		pr_debug("%s: Message received, notifying client\n",
+			 __func__);
+		cpe_notify_cmi_client(t_info,
+			t_info->tgt->outbox, CPE_SVC_SUCCESS);
+		rc = CPE_PROC_SUCCESS;
+	}
+
+	return rc;
+}
+
+static enum cpe_process_result cpe_process_kill_thread(
+	struct cpe_info *t_info,
+	struct cpe_command_node *command_node)
+{
+	struct cpe_svc_notification payload;
+
+	cpe_d.cpe_msg_buffer = 0;
+	payload.result = CPE_SVC_SHUTTING_DOWN;
+	payload.event = CPE_SVC_OFFLINE;
+	payload.payload = NULL;
+	payload.private_data = t_info->client_context;
+	/*
+	 * Make state as offline before broadcasting
+	 * the message to clients.
+	 */
+	cpe_change_state(t_info, CPE_STATE_OFFLINE,
+			 CPE_SS_IDLE);
+	cpe_broadcast_notification(t_info, &payload);
+
+	return CPE_PROC_KILLED;
+}
+
+static enum cpe_process_result cpe_mt_process_cmd(
+		struct cpe_command_node *command_node)
+{
+	struct cpe_info *t_info = cpe_d.cpe_default_handle;
+	enum cpe_svc_result cpe_rc = CPE_SVC_SUCCESS;
+	enum cpe_process_result rc = CPE_PROC_SUCCESS;
+	struct cpe_send_msg *m;
+	struct cmi_hdr *hdr;
+	u8 service = 0;
+	u8 retries = 0;
+
+	if (!t_info || !command_node) {
+		pr_err("%s: Invalid handle/command node\n",
+			__func__);
+		return CPE_PROC_FAILED;
+	}
+
+	pr_debug("%s: cmd = %u\n", __func__, command_node->command);
+
+	cpe_rc = cpe_is_command_valid(t_info, command_node->command);
+
+	if (cpe_rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Invalid command %d, err = %d\n",
+			__func__, command_node->command, cpe_rc);
+		return CPE_PROC_FAILED;
+	}
+
+	switch (command_node->command) {
+
+	case CPE_CMD_BOOT_INITIALIZE:
+		rc = cpe_boot_initialize(t_info, &cpe_rc);
+		break;
+
+	case CPE_CMD_BOOT_COMPLETE:
+		rc = cpe_boot_complete(t_info);
+		break;
+
+	case CPE_CMD_SEND_MSG:
+		rc = cpe_process_send_msg(t_info, &cpe_rc,
+					  command_node);
+		break;
+
+	case CPE_CMD_SEND_TRANS_MSG:
+		m = (struct cpe_send_msg *)command_node->data;
+
+		while (retries < CPE_SVC_INACTIVE_STATE_RETRIES_MAX) {
+			if (t_info->tgt->tgt_is_active()) {
+				++retries;
+				/* Wait for CPE to be inactive */
+				usleep_range(5000, 5100);
+			} else {
+				break;
+			}
+		}
+
+		pr_debug("%s: cpe inactive after %d attempts\n",
+			 __func__, retries);
+
+		cpe_change_state(t_info, CPE_STATE_SENDING_MSG,
+				CPE_SS_MSG_SEND_INBOX);
+		rc = cpe_send_msg_to_inbox(t_info, 0, m);
+		break;
+
+	case CPE_CMD_SEND_MSG_COMPLETE:
+		hdr = CMI_GET_HEADER(t_info->tgt->outbox);
+		service = CMI_HDR_GET_SERVICE(hdr);
+		pr_debug("%s: msg send success, notifying clients\n",
+			 __func__);
+		cpe_command_cleanup(command_node);
+		t_info->pending = NULL;
+		cpe_change_state(t_info,
+				 CPE_STATE_IDLE, CPE_SS_IDLE);
+		cpe_notify_cmi_client(t_info,
+			t_info->tgt->outbox, CPE_SVC_SUCCESS);
+		break;
+
+	case CPE_CMD_PROC_INCOMING_MSG:
+		rc = cpe_process_incoming(t_info);
+		break;
+
+	case CPE_CMD_KILL_THREAD:
+		rc = cpe_process_kill_thread(t_info, command_node);
+		break;
+
+	default:
+		pr_err("%s: unhandled cpe cmd = %d\n",
+			__func__, command_node->command);
+		break;
+	}
+
+	if (cpe_rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: failed to execute command\n", __func__);
+		if (t_info->pending) {
+			m = (struct cpe_send_msg *)t_info->pending;
+			cpe_notify_cmi_client(t_info, m->payload,
+					      CPE_SVC_FAILED);
+			t_info->pending = NULL;
+		}
+
+		cpe_command_cleanup(command_node);
+		rc = CPE_PROC_FAILED;
+		cpe_change_state(t_info, CPE_STATE_IDLE,
+			CPE_SS_IDLE);
+	}
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_mt_validate_cmd(
+		const struct cpe_info *t_info,
+		enum cpe_command command)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	if ((t_info == NULL) || t_info->initialized == false) {
+		pr_err("%s: cpe service is not ready\n",
+			__func__);
+		return CPE_SVC_NOT_READY;
+	}
+
+	switch (t_info->state) {
+	case CPE_STATE_UNINITIALIZED:
+	case CPE_STATE_INITIALIZED:
+		switch (command) {
+		case CPE_CMD_RESET:
+		case CPE_CMD_DL_SEGMENT:
+		case CPE_CMD_RAMDUMP:
+		case CPE_CMD_PROCESS_IRQ:
+		case CPE_CMD_KILL_THREAD:
+		case CPE_CMD_DEINITIALIZE:
+		case CPE_CMD_FTM_TEST:
+			rc = CPE_SVC_SUCCESS;
+			break;
+		default:
+			rc = CPE_SVC_NOT_READY;
+			break;
+		}
+		break;
+
+	case CPE_STATE_DOWNLOADING:
+		switch (command) {
+		case CPE_CMD_RESET:
+		case CPE_CMD_DL_SEGMENT:
+		case CPE_CMD_BOOT:
+		case CPE_CMD_FTM_TEST:
+			rc = CPE_SVC_SUCCESS;
+			break;
+		default:
+			rc = CPE_SVC_NOT_READY;
+			break;
+		}
+		break;
+
+	case CPE_STATE_BOOTING:
+		switch (command) {
+		case CPE_CMD_PROCESS_IRQ:
+		case CPE_CMD_BOOT_INITIALIZE:
+		case CPE_CMD_BOOT_COMPLETE:
+		case CPE_CMD_SHUTDOWN:
+			rc = CPE_SVC_SUCCESS;
+			break;
+		case CPE_CMD_FTM_TEST:
+			rc = CPE_SVC_BUSY;
+			break;
+		default:
+			rc = CPE_SVC_NOT_READY;
+			break;
+		}
+		break;
+
+	case CPE_STATE_IDLE:
+		switch (command) {
+		case CPE_CMD_SEND_MSG:
+		case CPE_CMD_SEND_TRANS_MSG:
+		case CPE_CMD_SEND_MSG_COMPLETE:
+		case CPE_CMD_PROCESS_IRQ:
+		case CPE_CMD_RESET:
+		case CPE_CMD_SHUTDOWN:
+		case CPE_CMD_KILL_THREAD:
+		case CPE_CMD_PROC_INCOMING_MSG:
+			rc = CPE_SVC_SUCCESS;
+			break;
+		case CPE_CMD_FTM_TEST:
+			rc = CPE_SVC_BUSY;
+			break;
+		default:
+			rc = CPE_SVC_FAILED;
+			break;
+		}
+		break;
+
+	case CPE_STATE_SENDING_MSG:
+		switch (command) {
+		case CPE_CMD_SEND_MSG:
+		case CPE_CMD_SEND_TRANS_MSG:
+		case CPE_CMD_SEND_MSG_COMPLETE:
+		case CPE_CMD_PROCESS_IRQ:
+		case CPE_CMD_SHUTDOWN:
+		case CPE_CMD_KILL_THREAD:
+		case CPE_CMD_PROC_INCOMING_MSG:
+			rc = CPE_SVC_SUCCESS;
+			break;
+		case CPE_CMD_FTM_TEST:
+			rc = CPE_SVC_BUSY;
+			break;
+		default:
+			rc = CPE_SVC_FAILED;
+			break;
+		}
+		break;
+
+	case CPE_STATE_OFFLINE:
+		switch (command) {
+		case CPE_CMD_RESET:
+		case CPE_CMD_RAMDUMP:
+		case CPE_CMD_KILL_THREAD:
+			rc = CPE_SVC_SUCCESS;
+			break;
+		default:
+			rc = CPE_SVC_NOT_READY;
+			break;
+		}
+		break;
+
+	default:
+		pr_debug("%s: unhandled state %d\n",
+			 __func__, t_info->state);
+		break;
+	}
+
+	if (rc != CPE_SVC_SUCCESS)
+		pr_err("%s: invalid command %d, state = %d\n",
+			__func__, command, t_info->state);
+	return rc;
+}
+
+void *cpe_svc_initialize(
+		void irq_control_callback(u32 enable),
+		const void *codec_info, void *context)
+{
+	struct cpe_info *t_info = NULL;
+	const struct cpe_svc_hw_cfg *cap = NULL;
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_svc_init_param *init_context =
+		(struct cpe_svc_init_param *) context;
+	void *client_context = NULL;
+
+	if (cpe_d.cpe_default_handle &&
+	    cpe_d.cpe_default_handle->initialized == true)
+		return (void *)cpe_d.cpe_default_handle;
+	cpe_d.cpe_query_freq_plans_cb = NULL;
+	cpe_d.cpe_change_freq_plan_cb = NULL;
+
+	if (context) {
+		client_context = init_context->context;
+		switch (init_context->version) {
+		case CPE_SVC_INIT_PARAM_V1:
+			cpe_d.cpe_query_freq_plans_cb =
+				init_context->query_freq_plans_cb;
+			cpe_d.cpe_change_freq_plan_cb =
+				init_context->change_freq_plan_cb;
+			break;
+		default:
+			break;
+		}
+	}
+
+	if (!cpe_d.cpe_default_handle) {
+		cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info),
+					     GFP_KERNEL);
+		if (!cpe_d.cpe_default_handle)
+			goto err_register;
+
+		memset(cpe_d.cpe_default_handle, 0,
+		       sizeof(struct cpe_info));
+	}
+
+	t_info = cpe_d.cpe_default_handle;
+	t_info->client_context = client_context;
+
+	INIT_LIST_HEAD(&t_info->client_list);
+	cpe_d.cdc_priv = client_context;
+	INIT_WORK(&t_info->clk_plan_work, cpe_clk_plan_work);
+	init_completion(&t_info->core_svc_cmd_compl);
+
+	t_info->tgt = kzalloc(sizeof(struct cpe_svc_tgt_abstraction),
+			      GFP_KERNEL);
+	if (!t_info->tgt)
+		goto err_tgt_alloc;
+	t_info->codec_id =
+		((struct cpe_svc_codec_info_v1 *) codec_info)->id;
+
+	rc = cpe_svc_tgt_init((struct cpe_svc_codec_info_v1 *)codec_info,
+			t_info->tgt);
+
+	if (rc != CPE_SVC_SUCCESS)
+		goto err_tgt_init;
+
+	cap = t_info->tgt->tgt_get_cpe_info();
+
+	memset(t_info->tgt->outbox, 0, cap->outbox_size);
+	memset(t_info->tgt->inbox, 0, cap->inbox_size);
+	mutex_init(&t_info->msg_lock);
+	cpe_d.cpe_irq_control_callback = irq_control_callback;
+	t_info->cpe_process_command = cpe_mt_process_cmd;
+	t_info->cpe_cmd_validate = cpe_mt_validate_cmd;
+	t_info->cpe_start_notification = broadcast_boot_event;
+	mutex_init(&cpe_d.cpe_api_mutex);
+	mutex_init(&cpe_d.cpe_svc_lock);
+	pr_debug("%s: cpe services initialized\n", __func__);
+	t_info->state = CPE_STATE_INITIALIZED;
+	t_info->initialized = true;
+
+	return t_info;
+
+err_tgt_init:
+	kfree(t_info->tgt);
+
+err_tgt_alloc:
+	kfree(cpe_d.cpe_default_handle);
+	cpe_d.cpe_default_handle = NULL;
+
+err_register:
+	return NULL;
+}
+
+enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_DEINITIALIZE);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Invalid command %d\n",
+			__func__, CPE_CMD_DEINITIALIZE);
+		return rc;
+	}
+
+	if (cpe_d.cpe_default_handle == t_info)
+		cpe_d.cpe_default_handle = NULL;
+
+	t_info->tgt->tgt_deinit(t_info->tgt);
+	cpe_change_state(t_info, CPE_STATE_UNINITIALIZED,
+			 CPE_SS_IDLE);
+	mutex_destroy(&t_info->msg_lock);
+	kfree(t_info->tgt);
+	kfree(t_info);
+	mutex_destroy(&cpe_d.cpe_api_mutex);
+	mutex_destroy(&cpe_d.cpe_svc_lock);
+
+	return rc;
+}
+
+void *cpe_svc_register(void *cpe_handle,
+		void (*notification_callback)
+			(const struct cpe_svc_notification *parameter),
+		u32 mask, const char *name)
+{
+	void *reg_handle;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!cpe_d.cpe_default_handle) {
+		cpe_d.cpe_default_handle = kzalloc(sizeof(struct cpe_info),
+					     GFP_KERNEL);
+		if (!cpe_d.cpe_default_handle) {
+			CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+			return NULL;
+		}
+
+		memset(cpe_d.cpe_default_handle, 0,
+			sizeof(struct cpe_info));
+	}
+
+	if (!cpe_handle)
+		cpe_handle = cpe_d.cpe_default_handle;
+
+	reg_handle = cpe_register_generic((struct cpe_info *)cpe_handle,
+					   notification_callback,
+					   NULL,
+					   mask, CPE_NO_SERVICE, name);
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return reg_handle;
+}
+
+enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle)
+{
+	enum cpe_svc_result rc;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!cpe_handle)
+		cpe_handle = cpe_d.cpe_default_handle;
+
+	rc = cpe_deregister_generic((struct cpe_info *)cpe_handle,
+				    reg_handle);
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle,
+	const struct cpe_svc_mem_segment *segment)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_DL_SEGMENT);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: cmd validation fail, cmd = %d\n",
+			__func__, CPE_CMD_DL_SEGMENT);
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return rc;
+	}
+
+	cpe_toggle_irq_notification(t_info, false);
+	t_info->state = CPE_STATE_DOWNLOADING;
+	t_info->substate = CPE_SS_DL_DOWNLOADING;
+	rc = t_info->tgt->tgt_write_ram(t_info, segment);
+	cpe_toggle_irq_notification(t_info, true);
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_BOOT);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: cmd validation fail, cmd = %d\n",
+			__func__, CPE_CMD_BOOT);
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return rc;
+	}
+
+	if (rc == CPE_SVC_SUCCESS) {
+		t_info->tgt->tgt_boot(debug_mode);
+		t_info->state = CPE_STATE_BOOTING;
+		t_info->substate = CPE_SS_BOOT;
+		pr_debug("%s: cpe service booting\n",
+			 __func__);
+	}
+
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	cpe_toggle_irq_notification(t_info, false);
+	cpe_process_irq_int(cpe_irq, t_info);
+	cpe_toggle_irq_notification(t_info, true);
+
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_route_notification(void *cpe_handle,
+		enum cpe_svc_module module, enum cpe_svc_route_dest dest)
+{
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+	enum cpe_svc_result rc = CPE_SVC_NOT_READY;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	if (t_info->tgt)
+		rc = t_info->tgt->tgt_route_notification(module, dest);
+
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	return rc;
+}
+
+static enum cpe_svc_result __cpe_svc_shutdown(void *cpe_handle)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+	struct cpe_command_node *n = NULL;
+	struct cpe_command_node kill_cmd;
+
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_SHUTDOWN);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: cmd validation fail, cmd = %d\n",
+			__func__, CPE_CMD_SHUTDOWN);
+		return rc;
+	}
+
+	while (!list_empty(&t_info->main_queue)) {
+		n = list_first_entry(&t_info->main_queue,
+				     struct cpe_command_node, list);
+
+		if (n->command == CPE_CMD_SEND_MSG) {
+			cpe_notify_cmi_client(t_info, (u8 *)n->data,
+				CPE_SVC_SHUTTING_DOWN);
+		}
+		/*
+		 * Since command cannot be processed,
+		 * delete it from the list and perform cleanup
+		 */
+		list_del(&n->list);
+		cpe_command_cleanup(n);
+		kfree(n);
+	}
+
+	pr_debug("%s: cpe service OFFLINE state\n", __func__);
+
+	t_info->state = CPE_STATE_OFFLINE;
+	t_info->substate = CPE_SS_IDLE;
+
+	memset(&kill_cmd, 0, sizeof(kill_cmd));
+	kill_cmd.command = CPE_CMD_KILL_THREAD;
+
+	if (t_info->pending) {
+		struct cpe_send_msg *m =
+			(struct cpe_send_msg *)t_info->pending;
+		cpe_notify_cmi_client(t_info, m->payload,
+			CPE_SVC_SHUTTING_DOWN);
+		kfree(t_info->pending);
+		t_info->pending = NULL;
+	}
+
+	cpe_cleanup_worker_thread(t_info);
+	t_info->cpe_process_command(&kill_cmd);
+
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	rc = __cpe_svc_shutdown(cpe_handle);
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_reset(void *cpe_handle)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_RESET);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: cmd validation fail, cmd = %d\n",
+			__func__, CPE_CMD_RESET);
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return rc;
+	}
+
+	if (t_info && t_info->tgt) {
+		rc = t_info->tgt->tgt_reset();
+		pr_debug("%s: cpe services in INITIALIZED state\n",
+			 __func__);
+		t_info->state = CPE_STATE_INITIALIZED;
+		t_info->substate = CPE_SS_IDLE;
+	}
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle,
+		struct cpe_svc_mem_segment *buffer)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_RAMDUMP);
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: cmd validation fail, cmd = %d\n",
+			__func__, CPE_CMD_RAMDUMP);
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return rc;
+	}
+
+	if (t_info->tgt) {
+		rc = t_info->tgt->tgt_read_ram(t_info, buffer);
+	} else {
+		pr_err("%s: cpe service not ready\n", __func__);
+		rc = CPE_SVC_NOT_READY;
+	}
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode)
+{
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+	enum cpe_svc_result rc = CPE_SVC_INVALID_HANDLE;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	if (t_info->tgt)
+		rc = t_info->tgt->tgt_set_debug_mode(mode);
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return rc;
+}
+
+const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle)
+{
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	if (t_info->tgt)
+		return t_info->tgt->tgt_get_cpe_info();
+
+	return NULL;
+}
+
+void *cmi_register(
+		void notification_callback(
+			const struct cmi_api_notification *parameter),
+		u32 service)
+{
+	void *reg_handle = NULL;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	reg_handle = cpe_register_generic(cpe_d.cpe_default_handle,
+			NULL,
+			notification_callback,
+			(CPE_SVC_CMI_MSG | CPE_SVC_OFFLINE |
+			 CPE_SVC_ONLINE),
+			service,
+			"CMI_CLIENT");
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+
+	return reg_handle;
+}
+
+enum cmi_api_result cmi_deregister(void *reg_handle)
+{
+	u32 clients = 0;
+	struct cpe_notif_node *n = NULL;
+	enum cmi_api_result rc = CMI_API_SUCCESS;
+	struct cpe_svc_notification payload;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	rc = (enum cmi_api_result) cpe_deregister_generic(
+		cpe_d.cpe_default_handle, reg_handle);
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+	list_for_each_entry(n, &cpe_d.cpe_default_handle->client_list, list) {
+		if (n->mask & CPE_SVC_CMI_MSG)
+			clients++;
+	}
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_svc_lock, "cpe_svc");
+
+	if (clients == 0) {
+		payload.event = CPE_SVC_CMI_CLIENTS_DEREG;
+		payload.payload = NULL;
+		payload.result = CPE_SVC_SUCCESS;
+		cpe_broadcast_notification(cpe_d.cpe_default_handle, &payload);
+	}
+
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	return rc;
+}
+
+enum cmi_api_result cmi_send_msg(void *message)
+{
+	enum cmi_api_result rc = CMI_API_SUCCESS;
+	struct cpe_send_msg *msg = NULL;
+	struct cmi_hdr *hdr;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	hdr = CMI_GET_HEADER(message);
+	msg = kzalloc(sizeof(struct cpe_send_msg),
+		      GFP_ATOMIC);
+	if (!msg) {
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return CPE_SVC_NO_MEMORY;
+	}
+
+	if (CMI_HDR_GET_OBM_FLAG(hdr) == CMI_OBM_FLAG_OUT_BAND)
+		msg->isobm = 1;
+	else
+		msg->isobm = 0;
+
+	msg->size = sizeof(struct cmi_hdr) +
+			CMI_HDR_GET_PAYLOAD_SIZE(hdr);
+
+	msg->payload = kzalloc(msg->size, GFP_ATOMIC);
+	if (!msg->payload) {
+		kfree(msg);
+		CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+		return CPE_SVC_NO_MEMORY;
+	}
+
+	msg->address = 0;
+	memcpy((void *)msg->payload, message, msg->size);
+
+	rc = (enum cmi_api_result) cpe_send_cmd_to_thread(
+			cpe_d.cpe_default_handle,
+			CPE_CMD_SEND_MSG,
+			(void *)msg, false);
+
+	if (rc != 0) {
+		pr_err("%s: Failed to queue message\n", __func__);
+		kfree(msg->payload);
+		kfree(msg);
+	}
+
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+	struct cpe_svc_mem_segment backup_seg;
+	struct cpe_svc_mem_segment waiti_seg;
+	u8 *backup_data = NULL;
+
+	CPE_SVC_GRAB_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	rc = cpe_is_command_valid(t_info, CPE_CMD_FTM_TEST);
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: cmd validation fail, cmd = %d\n",
+			__func__, CPE_CMD_FTM_TEST);
+		goto fail_cmd;
+	}
+
+	if (t_info && t_info->tgt) {
+		backup_data = kzalloc(
+				t_info->tgt->tgt_waiti_info->tgt_waiti_size,
+				GFP_KERNEL);
+
+		/* CPE reset */
+		rc = t_info->tgt->tgt_reset();
+		if (rc != CPE_SVC_SUCCESS) {
+			pr_err("%s: CPE reset fail! err = %d\n",
+				__func__, rc);
+			goto err_return;
+		}
+
+		/* Back up the 4 byte IRAM data first */
+		backup_seg.type = CPE_SVC_INSTRUCTION_MEM;
+		backup_seg.cpe_addr =
+			t_info->tgt->tgt_get_cpe_info()->IRAM_offset;
+		backup_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size;
+		backup_seg.data = backup_data;
+
+		pr_debug("%s: Backing up IRAM data from CPE\n",
+			__func__);
+
+		rc = t_info->tgt->tgt_read_ram(t_info, &backup_seg);
+		if (rc != CPE_SVC_SUCCESS) {
+			pr_err("%s: Fail to backup CPE IRAM data, err = %d\n",
+				__func__, rc);
+			goto err_return;
+		}
+
+		pr_debug("%s: Complete backing up IRAM data from CPE\n",
+			__func__);
+
+		/* Write the WAITI instruction data */
+		waiti_seg.type = CPE_SVC_INSTRUCTION_MEM;
+		waiti_seg.cpe_addr =
+			t_info->tgt->tgt_get_cpe_info()->IRAM_offset;
+		waiti_seg.size = t_info->tgt->tgt_waiti_info->tgt_waiti_size;
+		waiti_seg.data = t_info->tgt->tgt_waiti_info->tgt_waiti_data;
+
+		rc = t_info->tgt->tgt_write_ram(t_info, &waiti_seg);
+		if (rc != CPE_SVC_SUCCESS) {
+			pr_err("%s: Fail to write the WAITI data, err = %d\n",
+				__func__, rc);
+			goto restore_iram;
+		}
+
+		/* Boot up cpe to execute the WAITI instructions */
+		rc = t_info->tgt->tgt_boot(1);
+		if (rc != CPE_SVC_SUCCESS) {
+			pr_err("%s: Fail to boot CPE, err = %d\n",
+				__func__, rc);
+			goto reset;
+		}
+
+		/*
+		 * 1ms delay is suggested by the hw team to
+		 * wait for cpe to boot up.
+		 */
+		usleep_range(1000, 1100);
+
+		/* Check if the cpe init is done after executing the WAITI */
+		*status = t_info->tgt->tgt_cpar_init_done();
+
+reset:
+		/* Set the cpe back to reset state */
+		rc = t_info->tgt->tgt_reset();
+		if (rc != CPE_SVC_SUCCESS) {
+			pr_err("%s: CPE reset fail! err = %d\n",
+				__func__, rc);
+			goto restore_iram;
+		}
+
+restore_iram:
+		/* Restore the IRAM 4 bytes data */
+		rc = t_info->tgt->tgt_write_ram(t_info, &backup_seg);
+		if (rc != CPE_SVC_SUCCESS) {
+			pr_err("%s: Fail to restore the IRAM data, err = %d\n",
+				__func__, rc);
+			goto err_return;
+		}
+	}
+
+err_return:
+	kfree(backup_data);
+fail_cmd:
+	CPE_SVC_REL_LOCK(&cpe_d.cpe_api_mutex, "cpe_api");
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_boot(int debug_mode)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	if (!debug_mode)
+		rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG,
+				     0x3F, 0x31);
+	else
+		pr_info("%s: CPE in debug mode, WDOG disabled\n",
+			__func__);
+
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+			     0x02, 0x00);
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+			     0x0C, 0x04);
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG,
+			     0x01, 0x01);
+
+	return rc;
+}
+
+static u32 cpe_tgt_tomtom_is_cpar_init_done(void)
+{
+	u8 status = 0;
+
+	cpe_register_read(TOMTOM_A_SVASS_STATUS, &status);
+	return status & 0x01;
+}
+
+static u32 cpe_tgt_tomtom_is_active(void)
+{
+	u8 status = 0;
+
+	cpe_register_read(TOMTOM_A_SVASS_STATUS, &status);
+	return status & 0x04;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_reset(void)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_WDOG_CFG,
+			     0x30, 0x00);
+
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CPAR_CFG,
+			     0x01, 0x00);
+	rc = cpe_update_bits(TOMTOM_A_MEM_LEAKAGE_CTL,
+			     0x07, 0x03);
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+			     0x08, 0x08);
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CLKRST_CTL,
+			     0x02, 0x02);
+	return rc;
+}
+
+enum cpe_svc_result cpe_tgt_tomtom_voicetx(bool enable)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 val = 0;
+
+	if (enable)
+		val = 0x02;
+	else
+		val = 0x00;
+	rc = cpe_update_bits(TOMTOM_A_SVASS_CFG,
+			     0x02, val);
+	val = 0;
+	cpe_register_read(TOMTOM_A_SVASS_CFG, &val);
+	return rc;
+}
+
+enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable)
+{
+
+	struct cpe_info *t_info = (struct cpe_info *)cpe_handle;
+
+	if (!t_info)
+		t_info = cpe_d.cpe_default_handle;
+
+	if (t_info->tgt)
+		return t_info->tgt->tgt_voice_tx_lab(enable);
+	else
+		return CPE_SVC_INVALID_HANDLE;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_read_mailbox(u8 *buffer,
+	size_t size)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u32 cnt = 0;
+
+	if (size >= TOMTOM_A_SVASS_SPE_OUTBOX_SIZE)
+		size = TOMTOM_A_SVASS_SPE_OUTBOX_SIZE - 1;
+	for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) {
+		rc = cpe_register_read(TOMTOM_A_SVASS_SPE_OUTBOX(cnt),
+			&(buffer[cnt]));
+	}
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_write_mailbox(u8 *buffer,
+	size_t size)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u32 cnt = 0;
+
+	if (size >= TOMTOM_A_SVASS_SPE_INBOX_SIZE)
+		size = TOMTOM_A_SVASS_SPE_INBOX_SIZE - 1;
+	for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) {
+		rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX(cnt),
+			buffer[cnt]);
+	}
+
+	if (rc == CPE_SVC_SUCCESS)
+		rc = cpe_register_write(TOMTOM_A_SVASS_SPE_INBOX_TRG, 1);
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_get_mem_addr(struct cpe_info *t_info,
+		const struct cpe_svc_mem_segment *mem_seg,
+		u32 *addr, u8 *mem)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u32 offset, mem_sz, address;
+	u8 mem_type;
+
+	switch (mem_seg->type) {
+
+	case CPE_SVC_DATA_MEM:
+		mem_type = MEM_ACCESS_DRAM_VAL;
+		offset = TOMTOM_A_SVASS_SPE_DRAM_OFFSET;
+		mem_sz = TOMTOM_A_SVASS_SPE_DRAM_SIZE;
+		break;
+
+	case CPE_SVC_INSTRUCTION_MEM:
+		mem_type = MEM_ACCESS_IRAM_VAL;
+		offset = TOMTOM_A_SVASS_SPE_IRAM_OFFSET;
+		mem_sz = TOMTOM_A_SVASS_SPE_IRAM_SIZE;
+		break;
+
+	default:
+		pr_err("%s: Invalid mem type = %u\n",
+			__func__, mem_seg->type);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	if (mem_seg->cpe_addr < offset) {
+		pr_err("%s: Invalid addr %x for mem type %u\n",
+			__func__, mem_seg->cpe_addr, mem_type);
+			return CPE_SVC_INVALID_HANDLE;
+	}
+
+	address = mem_seg->cpe_addr - offset;
+	if (address + mem_seg->size > mem_sz) {
+		pr_err("%s: wrong size %zu, start address %x, mem_type %u\n",
+			__func__, mem_seg->size, address, mem_type);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	(*addr) = address;
+	(*mem) = mem_type;
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_read_RAM(struct cpe_info *t_info,
+		struct cpe_svc_mem_segment *mem_seg)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 mem_reg_val = 0;
+	u32 cnt = 0;
+	bool autoinc;
+	u8 mem = MEM_ACCESS_NONE_VAL;
+	u32 addr = 0;
+	u32 ptr_update = true;
+
+	if (!mem_seg) {
+		pr_err("%s: Invalid mem segment\n",
+			__func__);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Cannot obtain address, mem_type %u\n",
+			__func__, mem_seg->type);
+		return rc;
+	}
+
+	rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+	autoinc = cpe_register_read_autoinc_supported();
+	if (autoinc)
+		mem_reg_val |= 0x04;
+
+	mem_reg_val |= 0x08;
+	mem_reg_val |= mem;
+
+	do {
+		if (!autoinc || ptr_update) {
+			rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0,
+				(addr & 0xFF));
+			rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1,
+				((addr >> 8) & 0xFF));
+			rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2,
+				((addr >> 16) & 0xFF));
+
+			rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL,
+						mem_reg_val);
+
+			ptr_update = false;
+		}
+		rc = cpe_register_read(TOMTOM_A_SVASS_MEM_BANK,
+			&mem_seg->data[cnt]);
+
+		if (!autoinc)
+			rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+	} while (++cnt < mem_seg->size);
+
+	rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_write_RAM(struct cpe_info *t_info,
+		const struct cpe_svc_mem_segment *mem_seg)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 mem_reg_val = 0;
+	u8 mem = MEM_ACCESS_NONE_VAL;
+	u32 addr = 0;
+	u8 *temp_ptr = NULL;
+	u32 temp_size = 0;
+	bool autoinc;
+
+	if (!mem_seg) {
+		pr_err("%s: Invalid mem segment\n",
+			__func__);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	rc = cpe_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Cannot obtain address, mem_type %u\n",
+			__func__, mem_seg->type);
+		return rc;
+	}
+
+	autoinc = cpe_register_read_autoinc_supported();
+	if (autoinc)
+		mem_reg_val |= 0x04;
+	mem_reg_val |= mem;
+
+	rc = cpe_update_bits(TOMTOM_A_SVASS_MEM_CTL,
+			     0x0F, mem_reg_val);
+
+	rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR0,
+				(addr & 0xFF));
+	rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR1,
+				((addr >> 8) & 0xFF));
+
+	rc = cpe_register_write(TOMTOM_A_SVASS_MEM_PTR2,
+				((addr >> 16) & 0xFF));
+
+	temp_size = 0;
+	temp_ptr = mem_seg->data;
+
+	while (temp_size <= mem_seg->size) {
+		u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE)
+			? CHUNK_SIZE : (mem_seg->size-temp_size);
+
+		if (t_info->state == CPE_STATE_OFFLINE) {
+			pr_err("%s: CPE is offline\n", __func__);
+			return CPE_SVC_FAILED;
+		}
+
+		cpe_register_write_repeat(TOMTOM_A_SVASS_MEM_BANK,
+			temp_ptr, to_write);
+		temp_size += CHUNK_SIZE;
+		temp_ptr += CHUNK_SIZE;
+	}
+
+	rc = cpe_register_write(TOMTOM_A_SVASS_MEM_CTL, 0);
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_route_notification(
+		enum cpe_svc_module module,
+		enum cpe_svc_route_dest dest)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 ctl_reg_val = 0;
+
+	switch (module) {
+	case CPE_SVC_LISTEN_PROC:
+		switch (dest) {
+		case CPE_SVC_EXTERNAL:
+			ctl_reg_val = LISTEN_CTL_MSM_VAL;
+			break;
+		case CPE_SVC_INTERNAL:
+			ctl_reg_val = LISTEN_CTL_SPE_VAL;
+			break;
+		default:
+			pr_err("%s: Invalid dest %d\n",
+				__func__, dest);
+			return CPE_SVC_FAILED;
+		}
+
+		rc = cpe_update_bits(TOMTOM_A_SVASS_CFG,
+				     0x01, ctl_reg_val);
+		break;
+	default:
+		pr_err("%s: Invalid module %d\n",
+			__func__, module);
+		rc = CPE_SVC_FAILED;
+		break;
+	}
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_set_debug_mode(u32 enable)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 dbg_reg_val = 0x00;
+
+	if (enable)
+		dbg_reg_val = 0x08;
+	rc = cpe_update_bits(TOMTOM_A_SVASS_DEBUG,
+			     0x08, dbg_reg_val);
+	return rc;
+}
+
+static const struct cpe_svc_hw_cfg *cpe_tgt_tomtom_get_cpe_info(void)
+{
+	return &cpe_svc_tomtom_info;
+}
+
+static enum cpe_svc_result cpe_tgt_tomtom_deinit(
+		struct cpe_svc_tgt_abstraction *param)
+{
+	kfree(param->inbox);
+	param->inbox = NULL;
+	kfree(param->outbox);
+	param->outbox = NULL;
+	memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction));
+	return CPE_SVC_SUCCESS;
+}
+
+static u8 cpe_tgt_tomtom_waiti_data[] = {0x00, 0x70, 0x00, 0x00};
+
+static struct cpe_tgt_waiti_info cpe_tgt_tomtom_waiti_info = {
+	.tgt_waiti_size = ARRAY_SIZE(cpe_tgt_tomtom_waiti_data),
+	.tgt_waiti_data = cpe_tgt_tomtom_waiti_data,
+};
+
+static enum cpe_svc_result cpe_tgt_tomtom_init(
+		struct cpe_svc_codec_info_v1 *codec_info,
+		struct cpe_svc_tgt_abstraction *param)
+{
+	if (!codec_info)
+		return CPE_SVC_INVALID_HANDLE;
+	if (!param)
+		return CPE_SVC_INVALID_HANDLE;
+
+	if (codec_info->id == CPE_SVC_CODEC_TOMTOM) {
+		param->tgt_boot      = cpe_tgt_tomtom_boot;
+		param->tgt_cpar_init_done = cpe_tgt_tomtom_is_cpar_init_done;
+		param->tgt_is_active = cpe_tgt_tomtom_is_active;
+		param->tgt_reset = cpe_tgt_tomtom_reset;
+		param->tgt_read_mailbox = cpe_tgt_tomtom_read_mailbox;
+		param->tgt_write_mailbox = cpe_tgt_tomtom_write_mailbox;
+		param->tgt_read_ram = cpe_tgt_tomtom_read_RAM;
+		param->tgt_write_ram = cpe_tgt_tomtom_write_RAM;
+		param->tgt_route_notification =
+			cpe_tgt_tomtom_route_notification;
+		param->tgt_set_debug_mode = cpe_tgt_tomtom_set_debug_mode;
+		param->tgt_get_cpe_info = cpe_tgt_tomtom_get_cpe_info;
+		param->tgt_deinit = cpe_tgt_tomtom_deinit;
+		param->tgt_voice_tx_lab = cpe_tgt_tomtom_voicetx;
+		param->tgt_waiti_info = &cpe_tgt_tomtom_waiti_info;
+
+		param->inbox = kzalloc(TOMTOM_A_SVASS_SPE_INBOX_SIZE,
+				       GFP_KERNEL);
+		if (!param->inbox)
+			return CPE_SVC_NO_MEMORY;
+
+		param->outbox = kzalloc(TOMTOM_A_SVASS_SPE_OUTBOX_SIZE,
+					GFP_KERNEL);
+		if (!param->outbox) {
+			kfree(param->inbox);
+			return CPE_SVC_NO_MEMORY;
+		}
+	}
+
+	return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_boot(int debug_mode)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	if (!debug_mode)
+		rc |= cpe_update_bits(
+				WCD9335_CPE_SS_WDOG_CFG,
+				0x3f, 0x31);
+	else
+		pr_info("%s: CPE in debug mode, WDOG disabled\n",
+			__func__);
+
+	rc |= cpe_register_write(WCD9335_CPE_SS_CPARMAD_BUFRDY_INT_PERIOD, 19);
+	rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x00);
+	rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x02, 0x02);
+	rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x01, 0x01);
+
+	if (unlikely(rc)) {
+		pr_err("%s: Failed to boot, err = %d\n",
+			__func__, rc);
+		rc = CPE_SVC_FAILED;
+	}
+
+	return rc;
+}
+
+static u32 cpe_tgt_wcd9335_is_cpar_init_done(void)
+{
+	u8 temp = 0;
+
+	cpe_register_read(WCD9335_CPE_SS_STATUS, &temp);
+	return temp & 0x1;
+}
+
+static u32 cpe_tgt_wcd9335_is_active(void)
+{
+	u8 temp = 0;
+
+	cpe_register_read(WCD9335_CPE_SS_STATUS, &temp);
+	return temp & 0x4;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_reset(void)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CFG, 0x01, 0x00);
+
+	rc |= cpe_register_write(
+		WCD9335_CODEC_RPM_PWR_CPE_IRAM_SHUTDOWN, 0x00);
+	rc |= cpe_register_write(
+		WCD9335_CODEC_RPM_PWR_CPE_DRAM1_SHUTDOWN, 0x00);
+	rc |= cpe_register_write(
+		WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_1, 0x00);
+	rc |= cpe_register_write(
+		WCD9335_CODEC_RPM_PWR_CPE_DRAM0_SHUTDOWN_2, 0x00);
+
+	rc |= cpe_update_bits(WCD9335_CPE_SS_CPAR_CTL, 0x04, 0x04);
+
+	if (unlikely(rc)) {
+		pr_err("%s: failed to reset cpe, err = %d\n",
+			__func__, rc);
+		rc = CPE_SVC_FAILED;
+	}
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_read_mailbox(u8 *buffer,
+	size_t size)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u32 cnt = 0;
+
+	pr_debug("%s: size=%zd\n", __func__, size);
+
+	if (size > WCD9335_CPE_SS_SPE_OUTBOX_SIZE)
+		size = WCD9335_CPE_SS_SPE_OUTBOX_SIZE;
+
+	for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++)
+		rc = cpe_register_read(WCD9335_CPE_SS_SPE_OUTBOX1(cnt),
+				       &buffer[cnt]);
+
+	rc = cpe_register_write(WCD9335_CPE_SS_OUTBOX1_ACK, 0x01);
+
+	if (unlikely(rc)) {
+		pr_err("%s: failed to ACK outbox, err = %d\n",
+			__func__, rc);
+		rc = CPE_SVC_FAILED;
+	}
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_write_mailbox(u8 *buffer,
+	size_t size)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u32 cnt = 0;
+
+	pr_debug("%s: size = %zd\n", __func__, size);
+	if (size > WCD9335_CPE_SS_SPE_INBOX_SIZE)
+		size = WCD9335_CPE_SS_SPE_INBOX_SIZE;
+	for (cnt = 0; (cnt < size) && (rc == CPE_SVC_SUCCESS); cnt++) {
+		rc |= cpe_register_write(WCD9335_CPE_SS_SPE_INBOX1(cnt),
+			buffer[cnt]);
+	}
+
+	if (unlikely(rc)) {
+		pr_err("%s: Error %d writing mailbox registers\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = cpe_register_write(WCD9335_CPE_SS_INBOX1_TRG, 1);
+	return rc;
+}
+
+static enum cpe_svc_result cpe_wcd9335_get_mem_addr(struct cpe_info *t_info,
+		const struct cpe_svc_mem_segment *mem_seg,
+		u32 *addr, u8 *mem)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u32 offset, mem_sz, address;
+	u8 mem_type;
+
+	switch (mem_seg->type) {
+	case CPE_SVC_DATA_MEM:
+		mem_type = MEM_ACCESS_DRAM_VAL;
+		offset = WCD9335_CPE_SS_SPE_DRAM_OFFSET;
+		mem_sz = WCD9335_CPE_SS_SPE_DRAM_SIZE;
+		break;
+
+	case CPE_SVC_INSTRUCTION_MEM:
+		mem_type = MEM_ACCESS_IRAM_VAL;
+		offset = WCD9335_CPE_SS_SPE_IRAM_OFFSET;
+		mem_sz = WCD9335_CPE_SS_SPE_IRAM_SIZE;
+		break;
+
+	default:
+		pr_err("%s: Invalid mem type = %u\n",
+			__func__, mem_seg->type);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	if (mem_seg->cpe_addr < offset) {
+		pr_err("%s: Invalid addr %x for mem type %u\n",
+			__func__, mem_seg->cpe_addr, mem_type);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	address = mem_seg->cpe_addr - offset;
+	if (address + mem_seg->size > mem_sz) {
+		pr_err("%s: wrong size %zu, start address %x, mem_type %u\n",
+			__func__, mem_seg->size, address, mem_type);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	(*addr) = address;
+	(*mem) = mem_type;
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_read_RAM(struct cpe_info *t_info,
+		struct cpe_svc_mem_segment *mem_seg)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 temp = 0;
+	u32 cnt = 0;
+	u8 mem = 0x0;
+	u32 addr = 0;
+	u32 lastaddr = 0;
+	u32 ptr_update = true;
+	bool autoinc;
+
+	if (!mem_seg) {
+		pr_err("%s: Invalid buffer\n", __func__);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Cannot obtain address, mem_type %u\n",
+			__func__, mem_seg->type);
+		return rc;
+	}
+
+	rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+	autoinc = cpe_register_read_autoinc_supported();
+
+	if (autoinc)
+		temp = 0x18;
+	else
+		temp = 0x10;
+
+	temp |= mem;
+
+	lastaddr = ~addr;
+	do {
+		if (!autoinc || (ptr_update)) {
+			/* write LSB only if modified */
+			if ((lastaddr & 0xFF) != (addr & 0xFF))
+				rc |= cpe_register_write(
+						WCD9335_CPE_SS_MEM_PTR_0,
+						(addr & 0xFF));
+			/* write middle byte only if modified */
+			if (((lastaddr >> 8) & 0xFF) != ((addr >> 8) & 0xFF))
+				rc |= cpe_register_write(
+						WCD9335_CPE_SS_MEM_PTR_1,
+						((addr>>8) & 0xFF));
+			/* write MSB only if modified */
+			if (((lastaddr >> 16) & 0xFF) != ((addr >> 16) & 0xFF))
+				rc |= cpe_register_write(
+						WCD9335_CPE_SS_MEM_PTR_2,
+						((addr>>16) & 0xFF));
+
+			rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, temp);
+			lastaddr = addr;
+			addr++;
+			ptr_update = false;
+		}
+
+		rc |= cpe_register_read(WCD9335_CPE_SS_MEM_BANK_0,
+				       &mem_seg->data[cnt]);
+
+		if (!autoinc)
+			rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+	} while ((++cnt < mem_seg->size) ||
+		 (rc != CPE_SVC_SUCCESS));
+
+	rc |= cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+
+	if (rc)
+		pr_err("%s: Failed to read registers, err = %d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_write_RAM(struct cpe_info *t_info,
+		const struct cpe_svc_mem_segment *mem_seg)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 mem_reg_val = 0;
+	u8 mem = MEM_ACCESS_NONE_VAL;
+	u32 addr = 0;
+	u8 *temp_ptr = NULL;
+	u32 temp_size = 0;
+	bool autoinc;
+
+	if (!mem_seg) {
+		pr_err("%s: Invalid mem segment\n",
+			__func__);
+		return CPE_SVC_INVALID_HANDLE;
+	}
+
+	rc = cpe_wcd9335_get_mem_addr(t_info, mem_seg, &addr, &mem);
+
+	if (rc != CPE_SVC_SUCCESS) {
+		pr_err("%s: Cannot obtain address, mem_type %u\n",
+			__func__, mem_seg->type);
+		return rc;
+	}
+
+	autoinc = cpe_register_read_autoinc_supported();
+	if (autoinc)
+		mem_reg_val = 0x18;
+	else
+		mem_reg_val = 0x10;
+
+	mem_reg_val |= mem;
+
+	rc = cpe_update_bits(WCD9335_CPE_SS_MEM_CTRL,
+			     0x0F, mem_reg_val);
+
+	rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_0,
+				(addr & 0xFF));
+	rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_1,
+				((addr >> 8) & 0xFF));
+
+	rc = cpe_register_write(WCD9335_CPE_SS_MEM_PTR_2,
+				((addr >> 16) & 0xFF));
+
+	temp_size = 0;
+	temp_ptr = mem_seg->data;
+
+	while (temp_size <= mem_seg->size) {
+		u32 to_write = (mem_seg->size >= temp_size+CHUNK_SIZE)
+			? CHUNK_SIZE : (mem_seg->size - temp_size);
+
+		if (t_info->state == CPE_STATE_OFFLINE) {
+			pr_err("%s: CPE is offline\n", __func__);
+			return CPE_SVC_FAILED;
+		}
+
+		cpe_register_write_repeat(WCD9335_CPE_SS_MEM_BANK_0,
+			temp_ptr, to_write);
+		temp_size += CHUNK_SIZE;
+		temp_ptr += CHUNK_SIZE;
+	}
+
+	rc = cpe_register_write(WCD9335_CPE_SS_MEM_CTRL, 0);
+
+	if (rc)
+		pr_err("%s: Failed to write registers, err = %d\n",
+			__func__, rc);
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_route_notification(
+		enum cpe_svc_module module,
+		enum cpe_svc_route_dest dest)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	pr_debug("%s: Module = %d, Destination = %d\n",
+		 __func__, module, dest);
+
+	switch (module) {
+	case CPE_SVC_LISTEN_PROC:
+		switch (dest) {
+		case CPE_SVC_EXTERNAL:
+			rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x01);
+			break;
+		case CPE_SVC_INTERNAL:
+			rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x01, 0x00);
+			break;
+		default:
+			pr_err("%s: Invalid destination %d\n",
+				__func__, dest);
+			return CPE_SVC_FAILED;
+		}
+		break;
+	default:
+		pr_err("%s: Invalid module %d\n",
+			__func__, module);
+		rc = CPE_SVC_FAILED;
+		break;
+	}
+	return rc;
+}
+
+static enum cpe_svc_result cpe_tgt_wcd9335_set_debug_mode(u32 enable)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+
+	pr_debug("%s: enable = %s\n", __func__,
+		 (enable) ? "true" : "false");
+
+	return rc;
+}
+
+static const struct cpe_svc_hw_cfg *cpe_tgt_wcd9335_get_cpe_info(void)
+{
+	return &cpe_svc_wcd9335_info;
+}
+
+static enum cpe_svc_result
+cpe_tgt_wcd9335_deinit(struct cpe_svc_tgt_abstraction *param)
+{
+	kfree(param->inbox);
+	param->inbox = NULL;
+	kfree(param->outbox);
+	param->outbox = NULL;
+	memset(param, 0, sizeof(struct cpe_svc_tgt_abstraction));
+
+	return CPE_SVC_SUCCESS;
+}
+
+static enum cpe_svc_result
+	cpe_tgt_wcd9335_voicetx(bool enable)
+{
+	enum cpe_svc_result rc = CPE_SVC_SUCCESS;
+	u8 val = 0;
+
+	pr_debug("%s: enable = %u\n", __func__, enable);
+	if (enable)
+		val = 0x02;
+	else
+		val = 0x00;
+
+	rc = cpe_update_bits(WCD9335_CPE_SS_CFG, 0x02, val);
+	val = 0;
+	cpe_register_read(WCD9335_CPE_SS_CFG, &val);
+
+	return rc;
+}
+
+static u8 cpe_tgt_wcd9335_waiti_data[] = {0x00, 0x70, 0x00, 0x00};
+
+static struct cpe_tgt_waiti_info cpe_tgt_wcd9335_waiti_info = {
+	.tgt_waiti_size = ARRAY_SIZE(cpe_tgt_wcd9335_waiti_data),
+	.tgt_waiti_data = cpe_tgt_wcd9335_waiti_data,
+};
+
+static enum cpe_svc_result cpe_tgt_wcd9335_init(
+		struct cpe_svc_codec_info_v1 *codec_info,
+		struct cpe_svc_tgt_abstraction *param)
+{
+	if (!codec_info)
+		return CPE_SVC_INVALID_HANDLE;
+	if (!param)
+		return CPE_SVC_INVALID_HANDLE;
+
+	if (codec_info->id == CPE_SVC_CODEC_WCD9335) {
+		param->tgt_boot = cpe_tgt_wcd9335_boot;
+		param->tgt_cpar_init_done = cpe_tgt_wcd9335_is_cpar_init_done;
+		param->tgt_is_active = cpe_tgt_wcd9335_is_active;
+		param->tgt_reset = cpe_tgt_wcd9335_reset;
+		param->tgt_read_mailbox = cpe_tgt_wcd9335_read_mailbox;
+		param->tgt_write_mailbox = cpe_tgt_wcd9335_write_mailbox;
+		param->tgt_read_ram = cpe_tgt_wcd9335_read_RAM;
+		param->tgt_write_ram = cpe_tgt_wcd9335_write_RAM;
+		param->tgt_route_notification =
+			cpe_tgt_wcd9335_route_notification;
+		param->tgt_set_debug_mode = cpe_tgt_wcd9335_set_debug_mode;
+		param->tgt_get_cpe_info = cpe_tgt_wcd9335_get_cpe_info;
+		param->tgt_deinit = cpe_tgt_wcd9335_deinit;
+		param->tgt_voice_tx_lab = cpe_tgt_wcd9335_voicetx;
+		param->tgt_waiti_info = &cpe_tgt_wcd9335_waiti_info;
+
+		param->inbox = kzalloc(WCD9335_CPE_SS_SPE_INBOX_SIZE,
+				       GFP_KERNEL);
+		if (!param->inbox)
+			return CPE_SVC_NO_MEMORY;
+
+		param->outbox = kzalloc(WCD9335_CPE_SS_SPE_OUTBOX_SIZE,
+					GFP_KERNEL);
+		if (!param->outbox) {
+			kfree(param->inbox);
+			return CPE_SVC_NO_MEMORY;
+		}
+	}
+
+	return CPE_SVC_SUCCESS;
+}
+
+MODULE_DESCRIPTION("WCD CPE Services");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wcd_cpe_services.h b/sound/soc/codecs/wcd_cpe_services.h
new file mode 100644
index 0000000..68eb619
--- /dev/null
+++ b/sound/soc/codecs/wcd_cpe_services.h
@@ -0,0 +1,179 @@
+/* Copyright (c) 2014-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __CPE_SERVICES__
+#define __CPE_SERVICES__
+
+#define CPE_IRQ_OUTBOX_IRQ		0x01
+#define CPE_IRQ_MEM_ACCESS_ERROR	0x02
+#define CPE_IRQ_WDOG_BITE		0x04
+#define CPE_IRQ_BUFFER_OVERFLOW		0x08
+#define CPE_IRQ_LAB_OVFUNF		0x10
+#define CPE_IRQ_FLL_LOCK_LOST		0x20
+#define CPE_IRQ_RCO_WDOG_INT		0x40
+
+#define EFAILED (MAX_ERRNO - 1)
+#define ENOTREADY (MAX_ERRNO - 2)
+
+#define MAX_SUPPORTED_CLKFREQ 8
+#define CPE_SVC_INIT_PARAM_V1 1
+
+enum cpe_svc_result {
+	CPE_SVC_SUCCESS			= 0,
+	CPE_SVC_FAILED			= -EFAILED,
+	CPE_SVC_NO_MEMORY		= -ENOMEM,
+	CPE_SVC_INVALID_HANDLE		= -EINVAL,
+	CPE_SVC_NOT_READY		= -ENOTREADY,
+	CPE_SVC_SHUTTING_DOWN		= -ESHUTDOWN,
+	CPE_SVC_BUSY			= -EBUSY,
+};
+
+enum cpe_svc_event {
+	CPE_SVC_CMI_MSG			= 0x01,
+	CPE_SVC_OFFLINE			= 0x02,
+	CPE_SVC_ONLINE			= 0x04,
+	CPE_SVC_BOOT_FAILED		= 0x08,
+	CPE_SVC_READ_COMPLETE		= 0x10,
+	CPE_SVC_READ_ERROR		= 0x20,
+	CPE_SVC_BOOT			= 0x40,
+	CPE_SVC_CMI_CLIENTS_DEREG	= 0x100,
+	CPE_SVC_EVENT_ANCHOR		= 0x7FFF
+};
+
+enum cpe_svc_module {
+	CPE_SVC_LISTEN_PROC		= 1,
+	CPE_SVC_MODULE_ANCHOR		= 0x7F
+};
+
+enum cpe_svc_route_dest {
+	CPE_SVC_EXTERNAL		= 1,
+	CPE_SVC_INTERNAL		= 2,
+	CPE_SVC_ROUTE_ANCHOR		= 0x7F
+};
+
+enum cpe_svc_mem_type {
+	CPE_SVC_DATA_MEM		= 1,
+	CPE_SVC_INSTRUCTION_MEM		= 2,
+	CPE_SVC_IPC_MEM			= 3,
+	CPE_SVC_MEM_TYPE_ANCHOR		= 0x7F
+};
+
+enum cpe_svc_codec_id {
+	CPE_SVC_CODEC_TOMTOM		= 5,
+	CPE_SVC_CODEC_WCD9335		= 7,
+	CPE_SVC_CODEC_WCD9326		= 8,
+	CPE_SVC_CODEC_ID_ANCHOR		= 0x7ffffff
+};
+
+enum cpe_svc_codec_version {
+	CPE_SVC_CODEC_V1P0		= 1,
+	CPE_SVC_CODEC_VERSION_ANCHOR	= 0x7fffffff
+};
+
+struct cpe_svc_codec_info_v1 {
+	u16			major_version;/*must be 1*/
+	u16			minor_version;/*must be 0*/
+	u32			id;
+	u32			version;
+	/*Add 1.1 version fields after this line*/
+};
+
+struct cpe_svc_notification {
+	enum cpe_svc_event event;
+	enum cpe_svc_result result;
+	void *payload;
+	void *private_data;
+};
+
+struct cpe_svc_msg_payload {
+	u8    *cmi_msg;
+};
+
+struct cpe_svc_read_complete {
+	u8    *buffer;
+	size_t   size;
+};
+
+struct cpe_svc_boot_event {
+	u32 debug_address;
+	size_t debug_buffer_size;
+	u32 status;
+};
+
+struct cpe_svc_mem_segment {
+	enum cpe_svc_mem_type type;
+	u32 cpe_addr;
+	size_t size;
+	u8 *data;
+};
+
+struct cpe_svc_hw_cfg {
+	size_t DRAM_size;
+	u32 DRAM_offset;
+	size_t IRAM_size;
+	u32 IRAM_offset;
+	u8 inbox_size;
+	u8 outbox_size;
+};
+
+struct cpe_svc_cfg_clk_plan {
+	u32 current_clk_feq;
+	u32 num_clk_freqs;
+	u32 clk_freqs[MAX_SUPPORTED_CLKFREQ];
+};
+
+struct cpe_svc_init_param {
+	void *context;
+	u32 version;
+	void (*query_freq_plans_cb)(void *cdc_priv,
+			struct cpe_svc_cfg_clk_plan *clk_freq);
+	void (*change_freq_plan_cb)(void *cdc_priv,
+			u32 clk_freq);
+};
+
+
+void *cpe_svc_initialize(
+		void irq_control_callback(u32 enable),
+		const void *codec_info, void *context);
+enum cpe_svc_result cpe_svc_deinitialize(void *cpe_handle);
+
+void *cpe_svc_register(void *cpe_handle,
+		void (*notification_callback)(
+			const struct cpe_svc_notification *parameter),
+		u32 mask, const char *name);
+
+enum cpe_svc_result cpe_svc_deregister(void *cpe_handle, void *reg_handle);
+
+enum cpe_svc_result cpe_svc_download_segment(void *cpe_handle,
+		const struct cpe_svc_mem_segment *segment);
+
+enum cpe_svc_result cpe_svc_boot(void *cpe_handle, int debug_mode);
+
+enum cpe_svc_result cpe_svc_shutdown(void *cpe_handle);
+
+enum cpe_svc_result cpe_svc_reset(void *cpe_handle);
+
+enum cpe_svc_result cpe_svc_process_irq(void *cpe_handle, u32 cpe_irq);
+
+enum cpe_svc_result
+cpe_svc_route_notification(void *cpe_handle, enum cpe_svc_module module,
+		enum cpe_svc_route_dest dest);
+
+enum cpe_svc_result cpe_svc_ramdump(void *cpe_handle,
+		struct cpe_svc_mem_segment *buffer);
+
+enum cpe_svc_result cpe_svc_set_debug_mode(void *cpe_handle, u32 mode);
+
+const struct cpe_svc_hw_cfg *cpe_svc_get_hw_cfg(void *cpe_handle);
+enum cpe_svc_result cpe_svc_toggle_lab(void *cpe_handle, bool enable);
+enum cpe_svc_result cpe_svc_ftm_test(void *cpe_handle, u32 *status);
+#endif /*__CPE_SERVICES__*/
diff --git a/sound/soc/codecs/wcdcal-hwdep.c b/sound/soc/codecs/wcdcal-hwdep.c
new file mode 100644
index 0000000..31eae69
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/ioctl.h>
+#include <linux/bitops.h>
+#include <sound/hwdep.h>
+#include <sound/msmcal-hwdep.h>
+#include <sound/soc.h>
+#include "wcdcal-hwdep.h"
+
+const int cal_size_info[WCD9XXX_MAX_CAL] = {
+	[WCD9XXX_ANC_CAL] = 16384,
+	[WCD9XXX_MBHC_CAL] = 4096,
+	[WCD9XXX_MAD_CAL] = 4096,
+	[WCD9XXX_VBAT_CAL] = 72,
+};
+
+const char *cal_name_info[WCD9XXX_MAX_CAL] = {
+	[WCD9XXX_ANC_CAL] = "anc",
+	[WCD9XXX_MBHC_CAL] = "mbhc",
+	[WCD9XXX_MAD_CAL] = "mad",
+	[WCD9XXX_VBAT_CAL] = "vbat",
+};
+
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+					enum wcd_cal_type type)
+{
+	if (!fw_data) {
+		pr_err("%s: fw_data is NULL\n", __func__);
+		return NULL;
+	}
+	if (type >= WCD9XXX_MAX_CAL ||
+		type < WCD9XXX_MIN_CAL) {
+		pr_err("%s: wrong cal type sent %d\n", __func__, type);
+		return NULL;
+	}
+	mutex_lock(&fw_data->lock);
+	if (!test_bit(WCDCAL_RECIEVED,
+		&fw_data->wcdcal_state[type])) {
+		pr_err("%s: cal not sent by userspace %d\n",
+			__func__, type);
+		mutex_unlock(&fw_data->lock);
+		return NULL;
+	}
+	mutex_unlock(&fw_data->lock);
+	return fw_data->fw[type];
+}
+EXPORT_SYMBOL(wcdcal_get_fw_cal);
+
+static int wcdcal_hwdep_ioctl_shared(struct snd_hwdep *hw,
+			struct wcdcal_ioctl_buffer fw_user)
+{
+	struct fw_info *fw_data = hw->private_data;
+	struct firmware_cal **fw = fw_data->fw;
+	void *data;
+
+	if (!test_bit(fw_user.cal_type, fw_data->cal_bit)) {
+		pr_err("%s: codec didn't set this %d!!\n",
+				__func__, fw_user.cal_type);
+		return -EFAULT;
+	}
+	if (fw_user.cal_type >= WCD9XXX_MAX_CAL ||
+		fw_user.cal_type < WCD9XXX_MIN_CAL) {
+		pr_err("%s: wrong cal type sent %d\n",
+				__func__, fw_user.cal_type);
+		return -EFAULT;
+	}
+	if (fw_user.size > cal_size_info[fw_user.cal_type] ||
+		fw_user.size <= 0) {
+		pr_err("%s: incorrect firmware size %d for %s\n",
+			__func__, fw_user.size,
+			cal_name_info[fw_user.cal_type]);
+		return -EFAULT;
+	}
+	data = fw[fw_user.cal_type]->data;
+	if (copy_from_user(data, fw_user.buffer, fw_user.size))
+		return -EFAULT;
+	fw[fw_user.cal_type]->size = fw_user.size;
+	mutex_lock(&fw_data->lock);
+	set_bit(WCDCAL_RECIEVED, &fw_data->wcdcal_state[fw_user.cal_type]);
+	mutex_unlock(&fw_data->lock);
+	return 0;
+}
+
+#ifdef CONFIG_COMPAT
+struct wcdcal_ioctl_buffer32 {
+	u32 size;
+	compat_uptr_t buffer;
+	enum wcd_cal_type cal_type;
+};
+
+enum {
+	SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32 =
+		_IOW('U', 0x1, struct wcdcal_ioctl_buffer32),
+};
+
+static int wcdcal_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+	struct wcdcal_ioctl_buffer32 fw_user32;
+	struct wcdcal_ioctl_buffer fw_user_compat;
+
+	if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE32) {
+		pr_err("%s: wrong ioctl command sent %u!\n", __func__, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&fw_user32, argp, sizeof(fw_user32))) {
+		pr_err("%s: failed to copy\n", __func__);
+		return -EFAULT;
+	}
+	fw_user_compat.size = fw_user32.size;
+	fw_user_compat.buffer = compat_ptr(fw_user32.buffer);
+	fw_user_compat.cal_type = fw_user32.cal_type;
+	return wcdcal_hwdep_ioctl_shared(hw, fw_user_compat);
+}
+#else
+#define wcdcal_hwdep_ioctl_compat NULL
+#endif
+
+static int wcdcal_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	struct wcdcal_ioctl_buffer __user *argp = (void __user *)arg;
+	struct wcdcal_ioctl_buffer fw_user;
+
+	if (cmd != SNDRV_CTL_IOCTL_HWDEP_CAL_TYPE) {
+		pr_err("%s: wrong ioctl command sent %d!\n", __func__, cmd);
+		return -ENOIOCTLCMD;
+	}
+	if (copy_from_user(&fw_user, argp, sizeof(fw_user))) {
+		pr_err("%s: failed to copy\n", __func__);
+		return -EFAULT;
+	}
+	return wcdcal_hwdep_ioctl_shared(hw, fw_user);
+}
+
+static int wcdcal_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+	struct fw_info *fw_data = hw->private_data;
+
+	mutex_lock(&fw_data->lock);
+	/* clear all the calibrations */
+	memset(fw_data->wcdcal_state, 0,
+		sizeof(fw_data->wcdcal_state));
+	mutex_unlock(&fw_data->lock);
+	return 0;
+}
+
+int wcd_cal_create_hwdep(void *data, int node, struct snd_soc_codec *codec)
+{
+	char hwname[40];
+	struct snd_hwdep *hwdep;
+	struct firmware_cal **fw;
+	struct fw_info *fw_data = data;
+	int err, cal_bit;
+
+	if (!fw_data || !codec) {
+		pr_err("%s: wrong arguments passed\n", __func__);
+		return -EINVAL;
+	}
+
+	fw = fw_data->fw;
+	snprintf(hwname, strlen("Codec %s"), "Codec %s",
+		 codec->component.name);
+	err = snd_hwdep_new(codec->component.card->snd_card,
+			    hwname, node, &hwdep);
+	if (err < 0) {
+		dev_err(codec->dev, "%s: new hwdep failed %d\n",
+				__func__, err);
+		return err;
+	}
+	snprintf(hwdep->name, strlen("Codec %s"), "Codec %s",
+		 codec->component.name);
+	hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_CODEC;
+	hwdep->private_data = fw_data;
+	hwdep->ops.ioctl_compat = wcdcal_hwdep_ioctl_compat;
+	hwdep->ops.ioctl = wcdcal_hwdep_ioctl;
+	hwdep->ops.release = wcdcal_hwdep_release;
+	mutex_init(&fw_data->lock);
+
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		set_bit(WCDCAL_UNINITIALISED,
+				&fw_data->wcdcal_state[cal_bit]);
+		fw[cal_bit] = kzalloc(sizeof *(fw[cal_bit]), GFP_KERNEL);
+		if (!fw[cal_bit]) {
+			dev_err(codec->dev, "%s: no memory for %s cal\n",
+				__func__, cal_name_info[cal_bit]);
+			goto end;
+		}
+	}
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		fw[cal_bit]->data = kzalloc(cal_size_info[cal_bit],
+						GFP_KERNEL);
+		if (!fw[cal_bit]->data)
+			goto exit;
+		set_bit(WCDCAL_INITIALISED,
+			&fw_data->wcdcal_state[cal_bit]);
+	}
+	return 0;
+exit:
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		kfree(fw[cal_bit]->data);
+		fw[cal_bit]->data = NULL;
+	}
+end:
+	for_each_set_bit(cal_bit, fw_data->cal_bit, WCD9XXX_MAX_CAL) {
+		kfree(fw[cal_bit]);
+		fw[cal_bit] = NULL;
+	}
+	return -ENOMEM;
+}
+EXPORT_SYMBOL(wcd_cal_create_hwdep);
diff --git a/sound/soc/codecs/wcdcal-hwdep.h b/sound/soc/codecs/wcdcal-hwdep.h
new file mode 100644
index 0000000..632e2f1
--- /dev/null
+++ b/sound/soc/codecs/wcdcal-hwdep.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WCD9XXX_HWDEP_H__
+#define __WCD9XXX_HWDEP_H__
+#include <sound/msmcal-hwdep.h>
+
+enum wcd_cal_states {
+	WCDCAL_UNINITIALISED,
+	WCDCAL_INITIALISED,
+	WCDCAL_RECIEVED
+};
+
+struct fw_info {
+	struct firmware_cal *fw[WCD9XXX_MAX_CAL];
+	DECLARE_BITMAP(cal_bit, WCD9XXX_MAX_CAL);
+	/* for calibration tracking */
+	unsigned long wcdcal_state[WCD9XXX_MAX_CAL];
+	struct mutex lock;
+};
+
+struct firmware_cal {
+	u8 *data;
+	size_t size;
+};
+
+struct snd_soc_codec;
+int wcd_cal_create_hwdep(void *fw, int node, struct snd_soc_codec *codec);
+struct firmware_cal *wcdcal_get_fw_cal(struct fw_info *fw_data,
+					enum wcd_cal_type type);
+#endif /* __WCD9XXX_HWDEP_H__ */
diff --git a/sound/soc/codecs/wsa881x-analog.c b/sound/soc/codecs/wsa881x-analog.c
new file mode 100644
index 0000000..4de9624
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-analog.c
@@ -0,0 +1,1446 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/q6afe-v2.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/regmap.h>
+#include "wsa881x-analog.h"
+#include "wsa881x-temp-sensor.h"
+#include "../msm/msm-audio-pinctrl.h"
+
+#define SPK_GAIN_12DB 4
+#define WIDGET_NAME_MAX_SIZE 80
+
+/*
+ * Private data Structure for wsa881x. All parameters related to
+ * WSA881X codec needs to be defined here.
+ */
+struct wsa881x_pdata {
+	struct regmap *regmap[2];
+	struct i2c_client *client[2];
+	struct snd_soc_codec *codec;
+
+	/* track wsa881x status during probe */
+	int status;
+	bool boost_enable;
+	bool visense_enable;
+	int spk_pa_gain;
+	struct i2c_msg xfer_msg[2];
+	struct mutex xfer_lock;
+	bool regmap_flag;
+	bool wsa_active;
+	int index;
+	int (*enable_mclk)(struct snd_soc_card *, bool);
+	struct wsa881x_tz_priv tz_pdata;
+	int bg_cnt;
+	int clk_cnt;
+	int enable_cnt;
+	int version;
+	struct mutex bg_lock;
+	struct mutex res_lock;
+	struct delayed_work ocp_ctl_work;
+};
+
+enum {
+	WSA881X_STATUS_PROBING,
+	WSA881X_STATUS_I2C,
+};
+
+#define WSA881X_OCP_CTL_TIMER_SEC 2
+#define WSA881X_OCP_CTL_TEMP_CELSIUS 25
+#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60
+
+static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC;
+module_param(wsa881x_ocp_poll_timer_sec, int, 0664);
+MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling");
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+						bool enable);
+
+const char *wsa_tz_names[] = {"wsa881x.0e", "wsa881x.0f"};
+
+struct wsa881x_pdata wsa_pdata[MAX_WSA881X_DEVICE];
+
+static bool pinctrl_init;
+
+static int wsa881x_populate_dt_pdata(struct device *dev);
+static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable);
+static int wsa881x_startup(struct wsa881x_pdata *pdata);
+static int wsa881x_shutdown(struct wsa881x_pdata *pdata);
+
+static int delay_array_msec[] = {10, 20, 30, 40, 50};
+
+static int wsa881x_i2c_addr = -1;
+static int wsa881x_probing_count;
+static int wsa881x_presence_count;
+
+static const char * const wsa881x_spk_pa_gain_text[] = {
+"POS_13P5_DB", "POS_12_DB", "POS_10P5_DB", "POS_9_DB", "POS_7P5_DB",
+"POS_6_DB", "POS_4P5_DB", "POS_3_DB", "POS_1P5_DB", "POS_0_DB"};
+
+static const struct soc_enum wsa881x_spk_pa_gain_enum[] = {
+		SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa881x_spk_pa_gain_text),
+				    wsa881x_spk_pa_gain_text),
+};
+
+static int wsa881x_spk_pa_gain_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->spk_pa_gain;
+
+	dev_dbg(codec->dev, "%s: spk_pa_gain = %ld\n", __func__,
+				ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int wsa881x_spk_pa_gain_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	if (ucontrol->value.integer.value[0] < 0 ||
+		ucontrol->value.integer.value[0] > 0xC) {
+		dev_err(codec->dev, "%s: Unsupported gain val %ld\n",
+			 __func__, ucontrol->value.integer.value[0]);
+		return -EINVAL;
+	}
+	wsa881x->spk_pa_gain = ucontrol->value.integer.value[0];
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0] = %ld\n",
+			 __func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int get_i2c_wsa881x_device_index(u16 reg)
+{
+	u16 mask = 0x0f00;
+	int value = 0;
+
+	value = ((reg & mask) >> 8) & 0x000f;
+
+	switch (value) {
+	case 0:
+		return 0;
+	case 1:
+		return 1;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static int wsa881x_i2c_write_device(struct wsa881x_pdata *wsa881x,
+			unsigned int reg, unsigned int val)
+{
+	int i = 0, rc = 0;
+	int wsa881x_index;
+	struct i2c_msg *msg;
+	int ret = 0;
+	int bytes = 1;
+	u8 reg_addr = 0;
+	u8 data[bytes + 1];
+
+	wsa881x_index = get_i2c_wsa881x_device_index(reg);
+	if (wsa881x_index < 0) {
+		pr_err("%s:invalid register to write\n", __func__);
+		return -EINVAL;
+	}
+	if (wsa881x->regmap_flag) {
+		rc = regmap_write(wsa881x->regmap[wsa881x_index], reg, val);
+		for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) {
+			pr_err("Failed writing reg=%u - retry(%d)\n", reg, i);
+			/* retry after delay of increasing order */
+			msleep(delay_array_msec[i]);
+			rc = regmap_write(wsa881x->regmap[wsa881x_index],
+								reg, val);
+		}
+		if (rc)
+			pr_err("Failed writing reg=%u rc=%d\n", reg, rc);
+		else
+			pr_err("write success register = %x val = %x\n",
+							reg, val);
+	} else {
+		reg_addr = (u8)reg;
+		msg = &wsa881x->xfer_msg[0];
+		msg->addr = wsa881x->client[wsa881x_index]->addr;
+		msg->len = bytes + 1;
+		msg->flags = 0;
+		data[0] = reg;
+		data[1] = (u8)val;
+		msg->buf = data;
+		ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter,
+						wsa881x->xfer_msg, 1);
+		/* Try again if the write fails */
+		if (ret != 1) {
+			ret = i2c_transfer(
+					wsa881x->client[wsa881x_index]->adapter,
+							wsa881x->xfer_msg, 1);
+			if (ret != 1) {
+				pr_err("failed to write the device\n");
+				return ret;
+			}
+		}
+		pr_debug("write success reg = %x val = %x\n", reg, data[1]);
+	}
+	return rc;
+}
+
+static int wsa881x_i2c_read_device(struct wsa881x_pdata *wsa881x,
+				unsigned int reg)
+{
+	int wsa881x_index;
+	int i = 0, rc = 0;
+	unsigned int val;
+	struct i2c_msg *msg;
+	int ret = 0;
+	u8 reg_addr = 0;
+	u8 dest[5];
+
+	wsa881x_index = get_i2c_wsa881x_device_index(reg);
+	if (wsa881x_index < 0) {
+		pr_err("%s:invalid register to read\n", __func__);
+		return -EINVAL;
+	}
+	if (wsa881x->regmap_flag) {
+		rc = regmap_read(wsa881x->regmap[wsa881x_index], reg, &val);
+		for (i = 0; rc && i < ARRAY_SIZE(delay_array_msec); i++) {
+			pr_err("Failed reading reg=%u - retry(%d)\n", reg, i);
+			/* retry after delay of increasing order */
+			msleep(delay_array_msec[i]);
+			rc = regmap_read(wsa881x->regmap[wsa881x_index],
+						reg, &val);
+		}
+		if (rc) {
+			pr_err("Failed reading reg=%u rc=%d\n", reg, rc);
+			return rc;
+		}
+		pr_debug("read success reg = %x val = %x\n",
+						reg, val);
+	} else {
+		reg_addr = (u8)reg;
+		msg = &wsa881x->xfer_msg[0];
+		msg->addr = wsa881x->client[wsa881x_index]->addr;
+		msg->len = 1;
+		msg->flags = 0;
+		msg->buf = &reg_addr;
+
+		msg = &wsa881x->xfer_msg[1];
+		msg->addr = wsa881x->client[wsa881x_index]->addr;
+		msg->len = 1;
+		msg->flags = I2C_M_RD;
+		msg->buf = dest;
+		ret = i2c_transfer(wsa881x->client[wsa881x_index]->adapter,
+					wsa881x->xfer_msg, 2);
+
+		/* Try again if read fails first time */
+		if (ret != 2) {
+			ret = i2c_transfer(
+				wsa881x->client[wsa881x_index]->adapter,
+						wsa881x->xfer_msg, 2);
+			if (ret != 2) {
+				pr_err("failed to read wsa register:%d\n",
+								reg);
+				return ret;
+			}
+		}
+		val = dest[0];
+	}
+	return val;
+}
+
+static unsigned int wsa881x_i2c_read(struct snd_soc_codec *codec,
+				unsigned int reg)
+{
+	struct wsa881x_pdata *wsa881x;
+	unsigned int val;
+	int ret;
+
+	if (codec == NULL) {
+		pr_err("%s: invalid codec\n", __func__);
+		return -EINVAL;
+	}
+	wsa881x = snd_soc_codec_get_drvdata(codec);
+	if (!wsa881x->wsa_active) {
+		ret = snd_soc_cache_read(codec, reg, &val);
+		if (ret >= 0)
+			return val;
+		dev_err(codec->dev,
+			"cache read failed for reg: 0x%x ret: %d\n",
+						 reg, ret);
+		return ret;
+	}
+	return wsa881x_i2c_read_device(wsa881x, reg);
+}
+
+static int wsa881x_i2c_write(struct snd_soc_codec *codec, unsigned int reg,
+			unsigned int val)
+{
+	struct wsa881x_pdata *wsa881x;
+	int ret = 0;
+
+	if (codec == NULL) {
+		pr_err("%s: invalid codec\n", __func__);
+		return -EINVAL;
+	}
+	wsa881x = snd_soc_codec_get_drvdata(codec);
+	if (!wsa881x->wsa_active) {
+		ret = snd_soc_cache_write(codec, reg, val);
+		if (ret != 0)
+			dev_err(codec->dev, "cache write to %x failed: %d\n",
+						reg, ret);
+		return ret;
+	}
+	return wsa881x_i2c_write_device(wsa881x, reg, val);
+}
+
+static int wsa881x_i2c_get_client_index(struct i2c_client *client,
+					int *wsa881x_index)
+{
+	int ret = 0;
+
+	switch (client->addr) {
+	case WSA881X_I2C_SPK0_SLAVE0_ADDR:
+	case WSA881X_I2C_SPK0_SLAVE1_ADDR:
+		*wsa881x_index = WSA881X_I2C_SPK0_SLAVE0;
+	break;
+	case WSA881X_I2C_SPK1_SLAVE0_ADDR:
+	case WSA881X_I2C_SPK1_SLAVE1_ADDR:
+		*wsa881x_index = WSA881X_I2C_SPK1_SLAVE0;
+	break;
+	default:
+		ret = -EINVAL;
+	break;
+	}
+	return ret;
+}
+
+static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: enable:%d\n", __func__, enable);
+	if (enable) {
+		if (!WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_ANA_CTL,
+								0x01, 0x01);
+			snd_soc_update_bits(codec, WSA881X_ANA_CTL,
+								0x04, 0x04);
+			snd_soc_update_bits(codec, WSA881X_BOOST_PS_CTL,
+								0x40, 0x00);
+			snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1,
+								0xF0, 0xB0);
+			snd_soc_update_bits(codec, WSA881X_BOOST_ZX_CTL,
+								0x20, 0x00);
+			snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL,
+								0x80, 0x80);
+		} else {
+			snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY,
+							0x03, 0x03);
+			snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL,
+							0xFF, 0x14);
+			snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL,
+							0x80, 0x80);
+			snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL,
+							0x03, 0x00);
+			snd_soc_update_bits(codec,
+					WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+					0x0C, 0x04);
+			snd_soc_update_bits(codec,
+					WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+					0x03, 0x00);
+			snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1,
+							0xF0, 0x70);
+			snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x03, 0x01);
+			snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN,
+								0x08, 0x08);
+			snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x04, 0x04);
+			snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT,
+								0x0F, 0x08);
+			snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL,
+								0x80, 0x80);
+		}
+		/* For WSA8810, start-up time is 1500us as per qcrg sequence */
+		usleep_range(1500, 1510);
+	} else {
+		/* ENSURE: Class-D amp is shutdown. CLK is still on */
+		snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00);
+		/* boost settle time is 1500us as per qcrg sequence */
+		usleep_range(1500, 1510);
+	}
+	return 0;
+}
+
+static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable,
+				     u8 isense1_gain, u8 isense2_gain,
+				     u8 vsense_gain)
+{
+	u8 value = 0;
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: enable:%d\n", __func__, enable);
+
+	if (enable) {
+		if (WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_OTP_REG_28,
+						0x3F, 0x3A);
+			snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1,
+						0xFF, 0xB2);
+			snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2,
+						0xFF, 0x05);
+		}
+		snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM,
+					0x08, 0x00);
+		if (WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2,
+							0x1C, 0x04);
+		} else {
+			snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2,
+							0x08, 0x08);
+			snd_soc_update_bits(codec, WSA881X_SPKR_PROT_ATEST2,
+							0x02, 0x02);
+		}
+		value = ((isense2_gain << 6) | (isense1_gain << 4) |
+			(vsense_gain << 3));
+		snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+					0xF8, value);
+		snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+					0x01, 0x01);
+	} else {
+		if (WSA881X_IS_2_0(wsa881x->version))
+			snd_soc_update_bits(codec,
+				WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x10, 0x10);
+		else
+			snd_soc_update_bits(codec,
+				WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x08, 0x08);
+		/*
+		 * 200us sleep is needed after visense txfe disable as per
+		 * HW requirement.
+		 */
+		usleep_range(200, 210);
+
+		snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+					0x01, 0x00);
+	}
+	return 0;
+}
+
+static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: enable:%d\n", __func__, enable);
+	if (enable) {
+		if (!WSA881X_IS_2_0(wsa881x->version))
+			snd_soc_update_bits(codec, WSA881X_ADC_SEL_IBIAS,
+							0x70, 0x40);
+		snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBIAS,
+							0x07, 0x04);
+		snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x80);
+		snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x80);
+	} else {
+		/* Ensure: Speaker Protection has been stopped */
+		snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, 0x80, 0x00);
+		snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, 0x80, 0x00);
+	}
+
+	return 0;
+}
+
+static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__,
+		enable, wsa881x->bg_cnt);
+	mutex_lock(&wsa881x->bg_lock);
+	if (enable) {
+		++wsa881x->bg_cnt;
+		if (wsa881x->bg_cnt == 1) {
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP,
+					    0x08, 0x08);
+			/* 400usec sleep is needed as per HW requirement */
+			usleep_range(400, 410);
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x04);
+		}
+	} else {
+		--wsa881x->bg_cnt;
+		if (wsa881x->bg_cnt <= 0) {
+			WARN_ON(wsa881x->bg_cnt < 0);
+			wsa881x->bg_cnt = 0;
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00);
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00);
+		}
+	}
+	mutex_unlock(&wsa881x->bg_lock);
+}
+
+static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s:ss enable:%d, clk_count:%d\n", __func__,
+		enable, wsa881x->clk_cnt);
+	mutex_lock(&wsa881x->res_lock);
+	if (enable) {
+		++wsa881x->clk_cnt;
+		if (wsa881x->clk_cnt == 1) {
+			snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x02);
+			snd_soc_write(codec, WSA881X_CDC_RST_CTL, 0x03);
+			snd_soc_write(codec, WSA881X_CLOCK_CONFIG, 0x01);
+			snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01);
+			snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01);
+		}
+	} else {
+		--wsa881x->clk_cnt;
+		if (wsa881x->clk_cnt <= 0) {
+			WARN_ON(wsa881x->clk_cnt < 0);
+			wsa881x->clk_cnt = 0;
+			snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00);
+			snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00);
+			if (WSA881X_IS_2_0(wsa881x->version))
+				snd_soc_update_bits(codec,
+					WSA881X_CDC_TOP_CLK_CTL, 0x01, 0x00);
+		}
+	}
+	mutex_unlock(&wsa881x->res_lock);
+}
+
+static int wsa881x_rdac_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: enable:%d\n", __func__, enable);
+	if (enable) {
+		snd_soc_update_bits(codec, WSA881X_ANA_CTL, 0x08, 0x00);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0x08, 0x08);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x20);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x20, 0x00);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x40, 0x40);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x80);
+		if (WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+								0x01, 0x01);
+			snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL,
+								0x30, 0x30);
+			snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL,
+								0x0C, 0x00);
+		}
+		snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0, 0x40);
+		snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x01, 0x01);
+	} else {
+		/* Ensure class-D amp is off */
+		snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x80, 0x00);
+	}
+	return 0;
+}
+
+static int wsa881x_spkr_pa_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	int ret = 0;
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: enable:%d\n", __func__, enable);
+	if (enable) {
+		/*
+		 * Ensure: Boost is enabled and stable, Analog input is up
+		 * and outputting silence
+		 */
+		if (!WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I,
+								0xFF, 0x01);
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V,
+								0x02, 0x02);
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V,
+								0xFF, 0x10);
+			snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG,
+								0xA0, 0xA0);
+			snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN,
+								0x80, 0x80);
+			usleep_range(700, 710);
+			snd_soc_update_bits(codec, WSA881X_SPKR_PWRSTG_DBG,
+								0x00, 0x00);
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_V,
+								0xFF, 0x00);
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V,
+								0x02, 0x00);
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_DET_TEST_I,
+								0xFF, 0x00);
+		} else
+			snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN,
+								0x80, 0x80);
+		/* add 1000us delay as per qcrg */
+		usleep_range(1000, 1010);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x01, 0x01);
+		if (WSA881X_IS_2_0(wsa881x->version))
+			snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+								0x01, 0x00);
+		usleep_range(1000, 1010);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN, 0xF0,
+						(wsa881x->spk_pa_gain << 4));
+		if (wsa881x->visense_enable) {
+			ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1,
+							"wsa_vi");
+			if (ret) {
+				pr_err("%s: gpio set cannot be activated %s\n",
+					__func__, "wsa_vi");
+				return ret;
+			}
+			wsa881x_visense_txfe_ctrl(codec, true,
+						0x00, 0x01, 0x00);
+			wsa881x_visense_adc_ctrl(codec, true);
+		}
+	} else {
+		/*
+		 * Ensure: Boost is still on, Stream from Analog input and
+		 * Speaker Protection has been stopped and input is at 0V
+		 */
+		if (WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+								0x01, 0x01);
+			usleep_range(1000, 1010);
+			snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_CAL,
+								0x01, 0x00);
+			msleep(20);
+			snd_soc_update_bits(codec, WSA881X_ANA_CTL,
+								0x03, 0x00);
+			usleep_range(200, 210);
+		}
+		snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x80, 0x00);
+	}
+	return 0;
+}
+
+static int wsa881x_get_boost(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->boost_enable;
+	return 0;
+}
+
+static int wsa881x_set_boost(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int value = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n",
+		 __func__, wsa881x->boost_enable, value);
+	wsa881x->boost_enable = value;
+	return 0;
+}
+
+static int wsa881x_get_visense(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->visense_enable;
+	return 0;
+}
+
+static int wsa881x_set_visense(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int value = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n",
+		 __func__, wsa881x->visense_enable, value);
+	wsa881x->visense_enable = value;
+	return 0;
+}
+
+static const struct snd_kcontrol_new wsa881x_snd_controls[] = {
+	SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0,
+		wsa881x_get_boost, wsa881x_set_boost),
+
+	SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0,
+		wsa881x_get_visense, wsa881x_set_visense),
+
+	SOC_ENUM_EXT("WSA_SPK PA Gain", wsa881x_spk_pa_gain_enum[0],
+		wsa881x_spk_pa_gain_get, wsa881x_spk_pa_gain_put),
+};
+
+static const char * const rdac_text[] = {
+	"ZERO", "Switch",
+};
+
+static const struct soc_enum rdac_enum =
+	SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(rdac_text), rdac_text);
+
+static const struct snd_kcontrol_new rdac_mux[] = {
+	SOC_DAPM_ENUM("RDAC", rdac_enum)
+};
+
+static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n",
+		 __func__, w->name, event,
+		wsa881x->boost_enable, wsa881x->visense_enable);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = wsa881x_startup(wsa881x);
+		if (ret) {
+			pr_err("%s: wsa startup failed ret: %d", __func__, ret);
+			return ret;
+		}
+		wsa881x_clk_ctrl(codec, true);
+		snd_soc_update_bits(codec, WSA881X_SPKR_DAC_CTL, 0x02, 0x02);
+		if (!WSA881X_IS_2_0(wsa881x->version))
+			snd_soc_update_bits(codec, WSA881X_BIAS_REF_CTRL,
+								0x0F, 0x08);
+		wsa881x_bandgap_ctrl(codec, true);
+		if (!WSA881X_IS_2_0(wsa881x->version))
+			snd_soc_update_bits(codec, WSA881X_SPKR_BBM_CTL,
+								0x02, 0x02);
+		snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80);
+		snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06);
+		if (!WSA881X_IS_2_0(wsa881x->version)) {
+			snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL2,
+								0x04, 0x04);
+			snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT,
+								0x09, 0x09);
+		}
+		snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x20);
+		if (WSA881X_IS_2_0(wsa881x->version))
+			snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT,
+								0x0E, 0x0E);
+		if (wsa881x->boost_enable)
+			wsa881x_boost_ctrl(codec, true);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		wsa881x_rdac_ctrl(codec, true);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		wsa881x_rdac_ctrl(codec, false);
+		if (wsa881x->visense_enable) {
+			wsa881x_visense_adc_ctrl(codec, false);
+			wsa881x_visense_txfe_ctrl(codec, false,
+						0x00, 0x01, 0x00);
+			ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1,
+							"wsa_vi");
+			if (ret) {
+				pr_err("%s: gpio set cannot be suspended %s\n",
+					__func__, "wsa_vi");
+				return ret;
+			}
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (wsa881x->boost_enable)
+			wsa881x_boost_ctrl(codec, false);
+		wsa881x_clk_ctrl(codec, false);
+		wsa881x_bandgap_ctrl(codec, false);
+		ret = wsa881x_shutdown(wsa881x);
+		if (ret < 0) {
+			pr_err("%s: wsa shutdown failed ret: %d",
+					__func__, ret);
+			return ret;
+		}
+		break;
+	default:
+		pr_err("%s: invalid event:%d\n", __func__, event);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static void wsa881x_ocp_ctl_work(struct work_struct *work)
+{
+	struct wsa881x_pdata *wsa881x;
+	struct delayed_work *dwork;
+	struct snd_soc_codec *codec;
+	unsigned long temp_val;
+
+	dwork = to_delayed_work(work);
+	wsa881x = container_of(dwork, struct wsa881x_pdata, ocp_ctl_work);
+
+	if (!wsa881x)
+		return;
+
+	codec = wsa881x->codec;
+	wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val);
+	dev_dbg(codec->dev, " temp = %ld\n", temp_val);
+
+	if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS)
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00);
+	else
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+
+		schedule_delayed_work(&wsa881x->ocp_ctl_work,
+			msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000));
+}
+
+static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = w->codec;
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	pr_debug("%s: %s %d\n", __func__, w->name, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		wsa881x_spkr_pa_ctrl(codec, true);
+		schedule_delayed_work(&wsa881x->ocp_ctl_work,
+			msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000));
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		wsa881x_spkr_pa_ctrl(codec, false);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+		break;
+	default:
+		pr_err("%s: invalid event:%d\n", __func__, event);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+
+static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("WSA_IN"),
+
+	SND_SOC_DAPM_DAC_E("RDAC Analog", NULL, SND_SOC_NOPM, 0, 0,
+		wsa881x_rdac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+		SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_MUX("WSA_RDAC", SND_SOC_NOPM, 0, 0,
+		rdac_mux),
+
+	SND_SOC_DAPM_PGA_S("WSA_SPKR PGA", 1, SND_SOC_NOPM, 0, 0,
+			wsa881x_spkr_pa_event,
+			SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMU |	SND_SOC_DAPM_PRE_PMD |
+			SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_OUTPUT("WSA_SPKR"),
+};
+
+static const struct snd_soc_dapm_route wsa881x_audio_map[] = {
+	{"WSA_RDAC", "Switch", "WSA_IN"},
+	{"RDAC Analog", NULL, "WSA_RDAC"},
+	{"WSA_SPKR PGA", NULL, "RDAC Analog"},
+	{"WSA_SPKR", NULL, "WSA_SPKR PGA"},
+};
+
+
+static int wsa881x_startup(struct wsa881x_pdata *pdata)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = pdata->codec;
+	struct snd_soc_card *card = codec->component.card;
+
+	pr_debug("%s(): wsa startup, enable_cnt:%d\n", __func__,
+					pdata->enable_cnt);
+
+	if (pdata->enable_cnt++ > 0)
+		return 0;
+	ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_clk");
+	if (ret) {
+		pr_err("%s: gpio set cannot be activated %s\n",
+			__func__, "wsa_clk");
+		return ret;
+	}
+	if (pdata->enable_mclk) {
+		ret = pdata->enable_mclk(card, true);
+		if (ret < 0) {
+			dev_err_ratelimited(codec->dev,
+				"%s: mclk enable failed %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+	ret = wsa881x_reset(pdata, true);
+	return ret;
+}
+
+static int wsa881x_shutdown(struct wsa881x_pdata *pdata)
+{
+	int ret = 0, reg;
+	struct snd_soc_codec *codec = pdata->codec;
+	struct snd_soc_card *card = codec->component.card;
+
+	pr_debug("%s(): wsa shutdown, enable_cnt:%d\n", __func__,
+					pdata->enable_cnt);
+	if (--pdata->enable_cnt > 0)
+		return 0;
+	ret = wsa881x_reset(pdata, false);
+	if (ret) {
+		pr_err("%s: wsa reset failed suspend %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	if (pdata->enable_mclk) {
+		ret = pdata->enable_mclk(card, false);
+		if (ret < 0) {
+			pr_err("%s: mclk disable failed %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_clk");
+	if (ret) {
+		pr_err("%s: gpio set cannot be suspended %s\n",
+			__func__, "wsa_clk");
+		return ret;
+	}
+	if (pdata->codec) {
+		/* restore defaults to cache */
+		for (reg = 0; reg < ARRAY_SIZE(wsa881x_ana_reg_defaults);
+				reg++) {
+			if (wsa881x_ana_reg_readable[reg])
+				snd_soc_cache_write(pdata->codec,
+					wsa881x_ana_reg_defaults[reg].reg,
+					wsa881x_ana_reg_defaults[reg].def);
+		}
+	}
+	return 0;
+}
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+						bool enable)
+{
+	int ret = 0;
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	if (enable) {
+		ret = wsa881x_startup(wsa881x);
+		if (ret < 0) {
+			dev_err_ratelimited(codec->dev,
+				"%s: failed to startup\n", __func__);
+			return ret;
+		}
+	}
+	wsa881x_clk_ctrl(codec, enable);
+	wsa881x_bandgap_ctrl(codec, enable);
+	if (!enable) {
+		ret = wsa881x_shutdown(wsa881x);
+		if (ret < 0)
+			dev_err_ratelimited(codec->dev,
+				"%s: failed to shutdown\n", __func__);
+	}
+	return ret;
+}
+
+static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec,
+				     struct wsa_temp_register *wsa_temp_reg)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int ret = 0;
+
+	if (!wsa881x) {
+		dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	ret = wsa881x_resource_acquire(codec, true);
+	if (ret) {
+		dev_err_ratelimited(codec->dev,
+			"%s: resource acquire fail\n", __func__);
+		return ret;
+	}
+
+	if (WSA881X_IS_2_0(wsa881x->version)) {
+		snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00);
+		wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB);
+		wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB);
+		snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01);
+	} else {
+		wsa_temp_reg->dmeas_msb = snd_soc_read(codec,
+						   WSA881X_TEMP_DOUT_MSB);
+		wsa_temp_reg->dmeas_lsb = snd_soc_read(codec,
+						   WSA881X_TEMP_DOUT_LSB);
+	}
+	wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1);
+	wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2);
+	wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3);
+	wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4);
+
+	ret = wsa881x_resource_acquire(codec, false);
+	if (ret)
+		dev_err_ratelimited(codec->dev,
+			"%s: resource release fail\n", __func__);
+
+	return ret;
+}
+
+static int wsa881x_probe(struct snd_soc_codec *codec)
+{
+	struct i2c_client *client;
+	int ret = 0;
+	int wsa881x_index = 0;
+	struct snd_soc_dapm_context *dapm = &codec->dapm;
+	char *widget_name = NULL;
+	struct snd_soc_card *card = codec->component.card;
+	struct snd_soc_codec_conf *codec_conf = card->codec_conf;
+
+	client = dev_get_drvdata(codec->dev);
+	ret = wsa881x_i2c_get_client_index(client, &wsa881x_index);
+	if (ret != 0) {
+		dev_err(&client->dev, "%s: I2C get codec I2C\n"
+			"client failed\n", __func__);
+		return ret;
+	}
+	mutex_init(&wsa_pdata[wsa881x_index].bg_lock);
+	mutex_init(&wsa_pdata[wsa881x_index].res_lock);
+	snprintf(wsa_pdata[wsa881x_index].tz_pdata.name, 100, "%s",
+			wsa_tz_names[wsa881x_index]);
+	wsa_pdata[wsa881x_index].codec = codec;
+	wsa_pdata[wsa881x_index].spk_pa_gain = SPK_GAIN_12DB;
+	wsa_pdata[wsa881x_index].codec = codec;
+	wsa_pdata[wsa881x_index].tz_pdata.codec = codec;
+	wsa_pdata[wsa881x_index].tz_pdata.wsa_temp_reg_read =
+						wsa881x_temp_reg_read;
+	snd_soc_codec_set_drvdata(codec, &wsa_pdata[wsa881x_index]);
+	wsa881x_init_thermal(&wsa_pdata[wsa881x_index].tz_pdata);
+	INIT_DELAYED_WORK(&wsa_pdata[wsa881x_index].ocp_ctl_work,
+				wsa881x_ocp_ctl_work);
+
+	if (codec_conf->name_prefix) {
+		widget_name = kcalloc(WIDGET_NAME_MAX_SIZE, sizeof(char),
+					GFP_KERNEL);
+		if (!widget_name)
+			return -ENOMEM;
+
+		snprintf(widget_name, WIDGET_NAME_MAX_SIZE,
+			"%s WSA_SPKR", codec_conf->name_prefix);
+		snd_soc_dapm_ignore_suspend(dapm, widget_name);
+		snprintf(widget_name, WIDGET_NAME_MAX_SIZE,
+			"%s WSA_IN", codec_conf->name_prefix);
+		snd_soc_dapm_ignore_suspend(dapm, widget_name);
+		kfree(widget_name);
+	} else {
+		snd_soc_dapm_ignore_suspend(dapm, "WSA_SPKR");
+		snd_soc_dapm_ignore_suspend(dapm, "WSA_IN");
+	}
+
+	snd_soc_dapm_sync(dapm);
+	return 0;
+}
+
+static int wsa881x_remove(struct snd_soc_codec *codec)
+{
+	struct wsa881x_pdata *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	if (wsa881x->tz_pdata.tz_dev)
+		wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev);
+
+	mutex_destroy(&wsa881x->bg_lock);
+	mutex_destroy(&wsa881x->res_lock);
+	return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
+	.probe	= wsa881x_probe,
+	.remove	= wsa881x_remove,
+
+	.read = wsa881x_i2c_read,
+	.write = wsa881x_i2c_write,
+
+	.reg_cache_size = WSA881X_CACHE_SIZE,
+	.reg_cache_default = wsa881x_ana_reg_defaults,
+	.reg_word_size = 1,
+
+	.component_driver = {
+		.controls = wsa881x_snd_controls,
+		.num_controls = ARRAY_SIZE(wsa881x_snd_controls),
+		.dapm_widgets = wsa881x_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
+		.dapm_routes = wsa881x_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+	},
+};
+
+static int wsa881x_reset(struct wsa881x_pdata *pdata, bool enable)
+{
+	int ret = 0;
+
+	/*
+	 * shutdown the GPIOs WSA_EN, WSA_MCLK, regulators
+	 * and restore defaults in soc cache when shutdown.
+	 * Enable regulators, GPIOs WSA_MCLK, WSA_EN when powerup.
+	 */
+	if (enable) {
+		if (pdata->wsa_active)
+			return 0;
+		ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset");
+		if (ret) {
+			pr_err("%s: gpio set cannot be activated %s\n",
+				__func__, "wsa_reset");
+			return ret;
+		}
+		ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset");
+		if (ret) {
+			pr_err("%s: gpio set cannot be suspended(powerup) %s\n",
+				__func__, "wsa_reset");
+			return ret;
+		}
+		ret = msm_gpioset_activate(CLIENT_WSA_BONGO_1, "wsa_reset");
+		if (ret) {
+			pr_err("%s: gpio set cannot be activated %s\n",
+				__func__, "wsa_reset");
+			return ret;
+		}
+		pdata->wsa_active = true;
+	} else {
+		if (!pdata->wsa_active)
+			return 0;
+		ret = msm_gpioset_suspend(CLIENT_WSA_BONGO_1, "wsa_reset");
+		if (ret) {
+			pr_err("%s: gpio set cannot be suspended %s\n",
+				__func__, "wsa_reset");
+			return ret;
+		}
+		pdata->wsa_active = false;
+	}
+	return ret;
+}
+
+int wsa881x_get_client_index(void)
+{
+	return wsa881x_i2c_addr;
+}
+EXPORT_SYMBOL(wsa881x_get_client_index);
+
+int wsa881x_get_probing_count(void)
+{
+	return wsa881x_probing_count;
+}
+EXPORT_SYMBOL(wsa881x_get_probing_count);
+
+int wsa881x_get_presence_count(void)
+{
+	return wsa881x_presence_count;
+}
+EXPORT_SYMBOL(wsa881x_get_presence_count);
+
+int wsa881x_set_mclk_callback(
+	int (*enable_mclk_callback)(struct snd_soc_card *, bool))
+{
+	int i;
+
+	for (i = 0; i < MAX_WSA881X_DEVICE; i++) {
+		if (wsa_pdata[i].status == WSA881X_STATUS_I2C)
+			wsa_pdata[i].enable_mclk = enable_mclk_callback;
+	}
+	return 0;
+}
+EXPORT_SYMBOL(wsa881x_set_mclk_callback);
+
+static int check_wsa881x_presence(struct i2c_client *client)
+{
+	int ret = 0;
+	int wsa881x_index = 0;
+
+	ret = wsa881x_i2c_get_client_index(client, &wsa881x_index);
+	if (ret != 0) {
+		dev_err(&client->dev, "%s: I2C get codec I2C\n"
+			"client failed\n", __func__);
+		return ret;
+	}
+	ret = wsa881x_i2c_read_device(&wsa_pdata[wsa881x_index],
+					WSA881X_CDC_RST_CTL);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed to read wsa881x with addr %x\n",
+				client->addr);
+		return ret;
+	}
+	ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index],
+					WSA881X_CDC_RST_CTL, 0x01);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x1\n",
+					client->addr);
+		return ret;
+	}
+	/* allow 20ms before trigger next write to verify WSA881x presence */
+	msleep(20);
+	ret = wsa881x_i2c_write_device(&wsa_pdata[wsa881x_index],
+					WSA881X_CDC_RST_CTL, 0x00);
+	if (ret < 0) {
+		dev_err(&client->dev, "failed write addr %x reg:0x5 val:0x0\n",
+					client->addr);
+		return ret;
+	}
+	return ret;
+}
+
+static int wsa881x_populate_dt_pdata(struct device *dev)
+{
+	int ret = 0;
+
+	/* reading the gpio configurations from dtsi file */
+	if (!pinctrl_init) {
+		ret = msm_gpioset_initialize(CLIENT_WSA_BONGO_1, dev);
+		if (ret < 0) {
+			dev_err(dev,
+			"%s: error reading dtsi files%d\n", __func__, ret);
+			goto err;
+		}
+		pinctrl_init = true;
+	}
+err:
+	return ret;
+}
+
+static int wsa881x_i2c_probe(struct i2c_client *client,
+			const struct i2c_device_id *id)
+{
+	int ret = 0;
+	int wsa881x_index = 0;
+	struct wsa881x_pdata *pdata = NULL;
+
+	ret = wsa881x_i2c_get_client_index(client, &wsa881x_index);
+	if (ret != 0) {
+		dev_err(&client->dev, "%s: I2C get codec I2C\n"
+			"client failed\n", __func__);
+		return ret;
+	}
+
+	pdata = &wsa_pdata[wsa881x_index];
+
+	if ((client->addr == WSA881X_I2C_SPK0_SLAVE1_ADDR ||
+		client->addr == WSA881X_I2C_SPK1_SLAVE1_ADDR) &&
+		(pdata->status == WSA881X_STATUS_PROBING))
+		return ret;
+
+	if (pdata->status == WSA881X_STATUS_I2C) {
+		dev_dbg(&client->dev, "%s:probe for other slaves\n"
+			"devices of codec I2C slave Addr = %x\n",
+			__func__, client->addr);
+
+		dev_dbg(&client->dev, "%s:wsa_idx = %d SLAVE = %d\n",
+				__func__, wsa881x_index, WSA881X_ANALOG_SLAVE);
+		pdata->regmap[WSA881X_ANALOG_SLAVE] =
+			devm_regmap_init_i2c(
+				client,
+			&wsa881x_ana_regmap_config[WSA881X_ANALOG_SLAVE]);
+		regcache_cache_bypass(pdata->regmap[WSA881X_ANALOG_SLAVE],
+					true);
+		if (IS_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE])) {
+			ret = PTR_ERR(pdata->regmap[WSA881X_ANALOG_SLAVE]);
+			dev_err(&client->dev,
+				"%s: regmap_init failed %d\n",
+					__func__, ret);
+		}
+		client->dev.platform_data = pdata;
+		i2c_set_clientdata(client, pdata);
+		pdata->client[WSA881X_ANALOG_SLAVE] = client;
+		if (pdata->version == WSA881X_2_0)
+			wsa881x_update_regmap_2_0(
+					pdata->regmap[WSA881X_ANALOG_SLAVE],
+					WSA881X_ANALOG_SLAVE);
+
+		return ret;
+	} else if (pdata->status == WSA881X_STATUS_PROBING) {
+		pdata->index = wsa881x_index;
+		if (client->dev.of_node) {
+			dev_dbg(&client->dev, "%s:Platform data\n"
+				"from device tree\n", __func__);
+			ret = wsa881x_populate_dt_pdata(&client->dev);
+			if (ret < 0) {
+				dev_err(&client->dev,
+				"%s: Fail to obtain pdata from device tree\n",
+					 __func__);
+				ret = -EINVAL;
+				goto err;
+			}
+			client->dev.platform_data = pdata;
+		} else {
+			dev_dbg(&client->dev, "%s:Platform data from\n"
+				"board file\n", __func__);
+			pdata = client->dev.platform_data;
+		}
+		if (!pdata) {
+			dev_dbg(&client->dev, "no platform data?\n");
+			ret = -EINVAL;
+			goto err;
+		}
+		i2c_set_clientdata(client, pdata);
+		dev_set_drvdata(&client->dev, client);
+
+		pdata->regmap[WSA881X_DIGITAL_SLAVE] =
+			devm_regmap_init_i2c(
+				client,
+			&wsa881x_ana_regmap_config[WSA881X_DIGITAL_SLAVE]);
+		regcache_cache_bypass(pdata->regmap[WSA881X_DIGITAL_SLAVE],
+					true);
+		if (IS_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE])) {
+			ret = PTR_ERR(pdata->regmap[WSA881X_DIGITAL_SLAVE]);
+			dev_err(&client->dev, "%s: regmap_init failed %d\n",
+				__func__, ret);
+			goto err;
+		}
+		/* bus reset sequence */
+		ret = wsa881x_reset(pdata, true);
+		if (ret < 0) {
+			dev_err(&client->dev, "%s: WSA enable Failed %d\n",
+				__func__, ret);
+			goto err;
+		}
+		pdata->client[WSA881X_DIGITAL_SLAVE] = client;
+		pdata->regmap_flag = true;
+		ret = check_wsa881x_presence(client);
+		if (ret < 0) {
+			dev_err(&client->dev,
+				"failed to ping wsa with addr:%x, ret = %d\n",
+						client->addr, ret);
+			wsa881x_probing_count++;
+			goto err1;
+		}
+		pdata->version = wsa881x_i2c_read_device(pdata,
+					WSA881X_CHIP_ID1);
+		pr_debug("%s: wsa881x version: %d\n", __func__, pdata->version);
+		if (pdata->version == WSA881X_2_0) {
+			wsa881x_update_reg_defaults_2_0();
+			wsa881x_update_regmap_2_0(
+					pdata->regmap[WSA881X_DIGITAL_SLAVE],
+					WSA881X_DIGITAL_SLAVE);
+		}
+		wsa881x_presence_count++;
+		wsa881x_probing_count++;
+		ret = snd_soc_register_codec(&client->dev,
+					&soc_codec_dev_wsa881x,
+					     NULL, 0);
+		if (ret < 0)
+			goto err1;
+		pdata->status = WSA881X_STATUS_I2C;
+	}
+err1:
+	wsa881x_reset(pdata, false);
+err:
+	return 0;
+}
+
+static int wsa881x_i2c_remove(struct i2c_client *client)
+{
+	struct wsa881x_pdata *wsa881x = i2c_get_clientdata(client);
+
+	snd_soc_unregister_codec(&client->dev);
+	i2c_set_clientdata(client, NULL);
+	kfree(wsa881x);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int wsa881x_i2c_suspend(struct device *dev)
+{
+	pr_debug("%s: system suspend\n", __func__);
+	return 0;
+}
+
+static int wsa881x_i2c_resume(struct device *dev)
+{
+	pr_debug("%s: system resume\n", __func__);
+	return 0;
+}
+
+static const struct dev_pm_ops wsa881x_i2c_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(wsa881x_i2c_suspend, wsa881x_i2c_resume)
+};
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct i2c_device_id wsa881x_i2c_id[] = {
+	{"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE0_ADDR},
+	{"wsa881x-i2c-dev", WSA881X_I2C_SPK0_SLAVE1_ADDR},
+	{"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE0_ADDR},
+	{"wsa881x-i2c-dev", WSA881X_I2C_SPK1_SLAVE1_ADDR},
+	{}
+};
+
+MODULE_DEVICE_TABLE(i2c, wsa881x_i2c_id);
+
+
+static const struct of_device_id msm_match_table[] = {
+	{.compatible = "qcom,wsa881x-i2c-codec"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_match_table);
+
+static struct i2c_driver wsa881x_codec_driver = {
+	.driver = {
+		.name = "wsa881x-i2c-codec",
+		.owner = THIS_MODULE,
+#ifdef CONFIG_PM_SLEEP
+		.pm = &wsa881x_i2c_pm_ops,
+#endif
+		.of_match_table = msm_match_table,
+	},
+	.id_table = wsa881x_i2c_id,
+	.probe = wsa881x_i2c_probe,
+	.remove = wsa881x_i2c_remove,
+};
+
+static int __init wsa881x_codec_init(void)
+{
+	int i = 0;
+
+	for (i = 0; i < MAX_WSA881X_DEVICE; i++)
+		wsa_pdata[i].status = WSA881X_STATUS_PROBING;
+	return i2c_add_driver(&wsa881x_codec_driver);
+}
+module_init(wsa881x_codec_init);
+
+static void __exit wsa881x_codec_exit(void)
+{
+	i2c_del_driver(&wsa881x_codec_driver);
+}
+
+module_exit(wsa881x_codec_exit);
+
+MODULE_DESCRIPTION("WSA881x Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa881x-analog.h b/sound/soc/codecs/wsa881x-analog.h
new file mode 100644
index 0000000..a2ef2a2
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-analog.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WSA881X_H
+#define _WSA881X_H
+
+#include <linux/regmap.h>
+#include "wsa881x-registers-analog.h"
+#include <sound/soc.h>
+
+#define WSA881X_I2C_SPK0_SLAVE0_ADDR	0x0E
+#define WSA881X_I2C_SPK0_SLAVE1_ADDR	0x44
+#define WSA881X_I2C_SPK1_SLAVE0_ADDR	0x0F
+#define WSA881X_I2C_SPK1_SLAVE1_ADDR	0x45
+
+#define WSA881X_I2C_SPK0_SLAVE0	0
+#define WSA881X_I2C_SPK1_SLAVE0	1
+#define MAX_WSA881X_DEVICE 2
+#define WSA881X_DIGITAL_SLAVE 0
+#define WSA881X_ANALOG_SLAVE 1
+
+enum {
+	WSA881X_1_X = 0,
+	WSA881X_2_0,
+};
+
+#define WSA881X_IS_2_0(ver) \
+	((ver == WSA881X_2_0) ? 1 : 0)
+
+extern const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE];
+extern struct reg_default wsa881x_ana_reg_defaults[WSA881X_CACHE_SIZE];
+extern struct regmap_config wsa881x_ana_regmap_config[2];
+int wsa881x_get_client_index(void);
+int wsa881x_get_probing_count(void);
+int wsa881x_get_presence_count(void);
+int wsa881x_set_mclk_callback(
+	int (*enable_mclk_callback)(struct snd_soc_card *, bool));
+void wsa881x_update_reg_defaults_2_0(void);
+void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag);
+
+#endif /* _WSA881X_H */
diff --git a/sound/soc/codecs/wsa881x-irq.c b/sound/soc/codecs/wsa881x-irq.c
new file mode 100644
index 0000000..9afbd92
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-irq.c
@@ -0,0 +1,610 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/slab.h>
+#include <linux/ratelimit.h>
+#include <linux/pm_qos.h>
+#include <soc/qcom/pm.h>
+#include "wsa881x-irq.h"
+#include "wsa881x-registers-analog.h"
+
+#define BYTE_BIT_MASK(nr)		(1UL << ((nr) % BITS_PER_BYTE))
+#define BIT_BYTE(nr)			((nr) / BITS_PER_BYTE)
+
+
+#define WSA_MAX_NUM_IRQS 8
+
+#ifndef NO_IRQ
+#define NO_IRQ	(-1)
+#endif
+
+static int virq_to_phyirq(
+	struct wsa_resource *wsa_res, int virq);
+static int phyirq_to_virq(
+	struct wsa_resource *wsa_res, int irq);
+static unsigned int wsa_irq_get_upstream_irq(
+	struct wsa_resource *wsa_res);
+static void wsa_irq_put_upstream_irq(
+	struct wsa_resource *wsa_res);
+static int wsa_map_irq(
+	struct wsa_resource *wsa_res, int irq);
+
+static struct snd_soc_codec *ptr_codec;
+
+/**
+ * wsa_set_codec() - to update codec pointer
+ * @codec: codec pointer.
+ *
+ * To update the codec pointer, which is used to read/write
+ * wsa register.
+ *
+ * Return: void.
+ */
+void wsa_set_codec(struct snd_soc_codec *codec)
+{
+	if (codec == NULL) {
+		pr_err("%s: codec pointer is NULL\n", __func__);
+		ptr_codec = NULL;
+		return;
+	}
+	ptr_codec = codec;
+	/* Initialize interrupt mask and level registers */
+	snd_soc_write(codec, WSA881X_INTR_LEVEL, 0x8F);
+	snd_soc_write(codec, WSA881X_INTR_MASK, 0x8F);
+}
+
+static void wsa_irq_lock(struct irq_data *data)
+{
+	struct wsa_resource *wsa_res =
+			irq_data_get_irq_chip_data(data);
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res pointer is NULL\n", __func__);
+		return;
+	}
+	mutex_lock(&wsa_res->irq_lock);
+}
+
+static void wsa_irq_sync_unlock(struct irq_data *data)
+{
+	struct wsa_resource *wsa_res =
+			irq_data_get_irq_chip_data(data);
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res pointer is NULL\n", __func__);
+		return;
+	}
+	if (wsa_res->codec == NULL) {
+		pr_err("%s: codec pointer not registered\n", __func__);
+		if (ptr_codec == NULL) {
+			pr_err("%s: did not receive valid codec pointer\n",
+					__func__);
+			goto unlock;
+		} else {
+			wsa_res->codec = ptr_codec;
+		}
+	}
+
+	/*
+	 * If there's been a change in the mask write it back
+	 * to the hardware.
+	 */
+	if (wsa_res->irq_masks_cur !=
+			wsa_res->irq_masks_cache) {
+
+		wsa_res->irq_masks_cache =
+			wsa_res->irq_masks_cur;
+		snd_soc_write(wsa_res->codec,
+			WSA881X_INTR_MASK,
+			wsa_res->irq_masks_cur);
+	}
+unlock:
+	mutex_unlock(&wsa_res->irq_lock);
+}
+
+static void wsa_irq_enable(struct irq_data *data)
+{
+	struct wsa_resource *wsa_res =
+			irq_data_get_irq_chip_data(data);
+	int wsa_irq;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res pointer is NULL\n", __func__);
+		return;
+	}
+	wsa_irq = virq_to_phyirq(wsa_res, data->irq);
+	pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq);
+	wsa_res->irq_masks_cur &=
+			~(BYTE_BIT_MASK(wsa_irq));
+}
+
+static void wsa_irq_disable(struct irq_data *data)
+{
+	struct wsa_resource *wsa_res =
+			irq_data_get_irq_chip_data(data);
+	int wsa_irq;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res pointer is NULL\n", __func__);
+		return;
+	}
+	wsa_irq = virq_to_phyirq(wsa_res, data->irq);
+	pr_debug("%s: wsa_irq = %d\n", __func__, wsa_irq);
+	wsa_res->irq_masks_cur
+			|= BYTE_BIT_MASK(wsa_irq);
+}
+
+static void wsa_irq_ack(struct irq_data *data)
+{
+	int wsa_irq = 0;
+	struct wsa_resource *wsa_res =
+			irq_data_get_irq_chip_data(data);
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	wsa_irq = virq_to_phyirq(wsa_res, data->irq);
+	pr_debug("%s: IRQ_ACK called for WCD9XXX IRQ: %d\n",
+				__func__, wsa_irq);
+}
+
+static void wsa_irq_mask(struct irq_data *d)
+{
+	/* do nothing but required as linux calls irq_mask without NULL check */
+}
+
+static struct irq_chip wsa_irq_chip = {
+	.name = "wsa",
+	.irq_bus_lock = wsa_irq_lock,
+	.irq_bus_sync_unlock = wsa_irq_sync_unlock,
+	.irq_disable = wsa_irq_disable,
+	.irq_enable = wsa_irq_enable,
+	.irq_mask = wsa_irq_mask,
+	.irq_ack = wsa_irq_ack,
+};
+
+static irqreturn_t wsa_irq_thread(int irq, void *data)
+{
+	struct wsa_resource *wsa_res = data;
+	int i;
+	u8 status;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return IRQ_HANDLED;
+	}
+	if (wsa_res->codec == NULL) {
+		pr_err("%s: codec pointer not registered\n", __func__);
+		if (ptr_codec == NULL) {
+			pr_err("%s: did not receive valid codec pointer\n",
+					__func__);
+			return IRQ_HANDLED;
+		}
+		wsa_res->codec = ptr_codec;
+	}
+	status = snd_soc_read(wsa_res->codec, WSA881X_INTR_STATUS);
+	/* Apply masking */
+	status &= ~wsa_res->irq_masks_cur;
+
+	for (i = 0; i < wsa_res->num_irqs; i++) {
+		if (status & BYTE_BIT_MASK(i)) {
+			mutex_lock(&wsa_res->nested_irq_lock);
+			handle_nested_irq(phyirq_to_virq(wsa_res, i));
+			mutex_unlock(&wsa_res->nested_irq_lock);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * wsa_free_irq() - to free an interrupt
+ * @irq: interrupt number.
+ * @data: pointer to wsa resource.
+ *
+ * To free already requested interrupt.
+ *
+ * Return: void.
+ */
+void wsa_free_irq(int irq, void *data)
+{
+	struct wsa_resource *wsa_res = data;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	free_irq(phyirq_to_virq(wsa_res, irq), data);
+}
+
+/**
+ * wsa_enable_irq() - to enable an interrupt
+ * @wsa_res: pointer to wsa resource.
+ * @irq: interrupt number.
+ *
+ * This function is to enable an interrupt.
+ *
+ * Return: void.
+ */
+void wsa_enable_irq(struct wsa_resource *wsa_res, int irq)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	enable_irq(phyirq_to_virq(wsa_res, irq));
+}
+
+/**
+ * wsa_disable_irq() - to disable an interrupt
+ * @wsa_res: pointer to wsa resource.
+ * @irq: interrupt number.
+ *
+ * To disable an interrupt without waiting for executing
+ * handler to complete.
+ *
+ * Return: void.
+ */
+void wsa_disable_irq(struct wsa_resource *wsa_res, int irq)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	disable_irq_nosync(phyirq_to_virq(wsa_res, irq));
+}
+
+/**
+ * wsa_disable_irq_sync() - to disable an interrupt
+ * @wsa_res: pointer to wsa resource.
+ * @irq: interrupt number.
+ *
+ * To disable an interrupt, wait for executing IRQ
+ * handler to complete.
+ *
+ * Return: void.
+ */
+void wsa_disable_irq_sync(
+			struct wsa_resource *wsa_res, int irq)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	disable_irq(phyirq_to_virq(wsa_res, irq));
+}
+
+static int wsa_irq_setup_downstream_irq(struct wsa_resource *wsa_res)
+{
+	int irq, virq, ret;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: enter\n", __func__);
+
+	for (irq = 0; irq < wsa_res->num_irqs; irq++) {
+		/* Map OF irq */
+		virq = wsa_map_irq(wsa_res, irq);
+		pr_debug("%s: irq %d -> %d\n", __func__, irq, virq);
+		if (virq == NO_IRQ) {
+			pr_err("%s, No interrupt specifier for irq %d\n",
+			       __func__, irq);
+			return NO_IRQ;
+		}
+
+		ret = irq_set_chip_data(virq, wsa_res);
+		if (ret) {
+			pr_err("%s: Failed to configure irq %d (%d)\n",
+			       __func__, irq, ret);
+			return ret;
+		}
+
+		if (wsa_res->irq_level_high[irq])
+			irq_set_chip_and_handler(virq, &wsa_irq_chip,
+						 handle_level_irq);
+		else
+			irq_set_chip_and_handler(virq, &wsa_irq_chip,
+						 handle_edge_irq);
+
+		irq_set_nested_thread(virq, 1);
+	}
+
+	pr_debug("%s: leave\n", __func__);
+
+	return 0;
+}
+
+static int wsa_irq_init(struct wsa_resource *wsa_res)
+{
+	int i, ret;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return -EINVAL;
+	}
+	mutex_init(&wsa_res->irq_lock);
+	mutex_init(&wsa_res->nested_irq_lock);
+
+	wsa_res->irq = wsa_irq_get_upstream_irq(wsa_res);
+	if (!wsa_res->irq) {
+		pr_warn("%s: irq driver is not yet initialized\n", __func__);
+		mutex_destroy(&wsa_res->irq_lock);
+		mutex_destroy(&wsa_res->nested_irq_lock);
+		return -EPROBE_DEFER;
+	}
+	pr_debug("%s: probed irq %d\n", __func__, wsa_res->irq);
+
+	/* Setup downstream IRQs */
+	ret = wsa_irq_setup_downstream_irq(wsa_res);
+	if (ret) {
+		pr_err("%s: Failed to setup downstream IRQ\n", __func__);
+		goto fail_irq_init;
+	}
+
+	/* mask all the interrupts */
+	for (i = 0; i < wsa_res->num_irqs; i++) {
+		wsa_res->irq_masks_cur |= BYTE_BIT_MASK(i);
+		wsa_res->irq_masks_cache |= BYTE_BIT_MASK(i);
+	}
+
+	ret = request_threaded_irq(wsa_res->irq, NULL, wsa_irq_thread,
+				   IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+				   "wsa", wsa_res);
+	if (ret != 0) {
+		dev_err(wsa_res->dev, "Failed to request IRQ %d: %d\n",
+			wsa_res->irq, ret);
+	} else {
+		ret = enable_irq_wake(wsa_res->irq);
+		if (ret) {
+			dev_err(wsa_res->dev,
+				"Failed to set wake interrupt on IRQ %d: %d\n",
+				wsa_res->irq, ret);
+			free_irq(wsa_res->irq, wsa_res);
+		}
+	}
+
+	if (ret)
+		goto fail_irq_init;
+
+	return ret;
+
+fail_irq_init:
+	dev_err(wsa_res->dev,
+			"%s: Failed to init wsa irq\n", __func__);
+	wsa_irq_put_upstream_irq(wsa_res);
+	mutex_destroy(&wsa_res->irq_lock);
+	mutex_destroy(&wsa_res->nested_irq_lock);
+	return ret;
+}
+
+/**
+ * wsa_request_irq() - to request/register an interrupt
+ * @wsa_res: pointer to wsa_resource.
+ * @irq: interrupt number.
+ * @handler: interrupt handler function pointer.
+ * @name: interrupt name.
+ * @data: device info.
+ *
+ * Convert physical irq to virtual irq and then
+ * reguest for threaded handler.
+ *
+ * Return: Retuns success/failure.
+ */
+int wsa_request_irq(struct wsa_resource *wsa_res,
+			int irq, irq_handler_t handler,
+			const char *name, void *data)
+{
+	int virq;
+
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return -EINVAL;
+	}
+	virq = phyirq_to_virq(wsa_res, irq);
+
+	/*
+	 * ARM needs us to explicitly flag the IRQ as valid
+	 * and will set them noprobe when we do so.
+	 */
+#if defined(CONFIG_ARM) || defined(CONFIG_ARM64)
+	set_irq_flags(virq, IRQF_VALID);
+#else
+	set_irq_noprobe(virq);
+#endif
+
+	return request_threaded_irq(virq, NULL, handler, IRQF_TRIGGER_RISING,
+				    name, data);
+}
+
+/**
+ * wsa_irq_exit() - to disable/clear interrupt/resources
+ * @wsa_res: pointer to wsa_resource
+ *
+ * Disable and free the interrupts and then release resources.
+ *
+ * Return: void.
+ */
+void wsa_irq_exit(struct wsa_resource *wsa_res)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	dev_dbg(wsa_res->dev, "%s: Cleaning up irq %d\n", __func__,
+		wsa_res->irq);
+
+	if (wsa_res->irq) {
+		disable_irq_wake(wsa_res->irq);
+		free_irq(wsa_res->irq, wsa_res);
+		/* Release parent's of node */
+		wsa_irq_put_upstream_irq(wsa_res);
+	}
+	mutex_destroy(&wsa_res->irq_lock);
+	mutex_destroy(&wsa_res->nested_irq_lock);
+}
+
+static int phyirq_to_virq(struct wsa_resource *wsa_res, int offset)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return -EINVAL;
+	}
+	return irq_linear_revmap(wsa_res->domain, offset);
+}
+
+static int virq_to_phyirq(struct wsa_resource *wsa_res, int virq)
+{
+	struct irq_data *irq_data = irq_get_irq_data(virq);
+
+	if (unlikely(!irq_data)) {
+		pr_err("%s: irq_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	return irq_data->hwirq;
+}
+
+static unsigned int wsa_irq_get_upstream_irq(struct wsa_resource *wsa_res)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return -EINVAL;
+	}
+	return wsa_res->irq;
+}
+
+static void wsa_irq_put_upstream_irq(struct wsa_resource *wsa_res)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return;
+	}
+	/* Hold parent's of node */
+	of_node_put(wsa_res->dev->of_node);
+}
+
+static int wsa_map_irq(struct wsa_resource *wsa_res, int irq)
+{
+	if (wsa_res == NULL) {
+		pr_err("%s: wsa_res is NULL\n", __func__);
+		return -EINVAL;
+	}
+	return of_irq_to_resource(wsa_res->dev->of_node, irq, NULL);
+}
+
+static int wsa_irq_probe(struct platform_device *pdev)
+{
+	int irq;
+	struct wsa_resource *wsa_res = NULL;
+	int ret = -EINVAL;
+
+	irq = platform_get_irq_byname(pdev, "wsa-int");
+	if (irq < 0) {
+		dev_err(&pdev->dev, "%s: Couldn't find wsa-int node(%d)\n",
+			__func__, irq);
+		return -EINVAL;
+	}
+	pr_debug("%s: node %s\n", __func__, pdev->name);
+	wsa_res = kzalloc(sizeof(*wsa_res), GFP_KERNEL);
+	if (!wsa_res) {
+		pr_err("%s: could not allocate memory\n", __func__);
+		return -ENOMEM;
+	}
+	/*
+	 * wsa interrupt controller supports N to N irq mapping with
+	 * single cell binding with irq numbers(offsets) only.
+	 * Use irq_domain_simple_ops that has irq_domain_simple_map and
+	 * irq_domain_xlate_onetwocell.
+	 */
+	wsa_res->dev = &pdev->dev;
+	wsa_res->domain = irq_domain_add_linear(wsa_res->dev->of_node,
+			WSA_MAX_NUM_IRQS, &irq_domain_simple_ops,
+			wsa_res);
+	if (!wsa_res->domain) {
+		dev_err(&pdev->dev, "%s: domain is NULL\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+	wsa_res->dev = &pdev->dev;
+
+	dev_dbg(&pdev->dev, "%s: virq = %d\n", __func__, irq);
+	wsa_res->irq = irq;
+	wsa_res->num_irq_regs = 1;
+	wsa_res->num_irqs = WSA_NUM_IRQS;
+	ret = wsa_irq_init(wsa_res);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "%s: failed to do irq init %d\n",
+				__func__, ret);
+		goto err;
+	}
+
+	return ret;
+err:
+	kfree(wsa_res);
+	return ret;
+}
+
+static int wsa_irq_remove(struct platform_device *pdev)
+{
+	struct irq_domain *domain;
+	struct wsa_resource *data;
+
+	domain = irq_find_host(pdev->dev.of_node);
+	if (unlikely(!domain)) {
+		pr_err("%s: domain is NULL\n", __func__);
+		return -EINVAL;
+	}
+	data = (struct wsa_resource *)domain->host_data;
+	data->irq = 0;
+
+	return 0;
+}
+
+static const struct of_device_id of_match[] = {
+	{ .compatible = "qcom,wsa-irq" },
+	{ }
+};
+
+static struct platform_driver wsa_irq_driver = {
+	.probe = wsa_irq_probe,
+	.remove = wsa_irq_remove,
+	.driver = {
+		.name = "wsa_intc",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(of_match),
+	},
+};
+
+static int wsa_irq_drv_init(void)
+{
+	return platform_driver_register(&wsa_irq_driver);
+}
+subsys_initcall(wsa_irq_drv_init);
+
+static void wsa_irq_drv_exit(void)
+{
+	platform_driver_unregister(&wsa_irq_driver);
+}
+module_exit(wsa_irq_drv_exit);
+
+MODULE_DESCRIPTION("WSA881x IRQ driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa881x-irq.h b/sound/soc/codecs/wsa881x-irq.h
new file mode 100644
index 0000000..270eb91
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-irq.h
@@ -0,0 +1,82 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __WSA881X_IRQ_H__
+#define __WSA881X_IRQ_H__
+
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
+#include <sound/soc.h>
+
+/**
+ * enum wsa_interrupts - wsa interrupt number
+ * @WSA_INT_SAF2WAR:	Temp irq interrupt, from safe state to warning state.
+ * @WSA_INT_WAR2SAF:	Temp irq interrupt, from warning state to safe state.
+ * @WSA_INT_DISABLE:	Disable Temp sensor interrupts.
+ * @WSA_INT_OCP:	OCP interrupt.
+ * @WSA_INT_CLIP:	CLIP detect interrupt.
+ * @WSA_NUM_IRQS:	MAX Interrupt number.
+ *
+ * WSA IRQ Interrupt numbers.
+ */
+enum wsa_interrupts {
+	WSA_INT_SAF2WAR = 0,
+	WSA_INT_WAR2SAF,
+	WSA_INT_DISABLE,
+	WSA_INT_OCP,
+	WSA_INT_CLIP,
+	WSA_NUM_IRQS,
+};
+
+/**
+ * struct wsa_resource - the basic wsa_resource structure
+ * @irq_lock:	lock used by irq_chip functions.
+ * @nested_irq_lock: lock used while handling nested interrupts.
+ * @irq:	interrupt number.
+ * @irq_masks_cur: current mask value to be written to mask registers.
+ * @irq_masks_cache: cached mask value.
+ * @num_irqs: number of supported interrupts.
+ * @num_irq_regs: number of irq registers.
+ * @parent:	parent pointer.
+ * @dev:	device pointer.
+ * @domain:	irq domain pointer.
+ * codec:	codec pointer.
+ *
+ * Contains required members used in wsa irq driver.
+ */
+
+struct wsa_resource {
+	struct mutex irq_lock;
+	struct mutex nested_irq_lock;
+	unsigned int irq;
+	u8 irq_masks_cur;
+	u8 irq_masks_cache;
+	bool irq_level_high[8];
+	int num_irqs;
+	int num_irq_regs;
+	void *parent;
+	struct device *dev;
+	struct irq_domain *domain;
+	struct snd_soc_codec *codec;
+};
+
+void wsa_set_codec(struct snd_soc_codec *codec);
+void wsa_free_irq(int irq, void *data);
+void wsa_enable_irq(struct wsa_resource *wsa_res, int irq);
+void wsa_disable_irq(struct wsa_resource *wsa_res, int irq);
+void wsa_disable_irq_sync(struct wsa_resource *wsa_res, int irq);
+int wsa_request_irq(struct wsa_resource *wsa_res,
+			int irq, irq_handler_t handler,
+			const char *name, void *data);
+
+void wsa_irq_exit(struct wsa_resource *wsa_res);
+
+#endif /* __WSA881X_IRQ_H__ */
diff --git a/sound/soc/codecs/wsa881x-registers-analog.h b/sound/soc/codecs/wsa881x-registers-analog.h
new file mode 100644
index 0000000..a5ebf8e1
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-registers-analog.h
@@ -0,0 +1,206 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WSA881X_REGISTERS_H
+#define WSA881X_REGISTERS_H
+
+#define WSA881X_DIGITAL_BASE	0x0000
+#define WSA881X_ANALOG_BASE	0x0100
+
+#define WSA881X_CHIP_ID0                        (WSA881X_DIGITAL_BASE+0x0000)
+#define WSA881X_CHIP_ID1			(WSA881X_DIGITAL_BASE+0x0001)
+#define WSA881X_CHIP_ID2			(WSA881X_DIGITAL_BASE+0x0002)
+#define WSA881X_CHIP_ID3			(WSA881X_DIGITAL_BASE+0x0003)
+#define WSA881X_BUS_ID				(WSA881X_DIGITAL_BASE+0x0004)
+#define WSA881X_CDC_RST_CTL			(WSA881X_DIGITAL_BASE+0x0005)
+#define WSA881X_CDC_TOP_CLK_CTL			(WSA881X_DIGITAL_BASE+0x0006)
+#define WSA881X_CDC_ANA_CLK_CTL			(WSA881X_DIGITAL_BASE+0x0007)
+#define WSA881X_CDC_DIG_CLK_CTL			(WSA881X_DIGITAL_BASE+0x0008)
+#define WSA881X_CLOCK_CONFIG			(WSA881X_DIGITAL_BASE+0x0009)
+#define WSA881X_ANA_CTL				(WSA881X_DIGITAL_BASE+0x000A)
+#define WSA881X_SWR_RESET_EN			(WSA881X_DIGITAL_BASE+0x000B)
+#define WSA881X_RESET_CTL			(WSA881X_DIGITAL_BASE+0x000C)
+#define WSA881X_TADC_VALUE_CTL			(WSA881X_DIGITAL_BASE+0x000F)
+#define WSA881X_TEMP_DETECT_CTL			(WSA881X_DIGITAL_BASE+0x0010)
+#define WSA881X_TEMP_MSB			(WSA881X_DIGITAL_BASE+0x0011)
+#define WSA881X_TEMP_LSB			(WSA881X_DIGITAL_BASE+0x0012)
+#define WSA881X_TEMP_CONFIG0			(WSA881X_DIGITAL_BASE+0x0013)
+#define WSA881X_TEMP_CONFIG1			(WSA881X_DIGITAL_BASE+0x0014)
+#define WSA881X_CDC_CLIP_CTL			(WSA881X_DIGITAL_BASE+0x0015)
+#define WSA881X_SDM_PDM9_LSB			(WSA881X_DIGITAL_BASE+0x0016)
+#define WSA881X_SDM_PDM9_MSB			(WSA881X_DIGITAL_BASE+0x0017)
+#define WSA881X_CDC_RX_CTL			(WSA881X_DIGITAL_BASE+0x0018)
+#define WSA881X_DEM_BYPASS_DATA0		(WSA881X_DIGITAL_BASE+0x0019)
+#define WSA881X_DEM_BYPASS_DATA1		(WSA881X_DIGITAL_BASE+0x001A)
+#define WSA881X_DEM_BYPASS_DATA2		(WSA881X_DIGITAL_BASE+0x001B)
+#define WSA881X_DEM_BYPASS_DATA3		(WSA881X_DIGITAL_BASE+0x001C)
+#define WSA881X_OTP_CTRL0			(WSA881X_DIGITAL_BASE+0x001D)
+#define WSA881X_OTP_CTRL1			(WSA881X_DIGITAL_BASE+0x001E)
+#define WSA881X_HDRIVE_CTL_GROUP1		(WSA881X_DIGITAL_BASE+0x001F)
+#define WSA881X_INTR_MODE			(WSA881X_DIGITAL_BASE+0x0020)
+#define WSA881X_INTR_MASK			(WSA881X_DIGITAL_BASE+0x0021)
+#define WSA881X_INTR_STATUS			(WSA881X_DIGITAL_BASE+0x0022)
+#define WSA881X_INTR_CLEAR			(WSA881X_DIGITAL_BASE+0x0023)
+#define WSA881X_INTR_LEVEL			(WSA881X_DIGITAL_BASE+0x0024)
+#define WSA881X_INTR_SET			(WSA881X_DIGITAL_BASE+0x0025)
+#define WSA881X_INTR_TEST			(WSA881X_DIGITAL_BASE+0x0026)
+#define WSA881X_PDM_TEST_MODE			(WSA881X_DIGITAL_BASE+0x0030)
+#define WSA881X_ATE_TEST_MODE			(WSA881X_DIGITAL_BASE+0x0031)
+#define WSA881X_PIN_CTL_MODE			(WSA881X_DIGITAL_BASE+0x0032)
+#define WSA881X_PIN_CTL_OE			(WSA881X_DIGITAL_BASE+0x0033)
+#define WSA881X_PIN_WDATA_IOPAD			(WSA881X_DIGITAL_BASE+0x0034)
+#define WSA881X_PIN_STATUS			(WSA881X_DIGITAL_BASE+0x0035)
+#define WSA881X_DIG_DEBUG_MODE			(WSA881X_DIGITAL_BASE+0x0037)
+#define WSA881X_DIG_DEBUG_SEL			(WSA881X_DIGITAL_BASE+0x0038)
+#define WSA881X_DIG_DEBUG_EN			(WSA881X_DIGITAL_BASE+0x0039)
+#define WSA881X_SWR_HM_TEST1			(WSA881X_DIGITAL_BASE+0x003B)
+#define WSA881X_SWR_HM_TEST2			(WSA881X_DIGITAL_BASE+0x003C)
+#define WSA881X_TEMP_DETECT_DBG_CTL		(WSA881X_DIGITAL_BASE+0x003D)
+#define WSA881X_TEMP_DEBUG_MSB			(WSA881X_DIGITAL_BASE+0x003E)
+#define WSA881X_TEMP_DEBUG_LSB			(WSA881X_DIGITAL_BASE+0x003F)
+#define WSA881X_SAMPLE_EDGE_SEL			(WSA881X_DIGITAL_BASE+0x0044)
+#define WSA881X_IOPAD_CTL			(WSA881X_DIGITAL_BASE+0x0045)
+#define WSA881X_SPARE_0				(WSA881X_DIGITAL_BASE+0x0050)
+#define WSA881X_SPARE_1				(WSA881X_DIGITAL_BASE+0x0051)
+#define WSA881X_SPARE_2				(WSA881X_DIGITAL_BASE+0x0052)
+#define WSA881X_OTP_REG_0			(WSA881X_DIGITAL_BASE+0x0080)
+#define WSA881X_OTP_REG_1			(WSA881X_DIGITAL_BASE+0x0081)
+#define WSA881X_OTP_REG_2			(WSA881X_DIGITAL_BASE+0x0082)
+#define WSA881X_OTP_REG_3			(WSA881X_DIGITAL_BASE+0x0083)
+#define WSA881X_OTP_REG_4			(WSA881X_DIGITAL_BASE+0x0084)
+#define WSA881X_OTP_REG_5			(WSA881X_DIGITAL_BASE+0x0085)
+#define WSA881X_OTP_REG_6			(WSA881X_DIGITAL_BASE+0x0086)
+#define WSA881X_OTP_REG_7			(WSA881X_DIGITAL_BASE+0x0087)
+#define WSA881X_OTP_REG_8			(WSA881X_DIGITAL_BASE+0x0088)
+#define WSA881X_OTP_REG_9			(WSA881X_DIGITAL_BASE+0x0089)
+#define WSA881X_OTP_REG_10			(WSA881X_DIGITAL_BASE+0x008A)
+#define WSA881X_OTP_REG_11			(WSA881X_DIGITAL_BASE+0x008B)
+#define WSA881X_OTP_REG_12			(WSA881X_DIGITAL_BASE+0x008C)
+#define WSA881X_OTP_REG_13			(WSA881X_DIGITAL_BASE+0x008D)
+#define WSA881X_OTP_REG_14			(WSA881X_DIGITAL_BASE+0x008E)
+#define WSA881X_OTP_REG_15			(WSA881X_DIGITAL_BASE+0x008F)
+#define WSA881X_OTP_REG_16			(WSA881X_DIGITAL_BASE+0x0090)
+#define WSA881X_OTP_REG_17			(WSA881X_DIGITAL_BASE+0x0091)
+#define WSA881X_OTP_REG_18			(WSA881X_DIGITAL_BASE+0x0092)
+#define WSA881X_OTP_REG_19			(WSA881X_DIGITAL_BASE+0x0093)
+#define WSA881X_OTP_REG_20			(WSA881X_DIGITAL_BASE+0x0094)
+#define WSA881X_OTP_REG_21			(WSA881X_DIGITAL_BASE+0x0095)
+#define WSA881X_OTP_REG_22			(WSA881X_DIGITAL_BASE+0x0096)
+#define WSA881X_OTP_REG_23			(WSA881X_DIGITAL_BASE+0x0097)
+#define WSA881X_OTP_REG_24			(WSA881X_DIGITAL_BASE+0x0098)
+#define WSA881X_OTP_REG_25			(WSA881X_DIGITAL_BASE+0x0099)
+#define WSA881X_OTP_REG_26			(WSA881X_DIGITAL_BASE+0x009A)
+#define WSA881X_OTP_REG_27			(WSA881X_DIGITAL_BASE+0x009B)
+#define WSA881X_OTP_REG_28			(WSA881X_DIGITAL_BASE+0x009C)
+#define WSA881X_OTP_REG_29			(WSA881X_DIGITAL_BASE+0x009D)
+#define WSA881X_OTP_REG_30			(WSA881X_DIGITAL_BASE+0x009E)
+#define WSA881X_OTP_REG_31			(WSA881X_DIGITAL_BASE+0x009F)
+#define WSA881X_OTP_REG_32			(WSA881X_DIGITAL_BASE+0x00A0)
+#define WSA881X_OTP_REG_33			(WSA881X_DIGITAL_BASE+0x00A1)
+#define WSA881X_OTP_REG_34			(WSA881X_DIGITAL_BASE+0x00A2)
+#define WSA881X_OTP_REG_35			(WSA881X_DIGITAL_BASE+0x00A3)
+#define WSA881X_OTP_REG_36			(WSA881X_DIGITAL_BASE+0x00A4)
+#define WSA881X_OTP_REG_37			(WSA881X_DIGITAL_BASE+0x00A5)
+#define WSA881X_OTP_REG_38			(WSA881X_DIGITAL_BASE+0x00A6)
+#define WSA881X_OTP_REG_39			(WSA881X_DIGITAL_BASE+0x00A7)
+#define WSA881X_OTP_REG_40			(WSA881X_DIGITAL_BASE+0x00A8)
+#define WSA881X_OTP_REG_41			(WSA881X_DIGITAL_BASE+0x00A9)
+#define WSA881X_OTP_REG_42			(WSA881X_DIGITAL_BASE+0x00AA)
+#define WSA881X_OTP_REG_43			(WSA881X_DIGITAL_BASE+0x00AB)
+#define WSA881X_OTP_REG_44			(WSA881X_DIGITAL_BASE+0x00AC)
+#define WSA881X_OTP_REG_45			(WSA881X_DIGITAL_BASE+0x00AD)
+#define WSA881X_OTP_REG_46			(WSA881X_DIGITAL_BASE+0x00AE)
+#define WSA881X_OTP_REG_47			(WSA881X_DIGITAL_BASE+0x00AF)
+#define WSA881X_OTP_REG_48			(WSA881X_DIGITAL_BASE+0x00B0)
+#define WSA881X_OTP_REG_49			(WSA881X_DIGITAL_BASE+0x00B1)
+#define WSA881X_OTP_REG_50			(WSA881X_DIGITAL_BASE+0x00B2)
+#define WSA881X_OTP_REG_51			(WSA881X_DIGITAL_BASE+0x00B3)
+#define WSA881X_OTP_REG_52			(WSA881X_DIGITAL_BASE+0x00B4)
+#define WSA881X_OTP_REG_53			(WSA881X_DIGITAL_BASE+0x00B5)
+#define WSA881X_OTP_REG_54			(WSA881X_DIGITAL_BASE+0x00B6)
+#define WSA881X_OTP_REG_55			(WSA881X_DIGITAL_BASE+0x00B7)
+#define WSA881X_OTP_REG_56			(WSA881X_DIGITAL_BASE+0x00B8)
+#define WSA881X_OTP_REG_57			(WSA881X_DIGITAL_BASE+0x00B9)
+#define WSA881X_OTP_REG_58			(WSA881X_DIGITAL_BASE+0x00BA)
+#define WSA881X_OTP_REG_59			(WSA881X_DIGITAL_BASE+0x00BB)
+#define WSA881X_OTP_REG_60			(WSA881X_DIGITAL_BASE+0x00BC)
+#define WSA881X_OTP_REG_61			(WSA881X_DIGITAL_BASE+0x00BD)
+#define WSA881X_OTP_REG_62			(WSA881X_DIGITAL_BASE+0x00BE)
+#define WSA881X_OTP_REG_63			(WSA881X_DIGITAL_BASE+0x00BF)
+/* Analog Register address space */
+#define WSA881X_BIAS_REF_CTRL			(WSA881X_ANALOG_BASE+0x0000)
+#define WSA881X_BIAS_TEST			(WSA881X_ANALOG_BASE+0x0001)
+#define WSA881X_BIAS_BIAS			(WSA881X_ANALOG_BASE+0x0002)
+#define WSA881X_TEMP_OP				(WSA881X_ANALOG_BASE+0x0003)
+#define WSA881X_TEMP_IREF_CTRL			(WSA881X_ANALOG_BASE+0x0004)
+#define WSA881X_TEMP_ISENS_CTRL			(WSA881X_ANALOG_BASE+0x0005)
+#define WSA881X_TEMP_CLK_CTRL			(WSA881X_ANALOG_BASE+0x0006)
+#define WSA881X_TEMP_TEST			(WSA881X_ANALOG_BASE+0x0007)
+#define WSA881X_TEMP_BIAS			(WSA881X_ANALOG_BASE+0x0008)
+#define WSA881X_TEMP_ADC_CTRL			(WSA881X_ANALOG_BASE+0x0009)
+#define WSA881X_TEMP_DOUT_MSB			(WSA881X_ANALOG_BASE+0x000A)
+#define WSA881X_TEMP_DOUT_LSB			(WSA881X_ANALOG_BASE+0x000B)
+#define WSA881X_ADC_EN_MODU_V			(WSA881X_ANALOG_BASE+0x0010)
+#define WSA881X_ADC_EN_MODU_I			(WSA881X_ANALOG_BASE+0x0011)
+#define WSA881X_ADC_EN_DET_TEST_V		(WSA881X_ANALOG_BASE+0x0012)
+#define WSA881X_ADC_EN_DET_TEST_I		(WSA881X_ANALOG_BASE+0x0013)
+#define WSA881X_ADC_SEL_IBIAS			(WSA881X_ANALOG_BASE+0x0014)
+#define WSA881X_ADC_EN_SEL_IBIAS		(WSA881X_ANALOG_BASE+0x0015)
+#define WSA881X_SPKR_DRV_EN			(WSA881X_ANALOG_BASE+0x001A)
+#define WSA881X_SPKR_DRV_GAIN			(WSA881X_ANALOG_BASE+0x001B)
+#define WSA881X_SPKR_DAC_CTL			(WSA881X_ANALOG_BASE+0x001C)
+#define WSA881X_SPKR_DRV_DBG			(WSA881X_ANALOG_BASE+0x001D)
+#define WSA881X_SPKR_PWRSTG_DBG			(WSA881X_ANALOG_BASE+0x001E)
+#define WSA881X_SPKR_OCP_CTL			(WSA881X_ANALOG_BASE+0x001F)
+#define WSA881X_SPKR_CLIP_CTL			(WSA881X_ANALOG_BASE+0x0020)
+#define WSA881X_SPKR_BBM_CTL			(WSA881X_ANALOG_BASE+0x0021)
+#define WSA881X_SPKR_MISC_CTL1			(WSA881X_ANALOG_BASE+0x0022)
+#define WSA881X_SPKR_MISC_CTL2			(WSA881X_ANALOG_BASE+0x0023)
+#define WSA881X_SPKR_BIAS_INT			(WSA881X_ANALOG_BASE+0x0024)
+#define WSA881X_SPKR_PA_INT			(WSA881X_ANALOG_BASE+0x0025)
+#define WSA881X_SPKR_BIAS_CAL			(WSA881X_ANALOG_BASE+0x0026)
+#define WSA881X_SPKR_BIAS_PSRR			(WSA881X_ANALOG_BASE+0x0027)
+#define WSA881X_SPKR_STATUS1			(WSA881X_ANALOG_BASE+0x0028)
+#define WSA881X_SPKR_STATUS2			(WSA881X_ANALOG_BASE+0x0029)
+#define WSA881X_BOOST_EN_CTL			(WSA881X_ANALOG_BASE+0x002A)
+#define WSA881X_BOOST_CURRENT_LIMIT		(WSA881X_ANALOG_BASE+0x002B)
+#define WSA881X_BOOST_PS_CTL			(WSA881X_ANALOG_BASE+0x002C)
+#define WSA881X_BOOST_PRESET_OUT1		(WSA881X_ANALOG_BASE+0x002D)
+#define WSA881X_BOOST_PRESET_OUT2		(WSA881X_ANALOG_BASE+0x002E)
+#define WSA881X_BOOST_FORCE_OUT			(WSA881X_ANALOG_BASE+0x002F)
+#define WSA881X_BOOST_LDO_PROG			(WSA881X_ANALOG_BASE+0x0030)
+#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB	(WSA881X_ANALOG_BASE+0x0031)
+#define WSA881X_BOOST_RON_CTL			(WSA881X_ANALOG_BASE+0x0032)
+#define WSA881X_BOOST_LOOP_STABILITY		(WSA881X_ANALOG_BASE+0x0033)
+#define WSA881X_BOOST_ZX_CTL			(WSA881X_ANALOG_BASE+0x0034)
+#define WSA881X_BOOST_START_CTL			(WSA881X_ANALOG_BASE+0x0035)
+#define WSA881X_BOOST_MISC1_CTL			(WSA881X_ANALOG_BASE+0x0036)
+#define WSA881X_BOOST_MISC2_CTL			(WSA881X_ANALOG_BASE+0x0037)
+#define WSA881X_BOOST_MISC3_CTL			(WSA881X_ANALOG_BASE+0x0038)
+#define WSA881X_BOOST_ATEST_CTL			(WSA881X_ANALOG_BASE+0x0039)
+#define WSA881X_SPKR_PROT_FE_GAIN		(WSA881X_ANALOG_BASE+0x003A)
+#define WSA881X_SPKR_PROT_FE_CM_LDO_SET		(WSA881X_ANALOG_BASE+0x003B)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1	(WSA881X_ANALOG_BASE+0x003C)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2	(WSA881X_ANALOG_BASE+0x003D)
+#define WSA881X_SPKR_PROT_ATEST1		(WSA881X_ANALOG_BASE+0x003E)
+#define WSA881X_SPKR_PROT_ATEST2		(WSA881X_ANALOG_BASE+0x003F)
+#define WSA881X_SPKR_PROT_FE_VSENSE_VCM		(WSA881X_ANALOG_BASE+0x0040)
+#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1	(WSA881X_ANALOG_BASE+0x0041)
+#define WSA881X_BONGO_RESRV_REG1		(WSA881X_ANALOG_BASE+0x0042)
+#define WSA881X_BONGO_RESRV_REG2		(WSA881X_ANALOG_BASE+0x0043)
+#define WSA881X_SPKR_PROT_SAR			(WSA881X_ANALOG_BASE+0x0044)
+#define WSA881X_SPKR_STATUS3			(WSA881X_ANALOG_BASE+0x0045)
+
+#define WSA881X_NUM_REGISTERS		(WSA881X_SPKR_STATUS3+1)
+#define WSA881X_MAX_REGISTER		(WSA881X_NUM_REGISTERS-1)
+#define WSA881X_CACHE_SIZE		WSA881X_NUM_REGISTERS
+#endif /* WSA881X_REGISTERS_H */
diff --git a/sound/soc/codecs/wsa881x-registers.h b/sound/soc/codecs/wsa881x-registers.h
new file mode 100644
index 0000000..825a5f0
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-registers.h
@@ -0,0 +1,178 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef WSA881X_REGISTERS_H
+#define WSA881X_REGISTERS_H
+
+#define WSA881X_DIGITAL_BASE		0x3000
+#define WSA881X_ANALOG_BASE		0x3100
+
+/* Digital register address space */
+#define WSA881X_CHIP_ID0			(WSA881X_DIGITAL_BASE+0x0000)
+#define WSA881X_CHIP_ID1			(WSA881X_DIGITAL_BASE+0x0001)
+#define WSA881X_CHIP_ID2			(WSA881X_DIGITAL_BASE+0x0002)
+#define WSA881X_CHIP_ID3			(WSA881X_DIGITAL_BASE+0x0003)
+#define WSA881X_BUS_ID				(WSA881X_DIGITAL_BASE+0x0004)
+#define WSA881X_CDC_RST_CTL			(WSA881X_DIGITAL_BASE+0x0005)
+#define WSA881X_CDC_TOP_CLK_CTL			(WSA881X_DIGITAL_BASE+0x0006)
+#define WSA881X_CDC_ANA_CLK_CTL			(WSA881X_DIGITAL_BASE+0x0007)
+#define WSA881X_CDC_DIG_CLK_CTL			(WSA881X_DIGITAL_BASE+0x0008)
+#define WSA881X_CLOCK_CONFIG			(WSA881X_DIGITAL_BASE+0x0009)
+#define WSA881X_ANA_CTL				(WSA881X_DIGITAL_BASE+0x000A)
+#define WSA881X_SWR_RESET_EN			(WSA881X_DIGITAL_BASE+0x000B)
+#define WSA881X_RESET_CTL			(WSA881X_DIGITAL_BASE+0x000C)
+#define WSA881X_TADC_VALUE_CTL			(WSA881X_DIGITAL_BASE+0x000F)
+#define WSA881X_TEMP_DETECT_CTL			(WSA881X_DIGITAL_BASE+0x0010)
+#define WSA881X_TEMP_MSB			(WSA881X_DIGITAL_BASE+0x0011)
+#define WSA881X_TEMP_LSB			(WSA881X_DIGITAL_BASE+0x0012)
+#define WSA881X_TEMP_CONFIG0			(WSA881X_DIGITAL_BASE+0x0013)
+#define WSA881X_TEMP_CONFIG1			(WSA881X_DIGITAL_BASE+0x0014)
+#define WSA881X_CDC_CLIP_CTL			(WSA881X_DIGITAL_BASE+0x0015)
+#define WSA881X_SDM_PDM9_LSB			(WSA881X_DIGITAL_BASE+0x0016)
+#define WSA881X_SDM_PDM9_MSB			(WSA881X_DIGITAL_BASE+0x0017)
+#define WSA881X_CDC_RX_CTL			(WSA881X_DIGITAL_BASE+0x0018)
+#define WSA881X_DEM_BYPASS_DATA0		(WSA881X_DIGITAL_BASE+0x0019)
+#define WSA881X_DEM_BYPASS_DATA1		(WSA881X_DIGITAL_BASE+0x001A)
+#define WSA881X_DEM_BYPASS_DATA2		(WSA881X_DIGITAL_BASE+0x001B)
+#define WSA881X_DEM_BYPASS_DATA3		(WSA881X_DIGITAL_BASE+0x001C)
+#define WSA881X_OTP_CTRL0			(WSA881X_DIGITAL_BASE+0x001D)
+#define WSA881X_OTP_CTRL1			(WSA881X_DIGITAL_BASE+0x001E)
+#define WSA881X_HDRIVE_CTL_GROUP1		(WSA881X_DIGITAL_BASE+0x001F)
+#define WSA881X_INTR_MODE			(WSA881X_DIGITAL_BASE+0x0020)
+#define WSA881X_INTR_MASK			(WSA881X_DIGITAL_BASE+0x0021)
+#define WSA881X_INTR_STATUS			(WSA881X_DIGITAL_BASE+0x0022)
+#define WSA881X_INTR_CLEAR			(WSA881X_DIGITAL_BASE+0x0023)
+#define WSA881X_INTR_LEVEL			(WSA881X_DIGITAL_BASE+0x0024)
+#define WSA881X_INTR_SET			(WSA881X_DIGITAL_BASE+0x0025)
+#define WSA881X_INTR_TEST			(WSA881X_DIGITAL_BASE+0x0026)
+#define WSA881X_PDM_TEST_MODE			(WSA881X_DIGITAL_BASE+0x0030)
+#define WSA881X_ATE_TEST_MODE			(WSA881X_DIGITAL_BASE+0x0031)
+#define WSA881X_PIN_CTL_MODE			(WSA881X_DIGITAL_BASE+0x0032)
+#define WSA881X_PIN_CTL_OE			(WSA881X_DIGITAL_BASE+0x0033)
+#define WSA881X_PIN_WDATA_IOPAD			(WSA881X_DIGITAL_BASE+0x0034)
+#define WSA881X_PIN_STATUS			(WSA881X_DIGITAL_BASE+0x0035)
+#define WSA881X_DIG_DEBUG_MODE			(WSA881X_DIGITAL_BASE+0x0037)
+#define WSA881X_DIG_DEBUG_SEL			(WSA881X_DIGITAL_BASE+0x0038)
+#define WSA881X_DIG_DEBUG_EN			(WSA881X_DIGITAL_BASE+0x0039)
+#define WSA881X_SWR_HM_TEST1			(WSA881X_DIGITAL_BASE+0x003B)
+#define WSA881X_SWR_HM_TEST2			(WSA881X_DIGITAL_BASE+0x003C)
+#define WSA881X_TEMP_DETECT_DBG_CTL		(WSA881X_DIGITAL_BASE+0x003D)
+#define WSA881X_TEMP_DEBUG_MSB			(WSA881X_DIGITAL_BASE+0x003E)
+#define WSA881X_TEMP_DEBUG_LSB			(WSA881X_DIGITAL_BASE+0x003F)
+#define WSA881X_SAMPLE_EDGE_SEL			(WSA881X_DIGITAL_BASE+0x0044)
+#define WSA881X_IOPAD_CTL			(WSA881X_DIGITAL_BASE+0x0045)
+#define WSA881X_SPARE_0				(WSA881X_DIGITAL_BASE+0x0050)
+#define WSA881X_SPARE_1				(WSA881X_DIGITAL_BASE+0x0051)
+#define WSA881X_SPARE_2				(WSA881X_DIGITAL_BASE+0x0052)
+#define WSA881X_OTP_REG_0			(WSA881X_DIGITAL_BASE+0x0080)
+#define WSA881X_OTP_REG_1			(WSA881X_DIGITAL_BASE+0x0081)
+#define WSA881X_OTP_REG_2			(WSA881X_DIGITAL_BASE+0x0082)
+#define WSA881X_OTP_REG_3			(WSA881X_DIGITAL_BASE+0x0083)
+#define WSA881X_OTP_REG_4			(WSA881X_DIGITAL_BASE+0x0084)
+#define WSA881X_OTP_REG_5			(WSA881X_DIGITAL_BASE+0x0085)
+#define WSA881X_OTP_REG_6			(WSA881X_DIGITAL_BASE+0x0086)
+#define WSA881X_OTP_REG_7			(WSA881X_DIGITAL_BASE+0x0087)
+#define WSA881X_OTP_REG_8			(WSA881X_DIGITAL_BASE+0x0088)
+#define WSA881X_OTP_REG_9			(WSA881X_DIGITAL_BASE+0x0089)
+#define WSA881X_OTP_REG_10			(WSA881X_DIGITAL_BASE+0x008A)
+#define WSA881X_OTP_REG_11			(WSA881X_DIGITAL_BASE+0x008B)
+#define WSA881X_OTP_REG_12			(WSA881X_DIGITAL_BASE+0x008C)
+#define WSA881X_OTP_REG_13			(WSA881X_DIGITAL_BASE+0x008D)
+#define WSA881X_OTP_REG_14			(WSA881X_DIGITAL_BASE+0x008E)
+#define WSA881X_OTP_REG_15			(WSA881X_DIGITAL_BASE+0x008F)
+#define WSA881X_OTP_REG_16			(WSA881X_DIGITAL_BASE+0x0090)
+#define WSA881X_OTP_REG_17			(WSA881X_DIGITAL_BASE+0x0091)
+#define WSA881X_OTP_REG_18			(WSA881X_DIGITAL_BASE+0x0092)
+#define WSA881X_OTP_REG_19			(WSA881X_DIGITAL_BASE+0x0093)
+#define WSA881X_OTP_REG_20			(WSA881X_DIGITAL_BASE+0x0094)
+#define WSA881X_OTP_REG_21			(WSA881X_DIGITAL_BASE+0x0095)
+#define WSA881X_OTP_REG_22			(WSA881X_DIGITAL_BASE+0x0096)
+#define WSA881X_OTP_REG_23			(WSA881X_DIGITAL_BASE+0x0097)
+#define WSA881X_OTP_REG_24			(WSA881X_DIGITAL_BASE+0x0098)
+#define WSA881X_OTP_REG_25			(WSA881X_DIGITAL_BASE+0x0099)
+#define WSA881X_OTP_REG_26			(WSA881X_DIGITAL_BASE+0x009A)
+#define WSA881X_OTP_REG_27			(WSA881X_DIGITAL_BASE+0x009B)
+#define WSA881X_OTP_REG_28			(WSA881X_DIGITAL_BASE+0x009C)
+#define WSA881X_OTP_REG_29			(WSA881X_DIGITAL_BASE+0x009D)
+#define WSA881X_OTP_REG_30			(WSA881X_DIGITAL_BASE+0x009E)
+#define WSA881X_OTP_REG_31			(WSA881X_DIGITAL_BASE+0x009F)
+#define WSA881X_OTP_REG_63			(WSA881X_DIGITAL_BASE+0x00BF)
+
+/* Analog Register address space */
+#define WSA881X_BIAS_REF_CTRL			(WSA881X_ANALOG_BASE+0x0000)
+#define WSA881X_BIAS_TEST			(WSA881X_ANALOG_BASE+0x0001)
+#define WSA881X_BIAS_BIAS			(WSA881X_ANALOG_BASE+0x0002)
+#define WSA881X_TEMP_OP				(WSA881X_ANALOG_BASE+0x0003)
+#define WSA881X_TEMP_IREF_CTRL			(WSA881X_ANALOG_BASE+0x0004)
+#define WSA881X_TEMP_ISENS_CTRL			(WSA881X_ANALOG_BASE+0x0005)
+#define WSA881X_TEMP_CLK_CTRL			(WSA881X_ANALOG_BASE+0x0006)
+#define WSA881X_TEMP_TEST			(WSA881X_ANALOG_BASE+0x0007)
+#define WSA881X_TEMP_BIAS			(WSA881X_ANALOG_BASE+0x0008)
+#define WSA881X_TEMP_ADC_CTRL			(WSA881X_ANALOG_BASE+0x0009)
+#define WSA881X_TEMP_DOUT_MSB			(WSA881X_ANALOG_BASE+0x000A)
+#define WSA881X_TEMP_DOUT_LSB			(WSA881X_ANALOG_BASE+0x000B)
+#define WSA881X_ADC_EN_MODU_V			(WSA881X_ANALOG_BASE+0x0010)
+#define WSA881X_ADC_EN_MODU_I			(WSA881X_ANALOG_BASE+0x0011)
+#define WSA881X_ADC_EN_DET_TEST_V		(WSA881X_ANALOG_BASE+0x0012)
+#define WSA881X_ADC_EN_DET_TEST_I		(WSA881X_ANALOG_BASE+0x0013)
+#define WSA881X_ADC_SEL_IBIAS			(WSA881X_ANALOG_BASE+0x0014)
+#define WSA881X_ADC_EN_SEL_IBAIS		(WSA881X_ANALOG_BASE+0x0015)
+#define WSA881X_SPKR_DRV_EN			(WSA881X_ANALOG_BASE+0x001A)
+#define WSA881X_SPKR_DRV_GAIN			(WSA881X_ANALOG_BASE+0x001B)
+#define WSA881X_SPKR_DAC_CTL			(WSA881X_ANALOG_BASE+0x001C)
+#define WSA881X_SPKR_DRV_DBG			(WSA881X_ANALOG_BASE+0x001D)
+#define WSA881X_SPKR_PWRSTG_DBG			(WSA881X_ANALOG_BASE+0x001E)
+#define WSA881X_SPKR_OCP_CTL			(WSA881X_ANALOG_BASE+0x001F)
+#define WSA881X_SPKR_CLIP_CTL			(WSA881X_ANALOG_BASE+0x0020)
+#define WSA881X_SPKR_BBM_CTL			(WSA881X_ANALOG_BASE+0x0021)
+#define WSA881X_SPKR_MISC_CTL1			(WSA881X_ANALOG_BASE+0x0022)
+#define WSA881X_SPKR_MISC_CTL2			(WSA881X_ANALOG_BASE+0x0023)
+#define WSA881X_SPKR_BIAS_INT			(WSA881X_ANALOG_BASE+0x0024)
+#define WSA881X_SPKR_PA_INT			(WSA881X_ANALOG_BASE+0x0025)
+#define WSA881X_SPKR_BIAS_CAL			(WSA881X_ANALOG_BASE+0x0026)
+#define WSA881X_SPKR_BIAS_PSRR			(WSA881X_ANALOG_BASE+0x0027)
+#define WSA881X_SPKR_STATUS1			(WSA881X_ANALOG_BASE+0x0028)
+#define WSA881X_SPKR_STATUS2			(WSA881X_ANALOG_BASE+0x0029)
+#define WSA881X_BOOST_EN_CTL			(WSA881X_ANALOG_BASE+0x002A)
+#define WSA881X_BOOST_CURRENT_LIMIT		(WSA881X_ANALOG_BASE+0x002B)
+#define WSA881X_BOOST_PS_CTL			(WSA881X_ANALOG_BASE+0x002C)
+#define WSA881X_BOOST_PRESET_OUT1		(WSA881X_ANALOG_BASE+0x002D)
+#define WSA881X_BOOST_PRESET_OUT2		(WSA881X_ANALOG_BASE+0x002E)
+#define WSA881X_BOOST_FORCE_OUT			(WSA881X_ANALOG_BASE+0x002F)
+#define WSA881X_BOOST_LDO_PROG			(WSA881X_ANALOG_BASE+0x0030)
+#define WSA881X_BOOST_SLOPE_COMP_ISENSE_FB	(WSA881X_ANALOG_BASE+0x0031)
+#define WSA881X_BOOST_RON_CTL			(WSA881X_ANALOG_BASE+0x0032)
+#define WSA881X_BOOST_LOOP_STABILITY		(WSA881X_ANALOG_BASE+0x0033)
+#define WSA881X_BOOST_ZX_CTL			(WSA881X_ANALOG_BASE+0x0034)
+#define WSA881X_BOOST_START_CTL			(WSA881X_ANALOG_BASE+0x0035)
+#define WSA881X_BOOST_MISC1_CTL			(WSA881X_ANALOG_BASE+0x0036)
+#define WSA881X_BOOST_MISC2_CTL			(WSA881X_ANALOG_BASE+0x0037)
+#define WSA881X_BOOST_MISC3_CTL			(WSA881X_ANALOG_BASE+0x0038)
+#define WSA881X_BOOST_ATEST_CTL			(WSA881X_ANALOG_BASE+0x0039)
+#define WSA881X_SPKR_PROT_FE_GAIN		(WSA881X_ANALOG_BASE+0x003A)
+#define WSA881X_SPKR_PROT_FE_CM_LDO_SET		(WSA881X_ANALOG_BASE+0x003B)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1	(WSA881X_ANALOG_BASE+0x003C)
+#define WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2	(WSA881X_ANALOG_BASE+0x003D)
+#define WSA881X_SPKR_PROT_ATEST1		(WSA881X_ANALOG_BASE+0x003E)
+#define WSA881X_SPKR_PROT_ATEST2		(WSA881X_ANALOG_BASE+0x003F)
+#define WSA881X_SPKR_PROT_FE_VSENSE_VCM		(WSA881X_ANALOG_BASE+0x0040)
+#define WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1	(WSA881X_ANALOG_BASE+0x0041)
+#define WSA881X_BONGO_RESRV_REG1		(WSA881X_ANALOG_BASE+0x0042)
+#define WSA881X_BONGO_RESRV_REG2		(WSA881X_ANALOG_BASE+0x0043)
+#define WSA881X_SPKR_PROT_SAR			(WSA881X_ANALOG_BASE+0x0044)
+#define WSA881X_SPKR_STATUS3			(WSA881X_ANALOG_BASE+0x0045)
+
+#define WSA881X_NUM_REGISTERS			(WSA881X_SPKR_STATUS3+1)
+#define WSA881X_MAX_REGISTER			(WSA881X_NUM_REGISTERS-1)
+#define WSA881X_CACHE_SIZE			WSA881X_NUM_REGISTERS
+
+#endif /* WSA881X_REGISTERS_H */
diff --git a/sound/soc/codecs/wsa881x-regmap-analog.c b/sound/soc/codecs/wsa881x-regmap-analog.c
new file mode 100644
index 0000000..2bc3c9e
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-regmap-analog.c
@@ -0,0 +1,499 @@
+/*
+ * Copyright (c) 2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers-analog.h"
+#include "wsa881x-analog.h"
+
+struct reg_default wsa881x_ana_reg_defaults[] = {
+	{WSA881X_CHIP_ID0, 0x00},
+	{WSA881X_CHIP_ID1, 0x00},
+	{WSA881X_CHIP_ID2, 0x00},
+	{WSA881X_CHIP_ID3, 0x02},
+	{WSA881X_BUS_ID, 0x00},
+	{WSA881X_CDC_RST_CTL, 0x00},
+	{WSA881X_CDC_TOP_CLK_CTL, 0x03},
+	{WSA881X_CDC_ANA_CLK_CTL, 0x00},
+	{WSA881X_CDC_DIG_CLK_CTL, 0x00},
+	{WSA881X_CLOCK_CONFIG, 0x00},
+	{WSA881X_ANA_CTL, 0x08},
+	{WSA881X_SWR_RESET_EN, 0x00},
+	{WSA881X_TEMP_DETECT_CTL, 0x01},
+	{WSA881X_TEMP_MSB, 0x00},
+	{WSA881X_TEMP_LSB, 0x00},
+	{WSA881X_TEMP_CONFIG0, 0x00},
+	{WSA881X_TEMP_CONFIG1, 0x00},
+	{WSA881X_CDC_CLIP_CTL, 0x03},
+	{WSA881X_SDM_PDM9_LSB, 0x00},
+	{WSA881X_SDM_PDM9_MSB, 0x00},
+	{WSA881X_CDC_RX_CTL, 0x7E},
+	{WSA881X_DEM_BYPASS_DATA0, 0x00},
+	{WSA881X_DEM_BYPASS_DATA1, 0x00},
+	{WSA881X_DEM_BYPASS_DATA2, 0x00},
+	{WSA881X_DEM_BYPASS_DATA3, 0x00},
+	{WSA881X_OTP_CTRL0, 0x00},
+	{WSA881X_OTP_CTRL1, 0x00},
+	{WSA881X_HDRIVE_CTL_GROUP1, 0x00},
+	{WSA881X_INTR_MODE, 0x00},
+	{WSA881X_INTR_MASK, 0x1F},
+	{WSA881X_INTR_STATUS, 0x00},
+	{WSA881X_INTR_CLEAR, 0x00},
+	{WSA881X_INTR_LEVEL, 0x00},
+	{WSA881X_INTR_SET, 0x00},
+	{WSA881X_INTR_TEST, 0x00},
+	{WSA881X_PDM_TEST_MODE, 0x00},
+	{WSA881X_ATE_TEST_MODE, 0x00},
+	{WSA881X_PIN_CTL_MODE, 0x00},
+	{WSA881X_PIN_CTL_OE, 0x00},
+	{WSA881X_PIN_WDATA_IOPAD, 0x00},
+	{WSA881X_PIN_STATUS, 0x00},
+	{WSA881X_DIG_DEBUG_MODE, 0x00},
+	{WSA881X_DIG_DEBUG_SEL, 0x00},
+	{WSA881X_DIG_DEBUG_EN, 0x00},
+	{WSA881X_SWR_HM_TEST1, 0x08},
+	{WSA881X_SWR_HM_TEST2, 0x00},
+	{WSA881X_TEMP_DETECT_DBG_CTL, 0x00},
+	{WSA881X_TEMP_DEBUG_MSB, 0x00},
+	{WSA881X_TEMP_DEBUG_LSB, 0x00},
+	{WSA881X_SAMPLE_EDGE_SEL, 0x0C},
+	{WSA881X_SPARE_0, 0x00},
+	{WSA881X_SPARE_1, 0x00},
+	{WSA881X_SPARE_2, 0x00},
+	{WSA881X_OTP_REG_0, 0x01},
+	{WSA881X_OTP_REG_1, 0xFF},
+	{WSA881X_OTP_REG_2, 0xC0},
+	{WSA881X_OTP_REG_3, 0xFF},
+	{WSA881X_OTP_REG_4, 0xC0},
+	{WSA881X_OTP_REG_5, 0xFF},
+	{WSA881X_OTP_REG_6, 0xFF},
+	{WSA881X_OTP_REG_7, 0xFF},
+	{WSA881X_OTP_REG_8, 0xFF},
+	{WSA881X_OTP_REG_9, 0xFF},
+	{WSA881X_OTP_REG_10, 0xFF},
+	{WSA881X_OTP_REG_11, 0xFF},
+	{WSA881X_OTP_REG_12, 0xFF},
+	{WSA881X_OTP_REG_13, 0xFF},
+	{WSA881X_OTP_REG_14, 0xFF},
+	{WSA881X_OTP_REG_15, 0xFF},
+	{WSA881X_OTP_REG_16, 0xFF},
+	{WSA881X_OTP_REG_17, 0xFF},
+	{WSA881X_OTP_REG_18, 0xFF},
+	{WSA881X_OTP_REG_19, 0xFF},
+	{WSA881X_OTP_REG_20, 0xFF},
+	{WSA881X_OTP_REG_21, 0xFF},
+	{WSA881X_OTP_REG_22, 0xFF},
+	{WSA881X_OTP_REG_23, 0xFF},
+	{WSA881X_OTP_REG_24, 0x03},
+	{WSA881X_OTP_REG_25, 0x01},
+	{WSA881X_OTP_REG_26, 0x03},
+	{WSA881X_OTP_REG_27, 0x11},
+	{WSA881X_OTP_REG_28, 0xFF},
+	{WSA881X_OTP_REG_29, 0xFF},
+	{WSA881X_OTP_REG_30, 0xFF},
+	{WSA881X_OTP_REG_31, 0xFF},
+	{WSA881X_OTP_REG_63, 0x40},
+	/* WSA881x Analog registers */
+	{WSA881X_BIAS_REF_CTRL, 0x6C},
+	{WSA881X_BIAS_TEST, 0x16},
+	{WSA881X_BIAS_BIAS, 0xF0},
+	{WSA881X_TEMP_OP, 0x00},
+	{WSA881X_TEMP_IREF_CTRL, 0x56},
+	{WSA881X_TEMP_ISENS_CTRL, 0x47},
+	{WSA881X_TEMP_CLK_CTRL, 0x87},
+	{WSA881X_TEMP_TEST, 0x00},
+	{WSA881X_TEMP_BIAS, 0x51},
+	{WSA881X_TEMP_ADC_CTRL, 0x00},
+	{WSA881X_TEMP_DOUT_MSB, 0x00},
+	{WSA881X_TEMP_DOUT_LSB, 0x00},
+	{WSA881X_ADC_EN_MODU_V, 0x00},
+	{WSA881X_ADC_EN_MODU_I, 0x00},
+	{WSA881X_ADC_EN_DET_TEST_V, 0x00},
+	{WSA881X_ADC_EN_DET_TEST_I, 0x00},
+	{WSA881X_ADC_SEL_IBIAS, 0x25},
+	{WSA881X_ADC_EN_SEL_IBIAS, 0x10},
+	{WSA881X_SPKR_DRV_EN, 0x74},
+	{WSA881X_SPKR_DRV_GAIN, 0x01},
+	{WSA881X_SPKR_DAC_CTL, 0x40},
+	{WSA881X_SPKR_DRV_DBG, 0x15},
+	{WSA881X_SPKR_PWRSTG_DBG, 0x00},
+	{WSA881X_SPKR_OCP_CTL, 0xD4},
+	{WSA881X_SPKR_CLIP_CTL, 0x90},
+	{WSA881X_SPKR_BBM_CTL, 0x00},
+	{WSA881X_SPKR_MISC_CTL1, 0x80},
+	{WSA881X_SPKR_MISC_CTL2, 0x00},
+	{WSA881X_SPKR_BIAS_INT, 0x56},
+	{WSA881X_SPKR_PA_INT, 0x54},
+	{WSA881X_SPKR_BIAS_CAL, 0xAC},
+	{WSA881X_SPKR_BIAS_PSRR, 0x54},
+	{WSA881X_SPKR_STATUS1, 0x00},
+	{WSA881X_SPKR_STATUS2, 0x00},
+	{WSA881X_BOOST_EN_CTL, 0x18},
+	{WSA881X_BOOST_CURRENT_LIMIT, 0x7A},
+	{WSA881X_BOOST_PS_CTL, 0xC0},
+	{WSA881X_BOOST_PRESET_OUT1, 0x77},
+	{WSA881X_BOOST_PRESET_OUT2, 0x70},
+	{WSA881X_BOOST_FORCE_OUT, 0x0E},
+	{WSA881X_BOOST_LDO_PROG, 0x16},
+	{WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71},
+	{WSA881X_BOOST_RON_CTL, 0x0F},
+	{WSA881X_BOOST_LOOP_STABILITY, 0xAD},
+	{WSA881X_BOOST_ZX_CTL, 0x34},
+	{WSA881X_BOOST_START_CTL, 0x23},
+	{WSA881X_BOOST_MISC1_CTL, 0x80},
+	{WSA881X_BOOST_MISC2_CTL, 0x00},
+	{WSA881X_BOOST_MISC3_CTL, 0x00},
+	{WSA881X_BOOST_ATEST_CTL, 0x00},
+	{WSA881X_SPKR_PROT_FE_GAIN, 0x46},
+	{WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B},
+	{WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D},
+	{WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D},
+	{WSA881X_SPKR_PROT_ATEST1, 0x01},
+	{WSA881X_SPKR_PROT_ATEST2, 0x00},
+	{WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D},
+	{WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D},
+	{WSA881X_BONGO_RESRV_REG1, 0x00},
+	{WSA881X_BONGO_RESRV_REG2, 0x00},
+	{WSA881X_SPKR_PROT_SAR, 0x00},
+	{WSA881X_SPKR_STATUS3, 0x00},
+};
+
+struct reg_default wsa881x_ana_reg_defaults_0[] = {
+	{WSA881X_CHIP_ID0, 0x00},
+	{WSA881X_CHIP_ID1, 0x00},
+	{WSA881X_CHIP_ID2, 0x00},
+	{WSA881X_CHIP_ID3, 0x02},
+	{WSA881X_BUS_ID, 0x00},
+	{WSA881X_CDC_RST_CTL, 0x00},
+	{WSA881X_CDC_TOP_CLK_CTL, 0x03},
+	{WSA881X_CDC_ANA_CLK_CTL, 0x00},
+	{WSA881X_CDC_DIG_CLK_CTL, 0x00},
+	{WSA881X_CLOCK_CONFIG, 0x00},
+	{WSA881X_ANA_CTL, 0x08},
+	{WSA881X_SWR_RESET_EN, 0x00},
+	{WSA881X_TEMP_DETECT_CTL, 0x01},
+	{WSA881X_TEMP_MSB, 0x00},
+	{WSA881X_TEMP_LSB, 0x00},
+	{WSA881X_TEMP_CONFIG0, 0x00},
+	{WSA881X_TEMP_CONFIG1, 0x00},
+	{WSA881X_CDC_CLIP_CTL, 0x03},
+	{WSA881X_SDM_PDM9_LSB, 0x00},
+	{WSA881X_SDM_PDM9_MSB, 0x00},
+	{WSA881X_CDC_RX_CTL, 0x7E},
+	{WSA881X_DEM_BYPASS_DATA0, 0x00},
+	{WSA881X_DEM_BYPASS_DATA1, 0x00},
+	{WSA881X_DEM_BYPASS_DATA2, 0x00},
+	{WSA881X_DEM_BYPASS_DATA3, 0x00},
+	{WSA881X_OTP_CTRL0, 0x00},
+	{WSA881X_OTP_CTRL1, 0x00},
+	{WSA881X_HDRIVE_CTL_GROUP1, 0x00},
+	{WSA881X_INTR_MODE, 0x00},
+	{WSA881X_INTR_MASK, 0x1F},
+	{WSA881X_INTR_STATUS, 0x00},
+	{WSA881X_INTR_CLEAR, 0x00},
+	{WSA881X_INTR_LEVEL, 0x00},
+	{WSA881X_INTR_SET, 0x00},
+	{WSA881X_INTR_TEST, 0x00},
+	{WSA881X_PDM_TEST_MODE, 0x00},
+	{WSA881X_ATE_TEST_MODE, 0x00},
+	{WSA881X_PIN_CTL_MODE, 0x00},
+	{WSA881X_PIN_CTL_OE, 0x00},
+	{WSA881X_PIN_WDATA_IOPAD, 0x00},
+	{WSA881X_PIN_STATUS, 0x00},
+	{WSA881X_DIG_DEBUG_MODE, 0x00},
+	{WSA881X_DIG_DEBUG_SEL, 0x00},
+	{WSA881X_DIG_DEBUG_EN, 0x00},
+	{WSA881X_SWR_HM_TEST1, 0x08},
+	{WSA881X_SWR_HM_TEST2, 0x00},
+	{WSA881X_TEMP_DETECT_DBG_CTL, 0x00},
+	{WSA881X_TEMP_DEBUG_MSB, 0x00},
+	{WSA881X_TEMP_DEBUG_LSB, 0x00},
+	{WSA881X_SAMPLE_EDGE_SEL, 0x0C},
+	{WSA881X_SPARE_0, 0x00},
+	{WSA881X_SPARE_1, 0x00},
+	{WSA881X_SPARE_2, 0x00},
+	{WSA881X_OTP_REG_0, 0x01},
+	{WSA881X_OTP_REG_1, 0xFF},
+	{WSA881X_OTP_REG_2, 0xC0},
+	{WSA881X_OTP_REG_3, 0xFF},
+	{WSA881X_OTP_REG_4, 0xC0},
+	{WSA881X_OTP_REG_5, 0xFF},
+	{WSA881X_OTP_REG_6, 0xFF},
+	{WSA881X_OTP_REG_7, 0xFF},
+	{WSA881X_OTP_REG_8, 0xFF},
+	{WSA881X_OTP_REG_9, 0xFF},
+	{WSA881X_OTP_REG_10, 0xFF},
+	{WSA881X_OTP_REG_11, 0xFF},
+	{WSA881X_OTP_REG_12, 0xFF},
+	{WSA881X_OTP_REG_13, 0xFF},
+	{WSA881X_OTP_REG_14, 0xFF},
+	{WSA881X_OTP_REG_15, 0xFF},
+	{WSA881X_OTP_REG_16, 0xFF},
+	{WSA881X_OTP_REG_17, 0xFF},
+	{WSA881X_OTP_REG_18, 0xFF},
+	{WSA881X_OTP_REG_19, 0xFF},
+	{WSA881X_OTP_REG_20, 0xFF},
+	{WSA881X_OTP_REG_21, 0xFF},
+	{WSA881X_OTP_REG_22, 0xFF},
+	{WSA881X_OTP_REG_23, 0xFF},
+	{WSA881X_OTP_REG_24, 0x03},
+	{WSA881X_OTP_REG_25, 0x01},
+	{WSA881X_OTP_REG_26, 0x03},
+	{WSA881X_OTP_REG_27, 0x11},
+	{WSA881X_OTP_REG_28, 0xFF},
+	{WSA881X_OTP_REG_29, 0xFF},
+	{WSA881X_OTP_REG_30, 0xFF},
+	{WSA881X_OTP_REG_31, 0xFF},
+	{WSA881X_OTP_REG_63, 0x40},
+};
+
+struct reg_default wsa881x_ana_reg_defaults_1[] = {
+	{WSA881X_BIAS_REF_CTRL - WSA881X_ANALOG_BASE, 0x6C},
+	{WSA881X_BIAS_TEST - WSA881X_ANALOG_BASE, 0x16},
+	{WSA881X_BIAS_BIAS - WSA881X_ANALOG_BASE, 0xF0},
+	{WSA881X_TEMP_OP - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_TEMP_IREF_CTRL - WSA881X_ANALOG_BASE, 0x56},
+	{WSA881X_TEMP_ISENS_CTRL - WSA881X_ANALOG_BASE, 0x47},
+	{WSA881X_TEMP_CLK_CTRL - WSA881X_ANALOG_BASE, 0x87},
+	{WSA881X_TEMP_TEST - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_TEMP_BIAS - WSA881X_ANALOG_BASE, 0x51},
+	{WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_TEMP_DOUT_MSB - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_TEMP_DOUT_LSB - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_ADC_EN_MODU_V - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_ADC_EN_MODU_I - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_ADC_EN_DET_TEST_V - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_ADC_EN_DET_TEST_I - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x25},
+	{WSA881X_ADC_EN_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x10},
+	{WSA881X_SPKR_DRV_EN - WSA881X_ANALOG_BASE, 0x74},
+	{WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0x01},
+	{WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x40},
+	{WSA881X_SPKR_DRV_DBG - WSA881X_ANALOG_BASE, 0x15},
+	{WSA881X_SPKR_PWRSTG_DBG - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_OCP_CTL - WSA881X_ANALOG_BASE, 0xD4},
+	{WSA881X_SPKR_CLIP_CTL - WSA881X_ANALOG_BASE, 0x90},
+	{WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x80},
+	{WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x56},
+	{WSA881X_SPKR_PA_INT - WSA881X_ANALOG_BASE, 0x54},
+	{WSA881X_SPKR_BIAS_CAL - WSA881X_ANALOG_BASE, 0xAC},
+	{WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x54},
+	{WSA881X_SPKR_STATUS1 - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_STATUS2 - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_BOOST_EN_CTL - WSA881X_ANALOG_BASE, 0x18},
+	{WSA881X_BOOST_CURRENT_LIMIT - WSA881X_ANALOG_BASE, 0x7A},
+	{WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xC0},
+	{WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0x77},
+	{WSA881X_BOOST_PRESET_OUT2 - WSA881X_ANALOG_BASE, 0x70},
+	{WSA881X_BOOST_FORCE_OUT - WSA881X_ANALOG_BASE, 0x0E},
+	{WSA881X_BOOST_LDO_PROG - WSA881X_ANALOG_BASE, 0x16},
+	{WSA881X_BOOST_SLOPE_COMP_ISENSE_FB - WSA881X_ANALOG_BASE, 0x71},
+	{WSA881X_BOOST_RON_CTL - WSA881X_ANALOG_BASE, 0x0F},
+	{WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0xAD},
+	{WSA881X_BOOST_ZX_CTL - WSA881X_ANALOG_BASE, 0x34},
+	{WSA881X_BOOST_START_CTL - WSA881X_ANALOG_BASE, 0x23},
+	{WSA881X_BOOST_MISC1_CTL - WSA881X_ANALOG_BASE, 0x80},
+	{WSA881X_BOOST_MISC2_CTL - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_BOOST_MISC3_CTL - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_BOOST_ATEST_CTL - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_PROT_FE_GAIN - WSA881X_ANALOG_BASE, 0x46},
+	{WSA881X_SPKR_PROT_FE_CM_LDO_SET - WSA881X_ANALOG_BASE, 0x3B},
+	{WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x8D},
+	{WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2 - WSA881X_ANALOG_BASE, 0x8D},
+	{WSA881X_SPKR_PROT_ATEST1 - WSA881X_ANALOG_BASE, 0x01},
+	{WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_PROT_FE_VSENSE_VCM - WSA881X_ANALOG_BASE, 0x8D},
+	{WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1 - WSA881X_ANALOG_BASE, 0x4D},
+	{WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_PROT_SAR - WSA881X_ANALOG_BASE, 0x00},
+	{WSA881X_SPKR_STATUS3 - WSA881X_ANALOG_BASE, 0x00},
+};
+
+struct reg_default wsa881x_rev_2_0_dig[] = {
+	{WSA881X_RESET_CTL, 0x00},
+	{WSA881X_TADC_VALUE_CTL, 0x01},
+	{WSA881X_INTR_MASK, 0x1B},
+	{WSA881X_IOPAD_CTL, 0x00},
+	{WSA881X_OTP_REG_28, 0x3F},
+	{WSA881X_OTP_REG_29, 0x3F},
+	{WSA881X_OTP_REG_30, 0x01},
+	{WSA881X_OTP_REG_31, 0x01},
+};
+
+struct reg_default wsa881x_rev_2_0_ana[] = {
+	{WSA881X_TEMP_ADC_CTRL, 0x03},
+	{WSA881X_ADC_SEL_IBIAS, 0x45},
+	{WSA881X_SPKR_DRV_GAIN, 0xC1},
+	{WSA881X_SPKR_DAC_CTL, 0x42},
+	{WSA881X_SPKR_BBM_CTL, 0x02},
+	{WSA881X_SPKR_MISC_CTL1, 0x40},
+	{WSA881X_SPKR_MISC_CTL2, 0x07},
+	{WSA881X_SPKR_BIAS_INT, 0x5F},
+	{WSA881X_SPKR_BIAS_PSRR, 0x44},
+	{WSA881X_BOOST_PS_CTL, 0xA0},
+	{WSA881X_BOOST_PRESET_OUT1, 0xB7},
+	{WSA881X_BOOST_LOOP_STABILITY, 0x8D},
+	{WSA881X_SPKR_PROT_ATEST2, 0x02},
+	{WSA881X_BONGO_RESRV_REG1, 0x5E},
+	{WSA881X_BONGO_RESRV_REG2, 0x07},
+};
+
+struct reg_default wsa881x_rev_2_0_regmap_ana[] = {
+	{WSA881X_TEMP_ADC_CTRL - WSA881X_ANALOG_BASE, 0x03},
+	{WSA881X_ADC_SEL_IBIAS - WSA881X_ANALOG_BASE, 0x45},
+	{WSA881X_SPKR_DRV_GAIN - WSA881X_ANALOG_BASE, 0xC1},
+	{WSA881X_SPKR_DAC_CTL - WSA881X_ANALOG_BASE, 0x42},
+	{WSA881X_SPKR_BBM_CTL - WSA881X_ANALOG_BASE, 0x02},
+	{WSA881X_SPKR_MISC_CTL1 - WSA881X_ANALOG_BASE, 0x40},
+	{WSA881X_SPKR_MISC_CTL2 - WSA881X_ANALOG_BASE, 0x07},
+	{WSA881X_SPKR_BIAS_INT - WSA881X_ANALOG_BASE, 0x5F},
+	{WSA881X_SPKR_BIAS_PSRR - WSA881X_ANALOG_BASE, 0x44},
+	{WSA881X_BOOST_PS_CTL - WSA881X_ANALOG_BASE, 0xA0},
+	{WSA881X_BOOST_PRESET_OUT1 - WSA881X_ANALOG_BASE, 0xB7},
+	{WSA881X_BOOST_LOOP_STABILITY - WSA881X_ANALOG_BASE, 0x8D},
+	{WSA881X_SPKR_PROT_ATEST2 - WSA881X_ANALOG_BASE, 0x02},
+	{WSA881X_BONGO_RESRV_REG1 - WSA881X_ANALOG_BASE, 0x5E},
+	{WSA881X_BONGO_RESRV_REG2 - WSA881X_ANALOG_BASE, 0x07},
+};
+
+/**
+ * wsa881x_update_reg_defaults_2_0 - update default values of regs for v2.0
+ *
+ * WSA881x v2.0 has different default values for certain analog and digital
+ * registers compared to v1.x. Therefore, update the values of these registers
+ * with the values from tables defined above for v2.0.
+ */
+void wsa881x_update_reg_defaults_2_0(void)
+{
+	int i, j;
+
+	for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_dig); i++) {
+		for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++)
+			if (wsa881x_ana_reg_defaults[j].reg ==
+						wsa881x_rev_2_0_dig[i].reg)
+				wsa881x_ana_reg_defaults[j].def =
+						wsa881x_rev_2_0_dig[i].def;
+	}
+	for (i = 0; i < ARRAY_SIZE(wsa881x_rev_2_0_ana); i++) {
+		for (j = 0; j < ARRAY_SIZE(wsa881x_ana_reg_defaults); j++)
+			if (wsa881x_ana_reg_defaults[j].reg ==
+						wsa881x_rev_2_0_ana[i].reg)
+				wsa881x_ana_reg_defaults[j].def =
+						wsa881x_rev_2_0_ana[i].def;
+	}
+}
+EXPORT_SYMBOL(wsa881x_update_reg_defaults_2_0);
+
+/**
+ * wsa881x_update_regmap_2_0 - update regmap framework with new tables
+ * @regmap: pointer to WSA881x regmap structure
+ * @flag: indicates digital or analog WSA881x slave
+ *
+ * WSA881x v2.0 has some new registers for both analog and digital slaves.
+ * Update the regmap framework with all the new registers.
+ */
+void wsa881x_update_regmap_2_0(struct regmap *regmap, int flag)
+{
+	u16 ret = 0;
+
+	switch (flag) {
+	case WSA881X_DIGITAL_SLAVE:
+		ret = regmap_register_patch(regmap, wsa881x_rev_2_0_dig,
+					ARRAY_SIZE(wsa881x_rev_2_0_dig));
+		break;
+	case WSA881X_ANALOG_SLAVE:
+		ret = regmap_register_patch(regmap, wsa881x_rev_2_0_ana,
+					ARRAY_SIZE(wsa881x_rev_2_0_ana));
+		break;
+	default:
+		pr_debug("%s: unknown version", __func__);
+		ret = -EINVAL;
+		break;
+	}
+	if (ret)
+		pr_err("%s: Failed to update regmap defaults ret= %d\n",
+			__func__, ret);
+}
+EXPORT_SYMBOL(wsa881x_update_regmap_2_0);
+
+static bool wsa881x_readable_register(struct device *dev, unsigned int reg)
+{
+	return wsa881x_ana_reg_readable[reg];
+}
+
+static bool wsa881x_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WSA881X_CHIP_ID0:
+	case WSA881X_CHIP_ID1:
+	case WSA881X_CHIP_ID2:
+	case WSA881X_CHIP_ID3:
+	case WSA881X_BUS_ID:
+	case WSA881X_TEMP_MSB:
+	case WSA881X_TEMP_LSB:
+	case WSA881X_SDM_PDM9_LSB:
+	case WSA881X_SDM_PDM9_MSB:
+	case WSA881X_OTP_REG_0:
+	case WSA881X_OTP_REG_1:
+	case WSA881X_OTP_REG_2:
+	case WSA881X_OTP_REG_3:
+	case WSA881X_OTP_REG_4:
+	case WSA881X_OTP_REG_5:
+	case WSA881X_OTP_REG_31:
+	case WSA881X_TEMP_DOUT_MSB:
+	case WSA881X_TEMP_DOUT_LSB:
+	case WSA881X_TEMP_OP:
+	case WSA881X_OTP_CTRL1:
+	case WSA881X_INTR_STATUS:
+	case WSA881X_ATE_TEST_MODE:
+	case WSA881X_PIN_STATUS:
+	case WSA881X_SWR_HM_TEST2:
+	case WSA881X_SPKR_STATUS1:
+	case WSA881X_SPKR_STATUS2:
+	case WSA881X_SPKR_STATUS3:
+	case WSA881X_SPKR_PROT_SAR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+struct regmap_config wsa881x_ana_regmap_config[] = {
+{
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.reg_defaults = wsa881x_ana_reg_defaults_0,
+	.num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_0),
+	.max_register = WSA881X_MAX_REGISTER,
+	.volatile_reg = wsa881x_volatile_register,
+	.readable_reg = wsa881x_readable_register,
+	.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian = REGMAP_ENDIAN_NATIVE,
+},
+{
+	.reg_bits = 8,
+	.val_bits = 8,
+	.cache_type = REGCACHE_NONE,
+	.reg_defaults = wsa881x_ana_reg_defaults_1,
+	.num_reg_defaults = ARRAY_SIZE(wsa881x_ana_reg_defaults_1),
+	.max_register = WSA881X_MAX_REGISTER,
+	.volatile_reg = wsa881x_volatile_register,
+	.readable_reg = wsa881x_readable_register,
+	.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian = REGMAP_ENDIAN_NATIVE,
+}
+};
diff --git a/sound/soc/codecs/wsa881x-regmap.c b/sound/soc/codecs/wsa881x-regmap.c
new file mode 100644
index 0000000..63bbbfa
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-regmap.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers.h"
+#include "wsa881x.h"
+
+/*
+ * Default register reset values that are common across different versions
+ * are defined here. If a register reset value is changed based on version
+ * then remove it from this structure and add it in version specific
+ * structures.
+ */
+static struct reg_default wsa881x_defaults[] = {
+	{WSA881X_CHIP_ID0, 0x00},
+	{WSA881X_CHIP_ID1, 0x00},
+	{WSA881X_CHIP_ID2, 0x00},
+	{WSA881X_CHIP_ID3, 0x02},
+	{WSA881X_BUS_ID, 0x00},
+	{WSA881X_CDC_RST_CTL, 0x00},
+	{WSA881X_CDC_TOP_CLK_CTL, 0x03},
+	{WSA881X_CDC_ANA_CLK_CTL, 0x00},
+	{WSA881X_CDC_DIG_CLK_CTL, 0x00},
+	{WSA881X_CLOCK_CONFIG, 0x00},
+	{WSA881X_ANA_CTL, 0x08},
+	{WSA881X_SWR_RESET_EN, 0x00},
+	{WSA881X_TEMP_DETECT_CTL, 0x01},
+	{WSA881X_TEMP_MSB, 0x00},
+	{WSA881X_TEMP_LSB, 0x00},
+	{WSA881X_TEMP_CONFIG0, 0x00},
+	{WSA881X_TEMP_CONFIG1, 0x00},
+	{WSA881X_CDC_CLIP_CTL, 0x03},
+	{WSA881X_SDM_PDM9_LSB, 0x00},
+	{WSA881X_SDM_PDM9_MSB, 0x00},
+	{WSA881X_CDC_RX_CTL, 0x7E},
+	{WSA881X_DEM_BYPASS_DATA0, 0x00},
+	{WSA881X_DEM_BYPASS_DATA1, 0x00},
+	{WSA881X_DEM_BYPASS_DATA2, 0x00},
+	{WSA881X_DEM_BYPASS_DATA3, 0x00},
+	{WSA881X_OTP_CTRL0, 0x00},
+	{WSA881X_OTP_CTRL1, 0x00},
+	{WSA881X_HDRIVE_CTL_GROUP1, 0x00},
+	{WSA881X_INTR_MODE, 0x00},
+	{WSA881X_INTR_STATUS, 0x00},
+	{WSA881X_INTR_CLEAR, 0x00},
+	{WSA881X_INTR_LEVEL, 0x00},
+	{WSA881X_INTR_SET, 0x00},
+	{WSA881X_INTR_TEST, 0x00},
+	{WSA881X_PDM_TEST_MODE, 0x00},
+	{WSA881X_ATE_TEST_MODE, 0x00},
+	{WSA881X_PIN_CTL_MODE, 0x00},
+	{WSA881X_PIN_CTL_OE, 0x00},
+	{WSA881X_PIN_WDATA_IOPAD, 0x00},
+	{WSA881X_PIN_STATUS, 0x00},
+	{WSA881X_DIG_DEBUG_MODE, 0x00},
+	{WSA881X_DIG_DEBUG_SEL, 0x00},
+	{WSA881X_DIG_DEBUG_EN, 0x00},
+	{WSA881X_SWR_HM_TEST1, 0x08},
+	{WSA881X_SWR_HM_TEST2, 0x00},
+	{WSA881X_TEMP_DETECT_DBG_CTL, 0x00},
+	{WSA881X_TEMP_DEBUG_MSB, 0x00},
+	{WSA881X_TEMP_DEBUG_LSB, 0x00},
+	{WSA881X_SAMPLE_EDGE_SEL, 0x0C},
+	{WSA881X_SPARE_0, 0x00},
+	{WSA881X_SPARE_1, 0x00},
+	{WSA881X_SPARE_2, 0x00},
+	{WSA881X_OTP_REG_0, 0x01},
+	{WSA881X_OTP_REG_1, 0xFF},
+	{WSA881X_OTP_REG_2, 0xC0},
+	{WSA881X_OTP_REG_3, 0xFF},
+	{WSA881X_OTP_REG_4, 0xC0},
+	{WSA881X_OTP_REG_5, 0xFF},
+	{WSA881X_OTP_REG_6, 0xFF},
+	{WSA881X_OTP_REG_7, 0xFF},
+	{WSA881X_OTP_REG_8, 0xFF},
+	{WSA881X_OTP_REG_9, 0xFF},
+	{WSA881X_OTP_REG_10, 0xFF},
+	{WSA881X_OTP_REG_11, 0xFF},
+	{WSA881X_OTP_REG_12, 0xFF},
+	{WSA881X_OTP_REG_13, 0xFF},
+	{WSA881X_OTP_REG_14, 0xFF},
+	{WSA881X_OTP_REG_15, 0xFF},
+	{WSA881X_OTP_REG_16, 0xFF},
+	{WSA881X_OTP_REG_17, 0xFF},
+	{WSA881X_OTP_REG_18, 0xFF},
+	{WSA881X_OTP_REG_19, 0xFF},
+	{WSA881X_OTP_REG_20, 0xFF},
+	{WSA881X_OTP_REG_21, 0xFF},
+	{WSA881X_OTP_REG_22, 0xFF},
+	{WSA881X_OTP_REG_23, 0xFF},
+	{WSA881X_OTP_REG_24, 0x03},
+	{WSA881X_OTP_REG_25, 0x01},
+	{WSA881X_OTP_REG_26, 0x03},
+	{WSA881X_OTP_REG_27, 0x11},
+	{WSA881X_OTP_REG_63, 0x40},
+	/* WSA881x Analog registers */
+	{WSA881X_BIAS_REF_CTRL, 0x6C},
+	{WSA881X_BIAS_TEST, 0x16},
+	{WSA881X_BIAS_BIAS, 0xF0},
+	{WSA881X_TEMP_OP, 0x00},
+	{WSA881X_TEMP_IREF_CTRL, 0x56},
+	{WSA881X_TEMP_ISENS_CTRL, 0x47},
+	{WSA881X_TEMP_CLK_CTRL, 0x87},
+	{WSA881X_TEMP_TEST, 0x00},
+	{WSA881X_TEMP_BIAS, 0x51},
+	{WSA881X_TEMP_DOUT_MSB, 0x00},
+	{WSA881X_TEMP_DOUT_LSB, 0x00},
+	{WSA881X_ADC_EN_MODU_V, 0x00},
+	{WSA881X_ADC_EN_MODU_I, 0x00},
+	{WSA881X_ADC_EN_DET_TEST_V, 0x00},
+	{WSA881X_ADC_EN_DET_TEST_I, 0x00},
+	{WSA881X_ADC_EN_SEL_IBAIS, 0x10},
+	{WSA881X_SPKR_DRV_EN, 0x74},
+	{WSA881X_SPKR_DRV_DBG, 0x15},
+	{WSA881X_SPKR_PWRSTG_DBG, 0x00},
+	{WSA881X_SPKR_OCP_CTL, 0xD4},
+	{WSA881X_SPKR_CLIP_CTL, 0x90},
+	{WSA881X_SPKR_PA_INT, 0x54},
+	{WSA881X_SPKR_BIAS_CAL, 0xAC},
+	{WSA881X_SPKR_STATUS1, 0x00},
+	{WSA881X_SPKR_STATUS2, 0x00},
+	{WSA881X_BOOST_EN_CTL, 0x18},
+	{WSA881X_BOOST_CURRENT_LIMIT, 0x7A},
+	{WSA881X_BOOST_PRESET_OUT2, 0x70},
+	{WSA881X_BOOST_FORCE_OUT, 0x0E},
+	{WSA881X_BOOST_LDO_PROG, 0x16},
+	{WSA881X_BOOST_SLOPE_COMP_ISENSE_FB, 0x71},
+	{WSA881X_BOOST_RON_CTL, 0x0F},
+	{WSA881X_BOOST_ZX_CTL, 0x34},
+	{WSA881X_BOOST_START_CTL, 0x23},
+	{WSA881X_BOOST_MISC1_CTL, 0x80},
+	{WSA881X_BOOST_MISC2_CTL, 0x00},
+	{WSA881X_BOOST_MISC3_CTL, 0x00},
+	{WSA881X_BOOST_ATEST_CTL, 0x00},
+	{WSA881X_SPKR_PROT_FE_GAIN, 0x46},
+	{WSA881X_SPKR_PROT_FE_CM_LDO_SET, 0x3B},
+	{WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1, 0x8D},
+	{WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2, 0x8D},
+	{WSA881X_SPKR_PROT_ATEST1, 0x01},
+	{WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x8D},
+	{WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1, 0x4D},
+	{WSA881X_SPKR_PROT_SAR, 0x00},
+	{WSA881X_SPKR_STATUS3, 0x00},
+};
+
+/* Default register reset values for WSA881x rev 2.0 */
+static struct reg_sequence wsa881x_rev_2_0[] = {
+	{WSA881X_RESET_CTL, 0x00, 0x00},
+	{WSA881X_TADC_VALUE_CTL, 0x01, 0x00},
+	{WSA881X_INTR_MASK, 0x1B, 0x00},
+	{WSA881X_IOPAD_CTL, 0x00, 0x00},
+	{WSA881X_OTP_REG_28, 0x3F, 0x00},
+	{WSA881X_OTP_REG_29, 0x3F, 0x00},
+	{WSA881X_OTP_REG_30, 0x01, 0x00},
+	{WSA881X_OTP_REG_31, 0x01, 0x00},
+	{WSA881X_TEMP_ADC_CTRL, 0x03, 0x00},
+	{WSA881X_ADC_SEL_IBIAS, 0x45, 0x00},
+	{WSA881X_SPKR_DRV_GAIN, 0xC1, 0x00},
+	{WSA881X_SPKR_DAC_CTL, 0x42, 0x00},
+	{WSA881X_SPKR_BBM_CTL, 0x02, 0x00},
+	{WSA881X_SPKR_MISC_CTL1, 0x40, 0x00},
+	{WSA881X_SPKR_MISC_CTL2, 0x07, 0x00},
+	{WSA881X_SPKR_BIAS_INT, 0x5F, 0x00},
+	{WSA881X_SPKR_BIAS_PSRR, 0x44, 0x00},
+	{WSA881X_BOOST_PS_CTL, 0xA0, 0x00},
+	{WSA881X_BOOST_PRESET_OUT1, 0xB7, 0x00},
+	{WSA881X_BOOST_LOOP_STABILITY, 0x8D, 0x00},
+	{WSA881X_SPKR_PROT_ATEST2, 0x02, 0x00},
+	{WSA881X_BONGO_RESRV_REG1, 0x5E, 0x00},
+	{WSA881X_BONGO_RESRV_REG2, 0x07, 0x00},
+};
+
+/*
+ * wsa881x_regmap_defaults - update regmap default register values
+ * @regmap: pointer to regmap structure
+ * @version: wsa881x version id
+ *
+ * Update regmap default register values based on version id
+ *
+ */
+void wsa881x_regmap_defaults(struct regmap *regmap, u8 version)
+{
+	u16 ret = 0;
+
+	if (!regmap) {
+		pr_debug("%s: regmap structure is NULL\n", __func__);
+		return;
+	}
+
+	regcache_cache_only(regmap, true);
+	ret = regmap_multi_reg_write(regmap, wsa881x_rev_2_0,
+				     ARRAY_SIZE(wsa881x_rev_2_0));
+	regcache_cache_only(regmap, false);
+
+	if (ret)
+		pr_debug("%s: Failed to update regmap defaults ret= %d\n",
+			 __func__, ret);
+}
+EXPORT_SYMBOL(wsa881x_regmap_defaults);
+
+static bool wsa881x_readable_register(struct device *dev, unsigned int reg)
+{
+	return wsa881x_reg_readable[reg];
+}
+
+static bool wsa881x_volatile_register(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case WSA881X_CHIP_ID0:
+	case WSA881X_CHIP_ID1:
+	case WSA881X_CHIP_ID2:
+	case WSA881X_CHIP_ID3:
+	case WSA881X_BUS_ID:
+	case WSA881X_TEMP_MSB:
+	case WSA881X_TEMP_LSB:
+	case WSA881X_SDM_PDM9_LSB:
+	case WSA881X_SDM_PDM9_MSB:
+	case WSA881X_OTP_CTRL1:
+	case WSA881X_INTR_STATUS:
+	case WSA881X_ATE_TEST_MODE:
+	case WSA881X_PIN_STATUS:
+	case WSA881X_SWR_HM_TEST2:
+	case WSA881X_SPKR_STATUS1:
+	case WSA881X_SPKR_STATUS2:
+	case WSA881X_SPKR_STATUS3:
+	case WSA881X_OTP_REG_0:
+	case WSA881X_OTP_REG_1:
+	case WSA881X_OTP_REG_2:
+	case WSA881X_OTP_REG_3:
+	case WSA881X_OTP_REG_4:
+	case WSA881X_OTP_REG_5:
+	case WSA881X_OTP_REG_31:
+	case WSA881X_TEMP_DOUT_MSB:
+	case WSA881X_TEMP_DOUT_LSB:
+	case WSA881X_TEMP_OP:
+	case WSA881X_SPKR_PROT_SAR:
+		return true;
+	default:
+		return false;
+	}
+}
+
+struct regmap_config wsa881x_regmap_config = {
+	.reg_bits = 16,
+	.val_bits = 8,
+	.cache_type = REGCACHE_RBTREE,
+	.reg_defaults = wsa881x_defaults,
+	.num_reg_defaults = ARRAY_SIZE(wsa881x_defaults),
+	.max_register = WSA881X_MAX_REGISTER,
+	.volatile_reg = wsa881x_volatile_register,
+	.readable_reg = wsa881x_readable_register,
+	.reg_format_endian = REGMAP_ENDIAN_NATIVE,
+	.val_format_endian = REGMAP_ENDIAN_NATIVE,
+	.can_multi_write = true,
+};
diff --git a/sound/soc/codecs/wsa881x-tables-analog.c b/sound/soc/codecs/wsa881x-tables-analog.c
new file mode 100644
index 0000000..061ed6f
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-tables-analog.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers-analog.h"
+
+const u8 wsa881x_ana_reg_readable[WSA881X_CACHE_SIZE] = {
+	[WSA881X_CHIP_ID0] = 1,
+	[WSA881X_CHIP_ID1] = 1,
+	[WSA881X_CHIP_ID2] = 1,
+	[WSA881X_CHIP_ID3] = 1,
+	[WSA881X_BUS_ID] = 1,
+	[WSA881X_CDC_RST_CTL] = 1,
+	[WSA881X_CDC_TOP_CLK_CTL] = 1,
+	[WSA881X_CDC_ANA_CLK_CTL] = 1,
+	[WSA881X_CDC_DIG_CLK_CTL] = 1,
+	[WSA881X_CLOCK_CONFIG] = 1,
+	[WSA881X_ANA_CTL] = 1,
+	[WSA881X_SWR_RESET_EN] = 1,
+	[WSA881X_RESET_CTL] = 1,
+	[WSA881X_TADC_VALUE_CTL] = 1,
+	[WSA881X_TEMP_DETECT_CTL] = 1,
+	[WSA881X_TEMP_MSB] = 1,
+	[WSA881X_TEMP_LSB] = 1,
+	[WSA881X_TEMP_CONFIG0] = 1,
+	[WSA881X_TEMP_CONFIG1] = 1,
+	[WSA881X_CDC_CLIP_CTL] = 1,
+	[WSA881X_SDM_PDM9_LSB] = 1,
+	[WSA881X_SDM_PDM9_MSB] = 1,
+	[WSA881X_CDC_RX_CTL] = 1,
+	[WSA881X_DEM_BYPASS_DATA0] = 1,
+	[WSA881X_DEM_BYPASS_DATA1] = 1,
+	[WSA881X_DEM_BYPASS_DATA2] = 1,
+	[WSA881X_DEM_BYPASS_DATA3] = 1,
+	[WSA881X_OTP_CTRL0] = 1,
+	[WSA881X_OTP_CTRL1] = 1,
+	[WSA881X_HDRIVE_CTL_GROUP1] = 1,
+	[WSA881X_INTR_MODE] = 1,
+	[WSA881X_INTR_MASK] = 1,
+	[WSA881X_INTR_STATUS] = 1,
+	[WSA881X_INTR_CLEAR] = 1,
+	[WSA881X_INTR_LEVEL] = 1,
+	[WSA881X_INTR_SET] = 1,
+	[WSA881X_INTR_TEST] = 1,
+	[WSA881X_PDM_TEST_MODE] = 1,
+	[WSA881X_ATE_TEST_MODE] = 1,
+	[WSA881X_PIN_CTL_MODE] = 1,
+	[WSA881X_PIN_CTL_OE] = 1,
+	[WSA881X_PIN_WDATA_IOPAD] = 1,
+	[WSA881X_PIN_STATUS] = 1,
+	[WSA881X_DIG_DEBUG_MODE] = 1,
+	[WSA881X_DIG_DEBUG_SEL] = 1,
+	[WSA881X_DIG_DEBUG_EN] = 1,
+	[WSA881X_SWR_HM_TEST1] = 1,
+	[WSA881X_SWR_HM_TEST2] = 1,
+	[WSA881X_TEMP_DETECT_DBG_CTL] = 1,
+	[WSA881X_TEMP_DEBUG_MSB] = 1,
+	[WSA881X_TEMP_DEBUG_LSB] = 1,
+	[WSA881X_SAMPLE_EDGE_SEL] = 1,
+	[WSA881X_IOPAD_CTL] = 1,
+	[WSA881X_SPARE_0] = 1,
+	[WSA881X_SPARE_1] = 1,
+	[WSA881X_SPARE_2] = 1,
+	[WSA881X_OTP_REG_0] = 1,
+	[WSA881X_OTP_REG_1] = 1,
+	[WSA881X_OTP_REG_2] = 1,
+	[WSA881X_OTP_REG_3] = 1,
+	[WSA881X_OTP_REG_4] = 1,
+	[WSA881X_OTP_REG_5] = 1,
+	[WSA881X_OTP_REG_6] = 1,
+	[WSA881X_OTP_REG_7] = 1,
+	[WSA881X_OTP_REG_8] = 1,
+	[WSA881X_OTP_REG_9] = 1,
+	[WSA881X_OTP_REG_10] = 1,
+	[WSA881X_OTP_REG_11] = 1,
+	[WSA881X_OTP_REG_12] = 1,
+	[WSA881X_OTP_REG_13] = 1,
+	[WSA881X_OTP_REG_14] = 1,
+	[WSA881X_OTP_REG_15] = 1,
+	[WSA881X_OTP_REG_16] = 1,
+	[WSA881X_OTP_REG_17] = 1,
+	[WSA881X_OTP_REG_18] = 1,
+	[WSA881X_OTP_REG_19] = 1,
+	[WSA881X_OTP_REG_20] = 1,
+	[WSA881X_OTP_REG_21] = 1,
+	[WSA881X_OTP_REG_22] = 1,
+	[WSA881X_OTP_REG_23] = 1,
+	[WSA881X_OTP_REG_24] = 1,
+	[WSA881X_OTP_REG_25] = 1,
+	[WSA881X_OTP_REG_26] = 1,
+	[WSA881X_OTP_REG_27] = 1,
+	[WSA881X_OTP_REG_28] = 1,
+	[WSA881X_OTP_REG_29] = 1,
+	[WSA881X_OTP_REG_30] = 1,
+	[WSA881X_OTP_REG_31] = 1,
+	[WSA881X_OTP_REG_63] = 1,
+	/* Analog Registers */
+	[WSA881X_BIAS_REF_CTRL] = 1,
+	[WSA881X_BIAS_TEST] = 1,
+	[WSA881X_BIAS_BIAS] = 1,
+	[WSA881X_TEMP_OP] = 1,
+	[WSA881X_TEMP_IREF_CTRL] = 1,
+	[WSA881X_TEMP_ISENS_CTRL] = 1,
+	[WSA881X_TEMP_CLK_CTRL] = 1,
+	[WSA881X_TEMP_TEST] = 1,
+	[WSA881X_TEMP_BIAS] = 1,
+	[WSA881X_TEMP_ADC_CTRL] = 1,
+	[WSA881X_TEMP_DOUT_MSB] = 1,
+	[WSA881X_TEMP_DOUT_LSB] = 1,
+	[WSA881X_ADC_EN_MODU_V] = 1,
+	[WSA881X_ADC_EN_MODU_I] = 1,
+	[WSA881X_ADC_EN_DET_TEST_V] = 1,
+	[WSA881X_ADC_EN_DET_TEST_I] = 1,
+	[WSA881X_ADC_SEL_IBIAS] = 1,
+	[WSA881X_ADC_EN_SEL_IBIAS] = 1,
+	[WSA881X_SPKR_DRV_EN] = 1,
+	[WSA881X_SPKR_DRV_GAIN] = 1,
+	[WSA881X_SPKR_DAC_CTL] = 1,
+	[WSA881X_SPKR_DRV_DBG] = 1,
+	[WSA881X_SPKR_PWRSTG_DBG] = 1,
+	[WSA881X_SPKR_OCP_CTL] = 1,
+	[WSA881X_SPKR_CLIP_CTL] = 1,
+	[WSA881X_SPKR_BBM_CTL] = 1,
+	[WSA881X_SPKR_MISC_CTL1] = 1,
+	[WSA881X_SPKR_MISC_CTL2] = 1,
+	[WSA881X_SPKR_BIAS_INT] = 1,
+	[WSA881X_SPKR_PA_INT] = 1,
+	[WSA881X_SPKR_BIAS_CAL] = 1,
+	[WSA881X_SPKR_BIAS_PSRR] = 1,
+	[WSA881X_SPKR_STATUS1] = 1,
+	[WSA881X_SPKR_STATUS2] = 1,
+	[WSA881X_BOOST_EN_CTL] = 1,
+	[WSA881X_BOOST_CURRENT_LIMIT] = 1,
+	[WSA881X_BOOST_PS_CTL] = 1,
+	[WSA881X_BOOST_PRESET_OUT1] = 1,
+	[WSA881X_BOOST_PRESET_OUT2] = 1,
+	[WSA881X_BOOST_FORCE_OUT] = 1,
+	[WSA881X_BOOST_LDO_PROG] = 1,
+	[WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1,
+	[WSA881X_BOOST_RON_CTL] = 1,
+	[WSA881X_BOOST_LOOP_STABILITY] = 1,
+	[WSA881X_BOOST_ZX_CTL] = 1,
+	[WSA881X_BOOST_START_CTL] = 1,
+	[WSA881X_BOOST_MISC1_CTL] = 1,
+	[WSA881X_BOOST_MISC2_CTL] = 1,
+	[WSA881X_BOOST_MISC3_CTL] = 1,
+	[WSA881X_BOOST_ATEST_CTL] = 1,
+	[WSA881X_SPKR_PROT_FE_GAIN] = 1,
+	[WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1,
+	[WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1,
+	[WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1,
+	[WSA881X_SPKR_PROT_ATEST1] = 1,
+	[WSA881X_SPKR_PROT_ATEST2] = 1,
+	[WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1,
+	[WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1,
+	[WSA881X_BONGO_RESRV_REG1] = 1,
+	[WSA881X_BONGO_RESRV_REG2] = 1,
+	[WSA881X_SPKR_PROT_SAR] = 1,
+	[WSA881X_SPKR_STATUS3] = 1,
+};
diff --git a/sound/soc/codecs/wsa881x-tables.c b/sound/soc/codecs/wsa881x-tables.c
new file mode 100644
index 0000000..4f1212b
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-tables.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/regmap.h>
+#include <linux/device.h>
+#include "wsa881x-registers.h"
+
+const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE] = {
+	[WSA881X_CHIP_ID0] = 1,
+	[WSA881X_CHIP_ID1] = 1,
+	[WSA881X_CHIP_ID2] = 1,
+	[WSA881X_CHIP_ID3] = 1,
+	[WSA881X_BUS_ID] = 1,
+	[WSA881X_CDC_RST_CTL] = 1,
+	[WSA881X_CDC_TOP_CLK_CTL] = 1,
+	[WSA881X_CDC_ANA_CLK_CTL] = 1,
+	[WSA881X_CDC_DIG_CLK_CTL] = 1,
+	[WSA881X_CLOCK_CONFIG] = 1,
+	[WSA881X_ANA_CTL] = 1,
+	[WSA881X_SWR_RESET_EN] = 1,
+	[WSA881X_RESET_CTL] = 1,
+	[WSA881X_TADC_VALUE_CTL] = 1,
+	[WSA881X_TEMP_DETECT_CTL] = 1,
+	[WSA881X_TEMP_MSB] = 1,
+	[WSA881X_TEMP_LSB] = 1,
+	[WSA881X_TEMP_CONFIG0] = 1,
+	[WSA881X_TEMP_CONFIG1] = 1,
+	[WSA881X_CDC_CLIP_CTL] = 1,
+	[WSA881X_SDM_PDM9_LSB] = 1,
+	[WSA881X_SDM_PDM9_MSB] = 1,
+	[WSA881X_CDC_RX_CTL] = 1,
+	[WSA881X_DEM_BYPASS_DATA0] = 1,
+	[WSA881X_DEM_BYPASS_DATA1] = 1,
+	[WSA881X_DEM_BYPASS_DATA2] = 1,
+	[WSA881X_DEM_BYPASS_DATA3] = 1,
+	[WSA881X_OTP_CTRL0] = 1,
+	[WSA881X_OTP_CTRL1] = 1,
+	[WSA881X_HDRIVE_CTL_GROUP1] = 1,
+	[WSA881X_INTR_MODE] = 1,
+	[WSA881X_INTR_MASK] = 1,
+	[WSA881X_INTR_STATUS] = 1,
+	[WSA881X_INTR_CLEAR] = 1,
+	[WSA881X_INTR_LEVEL] = 1,
+	[WSA881X_INTR_SET] = 1,
+	[WSA881X_INTR_TEST] = 1,
+	[WSA881X_PDM_TEST_MODE] = 1,
+	[WSA881X_ATE_TEST_MODE] = 1,
+	[WSA881X_PIN_CTL_MODE] = 1,
+	[WSA881X_PIN_CTL_OE] = 1,
+	[WSA881X_PIN_WDATA_IOPAD] = 1,
+	[WSA881X_PIN_STATUS] = 1,
+	[WSA881X_DIG_DEBUG_MODE] = 1,
+	[WSA881X_DIG_DEBUG_SEL] = 1,
+	[WSA881X_DIG_DEBUG_EN] = 1,
+	[WSA881X_SWR_HM_TEST1] = 1,
+	[WSA881X_SWR_HM_TEST2] = 1,
+	[WSA881X_TEMP_DETECT_DBG_CTL] = 1,
+	[WSA881X_TEMP_DEBUG_MSB] = 1,
+	[WSA881X_TEMP_DEBUG_LSB] = 1,
+	[WSA881X_SAMPLE_EDGE_SEL] = 1,
+	[WSA881X_IOPAD_CTL] = 1,
+	[WSA881X_SPARE_0] = 1,
+	[WSA881X_SPARE_1] = 1,
+	[WSA881X_SPARE_2] = 1,
+	[WSA881X_OTP_REG_0] = 1,
+	[WSA881X_OTP_REG_1] = 1,
+	[WSA881X_OTP_REG_2] = 1,
+	[WSA881X_OTP_REG_3] = 1,
+	[WSA881X_OTP_REG_4] = 1,
+	[WSA881X_OTP_REG_5] = 1,
+	[WSA881X_OTP_REG_6] = 1,
+	[WSA881X_OTP_REG_7] = 1,
+	[WSA881X_OTP_REG_8] = 1,
+	[WSA881X_OTP_REG_9] = 1,
+	[WSA881X_OTP_REG_10] = 1,
+	[WSA881X_OTP_REG_11] = 1,
+	[WSA881X_OTP_REG_12] = 1,
+	[WSA881X_OTP_REG_13] = 1,
+	[WSA881X_OTP_REG_14] = 1,
+	[WSA881X_OTP_REG_15] = 1,
+	[WSA881X_OTP_REG_16] = 1,
+	[WSA881X_OTP_REG_17] = 1,
+	[WSA881X_OTP_REG_18] = 1,
+	[WSA881X_OTP_REG_19] = 1,
+	[WSA881X_OTP_REG_20] = 1,
+	[WSA881X_OTP_REG_21] = 1,
+	[WSA881X_OTP_REG_22] = 1,
+	[WSA881X_OTP_REG_23] = 1,
+	[WSA881X_OTP_REG_24] = 1,
+	[WSA881X_OTP_REG_25] = 1,
+	[WSA881X_OTP_REG_26] = 1,
+	[WSA881X_OTP_REG_27] = 1,
+	[WSA881X_OTP_REG_28] = 1,
+	[WSA881X_OTP_REG_29] = 1,
+	[WSA881X_OTP_REG_30] = 1,
+	[WSA881X_OTP_REG_31] = 1,
+	[WSA881X_OTP_REG_63] = 1,
+	/* Analog Registers */
+	[WSA881X_BIAS_REF_CTRL] = 1,
+	[WSA881X_BIAS_TEST] = 1,
+	[WSA881X_BIAS_BIAS] = 1,
+	[WSA881X_TEMP_OP] = 1,
+	[WSA881X_TEMP_IREF_CTRL] = 1,
+	[WSA881X_TEMP_ISENS_CTRL] = 1,
+	[WSA881X_TEMP_CLK_CTRL] = 1,
+	[WSA881X_TEMP_TEST] = 1,
+	[WSA881X_TEMP_BIAS] = 1,
+	[WSA881X_TEMP_ADC_CTRL] = 1,
+	[WSA881X_TEMP_DOUT_MSB] = 1,
+	[WSA881X_TEMP_DOUT_LSB] = 1,
+	[WSA881X_ADC_EN_MODU_V] = 1,
+	[WSA881X_ADC_EN_MODU_I] = 1,
+	[WSA881X_ADC_EN_DET_TEST_V] = 1,
+	[WSA881X_ADC_EN_DET_TEST_I] = 1,
+	[WSA881X_ADC_SEL_IBIAS] = 1,
+	[WSA881X_ADC_EN_SEL_IBAIS] = 1,
+	[WSA881X_SPKR_DRV_EN] = 1,
+	[WSA881X_SPKR_DRV_GAIN] = 1,
+	[WSA881X_SPKR_DAC_CTL] = 1,
+	[WSA881X_SPKR_DRV_DBG] = 1,
+	[WSA881X_SPKR_PWRSTG_DBG] = 1,
+	[WSA881X_SPKR_OCP_CTL] = 1,
+	[WSA881X_SPKR_CLIP_CTL] = 1,
+	[WSA881X_SPKR_BBM_CTL] = 1,
+	[WSA881X_SPKR_MISC_CTL1] = 1,
+	[WSA881X_SPKR_MISC_CTL2] = 1,
+	[WSA881X_SPKR_BIAS_INT] = 1,
+	[WSA881X_SPKR_PA_INT] = 1,
+	[WSA881X_SPKR_BIAS_CAL] = 1,
+	[WSA881X_SPKR_BIAS_PSRR] = 1,
+	[WSA881X_SPKR_STATUS1] = 1,
+	[WSA881X_SPKR_STATUS2] = 1,
+	[WSA881X_BOOST_EN_CTL] = 1,
+	[WSA881X_BOOST_CURRENT_LIMIT] = 1,
+	[WSA881X_BOOST_PS_CTL] = 1,
+	[WSA881X_BOOST_PRESET_OUT1] = 1,
+	[WSA881X_BOOST_PRESET_OUT2] = 1,
+	[WSA881X_BOOST_FORCE_OUT] = 1,
+	[WSA881X_BOOST_LDO_PROG] = 1,
+	[WSA881X_BOOST_SLOPE_COMP_ISENSE_FB] = 1,
+	[WSA881X_BOOST_RON_CTL] = 1,
+	[WSA881X_BOOST_LOOP_STABILITY] = 1,
+	[WSA881X_BOOST_ZX_CTL] = 1,
+	[WSA881X_BOOST_START_CTL] = 1,
+	[WSA881X_BOOST_MISC1_CTL] = 1,
+	[WSA881X_BOOST_MISC2_CTL] = 1,
+	[WSA881X_BOOST_MISC3_CTL] = 1,
+	[WSA881X_BOOST_ATEST_CTL] = 1,
+	[WSA881X_SPKR_PROT_FE_GAIN] = 1,
+	[WSA881X_SPKR_PROT_FE_CM_LDO_SET] = 1,
+	[WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET1] = 1,
+	[WSA881X_SPKR_PROT_FE_ISENSE_BIAS_SET2] = 1,
+	[WSA881X_SPKR_PROT_ATEST1] = 1,
+	[WSA881X_SPKR_PROT_ATEST2] = 1,
+	[WSA881X_SPKR_PROT_FE_VSENSE_VCM] = 1,
+	[WSA881X_SPKR_PROT_FE_VSENSE_BIAS_SET1] = 1,
+	[WSA881X_BONGO_RESRV_REG1] = 1,
+	[WSA881X_BONGO_RESRV_REG2] = 1,
+	[WSA881X_SPKR_PROT_SAR] = 1,
+	[WSA881X_SPKR_STATUS3] = 1,
+};
diff --git a/sound/soc/codecs/wsa881x-temp-sensor.c b/sound/soc/codecs/wsa881x-temp-sensor.c
new file mode 100644
index 0000000..0079d0f
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-temp-sensor.c
@@ -0,0 +1,143 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/thermal.h>
+#include <sound/soc.h>
+#include "wsa881x-temp-sensor.h"
+
+#define T1_TEMP -10
+#define T2_TEMP 150
+#define LOW_TEMP_THRESHOLD 5
+#define HIGH_TEMP_THRESHOLD 45
+#define TEMP_INVALID	0xFFFF
+
+/*
+ * wsa881x_get_temp - get wsa temperature
+ * @thermal: thermal zone device
+ * @temp: temperature value
+ *
+ * Get the temperature of wsa881x.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int wsa881x_get_temp(struct thermal_zone_device *thermal,
+		     int *temp)
+{
+	struct wsa881x_tz_priv *pdata;
+	struct snd_soc_codec *codec;
+	struct wsa_temp_register reg;
+	int dmeas, d1, d2;
+	int ret = 0;
+	int temp_val;
+	int t1 = T1_TEMP;
+	int t2 = T2_TEMP;
+
+	if (!thermal)
+		return -EINVAL;
+
+	if (thermal->devdata) {
+		pdata = thermal->devdata;
+		if (pdata->codec) {
+			codec = pdata->codec;
+		} else {
+			pr_err("%s: codec is NULL\n", __func__);
+			return -EINVAL;
+		}
+	} else {
+		pr_err("%s: pdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (pdata->wsa_temp_reg_read) {
+		ret = pdata->wsa_temp_reg_read(codec, &reg);
+		if (ret) {
+			pr_err("%s: temperature register read failed: %d\n",
+				__func__, ret);
+			return ret;
+		}
+	} else {
+		pr_err("%s: wsa_temp_reg_read is NULL\n", __func__);
+		return -EINVAL;
+	}
+	/*
+	 * Temperature register values are expected to be in the
+	 * following range.
+	 * d1_msb  = 68 - 92 and d1_lsb  = 0, 64, 128, 192
+	 * d2_msb  = 185 -218 and  d2_lsb  = 0, 64, 128, 192
+	 */
+	if ((reg.d1_msb < 68 || reg.d1_msb > 92) ||
+	    (!(reg.d1_lsb == 0 || reg.d1_lsb == 64 || reg.d1_lsb == 128 ||
+		reg.d1_lsb == 192)) ||
+	    (reg.d2_msb < 185 || reg.d2_msb > 218) ||
+	    (!(reg.d2_lsb == 0 || reg.d2_lsb == 64 || reg.d2_lsb == 128 ||
+		reg.d2_lsb == 192))) {
+		printk_ratelimited("%s: Temperature registers[%d %d %d %d] are out of range\n",
+				   __func__, reg.d1_msb, reg.d1_lsb, reg.d2_msb,
+				   reg.d2_lsb);
+	}
+	dmeas = ((reg.dmeas_msb << 0x8) | reg.dmeas_lsb) >> 0x6;
+	d1 = ((reg.d1_msb << 0x8) | reg.d1_lsb) >> 0x6;
+	d2 = ((reg.d2_msb << 0x8) | reg.d2_lsb) >> 0x6;
+
+	if (d1 == d2)
+		temp_val = TEMP_INVALID;
+	else
+		temp_val = t1 + (((dmeas - d1) * (t2 - t1))/(d2 - d1));
+
+	if (temp_val <= LOW_TEMP_THRESHOLD ||
+		temp_val >= HIGH_TEMP_THRESHOLD) {
+		printk_ratelimited("%s: T0: %d is out of range[%d, %d]\n",
+				   __func__, temp_val, LOW_TEMP_THRESHOLD,
+				   HIGH_TEMP_THRESHOLD);
+	}
+	if (temp)
+		*temp = temp_val;
+	pr_debug("%s: t0 measured: %d dmeas = %d, d1 = %d, d2 = %d\n",
+		  __func__, temp_val, dmeas, d1, d2);
+	return ret;
+}
+EXPORT_SYMBOL(wsa881x_get_temp);
+
+static struct thermal_zone_device_ops wsa881x_thermal_ops = {
+	.get_temp = wsa881x_get_temp,
+};
+
+int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata)
+{
+	struct thermal_zone_device *tz_dev;
+
+	if (tz_pdata == NULL) {
+		pr_err("%s: thermal pdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+	/* Register with the thermal zone */
+	tz_dev = thermal_zone_device_register(tz_pdata->name,
+				0, 0, tz_pdata,
+				&wsa881x_thermal_ops, NULL, 0, 0);
+	if (IS_ERR(tz_dev)) {
+		pr_err("%s: thermal device register failed.\n", __func__);
+		return -EINVAL;
+	}
+	tz_pdata->tz_dev = tz_dev;
+	return 0;
+}
+EXPORT_SYMBOL(wsa881x_init_thermal);
+
+void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev)
+{
+	if (tz_dev)
+		thermal_zone_device_unregister(tz_dev);
+}
+EXPORT_SYMBOL(wsa881x_deinit_thermal);
diff --git a/sound/soc/codecs/wsa881x-temp-sensor.h b/sound/soc/codecs/wsa881x-temp-sensor.h
new file mode 100644
index 0000000..d6c1eb7
--- /dev/null
+++ b/sound/soc/codecs/wsa881x-temp-sensor.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef WSA881X_TEMP_SENSOR_H
+#define WSA881X_TEMP_SENSOR_H
+
+#include <linux/thermal.h>
+#include <sound/soc.h>
+
+struct wsa_temp_register {
+	u8 d1_msb;
+	u8 d1_lsb;
+	u8 d2_msb;
+	u8 d2_lsb;
+	u8 dmeas_msb;
+	u8 dmeas_lsb;
+};
+typedef int32_t (*wsa_temp_register_read)(struct snd_soc_codec *codec,
+					struct wsa_temp_register *wsa_temp_reg);
+struct wsa881x_tz_priv {
+	struct thermal_zone_device *tz_dev;
+	struct snd_soc_codec *codec;
+	struct wsa_temp_register *wsa_temp_reg;
+	char name[80];
+	wsa_temp_register_read wsa_temp_reg_read;
+};
+
+int wsa881x_get_temp(struct thermal_zone_device *tz_dev, int *temp);
+int wsa881x_init_thermal(struct wsa881x_tz_priv *tz_pdata);
+void wsa881x_deinit_thermal(struct thermal_zone_device *tz_dev);
+#endif
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
new file mode 100644
index 0000000..e689570
--- /dev/null
+++ b/sound/soc/codecs/wsa881x.c
@@ -0,0 +1,1406 @@
+/*
+ * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <linux/debugfs.h>
+#include <linux/soundwire/soundwire.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include "wsa881x.h"
+#include "wsa881x-temp-sensor.h"
+
+#define WSA881X_NUM_RETRY	5
+
+enum {
+	G_18DB = 0,
+	G_16P5DB,
+	G_15DB,
+	G_13P5DB,
+	G_12DB,
+	G_10P5DB,
+	G_9DB,
+	G_7P5DB,
+	G_6DB,
+	G_4P5DB,
+	G_3DB,
+	G_1P5DB,
+	G_0DB,
+};
+
+enum {
+	DISABLE = 0,
+	ENABLE,
+};
+
+enum {
+	SWR_DAC_PORT,
+	SWR_COMP_PORT,
+	SWR_BOOST_PORT,
+	SWR_VISENSE_PORT,
+};
+
+struct swr_port {
+	u8 port_id;
+	u8 ch_mask;
+	u32 ch_rate;
+	u8 num_ch;
+};
+
+enum {
+	WSA881X_DEV_DOWN,
+	WSA881X_DEV_UP,
+};
+
+/*
+ * Private data Structure for wsa881x. All parameters related to
+ * WSA881X codec needs to be defined here.
+ */
+struct wsa881x_priv {
+	struct regmap *regmap;
+	struct device *dev;
+	struct swr_device *swr_slave;
+	struct snd_soc_codec *codec;
+	bool comp_enable;
+	bool boost_enable;
+	bool visense_enable;
+	u8 pa_gain;
+	struct swr_port port[WSA881X_MAX_SWR_PORTS];
+	int pd_gpio;
+	struct wsa881x_tz_priv tz_pdata;
+	int bg_cnt;
+	int clk_cnt;
+	int version;
+	struct mutex bg_lock;
+	struct mutex res_lock;
+	struct snd_info_entry *entry;
+	struct snd_info_entry *version_entry;
+	int state;
+	struct delayed_work ocp_ctl_work;
+	struct device_node *wsa_rst_np;
+};
+
+#define SWR_SLV_MAX_REG_ADDR	0x390
+#define SWR_SLV_START_REG_ADDR	0x40
+#define SWR_SLV_MAX_BUF_LEN	20
+#define BYTES_PER_LINE		12
+#define SWR_SLV_RD_BUF_LEN	8
+#define SWR_SLV_WR_BUF_LEN	32
+#define SWR_SLV_MAX_DEVICES	2
+
+#define WSA881X_VERSION_ENTRY_SIZE 27
+#define WSA881X_OCP_CTL_TIMER_SEC 2
+#define WSA881X_OCP_CTL_TEMP_CELSIUS 25
+#define WSA881X_OCP_CTL_POLL_TIMER_SEC 60
+
+static int wsa881x_ocp_poll_timer_sec = WSA881X_OCP_CTL_POLL_TIMER_SEC;
+module_param(wsa881x_ocp_poll_timer_sec, int, 0664);
+MODULE_PARM_DESC(wsa881x_ocp_poll_timer_sec, "timer for ocp ctl polling");
+
+static struct wsa881x_priv *dbgwsa881x;
+static struct dentry *debugfs_wsa881x_dent;
+static struct dentry *debugfs_peek;
+static struct dentry *debugfs_poke;
+static struct dentry *debugfs_reg_dump;
+static unsigned int read_data;
+static unsigned int devnum;
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+						bool enable);
+
+static const char * const wsa_pa_gain_text[] = {
+	"G_18_DB", "G_16P5_DB", "G_15_DB", "G_13P5_DB", "G_12_DB", "G_10P5_DB",
+	"G_9_DB", "G_7P5_DB", "G_6_DB", "G_4P5_DB", "G_3_DB", "G_1P5_DB",
+	"G_0_DB"
+};
+
+static const struct soc_enum wsa_pa_gain_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa_pa_gain_text), wsa_pa_gain_text);
+
+static int wsa_pa_gain_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->pa_gain;
+
+	dev_dbg(codec->dev, "%s: PA gain = 0x%x\n", __func__, wsa881x->pa_gain);
+
+	return 0;
+}
+
+static int wsa_pa_gain_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: ucontrol->value.integer.value[0]  = %ld\n",
+		__func__, ucontrol->value.integer.value[0]);
+
+	wsa881x->pa_gain =  ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new wsa_analog_gain_controls[] = {
+	SOC_ENUM_EXT("WSA PA Gain", wsa_pa_gain_enum,
+		     wsa_pa_gain_get, wsa_pa_gain_put),
+};
+
+static int codec_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static int get_parameters(char *buf, u32 *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (kstrtou32(token, base, &param1[cnt]) != 0)
+				return -EINVAL;
+
+			token = strsep(&buf, " ");
+		} else
+			return -EINVAL;
+	}
+	return 0;
+}
+
+static ssize_t wsa881x_codec_version_read(struct snd_info_entry *entry,
+			       void *file_private_data, struct file *file,
+			       char __user *buf, size_t count, loff_t pos)
+{
+	struct wsa881x_priv *wsa881x;
+	char buffer[WSA881X_VERSION_ENTRY_SIZE];
+	int len;
+
+	wsa881x = (struct wsa881x_priv *) entry->private_data;
+	if (!wsa881x) {
+		pr_err("%s: wsa881x priv is null\n", __func__);
+		return -EINVAL;
+	}
+
+	len = snprintf(buffer, sizeof(buffer), "WSA881X-SOUNDWIRE_2_0\n");
+
+	return simple_read_from_buffer(buf, count, &pos, buffer, len);
+}
+
+static struct snd_info_entry_ops wsa881x_codec_info_ops = {
+	.read = wsa881x_codec_version_read,
+};
+
+/*
+ * wsa881x_codec_info_create_codec_entry - creates wsa881x module
+ * @codec_root: The parent directory
+ * @codec: Codec instance
+ *
+ * Creates wsa881x module and version entry under the given
+ * parent directory.
+ *
+ * Return: 0 on success or negative error code on failure.
+ */
+int wsa881x_codec_info_create_codec_entry(struct snd_info_entry *codec_root,
+					  struct snd_soc_codec *codec)
+{
+	struct snd_info_entry *version_entry;
+	struct wsa881x_priv *wsa881x;
+	struct snd_soc_card *card;
+	char name[80];
+
+	if (!codec_root || !codec)
+		return -EINVAL;
+
+	wsa881x = snd_soc_codec_get_drvdata(codec);
+	card = codec->component.card;
+	snprintf(name, sizeof(name), "%s.%x", "wsa881x",
+		 (u32)wsa881x->swr_slave->addr);
+
+	wsa881x->entry = snd_info_create_subdir(codec_root->module,
+						(const char *)name,
+						codec_root);
+	if (!wsa881x->entry) {
+		dev_dbg(codec->dev, "%s: failed to create wsa881x entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry = snd_info_create_card_entry(card->snd_card,
+						   "version",
+						   wsa881x->entry);
+	if (!version_entry) {
+		dev_dbg(codec->dev, "%s: failed to create wsa881x version entry\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	version_entry->private_data = wsa881x;
+	version_entry->size = WSA881X_VERSION_ENTRY_SIZE;
+	version_entry->content = SNDRV_INFO_CONTENT_DATA;
+	version_entry->c.ops = &wsa881x_codec_info_ops;
+
+	if (snd_info_register(version_entry) < 0) {
+		snd_info_free_entry(version_entry);
+		return -ENOMEM;
+	}
+	wsa881x->version_entry = version_entry;
+
+	return 0;
+}
+EXPORT_SYMBOL(wsa881x_codec_info_create_codec_entry);
+
+static bool is_swr_slv_reg_readable(int reg)
+{
+	bool ret = true;
+
+	if (((reg > 0x46) && (reg < 0x4A)) ||
+	    ((reg > 0x4A) && (reg < 0x50)) ||
+	    ((reg > 0x55) && (reg < 0xE0)) ||
+	    ((reg > 0xE0) && (reg < 0xF0)) ||
+	    ((reg > 0xF0) && (reg < 0x100)) ||
+	    ((reg > 0x105) && (reg < 0x120)) ||
+	    ((reg > 0x128) && (reg < 0x130)) ||
+	    ((reg > 0x138) && (reg < 0x200)) ||
+	    ((reg > 0x205) && (reg < 0x220)) ||
+	    ((reg > 0x228) && (reg < 0x230)) ||
+	    ((reg > 0x238) && (reg < 0x300)) ||
+	    ((reg > 0x305) && (reg < 0x320)) ||
+	    ((reg > 0x328) && (reg < 0x330)) ||
+	    ((reg > 0x338) && (reg < 0x400)) ||
+	    ((reg > 0x405) && (reg < 0x420)))
+		ret = false;
+
+	return ret;
+}
+
+static ssize_t wsa881x_swrslave_reg_show(char __user *ubuf, size_t count,
+					  loff_t *ppos)
+{
+	int i, reg_val, len;
+	ssize_t total = 0;
+	char tmp_buf[SWR_SLV_MAX_BUF_LEN];
+
+	if (!ubuf || !ppos || (devnum == 0))
+		return 0;
+
+	for (i = (((int) *ppos / BYTES_PER_LINE) + SWR_SLV_START_REG_ADDR);
+		i <= SWR_SLV_MAX_REG_ADDR; i++) {
+		if (!is_swr_slv_reg_readable(i))
+			continue;
+		swr_read(dbgwsa881x->swr_slave, devnum,
+			i, &reg_val, 1);
+		len = snprintf(tmp_buf, 25, "0x%.3x: 0x%.2x\n", i,
+			       (reg_val & 0xFF));
+		if ((total + len) >= count - 1)
+			break;
+		if (copy_to_user((ubuf + total), tmp_buf, len)) {
+			pr_err("%s: fail to copy reg dump\n", __func__);
+			total = -EFAULT;
+			goto copy_err;
+		}
+		*ppos += len;
+		total += len;
+	}
+
+copy_err:
+	return total;
+}
+
+static ssize_t codec_debug_read(struct file *file, char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	char lbuf[SWR_SLV_RD_BUF_LEN];
+	char *access_str;
+	ssize_t ret_cnt;
+
+	if (!count || !file || !ppos || !ubuf)
+		return -EINVAL;
+
+	access_str = file->private_data;
+	if (*ppos < 0)
+		return -EINVAL;
+
+	if (!strcmp(access_str, "swrslave_peek")) {
+		snprintf(lbuf, sizeof(lbuf), "0x%x\n", (read_data & 0xFF));
+		ret_cnt = simple_read_from_buffer(ubuf, count, ppos, lbuf,
+					       strnlen(lbuf, 7));
+	} else if (!strcmp(access_str, "swrslave_reg_dump")) {
+		ret_cnt = wsa881x_swrslave_reg_show(ubuf, count, ppos);
+	} else {
+		pr_err("%s: %s not permitted to read\n", __func__, access_str);
+		ret_cnt = -EPERM;
+	}
+	return ret_cnt;
+}
+
+static ssize_t codec_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char lbuf[SWR_SLV_WR_BUF_LEN];
+	int rc;
+	u32 param[5];
+	char *access_str;
+
+	if (!filp || !ppos || !ubuf)
+		return -EINVAL;
+
+	access_str = filp->private_data;
+	if (cnt > sizeof(lbuf) - 1)
+		return -EINVAL;
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc)
+		return -EFAULT;
+
+	lbuf[cnt] = '\0';
+	if (!strcmp(access_str, "swrslave_poke")) {
+		/* write */
+		rc = get_parameters(lbuf, param, 3);
+		if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (param[1] <= 0xFF) &&
+			(rc == 0))
+			swr_write(dbgwsa881x->swr_slave, param[2],
+				param[0], &param[1]);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "swrslave_peek")) {
+		/* read */
+		rc = get_parameters(lbuf, param, 2);
+		if ((param[0] <= SWR_SLV_MAX_REG_ADDR) && (rc == 0))
+			swr_read(dbgwsa881x->swr_slave, param[1],
+				param[0], &read_data, 1);
+		else
+			rc = -EINVAL;
+	} else if (!strcmp(access_str, "swrslave_reg_dump")) {
+		/* reg dump */
+		rc = get_parameters(lbuf, param, 1);
+		if ((rc == 0) && (param[0] > 0) &&
+		    (param[0] <= SWR_SLV_MAX_DEVICES))
+			devnum = param[0];
+		else
+			rc = -EINVAL;
+	}
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations codec_debug_ops = {
+	.open = codec_debug_open,
+	.write = codec_debug_write,
+	.read = codec_debug_read,
+};
+
+static const struct reg_sequence wsa881x_pre_pmu_pa[] = {
+	{WSA881X_SPKR_DRV_GAIN, 0x41, 0},
+	{WSA881X_SPKR_MISC_CTL1, 0x01, 0},
+	{WSA881X_ADC_EN_DET_TEST_I, 0x01, 0},
+	{WSA881X_ADC_EN_MODU_V, 0x02, 0},
+	{WSA881X_ADC_EN_DET_TEST_V, 0x10, 0},
+	{WSA881X_SPKR_PWRSTG_DBG, 0xA0, 0},
+};
+
+static const struct reg_sequence wsa881x_pre_pmu_pa_2_0[] = {
+	{WSA881X_SPKR_DRV_GAIN, 0x41, 0},
+	{WSA881X_SPKR_MISC_CTL1, 0x87, 0},
+};
+
+static const struct reg_sequence wsa881x_post_pmu_pa[] = {
+	{WSA881X_SPKR_PWRSTG_DBG, 0x00, 0},
+	{WSA881X_ADC_EN_DET_TEST_V, 0x00, 0},
+	{WSA881X_ADC_EN_MODU_V, 0x00, 0},
+	{WSA881X_ADC_EN_DET_TEST_I, 0x00, 0},
+};
+
+static const struct reg_sequence wsa881x_vi_txfe_en[] = {
+	{WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0},
+	{WSA881X_SPKR_PROT_ATEST2, 0x0A, 0},
+	{WSA881X_SPKR_PROT_FE_GAIN, 0xCF, 0},
+};
+
+static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = {
+	{WSA881X_SPKR_PROT_FE_VSENSE_VCM, 0x85, 0},
+	{WSA881X_SPKR_PROT_ATEST2, 0x0A, 0},
+	{WSA881X_SPKR_PROT_FE_GAIN, 0x47, 0},
+};
+
+static int wsa881x_boost_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable);
+	if (enable)
+		snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x80);
+	else
+		snd_soc_update_bits(codec, WSA881X_BOOST_EN_CTL, 0x80, 0x00);
+	/*
+	 * 1.5ms sleep is needed after boost enable/disable as per
+	 * HW requirement
+	 */
+	usleep_range(1500, 1510);
+	return 0;
+}
+
+static int wsa881x_visense_txfe_ctrl(struct snd_soc_codec *codec, bool enable,
+				     u8 isense1_gain, u8 isense2_gain,
+				     u8 vsense_gain)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev,
+		"%s: enable:%d, isense1 gain: %d, isense2 gain: %d, vsense_gain %d\n",
+		__func__, enable, isense1_gain, isense2_gain, vsense_gain);
+
+	if (enable) {
+		regmap_multi_reg_write(wsa881x->regmap,
+				wsa881x_vi_txfe_en_2_0,
+				ARRAY_SIZE(wsa881x_vi_txfe_en_2_0));
+	} else {
+		snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_VSENSE_VCM,
+				    0x08, 0x08);
+		/*
+		 * 200us sleep is needed after visense txfe disable as per
+		 * HW requirement.
+		 */
+		usleep_range(200, 210);
+		snd_soc_update_bits(codec, WSA881X_SPKR_PROT_FE_GAIN,
+				    0x01, 0x00);
+	}
+	return 0;
+}
+
+static int wsa881x_visense_adc_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+
+	dev_dbg(codec->dev, "%s: enable:%d\n", __func__, enable);
+	snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_V, (0x01 << 7),
+			    (enable << 7));
+	snd_soc_update_bits(codec, WSA881X_ADC_EN_MODU_I, (0x01 << 7),
+			    (enable << 7));
+	return 0;
+}
+
+static void wsa881x_bandgap_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: enable:%d, bg_count:%d\n", __func__,
+		enable, wsa881x->bg_cnt);
+	mutex_lock(&wsa881x->bg_lock);
+	if (enable) {
+		++wsa881x->bg_cnt;
+		if (wsa881x->bg_cnt == 1) {
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP,
+					    0x08, 0x08);
+			/* 400usec sleep is needed as per HW requirement */
+			usleep_range(400, 410);
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP,
+					    0x04, 0x04);
+		}
+	} else {
+		--wsa881x->bg_cnt;
+		if (wsa881x->bg_cnt <= 0) {
+			WARN_ON(wsa881x->bg_cnt < 0);
+			wsa881x->bg_cnt = 0;
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x04, 0x00);
+			snd_soc_update_bits(codec, WSA881X_TEMP_OP, 0x08, 0x00);
+		}
+	}
+	mutex_unlock(&wsa881x->bg_lock);
+}
+
+static void wsa881x_clk_ctrl(struct snd_soc_codec *codec, bool enable)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: enable:%d, clk_count:%d\n", __func__,
+		enable, wsa881x->clk_cnt);
+	mutex_lock(&wsa881x->res_lock);
+	if (enable) {
+		++wsa881x->clk_cnt;
+		if (wsa881x->clk_cnt == 1) {
+			snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x01);
+			snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x01);
+		}
+	} else {
+		--wsa881x->clk_cnt;
+		if (wsa881x->clk_cnt <= 0) {
+			WARN_ON(wsa881x->clk_cnt < 0);
+			wsa881x->clk_cnt = 0;
+			snd_soc_write(codec, WSA881X_CDC_DIG_CLK_CTL, 0x00);
+			snd_soc_write(codec, WSA881X_CDC_ANA_CLK_CTL, 0x00);
+		}
+	}
+	mutex_unlock(&wsa881x->res_lock);
+}
+
+static int wsa881x_get_compander(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->comp_enable;
+	return 0;
+}
+
+static int wsa881x_set_compander(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int value = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: Compander enable current %d, new %d\n",
+		 __func__, wsa881x->comp_enable, value);
+	wsa881x->comp_enable = value;
+	return 0;
+}
+
+static int wsa881x_get_boost(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->boost_enable;
+	return 0;
+}
+
+static int wsa881x_set_boost(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int value = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: Boost enable current %d, new %d\n",
+		 __func__, wsa881x->boost_enable, value);
+	wsa881x->boost_enable = value;
+	return 0;
+}
+
+static int wsa881x_get_visense(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	ucontrol->value.integer.value[0] = wsa881x->visense_enable;
+	return 0;
+}
+
+static int wsa881x_set_visense(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int value = ucontrol->value.integer.value[0];
+
+	dev_dbg(codec->dev, "%s: VIsense enable current %d, new %d\n",
+		 __func__, wsa881x->visense_enable, value);
+	wsa881x->visense_enable = value;
+	return 0;
+}
+
+static const struct snd_kcontrol_new wsa881x_snd_controls[] = {
+	SOC_SINGLE_EXT("COMP Switch", SND_SOC_NOPM, 0, 1, 0,
+		wsa881x_get_compander, wsa881x_set_compander),
+
+	SOC_SINGLE_EXT("BOOST Switch", SND_SOC_NOPM, 0, 1, 0,
+		wsa881x_get_boost, wsa881x_set_boost),
+
+	SOC_SINGLE_EXT("VISENSE Switch", SND_SOC_NOPM, 0, 1, 0,
+		wsa881x_get_visense, wsa881x_set_visense),
+};
+
+static const struct snd_kcontrol_new swr_dac_port[] = {
+	SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0)
+};
+
+static int wsa881x_set_port(struct snd_soc_codec *codec, int port_idx,
+			u8 *port_id, u8 *num_ch, u8 *ch_mask, u32 *ch_rate)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	*port_id = wsa881x->port[port_idx].port_id;
+	*num_ch = wsa881x->port[port_idx].num_ch;
+	*ch_mask = wsa881x->port[port_idx].ch_mask;
+	*ch_rate = wsa881x->port[port_idx].ch_rate;
+	return 0;
+}
+
+static int wsa881x_enable_swr_dac_port(struct snd_soc_dapm_widget *w,
+	struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	u8 port_id[WSA881X_MAX_SWR_PORTS];
+	u8 num_ch[WSA881X_MAX_SWR_PORTS];
+	u8 ch_mask[WSA881X_MAX_SWR_PORTS];
+	u32 ch_rate[WSA881X_MAX_SWR_PORTS];
+	u8 num_port = 0;
+
+	dev_dbg(codec->dev, "%s: event %d name %s\n", __func__,
+		event, w->name);
+	if (wsa881x == NULL)
+		return -EINVAL;
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wsa881x_set_port(codec, SWR_DAC_PORT,
+				&port_id[num_port], &num_ch[num_port],
+				&ch_mask[num_port], &ch_rate[num_port]);
+		++num_port;
+
+		if (wsa881x->comp_enable) {
+			wsa881x_set_port(codec, SWR_COMP_PORT,
+					&port_id[num_port], &num_ch[num_port],
+					&ch_mask[num_port], &ch_rate[num_port]);
+			++num_port;
+		}
+		if (wsa881x->boost_enable) {
+			wsa881x_set_port(codec, SWR_BOOST_PORT,
+					&port_id[num_port], &num_ch[num_port],
+					&ch_mask[num_port], &ch_rate[num_port]);
+			++num_port;
+		}
+		if (wsa881x->visense_enable) {
+			wsa881x_set_port(codec, SWR_VISENSE_PORT,
+					&port_id[num_port], &num_ch[num_port],
+					&ch_mask[num_port], &ch_rate[num_port]);
+			++num_port;
+		}
+		swr_connect_port(wsa881x->swr_slave, &port_id[0], num_port,
+				&ch_mask[0], &ch_rate[0], &num_ch[0]);
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		port_id[num_port] = wsa881x->port[SWR_DAC_PORT].port_id;
+		++num_port;
+		if (wsa881x->comp_enable) {
+			port_id[num_port] =
+				wsa881x->port[SWR_COMP_PORT].port_id;
+			++num_port;
+		}
+		if (wsa881x->boost_enable) {
+			port_id[num_port] =
+				wsa881x->port[SWR_BOOST_PORT].port_id;
+			++num_port;
+		}
+		if (wsa881x->visense_enable) {
+			port_id[num_port] =
+				wsa881x->port[SWR_VISENSE_PORT].port_id;
+			++num_port;
+		}
+		swr_disconnect_port(wsa881x->swr_slave, &port_id[0], num_port);
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int wsa881x_rdac_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	dev_dbg(codec->dev, "%s: %s %d boost %d visense %d\n", __func__,
+		w->name, event,	wsa881x->boost_enable,
+		wsa881x->visense_enable);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		wsa881x_resource_acquire(codec, ENABLE);
+		if (wsa881x->boost_enable)
+			wsa881x_boost_ctrl(codec, ENABLE);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		swr_slvdev_datapath_control(wsa881x->swr_slave,
+					    wsa881x->swr_slave->dev_num,
+					    false);
+		if (wsa881x->boost_enable)
+			wsa881x_boost_ctrl(codec, DISABLE);
+		wsa881x_resource_acquire(codec, DISABLE);
+		break;
+	}
+	return 0;
+}
+
+static int wsa881x_ramp_pa_gain(struct snd_soc_codec *codec,
+				int min_gain, int max_gain, int udelay)
+{
+	int val;
+
+	for (val = min_gain; max_gain <= val; val--) {
+		snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+				    0xF0, val << 4);
+		/*
+		 * 1ms delay is needed for every step change in gain as per
+		 * HW requirement.
+		 */
+		usleep_range(udelay, udelay+10);
+	}
+	return 0;
+}
+
+static void wsa881x_ocp_ctl_work(struct work_struct *work)
+{
+	struct wsa881x_priv *wsa881x;
+	struct delayed_work *dwork;
+	struct snd_soc_codec *codec;
+	int temp_val;
+
+	dwork = to_delayed_work(work);
+	wsa881x = container_of(dwork, struct wsa881x_priv, ocp_ctl_work);
+
+	codec = wsa881x->codec;
+	wsa881x_get_temp(wsa881x->tz_pdata.tz_dev, &temp_val);
+	dev_dbg(codec->dev, " temp = %d\n", temp_val);
+
+	if (temp_val <= WSA881X_OCP_CTL_TEMP_CELSIUS)
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x00);
+	else
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+
+	schedule_delayed_work(&wsa881x->ocp_ctl_work,
+			msecs_to_jiffies(wsa881x_ocp_poll_timer_sec * 1000));
+}
+
+static int wsa881x_spkr_pa_event(struct snd_soc_dapm_widget *w,
+			struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int min_gain, max_gain;
+
+	dev_dbg(codec->dev, "%s: %s %d\n", __func__, w->name, event);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0x80);
+		regmap_multi_reg_write(wsa881x->regmap,
+				wsa881x_pre_pmu_pa_2_0,
+				ARRAY_SIZE(wsa881x_pre_pmu_pa_2_0));
+		swr_slvdev_datapath_control(wsa881x->swr_slave,
+					    wsa881x->swr_slave->dev_num,
+					    true);
+		/* Set register mode if compander is not enabled */
+		if (!wsa881x->comp_enable)
+			snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+					    0x08, 0x08);
+		else
+			snd_soc_update_bits(codec, WSA881X_SPKR_DRV_GAIN,
+					    0x08, 0x00);
+
+		break;
+	case SND_SOC_DAPM_POST_PMU:
+		if (!wsa881x->comp_enable) {
+			max_gain = wsa881x->pa_gain;
+			/*
+			 * Gain has to set incrementally in 4 steps
+			 * as per HW sequence
+			 */
+			if (max_gain > G_4P5DB)
+				min_gain = G_0DB;
+			else
+				min_gain = max_gain + 3;
+			/*
+			 * 1ms delay is needed before change in gain
+			 * as per HW requirement.
+			 */
+			usleep_range(1000, 1010);
+			wsa881x_ramp_pa_gain(codec, min_gain, max_gain, 1000);
+		}
+		if (wsa881x->visense_enable) {
+			wsa881x_visense_txfe_ctrl(codec, ENABLE,
+						0x00, 0x03, 0x01);
+			snd_soc_update_bits(codec, WSA881X_ADC_EN_SEL_IBAIS,
+					    0x07, 0x01);
+			wsa881x_visense_adc_ctrl(codec, ENABLE);
+		}
+		schedule_delayed_work(&wsa881x->ocp_ctl_work,
+			msecs_to_jiffies(WSA881X_OCP_CTL_TIMER_SEC * 1000));
+		/* Force remove group */
+		swr_remove_from_group(wsa881x->swr_slave,
+				      wsa881x->swr_slave->dev_num);
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		if (wsa881x->visense_enable) {
+			wsa881x_visense_adc_ctrl(codec, DISABLE);
+			wsa881x_visense_txfe_ctrl(codec, DISABLE,
+						0x00, 0x01, 0x01);
+		}
+		cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+		snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0xC0, 0xC0);
+		break;
+	}
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget wsa881x_dapm_widgets[] = {
+	SND_SOC_DAPM_INPUT("IN"),
+
+	SND_SOC_DAPM_MIXER_E("SWR DAC_Port", SND_SOC_NOPM, 0, 0, swr_dac_port,
+		ARRAY_SIZE(swr_dac_port), wsa881x_enable_swr_dac_port,
+		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("RDAC", NULL, WSA881X_SPKR_DAC_CTL, 7, 0,
+		wsa881x_rdac_event,
+		SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_PGA_E("SPKR PGA", WSA881X_SPKR_DRV_EN, 7, 0, NULL, 0,
+			wsa881x_spkr_pa_event, SND_SOC_DAPM_PRE_PMU |
+			SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_OUTPUT("SPKR"),
+};
+
+static const struct snd_soc_dapm_route wsa881x_audio_map[] = {
+	{"SWR DAC_Port", "Switch", "IN"},
+	{"RDAC", NULL, "SWR DAC_Port"},
+	{"SPKR PGA", NULL, "RDAC"},
+	{"SPKR", NULL, "SPKR PGA"},
+};
+
+int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port, u8 num_port,
+				unsigned int *ch_mask, unsigned int *ch_rate)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	int i;
+
+	if (!port || !ch_mask || !ch_rate ||
+		(num_port > WSA881X_MAX_SWR_PORTS)) {
+		dev_err(codec->dev,
+			"%s: Invalid port=%pK, ch_mask=%pK, ch_rate=%pK\n",
+			__func__, port, ch_mask, ch_rate);
+		return -EINVAL;
+	}
+	for (i = 0; i < num_port; i++) {
+		wsa881x->port[i].port_id = port[i];
+		wsa881x->port[i].ch_mask = ch_mask[i];
+		wsa881x->port[i].ch_rate = ch_rate[i];
+		wsa881x->port[i].num_ch = __sw_hweight8(ch_mask[i]);
+	}
+	return 0;
+}
+EXPORT_SYMBOL(wsa881x_set_channel_map);
+
+static void wsa881x_init(struct snd_soc_codec *codec)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	wsa881x->version = snd_soc_read(codec, WSA881X_CHIP_ID1);
+	wsa881x_regmap_defaults(wsa881x->regmap, wsa881x->version);
+	/* Bring out of analog reset */
+	snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x02, 0x02);
+	/* Bring out of digital reset */
+	snd_soc_update_bits(codec, WSA881X_CDC_RST_CTL, 0x01, 0x01);
+
+	snd_soc_update_bits(codec, WSA881X_CLOCK_CONFIG, 0x10, 0x10);
+	snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x02, 0x02);
+	snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0xC0, 0x80);
+	snd_soc_update_bits(codec, WSA881X_SPKR_MISC_CTL1, 0x06, 0x06);
+	snd_soc_update_bits(codec, WSA881X_SPKR_BIAS_INT, 0xFF, 0x00);
+	snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0xF0, 0x40);
+	snd_soc_update_bits(codec, WSA881X_SPKR_PA_INT, 0x0E, 0x0E);
+	snd_soc_update_bits(codec, WSA881X_BOOST_LOOP_STABILITY,
+			    0x03, 0x03);
+	snd_soc_update_bits(codec, WSA881X_BOOST_MISC2_CTL, 0xFF, 0x14);
+	snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x80, 0x80);
+	snd_soc_update_bits(codec, WSA881X_BOOST_START_CTL, 0x03, 0x00);
+	snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+			    0x0C, 0x04);
+	snd_soc_update_bits(codec, WSA881X_BOOST_SLOPE_COMP_ISENSE_FB,
+			    0x03, 0x00);
+	if (snd_soc_read(codec, WSA881X_OTP_REG_0))
+		snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT1,
+				    0xF0, 0x70);
+	snd_soc_update_bits(codec, WSA881X_BOOST_PRESET_OUT2,
+			    0xF0, 0x30);
+	snd_soc_update_bits(codec, WSA881X_SPKR_DRV_EN, 0x08, 0x08);
+	snd_soc_update_bits(codec, WSA881X_BOOST_CURRENT_LIMIT,
+			    0x0F, 0x08);
+	snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x30, 0x30);
+	snd_soc_update_bits(codec, WSA881X_SPKR_OCP_CTL, 0x0C, 0x00);
+	snd_soc_update_bits(codec, WSA881X_OTP_REG_28, 0x3F, 0x3A);
+	snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG1,
+			    0xFF, 0xB2);
+	snd_soc_update_bits(codec, WSA881X_BONGO_RESRV_REG2,
+			    0xFF, 0x05);
+}
+
+static int32_t wsa881x_resource_acquire(struct snd_soc_codec *codec,
+						bool enable)
+{
+	wsa881x_clk_ctrl(codec, enable);
+	wsa881x_bandgap_ctrl(codec, enable);
+	return 0;
+}
+
+static int32_t wsa881x_temp_reg_read(struct snd_soc_codec *codec,
+				     struct wsa_temp_register *wsa_temp_reg)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	struct swr_device *dev;
+	u8 devnum = 0;
+
+	if (!wsa881x) {
+		dev_err(codec->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev = wsa881x->swr_slave;
+	if (dev && (wsa881x->state == WSA881X_DEV_DOWN)) {
+		if (swr_get_logical_dev_num(dev, dev->addr, &devnum)) {
+			dev_err(codec->dev,
+				"%s get devnum %d for dev addr %lx failed\n",
+				__func__, devnum, dev->addr);
+			return -EINVAL;
+		}
+	}
+	mutex_lock(&wsa881x->res_lock);
+	if (!wsa881x->clk_cnt) {
+		regcache_mark_dirty(wsa881x->regmap);
+		regcache_sync(wsa881x->regmap);
+	}
+	mutex_unlock(&wsa881x->res_lock);
+
+	wsa881x_resource_acquire(codec, ENABLE);
+
+	snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x00);
+	wsa_temp_reg->dmeas_msb = snd_soc_read(codec, WSA881X_TEMP_MSB);
+	wsa_temp_reg->dmeas_lsb = snd_soc_read(codec, WSA881X_TEMP_LSB);
+	snd_soc_update_bits(codec, WSA881X_TADC_VALUE_CTL, 0x01, 0x01);
+	wsa_temp_reg->d1_msb = snd_soc_read(codec, WSA881X_OTP_REG_1);
+	wsa_temp_reg->d1_lsb = snd_soc_read(codec, WSA881X_OTP_REG_2);
+	wsa_temp_reg->d2_msb = snd_soc_read(codec, WSA881X_OTP_REG_3);
+	wsa_temp_reg->d2_lsb = snd_soc_read(codec, WSA881X_OTP_REG_4);
+
+	wsa881x_resource_acquire(codec, DISABLE);
+
+	return 0;
+}
+
+static int wsa881x_probe(struct snd_soc_codec *codec)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+	struct swr_device *dev;
+
+	if (!wsa881x)
+		return -EINVAL;
+
+	dev = wsa881x->swr_slave;
+	wsa881x->codec = codec;
+	mutex_init(&wsa881x->bg_lock);
+	mutex_init(&wsa881x->res_lock);
+	wsa881x_init(codec);
+	snprintf(wsa881x->tz_pdata.name, sizeof(wsa881x->tz_pdata.name),
+		"%s.%x", "wsatz", (u8)dev->addr);
+	wsa881x->bg_cnt = 0;
+	wsa881x->clk_cnt = 0;
+	wsa881x->tz_pdata.codec = codec;
+	wsa881x->tz_pdata.wsa_temp_reg_read = wsa881x_temp_reg_read;
+	wsa881x_init_thermal(&wsa881x->tz_pdata);
+	snd_soc_add_codec_controls(codec, wsa_analog_gain_controls,
+				   ARRAY_SIZE(wsa_analog_gain_controls));
+	INIT_DELAYED_WORK(&wsa881x->ocp_ctl_work, wsa881x_ocp_ctl_work);
+	return 0;
+}
+
+static int wsa881x_remove(struct snd_soc_codec *codec)
+{
+	struct wsa881x_priv *wsa881x = snd_soc_codec_get_drvdata(codec);
+
+	if (wsa881x->tz_pdata.tz_dev)
+		wsa881x_deinit_thermal(wsa881x->tz_pdata.tz_dev);
+	mutex_destroy(&wsa881x->bg_lock);
+	mutex_destroy(&wsa881x->res_lock);
+
+	return 0;
+}
+
+static struct regmap *wsa881x_get_regmap(struct device *dev)
+{
+	struct wsa881x_priv *control = swr_get_dev_data(to_swr_device(dev));
+
+	if (!control)
+		return NULL;
+
+	return control->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wsa881x = {
+	.probe = wsa881x_probe,
+	.remove = wsa881x_remove,
+	.get_regmap = wsa881x_get_regmap,
+	.component_driver = {
+		.controls = wsa881x_snd_controls,
+		.num_controls = ARRAY_SIZE(wsa881x_snd_controls),
+		.dapm_widgets = wsa881x_dapm_widgets,
+		.num_dapm_widgets = ARRAY_SIZE(wsa881x_dapm_widgets),
+		.dapm_routes = wsa881x_audio_map,
+		.num_dapm_routes = ARRAY_SIZE(wsa881x_audio_map),
+	},
+};
+
+static int wsa881x_swr_startup(struct swr_device *swr_dev)
+{
+	int ret = 0;
+	u8 devnum = 0;
+	struct wsa881x_priv *wsa881x;
+
+	wsa881x = swr_get_dev_data(swr_dev);
+	if (!wsa881x) {
+		dev_err(&swr_dev->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	/*
+	 * Add 5msec delay to provide sufficient time for
+	 * soundwire auto enumeration of slave devices as
+	 * as per HW requirement.
+	 */
+	usleep_range(5000, 5010);
+	ret = swr_get_logical_dev_num(swr_dev, swr_dev->addr, &devnum);
+	if (ret) {
+		dev_dbg(&swr_dev->dev, "%s failed to get devnum, err:%d\n",
+			__func__, ret);
+		goto err;
+	}
+	swr_dev->dev_num = devnum;
+
+	wsa881x->regmap = devm_regmap_init_swr(swr_dev,
+					       &wsa881x_regmap_config);
+	if (IS_ERR(wsa881x->regmap)) {
+		ret = PTR_ERR(wsa881x->regmap);
+		dev_err(&swr_dev->dev, "%s: regmap_init failed %d\n",
+			__func__, ret);
+		goto err;
+	}
+
+	ret = snd_soc_register_codec(&swr_dev->dev, &soc_codec_dev_wsa881x,
+				     NULL, 0);
+	if (ret) {
+		dev_err(&swr_dev->dev, "%s: Codec registration failed\n",
+			__func__);
+		goto err;
+	}
+
+err:
+	return ret;
+}
+
+static int wsa881x_gpio_ctrl(struct wsa881x_priv *wsa881x, bool enable)
+{
+	int ret = 0;
+
+	if (wsa881x->pd_gpio < 0) {
+		dev_err(wsa881x->dev, "%s: gpio is not valid %d\n",
+			__func__, wsa881x->pd_gpio);
+		return -EINVAL;
+	}
+
+	if (wsa881x->wsa_rst_np) {
+		if (enable)
+			ret = msm_cdc_pinctrl_select_active_state(
+							wsa881x->wsa_rst_np);
+		else
+			ret = msm_cdc_pinctrl_select_sleep_state(
+							wsa881x->wsa_rst_np);
+		if (ret != 0)
+			dev_err(wsa881x->dev,
+				"%s: Failed to turn state %d; ret=%d\n",
+				__func__, enable, ret);
+	} else {
+		if (gpio_is_valid(wsa881x->pd_gpio))
+			gpio_direction_output(wsa881x->pd_gpio, enable);
+	}
+
+	return ret;
+}
+
+static int wsa881x_gpio_init(struct swr_device *pdev)
+{
+	int ret = 0;
+	struct wsa881x_priv *wsa881x;
+
+	wsa881x = swr_get_dev_data(pdev);
+	if (!wsa881x) {
+		dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(&pdev->dev, "%s: gpio %d request with name %s\n",
+		__func__, wsa881x->pd_gpio, dev_name(&pdev->dev));
+	ret = gpio_request(wsa881x->pd_gpio, dev_name(&pdev->dev));
+	if (ret) {
+		if (ret == -EBUSY) {
+			/* GPIO was already requested */
+			dev_dbg(&pdev->dev,
+				 "%s: gpio %d is already set to high\n",
+				 __func__, wsa881x->pd_gpio);
+			ret = 0;
+		} else {
+			dev_err(&pdev->dev, "%s: Failed to request gpio %d, err: %d\n",
+				__func__, wsa881x->pd_gpio, ret);
+		}
+	}
+	return ret;
+}
+
+static int wsa881x_swr_probe(struct swr_device *pdev)
+{
+	int ret = 0;
+	struct wsa881x_priv *wsa881x;
+
+	wsa881x = devm_kzalloc(&pdev->dev, sizeof(struct wsa881x_priv),
+			    GFP_KERNEL);
+	if (!wsa881x)
+		return -ENOMEM;
+	wsa881x->wsa_rst_np = of_parse_phandle(pdev->dev.of_node,
+					     "qcom,spkr-sd-n-node", 0);
+	if (!wsa881x->wsa_rst_np) {
+		dev_dbg(&pdev->dev, "%s: Not using pinctrl, fallback to gpio\n",
+			__func__);
+		wsa881x->pd_gpio = of_get_named_gpio(pdev->dev.of_node,
+						     "qcom,spkr-sd-n-gpio", 0);
+		if (wsa881x->pd_gpio < 0) {
+			dev_err(&pdev->dev, "%s: %s property is not found %d\n",
+				__func__, "qcom,spkr-sd-n-gpio",
+				wsa881x->pd_gpio);
+			goto err;
+		}
+		dev_dbg(&pdev->dev, "%s: reset gpio %d\n", __func__,
+			wsa881x->pd_gpio);
+	}
+	swr_set_dev_data(pdev, wsa881x);
+
+	wsa881x->swr_slave = pdev;
+
+	if (!wsa881x->wsa_rst_np) {
+		ret = wsa881x_gpio_init(pdev);
+		if (ret)
+			goto err;
+	}
+	wsa881x_gpio_ctrl(wsa881x, true);
+	wsa881x->state = WSA881X_DEV_UP;
+
+	if (!debugfs_wsa881x_dent) {
+		dbgwsa881x = wsa881x;
+		debugfs_wsa881x_dent = debugfs_create_dir(
+						"wsa881x_swr_slave", 0);
+		if (!IS_ERR(debugfs_wsa881x_dent)) {
+			debugfs_peek = debugfs_create_file("swrslave_peek",
+					S_IFREG | 0444, debugfs_wsa881x_dent,
+					(void *) "swrslave_peek",
+					&codec_debug_ops);
+
+			debugfs_poke = debugfs_create_file("swrslave_poke",
+					S_IFREG | 0444, debugfs_wsa881x_dent,
+					(void *) "swrslave_poke",
+					&codec_debug_ops);
+
+			debugfs_reg_dump = debugfs_create_file(
+						"swrslave_reg_dump",
+						S_IFREG | 0444,
+						debugfs_wsa881x_dent,
+						(void *) "swrslave_reg_dump",
+						&codec_debug_ops);
+		}
+	}
+	return 0;
+
+err:
+	return ret;
+}
+
+static int wsa881x_swr_remove(struct swr_device *pdev)
+{
+	struct wsa881x_priv *wsa881x;
+
+	wsa881x = swr_get_dev_data(pdev);
+	if (!wsa881x) {
+		dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	debugfs_remove_recursive(debugfs_wsa881x_dent);
+	snd_soc_unregister_codec(&pdev->dev);
+	if (wsa881x->pd_gpio)
+		gpio_free(wsa881x->pd_gpio);
+	swr_set_dev_data(pdev, NULL);
+	return 0;
+}
+
+static int wsa881x_swr_up(struct swr_device *pdev)
+{
+	int ret;
+	struct wsa881x_priv *wsa881x;
+
+	wsa881x = swr_get_dev_data(pdev);
+	if (!wsa881x) {
+		dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	ret = wsa881x_gpio_ctrl(wsa881x, true);
+	if (ret)
+		dev_err(&pdev->dev, "%s: Failed to enable gpio\n", __func__);
+	else
+		wsa881x->state = WSA881X_DEV_UP;
+
+	return ret;
+}
+
+static int wsa881x_swr_down(struct swr_device *pdev)
+{
+	struct wsa881x_priv *wsa881x;
+	int ret;
+
+	wsa881x = swr_get_dev_data(pdev);
+	if (!wsa881x) {
+		dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (delayed_work_pending(&wsa881x->ocp_ctl_work))
+		cancel_delayed_work_sync(&wsa881x->ocp_ctl_work);
+	ret = wsa881x_gpio_ctrl(wsa881x, false);
+	if (ret)
+		dev_err(&pdev->dev, "%s: Failed to disable gpio\n", __func__);
+	else
+		wsa881x->state = WSA881X_DEV_DOWN;
+
+	return ret;
+}
+
+static int wsa881x_swr_reset(struct swr_device *pdev)
+{
+	struct wsa881x_priv *wsa881x;
+	u8 retry = WSA881X_NUM_RETRY;
+	u8 devnum = 0;
+
+	wsa881x = swr_get_dev_data(pdev);
+	if (!wsa881x) {
+		dev_err(&pdev->dev, "%s: wsa881x is NULL\n", __func__);
+		return -EINVAL;
+	}
+	wsa881x->bg_cnt = 0;
+	wsa881x->clk_cnt = 0;
+	while (swr_get_logical_dev_num(pdev, pdev->addr, &devnum) && retry--) {
+		/* Retry after 1 msec delay */
+		usleep_range(1000, 1100);
+	}
+	regcache_mark_dirty(wsa881x->regmap);
+	regcache_sync(wsa881x->regmap);
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int wsa881x_swr_suspend(struct device *dev)
+{
+	dev_dbg(dev, "%s: system suspend\n", __func__);
+	return 0;
+}
+
+static int wsa881x_swr_resume(struct device *dev)
+{
+	struct wsa881x_priv *wsa881x = swr_get_dev_data(to_swr_device(dev));
+
+	if (!wsa881x) {
+		dev_err(dev, "%s: wsa881x private data is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dev_dbg(dev, "%s: system resume\n", __func__);
+	return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops wsa881x_swr_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(wsa881x_swr_suspend, wsa881x_swr_resume)
+};
+
+static const struct swr_device_id wsa881x_swr_id[] = {
+	{"wsa881x", 0},
+	{}
+};
+
+static const struct of_device_id wsa881x_swr_dt_match[] = {
+	{
+		.compatible = "qcom,wsa881x",
+	},
+	{}
+};
+
+static struct swr_driver wsa881x_codec_driver = {
+	.driver = {
+		.name = "wsa881x",
+		.owner = THIS_MODULE,
+		.pm = &wsa881x_swr_pm_ops,
+		.of_match_table = wsa881x_swr_dt_match,
+	},
+	.probe = wsa881x_swr_probe,
+	.remove = wsa881x_swr_remove,
+	.id_table = wsa881x_swr_id,
+	.device_up = wsa881x_swr_up,
+	.device_down = wsa881x_swr_down,
+	.reset_device = wsa881x_swr_reset,
+	.startup = wsa881x_swr_startup,
+};
+
+static int __init wsa881x_codec_init(void)
+{
+	return swr_driver_register(&wsa881x_codec_driver);
+}
+
+static void __exit wsa881x_codec_exit(void)
+{
+	swr_driver_unregister(&wsa881x_codec_driver);
+}
+
+module_init(wsa881x_codec_init);
+module_exit(wsa881x_codec_exit);
+
+MODULE_DESCRIPTION("WSA881x Codec driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wsa881x.h b/sound/soc/codecs/wsa881x.h
new file mode 100644
index 0000000..be234ac
--- /dev/null
+++ b/sound/soc/codecs/wsa881x.h
@@ -0,0 +1,34 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WSA881X_H
+#define _WSA881X_H
+
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/info.h>
+#include "wsa881x-registers.h"
+
+#define WSA881X_MAX_SWR_PORTS   4
+
+extern int wsa881x_set_channel_map(struct snd_soc_codec *codec, u8 *port,
+				u8 num_port, unsigned int *ch_mask,
+				unsigned int *ch_rate);
+
+extern const u8 wsa881x_reg_readable[WSA881X_CACHE_SIZE];
+extern struct regmap_config wsa881x_regmap_config;
+extern int wsa881x_codec_info_create_codec_entry(
+					struct snd_info_entry *codec_root,
+					struct snd_soc_codec *codec);
+void wsa881x_regmap_defaults(struct regmap *regmap, u8 version);
+
+#endif /* _WSA881X_H */
diff --git a/sound/soc/msm/Kconfig b/sound/soc/msm/Kconfig
new file mode 100644
index 0000000..b3ac439
--- /dev/null
+++ b/sound/soc/msm/Kconfig
@@ -0,0 +1,263 @@
+menu "MSM SoC Audio support"
+
+config SND_SOC_MSM_HOSTLESS_PCM
+	tristate
+
+config SND_SOC_MSM_QDSP6V2_INTF
+	bool "SoC Q6 audio driver for MSM/APQ"
+	depends on MSM_QDSP6_APRV2_GLINK
+	help
+	 To add support for SoC audio on MSM/APQ.
+	 This will enable all the platform specific
+	 interactions towards DSP. It includes asm,
+	 adm and afe interfaces on the DSP.
+
+config SND_SOC_QDSP6V2
+	tristate "SoC ALSA audio driver for QDSP6V2"
+	select SND_SOC_MSM_QDSP6V2_INTF
+	select SND_SOC_COMPRESS
+	help
+	 To add support for MSM QDSP6V2 Soc Audio.
+	 This will enable sound soc platform specific
+	 audio drivers. This includes q6asm, q6adm,
+	 q6afe interfaces to DSP using apr.
+
+config SND_SOC_QDSP_DEBUG
+	bool "QDSP Audio Driver Debug Feature"
+	help
+	 Configuration to enable debugging utilities for
+	 QDSP6 based audio drivers. One debugging utility
+	 is inducing kernel panic upon encountering critical
+	 errors from DSP audio modules
+
+config DOLBY_DAP
+	bool "Enable Dolby DAP"
+	depends on SND_SOC_MSM_QDSP6V2_INTF
+	help
+	 To add support for dolby DAP post processing.
+	 This support is to configure the post processing parameters
+	 to DSP. The configuration includes sending the end point
+	 device, end point dependent post processing parameters and
+	 the various posrt processing parameters
+
+config DOLBY_DS2
+	bool "Enable Dolby DS2"
+	depends on SND_SOC_MSM_QDSP6V2_INTF
+	help
+	 To add support for dolby DAP post processing.
+	 This support is to configure the post processing parameters
+	 to DSP. The configuration includes sending the end point
+	 device, end point dependent post processing parameters and
+	 the various posrt processing parameters
+
+config DTS_EAGLE
+	bool "Enable DTS Eagle Support"
+	depends on SND_SOC_MSM_QDSP6V2_INTF
+	select SND_HWDEP
+	help
+	 To add DTS Eagle support on QDSP6 targets.
+	 Eagle is a DTS pre/post processing
+	 package that includes HeadphoneX. The configuration
+	 includes sending tuning parameters of various modules.
+
+config DTS_SRS_TM
+	bool "Enable DTS SRS"
+	depends on SND_SOC_MSM_QDSP6V2_INTF
+	help
+	 To add support for DTS SRS post processing.
+	 This support is to configure the post processing
+	 parameters to DSP. The configuration includes sending
+	 tuning parameters of various modules.
+
+config QTI_PP
+	bool "Enable QTI PP"
+	depends on SND_SOC_MSM_QDSP6V2_INTF
+	help
+	 To add support for default QTI post processing.
+	 This support is to configure the post processing
+	 parameters to DSP. The configuration includes sending
+	 tuning parameters of various modules such as equalizer,
+	 customized mixing.
+
+config QTI_PP_AUDIOSPHERE
+	bool "Enable QTI AUDIOSPHERE PP"
+	depends on SND_SOC_MSM_QDSP6V2_INTF
+	help
+	 To add support for QTI audio sphere post processing.
+	 This support is to configure the post processing
+	 parameters to DSP. The configuration includes sending
+	 tuning parameters of audio sphere module.
+
+config SND_SOC_CPE
+	tristate "CPE drivers"
+	depends on SND_SOC_WCD_CPE
+	help
+	 To add support for Codec Processing Engine. This support
+	 is to enable CPE block on the codec and this config needs
+	 to be added to codecs that contain the CPE hardware block.
+	 The configuration includes the cpe lsm driver to enable
+	 listen on codec.
+
+config SND_SOC_INT_CODEC
+	tristate "SoC Machine driver for MSMFALCON_INT"
+	depends on ARCH_QCOM
+	select SND_SOC_QDSP6V2
+	select SND_SOC_MSM_STUB
+	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_DYNAMIC_MINORS
+	select MSM_QDSP6_APRV2_GLINK
+	select MSM_QDSP6_SSR
+	select MSM_QDSP6_PDR
+	select MSM_QDSP6_NOTIFIER
+	select MSM_QDSP6V2_CODECS
+	select SND_SOC_MSM_SWR
+	select SND_SOC_MSM8X16_WCD
+	select QTI_PP
+	select DTS_SRS_TM
+	select DOLBY_DAP
+	select DOLBY_DS2
+	select SND_HWDEP
+	select MSM_ULTRASOUND
+	select DTS_EAGLE
+	select SND_SOC_MSMFALCON_COMMON
+	select SND_SOC_COMPRESS
+	help
+	To add support for SoC audio on MSM_INT.
+	This will enable sound soc drivers which
+	interfaces with DSP, also it will enable
+	the machine driver and the corresponding
+	DAI-links
+
+config SND_SOC_EXT_CODEC
+	tristate "SoC Machine driver for MSMFALCON_EXT"
+	depends on ARCH_QCOM
+	select SND_SOC_QDSP6V2
+	select SND_SOC_MSM_STUB
+	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_DYNAMIC_MINORS
+	select MSM_QDSP6_APRV2_GLINK
+	select MSM_QDSP6_SSR
+	select MSM_QDSP6_PDR
+	select MSM_QDSP6_NOTIFIER
+	select MSM_QDSP6V2_CODECS
+	select SND_SOC_WCD9335
+	select SND_SOC_WCD934X
+	select SND_SOC_WSA881X
+	select MFD_CORE
+	select QTI_PP
+	select DTS_SRS_TM
+	select DOLBY_DAP
+	select DOLBY_DS2
+	select SND_SOC_CPE
+	select SND_SOC_WCD_CPE
+	select SND_HWDEP
+	select MSM_ULTRASOUND
+	select DTS_EAGLE
+	select SND_SOC_MSMFALCON_COMMON
+	select SND_SOC_COMPRESS
+	help
+	To add support for SoC audio on MSM_EXT.
+	This will enable sound soc drivers which
+	interfaces with DSP, also it will enable
+	the machine driver and the corresponding
+	DAI-links
+
+config SND_SOC_MSM8996
+	tristate "SoC Machine driver for MSM8996 boards"
+	depends on ARCH_MSM8996
+	select SND_SOC_COMPRESS
+	select SND_SOC_QDSP6V2
+	select SND_SOC_MSM_STUB
+	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_DYNAMIC_MINORS
+	select MSM_QDSP6_APRV2
+	select MSM_QDSP6V2_CODECS
+	select SND_SOC_WCD9335
+	select SND_SOC_WSA881X
+	select SND_SOC_MSM_HDMI_CODEC_RX
+	select DTS_SRS_TM
+	select QTI_PP
+	select QTI_PP_AUDIOSPHERE
+	select SND_SOC_CPE
+	select MSM_ULTRASOUND
+	select DOLBY_DS2
+	select SND_HWDEP
+        select DTS_EAGLE
+	help
+	 To add support for SoC audio on MSM8996.
+	 This will enable sound soc drivers which
+	 interfaces with DSP, also it will enable
+	 the machine driver and the corresponding
+	 DAI-links
+
+config SND_SOC_MSM8998
+	tristate "SoC Machine driver for MSM8998 boards"
+	depends on ARCH_QCOM
+	select SND_SOC_COMPRESS
+	select SND_SOC_QDSP6V2
+	select SND_SOC_MSM_STUB
+	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_DYNAMIC_MINORS
+	select MSM_QDSP6_APRV2_GLINK
+	select MSM_QDSP6_SSR
+	select MSM_QDSP6_PDR
+	select MSM_QDSP6_NOTIFIER
+	select MSM_QDSP6V2_CODECS
+	select SND_SOC_WCD9335
+	select SND_SOC_WCD934X
+	select SND_SOC_WSA881X
+	select SND_SOC_MSM_HDMI_CODEC_RX
+	select DTS_SRS_TM
+	select QTI_PP
+	select SND_SOC_CPE
+	select MSM_ULTRASOUND
+	select DOLBY_DS2
+	select SND_HWDEP
+        select DTS_EAGLE
+	help
+	 To add support for SoC audio on MSM8998.
+	 This will enable sound soc drivers which
+	 interfaces with DSP, also it will enable
+	 the machine driver and the corresponding
+	 DAI-links
+
+config SND_SOC_FALCON
+	tristate "SoC Machine driver for MSMFALCON boards"
+	depends on ARCH_MSMFALCON
+	select SND_SOC_INT_CODEC
+	select SND_SOC_EXT_CODEC
+	help
+	 To add support for SoC audio on MSMFALCON.
+	 This will enable sound soc drivers which
+	 interfaces with DSP, also it will enable
+	 the machine driver and the corresponding
+	 DAI-links
+
+config SND_SOC_MSMSKUNK
+	tristate "SoC Machine driver for MSMSKUNK boards"
+	depends on ARCH_QCOM
+	select SND_SOC_COMPRESS
+	select SND_SOC_QDSP6V2
+	select SND_SOC_MSM_STUB
+	select SND_SOC_MSM_HOSTLESS_PCM
+	select SND_DYNAMIC_MINORS
+	select MSM_QDSP6_APRV2_GLINK
+	select MSM_QDSP6_SSR
+	select MSM_QDSP6_PDR
+	select MSM_QDSP6_NOTIFIER
+	select MSM_QDSP6V2_CODECS
+	select SND_SOC_WCD934X
+	select SND_SOC_WSA881X
+	select DTS_SRS_TM
+	select QTI_PP
+	select MSM_ULTRASOUND
+	select DOLBY_DS2
+	select SND_HWDEP
+        select DTS_EAGLE
+	help
+	 To add support for SoC audio on MSMSKUNK.
+	 This enables sound soc drivers that interfaces
+	 with DSP. This also enables the machine driver
+	 and the corresponding DAI-links.
+
+endmenu
diff --git a/sound/soc/msm/Makefile b/sound/soc/msm/Makefile
new file mode 100644
index 0000000..50ceda4
--- /dev/null
+++ b/sound/soc/msm/Makefile
@@ -0,0 +1,39 @@
+# MSM Machine Support
+
+snd-soc-hostless-pcm-objs := msm-pcm-hostless.o
+obj-$(CONFIG_SND_SOC_MSM_HOSTLESS_PCM) += snd-soc-hostless-pcm.o
+
+obj-$(CONFIG_SND_SOC_MSM_QDSP6V2_INTF) += qdsp6v2/
+
+snd-soc-qdsp6v2-objs := msm-dai-fe.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o
+
+#for CPE drivers
+snd-soc-cpe-objs := msm-cpe-lsm.o
+obj-$(CONFIG_SND_SOC_CPE) += snd-soc-cpe.o
+
+# for MSM8996 sound card driver
+snd-soc-msm8996-objs := msm8996.o
+obj-$(CONFIG_SND_SOC_MSM8996) += snd-soc-msm8996.o
+
+# for MSM8998 sound card driver
+snd-soc-msm8998-objs := msm8998.o
+obj-$(CONFIG_SND_SOC_MSM8998) += snd-soc-msm8998.o
+
+# for MSMFALCON sound card driver
+snd-soc-msmfalcon-common-objs := msm-audio-pinctrl.o msmfalcon-common.o
+obj-$(CONFIG_SND_SOC_MSMFALCON_COMMON) += snd-soc-msmfalcon-common.o
+
+# for MSMFALCON sound card driver
+snd-soc-int-codec-objs := msmfalcon-internal.o
+obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-msmfalcon-common.o
+obj-$(CONFIG_SND_SOC_INT_CODEC) += snd-soc-int-codec.o
+
+# for MSMFALCON sound card driver
+snd-soc-ext-codec-objs := msmfalcon-external.o msmfalcon-ext-dai-links.o
+obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-msmfalcon-common.o
+obj-$(CONFIG_SND_SOC_EXT_CODEC) += snd-soc-ext-codec.o
+
+# for MSMSKUNK sound card driver
+snd-soc-msmskunk-objs := msmskunk.o
+obj-$(CONFIG_SND_SOC_MSMSKUNK) += snd-soc-msmskunk.o
diff --git a/sound/soc/msm/device_event.h b/sound/soc/msm/device_event.h
new file mode 100644
index 0000000..408d114
--- /dev/null
+++ b/sound/soc/msm/device_event.h
@@ -0,0 +1,20 @@
+/* Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __DEVICE_EVENT_H
+#define __DEVICE_EVENT_H
+
+#define QC_AUDIO_EXTERNAL_SPK_1_EVENT "qc_ext_spk_1"
+#define QC_AUDIO_EXTERNAL_SPK_2_EVENT "qc_ext_spk_2"
+#define QC_AUDIO_EXTERNAL_MIC_EVENT "qc_ext_mic"
+
+#endif /* __DEVICE_EVENT_H */
diff --git a/sound/soc/msm/msm-audio-pinctrl.c b/sound/soc/msm/msm-audio-pinctrl.c
new file mode 100644
index 0000000..f0fba84
--- /dev/null
+++ b/sound/soc/msm/msm-audio-pinctrl.c
@@ -0,0 +1,316 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include "msm-audio-pinctrl.h"
+
+/*
+ * pinctrl -- handle to query pinctrl apis
+ * cdc lines -- stores pinctrl handles for pinctrl states
+ * active_set -- maintain the overall pinctrl state
+ */
+struct cdc_pinctrl_info {
+	struct pinctrl *pinctrl;
+	struct pinctrl_state **cdc_lines;
+	int active_set;
+};
+
+/*
+ * gpiosets -- stores all gpiosets mentioned in dtsi file
+ * gpiosets_comb_names -- stores all possible gpioset combinations
+ * gpioset_state -- maintains counter for each gpioset
+ * gpiosets_max -- maintain the total supported gpiosets
+ * gpiosets_comb_max -- maintain the total gpiosets combinations
+ */
+struct cdc_gpioset_info {
+	char **gpiosets;
+	char **gpiosets_comb_names;
+	uint8_t *gpioset_state;
+	int gpiosets_max;
+	int gpiosets_comb_max;
+};
+
+static struct cdc_pinctrl_info pinctrl_info[MAX_PINCTRL_CLIENT];
+static struct cdc_gpioset_info gpioset_info[MAX_PINCTRL_CLIENT];
+
+/* Finds the index for the gpio set in the dtsi file */
+int msm_get_gpioset_index(enum pinctrl_client client, char *keyword)
+{
+	int i;
+
+	for (i = 0; i < gpioset_info[client].gpiosets_max; i++) {
+		if (!(strcmp(gpioset_info[client].gpiosets[i], keyword)))
+			break;
+	}
+	/* Checking if the keyword is present in dtsi or not */
+	if (i != gpioset_info[client].gpiosets_max)
+		return i;
+	else
+		return -EINVAL;
+}
+
+/*
+ * This function reads the following from dtsi file
+ * 1. All gpio sets
+ * 2. All combinations of gpio sets
+ * 3. Pinctrl handles to gpio sets
+ *
+ * Returns error if there is
+ * 1. Problem reading from dtsi file
+ * 2. Memory allocation failure
+ */
+int msm_gpioset_initialize(enum pinctrl_client client,
+				struct device *dev)
+{
+	struct pinctrl *pinctrl;
+	const char *gpioset_names = "qcom,msm-gpios";
+	const char *gpioset_combinations = "qcom,pinctrl-names";
+	const char *gpioset_names_str = NULL;
+	const char *gpioset_comb_str = NULL;
+	int num_strings = 0;
+	int ret = 0;
+	int i = 0;
+
+	pr_debug("%s\n", __func__);
+	pinctrl = devm_pinctrl_get(dev);
+	if (IS_ERR(pinctrl)) {
+		pr_err("%s: Unable to get pinctrl handle\n",
+				__func__);
+		return -EINVAL;
+	}
+	pinctrl_info[client].pinctrl = pinctrl;
+
+	/* Reading of gpio sets */
+	num_strings = of_property_count_strings(dev->of_node,
+						gpioset_names);
+	if (num_strings < 0) {
+		dev_err(dev,
+			"%s: missing %s in dt node or length is incorrect\n",
+				__func__, gpioset_names);
+		goto err;
+	}
+	gpioset_info[client].gpiosets_max = num_strings;
+	gpioset_info[client].gpiosets = devm_kzalloc(dev,
+				gpioset_info[client].gpiosets_max *
+					sizeof(char *), GFP_KERNEL);
+	if (!gpioset_info[client].gpiosets) {
+		dev_err(dev, "Can't allocate memory for gpio set names\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < num_strings; i++) {
+		ret = of_property_read_string_index(dev->of_node,
+					gpioset_names, i, &gpioset_names_str);
+
+		gpioset_info[client].gpiosets[i] = devm_kzalloc(dev,
+				(strlen(gpioset_names_str) + 1), GFP_KERNEL);
+
+		if (!gpioset_info[client].gpiosets[i]) {
+			dev_err(dev, "%s: Can't allocate gpiosets[%d] data\n",
+						__func__, i);
+			ret = -ENOMEM;
+			goto err;
+		}
+		strlcpy(gpioset_info[client].gpiosets[i],
+				gpioset_names_str, strlen(gpioset_names_str)+1);
+		gpioset_names_str = NULL;
+	}
+	num_strings = 0;
+
+	/* Allocating memory for gpio set counter */
+	gpioset_info[client].gpioset_state = devm_kzalloc(dev,
+				gpioset_info[client].gpiosets_max *
+				sizeof(uint8_t), GFP_KERNEL);
+	if (!gpioset_info[client].gpioset_state) {
+		dev_err(dev, "Can't allocate memory for gpio set counter\n");
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* Reading of all combinations of gpio sets */
+	num_strings = of_property_count_strings(dev->of_node,
+						gpioset_combinations);
+	if (num_strings < 0) {
+		dev_err(dev,
+			"%s: missing %s in dt node or length is incorrect\n",
+			__func__, gpioset_combinations);
+		goto err;
+	}
+	gpioset_info[client].gpiosets_comb_max = num_strings;
+	gpioset_info[client].gpiosets_comb_names = devm_kzalloc(dev,
+				num_strings * sizeof(char *), GFP_KERNEL);
+	if (!gpioset_info[client].gpiosets_comb_names) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) {
+		ret = of_property_read_string_index(dev->of_node,
+				gpioset_combinations, i, &gpioset_comb_str);
+
+		gpioset_info[client].gpiosets_comb_names[i] = devm_kzalloc(dev,
+				(strlen(gpioset_comb_str) + 1), GFP_KERNEL);
+		if (!gpioset_info[client].gpiosets_comb_names[i]) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		strlcpy(gpioset_info[client].gpiosets_comb_names[i],
+					gpioset_comb_str,
+					strlen(gpioset_comb_str)+1);
+		pr_debug("%s: GPIO configuration %s\n",
+				__func__,
+				gpioset_info[client].gpiosets_comb_names[i]);
+		gpioset_comb_str = NULL;
+	}
+
+	/* Allocating memory for handles to pinctrl states */
+	pinctrl_info[client].cdc_lines = devm_kzalloc(dev,
+		num_strings * sizeof(char *), GFP_KERNEL);
+	if (!pinctrl_info[client].cdc_lines) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/* Get pinctrl handles for gpio sets in dtsi file */
+	for (i = 0; i < num_strings; i++) {
+		pinctrl_info[client].cdc_lines[i] = pinctrl_lookup_state(
+								pinctrl,
+					(const char *)gpioset_info[client].
+							gpiosets_comb_names[i]);
+		if (IS_ERR(pinctrl_info[client].cdc_lines[i]))
+			pr_err("%s: Unable to get pinctrl handle for %s\n",
+				__func__, gpioset_info[client].
+						gpiosets_comb_names[i]);
+	}
+	goto success;
+
+err:
+	/* Free up memory allocated for gpio set combinations */
+	for (i = 0; i < gpioset_info[client].gpiosets_max; i++) {
+		if (gpioset_info[client].gpiosets[i] != NULL) {
+			devm_kfree(dev, gpioset_info[client].gpiosets[i]);
+			gpioset_info[client].gpiosets[i] = NULL;
+		}
+	}
+	if (gpioset_info[client].gpiosets != NULL) {
+		devm_kfree(dev, gpioset_info[client].gpiosets);
+		gpioset_info[client].gpiosets = NULL;
+	}
+
+	/* Free up memory allocated for gpio set combinations */
+	for (i = 0; i < gpioset_info[client].gpiosets_comb_max; i++) {
+		if (gpioset_info[client].gpiosets_comb_names[i] != NULL) {
+			devm_kfree(dev,
+			gpioset_info[client].gpiosets_comb_names[i]);
+			gpioset_info[client].gpiosets_comb_names[i] = NULL;
+		}
+	}
+	if (gpioset_info[client].gpiosets_comb_names != NULL) {
+		devm_kfree(dev, gpioset_info[client].gpiosets_comb_names);
+		gpioset_info[client].gpiosets_comb_names = NULL;
+	}
+
+	/* Free up memory allocated for handles to pinctrl states */
+	if (pinctrl_info[client].cdc_lines != NULL) {
+		devm_kfree(dev, pinctrl_info[client].cdc_lines);
+		pinctrl_info[client].cdc_lines = NULL;
+	}
+
+	/* Free up memory allocated for counter of gpio sets */
+	if (gpioset_info[client].gpioset_state != NULL) {
+		devm_kfree(dev, gpioset_info[client].gpioset_state);
+		gpioset_info[client].gpioset_state = NULL;
+	}
+
+success:
+	return ret;
+}
+
+int msm_gpioset_activate(enum pinctrl_client client, char *keyword)
+{
+	int ret = 0;
+	int gp_set = 0;
+	int active_set = 0;
+
+	gp_set = msm_get_gpioset_index(client, keyword);
+	if (gp_set < 0) {
+		pr_err("%s: gpio set name does not exist\n",
+				__func__);
+		return gp_set;
+	}
+
+	if (!gpioset_info[client].gpioset_state[gp_set]) {
+		/*
+		 * If pinctrl pointer is not valid,
+		 * no need to proceed further
+		 */
+		active_set = pinctrl_info[client].active_set;
+		if (IS_ERR(pinctrl_info[client].cdc_lines[active_set]))
+			return 0;
+
+		pinctrl_info[client].active_set |= (1 << gp_set);
+		active_set = pinctrl_info[client].active_set;
+		pr_debug("%s: pinctrl.active_set: %d\n", __func__, active_set);
+
+		/* Select the appropriate pinctrl state */
+		ret =  pinctrl_select_state(pinctrl_info[client].pinctrl,
+			pinctrl_info[client].cdc_lines[active_set]);
+	}
+	gpioset_info[client].gpioset_state[gp_set]++;
+
+	return ret;
+}
+
+int msm_gpioset_suspend(enum pinctrl_client client, char *keyword)
+{
+	int ret = 0;
+	int gp_set = 0;
+	int active_set = 0;
+
+	gp_set = msm_get_gpioset_index(client, keyword);
+	if (gp_set < 0) {
+		pr_err("%s: gpio set name does not exist\n",
+				__func__);
+		return gp_set;
+	}
+
+	if (gpioset_info[client].gpioset_state[gp_set] == 1) {
+		pinctrl_info[client].active_set &= ~(1 << gp_set);
+		/*
+		 * If pinctrl pointer is not valid,
+		 * no need to proceed further
+		 */
+		active_set = pinctrl_info[client].active_set;
+		if (IS_ERR(pinctrl_info[client].cdc_lines[active_set]))
+			return -EINVAL;
+
+		pr_debug("%s: pinctrl.active_set: %d\n", __func__,
+				pinctrl_info[client].active_set);
+		/* Select the appropriate pinctrl state */
+		ret =  pinctrl_select_state(pinctrl_info[client].pinctrl,
+			pinctrl_info[client].cdc_lines[pinctrl_info[client].
+								active_set]);
+	}
+	if (!(gpioset_info[client].gpioset_state[gp_set])) {
+		pr_err("%s: Invalid call to de activate gpios: %d\n", __func__,
+				gpioset_info[client].gpioset_state[gp_set]);
+		return -EINVAL;
+	}
+
+	gpioset_info[client].gpioset_state[gp_set]--;
+
+	return ret;
+}
diff --git a/sound/soc/msm/msm-audio-pinctrl.h b/sound/soc/msm/msm-audio-pinctrl.h
new file mode 100644
index 0000000..ec7c6aa
--- /dev/null
+++ b/sound/soc/msm/msm-audio-pinctrl.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_AUDIO_PINCTRL_H
+#define __MSM_AUDIO_PINCTRL_H
+
+enum pinctrl_client {
+	CLIENT_WCD,
+	CLIENT_WSA_BONGO_1,
+	CLIENT_WSA_BONGO_2,
+	MAX_PINCTRL_CLIENT,
+};
+
+
+/* finds the index for the gpio set in the dtsi file */
+int msm_get_gpioset_index(enum pinctrl_client client, char *keyword);
+
+/*
+ * this function reads the following from dtsi file
+ * 1. all gpio sets
+ * 2. all combinations of gpio sets
+ * 3. pinctrl handles to gpio sets
+ *
+ * returns error if there is
+ * 1. problem reading from dtsi file
+ * 2. memory allocation failure
+ */
+int msm_gpioset_initialize(enum pinctrl_client client, struct device *dev);
+
+int msm_gpioset_activate(enum pinctrl_client client, char *keyword);
+
+int msm_gpioset_suspend(enum pinctrl_client client, char *keyword);
+
+#endif /* __MSM_AUDIO_PINCTRL_H */
diff --git a/sound/soc/msm/msm-cpe-lsm.c b/sound/soc/msm/msm-cpe-lsm.c
new file mode 100644
index 0000000..44927c8
--- /dev/null
+++ b/sound/soc/msm/msm-cpe-lsm.c
@@ -0,0 +1,3141 @@
+/*
+ * Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/freezer.h>
+#include <sound/soc.h>
+#include <sound/cpe_core.h>
+#include <sound/lsm_params.h>
+#include <sound/pcm_params.h>
+#include <sound/msm-slim-dma.h>
+
+#define SAMPLE_RATE_48KHZ 48000
+#define SAMPLE_RATE_16KHZ 16000
+#define LSM_VOICE_WAKEUP_APP_V2 2
+#define AFE_PORT_ID_1 1
+#define AFE_PORT_ID_3 3
+#define AFE_OUT_PORT_2 2
+#define LISTEN_MIN_NUM_PERIODS     2
+#define LISTEN_MAX_NUM_PERIODS     12
+#define LISTEN_MAX_PERIOD_SIZE     61440
+#define LISTEN_MIN_PERIOD_SIZE     320
+#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256
+#define MSM_CPE_MAX_CUSTOM_PARAM_SIZE 2048
+
+#define MSM_CPE_LAB_THREAD_TIMEOUT (3 * (HZ/10))
+
+#define MSM_CPE_LSM_GRAB_LOCK(lock, name)		\
+{						\
+	pr_debug("%s: %s lock acquire\n",	\
+		 __func__, name);		\
+	mutex_lock(lock);			\
+}
+
+#define MSM_CPE_LSM_REL_LOCK(lock, name)		\
+{						\
+	pr_debug("%s: %s lock release\n",	\
+		 __func__, name);		\
+	mutex_unlock(lock);			\
+}
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 16000, 48000, 192000, 384000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+
+static struct snd_pcm_hardware msm_pcm_hardware_listen = {
+	.info =	(SNDRV_PCM_INFO_BLOCK_TRANSFER |
+		 SNDRV_PCM_INFO_MMAP_VALID |
+		 SNDRV_PCM_INFO_INTERLEAVED |
+		 SNDRV_PCM_INFO_PAUSE |
+		 SNDRV_PCM_INFO_RESUME),
+	.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+		    SNDRV_PCM_FMTBIT_S24_LE |
+		    SNDRV_PCM_FMTBIT_S32_LE),
+	.rates = (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_48000 |
+		  SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_384000),
+	.rate_min = 16000,
+	.rate_max = 384000,
+	.channels_min =	1,
+	.channels_max =	1,
+	.buffer_bytes_max = LISTEN_MAX_NUM_PERIODS *
+			    LISTEN_MAX_PERIOD_SIZE,
+	.period_bytes_min = LISTEN_MIN_PERIOD_SIZE,
+	.period_bytes_max = LISTEN_MAX_PERIOD_SIZE,
+	.periods_min = LISTEN_MIN_NUM_PERIODS,
+	.periods_max = LISTEN_MAX_NUM_PERIODS,
+	.fifo_size = 0,
+};
+
+enum {
+	AFE_CMD_INVALID = 0,
+	AFE_CMD_PORT_START,
+	AFE_CMD_PORT_SUSPEND,
+	AFE_CMD_PORT_RESUME,
+	AFE_CMD_PORT_STOP,
+};
+
+enum cpe_lab_thread_status {
+	MSM_LSM_LAB_THREAD_STOP,
+	MSM_LSM_LAB_THREAD_RUNNING,
+	MSM_LSM_LAB_THREAD_ERROR,
+};
+
+struct cpe_hw_params {
+	u32 sample_rate;
+	u16 sample_size;
+	u32 buf_sz;
+	u32 period_count;
+	u16 channels;
+};
+
+struct cpe_data_pcm_buf {
+	u8 *mem;
+	phys_addr_t phys;
+};
+
+struct cpe_lsm_lab {
+	atomic_t in_count;
+	atomic_t abort_read;
+	u32 dma_write;
+	u32 buf_idx;
+	u32 pcm_size;
+	enum cpe_lab_thread_status thread_status;
+	struct cpe_data_pcm_buf *pcm_buf;
+	wait_queue_head_t period_wait;
+	struct completion comp;
+	struct completion thread_complete;
+};
+
+struct cpe_priv {
+	void *core_handle;
+	struct snd_soc_codec *codec;
+	struct wcd_cpe_lsm_ops lsm_ops;
+	struct wcd_cpe_afe_ops afe_ops;
+	bool afe_mad_ctl;
+	u32 input_port_id;
+};
+
+struct cpe_lsm_data {
+	struct device *dev;
+	struct cpe_lsm_session *lsm_session;
+	struct mutex lsm_api_lock;
+	struct cpe_lsm_lab lab;
+	struct cpe_hw_params hw_params;
+	struct snd_pcm_substream *substream;
+
+	wait_queue_head_t event_wait;
+	atomic_t event_avail;
+	atomic_t event_stop;
+
+	u8 ev_det_status;
+	u8 ev_det_pld_size;
+	u8 *ev_det_payload;
+
+	bool cpe_prepared;
+};
+
+static int msm_cpe_afe_mad_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct cpe_priv *cpe = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] = cpe->afe_mad_ctl;
+	return 0;
+}
+
+static int msm_cpe_afe_mad_ctl_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct cpe_priv *cpe = kcontrol->private_data;
+
+	cpe->afe_mad_ctl = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static struct snd_kcontrol_new msm_cpe_kcontrols[] = {
+	SOC_SINGLE_EXT("CPE AFE MAD Enable", SND_SOC_NOPM, 0, 1, 0,
+			msm_cpe_afe_mad_ctl_get, msm_cpe_afe_mad_ctl_put),
+};
+
+/*
+ * cpe_get_private_data: obtain ASoC platform driver private data
+ * @substream: ASoC substream for which private data to be obtained
+ */
+static struct cpe_priv *cpe_get_private_data(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: %s is invalid\n",
+			__func__,
+			(!substream) ? "substream" : "private_data");
+		goto err_ret;
+	}
+
+	rtd = substream->private_data;
+
+	if (!rtd || !rtd->platform) {
+		pr_err("%s: %s is invalid\n",
+			 __func__,
+			(!rtd) ? "runtime" : "platform");
+		goto err_ret;
+	}
+
+	return snd_soc_platform_get_drvdata(rtd->platform);
+
+err_ret:
+	return NULL;
+}
+
+/*
+ * cpe_get_lsm_data: obtain the lsm session data given the substream
+ * @substream: ASoC substream for which lsm session data to be obtained
+ */
+static struct cpe_lsm_data *cpe_get_lsm_data(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	return runtime->private_data;
+}
+
+static void msm_cpe_process_event_status(void *data,
+		u8 detect_status, u8 size, u8 *payload)
+{
+	struct cpe_lsm_data *lsm_d = data;
+
+	lsm_d->ev_det_status = detect_status;
+	lsm_d->ev_det_pld_size = size;
+
+	lsm_d->ev_det_payload = kzalloc(size, GFP_KERNEL);
+	if (!lsm_d->ev_det_payload)
+		return;
+
+	memcpy(lsm_d->ev_det_payload, payload, size);
+
+	atomic_set(&lsm_d->event_avail, 1);
+	wake_up(&lsm_d->event_wait);
+}
+
+static void msm_cpe_process_event_status_done(struct cpe_lsm_data *lsm_data)
+{
+	kfree(lsm_data->ev_det_payload);
+	lsm_data->ev_det_payload = NULL;
+
+	lsm_data->ev_det_status = 0;
+	lsm_data->ev_det_pld_size = 0;
+}
+
+/*
+ * msm_cpe_afe_port_cntl: Perform the afe port control
+ * @substream: substream for which afe port command to be performed
+ * @core_handle: handle to core
+ * @afe_ops: handle to the afe operations
+ * @afe_cfg: afe port configuration data
+ * @cmd: command to be sent to AFE
+ *
+ */
+static int msm_cpe_afe_port_cntl(
+		struct snd_pcm_substream *substream,
+		void *core_handle,
+		struct wcd_cpe_afe_ops *afe_ops,
+		struct wcd_cpe_afe_port_cfg *afe_cfg,
+		int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int rc = 0;
+
+	if (!afe_cfg->port_id) {
+		/*
+		 * It is possible driver can get closed without prepare,
+		 * in which case afe ports will not be initialized.
+		 */
+		dev_dbg(rtd->dev,
+			"%s: Invalid afe port id\n",
+			__func__);
+		return 0;
+	}
+
+	switch (cmd) {
+	case AFE_CMD_PORT_START:
+		rc = afe_ops->afe_port_start(core_handle, afe_cfg);
+		if (rc != 0)
+			dev_err(rtd->dev,
+				"%s: AFE port start failed\n",
+				__func__);
+		break;
+	case AFE_CMD_PORT_SUSPEND:
+		rc = afe_ops->afe_port_suspend(core_handle, afe_cfg);
+		if (rc != 0)
+			dev_err(rtd->dev,
+				"%s: afe_suspend failed, err = %d\n",
+				__func__, rc);
+		break;
+	case AFE_CMD_PORT_RESUME:
+		rc = afe_ops->afe_port_resume(core_handle, afe_cfg);
+		if (rc != 0)
+			dev_err(rtd->dev,
+				"%s: afe_resume failed, err = %d\n",
+				__func__, rc);
+		break;
+	case AFE_CMD_PORT_STOP:
+		rc = afe_ops->afe_port_stop(core_handle, afe_cfg);
+		if (rc != 0)
+			dev_err(rtd->dev,
+				"%s: afe_stopfailed, err = %d\n",
+				__func__, rc);
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_cpe_lsm_lab_stop(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct wcd_cpe_afe_ops *afe_ops;
+	struct cpe_lsm_session *session;
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	struct msm_slim_dma_data *dma_data = NULL;
+	int rc;
+
+	/*
+	 * the caller is not aware of LAB status and will
+	 * try to stop lab even if it is already stopped.
+	 * return success right away is LAB is already stopped
+	 */
+	if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) {
+		dev_dbg(rtd->dev,
+			"%s: lab already stopped\n",
+			__func__);
+		return 0;
+	}
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	lsm_ops = &cpe->lsm_ops;
+	afe_ops = &cpe->afe_ops;
+	session = lsm_d->lsm_session;
+	if (rtd->cpu_dai)
+		dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+					substream);
+	if (!dma_data || !dma_data->dai_channel_ctl) {
+		dev_err(rtd->dev,
+			"%s: dma_data is not set\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (lab_d->thread_status == MSM_LSM_LAB_THREAD_RUNNING) {
+		dev_dbg(rtd->dev, "%s: stopping lab thread\n",
+			__func__);
+		rc = kthread_stop(session->lsm_lab_thread);
+
+		/*
+		 * kthread_stop returns EINTR if the thread_fn
+		 * was not scheduled before calling kthread_stop.
+		 * In this case, we dont need to wait for lab
+		 * thread to complete as lab thread will not be
+		 * scheduled at all.
+		 */
+		if (rc == -EINTR)
+			goto done;
+
+		/* Wait for the lab thread to exit */
+		rc = wait_for_completion_timeout(
+				&lab_d->thread_complete,
+				MSM_CPE_LAB_THREAD_TIMEOUT);
+		if (!rc) {
+			dev_err(rtd->dev,
+				"%s: Wait for lab thread timedout\n",
+				__func__);
+			return -ETIMEDOUT;
+		}
+	}
+
+	rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+				   session,
+				   WCD_CPE_PRE_DISABLE);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: PRE ch teardown failed, err = %d\n",
+			__func__, rc);
+	/* continue with teardown even if any intermediate step fails */
+	rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, false);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: open data failed %d\n", __func__, rc);
+	dma_data->ph = 0;
+
+	/*
+	 * Even though LAB stop failed,
+	 * output AFE port needs to be stopped
+	 */
+	rc = afe_ops->afe_port_stop(cpe->core_handle,
+				    &session->afe_out_port_cfg);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: AFE out port stop failed, err = %d\n",
+			__func__, rc);
+
+	rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+				   session,
+				   WCD_CPE_POST_DISABLE);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: POST ch teardown failed, err = %d\n",
+			__func__, rc);
+
+done:
+	lab_d->thread_status = MSM_LSM_LAB_THREAD_STOP;
+	lab_d->buf_idx = 0;
+	atomic_set(&lab_d->in_count, 0);
+	lab_d->dma_write = 0;
+
+	return 0;
+}
+
+static int msm_cpe_lab_buf_alloc(struct snd_pcm_substream *substream,
+		struct cpe_lsm_session *session,
+		struct msm_slim_dma_data *dma_data)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	struct cpe_hw_params *hw_params = &lsm_d->hw_params;
+	struct cpe_data_pcm_buf *pcm_buf = NULL;
+	int rc = 0;
+	int dma_alloc = 0;
+	u32 count = 0;
+	u32 bufsz, bufcnt;
+
+	if (lab_d->pcm_buf &&
+	    lab_d->pcm_buf->mem) {
+		dev_dbg(rtd->dev,
+			"%s: LAB buf already allocated\n",
+			__func__);
+		goto exit;
+	}
+
+	bufsz = hw_params->buf_sz;
+	bufcnt = hw_params->period_count;
+
+	dev_dbg(rtd->dev,
+		"%s:Buf Size %d Buf count %d\n",
+		 __func__,
+		bufsz, bufcnt);
+
+	pcm_buf = kzalloc(((sizeof(struct cpe_data_pcm_buf)) * bufcnt),
+			  GFP_KERNEL);
+	if (!pcm_buf) {
+		rc = -ENOMEM;
+		goto exit;
+	}
+
+	lab_d->pcm_buf = pcm_buf;
+	dma_alloc = bufsz * bufcnt;
+	pcm_buf->mem = NULL;
+	pcm_buf->mem = dma_alloc_coherent(dma_data->sdev->dev.parent,
+					  dma_alloc,
+					  &(pcm_buf->phys),
+					  GFP_KERNEL);
+	if (!pcm_buf->mem) {
+		dev_err(rtd->dev,
+			"%s:DMA alloc failed size = %x\n",
+			__func__, dma_alloc);
+		rc = -ENOMEM;
+		goto fail;
+	}
+
+	count = 0;
+	while (count < bufcnt) {
+		pcm_buf[count].mem = pcm_buf[0].mem + (count * bufsz);
+		pcm_buf[count].phys = pcm_buf[0].phys + (count * bufsz);
+		dev_dbg(rtd->dev,
+			"%s: pcm_buf[%d].mem %pK pcm_buf[%d].phys %pK\n",
+			 __func__, count,
+			(void *)pcm_buf[count].mem,
+			count, &(pcm_buf[count].phys));
+		count++;
+	}
+
+	return 0;
+fail:
+	if (pcm_buf) {
+		if (pcm_buf->mem)
+			dma_free_coherent(dma_data->sdev->dev.parent, dma_alloc,
+					  pcm_buf->mem, pcm_buf->phys);
+		kfree(pcm_buf);
+		lab_d->pcm_buf = NULL;
+	}
+exit:
+	return rc;
+}
+
+static int msm_cpe_lab_buf_dealloc(struct snd_pcm_substream *substream,
+	struct cpe_lsm_session *session, struct msm_slim_dma_data *dma_data)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	struct cpe_hw_params *hw_params = &lsm_d->hw_params;
+	int rc = 0;
+	int dma_alloc = 0;
+	struct cpe_data_pcm_buf *pcm_buf = NULL;
+	int bufsz, bufcnt;
+
+	bufsz = hw_params->buf_sz;
+	bufcnt = hw_params->period_count;
+
+	dev_dbg(rtd->dev,
+		"%s:Buf Size %d Buf count %d\n", __func__,
+		bufsz, bufcnt);
+
+	if (bufcnt <= 0 || bufsz <= 0) {
+		dev_err(rtd->dev,
+			"%s: Invalid params, bufsz = %u, bufcnt = %u\n",
+			__func__, bufsz, bufcnt);
+		return -EINVAL;
+	}
+
+	pcm_buf = lab_d->pcm_buf;
+	dma_alloc = bufsz * bufcnt;
+	if (dma_data && pcm_buf)
+		dma_free_coherent(dma_data->sdev->dev.parent, dma_alloc,
+				  pcm_buf->mem, pcm_buf->phys);
+	kfree(pcm_buf);
+	lab_d->pcm_buf = NULL;
+	return rc;
+}
+
+/*
+ * msm_cpe_lab_thread: Initiated on KW detection
+ * @data: lab data
+ *
+ * Start lab thread and call CPE core API for SLIM
+ * read operations.
+ */
+static int msm_cpe_lab_thread(void *data)
+{
+	struct cpe_lsm_data *lsm_d = data;
+	struct cpe_lsm_session *session = lsm_d->lsm_session;
+	struct snd_pcm_substream *substream = lsm_d->substream;
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	struct cpe_hw_params *hw_params = &lsm_d->hw_params;
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct wcd_cpe_afe_ops *afe_ops;
+	struct cpe_data_pcm_buf *cur_buf, *next_buf;
+	struct msm_slim_dma_data *dma_data = NULL;
+	struct snd_soc_pcm_runtime *rtd = NULL;
+	bool wait_timedout = false;
+	int rc = 0;
+	u32 done_len = 0;
+	u32 buf_count = 0;
+	u32 prd_cnt;
+
+	allow_signal(SIGKILL);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	pr_debug("%s: Lab thread start\n", __func__);
+	init_completion(&lab_d->comp);
+
+	if (PCM_RUNTIME_CHECK(substream)) {
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (!cpe || !cpe->core_handle) {
+		pr_err("%s: Handle to %s is invalid\n",
+			__func__,
+			(!cpe) ? "cpe" : "core");
+		rc = -EINVAL;
+		goto done;
+	}
+
+	rtd = substream->private_data;
+	if (rtd->cpu_dai)
+		dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+					substream);
+	if (!dma_data || !dma_data->dai_channel_ctl) {
+		pr_err("%s: dma_data is not set\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	lsm_ops = &cpe->lsm_ops;
+	afe_ops = &cpe->afe_ops;
+
+	rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+				   session,
+				   WCD_CPE_PRE_ENABLE);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: PRE ch setup failed, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+
+	rc = dma_data->dai_channel_ctl(dma_data, rtd->cpu_dai, true);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: open data failed %d\n", __func__, rc);
+		goto done;
+	}
+
+	dev_dbg(rtd->dev, "%s: Established data channel\n",
+		__func__);
+
+	init_waitqueue_head(&lab_d->period_wait);
+	memset(lab_d->pcm_buf[0].mem, 0, lab_d->pcm_size);
+
+	rc = slim_port_xfer(dma_data->sdev, dma_data->ph,
+			    lab_d->pcm_buf[0].phys,
+			    hw_params->buf_sz, &lab_d->comp);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: buf[0] slim_port_xfer failed, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+
+	rc = slim_port_xfer(dma_data->sdev, dma_data->ph,
+			    lab_d->pcm_buf[1].phys,
+			    hw_params->buf_sz, &lab_d->comp);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: buf[0] slim_port_xfer failed, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+
+	cur_buf = &lab_d->pcm_buf[0];
+	next_buf = &lab_d->pcm_buf[2];
+	prd_cnt = hw_params->period_count;
+	rc = lsm_ops->lab_ch_setup(cpe->core_handle,
+				   session,
+				   WCD_CPE_POST_ENABLE);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: POST ch setup failed, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+
+	rc = afe_ops->afe_port_start(cpe->core_handle,
+			&session->afe_out_port_cfg);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: AFE out port start failed, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+
+	while (!kthread_should_stop() &&
+	       lab_d->thread_status != MSM_LSM_LAB_THREAD_ERROR) {
+
+		rc = slim_port_xfer(dma_data->sdev, dma_data->ph,
+				    next_buf->phys,
+				    hw_params->buf_sz, &lab_d->comp);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: slim_port_xfer failed, err = %d\n",
+				__func__, rc);
+			lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR;
+		}
+
+		rc = wait_for_completion_timeout(&lab_d->comp, (2 * HZ/10));
+		if (!rc) {
+			dev_err(rtd->dev,
+				"%s: wait timedout for slim buffer\n",
+				__func__);
+			wait_timedout = true;
+		} else {
+			wait_timedout = false;
+		}
+
+		rc = slim_port_get_xfer_status(dma_data->sdev,
+					       dma_data->ph,
+					       &cur_buf->phys, &done_len);
+		if (rc ||
+		    (!rc && wait_timedout)) {
+			dev_err(rtd->dev,
+				"%s: xfer_status failure, rc = %d, wait_timedout = %s\n",
+				__func__, rc,
+				(wait_timedout ? "true" : "false"));
+			lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR;
+		}
+
+		if (done_len ||
+		    ((!done_len) &&
+		     lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR)) {
+			atomic_inc(&lab_d->in_count);
+			lab_d->dma_write += snd_pcm_lib_period_bytes(substream);
+			snd_pcm_period_elapsed(substream);
+			wake_up(&lab_d->period_wait);
+			buf_count++;
+
+			cur_buf = &lab_d->pcm_buf[buf_count % prd_cnt];
+			next_buf = &lab_d->pcm_buf[(buf_count + 2) % prd_cnt];
+			dev_dbg(rtd->dev,
+				"%s: Cur buf.mem = %pK Next Buf.mem = %pK\n"
+				" buf count = 0x%x\n", __func__,
+				cur_buf->mem, next_buf->mem, buf_count);
+		} else {
+			dev_err(rtd->dev,
+				"%s: SB get status, invalid len = 0x%x\n",
+				__func__, done_len);
+		}
+		done_len = 0;
+	}
+
+done:
+	if (rc)
+		lab_d->thread_status = MSM_LSM_LAB_THREAD_ERROR;
+	pr_debug("%s: Exit lab_thread, exit_status=%d, thread_status=%d\n",
+		 __func__, rc, lab_d->thread_status);
+	complete(&lab_d->thread_complete);
+
+	return 0;
+}
+
+/*
+ * msm_cpe_lsm_open: ASoC call to open the stream
+ * @substream: substream that is to be opened
+ *
+ * Create session data for lsm session and open the lsm session
+ * on CPE.
+ */
+static int msm_cpe_lsm_open(struct snd_pcm_substream *substream)
+{
+	struct cpe_lsm_data *lsm_d;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	int rc = 0;
+
+	if (!cpe || !cpe->codec) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	runtime->hw = msm_pcm_hardware_listen;
+
+	rc = snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&constraints_sample_rates);
+	if (rc < 0) {
+		pr_err("snd_pcm_hw_constraint_list failed rc %d\n", rc);
+		return -EINVAL;
+	}
+
+	/* Ensure that buffer size is a multiple of period size */
+	rc = snd_pcm_hw_constraint_integer(runtime,
+					   SNDRV_PCM_HW_PARAM_PERIODS);
+	if (rc < 0) {
+		pr_err("%s: Unable to set pcm_param_periods, rc %d\n",
+			__func__, rc);
+		return -EINVAL;
+	}
+
+	rc = snd_pcm_hw_constraint_minmax(runtime,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+		LISTEN_MIN_NUM_PERIODS * LISTEN_MIN_PERIOD_SIZE,
+		LISTEN_MAX_NUM_PERIODS * LISTEN_MAX_PERIOD_SIZE);
+	if (rc < 0) {
+		pr_err("%s: Unable to set pcm constraints, rc %d\n",
+			__func__, rc);
+		return -EINVAL;
+	}
+
+	cpe->core_handle = wcd_cpe_get_core_handle(cpe->codec);
+
+	if (!cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid handle to codec core\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	lsm_ops = &cpe->lsm_ops;
+	lsm_d = kzalloc(sizeof(struct cpe_lsm_data), GFP_KERNEL);
+	if (!lsm_d) {
+		dev_err(rtd->dev,
+			"%s: ENOMEM for lsm session, size = %zd\n",
+			__func__, sizeof(struct cpe_lsm_data));
+		rc = -ENOMEM;
+		goto fail_return;
+	}
+	mutex_init(&lsm_d->lsm_api_lock);
+
+	lsm_d->lsm_session = lsm_ops->lsm_alloc_session(cpe->core_handle,
+					lsm_d, msm_cpe_process_event_status);
+	if (!lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: session allocation failed",
+			__func__);
+		rc = -EINVAL;
+		goto fail_session_alloc;
+	}
+	/* Explicitly Assign the LAB thread to STOP state */
+	lsm_d->lab.thread_status = MSM_LSM_LAB_THREAD_STOP;
+	lsm_d->lsm_session->started = false;
+	lsm_d->substream = substream;
+	init_waitqueue_head(&lsm_d->lab.period_wait);
+	lsm_d->cpe_prepared = false;
+
+	dev_dbg(rtd->dev, "%s: allocated session with id = %d\n",
+		__func__, lsm_d->lsm_session->id);
+
+
+	rc = lsm_ops->lsm_open_tx(cpe->core_handle, lsm_d->lsm_session,
+				   LSM_VOICE_WAKEUP_APP_V2, 16000);
+	if (rc  < 0) {
+		dev_err(rtd->dev,
+			"%s: OPEN_TX cmd failed, err = %d\n",
+			__func__, rc);
+		goto fail_open_tx;
+	}
+
+	init_waitqueue_head(&lsm_d->event_wait);
+	atomic_set(&lsm_d->event_avail, 0);
+	atomic_set(&lsm_d->event_stop, 0);
+	runtime->private_data = lsm_d;
+
+	return 0;
+
+fail_open_tx:
+	lsm_ops->lsm_dealloc_session(cpe->core_handle, lsm_d->lsm_session);
+
+fail_session_alloc:
+	mutex_destroy(&lsm_d->lsm_api_lock);
+	kfree(lsm_d);
+fail_return:
+	return rc;
+}
+
+/*
+ * msm_cpe_lsm_close: ASoC call to close/cleanup the stream
+ * @substream: substream that is to be closed
+ *
+ * Deallocate the session and release the AFE port. It is not
+ * required to deregister the sound model as long as we close
+ * the lsm session on CPE.
+ */
+static int msm_cpe_lsm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct cpe_lsm_session *session;
+	struct wcd_cpe_afe_ops *afe_ops;
+	struct wcd_cpe_afe_port_cfg *afe_cfg;
+	int rc = 0;
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	lsm_ops = &cpe->lsm_ops;
+	session = lsm_d->lsm_session;
+	afe_ops = &cpe->afe_ops;
+	afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
+
+	/*
+	 * If driver is closed without stopping LAB,
+	 * explicitly stop LAB before cleaning up the
+	 * driver resources.
+	 */
+	rc = msm_cpe_lsm_lab_stop(substream);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: Failed to stop lab, error = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = msm_cpe_afe_port_cntl(substream,
+				   cpe->core_handle,
+				   afe_ops, afe_cfg,
+				   AFE_CMD_PORT_STOP);
+
+	lsm_d->cpe_prepared = false;
+
+	rc = lsm_ops->lsm_close_tx(cpe->core_handle, session);
+	if (rc != 0) {
+		dev_err(rtd->dev,
+			"%s: lsm_close fail, err = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	lsm_ops->lsm_dealloc_session(cpe->core_handle, session);
+	runtime->private_data = NULL;
+	mutex_destroy(&lsm_d->lsm_api_lock);
+	kfree(lsm_d);
+
+	return rc;
+}
+
+static int msm_cpe_lsm_get_conf_levels(
+		struct cpe_lsm_session *session,
+		u8 *conf_levels_ptr)
+{
+	int rc = 0;
+
+	if (session->num_confidence_levels <= 0) {
+		pr_debug("%s: conf_levels (%u), skip set params\n",
+			 __func__,
+			session->num_confidence_levels);
+		goto done;
+	}
+
+	session->conf_levels = kzalloc(session->num_confidence_levels,
+				       GFP_KERNEL);
+	if (!session->conf_levels) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	if (copy_from_user(session->conf_levels,
+			   conf_levels_ptr,
+			   session->num_confidence_levels)) {
+		pr_err("%s: copy_from_user failed for confidence levels %u\n",
+			__func__, session->num_confidence_levels);
+		kfree(session->conf_levels);
+		session->conf_levels = NULL;
+		rc = -EFAULT;
+		goto done;
+	}
+
+done:
+	return rc;
+}
+
+static int msm_cpe_lsm_validate_out_format(
+	struct snd_pcm_substream *substream,
+	struct snd_lsm_output_format_cfg *cfg)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int rc = 0;
+
+	if (!cfg) {
+		dev_err(rtd->dev,
+			"%s: Invalid lsm out cfg\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (cfg->format != LSM_OUT_FORMAT_PCM &&
+	    cfg->format != LSM_OUT_FORMAT_ADPCM) {
+		dev_err(rtd->dev,
+			"%s: Invalid format %u\n",
+			__func__, cfg->format);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (cfg->packing != LSM_OUT_DATA_RAW &&
+	    cfg->packing != LSM_OUT_DATA_PACKED) {
+		dev_err(rtd->dev,
+			"%s: Invalid packing method %u\n",
+			__func__, cfg->packing);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (cfg->events != LSM_OUT_DATA_EVENTS_DISABLED &&
+	    cfg->events != LSM_OUT_DATA_EVENTS_ENABLED) {
+		dev_err(rtd->dev,
+			"%s: Invalid events provided %u\n",
+			__func__, cfg->events);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (cfg->mode != LSM_OUT_TRANSFER_MODE_RT &&
+	    cfg->mode != LSM_OUT_TRANSFER_MODE_FTRT) {
+		dev_err(rtd->dev,
+			"%s: Invalid transfer mode %u\n",
+			__func__, cfg->mode);
+		rc = -EINVAL;
+		goto done;
+	}
+
+done:
+	return rc;
+}
+
+/*
+ * msm_cpe_lsm_ioctl_shared: Shared IOCTL for this platform driver
+ * @substream: ASoC substream for which the operation is invoked
+ * @cmd: command for the ioctl
+ * @arg: argument for the ioctl
+ *
+ * Perform dedicated listen functions like register sound model,
+ * deregister sound model, etc
+ * Called with lsm_api_lock acquired.
+ */
+static int msm_cpe_lsm_ioctl_shared(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	struct snd_lsm_sound_model_v2 snd_model;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct cpe_lsm_session *session;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct msm_slim_dma_data *dma_data = NULL;
+	struct snd_lsm_event_status *user;
+	struct snd_lsm_detection_params det_params;
+	int rc = 0;
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	switch (cmd) {
+	case SNDRV_LSM_STOP_LAB:
+		dev_dbg(rtd->dev,
+			"%s: %s, lab_enable = %d, lab_thread_ststus = %d\n",
+			__func__, "SNDRV_LSM_STOP_LAB",
+			session->lab_enable,
+			lab_d->thread_status);
+
+		if (session->lab_enable &&
+		    lab_d->thread_status != MSM_LSM_LAB_THREAD_STOP) {
+			atomic_inc(&lab_d->abort_read);
+			wake_up(&lab_d->period_wait);
+			rc = msm_cpe_lsm_lab_stop(substream);
+			if (rc) {
+				dev_err(rtd->dev,
+					"%s: stop LAB failed, error = %d\n",
+					__func__, rc);
+				return rc;
+			}
+		} else if (!session->lab_enable) {
+			dev_dbg(rtd->dev,
+				"%s: LAB already stopped\n",
+				__func__);
+		}
+
+		break;
+
+	case SNDRV_LSM_LAB_CONTROL:
+		if (copy_from_user(&session->lab_enable, (void *)arg,
+				   sizeof(u32))) {
+			dev_err(rtd->dev,
+				"%s: copy_from_user failed, size %zd\n",
+				__func__, sizeof(u32));
+			return -EFAULT;
+		}
+
+		dev_dbg(rtd->dev,
+			"%s: %s, lab_enable = %d\n",
+			__func__, "SNDRV_LSM_LAB_CONTROL",
+			session->lab_enable);
+		if (rtd->cpu_dai)
+			dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+						substream);
+		if (!dma_data || !dma_data->dai_channel_ctl) {
+			dev_err(rtd->dev,
+				"%s: dma_data is not set\n", __func__);
+			return -EINVAL;
+		}
+
+		if (session->lab_enable) {
+			rc = msm_cpe_lab_buf_alloc(substream,
+						   session, dma_data);
+			if (rc < 0) {
+				dev_err(rtd->dev,
+					"%s: lab buffer alloc failed, err = %d\n",
+					__func__, rc);
+				return rc;
+			}
+
+			dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+			dma_buf->dev.dev = substream->pcm->card->dev;
+			dma_buf->private_data = NULL;
+			dma_buf->area = lab_d->pcm_buf[0].mem;
+			dma_buf->addr =  lab_d->pcm_buf[0].phys;
+			dma_buf->bytes = (lsm_d->hw_params.buf_sz *
+					lsm_d->hw_params.period_count);
+			init_completion(&lab_d->thread_complete);
+			snd_pcm_set_runtime_buffer(substream,
+						   &substream->dma_buffer);
+			rc = lsm_ops->lsm_lab_control(cpe->core_handle,
+					session, true);
+			if (rc < 0) {
+				dev_err(rtd->dev,
+					"%s: Lab Enable Failed rc %d\n",
+					__func__, rc);
+				return rc;
+			}
+		} else {
+			/*
+			 * It is possible that lab is still enabled
+			 * when trying to de-allocate the lab buffer.
+			 * Make sure to disable lab before de-allocating
+			 * the lab buffer.
+			 */
+			rc = msm_cpe_lsm_lab_stop(substream);
+			if (rc < 0) {
+				dev_err(rtd->dev,
+					"%s: LAB stop failed, error = %d\n",
+					__func__, rc);
+				return rc;
+			}
+			/*
+			 * Buffer has to be de-allocated even if
+			 * lab_control failed.
+			 */
+			rc = msm_cpe_lab_buf_dealloc(substream,
+						     session, dma_data);
+			if (rc < 0) {
+				dev_err(rtd->dev,
+					"%s: lab buffer free failed, err = %d\n",
+					__func__, rc);
+				return rc;
+			}
+		}
+	break;
+	case SNDRV_LSM_REG_SND_MODEL_V2:
+		dev_dbg(rtd->dev,
+			"%s: %s\n",
+			__func__, "SNDRV_LSM_REG_SND_MODEL_V2");
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: Invalid argument to ioctl %s\n",
+				__func__,
+				"SNDRV_LSM_REG_SND_MODEL_V2");
+			return -EINVAL;
+		}
+
+		memcpy(&snd_model, arg,
+			sizeof(struct snd_lsm_sound_model_v2));
+
+		session->num_confidence_levels =
+				snd_model.num_confidence_levels;
+		rc = msm_cpe_lsm_get_conf_levels(session,
+				snd_model.confidence_level);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: %s get_conf_levels fail, err = %d\n",
+				__func__, "SNDRV_LSM_REG_SND_MODEL_V2",
+				rc);
+			break;
+		}
+
+		session->snd_model_data = kzalloc(snd_model.data_size,
+						  GFP_KERNEL);
+		if (!session->snd_model_data) {
+			kfree(session->conf_levels);
+			session->conf_levels = NULL;
+			return -ENOMEM;
+		}
+		session->snd_model_size = snd_model.data_size;
+
+		if (copy_from_user(session->snd_model_data,
+				   snd_model.data, snd_model.data_size)) {
+			dev_err(rtd->dev,
+				"%s: copy_from_user failed for snd_model\n",
+				__func__);
+			kfree(session->conf_levels);
+			kfree(session->snd_model_data);
+			session->conf_levels = NULL;
+			session->snd_model_data = NULL;
+			return -EFAULT;
+		}
+
+		rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session,
+					       session->snd_model_size);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: shared memory allocation failed, err = %d\n",
+			       __func__, rc);
+			kfree(session->snd_model_data);
+			kfree(session->conf_levels);
+			session->snd_model_data = NULL;
+			session->conf_levels = NULL;
+			return rc;
+		}
+
+		rc = lsm_ops->lsm_register_snd_model(cpe->core_handle, session,
+						snd_model.detection_mode,
+						snd_model.detect_failure);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: snd_model_reg failed, err = %d\n",
+			       __func__, rc);
+			lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+			kfree(session->snd_model_data);
+			kfree(session->conf_levels);
+			session->snd_model_data = NULL;
+			session->conf_levels = NULL;
+			return rc;
+		}
+
+		break;
+
+	case SNDRV_LSM_DEREG_SND_MODEL:
+		dev_dbg(rtd->dev,
+			"%s: %s\n",
+			__func__, "SNDRV_LSM_DEREG_SND_MODEL");
+
+		if (session->lab_enable) {
+			/*
+			 * It is possible that lab is still enabled
+			 * when trying to deregister sound model.
+			 * Make sure to disable lab before de-allocating
+			 * the lab buffer.
+			 */
+			rc = msm_cpe_lsm_lab_stop(substream);
+			if (rc) {
+				dev_err(rtd->dev,
+					"%s: LAB stop failed, error = %d\n",
+					__func__, rc);
+				return rc;
+			}
+
+			rc = lsm_ops->lsm_lab_control(cpe->core_handle,
+					session, false);
+			if (rc)
+				dev_err(rtd->dev,
+					"%s: Lab Disable Failed rc %d\n",
+				       __func__, rc);
+
+			dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai,
+							substream);
+			if (!dma_data || !dma_data->dai_channel_ctl)
+				dev_err(rtd->dev,
+					"%s: dma_data is not set\n", __func__);
+
+			/*
+			 * Buffer has to be de-allocated even if
+			 * lab_control failed and/or dma data is invalid.
+			 */
+			rc = msm_cpe_lab_buf_dealloc(substream,
+						session, dma_data);
+			if (rc < 0)
+				dev_err(rtd->dev,
+					"%s: lab buffer free failed, err = %d\n",
+					__func__, rc);
+		}
+
+		rc = lsm_ops->lsm_deregister_snd_model(
+				cpe->core_handle, session);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: snd_model de-reg failed, err = %d\n",
+				__func__, rc);
+			return rc;
+		}
+
+		kfree(session->snd_model_data);
+		kfree(session->conf_levels);
+		session->snd_model_data = NULL;
+		session->conf_levels = NULL;
+
+		rc = lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: LSM shared memory dealloc failed, err = %d\n",
+				__func__, rc);
+			return rc;
+		}
+
+		break;
+
+	case SNDRV_LSM_EVENT_STATUS:
+		dev_dbg(rtd->dev,
+			"%s: %s\n",
+			__func__, "SNDRV_LSM_EVENT_STATUS");
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: Invalid argument to ioctl %s\n",
+				__func__,
+				"SNDRV_LSM_EVENT_STATUS");
+			return -EINVAL;
+		}
+
+		user = arg;
+
+		/*
+		 * Release the api lock before wait to allow
+		 * other IOCTLs to be invoked while waiting
+		 * for event
+		 */
+		MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock,
+				     "lsm_api_lock");
+
+		rc = wait_event_freezable(lsm_d->event_wait,
+				(atomic_read(&lsm_d->event_avail) == 1) ||
+				(atomic_read(&lsm_d->event_stop) == 1));
+
+		MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock,
+				      "lsm_api_lock");
+
+		if (!rc) {
+			if (atomic_read(&lsm_d->event_avail) == 1) {
+				rc = 0;
+				atomic_set(&lsm_d->event_avail, 0);
+				if (lsm_d->ev_det_pld_size >
+					user->payload_size) {
+					dev_err(rtd->dev,
+						"%s: avail pld_bytes = %u, needed = %u\n",
+						__func__,
+						user->payload_size,
+						lsm_d->ev_det_pld_size);
+					return -EINVAL;
+				}
+
+				user->status = lsm_d->ev_det_status;
+				user->payload_size = lsm_d->ev_det_pld_size;
+
+				memcpy(user->payload,
+				       lsm_d->ev_det_payload,
+				       lsm_d->ev_det_pld_size);
+
+			} else if (atomic_read(&lsm_d->event_stop) == 1) {
+				dev_dbg(rtd->dev,
+					"%s: wait_aborted\n", __func__);
+				user->payload_size = 0;
+				rc = 0;
+			}
+		}
+
+		break;
+
+	case SNDRV_LSM_ABORT_EVENT:
+		dev_dbg(rtd->dev,
+			"%s: %s\n",
+			__func__, "SNDRV_LSM_ABORT_EVENT");
+		atomic_set(&lsm_d->event_stop, 1);
+		wake_up(&lsm_d->event_wait);
+		break;
+
+	case SNDRV_LSM_START:
+		dev_dbg(rtd->dev,
+			"%s: %s\n",
+			__func__, "SNDRV_LSM_START");
+		rc = lsm_ops->lsm_start(cpe->core_handle, session);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: lsm_start fail, err = %d\n",
+				__func__, rc);
+			return rc;
+		}
+		session->started = true;
+		break;
+
+	case SNDRV_LSM_STOP:
+		dev_dbg(rtd->dev,
+			"%s: %s, lab_enable = %d, lab_thread_status = %d\n",
+			__func__, "SNDRV_LSM_STOP",
+			session->lab_enable,
+			lab_d->thread_status);
+		if ((session->lab_enable &&
+		     lab_d->thread_status ==
+		     MSM_LSM_LAB_THREAD_RUNNING)) {
+			/* Explicitly stop LAB */
+			rc = msm_cpe_lsm_lab_stop(substream);
+			if (rc) {
+				dev_err(rtd->dev,
+					"%s: lab_stop failed, err = %d\n",
+					__func__, rc);
+				return rc;
+			}
+		}
+
+		rc = lsm_ops->lsm_stop(cpe->core_handle, session);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: lsm_stop fail err = %d\n",
+				__func__, rc);
+
+			return rc;
+		}
+		session->started = false;
+		break;
+
+	case SNDRV_LSM_SET_PARAMS:
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s Invalid argument\n",
+				__func__, "SNDRV_LSM_SET_PARAMS");
+			return -EINVAL;
+		}
+		memcpy(&det_params, arg,
+			sizeof(det_params));
+		if (det_params.num_confidence_levels <= 0) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid confidence levels %u\n",
+				__func__, "SNDRV_LSM_SET_PARAMS",
+				det_params.num_confidence_levels);
+			return -EINVAL;
+		}
+
+		session->num_confidence_levels =
+				det_params.num_confidence_levels;
+		rc = msm_cpe_lsm_get_conf_levels(session,
+						det_params.conf_level);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: %s get_conf_levels fail, err = %d\n",
+				__func__, "SNDRV_LSM_SET_PARAMS",
+				rc);
+			break;
+		}
+
+		rc = lsm_ops->lsm_set_data(cpe->core_handle, session,
+					   det_params.detect_mode,
+					   det_params.detect_failure);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: lsm_set_data failed, err = %d\n",
+				__func__, rc);
+			return rc;
+		}
+
+		kfree(session->conf_levels);
+		session->conf_levels = NULL;
+
+		break;
+
+		case SNDRV_LSM_OUT_FORMAT_CFG: {
+			struct snd_lsm_output_format_cfg u_fmt_cfg;
+
+			if (!arg) {
+				dev_err(rtd->dev,
+					"%s: Invalid argument to ioctl %s\n",
+					__func__, "SNDRV_LSM_OUT_FORMAT_CFG");
+				return -EINVAL;
+			}
+
+			if (copy_from_user(&u_fmt_cfg, arg,
+					   sizeof(u_fmt_cfg))) {
+				dev_err(rtd->dev,
+					"%s: copy_from_user failed for out_fmt_cfg\n",
+					__func__);
+				return -EFAULT;
+			}
+
+			if (msm_cpe_lsm_validate_out_format(substream,
+							    &u_fmt_cfg))
+				return -EINVAL;
+
+			session->out_fmt_cfg.format = u_fmt_cfg.format;
+			session->out_fmt_cfg.pack_mode = u_fmt_cfg.packing;
+			session->out_fmt_cfg.data_path_events =
+						u_fmt_cfg.events;
+			session->out_fmt_cfg.transfer_mode = u_fmt_cfg.mode;
+
+			rc = lsm_ops->lsm_set_fmt_cfg(cpe->core_handle,
+						      session);
+			if (rc) {
+				dev_err(rtd->dev,
+					"%s: lsm_set_fmt_cfg failed, err = %d\n",
+					__func__, rc);
+				return rc;
+			}
+		}
+		break;
+
+	default:
+		dev_dbg(rtd->dev,
+			"%s: Default snd_lib_ioctl cmd 0x%x\n",
+			__func__, cmd);
+		rc = snd_pcm_lib_ioctl(substream, cmd, arg);
+	}
+
+	return rc;
+}
+
+static int msm_cpe_lsm_lab_start(struct snd_pcm_substream *substream,
+		struct snd_lsm_event_status *event_status)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct cpe_lsm_lab *lab_d = NULL;
+	struct cpe_hw_params *hw_params;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct wcd_cpe_afe_ops *afe_ops;
+	struct wcd_cpe_afe_port_cfg *out_port;
+	int rc;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: invalid substream (%pK)\n",
+			__func__, substream);
+		return -EINVAL;
+	}
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+	lab_d = &lsm_d->lab;
+	afe_ops = &cpe->afe_ops;
+	hw_params = &lsm_d->hw_params;
+
+	if (!session->started) {
+		dev_dbg(rtd->dev,
+			"%s: Session is stopped, cannot start LAB\n",
+			__func__);
+		return 0;
+	}
+
+	reinit_completion(&lab_d->thread_complete);
+
+	if (session->lab_enable &&
+	    event_status->status ==
+	    LSM_VOICE_WAKEUP_STATUS_DETECTED) {
+		out_port = &session->afe_out_port_cfg;
+		out_port->port_id = session->afe_out_port_id;
+		out_port->bit_width = hw_params->sample_size;
+		out_port->num_channels = hw_params->channels;
+		out_port->sample_rate = hw_params->sample_rate;
+		dev_dbg(rtd->dev, "%s: port_id= %u, bit_width= %u, rate= %u\n",
+			 __func__, out_port->port_id, out_port->bit_width,
+			out_port->sample_rate);
+
+		rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle,
+					       out_port);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: Failed afe generic config v2, err = %d\n",
+				__func__, rc);
+			return rc;
+		}
+
+		atomic_set(&lab_d->abort_read, 0);
+		dev_dbg(rtd->dev,
+			"%s: KW detected, scheduling LAB thread\n",
+			__func__);
+
+		/*
+		 * Even though thread might be only scheduled and
+		 * not currently running, mark the internal driver
+		 * status to running so driver can cancel this thread
+		 * if it needs to before the thread gets chance to run.
+		 */
+		lab_d->thread_status = MSM_LSM_LAB_THREAD_RUNNING;
+		session->lsm_lab_thread = kthread_run(
+				msm_cpe_lab_thread,
+				lsm_d,
+				"lab_thread");
+	}
+
+	return 0;
+}
+
+static bool msm_cpe_lsm_is_valid_stream(struct snd_pcm_substream *substream,
+		const char *func)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: invalid substream (%pK)\n",
+			func, substream);
+		return false;
+	}
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			func);
+		return false;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			func);
+		return false;
+	}
+
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	if (!lsm_ops) {
+		dev_err(rtd->dev,
+			"%s: Invalid lsm_ops\n", func);
+		return false;
+	}
+
+	return true;
+}
+
+static int msm_cpe_lsm_set_epd(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct snd_lsm_ep_det_thres epd_thres;
+	int rc;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	if (p_info->param_size != sizeof(epd_thres)) {
+		dev_err(rtd->dev,
+			"%s: Invalid param_size %d\n",
+			__func__, p_info->param_size);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (copy_from_user(&epd_thres, p_info->param_data,
+			   p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto done;
+	}
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, &epd_thres,
+				LSM_ENDPOINT_DETECT_THRESHOLD);
+	if (unlikely(rc))
+		dev_err(rtd->dev,
+			"%s: set_one_param(epd_threshold) failed, rc %d\n",
+			__func__, rc);
+done:
+	return rc;
+}
+
+static int msm_cpe_lsm_set_mode(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct snd_lsm_detect_mode det_mode;
+	int rc;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	if (p_info->param_size != sizeof(det_mode)) {
+		dev_err(rtd->dev,
+			"%s: Invalid param_size %d\n",
+			__func__, p_info->param_size);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (copy_from_user(&det_mode, p_info->param_data,
+			   p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto done;
+	}
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, &det_mode,
+				LSM_OPERATION_MODE);
+	if (unlikely(rc))
+		dev_err(rtd->dev,
+			"%s: set_one_param(epd_threshold) failed, rc %d\n",
+			__func__, rc);
+done:
+	return rc;
+}
+
+static int msm_cpe_lsm_set_gain(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	struct snd_lsm_gain gain;
+	int rc;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	if (p_info->param_size != sizeof(gain)) {
+		dev_err(rtd->dev,
+			"%s: Invalid param_size %d\n",
+			__func__, p_info->param_size);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (copy_from_user(&gain, p_info->param_data,
+			   p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto done;
+	}
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, &gain,
+				LSM_GAIN);
+	if (unlikely(rc))
+		dev_err(rtd->dev,
+			"%s: set_one_param(epd_threshold) failed, rc %d\n",
+			__func__, rc);
+done:
+	return rc;
+
+}
+
+static int msm_cpe_lsm_set_conf(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	int rc;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	session->num_confidence_levels =
+			p_info->param_size;
+	rc = msm_cpe_lsm_get_conf_levels(session,
+			p_info->param_data);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: get_conf_levels failed, err = %d\n",
+			__func__, rc);
+		goto done;
+	}
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, NULL,
+				LSM_MIN_CONFIDENCE_LEVELS);
+	if (unlikely(rc))
+		dev_err(rtd->dev,
+			"%s: set_one_param(conf_levels) failed, rc %d\n",
+			__func__, rc);
+done:
+	return rc;
+}
+
+static int msm_cpe_lsm_reg_model(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	int rc;
+	size_t offset;
+	u8 *snd_model_ptr;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	lsm_ops->lsm_get_snd_model_offset(cpe->core_handle,
+			session, &offset);
+	session->snd_model_size = p_info->param_size + offset;
+
+	session->snd_model_data = vzalloc(session->snd_model_size);
+	if (!session->snd_model_data)
+		return -ENOMEM;
+
+	snd_model_ptr = ((u8 *) session->snd_model_data) + offset;
+
+	if (copy_from_user(snd_model_ptr,
+			   p_info->param_data, p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user for snd_model failed\n",
+			__func__);
+		rc = -EFAULT;
+		goto free_snd_model_data;
+	}
+
+	rc = lsm_ops->lsm_shmem_alloc(cpe->core_handle, session,
+				      session->snd_model_size);
+	if (rc != 0) {
+		dev_err(rtd->dev,
+			"%s: shared memory allocation failed, err = %d\n",
+		       __func__, rc);
+		rc = -EINVAL;
+		goto free_snd_model_data;
+	}
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, NULL,
+				LSM_REG_SND_MODEL);
+	if (unlikely(rc)) {
+		dev_err(rtd->dev,
+			"%s: set_one_param(snd_model) failed, rc %d\n",
+			__func__, rc);
+		goto dealloc_shmem;
+	}
+	return 0;
+
+dealloc_shmem:
+	lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+
+free_snd_model_data:
+	vfree(session->snd_model_data);
+	return rc;
+}
+
+static int msm_cpe_lsm_dereg_model(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	int rc;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, NULL,
+				LSM_DEREG_SND_MODEL);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: dereg_snd_model failed\n",
+			__func__);
+	return lsm_ops->lsm_shmem_dealloc(cpe->core_handle, session);
+}
+
+static int msm_cpe_lsm_set_custom(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+	u8 *data;
+	int rc;
+
+	if (!msm_cpe_lsm_is_valid_stream(substream, __func__))
+		return -EINVAL;
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	if (p_info->param_size > MSM_CPE_MAX_CUSTOM_PARAM_SIZE) {
+		dev_err(rtd->dev,
+			"%s: invalid size %d, max allowed %d\n",
+			__func__, p_info->param_size,
+			MSM_CPE_MAX_CUSTOM_PARAM_SIZE);
+		return -EINVAL;
+	}
+
+	data = kzalloc(p_info->param_size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (copy_from_user(data, p_info->param_data,
+			   p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed for custom params, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto err_ret;
+	}
+
+	rc = lsm_ops->lsm_set_one_param(cpe->core_handle,
+				session, p_info, data,
+				LSM_CUSTOM_PARAMS);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: custom_params failed, err = %d\n",
+			__func__, rc);
+err_ret:
+	kfree(data);
+	return rc;
+}
+
+static int msm_cpe_lsm_process_params(struct snd_pcm_substream *substream,
+		struct snd_lsm_module_params *p_data,
+		void *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct lsm_params_info *p_info;
+	int i;
+	int rc = 0;
+
+	p_info = (struct lsm_params_info *) params;
+
+	for (i = 0; i < p_data->num_params; i++) {
+		dev_dbg(rtd->dev,
+			"%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n",
+			__func__, i, p_info->module_id,
+			p_info->param_id, p_info->param_size,
+			p_info->param_type);
+
+		switch (p_info->param_type) {
+		case LSM_ENDPOINT_DETECT_THRESHOLD:
+			rc = msm_cpe_lsm_set_epd(substream, p_info);
+			break;
+		case LSM_OPERATION_MODE:
+			rc = msm_cpe_lsm_set_mode(substream, p_info);
+			break;
+		case LSM_GAIN:
+			rc = msm_cpe_lsm_set_gain(substream, p_info);
+			break;
+		case LSM_MIN_CONFIDENCE_LEVELS:
+			rc = msm_cpe_lsm_set_conf(substream, p_info);
+			break;
+		case LSM_REG_SND_MODEL:
+			rc = msm_cpe_lsm_reg_model(substream, p_info);
+			break;
+		case LSM_DEREG_SND_MODEL:
+			rc = msm_cpe_lsm_dereg_model(substream, p_info);
+			break;
+		case LSM_CUSTOM_PARAMS:
+			rc = msm_cpe_lsm_set_custom(substream, p_info);
+			break;
+		default:
+			dev_err(rtd->dev,
+				"%s: Invalid param_type %d\n",
+				__func__, p_info->param_type);
+			rc = -EINVAL;
+			break;
+		}
+		if (rc) {
+			pr_err("%s: set_param fail for param_type %d\n",
+				__func__, p_info->param_type);
+			return rc;
+		}
+
+		p_info++;
+	}
+
+	return rc;
+}
+
+static int msm_cpe_lsm_ioctl(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	int err = 0;
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: invalid substream (%pK)\n",
+			__func__, substream);
+		return -EINVAL;
+	}
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock,
+			      "lsm_api_lock");
+
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	switch (cmd) {
+	case SNDRV_LSM_REG_SND_MODEL_V2: {
+		struct snd_lsm_sound_model_v2 snd_model;
+
+		if (session->is_topology_used) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "LSM_REG_SND_MODEL_V2");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&snd_model, (void *)arg,
+				   sizeof(struct snd_lsm_sound_model_v2))) {
+			dev_err(rtd->dev,
+				"%s: copy from user failed, size %zd\n",
+				__func__,
+				sizeof(struct snd_lsm_sound_model_v2));
+			err = -EFAULT;
+			goto done;
+		}
+
+		err = msm_cpe_lsm_ioctl_shared(substream, cmd,
+					       &snd_model);
+	}
+		break;
+	case SNDRV_LSM_EVENT_STATUS: {
+		struct snd_lsm_event_status u_event_status;
+		struct snd_lsm_event_status *event_status = NULL;
+		int u_pld_size = 0;
+
+		if (copy_from_user(&u_event_status, (void *)arg,
+				   sizeof(struct snd_lsm_event_status))) {
+			dev_err(rtd->dev,
+				"%s: event status copy from user failed, size %zd\n",
+				__func__,
+				sizeof(struct snd_lsm_event_status));
+			err = -EFAULT;
+			goto done;
+		}
+
+		if (u_event_status.payload_size >
+		    LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+			dev_err(rtd->dev,
+				"%s: payload_size %d is invalid, max allowed = %d\n",
+				__func__, u_event_status.payload_size,
+				LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+			err = -EINVAL;
+			goto done;
+		}
+
+		u_pld_size = sizeof(struct snd_lsm_event_status) +
+				u_event_status.payload_size;
+
+		event_status = kzalloc(u_pld_size, GFP_KERNEL);
+		if (!event_status) {
+			err = -ENOMEM;
+			goto done;
+		} else {
+			event_status->payload_size =
+				u_event_status.payload_size;
+			err = msm_cpe_lsm_ioctl_shared(substream,
+						       cmd, event_status);
+		}
+
+		if (!err  && copy_to_user(arg, event_status, u_pld_size)) {
+			dev_err(rtd->dev,
+				"%s: copy to user failed\n",
+				__func__);
+			kfree(event_status);
+			err = -EFAULT;
+			goto done;
+		}
+
+		msm_cpe_lsm_lab_start(substream, event_status);
+		msm_cpe_process_event_status_done(lsm_d);
+		kfree(event_status);
+	}
+		break;
+	case SNDRV_LSM_SET_PARAMS: {
+		struct snd_lsm_detection_params det_params;
+
+		if (session->is_topology_used) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "SNDRV_LSM_SET_PARAMS");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&det_params, (void *) arg,
+				   sizeof(det_params))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "SNDRV_LSM_SET_PARAMS",
+				sizeof(det_params));
+			err = -EFAULT;
+			goto done;
+		}
+
+		err = msm_cpe_lsm_ioctl_shared(substream, cmd,
+					       &det_params);
+	}
+		break;
+
+	case SNDRV_LSM_SET_MODULE_PARAMS: {
+		struct snd_lsm_module_params p_data;
+		size_t p_size;
+		u8 *params;
+
+		if (!session->is_topology_used) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if not using topology\n",
+				__func__, "SET_MODULE_PARAMS");
+			return -EINVAL;
+		}
+
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s: No Param data to set\n",
+				__func__, "SET_MODULE_PARAMS");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&p_data, arg,
+				   sizeof(p_data))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "p_data", sizeof(p_data));
+			return -EFAULT;
+		}
+
+		if (p_data.num_params > LSM_PARAMS_MAX) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid num_params %d\n",
+				__func__, "SET_MODULE_PARAMS",
+				p_data.num_params);
+			return -EINVAL;
+		}
+
+		p_size = p_data.num_params *
+			 sizeof(struct lsm_params_info);
+
+		if (p_data.data_size != p_size) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid size %zd\n",
+				__func__, "SET_MODULE_PARAMS", p_size);
+
+			return -EFAULT;
+		}
+
+		params = kzalloc(p_size, GFP_KERNEL);
+		if (!params)
+			return -ENOMEM;
+
+		if (copy_from_user(params, p_data.params,
+				   p_data.data_size)) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %d\n",
+				__func__, "params", p_data.data_size);
+			kfree(params);
+			return -EFAULT;
+		}
+
+		err = msm_cpe_lsm_process_params(substream, &p_data, params);
+		if (err)
+			dev_err(rtd->dev,
+				"%s: %s: Failed to set params, err = %d\n",
+				__func__, "SET_MODULE_PARAMS", err);
+		kfree(params);
+		break;
+	}
+	default:
+		err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg);
+		break;
+	}
+
+done:
+	MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock,
+			     "lsm_api_lock");
+	return err;
+}
+
+#ifdef CONFIG_COMPAT
+struct snd_lsm_event_status32 {
+	u16 status;
+	u16 payload_size;
+	u8 payload[0];
+};
+
+struct snd_lsm_sound_model_v2_32 {
+	compat_uptr_t data;
+	compat_uptr_t confidence_level;
+	u32 data_size;
+	enum lsm_detection_mode detection_mode;
+	u8 num_confidence_levels;
+	bool detect_failure;
+};
+
+struct snd_lsm_detection_params_32 {
+	compat_uptr_t conf_level;
+	enum lsm_detection_mode detect_mode;
+	u8 num_confidence_levels;
+	bool detect_failure;
+};
+
+struct lsm_params_info_32 {
+	u32 module_id;
+	u32 param_id;
+	u32 param_size;
+	compat_uptr_t param_data;
+	enum LSM_PARAM_TYPE param_type;
+};
+
+struct snd_lsm_module_params_32 {
+	compat_uptr_t params;
+	u32 num_params;
+	u32 data_size;
+};
+
+enum {
+	SNDRV_LSM_EVENT_STATUS32 =
+		_IOW('U', 0x02, struct snd_lsm_event_status32),
+	SNDRV_LSM_REG_SND_MODEL_V2_32 =
+		_IOW('U', 0x07, struct snd_lsm_sound_model_v2_32),
+	SNDRV_LSM_SET_PARAMS32 =
+		_IOW('U', 0x0A, struct snd_lsm_detection_params_32),
+	SNDRV_LSM_SET_MODULE_PARAMS_32 =
+		_IOW('U', 0x0B, struct snd_lsm_module_params_32),
+};
+
+static int msm_cpe_lsm_ioctl_compat(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	int err = 0;
+	struct snd_soc_pcm_runtime *rtd;
+	struct cpe_priv *cpe = NULL;
+	struct cpe_lsm_data *lsm_d = NULL;
+	struct cpe_lsm_session *session = NULL;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: invalid substream (%pK)\n",
+			__func__, substream);
+		return -EINVAL;
+	}
+
+	rtd = substream->private_data;
+	lsm_d = cpe_get_lsm_data(substream);
+	cpe = cpe_get_private_data(substream);
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	MSM_CPE_LSM_GRAB_LOCK(&lsm_d->lsm_api_lock,
+			      "lsm_api_lock");
+
+	session = lsm_d->lsm_session;
+	lsm_ops = &cpe->lsm_ops;
+
+	switch (cmd) {
+	case SNDRV_LSM_REG_SND_MODEL_V2_32: {
+		struct snd_lsm_sound_model_v2 snd_model;
+		struct snd_lsm_sound_model_v2_32 snd_model32;
+
+		if (session->is_topology_used) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "LSM_REG_SND_MODEL_V2_32");
+			return -EINVAL;
+		}
+
+		dev_dbg(rtd->dev,
+			"%s: ioctl %s\n", __func__,
+			"SNDRV_LSM_REG_SND_MODEL_V2_32");
+
+		if (copy_from_user(&snd_model32, (void *)arg,
+				   sizeof(snd_model32))) {
+			dev_err(rtd->dev,
+				"%s: copy from user failed, size %zd\n",
+				__func__,
+				sizeof(snd_model32));
+			err = -EFAULT;
+			goto done;
+		}
+
+		snd_model.data = compat_ptr(snd_model32.data);
+		snd_model.confidence_level =
+			compat_ptr(snd_model32.confidence_level);
+		snd_model.data_size = snd_model32.data_size;
+		snd_model.detect_failure = snd_model32.detect_failure;
+		snd_model.num_confidence_levels =
+			snd_model32.num_confidence_levels;
+		snd_model.detection_mode = snd_model32.detection_mode;
+
+		cmd = SNDRV_LSM_REG_SND_MODEL_V2;
+		err = msm_cpe_lsm_ioctl_shared(substream, cmd, &snd_model);
+		if (err)
+			dev_err(rtd->dev,
+				"%s: %s failed, error = %d\n",
+				__func__,
+				"SNDRV_LSM_REG_SND_MODEL_V2_32",
+				err);
+	}
+		break;
+	case SNDRV_LSM_EVENT_STATUS32: {
+		struct snd_lsm_event_status *event_status = NULL;
+		struct snd_lsm_event_status u_event_status32;
+		struct snd_lsm_event_status *udata_32 = NULL;
+		int u_pld_size = 0;
+
+		dev_dbg(rtd->dev,
+			"%s: ioctl %s\n", __func__,
+			"SNDRV_LSM_EVENT_STATUS32");
+
+		if (copy_from_user(&u_event_status32, (void *)arg,
+				   sizeof(struct snd_lsm_event_status))) {
+			dev_err(rtd->dev,
+				"%s: event status copy from user failed, size %zd\n",
+				__func__,
+				sizeof(struct snd_lsm_event_status));
+			err = -EFAULT;
+			goto done;
+		}
+
+		if (u_event_status32.payload_size >
+		   LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+			dev_err(rtd->dev,
+				"%s: payload_size %d is invalid, max allowed = %d\n",
+				__func__, u_event_status32.payload_size,
+				LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+			err = -EINVAL;
+			goto done;
+		}
+
+		u_pld_size = sizeof(struct snd_lsm_event_status) +
+				u_event_status32.payload_size;
+		event_status = kzalloc(u_pld_size, GFP_KERNEL);
+		if (!event_status) {
+			dev_err(rtd->dev,
+				"%s: No memory for event status\n",
+				__func__);
+			err = -ENOMEM;
+			goto done;
+		} else {
+			event_status->payload_size =
+				u_event_status32.payload_size;
+			cmd = SNDRV_LSM_EVENT_STATUS;
+			err = msm_cpe_lsm_ioctl_shared(substream,
+						       cmd, event_status);
+			if (err)
+				dev_err(rtd->dev,
+					"%s: %s failed, error = %d\n",
+					__func__,
+					"SNDRV_LSM_EVENT_STATUS32",
+					err);
+		}
+
+		if (!err) {
+			udata_32 = kzalloc(u_pld_size, GFP_KERNEL);
+			if (!udata_32) {
+				dev_err(rtd->dev,
+					"%s: nomem for udata\n",
+					__func__);
+				err = -EFAULT;
+			} else {
+				udata_32->status = event_status->status;
+				udata_32->payload_size =
+					event_status->payload_size;
+				memcpy(udata_32->payload,
+				       event_status->payload,
+				       u_pld_size);
+			}
+		}
+
+		if (!err  && copy_to_user(arg, udata_32,
+					  u_pld_size)) {
+			dev_err(rtd->dev,
+				"%s: copy to user failed\n",
+				__func__);
+			kfree(event_status);
+			kfree(udata_32);
+			err = -EFAULT;
+			goto done;
+		}
+
+		msm_cpe_lsm_lab_start(substream, event_status);
+		msm_cpe_process_event_status_done(lsm_d);
+		kfree(event_status);
+		kfree(udata_32);
+	}
+		break;
+	case SNDRV_LSM_SET_PARAMS32: {
+		struct snd_lsm_detection_params_32 det_params32;
+		struct snd_lsm_detection_params det_params;
+
+		if (session->is_topology_used) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "SNDRV_LSM_SET_PARAMS32");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&det_params32, arg,
+				   sizeof(det_params32))) {
+			err = -EFAULT;
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "SNDRV_LSM_SET_PARAMS_32",
+				sizeof(det_params32));
+		} else {
+			det_params.conf_level =
+				compat_ptr(det_params32.conf_level);
+			det_params.detect_mode =
+				det_params32.detect_mode;
+			det_params.num_confidence_levels =
+				det_params32.num_confidence_levels;
+			det_params.detect_failure =
+				det_params32.detect_failure;
+			cmd = SNDRV_LSM_SET_PARAMS;
+			err = msm_cpe_lsm_ioctl_shared(substream, cmd,
+						  &det_params);
+			if (err)
+				dev_err(rtd->dev,
+					"%s: ioctl %s failed\n", __func__,
+					"SNDRV_LSM_SET_PARAMS");
+		}
+
+		break;
+	}
+
+	case SNDRV_LSM_SET_MODULE_PARAMS_32: {
+		struct snd_lsm_module_params_32 p_data_32;
+		struct snd_lsm_module_params p_data;
+		u8 *params, *params32;
+		size_t p_size;
+		struct lsm_params_info_32 *p_info_32;
+		struct lsm_params_info *p_info;
+		int i;
+
+		if (!session->is_topology_used) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if not using topology\n",
+				__func__, "SET_MODULE_PARAMS_32");
+			return -EINVAL;
+		}
+
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s: No Param data to set\n",
+				__func__, "SET_MODULE_PARAMS_32");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&p_data_32, arg,
+				   sizeof(p_data_32))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "SET_MODULE_PARAMS_32",
+				sizeof(p_data_32));
+			return -EFAULT;
+		}
+
+		p_data.params = compat_ptr(p_data_32.params);
+		p_data.num_params = p_data_32.num_params;
+		p_data.data_size = p_data_32.data_size;
+
+		if (p_data.num_params > LSM_PARAMS_MAX) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid num_params %d\n",
+				__func__, "SET_MODULE_PARAMS_32",
+				p_data.num_params);
+			return -EINVAL;
+		}
+
+		if (p_data.data_size !=
+		    (p_data.num_params * sizeof(struct lsm_params_info_32))) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid size %d\n",
+				__func__, "SET_MODULE_PARAMS_32",
+				p_data.data_size);
+			return -EINVAL;
+		}
+
+		p_size = sizeof(struct lsm_params_info_32) *
+			 p_data.num_params;
+
+		params32 = kzalloc(p_size, GFP_KERNEL);
+		if (!params32)
+			return -ENOMEM;
+
+		p_size = sizeof(struct lsm_params_info) * p_data.num_params;
+		params = kzalloc(p_size, GFP_KERNEL);
+		if (!params) {
+			kfree(params32);
+			return -ENOMEM;
+		}
+
+		if (copy_from_user(params32, p_data.params,
+				   p_data.data_size)) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %d\n",
+				__func__, "params32", p_data.data_size);
+			kfree(params32);
+			kfree(params);
+			return -EFAULT;
+		}
+
+		p_info_32 = (struct lsm_params_info_32 *) params32;
+		p_info = (struct lsm_params_info *) params;
+		for (i = 0; i < p_data.num_params; i++) {
+			p_info->module_id = p_info_32->module_id;
+			p_info->param_id = p_info_32->param_id;
+			p_info->param_size = p_info_32->param_size;
+			p_info->param_data = compat_ptr(p_info_32->param_data);
+			p_info->param_type = p_info_32->param_type;
+
+			p_info_32++;
+			p_info++;
+		}
+
+		err = msm_cpe_lsm_process_params(substream,
+					     &p_data, params);
+		if (err)
+			dev_err(rtd->dev,
+				"%s: Failed to process params, err = %d\n",
+				__func__, err);
+		kfree(params);
+		kfree(params32);
+		break;
+	}
+	default:
+		err = msm_cpe_lsm_ioctl_shared(substream, cmd, arg);
+		break;
+	}
+done:
+	MSM_CPE_LSM_REL_LOCK(&lsm_d->lsm_api_lock,
+			     "lsm_api_lock");
+	return err;
+}
+
+#else
+#define msm_cpe_lsm_ioctl_compat NULL
+#endif
+
+/*
+ * msm_cpe_lsm_prepare: prepare call from ASoC core for this platform
+ * @substream: ASoC substream for which the operation is invoked
+ *
+ * start the AFE port on CPE associated for this listen session
+ */
+static int msm_cpe_lsm_prepare(struct snd_pcm_substream *substream)
+{
+	int rc = 0;
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct wcd_cpe_afe_ops *afe_ops;
+	struct wcd_cpe_afe_port_cfg *afe_cfg;
+	struct cpe_lsm_session *lsm_session;
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_hw_params lsm_param;
+	struct wcd_cpe_lsm_ops *lsm_ops;
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+	if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+	    runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+		pr_err("%s: XRUN ignore for now\n", __func__);
+		return 0;
+	}
+
+	lsm_session = lsm_d->lsm_session;
+	lab_d->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+
+	dev_dbg(rtd->dev,
+		"%s: pcm_size 0x%x", __func__, lab_d->pcm_size);
+
+	if (lsm_d->cpe_prepared) {
+		dev_dbg(rtd->dev, "%s: CPE is alredy prepared\n",
+			__func__);
+		return 0;
+	}
+
+	lsm_ops = &cpe->lsm_ops;
+	afe_ops = &cpe->afe_ops;
+	afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
+
+	switch (cpe->input_port_id) {
+	case AFE_PORT_ID_3:
+		afe_cfg->port_id = AFE_PORT_ID_3;
+		afe_cfg->bit_width = 16;
+		afe_cfg->num_channels = 1;
+		afe_cfg->sample_rate = SAMPLE_RATE_48KHZ;
+		rc = afe_ops->afe_port_cmd_cfg(cpe->core_handle, afe_cfg);
+		break;
+	case AFE_PORT_ID_1:
+	default:
+		afe_cfg->port_id = AFE_PORT_ID_1;
+		afe_cfg->bit_width = 16;
+		afe_cfg->num_channels = 1;
+		afe_cfg->sample_rate = SAMPLE_RATE_16KHZ;
+		rc = afe_ops->afe_set_params(cpe->core_handle,
+					     afe_cfg, cpe->afe_mad_ctl);
+		break;
+	}
+
+	if (rc != 0) {
+		dev_err(rtd->dev,
+			"%s: cpe afe params failed for port = %d, err = %d\n",
+			 __func__, afe_cfg->port_id, rc);
+		return rc;
+	}
+	lsm_param.sample_rate = afe_cfg->sample_rate;
+	lsm_param.num_chs = afe_cfg->num_channels;
+	lsm_param.bit_width = afe_cfg->bit_width;
+	rc = lsm_ops->lsm_set_media_fmt_params(cpe->core_handle, lsm_session,
+					       &lsm_param);
+	if (rc)
+		dev_dbg(rtd->dev,
+			"%s: failed to set lsm media fmt params, err = %d\n",
+			__func__, rc);
+
+	/* Send connect to port (input) */
+	rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+				   &cpe->input_port_id);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: Failed to set connect input port, err=%d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	if (cpe->input_port_id != 3) {
+		rc = lsm_ops->lsm_get_afe_out_port_id(cpe->core_handle,
+						      lsm_session);
+		if (rc != 0) {
+			dev_err(rtd->dev,
+				"%s: failed to get port id, err = %d\n",
+				__func__, rc);
+			return rc;
+		}
+		/* Send connect to port (output) */
+		rc = lsm_ops->lsm_set_port(cpe->core_handle, lsm_session,
+					   &lsm_session->afe_out_port_id);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: Failed to set connect output port, err=%d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+	rc = msm_cpe_afe_port_cntl(substream,
+				   cpe->core_handle,
+				   afe_ops, afe_cfg,
+				   AFE_CMD_PORT_START);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: cpe_afe_port start failed, err = %d\n",
+			__func__, rc);
+	else
+		lsm_d->cpe_prepared = true;
+
+	return rc;
+}
+
+/*
+ * msm_cpe_lsm_trigger: trigger call from ASoC core for this platform
+ * @substream: ASoC substream for which the operation is invoked
+ * @cmd: the trigger command from framework
+ *
+ * suspend/resume the AFE port on CPE associated with listen session
+ */
+static int msm_cpe_lsm_trigger(struct snd_pcm_substream *substream,
+			       int cmd)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct wcd_cpe_afe_ops *afe_ops;
+	struct wcd_cpe_afe_port_cfg *afe_cfg;
+	int afe_cmd = AFE_CMD_INVALID;
+	int rc = 0;
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid private data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid session data\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	afe_ops = &cpe->afe_ops;
+	afe_cfg = &(lsm_d->lsm_session->afe_port_cfg);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		afe_cmd = AFE_CMD_PORT_SUSPEND;
+		break;
+
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		afe_cmd = AFE_CMD_PORT_RESUME;
+		break;
+
+	default:
+		afe_cmd = AFE_CMD_INVALID;
+		dev_dbg(rtd->dev,
+			"%s: unhandled trigger cmd %d\n",
+			__func__, cmd);
+		break;
+	}
+
+	if (afe_cmd != AFE_CMD_INVALID)
+		rc = msm_cpe_afe_port_cntl(substream,
+					   cpe->core_handle,
+					   afe_ops, afe_cfg,
+					   afe_cmd);
+
+	return rc;
+}
+
+static int msm_cpe_lsm_hwparams(struct snd_pcm_substream *substream,
+					struct snd_pcm_hw_params *params)
+{
+
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct cpe_priv *cpe = cpe_get_private_data(substream);
+	struct cpe_lsm_session *session = NULL;
+	struct cpe_hw_params *hw_params = NULL;
+
+	if (!cpe || !cpe->core_handle) {
+		dev_err(rtd->dev,
+			"%s: Invalid %s\n",
+			__func__,
+			(!cpe) ? "cpe" : "core");
+		return -EINVAL;
+	}
+
+	if (!lsm_d || !lsm_d->lsm_session) {
+		dev_err(rtd->dev,
+			"%s: Invalid %s\n",
+			__func__,
+			(!lsm_d) ? "priv_data" : "session");
+		return -EINVAL;
+	}
+
+	session = lsm_d->lsm_session;
+	hw_params = &lsm_d->hw_params;
+	hw_params->buf_sz = (params_buffer_bytes(params)
+				/ params_periods(params));
+	hw_params->period_count = params_periods(params);
+	hw_params->channels = params_channels(params);
+	hw_params->sample_rate = params_rate(params);
+
+	if (params_format(params) == SNDRV_PCM_FORMAT_S16_LE)
+		hw_params->sample_size = 16;
+	else if (params_format(params) ==
+		 SNDRV_PCM_FORMAT_S24_LE)
+		hw_params->sample_size = 24;
+	else if (params_format(params) ==
+		 SNDRV_PCM_FORMAT_S32_LE)
+		hw_params->sample_size = 32;
+	else {
+		dev_err(rtd->dev,
+			"%s: Invalid Format 0x%x\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+
+	dev_dbg(rtd->dev,
+		"%s: Format %d buffer size(bytes) %d period count %d\n"
+		" Channel %d period in bytes 0x%x Period Size 0x%x rate = %d\n",
+		__func__, params_format(params), params_buffer_bytes(params),
+		params_periods(params), params_channels(params),
+		params_period_bytes(params), params_period_size(params),
+		params_rate(params));
+
+	return 0;
+}
+
+static snd_pcm_uframes_t msm_cpe_lsm_pointer(
+				struct snd_pcm_substream *substream)
+{
+
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct cpe_lsm_session *session;
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+
+	session = lsm_d->lsm_session;
+	if (lab_d->dma_write  >= lab_d->pcm_size)
+		lab_d->dma_write = 0;
+	dev_dbg(rtd->dev,
+		"%s:pcm_dma_pos = %d\n",
+		__func__, lab_d->dma_write);
+
+	return bytes_to_frames(runtime, (lab_d->dma_write));
+}
+
+static int msm_cpe_lsm_copy(struct snd_pcm_substream *substream, int a,
+	 snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	struct cpe_lsm_data *lsm_d = cpe_get_lsm_data(substream);
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct cpe_lsm_session *session;
+	struct cpe_lsm_lab *lab_d = &lsm_d->lab;
+	char *pcm_buf;
+	int fbytes = 0;
+	int rc = 0;
+
+	fbytes = frames_to_bytes(runtime, frames);
+	if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+	   runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+		pr_err("%s: XRUN ignore for now\n", __func__);
+		return 0;
+	}
+	session = lsm_d->lsm_session;
+
+	/* Check if buffer reading is already in error state */
+	if (lab_d->thread_status == MSM_LSM_LAB_THREAD_ERROR) {
+		dev_err(rtd->dev,
+			"%s: Bufferring is in error state\n",
+			__func__);
+		/*
+		 * Advance the period so there is no wait in case
+		 * read is invoked even after error is propogated
+		 */
+		atomic_inc(&lab_d->in_count);
+		lab_d->dma_write += snd_pcm_lib_period_bytes(substream);
+		snd_pcm_period_elapsed(substream);
+		return -ENETRESET;
+	} else if (lab_d->thread_status == MSM_LSM_LAB_THREAD_STOP) {
+		dev_err(rtd->dev,
+			"%s: Buferring is in stopped\n",
+			__func__);
+		return -EIO;
+	}
+
+	rc = wait_event_timeout(lab_d->period_wait,
+			(atomic_read(&lab_d->in_count) ||
+			atomic_read(&lab_d->abort_read)),
+			(2 * HZ));
+	if (atomic_read(&lab_d->abort_read)) {
+		pr_debug("%s: LSM LAB Abort read\n", __func__);
+		return -EIO;
+	}
+	if (lab_d->thread_status != MSM_LSM_LAB_THREAD_RUNNING) {
+		pr_err("%s: Lab stopped\n", __func__);
+		return -EIO;
+	}
+	if (!rc) {
+		pr_err("%s:LAB err wait_event_timeout\n", __func__);
+		rc = -EAGAIN;
+		goto fail;
+	}
+	if (lab_d->buf_idx >= (lsm_d->hw_params.period_count))
+		lab_d->buf_idx = 0;
+	pcm_buf = (lab_d->pcm_buf[lab_d->buf_idx].mem);
+	pr_debug("%s: Buf IDX = 0x%x pcm_buf %pK\n",
+		 __func__,  lab_d->buf_idx, pcm_buf);
+	if (pcm_buf) {
+		if (copy_to_user(buf, pcm_buf, fbytes)) {
+			pr_err("Failed to copy buf to user\n");
+			rc = -EFAULT;
+			goto fail;
+		}
+	}
+	lab_d->buf_idx++;
+	atomic_dec(&lab_d->in_count);
+	return 0;
+fail:
+	return rc;
+}
+
+/*
+ * msm_asoc_cpe_lsm_probe: ASoC framework for lsm platform driver
+ * @platform: platform registered with ASoC core
+ *
+ * Allocate the private data for this platform and obtain the ops for
+ * lsm and afe modules from underlying driver. Also find the codec
+ * for this platform as specified by machine driver for ASoC framework.
+ */
+static int msm_asoc_cpe_lsm_probe(struct snd_soc_platform *platform)
+{
+	struct snd_soc_card *card;
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_codec *codec;
+	struct cpe_priv *cpe_priv;
+	const struct snd_kcontrol_new *kcontrol;
+	bool found_runtime = false;
+	const char *cpe_dev_id = "qcom,msm-cpe-lsm-id";
+	u32 port_id = 0;
+	int ret = 0;
+
+	if (!platform || !platform->component.card) {
+		pr_err("%s: Invalid platform or card\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	card = platform->component.card;
+
+	/* Match platform to codec */
+	list_for_each_entry(rtd, &card->rtd_list, list) {
+		if (!rtd->platform)
+			continue;
+		if (!strcmp(rtd->platform->component.name,
+			    platform->component.name)) {
+			found_runtime = true;
+			break;
+		}
+	}
+
+	if (!found_runtime) {
+		dev_err(platform->dev,
+			"%s: Failed to find runtime for platform\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32(platform->dev->of_node, cpe_dev_id,
+				  &port_id);
+	if (ret) {
+		dev_dbg(platform->dev,
+			"%s: missing 0x%x in dt node\n", __func__, port_id);
+		port_id = 1;
+	}
+
+	codec = rtd->codec;
+
+	cpe_priv = kzalloc(sizeof(struct cpe_priv),
+			   GFP_KERNEL);
+	if (!cpe_priv)
+		return -ENOMEM;
+
+	cpe_priv->codec = codec;
+	cpe_priv->input_port_id = port_id;
+	wcd_cpe_get_lsm_ops(&cpe_priv->lsm_ops);
+	wcd_cpe_get_afe_ops(&cpe_priv->afe_ops);
+
+	snd_soc_platform_set_drvdata(platform, cpe_priv);
+	kcontrol = &msm_cpe_kcontrols[0];
+	snd_ctl_add(card->snd_card, snd_ctl_new1(kcontrol, cpe_priv));
+	return 0;
+}
+
+static const struct snd_pcm_ops msm_cpe_lsm_ops = {
+	.open = msm_cpe_lsm_open,
+	.close = msm_cpe_lsm_close,
+	.ioctl = msm_cpe_lsm_ioctl,
+	.prepare = msm_cpe_lsm_prepare,
+	.trigger = msm_cpe_lsm_trigger,
+	.pointer = msm_cpe_lsm_pointer,
+	.copy = msm_cpe_lsm_copy,
+	.hw_params = msm_cpe_lsm_hwparams,
+	.compat_ioctl = msm_cpe_lsm_ioctl_compat,
+};
+
+static struct snd_soc_platform_driver msm_soc_cpe_platform = {
+	.ops = &msm_cpe_lsm_ops,
+	.probe = msm_asoc_cpe_lsm_probe,
+};
+
+/*
+ * msm_cpe_lsm_probe: platform driver probe
+ * @pdev: platform device
+ *
+ * Register the ASoC platform driver with ASoC core
+ */
+static int msm_cpe_lsm_probe(struct platform_device *pdev)
+{
+
+	return snd_soc_register_platform(&pdev->dev,
+					 &msm_soc_cpe_platform);
+}
+
+/*
+ * msm_cpe_lsm_remove: platform driver remove
+ * @pdev: platform device
+ *
+ * Deregister the ASoC platform driver
+ */
+static int msm_cpe_lsm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_cpe_lsm_dt_match[] = {
+	{.compatible = "qcom,msm-cpe-lsm" },
+	{ }
+};
+
+static struct platform_driver msm_cpe_lsm_driver = {
+	.driver = {
+		.name = "msm-cpe-lsm",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(msm_cpe_lsm_dt_match),
+	},
+	.probe = msm_cpe_lsm_probe,
+	.remove = msm_cpe_lsm_remove,
+};
+module_platform_driver(msm_cpe_lsm_driver);
+
+MODULE_DESCRIPTION("CPE LSM platform driver");
+MODULE_DEVICE_TABLE(of, msm_cpe_lsm_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-dai-fe.c b/sound/soc/msm/msm-dai-fe.c
new file mode 100644
index 0000000..081f8b4
--- /dev/null
+++ b/sound/soc/msm/msm-dai-fe.c
@@ -0,0 +1,2555 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+static struct snd_soc_dai_ops msm_fe_dai_ops = {};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+	88200, 96000, 176400, 192000, 352800, 384000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static int multimedia_startup(struct snd_pcm_substream *substream,
+	struct snd_soc_dai *dai)
+{
+	snd_pcm_hw_constraint_list(substream->runtime, 0,
+		SNDRV_PCM_HW_PARAM_RATE,
+		&constraints_sample_rates);
+	return 0;
+}
+
+static int fe_dai_probe(struct snd_soc_dai *dai)
+{
+	struct snd_soc_dapm_route intercon;
+	struct snd_soc_dapm_context *dapm;
+
+	if (!dai || !dai->driver) {
+		pr_err("%s invalid params\n", __func__);
+		return -EINVAL;
+	}
+	dapm = snd_soc_component_get_dapm(dai->component);
+	memset(&intercon, 0, sizeof(intercon));
+	if (dai->driver->playback.stream_name &&
+		dai->driver->playback.aif_name) {
+		dev_dbg(dai->dev, "%s add route for widget %s",
+			   __func__, dai->driver->playback.stream_name);
+		intercon.source = dai->driver->playback.stream_name;
+		intercon.sink = dai->driver->playback.aif_name;
+		dev_dbg(dai->dev, "%s src %s sink %s\n",
+			   __func__, intercon.source, intercon.sink);
+		snd_soc_dapm_add_routes(dapm, &intercon, 1);
+		snd_soc_dapm_ignore_suspend(dapm, intercon.source);
+	}
+	if (dai->driver->capture.stream_name &&
+	   dai->driver->capture.aif_name) {
+		dev_dbg(dai->dev, "%s add route for widget %s",
+			   __func__, dai->driver->capture.stream_name);
+		intercon.sink = dai->driver->capture.stream_name;
+		intercon.source = dai->driver->capture.aif_name;
+		dev_dbg(dai->dev, "%s src %s sink %s\n",
+			   __func__, intercon.source, intercon.sink);
+		snd_soc_dapm_add_routes(dapm, &intercon, 1);
+		snd_soc_dapm_ignore_suspend(dapm, intercon.sink);
+	}
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm_fe_Multimedia_dai_ops = {
+	.startup	= multimedia_startup,
+};
+
+static const struct snd_soc_component_driver msm_fe_dai_component = {
+	.name		= "msm-dai-fe",
+};
+
+static struct snd_soc_dai_driver msm_fe_dais[] = {
+	{
+		.playback = {
+			.stream_name = "MultiMedia1 Playback",
+			.aif_name = "MM_DL1",
+			.rates = (SNDRV_PCM_RATE_8000_384000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia1 Capture",
+			.aif_name = "MM_UL1",
+			.rates = (SNDRV_PCM_RATE_8000_384000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 4,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia1",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia2 Playback",
+			.aif_name = "MM_DL2",
+			.rates = (SNDRV_PCM_RATE_8000_384000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia2 Capture",
+			.aif_name = "MM_UL2",
+			.rates = (SNDRV_PCM_RATE_8000_384000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia2",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "CS-VOICE Playback",
+			.aif_name = "CS-VOICE_DL1",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "CS-VOICE Capture",
+			.aif_name = "CS-VOICE_UL1",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "CS-VOICE",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VoIP Playback",
+			.aif_name = "VOIP_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+					SNDRV_PCM_FMTBIT_SPECIAL,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =	8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "VoIP Capture",
+			.aif_name = "VOIP_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+					SNDRV_PCM_FMTBIT_SPECIAL,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =	8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VoIP",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia3 Playback",
+			.aif_name = "MM_DL3",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 6,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia3 Capture",
+			.aif_name = "MM_UL3",
+			.rates = (SNDRV_PCM_RATE_8000_384000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia3",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia4 Playback",
+			.aif_name = "MM_DL4",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia4",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia5 Playback",
+			.aif_name = "MM_DL5",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia5 Capture",
+			.aif_name = "MM_UL5",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia5",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia6 Playback",
+			.aif_name = "MM_DL6",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia6 Capture",
+			.aif_name = "MM_UL6",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia6",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia7 Playback",
+			.aif_name = "MM_DL7",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia7",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia8 Playback",
+			.aif_name = "MM_DL8",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE |
+						SNDRV_PCM_FMTBIT_S24_3LE |
+						SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia8 Capture",
+			.aif_name = "MM_UL8",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia8",
+		.probe = fe_dai_probe,
+	},
+	/* FE DAIs created for hostless operation purpose */
+	{
+		.playback = {
+			.stream_name = "SLIMBUS0_HOSTLESS Playback",
+			.aif_name = "SLIM0_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS0_HOSTLESS Capture",
+			.aif_name = "SLIM0_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_96000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS1_HOSTLESS Playback",
+			.aif_name = "SLIM1_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS1_HOSTLESS Capture",
+			.aif_name = "SLIM1_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS3_HOSTLESS Playback",
+			.aif_name = "SLIM3_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS3_HOSTLESS Capture",
+			.aif_name = "SLIM3_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS4_HOSTLESS Playback",
+			.aif_name = "SLIM4_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+						SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS4_HOSTLESS Capture",
+			.aif_name = "SLIM4_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS6_HOSTLESS Playback",
+			.aif_name = "SLIM6_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "SLIMBUS8_HOSTLESS Playback",
+			.aif_name = "SLIM8_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.capture = {
+			.stream_name = "SLIMBUS8_HOSTLESS Capture",
+			.aif_name = "SLIM8_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SLIMBUS8_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "INT_FM_HOSTLESS Playback",
+			.aif_name = "INTFM_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT_FM_HOSTLESS Capture",
+			.aif_name = "INTFM_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "INT_FM_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "INT_HFP_BT Hostless Playback",
+			.aif_name = "INTHFP_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     16000,
+		},
+		.capture = {
+			.stream_name = "INT_HFP_BT Hostless Capture",
+			.aif_name = "INTHFP_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "INT_HFP_BT_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "AFE Playback",
+			.aif_name = "PCM_RX",
+			.rates = (SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_48000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "AFE Capture",
+			.aif_name = "PCM_TX",
+			.rates = (SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_48000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "AFE-PROXY",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "HDMI_HOSTLESS Playback",
+			.aif_name = "HDMI_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "HDMI_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "AUXPCM_HOSTLESS Playback",
+			.aif_name = "AUXPCM_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min =     8000,
+			.rate_max =     16000,
+		},
+		.capture = {
+			.stream_name = "AUXPCM_HOSTLESS Capture",
+			.aif_name = "AUXPCM_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min =     8000,
+			.rate_max =    16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "AUXPCM_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VOICE_STUB Playback",
+			.aif_name = "VOICE_STUB_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "VOICE_STUB Capture",
+			.aif_name = "VOICE_STUB_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VOICE_STUB",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VoLTE Playback",
+			.aif_name = "VoLTE_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "VoLTE Capture",
+			.aif_name = "VoLTE_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VoLTE",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MI2S_RX_HOSTLESS Playback",
+			.aif_name = "MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "MI2S_TX_HOSTLESS Capture",
+			.aif_name = "MI2S_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "MI2S_TX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "SEC_I2S_RX_HOSTLESS Playback",
+			.aif_name = "SEC_I2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =    48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_I2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary MI2S_TX Hostless Capture",
+			.aif_name = "PRI_MI2S_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_MI2S_TX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary MI2S_RX Hostless Playback",
+			.aif_name = "PRI_MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =    384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_MI2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary MI2S_TX Hostless Capture",
+			.aif_name = "SEC_MI2S_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_MI2S_TX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary MI2S_RX Hostless Playback",
+			.aif_name = "SEC_MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_MI2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary MI2S_TX Hostless Capture",
+			.aif_name = "TERT_MI2S_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_MI2S_TX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary MI2S_RX Hostless Playback",
+			.aif_name = "TERT_MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =	8000,
+			.rate_max =    384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_MI2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary MI2S_TX Hostless Capture",
+			.aif_name = "QUAT_MI2S_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_MI2S_TX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary MI2S_RX Hostless Playback",
+			.aif_name = "QUAT_MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_MI2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "INT0 MI2S_RX Hostless Playback",
+			.aif_name = "INT0_MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =    192000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "INT0_MI2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "INT4 MI2S_RX Hostless Playback",
+			.aif_name = "INT4_MI2S_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_192000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =    192000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "INT4_MI2S_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	/* TDM Hostless */
+	{
+		.capture = {
+			.stream_name = "Primary TDM0 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_0_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM0 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_0_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM1 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_1_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM1 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_1_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM2 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_2_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM2 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_2_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM3 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_3_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM3 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_3_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM4 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_4_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM4 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_4_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM5 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_5_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM5 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_5_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM6 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_6_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM6 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_6_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM7 Hostless Capture",
+			.aif_name = "PRI_TDM_TX_7_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_TX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM7 Hostless Playback",
+			.aif_name = "PRI_TDM_RX_7_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "PRI_TDM_RX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM0 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_0_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM0 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_0_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM1 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_1_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM1 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_1_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM2 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_2_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM2 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_2_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM3 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_3_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM3 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_3_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM4 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_4_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM4 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_4_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM5 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_5_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM5 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_5_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM6 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_6_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM6 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_6_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM7 Hostless Capture",
+			.aif_name = "SEC_TDM_TX_7_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_TX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM7 Hostless Playback",
+			.aif_name = "SEC_TDM_RX_7_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "SEC_TDM_RX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM0 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_0_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM0 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_0_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM1 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_1_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM1 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_1_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM2 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_2_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM2 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_2_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM3 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_3_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM3 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_3_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM4 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_4_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM4 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_4_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM5 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_5_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM5 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_5_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM6 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_6_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM6 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_6_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM7 Hostless Capture",
+			.aif_name = "TERT_TDM_TX_7_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_TX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM7 Hostless Playback",
+			.aif_name = "TERT_TDM_RX_7_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "TERT_TDM_RX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM0 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_0_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM0 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_0_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_0_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM1 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_1_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM1 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_1_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_1_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM2 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_2_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM2 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_2_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_2_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM3 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_3_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM3 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_3_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_3_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM4 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_4_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM4 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_4_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_4_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM5 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_5_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM5 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_5_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_5_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM6 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_6_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM6 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_6_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_6_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM7 Hostless Capture",
+			.aif_name = "QUAT_TDM_TX_7_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_TX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM7 Hostless Playback",
+			.aif_name = "QUAT_TDM_RX_7_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QUAT_TDM_RX_7_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Voice2 Playback",
+			.aif_name = "VOICE2_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "Voice2 Capture",
+			.aif_name = "VOICE2_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "Voice2",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "Pseudo Playback",
+			.aif_name = "MM_DL9",
+			.rates = (SNDRV_PCM_RATE_8000_48000 |
+					SNDRV_PCM_RATE_KNOT),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "Pseudo Capture",
+			.aif_name = "MM_UL9",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "Pseudo",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "DTMF_RX_HOSTLESS Playback",
+			.aif_name = "DTMF_DL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =	8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "DTMF_RX_HOSTLESS",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "CPE Listen Audio capture",
+			.aif_name = "CPE_LSM_UL_HL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE),
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "CPE_LSM_NOHOST",
+	},
+	{
+		.playback = {
+			.stream_name = "VOLTE_STUB Playback",
+			.aif_name = "VOLTE_STUB_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "VOLTE_STUB Capture",
+			.aif_name = "VOLTE_STUB_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VOLTE_STUB",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VOICE2_STUB Playback",
+			.aif_name = "VOICE2_STUB_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "VOICE2_STUB Capture",
+			.aif_name = "VOICE2_STUB_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VOICE2_STUB",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia9 Playback",
+			.aif_name = "MM_DL9",
+			.rates = (SNDRV_PCM_RATE_8000_384000|
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	384000,
+		},
+		.capture = {
+			.stream_name = "MultiMedia9 Capture",
+			.aif_name = "MM_UL9",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.name = "MultiMedia9",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "QCHAT Playback",
+			.aif_name = "QCHAT_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "QCHAT Capture",
+			.aif_name = "QCHAT_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "QCHAT",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 1 Audio Service Capture",
+			.aif_name = "LSM1_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM1",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 2 Audio Service Capture",
+			.aif_name = "LSM2_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM2",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 3 Audio Service Capture",
+			.aif_name = "LSM3_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM3",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 4 Audio Service Capture",
+			.aif_name = "LSM4_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM4",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 5 Audio Service Capture",
+			.aif_name = "LSM5_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM5",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 6 Audio Service Capture",
+			.aif_name = "LSM6_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM6",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 7 Audio Service Capture",
+			.aif_name = "LSM7_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM7",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "Listen 8 Audio Service Capture",
+			.aif_name = "LSM8_UL_HL",
+			.rates = SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_min = 16000,
+			.rate_max = 16000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "LSM8",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VoWLAN Playback",
+			.aif_name = "VoWLAN_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.capture = {
+			.stream_name = "VoWLAN Capture",
+			.aif_name = "VoWLAN_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VoWLAN",
+		.probe = fe_dai_probe,
+	},
+	/* FE DAIs created for multiple instances of offload playback */
+	{
+		.playback = {
+			.stream_name = "MultiMedia10 Playback",
+			.aif_name = "MM_DL10",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia10",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia11 Playback",
+			.aif_name = "MM_DL11",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia11",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia12 Playback",
+			.aif_name = "MM_DL12",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia12",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia13 Playback",
+			.aif_name = "MM_DL13",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia13",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia14 Playback",
+			.aif_name = "MM_DL14",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia14",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia15 Playback",
+			.aif_name = "MM_DL15",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia15",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "MultiMedia16 Playback",
+			.aif_name = "MM_DL16",
+			.rates = (SNDRV_PCM_RATE_8000_384000 |
+				  SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE |
+				    SNDRV_PCM_FMTBIT_S32_LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =	8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia16",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VoiceMMode1 Playback",
+			.aif_name = "VOICEMMODE1_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+	 },
+		.capture = {
+			.stream_name = "VoiceMMode1 Capture",
+			.aif_name = "VOICEMMODE1_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VoiceMMode1",
+		.probe = fe_dai_probe,
+	},
+	{
+		.playback = {
+			.stream_name = "VoiceMMode2 Playback",
+			.aif_name = "VOICEMMODE2_DL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "VoiceMMode2 Capture",
+			.aif_name = "VOICEMMODE2_UL",
+			.rates = SNDRV_PCM_RATE_8000_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_dai_ops,
+		.name = "VoiceMMode2",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "MultiMedia17 Capture",
+			.aif_name = "MM_UL17",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia17",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "MultiMedia18 Capture",
+			.aif_name = "MM_UL18",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia18",
+		.probe = fe_dai_probe,
+	},
+	{
+		.capture = {
+			.stream_name = "MultiMedia19 Capture",
+			.aif_name = "MM_UL19",
+			.rates = (SNDRV_PCM_RATE_8000_48000|
+					SNDRV_PCM_RATE_KNOT),
+			.formats = (SNDRV_PCM_FMTBIT_S16_LE |
+				    SNDRV_PCM_FMTBIT_S24_LE |
+				    SNDRV_PCM_FMTBIT_S24_3LE),
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_fe_Multimedia_dai_ops,
+		.compress_new = snd_soc_new_compress,
+		.name = "MultiMedia19",
+		.probe = fe_dai_probe,
+	},
+};
+
+static int msm_fe_dai_dev_probe(struct platform_device *pdev)
+{
+
+	dev_dbg(&pdev->dev, "%s: dev name %s\n", __func__,
+		dev_name(&pdev->dev));
+	return snd_soc_register_component(&pdev->dev, &msm_fe_dai_component,
+		msm_fe_dais, ARRAY_SIZE(msm_fe_dais));
+}
+
+static int msm_fe_dai_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_fe_dt_match[] = {
+	{.compatible = "qcom,msm-dai-fe"},
+	{}
+};
+
+static struct platform_driver msm_fe_dai_driver = {
+	.probe  = msm_fe_dai_dev_probe,
+	.remove = msm_fe_dai_dev_remove,
+	.driver = {
+		.name = "msm-dai-fe",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_fe_dt_match,
+	},
+};
+
+static int __init msm_fe_dai_init(void)
+{
+	return platform_driver_register(&msm_fe_dai_driver);
+}
+module_init(msm_fe_dai_init);
+
+static void __exit msm_fe_dai_exit(void)
+{
+	platform_driver_unregister(&msm_fe_dai_driver);
+}
+module_exit(msm_fe_dai_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Frontend DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm-pcm-hostless.c b/sound/soc/msm/msm-pcm-hostless.c
new file mode 100644
index 0000000..35766b4
--- /dev/null
+++ b/sound/soc/msm/msm-pcm-hostless.c
@@ -0,0 +1,82 @@
+/* Copyright (c) 2011-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+
+
+static int msm_pcm_hostless_prepare(struct snd_pcm_substream *substream)
+{
+	if (!substream) {
+		pr_err("%s: invalid params\n", __func__);
+		return -EINVAL;
+	}
+	pm_qos_remove_request(&substream->latency_pm_qos_req);
+	return 0;
+}
+
+static const struct snd_pcm_ops msm_pcm_hostless_ops = {
+	.prepare = msm_pcm_hostless_prepare
+};
+
+static struct snd_soc_platform_driver msm_soc_hostless_platform = {
+	.ops		= &msm_pcm_hostless_ops,
+};
+
+static int msm_pcm_hostless_probe(struct platform_device *pdev)
+{
+
+	pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+	return snd_soc_register_platform(&pdev->dev,
+				   &msm_soc_hostless_platform);
+}
+
+static int msm_pcm_hostless_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_pcm_hostless_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-hostless"},
+	{}
+};
+
+static struct platform_driver msm_pcm_hostless_driver = {
+	.driver = {
+		.name = "msm-pcm-hostless",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_hostless_dt_match,
+	},
+	.probe = msm_pcm_hostless_probe,
+	.remove = msm_pcm_hostless_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	return platform_driver_register(&msm_pcm_hostless_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_hostless_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Hostless platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/msm8996.c b/sound/soc/msm/msm8996.c
new file mode 100644
index 0000000..bc5f7e5
--- /dev/null
+++ b/sound/soc/msm/msm8996.c
@@ -0,0 +1,4006 @@
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9xxx-common.h"
+#include "../codecs/wcd9330.h"
+#include "../codecs/wcd9335.h"
+#include "../codecs/wsa881x.h"
+
+#define DRV_NAME "msm8996-asoc-snd"
+
+#define SAMPLING_RATE_8KHZ      8000
+#define SAMPLING_RATE_16KHZ     16000
+#define SAMPLING_RATE_32KHZ     32000
+#define SAMPLING_RATE_48KHZ     48000
+#define SAMPLING_RATE_96KHZ     96000
+#define SAMPLING_RATE_192KHZ    192000
+#define SAMPLING_RATE_44P1KHZ   44100
+
+#define MSM8996_SPK_ON     1
+#define MSM8996_HIFI_ON    1
+
+#define WCD9XXX_MBHC_DEF_BUTTONS    8
+#define WCD9XXX_MBHC_DEF_RLOADS     5
+#define CODEC_EXT_CLK_RATE         9600000
+#define ADSP_STATE_READY_TIMEOUT_MS    3000
+#define DEV_NAME_STR_LEN            32
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+static int slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim0_tx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim1_tx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int slim0_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int slim1_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ;
+static int slim5_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim5_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+static int slim6_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int slim6_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+
+static struct platform_device *spdev;
+static int ext_us_amp_gpio = -1;
+static int msm8996_spk_control = 1;
+static int msm_slim_0_rx_ch = 1;
+static int msm_slim_0_tx_ch = 1;
+static int msm_slim_1_tx_ch = 1;
+static int msm_slim_5_rx_ch = 1;
+static int msm_slim_6_rx_ch = 1;
+static int msm_hifi_control;
+static int msm_vi_feed_tx_ch = 2;
+
+static int msm_hdmi_rx_ch = 2;
+static int msm_proxy_rx_ch = 2;
+static int hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+static int msm_tert_mi2s_tx_ch = 2;
+
+static bool codec_reg_done;
+
+static const char *const hifi_function[] = {"Off", "On"};
+static const char *const pin_states[] = {"Disable", "active"};
+static const char *const spk_function[] = {"Off", "On"};
+static const char *const slim0_rx_ch_text[] = {"One", "Two"};
+static const char *const slim5_rx_ch_text[] = {"One", "Two"};
+static const char *const slim6_rx_ch_text[] = {"One", "Two"};
+static const char *const slim0_tx_ch_text[] = {"One", "Two", "Three", "Four",
+						"Five", "Six", "Seven",
+						"Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *hdmi_rx_ch_text[] = {"Two", "Three", "Four", "Five",
+					"Six", "Seven", "Eight"};
+static char const *rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *slim5_rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *slim6_rx_bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static char const *slim0_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+					"KHZ_192", "KHZ_44P1", "KHZ_8",
+					"KHZ_16", "KHZ_32"};
+static char const *slim5_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+						  "KHZ_192", "KHZ_44P1"};
+static char const *slim6_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+						  "KHZ_192", "KHZ_44P1"};
+static const char *const proxy_rx_ch_text[] = {"One", "Two", "Three", "Four",
+	"Five", "Six", "Seven", "Eight"};
+
+static char const *hdmi_rx_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+					"KHZ_192"};
+
+static const char *const auxpcm_rate_text[] = {"8000", "16000"};
+static const struct soc_enum msm8996_auxpcm_enum[] = {
+		SOC_ENUM_SINGLE_EXT(2, auxpcm_rate_text),
+};
+
+static struct afe_clk_set mi2s_tx_clk = {
+	AFE_API_VERSION_I2S_CONFIG,
+	Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+	Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+struct msm8996_wsa881x_dev_info {
+	struct device_node *of_node;
+	u32 index;
+};
+
+static struct snd_soc_aux_dev *msm8996_aux_dev;
+static struct snd_soc_codec_conf *msm8996_codec_conf;
+
+struct msm8996_asoc_mach_data {
+	u32 mclk_freq;
+	int us_euro_gpio;
+	int hph_en1_gpio;
+	int hph_en0_gpio;
+	struct snd_info_entry *codec_root;
+};
+
+struct msm8996_asoc_wcd93xx_codec {
+	void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+				   enum afe_config_type config_type);
+	void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+static struct msm8996_asoc_wcd93xx_codec msm8996_codec_fn;
+
+struct msm8996_liquid_dock_dev {
+	int dock_plug_gpio;
+	int dock_plug_irq;
+	int dock_plug_det;
+	struct work_struct irq_work;
+	struct switch_dev audio_sdev;
+};
+static struct msm8996_liquid_dock_dev *msm8996_liquid_dock_dev;
+
+static void *adsp_state_notifier;
+static void *def_tasha_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm);
+static int msm8996_wsa881x_init(struct snd_soc_component *component);
+
+/*
+ * Need to report LINEIN
+ * if R/L channel impedance is larger than 5K ohm
+ */
+static struct wcd_mbhc_config wcd_mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.detect_extn_cable = true,
+	.mono_stero_detection = false,
+	.swap_gnd_mic = NULL,
+	.hs_ext_micbias = true,
+	.key_code[0] = KEY_MEDIA,
+	.key_code[1] = KEY_VOICECOMMAND,
+	.key_code[2] = KEY_VOLUMEUP,
+	.key_code[3] = KEY_VOLUMEDOWN,
+	.key_code[4] = 0,
+	.key_code[5] = 0,
+	.key_code[6] = 0,
+	.key_code[7] = 0,
+	.linein_th = 5000,
+	.moisture_en = true,
+	.mbhc_micbias = MIC_BIAS_2,
+	.anc_micbias = MIC_BIAS_2,
+	.enable_anc_mic_detect = false,
+};
+
+static inline int param_is_mask(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+			(p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+					     int n)
+{
+	return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+	if (bit >= SNDRV_MASK_MAX)
+		return;
+	if (param_is_mask(n)) {
+		struct snd_mask *m = param_to_mask(p, n);
+
+		m->bits[0] = 0;
+		m->bits[1] = 0;
+		m->bits[bit >> 5] |= (1 << (bit & 31));
+	}
+}
+
+static void msm8996_liquid_docking_irq_work(struct work_struct *work)
+{
+	struct msm8996_liquid_dock_dev *dock_dev =
+		container_of(work, struct msm8996_liquid_dock_dev,
+			     irq_work);
+
+	dock_dev->dock_plug_det =
+		gpio_get_value(dock_dev->dock_plug_gpio);
+
+	switch_set_state(&dock_dev->audio_sdev, dock_dev->dock_plug_det);
+	/* notify to audio daemon */
+	sysfs_notify(&dock_dev->audio_sdev.dev->kobj, NULL, "state");
+}
+
+static irqreturn_t msm8996_liquid_docking_irq_handler(int irq, void *dev)
+{
+	struct msm8996_liquid_dock_dev *dock_dev = dev;
+
+	/* switch speakers should not run in interrupt context */
+	schedule_work(&dock_dev->irq_work);
+	return IRQ_HANDLED;
+}
+
+static int msm8996_liquid_init_docking(void)
+{
+	int ret = 0;
+	int dock_plug_gpio = 0;
+
+	/* plug in docking speaker+plug in device OR unplug one of them */
+	u32 dock_plug_irq_flags = IRQF_TRIGGER_RISING |
+				  IRQF_TRIGGER_FALLING |
+				  IRQF_SHARED;
+
+	dock_plug_gpio = of_get_named_gpio(spdev->dev.of_node,
+					   "qcom,dock-plug-det-irq", 0);
+
+	if (dock_plug_gpio >= 0) {
+		msm8996_liquid_dock_dev =
+		 kzalloc(sizeof(*msm8996_liquid_dock_dev), GFP_KERNEL);
+		if (!msm8996_liquid_dock_dev) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+
+		msm8996_liquid_dock_dev->dock_plug_gpio = dock_plug_gpio;
+
+		ret = gpio_request(msm8996_liquid_dock_dev->dock_plug_gpio,
+					   "dock-plug-det-irq");
+		if (ret) {
+			pr_err("%s:failed request msm8996_liquid_dock_plug_gpio err = %d\n",
+				__func__, ret);
+			ret = -EINVAL;
+			goto fail_dock_gpio;
+		}
+
+		msm8996_liquid_dock_dev->dock_plug_det =
+			gpio_get_value(
+				msm8996_liquid_dock_dev->dock_plug_gpio);
+		msm8996_liquid_dock_dev->dock_plug_irq =
+			gpio_to_irq(
+				msm8996_liquid_dock_dev->dock_plug_gpio);
+
+		ret = request_irq(msm8996_liquid_dock_dev->dock_plug_irq,
+				  msm8996_liquid_docking_irq_handler,
+				  dock_plug_irq_flags,
+				  "liquid_dock_plug_irq",
+				  msm8996_liquid_dock_dev);
+		if (ret < 0) {
+			pr_err("%s: Request Irq Failed err = %d\n",
+				__func__, ret);
+			goto fail_dock_gpio;
+		}
+
+		msm8996_liquid_dock_dev->audio_sdev.name =
+						QC_AUDIO_EXTERNAL_SPK_1_EVENT;
+
+		if (switch_dev_register(
+			 &msm8996_liquid_dock_dev->audio_sdev) < 0) {
+			pr_err("%s: dock device register in switch diretory failed\n",
+				__func__);
+			goto fail_switch_dev;
+		}
+
+		INIT_WORK(
+			&msm8996_liquid_dock_dev->irq_work,
+			msm8996_liquid_docking_irq_work);
+	}
+	return 0;
+
+fail_switch_dev:
+	free_irq(msm8996_liquid_dock_dev->dock_plug_irq,
+				msm8996_liquid_dock_dev);
+fail_dock_gpio:
+	gpio_free(msm8996_liquid_dock_dev->dock_plug_gpio);
+exit:
+	kfree(msm8996_liquid_dock_dev);
+	msm8996_liquid_dock_dev = NULL;
+	return ret;
+}
+
+static void msm8996_ext_control(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	pr_debug("%s: msm8996_spk_control = %d", __func__,
+		 msm8996_spk_control);
+	if (msm8996_spk_control == MSM8996_SPK_ON) {
+		snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+		snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+	} else {
+		snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+		snd_soc_dapm_disable_pin(dapm, "Lineout_2 amp");
+	}
+	snd_soc_dapm_sync(dapm);
+}
+
+static int msm8996_get_spk(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm8996_spk_control = %d\n",
+			 __func__, msm8996_spk_control);
+	ucontrol->value.integer.value[0] = msm8996_spk_control;
+	return 0;
+}
+
+static int msm8996_set_spk(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+		 __func__, ucontrol->value.integer.value[0]);
+	if (msm8996_spk_control == ucontrol->value.integer.value[0])
+		return 0;
+
+	msm8996_spk_control = ucontrol->value.integer.value[0];
+	msm8996_ext_control(codec);
+	return 1;
+}
+
+static int msm8996_hifi_ctrl(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct snd_soc_card *card = codec->component.card;
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s: msm_hifi_control = %d", __func__,
+		 msm_hifi_control);
+	if (pdata->hph_en1_gpio < 0) {
+		pr_err("%s: hph_en1_gpio is invalid\n", __func__);
+		return -EINVAL;
+	}
+	if (msm_hifi_control == MSM8996_HIFI_ON) {
+		gpio_direction_output(pdata->hph_en1_gpio, 1);
+		/* 5msec delay needed as per HW requirement */
+		usleep_range(5000, 5010);
+	} else {
+		gpio_direction_output(pdata->hph_en1_gpio, 0);
+	}
+	snd_soc_dapm_sync(dapm);
+	return 0;
+}
+
+static int msm8996_hifi_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_hifi_control = %d\n",
+			 __func__, msm_hifi_control);
+	ucontrol->value.integer.value[0] = msm_hifi_control;
+	return 0;
+}
+
+static int msm8996_hifi_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+		 __func__, ucontrol->value.integer.value[0]);
+
+	msm_hifi_control = ucontrol->value.integer.value[0];
+	msm8996_hifi_ctrl(codec);
+	return 1;
+}
+
+static int msm8996_ext_us_amp_init(void)
+{
+	int ret = 0;
+
+	ext_us_amp_gpio = of_get_named_gpio(spdev->dev.of_node,
+				"qcom,ext-ult-spk-amp-gpio", 0);
+	if (ext_us_amp_gpio >= 0) {
+		ret = gpio_request(ext_us_amp_gpio, "ext_us_amp_gpio");
+		if (ret) {
+			pr_err("%s: ext_us_amp_gpio request failed, ret:%d\n",
+				__func__, ret);
+			return ret;
+		}
+		gpio_direction_output(ext_us_amp_gpio, 0);
+	}
+	return ret;
+}
+
+static void msm8996_ext_us_amp_enable(u32 on)
+{
+	if (on)
+		gpio_direction_output(ext_us_amp_gpio, 1);
+	else
+		gpio_direction_output(ext_us_amp_gpio, 0);
+
+	pr_debug("%s: US Emitter GPIO enable:%s\n", __func__,
+			on ? "Enable" : "Disable");
+}
+
+static int msm_ext_ultrasound_event(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *k, int event)
+{
+	pr_debug("%s()\n", __func__);
+	if (strcmp(w->name, "ultrasound amp")) {
+		if (!gpio_is_valid(ext_us_amp_gpio)) {
+			pr_err("%s: ext_us_amp_gpio isn't configured\n",
+				__func__);
+			return -EINVAL;
+		}
+		if (SND_SOC_DAPM_EVENT_ON(event))
+			msm8996_ext_us_amp_enable(1);
+		else
+			msm8996_ext_us_amp_enable(0);
+	} else {
+		pr_err("%s() Invalid Widget = %s\n",
+				__func__, w->name);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm)
+{
+	int ret = 0;
+
+	if (!strcmp(dev_name(codec->dev), "tasha_codec")) {
+		ret = tasha_cdc_mclk_enable(codec, enable, dapm);
+	} else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int msm8996_mclk_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_snd_enable_codec_ext_clk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_snd_enable_codec_ext_clk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+					   int enable, bool dapm)
+{
+	int ret = 0;
+
+	if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+		ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+	else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int msm8996_mclk_tx_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
+				    struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct snd_soc_card *card = codec->component.card;
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	pr_debug("%s: msm_hifi_control = %d", __func__,
+		 msm_hifi_control);
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		if (msm_hifi_control == MSM8996_HIFI_ON) {
+			if (pdata->hph_en0_gpio < 0) {
+				pr_err("%s: hph_en0_gpio is invalid\n",
+					__func__);
+				ret = -EINVAL;
+				goto err;
+			}
+			gpio_direction_output(pdata->hph_en0_gpio, 1);
+		}
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		if (msm_hifi_control == MSM8996_HIFI_ON) {
+			if (pdata->hph_en0_gpio < 0) {
+				pr_err("%s: hph_en0_gpio is invalid\n",
+					__func__);
+				ret = -EINVAL;
+				goto err;
+			}
+			gpio_direction_output(pdata->hph_en0_gpio, 0);
+		}
+		break;
+	}
+err:
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget msm8996_dapm_widgets[] = {
+
+	SND_SOC_DAPM_SUPPLY("MCLK",  SND_SOC_NOPM, 0, 0,
+	msm8996_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("MCLK TX",  SND_SOC_NOPM, 0, 0,
+	msm8996_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
+	SND_SOC_DAPM_SPK("ultrasound amp", msm_ext_ultrasound_event),
+	SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic7", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic8", NULL),
+
+	SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+};
+
+static struct snd_soc_dapm_route wcd9335_audio_paths[] = {
+	{"MIC BIAS1", NULL, "MCLK TX"},
+	{"MIC BIAS2", NULL, "MCLK TX"},
+	{"MIC BIAS3", NULL, "MCLK TX"},
+	{"MIC BIAS4", NULL, "MCLK TX"},
+};
+
+static int slim5_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val = 0;
+
+	switch (slim5_rx_sample_rate) {
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: slim5_rx_sample_rate = %d\n", __func__,
+		 slim5_rx_sample_rate);
+
+	return 0;
+}
+
+static int slim5_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: ucontrol value = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		slim5_rx_sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 2:
+		slim5_rx_sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		slim5_rx_sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+	default:
+		slim5_rx_sample_rate = SAMPLING_RATE_48KHZ;
+	}
+
+	pr_debug("%s: slim5_rx_sample_rate = %d\n", __func__,
+		 slim5_rx_sample_rate);
+
+	return 0;
+}
+
+static int slim6_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val = 0;
+
+	switch (slim6_rx_sample_rate) {
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: slim6_rx_sample_rate = %d\n", __func__,
+		 slim6_rx_sample_rate);
+
+	return 0;
+}
+
+static int slim6_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		slim6_rx_sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 2:
+		slim6_rx_sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		slim6_rx_sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+	default:
+		slim6_rx_sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: ucontrol value = %ld, slim6_rx_sample_rate = %d\n",
+		 __func__, ucontrol->value.integer.value[0],
+		 slim6_rx_sample_rate);
+
+	return 0;
+}
+
+static int slim0_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	switch (slim0_tx_bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: slim0_tx_bit_format = %d, ucontrol value = %ld\n",
+			 __func__, slim0_tx_bit_format,
+			ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int slim0_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		slim0_tx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+		slim0_tx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	default:
+		pr_err("%s: invalid value %ld\n", __func__,
+		       ucontrol->value.integer.value[0]);
+		rc = -EINVAL;
+		break;
+	}
+
+	pr_debug("%s: ucontrol value = %ld, slim0_tx_bit_format = %d\n",
+		 __func__, ucontrol->value.integer.value[0],
+		 slim0_tx_bit_format);
+
+	return rc;
+}
+
+static int slim0_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val = 0;
+
+	switch (slim0_rx_sample_rate) {
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 6;
+		break;
+
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 5;
+		break;
+
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 4;
+		break;
+
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__,
+		 slim0_rx_sample_rate);
+
+	return 0;
+}
+
+static int slim0_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: ucontrol value = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 6:
+		slim0_rx_sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 5:
+		slim0_rx_sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 4:
+		slim0_rx_sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 3:
+		slim0_rx_sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 2:
+		slim0_rx_sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		slim0_rx_sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+	default:
+		slim0_rx_sample_rate = SAMPLING_RATE_48KHZ;
+	}
+
+	pr_debug("%s: slim0_rx_sample_rate = %d\n", __func__,
+		 slim0_rx_sample_rate);
+
+	return 0;
+}
+
+static int slim0_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val = 0;
+
+	switch (slim0_tx_sample_rate) {
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: slim0_tx_sample_rate = %d\n", __func__,
+				slim0_tx_sample_rate);
+	return 0;
+}
+
+static int slim0_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	pr_debug("%s: ucontrol value = %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		slim0_tx_sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		slim0_tx_sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+		slim0_tx_sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	default:
+		rc = -EINVAL;
+		pr_err("%s: invalid sample rate being passed\n", __func__);
+		break;
+	}
+
+	pr_debug("%s: slim0_tx_sample_rate = %d\n", __func__,
+			slim0_tx_sample_rate);
+	return rc;
+}
+
+static int slim5_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+
+	switch (slim5_rx_bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: slim5_rx_bit_format = %d, ucontrol value = %ld\n",
+		 __func__, slim5_rx_bit_format,
+			ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int slim5_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		slim5_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		slim5_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		slim5_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return 0;
+}
+
+static int slim6_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+
+	switch (slim6_rx_bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: slim6_rx_bit_format = %d, ucontrol value = %ld\n",
+		 __func__, slim6_rx_bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int slim6_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		slim6_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		slim6_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		slim6_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return 0;
+}
+
+static int slim0_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+
+	switch (slim0_rx_bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: slim0_rx_bit_format = %d, ucontrol value = %ld\n",
+		 __func__, slim0_rx_bit_format,
+			ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int slim0_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		slim0_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		slim0_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return 0;
+}
+
+static int msm_slim_5_rx_ch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_slim_5_rx_ch  = %d\n", __func__,
+		 msm_slim_5_rx_ch);
+	ucontrol->value.integer.value[0] = msm_slim_5_rx_ch - 1;
+	return 0;
+}
+
+static int msm_slim_5_rx_ch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	msm_slim_5_rx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm_slim_5_rx_ch = %d\n", __func__,
+		 msm_slim_5_rx_ch);
+	return 1;
+}
+
+static int msm_slim_6_rx_ch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_slim_6_rx_ch  = %d\n", __func__,
+		 msm_slim_6_rx_ch);
+	ucontrol->value.integer.value[0] = msm_slim_6_rx_ch - 1;
+	return 0;
+}
+
+static int msm_slim_6_rx_ch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	msm_slim_6_rx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm_slim_6_rx_ch = %d\n", __func__,
+		 msm_slim_6_rx_ch);
+	return 1;
+}
+
+static int msm_slim_0_rx_ch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_slim_0_rx_ch  = %d\n", __func__,
+		 msm_slim_0_rx_ch);
+	ucontrol->value.integer.value[0] = msm_slim_0_rx_ch - 1;
+	return 0;
+}
+
+static int msm_slim_0_rx_ch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	msm_slim_0_rx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm_slim_0_rx_ch = %d\n", __func__,
+		 msm_slim_0_rx_ch);
+	return 1;
+}
+
+static int msm_slim_0_tx_ch_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_slim_0_tx_ch  = %d\n", __func__,
+		 msm_slim_0_tx_ch);
+	ucontrol->value.integer.value[0] = msm_slim_0_tx_ch - 1;
+	return 0;
+}
+
+static int msm_slim_0_tx_ch_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	msm_slim_0_tx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm_slim_0_tx_ch = %d\n", __func__, msm_slim_0_tx_ch);
+	return 1;
+}
+
+static int msm_slim_1_tx_ch_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_slim_1_tx_ch  = %d\n", __func__,
+		 msm_slim_1_tx_ch);
+	ucontrol->value.integer.value[0] = msm_slim_1_tx_ch - 1;
+	return 0;
+}
+
+static int msm_slim_1_tx_ch_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	msm_slim_1_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: msm_slim_1_tx_ch = %d\n", __func__, msm_slim_1_tx_ch);
+	return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+	pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+	return 1;
+}
+
+static int hdmi_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+
+	switch (hdmi_rx_bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+		 __func__, hdmi_rx_bit_format,
+			ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int hdmi_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		hdmi_rx_bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: hdmi_rx_bit_format = %d, ucontrol value = %ld\n",
+		 __func__, hdmi_rx_bit_format,
+			ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_hdmi_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_hdmi_rx_ch  = %d\n", __func__,
+		 msm_hdmi_rx_ch);
+	ucontrol->value.integer.value[0] = msm_hdmi_rx_ch - 2;
+
+	return 0;
+}
+
+static int msm_hdmi_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	msm_hdmi_rx_ch = ucontrol->value.integer.value[0] + 2;
+	if (msm_hdmi_rx_ch > 8) {
+		pr_err("%s: channels %d exceeded 8.Limiting to max chs-8\n",
+			__func__, msm_hdmi_rx_ch);
+		msm_hdmi_rx_ch = 8;
+	}
+	pr_debug("%s: msm_hdmi_rx_ch = %d\n", __func__, msm_hdmi_rx_ch);
+
+	return 1;
+}
+
+static int hdmi_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val = 0;
+
+	switch (hdmi_rx_sample_rate) {
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+		 hdmi_rx_sample_rate);
+
+	return 0;
+}
+
+static int hdmi_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: ucontrol value = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		hdmi_rx_sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		hdmi_rx_sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+	default:
+		hdmi_rx_sample_rate = SAMPLING_RATE_48KHZ;
+	}
+
+	pr_debug("%s: hdmi_rx_sample_rate = %d\n", __func__,
+		 hdmi_rx_sample_rate);
+
+	return 0;
+}
+
+static int msm8996_auxpcm_rate_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm8996_auxpcm_rate;
+	return 0;
+}
+
+static int msm8996_auxpcm_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		msm8996_auxpcm_rate = SAMPLING_RATE_16KHZ;
+		break;
+	default:
+		msm8996_auxpcm_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	return 0;
+}
+
+static int msm_proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+	ucontrol->value.integer.value[0] = msm_proxy_rx_ch - 1;
+	return 0;
+}
+
+static int msm_proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	msm_proxy_rx_ch = ucontrol->value.integer.value[0] + 1;
+	pr_debug("%s: msm_proxy_rx_ch = %d\n", __func__, msm_proxy_rx_ch);
+	return 1;
+}
+
+static int msm_auxpcm_be_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				      struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate =
+	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+
+	struct snd_interval *channels =
+	    hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	rate->min = rate->max = msm8996_auxpcm_rate;
+	channels->min = channels->max = 1;
+
+	return 0;
+}
+
+static int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s: msm_proxy_rx_ch =%d\n", __func__, msm_proxy_rx_ch);
+
+	if (channels->max < 2)
+		channels->min = channels->max = 2;
+	channels->min = channels->max = msm_proxy_rx_ch;
+	rate->min = rate->max = SAMPLING_RATE_48KHZ;
+	return 0;
+}
+
+static int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					   struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	rate->min = rate->max = SAMPLING_RATE_48KHZ;
+	return 0;
+}
+
+static int msm8996_hdmi_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					      struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s channels->min %u channels->max %u ()\n", __func__,
+		 channels->min, channels->max);
+
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				hdmi_rx_bit_format);
+	if (channels->max < 2)
+		channels->min = channels->max = 2;
+	rate->min = rate->max = hdmi_rx_sample_rate;
+	channels->min = channels->max = msm_hdmi_rx_ch;
+
+	return 0;
+}
+
+static int msm_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				     struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s: channel:%d\n", __func__, msm_tert_mi2s_tx_ch);
+	rate->min = rate->max = SAMPLING_RATE_48KHZ;
+	channels->min = channels->max = msm_tert_mi2s_tx_ch;
+	return 0;
+}
+
+static int msm8996_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+	pr_debug("%s: substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	mi2s_tx_clk.enable = 1;
+	ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+				&mi2s_tx_clk);
+	if (ret < 0) {
+		pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret);
+		goto err;
+	}
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		pr_err("%s: set fmt cpu dai failed, err:%d\n", __func__, ret);
+err:
+	return ret;
+}
+
+static void msm8996_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	pr_debug("%s: substream = %s  stream = %d\n", __func__,
+		substream->name, substream->stream);
+
+	mi2s_tx_clk.enable = 0;
+	ret = afe_set_lpass_clock_v2(AFE_PORT_ID_TERTIARY_MI2S_TX,
+				&mi2s_tx_clk);
+	if (ret < 0)
+		pr_err("%s: afe lpass clock failed, err:%d\n", __func__, ret);
+}
+
+static struct snd_soc_ops msm8996_mi2s_be_ops = {
+	.startup = msm8996_mi2s_snd_startup,
+	.shutdown = msm8996_mi2s_snd_shutdown,
+};
+
+static int msm_slim_5_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   slim5_rx_bit_format);
+	rate->min = rate->max = slim5_rx_sample_rate;
+	channels->min = channels->max = msm_slim_5_rx_ch;
+
+	 pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+		  __func__, params_format(params), params_rate(params),
+		  msm_slim_5_rx_ch);
+
+	return 0;
+}
+
+static int msm_slim_6_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   slim6_rx_bit_format);
+	rate->min = rate->max = slim6_rx_sample_rate;
+	channels->min = channels->max = msm_slim_6_rx_ch;
+
+	pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+		 __func__, params_format(params), params_rate(params),
+		 msm_slim_6_rx_ch);
+
+	return 0;
+}
+
+static int msm_slim_0_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   slim0_rx_bit_format);
+	rate->min = rate->max = slim0_rx_sample_rate;
+	channels->min = channels->max = msm_slim_0_rx_ch;
+
+	 pr_debug("%s: format = %d, rate = %d, channels = %d\n",
+		  __func__, params_format(params), params_rate(params),
+		  msm_slim_0_rx_ch);
+
+	return 0;
+}
+
+static int msm_slim_0_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s()\n", __func__);
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, slim0_tx_bit_format);
+	rate->min = rate->max = slim0_tx_sample_rate;
+	channels->min = channels->max = msm_slim_0_tx_ch;
+
+	return 0;
+}
+
+static int msm_slim_1_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s()\n", __func__);
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT, slim1_tx_bit_format);
+	rate->min = rate->max = slim1_tx_sample_rate;
+	channels->min = channels->max = msm_slim_1_tx_ch;
+
+	return 0;
+}
+
+static int msm_slim_4_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+		       SNDRV_PCM_FORMAT_S32_LE);
+
+	rate->min = rate->max = SAMPLING_RATE_8KHZ;
+	channels->min = channels->max = msm_vi_feed_tx_ch;
+	pr_debug("%s: msm_vi_feed_tx_ch: %d\n", __func__, msm_vi_feed_tx_ch);
+
+	return 0;
+}
+
+static int msm_slim_5_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+					    struct snd_pcm_hw_params *params)
+{
+	int rc = 0;
+	void *config = NULL;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s: enter\n", __func__);
+	rate->min = rate->max = SAMPLING_RATE_16KHZ;
+	channels->min = channels->max = 1;
+
+	config = msm8996_codec_fn.get_afe_config_fn(codec,
+				AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+	if (config) {
+		rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG, config,
+				    SLIMBUS_5_TX);
+		if (rc) {
+			pr_err("%s: Failed to set slimbus slave port config %d\n",
+				__func__, rc);
+		}
+	}
+
+	return rc;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	pr_debug("%s:\n", __func__);
+	rate->min = rate->max = SAMPLING_RATE_48KHZ;
+	return 0;
+}
+
+static const struct soc_enum msm_snd_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, spk_function),
+	SOC_ENUM_SINGLE_EXT(2, slim0_rx_ch_text),
+	SOC_ENUM_SINGLE_EXT(8, slim0_tx_ch_text),
+	SOC_ENUM_SINGLE_EXT(7, hdmi_rx_ch_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(rx_bit_format_text),
+			    rx_bit_format_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim0_rx_sample_rate_text),
+			    slim0_rx_sample_rate_text),
+	SOC_ENUM_SINGLE_EXT(8, proxy_rx_ch_text),
+	SOC_ENUM_SINGLE_EXT(3, hdmi_rx_sample_rate_text),
+	SOC_ENUM_SINGLE_EXT(4, slim5_rx_sample_rate_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim5_rx_bit_format_text),
+			    slim5_rx_bit_format_text),
+	SOC_ENUM_SINGLE_EXT(2, slim5_rx_ch_text),
+	SOC_ENUM_SINGLE_EXT(2, hifi_function),
+	SOC_ENUM_SINGLE_EXT(2, vi_feed_ch_text),
+	SOC_ENUM_SINGLE_EXT(4, slim6_rx_sample_rate_text),
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(slim6_rx_bit_format_text),
+			    slim6_rx_bit_format_text),
+	SOC_ENUM_SINGLE_EXT(2, slim6_rx_ch_text),
+};
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+	SOC_ENUM_EXT("Speaker Function", msm_snd_enum[0], msm8996_get_spk,
+			msm8996_set_spk),
+	SOC_ENUM_EXT("SLIM_0_RX Channels", msm_snd_enum[1],
+			msm_slim_0_rx_ch_get, msm_slim_0_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_5_RX Channels", msm_snd_enum[10],
+			msm_slim_5_rx_ch_get, msm_slim_5_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_6_RX Channels", msm_snd_enum[15],
+			msm_slim_6_rx_ch_get, msm_slim_6_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_TX Channels", msm_snd_enum[2],
+			msm_slim_0_tx_ch_get, msm_slim_0_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_1_TX Channels", msm_snd_enum[2],
+			msm_slim_1_tx_ch_get, msm_slim_1_tx_ch_put),
+	SOC_ENUM_EXT("AUX PCM SampleRate", msm8996_auxpcm_enum[0],
+			msm8996_auxpcm_rate_get,
+			msm8996_auxpcm_rate_put),
+	SOC_ENUM_EXT("HDMI_RX Channels", msm_snd_enum[3],
+			msm_hdmi_rx_ch_get, msm_hdmi_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_RX Format", msm_snd_enum[4],
+			slim0_rx_bit_format_get, slim0_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_5_RX Format", msm_snd_enum[9],
+			slim5_rx_bit_format_get, slim5_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_6_RX Format", msm_snd_enum[14],
+			slim6_rx_bit_format_get, slim6_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_0_RX SampleRate", msm_snd_enum[5],
+			slim0_rx_sample_rate_get, slim0_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_5_RX SampleRate", msm_snd_enum[8],
+			slim5_rx_sample_rate_get, slim5_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_6_RX SampleRate", msm_snd_enum[13],
+			slim6_rx_sample_rate_get, slim6_rx_sample_rate_put),
+	SOC_ENUM_EXT("HDMI_RX Bit Format", msm_snd_enum[4],
+			hdmi_rx_bit_format_get, hdmi_rx_bit_format_put),
+	SOC_ENUM_EXT("PROXY_RX Channels", msm_snd_enum[6],
+			msm_proxy_rx_ch_get, msm_proxy_rx_ch_put),
+	SOC_ENUM_EXT("HDMI_RX SampleRate", msm_snd_enum[7],
+			hdmi_rx_sample_rate_get, hdmi_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_0_TX SampleRate", msm_snd_enum[5],
+			slim0_tx_sample_rate_get, slim0_tx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_0_TX Format", msm_snd_enum[4],
+			slim0_tx_bit_format_get, slim0_tx_bit_format_put),
+	SOC_ENUM_EXT("HiFi Function", msm_snd_enum[11], msm8996_hifi_get,
+			msm8996_hifi_put),
+	SOC_ENUM_EXT("VI_FEED_TX Channels", msm_snd_enum[12],
+			msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+};
+
+static bool msm8996_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+
+	pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+	gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+	return true;
+}
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+	int rc;
+	void *config_data = NULL;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (!msm8996_codec_fn.get_afe_config_fn) {
+		dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTERS_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set codec registers config %d\n",
+					__func__, rc);
+			return rc;
+		}
+	}
+
+	config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTER_PAGE_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+				    0);
+		if (rc)
+			pr_err("%s: Failed to set cdc register page config\n",
+				__func__);
+	}
+
+	config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+			AFE_SLIMBUS_SLAVE_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set slimbus slave config %d\n",
+					__func__, rc);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+	afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+	afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int  msm8996_adsp_state_callback(struct notifier_block *nb,
+					   unsigned long value, void *priv)
+{
+	if (value == SUBSYS_BEFORE_SHUTDOWN) {
+		pr_debug("%s: ADSP is about to shutdown. Clearing AFE config\n",
+			 __func__);
+		msm_afe_clear_config();
+	} else if (value == SUBSYS_AFTER_POWERUP) {
+		pr_debug("%s: ADSP is up\n", __func__);
+	}
+
+	return NOTIFY_OK;
+}
+
+static struct notifier_block adsp_state_notifier_block = {
+	.notifier_call = msm8996_adsp_state_callback,
+	.priority = -INT_MAX,
+};
+
+static int msm8996_wcd93xx_codec_up(struct snd_soc_codec *codec)
+{
+	int err;
+	unsigned long timeout;
+	int adsp_ready = 0;
+
+	timeout = jiffies +
+		msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+	do {
+		if (!q6core_is_adsp_ready()) {
+			pr_err_ratelimited("%s: ADSP Audio isn't ready\n",
+					   __func__);
+			/*
+			 * ADSP will be coming up after subsystem restart and
+			 * it might not be fully up when the control reaches
+			 * here. So, wait for 50msec before checking ADSP state
+			 */
+			msleep(50);
+		} else {
+			pr_debug("%s: ADSP Audio is ready\n", __func__);
+			adsp_ready = 1;
+			break;
+		}
+	} while (time_after(timeout, jiffies));
+
+	if (!adsp_ready) {
+		pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	err = msm_afe_set_config(codec);
+	if (err)
+		pr_err("%s: Failed to set AFE config. err %d\n",
+			__func__, err);
+	return err;
+}
+
+static int msm8996_tasha_codec_event_cb(struct snd_soc_codec *codec,
+					enum wcd9335_codec_event codec_event)
+{
+	switch (codec_event) {
+	case WCD9335_CODEC_EVENT_CODEC_UP:
+		return msm8996_wcd93xx_codec_up(codec);
+	default:
+		pr_err("%s: UnSupported codec event %d\n",
+			__func__, codec_event);
+		return -EINVAL;
+	}
+}
+
+static int msm8996_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm8996_asoc_mach_data *pdata;
+	int val;
+
+	if (!card)
+		return 0;
+
+	pdata = snd_soc_card_get_drvdata(card);
+	if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio))
+		return 0;
+
+	val = gpio_get_value_cansleep(pdata->hph_en0_gpio);
+	if ((!!val) == high)
+		return 0;
+
+	gpio_direction_output(pdata->hph_en0_gpio, (int)high);
+
+	return 1;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int err;
+	void *config_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_component *aux_comp;
+	void *mbhc_calibration;
+	struct snd_card *card;
+	struct snd_info_entry *entry;
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(rtd->card);
+
+	/* Codec SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+	 * TX14, TX15, TX16
+	 */
+	unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+					    151, 152, 153, 154, 155, 156};
+	unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133,
+					    134, 135, 136, 137, 138, 139,
+					    140, 141, 142, 143};
+
+	pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+	rtd->pmdown_time = 0;
+
+	err = snd_soc_add_codec_controls(codec, msm_snd_controls,
+					 ARRAY_SIZE(msm_snd_controls));
+	if (err < 0) {
+		pr_err("%s: add_codec_controls failed, err %d\n",
+			__func__, err);
+		return err;
+	}
+
+	err = msm8996_liquid_init_docking();
+	if (err) {
+		pr_err("%s: 8996 init Docking stat IRQ failed (%d)\n",
+			__func__, err);
+		return err;
+	}
+
+	err = msm8996_ext_us_amp_init();
+	if (err) {
+		pr_err("%s: 8996 US Emitter GPIO init failed (%d)\n",
+			__func__, err);
+		return err;
+	}
+
+	snd_soc_dapm_new_controls(dapm, msm8996_dapm_widgets,
+				ARRAY_SIZE(msm8996_dapm_widgets));
+
+	snd_soc_dapm_add_routes(dapm, wcd9335_audio_paths,
+				ARRAY_SIZE(wcd9335_audio_paths));
+	snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "ultrasound amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8");
+	snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+	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, "AMIC4");
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC5");
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC6");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC5");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC0");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+	snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+
+	snd_soc_dapm_sync(dapm);
+
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+	msm8996_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+	msm8996_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit;
+
+	err = msm_afe_set_config(codec);
+	if (err) {
+		pr_err("%s: Failed to set AFE config %d\n", __func__, err);
+		goto out;
+	}
+
+	config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+						AFE_AANC_VERSION);
+	if (config_data) {
+		err = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+		if (err) {
+			pr_err("%s: Failed to set aanc version %d\n",
+				__func__, err);
+			goto out;
+		}
+	}
+	config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+					    AFE_CDC_CLIP_REGISTERS_CONFIG);
+	if (config_data) {
+		err = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+				     config_data, 0);
+		if (err) {
+			pr_err("%s: Failed to set clip registers %d\n",
+				__func__, err);
+			goto out;
+		}
+	}
+	config_data = msm8996_codec_fn.get_afe_config_fn(codec,
+			AFE_CLIP_BANK_SEL);
+	if (config_data) {
+		err = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+		if (err) {
+			pr_err("%s: Failed to set AFE bank selection %d\n",
+				__func__, err);
+			goto out;
+		}
+	}
+	/* Start mbhc */
+	tasha_mbhc_zdet_gpio_ctrl(msm8996_config_hph_en0_gpio, rtd->codec);
+	mbhc_calibration = def_tasha_mbhc_cal();
+	if (mbhc_calibration) {
+		wcd_mbhc_cfg.calibration = mbhc_calibration;
+		err = tasha_mbhc_hs_detect(codec, &wcd_mbhc_cfg);
+		if (err) {
+			pr_err("%s: mbhc hs detect failed, err:%d\n",
+				__func__, err);
+			goto out;
+		}
+	} else {
+		pr_err("%s: mbhc_cfg calibration is NULL\n", __func__);
+		err = -ENOMEM;
+		goto out;
+	}
+	adsp_state_notifier = subsys_notif_register_notifier("adsp",
+						&adsp_state_notifier_block);
+	if (!adsp_state_notifier) {
+		pr_err("%s: Failed to register adsp state notifier\n",
+		       __func__);
+		err = -EFAULT;
+		msm8996_codec_fn.mbhc_hs_detect_exit(codec);
+		goto out;
+	}
+
+	tasha_event_register(msm8996_tasha_codec_event_cb, rtd->codec);
+
+	/*
+	 * Send speaker configuration only for WSA8810.
+	 * Defalut configuration is for WSA8815.
+	 */
+	if (!list_empty(&rtd->card->aux_comp_list)) {
+		aux_comp = list_first_entry(&rtd->card->aux_comp_list,
+				struct snd_soc_component, list_aux);
+		if (!strcmp(aux_comp->name, WSA8810_NAME_1) ||
+		    !strcmp(aux_comp->name, WSA8810_NAME_2)) {
+			tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+			tasha_set_spkr_gain_offset(rtd->codec,
+						   RX_GAIN_OFFSET_M1P5_DB);
+		}
+	}
+	codec_reg_done = true;
+
+	card = rtd->card->snd_card;
+	entry = snd_info_create_subdir(card->module, "codecs",
+					 card->proc_root);
+	if (!entry) {
+		pr_debug("%s: Cannot create codecs module entry\n",
+			 __func__);
+		err = 0;
+		goto out;
+	}
+	pdata->codec_root = entry;
+	tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
+
+	return 0;
+out:
+	return err;
+}
+
+static void *def_tasha_mbhc_cal(void)
+{
+	void *tasha_wcd_cal;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_high;
+
+	tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+				WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+	if (!tasha_wcd_cal)
+		return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y))
+	S(v_hs_max, 1500);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y))
+	S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal);
+	btn_high = ((void *)&btn_cfg->_v_btn_low) +
+		(sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+	btn_high[0] = 75;
+	btn_high[1] = 150;
+	btn_high[2] = 237;
+	btn_high[3] = 500;
+	btn_high[4] = 500;
+	btn_high[5] = 500;
+	btn_high[6] = 500;
+	btn_high[7] = 500;
+
+	return tasha_wcd_cal;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+	int ret = 0;
+	u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	u32 user_set_tx_ch = 0;
+	u32 rx_ch_count;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+		if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+			pr_debug("%s: rx_5_ch=%d\n", __func__,
+				  msm_slim_5_rx_ch);
+			rx_ch_count = msm_slim_5_rx_ch;
+		} else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+			pr_debug("%s: rx_6_ch=%d\n", __func__,
+				  msm_slim_6_rx_ch);
+			rx_ch_count = msm_slim_6_rx_ch;
+		} else {
+			pr_debug("%s: rx_0_ch=%d\n", __func__,
+				  msm_slim_0_rx_ch);
+			rx_ch_count = msm_slim_0_rx_ch;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+						  rx_ch_count, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	} else {
+
+		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+			 codec_dai->name, codec_dai->id, user_set_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map\n, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+		/* For <codec>_tx1 case */
+		if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+			user_set_tx_ch = msm_slim_0_tx_ch;
+		/* For <codec>_tx3 case */
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+			user_set_tx_ch = msm_slim_1_tx_ch;
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_3_TX)
+			/* DAI 5 is used for external EC reference from codec.
+			 * Since Rx is fed as reference for EC, the config of
+			 * this DAI is based on that of the Rx path.
+			 */
+			user_set_tx_ch = msm_slim_0_rx_ch;
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+			user_set_tx_ch = msm_vi_feed_tx_ch;
+		else
+			user_set_tx_ch = tx_ch_cnt;
+
+		pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n",
+			 __func__, msm_slim_0_tx_ch, user_set_tx_ch,
+			 tx_ch_cnt, dai_link->id);
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+						  user_set_tx_ch, tx_ch, 0, 0);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	}
+end:
+	return ret;
+}
+
+static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+	int ret = 0;
+	u32 tx_ch[SLIM_MAX_TX_PORTS];
+	u32 tx_ch_cnt = 0;
+	u32 user_set_tx_ch = 0;
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+		pr_err("%s: Invalid stream type %d\n",
+			__func__, substream->stream);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, NULL, NULL);
+	if (ret < 0) {
+		pr_err("%s: failed to get codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto end;
+	}
+
+	user_set_tx_ch = tx_ch_cnt;
+
+	pr_debug("%s: tx_ch_cnt(%d) BE id %d\n",
+		 __func__, tx_ch_cnt, dai_link->id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  user_set_tx_ch, tx_ch, 0, 0);
+	if (ret < 0)
+		pr_err("%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+end:
+	return ret;
+}
+
+static struct snd_soc_ops msm8996_be_ops = {
+	.hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm8996_cpe_ops = {
+	.hw_params = msm_snd_cpe_hw_params,
+};
+
+static int msm8996_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+	unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+	unsigned int num_tx_ch = 0;
+	unsigned int num_rx_ch = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		num_rx_ch =  params_channels(params);
+		pr_debug("%s: %s rx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_rx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+				num_rx_ch, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	} else {
+		num_tx_ch =  params_channels(params);
+		pr_debug("%s: %s  tx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+				num_tx_ch, tx_ch, 0, 0);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	}
+end:
+	return ret;
+}
+
+static struct snd_soc_ops msm8996_slimbus_2_be_ops = {
+	.hw_params = msm8996_slimbus_2_hw_params,
+};
+
+static int msm8996_get_ll_qos_val(struct snd_pcm_runtime *runtime)
+{
+	int usecs;
+
+	/* take 10% of period time as the deadline */
+	usecs = (100000 / runtime->rate) * runtime->period_size;
+	usecs += ((100000 % runtime->rate) * runtime->period_size) /
+		runtime->rate;
+
+	return usecs;
+}
+
+static int msm8996_mm5_prepare(struct snd_pcm_substream *substream)
+{
+	if (pm_qos_request_active(&substream->latency_pm_qos_req))
+		pm_qos_remove_request(&substream->latency_pm_qos_req);
+	pm_qos_add_request(&substream->latency_pm_qos_req,
+			   PM_QOS_CPU_DMA_LATENCY,
+			   msm8996_get_ll_qos_val(substream->runtime));
+	return 0;
+}
+
+static struct snd_soc_ops msm8996_mm5_ops = {
+	.prepare = msm8996_mm5_prepare,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm8996_common_dai_links[] = {
+	/* FrontEnd DAI Links */
+	{
+		.name = "MSM8996 Media1",
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name = "MultiMedia1",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+	{
+		.name = "MSM8996 Media2",
+		.stream_name = "MultiMedia2",
+		.cpu_dai_name = "MultiMedia2",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+	},
+	{
+		.name = "VoiceMMode1",
+		.stream_name = "VoiceMMode1",
+		.cpu_dai_name = "VoiceMMode1",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICEMMODE1,
+	},
+	{
+		.name = "MSM VoIP",
+		.stream_name = "VoIP",
+		.cpu_dai_name = "VoIP",
+		.platform_name = "msm-voip-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_VOIP,
+	},
+	{
+		.name = "MSM8996 ULL",
+		.stream_name = "MultiMedia3",
+		.cpu_dai_name = "MultiMedia3",
+		.platform_name = "msm-pcm-dsp.2",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+	},
+	/* Hostless PCM purpose */
+	{
+		.name = "SLIMBUS_0 Hostless",
+		.stream_name = "SLIMBUS_0 Hostless",
+		.cpu_dai_name = "SLIMBUS0_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "Tertiary MI2S TX_Hostless",
+		.stream_name = "Tertiary MI2S_TX Hostless Capture",
+		.cpu_dai_name = "TERT_MI2S_TX_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "MSM AFE-PCM RX",
+		.stream_name = "AFE-PROXY RX",
+		.cpu_dai_name = "msm-dai-q6-dev.241",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.platform_name = "msm-pcm-afe",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = "MSM AFE-PCM TX",
+		.stream_name = "AFE-PROXY TX",
+		.cpu_dai_name = "msm-dai-q6-dev.240",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.platform_name  = "msm-pcm-afe",
+		.ignore_suspend = 1,
+	},
+	{
+		.name = "MSM8996 Compress1",
+		.stream_name = "Compress1",
+		.cpu_dai_name = "MultiMedia4",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+	},
+	{
+		.name = "AUXPCM Hostless",
+		.stream_name = "AUXPCM Hostless",
+		.cpu_dai_name = "AUXPCM_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_1 Hostless",
+		.stream_name = "SLIMBUS_1 Hostless",
+		.cpu_dai_name = "SLIMBUS1_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_3 Hostless",
+		.stream_name = "SLIMBUS_3 Hostless",
+		.cpu_dai_name = "SLIMBUS3_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_4 Hostless",
+		.stream_name = "SLIMBUS_4 Hostless",
+		.cpu_dai_name = "SLIMBUS4_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "VoLTE",
+		.stream_name = "VoLTE",
+		.cpu_dai_name = "VoLTE",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOLTE,
+	},
+	{
+		.name = "MSM8996 LowLatency",
+		.stream_name = "MultiMedia5",
+		.cpu_dai_name = "MultiMedia5",
+		.platform_name = "msm-pcm-dsp.1",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+		.ops = &msm8996_mm5_ops,
+	},
+	{
+		.name = "Listen 1 Audio Service",
+		.stream_name = "Listen 1 Audio Service",
+		.cpu_dai_name = "LSM1",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+			     SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM1,
+	},
+	/* Multiple Tunnel instances */
+	{
+		.name = "MSM8996 Compress2",
+		.stream_name = "Compress2",
+		.cpu_dai_name = "MultiMedia7",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+	},
+	{
+		.name = "MSM8996 Compress3",
+		.stream_name = "Compress3",
+		.cpu_dai_name = "MultiMedia10",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+	},
+	{
+		.name = "MSM8996 ULL NOIRQ",
+		.stream_name = "MM_NOIRQ",
+		.cpu_dai_name = "MultiMedia8",
+		.platform_name = "msm-pcm-dsp-noirq",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+	},
+	{
+		.name = "QCHAT",
+		.stream_name = "QCHAT",
+		.cpu_dai_name = "QCHAT",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_QCHAT,
+	},
+	/* HDMI Hostless */
+	{
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name = "HDMI_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "VoiceMMode2",
+		.stream_name = "VoiceMMode2",
+		.cpu_dai_name = "VoiceMMode2",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICEMMODE2,
+	},
+	{
+		.name = "INT_HFP_BT Hostless",
+		.stream_name = "INT_HFP_BT Hostless",
+		.cpu_dai_name = "INT_HFP_BT_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "MSM8996 HFP TX",
+		.stream_name = "MultiMedia6",
+		.cpu_dai_name = "MultiMedia6",
+		.platform_name = "msm-pcm-loopback",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA6,
+	},
+	/* LSM FE */
+	{
+		.name = "Listen 2 Audio Service",
+		.stream_name = "Listen 2 Audio Service",
+		.cpu_dai_name = "LSM2",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM2,
+	},
+	{
+		.name = "Listen 3 Audio Service",
+		.stream_name = "Listen 3 Audio Service",
+		.cpu_dai_name = "LSM3",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM3,
+	},
+	{
+		.name = "Listen 4 Audio Service",
+		.stream_name = "Listen 4 Audio Service",
+		.cpu_dai_name = "LSM4",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM4,
+	},
+	{
+		.name = "Listen 5 Audio Service",
+		.stream_name = "Listen 5 Audio Service",
+		.cpu_dai_name = "LSM5",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM5,
+	},
+	{
+		.name = "Listen 6 Audio Service",
+		.stream_name = "Listen 6 Audio Service",
+		.cpu_dai_name = "LSM6",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM6,
+	},
+	{
+		.name = "Listen 7 Audio Service",
+		.stream_name = "Listen 7 Audio Service",
+		.cpu_dai_name = "LSM7",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM7,
+	},
+	{
+		.name = "Listen 8 Audio Service",
+		.stream_name = "Listen 8 Audio Service",
+		.cpu_dai_name = "LSM8",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM8,
+	},
+	{
+		.name = "MSM8996 Media9",
+		.stream_name = "MultiMedia9",
+		.cpu_dai_name = "MultiMedia9",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+	},
+	{
+		.name = "VoWLAN",
+		.stream_name = "VoWLAN",
+		.cpu_dai_name = "VoWLAN",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOWLAN,
+	},
+	{
+		.name = "MSM8996 Compress4",
+		.stream_name = "Compress4",
+		.cpu_dai_name = "MultiMedia11",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+	},
+	{
+		.name = "MSM8996 Compress5",
+		.stream_name = "Compress5",
+		.cpu_dai_name = "MultiMedia12",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+	},
+	{
+		.name = "MSM8996 Compress6",
+		.stream_name = "Compress6",
+		.cpu_dai_name = "MultiMedia13",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+	},
+	{
+		.name = "MSM8996 Compress7",
+		.stream_name = "Compress7",
+		.cpu_dai_name = "MultiMedia14",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+	},
+	{
+		.name = "MSM8996 Compress8",
+		.stream_name = "Compress8",
+		.cpu_dai_name = "MultiMedia15",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+	},
+	{
+		.name = "MSM8996 Compress9",
+		.stream_name = "Compress9",
+		.cpu_dai_name = "MultiMedia16",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+	},
+	{
+		.name = "Circuit-Switch Voice",
+		.stream_name = "CS-Voice",
+		.cpu_dai_name   = "CS-VOICE",
+		.platform_name  = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_CS_VOICE,
+	},
+	{
+		.name = "Voice2",
+		.stream_name = "Voice2",
+		.cpu_dai_name = "Voice2",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICE2,
+	},
+};
+
+static struct snd_soc_dai_link msm8996_tasha_fe_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_vifeedback",
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_slim_4_tx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+	},
+	/* Ultrasound RX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Playback",
+		.stream_name = "SLIMBUS_2 Hostless Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm8996_slimbus_2_be_ops,
+	},
+	/* Ultrasound TX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Capture",
+		.stream_name = "SLIMBUS_2 Hostless Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16389",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm8996_slimbus_2_be_ops,
+	},
+	/* CPE LSM direct dai-link */
+	{
+		.name = "CPE Listen service",
+		.stream_name = "CPE Listen Audio Service",
+		.cpu_dai_name = "msm-dai-slim",
+		.platform_name = "msm-cpe-lsm",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "tasha_mad1",
+		.codec_name = "tasha_codec",
+		.ops = &msm8996_cpe_ops,
+	},
+	/* slimbus rx 6 hostless */
+	{
+		.name = "SLIMBUS_6 Hostless Playback",
+		.stream_name = "SLIMBUS_6 Hostless",
+		.cpu_dai_name = "SLIMBUS6_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* CPE LSM EC PP direct dai-link */
+	{
+		.name = "CPE Listen service ECPP",
+		.stream_name = "CPE Listen Audio Service ECPP",
+		.cpu_dai_name = "CPE_LSM_NOHOST",
+		.platform_name = "msm-cpe-lsm.3",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "tasha_cpe",
+		.codec_name = "tasha_codec",
+	},
+};
+
+static struct snd_soc_dai_link msm8996_common_be_dai_links[] = {
+	/* Backend AFE DAI Links */
+	{
+		.name = LPASS_BE_AFE_PCM_RX,
+		.stream_name = "AFE Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.224",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = msm_proxy_rx_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_AFE_PCM_TX,
+		.stream_name = "AFE Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.225",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_AFE_PCM_TX,
+		.be_hw_params_fixup = msm_proxy_tx_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Primary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_AUXPCM_RX,
+		.stream_name = "AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_AUXPCM_RX,
+		.be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+	},
+	{
+		.name = LPASS_BE_AUXPCM_TX,
+		.stream_name = "AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_AUXPCM_TX,
+		.be_hw_params_fixup = msm_auxpcm_be_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music 2 BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
+		.stream_name = "Voice2 Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_TX,
+		.stream_name = "Tertiary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+		.be_hw_params_fixup = msm_tx_be_hw_params_fixup,
+		.ops = &msm8996_mi2s_be_ops,
+		.ignore_suspend = 1,
+	}
+};
+
+static struct snd_soc_dai_link msm8996_tasha_be_dai_links[] = {
+	/* Backend DAI Links */
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm8996_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm8996_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx3",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_slim_1_tx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_slim_0_tx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_slim_0_rx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_5_RX,
+		.stream_name = "Slimbus5 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16394",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx3",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+		.be_hw_params_fixup = msm_slim_5_rx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* MAD BE */
+	{
+		.name = LPASS_BE_SLIMBUS_5_TX,
+		.stream_name = "Slimbus5 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16395",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mad1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+		.be_hw_params_fixup = msm_slim_5_tx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_6_RX,
+		.stream_name = "Slimbus6 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16396",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx4",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+		.be_hw_params_fixup = msm_slim_6_rx_be_hw_params_fixup,
+		.ops = &msm8996_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm8996_hdmi_dai_link[] = {
+	/* HDMI BACK END DAI Link */
+	{
+		.name = LPASS_BE_HDMI,
+		.stream_name = "HDMI Playback",
+		.cpu_dai_name = "msm-dai-q6-hdmi.8",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-hdmi-audio-codec-rx",
+		.codec_dai_name = "msm_hdmi_audio_codec_rx_dai",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_HDMI_RX,
+		.be_hw_params_fixup = msm8996_hdmi_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm8996_tasha_dai_links[
+			 ARRAY_SIZE(msm8996_common_dai_links) +
+			 ARRAY_SIZE(msm8996_tasha_fe_dai_links) +
+			 ARRAY_SIZE(msm8996_common_be_dai_links) +
+			 ARRAY_SIZE(msm8996_tasha_be_dai_links) +
+			 ARRAY_SIZE(msm8996_hdmi_dai_link)];
+
+static int msm8996_wsa881x_init(struct snd_soc_component *component)
+{
+	u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+	u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+	unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+	unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+	struct msm8996_asoc_mach_data *pdata;
+	struct snd_soc_dapm_context *dapm;
+
+	if (!codec) {
+		pr_err("%s codec is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dapm = snd_soc_codec_get_dapm(codec);
+
+	if (!strcmp(component->name_prefix, "SpkrLeft")) {
+		dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+			__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkleft_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+		}
+	} else if (!strcmp(component->name_prefix, "SpkrRight")) {
+		dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+			__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkright_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+		}
+	} else {
+		dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+			codec->component.name);
+		return -EINVAL;
+	}
+	pdata = snd_soc_card_get_drvdata(component->card);
+	if (pdata && pdata->codec_root)
+		wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+						      codec);
+
+	return 0;
+}
+
+struct snd_soc_card snd_soc_card_tasha_msm8996 = {
+	.name		= "msm8996-tasha-snd-card",
+};
+
+static int msm8996_populate_dai_link_component_of_node(
+					struct snd_soc_card *card)
+{
+	int i, index, ret = 0;
+	struct device *cdev = card->dev;
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+	struct device_node *np;
+
+	if (!cdev) {
+		pr_err("%s: Sound card device memory NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < card->num_links; i++) {
+		if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+			continue;
+
+		/* populate platform_of_node for snd card dai links */
+		if (dai_link[i].platform_name &&
+		    !dai_link[i].platform_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						"asoc-platform-names",
+						dai_link[i].platform_name);
+			if (index < 0) {
+				pr_err("%s: No match found for platform name: %s\n",
+					__func__, dai_link[i].platform_name);
+				ret = index;
+				goto err;
+			}
+			np = of_parse_phandle(cdev->of_node, "asoc-platform",
+					      index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+					__func__, dai_link[i].platform_name,
+					index);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].platform_of_node = np;
+			dai_link[i].platform_name = NULL;
+		}
+
+		/* populate cpu_of_node for snd card dai links */
+		if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						 "asoc-cpu-names",
+						 dai_link[i].cpu_dai_name);
+			if (index >= 0) {
+				np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+						index);
+				if (!np) {
+					pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+						__func__,
+						dai_link[i].cpu_dai_name);
+					ret = -ENODEV;
+					goto err;
+				}
+				dai_link[i].cpu_of_node = np;
+				dai_link[i].cpu_dai_name = NULL;
+			}
+		}
+
+		/* populate codec_of_node for snd card dai links */
+		if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						 "asoc-codec-names",
+						 dai_link[i].codec_name);
+			if (index < 0)
+				continue;
+			np = of_parse_phandle(cdev->of_node, "asoc-codec",
+					      index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for codec %s failed\n",
+					__func__, dai_link[i].codec_name);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].codec_of_node = np;
+			dai_link[i].codec_name = NULL;
+		}
+	}
+
+err:
+	return ret;
+}
+
+static int msm8996_prepare_us_euro(struct snd_soc_card *card)
+{
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int ret;
+
+	if (pdata->us_euro_gpio >= 0) {
+		dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+			pdata->us_euro_gpio);
+		ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: Failed to request codec US/EURO gpio %d error %d\n",
+				__func__, pdata->us_euro_gpio, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int msm8996_prepare_hifi(struct snd_soc_card *card)
+{
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int ret;
+
+	if (gpio_is_valid(pdata->hph_en1_gpio)) {
+		dev_dbg(card->dev, "%s: hph_en1_gpio request %d\n", __func__,
+			pdata->hph_en1_gpio);
+		ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: hph_en1_gpio request failed, ret:%d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+	if (gpio_is_valid(pdata->hph_en0_gpio)) {
+		dev_dbg(card->dev, "%s: hph_en0_gpio request %d\n", __func__,
+			pdata->hph_en0_gpio);
+		ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: hph_en0_gpio request failed, ret:%d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static const struct of_device_id msm8996_asoc_machine_of_match[]  = {
+	{ .compatible = "qcom,msm8996-asoc-snd-tasha",
+	  .data = "tasha_codec"},
+	{},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+	struct snd_soc_card *card = NULL;
+	struct snd_soc_dai_link *dailink;
+	int len_1, len_2, len_3, len_4;
+	const struct of_device_id *match;
+
+	match = of_match_node(msm8996_asoc_machine_of_match, dev->of_node);
+	if (!match) {
+		dev_err(dev, "%s: No DT match found for sound card\n",
+			__func__);
+		return NULL;
+	}
+
+	if (!strcmp(match->data, "tasha_codec")) {
+		card = &snd_soc_card_tasha_msm8996;
+		len_1 = ARRAY_SIZE(msm8996_common_dai_links);
+		len_2 = len_1 + ARRAY_SIZE(msm8996_tasha_fe_dai_links);
+		len_3 = len_2 + ARRAY_SIZE(msm8996_common_be_dai_links);
+
+		memcpy(msm8996_tasha_dai_links,
+		       msm8996_common_dai_links,
+		       sizeof(msm8996_common_dai_links));
+		memcpy(msm8996_tasha_dai_links + len_1,
+		       msm8996_tasha_fe_dai_links,
+		       sizeof(msm8996_tasha_fe_dai_links));
+		memcpy(msm8996_tasha_dai_links + len_2,
+		       msm8996_common_be_dai_links,
+		       sizeof(msm8996_common_be_dai_links));
+		memcpy(msm8996_tasha_dai_links + len_3,
+		       msm8996_tasha_be_dai_links,
+		       sizeof(msm8996_tasha_be_dai_links));
+
+		dailink = msm8996_tasha_dai_links;
+		len_4 = len_3 + ARRAY_SIZE(msm8996_tasha_be_dai_links);
+	}
+
+	if (of_property_read_bool(dev->of_node, "qcom,hdmi-audio-rx")) {
+		dev_dbg(dev, "%s(): hdmi audio support present\n",
+				__func__);
+		memcpy(dailink + len_4, msm8996_hdmi_dai_link,
+			sizeof(msm8996_hdmi_dai_link));
+		len_4 += ARRAY_SIZE(msm8996_hdmi_dai_link);
+	} else {
+		dev_dbg(dev, "%s(): No hdmi audio support\n", __func__);
+	}
+
+	if (card) {
+		card->dai_link = dailink;
+		card->num_links = len_4;
+	}
+
+	return card;
+}
+
+static int msm8996_init_wsa_dev(struct platform_device *pdev,
+				struct snd_soc_card *card)
+{
+	struct device_node *wsa_of_node;
+	u32 wsa_max_devs;
+	u32 wsa_dev_cnt;
+	char *dev_name_str = NULL;
+	struct msm8996_wsa881x_dev_info *wsa881x_dev_info;
+	const char *wsa_auxdev_name_prefix[1];
+	int found = 0;
+	int i;
+	int ret;
+
+	/* Get maximum WSA device count for this platform */
+	ret = of_property_read_u32(pdev->dev.of_node,
+				   "qcom,wsa-max-devs", &wsa_max_devs);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			 "%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+			 __func__, pdev->dev.of_node->full_name, ret);
+		return 0;
+	}
+	if (wsa_max_devs == 0) {
+		dev_warn(&pdev->dev,
+			 "%s: Max WSA devices is 0 for this target?\n",
+			 __func__);
+		return 0;
+	}
+
+	/* Get count of WSA device phandles for this platform */
+	wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+						 "qcom,wsa-devs", NULL);
+	if (wsa_dev_cnt == -ENOENT) {
+		dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+			 __func__);
+		return 0;
+	} else if (wsa_dev_cnt <= 0) {
+		dev_err(&pdev->dev,
+			"%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+			__func__, wsa_dev_cnt);
+		return -EINVAL;
+	}
+
+	/*
+	 * Expect total phandles count to be NOT less than maximum possible
+	 * WSA count. However, if it is less, then assign same value to
+	 * max count as well.
+	 */
+	if (wsa_dev_cnt < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+			__func__, wsa_max_devs, wsa_dev_cnt);
+		wsa_max_devs = wsa_dev_cnt;
+	}
+
+	/* Make sure prefix string passed for each WSA device */
+	ret = of_property_count_strings(pdev->dev.of_node,
+					"qcom,wsa-aux-dev-prefix");
+	if (ret != wsa_dev_cnt) {
+		dev_err(&pdev->dev,
+			"%s: expecting %d wsa prefix. Defined only %d in DT\n",
+			__func__, wsa_dev_cnt, ret);
+		return -EINVAL;
+	}
+
+	/*
+	 * Alloc mem to store phandle and index info of WSA device, if already
+	 * registered with ALSA core
+	 */
+	wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+					sizeof(struct msm8996_wsa881x_dev_info),
+					GFP_KERNEL);
+	if (!wsa881x_dev_info)
+		return -ENOMEM;
+
+	/*
+	 * search and check whether all WSA devices are already
+	 * registered with ALSA core or not. If found a node, store
+	 * the node and the index in a local array of struct for later
+	 * use.
+	 */
+	for (i = 0; i < wsa_dev_cnt; i++) {
+		wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+					    "qcom,wsa-devs", i);
+		if (unlikely(!wsa_of_node)) {
+			/* we should not be here */
+			dev_err(&pdev->dev,
+				"%s: wsa dev node is not present\n",
+				__func__);
+			return -EINVAL;
+		}
+		if (soc_find_component(wsa_of_node, NULL)) {
+			/* WSA device registered with ALSA core */
+			wsa881x_dev_info[found].of_node = wsa_of_node;
+			wsa881x_dev_info[found].index = i;
+			found++;
+			if (found == wsa_max_devs)
+				break;
+		}
+	}
+
+	if (found < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: failed to find %d components. Found only %d\n",
+			__func__, wsa_max_devs, found);
+		return -EPROBE_DEFER;
+	}
+	dev_info(&pdev->dev,
+		"%s: found %d wsa881x devices registered with ALSA core\n",
+		__func__, found);
+
+	card->num_aux_devs = wsa_max_devs;
+	card->num_configs = wsa_max_devs;
+
+	/* Alloc array of AUX devs struct */
+	msm8996_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+				       sizeof(struct snd_soc_aux_dev),
+				       GFP_KERNEL);
+	if (!msm8996_aux_dev)
+		return -ENOMEM;
+
+	/* Alloc array of codec conf struct */
+	msm8996_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+					  sizeof(struct snd_soc_codec_conf),
+					  GFP_KERNEL);
+	if (!msm8996_codec_conf)
+		return -ENOMEM;
+
+	for (i = 0; i < card->num_aux_devs; i++) {
+		dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+					    GFP_KERNEL);
+		if (!dev_name_str)
+			return -ENOMEM;
+
+		ret = of_property_read_string_index(pdev->dev.of_node,
+						    "qcom,wsa-aux-dev-prefix",
+						    wsa881x_dev_info[i].index,
+						    wsa_auxdev_name_prefix);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: failed to read wsa aux dev prefix, ret = %d\n",
+				__func__, ret);
+			return -EINVAL;
+		}
+
+		snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+		msm8996_aux_dev[i].name = dev_name_str;
+		msm8996_aux_dev[i].codec_name = NULL;
+		msm8996_aux_dev[i].codec_of_node =
+					wsa881x_dev_info[i].of_node;
+		msm8996_aux_dev[i].init = msm8996_wsa881x_init;
+		msm8996_codec_conf[i].dev_name = NULL;
+		msm8996_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+		msm8996_codec_conf[i].of_node =
+					wsa881x_dev_info[i].of_node;
+	}
+	card->codec_conf = msm8996_codec_conf;
+	card->aux_dev = msm8996_aux_dev;
+
+	return 0;
+}
+
+static int msm8996_asoc_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card;
+	struct msm8996_asoc_mach_data *pdata;
+	const char *mbhc_audio_jack_type = NULL;
+	char *mclk_freq_prop_name;
+	const struct of_device_id *match;
+	int ret;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "No platform supplied from device tree\n");
+		return -EINVAL;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			sizeof(struct msm8996_asoc_mach_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	card = populate_snd_card_dailinks(&pdev->dev);
+	if (!card) {
+		dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, pdata);
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(&pdev->dev, "parse card name failed, err:%d\n",
+			ret);
+		goto err;
+	}
+
+	ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "parse audio routing failed, err:%d\n",
+			ret);
+		goto err;
+	}
+
+	match = of_match_node(msm8996_asoc_machine_of_match,
+			pdev->dev.of_node);
+	if (!match) {
+		dev_err(&pdev->dev, "%s: no matched codec is found.\n",
+			__func__);
+		goto err;
+	}
+
+	mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq";
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			mclk_freq_prop_name, &pdata->mclk_freq);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed, err%d\n",
+			mclk_freq_prop_name,
+			pdev->dev.of_node->full_name, ret);
+		goto err;
+	}
+
+	if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) {
+		dev_err(&pdev->dev, "unsupported mclk freq %u\n",
+			pdata->mclk_freq);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	spdev = pdev;
+
+	ret = msm8996_populate_dai_link_component_of_node(card);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+
+	ret = msm8996_init_wsa_dev(pdev, card);
+	if (ret)
+		goto err;
+
+	pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node,
+						"qcom,hph-en1-gpio", 0);
+	if (pdata->hph_en1_gpio < 0) {
+		dev_dbg(&pdev->dev, "%s: %s property not found %d\n",
+			__func__, "qcom,hph-en1-gpio", pdata->hph_en1_gpio);
+	}
+
+	pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node,
+						"qcom,hph-en0-gpio", 0);
+	if (pdata->hph_en0_gpio < 0) {
+		dev_dbg(&pdev->dev, "%s: %s property not found %d\n",
+			__func__, "qcom,hph-en0-gpio", pdata->hph_en0_gpio);
+	}
+	ret = msm8996_prepare_hifi(card);
+	if (ret)
+		dev_dbg(&pdev->dev, "msm8996_prepare_hifi failed (%d)\n",
+			ret);
+
+	ret = snd_soc_register_card(card);
+	if (ret == -EPROBE_DEFER) {
+		if (codec_reg_done)
+			ret = -EINVAL;
+		goto err;
+	} else if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err;
+	}
+	dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+
+	ret = of_property_read_string(pdev->dev.of_node,
+		"qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+			"qcom,mbhc-audio-jack-type",
+			pdev->dev.of_node->full_name);
+		dev_dbg(&pdev->dev, "Jack type properties set to default");
+	} else {
+		if (!strcmp(mbhc_audio_jack_type, "4-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+		else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+		else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+		else
+			dev_dbg(&pdev->dev, "Unknown value, set to default");
+	}
+	/*
+	 * Parse US-Euro gpio info from DT. Report no error if us-euro
+	 * entry is not found in DT file as some targets do not support
+	 * US-Euro detection
+	 */
+	pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"qcom,us-euro-gpios", 0);
+	if (pdata->us_euro_gpio < 0) {
+		dev_info(&pdev->dev, "property %s not detected in node %s",
+			"qcom,us-euro-gpios",
+			pdev->dev.of_node->full_name);
+	} else {
+		dev_dbg(&pdev->dev, "%s detected %d",
+			"qcom,us-euro-gpios", pdata->us_euro_gpio);
+		wcd_mbhc_cfg.swap_gnd_mic = msm8996_swap_gnd_mic;
+	}
+
+	ret = msm8996_prepare_us_euro(card);
+	if (ret)
+		dev_info(&pdev->dev, "msm8996_prepare_us_euro failed (%d)\n",
+			ret);
+	return 0;
+err:
+	if (pdata->us_euro_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+			__func__, pdata->us_euro_gpio);
+		gpio_free(pdata->us_euro_gpio);
+		pdata->us_euro_gpio = 0;
+	}
+	if (pdata->hph_en1_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n",
+			__func__, pdata->hph_en1_gpio);
+		gpio_free(pdata->hph_en1_gpio);
+		pdata->hph_en1_gpio = 0;
+	}
+	if (pdata->hph_en0_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n",
+			__func__, pdata->hph_en0_gpio);
+		gpio_free(pdata->hph_en0_gpio);
+		pdata->hph_en0_gpio = 0;
+	}
+	devm_kfree(&pdev->dev, pdata);
+	return ret;
+}
+
+static int msm8996_asoc_machine_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct msm8996_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	if (gpio_is_valid(ext_us_amp_gpio))
+		gpio_free(ext_us_amp_gpio);
+
+	gpio_free(pdata->us_euro_gpio);
+	gpio_free(pdata->hph_en1_gpio);
+	gpio_free(pdata->hph_en0_gpio);
+
+	if (msm8996_liquid_dock_dev != NULL) {
+		switch_dev_unregister(&msm8996_liquid_dock_dev->audio_sdev);
+
+		if (msm8996_liquid_dock_dev->dock_plug_irq)
+			free_irq(msm8996_liquid_dock_dev->dock_plug_irq,
+				 msm8996_liquid_dock_dev);
+
+		if (msm8996_liquid_dock_dev->dock_plug_gpio)
+			gpio_free(msm8996_liquid_dock_dev->dock_plug_gpio);
+
+		kfree(msm8996_liquid_dock_dev);
+		msm8996_liquid_dock_dev = NULL;
+	}
+	snd_soc_unregister_card(card);
+
+	return 0;
+}
+
+static struct platform_driver msm8996_asoc_machine_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = msm8996_asoc_machine_of_match,
+	},
+	.probe = msm8996_asoc_machine_probe,
+	.remove = msm8996_asoc_machine_remove,
+};
+module_platform_driver(msm8996_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, msm8996_asoc_machine_of_match);
diff --git a/sound/soc/msm/msm8998.c b/sound/soc/msm/msm8998.c
new file mode 100644
index 0000000..73755ed
--- /dev/null
+++ b/sound/soc/msm/msm8998.c
@@ -0,0 +1,6836 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9335.h"
+#include "../codecs/wcd934x/wcd934x.h"
+#include "../codecs/wcd934x/wcd934x-mbhc.h"
+#include "../codecs/wsa881x.h"
+
+#define DRV_NAME "msm8998-asoc-snd"
+
+#define __CHIPSET__ "MSM8998 "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define SAMPLING_RATE_8KHZ      8000
+#define SAMPLING_RATE_11P025KHZ 11025
+#define SAMPLING_RATE_16KHZ     16000
+#define SAMPLING_RATE_22P05KHZ  22050
+#define SAMPLING_RATE_32KHZ     32000
+#define SAMPLING_RATE_44P1KHZ   44100
+#define SAMPLING_RATE_48KHZ     48000
+#define SAMPLING_RATE_88P2KHZ   88200
+#define SAMPLING_RATE_96KHZ     96000
+#define SAMPLING_RATE_176P4KHZ  176400
+#define SAMPLING_RATE_192KHZ    192000
+#define SAMPLING_RATE_352P8KHZ  352800
+#define SAMPLING_RATE_384KHZ    384000
+
+#define WCD9XXX_MBHC_DEF_BUTTONS    8
+#define WCD9XXX_MBHC_DEF_RLOADS     5
+#define CODEC_EXT_CLK_RATE          9600000
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+#define DEV_NAME_STR_LEN            32
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+
+#define TDM_CHANNEL_MAX 8
+#define TDM_SLOT_OFFSET_MAX 8
+
+#define MSM_HIFI_ON 1
+
+enum {
+	SLIM_RX_0 = 0,
+	SLIM_RX_1,
+	SLIM_RX_2,
+	SLIM_RX_3,
+	SLIM_RX_4,
+	SLIM_RX_5,
+	SLIM_RX_6,
+	SLIM_RX_7,
+	SLIM_RX_MAX,
+};
+
+enum {
+	SLIM_TX_0 = 0,
+	SLIM_TX_1,
+	SLIM_TX_2,
+	SLIM_TX_3,
+	SLIM_TX_4,
+	SLIM_TX_5,
+	SLIM_TX_6,
+	SLIM_TX_7,
+	SLIM_TX_8,
+	SLIM_TX_MAX,
+};
+
+enum {
+	PRIM_MI2S = 0,
+	SEC_MI2S,
+	TERT_MI2S,
+	QUAT_MI2S,
+	MI2S_MAX,
+};
+
+enum {
+	PRIM_AUX_PCM = 0,
+	SEC_AUX_PCM,
+	TERT_AUX_PCM,
+	QUAT_AUX_PCM,
+	AUX_PCM_MAX,
+};
+
+enum {
+	PCM_I2S_SEL_PRIM = 0,
+	PCM_I2S_SEL_SEC,
+	PCM_I2S_SEL_TERT,
+	PCM_I2S_SEL_QUAT,
+	PCM_I2S_SEL_MAX,
+};
+
+struct mi2s_aux_pcm_common_conf {
+	struct mutex lock;
+	void *pcm_i2s_sel_vt_addr;
+};
+
+struct mi2s_conf {
+	struct mutex lock;
+	u32 ref_cnt;
+	u32 msm_is_mi2s_master;
+};
+
+struct auxpcm_conf {
+	struct mutex lock;
+	u32 ref_cnt;
+};
+
+struct dev_config {
+	u32 sample_rate;
+	u32 bit_format;
+	u32 channels;
+};
+
+enum {
+	HDMI_RX_IDX = 0,
+	DP_RX_IDX,
+	EXT_DISP_RX_IDX_MAX,
+};
+
+struct msm_wsa881x_dev_info {
+	struct device_node *of_node;
+	u32 index;
+};
+
+struct msm_asoc_mach_data {
+	u32 mclk_freq;
+	int us_euro_gpio; /* used by gpio driver API */
+	struct device_node *us_euro_gpio_p; /* used by pinctrl API */
+	struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
+	struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
+	struct snd_info_entry *codec_root;
+};
+
+struct msm_asoc_wcd93xx_codec {
+	void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+				   enum afe_config_type config_type);
+	void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+enum {
+	TDM_0 = 0,
+	TDM_1,
+	TDM_2,
+	TDM_3,
+	TDM_4,
+	TDM_5,
+	TDM_6,
+	TDM_7,
+	TDM_PORT_MAX,
+};
+
+enum {
+	TDM_PRI = 0,
+	TDM_SEC,
+	TDM_TERT,
+	TDM_QUAT,
+	TDM_INTERFACE_MAX,
+};
+
+struct tdm_port {
+	u32 mode;
+	u32 channel;
+};
+
+/* TDM default config */
+static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+	{ /* PRI TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* SEC TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* TERT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* QUAT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	}
+};
+
+/* TDM default config */
+static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+	{ /* PRI TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* SEC TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* TERT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* QUAT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	}
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+	{0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
+/* Default configuration of slimbus channels */
+static struct dev_config slim_rx_cfg[] = {
+	[SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config slim_tx_cfg[] = {
+	[SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+
+/* Default configuration of external display BE */
+static struct dev_config ext_disp_rx_cfg[] = {
+	[HDMI_RX_IDX] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[DP_RX_IDX] =   {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config usb_rx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 2,
+};
+
+static struct dev_config usb_tx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 1,
+};
+
+static struct dev_config proxy_rx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 2,
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config mi2s_rx_cfg[] = {
+	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config mi2s_tx_cfg[] = {
+	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_rx_cfg[] = {
+	[PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_AUX_PCM]  = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_tx_cfg[] = {
+	[PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_AUX_PCM]  = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static int msm_vi_feed_tx_ch = 2;
+static const char *const slim_rx_ch_text[] = {"One", "Two"};
+static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
+						"Five", "Six", "Seven",
+						"Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+					  "S32_LE"};
+static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"};
+static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
+					"KHZ_32", "KHZ_44P1", "KHZ_48",
+					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
+					"KHZ_192", "KHZ_352P8", "KHZ_384"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+					   "Five", "Six", "Seven",
+					   "Eight"};
+static char const *ch_text[] = {"Two", "Three", "Four", "Five",
+					"Six", "Seven", "Eight"};
+static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
+					"KHZ_16", "KHZ_22P05",
+					"KHZ_32", "KHZ_44P1", "KHZ_48",
+					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
+					"KHZ_192", "KHZ_352P8", "KHZ_384"};
+static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+						  "KHZ_192"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+				    "Five", "Six", "Seven", "Eight"};
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"};
+static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
+					     "KHZ_44P1", "KHZ_48", "KHZ_96",
+					     "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
+static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+				      "KHZ_32", "KHZ_44P1", "KHZ_48",
+				      "KHZ_96", "KHZ_192"};
+static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
+					   "Five", "Six", "Seven",
+					   "Eight"};
+static const char *const hifi_text[] = {"Off", "On"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
+				ext_disp_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text);
+
+static struct platform_device *spdev;
+static int msm_hifi_control;
+
+static bool is_initial_boot;
+static bool codec_reg_done;
+static struct snd_soc_aux_dev *msm_aux_dev;
+static struct snd_soc_codec_conf *msm_codec_conf;
+static struct msm_asoc_wcd93xx_codec msm_codec_fn;
+
+static void *def_tasha_mbhc_cal(void);
+static void *def_tavil_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm);
+static int msm_wsa881x_init(struct snd_soc_component *component);
+
+/*
+ * Need to report LINEIN
+ * if R/L channel impedance is larger than 5K ohm
+ */
+static struct wcd_mbhc_config wcd_mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.detect_extn_cable = true,
+	.mono_stero_detection = false,
+	.swap_gnd_mic = NULL,
+	.hs_ext_micbias = true,
+	.key_code[0] = KEY_MEDIA,
+	.key_code[1] = KEY_VOICECOMMAND,
+	.key_code[2] = KEY_VOLUMEUP,
+	.key_code[3] = KEY_VOLUMEDOWN,
+	.key_code[4] = 0,
+	.key_code[5] = 0,
+	.key_code[6] = 0,
+	.key_code[7] = 0,
+	.linein_th = 5000,
+	.moisture_en = true,
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = {
+	{"MIC BIAS1", NULL, "MCLK TX"},
+	{"MIC BIAS2", NULL, "MCLK TX"},
+	{"MIC BIAS3", NULL, "MCLK TX"},
+	{"MIC BIAS4", NULL, "MCLK TX"},
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths[] = {
+	{"MIC BIAS1", NULL, "MCLK"},
+	{"MIC BIAS2", NULL, "MCLK"},
+	{"MIC BIAS3", NULL, "MCLK"},
+	{"MIC BIAS4", NULL, "MCLK"},
+};
+
+static struct afe_clk_set mi2s_clk[MI2S_MAX] = {
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	}
+};
+
+static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
+static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
+static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
+
+static int slim_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val = 0;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 10;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int slim_get_sample_rate(int value)
+{
+	int sample_rate = 0;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int slim_get_bit_format_val(int bit_format)
+{
+	int val = 0;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		val = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		val = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		val = 0;
+		break;
+	}
+	return val;
+}
+
+static int slim_get_bit_format(int val)
+{
+	int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+	switch (val) {
+	case 0:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 3:
+		bit_fmt = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	default:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return bit_fmt;
+}
+
+static int slim_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int port_id = 0;
+
+	if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX")))
+		port_id = SLIM_RX_0;
+	else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX")))
+		port_id = SLIM_RX_2;
+	else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX")))
+		port_id = SLIM_RX_5;
+	else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX")))
+		port_id = SLIM_RX_6;
+	else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX")))
+		port_id = SLIM_TX_0;
+	else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX")))
+		port_id = SLIM_TX_1;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	return port_id;
+}
+
+static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate);
+
+	pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].sample_rate =
+		slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate);
+
+	pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate = 0;
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+	if (sample_rate == SAMPLING_RATE_44P1KHZ) {
+		pr_err("%s: Unsupported sample rate %d: for Tx path\n",
+			__func__, sample_rate);
+		return -EINVAL;
+	}
+	slim_tx_cfg[ch_num].sample_rate = sample_rate;
+
+	pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+			slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format);
+
+	pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].bit_format =
+		slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+			slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format);
+
+	pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_tx_cfg[ch_num].bit_format =
+		slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	pr_debug("%s: msm_slim_[%d]_rx_ch  = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].channels);
+	ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1;
+
+	return 0;
+}
+
+static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_slim_[%d]_rx_ch  = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].channels);
+
+	return 1;
+}
+
+static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	pr_debug("%s: msm_slim_[%d]_tx_ch  = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].channels);
+	ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1;
+
+	return 0;
+}
+
+static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].channels);
+
+	return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+	pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+	return 1;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	/*
+	 * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+	 * when used for BT_SCO use case. Return either Rx or Tx sample rate
+	 * value.
+	 */
+	switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+	case SAMPLING_RATE_48KHZ:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+	pr_debug("%s: sample rate = %d", __func__,
+		 slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+	return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 0:
+	default:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n",
+		 __func__,
+		 slim_rx_cfg[SLIM_RX_7].sample_rate,
+		 slim_tx_cfg[SLIM_TX_7].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: usb_audio_rx_ch  = %d\n", __func__,
+		 usb_rx_cfg.channels);
+	ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1;
+	return 0;
+}
+
+static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels);
+	return 1;
+}
+
+static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+
+	switch (usb_rx_cfg.sample_rate) {
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 12;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 11;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 10;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_22P05KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_11P025KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__,
+		 usb_rx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 12:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	case 11:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+		break;
+	case 2:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 1:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+		break;
+	case 0:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	default:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n",
+		__func__, ucontrol->value.integer.value[0],
+		usb_rx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (usb_rx_cfg.bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ucontrol->value.integer.value[0] = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_rx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	case 2:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_rx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return rc;
+}
+
+static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: usb_audio_tx_ch  = %d\n", __func__,
+		 usb_tx_cfg.channels);
+	ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1;
+	return 0;
+}
+
+static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels);
+	return 1;
+}
+
+static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+
+	switch (usb_tx_cfg.sample_rate) {
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 12;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 11;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 10;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_22P05KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_11P025KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	default:
+		sample_rate_val = 6;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__,
+		 usb_tx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 12:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	case 11:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+		break;
+	case 2:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 1:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+		break;
+	case 0:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	default:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n",
+		__func__, ucontrol->value.integer.value[0],
+		usb_tx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (usb_tx_cfg.bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ucontrol->value.integer.value[0] = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_tx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	case 2:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_tx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return rc;
+}
+
+static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "HDMI_RX", sizeof("HDMI_RX")))
+		idx = HDMI_RX_IDX;
+	else if (strnstr(kcontrol->id.name, "Display Port RX",
+			 sizeof("Display Port RX")))
+		idx = DP_RX_IDX;
+	else {
+		pr_err("%s: unsupported BE: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ext_disp_rx_cfg[idx].bit_format) {
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+		 __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+		 __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.integer.value[0] =
+			ext_disp_rx_cfg[idx].channels - 2;
+
+	pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+		 idx, ext_disp_rx_cfg[idx].channels);
+
+	return 0;
+}
+
+static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ext_disp_rx_cfg[idx].channels =
+			ucontrol->value.integer.value[0] + 2;
+
+	pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+		 idx, ext_disp_rx_cfg[idx].channels);
+	return 1;
+}
+
+static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ext_disp_rx_cfg[idx].sample_rate) {
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__,
+		 idx, ext_disp_rx_cfg[idx].sample_rate);
+
+	return 0;
+}
+
+static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+	default:
+		ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n",
+		 __func__, ucontrol->value.integer.value[0], idx,
+		 ext_disp_rx_cfg[idx].sample_rate);
+	return 0;
+}
+
+static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: proxy_rx channels = %d\n",
+		 __func__, proxy_rx_cfg.channels);
+	ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2;
+
+	return 0;
+}
+
+static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
+	pr_debug("%s: proxy_rx channels = %d\n",
+		 __func__, proxy_rx_cfg.channels);
+
+	return 1;
+}
+
+static int tdm_get_sample_rate(int value)
+{
+	int sample_rate = 0;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 7:
+		sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 8:
+		sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int aux_pcm_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 0:
+	default:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int tdm_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val = 0;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 8;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int aux_pcm_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int tdm_get_port_idx(struct snd_kcontrol *kcontrol,
+			    struct tdm_port *port)
+{
+	if (port) {
+		if (strnstr(kcontrol->id.name, "PRI",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_PRI;
+		} else if (strnstr(kcontrol->id.name, "SEC",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_SEC;
+		} else if (strnstr(kcontrol->id.name, "TERT",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_TERT;
+		} else if (strnstr(kcontrol->id.name, "QUAT",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_QUAT;
+		} else {
+			pr_err("%s: unsupported mode in: %s",
+				__func__, kcontrol->id.name);
+			return -EINVAL;
+		}
+
+		if (strnstr(kcontrol->id.name, "RX_0",
+		    sizeof(kcontrol->id.name)) ||
+		    strnstr(kcontrol->id.name, "TX_0",
+		    sizeof(kcontrol->id.name))) {
+			port->channel = TDM_0;
+		} else if (strnstr(kcontrol->id.name, "RX_1",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_1",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_1;
+		} else if (strnstr(kcontrol->id.name, "RX_2",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_2",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_2;
+		} else if (strnstr(kcontrol->id.name, "RX_3",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_3",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_3;
+		} else if (strnstr(kcontrol->id.name, "RX_4",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_4",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_4;
+		} else if (strnstr(kcontrol->id.name, "RX_5",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_5",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_5;
+		} else if (strnstr(kcontrol->id.name, "RX_6",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_6",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_6;
+		} else if (strnstr(kcontrol->id.name, "RX_7",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_7",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_7;
+		} else {
+			pr_err("%s: unsupported channel in: %s",
+				__func__, kcontrol->id.name);
+			return -EINVAL;
+		}
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+			tdm_rx_cfg[port.mode][port.channel].sample_rate);
+
+		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].sample_rate =
+			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+			tdm_tx_cfg[port.mode][port.channel].sample_rate);
+
+		pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].sample_rate =
+			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_get_format(int value)
+{
+	int format = 0;
+
+	switch (value) {
+	case 0:
+		format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	default:
+		format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return format;
+}
+
+static int tdm_get_format_val(int format)
+{
+	int value = 0;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		value = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		value = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		value = 2;
+		break;
+	default:
+		value = 0;
+		break;
+	}
+	return value;
+}
+
+static int tdm_rx_format_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+				tdm_rx_cfg[port.mode][port.channel].bit_format);
+
+		pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_format_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].bit_format =
+			tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_format_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+				tdm_tx_cfg[port.mode][port.channel].bit_format);
+
+		pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_format_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].bit_format =
+			tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+
+		ucontrol->value.enumerated.item[0] =
+			tdm_rx_cfg[port.mode][port.channel].channels - 1;
+
+		pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].channels - 1,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].channels =
+			ucontrol->value.enumerated.item[0] + 1;
+
+		pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].channels,
+			 ucontrol->value.enumerated.item[0] + 1);
+	}
+	return ret;
+}
+
+static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] =
+			tdm_tx_cfg[port.mode][port.channel].channels - 1;
+
+		pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].channels - 1,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].channels =
+			ucontrol->value.enumerated.item[0] + 1;
+
+		pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].channels,
+			 ucontrol->value.enumerated.item[0] + 1);
+	}
+	return ret;
+}
+
+static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM",
+		    sizeof("PRIM_AUX_PCM")))
+		idx = PRIM_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM",
+			 sizeof("SEC_AUX_PCM")))
+		idx = SEC_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM",
+			 sizeof("TERT_AUX_PCM")))
+		idx = TERT_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM",
+			 sizeof("QUAT_AUX_PCM")))
+		idx = QUAT_AUX_PCM;
+	else {
+		pr_err("%s: unsupported port: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	aux_pcm_rx_cfg[idx].sample_rate =
+		aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+	     aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	aux_pcm_tx_cfg[idx].sample_rate =
+		aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+	     aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX",
+	    sizeof("PRIM_MI2S_RX")))
+		idx = PRIM_MI2S;
+	else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX",
+		 sizeof("SEC_MI2S_RX")))
+		idx = SEC_MI2S;
+	else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX",
+		 sizeof("TERT_MI2S_RX")))
+		idx = TERT_MI2S;
+	else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX",
+		 sizeof("QUAT_MI2S_RX")))
+		idx = QUAT_MI2S;
+	else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX",
+		 sizeof("PRIM_MI2S_TX")))
+		idx = PRIM_MI2S;
+	else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX",
+		 sizeof("SEC_MI2S_TX")))
+		idx = SEC_MI2S;
+	else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX",
+		 sizeof("TERT_MI2S_TX")))
+		idx = TERT_MI2S;
+	else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX",
+		 sizeof("QUAT_MI2S_TX")))
+		idx = QUAT_MI2S;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int mi2s_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int mi2s_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_rx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_tx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: msm_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: msm_mi2s_[%d]_tx_ch  = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_mi2s_[%d]_tx_ch  = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int msm_hifi_ctrl(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s: msm_hifi_control = %d", __func__,
+		 msm_hifi_control);
+
+	if (!pdata || !pdata->hph_en1_gpio_p) {
+		pr_err("%s: hph_en1_gpio is invalid\n", __func__);
+		return -EINVAL;
+	}
+	if (msm_hifi_control == MSM_HIFI_ON) {
+		msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p);
+		/* 5msec delay needed as per HW requirement */
+		usleep_range(5000, 5010);
+	} else {
+		msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p);
+	}
+	snd_soc_dapm_sync(dapm);
+
+	return 0;
+}
+
+static int msm_hifi_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_hifi_control = %d\n",
+		 __func__, msm_hifi_control);
+	ucontrol->value.integer.value[0] = msm_hifi_control;
+
+	return 0;
+}
+
+static int msm_hifi_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+		 __func__, ucontrol->value.integer.value[0]);
+
+	msm_hifi_control = ucontrol->value.integer.value[0];
+	msm_hifi_ctrl(codec);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+	SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs,
+			msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs,
+			msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs,
+			msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs,
+			usb_audio_rx_ch_get, usb_audio_rx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
+			usb_audio_tx_ch_get, usb_audio_tx_ch_put),
+	SOC_ENUM_EXT("HDMI_RX Channels", ext_disp_rx_chs,
+			ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+	SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs,
+			ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+	SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs,
+			proxy_rx_ch_get, proxy_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format,
+			slim_tx_bit_format_get, slim_tx_bit_format_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format,
+			usb_audio_rx_format_get, usb_audio_rx_format_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
+			usb_audio_tx_format_get, usb_audio_tx_format_put),
+	SOC_ENUM_EXT("HDMI_RX Bit Format", ext_disp_rx_format,
+			ext_disp_rx_format_get, ext_disp_rx_format_put),
+	SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format,
+			ext_disp_rx_format_get, ext_disp_rx_format_put),
+	SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate,
+			slim_tx_sample_rate_get, slim_tx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+			msm_bt_sample_rate_get,
+			msm_bt_sample_rate_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
+			usb_audio_rx_sample_rate_get,
+			usb_audio_rx_sample_rate_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
+			usb_audio_tx_sample_rate_get,
+			usb_audio_tx_sample_rate_put),
+	SOC_ENUM_EXT("HDMI_RX SampleRate", ext_disp_rx_sample_rate,
+			ext_disp_rx_sample_rate_get,
+			ext_disp_rx_sample_rate_put),
+	SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate,
+			ext_disp_rx_sample_rate_get,
+			ext_disp_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get,
+			msm_hifi_put),
+};
+
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm)
+{
+	int ret = 0;
+
+	if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+		ret = tasha_cdc_mclk_enable(codec, enable, dapm);
+	else if (!strcmp(dev_name(codec->dev), "tavil_codec"))
+		ret = tavil_cdc_mclk_enable(codec, enable);
+	else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+					   int enable, bool dapm)
+{
+	int ret = 0;
+
+	if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+		ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+	else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int msm_mclk_tx_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_mclk_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_snd_enable_codec_ext_clk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_snd_enable_codec_ext_clk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
+			       struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control);
+
+	if (!pdata || !pdata->hph_en0_gpio_p) {
+		pr_err("%s: hph_en0_gpio is invalid\n", __func__);
+		return -EINVAL;
+	}
+
+	if (msm_hifi_control != MSM_HIFI_ON) {
+		pr_debug("%s: HiFi mixer control is not set\n",
+			 __func__);
+		return 0;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+	SND_SOC_DAPM_SUPPLY("MCLK",  SND_SOC_NOPM, 0, 0,
+			    msm_mclk_event,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("MCLK TX",  SND_SOC_NOPM, 0, 0,
+	msm_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
+	SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+
+	SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+};
+
+static inline int param_is_mask(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+			(p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+					     int n)
+{
+	return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+	if (bit >= SNDRV_MASK_MAX)
+		return;
+	if (param_is_mask(n)) {
+		struct snd_mask *m = param_to_mask(p, n);
+
+		m->bits[0] = 0;
+		m->bits[1] = 0;
+		m->bits[bit >> 5] |= (1 << (bit & 31));
+	}
+}
+
+static int msm_slim_get_ch_from_beid(int32_t be_id)
+{
+	int ch_id = 0;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+		ch_id = SLIM_RX_0;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+		ch_id = SLIM_RX_1;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+		ch_id = SLIM_RX_2;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+		ch_id = SLIM_RX_3;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+		ch_id = SLIM_RX_4;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+		ch_id = SLIM_RX_6;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+		ch_id = SLIM_TX_0;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+		ch_id = SLIM_TX_3;
+		break;
+	default:
+		ch_id = SLIM_RX_0;
+		break;
+	}
+
+	return ch_id;
+}
+
+static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
+{
+	int idx;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_HDMI_RX:
+		idx = HDMI_RX_IDX;
+		break;
+	case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+		idx = DP_RX_IDX;
+		break;
+	default:
+		pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id);
+		idx = -EINVAL;
+		break;
+	}
+
+	return idx;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	int rc = 0;
+	int idx;
+	void *config = NULL;
+	struct snd_soc_codec *codec = NULL;
+
+	pr_debug("%s: format = %d, rate = %d\n",
+		  __func__, params_format(params), params_rate(params));
+
+	switch (dai_link->id) {
+	case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+		idx = msm_slim_get_ch_from_beid(dai_link->id);
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[idx].bit_format);
+		rate->min = rate->max = slim_rx_cfg[idx].sample_rate;
+		channels->min = channels->max = slim_rx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+	case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+		idx = msm_slim_get_ch_from_beid(dai_link->id);
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_tx_cfg[idx].bit_format);
+		rate->min = rate->max = slim_tx_cfg[idx].sample_rate;
+		channels->min = channels->max = slim_tx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_1_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_tx_cfg[1].bit_format);
+		rate->min = rate->max = slim_tx_cfg[1].sample_rate;
+		channels->min = channels->max = slim_tx_cfg[1].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_4_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       SNDRV_PCM_FORMAT_S32_LE);
+		rate->min = rate->max = SAMPLING_RATE_8KHZ;
+		channels->min = channels->max = msm_vi_feed_tx_ch;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_5_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[5].bit_format);
+		rate->min = rate->max = slim_rx_cfg[5].sample_rate;
+		channels->min = channels->max = slim_rx_cfg[5].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_5_TX:
+		codec = rtd->codec;
+		rate->min = rate->max = SAMPLING_RATE_16KHZ;
+		channels->min = channels->max = 1;
+
+		config = msm_codec_fn.get_afe_config_fn(codec,
+					AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+		if (config) {
+			rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+					    config, SLIMBUS_5_TX);
+			if (rc)
+				pr_err("%s: Failed to set slimbus slave port config %d\n",
+					__func__, rc);
+		}
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[SLIM_RX_7].bit_format);
+		rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate;
+		channels->min = channels->max =
+			slim_rx_cfg[SLIM_RX_7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+		rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate;
+		channels->min = channels->max =
+			slim_tx_cfg[SLIM_TX_7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+		rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate;
+		channels->min = channels->max =
+			slim_tx_cfg[SLIM_TX_8].channels;
+		break;
+
+	case MSM_BACKEND_DAI_USB_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				usb_rx_cfg.bit_format);
+		rate->min = rate->max = usb_rx_cfg.sample_rate;
+		channels->min = channels->max = usb_rx_cfg.channels;
+		break;
+
+	case MSM_BACKEND_DAI_USB_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				usb_tx_cfg.bit_format);
+		rate->min = rate->max = usb_tx_cfg.sample_rate;
+		channels->min = channels->max = usb_tx_cfg.channels;
+		break;
+
+	case MSM_BACKEND_DAI_HDMI_RX:
+	case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+		idx = msm_ext_disp_get_idx_from_beid(dai_link->id);
+		if (idx < 0) {
+			pr_err("%s: Incorrect ext disp idx %d\n",
+			       __func__, idx);
+			rc = idx;
+			goto done;
+		}
+
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				ext_disp_rx_cfg[idx].bit_format);
+		rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate;
+		channels->min = channels->max = ext_disp_rx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_AFE_PCM_RX:
+		channels->min = channels->max = proxy_rx_cfg.channels;
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_PRI][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_PRI][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_PRI][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_PRI][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_SEC][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_SEC][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_QUAT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[PRIM_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[PRIM_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[SEC_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[SEC_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[TERT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[TERT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[QUAT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[QUAT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[PRIM_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[PRIM_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[SEC_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[SEC_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[TERT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[TERT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[QUAT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[QUAT_MI2S].channels;
+		break;
+
+	default:
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+done:
+	return rc;
+}
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int value = 0;
+
+	if (pdata->us_euro_gpio_p) {
+		value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p);
+		if (value)
+			msm_cdc_pinctrl_select_sleep_state(
+							pdata->us_euro_gpio_p);
+		else
+			msm_cdc_pinctrl_select_active_state(
+							pdata->us_euro_gpio_p);
+	} else if (pdata->us_euro_gpio >= 0) {
+		value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+		gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+	}
+	pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+	return true;
+}
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	void *config_data = NULL;
+
+	if (!msm_codec_fn.get_afe_config_fn) {
+		dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTERS_CONFIG);
+	if (config_data) {
+		ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+		if (ret) {
+			dev_err(codec->dev,
+				"%s: Failed to set codec registers config %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTER_PAGE_CONFIG);
+	if (config_data) {
+		ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+				    0);
+		if (ret)
+			dev_err(codec->dev,
+				"%s: Failed to set cdc register page config\n",
+				__func__);
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_SLIMBUS_SLAVE_CONFIG);
+	if (config_data) {
+		ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+		if (ret) {
+			dev_err(codec->dev,
+				"%s: Failed to set slimbus slave config %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+	afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+	afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int msm_adsp_power_up_config(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	unsigned long timeout;
+	int adsp_ready = 0;
+
+	timeout = jiffies +
+		msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+	do {
+		if (q6core_is_adsp_ready()) {
+			pr_debug("%s: ADSP Audio is ready\n", __func__);
+			adsp_ready = 1;
+			break;
+		}
+		/*
+		 * ADSP will be coming up after subsystem restart and
+		 * it might not be fully up when the control reaches
+		 * here. So, wait for 50msec before checking ADSP state
+		 */
+		msleep(50);
+	} while (time_after(timeout, jiffies));
+
+	if (!adsp_ready) {
+		pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+		ret = -ETIMEDOUT;
+		goto err_fail;
+	}
+
+	ret = msm_afe_set_config(codec);
+	if (ret)
+		pr_err("%s: Failed to set AFE config. err %d\n",
+			__func__, ret);
+
+	return 0;
+
+err_fail:
+	return ret;
+}
+
+static int msm8998_notifier_service_cb(struct notifier_block *this,
+					 unsigned long opcode, void *ptr)
+{
+	int ret;
+	struct snd_soc_card *card = NULL;
+	const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_codec *codec;
+
+	pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
+
+	switch (opcode) {
+	case AUDIO_NOTIFIER_SERVICE_DOWN:
+		/*
+		 * Use flag to ignore initial boot notifications
+		 * On initial boot msm_adsp_power_up_config is
+		 * called on init. There is no need to clear
+		 * and set the config again on initial boot.
+		 */
+		if (is_initial_boot)
+			break;
+		msm_afe_clear_config();
+		break;
+	case AUDIO_NOTIFIER_SERVICE_UP:
+		if (is_initial_boot) {
+			is_initial_boot = false;
+			break;
+		}
+		if (!spdev)
+			return -EINVAL;
+
+		card = platform_get_drvdata(spdev);
+		rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+		if (!rtd) {
+			dev_err(card->dev,
+				"%s: snd_soc_get_pcm_runtime for %s failed!\n",
+				__func__, be_dl_name);
+			ret = -EINVAL;
+			goto done;
+		}
+		codec = rtd->codec;
+
+		ret = msm_adsp_power_up_config(codec);
+		if (ret < 0) {
+			dev_err(card->dev,
+				"%s: msm_adsp_power_up_config failed ret = %d!\n",
+				__func__, ret);
+			goto done;
+		}
+		break;
+	default:
+		break;
+	}
+done:
+	return NOTIFY_OK;
+}
+
+static struct notifier_block service_nb = {
+	.notifier_call  = msm8998_notifier_service_cb,
+	.priority = -INT_MAX,
+};
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	void *config_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_component *aux_comp;
+	struct snd_card *card;
+	struct snd_info_entry *entry;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(rtd->card);
+
+	/* Codec SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+	 * TX14, TX15, TX16
+	 */
+	unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+					    151, 152, 153, 154, 155, 156};
+	unsigned int tx_ch[TASHA_TX_MAX] = {128, 129, 130, 131, 132, 133,
+					    134, 135, 136, 137, 138, 139,
+					    140, 141, 142, 143};
+
+	pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+	rtd->pmdown_time = 0;
+
+	ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+					 ARRAY_SIZE(msm_snd_controls));
+	if (ret < 0) {
+		pr_err("%s: add_codec_controls failed, err %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+				ARRAY_SIZE(msm_dapm_widgets));
+
+	if (!strcmp(dev_name(codec_dai->dev), "tasha_codec"))
+		snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha,
+					ARRAY_SIZE(wcd_audio_paths_tasha));
+	else
+		snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+					ARRAY_SIZE(wcd_audio_paths));
+
+	snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+	snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+	snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+
+	if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+		snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+		snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+		snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+		snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+		snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+		snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+	}
+
+	snd_soc_dapm_sync(dapm);
+
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+	if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+		msm_codec_fn.get_afe_config_fn = tavil_get_afe_config;
+	} else {
+		msm_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+		msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit;
+	}
+
+	ret = msm_adsp_power_up_config(codec);
+	if (ret) {
+		pr_err("%s: Failed to set AFE config %d\n", __func__, ret);
+		goto err_afe_cfg;
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+						     AFE_AANC_VERSION);
+	if (config_data) {
+		ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+		if (ret) {
+			pr_err("%s: Failed to set aanc version %d\n",
+				__func__, ret);
+			goto err_afe_cfg;
+		}
+	}
+
+	if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+		config_data = msm_codec_fn.get_afe_config_fn(codec,
+						AFE_CDC_CLIP_REGISTERS_CONFIG);
+		if (config_data) {
+			ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+						 config_data, 0);
+			if (ret) {
+				pr_err("%s: Failed to set clip registers %d\n",
+					__func__, ret);
+				goto err_afe_cfg;
+			}
+		}
+		config_data = msm_codec_fn.get_afe_config_fn(codec,
+				AFE_CLIP_BANK_SEL);
+		if (config_data) {
+			ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+			if (ret) {
+				pr_err("%s: Failed to set AFE bank selection %d\n",
+					__func__, ret);
+				goto err_afe_cfg;
+			}
+		}
+	}
+
+	/*
+	 * Send speaker configuration only for WSA8810.
+	 * Defalut configuration is for WSA8815.
+	 */
+	pr_debug("%s: Number of aux devices: %d\n",
+		__func__, rtd->card->num_aux_devs);
+	if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+		if (rtd->card->num_aux_devs &&
+		    !list_empty(&rtd->card->aux_comp_list)) {
+			aux_comp = list_first_entry(&rtd->card->aux_comp_list,
+					struct snd_soc_component, list_aux);
+			if (!strcmp(aux_comp->name, WSA8810_NAME_1) ||
+			    !strcmp(aux_comp->name, WSA8810_NAME_2)) {
+				tavil_set_spkr_mode(rtd->codec,
+						    WCD934X_SPKR_MODE_1);
+				tavil_set_spkr_gain_offset(rtd->codec,
+						WCD934X_RX_GAIN_OFFSET_M1P5_DB);
+			}
+		}
+		card = rtd->card->snd_card;
+		entry = snd_info_create_subdir(card->module, "codecs",
+						 card->proc_root);
+		if (!entry) {
+			pr_debug("%s: Cannot create codecs module entry\n",
+				 __func__);
+			pdata->codec_root = NULL;
+			goto done;
+		}
+		pdata->codec_root = entry;
+		tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
+	} else {
+		if (rtd->card->num_aux_devs &&
+		    !list_empty(&rtd->card->aux_comp_list)) {
+			aux_comp = list_first_entry(&rtd->card->aux_comp_list,
+					struct snd_soc_component, list_aux);
+			if (!strcmp(aux_comp->name, WSA8810_NAME_1) ||
+			    !strcmp(aux_comp->name, WSA8810_NAME_2)) {
+				tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+				tasha_set_spkr_gain_offset(rtd->codec,
+							RX_GAIN_OFFSET_M1P5_DB);
+			}
+		}
+		card = rtd->card->snd_card;
+		entry = snd_info_create_subdir(card->module, "codecs",
+						 card->proc_root);
+		if (!entry) {
+			pr_debug("%s: Cannot create codecs module entry\n",
+				 __func__);
+			ret = 0;
+			goto err_snd_module;
+		}
+		pdata->codec_root = entry;
+		tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
+	}
+done:
+	codec_reg_done = true;
+	return 0;
+
+err_snd_module:
+err_afe_cfg:
+	return ret;
+}
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+	unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+	unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX]  = {159, 160, 161};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+					   tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static void *def_tasha_mbhc_cal(void)
+{
+	void *tasha_wcd_cal;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_high;
+
+	tasha_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+				WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+	if (!tasha_wcd_cal)
+		return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tasha_wcd_cal)->X) = (Y))
+	S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal)->X) = (Y))
+	S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tasha_wcd_cal);
+	btn_high = ((void *)&btn_cfg->_v_btn_low) +
+		(sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+	btn_high[0] = 75;
+	btn_high[1] = 150;
+	btn_high[2] = 237;
+	btn_high[3] = 500;
+	btn_high[4] = 500;
+	btn_high[5] = 500;
+	btn_high[6] = 500;
+	btn_high[7] = 500;
+
+	return tasha_wcd_cal;
+}
+
+static void *def_tavil_mbhc_cal(void)
+{
+	void *tavil_wcd_cal;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_high;
+
+	tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+				WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+	if (!tavil_wcd_cal)
+		return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y))
+	S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y))
+	S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal);
+	btn_high = ((void *)&btn_cfg->_v_btn_low) +
+		(sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+	btn_high[0] = 75;
+	btn_high[1] = 150;
+	btn_high[2] = 237;
+	btn_high[3] = 500;
+	btn_high[4] = 500;
+	btn_high[5] = 500;
+	btn_high[6] = 500;
+	btn_high[7] = 500;
+
+	return tavil_wcd_cal;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+	int ret = 0;
+	u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	u32 user_set_tx_ch = 0;
+	u32 rx_ch_count;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+		if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+			pr_debug("%s: rx_5_ch=%d\n", __func__,
+				  slim_rx_cfg[5].channels);
+			rx_ch_count = slim_rx_cfg[5].channels;
+		} else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) {
+			pr_debug("%s: rx_2_ch=%d\n", __func__,
+				 slim_rx_cfg[2].channels);
+			rx_ch_count = slim_rx_cfg[2].channels;
+		} else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+			pr_debug("%s: rx_6_ch=%d\n", __func__,
+				  slim_rx_cfg[6].channels);
+			rx_ch_count = slim_rx_cfg[6].channels;
+		} else {
+			pr_debug("%s: rx_0_ch=%d\n", __func__,
+				  slim_rx_cfg[0].channels);
+			rx_ch_count = slim_rx_cfg[0].channels;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+						  rx_ch_count, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+	} else {
+
+		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+			 codec_dai->name, codec_dai->id, user_set_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map\n, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+		/* For <codec>_tx1 case */
+		if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+			user_set_tx_ch = slim_tx_cfg[0].channels;
+		/* For <codec>_tx3 case */
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+			user_set_tx_ch = slim_tx_cfg[1].channels;
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+			user_set_tx_ch = msm_vi_feed_tx_ch;
+		else
+			user_set_tx_ch = tx_ch_cnt;
+
+		pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n",
+			 __func__,  slim_tx_cfg[0].channels, user_set_tx_ch,
+			 tx_ch_cnt, dai_link->id);
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+						  user_set_tx_ch, tx_ch, 0, 0);
+		if (ret < 0)
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+	}
+
+err_ch_map:
+	return ret;
+}
+
+static int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+	int ret = 0;
+	u32 tx_ch[SLIM_MAX_TX_PORTS];
+	u32 tx_ch_cnt = 0;
+	u32 user_set_tx_ch = 0;
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+		pr_err("%s: Invalid stream type %d\n",
+			__func__, substream->stream);
+		ret = -EINVAL;
+		goto err_stream_type;
+	}
+
+	pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, NULL, NULL);
+	if (ret < 0) {
+		pr_err("%s: failed to get codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto err_ch_map;
+	}
+
+	user_set_tx_ch = tx_ch_cnt;
+
+	pr_debug("%s: tx_ch_cnt(%d) BE id %d\n",
+		 __func__, tx_ch_cnt, dai_link->id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  user_set_tx_ch, tx_ch, 0, 0);
+	if (ret < 0)
+		pr_err("%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+err_ch_map:
+err_stream_type:
+	return ret;
+}
+
+static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+	unsigned int num_tx_ch = 0;
+	unsigned int num_rx_ch = 0;
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		num_rx_ch =  params_channels(params);
+		pr_debug("%s: %s rx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_rx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+				num_rx_ch, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+	} else {
+		num_tx_ch =  params_channels(params);
+		pr_debug("%s: %s  tx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+				num_tx_ch, tx_ch, 0, 0);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+	}
+
+err_ch_map:
+	return ret;
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	int ret;
+
+	dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+	if (ret) {
+		dev_err(rtd->dev,
+			"%s: failed to get BTFM codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto exit;
+	}
+
+	dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n",
+		__func__, tx_ch_cnt, dai_link->id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+	if (ret)
+		dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+
+exit:
+	return ret;
+}
+
+static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id - 1;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto done;
+	}
+
+	mutex_lock(&auxpcm_intf_conf[index].lock);
+	if (++auxpcm_intf_conf[index].ref_cnt == 1) {
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(1,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
+				__func__);
+			ret = -EINVAL;
+		}
+	}
+	if (ret < 0)
+		auxpcm_intf_conf[index].ref_cnt--;
+
+	mutex_unlock(&auxpcm_intf_conf[index].lock);
+
+done:
+	return ret;
+}
+
+static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id - 1;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__,
+		substream->name, substream->stream,
+		rtd->cpu_dai->name, rtd->cpu_dai->id);
+
+	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, rtd->cpu_dai->id);
+		return;
+	}
+
+	mutex_lock(&auxpcm_intf_conf[index].lock);
+	if (--auxpcm_intf_conf[index].ref_cnt == 0) {
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(0,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
+				__func__);
+			auxpcm_intf_conf[index].ref_cnt++;
+		}
+	}
+	mutex_unlock(&auxpcm_intf_conf[index].lock);
+}
+
+static int msm_get_port_id(int be_id)
+{
+	int afe_port_id;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_PRI_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_PRI_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+		break;
+	default:
+		pr_err("%s: Invalid BE id: %d\n", __func__, be_id);
+		afe_port_id = -EINVAL;
+	}
+
+	return afe_port_id;
+}
+
+static u32 get_mi2s_bits_per_sample(u32 bit_format)
+{
+	u32 bit_per_sample;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bit_per_sample = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bit_per_sample = 16;
+		break;
+	}
+
+	return bit_per_sample;
+}
+
+static void update_mi2s_clk_val(int dai_id, int stream)
+{
+	u32 bit_per_sample;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		bit_per_sample =
+		    get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format);
+		mi2s_clk[dai_id].clk_freq_in_hz =
+		    mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+	} else {
+		bit_per_sample =
+		    get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format);
+		mi2s_clk[dai_id].clk_freq_in_hz =
+		    mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+	}
+
+	if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master)
+		mi2s_clk[dai_id].clk_freq_in_hz = 0;
+}
+
+static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int port_id = 0;
+	int index = cpu_dai->id;
+
+	port_id = msm_get_port_id(rtd->dai_link->id);
+	if (port_id < 0) {
+		dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+		ret = port_id;
+		goto done;
+	}
+
+	if (enable) {
+		update_mi2s_clk_val(index, substream->stream);
+		dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+			mi2s_clk[index].clk_freq_in_hz);
+	}
+
+	mi2s_clk[index].enable = enable;
+	ret = afe_set_lpass_clock_v2(port_id,
+				     &mi2s_clk[index]);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+			"%s: afe lpass clock failed for port 0x%x , err:%d\n",
+			__func__, port_id, ret);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id;
+	unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_MI2S || index > QUAT_MI2S) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto done;
+	}
+	/*
+	 * Muxtex protection in case the same MI2S
+	 * interface using for both TX and RX  so
+	 * that the same clock won't be enable twice.
+	 */
+	mutex_lock(&mi2s_intf_conf[index].lock);
+	if (++mi2s_intf_conf[index].ref_cnt == 1) {
+		ret = msm_mi2s_set_sclk(substream, true);
+		if (ret < 0) {
+			dev_err(rtd->card->dev,
+				"%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+				__func__, ret);
+			goto clean_up;
+		}
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(0,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
+				__func__, index);
+			ret = -EINVAL;
+			goto clk_off;
+		}
+		/* Check if msm needs to provide the clock to the interface */
+		if (!mi2s_intf_conf[index].msm_is_mi2s_master)
+			fmt = SND_SOC_DAIFMT_CBM_CFM;
+		ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+		if (ret < 0) {
+			pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
+				__func__, index, ret);
+			goto clk_off;
+		}
+	}
+clk_off:
+	if (ret < 0)
+		msm_mi2s_set_sclk(substream, false);
+clean_up:
+	if (ret < 0)
+		mi2s_intf_conf[index].ref_cnt--;
+	mutex_unlock(&mi2s_intf_conf[index].lock);
+done:
+	return ret;
+}
+
+static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id;
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+	if (index < PRIM_MI2S || index > QUAT_MI2S) {
+		pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+		return;
+	}
+
+	mutex_lock(&mi2s_intf_conf[index].lock);
+	if (--mi2s_intf_conf[index].ref_cnt == 0) {
+		ret = msm_mi2s_set_sclk(substream, false);
+		if (ret < 0) {
+			pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n",
+				__func__, index, ret);
+			mi2s_intf_conf[index].ref_cnt++;
+		}
+	}
+	mutex_unlock(&mi2s_intf_conf[index].lock);
+}
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+	.startup = msm_mi2s_snd_startup,
+	.shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+	.startup = msm_aux_pcm_snd_startup,
+	.shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+					    int slots)
+{
+	unsigned int slot_mask = 0;
+	int i, j;
+	unsigned int *slot_offset;
+
+	for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+		slot_offset = tdm_slot_offset[i];
+
+		for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+			if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+				slot_mask |=
+				(1 << ((slot_offset[j] * 8) / slot_width));
+			else
+				break;
+		}
+	}
+
+	return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+	int channels, slot_width, slots;
+	unsigned int slot_mask;
+	unsigned int *slot_offset;
+	int offset_channels = 0;
+	int i;
+
+	pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+	channels = params_channels(params);
+	switch (channels) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S32_LE:
+		case SNDRV_PCM_FORMAT_S24_LE:
+		case SNDRV_PCM_FORMAT_S16_LE:
+		/*
+		 * up to 8 channels HW config should
+		 * use 32 bit slot width for max support of
+		 * stream bit width. (slot_width > bit_width)
+		 */
+			slot_width = 32;
+			break;
+		default:
+			pr_err("%s: invalid param format 0x%x\n",
+				__func__, params_format(params));
+			return -EINVAL;
+		}
+		slots = 8;
+		slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+						    slot_width,
+						    slots);
+		if (!slot_mask) {
+			pr_err("%s: invalid slot_mask 0x%x\n",
+				__func__, slot_mask);
+			return -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s: invalid param channels %d\n",
+			__func__, channels);
+		return -EINVAL;
+	}
+	/* currently only supporting TDM_RX_0 and TDM_TX_0 */
+	switch (cpu_dai->id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		slot_offset = tdm_slot_offset[TDM_0];
+		break;
+	default:
+		pr_err("%s: dai id 0x%x not supported\n",
+			__func__, cpu_dai->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+		if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+			offset_channels++;
+		else
+			break;
+	}
+
+	if (offset_channels == 0) {
+		pr_err("%s: slot offset not supported, offset_channels %d\n",
+			__func__, offset_channels);
+		return -EINVAL;
+	}
+
+	if (channels > offset_channels) {
+		pr_err("%s: channels %d exceed offset_channels %d\n",
+			__func__, channels, offset_channels);
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+						  channels, slot_offset);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	} else {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+						  slot_offset, 0, NULL);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	}
+end:
+	return ret;
+}
+
+static struct snd_soc_ops msm_be_ops = {
+	.hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm_cpe_ops = {
+	.hw_params = msm_snd_cpe_hw_params,
+};
+
+static struct snd_soc_ops msm_slimbus_2_be_ops = {
+	.hw_params = msm_slimbus_2_hw_params,
+};
+
+static struct snd_soc_ops msm_wcn_ops = {
+	.hw_params = msm_wcn_hw_params,
+};
+
+static struct snd_soc_ops msm_tdm_be_ops = {
+	.hw_params = msm_tdm_snd_hw_params
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_common_dai_links[] = {
+	/* FrontEnd DAI Links */
+	{
+		.name = MSM_DAILINK_NAME(Media1),
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name = "MultiMedia1",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+	{
+		.name = MSM_DAILINK_NAME(Media2),
+		.stream_name = "MultiMedia2",
+		.cpu_dai_name = "MultiMedia2",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+	},
+	{
+		.name = "VoiceMMode1",
+		.stream_name = "VoiceMMode1",
+		.cpu_dai_name = "VoiceMMode1",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICEMMODE1,
+	},
+	{
+		.name = "MSM VoIP",
+		.stream_name = "VoIP",
+		.cpu_dai_name = "VoIP",
+		.platform_name = "msm-voip-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_VOIP,
+	},
+	{
+		.name = MSM_DAILINK_NAME(ULL),
+		.stream_name = "MultiMedia3",
+		.cpu_dai_name = "MultiMedia3",
+		.platform_name = "msm-pcm-dsp.2",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+	},
+	/* Hostless PCM purpose */
+	{
+		.name = "SLIMBUS_0 Hostless",
+		.stream_name = "SLIMBUS_0 Hostless",
+		.cpu_dai_name = "SLIMBUS0_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "MSM AFE-PCM RX",
+		.stream_name = "AFE-PROXY RX",
+		.cpu_dai_name = "msm-dai-q6-dev.241",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.platform_name = "msm-pcm-afe",
+		.dpcm_playback = 1,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = "MSM AFE-PCM TX",
+		.stream_name = "AFE-PROXY TX",
+		.cpu_dai_name = "msm-dai-q6-dev.240",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.platform_name  = "msm-pcm-afe",
+		.dpcm_capture = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress1),
+		.stream_name = "Compress1",
+		.cpu_dai_name = "MultiMedia4",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+	},
+	{
+		.name = "AUXPCM Hostless",
+		.stream_name = "AUXPCM Hostless",
+		.cpu_dai_name = "AUXPCM_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_1 Hostless",
+		.stream_name = "SLIMBUS_1 Hostless",
+		.cpu_dai_name = "SLIMBUS1_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_3 Hostless",
+		.stream_name = "SLIMBUS_3 Hostless",
+		.cpu_dai_name = "SLIMBUS3_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_4 Hostless",
+		.stream_name = "SLIMBUS_4 Hostless",
+		.cpu_dai_name = "SLIMBUS4_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = MSM_DAILINK_NAME(LowLatency),
+		.stream_name = "MultiMedia5",
+		.cpu_dai_name = "MultiMedia5",
+		.platform_name = "msm-pcm-dsp.1",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+	},
+	{
+		.name = "Listen 1 Audio Service",
+		.stream_name = "Listen 1 Audio Service",
+		.cpu_dai_name = "LSM1",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+			     SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM1,
+	},
+	/* Multiple Tunnel instances */
+	{
+		.name = MSM_DAILINK_NAME(Compress2),
+		.stream_name = "Compress2",
+		.cpu_dai_name = "MultiMedia7",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress3),
+		.stream_name = "Compress3",
+		.cpu_dai_name = "MultiMedia10",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+	},
+	{
+		.name = MSM_DAILINK_NAME(ULL_NOIRQ),
+		.stream_name = "MM_NOIRQ",
+		.cpu_dai_name = "MultiMedia8",
+		.platform_name = "msm-pcm-dsp-noirq",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+	},
+	/* HDMI Hostless */
+	{
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name = "HDMI_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "VoiceMMode2",
+		.stream_name = "VoiceMMode2",
+		.cpu_dai_name = "VoiceMMode2",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICEMMODE2,
+	},
+	/* LSM FE */
+	{
+		.name = "Listen 2 Audio Service",
+		.stream_name = "Listen 2 Audio Service",
+		.cpu_dai_name = "LSM2",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM2,
+	},
+	{
+		.name = "Listen 3 Audio Service",
+		.stream_name = "Listen 3 Audio Service",
+		.cpu_dai_name = "LSM3",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM3,
+	},
+	{
+		.name = "Listen 4 Audio Service",
+		.stream_name = "Listen 4 Audio Service",
+		.cpu_dai_name = "LSM4",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM4,
+	},
+	{
+		.name = "Listen 5 Audio Service",
+		.stream_name = "Listen 5 Audio Service",
+		.cpu_dai_name = "LSM5",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM5,
+	},
+	{
+		.name = "Listen 6 Audio Service",
+		.stream_name = "Listen 6 Audio Service",
+		.cpu_dai_name = "LSM6",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM6,
+	},
+	{
+		.name = "Listen 7 Audio Service",
+		.stream_name = "Listen 7 Audio Service",
+		.cpu_dai_name = "LSM7",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM7,
+	},
+	{
+		.name = "Listen 8 Audio Service",
+		.stream_name = "Listen 8 Audio Service",
+		.cpu_dai_name = "LSM8",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM8,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Media9),
+		.stream_name = "MultiMedia9",
+		.cpu_dai_name = "MultiMedia9",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress4),
+		.stream_name = "Compress4",
+		.cpu_dai_name = "MultiMedia11",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress5),
+		.stream_name = "Compress5",
+		.cpu_dai_name = "MultiMedia12",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress6),
+		.stream_name = "Compress6",
+		.cpu_dai_name = "MultiMedia13",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress7),
+		.stream_name = "Compress7",
+		.cpu_dai_name = "MultiMedia14",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress8),
+		.stream_name = "Compress8",
+		.cpu_dai_name = "MultiMedia15",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress9),
+		.stream_name = "Compress9",
+		.cpu_dai_name = "MultiMedia16",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+	},
+	{
+		.name = "SLIMBUS_8 Hostless",
+		.stream_name = "SLIMBUS8_HOSTLESS Capture",
+		.cpu_dai_name = "SLIMBUS8_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+};
+
+static struct snd_soc_dai_link msm_tasha_fe_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_vifeedback",
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+	},
+	/* Ultrasound RX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Playback",
+		.stream_name = "SLIMBUS_2 Hostless Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_slimbus_2_be_ops,
+	},
+	/* Ultrasound TX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Capture",
+		.stream_name = "SLIMBUS_2 Hostless Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16389",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_slimbus_2_be_ops,
+	},
+	/* CPE LSM direct dai-link */
+	{
+		.name = "CPE Listen service",
+		.stream_name = "CPE Listen Audio Service",
+		.cpu_dai_name = "msm-dai-slim",
+		.platform_name = "msm-cpe-lsm",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "tasha_mad1",
+		.codec_name = "tasha_codec",
+		.ops = &msm_cpe_ops,
+	},
+	{
+		.name = "SLIMBUS_6 Hostless Playback",
+		.stream_name = "SLIMBUS_6 Hostless",
+		.cpu_dai_name = "SLIMBUS6_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* CPE LSM EC PP direct dai-link */
+	{
+		.name = "CPE Listen service ECPP",
+		.stream_name = "CPE Listen Audio Service ECPP",
+		.cpu_dai_name = "CPE_LSM_NOHOST",
+		.platform_name = "msm-cpe-lsm.3",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "tasha_cpe",
+		.codec_name = "tasha_codec",
+	},
+};
+
+static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_vifeedback",
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+	},
+	/* Ultrasound RX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Playback",
+		.stream_name = "SLIMBUS_2 Hostless Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_slimbus_2_be_ops,
+	},
+	/* Ultrasound TX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Capture",
+		.stream_name = "SLIMBUS_2 Hostless Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16389",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_slimbus_2_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_common_be_dai_links[] = {
+	/* Backend AFE DAI Links */
+	{
+		.name = LPASS_BE_AFE_PCM_RX,
+		.stream_name = "AFE Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.224",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_AFE_PCM_TX,
+		.stream_name = "AFE Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.225",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_AFE_PCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music 2 BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
+		.stream_name = "Voice2 Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_RX,
+		.stream_name = "USB Audio Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.28672",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_USB_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_TX,
+		.stream_name = "USB Audio Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.28673",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_USB_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_RX_0,
+		.stream_name = "Primary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36864",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_TX_0,
+		.stream_name = "Primary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36865",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_RX_0,
+		.stream_name = "Secondary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36880",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_TX_0,
+		.stream_name = "Secondary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36881",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_RX_0,
+		.stream_name = "Tertiary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36896",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_TX_0,
+		.stream_name = "Tertiary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36897",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_RX_0,
+		.stream_name = "Quaternary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36912",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_TX_0,
+		.stream_name = "Quaternary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36913",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_tasha_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx3",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_5_RX,
+		.stream_name = "Slimbus5 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16394",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx3",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* MAD BE */
+	{
+		.name = LPASS_BE_SLIMBUS_5_TX,
+		.stream_name = "Slimbus5 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16395",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mad1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_6_RX,
+		.stream_name = "Slimbus6 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16396",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx4",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_tavil_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx3",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_2_RX,
+		.stream_name = "Slimbus2 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx2",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_2_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_5_RX,
+		.stream_name = "Slimbus5 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16394",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx3",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* MAD BE */
+	{
+		.name = LPASS_BE_SLIMBUS_5_TX,
+		.stream_name = "Slimbus5 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16395",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_mad1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_6_RX,
+		.stream_name = "Slimbus6 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16396",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx4",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_7_RX,
+		.stream_name = "Slimbus7 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16398",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		/* BT codec driver determines capabilities based on
+		 * dai name, bt codecdai name should always contains
+		 * supported usecase information
+		 */
+		.codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_7_TX,
+		.stream_name = "Slimbus7 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16399",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_bt_sco_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_8_TX,
+		.stream_name = "Slimbus8 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16401",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_fm_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.init = &msm_wcn_init,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
+	/* HDMI BACK END DAI Link */
+	{
+		.name = LPASS_BE_HDMI,
+		.stream_name = "HDMI Playback",
+		.cpu_dai_name = "msm-dai-q6-hdmi.8",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-ext-disp-audio-codec-rx",
+		.codec_dai_name = "msm_hdmi_audio_codec_rx_dai",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_HDMI_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* DISP PORT BACK END DAI Link */
+	{
+		.name = LPASS_BE_DISPLAY_PORT,
+		.stream_name = "Display Port Playback",
+		.cpu_dai_name = "msm-dai-q6-dp.24608",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-ext-disp-audio-codec-rx",
+		.codec_dai_name = "msm_dp_audio_codec_rx_dai",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+	{
+		.name = LPASS_BE_PRI_MI2S_RX,
+		.stream_name = "Primary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_MI2S_TX,
+		.stream_name = "Primary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_RX,
+		.stream_name = "Secondary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_TX,
+		.stream_name = "Secondary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_RX,
+		.stream_name = "Tertiary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_TX,
+		.stream_name = "Tertiary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_RX,
+		.stream_name = "Quaternary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_TX,
+		.stream_name = "Quaternary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+	/* Primary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_AUXPCM_RX,
+		.stream_name = "AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_AUXPCM_TX,
+		.stream_name = "AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Secondary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_SEC_AUXPCM_RX,
+		.stream_name = "Sec AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SEC_AUXPCM_TX,
+		.stream_name = "Sec AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Tertiary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_TERT_AUXPCM_RX,
+		.stream_name = "Tert AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_TERT_AUXPCM_TX,
+		.stream_name = "Tert AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Quaternary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_RX,
+		.stream_name = "Quat AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_TX,
+		.stream_name = "Quat AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_tasha_dai_links[
+			 ARRAY_SIZE(msm_common_dai_links) +
+			 ARRAY_SIZE(msm_tasha_fe_dai_links) +
+			 ARRAY_SIZE(msm_common_be_dai_links) +
+			 ARRAY_SIZE(msm_tasha_be_dai_links) +
+			 ARRAY_SIZE(msm_wcn_be_dai_links) +
+			 ARRAY_SIZE(ext_disp_be_dai_link) +
+			 ARRAY_SIZE(msm_mi2s_be_dai_links) +
+			 ARRAY_SIZE(msm_auxpcm_be_dai_links)];
+
+static struct snd_soc_dai_link msm_tavil_dai_links[
+			 ARRAY_SIZE(msm_common_dai_links) +
+			 ARRAY_SIZE(msm_tavil_fe_dai_links) +
+			 ARRAY_SIZE(msm_common_be_dai_links) +
+			 ARRAY_SIZE(msm_tavil_be_dai_links) +
+			 ARRAY_SIZE(msm_wcn_be_dai_links) +
+			 ARRAY_SIZE(ext_disp_be_dai_link) +
+			 ARRAY_SIZE(msm_mi2s_be_dai_links) +
+			 ARRAY_SIZE(msm_auxpcm_be_dai_links)];
+
+static int msm_snd_card_late_probe(struct snd_soc_card *card)
+{
+	const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+	struct snd_soc_pcm_runtime *rtd;
+	int ret = 0;
+	void *mbhc_calibration;
+
+	rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+	if (!rtd) {
+		dev_err(card->dev,
+			"%s: snd_soc_get_pcm_runtime for %s failed!\n",
+			__func__, be_dl_name);
+		ret = -EINVAL;
+		goto err_pcm_runtime;
+	}
+
+	mbhc_calibration = def_tasha_mbhc_cal();
+	if (!mbhc_calibration) {
+		ret = -ENOMEM;
+		goto err_mbhc_cal;
+	}
+	wcd_mbhc_cfg.calibration = mbhc_calibration;
+	ret = tasha_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg);
+	if (ret) {
+		dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n",
+			__func__, ret);
+		goto err_hs_detect;
+	}
+	return 0;
+
+err_hs_detect:
+	kfree(mbhc_calibration);
+err_mbhc_cal:
+err_pcm_runtime:
+	return ret;
+}
+
+static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card)
+{
+	const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+	struct snd_soc_pcm_runtime *rtd;
+	int ret = 0;
+	void *mbhc_calibration;
+
+	rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+	if (!rtd) {
+		dev_err(card->dev,
+			"%s: snd_soc_get_pcm_runtime for %s failed!\n",
+			__func__, be_dl_name);
+		ret = -EINVAL;
+		goto err_pcm_runtime;
+	}
+
+	mbhc_calibration = def_tavil_mbhc_cal();
+	if (!mbhc_calibration) {
+		ret = -ENOMEM;
+		goto err_mbhc_cal;
+	}
+	wcd_mbhc_cfg.calibration = mbhc_calibration;
+	ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg);
+	if (ret) {
+		dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n",
+			__func__, ret);
+		goto err_hs_detect;
+	}
+	return 0;
+
+err_hs_detect:
+	kfree(mbhc_calibration);
+err_mbhc_cal:
+err_pcm_runtime:
+	return ret;
+}
+
+struct snd_soc_card snd_soc_card_tasha_msm = {
+	.name		= "msm8998-tasha-snd-card",
+	.late_probe	= msm_snd_card_late_probe,
+};
+
+struct snd_soc_card snd_soc_card_tavil_msm = {
+	.name		= "msm8998-tavil-snd-card",
+	.late_probe	= msm_snd_card_tavil_late_probe,
+};
+
+static int msm_populate_dai_link_component_of_node(
+					struct snd_soc_card *card)
+{
+	int i, index, ret = 0;
+	struct device *cdev = card->dev;
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+	struct device_node *np;
+
+	if (!cdev) {
+		pr_err("%s: Sound card device memory NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < card->num_links; i++) {
+		if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+			continue;
+
+		/* populate platform_of_node for snd card dai links */
+		if (dai_link[i].platform_name &&
+		    !dai_link[i].platform_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						"asoc-platform-names",
+						dai_link[i].platform_name);
+			if (index < 0) {
+				pr_err("%s: No match found for platform name: %s\n",
+					__func__, dai_link[i].platform_name);
+				ret = index;
+				goto err;
+			}
+			np = of_parse_phandle(cdev->of_node, "asoc-platform",
+					      index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+					__func__, dai_link[i].platform_name,
+					index);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].platform_of_node = np;
+			dai_link[i].platform_name = NULL;
+		}
+
+		/* populate cpu_of_node for snd card dai links */
+		if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						 "asoc-cpu-names",
+						 dai_link[i].cpu_dai_name);
+			if (index >= 0) {
+				np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+						index);
+				if (!np) {
+					pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+						__func__,
+						dai_link[i].cpu_dai_name);
+					ret = -ENODEV;
+					goto err;
+				}
+				dai_link[i].cpu_of_node = np;
+				dai_link[i].cpu_dai_name = NULL;
+			}
+		}
+
+		/* populate codec_of_node for snd card dai links */
+		if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						 "asoc-codec-names",
+						 dai_link[i].codec_name);
+			if (index < 0)
+				continue;
+			np = of_parse_phandle(cdev->of_node, "asoc-codec",
+					      index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for codec %s failed\n",
+					__func__, dai_link[i].codec_name);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].codec_of_node = np;
+			dai_link[i].codec_name = NULL;
+		}
+	}
+
+err:
+	return ret;
+}
+
+static int msm_prepare_us_euro(struct snd_soc_card *card)
+{
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	if (pdata->us_euro_gpio >= 0) {
+		dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+			pdata->us_euro_gpio);
+		ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: Failed to request codec US/EURO gpio %d error %d\n",
+				__func__, pdata->us_euro_gpio, ret);
+		}
+	}
+
+	return ret;
+}
+
+static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+					 ARRAY_SIZE(msm_snd_controls));
+	if (ret < 0) {
+		dev_err(codec->dev, "%s: add_codec_controls failed, err%d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+				ARRAY_SIZE(msm_dapm_widgets));
+
+	return 0;
+}
+
+static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+	int ret = 0;
+	unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150,
+				151, 152, 153, 154, 155, 156};
+	unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133,
+				134, 135, 136, 137, 138, 139,
+				140, 141, 142, 143};
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+						  slim_rx_cfg[0].channels,
+						  rx_ch);
+		if (ret < 0)
+			pr_err("%s: RX failed to set cpu chan map error %d\n",
+				__func__, ret);
+	} else {
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+						  slim_tx_cfg[0].channels,
+						  tx_ch, 0, 0);
+		if (ret < 0)
+			pr_err("%s: TX failed to set cpu chan map error %d\n",
+				__func__, ret);
+	}
+
+	return ret;
+}
+
+static struct snd_soc_ops msm_stub_be_ops = {
+	.hw_params = msm_snd_stub_hw_params,
+};
+
+static struct snd_soc_dai_link msm_stub_fe_dai_links[] = {
+
+	/* FrontEnd DAI Links */
+	{
+		.name = "MSMSTUB Media1",
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name = "MultiMedia1",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+};
+
+static struct snd_soc_dai_link msm_stub_be_dai_links[] = {
+
+	/* Backend DAI Links */
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_stub_init,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.ignore_suspend = 1,
+		.ops = &msm_stub_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_stub_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_stub_dai_links[
+			 ARRAY_SIZE(msm_stub_fe_dai_links) +
+			 ARRAY_SIZE(msm_stub_be_dai_links)];
+
+struct snd_soc_card snd_soc_card_stub_msm = {
+	.name		= "msm8998-stub-snd-card",
+};
+
+static const struct of_device_id msm8998_asoc_machine_of_match[]  = {
+	{ .compatible = "qcom,msm8998-asoc-snd-tasha",
+	  .data = "tasha_codec"},
+	{ .compatible = "qcom,msm8998-asoc-snd-tavil",
+	  .data = "tavil_codec"},
+	{ .compatible = "qcom,msm8998-asoc-snd-stub",
+	  .data = "stub_codec"},
+	{},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+	struct snd_soc_card *card = NULL;
+	struct snd_soc_dai_link *dailink;
+	int len_1, len_2, len_3;
+	int total_links;
+	const struct of_device_id *match;
+
+	match = of_match_node(msm8998_asoc_machine_of_match, dev->of_node);
+	if (!match) {
+		dev_err(dev, "%s: No DT match found for sound card\n",
+			__func__);
+		return NULL;
+	}
+
+	if (!strcmp(match->data, "tasha_codec")) {
+		card = &snd_soc_card_tasha_msm;
+		len_1 = ARRAY_SIZE(msm_common_dai_links);
+		len_2 = len_1 + ARRAY_SIZE(msm_tasha_fe_dai_links);
+		len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links);
+		total_links = len_3 + ARRAY_SIZE(msm_tasha_be_dai_links);
+		memcpy(msm_tasha_dai_links,
+		       msm_common_dai_links,
+		       sizeof(msm_common_dai_links));
+		memcpy(msm_tasha_dai_links + len_1,
+		       msm_tasha_fe_dai_links,
+		       sizeof(msm_tasha_fe_dai_links));
+		memcpy(msm_tasha_dai_links + len_2,
+		       msm_common_be_dai_links,
+		       sizeof(msm_common_be_dai_links));
+		memcpy(msm_tasha_dai_links + len_3,
+		       msm_tasha_be_dai_links,
+		       sizeof(msm_tasha_be_dai_links));
+
+		if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+			dev_dbg(dev, "%s(): WCN BTFM support present\n",
+				__func__);
+			memcpy(msm_tasha_dai_links + total_links,
+			       msm_wcn_be_dai_links,
+			       sizeof(msm_wcn_be_dai_links));
+			total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
+		}
+
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,ext-disp-audio-rx")) {
+			dev_dbg(dev, "%s(): External display audio support present\n",
+				__func__);
+			memcpy(msm_tasha_dai_links + total_links,
+			ext_disp_be_dai_link,
+			sizeof(ext_disp_be_dai_link));
+			total_links += ARRAY_SIZE(ext_disp_be_dai_link);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,mi2s-audio-intf")) {
+			memcpy(msm_tasha_dai_links + total_links,
+			       msm_mi2s_be_dai_links,
+			       sizeof(msm_mi2s_be_dai_links));
+			total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,auxpcm-audio-intf")) {
+			memcpy(msm_tasha_dai_links + total_links,
+			       msm_auxpcm_be_dai_links,
+			       sizeof(msm_auxpcm_be_dai_links));
+			total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+		}
+		dailink = msm_tasha_dai_links;
+	}  else if (!strcmp(match->data, "tavil_codec")) {
+		card = &snd_soc_card_tavil_msm;
+		len_1 = ARRAY_SIZE(msm_common_dai_links);
+		len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links);
+		len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links);
+		total_links = len_3 + ARRAY_SIZE(msm_tavil_be_dai_links);
+		memcpy(msm_tavil_dai_links,
+		       msm_common_dai_links,
+		       sizeof(msm_common_dai_links));
+		memcpy(msm_tavil_dai_links + len_1,
+		       msm_tavil_fe_dai_links,
+		       sizeof(msm_tavil_fe_dai_links));
+		memcpy(msm_tavil_dai_links + len_2,
+		       msm_common_be_dai_links,
+		       sizeof(msm_common_be_dai_links));
+		memcpy(msm_tavil_dai_links + len_3,
+		       msm_tavil_be_dai_links,
+		       sizeof(msm_tavil_be_dai_links));
+
+		if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+			dev_dbg(dev, "%s(): WCN BTFM support present\n",
+				__func__);
+			memcpy(msm_tavil_dai_links + total_links,
+			       msm_wcn_be_dai_links,
+			       sizeof(msm_wcn_be_dai_links));
+			total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
+		}
+
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,ext-disp-audio-rx")) {
+			dev_dbg(dev, "%s(): ext disp audio support present\n",
+				__func__);
+			memcpy(msm_tavil_dai_links + total_links,
+			       ext_disp_be_dai_link,
+			       sizeof(ext_disp_be_dai_link));
+			total_links += ARRAY_SIZE(ext_disp_be_dai_link);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,mi2s-audio-intf")) {
+			memcpy(msm_tavil_dai_links + total_links,
+			       msm_mi2s_be_dai_links,
+			       sizeof(msm_mi2s_be_dai_links));
+			total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,auxpcm-audio-intf")) {
+			memcpy(msm_tavil_dai_links + total_links,
+			msm_auxpcm_be_dai_links,
+			sizeof(msm_auxpcm_be_dai_links));
+			total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+		}
+		dailink = msm_tavil_dai_links;
+	} else if (!strcmp(match->data, "stub_codec")) {
+		card = &snd_soc_card_stub_msm;
+		len_1 = ARRAY_SIZE(msm_stub_fe_dai_links);
+		len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links);
+
+		memcpy(msm_stub_dai_links,
+		       msm_stub_fe_dai_links,
+		       sizeof(msm_stub_fe_dai_links));
+		memcpy(msm_stub_dai_links + len_1,
+		       msm_stub_be_dai_links,
+		       sizeof(msm_stub_be_dai_links));
+
+		dailink = msm_stub_dai_links;
+		total_links = len_2;
+	}
+
+	if (card) {
+		card->dai_link = dailink;
+		card->num_links = total_links;
+	}
+
+	return card;
+}
+
+static int msm_wsa881x_init(struct snd_soc_component *component)
+{
+	u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+	u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+	unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+	unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+	struct msm_asoc_mach_data *pdata;
+	struct snd_soc_dapm_context *dapm;
+	int ret = 0;
+
+	if (!codec) {
+		pr_err("%s codec is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dapm = snd_soc_codec_get_dapm(codec);
+
+	if (!strcmp(component->name_prefix, "SpkrLeft")) {
+		dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+			__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkleft_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+		}
+	} else if (!strcmp(component->name_prefix, "SpkrRight")) {
+		dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+			__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkright_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+		}
+	} else {
+		dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+			codec->component.name);
+		ret = -EINVAL;
+		goto err_codec;
+	}
+	pdata = snd_soc_card_get_drvdata(component->card);
+	if (pdata && pdata->codec_root)
+		wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+						      codec);
+
+err_codec:
+	return ret;
+}
+
+static int msm_init_wsa_dev(struct platform_device *pdev,
+				struct snd_soc_card *card)
+{
+	struct device_node *wsa_of_node;
+	u32 wsa_max_devs;
+	u32 wsa_dev_cnt;
+	int i;
+	struct msm_wsa881x_dev_info *wsa881x_dev_info;
+	const char *wsa_auxdev_name_prefix[1];
+	char *dev_name_str = NULL;
+	int found = 0;
+	int ret = 0;
+
+	/* Get maximum WSA device count for this platform */
+	ret = of_property_read_u32(pdev->dev.of_node,
+				   "qcom,wsa-max-devs", &wsa_max_devs);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			 "%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+			 __func__, pdev->dev.of_node->full_name, ret);
+		goto err_dt;
+	}
+	if (wsa_max_devs == 0) {
+		dev_warn(&pdev->dev,
+			 "%s: Max WSA devices is 0 for this target?\n",
+			 __func__);
+		goto err_dt;
+	}
+
+	/* Get count of WSA device phandles for this platform */
+	wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+						 "qcom,wsa-devs", NULL);
+	if (wsa_dev_cnt == -ENOENT) {
+		dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+			 __func__);
+		goto err_dt;
+	} else if (wsa_dev_cnt <= 0) {
+		dev_err(&pdev->dev,
+			"%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+			__func__, wsa_dev_cnt);
+		ret = -EINVAL;
+		goto err_dt;
+	}
+
+	/*
+	 * Expect total phandles count to be NOT less than maximum possible
+	 * WSA count. However, if it is less, then assign same value to
+	 * max count as well.
+	 */
+	if (wsa_dev_cnt < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+			__func__, wsa_max_devs, wsa_dev_cnt);
+		wsa_max_devs = wsa_dev_cnt;
+	}
+
+	/* Make sure prefix string passed for each WSA device */
+	ret = of_property_count_strings(pdev->dev.of_node,
+					"qcom,wsa-aux-dev-prefix");
+	if (ret != wsa_dev_cnt) {
+		dev_err(&pdev->dev,
+			"%s: expecting %d wsa prefix. Defined only %d in DT\n",
+			__func__, wsa_dev_cnt, ret);
+		ret = -EINVAL;
+		goto err_dt;
+	}
+
+	/*
+	 * Alloc mem to store phandle and index info of WSA device, if already
+	 * registered with ALSA core
+	 */
+	wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+					sizeof(struct msm_wsa881x_dev_info),
+					GFP_KERNEL);
+	if (!wsa881x_dev_info) {
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	/*
+	 * search and check whether all WSA devices are already
+	 * registered with ALSA core or not. If found a node, store
+	 * the node and the index in a local array of struct for later
+	 * use.
+	 */
+	for (i = 0; i < wsa_dev_cnt; i++) {
+		wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+					    "qcom,wsa-devs", i);
+		if (unlikely(!wsa_of_node)) {
+			/* we should not be here */
+			dev_err(&pdev->dev,
+				"%s: wsa dev node is not present\n",
+				__func__);
+			ret = -EINVAL;
+			goto err_dev_node;
+		}
+		if (soc_find_component(wsa_of_node, NULL)) {
+			/* WSA device registered with ALSA core */
+			wsa881x_dev_info[found].of_node = wsa_of_node;
+			wsa881x_dev_info[found].index = i;
+			found++;
+			if (found == wsa_max_devs)
+				break;
+		}
+	}
+
+	if (found < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: failed to find %d components. Found only %d\n",
+			__func__, wsa_max_devs, found);
+		return -EPROBE_DEFER;
+	}
+	dev_info(&pdev->dev,
+		"%s: found %d wsa881x devices registered with ALSA core\n",
+		__func__, found);
+
+	card->num_aux_devs = wsa_max_devs;
+	card->num_configs = wsa_max_devs;
+
+	/* Alloc array of AUX devs struct */
+	msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+				       sizeof(struct snd_soc_aux_dev),
+				       GFP_KERNEL);
+	if (!msm_aux_dev) {
+		ret = -ENOMEM;
+		goto err_auxdev_mem;
+	}
+
+	/* Alloc array of codec conf struct */
+	msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+					  sizeof(struct snd_soc_codec_conf),
+					  GFP_KERNEL);
+	if (!msm_codec_conf) {
+		ret = -ENOMEM;
+		goto err_codec_conf;
+	}
+
+	for (i = 0; i < card->num_aux_devs; i++) {
+		dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+					    GFP_KERNEL);
+		if (!dev_name_str) {
+			ret = -ENOMEM;
+			goto err_dev_str;
+		}
+
+		ret = of_property_read_string_index(pdev->dev.of_node,
+						    "qcom,wsa-aux-dev-prefix",
+						    wsa881x_dev_info[i].index,
+						    wsa_auxdev_name_prefix);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: failed to read wsa aux dev prefix, ret = %d\n",
+				__func__, ret);
+			ret = -EINVAL;
+			goto err_dt_prop;
+		}
+
+		snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+		msm_aux_dev[i].name = dev_name_str;
+		msm_aux_dev[i].codec_name = NULL;
+		msm_aux_dev[i].codec_of_node =
+					wsa881x_dev_info[i].of_node;
+		msm_aux_dev[i].init = msm_wsa881x_init;
+		msm_codec_conf[i].dev_name = NULL;
+		msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+		msm_codec_conf[i].of_node =
+				wsa881x_dev_info[i].of_node;
+	}
+	card->codec_conf = msm_codec_conf;
+	card->aux_dev = msm_aux_dev;
+
+	return 0;
+
+err_dt_prop:
+	devm_kfree(&pdev->dev, dev_name_str);
+err_dev_str:
+	devm_kfree(&pdev->dev, msm_codec_conf);
+err_codec_conf:
+	devm_kfree(&pdev->dev, msm_aux_dev);
+err_auxdev_mem:
+err_dev_node:
+	devm_kfree(&pdev->dev, wsa881x_dev_info);
+err_mem:
+err_dt:
+	return ret;
+}
+
+static void i2s_auxpcm_init(struct platform_device *pdev)
+{
+	struct resource *muxsel;
+	int count;
+	u32 mi2s_master_slave[MI2S_MAX];
+	int ret;
+	char *str[PCM_I2S_SEL_MAX] = {
+		"lpaif_pri_mode_muxsel",
+		"lpaif_sec_mode_muxsel",
+		"lpaif_tert_mode_muxsel",
+		"lpaif_quat_mode_muxsel"
+	};
+
+	for (count = 0; count < MI2S_MAX; count++) {
+		mutex_init(&mi2s_intf_conf[count].lock);
+		mi2s_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < AUX_PCM_MAX; count++) {
+		mutex_init(&auxpcm_intf_conf[count].lock);
+		auxpcm_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		mutex_init(&mi2s_auxpcm_conf[count].lock);
+		mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
+	}
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						      str[count]);
+		if (muxsel) {
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
+				= ioremap(muxsel->start, resource_size(muxsel));
+		}
+	}
+
+	ret = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-mi2s-master",
+			mi2s_master_slave, MI2S_MAX);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n",
+			__func__);
+	} else {
+		for (count = 0; count < MI2S_MAX; count++) {
+			mi2s_intf_conf[count].msm_is_mi2s_master =
+				mi2s_master_slave[count];
+		}
+	}
+}
+
+static void i2s_auxpcm_deinit(void)
+{
+	int count;
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++)
+		if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
+			NULL)
+			iounmap(
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
+}
+
+static int msm_asoc_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card;
+	struct msm_asoc_mach_data *pdata;
+	const char *mbhc_audio_jack_type = NULL;
+	char *mclk_freq_prop_name;
+	const struct of_device_id *match;
+	int ret;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "No platform supplied from device tree\n");
+		return -EINVAL;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			sizeof(struct msm_asoc_mach_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	card = populate_snd_card_dailinks(&pdev->dev);
+	if (!card) {
+		dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, pdata);
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(&pdev->dev, "parse card name failed, err:%d\n",
+			ret);
+		goto err;
+	}
+
+	ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "parse audio routing failed, err:%d\n",
+			ret);
+		goto err;
+	}
+
+	match = of_match_node(msm8998_asoc_machine_of_match,
+			pdev->dev.of_node);
+	if (!match) {
+		dev_err(&pdev->dev, "%s: no matched codec is found.\n",
+			__func__);
+		goto err;
+	}
+
+	if (!strcmp(match->data, "tasha_codec"))
+		mclk_freq_prop_name = "qcom,tasha-mclk-clk-freq";
+	else
+		mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq";
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			mclk_freq_prop_name, &pdata->mclk_freq);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed, err%d\n",
+			mclk_freq_prop_name,
+			pdev->dev.of_node->full_name, ret);
+		goto err;
+	}
+
+	if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) {
+		dev_err(&pdev->dev, "unsupported mclk freq %u\n",
+			pdata->mclk_freq);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = msm_populate_dai_link_component_of_node(card);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+	ret = msm_init_wsa_dev(pdev, card);
+	if (ret)
+		goto err;
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret == -EPROBE_DEFER) {
+		if (codec_reg_done)
+			ret = -EINVAL;
+		goto err;
+	} else if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err;
+	}
+	dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+	spdev = pdev;
+
+	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n",
+			__func__, ret);
+	} else {
+		pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+							"qcom,hph-en1-gpio", 0);
+		if (!pdata->hph_en1_gpio_p) {
+			dev_dbg(&pdev->dev, "property %s not detected in node %s",
+				"qcom,hph-en1-gpio",
+				pdev->dev.of_node->full_name);
+		}
+
+		pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+							"qcom,hph-en0-gpio", 0);
+		if (!pdata->hph_en0_gpio_p) {
+			dev_dbg(&pdev->dev, "property %s not detected in node %s",
+				"qcom,hph-en0-gpio",
+				pdev->dev.of_node->full_name);
+		}
+	}
+
+	ret = of_property_read_string(pdev->dev.of_node,
+		"qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+			"qcom,mbhc-audio-jack-type",
+			pdev->dev.of_node->full_name);
+		dev_dbg(&pdev->dev, "Jack type properties set to default");
+	} else {
+		if (!strcmp(mbhc_audio_jack_type, "4-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+		else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+		else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+		else
+			dev_dbg(&pdev->dev, "Unknown value, set to default");
+	}
+	/*
+	 * Parse US-Euro gpio info from DT. Report no error if us-euro
+	 * entry is not found in DT file as some targets do not support
+	 * US-Euro detection
+	 */
+	pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"qcom,us-euro-gpios", 0);
+	if (!gpio_is_valid(pdata->us_euro_gpio))
+		pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node,
+					"qcom,us-euro-gpios", 0);
+	if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) {
+		dev_dbg(&pdev->dev, "property %s not detected in node %s",
+			"qcom,us-euro-gpios", pdev->dev.of_node->full_name);
+	} else {
+		dev_dbg(&pdev->dev, "%s detected",
+			"qcom,us-euro-gpios");
+		wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic;
+	}
+
+	ret = msm_prepare_us_euro(card);
+	if (ret)
+		dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
+			ret);
+
+	i2s_auxpcm_init(pdev);
+
+	is_initial_boot = true;
+	ret = audio_notifier_register("msm8998", AUDIO_NOTIFIER_ADSP_DOMAIN,
+				      &service_nb);
+	if (ret < 0)
+		pr_err("%s: Audio notifier register failed ret = %d\n",
+			__func__, ret);
+
+	return 0;
+err:
+	if (pdata->us_euro_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+			__func__, pdata->us_euro_gpio);
+		gpio_free(pdata->us_euro_gpio);
+		pdata->us_euro_gpio = 0;
+	}
+	devm_kfree(&pdev->dev, pdata);
+	return ret;
+}
+
+static int msm_asoc_machine_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	gpio_free(pdata->us_euro_gpio);
+	i2s_auxpcm_deinit();
+
+	snd_soc_unregister_card(card);
+	return 0;
+}
+
+static struct platform_driver msm8998_asoc_machine_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = msm8998_asoc_machine_of_match,
+	},
+	.probe = msm_asoc_machine_probe,
+	.remove = msm_asoc_machine_remove,
+};
+module_platform_driver(msm8998_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, msm8998_asoc_machine_of_match);
diff --git a/sound/soc/msm/msmfalcon-common.c b/sound/soc/msm/msmfalcon-common.c
new file mode 100644
index 0000000..fba9c28
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-common.c
@@ -0,0 +1,2806 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/input.h>
+#include <linux/of_gpio.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/pcm_params.h>
+#include <sound/q6afe-v2.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "msm-audio-pinctrl.h"
+#include "msmfalcon-common.h"
+#include "msmfalcon-internal.h"
+#include "msmfalcon-external.h"
+#include "../codecs/msm8x16/msm8x16-wcd.h"
+#include "../codecs/wsa881x.h"
+
+#define DRV_NAME "msmfalcon-asoc-snd"
+
+#define DEV_NAME_STR_LEN  32
+#define DEFAULT_MCLK_RATE 9600000
+
+struct dev_config {
+	u32 sample_rate;
+	u32 bit_format;
+	u32 channels;
+};
+
+/* TDM default config */
+static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+	{ /* PRI TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* SEC TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* TERT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* QUAT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	}
+};
+
+/* TDM default config */
+static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+	{ /* PRI TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* SEC TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* TERT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* QUAT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	}
+};
+
+static struct dev_config usb_rx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 2,
+};
+
+static struct dev_config usb_tx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 1,
+};
+
+enum {
+	PRIM_MI2S = 0,
+	SEC_MI2S,
+	TERT_MI2S,
+	QUAT_MI2S,
+	MI2S_MAX,
+};
+
+enum {
+	PRIM_AUX_PCM = 0,
+	SEC_AUX_PCM,
+	TERT_AUX_PCM,
+	QUAT_AUX_PCM,
+	AUX_PCM_MAX,
+};
+
+enum {
+	PCM_I2S_SEL_PRIM = 0,
+	PCM_I2S_SEL_SEC,
+	PCM_I2S_SEL_TERT,
+	PCM_I2S_SEL_QUAT,
+	PCM_I2S_SEL_MAX,
+};
+
+struct mi2s_aux_pcm_common_conf {
+	struct mutex lock;
+	void *pcm_i2s_sel_vt_addr;
+};
+
+struct mi2s_conf {
+	struct mutex lock;
+	u32 ref_cnt;
+	u32 msm_is_mi2s_master;
+};
+
+struct auxpcm_conf {
+	struct mutex lock;
+	u32 ref_cnt;
+};
+
+struct msm_wsa881x_dev_info {
+	struct device_node *of_node;
+	u32 index;
+};
+static struct snd_soc_aux_dev *msm_aux_dev;
+static struct snd_soc_codec_conf *msm_codec_conf;
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec);
+
+static struct wcd_mbhc_config mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.detect_extn_cable = true,
+	.mono_stero_detection = false,
+	.swap_gnd_mic = NULL,
+	.hs_ext_micbias = false,
+	.key_code[0] = KEY_MEDIA,
+	.key_code[1] = KEY_VOICECOMMAND,
+	.key_code[2] = KEY_VOLUMEUP,
+	.key_code[3] = KEY_VOLUMEDOWN,
+	.key_code[4] = 0,
+	.key_code[5] = 0,
+	.key_code[6] = 0,
+	.key_code[7] = 0,
+	.linein_th = 5000,
+	.moisture_en = false,
+	.mbhc_micbias = 0,
+	.anc_micbias = 0,
+	.enable_anc_mic_detect = false,
+};
+
+static struct dev_config proxy_rx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 2,
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config mi2s_rx_cfg[] = {
+	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config mi2s_tx_cfg[] = {
+	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_rx_cfg[] = {
+	[PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_AUX_PCM]  = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_tx_cfg[] = {
+	[PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_AUX_PCM]  = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static char const *ch_text[] = {"Two", "Three", "Four", "Five",
+					"Six", "Seven", "Eight"};
+static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
+static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+				      "KHZ_32", "KHZ_44P1", "KHZ_48",
+				      "KHZ_96", "KHZ_192"};
+static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
+					   "Five", "Six", "Seven",
+					   "Eight"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+					  "S32_LE"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+				    "Five", "Six", "Seven", "Eight"};
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"};
+static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
+					     "KHZ_44P1", "KHZ_48", "KHZ_96",
+					     "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+					   "Five", "Six", "Seven",
+					   "Eight"};
+static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
+					"KHZ_16", "KHZ_22P05",
+					"KHZ_32", "KHZ_44P1", "KHZ_48",
+					"KHZ_96", "KHZ_192", "KHZ_384"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text);
+
+static struct afe_clk_set mi2s_clk[MI2S_MAX] = {
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	}
+};
+
+static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
+static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
+static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
+
+static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: proxy_rx channels = %d\n",
+		 __func__, proxy_rx_cfg.channels);
+	ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2;
+
+	return 0;
+}
+
+static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
+	pr_debug("%s: proxy_rx channels = %d\n",
+		 __func__, proxy_rx_cfg.channels);
+
+	return 1;
+}
+
+static int tdm_get_sample_rate(int value)
+{
+	int sample_rate = 0;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 7:
+		sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 8:
+		sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int tdm_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val = 0;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 8;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int tdm_get_port_idx(struct snd_kcontrol *kcontrol,
+			    struct tdm_port *port)
+{
+	if (port) {
+		if (strnstr(kcontrol->id.name, "PRI",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_PRI;
+		} else if (strnstr(kcontrol->id.name, "SEC",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_SEC;
+		} else if (strnstr(kcontrol->id.name, "TERT",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_TERT;
+		} else if (strnstr(kcontrol->id.name, "QUAT",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_QUAT;
+		} else {
+			pr_err("%s: unsupported mode in: %s",
+				__func__, kcontrol->id.name);
+			return -EINVAL;
+		}
+
+		if (strnstr(kcontrol->id.name, "RX_0",
+		    sizeof(kcontrol->id.name)) ||
+		    strnstr(kcontrol->id.name, "TX_0",
+		    sizeof(kcontrol->id.name))) {
+			port->channel = TDM_0;
+		} else if (strnstr(kcontrol->id.name, "RX_1",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_1",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_1;
+		} else if (strnstr(kcontrol->id.name, "RX_2",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_2",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_2;
+		} else if (strnstr(kcontrol->id.name, "RX_3",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_3",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_3;
+		} else if (strnstr(kcontrol->id.name, "RX_4",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_4",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_4;
+		} else if (strnstr(kcontrol->id.name, "RX_5",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_5",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_5;
+		} else if (strnstr(kcontrol->id.name, "RX_6",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_6",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_6;
+		} else if (strnstr(kcontrol->id.name, "RX_7",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_7",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_7;
+		} else {
+			pr_err("%s: unsupported channel in: %s",
+				__func__, kcontrol->id.name);
+			return -EINVAL;
+		}
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+			tdm_rx_cfg[port.mode][port.channel].sample_rate);
+
+		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].sample_rate =
+			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+			tdm_tx_cfg[port.mode][port.channel].sample_rate);
+
+		pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].sample_rate =
+			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_get_format(int value)
+{
+	int format = 0;
+
+	switch (value) {
+	case 0:
+		format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	default:
+		format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return format;
+}
+
+static int tdm_get_format_val(int format)
+{
+	int value = 0;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		value = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		value = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		value = 2;
+		break;
+	default:
+		value = 0;
+		break;
+	}
+	return value;
+}
+
+static int tdm_rx_format_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+				tdm_rx_cfg[port.mode][port.channel].bit_format);
+
+		pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_format_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].bit_format =
+			tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_format_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+				tdm_tx_cfg[port.mode][port.channel].bit_format);
+
+		pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_format_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].bit_format =
+			tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+
+		ucontrol->value.enumerated.item[0] =
+			tdm_rx_cfg[port.mode][port.channel].channels - 1;
+
+		pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].channels - 1,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].channels =
+			ucontrol->value.enumerated.item[0] + 1;
+
+		pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].channels,
+			 ucontrol->value.enumerated.item[0] + 1);
+	}
+	return ret;
+}
+
+static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] =
+			tdm_tx_cfg[port.mode][port.channel].channels - 1;
+
+		pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].channels - 1,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].channels =
+			ucontrol->value.enumerated.item[0] + 1;
+
+		pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].channels,
+			 ucontrol->value.enumerated.item[0] + 1);
+	}
+	return ret;
+}
+
+static int aux_pcm_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 0:
+	default:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int aux_pcm_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM",
+		    sizeof("PRIM_AUX_PCM")))
+		idx = PRIM_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM",
+			 sizeof("SEC_AUX_PCM")))
+		idx = SEC_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM",
+			 sizeof("TERT_AUX_PCM")))
+		idx = TERT_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM",
+			 sizeof("QUAT_AUX_PCM")))
+		idx = QUAT_AUX_PCM;
+	else {
+		pr_err("%s: unsupported port: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	aux_pcm_rx_cfg[idx].sample_rate =
+		aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+	     aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	aux_pcm_tx_cfg[idx].sample_rate =
+		aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+	     aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX",
+	    sizeof("PRIM_MI2S_RX")))
+		idx = PRIM_MI2S;
+	else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX",
+		 sizeof("SEC_MI2S_RX")))
+		idx = SEC_MI2S;
+	else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX",
+		 sizeof("TERT_MI2S_RX")))
+		idx = TERT_MI2S;
+	else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX",
+		 sizeof("QUAT_MI2S_RX")))
+		idx = QUAT_MI2S;
+	else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX",
+		 sizeof("PRIM_MI2S_TX")))
+		idx = PRIM_MI2S;
+	else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX",
+		 sizeof("SEC_MI2S_TX")))
+		idx = SEC_MI2S;
+	else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX",
+		 sizeof("TERT_MI2S_TX")))
+		idx = TERT_MI2S;
+	else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX",
+		 sizeof("QUAT_MI2S_TX")))
+		idx = QUAT_MI2S;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int mi2s_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int mi2s_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_rx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_tx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: msm_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: msm_mi2s_[%d]_tx_ch  = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_mi2s_[%d]_tx_ch  = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: usb_audio_rx_ch  = %d\n", __func__,
+		 usb_rx_cfg.channels);
+	ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1;
+	return 0;
+}
+
+static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels);
+	return 1;
+}
+
+static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+
+	switch (usb_rx_cfg.sample_rate) {
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_22P05KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_11P025KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__,
+		 usb_rx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 9:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	case 8:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 7:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+		break;
+	case 2:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 1:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+		break;
+	case 0:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	default:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n",
+		__func__, ucontrol->value.integer.value[0],
+		usb_rx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (usb_rx_cfg.bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ucontrol->value.integer.value[0] = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_rx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	case 2:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_rx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return rc;
+}
+
+static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: usb_audio_tx_ch  = %d\n", __func__,
+		 usb_tx_cfg.channels);
+	ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1;
+	return 0;
+}
+
+static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels);
+	return 1;
+}
+
+static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+
+	switch (usb_tx_cfg.sample_rate) {
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_22P05KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_11P025KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	default:
+		sample_rate_val = 6;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__,
+		 usb_tx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 9:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	case 8:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 7:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+		break;
+	case 2:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 1:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+		break;
+	case 0:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	default:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n",
+		__func__, ucontrol->value.integer.value[0],
+		usb_tx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (usb_tx_cfg.bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ucontrol->value.integer.value[0] = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_tx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	case 2:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_tx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return rc;
+}
+
+const struct snd_kcontrol_new msm_common_snd_controls[] = {
+	SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs,
+			proxy_rx_ch_get, proxy_rx_ch_put),
+	SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs,
+			usb_audio_rx_ch_get, usb_audio_rx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
+			usb_audio_tx_ch_get, usb_audio_tx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format,
+			usb_audio_rx_format_get, usb_audio_rx_format_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
+			usb_audio_tx_format_get, usb_audio_tx_format_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
+			usb_audio_rx_sample_rate_get,
+			usb_audio_rx_sample_rate_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
+			usb_audio_tx_sample_rate_get,
+			usb_audio_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+};
+
+static inline int param_is_mask(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+			(p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+					     int n)
+{
+	return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+	if (bit >= SNDRV_MASK_MAX)
+		return;
+	if (param_is_mask(n)) {
+		struct snd_mask *m = param_to_mask(p, n);
+
+		m->bits[0] = 0;
+		m->bits[1] = 0;
+		m->bits[bit >> 5] |= (1 << (bit & 31));
+	}
+}
+
+/**
+ * msm_common_be_hw_params_fixup - updates settings of ALSA BE hw params.
+ *
+ * @rtd: runtime dailink instance
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0.
+ */
+int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	int rc = 0;
+
+	pr_debug("%s: format = %d, rate = %d\n",
+		  __func__, params_format(params), params_rate(params));
+
+	switch (dai_link->be_id) {
+	case MSM_BACKEND_DAI_USB_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				usb_rx_cfg.bit_format);
+		rate->min = rate->max = usb_rx_cfg.sample_rate;
+		channels->min = channels->max = usb_rx_cfg.channels;
+		break;
+
+	case MSM_BACKEND_DAI_USB_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				usb_tx_cfg.bit_format);
+		rate->min = rate->max = usb_tx_cfg.sample_rate;
+		channels->min = channels->max = usb_tx_cfg.channels;
+		break;
+
+	case MSM_BACKEND_DAI_AFE_PCM_RX:
+		channels->min = channels->max = proxy_rx_cfg.channels;
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_PRI][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_rx_cfg[TDM_PRI][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_PRI][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_tx_cfg[TDM_PRI][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_SEC][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_tx_cfg[TDM_SEC][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_QUAT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				   tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[PRIM_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[PRIM_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[SEC_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[SEC_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[TERT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[TERT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[QUAT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[QUAT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[PRIM_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[PRIM_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[SEC_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[SEC_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[TERT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[TERT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[QUAT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[QUAT_MI2S].channels;
+		break;
+
+	default:
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return rc;
+}
+EXPORT_SYMBOL(msm_common_be_hw_params_fixup);
+
+/**
+ * msm_aux_pcm_snd_startup - startup ops of auxpcm.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id - 1;
+	return ret = 0;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto done;
+	}
+
+	mutex_lock(&auxpcm_intf_conf[index].lock);
+	if (++auxpcm_intf_conf[index].ref_cnt == 1) {
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(1,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
+				__func__);
+			ret = -EINVAL;
+		}
+	}
+	if (ret < 0)
+		auxpcm_intf_conf[index].ref_cnt--;
+
+	mutex_unlock(&auxpcm_intf_conf[index].lock);
+
+done:
+	return ret;
+}
+EXPORT_SYMBOL(msm_aux_pcm_snd_startup);
+
+/**
+ * msm_aux_pcm_snd_shutdown - shutdown ops of auxpcm.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ */
+void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id - 1;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__,
+		substream->name, substream->stream,
+		rtd->cpu_dai->name, rtd->cpu_dai->id);
+
+	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, rtd->cpu_dai->id);
+		return;
+	}
+
+	mutex_lock(&auxpcm_intf_conf[index].lock);
+	if (--auxpcm_intf_conf[index].ref_cnt == 0) {
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(0,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
+				__func__);
+			auxpcm_intf_conf[index].ref_cnt++;
+		}
+	}
+	mutex_unlock(&auxpcm_intf_conf[index].lock);
+}
+EXPORT_SYMBOL(msm_aux_pcm_snd_shutdown);
+
+static int msm_get_port_id(int be_id)
+{
+	int afe_port_id;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_PRI_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_PRI_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+		break;
+	default:
+		pr_err("%s: Invalid be_id: %d\n", __func__, be_id);
+		afe_port_id = -EINVAL;
+	}
+
+	return afe_port_id;
+}
+
+static u32 get_mi2s_bits_per_sample(u32 bit_format)
+{
+	u32 bit_per_sample;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bit_per_sample = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bit_per_sample = 16;
+		break;
+	}
+
+	return bit_per_sample;
+}
+
+static void update_mi2s_clk_val(int dai_id, int stream)
+{
+	u32 bit_per_sample;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		bit_per_sample =
+		    get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format);
+		mi2s_clk[dai_id].clk_freq_in_hz =
+		    mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+	} else {
+		bit_per_sample =
+		    get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format);
+		mi2s_clk[dai_id].clk_freq_in_hz =
+		    mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+	}
+
+	if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master)
+		mi2s_clk[dai_id].clk_freq_in_hz = 0;
+}
+
+static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int port_id = 0;
+	int index = cpu_dai->id;
+
+	port_id = msm_get_port_id(rtd->dai_link->be_id);
+	if (port_id < 0) {
+		dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+		ret = port_id;
+		goto done;
+	}
+
+	if (enable) {
+		update_mi2s_clk_val(index, substream->stream);
+		dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+			mi2s_clk[index].clk_freq_in_hz);
+	}
+
+	mi2s_clk[index].enable = enable;
+	ret = afe_set_lpass_clock_v2(port_id,
+				     &mi2s_clk[index]);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+			"%s: afe lpass clock failed for port 0x%x , err:%d\n",
+			__func__, port_id, ret);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+/**
+ * msm_mi2s_snd_startup - startup ops of mi2s.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ *
+ * Returns 0 on success or -EINVAL on error.
+ */
+int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id;
+	unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_MI2S || index > QUAT_MI2S) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto done;
+	}
+	/*
+	 * Muxtex protection in case the same MI2S
+	 * interface using for both TX and RX  so
+	 * that the same clock won't be enable twice.
+	 */
+	mutex_lock(&mi2s_intf_conf[index].lock);
+	if (++mi2s_intf_conf[index].ref_cnt == 1) {
+		ret = msm_mi2s_set_sclk(substream, true);
+		if (ret < 0) {
+			dev_err(rtd->card->dev,
+				"%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+				__func__, ret);
+			goto clean_up;
+		}
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(0,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
+				__func__, index);
+			ret = -EINVAL;
+			goto clk_off;
+		}
+		/* Check if msm needs to provide the clock to the interface */
+		if (!mi2s_intf_conf[index].msm_is_mi2s_master)
+			fmt = SND_SOC_DAIFMT_CBM_CFM;
+		ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+		if (ret < 0) {
+			dev_err(rtd->card->dev,
+				"%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
+				__func__, index, ret);
+			goto clk_off;
+		}
+	}
+clk_off:
+	if (ret < 0)
+		msm_mi2s_set_sclk(substream, false);
+clean_up:
+	if (ret < 0)
+		mi2s_intf_conf[index].ref_cnt--;
+	mutex_unlock(&mi2s_intf_conf[index].lock);
+done:
+	return ret;
+}
+EXPORT_SYMBOL(msm_mi2s_snd_startup);
+
+/**
+ * msm_mi2s_snd_shutdown - shutdown ops of mi2s.
+ *
+ * @substream: PCM stream pointer of associated backend dailink
+ */
+void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id;
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+	if (index < PRIM_MI2S || index > QUAT_MI2S) {
+		pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+		return;
+	}
+
+	mutex_lock(&mi2s_intf_conf[index].lock);
+	if (--mi2s_intf_conf[index].ref_cnt == 0) {
+		ret = msm_mi2s_set_sclk(substream, false);
+		if (ret < 0) {
+			pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n",
+				__func__, index, ret);
+			mi2s_intf_conf[index].ref_cnt++;
+		}
+	}
+	mutex_unlock(&mi2s_intf_conf[index].lock);
+}
+EXPORT_SYMBOL(msm_mi2s_snd_shutdown);
+
+/* Validate whether US EU switch is present or not */
+static int msm_prepare_us_euro(struct snd_soc_card *card)
+{
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	if (pdata->us_euro_gpio >= 0) {
+		dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+			pdata->us_euro_gpio);
+		ret = gpio_request(pdata->us_euro_gpio, "TASHA_CODEC_US_EURO");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: Failed to request codec US/EURO gpio %d error %d\n",
+				__func__, pdata->us_euro_gpio, ret);
+		}
+	}
+
+	return ret;
+}
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int value = 0;
+
+	if (pdata->us_euro_gpio_p) {
+		value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p);
+		if (value)
+			msm_cdc_pinctrl_select_sleep_state(
+							pdata->us_euro_gpio_p);
+		else
+			msm_cdc_pinctrl_select_active_state(
+							pdata->us_euro_gpio_p);
+	} else if (pdata->us_euro_gpio >= 0) {
+		value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+		gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+	}
+	pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+	return true;
+}
+
+static int msm_populate_dai_link_component_of_node(
+		struct snd_soc_card *card)
+{
+	int i, index, ret = 0;
+	struct device *cdev = card->dev;
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+	struct device_node *phandle;
+
+	if (!cdev) {
+		pr_err("%s: Sound card device memory NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < card->num_links; i++) {
+		if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+			continue;
+
+		/* populate platform_of_node for snd card dai links */
+		if (dai_link[i].platform_name &&
+				!dai_link[i].platform_of_node) {
+			index = of_property_match_string(cdev->of_node,
+					"asoc-platform-names",
+					dai_link[i].platform_name);
+			if (index < 0) {
+				pr_err("%s: No match found for platform name: %s\n",
+					__func__, dai_link[i].platform_name);
+				ret = index;
+				goto cpu_dai;
+			}
+			phandle = of_parse_phandle(cdev->of_node,
+					"asoc-platform",
+					index);
+			if (!phandle) {
+				pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+					__func__, dai_link[i].platform_name,
+						index);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].platform_of_node = phandle;
+			dai_link[i].platform_name = NULL;
+		}
+cpu_dai:
+		/* populate cpu_of_node for snd card dai links */
+		if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+			index = of_property_match_string(cdev->of_node,
+					"asoc-cpu-names",
+					dai_link[i].cpu_dai_name);
+			if (index < 0)
+				goto codec_dai;
+			phandle = of_parse_phandle(cdev->of_node, "asoc-cpu",
+					index);
+			if (!phandle) {
+				pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+					__func__, dai_link[i].cpu_dai_name);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].cpu_of_node = phandle;
+			dai_link[i].cpu_dai_name = NULL;
+		}
+codec_dai:
+		/* populate codec_of_node for snd card dai links */
+		if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+			index = of_property_match_string(cdev->of_node,
+					"asoc-codec-names",
+					dai_link[i].codec_name);
+			if (index < 0)
+				continue;
+			phandle = of_parse_phandle(cdev->of_node, "asoc-codec",
+					index);
+			if (!phandle) {
+				pr_err("%s: retrieving phandle for codec dai %s failed\n",
+					__func__, dai_link[i].codec_name);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].codec_of_node = phandle;
+			dai_link[i].codec_name = NULL;
+		}
+	}
+err:
+	return ret;
+}
+
+static int msm_wsa881x_init(struct snd_soc_component *component)
+{
+	u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+	u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+	unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+	unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+	struct msm_asoc_mach_data *pdata;
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_codec_get_dapm(codec);
+
+	if (!codec) {
+		pr_err("%s codec is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (!strcmp(component->name_prefix, "SpkrLeft")) {
+		dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+				__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkleft_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+		}
+	} else if (!strcmp(component->name_prefix, "SpkrRight")) {
+		dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+				__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkright_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+		}
+	} else {
+		dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+				codec->component.name);
+		return -EINVAL;
+	}
+
+
+	pdata = snd_soc_card_get_drvdata(component->card);
+	if (pdata && pdata->codec_root)
+		wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+						      codec);
+	return 0;
+}
+
+
+static int msm_init_wsa_dev(struct platform_device *pdev,
+			    struct snd_soc_card *card)
+{
+	struct device_node *wsa_of_node;
+	u32 wsa_max_devs;
+	u32 wsa_dev_cnt;
+	char *dev_name_str = NULL;
+	struct msm_wsa881x_dev_info *wsa881x_dev_info;
+	const char *wsa_auxdev_name_prefix[1];
+	int found = 0;
+	int i;
+	int ret;
+
+	/* Get maximum WSA device count for this platform */
+	ret = of_property_read_u32(pdev->dev.of_node,
+					"qcom,wsa-max-devs", &wsa_max_devs);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			"%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+			__func__, pdev->dev.of_node->full_name, ret);
+		goto err_dt;
+	}
+	if (wsa_max_devs == 0) {
+		dev_warn(&pdev->dev,
+			"%s: Max WSA devices is 0 for this target?\n",
+			__func__);
+		goto err_dt;
+				}
+
+	/* Get count of WSA device phandles for this platform */
+	wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+						"qcom,wsa-devs", NULL);
+	if (wsa_dev_cnt == -ENOENT) {
+		dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+			__func__);
+		goto err_dt;
+	} else if (wsa_dev_cnt <= 0) {
+		dev_err(&pdev->dev,
+			"%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+			__func__, wsa_dev_cnt);
+		ret = -EINVAL;
+		goto err_dt;
+	}
+
+	/*
+	 * Expect total phandles count to be NOT less than maximum possible
+	 * WSA count. However, if it is less, then assign same value to
+	 * max count as well.
+	 */
+	if (wsa_dev_cnt < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+			__func__, wsa_max_devs, wsa_dev_cnt);
+		wsa_max_devs = wsa_dev_cnt;
+	}
+
+	/* Make sure prefix string passed for each WSA device */
+	ret = of_property_count_strings(pdev->dev.of_node,
+			"qcom,wsa-aux-dev-prefix");
+	if (ret != wsa_dev_cnt) {
+		dev_err(&pdev->dev,
+			"%s: expecting %d wsa prefix. Defined only %d in DT\n",
+			__func__, wsa_dev_cnt, ret);
+		ret = -EINVAL;
+		goto err_dt;
+	}
+
+	/*
+	 * Alloc mem to store phandle and index info of WSA device, if already
+	 * registered with ALSA core
+	 */
+	wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+					sizeof(struct msm_wsa881x_dev_info),
+					GFP_KERNEL);
+	if (!wsa881x_dev_info) {
+		ret = -ENOMEM;
+		goto err_mem;
+	}
+
+	/*
+	 * search and check whether all WSA devices are already
+	 * registered with ALSA core or not. If found a node, store
+	 * the node and the index in a local array of struct for later
+	 * use.
+	 */
+	for (i = 0; i < wsa_dev_cnt; i++) {
+		wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+					       "qcom,wsa-devs", i);
+		if (unlikely(!wsa_of_node)) {
+			/* we should not be here */
+			dev_err(&pdev->dev,
+				"%s: wsa dev node is not present\n",
+				__func__);
+			ret = -EINVAL;
+			goto err_dev_node;
+		}
+		if (soc_find_component(wsa_of_node, NULL)) {
+			/* WSA device registered with ALSA core */
+			wsa881x_dev_info[found].of_node = wsa_of_node;
+			wsa881x_dev_info[found].index = i;
+			found++;
+			if (found == wsa_max_devs)
+				break;
+		}
+	}
+
+	if (found < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: failed to find %d components. Found only %d\n",
+			__func__, wsa_max_devs, found);
+		return -EPROBE_DEFER;
+	}
+	dev_info(&pdev->dev,
+		"%s: found %d wsa881x devices registered with ALSA core\n",
+		__func__, found);
+
+	card->num_aux_devs = wsa_max_devs;
+	card->num_configs = wsa_max_devs;
+
+	/* Alloc array of AUX devs struct */
+	msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+				   sizeof(struct snd_soc_aux_dev),
+				   GFP_KERNEL);
+	if (!msm_aux_dev) {
+		ret = -ENOMEM;
+		goto err_auxdev_mem;
+	}
+
+	/* Alloc array of codec conf struct */
+	msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+				      sizeof(struct snd_soc_codec_conf),
+				      GFP_KERNEL);
+	if (!msm_codec_conf) {
+		ret = -ENOMEM;
+		goto err_codec_conf;
+	}
+
+	for (i = 0; i < card->num_aux_devs; i++) {
+		dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+					    GFP_KERNEL);
+		if (!dev_name_str) {
+			ret = -ENOMEM;
+			goto err_dev_str;
+		}
+
+		ret = of_property_read_string_index(pdev->dev.of_node,
+						    "qcom,wsa-aux-dev-prefix",
+						    wsa881x_dev_info[i].index,
+						    wsa_auxdev_name_prefix);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: failed to read wsa aux dev prefix, ret = %d\n",
+				__func__, ret);
+			ret = -EINVAL;
+			goto err_dt_prop;
+		}
+
+		snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+		msm_aux_dev[i].name = dev_name_str;
+		msm_aux_dev[i].codec_name = NULL;
+		msm_aux_dev[i].codec_of_node =
+			wsa881x_dev_info[i].of_node;
+		msm_aux_dev[i].init = msm_wsa881x_init;
+		msm_codec_conf[i].dev_name = NULL;
+		msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+		msm_codec_conf[i].of_node = wsa881x_dev_info[i].of_node;
+	}
+	card->codec_conf = msm_codec_conf;
+	card->aux_dev = msm_aux_dev;
+
+	return 0;
+
+err_dt_prop:
+	devm_kfree(&pdev->dev, dev_name_str);
+err_dev_str:
+	devm_kfree(&pdev->dev, msm_codec_conf);
+err_codec_conf:
+	devm_kfree(&pdev->dev, msm_aux_dev);
+err_auxdev_mem:
+err_dev_node:
+	devm_kfree(&pdev->dev, wsa881x_dev_info);
+err_mem:
+err_dt:
+	return ret;
+}
+
+static void msm_free_auxdev_mem(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	int i;
+
+	if (card->num_aux_devs > 0) {
+		for (i = 0; i < card->num_aux_devs; i++) {
+			kfree(msm_aux_dev[i].codec_name);
+			kfree(msm_codec_conf[i].dev_name);
+			kfree(msm_codec_conf[i].name_prefix);
+		}
+	}
+}
+
+static void i2s_auxpcm_init(struct platform_device *pdev)
+{
+	struct resource *muxsel;
+	int count;
+	u32 mi2s_master_slave[MI2S_MAX];
+	int ret;
+	char *str[PCM_I2S_SEL_MAX] = {
+		"lpaif_pri_mode_muxsel",
+		"lpaif_sec_mode_muxsel",
+		"lpaif_tert_mode_muxsel",
+		"lpaif_quat_mode_muxsel"
+	};
+
+	for (count = 0; count < MI2S_MAX; count++) {
+		mutex_init(&mi2s_intf_conf[count].lock);
+		mi2s_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < AUX_PCM_MAX; count++) {
+		mutex_init(&auxpcm_intf_conf[count].lock);
+		auxpcm_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		mutex_init(&mi2s_auxpcm_conf[count].lock);
+		mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
+	}
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						      str[count]);
+		if (muxsel) {
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
+				= ioremap(muxsel->start, resource_size(muxsel));
+		}
+	}
+
+	ret = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-mi2s-master",
+			mi2s_master_slave, MI2S_MAX);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n",
+			__func__);
+	} else {
+		for (count = 0; count < MI2S_MAX; count++) {
+			mi2s_intf_conf[count].msm_is_mi2s_master =
+				mi2s_master_slave[count];
+		}
+	}
+}
+
+static void i2s_auxpcm_deinit(void)
+{
+	int count;
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++)
+		if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
+			NULL)
+			iounmap(
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
+}
+
+static const struct of_device_id msmfalcon_asoc_machine_of_match[]  = {
+	{ .compatible = "qcom,msmfalcon-asoc-snd",
+	  .data = "internal_codec"},
+	{ .compatible = "qcom,msmfalcon-asoc-snd-tasha",
+	  .data = "tasha_codec"},
+	{ .compatible = "qcom,msmfalcon-asoc-snd-tavil",
+	  .data = "tavil_codec"},
+	{},
+};
+
+static int msm_asoc_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = NULL;
+	struct msm_asoc_mach_data *pdata = NULL;
+	const char *mclk = "qcom,msm-mclk-freq";
+	int ret = -EINVAL, id;
+	const struct of_device_id *match;
+
+	pdata = devm_kzalloc(&pdev->dev,
+			     sizeof(struct msm_asoc_mach_data),
+			     GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	match = of_match_node(msmfalcon_asoc_machine_of_match,
+			      pdev->dev.of_node);
+	if (!match)
+		goto err;
+
+	ret = of_property_read_u32(pdev->dev.of_node, mclk, &id);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, mclk);
+		id = DEFAULT_MCLK_RATE;
+	}
+	pdata->mclk_freq = id;
+
+	if (!strcmp(match->data, "tasha_codec") ||
+	    !strcmp(match->data, "tavil_codec")) {
+		if (!strcmp(match->data, "tasha_codec"))
+			pdata->snd_card_val = EXT_SND_CARD_TASHA;
+		else
+			pdata->snd_card_val = EXT_SND_CARD_TAVIL;
+		ret = msm_ext_cdc_init(pdev, pdata, &card, &mbhc_cfg);
+		if (ret)
+			goto err;
+	} else if (!strcmp(match->data, "internal_codec")) {
+		pdata->snd_card_val = INT_SND_CARD;
+		ret = msm_int_cdc_init(pdev, pdata, &card, &mbhc_cfg);
+		if (ret)
+			goto err;
+	} else {
+		dev_err(&pdev->dev,
+			"%s: Not a matching DT sound node\n", __func__);
+		goto err;
+	}
+	if (!card)
+		goto err;
+
+	if (pdata->snd_card_val == INT_SND_CARD) {
+		/*reading the gpio configurations from dtsi file*/
+		ret = msm_gpioset_initialize(CLIENT_WCD, &pdev->dev);
+		if (ret < 0) {
+			dev_err(&pdev->dev,
+				"%s: error reading dtsi files%d\n",
+				__func__, ret);
+			goto err;
+		}
+	}
+
+	/*
+	 * Parse US-Euro gpio info from DT. Report no error if us-euro
+	 * entry is not found in DT file as some targets do not support
+	 * US-Euro detection
+	 */
+	pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"qcom,us-euro-gpios", 0);
+	if (!gpio_is_valid(pdata->us_euro_gpio))
+		pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node,
+					"qcom,us-euro-gpios", 0);
+	if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) {
+		dev_dbg(&pdev->dev, "property %s not detected in node %s",
+			"qcom,us-euro-gpios", pdev->dev.of_node->full_name);
+	} else {
+		dev_dbg(&pdev->dev, "%s detected",
+			"qcom,us-euro-gpios");
+		mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic;
+	}
+
+	ret = msm_prepare_us_euro(card);
+	if (ret)
+		dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
+			ret);
+
+	i2s_auxpcm_init(pdev);
+
+	ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+	if (ret)
+		goto err;
+
+	ret = msm_populate_dai_link_component_of_node(card);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+	ret = msm_init_wsa_dev(pdev, card);
+	if (ret)
+		goto err;
+
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err;
+	}
+	if (pdata->snd_card_val != INT_SND_CARD)
+		msm_ext_register_audio_notifier();
+	return 0;
+err:
+	if (pdata->us_euro_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free us_euro gpio %d\n",
+			__func__, pdata->us_euro_gpio);
+		pdata->us_euro_gpio = 0;
+	}
+	if (pdata->hph_en1_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free hph_en1_gpio %d\n",
+			__func__, pdata->hph_en1_gpio);
+		gpio_free(pdata->hph_en1_gpio);
+		pdata->hph_en1_gpio = 0;
+	}
+	if (pdata->hph_en0_gpio > 0) {
+		dev_dbg(&pdev->dev, "%s free hph_en0_gpio %d\n",
+			__func__, pdata->hph_en0_gpio);
+		gpio_free(pdata->hph_en0_gpio);
+		pdata->hph_en0_gpio = 0;
+	}
+	devm_kfree(&pdev->dev, pdata);
+	return ret;
+}
+
+static int msm_asoc_machine_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+	if (pdata->snd_card_val == INT_SND_CARD)
+		mutex_destroy(&pdata->cdc_int_mclk0_mutex);
+	msm_free_auxdev_mem(pdev);
+
+	gpio_free(pdata->us_euro_gpio);
+	gpio_free(pdata->hph_en1_gpio);
+	gpio_free(pdata->hph_en0_gpio);
+	i2s_auxpcm_deinit();
+	snd_soc_unregister_card(card);
+	return 0;
+}
+
+static struct platform_driver msmfalcon_asoc_machine_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = msmfalcon_asoc_machine_of_match,
+	},
+	.probe = msm_asoc_machine_probe,
+	.remove = msm_asoc_machine_remove,
+};
+module_platform_driver(msmfalcon_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, msmfalcon_asoc_machine_of_match);
diff --git a/sound/soc/msm/msmfalcon-common.h b/sound/soc/msm/msmfalcon-common.h
new file mode 100644
index 0000000..5f6b859
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-common.h
@@ -0,0 +1,103 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSM_COMMON
+#define __MSM_COMMON
+
+#include <sound/soc.h>
+#include <sound/q6afe-v2.h>
+#include "../codecs/wcd-mbhc-v2.h"
+
+#define SAMPLING_RATE_8KHZ      8000
+#define SAMPLING_RATE_11P025KHZ 11025
+#define SAMPLING_RATE_16KHZ     16000
+#define SAMPLING_RATE_22P05KHZ  22050
+#define SAMPLING_RATE_32KHZ     32000
+#define SAMPLING_RATE_44P1KHZ   44100
+#define SAMPLING_RATE_48KHZ     48000
+#define SAMPLING_RATE_88P2KHZ   88200
+#define SAMPLING_RATE_96KHZ     96000
+#define SAMPLING_RATE_176P4KHZ  176400
+#define SAMPLING_RATE_192KHZ    192000
+#define SAMPLING_RATE_352P8KHZ  352800
+#define SAMPLING_RATE_384KHZ    384000
+
+#define TDM_CHANNEL_MAX 8
+#define TDM_SLOT_OFFSET_MAX 8
+
+enum {
+	TDM_0 = 0,
+	TDM_1,
+	TDM_2,
+	TDM_3,
+	TDM_4,
+	TDM_5,
+	TDM_6,
+	TDM_7,
+	TDM_PORT_MAX,
+};
+
+enum {
+	TDM_PRI = 0,
+	TDM_SEC,
+	TDM_TERT,
+	TDM_QUAT,
+	TDM_INTERFACE_MAX,
+};
+
+struct tdm_port {
+	u32 mode;
+	u32 channel;
+};
+
+extern const struct snd_kcontrol_new msm_common_snd_controls[];
+struct msmfalcon_codec {
+	void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+				   enum afe_config_type config_type);
+};
+
+enum {
+	INT_SND_CARD,
+	EXT_SND_CARD_TASHA,
+	EXT_SND_CARD_TAVIL,
+};
+
+struct msm_asoc_mach_data {
+	int us_euro_gpio; /* used by gpio driver API */
+	int hph_en1_gpio;
+	int hph_en0_gpio;
+	struct device_node *us_euro_gpio_p; /* used by pinctrl API */
+	struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
+	struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
+	struct snd_soc_codec *codec;
+	struct msmfalcon_codec msmfalcon_codec_fn;
+	struct snd_info_entry *codec_root;
+	int spk_ext_pa_gpio;
+	int mclk_freq;
+	int lb_mode;
+	int snd_card_val;
+	u8 micbias1_cap_mode;
+	u8 micbias2_cap_mode;
+	atomic_t int_mclk0_rsc_ref;
+	atomic_t int_mclk0_enabled;
+	struct mutex cdc_int_mclk0_mutex;
+	struct delayed_work disable_int_mclk0_work;
+	struct afe_clk_set digital_cdc_core_clk;
+};
+
+int msm_common_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_pcm_hw_params *params);
+int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream);
+void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream);
+int msm_mi2s_snd_startup(struct snd_pcm_substream *substream);
+void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream);
+#endif
diff --git a/sound/soc/msm/msmfalcon-ext-dai-links.c b/sound/soc/msm/msmfalcon-ext-dai-links.c
new file mode 100644
index 0000000..6f066c5
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-ext-dai-links.c
@@ -0,0 +1,1967 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/of.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd9335.h"
+#include "msmfalcon-common.h"
+#include "msmfalcon-external.h"
+
+#define DEV_NAME_STR_LEN            32
+#define __CHIPSET__ "MSMFALCON "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+
+static struct snd_soc_card snd_soc_card_msm_card_tavil;
+static struct snd_soc_card snd_soc_card_msm_card_tasha;
+
+static struct snd_soc_ops msm_ext_slimbus_be_ops = {
+	.hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm_ext_cpe_ops = {
+	.hw_params = msm_snd_cpe_hw_params,
+};
+
+static struct snd_soc_ops msm_ext_slimbus_2_be_ops = {
+	.hw_params = msm_ext_slimbus_2_hw_params,
+};
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+	.startup = msm_mi2s_snd_startup,
+	.shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+	.startup = msm_aux_pcm_snd_startup,
+	.shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+	unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+	unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX]  = {159, 160, 161};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+					   tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	int ret;
+
+	dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+	if (ret) {
+		dev_err(rtd->dev,
+			"%s: failed to get BTFM codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto exit;
+	}
+
+	dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n",
+		__func__, tx_ch_cnt, dai_link->be_id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+	if (ret)
+		dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+
+exit:
+	return ret;
+}
+
+static struct snd_soc_ops msm_wcn_ops = {
+	.hw_params = msm_wcn_hw_params,
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+	{0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+					    int slots)
+{
+	unsigned int slot_mask = 0;
+	int i, j;
+	unsigned int *slot_offset;
+
+	for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+		slot_offset = tdm_slot_offset[i];
+
+		for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+			if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+				slot_mask |=
+				(1 << ((slot_offset[j] * 8) / slot_width));
+			else
+				break;
+		}
+	}
+
+	return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+	int channels, slot_width, slots;
+	unsigned int slot_mask;
+	unsigned int *slot_offset;
+	int offset_channels = 0;
+	int i;
+
+	pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+	channels = params_channels(params);
+	switch (channels) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S32_LE:
+		case SNDRV_PCM_FORMAT_S24_LE:
+		case SNDRV_PCM_FORMAT_S16_LE:
+		/*
+		 * up to 8 channels HW config should
+		 * use 32 bit slot width for max support of
+		 * stream bit width. (slot_width > bit_width)
+		 */
+			slot_width = 32;
+			break;
+		default:
+			pr_err("%s: invalid param format 0x%x\n",
+				__func__, params_format(params));
+			return -EINVAL;
+		}
+		slots = 8;
+		slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+						    slot_width,
+						    slots);
+		if (!slot_mask) {
+			pr_err("%s: invalid slot_mask 0x%x\n",
+				__func__, slot_mask);
+			return -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s: invalid param channels %d\n",
+			__func__, channels);
+		return -EINVAL;
+	}
+	/* currently only supporting TDM_RX_0 and TDM_TX_0 */
+	switch (cpu_dai->id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		slot_offset = tdm_slot_offset[TDM_0];
+		break;
+	default:
+		pr_err("%s: dai id 0x%x not supported\n",
+			__func__, cpu_dai->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+		if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+			offset_channels++;
+		else
+			break;
+	}
+
+	if (offset_channels == 0) {
+		pr_err("%s: slot offset not supported, offset_channels %d\n",
+			__func__, offset_channels);
+		return -EINVAL;
+	}
+
+	if (channels > offset_channels) {
+		pr_err("%s: channels %d exceed offset_channels %d\n",
+			__func__, channels, offset_channels);
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+						  channels, slot_offset);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	} else {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+						  slot_offset, 0, NULL);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	}
+end:
+	return ret;
+}
+
+static struct snd_soc_ops msm_tdm_be_ops = {
+	.hw_params = msm_tdm_snd_hw_params
+};
+
+static struct snd_soc_dai_link msm_ext_tasha_fe_dai[] = {
+	/* tasha_vifeedback for speaker protection */
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_vifeedback",
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+	},
+	/* Ultrasound RX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Playback",
+		.stream_name = "SLIMBUS_2 Hostless Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx2",
+		.ignore_suspend = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_ext_slimbus_2_be_ops,
+	},
+	/* Ultrasound TX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Capture",
+		.stream_name = "SLIMBUS_2 Hostless Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16389",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx2",
+		.ignore_suspend = 1,
+		.dpcm_capture = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_ext_slimbus_2_be_ops,
+	},
+	/* CPE LSM direct dai-link */
+	{
+		.name = "CPE Listen service",
+		.stream_name = "CPE Listen Audio Service",
+		.cpu_dai_name = "msm-dai-slim",
+		.platform_name = "msm-cpe-lsm",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.dpcm_capture = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "tasha_mad1",
+		.codec_name = "tasha_codec",
+		.ops = &msm_ext_cpe_ops,
+	},
+	{
+		.name = "SLIMBUS_6 Hostless Playback",
+		.stream_name = "SLIMBUS_6 Hostless",
+		.cpu_dai_name = "SLIMBUS6_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* CPE LSM EC PP direct dai-link */
+	{
+		.name = "CPE Listen service ECPP",
+		.stream_name = "CPE Listen Audio Service ECPP",
+		.cpu_dai_name = "CPE_LSM_NOHOST",
+		.platform_name = "msm-cpe-lsm.3",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "tasha_cpe",
+		.codec_name = "tasha_codec",
+	},
+};
+
+static struct snd_soc_dai_link msm_ext_tavil_fe_dai[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_vifeedback",
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+	},
+	/* Ultrasound RX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Playback",
+		.stream_name = "SLIMBUS_2 Hostless Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_ext_slimbus_2_be_ops,
+	},
+	/* Ultrasound TX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Capture",
+		.stream_name = "SLIMBUS_2 Hostless Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16389",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_ext_slimbus_2_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_ext_tasha_be_dai[] = {
+	/* Backend DAI Links */
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_ext_slimbus_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_ext_slimbus_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx3",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mix_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_5_RX,
+		.stream_name = "Slimbus5 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16394",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx3",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* MAD BE */
+	{
+		.name = LPASS_BE_SLIMBUS_5_TX,
+		.stream_name = "Slimbus5 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16395",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_mad1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_6_RX,
+		.stream_name = "Slimbus6 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16396",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tasha_codec",
+		.codec_dai_name = "tasha_rx4",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_ext_tavil_be_dai[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_ext_slimbus_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_ext_slimbus_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx3",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_2_RX,
+		.stream_name = "Slimbus2 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx2",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_2_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_5_RX,
+		.stream_name = "Slimbus5 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16394",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx3",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* MAD BE */
+	{
+		.name = LPASS_BE_SLIMBUS_5_TX,
+		.stream_name = "Slimbus5 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16395",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_mad1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_6_RX,
+		.stream_name = "Slimbus6 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16396",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx4",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_ext_slimbus_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_ext_common_fe_dai[] = {
+	/* FrontEnd DAI Links */
+	{/* hw:x,0 */
+		.name = MSM_DAILINK_NAME(Media1),
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name	= "MultiMedia1",
+		.platform_name  = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+	{/* hw:x,1 */
+		.name = MSM_DAILINK_NAME(Media2),
+		.stream_name = "MultiMedia2",
+		.cpu_dai_name   = "MultiMedia2",
+		.platform_name  = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+	},
+	{/* hw:x,2 */
+		.name = "VoiceMMode1",
+		.stream_name = "VoiceMMode1",
+		.cpu_dai_name = "VoiceMMode1",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+	},
+	{/* hw:x,3 */
+		.name = "MSM VoIP",
+		.stream_name = "VoIP",
+		.cpu_dai_name	= "VoIP",
+		.platform_name  = "msm-voip-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_VOIP,
+	},
+	{/* hw:x,4 */
+		.name = MSM_DAILINK_NAME(ULL),
+		.stream_name = "ULL",
+		.cpu_dai_name	= "MultiMedia3",
+		.platform_name  = "msm-pcm-dsp.2",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+	},
+	/* Hostless PCM purpose */
+	{/* hw:x,5 */
+		.name = "SLIMBUS_0 Hostless",
+		.stream_name = "SLIMBUS_0 Hostless",
+		.cpu_dai_name = "SLIMBUS0_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* This dai link has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,6 */
+		.name = "MSM AFE-PCM RX",
+		.stream_name = "AFE-PROXY RX",
+		.cpu_dai_name = "msm-dai-q6-dev.241",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.platform_name  = "msm-pcm-afe",
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{/* hw:x,7 */
+		.name = "MSM AFE-PCM TX",
+		.stream_name = "AFE-PROXY TX",
+		.cpu_dai_name = "msm-dai-q6-dev.240",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.platform_name  = "msm-pcm-afe",
+		.ignore_suspend = 1,
+	},
+	{/* hw:x,8 */
+		.name = MSM_DAILINK_NAME(Compress1),
+		.stream_name = "Compress1",
+		.cpu_dai_name	= "MultiMedia4",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+	},
+	{/* hw:x,9*/
+		.name = "AUXPCM Hostless",
+		.stream_name = "AUXPCM Hostless",
+		.cpu_dai_name   = "AUXPCM_HOSTLESS",
+		.platform_name  = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,10 */
+		.name = "SLIMBUS_1 Hostless",
+		.stream_name = "SLIMBUS_1 Hostless",
+		.cpu_dai_name = "SLIMBUS1_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,11 */
+		.name = "SLIMBUS_3 Hostless",
+		.stream_name = "SLIMBUS_3 Hostless",
+		.cpu_dai_name = "SLIMBUS3_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,12 */
+		.name = "SLIMBUS_4 Hostless",
+		.stream_name = "SLIMBUS_4 Hostless",
+		.cpu_dai_name = "SLIMBUS4_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,13 */
+		.name = MSM_DAILINK_NAME(LowLatency),
+		.stream_name = "MultiMedia5",
+		.cpu_dai_name   = "MultiMedia5",
+		.platform_name  = "msm-pcm-dsp.1",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+	},
+	/* LSM FE */
+	{/* hw:x,14 */
+		.name = "Listen 1 Audio Service",
+		.stream_name = "Listen 1 Audio Service",
+		.cpu_dai_name = "LSM1",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM1,
+	},
+	{/* hw:x,15 */
+		.name = MSM_DAILINK_NAME(Compress2),
+		.stream_name = "Compress2",
+		.cpu_dai_name   = "MultiMedia7",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+	},
+	{/* hw:x,16 */
+		.name = MSM_DAILINK_NAME(Compress3),
+		.stream_name = "Compress3",
+		.cpu_dai_name	= "MultiMedia10",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+	},
+	{/* hw:x,17 */
+		.name = MSM_DAILINK_NAME(ULL_NOIRQ),
+		.stream_name = "MM_NOIRQ",
+		.cpu_dai_name	= "MultiMedia8",
+		.platform_name  = "msm-pcm-dsp-noirq",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+	},
+	{/* hw:x,18 */
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name	= "HDMI_HOSTLESS",
+		.platform_name  = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,19 */
+		.name = "VoiceMMode2",
+		.stream_name = "VoiceMMode2",
+		.cpu_dai_name = "VoiceMMode2",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+	},
+	{/* hw:x,20 */
+		.name = "Listen 2 Audio Service",
+		.stream_name = "Listen 2 Audio Service",
+		.cpu_dai_name = "LSM2",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM2,
+	},
+	{/* hw:x,21 */
+		.name = "Listen 3 Audio Service",
+		.stream_name = "Listen 3 Audio Service",
+		.cpu_dai_name = "LSM3",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM3,
+	},
+	{/* hw:x,22 */
+		.name = "Listen 4 Audio Service",
+		.stream_name = "Listen 4 Audio Service",
+		.cpu_dai_name = "LSM4",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM4,
+	},
+	{/* hw:x,23 */
+		.name = "Listen 5 Audio Service",
+		.stream_name = "Listen 5 Audio Service",
+		.cpu_dai_name = "LSM5",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM5,
+	},
+	{/* hw:x,24 */
+		.name = "Listen 6 Audio Service",
+		.stream_name = "Listen 6 Audio Service",
+		.cpu_dai_name = "LSM6",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM6
+	},
+	{/* hw:x,25 */
+		.name = "Listen 7 Audio Service",
+		.stream_name = "Listen 7 Audio Service",
+		.cpu_dai_name = "LSM7",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM7,
+	},
+	{/* hw:x,26 */
+		.name = "Listen 8 Audio Service",
+		.stream_name = "Listen 8 Audio Service",
+		.cpu_dai_name = "LSM8",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM8,
+	},
+	{/* hw:x,27 */
+		.name = MSM_DAILINK_NAME(Media9),
+		.stream_name = "MultiMedia9",
+		.cpu_dai_name	= "MultiMedia9",
+		.platform_name  = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+	},
+	{/* hw:x,28 */
+		.name = MSM_DAILINK_NAME(Compress4),
+		.stream_name = "Compress4",
+		.cpu_dai_name	= "MultiMedia11",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+	},
+	{/* hw:x,29 */
+		.name = MSM_DAILINK_NAME(Compress5),
+		.stream_name = "Compress5",
+		.cpu_dai_name	= "MultiMedia12",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+	},
+	{/* hw:x,30 */
+		.name = MSM_DAILINK_NAME(Compress6),
+		.stream_name = "Compress6",
+		.cpu_dai_name	= "MultiMedia13",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+	},
+	{/* hw:x,31 */
+		.name = MSM_DAILINK_NAME(Compress7),
+		.stream_name = "Compress7",
+		.cpu_dai_name	= "MultiMedia14",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+	},
+	{/* hw:x,32 */
+		.name = MSM_DAILINK_NAME(Compress8),
+		.stream_name = "Compress8",
+		.cpu_dai_name	= "MultiMedia15",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+	},
+	{/* hw:x,33 */
+		.name = MSM_DAILINK_NAME(Compress9),
+		.stream_name = "Compress9",
+		.cpu_dai_name	= "MultiMedia16",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+	},
+	{/* hw:x,34 */
+		.name = "SLIMBUS_8 Hostless",
+		.stream_name = "SLIMBUS8_HOSTLESS Capture",
+		.cpu_dai_name = "SLIMBUS8_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+};
+
+static struct snd_soc_dai_link msm_ext_common_be_dai[] = {
+	{
+		.name = LPASS_BE_AFE_PCM_RX,
+		.stream_name = "AFE Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.224",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_AFE_PCM_TX,
+		.stream_name = "AFE Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.225",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music 2 BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
+		.stream_name = "Voice2 Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_RX,
+		.stream_name = "USB Audio Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.28672",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_USB_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_TX,
+		.stream_name = "USB Audio Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.28673",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_USB_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_RX_0,
+		.stream_name = "Primary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36864",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_TX_0,
+		.stream_name = "Primary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36865",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_RX_0,
+		.stream_name = "Secondary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36880",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_TX_0,
+		.stream_name = "Secondary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36881",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_RX_0,
+		.stream_name = "Tertiary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36896",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_TX_0,
+		.stream_name = "Tertiary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36897",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_RX_0,
+		.stream_name = "Quaternary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36912",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_TX_0,
+		.stream_name = "Quaternary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36913",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+	{
+		.name = LPASS_BE_PRI_MI2S_RX,
+		.stream_name = "Primary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_MI2S_TX,
+		.stream_name = "Primary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_RX,
+		.stream_name = "Secondary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_TX,
+		.stream_name = "Secondary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_RX,
+		.stream_name = "Tertiary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_TX,
+		.stream_name = "Tertiary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_RX,
+		.stream_name = "Quaternary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_TX,
+		.stream_name = "Quaternary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+	/* Primary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_AUXPCM_RX,
+		.stream_name = "AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_AUXPCM_TX,
+		.stream_name = "AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Secondary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_SEC_AUXPCM_RX,
+		.stream_name = "Sec AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SEC_AUXPCM_TX,
+		.stream_name = "Sec AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Tertiary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_TERT_AUXPCM_RX,
+		.stream_name = "Tert AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_TERT_AUXPCM_TX,
+		.stream_name = "Tert AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Quaternary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_RX,
+		.stream_name = "Quat AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_TX,
+		.stream_name = "Quat AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_7_RX,
+		.stream_name = "Slimbus7 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16398",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		/* BT codec driver determines capabilities based on
+		 * dai name, bt codecdai name should always contains
+		 * supported usecase information
+		 */
+		.codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_7_TX,
+		.stream_name = "Slimbus7 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16399",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_bt_sco_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_8_TX,
+		.stream_name = "Slimbus8 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16401",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_fm_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+		.be_hw_params_fixup = msm_ext_be_hw_params_fixup,
+		.init = &msm_wcn_init,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_ext_tasha_dai_links[
+ARRAY_SIZE(msm_ext_common_fe_dai) +
+ARRAY_SIZE(msm_ext_tasha_fe_dai) +
+ARRAY_SIZE(msm_ext_common_be_dai) +
+ARRAY_SIZE(msm_ext_tasha_be_dai) +
+ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ARRAY_SIZE(msm_auxpcm_be_dai_links) +
+ARRAY_SIZE(msm_wcn_be_dai_links)];
+
+static struct snd_soc_dai_link msm_ext_tavil_dai_links[
+ARRAY_SIZE(msm_ext_common_fe_dai) +
+ARRAY_SIZE(msm_ext_tavil_fe_dai) +
+ARRAY_SIZE(msm_ext_common_be_dai) +
+ARRAY_SIZE(msm_ext_tavil_be_dai) +
+ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ARRAY_SIZE(msm_auxpcm_be_dai_links) +
+ARRAY_SIZE(msm_wcn_be_dai_links)];
+
+/**
+ * populate_snd_card_dailinks - prepares dailink array and initializes card.
+ *
+ * @dev: device handle
+ *
+ * Returns card on success or NULL on failure.
+ */
+struct snd_soc_card *populate_snd_card_dailinks(struct device *dev,
+						int snd_card_val)
+{
+	struct snd_soc_card *card;
+	struct snd_soc_dai_link *msm_ext_dai_links = NULL;
+	int ret, len1, len2, len3, len4;
+	enum codec_variant codec_ver = 0;
+
+	if (snd_card_val == EXT_SND_CARD_TASHA) {
+		card = &snd_soc_card_msm_card_tasha;
+	} else if (snd_card_val == EXT_SND_CARD_TAVIL) {
+		card = &snd_soc_card_msm_card_tavil;
+	} else {
+		dev_err(dev, "%s: failing as no matching card name\n",
+			__func__);
+		return NULL;
+	}
+
+	card->dev = dev;
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(dev, "%s: parse card name failed, err:%d\n",
+			__func__, ret);
+		return NULL;
+	}
+
+	if (strnstr(card->name, "tasha", strlen(card->name))) {
+		codec_ver = tasha_codec_ver();
+		if (codec_ver == WCD9326)
+			card->name = "msmfalcon-tashalite-snd-card";
+
+		len1 = ARRAY_SIZE(msm_ext_common_fe_dai);
+		len2 = len1 + ARRAY_SIZE(msm_ext_tasha_fe_dai);
+		len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai);
+		memcpy(msm_ext_tasha_dai_links, msm_ext_common_fe_dai,
+		       sizeof(msm_ext_common_fe_dai));
+		memcpy(msm_ext_tasha_dai_links + len1,
+		       msm_ext_tasha_fe_dai, sizeof(msm_ext_tasha_fe_dai));
+		memcpy(msm_ext_tasha_dai_links + len2,
+		       msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai));
+		memcpy(msm_ext_tasha_dai_links + len3,
+		       msm_ext_tasha_be_dai, sizeof(msm_ext_tasha_be_dai));
+		len4 = len3 + ARRAY_SIZE(msm_ext_tasha_be_dai);
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,mi2s-audio-intf")) {
+			memcpy(msm_ext_tasha_dai_links + len4,
+			       msm_mi2s_be_dai_links,
+			       sizeof(msm_mi2s_be_dai_links));
+			len4 += ARRAY_SIZE(msm_mi2s_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,auxpcm-audio-intf")) {
+			memcpy(msm_ext_tasha_dai_links + len4,
+			       msm_auxpcm_be_dai_links,
+			       sizeof(msm_auxpcm_be_dai_links));
+			len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+			dev_dbg(dev, "%s(): WCN BTFM support present\n",
+					__func__);
+			memcpy(msm_ext_tasha_dai_links + len4,
+				   msm_wcn_be_dai_links,
+				   sizeof(msm_wcn_be_dai_links));
+			len4 += ARRAY_SIZE(msm_wcn_be_dai_links);
+		}
+		msm_ext_dai_links = msm_ext_tasha_dai_links;
+	} else if (strnstr(card->name, "tavil", strlen(card->name))) {
+		len1 = ARRAY_SIZE(msm_ext_common_fe_dai);
+		len2 = len1 + ARRAY_SIZE(msm_ext_tavil_fe_dai);
+		len3 = len2 + ARRAY_SIZE(msm_ext_common_be_dai);
+		memcpy(msm_ext_tavil_dai_links, msm_ext_common_fe_dai,
+		       sizeof(msm_ext_common_fe_dai));
+		memcpy(msm_ext_tavil_dai_links + len1,
+		       msm_ext_tavil_fe_dai, sizeof(msm_ext_tavil_fe_dai));
+		memcpy(msm_ext_tavil_dai_links + len2,
+		       msm_ext_common_be_dai, sizeof(msm_ext_common_be_dai));
+		memcpy(msm_ext_tavil_dai_links + len3,
+		       msm_ext_tavil_be_dai, sizeof(msm_ext_tavil_be_dai));
+		len4 = len3 + ARRAY_SIZE(msm_ext_tavil_be_dai);
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,mi2s-audio-intf")) {
+			memcpy(msm_ext_tavil_dai_links + len4,
+			       msm_mi2s_be_dai_links,
+			       sizeof(msm_mi2s_be_dai_links));
+			len4 += ARRAY_SIZE(msm_mi2s_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,auxpcm-audio-intf")) {
+			memcpy(msm_ext_tavil_dai_links + len4,
+			       msm_auxpcm_be_dai_links,
+			       sizeof(msm_auxpcm_be_dai_links));
+			len4 += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+			dev_dbg(dev, "%s(): WCN BTFM support present\n",
+					__func__);
+			memcpy(msm_ext_tavil_dai_links + len4,
+				   msm_wcn_be_dai_links,
+				   sizeof(msm_wcn_be_dai_links));
+			len4 += ARRAY_SIZE(msm_wcn_be_dai_links);
+		}
+		msm_ext_dai_links = msm_ext_tavil_dai_links;
+	} else {
+		dev_err(dev, "%s: failing as no matching card name\n",
+			__func__);
+		return NULL;
+	}
+	card->dai_link = msm_ext_dai_links;
+	card->num_links = len4;
+
+	return card;
+}
+EXPORT_SYMBOL(populate_snd_card_dailinks);
diff --git a/sound/soc/msm/msmfalcon-external.c b/sound/soc/msm/msmfalcon-external.c
new file mode 100644
index 0000000..d7b002e
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-external.c
@@ -0,0 +1,1765 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/q6core.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "msm-audio-pinctrl.h"
+#include "msmfalcon-common.h"
+#include "msmfalcon-external.h"
+#include "../codecs/wcd9335.h"
+#include "../codecs/wcd934x/wcd934x.h"
+#include "../codecs/wcd934x/wcd934x-mbhc.h"
+
+#define MSMFALCON_SPK_ON     1
+#define MSMFALCON_SPK_OFF    0
+
+#define WCD9XXX_MBHC_DEF_BUTTONS    8
+#define WCD9XXX_MBHC_DEF_RLOADS     5
+#define CODEC_EXT_CLK_RATE          9600000
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+static int msm_ext_spk_control = 1;
+static struct wcd_mbhc_config *wcd_mbhc_cfg_ptr;
+
+struct msm_asoc_wcd93xx_codec {
+	void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+				   enum afe_config_type config_type);
+	void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+static struct msm_asoc_wcd93xx_codec msm_codec_fn;
+static struct platform_device *spdev;
+
+static bool is_initial_boot;
+
+static void *def_ext_mbhc_cal(void);
+
+enum {
+	SLIM_RX_0 = 0,
+	SLIM_RX_1,
+	SLIM_RX_2,
+	SLIM_RX_3,
+	SLIM_RX_4,
+	SLIM_RX_5,
+	SLIM_RX_6,
+	SLIM_RX_7,
+	SLIM_RX_MAX,
+};
+
+enum {
+	SLIM_TX_0 = 0,
+	SLIM_TX_1,
+	SLIM_TX_2,
+	SLIM_TX_3,
+	SLIM_TX_4,
+	SLIM_TX_5,
+	SLIM_TX_6,
+	SLIM_TX_7,
+	SLIM_TX_8,
+	SLIM_TX_MAX,
+};
+
+struct dev_config {
+	u32 sample_rate;
+	u32 bit_format;
+	u32 channels;
+};
+
+/* Default configuration of slimbus channels */
+static struct dev_config slim_rx_cfg[] = {
+	[SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config slim_tx_cfg[] = {
+	[SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static int msm_vi_feed_tx_ch = 2;
+static const char *const slim_rx_ch_text[] = {"One", "Two"};
+static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
+						"Five", "Six", "Seven",
+						"Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+					  "S32_LE"};
+static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
+					"KHZ_32", "KHZ_44P1", "KHZ_48",
+					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
+					"KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const spk_function_text[] = {"Off", "On"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(spk_func_en, spk_function_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+
+static int slim_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val = 0;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 10;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int slim_get_sample_rate(int value)
+{
+	int sample_rate = 0;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int slim_get_bit_format_val(int bit_format)
+{
+	int val = 0;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		val = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		val = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		val = 0;
+		break;
+	}
+	return val;
+}
+
+static int slim_get_bit_format(int val)
+{
+	int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+	switch (val) {
+	case 0:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 3:
+		bit_fmt = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	default:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return bit_fmt;
+}
+
+static int slim_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int port_id = 0;
+
+	if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX")))
+		port_id = SLIM_RX_0;
+	else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX")))
+		port_id = SLIM_RX_2;
+	else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX")))
+		port_id = SLIM_RX_5;
+	else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX")))
+		port_id = SLIM_RX_6;
+	else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX")))
+		port_id = SLIM_TX_0;
+	else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX")))
+		port_id = SLIM_TX_1;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	return port_id;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	/*
+	 * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+	 * when used for BT_SCO use case. Return either Rx or Tx sample rate
+	 * value.
+	 */
+	switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+	case SAMPLING_RATE_48KHZ:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+	pr_debug("%s: sample rate = %d", __func__,
+		 slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+	return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 0:
+	default:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n",
+		 __func__,
+		 slim_rx_cfg[SLIM_RX_7].sample_rate,
+		 slim_tx_cfg[SLIM_TX_7].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate);
+
+	pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].sample_rate =
+		slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate);
+
+	pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate = 0;
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+	if (sample_rate == SAMPLING_RATE_44P1KHZ) {
+		pr_err("%s: Unsupported sample rate %d: for Tx path\n",
+			__func__, sample_rate);
+		return -EINVAL;
+	}
+	slim_tx_cfg[ch_num].sample_rate = sample_rate;
+
+	pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+			slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format);
+
+	pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].bit_format =
+		slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+			slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format);
+
+	pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_tx_cfg[ch_num].bit_format =
+		slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	pr_debug("%s: msm_slim_[%d]_rx_ch  = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].channels);
+	ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1;
+
+	return 0;
+}
+
+static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_slim_[%d]_rx_ch  = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].channels);
+
+	return 1;
+}
+
+static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	pr_debug("%s: msm_slim_[%d]_tx_ch  = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].channels);
+	ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1;
+
+	return 0;
+}
+
+static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].channels);
+
+	return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+	pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+	return 1;
+}
+
+static void *def_ext_mbhc_cal(void)
+{
+	void *tavil_wcd_cal;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_high;
+
+	tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+				WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+	if (!tavil_wcd_cal)
+		return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y))
+	S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y))
+	S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal);
+	btn_high = ((void *)&btn_cfg->_v_btn_low) +
+		(sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+	btn_high[0] = 75;
+	btn_high[1] = 150;
+	btn_high[2] = 237;
+	btn_high[3] = 500;
+	btn_high[4] = 500;
+	btn_high[5] = 500;
+	btn_high[6] = 500;
+	btn_high[7] = 500;
+
+	return tavil_wcd_cal;
+}
+
+static inline int param_is_mask(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+		(p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p, int n)
+{
+	return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+
+static void msm_ext_control(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_codec_get_dapm(codec);
+
+	pr_debug("%s: msm_ext_spk_control = %d", __func__, msm_ext_spk_control);
+	if (msm_ext_spk_control == MSMFALCON_SPK_ON) {
+		snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+		snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+	} else {
+		snd_soc_dapm_disable_pin(dapm, "Lineout_1 amp");
+		snd_soc_dapm_disable_pin(dapm, "Lineout_3 amp");
+	}
+	snd_soc_dapm_sync(dapm);
+}
+
+static int msm_ext_get_spk(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_ext_spk_control = %d\n",
+		 __func__, msm_ext_spk_control);
+	ucontrol->value.integer.value[0] = msm_ext_spk_control;
+	return 0;
+}
+
+static int msm_ext_set_spk(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+
+	pr_debug("%s()\n", __func__);
+	if (msm_ext_spk_control == ucontrol->value.integer.value[0])
+		return 0;
+
+	msm_ext_spk_control = ucontrol->value.integer.value[0];
+	msm_ext_control(codec);
+	return 1;
+}
+
+
+int msm_ext_enable_codec_mclk(struct snd_soc_codec *codec, int enable,
+			      bool dapm)
+{
+	int ret;
+
+	pr_debug("%s: enable = %d\n", __func__, enable);
+
+	if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+		ret = tasha_cdc_mclk_enable(codec, enable, dapm);
+	else if (!strcmp(dev_name(codec->dev), "tavil_codec"))
+		ret = tavil_cdc_mclk_enable(codec, enable);
+	else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+	SOC_ENUM_EXT("Speaker Function", spk_func_en, msm_ext_get_spk,
+			msm_ext_set_spk),
+	SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs,
+			msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs,
+			msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs,
+			msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format,
+			slim_tx_bit_format_get, slim_tx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate,
+			slim_tx_sample_rate_get, slim_tx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+			msm_bt_sample_rate_get,
+			msm_bt_sample_rate_put),
+};
+
+static int msm_slim_get_ch_from_beid(int32_t be_id)
+{
+	int ch_id = 0;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+		ch_id = SLIM_RX_0;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+		ch_id = SLIM_RX_1;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+		ch_id = SLIM_RX_2;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+		ch_id = SLIM_RX_3;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+		ch_id = SLIM_RX_4;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+		ch_id = SLIM_RX_6;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+		ch_id = SLIM_TX_0;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+		ch_id = SLIM_TX_3;
+		break;
+	default:
+		ch_id = SLIM_RX_0;
+		break;
+	}
+
+	return ch_id;
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+	if (bit >= SNDRV_MASK_MAX)
+		return;
+	if (param_is_mask(n)) {
+		struct snd_mask *m = param_to_mask(p, n);
+
+		m->bits[0] = 0;
+		m->bits[1] = 0;
+		m->bits[bit >> 5] |= (1 << (bit & 31));
+	}
+}
+
+/**
+ * msm_ext_be_hw_params_fixup - updates settings of ALSA BE hw params.
+ *
+ * @rtd: runtime dailink instance
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or rc on failure.
+ */
+int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+			       struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	int rc = 0;
+	int idx;
+	void *config = NULL;
+	struct snd_soc_codec *codec = rtd->codec;
+
+	pr_debug("%s: format = %d, rate = %d\n",
+		  __func__, params_format(params), params_rate(params));
+
+	switch (dai_link->be_id) {
+	case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+		idx = msm_slim_get_ch_from_beid(dai_link->be_id);
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[idx].bit_format);
+		rate->min = rate->max = slim_rx_cfg[idx].sample_rate;
+		channels->min = channels->max = slim_rx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+	case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+		idx = msm_slim_get_ch_from_beid(dai_link->be_id);
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_tx_cfg[idx].bit_format);
+		rate->min = rate->max = slim_tx_cfg[idx].sample_rate;
+		channels->min = channels->max = slim_tx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_1_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_tx_cfg[1].bit_format);
+		rate->min = rate->max = slim_tx_cfg[1].sample_rate;
+		channels->min = channels->max = slim_tx_cfg[1].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_4_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       SNDRV_PCM_FORMAT_S32_LE);
+		rate->min = rate->max = SAMPLING_RATE_8KHZ;
+		channels->min = channels->max = msm_vi_feed_tx_ch;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_5_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[5].bit_format);
+		rate->min = rate->max = slim_rx_cfg[5].sample_rate;
+		channels->min = channels->max = slim_rx_cfg[5].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_5_TX:
+		rate->min = rate->max = SAMPLING_RATE_16KHZ;
+		channels->min = channels->max = 1;
+
+		config = msm_codec_fn.get_afe_config_fn(codec,
+					AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+		if (config) {
+			rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+					    config, SLIMBUS_5_TX);
+			if (rc)
+				pr_err("%s: Failed to set slimbus slave port config %d\n",
+					__func__, rc);
+		}
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[SLIM_RX_7].bit_format);
+		rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate;
+		channels->min = channels->max =
+			slim_rx_cfg[SLIM_RX_7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+		rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate;
+		channels->min = channels->max =
+			slim_tx_cfg[SLIM_TX_7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+		rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate;
+		channels->min = channels->max =
+			slim_tx_cfg[SLIM_TX_8].channels;
+		break;
+
+	default:
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return rc;
+}
+EXPORT_SYMBOL(msm_ext_be_hw_params_fixup);
+
+/**
+ * msm_snd_hw_params - hw params ops of backend dailink.
+ *
+ * @substream: PCM stream of associated backend dailink.
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_snd_hw_params(struct snd_pcm_substream *substream,
+		      struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+	int ret = 0;
+	u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	u32 user_set_tx_ch = 0;
+	u32 rx_ch_count;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+		if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+			pr_debug("%s: rx_5_ch=%d\n", __func__,
+				  slim_rx_cfg[5].channels);
+			rx_ch_count = slim_rx_cfg[5].channels;
+		} else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_2_RX) {
+			pr_debug("%s: rx_2_ch=%d\n", __func__,
+				 slim_rx_cfg[2].channels);
+			rx_ch_count = slim_rx_cfg[2].channels;
+		} else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+			pr_debug("%s: rx_6_ch=%d\n", __func__,
+				  slim_rx_cfg[6].channels);
+			rx_ch_count = slim_rx_cfg[6].channels;
+		} else {
+			pr_debug("%s: rx_0_ch=%d\n", __func__,
+				  slim_rx_cfg[0].channels);
+			rx_ch_count = slim_rx_cfg[0].channels;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+						  rx_ch_count, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+	} else {
+		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+			 codec_dai->name, codec_dai->id, user_set_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map\n, err:%d\n",
+				__func__, ret);
+			goto err_ch_map;
+		}
+		/* For <codec>_tx1 case */
+		if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+			user_set_tx_ch = slim_tx_cfg[0].channels;
+		/* For <codec>_tx3 case */
+		else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+			user_set_tx_ch = slim_tx_cfg[1].channels;
+		else if (dai_link->be_id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+			user_set_tx_ch = msm_vi_feed_tx_ch;
+		else
+			user_set_tx_ch = tx_ch_cnt;
+
+		pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), be_id (%d)\n",
+			 __func__,  slim_tx_cfg[0].channels, user_set_tx_ch,
+			 tx_ch_cnt, dai_link->be_id);
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+						  user_set_tx_ch, tx_ch, 0, 0);
+		if (ret < 0)
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+	}
+
+err_ch_map:
+	return ret;
+}
+EXPORT_SYMBOL(msm_snd_hw_params);
+
+/**
+ * msm_ext_slimbus_2_hw_params - hw params ops of slimbus_2 BE.
+ *
+ * @substream: PCM stream of associated backend dailink.
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+	unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+	unsigned int num_tx_ch = 0;
+	unsigned int num_rx_ch = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		num_rx_ch =  params_channels(params);
+		pr_debug("%s: %s rx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_rx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+				num_rx_ch, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	} else {
+		num_tx_ch =  params_channels(params);
+		pr_debug("%s: %s  tx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+				num_tx_ch, tx_ch, 0, 0);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	}
+end:
+	return ret;
+}
+EXPORT_SYMBOL(msm_ext_slimbus_2_hw_params);
+
+/**
+ * msm_snd_cpe_hw_params - hw params ops of CPE backend.
+ *
+ * @substream: PCM stream of associated backend dailink.
+ * @params: HW params of associated backend dailink.
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	int ret = 0;
+	u32 tx_ch[SLIM_MAX_TX_PORTS];
+	u32 tx_ch_cnt = 0;
+
+	if (substream->stream != SNDRV_PCM_STREAM_CAPTURE) {
+		pr_err("%s: Invalid stream type %d\n",
+			__func__, substream->stream);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	pr_debug("%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, NULL, NULL);
+	if (ret < 0) {
+		pr_err("%s: failed to get codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto end;
+	}
+
+	pr_debug("%s: tx_ch_cnt(%d) be_id %d\n",
+		 __func__, tx_ch_cnt, dai_link->be_id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  tx_ch_cnt, tx_ch, 0, 0);
+	if (ret < 0) {
+		pr_err("%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+		goto end;
+	}
+end:
+	return ret;
+}
+EXPORT_SYMBOL(msm_snd_cpe_hw_params);
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+	int rc;
+	void *config_data;
+
+	pr_debug("%s: enter\n", __func__);
+
+	if (!msm_codec_fn.get_afe_config_fn) {
+		dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+				__func__);
+		return -EINVAL;
+	}
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+				AFE_CDC_REGISTERS_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set codec registers config %d\n",
+					__func__, rc);
+			return rc;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTER_PAGE_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+				    0);
+		if (rc)
+			pr_err("%s: Failed to set cdc register page config\n",
+				__func__);
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_SLIMBUS_SLAVE_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set slimbus slave config %d\n",
+					__func__, rc);
+			return rc;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_AANC_VERSION);
+	if (config_data) {
+		rc = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set AANC version %d\n",
+					__func__, rc);
+			return rc;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+				AFE_CDC_CLIP_REGISTERS_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+					config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set clip registers %d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CLIP_BANK_SEL);
+	if (config_data) {
+		rc = afe_set_config(AFE_CLIP_BANK_SEL,
+				config_data, 0);
+		if (rc) {
+			pr_err("%s: Failed to set AFE bank selection %d\n",
+				__func__, rc);
+			return rc;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTER_PAGE_CONFIG);
+	if (config_data) {
+		rc = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+				0);
+		if (rc)
+			pr_err("%s: Failed to set cdc register page config\n",
+					__func__);
+	}
+
+	return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+	afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+	afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int msm_adsp_power_up_config(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	unsigned long timeout;
+	int adsp_ready = 0;
+
+	timeout = jiffies +
+		msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+	do {
+		if (q6core_is_adsp_ready()) {
+			pr_debug("%s: ADSP Audio is ready\n", __func__);
+			adsp_ready = 1;
+			break;
+		}
+		/*
+		 * ADSP will be coming up after subsystem restart and
+		 * it might not be fully up when the control reaches
+		 * here. So, wait for 50msec before checking ADSP state
+		 */
+		msleep(50);
+	} while (time_after(timeout, jiffies));
+
+	if (!adsp_ready) {
+		pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+		ret = -ETIMEDOUT;
+		goto err_fail;
+	}
+
+	ret = msm_afe_set_config(codec);
+	if (ret)
+		pr_err("%s: Failed to set AFE config. err %d\n",
+			__func__, ret);
+
+	return 0;
+
+err_fail:
+	return ret;
+}
+
+static int msmfalcon_notifier_service_cb(struct notifier_block *this,
+					 unsigned long opcode, void *ptr)
+{
+	int ret;
+	struct snd_soc_card *card = NULL;
+	const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_codec *codec;
+
+	pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
+
+	switch (opcode) {
+	case AUDIO_NOTIFIER_SERVICE_DOWN:
+		/*
+		 * Use flag to ignore initial boot notifications
+		 * On initial boot msm_adsp_power_up_config is
+		 * called on init. There is no need to clear
+		 * and set the config again on initial boot.
+		 */
+		if (is_initial_boot)
+			break;
+		msm_afe_clear_config();
+		break;
+	case AUDIO_NOTIFIER_SERVICE_UP:
+		if (is_initial_boot) {
+			is_initial_boot = false;
+			break;
+		}
+		if (!spdev)
+			return -EINVAL;
+
+		card = platform_get_drvdata(spdev);
+		rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+		if (!rtd) {
+			dev_err(card->dev,
+				"%s: snd_soc_get_pcm_runtime for %s failed!\n",
+				__func__, be_dl_name);
+			ret = -EINVAL;
+			goto done;
+		}
+		codec = rtd->codec;
+
+		ret = msm_adsp_power_up_config(codec);
+		if (ret < 0) {
+			dev_err(card->dev,
+				"%s: msm_adsp_power_up_config failed ret = %d!\n",
+				__func__, ret);
+			goto done;
+		}
+		break;
+	default:
+		break;
+	}
+done:
+	return NOTIFY_OK;
+}
+
+static struct notifier_block service_nb = {
+	.notifier_call  = msmfalcon_notifier_service_cb,
+	.priority = -INT_MAX,
+};
+
+static int msm_config_hph_en0_gpio(struct snd_soc_codec *codec, bool high)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata;
+	int val;
+
+	if (!card)
+		return 0;
+
+	pdata = snd_soc_card_get_drvdata(card);
+	if (!pdata || !gpio_is_valid(pdata->hph_en0_gpio))
+		return 0;
+
+	val = gpio_get_value_cansleep(pdata->hph_en0_gpio);
+	if ((!!val) == high)
+		return 0;
+
+	gpio_direction_output(pdata->hph_en0_gpio, (int)high);
+
+	return 1;
+}
+
+static int msm_snd_enable_codec_ext_tx_clk(struct snd_soc_codec *codec,
+					   int enable, bool dapm)
+{
+	int ret = 0;
+
+	if (!strcmp(dev_name(codec->dev), "tasha_codec"))
+		ret = tasha_cdc_mclk_tx_enable(codec, enable, dapm);
+	else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int msm_ext_mclk_tx_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_snd_enable_codec_ext_tx_clk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_snd_enable_codec_ext_tx_clk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_ext_mclk_event(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_ext_enable_codec_mclk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_ext_enable_codec_mclk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_ext_prepare_hifi(struct msm_asoc_mach_data *pdata)
+{
+	int ret = 0;
+
+	if (gpio_is_valid(pdata->hph_en1_gpio)) {
+		pr_debug("%s: hph_en1_gpio request %d\n", __func__,
+			pdata->hph_en1_gpio);
+		ret = gpio_request(pdata->hph_en1_gpio, "hph_en1_gpio");
+		if (ret) {
+			pr_err("%s: hph_en1_gpio request failed, ret:%d\n",
+				__func__, ret);
+			goto err;
+		}
+	}
+	if (gpio_is_valid(pdata->hph_en0_gpio)) {
+		pr_debug("%s: hph_en0_gpio request %d\n", __func__,
+			pdata->hph_en0_gpio);
+		ret = gpio_request(pdata->hph_en0_gpio, "hph_en0_gpio");
+		if (ret)
+			pr_err("%s: hph_en0_gpio request failed, ret:%d\n",
+				__func__, ret);
+	}
+
+err:
+	return ret;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+	SND_SOC_DAPM_SUPPLY_S("MCLK", -1,  SND_SOC_NOPM, 0, 0,
+	msm_ext_mclk_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY_S("MCLK TX", -1,  SND_SOC_NOPM, 0, 0,
+	msm_ext_mclk_tx_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_3 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_4 amp", NULL),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Secondary Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic4", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic6", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic7", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic8", NULL),
+
+	SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic6", NULL),
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths_tasha[] = {
+	{"MIC BIAS1", NULL, "MCLK TX"},
+	{"MIC BIAS2", NULL, "MCLK TX"},
+	{"MIC BIAS3", NULL, "MCLK TX"},
+	{"MIC BIAS4", NULL, "MCLK TX"},
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths[] = {
+	{"MIC BIAS1", NULL, "MCLK"},
+	{"MIC BIAS2", NULL, "MCLK"},
+	{"MIC BIAS3", NULL, "MCLK"},
+	{"MIC BIAS4", NULL, "MCLK"},
+};
+
+/**
+ * msm_audrx_init - Audio init function of sound card instantiate.
+ *
+ * @rtd: runtime dailink instance
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret;
+	void *config_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_codec_get_dapm(codec);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_pcm_runtime *rtd_aux = rtd->card->rtd_aux;
+	struct snd_card *card;
+	struct snd_info_entry *entry;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(rtd->card);
+
+	/* Codec SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8, RX9, RX10, RX11, RX12, RX13
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+	 * TX14, TX15, TX16
+	 */
+	unsigned int rx_ch[TASHA_RX_MAX] = {144, 145, 146, 147, 148, 149, 150,
+					    151, 152, 153, 154, 155, 156};
+	unsigned int tx_ch[TASHA_TX_MAX]  = {128, 129, 130, 131, 132, 133,
+					     134, 135, 136, 137, 138, 139,
+					     140, 141, 142, 143};
+
+	pr_debug("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+	rtd->pmdown_time = 0;
+
+	ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+					 ARRAY_SIZE(msm_snd_controls));
+	if (ret < 0) {
+		pr_err("%s: add_codec_controls failed: %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+			ARRAY_SIZE(msm_dapm_widgets));
+
+	if (!strcmp(dev_name(codec_dai->dev), "tasha_codec"))
+		snd_soc_dapm_add_routes(dapm, wcd_audio_paths_tasha,
+					ARRAY_SIZE(wcd_audio_paths_tasha));
+	else
+		snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+					ARRAY_SIZE(wcd_audio_paths));
+
+	snd_soc_dapm_enable_pin(dapm, "Lineout_1 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_3 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_2 amp");
+	snd_soc_dapm_enable_pin(dapm, "Lineout_4 amp");
+
+	snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_1 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_3 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_2 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "Lineout_4 amp");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic4");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic6");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic7");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic8");
+
+	snd_soc_dapm_ignore_suspend(dapm, "EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT3");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT4");
+	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, "AMIC4");
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC5");
+	snd_soc_dapm_ignore_suspend(dapm, "AMIC6");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC0");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC1");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC5");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HEADPHONE");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC LINEOUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+	snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+
+	snd_soc_dapm_sync(dapm);
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+	if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+		msm_codec_fn.get_afe_config_fn = tavil_get_afe_config;
+	} else {
+		msm_codec_fn.get_afe_config_fn = tasha_get_afe_config;
+		msm_codec_fn.mbhc_hs_detect_exit = tasha_mbhc_hs_detect_exit;
+	}
+
+	ret = msm_adsp_power_up_config(codec);
+	if (ret) {
+		pr_err("%s: Failed to set AFE config %d\n", __func__, ret);
+		goto err_afe_cfg;
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+						     AFE_AANC_VERSION);
+	if (config_data) {
+		ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+		if (ret) {
+			pr_err("%s: Failed to set aanc version %d\n",
+				__func__, ret);
+			goto err_afe_cfg;
+		}
+	}
+
+	if (!strcmp(dev_name(codec_dai->dev), "tasha_codec")) {
+		config_data = msm_codec_fn.get_afe_config_fn(codec,
+						AFE_CDC_CLIP_REGISTERS_CONFIG);
+		if (config_data) {
+			ret = afe_set_config(AFE_CDC_CLIP_REGISTERS_CONFIG,
+						 config_data, 0);
+			if (ret) {
+				pr_err("%s: Failed to set clip registers %d\n",
+					__func__, ret);
+				goto err_afe_cfg;
+			}
+		}
+		config_data = msm_codec_fn.get_afe_config_fn(codec,
+				AFE_CLIP_BANK_SEL);
+		if (config_data) {
+			ret = afe_set_config(AFE_CLIP_BANK_SEL, config_data, 0);
+			if (ret) {
+				pr_err("%s: Failed to set AFE bank selection %d\n",
+					__func__, ret);
+				goto err_afe_cfg;
+			}
+		}
+	}
+
+	/*
+	 * Send speaker configuration only for WSA8810.
+	 * Defalut configuration is for WSA8815.
+	 */
+	if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+		if (rtd_aux && rtd_aux->component)
+			if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+			    !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+				tavil_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+				tavil_set_spkr_gain_offset(rtd->codec,
+							RX_GAIN_OFFSET_M1P5_DB);
+		}
+		card = rtd->card->snd_card;
+		entry = snd_info_create_subdir(card->module, "codecs",
+						 card->proc_root);
+		if (!entry) {
+			pr_debug("%s: Cannot create codecs module entry\n",
+				 __func__);
+			pdata->codec_root = NULL;
+			goto done;
+		}
+		pdata->codec_root = entry;
+		tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
+	} else {
+		if (rtd_aux && rtd_aux->component)
+			if (!strcmp(rtd_aux->component->name, WSA8810_NAME_1) ||
+			    !strcmp(rtd_aux->component->name, WSA8810_NAME_2)) {
+				tasha_set_spkr_mode(rtd->codec, SPKR_MODE_1);
+				tasha_set_spkr_gain_offset(rtd->codec,
+							RX_GAIN_OFFSET_M1P5_DB);
+		}
+		card = rtd->card->snd_card;
+		entry = snd_info_create_subdir(card->module, "codecs",
+						 card->proc_root);
+		if (!entry) {
+			pr_debug("%s: Cannot create codecs module entry\n",
+				 __func__);
+			ret = 0;
+			goto err_snd_module;
+		}
+		pdata->codec_root = entry;
+		tasha_codec_info_create_codec_entry(pdata->codec_root, codec);
+		tasha_mbhc_zdet_gpio_ctrl(msm_config_hph_en0_gpio, rtd->codec);
+	}
+
+	wcd_mbhc_cfg_ptr->calibration = def_ext_mbhc_cal();
+	if (!strcmp(dev_name(codec_dai->dev), "tavil_codec")) {
+		if (wcd_mbhc_cfg_ptr->calibration) {
+			pdata->codec = codec;
+			ret = tavil_mbhc_hs_detect(codec, wcd_mbhc_cfg_ptr);
+			if (ret < 0)
+				pr_err("%s: Failed to intialise mbhc %d\n",
+						__func__, ret);
+		} else {
+			pr_err("%s: wcd_mbhc_cfg calibration is NULL\n",
+					__func__);
+			ret = -ENOMEM;
+			goto err_mbhc_cal;
+		}
+	} else {
+		if (wcd_mbhc_cfg_ptr->calibration) {
+			pdata->codec = codec;
+			ret = tasha_mbhc_hs_detect(codec, wcd_mbhc_cfg_ptr);
+			if (ret < 0)
+				pr_err("%s: Failed to intialise mbhc %d\n",
+						__func__, ret);
+		} else {
+			pr_err("%s: wcd_mbhc_cfg calibration is NULL\n",
+					__func__);
+			ret = -ENOMEM;
+			goto err_mbhc_cal;
+		}
+
+	}
+done:
+	return 0;
+
+err_snd_module:
+err_afe_cfg:
+err_mbhc_cal:
+	return ret;
+}
+EXPORT_SYMBOL(msm_audrx_init);
+
+/**
+ * msm_ext_register_audio_notifier - register SSR notifier.
+ */
+void msm_ext_register_audio_notifier(void)
+{
+	int ret;
+
+	ret = audio_notifier_register("msmfalcon", AUDIO_NOTIFIER_ADSP_DOMAIN,
+				      &service_nb);
+	if (ret < 0)
+		pr_err("%s: Audio notifier register failed ret = %d\n",
+			__func__, ret);
+}
+EXPORT_SYMBOL(msm_ext_register_audio_notifier);
+
+/**
+ * msm_ext_cdc_init - external codec machine specific init.
+ *
+ * @pdev: platform device handle
+ * @pdata: private data of machine driver
+ * @card: sound card pointer reference
+ * @mbhc_cfg: MBHC config reference
+ *
+ * Returns 0 on success or ret on failure.
+ */
+int msm_ext_cdc_init(struct platform_device *pdev,
+		     struct msm_asoc_mach_data *pdata,
+		     struct snd_soc_card **card,
+		     struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1)
+{
+	int ret = 0;
+
+	wcd_mbhc_cfg_ptr = wcd_mbhc_cfg_ptr1;
+	pdev->id = 0;
+	wcd_mbhc_cfg_ptr->moisture_en = true;
+	wcd_mbhc_cfg_ptr->mbhc_micbias = MIC_BIAS_2;
+	wcd_mbhc_cfg_ptr->anc_micbias = MIC_BIAS_2;
+	wcd_mbhc_cfg_ptr->enable_anc_mic_detect = false;
+
+	*card = populate_snd_card_dailinks(&pdev->dev, pdata->snd_card_val);
+	if (!(*card)) {
+		dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+	spdev = pdev;
+	platform_set_drvdata(pdev, *card);
+	snd_soc_card_set_drvdata(*card, pdata);
+	is_initial_boot = true;
+	pdata->hph_en1_gpio = of_get_named_gpio(pdev->dev.of_node,
+						"qcom,hph-en1-gpio", 0);
+	if (!gpio_is_valid(pdata->hph_en1_gpio))
+		pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+					"qcom,hph-en1-gpio", 0);
+	if (!gpio_is_valid(pdata->hph_en1_gpio) && (!pdata->hph_en1_gpio_p)) {
+		dev_dbg(&pdev->dev, "property %s not detected in node %s",
+			"qcom,hph-en1-gpio", pdev->dev.of_node->full_name);
+	}
+
+	pdata->hph_en0_gpio = of_get_named_gpio(pdev->dev.of_node,
+						"qcom,hph-en0-gpio", 0);
+	if (!gpio_is_valid(pdata->hph_en0_gpio))
+		pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+					"qcom,hph-en0-gpio", 0);
+	if (!gpio_is_valid(pdata->hph_en0_gpio) && (!pdata->hph_en0_gpio_p)) {
+		dev_dbg(&pdev->dev, "property %s not detected in node %s",
+			"qcom,hph-en0-gpio", pdev->dev.of_node->full_name);
+	}
+
+	ret = msm_ext_prepare_hifi(pdata);
+	if (ret) {
+		dev_dbg(&pdev->dev, "msm_ext_prepare_hifi failed (%d)\n",
+			ret);
+		ret = 0;
+	}
+err:
+	return ret;
+}
+EXPORT_SYMBOL(msm_ext_cdc_init);
diff --git a/sound/soc/msm/msmfalcon-external.h b/sound/soc/msm/msmfalcon-external.h
new file mode 100644
index 0000000..654cb70
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-external.h
@@ -0,0 +1,50 @@
+/* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSMFALCON_EXTERNAL
+#define __MSMFALCON_EXTERNAL
+
+int msm_snd_hw_params(struct snd_pcm_substream *substream,
+		      struct snd_pcm_hw_params *params);
+int msm_ext_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params);
+int msm_btsco_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				 struct snd_pcm_hw_params *params);
+int msm_proxy_rx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				    struct snd_pcm_hw_params *params);
+int msm_proxy_tx_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				    struct snd_pcm_hw_params *params);
+int msm_audrx_init(struct snd_soc_pcm_runtime *rtd);
+int msm_snd_cpe_hw_params(struct snd_pcm_substream *substream,
+			  struct snd_pcm_hw_params *params);
+struct snd_soc_card *populate_snd_card_dailinks(struct device *dev,
+						int snd_card_val);
+int msm_ext_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+			       struct snd_pcm_hw_params *params);
+#ifdef CONFIG_SND_SOC_EXT_CODEC
+int msm_ext_cdc_init(struct platform_device *, struct msm_asoc_mach_data *,
+		     struct snd_soc_card **, struct wcd_mbhc_config *);
+void msm_ext_register_audio_notifier(void);
+#else
+inline int msm_ext_cdc_init(struct platform_device *pdev,
+			    struct msm_asoc_mach_data *pdata,
+			    struct snd_soc_card **card,
+			    struct wcd_mbhc_config *wcd_mbhc_cfg_ptr1)
+{
+	return 0;
+}
+
+inline void msm_ext_register_audio_notifier(void)
+{
+}
+#endif
+#endif
diff --git a/sound/soc/msm/msmfalcon-internal.c b/sound/soc/msm/msmfalcon-internal.c
new file mode 100644
index 0000000..50efd69
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-internal.c
@@ -0,0 +1,2970 @@
+/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <sound/pcm_params.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "msm-audio-pinctrl.h"
+#include "msmfalcon-common.h"
+#include "../codecs/msm8x16/msm8x16-wcd.h"
+
+#define __CHIPSET__ "MSMFALCON "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define DEFAULT_MCLK_RATE 9600000
+#define NATIVE_MCLK_RATE 11289600
+
+#define WCD_MBHC_DEF_RLOADS 5
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+
+enum {
+	INT0_MI2S = 0,
+	INT1_MI2S,
+	INT2_MI2S,
+	INT3_MI2S,
+	INT4_MI2S,
+	INT5_MI2S,
+	INT6_MI2S,
+	INT_MI2S_MAX,
+};
+
+enum {
+	BT_SLIM7,
+	FM_SLIM8,
+	SLIM_MAX,
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+	{0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
+static struct afe_clk_set int_mi2s_clk[INT_MI2S_MAX] = {
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT0_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT1_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT2_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT3_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT4_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT5_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_INT6_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+};
+
+struct dev_config {
+	u32 sample_rate;
+	u32 bit_format;
+	u32 channels;
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config int_mi2s_cfg[] = {
+	[INT0_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[INT1_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[INT2_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[INT3_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[INT4_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[INT5_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[INT6_MI2S] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config bt_fm_cfg[] = {
+	[BT_SLIM7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[FM_SLIM8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static char const *int_mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+					   "KHZ_32", "KHZ_44P1", "KHZ_48",
+					   "KHZ_96", "KHZ_192"};
+static const char *const int_mi2s_ch_text[] = {"One", "Two"};
+static const char *const int_mi2s_tx_ch_text[] = {"One", "Two",
+						"Three", "Four"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE"};
+static const char *const loopback_mclk_text[] = {"DISABLE", "ENABLE"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_chs, int_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int0_mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_chs, int_mi2s_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int2_mi2s_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_chs, int_mi2s_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int3_mi2s_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_sample_rate, int_mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_chs, int_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int4_mi2s_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(int5_mi2s_tx_chs, int_mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(loopback_mclk_en, loopback_mclk_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+
+static int msm_dmic_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *kcontrol, int event);
+static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec, int enable,
+				      bool dapm);
+static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w,
+			      struct snd_kcontrol *kcontrol, int event);
+static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream);
+static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream);
+
+static struct wcd_mbhc_config *mbhc_cfg_ptr;
+
+static int int_mi2s_get_bit_format_val(int bit_format)
+{
+	int val = 0;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		val = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		val = 0;
+		break;
+	}
+	return val;
+}
+
+static int int_mi2s_get_bit_format(int val)
+{
+	int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+	switch (val) {
+	case 0:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	default:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return bit_fmt;
+}
+
+static int int_mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int port_id = 0;
+
+	if (strnstr(kcontrol->id.name, "INT0_MI2S", sizeof("INT0_MI2S")))
+		port_id = INT0_MI2S;
+	else if (strnstr(kcontrol->id.name, "INT2_MI2S", sizeof("INT2_MI2S")))
+		port_id = INT2_MI2S;
+	else if (strnstr(kcontrol->id.name, "INT3_MI2S", sizeof("INT3_MI2S")))
+		port_id = INT3_MI2S;
+	else if (strnstr(kcontrol->id.name, "INT4_MI2S", sizeof("INT4_MI2S")))
+		port_id = INT4_MI2S;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	return port_id;
+}
+
+static int int_mi2s_bit_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = int_mi2s_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		int_mi2s_get_bit_format_val(int_mi2s_cfg[ch_num].bit_format);
+
+	pr_debug("%s: int_mi2s[%d]_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, int_mi2s_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int int_mi2s_bit_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = int_mi2s_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	int_mi2s_cfg[ch_num].bit_format =
+		int_mi2s_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: int_mi2s[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, int_mi2s_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static inline int param_is_mask(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+			(p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+					     int n)
+{
+	return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n, unsigned int bit)
+{
+	if (bit >= SNDRV_MASK_MAX)
+		return;
+	if (param_is_mask(n)) {
+		struct snd_mask *m = param_to_mask(p, n);
+
+		m->bits[0] = 0;
+		m->bits[1] = 0;
+		m->bits[bit >> 5] |= (1 << (bit & 31));
+	}
+}
+
+static int int_mi2s_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int int_mi2s_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int int_mi2s_sample_rate_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = int_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	int_mi2s_cfg[idx].sample_rate =
+		int_mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__,
+		 idx, int_mi2s_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int int_mi2s_sample_rate_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = int_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		int_mi2s_get_sample_rate_val(int_mi2s_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_sample_rate = %d, item = %d\n", __func__,
+		 idx, int_mi2s_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int int_mi2s_ch_get(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = int_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: int_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, int_mi2s_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = int_mi2s_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int int_mi2s_ch_put(struct snd_kcontrol *kcontrol,
+			   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = int_mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	int_mi2s_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: int_mi2s_[%d]_ch  = %d\n", __func__,
+		 idx, int_mi2s_cfg[idx].channels);
+
+	return 1;
+}
+
+static const struct snd_soc_dapm_widget msm_int_dapm_widgets[] = {
+	SND_SOC_DAPM_SUPPLY_S("INT_MCLK0", -1, SND_SOC_NOPM, 0, 0,
+	msm_int_mclk0_event, SND_SOC_DAPM_POST_PMD),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Secondary Mic", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic1", msm_dmic_event),
+	SND_SOC_DAPM_MIC("Digital Mic2", msm_dmic_event),
+	SND_SOC_DAPM_MIC("Digital Mic3", msm_dmic_event),
+	SND_SOC_DAPM_MIC("Digital Mic4", msm_dmic_event),
+};
+
+static int msm_config_hph_compander_gpio(bool enable)
+{
+	int ret = 0;
+
+	pr_debug("%s: %s HPH Compander\n", __func__,
+		enable ? "Enable" : "Disable");
+
+	if (enable) {
+		ret = msm_gpioset_activate(CLIENT_WCD, "comp_gpio");
+		if (ret) {
+			pr_err("%s: gpio set cannot be activated %s\n",
+				__func__, "comp_gpio");
+			goto done;
+		}
+	} else {
+		ret = msm_gpioset_suspend(CLIENT_WCD, "comp_gpio");
+		if (ret) {
+			pr_err("%s: gpio set cannot be de-activated %s\n",
+				__func__, "comp_gpio");
+			goto done;
+		}
+	}
+
+done:
+	return ret;
+}
+
+static int is_ext_spk_gpio_support(struct platform_device *pdev,
+				   struct msm_asoc_mach_data *pdata)
+{
+	const char *spk_ext_pa = "qcom,msm-spk-ext-pa";
+
+	pr_debug("%s:Enter\n", __func__);
+
+	pdata->spk_ext_pa_gpio = of_get_named_gpio(pdev->dev.of_node,
+				spk_ext_pa, 0);
+
+	if (pdata->spk_ext_pa_gpio < 0) {
+		dev_dbg(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, spk_ext_pa);
+	} else {
+		if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) {
+			pr_err("%s: Invalid external speaker gpio: %d",
+				__func__, pdata->spk_ext_pa_gpio);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+static int enable_spk_ext_pa(struct snd_soc_codec *codec, int enable)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+	int ret;
+
+	if (!gpio_is_valid(pdata->spk_ext_pa_gpio)) {
+		pr_err("%s: Invalid gpio: %d\n", __func__,
+			pdata->spk_ext_pa_gpio);
+		return false;
+	}
+
+	pr_debug("%s: %s external speaker PA\n", __func__,
+		enable ? "Enable" : "Disable");
+
+	if (enable) {
+		ret = msm_gpioset_activate(CLIENT_WCD, "ext_spk_gpio");
+		if (ret) {
+			pr_err("%s: gpio set cannot be de-activated %s\n",
+					__func__, "ext_spk_gpio");
+			return ret;
+		}
+		gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable);
+	} else {
+		gpio_set_value_cansleep(pdata->spk_ext_pa_gpio, enable);
+		ret = msm_gpioset_suspend(CLIENT_WCD, "ext_spk_gpio");
+		if (ret) {
+			pr_err("%s: gpio set cannot be de-activated %s\n",
+					__func__, "ext_spk_gpio");
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int int_mi2s_get_idx_from_beid(int32_t be_id)
+{
+	int idx = 0;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_INT0_MI2S_RX:
+		idx = INT0_MI2S;
+		break;
+	case MSM_BACKEND_DAI_INT2_MI2S_TX:
+		idx = INT2_MI2S;
+		break;
+	case MSM_BACKEND_DAI_INT3_MI2S_TX:
+		idx = INT3_MI2S;
+		break;
+	case MSM_BACKEND_DAI_INT4_MI2S_RX:
+		idx = INT4_MI2S;
+		break;
+	case MSM_BACKEND_DAI_INT5_MI2S_TX:
+		idx = INT5_MI2S;
+		break;
+	default:
+		idx = INT0_MI2S;
+		break;
+	}
+
+	return idx;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	pr_debug("%s()\n", __func__);
+	rate->min = rate->max = 48000;
+	channels->min = channels->max = 2;
+
+	return 0;
+}
+
+static int int_mi2s_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	int idx;
+
+	pr_debug("%s: format = %d, rate = %d\n",
+		  __func__, params_format(params), params_rate(params));
+
+	switch (dai_link->be_id) {
+	case MSM_BACKEND_DAI_INT0_MI2S_RX:
+	case MSM_BACKEND_DAI_INT2_MI2S_TX:
+	case MSM_BACKEND_DAI_INT3_MI2S_TX:
+	case MSM_BACKEND_DAI_INT4_MI2S_RX:
+	case MSM_BACKEND_DAI_INT5_MI2S_TX:
+		idx = int_mi2s_get_idx_from_beid(dai_link->be_id);
+		rate->min = rate->max = int_mi2s_cfg[idx].sample_rate;
+		channels->min = channels->max =
+			int_mi2s_cfg[idx].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       int_mi2s_cfg[idx].bit_format);
+		break;
+	default:
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return 0;
+}
+
+static int msm_btfm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+
+	switch (dai_link->be_id) {
+	case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				bt_fm_cfg[BT_SLIM7].bit_format);
+		rate->min = rate->max = bt_fm_cfg[BT_SLIM7].sample_rate;
+		channels->min = channels->max =
+			bt_fm_cfg[BT_SLIM7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+		rate->min = rate->max = bt_fm_cfg[FM_SLIM8].sample_rate;
+		channels->min = channels->max =
+			bt_fm_cfg[FM_SLIM8].channels;
+		break;
+
+	default:
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return 0;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] =
+		(int_mi2s_cfg[INT5_MI2S].channels/2 - 1);
+	pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+				ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	int_mi2s_cfg[INT5_MI2S].channels =
+		roundup_pow_of_two(ucontrol->value.integer.value[0] + 2);
+
+	pr_debug("%s: msm_vi_feed_tx_ch = %d\n",
+		 __func__, int_mi2s_cfg[INT5_MI2S].channels);
+	return 1;
+}
+
+static int msm_int_enable_dig_cdc_clk(struct snd_soc_codec *codec,
+				      int enable, bool dapm)
+{
+	int ret = 0;
+	struct msm_asoc_mach_data *pdata = NULL;
+	int clk_freq_in_hz;
+	bool int_mclk0_freq_chg = false;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	pr_debug("%s: enable %d mclk ref counter %d\n",
+		   __func__, enable,
+		   atomic_read(&pdata->int_mclk0_rsc_ref));
+	if (enable) {
+		if (int_mi2s_cfg[INT0_MI2S].sample_rate ==
+				SAMPLING_RATE_44P1KHZ)
+			clk_freq_in_hz = NATIVE_MCLK_RATE;
+		else
+			clk_freq_in_hz = pdata->mclk_freq;
+
+		if (pdata->digital_cdc_core_clk.clk_freq_in_hz
+				!= clk_freq_in_hz)
+			int_mclk0_freq_chg = true;
+		if (!atomic_read(&pdata->int_mclk0_rsc_ref) ||
+				int_mclk0_freq_chg) {
+			cancel_delayed_work_sync(
+					&pdata->disable_int_mclk0_work);
+			mutex_lock(&pdata->cdc_int_mclk0_mutex);
+			if (atomic_read(&pdata->int_mclk0_enabled) == false ||
+				int_mclk0_freq_chg) {
+				pdata->digital_cdc_core_clk.clk_freq_in_hz =
+							clk_freq_in_hz;
+				pdata->digital_cdc_core_clk.enable = 1;
+				ret = afe_set_lpass_clock_v2(
+					AFE_PORT_ID_INT0_MI2S_RX,
+					&pdata->digital_cdc_core_clk);
+				if (ret < 0) {
+					pr_err("%s: failed to enable CCLK\n",
+							__func__);
+					mutex_unlock(
+						&pdata->cdc_int_mclk0_mutex);
+					return ret;
+				}
+				pr_debug("enabled digital codec core clk\n");
+				atomic_set(&pdata->int_mclk0_enabled, true);
+			}
+			mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+		}
+		atomic_inc(&pdata->int_mclk0_rsc_ref);
+	} else {
+		cancel_delayed_work_sync(&pdata->disable_int_mclk0_work);
+		mutex_lock(&pdata->cdc_int_mclk0_mutex);
+		if (atomic_read(&pdata->int_mclk0_enabled) == true) {
+			pdata->digital_cdc_core_clk.enable = 0;
+			ret = afe_set_lpass_clock_v2(
+				AFE_PORT_ID_INT0_MI2S_RX,
+				&pdata->digital_cdc_core_clk);
+			if (ret < 0)
+				pr_err("%s: failed to disable CCLK\n",
+						__func__);
+			atomic_set(&pdata->int_mclk0_enabled, false);
+		}
+		mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+	}
+	return ret;
+}
+
+static int loopback_mclk_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+
+static int loopback_mclk_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = -EINVAL;
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	pr_debug("%s: mclk_rsc_ref %d enable %ld\n",
+			__func__, atomic_read(&pdata->int_mclk0_rsc_ref),
+			ucontrol->value.integer.value[0]);
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		ret = msm_gpioset_activate(CLIENT_WCD, "int_pdm");
+		if (ret) {
+			pr_err("%s: failed to enable the pri gpios: %d\n",
+					__func__, ret);
+			break;
+		}
+		mutex_lock(&pdata->cdc_int_mclk0_mutex);
+		if ((!atomic_read(&pdata->int_mclk0_rsc_ref)) &&
+				(!atomic_read(&pdata->int_mclk0_enabled))) {
+			pdata->digital_cdc_core_clk.enable = 1;
+			ret = afe_set_lpass_clock_v2(
+				AFE_PORT_ID_INT0_MI2S_RX,
+				&pdata->digital_cdc_core_clk);
+			if (ret < 0) {
+				pr_err("%s: failed to enable the MCLK: %d\n",
+						__func__, ret);
+				mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+				ret = msm_gpioset_suspend(CLIENT_WCD,
+								"int_pdm");
+				if (ret)
+					pr_err("%s: failed to disable the pri gpios: %d\n",
+							__func__, ret);
+				break;
+			}
+			atomic_set(&pdata->int_mclk0_enabled, true);
+		}
+		mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+		atomic_inc(&pdata->int_mclk0_rsc_ref);
+		msm8x16_wcd_mclk_enable(codec, 1, true);
+		break;
+	case 0:
+		if (atomic_read(&pdata->int_mclk0_rsc_ref) <= 0)
+			break;
+		msm8x16_wcd_mclk_enable(codec, 0, true);
+		mutex_lock(&pdata->cdc_int_mclk0_mutex);
+		if ((!atomic_dec_return(&pdata->int_mclk0_rsc_ref)) &&
+				(atomic_read(&pdata->int_mclk0_enabled))) {
+			pdata->digital_cdc_core_clk.enable = 0;
+			ret = afe_set_lpass_clock_v2(
+				AFE_PORT_ID_INT0_MI2S_RX,
+				&pdata->digital_cdc_core_clk);
+			if (ret < 0) {
+				pr_err("%s: failed to disable the CCLK: %d\n",
+						__func__, ret);
+				mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+				break;
+			}
+			atomic_set(&pdata->int_mclk0_enabled, false);
+		}
+		mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+		ret = msm_gpioset_suspend(CLIENT_WCD, "int_pdm");
+		if (ret)
+			pr_err("%s: failed to disable the pri gpios: %d\n",
+					__func__, ret);
+		break;
+	default:
+		pr_err("%s: Unexpected input value\n", __func__);
+		break;
+	}
+	return ret;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	/*
+	 * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+	 * when used for BT_SCO use case. Return either Rx or Tx sample rate
+	 * value.
+	 */
+	switch (bt_fm_cfg[BT_SLIM7].sample_rate) {
+	case SAMPLING_RATE_48KHZ:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+	pr_debug("%s: sample rate = %d", __func__,
+		 bt_fm_cfg[BT_SLIM7].sample_rate);
+
+	return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 0:
+	default:
+		bt_fm_cfg[BT_SLIM7].sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	pr_debug("%s: sample rates: slim7_rx = %d, value = %d\n",
+		 __func__,
+		 bt_fm_cfg[BT_SLIM7].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+	SOC_ENUM_EXT("INT0_MI2S_RX Format", int0_mi2s_rx_format,
+		     int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+	SOC_ENUM_EXT("INT2_MI2S_TX Format", int2_mi2s_tx_format,
+		     int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+	SOC_ENUM_EXT("INT3_MI2S_TX Format", int3_mi2s_tx_format,
+		     int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+	SOC_ENUM_EXT("INT0_MI2S_RX SampleRate", int0_mi2s_rx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT2_MI2S_TX SampleRate", int2_mi2s_tx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT3_MI2S_TX SampleRate", int3_mi2s_tx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT0_MI2S_RX SampleRate", int0_mi2s_rx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT2_MI2S_TX SampleRate", int2_mi2s_tx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT3_MI2S_TX SampleRate", int3_mi2s_tx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT0_MI2S_RX Channels", int0_mi2s_rx_chs,
+			int_mi2s_ch_get, int_mi2s_ch_put),
+	SOC_ENUM_EXT("INT2_MI2S_TX Channels", int2_mi2s_tx_chs,
+			int_mi2s_ch_get, int_mi2s_ch_put),
+	SOC_ENUM_EXT("INT3_MI2S_TX Channels", int3_mi2s_tx_chs,
+			int_mi2s_ch_get, int_mi2s_ch_put),
+	SOC_ENUM_EXT("Loopback MCLK", loopback_mclk_en,
+		     loopback_mclk_get, loopback_mclk_put),
+	SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+			msm_bt_sample_rate_get,
+			msm_bt_sample_rate_put),
+};
+
+static const struct snd_kcontrol_new msm_swr_controls[] = {
+	SOC_ENUM_EXT("INT4_MI2S_RX Format", int4_mi2s_rx_format,
+		     int_mi2s_bit_format_get, int_mi2s_bit_format_put),
+	SOC_ENUM_EXT("INT4_MI2S_RX SampleRate", int4_mi2s_rx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT4_MI2S_RX SampleRate", int4_mi2s_rx_sample_rate,
+			int_mi2s_sample_rate_get,
+			int_mi2s_sample_rate_put),
+	SOC_ENUM_EXT("INT4_MI2S_RX Channels", int4_mi2s_rx_chs,
+			int_mi2s_ch_get, int_mi2s_ch_put),
+	SOC_ENUM_EXT("VI_FEED_TX Channels", int5_mi2s_tx_chs,
+		     msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+};
+
+static int msm_dmic_event(struct snd_soc_dapm_widget *w,
+			  struct snd_kcontrol *kcontrol, int event)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	pr_debug("%s: event = %d\n", __func__, event);
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		ret = msm_gpioset_activate(CLIENT_WCD, "dmic_gpio");
+		if (ret < 0) {
+			pr_err("%s: gpio set cannot be activated %sd",
+					__func__, "dmic_gpio");
+			return ret;
+		}
+		break;
+	case SND_SOC_DAPM_POST_PMD:
+		ret = msm_gpioset_suspend(CLIENT_WCD, "dmic_gpio");
+		if (ret < 0) {
+			pr_err("%s: gpio set cannot be de-activated %sd",
+					__func__, "dmic_gpio");
+			return ret;
+		}
+		break;
+	default:
+		pr_err("%s: invalid DAPM event %d\n", __func__, event);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int msm_int_mclk0_event(struct snd_soc_dapm_widget *w,
+			       struct snd_kcontrol *kcontrol, int event)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	int ret = 0;
+
+	pdata = snd_soc_card_get_drvdata(codec->component.card);
+	pr_debug("%s: event = %d\n", __func__, event);
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMD:
+		pr_debug("%s: mclk_res_ref = %d\n",
+			__func__, atomic_read(&pdata->int_mclk0_rsc_ref));
+		ret = msm_gpioset_suspend(CLIENT_WCD, "int_pdm");
+		if (ret < 0) {
+			pr_err("%s: gpio set cannot be de-activated %sd",
+					__func__, "int_pdm");
+			return ret;
+		}
+		if (atomic_read(&pdata->int_mclk0_rsc_ref) == 0) {
+			pr_debug("%s: disabling MCLK\n", __func__);
+			/* disable the codec mclk config*/
+			msm8x16_wcd_mclk_enable(codec, 0, true);
+			msm_int_enable_dig_cdc_clk(codec, 0, true);
+		}
+		break;
+	default:
+		pr_err("%s: invalid DAPM event %d\n", __func__, event);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int int_mi2s_get_port_id(int be_id)
+{
+	int afe_port_id;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_INT0_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_INT0_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_INT2_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_INT2_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_INT3_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_INT3_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_INT4_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_INT4_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_INT5_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_INT5_MI2S_TX;
+		break;
+	default:
+		pr_err("%s: Invalid be_id: %d\n", __func__, be_id);
+		afe_port_id = -EINVAL;
+	}
+
+	return afe_port_id;
+}
+
+static int int_mi2s_get_index(int port_id)
+{
+	int index;
+
+	switch (port_id) {
+	case AFE_PORT_ID_INT0_MI2S_RX:
+		index = INT0_MI2S;
+		break;
+	case AFE_PORT_ID_INT2_MI2S_TX:
+		index = INT2_MI2S;
+		break;
+	case AFE_PORT_ID_INT3_MI2S_TX:
+		index = INT3_MI2S;
+		break;
+	case AFE_PORT_ID_INT4_MI2S_RX:
+		index = INT4_MI2S;
+		break;
+	case AFE_PORT_ID_INT5_MI2S_TX:
+		index = INT5_MI2S;
+		break;
+	default:
+		pr_err("%s: Invalid port_id: %d\n", __func__, port_id);
+		index = -EINVAL;
+	}
+
+	return index;
+}
+
+static u32 get_int_mi2s_bits_per_sample(u32 bit_format)
+{
+	u32 bit_per_sample;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bit_per_sample = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bit_per_sample = 16;
+		break;
+	}
+
+	return bit_per_sample;
+}
+
+static void update_int_mi2s_clk_val(int idx, int stream)
+{
+	u32 bit_per_sample;
+
+	bit_per_sample =
+	    get_int_mi2s_bits_per_sample(int_mi2s_cfg[idx].bit_format);
+	int_mi2s_clk[idx].clk_freq_in_hz =
+	    (int_mi2s_cfg[idx].sample_rate * int_mi2s_cfg[idx].channels
+					* bit_per_sample);
+}
+
+static int int_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int port_id = 0;
+	int index;
+
+	port_id = int_mi2s_get_port_id(rtd->dai_link->be_id);
+	if (port_id < 0) {
+		dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+		ret = port_id;
+		goto done;
+	}
+	index = int_mi2s_get_index(port_id);
+	if (index < 0) {
+		dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+		ret = port_id;
+		goto done;
+	}
+	if (enable) {
+		update_int_mi2s_clk_val(index, substream->stream);
+		dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+			int_mi2s_clk[index].clk_freq_in_hz);
+	}
+
+	int_mi2s_clk[index].enable = enable;
+	ret = afe_set_lpass_clock_v2(port_id,
+				     &int_mi2s_clk[index]);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+			"%s: afe lpass clock failed for port 0x%x , err:%d\n",
+			__func__, port_id, ret);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_swr_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	ret = int_mi2s_set_sclk(substream, true);
+	if (ret < 0) {
+		pr_err("%s: failed to enable sclk %d\n",
+				__func__, ret);
+		return ret;
+	}
+	/* Enable the codec mclk config */
+	ret = msm_gpioset_activate(CLIENT_WCD, "swr_pin");
+	if (ret < 0) {
+		pr_err("%s: gpio set cannot be activated %sd",
+				__func__, "swr_pin");
+		return ret;
+	}
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static void msm_swr_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret;
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+			substream->name, substream->stream);
+
+	ret = int_mi2s_set_sclk(substream, false);
+	if (ret < 0)
+		pr_err("%s:clock disable failed; ret=%d\n", __func__,
+				ret);
+}
+
+static int msm_int_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_codec *codec = rtd->codec;
+	int ret = 0;
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+
+	ret = int_mi2s_set_sclk(substream, true);
+	if (ret < 0) {
+		pr_err("%s: failed to enable sclk %d\n",
+				__func__, ret);
+		return ret;
+	}
+	ret =  msm_int_enable_dig_cdc_clk(codec, 1, true);
+	if (ret < 0) {
+		pr_err("failed to enable mclk\n");
+		return ret;
+	}
+	/* Enable the codec mclk config */
+	ret = msm_gpioset_activate(CLIENT_WCD, "int_pdm");
+	if (ret < 0) {
+		pr_err("%s: gpio set cannot be activated %s\n",
+				__func__, "int_pdm");
+		return ret;
+	}
+	msm8x16_wcd_mclk_enable(codec, 1, true);
+	ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_CBS_CFS);
+	if (ret < 0)
+		pr_err("%s: set fmt cpu dai failed; ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static void msm_int_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_card *card = rtd->card;
+	struct msm_asoc_mach_data *pdata = snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+			substream->name, substream->stream);
+
+	ret = int_mi2s_set_sclk(substream, false);
+	if (ret < 0)
+		pr_err("%s:clock disable failed; ret=%d\n", __func__,
+				ret);
+	if (atomic_read(&pdata->int_mclk0_rsc_ref) > 0) {
+		atomic_dec(&pdata->int_mclk0_rsc_ref);
+		pr_debug("%s: decrementing mclk_res_ref %d\n",
+			 __func__,
+			 atomic_read(&pdata->int_mclk0_rsc_ref));
+	}
+}
+
+static void *def_msm_int_wcd_mbhc_cal(void)
+{
+	void *msm_int_wcd_cal;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_low, *btn_high;
+
+	msm_int_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+				WCD_MBHC_DEF_RLOADS), GFP_KERNEL);
+	if (!msm_int_wcd_cal)
+		return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(msm_int_wcd_cal)->X) = (Y))
+	S(v_hs_max, 1500);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal)->X) = (Y))
+	S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(msm_int_wcd_cal);
+	btn_low = btn_cfg->_v_btn_low;
+	btn_high = ((void *)&btn_cfg->_v_btn_low) +
+		(sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+	/*
+	 * In SW we are maintaining two sets of threshold register
+	 * one for current source and another for Micbias.
+	 * all btn_low corresponds to threshold for current source
+	 * all bt_high corresponds to threshold for Micbias
+	 * Below thresholds are based on following resistances
+	 * 0-70    == Button 0
+	 * 110-180 == Button 1
+	 * 210-290 == Button 2
+	 * 360-680 == Button 3
+	 */
+	btn_low[0] = 75;
+	btn_high[0] = 75;
+	btn_low[1] = 150;
+	btn_high[1] = 150;
+	btn_low[2] = 225;
+	btn_high[2] = 225;
+	btn_low[3] = 450;
+	btn_high[3] = 450;
+	btn_low[4] = 500;
+	btn_high[4] = 500;
+
+	return msm_int_wcd_cal;
+}
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_codec_get_dapm(codec);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = -ENOMEM;
+
+	pr_debug("%s(),dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+	snd_soc_add_codec_controls(codec, msm_snd_controls,
+			ARRAY_SIZE(msm_snd_controls));
+
+	snd_soc_add_codec_controls(codec, msm_common_snd_controls,
+			ARRAY_SIZE(msm_snd_controls));
+
+	snd_soc_dapm_new_controls(dapm, msm_int_dapm_widgets,
+			ARRAY_SIZE(msm_int_dapm_widgets));
+
+	snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Secondary Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+
+	snd_soc_dapm_ignore_suspend(dapm, "EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "HEADPHONE");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK_OUT");
+	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, "DMIC1");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC2");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC3");
+	snd_soc_dapm_ignore_suspend(dapm, "DMIC4");
+
+	snd_soc_dapm_sync(dapm);
+
+	msm8x16_wcd_spk_ext_pa_cb(enable_spk_ext_pa, codec);
+	msm8x16_wcd_hph_comp_cb(msm_config_hph_compander_gpio, codec);
+
+	mbhc_cfg_ptr->calibration = def_msm_int_wcd_mbhc_cal();
+	if (mbhc_cfg_ptr->calibration) {
+		ret = msm8x16_wcd_hs_detect(codec, mbhc_cfg_ptr);
+		if (ret) {
+			pr_err("%s: msm8x16_wcd_hs_detect failed\n", __func__);
+			kfree(mbhc_cfg_ptr->calibration);
+			return ret;
+		}
+	}
+	return 0;
+}
+
+static int msm_swr_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm =
+			snd_soc_codec_get_dapm(codec);
+
+	snd_soc_add_codec_controls(codec, msm_swr_controls,
+			ARRAY_SIZE(msm_swr_controls));
+
+	snd_soc_dapm_ignore_suspend(dapm, "AIF1_SWR Playback");
+	snd_soc_dapm_ignore_suspend(dapm, "VIfeed_SWR");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF1_SWR VI");
+	snd_soc_dapm_ignore_suspend(dapm, "VIINPUT_SWR");
+
+	snd_soc_dapm_sync(dapm);
+
+	return 0;
+}
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+	unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+	unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX]  = {159, 160, 161};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+					   tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	int ret;
+
+	dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+	if (ret) {
+		dev_err(rtd->dev,
+			"%s: failed to get BTFM codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto exit;
+	}
+
+	dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) be_id %d\n",
+		__func__, tx_ch_cnt, dai_link->be_id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+	if (ret)
+		dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+
+exit:
+	return ret;
+}
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+					    int slots)
+{
+	unsigned int slot_mask = 0;
+	int i, j;
+	unsigned int *slot_offset;
+
+	for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+		slot_offset = tdm_slot_offset[i];
+
+		for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+			if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+				slot_mask |=
+				(1 << ((slot_offset[j] * 8) / slot_width));
+			else
+				break;
+		}
+	}
+
+	return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+	int channels, slot_width, slots;
+	unsigned int slot_mask;
+	unsigned int *slot_offset;
+	int offset_channels = 0;
+	int i;
+
+	pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+	channels = params_channels(params);
+	switch (channels) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S32_LE:
+		case SNDRV_PCM_FORMAT_S24_LE:
+		case SNDRV_PCM_FORMAT_S16_LE:
+		/*
+		 * up to 8 channels HW config should
+		 * use 32 bit slot width for max support of
+		 * stream bit width. (slot_width > bit_width)
+		 */
+			slot_width = 32;
+			break;
+		default:
+			pr_err("%s: invalid param format 0x%x\n",
+				__func__, params_format(params));
+			return -EINVAL;
+		}
+		slots = 8;
+		slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+						    slot_width,
+						    slots);
+		if (!slot_mask) {
+			pr_err("%s: invalid slot_mask 0x%x\n",
+				__func__, slot_mask);
+			return -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s: invalid param channels %d\n",
+			__func__, channels);
+		return -EINVAL;
+	}
+	/* currently only supporting TDM_RX_0 and TDM_TX_0 */
+	switch (cpu_dai->id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		slot_offset = tdm_slot_offset[TDM_0];
+		break;
+	default:
+		pr_err("%s: dai id 0x%x not supported\n",
+			__func__, cpu_dai->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+		if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+			offset_channels++;
+		else
+			break;
+	}
+
+	if (offset_channels == 0) {
+		pr_err("%s: slot offset not supported, offset_channels %d\n",
+			__func__, offset_channels);
+		return -EINVAL;
+	}
+
+	if (channels > offset_channels) {
+		pr_err("%s: channels %d exceed offset_channels %d\n",
+			__func__, channels, offset_channels);
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+						  channels, slot_offset);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	} else {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+						  slot_offset, 0, NULL);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto end;
+		}
+	}
+end:
+	return ret;
+}
+
+static struct snd_soc_ops msm_tdm_be_ops = {
+	.hw_params = msm_tdm_snd_hw_params
+};
+
+static struct snd_soc_ops msm_wcn_ops = {
+	.hw_params = msm_wcn_hw_params,
+};
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+	.startup = msm_mi2s_snd_startup,
+	.shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+	.startup = msm_aux_pcm_snd_startup,
+	.shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_int_mi2s_be_ops = {
+	.startup = msm_int_mi2s_snd_startup,
+	.shutdown = msm_int_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_swr_mi2s_be_ops = {
+	.startup = msm_swr_mi2s_snd_startup,
+	.shutdown = msm_swr_mi2s_snd_shutdown,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_int_dai[] = {
+	/* FrontEnd DAI Links */
+	{/* hw:x,0 */
+		.name = MSM_DAILINK_NAME(Media1),
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name	= "MultiMedia1",
+		.platform_name  = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+	{/* hw:x,1 */
+		.name = MSM_DAILINK_NAME(Media2),
+		.stream_name = "MultiMedia2",
+		.cpu_dai_name   = "MultiMedia2",
+		.platform_name  = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+	},
+	{/* hw:x,2 */
+		.name = "VoiceMMode1",
+		.stream_name = "VoiceMMode1",
+		.cpu_dai_name = "VoiceMMode1",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_VOICEMMODE1,
+	},
+	{/* hw:x,3 */
+		.name = "MSM VoIP",
+		.stream_name = "VoIP",
+		.cpu_dai_name	= "VoIP",
+		.platform_name  = "msm-voip-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_VOIP,
+	},
+	{/* hw:x,4 */
+		.name = MSM_DAILINK_NAME(ULL),
+		.stream_name = "ULL",
+		.cpu_dai_name	= "MultiMedia3",
+		.platform_name  = "msm-pcm-dsp.2",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+	},
+	/* Hostless PCM purpose */
+	{/* hw:x,5 */
+		.name = "INT4 MI2S_RX Hostless",
+		.stream_name = "INT4 MI2S_RX Hostless",
+		.cpu_dai_name = "INT4_MI2S_RX_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		/* This dainlink has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,6 */
+		.name = "MSM AFE-PCM RX",
+		.stream_name = "AFE-PROXY RX",
+		.cpu_dai_name = "msm-dai-q6-dev.241",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.platform_name  = "msm-pcm-afe",
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{/* hw:x,7 */
+		.name = "MSM AFE-PCM TX",
+		.stream_name = "AFE-PROXY TX",
+		.cpu_dai_name = "msm-dai-q6-dev.240",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.platform_name  = "msm-pcm-afe",
+		.ignore_suspend = 1,
+	},
+	{/* hw:x,8 */
+		.name = MSM_DAILINK_NAME(Compress1),
+		.stream_name = "Compress1",
+		.cpu_dai_name	= "MultiMedia4",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+	},
+	{/* hw:x,9*/
+		.name = "AUXPCM Hostless",
+		.stream_name = "AUXPCM Hostless",
+		.cpu_dai_name   = "AUXPCM_HOSTLESS",
+		.platform_name  = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,10 */
+		.name = "SLIMBUS_1 Hostless",
+		.stream_name = "SLIMBUS_1 Hostless",
+		.cpu_dai_name = "SLIMBUS1_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,11 */
+		.name = "SLIMBUS_3 Hostless",
+		.stream_name = "SLIMBUS_3 Hostless",
+		.cpu_dai_name = "SLIMBUS3_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,12 */
+		.name = "SLIMBUS_4 Hostless",
+		.stream_name = "SLIMBUS_4 Hostless",
+		.cpu_dai_name = "SLIMBUS4_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,13 */
+		.name = MSM_DAILINK_NAME(LowLatency),
+		.stream_name = "MultiMedia5",
+		.cpu_dai_name   = "MultiMedia5",
+		.platform_name  = "msm-pcm-dsp.1",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+	},
+	/* LSM FE */
+	{/* hw:x,14 */
+		.name = "Listen 1 Audio Service",
+		.stream_name = "Listen 1 Audio Service",
+		.cpu_dai_name = "LSM1",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM1,
+	},
+	{/* hw:x,15 */
+		.name = MSM_DAILINK_NAME(Compress2),
+		.stream_name = "Compress2",
+		.cpu_dai_name   = "MultiMedia7",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+	},
+	{/* hw:x,16 */
+		.name = MSM_DAILINK_NAME(Compress3),
+		.stream_name = "Compress3",
+		.cpu_dai_name	= "MultiMedia10",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+	},
+	{/* hw:x,17 */
+		.name = MSM_DAILINK_NAME(ULL_NOIRQ),
+		.stream_name = "MM_NOIRQ",
+		.cpu_dai_name	= "MultiMedia8",
+		.platform_name  = "msm-pcm-dsp-noirq",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+	},
+	{/* hw:x,18 */
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name	= "HDMI_HOSTLESS",
+		.platform_name  = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,19 */
+		.name = "VoiceMMode2",
+		.stream_name = "VoiceMMode2",
+		.cpu_dai_name = "VoiceMMode2",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_VOICEMMODE2,
+	},
+	{/* hw:x,20 */
+		.name = "Listen 2 Audio Service",
+		.stream_name = "Listen 2 Audio Service",
+		.cpu_dai_name = "LSM2",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM2,
+	},
+	{/* hw:x,21 */
+		.name = "Listen 3 Audio Service",
+		.stream_name = "Listen 3 Audio Service",
+		.cpu_dai_name = "LSM3",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM3,
+	},
+	{/* hw:x,22 */
+		.name = "Listen 4 Audio Service",
+		.stream_name = "Listen 4 Audio Service",
+		.cpu_dai_name = "LSM4",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM4,
+	},
+	{/* hw:x,23 */
+		.name = "Listen 5 Audio Service",
+		.stream_name = "Listen 5 Audio Service",
+		.cpu_dai_name = "LSM5",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM5,
+	},
+	{/* hw:x,24 */
+		.name = "Listen 6 Audio Service",
+		.stream_name = "Listen 6 Audio Service",
+		.cpu_dai_name = "LSM6",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM6
+	},
+	{/* hw:x,25 */
+		.name = "Listen 7 Audio Service",
+		.stream_name = "Listen 7 Audio Service",
+		.cpu_dai_name = "LSM7",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM7,
+	},
+	{/* hw:x,26 */
+		.name = "Listen 8 Audio Service",
+		.stream_name = "Listen 8 Audio Service",
+		.cpu_dai_name = "LSM8",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.be_id = MSM_FRONTEND_DAI_LSM8,
+	},
+	{/* hw:x,27 */
+		.name = MSM_DAILINK_NAME(Media9),
+		.stream_name = "MultiMedia9",
+		.cpu_dai_name	= "MultiMedia9",
+		.platform_name  = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+	},
+	{/* hw:x,28 */
+		.name = MSM_DAILINK_NAME(Compress4),
+		.stream_name = "Compress4",
+		.cpu_dai_name	= "MultiMedia11",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+	},
+	{/* hw:x,29 */
+		.name = MSM_DAILINK_NAME(Compress5),
+		.stream_name = "Compress5",
+		.cpu_dai_name	= "MultiMedia12",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+	},
+	{/* hw:x,30 */
+		.name = MSM_DAILINK_NAME(Compress6),
+		.stream_name = "Compress6",
+		.cpu_dai_name	= "MultiMedia13",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+	},
+	{/* hw:x,31 */
+		.name = MSM_DAILINK_NAME(Compress7),
+		.stream_name = "Compress7",
+		.cpu_dai_name	= "MultiMedia14",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+	},
+	{/* hw:x,32 */
+		.name = MSM_DAILINK_NAME(Compress8),
+		.stream_name = "Compress8",
+		.cpu_dai_name	= "MultiMedia15",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+	},
+	{/* hw:x,33 */
+		.name = MSM_DAILINK_NAME(Compress9),
+		.stream_name = "Compress9",
+		.cpu_dai_name	= "MultiMedia16",
+		.platform_name  = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dai link has playback support */
+		.be_id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+	},
+	{/* hw:x,34 */
+		.name = "SLIMBUS_8 Hostless",
+		.stream_name = "SLIMBUS8_HOSTLESS Capture",
+		.cpu_dai_name = "SLIMBUS8_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,35 */
+		.name = LPASS_BE_INT5_MI2S_TX,
+		.stream_name = "INT5_mi2s Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.12",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "msm_swr_codec",
+		.codec_dai_name = "msm_swr_vifeedback",
+		.be_id = MSM_BACKEND_DAI_INT5_MI2S_TX,
+		.be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+		.ops = &msm_swr_mi2s_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.dpcm_capture = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{/* hw:x,36 */
+		.name = "Primary MI2S_RX Hostless",
+		.stream_name = "Primary MI2S_RX Hostless",
+		.cpu_dai_name = "PRI_MI2S_RX_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		/* This dainlink has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,37 */
+		.name = "Secondary MI2S_RX Hostless",
+		.stream_name = "Secondary MI2S_RX Hostless",
+		.cpu_dai_name = "SEC_MI2S_RX_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		/* This dainlink has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,38 */
+		.name = "Tertiary MI2S_RX Hostless",
+		.stream_name = "Tertiary MI2S_RX Hostless",
+		.cpu_dai_name = "TERT_MI2S_RX_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		/* This dainlink has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{/* hw:x,39 */
+		.name = "INT0 MI2S_RX Hostless",
+		.stream_name = "INT0 MI2S_RX Hostless",
+		.cpu_dai_name = "INT0_MI2S_RX_HOSTLESS",
+		.platform_name	= "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		/* This dainlink has MI2S support */
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	/* Backend I2S DAI Links */
+	{
+		.name = LPASS_BE_INT0_MI2S_RX,
+		.stream_name = "INT0 MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.7",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "cajon_codec",
+		.codec_dai_name = "msm8x16_wcd_i2s_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE |
+			ASYNC_DPCM_SND_SOC_HW_PARAMS,
+		.be_id = MSM_BACKEND_DAI_INT0_MI2S_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+		.ops = &msm_int_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_INT4_MI2S_RX,
+		.stream_name = "INT4 MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.11",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm_swr_codec",
+		.codec_dai_name = "msm_swr_i2s_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_INT4_MI2S_RX,
+		.init = &msm_swr_audrx_init,
+		.be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+		.ops = &msm_swr_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_INT2_MI2S_TX,
+		.stream_name = "INT2 MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.9",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "cajon_codec",
+		.codec_dai_name = "msm8x16_wcd_i2s_tx2",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE |
+			ASYNC_DPCM_SND_SOC_HW_PARAMS,
+		.be_id = MSM_BACKEND_DAI_INT2_MI2S_TX,
+		.be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+		.ops = &msm_int_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_INT3_MI2S_TX,
+		.stream_name = "INT3 MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.10",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "cajon_codec",
+		.codec_dai_name = "msm8x16_wcd_i2s_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE |
+			ASYNC_DPCM_SND_SOC_HW_PARAMS,
+		.be_id = MSM_BACKEND_DAI_INT3_MI2S_TX,
+		.be_hw_params_fixup = int_mi2s_be_hw_params_fixup,
+		.ops = &msm_int_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_AFE_PCM_RX,
+		.stream_name = "AFE Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.224",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_AFE_PCM_TX,
+		.stream_name = "AFE Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.225",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_AFE_PCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music 2 BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
+		.stream_name = "Voice2 Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.platform_name = "msm-pcm-routing",
+		.codec_name     = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_RX,
+		.stream_name = "USB Audio Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.28672",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_USB_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_TX,
+		.stream_name = "USB Audio Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.28673",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_USB_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_RX_0,
+		.stream_name = "Primary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36864",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_TX_0,
+		.stream_name = "Primary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36865",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_RX_0,
+		.stream_name = "Secondary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36880",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_TX_0,
+		.stream_name = "Secondary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36881",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_RX_0,
+		.stream_name = "Tertiary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36896",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_TX_0,
+		.stream_name = "Tertiary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36897",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_RX_0,
+		.stream_name = "Quaternary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36912",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_TX_0,
+		.stream_name = "Quaternary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36913",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+	{
+		.name = LPASS_BE_PRI_MI2S_RX,
+		.stream_name = "Primary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_MI2S_TX,
+		.stream_name = "Primary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_RX,
+		.stream_name = "Secondary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_TX,
+		.stream_name = "Secondary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_RX,
+		.stream_name = "Tertiary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_TX,
+		.stream_name = "Tertiary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_RX,
+		.stream_name = "Quaternary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_TX,
+		.stream_name = "Quaternary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+	/* Primary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_AUXPCM_RX,
+		.stream_name = "AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_AUXPCM_TX,
+		.stream_name = "AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Secondary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_SEC_AUXPCM_RX,
+		.stream_name = "Sec AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SEC_AUXPCM_TX,
+		.stream_name = "Sec AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Tertiary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_TERT_AUXPCM_RX,
+		.stream_name = "Tert AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_TERT_AUXPCM_TX,
+		.stream_name = "Tert AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Quaternary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_RX,
+		.stream_name = "Quat AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_TX,
+		.stream_name = "Quat AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_common_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+};
+
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_7_RX,
+		.stream_name = "Slimbus7 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16398",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		/* BT codec driver determines capabilities based on
+		 * dai name, bt codecdai name should always contains
+		 * supported usecase information
+		 */
+		.codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+		.be_hw_params_fixup = msm_btfm_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_7_TX,
+		.stream_name = "Slimbus7 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16399",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_bt_sco_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+		.be_hw_params_fixup = msm_btfm_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_8_TX,
+		.stream_name = "Slimbus8 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16401",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_fm_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.be_id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+		.be_hw_params_fixup = msm_btfm_be_hw_params_fixup,
+		.init = &msm_wcn_init,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_int_dai_links[
+ARRAY_SIZE(msm_int_dai) +
+ARRAY_SIZE(msm_mi2s_be_dai_links) +
+ARRAY_SIZE(msm_auxpcm_be_dai_links)+
+ARRAY_SIZE(msm_wcn_be_dai_links)];
+
+static struct snd_soc_card msmfalcon_card = {
+	/* snd_soc_card_msmfalcon */
+	.name		= "msmfalcon-snd-card",
+	.dai_link	= msm_int_dai,
+	.num_links	= ARRAY_SIZE(msm_int_dai),
+};
+
+static void msm_disable_int_mclk0(struct work_struct *work)
+{
+	struct msm_asoc_mach_data *pdata = NULL;
+	struct delayed_work *dwork;
+	int ret = 0;
+
+	dwork = to_delayed_work(work);
+	pdata = container_of(dwork, struct msm_asoc_mach_data,
+			disable_int_mclk0_work);
+	mutex_lock(&pdata->cdc_int_mclk0_mutex);
+	pr_debug("%s: mclk_enabled %d mclk_rsc_ref %d\n", __func__,
+			atomic_read(&pdata->int_mclk0_enabled),
+			atomic_read(&pdata->int_mclk0_rsc_ref));
+
+	if (atomic_read(&pdata->int_mclk0_enabled) == true
+			&& atomic_read(&pdata->int_mclk0_rsc_ref) == 0) {
+		pr_debug("Disable the mclk\n");
+		pdata->digital_cdc_core_clk.enable = 0;
+		ret = afe_set_lpass_clock_v2(
+			AFE_PORT_ID_INT0_MI2S_RX,
+			&pdata->digital_cdc_core_clk);
+		if (ret < 0)
+			pr_err("%s failed to disable the CCLK\n", __func__);
+		atomic_set(&pdata->int_mclk0_enabled, false);
+	}
+	mutex_unlock(&pdata->cdc_int_mclk0_mutex);
+}
+
+static void msm_int_dt_parse_cap_info(struct platform_device *pdev,
+				      struct msm_asoc_mach_data *pdata)
+{
+	const char *ext1_cap = "qcom,msm-micbias1-ext-cap";
+	const char *ext2_cap = "qcom,msm-micbias2-ext-cap";
+
+	pdata->micbias1_cap_mode =
+		(of_property_read_bool(pdev->dev.of_node, ext1_cap) ?
+		 MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+
+	pdata->micbias2_cap_mode =
+		(of_property_read_bool(pdev->dev.of_node, ext2_cap) ?
+		 MICBIAS_EXT_BYP_CAP : MICBIAS_NO_EXT_BYP_CAP);
+}
+
+static struct snd_soc_card *msm_int_populate_sndcard_dailinks(
+						struct device *dev)
+{
+	struct snd_soc_card *card = &msmfalcon_card;
+	struct snd_soc_dai_link *dailink;
+	int len1;
+
+	card->name = dev_name(dev);
+	len1 = ARRAY_SIZE(msm_int_dai);
+	memcpy(msm_int_dai_links, msm_int_dai, sizeof(msm_int_dai));
+	dailink = msm_int_dai_links;
+	if (of_property_read_bool(dev->of_node,
+				  "qcom,mi2s-audio-intf")) {
+		memcpy(dailink + len1,
+		       msm_mi2s_be_dai_links,
+		       sizeof(msm_mi2s_be_dai_links));
+		len1 += ARRAY_SIZE(msm_mi2s_be_dai_links);
+	}
+	if (of_property_read_bool(dev->of_node,
+				  "qcom,auxpcm-audio-intf")) {
+		memcpy(dailink + len1,
+		       msm_auxpcm_be_dai_links,
+		       sizeof(msm_auxpcm_be_dai_links));
+		len1 += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+	}
+	if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+		dev_dbg(dev, "%s(): WCN BTFM support present\n",
+				__func__);
+		memcpy(dailink + len1,
+		       msm_wcn_be_dai_links,
+		       sizeof(msm_wcn_be_dai_links));
+		len1 += ARRAY_SIZE(msm_wcn_be_dai_links);
+	}
+	card->dai_link = dailink;
+	card->num_links = len1;
+	return card;
+}
+
+static int msm_internal_init(struct platform_device *pdev,
+			     struct msm_asoc_mach_data *pdata,
+			     struct snd_soc_card *card)
+{
+	const char *type = NULL;
+	const char *hs_micbias_type = "qcom,msm-hs-micbias-type";
+	int ret;
+
+	ret = is_ext_spk_gpio_support(pdev, pdata);
+	if (ret < 0)
+		dev_dbg(&pdev->dev,
+			"%s: doesn't support external speaker pa\n",
+			__func__);
+
+	ret = of_property_read_string(pdev->dev.of_node,
+				      hs_micbias_type, &type);
+	if (ret) {
+		dev_err(&pdev->dev, "%s: missing %s in dt node\n",
+			__func__, hs_micbias_type);
+		goto err;
+	}
+	if (!strcmp(type, "external")) {
+		dev_dbg(&pdev->dev, "Headset is using external micbias\n");
+		mbhc_cfg_ptr->hs_ext_micbias = true;
+	} else {
+		dev_dbg(&pdev->dev, "Headset is using internal micbias\n");
+		mbhc_cfg_ptr->hs_ext_micbias = false;
+	}
+
+	/* initialize the int_mclk0 */
+	pdata->digital_cdc_core_clk.clk_set_minor_version =
+			AFE_API_VERSION_I2S_CONFIG;
+	pdata->digital_cdc_core_clk.clk_id =
+			Q6AFE_LPASS_CLK_ID_INT_MCLK_0;
+	pdata->digital_cdc_core_clk.clk_freq_in_hz =
+			pdata->mclk_freq;
+	pdata->digital_cdc_core_clk.clk_attri =
+			Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO;
+	pdata->digital_cdc_core_clk.clk_root =
+			Q6AFE_LPASS_CLK_ROOT_DEFAULT;
+	pdata->digital_cdc_core_clk.enable = 1;
+
+	/* Initialize loopback mode to false */
+	pdata->lb_mode = false;
+
+	msm_int_dt_parse_cap_info(pdev, pdata);
+
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, pdata);
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret)
+		goto err;
+	/* initialize timer */
+	INIT_DELAYED_WORK(&pdata->disable_int_mclk0_work,
+			  msm_disable_int_mclk0);
+	mutex_init(&pdata->cdc_int_mclk0_mutex);
+	atomic_set(&pdata->int_mclk0_rsc_ref, 0);
+	atomic_set(&pdata->int_mclk0_enabled, false);
+
+	dev_info(&pdev->dev, "%s: default codec configured\n", __func__);
+
+	return 0;
+err:
+	return ret;
+}
+
+/**
+ * msm_int_cdc_init - internal codec machine specific init.
+ *
+ * @pdev: platform device handle
+ * @pdata: private data of machine driver
+ * @card: sound card pointer reference
+ * @mbhc_cfg: MBHC config reference
+ *
+ * Returns 0.
+ */
+int msm_int_cdc_init(struct platform_device *pdev,
+		     struct msm_asoc_mach_data *pdata,
+		     struct snd_soc_card **card,
+		     struct wcd_mbhc_config *mbhc_cfg)
+{
+	mbhc_cfg_ptr = mbhc_cfg;
+
+	*card = msm_int_populate_sndcard_dailinks(&pdev->dev);
+	msm_internal_init(pdev, pdata, *card);
+	return 0;
+}
+EXPORT_SYMBOL(msm_int_cdc_init);
diff --git a/sound/soc/msm/msmfalcon-internal.h b/sound/soc/msm/msmfalcon-internal.h
new file mode 100644
index 0000000..e5e3e7c
--- /dev/null
+++ b/sound/soc/msm/msmfalcon-internal.h
@@ -0,0 +1,32 @@
+/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __MSMFALCON_INTERNAL
+#define __MSMFALCON_INTERNAL
+
+#include <sound/soc.h>
+
+#ifdef CONFIG_SND_SOC_INT_CODEC
+int msm_int_cdc_init(struct platform_device *pdev,
+		     struct msm_asoc_mach_data *pdata,
+		     struct snd_soc_card **card,
+		     struct wcd_mbhc_config *mbhc_cfg);
+#else
+int msm_int_cdc_init(struct platform_device *pdev,
+		     struct msm_asoc_mach_data *pdata,
+		     struct snd_soc_card **card,
+		     struct wcd_mbhc_config *mbhc_cfg)
+{
+	return 0;
+}
+#endif
+#endif
diff --git a/sound/soc/msm/msmskunk.c b/sound/soc/msm/msmskunk.c
new file mode 100644
index 0000000..4759235
--- /dev/null
+++ b/sound/soc/msm/msmskunk.c
@@ -0,0 +1,6268 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/of_device.h>
+#include <linux/mfd/msm-cdc-pinctrl.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/jack.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6core.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <device_event.h>
+#include <linux/qdsp6v2/audio_notifier.h>
+#include "qdsp6v2/msm-pcm-routing-v2.h"
+#include "../codecs/wcd934x/wcd934x.h"
+#include "../codecs/wcd934x/wcd934x-mbhc.h"
+#include "../codecs/wsa881x.h"
+
+#define DRV_NAME "msmskunk-asoc-snd"
+
+#define __CHIPSET__ "MSMSKUNK "
+#define MSM_DAILINK_NAME(name) (__CHIPSET__#name)
+
+#define SAMPLING_RATE_8KHZ      8000
+#define SAMPLING_RATE_11P025KHZ 11025
+#define SAMPLING_RATE_16KHZ     16000
+#define SAMPLING_RATE_22P05KHZ  22050
+#define SAMPLING_RATE_32KHZ     32000
+#define SAMPLING_RATE_44P1KHZ   44100
+#define SAMPLING_RATE_48KHZ     48000
+#define SAMPLING_RATE_88P2KHZ   88200
+#define SAMPLING_RATE_96KHZ     96000
+#define SAMPLING_RATE_176P4KHZ  176400
+#define SAMPLING_RATE_192KHZ    192000
+#define SAMPLING_RATE_352P8KHZ  352800
+#define SAMPLING_RATE_384KHZ    384000
+
+#define WCD9XXX_MBHC_DEF_BUTTONS    8
+#define WCD9XXX_MBHC_DEF_RLOADS     5
+#define CODEC_EXT_CLK_RATE          9600000
+#define ADSP_STATE_READY_TIMEOUT_MS 3000
+#define DEV_NAME_STR_LEN            32
+
+#define WSA8810_NAME_1 "wsa881x.20170211"
+#define WSA8810_NAME_2 "wsa881x.20170212"
+
+#define WCN_CDC_SLIM_RX_CH_MAX 2
+#define WCN_CDC_SLIM_TX_CH_MAX 3
+
+#define TDM_CHANNEL_MAX 8
+#define TDM_SLOT_OFFSET_MAX 8
+
+#define MSM_HIFI_ON 1
+
+enum {
+	SLIM_RX_0 = 0,
+	SLIM_RX_1,
+	SLIM_RX_2,
+	SLIM_RX_3,
+	SLIM_RX_4,
+	SLIM_RX_5,
+	SLIM_RX_6,
+	SLIM_RX_7,
+	SLIM_RX_MAX,
+};
+
+enum {
+	SLIM_TX_0 = 0,
+	SLIM_TX_1,
+	SLIM_TX_2,
+	SLIM_TX_3,
+	SLIM_TX_4,
+	SLIM_TX_5,
+	SLIM_TX_6,
+	SLIM_TX_7,
+	SLIM_TX_8,
+	SLIM_TX_MAX,
+};
+
+enum {
+	PRIM_MI2S = 0,
+	SEC_MI2S,
+	TERT_MI2S,
+	QUAT_MI2S,
+	MI2S_MAX,
+};
+
+enum {
+	PRIM_AUX_PCM = 0,
+	SEC_AUX_PCM,
+	TERT_AUX_PCM,
+	QUAT_AUX_PCM,
+	AUX_PCM_MAX,
+};
+
+enum {
+	PCM_I2S_SEL_PRIM = 0,
+	PCM_I2S_SEL_SEC,
+	PCM_I2S_SEL_TERT,
+	PCM_I2S_SEL_QUAT,
+	PCM_I2S_SEL_MAX,
+};
+
+struct mi2s_aux_pcm_common_conf {
+	struct mutex lock;
+	void *pcm_i2s_sel_vt_addr;
+};
+
+struct mi2s_conf {
+	struct mutex lock;
+	u32 ref_cnt;
+	u32 msm_is_mi2s_master;
+};
+
+struct auxpcm_conf {
+	struct mutex lock;
+	u32 ref_cnt;
+};
+
+struct dev_config {
+	u32 sample_rate;
+	u32 bit_format;
+	u32 channels;
+};
+
+enum {
+	DP_RX_IDX = 0,
+	EXT_DISP_RX_IDX_MAX,
+};
+
+struct msm_wsa881x_dev_info {
+	struct device_node *of_node;
+	u32 index;
+};
+
+struct msm_asoc_mach_data {
+	u32 mclk_freq;
+	int us_euro_gpio; /* used by gpio driver API */
+	struct device_node *us_euro_gpio_p; /* used by pinctrl API */
+	struct device_node *hph_en1_gpio_p; /* used by pinctrl API */
+	struct device_node *hph_en0_gpio_p; /* used by pinctrl API */
+	struct snd_info_entry *codec_root;
+};
+
+struct msm_asoc_wcd93xx_codec {
+	void* (*get_afe_config_fn)(struct snd_soc_codec *codec,
+				   enum afe_config_type config_type);
+	void (*mbhc_hs_detect_exit)(struct snd_soc_codec *codec);
+};
+
+enum {
+	TDM_0 = 0,
+	TDM_1,
+	TDM_2,
+	TDM_3,
+	TDM_4,
+	TDM_5,
+	TDM_6,
+	TDM_7,
+	TDM_PORT_MAX,
+};
+
+enum {
+	TDM_PRI = 0,
+	TDM_SEC,
+	TDM_TERT,
+	TDM_QUAT,
+	TDM_INTERFACE_MAX,
+};
+
+struct tdm_port {
+	u32 mode;
+	u32 channel;
+};
+
+/* TDM default config */
+static struct dev_config tdm_rx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+	{ /* PRI TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* SEC TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* TERT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	},
+	{ /* QUAT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* RX_7 */
+	}
+};
+
+/* TDM default config */
+static struct dev_config tdm_tx_cfg[TDM_INTERFACE_MAX][TDM_PORT_MAX] = {
+	{ /* PRI TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* SEC TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* TERT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	},
+	{ /* QUAT TDM */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_0 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_1 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_2 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_3 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_4 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_5 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_6 */
+		{SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1}, /* TX_7 */
+	}
+};
+
+/*TDM default offset currently only supporting TDM_RX_0 and TDM_TX_0 */
+static unsigned int tdm_slot_offset[TDM_PORT_MAX][TDM_SLOT_OFFSET_MAX] = {
+	{0, 4, 8, 12, 16, 20, 24, 28},/* TX_0 | RX_0 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_1 | RX_1 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_2 | RX_2 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_3 | RX_3 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_4 | RX_4 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_5 | RX_5 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_6 | RX_6 */
+	{AFE_SLOT_MAPPING_OFFSET_INVALID},/* TX_7 | RX_7 */
+};
+
+/* Default configuration of slimbus channels */
+static struct dev_config slim_rx_cfg[] = {
+	[SLIM_RX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_RX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config slim_tx_cfg[] = {
+	[SLIM_TX_0] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_1] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_2] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_3] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_4] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_5] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_6] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_7] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SLIM_TX_8] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+
+/* Default configuration of external display BE */
+static struct dev_config ext_disp_rx_cfg[] = {
+	[DP_RX_IDX] =   {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config usb_rx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 2,
+};
+
+static struct dev_config usb_tx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 1,
+};
+
+static struct dev_config proxy_rx_cfg = {
+	.sample_rate = SAMPLING_RATE_48KHZ,
+	.bit_format = SNDRV_PCM_FORMAT_S16_LE,
+	.channels = 2,
+};
+
+/* Default configuration of MI2S channels */
+static struct dev_config mi2s_rx_cfg[] = {
+	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+	[QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 2},
+};
+
+static struct dev_config mi2s_tx_cfg[] = {
+	[PRIM_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_MI2S]  = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_MI2S] = {SAMPLING_RATE_48KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_rx_cfg[] = {
+	[PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_AUX_PCM]  = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static struct dev_config aux_pcm_tx_cfg[] = {
+	[PRIM_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[SEC_AUX_PCM]  = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[TERT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+	[QUAT_AUX_PCM] = {SAMPLING_RATE_8KHZ, SNDRV_PCM_FORMAT_S16_LE, 1},
+};
+
+static int msm_vi_feed_tx_ch = 2;
+static const char *const slim_rx_ch_text[] = {"One", "Two"};
+static const char *const slim_tx_ch_text[] = {"One", "Two", "Three", "Four",
+						"Five", "Six", "Seven",
+						"Eight"};
+static const char *const vi_feed_ch_text[] = {"One", "Two"};
+static char const *bit_format_text[] = {"S16_LE", "S24_LE", "S24_3LE",
+					  "S32_LE"};
+static char const *ext_disp_bit_format_text[] = {"S16_LE", "S24_LE"};
+static char const *slim_sample_rate_text[] = {"KHZ_8", "KHZ_16",
+					"KHZ_32", "KHZ_44P1", "KHZ_48",
+					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
+					"KHZ_192", "KHZ_352P8", "KHZ_384"};
+static char const *bt_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_48"};
+static const char *const usb_ch_text[] = {"One", "Two", "Three", "Four",
+					   "Five", "Six", "Seven",
+					   "Eight"};
+static char const *ch_text[] = {"Two", "Three", "Four", "Five",
+					"Six", "Seven", "Eight"};
+static char const *usb_sample_rate_text[] = {"KHZ_8", "KHZ_11P025",
+					"KHZ_16", "KHZ_22P05",
+					"KHZ_32", "KHZ_44P1", "KHZ_48",
+					"KHZ_88P2", "KHZ_96", "KHZ_176P4",
+					"KHZ_192", "KHZ_352P8", "KHZ_384"};
+static char const *ext_disp_sample_rate_text[] = {"KHZ_48", "KHZ_96",
+						  "KHZ_192"};
+static char const *tdm_ch_text[] = {"One", "Two", "Three", "Four",
+				    "Five", "Six", "Seven", "Eight"};
+static char const *tdm_bit_format_text[] = {"S16_LE", "S24_LE", "S32_LE"};
+static char const *tdm_sample_rate_text[] = {"KHZ_8", "KHZ_16", "KHZ_32",
+					     "KHZ_44P1", "KHZ_48", "KHZ_96",
+					     "KHZ_192", "KHZ_352P8", "KHZ_384"};
+static const char *const auxpcm_rate_text[] = {"KHZ_8", "KHZ_16"};
+static char const *mi2s_rate_text[] = {"KHZ_8", "KHZ_16",
+				      "KHZ_32", "KHZ_44P1", "KHZ_48",
+				      "KHZ_96", "KHZ_192"};
+static const char *const mi2s_ch_text[] = {"One", "Two", "Three", "Four",
+					   "Five", "Six", "Seven",
+					   "Eight"};
+static const char *const hifi_text[] = {"Off", "On"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_1_tx_chs, slim_tx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_chs, slim_rx_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_chs, usb_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(vi_feed_tx_chs, vi_feed_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(proxy_rx_chs, ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_format, bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_format, ext_disp_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_2_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_0_tx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_5_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(slim_6_rx_sample_rate, slim_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(bt_sample_rate, bt_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_rx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(usb_tx_sample_rate, usb_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(ext_disp_rx_sample_rate,
+				ext_disp_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_tx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_chs, tdm_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_format, tdm_bit_format_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tdm_rx_sample_rate, tdm_sample_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_rx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_aux_pcm_tx_sample_rate, auxpcm_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_sample_rate, mi2s_rate_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(prim_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(sec_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(tert_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_rx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(quat_mi2s_tx_chs, mi2s_ch_text);
+static SOC_ENUM_SINGLE_EXT_DECL(hifi_function, hifi_text);
+
+static struct platform_device *spdev;
+static int msm_hifi_control;
+
+static bool is_initial_boot;
+static bool codec_reg_done;
+static struct snd_soc_aux_dev *msm_aux_dev;
+static struct snd_soc_codec_conf *msm_codec_conf;
+static struct msm_asoc_wcd93xx_codec msm_codec_fn;
+
+static void *def_tavil_mbhc_cal(void);
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm);
+static int msm_wsa881x_init(struct snd_soc_component *component);
+
+/*
+ * Need to report LINEIN
+ * if R/L channel impedance is larger than 5K ohm
+ */
+static struct wcd_mbhc_config wcd_mbhc_cfg = {
+	.read_fw_bin = false,
+	.calibration = NULL,
+	.detect_extn_cable = true,
+	.mono_stero_detection = false,
+	.swap_gnd_mic = NULL,
+	.hs_ext_micbias = true,
+	.key_code[0] = KEY_MEDIA,
+	.key_code[1] = KEY_VOICECOMMAND,
+	.key_code[2] = KEY_VOLUMEUP,
+	.key_code[3] = KEY_VOLUMEDOWN,
+	.key_code[4] = 0,
+	.key_code[5] = 0,
+	.key_code[6] = 0,
+	.key_code[7] = 0,
+	.linein_th = 5000,
+	.moisture_en = true,
+};
+
+static struct snd_soc_dapm_route wcd_audio_paths[] = {
+	{"MIC BIAS1", NULL, "MCLK"},
+	{"MIC BIAS2", NULL, "MCLK"},
+	{"MIC BIAS3", NULL, "MCLK"},
+	{"MIC BIAS4", NULL, "MCLK"},
+};
+
+static struct afe_clk_set mi2s_clk[MI2S_MAX] = {
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_PRI_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_SEC_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_TER_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	},
+	{
+		AFE_API_VERSION_I2S_CONFIG,
+		Q6AFE_LPASS_CLK_ID_QUAD_MI2S_IBIT,
+		Q6AFE_LPASS_IBIT_CLK_1_P536_MHZ,
+		Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+		Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+		0,
+	}
+};
+
+static struct mi2s_aux_pcm_common_conf mi2s_auxpcm_conf[PCM_I2S_SEL_MAX];
+static struct mi2s_conf mi2s_intf_conf[MI2S_MAX];
+static struct auxpcm_conf auxpcm_intf_conf[AUX_PCM_MAX];
+
+static int slim_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val = 0;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 10;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int slim_get_sample_rate(int value)
+{
+	int sample_rate = 0;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int slim_get_bit_format_val(int bit_format)
+{
+	int val = 0;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		val = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		val = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		val = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		val = 0;
+		break;
+	}
+	return val;
+}
+
+static int slim_get_bit_format(int val)
+{
+	int bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+
+	switch (val) {
+	case 0:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		bit_fmt = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 3:
+		bit_fmt = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	default:
+		bit_fmt = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return bit_fmt;
+}
+
+static int slim_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int port_id = 0;
+
+	if (strnstr(kcontrol->id.name, "SLIM_0_RX", sizeof("SLIM_0_RX")))
+		port_id = SLIM_RX_0;
+	else if (strnstr(kcontrol->id.name, "SLIM_2_RX", sizeof("SLIM_2_RX")))
+		port_id = SLIM_RX_2;
+	else if (strnstr(kcontrol->id.name, "SLIM_5_RX", sizeof("SLIM_5_RX")))
+		port_id = SLIM_RX_5;
+	else if (strnstr(kcontrol->id.name, "SLIM_6_RX", sizeof("SLIM_6_RX")))
+		port_id = SLIM_RX_6;
+	else if (strnstr(kcontrol->id.name, "SLIM_0_TX", sizeof("SLIM_0_TX")))
+		port_id = SLIM_TX_0;
+	else if (strnstr(kcontrol->id.name, "SLIM_1_TX", sizeof("SLIM_1_TX")))
+		port_id = SLIM_TX_1;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	return port_id;
+}
+
+static int slim_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		slim_get_sample_rate_val(slim_rx_cfg[ch_num].sample_rate);
+
+	pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].sample_rate =
+		slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+		slim_get_sample_rate_val(slim_tx_cfg[ch_num].sample_rate);
+
+	pr_debug("%s: slim[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate = 0;
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	sample_rate = slim_get_sample_rate(ucontrol->value.enumerated.item[0]);
+	if (sample_rate == SAMPLING_RATE_44P1KHZ) {
+		pr_err("%s: Unsupported sample rate %d: for Tx path\n",
+			__func__, sample_rate);
+		return -EINVAL;
+	}
+	slim_tx_cfg[ch_num].sample_rate = sample_rate;
+
+	pr_debug("%s: slim[%d]_tx_sample_rate = %d, value = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+			slim_get_bit_format_val(slim_rx_cfg[ch_num].bit_format);
+
+	pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_rx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].bit_format =
+		slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_rx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_rx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_bit_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	ucontrol->value.enumerated.item[0] =
+			slim_get_bit_format_val(slim_tx_cfg[ch_num].bit_format);
+
+	pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int slim_tx_bit_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_tx_cfg[ch_num].bit_format =
+		slim_get_bit_format(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: slim[%d]_tx_bit_format = %d, ucontrol value = %d\n",
+		 __func__, ch_num, slim_tx_cfg[ch_num].bit_format,
+			ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_slim_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	pr_debug("%s: msm_slim_[%d]_rx_ch  = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].channels);
+	ucontrol->value.enumerated.item[0] = slim_rx_cfg[ch_num].channels - 1;
+
+	return 0;
+}
+
+static int msm_slim_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_rx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_slim_[%d]_rx_ch  = %d\n", __func__,
+		 ch_num, slim_rx_cfg[ch_num].channels);
+
+	return 1;
+}
+
+static int msm_slim_tx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	pr_debug("%s: msm_slim_[%d]_tx_ch  = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].channels);
+	ucontrol->value.enumerated.item[0] = slim_tx_cfg[ch_num].channels - 1;
+
+	return 0;
+}
+
+static int msm_slim_tx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ch_num = slim_get_port_idx(kcontrol);
+
+	if (ch_num < 0)
+		return ch_num;
+
+	slim_tx_cfg[ch_num].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_slim_[%d]_tx_ch = %d\n", __func__,
+		 ch_num, slim_tx_cfg[ch_num].channels);
+
+	return 1;
+}
+
+static int msm_vi_feed_tx_ch_get(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_vi_feed_tx_ch - 1;
+	pr_debug("%s: msm_vi_feed_tx_ch = %ld\n", __func__,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_vi_feed_tx_ch_put(struct snd_kcontrol *kcontrol,
+				 struct snd_ctl_elem_value *ucontrol)
+{
+	msm_vi_feed_tx_ch = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: msm_vi_feed_tx_ch = %d\n", __func__, msm_vi_feed_tx_ch);
+	return 1;
+}
+
+static int msm_bt_sample_rate_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	/*
+	 * Slimbus_7_Rx/Tx sample rate values should always be in sync (same)
+	 * when used for BT_SCO use case. Return either Rx or Tx sample rate
+	 * value.
+	 */
+	switch (slim_rx_cfg[SLIM_RX_7].sample_rate) {
+	case SAMPLING_RATE_48KHZ:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+	pr_debug("%s: sample rate = %d", __func__,
+		 slim_rx_cfg[SLIM_RX_7].sample_rate);
+
+	return 0;
+}
+
+static int msm_bt_sample_rate_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_16KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_48KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 0:
+	default:
+		slim_rx_cfg[SLIM_RX_7].sample_rate = SAMPLING_RATE_8KHZ;
+		slim_tx_cfg[SLIM_TX_7].sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	pr_debug("%s: sample rates: slim7_rx = %d, slim7_tx = %d, value = %d\n",
+		 __func__,
+		 slim_rx_cfg[SLIM_RX_7].sample_rate,
+		 slim_tx_cfg[SLIM_TX_7].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int usb_audio_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: usb_audio_rx_ch  = %d\n", __func__,
+		 usb_rx_cfg.channels);
+	ucontrol->value.integer.value[0] = usb_rx_cfg.channels - 1;
+	return 0;
+}
+
+static int usb_audio_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	usb_rx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: usb_audio_rx_ch = %d\n", __func__, usb_rx_cfg.channels);
+	return 1;
+}
+
+static int usb_audio_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+
+	switch (usb_rx_cfg.sample_rate) {
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 12;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 11;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 10;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_22P05KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_11P025KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: usb_audio_rx_sample_rate = %d\n", __func__,
+		 usb_rx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 12:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	case 11:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+		break;
+	case 2:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 1:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+		break;
+	case 0:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	default:
+		usb_rx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, usb_audio_rx_sample_rate = %d\n",
+		__func__, ucontrol->value.integer.value[0],
+		usb_rx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_rx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (usb_rx_cfg.bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ucontrol->value.integer.value[0] = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_rx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int usb_audio_rx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	case 2:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		usb_rx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: usb_audio_rx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_rx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return rc;
+}
+
+static int usb_audio_tx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: usb_audio_tx_ch  = %d\n", __func__,
+		 usb_tx_cfg.channels);
+	ucontrol->value.integer.value[0] = usb_tx_cfg.channels - 1;
+	return 0;
+}
+
+static int usb_audio_tx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	usb_tx_cfg.channels = ucontrol->value.integer.value[0] + 1;
+
+	pr_debug("%s: usb_audio_tx_ch = %d\n", __func__, usb_tx_cfg.channels);
+	return 1;
+}
+
+static int usb_audio_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+
+	switch (usb_tx_cfg.sample_rate) {
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 12;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 11;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 10;
+		break;
+	case SAMPLING_RATE_176P4KHZ:
+		sample_rate_val = 9;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 8;
+		break;
+	case SAMPLING_RATE_88P2KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_22P05KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_11P025KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	default:
+		sample_rate_val = 6;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: usb_audio_tx_sample_rate = %d\n", __func__,
+		 usb_tx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	switch (ucontrol->value.integer.value[0]) {
+	case 12:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	case 11:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 10:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 9:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_176P4KHZ;
+		break;
+	case 8:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 7:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_88P2KHZ;
+		break;
+	case 6:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_22P05KHZ;
+		break;
+	case 2:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 1:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_11P025KHZ;
+		break;
+	case 0:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	default:
+		usb_tx_cfg.sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, usb_audio_tx_sample_rate = %d\n",
+		__func__, ucontrol->value.integer.value[0],
+		usb_tx_cfg.sample_rate);
+	return 0;
+}
+
+static int usb_audio_tx_format_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	switch (usb_tx_cfg.bit_format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		ucontrol->value.integer.value[0] = 3;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		ucontrol->value.integer.value[0] = 2;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_tx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int usb_audio_tx_format_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 3:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	case 2:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_3LE;
+		break;
+	case 1:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		usb_tx_cfg.bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: usb_audio_tx_format = %d, ucontrol value = %ld\n",
+		 __func__, usb_tx_cfg.bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return rc;
+}
+
+static int ext_disp_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "Display Port RX",
+		    sizeof("Display Port RX"))) {
+		idx = DP_RX_IDX;
+	} else {
+		pr_err("%s: unsupported BE: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int ext_disp_rx_format_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ext_disp_rx_cfg[idx].bit_format) {
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+
+	pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+		 __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+		 ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int ext_disp_rx_format_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		ext_disp_rx_cfg[idx].bit_format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: ext_disp_rx[%d].format = %d, ucontrol value = %ld\n",
+		 __func__, idx, ext_disp_rx_cfg[idx].bit_format,
+		 ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int ext_disp_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.integer.value[0] =
+			ext_disp_rx_cfg[idx].channels - 2;
+
+	pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+		 idx, ext_disp_rx_cfg[idx].channels);
+
+	return 0;
+}
+
+static int ext_disp_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ext_disp_rx_cfg[idx].channels =
+			ucontrol->value.integer.value[0] + 2;
+
+	pr_debug("%s: ext_disp_rx[%d].ch = %d\n", __func__,
+		 idx, ext_disp_rx_cfg[idx].channels);
+	return 1;
+}
+
+static int ext_disp_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int sample_rate_val;
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ext_disp_rx_cfg[idx].sample_rate) {
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 2;
+		break;
+
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 1;
+		break;
+
+	case SAMPLING_RATE_48KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+
+	ucontrol->value.integer.value[0] = sample_rate_val;
+	pr_debug("%s: ext_disp_rx[%d].sample_rate = %d\n", __func__,
+		 idx, ext_disp_rx_cfg[idx].sample_rate);
+
+	return 0;
+}
+
+static int ext_disp_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = ext_disp_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	switch (ucontrol->value.integer.value[0]) {
+	case 2:
+		ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 1:
+		ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 0:
+	default:
+		ext_disp_rx_cfg[idx].sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+	pr_debug("%s: control value = %ld, ext_disp_rx[%d].sample_rate = %d\n",
+		 __func__, ucontrol->value.integer.value[0], idx,
+		 ext_disp_rx_cfg[idx].sample_rate);
+	return 0;
+}
+
+static int proxy_rx_ch_get(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: proxy_rx channels = %d\n",
+		 __func__, proxy_rx_cfg.channels);
+	ucontrol->value.integer.value[0] = proxy_rx_cfg.channels - 2;
+
+	return 0;
+}
+
+static int proxy_rx_ch_put(struct snd_kcontrol *kcontrol,
+			       struct snd_ctl_elem_value *ucontrol)
+{
+	proxy_rx_cfg.channels = ucontrol->value.integer.value[0] + 2;
+	pr_debug("%s: proxy_rx channels = %d\n",
+		 __func__, proxy_rx_cfg.channels);
+
+	return 1;
+}
+
+static int tdm_get_sample_rate(int value)
+{
+	int sample_rate = 0;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	case 7:
+		sample_rate = SAMPLING_RATE_352P8KHZ;
+		break;
+	case 8:
+		sample_rate = SAMPLING_RATE_384KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int aux_pcm_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 0:
+	default:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int tdm_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val = 0;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	case SAMPLING_RATE_352P8KHZ:
+		sample_rate_val = 7;
+		break;
+	case SAMPLING_RATE_384KHZ:
+		sample_rate_val = 8;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int aux_pcm_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_8KHZ:
+	default:
+		sample_rate_val = 0;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int tdm_get_port_idx(struct snd_kcontrol *kcontrol,
+			    struct tdm_port *port)
+{
+	if (port) {
+		if (strnstr(kcontrol->id.name, "PRI",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_PRI;
+		} else if (strnstr(kcontrol->id.name, "SEC",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_SEC;
+		} else if (strnstr(kcontrol->id.name, "TERT",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_TERT;
+		} else if (strnstr(kcontrol->id.name, "QUAT",
+		    sizeof(kcontrol->id.name))) {
+			port->mode = TDM_QUAT;
+		} else {
+			pr_err("%s: unsupported mode in: %s",
+				__func__, kcontrol->id.name);
+			return -EINVAL;
+		}
+
+		if (strnstr(kcontrol->id.name, "RX_0",
+		    sizeof(kcontrol->id.name)) ||
+		    strnstr(kcontrol->id.name, "TX_0",
+		    sizeof(kcontrol->id.name))) {
+			port->channel = TDM_0;
+		} else if (strnstr(kcontrol->id.name, "RX_1",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_1",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_1;
+		} else if (strnstr(kcontrol->id.name, "RX_2",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_2",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_2;
+		} else if (strnstr(kcontrol->id.name, "RX_3",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_3",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_3;
+		} else if (strnstr(kcontrol->id.name, "RX_4",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_4",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_4;
+		} else if (strnstr(kcontrol->id.name, "RX_5",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_5",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_5;
+		} else if (strnstr(kcontrol->id.name, "RX_6",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_6",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_6;
+		} else if (strnstr(kcontrol->id.name, "RX_7",
+			   sizeof(kcontrol->id.name)) ||
+			   strnstr(kcontrol->id.name, "TX_7",
+			   sizeof(kcontrol->id.name))) {
+			port->channel = TDM_7;
+		} else {
+			pr_err("%s: unsupported channel in: %s",
+				__func__, kcontrol->id.name);
+			return -EINVAL;
+		}
+	} else
+		return -EINVAL;
+	return 0;
+}
+
+static int tdm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+			tdm_rx_cfg[port.mode][port.channel].sample_rate);
+
+		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].sample_rate =
+			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_rx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_sample_rate_val(
+			tdm_tx_cfg[port.mode][port.channel].sample_rate);
+
+		pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].sample_rate =
+			tdm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_tx_sample_rate = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].sample_rate,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_get_format(int value)
+{
+	int format = 0;
+
+	switch (value) {
+	case 0:
+		format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	case 1:
+		format = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 2:
+		format = SNDRV_PCM_FORMAT_S32_LE;
+		break;
+	default:
+		format = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	return format;
+}
+
+static int tdm_get_format_val(int format)
+{
+	int value = 0;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		value = 0;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		value = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		value = 2;
+		break;
+	default:
+		value = 0;
+		break;
+	}
+	return value;
+}
+
+static int tdm_rx_format_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+				tdm_rx_cfg[port.mode][port.channel].bit_format);
+
+		pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_format_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].bit_format =
+			tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_rx_bit_format = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_format_get(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] = tdm_get_format_val(
+				tdm_tx_cfg[port.mode][port.channel].bit_format);
+
+		pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_format_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].bit_format =
+			tdm_get_format(ucontrol->value.enumerated.item[0]);
+
+		pr_debug("%s: tdm_tx_bit_format = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].bit_format,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_ch_get(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+
+		ucontrol->value.enumerated.item[0] =
+			tdm_rx_cfg[port.mode][port.channel].channels - 1;
+
+		pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].channels - 1,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_rx_ch_put(struct snd_kcontrol *kcontrol,
+			 struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_rx_cfg[port.mode][port.channel].channels =
+			ucontrol->value.enumerated.item[0] + 1;
+
+		pr_debug("%s: tdm_rx_ch = %d, item = %d\n", __func__,
+			 tdm_rx_cfg[port.mode][port.channel].channels,
+			 ucontrol->value.enumerated.item[0] + 1);
+	}
+	return ret;
+}
+
+static int tdm_tx_ch_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		ucontrol->value.enumerated.item[0] =
+			tdm_tx_cfg[port.mode][port.channel].channels - 1;
+
+		pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].channels - 1,
+			 ucontrol->value.enumerated.item[0]);
+	}
+	return ret;
+}
+
+static int tdm_tx_ch_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct tdm_port port;
+	int ret = tdm_get_port_idx(kcontrol, &port);
+
+	if (ret) {
+		pr_err("%s: unsupported control: %s",
+			__func__, kcontrol->id.name);
+	} else {
+		tdm_tx_cfg[port.mode][port.channel].channels =
+			ucontrol->value.enumerated.item[0] + 1;
+
+		pr_debug("%s: tdm_tx_ch = %d, item = %d\n", __func__,
+			 tdm_tx_cfg[port.mode][port.channel].channels,
+			 ucontrol->value.enumerated.item[0] + 1);
+	}
+	return ret;
+}
+
+static int aux_pcm_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "PRIM_AUX_PCM",
+		    sizeof("PRIM_AUX_PCM")))
+		idx = PRIM_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "SEC_AUX_PCM",
+			 sizeof("SEC_AUX_PCM")))
+		idx = SEC_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "TERT_AUX_PCM",
+			 sizeof("TERT_AUX_PCM")))
+		idx = TERT_AUX_PCM;
+	else if (strnstr(kcontrol->id.name, "QUAT_AUX_PCM",
+			 sizeof("QUAT_AUX_PCM")))
+		idx = QUAT_AUX_PCM;
+	else {
+		pr_err("%s: unsupported port: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int aux_pcm_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	aux_pcm_rx_cfg[idx].sample_rate =
+		aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+	     aux_pcm_get_sample_rate_val(aux_pcm_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	aux_pcm_tx_cfg[idx].sample_rate =
+		aux_pcm_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int aux_pcm_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = aux_pcm_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+	     aux_pcm_get_sample_rate_val(aux_pcm_tx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, aux_pcm_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_get_port_idx(struct snd_kcontrol *kcontrol)
+{
+	int idx;
+
+	if (strnstr(kcontrol->id.name, "PRIM_MI2S_RX",
+	    sizeof("PRIM_MI2S_RX")))
+		idx = PRIM_MI2S;
+	else if (strnstr(kcontrol->id.name, "SEC_MI2S_RX",
+		 sizeof("SEC_MI2S_RX")))
+		idx = SEC_MI2S;
+	else if (strnstr(kcontrol->id.name, "TERT_MI2S_RX",
+		 sizeof("TERT_MI2S_RX")))
+		idx = TERT_MI2S;
+	else if (strnstr(kcontrol->id.name, "QUAT_MI2S_RX",
+		 sizeof("QUAT_MI2S_RX")))
+		idx = QUAT_MI2S;
+	else if (strnstr(kcontrol->id.name, "PRIM_MI2S_TX",
+		 sizeof("PRIM_MI2S_TX")))
+		idx = PRIM_MI2S;
+	else if (strnstr(kcontrol->id.name, "SEC_MI2S_TX",
+		 sizeof("SEC_MI2S_TX")))
+		idx = SEC_MI2S;
+	else if (strnstr(kcontrol->id.name, "TERT_MI2S_TX",
+		 sizeof("TERT_MI2S_TX")))
+		idx = TERT_MI2S;
+	else if (strnstr(kcontrol->id.name, "QUAT_MI2S_TX",
+		 sizeof("QUAT_MI2S_TX")))
+		idx = QUAT_MI2S;
+	else {
+		pr_err("%s: unsupported channel: %s",
+			__func__, kcontrol->id.name);
+		idx = -EINVAL;
+	}
+
+	return idx;
+}
+
+static int mi2s_get_sample_rate_val(int sample_rate)
+{
+	int sample_rate_val;
+
+	switch (sample_rate) {
+	case SAMPLING_RATE_8KHZ:
+		sample_rate_val = 0;
+		break;
+	case SAMPLING_RATE_16KHZ:
+		sample_rate_val = 1;
+		break;
+	case SAMPLING_RATE_32KHZ:
+		sample_rate_val = 2;
+		break;
+	case SAMPLING_RATE_44P1KHZ:
+		sample_rate_val = 3;
+		break;
+	case SAMPLING_RATE_48KHZ:
+		sample_rate_val = 4;
+		break;
+	case SAMPLING_RATE_96KHZ:
+		sample_rate_val = 5;
+		break;
+	case SAMPLING_RATE_192KHZ:
+		sample_rate_val = 6;
+		break;
+	default:
+		sample_rate_val = 4;
+		break;
+	}
+	return sample_rate_val;
+}
+
+static int mi2s_get_sample_rate(int value)
+{
+	int sample_rate;
+
+	switch (value) {
+	case 0:
+		sample_rate = SAMPLING_RATE_8KHZ;
+		break;
+	case 1:
+		sample_rate = SAMPLING_RATE_16KHZ;
+		break;
+	case 2:
+		sample_rate = SAMPLING_RATE_32KHZ;
+		break;
+	case 3:
+		sample_rate = SAMPLING_RATE_44P1KHZ;
+		break;
+	case 4:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	case 5:
+		sample_rate = SAMPLING_RATE_96KHZ;
+		break;
+	case 6:
+		sample_rate = SAMPLING_RATE_192KHZ;
+		break;
+	default:
+		sample_rate = SAMPLING_RATE_48KHZ;
+		break;
+	}
+	return sample_rate;
+}
+
+static int mi2s_rx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_rx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_rx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(mi2s_rx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_rx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_tx_sample_rate_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_tx_cfg[idx].sample_rate =
+		mi2s_get_sample_rate(ucontrol->value.enumerated.item[0]);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int mi2s_tx_sample_rate_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	ucontrol->value.enumerated.item[0] =
+		mi2s_get_sample_rate_val(mi2s_tx_cfg[idx].sample_rate);
+
+	pr_debug("%s: idx[%d]_tx_sample_rate = %d, item = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].sample_rate,
+		 ucontrol->value.enumerated.item[0]);
+
+	return 0;
+}
+
+static int msm_mi2s_rx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: msm_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = mi2s_rx_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int msm_mi2s_rx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_rx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_mi2s_[%d]_rx_ch  = %d\n", __func__,
+		 idx, mi2s_rx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int msm_mi2s_tx_ch_get(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	pr_debug("%s: msm_mi2s_[%d]_tx_ch  = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].channels);
+	ucontrol->value.enumerated.item[0] = mi2s_tx_cfg[idx].channels - 1;
+
+	return 0;
+}
+
+static int msm_mi2s_tx_ch_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = mi2s_get_port_idx(kcontrol);
+
+	if (idx < 0)
+		return idx;
+
+	mi2s_tx_cfg[idx].channels = ucontrol->value.enumerated.item[0] + 1;
+	pr_debug("%s: msm_mi2s_[%d]_tx_ch  = %d\n", __func__,
+		 idx, mi2s_tx_cfg[idx].channels);
+
+	return 1;
+}
+
+static int msm_hifi_ctrl(struct snd_soc_codec *codec)
+{
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s: msm_hifi_control = %d", __func__,
+		 msm_hifi_control);
+
+	if (!pdata || !pdata->hph_en1_gpio_p) {
+		pr_err("%s: hph_en1_gpio is invalid\n", __func__);
+		return -EINVAL;
+	}
+	if (msm_hifi_control == MSM_HIFI_ON) {
+		msm_cdc_pinctrl_select_active_state(pdata->hph_en1_gpio_p);
+		/* 5msec delay needed as per HW requirement */
+		usleep_range(5000, 5010);
+	} else {
+		msm_cdc_pinctrl_select_sleep_state(pdata->hph_en1_gpio_p);
+	}
+	snd_soc_dapm_sync(dapm);
+
+	return 0;
+}
+
+static int msm_hifi_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: msm_hifi_control = %d\n",
+		 __func__, msm_hifi_control);
+	ucontrol->value.integer.value[0] = msm_hifi_control;
+
+	return 0;
+}
+
+static int msm_hifi_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+
+	pr_debug("%s() ucontrol->value.integer.value[0] = %ld\n",
+		 __func__, ucontrol->value.integer.value[0]);
+
+	msm_hifi_control = ucontrol->value.integer.value[0];
+	msm_hifi_ctrl(codec);
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new msm_snd_controls[] = {
+	SOC_ENUM_EXT("SLIM_0_RX Channels", slim_0_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_2_RX Channels", slim_2_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_TX Channels", slim_0_tx_chs,
+			msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_1_TX Channels", slim_1_tx_chs,
+			msm_slim_tx_ch_get, msm_slim_tx_ch_put),
+	SOC_ENUM_EXT("SLIM_5_RX Channels", slim_5_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_6_RX Channels", slim_6_rx_chs,
+			msm_slim_rx_ch_get, msm_slim_rx_ch_put),
+	SOC_ENUM_EXT("VI_FEED_TX Channels", vi_feed_tx_chs,
+			msm_vi_feed_tx_ch_get, msm_vi_feed_tx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX Channels", usb_rx_chs,
+			usb_audio_rx_ch_get, usb_audio_rx_ch_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX Channels", usb_tx_chs,
+			usb_audio_tx_ch_get, usb_audio_tx_ch_put),
+	SOC_ENUM_EXT("Display Port RX Channels", ext_disp_rx_chs,
+			ext_disp_rx_ch_get, ext_disp_rx_ch_put),
+	SOC_ENUM_EXT("PROXY_RX Channels", proxy_rx_chs,
+			proxy_rx_ch_get, proxy_rx_ch_put),
+	SOC_ENUM_EXT("SLIM_0_RX Format", slim_0_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_5_RX Format", slim_5_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_6_RX Format", slim_6_rx_format,
+			slim_rx_bit_format_get, slim_rx_bit_format_put),
+	SOC_ENUM_EXT("SLIM_0_TX Format", slim_0_tx_format,
+			slim_tx_bit_format_get, slim_tx_bit_format_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX Format", usb_rx_format,
+			usb_audio_rx_format_get, usb_audio_rx_format_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX Format", usb_tx_format,
+			usb_audio_tx_format_get, usb_audio_tx_format_put),
+	SOC_ENUM_EXT("Display Port RX Bit Format", ext_disp_rx_format,
+			ext_disp_rx_format_get, ext_disp_rx_format_put),
+	SOC_ENUM_EXT("SLIM_0_RX SampleRate", slim_0_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_2_RX SampleRate", slim_2_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_0_TX SampleRate", slim_0_tx_sample_rate,
+			slim_tx_sample_rate_get, slim_tx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_5_RX SampleRate", slim_5_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("SLIM_6_RX SampleRate", slim_6_rx_sample_rate,
+			slim_rx_sample_rate_get, slim_rx_sample_rate_put),
+	SOC_ENUM_EXT("BT SampleRate", bt_sample_rate,
+			msm_bt_sample_rate_get,
+			msm_bt_sample_rate_put),
+	SOC_ENUM_EXT("USB_AUDIO_RX SampleRate", usb_rx_sample_rate,
+			usb_audio_rx_sample_rate_get,
+			usb_audio_rx_sample_rate_put),
+	SOC_ENUM_EXT("USB_AUDIO_TX SampleRate", usb_tx_sample_rate,
+			usb_audio_tx_sample_rate_get,
+			usb_audio_tx_sample_rate_put),
+	SOC_ENUM_EXT("Display Port RX SampleRate", ext_disp_rx_sample_rate,
+			ext_disp_rx_sample_rate_get,
+			ext_disp_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 SampleRate", tdm_rx_sample_rate,
+			tdm_rx_sample_rate_get,
+			tdm_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 SampleRate", tdm_tx_sample_rate,
+			tdm_tx_sample_rate_get,
+			tdm_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Format", tdm_rx_format,
+			tdm_rx_format_get,
+			tdm_rx_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Format", tdm_tx_format,
+			tdm_tx_format_get,
+			tdm_tx_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Channels", tdm_rx_chs,
+			tdm_rx_ch_get,
+			tdm_rx_ch_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Channels", tdm_tx_chs,
+			tdm_tx_ch_get,
+			tdm_tx_ch_put),
+	SOC_ENUM_EXT("PRIM_AUX_PCM_RX SampleRate", prim_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_AUX_PCM_RX SampleRate", sec_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_AUX_PCM_RX SampleRate", tert_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_AUX_PCM_RX SampleRate", quat_aux_pcm_rx_sample_rate,
+			aux_pcm_rx_sample_rate_get,
+			aux_pcm_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_AUX_PCM_TX SampleRate", prim_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_AUX_PCM_TX SampleRate", sec_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_AUX_PCM_TX SampleRate", tert_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_AUX_PCM_TX SampleRate", quat_aux_pcm_tx_sample_rate,
+			aux_pcm_tx_sample_rate_get,
+			aux_pcm_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_RX SampleRate", prim_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_MI2S_RX SampleRate", sec_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_MI2S_RX SampleRate", tert_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_MI2S_RX SampleRate", quat_mi2s_rx_sample_rate,
+			mi2s_rx_sample_rate_get,
+			mi2s_rx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_TX SampleRate", prim_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("SEC_MI2S_TX SampleRate", sec_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("TERT_MI2S_TX SampleRate", tert_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("QUAT_MI2S_TX SampleRate", quat_mi2s_tx_sample_rate,
+			mi2s_tx_sample_rate_get,
+			mi2s_tx_sample_rate_put),
+	SOC_ENUM_EXT("PRIM_MI2S_RX Channels", prim_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("PRIM_MI2S_TX Channels", prim_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("SEC_MI2S_RX Channels", sec_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("SEC_MI2S_TX Channels", sec_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("TERT_MI2S_RX Channels", tert_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("TERT_MI2S_TX Channels", tert_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("QUAT_MI2S_RX Channels", quat_mi2s_rx_chs,
+			msm_mi2s_rx_ch_get, msm_mi2s_rx_ch_put),
+	SOC_ENUM_EXT("QUAT_MI2S_TX Channels", quat_mi2s_tx_chs,
+			msm_mi2s_tx_ch_get, msm_mi2s_tx_ch_put),
+	SOC_ENUM_EXT("HiFi Function", hifi_function, msm_hifi_get,
+			msm_hifi_put),
+};
+
+static int msm_snd_enable_codec_ext_clk(struct snd_soc_codec *codec,
+					int enable, bool dapm)
+{
+	int ret = 0;
+
+	if (!strcmp(dev_name(codec->dev), "tavil_codec")) {
+		ret = tavil_cdc_mclk_enable(codec, enable);
+	} else {
+		dev_err(codec->dev, "%s: unknown codec to enable ext clk\n",
+			__func__);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+static int msm_mclk_event(struct snd_soc_dapm_widget *w,
+				 struct snd_kcontrol *kcontrol, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+	pr_debug("%s: event = %d\n", __func__, event);
+
+	switch (event) {
+	case SND_SOC_DAPM_PRE_PMU:
+		return msm_snd_enable_codec_ext_clk(codec, 1, true);
+	case SND_SOC_DAPM_POST_PMD:
+		return msm_snd_enable_codec_ext_clk(codec, 0, true);
+	}
+	return 0;
+}
+
+static int msm_hifi_ctrl_event(struct snd_soc_dapm_widget *w,
+			       struct snd_kcontrol *k, int event)
+{
+	struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	pr_debug("%s: msm_hifi_control = %d", __func__, msm_hifi_control);
+
+	if (!pdata || !pdata->hph_en0_gpio_p) {
+		pr_err("%s: hph_en0_gpio is invalid\n", __func__);
+		return -EINVAL;
+	}
+
+	if (msm_hifi_control != MSM_HIFI_ON) {
+		pr_debug("%s: HiFi mixer control is not set\n",
+			 __func__);
+		return 0;
+	}
+
+	switch (event) {
+	case SND_SOC_DAPM_POST_PMU:
+		msm_cdc_pinctrl_select_active_state(pdata->hph_en0_gpio_p);
+		break;
+	case SND_SOC_DAPM_PRE_PMD:
+		msm_cdc_pinctrl_select_sleep_state(pdata->hph_en0_gpio_p);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_soc_dapm_widget msm_dapm_widgets[] = {
+
+	SND_SOC_DAPM_SUPPLY("MCLK",  SND_SOC_NOPM, 0, 0,
+			    msm_mclk_event,
+			    SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SUPPLY("MCLK TX",  SND_SOC_NOPM, 0, 0,
+	NULL, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+	SND_SOC_DAPM_SPK("Lineout_1 amp", NULL),
+	SND_SOC_DAPM_SPK("Lineout_2 amp", NULL),
+	SND_SOC_DAPM_SPK("hifi amp", msm_hifi_ctrl_event),
+	SND_SOC_DAPM_MIC("Handset Mic", NULL),
+	SND_SOC_DAPM_MIC("Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCRight Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("ANCLeft Headset Mic", NULL),
+	SND_SOC_DAPM_MIC("Analog Mic5", NULL),
+
+	SND_SOC_DAPM_MIC("Digital Mic0", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic1", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic2", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic3", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic4", NULL),
+	SND_SOC_DAPM_MIC("Digital Mic5", NULL),
+};
+
+static inline int param_is_mask(int p)
+{
+	return (p >= SNDRV_PCM_HW_PARAM_FIRST_MASK) &&
+			(p <= SNDRV_PCM_HW_PARAM_LAST_MASK);
+}
+
+static inline struct snd_mask *param_to_mask(struct snd_pcm_hw_params *p,
+					     int n)
+{
+	return &(p->masks[n - SNDRV_PCM_HW_PARAM_FIRST_MASK]);
+}
+
+static void param_set_mask(struct snd_pcm_hw_params *p, int n,
+			   unsigned int bit)
+{
+	if (bit >= SNDRV_MASK_MAX)
+		return;
+	if (param_is_mask(n)) {
+		struct snd_mask *m = param_to_mask(p, n);
+
+		m->bits[0] = 0;
+		m->bits[1] = 0;
+		m->bits[bit >> 5] |= (1 << (bit & 31));
+	}
+}
+
+static int msm_slim_get_ch_from_beid(int32_t be_id)
+{
+	int ch_id = 0;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+		ch_id = SLIM_RX_0;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+		ch_id = SLIM_RX_1;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+		ch_id = SLIM_RX_2;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+		ch_id = SLIM_RX_3;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+		ch_id = SLIM_RX_4;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+		ch_id = SLIM_RX_6;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+		ch_id = SLIM_TX_0;
+		break;
+	case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+		ch_id = SLIM_TX_3;
+		break;
+	default:
+		ch_id = SLIM_RX_0;
+		break;
+	}
+
+	return ch_id;
+}
+
+static int msm_ext_disp_get_idx_from_beid(int32_t be_id)
+{
+	int idx;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+		idx = DP_RX_IDX;
+		break;
+	default:
+		pr_err("%s: Incorrect ext_disp BE id %d\n", __func__, be_id);
+		idx = -EINVAL;
+		break;
+	}
+
+	return idx;
+}
+
+static int msm_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+				  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	struct snd_interval *rate = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval *channels = hw_param_interval(params,
+					SNDRV_PCM_HW_PARAM_CHANNELS);
+	int rc = 0;
+	int idx;
+	void *config = NULL;
+	struct snd_soc_codec *codec = NULL;
+
+	pr_debug("%s: format = %d, rate = %d\n",
+		  __func__, params_format(params), params_rate(params));
+
+	switch (dai_link->id) {
+	case MSM_BACKEND_DAI_SLIMBUS_0_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_1_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_2_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_3_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_4_RX:
+	case MSM_BACKEND_DAI_SLIMBUS_6_RX:
+		idx = msm_slim_get_ch_from_beid(dai_link->id);
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[idx].bit_format);
+		rate->min = rate->max = slim_rx_cfg[idx].sample_rate;
+		channels->min = channels->max = slim_rx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_0_TX:
+	case MSM_BACKEND_DAI_SLIMBUS_3_TX:
+		idx = msm_slim_get_ch_from_beid(dai_link->id);
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_tx_cfg[idx].bit_format);
+		rate->min = rate->max = slim_tx_cfg[idx].sample_rate;
+		channels->min = channels->max = slim_tx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_1_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_tx_cfg[1].bit_format);
+		rate->min = rate->max = slim_tx_cfg[1].sample_rate;
+		channels->min = channels->max = slim_tx_cfg[1].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_4_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       SNDRV_PCM_FORMAT_S32_LE);
+		rate->min = rate->max = SAMPLING_RATE_8KHZ;
+		channels->min = channels->max = msm_vi_feed_tx_ch;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_5_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[5].bit_format);
+		rate->min = rate->max = slim_rx_cfg[5].sample_rate;
+		channels->min = channels->max = slim_rx_cfg[5].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_5_TX:
+		codec = rtd->codec;
+		rate->min = rate->max = SAMPLING_RATE_16KHZ;
+		channels->min = channels->max = 1;
+
+		config = msm_codec_fn.get_afe_config_fn(codec,
+					AFE_SLIMBUS_SLAVE_PORT_CONFIG);
+		if (config) {
+			rc = afe_set_config(AFE_SLIMBUS_SLAVE_PORT_CONFIG,
+					    config, SLIMBUS_5_TX);
+			if (rc)
+				pr_err("%s: Failed to set slimbus slave port config %d\n",
+					__func__, rc);
+		}
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_7_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				slim_rx_cfg[SLIM_RX_7].bit_format);
+		rate->min = rate->max = slim_rx_cfg[SLIM_RX_7].sample_rate;
+		channels->min = channels->max =
+			slim_rx_cfg[SLIM_RX_7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_7_TX:
+		rate->min = rate->max = slim_tx_cfg[SLIM_TX_7].sample_rate;
+		channels->min = channels->max =
+			slim_tx_cfg[SLIM_TX_7].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SLIMBUS_8_TX:
+		rate->min = rate->max = slim_tx_cfg[SLIM_TX_8].sample_rate;
+		channels->min = channels->max =
+			slim_tx_cfg[SLIM_TX_8].channels;
+		break;
+
+	case MSM_BACKEND_DAI_USB_RX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				usb_rx_cfg.bit_format);
+		rate->min = rate->max = usb_rx_cfg.sample_rate;
+		channels->min = channels->max = usb_rx_cfg.channels;
+		break;
+
+	case MSM_BACKEND_DAI_USB_TX:
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				usb_tx_cfg.bit_format);
+		rate->min = rate->max = usb_tx_cfg.sample_rate;
+		channels->min = channels->max = usb_tx_cfg.channels;
+		break;
+
+	case MSM_BACKEND_DAI_DISPLAY_PORT_RX:
+		idx = msm_ext_disp_get_idx_from_beid(dai_link->id);
+		if (idx < 0) {
+			pr_err("%s: Incorrect ext disp idx %d\n",
+			       __func__, idx);
+			rc = idx;
+			goto done;
+		}
+
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+				ext_disp_rx_cfg[idx].bit_format);
+		rate->min = rate->max = ext_disp_rx_cfg[idx].sample_rate;
+		channels->min = channels->max = ext_disp_rx_cfg[idx].channels;
+		break;
+
+	case MSM_BACKEND_DAI_AFE_PCM_RX:
+		channels->min = channels->max = proxy_rx_cfg.channels;
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_PRI][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_PRI][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_PRI][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_PRI][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_PRI][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_PRI][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_SEC][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_SEC][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_SEC][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_SEC][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_SEC][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_SEC][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_TERT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_TERT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_TERT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_TERT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_TERT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_TERT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_TDM_RX_0:
+		channels->min = channels->max =
+				tdm_rx_cfg[TDM_QUAT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_rx_cfg[TDM_QUAT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_rx_cfg[TDM_QUAT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_TDM_TX_0:
+		channels->min = channels->max =
+				tdm_tx_cfg[TDM_QUAT][TDM_0].channels;
+		param_set_mask(params, SNDRV_PCM_HW_PARAM_FORMAT,
+			       tdm_tx_cfg[TDM_QUAT][TDM_0].bit_format);
+		rate->min = rate->max = tdm_tx_cfg[TDM_QUAT][TDM_0].sample_rate;
+		break;
+
+	case MSM_BACKEND_DAI_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[PRIM_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[PRIM_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[PRIM_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[PRIM_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[SEC_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[SEC_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SEC_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[SEC_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[SEC_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[TERT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[TERT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERT_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[TERT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[TERT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_AUXPCM_RX:
+		rate->min = rate->max =
+			aux_pcm_rx_cfg[QUAT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_rx_cfg[QUAT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUAT_AUXPCM_TX:
+		rate->min = rate->max =
+			aux_pcm_tx_cfg[QUAT_AUX_PCM].sample_rate;
+		channels->min = channels->max =
+			aux_pcm_tx_cfg[QUAT_AUX_PCM].channels;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[PRIM_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[PRIM_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_PRI_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[PRIM_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[PRIM_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[SEC_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[SEC_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[SEC_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[SEC_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[TERT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[TERT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[TERT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[TERT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+		rate->min = rate->max = mi2s_rx_cfg[QUAT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_rx_cfg[QUAT_MI2S].channels;
+		break;
+
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+		rate->min = rate->max = mi2s_tx_cfg[QUAT_MI2S].sample_rate;
+		channels->min = channels->max =
+			mi2s_tx_cfg[QUAT_MI2S].channels;
+		break;
+
+	default:
+		rate->min = rate->max = SAMPLING_RATE_48KHZ;
+		break;
+	}
+
+done:
+	return rc;
+}
+
+static bool msm_swap_gnd_mic(struct snd_soc_codec *codec)
+{
+	struct snd_soc_card *card = codec->component.card;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int value = 0;
+
+	if (pdata->us_euro_gpio_p) {
+		value = msm_cdc_pinctrl_get_state(pdata->us_euro_gpio_p);
+		if (value)
+			msm_cdc_pinctrl_select_sleep_state(
+							pdata->us_euro_gpio_p);
+		else
+			msm_cdc_pinctrl_select_active_state(
+							pdata->us_euro_gpio_p);
+	} else if (pdata->us_euro_gpio >= 0) {
+		value = gpio_get_value_cansleep(pdata->us_euro_gpio);
+		gpio_set_value_cansleep(pdata->us_euro_gpio, !value);
+	}
+	pr_debug("%s: swap select switch %d to %d\n", __func__, value, !value);
+	return true;
+}
+
+static int msm_afe_set_config(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	void *config_data = NULL;
+
+	if (!msm_codec_fn.get_afe_config_fn) {
+		dev_err(codec->dev, "%s: codec get afe config not init'ed\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTERS_CONFIG);
+	if (config_data) {
+		ret = afe_set_config(AFE_CDC_REGISTERS_CONFIG, config_data, 0);
+		if (ret) {
+			dev_err(codec->dev,
+				"%s: Failed to set codec registers config %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_CDC_REGISTER_PAGE_CONFIG);
+	if (config_data) {
+		ret = afe_set_config(AFE_CDC_REGISTER_PAGE_CONFIG, config_data,
+				    0);
+		if (ret)
+			dev_err(codec->dev,
+				"%s: Failed to set cdc register page config\n",
+				__func__);
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+			AFE_SLIMBUS_SLAVE_CONFIG);
+	if (config_data) {
+		ret = afe_set_config(AFE_SLIMBUS_SLAVE_CONFIG, config_data, 0);
+		if (ret) {
+			dev_err(codec->dev,
+				"%s: Failed to set slimbus slave config %d\n",
+				__func__, ret);
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static void msm_afe_clear_config(void)
+{
+	afe_clear_config(AFE_CDC_REGISTERS_CONFIG);
+	afe_clear_config(AFE_SLIMBUS_SLAVE_CONFIG);
+}
+
+static int msm_adsp_power_up_config(struct snd_soc_codec *codec)
+{
+	int ret = 0;
+	unsigned long timeout;
+	int adsp_ready = 0;
+
+	timeout = jiffies +
+		msecs_to_jiffies(ADSP_STATE_READY_TIMEOUT_MS);
+
+	do {
+		if (q6core_is_adsp_ready()) {
+			pr_debug("%s: ADSP Audio is ready\n", __func__);
+			adsp_ready = 1;
+			break;
+		}
+		/*
+		 * ADSP will be coming up after subsystem restart and
+		 * it might not be fully up when the control reaches
+		 * here. So, wait for 50msec before checking ADSP state
+		 */
+		msleep(50);
+	} while (time_after(timeout, jiffies));
+
+	if (!adsp_ready) {
+		pr_err("%s: timed out waiting for ADSP Audio\n", __func__);
+		ret = -ETIMEDOUT;
+		goto err;
+	}
+
+	ret = msm_afe_set_config(codec);
+	if (ret)
+		pr_err("%s: Failed to set AFE config. err %d\n",
+			__func__, ret);
+
+	return 0;
+
+err:
+	return ret;
+}
+
+static int msmskunk_notifier_service_cb(struct notifier_block *this,
+					 unsigned long opcode, void *ptr)
+{
+	int ret;
+	struct snd_soc_card *card = NULL;
+	const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_soc_codec *codec;
+
+	pr_debug("%s: Service opcode 0x%lx\n", __func__, opcode);
+
+	switch (opcode) {
+	case AUDIO_NOTIFIER_SERVICE_DOWN:
+		/*
+		 * Use flag to ignore initial boot notifications
+		 * On initial boot msm_adsp_power_up_config is
+		 * called on init. There is no need to clear
+		 * and set the config again on initial boot.
+		 */
+		if (is_initial_boot)
+			break;
+		msm_afe_clear_config();
+		break;
+	case AUDIO_NOTIFIER_SERVICE_UP:
+		if (is_initial_boot) {
+			is_initial_boot = false;
+			break;
+		}
+		if (!spdev)
+			return -EINVAL;
+
+		card = platform_get_drvdata(spdev);
+		rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+		if (!rtd) {
+			dev_err(card->dev,
+				"%s: snd_soc_get_pcm_runtime for %s failed!\n",
+				__func__, be_dl_name);
+			ret = -EINVAL;
+			goto err;
+		}
+		codec = rtd->codec;
+
+		ret = msm_adsp_power_up_config(codec);
+		if (ret < 0) {
+			dev_err(card->dev,
+				"%s: msm_adsp_power_up_config failed ret = %d!\n",
+				__func__, ret);
+			goto err;
+		}
+		break;
+	default:
+		break;
+	}
+err:
+	return NOTIFY_OK;
+}
+
+static struct notifier_block service_nb = {
+	.notifier_call  = msmskunk_notifier_service_cb,
+	.priority = -INT_MAX,
+};
+
+static int msm_audrx_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	void *config_data;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_component *aux_comp;
+	struct snd_card *card;
+	struct snd_info_entry *entry;
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(rtd->card);
+
+	/* Codec SLIMBUS configuration
+	 * RX1, RX2, RX3, RX4, RX5, RX6, RX7, RX8
+	 * TX1, TX2, TX3, TX4, TX5, TX6, TX7, TX8, TX9, TX10, TX11, TX12, TX13
+	 * TX14, TX15, TX16
+	 */
+	unsigned int rx_ch[WCD934X_RX_MAX] = {144, 145, 146, 147, 148, 149,
+					      150, 151};
+	unsigned int tx_ch[WCD934X_TX_MAX] = {128, 129, 130, 131, 132, 133,
+					      134, 135, 136, 137, 138, 139,
+					      140, 141, 142, 143};
+
+	pr_info("%s: dev_name%s\n", __func__, dev_name(cpu_dai->dev));
+
+	rtd->pmdown_time = 0;
+
+	ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+					 ARRAY_SIZE(msm_snd_controls));
+	if (ret < 0) {
+		pr_err("%s: add_codec_controls failed, err %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+				ARRAY_SIZE(msm_dapm_widgets));
+
+	snd_soc_dapm_add_routes(dapm, wcd_audio_paths,
+				ARRAY_SIZE(wcd_audio_paths));
+
+	snd_soc_dapm_ignore_suspend(dapm, "Handset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCRight Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "ANCLeft Headset Mic");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic0");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic1");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic2");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic3");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic4");
+	snd_soc_dapm_ignore_suspend(dapm, "Digital Mic5");
+	snd_soc_dapm_ignore_suspend(dapm, "Analog Mic5");
+	snd_soc_dapm_ignore_suspend(dapm, "MADINPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_INPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "MAD_CPE_OUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT1");
+	snd_soc_dapm_ignore_suspend(dapm, "LINEOUT2");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC EAR");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK1 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "SPK2 OUT");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "HPHR");
+	snd_soc_dapm_ignore_suspend(dapm, "AIF4 VI");
+	snd_soc_dapm_ignore_suspend(dapm, "VIINPUT");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HPHL");
+	snd_soc_dapm_ignore_suspend(dapm, "ANC HPHR");
+
+	snd_soc_dapm_sync(dapm);
+
+	snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+				    tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+
+	msm_codec_fn.get_afe_config_fn = tavil_get_afe_config;
+
+	ret = msm_adsp_power_up_config(codec);
+	if (ret) {
+		pr_err("%s: Failed to set AFE config %d\n", __func__, ret);
+		goto err;
+	}
+
+	config_data = msm_codec_fn.get_afe_config_fn(codec,
+						     AFE_AANC_VERSION);
+	if (config_data) {
+		ret = afe_set_config(AFE_AANC_VERSION, config_data, 0);
+		if (ret) {
+			pr_err("%s: Failed to set aanc version %d\n",
+				__func__, ret);
+			goto err;
+		}
+	}
+
+	/*
+	 * Send speaker configuration only for WSA8810.
+	 * Default configuration is for WSA8815.
+	 */
+	pr_debug("%s: Number of aux devices: %d\n",
+		__func__, rtd->card->num_aux_devs);
+	if (rtd->card->num_aux_devs &&
+	    !list_empty(&rtd->card->aux_comp_list)) {
+		aux_comp = list_first_entry(&rtd->card->aux_comp_list,
+				struct snd_soc_component, list_aux);
+		if (!strcmp(aux_comp->name, WSA8810_NAME_1) ||
+		    !strcmp(aux_comp->name, WSA8810_NAME_2)) {
+			tavil_set_spkr_mode(rtd->codec, WCD934X_SPKR_MODE_1);
+			tavil_set_spkr_gain_offset(rtd->codec,
+					WCD934X_RX_GAIN_OFFSET_M1P5_DB);
+		}
+	}
+	card = rtd->card->snd_card;
+	entry = snd_info_create_subdir(card->module, "codecs",
+				       card->proc_root);
+	if (!entry) {
+		pr_debug("%s: Cannot create codecs module entry\n",
+			 __func__);
+		pdata->codec_root = NULL;
+		goto done;
+	}
+	pdata->codec_root = entry;
+	tavil_codec_info_create_codec_entry(pdata->codec_root, codec);
+
+done:
+	codec_reg_done = true;
+	return 0;
+
+err:
+	return ret;
+}
+
+static int msm_wcn_init(struct snd_soc_pcm_runtime *rtd)
+{
+	unsigned int rx_ch[WCN_CDC_SLIM_RX_CH_MAX] = {157, 158};
+	unsigned int tx_ch[WCN_CDC_SLIM_TX_CH_MAX]  = {159, 160, 161};
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+	return snd_soc_dai_set_channel_map(codec_dai, ARRAY_SIZE(tx_ch),
+					   tx_ch, ARRAY_SIZE(rx_ch), rx_ch);
+}
+
+static void *def_tavil_mbhc_cal(void)
+{
+	void *tavil_wcd_cal;
+	struct wcd_mbhc_btn_detect_cfg *btn_cfg;
+	u16 *btn_high;
+
+	tavil_wcd_cal = kzalloc(WCD_MBHC_CAL_SIZE(WCD_MBHC_DEF_BUTTONS,
+				WCD9XXX_MBHC_DEF_RLOADS), GFP_KERNEL);
+	if (!tavil_wcd_cal)
+		return NULL;
+
+#define S(X, Y) ((WCD_MBHC_CAL_PLUG_TYPE_PTR(tavil_wcd_cal)->X) = (Y))
+	S(v_hs_max, 1600);
+#undef S
+#define S(X, Y) ((WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal)->X) = (Y))
+	S(num_btn, WCD_MBHC_DEF_BUTTONS);
+#undef S
+
+	btn_cfg = WCD_MBHC_CAL_BTN_DET_PTR(tavil_wcd_cal);
+	btn_high = ((void *)&btn_cfg->_v_btn_low) +
+		(sizeof(btn_cfg->_v_btn_low[0]) * btn_cfg->num_btn);
+
+	btn_high[0] = 75;
+	btn_high[1] = 150;
+	btn_high[2] = 237;
+	btn_high[3] = 500;
+	btn_high[4] = 500;
+	btn_high[5] = 500;
+	btn_high[6] = 500;
+	btn_high[7] = 500;
+
+	return tavil_wcd_cal;
+}
+
+static int msm_snd_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+
+	int ret = 0;
+	u32 rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	u32 user_set_tx_ch = 0;
+	u32 rx_ch_count;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+		if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_5_RX) {
+			pr_debug("%s: rx_5_ch=%d\n", __func__,
+				  slim_rx_cfg[5].channels);
+			rx_ch_count = slim_rx_cfg[5].channels;
+		} else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_2_RX) {
+			pr_debug("%s: rx_2_ch=%d\n", __func__,
+				 slim_rx_cfg[2].channels);
+			rx_ch_count = slim_rx_cfg[2].channels;
+		} else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_6_RX) {
+			pr_debug("%s: rx_6_ch=%d\n", __func__,
+				  slim_rx_cfg[6].channels);
+			rx_ch_count = slim_rx_cfg[6].channels;
+		} else {
+			pr_debug("%s: rx_0_ch=%d\n", __func__,
+				  slim_rx_cfg[0].channels);
+			rx_ch_count = slim_rx_cfg[0].channels;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+						  rx_ch_count, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+	} else {
+
+		pr_debug("%s: %s_tx_dai_id_%d_ch=%d\n", __func__,
+			 codec_dai->name, codec_dai->id, user_set_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+					 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map\n, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+		/* For <codec>_tx1 case */
+		if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_0_TX)
+			user_set_tx_ch = slim_tx_cfg[0].channels;
+		/* For <codec>_tx3 case */
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_1_TX)
+			user_set_tx_ch = slim_tx_cfg[1].channels;
+		else if (dai_link->id == MSM_BACKEND_DAI_SLIMBUS_4_TX)
+			user_set_tx_ch = msm_vi_feed_tx_ch;
+		else
+			user_set_tx_ch = tx_ch_cnt;
+
+		pr_debug("%s: msm_slim_0_tx_ch(%d) user_set_tx_ch(%d) tx_ch_cnt(%d), BE id (%d)\n",
+			 __func__,  slim_tx_cfg[0].channels, user_set_tx_ch,
+			 tx_ch_cnt, dai_link->id);
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+						  user_set_tx_ch, tx_ch, 0, 0);
+		if (ret < 0)
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+	}
+
+err:
+	return ret;
+}
+
+static int msm_slimbus_2_hw_params(struct snd_pcm_substream *substream,
+					  struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	unsigned int rx_ch[SLIM_MAX_RX_PORTS], tx_ch[SLIM_MAX_TX_PORTS];
+	unsigned int rx_ch_cnt = 0, tx_ch_cnt = 0;
+	unsigned int num_tx_ch = 0;
+	unsigned int num_rx_ch = 0;
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		num_rx_ch =  params_channels(params);
+		pr_debug("%s: %s rx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_rx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+				num_rx_ch, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+	} else {
+		num_tx_ch =  params_channels(params);
+		pr_debug("%s: %s  tx_dai_id = %d  num_ch = %d\n", __func__,
+			codec_dai->name, codec_dai->id, num_tx_ch);
+		ret = snd_soc_dai_get_channel_map(codec_dai,
+				&tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+		if (ret < 0) {
+			pr_err("%s: failed to get codec chan map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+				num_tx_ch, tx_ch, 0, 0);
+		if (ret < 0) {
+			pr_err("%s: failed to set cpu chan map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+	}
+
+err:
+	return ret;
+}
+
+static int msm_wcn_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *codec_dai = rtd->codec_dai;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	struct snd_soc_dai_link *dai_link = rtd->dai_link;
+	u32 rx_ch[WCN_CDC_SLIM_RX_CH_MAX], tx_ch[WCN_CDC_SLIM_TX_CH_MAX];
+	u32 rx_ch_cnt = 0, tx_ch_cnt = 0;
+	int ret;
+
+	dev_dbg(rtd->dev, "%s: %s_tx_dai_id_%d\n", __func__,
+		 codec_dai->name, codec_dai->id);
+	ret = snd_soc_dai_get_channel_map(codec_dai,
+				 &tx_ch_cnt, tx_ch, &rx_ch_cnt, rx_ch);
+	if (ret) {
+		dev_err(rtd->dev,
+			"%s: failed to get BTFM codec chan map\n, err:%d\n",
+			__func__, ret);
+		goto err;
+	}
+
+	dev_dbg(rtd->dev, "%s: tx_ch_cnt(%d) BE id %d\n",
+		__func__, tx_ch_cnt, dai_link->id);
+
+	ret = snd_soc_dai_set_channel_map(cpu_dai,
+					  tx_ch_cnt, tx_ch, rx_ch_cnt, rx_ch);
+	if (ret)
+		dev_err(rtd->dev, "%s: failed to set cpu chan map, err:%d\n",
+			__func__, ret);
+
+err:
+	return ret;
+}
+
+static int msm_aux_pcm_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id - 1;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto err;
+	}
+
+	mutex_lock(&auxpcm_intf_conf[index].lock);
+	if (++auxpcm_intf_conf[index].ref_cnt == 1) {
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(1,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
+				__func__);
+			ret = -EINVAL;
+		}
+	}
+	if (ret < 0)
+		auxpcm_intf_conf[index].ref_cnt--;
+
+	mutex_unlock(&auxpcm_intf_conf[index].lock);
+
+err:
+	return ret;
+}
+
+static void msm_aux_pcm_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id - 1;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__,
+		substream->name, substream->stream,
+		rtd->cpu_dai->name, rtd->cpu_dai->id);
+
+	if (index < PRIM_AUX_PCM || index > QUAT_AUX_PCM) {
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, rtd->cpu_dai->id);
+		return;
+	}
+
+	mutex_lock(&auxpcm_intf_conf[index].lock);
+	if (--auxpcm_intf_conf[index].ref_cnt == 0) {
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(0,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_tert_muxsel_virt_addr is NULL\n",
+				__func__);
+			auxpcm_intf_conf[index].ref_cnt++;
+		}
+	}
+	mutex_unlock(&auxpcm_intf_conf[index].lock);
+}
+
+static int msm_get_port_id(int be_id)
+{
+	int afe_port_id;
+
+	switch (be_id) {
+	case MSM_BACKEND_DAI_PRI_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_PRI_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_SECONDARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_TERTIARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		break;
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_RX:
+		afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+		break;
+	case MSM_BACKEND_DAI_QUATERNARY_MI2S_TX:
+		afe_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+		break;
+	default:
+		pr_err("%s: Invalid BE id: %d\n", __func__, be_id);
+		afe_port_id = -EINVAL;
+	}
+
+	return afe_port_id;
+}
+
+static u32 get_mi2s_bits_per_sample(u32 bit_format)
+{
+	u32 bit_per_sample;
+
+	switch (bit_format) {
+	case SNDRV_PCM_FORMAT_S24_3LE:
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bit_per_sample = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bit_per_sample = 16;
+		break;
+	}
+
+	return bit_per_sample;
+}
+
+static void update_mi2s_clk_val(int dai_id, int stream)
+{
+	u32 bit_per_sample;
+
+	if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		bit_per_sample =
+		    get_mi2s_bits_per_sample(mi2s_rx_cfg[dai_id].bit_format);
+		mi2s_clk[dai_id].clk_freq_in_hz =
+		    mi2s_rx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+	} else {
+		bit_per_sample =
+		    get_mi2s_bits_per_sample(mi2s_tx_cfg[dai_id].bit_format);
+		mi2s_clk[dai_id].clk_freq_in_hz =
+		    mi2s_tx_cfg[dai_id].sample_rate * 2 * bit_per_sample;
+	}
+
+	if (!mi2s_intf_conf[dai_id].msm_is_mi2s_master)
+		mi2s_clk[dai_id].clk_freq_in_hz = 0;
+}
+
+static int msm_mi2s_set_sclk(struct snd_pcm_substream *substream, bool enable)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int port_id = 0;
+	int index = cpu_dai->id;
+
+	port_id = msm_get_port_id(rtd->dai_link->id);
+	if (port_id < 0) {
+		dev_err(rtd->card->dev, "%s: Invalid port_id\n", __func__);
+		ret = port_id;
+		goto err;
+	}
+
+	if (enable) {
+		update_mi2s_clk_val(index, substream->stream);
+		dev_dbg(rtd->card->dev, "%s: clock rate %ul\n", __func__,
+			mi2s_clk[index].clk_freq_in_hz);
+	}
+
+	mi2s_clk[index].enable = enable;
+	ret = afe_set_lpass_clock_v2(port_id,
+				     &mi2s_clk[index]);
+	if (ret < 0) {
+		dev_err(rtd->card->dev,
+			"%s: afe lpass clock failed for port 0x%x , err:%d\n",
+			__func__, port_id, ret);
+		goto err;
+	}
+
+err:
+	return ret;
+}
+
+static int msm_mi2s_snd_startup(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int index = cpu_dai->id;
+	unsigned int fmt = SND_SOC_DAIFMT_CBS_CFS;
+
+	dev_dbg(rtd->card->dev,
+		"%s: substream = %s  stream = %d, dai name %s, dai ID %d\n",
+		__func__, substream->name, substream->stream,
+		cpu_dai->name, cpu_dai->id);
+
+	if (index < PRIM_MI2S || index > QUAT_MI2S) {
+		ret = -EINVAL;
+		dev_err(rtd->card->dev,
+			"%s: CPU DAI id (%d) out of range\n",
+			__func__, cpu_dai->id);
+		goto err;
+	}
+	/*
+	 * Muxtex protection in case the same MI2S
+	 * interface using for both TX and RX  so
+	 * that the same clock won't be enable twice.
+	 */
+	mutex_lock(&mi2s_intf_conf[index].lock);
+	if (++mi2s_intf_conf[index].ref_cnt == 1) {
+		ret = msm_mi2s_set_sclk(substream, true);
+		if (ret < 0) {
+			dev_err(rtd->card->dev,
+				"%s: afe lpass clock failed to enable MI2S clock, err:%d\n",
+				__func__, ret);
+			goto clean_up;
+		}
+		if (mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr != NULL) {
+			mutex_lock(&mi2s_auxpcm_conf[index].lock);
+			iowrite32(0,
+				mi2s_auxpcm_conf[index].pcm_i2s_sel_vt_addr);
+			mutex_unlock(&mi2s_auxpcm_conf[index].lock);
+		} else {
+			dev_err(rtd->card->dev,
+				"%s lpaif_muxsel_virt_addr is NULL for dai %d\n",
+				__func__, index);
+			ret = -EINVAL;
+			goto clk_off;
+		}
+		/* Check if msm needs to provide the clock to the interface */
+		if (!mi2s_intf_conf[index].msm_is_mi2s_master)
+			fmt = SND_SOC_DAIFMT_CBM_CFM;
+		ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
+		if (ret < 0) {
+			pr_err("%s: set fmt cpu dai failed for MI2S (%d), err:%d\n",
+				__func__, index, ret);
+			goto clk_off;
+		}
+	}
+clk_off:
+	if (ret < 0)
+		msm_mi2s_set_sclk(substream, false);
+clean_up:
+	if (ret < 0)
+		mi2s_intf_conf[index].ref_cnt--;
+	mutex_unlock(&mi2s_intf_conf[index].lock);
+err:
+	return ret;
+}
+
+static void msm_mi2s_snd_shutdown(struct snd_pcm_substream *substream)
+{
+	int ret;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int index = rtd->cpu_dai->id;
+
+	pr_debug("%s(): substream = %s  stream = %d\n", __func__,
+		 substream->name, substream->stream);
+	if (index < PRIM_MI2S || index > QUAT_MI2S) {
+		pr_err("%s:invalid MI2S DAI(%d)\n", __func__, index);
+		return;
+	}
+
+	mutex_lock(&mi2s_intf_conf[index].lock);
+	if (--mi2s_intf_conf[index].ref_cnt == 0) {
+		ret = msm_mi2s_set_sclk(substream, false);
+		if (ret < 0) {
+			pr_err("%s:clock disable failed for MI2S (%d); ret=%d\n",
+				__func__, index, ret);
+			mi2s_intf_conf[index].ref_cnt++;
+		}
+	}
+	mutex_unlock(&mi2s_intf_conf[index].lock);
+}
+
+static struct snd_soc_ops msm_mi2s_be_ops = {
+	.startup = msm_mi2s_snd_startup,
+	.shutdown = msm_mi2s_snd_shutdown,
+};
+
+static struct snd_soc_ops msm_aux_pcm_be_ops = {
+	.startup = msm_aux_pcm_snd_startup,
+	.shutdown = msm_aux_pcm_snd_shutdown,
+};
+
+static unsigned int tdm_param_set_slot_mask(u16 port_id, int slot_width,
+					    int slots)
+{
+	unsigned int slot_mask = 0;
+	int i, j;
+	unsigned int *slot_offset;
+
+	for (i = TDM_0; i < TDM_PORT_MAX; i++) {
+		slot_offset = tdm_slot_offset[i];
+
+		for (j = 0; j < TDM_SLOT_OFFSET_MAX; j++) {
+			if (slot_offset[j] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+				slot_mask |=
+				(1 << ((slot_offset[j] * 8) / slot_width));
+			else
+				break;
+		}
+	}
+
+	return slot_mask;
+}
+
+static int msm_tdm_snd_hw_params(struct snd_pcm_substream *substream,
+				     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+	int ret = 0;
+	int channels, slot_width, slots;
+	unsigned int slot_mask;
+	unsigned int *slot_offset;
+	int offset_channels = 0;
+	int i;
+
+	pr_debug("%s: dai id = 0x%x\n", __func__, cpu_dai->id);
+
+	channels = params_channels(params);
+	switch (channels) {
+	case 1:
+	case 2:
+	case 3:
+	case 4:
+	case 5:
+	case 6:
+	case 7:
+	case 8:
+		switch (params_format(params)) {
+		case SNDRV_PCM_FORMAT_S32_LE:
+		case SNDRV_PCM_FORMAT_S24_LE:
+		case SNDRV_PCM_FORMAT_S16_LE:
+		/*
+		 * Up to 8 channels HW config should
+		 * use 32 bit slot width for max support of
+		 * stream bit width. (slot_width > bit_width)
+		 */
+			slot_width = 32;
+			break;
+		default:
+			pr_err("%s: invalid param format 0x%x\n",
+				__func__, params_format(params));
+			return -EINVAL;
+		}
+		slots = 8;
+		slot_mask = tdm_param_set_slot_mask(cpu_dai->id,
+						    slot_width,
+						    slots);
+		if (!slot_mask) {
+			pr_err("%s: invalid slot_mask 0x%x\n",
+				__func__, slot_mask);
+			return -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s: invalid param channels %d\n",
+			__func__, channels);
+		return -EINVAL;
+	}
+	/* currently only supporting TDM_RX_0 and TDM_TX_0 */
+	switch (cpu_dai->id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		slot_offset = tdm_slot_offset[TDM_0];
+		break;
+	default:
+		pr_err("%s: dai id 0x%x not supported\n",
+			__func__, cpu_dai->id);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < TDM_SLOT_OFFSET_MAX; i++) {
+		if (slot_offset[i] != AFE_SLOT_MAPPING_OFFSET_INVALID)
+			offset_channels++;
+		else
+			break;
+	}
+
+	if (offset_channels == 0) {
+		pr_err("%s: slot offset not supported, offset_channels %d\n",
+			__func__, offset_channels);
+		return -EINVAL;
+	}
+
+	if (channels > offset_channels) {
+		pr_err("%s: channels %d exceed offset_channels %d\n",
+			__func__, channels, offset_channels);
+		return -EINVAL;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, slot_mask,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, NULL,
+						  channels, slot_offset);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+	} else {
+		ret = snd_soc_dai_set_tdm_slot(cpu_dai, slot_mask, 0,
+					       slots, slot_width);
+		if (ret < 0) {
+			pr_err("%s: failed to set tdm slot, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+
+		ret = snd_soc_dai_set_channel_map(cpu_dai, channels,
+						  slot_offset, 0, NULL);
+		if (ret < 0) {
+			pr_err("%s: failed to set channel map, err:%d\n",
+				__func__, ret);
+			goto err;
+		}
+	}
+err:
+	return ret;
+}
+
+static struct snd_soc_ops msm_be_ops = {
+	.hw_params = msm_snd_hw_params,
+};
+
+static struct snd_soc_ops msm_slimbus_2_be_ops = {
+	.hw_params = msm_slimbus_2_hw_params,
+};
+
+static struct snd_soc_ops msm_wcn_ops = {
+	.hw_params = msm_wcn_hw_params,
+};
+
+static struct snd_soc_ops msm_tdm_be_ops = {
+	.hw_params = msm_tdm_snd_hw_params
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link msm_common_dai_links[] = {
+	/* FrontEnd DAI Links */
+	{
+		.name = MSM_DAILINK_NAME(Media1),
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name = "MultiMedia1",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+	{
+		.name = MSM_DAILINK_NAME(Media2),
+		.stream_name = "MultiMedia2",
+		.cpu_dai_name = "MultiMedia2",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA2,
+	},
+	{
+		.name = "VoiceMMode1",
+		.stream_name = "VoiceMMode1",
+		.cpu_dai_name = "VoiceMMode1",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICEMMODE1,
+	},
+	{
+		.name = "MSM VoIP",
+		.stream_name = "VoIP",
+		.cpu_dai_name = "VoIP",
+		.platform_name = "msm-voip-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_VOIP,
+	},
+	{
+		.name = MSM_DAILINK_NAME(ULL),
+		.stream_name = "MultiMedia3",
+		.cpu_dai_name = "MultiMedia3",
+		.platform_name = "msm-pcm-dsp.2",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA3,
+	},
+	/* Hostless PCM purpose */
+	{
+		.name = "SLIMBUS_0 Hostless",
+		.stream_name = "SLIMBUS_0 Hostless",
+		.cpu_dai_name = "SLIMBUS0_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "MSM AFE-PCM RX",
+		.stream_name = "AFE-PROXY RX",
+		.cpu_dai_name = "msm-dai-q6-dev.241",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.platform_name = "msm-pcm-afe",
+		.dpcm_playback = 1,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = "MSM AFE-PCM TX",
+		.stream_name = "AFE-PROXY TX",
+		.cpu_dai_name = "msm-dai-q6-dev.240",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.platform_name  = "msm-pcm-afe",
+		.dpcm_capture = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress1),
+		.stream_name = "Compress1",
+		.cpu_dai_name = "MultiMedia4",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_HW_PARAMS,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA4,
+	},
+	{
+		.name = "AUXPCM Hostless",
+		.stream_name = "AUXPCM Hostless",
+		.cpu_dai_name = "AUXPCM_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_1 Hostless",
+		.stream_name = "SLIMBUS_1 Hostless",
+		.cpu_dai_name = "SLIMBUS1_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_3 Hostless",
+		.stream_name = "SLIMBUS_3 Hostless",
+		.cpu_dai_name = "SLIMBUS3_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "SLIMBUS_4 Hostless",
+		.stream_name = "SLIMBUS_4 Hostless",
+		.cpu_dai_name = "SLIMBUS4_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		 /* this dailink has playback support */
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = MSM_DAILINK_NAME(LowLatency),
+		.stream_name = "MultiMedia5",
+		.cpu_dai_name = "MultiMedia5",
+		.platform_name = "msm-pcm-dsp.1",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA5,
+	},
+	{
+		.name = "Listen 1 Audio Service",
+		.stream_name = "Listen 1 Audio Service",
+		.cpu_dai_name = "LSM1",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+			     SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM1,
+	},
+	/* Multiple Tunnel instances */
+	{
+		.name = MSM_DAILINK_NAME(Compress2),
+		.stream_name = "Compress2",
+		.cpu_dai_name = "MultiMedia7",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA7,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress3),
+		.stream_name = "Compress3",
+		.cpu_dai_name = "MultiMedia10",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA10,
+	},
+	{
+		.name = MSM_DAILINK_NAME(ULL_NOIRQ),
+		.stream_name = "MM_NOIRQ",
+		.cpu_dai_name = "MultiMedia8",
+		.platform_name = "msm-pcm-dsp-noirq",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA8,
+	},
+	/* HDMI Hostless */
+	{
+		.name = "HDMI_RX_HOSTLESS",
+		.stream_name = "HDMI_RX_HOSTLESS",
+		.cpu_dai_name = "HDMI_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+	{
+		.name = "VoiceMMode2",
+		.stream_name = "VoiceMMode2",
+		.cpu_dai_name = "VoiceMMode2",
+		.platform_name = "msm-pcm-voice",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_VOICEMMODE2,
+	},
+	/* LSM FE */
+	{
+		.name = "Listen 2 Audio Service",
+		.stream_name = "Listen 2 Audio Service",
+		.cpu_dai_name = "LSM2",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM2,
+	},
+	{
+		.name = "Listen 3 Audio Service",
+		.stream_name = "Listen 3 Audio Service",
+		.cpu_dai_name = "LSM3",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM3,
+	},
+	{
+		.name = "Listen 4 Audio Service",
+		.stream_name = "Listen 4 Audio Service",
+		.cpu_dai_name = "LSM4",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM4,
+	},
+	{
+		.name = "Listen 5 Audio Service",
+		.stream_name = "Listen 5 Audio Service",
+		.cpu_dai_name = "LSM5",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM5,
+	},
+	{
+		.name = "Listen 6 Audio Service",
+		.stream_name = "Listen 6 Audio Service",
+		.cpu_dai_name = "LSM6",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM6,
+	},
+	{
+		.name = "Listen 7 Audio Service",
+		.stream_name = "Listen 7 Audio Service",
+		.cpu_dai_name = "LSM7",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM7,
+	},
+	{
+		.name = "Listen 8 Audio Service",
+		.stream_name = "Listen 8 Audio Service",
+		.cpu_dai_name = "LSM8",
+		.platform_name = "msm-lsm-client",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = { SND_SOC_DPCM_TRIGGER_POST,
+				 SND_SOC_DPCM_TRIGGER_POST },
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.id = MSM_FRONTEND_DAI_LSM8,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Media9),
+		.stream_name = "MultiMedia9",
+		.cpu_dai_name = "MultiMedia9",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+				SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA9,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress4),
+		.stream_name = "Compress4",
+		.cpu_dai_name = "MultiMedia11",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA11,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress5),
+		.stream_name = "Compress5",
+		.cpu_dai_name = "MultiMedia12",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA12,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress6),
+		.stream_name = "Compress6",
+		.cpu_dai_name = "MultiMedia13",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA13,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress7),
+		.stream_name = "Compress7",
+		.cpu_dai_name = "MultiMedia14",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA14,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress8),
+		.stream_name = "Compress8",
+		.cpu_dai_name = "MultiMedia15",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA15,
+	},
+	{
+		.name = MSM_DAILINK_NAME(Compress9),
+		.stream_name = "Compress9",
+		.cpu_dai_name = "MultiMedia16",
+		.platform_name = "msm-compress-dsp",
+		.dynamic = 1,
+		.dpcm_playback = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			 SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		 /* this dainlink has playback support */
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA16,
+	},
+	{
+		.name = "SLIMBUS_8 Hostless",
+		.stream_name = "SLIMBUS8_HOSTLESS Capture",
+		.cpu_dai_name = "SLIMBUS8_HOSTLESS",
+		.platform_name = "msm-pcm-hostless",
+		.dynamic = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			    SND_SOC_DPCM_TRIGGER_POST},
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+	},
+};
+
+static struct snd_soc_dai_link msm_tavil_fe_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_4_TX,
+		.stream_name = "Slimbus4 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16393",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_vifeedback",
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ignore_suspend = 1,
+	},
+	/* Ultrasound RX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Playback",
+		.stream_name = "SLIMBUS_2 Hostless Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_slimbus_2_be_ops,
+	},
+	/* Ultrasound TX DAI Link */
+	{
+		.name = "SLIMBUS_2 Hostless Capture",
+		.stream_name = "SLIMBUS_2 Hostless Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16389",
+		.platform_name = "msm-pcm-hostless",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx2",
+		.ignore_suspend = 1,
+		.no_host_mode = SND_SOC_DAI_LINK_NO_HOST,
+		.ops = &msm_slimbus_2_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_common_be_dai_links[] = {
+	/* Backend AFE DAI Links */
+	{
+		.name = LPASS_BE_AFE_PCM_RX,
+		.stream_name = "AFE Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.224",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_AFE_PCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_AFE_PCM_TX,
+		.stream_name = "AFE Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.225",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_AFE_PCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Uplink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_TX,
+		.stream_name = "Voice Uplink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32772",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Record Downlink BACK END DAI Link */
+	{
+		.name = LPASS_BE_INCALL_RECORD_RX,
+		.stream_name = "Voice Downlink Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.32771",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE_PLAYBACK_TX,
+		.stream_name = "Voice Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32773",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	/* Incall Music 2 BACK END DAI Link */
+	{
+		.name = LPASS_BE_VOICE2_PLAYBACK_TX,
+		.stream_name = "Voice2 Farend Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.32770",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_RX,
+		.stream_name = "USB Audio Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.28672",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_USB_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_USB_AUDIO_TX,
+		.stream_name = "USB Audio Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.28673",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_USB_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_RX_0,
+		.stream_name = "Primary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36864",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_PRI_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_TDM_TX_0,
+		.stream_name = "Primary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36865",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_PRI_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_RX_0,
+		.stream_name = "Secondary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36880",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SEC_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_TDM_TX_0,
+		.stream_name = "Secondary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36881",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SEC_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_RX_0,
+		.stream_name = "Tertiary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36896",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_TDM_TX_0,
+		.stream_name = "Tertiary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36897",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERT_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_RX_0,
+		.stream_name = "Quaternary TDM0 Playback",
+		.cpu_dai_name = "msm-dai-q6-tdm.36912",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_TDM_TX_0,
+		.stream_name = "Quaternary TDM0 Capture",
+		.cpu_dai_name = "msm-dai-q6-tdm.36913",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_tdm_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_tavil_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_init,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_RX,
+		.stream_name = "Slimbus1 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16386",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_1_TX,
+		.stream_name = "Slimbus1 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16387",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx3",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_1_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_2_RX,
+		.stream_name = "Slimbus2 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16388",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx2",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_2_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_RX,
+		.stream_name = "Slimbus3 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16390",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_3_TX,
+		.stream_name = "Slimbus3 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16391",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_tx1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_3_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_4_RX,
+		.stream_name = "Slimbus4 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16392",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx1",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_4_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_5_RX,
+		.stream_name = "Slimbus5 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16394",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx3",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	/* MAD BE */
+	{
+		.name = LPASS_BE_SLIMBUS_5_TX,
+		.stream_name = "Slimbus5 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16395",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_mad1",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_5_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_6_RX,
+		.stream_name = "Slimbus6 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16396",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "tavil_codec",
+		.codec_dai_name = "tavil_rx4",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_6_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_be_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_wcn_be_dai_links[] = {
+	{
+		.name = LPASS_BE_SLIMBUS_7_RX,
+		.stream_name = "Slimbus7 Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16398",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		/* BT codec driver determines capabilities based on
+		 * dai name, bt codecdai name should always contains
+		 * supported usecase information
+		 */
+		.codec_dai_name = "btfm_bt_sco_a2dp_slim_rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_7_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		/* dai link has playback support */
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_7_TX,
+		.stream_name = "Slimbus7 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16399",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_bt_sco_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_7_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_8_TX,
+		.stream_name = "Slimbus8 Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16401",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "btfmslim_slave",
+		.codec_dai_name = "btfm_fm_slim_tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_8_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.init = &msm_wcn_init,
+		.ops = &msm_wcn_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link ext_disp_be_dai_link[] = {
+	/* DISP PORT BACK END DAI Link */
+	{
+		.name = LPASS_BE_DISPLAY_PORT,
+		.stream_name = "Display Port Playback",
+		.cpu_dai_name = "msm-dai-q6-dp.24608",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-ext-disp-audio-codec-rx",
+		.codec_dai_name = "msm_dp_audio_codec_rx_dai",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_mi2s_be_dai_links[] = {
+	{
+		.name = LPASS_BE_PRI_MI2S_RX,
+		.stream_name = "Primary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_PRI_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_PRI_MI2S_TX,
+		.stream_name = "Primary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.0",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_PRI_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_RX,
+		.stream_name = "Secondary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_SEC_MI2S_TX,
+		.stream_name = "Secondary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_RX,
+		.stream_name = "Tertiary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_TERT_MI2S_TX,
+		.stream_name = "Tertiary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_RX,
+		.stream_name = "Quaternary MI2S Playback",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+	},
+	{
+		.name = LPASS_BE_QUAT_MI2S_TX,
+		.stream_name = "Quaternary MI2S Capture",
+		.cpu_dai_name = "msm-dai-q6-mi2s.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ops = &msm_mi2s_be_ops,
+		.ignore_suspend = 1,
+	},
+};
+
+static struct snd_soc_dai_link msm_auxpcm_be_dai_links[] = {
+	/* Primary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_AUXPCM_RX,
+		.stream_name = "AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_AUXPCM_TX,
+		.stream_name = "AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.1",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Secondary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_SEC_AUXPCM_RX,
+		.stream_name = "Sec AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_SEC_AUXPCM_TX,
+		.stream_name = "Sec AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.2",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Tertiary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_TERT_AUXPCM_RX,
+		.stream_name = "Tert AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_TERT_AUXPCM_TX,
+		.stream_name = "Tert AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.3",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	/* Quaternary AUX PCM Backend DAI Links */
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_RX,
+		.stream_name = "Quat AUX PCM Playback",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1,
+		.ignore_suspend = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+	{
+		.name = LPASS_BE_QUAT_AUXPCM_TX,
+		.stream_name = "Quat AUX PCM Capture",
+		.cpu_dai_name = "msm-dai-q6-auxpcm.4",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ignore_pmdown_time = 1,
+		.ops = &msm_aux_pcm_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_tavil_snd_card_dai_links[
+			 ARRAY_SIZE(msm_common_dai_links) +
+			 ARRAY_SIZE(msm_tavil_fe_dai_links) +
+			 ARRAY_SIZE(msm_common_be_dai_links) +
+			 ARRAY_SIZE(msm_tavil_be_dai_links) +
+			 ARRAY_SIZE(msm_wcn_be_dai_links) +
+			 ARRAY_SIZE(ext_disp_be_dai_link) +
+			 ARRAY_SIZE(msm_mi2s_be_dai_links) +
+			 ARRAY_SIZE(msm_auxpcm_be_dai_links)];
+
+static int msm_snd_card_tavil_late_probe(struct snd_soc_card *card)
+{
+	const char *be_dl_name = LPASS_BE_SLIMBUS_0_RX;
+	struct snd_soc_pcm_runtime *rtd;
+	int ret = 0;
+	void *mbhc_calibration;
+
+	rtd = snd_soc_get_pcm_runtime(card, be_dl_name);
+	if (!rtd) {
+		dev_err(card->dev,
+			"%s: snd_soc_get_pcm_runtime for %s failed!\n",
+			__func__, be_dl_name);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	mbhc_calibration = def_tavil_mbhc_cal();
+	if (!mbhc_calibration) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	wcd_mbhc_cfg.calibration = mbhc_calibration;
+	ret = tavil_mbhc_hs_detect(rtd->codec, &wcd_mbhc_cfg);
+	if (ret) {
+		dev_err(card->dev, "%s: mbhc hs detect failed, err:%d\n",
+			__func__, ret);
+		goto err_free_mbhc_cal;
+	}
+	return 0;
+
+err_free_mbhc_cal:
+	kfree(mbhc_calibration);
+err:
+	return ret;
+}
+
+struct snd_soc_card snd_soc_card_tavil_msm = {
+	.name		= "msmskunk-tavil-snd-card",
+	.late_probe	= msm_snd_card_tavil_late_probe,
+};
+
+static int msm_populate_dai_link_component_of_node(
+					struct snd_soc_card *card)
+{
+	int i, index, ret = 0;
+	struct device *cdev = card->dev;
+	struct snd_soc_dai_link *dai_link = card->dai_link;
+	struct device_node *np;
+
+	if (!cdev) {
+		pr_err("%s: Sound card device memory NULL\n", __func__);
+		return -ENODEV;
+	}
+
+	for (i = 0; i < card->num_links; i++) {
+		if (dai_link[i].platform_of_node && dai_link[i].cpu_of_node)
+			continue;
+
+		/* populate platform_of_node for snd card dai links */
+		if (dai_link[i].platform_name &&
+		    !dai_link[i].platform_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						"asoc-platform-names",
+						dai_link[i].platform_name);
+			if (index < 0) {
+				pr_err("%s: No match found for platform name: %s\n",
+					__func__, dai_link[i].platform_name);
+				ret = index;
+				goto err;
+			}
+			np = of_parse_phandle(cdev->of_node, "asoc-platform",
+					      index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for platform %s, index %d failed\n",
+					__func__, dai_link[i].platform_name,
+					index);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].platform_of_node = np;
+			dai_link[i].platform_name = NULL;
+		}
+
+		/* populate cpu_of_node for snd card dai links */
+		if (dai_link[i].cpu_dai_name && !dai_link[i].cpu_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						 "asoc-cpu-names",
+						 dai_link[i].cpu_dai_name);
+			if (index >= 0) {
+				np = of_parse_phandle(cdev->of_node, "asoc-cpu",
+						index);
+				if (!np) {
+					pr_err("%s: retrieving phandle for cpu dai %s failed\n",
+						__func__,
+						dai_link[i].cpu_dai_name);
+					ret = -ENODEV;
+					goto err;
+				}
+				dai_link[i].cpu_of_node = np;
+				dai_link[i].cpu_dai_name = NULL;
+			}
+		}
+
+		/* populate codec_of_node for snd card dai links */
+		if (dai_link[i].codec_name && !dai_link[i].codec_of_node) {
+			index = of_property_match_string(cdev->of_node,
+						 "asoc-codec-names",
+						 dai_link[i].codec_name);
+			if (index < 0)
+				continue;
+			np = of_parse_phandle(cdev->of_node, "asoc-codec",
+					      index);
+			if (!np) {
+				pr_err("%s: retrieving phandle for codec %s failed\n",
+					__func__, dai_link[i].codec_name);
+				ret = -ENODEV;
+				goto err;
+			}
+			dai_link[i].codec_of_node = np;
+			dai_link[i].codec_name = NULL;
+		}
+	}
+
+err:
+	return ret;
+}
+
+static int msm_prepare_us_euro(struct snd_soc_card *card)
+{
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+	int ret = 0;
+
+	if (pdata->us_euro_gpio >= 0) {
+		dev_dbg(card->dev, "%s: us_euro gpio request %d", __func__,
+			pdata->us_euro_gpio);
+		ret = gpio_request(pdata->us_euro_gpio, "TAVIL_CODEC_US_EURO");
+		if (ret) {
+			dev_err(card->dev,
+				"%s: Failed to request codec US/EURO gpio %d error %d\n",
+				__func__, pdata->us_euro_gpio, ret);
+		}
+	}
+
+	return ret;
+}
+
+static int msm_audrx_stub_init(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	struct snd_soc_codec *codec = rtd->codec;
+	struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+	ret = snd_soc_add_codec_controls(codec, msm_snd_controls,
+					 ARRAY_SIZE(msm_snd_controls));
+	if (ret < 0) {
+		dev_err(codec->dev,
+			"%s: add_codec_controls failed, err = %d\n",
+			__func__, ret);
+		return ret;
+	}
+
+	snd_soc_dapm_new_controls(dapm, msm_dapm_widgets,
+				  ARRAY_SIZE(msm_dapm_widgets));
+
+	return 0;
+}
+
+static int msm_snd_stub_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+
+	int ret = 0;
+	unsigned int rx_ch[] = {144, 145, 146, 147, 148, 149, 150,
+				151};
+	unsigned int tx_ch[] = {128, 129, 130, 131, 132, 133,
+				134, 135, 136, 137, 138, 139,
+				140, 141, 142, 143};
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_soc_dai_set_channel_map(cpu_dai, 0, 0,
+						  slim_rx_cfg[0].channels,
+						  rx_ch);
+		if (ret < 0)
+			pr_err("%s: RX failed to set cpu chan map error %d\n",
+				__func__, ret);
+	} else {
+		ret = snd_soc_dai_set_channel_map(cpu_dai,
+						  slim_tx_cfg[0].channels,
+						  tx_ch, 0, 0);
+		if (ret < 0)
+			pr_err("%s: TX failed to set cpu chan map error %d\n",
+				__func__, ret);
+	}
+
+	return ret;
+}
+
+static struct snd_soc_ops msm_stub_be_ops = {
+	.hw_params = msm_snd_stub_hw_params,
+};
+
+static struct snd_soc_dai_link msm_stub_fe_dai_links[] = {
+
+	/* FrontEnd DAI Links */
+	{
+		.name = "MSMSTUB Media1",
+		.stream_name = "MultiMedia1",
+		.cpu_dai_name = "MultiMedia1",
+		.platform_name = "msm-pcm-dsp.0",
+		.dynamic = 1,
+		.async_ops = ASYNC_DPCM_SND_SOC_PREPARE,
+		.dpcm_playback = 1,
+		.dpcm_capture = 1,
+		.trigger = {SND_SOC_DPCM_TRIGGER_POST,
+			SND_SOC_DPCM_TRIGGER_POST},
+		.codec_dai_name = "snd-soc-dummy-dai",
+		.codec_name = "snd-soc-dummy",
+		.ignore_suspend = 1,
+		/* this dainlink has playback support */
+		.ignore_pmdown_time = 1,
+		.id = MSM_FRONTEND_DAI_MULTIMEDIA1
+	},
+};
+
+static struct snd_soc_dai_link msm_stub_be_dai_links[] = {
+
+	/* Backend DAI Links */
+	{
+		.name = LPASS_BE_SLIMBUS_0_RX,
+		.stream_name = "Slimbus Playback",
+		.cpu_dai_name = "msm-dai-q6-dev.16384",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-rx",
+		.no_pcm = 1,
+		.dpcm_playback = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_RX,
+		.init = &msm_audrx_stub_init,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_pmdown_time = 1, /* dai link has playback support */
+		.ignore_suspend = 1,
+		.ops = &msm_stub_be_ops,
+	},
+	{
+		.name = LPASS_BE_SLIMBUS_0_TX,
+		.stream_name = "Slimbus Capture",
+		.cpu_dai_name = "msm-dai-q6-dev.16385",
+		.platform_name = "msm-pcm-routing",
+		.codec_name = "msm-stub-codec.1",
+		.codec_dai_name = "msm-stub-tx",
+		.no_pcm = 1,
+		.dpcm_capture = 1,
+		.id = MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		.be_hw_params_fixup = msm_be_hw_params_fixup,
+		.ignore_suspend = 1,
+		.ops = &msm_stub_be_ops,
+	},
+};
+
+static struct snd_soc_dai_link msm_stub_dai_links[
+			 ARRAY_SIZE(msm_stub_fe_dai_links) +
+			 ARRAY_SIZE(msm_stub_be_dai_links)];
+
+struct snd_soc_card snd_soc_card_stub_msm = {
+	.name		= "msmskunk-stub-snd-card",
+};
+
+static const struct of_device_id msmskunk_asoc_machine_of_match[]  = {
+	{ .compatible = "qcom,msmskunk-asoc-snd-tavil",
+	  .data = "tavil_codec"},
+	{ .compatible = "qcom,msmskunk-asoc-snd-stub",
+	  .data = "stub_codec"},
+	{},
+};
+
+static struct snd_soc_card *populate_snd_card_dailinks(struct device *dev)
+{
+	struct snd_soc_card *card = NULL;
+	struct snd_soc_dai_link *dailink;
+	int len_1, len_2, len_3;
+	int total_links;
+	const struct of_device_id *match;
+
+	match = of_match_node(msmskunk_asoc_machine_of_match, dev->of_node);
+	if (!match) {
+		dev_err(dev, "%s: No DT match found for sound card\n",
+			__func__);
+		return NULL;
+	}
+
+	if (!strcmp(match->data, "tavil_codec")) {
+		card = &snd_soc_card_tavil_msm;
+		len_1 = ARRAY_SIZE(msm_common_dai_links);
+		len_2 = len_1 + ARRAY_SIZE(msm_tavil_fe_dai_links);
+		len_3 = len_2 + ARRAY_SIZE(msm_common_be_dai_links);
+		total_links = len_3 + ARRAY_SIZE(msm_tavil_be_dai_links);
+		memcpy(msm_tavil_snd_card_dai_links,
+		       msm_common_dai_links,
+		       sizeof(msm_common_dai_links));
+		memcpy(msm_tavil_snd_card_dai_links + len_1,
+		       msm_tavil_fe_dai_links,
+		       sizeof(msm_tavil_fe_dai_links));
+		memcpy(msm_tavil_snd_card_dai_links + len_2,
+		       msm_common_be_dai_links,
+		       sizeof(msm_common_be_dai_links));
+		memcpy(msm_tavil_snd_card_dai_links + len_3,
+		       msm_tavil_be_dai_links,
+		       sizeof(msm_tavil_be_dai_links));
+
+		if (of_property_read_bool(dev->of_node, "qcom,wcn-btfm")) {
+			dev_dbg(dev, "%s(): WCN BTFM support present\n",
+				__func__);
+			memcpy(msm_tavil_snd_card_dai_links + total_links,
+			       msm_wcn_be_dai_links,
+			       sizeof(msm_wcn_be_dai_links));
+			total_links += ARRAY_SIZE(msm_wcn_be_dai_links);
+		}
+
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,ext-disp-audio-rx")) {
+			dev_dbg(dev, "%s(): ext disp audio support present\n",
+				__func__);
+			memcpy(msm_tavil_snd_card_dai_links + total_links,
+			       ext_disp_be_dai_link,
+			       sizeof(ext_disp_be_dai_link));
+			total_links += ARRAY_SIZE(ext_disp_be_dai_link);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,mi2s-audio-intf")) {
+			memcpy(msm_tavil_snd_card_dai_links + total_links,
+			       msm_mi2s_be_dai_links,
+			       sizeof(msm_mi2s_be_dai_links));
+			total_links += ARRAY_SIZE(msm_mi2s_be_dai_links);
+		}
+		if (of_property_read_bool(dev->of_node,
+					  "qcom,auxpcm-audio-intf")) {
+			memcpy(msm_tavil_snd_card_dai_links + total_links,
+			msm_auxpcm_be_dai_links,
+			sizeof(msm_auxpcm_be_dai_links));
+			total_links += ARRAY_SIZE(msm_auxpcm_be_dai_links);
+		}
+		dailink = msm_tavil_snd_card_dai_links;
+	} else if (!strcmp(match->data, "stub_codec")) {
+		card = &snd_soc_card_stub_msm;
+		len_1 = ARRAY_SIZE(msm_stub_fe_dai_links);
+		len_2 = len_1 + ARRAY_SIZE(msm_stub_be_dai_links);
+
+		memcpy(msm_stub_dai_links,
+		       msm_stub_fe_dai_links,
+		       sizeof(msm_stub_fe_dai_links));
+		memcpy(msm_stub_dai_links + len_1,
+		       msm_stub_be_dai_links,
+		       sizeof(msm_stub_be_dai_links));
+
+		dailink = msm_stub_dai_links;
+		total_links = len_2;
+	}
+
+	if (card) {
+		card->dai_link = dailink;
+		card->num_links = total_links;
+	}
+
+	return card;
+}
+
+static int msm_wsa881x_init(struct snd_soc_component *component)
+{
+	u8 spkleft_ports[WSA881X_MAX_SWR_PORTS] = {100, 101, 102, 106};
+	u8 spkright_ports[WSA881X_MAX_SWR_PORTS] = {103, 104, 105, 107};
+	unsigned int ch_rate[WSA881X_MAX_SWR_PORTS] = {2400, 600, 300, 1200};
+	unsigned int ch_mask[WSA881X_MAX_SWR_PORTS] = {0x1, 0xF, 0x3, 0x3};
+	struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
+	struct msm_asoc_mach_data *pdata;
+	struct snd_soc_dapm_context *dapm;
+	int ret = 0;
+
+	if (!codec) {
+		pr_err("%s codec is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dapm = snd_soc_codec_get_dapm(codec);
+
+	if (!strcmp(component->name_prefix, "SpkrLeft")) {
+		dev_dbg(codec->dev, "%s: setting left ch map to codec %s\n",
+			__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkleft_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrLeft SPKR");
+		}
+	} else if (!strcmp(component->name_prefix, "SpkrRight")) {
+		dev_dbg(codec->dev, "%s: setting right ch map to codec %s\n",
+			__func__, codec->component.name);
+		wsa881x_set_channel_map(codec, &spkright_ports[0],
+				WSA881X_MAX_SWR_PORTS, &ch_mask[0],
+				&ch_rate[0]);
+		if (dapm->component) {
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight IN");
+			snd_soc_dapm_ignore_suspend(dapm, "SpkrRight SPKR");
+		}
+	} else {
+		dev_err(codec->dev, "%s: wrong codec name %s\n", __func__,
+			codec->component.name);
+		ret = -EINVAL;
+		goto err;
+	}
+	pdata = snd_soc_card_get_drvdata(component->card);
+	if (pdata && pdata->codec_root)
+		wsa881x_codec_info_create_codec_entry(pdata->codec_root,
+						      codec);
+
+err:
+	return ret;
+}
+
+static int msm_init_wsa_dev(struct platform_device *pdev,
+				struct snd_soc_card *card)
+{
+	struct device_node *wsa_of_node;
+	u32 wsa_max_devs;
+	u32 wsa_dev_cnt;
+	int i;
+	struct msm_wsa881x_dev_info *wsa881x_dev_info;
+	const char *wsa_auxdev_name_prefix[1];
+	char *dev_name_str = NULL;
+	int found = 0;
+	int ret = 0;
+
+	/* Get maximum WSA device count for this platform */
+	ret = of_property_read_u32(pdev->dev.of_node,
+				   "qcom,wsa-max-devs", &wsa_max_devs);
+	if (ret) {
+		dev_dbg(&pdev->dev,
+			 "%s: wsa-max-devs property missing in DT %s, ret = %d\n",
+			 __func__, pdev->dev.of_node->full_name, ret);
+		goto err;
+	}
+	if (wsa_max_devs == 0) {
+		dev_warn(&pdev->dev,
+			 "%s: Max WSA devices is 0 for this target?\n",
+			 __func__);
+		goto err;
+	}
+
+	/* Get count of WSA device phandles for this platform */
+	wsa_dev_cnt = of_count_phandle_with_args(pdev->dev.of_node,
+						 "qcom,wsa-devs", NULL);
+	if (wsa_dev_cnt == -ENOENT) {
+		dev_warn(&pdev->dev, "%s: No wsa device defined in DT.\n",
+			 __func__);
+		goto err;
+	} else if (wsa_dev_cnt <= 0) {
+		dev_err(&pdev->dev,
+			"%s: Error reading wsa device from DT. wsa_dev_cnt = %d\n",
+			__func__, wsa_dev_cnt);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/*
+	 * Expect total phandles count to be NOT less than maximum possible
+	 * WSA count. However, if it is less, then assign same value to
+	 * max count as well.
+	 */
+	if (wsa_dev_cnt < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: wsa_max_devs = %d cannot exceed wsa_dev_cnt = %d\n",
+			__func__, wsa_max_devs, wsa_dev_cnt);
+		wsa_max_devs = wsa_dev_cnt;
+	}
+
+	/* Make sure prefix string passed for each WSA device */
+	ret = of_property_count_strings(pdev->dev.of_node,
+					"qcom,wsa-aux-dev-prefix");
+	if (ret != wsa_dev_cnt) {
+		dev_err(&pdev->dev,
+			"%s: expecting %d wsa prefix. Defined only %d in DT\n",
+			__func__, wsa_dev_cnt, ret);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	/*
+	 * Alloc mem to store phandle and index info of WSA device, if already
+	 * registered with ALSA core
+	 */
+	wsa881x_dev_info = devm_kcalloc(&pdev->dev, wsa_max_devs,
+					sizeof(struct msm_wsa881x_dev_info),
+					GFP_KERNEL);
+	if (!wsa881x_dev_info) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	/*
+	 * search and check whether all WSA devices are already
+	 * registered with ALSA core or not. If found a node, store
+	 * the node and the index in a local array of struct for later
+	 * use.
+	 */
+	for (i = 0; i < wsa_dev_cnt; i++) {
+		wsa_of_node = of_parse_phandle(pdev->dev.of_node,
+					    "qcom,wsa-devs", i);
+		if (unlikely(!wsa_of_node)) {
+			/* we should not be here */
+			dev_err(&pdev->dev,
+				"%s: wsa dev node is not present\n",
+				__func__);
+			ret = -EINVAL;
+			goto err_free_dev_info;
+		}
+		if (soc_find_component(wsa_of_node, NULL)) {
+			/* WSA device registered with ALSA core */
+			wsa881x_dev_info[found].of_node = wsa_of_node;
+			wsa881x_dev_info[found].index = i;
+			found++;
+			if (found == wsa_max_devs)
+				break;
+		}
+	}
+
+	if (found < wsa_max_devs) {
+		dev_dbg(&pdev->dev,
+			"%s: failed to find %d components. Found only %d\n",
+			__func__, wsa_max_devs, found);
+		return -EPROBE_DEFER;
+	}
+	dev_info(&pdev->dev,
+		"%s: found %d wsa881x devices registered with ALSA core\n",
+		__func__, found);
+
+	card->num_aux_devs = wsa_max_devs;
+	card->num_configs = wsa_max_devs;
+
+	/* Alloc array of AUX devs struct */
+	msm_aux_dev = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+				       sizeof(struct snd_soc_aux_dev),
+				       GFP_KERNEL);
+	if (!msm_aux_dev) {
+		ret = -ENOMEM;
+		goto err_free_dev_info;
+	}
+
+	/* Alloc array of codec conf struct */
+	msm_codec_conf = devm_kcalloc(&pdev->dev, card->num_aux_devs,
+					  sizeof(struct snd_soc_codec_conf),
+					  GFP_KERNEL);
+	if (!msm_codec_conf) {
+		ret = -ENOMEM;
+		goto err_free_aux_dev;
+	}
+
+	for (i = 0; i < card->num_aux_devs; i++) {
+		dev_name_str = devm_kzalloc(&pdev->dev, DEV_NAME_STR_LEN,
+					    GFP_KERNEL);
+		if (!dev_name_str) {
+			ret = -ENOMEM;
+			goto err_free_cdc_conf;
+		}
+
+		ret = of_property_read_string_index(pdev->dev.of_node,
+						    "qcom,wsa-aux-dev-prefix",
+						    wsa881x_dev_info[i].index,
+						    wsa_auxdev_name_prefix);
+		if (ret) {
+			dev_err(&pdev->dev,
+				"%s: failed to read wsa aux dev prefix, ret = %d\n",
+				__func__, ret);
+			ret = -EINVAL;
+			goto err_free_dev_name_str;
+		}
+
+		snprintf(dev_name_str, strlen("wsa881x.%d"), "wsa881x.%d", i);
+		msm_aux_dev[i].name = dev_name_str;
+		msm_aux_dev[i].codec_name = NULL;
+		msm_aux_dev[i].codec_of_node =
+					wsa881x_dev_info[i].of_node;
+		msm_aux_dev[i].init = msm_wsa881x_init;
+		msm_codec_conf[i].dev_name = NULL;
+		msm_codec_conf[i].name_prefix = wsa_auxdev_name_prefix[0];
+		msm_codec_conf[i].of_node =
+				wsa881x_dev_info[i].of_node;
+	}
+	card->codec_conf = msm_codec_conf;
+	card->aux_dev = msm_aux_dev;
+
+	return 0;
+
+err_free_dev_name_str:
+	devm_kfree(&pdev->dev, dev_name_str);
+err_free_cdc_conf:
+	devm_kfree(&pdev->dev, msm_codec_conf);
+err_free_aux_dev:
+	devm_kfree(&pdev->dev, msm_aux_dev);
+err_free_dev_info:
+	devm_kfree(&pdev->dev, wsa881x_dev_info);
+err:
+	return ret;
+}
+
+static void msm_i2s_auxpcm_init(struct platform_device *pdev)
+{
+	struct resource *muxsel;
+	int count;
+	u32 mi2s_master_slave[MI2S_MAX];
+	int ret;
+	char *str[PCM_I2S_SEL_MAX] = {
+		"lpaif_pri_mode_muxsel",
+		"lpaif_sec_mode_muxsel",
+		"lpaif_tert_mode_muxsel",
+		"lpaif_quat_mode_muxsel"
+	};
+
+	for (count = 0; count < MI2S_MAX; count++) {
+		mutex_init(&mi2s_intf_conf[count].lock);
+		mi2s_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < AUX_PCM_MAX; count++) {
+		mutex_init(&auxpcm_intf_conf[count].lock);
+		auxpcm_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		mutex_init(&mi2s_auxpcm_conf[count].lock);
+		mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
+	}
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		muxsel = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+						      str[count]);
+		if (muxsel) {
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr
+				= ioremap(muxsel->start, resource_size(muxsel));
+		}
+	}
+
+	ret = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-mi2s-master",
+			mi2s_master_slave, MI2S_MAX);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: no qcom,msm-mi2s-master in DT node\n",
+			__func__);
+	} else {
+		for (count = 0; count < MI2S_MAX; count++) {
+			mi2s_intf_conf[count].msm_is_mi2s_master =
+				mi2s_master_slave[count];
+		}
+	}
+}
+
+static void msm_i2s_auxpcm_deinit(void)
+{
+	int count;
+
+	for (count = 0; count < PCM_I2S_SEL_MAX; count++) {
+		if (mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr !=
+		    NULL) {
+			iounmap(
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr);
+			mi2s_auxpcm_conf[count].pcm_i2s_sel_vt_addr = NULL;
+		}
+		mutex_destroy(&mi2s_auxpcm_conf[count].lock);
+	}
+
+	for (count = 0; count < AUX_PCM_MAX; count++) {
+		mutex_destroy(&auxpcm_intf_conf[count].lock);
+		auxpcm_intf_conf[count].ref_cnt = 0;
+	}
+
+	for (count = 0; count < MI2S_MAX; count++) {
+		mutex_destroy(&mi2s_intf_conf[count].lock);
+		mi2s_intf_conf[count].ref_cnt = 0;
+		mi2s_intf_conf[count].msm_is_mi2s_master = 0;
+	}
+}
+
+static int msm_asoc_machine_probe(struct platform_device *pdev)
+{
+	struct snd_soc_card *card;
+	struct msm_asoc_mach_data *pdata;
+	const char *mbhc_audio_jack_type = NULL;
+	char *mclk_freq_prop_name;
+	const struct of_device_id *match;
+	int ret;
+
+	if (!pdev->dev.of_node) {
+		dev_err(&pdev->dev, "No platform supplied from device tree\n");
+		return -EINVAL;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			sizeof(struct msm_asoc_mach_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	card = populate_snd_card_dailinks(&pdev->dev);
+	if (!card) {
+		dev_err(&pdev->dev, "%s: Card uninitialized\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+	card->dev = &pdev->dev;
+	platform_set_drvdata(pdev, card);
+	snd_soc_card_set_drvdata(card, pdata);
+
+	ret = snd_soc_of_parse_card_name(card, "qcom,model");
+	if (ret) {
+		dev_err(&pdev->dev, "parse card name failed, err:%d\n",
+			ret);
+		goto err;
+	}
+
+	ret = snd_soc_of_parse_audio_routing(card, "qcom,audio-routing");
+	if (ret) {
+		dev_err(&pdev->dev, "parse audio routing failed, err:%d\n",
+			ret);
+		goto err;
+	}
+
+	match = of_match_node(msmskunk_asoc_machine_of_match,
+			pdev->dev.of_node);
+	if (!match) {
+		dev_err(&pdev->dev, "%s: no matched codec is found.\n",
+			__func__);
+		goto err;
+	}
+
+	mclk_freq_prop_name = "qcom,tavil-mclk-clk-freq";
+
+	ret = of_property_read_u32(pdev->dev.of_node,
+			mclk_freq_prop_name, &pdata->mclk_freq);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Looking up %s property in node %s failed, err%d\n",
+			mclk_freq_prop_name,
+			pdev->dev.of_node->full_name, ret);
+		goto err;
+	}
+
+	if (pdata->mclk_freq != CODEC_EXT_CLK_RATE) {
+		dev_err(&pdev->dev, "unsupported mclk freq %u\n",
+			pdata->mclk_freq);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = msm_populate_dai_link_component_of_node(card);
+	if (ret) {
+		ret = -EPROBE_DEFER;
+		goto err;
+	}
+	ret = msm_init_wsa_dev(pdev, card);
+	if (ret)
+		goto err;
+
+	ret = devm_snd_soc_register_card(&pdev->dev, card);
+	if (ret == -EPROBE_DEFER) {
+		if (codec_reg_done)
+			ret = -EINVAL;
+		goto err;
+	} else if (ret) {
+		dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+			ret);
+		goto err;
+	}
+	dev_info(&pdev->dev, "Sound card %s registered\n", card->name);
+	spdev = pdev;
+
+	ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (ret) {
+		dev_dbg(&pdev->dev, "%s: failed to add child nodes, ret=%d\n",
+			__func__, ret);
+	} else {
+		pdata->hph_en1_gpio_p = of_parse_phandle(pdev->dev.of_node,
+							"qcom,hph-en1-gpio", 0);
+		if (!pdata->hph_en1_gpio_p) {
+			dev_dbg(&pdev->dev, "property %s not detected in node %s",
+				"qcom,hph-en1-gpio",
+				pdev->dev.of_node->full_name);
+		}
+
+		pdata->hph_en0_gpio_p = of_parse_phandle(pdev->dev.of_node,
+							"qcom,hph-en0-gpio", 0);
+		if (!pdata->hph_en0_gpio_p) {
+			dev_dbg(&pdev->dev, "property %s not detected in node %s",
+				"qcom,hph-en0-gpio",
+				pdev->dev.of_node->full_name);
+		}
+	}
+
+	ret = of_property_read_string(pdev->dev.of_node,
+		"qcom,mbhc-audio-jack-type", &mbhc_audio_jack_type);
+	if (ret) {
+		dev_dbg(&pdev->dev, "Looking up %s property in node %s failed",
+			"qcom,mbhc-audio-jack-type",
+			pdev->dev.of_node->full_name);
+		dev_dbg(&pdev->dev, "Jack type properties set to default");
+	} else {
+		if (!strcmp(mbhc_audio_jack_type, "4-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 4 pole jack");
+		else if (!strcmp(mbhc_audio_jack_type, "5-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 5 pole jack");
+		else if (!strcmp(mbhc_audio_jack_type, "6-pole-jack"))
+			dev_dbg(&pdev->dev, "This hardware has 6 pole jack");
+		else
+			dev_dbg(&pdev->dev, "Unknown value, set to default");
+	}
+	/*
+	 * Parse US-Euro gpio info from DT. Report no error if us-euro
+	 * entry is not found in DT file as some targets do not support
+	 * US-Euro detection
+	 */
+	pdata->us_euro_gpio = of_get_named_gpio(pdev->dev.of_node,
+				"qcom,us-euro-gpios", 0);
+	if (!gpio_is_valid(pdata->us_euro_gpio))
+		pdata->us_euro_gpio_p = of_parse_phandle(pdev->dev.of_node,
+					"qcom,us-euro-gpios", 0);
+	if (!gpio_is_valid(pdata->us_euro_gpio) && (!pdata->us_euro_gpio_p)) {
+		dev_dbg(&pdev->dev, "property %s not detected in node %s",
+			"qcom,us-euro-gpios", pdev->dev.of_node->full_name);
+	} else {
+		dev_dbg(&pdev->dev, "%s detected",
+			"qcom,us-euro-gpios");
+		wcd_mbhc_cfg.swap_gnd_mic = msm_swap_gnd_mic;
+	}
+
+	ret = msm_prepare_us_euro(card);
+	if (ret)
+		dev_dbg(&pdev->dev, "msm_prepare_us_euro failed (%d)\n",
+			ret);
+
+	msm_i2s_auxpcm_init(pdev);
+
+	is_initial_boot = true;
+	ret = audio_notifier_register("msmskunk", AUDIO_NOTIFIER_ADSP_DOMAIN,
+				      &service_nb);
+	if (ret < 0)
+		pr_err("%s: Audio notifier register failed ret = %d\n",
+			__func__, ret);
+
+	return 0;
+err:
+	devm_kfree(&pdev->dev, pdata);
+	return ret;
+}
+
+static int msm_asoc_machine_remove(struct platform_device *pdev)
+{
+	struct snd_soc_card *card = platform_get_drvdata(pdev);
+	struct msm_asoc_mach_data *pdata =
+				snd_soc_card_get_drvdata(card);
+
+	if (pdata->us_euro_gpio > 0) {
+		gpio_free(pdata->us_euro_gpio);
+		pdata->us_euro_gpio = 0;
+	}
+	msm_i2s_auxpcm_deinit();
+
+	snd_soc_unregister_card(card);
+	return 0;
+}
+
+static struct platform_driver msmskunk_asoc_machine_driver = {
+	.driver = {
+		.name = DRV_NAME,
+		.owner = THIS_MODULE,
+		.pm = &snd_soc_pm_ops,
+		.of_match_table = msmskunk_asoc_machine_of_match,
+	},
+	.probe = msm_asoc_machine_probe,
+	.remove = msm_asoc_machine_remove,
+};
+module_platform_driver(msmskunk_asoc_machine_driver);
+
+MODULE_DESCRIPTION("ALSA SoC msm");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, msmskunk_asoc_machine_of_match);
diff --git a/sound/soc/msm/qdsp6v2/Makefile b/sound/soc/msm/qdsp6v2/Makefile
new file mode 100644
index 0000000..461c09d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/Makefile
@@ -0,0 +1,21 @@
+snd-soc-qdsp6v2-objs += msm-dai-q6-v2.o msm-pcm-q6-v2.o msm-pcm-routing-v2.o \
+			msm-compress-q6-v2.o msm-compr-q6-v2.o \
+			msm-pcm-afe-v2.o msm-pcm-voip-v2.o \
+			msm-pcm-voice-v2.o msm-dai-q6-hdmi-v2.o \
+			msm-lsm-client.o msm-pcm-host-voice-v2.o \
+			msm-audio-effects-q6-v2.o msm-pcm-loopback-v2.o \
+			msm-dai-slim.o \
+			adsp_err.o
+obj-$(CONFIG_SND_SOC_QDSP6V2) += snd-soc-qdsp6v2.o msm-pcm-dtmf-v2.o \
+				 msm-dai-stub-v2.o
+obj-$(CONFIG_SND_HWDEP) += msm-pcm-routing-devdep.o
+obj-$(CONFIG_DTS_EAGLE) += msm-dts-eagle.o
+obj-$(CONFIG_DOLBY_DAP) += msm-dolby-dap-config.o
+obj-$(CONFIG_DOLBY_DS2) += msm-ds2-dap-config.o
+obj-$(CONFIG_DTS_SRS_TM) += msm-dts-srs-tm-config.o
+obj-$(CONFIG_QTI_PP) += msm-qti-pp-config.o
+obj-y += audio_calibration.o audio_cal_utils.o q6adm.o q6afe.o q6asm.o \
+	q6audio-v2.o q6voice.o q6core.o rtac.o q6lsm.o audio_slimslave.o \
+	msm-pcm-q6-noirq.o
+ocmem-audio-objs += audio_ocmem.o
+obj-$(CONFIG_AUDIO_OCMEM) += ocmem-audio.o
diff --git a/sound/soc/msm/qdsp6v2/adsp_err.c b/sound/soc/msm/qdsp6v2/adsp_err.c
new file mode 100644
index 0000000..d17bd6a
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/adsp_err.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/errno.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <sound/apr_audio-v2.h>
+
+
+/* ERROR STRING */
+/* Success. The operation completed with no errors. */
+#define ADSP_EOK_STR          "ADSP_EOK"
+/* General failure. */
+#define ADSP_EFAILED_STR      "ADSP_EFAILED"
+/* Bad operation parameter. */
+#define ADSP_EBADPARAM_STR    "ADSP_EBADPARAM"
+/* Unsupported routine or operation. */
+#define ADSP_EUNSUPPORTED_STR "ADSP_EUNSUPPORTED"
+/* Unsupported version. */
+#define ADSP_EVERSION_STR     "ADSP_EVERSION"
+/* Unexpected problem encountered. */
+#define ADSP_EUNEXPECTED_STR  "ADSP_EUNEXPECTED"
+/* Unhandled problem occurred. */
+#define ADSP_EPANIC_STR       "ADSP_EPANIC"
+/* Unable to allocate resource. */
+#define ADSP_ENORESOURCE_STR  "ADSP_ENORESOURCE"
+/* Invalid handle. */
+#define ADSP_EHANDLE_STR      "ADSP_EHANDLE"
+/* Operation is already processed. */
+#define ADSP_EALREADY_STR     "ADSP_EALREADY"
+/* Operation is not ready to be processed. */
+#define ADSP_ENOTREADY_STR    "ADSP_ENOTREADY"
+/* Operation is pending completion. */
+#define ADSP_EPENDING_STR     "ADSP_EPENDING"
+/* Operation could not be accepted or processed. */
+#define ADSP_EBUSY_STR        "ADSP_EBUSY"
+/* Operation aborted due to an error. */
+#define ADSP_EABORTED_STR     "ADSP_EABORTED"
+/* Operation preempted by a higher priority. */
+#define ADSP_EPREEMPTED_STR   "ADSP_EPREEMPTED"
+/* Operation requests intervention to complete. */
+#define ADSP_ECONTINUE_STR    "ADSP_ECONTINUE"
+/* Operation requests immediate intervention to complete. */
+#define ADSP_EIMMEDIATE_STR   "ADSP_EIMMEDIATE"
+/* Operation is not implemented. */
+#define ADSP_ENOTIMPL_STR     "ADSP_ENOTIMPL"
+/* Operation needs more data or resources. */
+#define ADSP_ENEEDMORE_STR    "ADSP_ENEEDMORE"
+/* Operation does not have memory. */
+#define ADSP_ENOMEMORY_STR    "ADSP_ENOMEMORY"
+/* Item does not exist. */
+#define ADSP_ENOTEXIST_STR    "ADSP_ENOTEXIST"
+/* Unexpected error code. */
+#define ADSP_ERR_MAX_STR      "ADSP_ERR_MAX"
+
+#ifdef CONFIG_SND_SOC_QDSP_DEBUG
+static bool adsp_err_panic;
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_adsp_err;
+
+static ssize_t adsp_err_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char cmd;
+
+	if (copy_from_user(&cmd, ubuf, 1))
+		return -EFAULT;
+
+	if (cmd == '0')
+		adsp_err_panic = false;
+	else
+		adsp_err_panic = true;
+
+	return cnt;
+}
+
+static const struct file_operations adsp_err_debug_ops = {
+	.write = adsp_err_debug_write,
+};
+#endif
+#endif
+
+struct adsp_err_code {
+	int		lnx_err_code;
+	char	*adsp_err_str;
+};
+
+
+static struct adsp_err_code adsp_err_code_info[ADSP_ERR_MAX+1] = {
+	{ 0, ADSP_EOK_STR},
+	{ -ENOTRECOVERABLE, ADSP_EFAILED_STR},
+	{ -EINVAL, ADSP_EBADPARAM_STR},
+	{ -EOPNOTSUPP, ADSP_EUNSUPPORTED_STR},
+	{ -ENOPROTOOPT, ADSP_EVERSION_STR},
+	{ -ENOTRECOVERABLE, ADSP_EUNEXPECTED_STR},
+	{ -ENOTRECOVERABLE, ADSP_EPANIC_STR},
+	{ -ENOSPC, ADSP_ENORESOURCE_STR},
+	{ -EBADR, ADSP_EHANDLE_STR},
+	{ -EALREADY, ADSP_EALREADY_STR},
+	{ -EPERM, ADSP_ENOTREADY_STR},
+	{ -EINPROGRESS, ADSP_EPENDING_STR},
+	{ -EBUSY, ADSP_EBUSY_STR},
+	{ -ECANCELED, ADSP_EABORTED_STR},
+	{ -EAGAIN, ADSP_EPREEMPTED_STR},
+	{ -EAGAIN, ADSP_ECONTINUE_STR},
+	{ -EAGAIN, ADSP_EIMMEDIATE_STR},
+	{ -EAGAIN, ADSP_ENOTIMPL_STR},
+	{ -ENODATA, ADSP_ENEEDMORE_STR},
+	{ -EADV, ADSP_ERR_MAX_STR},
+	{ -ENOMEM, ADSP_ENOMEMORY_STR},
+	{ -ENODEV, ADSP_ENOTEXIST_STR},
+	{ -EADV, ADSP_ERR_MAX_STR},
+};
+
+#ifdef CONFIG_SND_SOC_QDSP_DEBUG
+static inline void adsp_err_check_panic(u32 adsp_error)
+{
+	if (adsp_err_panic && adsp_error != ADSP_EALREADY)
+		panic("%s: encounter adsp_err=0x%x\n", __func__, adsp_error);
+}
+#else
+static inline void adsp_err_check_panic(u32 adsp_error) {}
+#endif
+
+int adsp_err_get_lnx_err_code(u32 adsp_error)
+{
+	adsp_err_check_panic(adsp_error);
+
+	if (adsp_error > ADSP_ERR_MAX)
+		return adsp_err_code_info[ADSP_ERR_MAX].lnx_err_code;
+	else
+		return adsp_err_code_info[adsp_error].lnx_err_code;
+}
+
+char *adsp_err_get_err_str(u32 adsp_error)
+{
+	if (adsp_error > ADSP_ERR_MAX)
+		return adsp_err_code_info[ADSP_ERR_MAX].adsp_err_str;
+	else
+		return adsp_err_code_info[adsp_error].adsp_err_str;
+}
+
+#if defined(CONFIG_SND_SOC_QDSP_DEBUG) && defined(CONFIG_DEBUG_FS)
+static int __init adsp_err_init(void)
+{
+
+
+	debugfs_adsp_err = debugfs_create_file("msm_adsp_audio_debug",
+					       S_IFREG | 0444, NULL, NULL,
+					       &adsp_err_debug_ops);
+
+	return 0;
+}
+
+device_initcall(adsp_err_init);
+#endif
diff --git a/sound/soc/msm/qdsp6v2/audio_cal_utils.c b/sound/soc/msm/qdsp6v2/audio_cal_utils.c
new file mode 100644
index 0000000..f88087b
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_cal_utils.c
@@ -0,0 +1,965 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <sound/audio_cal_utils.h>
+
+static int unmap_memory(struct cal_type_data *cal_type,
+			struct cal_block_data *cal_block);
+
+size_t get_cal_info_size(int32_t cal_type)
+{
+	size_t size = 0;
+
+	switch (cal_type) {
+	case CVP_VOC_RX_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_voc_top);
+		break;
+	case CVP_VOC_TX_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_voc_top);
+		break;
+	case CVP_VOCPROC_STATIC_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_vocproc);
+		break;
+	case CVP_VOCPROC_DYNAMIC_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_vocvol);
+		break;
+	case CVS_VOCSTRM_STATIC_CAL_TYPE:
+		size = 0;
+		break;
+	case CVP_VOCDEV_CFG_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_vocdev_cfg);
+		break;
+	case CVP_VOCPROC_STATIC_COL_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_voc_col);
+		break;
+	case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_voc_col);
+		break;
+	case CVS_VOCSTRM_STATIC_COL_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_voc_col);
+		break;
+	case ADM_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_adm_top);
+		break;
+	case ADM_CUST_TOPOLOGY_CAL_TYPE:
+	case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE:
+		size = 0;
+		break;
+	case ADM_AUDPROC_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_audproc);
+		break;
+	case ADM_AUDVOL_CAL_TYPE:
+	case ADM_RTAC_AUDVOL_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_audvol);
+		break;
+	case ASM_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_asm_top);
+		break;
+	case ASM_CUST_TOPOLOGY_CAL_TYPE:
+		size = 0;
+		break;
+	case ASM_AUDSTRM_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_audstrm);
+		break;
+	case AFE_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_afe_top);
+		break;
+	case AFE_CUST_TOPOLOGY_CAL_TYPE:
+		size = 0;
+		break;
+	case AFE_COMMON_RX_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_afe);
+		break;
+	case AFE_COMMON_TX_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_afe);
+		break;
+	case AFE_FB_SPKR_PROT_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_spk_prot_cfg);
+		break;
+	case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE:
+		/*
+		 * Since get and set parameter structures are different in size
+		 * use the maximum size of get and set parameter structure
+		 */
+		size = max(sizeof(struct audio_cal_info_sp_th_vi_ftm_cfg),
+			   sizeof(struct audio_cal_info_sp_th_vi_param));
+		break;
+	case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE:
+		/*
+		 * Since get and set parameter structures are different in size
+		 * use the maximum size of get and set parameter structure
+		 */
+		size = max(sizeof(struct audio_cal_info_sp_ex_vi_ftm_cfg),
+			   sizeof(struct audio_cal_info_sp_ex_vi_param));
+		break;
+	case AFE_ANC_CAL_TYPE:
+		size = 0;
+		break;
+	case AFE_AANC_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_aanc);
+		break;
+	case AFE_HW_DELAY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_hw_delay);
+		break;
+	case AFE_SIDETONE_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_sidetone);
+		break;
+	case LSM_CUST_TOPOLOGY_CAL_TYPE:
+		size = 0;
+		break;
+	case LSM_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_lsm_top);
+		break;
+	case ULP_LSM_TOPOLOGY_ID_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_lsm_top);
+		break;
+	case LSM_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_lsm);
+		break;
+	case ADM_RTAC_INFO_CAL_TYPE:
+		size = 0;
+		break;
+	case VOICE_RTAC_INFO_CAL_TYPE:
+		size = 0;
+		break;
+	case ADM_RTAC_APR_CAL_TYPE:
+		size = 0;
+		break;
+	case ASM_RTAC_APR_CAL_TYPE:
+		size = 0;
+		break;
+	case VOICE_RTAC_APR_CAL_TYPE:
+		size = 0;
+		break;
+	case MAD_CAL_TYPE:
+		size = 0;
+		break;
+	case ULP_AFE_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_afe);
+		break;
+	case ULP_LSM_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_lsm);
+		break;
+	case DTS_EAGLE_CAL_TYPE:
+		size = 0;
+		break;
+	case AUDIO_CORE_METAINFO_CAL_TYPE:
+		size = sizeof(struct audio_cal_info_metainfo);
+		break;
+	case SRS_TRUMEDIA_CAL_TYPE:
+		size = 0;
+		break;
+	default:
+		pr_err("%s:Invalid cal type %d!",
+			__func__, cal_type);
+	}
+	return size;
+}
+
+size_t get_user_cal_type_size(int32_t cal_type)
+{
+	size_t size = 0;
+
+	switch (cal_type) {
+	case CVP_VOC_RX_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_voc_top);
+		break;
+	case CVP_VOC_TX_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_voc_top);
+		break;
+	case CVP_VOCPROC_STATIC_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_vocproc);
+		break;
+	case CVP_VOCPROC_DYNAMIC_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_vocvol);
+		break;
+	case CVS_VOCSTRM_STATIC_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_basic);
+		break;
+	case CVP_VOCDEV_CFG_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_vocdev_cfg);
+		break;
+	case CVP_VOCPROC_STATIC_COL_CAL_TYPE:
+	case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE:
+	case CVS_VOCSTRM_STATIC_COL_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_voc_col);
+		break;
+	case ADM_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_adm_top);
+		break;
+	case ADM_CUST_TOPOLOGY_CAL_TYPE:
+	case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_basic);
+		break;
+	case ADM_AUDPROC_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_audproc);
+		break;
+	case ADM_AUDVOL_CAL_TYPE:
+	case ADM_RTAC_AUDVOL_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_audvol);
+		break;
+	case ASM_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_asm_top);
+		break;
+	case ASM_CUST_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_basic);
+		break;
+	case ASM_AUDSTRM_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_audstrm);
+		break;
+	case AFE_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_afe_top);
+		break;
+	case AFE_CUST_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_basic);
+		break;
+	case AFE_COMMON_RX_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_afe);
+		break;
+	case AFE_COMMON_TX_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_afe);
+		break;
+	case AFE_FB_SPKR_PROT_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_fb_spk_prot_cfg);
+		break;
+	case AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE:
+		/*
+		 * Since get and set parameter structures are different in size
+		 * use the maximum size of get and set parameter structure
+		 */
+		size = max(sizeof(struct audio_cal_type_sp_th_vi_ftm_cfg),
+			   sizeof(struct audio_cal_type_sp_th_vi_param));
+		break;
+	case AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE:
+		/*
+		 * Since get and set parameter structures are different in size
+		 * use the maximum size of get and set parameter structure
+		 */
+		size = max(sizeof(struct audio_cal_type_sp_ex_vi_ftm_cfg),
+			   sizeof(struct audio_cal_type_sp_ex_vi_param));
+		break;
+	case AFE_ANC_CAL_TYPE:
+		size = 0;
+		break;
+	case AFE_AANC_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_aanc);
+		break;
+	case AFE_HW_DELAY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_hw_delay);
+		break;
+	case AFE_SIDETONE_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_sidetone);
+		break;
+	case LSM_CUST_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_basic);
+		break;
+	case LSM_TOPOLOGY_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_lsm_top);
+		break;
+	case ULP_LSM_TOPOLOGY_ID_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_lsm_top);
+		break;
+	case LSM_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_lsm);
+		break;
+	case ADM_RTAC_INFO_CAL_TYPE:
+		size = 0;
+		break;
+	case VOICE_RTAC_INFO_CAL_TYPE:
+		size = 0;
+		break;
+	case ADM_RTAC_APR_CAL_TYPE:
+		size = 0;
+		break;
+	case ASM_RTAC_APR_CAL_TYPE:
+		size = 0;
+		break;
+	case VOICE_RTAC_APR_CAL_TYPE:
+		size = 0;
+		break;
+	case MAD_CAL_TYPE:
+		size = 0;
+		break;
+	case ULP_AFE_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_afe);
+		break;
+	case ULP_LSM_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_lsm);
+		break;
+	case DTS_EAGLE_CAL_TYPE:
+		size = 0;
+		break;
+	case AUDIO_CORE_METAINFO_CAL_TYPE:
+		size = sizeof(struct audio_cal_type_metainfo);
+		break;
+	case SRS_TRUMEDIA_CAL_TYPE:
+		size = 0;
+		break;
+	default:
+		pr_err("%s:Invalid cal type %d!",
+			__func__, cal_type);
+	}
+	return size;
+}
+
+int32_t cal_utils_get_cal_type_version(void *cal_type_data)
+{
+	struct audio_cal_type_basic *data = NULL;
+
+	data = (struct audio_cal_type_basic *)cal_type_data;
+
+	return data->cal_hdr.version;
+}
+
+static struct cal_type_data *create_cal_type_data(
+				struct cal_type_info *info)
+{
+	struct cal_type_data	*cal_type = NULL;
+
+	if ((info->reg.cal_type < 0) ||
+		(info->reg.cal_type >= MAX_CAL_TYPES)) {
+		pr_err("%s: cal type %d is Invalid!\n",
+			__func__, info->reg.cal_type);
+		goto done;
+	}
+
+	if (info->cal_util_callbacks.match_block == NULL) {
+		pr_err("%s: cal type %d no method to match blocks!\n",
+			__func__, info->reg.cal_type);
+		goto done;
+	}
+
+	cal_type = kmalloc(sizeof(*cal_type), GFP_KERNEL);
+	if (cal_type == NULL)
+		goto done;
+
+	INIT_LIST_HEAD(&cal_type->cal_blocks);
+	mutex_init(&cal_type->lock);
+	memcpy(&cal_type->info, info,
+		sizeof(cal_type->info));
+done:
+	return cal_type;
+}
+
+int cal_utils_create_cal_types(int num_cal_types,
+			struct cal_type_data **cal_type,
+			struct cal_type_info *info)
+{
+	int ret = 0;
+	int i;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if (info == NULL) {
+		pr_err("%s: info is NULL!\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if ((num_cal_types <= 0) ||
+		(num_cal_types > MAX_CAL_TYPES)) {
+		pr_err("%s: num_cal_types of %d is Invalid!\n",
+			__func__, num_cal_types);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	for (i = 0; i < num_cal_types; i++) {
+		if ((info[i].reg.cal_type < 0) ||
+			(info[i].reg.cal_type >= MAX_CAL_TYPES)) {
+			pr_err("%s: cal type %d at index %d is Invalid!\n",
+				__func__, info[i].reg.cal_type, i);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		cal_type[i] = create_cal_type_data(&info[i]);
+		if (cal_type[i] == NULL) {
+			pr_err("%s: Could not allocate cal_type of index %d!\n",
+				__func__, i);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		ret = audio_cal_register(1, &info[i].reg);
+		if (ret < 0) {
+			pr_err("%s: audio_cal_register failed, ret = %d!\n",
+				__func__, ret);
+			ret = -EINVAL;
+			goto done;
+		}
+		pr_debug("%s: cal type %d at index %d!\n",
+			__func__, info[i].reg.cal_type, i);
+	}
+done:
+	return ret;
+}
+
+static void delete_cal_block(struct cal_block_data *cal_block)
+{
+	pr_debug("%s\n", __func__);
+
+	if (cal_block == NULL)
+		goto done;
+
+	list_del(&cal_block->list);
+	kfree(cal_block->client_info);
+	cal_block->client_info = NULL;
+	kfree(cal_block->cal_info);
+	cal_block->cal_info = NULL;
+	if (cal_block->map_data.ion_client  != NULL) {
+		msm_audio_ion_free(cal_block->map_data.ion_client,
+			cal_block->map_data.ion_handle);
+		cal_block->map_data.ion_client = NULL;
+		cal_block->map_data.ion_handle = NULL;
+	}
+	kfree(cal_block);
+done:
+	return;
+}
+
+static void destroy_all_cal_blocks(struct cal_type_data *cal_type)
+{
+	int ret = 0;
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block;
+
+	list_for_each_safe(ptr, next,
+		&cal_type->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		ret = unmap_memory(cal_type, cal_block);
+		if (ret < 0) {
+			pr_err("%s: unmap_memory failed, cal type %d, ret = %d!\n",
+				__func__,
+			       cal_type->info.reg.cal_type,
+				ret);
+		}
+		delete_cal_block(cal_block);
+		cal_block = NULL;
+	}
+}
+
+static void destroy_cal_type_data(struct cal_type_data *cal_type)
+{
+	if (cal_type == NULL)
+		goto done;
+
+	destroy_all_cal_blocks(cal_type);
+	list_del(&cal_type->cal_blocks);
+	kfree(cal_type);
+done:
+	return;
+}
+
+void cal_utils_destroy_cal_types(int num_cal_types,
+			struct cal_type_data **cal_type)
+{
+	int i;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n", __func__);
+		goto done;
+	} else if ((num_cal_types <= 0) ||
+		(num_cal_types > MAX_CAL_TYPES)) {
+		pr_err("%s: num_cal_types of %d is Invalid!\n",
+			__func__, num_cal_types);
+		goto done;
+	}
+
+	for (i = 0; i < num_cal_types; i++) {
+		audio_cal_deregister(1, &cal_type[i]->info.reg);
+		destroy_cal_type_data(cal_type[i]);
+		cal_type[i] = NULL;
+	}
+done:
+	return;
+}
+
+struct cal_block_data *cal_utils_get_only_cal_block(
+			struct cal_type_data *cal_type)
+{
+	struct list_head		*ptr, *next;
+	struct cal_block_data		*cal_block = NULL;
+
+	if (cal_type == NULL)
+		goto done;
+
+	list_for_each_safe(ptr, next,
+		&cal_type->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+		break;
+	}
+done:
+	return cal_block;
+}
+
+bool cal_utils_match_buf_num(struct cal_block_data *cal_block,
+					void *user_data)
+{
+	bool ret = false;
+	struct audio_cal_type_basic	*data = user_data;
+
+	if (cal_block->buffer_number == data->cal_hdr.buffer_number)
+		ret = true;
+
+	return ret;
+}
+
+static struct cal_block_data *get_matching_cal_block(
+					struct cal_type_data *cal_type,
+					void *data)
+{
+	struct list_head		*ptr, *next;
+	struct cal_block_data		*cal_block = NULL;
+
+	list_for_each_safe(ptr, next,
+		&cal_type->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		if (cal_type->info.cal_util_callbacks.
+			match_block(cal_block, data))
+			return cal_block;
+	}
+
+	return NULL;
+}
+
+static int cal_block_ion_alloc(struct cal_block_data *cal_block)
+{
+	int	ret = 0;
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_audio_ion_import("audio_cal_client",
+		&cal_block->map_data.ion_client,
+		&cal_block->map_data.ion_handle,
+		cal_block->map_data.ion_map_handle,
+		NULL, 0,
+		&cal_block->cal_data.paddr,
+		&cal_block->map_data.map_size,
+		&cal_block->cal_data.kvaddr);
+	if (ret) {
+		pr_err("%s: audio ION import failed, rc = %d\n",
+			__func__, ret);
+		ret = -ENOMEM;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static struct cal_block_data *create_cal_block(struct cal_type_data *cal_type,
+				struct audio_cal_type_basic *basic_cal,
+				size_t client_info_size, void *client_info)
+{
+	struct cal_block_data	*cal_block = NULL;
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n", __func__);
+		goto done;
+	} else if (basic_cal == NULL) {
+		pr_err("%s: basic_cal is NULL!\n", __func__);
+		goto done;
+	}
+
+	cal_block = kzalloc(sizeof(*cal_block),
+		GFP_KERNEL);
+	if (cal_block == NULL)
+		goto done;
+
+	INIT_LIST_HEAD(&cal_block->list);
+	list_add_tail(&cal_block->list, &cal_type->cal_blocks);
+
+	cal_block->map_data.ion_map_handle = basic_cal->cal_data.mem_handle;
+	if (basic_cal->cal_data.mem_handle > 0) {
+		if (cal_block_ion_alloc(cal_block)) {
+			pr_err("%s: cal_block_ion_alloc failed!\n",
+				__func__);
+			goto err;
+		}
+	}
+	if (client_info_size > 0) {
+		cal_block->client_info_size = client_info_size;
+		cal_block->client_info = kmalloc(client_info_size, GFP_KERNEL);
+		if (cal_block->client_info == NULL) {
+			pr_err("%s: could not allocats client_info!\n",
+				__func__);
+			goto err;
+		}
+		if (client_info != NULL)
+			memcpy(cal_block->client_info, client_info,
+				client_info_size);
+	}
+
+	cal_block->cal_info = kzalloc(
+		get_cal_info_size(cal_type->info.reg.cal_type),
+		GFP_KERNEL);
+	if (cal_block->cal_info == NULL) {
+		pr_err("%s: could not allocats cal_info!\n",
+			__func__);
+		goto err;
+	}
+	cal_block->buffer_number = basic_cal->cal_hdr.buffer_number;
+	pr_debug("%s: created block for cal type %d, buf num %d, map handle %d, map size %zd paddr 0x%pK!\n",
+		__func__, cal_type->info.reg.cal_type,
+		cal_block->buffer_number,
+		cal_block->map_data.ion_map_handle,
+		cal_block->map_data.map_size,
+		&cal_block->cal_data.paddr);
+done:
+	return cal_block;
+err:
+	kfree(cal_block);
+	cal_block = NULL;
+	return cal_block;
+}
+
+void cal_utils_clear_cal_block_q6maps(int num_cal_types,
+					struct cal_type_data **cal_type)
+{
+	int i = 0;
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n", __func__);
+		goto done;
+	} else if ((num_cal_types <= 0) ||
+		(num_cal_types > MAX_CAL_TYPES)) {
+		pr_err("%s: num_cal_types of %d is Invalid!\n",
+			__func__, num_cal_types);
+		goto done;
+	}
+
+	for (; i < num_cal_types; i++) {
+		if (cal_type[i] == NULL)
+			continue;
+
+		mutex_lock(&cal_type[i]->lock);
+		list_for_each_safe(ptr, next,
+			&cal_type[i]->cal_blocks) {
+
+			cal_block = list_entry(ptr,
+				struct cal_block_data, list);
+
+			cal_block->map_data.q6map_handle = 0;
+		}
+		mutex_unlock(&cal_type[i]->lock);
+	}
+done:
+	return;
+}
+
+
+
+static int realloc_memory(struct cal_block_data *cal_block)
+{
+	int ret = 0;
+
+	msm_audio_ion_free(cal_block->map_data.ion_client,
+		cal_block->map_data.ion_handle);
+	cal_block->map_data.ion_client = NULL;
+	cal_block->map_data.ion_handle = NULL;
+	cal_block->cal_data.size = 0;
+
+	ret = cal_block_ion_alloc(cal_block);
+	if (ret < 0)
+		pr_err("%s: realloc_memory failed!\n",
+			__func__);
+	return ret;
+}
+
+static int map_memory(struct cal_type_data *cal_type,
+			struct cal_block_data *cal_block)
+{
+	int ret = 0;
+
+
+	if (cal_type->info.cal_util_callbacks.map_cal != NULL) {
+		if ((cal_block->map_data.ion_map_handle < 0) ||
+			(cal_block->map_data.map_size <= 0) ||
+			(cal_block->map_data.q6map_handle != 0)) {
+			goto done;
+		}
+
+		pr_debug("%s: cal type %d call map\n",
+			__func__, cal_type->info.reg.cal_type);
+		ret = cal_type->info.cal_util_callbacks.
+			map_cal(cal_type->info.reg.cal_type, cal_block);
+		if (ret < 0) {
+			pr_err("%s: map_cal failed, cal type %d, ret = %d!\n",
+				__func__, cal_type->info.reg.cal_type,
+				ret);
+			goto done;
+		}
+	}
+done:
+	return ret;
+}
+
+static int unmap_memory(struct cal_type_data *cal_type,
+			struct cal_block_data *cal_block)
+{
+	int ret = 0;
+
+	if (cal_type->info.cal_util_callbacks.unmap_cal != NULL) {
+		if ((cal_block->map_data.ion_map_handle < 0) ||
+			(cal_block->map_data.map_size <= 0) ||
+			(cal_block->map_data.q6map_handle == 0)) {
+			goto done;
+		}
+		pr_debug("%s: cal type %d call unmap\n",
+			__func__, cal_type->info.reg.cal_type);
+		ret = cal_type->info.cal_util_callbacks.
+			unmap_cal(cal_type->info.reg.cal_type, cal_block);
+		if (ret < 0) {
+			pr_err("%s: unmap_cal failed, cal type %d, ret = %d!\n",
+				__func__, cal_type->info.reg.cal_type,
+				ret);
+			goto done;
+		}
+	}
+done:
+	return ret;
+}
+
+int cal_utils_alloc_cal(size_t data_size, void *data,
+			struct cal_type_data *cal_type,
+			size_t client_info_size, void *client_info)
+{
+	int ret = 0;
+	struct cal_block_data *cal_block;
+	struct audio_cal_type_alloc *alloc_data = data;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (data_size < sizeof(struct audio_cal_type_alloc)) {
+		pr_err("%s: data_size of %zd does not equal alloc struct size of %zd!\n",
+			__func__, data_size,
+		       sizeof(struct audio_cal_type_alloc));
+		ret = -EINVAL;
+		goto done;
+	}
+	if ((client_info_size > 0) && (client_info == NULL)) {
+		pr_err("%s: User info pointer is NULL but size is %zd!\n",
+			__func__, client_info_size);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (alloc_data->cal_data.mem_handle < 0) {
+		pr_err("%s: mem_handle %d invalid!\n",
+			__func__, alloc_data->cal_data.mem_handle);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&cal_type->lock);
+
+	cal_block = get_matching_cal_block(cal_type,
+		data);
+	if (cal_block != NULL) {
+		ret = unmap_memory(cal_type, cal_block);
+		if (ret < 0)
+			goto err;
+		ret = realloc_memory(cal_block);
+		if (ret < 0)
+			goto err;
+	} else {
+		cal_block = create_cal_block(cal_type,
+			(struct audio_cal_type_basic *)alloc_data,
+			client_info_size, client_info);
+		if (cal_block == NULL) {
+			pr_err("%s: create_cal_block failed for %d!\n",
+				__func__, alloc_data->cal_data.mem_handle);
+			ret = -EINVAL;
+			goto err;
+		}
+	}
+
+	ret = map_memory(cal_type, cal_block);
+	if (ret < 0)
+		goto err;
+err:
+	mutex_unlock(&cal_type->lock);
+done:
+	return ret;
+}
+
+int cal_utils_dealloc_cal(size_t data_size, void *data,
+			struct cal_type_data *cal_type)
+{
+	int ret = 0;
+	struct cal_block_data *cal_block;
+	struct audio_cal_type_dealloc *dealloc_data = data;
+
+	pr_debug("%s\n", __func__);
+
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (data_size < sizeof(struct audio_cal_type_dealloc)) {
+		pr_err("%s: data_size of %zd does not equal struct size of %zd!\n",
+			__func__, data_size,
+			sizeof(struct audio_cal_type_dealloc));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((dealloc_data->cal_data.mem_handle == -1) &&
+		(dealloc_data->cal_hdr.buffer_number == ALL_CAL_BLOCKS)) {
+		destroy_all_cal_blocks(cal_type);
+		goto done;
+	}
+
+	if (dealloc_data->cal_data.mem_handle < 0) {
+		pr_err("%s: mem_handle %d invalid!\n",
+			__func__, dealloc_data->cal_data.mem_handle);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&cal_type->lock);
+	cal_block = get_matching_cal_block(
+		cal_type,
+		data);
+	if (cal_block == NULL) {
+		pr_err("%s: allocation does not exist for %d!\n",
+			__func__, dealloc_data->cal_data.mem_handle);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	ret = unmap_memory(cal_type, cal_block);
+	if (ret < 0)
+		goto err;
+
+	delete_cal_block(cal_block);
+err:
+	mutex_unlock(&cal_type->lock);
+done:
+	return ret;
+}
+
+int cal_utils_set_cal(size_t data_size, void *data,
+			struct cal_type_data *cal_type,
+			size_t client_info_size, void *client_info)
+{
+	int ret = 0;
+	struct cal_block_data *cal_block;
+	struct audio_cal_type_basic *basic_data = data;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type == NULL) {
+		pr_err("%s: cal_type is NULL!\n",
+			__func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((client_info_size > 0) && (client_info == NULL)) {
+		pr_err("%s: User info pointer is NULL but size is %zd!\n",
+			__func__, client_info_size);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((data_size > get_user_cal_type_size(
+		cal_type->info.reg.cal_type)) || (data_size < 0)) {
+		pr_err("%s: cal_type %d, data_size of %zd is invalid, expecting %zd!\n",
+			__func__, cal_type->info.reg.cal_type, data_size,
+			get_user_cal_type_size(cal_type->info.reg.cal_type));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&cal_type->lock);
+	cal_block = get_matching_cal_block(
+		cal_type,
+		data);
+	if (cal_block == NULL) {
+		if (basic_data->cal_data.mem_handle > 0) {
+			pr_err("%s: allocation does not exist for %d!\n",
+				__func__, basic_data->cal_data.mem_handle);
+			ret = -EINVAL;
+			goto err;
+		} else {
+			cal_block = create_cal_block(
+				cal_type,
+				basic_data,
+				client_info_size, client_info);
+			if (cal_block == NULL) {
+				pr_err("%s: create_cal_block failed for cal type %d!\n",
+					__func__,
+				       cal_type->info.reg.cal_type);
+				ret = -EINVAL;
+				goto err;
+			}
+		}
+	}
+
+	ret = map_memory(cal_type, cal_block);
+	if (ret < 0)
+		goto err;
+
+	cal_block->cal_data.size = basic_data->cal_data.cal_size;
+
+	if (client_info_size > 0) {
+		memcpy(cal_block->client_info,
+			client_info,
+			client_info_size);
+	}
+
+	memcpy(cal_block->cal_info,
+		((uint8_t *)data + sizeof(struct audio_cal_type_basic)),
+		data_size - sizeof(struct audio_cal_type_basic));
+
+err:
+	mutex_unlock(&cal_type->lock);
+done:
+	return ret;
+}
diff --git a/sound/soc/msm/qdsp6v2/audio_calibration.c b/sound/soc/msm/qdsp6v2/audio_calibration.c
new file mode 100644
index 0000000..808a0e4
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_calibration.c
@@ -0,0 +1,636 @@
+/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/msm_ion.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/audio_calibration.h>
+#include <sound/audio_cal_utils.h>
+
+struct audio_cal_client_info {
+	struct list_head		list;
+	struct audio_cal_callbacks	*callbacks;
+};
+
+struct audio_cal_info {
+	struct mutex			common_lock;
+	struct mutex			cal_mutex[MAX_CAL_TYPES];
+	struct list_head		client_info[MAX_CAL_TYPES];
+	int				ref_count;
+};
+
+static struct audio_cal_info	audio_cal;
+
+
+static bool callbacks_are_equal(struct audio_cal_callbacks *callback1,
+				struct audio_cal_callbacks *callback2)
+{
+	bool ret = true;
+	struct audio_cal_callbacks *call1 = callback1;
+	struct audio_cal_callbacks *call2 = callback2;
+
+	pr_debug("%s\n", __func__);
+
+	if ((call1 == NULL) && (call2 == NULL))
+		ret = true;
+	else if ((call1 == NULL) || (call2 == NULL))
+		ret = false;
+	else if ((call1->alloc != call2->alloc) ||
+		(call1->dealloc != call2->dealloc) ||
+		(call1->pre_cal != call2->pre_cal) ||
+		(call1->set_cal != call2->set_cal) ||
+		(call1->get_cal != call2->get_cal) ||
+		(call1->post_cal != call2->post_cal))
+		ret = false;
+	return ret;
+}
+
+int audio_cal_deregister(int num_cal_types,
+			 struct audio_cal_reg *reg_data)
+{
+	int ret = 0;
+	int i = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (reg_data == NULL) {
+		pr_err("%s: reg_data is NULL!\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if ((num_cal_types <= 0) ||
+		(num_cal_types > MAX_CAL_TYPES)) {
+		pr_err("%s: num_cal_types of %d is Invalid!\n",
+			__func__, num_cal_types);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	for (; i < num_cal_types; i++) {
+		if ((reg_data[i].cal_type < 0) ||
+			(reg_data[i].cal_type >= MAX_CAL_TYPES)) {
+			pr_err("%s: cal type %d at index %d is Invalid!\n",
+				__func__, reg_data[i].cal_type, i);
+			ret = -EINVAL;
+			continue;
+		}
+
+		mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+		list_for_each_safe(ptr, next,
+			&audio_cal.client_info[reg_data[i].cal_type]) {
+
+			client_info_node = list_entry(ptr,
+				struct audio_cal_client_info, list);
+			if (callbacks_are_equal(client_info_node->callbacks,
+				&reg_data[i].callbacks)) {
+				list_del(&client_info_node->list);
+				kfree(client_info_node->callbacks);
+				client_info_node->callbacks = NULL;
+				kfree(client_info_node);
+				client_info_node = NULL;
+				break;
+			}
+		}
+		mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+	}
+done:
+	return ret;
+}
+
+
+int audio_cal_register(int num_cal_types,
+			 struct audio_cal_reg *reg_data)
+{
+	int ret = 0;
+	int i = 0;
+	struct audio_cal_client_info *client_info_node = NULL;
+	struct audio_cal_callbacks *callback_node = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (reg_data == NULL) {
+		pr_err("%s: callbacks are NULL!\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if ((num_cal_types <= 0) ||
+		(num_cal_types > MAX_CAL_TYPES)) {
+		pr_err("%s: num_cal_types of %d is Invalid!\n",
+			__func__, num_cal_types);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	for (; i < num_cal_types; i++) {
+		if ((reg_data[i].cal_type < 0) ||
+			(reg_data[i].cal_type >= MAX_CAL_TYPES)) {
+			pr_err("%s: cal type %d at index %d is Invalid!\n",
+				__func__, reg_data[i].cal_type, i);
+			ret = -EINVAL;
+			goto err;
+		}
+
+		client_info_node = kmalloc(sizeof(*client_info_node),
+			GFP_KERNEL);
+		if (client_info_node == NULL) {
+			ret = -ENOMEM;
+			goto err;
+		}
+		INIT_LIST_HEAD(&client_info_node->list);
+
+		callback_node = kmalloc(sizeof(*callback_node),
+			GFP_KERNEL);
+		if (callback_node == NULL) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		memcpy(callback_node, &reg_data[i].callbacks,
+			sizeof(*callback_node));
+		client_info_node->callbacks = callback_node;
+
+		mutex_lock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+		list_add_tail(&client_info_node->list,
+			&audio_cal.client_info[reg_data[i].cal_type]);
+		mutex_unlock(&audio_cal.cal_mutex[reg_data[i].cal_type]);
+	}
+done:
+	return ret;
+err:
+	audio_cal_deregister(num_cal_types, reg_data);
+	return ret;
+}
+
+static int call_allocs(int32_t cal_type,
+				size_t cal_type_size, void *data)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	list_for_each_safe(ptr, next,
+			&audio_cal.client_info[cal_type]) {
+
+		client_info_node = list_entry(ptr,
+			struct audio_cal_client_info, list);
+
+		if (client_info_node->callbacks->alloc == NULL)
+			continue;
+
+		ret2 = client_info_node->callbacks->
+			alloc(cal_type, cal_type_size, data);
+		if (ret2 < 0) {
+			pr_err("%s: alloc failed!\n", __func__);
+			ret = ret2;
+		}
+	}
+	return ret;
+}
+
+static int call_deallocs(int32_t cal_type,
+				size_t cal_type_size, void *data)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s cal type %d\n", __func__, cal_type);
+
+	list_for_each_safe(ptr, next,
+			&audio_cal.client_info[cal_type]) {
+
+		client_info_node = list_entry(ptr,
+			struct audio_cal_client_info, list);
+
+		if (client_info_node->callbacks->dealloc == NULL)
+			continue;
+
+		ret2 = client_info_node->callbacks->
+			dealloc(cal_type, cal_type_size, data);
+		if (ret2 < 0) {
+			pr_err("%s: dealloc failed!\n", __func__);
+			ret = ret2;
+		}
+	}
+	return ret;
+}
+
+static int call_pre_cals(int32_t cal_type,
+				size_t cal_type_size, void *data)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s cal type %d\n", __func__, cal_type);
+
+	list_for_each_safe(ptr, next,
+			&audio_cal.client_info[cal_type]) {
+
+		client_info_node = list_entry(ptr,
+			struct audio_cal_client_info, list);
+
+		if (client_info_node->callbacks->pre_cal == NULL)
+			continue;
+
+		ret2 = client_info_node->callbacks->
+			pre_cal(cal_type, cal_type_size, data);
+		if (ret2 < 0) {
+			pr_err("%s: pre_cal failed!\n", __func__);
+			ret = ret2;
+		}
+	}
+	return ret;
+}
+
+static int call_post_cals(int32_t cal_type,
+				size_t cal_type_size, void *data)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s cal type %d\n", __func__, cal_type);
+
+	list_for_each_safe(ptr, next,
+			&audio_cal.client_info[cal_type]) {
+
+		client_info_node = list_entry(ptr,
+			struct audio_cal_client_info, list);
+
+		if (client_info_node->callbacks->post_cal == NULL)
+			continue;
+
+		ret2 = client_info_node->callbacks->
+			post_cal(cal_type, cal_type_size, data);
+		if (ret2 < 0) {
+			pr_err("%s: post_cal failed!\n", __func__);
+			ret = ret2;
+		}
+	}
+	return ret;
+}
+
+static int call_set_cals(int32_t cal_type,
+				size_t cal_type_size, void *data)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s cal type %d\n", __func__, cal_type);
+
+	list_for_each_safe(ptr, next,
+			&audio_cal.client_info[cal_type]) {
+
+		client_info_node = list_entry(ptr,
+			struct audio_cal_client_info, list);
+
+		if (client_info_node->callbacks->set_cal == NULL)
+			continue;
+
+		ret2 = client_info_node->callbacks->
+			set_cal(cal_type, cal_type_size, data);
+		if (ret2 < 0) {
+			pr_err("%s: set_cal failed!\n", __func__);
+			ret = ret2;
+		}
+	}
+	return ret;
+}
+
+static int call_get_cals(int32_t cal_type,
+				size_t cal_type_size, void *data)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node = NULL;
+
+	pr_debug("%s cal type %d\n", __func__, cal_type);
+
+	list_for_each_safe(ptr, next,
+			&audio_cal.client_info[cal_type]) {
+
+		client_info_node = list_entry(ptr,
+			struct audio_cal_client_info, list);
+
+		if (client_info_node->callbacks->get_cal == NULL)
+			continue;
+
+		ret2 = client_info_node->callbacks->
+			get_cal(cal_type, cal_type_size, data);
+		if (ret2 < 0) {
+			pr_err("%s: get_cal failed!\n", __func__);
+			ret = ret2;
+		}
+	}
+	return ret;
+}
+
+static int audio_cal_open(struct inode *inode, struct file *f)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&audio_cal.common_lock);
+	audio_cal.ref_count++;
+	mutex_unlock(&audio_cal.common_lock);
+
+	return ret;
+}
+
+static void dealloc_all_clients(void)
+{
+	int i = 0;
+	struct audio_cal_type_dealloc dealloc_data;
+
+	pr_debug("%s\n", __func__);
+
+	dealloc_data.cal_hdr.version = VERSION_0_0;
+	dealloc_data.cal_hdr.buffer_number = ALL_CAL_BLOCKS;
+	dealloc_data.cal_data.mem_handle = -1;
+
+	for (; i < MAX_CAL_TYPES; i++)
+		call_deallocs(i, sizeof(dealloc_data), &dealloc_data);
+}
+
+static int audio_cal_release(struct inode *inode, struct file *f)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&audio_cal.common_lock);
+	audio_cal.ref_count--;
+	if (audio_cal.ref_count <= 0) {
+		audio_cal.ref_count = 0;
+		dealloc_all_clients();
+	}
+	mutex_unlock(&audio_cal.common_lock);
+
+	return ret;
+}
+
+static long audio_cal_shared_ioctl(struct file *file, unsigned int cmd,
+							void __user *arg)
+{
+	int ret = 0;
+	int32_t size;
+	struct audio_cal_basic *data = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	switch (cmd) {
+	case AUDIO_ALLOCATE_CALIBRATION:
+	case AUDIO_DEALLOCATE_CALIBRATION:
+	case AUDIO_PREPARE_CALIBRATION:
+	case AUDIO_SET_CALIBRATION:
+	case AUDIO_GET_CALIBRATION:
+	case AUDIO_POST_CALIBRATION:
+		break;
+	default:
+		pr_err("%s: ioctl not found!\n", __func__);
+		ret = -EFAULT;
+		goto done;
+	}
+
+	if (copy_from_user(&size, (void *)arg, sizeof(size))) {
+		pr_err("%s: Could not copy size value from user\n", __func__);
+		ret = -EFAULT;
+		goto done;
+	} else if ((size < sizeof(struct audio_cal_basic))
+		|| (size > MAX_IOCTL_CMD_SIZE)) {
+		pr_err("%s: Invalid size sent to driver: %d, max size is %d, min size is %zd\n",
+			__func__, size, MAX_IOCTL_CMD_SIZE,
+			sizeof(struct audio_cal_basic));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	data = kmalloc(size, GFP_KERNEL);
+	if (data == NULL) {
+		ret = -ENOMEM;
+		goto done;
+	} else if (copy_from_user(data, (void *)arg, size)) {
+		pr_err("%s: Could not copy data from user\n",
+			__func__);
+		ret = -EFAULT;
+		goto done;
+	} else if ((data->hdr.cal_type < 0) ||
+		(data->hdr.cal_type >= MAX_CAL_TYPES)) {
+		pr_err("%s: cal type %d is Invalid!\n",
+			__func__, data->hdr.cal_type);
+		ret = -EINVAL;
+		goto done;
+	} else if ((data->hdr.cal_type_size <
+		sizeof(struct audio_cal_type_basic)) ||
+		(data->hdr.cal_type_size >
+		get_user_cal_type_size(data->hdr.cal_type))) {
+		pr_err("%s: cal type size %d is Invalid! Max is %zd!\n",
+			__func__, data->hdr.cal_type_size,
+			get_user_cal_type_size(data->hdr.cal_type));
+		ret = -EINVAL;
+		goto done;
+	} else if (data->cal_type.cal_hdr.buffer_number < 0) {
+		pr_err("%s: cal type %d Invalid buffer number %d!\n",
+			__func__, data->hdr.cal_type,
+			data->cal_type.cal_hdr.buffer_number);
+		ret = -EINVAL;
+		goto done;
+	}
+
+
+	mutex_lock(&audio_cal.cal_mutex[data->hdr.cal_type]);
+
+	switch (cmd) {
+	case AUDIO_ALLOCATE_CALIBRATION:
+		ret = call_allocs(data->hdr.cal_type,
+			data->hdr.cal_type_size, &data->cal_type);
+		break;
+	case AUDIO_DEALLOCATE_CALIBRATION:
+		ret = call_deallocs(data->hdr.cal_type,
+			data->hdr.cal_type_size, &data->cal_type);
+		break;
+	case AUDIO_PREPARE_CALIBRATION:
+		ret = call_pre_cals(data->hdr.cal_type,
+			data->hdr.cal_type_size, &data->cal_type);
+		break;
+	case AUDIO_SET_CALIBRATION:
+		ret = call_set_cals(data->hdr.cal_type,
+			data->hdr.cal_type_size, &data->cal_type);
+		break;
+	case AUDIO_GET_CALIBRATION:
+		ret = call_get_cals(data->hdr.cal_type,
+			data->hdr.cal_type_size, &data->cal_type);
+		break;
+	case AUDIO_POST_CALIBRATION:
+		ret = call_post_cals(data->hdr.cal_type,
+			data->hdr.cal_type_size, &data->cal_type);
+		break;
+	}
+
+	if (cmd == AUDIO_GET_CALIBRATION) {
+		if (data->hdr.cal_type_size == 0)
+			goto unlock;
+		if (data == NULL)
+			goto unlock;
+		if ((sizeof(data->hdr) + data->hdr.cal_type_size) > size) {
+			pr_err("%s: header size %zd plus cal type size %d are greater than data buffer size %d\n",
+				__func__, sizeof(data->hdr),
+				data->hdr.cal_type_size, size);
+			ret = -EFAULT;
+			goto unlock;
+		} else if (copy_to_user((void *)arg, data,
+			sizeof(data->hdr) + data->hdr.cal_type_size)) {
+			pr_err("%s: Could not copy cal type to user\n",
+				__func__);
+			ret = -EFAULT;
+			goto unlock;
+		}
+	}
+
+unlock:
+	mutex_unlock(&audio_cal.cal_mutex[data->hdr.cal_type]);
+done:
+	kfree(data);
+	return ret;
+}
+
+static long audio_cal_ioctl(struct file *f,
+		unsigned int cmd, unsigned long arg)
+{
+	return audio_cal_shared_ioctl(f, cmd, (void __user *)arg);
+}
+
+#ifdef CONFIG_COMPAT
+
+#define AUDIO_ALLOCATE_CALIBRATION32	_IOWR(CAL_IOCTL_MAGIC, \
+							200, compat_uptr_t)
+#define AUDIO_DEALLOCATE_CALIBRATION32	_IOWR(CAL_IOCTL_MAGIC, \
+							201, compat_uptr_t)
+#define AUDIO_PREPARE_CALIBRATION32	_IOWR(CAL_IOCTL_MAGIC, \
+							202, compat_uptr_t)
+#define AUDIO_SET_CALIBRATION32		_IOWR(CAL_IOCTL_MAGIC, \
+							203, compat_uptr_t)
+#define AUDIO_GET_CALIBRATION32		_IOWR(CAL_IOCTL_MAGIC, \
+							204, compat_uptr_t)
+#define AUDIO_POST_CALIBRATION32	_IOWR(CAL_IOCTL_MAGIC, \
+							205, compat_uptr_t)
+
+static long audio_cal_compat_ioctl(struct file *f,
+		unsigned int cmd, unsigned long arg)
+{
+	unsigned int cmd64;
+	int ret = 0;
+
+	switch (cmd) {
+	case AUDIO_ALLOCATE_CALIBRATION32:
+		cmd64 = AUDIO_ALLOCATE_CALIBRATION;
+		break;
+	case AUDIO_DEALLOCATE_CALIBRATION32:
+		cmd64 = AUDIO_DEALLOCATE_CALIBRATION;
+		break;
+	case AUDIO_PREPARE_CALIBRATION32:
+		cmd64 = AUDIO_PREPARE_CALIBRATION;
+		break;
+	case AUDIO_SET_CALIBRATION32:
+		cmd64 = AUDIO_SET_CALIBRATION;
+		break;
+	case AUDIO_GET_CALIBRATION32:
+		cmd64 = AUDIO_GET_CALIBRATION;
+		break;
+	case AUDIO_POST_CALIBRATION32:
+		cmd64 = AUDIO_POST_CALIBRATION;
+		break;
+	default:
+		pr_err("%s: ioctl not found!\n", __func__);
+		ret = -EFAULT;
+		goto done;
+	}
+
+	ret = audio_cal_shared_ioctl(f, cmd64, compat_ptr(arg));
+done:
+	return ret;
+}
+#endif
+
+static const struct file_operations audio_cal_fops = {
+	.owner = THIS_MODULE,
+	.open = audio_cal_open,
+	.release = audio_cal_release,
+	.unlocked_ioctl = audio_cal_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl =   audio_cal_compat_ioctl,
+#endif
+};
+
+struct miscdevice audio_cal_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_audio_cal",
+	.fops	= &audio_cal_fops,
+};
+
+static int __init audio_cal_init(void)
+{
+	int i = 0;
+
+	pr_debug("%s\n", __func__);
+
+	memset(&audio_cal, 0, sizeof(audio_cal));
+	mutex_init(&audio_cal.common_lock);
+	for (; i < MAX_CAL_TYPES; i++) {
+		INIT_LIST_HEAD(&audio_cal.client_info[i]);
+		mutex_init(&audio_cal.cal_mutex[i]);
+	}
+
+	return misc_register(&audio_cal_misc);
+}
+
+static void __exit audio_cal_exit(void)
+{
+	int i = 0;
+	struct list_head *ptr, *next;
+	struct audio_cal_client_info *client_info_node;
+
+	for (; i < MAX_CAL_TYPES; i++) {
+		list_for_each_safe(ptr, next,
+			&audio_cal.client_info[i]) {
+			client_info_node = list_entry(ptr,
+				struct audio_cal_client_info, list);
+			list_del(&client_info_node->list);
+			kfree(client_info_node->callbacks);
+			client_info_node->callbacks = NULL;
+			kfree(client_info_node);
+			client_info_node = NULL;
+		}
+	}
+}
+
+subsys_initcall(audio_cal_init);
+module_exit(audio_cal_exit);
+
+MODULE_DESCRIPTION("SoC QDSP6v2 Audio Calibration driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/audio_slimslave.c b/sound/soc/msm/qdsp6v2/audio_slimslave.c
new file mode 100644
index 0000000..e9ecfd5
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/audio_slimslave.c
@@ -0,0 +1,177 @@
+/* Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/miscdevice.h>
+#include <sound/audio_slimslave.h>
+#include <linux/slimbus/slimbus.h>
+#include <linux/pm_runtime.h>
+
+static struct slim_device *slim;
+static int vote_count;
+struct mutex suspend_lock;
+bool suspend;
+
+static int audio_slim_open(struct inode *inode, struct file *file)
+{
+	pr_debug("%s:\n", __func__);
+
+	if (vote_count) {
+		pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count);
+		pm_runtime_mark_last_busy(slim->dev.parent);
+		pm_runtime_put(slim->dev.parent);
+		vote_count--;
+	}
+	return 0;
+};
+
+static int audio_slim_release(struct inode *inode, struct file *file)
+{
+	pr_debug("%s:\n", __func__);
+
+	if (vote_count) {
+		pr_debug("%s: unvote: vote_count=%d\n", __func__, vote_count);
+		pm_runtime_mark_last_busy(slim->dev.parent);
+		pm_runtime_put(slim->dev.parent);
+		vote_count--;
+	} else {
+		pr_debug("%s: vote: vote_count=%d\n", __func__, vote_count);
+		pm_runtime_get_sync(slim->dev.parent);
+		vote_count++;
+	}
+	return 0;
+};
+
+static long audio_slim_ioctl(struct file *file, unsigned int cmd,
+			     unsigned long u_arg)
+{
+	switch (cmd) {
+	case AUDIO_SLIMSLAVE_VOTE:
+		mutex_lock(&suspend_lock);
+		if (!vote_count && !suspend) {
+			pr_debug("%s:AUDIO_SLIMSLAVE_VOTE\n", __func__);
+			pm_runtime_get_sync(slim->dev.parent);
+			vote_count++;
+		} else {
+			pr_err("%s:Invalid vote: vote_count=%d suspend=%d\n",
+				 __func__, vote_count, suspend);
+		}
+		mutex_unlock(&suspend_lock);
+		break;
+	case AUDIO_SLIMSLAVE_UNVOTE:
+		mutex_lock(&suspend_lock);
+		if (vote_count && !suspend) {
+			pr_debug("%s:AUDIO_SLIMSLAVE_UNVOTE\n", __func__);
+			pm_runtime_mark_last_busy(slim->dev.parent);
+			pm_runtime_put(slim->dev.parent);
+			vote_count--;
+		} else {
+			pr_err("%s:Invalid unvote: vote_count=%d suspend=%d\n",
+				 __func__, vote_count, suspend);
+		}
+		mutex_unlock(&suspend_lock);
+		break;
+	default:
+		pr_debug("%s: Invalid ioctl cmd: %d\n", __func__, cmd);
+		break;
+	}
+	return 0;
+}
+
+static const struct file_operations audio_slimslave_fops = {
+	.open =                 audio_slim_open,
+	.unlocked_ioctl =       audio_slim_ioctl,
+	.release =              audio_slim_release,
+};
+
+struct miscdevice audio_slimslave_misc = {
+	.minor  =       MISC_DYNAMIC_MINOR,
+	.name   =       AUDIO_SLIMSLAVE_IOCTL_NAME,
+	.fops   =       &audio_slimslave_fops,
+};
+
+static int audio_slimslave_probe(struct slim_device *audio_slim)
+{
+	pr_debug("%s:\n", __func__);
+
+	mutex_init(&suspend_lock);
+	suspend = false;
+	slim = audio_slim;
+	misc_register(&audio_slimslave_misc);
+	return 0;
+}
+
+static int audio_slimslave_remove(struct slim_device *audio_slim)
+{
+	pr_debug("%s:\n", __func__);
+
+	misc_deregister(&audio_slimslave_misc);
+	return 0;
+}
+
+static int audio_slimslave_resume(struct slim_device *audio_slim)
+{
+	pr_debug("%s:\n", __func__);
+
+	mutex_lock(&suspend_lock);
+	suspend = false;
+	mutex_unlock(&suspend_lock);
+	return 0;
+}
+
+static int audio_slimslave_suspend(struct slim_device *audio_slim,
+				   pm_message_t pmesg)
+{
+	pr_debug("%s:\n", __func__);
+
+	mutex_lock(&suspend_lock);
+	suspend = true;
+	mutex_unlock(&suspend_lock);
+	return 0;
+}
+
+static const struct slim_device_id audio_slimslave_dt_match[] = {
+	{"audio-slimslave", 0},
+	{}
+};
+
+static struct slim_driver audio_slimslave_driver = {
+	.driver = {
+		.name = "audio-slimslave",
+		.owner = THIS_MODULE,
+	},
+	.probe = audio_slimslave_probe,
+	.remove = audio_slimslave_remove,
+	.id_table = audio_slimslave_dt_match,
+	.resume = audio_slimslave_resume,
+	.suspend = audio_slimslave_suspend,
+};
+
+static int __init audio_slimslave_init(void)
+{
+	return slim_driver_register(&audio_slimslave_driver);
+}
+module_init(audio_slimslave_init);
+
+static void __exit audio_slimslave_exit(void)
+{
+
+}
+module_exit(audio_slimslave_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Audio side Slimbus slave driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
new file mode 100644
index 0000000..225f978
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-audio-effects-q6-v2.c
@@ -0,0 +1,1399 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/compress_params.h>
+#include <sound/msm-audio-effects-q6-v2.h>
+#include <sound/msm-dts-eagle.h>
+#include <sound/devdep_params.h>
+
+#define MAX_ENABLE_CMD_SIZE 32
+
+#define GET_NEXT(ptr, upper_limit, rc)                                  \
+({                                                                      \
+	if (((ptr) + 1) > (upper_limit)) {                              \
+		pr_err("%s: param list out of boundary\n", __func__);   \
+		(rc) = -EINVAL;                                         \
+	}                                                               \
+	((rc) == 0) ? *(ptr)++ :  -EINVAL;                              \
+})
+
+#define CHECK_PARAM_LEN(len, max_len, tag, rc)                          \
+do {                                                                    \
+	if ((len) > (max_len)) {                                        \
+		pr_err("%s: params length overflows\n", (tag));         \
+		(rc) = -EINVAL;                                         \
+	}                                                               \
+} while (0)
+
+
+bool msm_audio_effects_is_effmodule_supp_in_top(int effect_module,
+						int topology)
+{
+	switch (effect_module) {
+	case VIRTUALIZER_MODULE:
+	case REVERB_MODULE:
+	case BASS_BOOST_MODULE:
+	case PBE_MODULE:
+	case EQ_MODULE:
+		switch (topology) {
+		case ASM_STREAM_POSTPROC_TOPO_ID_SA_PLUS:
+		case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS:
+		case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+			return true;
+		default:
+			return false;
+		}
+	case DTS_EAGLE_MODULE:
+		switch (topology) {
+		case ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX:
+		case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS:
+		case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+			return true;
+		default:
+			return false;
+		}
+	case SOFT_VOLUME2_MODULE:
+	case DTS_EAGLE_MODULE_ENABLE:
+		switch (topology) {
+		case ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS:
+		case ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER:
+			return true;
+		default:
+			return false;
+		}
+	default:
+		return false;
+	}
+}
+
+int msm_audio_effects_enable_extn(struct audio_client *ac,
+				struct msm_nt_eff_all_config *effects,
+				bool flag)
+{
+	uint32_t updt_params[MAX_ENABLE_CMD_SIZE] = {0};
+	uint32_t params_length;
+	int rc = 0;
+
+	pr_debug("%s\n", __func__);
+	if (!ac) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params_length = 0;
+	updt_params[0] = AUDPROC_MODULE_ID_VIRTUALIZER;
+	updt_params[1] = AUDPROC_PARAM_ID_ENABLE;
+	updt_params[2] = VIRTUALIZER_ENABLE_PARAM_SZ;
+	updt_params[3] = flag;
+	params_length += COMMAND_PAYLOAD_SZ + VIRTUALIZER_ENABLE_PARAM_SZ;
+	if (effects->virtualizer.enable_flag)
+		q6asm_send_audio_effects_params(ac, (char *)&updt_params[0],
+					params_length);
+	memset(updt_params, 0, MAX_ENABLE_CMD_SIZE);
+	params_length = 0;
+	updt_params[0] = AUDPROC_MODULE_ID_BASS_BOOST;
+	updt_params[1] = AUDPROC_PARAM_ID_ENABLE;
+	updt_params[2] = BASS_BOOST_ENABLE_PARAM_SZ;
+	updt_params[3] = flag;
+	params_length += COMMAND_PAYLOAD_SZ + BASS_BOOST_ENABLE_PARAM_SZ;
+	if (effects->bass_boost.enable_flag)
+		q6asm_send_audio_effects_params(ac, (char *)&updt_params[0],
+					params_length);
+	memset(updt_params, 0, MAX_ENABLE_CMD_SIZE);
+	params_length = 0;
+	updt_params[0] = AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+	updt_params[1] = AUDPROC_PARAM_ID_ENABLE;
+	updt_params[2] = EQ_ENABLE_PARAM_SZ;
+	updt_params[3] = flag;
+	params_length += COMMAND_PAYLOAD_SZ + EQ_ENABLE_PARAM_SZ;
+	if (effects->equalizer.enable_flag)
+		q6asm_send_audio_effects_params(ac, (char *)&updt_params[0],
+					params_length);
+	return rc;
+}
+
+int msm_audio_effects_virtualizer_handler(struct audio_client *ac,
+				struct virtualizer_params *virtualizer,
+				long *values)
+{
+	long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+	char *params = NULL;
+	int rc = 0;
+	int devices = GET_NEXT(values, param_max_offset, rc);
+	int num_commands = GET_NEXT(values, param_max_offset, rc);
+	int *updt_params, i, prev_enable_flag;
+	uint32_t params_length = (MAX_INBAND_PARAM_SZ);
+
+	pr_debug("%s\n", __func__);
+	if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params = kzalloc(params_length, GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	pr_debug("%s: device: %d\n", __func__, devices);
+	updt_params = (int *)params;
+	params_length = 0;
+	for (i = 0; i < num_commands; i++) {
+		uint32_t command_id =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t command_config_state =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t index_offset =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t length =
+			GET_NEXT(values, param_max_offset, rc);
+		switch (command_id) {
+		case VIRTUALIZER_ENABLE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("VIRT ENABLE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			prev_enable_flag = virtualizer->enable_flag;
+			virtualizer->enable_flag =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s:VIRT ENABLE prev:%d, new:%d\n", __func__,
+				prev_enable_flag, virtualizer->enable_flag);
+			if (prev_enable_flag != virtualizer->enable_flag) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					VIRTUALIZER_ENABLE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"VIRT ENABLE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+				AUDPROC_MODULE_ID_VIRTUALIZER;
+				*updt_params++ =
+				AUDPROC_PARAM_ID_VIRTUALIZER_ENABLE;
+				*updt_params++ =
+				VIRTUALIZER_ENABLE_PARAM_SZ;
+				*updt_params++ =
+				virtualizer->enable_flag;
+			}
+			break;
+		case VIRTUALIZER_STRENGTH:
+			if (length != 1 || index_offset != 0) {
+				pr_err("VIRT STRENGTH:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			virtualizer->strength =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: VIRT STRENGTH val: %d\n",
+					__func__, virtualizer->strength);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					VIRTUALIZER_STRENGTH_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"VIRT STRENGTH", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_VIRTUALIZER;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_VIRTUALIZER_STRENGTH;
+				*updt_params++ =
+					VIRTUALIZER_STRENGTH_PARAM_SZ;
+				*updt_params++ =
+					virtualizer->strength;
+			}
+			break;
+		case VIRTUALIZER_OUT_TYPE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("VIRT OUT_TYPE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			virtualizer->out_type =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: VIRT OUT_TYPE val:%d\n",
+				__func__, virtualizer->out_type);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					VIRTUALIZER_OUT_TYPE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"VIRT OUT_TYPE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_VIRTUALIZER;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_VIRTUALIZER_OUT_TYPE;
+				*updt_params++ =
+					VIRTUALIZER_OUT_TYPE_PARAM_SZ;
+				*updt_params++ =
+					virtualizer->out_type;
+			}
+			break;
+		case VIRTUALIZER_GAIN_ADJUST:
+			if (length != 1 || index_offset != 0) {
+				pr_err("VIRT GAIN_ADJUST: invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			virtualizer->gain_adjust =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: VIRT GAIN_ADJUST val:%d\n",
+				__func__, virtualizer->gain_adjust);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					VIRTUALIZER_GAIN_ADJUST_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"VIRT GAIN_ADJUST", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+				AUDPROC_MODULE_ID_VIRTUALIZER;
+				*updt_params++ =
+				AUDPROC_PARAM_ID_VIRTUALIZER_GAIN_ADJUST;
+				*updt_params++ =
+				VIRTUALIZER_GAIN_ADJUST_PARAM_SZ;
+				*updt_params++ =
+				virtualizer->gain_adjust;
+			}
+			break;
+		default:
+			pr_err("%s: Invalid command to set config\n", __func__);
+			break;
+		}
+	}
+	if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+		q6asm_send_audio_effects_params(ac, params,
+						params_length);
+	else
+		pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+	kfree(params);
+	return rc;
+}
+
+int msm_audio_effects_reverb_handler(struct audio_client *ac,
+				     struct reverb_params *reverb,
+				     long *values)
+{
+	long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+	char *params = NULL;
+	int rc = 0;
+	int devices = GET_NEXT(values, param_max_offset, rc);
+	int num_commands = GET_NEXT(values, param_max_offset, rc);
+	int *updt_params, i, prev_enable_flag;
+	uint32_t params_length = (MAX_INBAND_PARAM_SZ);
+
+	pr_debug("%s\n", __func__);
+	if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params = kzalloc(params_length, GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	pr_debug("%s: device: %d\n", __func__, devices);
+	updt_params = (int *)params;
+	params_length = 0;
+	for (i = 0; i < num_commands; i++) {
+		uint32_t command_id =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t command_config_state =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t index_offset =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t length =
+			GET_NEXT(values, param_max_offset, rc);
+		switch (command_id) {
+		case REVERB_ENABLE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_ENABLE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			prev_enable_flag = reverb->enable_flag;
+			reverb->enable_flag =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s:REVERB_ENABLE prev:%d,new:%d\n", __func__,
+					prev_enable_flag, reverb->enable_flag);
+			if (prev_enable_flag != reverb->enable_flag) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_ENABLE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_ENABLE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_ENABLE;
+				*updt_params++ =
+					REVERB_ENABLE_PARAM_SZ;
+				*updt_params++ =
+					reverb->enable_flag;
+			}
+			break;
+		case REVERB_MODE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_MODE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->mode =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_MODE val:%d\n",
+				__func__, reverb->mode);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_MODE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_MODE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_MODE;
+				*updt_params++ =
+					REVERB_MODE_PARAM_SZ;
+				*updt_params++ =
+					reverb->mode;
+			}
+			break;
+		case REVERB_PRESET:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_PRESET:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->preset =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_PRESET val:%d\n",
+					__func__, reverb->preset);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_PRESET_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_PRESET", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_PRESET;
+				*updt_params++ =
+					REVERB_PRESET_PARAM_SZ;
+				*updt_params++ =
+					reverb->preset;
+			}
+			break;
+		case REVERB_WET_MIX:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_WET_MIX:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->wet_mix =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_WET_MIX val:%d\n",
+				__func__, reverb->wet_mix);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_WET_MIX_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_WET_MIX", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_WET_MIX;
+				*updt_params++ =
+					REVERB_WET_MIX_PARAM_SZ;
+				*updt_params++ =
+					reverb->wet_mix;
+			}
+			break;
+		case REVERB_GAIN_ADJUST:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_GAIN_ADJUST:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->gain_adjust =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_GAIN_ADJUST val:%d\n",
+					__func__, reverb->gain_adjust);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_GAIN_ADJUST_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_GAIN_ADJUST", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_GAIN_ADJUST;
+				*updt_params++ =
+					REVERB_GAIN_ADJUST_PARAM_SZ;
+				*updt_params++ =
+					reverb->gain_adjust;
+			}
+			break;
+		case REVERB_ROOM_LEVEL:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_ROOM_LEVEL:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->room_level =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_ROOM_LEVEL val:%d\n",
+				__func__, reverb->room_level);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_ROOM_LEVEL_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_ROOM_LEVEL", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_ROOM_LEVEL;
+				*updt_params++ =
+					REVERB_ROOM_LEVEL_PARAM_SZ;
+				*updt_params++ =
+					reverb->room_level;
+			}
+			break;
+		case REVERB_ROOM_HF_LEVEL:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_ROOM_HF_LEVEL:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->room_hf_level =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_ROOM_HF_LEVEL val%d\n",
+				__func__, reverb->room_hf_level);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_ROOM_HF_LEVEL_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_ROOM_HF_LEVEL", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_ROOM_HF_LEVEL;
+				*updt_params++ =
+					REVERB_ROOM_HF_LEVEL_PARAM_SZ;
+				*updt_params++ =
+					reverb->room_hf_level;
+			}
+			break;
+		case REVERB_DECAY_TIME:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_DECAY_TIME:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->decay_time =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_DECAY_TIME val:%d\n",
+				__func__, reverb->decay_time);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_DECAY_TIME_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_DECAY_TIME", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_DECAY_TIME;
+				*updt_params++ =
+					REVERB_DECAY_TIME_PARAM_SZ;
+				*updt_params++ =
+					reverb->decay_time;
+			}
+			break;
+		case REVERB_DECAY_HF_RATIO:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_DECAY_HF_RATIOinvalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->decay_hf_ratio =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_DECAY_HF_RATIO val%d\n",
+				__func__, reverb->decay_hf_ratio);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_DECAY_HF_RATIO_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_DECAY_HF_RATIO", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_DECAY_HF_RATIO;
+				*updt_params++ =
+					REVERB_DECAY_HF_RATIO_PARAM_SZ;
+				*updt_params++ =
+					reverb->decay_hf_ratio;
+			}
+			break;
+		case REVERB_REFLECTIONS_LEVEL:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_REFLECTION_LVLinvalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->reflections_level =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_REFLECTIONS_LEVEL val:%d\n",
+				__func__, reverb->reflections_level);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_REFLECTIONS_LEVEL_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_REFLECTIONS_LEVEL", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+				AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+				AUDPROC_PARAM_ID_REVERB_REFLECTIONS_LEVEL;
+				*updt_params++ =
+				REVERB_REFLECTIONS_LEVEL_PARAM_SZ;
+				*updt_params++ =
+				reverb->reflections_level;
+			}
+			break;
+		case REVERB_REFLECTIONS_DELAY:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_REFLECTION_DLYinvalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->reflections_delay =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_REFLECTIONS_DELAY val:%d\n",
+				__func__, reverb->reflections_delay);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_REFLECTIONS_DELAY_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_REFLECTIONS_DELAY", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+				AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+				AUDPROC_PARAM_ID_REVERB_REFLECTIONS_DELAY;
+				*updt_params++ =
+				REVERB_REFLECTIONS_DELAY_PARAM_SZ;
+				*updt_params++ =
+				reverb->reflections_delay;
+			}
+			break;
+		case REVERB_LEVEL:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_LEVEL:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->level =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_LEVEL val:%d\n",
+				__func__, reverb->level);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_LEVEL_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_LEVEL", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_LEVEL;
+				*updt_params++ =
+					REVERB_LEVEL_PARAM_SZ;
+				*updt_params++ =
+					reverb->level;
+			}
+			break;
+		case REVERB_DELAY:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_DELAY:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->delay =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s:REVERB_DELAY val:%d\n",
+					__func__, reverb->delay);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_DELAY_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_DELAY", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_DELAY;
+				*updt_params++ =
+					REVERB_DELAY_PARAM_SZ;
+				*updt_params++ =
+					reverb->delay;
+			}
+			break;
+		case REVERB_DIFFUSION:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_DIFFUSION:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->diffusion =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_DIFFUSION val:%d\n",
+				__func__, reverb->diffusion);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_DIFFUSION_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_DIFFUSION", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_DIFFUSION;
+				*updt_params++ =
+					REVERB_DIFFUSION_PARAM_SZ;
+				*updt_params++ =
+					reverb->diffusion;
+			}
+			break;
+		case REVERB_DENSITY:
+			if (length != 1 || index_offset != 0) {
+				pr_err("REVERB_DENSITY:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			reverb->density =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: REVERB_DENSITY val:%d\n",
+				__func__, reverb->density);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					REVERB_DENSITY_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"REVERB_DENSITY", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_REVERB;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_REVERB_DENSITY;
+				*updt_params++ =
+					REVERB_DENSITY_PARAM_SZ;
+				*updt_params++ =
+					reverb->density;
+			}
+			break;
+		default:
+			pr_err("%s: Invalid command to set config\n", __func__);
+			break;
+		}
+	}
+	if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+		q6asm_send_audio_effects_params(ac, params,
+						params_length);
+	else
+		pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+	kfree(params);
+	return rc;
+}
+
+int msm_audio_effects_bass_boost_handler(struct audio_client *ac,
+					struct bass_boost_params *bass_boost,
+					long *values)
+{
+	long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+	char *params = NULL;
+	int rc = 0;
+	int devices = GET_NEXT(values, param_max_offset, rc);
+	int num_commands = GET_NEXT(values, param_max_offset, rc);
+	int *updt_params, i, prev_enable_flag;
+	uint32_t params_length = (MAX_INBAND_PARAM_SZ);
+
+	pr_debug("%s\n", __func__);
+	if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params = kzalloc(params_length, GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	pr_debug("%s: device: %d\n", __func__, devices);
+	updt_params = (int *)params;
+	params_length = 0;
+	for (i = 0; i < num_commands; i++) {
+		uint32_t command_id =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t command_config_state =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t index_offset =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t length =
+			GET_NEXT(values, param_max_offset, rc);
+		switch (command_id) {
+		case BASS_BOOST_ENABLE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("BASS_BOOST_ENABLE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			prev_enable_flag = bass_boost->enable_flag;
+			bass_boost->enable_flag =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: BASS_BOOST_ENABLE prev:%d new:%d\n",
+				__func__, prev_enable_flag,
+				bass_boost->enable_flag);
+			if (prev_enable_flag != bass_boost->enable_flag) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					BASS_BOOST_ENABLE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"BASS_BOOST_ENABLE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_BASS_BOOST;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_BASS_BOOST_ENABLE;
+				*updt_params++ =
+					BASS_BOOST_ENABLE_PARAM_SZ;
+				*updt_params++ =
+					bass_boost->enable_flag;
+			}
+			break;
+		case BASS_BOOST_MODE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("BASS_BOOST_MODE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			bass_boost->mode =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: BASS_BOOST_MODE val:%d\n",
+				__func__, bass_boost->mode);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					BASS_BOOST_MODE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"BASS_BOOST_MODE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_BASS_BOOST;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_BASS_BOOST_MODE;
+				*updt_params++ =
+					BASS_BOOST_MODE_PARAM_SZ;
+				*updt_params++ =
+					bass_boost->mode;
+			}
+			break;
+		case BASS_BOOST_STRENGTH:
+			if (length != 1 || index_offset != 0) {
+				pr_err("BASS_BOOST_STRENGTH:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			bass_boost->strength =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: BASS_BOOST_STRENGTH val:%d\n",
+				__func__, bass_boost->strength);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					BASS_BOOST_STRENGTH_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"BASS_BOOST_STRENGTH", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_BASS_BOOST;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_BASS_BOOST_STRENGTH;
+				*updt_params++ =
+					BASS_BOOST_STRENGTH_PARAM_SZ;
+				*updt_params++ =
+					bass_boost->strength;
+			}
+			break;
+		default:
+			pr_err("%s: Invalid command to set config\n", __func__);
+			break;
+		}
+	}
+	if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+		q6asm_send_audio_effects_params(ac, params,
+						params_length);
+	else
+		pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+	kfree(params);
+	return rc;
+}
+
+int msm_audio_effects_pbe_handler(struct audio_client *ac,
+					struct pbe_params *pbe,
+					long *values)
+{
+	long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+	char *params = NULL;
+	int rc = 0;
+	int devices = GET_NEXT(values, param_max_offset, rc);
+	int num_commands = GET_NEXT(values, param_max_offset, rc);
+	int *updt_params, i, j, prev_enable_flag;
+	uint32_t params_length = (MAX_INBAND_PARAM_SZ);
+
+	pr_debug("%s\n", __func__);
+	if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params = kzalloc(params_length, GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	pr_debug("%s: device: %d\n", __func__, devices);
+	updt_params = (int *)params;
+	params_length = 0;
+	for (i = 0; i < num_commands; i++) {
+		uint32_t command_id =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t command_config_state =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t index_offset =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t length =
+			GET_NEXT(values, param_max_offset, rc);
+		switch (command_id) {
+		case PBE_ENABLE:
+			pr_debug("%s: PBE_ENABLE\n", __func__);
+			if (length != 1 || index_offset != 0) {
+				pr_err("no valid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			prev_enable_flag = pbe->enable_flag;
+			pbe->enable_flag =
+				GET_NEXT(values, param_max_offset, rc);
+			if (prev_enable_flag != pbe->enable_flag) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					PBE_ENABLE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"PBE_ENABLE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_PBE;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_PBE_ENABLE;
+				*updt_params++ =
+					PBE_ENABLE_PARAM_SZ;
+				*updt_params++ =
+					pbe->enable_flag;
+			}
+			break;
+		case PBE_CONFIG:
+			pr_debug("%s: PBE_PARAM length %u\n", __func__, length);
+			if (length > sizeof(struct pbe_config_t) ||
+				length < PBE_CONFIG_PARAM_LEN ||
+				index_offset != 0) {
+				pr_err("no valid params, len %d\n", length);
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ + length;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"PBE_PARAM", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_PBE;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_PBE_PARAM_CONFIG;
+				*updt_params++ =
+					length;
+				for (j = 0; j < length; ) {
+					j += sizeof(*updt_params);
+					*updt_params++ =
+						GET_NEXT(
+						values,
+						param_max_offset,
+						rc);
+				}
+			}
+			break;
+		default:
+			pr_err("%s: Invalid command to set config\n", __func__);
+			break;
+		}
+	}
+	if (params_length && (rc == 0))
+		q6asm_send_audio_effects_params(ac, params,
+						params_length);
+invalid_config:
+	kfree(params);
+	return rc;
+}
+
+int msm_audio_effects_popless_eq_handler(struct audio_client *ac,
+					 struct eq_params *eq,
+					 long *values)
+{
+	long *param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+	char *params = NULL;
+	int rc = 0;
+	int devices = GET_NEXT(values, param_max_offset, rc);
+	int num_commands = GET_NEXT(values, param_max_offset, rc);
+	int *updt_params, i, prev_enable_flag;
+	uint32_t params_length = (MAX_INBAND_PARAM_SZ);
+
+	pr_debug("%s\n", __func__);
+	if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params = kzalloc(params_length, GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	pr_debug("%s: device: %d\n", __func__, devices);
+	updt_params = (int *)params;
+	params_length = 0;
+	for (i = 0; i < num_commands; i++) {
+		uint32_t command_id =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t command_config_state =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t index_offset =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t length =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t idx;
+		int j;
+
+		switch (command_id) {
+		case EQ_ENABLE:
+			if (length != 1 || index_offset != 0) {
+				pr_err("EQ_ENABLE:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			prev_enable_flag = eq->enable_flag;
+			eq->enable_flag =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: EQ_ENABLE prev:%d new:%d\n", __func__,
+				prev_enable_flag, eq->enable_flag);
+			if (prev_enable_flag != eq->enable_flag) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					EQ_ENABLE_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"EQ_ENABLE", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_EQ_ENABLE;
+				*updt_params++ =
+					EQ_ENABLE_PARAM_SZ;
+				*updt_params++ =
+					eq->enable_flag;
+			}
+			break;
+		case EQ_CONFIG:
+			if (length < EQ_CONFIG_PARAM_LEN || index_offset != 0) {
+				pr_err("EQ_CONFIG:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			pr_debug("%s: EQ_CONFIG bands:%d, pgain:%d, pset:%d\n",
+				 __func__, eq->config.num_bands,
+				eq->config.eq_pregain, eq->config.preset_id);
+			for (idx = 0; idx < MAX_EQ_BANDS; idx++)
+				eq->per_band_cfg[idx].band_idx = -1;
+			eq->config.eq_pregain =
+				GET_NEXT(values, param_max_offset, rc);
+			eq->config.preset_id =
+				GET_NEXT(values, param_max_offset, rc);
+			eq->config.num_bands =
+				GET_NEXT(values, param_max_offset, rc);
+			if (eq->config.num_bands > MAX_EQ_BANDS) {
+				pr_err("EQ_CONFIG:invalid num of bands\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			if (eq->config.num_bands &&
+			    (((length - EQ_CONFIG_PARAM_LEN)/
+				EQ_CONFIG_PER_BAND_PARAM_LEN)
+				!= eq->config.num_bands)) {
+				pr_err("EQ_CONFIG:invalid length per band\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			for (j = 0; j < eq->config.num_bands; j++) {
+				idx = GET_NEXT(values, param_max_offset, rc);
+				if (idx >= MAX_EQ_BANDS) {
+					pr_err("EQ_CONFIG:invalid band index\n");
+					rc = -EINVAL;
+					goto invalid_config;
+				}
+				eq->per_band_cfg[idx].band_idx = idx;
+				eq->per_band_cfg[idx].filter_type =
+					GET_NEXT(values, param_max_offset, rc);
+				eq->per_band_cfg[idx].freq_millihertz =
+					GET_NEXT(values, param_max_offset, rc);
+				eq->per_band_cfg[idx].gain_millibels =
+					GET_NEXT(values, param_max_offset, rc);
+				eq->per_band_cfg[idx].quality_factor =
+					GET_NEXT(values, param_max_offset, rc);
+			}
+			if (command_config_state == CONFIG_SET) {
+				int config_param_length = EQ_CONFIG_PARAM_SZ +
+					(EQ_CONFIG_PER_BAND_PARAM_SZ*
+					 eq->config.num_bands);
+				params_length += COMMAND_PAYLOAD_SZ +
+						config_param_length;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"EQ_CONFIG", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_EQ_CONFIG;
+				*updt_params++ =
+					config_param_length;
+				*updt_params++ =
+					eq->config.eq_pregain;
+				*updt_params++ =
+					eq->config.preset_id;
+				*updt_params++ =
+					eq->config.num_bands;
+				for (idx = 0; idx < MAX_EQ_BANDS; idx++) {
+					if (eq->per_band_cfg[idx].band_idx < 0)
+						continue;
+					*updt_params++ =
+					eq->per_band_cfg[idx].filter_type;
+					*updt_params++ =
+					eq->per_band_cfg[idx].freq_millihertz;
+					*updt_params++ =
+					eq->per_band_cfg[idx].gain_millibels;
+					*updt_params++ =
+					eq->per_band_cfg[idx].quality_factor;
+					*updt_params++ =
+					eq->per_band_cfg[idx].band_idx;
+				}
+			}
+			break;
+		case EQ_BAND_INDEX:
+			if (length != 1 || index_offset != 0) {
+				pr_err("EQ_BAND_INDEX:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			idx = GET_NEXT(values, param_max_offset, rc);
+			if (idx > MAX_EQ_BANDS) {
+				pr_err("EQ_BAND_INDEX:invalid band index\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			eq->band_index = idx;
+			pr_debug("%s: EQ_BAND_INDEX val:%d\n",
+				__func__, eq->band_index);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					EQ_BAND_INDEX_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"EQ_BAND_INDEX", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_EQ_BAND_INDEX;
+				*updt_params++ =
+					EQ_BAND_INDEX_PARAM_SZ;
+				*updt_params++ =
+					eq->band_index;
+			}
+			break;
+		case EQ_SINGLE_BAND_FREQ:
+			if (length != 1 || index_offset != 0) {
+				pr_err("EQ_SINGLE_BAND_FREQ:invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			if (eq->band_index > MAX_EQ_BANDS) {
+				pr_err("EQ_SINGLE_BAND_FREQ:invalid index\n");
+				break;
+			}
+			eq->freq_millihertz =
+				GET_NEXT(values, param_max_offset, rc);
+			pr_debug("%s: EQ_SINGLE_BAND_FREQ idx:%d, val:%d\n",
+				__func__, eq->band_index, eq->freq_millihertz);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+					EQ_SINGLE_BAND_FREQ_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"EQ_SINGLE_BAND_FREQ", rc);
+				if (rc != 0)
+					break;
+				*updt_params++ =
+					AUDPROC_MODULE_ID_POPLESS_EQUALIZER;
+				*updt_params++ =
+					AUDPROC_PARAM_ID_EQ_SINGLE_BAND_FREQ;
+				*updt_params++ =
+					EQ_SINGLE_BAND_FREQ_PARAM_SZ;
+				*updt_params++ =
+					eq->freq_millihertz;
+			}
+			break;
+		default:
+			pr_err("%s: Invalid command to set config\n", __func__);
+			break;
+		}
+	}
+	if (params_length && !msm_dts_eagle_is_hpx_on() && (rc == 0))
+		q6asm_send_audio_effects_params(ac, params,
+						params_length);
+	else
+		pr_debug("%s: did not send pp params\n", __func__);
+invalid_config:
+	kfree(params);
+	return rc;
+}
+
+static int __msm_audio_effects_volume_handler(struct audio_client *ac,
+					      struct soft_volume_params *vol,
+					      long *values,
+					      int instance)
+{
+	int devices;
+	int num_commands;
+	char *params = NULL;
+	int *updt_params, i;
+	uint32_t params_length = (MAX_INBAND_PARAM_SZ);
+	long *param_max_offset;
+	int rc = 0;
+
+	pr_debug("%s: instance: %d\n", __func__, instance);
+	if (!values) {
+		pr_err("%s: set audio effects failed, no valid data\n",
+			__func__);
+		return -EINVAL;
+	}
+	param_max_offset = values + MAX_PP_PARAMS_SZ - 1;
+	devices = GET_NEXT(values, param_max_offset, rc);
+	num_commands = GET_NEXT(values, param_max_offset, rc);
+	if (!ac || (devices == -EINVAL) || (num_commands == -EINVAL)) {
+		pr_err("%s: cannot set audio effects\n", __func__);
+		return -EINVAL;
+	}
+	params = kzalloc(params_length, GFP_KERNEL);
+	if (!params)
+		return -ENOMEM;
+
+	updt_params = (int *)params;
+	params_length = 0;
+	for (i = 0; i < num_commands; i++) {
+		uint32_t command_id =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t command_config_state =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t index_offset =
+			GET_NEXT(values, param_max_offset, rc);
+		uint32_t length =
+			GET_NEXT(values, param_max_offset, rc);
+		switch (command_id) {
+		case SOFT_VOLUME_GAIN_2CH:
+		case SOFT_VOLUME2_GAIN_2CH:
+			if (length != 2 || index_offset != 0) {
+				pr_err("VOLUME_GAIN_2CH: invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			vol->left_gain = GET_NEXT(values, param_max_offset, rc);
+			vol->right_gain =
+				GET_NEXT(values, param_max_offset, rc);
+			vol->master_gain = 0x2000;
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+						SOFT_VOLUME_GAIN_2CH_PARAM_SZ;
+				params_length += COMMAND_PAYLOAD_SZ +
+					SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"VOLUME/VOLUME2_GAIN_2CH",
+						rc);
+				if (rc != 0)
+					break;
+				if (instance == SOFT_VOLUME_INSTANCE_2)
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL2;
+				else
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL;
+				*updt_params++ =
+					ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN;
+				*updt_params++ =
+					SOFT_VOLUME_GAIN_2CH_PARAM_SZ;
+				*updt_params++ =
+					(vol->left_gain << 16) |
+						vol->right_gain;
+				if (instance == SOFT_VOLUME_INSTANCE_2)
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL2;
+				else
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL;
+				*updt_params++ =
+					ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+				*updt_params++ =
+					SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+				*updt_params++ =
+					vol->master_gain;
+			}
+			break;
+		case SOFT_VOLUME_GAIN_MASTER:
+		case SOFT_VOLUME2_GAIN_MASTER:
+			if (length != 1 || index_offset != 0) {
+				pr_err("VOLUME_GAIN_MASTER: invalid params\n");
+				rc = -EINVAL;
+				goto invalid_config;
+			}
+			vol->left_gain = 0x2000;
+			vol->right_gain = 0x2000;
+			vol->master_gain =
+				GET_NEXT(values, param_max_offset, rc);
+			if (command_config_state == CONFIG_SET) {
+				params_length += COMMAND_PAYLOAD_SZ +
+						SOFT_VOLUME_GAIN_2CH_PARAM_SZ;
+				params_length += COMMAND_PAYLOAD_SZ +
+					SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+				CHECK_PARAM_LEN(params_length,
+						MAX_INBAND_PARAM_SZ,
+						"VOLUME/VOLUME2_GAIN_MASTER",
+						rc);
+				if (rc != 0)
+					break;
+				if (instance == SOFT_VOLUME_INSTANCE_2)
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL2;
+				else
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL;
+				*updt_params++ =
+					ASM_PARAM_ID_VOL_CTRL_LR_CHANNEL_GAIN;
+				*updt_params++ =
+					SOFT_VOLUME_GAIN_2CH_PARAM_SZ;
+				*updt_params++ =
+					(vol->left_gain << 16) |
+						vol->right_gain;
+				if (instance == SOFT_VOLUME_INSTANCE_2)
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL2;
+				else
+					*updt_params++ =
+						ASM_MODULE_ID_VOL_CTRL;
+				*updt_params++ =
+					ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+				*updt_params++ =
+					SOFT_VOLUME_GAIN_MASTER_PARAM_SZ;
+				*updt_params++ =
+					vol->master_gain;
+			}
+			break;
+		default:
+			pr_err("%s: Invalid command id: %d to set config\n",
+				__func__, command_id);
+			break;
+		}
+	}
+	if (params_length && (rc == 0))
+		q6asm_send_audio_effects_params(ac, params,
+						params_length);
+invalid_config:
+	kfree(params);
+	return rc;
+}
+
+int msm_audio_effects_volume_handler(struct audio_client *ac,
+				     struct soft_volume_params *vol,
+				     long *values)
+{
+	return __msm_audio_effects_volume_handler(ac, vol, values,
+						  SOFT_VOLUME_INSTANCE_1);
+}
+
+int msm_audio_effects_volume_handler_v2(struct audio_client *ac,
+					struct soft_volume_params *vol,
+					long *values, int instance)
+{
+	return __msm_audio_effects_volume_handler(ac, vol, values, instance);
+}
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
new file mode 100644
index 0000000..449325c
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.c
@@ -0,0 +1,1714 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6asm-v2.h>
+#include <sound/pcm_params.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+
+#include <sound/timer.h>
+
+#include "msm-compr-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+#include <sound/tlv.h>
+
+#define COMPRE_CAPTURE_NUM_PERIODS	16
+/* Allocate the worst case frame size for compressed audio */
+#define COMPRE_CAPTURE_HEADER_SIZE	(sizeof(struct snd_compr_audio_info))
+/* Changing period size to 4032. 4032 will make sure COMPRE_CAPTURE_PERIOD_SIZE
+ * is 4096 with meta data size of 64 and MAX_NUM_FRAMES_PER_BUFFER 1
+ */
+#define COMPRE_CAPTURE_MAX_FRAME_SIZE	(4032)
+#define COMPRE_CAPTURE_PERIOD_SIZE	((COMPRE_CAPTURE_MAX_FRAME_SIZE + \
+					  COMPRE_CAPTURE_HEADER_SIZE) * \
+					  MAX_NUM_FRAMES_PER_BUFFER)
+#define COMPRE_OUTPUT_METADATA_SIZE	(sizeof(struct output_meta_data_st))
+#define COMPRESSED_LR_VOL_MAX_STEPS	0x20002000
+
+#define MAX_AC3_PARAM_SIZE		(18*2*sizeof(int))
+#define AMR_WB_BAND_MODE 8
+#define AMR_WB_DTX_MODE 0
+
+
+const DECLARE_TLV_DB_LINEAR(compr_rx_vol_gain, 0,
+			    COMPRESSED_LR_VOL_MAX_STEPS);
+
+static struct audio_locks the_locks;
+
+static struct snd_pcm_hardware msm_compr_hardware_capture = {
+	.info =		 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =	      SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =		SNDRV_PCM_RATE_8000_48000,
+	.rate_min =	     8000,
+	.rate_max =	     48000,
+	.channels_min =	 1,
+	.channels_max =	 8,
+	.buffer_bytes_max =
+		COMPRE_CAPTURE_PERIOD_SIZE * COMPRE_CAPTURE_NUM_PERIODS,
+	.period_bytes_min =	COMPRE_CAPTURE_PERIOD_SIZE,
+	.period_bytes_max = COMPRE_CAPTURE_PERIOD_SIZE,
+	.periods_min =	  COMPRE_CAPTURE_NUM_PERIODS,
+	.periods_max =	  COMPRE_CAPTURE_NUM_PERIODS,
+	.fifo_size =	    0,
+};
+
+static struct snd_pcm_hardware msm_compr_hardware_playback = {
+	.info =		 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =	      SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+	.rates =		SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT,
+	.rate_min =	     8000,
+	.rate_max =	     48000,
+	.channels_min =	 1,
+	.channels_max =	 8,
+	.buffer_bytes_max =     1024 * 1024,
+	.period_bytes_min =	128 * 1024,
+	.period_bytes_max =     256 * 1024,
+	.periods_min =	  4,
+	.periods_max =	  8,
+	.fifo_size =	    0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+/* Add supported codecs for compress capture path */
+static uint32_t supported_compr_capture_codecs[] = {
+	SND_AUDIOCODEC_AMRWB
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static bool msm_compr_capture_codecs(uint32_t req_codec)
+{
+	int i;
+
+	pr_debug("%s req_codec:%d\n", __func__, req_codec);
+	if (req_codec == 0)
+		return false;
+	for (i = 0; i < ARRAY_SIZE(supported_compr_capture_codecs); i++) {
+		if (req_codec == supported_compr_capture_codecs[i])
+			return true;
+	}
+	return false;
+}
+
+static void compr_event_handler(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv)
+{
+	struct compr_audio *compr = priv;
+	struct msm_audio *prtd = &compr->prtd;
+	struct snd_pcm_substream *substream = prtd->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct audio_aio_write_param param;
+	struct audio_aio_read_param read_param;
+	struct audio_buffer *buf = NULL;
+	phys_addr_t temp;
+	struct output_meta_data_st output_meta_data;
+	uint32_t *ptrmem = (uint32_t *)payload;
+	int i = 0;
+	int time_stamp_flag = 0;
+	int buffer_length = 0;
+	int stop_playback = 0;
+
+	pr_debug("%s opcode =%08x\n", __func__, opcode);
+	switch (opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2: {
+		uint32_t *ptrmem = (uint32_t *)&param;
+
+		pr_debug("ASM_DATA_EVENT_WRITE_DONE\n");
+		pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+		prtd->pcm_irq_pos += prtd->pcm_count;
+		if (atomic_read(&prtd->start))
+			snd_pcm_period_elapsed(substream);
+		else
+			if (substream->timer_running)
+				snd_timer_interrupt(substream->timer, 1);
+		atomic_inc(&prtd->out_count);
+		wake_up(&the_locks.write_wait);
+		if (!atomic_read(&prtd->start)) {
+			atomic_set(&prtd->pending_buffer, 1);
+			break;
+		}
+		atomic_set(&prtd->pending_buffer, 0);
+
+		/*
+		 * check for underrun
+		 */
+		snd_pcm_stream_lock_irq(substream);
+		if (runtime->status->hw_ptr >= runtime->control->appl_ptr) {
+			runtime->render_flag |= SNDRV_RENDER_STOPPED;
+			stop_playback = 1;
+		}
+		snd_pcm_stream_unlock_irq(substream);
+
+		if (stop_playback) {
+			pr_err("underrun! render stopped\n");
+			break;
+		}
+
+		buf = prtd->audio_client->port[IN].buf;
+		pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+				__func__, prtd->pcm_count, prtd->out_head);
+		temp = buf[0].phys + (prtd->out_head * prtd->pcm_count);
+		pr_debug("%s:writing buffer[%d] from 0x%pK\n",
+			__func__, prtd->out_head, &temp);
+
+		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+			time_stamp_flag = SET_TIMESTAMP;
+		else
+			time_stamp_flag = NO_TIMESTAMP;
+		memcpy(&output_meta_data, (char *)(buf->data +
+			prtd->out_head * prtd->pcm_count),
+			COMPRE_OUTPUT_METADATA_SIZE);
+
+		buffer_length = output_meta_data.frame_size;
+		pr_debug("meta_data_length: %d, frame_length: %d\n",
+			 output_meta_data.meta_data_length,
+			 output_meta_data.frame_size);
+		pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
+			 output_meta_data.timestamp_msw,
+			 output_meta_data.timestamp_lsw);
+		if (buffer_length == 0) {
+			pr_debug("Received a zero length buffer-break out");
+			break;
+		}
+		param.paddr = temp + output_meta_data.meta_data_length;
+		param.len = buffer_length;
+		param.msw_ts = output_meta_data.timestamp_msw;
+		param.lsw_ts = output_meta_data.timestamp_lsw;
+		param.flags = time_stamp_flag;
+		param.uid = prtd->session_id;
+		for (i = 0; i < sizeof(struct audio_aio_write_param)/4;
+					i++, ++ptrmem)
+			pr_debug("cmd[%d]=0x%08x\n", i, *ptrmem);
+		if (q6asm_async_write(prtd->audio_client,
+					&param) < 0)
+			pr_err("%s:q6asm_async_write failed\n",
+				__func__);
+		else
+			prtd->out_head =
+				(prtd->out_head + 1) & (runtime->periods - 1);
+		break;
+	}
+	case ASM_DATA_EVENT_RENDERED_EOS:
+		pr_debug("ASM_DATA_CMDRSP_EOS\n");
+		if (atomic_read(&prtd->eos)) {
+			pr_debug("ASM_DATA_CMDRSP_EOS wake up\n");
+			prtd->cmd_ack = 1;
+			wake_up(&the_locks.eos_wait);
+			atomic_set(&prtd->eos, 0);
+		}
+		break;
+	case ASM_DATA_EVENT_READ_DONE_V2: {
+		pr_debug("ASM_DATA_EVENT_READ_DONE\n");
+		pr_debug("buf = %pK, data = 0x%X, *data = %pK,\n"
+			 "prtd->pcm_irq_pos = %d\n",
+				prtd->audio_client->port[OUT].buf,
+			 *(uint32_t *)prtd->audio_client->port[OUT].buf->data,
+				prtd->audio_client->port[OUT].buf->data,
+				prtd->pcm_irq_pos);
+
+		memcpy(prtd->audio_client->port[OUT].buf->data +
+			   prtd->pcm_irq_pos, (ptrmem + READDONE_IDX_SIZE),
+			   COMPRE_CAPTURE_HEADER_SIZE);
+		pr_debug("buf = %pK, updated data = 0x%X, *data = %pK\n",
+				prtd->audio_client->port[OUT].buf,
+			*(uint32_t *)(prtd->audio_client->port[OUT].buf->data +
+				prtd->pcm_irq_pos),
+				prtd->audio_client->port[OUT].buf->data);
+		if (!atomic_read(&prtd->start))
+			break;
+		pr_debug("frame size=%d, buffer = 0x%X\n",
+				ptrmem[READDONE_IDX_SIZE],
+				ptrmem[READDONE_IDX_BUFADD_LSW]);
+		if (ptrmem[READDONE_IDX_SIZE] > COMPRE_CAPTURE_MAX_FRAME_SIZE) {
+			pr_err("Frame length exceeded the max length");
+			break;
+		}
+		buf = prtd->audio_client->port[OUT].buf;
+
+		pr_debug("pcm_irq_pos=%d, buf[0].phys = 0x%pK\n",
+				prtd->pcm_irq_pos, &buf[0].phys);
+		read_param.len = prtd->pcm_count - COMPRE_CAPTURE_HEADER_SIZE;
+		read_param.paddr = buf[0].phys +
+			prtd->pcm_irq_pos + COMPRE_CAPTURE_HEADER_SIZE;
+		prtd->pcm_irq_pos += prtd->pcm_count;
+
+		if (atomic_read(&prtd->start))
+			snd_pcm_period_elapsed(substream);
+
+		q6asm_async_read(prtd->audio_client, &read_param);
+		break;
+	}
+	case APR_BASIC_RSP_RESULT: {
+		switch (payload[0]) {
+		case ASM_SESSION_CMD_RUN_V2: {
+			if (substream->stream
+				!= SNDRV_PCM_STREAM_PLAYBACK) {
+				atomic_set(&prtd->start, 1);
+				break;
+			}
+			if (!atomic_read(&prtd->pending_buffer))
+				break;
+			pr_debug("%s: writing %d bytes of buffer[%d] to dsp\n",
+				__func__, prtd->pcm_count, prtd->out_head);
+			buf = prtd->audio_client->port[IN].buf;
+			pr_debug("%s: writing buffer[%d] from 0x%pK head %d count %d\n",
+				__func__, prtd->out_head, &buf[0].phys,
+				prtd->pcm_count, prtd->out_head);
+			if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+				time_stamp_flag = SET_TIMESTAMP;
+			else
+				time_stamp_flag = NO_TIMESTAMP;
+			memcpy(&output_meta_data, (char *)(buf->data +
+				prtd->out_head * prtd->pcm_count),
+				COMPRE_OUTPUT_METADATA_SIZE);
+			buffer_length = output_meta_data.frame_size;
+			pr_debug("meta_data_length: %d, frame_length: %d\n",
+				 output_meta_data.meta_data_length,
+				 output_meta_data.frame_size);
+			pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
+				 output_meta_data.timestamp_msw,
+				 output_meta_data.timestamp_lsw);
+			param.paddr = buf[prtd->out_head].phys
+					+ output_meta_data.meta_data_length;
+			param.len = buffer_length;
+			param.msw_ts = output_meta_data.timestamp_msw;
+			param.lsw_ts = output_meta_data.timestamp_lsw;
+			param.flags = time_stamp_flag;
+			param.uid = prtd->session_id;
+			param.metadata_len = COMPRE_OUTPUT_METADATA_SIZE;
+			if (q6asm_async_write(prtd->audio_client,
+						&param) < 0)
+				pr_err("%s:q6asm_async_write failed\n",
+					__func__);
+			else
+				prtd->out_head =
+					(prtd->out_head + 1)
+					& (runtime->periods - 1);
+			atomic_set(&prtd->pending_buffer, 0);
+		}
+			break;
+		case ASM_STREAM_CMD_FLUSH:
+			pr_debug("ASM_STREAM_CMD_FLUSH\n");
+			prtd->cmd_ack = 1;
+			wake_up(&the_locks.flush_wait);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	default:
+		pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+		break;
+	}
+}
+
+static int msm_compr_send_ddp_cfg(struct audio_client *ac,
+					struct snd_dec_ddp *ddp)
+{
+	int i, rc;
+
+	pr_debug("%s\n", __func__);
+
+	if (ddp->params_length / 2 > SND_DEC_DDP_MAX_PARAMS) {
+		pr_err("%s: Invalid number of params %u, max allowed %u\n",
+			__func__, ddp->params_length / 2,
+			SND_DEC_DDP_MAX_PARAMS);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ddp->params_length/2; i++) {
+		rc = q6asm_ds1_set_endp_params(ac, ddp->params_id[i],
+						ddp->params_value[i]);
+		if (rc) {
+			pr_err("sending params_id: %d failed\n",
+				ddp->params_id[i]);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+static int msm_compr_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	struct snd_pcm_hw_params *params;
+	struct asm_aac_cfg aac_cfg;
+	uint16_t bits_per_sample = 16;
+	int ret;
+
+	struct asm_softpause_params softpause = {
+		.enable = SOFT_PAUSE_ENABLE,
+		.period = SOFT_PAUSE_PERIOD,
+		.step = SOFT_PAUSE_STEP,
+		.rampingcurve = SOFT_PAUSE_CURVE_LINEAR,
+	};
+	struct asm_softvolume_params softvol = {
+		.period = SOFT_VOLUME_PERIOD,
+		.step = SOFT_VOLUME_STEP,
+		.rampingcurve = SOFT_VOLUME_CURVE_LINEAR,
+	};
+
+	pr_debug("%s\n", __func__);
+
+	params = &soc_prtd->dpcm[substream->stream].hw_params;
+	if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
+		bits_per_sample = 24;
+
+	ret = q6asm_open_write_v2(prtd->audio_client,
+			compr->codec, bits_per_sample);
+	if (ret < 0) {
+		pr_err("%s: Session out open failed\n",
+				__func__);
+		return -ENOMEM;
+	}
+	msm_pcm_routing_reg_phy_stream(
+			soc_prtd->dai_link->id,
+			prtd->audio_client->perf_mode,
+			prtd->session_id,
+			substream->stream);
+	/*
+	 * the number of channels are required to call volume api
+	 * accoridngly. So, get channels from hw params
+	 */
+	if ((params_channels(params) > 0) &&
+			(params_periods(params) <= runtime->hw.channels_max))
+		prtd->channel_mode = params_channels(params);
+
+	ret = q6asm_set_softpause(prtd->audio_client, &softpause);
+	if (ret < 0)
+		pr_err("%s: Send SoftPause Param failed ret=%d\n",
+				__func__, ret);
+	ret = q6asm_set_softvolume(prtd->audio_client, &softvol);
+	if (ret < 0)
+		pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+				__func__, ret);
+
+	ret = q6asm_set_io_mode(prtd->audio_client,
+			(COMPRESSED_IO | ASYNC_IO_MODE));
+	if (ret < 0) {
+		pr_err("%s: Set IO mode failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_irq_pos = 0;
+	/* rate and channels are sent to audio driver */
+	prtd->samp_rate = runtime->rate;
+	prtd->channel_mode = runtime->channels;
+	prtd->out_head = 0;
+	atomic_set(&prtd->out_count, runtime->periods);
+
+	if (prtd->enabled)
+		return 0;
+
+	switch (compr->info.codec_param.codec.id) {
+	case SND_AUDIOCODEC_MP3:
+		/* No media format block for mp3 */
+		break;
+	case SND_AUDIOCODEC_AAC:
+		pr_debug("%s: SND_AUDIOCODEC_AAC\n", __func__);
+		memset(&aac_cfg, 0x0, sizeof(struct asm_aac_cfg));
+		aac_cfg.aot = AAC_ENC_MODE_EAAC_P;
+		aac_cfg.format = 0x03;
+		aac_cfg.ch_cfg = runtime->channels;
+		aac_cfg.sample_rate =  runtime->rate;
+		ret = q6asm_media_format_block_aac(prtd->audio_client,
+					&aac_cfg);
+		if (ret < 0)
+			pr_err("%s: CMD Format block failed\n", __func__);
+		break;
+	case SND_AUDIOCODEC_AC3: {
+		struct snd_dec_ddp *ddp =
+				&compr->info.codec_param.codec.options.ddp;
+		pr_debug("%s: SND_AUDIOCODEC_AC3\n", __func__);
+		ret = msm_compr_send_ddp_cfg(prtd->audio_client, ddp);
+		if (ret < 0)
+			pr_err("%s: DDP CMD CFG failed\n", __func__);
+		break;
+	}
+	case SND_AUDIOCODEC_EAC3: {
+		struct snd_dec_ddp *ddp =
+				&compr->info.codec_param.codec.options.ddp;
+		pr_debug("%s: SND_AUDIOCODEC_EAC3\n", __func__);
+		ret = msm_compr_send_ddp_cfg(prtd->audio_client, ddp);
+		if (ret < 0)
+			pr_err("%s: DDP CMD CFG failed\n", __func__);
+		break;
+	}
+	default:
+		return -EINVAL;
+	}
+
+	prtd->enabled = 1;
+	prtd->cmd_ack = 0;
+	prtd->cmd_interrupt = 0;
+
+	return 0;
+}
+
+static int msm_compr_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	struct audio_buffer *buf = prtd->audio_client->port[OUT].buf;
+	struct snd_codec *codec = &compr->info.codec_param.codec;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct audio_aio_read_param read_param;
+	uint16_t bits_per_sample = 16;
+	int ret = 0;
+	int i;
+
+	prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_irq_pos = 0;
+
+	if (runtime->format == SNDRV_PCM_FORMAT_S24_LE)
+		bits_per_sample = 24;
+
+	if (!msm_compr_capture_codecs(
+				compr->info.codec_param.codec.id)) {
+		/*
+		 * request codec invalid or not supported,
+		 * use default compress format
+		 */
+		compr->info.codec_param.codec.id =
+			SND_AUDIOCODEC_AMRWB;
+	}
+	switch (compr->info.codec_param.codec.id) {
+	case SND_AUDIOCODEC_AMRWB:
+		pr_debug("q6asm_open_read(FORMAT_AMRWB)\n");
+		ret = q6asm_open_read(prtd->audio_client,
+				FORMAT_AMRWB);
+		if (ret < 0) {
+			pr_err("%s: compressed Session out open failed\n",
+					__func__);
+			return -ENOMEM;
+		}
+		pr_debug("msm_pcm_routing_reg_phy_stream\n");
+		msm_pcm_routing_reg_phy_stream(
+				soc_prtd->dai_link->id,
+				prtd->audio_client->perf_mode,
+				prtd->session_id, substream->stream);
+		break;
+	default:
+		pr_debug("q6asm_open_read_compressed(COMPRESSED_META_DATA_MODE)\n");
+		/*
+		 * ret = q6asm_open_read_compressed(prtd->audio_client,
+		 * MAX_NUM_FRAMES_PER_BUFFER,
+		 * COMPRESSED_META_DATA_MODE);
+		 */
+			ret = -EINVAL;
+			break;
+	}
+
+	if (ret < 0) {
+		pr_err("%s: compressed Session out open failed\n",
+				__func__);
+		return -ENOMEM;
+	}
+
+	ret = q6asm_set_io_mode(prtd->audio_client,
+		(COMPRESSED_IO | ASYNC_IO_MODE));
+		if (ret < 0) {
+			pr_err("%s: Set IO mode failed\n", __func__);
+				return -ENOMEM;
+		}
+
+	if (!msm_compr_capture_codecs(codec->id)) {
+		/*
+		 * request codec invalid or not supported,
+		 * use default compress format
+		 */
+		codec->id = SND_AUDIOCODEC_AMRWB;
+	}
+	/* rate and channels are sent to audio driver */
+	prtd->samp_rate = runtime->rate;
+	prtd->channel_mode = runtime->channels;
+
+	if (prtd->enabled)
+		return ret;
+	read_param.len = prtd->pcm_count;
+
+	switch (codec->id) {
+	case SND_AUDIOCODEC_AMRWB:
+		pr_debug("SND_AUDIOCODEC_AMRWB\n");
+		ret = q6asm_enc_cfg_blk_amrwb(prtd->audio_client,
+			MAX_NUM_FRAMES_PER_BUFFER,
+			/*
+			 * use fixed band mode and dtx mode
+			 * band mode - 23.85 kbps
+			 */
+			AMR_WB_BAND_MODE,
+			/* dtx mode - disable */
+			AMR_WB_DTX_MODE);
+		if (ret < 0)
+			pr_err("%s: CMD Format block failed: %d\n",
+				__func__, ret);
+		break;
+	default:
+		pr_debug("No config for codec %d\n", codec->id);
+	}
+	pr_debug("%s: Samp_rate = %d, Channel = %d, pcm_size = %d,\n"
+			 "pcm_count = %d, periods = %d\n",
+			 __func__, prtd->samp_rate, prtd->channel_mode,
+			 prtd->pcm_size, prtd->pcm_count, runtime->periods);
+
+	for (i = 0; i < runtime->periods; i++) {
+		read_param.uid = i;
+		switch (codec->id) {
+		case SND_AUDIOCODEC_AMRWB:
+			read_param.len = prtd->pcm_count
+					- COMPRE_CAPTURE_HEADER_SIZE;
+			read_param.paddr = buf[i].phys
+					+ COMPRE_CAPTURE_HEADER_SIZE;
+			pr_debug("Push buffer [%d] to DSP, paddr: %pK, vaddr: %pK\n",
+					i, &read_param.paddr,
+					buf[i].data);
+			q6asm_async_read(prtd->audio_client, &read_param);
+			break;
+		default:
+			read_param.paddr = buf[i].phys;
+			/* q6asm_async_read_compressed(prtd->audio_client,
+			 * &read_param);
+			 */
+			pr_debug("%s: To add support for read compressed\n",
+								__func__);
+			ret = -EINVAL;
+			break;
+		}
+	}
+	prtd->periods = runtime->periods;
+
+	prtd->enabled = 1;
+
+	return ret;
+}
+
+static int msm_compr_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+
+	pr_debug("%s\n", __func__);
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		prtd->pcm_irq_pos = 0;
+
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			if (!msm_compr_capture_codecs(
+				compr->info.codec_param.codec.id)) {
+				/*
+				 * request codec invalid or not supported,
+				 * use default compress format
+				 */
+				compr->info.codec_param.codec.id =
+				SND_AUDIOCODEC_AMRWB;
+			}
+			switch (compr->info.codec_param.codec.id) {
+			case SND_AUDIOCODEC_AMRWB:
+				break;
+			default:
+				msm_pcm_routing_reg_psthr_stream(
+					soc_prtd->dai_link->id,
+					prtd->session_id, substream->stream);
+				break;
+			}
+		}
+		atomic_set(&prtd->pending_buffer, 1);
+		/* fallthrough */
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("%s: Trigger start\n", __func__);
+		q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+		atomic_set(&prtd->start, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			switch (compr->info.codec_param.codec.id) {
+			case SND_AUDIOCODEC_AMRWB:
+				break;
+			default:
+				msm_pcm_routing_reg_psthr_stream(
+					soc_prtd->dai_link->id,
+					prtd->session_id, substream->stream);
+				break;
+			}
+		}
+		atomic_set(&prtd->start, 0);
+		runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+		q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+		atomic_set(&prtd->start, 0);
+		runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static void populate_codec_list(struct compr_audio *compr,
+		struct snd_pcm_runtime *runtime)
+{
+	pr_debug("%s\n", __func__);
+	/* MP3 Block */
+	compr->info.compr_cap.num_codecs = 5;
+	compr->info.compr_cap.min_fragment_size = runtime->hw.period_bytes_min;
+	compr->info.compr_cap.max_fragment_size = runtime->hw.period_bytes_max;
+	compr->info.compr_cap.min_fragments = runtime->hw.periods_min;
+	compr->info.compr_cap.max_fragments = runtime->hw.periods_max;
+	compr->info.compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
+	compr->info.compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
+	compr->info.compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
+	compr->info.compr_cap.codecs[3] = SND_AUDIOCODEC_EAC3;
+	compr->info.compr_cap.codecs[4] = SND_AUDIOCODEC_AMRWB;
+	/* Add new codecs here */
+}
+
+static int msm_compr_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr;
+	struct msm_audio *prtd;
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+	compr = kzalloc(sizeof(struct compr_audio), GFP_KERNEL);
+	if (compr == NULL) {
+		pr_err("Failed to allocate memory for msm_audio\n");
+		return -ENOMEM;
+	}
+	prtd = &compr->prtd;
+	prtd->substream = substream;
+	runtime->render_flag = SNDRV_DMA_MODE;
+	prtd->audio_client = q6asm_audio_client_alloc(
+				(app_cb)compr_event_handler, compr);
+	if (!prtd->audio_client) {
+		pr_info("%s: Could not allocate memory\n", __func__);
+		kfree(prtd);
+		return -ENOMEM;
+	}
+
+	prtd->audio_client->perf_mode = false;
+	pr_info("%s: session ID %d\n", __func__, prtd->audio_client->session);
+
+	prtd->session_id = prtd->audio_client->session;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		runtime->hw = msm_compr_hardware_playback;
+		prtd->cmd_ack = 1;
+	} else {
+		runtime->hw = msm_compr_hardware_capture;
+	}
+
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+			SNDRV_PCM_HW_PARAM_RATE,
+			&constraints_sample_rates);
+	if (ret < 0)
+		pr_info("snd_pcm_hw_constraint_list failed\n");
+	/* Ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+			    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+	prtd->dsp_cnt = 0;
+	atomic_set(&prtd->pending_buffer, 1);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		compr->codec = FORMAT_MP3;
+	populate_codec_list(compr, runtime);
+	runtime->private_data = compr;
+	atomic_set(&prtd->eos, 0);
+	return 0;
+}
+
+static int compressed_set_volume(struct msm_audio *prtd, uint32_t volume)
+{
+	int rc = 0;
+	int avg_vol = 0;
+	int lgain = (volume >> 16) & 0xFFFF;
+	int rgain = volume & 0xFFFF;
+
+	if (prtd && prtd->audio_client) {
+		pr_debug("%s: channels %d volume 0x%x\n", __func__,
+			prtd->channel_mode, volume);
+		if ((prtd->channel_mode == 2) &&
+			(lgain != rgain)) {
+			pr_debug("%s: call q6asm_set_lrgain\n", __func__);
+			rc = q6asm_set_lrgain(prtd->audio_client, lgain, rgain);
+		} else {
+			avg_vol = (lgain + rgain)/2;
+			pr_debug("%s: call q6asm_set_volume\n", __func__);
+			rc = q6asm_set_volume(prtd->audio_client, avg_vol);
+		}
+		if (rc < 0) {
+			pr_err("%s: Send Volume command failed rc=%d\n",
+				__func__, rc);
+		}
+	}
+	return rc;
+}
+
+static int msm_compr_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	int dir = 0;
+
+	pr_debug("%s\n", __func__);
+
+	dir = IN;
+	atomic_set(&prtd->pending_buffer, 0);
+
+	prtd->pcm_irq_pos = 0;
+	q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+	q6asm_audio_client_buf_free_contiguous(dir,
+				prtd->audio_client);
+		msm_pcm_routing_dereg_phy_stream(
+			soc_prtd->dai_link->id,
+			SNDRV_PCM_STREAM_PLAYBACK);
+	q6asm_audio_client_free(prtd->audio_client);
+	kfree(prtd);
+	return 0;
+}
+
+static int msm_compr_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	int dir = OUT;
+
+	pr_debug("%s\n", __func__);
+	atomic_set(&prtd->pending_buffer, 0);
+	q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+	q6asm_audio_client_buf_free_contiguous(dir,
+				prtd->audio_client);
+	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
+				SNDRV_PCM_STREAM_CAPTURE);
+	q6asm_audio_client_free(prtd->audio_client);
+	kfree(prtd);
+	return 0;
+}
+
+static int msm_compr_close(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_compr_playback_close(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_compr_capture_close(substream);
+	return ret;
+}
+
+static int msm_compr_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_compr_playback_prepare(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_compr_capture_prepare(substream);
+	return ret;
+}
+
+static snd_pcm_uframes_t msm_compr_pointer(struct snd_pcm_substream *substream)
+{
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+
+	if (prtd->pcm_irq_pos >= prtd->pcm_size)
+		prtd->pcm_irq_pos = 0;
+
+	pr_debug("%s: pcm_irq_pos = %d, pcm_size = %d, sample_bits = %d,\n"
+			 "frame_bits = %d\n", __func__, prtd->pcm_irq_pos,
+			 prtd->pcm_size, runtime->sample_bits,
+			 runtime->frame_bits);
+	return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_compr_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	struct audio_client *ac = prtd->audio_client;
+	struct audio_port_data *apd = ac->port;
+	struct audio_buffer *ab;
+	int dir = -1;
+
+	prtd->mmap_flag = 1;
+	runtime->render_flag = SNDRV_NON_DMA_MODE;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+	ab = &(apd[dir].buf[0]);
+
+	return msm_audio_ion_mmap(ab, vma);
+}
+
+static int msm_compr_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct audio_buffer *buf;
+	int dir, ret;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+	/* Modifying kernel hardware params based on userspace config */
+	if (params_periods(params) > 0 &&
+		(params_periods(params) != runtime->hw.periods_max)) {
+		runtime->hw.periods_max = params_periods(params);
+	}
+	if (params_period_bytes(params) > 0 &&
+		(params_period_bytes(params) != runtime->hw.period_bytes_min)) {
+		runtime->hw.period_bytes_min = params_period_bytes(params);
+	}
+	runtime->hw.buffer_bytes_max =
+			runtime->hw.period_bytes_min * runtime->hw.periods_max;
+	pr_debug("allocate %zd buffers each of size %d\n",
+		runtime->hw.period_bytes_min,
+		runtime->hw.periods_max);
+	ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+			prtd->audio_client,
+			runtime->hw.period_bytes_min,
+			runtime->hw.periods_max);
+	if (ret < 0) {
+		pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
+						ret);
+		return -ENOMEM;
+	}
+	buf = prtd->audio_client->port[dir].buf;
+
+	dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dma_buf->dev.dev = substream->pcm->card->dev;
+	dma_buf->private_data = NULL;
+	dma_buf->area = buf[0].data;
+	dma_buf->addr =  buf[0].phys;
+	dma_buf->bytes = runtime->hw.buffer_bytes_max;
+
+	pr_debug("%s: buf[%pK]dma_buf->area[%pK]dma_buf->addr[%pK]\n"
+		 "dma_buf->bytes[%zd]\n", __func__,
+		 (void *)buf, (void *)dma_buf->area,
+		 &dma_buf->addr, dma_buf->bytes);
+	if (!dma_buf->area)
+		return -ENOMEM;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	return 0;
+}
+
+static int msm_compr_ioctl_shared(struct snd_pcm_substream *substream,
+		unsigned int cmd, void *arg)
+{
+	int rc = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	uint64_t timestamp;
+	uint64_t temp;
+
+	switch (cmd) {
+	case SNDRV_COMPRESS_TSTAMP: {
+		struct snd_compr_tstamp *tstamp;
+
+		pr_debug("SNDRV_COMPRESS_TSTAMP\n");
+		tstamp = arg;
+		memset(tstamp, 0x0, sizeof(*tstamp));
+		rc = q6asm_get_session_time(prtd->audio_client, &timestamp);
+		if (rc < 0) {
+			pr_err("%s: Get Session Time return value =%lld\n",
+				__func__, timestamp);
+			return -EAGAIN;
+		}
+		temp = (timestamp * 2 * runtime->channels);
+		temp = temp * (runtime->rate/1000);
+		temp = div_u64(temp, 1000);
+		tstamp->sampling_rate = runtime->rate;
+		tstamp->timestamp = timestamp;
+		pr_debug("%s: bytes_consumed:,timestamp = %lld,\n",
+						__func__,
+			tstamp->timestamp);
+		return 0;
+	}
+	case SNDRV_COMPRESS_GET_CAPS: {
+		struct snd_compr_caps *caps;
+
+		caps = arg;
+		memset(caps, 0, sizeof(*caps));
+		pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
+		memcpy(caps, &compr->info.compr_cap, sizeof(*caps));
+		return 0;
+	}
+	case SNDRV_COMPRESS_SET_PARAMS:
+		pr_debug("SNDRV_COMPRESS_SET_PARAMS:\n");
+		memcpy(&compr->info.codec_param, (void *) arg,
+			sizeof(struct snd_compr_params));
+		switch (compr->info.codec_param.codec.id) {
+		case SND_AUDIOCODEC_MP3:
+			/* For MP3 we dont need any other parameter */
+			pr_debug("SND_AUDIOCODEC_MP3\n");
+			compr->codec = FORMAT_MP3;
+			break;
+		case SND_AUDIOCODEC_AAC:
+			pr_debug("SND_AUDIOCODEC_AAC\n");
+			compr->codec = FORMAT_MPEG4_AAC;
+			break;
+		case SND_AUDIOCODEC_AC3: {
+			char params_value[MAX_AC3_PARAM_SIZE];
+			int *params_value_data = (int *)params_value;
+			/* 36 is the max param length for ddp */
+			int i;
+			struct snd_dec_ddp *ddp =
+				&compr->info.codec_param.codec.options.ddp;
+			uint32_t params_length = 0;
+
+			memset(params_value, 0, MAX_AC3_PARAM_SIZE);
+			/* check integer overflow */
+			if (ddp->params_length > UINT_MAX/sizeof(int)) {
+				pr_err("%s: Integer overflow ddp->params_length %d\n",
+				__func__, ddp->params_length);
+				return -EINVAL;
+			}
+			params_length = ddp->params_length*sizeof(int);
+			if (params_length > MAX_AC3_PARAM_SIZE) {
+				/*MAX is 36*sizeof(int) this should not happen*/
+				pr_err("%s: params_length(%d) is greater than %zd\n",
+				__func__, params_length, MAX_AC3_PARAM_SIZE);
+				return -EINVAL;
+			}
+			pr_debug("SND_AUDIOCODEC_AC3\n");
+			compr->codec = FORMAT_AC3;
+			pr_debug("params_length: %d\n", ddp->params_length);
+			for (i = 0; i < params_length/sizeof(int); i++)
+				pr_debug("params_value[%d]: %x\n", i,
+					params_value_data[i]);
+			for (i = 0; i < ddp->params_length/2; i++) {
+				ddp->params_id[i] = params_value_data[2*i];
+				ddp->params_value[i] = params_value_data[2*i+1];
+			}
+			if (atomic_read(&prtd->start)) {
+				rc = msm_compr_send_ddp_cfg(prtd->audio_client,
+								ddp);
+				if (rc < 0)
+					pr_err("%s: DDP CMD CFG failed\n",
+						__func__);
+			}
+			break;
+		}
+		case SND_AUDIOCODEC_EAC3: {
+			char params_value[MAX_AC3_PARAM_SIZE];
+			int *params_value_data = (int *)params_value;
+			/* 36 is the max param length for ddp */
+			int i;
+			struct snd_dec_ddp *ddp =
+				&compr->info.codec_param.codec.options.ddp;
+			uint32_t params_length = 0;
+
+			memset(params_value, 0, MAX_AC3_PARAM_SIZE);
+			/* check integer overflow */
+			if (ddp->params_length > UINT_MAX/sizeof(int)) {
+				pr_err("%s: Integer overflow ddp->params_length %d\n",
+				__func__, ddp->params_length);
+				return -EINVAL;
+			}
+			params_length = ddp->params_length*sizeof(int);
+			if (params_length > MAX_AC3_PARAM_SIZE) {
+				/*MAX is 36*sizeof(int) this should not happen*/
+				pr_err("%s: params_length(%d) is greater than %zd\n",
+				__func__, params_length, MAX_AC3_PARAM_SIZE);
+				return -EINVAL;
+			}
+			pr_debug("SND_AUDIOCODEC_EAC3\n");
+			compr->codec = FORMAT_EAC3;
+			pr_debug("params_length: %d\n", ddp->params_length);
+			for (i = 0; i < ddp->params_length; i++)
+				pr_debug("params_value[%d]: %x\n", i,
+					params_value_data[i]);
+			for (i = 0; i < ddp->params_length/2; i++) {
+				ddp->params_id[i] = params_value_data[2*i];
+				ddp->params_value[i] = params_value_data[2*i+1];
+			}
+			if (atomic_read(&prtd->start)) {
+				rc = msm_compr_send_ddp_cfg(prtd->audio_client,
+								ddp);
+				if (rc < 0)
+					pr_err("%s: DDP CMD CFG failed\n",
+						__func__);
+			}
+			break;
+		}
+		default:
+			pr_debug("FORMAT_LINEAR_PCM\n");
+			compr->codec = FORMAT_LINEAR_PCM;
+			break;
+		}
+		return 0;
+	case SNDRV_PCM_IOCTL1_RESET:
+		pr_debug("SNDRV_PCM_IOCTL1_RESET\n");
+		/* Flush only when session is started during CAPTURE,
+		 * while PLAYBACK has no such restriction.
+		 */
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ||
+			  (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+						atomic_read(&prtd->start))) {
+			if (atomic_read(&prtd->eos)) {
+				prtd->cmd_interrupt = 1;
+				wake_up(&the_locks.eos_wait);
+				atomic_set(&prtd->eos, 0);
+			}
+
+			/* A unlikely race condition possible with FLUSH
+			 * DRAIN if ack is set by flush and reset by drain
+			 */
+			prtd->cmd_ack = 0;
+			rc = q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+			if (rc < 0) {
+				pr_err("%s: flush cmd failed rc=%d\n",
+					__func__, rc);
+				return rc;
+			}
+			rc = wait_event_timeout(the_locks.flush_wait,
+				prtd->cmd_ack, 5 * HZ);
+			if (!rc)
+				pr_err("Flush cmd timeout\n");
+			prtd->pcm_irq_pos = 0;
+		}
+		break;
+	case SNDRV_COMPRESS_DRAIN:
+		pr_debug("%s: SNDRV_COMPRESS_DRAIN\n", __func__);
+		if (atomic_read(&prtd->pending_buffer)) {
+			pr_debug("%s: no pending writes, drain would block\n",
+			 __func__);
+			return -EWOULDBLOCK;
+		}
+
+		atomic_set(&prtd->eos, 1);
+		atomic_set(&prtd->pending_buffer, 0);
+		prtd->cmd_ack = 0;
+		q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+		/* Wait indefinitely for  DRAIN. Flush can also signal this*/
+		rc = wait_event_interruptible(the_locks.eos_wait,
+			(prtd->cmd_ack || prtd->cmd_interrupt));
+
+		if (rc < 0)
+			pr_err("EOS cmd interrupted\n");
+		pr_debug("%s: SNDRV_COMPRESS_DRAIN  out of wait\n", __func__);
+
+		if (prtd->cmd_interrupt)
+			rc = -EINTR;
+
+		prtd->cmd_interrupt = 0;
+		return rc;
+	default:
+		break;
+	}
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+#ifdef CONFIG_COMPAT
+struct snd_enc_wma32 {
+	u32 super_block_align; /* WMA Type-specific data */
+	u32 encodeopt1;
+	u32 encodeopt2;
+};
+
+struct snd_enc_vorbis32 {
+	s32 quality;
+	u32 managed;
+	u32 max_bit_rate;
+	u32 min_bit_rate;
+	u32 downmix;
+};
+
+struct snd_enc_real32 {
+	u32 quant_bits;
+	u32 start_region;
+	u32 num_regions;
+};
+
+struct snd_enc_flac32 {
+	u32 num;
+	u32 gain;
+};
+
+struct snd_enc_generic32 {
+	u32 bw;	/* encoder bandwidth */
+	s32 reserved[15];
+};
+struct snd_dec_ddp32 {
+	u32 params_length;
+	u32 params_id[18];
+	u32 params_value[18];
+};
+
+union snd_codec_options32 {
+	struct snd_enc_wma32 wma;
+	struct snd_enc_vorbis32 vorbis;
+	struct snd_enc_real32 real;
+	struct snd_enc_flac32 flac;
+	struct snd_enc_generic32 generic;
+	struct snd_dec_ddp32 ddp;
+};
+
+struct snd_codec32 {
+	u32 id;
+	u32 ch_in;
+	u32 ch_out;
+	u32 sample_rate;
+	u32 bit_rate;
+	u32 rate_control;
+	u32 profile;
+	u32 level;
+	u32 ch_mode;
+	u32 format;
+	u32 align;
+	union snd_codec_options32 options;
+	u32 reserved[3];
+};
+
+struct snd_compressed_buffer32 {
+	u32 fragment_size;
+	u32 fragments;
+};
+
+struct snd_compr_params32 {
+	struct snd_compressed_buffer32 buffer;
+	struct snd_codec32 codec;
+	u8 no_wake_mode;
+};
+
+struct snd_compr_caps32 {
+	u32 num_codecs;
+	u32 direction;
+	u32 min_fragment_size;
+	u32 max_fragment_size;
+	u32 min_fragments;
+	u32 max_fragments;
+	u32 codecs[MAX_NUM_CODECS];
+	u32 reserved[11];
+};
+struct snd_compr_tstamp32 {
+	u32 byte_offset;
+	u32 copied_total;
+	compat_ulong_t pcm_frames;
+	compat_ulong_t pcm_io_frames;
+	u32 sampling_rate;
+	compat_u64 timestamp;
+};
+enum {
+	SNDRV_COMPRESS_TSTAMP32 = _IOR('C', 0x20, struct snd_compr_tstamp32),
+	SNDRV_COMPRESS_GET_CAPS32 = _IOWR('C', 0x10, struct snd_compr_caps32),
+	SNDRV_COMPRESS_SET_PARAMS32 =
+	_IOW('C', 0x12, struct snd_compr_params32),
+};
+static int msm_compr_compat_ioctl(struct snd_pcm_substream *substream,
+		unsigned int cmd, void *arg)
+{
+	int err = 0;
+
+	switch (cmd) {
+	case SNDRV_COMPRESS_TSTAMP32: {
+		struct snd_compr_tstamp tstamp;
+		struct snd_compr_tstamp32 tstamp32;
+
+		memset(&tstamp, 0, sizeof(tstamp));
+		memset(&tstamp32, 0, sizeof(tstamp32));
+		cmd = SNDRV_COMPRESS_TSTAMP;
+		err = msm_compr_ioctl_shared(substream, cmd, &tstamp);
+		if (err) {
+			pr_err("%s: COMPRESS_TSTAMP failed rc %d\n",
+			__func__, err);
+			goto bail_out;
+		}
+		tstamp32.byte_offset = tstamp.byte_offset;
+		tstamp32.copied_total = tstamp.copied_total;
+		tstamp32.pcm_frames = tstamp.pcm_frames;
+		tstamp32.pcm_io_frames = tstamp.pcm_io_frames;
+		tstamp32.sampling_rate = tstamp.sampling_rate;
+		tstamp32.timestamp = tstamp.timestamp;
+		if (copy_to_user(arg, &tstamp32, sizeof(tstamp32))) {
+			pr_err("%s: copytouser failed COMPRESS_TSTAMP32\n",
+			__func__);
+			err = -EFAULT;
+		}
+		break;
+	}
+	case SNDRV_COMPRESS_GET_CAPS32: {
+		struct snd_compr_caps caps;
+		struct snd_compr_caps32 caps32;
+		u32 i;
+
+		memset(&caps, 0, sizeof(caps));
+		memset(&caps32, 0, sizeof(caps32));
+		cmd = SNDRV_COMPRESS_GET_CAPS;
+		err = msm_compr_ioctl_shared(substream, cmd, &caps);
+		if (err) {
+			pr_err("%s: GET_CAPS failed rc %d\n",
+			__func__, err);
+			goto bail_out;
+		}
+		pr_debug("SNDRV_COMPRESS_GET_CAPS_32\n");
+		if (!err && caps.num_codecs >= MAX_NUM_CODECS) {
+			pr_err("%s: Invalid number of codecs\n", __func__);
+			err = -EINVAL;
+			goto bail_out;
+		}
+		caps32.direction = caps.direction;
+		caps32.max_fragment_size = caps.max_fragment_size;
+		caps32.max_fragments = caps.max_fragments;
+		caps32.min_fragment_size = caps.min_fragment_size;
+		caps32.num_codecs = caps.num_codecs;
+		for (i = 0; i < caps.num_codecs; i++)
+			caps32.codecs[i] = caps.codecs[i];
+		if (copy_to_user(arg, &caps32, sizeof(caps32))) {
+			pr_err("%s: copytouser failed COMPRESS_GETCAPS32\n",
+			__func__);
+			err = -EFAULT;
+		}
+		break;
+	}
+	case SNDRV_COMPRESS_SET_PARAMS32: {
+		struct snd_compr_params32 params32;
+		struct snd_compr_params params;
+
+		memset(&params32, 0, sizeof(params32));
+		memset(&params, 0, sizeof(params));
+		cmd = SNDRV_COMPRESS_SET_PARAMS;
+		if (copy_from_user(&params32, arg, sizeof(params32))) {
+			pr_err("%s: copyfromuser failed SET_PARAMS32\n",
+			__func__);
+			err = -EFAULT;
+			goto bail_out;
+		}
+		params.no_wake_mode = params32.no_wake_mode;
+		params.codec.id = params32.codec.id;
+		params.codec.ch_in = params32.codec.ch_in;
+		params.codec.ch_out = params32.codec.ch_out;
+		params.codec.sample_rate = params32.codec.sample_rate;
+		params.codec.bit_rate = params32.codec.bit_rate;
+		params.codec.rate_control = params32.codec.rate_control;
+		params.codec.profile = params32.codec.profile;
+		params.codec.level = params32.codec.level;
+		params.codec.ch_mode = params32.codec.ch_mode;
+		params.codec.format = params32.codec.format;
+		params.codec.align = params32.codec.align;
+
+		switch (params.codec.id) {
+		case SND_AUDIOCODEC_WMA:
+		case SND_AUDIOCODEC_WMA_PRO:
+			params.codec.options.wma.encodeopt1 =
+			params32.codec.options.wma.encodeopt1;
+			params.codec.options.wma.encodeopt2 =
+			params32.codec.options.wma.encodeopt2;
+			params.codec.options.wma.super_block_align =
+			params32.codec.options.wma.super_block_align;
+		break;
+		case SND_AUDIOCODEC_VORBIS:
+			params.codec.options.vorbis.downmix =
+			params32.codec.options.vorbis.downmix;
+			params.codec.options.vorbis.managed =
+			params32.codec.options.vorbis.managed;
+			params.codec.options.vorbis.max_bit_rate =
+			params32.codec.options.vorbis.max_bit_rate;
+			params.codec.options.vorbis.min_bit_rate =
+			params32.codec.options.vorbis.min_bit_rate;
+			params.codec.options.vorbis.quality =
+			params32.codec.options.vorbis.quality;
+		break;
+		case SND_AUDIOCODEC_REAL:
+			params.codec.options.real.num_regions =
+			params32.codec.options.real.num_regions;
+			params.codec.options.real.quant_bits =
+			params32.codec.options.real.quant_bits;
+			params.codec.options.real.start_region =
+			params32.codec.options.real.start_region;
+		break;
+		case SND_AUDIOCODEC_FLAC:
+			params.codec.options.flac.gain =
+			params32.codec.options.flac.gain;
+			params.codec.options.flac.num =
+			params32.codec.options.flac.num;
+		break;
+		case SND_AUDIOCODEC_DTS:
+		case SND_AUDIOCODEC_DTS_PASS_THROUGH:
+		case SND_AUDIOCODEC_DTS_LBR:
+		case SND_AUDIOCODEC_DTS_LBR_PASS_THROUGH:
+		case SND_AUDIOCODEC_DTS_TRANSCODE_LOOPBACK:
+		break;
+		case SND_AUDIOCODEC_AC3:
+		case SND_AUDIOCODEC_EAC3:
+			params.codec.options.ddp.params_length =
+			params32.codec.options.ddp.params_length;
+			memcpy(params.codec.options.ddp.params_value,
+			params32.codec.options.ddp.params_value,
+			sizeof(params32.codec.options.ddp.params_value));
+			memcpy(params.codec.options.ddp.params_id,
+			params32.codec.options.ddp.params_id,
+			sizeof(params32.codec.options.ddp.params_id));
+		break;
+		default:
+			params.codec.options.generic.bw =
+			params32.codec.options.generic.bw;
+		break;
+		}
+		if (!err)
+			err = msm_compr_ioctl_shared(substream, cmd, &params);
+		break;
+	}
+	default:
+		err = msm_compr_ioctl_shared(substream, cmd, arg);
+	}
+bail_out:
+	return err;
+
+}
+#endif
+static int msm_compr_ioctl(struct snd_pcm_substream *substream,
+		unsigned int cmd, void *arg)
+{
+	int err = 0;
+
+	if (!substream) {
+		pr_err("%s: Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s called with cmd = %d\n", __func__, cmd);
+	switch (cmd) {
+	case SNDRV_COMPRESS_TSTAMP: {
+		struct snd_compr_tstamp tstamp;
+
+		if (!arg) {
+			pr_err("%s: Invalid params Tstamp\n", __func__);
+			return -EINVAL;
+		}
+		err = msm_compr_ioctl_shared(substream, cmd, &tstamp);
+		if (err)
+			pr_err("%s: COMPRESS_TSTAMP failed rc %d\n",
+			__func__, err);
+		if (!err && copy_to_user(arg, &tstamp, sizeof(tstamp))) {
+			pr_err("%s: copytouser failed COMPRESS_TSTAMP\n",
+			__func__);
+			err = -EFAULT;
+		}
+		break;
+	}
+	case SNDRV_COMPRESS_GET_CAPS: {
+		struct snd_compr_caps cap;
+
+		if (!arg) {
+			pr_err("%s: Invalid params getcaps\n", __func__);
+			return -EINVAL;
+		}
+		pr_debug("SNDRV_COMPRESS_GET_CAPS\n");
+		err = msm_compr_ioctl_shared(substream, cmd, &cap);
+		if (err)
+			pr_err("%s: GET_CAPS failed rc %d\n",
+			__func__, err);
+		if (!err && copy_to_user(arg, &cap, sizeof(cap))) {
+			pr_err("%s: copytouser failed GET_CAPS\n",
+			__func__);
+			err = -EFAULT;
+		}
+		break;
+	}
+	case SNDRV_COMPRESS_SET_PARAMS: {
+		struct snd_compr_params params;
+
+		if (!arg) {
+			pr_err("%s: Invalid params setparam\n", __func__);
+			return -EINVAL;
+		}
+		if (copy_from_user(&params, arg,
+			sizeof(struct snd_compr_params))) {
+			pr_err("%s: SET_PARAMS\n", __func__);
+			return -EFAULT;
+		}
+		err = msm_compr_ioctl_shared(substream, cmd, &params);
+		if (err)
+			pr_err("%s: SET_PARAMS failed rc %d\n",
+			__func__, err);
+		break;
+	}
+	default:
+		err = msm_compr_ioctl_shared(substream, cmd, arg);
+	}
+	return err;
+}
+
+static int msm_compr_restart(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct compr_audio *compr = runtime->private_data;
+	struct msm_audio *prtd = &compr->prtd;
+	struct audio_aio_write_param param;
+	struct audio_buffer *buf = NULL;
+	struct output_meta_data_st output_meta_data;
+	int time_stamp_flag = 0;
+	int buffer_length = 0;
+
+	pr_debug("%s, trigger restart\n", __func__);
+
+	if (runtime->render_flag & SNDRV_RENDER_STOPPED) {
+		buf = prtd->audio_client->port[IN].buf;
+		pr_debug("%s:writing %d bytes of buffer[%d] to dsp 2\n",
+				__func__, prtd->pcm_count, prtd->out_head);
+		pr_debug("%s:writing buffer[%d] from 0x%08x\n",
+				__func__, prtd->out_head,
+				((unsigned int)buf[0].phys
+				+ (prtd->out_head * prtd->pcm_count)));
+
+		if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
+			time_stamp_flag = SET_TIMESTAMP;
+		else
+			time_stamp_flag = NO_TIMESTAMP;
+		memcpy(&output_meta_data, (char *)(buf->data +
+			prtd->out_head * prtd->pcm_count),
+			COMPRE_OUTPUT_METADATA_SIZE);
+
+		buffer_length = output_meta_data.frame_size;
+		pr_debug("meta_data_length: %d, frame_length: %d\n",
+			 output_meta_data.meta_data_length,
+			 output_meta_data.frame_size);
+		pr_debug("timestamp_msw: %d, timestamp_lsw: %d\n",
+			 output_meta_data.timestamp_msw,
+			 output_meta_data.timestamp_lsw);
+
+		param.paddr = (unsigned long)buf[0].phys
+				+ (prtd->out_head * prtd->pcm_count)
+				+ output_meta_data.meta_data_length;
+		param.len = buffer_length;
+		param.msw_ts = output_meta_data.timestamp_msw;
+		param.lsw_ts = output_meta_data.timestamp_lsw;
+		param.flags = time_stamp_flag;
+		param.uid = prtd->session_id;
+		if (q6asm_async_write(prtd->audio_client,
+					&param) < 0)
+			pr_err("%s:q6asm_async_write failed\n",
+				__func__);
+		else
+			prtd->out_head =
+				(prtd->out_head + 1) & (runtime->periods - 1);
+
+		runtime->render_flag &= ~SNDRV_RENDER_STOPPED;
+		return 0;
+	}
+	return 0;
+}
+
+static int msm_compr_volume_ctl_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+			 vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_audio *prtd;
+	int volume = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: volume : %x\n", __func__, volume);
+	if (!substream)
+		return -ENODEV;
+	if (!substream->runtime)
+		return 0;
+	prtd = substream->runtime->private_data;
+	if (prtd)
+		rc = compressed_set_volume(prtd, volume);
+
+	return rc;
+}
+
+static int msm_compr_volume_ctl_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+			 vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s\n", __func__);
+	if (!substream)
+		return -ENODEV;
+	if (!substream->runtime)
+		return 0;
+	prtd = substream->runtime->private_data;
+	if (prtd)
+		ucontrol->value.integer.value[0] = prtd->volume;
+	return 0;
+}
+
+static int msm_compr_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_volume *volume_info;
+	struct snd_kcontrol *kctl;
+
+	dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
+	ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				      NULL, 1, rtd->dai_link->id,
+				      &volume_info);
+	if (ret < 0)
+		return ret;
+	kctl = volume_info->kctl;
+	kctl->put = msm_compr_volume_ctl_put;
+	kctl->get = msm_compr_volume_ctl_get;
+	kctl->tlv.p = compr_rx_vol_gain;
+	return 0;
+}
+
+static const struct snd_pcm_ops msm_compr_ops = {
+	.open	   = msm_compr_open,
+	.hw_params	= msm_compr_hw_params,
+	.close	  = msm_compr_close,
+	.ioctl	  = msm_compr_ioctl,
+	.prepare	= msm_compr_prepare,
+	.trigger	= msm_compr_trigger,
+	.pointer	= msm_compr_pointer,
+	.mmap		= msm_compr_mmap,
+	.restart	= msm_compr_restart,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl   = msm_compr_compat_ioctl,
+#endif
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	ret = msm_compr_add_controls(rtd);
+	if (ret)
+		pr_err("%s, kctl add failed\n", __func__);
+	return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_compr_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+};
+
+static int msm_compr_probe(struct platform_device *pdev)
+{
+
+	dev_info(&pdev->dev, "%s: dev name %s\n",
+			 __func__, dev_name(&pdev->dev));
+
+	return snd_soc_register_platform(&pdev->dev,
+				   &msm_soc_platform);
+}
+
+static int msm_compr_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_compr_dt_match[] = {
+	{.compatible = "qcom,msm-compr-dsp"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_compr_dt_match);
+
+static struct platform_driver msm_compr_driver = {
+	.driver = {
+		.name = "msm-compr-dsp",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_compr_dt_match,
+	},
+	.probe = msm_compr_probe,
+	.remove = msm_compr_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	init_waitqueue_head(&the_locks.enable_wait);
+	init_waitqueue_head(&the_locks.eos_wait);
+	init_waitqueue_head(&the_locks.write_wait);
+	init_waitqueue_head(&the_locks.read_wait);
+	init_waitqueue_head(&the_locks.flush_wait);
+
+	return platform_driver_register(&msm_compr_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_compr_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
new file mode 100644
index 0000000..d6e3ec6
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-compr-q6-v2.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_COMPR_H
+#define _MSM_COMPR_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/compress_params.h>
+#include <sound/compress_offload.h>
+#include <sound/compress_driver.h>
+
+#include "msm-pcm-q6-v2.h"
+
+struct compr_info {
+	struct snd_compr_caps compr_cap;
+	struct snd_compr_codec_caps codec_caps;
+	struct snd_compr_params codec_param;
+};
+
+struct compr_audio {
+	struct msm_audio prtd;
+	struct compr_info info;
+	uint32_t codec;
+};
+
+#endif /*_MSM_COMPR_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
index 1ceb006..48f58f1 100644
--- a/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
+++ b/sound/soc/msm/qdsp6v2/msm-compress-q6-v2.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -55,6 +55,10 @@
 #define DSP_NUM_OUTPUT_FRAME_BUFFERED	2
 #define FLAC_BLK_SIZE_LIMIT		65535
 
+/* Timestamp mode payload offsets */
+#define TS_LSW_OFFSET			6
+#define TS_MSW_OFFSET			7
+
 /* decoder parameter length */
 #define DDP_DEC_MAX_NUM_PARAM		18
 
@@ -68,12 +72,6 @@
 const DECLARE_TLV_DB_LINEAR(msm_compr_vol_gain, 0,
 				COMPRESSED_LR_VOL_MAX_STEPS);
 
-/*
- * LSB 8 bits is used as stream id for some DSP
- * commands for compressed playback.
- */
-#define STREAM_ID_FROM_TOKEN(i) (i & 0xFF)
-
 /* Stream id switches between 1 and 2 */
 #define NEXT_STREAM_ID(stream_id) ((stream_id & 1) + 1)
 
@@ -102,7 +100,7 @@ struct msm_compr_gapless_state {
 
 static unsigned int supported_sample_rates[] = {
 	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, 64000,
-	88200, 96000, 176400, 192000
+	88200, 96000, 176400, 192000, 352800, 384000, 2822400, 5644800
 };
 
 struct msm_compr_pdata {
@@ -110,6 +108,7 @@ struct msm_compr_pdata {
 	uint32_t volume[MSM_FRONTEND_DAI_MAX][2]; /* For both L & R */
 	struct msm_compr_audio_effects *audio_effects[MSM_FRONTEND_DAI_MAX];
 	bool use_dsp_gapless_mode;
+	bool use_legacy_api; /* indicates use older asm apis*/
 	struct msm_compr_dec_params *dec_params[MSM_FRONTEND_DAI_MAX];
 	struct msm_compr_ch_map *ch_map[MSM_FRONTEND_DAI_MAX];
 };
@@ -132,6 +131,13 @@ struct msm_compr_audio {
 	uint64_t bytes_received; /* from userspace */
 	uint64_t bytes_sent; /* to DSP */
 
+	uint64_t received_total; /* bytes received from DSP */
+	uint64_t bytes_copied; /* to userspace */
+	uint64_t bytes_read; /* from DSP */
+	uint32_t bytes_read_offset; /* bytes read offset */
+
+	uint32_t ts_header_offset; /* holds the timestamp header offset */
+
 	int32_t first_buffer;
 	int32_t last_buffer;
 	int32_t partial_drain_delay;
@@ -174,7 +180,9 @@ struct msm_compr_audio {
 	spinlock_t lock;
 };
 
-const u32 compr_codecs[] = {SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3};
+const u32 compr_codecs[] = {
+	SND_AUDIOCODEC_AC3, SND_AUDIOCODEC_EAC3, SND_AUDIOCODEC_DTS,
+	SND_AUDIOCODEC_DSD};
 
 struct query_audio_effect {
 	uint32_t mod_id;
@@ -211,7 +219,7 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream,
 				uint32_t volume_l, uint32_t volume_r)
 {
 	struct msm_compr_audio *prtd;
-	int rc = 0, i;
+	int rc = 0;
 	uint32_t avg_vol, gain_list[VOLUME_CONTROL_MAX_CHANNELS];
 	uint32_t num_channels;
 	struct snd_soc_pcm_runtime *rtd;
@@ -240,8 +248,8 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream,
 		return rc;
 	}
 
-	use_default = !(pdata->ch_map[rtd->dai_link->be_id]->set_ch_map);
-	chmap = pdata->ch_map[rtd->dai_link->be_id]->channel_map;
+	use_default = !(pdata->ch_map[rtd->dai_link->id]->set_ch_map);
+	chmap = pdata->ch_map[rtd->dai_link->id]->channel_map;
 	num_channels = prtd->num_channels;
 
 	if (prtd->num_channels > 2) {
@@ -255,9 +263,7 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream,
 		 *
 		 */
 		avg_vol = (volume_l + volume_r) / 2;
-		for (i = 0; i < prtd->num_channels; i++)
-			gain_list[i] = avg_vol;
-
+		rc = q6asm_set_volume(prtd->audio_client, avg_vol);
 	} else {
 		gain_list[0] = volume_l;
 		gain_list[1] = volume_r;
@@ -267,11 +273,10 @@ static int msm_compr_set_volume(struct snd_compr_stream *cstream,
 			num_channels = 3;
 			use_default = true;
 		}
+		rc = q6asm_set_multich_gain(prtd->audio_client, num_channels,
+					gain_list, chmap, use_default);
 	}
 
-	rc = q6asm_set_multich_gain(prtd->audio_client, num_channels,
-				    gain_list, chmap, use_default);
-
 	if (rc < 0)
 		pr_err("%s: Send vol gain command failed rc=%d\n",
 		       __func__, rc);
@@ -370,6 +375,53 @@ static int msm_compr_send_buffer(struct msm_compr_audio *prtd)
 	return 0;
 }
 
+static int msm_compr_read_buffer(struct msm_compr_audio *prtd)
+{
+	int buffer_length;
+	uint64_t bytes_available;
+	uint64_t buffer_sent;
+	struct audio_aio_read_param param;
+	int ret;
+
+	if (!atomic_read(&prtd->start)) {
+		pr_err("%s: stream is not in started state\n", __func__);
+		return -EINVAL;
+	}
+
+	buffer_length = prtd->codec_param.buffer.fragment_size -
+						 prtd->ts_header_offset;
+	bytes_available = prtd->received_total - prtd->bytes_copied;
+	buffer_sent = prtd->bytes_read - prtd->bytes_copied;
+	if (buffer_sent + buffer_length + prtd->ts_header_offset
+						> prtd->buffer_size) {
+		pr_debug(" %s : Buffer is Full bytes_available: %llu\n",
+				__func__, bytes_available);
+		return 0;
+	}
+
+	memset(&param, 0x0, sizeof(struct audio_aio_read_param));
+	param.paddr = prtd->buffer_paddr + prtd->bytes_read_offset +
+						prtd->ts_header_offset;
+	param.len = buffer_length;
+	param.uid = buffer_length;
+	param.flags = prtd->codec_param.codec.flags;
+
+	pr_debug("%s: reading %d bytes from DSP byte_offset = %llu\n",
+			__func__, buffer_length, prtd->bytes_read);
+	ret = q6asm_async_read(prtd->audio_client, &param);
+	if (ret < 0) {
+		pr_err("%s: q6asm_async_read failed - %d\n",
+			__func__, ret);
+		return ret;
+	}
+	prtd->bytes_read += buffer_length;
+	prtd->bytes_read_offset += buffer_length;
+	if (prtd->bytes_read_offset >= prtd->buffer_size)
+		prtd->bytes_read_offset -= prtd->buffer_size;
+
+	return 0;
+}
+
 static void compr_event_handler(uint32_t opcode,
 		uint32_t token, uint32_t *payload, void *priv)
 {
@@ -382,6 +434,8 @@ static void compr_event_handler(uint32_t opcode,
 	int stream_id;
 	uint32_t stream_index;
 	unsigned long flags;
+	uint64_t read_size;
+	uint32_t *buff_addr;
 
 	if (!prtd) {
 		pr_err("%s: prtd is NULL\n", __func__);
@@ -390,6 +444,12 @@ static void compr_event_handler(uint32_t opcode,
 	cstream = prtd->cstream;
 	ac = prtd->audio_client;
 
+	/*
+	 * Token for rest of the compressed commands use to set
+	 * session id, stream id, dir etc.
+	 */
+	stream_id = q6asm_get_stream_id_from_token(token);
+
 	pr_debug("%s opcode =%08x\n", __func__, opcode);
 	switch (opcode) {
 	case ASM_DATA_EVENT_WRITE_DONE_V2:
@@ -417,6 +477,12 @@ static void compr_event_handler(uint32_t opcode,
 				 prtd->byte_offset, token);
 		}
 
+		/*
+		 * Token for WRITE command represents the amount of data
+		 * written to ADSP in the last write, update offset and
+		 * total copied data accordingly.
+		 */
+
 		prtd->byte_offset += token;
 		prtd->copied_total += token;
 		if (prtd->byte_offset >= prtd->buffer_size)
@@ -455,11 +521,53 @@ static void compr_event_handler(uint32_t opcode,
 
 		spin_unlock_irqrestore(&prtd->lock, flags);
 		break;
+
+	case ASM_DATA_EVENT_READ_DONE_V2:
+		spin_lock_irqsave(&prtd->lock, flags);
+
+		pr_debug("ASM_DATA_EVENT_READ_DONE_V2 offset %d, length %d\n",
+				 prtd->byte_offset, payload[4]);
+
+		if (prtd->ts_header_offset) {
+			/* Update the header for received buffer */
+			buff_addr = prtd->buffer + prtd->byte_offset;
+			/* Write the length of the buffer */
+			*buff_addr = prtd->codec_param.buffer.fragment_size
+						 - prtd->ts_header_offset;
+			buff_addr++;
+			/* Write the offset */
+			*buff_addr = prtd->ts_header_offset;
+			buff_addr++;
+			/* Write the TS LSW */
+			*buff_addr = payload[TS_LSW_OFFSET];
+			buff_addr++;
+			/* Write the TS MSW */
+			*buff_addr = payload[TS_MSW_OFFSET];
+		}
+		/* Always assume read_size is same as fragment_size */
+		read_size = prtd->codec_param.buffer.fragment_size;
+		prtd->byte_offset += read_size;
+		prtd->received_total += read_size;
+		if (prtd->byte_offset >= prtd->buffer_size)
+			prtd->byte_offset -= prtd->buffer_size;
+
+		snd_compr_fragment_elapsed(cstream);
+
+		if (!atomic_read(&prtd->start)) {
+			pr_debug("read_done received while not started, treat as xrun");
+			atomic_set(&prtd->xrun, 1);
+			spin_unlock_irqrestore(&prtd->lock, flags);
+			break;
+		}
+		msm_compr_read_buffer(prtd);
+
+		spin_unlock_irqrestore(&prtd->lock, flags);
+		break;
+
 	case ASM_DATA_EVENT_RENDERED_EOS:
 		spin_lock_irqsave(&prtd->lock, flags);
 		pr_debug("%s: ASM_DATA_CMDRSP_EOS token 0x%x,stream id %d\n",
-			  __func__, token, STREAM_ID_FROM_TOKEN(token));
-		stream_id = STREAM_ID_FROM_TOKEN(token);
+			  __func__, token, stream_id);
 		if (atomic_read(&prtd->eos) &&
 		    !prtd->gapless_state.set_next_stream_id) {
 			pr_debug("ASM_DATA_CMDRSP_EOS wake up\n");
@@ -509,6 +617,14 @@ static void compr_event_handler(uint32_t opcode,
 
 			/* FIXME: A state is a better way, dealing with this */
 			spin_lock_irqsave(&prtd->lock, flags);
+
+			if (cstream->direction == SND_COMPRESS_CAPTURE) {
+				atomic_set(&prtd->start, 1);
+				msm_compr_read_buffer(prtd);
+				spin_unlock_irqrestore(&prtd->lock, flags);
+				break;
+			}
+
 			if (!prtd->bytes_sent) {
 				bytes_available = prtd->bytes_received -
 						  prtd->copied_total;
@@ -547,25 +663,25 @@ static void compr_event_handler(uint32_t opcode,
 		case ASM_STREAM_CMD_FLUSH:
 			pr_debug("%s: ASM_STREAM_CMD_FLUSH:", __func__);
 			pr_debug("token 0x%x, stream id %d\n", token,
-				  STREAM_ID_FROM_TOKEN(token));
+				  stream_id);
 			prtd->cmd_ack = 1;
 			break;
 		case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
 			pr_debug("%s: ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:",
 				   __func__);
 			pr_debug("token 0x%x, stream id = %d\n", token,
-				  STREAM_ID_FROM_TOKEN(token));
+				  stream_id);
 			break;
 		case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
 			pr_debug("%s: ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:",
 				  __func__);
 			pr_debug("token = 0x%x,	stream id = %d\n", token,
-				  STREAM_ID_FROM_TOKEN(token));
+				  stream_id);
 			break;
 		case ASM_STREAM_CMD_CLOSE:
 			pr_debug("%s: ASM_DATA_CMD_CLOSE:", __func__);
 			pr_debug("token 0x%x, stream id %d\n", token,
-				  STREAM_ID_FROM_TOKEN(token));
+				  stream_id);
 			/*
 			 * wakeup wait for stream avail on stream 3
 			 * after stream 1 ends.
@@ -597,8 +713,12 @@ static void compr_event_handler(uint32_t opcode,
 		pr_err("%s: Received reset events CB, move to error state",
 			__func__);
 		spin_lock_irqsave(&prtd->lock, flags);
-		snd_compr_fragment_elapsed(cstream);
+		/*
+		 * Since ADSP is down, let this driver pretend that it copied
+		 * all the bytes received, so that next write will be triggered
+		 */
 		prtd->copied_total = prtd->bytes_received;
+		snd_compr_fragment_elapsed(cstream);
 		atomic_set(&prtd->error, 1);
 		wake_up(&prtd->drain_wait);
 		if (atomic_cmpxchg(&prtd->eos, 1, 0)) {
@@ -623,7 +743,8 @@ static int msm_compr_get_partial_drain_delay(int frame_sz, int sample_rate)
 	delay_time_ms = delay_time_ms > PARTIAL_DRAIN_ACK_EARLY_BY_MSEC ?
 			delay_time_ms - PARTIAL_DRAIN_ACK_EARLY_BY_MSEC : 0;
 
-	pr_debug("%s: partial drain delay %d\n", __func__, delay_time_ms);
+	pr_debug("%s: frame_sz %d, sample_rate %d, partial drain delay %d\n",
+		__func__, frame_sz, sample_rate, delay_time_ms);
 	return delay_time_ms;
 }
 
@@ -639,7 +760,7 @@ static void populate_codec_list(struct msm_compr_audio *prtd)
 			COMPR_PLAYBACK_MIN_NUM_FRAGMENTS;
 	prtd->compr_cap.max_fragments =
 			COMPR_PLAYBACK_MAX_NUM_FRAGMENTS;
-	prtd->compr_cap.num_codecs = 12;
+	prtd->compr_cap.num_codecs = 14;
 	prtd->compr_cap.codecs[0] = SND_AUDIOCODEC_MP3;
 	prtd->compr_cap.codecs[1] = SND_AUDIOCODEC_AAC;
 	prtd->compr_cap.codecs[2] = SND_AUDIOCODEC_AC3;
@@ -652,6 +773,8 @@ static void populate_codec_list(struct msm_compr_audio *prtd)
 	prtd->compr_cap.codecs[9] = SND_AUDIOCODEC_VORBIS;
 	prtd->compr_cap.codecs[10] = SND_AUDIOCODEC_ALAC;
 	prtd->compr_cap.codecs[11] = SND_AUDIOCODEC_APE;
+	prtd->compr_cap.codecs[12] = SND_AUDIOCODEC_DTS;
+	prtd->compr_cap.codecs[13] = SND_AUDIOCODEC_DSD;
 }
 
 static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
@@ -670,12 +793,14 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
 	struct asm_vorbis_cfg vorbis_cfg;
 	struct asm_alac_cfg alac_cfg;
 	struct asm_ape_cfg ape_cfg;
+	struct asm_dsd_cfg dsd_cfg;
 	union snd_codec_options *codec_options;
 
 	int ret = 0;
-	uint16_t bit_width = 16;
+	uint16_t bit_width;
 	bool use_default_chmap = true;
 	char *chmap = NULL;
+	uint16_t sample_word_size;
 
 	pr_debug("%s: use_gapless_codec_options %d\n",
 			__func__, use_gapless_codec_options);
@@ -693,21 +818,42 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
 	switch (prtd->codec) {
 	case FORMAT_LINEAR_PCM:
 		pr_debug("SND_AUDIOCODEC_PCM\n");
-		if (pdata->ch_map[rtd->dai_link->be_id]) {
+		if (pdata->ch_map[rtd->dai_link->id]) {
 			use_default_chmap =
-			    !(pdata->ch_map[rtd->dai_link->be_id]->set_ch_map);
+			    !(pdata->ch_map[rtd->dai_link->id]->set_ch_map);
 			chmap =
-			    pdata->ch_map[rtd->dai_link->be_id]->channel_map;
+			    pdata->ch_map[rtd->dai_link->id]->channel_map;
 		}
-		if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE)
+
+		switch (prtd->codec_param.codec.format) {
+		case SNDRV_PCM_FORMAT_S32_LE:
+			bit_width = 32;
+			sample_word_size = 32;
+			break;
+		case SNDRV_PCM_FORMAT_S24_LE:
 			bit_width = 24;
-		ret = q6asm_media_format_block_pcm_format_support_v2(
+			sample_word_size = 32;
+			break;
+		case SNDRV_PCM_FORMAT_S24_3LE:
+			bit_width = 24;
+			sample_word_size = 24;
+			break;
+		case SNDRV_PCM_FORMAT_S16_LE:
+		default:
+			bit_width = 16;
+			sample_word_size = 16;
+			break;
+		}
+		ret = q6asm_media_format_block_pcm_format_support_v4(
 							prtd->audio_client,
 							prtd->sample_rate,
 							prtd->num_channels,
 							bit_width, stream_id,
 							use_default_chmap,
-							chmap);
+							chmap,
+							sample_word_size,
+							ASM_LITTLE_ENDIAN,
+							DEFAULT_QF);
 		if (ret < 0)
 			pr_err("%s: CMD Format block failed\n", __func__);
 
@@ -861,7 +1007,24 @@ static int msm_compr_send_media_format_block(struct snd_compr_stream *cstream,
 			pr_err("%s: CMD Format block failed ret %d\n",
 					__func__, ret);
 		break;
-
+	case FORMAT_DTS:
+		pr_debug("SND_AUDIOCODEC_DTS\n");
+		/* no media format block needed */
+		break;
+	case FORMAT_DSD:
+		pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__);
+		memset(&dsd_cfg, 0x0, sizeof(struct asm_dsd_cfg));
+		dsd_cfg.num_channels = prtd->num_channels;
+		dsd_cfg.dsd_data_rate = prtd->sample_rate;
+		dsd_cfg.num_version = 0;
+		dsd_cfg.is_bitwise_big_endian = 1;
+		dsd_cfg.dsd_channel_block_size = 1;
+		ret = q6asm_media_format_block_dsd(prtd->audio_client,
+						   &dsd_cfg, stream_id);
+		if (ret < 0)
+			pr_err("%s: CMD DSD Format block failed ret %d\n",
+				__func__, ret);
+		break;
 	default:
 		pr_debug("%s, unsupported format, skip", __func__);
 		break;
@@ -912,7 +1075,8 @@ static int msm_compr_init_pp_params(struct snd_compr_stream *cstream,
 	return ret;
 }
 
-static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
+static int msm_compr_configure_dsp_for_playback
+			(struct snd_compr_stream *cstream)
 {
 	struct snd_compr_runtime *runtime = cstream->runtime;
 	struct msm_compr_audio *prtd = runtime->private_data;
@@ -934,7 +1098,14 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
 	};
 
 	pr_debug("%s: stream_id %d\n", __func__, ac->stream_id);
-	if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE)
+	stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
+	if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
+		pr_err("%s: Invalid stream index:%d", __func__, stream_index);
+		return -EINVAL;
+	}
+
+	if ((prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_LE) ||
+		(prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S24_3LE))
 		bits_per_sample = 24;
 	else if (prtd->codec_param.codec.format == SNDRV_PCM_FORMAT_S32_LE)
 		bits_per_sample = 32;
@@ -947,8 +1118,10 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
 				__func__, ret, prtd->compr_passthr);
 			return ret;
 		}
+		prtd->gapless_state.stream_opened[stream_index] = 1;
+
 		ret = msm_pcm_routing_reg_phy_compr_stream(
-				soc_prtd->dai_link->be_id,
+				soc_prtd->dai_link->id,
 				ac->perf_mode,
 				prtd->session_id,
 				SNDRV_PCM_STREAM_PLAYBACK,
@@ -961,7 +1134,7 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
 	} else {
 		pr_debug("%s: stream_id %d bits_per_sample %d\n",
 				__func__, ac->stream_id, bits_per_sample);
-		ret = q6asm_stream_open_write_v2(ac,
+		ret = q6asm_stream_open_write_v4(ac,
 				prtd->codec, bits_per_sample,
 				ac->stream_id,
 				prtd->gapless_state.use_dsp_gapless_mode);
@@ -970,9 +1143,10 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
 				__func__, ret, prtd->compr_passthr);
 			return -ENOMEM;
 		}
+		prtd->gapless_state.stream_opened[stream_index] = 1;
 
-		pr_debug("%s: be_id %d\n", __func__, soc_prtd->dai_link->be_id);
-		ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->be_id,
+		pr_debug("%s: BE id %d\n", __func__, soc_prtd->dai_link->id);
+		ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id,
 				ac->perf_mode,
 				prtd->session_id,
 				SNDRV_PCM_STREAM_PLAYBACK);
@@ -986,31 +1160,30 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
 	if (ret < 0)
 		pr_err("%s : Set Volume failed : %d", __func__, ret);
 
-	ret = q6asm_send_cal(ac);
-	if (ret < 0)
-		pr_debug("%s : Send cal failed : %d", __func__, ret);
+	if (prtd->compr_passthr != LEGACY_PCM) {
+		pr_debug("%s : Don't send cal and PP params for compress path",
+				__func__);
+	} else {
+		ret = q6asm_send_cal(ac);
+		if (ret < 0)
+			pr_debug("%s : Send cal failed : %d", __func__, ret);
 
-	ret = q6asm_set_softpause(ac, &softpause);
-	if (ret < 0)
-		pr_err("%s: Send SoftPause Param failed ret=%d\n",
-				__func__, ret);
-	ret = q6asm_set_softvolume(ac, &softvol);
-	if (ret < 0)
-		pr_err("%s: Send SoftVolume Param failed ret=%d\n",
-				__func__, ret);
+		ret = q6asm_set_softpause(ac, &softpause);
+		if (ret < 0)
+			pr_err("%s: Send SoftPause Param failed ret=%d\n",
+					__func__, ret);
 
+		ret = q6asm_set_softvolume(ac, &softvol);
+		if (ret < 0)
+			pr_err("%s: Send SoftVolume Param failed ret=%d\n",
+					__func__, ret);
+	}
 	ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE));
 	if (ret < 0) {
 		pr_err("%s: Set IO mode failed\n", __func__);
 		return -EINVAL;
 	}
-	stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
-	if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
-		pr_err("%s: Invalid stream index:%d", __func__, stream_index);
-		return -EINVAL;
-	}
 
-	prtd->gapless_state.stream_opened[stream_index] = 1;
 	runtime->fragments = prtd->codec_param.buffer.fragments;
 	runtime->fragment_size = prtd->codec_param.buffer.fragment_size;
 	pr_debug("allocate %d buffers each of size %d\n",
@@ -1040,7 +1213,108 @@ static int msm_compr_configure_dsp(struct snd_compr_stream *cstream)
 	return ret;
 }
 
-static int msm_compr_open(struct snd_compr_stream *cstream)
+static int msm_compr_configure_dsp_for_capture(struct snd_compr_stream *cstream)
+{
+	struct snd_compr_runtime *runtime = cstream->runtime;
+	struct msm_compr_audio *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *soc_prtd = cstream->private_data;
+	uint16_t bits_per_sample;
+	uint16_t sample_word_size;
+	int dir = OUT, ret = 0;
+	struct audio_client *ac = prtd->audio_client;
+	uint32_t stream_index;
+
+	switch (prtd->codec_param.codec.format) {
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits_per_sample = 24;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		bits_per_sample = 24;
+		sample_word_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bits_per_sample = 32;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bits_per_sample = 16;
+		sample_word_size = 16;
+		break;
+	}
+
+	pr_debug("%s: stream_id %d bits_per_sample %d\n",
+			__func__, ac->stream_id, bits_per_sample);
+
+	ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM,
+		bits_per_sample);
+	if (ret < 0) {
+		pr_err("%s: q6asm_open_read failed:%d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id,
+			ac->perf_mode,
+			prtd->session_id,
+			SNDRV_PCM_STREAM_CAPTURE);
+	if (ret) {
+		pr_err("%s: stream reg failed:%d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = q6asm_set_io_mode(ac, (COMPRESSED_STREAM_IO | ASYNC_IO_MODE));
+	if (ret < 0) {
+		pr_err("%s: Set IO mode failed\n", __func__);
+		return -EINVAL;
+	}
+
+	stream_index = STREAM_ARRAY_INDEX(ac->stream_id);
+	if (stream_index >= MAX_NUMBER_OF_STREAMS || stream_index < 0) {
+		pr_err("%s: Invalid stream index:%d", __func__, stream_index);
+		return -EINVAL;
+	}
+
+	runtime->fragments = prtd->codec_param.buffer.fragments;
+	runtime->fragment_size = prtd->codec_param.buffer.fragment_size;
+	pr_debug("%s: allocate %d buffers each of size %d\n",
+			__func__, runtime->fragments,
+			runtime->fragment_size);
+	ret = q6asm_audio_client_buf_alloc_contiguous(dir, ac,
+					runtime->fragment_size,
+					runtime->fragments);
+	if (ret < 0) {
+		pr_err("Audio Start: Buffer Allocation failed rc = %d\n", ret);
+		return -ENOMEM;
+	}
+
+	prtd->byte_offset    = 0;
+	prtd->received_total = 0;
+	prtd->app_pointer    = 0;
+	prtd->bytes_copied   = 0;
+	prtd->bytes_read     = 0;
+	prtd->bytes_read_offset = 0;
+	prtd->buffer         = ac->port[dir].buf[0].data;
+	prtd->buffer_paddr   = ac->port[dir].buf[0].phys;
+	prtd->buffer_size    = runtime->fragments * runtime->fragment_size;
+
+	/* Bit-0 of flags represent timestamp mode */
+	if (prtd->codec_param.codec.flags & COMPRESSED_TIMESTAMP_FLAG)
+		prtd->ts_header_offset = sizeof(struct snd_codec_metadata);
+	else
+		prtd->ts_header_offset = 0;
+
+	pr_debug("%s: sample_rate = %d channels = %d bps = %d sample_word_size = %d\n",
+			__func__, prtd->sample_rate, prtd->num_channels,
+					 bits_per_sample, sample_word_size);
+	ret = q6asm_enc_cfg_blk_pcm_format_support_v3(prtd->audio_client,
+					prtd->sample_rate, prtd->num_channels,
+					bits_per_sample, sample_word_size);
+
+	return ret;
+}
+
+static int msm_compr_playback_open(struct snd_compr_stream *cstream)
 {
 	struct snd_compr_runtime *runtime = cstream->runtime;
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -1057,39 +1331,25 @@ static int msm_compr_open(struct snd_compr_stream *cstream)
 
 	runtime->private_data = NULL;
 	prtd->cstream = cstream;
-	pdata->cstream[rtd->dai_link->be_id] = cstream;
-	pdata->audio_effects[rtd->dai_link->be_id] =
+	pdata->cstream[rtd->dai_link->id] = cstream;
+	pdata->audio_effects[rtd->dai_link->id] =
 		 kzalloc(sizeof(struct msm_compr_audio_effects), GFP_KERNEL);
-	if (!pdata->audio_effects[rtd->dai_link->be_id]) {
+	if (!pdata->audio_effects[rtd->dai_link->id]) {
 		pr_err("%s: Could not allocate memory for effects\n", __func__);
-		pdata->cstream[rtd->dai_link->be_id] = NULL;
+		pdata->cstream[rtd->dai_link->id] = NULL;
 		kfree(prtd);
 		return -ENOMEM;
 	}
-	pdata->dec_params[rtd->dai_link->be_id] =
+	pdata->dec_params[rtd->dai_link->id] =
 		 kzalloc(sizeof(struct msm_compr_dec_params), GFP_KERNEL);
-	if (!pdata->dec_params[rtd->dai_link->be_id]) {
+	if (!pdata->dec_params[rtd->dai_link->id]) {
 		pr_err("%s: Could not allocate memory for dec params\n",
 			__func__);
-		kfree(pdata->audio_effects[rtd->dai_link->be_id]);
-		pdata->cstream[rtd->dai_link->be_id] = NULL;
+		kfree(pdata->audio_effects[rtd->dai_link->id]);
+		pdata->cstream[rtd->dai_link->id] = NULL;
 		kfree(prtd);
 		return -ENOMEM;
 	}
-	prtd->audio_client = q6asm_audio_client_alloc(
-				(app_cb)compr_event_handler, prtd);
-	if (!prtd->audio_client) {
-		pr_err("%s: Could not allocate memory for client\n", __func__);
-		kfree(pdata->audio_effects[rtd->dai_link->be_id]);
-		kfree(pdata->dec_params[rtd->dai_link->be_id]);
-		pdata->cstream[rtd->dai_link->be_id] = NULL;
-		kfree(prtd);
-		return -ENOMEM;
-	}
-
-	pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
-	prtd->audio_client->perf_mode = false;
-	prtd->session_id = prtd->audio_client->session;
 	prtd->codec = FORMAT_MP3;
 	prtd->bytes_received = 0;
 	prtd->bytes_sent = 0;
@@ -1128,11 +1388,91 @@ static int msm_compr_open(struct snd_compr_stream *cstream)
 
 	runtime->private_data = prtd;
 	populate_codec_list(prtd);
+	prtd->audio_client = q6asm_audio_client_alloc(
+				(app_cb)compr_event_handler, prtd);
+	if (!prtd->audio_client) {
+		pr_err("%s: Could not allocate memory for client\n", __func__);
+		kfree(pdata->audio_effects[rtd->dai_link->id]);
+		kfree(pdata->dec_params[rtd->dai_link->id]);
+		pdata->cstream[rtd->dai_link->id] = NULL;
+		runtime->private_data = NULL;
+		kfree(prtd);
+		return -ENOMEM;
+	}
+	pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+	prtd->audio_client->perf_mode = false;
+	prtd->session_id = prtd->audio_client->session;
 
 	return 0;
 }
 
-static int msm_compr_free(struct snd_compr_stream *cstream)
+static int msm_compr_capture_open(struct snd_compr_stream *cstream)
+{
+	struct snd_compr_runtime *runtime = cstream->runtime;
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+	struct msm_compr_audio *prtd;
+	struct msm_compr_pdata *pdata =
+			snd_soc_platform_get_drvdata(rtd->platform);
+
+	pr_debug("%s\n", __func__);
+	prtd = kzalloc(sizeof(struct msm_compr_audio), GFP_KERNEL);
+	if (prtd == NULL) {
+		pr_err("Failed to allocate memory for msm_compr_audio\n");
+		return -ENOMEM;
+	}
+
+	runtime->private_data = NULL;
+	prtd->cstream = cstream;
+	pdata->cstream[rtd->dai_link->id] = cstream;
+
+	prtd->audio_client = q6asm_audio_client_alloc(
+				(app_cb)compr_event_handler, prtd);
+	if (!prtd->audio_client) {
+		pr_err("%s: Could not allocate memory for client\n", __func__);
+		pdata->cstream[rtd->dai_link->id] = NULL;
+		kfree(prtd);
+		return -ENOMEM;
+	}
+	pr_debug("%s: session ID %d\n", __func__, prtd->audio_client->session);
+	prtd->audio_client->perf_mode = false;
+	prtd->session_id = prtd->audio_client->session;
+	prtd->codec = FORMAT_LINEAR_PCM;
+	prtd->bytes_copied = 0;
+	prtd->bytes_read = 0;
+	prtd->bytes_read_offset = 0;
+	prtd->received_total = 0;
+	prtd->byte_offset = 0;
+	prtd->sample_rate = 48000;
+	prtd->num_channels = 2;
+	prtd->first_buffer = 0;
+
+	spin_lock_init(&prtd->lock);
+
+	atomic_set(&prtd->eos, 0);
+	atomic_set(&prtd->start, 0);
+	atomic_set(&prtd->drain, 0);
+	atomic_set(&prtd->xrun, 0);
+	atomic_set(&prtd->close, 0);
+	atomic_set(&prtd->wait_on_close, 0);
+	atomic_set(&prtd->error, 0);
+
+	runtime->private_data = prtd;
+
+	return 0;
+}
+
+static int msm_compr_open(struct snd_compr_stream *cstream)
+{
+	int ret = 0;
+
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		ret = msm_compr_playback_open(cstream);
+	else if (cstream->direction == SND_COMPRESS_CAPTURE)
+		ret = msm_compr_capture_open(cstream);
+	return ret;
+}
+
+static int msm_compr_playback_free(struct snd_compr_stream *cstream)
 {
 	struct snd_compr_runtime *runtime;
 	struct msm_compr_audio *prtd;
@@ -1208,9 +1548,9 @@ static int msm_compr_free(struct snd_compr_stream *cstream)
 	}
 	spin_unlock_irqrestore(&prtd->lock, flags);
 
-	pdata->cstream[soc_prtd->dai_link->be_id] = NULL;
+	pdata->cstream[soc_prtd->dai_link->id] = NULL;
 	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
-		msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->be_id,
+		msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
 						SNDRV_PCM_STREAM_PLAYBACK);
 	}
 
@@ -1218,15 +1558,85 @@ static int msm_compr_free(struct snd_compr_stream *cstream)
 
 	q6asm_audio_client_free(ac);
 
-	kfree(pdata->audio_effects[soc_prtd->dai_link->be_id]);
-	pdata->audio_effects[soc_prtd->dai_link->be_id] = NULL;
-	kfree(pdata->dec_params[soc_prtd->dai_link->be_id]);
-	pdata->dec_params[soc_prtd->dai_link->be_id] = NULL;
+	kfree(pdata->audio_effects[soc_prtd->dai_link->id]);
+	pdata->audio_effects[soc_prtd->dai_link->id] = NULL;
+	kfree(pdata->dec_params[soc_prtd->dai_link->id]);
+	pdata->dec_params[soc_prtd->dai_link->id] = NULL;
 	kfree(prtd);
 
 	return 0;
 }
 
+static int msm_compr_capture_free(struct snd_compr_stream *cstream)
+{
+	struct snd_compr_runtime *runtime;
+	struct msm_compr_audio *prtd;
+	struct snd_soc_pcm_runtime *soc_prtd;
+	struct msm_compr_pdata *pdata;
+	struct audio_client *ac;
+	int dir = OUT, stream_id;
+	unsigned long flags;
+	uint32_t stream_index;
+
+	if (!cstream) {
+		pr_err("%s cstream is null\n", __func__);
+		return 0;
+	}
+	runtime = cstream->runtime;
+	soc_prtd = cstream->private_data;
+	if (!runtime || !soc_prtd || !(soc_prtd->platform)) {
+		pr_err("%s runtime or soc_prtd or platform is null\n",
+			__func__);
+		return 0;
+	}
+	prtd = runtime->private_data;
+	if (!prtd) {
+		pr_err("%s prtd is null\n", __func__);
+		return 0;
+	}
+	pdata = snd_soc_platform_get_drvdata(soc_prtd->platform);
+	ac = prtd->audio_client;
+	if (!pdata || !ac) {
+		pr_err("%s pdata or ac is null\n", __func__);
+		return 0;
+	}
+
+	spin_lock_irqsave(&prtd->lock, flags);
+	stream_id = ac->stream_id;
+
+	stream_index = STREAM_ARRAY_INDEX(stream_id);
+	if ((stream_index < MAX_NUMBER_OF_STREAMS && stream_index >= 0)) {
+		spin_unlock_irqrestore(&prtd->lock, flags);
+		pr_debug("close stream %d", stream_id);
+		q6asm_stream_cmd(ac, CMD_CLOSE, stream_id);
+		spin_lock_irqsave(&prtd->lock, flags);
+	}
+	spin_unlock_irqrestore(&prtd->lock, flags);
+
+	pdata->cstream[soc_prtd->dai_link->id] = NULL;
+	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
+					SNDRV_PCM_STREAM_CAPTURE);
+
+	q6asm_audio_client_buf_free_contiguous(dir, ac);
+
+	q6asm_audio_client_free(ac);
+
+	kfree(prtd);
+
+	return 0;
+}
+
+static int msm_compr_free(struct snd_compr_stream *cstream)
+{
+	int ret = 0;
+
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		ret = msm_compr_playback_free(cstream);
+	else if (cstream->direction == SND_COMPRESS_CAPTURE)
+		ret = msm_compr_capture_free(cstream);
+	return ret;
+}
+
 static bool msm_compr_validate_codec_compr(__u32 codec_id)
 {
 	int32_t i;
@@ -1257,8 +1667,14 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream,
 	if (i == num_rates)
 		return -EINVAL;
 
-	if (prtd->codec_param.codec.compr_passthr >= 0 &&
-		prtd->codec_param.codec.compr_passthr <= 2)
+	memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params));
+	/* ToDo: remove duplicates */
+	prtd->num_channels = prtd->codec_param.codec.ch_in;
+	prtd->sample_rate = prtd->codec_param.codec.sample_rate;
+	pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate);
+
+	if (prtd->codec_param.codec.compr_passthr >= LEGACY_PCM &&
+	    prtd->codec_param.codec.compr_passthr <= COMPRESSED_PASSTHROUGH_DSD)
 		prtd->compr_passthr = prtd->codec_param.codec.compr_passthr;
 	else
 		prtd->compr_passthr = LEGACY_PCM;
@@ -1363,6 +1779,18 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream,
 		break;
 	}
 
+	case SND_AUDIOCODEC_DTS: {
+		pr_debug("%s: SND_AUDIOCODEC_DTS\n", __func__);
+		prtd->codec = FORMAT_DTS;
+		break;
+	}
+
+	case SND_AUDIOCODEC_DSD: {
+		pr_debug("%s: SND_AUDIOCODEC_DSD\n", __func__);
+		prtd->codec = FORMAT_DSD;
+		break;
+	}
+
 	default:
 		pr_err("codec not supported, id =%d\n", params->codec.id);
 		return -EINVAL;
@@ -1374,13 +1802,10 @@ static int msm_compr_set_params(struct snd_compr_stream *cstream,
 	prtd->partial_drain_delay =
 		msm_compr_get_partial_drain_delay(frame_sz, prtd->sample_rate);
 
-	memcpy(&prtd->codec_param, params, sizeof(struct snd_compr_params));
-
-	/* ToDo: remove duplicates */
-	prtd->num_channels = prtd->codec_param.codec.ch_in;
-	prtd->sample_rate = prtd->codec_param.codec.sample_rate;
-	pr_debug("%s: sample_rate %d\n", __func__, prtd->sample_rate);
-	ret = msm_compr_configure_dsp(cstream);
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		ret = msm_compr_configure_dsp_for_playback(cstream);
+	else if (cstream->direction == SND_COMPRESS_CAPTURE)
+		ret = msm_compr_configure_dsp_for_capture(cstream);
 
 	return ret;
 }
@@ -1461,9 +1886,9 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 	struct msm_compr_pdata *pdata =
 			snd_soc_platform_get_drvdata(rtd->platform);
-	uint32_t *volume = pdata->volume[rtd->dai_link->be_id];
+	uint32_t *volume = pdata->volume[rtd->dai_link->id];
 	struct audio_client *ac = prtd->audio_client;
-	unsigned long fe_id = rtd->dai_link->be_id;
+	unsigned long fe_id = rtd->dai_link->id;
 	int rc = 0;
 	int bytes_to_write;
 	unsigned long flags;
@@ -1471,11 +1896,6 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 	uint32_t stream_index;
 	uint16_t bits_per_sample = 16;
 
-	if (cstream->direction != SND_COMPRESS_PLAYBACK) {
-		pr_err("%s: Unsupported stream type\n", __func__);
-		return -EINVAL;
-	}
-
 	spin_lock_irqsave(&prtd->lock, flags);
 	if (atomic_read(&prtd->error)) {
 		pr_err("%s Got RESET EVENTS notification, return immediately",
@@ -1490,17 +1910,30 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 		pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
 		atomic_set(&prtd->start, 1);
 
-		/* set volume for the stream before RUN */
-		rc = msm_compr_set_volume(cstream, volume[0], volume[1]);
-		if (rc)
-			pr_err("%s : Set Volume failed : %d\n",
-				__func__, rc);
+		/*
+		 * compr_set_volume and compr_init_pp_params
+		 * are used to configure ASM volume hence not
+		 * needed for compress passthrough playback.
+		 *
+		 * compress passthrough volume is controlled in
+		 * ADM by adm_send_compressed_device_mute()
+		 */
+		if (prtd->compr_passthr == LEGACY_PCM &&
+			cstream->direction == SND_COMPRESS_PLAYBACK) {
+			/* set volume for the stream before RUN */
+			rc = msm_compr_set_volume(cstream,
+				volume[0], volume[1]);
+			if (rc)
+				pr_err("%s : Set Volume failed : %d\n",
+					__func__, rc);
 
-		rc = msm_compr_init_pp_params(cstream, ac);
-		if (rc)
-			pr_err("%s : init PP params failed : %d\n",
-				__func__, rc);
-
+			rc = msm_compr_init_pp_params(cstream, ac);
+			if (rc)
+				pr_err("%s : init PP params failed : %d\n",
+					__func__, rc);
+		} else {
+			msm_compr_read_buffer(prtd);
+		}
 		/* issue RUN command for the stream */
 		q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
 		break;
@@ -1510,6 +1943,18 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 					prtd->gapless_state.gapless_transition);
 		stream_id = ac->stream_id;
 		atomic_set(&prtd->start, 0);
+		if (cstream->direction == SND_COMPRESS_CAPTURE) {
+			q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+			atomic_set(&prtd->xrun, 0);
+			prtd->received_total = 0;
+			prtd->bytes_copied = 0;
+			prtd->bytes_read = 0;
+			prtd->bytes_read_offset = 0;
+			prtd->byte_offset  = 0;
+			prtd->app_pointer  = 0;
+			spin_unlock_irqrestore(&prtd->lock, flags);
+			break;
+		}
 		if (prtd->next_stream) {
 			pr_debug("%s: interrupt next track wait queues\n",
 								__func__);
@@ -1519,8 +1964,15 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 		}
 		if (atomic_read(&prtd->eos)) {
 			pr_debug("%s: interrupt eos wait queues", __func__);
-			prtd->cmd_interrupt = 1;
-			wake_up(&prtd->eos_wait);
+			/*
+			 * Gapless playback does not wait for eos, do not set
+			 * cmd_int and do not wake up eos_wait during gapless
+			 * transition
+			 */
+			if (!prtd->gapless_state.gapless_transition) {
+				prtd->cmd_interrupt = 1;
+				wake_up(&prtd->eos_wait);
+			}
 			atomic_set(&prtd->eos, 0);
 		}
 		if (atomic_read(&prtd->drain)) {
@@ -1629,7 +2081,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 			if (prtd->last_buffer) {
 				pr_debug("%s: last buffer drain\n", __func__);
 				rc = msm_compr_drain_buffer(prtd, &flags);
-				if (rc) {
+				if (rc || !atomic_read(&prtd->start)) {
 					spin_unlock_irqrestore(&prtd->lock,
 									flags);
 					break;
@@ -1649,7 +2101,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 			/* wait for the zero length buffer to be returned */
 			pr_debug("%s: zero length buffer drain\n", __func__);
 			rc = msm_compr_drain_buffer(prtd, &flags);
-			if (rc) {
+			if (rc || !atomic_read(&prtd->start)) {
 				spin_unlock_irqrestore(&prtd->lock, flags);
 				break;
 			}
@@ -1683,7 +2135,12 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 			prtd->app_pointer  = 0;
 			prtd->first_buffer = 1;
 			prtd->last_buffer = 0;
-			prtd->gapless_state.gapless_transition = 1;
+			/*
+			 * Set gapless transition flag only if EOS hasn't been
+			 * acknowledged already.
+			 */
+			if (atomic_read(&prtd->eos))
+				prtd->gapless_state.gapless_transition = 1;
 			prtd->marker_timestamp = 0;
 
 			/*
@@ -1707,6 +2164,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 		 * stream can be used for gapless playback
 		 */
 		prtd->gapless_state.set_next_stream_id = false;
+		prtd->gapless_state.gapless_transition = 0;
 		pr_debug("%s:CMD_EOS stream_id %d\n", __func__, ac->stream_id);
 
 		prtd->eos_ack = 0;
@@ -1754,8 +2212,14 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 			/*
 			 * Cache this time as last known time
 			 */
-			q6asm_get_session_time(prtd->audio_client,
-					       &prtd->marker_timestamp);
+			if (pdata->use_legacy_api)
+				q6asm_get_session_time_legacy(
+							prtd->audio_client,
+						       &prtd->marker_timestamp);
+			else
+				q6asm_get_session_time(prtd->audio_client,
+						       &prtd->marker_timestamp);
+
 			spin_lock_irqsave(&prtd->lock, flags);
 			/*
 			 * Don't reset these as these vars map to
@@ -1851,7 +2315,7 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 
 		pr_debug("%s: open_write stream_id %d bits_per_sample %d",
 				__func__, stream_id, bits_per_sample);
-		rc = q6asm_stream_open_write_v2(prtd->audio_client,
+		rc = q6asm_stream_open_write_v4(prtd->audio_client,
 				prtd->codec, bits_per_sample,
 				stream_id,
 				prtd->gapless_state.use_dsp_gapless_mode);
@@ -1882,57 +2346,77 @@ static int msm_compr_trigger(struct snd_compr_stream *cstream, int cmd)
 }
 
 static int msm_compr_pointer(struct snd_compr_stream *cstream,
-				struct snd_compr_tstamp *arg)
+					struct snd_compr_tstamp *arg)
 {
 	struct snd_compr_runtime *runtime = cstream->runtime;
+	struct snd_soc_pcm_runtime *rtd = cstream->private_data;
 	struct msm_compr_audio *prtd = runtime->private_data;
+	struct msm_compr_pdata *pdata = NULL;
 	struct snd_compr_tstamp tstamp;
 	uint64_t timestamp = 0;
 	int rc = 0, first_buffer;
 	unsigned long flags;
 	uint32_t gapless_transition;
 
+	pdata = snd_soc_platform_get_drvdata(rtd->platform);
 	pr_debug("%s\n", __func__);
 	memset(&tstamp, 0x0, sizeof(struct snd_compr_tstamp));
 
 	spin_lock_irqsave(&prtd->lock, flags);
 	tstamp.sampling_rate = prtd->sample_rate;
 	tstamp.byte_offset = prtd->byte_offset;
-	tstamp.copied_total = prtd->copied_total;
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		tstamp.copied_total = prtd->copied_total;
+	else if (cstream->direction == SND_COMPRESS_CAPTURE)
+		tstamp.copied_total = prtd->received_total;
 	first_buffer = prtd->first_buffer;
 	if (atomic_read(&prtd->error)) {
-		pr_err("%s Got RESET EVENTS notification, return error",
+		pr_err("%s Got RESET EVENTS notification, return error\n",
 			__func__);
+		if (cstream->direction == SND_COMPRESS_PLAYBACK)
+			runtime->total_bytes_transferred = tstamp.copied_total;
+		else
+			runtime->total_bytes_available = tstamp.copied_total;
 		tstamp.pcm_io_frames = 0;
 		memcpy(arg, &tstamp, sizeof(struct snd_compr_tstamp));
 		spin_unlock_irqrestore(&prtd->lock, flags);
 		return -ENETRESET;
 	}
+	if (cstream->direction == SND_COMPRESS_PLAYBACK) {
 
-	gapless_transition = prtd->gapless_state.gapless_transition;
-	spin_unlock_irqrestore(&prtd->lock, flags);
-
-	/*
-	 * Query timestamp from DSP if some data is with it.
-	 * This prevents timeouts.
-	 */
-	if (!first_buffer || gapless_transition) {
+		gapless_transition = prtd->gapless_state.gapless_transition;
+		spin_unlock_irqrestore(&prtd->lock, flags);
 		if (gapless_transition)
 			pr_debug("%s session time in gapless transition",
-				 __func__);
+				__func__);
+		/*
+		 *- Do not query if no buffer has been given.
+		 *- Do not query on a gapless transition.
+		 *  Playback for the 2nd stream can start (thus returning time
+		 *  starting from 0) before the driver knows about EOS of first
+		 *  stream.
+		 */
+		if (!first_buffer || gapless_transition) {
 
-		rc = q6asm_get_session_time(prtd->audio_client, &timestamp);
-		if (rc < 0) {
-			pr_err("%s: Get Session Time return value =%lld\n",
-				__func__, timestamp);
-			if (atomic_read(&prtd->error))
-				return -ENETRESET;
+			if (pdata->use_legacy_api)
+				rc = q6asm_get_session_time_legacy(
+				prtd->audio_client, &prtd->marker_timestamp);
 			else
-				return -EAGAIN;
+				rc = q6asm_get_session_time(
+				prtd->audio_client, &prtd->marker_timestamp);
+			if (rc < 0) {
+				pr_err("%s: Get Session Time return =%lld\n",
+					__func__, timestamp);
+				if (atomic_read(&prtd->error))
+					return -ENETRESET;
+				else
+					return -EAGAIN;
+			}
 		}
 	} else {
-		timestamp = prtd->marker_timestamp;
+		spin_unlock_irqrestore(&prtd->lock, flags);
 	}
+	timestamp = prtd->marker_timestamp;
 
 	/* DSP returns timestamp in usec */
 	pr_debug("%s: timestamp = %lld usec\n", __func__, timestamp);
@@ -1991,8 +2475,8 @@ static int msm_compr_ack(struct snd_compr_stream *cstream,
 	return 0;
 }
 
-static int msm_compr_copy(struct snd_compr_stream *cstream,
-			  char __user *buf, size_t count)
+static int msm_compr_playback_copy(struct snd_compr_stream *cstream,
+				  char __user *buf, size_t count)
 {
 	struct snd_compr_runtime *runtime = cstream->runtime;
 	struct msm_compr_audio *prtd = runtime->private_data;
@@ -2055,6 +2539,60 @@ static int msm_compr_copy(struct snd_compr_stream *cstream,
 	return count;
 }
 
+static int msm_compr_capture_copy(struct snd_compr_stream *cstream,
+					char __user *buf, size_t count)
+{
+	struct snd_compr_runtime *runtime = cstream->runtime;
+	struct msm_compr_audio *prtd = runtime->private_data;
+	void *source;
+	unsigned long flags;
+
+	pr_debug("%s: count = %zd\n", __func__, count);
+	if (!prtd->buffer) {
+		pr_err("%s: Buffer is not allocated yet ??", __func__);
+		return 0;
+	}
+
+	spin_lock_irqsave(&prtd->lock, flags);
+	if (atomic_read(&prtd->error)) {
+		pr_err("%s Got RESET EVENTS notification", __func__);
+		spin_unlock_irqrestore(&prtd->lock, flags);
+		return -ENETRESET;
+	}
+
+	source = prtd->buffer + prtd->app_pointer;
+	/* check if we have requested amount of data to copy to user*/
+	if (count <= prtd->received_total - prtd->bytes_copied)	{
+		spin_unlock_irqrestore(&prtd->lock, flags);
+		if (copy_to_user(buf, source, count)) {
+			pr_err("copy_to_user failed");
+			return -EFAULT;
+		}
+		spin_lock_irqsave(&prtd->lock, flags);
+		prtd->app_pointer += count;
+		if (prtd->app_pointer >= prtd->buffer_size)
+			prtd->app_pointer -= prtd->buffer_size;
+		prtd->bytes_copied += count;
+	}
+	msm_compr_read_buffer(prtd);
+
+	spin_unlock_irqrestore(&prtd->lock, flags);
+	return count;
+}
+
+static int msm_compr_copy(struct snd_compr_stream *cstream,
+				char __user *buf, size_t count)
+{
+	int ret = 0;
+
+	pr_debug(" In %s\n", __func__);
+	if (cstream->direction == SND_COMPRESS_PLAYBACK)
+		ret = msm_compr_playback_copy(cstream, buf, count);
+	else if (cstream->direction == SND_COMPRESS_CAPTURE)
+		ret = msm_compr_capture_copy(cstream, buf, count);
+	return ret;
+}
+
 static int msm_compr_get_caps(struct snd_compr_stream *cstream,
 				struct snd_compr_caps *arg)
 {
@@ -2067,7 +2605,7 @@ static int msm_compr_get_caps(struct snd_compr_stream *cstream,
 		memcpy(arg, &prtd->compr_cap, sizeof(struct snd_compr_caps));
 	} else {
 		ret = -EINVAL;
-		pr_err("%s: arg (0x%p), prtd (0x%p)\n", __func__, arg, prtd);
+		pr_err("%s: arg (0x%pK), prtd (0x%pK)\n", __func__, arg, prtd);
 	}
 
 	return ret;
@@ -2123,6 +2661,10 @@ static int msm_compr_get_codec_caps(struct snd_compr_stream *cstream,
 		break;
 	case SND_AUDIOCODEC_APE:
 		break;
+	case SND_AUDIOCODEC_DTS:
+		break;
+	case SND_AUDIOCODEC_DSD:
+		break;
 	default:
 		pr_err("%s: Unsupported audio codec %d\n",
 			__func__, codec->codec);
@@ -2600,6 +3142,8 @@ static int msm_compr_dec_params_put(struct snd_kcontrol *kcontrol,
 	case FORMAT_VORBIS:
 	case FORMAT_ALAC:
 	case FORMAT_APE:
+	case FORMAT_DTS:
+	case FORMAT_DSD:
 		pr_debug("%s: no runtime parameters for codec: %d\n", __func__,
 			 prtd->codec);
 		break;
@@ -2646,7 +3190,7 @@ static int msm_compr_dec_params_get(struct snd_kcontrol *kcontrol,
 	return 0;
 }
 
-static int msm_compr_app_type_cfg_put(struct snd_kcontrol *kcontrol,
+static int msm_compr_playback_app_type_cfg_put(struct snd_kcontrol *kcontrol,
 				      struct snd_ctl_elem_value *ucontrol)
 {
 	u64 fe_id = kcontrol->private_value;
@@ -2665,20 +3209,111 @@ static int msm_compr_app_type_cfg_put(struct snd_kcontrol *kcontrol,
 	acdb_dev_id = ucontrol->value.integer.value[1];
 	if (ucontrol->value.integer.value[2] != 0)
 		sample_rate = ucontrol->value.integer.value[2];
-	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d\n",
-		__func__, app_type, acdb_dev_id, sample_rate);
+	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n",
+		__func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_RX);
 	msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type,
-						acdb_dev_id, sample_rate);
+			acdb_dev_id, sample_rate, SESSION_TYPE_RX);
 
 	return 0;
 }
 
-static int msm_compr_app_type_cfg_get(struct snd_kcontrol *kcontrol,
+static int msm_compr_playback_app_type_cfg_get(struct snd_kcontrol *kcontrol,
 				      struct snd_ctl_elem_value *ucontrol)
 {
+	u64 fe_id = kcontrol->private_value;
+	int ret = 0;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_RX,
+		&app_type, &acdb_dev_id, &sample_rate);
+	if (ret < 0) {
+		pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ucontrol->value.integer.value[0] = app_type;
+	ucontrol->value.integer.value[1] = acdb_dev_id;
+	ucontrol->value.integer.value[2] = sample_rate;
+	pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fe_id, SESSION_TYPE_RX,
+		app_type, acdb_dev_id, sample_rate);
+done:
+	return ret;
+}
+
+static int msm_compr_capture_app_type_cfg_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate = 48000;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		return -EINVAL;
+	}
+
+	app_type = ucontrol->value.integer.value[0];
+	acdb_dev_id = ucontrol->value.integer.value[1];
+	if (ucontrol->value.integer.value[2] != 0)
+		sample_rate = ucontrol->value.integer.value[2];
+	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n",
+		__func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_TX);
+	msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type,
+		acdb_dev_id, sample_rate, SESSION_TYPE_TX);
+
 	return 0;
 }
 
+static int msm_compr_capture_app_type_cfg_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int ret = 0;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_TX,
+		&app_type, &acdb_dev_id, &sample_rate);
+	if (ret < 0) {
+		pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ucontrol->value.integer.value[0] = app_type;
+	ucontrol->value.integer.value[1] = acdb_dev_id;
+	ucontrol->value.integer.value[2] = sample_rate;
+	pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fe_id, SESSION_TYPE_TX,
+		app_type, acdb_dev_id, sample_rate);
+done:
+	return ret;
+}
+
 static int msm_compr_channel_map_put(struct snd_kcontrol *kcontrol,
 				     struct snd_ctl_elem_value *ucontrol)
 {
@@ -2773,6 +3408,8 @@ static int msm_compr_probe(struct snd_soc_platform *platform)
 {
 	struct msm_compr_pdata *pdata;
 	int i;
+	int rc;
+	const char *qdsp_version;
 
 	pr_debug("%s\n", __func__);
 	pdata = (struct msm_compr_pdata *)
@@ -2794,6 +3431,17 @@ static int msm_compr_probe(struct snd_soc_platform *platform)
 	snd_soc_add_platform_controls(platform, msm_compr_gapless_controls,
 				      ARRAY_SIZE(msm_compr_gapless_controls));
 
+	rc =  of_property_read_string(platform->dev->of_node,
+		"qcom,adsp-version", &qdsp_version);
+	if (!rc) {
+		if (!strcmp(qdsp_version, "MDSP 1.2"))
+			pdata->use_legacy_api = true;
+		else
+			pdata->use_legacy_api = false;
+	} else
+		pdata->use_legacy_api = false;
+
+	pr_debug("%s: use legacy api %d\n", __func__, pdata->use_legacy_api);
 	/*
 	 * use_dsp_gapless_mode part of platform data(pdata) is updated from HAL
 	 * through a mixer control before compress driver is opened. The mixer
@@ -2818,7 +3466,7 @@ static int msm_compr_audio_effects_config_info(struct snd_kcontrol *kcontrol,
 					       struct snd_ctl_elem_info *uinfo)
 {
 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-	uinfo->count = 128;
+	uinfo->count = MAX_PP_PARAMS_SZ;
 	uinfo->value.integer.min = 0;
 	uinfo->value.integer.max = 0xFFFFFFFF;
 	return 0;
@@ -2890,7 +3538,7 @@ static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd)
 		return 0;
 	}
 	pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
-		 __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+		 __func__, rtd->dai_link->name, rtd->dai_link->id,
 		 rtd->dai_link->cpu_dai_name, rtd->pcm->device);
 	ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
 		  strlen(suffix) + 1;
@@ -2902,7 +3550,7 @@ static int msm_compr_add_volume_control(struct snd_soc_pcm_runtime *rtd)
 	snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
 		 rtd->pcm->device, suffix);
 	fe_volume_control[0].name = mixer_str;
-	fe_volume_control[0].private_value = rtd->dai_link->be_id;
+	fe_volume_control[0].private_value = rtd->dai_link->id;
 	pr_debug("Registering new mixer ctl %s", mixer_str);
 	snd_soc_add_platform_controls(rtd->platform, fe_volume_control,
 				      ARRAY_SIZE(fe_volume_control));
@@ -2935,7 +3583,7 @@ static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd)
 	}
 
 	pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
-		 __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+		 __func__, rtd->dai_link->name, rtd->dai_link->id,
 		 rtd->dai_link->cpu_dai_name, rtd->pcm->device);
 
 	ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
@@ -2947,7 +3595,7 @@ static int msm_compr_add_audio_effects_control(struct snd_soc_pcm_runtime *rtd)
 	snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
 
 	fe_audio_effects_config_control[0].name = mixer_str;
-	fe_audio_effects_config_control[0].private_value = rtd->dai_link->be_id;
+	fe_audio_effects_config_control[0].private_value = rtd->dai_link->id;
 	pr_debug("Registering new mixer ctl %s\n", mixer_str);
 	snd_soc_add_platform_controls(rtd->platform,
 				fe_audio_effects_config_control,
@@ -2979,7 +3627,7 @@ static int msm_compr_add_query_audio_effect_control(
 		return 0;
 	}
 	pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
-		 __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+		 __func__, rtd->dai_link->name, rtd->dai_link->id,
 		 rtd->dai_link->cpu_dai_name, rtd->pcm->device);
 	ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1;
 	mixer_str = kzalloc(ctl_len, GFP_KERNEL);
@@ -2989,7 +3637,7 @@ static int msm_compr_add_query_audio_effect_control(
 	}
 	snprintf(mixer_str, ctl_len, "%s %d", mixer_ctl_name, rtd->pcm->device);
 	fe_query_audio_effect_control[0].name = mixer_str;
-	fe_query_audio_effect_control[0].private_value = rtd->dai_link->be_id;
+	fe_query_audio_effect_control[0].private_value = rtd->dai_link->id;
 	pr_debug("%s: registering new mixer ctl %s\n", __func__, mixer_str);
 	snd_soc_add_platform_controls(rtd->platform,
 				fe_query_audio_effect_control,
@@ -3024,7 +3672,7 @@ static int msm_compr_add_dec_runtime_params_control(
 	}
 
 	pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
-		 __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+		 __func__, rtd->dai_link->name, rtd->dai_link->id,
 		 rtd->dai_link->cpu_dai_name, rtd->pcm->device);
 
 	ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
@@ -3038,7 +3686,7 @@ static int msm_compr_add_dec_runtime_params_control(
 		 rtd->pcm->device, suffix);
 
 	fe_dec_params_control[0].name = mixer_str;
-	fe_dec_params_control[0].private_value = rtd->dai_link->be_id;
+	fe_dec_params_control[0].private_value = rtd->dai_link->id;
 	pr_debug("Registering new mixer ctl %s", mixer_str);
 	snd_soc_add_platform_controls(rtd->platform,
 				      fe_dec_params_control,
@@ -3049,7 +3697,8 @@ static int msm_compr_add_dec_runtime_params_control(
 
 static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd)
 {
-	const char *mixer_ctl_name	= "Audio Stream";
+	const char *playback_mixer_ctl_name	= "Audio Stream";
+	const char *capture_mixer_ctl_name	= "Audio Stream Capture";
 	const char *deviceNo		= "NN";
 	const char *suffix		= "App Type Cfg";
 	char *mixer_str = NULL;
@@ -3060,8 +3709,8 @@ static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd)
 		.name = "?",
 		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
 		.info = msm_compr_app_type_cfg_info,
-		.put = msm_compr_app_type_cfg_put,
-		.get = msm_compr_app_type_cfg_get,
+		.put = msm_compr_playback_app_type_cfg_put,
+		.get = msm_compr_playback_app_type_cfg_get,
 		.private_value = 0,
 		}
 	};
@@ -3072,24 +3721,45 @@ static int msm_compr_add_app_type_cfg_control(struct snd_soc_pcm_runtime *rtd)
 	}
 
 	pr_debug("%s: added new compr FE ctl with name %s, id %d, cpu dai %s, device no %d\n",
-		 __func__, rtd->dai_link->name, rtd->dai_link->be_id,
-		 rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+		__func__, rtd->dai_link->name, rtd->dai_link->id,
+			rtd->dai_link->cpu_dai_name, rtd->pcm->device);
+	if (rtd->compr->direction == SND_COMPRESS_PLAYBACK)
+		ctl_len = strlen(playback_mixer_ctl_name) + 1 + strlen(deviceNo)
+			 + 1 + strlen(suffix) + 1;
+	else
+		ctl_len = strlen(capture_mixer_ctl_name) + 1 + strlen(deviceNo)
+			+ 1 + strlen(suffix) + 1;
 
-	ctl_len = strlen(mixer_ctl_name) + 1 + strlen(deviceNo) + 1 +
-		  strlen(suffix) + 1;
 	mixer_str = kzalloc(ctl_len, GFP_KERNEL);
 
 	if (!mixer_str)
 		return 0;
 
-	snprintf(mixer_str, ctl_len, "%s %d %s", mixer_ctl_name,
-		 rtd->pcm->device, suffix);
+	if (rtd->compr->direction == SND_COMPRESS_PLAYBACK)
+		snprintf(mixer_str, ctl_len, "%s %d %s",
+			 playback_mixer_ctl_name, rtd->pcm->device, suffix);
+	else
+		snprintf(mixer_str, ctl_len, "%s %d %s",
+			 capture_mixer_ctl_name, rtd->pcm->device, suffix);
+
 	fe_app_type_cfg_control[0].name = mixer_str;
-	fe_app_type_cfg_control[0].private_value = rtd->dai_link->be_id;
+	fe_app_type_cfg_control[0].private_value = rtd->dai_link->id;
+
+	if (rtd->compr->direction == SND_COMPRESS_PLAYBACK) {
+		fe_app_type_cfg_control[0].put =
+					 msm_compr_playback_app_type_cfg_put;
+		fe_app_type_cfg_control[0].get =
+					 msm_compr_playback_app_type_cfg_get;
+	} else {
+		fe_app_type_cfg_control[0].put =
+					 msm_compr_capture_app_type_cfg_put;
+		fe_app_type_cfg_control[0].get =
+					 msm_compr_capture_app_type_cfg_get;
+	}
 	pr_debug("Registering new mixer ctl %s", mixer_str);
 	snd_soc_add_platform_controls(rtd->platform,
-				      fe_app_type_cfg_control,
-				      ARRAY_SIZE(fe_app_type_cfg_control));
+				fe_app_type_cfg_control,
+				ARRAY_SIZE(fe_app_type_cfg_control));
 	kfree(mixer_str);
 	return 0;
 }
@@ -3119,7 +3789,7 @@ static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd)
 	}
 
 	pr_debug("%s: added new compr FE with name %s, id %d, cpu dai %s, device no %d\n",
-		 __func__, rtd->dai_link->name, rtd->dai_link->be_id,
+		 __func__, rtd->dai_link->name, rtd->dai_link->id,
 		 rtd->dai_link->cpu_dai_name, rtd->pcm->device);
 
 	ctl_len = strlen(mixer_ctl_name) + strlen(deviceNo) + 1;
@@ -3131,16 +3801,16 @@ static int msm_compr_add_channel_map_control(struct snd_soc_pcm_runtime *rtd)
 	snprintf(mixer_str, ctl_len, "%s%d", mixer_ctl_name, rtd->pcm->device);
 
 	fe_channel_map_control[0].name = mixer_str;
-	fe_channel_map_control[0].private_value = rtd->dai_link->be_id;
+	fe_channel_map_control[0].private_value = rtd->dai_link->id;
 	pr_debug("%s: Registering new mixer ctl %s\n", __func__, mixer_str);
 	snd_soc_add_platform_controls(rtd->platform,
 				fe_channel_map_control,
 				ARRAY_SIZE(fe_channel_map_control));
 
 	pdata = snd_soc_platform_get_drvdata(rtd->platform);
-	pdata->ch_map[rtd->dai_link->be_id] =
+	pdata->ch_map[rtd->dai_link->id] =
 		 kzalloc(sizeof(struct msm_compr_ch_map), GFP_KERNEL);
-	if (!pdata->ch_map[rtd->dai_link->be_id]) {
+	if (!pdata->ch_map[rtd->dai_link->id]) {
 		pr_err("%s: Could not allocate memory for channel map\n",
 			__func__);
 		kfree(mixer_str);
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
new file mode 100644
index 0000000..dffac45
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-hdmi-v2.c
@@ -0,0 +1,486 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <sound/pcm_params.h>
+
+#define HDMI_RX_CA_MAX 0x32
+
+enum {
+	STATUS_PORT_STARTED, /* track if AFE port has started */
+	STATUS_MAX
+};
+
+struct msm_ext_disp_ca {
+	bool set_ca;
+	u32 ca;
+};
+
+struct msm_dai_q6_hdmi_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	u32 rate;
+	u32 channels;
+	struct msm_ext_disp_ca ca;
+	union afe_port_config port_config;
+};
+
+static int msm_dai_q6_ext_disp_format_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	if (!dai_data) {
+		pr_err("%s: dai_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dai_data->port_config.hdmi_multi_ch.datatype = value;
+	pr_debug("%s: value = %d\n", __func__, value);
+
+	return 0;
+}
+
+static int msm_dai_q6_ext_disp_format_get(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+	if (!dai_data) {
+		pr_err("%s: dai_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] =
+		dai_data->port_config.hdmi_multi_ch.datatype;
+	pr_debug("%s: value = %ld\n",
+		 __func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm_dai_q6_ext_disp_ca_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+	if (!dai_data) {
+		pr_err("%s: dai_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dai_data->ca.ca = ucontrol->value.integer.value[0];
+	dai_data->ca.set_ca = true;
+	pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca);
+	return 0;
+}
+
+static int msm_dai_q6_ext_disp_ca_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = kcontrol->private_data;
+
+	if (!dai_data) {
+		pr_err("%s: dai_data is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = dai_data->ca.ca;
+	pr_debug("%s: ca = %d\n", __func__, dai_data->ca.ca);
+	return 0;
+}
+
+/* HDMI format field for AFE_PORT_MULTI_CHAN_HDMI_AUDIO_IF_CONFIG command
+ *  0: linear PCM
+ *  1: non-linear PCM
+ */
+static const char * const hdmi_format[] = {
+	"LPCM",
+	"Compr"
+};
+
+static const struct soc_enum hdmi_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, hdmi_format),
+};
+
+static const struct snd_kcontrol_new hdmi_config_controls[] = {
+	SOC_ENUM_EXT("HDMI RX Format", hdmi_config_enum[0],
+				 msm_dai_q6_ext_disp_format_get,
+				 msm_dai_q6_ext_disp_format_put),
+	SOC_SINGLE_MULTI_EXT("HDMI RX CA", SND_SOC_NOPM, 0,
+				 HDMI_RX_CA_MAX, 0, 1,
+				 msm_dai_q6_ext_disp_ca_get,
+				 msm_dai_q6_ext_disp_ca_put),
+};
+
+static const struct snd_kcontrol_new display_port_config_controls[] = {
+	SOC_ENUM_EXT("Display Port RX Format", hdmi_config_enum[0],
+				 msm_dai_q6_ext_disp_format_get,
+				 msm_dai_q6_ext_disp_format_put),
+	SOC_SINGLE_MULTI_EXT("Display Port RX CA", SND_SOC_NOPM, 0,
+				 HDMI_RX_CA_MAX, 0, 1,
+				 msm_dai_q6_ext_disp_ca_get,
+				 msm_dai_q6_ext_disp_ca_put),
+};
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hdmi_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.hdmi_multi_ch.reserved = 0;
+	dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version = 1;
+	dai_data->port_config.hdmi_multi_ch.sample_rate = dai_data->rate;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_data->port_config.hdmi_multi_ch.bit_width = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dai_data->port_config.hdmi_multi_ch.bit_width = 24;
+		break;
+	}
+
+	/*refer to HDMI spec CEA-861-E: Table 28 Audio InfoFrame Data Byte 4*/
+	switch (dai_data->channels) {
+	case 2:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0;
+		break;
+	case 3:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x02;
+		break;
+	case 4:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x06;
+		break;
+	case 5:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0A;
+		break;
+	case 6:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x0B;
+		break;
+	case 7:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x12;
+		break;
+	case 8:
+		dai_data->port_config.hdmi_multi_ch.channel_allocation = 0x13;
+		break;
+	default:
+		dev_err(dai->dev, "invalid Channels = %u\n",
+				dai_data->channels);
+		return -EINVAL;
+	}
+	dev_dbg(dai->dev, "%s() minor version: %u samplerate: %u bitwidth: %u\n"
+		"num_ch = %u channel_allocation = %u datatype = %d\n", __func__,
+		dai_data->port_config.hdmi_multi_ch.hdmi_cfg_minor_version,
+		dai_data->port_config.hdmi_multi_ch.sample_rate,
+		dai_data->port_config.hdmi_multi_ch.bit_width,
+		dai_data->channels,
+		dai_data->port_config.hdmi_multi_ch.channel_allocation,
+		dai_data->port_config.hdmi_multi_ch.datatype);
+
+	return 0;
+}
+
+
+static void msm_dai_q6_hdmi_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_info("%s:  afe port not started. dai_data->status_mask = %ld\n",
+		 __func__, *dai_data->status_mask);
+		return;
+	}
+
+	rc = afe_close(dai->id); /* can block */
+	if (rc < 0)
+		dev_err(dai->dev, "fail to close AFE port\n");
+
+	pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+			*dai_data->status_mask);
+
+	clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_hdmi_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (dai_data->ca.set_ca)
+		dai_data->port_config.hdmi_multi_ch.channel_allocation =
+							      dai_data->ca.ca;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_port_start(dai->id, &dai_data->port_config,
+				    dai_data->rate);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to open AFE port %x\n",
+				dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+	}
+
+	return rc;
+}
+
+static inline void msm_dai_q6_hdmi_set_dai_id(struct snd_soc_dai *dai)
+{
+	if (!dai->driver->id) {
+		dev_warn(dai->dev, "DAI driver id is not set\n");
+		return;
+	}
+	dai->id = dai->driver->id;
+}
+
+static int msm_dai_q6_hdmi_dai_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data;
+	const struct snd_kcontrol_new *kcontrol;
+	int rc = 0;
+	struct snd_soc_dapm_route intercon;
+	struct snd_soc_dapm_context *dapm;
+
+	if (!dai || !dai->driver) {
+		pr_err("%s: dai or dai->driver is NULL\n", __func__);
+		return -EINVAL;
+	}
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_hdmi_dai_data),
+		GFP_KERNEL);
+
+	if (!dai_data) {
+		dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+		dai->id);
+		rc = -ENOMEM;
+	} else
+		dev_set_drvdata(dai->dev, dai_data);
+
+	msm_dai_q6_hdmi_set_dai_id(dai);
+
+	if (dai->driver->id == HDMI_RX) {
+		kcontrol = &hdmi_config_controls[0];
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(kcontrol, dai_data));
+
+		kcontrol = &hdmi_config_controls[1];
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(kcontrol, dai_data));
+	} else if (dai->driver->id == DISPLAY_PORT_RX) {
+		kcontrol = &display_port_config_controls[0];
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(kcontrol, dai_data));
+
+		kcontrol = &display_port_config_controls[1];
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(kcontrol, dai_data));
+	} else {
+		dev_err(dai->dev, "%s: Invalid id:%d\n",
+			__func__, dai->driver->id);
+		kfree(dai_data);
+		dev_set_drvdata(dai->dev, NULL);
+		return -EINVAL;
+	}
+
+	dapm = snd_soc_component_get_dapm(dai->component);
+	memset(&intercon, 0, sizeof(intercon));
+	if (!rc) {
+		if (dai->driver->playback.stream_name &&
+			dai->driver->playback.aif_name) {
+			dev_dbg(dai->dev, "%s add route for widget %s",
+				   __func__, dai->driver->playback.stream_name);
+			intercon.source = dai->driver->playback.aif_name;
+			intercon.sink = dai->driver->playback.stream_name;
+			dev_dbg(dai->dev, "%s src %s sink %s\n",
+				   __func__, intercon.source, intercon.sink);
+			snd_soc_dapm_add_routes(dapm, &intercon, 1);
+		}
+		if (dai->driver->capture.stream_name &&
+		   dai->driver->capture.aif_name) {
+			dev_dbg(dai->dev, "%s add route for widget %s",
+				   __func__, dai->driver->capture.stream_name);
+			intercon.sink = dai->driver->capture.aif_name;
+			intercon.source = dai->driver->capture.stream_name;
+			dev_dbg(dai->dev, "%s src %s sink %s\n",
+				   __func__, intercon.source, intercon.sink);
+			snd_soc_dapm_add_routes(dapm, &intercon, 1);
+		}
+	}
+	return rc;
+}
+
+static int msm_dai_q6_hdmi_dai_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_hdmi_dai_data *dai_data;
+	int rc;
+
+	dai_data = dev_get_drvdata(dai->dev);
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(dai->id); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AFE port\n");
+
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+	kfree(dai_data);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_hdmi_ops = {
+	.prepare	= msm_dai_q6_hdmi_prepare,
+	.hw_params	= msm_dai_q6_hdmi_hw_params,
+	.shutdown	= msm_dai_q6_hdmi_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_hdmi_hdmi_rx_dai = {
+	.playback = {
+		.stream_name = "HDMI Playback",
+		.aif_name = "HDMI",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+		 SNDRV_PCM_RATE_192000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.channels_min = 2,
+		.channels_max = 8,
+		.rate_max = 192000,
+		.rate_min = 48000,
+	},
+	.ops = &msm_dai_q6_hdmi_ops,
+	.id = HDMI_RX,
+	.probe = msm_dai_q6_hdmi_dai_probe,
+	.remove = msm_dai_q6_hdmi_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_display_port_rx_dai[] = {
+	{
+		.playback = {
+			.stream_name = "Display Port Playback",
+			.aif_name = "DISPLAY_PORT",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 2,
+			.channels_max = 8,
+			.rate_max =     192000,
+			.rate_min =     48000,
+		},
+		.ops = &msm_dai_q6_hdmi_ops,
+		.id = DISPLAY_PORT_RX,
+		.probe = msm_dai_q6_hdmi_dai_probe,
+		.remove = msm_dai_q6_hdmi_dai_remove,
+	},
+};
+
+static const struct snd_soc_component_driver msm_dai_hdmi_q6_component = {
+	.name		= "msm-dai-q6-hdmi",
+};
+
+/* To do: change to register DAIs as batch */
+static int msm_dai_q6_hdmi_dev_probe(struct platform_device *pdev)
+{
+	int rc, id;
+	const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, q6_dev_id);
+		return rc;
+	}
+
+	pdev->id = id;
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+			dev_name(&pdev->dev), pdev->id);
+
+	switch (pdev->id) {
+	case HDMI_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_hdmi_q6_component,
+			&msm_dai_q6_hdmi_hdmi_rx_dai, 1);
+		break;
+	case DISPLAY_PORT_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_hdmi_q6_component,
+			&msm_dai_q6_display_port_rx_dai[0],
+			ARRAY_SIZE(msm_dai_q6_display_port_rx_dai));
+		break;
+	default:
+		dev_err(&pdev->dev, "invalid device ID %d\n", pdev->id);
+		rc = -ENODEV;
+		break;
+	}
+	return rc;
+}
+
+static int msm_dai_q6_hdmi_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_hdmi_dt_match[] = {
+	{.compatible = "qcom,msm-dai-q6-hdmi"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_hdmi_dt_match);
+
+static struct platform_driver msm_dai_q6_hdmi_driver = {
+	.probe  = msm_dai_q6_hdmi_dev_probe,
+	.remove = msm_dai_q6_hdmi_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-hdmi",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_hdmi_dt_match,
+	},
+};
+
+static int __init msm_dai_q6_hdmi_init(void)
+{
+	return platform_driver_register(&msm_dai_q6_hdmi_driver);
+}
+module_init(msm_dai_q6_hdmi_init);
+
+static void __exit msm_dai_q6_hdmi_exit(void)
+{
+	platform_driver_unregister(&msm_dai_q6_hdmi_driver);
+}
+module_exit(msm_dai_q6_hdmi_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP HDMI DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
new file mode 100644
index 0000000..52c2296
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-q6-v2.c
@@ -0,0 +1,7877 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/wcd9xxx/core.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/msm-dai-q6-v2.h>
+#include <sound/pcm_params.h>
+
+#define MSM_DAI_PRI_AUXPCM_DT_DEV_ID 1
+#define MSM_DAI_SEC_AUXPCM_DT_DEV_ID 2
+#define MSM_DAI_TERT_AUXPCM_DT_DEV_ID 3
+#define MSM_DAI_QUAT_AUXPCM_DT_DEV_ID 4
+
+
+#define spdif_clock_value(rate) (2*rate*32*2)
+#define CHANNEL_STATUS_SIZE 24
+#define CHANNEL_STATUS_MASK_INIT 0x0
+#define CHANNEL_STATUS_MASK 0x4
+#define AFE_API_VERSION_CLOCK_SET 1
+
+#define DAI_FORMATS_S16_S24_S32_LE (SNDRV_PCM_FMTBIT_S16_LE | \
+				    SNDRV_PCM_FMTBIT_S24_LE | \
+				    SNDRV_PCM_FMTBIT_S32_LE)
+
+enum {
+	ENC_FMT_NONE,
+	ENC_FMT_SBC = ASM_MEDIA_FMT_SBC,
+	ENC_FMT_AAC_V2 = ASM_MEDIA_FMT_AAC_V2,
+	ENC_FMT_APTX = ASM_MEDIA_FMT_APTX,
+	ENC_FMT_APTX_HD = ASM_MEDIA_FMT_APTX_HD,
+};
+
+static const struct afe_clk_set lpass_clk_set_default = {
+	AFE_API_VERSION_CLOCK_SET,
+	Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT,
+	Q6AFE_LPASS_OSR_CLK_2_P048_MHZ,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+static const struct afe_clk_cfg lpass_clk_cfg_default = {
+	AFE_API_VERSION_I2S_CONFIG,
+	Q6AFE_LPASS_OSR_CLK_2_P048_MHZ,
+	0,
+	Q6AFE_LPASS_CLK_SRC_INTERNAL,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	Q6AFE_LPASS_MODE_CLK1_VALID,
+	0,
+};
+enum {
+	STATUS_PORT_STARTED, /* track if AFE port has started */
+	/* track AFE Tx port status for bi-directional transfers */
+	STATUS_TX_PORT,
+	/* track AFE Rx port status for bi-directional transfers */
+	STATUS_RX_PORT,
+	STATUS_MAX
+};
+
+enum {
+	RATE_8KHZ,
+	RATE_16KHZ,
+	RATE_MAX_NUM_OF_AUX_PCM_RATES,
+};
+
+enum {
+	IDX_PRIMARY_TDM_RX_0,
+	IDX_PRIMARY_TDM_RX_1,
+	IDX_PRIMARY_TDM_RX_2,
+	IDX_PRIMARY_TDM_RX_3,
+	IDX_PRIMARY_TDM_RX_4,
+	IDX_PRIMARY_TDM_RX_5,
+	IDX_PRIMARY_TDM_RX_6,
+	IDX_PRIMARY_TDM_RX_7,
+	IDX_PRIMARY_TDM_TX_0,
+	IDX_PRIMARY_TDM_TX_1,
+	IDX_PRIMARY_TDM_TX_2,
+	IDX_PRIMARY_TDM_TX_3,
+	IDX_PRIMARY_TDM_TX_4,
+	IDX_PRIMARY_TDM_TX_5,
+	IDX_PRIMARY_TDM_TX_6,
+	IDX_PRIMARY_TDM_TX_7,
+	IDX_SECONDARY_TDM_RX_0,
+	IDX_SECONDARY_TDM_RX_1,
+	IDX_SECONDARY_TDM_RX_2,
+	IDX_SECONDARY_TDM_RX_3,
+	IDX_SECONDARY_TDM_RX_4,
+	IDX_SECONDARY_TDM_RX_5,
+	IDX_SECONDARY_TDM_RX_6,
+	IDX_SECONDARY_TDM_RX_7,
+	IDX_SECONDARY_TDM_TX_0,
+	IDX_SECONDARY_TDM_TX_1,
+	IDX_SECONDARY_TDM_TX_2,
+	IDX_SECONDARY_TDM_TX_3,
+	IDX_SECONDARY_TDM_TX_4,
+	IDX_SECONDARY_TDM_TX_5,
+	IDX_SECONDARY_TDM_TX_6,
+	IDX_SECONDARY_TDM_TX_7,
+	IDX_TERTIARY_TDM_RX_0,
+	IDX_TERTIARY_TDM_RX_1,
+	IDX_TERTIARY_TDM_RX_2,
+	IDX_TERTIARY_TDM_RX_3,
+	IDX_TERTIARY_TDM_RX_4,
+	IDX_TERTIARY_TDM_RX_5,
+	IDX_TERTIARY_TDM_RX_6,
+	IDX_TERTIARY_TDM_RX_7,
+	IDX_TERTIARY_TDM_TX_0,
+	IDX_TERTIARY_TDM_TX_1,
+	IDX_TERTIARY_TDM_TX_2,
+	IDX_TERTIARY_TDM_TX_3,
+	IDX_TERTIARY_TDM_TX_4,
+	IDX_TERTIARY_TDM_TX_5,
+	IDX_TERTIARY_TDM_TX_6,
+	IDX_TERTIARY_TDM_TX_7,
+	IDX_QUATERNARY_TDM_RX_0,
+	IDX_QUATERNARY_TDM_RX_1,
+	IDX_QUATERNARY_TDM_RX_2,
+	IDX_QUATERNARY_TDM_RX_3,
+	IDX_QUATERNARY_TDM_RX_4,
+	IDX_QUATERNARY_TDM_RX_5,
+	IDX_QUATERNARY_TDM_RX_6,
+	IDX_QUATERNARY_TDM_RX_7,
+	IDX_QUATERNARY_TDM_TX_0,
+	IDX_QUATERNARY_TDM_TX_1,
+	IDX_QUATERNARY_TDM_TX_2,
+	IDX_QUATERNARY_TDM_TX_3,
+	IDX_QUATERNARY_TDM_TX_4,
+	IDX_QUATERNARY_TDM_TX_5,
+	IDX_QUATERNARY_TDM_TX_6,
+	IDX_QUATERNARY_TDM_TX_7,
+	IDX_TDM_MAX,
+};
+
+enum {
+	IDX_GROUP_PRIMARY_TDM_RX,
+	IDX_GROUP_PRIMARY_TDM_TX,
+	IDX_GROUP_SECONDARY_TDM_RX,
+	IDX_GROUP_SECONDARY_TDM_TX,
+	IDX_GROUP_TERTIARY_TDM_RX,
+	IDX_GROUP_TERTIARY_TDM_TX,
+	IDX_GROUP_QUATERNARY_TDM_RX,
+	IDX_GROUP_QUATERNARY_TDM_TX,
+	IDX_GROUP_TDM_MAX,
+};
+
+struct msm_dai_q6_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	DECLARE_BITMAP(hwfree_status, STATUS_MAX);
+	u32 rate;
+	u32 channels;
+	u32 bitwidth;
+	u32 cal_mode;
+	u32 afe_in_channels;
+	u16 afe_in_bitformat;
+	struct afe_enc_config enc_config;
+	union afe_port_config port_config;
+};
+
+struct msm_dai_q6_spdif_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	u32 rate;
+	u32 channels;
+	u32 bitwidth;
+	struct afe_spdif_port_config spdif_port;
+};
+
+struct msm_dai_q6_mi2s_dai_config {
+	u16 pdata_mi2s_lines;
+	struct msm_dai_q6_dai_data mi2s_dai_data;
+};
+
+struct msm_dai_q6_mi2s_dai_data {
+	struct msm_dai_q6_mi2s_dai_config tx_dai;
+	struct msm_dai_q6_mi2s_dai_config rx_dai;
+};
+
+struct msm_dai_q6_auxpcm_dai_data {
+	/* BITMAP to track Rx and Tx port usage count */
+	DECLARE_BITMAP(auxpcm_port_status, STATUS_MAX);
+	struct mutex rlock; /* auxpcm dev resource lock */
+	u16 rx_pid; /* AUXPCM RX AFE port ID */
+	u16 tx_pid; /* AUXPCM TX AFE port ID */
+	u16 afe_clk_ver;
+	struct afe_clk_cfg clk_cfg; /* hold LPASS clock configuration */
+	struct afe_clk_set clk_set; /* hold LPASS clock configuration */
+	struct msm_dai_q6_dai_data bdai_data; /* incoporate base DAI data */
+};
+
+struct msm_dai_q6_tdm_dai_data {
+	DECLARE_BITMAP(status_mask, STATUS_MAX);
+	u32 rate;
+	u32 channels;
+	u32 bitwidth;
+	struct afe_clk_set clk_set; /* hold LPASS clock config. */
+	union afe_port_group_config group_cfg; /* hold tdm group config */
+	struct afe_tdm_port_config port_cfg; /* hold tdm config */
+};
+
+/* MI2S format field for AFE_PORT_CMD_I2S_CONFIG command
+ *  0: linear PCM
+ *  1: non-linear PCM
+ *  2: PCM data in IEC 60968 container
+ *  3: compressed data in IEC 60958 container
+ */
+static const char *const mi2s_format[] = {
+	"LPCM",
+	"Compr",
+	"LPCM-60958",
+	"Compr-60958"
+};
+
+static const struct soc_enum mi2s_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(4, mi2s_format),
+};
+
+static const char *const sb_format[] = {
+	"UNPACKED",
+	"PACKED_16B",
+	"DSD_DOP",
+};
+
+static const struct soc_enum sb_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(3, sb_format),
+};
+
+static const char *const tdm_data_format[] = {
+	"LPCM",
+	"Compr",
+};
+
+static const char *const tdm_header_type[] = {
+	"Invalid",
+	"Default",
+	"Entertainment",
+};
+
+static const struct soc_enum tdm_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, tdm_data_format),
+	SOC_ENUM_SINGLE_EXT(3, tdm_header_type),
+};
+
+static DEFINE_MUTEX(tdm_mutex);
+
+static atomic_t tdm_group_ref[IDX_GROUP_TDM_MAX];
+
+/* cache of group cfg per parent node */
+static struct afe_param_id_group_device_tdm_cfg tdm_group_cfg = {
+	AFE_API_VERSION_GROUP_DEVICE_TDM_CONFIG,
+	AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX,
+	0,
+	{AFE_PORT_ID_QUATERNARY_TDM_RX,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_1,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_2,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_3,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_4,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_5,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_6,
+	AFE_PORT_ID_QUATERNARY_TDM_RX_7},
+	8,
+	48000,
+	32,
+	8,
+	32,
+	0xFF,
+};
+
+static struct afe_clk_set tdm_clk_set = {
+	AFE_API_VERSION_CLOCK_SET,
+	Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT,
+	Q6AFE_LPASS_IBIT_CLK_DISABLE,
+	Q6AFE_LPASS_CLK_ATTRIBUTE_INVERT_COUPLE_NO,
+	Q6AFE_LPASS_CLK_ROOT_DEFAULT,
+	0,
+};
+
+int msm_dai_q6_get_group_idx(u16 id)
+{
+	switch (id) {
+	case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+		return IDX_GROUP_PRIMARY_TDM_RX;
+	case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+		return IDX_GROUP_PRIMARY_TDM_TX;
+	case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+		return IDX_GROUP_SECONDARY_TDM_RX;
+	case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+		return IDX_GROUP_SECONDARY_TDM_TX;
+	case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+		return IDX_GROUP_TERTIARY_TDM_RX;
+	case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+		return IDX_GROUP_TERTIARY_TDM_TX;
+	case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		return IDX_GROUP_QUATERNARY_TDM_RX;
+	case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		return IDX_GROUP_QUATERNARY_TDM_TX;
+	default: return -EINVAL;
+	}
+}
+
+int msm_dai_q6_get_port_idx(u16 id)
+{
+	switch (id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+		return IDX_PRIMARY_TDM_RX_0;
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+		return IDX_PRIMARY_TDM_TX_0;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+		return IDX_PRIMARY_TDM_RX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+		return IDX_PRIMARY_TDM_TX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+		return IDX_PRIMARY_TDM_RX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+		return IDX_PRIMARY_TDM_TX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+		return IDX_PRIMARY_TDM_RX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+		return IDX_PRIMARY_TDM_TX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+		return IDX_PRIMARY_TDM_RX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+		return IDX_PRIMARY_TDM_TX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+		return IDX_PRIMARY_TDM_RX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+		return IDX_PRIMARY_TDM_TX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+		return IDX_PRIMARY_TDM_RX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+		return IDX_PRIMARY_TDM_TX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+		return IDX_PRIMARY_TDM_RX_7;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+		return IDX_PRIMARY_TDM_TX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+		return IDX_SECONDARY_TDM_RX_0;
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+		return IDX_SECONDARY_TDM_TX_0;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+		return IDX_SECONDARY_TDM_RX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+		return IDX_SECONDARY_TDM_TX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+		return IDX_SECONDARY_TDM_RX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+		return IDX_SECONDARY_TDM_TX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+		return IDX_SECONDARY_TDM_RX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+		return IDX_SECONDARY_TDM_TX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+		return IDX_SECONDARY_TDM_RX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+		return IDX_SECONDARY_TDM_TX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+		return IDX_SECONDARY_TDM_RX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+		return IDX_SECONDARY_TDM_TX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+		return IDX_SECONDARY_TDM_RX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+		return IDX_SECONDARY_TDM_TX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+		return IDX_SECONDARY_TDM_RX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+		return IDX_SECONDARY_TDM_TX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+		return IDX_TERTIARY_TDM_RX_0;
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+		return IDX_TERTIARY_TDM_TX_0;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+		return IDX_TERTIARY_TDM_RX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+		return IDX_TERTIARY_TDM_TX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+		return IDX_TERTIARY_TDM_RX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+		return IDX_TERTIARY_TDM_TX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+		return IDX_TERTIARY_TDM_RX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+		return IDX_TERTIARY_TDM_TX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+		return IDX_TERTIARY_TDM_RX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+		return IDX_TERTIARY_TDM_TX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+		return IDX_TERTIARY_TDM_RX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+		return IDX_TERTIARY_TDM_TX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+		return IDX_TERTIARY_TDM_RX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+		return IDX_TERTIARY_TDM_TX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+		return IDX_TERTIARY_TDM_RX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+		return IDX_TERTIARY_TDM_TX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+		return IDX_QUATERNARY_TDM_RX_0;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		return IDX_QUATERNARY_TDM_TX_0;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+		return IDX_QUATERNARY_TDM_RX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+		return IDX_QUATERNARY_TDM_TX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+		return IDX_QUATERNARY_TDM_RX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+		return IDX_QUATERNARY_TDM_TX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+		return IDX_QUATERNARY_TDM_RX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+		return IDX_QUATERNARY_TDM_TX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+		return IDX_QUATERNARY_TDM_RX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+		return IDX_QUATERNARY_TDM_TX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+		return IDX_QUATERNARY_TDM_RX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+		return IDX_QUATERNARY_TDM_TX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+		return IDX_QUATERNARY_TDM_RX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+		return IDX_QUATERNARY_TDM_TX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		return IDX_QUATERNARY_TDM_RX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		return IDX_QUATERNARY_TDM_TX_7;
+	default: return -EINVAL;
+	}
+}
+
+static u16 msm_dai_q6_max_num_slot(int frame_rate)
+{
+	/* Max num of slots is bits per frame divided
+	 * by bits per sample which is 16
+	 */
+	switch (frame_rate) {
+	case AFE_PORT_PCM_BITS_PER_FRAME_8:
+		return 0;
+	case AFE_PORT_PCM_BITS_PER_FRAME_16:
+		return 1;
+	case AFE_PORT_PCM_BITS_PER_FRAME_32:
+		return 2;
+	case AFE_PORT_PCM_BITS_PER_FRAME_64:
+		return 4;
+	case AFE_PORT_PCM_BITS_PER_FRAME_128:
+		return 8;
+	case AFE_PORT_PCM_BITS_PER_FRAME_256:
+		return 16;
+	default:
+		pr_err("%s Invalid bits per frame %d\n",
+			__func__, frame_rate);
+		return 0;
+	}
+}
+
+static int msm_dai_q6_dai_add_route(struct snd_soc_dai *dai)
+{
+	struct snd_soc_dapm_route intercon;
+	struct snd_soc_dapm_context *dapm;
+
+	if (!dai) {
+		pr_err("%s: Invalid params dai\n", __func__);
+		return -EINVAL;
+	}
+	if (!dai->driver) {
+		pr_err("%s: Invalid params dai driver\n", __func__);
+		return -EINVAL;
+	}
+	dapm = snd_soc_component_get_dapm(dai->component);
+	memset(&intercon, 0, sizeof(intercon));
+	if (dai->driver->playback.stream_name &&
+		dai->driver->playback.aif_name) {
+		dev_dbg(dai->dev, "%s: add route for widget %s",
+				__func__, dai->driver->playback.stream_name);
+		intercon.source = dai->driver->playback.aif_name;
+		intercon.sink = dai->driver->playback.stream_name;
+		dev_dbg(dai->dev, "%s: src %s sink %s\n",
+				__func__, intercon.source, intercon.sink);
+		snd_soc_dapm_add_routes(dapm, &intercon, 1);
+	}
+	if (dai->driver->capture.stream_name &&
+		dai->driver->capture.aif_name) {
+		dev_dbg(dai->dev, "%s: add route for widget %s",
+				__func__, dai->driver->capture.stream_name);
+		intercon.sink = dai->driver->capture.aif_name;
+		intercon.source = dai->driver->capture.stream_name;
+		dev_dbg(dai->dev, "%s: src %s sink %s\n",
+				__func__, intercon.source, intercon.sink);
+		snd_soc_dapm_add_routes(dapm, &intercon, 1);
+	}
+	return 0;
+}
+
+static int msm_dai_q6_auxpcm_hw_params(
+				struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data;
+	struct msm_dai_auxpcm_pdata *auxpcm_pdata =
+			(struct msm_dai_auxpcm_pdata *) dai->dev->platform_data;
+	int rc = 0, slot_mapping_copy_len = 0;
+
+	if (params_channels(params) != 1 || (params_rate(params) != 8000 &&
+	    params_rate(params) != 16000)) {
+		dev_err(dai->dev, "%s: invalid param chan %d rate %d\n",
+			__func__, params_channels(params), params_rate(params));
+		return -EINVAL;
+	}
+
+	mutex_lock(&aux_dai_data->rlock);
+
+	if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+	    test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+		/* AUXPCM DAI in use */
+		if (dai_data->rate != params_rate(params)) {
+			dev_err(dai->dev, "%s: rate mismatch of running DAI\n",
+			__func__);
+			rc = -EINVAL;
+		}
+		mutex_unlock(&aux_dai_data->rlock);
+		return rc;
+	}
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+
+	if (dai_data->rate == 8000) {
+		dai_data->port_config.pcm.pcm_cfg_minor_version =
+				AFE_API_VERSION_PCM_CONFIG;
+		dai_data->port_config.pcm.aux_mode = auxpcm_pdata->mode_8k.mode;
+		dai_data->port_config.pcm.sync_src = auxpcm_pdata->mode_8k.sync;
+		dai_data->port_config.pcm.frame_setting =
+					auxpcm_pdata->mode_8k.frame;
+		dai_data->port_config.pcm.quantype =
+					 auxpcm_pdata->mode_8k.quant;
+		dai_data->port_config.pcm.ctrl_data_out_enable =
+					 auxpcm_pdata->mode_8k.data;
+		dai_data->port_config.pcm.sample_rate = dai_data->rate;
+		dai_data->port_config.pcm.num_channels = dai_data->channels;
+		dai_data->port_config.pcm.bit_width = 16;
+		if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <=
+		    auxpcm_pdata->mode_8k.num_slots)
+			slot_mapping_copy_len =
+				ARRAY_SIZE(
+				dai_data->port_config.pcm.slot_number_mapping)
+				 * sizeof(uint16_t);
+		else
+			slot_mapping_copy_len = auxpcm_pdata->mode_8k.num_slots
+				* sizeof(uint16_t);
+
+		if (auxpcm_pdata->mode_8k.slot_mapping) {
+			memcpy(dai_data->port_config.pcm.slot_number_mapping,
+			       auxpcm_pdata->mode_8k.slot_mapping,
+			       slot_mapping_copy_len);
+		} else {
+			dev_err(dai->dev, "%s 8khz slot mapping is NULL\n",
+				__func__);
+			mutex_unlock(&aux_dai_data->rlock);
+			return -EINVAL;
+		}
+	} else {
+		dai_data->port_config.pcm.pcm_cfg_minor_version =
+				AFE_API_VERSION_PCM_CONFIG;
+		dai_data->port_config.pcm.aux_mode =
+					auxpcm_pdata->mode_16k.mode;
+		dai_data->port_config.pcm.sync_src =
+					auxpcm_pdata->mode_16k.sync;
+		dai_data->port_config.pcm.frame_setting =
+					auxpcm_pdata->mode_16k.frame;
+		dai_data->port_config.pcm.quantype =
+					auxpcm_pdata->mode_16k.quant;
+		dai_data->port_config.pcm.ctrl_data_out_enable =
+					auxpcm_pdata->mode_16k.data;
+		dai_data->port_config.pcm.sample_rate = dai_data->rate;
+		dai_data->port_config.pcm.num_channels = dai_data->channels;
+		dai_data->port_config.pcm.bit_width = 16;
+		if (ARRAY_SIZE(dai_data->port_config.pcm.slot_number_mapping) <=
+		    auxpcm_pdata->mode_16k.num_slots)
+			slot_mapping_copy_len =
+				ARRAY_SIZE(
+				dai_data->port_config.pcm.slot_number_mapping)
+				 * sizeof(uint16_t);
+		else
+			slot_mapping_copy_len = auxpcm_pdata->mode_16k.num_slots
+				* sizeof(uint16_t);
+
+		if (auxpcm_pdata->mode_16k.slot_mapping) {
+			memcpy(dai_data->port_config.pcm.slot_number_mapping,
+			       auxpcm_pdata->mode_16k.slot_mapping,
+			       slot_mapping_copy_len);
+		} else {
+			dev_err(dai->dev, "%s 16khz slot mapping is NULL\n",
+				__func__);
+			mutex_unlock(&aux_dai_data->rlock);
+			return -EINVAL;
+		}
+	}
+
+	dev_dbg(dai->dev, "%s: aux_mode 0x%x sync_src 0x%x frame_setting 0x%x\n",
+		__func__, dai_data->port_config.pcm.aux_mode,
+		dai_data->port_config.pcm.sync_src,
+		dai_data->port_config.pcm.frame_setting);
+	dev_dbg(dai->dev, "%s: qtype 0x%x dout 0x%x num_map[0] 0x%x\n"
+		"num_map[1] 0x%x num_map[2] 0x%x num_map[3] 0x%x\n",
+		__func__, dai_data->port_config.pcm.quantype,
+		dai_data->port_config.pcm.ctrl_data_out_enable,
+		dai_data->port_config.pcm.slot_number_mapping[0],
+		dai_data->port_config.pcm.slot_number_mapping[1],
+		dai_data->port_config.pcm.slot_number_mapping[2],
+		dai_data->port_config.pcm.slot_number_mapping[3]);
+
+	mutex_unlock(&aux_dai_data->rlock);
+	return rc;
+}
+
+static int msm_dai_q6_auxpcm_set_clk(
+		struct msm_dai_q6_auxpcm_dai_data *aux_dai_data,
+		u16 port_id, bool enable)
+{
+	int rc;
+
+	pr_debug("%s: afe_clk_ver: %d, port_id: %d, enable: %d\n", __func__,
+		 aux_dai_data->afe_clk_ver, port_id, enable);
+	if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) {
+		aux_dai_data->clk_set.enable = enable;
+		rc = afe_set_lpass_clock_v2(port_id,
+					&aux_dai_data->clk_set);
+	} else {
+		if (!enable)
+			aux_dai_data->clk_cfg.clk_val1 = 0;
+		rc = afe_set_lpass_clock(port_id,
+					&aux_dai_data->clk_cfg);
+	}
+	return rc;
+}
+
+static void msm_dai_q6_auxpcm_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	int rc = 0;
+	struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+		dev_get_drvdata(dai->dev);
+
+	mutex_lock(&aux_dai_data->rlock);
+
+	if (!(test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+	      test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))) {
+		dev_dbg(dai->dev, "%s(): dai->id %d PCM ports already closed\n",
+				__func__, dai->id);
+		goto exit;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status))
+			clear_bit(STATUS_TX_PORT,
+				  aux_dai_data->auxpcm_port_status);
+		else {
+			dev_dbg(dai->dev, "%s: PCM_TX port already closed\n",
+				__func__);
+			goto exit;
+		}
+	} else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status))
+			clear_bit(STATUS_RX_PORT,
+				  aux_dai_data->auxpcm_port_status);
+		else {
+			dev_dbg(dai->dev, "%s: PCM_RX port already closed\n",
+				__func__);
+			goto exit;
+		}
+	}
+	if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+	    test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+		dev_dbg(dai->dev, "%s: cannot shutdown PCM ports\n",
+			__func__);
+		goto exit;
+	}
+
+	dev_dbg(dai->dev, "%s: dai->id = %d closing PCM AFE ports\n",
+			__func__, dai->id);
+
+	rc = afe_close(aux_dai_data->rx_pid); /* can block */
+	if (rc < 0)
+		dev_err(dai->dev, "fail to close PCM_RX  AFE port\n");
+
+	rc = afe_close(aux_dai_data->tx_pid);
+	if (rc < 0)
+		dev_err(dai->dev, "fail to close AUX PCM TX port\n");
+
+	msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false);
+	msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false);
+exit:
+	mutex_unlock(&aux_dai_data->rlock);
+}
+
+static int msm_dai_q6_auxpcm_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_auxpcm_dai_data *aux_dai_data =
+		dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data = &aux_dai_data->bdai_data;
+	struct msm_dai_auxpcm_pdata *auxpcm_pdata = NULL;
+	int rc = 0;
+	u32 pcm_clk_rate;
+
+	auxpcm_pdata = dai->dev->platform_data;
+	mutex_lock(&aux_dai_data->rlock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (test_bit(STATUS_TX_PORT,
+				aux_dai_data->auxpcm_port_status)) {
+			dev_dbg(dai->dev, "%s: PCM_TX port already ON\n",
+				__func__);
+			goto exit;
+		} else
+			set_bit(STATUS_TX_PORT,
+				  aux_dai_data->auxpcm_port_status);
+	} else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (test_bit(STATUS_RX_PORT,
+				aux_dai_data->auxpcm_port_status)) {
+			dev_dbg(dai->dev, "%s: PCM_RX port already ON\n",
+				__func__);
+			goto exit;
+		} else
+			set_bit(STATUS_RX_PORT,
+				  aux_dai_data->auxpcm_port_status);
+	}
+	if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) &&
+	    test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+		dev_dbg(dai->dev, "%s: PCM ports already set\n", __func__);
+		goto exit;
+	}
+
+	dev_dbg(dai->dev, "%s: dai->id:%d  opening afe ports\n",
+			__func__, dai->id);
+
+	rc = afe_q6_interface_prepare();
+	if (rc < 0) {
+		dev_err(dai->dev, "fail to open AFE APR\n");
+		goto fail;
+	}
+
+	/*
+	 * For AUX PCM Interface the below sequence of clk
+	 * settings and afe_open is a strict requirement.
+	 *
+	 * Also using afe_open instead of afe_port_start_nowait
+	 * to make sure the port is open before deasserting the
+	 * clock line. This is required because pcm register is
+	 * not written before clock deassert. Hence the hw does
+	 * not get updated with new setting if the below clock
+	 * assert/deasset and afe_open sequence is not followed.
+	 */
+
+	if (dai_data->rate == 8000) {
+		pcm_clk_rate = auxpcm_pdata->mode_8k.pcm_clk_rate;
+	} else if (dai_data->rate == 16000) {
+		pcm_clk_rate = (auxpcm_pdata->mode_16k.pcm_clk_rate);
+	} else {
+		dev_err(dai->dev, "%s: Invalid AUX PCM rate %d\n", __func__,
+			dai_data->rate);
+		rc = -EINVAL;
+		goto fail;
+	}
+	if (aux_dai_data->afe_clk_ver == AFE_CLK_VERSION_V2) {
+		memcpy(&aux_dai_data->clk_set, &lpass_clk_set_default,
+				sizeof(struct afe_clk_set));
+		aux_dai_data->clk_set.clk_freq_in_hz = pcm_clk_rate;
+
+		switch (dai->id) {
+		case MSM_DAI_PRI_AUXPCM_DT_DEV_ID:
+			if (pcm_clk_rate)
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_PRI_PCM_IBIT;
+			else
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_PRI_PCM_EBIT;
+			break;
+		case MSM_DAI_SEC_AUXPCM_DT_DEV_ID:
+			if (pcm_clk_rate)
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_SEC_PCM_IBIT;
+			else
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_SEC_PCM_EBIT;
+			break;
+		case MSM_DAI_TERT_AUXPCM_DT_DEV_ID:
+			if (pcm_clk_rate)
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_TER_PCM_IBIT;
+			else
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_TER_PCM_EBIT;
+			break;
+		case MSM_DAI_QUAT_AUXPCM_DT_DEV_ID:
+			if (pcm_clk_rate)
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_QUAD_PCM_IBIT;
+			else
+				aux_dai_data->clk_set.clk_id =
+					Q6AFE_LPASS_CLK_ID_QUAD_PCM_EBIT;
+			break;
+		default:
+			dev_err(dai->dev, "%s: AUXPCM id: %d not supported\n",
+				__func__, dai->id);
+			break;
+		}
+	} else {
+		memcpy(&aux_dai_data->clk_cfg, &lpass_clk_cfg_default,
+				sizeof(struct afe_clk_cfg));
+		aux_dai_data->clk_cfg.clk_val1 = pcm_clk_rate;
+	}
+
+	rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data,
+				       aux_dai_data->rx_pid, true);
+	if (rc < 0) {
+		dev_err(dai->dev,
+			"%s:afe_set_lpass_clock on RX pcm_src_clk failed\n",
+			__func__);
+		goto fail;
+	}
+
+	rc = msm_dai_q6_auxpcm_set_clk(aux_dai_data,
+				       aux_dai_data->tx_pid, true);
+	if (rc < 0) {
+		dev_err(dai->dev,
+			"%s:afe_set_lpass_clock on TX pcm_src_clk failed\n",
+			__func__);
+		goto fail;
+	}
+
+	afe_open(aux_dai_data->rx_pid, &dai_data->port_config, dai_data->rate);
+	afe_open(aux_dai_data->tx_pid, &dai_data->port_config, dai_data->rate);
+	goto exit;
+
+fail:
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status);
+	else if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status);
+
+exit:
+	mutex_unlock(&aux_dai_data->rlock);
+	return rc;
+}
+
+static int msm_dai_q6_auxpcm_trigger(struct snd_pcm_substream *substream,
+		int cmd, struct snd_soc_dai *dai)
+{
+	int rc = 0;
+
+	pr_debug("%s:port:%d  cmd:%d\n",
+		__func__, dai->id, cmd);
+
+	switch (cmd) {
+
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		/* afe_open will be called from prepare */
+		return 0;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		return 0;
+
+	default:
+		pr_err("%s: cmd %d\n", __func__, cmd);
+		rc = -EINVAL;
+	}
+
+	return rc;
+
+}
+
+static int msm_dai_q6_dai_auxpcm_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_auxpcm_dai_data *aux_dai_data;
+	int rc;
+
+	aux_dai_data = dev_get_drvdata(dai->dev);
+
+	dev_dbg(dai->dev, "%s: dai->id %d closing afe\n",
+		__func__, dai->id);
+
+	if (test_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status) ||
+	    test_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status)) {
+		rc = afe_close(aux_dai_data->rx_pid); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AUXPCM RX AFE port\n");
+		rc = afe_close(aux_dai_data->tx_pid);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AUXPCM TX AFE port\n");
+		clear_bit(STATUS_TX_PORT, aux_dai_data->auxpcm_port_status);
+		clear_bit(STATUS_RX_PORT, aux_dai_data->auxpcm_port_status);
+	}
+	msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->rx_pid, false);
+	msm_dai_q6_auxpcm_set_clk(aux_dai_data, aux_dai_data->tx_pid, false);
+	return 0;
+}
+
+static int msm_dai_q6_aux_pcm_probe(struct snd_soc_dai *dai)
+{
+	int rc = 0;
+
+	if (!dai) {
+		pr_err("%s: Invalid params dai\n", __func__);
+		return -EINVAL;
+	}
+	if (!dai->dev) {
+		pr_err("%s: Invalid params dai dev\n", __func__);
+		return -EINVAL;
+	}
+	if (!dai->driver->id) {
+		dev_warn(dai->dev, "DAI driver id is not set\n");
+		return -EINVAL;
+	}
+	dai->id = dai->driver->id;
+	rc = msm_dai_q6_dai_add_route(dai);
+	return rc;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_auxpcm_ops = {
+	.prepare	= msm_dai_q6_auxpcm_prepare,
+	.trigger	= msm_dai_q6_auxpcm_trigger,
+	.hw_params	= msm_dai_q6_auxpcm_hw_params,
+	.shutdown	= msm_dai_q6_auxpcm_shutdown,
+};
+
+static const struct snd_soc_component_driver
+	msm_dai_q6_aux_pcm_dai_component = {
+	.name		= "msm-auxpcm-dev",
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_aux_pcm_dai[] = {
+	{
+		.playback = {
+			.stream_name = "AUX PCM Playback",
+			.aif_name = "AUX_PCM_RX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.capture = {
+			.stream_name = "AUX PCM Capture",
+			.aif_name = "AUX_PCM_TX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID,
+		.ops = &msm_dai_q6_auxpcm_ops,
+		.probe = msm_dai_q6_aux_pcm_probe,
+		.remove = msm_dai_q6_dai_auxpcm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Sec AUX PCM Playback",
+			.aif_name = "SEC_AUX_PCM_RX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.capture = {
+			.stream_name = "Sec AUX PCM Capture",
+			.aif_name = "SEC_AUX_PCM_TX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID,
+		.ops = &msm_dai_q6_auxpcm_ops,
+		.probe = msm_dai_q6_aux_pcm_probe,
+		.remove = msm_dai_q6_dai_auxpcm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tert AUX PCM Playback",
+			.aif_name = "TERT_AUX_PCM_RX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.capture = {
+			.stream_name = "Tert AUX PCM Capture",
+			.aif_name = "TERT_AUX_PCM_TX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID,
+		.ops = &msm_dai_q6_auxpcm_ops,
+		.probe = msm_dai_q6_aux_pcm_probe,
+		.remove = msm_dai_q6_dai_auxpcm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quat AUX PCM Playback",
+			.aif_name = "QUAT_AUX_PCM_RX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.capture = {
+			.stream_name = "Quat AUX PCM Capture",
+			.aif_name = "QUAT_AUX_PCM_TX",
+			.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000),
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 1,
+			.rate_max = 16000,
+			.rate_min = 8000,
+		},
+		.id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID,
+		.ops = &msm_dai_q6_auxpcm_ops,
+		.probe = msm_dai_q6_aux_pcm_probe,
+		.remove = msm_dai_q6_dai_auxpcm_remove,
+	},
+};
+
+static int msm_dai_q6_spdif_format_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	dai_data->spdif_port.cfg.data_format = value;
+	pr_debug("%s: value = %d\n", __func__, value);
+	return 0;
+}
+
+static int msm_dai_q6_spdif_format_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] =
+		dai_data->spdif_port.cfg.data_format;
+	return 0;
+}
+
+static const char * const spdif_format[] = {
+	"LPCM",
+	"Compr"
+};
+
+static const struct soc_enum spdif_config_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, spdif_format),
+};
+
+static int msm_dai_q6_spdif_chstatus_put(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+	int ret = 0;
+
+	dai_data->spdif_port.ch_status.status_type =
+		AFE_API_VERSION_SPDIF_CH_STATUS_CONFIG;
+	memset(dai_data->spdif_port.ch_status.status_mask,
+			CHANNEL_STATUS_MASK_INIT, CHANNEL_STATUS_SIZE);
+	dai_data->spdif_port.ch_status.status_mask[0] =
+		CHANNEL_STATUS_MASK;
+
+	memcpy(dai_data->spdif_port.ch_status.status_bits,
+			ucontrol->value.iec958.status, CHANNEL_STATUS_SIZE);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_debug("%s: Port already started. Dynamic update\n",
+				__func__);
+		ret = afe_send_spdif_ch_status_cfg(
+				&dai_data->spdif_port.ch_status,
+				AFE_PORT_ID_SPDIF_RX);
+	}
+	return ret;
+}
+
+static int msm_dai_q6_spdif_chstatus_get(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+
+	struct msm_dai_q6_spdif_dai_data *dai_data = kcontrol->private_data;
+
+	memcpy(ucontrol->value.iec958.status,
+			dai_data->spdif_port.ch_status.status_bits,
+			CHANNEL_STATUS_SIZE);
+	return 0;
+}
+
+static int msm_dai_q6_spdif_chstatus_info(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+	uinfo->count = 1;
+	return 0;
+}
+
+static const struct snd_kcontrol_new spdif_config_controls[] = {
+	{
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+				SNDRV_CTL_ELEM_ACCESS_INACTIVE),
+		.iface  =   SNDRV_CTL_ELEM_IFACE_PCM,
+		.name   =   SNDRV_CTL_NAME_IEC958("", PLAYBACK, PCM_STREAM),
+		.info   =   msm_dai_q6_spdif_chstatus_info,
+		.get    =   msm_dai_q6_spdif_chstatus_get,
+		.put    =   msm_dai_q6_spdif_chstatus_put,
+	},
+	SOC_ENUM_EXT("SPDIF RX Format", spdif_config_enum[0],
+			msm_dai_q6_spdif_format_get,
+			msm_dai_q6_spdif_format_put)
+};
+
+
+static int msm_dai_q6_spdif_hw_params(struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai->id = AFE_PORT_ID_SPDIF_RX;
+	dai_data->channels = params_channels(params);
+	dai_data->spdif_port.cfg.num_channels = dai_data->channels;
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_data->spdif_port.cfg.bit_width = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		dai_data->spdif_port.cfg.bit_width = 24;
+		break;
+	default:
+		pr_err("%s: format %d\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+
+	dai_data->rate = params_rate(params);
+	dai_data->bitwidth = dai_data->spdif_port.cfg.bit_width;
+	dai_data->spdif_port.cfg.sample_rate = dai_data->rate;
+	dai_data->spdif_port.cfg.spdif_cfg_minor_version =
+		AFE_API_VERSION_SPDIF_CONFIG;
+	dev_dbg(dai->dev, " channel %d sample rate %d bit width %d\n",
+			dai_data->channels, dai_data->rate,
+			dai_data->spdif_port.cfg.bit_width);
+	dai_data->spdif_port.cfg.reserved = 0;
+	return 0;
+}
+
+static void msm_dai_q6_spdif_shutdown(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_info("%s:  afe port not started. dai_data->status_mask = %ld\n",
+				__func__, *dai_data->status_mask);
+		return;
+	}
+
+	rc = afe_close(dai->id);
+	if (rc < 0)
+		dev_err(dai->dev, "fail to close AFE port\n");
+
+	pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+			*dai_data->status_mask);
+
+	clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+}
+
+
+static int msm_dai_q6_spdif_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_spdif_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_spdif_port_start(dai->id, &dai_data->spdif_port,
+				dai_data->rate);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+					dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+					dai_data->status_mask);
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_spdif_dai_data *dai_data;
+	const struct snd_kcontrol_new *kcontrol;
+	int rc = 0;
+	struct snd_soc_dapm_route intercon;
+	struct snd_soc_dapm_context *dapm;
+
+	if (!dai) {
+		pr_err("%s: dai not found!!\n", __func__);
+		return -EINVAL;
+	}
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_spdif_dai_data),
+			GFP_KERNEL);
+
+	if (!dai_data) {
+		dev_err(dai->dev, "DAI-%d: fail to allocate dai data\n",
+				AFE_PORT_ID_SPDIF_RX);
+		rc = -ENOMEM;
+	} else
+		dev_set_drvdata(dai->dev, dai_data);
+
+	kcontrol = &spdif_config_controls[1];
+	dapm = snd_soc_component_get_dapm(dai->component);
+
+	rc = snd_ctl_add(dai->component->card->snd_card,
+			snd_ctl_new1(kcontrol, dai_data));
+
+	memset(&intercon, 0, sizeof(intercon));
+	if (!rc && dai && dai->driver) {
+		if (dai->driver->playback.stream_name &&
+				dai->driver->playback.aif_name) {
+			dev_dbg(dai->dev, "%s: add route for widget %s",
+				__func__, dai->driver->playback.stream_name);
+			intercon.source = dai->driver->playback.aif_name;
+			intercon.sink = dai->driver->playback.stream_name;
+			dev_dbg(dai->dev, "%s: src %s sink %s\n",
+				__func__, intercon.source, intercon.sink);
+			snd_soc_dapm_add_routes(dapm, &intercon, 1);
+		}
+		if (dai->driver->capture.stream_name &&
+				dai->driver->capture.aif_name) {
+			dev_dbg(dai->dev, "%s: add route for widget %s",
+				__func__, dai->driver->capture.stream_name);
+			intercon.sink = dai->driver->capture.aif_name;
+			intercon.source = dai->driver->capture.stream_name;
+			dev_dbg(dai->dev, "%s: src %s sink %s\n",
+				__func__, intercon.source, intercon.sink);
+			snd_soc_dapm_add_routes(dapm, &intercon, 1);
+		}
+	}
+	return rc;
+}
+
+static int msm_dai_q6_spdif_dai_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_spdif_dai_data *dai_data;
+	int rc;
+
+	dai_data = dev_get_drvdata(dai->dev);
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(dai->id); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AFE port\n");
+
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+	kfree(dai_data);
+
+	return 0;
+}
+
+
+static struct snd_soc_dai_ops msm_dai_q6_spdif_ops = {
+	.prepare	= msm_dai_q6_spdif_prepare,
+	.hw_params	= msm_dai_q6_spdif_hw_params,
+	.shutdown	= msm_dai_q6_spdif_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_spdif_spdif_rx_dai = {
+	.playback = {
+		.stream_name = "SPDIF Playback",
+		.aif_name = "SPDIF_RX",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+		.channels_min = 1,
+		.channels_max = 4,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &msm_dai_q6_spdif_ops,
+	.probe = msm_dai_q6_spdif_dai_probe,
+	.remove = msm_dai_q6_spdif_dai_remove,
+};
+
+static const struct snd_soc_component_driver msm_dai_spdif_q6_component = {
+	.name		= "msm-dai-q6-spdif",
+};
+
+static int msm_dai_q6_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		if (dai_data->enc_config.format != ENC_FMT_NONE) {
+			int bitwidth = 0;
+
+			if (dai_data->afe_in_bitformat ==
+			    SNDRV_PCM_FORMAT_S24_LE)
+				bitwidth = 24;
+			else if (dai_data->afe_in_bitformat ==
+				 SNDRV_PCM_FORMAT_S16_LE)
+				bitwidth = 16;
+			pr_debug("%s: calling AFE_PORT_START_V2 with enc_format: %d\n",
+				 __func__, dai_data->enc_config.format);
+			rc = afe_port_start_v2(dai->id, &dai_data->port_config,
+					       dai_data->rate,
+					       dai_data->afe_in_channels,
+					       bitwidth,
+					       &dai_data->enc_config);
+			if (rc < 0)
+				pr_err("%s: afe_port_start_v2 failed error: %d\n",
+					__func__, rc);
+		} else {
+			rc = afe_port_start(dai->id, &dai_data->port_config,
+						dai_data->rate);
+		}
+		if (rc < 0)
+			dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+				dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+	}
+	return rc;
+}
+
+static int msm_dai_q6_cdc_hw_params(struct snd_pcm_hw_params *params,
+				    struct snd_soc_dai *dai, int stream)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->channels = params_channels(params);
+	switch (dai_data->channels) {
+	case 2:
+		dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+		break;
+	case 1:
+		dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+		break;
+	default:
+		return -EINVAL;
+		pr_err("%s: err channels %d\n",
+			__func__, dai_data->channels);
+		break;
+	}
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		dai_data->port_config.i2s.bit_width = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		dai_data->port_config.i2s.bit_width = 24;
+		break;
+	default:
+		pr_err("%s: format %d\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.i2s.sample_rate = dai_data->rate;
+	dai_data->port_config.i2s.i2s_cfg_minor_version =
+						AFE_API_VERSION_I2S_CONFIG;
+	dai_data->port_config.i2s.data_format =  AFE_LINEAR_PCM_DATA;
+	dev_dbg(dai->dev, " channel %d sample rate %d entered\n",
+	dai_data->channels, dai_data->rate);
+
+	dai_data->port_config.i2s.channel_mode = 1;
+	return 0;
+}
+
+static u8 num_of_bits_set(u8 sd_line_mask)
+{
+	u8 num_bits_set = 0;
+
+	while (sd_line_mask) {
+		num_bits_set++;
+		sd_line_mask = sd_line_mask & (sd_line_mask - 1);
+	}
+	return num_bits_set;
+}
+
+static int msm_dai_q6_i2s_hw_params(struct snd_pcm_hw_params *params,
+				    struct snd_soc_dai *dai, int stream)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	struct msm_i2s_data *i2s_pdata =
+			(struct msm_i2s_data *) dai->dev->platform_data;
+
+	dai_data->channels = params_channels(params);
+	if (num_of_bits_set(i2s_pdata->sd_lines) == 1) {
+		switch (dai_data->channels) {
+		case 2:
+			dai_data->port_config.i2s.mono_stereo = MSM_AFE_STEREO;
+			break;
+		case 1:
+			dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+			break;
+		default:
+			pr_warn("%s: greater than stereo has not been validated %d",
+				__func__, dai_data->channels);
+			break;
+		}
+	}
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.i2s.sample_rate = dai_data->rate;
+	dai_data->port_config.i2s.i2s_cfg_minor_version =
+						AFE_API_VERSION_I2S_CONFIG;
+	dai_data->port_config.i2s.data_format =  AFE_LINEAR_PCM_DATA;
+	/* Q6 only supports 16 as now */
+	dai_data->port_config.i2s.bit_width = 16;
+	dai_data->port_config.i2s.channel_mode = 1;
+
+	return 0;
+}
+
+static int msm_dai_q6_slim_bus_hw_params(struct snd_pcm_hw_params *params,
+				    struct snd_soc_dai *dai, int stream)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		dai_data->port_config.slim_sch.bit_width = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		dai_data->port_config.slim_sch.bit_width = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dai_data->port_config.slim_sch.bit_width = 32;
+		break;
+	default:
+		pr_err("%s: format %d\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+
+	dai_data->port_config.slim_sch.sb_cfg_minor_version =
+				AFE_API_VERSION_SLIMBUS_CONFIG;
+	dai_data->port_config.slim_sch.sample_rate = dai_data->rate;
+	dai_data->port_config.slim_sch.num_channels = dai_data->channels;
+
+	switch (dai->id) {
+	case SLIMBUS_7_RX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_8_TX:
+		dai_data->port_config.slim_sch.slimbus_dev_id =
+			AFE_SLIMBUS_DEVICE_2;
+		break;
+	default:
+		dai_data->port_config.slim_sch.slimbus_dev_id =
+			AFE_SLIMBUS_DEVICE_1;
+		break;
+	}
+
+	dev_dbg(dai->dev, "%s:slimbus_dev_id[%hu] bit_wd[%hu] format[%hu]\n"
+		"num_channel %hu  shared_ch_mapping[0]  %hu\n"
+		"slave_port_mapping[1]  %hu slave_port_mapping[2]  %hu\n"
+		"sample_rate %d\n", __func__,
+		dai_data->port_config.slim_sch.slimbus_dev_id,
+		dai_data->port_config.slim_sch.bit_width,
+		dai_data->port_config.slim_sch.data_format,
+		dai_data->port_config.slim_sch.num_channels,
+		dai_data->port_config.slim_sch.shared_ch_mapping[0],
+		dai_data->port_config.slim_sch.shared_ch_mapping[1],
+		dai_data->port_config.slim_sch.shared_ch_mapping[2],
+		dai_data->rate);
+
+	return 0;
+}
+
+static int msm_dai_q6_usb_audio_hw_params(struct snd_pcm_hw_params *params,
+					  struct snd_soc_dai *dai, int stream)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		dai_data->port_config.usb_audio.bit_width = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		dai_data->port_config.usb_audio.bit_width = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dai_data->port_config.usb_audio.bit_width = 32;
+		break;
+
+	default:
+		dev_err(dai->dev, "%s: invalid format %d\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+	dai_data->port_config.usb_audio.cfg_minor_version =
+					AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG;
+	dai_data->port_config.usb_audio.num_channels = dai_data->channels;
+	dai_data->port_config.usb_audio.sample_rate = dai_data->rate;
+
+	dev_dbg(dai->dev, "%s: dev_id[0x%x] bit_wd[%hu] format[%hu]\n"
+		"num_channel %hu  sample_rate %d\n", __func__,
+		dai_data->port_config.usb_audio.dev_token,
+		dai_data->port_config.usb_audio.bit_width,
+		dai_data->port_config.usb_audio.data_format,
+		dai_data->port_config.usb_audio.num_channels,
+		dai_data->port_config.usb_audio.sample_rate);
+
+	return 0;
+}
+
+static int msm_dai_q6_bt_fm_hw_params(struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai, int stream)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+
+	dev_dbg(dai->dev, "channels %d sample rate %d entered\n",
+		dai_data->channels, dai_data->rate);
+
+	memset(&dai_data->port_config, 0, sizeof(dai_data->port_config));
+
+	pr_debug("%s: setting bt_fm parameters\n", __func__);
+
+	dai_data->port_config.int_bt_fm.bt_fm_cfg_minor_version =
+					AFE_API_VERSION_INTERNAL_BT_FM_CONFIG;
+	dai_data->port_config.int_bt_fm.num_channels = dai_data->channels;
+	dai_data->port_config.int_bt_fm.sample_rate = dai_data->rate;
+	dai_data->port_config.int_bt_fm.bit_width = 16;
+
+	return 0;
+}
+
+static int msm_dai_q6_afe_rtproxy_hw_params(struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->rate = params_rate(params);
+	dai_data->port_config.rtproxy.num_channels = params_channels(params);
+	dai_data->port_config.rtproxy.sample_rate = params_rate(params);
+
+	pr_debug("channel %d entered,dai_id: %d,rate: %d\n",
+	dai_data->port_config.rtproxy.num_channels, dai->id, dai_data->rate);
+
+	dai_data->port_config.rtproxy.rt_proxy_cfg_minor_version =
+				AFE_API_VERSION_RT_PROXY_CONFIG;
+	dai_data->port_config.rtproxy.bit_width = 16; /* Q6 only supports 16 */
+	dai_data->port_config.rtproxy.interleaved = 1;
+	dai_data->port_config.rtproxy.frame_size = params_period_bytes(params);
+	dai_data->port_config.rtproxy.jitter_allowance =
+				dai_data->port_config.rtproxy.frame_size/2;
+	dai_data->port_config.rtproxy.low_water_mark = 0;
+	dai_data->port_config.rtproxy.high_water_mark = 0;
+
+	return 0;
+}
+
+static int msm_dai_q6_pseudo_port_hw_params(struct snd_pcm_hw_params *params,
+				    struct snd_soc_dai *dai, int stream)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+
+	/* Q6 only supports 16 as now */
+	dai_data->port_config.pseudo_port.pseud_port_cfg_minor_version =
+				AFE_API_VERSION_PSEUDO_PORT_CONFIG;
+	dai_data->port_config.pseudo_port.num_channels =
+				params_channels(params);
+	dai_data->port_config.pseudo_port.bit_width = 16;
+	dai_data->port_config.pseudo_port.data_format = 0;
+	dai_data->port_config.pseudo_port.timing_mode =
+				AFE_PSEUDOPORT_TIMING_MODE_TIMER;
+	dai_data->port_config.pseudo_port.sample_rate = params_rate(params);
+
+	dev_dbg(dai->dev, "%s: bit_wd[%hu] num_channels [%hu] format[%hu]\n"
+		"timing Mode %hu sample_rate %d\n", __func__,
+		dai_data->port_config.pseudo_port.bit_width,
+		dai_data->port_config.pseudo_port.num_channels,
+		dai_data->port_config.pseudo_port.data_format,
+		dai_data->port_config.pseudo_port.timing_mode,
+		dai_data->port_config.pseudo_port.sample_rate);
+
+	return 0;
+}
+
+/* Current implementation assumes hw_param is called once
+ * This may not be the case but what to do when ADM and AFE
+ * port are already opened and parameter changes
+ */
+static int msm_dai_q6_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	int rc = 0;
+
+	switch (dai->id) {
+	case PRIMARY_I2S_TX:
+	case PRIMARY_I2S_RX:
+	case SECONDARY_I2S_RX:
+		rc = msm_dai_q6_cdc_hw_params(params, dai, substream->stream);
+		break;
+	case MI2S_RX:
+		rc = msm_dai_q6_i2s_hw_params(params, dai, substream->stream);
+		break;
+	case SLIMBUS_0_RX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_TX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_TX:
+		rc = msm_dai_q6_slim_bus_hw_params(params, dai,
+				substream->stream);
+		break;
+	case INT_BT_SCO_RX:
+	case INT_BT_SCO_TX:
+	case INT_BT_A2DP_RX:
+	case INT_FM_RX:
+	case INT_FM_TX:
+		rc = msm_dai_q6_bt_fm_hw_params(params, dai, substream->stream);
+		break;
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_USB_TX:
+		rc = msm_dai_q6_usb_audio_hw_params(params, dai,
+						    substream->stream);
+		break;
+	case RT_PROXY_DAI_001_TX:
+	case RT_PROXY_DAI_001_RX:
+	case RT_PROXY_DAI_002_TX:
+	case RT_PROXY_DAI_002_RX:
+		rc = msm_dai_q6_afe_rtproxy_hw_params(params, dai);
+		break;
+	case VOICE_PLAYBACK_TX:
+	case VOICE2_PLAYBACK_TX:
+	case VOICE_RECORD_RX:
+	case VOICE_RECORD_TX:
+		rc = msm_dai_q6_pseudo_port_hw_params(params,
+						dai, substream->stream);
+		break;
+	default:
+		dev_err(dai->dev, "invalid AFE port ID 0x%x\n", dai->id);
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static void msm_dai_q6_shutdown(struct snd_pcm_substream *substream,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	int rc = 0;
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_debug("%s: stop pseudo port:%d\n", __func__,  dai->id);
+		rc = afe_close(dai->id); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AFE port\n");
+		pr_debug("%s: dai_data->status_mask = %ld\n", __func__,
+			*dai_data->status_mask);
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+}
+
+static int msm_dai_q6_cdc_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		dai_data->port_config.i2s.ws_src = 1; /* CPU is master */
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		dai_data->port_config.i2s.ws_src = 0; /* CPU is slave */
+		break;
+	default:
+		pr_err("%s: fmt 0x%x\n",
+			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	int rc = 0;
+
+	dev_dbg(dai->dev, "%s: id = %d fmt[%d]\n", __func__,
+							dai->id, fmt);
+	switch (dai->id) {
+	case PRIMARY_I2S_TX:
+	case PRIMARY_I2S_RX:
+	case MI2S_RX:
+	case SECONDARY_I2S_RX:
+		rc = msm_dai_q6_cdc_set_fmt(dai, fmt);
+		break;
+	default:
+		dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id);
+		rc = -EINVAL;
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_set_channel_map(struct snd_soc_dai *dai,
+				unsigned int tx_num, unsigned int *tx_slot,
+				unsigned int rx_num, unsigned int *rx_slot)
+
+{
+	int rc = 0;
+	struct msm_dai_q6_dai_data *dai_data = dev_get_drvdata(dai->dev);
+	unsigned int i = 0;
+
+	dev_dbg(dai->dev, "%s: id = %d\n", __func__, dai->id);
+	switch (dai->id) {
+	case SLIMBUS_0_RX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_8_RX:
+		/*
+		 * channel number to be between 128 and 255.
+		 * For RX port use channel numbers
+		 * from 138 to 144 for pre-Taiko
+		 * from 144 to 159 for Taiko
+		 */
+		if (!rx_slot) {
+			pr_err("%s: rx slot not found\n", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < rx_num; i++) {
+			dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+			    rx_slot[i];
+			pr_debug("%s: find number of channels[%d] ch[%d]\n",
+			       __func__, i, rx_slot[i]);
+		}
+		dai_data->port_config.slim_sch.num_channels = rx_num;
+		pr_debug("%s: SLIMBUS_%d_RX cnt[%d] ch[%d %d]\n", __func__,
+			(dai->id - SLIMBUS_0_RX) / 2, rx_num,
+			dai_data->port_config.slim_sch.shared_ch_mapping[0],
+			dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+
+		break;
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_TX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_TX:
+		/*
+		 * channel number to be between 128 and 255.
+		 * For TX port use channel numbers
+		 * from 128 to 137 for pre-Taiko
+		 * from 128 to 143 for Taiko
+		 */
+		if (!tx_slot) {
+			pr_err("%s: tx slot not found\n", __func__);
+			return -EINVAL;
+		}
+		for (i = 0; i < tx_num; i++) {
+			dai_data->port_config.slim_sch.shared_ch_mapping[i] =
+			    tx_slot[i];
+			pr_debug("%s: find number of channels[%d] ch[%d]\n",
+				 __func__, i, tx_slot[i]);
+		}
+		dai_data->port_config.slim_sch.num_channels = tx_num;
+		pr_debug("%s:SLIMBUS_%d_TX cnt[%d] ch[%d %d]\n", __func__,
+			(dai->id - SLIMBUS_0_TX) / 2, tx_num,
+			dai_data->port_config.slim_sch.shared_ch_mapping[0],
+			dai_data->port_config.slim_sch.shared_ch_mapping[1]);
+		break;
+	default:
+		dev_err(dai->dev, "invalid cpu_dai id 0x%x\n", dai->id);
+		rc = -EINVAL;
+		break;
+	}
+	return rc;
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_ops = {
+	.prepare	= msm_dai_q6_prepare,
+	.hw_params	= msm_dai_q6_hw_params,
+	.shutdown	= msm_dai_q6_shutdown,
+	.set_fmt	= msm_dai_q6_set_fmt,
+	.set_channel_map = msm_dai_q6_set_channel_map,
+};
+
+/*
+ * For single CPU DAI registration, the dai id needs to be
+ * set explicitly in the dai probe as ASoC does not read
+ * the cpu->driver->id field rather it assigns the dai id
+ * from the device name that is in the form %s.%d. This dai
+ * id should be assigned to back-end AFE port id and used
+ * during dai prepare. For multiple dai registration, it
+ * is not required to call this function, however the dai->
+ * driver->id field must be defined and set to corresponding
+ * AFE Port id.
+ */
+static inline void msm_dai_q6_set_dai_id(struct snd_soc_dai *dai)
+{
+	if (!dai->driver->id) {
+		dev_warn(dai->dev, "DAI driver id is not set\n");
+		return;
+	}
+	dai->id = dai->driver->id;
+}
+
+static int msm_dai_q6_cal_info_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	u16 port_id = ((struct soc_enum *)
+					kcontrol->private_value)->reg;
+
+	dai_data->cal_mode = ucontrol->value.integer.value[0];
+	pr_debug("%s: setting cal_mode to %d\n",
+		__func__, dai_data->cal_mode);
+	afe_set_cal_mode(port_id, dai_data->cal_mode);
+
+	return 0;
+}
+
+static int msm_dai_q6_cal_info_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] = dai_data->cal_mode;
+	return 0;
+}
+
+static int msm_dai_q6_sb_format_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	if (dai_data) {
+		dai_data->port_config.slim_sch.data_format = value;
+		pr_debug("%s: format = %d\n",  __func__, value);
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_sb_format_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (dai_data)
+		ucontrol->value.integer.value[0] =
+			dai_data->port_config.slim_sch.data_format;
+
+	return 0;
+}
+
+static int msm_dai_q6_usb_audio_cfg_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	u32 val = ucontrol->value.integer.value[0];
+
+	if (dai_data) {
+		dai_data->port_config.usb_audio.dev_token = val;
+		pr_debug("%s: dev_token = 0x%x\n",  __func__,
+				 dai_data->port_config.usb_audio.dev_token);
+	} else {
+		pr_err("%s: dai_data is NULL\n", __func__);
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_usb_audio_cfg_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (dai_data) {
+		ucontrol->value.integer.value[0] =
+			 dai_data->port_config.usb_audio.dev_token;
+		pr_debug("%s: dev_token = 0x%x\n",  __func__,
+				 dai_data->port_config.usb_audio.dev_token);
+	} else {
+		pr_err("%s: dai_data is NULL\n", __func__);
+	}
+
+	return 0;
+}
+
+static int  msm_dai_q6_afe_enc_cfg_info(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = sizeof(struct afe_enc_config);
+
+	return 0;
+}
+
+static int msm_dai_q6_afe_enc_cfg_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (dai_data) {
+		int format_size = sizeof(dai_data->enc_config.format);
+
+		pr_debug("%s:encoder config for %d format\n",
+			 __func__, dai_data->enc_config.format);
+		memcpy(ucontrol->value.bytes.data,
+			&dai_data->enc_config.format,
+			format_size);
+		switch (dai_data->enc_config.format) {
+		case ENC_FMT_SBC:
+			memcpy(ucontrol->value.bytes.data + format_size,
+				&dai_data->enc_config.data,
+				sizeof(struct asm_sbc_enc_cfg_t));
+			break;
+		case ENC_FMT_AAC_V2:
+			memcpy(ucontrol->value.bytes.data + format_size,
+				&dai_data->enc_config.data,
+				sizeof(struct asm_aac_enc_cfg_v2_t));
+			break;
+		case ENC_FMT_APTX:
+		case ENC_FMT_APTX_HD:
+			memcpy(ucontrol->value.bytes.data + format_size,
+				&dai_data->enc_config.data,
+				sizeof(struct asm_aac_enc_cfg_v2_t));
+			break;
+		default:
+			pr_debug("%s: unknown format = %d\n",
+				 __func__, dai_data->enc_config.format);
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+static int msm_dai_q6_afe_enc_cfg_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (dai_data) {
+		int format_size = sizeof(dai_data->enc_config.format);
+
+		memset(&dai_data->enc_config, 0x0,
+			sizeof(struct afe_enc_config));
+		memcpy(&dai_data->enc_config.format,
+			ucontrol->value.bytes.data,
+			format_size);
+		pr_debug("%s: Received encoder config for %d format\n",
+			 __func__, dai_data->enc_config.format);
+		switch (dai_data->enc_config.format) {
+		case ENC_FMT_SBC:
+			memcpy(&dai_data->enc_config.data,
+				ucontrol->value.bytes.data + format_size,
+				sizeof(struct asm_sbc_enc_cfg_t));
+			break;
+		case ENC_FMT_AAC_V2:
+			memcpy(&dai_data->enc_config.data,
+				ucontrol->value.bytes.data + format_size,
+				sizeof(struct asm_aac_enc_cfg_v2_t));
+			break;
+		case ENC_FMT_APTX:
+		case ENC_FMT_APTX_HD:
+			memcpy(&dai_data->enc_config.data,
+				ucontrol->value.bytes.data + format_size,
+				sizeof(struct asm_custom_enc_cfg_aptx_t));
+			break;
+		default:
+			pr_debug("%s: Ignore enc config for unknown format = %d\n",
+				 __func__, dai_data->enc_config.format);
+			ret = -EINVAL;
+			break;
+		}
+	} else
+		ret = -EINVAL;
+
+	return ret;
+}
+
+static const char *const afe_input_chs_text[] = {"Zero", "One", "Two"};
+
+static const struct soc_enum afe_input_chs_enum[] = {
+	SOC_ENUM_SINGLE_EXT(3, afe_input_chs_text),
+};
+
+static const char *const afe_input_bit_format_text[] = {"S16_LE", "S24_LE"};
+
+static const struct soc_enum afe_input_bit_format_enum[] = {
+	SOC_ENUM_SINGLE_EXT(2, afe_input_bit_format_text),
+};
+
+static int msm_dai_q6_afe_input_channel_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (dai_data) {
+		ucontrol->value.integer.value[0] = dai_data->afe_in_channels;
+		pr_debug("%s:afe input channel = %d\n",
+			  __func__, dai_data->afe_in_channels);
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_afe_input_channel_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (dai_data) {
+		dai_data->afe_in_channels = ucontrol->value.integer.value[0];
+		pr_debug("%s: updating afe input channel : %d\n",
+			__func__, dai_data->afe_in_channels);
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_afe_input_bit_format_get(
+			struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (!dai_data) {
+		pr_err("%s: Invalid dai data\n", __func__);
+		return -EINVAL;
+	}
+
+	switch (dai_data->afe_in_bitformat) {
+	case SNDRV_PCM_FORMAT_S24_LE:
+		ucontrol->value.integer.value[0] = 1;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		ucontrol->value.integer.value[0] = 0;
+		break;
+	}
+	pr_debug("%s: afe input bit format : %ld\n",
+		  __func__, ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm_dai_q6_afe_input_bit_format_put(
+			struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	if (!dai_data) {
+		pr_err("%s: Invalid dai data\n", __func__);
+		return -EINVAL;
+	}
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S24_LE;
+		break;
+	case 0:
+	default:
+		dai_data->afe_in_bitformat = SNDRV_PCM_FORMAT_S16_LE;
+		break;
+	}
+	pr_debug("%s: updating afe input bit format : %d\n",
+		__func__, dai_data->afe_in_bitformat);
+
+	return 0;
+}
+
+
+static const struct snd_kcontrol_new afe_enc_config_controls[] = {
+	{
+		.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
+			   SNDRV_CTL_ELEM_ACCESS_INACTIVE),
+		.iface = SNDRV_CTL_ELEM_IFACE_PCM,
+		.name = "SLIM_7_RX Encoder Config",
+		.info = msm_dai_q6_afe_enc_cfg_info,
+		.get = msm_dai_q6_afe_enc_cfg_get,
+		.put = msm_dai_q6_afe_enc_cfg_put,
+	},
+	SOC_ENUM_EXT("AFE Input Channels", afe_input_chs_enum[0],
+		     msm_dai_q6_afe_input_channel_get,
+		     msm_dai_q6_afe_input_channel_put),
+	SOC_ENUM_EXT("AFE Input Bit Format", afe_input_bit_format_enum[0],
+		     msm_dai_q6_afe_input_bit_format_get,
+		     msm_dai_q6_afe_input_bit_format_put),
+};
+
+static const char * const afe_cal_mode_text[] = {
+	"CAL_MODE_DEFAULT", "CAL_MODE_NONE"
+};
+
+static const struct soc_enum slim_2_rx_enum =
+	SOC_ENUM_SINGLE(SLIMBUS_2_RX, 0, ARRAY_SIZE(afe_cal_mode_text),
+			afe_cal_mode_text);
+
+static const struct soc_enum rt_proxy_1_rx_enum =
+	SOC_ENUM_SINGLE(RT_PROXY_PORT_001_RX, 0, ARRAY_SIZE(afe_cal_mode_text),
+			afe_cal_mode_text);
+
+static const struct soc_enum rt_proxy_1_tx_enum =
+	SOC_ENUM_SINGLE(RT_PROXY_PORT_001_TX, 0, ARRAY_SIZE(afe_cal_mode_text),
+			afe_cal_mode_text);
+
+static const struct snd_kcontrol_new sb_config_controls[] = {
+	SOC_ENUM_EXT("SLIM_4_TX Format", sb_config_enum[0],
+		     msm_dai_q6_sb_format_get,
+		     msm_dai_q6_sb_format_put),
+	SOC_ENUM_EXT("SLIM_2_RX SetCalMode", slim_2_rx_enum,
+		     msm_dai_q6_cal_info_get,
+		     msm_dai_q6_cal_info_put),
+	SOC_ENUM_EXT("SLIM_2_RX Format", sb_config_enum[0],
+		     msm_dai_q6_sb_format_get,
+		     msm_dai_q6_sb_format_put)
+};
+
+static const struct snd_kcontrol_new rt_proxy_config_controls[] = {
+	SOC_ENUM_EXT("RT_PROXY_1_RX SetCalMode", rt_proxy_1_rx_enum,
+		     msm_dai_q6_cal_info_get,
+		     msm_dai_q6_cal_info_put),
+	SOC_ENUM_EXT("RT_PROXY_1_TX SetCalMode", rt_proxy_1_tx_enum,
+		     msm_dai_q6_cal_info_get,
+		     msm_dai_q6_cal_info_put),
+};
+
+static const struct snd_kcontrol_new usb_audio_cfg_controls[] = {
+	SOC_SINGLE_EXT("USB_AUDIO_RX dev_token", 0, 0, UINT_MAX, 0,
+			msm_dai_q6_usb_audio_cfg_get,
+			msm_dai_q6_usb_audio_cfg_put),
+	SOC_SINGLE_EXT("USB_AUDIO_TX dev_token", 0, 0, UINT_MAX, 0,
+			msm_dai_q6_usb_audio_cfg_get,
+			msm_dai_q6_usb_audio_cfg_put),
+};
+
+static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data;
+	int rc = 0;
+
+	if (!dai) {
+		pr_err("%s: Invalid params dai\n", __func__);
+		return -EINVAL;
+	}
+	if (!dai->dev) {
+		pr_err("%s: Invalid params dai dev\n", __func__);
+		return -EINVAL;
+	}
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_dai_data), GFP_KERNEL);
+
+	if (!dai_data)
+		rc = -ENOMEM;
+	else
+		dev_set_drvdata(dai->dev, dai_data);
+
+	msm_dai_q6_set_dai_id(dai);
+
+	switch (dai->id) {
+	case SLIMBUS_4_TX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&sb_config_controls[0],
+				 dai_data));
+		break;
+	case SLIMBUS_2_RX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&sb_config_controls[1],
+				 dai_data));
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&sb_config_controls[2],
+				 dai_data));
+		break;
+	case SLIMBUS_7_RX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&afe_enc_config_controls[0],
+				 dai_data));
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&afe_enc_config_controls[1],
+				 dai_data));
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&afe_enc_config_controls[2],
+				 dai_data));
+		break;
+	case RT_PROXY_DAI_001_RX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&rt_proxy_config_controls[0],
+				 dai_data));
+		break;
+	case RT_PROXY_DAI_001_TX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&rt_proxy_config_controls[1],
+				 dai_data));
+		break;
+	case AFE_PORT_ID_USB_RX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&usb_audio_cfg_controls[0],
+				 dai_data));
+		break;
+	case AFE_PORT_ID_USB_TX:
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 snd_ctl_new1(&usb_audio_cfg_controls[1],
+				 dai_data));
+		break;
+	}
+	if (rc < 0)
+		dev_err(dai->dev, "%s: err add config ctl, DAI = %s\n",
+			__func__, dai->name);
+
+	rc = msm_dai_q6_dai_add_route(dai);
+	return rc;
+}
+
+static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_dai_data *dai_data;
+	int rc;
+
+	dai_data = dev_get_drvdata(dai->dev);
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		pr_debug("%s: stop pseudo port:%d\n", __func__,  dai->id);
+		rc = afe_close(dai->id); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AFE port\n");
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+	kfree(dai_data);
+
+	return 0;
+}
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_rx_dai[] = {
+	{
+		.playback = {
+			.stream_name = "AFE Playback",
+			.aif_name = "PCM_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+			SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = RT_PROXY_DAI_001_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			 .stream_name = "AFE-PROXY RX",
+			 .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			 SNDRV_PCM_RATE_16000,
+			 .formats = SNDRV_PCM_FMTBIT_S16_LE |
+			 SNDRV_PCM_FMTBIT_S24_LE,
+			 .channels_min = 1,
+			 .channels_max = 2,
+			 .rate_min =     8000,
+			 .rate_max =	48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = RT_PROXY_DAI_002_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_afe_tx_dai[] = {
+	{
+		.capture = {
+			.stream_name = "AFE Capture",
+			.aif_name = "PCM_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = RT_PROXY_DAI_002_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "AFE-PROXY TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min =     8000,
+			.rate_max =	48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = RT_PROXY_DAI_001_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_rx_dai = {
+	.playback = {
+		.stream_name = "Internal BT-SCO Playback",
+		.aif_name = "INT_BT_SCO_RX",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,
+		.rate_max = 16000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = INT_BT_SCO_RX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_a2dp_rx_dai = {
+	.playback = {
+		.stream_name = "Internal BT-A2DP Playback",
+		.aif_name = "INT_BT_A2DP_RX",
+		.rates = SNDRV_PCM_RATE_48000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 48000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = INT_BT_A2DP_RX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_bt_sco_tx_dai = {
+	.capture = {
+		.stream_name = "Internal BT-SCO Capture",
+		.aif_name = "INT_BT_SCO_TX",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 1,
+		.rate_max = 16000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = INT_BT_SCO_TX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_rx_dai = {
+	.playback = {
+		.stream_name = "Internal FM Playback",
+		.aif_name = "INT_FM_RX",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = INT_FM_RX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_fm_tx_dai = {
+	.capture = {
+		.stream_name = "Internal FM Capture",
+		.aif_name = "INT_FM_TX",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+		SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 2,
+		.channels_max = 2,
+		.rate_max = 48000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = INT_FM_TX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_voc_playback_dai[] = {
+	{
+		.playback = {
+			.stream_name = "Voice Farend Playback",
+			.aif_name = "VOICE_PLAYBACK_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				 SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = VOICE_PLAYBACK_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Voice2 Farend Playback",
+			.aif_name = "VOICE2_PLAYBACK_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				 SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = VOICE2_PLAYBACK_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_incall_record_dai[] = {
+	{
+		.capture = {
+			.stream_name = "Voice Uplink Capture",
+			.aif_name = "INCALL_RECORD_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = VOICE_RECORD_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Voice Downlink Capture",
+			.aif_name = "INCALL_RECORD_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = VOICE_RECORD_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_usb_rx_dai = {
+	.playback = {
+		.stream_name = "USB Audio Playback",
+		.aif_name = "USB_AUDIO_RX",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+			 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+			 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+			 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+			 SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+			 SNDRV_PCM_RATE_384000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 1,
+		.channels_max = 8,
+		.rate_max = 384000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = AFE_PORT_ID_USB_RX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_usb_tx_dai = {
+	.capture = {
+		.stream_name = "USB Audio Capture",
+		.aif_name = "USB_AUDIO_TX",
+		.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+			 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+			 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+			 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+			 SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+			 SNDRV_PCM_RATE_192000 | SNDRV_PCM_RATE_352800 |
+			 SNDRV_PCM_RATE_384000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+			   SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE,
+		.channels_min = 1,
+		.channels_max = 8,
+		.rate_max = 384000,
+		.rate_min = 8000,
+	},
+	.ops = &msm_dai_q6_ops,
+	.id = AFE_PORT_ID_USB_TX,
+	.probe = msm_dai_q6_dai_probe,
+	.remove = msm_dai_q6_dai_remove,
+};
+
+static int msm_auxpcm_dev_probe(struct platform_device *pdev)
+{
+	struct msm_dai_q6_auxpcm_dai_data *dai_data;
+	struct msm_dai_auxpcm_pdata *auxpcm_pdata;
+	uint32_t val_array[RATE_MAX_NUM_OF_AUX_PCM_RATES];
+	uint32_t val = 0;
+	const char *intf_name;
+	int rc = 0, i = 0, len = 0;
+	const uint32_t *slot_mapping_array = NULL;
+	u32 array_length = 0;
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_auxpcm_dai_data),
+			   GFP_KERNEL);
+	if (!dai_data)
+		return -ENOMEM;
+
+	auxpcm_pdata = kzalloc(sizeof(struct msm_dai_auxpcm_pdata),
+				GFP_KERNEL);
+
+	if (!auxpcm_pdata) {
+		dev_err(&pdev->dev, "Failed to allocate memory for platform data\n");
+		goto fail_pdata_nomem;
+	}
+
+	dev_dbg(&pdev->dev, "%s: dev %pK, dai_data %pK, auxpcm_pdata %pK\n",
+		__func__, &pdev->dev, dai_data, auxpcm_pdata);
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-mode",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-mode missing in DT node\n",
+			__func__);
+		goto fail_invalid_dt;
+	}
+	auxpcm_pdata->mode_8k.mode = (u16)val_array[RATE_8KHZ];
+	auxpcm_pdata->mode_16k.mode = (u16)val_array[RATE_16KHZ];
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-sync",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-sync missing in DT node\n",
+			__func__);
+		goto fail_invalid_dt;
+	}
+	auxpcm_pdata->mode_8k.sync = (u16)val_array[RATE_8KHZ];
+	auxpcm_pdata->mode_16k.sync = (u16)val_array[RATE_16KHZ];
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-frame",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-frame missing in DT node\n",
+			__func__);
+		goto fail_invalid_dt;
+	}
+	auxpcm_pdata->mode_8k.frame = (u16)val_array[RATE_8KHZ];
+	auxpcm_pdata->mode_16k.frame = (u16)val_array[RATE_16KHZ];
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-quant",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-quant missing in DT node\n",
+			__func__);
+		goto fail_invalid_dt;
+	}
+	auxpcm_pdata->mode_8k.quant = (u16)val_array[RATE_8KHZ];
+	auxpcm_pdata->mode_16k.quant = (u16)val_array[RATE_16KHZ];
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-num-slots",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-num-slots missing in DT node\n",
+			__func__);
+		goto fail_invalid_dt;
+	}
+	auxpcm_pdata->mode_8k.num_slots = (u16)val_array[RATE_8KHZ];
+
+	if (auxpcm_pdata->mode_8k.num_slots >
+	    msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame)) {
+		dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n",
+			 __func__,
+			msm_dai_q6_max_num_slot(auxpcm_pdata->mode_8k.frame),
+			auxpcm_pdata->mode_8k.num_slots);
+		rc = -EINVAL;
+		goto fail_invalid_dt;
+	}
+	auxpcm_pdata->mode_16k.num_slots = (u16)val_array[RATE_16KHZ];
+
+	if (auxpcm_pdata->mode_16k.num_slots >
+	    msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame)) {
+		dev_err(&pdev->dev, "%s Max slots %d greater than DT node %d\n",
+			__func__,
+			msm_dai_q6_max_num_slot(auxpcm_pdata->mode_16k.frame),
+			auxpcm_pdata->mode_16k.num_slots);
+		rc = -EINVAL;
+		goto fail_invalid_dt;
+	}
+
+	slot_mapping_array = of_get_property(pdev->dev.of_node,
+				"qcom,msm-cpudai-auxpcm-slot-mapping", &len);
+
+	if (slot_mapping_array == NULL) {
+		dev_err(&pdev->dev, "%s slot_mapping_array is not valid\n",
+			__func__);
+		rc = -EINVAL;
+		goto fail_invalid_dt;
+	}
+
+	array_length = auxpcm_pdata->mode_8k.num_slots +
+		       auxpcm_pdata->mode_16k.num_slots;
+
+	if (len != sizeof(uint32_t) * array_length) {
+		dev_err(&pdev->dev, "%s Length is %d and expected is %zd\n",
+			__func__, len, sizeof(uint32_t) * array_length);
+		rc = -EINVAL;
+		goto fail_invalid_dt;
+	}
+
+	auxpcm_pdata->mode_8k.slot_mapping =
+					kzalloc(sizeof(uint16_t) *
+					    auxpcm_pdata->mode_8k.num_slots,
+					    GFP_KERNEL);
+	if (!auxpcm_pdata->mode_8k.slot_mapping) {
+		dev_err(&pdev->dev, "%s No mem for mode_8k slot mapping\n",
+			__func__);
+		rc = -ENOMEM;
+		goto fail_invalid_dt;
+	}
+
+	for (i = 0; i < auxpcm_pdata->mode_8k.num_slots; i++)
+		auxpcm_pdata->mode_8k.slot_mapping[i] =
+				(u16)be32_to_cpu(slot_mapping_array[i]);
+
+	auxpcm_pdata->mode_16k.slot_mapping =
+					kzalloc(sizeof(uint16_t) *
+					     auxpcm_pdata->mode_16k.num_slots,
+					     GFP_KERNEL);
+
+	if (!auxpcm_pdata->mode_16k.slot_mapping) {
+		dev_err(&pdev->dev, "%s No mem for mode_16k slot mapping\n",
+			__func__);
+		rc = -ENOMEM;
+		goto fail_invalid_16k_slot_mapping;
+	}
+
+	for (i = 0; i < auxpcm_pdata->mode_16k.num_slots; i++)
+		auxpcm_pdata->mode_16k.slot_mapping[i] =
+			(u16)be32_to_cpu(slot_mapping_array[i +
+					auxpcm_pdata->mode_8k.num_slots]);
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-data",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-cpudai-auxpcm-data missing in DT node\n",
+			__func__);
+		goto fail_invalid_dt1;
+	}
+	auxpcm_pdata->mode_8k.data = (u16)val_array[RATE_8KHZ];
+	auxpcm_pdata->mode_16k.data = (u16)val_array[RATE_16KHZ];
+
+	rc = of_property_read_u32_array(pdev->dev.of_node,
+			"qcom,msm-cpudai-auxpcm-pcm-clk-rate",
+			val_array, RATE_MAX_NUM_OF_AUX_PCM_RATES);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: qcom,msm-cpudai-auxpcm-pcm-clk-rate missing in DT\n",
+			__func__);
+		goto fail_invalid_dt1;
+	}
+	auxpcm_pdata->mode_8k.pcm_clk_rate = (int)val_array[RATE_8KHZ];
+	auxpcm_pdata->mode_16k.pcm_clk_rate = (int)val_array[RATE_16KHZ];
+
+	rc = of_property_read_string(pdev->dev.of_node,
+			"qcom,msm-auxpcm-interface", &intf_name);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: qcom,msm-auxpcm-interface missing in DT node\n",
+			__func__);
+		goto fail_nodev_intf;
+	}
+
+	if (!strcmp(intf_name, "primary")) {
+		dai_data->rx_pid = AFE_PORT_ID_PRIMARY_PCM_RX;
+		dai_data->tx_pid = AFE_PORT_ID_PRIMARY_PCM_TX;
+		pdev->id = MSM_DAI_PRI_AUXPCM_DT_DEV_ID;
+		i = 0;
+	} else if (!strcmp(intf_name, "secondary")) {
+		dai_data->rx_pid = AFE_PORT_ID_SECONDARY_PCM_RX;
+		dai_data->tx_pid = AFE_PORT_ID_SECONDARY_PCM_TX;
+		pdev->id = MSM_DAI_SEC_AUXPCM_DT_DEV_ID;
+		i = 1;
+	} else if (!strcmp(intf_name, "tertiary")) {
+		dai_data->rx_pid = AFE_PORT_ID_TERTIARY_PCM_RX;
+		dai_data->tx_pid = AFE_PORT_ID_TERTIARY_PCM_TX;
+		pdev->id = MSM_DAI_TERT_AUXPCM_DT_DEV_ID;
+		i = 2;
+	} else if (!strcmp(intf_name, "quaternary")) {
+		dai_data->rx_pid = AFE_PORT_ID_QUATERNARY_PCM_RX;
+		dai_data->tx_pid = AFE_PORT_ID_QUATERNARY_PCM_TX;
+		pdev->id = MSM_DAI_QUAT_AUXPCM_DT_DEV_ID;
+		i = 3;
+	} else {
+		dev_err(&pdev->dev, "%s: invalid DT intf name %s\n",
+			__func__, intf_name);
+		goto fail_invalid_intf;
+	}
+	rc = of_property_read_u32(pdev->dev.of_node,
+				  "qcom,msm-cpudai-afe-clk-ver", &val);
+	if (rc)
+		dai_data->afe_clk_ver = AFE_CLK_VERSION_V1;
+	else
+		dai_data->afe_clk_ver = val;
+
+	mutex_init(&dai_data->rlock);
+	dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+	dev_set_drvdata(&pdev->dev, dai_data);
+	pdev->dev.platform_data = (void *) auxpcm_pdata;
+
+	rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_aux_pcm_dai_component,
+			&msm_dai_q6_aux_pcm_dai[i], 1);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: auxpcm dai reg failed, rc=%d\n",
+				__func__, rc);
+		goto fail_reg_dai;
+	}
+
+	return rc;
+
+fail_reg_dai:
+fail_invalid_intf:
+fail_nodev_intf:
+fail_invalid_dt1:
+	kfree(auxpcm_pdata->mode_16k.slot_mapping);
+fail_invalid_16k_slot_mapping:
+	kfree(auxpcm_pdata->mode_8k.slot_mapping);
+fail_invalid_dt:
+	kfree(auxpcm_pdata);
+fail_pdata_nomem:
+	kfree(dai_data);
+	return rc;
+}
+
+static int msm_auxpcm_dev_remove(struct platform_device *pdev)
+{
+	struct msm_dai_q6_auxpcm_dai_data *dai_data;
+
+	dai_data = dev_get_drvdata(&pdev->dev);
+
+	snd_soc_unregister_component(&pdev->dev);
+
+	mutex_destroy(&dai_data->rlock);
+	kfree(dai_data);
+	kfree(pdev->dev.platform_data);
+
+	return 0;
+}
+
+static const struct of_device_id msm_auxpcm_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-auxpcm-dev", },
+	{}
+};
+
+
+static struct platform_driver msm_auxpcm_dev_driver = {
+	.probe  = msm_auxpcm_dev_probe,
+	.remove = msm_auxpcm_dev_remove,
+	.driver = {
+		.name = "msm-auxpcm-dev",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_auxpcm_dev_dt_match,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_rx_dai[] = {
+	{
+		.playback = {
+			.stream_name = "Slimbus Playback",
+			.aif_name = "SLIMBUS_0_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_0_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus1 Playback",
+			.aif_name = "SLIMBUS_1_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_1_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus2 Playback",
+			.aif_name = "SLIMBUS_2_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_2_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus3 Playback",
+			.aif_name = "SLIMBUS_3_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_3_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus4 Playback",
+			.aif_name = "SLIMBUS_4_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_4_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus6 Playback",
+			.aif_name = "SLIMBUS_6_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_6_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus5 Playback",
+			.aif_name = "SLIMBUS_5_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_5_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus7 Playback",
+			.aif_name = "SLIMBUS_7_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_7_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Slimbus8 Playback",
+			.aif_name = "SLIMBUS_8_RX",
+			.rates = SNDRV_PCM_RATE_8000_384000,
+			.formats = DAI_FORMATS_S16_S24_S32_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 384000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_8_RX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_slimbus_tx_dai[] = {
+	{
+		.capture = {
+			.stream_name = "Slimbus Capture",
+			.aif_name = "SLIMBUS_0_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S24_3LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_0_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus1 Capture",
+			.aif_name = "SLIMBUS_1_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S24_3LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_1_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus2 Capture",
+			.aif_name = "SLIMBUS_2_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_2_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus3 Capture",
+			.aif_name = "SLIMBUS_3_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 2,
+			.channels_max = 4,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_3_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus4 Capture",
+			.aif_name = "SLIMBUS_4_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+			.channels_min = 2,
+			.channels_max = 4,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_4_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus5 Capture",
+			.aif_name = "SLIMBUS_5_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_5_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus6 Capture",
+			.aif_name = "SLIMBUS_6_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_6_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus7 Capture",
+			.aif_name = "SLIMBUS_7_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_7_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Slimbus8 Capture",
+			.aif_name = "SLIMBUS_8_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+			SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				   SNDRV_PCM_FMTBIT_S24_LE |
+				   SNDRV_PCM_FMTBIT_S32_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 192000,
+		},
+		.ops = &msm_dai_q6_ops,
+		.id = SLIMBUS_8_TX,
+		.probe = msm_dai_q6_dai_probe,
+		.remove = msm_dai_q6_dai_remove,
+	},
+};
+
+static int msm_dai_q6_mi2s_format_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	dai_data->port_config.i2s.data_format = value;
+	pr_debug("%s: value = %d, channel = %d, line = %d\n",
+		 __func__, value, dai_data->port_config.i2s.mono_stereo,
+		 dai_data->port_config.i2s.channel_mode);
+	return 0;
+}
+
+static int msm_dai_q6_mi2s_format_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_dai_data *dai_data = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] =
+		dai_data->port_config.i2s.data_format;
+	return 0;
+}
+
+static const struct snd_kcontrol_new mi2s_config_controls[] = {
+	SOC_ENUM_EXT("PRI MI2S RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("SEC MI2S RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("TERT MI2S RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("QUAT MI2S RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("QUIN MI2S RX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("PRI MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("SEC MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("TERT MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("QUAT MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("QUIN MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+	SOC_ENUM_EXT("SENARY MI2S TX Format", mi2s_config_enum[0],
+		     msm_dai_q6_mi2s_format_get,
+		     msm_dai_q6_mi2s_format_put),
+};
+
+static int msm_dai_q6_dai_mi2s_probe(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct msm_mi2s_pdata *mi2s_pdata =
+			(struct msm_mi2s_pdata *) dai->dev->platform_data;
+	struct snd_kcontrol *kcontrol = NULL;
+	int rc = 0;
+	const struct snd_kcontrol_new *ctrl = NULL;
+
+	dai->id = mi2s_pdata->intf_id;
+
+	if (mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+		if (dai->id == MSM_PRIM_MI2S)
+			ctrl = &mi2s_config_controls[0];
+		if (dai->id == MSM_SEC_MI2S)
+			ctrl = &mi2s_config_controls[1];
+		if (dai->id == MSM_TERT_MI2S)
+			ctrl = &mi2s_config_controls[2];
+		if (dai->id == MSM_QUAT_MI2S)
+			ctrl = &mi2s_config_controls[3];
+		if (dai->id == MSM_QUIN_MI2S)
+			ctrl = &mi2s_config_controls[4];
+	}
+
+	if (ctrl) {
+		kcontrol = snd_ctl_new1(ctrl,
+					&mi2s_dai_data->rx_dai.mi2s_dai_data);
+		rc = snd_ctl_add(dai->component->card->snd_card, kcontrol);
+		if (rc < 0) {
+			dev_err(dai->dev, "%s: err add RX fmt ctl DAI = %s\n",
+				__func__, dai->name);
+			goto rtn;
+		}
+	}
+
+	ctrl = NULL;
+	if (mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode) {
+		if (dai->id == MSM_PRIM_MI2S)
+			ctrl = &mi2s_config_controls[4];
+		if (dai->id == MSM_SEC_MI2S)
+			ctrl = &mi2s_config_controls[5];
+		if (dai->id == MSM_TERT_MI2S)
+			ctrl = &mi2s_config_controls[6];
+		if (dai->id == MSM_QUAT_MI2S)
+			ctrl = &mi2s_config_controls[7];
+		if (dai->id == MSM_QUIN_MI2S)
+			ctrl = &mi2s_config_controls[9];
+		if (dai->id == MSM_SENARY_MI2S)
+			ctrl = &mi2s_config_controls[10];
+	}
+
+	if (ctrl) {
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				snd_ctl_new1(ctrl,
+				&mi2s_dai_data->tx_dai.mi2s_dai_data));
+		if (rc < 0) {
+			if (kcontrol)
+				snd_ctl_remove(dai->component->card->snd_card,
+						kcontrol);
+			dev_err(dai->dev, "%s: err add TX fmt ctl DAI = %s\n",
+				__func__, dai->name);
+		}
+	}
+	rc = msm_dai_q6_dai_add_route(dai);
+rtn:
+	return rc;
+}
+
+
+static int msm_dai_q6_dai_mi2s_remove(struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+	int rc;
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED,
+		     mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask)) {
+		rc = afe_close(MI2S_RX); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close MI2S_RX port\n");
+		clear_bit(STATUS_PORT_STARTED,
+			  mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask);
+	}
+	if (test_bit(STATUS_PORT_STARTED,
+		     mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+		rc = afe_close(MI2S_TX); /* can block */
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close MI2S_TX port\n");
+		clear_bit(STATUS_PORT_STARTED,
+			  mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask);
+	}
+	kfree(mi2s_dai_data);
+	return 0;
+}
+
+static int msm_dai_q6_mi2s_startup(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+
+	return 0;
+}
+
+
+static int msm_mi2s_get_port_id(u32 mi2s_id, int stream, u16 *port_id)
+{
+	int ret = 0;
+
+	switch (stream) {
+	case SNDRV_PCM_STREAM_PLAYBACK:
+		switch (mi2s_id) {
+		case MSM_PRIM_MI2S:
+			*port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+			break;
+		case MSM_SEC_MI2S:
+			*port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+			break;
+		case MSM_TERT_MI2S:
+			*port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+			break;
+		case MSM_QUAT_MI2S:
+			*port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+			break;
+		case MSM_SEC_MI2S_SD1:
+			*port_id = AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+			break;
+		case MSM_QUIN_MI2S:
+			*port_id = AFE_PORT_ID_QUINARY_MI2S_RX;
+			break;
+		case MSM_INT0_MI2S:
+			*port_id = AFE_PORT_ID_INT0_MI2S_RX;
+			break;
+		case MSM_INT1_MI2S:
+			*port_id = AFE_PORT_ID_INT1_MI2S_RX;
+			break;
+		case MSM_INT2_MI2S:
+			*port_id = AFE_PORT_ID_INT2_MI2S_RX;
+			break;
+		case MSM_INT3_MI2S:
+			*port_id = AFE_PORT_ID_INT3_MI2S_RX;
+			break;
+		case MSM_INT4_MI2S:
+			*port_id = AFE_PORT_ID_INT4_MI2S_RX;
+			break;
+		case MSM_INT5_MI2S:
+			*port_id = AFE_PORT_ID_INT5_MI2S_RX;
+			break;
+		case MSM_INT6_MI2S:
+			*port_id = AFE_PORT_ID_INT6_MI2S_RX;
+			break;
+		default:
+			pr_err("%s: playback err id 0x%x\n",
+				__func__, mi2s_id);
+			ret = -1;
+			break;
+		}
+	break;
+	case SNDRV_PCM_STREAM_CAPTURE:
+		switch (mi2s_id) {
+		case MSM_PRIM_MI2S:
+			*port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+			break;
+		case MSM_SEC_MI2S:
+			*port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+			break;
+		case MSM_TERT_MI2S:
+			*port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+			break;
+		case MSM_QUAT_MI2S:
+			*port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+			break;
+		case MSM_QUIN_MI2S:
+			*port_id = AFE_PORT_ID_QUINARY_MI2S_TX;
+			break;
+		case MSM_SENARY_MI2S:
+			*port_id = AFE_PORT_ID_SENARY_MI2S_TX;
+			break;
+		case MSM_INT0_MI2S:
+			*port_id = AFE_PORT_ID_INT0_MI2S_TX;
+			break;
+		case MSM_INT1_MI2S:
+			*port_id = AFE_PORT_ID_INT1_MI2S_TX;
+			break;
+		case MSM_INT2_MI2S:
+			*port_id = AFE_PORT_ID_INT2_MI2S_TX;
+			break;
+		case MSM_INT3_MI2S:
+			*port_id = AFE_PORT_ID_INT3_MI2S_TX;
+			break;
+		case MSM_INT4_MI2S:
+			*port_id = AFE_PORT_ID_INT4_MI2S_TX;
+			break;
+		case MSM_INT5_MI2S:
+			*port_id = AFE_PORT_ID_INT5_MI2S_TX;
+			break;
+		case MSM_INT6_MI2S:
+			*port_id = AFE_PORT_ID_INT6_MI2S_TX;
+			break;
+		default:
+			pr_err("%s: capture err id 0x%x\n", __func__, mi2s_id);
+			ret = -1;
+			break;
+		}
+	break;
+	default:
+		pr_err("%s: default err %d\n", __func__, stream);
+		ret = -1;
+	break;
+	}
+	pr_debug("%s: port_id = 0x%x\n", __func__, *port_id);
+	return ret;
+}
+
+static int msm_dai_q6_mi2s_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		 &mi2s_dai_data->rx_dai.mi2s_dai_data :
+		 &mi2s_dai_data->tx_dai.mi2s_dai_data);
+	u16 port_id = 0;
+	int rc = 0;
+
+	if (msm_mi2s_get_port_id(dai->id, substream->stream,
+				 &port_id) != 0) {
+		dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
+				__func__, port_id);
+		return -EINVAL;
+	}
+
+	dev_dbg(dai->dev, "%s: dai id %d, afe port id = 0x%x\n"
+		"dai_data->channels = %u sample_rate = %u\n", __func__,
+		dai->id, port_id, dai_data->channels, dai_data->rate);
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		/* PORT START should be set if prepare called
+		 * in active state.
+		 */
+		rc = afe_port_start(port_id, &dai_data->port_config,
+				    dai_data->rate);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to open AFE port 0x%x\n",
+				dai->id);
+		else
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+	}
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+		set_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+		dev_dbg(dai->dev, "%s: set hwfree_status to started\n",
+				__func__);
+	}
+	return rc;
+}
+
+static int msm_dai_q6_mi2s_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+		dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_mi2s_dai_config *mi2s_dai_config =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		&mi2s_dai_data->rx_dai : &mi2s_dai_data->tx_dai);
+	struct msm_dai_q6_dai_data *dai_data = &mi2s_dai_config->mi2s_dai_data;
+	struct afe_param_id_i2s_cfg *i2s = &dai_data->port_config.i2s;
+
+	dai_data->channels = params_channels(params);
+	switch (dai_data->channels) {
+	case 8:
+	case 7:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_8CHS)
+			goto error_invalid_data;
+		dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_8CHS;
+		break;
+	case 6:
+	case 5:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_6CHS)
+			goto error_invalid_data;
+		dai_data->port_config.i2s.channel_mode = AFE_PORT_I2S_6CHS;
+		break;
+	case 4:
+	case 3:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_QUAD01)
+			goto error_invalid_data;
+		if (mi2s_dai_config->pdata_mi2s_lines == AFE_PORT_I2S_QUAD23)
+			dai_data->port_config.i2s.channel_mode =
+				mi2s_dai_config->pdata_mi2s_lines;
+		else
+			dai_data->port_config.i2s.channel_mode =
+					AFE_PORT_I2S_QUAD01;
+		break;
+	case 2:
+	case 1:
+		if (mi2s_dai_config->pdata_mi2s_lines < AFE_PORT_I2S_SD0)
+			goto error_invalid_data;
+		switch (mi2s_dai_config->pdata_mi2s_lines) {
+		case AFE_PORT_I2S_SD0:
+		case AFE_PORT_I2S_SD1:
+		case AFE_PORT_I2S_SD2:
+		case AFE_PORT_I2S_SD3:
+			dai_data->port_config.i2s.channel_mode =
+				mi2s_dai_config->pdata_mi2s_lines;
+			break;
+		case AFE_PORT_I2S_QUAD01:
+		case AFE_PORT_I2S_6CHS:
+		case AFE_PORT_I2S_8CHS:
+			dai_data->port_config.i2s.channel_mode =
+						AFE_PORT_I2S_SD0;
+			break;
+		case AFE_PORT_I2S_QUAD23:
+			dai_data->port_config.i2s.channel_mode =
+						AFE_PORT_I2S_SD2;
+			break;
+		}
+		if (dai_data->channels == 2)
+			dai_data->port_config.i2s.mono_stereo =
+						MSM_AFE_CH_STEREO;
+		else
+			dai_data->port_config.i2s.mono_stereo = MSM_AFE_MONO;
+		break;
+	default:
+		pr_err("%s: default err channels %d\n",
+			__func__, dai_data->channels);
+		goto error_invalid_data;
+	}
+	dai_data->rate = params_rate(params);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+	case SNDRV_PCM_FORMAT_SPECIAL:
+		dai_data->port_config.i2s.bit_width = 16;
+		dai_data->bitwidth = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		dai_data->port_config.i2s.bit_width = 24;
+		dai_data->bitwidth = 24;
+		break;
+	default:
+		pr_err("%s: format %d\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+
+	dai_data->port_config.i2s.i2s_cfg_minor_version =
+			AFE_API_VERSION_I2S_CONFIG;
+	dai_data->port_config.i2s.sample_rate = dai_data->rate;
+	if ((test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) &&
+	    test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->rx_dai.mi2s_dai_data.hwfree_status)) ||
+	    (test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask) &&
+	    test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->tx_dai.mi2s_dai_data.hwfree_status))) {
+		if ((mi2s_dai_data->tx_dai.mi2s_dai_data.rate !=
+		    mi2s_dai_data->rx_dai.mi2s_dai_data.rate) ||
+		   (mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth !=
+		    mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth)) {
+			dev_err(dai->dev, "%s: Error mismatch in HW params\n"
+				"Tx sample_rate = %u bit_width = %hu\n"
+				"Rx sample_rate = %u bit_width = %hu\n"
+				, __func__,
+				mi2s_dai_data->tx_dai.mi2s_dai_data.rate,
+				mi2s_dai_data->tx_dai.mi2s_dai_data.bitwidth,
+				mi2s_dai_data->rx_dai.mi2s_dai_data.rate,
+				mi2s_dai_data->rx_dai.mi2s_dai_data.bitwidth);
+			return -EINVAL;
+		}
+	}
+	dev_dbg(dai->dev, "%s: dai id %d dai_data->channels = %d\n"
+		"sample_rate = %u i2s_cfg_minor_version = 0x%x\n"
+		"bit_width = %hu  channel_mode = 0x%x mono_stereo = %#x\n"
+		"ws_src = 0x%x sample_rate = %u data_format = 0x%x\n"
+		"reserved = %u\n", __func__, dai->id, dai_data->channels,
+		dai_data->rate, i2s->i2s_cfg_minor_version, i2s->bit_width,
+		i2s->channel_mode, i2s->mono_stereo, i2s->ws_src,
+		i2s->sample_rate, i2s->data_format, i2s->reserved);
+
+	return 0;
+
+error_invalid_data:
+	pr_err("%s: dai_data->channels = %d channel_mode = %d\n", __func__,
+		 dai_data->channels, dai_data->port_config.i2s.channel_mode);
+	return -EINVAL;
+}
+
+
+static int msm_dai_q6_mi2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+	dev_get_drvdata(dai->dev);
+
+	if (test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->rx_dai.mi2s_dai_data.status_mask) ||
+	    test_bit(STATUS_PORT_STARTED,
+	    mi2s_dai_data->tx_dai.mi2s_dai_data.status_mask)) {
+		dev_err(dai->dev, "%s: err chg i2s mode while dai running",
+			__func__);
+		return -EPERM;
+	}
+
+	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+	case SND_SOC_DAIFMT_CBS_CFS:
+		mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+		mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 1;
+		break;
+	case SND_SOC_DAIFMT_CBM_CFM:
+		mi2s_dai_data->rx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+		mi2s_dai_data->tx_dai.mi2s_dai_data.port_config.i2s.ws_src = 0;
+		break;
+	default:
+		pr_err("%s: fmt %d\n",
+			__func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_mi2s_hw_free(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		 &mi2s_dai_data->rx_dai.mi2s_dai_data :
+		 &mi2s_dai_data->tx_dai.mi2s_dai_data);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status)) {
+		clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+		dev_dbg(dai->dev, "%s: clear hwfree_status\n", __func__);
+	}
+	return 0;
+}
+
+static void msm_dai_q6_mi2s_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_mi2s_dai_data *mi2s_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct msm_dai_q6_dai_data *dai_data =
+		(substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		 &mi2s_dai_data->rx_dai.mi2s_dai_data :
+		 &mi2s_dai_data->tx_dai.mi2s_dai_data);
+	 u16 port_id = 0;
+	int rc = 0;
+
+	if (msm_mi2s_get_port_id(dai->id, substream->stream,
+				 &port_id) != 0) {
+		dev_err(dai->dev, "%s: Invalid Port ID 0x%x\n",
+				__func__, port_id);
+	}
+
+	dev_dbg(dai->dev, "%s: closing afe port id = 0x%x\n",
+			__func__, port_id);
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(port_id);
+		if (rc < 0)
+			dev_err(dai->dev, "fail to close AFE port\n");
+		clear_bit(STATUS_PORT_STARTED, dai_data->status_mask);
+	}
+	if (test_bit(STATUS_PORT_STARTED, dai_data->hwfree_status))
+		clear_bit(STATUS_PORT_STARTED, dai_data->hwfree_status);
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_mi2s_ops = {
+	.startup	= msm_dai_q6_mi2s_startup,
+	.prepare	= msm_dai_q6_mi2s_prepare,
+	.hw_params	= msm_dai_q6_mi2s_hw_params,
+	.hw_free	= msm_dai_q6_mi2s_hw_free,
+	.set_fmt	= msm_dai_q6_mi2s_set_fmt,
+	.shutdown	= msm_dai_q6_mi2s_shutdown,
+};
+
+/* Channel min and max are initialized base on platform data */
+static struct snd_soc_dai_driver msm_dai_q6_mi2s_dai[] = {
+	{
+		.playback = {
+			.stream_name = "Primary MI2S Playback",
+			.aif_name = "PRI_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.capture = {
+			.stream_name = "Primary MI2S Capture",
+			.aif_name = "PRI_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_PRIM_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary MI2S Playback",
+			.aif_name = "SEC_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.capture = {
+			.stream_name = "Secondary MI2S Capture",
+			.aif_name = "SEC_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_SEC_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary MI2S Playback",
+			.aif_name = "TERT_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.capture = {
+			.stream_name = "Tertiary MI2S Capture",
+			.aif_name = "TERT_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_TERT_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary MI2S Playback",
+			.aif_name = "QUAT_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.capture = {
+			.stream_name = "Quaternary MI2S Capture",
+			.aif_name = "QUAT_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+				 SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+				 SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+				 SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+				 SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_QUAT_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary MI2S Playback SD1",
+			.aif_name = "SEC_MI2S_RX_SD1",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.id = MSM_SEC_MI2S_SD1,
+	},
+	{
+		.playback = {
+			.stream_name = "Quinary MI2S Playback",
+			.aif_name = "QUIN_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_96000 |
+			SNDRV_PCM_RATE_192000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     192000,
+		},
+		.capture = {
+			.stream_name = "Quinary MI2S Capture",
+			.aif_name = "QUIN_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_QUIN_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Senary_mi2s Capture",
+			.aif_name = "SENARY_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_SENARY_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT0 MI2S Playback",
+			.aif_name = "INT0_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_44100,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT0 MI2S Capture",
+			.aif_name = "INT0_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT0_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT1 MI2S Playback",
+			.aif_name = "INT1_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT1 MI2S Capture",
+			.aif_name = "INT1_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT1_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT2 MI2S Playback",
+			.aif_name = "INT2_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT2 MI2S Capture",
+			.aif_name = "INT2_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT2_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT3 MI2S Playback",
+			.aif_name = "INT3_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT3 MI2S Capture",
+			.aif_name = "INT3_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT3_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT4 MI2S Playback",
+			.aif_name = "INT4_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT4 MI2S Capture",
+			.aif_name = "INT4_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT4_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT5 MI2S Playback",
+			.aif_name = "INT5_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT5 MI2S Capture",
+			.aif_name = "INT5_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT5_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "INT6 MI2S Playback",
+			.aif_name = "INT6_MI2S_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.capture = {
+			.stream_name = "INT6 MI2S Capture",
+			.aif_name = "INT6_MI2S_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.rate_min =     8000,
+			.rate_max =     48000,
+		},
+		.ops = &msm_dai_q6_mi2s_ops,
+		.id = MSM_INT6_MI2S,
+		.probe = msm_dai_q6_dai_mi2s_probe,
+		.remove = msm_dai_q6_dai_mi2s_remove,
+	},
+};
+
+
+static int msm_dai_q6_mi2s_get_lineconfig(u16 sd_lines, u16 *config_ptr,
+					  unsigned int *ch_cnt)
+{
+	u8 num_of_sd_lines;
+
+	num_of_sd_lines = num_of_bits_set(sd_lines);
+	switch (num_of_sd_lines) {
+	case 0:
+		pr_debug("%s: no line is assigned\n", __func__);
+		break;
+	case 1:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0:
+			*config_ptr = AFE_PORT_I2S_SD0;
+			break;
+		case MSM_MI2S_SD1:
+			*config_ptr = AFE_PORT_I2S_SD1;
+			break;
+		case MSM_MI2S_SD2:
+			*config_ptr = AFE_PORT_I2S_SD2;
+			break;
+		case MSM_MI2S_SD3:
+			*config_ptr = AFE_PORT_I2S_SD3;
+			break;
+		default:
+			pr_err("%s: invalid SD lines %d\n",
+				   __func__, sd_lines);
+			goto error_invalid_data;
+		}
+		break;
+	case 2:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0 | MSM_MI2S_SD1:
+			*config_ptr = AFE_PORT_I2S_QUAD01;
+			break;
+		case MSM_MI2S_SD2 | MSM_MI2S_SD3:
+			*config_ptr = AFE_PORT_I2S_QUAD23;
+			break;
+		default:
+			pr_err("%s: invalid SD lines %d\n",
+				   __func__, sd_lines);
+			goto error_invalid_data;
+		}
+		break;
+	case 3:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2:
+			*config_ptr = AFE_PORT_I2S_6CHS;
+			break;
+		default:
+			pr_err("%s: invalid SD lines %d\n",
+				   __func__, sd_lines);
+			goto error_invalid_data;
+		}
+		break;
+	case 4:
+		switch (sd_lines) {
+		case MSM_MI2S_SD0 | MSM_MI2S_SD1 | MSM_MI2S_SD2 | MSM_MI2S_SD3:
+			*config_ptr = AFE_PORT_I2S_8CHS;
+			break;
+		default:
+			pr_err("%s: invalid SD lines %d\n",
+				   __func__, sd_lines);
+			goto error_invalid_data;
+		}
+		break;
+	default:
+		pr_err("%s: invalid SD lines %d\n", __func__, num_of_sd_lines);
+		goto error_invalid_data;
+	}
+	*ch_cnt = num_of_sd_lines;
+	return 0;
+
+error_invalid_data:
+	pr_err("%s: invalid data\n", __func__);
+	return -EINVAL;
+}
+
+static int msm_dai_q6_mi2s_platform_data_validation(
+	struct platform_device *pdev, struct snd_soc_dai_driver *dai_driver)
+{
+	struct msm_dai_q6_mi2s_dai_data *dai_data = dev_get_drvdata(&pdev->dev);
+	struct msm_mi2s_pdata *mi2s_pdata =
+			(struct msm_mi2s_pdata *) pdev->dev.platform_data;
+	unsigned int ch_cnt;
+	int rc = 0;
+	u16 sd_line;
+
+	if (mi2s_pdata == NULL) {
+		pr_err("%s: mi2s_pdata NULL", __func__);
+		return -EINVAL;
+	}
+
+	rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->rx_sd_lines,
+					    &sd_line, &ch_cnt);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "invalid MI2S RX sd line config\n");
+		goto rtn;
+	}
+
+	if (ch_cnt) {
+		dai_data->rx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+		sd_line;
+		dai_data->rx_dai.pdata_mi2s_lines = sd_line;
+		dai_driver->playback.channels_min = 1;
+		dai_driver->playback.channels_max = ch_cnt << 1;
+	} else {
+		dai_driver->playback.channels_min = 0;
+		dai_driver->playback.channels_max = 0;
+	}
+	rc = msm_dai_q6_mi2s_get_lineconfig(mi2s_pdata->tx_sd_lines,
+					    &sd_line, &ch_cnt);
+	if (rc < 0) {
+		dev_err(&pdev->dev, "invalid MI2S TX sd line config\n");
+		goto rtn;
+	}
+
+	if (ch_cnt) {
+		dai_data->tx_dai.mi2s_dai_data.port_config.i2s.channel_mode =
+		sd_line;
+		dai_data->tx_dai.pdata_mi2s_lines = sd_line;
+		dai_driver->capture.channels_min = 1;
+		dai_driver->capture.channels_max = ch_cnt << 1;
+	} else {
+		dai_driver->capture.channels_min = 0;
+		dai_driver->capture.channels_max = 0;
+	}
+
+	dev_dbg(&pdev->dev, "%s: playback sdline 0x%x capture sdline 0x%x\n",
+		__func__, dai_data->rx_dai.pdata_mi2s_lines,
+		dai_data->tx_dai.pdata_mi2s_lines);
+	dev_dbg(&pdev->dev, "%s: playback ch_max %d capture ch_mx %d\n",
+		__func__, dai_driver->playback.channels_max,
+		dai_driver->capture.channels_max);
+rtn:
+	return rc;
+}
+
+static const struct snd_soc_component_driver msm_q6_mi2s_dai_component = {
+	.name		= "msm-dai-q6-mi2s",
+};
+static int msm_dai_q6_mi2s_dev_probe(struct platform_device *pdev)
+{
+	struct msm_dai_q6_mi2s_dai_data *dai_data;
+	const char *q6_mi2s_dev_id = "qcom,msm-dai-q6-mi2s-dev-id";
+	u32 tx_line = 0;
+	u32  rx_line = 0;
+	u32 mi2s_intf = 0;
+	struct msm_mi2s_pdata *mi2s_pdata;
+	int rc;
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_mi2s_dev_id,
+				  &mi2s_intf);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing 0x%x in dt node\n", __func__, mi2s_intf);
+		goto rtn;
+	}
+
+	dev_dbg(&pdev->dev, "dev name %s dev id 0x%x\n", dev_name(&pdev->dev),
+		mi2s_intf);
+
+	if ((mi2s_intf < MSM_MI2S_MIN || mi2s_intf > MSM_MI2S_MAX)
+		|| (mi2s_intf >= ARRAY_SIZE(msm_dai_q6_mi2s_dai))) {
+		dev_err(&pdev->dev,
+			"%s: Invalid MI2S ID %u from Device Tree\n",
+			__func__, mi2s_intf);
+		rc = -ENXIO;
+		goto rtn;
+	}
+
+	pdev->id = mi2s_intf;
+
+	mi2s_pdata = kzalloc(sizeof(struct msm_mi2s_pdata), GFP_KERNEL);
+	if (!mi2s_pdata) {
+		rc = -ENOMEM;
+		goto rtn;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-rx-lines",
+				  &rx_line);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Rx line from DT file %s\n", __func__,
+			"qcom,msm-mi2s-rx-lines");
+		goto free_pdata;
+	}
+
+	rc = of_property_read_u32(pdev->dev.of_node, "qcom,msm-mi2s-tx-lines",
+				  &tx_line);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Tx line from DT file %s\n", __func__,
+			"qcom,msm-mi2s-tx-lines");
+		goto free_pdata;
+	}
+	dev_dbg(&pdev->dev, "dev name %s Rx line 0x%x , Tx ine 0x%x\n",
+		dev_name(&pdev->dev), rx_line, tx_line);
+	mi2s_pdata->rx_sd_lines = rx_line;
+	mi2s_pdata->tx_sd_lines = tx_line;
+	mi2s_pdata->intf_id = mi2s_intf;
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_mi2s_dai_data),
+			   GFP_KERNEL);
+	if (!dai_data) {
+		rc = -ENOMEM;
+		goto free_pdata;
+	} else
+		dev_set_drvdata(&pdev->dev, dai_data);
+
+	pdev->dev.platform_data = mi2s_pdata;
+
+	rc = msm_dai_q6_mi2s_platform_data_validation(pdev,
+			&msm_dai_q6_mi2s_dai[mi2s_intf]);
+	if (rc < 0)
+		goto free_dai_data;
+
+	rc = snd_soc_register_component(&pdev->dev, &msm_q6_mi2s_dai_component,
+	&msm_dai_q6_mi2s_dai[mi2s_intf], 1);
+	if (rc < 0)
+		goto err_register;
+	return 0;
+
+err_register:
+	dev_err(&pdev->dev, "fail to msm_dai_q6_mi2s_dev_probe\n");
+free_dai_data:
+	kfree(dai_data);
+free_pdata:
+	kfree(mi2s_pdata);
+rtn:
+	return rc;
+}
+
+static int msm_dai_q6_mi2s_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct snd_soc_component_driver msm_dai_q6_component = {
+	.name		= "msm-dai-q6-dev",
+};
+
+static int msm_dai_q6_dev_probe(struct platform_device *pdev)
+{
+	int rc, id, i, len;
+	const char *q6_dev_id = "qcom,msm-dai-q6-dev-id";
+	char stream_name[80];
+
+	rc = of_property_read_u32(pdev->dev.of_node, q6_dev_id, &id);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, q6_dev_id);
+		return rc;
+	}
+
+	pdev->id = id;
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+		 dev_name(&pdev->dev), pdev->id);
+
+	switch (id) {
+	case SLIMBUS_0_RX:
+		strlcpy(stream_name, "Slimbus Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_2_RX:
+		strlcpy(stream_name, "Slimbus2 Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_1_RX:
+		strlcpy(stream_name, "Slimbus1 Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_3_RX:
+		strlcpy(stream_name, "Slimbus3 Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_4_RX:
+		strlcpy(stream_name, "Slimbus4 Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_5_RX:
+		strlcpy(stream_name, "Slimbus5 Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_6_RX:
+		strlcpy(stream_name, "Slimbus6 Playback", 80);
+		goto register_slim_playback;
+	case SLIMBUS_7_RX:
+		strlcpy(stream_name, "Slimbus7 Playback", sizeof(stream_name));
+		goto register_slim_playback;
+	case SLIMBUS_8_RX:
+		strlcpy(stream_name, "Slimbus8 Playback", sizeof(stream_name));
+		goto register_slim_playback;
+register_slim_playback:
+		rc = -ENODEV;
+		len = strnlen(stream_name, 80);
+		for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_rx_dai); i++) {
+			if (msm_dai_q6_slimbus_rx_dai[i].playback.stream_name &&
+				!strcmp(stream_name,
+				msm_dai_q6_slimbus_rx_dai[i]
+				.playback.stream_name)) {
+				rc = snd_soc_register_component(&pdev->dev,
+				&msm_dai_q6_component,
+				&msm_dai_q6_slimbus_rx_dai[i], 1);
+				break;
+			}
+		}
+		if (rc)
+			pr_err("%s: Device not found stream name %s\n",
+				__func__, stream_name);
+		break;
+	case SLIMBUS_0_TX:
+		strlcpy(stream_name, "Slimbus Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_1_TX:
+		strlcpy(stream_name, "Slimbus1 Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_2_TX:
+		strlcpy(stream_name, "Slimbus2 Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_3_TX:
+		strlcpy(stream_name, "Slimbus3 Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_4_TX:
+		strlcpy(stream_name, "Slimbus4 Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_5_TX:
+		strlcpy(stream_name, "Slimbus5 Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_6_TX:
+		strlcpy(stream_name, "Slimbus6 Capture", 80);
+		goto register_slim_capture;
+	case SLIMBUS_7_TX:
+		strlcpy(stream_name, "Slimbus7 Capture", sizeof(stream_name));
+		goto register_slim_capture;
+	case SLIMBUS_8_TX:
+		strlcpy(stream_name, "Slimbus8 Capture", sizeof(stream_name));
+		goto register_slim_capture;
+register_slim_capture:
+		rc = -ENODEV;
+		len = strnlen(stream_name, 80);
+		for (i = 0; i < ARRAY_SIZE(msm_dai_q6_slimbus_tx_dai); i++) {
+			if (msm_dai_q6_slimbus_tx_dai[i].capture.stream_name &&
+				!strcmp(stream_name,
+				msm_dai_q6_slimbus_tx_dai[i]
+				.capture.stream_name)) {
+				rc = snd_soc_register_component(&pdev->dev,
+				&msm_dai_q6_component,
+				&msm_dai_q6_slimbus_tx_dai[i], 1);
+				break;
+			}
+		}
+		if (rc)
+			pr_err("%s: Device not found stream name %s\n",
+				__func__, stream_name);
+		break;
+	case INT_BT_SCO_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_bt_sco_rx_dai, 1);
+		break;
+	case INT_BT_SCO_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_bt_sco_tx_dai, 1);
+		break;
+	case INT_BT_A2DP_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_bt_a2dp_rx_dai, 1);
+		break;
+	case INT_FM_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_fm_rx_dai, 1);
+		break;
+	case INT_FM_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_fm_tx_dai, 1);
+		break;
+	case AFE_PORT_ID_USB_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_usb_rx_dai, 1);
+		break;
+	case AFE_PORT_ID_USB_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_q6_component, &msm_dai_q6_usb_tx_dai, 1);
+		break;
+	case RT_PROXY_DAI_001_RX:
+		strlcpy(stream_name, "AFE Playback", 80);
+		goto register_afe_playback;
+	case RT_PROXY_DAI_002_RX:
+		strlcpy(stream_name, "AFE-PROXY RX", 80);
+register_afe_playback:
+		rc = -ENODEV;
+		len = strnlen(stream_name, 80);
+		for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_rx_dai); i++) {
+			if (msm_dai_q6_afe_rx_dai[i].playback.stream_name &&
+			    !strcmp(stream_name,
+			    msm_dai_q6_afe_rx_dai[i].playback.stream_name)) {
+				rc = snd_soc_register_component(&pdev->dev,
+					&msm_dai_q6_component,
+					&msm_dai_q6_afe_rx_dai[i], 1);
+				break;
+			}
+		}
+		if (rc)
+			pr_err("%s: Device not found stream name %s\n",
+			__func__, stream_name);
+		break;
+	case RT_PROXY_DAI_001_TX:
+		strlcpy(stream_name, "AFE-PROXY TX", 80);
+		goto register_afe_capture;
+	case RT_PROXY_DAI_002_TX:
+		strlcpy(stream_name, "AFE Capture", 80);
+register_afe_capture:
+		rc = -ENODEV;
+		len = strnlen(stream_name, 80);
+		for (i = 0; i < ARRAY_SIZE(msm_dai_q6_afe_tx_dai); i++) {
+			if (msm_dai_q6_afe_tx_dai[i].capture.stream_name &&
+				!strcmp(stream_name,
+				msm_dai_q6_afe_tx_dai[i].capture.stream_name)) {
+				rc = snd_soc_register_component(&pdev->dev,
+					&msm_dai_q6_component,
+					&msm_dai_q6_afe_tx_dai[i], 1);
+				break;
+			}
+		}
+		if (rc)
+			pr_err("%s: Device not found stream name %s\n",
+			__func__, stream_name);
+		break;
+	case VOICE_PLAYBACK_TX:
+		strlcpy(stream_name, "Voice Farend Playback", 80);
+		goto register_voice_playback;
+	case VOICE2_PLAYBACK_TX:
+		strlcpy(stream_name, "Voice2 Farend Playback", 80);
+register_voice_playback:
+		rc = -ENODEV;
+		len = strnlen(stream_name, 80);
+		for (i = 0; i < ARRAY_SIZE(msm_dai_q6_voc_playback_dai); i++) {
+			if (msm_dai_q6_voc_playback_dai[i].playback.stream_name
+			    && !strcmp(stream_name,
+			 msm_dai_q6_voc_playback_dai[i].playback.stream_name)) {
+				rc = snd_soc_register_component(&pdev->dev,
+					&msm_dai_q6_component,
+					&msm_dai_q6_voc_playback_dai[i], 1);
+				break;
+			}
+		}
+		if (rc)
+			pr_err("%s Device not found stream name %s\n",
+			       __func__, stream_name);
+		break;
+	case VOICE_RECORD_RX:
+		strlcpy(stream_name, "Voice Downlink Capture", 80);
+		goto register_uplink_capture;
+	case VOICE_RECORD_TX:
+		strlcpy(stream_name, "Voice Uplink Capture", 80);
+register_uplink_capture:
+		rc = -ENODEV;
+		len = strnlen(stream_name, 80);
+		for (i = 0; i < ARRAY_SIZE(msm_dai_q6_incall_record_dai); i++) {
+			if (msm_dai_q6_incall_record_dai[i].capture.stream_name
+			    && !strcmp(stream_name,
+			    msm_dai_q6_incall_record_dai[i].
+			    capture.stream_name)) {
+				rc = snd_soc_register_component(&pdev->dev,
+					&msm_dai_q6_component,
+					&msm_dai_q6_incall_record_dai[i], 1);
+				break;
+			}
+		}
+		if (rc)
+			pr_err("%s: Device not found stream name %s\n",
+			__func__, stream_name);
+		break;
+
+	default:
+		rc = -ENODEV;
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-q6-dev", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_dev = {
+	.probe  = msm_dai_q6_dev_probe,
+	.remove = msm_dai_q6_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-dev",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_dev_dt_match,
+	},
+};
+
+static int msm_dai_q6_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+		 dev_name(&pdev->dev), pdev->id);
+	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+			__func__, rc);
+	} else
+		dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+	return rc;
+}
+
+static int msm_dai_q6_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-q6", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_dt_match);
+static struct platform_driver msm_dai_q6 = {
+	.probe  = msm_dai_q6_probe,
+	.remove = msm_dai_q6_remove,
+	.driver = {
+		.name = "msm-dai-q6",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_dt_match,
+	},
+};
+
+static int msm_dai_mi2s_q6_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+			__func__, rc);
+	} else
+		dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+	return rc;
+}
+
+static int msm_dai_mi2s_q6_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id msm_dai_mi2s_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-mi2s", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_mi2s_dt_match);
+
+static struct platform_driver msm_dai_mi2s_q6 = {
+	.probe  = msm_dai_mi2s_q6_probe,
+	.remove = msm_dai_mi2s_q6_remove,
+	.driver = {
+		.name = "msm-dai-mi2s",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_mi2s_dt_match,
+	},
+};
+
+static const struct of_device_id msm_dai_q6_mi2s_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-q6-mi2s", },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_mi2s_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_mi2s_driver = {
+	.probe  = msm_dai_q6_mi2s_dev_probe,
+	.remove  = msm_dai_q6_mi2s_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-mi2s",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_mi2s_dev_dt_match,
+	},
+};
+
+static int msm_dai_q6_spdif_dev_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	pdev->id = AFE_PORT_ID_SPDIF_RX;
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+			dev_name(&pdev->dev), pdev->id);
+
+	rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_spdif_q6_component,
+			&msm_dai_q6_spdif_spdif_rx_dai, 1);
+	return rc;
+}
+
+static int msm_dai_q6_spdif_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_spdif_dt_match[] = {
+	{.compatible = "qcom,msm-dai-q6-spdif"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_dai_q6_spdif_dt_match);
+
+static struct platform_driver msm_dai_q6_spdif_driver = {
+	.probe  = msm_dai_q6_spdif_dev_probe,
+	.remove = msm_dai_q6_spdif_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-spdif",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_spdif_dt_match,
+	},
+};
+
+static int msm_dai_tdm_q6_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+	u32 num_ports = 0;
+	const uint32_t *port_id_array = NULL;
+	uint32_t array_length = 0;
+	int i = 0;
+	int group_idx = 0;
+
+	/* extract tdm group info into static */
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-group-id",
+		(u32 *)&tdm_group_cfg.group_id);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Group ID from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-group-id");
+		goto rtn;
+	}
+	dev_dbg(&pdev->dev, "%s: Group ID from DT file 0x%x\n",
+		__func__, tdm_group_cfg.group_id);
+
+	dev_info(&pdev->dev, "%s: dev_name: %s group_id: 0x%x\n",
+		__func__, dev_name(&pdev->dev), tdm_group_cfg.group_id);
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-group-num-ports",
+		&num_ports);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Group Num Ports from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-group-num-ports");
+		goto rtn;
+	}
+	dev_dbg(&pdev->dev, "%s: Group Num Ports from DT file 0x%x\n",
+		__func__, num_ports);
+
+	if (num_ports > AFE_GROUP_DEVICE_NUM_PORTS) {
+		dev_err(&pdev->dev, "%s Group Num Ports %d greater than Max %d\n",
+			__func__, num_ports, AFE_GROUP_DEVICE_NUM_PORTS);
+		rc = -EINVAL;
+		goto rtn;
+	}
+
+	port_id_array = of_get_property(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-group-port-id",
+		&array_length);
+	if (port_id_array == NULL) {
+		dev_err(&pdev->dev, "%s port_id_array is not valid\n",
+			__func__);
+		rc = -EINVAL;
+		goto rtn;
+	}
+	if (array_length != sizeof(uint32_t) * num_ports) {
+		dev_err(&pdev->dev, "%s array_length is %d, expected is %zd\n",
+			__func__, array_length, sizeof(uint32_t) * num_ports);
+		rc = -EINVAL;
+		goto rtn;
+	}
+
+	for (i = 0; i < num_ports; i++)
+		tdm_group_cfg.port_id[i] =
+			(u16)be32_to_cpu(port_id_array[i]);
+	/* Unused index should be filled with 0 or AFE_PORT_INVALID */
+	for (i = num_ports; i < AFE_GROUP_DEVICE_NUM_PORTS; i++)
+		tdm_group_cfg.port_id[i] =
+			AFE_PORT_INVALID;
+
+	/* extract tdm clk info into static */
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-clk-rate",
+		&tdm_clk_set.clk_freq_in_hz);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Clk Rate from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-clk-rate");
+		goto rtn;
+	}
+	dev_dbg(&pdev->dev, "%s: Clk Rate from DT file %d\n",
+		__func__, tdm_clk_set.clk_freq_in_hz);
+
+	/* other initializations within device group */
+	group_idx = msm_dai_q6_get_group_idx(tdm_group_cfg.group_id);
+	if (group_idx < 0) {
+		dev_err(&pdev->dev, "%s: group id 0x%x not supported\n",
+			__func__, tdm_group_cfg.group_id);
+		rc = -EINVAL;
+		goto rtn;
+	}
+	atomic_set(&tdm_group_ref[group_idx], 0);
+
+	/* probe child node info */
+	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+			__func__, rc);
+		goto rtn;
+	} else
+		dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+rtn:
+	return rc;
+}
+
+static int msm_dai_tdm_q6_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static const struct of_device_id msm_dai_tdm_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-tdm", },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_tdm_dt_match);
+
+static struct platform_driver msm_dai_tdm_q6 = {
+	.probe  = msm_dai_tdm_q6_probe,
+	.remove = msm_dai_tdm_q6_remove,
+	.driver = {
+		.name = "msm-dai-tdm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_tdm_dt_match,
+	},
+};
+
+static int msm_dai_q6_tdm_data_format_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	dai_data->port_cfg.tdm.data_format = value;
+	pr_debug("%s: data_format = %d\n",
+		__func__, dai_data->port_cfg.tdm.data_format);
+	return 0;
+}
+
+static int msm_dai_q6_tdm_data_format_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] =
+		dai_data->port_cfg.tdm.data_format;
+	pr_debug("%s: data_format = %d\n",
+		__func__, dai_data->port_cfg.tdm.data_format);
+	return 0;
+}
+
+static int msm_dai_q6_tdm_header_type_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+	int value = ucontrol->value.integer.value[0];
+
+	dai_data->port_cfg.custom_tdm_header.header_type = value;
+	pr_debug("%s: header_type = %d\n",
+		__func__,
+		dai_data->port_cfg.custom_tdm_header.header_type);
+	return 0;
+}
+
+static int msm_dai_q6_tdm_header_type_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+
+	ucontrol->value.integer.value[0] =
+		dai_data->port_cfg.custom_tdm_header.header_type;
+	pr_debug("%s: header_type = %d\n",
+		__func__,
+		dai_data->port_cfg.custom_tdm_header.header_type);
+	return 0;
+}
+
+static int msm_dai_q6_tdm_header_put(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+	int i = 0;
+
+	for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) {
+		dai_data->port_cfg.custom_tdm_header.header[i] =
+			(u16)ucontrol->value.integer.value[i];
+		pr_debug("%s: header #%d = 0x%x\n",
+			__func__, i,
+			dai_data->port_cfg.custom_tdm_header.header[i]);
+	}
+	return 0;
+}
+
+static int msm_dai_q6_tdm_header_get(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = kcontrol->private_data;
+	int i = 0;
+
+	for (i = 0; i < AFE_CUSTOM_TDM_HEADER_MAX_CNT; i++) {
+		ucontrol->value.integer.value[i] =
+			dai_data->port_cfg.custom_tdm_header.header[i];
+		pr_debug("%s: header #%d = 0x%x\n",
+			__func__, i,
+			dai_data->port_cfg.custom_tdm_header.header[i]);
+	}
+	return 0;
+}
+
+static const struct snd_kcontrol_new tdm_config_controls_data_format[] = {
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_1 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_2 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_3 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_4 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_5 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_6 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_7 Data Format", tdm_config_enum[0],
+			msm_dai_q6_tdm_data_format_get,
+			msm_dai_q6_tdm_data_format_put),
+};
+
+static const struct snd_kcontrol_new tdm_config_controls_header_type[] = {
+	SOC_ENUM_EXT("PRI_TDM_RX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_RX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("PRI_TDM_TX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_RX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("SEC_TDM_TX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_RX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("TERT_TDM_TX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_RX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_0 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_1 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_2 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_3 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_4 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_5 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_6 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+	SOC_ENUM_EXT("QUAT_TDM_TX_7 Header Type", tdm_config_enum[1],
+			msm_dai_q6_tdm_header_type_get,
+			msm_dai_q6_tdm_header_type_put),
+};
+
+static const struct snd_kcontrol_new tdm_config_controls_header[] = {
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_RX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("PRI_TDM_TX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_RX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("SEC_TDM_TX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_RX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("TERT_TDM_TX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_RX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_0 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_1 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_2 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_3 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_4 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_5 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_6 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+	SOC_SINGLE_MULTI_EXT("QUAT_TDM_TX_7 Header",
+			SND_SOC_NOPM, 0, 0xFFFFFFFF, 0, 8,
+			msm_dai_q6_tdm_header_get,
+			msm_dai_q6_tdm_header_put),
+};
+
+static int msm_dai_q6_tdm_set_clk(
+		struct msm_dai_q6_tdm_dai_data *dai_data,
+		u16 port_id, bool enable)
+{
+	int rc = 0;
+
+	switch (dai_data->group_cfg.tdm_cfg.group_id) {
+	case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+		if (dai_data->clk_set.clk_freq_in_hz) {
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_PRI_TDM_IBIT;
+		} else
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_PRI_TDM_EBIT;
+		break;
+	case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+		if (dai_data->clk_set.clk_freq_in_hz) {
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_SEC_TDM_IBIT;
+		} else
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_SEC_TDM_EBIT;
+		break;
+	case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+		if (dai_data->clk_set.clk_freq_in_hz) {
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_TER_TDM_IBIT;
+		} else
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_TER_TDM_EBIT;
+		break;
+	case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+		if (dai_data->clk_set.clk_freq_in_hz) {
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_QUAD_TDM_IBIT;
+		} else
+			dai_data->clk_set.clk_id =
+				Q6AFE_LPASS_CLK_ID_QUAD_TDM_EBIT;
+		break;
+	default:
+		pr_err("%s: port id 0x%x not supported\n",
+			__func__, port_id);
+		return -EINVAL;
+	}
+	dai_data->clk_set.enable = enable;
+
+	rc = afe_set_lpass_clock_v2(port_id,
+		&dai_data->clk_set);
+	if (rc < 0)
+		pr_err("%s: afe lpass clock failed, err:%d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static int msm_dai_q6_dai_tdm_probe(struct snd_soc_dai *dai)
+{
+	int rc = 0;
+	struct msm_dai_q6_tdm_dai_data *tdm_dai_data =
+			dev_get_drvdata(dai->dev);
+	struct snd_kcontrol *data_format_kcontrol = NULL;
+	struct snd_kcontrol *header_type_kcontrol = NULL;
+	struct snd_kcontrol *header_kcontrol = NULL;
+	int port_idx = 0;
+	const struct snd_kcontrol_new *data_format_ctrl = NULL;
+	const struct snd_kcontrol_new *header_type_ctrl = NULL;
+	const struct snd_kcontrol_new *header_ctrl = NULL;
+
+	msm_dai_q6_set_dai_id(dai);
+
+	port_idx = msm_dai_q6_get_port_idx(dai->id);
+	if (port_idx < 0) {
+		dev_err(dai->dev, "%s port id 0x%x not supported\n",
+			__func__, dai->id);
+		rc = -EINVAL;
+		goto rtn;
+	}
+
+	data_format_ctrl =
+		&tdm_config_controls_data_format[port_idx];
+	header_type_ctrl =
+		&tdm_config_controls_header_type[port_idx];
+	header_ctrl =
+		&tdm_config_controls_header[port_idx];
+
+	if (data_format_ctrl) {
+		data_format_kcontrol = snd_ctl_new1(data_format_ctrl,
+					tdm_dai_data);
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 data_format_kcontrol);
+		if (rc < 0) {
+			dev_err(dai->dev, "%s: err add data format ctrl DAI = %s\n",
+				__func__, dai->name);
+			goto rtn;
+		}
+	}
+
+	if (header_type_ctrl) {
+		header_type_kcontrol = snd_ctl_new1(header_type_ctrl,
+					tdm_dai_data);
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 header_type_kcontrol);
+		if (rc < 0) {
+			if (data_format_kcontrol)
+				snd_ctl_remove(dai->component->card->snd_card,
+					data_format_kcontrol);
+			dev_err(dai->dev, "%s: err add header type ctrl DAI = %s\n",
+				__func__, dai->name);
+			goto rtn;
+		}
+	}
+
+	if (header_ctrl) {
+		header_kcontrol = snd_ctl_new1(header_ctrl,
+					tdm_dai_data);
+		rc = snd_ctl_add(dai->component->card->snd_card,
+				 header_kcontrol);
+		if (rc < 0) {
+			if (header_type_kcontrol)
+				snd_ctl_remove(dai->component->card->snd_card,
+					header_type_kcontrol);
+			if (data_format_kcontrol)
+				snd_ctl_remove(dai->component->card->snd_card,
+					data_format_kcontrol);
+			dev_err(dai->dev, "%s: err add header ctrl DAI = %s\n",
+				__func__, dai->name);
+			goto rtn;
+		}
+	}
+
+	rc = msm_dai_q6_dai_add_route(dai);
+
+rtn:
+	return rc;
+}
+
+
+static int msm_dai_q6_dai_tdm_remove(struct snd_soc_dai *dai)
+{
+	int rc = 0;
+	struct msm_dai_q6_tdm_dai_data *tdm_dai_data =
+		dev_get_drvdata(dai->dev);
+	u16 group_id = tdm_dai_data->group_cfg.tdm_cfg.group_id;
+	int group_idx = 0;
+	atomic_t *group_ref = NULL;
+
+	group_idx = msm_dai_q6_get_group_idx(dai->id);
+	if (group_idx < 0) {
+		dev_err(dai->dev, "%s port id 0x%x not supported\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	group_ref = &tdm_group_ref[group_idx];
+
+	/* If AFE port is still up, close it */
+	if (test_bit(STATUS_PORT_STARTED, tdm_dai_data->status_mask)) {
+		rc = afe_close(dai->id); /* can block */
+		if (rc < 0) {
+			dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n",
+				__func__, dai->id);
+		}
+		atomic_dec(group_ref);
+		clear_bit(STATUS_PORT_STARTED,
+			  tdm_dai_data->status_mask);
+
+		if (atomic_read(group_ref) == 0) {
+			rc = afe_port_group_enable(group_id,
+				NULL, false);
+			if (rc < 0) {
+				dev_err(dai->dev, "fail to disable AFE group 0x%x\n",
+					group_id);
+			}
+			rc = msm_dai_q6_tdm_set_clk(tdm_dai_data,
+				dai->id, false);
+			if (rc < 0) {
+				dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n",
+					__func__, dai->id);
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_tdm_set_tdm_slot(struct snd_soc_dai *dai,
+				unsigned int tx_mask,
+				unsigned int rx_mask,
+				int slots, int slot_width)
+{
+	int rc = 0;
+	struct msm_dai_q6_tdm_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	struct afe_param_id_group_device_tdm_cfg *tdm_group =
+		&dai_data->group_cfg.tdm_cfg;
+	unsigned int cap_mask;
+
+	dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id);
+
+	/* HW only supports 16 and 32 bit slot width configuration */
+	if ((slot_width != 16) && (slot_width != 32)) {
+		dev_err(dai->dev, "%s: invalid slot_width %d\n",
+			__func__, slot_width);
+		return -EINVAL;
+	}
+
+	/* HW only supports 16 and 8 slots configuration */
+	switch (slots) {
+	case 8:
+		cap_mask = 0xFF;
+		break;
+	case 16:
+		cap_mask = 0xFFFF;
+		break;
+	default:
+		dev_err(dai->dev, "%s: invalid slots %d\n",
+			__func__, slots);
+		return -EINVAL;
+	}
+
+	switch (dai->id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		tdm_group->nslots_per_frame = slots;
+		tdm_group->slot_width = slot_width;
+		tdm_group->slot_mask = rx_mask & cap_mask;
+		break;
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		tdm_group->nslots_per_frame = slots;
+		tdm_group->slot_width = slot_width;
+		tdm_group->slot_mask = tx_mask & cap_mask;
+		break;
+	default:
+		dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_tdm_set_channel_map(struct snd_soc_dai *dai,
+				unsigned int tx_num, unsigned int *tx_slot,
+				unsigned int rx_num, unsigned int *rx_slot)
+{
+	int rc = 0;
+	struct msm_dai_q6_tdm_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	struct afe_param_id_slot_mapping_cfg *slot_mapping =
+		&dai_data->port_cfg.slot_mapping;
+	int i = 0;
+
+	dev_dbg(dai->dev, "%s: dai id = 0x%x\n", __func__, dai->id);
+
+	switch (dai->id) {
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		if (!rx_slot) {
+			dev_err(dai->dev, "%s: rx slot not found\n", __func__);
+			return -EINVAL;
+		}
+		if (rx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+			dev_err(dai->dev, "%s: invalid rx num %d\n", __func__,
+				rx_num);
+			return -EINVAL;
+		}
+
+		for (i = 0; i < rx_num; i++)
+			slot_mapping->offset[i] = rx_slot[i];
+		for (i = rx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+			slot_mapping->offset[i] =
+				AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+		slot_mapping->num_channel = rx_num;
+		break;
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		if (!tx_slot) {
+			dev_err(dai->dev, "%s: tx slot not found\n", __func__);
+			return -EINVAL;
+		}
+		if (tx_num > AFE_PORT_MAX_AUDIO_CHAN_CNT) {
+			dev_err(dai->dev, "%s: invalid tx num %d\n", __func__,
+				tx_num);
+			return -EINVAL;
+		}
+
+		for (i = 0; i < tx_num; i++)
+			slot_mapping->offset[i] = tx_slot[i];
+		for (i = tx_num; i < AFE_PORT_MAX_AUDIO_CHAN_CNT; i++)
+			slot_mapping->offset[i] =
+				AFE_SLOT_MAPPING_OFFSET_INVALID;
+
+		slot_mapping->num_channel = tx_num;
+		break;
+	default:
+		dev_err(dai->dev, "%s: invalid dai id 0x%x\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	return rc;
+}
+
+static int msm_dai_q6_tdm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params,
+				struct snd_soc_dai *dai)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+
+	struct afe_param_id_group_device_tdm_cfg *tdm_group =
+		&dai_data->group_cfg.tdm_cfg;
+	struct afe_param_id_tdm_cfg *tdm =
+		&dai_data->port_cfg.tdm;
+	struct afe_param_id_slot_mapping_cfg *slot_mapping =
+		&dai_data->port_cfg.slot_mapping;
+	struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header =
+		&dai_data->port_cfg.custom_tdm_header;
+
+	pr_debug("%s: dev_name: %s\n",
+		__func__, dev_name(dai->dev));
+
+	if ((params_channels(params) == 0) ||
+		(params_channels(params) > 8)) {
+		dev_err(dai->dev, "%s: invalid param channels %d\n",
+			__func__, params_channels(params));
+		return -EINVAL;
+	}
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_data->bitwidth = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		dai_data->bitwidth = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dai_data->bitwidth = 32;
+		break;
+	default:
+		dev_err(dai->dev, "%s: invalid param format 0x%x\n",
+			__func__, params_format(params));
+		return -EINVAL;
+	}
+	dai_data->channels = params_channels(params);
+	dai_data->rate = params_rate(params);
+
+	/*
+	 * update tdm group config param
+	 * NOTE: group config is set to the same as slot config.
+	 */
+	tdm_group->bit_width = tdm_group->slot_width;
+	tdm_group->num_channels = tdm_group->nslots_per_frame;
+	tdm_group->sample_rate = dai_data->rate;
+
+	pr_debug("%s: TDM GROUP:\n"
+		"num_channels=%d sample_rate=%d bit_width=%d\n"
+		"nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n",
+		__func__,
+		tdm_group->num_channels,
+		tdm_group->sample_rate,
+		tdm_group->bit_width,
+		tdm_group->nslots_per_frame,
+		tdm_group->slot_width,
+		tdm_group->slot_mask);
+	pr_debug("%s: TDM GROUP:\n"
+		"port_id[0]=0x%x port_id[1]=0x%x port_id[2]=0x%x port_id[3]=0x%x\n"
+		"port_id[4]=0x%x port_id[5]=0x%x port_id[6]=0x%x port_id[7]=0x%x\n",
+		__func__,
+		tdm_group->port_id[0],
+		tdm_group->port_id[1],
+		tdm_group->port_id[2],
+		tdm_group->port_id[3],
+		tdm_group->port_id[4],
+		tdm_group->port_id[5],
+		tdm_group->port_id[6],
+		tdm_group->port_id[7]);
+
+	/*
+	 * update tdm config param
+	 * NOTE: channels/rate/bitwidth are per stream property
+	 */
+	tdm->num_channels = dai_data->channels;
+	tdm->sample_rate = dai_data->rate;
+	tdm->bit_width = dai_data->bitwidth;
+	/*
+	 * port slot config is the same as group slot config
+	 * port slot mask should be set according to offset
+	 */
+	tdm->nslots_per_frame = tdm_group->nslots_per_frame;
+	tdm->slot_width = tdm_group->slot_width;
+	tdm->slot_mask = tdm_group->slot_mask;
+
+	pr_debug("%s: TDM:\n"
+		"num_channels=%d sample_rate=%d bit_width=%d\n"
+		"nslots_per_frame=%d slot_width=%d slot_mask=0x%x\n"
+		"data_format=0x%x sync_mode=0x%x sync_src=0x%x\n"
+		"data_out=0x%x invert_sync=0x%x data_delay=0x%x\n",
+		__func__,
+		tdm->num_channels,
+		tdm->sample_rate,
+		tdm->bit_width,
+		tdm->nslots_per_frame,
+		tdm->slot_width,
+		tdm->slot_mask,
+		tdm->data_format,
+		tdm->sync_mode,
+		tdm->sync_src,
+		tdm->ctrl_data_out_enable,
+		tdm->ctrl_invert_sync_pulse,
+		tdm->ctrl_sync_data_delay);
+
+	/*
+	 * update slot mapping config param
+	 * NOTE: channels/rate/bitwidth are per stream property
+	 */
+	slot_mapping->bitwidth = dai_data->bitwidth;
+
+	pr_debug("%s: SLOT MAPPING:\n"
+		"num_channel=%d bitwidth=%d data_align=0x%x\n",
+		__func__,
+		slot_mapping->num_channel,
+		slot_mapping->bitwidth,
+		slot_mapping->data_align_type);
+	pr_debug("%s: SLOT MAPPING:\n"
+		"offset[0]=0x%x offset[1]=0x%x offset[2]=0x%x offset[3]=0x%x\n"
+		"offset[4]=0x%x offset[5]=0x%x offset[6]=0x%x offset[7]=0x%x\n",
+		__func__,
+		slot_mapping->offset[0],
+		slot_mapping->offset[1],
+		slot_mapping->offset[2],
+		slot_mapping->offset[3],
+		slot_mapping->offset[4],
+		slot_mapping->offset[5],
+		slot_mapping->offset[6],
+		slot_mapping->offset[7]);
+
+	/*
+	 * update custom header config param
+	 * NOTE: channels/rate/bitwidth are per playback stream property.
+	 * custom tdm header only applicable to playback stream.
+	 */
+	if (custom_tdm_header->header_type !=
+		AFE_CUSTOM_TDM_HEADER_TYPE_INVALID) {
+		pr_debug("%s: CUSTOM TDM HEADER:\n"
+			"start_offset=0x%x header_width=%d\n"
+			"num_frame_repeat=%d header_type=0x%x\n",
+			__func__,
+			custom_tdm_header->start_offset,
+			custom_tdm_header->header_width,
+			custom_tdm_header->num_frame_repeat,
+			custom_tdm_header->header_type);
+		pr_debug("%s: CUSTOM TDM HEADER:\n"
+			"header[0]=0x%x header[1]=0x%x header[2]=0x%x header[3]=0x%x\n"
+			"header[4]=0x%x header[5]=0x%x header[6]=0x%x header[7]=0x%x\n",
+			__func__,
+			custom_tdm_header->header[0],
+			custom_tdm_header->header[1],
+			custom_tdm_header->header[2],
+			custom_tdm_header->header[3],
+			custom_tdm_header->header[4],
+			custom_tdm_header->header[5],
+			custom_tdm_header->header[6],
+			custom_tdm_header->header[7]);
+	}
+
+	return 0;
+}
+
+static int msm_dai_q6_tdm_prepare(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	int rc = 0;
+	struct msm_dai_q6_tdm_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	u16 group_id = dai_data->group_cfg.tdm_cfg.group_id;
+	int group_idx = 0;
+	atomic_t *group_ref = NULL;
+
+	group_idx = msm_dai_q6_get_group_idx(dai->id);
+	if (group_idx < 0) {
+		dev_err(dai->dev, "%s port id 0x%x not supported\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&tdm_mutex);
+
+	group_ref = &tdm_group_ref[group_idx];
+
+	if (!test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		/* PORT START should be set if prepare called
+		 * in active state.
+		 */
+		if (atomic_read(group_ref) == 0) {
+			/* TX and RX share the same clk.
+			 * AFE clk is enabled per group to simplify the logic.
+			 * DSP will monitor the clk count.
+			 */
+			rc = msm_dai_q6_tdm_set_clk(dai_data,
+				dai->id, true);
+			if (rc < 0) {
+				dev_err(dai->dev, "%s: fail to enable AFE clk 0x%x\n",
+					__func__, dai->id);
+				goto rtn;
+			}
+			rc = afe_port_group_enable(group_id,
+				&dai_data->group_cfg, true);
+			if (rc < 0) {
+				dev_err(dai->dev, "%s: fail to enable AFE group 0x%x\n",
+					__func__, group_id);
+				goto rtn;
+			}
+		}
+
+		rc = afe_tdm_port_start(dai->id, &dai_data->port_cfg,
+			dai_data->rate);
+		if (rc < 0) {
+			if (atomic_read(group_ref) == 0) {
+				afe_port_group_enable(group_id,
+					NULL, false);
+				msm_dai_q6_tdm_set_clk(dai_data,
+					dai->id, false);
+			}
+			dev_err(dai->dev, "%s: fail to open AFE port 0x%x\n",
+				__func__, dai->id);
+		} else {
+			set_bit(STATUS_PORT_STARTED,
+				dai_data->status_mask);
+			atomic_inc(group_ref);
+		}
+
+		/* TODO: need to monitor PCM/MI2S/TDM HW status */
+		/* NOTE: AFE should error out if HW resource contention */
+
+	}
+
+rtn:
+	mutex_unlock(&tdm_mutex);
+	return rc;
+}
+
+static void msm_dai_q6_tdm_shutdown(struct snd_pcm_substream *substream,
+				     struct snd_soc_dai *dai)
+{
+	int rc = 0;
+	struct msm_dai_q6_tdm_dai_data *dai_data =
+		dev_get_drvdata(dai->dev);
+	u16 group_id = dai_data->group_cfg.tdm_cfg.group_id;
+	int group_idx = 0;
+	atomic_t *group_ref = NULL;
+
+	group_idx = msm_dai_q6_get_group_idx(dai->id);
+	if (group_idx < 0) {
+		dev_err(dai->dev, "%s port id 0x%x not supported\n",
+			__func__, dai->id);
+		return;
+	}
+
+	mutex_lock(&tdm_mutex);
+
+	group_ref = &tdm_group_ref[group_idx];
+
+	if (test_bit(STATUS_PORT_STARTED, dai_data->status_mask)) {
+		rc = afe_close(dai->id);
+		if (rc < 0) {
+			dev_err(dai->dev, "%s: fail to close AFE port 0x%x\n",
+				__func__, dai->id);
+		}
+		atomic_dec(group_ref);
+		clear_bit(STATUS_PORT_STARTED,
+			dai_data->status_mask);
+
+		if (atomic_read(group_ref) == 0) {
+			rc = afe_port_group_enable(group_id,
+				NULL, false);
+			if (rc < 0) {
+				dev_err(dai->dev, "%s: fail to disable AFE group 0x%x\n",
+					__func__, group_id);
+			}
+			rc = msm_dai_q6_tdm_set_clk(dai_data,
+				dai->id, false);
+			if (rc < 0) {
+				dev_err(dai->dev, "%s: fail to disable AFE clk 0x%x\n",
+					__func__, dai->id);
+			}
+		}
+
+		/* TODO: need to monitor PCM/MI2S/TDM HW status */
+		/* NOTE: AFE should error out if HW resource contention */
+
+	}
+
+	mutex_unlock(&tdm_mutex);
+}
+
+static struct snd_soc_dai_ops msm_dai_q6_tdm_ops = {
+	.prepare          = msm_dai_q6_tdm_prepare,
+	.hw_params        = msm_dai_q6_tdm_hw_params,
+	.set_tdm_slot     = msm_dai_q6_tdm_set_tdm_slot,
+	.set_channel_map  = msm_dai_q6_tdm_set_channel_map,
+	.shutdown         = msm_dai_q6_tdm_shutdown,
+};
+
+static struct snd_soc_dai_driver msm_dai_q6_tdm_dai[] = {
+	{
+		.playback = {
+			.stream_name = "Primary TDM0 Playback",
+			.aif_name = "PRI_TDM_RX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM1 Playback",
+			.aif_name = "PRI_TDM_RX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM2 Playback",
+			.aif_name = "PRI_TDM_RX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM3 Playback",
+			.aif_name = "PRI_TDM_RX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM4 Playback",
+			.aif_name = "PRI_TDM_RX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM5 Playback",
+			.aif_name = "PRI_TDM_RX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM6 Playback",
+			.aif_name = "PRI_TDM_RX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Primary TDM7 Playback",
+			.aif_name = "PRI_TDM_RX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_RX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM0 Capture",
+			.aif_name = "PRI_TDM_TX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM1 Capture",
+			.aif_name = "PRI_TDM_TX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM2 Capture",
+			.aif_name = "PRI_TDM_TX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM3 Capture",
+			.aif_name = "PRI_TDM_TX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM4 Capture",
+			.aif_name = "PRI_TDM_TX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM5 Capture",
+			.aif_name = "PRI_TDM_TX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM6 Capture",
+			.aif_name = "PRI_TDM_TX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Primary TDM7 Capture",
+			.aif_name = "PRI_TDM_TX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_PRIMARY_TDM_TX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM0 Playback",
+			.aif_name = "SEC_TDM_RX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM1 Playback",
+			.aif_name = "SEC_TDM_RX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM2 Playback",
+			.aif_name = "SEC_TDM_RX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM3 Playback",
+			.aif_name = "SEC_TDM_RX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM4 Playback",
+			.aif_name = "SEC_TDM_RX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM5 Playback",
+			.aif_name = "SEC_TDM_RX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM6 Playback",
+			.aif_name = "SEC_TDM_RX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Secondary TDM7 Playback",
+			.aif_name = "SEC_TDM_RX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_RX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM0 Capture",
+			.aif_name = "SEC_TDM_TX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM1 Capture",
+			.aif_name = "SEC_TDM_TX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM2 Capture",
+			.aif_name = "SEC_TDM_TX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM3 Capture",
+			.aif_name = "SEC_TDM_TX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM4 Capture",
+			.aif_name = "SEC_TDM_TX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM5 Capture",
+			.aif_name = "SEC_TDM_TX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM6 Capture",
+			.aif_name = "SEC_TDM_TX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Secondary TDM7 Capture",
+			.aif_name = "SEC_TDM_TX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_SECONDARY_TDM_TX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM0 Playback",
+			.aif_name = "TERT_TDM_RX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM1 Playback",
+			.aif_name = "TERT_TDM_RX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM2 Playback",
+			.aif_name = "TERT_TDM_RX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM3 Playback",
+			.aif_name = "TERT_TDM_RX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM4 Playback",
+			.aif_name = "TERT_TDM_RX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM5 Playback",
+			.aif_name = "TERT_TDM_RX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM6 Playback",
+			.aif_name = "TERT_TDM_RX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Tertiary TDM7 Playback",
+			.aif_name = "TERT_TDM_RX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_RX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM0 Capture",
+			.aif_name = "TERT_TDM_TX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM1 Capture",
+			.aif_name = "TERT_TDM_TX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM2 Capture",
+			.aif_name = "TERT_TDM_TX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM3 Capture",
+			.aif_name = "TERT_TDM_TX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM4 Capture",
+			.aif_name = "TERT_TDM_TX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM5 Capture",
+			.aif_name = "TERT_TDM_TX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM6 Capture",
+			.aif_name = "TERT_TDM_TX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Tertiary TDM7 Capture",
+			.aif_name = "TERT_TDM_TX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_TERTIARY_TDM_TX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM0 Playback",
+			.aif_name = "QUAT_TDM_RX_0",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM1 Playback",
+			.aif_name = "QUAT_TDM_RX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM2 Playback",
+			.aif_name = "QUAT_TDM_RX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM3 Playback",
+			.aif_name = "QUAT_TDM_RX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM4 Playback",
+			.aif_name = "QUAT_TDM_RX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM5 Playback",
+			.aif_name = "QUAT_TDM_RX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM6 Playback",
+			.aif_name = "QUAT_TDM_RX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "Quaternary TDM7 Playback",
+			.aif_name = "QUAT_TDM_RX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_RX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM0 Capture",
+			.aif_name = "QUAT_TDM_TX_0",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM1 Capture",
+			.aif_name = "QUAT_TDM_TX_1",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_1,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM2 Capture",
+			.aif_name = "QUAT_TDM_TX_2",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_2,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM3 Capture",
+			.aif_name = "QUAT_TDM_TX_3",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_3,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM4 Capture",
+			.aif_name = "QUAT_TDM_TX_4",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_4,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM5 Capture",
+			.aif_name = "QUAT_TDM_TX_5",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_5,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM6 Capture",
+			.aif_name = "QUAT_TDM_TX_6",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_6,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Quaternary TDM7 Capture",
+			.aif_name = "QUAT_TDM_TX_7",
+			.rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE,
+			.channels_min = 1,
+			.channels_max = 8,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_q6_tdm_ops,
+		.id = AFE_PORT_ID_QUATERNARY_TDM_TX_7,
+		.probe = msm_dai_q6_dai_tdm_probe,
+		.remove = msm_dai_q6_dai_tdm_remove,
+	},
+};
+
+static const struct snd_soc_component_driver msm_q6_tdm_dai_component = {
+	.name		= "msm-dai-q6-tdm",
+};
+
+static int msm_dai_q6_tdm_dev_probe(struct platform_device *pdev)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data = NULL;
+	struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header = NULL;
+	int rc = 0;
+	u32 tdm_dev_id = 0;
+	int port_idx = 0;
+
+	/* retrieve device/afe id */
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-dev-id",
+		&tdm_dev_id);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Device ID missing in DT file\n",
+			__func__);
+		goto rtn;
+	}
+	if ((tdm_dev_id < AFE_PORT_ID_TDM_PORT_RANGE_START) ||
+		(tdm_dev_id > AFE_PORT_ID_TDM_PORT_RANGE_END)) {
+		dev_err(&pdev->dev, "%s: Invalid TDM Device ID 0x%x in DT file\n",
+			__func__, tdm_dev_id);
+		rc = -ENXIO;
+		goto rtn;
+	}
+	pdev->id = tdm_dev_id;
+
+	dev_info(&pdev->dev, "%s: dev_name: %s dev_id: 0x%x\n",
+		__func__, dev_name(&pdev->dev), tdm_dev_id);
+
+	dai_data = kzalloc(sizeof(struct msm_dai_q6_tdm_dai_data),
+				GFP_KERNEL);
+	if (!dai_data) {
+		rc = -ENOMEM;
+		dev_err(&pdev->dev,
+			"%s Failed to allocate memory for tdm dai_data\n",
+			__func__);
+		goto rtn;
+	}
+	memset(dai_data, 0, sizeof(*dai_data));
+
+	/* TDM CFG */
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-sync-mode",
+		(u32 *)&dai_data->port_cfg.tdm.sync_mode);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Sync Mode from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-sync-mode");
+		goto free_dai_data;
+	}
+	dev_dbg(&pdev->dev, "%s: Sync Mode from DT file 0x%x\n",
+		__func__, dai_data->port_cfg.tdm.sync_mode);
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-sync-src",
+		(u32 *)&dai_data->port_cfg.tdm.sync_src);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Sync Src from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-sync-src");
+		goto free_dai_data;
+	}
+	dev_dbg(&pdev->dev, "%s: Sync Src from DT file 0x%x\n",
+		__func__, dai_data->port_cfg.tdm.sync_src);
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-data-out",
+		(u32 *)&dai_data->port_cfg.tdm.ctrl_data_out_enable);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Data Out from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-data-out");
+		goto free_dai_data;
+	}
+	dev_dbg(&pdev->dev, "%s: Data Out from DT file 0x%x\n",
+		__func__, dai_data->port_cfg.tdm.ctrl_data_out_enable);
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-invert-sync",
+		(u32 *)&dai_data->port_cfg.tdm.ctrl_invert_sync_pulse);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Invert Sync from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-invert-sync");
+		goto free_dai_data;
+	}
+	dev_dbg(&pdev->dev, "%s: Invert Sync from DT file 0x%x\n",
+		__func__, dai_data->port_cfg.tdm.ctrl_invert_sync_pulse);
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-data-delay",
+		(u32 *)&dai_data->port_cfg.tdm.ctrl_sync_data_delay);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Data Delay from DT file %s\n",
+			__func__, "qcom,msm-cpudai-tdm-data-delay");
+		goto free_dai_data;
+	}
+	dev_dbg(&pdev->dev, "%s: Data Delay from DT file 0x%x\n",
+		__func__, dai_data->port_cfg.tdm.ctrl_sync_data_delay);
+
+	/* TDM CFG -- set default */
+	dai_data->port_cfg.tdm.data_format = AFE_LINEAR_PCM_DATA;
+	dai_data->port_cfg.tdm.tdm_cfg_minor_version =
+		AFE_API_VERSION_TDM_CONFIG;
+
+	/* TDM SLOT MAPPING CFG */
+	rc = of_property_read_u32(pdev->dev.of_node,
+		"qcom,msm-cpudai-tdm-data-align",
+		&dai_data->port_cfg.slot_mapping.data_align_type);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: Data Align from DT file %s\n",
+			__func__,
+			"qcom,msm-cpudai-tdm-data-align");
+		goto free_dai_data;
+	}
+	dev_dbg(&pdev->dev, "%s: Data Align from DT file 0x%x\n",
+		__func__, dai_data->port_cfg.slot_mapping.data_align_type);
+
+	/* TDM SLOT MAPPING CFG -- set default */
+	dai_data->port_cfg.slot_mapping.minor_version =
+		AFE_API_VERSION_SLOT_MAPPING_CONFIG;
+
+	/* CUSTOM TDM HEADER CFG */
+	custom_tdm_header = &dai_data->port_cfg.custom_tdm_header;
+	if (of_find_property(pdev->dev.of_node,
+			"qcom,msm-cpudai-tdm-header-start-offset", NULL) &&
+		of_find_property(pdev->dev.of_node,
+			"qcom,msm-cpudai-tdm-header-width", NULL) &&
+		of_find_property(pdev->dev.of_node,
+			"qcom,msm-cpudai-tdm-header-num-frame-repeat", NULL)) {
+		/* if the property exist */
+		rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,msm-cpudai-tdm-header-start-offset",
+			(u32 *)&custom_tdm_header->start_offset);
+		if (rc) {
+			dev_err(&pdev->dev, "%s: Header Start Offset from DT file %s\n",
+				__func__,
+				"qcom,msm-cpudai-tdm-header-start-offset");
+			goto free_dai_data;
+		}
+		dev_dbg(&pdev->dev, "%s: Header Start Offset from DT file 0x%x\n",
+			__func__, custom_tdm_header->start_offset);
+
+		rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,msm-cpudai-tdm-header-width",
+			(u32 *)&custom_tdm_header->header_width);
+		if (rc) {
+			dev_err(&pdev->dev, "%s: Header Width from DT file %s\n",
+				__func__, "qcom,msm-cpudai-tdm-header-width");
+			goto free_dai_data;
+		}
+		dev_dbg(&pdev->dev, "%s: Header Width from DT file 0x%x\n",
+			__func__, custom_tdm_header->header_width);
+
+		rc = of_property_read_u32(pdev->dev.of_node,
+			"qcom,msm-cpudai-tdm-header-num-frame-repeat",
+			(u32 *)&custom_tdm_header->num_frame_repeat);
+		if (rc) {
+			dev_err(&pdev->dev, "%s: Header Num Frame Repeat from DT file %s\n",
+				__func__,
+				"qcom,msm-cpudai-tdm-header-num-frame-repeat");
+			goto free_dai_data;
+		}
+		dev_dbg(&pdev->dev, "%s: Header Num Frame Repeat from DT file 0x%x\n",
+			__func__, custom_tdm_header->num_frame_repeat);
+
+		/* CUSTOM TDM HEADER CFG -- set default */
+		custom_tdm_header->minor_version =
+			AFE_API_VERSION_CUSTOM_TDM_HEADER_CONFIG;
+		custom_tdm_header->header_type =
+			AFE_CUSTOM_TDM_HEADER_TYPE_INVALID;
+	} else {
+		dev_info(&pdev->dev,
+			"%s: Custom tdm header not supported\n", __func__);
+		/* CUSTOM TDM HEADER CFG -- set default */
+		custom_tdm_header->header_type =
+			AFE_CUSTOM_TDM_HEADER_TYPE_INVALID;
+		/* proceed with probe */
+	}
+
+	/* copy static clk per parent node */
+	dai_data->clk_set = tdm_clk_set;
+	/* copy static group cfg per parent node */
+	dai_data->group_cfg.tdm_cfg = tdm_group_cfg;
+
+	dev_set_drvdata(&pdev->dev, dai_data);
+
+	port_idx = msm_dai_q6_get_port_idx(tdm_dev_id);
+	if (port_idx < 0) {
+		dev_err(&pdev->dev, "%s Port id 0x%x not supported\n",
+			__func__, tdm_dev_id);
+		rc = -EINVAL;
+		goto free_dai_data;
+	}
+
+	rc = snd_soc_register_component(&pdev->dev,
+		&msm_q6_tdm_dai_component,
+		&msm_dai_q6_tdm_dai[port_idx], 1);
+
+	if (rc) {
+		dev_err(&pdev->dev, "%s: TDM dai 0x%x register failed, rc=%d\n",
+			__func__, tdm_dev_id, rc);
+		goto err_register;
+	}
+
+	return 0;
+
+err_register:
+free_dai_data:
+	kfree(dai_data);
+rtn:
+	return rc;
+}
+
+static int msm_dai_q6_tdm_dev_remove(struct platform_device *pdev)
+{
+	struct msm_dai_q6_tdm_dai_data *dai_data =
+		dev_get_drvdata(&pdev->dev);
+
+	snd_soc_unregister_component(&pdev->dev);
+
+	kfree(dai_data);
+
+	return 0;
+}
+
+static const struct of_device_id msm_dai_q6_tdm_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-q6-tdm", },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_q6_tdm_dev_dt_match);
+
+static struct platform_driver msm_dai_q6_tdm_driver = {
+	.probe  = msm_dai_q6_tdm_dev_probe,
+	.remove  = msm_dai_q6_tdm_dev_remove,
+	.driver = {
+		.name = "msm-dai-q6-tdm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_q6_tdm_dev_dt_match,
+	},
+};
+
+static int __init msm_dai_q6_init(void)
+{
+	int rc;
+
+	rc = platform_driver_register(&msm_auxpcm_dev_driver);
+	if (rc) {
+		pr_err("%s: fail to register auxpcm dev driver", __func__);
+		goto fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_q6);
+	if (rc) {
+		pr_err("%s: fail to register dai q6 driver", __func__);
+		goto dai_q6_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_q6_dev);
+	if (rc) {
+		pr_err("%s: fail to register dai q6 dev driver", __func__);
+		goto dai_q6_dev_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_q6_mi2s_driver);
+	if (rc) {
+		pr_err("%s: fail to register dai MI2S dev drv\n", __func__);
+		goto dai_q6_mi2s_drv_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_mi2s_q6);
+	if (rc) {
+		pr_err("%s: fail to register dai MI2S\n", __func__);
+		goto dai_mi2s_q6_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_q6_spdif_driver);
+	if (rc) {
+		pr_err("%s: fail to register dai SPDIF\n", __func__);
+		goto dai_spdif_q6_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_q6_tdm_driver);
+	if (rc) {
+		pr_err("%s: fail to register dai TDM dev drv\n", __func__);
+		goto dai_q6_tdm_drv_fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_tdm_q6);
+	if (rc) {
+		pr_err("%s: fail to register dai TDM\n", __func__);
+		goto dai_tdm_q6_fail;
+	}
+	return rc;
+
+dai_tdm_q6_fail:
+	platform_driver_unregister(&msm_dai_q6_tdm_driver);
+dai_q6_tdm_drv_fail:
+	platform_driver_unregister(&msm_dai_q6_spdif_driver);
+dai_spdif_q6_fail:
+	platform_driver_unregister(&msm_dai_mi2s_q6);
+dai_mi2s_q6_fail:
+	platform_driver_unregister(&msm_dai_q6_mi2s_driver);
+dai_q6_mi2s_drv_fail:
+	platform_driver_unregister(&msm_dai_q6_dev);
+dai_q6_dev_fail:
+	platform_driver_unregister(&msm_dai_q6);
+dai_q6_fail:
+	platform_driver_unregister(&msm_auxpcm_dev_driver);
+fail:
+	return rc;
+}
+module_init(msm_dai_q6_init);
+
+static void __exit msm_dai_q6_exit(void)
+{
+	platform_driver_unregister(&msm_dai_q6_dev);
+	platform_driver_unregister(&msm_dai_q6);
+	platform_driver_unregister(&msm_auxpcm_dev_driver);
+	platform_driver_unregister(&msm_dai_q6_spdif_driver);
+}
+module_exit(msm_dai_q6_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-slim.c b/sound/soc/msm/qdsp6v2/msm-dai-slim.c
new file mode 100644
index 0000000..77fb8d4
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-slim.c
@@ -0,0 +1,657 @@
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/bitops.h>
+#include <linux/slimbus/slimbus.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/msm-slim-dma.h>
+
+#define SLIM_DEV_NAME "msm-dai-slim"
+
+#define SLIM_DAI_RATES (SNDRV_PCM_RATE_48000 | \
+			SNDRV_PCM_RATE_8000 | \
+			SNDRV_PCM_RATE_16000 | \
+			SNDRV_PCM_RATE_96000 | \
+			SNDRV_PCM_RATE_192000 | \
+			SNDRV_PCM_RATE_384000)
+
+#define SLIM_DAI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+			  SNDRV_PCM_FMTBIT_S24_LE | \
+			  SNDRV_PCM_FMTBIT_S32_LE)
+
+#define DAI_STATE_INITIALIZED (0x01 << 0)
+#define DAI_STATE_PREPARED (0x01 << 1)
+#define DAI_STATE_RUNNING (0x01 << 2)
+
+#define SET_DAI_STATE(status, state) \
+	(status |= state)
+
+#define CLR_DAI_STATE(status, state) \
+	(status = status & (~state))
+
+enum {
+	MSM_DAI_SLIM0 = 0,
+	NUM_SLIM_DAIS,
+};
+
+struct msm_slim_dai_data {
+	unsigned int dai_id;
+	u16 *chan_h;
+	u16 *sh_ch;
+	u16 grph;
+	u32 rate;
+	u16 bits;
+	u16 ch_cnt;
+	u8 status;
+	struct snd_soc_dai_driver *dai_drv;
+	struct msm_slim_dma_data dma_data;
+	struct slim_port_cfg port_cfg;
+};
+
+struct msm_dai_slim_drv_data {
+	struct slim_device *sdev;
+	u16 num_dais;
+	struct msm_slim_dai_data slim_dai_data[NUM_SLIM_DAIS];
+};
+
+struct msm_slim_dai_data *msm_slim_get_dai_data(
+	struct msm_dai_slim_drv_data *drv_data,
+	struct snd_soc_dai *dai)
+{
+	struct msm_slim_dai_data *dai_data_t;
+	int i;
+
+	for (i = 0; i < drv_data->num_dais; i++) {
+		dai_data_t = &drv_data->slim_dai_data[i];
+		if (dai_data_t->dai_id == dai->id)
+			return dai_data_t;
+	}
+
+	dev_err(dai->dev,
+		"%s: no dai data found for dai_id %d\n",
+		__func__, dai->id);
+	return NULL;
+}
+
+static int msm_dai_slim_ch_ctl(struct msm_slim_dma_data *dma_data,
+	struct snd_soc_dai *dai, bool enable)
+{
+	struct slim_device *sdev;
+	struct msm_dai_slim_drv_data *drv_data;
+	struct msm_slim_dai_data *dai_data;
+	int rc, rc1, i;
+
+	if (!dma_data || !dma_data->sdev) {
+		pr_err("%s: Invalid %s\n", __func__,
+		       (!dma_data) ? "dma_data" : "slim_device");
+		return -EINVAL;
+	}
+
+	sdev = dma_data->sdev;
+	drv_data = dev_get_drvdata(&sdev->dev);
+	dai_data = msm_slim_get_dai_data(drv_data, dai);
+
+	if (!dai_data) {
+		dev_err(dai->dev,
+			"%s: Invalid dai_data for dai_id %d\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	dev_dbg(&sdev->dev,
+		"%s: enable = %s, rate = %u\n", __func__,
+		enable ? "true" : "false",
+		dai_data->rate);
+
+	if (enable) {
+		if (!(dai_data->status & DAI_STATE_PREPARED)) {
+			dev_err(&sdev->dev,
+				"%s: dai id (%d) has invalid state 0x%x\n",
+				__func__, dai->id, dai_data->status);
+			return -EINVAL;
+		}
+
+		rc = slim_alloc_mgrports(sdev,
+					 SLIM_REQ_DEFAULT, dai_data->ch_cnt,
+					 &(dma_data->ph),
+					 sizeof(dma_data->ph));
+		if (rc < 0) {
+			dev_err(&sdev->dev,
+				"%s:alloc mgrport failed rc %d\n",
+				__func__, rc);
+			goto done;
+		}
+
+		rc = slim_config_mgrports(sdev, &(dma_data->ph),
+					  dai_data->ch_cnt,
+					  &(dai_data->port_cfg));
+		if (rc < 0) {
+			dev_err(&sdev->dev,
+				"%s: config mgrport failed rc %d\n",
+				__func__, rc);
+			goto err_done;
+		}
+
+		for (i = 0; i < dai_data->ch_cnt; i++) {
+			rc = slim_connect_sink(sdev,
+					       &dma_data->ph, 1,
+					       dai_data->chan_h[i]);
+			if (rc < 0) {
+				dev_err(&sdev->dev,
+					"%s: slim_connect_sink failed, ch = %d, err = %d\n",
+					__func__, i, rc);
+				goto err_done;
+			}
+		}
+
+		rc = slim_control_ch(sdev,
+				     dai_data->grph,
+				     SLIM_CH_ACTIVATE, true);
+		if (rc < 0) {
+			dev_err(&sdev->dev,
+				"%s: slim activate ch failed, err = %d\n",
+				__func__, rc);
+			goto err_done;
+		}
+		/* Mark dai status as running */
+		SET_DAI_STATE(dai_data->status, DAI_STATE_RUNNING);
+	} else {
+		if (!(dai_data->status & DAI_STATE_RUNNING)) {
+			dev_err(&sdev->dev,
+				"%s: dai id (%d) has invalid state 0x%x\n",
+				__func__, dai->id, dai_data->status);
+			return -EINVAL;
+		}
+
+		rc = slim_control_ch(sdev,
+				     dai_data->grph,
+				     SLIM_CH_REMOVE, true);
+		if (rc < 0) {
+			dev_err(&sdev->dev,
+				"%s: slim activate ch failed, err = %d\n",
+				__func__, rc);
+			goto done;
+		}
+
+		rc = slim_dealloc_mgrports(sdev,
+					   &dma_data->ph, 1);
+		if (rc < 0) {
+			dev_err(&sdev->dev,
+				"%s: dealloc mgrport failed, err = %d\n",
+				__func__, rc);
+			goto done;
+		}
+		/* clear running state for dai*/
+		CLR_DAI_STATE(dai_data->status, DAI_STATE_RUNNING);
+	}
+
+	return rc;
+
+err_done:
+	rc1 = slim_dealloc_mgrports(sdev,
+				   &dma_data->ph, 1);
+	if (rc1 < 0)
+		dev_err(&sdev->dev,
+			"%s: dealloc mgrport failed, err = %d\n",
+			__func__, rc1);
+done:
+	return rc;
+}
+
+static int msm_dai_slim_hw_params(
+		struct snd_pcm_substream *substream,
+		struct snd_pcm_hw_params *params,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+	struct msm_slim_dai_data *dai_data;
+	int rc = 0;
+
+	dai_data = msm_slim_get_dai_data(drv_data, dai);
+	if (!dai_data) {
+		dev_err(dai->dev,
+			"%s: Invalid dai_data for dai_id %d\n",
+			__func__, dai->id);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (!dai_data->ch_cnt || dai_data->ch_cnt != params_channels(params)) {
+		dev_err(dai->dev, "%s: invalid ch_cnt %d %d\n",
+			__func__, dai_data->ch_cnt, params_channels(params));
+		rc = -EINVAL;
+		goto done;
+	}
+
+	dai_data->rate = params_rate(params);
+	dai_data->port_cfg.port_opts = SLIM_OPT_NONE;
+	if (dai_data->rate >= SNDRV_PCM_RATE_48000)
+		dai_data->port_cfg.watermark = 16;
+	else
+		dai_data->port_cfg.watermark = 8;
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S16_LE:
+		dai_data->bits = 16;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		dai_data->bits = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S32_LE:
+		dai_data->bits = 32;
+		break;
+	default:
+		dev_err(dai->dev, "%s: invalid format %d\n", __func__,
+			params_format(params));
+		rc = -EINVAL;
+		goto done;
+	}
+
+	dev_dbg(dai->dev, "%s: ch_cnt=%u rate=%u, bit_width = %u\n",
+		__func__, dai_data->ch_cnt, dai_data->rate,
+		dai_data->bits);
+done:
+	return rc;
+}
+
+static int msm_dai_slim_set_channel_map(struct snd_soc_dai *dai,
+	unsigned int tx_num, unsigned int *tx_slot,
+	unsigned int rx_num, unsigned int *rx_slot)
+{
+	struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+	struct msm_slim_dai_data *dai_data;
+	struct snd_soc_dai_driver *dai_drv;
+	u8 i = 0;
+
+	dev_dbg(dai->dev,
+		"%s: tx_num=%u, rx_num=%u\n",
+		__func__, tx_num, rx_num);
+
+	dai_data = msm_slim_get_dai_data(drv_data, dai);
+	if (!dai_data) {
+		dev_err(dai->dev,
+			"%s: Invalid dai_data for dai_id %d\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	dai_drv = dai_data->dai_drv;
+
+	if (tx_num > dai_drv->capture.channels_max) {
+		dev_err(dai->dev, "%s: tx_num %u max out master port cnt\n",
+			__func__, tx_num);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < tx_num; i++)
+		dai_data->sh_ch[i] = tx_slot[i];
+
+	dai_data->ch_cnt = tx_num;
+	return 0;
+}
+
+static int msm_dai_slim_prepare(struct snd_pcm_substream *substream,
+				   struct snd_soc_dai *dai)
+{
+	struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+	struct msm_slim_dma_data *dma_data;
+	struct msm_slim_dai_data *dai_data = NULL;
+	struct slim_ch prop;
+	int rc;
+	u8 i, j;
+
+	dai_data = msm_slim_get_dai_data(drv_data, dai);
+	if (!dai_data) {
+		dev_err(dai->dev,
+			"%s: Invalid dai_data for dai %d\n",
+			__func__, dai->id);
+		return -EINVAL;
+	}
+
+	if (!(dai_data->status & DAI_STATE_INITIALIZED)) {
+		dev_err(dai->dev,
+			"%s: dai id (%d) has invalid state 0x%x\n",
+			__func__, dai->id, dai_data->status);
+		return -EINVAL;
+	}
+
+	dma_data = &dai_data->dma_data;
+	snd_soc_dai_set_dma_data(dai, substream, dma_data);
+
+	for (i = 0; i < dai_data->ch_cnt; i++) {
+		rc = slim_query_ch(drv_data->sdev, dai_data->sh_ch[i],
+				   &dai_data->chan_h[i]);
+		if (rc) {
+			dev_err(dai->dev, "%s:query chan handle failed rc %d\n",
+				__func__, rc);
+			goto error_chan_query;
+		}
+	}
+
+	prop.prot = SLIM_AUTO_ISO;
+	prop.baser = SLIM_RATE_4000HZ;
+	prop.dataf = SLIM_CH_DATAF_NOT_DEFINED;
+	prop.auxf = SLIM_CH_AUXF_NOT_APPLICABLE;
+	prop.ratem = (dai_data->rate/4000);
+	prop.sampleszbits = dai_data->bits;
+
+	rc = slim_define_ch(drv_data->sdev, &prop, dai_data->chan_h,
+			    dai_data->ch_cnt, true, &dai_data->grph);
+
+	if (rc) {
+		dev_err(dai->dev, "%s:define chan failed rc %d\n",
+				__func__, rc);
+		goto error_define_chan;
+	}
+
+	/* Mark stream status as prepared */
+	SET_DAI_STATE(dai_data->status, DAI_STATE_PREPARED);
+
+	return rc;
+
+error_define_chan:
+error_chan_query:
+	for (j = 0; j < i; j++)
+		slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[j]);
+	return rc;
+}
+
+static void msm_dai_slim_shutdown(struct snd_pcm_substream *stream,
+		struct snd_soc_dai *dai)
+{
+	struct msm_dai_slim_drv_data *drv_data = dev_get_drvdata(dai->dev);
+	struct msm_slim_dma_data *dma_data = NULL;
+	struct msm_slim_dai_data *dai_data;
+	int i, rc = 0;
+
+	dai_data = msm_slim_get_dai_data(drv_data, dai);
+	dma_data = snd_soc_dai_get_dma_data(dai, stream);
+	if (!dma_data || !dai_data) {
+		dev_err(dai->dev,
+			"%s: Invalid %s\n", __func__,
+			(!dma_data) ? "dma_data" : "dai_data");
+		return;
+	}
+
+	if ((!(dai_data->status & DAI_STATE_PREPARED)) ||
+	     dai_data->status & DAI_STATE_RUNNING) {
+		dev_err(dai->dev,
+			"%s: dai id (%d) has invalid state 0x%x\n",
+			__func__, dai->id, dai_data->status);
+		return;
+	}
+
+	for (i = 0; i < dai_data->ch_cnt; i++) {
+		rc = slim_dealloc_ch(drv_data->sdev, dai_data->chan_h[i]);
+		if (rc) {
+			dev_err(dai->dev,
+				"%s: dealloc_ch failed, err = %d\n",
+				__func__, rc);
+		}
+	}
+
+	snd_soc_dai_set_dma_data(dai, stream, NULL);
+	/* clear prepared state for the dai */
+	CLR_DAI_STATE(dai_data->status, DAI_STATE_PREPARED);
+}
+
+static const struct snd_soc_component_driver msm_dai_slim_component = {
+	.name		= "msm-dai-slim-cmpnt",
+};
+
+static struct snd_soc_dai_ops msm_dai_slim_ops = {
+	.prepare	= msm_dai_slim_prepare,
+	.hw_params	= msm_dai_slim_hw_params,
+	.shutdown	= msm_dai_slim_shutdown,
+	.set_channel_map = msm_dai_slim_set_channel_map,
+};
+
+static struct snd_soc_dai_driver msm_slim_dais[] = {
+	{
+		/*
+		 * The first dai name should be same as device name
+		 * to support registering single and multile dais.
+		 */
+		.name = SLIM_DEV_NAME,
+		.id = MSM_DAI_SLIM0,
+		.capture = {
+			.rates = SLIM_DAI_RATES,
+			.formats = SLIM_DAI_FORMATS,
+			.channels_min = 1,
+			/*
+			 * max channels allowed is
+			 * dependent on platform and
+			 * will be updated before this
+			 * dai driver is registered.
+			 */
+			.channels_max = 1,
+			.rate_min = 8000,
+			.rate_max = 384000,
+			.stream_name = "SLIM_DAI0 Capture",
+		},
+		.ops = &msm_dai_slim_ops,
+	},
+	/*
+	 * If multiple dais are needed,
+	 * add dais here and update the
+	 * dai_id enum.
+	 */
+};
+
+static void msm_dai_slim_remove_dai_data(
+		struct device *dev,
+		struct msm_dai_slim_drv_data *drv_data)
+{
+	int i;
+	struct msm_slim_dai_data *dai_data_t;
+
+	for (i = 0; i < drv_data->num_dais; i++) {
+		dai_data_t = &drv_data->slim_dai_data[i];
+
+		kfree(dai_data_t->chan_h);
+		dai_data_t->chan_h = NULL;
+		kfree(dai_data_t->sh_ch);
+		dai_data_t->sh_ch = NULL;
+	}
+}
+
+static int msm_dai_slim_populate_dai_data(struct device *dev,
+		struct msm_dai_slim_drv_data *drv_data)
+{
+	struct snd_soc_dai_driver *dai_drv;
+	struct msm_slim_dai_data *dai_data_t;
+	u8 num_ch;
+	int i, j, rc;
+
+	for (i = 0; i < drv_data->num_dais; i++) {
+		num_ch = 0;
+		dai_drv = &msm_slim_dais[i];
+		num_ch += dai_drv->capture.channels_max;
+		num_ch += dai_drv->playback.channels_max;
+
+		dai_data_t = &drv_data->slim_dai_data[i];
+		dai_data_t->dai_drv = dai_drv;
+		dai_data_t->dai_id = dai_drv->id;
+		dai_data_t->dma_data.sdev = drv_data->sdev;
+		dai_data_t->dma_data.dai_channel_ctl =
+				msm_dai_slim_ch_ctl;
+		SET_DAI_STATE(dai_data_t->status,
+			      DAI_STATE_INITIALIZED);
+
+		dai_data_t->chan_h = devm_kzalloc(dev,
+					sizeof(u16) * num_ch,
+					GFP_KERNEL);
+		if (!dai_data_t->chan_h) {
+			dev_err(dev,
+				"%s: DAI ID %d, Failed to alloc channel handles\n",
+				__func__, i);
+			rc = -ENOMEM;
+			goto err_mem_alloc;
+		}
+
+		dai_data_t->sh_ch = devm_kzalloc(dev,
+					sizeof(u16) * num_ch,
+					GFP_KERNEL);
+		if (!dai_data_t->sh_ch) {
+			dev_err(dev,
+				"%s: DAI ID %d, Failed to alloc sh_ch\n",
+				__func__, i);
+			rc = -ENOMEM;
+			goto err_mem_alloc;
+		}
+	}
+	return 0;
+
+err_mem_alloc:
+	for (j = 0; j < i; j++) {
+		dai_data_t = &drv_data->slim_dai_data[i];
+
+		devm_kfree(dev, dai_data_t->chan_h);
+		dai_data_t->chan_h = NULL;
+
+		devm_kfree(dev, dai_data_t->sh_ch);
+		dai_data_t->sh_ch = NULL;
+	}
+	return rc;
+}
+
+static int msm_dai_slim_dev_probe(struct slim_device *sdev)
+{
+	int rc, i;
+	u8 max_channels;
+	u32 apps_ch_pipes;
+	struct msm_dai_slim_drv_data *drv_data;
+	struct device *dev = &sdev->dev;
+	struct snd_soc_dai_driver *dai_drv;
+
+	if (!dev->of_node ||
+	    !dev->of_node->parent) {
+		dev_err(dev,
+			"%s: Invalid %s\n", __func__,
+			(!dev->of_node) ? "of_node" : "parent_of_node");
+		return -EINVAL;
+	}
+
+	rc = of_property_read_u32(dev->of_node->parent,
+					 "qcom,apps-ch-pipes",
+					 &apps_ch_pipes);
+	if (rc) {
+		dev_err(dev,
+			"%s: Failed to lookup property %s in node %s, err = %d\n",
+			__func__, "qcom,apps-ch-pipes",
+			dev->of_node->parent->full_name, rc);
+		goto err_ret;
+	}
+
+	max_channels = hweight_long(apps_ch_pipes);
+	if (max_channels <= 0) {
+		dev_err(dev,
+			"%s: Invalid apps owned ports %d\n",
+			__func__, max_channels);
+		goto err_ret;
+	}
+
+	dev_dbg(dev, "%s: max channels = %u\n",
+		__func__, max_channels);
+
+	for (i = 0; i < ARRAY_SIZE(msm_slim_dais); i++) {
+		dai_drv = &msm_slim_dais[i];
+		dai_drv->capture.channels_max = max_channels;
+		dai_drv->playback.channels_max = max_channels;
+	}
+
+	drv_data = devm_kzalloc(dev, sizeof(*drv_data),
+				GFP_KERNEL);
+	if (!drv_data) {
+		rc = -ENOMEM;
+		goto err_ret;
+	}
+
+	drv_data->sdev = sdev;
+	drv_data->num_dais = NUM_SLIM_DAIS;
+
+	rc = msm_dai_slim_populate_dai_data(dev, drv_data);
+	if (rc) {
+		dev_err(dev,
+			"%s: failed to setup dai_data, err = %d\n",
+			__func__, rc);
+		goto err_populate_dai;
+	}
+
+	rc = snd_soc_register_component(&sdev->dev, &msm_dai_slim_component,
+					msm_slim_dais, NUM_SLIM_DAIS);
+	if (rc < 0) {
+		dev_err(dev, "%s: failed to register DAI, err = %d\n",
+			__func__, rc);
+		goto err_reg_comp;
+	}
+
+	dev_set_drvdata(dev, drv_data);
+	return rc;
+
+err_reg_comp:
+	msm_dai_slim_remove_dai_data(dev, drv_data);
+
+err_populate_dai:
+	devm_kfree(dev, drv_data);
+
+err_ret:
+	return rc;
+}
+
+static int msm_dai_slim_dev_remove(struct slim_device *sdev)
+{
+	snd_soc_unregister_component(&sdev->dev);
+	return 0;
+}
+
+static const struct slim_device_id msm_dai_slim_dt_match[] = {
+	{SLIM_DEV_NAME, 0 },
+	{}
+};
+
+static struct slim_driver msm_dai_slim_driver = {
+	.driver = {
+		.name = SLIM_DEV_NAME,
+		.owner = THIS_MODULE,
+	},
+	.probe = msm_dai_slim_dev_probe,
+	.remove = msm_dai_slim_dev_remove,
+	.id_table = msm_dai_slim_dt_match,
+};
+
+static int __init msm_dai_slim_init(void)
+{
+	int rc;
+
+	rc = slim_driver_register(&msm_dai_slim_driver);
+	if (rc)
+		pr_err("%s: failed to register with slimbus driver rc = %d",
+			__func__, rc);
+	return rc;
+}
+module_init(msm_dai_slim_init);
+
+static void __exit msm_dai_slim_exit(void)
+{
+}
+module_exit(msm_dai_slim_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Slimbus apps-owned channel handling driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c b/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c
new file mode 100644
index 0000000..3a2c3d3
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dai-stub-v2.c
@@ -0,0 +1,394 @@
+/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+
+enum {
+	STUB_RX,
+	STUB_TX,
+	STUB_1_RX,
+	STUB_1_TX,
+	STUB_DTMF_TX,
+	STUB_HOST_RX_CAPTURE_TX,
+	STUB_HOST_RX_PLAYBACK_RX,
+	STUB_HOST_TX_CAPTURE_TX,
+	STUB_HOST_TX_PLAYBACK_RX,
+};
+
+static int msm_dai_stub_set_channel_map(struct snd_soc_dai *dai,
+		unsigned int tx_num, unsigned int *tx_slot,
+		unsigned int rx_num, unsigned int *rx_slot)
+{
+	pr_debug("%s:\n", __func__);
+
+	return 0;
+}
+
+static struct snd_soc_dai_ops msm_dai_stub_ops = {
+	.set_channel_map = msm_dai_stub_set_channel_map,
+};
+
+static int msm_dai_stub_add_route(struct snd_soc_dai *dai)
+{
+	struct snd_soc_dapm_route intercon;
+	struct snd_soc_dapm_context *dapm;
+
+	if (!dai || !dai->driver) {
+		pr_err("%s Invalid params\n", __func__);
+		return -EINVAL;
+	}
+	dapm = snd_soc_component_get_dapm(dai->component);
+	memset(&intercon, 0, sizeof(intercon));
+	if (dai->driver->playback.stream_name &&
+		dai->driver->playback.aif_name) {
+		dev_dbg(dai->dev, "%s add route for widget %s",
+			__func__, dai->driver->playback.stream_name);
+		intercon.source = dai->driver->playback.aif_name;
+		intercon.sink = dai->driver->playback.stream_name;
+		dev_dbg(dai->dev, "%s src %s sink %s\n",
+			__func__, intercon.source, intercon.sink);
+		snd_soc_dapm_add_routes(dapm, &intercon, 1);
+	}
+	if (dai->driver->capture.stream_name &&
+		dai->driver->capture.aif_name) {
+		dev_dbg(dai->dev, "%s add route for widget %s",
+			__func__, dai->driver->capture.stream_name);
+		intercon.sink = dai->driver->capture.aif_name;
+		intercon.source = dai->driver->capture.stream_name;
+		dev_dbg(dai->dev, "%s src %s sink %s\n",
+			__func__, intercon.source, intercon.sink);
+		snd_soc_dapm_add_routes(dapm, &intercon, 1);
+	}
+	return 0;
+}
+
+static int msm_dai_stub_dai_probe(struct snd_soc_dai *dai)
+{
+	return msm_dai_stub_add_route(dai);
+}
+
+static int msm_dai_stub_dai_remove(struct snd_soc_dai *dai)
+{
+	pr_debug("%s:\n", __func__);
+	return 0;
+}
+
+static struct snd_soc_dai_driver msm_dai_stub_dai_rx = {
+	.playback = {
+		.stream_name = "Stub Playback",
+		.aif_name = "STUB_RX",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &msm_dai_stub_ops,
+	.probe = &msm_dai_stub_dai_probe,
+	.remove = &msm_dai_stub_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_dai_tx[] = {
+	{
+		.capture = {
+			.stream_name = "Stub Capture",
+			.aif_name = "STUB_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_stub_ops,
+		.probe = &msm_dai_stub_dai_probe,
+		.remove = &msm_dai_stub_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "Stub1 Capture",
+			.aif_name = "STUB_1_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_stub_ops,
+		.probe = &msm_dai_stub_dai_probe,
+		.remove = &msm_dai_stub_dai_remove,
+	}
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_dtmf_tx_dai = {
+	.capture = {
+		.stream_name = "DTMF TX",
+		.aif_name = "STUB_DTMF_TX",
+		.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+			 SNDRV_PCM_RATE_16000,
+		.formats = SNDRV_PCM_FMTBIT_S16_LE,
+		.channels_min = 1,
+		.channels_max = 2,
+		.rate_min = 8000,
+		.rate_max = 48000,
+	},
+	.ops = &msm_dai_stub_ops,
+	.probe = &msm_dai_stub_dai_probe,
+	.remove = &msm_dai_stub_dai_remove,
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_host_capture_tx_dai[] = {
+	{
+		.capture = {
+			.stream_name = "CS-VOICE HOST RX CAPTURE",
+			.aif_name = "STUB_HOST_RX_CAPTURE_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_stub_ops,
+		.probe = &msm_dai_stub_dai_probe,
+		.remove = &msm_dai_stub_dai_remove,
+	},
+	{
+		.capture = {
+			.stream_name = "CS-VOICE HOST TX CAPTURE",
+			.aif_name = "STUB_HOST_TX_CAPTURE_TX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_stub_ops,
+		.probe = &msm_dai_stub_dai_probe,
+		.remove = &msm_dai_stub_dai_remove,
+	},
+};
+
+static struct snd_soc_dai_driver msm_dai_stub_host_playback_rx_dai[] = {
+	{
+		.playback = {
+			.stream_name = "CS-VOICE HOST RX PLAYBACK",
+			.aif_name = "STUB_HOST_RX_PLAYBACK_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				 SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_stub_ops,
+		.probe = &msm_dai_stub_dai_probe,
+		.remove = &msm_dai_stub_dai_remove,
+	},
+	{
+		.playback = {
+			.stream_name = "CS-VOICE HOST TX PLAYBACK",
+			.aif_name = "STUB_HOST_TX_PLAYBACK_RX",
+			.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_8000 |
+				 SNDRV_PCM_RATE_16000,
+			.formats = SNDRV_PCM_FMTBIT_S16_LE,
+			.channels_min = 1,
+			.channels_max = 2,
+			.rate_min = 8000,
+			.rate_max = 48000,
+		},
+		.ops = &msm_dai_stub_ops,
+		.probe = &msm_dai_stub_dai_probe,
+		.remove = &msm_dai_stub_dai_remove,
+	},
+};
+
+static const struct snd_soc_component_driver msm_dai_stub_component = {
+	.name		= "msm-dai-stub-dev",
+};
+
+static int msm_dai_stub_dev_probe(struct platform_device *pdev)
+{
+	int rc, id = -1;
+	const char *stub_dev_id = "qcom,msm-dai-stub-dev-id";
+
+	rc = of_property_read_u32(pdev->dev.of_node, stub_dev_id, &id);
+	if (rc) {
+		dev_err(&pdev->dev,
+			"%s: missing %s in dt node\n", __func__, stub_dev_id);
+		return rc;
+	}
+
+	pdev->id = id;
+
+	pr_debug("%s: dev name %s, id:%d\n", __func__,
+		 dev_name(&pdev->dev), pdev->id);
+
+	switch (id) {
+	case STUB_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component, &msm_dai_stub_dai_rx, 1);
+		break;
+	case STUB_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component, &msm_dai_stub_dai_tx[0], 1);
+		break;
+	case STUB_1_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component, &msm_dai_stub_dai_tx[1], 1);
+		break;
+	case STUB_DTMF_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component,
+			&msm_dai_stub_dtmf_tx_dai, 1);
+		break;
+	case STUB_HOST_RX_CAPTURE_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component,
+			&msm_dai_stub_host_capture_tx_dai[0], 1);
+		break;
+	case STUB_HOST_TX_CAPTURE_TX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component,
+			&msm_dai_stub_host_capture_tx_dai[1], 1);
+		break;
+	case STUB_HOST_RX_PLAYBACK_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component,
+			&msm_dai_stub_host_playback_rx_dai[0], 1);
+		break;
+	case STUB_HOST_TX_PLAYBACK_RX:
+		rc = snd_soc_register_component(&pdev->dev,
+			&msm_dai_stub_component,
+			&msm_dai_stub_host_playback_rx_dai[1], 1);
+		break;
+	}
+
+	return rc;
+}
+
+static int msm_dai_stub_dev_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_component(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_dai_stub_dev_dt_match[] = {
+	{ .compatible = "qcom,msm-dai-stub-dev", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, msm_dai_stub_dev_dt_match);
+
+static struct platform_driver msm_dai_stub_dev = {
+	.probe  = msm_dai_stub_dev_probe,
+	.remove = msm_dai_stub_dev_remove,
+	.driver = {
+		.name = "msm-dai-stub-dev",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_stub_dev_dt_match,
+	},
+};
+
+static int msm_dai_stub_probe(struct platform_device *pdev)
+{
+	int rc = 0;
+
+	dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+
+	rc = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: failed to add child nodes, rc=%d\n",
+			__func__, rc);
+	} else
+		dev_dbg(&pdev->dev, "%s: added child node\n", __func__);
+
+	return rc;
+}
+
+static int msm_dai_stub_remove(struct platform_device *pdev)
+{
+	pr_debug("%s:\n", __func__);
+
+	return 0;
+}
+
+static const struct of_device_id msm_dai_stub_dt_match[] = {
+	{.compatible = "qcom,msm-dai-stub"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_dai_stub_dt_match);
+
+
+static struct platform_driver msm_dai_stub_driver = {
+	.probe  = msm_dai_stub_probe,
+	.remove = msm_dai_stub_remove,
+	.driver = {
+		.name = "msm-dai-stub",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_dai_stub_dt_match,
+	},
+};
+
+static int __init msm_dai_stub_init(void)
+{
+	int rc = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	rc = platform_driver_register(&msm_dai_stub_driver);
+	if (rc) {
+		pr_err("%s: fail to register dai q6 driver", __func__);
+		goto fail;
+	}
+
+	rc = platform_driver_register(&msm_dai_stub_dev);
+	if (rc) {
+		pr_err("%s: fail to register dai q6 dev driver", __func__);
+		goto dai_stub_dev_fail;
+	}
+	return rc;
+
+dai_stub_dev_fail:
+	platform_driver_unregister(&msm_dai_stub_driver);
+fail:
+	return rc;
+}
+module_init(msm_dai_stub_init);
+
+static void __exit msm_dai_stub_exit(void)
+{
+	pr_debug("%s:\n", __func__);
+
+	platform_driver_unregister(&msm_dai_stub_dev);
+	platform_driver_unregister(&msm_dai_stub_driver);
+}
+module_exit(msm_dai_stub_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("MSM Stub DSP DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-common.h b/sound/soc/msm/qdsp6v2/msm-dolby-common.h
new file mode 100644
index 0000000..f14e42e
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-common.h
@@ -0,0 +1,266 @@
+
+/* Copyright (c) 2013-2014, 2016 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DOLBY_COMMON_H_
+#define _MSM_DOLBY_COMMON_H_
+
+#include <sound/soc.h>
+
+
+#define DOLBY_BUNDLE_MODULE_ID		0x00010723
+#define DOLBY_VISUALIZER_MODULE_ID	0x0001072B
+
+#define DOLBY_PARAM_ID_VDHE		0x0001074D
+#define DOLBY_PARAM_ID_VSPE		0x00010750
+#define DOLBY_PARAM_ID_DSSF		0x00010753
+#define DOLBY_PARAM_ID_DVLI		0x0001073E
+#define DOLBY_PARAM_ID_DVLO		0x0001073F
+#define DOLBY_PARAM_ID_DVLE		0x0001073C
+#define DOLBY_PARAM_ID_DVMC		0x00010741
+#define DOLBY_PARAM_ID_DVME		0x00010740
+#define DOLBY_PARAM_ID_IENB		0x00010744
+#define DOLBY_PARAM_ID_IEBF		0x00010745
+#define DOLBY_PARAM_ID_IEON		0x00010743
+#define DOLBY_PARAM_ID_DEON		0x00010738
+#define DOLBY_PARAM_ID_NGON		0x00010736
+#define DOLBY_PARAM_ID_GEON		0x00010748
+#define DOLBY_PARAM_ID_GENB		0x00010749
+#define DOLBY_PARAM_ID_GEBF		0x0001074A
+#define DOLBY_PARAM_ID_AONB		0x0001075B
+#define DOLBY_PARAM_ID_AOBF		0x0001075C
+#define DOLBY_PARAM_ID_AOBG		0x0001075D
+#define DOLBY_PARAM_ID_AOON		0x00010759
+#define DOLBY_PARAM_ID_ARNB		0x0001075F
+#define DOLBY_PARAM_ID_ARBF		0x00010760
+#define DOLBY_PARAM_ID_PLB		0x00010768
+#define DOLBY_PARAM_ID_PLMD		0x00010767
+#define DOLBY_PARAM_ID_DHSB		0x0001074E
+#define DOLBY_PARAM_ID_DHRG		0x0001074F
+#define DOLBY_PARAM_ID_DSSB		0x00010751
+#define DOLBY_PARAM_ID_DSSA		0x00010752
+#define DOLBY_PARAM_ID_DVLA		0x0001073D
+#define DOLBY_PARAM_ID_IEBT		0x00010746
+#define DOLBY_PARAM_ID_IEA		0x0001076A
+#define DOLBY_PARAM_ID_DEA		0x00010739
+#define DOLBY_PARAM_ID_DED		0x0001073A
+#define DOLBY_PARAM_ID_GEBG		0x0001074B
+#define DOLBY_PARAM_ID_AOCC		0x0001075A
+#define DOLBY_PARAM_ID_ARBI		0x00010761
+#define DOLBY_PARAM_ID_ARBL		0x00010762
+#define DOLBY_PARAM_ID_ARBH		0x00010763
+#define DOLBY_PARAM_ID_AROD		0x00010764
+#define DOLBY_PARAM_ID_ARTP		0x00010765
+#define DOLBY_PARAM_ID_VMON		0x00010756
+#define DOLBY_PARAM_ID_VMB		0x00010757
+#define DOLBY_PARAM_ID_VCNB		0x00010733
+#define DOLBY_PARAM_ID_VCBF		0x00010734
+#define DOLBY_PARAM_ID_PREG		0x00010728
+#define DOLBY_PARAM_ID_VEN		0x00010732
+#define DOLBY_PARAM_ID_PSTG		0x00010729
+#define DOLBY_PARAM_ID_INIT_ENDP	0x00010727
+
+/* Not Used with Set Param kcontrol, only to query using Get Param */
+#define DOLBY_PARAM_ID_VER		0x00010726
+
+#define DOLBY_PARAM_ID_VCBG		0x00010730
+#define DOLBY_PARAM_ID_VCBE		0x00010731
+
+/* DOLBY DAP control params */
+#define DOLBY_COMMIT_ALL_TO_DSP		0x70000001
+#define DOLBY_COMMIT_TO_DSP		0x70000002
+#define DOLBY_USE_CACHE			0x70000003
+#define DOLBY_AUTO_ENDP			0x70000004
+#define DOLBY_AUTO_ENDDEP_PARAMS	0x70000005
+#define DOLBY_DAP_BYPASS		0x70000006
+
+#define DOLBY_ENABLE_CUSTOM_STEREO	0x000108c7
+
+/* DOLBY DAP offsets start */
+#define DOLBY_PARAM_VDHE_LENGTH   1
+#define DOLBY_PARAM_VDHE_OFFSET   0
+#define DOLBY_PARAM_VSPE_LENGTH   1
+#define DOLBY_PARAM_VSPE_OFFSET   (DOLBY_PARAM_VDHE_OFFSET + \
+					DOLBY_PARAM_VDHE_LENGTH)
+#define DOLBY_PARAM_DSSF_LENGTH   1
+#define DOLBY_PARAM_DSSF_OFFSET   (DOLBY_PARAM_VSPE_OFFSET + \
+					DOLBY_PARAM_VSPE_LENGTH)
+#define DOLBY_PARAM_DVLI_LENGTH   1
+#define DOLBY_PARAM_DVLI_OFFSET   (DOLBY_PARAM_DSSF_OFFSET + \
+					DOLBY_PARAM_DSSF_LENGTH)
+#define DOLBY_PARAM_DVLO_LENGTH   1
+#define DOLBY_PARAM_DVLO_OFFSET   (DOLBY_PARAM_DVLI_OFFSET + \
+					DOLBY_PARAM_DVLI_LENGTH)
+#define DOLBY_PARAM_DVLE_LENGTH   1
+#define DOLBY_PARAM_DVLE_OFFSET   (DOLBY_PARAM_DVLO_OFFSET + \
+					DOLBY_PARAM_DVLO_LENGTH)
+#define DOLBY_PARAM_DVMC_LENGTH   1
+#define DOLBY_PARAM_DVMC_OFFSET   (DOLBY_PARAM_DVLE_OFFSET + \
+					DOLBY_PARAM_DVLE_LENGTH)
+#define DOLBY_PARAM_DVME_LENGTH   1
+#define DOLBY_PARAM_DVME_OFFSET   (DOLBY_PARAM_DVMC_OFFSET + \
+					DOLBY_PARAM_DVMC_LENGTH)
+#define DOLBY_PARAM_IENB_LENGTH   1
+#define DOLBY_PARAM_IENB_OFFSET   (DOLBY_PARAM_DVME_OFFSET + \
+					DOLBY_PARAM_DVME_LENGTH)
+#define DOLBY_PARAM_IEBF_LENGTH   40
+#define DOLBY_PARAM_IEBF_OFFSET   (DOLBY_PARAM_IENB_OFFSET + \
+					DOLBY_PARAM_IENB_LENGTH)
+#define DOLBY_PARAM_IEON_LENGTH   1
+#define DOLBY_PARAM_IEON_OFFSET   (DOLBY_PARAM_IEBF_OFFSET + \
+					DOLBY_PARAM_IEBF_LENGTH)
+#define DOLBY_PARAM_DEON_LENGTH   1
+#define DOLBY_PARAM_DEON_OFFSET   (DOLBY_PARAM_IEON_OFFSET + \
+					DOLBY_PARAM_IEON_LENGTH)
+#define DOLBY_PARAM_NGON_LENGTH   1
+#define DOLBY_PARAM_NGON_OFFSET   (DOLBY_PARAM_DEON_OFFSET + \
+					DOLBY_PARAM_DEON_LENGTH)
+#define DOLBY_PARAM_GEON_LENGTH   1
+#define DOLBY_PARAM_GEON_OFFSET   (DOLBY_PARAM_NGON_OFFSET + \
+					DOLBY_PARAM_NGON_LENGTH)
+#define DOLBY_PARAM_GENB_LENGTH   1
+#define DOLBY_PARAM_GENB_OFFSET   (DOLBY_PARAM_GEON_OFFSET + \
+					DOLBY_PARAM_GEON_LENGTH)
+#define DOLBY_PARAM_GEBF_LENGTH   40
+#define DOLBY_PARAM_GEBF_OFFSET   (DOLBY_PARAM_GENB_OFFSET + \
+					DOLBY_PARAM_GENB_LENGTH)
+#define DOLBY_PARAM_AONB_LENGTH   1
+#define DOLBY_PARAM_AONB_OFFSET   (DOLBY_PARAM_GEBF_OFFSET + \
+					DOLBY_PARAM_GEBF_LENGTH)
+#define DOLBY_PARAM_AOBF_LENGTH   40
+#define DOLBY_PARAM_AOBF_OFFSET   (DOLBY_PARAM_AONB_OFFSET + \
+					DOLBY_PARAM_AONB_LENGTH)
+#define DOLBY_PARAM_AOBG_LENGTH   329
+#define DOLBY_PARAM_AOBG_OFFSET   (DOLBY_PARAM_AOBF_OFFSET + \
+					DOLBY_PARAM_AOBF_LENGTH)
+#define DOLBY_PARAM_AOON_LENGTH   1
+#define DOLBY_PARAM_AOON_OFFSET   (DOLBY_PARAM_AOBG_OFFSET + \
+					DOLBY_PARAM_AOBG_LENGTH)
+#define DOLBY_PARAM_ARNB_LENGTH   1
+#define DOLBY_PARAM_ARNB_OFFSET   (DOLBY_PARAM_AOON_OFFSET + \
+					DOLBY_PARAM_AOON_LENGTH)
+#define DOLBY_PARAM_ARBF_LENGTH   40
+#define DOLBY_PARAM_ARBF_OFFSET   (DOLBY_PARAM_ARNB_OFFSET + \
+					DOLBY_PARAM_ARNB_LENGTH)
+#define DOLBY_PARAM_PLB_LENGTH    1
+#define DOLBY_PARAM_PLB_OFFSET    (DOLBY_PARAM_ARBF_OFFSET + \
+					DOLBY_PARAM_ARBF_LENGTH)
+#define DOLBY_PARAM_PLMD_LENGTH   1
+#define DOLBY_PARAM_PLMD_OFFSET   (DOLBY_PARAM_PLB_OFFSET + \
+					DOLBY_PARAM_PLB_LENGTH)
+#define DOLBY_PARAM_DHSB_LENGTH   1
+#define DOLBY_PARAM_DHSB_OFFSET   (DOLBY_PARAM_PLMD_OFFSET + \
+					DOLBY_PARAM_PLMD_LENGTH)
+#define DOLBY_PARAM_DHRG_LENGTH   1
+#define DOLBY_PARAM_DHRG_OFFSET   (DOLBY_PARAM_DHSB_OFFSET + \
+					DOLBY_PARAM_DHSB_LENGTH)
+#define DOLBY_PARAM_DSSB_LENGTH   1
+#define DOLBY_PARAM_DSSB_OFFSET   (DOLBY_PARAM_DHRG_OFFSET + \
+					DOLBY_PARAM_DHRG_LENGTH)
+#define DOLBY_PARAM_DSSA_LENGTH   1
+#define DOLBY_PARAM_DSSA_OFFSET   (DOLBY_PARAM_DSSB_OFFSET + \
+					DOLBY_PARAM_DSSB_LENGTH)
+#define DOLBY_PARAM_DVLA_LENGTH   1
+#define DOLBY_PARAM_DVLA_OFFSET   (DOLBY_PARAM_DSSA_OFFSET + \
+					DOLBY_PARAM_DSSA_LENGTH)
+#define DOLBY_PARAM_IEBT_LENGTH   40
+#define DOLBY_PARAM_IEBT_OFFSET   (DOLBY_PARAM_DVLA_OFFSET + \
+					DOLBY_PARAM_DVLA_LENGTH)
+#define DOLBY_PARAM_IEA_LENGTH    1
+#define DOLBY_PARAM_IEA_OFFSET    (DOLBY_PARAM_IEBT_OFFSET + \
+					DOLBY_PARAM_IEBT_LENGTH)
+#define DOLBY_PARAM_DEA_LENGTH    1
+#define DOLBY_PARAM_DEA_OFFSET    (DOLBY_PARAM_IEA_OFFSET + \
+					DOLBY_PARAM_IEA_LENGTH)
+#define DOLBY_PARAM_DED_LENGTH    1
+#define DOLBY_PARAM_DED_OFFSET    (DOLBY_PARAM_DEA_OFFSET + \
+					DOLBY_PARAM_DEA_LENGTH)
+#define DOLBY_PARAM_GEBG_LENGTH   40
+#define DOLBY_PARAM_GEBG_OFFSET   (DOLBY_PARAM_DED_OFFSET + \
+					DOLBY_PARAM_DED_LENGTH)
+#define DOLBY_PARAM_AOCC_LENGTH   1
+#define DOLBY_PARAM_AOCC_OFFSET   (DOLBY_PARAM_GEBG_OFFSET + \
+					DOLBY_PARAM_GEBG_LENGTH)
+#define DOLBY_PARAM_ARBI_LENGTH   40
+#define DOLBY_PARAM_ARBI_OFFSET   (DOLBY_PARAM_AOCC_OFFSET + \
+					DOLBY_PARAM_AOCC_LENGTH)
+#define DOLBY_PARAM_ARBL_LENGTH   40
+#define DOLBY_PARAM_ARBL_OFFSET   (DOLBY_PARAM_ARBI_OFFSET + \
+					DOLBY_PARAM_ARBI_LENGTH)
+#define DOLBY_PARAM_ARBH_LENGTH   40
+#define DOLBY_PARAM_ARBH_OFFSET   (DOLBY_PARAM_ARBL_OFFSET + \
+					DOLBY_PARAM_ARBL_LENGTH)
+#define DOLBY_PARAM_AROD_LENGTH   1
+#define DOLBY_PARAM_AROD_OFFSET   (DOLBY_PARAM_ARBH_OFFSET + \
+					DOLBY_PARAM_ARBH_LENGTH)
+#define DOLBY_PARAM_ARTP_LENGTH   1
+#define DOLBY_PARAM_ARTP_OFFSET   (DOLBY_PARAM_AROD_OFFSET + \
+					DOLBY_PARAM_AROD_LENGTH)
+#define DOLBY_PARAM_VMON_LENGTH   1
+#define DOLBY_PARAM_VMON_OFFSET   (DOLBY_PARAM_ARTP_OFFSET + \
+					DOLBY_PARAM_ARTP_LENGTH)
+#define DOLBY_PARAM_VMB_LENGTH    1
+#define DOLBY_PARAM_VMB_OFFSET    (DOLBY_PARAM_VMON_OFFSET + \
+					DOLBY_PARAM_VMON_LENGTH)
+#define DOLBY_PARAM_VCNB_LENGTH   1
+#define DOLBY_PARAM_VCNB_OFFSET   (DOLBY_PARAM_VMB_OFFSET + \
+					DOLBY_PARAM_VMB_LENGTH)
+#define DOLBY_PARAM_VCBF_LENGTH   20
+#define DOLBY_PARAM_VCBF_OFFSET   (DOLBY_PARAM_VCNB_OFFSET + \
+					DOLBY_PARAM_VCNB_LENGTH)
+#define DOLBY_PARAM_PREG_LENGTH   1
+#define DOLBY_PARAM_PREG_OFFSET   (DOLBY_PARAM_VCBF_OFFSET + \
+					DOLBY_PARAM_VCBF_LENGTH)
+#define DOLBY_PARAM_VEN_LENGTH    1
+#define DOLBY_PARAM_VEN_OFFSET    (DOLBY_PARAM_PREG_OFFSET + \
+					DOLBY_PARAM_PREG_LENGTH)
+#define DOLBY_PARAM_PSTG_LENGTH   1
+#define DOLBY_PARAM_PSTG_OFFSET   (DOLBY_PARAM_VEN_OFFSET + \
+					DOLBY_PARAM_VEN_LENGTH)
+
+#define DOLBY_PARAM_INT_ENDP_LENGTH		1
+#define DOLBY_PARAM_PAYLOAD_SIZE		3
+#define DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM	329
+
+#define TOTAL_LENGTH_DOLBY_PARAM		745
+#define DOLBY_VIS_PARAM_HEADER_SIZE		25
+#define DOLBY_PARAM_VCNB_MAX_LENGTH		40
+
+#define DOLBY_INVALID_PORT_ID			-1
+
+enum {
+	DEVICE_NONE			= 0x0,
+	/* output devices */
+	EARPIECE			= 0x1,
+	SPEAKER				= 0x2,
+	WIRED_HEADSET			= 0x4,
+	WIRED_HEADPHONE			= 0x8,
+	BLUETOOTH_SCO			= 0x10,
+	BLUETOOTH_SCO_HEADSET		= 0x20,
+	BLUETOOTH_SCO_CARKIT		= 0x40,
+	BLUETOOTH_A2DP			= 0x80,
+	BLUETOOTH_A2DP_HEADPHONES	= 0x100,
+	BLUETOOTH_A2DP_SPEAKER		= 0x200,
+	AUX_DIGITAL			= 0x400,
+	ANLG_DOCK_HEADSET		= 0x800,
+	DGTL_DOCK_HEADSET		= 0x1000,
+	USB_ACCESSORY			= 0x2000,
+	USB_DEVICE			= 0x4000,
+	REMOTE_SUBMIX			= 0x8000,
+	ANC_HEADSET			= 0x10000,
+	ANC_HEADPHONE			= 0x20000,
+	PROXY				= 0x2000000,
+	FM				= 0x100000,
+	FM_TX				= 0x1000000,
+	DEVICE_OUT_DEFAULT		= 0x40000000,
+	DEVICE_OUT_ALL			= 0x403FFFFF,
+};
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
new file mode 100644
index 0000000..29a1b3d
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.c
@@ -0,0 +1,1071 @@
+/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6core.h>
+
+#include "msm-dolby-dap-config.h"
+
+/* dolby endp based parameters */
+struct dolby_dap_endp_params_s {
+	int device;
+	int device_ch_caps;
+	int dap_device;
+	int params_id[DOLBY_NUM_ENDP_DEPENDENT_PARAMS];
+	int params_len[DOLBY_NUM_ENDP_DEPENDENT_PARAMS];
+	int params_offset[DOLBY_NUM_ENDP_DEPENDENT_PARAMS];
+	int params_val[DOLBY_ENDDEP_PARAM_LENGTH];
+};
+
+const struct dolby_dap_endp_params_s
+			dolby_dap_endp_params[NUM_DOLBY_ENDP_DEVICE] = {
+	{EARPIECE, 2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{SPEAKER, 2, DOLBY_ENDP_INT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{WIRED_HEADSET,	2, DOLBY_ENDP_HEADPHONES,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{WIRED_HEADPHONE, 2, DOLBY_ENDP_HEADPHONES,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{BLUETOOTH_SCO,	2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{BLUETOOTH_SCO_HEADSET,	2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{BLUETOOTH_SCO_CARKIT, 2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{BLUETOOTH_A2DP, 2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{BLUETOOTH_A2DP_HEADPHONES, 2, DOLBY_ENDP_HEADPHONES,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{BLUETOOTH_A2DP_SPEAKER, 2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{AUX_DIGITAL, 2, DOLBY_ENDP_HDMI,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-496, -496, 0}
+	},
+	{AUX_DIGITAL, 6, DOLBY_ENDP_HDMI,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-496, -496, 0}
+	},
+	{AUX_DIGITAL, 8, DOLBY_ENDP_HDMI,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-496, -496, 0}
+	},
+	{ANLG_DOCK_HEADSET, 2, DOLBY_ENDP_HEADPHONES,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{DGTL_DOCK_HEADSET, 2, DOLBY_ENDP_HEADPHONES,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{USB_ACCESSORY,	2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{USB_DEVICE, 2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{REMOTE_SUBMIX,	2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{PROXY,	2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{PROXY,	6, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{FM, 2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+	{FM_TX,	2, DOLBY_ENDP_EXT_SPEAKERS,
+		{DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_VMB},
+		{DOLBY_ENDDEP_PARAM_DVLO_LENGTH, DOLBY_ENDDEP_PARAM_DVLI_LENGTH,
+		 DOLBY_ENDDEP_PARAM_VMB_LENGTH},
+		{DOLBY_ENDDEP_PARAM_DVLO_OFFSET, DOLBY_ENDDEP_PARAM_DVLI_OFFSET,
+		 DOLBY_ENDDEP_PARAM_VMB_OFFSET},
+		{-320, -320, 144}
+	},
+};
+
+/* dolby param ids to/from dsp */
+static uint32_t	dolby_dap_params_id[ALL_DOLBY_PARAMS] = {
+	DOLBY_PARAM_ID_VDHE, DOLBY_PARAM_ID_VSPE, DOLBY_PARAM_ID_DSSF,
+	DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLE,
+	DOLBY_PARAM_ID_DVMC, DOLBY_PARAM_ID_DVME, DOLBY_PARAM_ID_IENB,
+	DOLBY_PARAM_ID_IEBF, DOLBY_PARAM_ID_IEON, DOLBY_PARAM_ID_DEON,
+	DOLBY_PARAM_ID_NGON, DOLBY_PARAM_ID_GEON, DOLBY_PARAM_ID_GENB,
+	DOLBY_PARAM_ID_GEBF, DOLBY_PARAM_ID_AONB, DOLBY_PARAM_ID_AOBF,
+	DOLBY_PARAM_ID_AOBG, DOLBY_PARAM_ID_AOON, DOLBY_PARAM_ID_ARNB,
+	DOLBY_PARAM_ID_ARBF, DOLBY_PARAM_ID_PLB,  DOLBY_PARAM_ID_PLMD,
+	DOLBY_PARAM_ID_DHSB, DOLBY_PARAM_ID_DHRG, DOLBY_PARAM_ID_DSSB,
+	DOLBY_PARAM_ID_DSSA, DOLBY_PARAM_ID_DVLA, DOLBY_PARAM_ID_IEBT,
+	DOLBY_PARAM_ID_IEA,  DOLBY_PARAM_ID_DEA,  DOLBY_PARAM_ID_DED,
+	DOLBY_PARAM_ID_GEBG, DOLBY_PARAM_ID_AOCC, DOLBY_PARAM_ID_ARBI,
+	DOLBY_PARAM_ID_ARBL, DOLBY_PARAM_ID_ARBH, DOLBY_PARAM_ID_AROD,
+	DOLBY_PARAM_ID_ARTP, DOLBY_PARAM_ID_VMON, DOLBY_PARAM_ID_VMB,
+	DOLBY_PARAM_ID_VCNB, DOLBY_PARAM_ID_VCBF, DOLBY_PARAM_ID_PREG,
+	DOLBY_PARAM_ID_VEN,  DOLBY_PARAM_ID_PSTG, DOLBY_COMMIT_ALL_TO_DSP,
+	DOLBY_COMMIT_TO_DSP, DOLBY_USE_CACHE, DOLBY_AUTO_ENDP,
+	DOLBY_AUTO_ENDDEP_PARAMS
+};
+
+/* modifed state:	0x00000000 - Not updated
+ *			> 0x00000000 && < 0x00010000
+ *				Updated and not committed to DSP
+ *			0x00010001 - Updated and committed to DSP
+ *			> 0x00010001 - Modified the committed value
+ */
+static int dolby_dap_params_modified[MAX_DOLBY_PARAMS] = { 0 };
+/* param offset */
+static uint32_t	dolby_dap_params_offset[MAX_DOLBY_PARAMS] = {
+	DOLBY_PARAM_VDHE_OFFSET, DOLBY_PARAM_VSPE_OFFSET,
+	DOLBY_PARAM_DSSF_OFFSET, DOLBY_PARAM_DVLI_OFFSET,
+	DOLBY_PARAM_DVLO_OFFSET, DOLBY_PARAM_DVLE_OFFSET,
+	DOLBY_PARAM_DVMC_OFFSET, DOLBY_PARAM_DVME_OFFSET,
+	DOLBY_PARAM_IENB_OFFSET, DOLBY_PARAM_IEBF_OFFSET,
+	DOLBY_PARAM_IEON_OFFSET, DOLBY_PARAM_DEON_OFFSET,
+	DOLBY_PARAM_NGON_OFFSET, DOLBY_PARAM_GEON_OFFSET,
+	DOLBY_PARAM_GENB_OFFSET, DOLBY_PARAM_GEBF_OFFSET,
+	DOLBY_PARAM_AONB_OFFSET, DOLBY_PARAM_AOBF_OFFSET,
+	DOLBY_PARAM_AOBG_OFFSET, DOLBY_PARAM_AOON_OFFSET,
+	DOLBY_PARAM_ARNB_OFFSET, DOLBY_PARAM_ARBF_OFFSET,
+	DOLBY_PARAM_PLB_OFFSET,  DOLBY_PARAM_PLMD_OFFSET,
+	DOLBY_PARAM_DHSB_OFFSET, DOLBY_PARAM_DHRG_OFFSET,
+	DOLBY_PARAM_DSSB_OFFSET, DOLBY_PARAM_DSSA_OFFSET,
+	DOLBY_PARAM_DVLA_OFFSET, DOLBY_PARAM_IEBT_OFFSET,
+	DOLBY_PARAM_IEA_OFFSET,  DOLBY_PARAM_DEA_OFFSET,
+	DOLBY_PARAM_DED_OFFSET,  DOLBY_PARAM_GEBG_OFFSET,
+	DOLBY_PARAM_AOCC_OFFSET, DOLBY_PARAM_ARBI_OFFSET,
+	DOLBY_PARAM_ARBL_OFFSET, DOLBY_PARAM_ARBH_OFFSET,
+	DOLBY_PARAM_AROD_OFFSET, DOLBY_PARAM_ARTP_OFFSET,
+	DOLBY_PARAM_VMON_OFFSET, DOLBY_PARAM_VMB_OFFSET,
+	DOLBY_PARAM_VCNB_OFFSET, DOLBY_PARAM_VCBF_OFFSET,
+	DOLBY_PARAM_PREG_OFFSET, DOLBY_PARAM_VEN_OFFSET,
+	DOLBY_PARAM_PSTG_OFFSET
+};
+/* param_length */
+static uint32_t	dolby_dap_params_length[MAX_DOLBY_PARAMS] = {
+	DOLBY_PARAM_VDHE_LENGTH, DOLBY_PARAM_VSPE_LENGTH,
+	DOLBY_PARAM_DSSF_LENGTH, DOLBY_PARAM_DVLI_LENGTH,
+	DOLBY_PARAM_DVLO_LENGTH, DOLBY_PARAM_DVLE_LENGTH,
+	DOLBY_PARAM_DVMC_LENGTH, DOLBY_PARAM_DVME_LENGTH,
+	DOLBY_PARAM_IENB_LENGTH, DOLBY_PARAM_IEBF_LENGTH,
+	DOLBY_PARAM_IEON_LENGTH, DOLBY_PARAM_DEON_LENGTH,
+	DOLBY_PARAM_NGON_LENGTH, DOLBY_PARAM_GEON_LENGTH,
+	DOLBY_PARAM_GENB_LENGTH, DOLBY_PARAM_GEBF_LENGTH,
+	DOLBY_PARAM_AONB_LENGTH, DOLBY_PARAM_AOBF_LENGTH,
+	DOLBY_PARAM_AOBG_LENGTH, DOLBY_PARAM_AOON_LENGTH,
+	DOLBY_PARAM_ARNB_LENGTH, DOLBY_PARAM_ARBF_LENGTH,
+	DOLBY_PARAM_PLB_LENGTH,  DOLBY_PARAM_PLMD_LENGTH,
+	DOLBY_PARAM_DHSB_LENGTH, DOLBY_PARAM_DHRG_LENGTH,
+	DOLBY_PARAM_DSSB_LENGTH, DOLBY_PARAM_DSSA_LENGTH,
+	DOLBY_PARAM_DVLA_LENGTH, DOLBY_PARAM_IEBT_LENGTH,
+	DOLBY_PARAM_IEA_LENGTH,  DOLBY_PARAM_DEA_LENGTH,
+	DOLBY_PARAM_DED_LENGTH,  DOLBY_PARAM_GEBG_LENGTH,
+	DOLBY_PARAM_AOCC_LENGTH, DOLBY_PARAM_ARBI_LENGTH,
+	DOLBY_PARAM_ARBL_LENGTH, DOLBY_PARAM_ARBH_LENGTH,
+	DOLBY_PARAM_AROD_LENGTH, DOLBY_PARAM_ARTP_LENGTH,
+	DOLBY_PARAM_VMON_LENGTH, DOLBY_PARAM_VMB_LENGTH,
+	DOLBY_PARAM_VCNB_LENGTH, DOLBY_PARAM_VCBF_LENGTH,
+	DOLBY_PARAM_PREG_LENGTH, DOLBY_PARAM_VEN_LENGTH,
+	DOLBY_PARAM_PSTG_LENGTH
+};
+
+/* param_value */
+static uint32_t	dolby_dap_params_value[TOTAL_LENGTH_DOLBY_PARAM] = {0};
+
+struct dolby_dap_params_get_s {
+	int32_t  port_id;
+	uint32_t device_id;
+	uint32_t param_id;
+	uint32_t offset;
+	uint32_t length;
+};
+
+struct dolby_dap_params_states_s {
+	bool use_cache;
+	bool auto_endp;
+	bool enddep_params;
+	int  port_id[AFE_MAX_PORTS];
+	int  copp_idx[AFE_MAX_PORTS];
+	int  port_open_count;
+	int  port_ids_dolby_can_be_enabled;
+	int  device;
+};
+
+static struct dolby_dap_params_get_s dolby_dap_params_get = {-1, DEVICE_OUT_ALL,
+							     0, 0, 0};
+static struct dolby_dap_params_states_s dolby_dap_params_states = { true, true,
+						true, {DOLBY_INVALID_PORT_ID},
+						{-1}, 0, DEVICE_OUT_ALL, 0 };
+/*
+ * port_ids_dolby_can_be_enabled is set to 0x7FFFFFFF.
+ * this needs to be removed after interface validation
+ */
+
+static int msm_dolby_dap_map_device_to_dolby_endpoint(int device)
+{
+	int i, dolby_dap_device = DOLBY_ENDP_EXT_SPEAKERS;
+
+	for (i = 0; i < NUM_DOLBY_ENDP_DEVICE; i++) {
+		if (dolby_dap_endp_params[i].device == device) {
+			dolby_dap_device = dolby_dap_endp_params[i].dap_device;
+			break;
+		}
+	}
+	/* default the endpoint to speaker if corresponding device entry */
+	/* not found */
+	if (i >= NUM_DOLBY_ENDP_DEVICE)
+		dolby_dap_params_states.device = SPEAKER;
+	return dolby_dap_device;
+}
+
+static int msm_dolby_dap_send_end_point(int port_id, int copp_idx)
+{
+	int rc = 0;
+	char *params_value;
+	int *update_params_value;
+	uint32_t params_length = (DOLBY_PARAM_INT_ENDP_LENGTH +
+				DOLBY_PARAM_PAYLOAD_SIZE) * sizeof(uint32_t);
+
+	pr_debug("%s\n", __func__);
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		pr_err("%s, params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+	update_params_value = (int *)params_value;
+	*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+	*update_params_value++ = DOLBY_PARAM_ID_INIT_ENDP;
+	*update_params_value++ = DOLBY_PARAM_INT_ENDP_LENGTH * sizeof(uint32_t);
+	*update_params_value++ =
+		 msm_dolby_dap_map_device_to_dolby_endpoint(
+						dolby_dap_params_states.device);
+	rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value,
+				       params_length);
+	if (rc) {
+		pr_err("%s: send dolby params failed\n", __func__);
+		rc = -EINVAL;
+	}
+	kfree(params_value);
+	return rc;
+}
+
+static int msm_dolby_dap_send_enddep_params(int port_id, int copp_idx,
+					    int device_channels)
+{
+	int i, j, rc = 0, idx, offset;
+	char *params_value;
+	int *update_params_value;
+	uint32_t params_length = (DOLBY_ENDDEP_PARAM_LENGTH +
+					DOLBY_NUM_ENDP_DEPENDENT_PARAMS *
+					DOLBY_PARAM_PAYLOAD_SIZE) *
+				sizeof(uint32_t);
+
+	pr_debug("%s\n", __func__);
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		pr_err("%s, params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+	update_params_value = (int *)params_value;
+	for (idx = 0; idx < NUM_DOLBY_ENDP_DEVICE; idx++) {
+		if (dolby_dap_endp_params[idx].device ==
+			dolby_dap_params_states.device) {
+			if (dolby_dap_params_states.device == AUX_DIGITAL ||
+			    dolby_dap_params_states.device == PROXY) {
+				if (dolby_dap_endp_params[idx].device_ch_caps ==
+					device_channels)
+					break;
+			} else {
+				break;
+			}
+		}
+	}
+	if (idx >= NUM_DOLBY_ENDP_DEVICE) {
+		pr_err("%s: device is not set accordingly\n", __func__);
+		kfree(params_value);
+		return -EINVAL;
+	}
+	for (i = 0; i < DOLBY_ENDDEP_PARAM_LENGTH; i++) {
+		*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+		*update_params_value++ =
+				dolby_dap_endp_params[idx].params_id[i];
+		*update_params_value++ =
+			dolby_dap_endp_params[idx].params_len[i] *
+				sizeof(uint32_t);
+		offset = dolby_dap_endp_params[idx].params_offset[i];
+		for (j = 0; j < dolby_dap_endp_params[idx].params_len[i]; j++)
+			*update_params_value++ =
+				dolby_dap_endp_params[idx].params_val[offset+j];
+	}
+	rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value,
+				       params_length);
+	if (rc) {
+		pr_err("%s: send dolby params failed\n", __func__);
+		rc = -EINVAL;
+	}
+	kfree(params_value);
+	return rc;
+}
+
+static int msm_dolby_dap_send_cached_params(int port_id, int copp_idx,
+					    int commit)
+{
+	char *params_value;
+	int *update_params_value, rc = 0;
+	uint32_t index_offset, i, j;
+	uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM +
+				MAX_DOLBY_PARAMS * DOLBY_PARAM_PAYLOAD_SIZE) *
+				sizeof(uint32_t);
+
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value)
+		return -ENOMEM;
+
+	update_params_value = (int *)params_value;
+	params_length = 0;
+	for (i = 0; i < MAX_DOLBY_PARAMS; i++) {
+		if ((dolby_dap_params_modified[i] == 0) ||
+		    ((commit) &&
+		     ((dolby_dap_params_modified[i] & 0x00010000) &&
+		     ((dolby_dap_params_modified[i] & 0x0000FFFF) <= 1))))
+			continue;
+		*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+		*update_params_value++ = dolby_dap_params_id[i];
+		*update_params_value++ = dolby_dap_params_length[i] *
+						sizeof(uint32_t);
+		index_offset = dolby_dap_params_offset[i];
+		for (j = 0; j < dolby_dap_params_length[i]; j++) {
+			*update_params_value++ =
+					dolby_dap_params_value[index_offset+j];
+		}
+		params_length += (DOLBY_PARAM_PAYLOAD_SIZE +
+				dolby_dap_params_length[i]) * sizeof(uint32_t);
+	}
+	pr_debug("%s, valid param length: %d", __func__, params_length);
+	if (params_length) {
+		rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value,
+						params_length);
+		if (rc) {
+			pr_err("%s: send dolby params failed\n", __func__);
+			kfree(params_value);
+			return -EINVAL;
+		}
+		for (i = 0; i < MAX_DOLBY_PARAMS; i++) {
+			if ((dolby_dap_params_modified[i] == 0) ||
+			    ((commit) &&
+			     ((dolby_dap_params_modified[i] & 0x00010000) &&
+			     ((dolby_dap_params_modified[i] & 0x0000FFFF) <= 1))
+			    ))
+				continue;
+			dolby_dap_params_modified[i] = 0x00010001;
+		}
+	}
+	kfree(params_value);
+	return 0;
+}
+
+int msm_dolby_dap_init(int port_id, int copp_idx, int channels,
+		       bool is_custom_stereo_on)
+{
+	int ret = 0;
+	int index = adm_validate_and_get_port_index(port_id);
+
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+			port_id);
+		return -EINVAL;
+	}
+	if ((port_id != DOLBY_INVALID_PORT_ID) &&
+	    (port_id & dolby_dap_params_states.port_ids_dolby_can_be_enabled)) {
+		dolby_dap_params_states.port_id[index] = port_id;
+		dolby_dap_params_states.copp_idx[index] = copp_idx;
+		dolby_dap_params_states.port_open_count++;
+		if (dolby_dap_params_states.auto_endp) {
+			ret = msm_dolby_dap_send_end_point(port_id, copp_idx);
+			if (ret) {
+				pr_err("%s: err sending endppoint\n", __func__);
+				return ret;
+			}
+		}
+		if (dolby_dap_params_states.use_cache) {
+			ret = msm_dolby_dap_send_cached_params(port_id,
+							       copp_idx, 0);
+			if (ret) {
+				pr_err("%s: err sending cached params\n",
+					__func__);
+				return ret;
+			}
+		}
+		if (dolby_dap_params_states.enddep_params) {
+			msm_dolby_dap_send_enddep_params(port_id, copp_idx,
+							 channels);
+			if (ret) {
+				pr_err("%s: err sending endp dependent params\n",
+					__func__);
+				return ret;
+			}
+		}
+		if (is_custom_stereo_on)
+			dolby_dap_set_custom_stereo_onoff(port_id, copp_idx,
+							  is_custom_stereo_on);
+	}
+	return ret;
+}
+
+void msm_dolby_dap_deinit(int port_id)
+{
+	int index = adm_validate_and_get_port_index(port_id);
+
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+			port_id);
+		return;
+	}
+	dolby_dap_params_states.port_open_count--;
+	if ((dolby_dap_params_states.port_id[index] == port_id) &&
+		(!dolby_dap_params_states.port_open_count)) {
+		dolby_dap_params_states.port_id[index] = DOLBY_INVALID_PORT_ID;
+		dolby_dap_params_states.copp_idx[index] = -1;
+	}
+}
+
+static int msm_dolby_dap_set_vspe_vdhe(int port_id, int copp_idx,
+				       bool is_custom_stereo_enabled)
+{
+	char *params_value;
+	int *update_params_value, rc = 0;
+	uint32_t index_offset, i, j;
+	uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM +
+				2 * DOLBY_PARAM_PAYLOAD_SIZE) *
+				sizeof(uint32_t);
+	if (port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s: Not a Dolby topology. Do not set custom stereo mixing\n",
+			__func__);
+		return -EINVAL;
+	}
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value)
+		return -ENOMEM;
+
+	update_params_value = (int *)params_value;
+	params_length = 0;
+	/* for VDHE and VSPE DAP params at index 0 and 1 in table */
+	for (i = 0; i < 2; i++) {
+		*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+		*update_params_value++ = dolby_dap_params_id[i];
+		*update_params_value++ = dolby_dap_params_length[i] *
+					sizeof(uint32_t);
+		index_offset = dolby_dap_params_offset[i];
+		for (j = 0; j < dolby_dap_params_length[i]; j++) {
+			if (is_custom_stereo_enabled)
+				*update_params_value++ = 0;
+			else
+				*update_params_value++ =
+					dolby_dap_params_value[index_offset+j];
+		}
+		params_length += (DOLBY_PARAM_PAYLOAD_SIZE +
+				dolby_dap_params_length[i]) * sizeof(uint32_t);
+	}
+	pr_debug("%s, valid param length: %d", __func__, params_length);
+	if (params_length) {
+		rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value,
+					       params_length);
+		if (rc) {
+			pr_err("%s: send vdhe/vspe params failed with rc=%d\n",
+				__func__, rc);
+			kfree(params_value);
+			return -EINVAL;
+		}
+	}
+	kfree(params_value);
+	return 0;
+}
+
+int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+				      bool is_custom_stereo_enabled)
+{
+	char *params_value;
+	int *update_params_value, rc = 0;
+	uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM +
+				DOLBY_PARAM_PAYLOAD_SIZE) *
+				sizeof(uint32_t);
+	if (port_id == DOLBY_INVALID_PORT_ID)
+		return -EINVAL;
+
+	msm_dolby_dap_set_vspe_vdhe(port_id, copp_idx,
+				    is_custom_stereo_enabled);
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		pr_err("%s, params memory alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+	update_params_value = (int *)params_value;
+	params_length = 0;
+	*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+	*update_params_value++ = DOLBY_ENABLE_CUSTOM_STEREO;
+	*update_params_value++ = sizeof(uint32_t);
+	if (is_custom_stereo_enabled)
+		*update_params_value++ = 1;
+	else
+		*update_params_value++ = 0;
+	params_length += (DOLBY_PARAM_PAYLOAD_SIZE + 1) * sizeof(uint32_t);
+	pr_debug("%s, valid param length: %d", __func__, params_length);
+	if (params_length) {
+		rc = adm_dolby_dap_send_params(port_id, copp_idx, params_value,
+					       params_length);
+		if (rc) {
+			pr_err("%s: setting ds1 custom stereo param failed with rc=%d\n",
+				__func__, rc);
+			kfree(params_value);
+			return -EINVAL;
+		}
+	}
+	kfree(params_value);
+	return 0;
+}
+
+static int msm_dolby_dap_map_device_to_port_id(int device)
+{
+	int port_id = SLIMBUS_0_RX;
+
+	device = DEVICE_OUT_ALL;
+	/*update the device when single stream to multiple device is handled*/
+	if (device == DEVICE_OUT_ALL) {
+		port_id = PRIMARY_I2S_RX | SLIMBUS_0_RX | HDMI_RX |
+				INT_BT_SCO_RX | INT_FM_RX |
+				RT_PROXY_PORT_001_RX |
+				AFE_PORT_ID_PRIMARY_PCM_RX |
+				MI2S_RX | SECONDARY_I2S_RX |
+				SLIMBUS_1_RX | SLIMBUS_4_RX | SLIMBUS_3_RX |
+				AFE_PORT_ID_SECONDARY_MI2S_RX;
+	} else {
+		/* update port_id based on the device */
+	}
+	return port_id;
+}
+
+int msm_dolby_dap_param_to_set_control_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	/* not used while setting the parameters */
+	return 0;
+}
+
+int msm_dolby_dap_param_to_set_control_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0, port_id, copp_idx;
+	uint32_t idx, j;
+	uint32_t device = ucontrol->value.integer.value[0];
+	uint32_t param_id = ucontrol->value.integer.value[1];
+	uint32_t offset = ucontrol->value.integer.value[2];
+	uint32_t length = ucontrol->value.integer.value[3];
+
+	dolby_dap_params_states.port_ids_dolby_can_be_enabled =
+				msm_dolby_dap_map_device_to_port_id(device);
+	for (idx = 0; idx < ALL_DOLBY_PARAMS; idx++) {
+		/*paramid from user space*/
+		if (param_id == dolby_dap_params_id[idx])
+			break;
+	}
+	if (idx > ALL_DOLBY_PARAMS-1) {
+		pr_err("%s: invalid param id 0x%x to set\n", __func__,
+			param_id);
+		return -EINVAL;
+	}
+	switch (idx) {
+		case DOLBY_COMMIT_ALL_IDX: {
+			/* COMIIT ALL: Send all parameters to DSP */
+			pr_debug("%s: COMMIT_ALL recvd\n", __func__);
+			for (idx = 0; idx < AFE_MAX_PORTS; idx++) {
+				port_id = dolby_dap_params_states.port_id[idx];
+				copp_idx =
+					dolby_dap_params_states.copp_idx[idx];
+				if ((copp_idx > 0) &&
+				    (copp_idx < MAX_COPPS_PER_PORT) &&
+				    (port_id != DOLBY_INVALID_PORT_ID))
+					rc |= msm_dolby_dap_send_cached_params(
+								      port_id,
+								      copp_idx,
+								      0);
+			}
+		}
+		break;
+		case DOLBY_COMMIT_IDX: {
+			pr_debug("%s: COMMIT recvd\n", __func__);
+			/* COMMIT: Send only modified parameters to DSP */
+			for (idx = 0; idx < AFE_MAX_PORTS; idx++) {
+				port_id = dolby_dap_params_states.port_id[idx];
+				copp_idx =
+					dolby_dap_params_states.copp_idx[idx];
+				if ((copp_idx > 0) &&
+				    (copp_idx < MAX_COPPS_PER_PORT) &&
+				    (port_id == DOLBY_INVALID_PORT_ID))
+					rc |= msm_dolby_dap_send_cached_params(
+								      port_id,
+								      copp_idx,
+								      1);
+			}
+		}
+		break;
+		case DOLBY_USE_CACHE_IDX: {
+			pr_debug("%s: USE CACHE recvd val: %ld\n", __func__,
+				ucontrol->value.integer.value[4]);
+			dolby_dap_params_states.use_cache =
+				ucontrol->value.integer.value[4];
+		}
+		break;
+		case DOLBY_AUTO_ENDP_IDX: {
+			pr_debug("%s: AUTO_ENDP recvd val: %ld\n", __func__,
+				ucontrol->value.integer.value[4]);
+			dolby_dap_params_states.auto_endp =
+				ucontrol->value.integer.value[4];
+		}
+		break;
+		case DOLBY_AUTO_ENDDEP_IDX: {
+			pr_debug("%s: USE_ENDDEP_PARAMS recvd val: %ld\n",
+				__func__, ucontrol->value.integer.value[4]);
+			dolby_dap_params_states.enddep_params =
+				ucontrol->value.integer.value[4];
+		}
+		break;
+		default: {
+			/* cache the parameters */
+			dolby_dap_params_modified[idx] += 1;
+			dolby_dap_params_length[idx] = length;
+			pr_debug("%s: param recvd deviceId=0x%x paramId=0x%x offset=%d length=%d\n",
+				__func__, device, param_id, offset, length);
+			for (j = 0; j < length; j++) {
+				dolby_dap_params_value[
+					dolby_dap_params_offset[idx] +
+					offset + j]
+				= ucontrol->value.integer.value[4+j];
+				pr_debug("value[%d]: %ld\n", j,
+					ucontrol->value.integer.value[4+j]);
+			}
+		}
+	}
+
+	return rc;
+}
+
+int msm_dolby_dap_param_to_get_control_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0, i, index;
+	char *params_value;
+	int *update_params_value;
+	uint32_t params_length = DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM *
+					sizeof(uint32_t);
+	uint32_t param_payload_len =
+			DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+	int port_id = dolby_dap_params_get.port_id, copp_idx;
+
+	if (port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s, port_id not set, do not query ADM\n", __func__);
+		return -EINVAL;
+	}
+	index = adm_validate_and_get_port_index(port_id);
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+			port_id);
+		return -EINVAL;
+	}
+	copp_idx = dolby_dap_params_states.copp_idx[index];
+	if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_debug("%s: get params called before copp open.copp_idx:%d\n",
+			 __func__, copp_idx);
+		return -EINVAL;
+	}
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value)
+		return -ENOMEM;
+
+	if (dolby_dap_params_get.param_id == DOLBY_PARAM_ID_VER) {
+		rc = adm_get_params(port_id, copp_idx,
+				    DOLBY_BUNDLE_MODULE_ID, DOLBY_PARAM_ID_VER,
+				    params_length + param_payload_len,
+				    params_value);
+	} else {
+		for (i = 0; i < MAX_DOLBY_PARAMS; i++)
+			if (dolby_dap_params_id[i] ==
+				dolby_dap_params_get.param_id)
+				break;
+		if (i > MAX_DOLBY_PARAMS-1) {
+			pr_err("%s: invalid param id to set", __func__);
+			rc = -EINVAL;
+		} else {
+			params_length = (dolby_dap_params_length[i] +
+						DOLBY_PARAM_PAYLOAD_SIZE) *
+						sizeof(uint32_t);
+			rc = adm_get_params(port_id, copp_idx,
+					    DOLBY_BUNDLE_MODULE_ID,
+					    dolby_dap_params_id[i],
+					    params_length + param_payload_len,
+					    params_value);
+		}
+	}
+	if (rc) {
+		pr_err("%s: get parameters failed rc:%d\n", __func__, rc);
+		kfree(params_value);
+		return -EINVAL;
+	}
+	update_params_value = (int *)params_value;
+	ucontrol->value.integer.value[0] = dolby_dap_params_get.device_id;
+	ucontrol->value.integer.value[1] = dolby_dap_params_get.param_id;
+	ucontrol->value.integer.value[2] = dolby_dap_params_get.offset;
+	ucontrol->value.integer.value[3] = dolby_dap_params_get.length;
+
+	pr_debug("%s: FROM DSP value[0] 0x%x value[1] %d value[2] 0x%x\n",
+			__func__, update_params_value[0],
+			update_params_value[1], update_params_value[2]);
+	for (i = 0; i < dolby_dap_params_get.length; i++) {
+		ucontrol->value.integer.value[DOLBY_PARAM_PAYLOAD_SIZE+i] =
+			update_params_value[i];
+		pr_debug("value[%d]:%d\n", i, update_params_value[i]);
+	}
+	pr_debug("%s: Returning param_id=0x%x offset=%d length=%d\n",
+			__func__, dolby_dap_params_get.param_id,
+			dolby_dap_params_get.offset,
+			dolby_dap_params_get.length);
+	kfree(params_value);
+	return 0;
+}
+
+int msm_dolby_dap_param_to_get_control_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	int port_id, idx, copp_idx;
+
+	dolby_dap_params_get.device_id = ucontrol->value.integer.value[0];
+	port_id = msm_dolby_dap_map_device_to_port_id(
+						dolby_dap_params_get.device_id);
+	for (idx = 0; idx < AFE_MAX_PORTS; idx++) {
+		port_id = dolby_dap_params_states.port_id[idx];
+		copp_idx = dolby_dap_params_states.copp_idx[idx];
+		if ((copp_idx < 0) ||
+		    (copp_idx >= MAX_COPPS_PER_PORT) ||
+		    (port_id == DOLBY_INVALID_PORT_ID))
+			continue;
+		else
+			break;
+	}
+	if (idx == AFE_MAX_PORTS)
+		port_id = SLIMBUS_0_RX;
+	dolby_dap_params_get.port_id = port_id;
+	dolby_dap_params_get.param_id = ucontrol->value.integer.value[1];
+	dolby_dap_params_get.offset = ucontrol->value.integer.value[2];
+	dolby_dap_params_get.length = ucontrol->value.integer.value[3];
+	pr_debug("%s: param_id=0x%x offset=%d length=%d\n", __func__,
+		dolby_dap_params_get.param_id, dolby_dap_params_get.offset,
+		dolby_dap_params_get.length);
+	return 0;
+}
+
+int msm_dolby_dap_param_visualizer_control_get(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	uint32_t length = dolby_dap_params_value[DOLBY_PARAM_VCNB_OFFSET];
+	char *visualizer_data;
+	int i, rc;
+	int *update_visualizer_data;
+	uint32_t offset, params_length =
+		(2*length + DOLBY_VIS_PARAM_HEADER_SIZE)*sizeof(uint32_t);
+	uint32_t param_payload_len =
+		DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+	int port_id, copp_idx, idx;
+
+	for (idx = 0; idx < AFE_MAX_PORTS; idx++) {
+		port_id = dolby_dap_params_states.port_id[idx];
+		copp_idx = dolby_dap_params_states.copp_idx[idx];
+		if ((copp_idx < 0) ||
+		    (copp_idx >= MAX_COPPS_PER_PORT) ||
+		    (port_id == DOLBY_INVALID_PORT_ID))
+			continue;
+		else
+			break;
+	}
+	if (idx == AFE_MAX_PORTS) {
+		pr_debug("%s, port_id not set, returning error", __func__);
+		ucontrol->value.integer.value[0] = 0;
+		return -EINVAL;
+	}
+	visualizer_data = kzalloc(params_length, GFP_KERNEL);
+	if (!visualizer_data)
+		return -ENOMEM;
+
+	offset = 0;
+	params_length = length * sizeof(uint32_t);
+	rc = adm_get_params(port_id, copp_idx, DOLBY_BUNDLE_MODULE_ID,
+			    DOLBY_PARAM_ID_VCBG,
+			    params_length + param_payload_len,
+			    visualizer_data + offset);
+	if (rc) {
+		pr_err("%s: get parameters failed\n", __func__);
+		kfree(visualizer_data);
+		return -EINVAL;
+	}
+
+	offset = length * sizeof(uint32_t);
+	rc = adm_get_params(port_id, copp_idx, DOLBY_BUNDLE_MODULE_ID,
+			    DOLBY_PARAM_ID_VCBE,
+			    params_length + param_payload_len,
+			    visualizer_data + offset);
+	if (rc) {
+		pr_err("%s: get parameters failed\n", __func__);
+		kfree(visualizer_data);
+		return -EINVAL;
+	}
+
+	ucontrol->value.integer.value[0] = 2*length;
+	pr_debug("%s: visualizer data length %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	update_visualizer_data = (int *)visualizer_data;
+	for (i = 0; i < 2*length; i++) {
+		ucontrol->value.integer.value[1+i] = update_visualizer_data[i];
+		pr_debug("value[%d] %d\n", i, update_visualizer_data[i]);
+	}
+	kfree(visualizer_data);
+	return 0;
+}
+
+int msm_dolby_dap_param_visualizer_control_put(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	/* not used while getting the visualizer data */
+	return 0;
+}
+
+int msm_dolby_dap_endpoint_control_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	/* not used while setting the endpoint */
+	return 0;
+}
+
+int msm_dolby_dap_endpoint_control_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int device = ucontrol->value.integer.value[0];
+
+	dolby_dap_params_states.device = device;
+	return 0;
+}
+
+int msm_dolby_dap_security_control_get(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	/* not used while setting the manfr id*/
+	return 0;
+}
+
+int msm_dolby_dap_security_control_put(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	int manufacturer_id = ucontrol->value.integer.value[0];
+
+	core_set_dolby_manufacturer_id(manufacturer_id);
+	return 0;
+}
+
+int msm_dolby_dap_license_control_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] =
+			core_get_license_status(DOLBY_DS1_LICENSE_ID);
+	return 0;
+}
+
+int msm_dolby_dap_license_control_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	return core_set_license(ucontrol->value.integer.value[0],
+						DOLBY_DS1_LICENSE_ID);
+}
+
+static const struct snd_kcontrol_new dolby_license_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DS1 License", SND_SOC_NOPM, 0,
+	0xFFFFFFFF, 0, 1, msm_dolby_dap_license_control_get,
+	msm_dolby_dap_license_control_put),
+};
+
+static const struct snd_kcontrol_new dolby_security_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DS1 Security", SND_SOC_NOPM, 0,
+	0xFFFFFFFF, 0, 1, msm_dolby_dap_security_control_get,
+	msm_dolby_dap_security_control_put),
+};
+
+static const struct snd_kcontrol_new dolby_dap_param_to_set_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DS1 DAP Set Param", SND_SOC_NOPM, 0, 0xFFFFFFFF,
+	0, 128, msm_dolby_dap_param_to_set_control_get,
+	msm_dolby_dap_param_to_set_control_put),
+};
+
+static const struct snd_kcontrol_new dolby_dap_param_to_get_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DS1 DAP Get Param", SND_SOC_NOPM, 0, 0xFFFFFFFF,
+	0, 128, msm_dolby_dap_param_to_get_control_get,
+	msm_dolby_dap_param_to_get_control_put),
+};
+
+static const struct snd_kcontrol_new dolby_dap_param_visualizer_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DS1 DAP Get Visualizer", SND_SOC_NOPM, 0,
+	0xFFFFFFFF, 0, 41, msm_dolby_dap_param_visualizer_control_get,
+	msm_dolby_dap_param_visualizer_control_put),
+};
+
+static const struct snd_kcontrol_new dolby_dap_param_end_point_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DS1 DAP Endpoint", SND_SOC_NOPM, 0,
+	0xFFFFFFFF, 0, 1, msm_dolby_dap_endpoint_control_get,
+	msm_dolby_dap_endpoint_control_put),
+};
+
+void msm_dolby_dap_add_controls(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform,
+				dolby_license_controls,
+			ARRAY_SIZE(dolby_license_controls));
+
+	snd_soc_add_platform_controls(platform,
+				dolby_security_controls,
+			ARRAY_SIZE(dolby_security_controls));
+
+	snd_soc_add_platform_controls(platform,
+				dolby_dap_param_to_set_controls,
+			ARRAY_SIZE(dolby_dap_param_to_set_controls));
+
+	snd_soc_add_platform_controls(platform,
+				dolby_dap_param_to_get_controls,
+			ARRAY_SIZE(dolby_dap_param_to_get_controls));
+
+	snd_soc_add_platform_controls(platform,
+				dolby_dap_param_visualizer_controls,
+			ARRAY_SIZE(dolby_dap_param_visualizer_controls));
+
+	snd_soc_add_platform_controls(platform,
+				dolby_dap_param_end_point_controls,
+			ARRAY_SIZE(dolby_dap_param_end_point_controls));
+}
diff --git a/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
new file mode 100644
index 0000000..ca6310a
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dolby-dap-config.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DOLBY_DAP_CONFIG_H_
+#define _MSM_DOLBY_DAP_CONFIG_H_
+
+#include <sound/soc.h>
+#include "msm-dolby-common.h"
+
+#ifdef CONFIG_DOLBY_DAP
+/* DOLBY DOLBY GUIDS */
+#define DOLBY_ADM_COPP_TOPOLOGY_ID	0x0001033B
+#define NUM_DOLBY_ENDP_DEVICE                 23
+
+#define DOLBY_NUM_ENDP_DEPENDENT_PARAMS	  3
+#define DOLBY_ENDDEP_PARAM_DVLO_OFFSET	  0
+#define DOLBY_ENDDEP_PARAM_DVLO_LENGTH	  1
+#define DOLBY_ENDDEP_PARAM_DVLI_OFFSET    (DOLBY_ENDDEP_PARAM_DVLO_OFFSET + \
+						DOLBY_ENDDEP_PARAM_DVLO_LENGTH)
+#define DOLBY_ENDDEP_PARAM_DVLI_LENGTH    1
+#define DOLBY_ENDDEP_PARAM_VMB_OFFSET     (DOLBY_ENDDEP_PARAM_DVLI_OFFSET + \
+						DOLBY_ENDDEP_PARAM_DVLI_LENGTH)
+#define DOLBY_ENDDEP_PARAM_VMB_LENGTH     1
+#define DOLBY_ENDDEP_PARAM_LENGTH         (DOLBY_ENDDEP_PARAM_DVLO_LENGTH + \
+		DOLBY_ENDDEP_PARAM_DVLI_LENGTH + DOLBY_ENDDEP_PARAM_VMB_LENGTH)
+
+#define MAX_DOLBY_PARAMS			47
+#define MAX_DOLBY_CTRL_PARAMS			5
+#define ALL_DOLBY_PARAMS			(MAX_DOLBY_PARAMS + \
+							MAX_DOLBY_CTRL_PARAMS)
+#define DOLBY_COMMIT_ALL_IDX			MAX_DOLBY_PARAMS
+#define DOLBY_COMMIT_IDX			(MAX_DOLBY_PARAMS+1)
+#define DOLBY_USE_CACHE_IDX			(MAX_DOLBY_PARAMS+2)
+#define DOLBY_AUTO_ENDP_IDX			(MAX_DOLBY_PARAMS+3)
+#define DOLBY_AUTO_ENDDEP_IDX			(MAX_DOLBY_PARAMS+4)
+
+/* DOLBY device definitions */
+enum {
+	DOLBY_ENDP_INT_SPEAKERS = 0,
+	DOLBY_ENDP_EXT_SPEAKERS,
+	DOLBY_ENDP_HEADPHONES,
+	DOLBY_ENDP_HDMI,
+	DOLBY_ENDP_SPDIF,
+	DOLBY_ENDP_DLNA,
+	DOLBY_ENDP_ANALOG,
+};
+
+/* DOLBY device definitions end */
+
+struct dolby_dap_params {
+	uint32_t value[TOTAL_LENGTH_DOLBY_PARAM + MAX_DOLBY_PARAMS];
+} __packed;
+
+int msm_dolby_dap_init(int port_id, int copp_idx, int channels,
+		       bool is_custom_stereo_on);
+void msm_dolby_dap_deinit(int port_id);
+void msm_dolby_dap_add_controls(struct snd_soc_platform *platform);
+int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+				      bool is_custom_stereo_enabled);
+/* Dolby DOLBY end */
+#else
+int msm_dolby_dap_init(int port_id, int copp_idx, int channels,
+		       bool is_custom_stereo_on)
+{
+	return 0;
+}
+void msm_dolby_dap_deinit(int port_id) { }
+void msm_dolby_dap_add_controls(struct snd_soc_platform *platform) { }
+int dolby_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+				      bool is_custom_stereo_enabled)
+{
+	return 0;
+}
+#endif
+
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
new file mode 100644
index 0000000..40ea49c
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.c
@@ -0,0 +1,2299 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+
+#include "msm-ds2-dap-config.h"
+#include "msm-pcm-routing-v2.h"
+#include <sound/q6core.h>
+
+
+#ifdef CONFIG_DOLBY_DS2
+
+/* ramp up/down for 30ms    */
+#define DOLBY_SOFT_VOLUME_PERIOD	40
+/* Step value 0ms or 0us */
+#define DOLBY_SOFT_VOLUME_STEP		1000
+#define DOLBY_ADDITIONAL_RAMP_WAIT	10
+#define SOFT_VOLUME_PARAM_SIZE		3
+#define PARAM_PAYLOAD_SIZE		3
+
+enum {
+	DOLBY_SOFT_VOLUME_CURVE_LINEAR = 0,
+	DOLBY_SOFT_VOLUME_CURVE_EXP,
+	DOLBY_SOFT_VOLUME_CURVE_LOG,
+};
+
+#define VOLUME_ZERO_GAIN     0x0
+#define VOLUME_UNITY_GAIN    0x2000
+/* Wait time for module enable/disble */
+#define DOLBY_MODULE_ENABLE_PERIOD     50
+
+/* DOLBY device definitions end */
+enum {
+	DOLBY_OFF_CACHE = 0,
+	DOLBY_SPEAKER_CACHE,
+	DOLBY_HEADPHONE_CACHE,
+	DOLBY_HDMI_CACHE,
+	DOLBY_WFD_CACHE,
+	DOLBY_FM_CACHE,
+	DOLBY_MAX_CACHE,
+};
+
+enum {
+	DAP_SOFT_BYPASS = 0,
+	DAP_HARD_BYPASS,
+};
+
+enum {
+	MODULE_DISABLE = 0,
+	MODULE_ENABLE,
+};
+/* dolby param ids to/from dsp */
+static uint32_t	ds2_dap_params_id[MAX_DS2_PARAMS] = {
+	DOLBY_PARAM_ID_VDHE, DOLBY_PARAM_ID_VSPE, DOLBY_PARAM_ID_DSSF,
+	DOLBY_PARAM_ID_DVLI, DOLBY_PARAM_ID_DVLO, DOLBY_PARAM_ID_DVLE,
+	DOLBY_PARAM_ID_DVMC, DOLBY_PARAM_ID_DVME, DOLBY_PARAM_ID_IENB,
+	DOLBY_PARAM_ID_IEBF, DOLBY_PARAM_ID_IEON, DOLBY_PARAM_ID_DEON,
+	DOLBY_PARAM_ID_NGON, DOLBY_PARAM_ID_GEON, DOLBY_PARAM_ID_GENB,
+	DOLBY_PARAM_ID_GEBF, DOLBY_PARAM_ID_AONB, DOLBY_PARAM_ID_AOBF,
+	DOLBY_PARAM_ID_AOBG, DOLBY_PARAM_ID_AOON, DOLBY_PARAM_ID_ARNB,
+	DOLBY_PARAM_ID_ARBF, DOLBY_PARAM_ID_PLB,  DOLBY_PARAM_ID_PLMD,
+	DOLBY_PARAM_ID_DHSB, DOLBY_PARAM_ID_DHRG, DOLBY_PARAM_ID_DSSB,
+	DOLBY_PARAM_ID_DSSA, DOLBY_PARAM_ID_DVLA, DOLBY_PARAM_ID_IEBT,
+	DOLBY_PARAM_ID_IEA,  DOLBY_PARAM_ID_DEA,  DOLBY_PARAM_ID_DED,
+	DOLBY_PARAM_ID_GEBG, DOLBY_PARAM_ID_AOCC, DOLBY_PARAM_ID_ARBI,
+	DOLBY_PARAM_ID_ARBL, DOLBY_PARAM_ID_ARBH, DOLBY_PARAM_ID_AROD,
+	DOLBY_PARAM_ID_ARTP, DOLBY_PARAM_ID_VMON, DOLBY_PARAM_ID_VMB,
+	DOLBY_PARAM_ID_VCNB, DOLBY_PARAM_ID_VCBF, DOLBY_PARAM_ID_PREG,
+	DOLBY_PARAM_ID_VEN,  DOLBY_PARAM_ID_PSTG, DOLBY_PARAM_ID_INIT_ENDP,
+};
+
+/* modifed state:	0x00000000 - Not updated
+ *			> 0x00000000 && < 0x00010000
+ *				Updated and not committed to DSP
+ *			0x00010001 - Updated and committed to DSP
+ *			> 0x00010001 - Modified the committed value
+ */
+/* param offset */
+static uint32_t	ds2_dap_params_offset[MAX_DS2_PARAMS] = {
+	DOLBY_PARAM_VDHE_OFFSET, DOLBY_PARAM_VSPE_OFFSET,
+	DOLBY_PARAM_DSSF_OFFSET, DOLBY_PARAM_DVLI_OFFSET,
+	DOLBY_PARAM_DVLO_OFFSET, DOLBY_PARAM_DVLE_OFFSET,
+	DOLBY_PARAM_DVMC_OFFSET, DOLBY_PARAM_DVME_OFFSET,
+	DOLBY_PARAM_IENB_OFFSET, DOLBY_PARAM_IEBF_OFFSET,
+	DOLBY_PARAM_IEON_OFFSET, DOLBY_PARAM_DEON_OFFSET,
+	DOLBY_PARAM_NGON_OFFSET, DOLBY_PARAM_GEON_OFFSET,
+	DOLBY_PARAM_GENB_OFFSET, DOLBY_PARAM_GEBF_OFFSET,
+	DOLBY_PARAM_AONB_OFFSET, DOLBY_PARAM_AOBF_OFFSET,
+	DOLBY_PARAM_AOBG_OFFSET, DOLBY_PARAM_AOON_OFFSET,
+	DOLBY_PARAM_ARNB_OFFSET, DOLBY_PARAM_ARBF_OFFSET,
+	DOLBY_PARAM_PLB_OFFSET,  DOLBY_PARAM_PLMD_OFFSET,
+	DOLBY_PARAM_DHSB_OFFSET, DOLBY_PARAM_DHRG_OFFSET,
+	DOLBY_PARAM_DSSB_OFFSET, DOLBY_PARAM_DSSA_OFFSET,
+	DOLBY_PARAM_DVLA_OFFSET, DOLBY_PARAM_IEBT_OFFSET,
+	DOLBY_PARAM_IEA_OFFSET,  DOLBY_PARAM_DEA_OFFSET,
+	DOLBY_PARAM_DED_OFFSET,  DOLBY_PARAM_GEBG_OFFSET,
+	DOLBY_PARAM_AOCC_OFFSET, DOLBY_PARAM_ARBI_OFFSET,
+	DOLBY_PARAM_ARBL_OFFSET, DOLBY_PARAM_ARBH_OFFSET,
+	DOLBY_PARAM_AROD_OFFSET, DOLBY_PARAM_ARTP_OFFSET,
+	DOLBY_PARAM_VMON_OFFSET, DOLBY_PARAM_VMB_OFFSET,
+	DOLBY_PARAM_VCNB_OFFSET, DOLBY_PARAM_VCBF_OFFSET,
+	DOLBY_PARAM_PREG_OFFSET, DOLBY_PARAM_VEN_OFFSET,
+	DOLBY_PARAM_PSTG_OFFSET, DOLBY_PARAM_INT_ENDP_OFFSET,
+};
+/* param_length */
+static uint32_t	ds2_dap_params_length[MAX_DS2_PARAMS] = {
+	DOLBY_PARAM_VDHE_LENGTH, DOLBY_PARAM_VSPE_LENGTH,
+	DOLBY_PARAM_DSSF_LENGTH, DOLBY_PARAM_DVLI_LENGTH,
+	DOLBY_PARAM_DVLO_LENGTH, DOLBY_PARAM_DVLE_LENGTH,
+	DOLBY_PARAM_DVMC_LENGTH, DOLBY_PARAM_DVME_LENGTH,
+	DOLBY_PARAM_IENB_LENGTH, DOLBY_PARAM_IEBF_LENGTH,
+	DOLBY_PARAM_IEON_LENGTH, DOLBY_PARAM_DEON_LENGTH,
+	DOLBY_PARAM_NGON_LENGTH, DOLBY_PARAM_GEON_LENGTH,
+	DOLBY_PARAM_GENB_LENGTH, DOLBY_PARAM_GEBF_LENGTH,
+	DOLBY_PARAM_AONB_LENGTH, DOLBY_PARAM_AOBF_LENGTH,
+	DOLBY_PARAM_AOBG_LENGTH, DOLBY_PARAM_AOON_LENGTH,
+	DOLBY_PARAM_ARNB_LENGTH, DOLBY_PARAM_ARBF_LENGTH,
+	DOLBY_PARAM_PLB_LENGTH,  DOLBY_PARAM_PLMD_LENGTH,
+	DOLBY_PARAM_DHSB_LENGTH, DOLBY_PARAM_DHRG_LENGTH,
+	DOLBY_PARAM_DSSB_LENGTH, DOLBY_PARAM_DSSA_LENGTH,
+	DOLBY_PARAM_DVLA_LENGTH, DOLBY_PARAM_IEBT_LENGTH,
+	DOLBY_PARAM_IEA_LENGTH,  DOLBY_PARAM_DEA_LENGTH,
+	DOLBY_PARAM_DED_LENGTH,  DOLBY_PARAM_GEBG_LENGTH,
+	DOLBY_PARAM_AOCC_LENGTH, DOLBY_PARAM_ARBI_LENGTH,
+	DOLBY_PARAM_ARBL_LENGTH, DOLBY_PARAM_ARBH_LENGTH,
+	DOLBY_PARAM_AROD_LENGTH, DOLBY_PARAM_ARTP_LENGTH,
+	DOLBY_PARAM_VMON_LENGTH, DOLBY_PARAM_VMB_LENGTH,
+	DOLBY_PARAM_VCNB_LENGTH, DOLBY_PARAM_VCBF_LENGTH,
+	DOLBY_PARAM_PREG_LENGTH, DOLBY_PARAM_VEN_LENGTH,
+	DOLBY_PARAM_PSTG_LENGTH, DOLBY_PARAM_INT_ENDP_LENGTH,
+};
+
+struct ds2_dap_params_s {
+	int32_t params_val[TOTAL_LENGTH_DS2_PARAM];
+	int32_t dap_params_modified[MAX_DS2_PARAMS];
+};
+
+struct audio_rx_cal_data {
+	char aud_proc_data[AUD_PROC_BLOCK_SIZE];
+	int32_t  aud_proc_size;
+	char aud_vol_data[AUD_VOL_BLOCK_SIZE];
+	int32_t aud_vol_size;
+};
+
+static struct ds2_dap_params_s ds2_dap_params[DOLBY_MAX_CACHE];
+
+struct ds2_device_mapping {
+	int32_t device_id; /* audio_out_... */
+	int port_id; /* afe port. constant for a target variant. routing-v2*/
+	/*Only one Dolby COPP  for a specific port*/
+	int copp_idx; /* idx for the copp port on which ds2 is active */
+	int cache_dev; /* idx to a shared parameter array dependent on device*/
+	uint32_t stream_ref_count;
+	bool active;
+	void *cal_data;
+};
+
+static struct ds2_device_mapping dev_map[DS2_DEVICES_ALL];
+
+struct ds2_dap_params_states_s {
+	bool use_cache;
+	bool dap_bypass;
+	bool dap_bypass_type;
+	bool node_opened;
+	int32_t  device;
+	bool custom_stereo_onoff;
+};
+
+static struct ds2_dap_params_states_s ds2_dap_params_states = {true, false,
+				false, DEVICE_NONE};
+
+static int all_supported_devices = EARPIECE|SPEAKER|WIRED_HEADSET|
+			WIRED_HEADPHONE|BLUETOOTH_SCO|AUX_DIGITAL|
+			ANLG_DOCK_HEADSET|DGTL_DOCK_HEADSET|
+			REMOTE_SUBMIX|ANC_HEADSET|ANC_HEADPHONE|
+			PROXY|FM|FM_TX|DEVICE_NONE|
+			BLUETOOTH_SCO_HEADSET|BLUETOOTH_SCO_CARKIT;
+
+
+static void msm_ds2_dap_check_and_update_ramp_wait(int port_id, int copp_idx,
+						   int *ramp_wait)
+{
+
+	int32_t *update_params_value = NULL;
+	uint32_t params_length = SOFT_VOLUME_PARAM_SIZE * sizeof(uint32_t);
+	uint32_t param_payload_len = PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+	int rc = 0;
+
+	update_params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!update_params_value)
+		goto end;
+
+	rc = adm_get_params(port_id, copp_idx,
+			    AUDPROC_MODULE_ID_VOL_CTRL,
+			    AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS,
+			    params_length + param_payload_len,
+			    (char *) update_params_value);
+	if (rc == 0) {
+		pr_debug("%s: params_value [0x%x, 0x%x, 0x%x]\n",
+			__func__, update_params_value[0],
+			update_params_value[1],
+			update_params_value[2]);
+		*ramp_wait = update_params_value[0];
+	}
+end:
+	kfree(update_params_value);
+	/*
+	 * No error returned as we do not need to error out from dap on/dap
+	 * bypass. The default ramp parameter will be used to wait during
+	 * ramp down.
+	 */
+}
+
+static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx,
+				     bool is_custom_stereo_enabled)
+{
+	int32_t *update_params_value = NULL;
+	int32_t *param_val = NULL;
+	int idx, i, j, rc = 0, cdev;
+	uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM +
+				2 * DOLBY_PARAM_PAYLOAD_SIZE) *
+				sizeof(uint32_t);
+
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s: Invalid port id\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if ((dev_map[dev_map_idx].copp_idx < 0) ||
+		(dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_err("%s: Invalid copp_idx\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) &&
+	     (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) {
+		pr_debug("%s:No Custom stereo for port:0x%x\n",
+			 __func__, dev_map[dev_map_idx].port_id);
+		goto end;
+	}
+
+	update_params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!update_params_value) {
+		rc = -ENOMEM;
+		goto end;
+	}
+	params_length = 0;
+	param_val = update_params_value;
+	cdev = dev_map[dev_map_idx].cache_dev;
+	/* for VDHE and VSPE DAP params at index 0 and 1 in table */
+	for (i = 0; i < 2; i++) {
+		*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+		*update_params_value++ = ds2_dap_params_id[i];
+		*update_params_value++ = ds2_dap_params_length[i] *
+					sizeof(uint32_t);
+		idx = ds2_dap_params_offset[i];
+		for (j = 0; j < ds2_dap_params_length[i]; j++) {
+			if (is_custom_stereo_enabled)
+				*update_params_value++ = 0;
+			else
+				*update_params_value++ =
+					ds2_dap_params[cdev].params_val[idx+j];
+		}
+		params_length += (DOLBY_PARAM_PAYLOAD_SIZE +
+				  ds2_dap_params_length[i]) *
+				  sizeof(uint32_t);
+	}
+
+	pr_debug("%s: valid param length: %d\n", __func__, params_length);
+	if (params_length) {
+		rc = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id,
+					       dev_map[dev_map_idx].copp_idx,
+					       (char *)param_val,
+					       params_length);
+		if (rc) {
+			pr_err("%s: send vdhe/vspe params failed with rc=%d\n",
+				__func__, rc);
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+end:
+	kfree(param_val);
+	return rc;
+}
+
+int qti_set_custom_stereo_on(int port_id, int copp_idx,
+			     bool is_custom_stereo_on)
+{
+
+	uint16_t op_FL_ip_FL_weight;
+	uint16_t op_FL_ip_FR_weight;
+	uint16_t op_FR_ip_FL_weight;
+	uint16_t op_FR_ip_FR_weight;
+
+	int32_t *update_params_value32 = NULL, rc = 0;
+	int32_t *param_val = NULL;
+	int16_t *update_params_value16 = 0;
+	uint32_t params_length_bytes = CUSTOM_STEREO_PAYLOAD_SIZE *
+				       sizeof(uint32_t);
+	uint32_t avail_length = params_length_bytes;
+
+	if ((port_id != SLIMBUS_0_RX) &&
+	     (port_id != RT_PROXY_PORT_001_RX)) {
+		pr_debug("%s:No Custom stereo for port:0x%x\n",
+			 __func__, port_id);
+		goto skip_send_cmd;
+	}
+
+	pr_debug("%s: port 0x%x, copp_idx %d, is_custom_stereo_on %d\n",
+		 __func__, port_id, copp_idx, is_custom_stereo_on);
+	if (is_custom_stereo_on) {
+		op_FL_ip_FL_weight =
+			Q14_GAIN_ZERO_POINT_FIVE;
+		op_FL_ip_FR_weight =
+			Q14_GAIN_ZERO_POINT_FIVE;
+		op_FR_ip_FL_weight =
+			Q14_GAIN_ZERO_POINT_FIVE;
+		op_FR_ip_FR_weight =
+			Q14_GAIN_ZERO_POINT_FIVE;
+	} else {
+		op_FL_ip_FL_weight = Q14_GAIN_UNITY;
+		op_FL_ip_FR_weight = 0;
+		op_FR_ip_FL_weight = 0;
+		op_FR_ip_FR_weight = Q14_GAIN_UNITY;
+	}
+
+	update_params_value32 = kzalloc(params_length_bytes, GFP_KERNEL);
+	if (!update_params_value32) {
+		rc = -ENOMEM;
+		goto skip_send_cmd;
+	}
+	param_val = update_params_value32;
+	if (avail_length < 2 * sizeof(uint32_t))
+		goto skip_send_cmd;
+	*update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER;
+	*update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF;
+	avail_length = avail_length - (2 * sizeof(uint32_t));
+
+	update_params_value16 = (int16_t *)update_params_value32;
+	if (avail_length < 10 * sizeof(uint16_t))
+		goto skip_send_cmd;
+	*update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE;
+	/* for alignment only*/
+	*update_params_value16++ = 0;
+	/* index is 32-bit param in little endian*/
+	*update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM;
+	*update_params_value16++ = 0;
+	/* for stereo mixing num out ch*/
+	*update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH;
+	/* for stereo mixing num in ch*/
+	*update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH;
+
+	/* Out ch map FL/FR*/
+	*update_params_value16++ = PCM_CHANNEL_FL;
+	*update_params_value16++ = PCM_CHANNEL_FR;
+
+	/* In ch map FL/FR*/
+	*update_params_value16++ = PCM_CHANNEL_FL;
+	*update_params_value16++ = PCM_CHANNEL_FR;
+	avail_length = avail_length - (10 * sizeof(uint16_t));
+	/* weighting coefficients as name suggests,
+	 * mixing will be done according to these coefficients
+	 */
+	if (avail_length < 4 * sizeof(uint16_t))
+		goto skip_send_cmd;
+	*update_params_value16++ = op_FL_ip_FL_weight;
+	*update_params_value16++ = op_FL_ip_FR_weight;
+	*update_params_value16++ = op_FR_ip_FL_weight;
+	*update_params_value16++ = op_FR_ip_FR_weight;
+	avail_length = avail_length - (4 * sizeof(uint16_t));
+	if (params_length_bytes != 0) {
+		rc = adm_dolby_dap_send_params(port_id, copp_idx,
+				(char *)param_val,
+				params_length_bytes);
+		if (rc) {
+			pr_err("%s: send params failed rc=%d\n", __func__, rc);
+			rc = -EINVAL;
+			goto skip_send_cmd;
+		}
+	}
+	kfree(param_val);
+	return 0;
+skip_send_cmd:
+		pr_err("%s: insufficient memory, send cmd failed\n",
+			__func__);
+		kfree(param_val);
+		return rc;
+}
+static int dap_set_custom_stereo_onoff(int dev_map_idx,
+					bool is_custom_stereo_enabled)
+{
+
+	int32_t *update_params_value = NULL, rc = 0;
+	int32_t *param_val = NULL;
+	uint32_t params_length_bytes = (TOTAL_LENGTH_DOLBY_PARAM +
+				DOLBY_PARAM_PAYLOAD_SIZE) * sizeof(uint32_t);
+	if ((dev_map[dev_map_idx].port_id != SLIMBUS_0_RX) &&
+	     (dev_map[dev_map_idx].port_id != RT_PROXY_PORT_001_RX)) {
+		pr_debug("%s:No Custom stereo for port:0x%x\n",
+			 __func__, dev_map[dev_map_idx].port_id);
+		goto end;
+	}
+
+	if ((dev_map[dev_map_idx].copp_idx < 0) ||
+		(dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+		rc = -EINVAL;
+		goto end;
+	}
+
+	/* DAP custom stereo */
+	msm_ds2_dap_set_vspe_vdhe(dev_map_idx,
+				  is_custom_stereo_enabled);
+	update_params_value = kzalloc(params_length_bytes, GFP_KERNEL);
+	if (!update_params_value) {
+		pr_err("%s: params memory alloc failed\n", __func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+	params_length_bytes = 0;
+	param_val = update_params_value;
+	*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+	*update_params_value++ = DOLBY_ENABLE_CUSTOM_STEREO;
+	*update_params_value++ = sizeof(uint32_t);
+	if (is_custom_stereo_enabled)
+		*update_params_value++ = 1;
+	else
+		*update_params_value++ = 0;
+	params_length_bytes += (DOLBY_PARAM_PAYLOAD_SIZE + 1) *
+				sizeof(uint32_t);
+	pr_debug("%s: valid param length: %d\n", __func__, params_length_bytes);
+	if (params_length_bytes) {
+		rc = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id,
+					       dev_map[dev_map_idx].copp_idx,
+					       (char *)param_val,
+					       params_length_bytes);
+		if (rc) {
+			pr_err("%s: custom stereo param failed with rc=%d\n",
+				__func__, rc);
+			rc = -EINVAL;
+			goto end;
+		}
+	}
+end:
+	kfree(param_val);
+	return rc;
+
+}
+
+
+static int set_custom_stereo_onoff(int dev_map_idx,
+					bool is_custom_stereo_enabled)
+{
+	int rc = 0;
+
+	pr_debug("%s: map index %d, custom stereo %d\n", __func__, dev_map_idx,
+		 is_custom_stereo_enabled);
+
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s: invalid port id\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if ((dev_map[dev_map_idx].copp_idx < 0) ||
+		(dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_err("%s: invalid copp idx\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (ds2_dap_params_states.dap_bypass == true &&
+		ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS) {
+
+		rc = qti_set_custom_stereo_on(dev_map[dev_map_idx].port_id,
+					      dev_map[dev_map_idx].copp_idx,
+					      is_custom_stereo_enabled);
+		if (rc < 0) {
+			pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d",
+				__func__, is_custom_stereo_enabled);
+		}
+		goto end;
+
+	}
+
+	if (ds2_dap_params_states.dap_bypass == false) {
+		rc = dap_set_custom_stereo_onoff(dev_map_idx,
+						 is_custom_stereo_enabled);
+		if (rc < 0) {
+			pr_err("%s:qti_set_custom_stereo_on_copp failed C.S %d",
+				__func__, is_custom_stereo_enabled);
+		}
+		goto end;
+	}
+end:
+	return rc;
+}
+
+static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path,
+					    int perf_mode)
+{
+	int rc = 0;
+	struct audio_rx_cal_data *aud_cal_data;
+
+	pr_debug("%s: path %d, perf_mode %d, dev_map_idx %d\n",
+		__func__, path, perf_mode, dev_map_idx);
+
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	aud_cal_data = kzalloc(sizeof(struct audio_rx_cal_data), GFP_KERNEL);
+	if (!aud_cal_data) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	rc = adm_store_cal_data(dev_map[dev_map_idx].port_id,
+				dev_map[dev_map_idx].copp_idx, path, perf_mode,
+				ADM_AUDPROC_CAL, aud_cal_data->aud_proc_data,
+				&aud_cal_data->aud_proc_size);
+	if (rc < 0) {
+		pr_err("%s: store cal data err %d\n", __func__, rc);
+		kfree(aud_cal_data);
+		goto end;
+	}
+
+	rc = adm_store_cal_data(dev_map[dev_map_idx].port_id,
+				dev_map[dev_map_idx].copp_idx, path, perf_mode,
+				ADM_AUDVOL_CAL, aud_cal_data->aud_vol_data,
+				&aud_cal_data->aud_vol_size);
+	if (rc < 0) {
+		pr_err("%s: store cal data err %d\n", __func__, rc);
+		kfree(aud_cal_data);
+		goto end;
+	}
+
+	dev_map[dev_map_idx].cal_data = (void *)aud_cal_data;
+
+end:
+	pr_debug("%s: ret %d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_ds2_dap_free_cal_data(int dev_map_idx)
+{
+	int rc = 0;
+	struct audio_rx_cal_data *aud_cal_data;
+
+	pr_debug("%s: dev_map_idx %d\n", __func__, dev_map_idx);
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+	aud_cal_data = (struct audio_rx_cal_data *)
+				dev_map[dev_map_idx].cal_data;
+	kfree(aud_cal_data);
+	dev_map[dev_map_idx].cal_data = NULL;
+
+end:
+	return rc;
+}
+
+static int msm_ds2_dap_send_cal_data(int dev_map_idx)
+{
+	int rc = 0;
+	struct audio_rx_cal_data *aud_cal_data = NULL;
+
+	pr_debug("%s: devmap index %d\n", __func__, dev_map_idx);
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (dev_map[dev_map_idx].cal_data == NULL) {
+		pr_err("%s: No valid calibration data stored for idx %d\n",
+			__func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	/* send aud proc cal */
+	aud_cal_data = (struct audio_rx_cal_data *)
+				dev_map[dev_map_idx].cal_data;
+	rc = adm_send_calibration(dev_map[dev_map_idx].port_id,
+				  dev_map[dev_map_idx].copp_idx,
+				  ADM_PATH_PLAYBACK, 0,
+				  ADM_AUDPROC_CAL,
+				  aud_cal_data->aud_proc_data,
+				  aud_cal_data->aud_proc_size);
+	if (rc < 0) {
+		pr_err("%s: adm_send_calibration failed %d\n", __func__, rc);
+		goto end;
+	}
+
+	/* send aud volume cal*/
+	rc = adm_send_calibration(dev_map[dev_map_idx].port_id,
+				  dev_map[dev_map_idx].copp_idx,
+				  ADM_PATH_PLAYBACK, 0,
+				  ADM_AUDVOL_CAL,
+				  aud_cal_data->aud_vol_data,
+				  aud_cal_data->aud_vol_size);
+	if (rc < 0)
+		pr_err("%s: adm_send_calibration failed %d\n", __func__, rc);
+end:
+	pr_debug("%s: return  %d\n", __func__, rc);
+	return rc;
+}
+
+static inline int msm_ds2_dap_can_enable_module(int32_t module_id)
+{
+	if (module_id == MTMX_MODULE_ID_DEFAULT_CHMIXER ||
+		module_id == AUDPROC_MODULE_ID_RESAMPLER ||
+		module_id == AUDPROC_MODULE_ID_VOL_CTRL) {
+		return false;
+	}
+	return true;
+}
+
+static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx)
+{
+	int rc = 0, i = 0, port_id, copp_idx;
+	/* Account for 32 bit integer allocation */
+	int32_t param_sz = (ADM_GET_TOPO_MODULE_LIST_LENGTH / sizeof(uint32_t));
+	int32_t *update_param_val = NULL;
+
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	port_id = dev_map[dev_map_idx].port_id;
+	copp_idx = dev_map[dev_map_idx].copp_idx;
+	pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id, copp_idx);
+	update_param_val = kzalloc(ADM_GET_TOPO_MODULE_LIST_LENGTH, GFP_KERNEL);
+	if (!update_param_val) {
+		pr_err("%s, param memory alloc failed\n", __func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	if (!ds2_dap_params_states.dap_bypass) {
+		/* get modules from dsp */
+		rc = adm_get_pp_topo_module_list(port_id, copp_idx,
+			ADM_GET_TOPO_MODULE_LIST_LENGTH,
+			(char *)update_param_val);
+		if (rc < 0) {
+			pr_err("%s:topo list port %d, err %d,copp_idx %d\n",
+				__func__, port_id, copp_idx, rc);
+			goto end;
+		}
+
+		if (update_param_val[0] > (param_sz - 1)) {
+			pr_err("%s:max modules exp/ret [%d: %d]\n",
+				__func__, (param_sz - 1),
+				update_param_val[0]);
+			rc = -EINVAL;
+			goto end;
+		}
+		/* Turn off modules */
+		for (i = 1; i < update_param_val[0]; i++) {
+			if (!msm_ds2_dap_can_enable_module(
+				update_param_val[i]) ||
+				(update_param_val[i] == DS2_MODULE_ID)) {
+				pr_debug("%s: Do not enable/disable %d\n",
+					 __func__, update_param_val[i]);
+				continue;
+			}
+
+			pr_debug("%s: param disable %d\n",
+				__func__, update_param_val[i]);
+			adm_param_enable(port_id, copp_idx, update_param_val[i],
+					 MODULE_DISABLE);
+		}
+	} else {
+		msm_ds2_dap_send_cal_data(dev_map_idx);
+
+	}
+	adm_param_enable(port_id, copp_idx, DS2_MODULE_ID,
+			 !ds2_dap_params_states.dap_bypass);
+end:
+	kfree(update_param_val);
+	return rc;
+}
+
+static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified,
+				    int32_t idx, int32_t commit)
+{
+	if ((dap_params_modified[idx] == 0) ||
+		(commit &&
+		((dap_params_modified[idx] & 0x00010000) &&
+		((dap_params_modified[idx] & 0x0000FFFF) <= 1)))) {
+		pr_debug("%s: not modified at idx %d\n", __func__, idx);
+		return false;
+	}
+	pr_debug("%s: modified at idx %d\n", __func__, idx);
+	return true;
+}
+
+static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id)
+{
+	int32_t cache_dev = -1;
+
+	switch (device_id) {
+	case DEVICE_NONE:
+		cache_dev = DOLBY_OFF_CACHE;
+		break;
+	case EARPIECE:
+	case SPEAKER:
+		cache_dev = DOLBY_SPEAKER_CACHE;
+		break;
+	case WIRED_HEADSET:
+	case WIRED_HEADPHONE:
+	case ANLG_DOCK_HEADSET:
+	case DGTL_DOCK_HEADSET:
+	case ANC_HEADSET:
+	case ANC_HEADPHONE:
+	case BLUETOOTH_SCO:
+	case BLUETOOTH_SCO_HEADSET:
+	case BLUETOOTH_SCO_CARKIT:
+		cache_dev = DOLBY_HEADPHONE_CACHE;
+		break;
+	case FM:
+	case FM_TX:
+		cache_dev = DOLBY_FM_CACHE;
+		break;
+	case AUX_DIGITAL:
+		cache_dev = DOLBY_HDMI_CACHE;
+		break;
+	case PROXY:
+	case REMOTE_SUBMIX:
+		cache_dev = DOLBY_WFD_CACHE;
+		break;
+	default:
+		pr_err("%s: invalid cache device\n", __func__);
+	}
+	pr_debug("%s: cache device %d\n", __func__, cache_dev);
+	return cache_dev;
+}
+
+static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data,
+				      int32_t *num_device, int32_t *dev_arr,
+				      int32_t array_size)
+{
+	int32_t idx = 0;
+	int supported_devices = 0;
+
+	if (!array_size) {
+		pr_err("%s: array size zero\n", __func__);
+		return -EINVAL;
+	}
+
+	if (dolby_data->device_id == DEVICE_OUT_ALL ||
+		dolby_data->device_id == DEVICE_OUT_DEFAULT)
+		supported_devices = all_supported_devices;
+	else
+		supported_devices = dolby_data->device_id;
+
+	if ((idx < array_size) && (supported_devices & EARPIECE))
+		dev_arr[idx++] = EARPIECE;
+	if ((idx < array_size) && (supported_devices & SPEAKER))
+		dev_arr[idx++] = SPEAKER;
+	if ((idx < array_size) && (supported_devices & WIRED_HEADSET))
+		dev_arr[idx++] = WIRED_HEADSET;
+	if ((idx < array_size) && (supported_devices & WIRED_HEADPHONE))
+		dev_arr[idx++] = WIRED_HEADPHONE;
+	if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO))
+		dev_arr[idx++] = BLUETOOTH_SCO;
+	if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_CARKIT))
+		dev_arr[idx++] = BLUETOOTH_SCO_CARKIT;
+	if ((idx < array_size) && (supported_devices & BLUETOOTH_SCO_HEADSET))
+		dev_arr[idx++] = BLUETOOTH_SCO_HEADSET;
+	if ((idx < array_size) && (supported_devices & AUX_DIGITAL))
+		dev_arr[idx++] = AUX_DIGITAL;
+	if ((idx < array_size) && (supported_devices & ANLG_DOCK_HEADSET))
+		dev_arr[idx++] = ANLG_DOCK_HEADSET;
+	if ((idx < array_size) && (supported_devices & DGTL_DOCK_HEADSET))
+		dev_arr[idx++] = DGTL_DOCK_HEADSET;
+	if ((idx < array_size) && (supported_devices & REMOTE_SUBMIX))
+		dev_arr[idx++] = REMOTE_SUBMIX;
+	if ((idx < array_size) && (supported_devices & ANC_HEADSET))
+		dev_arr[idx++] = ANC_HEADSET;
+	if ((idx < array_size) && (supported_devices & ANC_HEADPHONE))
+		dev_arr[idx++] = ANC_HEADPHONE;
+	if ((idx < array_size) && (supported_devices & PROXY))
+		dev_arr[idx++] = PROXY;
+	if ((idx < array_size) && (supported_devices & FM))
+		dev_arr[idx++] = FM;
+	if ((idx < array_size) && (supported_devices & FM_TX))
+		dev_arr[idx++] = FM_TX;
+	/* CHECK device none separately */
+	if ((idx < array_size) && (supported_devices == DEVICE_NONE))
+		dev_arr[idx++] = DEVICE_NONE;
+	pr_debug("%s: dev id 0x%x, idx %d\n", __func__,
+		 supported_devices, idx);
+	*num_device = idx;
+	return 0;
+}
+
+static int msm_ds2_dap_get_port_id(
+		int32_t device_id, int32_t be_id)
+{
+	struct msm_pcm_routing_bdai_data bedais;
+	int port_id = DOLBY_INVALID_PORT_ID;
+	int port_type = 0;
+
+	if (be_id < 0) {
+		port_id = -1;
+		goto end;
+	}
+
+	msm_pcm_routing_get_bedai_info(be_id, &bedais);
+	pr_debug("%s: be port_id %d\n", __func__, bedais.port_id);
+	port_id = bedais.port_id;
+	port_type = afe_get_port_type(bedais.port_id);
+	if (port_type != MSM_AFE_PORT_TYPE_RX)
+		port_id = DOLBY_INVALID_PORT_ID;
+end:
+	pr_debug("%s: device_id 0x%x, be_id %d, port_id %d\n",
+		 __func__, device_id, be_id, port_id);
+	return port_id;
+}
+
+static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id)
+{
+	int i;
+
+	for (i = 0; i < DS2_DEVICES_ALL; i++) {
+		if (dev_map[i].device_id == device_id)
+			dev_map[i].port_id = port_id;
+	}
+	pr_debug("%s: port_id %d, device_id 0x%x\n",
+		 __func__, port_id, device_id);
+	return 0;
+}
+
+static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx,
+					  int wait_time)
+{
+	int ret = 0;
+
+	adm_set_wait_parameters(port_id, copp_idx);
+	msm_pcm_routing_release_lock();
+	ret = adm_wait_timeout(port_id, copp_idx, wait_time);
+	msm_pcm_routing_acquire_lock();
+	/* Reset the parameters if wait has timed out */
+	if (ret == 0)
+		adm_reset_wait_parameters(port_id, copp_idx);
+	return ret;
+}
+
+static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data)
+{
+	int rc = 0, i = 0, j = 0;
+	/*Account for 32 bit integer allocation  */
+	int32_t param_sz = (ADM_GET_TOPO_MODULE_LIST_LENGTH / sizeof(uint32_t));
+	int32_t *mod_list = NULL;
+	int port_id = 0, copp_idx = -1;
+	bool cs_onoff = ds2_dap_params_states.custom_stereo_onoff;
+	int ramp_wait = DOLBY_SOFT_VOLUME_PERIOD;
+
+	pr_debug("%s: bypass type %d bypass %d custom stereo %d\n", __func__,
+		 ds2_dap_params_states.dap_bypass_type,
+		 ds2_dap_params_states.dap_bypass,
+		 ds2_dap_params_states.custom_stereo_onoff);
+	mod_list = kzalloc(ADM_GET_TOPO_MODULE_LIST_LENGTH, GFP_KERNEL);
+	if (!mod_list) {
+		pr_err("%s: param memory alloc failed\n", __func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	for (i = 0; i < DS2_DEVICES_ALL; i++) {
+		pr_debug("%s: active dev %d\n", __func__, dev_map[i].active);
+		if (dev_map[i].active) {
+			port_id = dev_map[i].port_id;
+			copp_idx = dev_map[i].copp_idx;
+
+			if (port_id == DOLBY_INVALID_PORT_ID) {
+				pr_err("%s: invalid port\n", __func__);
+				rc = 0;
+				goto end;
+			}
+
+			if ((copp_idx < 0) ||
+				(copp_idx >= MAX_COPPS_PER_PORT)) {
+				pr_err("%s: Invalid copp_idx\n", __func__);
+				rc = 0;
+				goto end;
+			}
+
+			/* getmodules from dsp */
+			rc = adm_get_pp_topo_module_list(port_id, copp_idx,
+				    ADM_GET_TOPO_MODULE_LIST_LENGTH,
+				    (char *)mod_list);
+			if (rc < 0) {
+				pr_err("%s:adm get topo list port %d",
+					__func__, port_id);
+				pr_err("copp_idx %d, err %d\n",
+					copp_idx, rc);
+				goto end;
+			}
+			if (mod_list[0] > (param_sz - 1)) {
+				pr_err("%s:max modules exp/ret [%d: %d]\n",
+					__func__, (param_sz - 1),
+					mod_list[0]);
+				rc = -EINVAL;
+				goto end;
+			}
+			/*
+			 * get ramp parameters
+			 * check for change in ramp parameters
+			 * update ramp wait
+			 */
+			msm_ds2_dap_check_and_update_ramp_wait(port_id,
+							       copp_idx,
+							       &ramp_wait);
+
+			/* Mute before switching modules */
+			rc = adm_set_volume(port_id, copp_idx,
+					    VOLUME_ZERO_GAIN);
+			if (rc < 0) {
+				/*
+				 * Not Fatal can continue bypass operations.
+				 * Do not need to block playback
+				 */
+				pr_info("%s :Set volume port_id %d",
+					__func__, port_id);
+				pr_info("copp_idx %d, error %d\n",
+					copp_idx, rc);
+			}
+
+			rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx,
+					    (ramp_wait +
+					     DOLBY_ADDITIONAL_RAMP_WAIT));
+			if (rc == -EINTR) {
+				pr_info("%s:bypass interrupted-ignore,port %d",
+					__func__, port_id);
+				pr_info("copp_idx %d\n", copp_idx);
+				rc = 0;
+				continue;
+			}
+
+			/* if dap bypass is set */
+			if (ds2_dap_params_states.dap_bypass) {
+				/* Turn off dap module */
+				adm_param_enable(port_id, copp_idx,
+						 DS2_MODULE_ID, MODULE_DISABLE);
+				/*
+				 * If custom stereo is on at the time of bypass,
+				 * switch off custom stereo on dap and turn on
+				 * custom stereo on qti channel mixer.
+				 */
+				if (cs_onoff) {
+					rc = dap_set_custom_stereo_onoff(i,
+								!cs_onoff);
+					if (rc < 0) {
+						pr_info("%s:D_CS i %d,rc %d\n",
+							__func__, i, rc);
+					}
+					rc = qti_set_custom_stereo_on(port_id,
+								      copp_idx,
+								      cs_onoff);
+					if (rc < 0) {
+						pr_info("%s:Q_CS port id 0x%x",
+							 __func__, port_id);
+						pr_info("copp idx %d, rc %d\n",
+							copp_idx, rc);
+					}
+				}
+				/* Add adm api to resend calibration on port */
+				rc = msm_ds2_dap_send_cal_data(i);
+				if (rc < 0) {
+					/*
+					 * Not fatal,continue bypass operations.
+					 * Do not need to block playback
+					 */
+					pr_info("%s:send cal err %d index %d\n",
+						__func__, rc, i);
+				}
+			} else {
+				/* Turn off qti modules */
+				for (j = 1; j < mod_list[0]; j++) {
+					if (!msm_ds2_dap_can_enable_module(
+						mod_list[j]) ||
+						mod_list[j] ==
+						DS2_MODULE_ID)
+						continue;
+					pr_debug("%s: param disable %d\n",
+						__func__, mod_list[j]);
+					adm_param_enable(port_id, copp_idx,
+							 mod_list[j],
+							 MODULE_DISABLE);
+				}
+
+				/* Enable DAP modules */
+				pr_debug("%s:DS2 param enable\n", __func__);
+				adm_param_enable(port_id, copp_idx,
+						 DS2_MODULE_ID, MODULE_ENABLE);
+				/*
+				 * If custom stereo is on at the time of dap on,
+				 * switch off custom stereo on qti channel mixer
+				 * and turn on custom stereo on DAP.
+				 * mixer(qti).
+				 */
+				if (cs_onoff) {
+					rc = qti_set_custom_stereo_on(port_id,
+								copp_idx,
+								!cs_onoff);
+					if (rc < 0) {
+						pr_info("%s:Q_CS port_id 0x%x",
+							__func__, port_id);
+						pr_info("copp_idx %d rc %d\n",
+							copp_idx, rc);
+					}
+					rc = dap_set_custom_stereo_onoff(i,
+								cs_onoff);
+					if (rc < 0) {
+						pr_info("%s:D_CS i %d,rc %d\n",
+							__func__, i, rc);
+					}
+				}
+			}
+
+			rc = msm_ds2_dap_handle_bypass_wait(port_id, copp_idx,
+				DOLBY_MODULE_ENABLE_PERIOD);
+			if (rc == -EINTR) {
+				pr_info("%s:bypass interrupted port_id %d copp_idx %d\n",
+					__func__, port_id, copp_idx);
+				/* Interrupted ignore bypass */
+				rc = 0;
+				continue;
+			}
+
+			/* set volume to unity gain after module on/off */
+			rc = adm_set_volume(port_id, copp_idx,
+					    VOLUME_UNITY_GAIN);
+			if (rc < 0) {
+				/*
+				 * Not Fatal can continue bypass operations.
+				 * Do not need to block playback
+				 */
+				pr_info("%s: Set vol port %d copp %d, rc %d\n",
+					__func__, port_id, copp_idx, rc);
+				rc = 0;
+			}
+		}
+	}
+
+end:
+	kfree(mod_list);
+	pr_debug("%s:return rc=%d\n", __func__, rc);
+	return rc;
+}
+
+static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx)
+{
+	int rc = 0;
+	int32_t  *update_params_value = NULL, *params_value = NULL;
+	uint32_t params_length = (DOLBY_PARAM_INT_ENDP_LENGTH +
+				DOLBY_PARAM_PAYLOAD_SIZE) * sizeof(uint32_t);
+	int cache_device = 0;
+	struct ds2_dap_params_s *ds2_ap_params_obj = NULL;
+	int32_t *modified_param = NULL;
+
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		rc = -EINVAL;
+		goto end;
+	}
+	cache_device = dev_map[dev_map_idx].cache_dev;
+
+	ds2_ap_params_obj = &ds2_dap_params[cache_device];
+	pr_debug("%s: cache dev %d, dev_map_idx %d\n", __func__,
+		 cache_device, dev_map_idx);
+	pr_debug("%s: endp - %pK %pK\n",  __func__,
+		 &ds2_dap_params[cache_device], ds2_ap_params_obj);
+
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s: invalid port\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if ((dev_map[dev_map_idx].copp_idx < 0) ||
+		(dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_err("%s: Invalid copp_idx\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	update_params_value = params_value;
+	*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+	*update_params_value++ = DOLBY_PARAM_ID_INIT_ENDP;
+	*update_params_value++ = DOLBY_PARAM_INT_ENDP_LENGTH * sizeof(uint32_t);
+	*update_params_value++ = ds2_ap_params_obj->params_val[
+					ds2_dap_params_offset[endp_idx]];
+	pr_debug("%s: off %d, length %d\n", __func__,
+		 ds2_dap_params_offset[endp_idx],
+		 ds2_dap_params_length[endp_idx]);
+	pr_debug("%s: param 0x%x, param val %d\n", __func__,
+		 ds2_dap_params_id[endp_idx], ds2_ap_params_obj->
+		 params_val[ds2_dap_params_offset[endp_idx]]);
+	rc = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id,
+				       dev_map[dev_map_idx].copp_idx,
+				       (char *)params_value, params_length);
+	if (rc) {
+		pr_err("%s: send dolby params failed rc %d\n", __func__, rc);
+		rc = -EINVAL;
+	}
+	modified_param = ds2_ap_params_obj->dap_params_modified;
+	if (modified_param == NULL) {
+		pr_err("%s: modified param structure invalid\n",
+		       __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (msm_ds2_dap_check_is_param_modified(modified_param, endp_idx, 0))
+		ds2_ap_params_obj->dap_params_modified[endp_idx] = 0x00010001;
+
+end:
+	kfree(params_value);
+	return rc;
+}
+
+static int msm_ds2_dap_send_cached_params(int dev_map_idx,
+					  int commit)
+{
+	int32_t *update_params_value = NULL, *params_value = NULL;
+	uint32_t idx, i, j, ret = 0;
+	uint32_t params_length = (TOTAL_LENGTH_DOLBY_PARAM +
+				(MAX_DS2_PARAMS - 1) *
+				DOLBY_PARAM_PAYLOAD_SIZE) *
+				sizeof(uint32_t);
+	int cache_device = 0;
+	struct ds2_dap_params_s *ds2_ap_params_obj = NULL;
+	int32_t *modified_param = NULL;
+
+	if (dev_map_idx < 0 || dev_map_idx >= DS2_DEVICES_ALL) {
+		pr_err("%s: invalid dev map index %d\n", __func__, dev_map_idx);
+		ret = -EINVAL;
+		goto end;
+	}
+	cache_device = dev_map[dev_map_idx].cache_dev;
+
+	/* Use off profile cache in only for soft bypass */
+	if (ds2_dap_params_states.dap_bypass_type == DAP_SOFT_BYPASS &&
+		ds2_dap_params_states.dap_bypass == true) {
+		pr_debug("%s: use bypass cache 0\n", __func__);
+		cache_device =  dev_map[0].cache_dev;
+	}
+
+	ds2_ap_params_obj = &ds2_dap_params[cache_device];
+	pr_debug("%s: cached param - %pK %pK, cache_device %d\n", __func__,
+		 &ds2_dap_params[cache_device], ds2_ap_params_obj,
+		 cache_device);
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		pr_err("%s: params memory alloc failed\n", __func__);
+		ret =  -ENOMEM;
+		goto end;
+	}
+
+	if (dev_map[dev_map_idx].port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s: invalid port id\n", __func__);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if ((dev_map[dev_map_idx].copp_idx < 0) ||
+		(dev_map[dev_map_idx].copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_err("%s: Invalid copp_idx\n", __func__);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	update_params_value = params_value;
+	params_length = 0;
+	for (i = 0; i < (MAX_DS2_PARAMS-1); i++) {
+		/*get the pointer to the param modified array in the cache*/
+		modified_param = ds2_ap_params_obj->dap_params_modified;
+		if (modified_param == NULL) {
+			pr_err("%s: modified param structure invalid\n",
+			       __func__);
+			ret = -EINVAL;
+			goto end;
+		}
+		if (!msm_ds2_dap_check_is_param_modified(modified_param, i,
+							 commit))
+			continue;
+		*update_params_value++ = DOLBY_BUNDLE_MODULE_ID;
+		*update_params_value++ = ds2_dap_params_id[i];
+		*update_params_value++ = ds2_dap_params_length[i] *
+						sizeof(uint32_t);
+		idx = ds2_dap_params_offset[i];
+		for (j = 0; j < ds2_dap_params_length[i]; j++) {
+			*update_params_value++ =
+					ds2_ap_params_obj->params_val[idx+j];
+			pr_debug("%s: id 0x%x,val %d\n", __func__,
+				 ds2_dap_params_id[i],
+				 ds2_ap_params_obj->params_val[idx+j]);
+		}
+		params_length += (DOLBY_PARAM_PAYLOAD_SIZE +
+				ds2_dap_params_length[i]) * sizeof(uint32_t);
+	}
+
+	pr_debug("%s: valid param length: %d\n", __func__, params_length);
+	if (params_length) {
+		ret = adm_dolby_dap_send_params(dev_map[dev_map_idx].port_id,
+						dev_map[dev_map_idx].copp_idx,
+						(char *)params_value,
+						params_length);
+		if (ret) {
+			pr_err("%s: send dolby params failed ret %d\n",
+				__func__, ret);
+			ret = -EINVAL;
+			goto end;
+		}
+		for (i = 0; i < MAX_DS2_PARAMS-1; i++) {
+			/*get pointer to the param modified array in the cache*/
+			modified_param = ds2_ap_params_obj->dap_params_modified;
+			if (modified_param == NULL) {
+				pr_err("%s: modified param struct invalid\n",
+					__func__);
+				ret = -EINVAL;
+				goto end;
+			}
+			if (!msm_ds2_dap_check_is_param_modified(
+					modified_param, i, commit))
+				continue;
+			ds2_ap_params_obj->dap_params_modified[i] = 0x00010001;
+		}
+	}
+end:
+	kfree(params_value);
+	return ret;
+}
+
+static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data,
+				 int commit)
+{
+	int ret = 0, i, idx;
+	struct ds2_dap_params_s *ds2_ap_params_obj =  NULL;
+	int32_t *modified_param = NULL;
+
+	/* Do not commit params if in hard bypass */
+	if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS &&
+		ds2_dap_params_states.dap_bypass == true) {
+		pr_debug("%s: called in bypass", __func__);
+		ret = -EINVAL;
+		goto end;
+	}
+	for (idx = 0; idx < MAX_DS2_PARAMS; idx++) {
+		if (ds2_dap_params_id[idx] == DOLBY_PARAM_ID_INIT_ENDP)
+			break;
+	}
+	if (idx >= MAX_DS2_PARAMS || idx < 0) {
+		pr_err("%s: index of DS2 Param not found idx %d\n",
+			__func__, idx);
+		ret = -EINVAL;
+		goto end;
+	}
+	pr_debug("%s: found endp - idx %d 0x%x\n", __func__, idx,
+		ds2_dap_params_id[idx]);
+	for (i = 0; i < DS2_DEVICES_ALL; i++) {
+		pr_debug("%s:dev[0x%x,0x%x],i:%d,active:%d,bypass:%d,type:%d\n",
+			__func__, dolby_data->device_id, dev_map[i].device_id,
+			i, dev_map[i].active, ds2_dap_params_states.dap_bypass,
+			ds2_dap_params_states.dap_bypass_type);
+
+		if (((dev_map[i].device_id & ds2_dap_params_states.device) ||
+			((ds2_dap_params_states.dap_bypass_type ==
+			DAP_SOFT_BYPASS) &&
+			(ds2_dap_params_states.dap_bypass == true))) &&
+			(dev_map[i].active == true)) {
+
+			/*get ptr to the cache storing the params for device*/
+			if ((ds2_dap_params_states.dap_bypass_type ==
+				DAP_SOFT_BYPASS) &&
+				(ds2_dap_params_states.dap_bypass == true))
+				ds2_ap_params_obj =
+					&ds2_dap_params[dev_map[0].cache_dev];
+			else
+				ds2_ap_params_obj =
+					&ds2_dap_params[dev_map[i].cache_dev];
+
+			/*get the pointer to the param modified array in cache*/
+			modified_param = ds2_ap_params_obj->dap_params_modified;
+			if (modified_param == NULL) {
+				pr_err("%s: modified_param NULL\n", __func__);
+				ret = -EINVAL;
+				goto end;
+			}
+
+			/*
+			 * Send the endp param if use cache is set
+			 * or if param is modified
+			 */
+			if (!commit || msm_ds2_dap_check_is_param_modified(
+					modified_param, idx, commit)) {
+				msm_ds2_dap_send_end_point(i, idx);
+				commit = 0;
+			}
+			ret = msm_ds2_dap_send_cached_params(i, commit);
+			if (ret < 0) {
+				pr_err("%s: send cached param %d\n",
+					__func__, ret);
+				goto end;
+			}
+		}
+	}
+end:
+	return ret;
+}
+
+static int msm_ds2_dap_handle_commands(u32 cmd, void *arg)
+{
+	int ret  = 0, port_id = 0;
+	int32_t data;
+	struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+
+	if (get_user(data, &dolby_data->data[0])) {
+		pr_debug("%s error getting data\n", __func__);
+		ret = -EFAULT;
+		goto end;
+	}
+
+	pr_debug("%s: param_id %d,be_id %d,device_id 0x%x,length %d,data %d\n",
+		 __func__, dolby_data->param_id, dolby_data->be_id,
+		dolby_data->device_id, dolby_data->length, data);
+
+	switch (dolby_data->param_id) {
+	case DAP_CMD_COMMIT_ALL:
+		msm_ds2_dap_commit_params(dolby_data, 0);
+	break;
+
+	case DAP_CMD_COMMIT_CHANGED:
+		msm_ds2_dap_commit_params(dolby_data, 1);
+	break;
+
+	case DAP_CMD_USE_CACHE_FOR_INIT:
+		ds2_dap_params_states.use_cache = data;
+	break;
+
+	case DAP_CMD_SET_BYPASS:
+		pr_debug("%s: bypass %d bypass type %d, data %d\n", __func__,
+			 ds2_dap_params_states.dap_bypass,
+			 ds2_dap_params_states.dap_bypass_type,
+			 data);
+		/* Do not perform bypass operation if bypass state is same*/
+		if (ds2_dap_params_states.dap_bypass == data)
+			break;
+		ds2_dap_params_states.dap_bypass = data;
+		/* hard bypass */
+		if (ds2_dap_params_states.dap_bypass_type == DAP_HARD_BYPASS)
+			msm_ds2_dap_handle_bypass(dolby_data);
+		/* soft bypass */
+		msm_ds2_dap_commit_params(dolby_data, 0);
+	break;
+
+	case DAP_CMD_SET_BYPASS_TYPE:
+		if (data == true)
+			ds2_dap_params_states.dap_bypass_type =
+				DAP_HARD_BYPASS;
+		else
+			ds2_dap_params_states.dap_bypass_type =
+				DAP_SOFT_BYPASS;
+		pr_debug("%s: bypass type %d", __func__,
+			 ds2_dap_params_states.dap_bypass_type);
+	break;
+
+	case DAP_CMD_SET_ACTIVE_DEVICE:
+		pr_debug("%s: DAP_CMD_SET_ACTIVE_DEVICE length %d\n",
+			__func__, dolby_data->length);
+		/* TODO: need to handle multiple instance*/
+		ds2_dap_params_states.device |= dolby_data->device_id;
+		port_id = msm_ds2_dap_get_port_id(
+						  dolby_data->device_id,
+						  dolby_data->be_id);
+		pr_debug("%s: device id 0x%x all_dev 0x%x port_id %d\n",
+			__func__, dolby_data->device_id,
+			ds2_dap_params_states.device, port_id);
+		msm_ds2_dap_update_dev_map_port_id(dolby_data->device_id,
+					   port_id);
+		if (port_id == DOLBY_INVALID_PORT_ID) {
+			pr_err("%s: invalid port id %d\n", __func__, port_id);
+			ret = -EINVAL;
+			goto end;
+		}
+	break;
+	}
+end:
+	return ret;
+
+}
+
+static int msm_ds2_dap_set_param(u32 cmd, void *arg)
+{
+	int rc = 0, idx, i, j, off, port_id = 0, cdev = 0;
+	int32_t num_device = 0;
+	int32_t data = 0;
+	int32_t dev_arr[DS2_DSP_SUPPORTED_ENDP_DEVICE] = {0};
+	struct dolby_param_data *dolby_data =  (struct dolby_param_data *)arg;
+
+	rc = msm_ds2_dap_update_num_devices(dolby_data, &num_device, dev_arr,
+				   DS2_DSP_SUPPORTED_ENDP_DEVICE);
+	if (num_device == 0 || rc < 0) {
+		pr_err("%s: num devices 0\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+	for (i = 0; i < num_device; i++) {
+		port_id = msm_ds2_dap_get_port_id(dev_arr[i],
+						  dolby_data->be_id);
+		if (port_id != DOLBY_INVALID_PORT_ID)
+			msm_ds2_dap_update_dev_map_port_id(dev_arr[i], port_id);
+
+		cdev = msm_ds2_dap_map_device_to_dolby_cache_devices(
+							  dev_arr[i]);
+		if (cdev < 0 || cdev >= DOLBY_MAX_CACHE) {
+			pr_err("%s: Invalid cache device %d for device 0x%x\n",
+				__func__, cdev, dev_arr[i]);
+			rc = -EINVAL;
+			goto end;
+		}
+		pr_debug("%s:port:%d,be:%d,dev:0x%x,cdev:%d,param:0x%x,len:%d\n"
+			 , __func__, port_id, dolby_data->be_id, dev_arr[i],
+			 cdev, dolby_data->param_id, dolby_data->length);
+		for (idx = 0; idx < MAX_DS2_PARAMS; idx++) {
+			/*paramid from user space*/
+			if (dolby_data->param_id == ds2_dap_params_id[idx])
+				break;
+		}
+		if (idx > MAX_DS2_PARAMS-1) {
+			pr_err("%s: invalid param id 0x%x at idx %d\n",
+				__func__, dolby_data->param_id, idx);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		off = ds2_dap_params_offset[idx];
+		if ((dolby_data->length <= 0) ||
+			(dolby_data->length > TOTAL_LENGTH_DS2_PARAM - off)) {
+			pr_err("%s: invalid length %d at idx %d\n",
+				__func__, dolby_data->length, idx);
+			rc = -EINVAL;
+			goto end;
+		}
+
+		/* cache the parameters */
+		ds2_dap_params[cdev].dap_params_modified[idx] += 1;
+		for (j = 0; j <  dolby_data->length; j++) {
+			if (get_user(data, &dolby_data->data[j])) {
+				pr_debug("%s:error getting data\n", __func__);
+				rc = -EFAULT;
+				goto end;
+			}
+			ds2_dap_params[cdev].params_val[off + j] = data;
+				pr_debug("%s:off %d,val[i/p:o/p]-[%d / %d]\n",
+					 __func__, off, data,
+					 ds2_dap_params[cdev].
+					 params_val[off + j]);
+		}
+	}
+end:
+	return rc;
+}
+
+static int msm_ds2_dap_get_param(u32 cmd, void *arg)
+{
+	int rc = 0, i, port_id = 0, copp_idx = -1;
+	struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+	int32_t *update_params_value = NULL, *params_value = NULL;
+	uint32_t params_length = DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM *
+					sizeof(uint32_t);
+	uint32_t param_payload_len =
+			DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+
+	/* Return error on get param in soft or hard bypass */
+	if (ds2_dap_params_states.dap_bypass == true) {
+		pr_err("%s: called in bypass_type %d bypass %d\n", __func__,
+			ds2_dap_params_states.dap_bypass_type,
+			ds2_dap_params_states.dap_bypass);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	/* Return if invalid length */
+	if ((dolby_data->length >
+	      (DOLBY_MAX_LENGTH_INDIVIDUAL_PARAM - DOLBY_PARAM_PAYLOAD_SIZE)) ||
+	      (dolby_data->length <= 0)) {
+		pr_err("Invalid length %d", dolby_data->length);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	for (i = 0; i < DS2_DEVICES_ALL; i++) {
+		if ((dev_map[i].active) &&
+			(dev_map[i].device_id & dolby_data->device_id)) {
+			port_id = dev_map[i].port_id;
+			copp_idx = dev_map[i].copp_idx;
+			break;
+		}
+	}
+
+	if (port_id == DOLBY_INVALID_PORT_ID) {
+		pr_err("%s: Invalid port\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_err("%s: Invalid copp_idx\n", __func__);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	pr_debug("%s: port_id 0x%x, copp_idx %d, dev_map[i].device_id %x\n",
+		 __func__, port_id, copp_idx, dev_map[i].device_id);
+
+	params_value = kzalloc(params_length + param_payload_len,
+				GFP_KERNEL);
+	if (!params_value) {
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	if (dolby_data->param_id == DOLBY_PARAM_ID_VER) {
+		rc = adm_get_params(port_id, copp_idx,
+				    DOLBY_BUNDLE_MODULE_ID,
+				    DOLBY_PARAM_ID_VER,
+				    params_length + param_payload_len,
+				    (char *)params_value);
+	} else {
+		for (i = 0; i < MAX_DS2_PARAMS; i++)
+			if (ds2_dap_params_id[i] ==
+				dolby_data->param_id)
+				break;
+		if (i > MAX_DS2_PARAMS-1) {
+			pr_err("%s: invalid param id 0x%x at id %d\n", __func__,
+				dolby_data->param_id, i);
+			rc = -EINVAL;
+			goto end;
+		} else {
+			params_length =
+			ds2_dap_params_length[i] * sizeof(uint32_t);
+
+			rc = adm_get_params(port_id, copp_idx,
+					    DOLBY_BUNDLE_MODULE_ID,
+					    ds2_dap_params_id[i],
+					    params_length +
+					    param_payload_len,
+					    (char *)params_value);
+		}
+	}
+	if (rc) {
+		pr_err("%s: get parameters failed rc %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto end;
+	}
+	update_params_value = params_value;
+	if (copy_to_user((void *)dolby_data->data,
+			&update_params_value[DOLBY_PARAM_PAYLOAD_SIZE],
+			(dolby_data->length * sizeof(uint32_t)))) {
+		pr_err("%s: error getting param\n", __func__);
+		rc = -EFAULT;
+		goto end;
+	}
+end:
+	kfree(params_value);
+	return rc;
+}
+
+static int msm_ds2_dap_param_visualizer_control_get(u32 cmd, void *arg)
+{
+	int32_t *visualizer_data = NULL;
+	int  i = 0, ret = 0, port_id = -1, cache_dev = -1, copp_idx = -1;
+	int32_t *update_visualizer_data = NULL;
+	struct dolby_param_data *dolby_data = (struct dolby_param_data *)arg;
+	uint32_t offset, length, params_length;
+	uint32_t param_payload_len =
+		DOLBY_PARAM_PAYLOAD_SIZE * sizeof(uint32_t);
+
+	for (i = 0; i < DS2_DEVICES_ALL; i++) {
+		if ((dev_map[i].active))  {
+			port_id = dev_map[i].port_id;
+			cache_dev = dev_map[i].cache_dev;
+			copp_idx = dev_map[i].copp_idx;
+			break;
+		}
+	}
+
+	if (port_id == DOLBY_INVALID_PORT_ID ||
+		(copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+		ret = 0;
+		dolby_data->length = 0;
+		pr_err("%s: no device active\n", __func__);
+		goto end;
+	}
+
+	length = ds2_dap_params[cache_dev].params_val[DOLBY_PARAM_VCNB_OFFSET];
+
+	if (length > DOLBY_PARAM_VCNB_MAX_LENGTH || length <= 0) {
+		ret = 0;
+		dolby_data->length = 0;
+		pr_err("%s Incorrect VCNB length", __func__);
+	}
+
+	params_length = (2*length + DOLBY_VIS_PARAM_HEADER_SIZE) *
+							 sizeof(uint32_t);
+
+	visualizer_data = kzalloc(params_length, GFP_KERNEL);
+	if (!visualizer_data) {
+		ret = -ENOMEM;
+		dolby_data->length = 0;
+		goto end;
+	}
+	memset(visualizer_data, 0x0, params_length);
+
+	/* Return error on get param in soft or hard bypass */
+	if (ds2_dap_params_states.dap_bypass == true) {
+		pr_debug("%s: visualizer called in bypass, return 0\n",
+			 __func__);
+		ret = 0;
+		dolby_data->length = 0;
+		goto end;
+	}
+
+	offset = 0;
+	params_length = length * sizeof(uint32_t);
+	ret = adm_get_params(port_id, copp_idx,
+			    DOLBY_BUNDLE_MODULE_ID,
+			    DOLBY_PARAM_ID_VCBG,
+			    params_length + param_payload_len,
+			    (((char *)(visualizer_data)) + offset));
+	if (ret) {
+		pr_err("%s: get parameters failed ret %d\n", __func__, ret);
+		ret = -EINVAL;
+		dolby_data->length = 0;
+		goto end;
+	}
+	offset = length * sizeof(uint32_t);
+	ret = adm_get_params(port_id, copp_idx,
+			    DOLBY_BUNDLE_MODULE_ID,
+			    DOLBY_PARAM_ID_VCBE,
+			    params_length + param_payload_len,
+			    (((char *)(visualizer_data)) + offset));
+	if (ret) {
+		pr_err("%s: get parameters failed ret %d\n", __func__, ret);
+		ret = -EINVAL;
+		dolby_data->length = 0;
+		goto end;
+	}
+	update_visualizer_data = visualizer_data;
+	dolby_data->length = 2 * length;
+
+	if (copy_to_user((void *)dolby_data->data,
+			(void *)update_visualizer_data,
+			(dolby_data->length * sizeof(uint32_t)))) {
+		pr_err("%s: copy to user failed for data\n", __func__);
+		dolby_data->length = 0;
+		ret = -EFAULT;
+		goto end;
+	}
+
+end:
+	kfree(visualizer_data);
+	return ret;
+}
+
+int msm_ds2_dap_set_security_control(u32 cmd, void *arg)
+{
+	struct dolby_param_license *dolby_license =
+				 ((struct dolby_param_license *)arg);
+	pr_debug("%s: dmid %d license key %d\n", __func__,
+		dolby_license->dmid, dolby_license->license_key);
+	core_set_dolby_manufacturer_id(dolby_license->dmid);
+	core_set_license(dolby_license->license_key, DOLBY_DS1_LICENSE_ID);
+	return 0;
+}
+
+int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw,  struct file *file,
+				       bool open)
+{
+	int  i = 0, dev_id = 0;
+
+	pr_debug("%s: open %d\n", __func__, open);
+	ds2_dap_params_states.node_opened = open;
+	ds2_dap_params_states.dap_bypass = true;
+	ds2_dap_params_states.dap_bypass_type = 0;
+	ds2_dap_params_states.use_cache = 0;
+	ds2_dap_params_states.device = 0;
+	ds2_dap_params_states.custom_stereo_onoff = 0;
+	for (i = 0; i < DS2_DEVICES_ALL; i++) {
+		if (i == 0)
+			dev_map[i].device_id = 0;
+		else {
+			dev_id = (1 << (i-1));
+			if (all_supported_devices & dev_id)
+				dev_map[i].device_id = dev_id;
+			else
+				continue;
+		}
+		dev_map[i].cache_dev =
+			msm_ds2_dap_map_device_to_dolby_cache_devices(
+				    dev_map[i].device_id);
+		if (dev_map[i].cache_dev < 0 ||
+				dev_map[i].cache_dev >= DOLBY_MAX_CACHE)
+			pr_err("%s: Invalid cache device %d for device 0x%x\n",
+						__func__,
+						dev_map[i].cache_dev,
+						dev_map[i].device_id);
+		dev_map[i].port_id = -1;
+		dev_map[i].active = false;
+		dev_map[i].stream_ref_count = 0;
+		dev_map[i].cal_data = NULL;
+		dev_map[i].copp_idx = -1;
+		pr_debug("%s: device_id 0x%x, cache_dev %d act  %d\n", __func__,
+			 dev_map[i].device_id, dev_map[i].cache_dev,
+			 dev_map[i].active);
+	}
+	return 0;
+
+}
+
+int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file,
+			     u32 cmd, void *arg)
+{
+	int ret = 0;
+
+	pr_debug("%s: cmd: 0x%x\n", __func__, cmd);
+	switch (cmd) {
+	case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM:
+		ret = msm_ds2_dap_set_param(cmd, arg);
+	break;
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM:
+		ret = msm_ds2_dap_get_param(cmd, arg);
+	break;
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND:
+		ret = msm_ds2_dap_handle_commands(cmd, arg);
+	break;
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE:
+		ret = msm_ds2_dap_set_security_control(cmd, arg);
+	break;
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER:
+		ret = msm_ds2_dap_param_visualizer_control_get(cmd, arg);
+	break;
+	default:
+		pr_err("%s: called with invalid control 0x%x\n", __func__, cmd);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file,
+		      u32 cmd, void *arg)
+{
+
+	int ret = 0;
+
+	pr_debug("%s: cmd: 0x%x\n", __func__, cmd);
+	if (!arg) {
+		pr_err("%s: Invalid params event status\n", __func__);
+		ret = -EINVAL;
+		goto end;
+	}
+	switch (cmd) {
+	case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM:
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND: {
+		struct dolby_param_data dolby_data;
+
+		if (copy_from_user((void *)&dolby_data, (void *)arg,
+				sizeof(struct dolby_param_data))) {
+			pr_err("%s: Copy from user failed\n", __func__);
+			ret =  -EFAULT;
+			goto end;
+		}
+		ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+		break;
+	}
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE: {
+		struct dolby_param_license dolby_license;
+
+		if (copy_from_user((void *)&dolby_license, (void *)arg,
+				sizeof(struct dolby_param_license))) {
+			pr_err("%s: Copy from user failed\n", __func__);
+			ret = -EFAULT;
+			goto end;
+		}
+		ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license);
+		break;
+	}
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM:
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER: {
+		struct dolby_param_data dolby_data;
+
+		if (copy_from_user((void *)&dolby_data, (void *)arg,
+				sizeof(struct dolby_param_data))) {
+			pr_err("%s: Copy from user failed\n", __func__);
+			ret =  -EFAULT;
+			goto end;
+		}
+		ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+		if (ret < 0)
+			pr_err("%s: ioctl cmd %d returned err %d\n",
+				__func__, cmd, ret);
+		if (copy_to_user((void *)arg, &dolby_data,
+			sizeof(struct dolby_param_data))) {
+			pr_err("%s: Copy to user failed\n", __func__);
+			ret = -EFAULT;
+			goto end;
+		}
+		break;
+	}
+	default:
+		pr_err("%s: called with invalid control 0x%x\n", __func__, cmd);
+		ret = -EINVAL;
+	}
+end:
+	return ret;
+
+}
+#ifdef CONFIG_COMPAT
+int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw, struct file *file,
+			     u32 cmd, void *arg)
+{
+	int ret = 0;
+
+	pr_debug("%s: cmd: 0x%x\n", __func__, cmd);
+	switch (cmd) {
+	case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32:
+		cmd = SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM;
+		goto handle_set_ioctl;
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32:
+		cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND;
+handle_set_ioctl:
+	{
+		struct dolby_param_data32 dolby_data32;
+		struct dolby_param_data dolby_data;
+
+		memset(&dolby_data32, 0, sizeof(dolby_data32));
+		memset(&dolby_data, 0, sizeof(dolby_data));
+		if (copy_from_user(&dolby_data32, (void *)arg,
+				sizeof(struct dolby_param_data32))) {
+			pr_err("%s: Copy from user failed\n", __func__);
+			ret =  -EFAULT;
+			goto end;
+		}
+		dolby_data.version = dolby_data32.version;
+		dolby_data.device_id = dolby_data32.device_id;
+		dolby_data.be_id = dolby_data32.be_id;
+		dolby_data.param_id = dolby_data32.param_id;
+		dolby_data.length = dolby_data32.length;
+		dolby_data.data = compat_ptr(dolby_data32.data);
+
+		ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+		break;
+	}
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32:
+		cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM;
+		goto handle_get_ioctl;
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32:
+		cmd = SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER;
+handle_get_ioctl:
+	{
+		struct dolby_param_data32 dolby_data32;
+		struct dolby_param_data dolby_data;
+
+		memset(&dolby_data32, 0, sizeof(dolby_data32));
+		memset(&dolby_data, 0, sizeof(dolby_data));
+		if (copy_from_user(&dolby_data32, (void *)arg,
+				sizeof(struct dolby_param_data32))) {
+			pr_err("%s: Copy from user failed\n", __func__);
+			ret =  -EFAULT;
+			goto end;
+		}
+		dolby_data.version = dolby_data32.version;
+		dolby_data.device_id = dolby_data32.device_id;
+		dolby_data.be_id = dolby_data32.be_id;
+		dolby_data.param_id = dolby_data32.param_id;
+		dolby_data.length = dolby_data32.length;
+		dolby_data.data = compat_ptr(dolby_data32.data);
+
+		ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_data);
+		if (ret < 0)
+			pr_err("%s: ioctl cmd %d, returned err %d\n",
+				__func__, cmd, ret);
+		dolby_data32.length = dolby_data.length;
+		if (copy_to_user((void *)arg, &dolby_data32,
+			sizeof(struct dolby_param_data32))) {
+			pr_err("%s: Copy to user failed\n", __func__);
+			ret = -EFAULT;
+			goto end;
+		}
+		break;
+	}
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32: {
+		struct dolby_param_license32 dolby_license32;
+		struct dolby_param_license dolby_license;
+
+		cmd = SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE;
+		if (copy_from_user((void *)&dolby_license32, (void *)arg,
+			sizeof(struct dolby_param_license32))) {
+			pr_err("%s: Copy from user failed\n", __func__);
+			ret = -EFAULT;
+			goto end;
+		}
+		dolby_license.dmid = dolby_license32.dmid;
+		dolby_license.license_key = dolby_license32.license_key;
+		ret = msm_ds2_dap_ioctl_shared(hw, file, cmd, &dolby_license);
+		break;
+	}
+	default:
+		pr_err("%s: called with invalid control 0x%x\n",
+			__func__, cmd);
+		ret = -EINVAL;
+	}
+end:
+	return ret;
+
+}
+#endif
+
+int msm_ds2_dap_init(int port_id, int copp_idx, int channels,
+		     bool is_custom_stereo_on)
+{
+	int ret = 0, idx = -1, i;
+	struct dolby_param_data dolby_data;
+
+	struct audproc_softvolume_params softvol = {
+		.period = DOLBY_SOFT_VOLUME_PERIOD,
+		.step = DOLBY_SOFT_VOLUME_STEP,
+		.rampingcurve = DOLBY_SOFT_VOLUME_CURVE_EXP,
+	};
+
+	pr_debug("%s: port id  %d, copp_idx %d\n", __func__, port_id, copp_idx);
+
+	if (port_id != DOLBY_INVALID_PORT_ID) {
+		for (i = 0; i < DS2_DEVICES_ALL; i++) {
+			if ((dev_map[i].port_id == port_id) &&
+				/* device part of active device */
+				(dev_map[i].device_id &
+				ds2_dap_params_states.device)) {
+				idx = i;
+				/* Give priority to headset in case of
+				 * combo device
+				 */
+				if (dev_map[i].device_id == SPEAKER)
+					continue;
+				else
+					break;
+			}
+		}
+		if (idx < 0) {
+			pr_err("%s: invalid index for port %d\n",
+				__func__, port_id);
+			ret = -EINVAL;
+			goto end;
+		}
+		pr_debug("%s:index %d, dev[0x%x,0x%x]\n", __func__, idx,
+			 dev_map[idx].device_id, ds2_dap_params_states.device);
+		dev_map[idx].active = true;
+		dev_map[idx].copp_idx = copp_idx;
+		dolby_data.param_id = DOLBY_COMMIT_ALL_TO_DSP;
+		dolby_data.length = 0;
+		dolby_data.data = NULL;
+		dolby_data.device_id = dev_map[idx].device_id;
+		pr_debug("%s:  idx  %d, active %d, dev id 0x%x, ref count %d\n",
+			 __func__, idx, dev_map[idx].active,
+			 dev_map[idx].device_id,
+			 dev_map[idx].stream_ref_count);
+		if (dev_map[idx].stream_ref_count == 0) {
+			/*perform next 3 func only if hard bypass enabled*/
+			if (ds2_dap_params_states.dap_bypass_type ==
+				DAP_HARD_BYPASS) {
+				ret = msm_ds2_dap_alloc_and_store_cal_data(idx,
+						       ADM_PATH_PLAYBACK, 0);
+				if (ret < 0) {
+					pr_err("%s: Failed to alloc and store cal data for idx %d, device %d, copp_idx %d",
+					       __func__,
+					       idx, dev_map[idx].device_id,
+					       dev_map[idx].copp_idx);
+					dev_map[idx].active = false;
+					dev_map[idx].copp_idx = -1;
+					goto end;
+				}
+
+				ret = adm_set_softvolume(port_id, copp_idx,
+							 &softvol);
+				if (ret < 0) {
+					pr_err("%s: Soft volume ret error %d\n",
+						__func__, ret);
+					dev_map[idx].active = false;
+					dev_map[idx].copp_idx = -1;
+					goto end;
+				}
+
+				ret = msm_ds2_dap_init_modules_in_topology(
+							idx);
+				if (ret < 0) {
+					pr_err("%s: Failed to init modules in topolofy for idx %d, device %d, copp_idx %d\n",
+					       __func__, idx,
+					       dev_map[idx].device_id,
+					       dev_map[idx].copp_idx);
+					dev_map[idx].active = false;
+					dev_map[idx].copp_idx = -1;
+					goto end;
+				}
+			}
+
+			ret =  msm_ds2_dap_commit_params(&dolby_data, 0);
+			if (ret < 0) {
+				pr_debug("%s: commit params ret %d\n",
+					__func__, ret);
+				ret = 0;
+			}
+		}
+		dev_map[idx].stream_ref_count++;
+		if (is_custom_stereo_on) {
+			ds2_dap_params_states.custom_stereo_onoff =
+				is_custom_stereo_on;
+			set_custom_stereo_onoff(idx,
+						is_custom_stereo_on);
+		}
+	}
+
+end:
+	return ret;
+}
+
+void msm_ds2_dap_deinit(int port_id)
+{
+	/*
+	 * Get the active port corrresponding to the active device
+	 * Check if this is same as incoming port
+	 * Set it to invalid
+	 */
+	int idx = -1, i;
+
+	pr_debug("%s: port_id %d\n", __func__, port_id);
+	if (port_id != DOLBY_INVALID_PORT_ID) {
+		for (i = 0; i < DS2_DEVICES_ALL; i++) {
+			/* Active port */
+			if ((dev_map[i].port_id == port_id) &&
+				/* device part of active device */
+				(dev_map[i].device_id &
+				ds2_dap_params_states.device) &&
+				/*
+				 * Need this check to avoid race condition of
+				 * active device being set and playback
+				 * instance opened
+				 */
+				/* active device*/
+				dev_map[i].active) {
+				idx = i;
+				if (dev_map[i].device_id == SPEAKER)
+					continue;
+				else
+					break;
+			}
+		}
+		if (idx < 0) {
+			pr_err("%s: invalid index for port %d\n",
+				__func__, port_id);
+			return;
+		}
+		pr_debug("%s:index %d, dev [0x%x, 0x%x]\n", __func__, idx,
+			 dev_map[idx].device_id, ds2_dap_params_states.device);
+		dev_map[idx].stream_ref_count--;
+		if (dev_map[idx].stream_ref_count == 0) {
+			/*perform next func only if hard bypass enabled*/
+			if (ds2_dap_params_states.dap_bypass_type ==
+				DAP_HARD_BYPASS) {
+				msm_ds2_dap_free_cal_data(idx);
+			}
+			ds2_dap_params_states.device &= ~dev_map[idx].device_id;
+			dev_map[idx].active = false;
+			dev_map[idx].copp_idx = -1;
+		}
+		pr_debug("%s:idx  %d, active %d, dev id 0x%x ref count %d\n",
+			 __func__, idx, dev_map[idx].active,
+			 dev_map[idx].device_id, dev_map[idx].stream_ref_count);
+	}
+}
+
+int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+					bool is_custom_stereo_enabled)
+{
+	int idx = -1, rc = 0, i;
+
+	pr_debug("%s: port_id %d\n", __func__, port_id);
+	if (port_id != DOLBY_INVALID_PORT_ID) {
+		for (i = 0; i < DS2_DEVICES_ALL; i++) {
+			if ((dev_map[i].port_id == port_id) &&
+				/* device part of active device */
+				(dev_map[i].device_id &
+				ds2_dap_params_states.device)) {
+				idx = i;
+				if (dev_map[i].device_id == SPEAKER)
+					continue;
+				else
+					break;
+			}
+		}
+		if (idx < 0) {
+			pr_err("%s: invalid index for port %d\n",
+				__func__, port_id);
+			return rc;
+		}
+		ds2_dap_params_states.custom_stereo_onoff =
+			is_custom_stereo_enabled;
+		rc = set_custom_stereo_onoff(idx,
+					is_custom_stereo_enabled);
+		if (rc < 0) {
+			pr_err("%s: Custom stereo err %d on port %d\n",
+				__func__, rc, port_id);
+		}
+	}
+	return rc;
+}
+
+#else
+
+static int msm_ds2_dap_alloc_and_store_cal_data(int dev_map_idx, int path,
+					    int perf_mode)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_free_cal_data(int dev_map_idx)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_send_cal_data(int dev_map_idx)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_can_enable_module(int32_t module_id)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_init_modules_in_topology(int dev_map_idx)
+{
+	return 0;
+}
+
+static bool msm_ds2_dap_check_is_param_modified(int32_t *dap_params_modified,
+				    int32_t idx, int32_t commit)
+{
+	return false;
+}
+
+
+static int msm_ds2_dap_map_device_to_dolby_cache_devices(int32_t device_id)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_update_num_devices(struct dolby_param_data *dolby_data,
+				      int32_t *num_device, int32_t *dev_arr,
+				      int32_t array_size)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_commit_params(struct dolby_param_data *dolby_data,
+				 int commit)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_handle_commands(u32 cmd, void *arg)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_set_param(u32 cmd, void *arg)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_get_param(u32 cmd, void *arg)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_send_end_point(int dev_map_idx, int endp_idx)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_send_cached_params(int dev_map_idx,
+					  int commit)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_set_vspe_vdhe(int dev_map_idx,
+				     bool is_custom_stereo_enabled)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_param_visualizer_control_get(
+			u32 cmd, void *arg,
+			struct msm_pcm_routing_bdai_data *bedais)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_set_security_control(u32 cmd, void *arg)
+{
+	return 0
+}
+
+static int msm_ds2_dap_update_dev_map_port_id(int32_t device_id, int port_id)
+{
+	return 0;
+}
+
+static int32_t msm_ds2_dap_get_port_id(
+		int32_t device_id, int32_t be_id)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_handle_bypass(struct dolby_param_data *dolby_data)
+{
+	return 0;
+}
+
+static int msm_ds2_dap_handle_bypass_wait(int port_id, int copp_idx,
+					  int wait_time)
+{
+	return 0;
+}
+
+static int dap_set_custom_stereo_onoff(int dev_map_idx,
+					bool is_custom_stereo_enabled)
+{
+	return 0;
+}
+int qti_set_custom_stereo_on(int port_id, int copp_idx,
+			     bool is_custom_stereo_on)
+{
+	return 0;
+}
+int set_custom_stereo_onoff(int dev_map_idx,
+			    bool is_custom_stereo_enabled)
+{
+	return 0;
+}
+int msm_ds2_dap_ioctl_shared(struct snd_hwdep *hw, struct file *file,
+			     u32 cmd, void *arg)
+{
+	return 0;
+}
+#endif /*CONFIG_DOLBY_DS2*/
diff --git a/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h
new file mode 100644
index 0000000..f2c2069
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-ds2-dap-config.h
@@ -0,0 +1,121 @@
+/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DS2_DAP_CONFIG_H_
+#define _MSM_DS2_DAP_CONFIG_H_
+
+#include <sound/soc.h>
+#include "msm-dolby-common.h"
+#include <sound/hwdep.h>
+#include <uapi/sound/devdep_params.h>
+
+#ifdef CONFIG_COMPAT
+struct dolby_param_data32 {
+	s32 version;
+	s32 device_id;
+	s32 be_id;
+	s32 param_id;
+	s32 length;
+	compat_uptr_t data;
+};
+
+struct dolby_param_license32 {
+	compat_uptr_t dmid;
+	compat_uptr_t license_key;
+};
+
+
+#define SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32\
+		_IOWR('U', 0x10, struct dolby_param_data32)
+#define SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32\
+		_IOR('U', 0x11, struct dolby_param_data32)
+#define SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32\
+		_IOWR('U', 0x13, struct dolby_param_data32)
+#define SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32\
+		_IOWR('U', 0x14, struct dolby_param_license32)
+#define SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32\
+		_IOR('U', 0x15, struct dolby_param_data32)
+#endif
+
+#ifdef CONFIG_DOLBY_DS2
+/* DOLBY DOLBY GUIDS */
+#define DS2_MODULE_ID			0x00010775
+
+#define DS2_DSP_SUPPORTED_ENDP_DEVICE		17
+#define DS2_DEVICES_ALL				32 /* enum val is 4 bytes */
+
+enum {
+
+	DAP_CMD_COMMIT_ALL         = 0,
+	DAP_CMD_COMMIT_CHANGED     = 1,
+	DAP_CMD_USE_CACHE_FOR_INIT = 2,
+	DAP_CMD_SET_BYPASS         = 3,
+	DAP_CMD_SET_ACTIVE_DEVICE  = 4,
+	DAP_CMD_SET_BYPASS_TYPE    = 5,
+};
+
+#define DOLBY_PARAM_INT_ENDP_LENGTH             1
+#define DOLBY_PARAM_INT_ENDP_OFFSET		(DOLBY_PARAM_PSTG_OFFSET + \
+							DOLBY_PARAM_PSTG_LENGTH)
+#define MAX_DS2_PARAMS				48
+#define MAX_DS2_CTRL_PARAMS			4
+#define ALL_DS2_PARAMS				(MAX_DS2_PARAMS + \
+							MAX_DS2_CTRL_PARAMS)
+#define TOTAL_LENGTH_DS2_PARAM (TOTAL_LENGTH_DOLBY_PARAM + 1)
+
+int msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw,  struct file *file,
+				       bool open);
+int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file,
+		      u32 cmd, void *arg);
+int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw,
+			     struct file *file,
+			     u32 cmd, void *arg);
+int msm_ds2_dap_init(int port_id, int copp_idx, int channels,
+		     bool is_custom_stereo_on);
+void msm_ds2_dap_deinit(int port_id);
+int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+					bool is_custom_stereo_enabled);
+/* Dolby DOLBY end */
+#else
+
+static inline void msm_ds2_dap_update_port_parameters(struct snd_hwdep *hw,
+					       struct file *file,
+					       bool open)
+{
+}
+
+static inline int msm_ds2_dap_ioctl(struct snd_hwdep *hw, struct file *file,
+				    u32 cmd, void *arg)
+{
+	return 0;
+}
+
+static inline int msm_ds2_dap_compat_ioctl(struct snd_hwdep *hw,
+					   struct file *file,
+					   u32 cmd, void *arg)
+{
+	return 0;
+}
+static inline int msm_ds2_dap_init(int port_id, int copp_idx, int channels,
+		     bool is_custom_stereo_on)
+{
+	return 0;
+}
+
+static inline void msm_ds2_dap_deinit(int port_id) { }
+
+static inline int msm_ds2_dap_set_custom_stereo_onoff(int port_id, int copp_idx,
+				    bool is_custom_stereo_enabled)
+{
+	return 0;
+}
+#endif
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-dts-eagle.c b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c
new file mode 100644
index 0000000..5ad55dc
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dts-eagle.c
@@ -0,0 +1,1659 @@
+/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/msm_ion.h>
+#include <linux/mm.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/vmalloc.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6audio-v2.h>
+#include <sound/audio_effects.h>
+#include <sound/hwdep.h>
+#include <sound/msm-dts-eagle.h>
+#include <sound/q6core.h>
+
+#include "msm-pcm-routing-v2.h"
+
+#define ION_MEM_SIZE  131072
+#define DEPC_MAX_SIZE 524288
+
+#define MPST				AUDPROC_MODULE_ID_DTS_HPX_POSTMIX
+#define MPRE				AUDPROC_MODULE_ID_DTS_HPX_PREMIX
+
+#define eagle_vol_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__)
+#define eagle_vol_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER_VOLUME: " fmt "\n", ##__VA_ARGS__)
+#define eagle_drv_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__)
+#define eagle_drv_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER: " fmt "\n", ##__VA_ARGS__)
+#define eagle_precache_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__)
+#define eagle_precache_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER_SENDCACHE_PRE: " fmt "\n", ##__VA_ARGS__)
+#define eagle_postcache_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__)
+#define eagle_postcache_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER_SENDCACHE_POST: " fmt "\n", ##__VA_ARGS__)
+#define eagle_ioctl_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__)
+#define eagle_ioctl_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER_IOCTL: " fmt "\n", ##__VA_ARGS__)
+#define eagle_asm_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__)
+#define eagle_asm_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER_ASM: " fmt "\n", ##__VA_ARGS__)
+#define eagle_adm_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__)
+#define eagle_adm_err(fmt, ...) \
+	pr_err("DTS_EAGLE_DRIVER_ADM: " fmt "\n", ##__VA_ARGS__)
+#define eagle_enable_dbg(fmt, ...) \
+	pr_debug("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__)
+#define eagle_enable_err(fmt, ...) \
+	pr_err("DTS_EAGLE_ENABLE: " fmt "\n", ##__VA_ARGS__)
+#define eagle_ioctl_info(fmt, ...) \
+	pr_err("DTS_EAGLE_IOCTL: " fmt "\n", ##__VA_ARGS__)
+
+enum {
+	AUDIO_DEVICE_OUT_EARPIECE = 0,
+	AUDIO_DEVICE_OUT_SPEAKER,
+	AUDIO_DEVICE_OUT_WIRED_HEADSET,
+	AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+	AUDIO_DEVICE_OUT_BLUETOOTH_SCO,
+	AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET,
+	AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT,
+	AUDIO_DEVICE_OUT_BLUETOOTH_A2DP,
+	AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES,
+	AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+	AUDIO_DEVICE_OUT_AUX_DIGITAL,
+	AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET,
+	AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET,
+	AUDIO_DEVICE_OUT_USB_ACCESSORY,
+	AUDIO_DEVICE_OUT_USB_DEVICE,
+	AUDIO_DEVICE_OUT_REMOTE_SUBMIX,
+	AUDIO_DEVICE_OUT_ANC_HEADSET,
+	AUDIO_DEVICE_OUT_ANC_HEADPHONE,
+	AUDIO_DEVICE_OUT_PROXY,
+	AUDIO_DEVICE_OUT_FM,
+	AUDIO_DEVICE_OUT_FM_TX,
+
+	AUDIO_DEVICE_OUT_COUNT
+};
+
+#define AUDIO_DEVICE_COMBO 0x400000 /* bit 23 */
+
+enum { /* cache block */
+	CB_0 = 0,
+	CB_1,
+	CB_2,
+	CB_3,
+	CB_4,
+	CB_5,
+	CB_6,
+	CB_7,
+
+	CB_COUNT
+};
+
+enum { /* cache block description */
+	CBD_DEV_MASK = 0,
+	CBD_OFFSG,
+	CBD_CMD0,
+	CBD_SZ0,
+	CBD_OFFS1,
+	CBD_CMD1,
+	CBD_SZ1,
+	CBD_OFFS2,
+	CBD_CMD2,
+	CBD_SZ2,
+	CBD_OFFS3,
+	CBD_CMD3,
+	CBD_SZ3,
+
+	CBD_COUNT,
+};
+
+static s32 _fx_logN(s32 x)
+{
+	s32 t, y = 0xa65af;
+
+	if (x < 0x00008000) {
+		x <<= 16; y -= 0xb1721; }
+	if (x < 0x00800000) {
+		x <<= 8; y -= 0x58b91; }
+	if (x < 0x08000000) {
+		x <<= 4; y -= 0x2c5c8; }
+	if (x < 0x20000000) {
+		x <<= 2; y -= 0x162e4; }
+	if (x < 0x40000000) {
+		x <<= 1; y -= 0x0b172; }
+	t = x + (x >> 1);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x067cd; }
+	t = x + (x >> 2);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x03920; }
+	t = x + (x >> 3);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x01e27; }
+	t = x + (x >> 4);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x00f85; }
+	t = x + (x >> 5);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x007e1; }
+	t = x + (x >> 6);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x003f8; }
+	t = x + (x >> 7);
+	if ((t & 0x80000000) == 0) {
+		x = t; y -= 0x001fe; }
+	x = 0x80000000 - x;
+	y -= x >> 15;
+	return y;
+}
+
+static inline void *_getd(struct dts_eagle_param_desc *depd)
+{
+	return (void *)(((char *)depd) + sizeof(struct dts_eagle_param_desc));
+}
+
+static int _ref_cnt;
+/* dts eagle parameter cache */
+static char *_depc;
+static u32 _depc_size;
+static s32 _c_bl[CB_COUNT][CBD_COUNT];
+static u32 _device_primary;
+static u32 _device_all;
+/* ION states */
+static struct ion_client *_ion_client;
+static struct ion_handle *_ion_handle;
+static struct param_outband _po;
+static struct audio_client *_ac_NT;
+static struct ion_client *_ion_client_NT;
+static struct ion_handle *_ion_handle_NT;
+static struct param_outband _po_NT;
+
+#define SEC_BLOB_MAX_CNT 10
+#define SEC_BLOB_MAX_SIZE 0x4004 /*extra 4 for size*/
+static char *_sec_blob[SEC_BLOB_MAX_CNT];
+
+/* multi-copp support */
+static int _cidx[AFE_MAX_PORTS] = {-1};
+
+/* volume controls */
+#define VOL_CMD_CNT_MAX 10
+static u32 _vol_cmd_cnt;
+static s32 **_vol_cmds;
+struct vol_cmds_d {
+	s32 d[4];
+};
+static struct vol_cmds_d *_vol_cmds_d;
+static const s32 _log10_10_inv_x20 = 0x0008af84;
+
+/* hpx master control */
+static u32 _is_hpx_enabled;
+
+static void _volume_cmds_free(void)
+{
+	int i;
+
+	for (i = 0; i < _vol_cmd_cnt; i++)
+		kfree(_vol_cmds[i]);
+	_vol_cmd_cnt = 0;
+	kfree(_vol_cmds);
+	kfree(_vol_cmds_d);
+	_vol_cmds = NULL;
+	_vol_cmds_d = NULL;
+}
+
+static s32 _volume_cmds_alloc1(s32 size)
+{
+	_volume_cmds_free();
+	_vol_cmd_cnt = size;
+	_vol_cmds = kzalloc(_vol_cmd_cnt * sizeof(int *), GFP_KERNEL);
+	if (_vol_cmds) {
+		_vol_cmds_d = kzalloc(_vol_cmd_cnt * sizeof(struct vol_cmds_d),
+					GFP_KERNEL);
+	}
+	if (_vol_cmds_d)
+		return 0;
+	_volume_cmds_free();
+	return -ENOMEM;
+}
+
+/* assumes size is equal or less than 0xFFF */
+static s32 _volume_cmds_alloc2(s32 idx, s32 size)
+{
+	kfree(_vol_cmds[idx]);
+	_vol_cmds[idx] = kzalloc(size, GFP_KERNEL);
+	if (_vol_cmds[idx])
+		return 0;
+	_vol_cmds_d[idx].d[0] = 0;
+	return -ENOMEM;
+}
+
+static void _init_cb_descs(void)
+{
+	int i;
+
+	for (i = 0; i < CB_COUNT; i++) {
+		_c_bl[i][CBD_DEV_MASK] = 0;
+		_c_bl[i][CBD_OFFSG] = _c_bl[i][CBD_OFFS1] =
+		_c_bl[i][CBD_OFFS2] = _c_bl[i][CBD_OFFS3] =
+		0xFFFFFFFF;
+		_c_bl[i][CBD_CMD0] = _c_bl[i][CBD_SZ0] =
+		_c_bl[i][CBD_CMD1] = _c_bl[i][CBD_SZ1] =
+		_c_bl[i][CBD_CMD2] = _c_bl[i][CBD_SZ2] =
+		_c_bl[i][CBD_CMD3] = _c_bl[i][CBD_SZ3] = 0;
+	}
+}
+
+static u32 _get_dev_mask_for_pid(int pid)
+{
+	switch (pid) {
+	case SLIMBUS_0_RX:
+		return (1 << AUDIO_DEVICE_OUT_EARPIECE) |
+			(1 << AUDIO_DEVICE_OUT_SPEAKER) |
+			(1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) |
+			(1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) |
+			(1 << AUDIO_DEVICE_OUT_ANC_HEADSET) |
+			(1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE);
+		/* fallthrough */
+	case INT_BT_SCO_RX:
+		return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) |
+			(1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) |
+			(1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT);
+		/* fallthrough */
+	case RT_PROXY_PORT_001_RX:
+		return (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) |
+			(1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) |
+			(1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) |
+			(1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) |
+			(1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) |
+			(1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) |
+			(1 << AUDIO_DEVICE_OUT_USB_DEVICE) |
+			(1 << AUDIO_DEVICE_OUT_PROXY);
+		/* fallthrough */
+	case HDMI_RX:
+		return 1 << AUDIO_DEVICE_OUT_AUX_DIGITAL;
+	case INT_FM_RX:
+		return 1 << AUDIO_DEVICE_OUT_FM;
+	case INT_FM_TX:
+		return 1 << AUDIO_DEVICE_OUT_FM_TX;
+	default:
+		return 0;
+	}
+}
+
+static int _get_pid_from_dev(u32 device)
+{
+	if (device & (1 << AUDIO_DEVICE_OUT_EARPIECE) ||
+	    device & (1 << AUDIO_DEVICE_OUT_SPEAKER) ||
+	    device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADSET) ||
+	    device & (1 << AUDIO_DEVICE_OUT_WIRED_HEADPHONE) ||
+	    device & (1 << AUDIO_DEVICE_OUT_ANC_HEADSET) ||
+	    device & (1 << AUDIO_DEVICE_OUT_ANC_HEADPHONE)) {
+		return SLIMBUS_0_RX;
+	} else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) ||
+		   device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) ||
+		   device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT)) {
+		return INT_BT_SCO_RX;
+	} else if (device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) ||
+		   device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) ||
+		   device & (1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) ||
+		   device & (1 << AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET) ||
+		   device & (1 << AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET) ||
+		   device & (1 << AUDIO_DEVICE_OUT_USB_ACCESSORY) ||
+		   device & (1 << AUDIO_DEVICE_OUT_USB_DEVICE) ||
+		   device & (1 << AUDIO_DEVICE_OUT_PROXY)) {
+		return RT_PROXY_PORT_001_RX;
+	} else if (device & (1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)) {
+		return HDMI_RX;
+	} else if (device & (1 << AUDIO_DEVICE_OUT_FM)) {
+		return INT_FM_RX;
+	} else if (device & (1 << AUDIO_DEVICE_OUT_FM_TX)) {
+		return INT_FM_TX;
+	}
+	return 0;
+}
+
+static s32 _get_cb_for_dev(int device)
+{
+	s32 i;
+
+	if (device & AUDIO_DEVICE_COMBO) {
+		for (i = 0; i < CB_COUNT; i++) {
+			if ((_c_bl[i][CBD_DEV_MASK] & device) == device)
+				return i;
+		}
+	} else {
+		for (i = 0; i < CB_COUNT; i++) {
+			if ((_c_bl[i][CBD_DEV_MASK] & device) &&
+			    !(_c_bl[i][CBD_DEV_MASK] & AUDIO_DEVICE_COMBO))
+				return i;
+		}
+	}
+	eagle_drv_err("%s: device %i not found", __func__, device);
+	return -EINVAL;
+}
+
+static int _is_port_open_and_eagle(int pid)
+{
+	if (msm_routing_check_backend_enabled(pid))
+		return 1;
+	return 1;
+}
+
+static int _isNTDevice(u32 device)
+{
+	if (device &
+		((1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO) |
+		(1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) |
+		(1 << AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) |
+		(1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP) |
+		(1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES) |
+		(1 << AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER) |
+		(1 << AUDIO_DEVICE_OUT_AUX_DIGITAL)))
+		return 1;
+	return 0;
+}
+
+static void _reg_ion_mem(void)
+{
+	int rc;
+
+	rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client, &_ion_handle,
+			    ION_MEM_SIZE, &_po.paddr, &_po.size, &_po.kvaddr);
+	if (rc)
+		eagle_drv_err("%s: msm audio ion alloc failed with %i",
+				__func__, rc);
+}
+
+static void _unreg_ion_mem(void)
+{
+	int rc;
+
+	rc = msm_audio_ion_free(_ion_client, _ion_handle);
+	if (rc)
+		eagle_drv_err("%s: msm audio ion alloc failed with %i",
+				__func__, rc);
+}
+
+static void _reg_ion_mem_NT(void)
+{
+	int rc;
+
+	eagle_drv_dbg("%s: NT ion mem", __func__);
+	rc = msm_audio_ion_alloc("DTS_EAGLE", &_ion_client_NT,
+				 &_ion_handle_NT, ION_MEM_SIZE,
+				 &_po_NT.paddr, &_po_NT.size, &_po_NT.kvaddr);
+	if (rc) {
+		eagle_drv_err("%s: msm audio ion alloc failed",	__func__);
+		return;
+	}
+	rc = q6asm_memory_map(_ac_NT, _po_NT.paddr,
+			      IN, _po_NT.size, 1);
+	if (rc < 0) {
+		eagle_drv_err("%s: memory map failed", __func__);
+		msm_audio_ion_free(_ion_client_NT, _ion_handle_NT);
+		_ion_client_NT = NULL;
+		_ion_handle_NT = NULL;
+	}
+}
+
+static void _unreg_ion_mem_NT(void)
+{
+	int rc;
+
+	rc = q6asm_memory_unmap(_ac_NT,	_po_NT.paddr, IN);
+	if (rc < 0)
+		eagle_drv_err("%s: mem unmap failed", __func__);
+	rc = msm_audio_ion_free(_ion_client_NT, _ion_handle_NT);
+	if (rc < 0)
+		eagle_drv_err("%s: mem free failed", __func__);
+
+	_ion_client_NT = NULL;
+	_ion_handle_NT = NULL;
+}
+
+static struct audio_client *_getNTDeviceAC(void)
+{
+	return _ac_NT;
+}
+
+static void _set_audioclient(struct audio_client *ac)
+{
+	_ac_NT = ac;
+	_reg_ion_mem_NT();
+}
+
+static void _clear_audioclient(void)
+{
+	_unreg_ion_mem_NT();
+	_ac_NT = NULL;
+}
+
+
+static int _sendcache_pre(struct audio_client *ac)
+{
+	uint32_t offset, size;
+	int32_t cidx, cmd, err = 0;
+
+	cidx = _get_cb_for_dev(_device_primary);
+	if (cidx < 0) {
+		eagle_precache_err("%s: no cache for primary device %i found",
+			__func__, _device_primary);
+		return -EINVAL;
+	}
+	offset = _c_bl[cidx][CBD_OFFSG];
+	cmd = _c_bl[cidx][CBD_CMD0];
+	size = _c_bl[cidx][CBD_SZ0];
+	/* check for integer overflow */
+	if (offset > (UINT_MAX - size))
+		err = -EINVAL;
+	if ((_depc_size == 0) || !_depc || (size == 0) ||
+		cmd == 0 || ((offset + size) > _depc_size) || (err != 0)) {
+		eagle_precache_err("%s: primary device %i cache index %i general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i",
+			__func__, _device_primary, cidx, _depc_size, _depc,
+			offset, size, cmd);
+		return -EINVAL;
+	}
+
+	if ((offset < (UINT_MAX - 124)) && ((offset + 124) < _depc_size))
+		eagle_precache_dbg("%s: first 6 integers %i %i %i %i %i %i (30th %i)",
+			__func__, *((int *)&_depc[offset]),
+			*((int *)&_depc[offset+4]),
+			*((int *)&_depc[offset+8]),
+			*((int *)&_depc[offset+12]),
+			*((int *)&_depc[offset+16]),
+			*((int *)&_depc[offset+20]),
+			*((int *)&_depc[offset+120]));
+	eagle_precache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, param = 0x%X, offset = %u, and size = %u",
+		  __func__, cidx, _c_bl[cidx][CBD_DEV_MASK], cmd, offset, size);
+
+	if (q6asm_dts_eagle_set(ac, cmd, size, (void *)&_depc[offset],
+				NULL, MPRE))
+		eagle_precache_err("%s: q6asm_dts_eagle_set failed with id = %d and size = %u",
+			__func__, cmd, size);
+	else
+		eagle_precache_dbg("%s: q6asm_dts_eagle_set succeeded with id = %d and size = %u",
+			 __func__, cmd, size);
+	return 0;
+}
+
+static int _sendcache_post(int port_id, int copp_idx, int topology)
+{
+	int cidx = -1, cmd, mask, index, err = 0;
+	uint32_t offset, size;
+
+	if (port_id == -1) {
+		cidx = _get_cb_for_dev(_device_primary);
+		if (cidx < 0) {
+			eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X",
+				__func__, _device_primary, port_id);
+			return -EINVAL;
+		}
+		goto NT_MODE_GOTO;
+	}
+
+	index = adm_validate_and_get_port_index(port_id);
+	if (index < 0) {
+		eagle_postcache_err("%s: Invalid port idx %d port_id %#x",
+			__func__, index, port_id);
+		return -EINVAL;
+	}
+	eagle_postcache_dbg("%s: valid port idx %d for port_id %#x set to %i",
+		__func__, index, port_id, copp_idx);
+	_cidx[index] = copp_idx;
+
+	mask = _get_dev_mask_for_pid(port_id);
+	if (mask & _device_primary) {
+		cidx = _get_cb_for_dev(_device_primary);
+		if (cidx < 0) {
+			eagle_postcache_err("%s: no cache for primary device %i found. Port id was 0x%X",
+				__func__, _device_primary, port_id);
+			return -EINVAL;
+		}
+	} else if (mask & _device_all) {
+		cidx = _get_cb_for_dev(_device_all);
+		if (cidx < 0) {
+			eagle_postcache_err("%s: no cache for combo device %i found. Port id was 0x%X",
+				__func__, _device_all, port_id);
+			return -EINVAL;
+		}
+	} else {
+		eagle_postcache_err("%s: port id 0x%X not for primary or combo device %i",
+			__func__, port_id, _device_primary);
+		return -EINVAL;
+	}
+
+NT_MODE_GOTO:
+	offset = _c_bl[cidx][CBD_OFFSG] + _c_bl[cidx][CBD_OFFS2];
+	cmd = _c_bl[cidx][CBD_CMD2];
+	size = _c_bl[cidx][CBD_SZ2];
+
+	/* check for integer overflow */
+	if (offset > (UINT_MAX - size))
+		err = -EINVAL;
+	if ((_depc_size == 0) || !_depc || (err != 0) || (size == 0) ||
+		(cmd == 0) || (offset + size) > _depc_size) {
+		eagle_postcache_err("%s: primary device %i cache index %i port_id 0x%X general error - cache size = %u, cache ptr = %pK, offset = %u, size = %u, cmd = %i",
+			__func__, _device_primary, cidx, port_id,
+			_depc_size, _depc, offset, size, cmd);
+		return -EINVAL;
+	}
+
+	if ((offset < (UINT_MAX - 24)) && ((offset + 24) < _depc_size))
+		eagle_postcache_dbg("%s: first 6 integers %i %i %i %i %i %i",
+			__func__, *((int *)&_depc[offset]),
+			*((int *)&_depc[offset+4]),
+			*((int *)&_depc[offset+8]),
+			*((int *)&_depc[offset+12]),
+			*((int *)&_depc[offset+16]),
+			*((int *)&_depc[offset+20]));
+	eagle_postcache_dbg("%s: sending full data block to port, with cache index = %d device mask 0x%X, port_id = 0x%X, param = 0x%X, offset = %u, and size = %u",
+		__func__, cidx, _c_bl[cidx][CBD_DEV_MASK], port_id, cmd,
+		offset, size);
+
+	if (_ac_NT) {
+		eagle_postcache_dbg("%s: NT Route detected", __func__);
+		if (q6asm_dts_eagle_set(_getNTDeviceAC(), cmd, size,
+					(void *)&_depc[offset],
+					&_po_NT, MPST))
+			eagle_postcache_err("%s: q6asm_dts_eagle_set failed with id = 0x%X and size = %u",
+				__func__, cmd, size);
+	} else if (adm_dts_eagle_set(port_id, copp_idx, cmd,
+			      (void *)&_depc[offset], size) < 0)
+		eagle_postcache_err("%s: adm_dts_eagle_set failed with id = 0x%X and size = %u",
+			__func__, cmd, size);
+	else
+		eagle_postcache_dbg("%s: adm_dts_eagle_set succeeded with id = 0x%X and size = %u",
+			 __func__, cmd, size);
+	return 0;
+}
+
+static int _enable_post_get_control(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = _is_hpx_enabled;
+	return 0;
+}
+
+static int _enable_post_put_control(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = 0, be_index = 0, port_id, topology;
+	int flag = ucontrol->value.integer.value[0];
+	struct msm_pcm_routing_bdai_data msm_bedai;
+
+	eagle_drv_dbg("%s: flag %d", __func__, flag);
+
+	_is_hpx_enabled = flag ? true : false;
+	msm_pcm_routing_acquire_lock();
+	/* send cache postmix params when hpx is set On */
+	for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
+		msm_pcm_routing_get_bedai_info(be_index, &msm_bedai);
+		port_id = msm_bedai.port_id;
+		if (!(((port_id == SLIMBUS_0_RX) ||
+		      (port_id == RT_PROXY_PORT_001_RX)) &&
+		      msm_bedai.active))
+			continue;
+		for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+			topology = adm_get_topology_for_port_copp_idx(
+								port_id, idx);
+			if (topology ==
+				ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) {
+				msm_dts_eagle_enable_adm(port_id, idx,
+							 _is_hpx_enabled);
+			}
+		}
+	}
+	msm_pcm_routing_release_lock();
+	return 0;
+}
+
+static const struct snd_kcontrol_new _hpx_enabled_controls[] = {
+	SOC_SINGLE_EXT("Set HPX OnOff", SND_SOC_NOPM, 0, 1, 0,
+	_enable_post_get_control, _enable_post_put_control)
+};
+
+/**
+ * msm_dts_ion_memmap() - helper function to map ION memory
+ * @po_:	Out of band memory structure used as memory.
+ *
+ * Assign already allocated ION memory for mapping it to dsp.
+ *
+ * Return: No return value.
+ */
+void msm_dts_ion_memmap(struct param_outband *po_)
+{
+	po_->size = ION_MEM_SIZE;
+	po_->kvaddr = _po.kvaddr;
+	po_->paddr = _po.paddr;
+}
+
+/**
+ * msm_dts_eagle_enable_asm() - Enable/disable dts module
+ * @ac:	Enable/disable module in ASM session associated with this audio client.
+ * @enable:	Enable/disable the dts module.
+ * @module:	module id.
+ *
+ * Enable/disable specified dts module id in asm.
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_enable_asm(struct audio_client *ac, u32 enable, int module)
+{
+	int ret = 0;
+
+	eagle_enable_dbg("%s: enable = %i on module %i",
+		 __func__, enable, module);
+	_is_hpx_enabled = enable;
+	ret = q6asm_dts_eagle_set(ac, AUDPROC_PARAM_ID_ENABLE,
+				      sizeof(enable), &enable,
+				      NULL, module);
+	if (_is_hpx_enabled) {
+		if (module == MPRE)
+			_sendcache_pre(ac);
+		else if (module == MPST)
+			_sendcache_post(-1, 0, 0);
+	}
+	return ret;
+}
+
+/**
+ * msm_dts_eagle_enable_adm() - Enable/disable dts module in adm
+ * @port_id:	Send enable/disable param to this port id.
+ * @copp_idx:	Send enable/disable param to the relevant copp.
+ * @enable:	Enable/disable the dts module.
+ *
+ * Enable/disable dts module in adm.
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_enable_adm(int port_id, int copp_idx, u32 enable)
+{
+	int ret = 0;
+
+	eagle_enable_dbg("%s: enable = %i", __func__, enable);
+	_is_hpx_enabled = enable;
+	ret = adm_dts_eagle_set(port_id, copp_idx, AUDPROC_PARAM_ID_ENABLE,
+			     (char *)&enable, sizeof(enable));
+	if (_is_hpx_enabled)
+		_sendcache_post(port_id, copp_idx, MPST);
+	return ret;
+}
+
+/**
+ * msm_dts_eagle_add_controls() -  Add mixer control to Enable/Disable DTS HPX
+ * @platform:	Add mixer controls to this platform.
+ *
+ * Add mixer control to Enable/Disable DTS HPX module in ADM.
+ *
+ * Return: No return value.
+ */
+void msm_dts_eagle_add_controls(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, _hpx_enabled_controls,
+				      ARRAY_SIZE(_hpx_enabled_controls));
+}
+
+/**
+ * msm_dts_eagle_set_stream_gain() -  Set stream gain to DTS Premix module
+ * @ac:	Set stream gain to ASM session associated with this audio client.
+ * @lgain:	Left gain value.
+ * @rgain:	Right gain value.
+ *
+ * Set stream gain to DTS Premix module in ASM.
+ *
+ * Return: failure or success.
+ */
+int msm_dts_eagle_set_stream_gain(struct audio_client *ac, int lgain, int rgain)
+{
+	u32 i, val;
+	s32 idx, err = 0;
+
+	eagle_vol_dbg("%s: - entry: vol_cmd_cnt = %u, lgain = %i, rgain = %i",
+		 __func__, _vol_cmd_cnt, lgain, rgain);
+
+	if (_depc_size == 0) {
+		eagle_vol_dbg("%s: driver cache not initialized", __func__);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < _vol_cmd_cnt; i++) {
+		if (_vol_cmds_d[i].d[0] & 0x8000) {
+			idx = (sizeof(struct dts_eagle_param_desc)/sizeof(int))
+				+ (_vol_cmds_d[i].d[0] & 0x3FF);
+			val = _fx_logN(((s32)(lgain+rgain)) << 2);
+			val = ((long long)val * _log10_10_inv_x20) >> 16;
+			_vol_cmds[i][idx] = (s32)clamp((int)(((long long)val *
+						    _vol_cmds_d[i].d[1]) >> 16),
+						    _vol_cmds_d[i].d[2],
+						    _vol_cmds_d[i].d[3]);
+			 eagle_vol_dbg("%s: loop %u cmd desc found %i, idx = %i. volume info: lgain = %i, rgain = %i, volume = %i (scale %i, min %i, max %i)",
+				 __func__, i, _vol_cmds_d[i].d[0], idx, lgain,
+				 rgain, _vol_cmds[i][idx], _vol_cmds_d[i].d[1],
+				 _vol_cmds_d[i].d[2], _vol_cmds_d[i].d[3]);
+		}
+		idx = _get_cb_for_dev(_device_primary);
+		if (idx < 0) {
+			eagle_vol_err("%s: no cache for primary device %i found",
+				__func__, _device_primary);
+			return -EINVAL;
+		}
+		val = _c_bl[idx][CBD_OFFSG] + _vol_cmds[i][2];
+		/* check for integer overflow */
+		if (val > (UINT_MAX - _vol_cmds[i][1]))
+			err = -EINVAL;
+		if ((err != 0) || ((val + _vol_cmds[i][1]) > _depc_size)) {
+			eagle_vol_err("%s: volume size (%u) + offset (%i) out of bounds %i",
+				__func__, val, _vol_cmds[i][1], _depc_size);
+			return -EINVAL;
+		}
+		memcpy((void *)&_depc[val], &_vol_cmds[i][4], _vol_cmds[i][1]);
+		if (q6asm_dts_eagle_set(ac, _vol_cmds[i][0],
+			_vol_cmds[i][1], (void *)&_depc[val], NULL, MPRE))
+			eagle_vol_err("%s: loop %u - volume set failed with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i",
+				__func__, i, _vol_cmds[i][0], _vol_cmds[i][1],
+				_vol_cmds[i][2], _vol_cmds_d[i].d[0],
+				_vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2],
+				_vol_cmds_d[i].d[3], _vol_cmds[i][4]);
+		else
+			eagle_vol_dbg("%s: loop %u - volume set succeeded with id 0x%X, size %i, offset %i, cmd_desc %i, scale %i, min %i, max %i, data(...) %i",
+				 __func__, i, _vol_cmds[i][0], _vol_cmds[i][1],
+				 _vol_cmds[i][2], _vol_cmds_d[i].d[0],
+				 _vol_cmds_d[i].d[1], _vol_cmds_d[i].d[2],
+				 _vol_cmds_d[i].d[3], _vol_cmds[i][4]);
+	}
+	return 0;
+}
+
+/**
+ * msm_dts_eagle_handle_asm() - Set or Get params from ASM
+ * @depd:	DTS Eagle Params structure.
+ * @buf:	Buffer to get queried param value.
+ * @for_pre:	For premix module or postmix module.
+ * @get:	Getting param from DSP or setting param.
+ * @ac:	Set/Get from ASM session associated with this audio client.
+ * @po:	Out of band memory to set or get postmix params.
+ *
+ * Set or Get params from modules in ASM session.
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_handle_asm(struct dts_eagle_param_desc *depd, char *buf,
+			     bool for_pre, bool get, struct audio_client *ac,
+			     struct param_outband *po)
+{
+	struct dts_eagle_param_desc depd_ = {0};
+	s32 ret = 0, isALSA = 0, err = 0, i, mod = for_pre ? MPRE : MPST;
+	u32 offset;
+
+	eagle_asm_dbg("%s: set/get asm", __func__);
+
+	/* special handling for ALSA route, to accommodate 64 bit platforms */
+	if (depd == NULL) {
+		long *arg_ = (long *)buf;
+
+		depd = &depd_;
+		depd->id = (u32)*arg_++;
+		depd->size = (u32)*arg_++;
+		depd->offset = (s32)*arg_++;
+		depd->device = (u32)*arg_++;
+		buf = (char *)arg_;
+		isALSA = 1;
+	}
+
+	if (depd->size & 1) {
+		eagle_asm_err("%s: parameter size %u is not a multiple of 2",
+			__func__, depd->size);
+		return -EINVAL;
+	}
+
+	if (get) {
+		void *buf_, *buf_m = NULL;
+
+		eagle_asm_dbg("%s: get requested", __func__);
+		if (depd->offset == -1) {
+			eagle_asm_dbg("%s: get from dsp requested", __func__);
+			if (depd->size > 0 && depd->size <= DEPC_MAX_SIZE) {
+				buf_ = buf_m = vzalloc(depd->size);
+			} else {
+				eagle_asm_err("%s: get size %u invalid",
+					      __func__, depd->size);
+				return -EINVAL;
+			}
+			if (!buf_m) {
+				eagle_asm_err("%s: out of memory", __func__);
+				return -ENOMEM;
+			} else if (q6asm_dts_eagle_get(ac, depd->id,
+						       depd->size, buf_m,
+						       po, mod) < 0) {
+				eagle_asm_err("%s: asm get failed", __func__);
+				ret = -EFAULT;
+				goto DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT;
+			}
+			eagle_asm_dbg("%s: get result: param id 0x%x value %d size %u",
+				 __func__, depd->id, *(int *)buf_m, depd->size);
+		} else {
+			s32 tgt = _get_cb_for_dev(depd->device);
+
+			if (tgt < 0) {
+				eagle_asm_err("%s: no cache for device %u found",
+					__func__, depd->device);
+				return -EINVAL;
+			}
+			offset = _c_bl[tgt][CBD_OFFSG] + depd->offset;
+			/* check for integer overflow */
+			if (offset > (UINT_MAX - depd->size))
+				err = -EINVAL;
+			if ((err != 0) || (offset + depd->size) > _depc_size) {
+				eagle_asm_err("%s: invalid size %u and/or offset %u",
+					__func__, depd->size, offset);
+				return -EINVAL;
+			}
+			buf_ = (u32 *)&_depc[offset];
+		}
+		if (isALSA) {
+			if (depd->size == 2) {
+				*(long *)buf = (long)*(__u16 *)buf_;
+				eagle_asm_dbg("%s: asm out 16 bit value %li",
+						__func__, *(long *)buf);
+			} else {
+				s32 *pbuf = (s32 *)buf_;
+				long *bufl = (long *)buf;
+
+				for (i = 0; i < (depd->size >> 2); i++) {
+					*bufl++ = (long)*pbuf++;
+					eagle_asm_dbg("%s: asm out value %li",
+							 __func__, *(bufl-1));
+				}
+			}
+		} else {
+			memcpy(buf, buf_, depd->size);
+		}
+DTS_EAGLE_IOCTL_GET_PARAM_PRE_EXIT:
+		vfree(buf_m);
+		return (int)ret;
+	} else {
+		s32 tgt = _get_cb_for_dev(depd->device);
+
+		if (tgt < 0) {
+			eagle_asm_err("%s: no cache for device %u found",
+				__func__, depd->device);
+			return -EINVAL;
+		}
+		offset = _c_bl[tgt][CBD_OFFSG] + depd->offset;
+		/* check for integer overflow */
+		if (offset > (UINT_MAX - depd->size))
+			err = -EINVAL;
+		if ((err != 0) || ((offset + depd->size) > _depc_size)) {
+			eagle_asm_err("%s: invalid size %u and/or offset %u for parameter (cache is size %u)",
+				__func__, depd->size, offset, _depc_size);
+			return -EINVAL;
+		}
+		if (isALSA) {
+			if (depd->size == 2) {
+				*(__u16 *)&_depc[offset] = (__u16)*(long *)buf;
+				eagle_asm_dbg("%s: asm in 16 bit value %li",
+						__func__, *(long *)buf);
+			} else {
+				s32 *pbuf = (s32 *)&_depc[offset];
+				long *bufl = (long *)buf;
+
+				for (i = 0; i < (depd->size >> 2); i++) {
+					*pbuf++ = (s32)*bufl++;
+					eagle_asm_dbg("%s: asm in value %i",
+							__func__, *(pbuf-1));
+				}
+			}
+		} else {
+			memcpy(&_depc[offset], buf, depd->size);
+		}
+		eagle_asm_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i",
+			__func__, depd->id, depd->size, depd->offset,
+			depd->device,
+			tgt, offset, *(int *)&_depc[offset]);
+		if (q6asm_dts_eagle_set(ac, depd->id, depd->size,
+					(void *)&_depc[offset], po, mod))
+			eagle_asm_err("%s: q6asm_dts_eagle_set failed with id = 0x%X, size = %u, offset = %d",
+				__func__, depd->id, depd->size, depd->offset);
+		else
+			eagle_asm_dbg("%s: q6asm_dts_eagle_set succeeded with id = 0x%X, size = %u, offset = %d",
+				 __func__, depd->id, depd->size, depd->offset);
+	}
+
+	return (int)ret;
+}
+
+/**
+ * msm_dts_eagle_handle_adm() - Set or Get params from ADM
+ * @depd:	DTS Eagle Params structure used to set or get.
+ * @buf:	Buffer to get queried param value in NT mode.
+ * @for_pre:	For premix module or postmix module.
+ * @get:	Getting param from DSP or setting param.
+ *
+ * Set or Get params from modules in ADM session.
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_handle_adm(struct dts_eagle_param_desc *depd, char *buf,
+			     bool for_pre, bool get)
+{
+	u32 pid = _get_pid_from_dev(depd->device), cidx;
+	s32 ret = 0;
+
+	eagle_adm_dbg("%s: set/get adm", __func__);
+
+	if (_isNTDevice(depd->device)) {
+		eagle_adm_dbg("%s: NT Route detected", __func__);
+		ret = msm_dts_eagle_handle_asm(depd, buf, for_pre, get,
+					       _getNTDeviceAC(), &_po_NT);
+		if (ret < 0)
+			eagle_adm_err("%s: NT Route set failed with id = 0x%X, size = %u, offset = %i, device = %u",
+				__func__, depd->id, depd->size, depd->offset,
+				depd->device);
+	} else if (get) {
+		cidx = adm_validate_and_get_port_index(pid);
+		eagle_adm_dbg("%s: get from qdsp requested (port id 0x%X)",
+			 __func__, pid);
+		if (adm_dts_eagle_get(pid, _cidx[cidx], depd->id,
+				      buf, depd->size) < 0) {
+			eagle_adm_err("%s: get from qdsp via adm with port id 0x%X failed",
+				 __func__, pid);
+			return -EFAULT;
+		}
+	} else if (_is_port_open_and_eagle(pid)) {
+		cidx = adm_validate_and_get_port_index(pid);
+		eagle_adm_dbg("%s: adm_dts_eagle_set called with id = 0x%X, size = %u, offset = %i, device = %u, port id = %u, copp index = %u",
+				__func__, depd->id, depd->size, depd->offset,
+				depd->device, pid, cidx);
+		ret = adm_dts_eagle_set(pid, _cidx[cidx], depd->id,
+					(void *)buf, depd->size);
+		if (ret < 0)
+			eagle_adm_err("%s: adm_dts_eagle_set failed", __func__);
+		else
+			eagle_adm_dbg("%s: adm_dts_eagle_set succeeded",
+				__func__);
+	} else {
+		ret = -EINVAL;
+		eagle_adm_dbg("%s: port id 0x%X not active or not Eagle",
+			 __func__, pid);
+	}
+	return (int)ret;
+}
+
+/**
+ * msm_dts_eagle_ioctl() - ioctl handler function
+ * @cmd:	cmd to handle.
+ * @arg:	argument to the cmd.
+ *
+ * Handle DTS Eagle ioctl cmds.
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_ioctl(unsigned int cmd, unsigned long arg)
+{
+	s32 ret = 0;
+
+	switch (cmd) {
+	case DTS_EAGLE_IOCTL_GET_CACHE_SIZE: {
+		eagle_ioctl_info("%s: called with control 0x%X (get param cache size)",
+			__func__, cmd);
+		if (copy_to_user((void *)arg, &_depc_size,
+				 sizeof(_depc_size))) {
+			eagle_ioctl_err("%s: error writing size", __func__);
+			return -EFAULT;
+		}
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SET_CACHE_SIZE: {
+		u32 size = 0;
+
+		eagle_ioctl_info("%s: called with control 0x%X (allocate param cache)",
+			__func__, cmd);
+		if (copy_from_user((void *)&size, (void *)arg, sizeof(size))) {
+			eagle_ioctl_err("%s: error copying size (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, &size, sizeof(size));
+			return -EFAULT;
+		} else if (size > DEPC_MAX_SIZE) {
+			eagle_ioctl_err("%s: cache size %u not allowed (min 0, max %u)",
+				__func__, size, DEPC_MAX_SIZE);
+			return -EINVAL;
+		}
+		if (_depc) {
+			eagle_ioctl_dbg("%s: previous param cache of size %u freed",
+				__func__, _depc_size);
+			_depc_size = 0;
+			vfree(_depc);
+			_depc = NULL;
+		}
+		if (size)
+			_depc = vzalloc(size);
+		else
+			eagle_ioctl_dbg("%s: %u bytes requested for param cache, nothing allocated",
+				__func__, size);
+		if (_depc) {
+			eagle_ioctl_dbg("%s: %u bytes allocated for param cache",
+				__func__, size);
+			_depc_size = size;
+		} else {
+			eagle_ioctl_err("%s: error allocating param cache (vzalloc failed on %u bytes)",
+				__func__, size);
+			_depc_size = 0;
+			return -ENOMEM;
+		}
+		break;
+	}
+	case DTS_EAGLE_IOCTL_GET_PARAM: {
+		struct dts_eagle_param_desc depd;
+		s32 for_pre = 0, get_from_core = 0, err = 0;
+		u32 offset;
+		void *buf, *buf_m = NULL;
+
+		eagle_ioctl_info("%s: control 0x%X (get param)",
+			__func__, cmd);
+		if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) {
+			eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, &depd, sizeof(depd));
+			return -EFAULT;
+		}
+		if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) {
+			eagle_ioctl_dbg("%s: using for premix", __func__);
+			for_pre = 1;
+		}
+		if (depd.device & DTS_EAGLE_FLAG_IOCTL_GETFROMCORE) {
+			eagle_ioctl_dbg("%s: 'get from core' requested",
+				__func__);
+			get_from_core = 1;
+			depd.offset = -1;
+		}
+		depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK;
+		if (depd.offset == -1) {
+			if (depd.size > 0 && depd.size <= DEPC_MAX_SIZE) {
+				buf = buf_m = vzalloc(depd.size);
+			} else {
+				eagle_ioctl_err("%s: get size %u invalid",
+						__func__, depd.size);
+				return -EINVAL;
+			}
+			if (!buf_m) {
+				eagle_ioctl_err("%s: out of memory", __func__);
+				return -ENOMEM;
+			}
+			if (get_from_core)
+				ret = core_dts_eagle_get(depd.id, depd.size,
+							 buf);
+			else
+				ret = msm_dts_eagle_handle_adm(&depd, buf,
+								for_pre, true);
+		} else {
+			s32 cb = _get_cb_for_dev(depd.device);
+
+			if (cb < 0) {
+				eagle_ioctl_err("%s: no cache for device %u found",
+					__func__, depd.device);
+				return -EINVAL;
+			}
+			offset = _c_bl[cb][CBD_OFFSG] + depd.offset;
+			/* check for integer overflow */
+			if (offset > (UINT_MAX - depd.size))
+				err = -EINVAL;
+			if ((err != 0) ||
+			    ((offset + depd.size) > _depc_size)) {
+				eagle_ioctl_err("%s: invalid size %u and/or offset %u",
+					__func__, depd.size, offset);
+				return -EINVAL;
+			}
+			buf = (void *)&_depc[offset];
+		}
+		if (ret < 0)
+			eagle_ioctl_err("%s: error %i getting data", __func__,
+				ret);
+		else if (copy_to_user((void *)(((char *)arg)+sizeof(depd)),
+						  buf, depd.size)) {
+			eagle_ioctl_err("%s: error copying get data", __func__);
+			ret = -EFAULT;
+		}
+		vfree(buf_m);
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SET_PARAM: {
+		struct dts_eagle_param_desc depd;
+		s32 just_set_cache = 0, for_pre = 0, err = 0;
+		u32 offset;
+		s32 tgt;
+
+		eagle_ioctl_info("%s: control 0x%X (set param)",
+			__func__, cmd);
+		if (copy_from_user((void *)&depd, (void *)arg, sizeof(depd))) {
+			eagle_ioctl_err("%s: error copying dts_eagle_param_desc (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, &depd, sizeof(depd));
+			return -EFAULT;
+		}
+		if (depd.device & DTS_EAGLE_FLAG_IOCTL_PRE) {
+			eagle_ioctl_dbg("%s: using for premix", __func__);
+			for_pre = 1;
+		}
+		if (depd.device & DTS_EAGLE_FLAG_IOCTL_JUSTSETCACHE) {
+			eagle_ioctl_dbg("%s: 'just set cache' requested",
+				__func__);
+			just_set_cache = 1;
+		}
+		depd.device &= DTS_EAGLE_FLAG_IOCTL_MASK;
+		tgt = _get_cb_for_dev(depd.device);
+		if (tgt < 0) {
+			eagle_ioctl_err("%s: no cache for device %u found",
+				__func__, depd.device);
+			return -EINVAL;
+		}
+		offset = _c_bl[tgt][CBD_OFFSG] + depd.offset;
+		/* check for integer overflow */
+		if (offset > (UINT_MAX - depd.size))
+			err = -EINVAL;
+		if ((err != 0) || ((offset + depd.size) > _depc_size)) {
+			eagle_ioctl_err("%s: invalid size %u and/or offset %u for parameter (target cache block %i with offset %i, global cache is size %u)",
+				__func__, depd.size, offset, tgt,
+				_c_bl[tgt][CBD_OFFSG], _depc_size);
+			return -EINVAL;
+		}
+		if (copy_from_user((void *)&_depc[offset],
+				   (void *)(((char *)arg)+sizeof(depd)),
+					depd.size)) {
+			eagle_ioctl_err("%s: error copying param to cache (src:%pK, tgt:%pK, size:%u)",
+				__func__, ((char *)arg)+sizeof(depd),
+				&_depc[offset], depd.size);
+			return -EFAULT;
+		}
+		eagle_ioctl_dbg("%s: param info: param = 0x%X, size = %u, offset = %i, device = %u, cache block %i, global offset = %u, first bytes as integer = %i",
+			__func__, depd.id, depd.size, depd.offset,
+			depd.device, tgt, offset, *(int *)&_depc[offset]);
+		if (!just_set_cache) {
+			ret = msm_dts_eagle_handle_adm(&depd, &_depc[offset],
+						       for_pre, false);
+		}
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK: {
+		u32 b_[CBD_COUNT+1], *b = &b_[1], cb;
+
+		eagle_ioctl_info("%s: with control 0x%X (set param cache block)",
+			 __func__, cmd);
+		if (copy_from_user((void *)b_, (void *)arg, sizeof(b_))) {
+			eagle_ioctl_err("%s: error copying cache block data (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, b_, sizeof(b_));
+			return -EFAULT;
+		}
+		cb = b_[0];
+		if (cb >= CB_COUNT) {
+			eagle_ioctl_err("%s: cache block %u out of range (max %u)",
+				__func__, cb, CB_COUNT-1);
+			return -EINVAL;
+		}
+		eagle_ioctl_dbg("%s: cache block %i set: devices 0x%X, global offset %i, offsets 1:%u 2:%u 3:%u, cmds/sizes 0:0x%X %u 1:0x%X %u 2:0x%X %u 3:0x%X %u",
+		__func__, cb, _c_bl[cb][CBD_DEV_MASK], _c_bl[cb][CBD_OFFSG],
+		_c_bl[cb][CBD_OFFS1], _c_bl[cb][CBD_OFFS2],
+		_c_bl[cb][CBD_OFFS3], _c_bl[cb][CBD_CMD0], _c_bl[cb][CBD_SZ0],
+		_c_bl[cb][CBD_CMD1], _c_bl[cb][CBD_SZ1], _c_bl[cb][CBD_CMD2],
+		_c_bl[cb][CBD_SZ2], _c_bl[cb][CBD_CMD3], _c_bl[cb][CBD_SZ3]);
+		if ((b[CBD_OFFSG]+b[CBD_OFFS1]+b[CBD_SZ1]) > _depc_size ||
+			(b[CBD_OFFSG]+b[CBD_OFFS2]+b[CBD_SZ2]) > _depc_size ||
+			(b[CBD_OFFSG]+b[CBD_OFFS3]+b[CBD_SZ3]) > _depc_size) {
+			eagle_ioctl_err("%s: cache block bounds out of range",
+					__func__);
+			return -EINVAL;
+		}
+		memcpy(_c_bl[cb], b, sizeof(_c_bl[cb]));
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE: {
+		u32 data[2];
+
+		eagle_ioctl_dbg("%s: with control 0x%X (set active device)",
+			 __func__, cmd);
+		if (copy_from_user((void *)data, (void *)arg, sizeof(data))) {
+			eagle_ioctl_err("%s: error copying active device data (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, data, sizeof(data));
+			return -EFAULT;
+		}
+		if (data[1] != 0) {
+			_device_primary = data[0];
+			eagle_ioctl_dbg("%s: primary device %i", __func__,
+				 data[0]);
+		} else {
+			_device_all = data[0];
+			eagle_ioctl_dbg("%s: all devices 0x%X", __func__,
+				 data[0]);
+		}
+		break;
+	}
+	case DTS_EAGLE_IOCTL_GET_LICENSE: {
+		u32 target = 0, size = 0;
+		s32 size_only;
+
+		eagle_ioctl_dbg("%s: with control 0x%X (get license)",
+			 __func__, cmd);
+		if (copy_from_user((void *)&target, (void *)arg,
+				   sizeof(target))) {
+			eagle_ioctl_err("%s: error reading license index. (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, &target, sizeof(target));
+			return -EFAULT;
+		}
+		size_only = target & (1<<31) ? 1 : 0;
+		target &= 0x7FFFFFFF;
+		if (target >= SEC_BLOB_MAX_CNT) {
+			eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)",
+				   __func__, target, SEC_BLOB_MAX_CNT);
+			return -EINVAL;
+		}
+		if (_sec_blob[target] == NULL) {
+			eagle_ioctl_err("%s: license index %u never initialized",
+				   __func__, target);
+			return -EINVAL;
+		}
+		size = ((u32 *)_sec_blob[target])[0];
+		if ((size == 0) || (size > SEC_BLOB_MAX_SIZE)) {
+			eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)",
+				   __func__, size, target, SEC_BLOB_MAX_SIZE);
+			return -EINVAL;
+		}
+		if (size_only) {
+			eagle_ioctl_dbg("%s: reporting size of license data only",
+					__func__);
+			if (copy_to_user((void *)(((char *)arg)+sizeof(target)),
+				 (void *)&size, sizeof(size))) {
+				eagle_ioctl_err("%s: error copying license size",
+						__func__);
+				return -EFAULT;
+			}
+		} else if (copy_to_user((void *)(((char *)arg)+sizeof(target)),
+			   (void *)&(((s32 *)_sec_blob[target])[1]), size)) {
+			eagle_ioctl_err("%s: error copying license data",
+				__func__);
+			return -EFAULT;
+		} else {
+			eagle_ioctl_info("%s: license file %u bytes long from license index %u returned to user",
+				  __func__, size, target);
+		}
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SET_LICENSE: {
+		u32 target[2] = {0, 0};
+
+		eagle_ioctl_dbg("%s: control 0x%X (set license)", __func__,
+				cmd);
+		if (copy_from_user((void *)target, (void *)arg,
+				   sizeof(target))) {
+			eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, target, sizeof(target));
+			return -EFAULT;
+		}
+		if (target[0] >= SEC_BLOB_MAX_CNT) {
+			eagle_ioctl_err("%s: license index %u out of bounds (max index is %u)",
+				   __func__, target[0], SEC_BLOB_MAX_CNT-1);
+			return -EINVAL;
+		}
+		if (target[1] == 0) {
+			eagle_ioctl_dbg("%s: request to free license index %u",
+				 __func__, target[0]);
+			kfree(_sec_blob[target[0]]);
+			_sec_blob[target[0]] = NULL;
+			break;
+		}
+		if ((target[1] == 0) || (target[1] >= SEC_BLOB_MAX_SIZE)) {
+			eagle_ioctl_err("%s: license size %u for index %u invalid (min size is 1, max size is %u)",
+				__func__, target[1], target[0],
+				SEC_BLOB_MAX_SIZE);
+			return -EINVAL;
+		}
+		if (_sec_blob[target[0]] != NULL) {
+			if (((u32 *)_sec_blob[target[0]])[1] != target[1]) {
+				eagle_ioctl_dbg("%s: request new size for already allocated license index %u",
+					 __func__, target[0]);
+				kfree(_sec_blob[target[0]]);
+				_sec_blob[target[0]] = NULL;
+			}
+		}
+		eagle_ioctl_dbg("%s: allocating %u bytes for license index %u",
+				__func__, target[1], target[0]);
+		_sec_blob[target[0]] = kzalloc(target[1] + 4, GFP_KERNEL);
+		if (!_sec_blob[target[0]]) {
+			eagle_ioctl_err("%s: error allocating license index %u (kzalloc failed on %u bytes)",
+					__func__, target[0], target[1]);
+			return -ENOMEM;
+		}
+		((u32 *)_sec_blob[target[0]])[0] = target[1];
+		if (copy_from_user(
+				(void *)&(((u32 *)_sec_blob[target[0]])[1]),
+				(void *)(((char *)arg)+sizeof(target)),
+				target[1])) {
+			eagle_ioctl_err("%s: error copying license to index %u, size %u (src:%pK, tgt:%pK, size:%u)",
+					__func__, target[0], target[1],
+					((char *)arg)+sizeof(target),
+					&(((u32 *)_sec_blob[target[0]])[1]),
+					target[1]);
+			return -EFAULT;
+		}
+		eagle_ioctl_info("%s: license file %u bytes long copied to index license index %u",
+				  __func__, target[1], target[0]);
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SEND_LICENSE: {
+		u32 target = 0;
+
+		eagle_ioctl_dbg("%s: control 0x%X (send license)", __func__,
+				cmd);
+		if (copy_from_user((void *)&target, (void *)arg,
+				   sizeof(target))) {
+			eagle_ioctl_err("%s: error reading license index (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, &target, sizeof(target));
+			return -EFAULT;
+		}
+		if (target >= SEC_BLOB_MAX_CNT) {
+			eagle_ioctl_err("%s: license index %u out of bounds (max index is %i)",
+					__func__, target, SEC_BLOB_MAX_CNT-1);
+			return -EINVAL;
+		}
+		if (!_sec_blob[target] ||
+		    ((u32 *)_sec_blob[target])[0] == 0) {
+			eagle_ioctl_err("%s: license index %u is invalid",
+				__func__, target);
+			return -EINVAL;
+		}
+		if (core_dts_eagle_set(((s32 *)_sec_blob[target])[0],
+				(char *)&((s32 *)_sec_blob[target])[1]) < 0)
+			eagle_ioctl_err("%s: core_dts_eagle_set failed with id = %u",
+				__func__, target);
+		else
+			eagle_ioctl_info("%s: core_dts_eagle_set succeeded with id = %u",
+				 __func__, target);
+		break;
+	}
+	case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS: {
+		s32 spec = 0;
+
+		eagle_ioctl_info("%s: control 0x%X (set volume commands)",
+				__func__, cmd);
+		if (copy_from_user((void *)&spec, (void *)arg,
+					sizeof(spec))) {
+			eagle_ioctl_err("%s: error reading volume command specifier (src:%pK, tgt:%pK, size:%zu)",
+				__func__, (void *)arg, &spec, sizeof(spec));
+			return -EFAULT;
+		}
+		if (spec & 0x80000000) {
+			u32 idx = (spec & 0x0000F000) >> 12;
+			s32 size = spec & 0x00000FFF;
+
+			eagle_ioctl_dbg("%s: setting volume command %i size: %i",
+				__func__, idx, size);
+			if (idx >= _vol_cmd_cnt) {
+				eagle_ioctl_err("%s: volume command index %u out of bounds (only %u allocated)",
+					__func__, idx, _vol_cmd_cnt);
+				return -EINVAL;
+			}
+			if (_volume_cmds_alloc2(idx, size) < 0) {
+				eagle_ioctl_err("%s: error allocating memory for volume controls",
+						__func__);
+				return -ENOMEM;
+			}
+			if (copy_from_user((void *)&_vol_cmds_d[idx],
+					(void *)(((char *)arg) + sizeof(int)),
+					sizeof(struct vol_cmds_d))) {
+				eagle_ioctl_err("%s: error reading volume command descriptor (src:%pK, tgt:%pK, size:%zu)",
+					__func__, ((char *)arg) + sizeof(int),
+					&_vol_cmds_d[idx],
+					sizeof(struct vol_cmds_d));
+				return -EFAULT;
+			}
+			eagle_ioctl_dbg("%s: setting volume command %i spec (size %zu): %i %i %i %i",
+				  __func__, idx, sizeof(struct vol_cmds_d),
+				  _vol_cmds_d[idx].d[0], _vol_cmds_d[idx].d[1],
+				  _vol_cmds_d[idx].d[2], _vol_cmds_d[idx].d[3]);
+			if (copy_from_user((void *)_vol_cmds[idx],
+					(void *)(((char *)arg) + (sizeof(int) +
+					sizeof(struct vol_cmds_d))), size)) {
+				eagle_ioctl_err("%s: error reading volume command string (src:%pK, tgt:%pK, size:%i)",
+					__func__, ((char *)arg) + (sizeof(int) +
+					sizeof(struct vol_cmds_d)),
+					_vol_cmds[idx], size);
+				return -EFAULT;
+			}
+		} else {
+			eagle_ioctl_dbg("%s: setting volume command size",
+					__func__);
+			if (spec < 0 || spec > VOL_CMD_CNT_MAX) {
+				eagle_ioctl_err("%s: volume command count %i out of bounds (min 0, max %i)",
+					__func__, spec, VOL_CMD_CNT_MAX);
+				return -EINVAL;
+			} else if (spec == 0) {
+				eagle_ioctl_dbg("%s: request to free volume commands",
+						__func__);
+				_volume_cmds_free();
+				break;
+			}
+			eagle_ioctl_dbg("%s: setting volume command size requested = %i",
+				  __func__, spec);
+			if (_volume_cmds_alloc1(spec) < 0) {
+				eagle_ioctl_err("%s: error allocating memory for volume controls",
+						__func__);
+				return -ENOMEM;
+			}
+		}
+		break;
+	}
+	default: {
+		eagle_ioctl_err("%s: control 0x%X (invalid control)",
+			 __func__, cmd);
+		ret = -EINVAL;
+	}
+	}
+	return (int)ret;
+}
+
+/**
+ * msm_dts_eagle_compat_ioctl() - To handle 32bit to 64bit ioctl compatibility
+ * @cmd:	cmd to handle.
+ * @arg:	argument to the cmd.
+ *
+ * Handle DTS Eagle ioctl cmds from 32bit userspace.
+ *
+ * Return: Return failure if any.
+ */
+#ifdef CONFIG_COMPAT
+int msm_dts_eagle_compat_ioctl(unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+	case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32:
+		cmd = DTS_EAGLE_IOCTL_GET_CACHE_SIZE;
+		break;
+	case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32:
+		cmd = DTS_EAGLE_IOCTL_SET_CACHE_SIZE;
+		break;
+	case DTS_EAGLE_IOCTL_GET_PARAM32:
+		cmd = DTS_EAGLE_IOCTL_GET_PARAM;
+		break;
+	case DTS_EAGLE_IOCTL_SET_PARAM32:
+		cmd = DTS_EAGLE_IOCTL_SET_PARAM;
+		break;
+	case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32:
+		cmd = DTS_EAGLE_IOCTL_SET_CACHE_BLOCK;
+		break;
+	case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32:
+		cmd = DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE;
+		break;
+	case DTS_EAGLE_IOCTL_GET_LICENSE32:
+		cmd = DTS_EAGLE_IOCTL_GET_LICENSE;
+		break;
+	case DTS_EAGLE_IOCTL_SET_LICENSE32:
+		cmd = DTS_EAGLE_IOCTL_SET_LICENSE;
+		break;
+	case DTS_EAGLE_IOCTL_SEND_LICENSE32:
+		cmd = DTS_EAGLE_IOCTL_SEND_LICENSE;
+		break;
+	case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32:
+		cmd = DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS;
+		break;
+	default:
+		break;
+	}
+	return msm_dts_eagle_ioctl(cmd, arg);
+}
+#endif
+/**
+ * msm_dts_eagle_init_pre() - Initialize DTS premix module
+ * @ac:	Initialize premix module in the ASM session.
+ *
+ * Initialize DTS premix module on provided ASM session
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_init_pre(struct audio_client *ac)
+{
+	return msm_dts_eagle_enable_asm(ac, _is_hpx_enabled,
+				 AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
+}
+
+/**
+ * msm_dts_eagle_deinit_pre() - Deinitialize DTS premix module
+ * @ac:	Deinitialize premix module in the ASM session.
+ *
+ * Deinitialize DTS premix module on provided ASM session
+ *
+ * Return: Currently does nothing so 0.
+ */
+int msm_dts_eagle_deinit_pre(struct audio_client *ac)
+{
+	return 0;
+}
+
+/**
+ * msm_dts_eagle_init_post() - Initialize DTS postmix module
+ * @port_id:	Port id for the ADM session.
+ * @copp_idx:	Copp idx for the ADM session.
+ *
+ * Initialize DTS postmix module on ADM session
+ *
+ * Return: Return failure if any.
+ */
+int msm_dts_eagle_init_post(int port_id, int copp_idx)
+{
+	return msm_dts_eagle_enable_adm(port_id, copp_idx, _is_hpx_enabled);
+}
+
+/**
+ * msm_dts_eagle_deinit_post() - Deinitialize DTS postmix module
+ * @port_id:	Port id for the ADM session.
+ * @topology:	Topology in use.
+ *
+ * Deinitialize DTS postmix module on ADM session
+ *
+ * Return: Currently does nothing so 0.
+ */
+int msm_dts_eagle_deinit_post(int port_id, int topology)
+{
+	return 0;
+}
+
+/**
+ * msm_dts_eagle_init_master_module() - Initialize both DTS modules
+ * @ac:	Initialize modules in the ASM session.
+ *
+ * Initialize DTS modules on ASM session
+ *
+ * Return: Success.
+ */
+int msm_dts_eagle_init_master_module(struct audio_client *ac)
+{
+	_set_audioclient(ac);
+	msm_dts_eagle_enable_asm(ac, _is_hpx_enabled,
+				 AUDPROC_MODULE_ID_DTS_HPX_PREMIX);
+	msm_dts_eagle_enable_asm(ac, _is_hpx_enabled,
+				 AUDPROC_MODULE_ID_DTS_HPX_POSTMIX);
+	return 0;
+}
+
+/**
+ * msm_dts_eagle_deinit_master_module() - Deinitialize both DTS modules
+ * @ac:	Deinitialize modules in the ASM session.
+ *
+ * Deinitialize DTS modules on ASM session
+ *
+ * Return: Success.
+ */
+int msm_dts_eagle_deinit_master_module(struct audio_client *ac)
+{
+	msm_dts_eagle_deinit_pre(ac);
+	msm_dts_eagle_deinit_post(-1, 0);
+	_clear_audioclient();
+	return 0;
+}
+
+/**
+ * msm_dts_eagle_is_hpx_on() - Check if HPX effects are On
+ *
+ * Check if HPX effects are On
+ *
+ * Return: On/Off.
+ */
+int msm_dts_eagle_is_hpx_on(void)
+{
+	return _is_hpx_enabled;
+}
+
+/**
+ * msm_dts_eagle_pcm_new() - Create hwdep node
+ * @runtime:	snd_soc_pcm_runtime structure.
+ *
+ * Create hwdep node
+ *
+ * Return: Success.
+ */
+int msm_dts_eagle_pcm_new(struct snd_soc_pcm_runtime *runtime)
+{
+	if (!_ref_cnt++) {
+		_init_cb_descs();
+		_reg_ion_mem();
+	}
+	return 0;
+}
+
+/**
+ * msm_dts_eagle_pcm_free() - remove hwdep node
+ * @runtime:	snd_soc_pcm_runtime structure.
+ *
+ * Remove hwdep node
+ *
+ * Return: void.
+ */
+void msm_dts_eagle_pcm_free(struct snd_pcm *pcm)
+{
+	if (!--_ref_cnt)
+		_unreg_ion_mem();
+	vfree(_depc);
+}
+
+MODULE_DESCRIPTION("DTS EAGLE platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c
new file mode 100644
index 0000000..5c6f1df
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.c
@@ -0,0 +1,358 @@
+/* Copyright (c) 2012-2014, 2016-2017, The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/atomic.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/asound.h>
+#include <sound/msm-dts-eagle.h>
+#include "msm-dts-srs-tm-config.h"
+#include "msm-pcm-routing-v2.h"
+
+static int srs_port_id[AFE_MAX_PORTS] = {-1};
+static int srs_copp_idx[AFE_MAX_PORTS] = {-1};
+static union srs_trumedia_params_u msm_srs_trumedia_params;
+static struct ion_client *ion_client;
+static struct ion_handle *ion_handle;
+static struct param_outband po;
+static atomic_t ref_cnt;
+#define ION_MEM_SIZE	(8 * 1024)
+
+static int set_port_id(int port_id, int copp_idx)
+{
+	int index = adm_validate_and_get_port_index(port_id);
+
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+			port_id);
+		return -EINVAL;
+	}
+	srs_port_id[index] = port_id;
+	srs_copp_idx[index] = copp_idx;
+	return 0;
+}
+
+static void msm_dts_srs_tm_send_params(__s32 port_id, __u32 techs)
+{
+	__s32 index = adm_validate_and_get_port_index(port_id);
+
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id 0x%x\n",
+			__func__, index, port_id);
+		return;
+	}
+	if ((srs_copp_idx[index] < 0) ||
+	    (srs_copp_idx[index] >= MAX_COPPS_PER_PORT)) {
+		pr_debug("%s: send params called before copp open. so, caching\n",
+			 __func__);
+		return;
+	}
+	pr_debug("SRS %s: called, port_id = %d, techs flags = %u\n",
+		__func__, port_id, techs);
+	/* force all if techs is set to 1 */
+	if (techs == 1)
+		techs = 0xFFFFFFFF;
+
+	if (techs & (1 << SRS_ID_WOWHD))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_WOWHD,
+			(void *)&msm_srs_trumedia_params.srs_params.wowhd);
+	if (techs & (1 << SRS_ID_CSHP))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_CSHP,
+			(void *)&msm_srs_trumedia_params.srs_params.cshp);
+	if (techs & (1 << SRS_ID_HPF))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HPF,
+			(void *)&msm_srs_trumedia_params.srs_params.hpf);
+	if (techs & (1 << SRS_ID_AEQ))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_AEQ,
+			(void *)&msm_srs_trumedia_params.srs_params.aeq);
+	if (techs & (1 << SRS_ID_HL))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_HL,
+			(void *)&msm_srs_trumedia_params.srs_params.hl);
+	if (techs & (1 << SRS_ID_GEQ))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GEQ,
+			(void *)&msm_srs_trumedia_params.srs_params.geq);
+	if (techs & (1 << SRS_ID_GLOBAL))
+		srs_trumedia_open(port_id, srs_copp_idx[index], SRS_ID_GLOBAL,
+			(void *)&msm_srs_trumedia_params.srs_params.global);
+}
+
+
+static int msm_dts_srs_trumedia_control_get(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static int msm_dts_srs_trumedia_control_set_(int port_id,
+					    struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+
+	__u16 offset, value, max = sizeof(msm_srs_trumedia_params) >> 1;
+
+	if (SRS_CMD_UPLOAD ==
+		(ucontrol->value.integer.value[0] & SRS_CMD_UPLOAD)) {
+		__u32 techs = ucontrol->value.integer.value[0] & 0xFF;
+		__s32 index = adm_validate_and_get_port_index(port_id);
+
+		if (index < 0) {
+			pr_err("%s: Invalid port idx %d port_id 0x%x\n",
+					__func__, index, port_id);
+				return -EINVAL;
+			}
+		pr_debug("SRS %s: send params request, flag = %u\n",
+					__func__, techs);
+		if (srs_port_id[index] >= 0 && techs)
+			msm_dts_srs_tm_send_params(port_id, techs);
+		return 0;
+	}
+	offset = (__u16)((ucontrol->value.integer.value[0] &
+			SRS_PARAM_OFFSET_MASK) >> 16);
+	value = (__u16)(ucontrol->value.integer.value[0] &
+			SRS_PARAM_VALUE_MASK);
+	if (offset < max) {
+		msm_srs_trumedia_params.raw_params[offset] = value;
+		pr_debug("SRS %s: index set... (max %d, requested %d, value 0x%X)\n",
+			 __func__, max, offset, value);
+	} else {
+		pr_err("SRS %s: index out of bounds! (max %d, requested %d)\n",
+		       __func__, max, offset);
+	}
+	return 0;
+}
+
+static int msm_dts_srs_trumedia_control_set(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_value *ucontrol)
+{
+	int ret, port_id;
+
+	pr_debug("SRS control normal called\n");
+	msm_pcm_routing_acquire_lock();
+	port_id = SLIMBUS_0_RX;
+	ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+	msm_pcm_routing_release_lock();
+	return ret;
+}
+
+static int msm_dts_srs_trumedia_control_i2s_set(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	int ret, port_id;
+
+	pr_debug("SRS control I2S called\n");
+	msm_pcm_routing_acquire_lock();
+	port_id = PRIMARY_I2S_RX;
+	ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+	msm_pcm_routing_release_lock();
+	return ret;
+}
+
+static int msm_dts_srs_trumedia_control_mi2s_set(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	int ret, port_id;
+
+	pr_debug("SRS control MI2S called\n");
+	msm_pcm_routing_acquire_lock();
+	port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+	ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+	msm_pcm_routing_release_lock();
+	return ret;
+}
+
+static int msm_dts_srs_trumedia_control_hdmi_set(struct snd_kcontrol *kcontrol,
+					   struct snd_ctl_elem_value *ucontrol)
+{
+	int ret, port_id;
+
+	pr_debug("SRS control HDMI called\n");
+	msm_pcm_routing_acquire_lock();
+	port_id = HDMI_RX;
+	ret = msm_dts_srs_trumedia_control_set_(port_id, kcontrol, ucontrol);
+	msm_pcm_routing_release_lock();
+	return ret;
+}
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls[] = {
+	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "SRS TruMedia",
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+			SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = snd_soc_info_volsw,
+	.get = msm_dts_srs_trumedia_control_get,
+	.put = msm_dts_srs_trumedia_control_set,
+	.private_value = ((unsigned long)&(struct soc_mixer_control)
+	{.reg = SND_SOC_NOPM,
+	.rreg = SND_SOC_NOPM,
+	.shift = 0,
+	.rshift = 0,
+	.max = 0xFFFFFFFF,
+	.platform_max = 0xFFFFFFFF,
+	.invert = 0
+	})
+	}
+};
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls_hdmi[] = {
+	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "SRS TruMedia HDMI",
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+			SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = snd_soc_info_volsw,
+	.get = msm_dts_srs_trumedia_control_get,
+	.put = msm_dts_srs_trumedia_control_hdmi_set,
+	.private_value = ((unsigned long)&(struct soc_mixer_control)
+	{.reg = SND_SOC_NOPM,
+	.rreg = SND_SOC_NOPM,
+	.shift = 0,
+	.rshift = 0,
+	.max = 0xFFFFFFFF,
+	.platform_max = 0xFFFFFFFF,
+	.invert = 0
+	})
+	}
+};
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls_i2s[] = {
+	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+	.name = "SRS TruMedia I2S",
+	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+		SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info = snd_soc_info_volsw,
+	.get = msm_dts_srs_trumedia_control_get,
+	.put = msm_dts_srs_trumedia_control_i2s_set,
+	.private_value = ((unsigned long)&(struct soc_mixer_control)
+	{.reg = SND_SOC_NOPM,
+	.rreg = SND_SOC_NOPM,
+	.shift = 0,
+	.rshift = 0,
+	.max = 0xFFFFFFFF,
+	.platform_max = 0xFFFFFFFF,
+	.invert = 0
+	})
+	}
+};
+
+static const struct snd_kcontrol_new lpa_srs_trumedia_controls_mi2s[] = {
+	{
+		.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name = "SRS TruMedia MI2S",
+		.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+			SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.info = snd_soc_info_volsw,
+		.get = msm_dts_srs_trumedia_control_get,
+		.put = msm_dts_srs_trumedia_control_mi2s_set,
+		.private_value = ((unsigned long)&(struct soc_mixer_control)
+		{
+			.reg = SND_SOC_NOPM,
+			.rreg = SND_SOC_NOPM,
+			.shift = 0,
+			.rshift = 0,
+			.max = 0xFFFFFFFF,
+			.platform_max = 0xFFFFFFFF,
+			.invert = 0
+		})
+	}
+};
+
+void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform,
+				lpa_srs_trumedia_controls,
+			ARRAY_SIZE(lpa_srs_trumedia_controls));
+
+	snd_soc_add_platform_controls(platform,
+				lpa_srs_trumedia_controls_hdmi,
+			ARRAY_SIZE(lpa_srs_trumedia_controls_hdmi));
+
+	snd_soc_add_platform_controls(platform,
+				lpa_srs_trumedia_controls_i2s,
+			ARRAY_SIZE(lpa_srs_trumedia_controls_i2s));
+	snd_soc_add_platform_controls(platform,
+				lpa_srs_trumedia_controls_mi2s,
+			ARRAY_SIZE(lpa_srs_trumedia_controls_mi2s));
+}
+
+static int reg_ion_mem(void)
+{
+	int rc;
+
+	rc = msm_audio_ion_alloc("SRS_TRUMEDIA", &ion_client, &ion_handle,
+				 ION_MEM_SIZE, &po.paddr, (size_t *)&po.size,
+				 &po.kvaddr);
+	if (rc != 0)
+		pr_err("%s: failed to allocate memory.\n", __func__);
+		pr_debug("%s: exited ion_client = %pK, ion_handle = %pK, phys_addr = %lu, length = %d, vaddr = %pK, rc = 0x%x\n",
+			__func__, ion_client, ion_handle, (long)po.paddr,
+			(unsigned int)po.size, po.kvaddr, rc);
+	return rc;
+}
+
+void msm_dts_srs_tm_ion_memmap(struct param_outband *po_)
+{
+	if (po.kvaddr == NULL) {
+		pr_debug("%s: callingreg_ion_mem()\n", __func__);
+		reg_ion_mem();
+	}
+	po_->size = ION_MEM_SIZE;
+	po_->kvaddr = po.kvaddr;
+	po_->paddr = po.paddr;
+}
+
+static void unreg_ion_mem(void)
+{
+	msm_audio_ion_free(ion_client, ion_handle);
+	po.kvaddr = NULL;
+	po.paddr = 0;
+	po.size = 0;
+}
+
+void msm_dts_srs_tm_deinit(int port_id)
+{
+	set_port_id(port_id, -1);
+	atomic_dec(&ref_cnt);
+	if (po.kvaddr != NULL) {
+		if (!atomic_read(&ref_cnt)) {
+			pr_debug("%s: calling unreg_ion_mem()\n", __func__);
+			unreg_ion_mem();
+		}
+	}
+}
+
+void msm_dts_srs_tm_init(int port_id, int copp_idx)
+{
+	int cur_ref_cnt = 0;
+
+	if (set_port_id(port_id, copp_idx) < 0) {
+		pr_err("%s: Invalid port_id: %d\n", __func__, port_id);
+		return;
+	}
+
+	cur_ref_cnt = atomic_read(&ref_cnt);
+	atomic_inc(&ref_cnt);
+	if (!cur_ref_cnt && po.kvaddr == NULL) {
+		pr_debug("%s: calling reg_ion_mem()\n", __func__);
+		if (reg_ion_mem() != 0) {
+			atomic_dec(&ref_cnt);
+			po.kvaddr = NULL;
+			return;
+		}
+	}
+	msm_dts_srs_tm_send_params(port_id, 1);
+}
diff --git a/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h
new file mode 100644
index 0000000..1dd7aed
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-dts-srs-tm-config.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2012-2014, 2017 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_DTS_SRS_TM_CONFIG_H_
+#define _MSM_DTS_SRS_TM_CONFIG_H_
+
+#include <sound/soc.h>
+
+struct param_outband;
+
+#ifdef CONFIG_DTS_SRS_TM
+
+union srs_trumedia_params_u {
+	struct srs_trumedia_params srs_params;
+	__u16 raw_params[1];
+};
+
+void msm_dts_srs_tm_ion_memmap(struct param_outband *po_);
+void msm_dts_srs_tm_init(int port_id, int copp_idx);
+void msm_dts_srs_tm_deinit(int port_id);
+void msm_dts_srs_tm_add_controls(struct snd_soc_platform *platform);
+#else
+static inline void msm_dts_srs_tm_ion_memmap(struct param_outband *po_) { }
+static inline void msm_dts_srs_tm_init(int port_id, int copp_idx) { }
+static inline void msm_dts_srs_tm_deinit(int port_id) { }
+static inline void msm_dts_srs_tm_add_controls(
+					struct snd_soc_platform *platform) { }
+
+#endif
+
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-lsm-client.c b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
new file mode 100644
index 0000000..d19c346
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-lsm-client.c
@@ -0,0 +1,1989 @@
+/*
+ * Copyright (c) 2013-2015, 2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/of.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/freezer.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/timer.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6lsm.h>
+#include <sound/lsm_params.h>
+#include <sound/pcm_params.h>
+#include "msm-pcm-routing-v2.h"
+
+#define CAPTURE_MIN_NUM_PERIODS     2
+#define CAPTURE_MAX_NUM_PERIODS     8
+#define CAPTURE_MAX_PERIOD_SIZE     4096
+#define CAPTURE_MIN_PERIOD_SIZE     320
+#define LISTEN_MAX_STATUS_PAYLOAD_SIZE 256
+
+#define LAB_BUFFER_ALLOC 1
+#define LAB_BUFFER_DEALLOC 0
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =                SNDRV_PCM_RATE_16000,
+	.rate_min =             16000,
+	.rate_max =             16000,
+	.channels_min =         1,
+	.channels_max =         1,
+	.buffer_bytes_max =     CAPTURE_MAX_NUM_PERIODS *
+				CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min =	CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max =     CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min =          CAPTURE_MIN_NUM_PERIODS,
+	.periods_max =          CAPTURE_MAX_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	16000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+struct lsm_priv {
+	struct snd_pcm_substream *substream;
+	struct lsm_client *lsm_client;
+	struct snd_lsm_event_status *event_status;
+	spinlock_t event_lock;
+	wait_queue_head_t event_wait;
+	unsigned long event_avail;
+	atomic_t event_wait_stop;
+	atomic_t buf_count;
+	atomic_t read_abort;
+	wait_queue_head_t period_wait;
+	int appl_cnt;
+	int dma_write;
+};
+
+static int msm_lsm_queue_lab_buffer(struct lsm_priv *prtd, int i)
+{
+	int rc = 0;
+	struct lsm_cmd_read cmd_read;
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!prtd || !prtd->lsm_client) {
+		pr_err("%s: Invalid params prtd %pK lsm client %pK\n",
+			__func__, prtd, ((!prtd) ? NULL : prtd->lsm_client));
+		return -EINVAL;
+	}
+	if (!prtd->substream || !prtd->substream->private_data) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!prtd->substream) ? "substream" : "private_data");
+		return -EINVAL;
+	}
+	rtd = prtd->substream->private_data;
+
+	if (!prtd->lsm_client->lab_buffer ||
+		i >= prtd->lsm_client->hw_params.period_count) {
+		dev_err(rtd->dev,
+			"%s: Lab buffer not setup %pK incorrect index %d period count %d\n",
+			__func__, prtd->lsm_client->lab_buffer, i,
+			prtd->lsm_client->hw_params.period_count);
+		return -EINVAL;
+	}
+	cmd_read.buf_addr_lsw =
+		lower_32_bits(prtd->lsm_client->lab_buffer[i].phys);
+	cmd_read.buf_addr_msw =
+		msm_audio_populate_upper_32_bits(
+				prtd->lsm_client->lab_buffer[i].phys);
+	cmd_read.buf_size = prtd->lsm_client->lab_buffer[i].size;
+	cmd_read.mem_map_handle =
+		prtd->lsm_client->lab_buffer[i].mem_map_handle;
+	rc = q6lsm_read(prtd->lsm_client, &cmd_read);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: error in queuing the lab buffer rc %d\n",
+			__func__, rc);
+	return rc;
+}
+
+static int lsm_lab_buffer_sanity(struct lsm_priv *prtd,
+		struct lsm_cmd_read_done *read_done, int *index)
+{
+	int i = 0, rc = -EINVAL;
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!prtd || !read_done || !index) {
+		pr_err("%s: Invalid params prtd %pK read_done %pK index %pK\n",
+			__func__, prtd, read_done, index);
+		return -EINVAL;
+	}
+
+	if (!prtd->substream || !prtd->substream->private_data) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!prtd->substream) ? "substream" : "private_data");
+		return -EINVAL;
+	}
+	rtd = prtd->substream->private_data;
+
+	if (!prtd->lsm_client->lab_enable || !prtd->lsm_client->lab_buffer) {
+		dev_err(rtd->dev,
+			"%s: Lab not enabled %d invalid lab buffer %pK\n",
+			__func__, prtd->lsm_client->lab_enable,
+			prtd->lsm_client->lab_buffer);
+		return -EINVAL;
+	}
+	for (i = 0; i < prtd->lsm_client->hw_params.period_count; i++) {
+		if ((lower_32_bits(prtd->lsm_client->lab_buffer[i].phys) ==
+			read_done->buf_addr_lsw) &&
+			(msm_audio_populate_upper_32_bits
+				(prtd->lsm_client->lab_buffer[i].phys) ==
+			read_done->buf_addr_msw) &&
+			(prtd->lsm_client->lab_buffer[i].mem_map_handle ==
+			read_done->mem_map_handle)) {
+			dev_dbg(rtd->dev,
+				"%s: Buffer found %pK memmap handle %d\n",
+				__func__, &prtd->lsm_client->lab_buffer[i].phys,
+			prtd->lsm_client->lab_buffer[i].mem_map_handle);
+			if (read_done->total_size >
+				prtd->lsm_client->lab_buffer[i].size) {
+				dev_err(rtd->dev,
+					"%s: Size mismatch call back size %d actual size %zd\n",
+					__func__, read_done->total_size,
+				prtd->lsm_client->lab_buffer[i].size);
+				rc = -EINVAL;
+				break;
+			} else {
+				*index = i;
+				rc = 0;
+				break;
+			}
+		}
+	}
+	return rc;
+}
+
+static void lsm_event_handler(uint32_t opcode, uint32_t token,
+			      void *payload, void *priv)
+{
+	unsigned long flags;
+	struct lsm_priv *prtd = priv;
+	struct snd_pcm_substream *substream = prtd->substream;
+	struct snd_soc_pcm_runtime *rtd;
+	struct snd_lsm_event_status *temp;
+	uint16_t status = 0;
+	uint16_t payload_size = 0;
+	uint16_t index = 0;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!substream) ? "substream" : "private_data");
+		return;
+	}
+	rtd = substream->private_data;
+
+	switch (opcode) {
+	case LSM_DATA_EVENT_READ_DONE: {
+		int rc;
+		struct lsm_cmd_read_done *read_done = payload;
+		int buf_index = 0;
+
+		if (prtd->lsm_client->session != token ||
+		    !read_done) {
+			dev_err(rtd->dev,
+				"%s: EVENT_READ_DONE invalid callback, session %d callback %d payload %pK",
+				__func__, prtd->lsm_client->session,
+				token, read_done);
+			return;
+		}
+		if (atomic_read(&prtd->read_abort)) {
+			dev_dbg(rtd->dev,
+				"%s: read abort set skip data\n", __func__);
+			return;
+		}
+		if (!lsm_lab_buffer_sanity(prtd, read_done, &buf_index)) {
+			dev_dbg(rtd->dev,
+				"%s: process read done index %d\n",
+				__func__, buf_index);
+			if (buf_index >=
+				prtd->lsm_client->hw_params.period_count) {
+				dev_err(rtd->dev,
+					"%s: Invalid index %d buf_index max cnt %d\n",
+					__func__, buf_index,
+				prtd->lsm_client->hw_params.period_count);
+				return;
+			}
+			prtd->dma_write += read_done->total_size;
+			atomic_inc(&prtd->buf_count);
+			snd_pcm_period_elapsed(substream);
+			wake_up(&prtd->period_wait);
+			/* queue the next period buffer */
+			buf_index = (buf_index + 1) %
+			prtd->lsm_client->hw_params.period_count;
+			rc = msm_lsm_queue_lab_buffer(prtd, buf_index);
+			if (rc)
+				dev_err(rtd->dev,
+					"%s: error in queuing the lab buffer rc %d\n",
+					__func__, rc);
+		} else
+			dev_err(rtd->dev, "%s: Invalid lab buffer returned by dsp\n",
+				__func__);
+		break;
+	}
+
+	case LSM_SESSION_EVENT_DETECTION_STATUS:
+		status = (uint16_t)((uint8_t *)payload)[0];
+		payload_size = (uint16_t)((uint8_t *)payload)[2];
+		index = 4;
+		dev_dbg(rtd->dev,
+			"%s: event detect status = %d payload size = %d\n",
+			__func__, status, payload_size);
+	break;
+
+	case LSM_SESSION_EVENT_DETECTION_STATUS_V2:
+		status = (uint16_t)((uint8_t *)payload)[0];
+		payload_size = (uint16_t)((uint8_t *)payload)[1];
+		index = 2;
+		dev_dbg(rtd->dev,
+			"%s: event detect status = %d payload size = %d\n",
+			__func__, status, payload_size);
+		break;
+	default:
+		break;
+	}
+
+	if (opcode == LSM_SESSION_EVENT_DETECTION_STATUS ||
+		opcode == LSM_SESSION_EVENT_DETECTION_STATUS_V2) {
+		spin_lock_irqsave(&prtd->event_lock, flags);
+		temp = krealloc(prtd->event_status,
+				sizeof(struct snd_lsm_event_status) +
+				payload_size, GFP_ATOMIC);
+		if (!temp) {
+			dev_err(rtd->dev, "%s: no memory for event status\n",
+				__func__);
+			return;
+		}
+
+		prtd->event_status = temp;
+		prtd->event_status->status = status;
+		prtd->event_status->payload_size = payload_size;
+		if (likely(prtd->event_status)) {
+			memcpy(prtd->event_status->payload,
+			       &((uint8_t *)payload)[index],
+			       payload_size);
+			prtd->event_avail = 1;
+			spin_unlock_irqrestore(&prtd->event_lock, flags);
+			wake_up(&prtd->event_wait);
+		} else {
+			spin_unlock_irqrestore(&prtd->event_lock, flags);
+			dev_err(rtd->dev,
+				"%s: Couldn't allocate %d bytes of memory\n",
+				__func__, payload_size);
+		}
+		if (substream->timer_running)
+			snd_timer_interrupt(substream->timer, 1);
+	}
+}
+
+static int msm_lsm_lab_buffer_alloc(struct lsm_priv *lsm, int alloc)
+{
+	int ret = 0;
+	struct snd_dma_buffer *dma_buf = NULL;
+
+	if (!lsm) {
+		pr_err("%s: Invalid param lsm %pK\n", __func__, lsm);
+		return -EINVAL;
+	}
+	if (alloc) {
+		if (!lsm->substream) {
+			pr_err("%s: substream is NULL\n", __func__);
+			return -EINVAL;
+		}
+		ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc);
+		if (ret) {
+			pr_err("%s: alloc lab buffer failed ret %d\n",
+				__func__, ret);
+			goto exit;
+		}
+		dma_buf = &lsm->substream->dma_buffer;
+		dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+		dma_buf->dev.dev = lsm->substream->pcm->card->dev;
+		dma_buf->private_data = NULL;
+		dma_buf->area = lsm->lsm_client->lab_buffer[0].data;
+		dma_buf->addr = lsm->lsm_client->lab_buffer[0].phys;
+		dma_buf->bytes = lsm->lsm_client->hw_params.buf_sz *
+		lsm->lsm_client->hw_params.period_count;
+		snd_pcm_set_runtime_buffer(lsm->substream, dma_buf);
+	} else {
+		ret = q6lsm_lab_buffer_alloc(lsm->lsm_client, alloc);
+		if (ret)
+			pr_err("%s: free lab buffer failed ret %d\n",
+				__func__, ret);
+		kfree(lsm->lsm_client->lab_buffer);
+		lsm->lsm_client->lab_buffer = NULL;
+	}
+exit:
+	return ret;
+}
+
+static int msm_lsm_get_conf_levels(struct lsm_client *client,
+				   u8 *conf_levels_ptr)
+{
+	int rc = 0;
+
+	if (client->num_confidence_levels == 0) {
+		pr_debug("%s: no confidence levels provided\n",
+			__func__);
+		client->confidence_levels = NULL;
+		goto done;
+	}
+
+	client->confidence_levels =
+		kzalloc((sizeof(uint8_t) * client->num_confidence_levels),
+			 GFP_KERNEL);
+	if (!client->confidence_levels) {
+		pr_err("%s: No memory for confidence\n"
+			"levels num of level from user = %d\n",
+			__func__, client->num_confidence_levels);
+			rc = -ENOMEM;
+			goto done;
+	}
+
+	if (copy_from_user(client->confidence_levels,
+			   conf_levels_ptr,
+			   client->num_confidence_levels)) {
+		pr_err("%s: copy from user failed, size = %d\n",
+		       __func__, client->num_confidence_levels);
+		rc = -EFAULT;
+		goto copy_err;
+	}
+
+	return rc;
+
+copy_err:
+	kfree(client->confidence_levels);
+	client->confidence_levels = NULL;
+done:
+	return rc;
+
+}
+
+static int msm_lsm_set_epd(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int rc = 0;
+	struct snd_lsm_ep_det_thres epd_th;
+
+	if (p_info->param_size != sizeof(epd_th)) {
+		dev_err(rtd->dev,
+			"%s: Invalid param_size %d\n",
+			__func__, p_info->param_size);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (copy_from_user(&epd_th, p_info->param_data,
+			   p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto done;
+	}
+
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+				 &epd_th, LSM_ENDPOINT_DETECT_THRESHOLD);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: Failed to set epd param, err = %d\n",
+			__func__, rc);
+done:
+	return rc;
+}
+
+static int msm_lsm_set_mode(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_lsm_detect_mode mode;
+	int rc = 0;
+
+	if (p_info->param_size != sizeof(mode)) {
+		dev_err(rtd->dev,
+			"%s: Invalid param_size %d\n",
+			__func__, p_info->param_size);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (copy_from_user(&mode, p_info->param_data,
+			   sizeof(mode))) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed, size = %zd\n",
+			__func__, sizeof(mode));
+		rc = -EFAULT;
+		goto done;
+	}
+
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+				 &mode, LSM_OPERATION_MODE);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: Failed to set det_mode param, err = %d\n",
+			__func__, rc);
+done:
+	return rc;
+}
+
+static int msm_lsm_set_gain(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_lsm_gain gain;
+	int rc = 0;
+
+	if (p_info->param_size != sizeof(gain)) {
+		dev_err(rtd->dev,
+			"%s: Invalid param_size %d\n",
+			__func__, p_info->param_size);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (copy_from_user(&gain, p_info->param_data,
+			   sizeof(gain))) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed, size = %zd\n",
+			__func__, sizeof(gain));
+		rc = -EFAULT;
+		goto done;
+	}
+
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+				 &gain, LSM_GAIN);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: Failed to set det_mode param, err = %d\n",
+			__func__, rc);
+done:
+	return rc;
+}
+
+static int msm_lsm_set_conf(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int rc = 0;
+
+	if (p_info->param_size > MAX_NUM_CONFIDENCE) {
+		dev_err(rtd->dev,
+			"%s: invalid confidence levels %d\n",
+			__func__, p_info->param_size);
+		return -EINVAL;
+	}
+
+	prtd->lsm_client->num_confidence_levels =
+			p_info->param_size;
+	rc = msm_lsm_get_conf_levels(prtd->lsm_client,
+				     p_info->param_data);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: get_conf_levels failed, err = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+				 prtd->lsm_client->confidence_levels,
+				 LSM_MIN_CONFIDENCE_LEVELS);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: Failed to set min_conf_levels, err = %d\n",
+			__func__, rc);
+
+	return rc;
+}
+
+static int msm_lsm_reg_model(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int rc = 0;
+	u8 *snd_model_ptr;
+	size_t offset;
+
+	rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client,
+				       p_info->param_size,
+				       true);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: snd_model buf alloc failed, size = %d\n",
+			__func__, p_info->param_size);
+		return rc;
+	}
+
+	q6lsm_sm_set_param_data(prtd->lsm_client, p_info, &offset);
+
+	/*
+	 * For set_param, advance the sound model data with the
+	 * number of bytes required by param_data.
+	 */
+	snd_model_ptr = ((u8 *) prtd->lsm_client->sound_model.data) + offset;
+
+	if (copy_from_user(snd_model_ptr,
+			   p_info->param_data, p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user for snd_model failed, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto err_copy;
+	}
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info, NULL,
+				 LSM_REG_SND_MODEL);
+	if (rc) {
+		dev_err(rtd->dev,
+			"%s: Failed to set sound_model, err = %d\n",
+			__func__, rc);
+		goto err_copy;
+	}
+	return rc;
+
+err_copy:
+	q6lsm_snd_model_buf_free(prtd->lsm_client);
+	return rc;
+}
+
+static int msm_lsm_dereg_model(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	int rc = 0;
+
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+				 NULL, LSM_DEREG_SND_MODEL);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: Failed to set det_mode param, err = %d\n",
+			__func__, rc);
+
+	q6lsm_snd_model_buf_free(prtd->lsm_client);
+
+	return rc;
+}
+
+static int msm_lsm_set_custom(struct snd_pcm_substream *substream,
+		struct lsm_params_info *p_info)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	u8 *data;
+	int rc = 0;
+
+	data = kzalloc(p_info->param_size, GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	if (copy_from_user(data, p_info->param_data,
+			   p_info->param_size)) {
+		dev_err(rtd->dev,
+			"%s: copy_from_user failed for custom params, size = %d\n",
+			__func__, p_info->param_size);
+		rc = -EFAULT;
+		goto err_ret;
+	}
+
+	rc = q6lsm_set_one_param(prtd->lsm_client, p_info,
+				 data, LSM_CUSTOM_PARAMS);
+	if (rc)
+		dev_err(rtd->dev,
+			"%s: Failed to set custom param, err = %d\n",
+			__func__, rc);
+
+err_ret:
+	kfree(data);
+	return rc;
+}
+
+static int msm_lsm_process_params(struct snd_pcm_substream *substream,
+		struct snd_lsm_module_params *p_data,
+		void *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct lsm_params_info *p_info;
+	int i;
+	int rc = 0;
+
+	p_info = (struct lsm_params_info *) params;
+
+	for (i = 0; i < p_data->num_params; i++) {
+		dev_dbg(rtd->dev,
+			"%s: param (%d), module_id = 0x%x, param_id = 0x%x, param_size = 0x%x, param_type = 0x%x\n",
+			__func__, i, p_info->module_id,
+			p_info->param_id, p_info->param_size,
+			p_info->param_type);
+
+		switch (p_info->param_type) {
+		case LSM_ENDPOINT_DETECT_THRESHOLD:
+			rc = msm_lsm_set_epd(substream, p_info);
+			break;
+		case LSM_OPERATION_MODE:
+			rc = msm_lsm_set_mode(substream, p_info);
+			break;
+		case LSM_GAIN:
+			rc = msm_lsm_set_gain(substream, p_info);
+			break;
+		case LSM_MIN_CONFIDENCE_LEVELS:
+			rc = msm_lsm_set_conf(substream, p_info);
+			break;
+		case LSM_REG_SND_MODEL:
+			rc = msm_lsm_reg_model(substream, p_info);
+			break;
+		case LSM_DEREG_SND_MODEL:
+			rc = msm_lsm_dereg_model(substream, p_info);
+			break;
+		case LSM_CUSTOM_PARAMS:
+			rc = msm_lsm_set_custom(substream, p_info);
+			break;
+		default:
+			dev_err(rtd->dev,
+				"%s: Invalid param_type %d\n",
+				__func__, p_info->param_type);
+			rc = -EINVAL;
+			break;
+		}
+		if (rc) {
+			pr_err("%s: set_param fail for param_type %d\n",
+				__func__, p_info->param_type);
+			return rc;
+		}
+
+		p_info++;
+	}
+
+	return rc;
+}
+
+static int msm_lsm_ioctl_shared(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	struct snd_soc_pcm_runtime *rtd;
+	unsigned long flags;
+	int ret;
+	struct snd_lsm_sound_model_v2 snd_model_v2;
+	struct snd_lsm_session_data session_data;
+	int rc = 0;
+	int xchg = 0;
+	u32 size = 0;
+	struct snd_pcm_runtime *runtime;
+	struct lsm_priv *prtd;
+	struct snd_lsm_event_status *user = arg;
+	struct snd_lsm_detection_params det_params;
+	uint8_t *confidence_level = NULL;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!substream) ? "substream" : "private_data");
+		return -EINVAL;
+	}
+
+	runtime = substream->runtime;
+	prtd = runtime->private_data;
+	rtd = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_LSM_SET_SESSION_DATA:
+		dev_dbg(rtd->dev, "%s: set session data\n", __func__);
+		memcpy(&session_data, arg,
+		       sizeof(struct snd_lsm_session_data));
+		if (session_data.app_id != LSM_VOICE_WAKEUP_APP_ID_V2) {
+			dev_err(rtd->dev,
+				"%s:Invalid App id %d for Listen client\n",
+			       __func__, session_data.app_id);
+			rc = -EINVAL;
+			break;
+		}
+
+		prtd->lsm_client->app_id = session_data.app_id;
+		ret = q6lsm_open(prtd->lsm_client,
+				 prtd->lsm_client->app_id);
+		if (ret < 0) {
+			dev_err(rtd->dev,
+				"%s: lsm open failed, %d\n",
+				__func__, ret);
+			return ret;
+		}
+		prtd->lsm_client->opened = true;
+		dev_dbg(rtd->dev, "%s: Session_ID = %d, APP ID = %d\n",
+			__func__,
+			prtd->lsm_client->session,
+			prtd->lsm_client->app_id);
+		break;
+	case SNDRV_LSM_REG_SND_MODEL_V2:
+		dev_dbg(rtd->dev, "%s: Registering sound model V2\n",
+			__func__);
+		memcpy(&snd_model_v2, arg,
+		       sizeof(struct snd_lsm_sound_model_v2));
+		if (snd_model_v2.num_confidence_levels >
+		    MAX_NUM_CONFIDENCE) {
+			dev_err(rtd->dev,
+				"%s: Invalid conf_levels = %d, maximum allowed = %d\n",
+				__func__, snd_model_v2.num_confidence_levels,
+				MAX_NUM_CONFIDENCE);
+			rc = -EINVAL;
+			break;
+		}
+		rc = q6lsm_snd_model_buf_alloc(prtd->lsm_client,
+					       snd_model_v2.data_size, false);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: q6lsm buffer alloc failed V2, size %d\n",
+			       __func__, snd_model_v2.data_size);
+			break;
+		}
+		if (copy_from_user(prtd->lsm_client->sound_model.data,
+			   snd_model_v2.data, snd_model_v2.data_size)) {
+			dev_err(rtd->dev,
+				"%s: copy from user data failed\n"
+			       "data %pK size %d\n", __func__,
+			       snd_model_v2.data, snd_model_v2.data_size);
+			q6lsm_snd_model_buf_free(prtd->lsm_client);
+			rc = -EFAULT;
+			break;
+		}
+
+		dev_dbg(rtd->dev, "SND Model Magic no byte[0] %x,\n"
+			 "byte[1] %x, byte[2] %x byte[3] %x\n",
+			 snd_model_v2.data[0], snd_model_v2.data[1],
+			 snd_model_v2.data[2], snd_model_v2.data[3]);
+		prtd->lsm_client->num_confidence_levels =
+			snd_model_v2.num_confidence_levels;
+
+		rc = msm_lsm_get_conf_levels(prtd->lsm_client,
+				snd_model_v2.confidence_level);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: get_conf_levels failed, err = %d\n",
+				__func__, rc);
+			break;
+		}
+
+		rc = q6lsm_register_sound_model(prtd->lsm_client,
+					snd_model_v2.detection_mode,
+					snd_model_v2.detect_failure);
+		if (rc < 0) {
+			dev_err(rtd->dev,
+				"%s: Register snd Model v2 failed =%d\n",
+			       __func__, rc);
+			kfree(confidence_level);
+			q6lsm_snd_model_buf_free(prtd->lsm_client);
+		}
+
+		kfree(prtd->lsm_client->confidence_levels);
+		prtd->lsm_client->confidence_levels = NULL;
+		break;
+
+	case SNDRV_LSM_SET_PARAMS:
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s Invalid argument\n",
+				__func__, "SNDRV_LSM_SET_PARAMS");
+			return -EINVAL;
+		}
+
+		dev_dbg(rtd->dev, "%s: set_params\n", __func__);
+		memcpy(&det_params, arg,
+			sizeof(det_params));
+		if (det_params.num_confidence_levels >
+		    MAX_NUM_CONFIDENCE) {
+			rc = -EINVAL;
+			break;
+		}
+
+		prtd->lsm_client->num_confidence_levels =
+			det_params.num_confidence_levels;
+
+		rc = msm_lsm_get_conf_levels(prtd->lsm_client,
+				det_params.conf_level);
+		if (rc) {
+			dev_err(rtd->dev,
+				"%s: Failed to get conf_levels, err = %d\n",
+				__func__, rc);
+			break;
+		}
+
+		rc = q6lsm_set_data(prtd->lsm_client,
+			       det_params.detect_mode,
+			       det_params.detect_failure);
+		if (rc)
+			dev_err(rtd->dev,
+				"%s: Failed to set params, err = %d\n",
+				__func__, rc);
+
+		kfree(prtd->lsm_client->confidence_levels);
+		prtd->lsm_client->confidence_levels = NULL;
+
+		break;
+
+	case SNDRV_LSM_DEREG_SND_MODEL:
+		dev_dbg(rtd->dev, "%s: Deregistering sound model\n",
+			__func__);
+		rc = q6lsm_deregister_sound_model(prtd->lsm_client);
+		if (rc)
+			dev_err(rtd->dev,
+				"%s: Sound model de-register failed, err = %d\n",
+				__func__, rc);
+		break;
+
+	case SNDRV_LSM_EVENT_STATUS:
+		dev_dbg(rtd->dev, "%s: Get event status\n", __func__);
+		atomic_set(&prtd->event_wait_stop, 0);
+		rc = wait_event_freezable(prtd->event_wait,
+				(cmpxchg(&prtd->event_avail, 1, 0) ||
+				 (xchg = atomic_cmpxchg(&prtd->event_wait_stop,
+							1, 0))));
+		dev_dbg(rtd->dev, "%s: wait_event_freezable %d event_wait_stop %d\n",
+			 __func__, rc, xchg);
+		if (!rc && !xchg) {
+			dev_dbg(rtd->dev, "%s: New event available %ld\n",
+				__func__, prtd->event_avail);
+			spin_lock_irqsave(&prtd->event_lock, flags);
+			if (prtd->event_status) {
+				size = sizeof(*(prtd->event_status)) +
+				prtd->event_status->payload_size;
+				spin_unlock_irqrestore(&prtd->event_lock,
+						       flags);
+			} else {
+				spin_unlock_irqrestore(&prtd->event_lock,
+						       flags);
+				rc = -EINVAL;
+				dev_err(rtd->dev,
+					"%s: prtd->event_status is NULL\n",
+					__func__);
+				break;
+			}
+			if (user->payload_size <
+			    prtd->event_status->payload_size) {
+				dev_dbg(rtd->dev,
+					"%s: provided %d bytes isn't enough, needs %d bytes\n",
+					__func__, user->payload_size,
+					prtd->event_status->payload_size);
+				rc = -ENOMEM;
+			} else {
+				memcpy(user, prtd->event_status, size);
+				if (prtd->lsm_client->lab_enable
+					&& !prtd->lsm_client->lab_started
+					&& prtd->event_status->status ==
+					LSM_VOICE_WAKEUP_STATUS_DETECTED) {
+					atomic_set(&prtd->read_abort, 0);
+					atomic_set(&prtd->buf_count, 0);
+					prtd->appl_cnt = 0;
+					prtd->dma_write = 0;
+					rc = msm_lsm_queue_lab_buffer(prtd,
+						0);
+					if (rc)
+						dev_err(rtd->dev,
+							"%s: Queue buffer failed for lab rc = %d\n",
+							__func__, rc);
+					else
+						prtd->lsm_client->lab_started
+						= true;
+				}
+			}
+		} else if (xchg) {
+			dev_dbg(rtd->dev, "%s: Wait aborted\n", __func__);
+			rc = 0;
+		}
+		break;
+
+	case SNDRV_LSM_ABORT_EVENT:
+		dev_dbg(rtd->dev, "%s: Aborting event status wait\n",
+			__func__);
+		atomic_set(&prtd->event_wait_stop, 1);
+		wake_up(&prtd->event_wait);
+		break;
+
+	case SNDRV_LSM_START:
+		dev_dbg(rtd->dev, "%s: Starting LSM client session\n",
+			__func__);
+		if (!prtd->lsm_client->started) {
+			ret = q6lsm_start(prtd->lsm_client, true);
+			if (!ret) {
+				prtd->lsm_client->started = true;
+				dev_dbg(rtd->dev, "%s: LSM client session started\n",
+					 __func__);
+			}
+		}
+		break;
+
+	case SNDRV_LSM_STOP: {
+		dev_dbg(rtd->dev,
+			"%s: Stopping LSM client session\n",
+			__func__);
+		if (prtd->lsm_client->started) {
+			if (prtd->lsm_client->lab_enable) {
+				atomic_set(&prtd->read_abort, 1);
+				if (prtd->lsm_client->lab_started) {
+					ret = q6lsm_stop_lab(prtd->lsm_client);
+					if (ret)
+						dev_err(rtd->dev,
+							"%s: stop lab failed ret %d\n",
+							__func__, ret);
+					prtd->lsm_client->lab_started = false;
+				}
+			}
+			ret = q6lsm_stop(prtd->lsm_client, true);
+			if (!ret)
+				dev_dbg(rtd->dev,
+					"%s: LSM client session stopped %d\n",
+					__func__, ret);
+			prtd->lsm_client->started = false;
+		}
+		break;
+	}
+	case SNDRV_LSM_LAB_CONTROL: {
+		u32 *enable = NULL;
+
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: Invalid param arg for ioctl %s session %d\n",
+				__func__, "SNDRV_LSM_LAB_CONTROL",
+				prtd->lsm_client->session);
+			rc = -EINVAL;
+			break;
+		}
+		enable = (int *)arg;
+		dev_dbg(rtd->dev, "%s: ioctl %s, enable = %d\n",
+			 __func__, "SNDRV_LSM_LAB_CONTROL", *enable);
+		if (!prtd->lsm_client->started) {
+			if (prtd->lsm_client->lab_enable == *enable) {
+				dev_dbg(rtd->dev,
+					"%s: Lab for session %d already %s\n",
+					__func__, prtd->lsm_client->session,
+					((*enable) ? "enabled" : "disabled"));
+				rc = 0;
+				break;
+			}
+			rc = q6lsm_lab_control(prtd->lsm_client, *enable);
+			if (rc) {
+				dev_err(rtd->dev,
+					"%s: ioctl %s failed rc %d to %s lab for session %d\n",
+					__func__, "SNDRV_LAB_CONTROL", rc,
+					((*enable) ? "enable" : "disable"),
+					prtd->lsm_client->session);
+			} else {
+				rc = msm_lsm_lab_buffer_alloc(prtd,
+					((*enable) ? LAB_BUFFER_ALLOC
+					: LAB_BUFFER_DEALLOC));
+				if (rc)
+					dev_err(rtd->dev,
+						"%s: msm_lsm_lab_buffer_alloc failed rc %d for %s",
+						__func__, rc,
+					((*enable) ? "ALLOC" : "DEALLOC"));
+				if (!rc)
+					prtd->lsm_client->lab_enable = *enable;
+			}
+		} else {
+			dev_err(rtd->dev, "%s: ioctl %s issued after start",
+				__func__, "SNDRV_LSM_LAB_CONTROL");
+			rc = -EINVAL;
+		}
+		break;
+	}
+	case SNDRV_LSM_STOP_LAB:
+		dev_dbg(rtd->dev, "%s: stopping LAB\n", __func__);
+		if (prtd->lsm_client->lab_enable &&
+			prtd->lsm_client->lab_started) {
+			atomic_set(&prtd->read_abort, 1);
+			rc = q6lsm_stop_lab(prtd->lsm_client);
+			if (rc)
+				dev_err(rtd->dev,
+					"%s: Lab stop failed for session %d rc %d\n",
+					__func__,
+					prtd->lsm_client->session, rc);
+			prtd->lsm_client->lab_started = false;
+		}
+	break;
+	default:
+		dev_dbg(rtd->dev,
+			"%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
+			 __func__, cmd);
+		rc = snd_pcm_lib_ioctl(substream, cmd, arg);
+		break;
+	}
+
+	if (!rc)
+		dev_dbg(rtd->dev, "%s: leave (%d)\n",
+			__func__, rc);
+	else
+		dev_err(rtd->dev, "%s: cmd 0x%x failed %d\n",
+			__func__, cmd, rc);
+
+	return rc;
+}
+#ifdef CONFIG_COMPAT
+struct snd_lsm_event_status32 {
+	u16 status;
+	u16 payload_size;
+	u8 payload[0];
+};
+
+struct snd_lsm_sound_model_v2_32 {
+	compat_uptr_t data;
+	compat_uptr_t confidence_level;
+	u32 data_size;
+	enum lsm_detection_mode detection_mode;
+	u8 num_confidence_levels;
+	bool detect_failure;
+};
+
+struct snd_lsm_detection_params_32 {
+	compat_uptr_t conf_level;
+	enum lsm_detection_mode detect_mode;
+	u8 num_confidence_levels;
+	bool detect_failure;
+};
+
+struct lsm_params_info_32 {
+	u32 module_id;
+	u32 param_id;
+	u32 param_size;
+	compat_uptr_t param_data;
+	enum LSM_PARAM_TYPE param_type;
+};
+
+struct snd_lsm_module_params_32 {
+	compat_uptr_t params;
+	u32 num_params;
+	u32 data_size;
+};
+
+enum {
+	SNDRV_LSM_EVENT_STATUS32 =
+		_IOW('U', 0x02, struct snd_lsm_event_status32),
+	SNDRV_LSM_REG_SND_MODEL_V2_32 =
+		_IOW('U', 0x07, struct snd_lsm_sound_model_v2_32),
+	SNDRV_LSM_SET_PARAMS_32 =
+		_IOW('U', 0x0A, struct snd_lsm_detection_params_32),
+	SNDRV_LSM_SET_MODULE_PARAMS_32 =
+		_IOW('U', 0x0B, struct snd_lsm_module_params_32),
+};
+
+static int msm_lsm_ioctl_compat(struct snd_pcm_substream *substream,
+			  unsigned int cmd, void __user *arg)
+{
+	struct snd_pcm_runtime *runtime;
+	struct lsm_priv *prtd;
+	struct snd_soc_pcm_runtime *rtd;
+	int err = 0;
+	u32 size = 0;
+
+	if (PCM_RUNTIME_CHECK(substream))
+		return -ENXIO;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!substream) ? "substream" : "private_data");
+		return -EINVAL;
+	}
+	runtime = substream->runtime;
+	rtd = substream->private_data;
+	prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_LSM_EVENT_STATUS32: {
+		struct snd_lsm_event_status32 userarg32, *user32 = NULL;
+		struct snd_lsm_event_status *user = NULL;
+
+		if (copy_from_user(&userarg32, arg, sizeof(userarg32))) {
+			dev_err(rtd->dev, "%s: err copyuser ioctl %s\n",
+				__func__, "SNDRV_LSM_EVENT_STATUS32");
+			return -EFAULT;
+		}
+
+		if (userarg32.payload_size >
+		    LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+			pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
+				__func__, userarg32.payload_size,
+				LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+			return -EINVAL;
+		}
+
+		size = sizeof(*user) + userarg32.payload_size;
+		user = kmalloc(size, GFP_KERNEL);
+		if (!user) {
+			dev_err(rtd->dev,
+				"%s: Allocation failed event status size %d\n",
+				__func__, size);
+			return -EFAULT;
+		} else {
+			cmd = SNDRV_LSM_EVENT_STATUS;
+			user->payload_size = userarg32.payload_size;
+			err = msm_lsm_ioctl_shared(substream, cmd, user);
+		}
+
+		/* Update size with actual payload size */
+		size = sizeof(userarg32) + user->payload_size;
+		if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
+			dev_err(rtd->dev,
+				"%s: write verify failed size %d\n",
+				__func__, size);
+			err = -EFAULT;
+		}
+		if (!err) {
+			user32 = kmalloc(size, GFP_KERNEL);
+			if (!user32) {
+				dev_err(rtd->dev,
+					"%s: Allocation event user status size %d\n",
+					__func__, size);
+				err = -EFAULT;
+			} else {
+				user32->status = user->status;
+				user32->payload_size = user->payload_size;
+				memcpy(user32->payload,
+				user->payload, user32->payload_size);
+			}
+		}
+		if (!err && (copy_to_user(arg, user32, size))) {
+			dev_err(rtd->dev, "%s: failed to copy payload %d",
+				__func__, size);
+			err = -EFAULT;
+		}
+		kfree(user);
+		kfree(user32);
+		if (err)
+			dev_err(rtd->dev, "%s: lsmevent failed %d",
+				__func__, err);
+		break;
+	}
+
+	case SNDRV_LSM_REG_SND_MODEL_V2_32: {
+		struct snd_lsm_sound_model_v2_32 snd_modelv232;
+		struct snd_lsm_sound_model_v2 snd_modelv2;
+
+		if (prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "REG_SND_MODEL_V2");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&snd_modelv232, arg,
+			sizeof(snd_modelv232))) {
+			err = -EFAULT;
+			dev_err(rtd->dev,
+				"%s: copy user failed, size %zd %s\n",
+				__func__,
+				sizeof(struct snd_lsm_sound_model_v2_32),
+				"SNDRV_LSM_REG_SND_MODEL_V2_32");
+		} else {
+			snd_modelv2.confidence_level =
+			compat_ptr(snd_modelv232.confidence_level);
+			snd_modelv2.data = compat_ptr(snd_modelv232.data);
+			snd_modelv2.data_size = snd_modelv232.data_size;
+			snd_modelv2.detect_failure =
+			snd_modelv232.detect_failure;
+			snd_modelv2.detection_mode =
+			snd_modelv232.detection_mode;
+			snd_modelv2.num_confidence_levels =
+			snd_modelv232.num_confidence_levels;
+			cmd = SNDRV_LSM_REG_SND_MODEL_V2;
+			err = msm_lsm_ioctl_shared(substream, cmd,
+				&snd_modelv2);
+			if (err)
+				dev_err(rtd->dev,
+					"%s: ioctl %s failed\n", __func__,
+					"SNDDRV_LSM_REG_SND_MODEL_V2_32");
+		}
+		break;
+	}
+
+	case SNDRV_LSM_SET_PARAMS_32:{
+		struct snd_lsm_detection_params_32 det_params32;
+		struct snd_lsm_detection_params det_params;
+
+		if (prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "SET_PARAMS_32");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&det_params32, arg,
+				   sizeof(det_params32))) {
+			err = -EFAULT;
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "SNDRV_LSM_SET_PARAMS_32",
+				sizeof(det_params32));
+		} else {
+			det_params.conf_level =
+				compat_ptr(det_params32.conf_level);
+			det_params.detect_mode =
+				det_params32.detect_mode;
+			det_params.num_confidence_levels =
+				det_params32.num_confidence_levels;
+			det_params.detect_failure =
+				det_params32.detect_failure;
+			cmd = SNDRV_LSM_SET_PARAMS;
+			err = msm_lsm_ioctl_shared(substream, cmd,
+					&det_params);
+			if (err)
+				dev_err(rtd->dev,
+					"%s: ioctl %s failed\n", __func__,
+					"SNDRV_LSM_SET_PARAMS");
+		}
+		break;
+	}
+
+	case SNDRV_LSM_SET_MODULE_PARAMS_32: {
+		struct snd_lsm_module_params_32 p_data_32;
+		struct snd_lsm_module_params p_data;
+		u8 *params, *params32;
+		size_t p_size;
+		struct lsm_params_info_32 *p_info_32;
+		struct lsm_params_info *p_info;
+		int i;
+
+		if (!prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if not using topology\n",
+				__func__, "SET_MODULE_PARAMS_32");
+			return -EINVAL;
+		}
+
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s: No Param data to set\n",
+				__func__, "SET_MODULE_PARAMS_32");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&p_data_32, arg,
+				   sizeof(p_data_32))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "SET_MODULE_PARAMS_32",
+				sizeof(p_data_32));
+			return -EFAULT;
+		}
+
+		p_data.params = compat_ptr(p_data_32.params);
+		p_data.num_params = p_data_32.num_params;
+		p_data.data_size = p_data_32.data_size;
+
+		if (p_data.num_params > LSM_PARAMS_MAX) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid num_params %d\n",
+				__func__, "SET_MODULE_PARAMS_32",
+				p_data.num_params);
+			return -EINVAL;
+		}
+
+		if (p_data.data_size !=
+		    (p_data.num_params * sizeof(struct lsm_params_info_32))) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid size %d\n",
+				__func__, "SET_MODULE_PARAMS_32",
+				p_data.data_size);
+			return -EINVAL;
+		}
+
+		p_size = sizeof(struct lsm_params_info_32) *
+			 p_data.num_params;
+
+		params32 = kzalloc(p_size, GFP_KERNEL);
+		if (!params32)
+			return -ENOMEM;
+
+		p_size = sizeof(struct lsm_params_info) * p_data.num_params;
+		params = kzalloc(p_size, GFP_KERNEL);
+		if (!params) {
+			dev_err(rtd->dev,
+				"%s: no memory for params, size = %zd\n",
+				__func__, p_size);
+			kfree(params32);
+			return -ENOMEM;
+		}
+
+		if (copy_from_user(params32, p_data.params,
+				   p_data.data_size)) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %d\n",
+				__func__, "params32", p_data.data_size);
+			kfree(params32);
+			kfree(params);
+			return -EFAULT;
+		}
+
+		p_info_32 = (struct lsm_params_info_32 *) params32;
+		p_info = (struct lsm_params_info *) params;
+		for (i = 0; i < p_data.num_params; i++) {
+			p_info->module_id = p_info_32->module_id;
+			p_info->param_id = p_info_32->param_id;
+			p_info->param_size = p_info_32->param_size;
+			p_info->param_data = compat_ptr(p_info_32->param_data);
+			p_info->param_type = p_info_32->param_type;
+
+			p_info_32++;
+			p_info++;
+		}
+
+		err = msm_lsm_process_params(substream,
+					     &p_data, params);
+		if (err)
+			dev_err(rtd->dev,
+				"%s: Failed to process params, err = %d\n",
+				__func__, err);
+		kfree(params);
+		kfree(params32);
+		break;
+	}
+	default:
+		err = msm_lsm_ioctl_shared(substream, cmd, arg);
+		break;
+	}
+	return err;
+}
+#else
+#define msm_lsm_ioctl_compat NULL
+#endif
+
+static int msm_lsm_ioctl(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	int err = 0;
+	u32 size = 0;
+	struct snd_lsm_session_data session_data;
+	struct snd_pcm_runtime *runtime;
+	struct snd_soc_pcm_runtime *rtd;
+	struct lsm_priv *prtd;
+
+	if (!substream || !substream->private_data) {
+		pr_err("%s: Invalid %s\n", __func__,
+			(!substream) ? "substream" : "private_data");
+		return -EINVAL;
+	}
+	runtime = substream->runtime;
+	prtd = runtime->private_data;
+	rtd = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_LSM_SET_SESSION_DATA:
+		dev_dbg(rtd->dev,
+			"%s: SNDRV_LSM_SET_SESSION_DATA\n",
+			__func__);
+		if (copy_from_user(&session_data, (void *)arg,
+				   sizeof(struct snd_lsm_session_data))) {
+			err = -EFAULT;
+			dev_err(rtd->dev,
+				"%s: copy from user failed, size %zd\n",
+				__func__, sizeof(struct snd_lsm_session_data));
+			break;
+		}
+		if (!err)
+			err = msm_lsm_ioctl_shared(substream,
+						   cmd, &session_data);
+		if (err)
+			dev_err(rtd->dev,
+				"%s REG_SND_MODEL failed err %d\n",
+				__func__, err);
+		break;
+	case SNDRV_LSM_REG_SND_MODEL_V2: {
+		struct snd_lsm_sound_model_v2 snd_model_v2;
+
+		if (prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "REG_SND_MODEL_V2");
+			return -EINVAL;
+		}
+
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: Invalid params snd_model\n", __func__);
+			return -EINVAL;
+		}
+		if (copy_from_user(&snd_model_v2, arg, sizeof(snd_model_v2))) {
+			err = -EFAULT;
+			dev_err(rtd->dev,
+				"%s: copy from user failed, size %zd\n",
+				__func__,
+				sizeof(struct snd_lsm_sound_model_v2));
+		}
+		if (!err)
+			err = msm_lsm_ioctl_shared(substream, cmd,
+						   &snd_model_v2);
+		if (err)
+			dev_err(rtd->dev,
+				"%s REG_SND_MODEL failed err %d\n",
+				__func__, err);
+		return err;
+		}
+		break;
+	case SNDRV_LSM_SET_PARAMS: {
+		struct snd_lsm_detection_params det_params;
+
+		if (prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if using topology\n",
+				__func__, "SET_PARAMS");
+			return -EINVAL;
+		}
+
+		pr_debug("%s: SNDRV_LSM_SET_PARAMS\n", __func__);
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s, Invalid params\n",
+				__func__, "SNDRV_LSM_SET_PARAMS");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&det_params, arg,
+				   sizeof(det_params))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size %zd\n",
+				__func__, "SNDRV_LSM_SET_PARAMS",
+				sizeof(det_params));
+			err = -EFAULT;
+		}
+
+		if (!err)
+			err = msm_lsm_ioctl_shared(substream, cmd,
+						   &det_params);
+		else
+			dev_err(rtd->dev,
+				"%s: LSM_SET_PARAMS failed, err %d\n",
+				__func__, err);
+		return err;
+	}
+
+	case SNDRV_LSM_SET_MODULE_PARAMS: {
+		struct snd_lsm_module_params p_data;
+		size_t p_size;
+		u8 *params;
+
+		if (!prtd->lsm_client->use_topology) {
+			dev_err(rtd->dev,
+				"%s: %s: not supported if not using topology\n",
+				__func__, "SET_MODULE_PARAMS");
+			return -EINVAL;
+		}
+
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: %s: No Param data to set\n",
+				__func__, "SET_MODULE_PARAMS");
+			return -EINVAL;
+		}
+
+		if (copy_from_user(&p_data, arg,
+				   sizeof(p_data))) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %zd\n",
+				__func__, "p_data", sizeof(p_data));
+			return -EFAULT;
+		}
+
+		if (p_data.num_params > LSM_PARAMS_MAX) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid num_params %d\n",
+				__func__, "SET_MODULE_PARAMS",
+				p_data.num_params);
+			return -EINVAL;
+		}
+
+		p_size = p_data.num_params *
+			 sizeof(struct lsm_params_info);
+
+		if (p_data.data_size != p_size) {
+			dev_err(rtd->dev,
+				"%s: %s: Invalid size %zd\n",
+				__func__, "SET_MODULE_PARAMS", p_size);
+
+			return -EFAULT;
+		}
+
+		params = kzalloc(p_size, GFP_KERNEL);
+		if (!params)
+			return -ENOMEM;
+
+		if (copy_from_user(params, p_data.params,
+				   p_data.data_size)) {
+			dev_err(rtd->dev,
+				"%s: %s: copy_from_user failed, size = %d\n",
+				__func__, "params", p_data.data_size);
+			kfree(params);
+			return -EFAULT;
+		}
+
+		err = msm_lsm_process_params(substream, &p_data, params);
+		if (err)
+			dev_err(rtd->dev,
+				"%s: %s: Failed to set params, err = %d\n",
+				__func__, "SET_MODULE_PARAMS", err);
+		kfree(params);
+		break;
+	}
+
+	case SNDRV_LSM_EVENT_STATUS: {
+		struct snd_lsm_event_status *user = NULL, userarg;
+
+		dev_dbg(rtd->dev,
+			"%s: SNDRV_LSM_EVENT_STATUS\n", __func__);
+		if (!arg) {
+			dev_err(rtd->dev,
+				"%s: Invalid params event status\n",
+				__func__);
+			return -EINVAL;
+		}
+		if (copy_from_user(&userarg, arg, sizeof(userarg))) {
+			dev_err(rtd->dev,
+				"%s: err copyuser event_status\n",
+				__func__);
+			return -EFAULT;
+		}
+
+		if (userarg.payload_size >
+		    LISTEN_MAX_STATUS_PAYLOAD_SIZE) {
+			pr_err("%s: payload_size %d is invalid, max allowed = %d\n",
+				__func__, userarg.payload_size,
+				LISTEN_MAX_STATUS_PAYLOAD_SIZE);
+			return -EINVAL;
+		}
+
+		size = sizeof(struct snd_lsm_event_status) +
+		userarg.payload_size;
+		user = kmalloc(size, GFP_KERNEL);
+		if (!user) {
+			dev_err(rtd->dev,
+				"%s: Allocation failed event status size %d\n",
+				__func__, size);
+			return -EFAULT;
+		}
+		user->payload_size = userarg.payload_size;
+		err = msm_lsm_ioctl_shared(substream, cmd, user);
+
+		/* Update size with actual payload size */
+		size = sizeof(*user) + user->payload_size;
+		if (!err && !access_ok(VERIFY_WRITE, arg, size)) {
+			dev_err(rtd->dev,
+				"%s: write verify failed size %d\n",
+				__func__, size);
+			err = -EFAULT;
+		}
+		if (!err && (copy_to_user(arg, user, size))) {
+			dev_err(rtd->dev,
+				"%s: failed to copy payload %d",
+				__func__, size);
+			err = -EFAULT;
+		}
+		kfree(user);
+		if (err)
+			dev_err(rtd->dev,
+				"%s: lsmevent failed %d", __func__, err);
+		return err;
+	}
+	default:
+		err = msm_lsm_ioctl_shared(substream, cmd, arg);
+	break;
+	}
+	return err;
+}
+
+static int msm_lsm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd;
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+	prtd = kzalloc(sizeof(struct lsm_priv), GFP_KERNEL);
+	if (!prtd) {
+		pr_err("%s: Failed to allocate memory for lsm_priv\n",
+		       __func__);
+		return -ENOMEM;
+	}
+	spin_lock_init(&prtd->event_lock);
+	init_waitqueue_head(&prtd->event_wait);
+	init_waitqueue_head(&prtd->period_wait);
+	prtd->substream = substream;
+	runtime->private_data = prtd;
+	runtime->hw = msm_pcm_hardware_capture;
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&constraints_sample_rates);
+	if (ret < 0)
+		pr_info("%s: snd_pcm_hw_constraint_list failed ret %d\n",
+			 __func__, ret);
+	/* Ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+			    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		pr_info("%s: snd_pcm_hw_constraint_integer failed ret %d\n",
+			__func__, ret);
+
+	ret = snd_pcm_hw_constraint_minmax(runtime,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+		CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE,
+		CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE);
+	if (ret < 0)
+		pr_info("%s: constraint for buffer bytes min max ret = %d\n",
+			__func__, ret);
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (ret < 0) {
+		pr_info("%s: constraint for period bytes step ret = %d\n",
+			__func__, ret);
+	}
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (ret < 0)
+		pr_info("%s: constraint for buffer bytes step ret = %d\n",
+			__func__, ret);
+	prtd->lsm_client = q6lsm_client_alloc(
+				(lsm_app_cb)lsm_event_handler, prtd);
+	if (!prtd->lsm_client) {
+		pr_err("%s: Could not allocate memory\n", __func__);
+		kfree(prtd);
+		runtime->private_data = NULL;
+		return -ENOMEM;
+	}
+	prtd->lsm_client->opened = false;
+	return 0;
+}
+
+static int msm_lsm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!substream->private_data) {
+		pr_err("%s: Invalid private_data", __func__);
+		return -EINVAL;
+	}
+
+	rtd = prtd->substream->private_data;
+
+	if (!prtd->lsm_client) {
+		dev_err(rtd->dev,
+			"%s: LSM client data ptr is NULL\n", __func__);
+		return -EINVAL;
+	}
+	prtd->lsm_client->started = false;
+	runtime->private_data = prtd;
+	return 0;
+}
+
+static int msm_lsm_close(struct snd_pcm_substream *substream)
+{
+	unsigned long flags;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd;
+	int ret = 0;
+
+	if (!substream->private_data) {
+		pr_err("%s: Invalid private_data", __func__);
+		return -EINVAL;
+	}
+	if (!prtd || !prtd->lsm_client) {
+		pr_err("%s: No LSM session active\n", __func__);
+		return -EINVAL;
+	}
+	rtd = substream->private_data;
+
+	dev_dbg(rtd->dev, "%s\n", __func__);
+	if (prtd->lsm_client->started) {
+		ret = q6lsm_stop(prtd->lsm_client, true);
+		if (ret)
+			dev_err(rtd->dev,
+				"%s: session stop failed, err = %d\n",
+				__func__, ret);
+		else
+			dev_dbg(rtd->dev,
+				"%s: LSM client session stopped %d\n",
+				 __func__, ret);
+
+		/*
+		 * Go Ahead and try de-register sound model,
+		 * even if stop failed
+		 */
+		prtd->lsm_client->started = false;
+
+		ret = q6lsm_deregister_sound_model(prtd->lsm_client);
+		if (ret)
+			dev_err(rtd->dev,
+				"%s: dereg_snd_model failed, err = %d\n",
+				__func__, ret);
+		else
+			dev_dbg(rtd->dev, "%s: dereg_snd_model successful\n",
+				 __func__);
+	}
+
+	if (prtd->lsm_client->opened) {
+		q6lsm_close(prtd->lsm_client);
+		prtd->lsm_client->opened = false;
+	}
+	q6lsm_client_free(prtd->lsm_client);
+
+	spin_lock_irqsave(&prtd->event_lock, flags);
+	kfree(prtd->event_status);
+	prtd->event_status = NULL;
+	spin_unlock_irqrestore(&prtd->event_lock, flags);
+	kfree(prtd);
+	runtime->private_data = NULL;
+
+	return 0;
+}
+
+static int msm_lsm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct lsm_lab_hw_params *hw_params = NULL;
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!substream->private_data) {
+		pr_err("%s: Invalid private_data", __func__);
+		return -EINVAL;
+	}
+	rtd = substream->private_data;
+
+	if (!prtd || !params) {
+		dev_err(rtd->dev,
+			"%s: invalid params prtd %pK params %pK",
+			 __func__, prtd, params);
+		return -EINVAL;
+	}
+	hw_params = &prtd->lsm_client->hw_params;
+	hw_params->sample_rate = params_rate(params);
+	hw_params->sample_size =
+	(params_format(params) == SNDRV_PCM_FORMAT_S16_LE) ? 16 : 0;
+	hw_params->period_count = params_periods(params);
+	if (hw_params->sample_rate != 16000 || hw_params->sample_size != 16 ||
+		hw_params->period_count == 0) {
+		dev_err(rtd->dev,
+			"%s: Invalid params sample rate %d sample size %d period count %d",
+			__func__, hw_params->sample_rate,
+			hw_params->sample_size,
+		hw_params->period_count);
+		return -EINVAL;
+	}
+	hw_params->buf_sz = params_buffer_bytes(params) /
+	hw_params->period_count;
+	dev_dbg(rtd->dev,
+		"%s: sample rate %d sample size %d buffer size %d period count %d\n",
+		__func__, hw_params->sample_rate, hw_params->sample_size,
+		hw_params->buf_sz, hw_params->period_count);
+	return 0;
+}
+
+static snd_pcm_uframes_t msm_lsm_pcm_pointer(
+	struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!substream->private_data) {
+		pr_err("%s: Invalid private_data", __func__);
+		return -EINVAL;
+	}
+	rtd = substream->private_data;
+
+	if (!prtd) {
+		dev_err(rtd->dev,
+			"%s: Invalid param %pK\n", __func__, prtd);
+		return 0;
+	}
+
+	if (prtd->dma_write >= snd_pcm_lib_buffer_bytes(substream))
+		prtd->dma_write = 0;
+	dev_dbg(rtd->dev,
+		"%s: dma post = %d\n", __func__, prtd->dma_write);
+	return bytes_to_frames(runtime, prtd->dma_write);
+}
+
+static int msm_lsm_pcm_copy(struct snd_pcm_substream *substream, int ch,
+	snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct lsm_priv *prtd = runtime->private_data;
+	char *pcm_buf = NULL;
+	int fbytes = 0, rc = 0;
+	struct snd_soc_pcm_runtime *rtd;
+
+	if (!substream->private_data) {
+		pr_err("%s: Invalid private_data", __func__);
+		return -EINVAL;
+	}
+	rtd = substream->private_data;
+
+	if (!prtd) {
+		dev_err(rtd->dev,
+			"%s: Invalid param %pK\n", __func__, prtd);
+		return -EINVAL;
+	}
+
+	fbytes = frames_to_bytes(runtime, frames);
+	if (runtime->status->state == SNDRV_PCM_STATE_XRUN ||
+	    runtime->status->state == SNDRV_PCM_STATE_PREPARED) {
+		dev_err(rtd->dev,
+			"%s: runtime state incorrect %d", __func__,
+			runtime->status->state);
+		return 0;
+	}
+	rc = wait_event_timeout(prtd->period_wait,
+		(atomic_read(&prtd->buf_count) |
+		atomic_read(&prtd->read_abort)), (2 * HZ));
+	if (!rc) {
+		dev_err(rtd->dev,
+			"%s: timeout for read retry\n", __func__);
+		return -EAGAIN;
+	}
+	if (atomic_read(&prtd->read_abort)) {
+		dev_err(rtd->dev,
+			"%s: Read abort received\n", __func__);
+		return -EIO;
+	}
+	prtd->appl_cnt = prtd->appl_cnt %
+		prtd->lsm_client->hw_params.period_count;
+	pcm_buf = prtd->lsm_client->lab_buffer[prtd->appl_cnt].data;
+	dev_dbg(rtd->dev,
+		"%s: copy the pcm data size %d\n",
+		__func__, fbytes);
+	if (pcm_buf) {
+		if (copy_to_user(buf, pcm_buf, fbytes)) {
+			dev_err(rtd->dev,
+				"%s: failed to copy bytes %d\n",
+				__func__, fbytes);
+			return -EINVAL;
+		}
+	} else {
+		dev_err(rtd->dev,
+			"%s: Invalid pcm buffer\n", __func__);
+		return -EINVAL;
+	}
+	prtd->appl_cnt = (prtd->appl_cnt + 1) %
+		prtd->lsm_client->hw_params.period_count;
+	atomic_dec(&prtd->buf_count);
+	return 0;
+}
+
+static const struct snd_pcm_ops msm_lsm_ops = {
+	.open           = msm_lsm_open,
+	.close          = msm_lsm_close,
+	.ioctl          = msm_lsm_ioctl,
+	.prepare	= msm_lsm_prepare,
+	.compat_ioctl   = msm_lsm_ioctl_compat,
+	.hw_params      = msm_lsm_hw_params,
+	.copy           = msm_lsm_pcm_copy,
+	.pointer        = msm_lsm_pcm_pointer,
+};
+
+static int msm_asoc_lsm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	return 0;
+}
+
+static int msm_asoc_lsm_probe(struct snd_soc_platform *platform)
+{
+	pr_debug("enter %s\n", __func__);
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_lsm_ops,
+	.pcm_new	= msm_asoc_lsm_new,
+	.probe		= msm_asoc_lsm_probe,
+};
+
+static int msm_lsm_probe(struct platform_device *pdev)
+{
+
+	return snd_soc_register_platform(&pdev->dev, &msm_soc_platform);
+}
+
+static int msm_lsm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+
+	return 0;
+}
+
+static const struct of_device_id msm_lsm_client_dt_match[] = {
+	{.compatible = "qcom,msm-lsm-client" },
+	{ }
+};
+
+static struct platform_driver msm_lsm_driver = {
+	.driver = {
+		.name = "msm-lsm-client",
+		.owner = THIS_MODULE,
+		.of_match_table = of_match_ptr(msm_lsm_client_dt_match),
+	},
+	.probe = msm_lsm_probe,
+	.remove = msm_lsm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	return platform_driver_register(&msm_lsm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_lsm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("LSM client platform driver");
+MODULE_DEVICE_TABLE(of, msm_lsm_client_dt_match);
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
new file mode 100644
index 0000000..8d43186
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.c
@@ -0,0 +1,921 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <asm/dma.h>
+#include "msm-pcm-afe-v2.h"
+
+#define MIN_PLAYBACK_PERIOD_SIZE (128 * 2)
+#define MAX_PLAYBACK_PERIOD_SIZE (128 * 2 * 2 * 6)
+#define MIN_PLAYBACK_NUM_PERIODS (4)
+#define MAX_PLAYBACK_NUM_PERIODS (384)
+
+#define MIN_CAPTURE_PERIOD_SIZE (128 * 2)
+#define MAX_CAPTURE_PERIOD_SIZE (192 * 2 * 2 * 8 * 4)
+#define MIN_CAPTURE_NUM_PERIODS (4)
+#define MAX_CAPTURE_NUM_PERIODS (384)
+
+static struct snd_pcm_hardware msm_afe_hardware_playback = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE|
+				SNDRV_PCM_FMTBIT_S24_LE,
+	.rates =                (SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_48000),
+	.rate_min =             8000,
+	.rate_max =             48000,
+	.channels_min =         1,
+	.channels_max =         6,
+	.buffer_bytes_max =     MAX_PLAYBACK_PERIOD_SIZE *
+				MAX_PLAYBACK_NUM_PERIODS,
+	.period_bytes_min =     MIN_PLAYBACK_PERIOD_SIZE,
+	.period_bytes_max =     MAX_PLAYBACK_PERIOD_SIZE,
+	.periods_min =          MIN_PLAYBACK_NUM_PERIODS,
+	.periods_max =          MAX_PLAYBACK_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+static struct snd_pcm_hardware msm_afe_hardware_capture = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE|
+				SNDRV_PCM_FMTBIT_S24_LE,
+	.rates =                (SNDRV_PCM_RATE_8000 |
+				SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_48000),
+	.rate_min =             8000,
+	.rate_max =             48000,
+	.channels_min =         1,
+	.channels_max =         6,
+	.buffer_bytes_max =     MAX_CAPTURE_PERIOD_SIZE *
+				MAX_CAPTURE_NUM_PERIODS,
+	.period_bytes_min =     MIN_CAPTURE_PERIOD_SIZE,
+	.period_bytes_max =     MAX_CAPTURE_PERIOD_SIZE,
+	.periods_min =          MIN_CAPTURE_NUM_PERIODS,
+	.periods_max =          MAX_CAPTURE_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt);
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt);
+
+static enum hrtimer_restart afe_hrtimer_callback(struct hrtimer *hrt)
+{
+	struct pcm_afe_info *prtd =
+		container_of(hrt, struct pcm_afe_info, hrt);
+	struct snd_pcm_substream *substream = prtd->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	u32 mem_map_handle = 0;
+
+	mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+	if (!mem_map_handle)
+		pr_err("%s: mem_map_handle is NULL\n", __func__);
+
+	if (prtd->start) {
+		pr_debug("sending frame to DSP: poll_time: %d\n",
+				prtd->poll_time);
+		if (prtd->dsp_cnt == runtime->periods)
+			prtd->dsp_cnt = 0;
+		pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+		afe_rt_proxy_port_write(
+		(prtd->dma_addr +
+		(prtd->dsp_cnt *
+		snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+		snd_pcm_lib_period_bytes(prtd->substream));
+		prtd->dsp_cnt++;
+		hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+					* 1000));
+
+		return HRTIMER_RESTART;
+	} else
+		return HRTIMER_NORESTART;
+}
+static enum hrtimer_restart afe_hrtimer_rec_callback(struct hrtimer *hrt)
+{
+	struct pcm_afe_info *prtd =
+		container_of(hrt, struct pcm_afe_info, hrt);
+	struct snd_pcm_substream *substream = prtd->substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	u32 mem_map_handle = 0;
+	int ret;
+
+	mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+	if (!mem_map_handle)
+		pr_err("%s: mem_map_handle is NULL\n", __func__);
+
+	if (prtd->start) {
+		if (prtd->dsp_cnt == runtime->periods)
+			prtd->dsp_cnt = 0;
+		pr_debug("%s: mem_map_handle 0x%x\n", __func__, mem_map_handle);
+		ret = afe_rt_proxy_port_read(
+		(prtd->dma_addr + (prtd->dsp_cnt
+		* snd_pcm_lib_period_bytes(prtd->substream))), mem_map_handle,
+		snd_pcm_lib_period_bytes(prtd->substream));
+		if (ret < 0) {
+			pr_err("%s: AFE port read fails: %d\n", __func__, ret);
+			prtd->start = 0;
+			return HRTIMER_NORESTART;
+		}
+		prtd->dsp_cnt++;
+		pr_debug("sending frame rec to DSP: poll_time: %d\n",
+				prtd->poll_time);
+		hrtimer_forward_now(hrt, ns_to_ktime(prtd->poll_time
+				* 1000));
+
+		return HRTIMER_RESTART;
+	} else
+		return HRTIMER_NORESTART;
+}
+static void pcm_afe_process_tx_pkt(uint32_t opcode,
+		uint32_t token, uint32_t *payload,
+		 void *priv)
+{
+	struct pcm_afe_info *prtd = priv;
+	unsigned long dsp_flags;
+	struct snd_pcm_substream *substream = NULL;
+	struct snd_pcm_runtime *runtime = NULL;
+	uint16_t event;
+	uint64_t period_bytes;
+	uint64_t bytes_one_sec;
+
+	if (prtd == NULL)
+		return;
+	substream =  prtd->substream;
+	runtime = substream->runtime;
+	pr_debug("%s\n", __func__);
+	spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+	switch (opcode) {
+	case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+		event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+			switch (event) {
+			case AFE_EVENT_RTPORT_START: {
+				prtd->dsp_cnt = 0;
+				/* Calculate poll time.
+				 * Split steps to avoid overflow.
+				 * Poll time-time corresponding to one period
+				 * in bytes.
+				 * (Samplerate * channelcount * format) =
+				 * bytes in 1 sec.
+				 * Poll time =
+				 *	(period bytes / bytes in one sec) *
+				 *	 1000000 micro seconds.
+				 * Multiplication by 1000000 is done in two
+				 * steps to keep the accuracy of poll time.
+				 */
+				if (prtd->mmap_flag) {
+					period_bytes = ((uint64_t)(
+						(snd_pcm_lib_period_bytes(
+							prtd->substream)) *
+							1000));
+					bytes_one_sec = (runtime->rate
+						* runtime->channels * 2);
+					bytes_one_sec =
+						div_u64(bytes_one_sec, 1000);
+					prtd->poll_time =
+						div_u64(period_bytes,
+						bytes_one_sec);
+					pr_debug("prtd->poll_time: %d",
+							prtd->poll_time);
+				}
+				break;
+			}
+			case AFE_EVENT_RTPORT_STOP:
+				pr_debug("%s: event!=0\n", __func__);
+				prtd->start = 0;
+				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+				break;
+			case AFE_EVENT_RTPORT_LOW_WM:
+				pr_debug("%s: Underrun\n", __func__);
+				break;
+			case AFE_EVENT_RTPORT_HI_WM:
+				pr_debug("%s: Overrun\n", __func__);
+				break;
+			default:
+				break;
+			}
+			break;
+	}
+	case APR_BASIC_RSP_RESULT: {
+		switch (payload[0]) {
+		case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
+			pr_debug("write done\n");
+			prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+							(prtd->substream);
+			snd_pcm_period_elapsed(prtd->substream);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case RESET_EVENTS:
+		prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+						(prtd->substream);
+		prtd->reset_event = true;
+		snd_pcm_period_elapsed(prtd->substream);
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static void pcm_afe_process_rx_pkt(uint32_t opcode,
+		uint32_t token, uint32_t *payload,
+		 void *priv)
+{
+	struct pcm_afe_info *prtd = priv;
+	unsigned long dsp_flags;
+	struct snd_pcm_substream *substream = NULL;
+	struct snd_pcm_runtime *runtime = NULL;
+	uint16_t event;
+	uint64_t period_bytes;
+	uint64_t bytes_one_sec;
+	uint32_t mem_map_handle = 0;
+
+	if (prtd == NULL)
+		return;
+	substream =  prtd->substream;
+	runtime = substream->runtime;
+	pr_debug("%s\n", __func__);
+	spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+	switch (opcode) {
+	case AFE_EVENT_RT_PROXY_PORT_STATUS: {
+		event = (uint16_t)((0xFFFF0000 & payload[0]) >> 0x10);
+		switch (event) {
+		case AFE_EVENT_RTPORT_START: {
+			prtd->dsp_cnt = 0;
+			/* Calculate poll time. Split steps to avoid overflow.
+			 * Poll time-time corresponding to one period in bytes.
+			 * (Samplerate * channelcount * format)=bytes in 1 sec.
+			 * Poll time =  (period bytes / bytes in one sec) *
+			 * 1000000 micro seconds.
+			 * Multiplication by 1000000 is done in two steps to
+			 * keep the accuracy of poll time.
+			 */
+			if (prtd->mmap_flag) {
+				period_bytes = ((uint64_t)(
+					(snd_pcm_lib_period_bytes(
+					prtd->substream)) * 1000));
+				bytes_one_sec = (runtime->rate *
+						runtime->channels * 2);
+				bytes_one_sec = div_u64(bytes_one_sec, 1000);
+				prtd->poll_time =
+					div_u64(period_bytes, bytes_one_sec);
+				pr_debug("prtd->poll_time : %d\n",
+					prtd->poll_time);
+			} else {
+				mem_map_handle =
+					afe_req_mmap_handle(prtd->audio_client);
+				if (!mem_map_handle)
+					pr_err("%s:mem_map_handle is NULL\n",
+							 __func__);
+				/* Do initial read to start transfer */
+				afe_rt_proxy_port_read((prtd->dma_addr +
+					(prtd->dsp_cnt *
+					snd_pcm_lib_period_bytes(
+						prtd->substream))),
+					mem_map_handle,
+					snd_pcm_lib_period_bytes(
+						prtd->substream));
+				prtd->dsp_cnt++;
+			}
+			break;
+		}
+		case AFE_EVENT_RTPORT_STOP:
+			pr_debug("%s: event!=0\n", __func__);
+			prtd->start = 0;
+			snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);
+			break;
+		case AFE_EVENT_RTPORT_LOW_WM:
+			pr_debug("%s: Underrun\n", __func__);
+			break;
+		case AFE_EVENT_RTPORT_HI_WM:
+			pr_debug("%s: Overrun\n", __func__);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case APR_BASIC_RSP_RESULT: {
+		switch (payload[0]) {
+		case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
+			pr_debug("%s :Read done\n", __func__);
+			prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+							(prtd->substream);
+			if (!prtd->mmap_flag) {
+				atomic_set(&prtd->rec_bytes_avail, 1);
+				wake_up(&prtd->read_wait);
+			}
+			snd_pcm_period_elapsed(prtd->substream);
+			break;
+		default:
+			break;
+		}
+		break;
+	}
+	case RESET_EVENTS:
+		prtd->pcm_irq_pos += snd_pcm_lib_period_bytes
+							(prtd->substream);
+		prtd->reset_event = true;
+		if (!prtd->mmap_flag) {
+			atomic_set(&prtd->rec_bytes_avail, 1);
+			wake_up(&prtd->read_wait);
+		}
+		snd_pcm_period_elapsed(prtd->substream);
+		break;
+	default:
+		break;
+	}
+	spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+}
+
+static int msm_afe_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *dai = rtd->cpu_dai;
+	int ret = 0;
+
+	pr_debug("%s: sample_rate=%d\n", __func__, runtime->rate);
+
+	pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+	ret = afe_register_get_events(dai->id,
+			pcm_afe_process_tx_pkt, prtd);
+	if (ret < 0) {
+		pr_err("afe-pcm:register for events failed\n");
+		return ret;
+	}
+	pr_debug("%s:success\n", __func__);
+	prtd->prepared++;
+	return ret;
+}
+
+static int msm_afe_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	struct snd_soc_dai *dai = rtd->cpu_dai;
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+	ret = afe_register_get_events(dai->id,
+			pcm_afe_process_rx_pkt, prtd);
+	if (ret < 0) {
+		pr_err("afe-pcm:register for events failed\n");
+		return ret;
+	}
+	pr_debug("%s:success\n", __func__);
+	prtd->prepared++;
+	return 0;
+}
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 16000, 48000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static int msm_afe_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = NULL;
+	int ret = 0;
+
+	prtd = kzalloc(sizeof(struct pcm_afe_info), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+	pr_debug("prtd %pK\n", prtd);
+
+	mutex_init(&prtd->lock);
+	spin_lock_init(&prtd->dsp_lock);
+	prtd->dsp_cnt = 0;
+
+	mutex_lock(&prtd->lock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = msm_afe_hardware_playback;
+	else
+		runtime->hw = msm_afe_hardware_capture;
+
+	prtd->substream = substream;
+	runtime->private_data = prtd;
+	prtd->audio_client = q6afe_audio_client_alloc(prtd);
+	if (!prtd->audio_client) {
+		pr_debug("%s: Could not allocate memory\n", __func__);
+		mutex_unlock(&prtd->lock);
+		kfree(prtd);
+		return -ENOMEM;
+	}
+
+	atomic_set(&prtd->rec_bytes_avail, 0);
+	init_waitqueue_head(&prtd->read_wait);
+
+	hrtimer_init(&prtd->hrt, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prtd->hrt.function = afe_hrtimer_callback;
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		prtd->hrt.function = afe_hrtimer_rec_callback;
+
+	mutex_unlock(&prtd->lock);
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&constraints_sample_rates);
+	if (ret < 0)
+		pr_err("snd_pcm_hw_constraint_list failed\n");
+	/* Ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		pr_err("snd_pcm_hw_constraint_integer failed\n");
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			MIN_CAPTURE_NUM_PERIODS * MIN_CAPTURE_PERIOD_SIZE,
+			MAX_CAPTURE_NUM_PERIODS * MAX_CAPTURE_PERIOD_SIZE);
+
+		if (ret < 0) {
+			pr_err("constraint for buffer bytes min max ret = %d\n",
+			      ret);
+		}
+	}
+
+	prtd->reset_event = false;
+	return 0;
+}
+
+static int msm_afe_playback_copy(struct snd_pcm_substream *substream,
+				int channel, snd_pcm_uframes_t hwoff,
+				void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+	u32 mem_map_handle = 0;
+
+	pr_debug("%s : appl_ptr 0x%lx hw_ptr 0x%lx dest_to_copy 0x%pK\n",
+		__func__,
+		runtime->control->appl_ptr, runtime->status->hw_ptr, hwbuf);
+
+	if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames))) {
+		pr_err("%s :Failed to copy audio from user buffer\n",
+			__func__);
+
+		ret = -EFAULT;
+		goto fail;
+	}
+
+	if (!prtd->mmap_flag) {
+		mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+		if (!mem_map_handle) {
+			pr_err("%s: mem_map_handle is NULL\n", __func__);
+			ret = -EFAULT;
+			goto fail;
+		}
+
+		pr_debug("%s : prtd-> dma_addr 0x%lx dsp_cnt %d\n", __func__,
+			prtd->dma_addr, prtd->dsp_cnt);
+
+		if (prtd->dsp_cnt == runtime->periods)
+			prtd->dsp_cnt = 0;
+
+		ret = afe_rt_proxy_port_write(
+				(prtd->dma_addr + (prtd->dsp_cnt *
+				snd_pcm_lib_period_bytes(prtd->substream))),
+				mem_map_handle,
+				snd_pcm_lib_period_bytes(prtd->substream));
+
+		if (ret) {
+			pr_err("%s: AFE proxy port write failed %d\n",
+				__func__, ret);
+			goto fail;
+		}
+		prtd->dsp_cnt++;
+	}
+fail:
+	return ret;
+}
+
+static int msm_afe_capture_copy(struct snd_pcm_substream *substream,
+				int channel, snd_pcm_uframes_t hwoff,
+				void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+	char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);
+	u32 mem_map_handle = 0;
+
+	if (!prtd->mmap_flag) {
+		mem_map_handle = afe_req_mmap_handle(prtd->audio_client);
+
+		if (!mem_map_handle) {
+			pr_err("%s: mem_map_handle is NULL\n", __func__);
+			ret = -EFAULT;
+			goto fail;
+		}
+
+		if (prtd->dsp_cnt == runtime->periods)
+			prtd->dsp_cnt = 0;
+
+		ret = afe_rt_proxy_port_read((prtd->dma_addr +
+				(prtd->dsp_cnt *
+				snd_pcm_lib_period_bytes(prtd->substream))),
+				mem_map_handle,
+				snd_pcm_lib_period_bytes(prtd->substream));
+
+		if (ret) {
+			pr_err("%s: AFE proxy port read failed %d\n",
+				__func__, ret);
+			goto fail;
+		}
+
+		prtd->dsp_cnt++;
+		ret = wait_event_timeout(prtd->read_wait,
+				atomic_read(&prtd->rec_bytes_avail), 5 * HZ);
+		if (ret < 0) {
+			pr_err("%s: wait_event_timeout failed\n", __func__);
+
+			ret = -ETIMEDOUT;
+			goto fail;
+		}
+		atomic_set(&prtd->rec_bytes_avail, 0);
+	}
+	pr_debug("%s:appl_ptr 0x%lx hw_ptr 0x%lx src_to_copy 0x%pK\n",
+			__func__, runtime->control->appl_ptr,
+			runtime->status->hw_ptr, hwbuf);
+
+	if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames))) {
+		pr_err("%s: copy to user failed\n", __func__);
+
+		goto fail;
+		ret = -EFAULT;
+	}
+
+fail:
+	return ret;
+}
+
+static int msm_afe_copy(struct snd_pcm_substream *substream, int channel,
+			snd_pcm_uframes_t hwoff, void __user *buf,
+			snd_pcm_uframes_t frames)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+
+	int ret = 0;
+
+	if (prtd->reset_event) {
+		pr_debug("%s: reset events received from ADSP, return error\n",
+			__func__);
+		return -ENETRESET;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_afe_playback_copy(substream, channel, hwoff,
+					buf, frames);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_afe_capture_copy(substream, channel, hwoff,
+					buf, frames);
+	return ret;
+}
+
+static int msm_afe_close(struct snd_pcm_substream *substream)
+{
+	int rc = 0;
+	struct snd_dma_buffer *dma_buf;
+	struct snd_pcm_runtime *runtime;
+	struct pcm_afe_info *prtd;
+	struct snd_soc_pcm_runtime *rtd = NULL;
+	struct snd_soc_dai *dai = NULL;
+	int dir = IN;
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+	if (substream == NULL) {
+		pr_err("substream is NULL\n");
+		return -EINVAL;
+	}
+	rtd = substream->private_data;
+	dai = rtd->cpu_dai;
+	runtime = substream->runtime;
+	prtd = runtime->private_data;
+
+	mutex_lock(&prtd->lock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		dir = IN;
+		ret =  afe_unregister_get_events(dai->id);
+		if (ret < 0)
+			pr_err("AFE unregister for events failed\n");
+	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		dir = OUT;
+		ret =  afe_unregister_get_events(dai->id);
+		if (ret < 0)
+			pr_err("AFE unregister for events failed\n");
+	}
+	if (prtd->mmap_flag)
+		hrtimer_cancel(&prtd->hrt);
+
+	rc = afe_cmd_memory_unmap(afe_req_mmap_handle(prtd->audio_client));
+	if (rc < 0)
+		pr_err("AFE memory unmap failed\n");
+
+	pr_debug("release all buffer\n");
+	dma_buf = &substream->dma_buffer;
+	if (dma_buf == NULL) {
+		pr_debug("dma_buf is NULL\n");
+			goto done;
+	}
+
+	if (dma_buf->area)
+		dma_buf->area = NULL;
+	q6afe_audio_client_buf_free_contiguous(dir, prtd->audio_client);
+done:
+	pr_debug("%s: dai->id =%x\n", __func__, dai->id);
+	q6afe_audio_client_free(prtd->audio_client);
+	mutex_unlock(&prtd->lock);
+	prtd->prepared--;
+	kfree(prtd);
+	return 0;
+}
+static int msm_afe_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+
+	prtd->pcm_irq_pos = 0;
+	if (prtd->prepared)
+		return 0;
+	mutex_lock(&prtd->lock);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_afe_playback_prepare(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_afe_capture_prepare(substream);
+	mutex_unlock(&prtd->lock);
+	return ret;
+}
+static int msm_afe_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+	struct afe_audio_client *ac = prtd->audio_client;
+	struct afe_audio_port_data *apd = ac->port;
+	struct afe_audio_buffer *ab;
+	int dir = -1;
+
+	pr_debug("%s\n", __func__);
+	prtd->mmap_flag = 1;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+	ab = &(apd[dir].buf[0]);
+
+	return msm_audio_ion_mmap((struct audio_buffer *)ab, vma);
+}
+static int msm_afe_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_START\n", __func__);
+		prtd->start = 1;
+		if (prtd->mmap_flag)
+			hrtimer_start(&prtd->hrt, ns_to_ktime(0),
+					HRTIMER_MODE_REL);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+		prtd->start = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+static int msm_afe_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct pcm_afe_info *prtd = runtime->private_data;
+	struct afe_audio_buffer *buf;
+	int dir, rc;
+
+	pr_debug("%s:\n", __func__);
+
+	mutex_lock(&prtd->lock);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+
+	rc = q6afe_audio_client_buf_alloc_contiguous(dir,
+		prtd->audio_client,
+		(params_buffer_bytes(params) / params_periods(params)),
+		params_periods(params));
+	pr_debug("params_buffer_bytes(params) = %d\n",
+			(params_buffer_bytes(params)));
+	pr_debug("params_periods(params) = %d\n",
+			(params_periods(params)));
+	pr_debug("params_periodsize(params) = %d\n",
+		(params_buffer_bytes(params) / params_periods(params)));
+
+	if (rc < 0) {
+		pr_err("Audio Start: Buffer Allocation failed rc = %d\n", rc);
+		mutex_unlock(&prtd->lock);
+		return -ENOMEM;
+	}
+	buf = prtd->audio_client->port[dir].buf;
+
+	if (buf == NULL || buf[0].data == NULL) {
+		mutex_unlock(&prtd->lock);
+		return -ENOMEM;
+	}
+
+	pr_debug("%s:buf = %pK\n", __func__, buf);
+	dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dma_buf->dev.dev = substream->pcm->card->dev;
+	dma_buf->private_data = NULL;
+	dma_buf->area = buf[0].data;
+	dma_buf->addr = buf[0].phys;
+
+	dma_buf->bytes = params_buffer_bytes(params);
+
+	if (!dma_buf->area) {
+		pr_err("%s:MSM AFE physical memory allocation failed\n",
+							__func__);
+		mutex_unlock(&prtd->lock);
+		return -ENOMEM;
+	}
+
+	memset(dma_buf->area, 0,  params_buffer_bytes(params));
+
+	prtd->dma_addr = (phys_addr_t) dma_buf->addr;
+
+	mutex_unlock(&prtd->lock);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	rc = afe_memory_map(dma_buf->addr, dma_buf->bytes, prtd->audio_client);
+	if (rc < 0)
+		pr_err("fail to map memory to DSP\n");
+
+	return rc;
+}
+static snd_pcm_uframes_t msm_afe_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct pcm_afe_info *prtd = runtime->private_data;
+
+	if (prtd->pcm_irq_pos >= snd_pcm_lib_buffer_bytes(substream))
+		prtd->pcm_irq_pos = 0;
+
+	if (prtd->reset_event) {
+		pr_debug("%s: reset events received from ADSP, return XRUN\n",
+			__func__);
+		return SNDRV_PCM_POS_XRUN;
+	}
+
+	pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+	return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static const struct snd_pcm_ops msm_afe_ops = {
+	.open           = msm_afe_open,
+	.copy           = msm_afe_copy,
+	.hw_params	= msm_afe_hw_params,
+	.trigger	= msm_afe_trigger,
+	.close          = msm_afe_close,
+	.prepare        = msm_afe_prepare,
+	.mmap		= msm_afe_mmap,
+	.pointer	= msm_afe_pointer,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	return ret;
+}
+
+static int msm_afe_afe_probe(struct snd_soc_platform *platform)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_afe_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.probe		= msm_afe_afe_probe,
+};
+
+static int msm_afe_probe(struct platform_device *pdev)
+{
+
+	pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+	return snd_soc_register_platform(&pdev->dev,
+				   &msm_soc_platform);
+}
+
+static int msm_afe_remove(struct platform_device *pdev)
+{
+	pr_debug("%s\n", __func__);
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+static const struct of_device_id msm_pcm_afe_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-afe"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_afe_dt_match);
+
+static struct platform_driver msm_afe_driver = {
+	.driver = {
+		.name = "msm-pcm-afe",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_afe_dt_match,
+	},
+	.probe = msm_afe_probe,
+	.remove = msm_afe_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	pr_debug("%s\n", __func__);
+	return platform_driver_register(&msm_afe_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	pr_debug("%s\n", __func__);
+	platform_driver_unregister(&msm_afe_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("AFE PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
new file mode 100644
index 0000000..84a4c3c
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-afe-v2.h
@@ -0,0 +1,49 @@
+/* Copyright (c) 2012,2015-2016 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_AFE_H
+#define _MSM_PCM_AFE_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+
+
+struct pcm_afe_info {
+	unsigned long dma_addr;
+	struct snd_pcm_substream *substream;
+	unsigned int pcm_irq_pos;       /* IRQ position */
+	struct mutex lock;
+	spinlock_t dsp_lock;
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint8_t start;
+	uint32_t dsp_cnt;
+	uint32_t buf_phys;
+	int32_t mmap_flag;
+	int prepared;
+	struct hrtimer hrt;
+	int poll_time;
+	struct afe_audio_client *audio_client;
+	wait_queue_head_t read_wait;
+	atomic_t rec_bytes_avail;
+	bool reset_event;
+};
+
+
+#define MSM_EXT(xname, fp_info, fp_get, fp_put, addr) \
+	{.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+	.access = SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+	.name = xname, \
+	.info = fp_info,\
+	.get = fp_get, .put = fp_put, \
+	.private_value = addr, \
+	}
+
+#endif /*_MSM_PCM_AFE_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c
new file mode 100644
index 0000000..f4e03fe
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-dtmf-v2.c
@@ -0,0 +1,596 @@
+/* Copyright (c) 2013-2014, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/pcm.h>
+#include <sound/q6afe-v2.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+#include "q6voice.h"
+
+enum {
+	DTMF_IN_RX,
+	DTMF_IN_TX,
+};
+
+enum format {
+	FORMAT_S16_LE = 2
+};
+
+struct dtmf_det_info {
+	char     session[MAX_SESSION_NAME_LEN];
+	uint8_t  dir;
+	uint16_t high_freq;
+	uint16_t low_freq;
+};
+
+struct dtmf_buf_node {
+	struct list_head list;
+	struct dtmf_det_info dtmf_det_pkt;
+};
+
+enum dtmf_state {
+	DTMF_GEN_RX_STOPPED,
+	DTMF_GEN_RX_STARTED,
+};
+
+#define DTMF_MAX_Q_LEN 10
+#define DTMF_PKT_SIZE sizeof(struct dtmf_det_info)
+
+struct dtmf_drv_info {
+	enum  dtmf_state state;
+	struct snd_pcm_substream *capture_substream;
+
+	struct list_head out_queue;
+	struct list_head free_out_queue;
+
+	wait_queue_head_t out_wait;
+
+	struct mutex lock;
+	spinlock_t dsp_lock;
+
+	uint8_t capture_start;
+	uint8_t capture_instance;
+
+	unsigned int pcm_capture_size;
+	unsigned int pcm_capture_count;
+	unsigned int pcm_capture_irq_pos;
+	unsigned int pcm_capture_buf_pos;
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				 SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				 SNDRV_PCM_INFO_MMAP_VALID |
+				 SNDRV_PCM_INFO_INTERLEAVED),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE,
+	.channels_min =         1,
+	.channels_max =         1,
+	.buffer_bytes_max =	(sizeof(struct dtmf_buf_node) * DTMF_MAX_Q_LEN),
+	.period_bytes_min =	DTMF_PKT_SIZE,
+	.period_bytes_max =	DTMF_PKT_SIZE,
+	.periods_min =		DTMF_MAX_Q_LEN,
+	.periods_max =		DTMF_MAX_Q_LEN,
+	.fifo_size =            0,
+};
+
+static int msm_dtmf_rx_generate_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	uint16_t low_freq = ucontrol->value.integer.value[0];
+	uint16_t high_freq = ucontrol->value.integer.value[1];
+	int64_t duration = ucontrol->value.integer.value[2];
+	uint16_t gain = ucontrol->value.integer.value[3];
+
+	pr_debug("%s: low_freq=%d high_freq=%d duration=%d gain=%d\n",
+		 __func__, low_freq, high_freq, (int)duration, gain);
+	afe_dtmf_generate_rx(duration, high_freq, low_freq, gain);
+	return 0;
+}
+
+static int msm_dtmf_rx_generate_get(struct  snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s:\n", __func__);
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: enable=%d\n", __func__, enable);
+	voc_enable_dtmf_rx_detection(voc_get_session_id(VOICE_SESSION_NAME),
+				     enable);
+
+	return 0;
+}
+
+static int msm_dtmf_detect_voice_rx_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int enable = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: enable=%d\n", __func__, enable);
+	voc_enable_dtmf_rx_detection(voc_get_session_id(VOLTE_SESSION_NAME),
+				     enable);
+
+	return 0;
+}
+
+static int msm_dtmf_detect_volte_rx_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = 0;
+	return 0;
+}
+
+static struct snd_kcontrol_new msm_dtmf_controls[] = {
+	SOC_SINGLE_MULTI_EXT("DTMF_Generate Rx Low High Duration Gain",
+			     SND_SOC_NOPM, 0, 5000, 0, 4,
+			     msm_dtmf_rx_generate_get,
+			     msm_dtmf_rx_generate_put),
+	SOC_SINGLE_EXT("DTMF_Detect Rx Voice enable", SND_SOC_NOPM, 0, 1, 0,
+				msm_dtmf_detect_voice_rx_get,
+				msm_dtmf_detect_voice_rx_put),
+	SOC_SINGLE_EXT("DTMF_Detect Rx VoLTE enable", SND_SOC_NOPM, 0, 1, 0,
+				msm_dtmf_detect_volte_rx_get,
+				msm_dtmf_detect_volte_rx_put),
+};
+
+static int msm_pcm_dtmf_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, msm_dtmf_controls,
+				      ARRAY_SIZE(msm_dtmf_controls));
+	return 0;
+}
+
+static void dtmf_rx_detected_cb(uint8_t *pkt,
+				char *session,
+				void *private_data)
+{
+	struct dtmf_buf_node *buf_node = NULL;
+	struct vss_istream_evt_rx_dtmf_detected *dtmf_det_pkt =
+		(struct vss_istream_evt_rx_dtmf_detected *)pkt;
+	struct dtmf_drv_info *prtd = private_data;
+	unsigned long dsp_flags;
+
+	pr_debug("%s\n", __func__);
+	if (prtd->capture_substream == NULL)
+		return;
+
+	/* Copy dtmf detected info into out_queue. */
+	spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+	/* discarding dtmf detection info till start is received */
+	if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+		buf_node = list_first_entry(&prtd->free_out_queue,
+					    struct dtmf_buf_node, list);
+		list_del(&buf_node->list);
+		buf_node->dtmf_det_pkt.high_freq = dtmf_det_pkt->high_freq;
+		buf_node->dtmf_det_pkt.low_freq = dtmf_det_pkt->low_freq;
+		if (session != NULL)
+			strlcpy(buf_node->dtmf_det_pkt.session,
+				session, MAX_SESSION_NAME_LEN);
+
+		buf_node->dtmf_det_pkt.dir = DTMF_IN_RX;
+		pr_debug("high =%d, low=%d session=%s\n",
+			 buf_node->dtmf_det_pkt.high_freq,
+			 buf_node->dtmf_det_pkt.low_freq,
+			 buf_node->dtmf_det_pkt.session);
+		list_add_tail(&buf_node->list, &prtd->out_queue);
+		prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+		spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		snd_pcm_period_elapsed(prtd->capture_substream);
+	} else {
+		spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		pr_err("DTMF detection pkt in Rx  dropped, no free node available\n");
+	}
+
+	wake_up(&prtd->out_wait);
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+				int channel, snd_pcm_uframes_t hwoff,
+				void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	int count = 0;
+	struct dtmf_buf_node *buf_node = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+	unsigned long dsp_flags;
+
+	count = frames_to_bytes(runtime, frames);
+
+	ret = wait_event_interruptible_timeout(prtd->out_wait,
+				(!list_empty(&prtd->out_queue)),
+				1 * HZ);
+
+	if (ret > 0) {
+		if (count <= DTMF_PKT_SIZE) {
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			buf_node = list_first_entry(&prtd->out_queue,
+					struct dtmf_buf_node, list);
+			list_del(&buf_node->list);
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+			ret = copy_to_user(buf,
+					   &buf_node->dtmf_det_pkt,
+					   count);
+			if (ret) {
+				pr_err("%s: Copy to user returned %d\n",
+					__func__, ret);
+				ret = -EFAULT;
+			}
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			list_add_tail(&buf_node->list,
+				      &prtd->free_out_queue);
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+
+		} else {
+			pr_err("%s: Read count %d > DTMF_PKT_SIZE\n",
+				__func__, count);
+			ret = -ENOMEM;
+		}
+	} else if (ret == 0) {
+		pr_err("%s: No UL data available\n", __func__);
+		ret = -ETIMEDOUT;
+	} else {
+		pr_err("%s: Read was interrupted\n", __func__);
+		ret = -ERESTARTSYS;
+	}
+	return ret;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+	 snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+
+	pr_debug("%s() DTMF\n", __func__);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+	return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = NULL;
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		prtd = kzalloc(sizeof(struct dtmf_drv_info), GFP_KERNEL);
+
+		if (prtd == NULL) {
+			ret = -ENOMEM;
+			goto done;
+		}
+
+		mutex_init(&prtd->lock);
+		spin_lock_init(&prtd->dsp_lock);
+		init_waitqueue_head(&prtd->out_wait);
+		INIT_LIST_HEAD(&prtd->out_queue);
+		INIT_LIST_HEAD(&prtd->free_out_queue);
+
+		runtime->hw = msm_pcm_hardware;
+
+		ret = snd_pcm_hw_constraint_integer(runtime,
+						    SNDRV_PCM_HW_PARAM_PERIODS);
+		if (ret < 0)
+			pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+		prtd->capture_substream = substream;
+		prtd->capture_instance++;
+		runtime->private_data = prtd;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct list_head *ptr = NULL;
+	struct list_head *next = NULL;
+	struct dtmf_buf_node *buf_node = NULL;
+	struct snd_dma_buffer *c_dma_buf;
+	struct snd_pcm_substream *c_substream;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+	unsigned long dsp_flags;
+
+	pr_debug("%s() DTMF\n", __func__);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mutex_lock(&prtd->lock);
+		wake_up(&prtd->out_wait);
+
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_instance--;
+
+		if (!prtd->capture_instance) {
+			if (prtd->state == DTMF_GEN_RX_STARTED) {
+				prtd->state = DTMF_GEN_RX_STOPPED;
+				voc_disable_dtmf_det_on_active_sessions();
+				voc_register_dtmf_rx_detection_cb(NULL, NULL);
+			}
+			/* release all buffer */
+			/* release out_queue and free_out_queue */
+			pr_debug("release all buffer\n");
+			c_substream = prtd->capture_substream;
+			if (c_substream == NULL) {
+				pr_debug("c_substream is NULL\n");
+				mutex_unlock(&prtd->lock);
+				return -EINVAL;
+			}
+
+			c_dma_buf = &c_substream->dma_buffer;
+			if (c_dma_buf == NULL) {
+				pr_debug("c_dma_buf is NULL.\n");
+				mutex_unlock(&prtd->lock);
+				return -EINVAL;
+			}
+
+			if (c_dma_buf->area != NULL) {
+				spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+				list_for_each_safe(ptr, next,
+							&prtd->out_queue) {
+					buf_node = list_entry(ptr,
+						   struct dtmf_buf_node, list);
+					list_del(&buf_node->list);
+				}
+
+				list_for_each_safe(ptr, next,
+						   &prtd->free_out_queue) {
+					buf_node = list_entry(ptr,
+						   struct dtmf_buf_node, list);
+					list_del(&buf_node->list);
+				}
+
+				spin_unlock_irqrestore(&prtd->dsp_lock,
+						       dsp_flags);
+				dma_free_coherent(c_substream->pcm->card->dev,
+						  runtime->hw.buffer_bytes_max,
+						  c_dma_buf->area,
+						  c_dma_buf->addr);
+				c_dma_buf->area = NULL;
+			}
+		}
+		prtd->capture_substream = NULL;
+		mutex_unlock(&prtd->lock);
+	}
+
+	return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct dtmf_buf_node *buf_node = NULL;
+	int i = 0, offset = 0;
+	int ret = 0;
+
+	pr_debug("%s: DTMF\n", __func__);
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mutex_lock(&prtd->lock);
+		dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+		dma_buf->dev.dev = substream->pcm->card->dev;
+		dma_buf->private_data = NULL;
+
+		dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+						runtime->hw.buffer_bytes_max,
+						&dma_buf->addr, GFP_KERNEL);
+		if (!dma_buf->area) {
+			pr_err("%s:MSM DTMF dma_alloc failed\n", __func__);
+			mutex_unlock(&prtd->lock);
+			return -ENOMEM;
+		}
+
+		dma_buf->bytes = runtime->hw.buffer_bytes_max;
+		memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+		for (i = 0; i < DTMF_MAX_Q_LEN; i++) {
+			pr_debug("node =%d\n", i);
+			buf_node = (void *) dma_buf->area + offset;
+			list_add_tail(&buf_node->list,
+				      &prtd->free_out_queue);
+			offset = offset + sizeof(struct dtmf_buf_node);
+		}
+
+		snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+		mutex_unlock(&prtd->lock);
+	}
+
+	return ret;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	pr_debug("%s: DTMF\n", __func__);
+	prtd->pcm_capture_size  = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_capture_irq_pos = 0;
+	prtd->pcm_capture_buf_pos = 0;
+	return 0;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	pr_debug("%s: DTMF\n", __func__);
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		mutex_lock(&prtd->lock);
+
+		msm_pcm_capture_prepare(substream);
+
+		if (runtime->format != FORMAT_S16_LE) {
+			pr_err("format:%u doesn't match %d\n",
+			       (uint32_t)runtime->format, FORMAT_S16_LE);
+			mutex_unlock(&prtd->lock);
+			return -EINVAL;
+		}
+
+		if (prtd->capture_instance &&
+			(prtd->state != DTMF_GEN_RX_STARTED)) {
+			voc_register_dtmf_rx_detection_cb(dtmf_rx_detected_cb,
+							  prtd);
+			prtd->state = DTMF_GEN_RX_STARTED;
+		}
+		mutex_unlock(&prtd->lock);
+	}
+
+	return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		pr_debug("%s: Trigger start\n", __func__);
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_start = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_start = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	snd_pcm_uframes_t ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct dtmf_drv_info *prtd = runtime->private_data;
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+			prtd->pcm_capture_irq_pos = 0;
+		ret = bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+	}
+
+	return ret;
+}
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open           = msm_pcm_open,
+	.copy		= msm_pcm_copy,
+	.hw_params	= msm_pcm_hw_params,
+	.close          = msm_pcm_close,
+	.prepare        = msm_pcm_prepare,
+	.trigger        = msm_pcm_trigger,
+	.pointer        = msm_pcm_pointer,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.probe		= msm_pcm_dtmf_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+	pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+
+	return snd_soc_register_platform(&pdev->dev,
+					 &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_pcm_dtmf_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-dtmf"},
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, msm_pcm_dtmf_dt_match);
+
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-pcm-dtmf",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_dtmf_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("DTMF platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c
new file mode 100644
index 0000000..2f60db9
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-host-voice-v2.c
@@ -0,0 +1,1553 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/msm_audio_ion.h>
+#include "q6voice.h"
+
+#define HPCM_MAX_Q_LEN 2
+#define HPCM_MIN_VOC_PKT_SIZE 320
+#define HPCM_MAX_VOC_PKT_SIZE 640
+#define VHPCM_BLOCK_SIZE 4096
+#define CACHE_ALIGNMENT_SIZE 128
+#define CACHE_ALIGNMENT_MASK 0xFFFFFF80
+
+#define VOICE_TX_CAPTURE_DAI_ID  "CS-VOICE HOST TX CAPTURE"
+#define VOICE_TX_PLAYBACK_DAI_ID "CS-VOICE HOST TX PLAYBACK"
+#define VOICE_RX_CAPTURE_DAI_ID  "CS-VOICE HOST RX CAPTURE"
+#define VOICE_RX_PLAYBACK_DAI_ID "CS-VOICE HOST RX PLAYBACK"
+
+#define VOLTE_TX_CAPTURE_DAI_ID  "VOLTE HOST TX CAPTURE"
+#define VOLTE_TX_PLAYBACK_DAI_ID "VOLTE HOST TX PLAYBACK"
+#define VOLTE_RX_CAPTURE_DAI_ID  "VOLTE HOST RX CAPTURE"
+#define VOLTE_RX_PLAYBACK_DAI_ID "VOLTE HOST RX PLAYBACK"
+
+
+#define VoMMode1_TX_CAPTURE_DAI_ID  "VoiceMMode1 HOST TX CAPTURE"
+#define VoMMode1_TX_PLAYBACK_DAI_ID "VoiceMMode1 HOST TX PLAYBACK"
+#define VoMMode1_RX_CAPTURE_DAI_ID  "VoiceMMode1 HOST RX CAPTURE"
+#define VoMMode1_RX_PLAYBACK_DAI_ID "VoiceMMode1 HOST RX PLAYBACK"
+
+#define VoMMode2_TX_CAPTURE_DAI_ID  "VoiceMMode2 HOST TX CAPTURE"
+#define VoMMode2_TX_PLAYBACK_DAI_ID "VoiceMMode2 HOST TX PLAYBACK"
+#define VoMMode2_RX_CAPTURE_DAI_ID  "VoiceMMode2 HOST RX CAPTURE"
+#define VoMMode2_RX_PLAYBACK_DAI_ID "VoiceMMode2 HOST RX PLAYBACK"
+
+enum {
+	RX = 1,
+	TX,
+};
+
+enum {
+	VOICE_INDEX = 0,
+	VOLTE_INDEX,
+	VOMMODE1_INDEX,
+	VOMMODE2_INDEX,
+	MAX_SESSION
+};
+
+enum hpcm_state {
+	HPCM_STOPPED = 1,
+	HPCM_CLOSED,
+	HPCM_PREPARED,
+	HPCM_STARTED,
+};
+
+struct hpcm_frame {
+	uint32_t len;
+	uint8_t voc_pkt[HPCM_MAX_VOC_PKT_SIZE];
+};
+
+struct hpcm_buf_node {
+	struct list_head list;
+	struct hpcm_frame frame;
+};
+
+struct vocpcm_ion_buffer {
+	/* Physical address */
+	phys_addr_t paddr;
+	/* Kernel virtual address */
+	void *kvaddr;
+};
+
+struct dai_data {
+	enum  hpcm_state state;
+	struct snd_pcm_substream *substream;
+	struct list_head filled_queue;
+	struct list_head free_queue;
+	wait_queue_head_t queue_wait;
+	spinlock_t dsp_lock;
+	uint32_t pcm_size;
+	uint32_t pcm_count;
+	/* IRQ position */
+	uint32_t pcm_irq_pos;
+	/* Position in buffer */
+	uint32_t pcm_buf_pos;
+	struct vocpcm_ion_buffer vocpcm_ion_buffer;
+};
+
+struct tap_point {
+	struct dai_data playback_dai_data;
+	struct dai_data capture_dai_data;
+};
+
+struct session {
+	struct tap_point tx_tap_point;
+	struct tap_point rx_tap_point;
+	phys_addr_t sess_paddr;
+	void *sess_kvaddr;
+	struct ion_handle *ion_handle;
+	struct mem_map_table tp_mem_table;
+};
+
+struct tappnt_mxr_data {
+	bool enable;
+	uint16_t direction;
+	uint16_t sample_rate;
+};
+
+/* Values from mixer ctl are cached in this structure */
+struct mixer_conf {
+	int8_t sess_indx;
+	struct tappnt_mxr_data rx;
+	struct tappnt_mxr_data tx;
+};
+
+struct start_cmd {
+	struct vss_ivpcm_tap_point tap_pnt[2];
+	uint32_t no_of_tapoints;
+};
+
+struct hpcm_drv {
+	struct mutex lock;
+	struct session session[MAX_SESSION];
+	struct mixer_conf mixer_conf;
+	struct ion_client *ion_client;
+	struct start_cmd start_cmd;
+};
+
+static struct hpcm_drv hpcm_drv;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_SPECIAL,
+	.rates =                SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+	.rate_min =             8000,
+	.rate_max =             16000,
+	.channels_min =         1,
+	.channels_max =         1,
+	.buffer_bytes_max =	sizeof(struct hpcm_buf_node) * HPCM_MAX_Q_LEN,
+	.period_bytes_min =	HPCM_MIN_VOC_PKT_SIZE,
+	.period_bytes_max =	HPCM_MAX_VOC_PKT_SIZE,
+	.periods_min =		HPCM_MAX_Q_LEN,
+	.periods_max =		HPCM_MAX_Q_LEN,
+	.fifo_size =            0,
+};
+
+static char *hpcm_get_sess_name(int sess_indx)
+{
+	char *sess_name = NULL;
+
+	if (sess_indx == VOICE_INDEX)
+		sess_name = VOICE_SESSION_NAME;
+	else if (sess_indx == VOLTE_INDEX)
+		sess_name = VOLTE_SESSION_NAME;
+	else if (sess_indx == VOMMODE1_INDEX)
+		sess_name = VOICEMMODE1_NAME;
+	else if (sess_indx == VOMMODE2_INDEX)
+		sess_name = VOICEMMODE2_NAME;
+	else
+		pr_err("%s:, Invalid sess_index\n", __func__);
+
+	return sess_name;
+}
+
+static void hpcm_reset_mixer_config(struct hpcm_drv *prtd)
+{
+	prtd->mixer_conf.sess_indx = -1;
+	prtd->mixer_conf.rx.enable = false;
+	prtd->mixer_conf.rx.direction = -1;
+	prtd->mixer_conf.rx.sample_rate = 0;
+
+	prtd->mixer_conf.tx.enable = false;
+	prtd->mixer_conf.tx.direction = -1;
+	prtd->mixer_conf.tx.sample_rate = 0;
+}
+
+/* Check for valid mixer control values */
+static bool hpcm_is_valid_config(int sess_indx, int tap_point,
+				 uint16_t direction, uint16_t samplerate)
+{
+	if (sess_indx < VOICE_INDEX || sess_indx > VOMMODE2_INDEX) {
+		pr_err("%s: invalid sess_indx :%d\n", __func__, sess_indx);
+		goto error;
+	}
+
+	if (samplerate != VSS_IVPCM_SAMPLING_RATE_8K &&
+	    samplerate != VSS_IVPCM_SAMPLING_RATE_16K) {
+		pr_err("%s: invalid sample rate :%d\n", __func__, samplerate);
+		goto error;
+	}
+
+	if ((tap_point != RX) && (tap_point != TX)) {
+		pr_err("%s: invalid tappoint :%d\n", __func__, tap_point);
+		goto error;
+	}
+
+	if ((direction != VSS_IVPCM_TAP_POINT_DIR_IN) &&
+	    (direction != VSS_IVPCM_TAP_POINT_DIR_OUT) &&
+	    (direction != VSS_IVPCM_TAP_POINT_DIR_OUT_IN)) {
+		pr_err("%s: invalid direction :%d\n", __func__, direction);
+		goto error;
+	}
+
+	return true;
+
+error:
+	return false;
+}
+
+
+static struct dai_data *hpcm_get_dai_data(char *pcm_id, struct hpcm_drv *prtd)
+{
+	struct dai_data *dai_data = NULL;
+	size_t size = 0;
+
+	if (pcm_id) {
+		size = strlen(pcm_id);
+		/* Check for Voice DAI */
+		if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOICE_INDEX].tx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOICE_INDEX].tx_tap_point.playback_dai_data;
+		} else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOICE_INDEX].rx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOICE_INDEX].rx_tap_point.playback_dai_data;
+		/* Check for VoLTE DAI */
+		} else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOLTE_INDEX].tx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOLTE_INDEX].tx_tap_point.playback_dai_data;
+		} else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOLTE_INDEX].rx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOLTE_INDEX].rx_tap_point.playback_dai_data;
+		/* check for VoiceMMode1 DAI */
+		} else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE1_INDEX].tx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE1_INDEX].tx_tap_point.playback_dai_data;
+		} else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE1_INDEX].rx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE1_INDEX].rx_tap_point.playback_dai_data;
+		/* check for VOiceMMode2 DAI */
+		} else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE2_INDEX].tx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE2_INDEX].tx_tap_point.playback_dai_data;
+		} else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE2_INDEX].rx_tap_point.capture_dai_data;
+		} else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) {
+			dai_data =
+		&prtd->session[VOMMODE2_INDEX].rx_tap_point.playback_dai_data;
+
+		} else {
+			pr_err("%s: Wrong dai id\n", __func__);
+		}
+	}
+
+	return dai_data;
+}
+
+static struct tap_point *hpcm_get_tappoint_data(char *pcm_id,
+						struct hpcm_drv *prtd)
+{
+	struct tap_point *tp = NULL;
+	size_t size = 0;
+
+	if (pcm_id) {
+		size = strlen(pcm_id);
+		/* Check for Voice DAI */
+		if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOICE_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOICE_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VOICE_RX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOICE_INDEX].rx_tap_point;
+		} else if (strnstr(pcm_id, VOICE_RX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOICE_INDEX].rx_tap_point;
+		/* Check for VoLTE DAI */
+		} else if (strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOLTE_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOLTE_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VOLTE_RX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOLTE_INDEX].rx_tap_point;
+		} else if (strnstr(pcm_id, VOLTE_RX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOLTE_INDEX].rx_tap_point;
+		/* check for VoiceMMode1 */
+		} else if (strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE1_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VoMMode1_RX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point;
+		} else if (strnstr(pcm_id, VoMMode1_RX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE1_INDEX].rx_tap_point;
+		/* check for VoiceMMode2 */
+		} else if (strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE2_INDEX].tx_tap_point;
+		} else if (strnstr(pcm_id, VoMMode2_RX_CAPTURE_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point;
+		} else if (strnstr(pcm_id, VoMMode2_RX_PLAYBACK_DAI_ID, size)) {
+			tp = &prtd->session[VOMMODE2_INDEX].rx_tap_point;
+		} else {
+			pr_err("%s: wrong dai id\n", __func__);
+		}
+	}
+
+	return tp;
+}
+
+static struct tappnt_mxr_data *hpcm_get_tappnt_mixer_data(char *pcm_id,
+						struct hpcm_drv *prtd)
+{
+
+	if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) {
+		return &prtd->mixer_conf.tx;
+	} else {
+		return &prtd->mixer_conf.rx;
+	}
+}
+
+static int get_tappnt_value(char *pcm_id)
+{
+
+	if (strnstr(pcm_id, VOICE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VOICE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VOLTE_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VOLTE_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode1_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode1_TX_PLAYBACK_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode2_TX_CAPTURE_DAI_ID, strlen(pcm_id)) ||
+	    strnstr(pcm_id, VoMMode2_TX_PLAYBACK_DAI_ID, strlen(pcm_id))) {
+		return TX;
+	} else {
+		return RX;
+	}
+}
+
+static bool hpcm_all_dais_are_ready(uint16_t direction, struct tap_point *tp,
+				    enum hpcm_state state)
+{
+	bool dais_started = false;
+
+	/*
+	 * Based on the direction set per tap point in the mixer control,
+	 * all the dais per tap point should meet the required state for the
+	 * commands such as vpcm_map_memory/vpcm_start to be executed.
+	 */
+	switch (direction) {
+	case VSS_IVPCM_TAP_POINT_DIR_OUT_IN:
+		if ((tp->playback_dai_data.state >= state) &&
+		    (tp->capture_dai_data.state >= state)) {
+			dais_started = true;
+		}
+		break;
+
+	case VSS_IVPCM_TAP_POINT_DIR_IN:
+		if (tp->playback_dai_data.state >= state)
+			dais_started = true;
+		break;
+
+	case VSS_IVPCM_TAP_POINT_DIR_OUT:
+		if (tp->capture_dai_data.state >= state)
+			dais_started = true;
+		break;
+
+	default:
+		pr_err("invalid direction\n");
+	}
+
+	return dais_started;
+}
+
+static void hpcm_create_free_queue(struct snd_dma_buffer *dma_buf,
+				   struct dai_data *dai_data)
+{
+	struct hpcm_buf_node *buf_node = NULL;
+	int i = 0, offset = 0;
+
+	for (i = 0; i < HPCM_MAX_Q_LEN; i++) {
+		buf_node = (void *)dma_buf->area + offset;
+		list_add_tail(&buf_node->list,
+			      &dai_data->free_queue);
+		offset = offset + sizeof(struct hpcm_buf_node);
+	}
+}
+
+static void hpcm_free_allocated_mem(struct hpcm_drv *prtd)
+{
+	phys_addr_t paddr = 0;
+	struct tap_point *txtp = NULL;
+	struct tap_point *rxtp = NULL;
+	struct session *sess = NULL;
+
+	sess = &prtd->session[prtd->mixer_conf.sess_indx];
+	txtp = &sess->tx_tap_point;
+	rxtp = &sess->rx_tap_point;
+	paddr = sess->sess_paddr;
+
+	if (paddr) {
+		msm_audio_ion_free(prtd->ion_client, sess->ion_handle);
+		prtd->ion_client = NULL;
+		sess->ion_handle = NULL;
+		msm_audio_ion_free(sess->tp_mem_table.client,
+				   sess->tp_mem_table.handle);
+		sess->tp_mem_table.client = NULL;
+		sess->tp_mem_table.handle = NULL;
+		sess->sess_paddr = 0;
+		sess->sess_kvaddr = 0;
+		sess->ion_handle = 0;
+		prtd->ion_client = 0;
+		sess->tp_mem_table.client = 0;
+		sess->tp_mem_table.handle = 0;
+
+		txtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0;
+		txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+
+		txtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0;
+		txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+
+		rxtp->capture_dai_data.vocpcm_ion_buffer.paddr = 0;
+		rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+
+		rxtp->playback_dai_data.vocpcm_ion_buffer.paddr = 0;
+		rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr = 0;
+	} else {
+		pr_debug("%s, paddr = 0, nothing to free\n", __func__);
+	}
+}
+
+static void hpcm_unmap_and_free_shared_memory(struct hpcm_drv *prtd)
+
+{
+	phys_addr_t paddr = 0;
+	char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+
+	if (prtd->mixer_conf.sess_indx >= 0)
+		paddr = prtd->session[prtd->mixer_conf.sess_indx].sess_paddr;
+	else
+		paddr = 0;
+
+	if (paddr) {
+		voc_send_cvp_unmap_vocpcm_memory(voc_get_session_id(sess_name));
+		hpcm_free_allocated_mem(prtd);
+	} else {
+		pr_debug("%s, paddr = 0, nothing to unmap/free\n", __func__);
+	}
+}
+
+static int hpcm_map_vocpcm_memory(struct hpcm_drv *prtd)
+{
+	int ret = 0;
+	char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+	struct session *sess = NULL;
+
+	sess = &prtd->session[prtd->mixer_conf.sess_indx];
+
+	ret = voc_send_cvp_map_vocpcm_memory(voc_get_session_id(sess_name),
+					     &sess->tp_mem_table,
+					     sess->sess_paddr,
+					     VHPCM_BLOCK_SIZE);
+
+	return ret;
+}
+
+static int hpcm_allocate_shared_memory(struct hpcm_drv *prtd)
+{
+	int result;
+	int ret = 0;
+	size_t mem_len;
+	size_t len;
+	struct tap_point *txtp = NULL;
+	struct tap_point *rxtp = NULL;
+	struct session *sess = NULL;
+
+	sess = &prtd->session[prtd->mixer_conf.sess_indx];
+	txtp = &sess->tx_tap_point;
+	rxtp = &sess->rx_tap_point;
+
+	result = msm_audio_ion_alloc("host_pcm_buffer",
+				     &prtd->ion_client,
+				     &sess->ion_handle,
+				     VHPCM_BLOCK_SIZE,
+				     &sess->sess_paddr,
+				     &mem_len,
+				     &sess->sess_kvaddr);
+	if (result) {
+		pr_err("%s: msm_audio_ion_alloc error, rc = %d\n",
+			__func__, result);
+		sess->sess_paddr = 0;
+		sess->sess_kvaddr = 0;
+		ret = -ENOMEM;
+		goto done;
+	}
+	pr_debug("%s: Host PCM memory block allocated\n", __func__);
+
+	/* Allocate mem_map_table for tap point */
+	result = msm_audio_ion_alloc("host_pcm_table",
+			&sess->tp_mem_table.client,
+			&sess->tp_mem_table.handle,
+			sizeof(struct vss_imemory_table_t),
+			&sess->tp_mem_table.phys,
+			&len,
+			&sess->tp_mem_table.data);
+
+	if (result) {
+		pr_err("%s: msm_audio_ion_alloc error, rc = %d\n",
+			__func__, result);
+		msm_audio_ion_free(prtd->ion_client, sess->ion_handle);
+		prtd->ion_client = NULL;
+		sess->ion_handle = NULL;
+		sess->sess_paddr = 0;
+		sess->sess_kvaddr = 0;
+		ret = -ENOMEM;
+		goto done;
+	}
+	pr_debug("%s:  Host PCM memory table allocated\n", __func__);
+
+	memset(sess->tp_mem_table.data, 0,
+	       sizeof(struct vss_imemory_table_t));
+
+	sess->tp_mem_table.size = sizeof(struct vss_imemory_table_t);
+
+	pr_debug("%s: data %pK phys %pK\n", __func__,
+		 sess->tp_mem_table.data, &sess->tp_mem_table.phys);
+
+	/* Split 4096 block into four 1024 byte blocks for each dai */
+	txtp->capture_dai_data.vocpcm_ion_buffer.paddr =
+	sess->sess_paddr;
+	txtp->capture_dai_data.vocpcm_ion_buffer.kvaddr =
+	sess->sess_kvaddr;
+
+	txtp->playback_dai_data.vocpcm_ion_buffer.paddr =
+	sess->sess_paddr + VHPCM_BLOCK_SIZE/4;
+	txtp->playback_dai_data.vocpcm_ion_buffer.kvaddr =
+	sess->sess_kvaddr + VHPCM_BLOCK_SIZE/4;
+
+	rxtp->capture_dai_data.vocpcm_ion_buffer.paddr =
+	sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 2;
+	rxtp->capture_dai_data.vocpcm_ion_buffer.kvaddr =
+	sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 2;
+
+	rxtp->playback_dai_data.vocpcm_ion_buffer.paddr =
+	sess->sess_paddr + (VHPCM_BLOCK_SIZE/4) * 3;
+	rxtp->playback_dai_data.vocpcm_ion_buffer.kvaddr =
+	sess->sess_kvaddr + (VHPCM_BLOCK_SIZE/4) * 3;
+
+done:
+	return ret;
+}
+
+static int hpcm_start_vocpcm(char *pcm_id, struct hpcm_drv *prtd,
+			     struct tap_point *tp)
+{
+	int indx = prtd->mixer_conf.sess_indx;
+	uint32_t *no_of_tp = &prtd->start_cmd.no_of_tapoints;
+	struct vss_ivpcm_tap_point *tap_pnt = &prtd->start_cmd.tap_pnt[0];
+	uint32_t no_of_tp_req = 0;
+	char *sess_name = hpcm_get_sess_name(indx);
+
+	if (prtd->mixer_conf.rx.enable)
+		no_of_tp_req++;
+	if (prtd->mixer_conf.tx.enable)
+		no_of_tp_req++;
+
+	if (prtd->mixer_conf.rx.enable && (get_tappnt_value(pcm_id) == RX)) {
+		if (hpcm_all_dais_are_ready(prtd->mixer_conf.rx.direction,
+					    tp, HPCM_PREPARED)) {
+			pr_debug("%s: RX conditions met\n", __func__);
+			tap_pnt[*no_of_tp].tap_point =
+					VSS_IVPCM_TAP_POINT_RX_DEFAULT;
+			tap_pnt[*no_of_tp].direction =
+					prtd->mixer_conf.rx.direction;
+			tap_pnt[*no_of_tp].sampling_rate =
+					prtd->mixer_conf.rx.sample_rate;
+			(*no_of_tp)++;
+		}
+	}
+
+	if (prtd->mixer_conf.tx.enable && (get_tappnt_value(pcm_id) == TX)) {
+		if (hpcm_all_dais_are_ready(prtd->mixer_conf.tx.direction,
+					    tp, HPCM_PREPARED)) {
+			pr_debug("%s: TX conditions met\n", __func__);
+			tap_pnt[*no_of_tp].tap_point =
+						VSS_IVPCM_TAP_POINT_TX_DEFAULT;
+			tap_pnt[*no_of_tp].direction =
+						prtd->mixer_conf.tx.direction;
+			tap_pnt[*no_of_tp].sampling_rate =
+						prtd->mixer_conf.tx.sample_rate;
+			(*no_of_tp)++;
+		}
+	}
+
+	if ((prtd->mixer_conf.tx.enable || prtd->mixer_conf.rx.enable) &&
+	    *no_of_tp == no_of_tp_req) {
+		voc_send_cvp_start_vocpcm(voc_get_session_id(sess_name),
+					  tap_pnt, *no_of_tp);
+		/* Reset the start command so that it is not called twice */
+		memset(&prtd->start_cmd, 0, sizeof(struct start_cmd));
+	} else {
+		pr_debug("%s: required pcm handles not opened yet\n", __func__);
+	}
+
+	return 0;
+}
+
+/* Playback path*/
+static void hpcm_copy_playback_data_from_queue(struct dai_data *dai_data,
+					       uint32_t *len)
+{
+	struct hpcm_buf_node *buf_node = NULL;
+	unsigned long dsp_flags;
+
+	if (dai_data->substream == NULL)
+		return;
+
+	spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+
+	if (!list_empty(&dai_data->filled_queue)) {
+		buf_node = list_first_entry(&dai_data->filled_queue,
+				struct hpcm_buf_node, list);
+		list_del(&buf_node->list);
+		*len = buf_node->frame.len;
+		memcpy((u8 *)dai_data->vocpcm_ion_buffer.kvaddr,
+		       &buf_node->frame.voc_pkt[0],
+		       buf_node->frame.len);
+
+		list_add_tail(&buf_node->list, &dai_data->free_queue);
+		dai_data->pcm_irq_pos += dai_data->pcm_count;
+		spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+		snd_pcm_period_elapsed(dai_data->substream);
+	} else {
+		*len = 0;
+		spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+		pr_err("IN data not available\n");
+	}
+
+	wake_up(&dai_data->queue_wait);
+}
+
+/* Capture path*/
+static void hpcm_copy_capture_data_to_queue(struct dai_data *dai_data,
+					    uint32_t len)
+{
+	struct hpcm_buf_node *buf_node = NULL;
+	unsigned long dsp_flags;
+
+	if (dai_data->substream == NULL)
+		return;
+
+	/* Copy out buffer packet into free_queue */
+	spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+
+	if (!list_empty(&dai_data->free_queue)) {
+		buf_node = list_first_entry(&dai_data->free_queue,
+					struct hpcm_buf_node, list);
+		list_del(&buf_node->list);
+		buf_node->frame.len = len;
+		memcpy(&buf_node->frame.voc_pkt[0],
+		       (uint8_t *)dai_data->vocpcm_ion_buffer.kvaddr,
+		       buf_node->frame.len);
+		list_add_tail(&buf_node->list, &dai_data->filled_queue);
+		dai_data->pcm_irq_pos += dai_data->pcm_count;
+		spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+		snd_pcm_period_elapsed(dai_data->substream);
+	} else {
+		spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+		pr_err("OUTPUT data dropped\n");
+	}
+
+	wake_up(&dai_data->queue_wait);
+}
+
+void hpcm_notify_evt_processing(uint8_t *data, char *session,
+				void *private_data)
+{
+	struct hpcm_drv *prtd = (struct hpcm_drv *)private_data;
+	struct vss_ivpcm_evt_notify_v2_t *notify_evt =
+				(struct vss_ivpcm_evt_notify_v2_t *)data;
+	struct vss_ivpcm_evt_push_buffer_v2_t push_buff_event;
+	struct tap_point *tp = NULL;
+	int in_buf_len = 0;
+	struct tappnt_mxr_data *tmd = NULL;
+	char *sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+
+	/* If it's not a timetick, it's a error notification, drop the event */
+	if ((notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_TIMETICK) == 0) {
+		pr_err("%s: Error notification. mask=%d\n", __func__,
+			notify_evt->notify_mask);
+		return;
+	}
+
+	if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_TX_DEFAULT) {
+		tp = &prtd->session[prtd->mixer_conf.sess_indx].tx_tap_point;
+		tmd = &prtd->mixer_conf.tx;
+	} else if (notify_evt->tap_point == VSS_IVPCM_TAP_POINT_RX_DEFAULT) {
+		tp = &prtd->session[prtd->mixer_conf.sess_indx].rx_tap_point;
+		tmd = &prtd->mixer_conf.rx;
+	}
+
+	if (tp == NULL || tmd == NULL) {
+		pr_err("%s: tp = %pK or tmd = %pK is null\n", __func__,
+		       tp, tmd);
+
+		return;
+	}
+
+	if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER) {
+		hpcm_copy_capture_data_to_queue(&tp->capture_dai_data,
+						notify_evt->filled_out_size);
+	}
+
+	if (notify_evt->notify_mask & VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER) {
+		hpcm_copy_playback_data_from_queue(&tp->playback_dai_data,
+						   &in_buf_len);
+	}
+
+	switch (tmd->direction) {
+	/*
+	 * When the dir is OUT_IN, for the first notify mask, pushbuf mask
+	 * should be set to VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER since we
+	 * atleast need one buffer's worth data before we can send IN buffer.
+	 * For the consecutive notify evts, the push buf mask will set for both
+	 * VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER and
+	 * VSS_IVPCM_PUSH_BUFFER_MASK_IN_BUFFER.
+	 */
+	case VSS_IVPCM_TAP_POINT_DIR_OUT_IN:
+		if (notify_evt->notify_mask ==
+		    VSS_IVPCM_NOTIFY_MASK_TIMETICK) {
+			push_buff_event.push_buf_mask =
+				VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER;
+		} else {
+			push_buff_event.push_buf_mask =
+			   VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER |
+			   VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER;
+		}
+		break;
+
+	case VSS_IVPCM_TAP_POINT_DIR_IN:
+		push_buff_event.push_buf_mask =
+			VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER;
+		break;
+
+	case VSS_IVPCM_TAP_POINT_DIR_OUT:
+		push_buff_event.push_buf_mask =
+			 VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER;
+		break;
+	}
+
+	push_buff_event.tap_point = notify_evt->tap_point;
+	push_buff_event.out_buf_mem_address =
+		      tp->capture_dai_data.vocpcm_ion_buffer.paddr;
+	push_buff_event.in_buf_mem_address =
+		      tp->playback_dai_data.vocpcm_ion_buffer.paddr;
+	push_buff_event.sampling_rate = notify_evt->sampling_rate;
+	push_buff_event.num_in_channels = 1;
+
+	/*
+	 * ADSP must read and write from a cache aligned (128 byte) location,
+	 * and in blocks of the cache alignment size. The 128 byte cache
+	 * alignment requirement is guaranteed due to 4096 byte memory
+	 * alignment requirement during memory allocation/mapping. The output
+	 * buffer (ADSP write) size mask ensures that a 128 byte multiple
+	 * worth of will be written.  Internally, the input buffer (ADSP read)
+	 * size will also be a multiple of 128 bytes.  However it is the
+	 * application's responsibility to ensure no other data is written in
+	 * the specified length of memory.
+	 */
+	push_buff_event.out_buf_mem_size = ((notify_evt->request_buf_size) +
+				CACHE_ALIGNMENT_SIZE) & CACHE_ALIGNMENT_MASK;
+	push_buff_event.in_buf_mem_size = in_buf_len;
+
+	voc_send_cvp_vocpcm_push_buf_evt(voc_get_session_id(sess_name),
+					 &push_buff_event);
+}
+
+static int msm_hpcm_configure_voice_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+
+	int tap_point = ucontrol->value.integer.value[0];
+	uint16_t direction = ucontrol->value.integer.value[1];
+	uint16_t sample_rate = ucontrol->value.integer.value[2];
+	struct tappnt_mxr_data *tmd = NULL;
+	int ret = 0;
+
+	mutex_lock(&hpcm_drv.lock);
+	pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n",
+		 __func__, tap_point, direction, sample_rate);
+
+	if (!hpcm_is_valid_config(VOICE_INDEX, tap_point, direction,
+				  sample_rate)) {
+		pr_err("Invalid vpcm mixer control voice values\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (tap_point == RX)
+		tmd = &hpcm_drv.mixer_conf.rx;
+	else
+		tmd = &hpcm_drv.mixer_conf.tx;
+
+	tmd->enable = true;
+	tmd->direction = direction;
+	tmd->sample_rate = sample_rate;
+	hpcm_drv.mixer_conf.sess_indx = VOICE_INDEX;
+
+done:
+	mutex_unlock(&hpcm_drv.lock);
+	return ret;
+}
+
+static int msm_hpcm_configure_vmmode1_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+
+	int tap_point = ucontrol->value.integer.value[0];
+	uint16_t direction = ucontrol->value.integer.value[1];
+	uint16_t sample_rate = ucontrol->value.integer.value[2];
+	struct tappnt_mxr_data *tmd = NULL;
+	int ret = 0;
+
+	mutex_lock(&hpcm_drv.lock);
+	pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n",
+		 __func__, tap_point, direction, sample_rate);
+
+	if (!hpcm_is_valid_config(VOMMODE1_INDEX, tap_point, direction,
+				  sample_rate)) {
+		pr_err("Invalid vpcm mixer control voice values\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (tap_point == RX)
+		tmd = &hpcm_drv.mixer_conf.rx;
+	else
+		tmd = &hpcm_drv.mixer_conf.tx;
+
+	tmd->enable = true;
+	tmd->direction = direction;
+	tmd->sample_rate = sample_rate;
+	hpcm_drv.mixer_conf.sess_indx = VOMMODE1_INDEX;
+
+done:
+	mutex_unlock(&hpcm_drv.lock);
+	return ret;
+}
+
+static int msm_hpcm_configure_vmmode2_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+
+	int tap_point = ucontrol->value.integer.value[0];
+	uint16_t direction = ucontrol->value.integer.value[1];
+	uint16_t sample_rate = ucontrol->value.integer.value[2];
+	struct tappnt_mxr_data *tmd = NULL;
+	int ret = 0;
+
+	mutex_lock(&hpcm_drv.lock);
+	pr_debug("%s: tap_point = %d direction = %d sample_rate = %d\n",
+		 __func__, tap_point, direction, sample_rate);
+
+	if (!hpcm_is_valid_config(VOMMODE2_INDEX, tap_point, direction,
+				  sample_rate)) {
+		pr_err("Invalid vpcm mixer control voice values\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (tap_point == RX)
+		tmd = &hpcm_drv.mixer_conf.rx;
+	else
+		tmd = &hpcm_drv.mixer_conf.tx;
+
+	tmd->enable = true;
+	tmd->direction = direction;
+	tmd->sample_rate = sample_rate;
+	hpcm_drv.mixer_conf.sess_indx = VOMMODE2_INDEX;
+
+done:
+	mutex_unlock(&hpcm_drv.lock);
+	return ret;
+}
+
+static int msm_hpcm_configure_volte_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+
+	int tap_point = ucontrol->value.integer.value[0];
+	uint16_t direction = ucontrol->value.integer.value[1];
+	uint16_t sample_rate = ucontrol->value.integer.value[2];
+	struct tappnt_mxr_data *tmd = NULL;
+	int ret = 0;
+
+	mutex_lock(&hpcm_drv.lock);
+	pr_debug("%s: tap_point=%d direction=%d sample_rate=%d\n",
+		 __func__, tap_point, direction, sample_rate);
+
+	if (!hpcm_is_valid_config(VOLTE_INDEX, tap_point, direction,
+				  sample_rate)) {
+		pr_err("Invalid vpcm mixer control volte values\n");
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (tap_point == RX)
+		tmd = &hpcm_drv.mixer_conf.rx;
+	else
+		tmd = &hpcm_drv.mixer_conf.tx;
+
+	tmd->enable = true;
+	tmd->direction = direction;
+	tmd->sample_rate = sample_rate;
+	hpcm_drv.mixer_conf.sess_indx = VOLTE_INDEX;
+
+done:
+	mutex_unlock(&hpcm_drv.lock);
+	return ret;
+
+}
+
+static struct snd_kcontrol_new msm_hpcm_controls[] = {
+	SOC_SINGLE_MULTI_EXT("HPCM_Voice tappoint direction samplerate",
+			     SND_SOC_NOPM, 0, 16000, 0, 3,
+			     NULL, msm_hpcm_configure_voice_put),
+	SOC_SINGLE_MULTI_EXT("HPCM_VoLTE tappoint direction samplerate",
+			     SND_SOC_NOPM, 0, 16000, 0, 3,
+			     NULL, msm_hpcm_configure_volte_put),
+	SOC_SINGLE_MULTI_EXT("HPCM_VMMode1 tappoint direction samplerate",
+			     SND_SOC_NOPM, 0, 16000, 0, 3,
+			     NULL, msm_hpcm_configure_vmmode1_put),
+	SOC_SINGLE_MULTI_EXT("HPCM_VMMode2 tappoint direction samplerate",
+			     SND_SOC_NOPM, 0, 16000, 0, 3,
+			     NULL, msm_hpcm_configure_vmmode2_put),
+};
+
+/* Sample rates supported */
+static unsigned int supported_sample_rates[] = {8000, 16000};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct list_head *ptr = NULL;
+	struct list_head *next = NULL;
+	struct hpcm_buf_node *buf_node = NULL;
+	struct snd_dma_buffer *dma_buf;
+	struct snd_pcm_runtime *runtime;
+	struct hpcm_drv *prtd;
+	unsigned long dsp_flags;
+	struct dai_data *dai_data = NULL;
+	struct tap_point *tp = NULL;
+	struct tappnt_mxr_data *tmd = NULL;
+	char *sess_name = NULL;
+
+	if (substream == NULL) {
+		pr_err("substream is NULL\n");
+		return -EINVAL;
+	}
+
+	pr_debug("%s, %s\n", __func__, substream->pcm->id);
+	runtime = substream->runtime;
+	prtd = runtime->private_data;
+	sess_name = hpcm_get_sess_name(prtd->mixer_conf.sess_indx);
+	dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	wake_up(&dai_data->queue_wait);
+	mutex_lock(&prtd->lock);
+
+	tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd);
+
+	tp = hpcm_get_tappoint_data(substream->pcm->id, prtd);
+	/* Send stop command */
+	voc_send_cvp_stop_vocpcm(voc_get_session_id(sess_name));
+	/* Memory unmap/free takes place only when called the first time */
+	hpcm_unmap_and_free_shared_memory(prtd);
+	/* Unregister host PCM event callback function */
+	voc_deregister_hpcm_evt_cb();
+	/* Reset the cached start cmd */
+	memset(&prtd->start_cmd, 0, sizeof(struct start_cmd));
+	/* Release all buffer */
+	pr_debug("%s: Release all buffer\n", __func__);
+	substream = dai_data->substream;
+	if (substream == NULL) {
+		pr_debug("%s: substream is NULL\n", __func__);
+		goto done;
+	}
+	dma_buf = &substream->dma_buffer;
+	if (dma_buf == NULL) {
+		pr_debug("%s: dma_buf is NULL\n", __func__);
+		goto done;
+	}
+	if (dma_buf->area != NULL) {
+		spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+		list_for_each_safe(ptr, next, &dai_data->filled_queue) {
+			buf_node = list_entry(ptr,
+					struct hpcm_buf_node, list);
+			list_del(&buf_node->list);
+		}
+		list_for_each_safe(ptr, next, &dai_data->free_queue) {
+			buf_node = list_entry(ptr,
+					struct hpcm_buf_node, list);
+			list_del(&buf_node->list);
+		}
+		spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+		dma_free_coherent(substream->pcm->card->dev,
+			runtime->hw.buffer_bytes_max, dma_buf->area,
+			dma_buf->addr);
+		dma_buf->area = NULL;
+	}
+	dai_data->substream = NULL;
+	dai_data->pcm_buf_pos = 0;
+	dai_data->pcm_count = 0;
+	dai_data->pcm_irq_pos = 0;
+	dai_data->pcm_size = 0;
+	dai_data->state = HPCM_CLOSED;
+	hpcm_reset_mixer_config(prtd);
+
+done:
+	mutex_unlock(&prtd->lock);
+	return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+				 snd_pcm_uframes_t hwoff, void __user *buf,
+				 snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	struct hpcm_buf_node *buf_node = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hpcm_drv *prtd = runtime->private_data;
+	struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+	unsigned long dsp_flags;
+
+	int count = frames_to_bytes(runtime, frames);
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is null\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_interruptible_timeout(dai_data->queue_wait,
+				(!list_empty(&dai_data->free_queue) ||
+				dai_data->state == HPCM_STOPPED),
+				1 * HZ);
+	if (ret > 0) {
+		if (count <= HPCM_MAX_VOC_PKT_SIZE) {
+			spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+			buf_node =
+				list_first_entry(&dai_data->free_queue,
+						struct hpcm_buf_node, list);
+			list_del(&buf_node->list);
+			spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+			ret = copy_from_user(&buf_node->frame.voc_pkt, buf,
+					     count);
+			buf_node->frame.len = count;
+			spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+			list_add_tail(&buf_node->list, &dai_data->filled_queue);
+			spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+		} else {
+			pr_err("%s: Write cnt %d is > HPCM_MAX_VOC_PKT_SIZE\n",
+				__func__, count);
+			ret = -ENOMEM;
+		}
+	} else if (ret == 0) {
+		pr_err("%s: No free Playback buffer\n", __func__);
+		ret = -ETIMEDOUT;
+	} else {
+		pr_err("%s: playback copy  was interrupted\n", __func__);
+	}
+
+done:
+	return  ret;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+				int channel, snd_pcm_uframes_t hwoff,
+				void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	int count = 0;
+	struct hpcm_buf_node *buf_node = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hpcm_drv *prtd = runtime->private_data;
+	struct dai_data *dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+	unsigned long dsp_flags;
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is null\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	count = frames_to_bytes(runtime, frames);
+
+	ret = wait_event_interruptible_timeout(dai_data->queue_wait,
+				(!list_empty(&dai_data->filled_queue) ||
+				dai_data->state == HPCM_STOPPED),
+				1 * HZ);
+
+	if (ret > 0) {
+		if (count <= HPCM_MAX_VOC_PKT_SIZE) {
+			spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+			buf_node = list_first_entry(&dai_data->filled_queue,
+					struct hpcm_buf_node, list);
+			list_del(&buf_node->list);
+			spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+			ret = copy_to_user(buf, &buf_node->frame.voc_pkt,
+					   buf_node->frame.len);
+			if (ret) {
+				pr_err("%s: Copy to user returned %d\n",
+					__func__, ret);
+				ret = -EFAULT;
+			}
+			spin_lock_irqsave(&dai_data->dsp_lock, dsp_flags);
+			list_add_tail(&buf_node->list, &dai_data->free_queue);
+			spin_unlock_irqrestore(&dai_data->dsp_lock, dsp_flags);
+
+		} else {
+			pr_err("%s: Read count %d > HPCM_MAX_VOC_PKT_SIZE\n",
+				__func__, count);
+			ret = -ENOMEM;
+		}
+
+	} else if (ret == 0) {
+		pr_err("%s: No Caputre data available\n", __func__);
+		ret = -ETIMEDOUT;
+	} else {
+		pr_err("%s: Read was interrupted\n", __func__);
+		ret = -ERESTARTSYS;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int channel,
+			snd_pcm_uframes_t hwoff, void __user *buf,
+			snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_copy(substream, channel,
+					    hwoff, buf, frames);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_copy(substream, channel,
+					   hwoff, buf, frames);
+
+	return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct dai_data *dai_data = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hpcm_drv *prtd = runtime->private_data;
+	snd_pcm_uframes_t ret;
+
+	dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is null\n", __func__);
+
+		ret = 0;
+		goto done;
+	}
+
+	if (dai_data->pcm_irq_pos >= dai_data->pcm_size)
+		dai_data->pcm_irq_pos = 0;
+
+	ret = bytes_to_frames(runtime, (dai_data->pcm_irq_pos));
+
+done:
+	return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hpcm_drv *prtd = runtime->private_data;
+	struct dai_data *dai_data =
+			hpcm_get_dai_data(substream->pcm->id, prtd);
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is null\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s, %s\n", __func__, substream->pcm->id);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		pr_debug("SNDRV_PCM_TRIGGER_START\n");
+		dai_data->state = HPCM_STARTED;
+		break;
+
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+		dai_data->state = HPCM_STOPPED;
+		break;
+
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hpcm_drv *prtd = runtime->private_data;
+	struct dai_data *dai_data = NULL;
+	struct tap_point *tp = NULL;
+
+	pr_debug("%s, %s\n", __func__, substream->pcm->id);
+	mutex_lock(&prtd->lock);
+
+	dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is null\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	dai_data->pcm_size  = snd_pcm_lib_buffer_bytes(substream);
+	dai_data->pcm_count = snd_pcm_lib_period_bytes(substream);
+	dai_data->pcm_irq_pos = 0;
+	dai_data->pcm_buf_pos = 0;
+	dai_data->state = HPCM_PREPARED;
+
+	/* Register event notify processing callback in prepare instead of
+	 * init() as q6voice module's init() can be called at a later point
+	 */
+	voc_register_hpcm_evt_cb(hpcm_notify_evt_processing, &hpcm_drv);
+
+	tp = hpcm_get_tappoint_data(substream->pcm->id, prtd);
+	if (tp != NULL) {
+		ret = hpcm_start_vocpcm(substream->pcm->id, prtd, tp);
+		if (ret) {
+			pr_err("error sending start cmd err=%d\n", ret);
+			goto done;
+		}
+	} else {
+		pr_err("%s tp is NULL\n", __func__);
+	}
+done:
+	mutex_unlock(&prtd->lock);
+	return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+			     struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct hpcm_drv *prtd = (struct hpcm_drv *)runtime->private_data;
+	int ret = 0;
+
+	pr_debug("%s: %s\n", __func__, substream->pcm->id);
+	mutex_lock(&prtd->lock);
+
+	/* Allocate and map voice host PCM ion buffer */
+	if (prtd->session[prtd->mixer_conf.sess_indx].sess_paddr == 0) {
+		ret = hpcm_allocate_shared_memory(prtd);
+		if (ret) {
+			pr_err("error creating shared memory err=%d\n", ret);
+			goto done;
+		}
+
+		ret = hpcm_map_vocpcm_memory(prtd);
+		if (ret) {
+			pr_err("error mapping shared memory err=%d\n", ret);
+			hpcm_free_allocated_mem(prtd);
+			goto done;
+		}
+	} else {
+		pr_debug("%s, VHPCM memory allocation/mapping not performed\n"
+			 , __func__);
+	}
+
+	dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dma_buf->dev.dev = substream->pcm->card->dev;
+	dma_buf->private_data = NULL;
+
+	dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+			runtime->hw.buffer_bytes_max,
+			&dma_buf->addr, GFP_KERNEL);
+
+	if (!dma_buf->area) {
+		pr_err("%s:MSM dma_alloc failed\n", __func__);
+		ret = -ENOMEM;
+		goto done;
+	}
+
+	dma_buf->bytes = runtime->hw.buffer_bytes_max;
+	memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+	hpcm_create_free_queue(dma_buf,
+		hpcm_get_dai_data(substream->pcm->id, prtd));
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+done:
+	mutex_unlock(&prtd->lock);
+	return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct hpcm_drv *prtd = &hpcm_drv;
+	struct tappnt_mxr_data *tmd = NULL;
+	struct dai_data *dai_data = NULL;
+	int ret = 0;
+	int tp_val = 0;
+
+	pr_debug("%s, %s\n", __func__, substream->pcm->id);
+	mutex_lock(&prtd->lock);
+
+	dai_data = hpcm_get_dai_data(substream->pcm->id, prtd);
+
+	if (dai_data == NULL) {
+		pr_err("%s, dai_data is null\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	runtime->hw = msm_pcm_hardware;
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+					 &constraints_sample_rates);
+	if (ret < 0)
+		pr_debug("snd_pcm_hw_constraint_list failed\n");
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		pr_debug("snd_pcm_hw_constraint_integer failed\n");
+		goto done;
+	}
+
+	tp_val = get_tappnt_value(substream->pcm->id);
+	tmd = hpcm_get_tappnt_mixer_data(substream->pcm->id, prtd);
+
+	/* Check wheather the kcontrol values set are valid */
+	if (!tmd ||
+	    !(tmd->enable) ||
+	    !hpcm_is_valid_config(prtd->mixer_conf.sess_indx,
+				  tp_val, tmd->direction,
+				  tmd->sample_rate)) {
+		ret = -EINVAL;
+		goto done;
+	}
+
+	dai_data->substream = substream;
+	runtime->private_data = prtd;
+
+done:
+	mutex_unlock(&prtd->lock);
+	return ret;
+}
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open           = msm_pcm_open,
+	.hw_params      = msm_pcm_hw_params,
+	.prepare        = msm_pcm_prepare,
+	.trigger        = msm_pcm_trigger,
+	.pointer        = msm_pcm_pointer,
+	.copy           = msm_pcm_copy,
+	.close          = msm_pcm_close,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+
+	pr_debug("%s:\n", __func__);
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	return 0;
+}
+
+static int msm_pcm_hpcm_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, msm_hpcm_controls,
+				ARRAY_SIZE(msm_hpcm_controls));
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.probe		= msm_pcm_hpcm_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+
+	pr_info("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+	return snd_soc_register_platform(&pdev->dev, &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_voice_host_pcm_dt_match[] = {
+	{.compatible = "qcom,msm-voice-host-pcm"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_voice_host_pcm_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-voice-host-pcm",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_voice_host_pcm_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	int i = 0;
+	struct session *s = NULL;
+
+	memset(&hpcm_drv, 0, sizeof(hpcm_drv));
+	mutex_init(&hpcm_drv.lock);
+
+	for (i = 0; i < MAX_SESSION; i++) {
+		s = &hpcm_drv.session[i];
+		spin_lock_init(&s->rx_tap_point.capture_dai_data.dsp_lock);
+		spin_lock_init(&s->rx_tap_point.playback_dai_data.dsp_lock);
+		spin_lock_init(&s->tx_tap_point.capture_dai_data.dsp_lock);
+		spin_lock_init(&s->tx_tap_point.playback_dai_data.dsp_lock);
+
+		init_waitqueue_head(
+			&s->rx_tap_point.capture_dai_data.queue_wait);
+		init_waitqueue_head(
+			&s->rx_tap_point.playback_dai_data.queue_wait);
+		init_waitqueue_head(
+			&s->tx_tap_point.capture_dai_data.queue_wait);
+		init_waitqueue_head(
+			&s->tx_tap_point.playback_dai_data.queue_wait);
+
+		INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.filled_queue);
+		INIT_LIST_HEAD(&s->rx_tap_point.capture_dai_data.free_queue);
+		INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.filled_queue);
+		INIT_LIST_HEAD(&s->rx_tap_point.playback_dai_data.free_queue);
+
+		INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.filled_queue);
+		INIT_LIST_HEAD(&s->tx_tap_point.capture_dai_data.free_queue);
+		INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.filled_queue);
+		INIT_LIST_HEAD(&s->tx_tap_point.playback_dai_data.free_queue);
+	}
+
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
new file mode 100644
index 0000000..15809ce
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-loopback-v2.c
@@ -0,0 +1,790 @@
+/* Copyright (c) 2013-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/q6asm-v2.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <asm/dma.h>
+
+#include "msm-pcm-routing-v2.h"
+
+#define LOOPBACK_VOL_MAX_STEPS 0x2000
+#define LOOPBACK_SESSION_MAX 4
+
+static DEFINE_MUTEX(loopback_session_lock);
+static const DECLARE_TLV_DB_LINEAR(loopback_rx_vol_gain, 0,
+				LOOPBACK_VOL_MAX_STEPS);
+
+struct msm_pcm_loopback {
+	struct snd_pcm_substream *playback_substream;
+	struct snd_pcm_substream *capture_substream;
+
+	int instance;
+
+	struct mutex lock;
+
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+
+	int playback_start;
+	int capture_start;
+	int session_id;
+	struct audio_client *audio_client;
+	uint32_t volume;
+};
+
+struct fe_dai_session_map {
+	char stream_name[32];
+	struct msm_pcm_loopback *loopback_priv;
+};
+
+static struct fe_dai_session_map session_map[LOOPBACK_SESSION_MAX] = {
+	{ {}, NULL},
+	{ {}, NULL},
+	{ {}, NULL},
+	{ {}, NULL},
+};
+
+static u32 hfp_tx_mute;
+
+static void stop_pcm(struct msm_pcm_loopback *pcm);
+static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
+					struct msm_pcm_loopback **pcm);
+
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+					void *priv_data)
+{
+	struct msm_pcm_loopback *pcm = priv_data;
+
+	WARN_ON(!pcm);
+
+	pr_debug("%s: event 0x%x\n", __func__, event);
+
+	switch (event) {
+	case MSM_PCM_RT_EVT_DEVSWITCH:
+		q6asm_cmd(pcm->audio_client, CMD_PAUSE);
+		q6asm_cmd(pcm->audio_client, CMD_FLUSH);
+		q6asm_run(pcm->audio_client, 0, 0, 0);
+		/* fallthrough */
+	default:
+		pr_err("%s: default event 0x%x\n", __func__, event);
+		break;
+	}
+}
+
+static void msm_pcm_loopback_event_handler(uint32_t opcode, uint32_t token,
+					   uint32_t *payload, void *priv)
+{
+	pr_debug("%s:\n", __func__);
+	switch (opcode) {
+	case APR_BASIC_RSP_RESULT: {
+		switch (payload[0]) {
+			break;
+		default:
+			break;
+		}
+	}
+		break;
+	default:
+		pr_err("%s: Not Supported Event opcode[0x%x]\n",
+			__func__, opcode);
+		break;
+	}
+}
+
+static int msm_loopback_session_mute_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hfp_tx_mute;
+	return 0;
+}
+
+static int msm_loopback_session_mute_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0, n = 0;
+	int mute = ucontrol->value.integer.value[0];
+	struct msm_pcm_loopback *pcm = NULL;
+
+	if ((mute < 0) || (mute > 1)) {
+		pr_err(" %s Invalid arguments", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: mute=%d\n", __func__, mute);
+	hfp_tx_mute = mute;
+	for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
+		if (!strcmp(session_map[n].stream_name, "MultiMedia6"))
+			pcm = session_map[n].loopback_priv;
+	}
+	if (pcm && pcm->audio_client) {
+		ret = q6asm_set_mute(pcm->audio_client, mute);
+		if (ret < 0)
+			pr_err("%s: Send mute command failed rc=%d\n",
+				__func__, ret);
+	}
+done:
+	return ret;
+}
+
+static struct snd_kcontrol_new msm_loopback_controls[] = {
+	SOC_SINGLE_EXT("HFP TX Mute", SND_SOC_NOPM, 0, 1, 0,
+			msm_loopback_session_mute_get,
+			msm_loopback_session_mute_put),
+};
+
+static int msm_pcm_loopback_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, msm_loopback_controls,
+				      ARRAY_SIZE(msm_loopback_controls));
+
+	return 0;
+}
+static int pcm_loopback_set_volume(struct msm_pcm_loopback *prtd,
+				   uint32_t volume)
+{
+	int rc = -EINVAL;
+
+	pr_debug("%s: Setting volume 0x%x\n", __func__, volume);
+
+	if (prtd && prtd->audio_client) {
+		rc = q6asm_set_volume(prtd->audio_client, volume);
+		if (rc < 0) {
+			pr_err("%s: Send Volume command failed rc = %d\n",
+				__func__, rc);
+			return rc;
+		}
+		prtd->volume = volume;
+	}
+	return rc;
+}
+
+static int msm_pcm_loopback_get_session(struct snd_soc_pcm_runtime *rtd,
+					struct msm_pcm_loopback **pcm)
+{
+	int ret = 0;
+	int n, index = -1;
+
+	dev_dbg(rtd->platform->dev, "%s: stream %s\n", __func__,
+		rtd->dai_link->stream_name);
+
+	mutex_lock(&loopback_session_lock);
+	for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
+		if (!strcmp(rtd->dai_link->stream_name,
+		    session_map[n].stream_name)) {
+			*pcm = session_map[n].loopback_priv;
+			goto exit;
+		}
+		/*
+		 * Store the min index value for allocating a new session.
+		 * Here, if session stream name is not found in the
+		 * existing entries after the loop iteration, then this
+		 * index will be used to allocate the new session.
+		 * This index variable is expected to point to the topmost
+		 * available free session.
+		 */
+		if (!(session_map[n].stream_name[0]) && (index < 0))
+			index = n;
+	}
+
+	if (index < 0) {
+		dev_err(rtd->platform->dev, "%s: Max Sessions allocated\n",
+				 __func__);
+		ret = -EAGAIN;
+		goto exit;
+	}
+
+	session_map[index].loopback_priv = kzalloc(
+		sizeof(struct msm_pcm_loopback), GFP_KERNEL);
+	if (!session_map[index].loopback_priv) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	strlcpy(session_map[index].stream_name,
+		rtd->dai_link->stream_name,
+		sizeof(session_map[index].stream_name));
+	dev_dbg(rtd->platform->dev, "%s: stream %s index %d\n",
+		__func__, session_map[index].stream_name, index);
+
+	mutex_init(&session_map[index].loopback_priv->lock);
+	*pcm = session_map[index].loopback_priv;
+exit:
+	mutex_unlock(&loopback_session_lock);
+	return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	struct msm_pcm_loopback *pcm = NULL;
+	int ret = 0;
+	uint16_t bits_per_sample = 16;
+	struct msm_pcm_routing_evt event;
+	struct asm_session_mtmx_strtr_param_window_v2_t asm_mtmx_strtr_window;
+	uint32_t param_id;
+
+	ret =  msm_pcm_loopback_get_session(rtd, &pcm);
+	if (ret)
+		return ret;
+
+	mutex_lock(&pcm->lock);
+
+	pcm->volume = 0x2000;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		pcm->playback_substream = substream;
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		pcm->capture_substream = substream;
+
+	pcm->instance++;
+	dev_dbg(rtd->platform->dev, "%s: pcm out open: %d,%d\n", __func__,
+			pcm->instance, substream->stream);
+	if (pcm->instance == 2) {
+		struct snd_soc_pcm_runtime *soc_pcm_rx =
+				pcm->playback_substream->private_data;
+		struct snd_soc_pcm_runtime *soc_pcm_tx =
+				pcm->capture_substream->private_data;
+		if (pcm->audio_client != NULL)
+			stop_pcm(pcm);
+
+		pcm->audio_client = q6asm_audio_client_alloc(
+				(app_cb)msm_pcm_loopback_event_handler, pcm);
+		if (!pcm->audio_client) {
+			dev_err(rtd->platform->dev,
+				"%s: Could not allocate memory\n", __func__);
+			mutex_unlock(&pcm->lock);
+			return -ENOMEM;
+		}
+		pcm->session_id = pcm->audio_client->session;
+		pcm->audio_client->perf_mode = false;
+		ret = q6asm_open_loopback_v2(pcm->audio_client,
+					     bits_per_sample);
+		if (ret < 0) {
+			dev_err(rtd->platform->dev,
+				"%s: pcm out open failed\n", __func__);
+			q6asm_audio_client_free(pcm->audio_client);
+			mutex_unlock(&pcm->lock);
+			return -ENOMEM;
+		}
+		event.event_func = msm_pcm_route_event_handler;
+		event.priv_data = (void *) pcm;
+		msm_pcm_routing_reg_phy_stream(soc_pcm_tx->dai_link->id,
+			pcm->audio_client->perf_mode,
+			pcm->session_id, pcm->capture_substream->stream);
+		msm_pcm_routing_reg_phy_stream_v2(soc_pcm_rx->dai_link->id,
+			pcm->audio_client->perf_mode,
+			pcm->session_id, pcm->playback_substream->stream,
+			event);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			pcm->playback_substream = substream;
+			ret = pcm_loopback_set_volume(pcm, pcm->volume);
+			if (ret < 0)
+				dev_err(rtd->platform->dev,
+					"Error %d setting volume", ret);
+		}
+		/* Set to largest negative value */
+		asm_mtmx_strtr_window.window_lsw = 0x00000000;
+		asm_mtmx_strtr_window.window_msw = 0x80000000;
+		param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_START_V2;
+		q6asm_send_mtmx_strtr_window(pcm->audio_client,
+					     &asm_mtmx_strtr_window,
+					     param_id);
+		/* Set to largest positive value */
+		asm_mtmx_strtr_window.window_lsw = 0xffffffff;
+		asm_mtmx_strtr_window.window_msw = 0x7fffffff;
+		param_id = ASM_SESSION_MTMX_STRTR_PARAM_RENDER_WINDOW_END_V2;
+		q6asm_send_mtmx_strtr_window(pcm->audio_client,
+					     &asm_mtmx_strtr_window,
+					     param_id);
+	}
+	dev_info(rtd->platform->dev, "%s: Instance = %d, Stream ID = %s\n",
+			__func__, pcm->instance, substream->pcm->id);
+	runtime->private_data = pcm;
+
+	mutex_unlock(&pcm->lock);
+
+	return 0;
+}
+
+static void stop_pcm(struct msm_pcm_loopback *pcm)
+{
+	struct snd_soc_pcm_runtime *soc_pcm_rx;
+	struct snd_soc_pcm_runtime *soc_pcm_tx;
+
+	if (pcm->audio_client == NULL)
+		return;
+	q6asm_cmd(pcm->audio_client, CMD_CLOSE);
+
+	if (pcm->playback_substream != NULL) {
+		soc_pcm_rx = pcm->playback_substream->private_data;
+		msm_pcm_routing_dereg_phy_stream(soc_pcm_rx->dai_link->id,
+				SNDRV_PCM_STREAM_PLAYBACK);
+	}
+	if (pcm->capture_substream != NULL) {
+		soc_pcm_tx = pcm->capture_substream->private_data;
+		msm_pcm_routing_dereg_phy_stream(soc_pcm_tx->dai_link->id,
+				SNDRV_PCM_STREAM_CAPTURE);
+	}
+	q6asm_audio_client_free(pcm->audio_client);
+	pcm->audio_client = NULL;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_pcm_loopback *pcm = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+	int ret = 0, n;
+	bool found = false;
+
+	mutex_lock(&pcm->lock);
+
+	dev_dbg(rtd->platform->dev, "%s: end pcm call:%d\n",
+		__func__, substream->stream);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		pcm->playback_start = 0;
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		pcm->capture_start = 0;
+
+	pcm->instance--;
+	if (!pcm->playback_start || !pcm->capture_start) {
+		dev_dbg(rtd->platform->dev, "%s: end pcm call\n", __func__);
+		stop_pcm(pcm);
+	}
+
+	if (!pcm->instance) {
+		mutex_lock(&loopback_session_lock);
+		for (n = 0; n < LOOPBACK_SESSION_MAX; n++) {
+			if (!strcmp(rtd->dai_link->stream_name,
+					session_map[n].stream_name)) {
+				found = true;
+				break;
+			}
+		}
+		if (found) {
+			memset(session_map[n].stream_name, 0,
+				sizeof(session_map[n].stream_name));
+			mutex_unlock(&pcm->lock);
+			mutex_destroy(&session_map[n].loopback_priv->lock);
+			session_map[n].loopback_priv = NULL;
+			kfree(pcm);
+			dev_dbg(rtd->platform->dev, "%s: stream freed %s\n",
+				__func__, rtd->dai_link->stream_name);
+			mutex_unlock(&loopback_session_lock);
+			return 0;
+		}
+		mutex_unlock(&loopback_session_lock);
+	}
+	mutex_unlock(&pcm->lock);
+	return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_pcm_loopback *pcm = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+	mutex_lock(&pcm->lock);
+
+	dev_dbg(rtd->platform->dev, "%s: ASM loopback stream:%d\n",
+		__func__, substream->stream);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (!pcm->playback_start)
+			pcm->playback_start = 1;
+	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		if (!pcm->capture_start)
+			pcm->capture_start = 1;
+	}
+	mutex_unlock(&pcm->lock);
+
+	return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_pcm_loopback *pcm = runtime->private_data;
+	struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		dev_dbg(rtd->platform->dev,
+			"%s: playback_start:%d,capture_start:%d\n", __func__,
+			pcm->playback_start, pcm->capture_start);
+		if (pcm->playback_start && pcm->capture_start)
+			q6asm_run_nowait(pcm->audio_client, 0, 0, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+	case SNDRV_PCM_TRIGGER_STOP:
+		dev_dbg(rtd->platform->dev,
+			"%s:Pause/Stop - playback_start:%d,capture_start:%d\n",
+			__func__, pcm->playback_start, pcm->capture_start);
+		if (pcm->playback_start && pcm->capture_start)
+			q6asm_cmd_nowait(pcm->audio_client, CMD_PAUSE);
+		break;
+	default:
+		pr_err("%s: default cmd %d\n", __func__, cmd);
+		break;
+	}
+
+	return 0;
+}
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open           = msm_pcm_open,
+	.close          = msm_pcm_close,
+	.prepare        = msm_pcm_prepare,
+	.trigger        = msm_pcm_trigger,
+};
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+	struct snd_pcm_volume *vol = kcontrol->private_data;
+	struct snd_pcm_substream *substream = vol->pcm->streams[0].substream;
+	struct msm_pcm_loopback *prtd;
+	int volume = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: volume : 0x%x\n", __func__, volume);
+	if ((!substream) || (!substream->runtime)) {
+		pr_err("%s substream or runtime not found\n", __func__);
+		rc = -ENODEV;
+		goto exit;
+	}
+	prtd = substream->runtime->private_data;
+	if (!prtd) {
+		rc = -ENODEV;
+		goto exit;
+	}
+	rc = pcm_loopback_set_volume(prtd, volume);
+
+exit:
+	return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+		vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_pcm_loopback *prtd;
+
+	pr_debug("%s\n", __func__);
+	if ((!substream) || (!substream->runtime)) {
+		pr_err("%s substream or runtime not found\n", __func__);
+		rc = -ENODEV;
+		goto exit;
+	}
+	prtd = substream->runtime->private_data;
+	if (!prtd) {
+		rc = -ENODEV;
+		goto exit;
+	}
+	ucontrol->value.integer.value[0] = prtd->volume;
+
+exit:
+	return rc;
+}
+
+static int msm_pcm_add_volume_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+	struct snd_pcm_volume *volume_info;
+	struct snd_kcontrol *kctl;
+	int ret = 0;
+
+	dev_dbg(rtd->dev, "%s, Volume cntrl add\n", __func__);
+	ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				      NULL, 1,
+				      rtd->dai_link->id,
+				      &volume_info);
+	if (ret < 0)
+		return ret;
+	kctl = volume_info->kctl;
+	kctl->put = msm_pcm_volume_ctl_put;
+	kctl->get = msm_pcm_volume_ctl_get;
+	kctl->tlv.p = loopback_rx_vol_gain;
+	return 0;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate = 48000;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s: Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		return -EINVAL;
+	}
+
+	app_type = ucontrol->value.integer.value[0];
+	acdb_dev_id = ucontrol->value.integer.value[1];
+	if (ucontrol->value.integer.value[2] != 0)
+		sample_rate = ucontrol->value.integer.value[2];
+	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n",
+		__func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_RX);
+	msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type,
+			acdb_dev_id, sample_rate, SESSION_TYPE_RX);
+
+	return 0;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int ret = 0;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s: Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_RX,
+		&app_type, &acdb_dev_id, &sample_rate);
+	if (ret < 0) {
+		pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ucontrol->value.integer.value[0] = app_type;
+	ucontrol->value.integer.value[1] = acdb_dev_id;
+	ucontrol->value.integer.value[2] = sample_rate;
+	pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fe_id, SESSION_TYPE_RX,
+		app_type, acdb_dev_id, sample_rate);
+done:
+	return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate = 48000;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s: Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		return -EINVAL;
+	}
+
+	app_type = ucontrol->value.integer.value[0];
+	acdb_dev_id = ucontrol->value.integer.value[1];
+	if (ucontrol->value.integer.value[2] != 0)
+		sample_rate = ucontrol->value.integer.value[2];
+	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n",
+		__func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_TX);
+	msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type,
+			acdb_dev_id, sample_rate, SESSION_TYPE_TX);
+
+	return 0;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int ret = 0;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s: Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_TX,
+		&app_type, &acdb_dev_id, &sample_rate);
+	if (ret < 0) {
+		pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ucontrol->value.integer.value[0] = app_type;
+	ucontrol->value.integer.value[1] = acdb_dev_id;
+	ucontrol->value.integer.value[2] = sample_rate;
+	pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fe_id, SESSION_TYPE_TX,
+		app_type, acdb_dev_id, sample_rate);
+done:
+	return ret;
+}
+
+static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm->streams[0].pcm;
+	struct snd_pcm_usr *app_type_info;
+	struct snd_kcontrol *kctl;
+	const char *playback_mixer_ctl_name	= "Audio Stream";
+	const char *capture_mixer_ctl_name	= "Audio Stream Capture";
+	const char *deviceNo		= "NN";
+	const char *suffix		= "App Type Cfg";
+	int ctl_len, ret = 0;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+				strlen(deviceNo) + 1 + strlen(suffix) + 1;
+		pr_debug("%s: Playback app type cntrl add\n", __func__);
+		ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					NULL, 1, ctl_len, rtd->dai_link->id,
+					&app_type_info);
+		if (ret < 0)
+			return ret;
+		kctl = app_type_info->kctl;
+		snprintf(kctl->id.name, ctl_len, "%s %d %s",
+			playback_mixer_ctl_name, rtd->pcm->device, suffix);
+		kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
+		kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+				strlen(deviceNo) + 1 + strlen(suffix) + 1;
+		pr_debug("%s: Capture app type cntrl add\n", __func__);
+		ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+					NULL, 1, ctl_len, rtd->dai_link->id,
+					&app_type_info);
+		if (ret < 0)
+			return ret;
+		kctl = app_type_info->kctl;
+		snprintf(kctl->id.name, ctl_len, "%s %d %s",
+			capture_mixer_ctl_name, rtd->pcm->device, suffix);
+		kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
+		kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
+	}
+
+	return 0;
+}
+
+static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+	ret = msm_pcm_add_volume_controls(rtd);
+	if (ret)
+		pr_err("%s: pcm add volume controls failed:%d\n",
+			__func__, ret);
+	ret = msm_pcm_add_app_type_controls(rtd);
+	if (ret)
+		pr_err("%s: pcm add app type controls failed:%d\n",
+			__func__, ret);
+	return ret;
+}
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	ret = msm_pcm_add_controls(rtd);
+	if (ret)
+		dev_err(rtd->dev, "%s, kctl add failed\n", __func__);
+	return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops            = &msm_pcm_ops,
+	.pcm_new        = msm_asoc_pcm_new,
+	.probe          = msm_pcm_loopback_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+	dev_dbg(&pdev->dev, "%s: dev name %s\n",
+		__func__, dev_name(&pdev->dev));
+
+	return snd_soc_register_platform(&pdev->dev,
+				   &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_pcm_loopback_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-loopback"},
+	{}
+};
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-pcm-loopback",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_loopback_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM loopback platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
new file mode 100644
index 0000000..ecf194f
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-noirq.c
@@ -0,0 +1,845 @@
+/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6audio-v2.h>
+#include <sound/timer.h>
+#include <asm/dma.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+#define PCM_MASTER_VOL_MAX_STEPS	0x2000
+static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0,
+			PCM_MASTER_VOL_MAX_STEPS);
+
+struct snd_msm {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+};
+
+#define CMD_EOS_MIN_TIMEOUT_LENGTH  50
+#define CMD_EOS_TIMEOUT_MULTIPLIER  (HZ * 50)
+
+#define ATRACE_END() \
+	trace_printk("tracing_mark_write: E\n")
+#define ATRACE_BEGIN(name) \
+	trace_printk("tracing_mark_write: B|%d|%s\n", current->tgid, name)
+#define ATRACE_FUNC() ATRACE_BEGIN(__func__)
+#define ATRACE_INT(name, value) \
+	trace_printk("tracing_mark_write: C|%d|%s|%d\n", \
+			current->tgid, name, (int)(value))
+
+#define SIO_PLAYBACK_MAX_PERIOD_SIZE PLAYBACK_MAX_PERIOD_SIZE
+#define SIO_PLAYBACK_MIN_PERIOD_SIZE 48
+#define SIO_PLAYBACK_MAX_NUM_PERIODS 512
+#define SIO_PLAYBACK_MIN_NUM_PERIODS PLAYBACK_MIN_NUM_PERIODS
+#define SIO_PLAYBACK_MIN_BYTES (SIO_PLAYBACK_MIN_NUM_PERIODS *	\
+				SIO_PLAYBACK_MIN_PERIOD_SIZE)
+
+#define SIO_PLAYBACK_MAX_BYTES ((SIO_PLAYBACK_MAX_NUM_PERIODS) *	\
+				(SIO_PLAYBACK_MAX_PERIOD_SIZE))
+
+#define SIO_CAPTURE_MAX_PERIOD_SIZE CAPTURE_MAX_PERIOD_SIZE
+#define SIO_CAPTURE_MIN_PERIOD_SIZE 48
+#define SIO_CAPTURE_MAX_NUM_PERIODS 512
+#define SIO_CAPTURE_MIN_NUM_PERIODS CAPTURE_MIN_NUM_PERIODS
+
+#define SIO_CAPTURE_MIN_BYTES (SIO_CAPTURE_MIN_NUM_PERIODS *	\
+			       SIO_CAPTURE_MIN_PERIOD_SIZE)
+
+#define SIO_CAPTURE_MAX_BYTES (SIO_CAPTURE_MAX_NUM_PERIODS *	\
+				SIO_CAPTURE_MAX_PERIOD_SIZE)
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =              (SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE),
+	.rates =                SNDRV_PCM_RATE_8000_192000,
+	.rate_min =             8000,
+	.rate_max =             192000,
+	.channels_min =         1,
+	.channels_max =         8,
+	.buffer_bytes_max =     SIO_PLAYBACK_MAX_NUM_PERIODS *
+				SIO_PLAYBACK_MAX_PERIOD_SIZE,
+	.period_bytes_min =	SIO_PLAYBACK_MIN_PERIOD_SIZE,
+	.period_bytes_max =     SIO_PLAYBACK_MAX_PERIOD_SIZE,
+	.periods_min =          SIO_PLAYBACK_MIN_NUM_PERIODS,
+	.periods_max =          SIO_PLAYBACK_MAX_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_NO_PERIOD_WAKEUP |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =              (SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE),
+	.rates =                SNDRV_PCM_RATE_8000_48000,
+	.rate_min =             8000,
+	.rate_max =             48000,
+	.channels_min =         1,
+	.channels_max =         4,
+	.buffer_bytes_max =     SIO_CAPTURE_MAX_NUM_PERIODS *
+				SIO_CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min =	SIO_CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max =     SIO_CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min =          SIO_CAPTURE_MIN_NUM_PERIODS,
+	.periods_max =          SIO_CAPTURE_MAX_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+	88200, 96000, 176400, 192000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static void event_handler(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv)
+{
+	uint32_t *ptrmem = (uint32_t *)payload;
+
+	switch (opcode) {
+	case ASM_DATA_EVENT_WATERMARK:
+		pr_debug("%s: Watermark level = 0x%08x\n", __func__, *ptrmem);
+		break;
+	case APR_BASIC_RSP_RESULT:
+		pr_debug("%s: Payload = [0x%x]stat[0x%x]\n",
+				__func__, payload[0], payload[1]);
+		switch (payload[0]) {
+		case ASM_SESSION_CMD_RUN_V2:
+		case ASM_SESSION_CMD_PAUSE:
+		case ASM_STREAM_CMD_FLUSH:
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+		break;
+	}
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd;
+	int ret = 0;
+
+	prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	prtd->substream = substream;
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = msm_pcm_hardware_playback;
+	else
+		runtime->hw = msm_pcm_hardware_capture;
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&constraints_sample_rates);
+	if (ret)
+		pr_info("snd_pcm_hw_constraint_list failed\n");
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret)
+		pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			SIO_PLAYBACK_MIN_BYTES,
+			SIO_PLAYBACK_MAX_BYTES);
+		if (ret) {
+			pr_info("%s: P buffer bytes minmax constraint ret %d\n",
+			       __func__, ret);
+		}
+	} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+			   SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			   SIO_CAPTURE_MIN_BYTES,
+			   SIO_CAPTURE_MAX_BYTES);
+		if (ret) {
+			pr_info("%s: C buffer bytes minmax constraint ret %d\n",
+			       __func__, ret);
+		}
+	}
+
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (ret) {
+		pr_err("%s: Constraint for period bytes step ret = %d\n",
+				__func__, ret);
+	}
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (ret) {
+		pr_err("%s: Constraint for buffer bytes step ret = %d\n",
+				__func__, ret);
+	}
+	prtd->audio_client = q6asm_audio_client_alloc(
+				(app_cb)event_handler, prtd);
+	if (!prtd->audio_client) {
+		pr_err("%s: client alloc failed\n", __func__);
+		ret = -ENOMEM;
+		goto fail_cmd;
+	}
+	prtd->dsp_cnt = 0;
+	prtd->set_channel_map = false;
+	runtime->private_data = prtd;
+	return 0;
+
+fail_cmd:
+	kfree(prtd);
+	return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd = runtime->private_data;
+	struct msm_plat_data *pdata;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct audio_buffer *buf;
+	struct shared_io_config config;
+	uint16_t sample_word_size;
+	uint16_t bits_per_sample;
+	int ret;
+	int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? IN : OUT;
+
+	pdata = (struct msm_plat_data *)
+		dev_get_drvdata(soc_prtd->platform->dev);
+	if (!pdata) {
+		ret = -EINVAL;
+		pr_err("%s: platform data not populated ret: %d\n", __func__,
+		       ret);
+		return ret;
+	}
+
+	/* need to set LOW_LATENCY_PCM_MODE for capture since
+	 * push mode does not support ULL
+	 */
+	prtd->audio_client->perf_mode = (dir == IN) ?
+					pdata->perf_mode :
+					LOW_LATENCY_PCM_MODE;
+
+	/* rate and channels are sent to audio driver */
+	prtd->samp_rate = params_rate(params);
+	prtd->channel_mode = params_channels(params);
+	if (prtd->enabled)
+		return 0;
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits_per_sample = 24;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		bits_per_sample = 24;
+		sample_word_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bits_per_sample = 16;
+		sample_word_size = 16;
+		break;
+	}
+
+	config.format = FORMAT_LINEAR_PCM;
+	config.bits_per_sample = bits_per_sample;
+	config.rate = params_rate(params);
+	config.channels = params_channels(params);
+	config.sample_word_size = sample_word_size;
+	config.bufsz = params_buffer_bytes(params) / params_periods(params);
+	config.bufcnt = params_periods(params);
+
+	ret = q6asm_open_shared_io(prtd->audio_client, &config, dir);
+	if (ret) {
+		pr_err("%s: q6asm_open_write_shared_io failed ret: %d\n",
+		       __func__, ret);
+		return ret;
+	}
+
+	prtd->pcm_size = params_buffer_bytes(params);
+	prtd->pcm_count = params_buffer_bytes(params);
+	prtd->pcm_irq_pos = 0;
+
+	buf = prtd->audio_client->port[dir].buf;
+	dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dma_buf->dev.dev = substream->pcm->card->dev;
+	dma_buf->private_data = NULL;
+	dma_buf->area = buf->data;
+	dma_buf->addr = buf->phys;
+	dma_buf->bytes = prtd->pcm_size;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	pr_debug("%s: session ID %d, perf %d\n", __func__,
+	       prtd->audio_client->session,
+		prtd->audio_client->perf_mode);
+	prtd->session_id = prtd->audio_client->session;
+
+	pr_debug("msm_pcm_routing_reg_phy_stream w/ id %d\n",
+		 soc_prtd->dai_link->id);
+	ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id,
+				       prtd->audio_client->perf_mode,
+				       prtd->session_id, substream->stream);
+
+	if (ret) {
+		pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+		return ret;
+	}
+
+	atomic_set(&prtd->out_count, runtime->periods);
+	prtd->enabled = 1;
+	prtd->cmd_pending = 0;
+	prtd->cmd_interrupt = 0;
+
+	return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+	struct audio_buffer *buf;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("%s: %s Trigger start\n", __func__,
+			 dir == 0 ? "P" : "C");
+		ret = q6asm_run(prtd->audio_client, 0, 0, 0);
+		if (ret)
+			break;
+		atomic_set(&prtd->start, 1);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_STOP\n", __func__);
+		atomic_set(&prtd->start, 0);
+		q6asm_cmd(prtd->audio_client, CMD_PAUSE);
+		q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+		buf = q6asm_shared_io_buf(prtd->audio_client, dir);
+		if (buf == NULL) {
+			pr_err("%s: shared IO buffer is null\n", __func__);
+			ret = -EINVAL;
+			break;
+		}
+		memset(buf->data, 0, buf->actual_size);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("%s: SNDRV_PCM_TRIGGER_PAUSE\n", __func__);
+		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+		atomic_set(&prtd->start, 0);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	int dir = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? 0 : 1;
+	struct audio_buffer *buf;
+
+	switch (cmd) {
+	case SNDRV_PCM_IOCTL1_RESET:
+		pr_debug("%s: %s SNDRV_PCM_IOCTL1_RESET\n", __func__,
+		       dir == 0 ? "P" : "C");
+		buf = q6asm_shared_io_buf(prtd->audio_client, dir);
+
+		if (buf && buf->data)
+			memset(buf->data, 0, buf->actual_size);
+		break;
+	default:
+		break;
+	}
+
+	return snd_pcm_lib_ioctl(substream, cmd, arg);
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	uint32_t read_index, wall_clk_msw, wall_clk_lsw;
+	/*these are offsets, unlike ASoC's full values*/
+	snd_pcm_sframes_t hw_ptr;
+	snd_pcm_sframes_t period_size;
+	int ret;
+	int retries = 10;
+	struct msm_audio *prtd = runtime->private_data;
+
+	period_size = runtime->period_size;
+
+	do {
+		ret = q6asm_get_shared_pos(prtd->audio_client,
+					   &read_index, &wall_clk_msw,
+					   &wall_clk_lsw);
+	} while (ret == -EAGAIN && --retries);
+
+	if (ret || !period_size) {
+		pr_err("get_shared_pos error or zero period size\n");
+		return 0;
+	}
+
+	hw_ptr = bytes_to_frames(substream->runtime,
+				 read_index);
+
+	if (runtime->control->appl_ptr == 0) {
+		pr_debug("ptr(%s): appl(0), hw = %lu read_index = %u\n",
+			 prtd->substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+			 "P" : "C",
+			 hw_ptr, read_index);
+	}
+	return (hw_ptr/period_size) * period_size;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+	 snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	return -EINVAL;
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	struct audio_client *ac = prtd->audio_client;
+	struct audio_port_data *apd = ac->port;
+	struct audio_buffer *ab;
+	int dir = -1;
+	int ret;
+
+	pr_debug("%s: mmap begin\n", __func__);
+	prtd->mmap_flag = 1;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+
+	ab = &(apd[dir].buf[0]);
+
+	ret = msm_audio_ion_mmap(ab, vma);
+
+	if (ret)
+		prtd->mmap_flag = 0;
+
+	return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+
+	if (!prtd || !prtd->mmap_flag)
+		return -EIO;
+
+	return 0;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd = runtime->private_data;
+	struct audio_client *ac = prtd->audio_client;
+	uint32_t timeout;
+	int dir = 0;
+	int ret = 0;
+
+	if (ac) {
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			dir = IN;
+		else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			dir = OUT;
+
+		/* determine timeout length */
+		if (runtime->frame_bits == 0 || runtime->rate == 0) {
+			timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+		} else {
+			timeout = (runtime->period_size *
+					CMD_EOS_TIMEOUT_MULTIPLIER) /
+					((runtime->frame_bits / 8) *
+					 runtime->rate);
+			if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH)
+				timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+		}
+
+		q6asm_cmd(ac, CMD_CLOSE);
+
+		ret = q6asm_shared_io_free(ac, dir);
+
+		if (ret) {
+			pr_err("%s: Failed to close pull mode, ret %d\n",
+					__func__, ret);
+		}
+		q6asm_audio_client_free(ac);
+	}
+	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
+					 dir == IN ?
+					 SNDRV_PCM_STREAM_PLAYBACK :
+					 SNDRV_PCM_STREAM_CAPTURE);
+	kfree(prtd);
+	return 0;
+}
+
+static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume)
+{
+	int rc = 0;
+
+	if (prtd && prtd->audio_client) {
+		pr_debug("%s: channels %d volume 0x%x\n", __func__,
+				prtd->channel_mode, volume);
+		rc = q6asm_set_volume(prtd->audio_client, volume);
+		if (rc < 0) {
+			pr_err("%s: Send Volume command failed rc=%d\n",
+					__func__, rc);
+		}
+	}
+	return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+		vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s\n", __func__);
+	if (!substream) {
+		pr_err("%s substream not found\n", __func__);
+		return -ENODEV;
+	}
+	if (!substream->runtime) {
+		pr_err("%s substream runtime not found\n", __func__);
+		return 0;
+	}
+	prtd = substream->runtime->private_data;
+	if (prtd)
+		ucontrol->value.integer.value[0] = prtd->volume;
+	return 0;
+}
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+		vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_audio *prtd;
+	int volume = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: volume : 0x%x\n", __func__, volume);
+	if (!substream) {
+		pr_err("%s substream not found\n", __func__);
+		return -ENODEV;
+	}
+	if (!substream->runtime) {
+		pr_err("%s substream runtime not found\n", __func__);
+		return 0;
+	}
+	prtd = substream->runtime->private_data;
+	if (prtd) {
+		rc = msm_pcm_set_volume(prtd, volume);
+		prtd->volume = volume;
+	}
+	return rc;
+}
+
+static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_volume *volume_info;
+	struct snd_kcontrol *kctl;
+
+	dev_dbg(rtd->dev, "%s, Volume control add\n", __func__);
+	ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			NULL, 1, rtd->dai_link->id,
+			&volume_info);
+	if (ret < 0) {
+		pr_err("%s volume control failed ret %d\n", __func__, ret);
+		return ret;
+	}
+	kctl = volume_info->kctl;
+	kctl->put = msm_pcm_volume_ctl_put;
+	kctl->get = msm_pcm_volume_ctl_get;
+	kctl->tlv.p = msm_pcm_vol_gain;
+	return 0;
+}
+
+static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_pcm_substream *substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s", __func__);
+	substream = snd_pcm_chmap_substream(info, idx);
+	if (!substream)
+		return -ENODEV;
+	if (!substream->runtime)
+		return 0;
+
+	prtd = substream->runtime->private_data;
+	if (prtd) {
+		prtd->set_channel_map = true;
+			for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+				prtd->channel_map[i] =
+				(char)(ucontrol->value.integer.value[i]);
+	}
+	return 0;
+}
+
+static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_pcm_substream *substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s", __func__);
+	substream = snd_pcm_chmap_substream(info, idx);
+	if (!substream)
+		return -ENODEV;
+	memset(ucontrol->value.integer.value, 0,
+		sizeof(ucontrol->value.integer.value));
+	if (!substream->runtime)
+		return 0; /* no channels set */
+
+	prtd = substream->runtime->private_data;
+
+	if (prtd && prtd->set_channel_map == true) {
+		for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+			ucontrol->value.integer.value[i] =
+					(int)prtd->channel_map[i];
+	} else {
+		for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+			ucontrol->value.integer.value[i] = 0;
+	}
+
+	return 0;
+}
+
+static int msm_pcm_add_chmap_control(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_chmap *chmap_info;
+	struct snd_kcontrol *kctl;
+	char device_num[12];
+	int i, ret;
+
+	pr_debug("%s, Channel map cntrl add\n", __func__);
+	ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				     snd_pcm_std_chmaps,
+				     PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+				     &chmap_info);
+	if (ret)
+		return ret;
+
+	kctl = chmap_info->kctl;
+	for (i = 0; i < kctl->count; i++)
+		kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+	snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+	strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+	pr_debug("%s, Overwriting channel map control name to: %s",
+		__func__, kctl->id.name);
+	kctl->put = msm_pcm_chmap_ctl_put;
+	kctl->get = msm_pcm_chmap_ctl_get;
+	return 0;
+}
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	struct snd_pcm *pcm = rtd->pcm;
+	int ret;
+
+	pr_debug("%s , register new control\n", __func__);
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	ret = msm_pcm_add_chmap_control(rtd);
+	if (ret) {
+		pr_err("%s failed to add chmap cntls\n", __func__);
+		goto exit;
+	}
+	ret = msm_pcm_add_volume_control(rtd);
+	if (ret) {
+		pr_err("%s: Could not add pcm Volume Control %d\n",
+			__func__, ret);
+	}
+	pcm->nonatomic = true;
+exit:
+	return ret;
+}
+
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open           = msm_pcm_open,
+	.prepare        = msm_pcm_prepare,
+	.copy           = msm_pcm_copy,
+	.hw_params	= msm_pcm_hw_params,
+	.ioctl          = msm_pcm_ioctl,
+	.trigger        = msm_pcm_trigger,
+	.pointer        = msm_pcm_pointer,
+	.mmap           = msm_pcm_mmap,
+	.close          = msm_pcm_close,
+};
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+	int rc;
+	struct msm_plat_data *pdata;
+	const char *latency_level;
+	int perf_mode = LOW_LATENCY_PCM_MODE;
+
+	dev_dbg(&pdev->dev, "Pull mode driver probe\n");
+
+	if (of_property_read_bool(pdev->dev.of_node,
+				  "qcom,msm-pcm-low-latency")) {
+
+		rc = of_property_read_string(pdev->dev.of_node,
+			"qcom,latency-level", &latency_level);
+		if (!rc && !strcmp(latency_level, "ultra"))
+			perf_mode = ULTRA_LOW_LATENCY_PCM_MODE;
+	}
+
+	pdata = devm_kzalloc(&pdev->dev,
+			     sizeof(struct msm_plat_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	pdata->perf_mode = perf_mode;
+
+	dev_set_drvdata(&pdev->dev, pdata);
+
+	dev_dbg(&pdev->dev, "%s: dev name %s\n",
+				__func__, dev_name(&pdev->dev));
+	dev_dbg(&pdev->dev, "Pull mode driver register\n");
+	rc = snd_soc_register_platform(&pdev->dev,
+				       &msm_soc_platform);
+
+	if (rc)
+		dev_err(&pdev->dev, "Failed to register pull mode driver\n");
+
+	return rc;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	struct msm_plat_data *pdata;
+
+	dev_dbg(&pdev->dev, "Pull mode remove\n");
+	pdata = dev_get_drvdata(&pdev->dev);
+	devm_kfree(&pdev->dev, pdata);
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+static const struct of_device_id msm_pcm_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-dsp-noirq"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_dt_match);
+
+static struct platform_driver msm_pcm_driver_noirq = {
+	.driver = {
+		.name = "msm-pcm-dsp-noirq",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	return platform_driver_register(&msm_pcm_driver_noirq);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver_noirq);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM NOIRQ module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
new file mode 100644
index 0000000..e899f5e
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.c
@@ -0,0 +1,1515 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6audio-v2.h>
+#include <sound/timer.h>
+#include <asm/dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/msm_audio_ion.h>
+
+#include <linux/of_device.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+
+enum stream_state {
+	IDLE = 0,
+	STOPPED,
+	RUNNING,
+};
+
+static struct audio_locks the_locks;
+
+#define PCM_MASTER_VOL_MAX_STEPS	0x2000
+static const DECLARE_TLV_DB_LINEAR(msm_pcm_vol_gain, 0,
+			PCM_MASTER_VOL_MAX_STEPS);
+
+struct snd_msm {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+};
+
+#define CMD_EOS_MIN_TIMEOUT_LENGTH  50
+#define CMD_EOS_TIMEOUT_MULTIPLIER  (HZ * 50)
+#define MAX_PB_COPY_RETRIES         3
+
+static struct snd_pcm_hardware msm_pcm_hardware_capture = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =              (SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE |
+				SNDRV_PCM_FMTBIT_S32_LE),
+	.rates =                SNDRV_PCM_RATE_8000_384000,
+	.rate_min =             8000,
+	.rate_max =             384000,
+	.channels_min =         1,
+	.channels_max =         4,
+	.buffer_bytes_max =     CAPTURE_MAX_NUM_PERIODS *
+				CAPTURE_MAX_PERIOD_SIZE,
+	.period_bytes_min =	CAPTURE_MIN_PERIOD_SIZE,
+	.period_bytes_max =     CAPTURE_MAX_PERIOD_SIZE,
+	.periods_min =          CAPTURE_MIN_NUM_PERIODS,
+	.periods_max =          CAPTURE_MAX_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+static struct snd_pcm_hardware msm_pcm_hardware_playback = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE | SNDRV_PCM_INFO_RESUME),
+	.formats =              (SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_S24_LE |
+				SNDRV_PCM_FMTBIT_S24_3LE |
+				SNDRV_PCM_FMTBIT_S32_LE),
+	.rates =                SNDRV_PCM_RATE_8000_384000,
+	.rate_min =             8000,
+	.rate_max =             384000,
+	.channels_min =         1,
+	.channels_max =         8,
+	.buffer_bytes_max =     PLAYBACK_MAX_NUM_PERIODS *
+				PLAYBACK_MAX_PERIOD_SIZE,
+	.period_bytes_min =	PLAYBACK_MIN_PERIOD_SIZE,
+	.period_bytes_max =     PLAYBACK_MAX_PERIOD_SIZE,
+	.periods_min =          PLAYBACK_MIN_NUM_PERIODS,
+	.periods_max =          PLAYBACK_MAX_NUM_PERIODS,
+	.fifo_size =            0,
+};
+
+/* Conventional and unconventional sample rate supported */
+static unsigned int supported_sample_rates[] = {
+	8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000,
+	88200, 96000, 176400, 192000, 384000
+};
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static void msm_pcm_route_event_handler(enum msm_pcm_routing_event event,
+					void *priv_data)
+{
+	struct msm_audio *prtd = priv_data;
+
+	WARN_ON(!prtd);
+
+	pr_debug("%s: event %x\n", __func__, event);
+
+	switch (event) {
+	case MSM_PCM_RT_EVT_BUF_RECFG:
+		q6asm_cmd(prtd->audio_client, CMD_PAUSE);
+		q6asm_cmd(prtd->audio_client, CMD_FLUSH);
+		q6asm_run(prtd->audio_client, 0, 0, 0);
+		/* fallthrough */
+	default:
+		break;
+	}
+}
+
+static void event_handler(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv)
+{
+	struct msm_audio *prtd = priv;
+	struct snd_pcm_substream *substream = prtd->substream;
+	uint32_t *ptrmem = (uint32_t *)payload;
+	uint32_t idx = 0;
+	uint32_t size = 0;
+	uint8_t buf_index;
+
+	switch (opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2: {
+		pr_debug("ASM_DATA_EVENT_WRITE_DONE_V2\n");
+		pr_debug("Buffer Consumed = 0x%08x\n", *ptrmem);
+		prtd->pcm_irq_pos += prtd->pcm_count;
+		if (atomic_read(&prtd->start))
+			snd_pcm_period_elapsed(substream);
+		atomic_inc(&prtd->out_count);
+		wake_up(&the_locks.write_wait);
+		if (!atomic_read(&prtd->start))
+			break;
+		if (!prtd->mmap_flag || prtd->reset_event)
+			break;
+		if (q6asm_is_cpu_buf_avail_nolock(IN,
+				prtd->audio_client,
+				&size, &idx)) {
+			pr_debug("%s:writing %d bytes of buffer to dsp 2\n",
+					__func__, prtd->pcm_count);
+			q6asm_write_nolock(prtd->audio_client,
+				prtd->pcm_count, 0, 0, NO_TIMESTAMP);
+		}
+		break;
+	}
+	case ASM_DATA_EVENT_RENDERED_EOS:
+		pr_debug("ASM_DATA_EVENT_RENDERED_EOS\n");
+		clear_bit(CMD_EOS, &prtd->cmd_pending);
+		wake_up(&the_locks.eos_wait);
+		break;
+	case ASM_DATA_EVENT_READ_DONE_V2: {
+		pr_debug("ASM_DATA_EVENT_READ_DONE_V2\n");
+		buf_index = q6asm_get_buf_index_from_token(token);
+		pr_debug("%s: token=0x%08x buf_index=0x%08x\n",
+			 __func__, token, buf_index);
+		prtd->in_frame_info[buf_index].size = payload[4];
+		prtd->in_frame_info[buf_index].offset = payload[5];
+		/* assume data size = 0 during flushing */
+		if (prtd->in_frame_info[buf_index].size) {
+			prtd->pcm_irq_pos +=
+				prtd->in_frame_info[buf_index].size;
+			pr_debug("pcm_irq_pos=%d\n", prtd->pcm_irq_pos);
+			if (atomic_read(&prtd->start))
+				snd_pcm_period_elapsed(substream);
+			if (atomic_read(&prtd->in_count) <= prtd->periods)
+				atomic_inc(&prtd->in_count);
+			wake_up(&the_locks.read_wait);
+			if (prtd->mmap_flag &&
+			    q6asm_is_cpu_buf_avail_nolock(OUT,
+				prtd->audio_client,
+				&size, &idx) &&
+			    (substream->runtime->status->state ==
+			     SNDRV_PCM_STATE_RUNNING))
+				q6asm_read_nolock(prtd->audio_client);
+		} else {
+			pr_debug("%s: reclaim flushed buf in_count %x\n",
+				__func__, atomic_read(&prtd->in_count));
+			prtd->pcm_irq_pos += prtd->pcm_count;
+			if (prtd->mmap_flag) {
+				if (q6asm_is_cpu_buf_avail_nolock(OUT,
+				    prtd->audio_client,
+				    &size, &idx) &&
+				    (substream->runtime->status->state ==
+				    SNDRV_PCM_STATE_RUNNING))
+					q6asm_read_nolock(prtd->audio_client);
+			} else {
+				atomic_inc(&prtd->in_count);
+			}
+			if (atomic_read(&prtd->in_count) == prtd->periods) {
+				pr_info("%s: reclaimed all bufs\n", __func__);
+				if (atomic_read(&prtd->start))
+					snd_pcm_period_elapsed(substream);
+				wake_up(&the_locks.read_wait);
+			}
+		}
+		break;
+	}
+	case APR_BASIC_RSP_RESULT: {
+		switch (payload[0]) {
+		case ASM_SESSION_CMD_RUN_V2:
+			if (substream->stream
+				!= SNDRV_PCM_STREAM_PLAYBACK) {
+				atomic_set(&prtd->start, 1);
+				break;
+			}
+			if (prtd->mmap_flag) {
+				pr_debug("%s:writing %d bytes of buffer to dsp\n",
+					__func__,
+					prtd->pcm_count);
+				q6asm_write_nolock(prtd->audio_client,
+					prtd->pcm_count,
+					0, 0, NO_TIMESTAMP);
+			} else {
+				while (atomic_read(&prtd->out_needed)) {
+					pr_debug("%s:writing %d bytes of buffer to dsp\n",
+						__func__,
+						prtd->pcm_count);
+					q6asm_write_nolock(prtd->audio_client,
+						prtd->pcm_count,
+						0, 0, NO_TIMESTAMP);
+					atomic_dec(&prtd->out_needed);
+					wake_up(&the_locks.write_wait);
+				};
+			}
+			atomic_set(&prtd->start, 1);
+			break;
+		default:
+			pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+				__func__, payload[0], payload[1]);
+			break;
+		}
+	}
+	break;
+	case RESET_EVENTS:
+		pr_debug("%s RESET_EVENTS\n", __func__);
+		prtd->pcm_irq_pos += prtd->pcm_count;
+		atomic_inc(&prtd->out_count);
+		atomic_inc(&prtd->in_count);
+		prtd->reset_event = true;
+		if (atomic_read(&prtd->start))
+			snd_pcm_period_elapsed(substream);
+		wake_up(&the_locks.eos_wait);
+		wake_up(&the_locks.write_wait);
+		wake_up(&the_locks.read_wait);
+		break;
+	default:
+		pr_debug("Not Supported Event opcode[0x%x]\n", opcode);
+		break;
+	}
+}
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd = runtime->private_data;
+	struct msm_plat_data *pdata;
+	struct snd_pcm_hw_params *params;
+	int ret;
+	uint16_t bits_per_sample;
+	uint16_t sample_word_size;
+
+	pdata = (struct msm_plat_data *)
+		dev_get_drvdata(soc_prtd->platform->dev);
+	if (!pdata) {
+		pr_err("%s: platform data not populated\n", __func__);
+		return -EINVAL;
+	}
+	if (!prtd || !prtd->audio_client) {
+		pr_err("%s: private data null or audio client freed\n",
+			__func__);
+		return -EINVAL;
+	}
+	params = &soc_prtd->dpcm[substream->stream].hw_params;
+
+	pr_debug("%s\n", __func__);
+	prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_irq_pos = 0;
+	/* rate and channels are sent to audio driver */
+	prtd->samp_rate = runtime->rate;
+	prtd->channel_mode = runtime->channels;
+	if (prtd->enabled)
+		return 0;
+
+	prtd->audio_client->perf_mode = pdata->perf_mode;
+	pr_debug("%s: perf: %x\n", __func__, pdata->perf_mode);
+
+	switch (params_format(params)) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bits_per_sample = 32;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits_per_sample = 24;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		bits_per_sample = 24;
+		sample_word_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bits_per_sample = 16;
+		sample_word_size = 16;
+		break;
+	}
+
+	ret = q6asm_open_write_v4(prtd->audio_client,
+				  FORMAT_LINEAR_PCM, bits_per_sample);
+
+	if (ret < 0) {
+		pr_err("%s: q6asm_open_write_v2 failed\n", __func__);
+		q6asm_audio_client_free(prtd->audio_client);
+		prtd->audio_client = NULL;
+		return -ENOMEM;
+	}
+
+	ret = q6asm_send_cal(prtd->audio_client);
+	if (ret < 0)
+		pr_debug("%s : Send cal failed : %d", __func__, ret);
+
+	pr_debug("%s: session ID %d\n", __func__,
+			prtd->audio_client->session);
+	prtd->session_id = prtd->audio_client->session;
+	ret = msm_pcm_routing_reg_phy_stream(soc_prtd->dai_link->id,
+			prtd->audio_client->perf_mode,
+			prtd->session_id, substream->stream);
+	if (ret) {
+		pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = q6asm_media_format_block_multi_ch_pcm_v4(
+				prtd->audio_client, runtime->rate,
+				runtime->channels, !prtd->set_channel_map,
+				prtd->channel_map, bits_per_sample,
+				sample_word_size, ASM_LITTLE_ENDIAN,
+				DEFAULT_QF);
+	if (ret < 0)
+		pr_info("%s: CMD Format block failed\n", __func__);
+
+	atomic_set(&prtd->out_count, runtime->periods);
+
+	prtd->enabled = 1;
+	prtd->cmd_pending = 0;
+	prtd->cmd_interrupt = 0;
+
+	return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_plat_data *pdata;
+	struct snd_pcm_hw_params *params;
+	struct msm_pcm_routing_evt event;
+	int ret = 0;
+	int i = 0;
+	uint16_t bits_per_sample = 16;
+	uint16_t sample_word_size;
+
+	pdata = (struct msm_plat_data *)
+		dev_get_drvdata(soc_prtd->platform->dev);
+	if (!pdata) {
+		pr_err("%s: platform data not populated\n", __func__);
+		return -EINVAL;
+	}
+	if (!prtd || !prtd->audio_client) {
+		pr_err("%s: private data null or audio client freed\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	if (prtd->enabled == IDLE) {
+		pr_debug("%s:perf_mode=%d periods=%d\n", __func__,
+			pdata->perf_mode, runtime->periods);
+		params = &soc_prtd->dpcm[substream->stream].hw_params;
+		if ((params_format(params) == SNDRV_PCM_FORMAT_S24_LE) ||
+			(params_format(params) == SNDRV_PCM_FORMAT_S24_3LE))
+			bits_per_sample = 24;
+		else if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+			bits_per_sample = 32;
+
+		/* ULL mode is not supported in capture path */
+		if (pdata->perf_mode == LEGACY_PCM_MODE)
+			prtd->audio_client->perf_mode = LEGACY_PCM_MODE;
+		else
+			prtd->audio_client->perf_mode = LOW_LATENCY_PCM_MODE;
+
+		pr_debug("%s Opening %d-ch PCM read stream, perf_mode %d\n",
+				__func__, params_channels(params),
+				prtd->audio_client->perf_mode);
+
+		ret = q6asm_open_read_v4(prtd->audio_client, FORMAT_LINEAR_PCM,
+				bits_per_sample);
+		if (ret < 0) {
+			pr_err("%s: q6asm_open_read failed\n", __func__);
+			q6asm_audio_client_free(prtd->audio_client);
+			prtd->audio_client = NULL;
+			return -ENOMEM;
+		}
+
+		ret = q6asm_send_cal(prtd->audio_client);
+		if (ret < 0)
+			pr_debug("%s : Send cal failed : %d", __func__, ret);
+
+		pr_debug("%s: session ID %d\n",
+				__func__, prtd->audio_client->session);
+		prtd->session_id = prtd->audio_client->session;
+		event.event_func = msm_pcm_route_event_handler;
+		event.priv_data = (void *) prtd;
+		ret = msm_pcm_routing_reg_phy_stream_v2(
+				soc_prtd->dai_link->id,
+				prtd->audio_client->perf_mode,
+				prtd->session_id, substream->stream,
+				event);
+		if (ret) {
+			pr_err("%s: stream reg failed ret:%d\n", __func__, ret);
+			return ret;
+		}
+	}
+
+	prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_irq_pos = 0;
+	/* rate and channels are sent to audio driver */
+	prtd->samp_rate = runtime->rate;
+	prtd->channel_mode = runtime->channels;
+
+	if (prtd->enabled == IDLE || prtd->enabled == STOPPED) {
+		for (i = 0; i < runtime->periods; i++)
+			q6asm_read(prtd->audio_client);
+		prtd->periods = runtime->periods;
+	}
+
+	if (prtd->enabled != IDLE)
+		return 0;
+
+	switch (runtime->format) {
+	case SNDRV_PCM_FORMAT_S32_LE:
+		bits_per_sample = 32;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S24_LE:
+		bits_per_sample = 24;
+		sample_word_size = 32;
+		break;
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		bits_per_sample = 24;
+		sample_word_size = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bits_per_sample = 16;
+		sample_word_size = 16;
+		break;
+	}
+
+	pr_debug("%s: Samp_rate = %d Channel = %d bit width = %d, word size = %d\n",
+			__func__, prtd->samp_rate, prtd->channel_mode,
+			bits_per_sample, sample_word_size);
+	ret = q6asm_enc_cfg_blk_pcm_format_support_v4(prtd->audio_client,
+						      prtd->samp_rate,
+						      prtd->channel_mode,
+						      bits_per_sample,
+						      sample_word_size,
+						      ASM_LITTLE_ENDIAN,
+						      DEFAULT_QF);
+	if (ret < 0)
+		pr_debug("%s: cmd cfg pcm was block failed", __func__);
+
+	prtd->enabled = RUNNING;
+
+	return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("%s: Trigger start\n", __func__);
+		ret = q6asm_run_nowait(prtd->audio_client, 0, 0, 0);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+		atomic_set(&prtd->start, 0);
+		if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
+			prtd->enabled = STOPPED;
+			ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+			break;
+		}
+		/* pending CMD_EOS isn't expected */
+		WARN_ON_ONCE(test_bit(CMD_EOS, &prtd->cmd_pending));
+		set_bit(CMD_EOS, &prtd->cmd_pending);
+		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_EOS);
+		if (ret)
+			clear_bit(CMD_EOS, &prtd->cmd_pending);
+		break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("SNDRV_PCM_TRIGGER_PAUSE\n");
+		ret = q6asm_cmd_nowait(prtd->audio_client, CMD_PAUSE);
+		atomic_set(&prtd->start, 0);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd;
+	int ret = 0;
+
+	prtd = kzalloc(sizeof(struct msm_audio), GFP_KERNEL);
+	if (prtd == NULL)
+		return -ENOMEM;
+
+	prtd->substream = substream;
+	prtd->audio_client = q6asm_audio_client_alloc(
+				(app_cb)event_handler, prtd);
+	if (!prtd->audio_client) {
+		pr_info("%s: Could not allocate memory\n", __func__);
+		kfree(prtd);
+		return -ENOMEM;
+	}
+
+	prtd->audio_client->dev = soc_prtd->platform->dev;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		runtime->hw = msm_pcm_hardware_playback;
+
+	/* Capture path */
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		runtime->hw = msm_pcm_hardware_capture;
+	else {
+		pr_err("Invalid Stream type %d\n", substream->stream);
+		return -EINVAL;
+	}
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+				SNDRV_PCM_HW_PARAM_RATE,
+				&constraints_sample_rates);
+	if (ret < 0)
+		pr_info("snd_pcm_hw_constraint_list failed\n");
+	/* Ensure that buffer size is a multiple of period size */
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					    SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0)
+		pr_info("snd_pcm_hw_constraint_integer failed\n");
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			PLAYBACK_MIN_NUM_PERIODS * PLAYBACK_MIN_PERIOD_SIZE,
+			PLAYBACK_MAX_NUM_PERIODS * PLAYBACK_MAX_PERIOD_SIZE);
+		if (ret < 0) {
+			pr_err("constraint for buffer bytes min max ret = %d\n",
+									ret);
+		}
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		ret = snd_pcm_hw_constraint_minmax(runtime,
+			SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+			CAPTURE_MIN_NUM_PERIODS * CAPTURE_MIN_PERIOD_SIZE,
+			CAPTURE_MAX_NUM_PERIODS * CAPTURE_MAX_PERIOD_SIZE);
+		if (ret < 0) {
+			pr_err("constraint for buffer bytes min max ret = %d\n",
+									ret);
+		}
+	}
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (ret < 0) {
+		pr_err("constraint for period bytes step ret = %d\n",
+								ret);
+	}
+	ret = snd_pcm_hw_constraint_step(runtime, 0,
+		SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (ret < 0) {
+		pr_err("constraint for buffer bytes step ret = %d\n",
+								ret);
+	}
+
+	prtd->enabled = IDLE;
+	prtd->dsp_cnt = 0;
+	prtd->set_channel_map = false;
+	prtd->reset_event = false;
+	runtime->private_data = prtd;
+
+	return 0;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+	snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	int fbytes = 0;
+	int xfer = 0;
+	char *bufptr = NULL;
+	void *data = NULL;
+	uint32_t idx = 0;
+	uint32_t size = 0;
+	uint32_t retries = 0;
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+
+	fbytes = frames_to_bytes(runtime, frames);
+	pr_debug("%s: prtd->out_count = %d\n",
+				__func__, atomic_read(&prtd->out_count));
+
+	while ((fbytes > 0) && (retries < MAX_PB_COPY_RETRIES)) {
+		if (prtd->reset_event) {
+			pr_err("%s: In SSR return ENETRESET before wait\n",
+				__func__);
+			return -ENETRESET;
+		}
+
+		ret = wait_event_timeout(the_locks.write_wait,
+				(atomic_read(&prtd->out_count)), 5 * HZ);
+		if (!ret) {
+			pr_err("%s: wait_event_timeout failed\n", __func__);
+			ret = -ETIMEDOUT;
+			goto fail;
+		}
+		ret = 0;
+
+		if (prtd->reset_event) {
+			pr_err("%s: In SSR return ENETRESET after wait\n",
+				__func__);
+			return -ENETRESET;
+		}
+
+		if (!atomic_read(&prtd->out_count)) {
+			pr_err("%s: pcm stopped out_count 0\n", __func__);
+			return 0;
+		}
+
+		data = q6asm_is_cpu_buf_avail(IN, prtd->audio_client, &size,
+			&idx);
+		if (data == NULL) {
+			retries++;
+			continue;
+		} else {
+			retries = 0;
+		}
+
+		if (fbytes > size)
+			xfer = size;
+		else
+			xfer = fbytes;
+
+		bufptr = data;
+		if (bufptr) {
+			pr_debug("%s:fbytes =%d: xfer=%d size=%d\n",
+						__func__, fbytes, xfer, size);
+			if (copy_from_user(bufptr, buf, xfer)) {
+				ret = -EFAULT;
+				pr_err("%s: copy_from_user failed\n",
+					__func__);
+				q6asm_cpu_buf_release(IN, prtd->audio_client);
+				goto fail;
+			}
+			buf += xfer;
+			fbytes -= xfer;
+			pr_debug("%s:fbytes = %d: xfer=%d\n", __func__, fbytes,
+				xfer);
+			if (atomic_read(&prtd->start)) {
+				pr_debug("%s:writing %d bytes of buffer to dsp\n",
+						__func__, xfer);
+				ret = q6asm_write(prtd->audio_client, xfer,
+							0, 0, NO_TIMESTAMP);
+				if (ret < 0) {
+					ret = -EFAULT;
+					q6asm_cpu_buf_release(IN,
+						prtd->audio_client);
+					goto fail;
+				}
+			} else
+				atomic_inc(&prtd->out_needed);
+			atomic_dec(&prtd->out_count);
+		}
+	}
+fail:
+	if (retries >= MAX_PB_COPY_RETRIES)
+		ret = -ENOMEM;
+
+	return  ret;
+}
+
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd = runtime->private_data;
+	uint32_t timeout;
+	int dir = 0;
+	int ret = 0;
+
+	pr_debug("%s: cmd_pending 0x%lx\n", __func__, prtd->cmd_pending);
+
+	if (prtd->audio_client) {
+		dir = IN;
+
+		/* determine timeout length */
+		if (runtime->frame_bits == 0 || runtime->rate == 0) {
+			timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+		} else {
+			timeout = (runtime->period_size *
+					CMD_EOS_TIMEOUT_MULTIPLIER) /
+					((runtime->frame_bits / 8) *
+					 runtime->rate);
+			if (timeout < CMD_EOS_MIN_TIMEOUT_LENGTH)
+				timeout = CMD_EOS_MIN_TIMEOUT_LENGTH;
+		}
+		pr_debug("%s: CMD_EOS timeout is %d\n", __func__, timeout);
+
+		ret = wait_event_timeout(the_locks.eos_wait,
+					 !test_bit(CMD_EOS, &prtd->cmd_pending),
+					 timeout);
+		if (!ret)
+			pr_err("%s: CMD_EOS failed, cmd_pending 0x%lx\n",
+			       __func__, prtd->cmd_pending);
+		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+		q6asm_audio_client_buf_free_contiguous(dir,
+					prtd->audio_client);
+		q6asm_audio_client_free(prtd->audio_client);
+	}
+	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
+						SNDRV_PCM_STREAM_PLAYBACK);
+	kfree(prtd);
+	return 0;
+}
+
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+		 int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+						 snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	int fbytes = 0;
+	int xfer;
+	char *bufptr;
+	void *data = NULL;
+	static uint32_t idx;
+	static uint32_t size;
+	uint32_t offset = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = substream->runtime->private_data;
+
+
+	pr_debug("%s\n", __func__);
+	fbytes = frames_to_bytes(runtime, frames);
+
+	pr_debug("appl_ptr %d\n", (int)runtime->control->appl_ptr);
+	pr_debug("hw_ptr %d\n", (int)runtime->status->hw_ptr);
+	pr_debug("avail_min %d\n", (int)runtime->control->avail_min);
+
+	if (prtd->reset_event) {
+		pr_err("%s: In SSR return ENETRESET before wait\n", __func__);
+		return -ENETRESET;
+	}
+	ret = wait_event_timeout(the_locks.read_wait,
+			(atomic_read(&prtd->in_count)), 5 * HZ);
+	if (!ret) {
+		pr_debug("%s: wait_event_timeout failed\n", __func__);
+		goto fail;
+	}
+	if (prtd->reset_event) {
+		pr_err("%s: In SSR return ENETRESET after wait\n", __func__);
+		return -ENETRESET;
+	}
+	if (!atomic_read(&prtd->in_count)) {
+		pr_debug("%s: pcm stopped in_count 0\n", __func__);
+		return 0;
+	}
+	pr_debug("Checking if valid buffer is available...%pK\n",
+						data);
+	data = q6asm_is_cpu_buf_avail(OUT, prtd->audio_client, &size, &idx);
+	bufptr = data;
+	pr_debug("Size = %d\n", size);
+	pr_debug("fbytes = %d\n", fbytes);
+	pr_debug("idx = %d\n", idx);
+	if (bufptr) {
+		xfer = fbytes;
+		if (xfer > size)
+			xfer = size;
+		offset = prtd->in_frame_info[idx].offset;
+		pr_debug("Offset value = %d\n", offset);
+		if (copy_to_user(buf, bufptr+offset, xfer)) {
+			pr_err("Failed to copy buf to user\n");
+			ret = -EFAULT;
+			q6asm_cpu_buf_release(OUT, prtd->audio_client);
+			goto fail;
+		}
+		fbytes -= xfer;
+		size -= xfer;
+		prtd->in_frame_info[idx].offset += xfer;
+		pr_debug("%s:fbytes = %d: size=%d: xfer=%d\n",
+					__func__, fbytes, size, xfer);
+		pr_debug(" Sending next buffer to dsp\n");
+		memset(&prtd->in_frame_info[idx], 0,
+		       sizeof(struct msm_audio_in_frame_info));
+		atomic_dec(&prtd->in_count);
+		ret = q6asm_read(prtd->audio_client);
+		if (ret < 0) {
+			pr_err("q6asm read failed\n");
+			ret = -EFAULT;
+			q6asm_cpu_buf_release(OUT, prtd->audio_client);
+			goto fail;
+		}
+	} else
+		pr_err("No valid buffer\n");
+
+	pr_debug("Returning from capture_copy... %d\n", ret);
+fail:
+	return ret;
+}
+
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_soc_pcm_runtime *soc_prtd = substream->private_data;
+	struct msm_audio *prtd = runtime->private_data;
+	int dir = OUT;
+
+	pr_debug("%s\n", __func__);
+	if (prtd->audio_client) {
+		q6asm_cmd(prtd->audio_client, CMD_CLOSE);
+		q6asm_audio_client_buf_free_contiguous(dir,
+				prtd->audio_client);
+		q6asm_audio_client_free(prtd->audio_client);
+	}
+
+	msm_pcm_routing_dereg_phy_stream(soc_prtd->dai_link->id,
+		SNDRV_PCM_STREAM_CAPTURE);
+	kfree(prtd);
+
+	return 0;
+}
+
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+	 snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+	return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_close(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_close(substream);
+	return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_prepare(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_prepare(substream);
+	return ret;
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+
+	if (prtd->pcm_irq_pos >= prtd->pcm_size)
+		prtd->pcm_irq_pos = 0;
+
+	pr_debug("pcm_irq_pos = %d\n", prtd->pcm_irq_pos);
+	return bytes_to_frames(runtime, (prtd->pcm_irq_pos));
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+				struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	struct audio_client *ac = prtd->audio_client;
+	struct audio_port_data *apd = ac->port;
+	struct audio_buffer *ab;
+	int dir = -1;
+
+	prtd->mmap_flag = 1;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+	ab = &(apd[dir].buf[0]);
+
+	return msm_audio_ion_mmap(ab, vma);
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct audio_buffer *buf;
+	int dir, ret;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		dir = IN;
+	else
+		dir = OUT;
+	ret = q6asm_audio_client_buf_alloc_contiguous(dir,
+			prtd->audio_client,
+			(params_buffer_bytes(params) / params_periods(params)),
+			 params_periods(params));
+	if (ret < 0) {
+		pr_err("Audio Start: Buffer Allocation failed rc = %d\n",
+							ret);
+		return -ENOMEM;
+	}
+	buf = prtd->audio_client->port[dir].buf;
+	if (buf == NULL || buf[0].data == NULL)
+		return -ENOMEM;
+
+	pr_debug("%s:buf = %pK\n", __func__, buf);
+	dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dma_buf->dev.dev = substream->pcm->card->dev;
+	dma_buf->private_data = NULL;
+	dma_buf->area = buf[0].data;
+	dma_buf->addr =  buf[0].phys;
+	dma_buf->bytes = params_buffer_bytes(params);
+	if (!dma_buf->area)
+		return -ENOMEM;
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+	return 0;
+}
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open           = msm_pcm_open,
+	.copy		= msm_pcm_copy,
+	.hw_params	= msm_pcm_hw_params,
+	.close          = msm_pcm_close,
+	.ioctl          = snd_pcm_lib_ioctl,
+	.prepare        = msm_pcm_prepare,
+	.trigger        = msm_pcm_trigger,
+	.pointer        = msm_pcm_pointer,
+	.mmap		= msm_pcm_mmap,
+};
+
+static int msm_pcm_set_volume(struct msm_audio *prtd, uint32_t volume)
+{
+	int rc = 0;
+
+	if (prtd && prtd->audio_client) {
+		pr_debug("%s: channels %d volume 0x%x\n", __func__,
+				prtd->channel_mode, volume);
+		rc = q6asm_set_volume(prtd->audio_client, volume);
+		if (rc < 0) {
+			pr_err("%s: Send Volume command failed rc=%d\n",
+					__func__, rc);
+		}
+	}
+	return rc;
+}
+
+static int msm_pcm_volume_ctl_get(struct snd_kcontrol *kcontrol,
+		      struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+		vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s\n", __func__);
+	if (!substream) {
+		pr_err("%s substream not found\n", __func__);
+		return -ENODEV;
+	}
+	if (!substream->runtime) {
+		pr_err("%s substream runtime not found\n", __func__);
+		return 0;
+	}
+	prtd = substream->runtime->private_data;
+	if (prtd)
+		ucontrol->value.integer.value[0] = prtd->volume;
+	return 0;
+}
+
+static int msm_pcm_volume_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+	struct snd_pcm_volume *vol = snd_kcontrol_chip(kcontrol);
+	struct snd_pcm_substream *substream =
+		vol->pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+	struct msm_audio *prtd;
+	int volume = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: volume : 0x%x\n", __func__, volume);
+	if (!substream) {
+		pr_err("%s substream not found\n", __func__);
+		return -ENODEV;
+	}
+	if (!substream->runtime) {
+		pr_err("%s substream runtime not found\n", __func__);
+		return 0;
+	}
+	prtd = substream->runtime->private_data;
+	if (prtd) {
+		rc = msm_pcm_set_volume(prtd, volume);
+		prtd->volume = volume;
+	}
+	return rc;
+}
+
+static int msm_pcm_add_volume_control(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_volume *volume_info;
+	struct snd_kcontrol *kctl;
+
+	dev_dbg(rtd->dev, "%s, Volume control add\n", __func__);
+	ret = snd_pcm_add_volume_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+			NULL, 1, rtd->dai_link->id,
+			&volume_info);
+	if (ret < 0) {
+		pr_err("%s volume control failed ret %d\n", __func__, ret);
+		return ret;
+	}
+	kctl = volume_info->kctl;
+	kctl->put = msm_pcm_volume_ctl_put;
+	kctl->get = msm_pcm_volume_ctl_get;
+	kctl->tlv.p = msm_pcm_vol_gain;
+	return 0;
+}
+
+static int msm_pcm_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_pcm_substream *substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s", __func__);
+	substream = snd_pcm_chmap_substream(info, idx);
+	if (!substream)
+		return -ENODEV;
+	if (!substream->runtime)
+		return 0;
+
+	prtd = substream->runtime->private_data;
+	if (prtd) {
+		prtd->set_channel_map = true;
+			for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+				prtd->channel_map[i] =
+				(char)(ucontrol->value.integer.value[i]);
+	}
+	return 0;
+}
+
+static int msm_pcm_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+	unsigned int idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+	struct snd_pcm_substream *substream;
+	struct msm_audio *prtd;
+
+	pr_debug("%s", __func__);
+	substream = snd_pcm_chmap_substream(info, idx);
+	if (!substream)
+		return -ENODEV;
+	memset(ucontrol->value.integer.value, 0,
+		sizeof(ucontrol->value.integer.value));
+	if (!substream->runtime)
+		return 0; /* no channels set */
+
+	prtd = substream->runtime->private_data;
+
+	if (prtd && prtd->set_channel_map == true) {
+		for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+			ucontrol->value.integer.value[i] =
+					(int)prtd->channel_map[i];
+	} else {
+		for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+			ucontrol->value.integer.value[i] = 0;
+	}
+
+	return 0;
+}
+
+static int msm_pcm_add_chmap_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_chmap *chmap_info;
+	struct snd_kcontrol *kctl;
+	char device_num[12];
+	int i, ret = 0;
+
+	pr_debug("%s, Channel map cntrl add\n", __func__);
+	ret = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+				     snd_pcm_std_chmaps,
+				     PCM_FORMAT_MAX_NUM_CHANNEL, 0,
+				     &chmap_info);
+	if (ret < 0) {
+		pr_err("%s, channel map cntrl add failed\n", __func__);
+		return ret;
+	}
+	kctl = chmap_info->kctl;
+	for (i = 0; i < kctl->count; i++)
+		kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+	snprintf(device_num, sizeof(device_num), "%d", pcm->device);
+	strlcat(kctl->id.name, device_num, sizeof(kctl->id.name));
+	pr_debug("%s, Overwriting channel map control name to: %s\n",
+		__func__, kctl->id.name);
+	kctl->put = msm_pcm_chmap_ctl_put;
+	kctl->get = msm_pcm_chmap_ctl_get;
+	return 0;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate = 48000;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		return -EINVAL;
+	}
+
+	app_type = ucontrol->value.integer.value[0];
+	acdb_dev_id = ucontrol->value.integer.value[1];
+	if (ucontrol->value.integer.value[2] != 0)
+		sample_rate = ucontrol->value.integer.value[2];
+	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n",
+		__func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_RX);
+	msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type,
+			acdb_dev_id, sample_rate, SESSION_TYPE_RX);
+
+	return 0;
+}
+
+static int msm_pcm_playback_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int ret = 0;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_RX,
+		&app_type, &acdb_dev_id, &sample_rate);
+	if (ret < 0) {
+		pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ucontrol->value.integer.value[0] = app_type;
+	ucontrol->value.integer.value[1] = acdb_dev_id;
+	ucontrol->value.integer.value[2] = sample_rate;
+	pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fe_id, SESSION_TYPE_RX,
+		app_type, acdb_dev_id, sample_rate);
+done:
+	return ret;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate = 48000;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s: Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		return -EINVAL;
+	}
+
+	app_type = ucontrol->value.integer.value[0];
+	acdb_dev_id = ucontrol->value.integer.value[1];
+	if (ucontrol->value.integer.value[2] != 0)
+		sample_rate = ucontrol->value.integer.value[2];
+	pr_debug("%s: app_type- %d acdb_dev_id- %d sample_rate- %d session_type- %d\n",
+		__func__, app_type, acdb_dev_id, sample_rate, SESSION_TYPE_TX);
+	msm_pcm_routing_reg_stream_app_type_cfg(fe_id, app_type,
+			acdb_dev_id, sample_rate, SESSION_TYPE_TX);
+
+	return 0;
+}
+
+static int msm_pcm_capture_app_type_cfg_ctl_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	u64 fe_id = kcontrol->private_value;
+	int ret = 0;
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+
+	pr_debug("%s: fe_id- %llu\n", __func__, fe_id);
+	if (fe_id >= MSM_FRONTEND_DAI_MAX) {
+		pr_err("%s: Received out of bounds fe_id %llu\n",
+			__func__, fe_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_pcm_routing_get_stream_app_type_cfg(fe_id, SESSION_TYPE_TX,
+		&app_type, &acdb_dev_id, &sample_rate);
+	if (ret < 0) {
+		pr_err("%s: msm_pcm_routing_get_stream_app_type_cfg failed returned %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ucontrol->value.integer.value[0] = app_type;
+	ucontrol->value.integer.value[1] = acdb_dev_id;
+	ucontrol->value.integer.value[2] = sample_rate;
+	pr_debug("%s: fedai_id %llu, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fe_id, SESSION_TYPE_TX,
+		app_type, acdb_dev_id, sample_rate);
+done:
+	return ret;
+}
+
+static int msm_pcm_add_app_type_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_pcm *pcm = rtd->pcm;
+	struct snd_pcm_usr *app_type_info;
+	struct snd_kcontrol *kctl;
+	const char *playback_mixer_ctl_name	= "Audio Stream";
+	const char *capture_mixer_ctl_name	= "Audio Stream Capture";
+	const char *deviceNo		= "NN";
+	const char *suffix		= "App Type Cfg";
+	int ctl_len, ret = 0;
+
+	if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+		ctl_len = strlen(playback_mixer_ctl_name) + 1 +
+				strlen(deviceNo) + 1 + strlen(suffix) + 1;
+		pr_debug("%s: Playback app type cntrl add\n", __func__);
+		ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+					NULL, 1, ctl_len, rtd->dai_link->id,
+					&app_type_info);
+		if (ret < 0) {
+			pr_err("%s: playback app type cntrl add failed: %d\n",
+				__func__, ret);
+			return ret;
+		}
+		kctl = app_type_info->kctl;
+		snprintf(kctl->id.name, ctl_len, "%s %d %s",
+			playback_mixer_ctl_name, rtd->pcm->device, suffix);
+		kctl->put = msm_pcm_playback_app_type_cfg_ctl_put;
+		kctl->get = msm_pcm_playback_app_type_cfg_ctl_get;
+	}
+
+	if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+		ctl_len = strlen(capture_mixer_ctl_name) + 1 +
+				strlen(deviceNo) + 1 + strlen(suffix) + 1;
+		pr_debug("%s: Capture app type cntrl add\n", __func__);
+		ret = snd_pcm_add_usr_ctls(pcm, SNDRV_PCM_STREAM_CAPTURE,
+					NULL, 1, ctl_len, rtd->dai_link->id,
+					&app_type_info);
+		if (ret < 0) {
+			pr_err("%s: capture app type cntrl add failed: %d\n",
+				__func__, ret);
+			return ret;
+		}
+		kctl = app_type_info->kctl;
+		snprintf(kctl->id.name, ctl_len, "%s %d %s",
+			capture_mixer_ctl_name, rtd->pcm->device, suffix);
+		kctl->put = msm_pcm_capture_app_type_cfg_ctl_put;
+		kctl->get = msm_pcm_capture_app_type_cfg_ctl_get;
+	}
+
+	return 0;
+}
+
+static int msm_pcm_add_controls(struct snd_soc_pcm_runtime *rtd)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+	ret = msm_pcm_add_chmap_controls(rtd);
+	if (ret)
+		pr_err("%s: pcm add controls failed:%d\n", __func__, ret);
+	ret = msm_pcm_add_app_type_controls(rtd);
+	if (ret)
+		pr_err("%s: pcm add app type controls failed:%d\n",
+			__func__, ret);
+	return ret;
+}
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+
+	ret = msm_pcm_add_controls(rtd);
+	if (ret) {
+		pr_err("%s, kctl add failed:%d\n", __func__, ret);
+		return ret;
+	}
+
+	ret = msm_pcm_add_volume_control(rtd);
+	if (ret)
+		pr_err("%s: Could not add pcm Volume Control %d\n",
+			__func__, ret);
+
+	return ret;
+}
+
+static snd_pcm_sframes_t msm_pcm_delay_blk(struct snd_pcm_substream *substream,
+		struct snd_soc_dai *dai)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_audio *prtd = runtime->private_data;
+	struct audio_client *ac = prtd->audio_client;
+	snd_pcm_sframes_t frames;
+	int ret;
+
+	ret = q6asm_get_path_delay(prtd->audio_client);
+	if (ret) {
+		pr_err("%s: get_path_delay failed, ret=%d\n", __func__, ret);
+		return 0;
+	}
+
+	/* convert microseconds to frames */
+	frames = ac->path_delay / 1000 * runtime->rate / 1000;
+
+	/* also convert the remainder from the initial division */
+	frames += ac->path_delay % 1000 * runtime->rate / 1000000;
+
+	/* overcompensate for the loss of precision (empirical) */
+	frames += 2;
+
+	return frames;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.delay_blk      = msm_pcm_delay_blk,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+	int rc;
+	int id;
+	struct msm_plat_data *pdata;
+	const char *latency_level;
+
+	rc = of_property_read_u32(pdev->dev.of_node,
+				"qcom,msm-pcm-dsp-id", &id);
+	if (rc) {
+		dev_err(&pdev->dev, "%s: qcom,msm-pcm-dsp-id missing in DT node\n",
+					__func__);
+		return rc;
+	}
+
+	pdata = kzalloc(sizeof(struct msm_plat_data), GFP_KERNEL);
+	if (!pdata)
+		return -ENOMEM;
+
+	if (of_property_read_bool(pdev->dev.of_node,
+				"qcom,msm-pcm-low-latency")) {
+
+		pdata->perf_mode = LOW_LATENCY_PCM_MODE;
+		rc = of_property_read_string(pdev->dev.of_node,
+			"qcom,latency-level", &latency_level);
+		if (!rc) {
+			if (!strcmp(latency_level, "ultra"))
+				pdata->perf_mode = ULTRA_LOW_LATENCY_PCM_MODE;
+			else if (!strcmp(latency_level, "ull-pp"))
+				pdata->perf_mode =
+					ULL_POST_PROCESSING_PCM_MODE;
+		}
+	} else {
+		pdata->perf_mode = LEGACY_PCM_MODE;
+	}
+
+	dev_set_drvdata(&pdev->dev, pdata);
+
+
+	dev_dbg(&pdev->dev, "%s: dev name %s\n",
+				__func__, dev_name(&pdev->dev));
+	return snd_soc_register_platform(&pdev->dev,
+				   &msm_soc_platform);
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	struct msm_plat_data *pdata;
+
+	pdata = dev_get_drvdata(&pdev->dev);
+	kfree(pdata);
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+static const struct of_device_id msm_pcm_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-dsp"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-pcm-dsp",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	init_waitqueue_head(&the_locks.enable_wait);
+	init_waitqueue_head(&the_locks.eos_wait);
+	init_waitqueue_head(&the_locks.write_wait);
+	init_waitqueue_head(&the_locks.read_wait);
+
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
new file mode 100644
index 0000000..5290d34
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-q6-v2.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2008 Google, Inc.
+ * Copyright (C) 2008 HTC Corporation
+ * Copyright (c) 2012-2017 The Linux Foundation. All rights reserved.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * See the GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can find it at http://www.fsf.org.
+ */
+
+#ifndef _MSM_PCM_H
+#define _MSM_PCM_H
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+
+
+
+/* Support unconventional sample rates 12000, 24000 as well */
+#define USE_RATE                \
+			(SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+extern int copy_count;
+
+struct buffer {
+	void *data;
+	unsigned int size;
+	unsigned int used;
+	unsigned int addr;
+};
+
+struct buffer_rec {
+	void *data;
+	unsigned int size;
+	unsigned int read;
+	unsigned int addr;
+};
+
+struct audio_locks {
+	spinlock_t event_lock;
+	wait_queue_head_t read_wait;
+	wait_queue_head_t write_wait;
+	wait_queue_head_t eos_wait;
+	wait_queue_head_t enable_wait;
+	wait_queue_head_t flush_wait;
+};
+
+struct msm_audio_in_frame_info {
+	uint32_t size;
+	uint32_t offset;
+};
+
+#define PLAYBACK_MIN_NUM_PERIODS    2
+#define PLAYBACK_MAX_NUM_PERIODS    8
+#define PLAYBACK_MAX_PERIOD_SIZE    122880
+#define PLAYBACK_MIN_PERIOD_SIZE    128
+#define CAPTURE_MIN_NUM_PERIODS     2
+#define CAPTURE_MAX_NUM_PERIODS     8
+#define CAPTURE_MAX_PERIOD_SIZE     122880
+#define CAPTURE_MIN_PERIOD_SIZE     320
+
+struct msm_audio {
+	struct snd_pcm_substream *substream;
+	unsigned int pcm_size;
+	unsigned int pcm_count;
+	unsigned int pcm_irq_pos;       /* IRQ position */
+	uint16_t source; /* Encoding source bit mask */
+
+	struct audio_client *audio_client;
+
+	uint16_t session_id;
+
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+	uint32_t dsp_cnt;
+
+	int abort; /* set when error, like sample rate mismatch */
+
+	bool reset_event;
+	int enabled;
+	int close_ack;
+	int cmd_ack;
+	/*
+	 * cmd_ack doesn't tell if paticular command has been sent so can't
+	 * determine if it needs to wait for completion.
+	 * Use cmd_pending instead when checking whether a command is been
+	 * sent or not.
+	 */
+	unsigned long cmd_pending;
+	atomic_t start;
+	atomic_t stop;
+	atomic_t out_count;
+	atomic_t in_count;
+	atomic_t out_needed;
+	atomic_t eos;
+	int out_head;
+	int periods;
+	int mmap_flag;
+	atomic_t pending_buffer;
+	bool set_channel_map;
+	char channel_map[8];
+	int cmd_interrupt;
+	bool meta_data_mode;
+	uint32_t volume;
+	/* array of frame info */
+	struct msm_audio_in_frame_info in_frame_info[CAPTURE_MAX_NUM_PERIODS];
+};
+
+struct output_meta_data_st {
+	uint32_t meta_data_length;
+	uint32_t frame_size;
+	uint32_t timestamp_lsw;
+	uint32_t timestamp_msw;
+	uint32_t reserved[12];
+};
+
+struct msm_plat_data {
+	int perf_mode;
+};
+
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c
new file mode 100644
index 0000000..1dfbd7a
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.c
@@ -0,0 +1,176 @@
+/* Copyright (c) 2014, 2016-2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <sound/hwdep.h>
+#include <sound/devdep_params.h>
+#include <sound/msm-dts-eagle.h>
+
+#include "msm-pcm-routing-devdep.h"
+#include "msm-ds2-dap-config.h"
+
+#ifdef CONFIG_SND_HWDEP
+static int msm_pcm_routing_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+	pr_debug("%s\n", __func__);
+	msm_ds2_dap_update_port_parameters(hw, file, true);
+	return 0;
+}
+
+static int msm_pcm_routing_hwdep_release(struct snd_hwdep *hw,
+					 struct file *file)
+{
+	pr_debug("%s\n", __func__);
+	msm_ds2_dap_update_port_parameters(hw, file, false);
+	return 0;
+}
+
+static int msm_pcm_routing_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+				       unsigned int cmd, unsigned long arg)
+{
+	int ret = 0;
+	void __user *argp = (void __user *)arg;
+
+	pr_debug("%s:cmd %x\n", __func__, cmd);
+	switch (cmd) {
+	case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM:
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM:
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND:
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE:
+		msm_pcm_routing_acquire_lock();
+		ret = msm_ds2_dap_ioctl(hw, file, cmd, argp);
+		msm_pcm_routing_release_lock();
+		break;
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER:
+		ret = msm_ds2_dap_ioctl(hw, file, cmd, argp);
+		break;
+	case DTS_EAGLE_IOCTL_GET_CACHE_SIZE:
+	case DTS_EAGLE_IOCTL_SET_CACHE_SIZE:
+	case DTS_EAGLE_IOCTL_GET_PARAM:
+	case DTS_EAGLE_IOCTL_SET_PARAM:
+	case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK:
+	case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE:
+	case DTS_EAGLE_IOCTL_GET_LICENSE:
+	case DTS_EAGLE_IOCTL_SET_LICENSE:
+	case DTS_EAGLE_IOCTL_SEND_LICENSE:
+	case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS:
+		ret = msm_dts_eagle_ioctl(cmd, arg);
+		if (ret == -EPERM) {
+			pr_err("%s called with invalid control 0x%X\n",
+				__func__, cmd);
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s called with invalid control 0x%X\n", __func__, cmd);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+
+void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm)
+{
+	pr_debug("%s\n", __func__);
+	msm_dts_eagle_pcm_free(pcm);
+}
+
+#ifdef CONFIG_COMPAT
+static int msm_pcm_routing_hwdep_compat_ioctl(struct snd_hwdep *hw,
+					      struct file *file,
+					      unsigned int cmd,
+					      unsigned long arg)
+{
+	int ret = 0;
+	void __user *argp = (void __user *)arg;
+
+	pr_debug("%s:cmd %x\n", __func__, cmd);
+	switch (cmd) {
+	case SNDRV_DEVDEP_DAP_IOCTL_SET_PARAM32:
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_PARAM32:
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_COMMAND32:
+	case SNDRV_DEVDEP_DAP_IOCTL_DAP_LICENSE32:
+		msm_pcm_routing_acquire_lock();
+		ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp);
+		msm_pcm_routing_release_lock();
+		break;
+	case SNDRV_DEVDEP_DAP_IOCTL_GET_VISUALIZER32:
+		ret = msm_ds2_dap_compat_ioctl(hw, file, cmd, argp);
+		break;
+	case DTS_EAGLE_IOCTL_GET_CACHE_SIZE32:
+	case DTS_EAGLE_IOCTL_SET_CACHE_SIZE32:
+	case DTS_EAGLE_IOCTL_GET_PARAM32:
+	case DTS_EAGLE_IOCTL_SET_PARAM32:
+	case DTS_EAGLE_IOCTL_SET_CACHE_BLOCK32:
+	case DTS_EAGLE_IOCTL_SET_ACTIVE_DEVICE32:
+	case DTS_EAGLE_IOCTL_GET_LICENSE32:
+	case DTS_EAGLE_IOCTL_SET_LICENSE32:
+	case DTS_EAGLE_IOCTL_SEND_LICENSE32:
+	case DTS_EAGLE_IOCTL_SET_VOLUME_COMMANDS32:
+		ret = msm_dts_eagle_compat_ioctl(cmd, arg);
+		if (ret == -EPERM) {
+			pr_err("%s called with invalid control 0x%X\n",
+				__func__, cmd);
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		pr_err("%s called with invalid control 0x%X\n", __func__, cmd);
+		ret = -EINVAL;
+		break;
+	}
+	return ret;
+}
+#endif
+
+int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime,
+			      struct msm_pcm_routing_bdai_data *msm_bedais)
+{
+	struct snd_hwdep *hwdep;
+	struct snd_soc_dai_link *dai_link = runtime->dai_link;
+	int rc;
+
+	if (dai_link->id < 0 ||
+		dai_link->id >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s:BE id %d invalid index\n",
+			__func__, dai_link->id);
+		return -EINVAL;
+	}
+	pr_debug("%s BE id %d\n", __func__, dai_link->id);
+	rc = snd_hwdep_new(runtime->card->snd_card,
+			   msm_bedais[dai_link->id].name,
+			   dai_link->id, &hwdep);
+	if (hwdep == NULL) {
+		pr_err("%s: hwdep intf failed to create %s- hwdep NULL\n",
+			__func__, msm_bedais[dai_link->id].name);
+		return rc;
+	}
+	if (rc < 0) {
+		pr_err("%s: hwdep intf failed to create %s rc %d\n", __func__,
+			msm_bedais[dai_link->id].name, rc);
+		return rc;
+	}
+
+	hwdep->iface = SNDRV_HWDEP_IFACE_AUDIO_BE;
+	hwdep->private_data = &msm_bedais[dai_link->id];
+	hwdep->ops.open = msm_pcm_routing_hwdep_open;
+	hwdep->ops.ioctl = msm_pcm_routing_hwdep_ioctl;
+	hwdep->ops.release = msm_pcm_routing_hwdep_release;
+#ifdef CONFIG_COMPAT
+	hwdep->ops.ioctl_compat = msm_pcm_routing_hwdep_compat_ioctl;
+#endif
+	return msm_dts_eagle_pcm_new(runtime);
+}
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h
new file mode 100644
index 0000000..d93d048
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-devdep.h
@@ -0,0 +1,33 @@
+/* Copyright (c) 2014-2015, 2017 The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_PCM_ROUTING_DEVDEP_H_
+#define _MSM_PCM_ROUTING_DEVDEP_H_
+
+#include <sound/soc.h>
+#include "msm-pcm-routing-v2.h"
+
+#ifdef CONFIG_SND_HWDEP
+int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime,
+			      struct msm_pcm_routing_bdai_data *msm_bedais);
+void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm);
+#else
+static inline int msm_pcm_routing_hwdep_new(struct snd_soc_pcm_runtime *runtime,
+				struct msm_pcm_routing_bdai_data *msm_bedais)
+{
+	return 0;
+}
+
+static inline void msm_pcm_routing_hwdep_free(struct snd_pcm *pcm)
+{
+}
+#endif
+#endif
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
new file mode 100644
index 0000000..de7d790
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.c
@@ -0,0 +1,11931 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/tlv.h>
+#include <sound/asound.h>
+#include <sound/pcm_params.h>
+#include <sound/q6core.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/msm-dts-eagle.h>
+#include <sound/audio_effects.h>
+#include <sound/hwdep.h>
+
+#include "msm-pcm-routing-v2.h"
+#include "msm-pcm-routing-devdep.h"
+#include "msm-qti-pp-config.h"
+#include "msm-dts-srs-tm-config.h"
+#include "msm-dolby-dap-config.h"
+#include "msm-ds2-dap-config.h"
+#include "q6voice.h"
+#include "sound/q6lsm.h"
+
+static int get_cal_path(int path_type);
+
+static struct mutex routing_lock;
+
+static struct cal_type_data *cal_data;
+
+static int fm_switch_enable;
+static int hfp_switch_enable;
+static int pri_mi2s_switch_enable;
+static int sec_mi2s_switch_enable;
+static int tert_mi2s_switch_enable;
+static int quat_mi2s_switch_enable;
+static int fm_pcmrx_switch_enable;
+static int lsm_mux_slim_port;
+static int slim0_rx_aanc_fb_port;
+static int msm_route_ec_ref_rx;
+static uint32_t voc_session_id = ALL_SESSION_VSID;
+static int msm_route_ext_ec_ref;
+static bool is_custom_stereo_on;
+static bool is_ds2_on;
+
+enum {
+	MADNONE,
+	MADAUDIO,
+	MADBEACON,
+	MADULTRASOUND,
+	MADSWAUDIO,
+};
+
+#define SLIMBUS_0_TX_TEXT "SLIMBUS_0_TX"
+#define SLIMBUS_1_TX_TEXT "SLIMBUS_1_TX"
+#define SLIMBUS_2_TX_TEXT "SLIMBUS_2_TX"
+#define SLIMBUS_3_TX_TEXT "SLIMBUS_3_TX"
+#define SLIMBUS_4_TX_TEXT "SLIMBUS_4_TX"
+#define SLIMBUS_5_TX_TEXT "SLIMBUS_5_TX"
+#define TERT_MI2S_TX_TEXT "TERT_MI2S_TX"
+#define LSM_FUNCTION_TEXT "LSM Function"
+static const char * const mad_audio_mux_text[] = {
+	"None",
+	SLIMBUS_0_TX_TEXT, SLIMBUS_1_TX_TEXT, SLIMBUS_2_TX_TEXT,
+	SLIMBUS_3_TX_TEXT, SLIMBUS_4_TX_TEXT, SLIMBUS_5_TX_TEXT,
+	TERT_MI2S_TX_TEXT
+};
+
+struct msm_pcm_route_bdai_pp_params {
+	u16 port_id; /* AFE port ID */
+	unsigned long pp_params_config;
+	bool mute_on;
+	int latency;
+};
+
+static struct msm_pcm_route_bdai_pp_params
+	msm_bedais_pp_params[MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX] = {
+	{HDMI_RX, 0, 0, 0},
+	{DISPLAY_PORT_RX, 0, 0, 0},
+};
+
+static int msm_routing_send_device_pp_params(int port_id,  int copp_idx);
+
+static int msm_routing_get_bit_width(unsigned int format)
+{
+	int bit_width;
+
+	switch (format) {
+	case SNDRV_PCM_FORMAT_S24_LE:
+	case SNDRV_PCM_FORMAT_S24_3LE:
+		bit_width = 24;
+		break;
+	case SNDRV_PCM_FORMAT_S16_LE:
+	default:
+		bit_width = 16;
+	}
+	return bit_width;
+}
+
+static bool msm_is_resample_needed(int input_sr, int output_sr)
+{
+	bool rc = false;
+
+	if (input_sr != output_sr)
+		rc = true;
+
+	pr_debug("perform resampling (%s) for copp rate (%d)afe rate (%d)",
+		(rc ? "oh yes" : "not really"),
+		input_sr, output_sr);
+
+	return rc;
+}
+
+static void msm_pcm_routing_cfg_pp(int port_id, int copp_idx, int topology,
+				   int channels)
+{
+	int rc = 0;
+
+	switch (topology) {
+	case SRS_TRUMEDIA_TOPOLOGY_ID:
+		pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__);
+		msm_dts_srs_tm_init(port_id, copp_idx);
+		break;
+	case DS2_ADM_COPP_TOPOLOGY_ID:
+		pr_debug("%s: DS2_ADM_COPP_TOPOLOGY %d\n",
+			 __func__, DS2_ADM_COPP_TOPOLOGY_ID);
+		rc = msm_ds2_dap_init(port_id, copp_idx, channels,
+				      is_custom_stereo_on);
+		if (rc < 0)
+			pr_err("%s: DS2 topo_id 0x%x, port %d, CS %d rc %d\n",
+				__func__, topology, port_id,
+				is_custom_stereo_on, rc);
+		break;
+	case DOLBY_ADM_COPP_TOPOLOGY_ID:
+		if (is_ds2_on) {
+			pr_debug("%s: DS2_ADM_COPP_TOPOLOGY\n", __func__);
+			rc = msm_ds2_dap_init(port_id, copp_idx, channels,
+				is_custom_stereo_on);
+			if (rc < 0)
+				pr_err("%s:DS2 topo_id 0x%x, port %d, rc %d\n",
+					__func__, topology, port_id, rc);
+		} else {
+			pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__);
+			rc = msm_dolby_dap_init(port_id, copp_idx, channels,
+						is_custom_stereo_on);
+			if (rc < 0)
+				pr_err("%s: DS1 topo_id 0x%x, port %d, rc %d\n",
+					__func__, topology, port_id, rc);
+		}
+		break;
+	case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX:
+		pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__);
+		msm_dts_eagle_init_post(port_id, copp_idx);
+		break;
+	case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
+		pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
+		rc = msm_qti_pp_asphere_init(port_id, copp_idx);
+		if (rc < 0)
+			pr_err("%s: topo_id 0x%x, port %d, copp %d, rc %d\n",
+				__func__, topology, port_id, copp_idx, rc);
+		break;
+	default:
+		/* custom topology specific feature param handlers */
+		break;
+	}
+}
+
+static void msm_pcm_routing_deinit_pp(int port_id, int topology)
+{
+	switch (topology) {
+	case SRS_TRUMEDIA_TOPOLOGY_ID:
+		pr_debug("%s: SRS_TRUMEDIA_TOPOLOGY_ID\n", __func__);
+		msm_dts_srs_tm_deinit(port_id);
+		break;
+	case DS2_ADM_COPP_TOPOLOGY_ID:
+		pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID %d\n",
+			 __func__, DS2_ADM_COPP_TOPOLOGY_ID);
+		msm_ds2_dap_deinit(port_id);
+		break;
+	case DOLBY_ADM_COPP_TOPOLOGY_ID:
+		if (is_ds2_on) {
+			pr_debug("%s: DS2_ADM_COPP_TOPOLOGY_ID\n", __func__);
+			msm_ds2_dap_deinit(port_id);
+		} else {
+			pr_debug("%s: DOLBY_ADM_COPP_TOPOLOGY_ID\n", __func__);
+			msm_dolby_dap_deinit(port_id);
+		}
+		break;
+	case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX:
+		pr_debug("%s: DTS_EAGLE_COPP_TOPOLOGY_ID\n", __func__);
+		msm_dts_eagle_deinit_post(port_id, topology);
+		break;
+	case ADM_CMD_COPP_OPEN_TOPOLOGY_ID_AUDIOSPHERE:
+		pr_debug("%s: TOPOLOGY_ID_AUDIOSPHERE\n", __func__);
+		msm_qti_pp_asphere_deinit(port_id);
+		break;
+	default:
+		/* custom topology specific feature deinit handlers */
+		break;
+	}
+}
+
+static void msm_pcm_routng_cfg_matrix_map_pp(struct route_payload payload,
+					     int path_type, int perf_mode)
+{
+	int itr = 0, rc = 0;
+
+	if ((path_type == ADM_PATH_PLAYBACK) &&
+	    (perf_mode == LEGACY_PCM_MODE) &&
+	    is_custom_stereo_on) {
+		for (itr = 0; itr < payload.num_copps; itr++) {
+			if ((payload.port_id[itr] != SLIMBUS_0_RX) &&
+			    (payload.port_id[itr] != RT_PROXY_PORT_001_RX)) {
+				continue;
+			}
+
+			rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd(
+				payload.port_id[itr],
+				payload.copp_idx[itr],
+				payload.session_id,
+				Q14_GAIN_ZERO_POINT_FIVE,
+				Q14_GAIN_ZERO_POINT_FIVE,
+				Q14_GAIN_ZERO_POINT_FIVE,
+				Q14_GAIN_ZERO_POINT_FIVE);
+			if (rc < 0)
+				pr_err("%s: err setting custom stereo\n",
+					__func__);
+		}
+	}
+}
+
+#define SLIMBUS_EXTPROC_RX AFE_PORT_INVALID
+struct msm_pcm_routing_bdai_data msm_bedais[MSM_BACKEND_DAI_MAX] = {
+	{ PRIMARY_I2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_PRI_I2S_RX},
+	{ PRIMARY_I2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_PRI_I2S_TX},
+	{ SLIMBUS_0_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_0_RX},
+	{ SLIMBUS_0_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_0_TX},
+	{ HDMI_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_HDMI},
+	{ INT_BT_SCO_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_BT_SCO_RX},
+	{ INT_BT_SCO_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_BT_SCO_TX},
+	{ INT_FM_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_FM_RX},
+	{ INT_FM_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_FM_TX},
+	{ RT_PROXY_PORT_001_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_AFE_PCM_RX},
+	{ RT_PROXY_PORT_001_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_AFE_PCM_TX},
+	{ AFE_PORT_ID_PRIMARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_AUXPCM_RX},
+	{ AFE_PORT_ID_PRIMARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_AUXPCM_TX},
+	{ VOICE_PLAYBACK_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_VOICE_PLAYBACK_TX},
+	{ VOICE2_PLAYBACK_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_VOICE2_PLAYBACK_TX},
+	{ VOICE_RECORD_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INCALL_RECORD_RX},
+	{ VOICE_RECORD_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INCALL_RECORD_TX},
+	{ MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_MI2S_RX},
+	{ MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_MI2S_TX},
+	{ SECONDARY_I2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SEC_I2S_RX},
+	{ SLIMBUS_1_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_1_RX},
+	{ SLIMBUS_1_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_1_TX},
+	{ SLIMBUS_2_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_2_RX},
+	{ SLIMBUS_2_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_2_TX},
+	{ SLIMBUS_3_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_3_RX},
+	{ SLIMBUS_3_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_3_TX},
+	{ SLIMBUS_4_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_4_RX},
+	{ SLIMBUS_4_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_4_TX},
+	{ SLIMBUS_5_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_5_RX},
+	{ SLIMBUS_5_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_5_TX},
+	{ SLIMBUS_6_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_6_RX},
+	{ SLIMBUS_6_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_6_TX},
+	{ SLIMBUS_7_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_7_RX},
+	{ SLIMBUS_7_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_7_TX},
+	{ SLIMBUS_8_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_RX},
+	{ SLIMBUS_8_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SLIMBUS_8_TX},
+	{ SLIMBUS_EXTPROC_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_STUB_RX},
+	{ SLIMBUS_EXTPROC_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_STUB_TX},
+	{ SLIMBUS_EXTPROC_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_STUB_1_TX},
+	{ AFE_PORT_ID_QUATERNARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_MI2S_RX},
+	{ AFE_PORT_ID_QUATERNARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_MI2S_TX},
+	{ AFE_PORT_ID_SECONDARY_MI2S_RX,  0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_MI2S_RX},
+	{ AFE_PORT_ID_SECONDARY_MI2S_TX,  0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_MI2S_TX},
+	{ AFE_PORT_ID_PRIMARY_MI2S_RX,    0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_MI2S_RX},
+	{ AFE_PORT_ID_PRIMARY_MI2S_TX,    0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_MI2S_TX},
+	{ AFE_PORT_ID_TERTIARY_MI2S_RX,   0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_MI2S_RX},
+	{ AFE_PORT_ID_TERTIARY_MI2S_TX,   0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_MI2S_TX},
+	{ AUDIO_PORT_ID_I2S_RX,           0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_AUDIO_I2S_RX},
+	{ AFE_PORT_ID_SECONDARY_PCM_RX,	  0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_AUXPCM_RX},
+	{ AFE_PORT_ID_SECONDARY_PCM_TX,   0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_AUXPCM_TX},
+	{ AFE_PORT_ID_SPDIF_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_SPDIF_RX},
+	{ AFE_PORT_ID_SECONDARY_MI2S_RX_SD1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_MI2S_RX_SD1},
+	{ AFE_PORT_ID_QUINARY_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUIN_MI2S_RX},
+	{ AFE_PORT_ID_QUINARY_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUIN_MI2S_TX},
+	{ AFE_PORT_ID_SENARY_MI2S_TX,   0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SENARY_MI2S_TX},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_0},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_0},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_1},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_1},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_2},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_2},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_3},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_3},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_4},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_4},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_5},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_5},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_6},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_6},
+	{ AFE_PORT_ID_PRIMARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_RX_7},
+	{ AFE_PORT_ID_PRIMARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_PRI_TDM_TX_7},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_0},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_0},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_1},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_1},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_2},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_2},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_3},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_3},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_4},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_4},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_5},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_5},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_6},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_6},
+	{ AFE_PORT_ID_SECONDARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_RX_7},
+	{ AFE_PORT_ID_SECONDARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_SEC_TDM_TX_7},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_0},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_0},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_1},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_1},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_2},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_2},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_3},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_3},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_4},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_4},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_5},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_5},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_6},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_6},
+	{ AFE_PORT_ID_TERTIARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_RX_7},
+	{ AFE_PORT_ID_TERTIARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_TDM_TX_7},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_0},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_0},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_1},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_1, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_1},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_2},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_2, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_2},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_3},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_3, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_3},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_4},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_4, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_4},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_5},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_5, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_5},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_6},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_6, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_6},
+	{ AFE_PORT_ID_QUATERNARY_TDM_RX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_RX_7},
+	{ AFE_PORT_ID_QUATERNARY_TDM_TX_7, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_TDM_TX_7},
+	{ INT_BT_A2DP_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_INT_BT_A2DP_RX},
+	{ AFE_PORT_ID_USB_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_USB_AUDIO_RX},
+	{ AFE_PORT_ID_USB_TX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_USB_AUDIO_TX},
+	{ DISPLAY_PORT_RX, 0, 0, {0}, 0, 0, 0, 0, 0, LPASS_BE_DISPLAY_PORT},
+	{ AFE_PORT_ID_TERTIARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_AUXPCM_RX},
+	{ AFE_PORT_ID_TERTIARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_TERT_AUXPCM_TX},
+	{ AFE_PORT_ID_QUATERNARY_PCM_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_AUXPCM_RX},
+	{ AFE_PORT_ID_QUATERNARY_PCM_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_QUAT_AUXPCM_TX},
+	{ AFE_PORT_ID_INT0_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT0_MI2S_RX},
+	{ AFE_PORT_ID_INT0_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT0_MI2S_TX},
+	{ AFE_PORT_ID_INT1_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT1_MI2S_RX},
+	{ AFE_PORT_ID_INT1_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT1_MI2S_TX},
+	{ AFE_PORT_ID_INT2_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT2_MI2S_RX},
+	{ AFE_PORT_ID_INT2_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT2_MI2S_TX},
+	{ AFE_PORT_ID_INT3_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT3_MI2S_RX},
+	{ AFE_PORT_ID_INT3_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT3_MI2S_TX},
+	{ AFE_PORT_ID_INT4_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT4_MI2S_RX},
+	{ AFE_PORT_ID_INT4_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT4_MI2S_TX},
+	{ AFE_PORT_ID_INT5_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT5_MI2S_RX},
+	{ AFE_PORT_ID_INT5_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT5_MI2S_TX},
+	{ AFE_PORT_ID_INT6_MI2S_RX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT6_MI2S_RX},
+	{ AFE_PORT_ID_INT6_MI2S_TX, 0, 0, {0}, 0, 0, 0, 0, 0,
+	  LPASS_BE_INT6_MI2S_TX},
+};
+
+/* Track ASM playback & capture sessions of DAI */
+static struct msm_pcm_routing_fdai_data
+	fe_dai_map[MSM_FRONTEND_DAI_MM_SIZE][2] = {
+	/* MULTIMEDIA1 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA2 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA3 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA4 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA5 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA6 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA7*/
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA8 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA9 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA10 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA11 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA12 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA13 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA14 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA15 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA16 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA17 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA18 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+	/* MULTIMEDIA19 */
+	{{0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} },
+	 {0, INVALID_SESSION, LEGACY_PCM_MODE, {NULL, NULL} } },
+};
+
+static unsigned long session_copp_map[MSM_FRONTEND_DAI_MM_SIZE][2]
+				     [MSM_BACKEND_DAI_MAX];
+static struct msm_pcm_routing_app_type_data app_type_cfg[MAX_APP_TYPES];
+static struct msm_pcm_stream_app_type_cfg
+			 fe_dai_app_type_cfg[MSM_FRONTEND_DAI_MM_SIZE][2];
+
+/* The caller of this should aqcuire routing lock */
+void msm_pcm_routing_get_bedai_info(int be_idx,
+				    struct msm_pcm_routing_bdai_data *be_dai)
+{
+	if (be_idx >= 0 && be_idx < MSM_BACKEND_DAI_MAX)
+		memcpy(be_dai, &msm_bedais[be_idx],
+		       sizeof(struct msm_pcm_routing_bdai_data));
+}
+
+/* The caller of this should aqcuire routing lock */
+void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type,
+				    struct msm_pcm_routing_fdai_data *fe_dai)
+{
+	if ((sess_type == SESSION_TYPE_TX) || (sess_type == SESSION_TYPE_RX))
+		memcpy(fe_dai, &fe_dai_map[fe_idx][sess_type],
+		       sizeof(struct msm_pcm_routing_fdai_data));
+}
+
+void msm_pcm_routing_acquire_lock(void)
+{
+	mutex_lock(&routing_lock);
+}
+
+void msm_pcm_routing_release_lock(void)
+{
+	mutex_unlock(&routing_lock);
+}
+
+static int msm_pcm_routing_get_app_type_idx(int app_type)
+{
+	int idx;
+
+	pr_debug("%s: app_type: %d\n", __func__, app_type);
+	for (idx = 0; idx < MAX_APP_TYPES; idx++) {
+		if (app_type_cfg[idx].app_type == app_type)
+			return idx;
+	}
+	pr_info("%s: App type not available, fallback to default\n", __func__);
+	return 0;
+}
+
+void msm_pcm_routing_reg_stream_app_type_cfg(int fedai_id, int app_type,
+	int acdb_dev_id, int sample_rate, int session_type)
+{
+	pr_debug("%s: fedai_id %d, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fedai_id, session_type, app_type,
+		acdb_dev_id, sample_rate);
+	if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		pr_err("%s: Invalid machine driver ID %d\n",
+			__func__, fedai_id);
+		return;
+	}
+	if (session_type != SESSION_TYPE_RX &&
+		session_type != SESSION_TYPE_TX) {
+		pr_err("%s: Invalid session type %d\n",
+			__func__, session_type);
+		return;
+	}
+	fe_dai_app_type_cfg[fedai_id][session_type].app_type = app_type;
+	fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id = acdb_dev_id;
+	fe_dai_app_type_cfg[fedai_id][session_type].sample_rate = sample_rate;
+}
+
+/**
+ * msm_pcm_routing_get_stream_app_type_cfg
+ *
+ * Receives fedai_id, session_type and populates app_type, acdb_dev_id, &
+ * sample rate. Returns 0 on success. On failure returns
+ * -EINVAL and does not alter passed values.
+ *
+ * fedai_id - Passed value, front end ID for which app type config is wanted
+ * session_type - Passed value, session type for which app type config
+ *                is wanted
+ * app_type - Returned value, app type used by app type config
+ * acdb_dev_id - Returned value, ACDB device ID used by app type config
+ * sample_rate - Returned value, sample rate used by app type config
+ */
+int msm_pcm_routing_get_stream_app_type_cfg(int fedai_id, int session_type,
+	int *app_type, int *acdb_dev_id, int *sample_rate)
+{
+	int ret = 0;
+
+	if (app_type == NULL) {
+		pr_err("%s: NULL pointer sent for app_type\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if (acdb_dev_id == NULL) {
+		pr_err("%s: NULL pointer sent for acdb_dev_id\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if (sample_rate == NULL) {
+		pr_err("%s: NULL pointer sent for sample rate\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	} else if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		pr_err("%s: Invalid FE ID %d\n",
+			__func__, fedai_id);
+		ret = -EINVAL;
+		goto done;
+	} else if (session_type != SESSION_TYPE_RX &&
+		session_type != SESSION_TYPE_TX) {
+		pr_err("%s: Invalid session type %d\n",
+			__func__, session_type);
+		ret = -EINVAL;
+		goto done;
+	}
+	*app_type = fe_dai_app_type_cfg[fedai_id][session_type].app_type;
+	*acdb_dev_id = fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id;
+	*sample_rate = fe_dai_app_type_cfg[fedai_id][session_type].sample_rate;
+
+	pr_debug("%s: fedai_id %d, session_type %d, app_type %d, acdb_dev_id %d, sample_rate %d\n",
+		__func__, fedai_id, session_type,
+		*app_type, *acdb_dev_id, *sample_rate);
+done:
+	return ret;
+}
+EXPORT_SYMBOL(msm_pcm_routing_get_stream_app_type_cfg);
+
+static struct cal_block_data *msm_routing_find_topology_by_path(int path)
+{
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	list_for_each_safe(ptr, next,
+		&cal_data->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		if (((struct audio_cal_info_adm_top *)cal_block->cal_info)
+			->path == path) {
+			return cal_block;
+		}
+	}
+	pr_debug("%s: Can't find topology for path %d\n", __func__, path);
+	return NULL;
+}
+
+static struct cal_block_data *msm_routing_find_topology(int path,
+							int app_type,
+							int acdb_id,
+							int sample_rate)
+{
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block = NULL;
+	struct audio_cal_info_adm_top *cal_info;
+
+	pr_debug("%s\n", __func__);
+
+	list_for_each_safe(ptr, next,
+		&cal_data->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		cal_info = (struct audio_cal_info_adm_top *)
+			cal_block->cal_info;
+		if ((cal_info->path == path)  &&
+			(cal_info->app_type == app_type) &&
+			(cal_info->acdb_id == acdb_id) &&
+			(cal_info->sample_rate == sample_rate)) {
+			return cal_block;
+		}
+	}
+	pr_debug("%s: Can't find topology for path %d, app %d, acdb_id %d sample_rate %d defaulting to search by path\n",
+		__func__, path, app_type, acdb_id, sample_rate);
+	return msm_routing_find_topology_by_path(path);
+}
+
+static int msm_routing_get_adm_topology(int path, int fedai_id,
+					int session_type)
+{
+	int topology = NULL_COPP_TOPOLOGY;
+	struct cal_block_data *cal_block = NULL;
+	int app_type = 0, acdb_dev_id = 0, sample_rate = 0;
+
+	pr_debug("%s\n", __func__);
+
+	path = get_cal_path(path);
+	if (cal_data == NULL)
+		goto done;
+
+	mutex_lock(&cal_data->lock);
+
+	app_type = fe_dai_app_type_cfg[fedai_id][session_type].app_type;
+	acdb_dev_id = fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id;
+	sample_rate = fe_dai_app_type_cfg[fedai_id][session_type].sample_rate;
+
+	cal_block = msm_routing_find_topology(path, app_type,
+					      acdb_dev_id, sample_rate);
+	if (cal_block == NULL)
+		goto unlock;
+
+	topology = ((struct audio_cal_info_adm_top *)
+		cal_block->cal_info)->topology;
+unlock:
+	mutex_unlock(&cal_data->lock);
+done:
+	pr_debug("%s: Using topology %d\n", __func__, topology);
+	return topology;
+}
+
+static uint8_t is_be_dai_extproc(int be_dai)
+{
+	if (be_dai == MSM_BACKEND_DAI_EXTPROC_RX ||
+	   be_dai == MSM_BACKEND_DAI_EXTPROC_TX ||
+	   be_dai == MSM_BACKEND_DAI_EXTPROC_EC_TX)
+		return 1;
+	else
+		return 0;
+}
+
+static void msm_pcm_routing_build_matrix(int fedai_id, int sess_type,
+					 int path_type, int perf_mode)
+{
+	int i, port_type, j, num_copps = 0;
+	struct route_payload payload;
+
+	port_type = ((path_type == ADM_PATH_PLAYBACK  ||
+		     path_type == ADM_PATH_COMPRESSED_RX) ?
+		MSM_AFE_PORT_TYPE_RX : MSM_AFE_PORT_TYPE_TX);
+
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (!is_be_dai_extproc(i) &&
+		   (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+		   (msm_bedais[i].active) &&
+		   (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+			for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+				unsigned long copp =
+				      session_copp_map[fedai_id][sess_type][i];
+				if (test_bit(j, &copp)) {
+					payload.port_id[num_copps] =
+							msm_bedais[i].port_id;
+					payload.copp_idx[num_copps] = j;
+					num_copps++;
+				}
+			}
+		}
+	}
+
+	if (num_copps) {
+		payload.num_copps = num_copps;
+		payload.session_id = fe_dai_map[fedai_id][sess_type].strm_id;
+		payload.app_type =
+			fe_dai_app_type_cfg[fedai_id][sess_type].app_type;
+		payload.acdb_dev_id =
+			fe_dai_app_type_cfg[fedai_id][sess_type].acdb_dev_id;
+		payload.sample_rate =
+			fe_dai_app_type_cfg[fedai_id][sess_type].sample_rate;
+		adm_matrix_map(path_type, payload, perf_mode);
+		msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
+	}
+}
+
+void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
+				      int stream_type)
+{
+	int i, session_type, path_type, port_type;
+	u32 mode = 0;
+
+	if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* bad ID assigned in machine driver */
+		pr_err("%s: bad MM ID\n", __func__);
+		return;
+	}
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+		session_type = SESSION_TYPE_RX;
+		path_type = ADM_PATH_PLAYBACK;
+		port_type = MSM_AFE_PORT_TYPE_RX;
+	} else {
+		session_type = SESSION_TYPE_TX;
+		path_type = ADM_PATH_LIVE_REC;
+		port_type = MSM_AFE_PORT_TYPE_TX;
+	}
+
+	mutex_lock(&routing_lock);
+
+	fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (!is_be_dai_extproc(i) &&
+		    (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+		    (msm_bedais[i].active) &&
+		    (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+			mode = afe_get_port_type(msm_bedais[i].port_id);
+			adm_connect_afe_port(mode, dspst_id,
+					     msm_bedais[i].port_id);
+			break;
+		}
+	}
+	mutex_unlock(&routing_lock);
+}
+
+int msm_pcm_routing_reg_phy_compr_stream(int fe_id, int perf_mode,
+					  int dspst_id, int stream_type,
+					  uint32_t compr_passthr_mode)
+{
+	int i, j, session_type, path_type, port_type, topology, num_copps = 0;
+	struct route_payload payload;
+	u32 channels, sample_rate;
+	u16 bit_width = 16;
+
+	pr_debug("%s:fe_id[%d] perf_mode[%d] id[%d] stream_type[%d] passt[%d]",
+		 __func__, fe_id, perf_mode, dspst_id,
+		 stream_type, compr_passthr_mode);
+
+	if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* bad ID assigned in machine driver */
+		pr_err("%s: bad MM ID %d\n", __func__, fe_id);
+		return -EINVAL;
+	}
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+		session_type = SESSION_TYPE_RX;
+		if (compr_passthr_mode != LEGACY_PCM)
+			path_type = ADM_PATH_COMPRESSED_RX;
+		else
+			path_type = ADM_PATH_PLAYBACK;
+		port_type = MSM_AFE_PORT_TYPE_RX;
+	} else if (stream_type == SNDRV_PCM_STREAM_CAPTURE) {
+		session_type = SESSION_TYPE_TX;
+		path_type = ADM_PATH_LIVE_REC;
+		port_type = MSM_AFE_PORT_TYPE_TX;
+	} else {
+		pr_err("%s: invalid stream type %d\n", __func__, stream_type);
+		return -EINVAL;
+	}
+
+	mutex_lock(&routing_lock);
+
+	payload.num_copps = 0; /* only RX needs to use payload */
+	fe_dai_map[fe_id][session_type].strm_id = dspst_id;
+	/* re-enable EQ if active */
+	msm_qti_pp_send_eq_values(fe_id);
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (test_bit(fe_id, &msm_bedais[i].fe_sessions))
+			msm_bedais[i].compr_passthr_mode = compr_passthr_mode;
+
+		if (!is_be_dai_extproc(i) &&
+			(afe_get_port_type(msm_bedais[i].port_id) ==
+			port_type) &&
+			(msm_bedais[i].active) &&
+			(test_bit(fe_id, &msm_bedais[i].fe_sessions))) {
+			int app_type, app_type_idx, copp_idx, acdb_dev_id;
+
+			/*
+			 * check if ADM needs to be configured with different
+			 * channel mapping than backend
+			 */
+			if (!msm_bedais[i].adm_override_ch)
+				channels = msm_bedais[i].channel;
+			else
+				channels = msm_bedais[i].adm_override_ch;
+
+			bit_width = msm_routing_get_bit_width(
+						msm_bedais[i].format);
+			app_type =
+			fe_dai_app_type_cfg[fe_id][session_type].app_type;
+			if (app_type) {
+				app_type_idx =
+					msm_pcm_routing_get_app_type_idx(
+						app_type);
+				sample_rate =
+			fe_dai_app_type_cfg[fe_id][session_type].sample_rate;
+				bit_width =
+					app_type_cfg[app_type_idx].bit_width;
+			} else {
+				sample_rate = msm_bedais[i].sample_rate;
+			}
+			acdb_dev_id =
+			fe_dai_app_type_cfg[fe_id][session_type].acdb_dev_id;
+			topology = msm_routing_get_adm_topology(path_type,
+						fe_id, session_type);
+			if (compr_passthr_mode == COMPRESSED_PASSTHROUGH_DSD)
+				topology = COMPRESS_PASSTHROUGH_NONE_TOPOLOGY;
+			pr_err("%s: Before adm open topology %d\n", __func__,
+				topology);
+
+			copp_idx =
+				adm_open(msm_bedais[i].port_id,
+					 path_type, sample_rate, channels,
+					 topology, perf_mode, bit_width,
+					 app_type, acdb_dev_id);
+			if ((copp_idx < 0) ||
+				(copp_idx >= MAX_COPPS_PER_PORT)) {
+				pr_err("%s:adm open failed coppid:%d\n",
+				__func__, copp_idx);
+				mutex_unlock(&routing_lock);
+				return -EINVAL;
+			}
+			pr_debug("%s: set idx bit of fe:%d, type: %d, be:%d\n",
+				 __func__, fe_id, session_type, i);
+			set_bit(copp_idx,
+				&session_copp_map[fe_id][session_type][i]);
+
+			if (msm_is_resample_needed(
+				sample_rate,
+				msm_bedais[i].sample_rate))
+				adm_copp_mfc_cfg(
+					msm_bedais[i].port_id, copp_idx,
+					msm_bedais[i].sample_rate);
+
+			for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+				unsigned long copp =
+				session_copp_map[fe_id][session_type][i];
+				if (test_bit(j, &copp)) {
+					payload.port_id[num_copps] =
+					msm_bedais[i].port_id;
+					payload.copp_idx[num_copps] = j;
+					num_copps++;
+				}
+			}
+			if (compr_passthr_mode != COMPRESSED_PASSTHROUGH_DSD) {
+				msm_routing_send_device_pp_params(
+				msm_bedais[i].port_id,
+				copp_idx);
+			}
+		}
+	}
+	if (num_copps) {
+		payload.num_copps = num_copps;
+		payload.session_id = fe_dai_map[fe_id][session_type].strm_id;
+		payload.app_type =
+			fe_dai_app_type_cfg[fe_id][session_type].app_type;
+		payload.acdb_dev_id =
+			fe_dai_app_type_cfg[fe_id][session_type].acdb_dev_id;
+		adm_matrix_map(path_type, payload, perf_mode);
+		msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
+	}
+	mutex_unlock(&routing_lock);
+	return 0;
+}
+
+static u32 msm_pcm_routing_get_voc_sessionid(u16 val)
+{
+	u32 session_id;
+
+	switch (val) {
+	case MSM_FRONTEND_DAI_CS_VOICE:
+		session_id = voc_get_session_id(VOICE_SESSION_NAME);
+		break;
+	case MSM_FRONTEND_DAI_VOLTE:
+		session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+		break;
+	case MSM_FRONTEND_DAI_VOWLAN:
+		session_id = voc_get_session_id(VOWLAN_SESSION_NAME);
+		break;
+	case MSM_FRONTEND_DAI_VOICE2:
+		session_id = voc_get_session_id(VOICE2_SESSION_NAME);
+		break;
+	case MSM_FRONTEND_DAI_QCHAT:
+		session_id = voc_get_session_id(QCHAT_SESSION_NAME);
+		break;
+	case MSM_FRONTEND_DAI_VOIP:
+		session_id = voc_get_session_id(VOIP_SESSION_NAME);
+		break;
+	case MSM_FRONTEND_DAI_VOICEMMODE1:
+		session_id = voc_get_session_id(VOICEMMODE1_NAME);
+		break;
+	case MSM_FRONTEND_DAI_VOICEMMODE2:
+		session_id = voc_get_session_id(VOICEMMODE2_NAME);
+		break;
+	default:
+		session_id = 0;
+	}
+
+	pr_debug("%s session_id 0x%x", __func__, session_id);
+	return session_id;
+}
+
+int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode,
+					int dspst_id, int stream_type)
+{
+	int i, j, session_type, path_type, port_type, topology, num_copps = 0;
+	struct route_payload payload;
+	u32 channels, sample_rate;
+	uint16_t bits_per_sample = 16;
+
+	if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* bad ID assigned in machine driver */
+		pr_err("%s: bad MM ID %d\n", __func__, fedai_id);
+		return -EINVAL;
+	}
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+		session_type = SESSION_TYPE_RX;
+		path_type = ADM_PATH_PLAYBACK;
+		port_type = MSM_AFE_PORT_TYPE_RX;
+	} else {
+		session_type = SESSION_TYPE_TX;
+		path_type = ADM_PATH_LIVE_REC;
+		port_type = MSM_AFE_PORT_TYPE_TX;
+	}
+
+	mutex_lock(&routing_lock);
+
+	payload.num_copps = 0; /* only RX needs to use payload */
+	fe_dai_map[fedai_id][session_type].strm_id = dspst_id;
+	fe_dai_map[fedai_id][session_type].perf_mode = perf_mode;
+
+	/* re-enable EQ if active */
+	msm_qti_pp_send_eq_values(fedai_id);
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (!is_be_dai_extproc(i) &&
+		   (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+		   (msm_bedais[i].active) &&
+		   (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+			int app_type, app_type_idx, copp_idx, acdb_dev_id;
+			/*
+			 * check if ADM needs to be configured with different
+			 * channel mapping than backend
+			 */
+			if (!msm_bedais[i].adm_override_ch)
+				channels = msm_bedais[i].channel;
+			else
+				channels = msm_bedais[i].adm_override_ch;
+			msm_bedais[i].compr_passthr_mode =
+				LEGACY_PCM;
+
+			bits_per_sample = msm_routing_get_bit_width(
+						msm_bedais[i].format);
+
+			app_type =
+			fe_dai_app_type_cfg[fedai_id][session_type].app_type;
+			if (app_type) {
+				app_type_idx =
+				msm_pcm_routing_get_app_type_idx(app_type);
+				sample_rate =
+				fe_dai_app_type_cfg[fedai_id][session_type].
+					sample_rate;
+				bits_per_sample =
+					app_type_cfg[app_type_idx].bit_width;
+			} else
+				sample_rate = msm_bedais[i].sample_rate;
+
+			acdb_dev_id =
+			fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id;
+			topology = msm_routing_get_adm_topology(path_type,
+						fedai_id, session_type);
+			copp_idx = adm_open(msm_bedais[i].port_id, path_type,
+					    sample_rate, channels, topology,
+					    perf_mode, bits_per_sample,
+					    app_type, acdb_dev_id);
+			if ((copp_idx < 0) ||
+				(copp_idx >= MAX_COPPS_PER_PORT)) {
+				pr_err("%s: adm open failed copp_idx:%d\n",
+					__func__, copp_idx);
+				mutex_unlock(&routing_lock);
+				return -EINVAL;
+			}
+			pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n",
+				 __func__, fedai_id, session_type, i);
+			set_bit(copp_idx,
+				&session_copp_map[fedai_id][session_type][i]);
+
+			if (msm_is_resample_needed(
+				sample_rate,
+				msm_bedais[i].sample_rate))
+				adm_copp_mfc_cfg(
+					msm_bedais[i].port_id, copp_idx,
+					msm_bedais[i].sample_rate);
+
+			for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+				unsigned long copp =
+				    session_copp_map[fedai_id][session_type][i];
+				if (test_bit(j, &copp)) {
+					payload.port_id[num_copps] =
+							msm_bedais[i].port_id;
+					payload.copp_idx[num_copps] = j;
+					num_copps++;
+				}
+			}
+			if ((perf_mode == LEGACY_PCM_MODE) &&
+				(msm_bedais[i].compr_passthr_mode ==
+				LEGACY_PCM))
+				msm_pcm_routing_cfg_pp(msm_bedais[i].port_id,
+						       copp_idx, topology,
+						       channels);
+		}
+	}
+	if (num_copps) {
+		payload.num_copps = num_copps;
+		payload.session_id = fe_dai_map[fedai_id][session_type].strm_id;
+		payload.app_type =
+			fe_dai_app_type_cfg[fedai_id][session_type].app_type;
+		payload.acdb_dev_id =
+			fe_dai_app_type_cfg[fedai_id][session_type].acdb_dev_id;
+		payload.sample_rate =
+			fe_dai_app_type_cfg[fedai_id][session_type].sample_rate;
+		adm_matrix_map(path_type, payload, perf_mode);
+		msm_pcm_routng_cfg_matrix_map_pp(payload, path_type, perf_mode);
+	}
+	mutex_unlock(&routing_lock);
+	return 0;
+}
+
+int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode,
+				      int dspst_id, int stream_type,
+				      struct msm_pcm_routing_evt event_info)
+{
+	if (msm_pcm_routing_reg_phy_stream(fedai_id, perf_mode, dspst_id,
+				       stream_type)) {
+		pr_err("%s: failed to reg phy stream\n", __func__);
+		return -EINVAL;
+	}
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK)
+		fe_dai_map[fedai_id][SESSION_TYPE_RX].event_info = event_info;
+	else
+		fe_dai_map[fedai_id][SESSION_TYPE_TX].event_info = event_info;
+	return 0;
+}
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type)
+{
+	int i, port_type, session_type, path_type, topology;
+	struct msm_pcm_routing_fdai_data *fdai;
+
+	if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* bad ID assigned in machine driver */
+		pr_err("%s: bad MM ID\n", __func__);
+		return;
+	}
+
+	if (stream_type == SNDRV_PCM_STREAM_PLAYBACK) {
+		port_type = MSM_AFE_PORT_TYPE_RX;
+		session_type = SESSION_TYPE_RX;
+		path_type = ADM_PATH_PLAYBACK;
+	} else {
+		port_type = MSM_AFE_PORT_TYPE_TX;
+		session_type = SESSION_TYPE_TX;
+		path_type = ADM_PATH_LIVE_REC;
+	}
+
+	mutex_lock(&routing_lock);
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (!is_be_dai_extproc(i) &&
+		   (afe_get_port_type(msm_bedais[i].port_id) == port_type) &&
+		   (msm_bedais[i].active) &&
+		   (test_bit(fedai_id, &msm_bedais[i].fe_sessions))) {
+			int idx;
+			unsigned long copp =
+				session_copp_map[fedai_id][session_type][i];
+			fdai = &fe_dai_map[fedai_id][session_type];
+
+			for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+				if (test_bit(idx, &copp))
+					break;
+
+			if (idx >= MAX_COPPS_PER_PORT || idx < 0) {
+				pr_debug("%s: copp idx is invalid, exiting\n",
+								__func__);
+				continue;
+			}
+			topology = adm_get_topology_for_port_copp_idx(
+					msm_bedais[i].port_id, idx);
+			adm_close(msm_bedais[i].port_id, fdai->perf_mode, idx);
+			pr_debug("%s:copp:%ld,idx bit fe:%d,type:%d,be:%d\n",
+				 __func__, copp, fedai_id, session_type, i);
+			clear_bit(idx,
+				  &session_copp_map[fedai_id][session_type][i]);
+			if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID ||
+				topology == DS2_ADM_COPP_TOPOLOGY_ID) &&
+			    (fdai->perf_mode == LEGACY_PCM_MODE) &&
+			    (msm_bedais[i].compr_passthr_mode ==
+					LEGACY_PCM))
+				msm_pcm_routing_deinit_pp(msm_bedais[i].port_id,
+							  topology);
+		}
+	}
+
+	fe_dai_map[fedai_id][session_type].strm_id = INVALID_SESSION;
+	fe_dai_map[fedai_id][session_type].be_srate = 0;
+	mutex_unlock(&routing_lock);
+}
+
+/* Check if FE/BE route is set */
+static bool msm_pcm_routing_route_is_set(u16 be_id, u16 fe_id)
+{
+	bool rc = false;
+
+	if (fe_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* recheck FE ID in the mixer control defined in this file */
+		pr_err("%s: bad MM ID\n", __func__);
+		return rc;
+	}
+
+	if (test_bit(fe_id, &msm_bedais[be_id].fe_sessions))
+		rc = true;
+
+	return rc;
+}
+
+static void msm_pcm_routing_process_audio(u16 reg, u16 val, int set)
+{
+	int session_type, path_type, topology;
+	u32 channels, sample_rate;
+	uint16_t bits_per_sample = 16;
+	struct msm_pcm_routing_fdai_data *fdai;
+
+	pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+	if (val > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* recheck FE ID in the mixer control defined in this file */
+		pr_err("%s: bad MM ID\n", __func__);
+		return;
+	}
+
+	if (afe_get_port_type(msm_bedais[reg].port_id) ==
+		MSM_AFE_PORT_TYPE_RX) {
+		session_type = SESSION_TYPE_RX;
+		if (msm_bedais[reg].compr_passthr_mode != LEGACY_PCM)
+			path_type = ADM_PATH_COMPRESSED_RX;
+		else
+			path_type = ADM_PATH_PLAYBACK;
+	} else {
+		session_type = SESSION_TYPE_TX;
+		path_type = ADM_PATH_LIVE_REC;
+	}
+
+	mutex_lock(&routing_lock);
+	if (set) {
+		if (!test_bit(val, &msm_bedais[reg].fe_sessions) &&
+			((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) ||
+			(msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX)))
+			voc_start_playback(set, msm_bedais[reg].port_id);
+
+		set_bit(val, &msm_bedais[reg].fe_sessions);
+		fdai = &fe_dai_map[val][session_type];
+		if (msm_bedais[reg].active && fdai->strm_id !=
+			INVALID_SESSION) {
+			int app_type, app_type_idx, copp_idx, acdb_dev_id;
+			/*
+			 * check if ADM needs to be configured with different
+			 * channel mapping than backend
+			 */
+			if (!msm_bedais[reg].adm_override_ch)
+				channels = msm_bedais[reg].channel;
+			else
+				channels = msm_bedais[reg].adm_override_ch;
+			if (session_type == SESSION_TYPE_TX &&
+			    fdai->be_srate &&
+			    (fdai->be_srate != msm_bedais[reg].sample_rate)) {
+				pr_debug("%s: flush strm %d diff BE rates\n",
+					__func__, fdai->strm_id);
+
+				if (fdai->event_info.event_func)
+					fdai->event_info.event_func(
+						MSM_PCM_RT_EVT_BUF_RECFG,
+						fdai->event_info.priv_data);
+				fdai->be_srate = 0; /* might not need it */
+			}
+
+			bits_per_sample = msm_routing_get_bit_width(
+						msm_bedais[reg].format);
+
+			app_type =
+				fe_dai_app_type_cfg[val][session_type].app_type;
+			if (app_type) {
+				app_type_idx =
+				msm_pcm_routing_get_app_type_idx(app_type);
+				sample_rate =
+				fe_dai_app_type_cfg[val][session_type].
+					sample_rate;
+				bits_per_sample =
+					app_type_cfg[app_type_idx].bit_width;
+			} else
+				sample_rate = msm_bedais[reg].sample_rate;
+
+			topology = msm_routing_get_adm_topology(path_type, val,
+						session_type);
+			acdb_dev_id =
+			fe_dai_app_type_cfg[val][session_type].acdb_dev_id;
+			copp_idx = adm_open(msm_bedais[reg].port_id, path_type,
+					    sample_rate, channels, topology,
+					    fdai->perf_mode, bits_per_sample,
+					    app_type, acdb_dev_id);
+			if ((copp_idx < 0) ||
+			    (copp_idx >= MAX_COPPS_PER_PORT)) {
+				pr_err("%s: adm open failed\n", __func__);
+				mutex_unlock(&routing_lock);
+				return;
+			}
+			pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n",
+				 __func__, val, session_type, reg);
+			set_bit(copp_idx,
+				&session_copp_map[val][session_type][reg]);
+
+			if (msm_is_resample_needed(
+				sample_rate,
+				msm_bedais[reg].sample_rate))
+				adm_copp_mfc_cfg(
+					msm_bedais[reg].port_id, copp_idx,
+					msm_bedais[reg].sample_rate);
+
+			if (session_type == SESSION_TYPE_RX &&
+			    fdai->event_info.event_func)
+				fdai->event_info.event_func(
+					MSM_PCM_RT_EVT_DEVSWITCH,
+					fdai->event_info.priv_data);
+
+			msm_pcm_routing_build_matrix(val, session_type,
+						     path_type,
+						     fdai->perf_mode);
+			if ((fdai->perf_mode == LEGACY_PCM_MODE) &&
+				(msm_bedais[reg].compr_passthr_mode ==
+					LEGACY_PCM))
+				msm_pcm_routing_cfg_pp(msm_bedais[reg].port_id,
+						       copp_idx, topology,
+						       channels);
+		}
+	} else {
+		if (test_bit(val, &msm_bedais[reg].fe_sessions) &&
+			((msm_bedais[reg].port_id == VOICE_PLAYBACK_TX) ||
+			(msm_bedais[reg].port_id == VOICE2_PLAYBACK_TX)))
+			voc_start_playback(set, msm_bedais[reg].port_id);
+		clear_bit(val, &msm_bedais[reg].fe_sessions);
+		fdai = &fe_dai_map[val][session_type];
+		if (msm_bedais[reg].active && fdai->strm_id !=
+			INVALID_SESSION) {
+			int idx;
+			int port_id;
+			unsigned long copp =
+				session_copp_map[val][session_type][reg];
+			for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+				if (test_bit(idx, &copp))
+					break;
+
+			port_id = msm_bedais[reg].port_id;
+			topology = adm_get_topology_for_port_copp_idx(port_id,
+								      idx);
+			adm_close(msm_bedais[reg].port_id, fdai->perf_mode,
+				  idx);
+			pr_debug("%s: copp: %ld, reset idx bit fe:%d, type: %d, be:%d topology=0x%x\n",
+				 __func__, copp, val, session_type, reg,
+				 topology);
+			clear_bit(idx,
+				  &session_copp_map[val][session_type][reg]);
+			if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID ||
+				topology == DS2_ADM_COPP_TOPOLOGY_ID) &&
+			    (fdai->perf_mode == LEGACY_PCM_MODE) &&
+			    (msm_bedais[reg].compr_passthr_mode ==
+				LEGACY_PCM))
+				msm_pcm_routing_deinit_pp(
+						msm_bedais[reg].port_id,
+						topology);
+			msm_pcm_routing_build_matrix(val, session_type,
+						     path_type,
+						     fdai->perf_mode);
+		}
+	}
+	if ((msm_bedais[reg].port_id == VOICE_RECORD_RX)
+			|| (msm_bedais[reg].port_id == VOICE_RECORD_TX))
+		voc_start_record(msm_bedais[reg].port_id, set, voc_session_id);
+
+	mutex_unlock(&routing_lock);
+}
+
+static int msm_routing_get_audio_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+	(struct soc_mixer_control *)kcontrol->private_value;
+
+	if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+	ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+
+	if (ucontrol->value.integer.value[0] &&
+	   msm_pcm_routing_route_is_set(mc->reg, mc->shift) == false) {
+		msm_pcm_routing_process_audio(mc->reg, mc->shift, 1);
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+			update);
+	} else if (!ucontrol->value.integer.value[0] &&
+		  msm_pcm_routing_route_is_set(mc->reg, mc->shift) == true) {
+		msm_pcm_routing_process_audio(mc->reg, mc->shift, 0);
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+			update);
+	}
+
+	return 1;
+}
+
+static void msm_pcm_routing_process_voice(u16 reg, u16 val, int set)
+{
+	u32 session_id = 0;
+	u16 path_type;
+
+	pr_debug("%s: reg %x val %x set %x\n", __func__, reg, val, set);
+
+	session_id = msm_pcm_routing_get_voc_sessionid(val);
+
+	pr_debug("%s: FE DAI 0x%x session_id 0x%x\n",
+		__func__, val, session_id);
+
+	mutex_lock(&routing_lock);
+
+	if (set)
+		set_bit(val, &msm_bedais[reg].fe_sessions);
+	else
+		clear_bit(val, &msm_bedais[reg].fe_sessions);
+
+	if (val == MSM_FRONTEND_DAI_DTMF_RX &&
+	    afe_get_port_type(msm_bedais[reg].port_id) ==
+						MSM_AFE_PORT_TYPE_RX) {
+		pr_debug("%s(): set=%d port id=0x%x for dtmf generation\n",
+			 __func__, set, msm_bedais[reg].port_id);
+		afe_set_dtmf_gen_rx_portid(msm_bedais[reg].port_id, set);
+	}
+
+	if (afe_get_port_type(msm_bedais[reg].port_id) ==
+						MSM_AFE_PORT_TYPE_RX)
+		path_type = RX_PATH;
+	else
+		path_type = TX_PATH;
+
+	if (set) {
+		if (msm_bedais[reg].active) {
+			voc_set_route_flag(session_id, path_type, 1);
+			voc_set_device_config(session_id, path_type,
+			   msm_bedais[reg].channel, msm_bedais[reg].port_id);
+
+			if (voc_get_route_flag(session_id, TX_PATH) &&
+				voc_get_route_flag(session_id, RX_PATH))
+				voc_enable_device(session_id);
+		} else {
+			pr_debug("%s BE is not active\n", __func__);
+		}
+	} else {
+		voc_set_route_flag(session_id, path_type, 0);
+		voc_disable_device(session_id);
+	}
+
+	mutex_unlock(&routing_lock);
+
+}
+
+static int msm_routing_get_voice_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+	(struct soc_mixer_control *)kcontrol->private_value;
+
+	mutex_lock(&routing_lock);
+
+	if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	mutex_unlock(&routing_lock);
+
+	pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+			ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm_routing_put_voice_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+
+	if (ucontrol->value.integer.value[0]) {
+		msm_pcm_routing_process_voice(mc->reg, mc->shift, 1);
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	} else {
+		msm_pcm_routing_process_voice(mc->reg, mc->shift, 0);
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	}
+
+	return 1;
+}
+
+static int msm_routing_get_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	mutex_lock(&routing_lock);
+
+	if (test_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions))
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	mutex_unlock(&routing_lock);
+
+	pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+		ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm_routing_put_voice_stub_mixer(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+
+	if (ucontrol->value.integer.value[0]) {
+		mutex_lock(&routing_lock);
+		set_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions);
+		mutex_unlock(&routing_lock);
+
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	} else {
+		mutex_lock(&routing_lock);
+		clear_bit(mc->shift, &msm_bedais[mc->reg].fe_sessions);
+		mutex_unlock(&routing_lock);
+
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	}
+
+	pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+		ucontrol->value.integer.value[0]);
+
+	return 1;
+}
+
+static int msm_routing_get_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = fm_switch_enable;
+	pr_debug("%s: FM Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: FM Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	fm_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_get_hfp_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = hfp_switch_enable;
+	pr_debug("%s: HFP Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_hfp_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: HFP Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
+						1, update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol,
+						0, update);
+	hfp_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_get_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = pri_mi2s_switch_enable;
+	pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_pri_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: PRI MI2S Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	pri_mi2s_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_get_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = sec_mi2s_switch_enable;
+	pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_sec_mi2s_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: SEC MI2S Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	sec_mi2s_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_get_tert_mi2s_switch_mixer(
+				struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = tert_mi2s_switch_enable;
+	pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_tert_mi2s_switch_mixer(
+				struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: TERT MI2S Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	tert_mi2s_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_get_quat_mi2s_switch_mixer(
+				struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = quat_mi2s_switch_enable;
+	pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_quat_mi2s_switch_mixer(
+				struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: QUAT MI2S Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	quat_mi2s_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_get_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = fm_pcmrx_switch_enable;
+	pr_debug("%s: FM Switch enable %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_routing_put_fm_pcmrx_switch_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: FM Switch enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	if (ucontrol->value.integer.value[0])
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 1,
+						update);
+	else
+		snd_soc_dapm_mixer_update_power(widget->dapm, kcontrol, 0,
+						update);
+	fm_pcmrx_switch_enable = ucontrol->value.integer.value[0];
+	return 1;
+}
+
+static int msm_routing_lsm_mux_get(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = lsm_mux_slim_port;
+	return 0;
+}
+
+static int msm_routing_lsm_mux_put(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	int mux = ucontrol->value.enumerated.item[0];
+	int lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+	struct snd_soc_dapm_update *update = NULL;
+
+	pr_debug("%s: LSM enable %ld\n", __func__,
+			ucontrol->value.integer.value[0]);
+	switch (ucontrol->value.integer.value[0]) {
+	case 1:
+		lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX;
+		break;
+	case 2:
+		lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX;
+		break;
+	case 3:
+		lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX;
+		break;
+	case 4:
+		lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX;
+		break;
+	case 5:
+		lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
+		break;
+	case 6:
+		lsm_port = AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+		break;
+	case 7:
+		lsm_port = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		break;
+	default:
+		pr_err("Default lsm port");
+		break;
+	}
+	set_lsm_port(lsm_port);
+
+	if (ucontrol->value.integer.value[0]) {
+		lsm_mux_slim_port = ucontrol->value.integer.value[0];
+		snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e,
+						update);
+	} else {
+		snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e,
+						update);
+		lsm_mux_slim_port = ucontrol->value.integer.value[0];
+	}
+
+	return 0;
+}
+
+static int msm_routing_lsm_func_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	u16 port_id;
+	enum afe_mad_type mad_type;
+
+	pr_debug("%s: enter\n", __func__);
+	for (i = 0; i < ARRAY_SIZE(mad_audio_mux_text); i++)
+		if (!strcmp(kcontrol->id.name, mad_audio_mux_text[i]))
+			break;
+
+	if (i-- == ARRAY_SIZE(mad_audio_mux_text)) {
+		WARN(1, "Invalid id name %s\n", kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	/*Check for Tertiary TX port*/
+	if (!strcmp(kcontrol->id.name, mad_audio_mux_text[7])) {
+		ucontrol->value.integer.value[0] = MADSWAUDIO;
+		return 0;
+	}
+
+	port_id = i * 2 + 1 + SLIMBUS_0_RX;
+	mad_type = afe_port_get_mad_type(port_id);
+	pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+		 mad_type);
+	switch (mad_type) {
+	case MAD_HW_NONE:
+		ucontrol->value.integer.value[0] = MADNONE;
+		break;
+	case MAD_HW_AUDIO:
+		ucontrol->value.integer.value[0] = MADAUDIO;
+		break;
+	case MAD_HW_BEACON:
+		ucontrol->value.integer.value[0] = MADBEACON;
+		break;
+	case MAD_HW_ULTRASOUND:
+		ucontrol->value.integer.value[0] = MADULTRASOUND;
+		break;
+	case MAD_SW_AUDIO:
+		ucontrol->value.integer.value[0] = MADSWAUDIO;
+	break;
+	default:
+		WARN(1, "Unknown\n");
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int msm_routing_lsm_func_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	int i;
+	u16 port_id;
+	enum afe_mad_type mad_type;
+
+	pr_debug("%s: enter\n", __func__);
+	for (i = 0; i < ARRAY_SIZE(mad_audio_mux_text); i++)
+		if (!strcmp(kcontrol->id.name, mad_audio_mux_text[i]))
+			break;
+
+	if (i-- == ARRAY_SIZE(mad_audio_mux_text)) {
+		WARN(1, "Invalid id name %s\n", kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	port_id = i * 2 + 1 + SLIMBUS_0_RX;
+	switch (ucontrol->value.integer.value[0]) {
+	case MADNONE:
+		mad_type = MAD_HW_NONE;
+		break;
+	case MADAUDIO:
+		mad_type = MAD_HW_AUDIO;
+		break;
+	case MADBEACON:
+		mad_type = MAD_HW_BEACON;
+		break;
+	case MADULTRASOUND:
+		mad_type = MAD_HW_ULTRASOUND;
+		break;
+	case MADSWAUDIO:
+		mad_type = MAD_SW_AUDIO;
+		break;
+	default:
+		WARN(1, "Unknown\n");
+		return -EINVAL;
+	}
+
+	/*Check for Tertiary TX port*/
+	if (!strcmp(kcontrol->id.name, mad_audio_mux_text[7])) {
+		port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		mad_type = MAD_SW_AUDIO;
+	}
+
+	pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+		 mad_type);
+	return afe_port_set_mad_type(port_id, mad_type);
+}
+
+static const char *const adm_override_chs_text[] = {"Zero", "One", "Two"};
+
+static SOC_ENUM_SINGLE_EXT_DECL(slim_7_rx_adm_override_chs,
+				adm_override_chs_text);
+
+static int msm_routing_adm_get_backend_idx(struct snd_kcontrol *kcontrol)
+{
+	int backend_id;
+
+	if (strnstr(kcontrol->id.name, "SLIM7_RX", sizeof("SLIM7_RX"))) {
+		backend_id = MSM_BACKEND_DAI_SLIMBUS_7_RX;
+	} else {
+		pr_err("%s: unsupported backend id: %s",
+			__func__, kcontrol->id.name);
+		return -EINVAL;
+	}
+
+	return backend_id;
+}
+static int msm_routing_adm_channel_config_get(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int backend_id = msm_routing_adm_get_backend_idx(kcontrol);
+
+	if (backend_id >= 0) {
+		mutex_lock(&routing_lock);
+		ucontrol->value.integer.value[0] =
+			 msm_bedais[backend_id].adm_override_ch;
+		pr_debug("%s: adm channel count %ld for BE:%d\n", __func__,
+			 ucontrol->value.integer.value[0], backend_id);
+		 mutex_unlock(&routing_lock);
+	}
+
+	return 0;
+}
+
+static int msm_routing_adm_channel_config_put(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int backend_id = msm_routing_adm_get_backend_idx(kcontrol);
+
+	if (backend_id >= 0) {
+		mutex_lock(&routing_lock);
+		msm_bedais[backend_id].adm_override_ch =
+				 ucontrol->value.integer.value[0];
+		pr_debug("%s:updating BE :%d  adm channels: %d\n",
+			  __func__, backend_id,
+			  msm_bedais[backend_id].adm_override_ch);
+		mutex_unlock(&routing_lock);
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new adm_channel_config_controls[] = {
+	SOC_ENUM_EXT("SLIM7_RX ADM Channels", slim_7_rx_adm_override_chs,
+			msm_routing_adm_channel_config_get,
+			msm_routing_adm_channel_config_put),
+};
+
+static int msm_routing_slim_0_rx_aanc_mux_get(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+
+	mutex_lock(&routing_lock);
+	ucontrol->value.integer.value[0] = slim0_rx_aanc_fb_port;
+	mutex_unlock(&routing_lock);
+	pr_debug("%s: AANC Mux Port %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+};
+
+static int msm_routing_slim_0_rx_aanc_mux_put(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	struct aanc_data aanc_info;
+
+	mutex_lock(&routing_lock);
+	memset(&aanc_info, 0x00, sizeof(aanc_info));
+	pr_debug("%s: AANC Mux Port %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	slim0_rx_aanc_fb_port = ucontrol->value.integer.value[0];
+	if (ucontrol->value.integer.value[0] == 0) {
+		aanc_info.aanc_active = false;
+		aanc_info.aanc_tx_port = 0;
+		aanc_info.aanc_rx_port = 0;
+	} else {
+		aanc_info.aanc_active = true;
+		aanc_info.aanc_rx_port = SLIMBUS_0_RX;
+		aanc_info.aanc_tx_port =
+			(SLIMBUS_0_RX - 1 + (slim0_rx_aanc_fb_port * 2));
+	}
+	afe_set_aanc_info(&aanc_info);
+	mutex_unlock(&routing_lock);
+	return 0;
+};
+static int msm_routing_get_port_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = 0, shift = 0;
+	struct soc_mixer_control *mc =
+	(struct soc_mixer_control *)kcontrol->private_value;
+
+	idx = mc->shift/(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8);
+	shift = mc->shift%(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8);
+
+	if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) {
+		pr_err("%s: Invalid idx = %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	if (test_bit(shift,
+		(unsigned long *)&msm_bedais[mc->reg].port_sessions[idx]))
+		ucontrol->value.integer.value[0] = 1;
+	else
+		ucontrol->value.integer.value[0] = 0;
+
+	pr_debug("%s: reg %x shift %x val %ld\n", __func__, mc->reg, mc->shift,
+	ucontrol->value.integer.value[0]);
+
+	return 0;
+}
+
+static int msm_routing_put_port_mixer(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int idx = 0, shift = 0;
+	struct soc_mixer_control *mc =
+		(struct soc_mixer_control *)kcontrol->private_value;
+
+	idx = mc->shift/(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8);
+	shift = mc->shift%(sizeof(msm_bedais[mc->reg].port_sessions[0]) * 8);
+
+	if (idx >= BE_DAI_PORT_SESSIONS_IDX_MAX) {
+		pr_err("%s: Invalid idx = %d\n", __func__, idx);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: reg 0x%x shift 0x%x val %ld idx %d reminder shift %d\n",
+		 __func__, mc->reg, mc->shift,
+		 ucontrol->value.integer.value[0], idx, shift);
+
+	if (ucontrol->value.integer.value[0]) {
+		afe_loopback(1, msm_bedais[mc->reg].port_id,
+			    msm_bedais[mc->shift].port_id);
+		set_bit(shift,
+		(unsigned long *)&msm_bedais[mc->reg].port_sessions[idx]);
+	} else {
+		afe_loopback(0, msm_bedais[mc->reg].port_id,
+			    msm_bedais[mc->shift].port_id);
+		clear_bit(shift,
+		(unsigned long *)&msm_bedais[mc->reg].port_sessions[idx]);
+	}
+
+	return 1;
+}
+
+static int msm_routing_ec_ref_rx_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: ec_ref_rx  = %d", __func__, msm_route_ec_ref_rx);
+	mutex_lock(&routing_lock);
+	ucontrol->value.integer.value[0] = msm_route_ec_ref_rx;
+	mutex_unlock(&routing_lock);
+	return 0;
+}
+
+static int msm_routing_ec_ref_rx_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int ec_ref_port_id;
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	int mux = ucontrol->value.enumerated.item[0];
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	struct snd_soc_dapm_update *update = NULL;
+
+	mutex_lock(&routing_lock);
+	switch (ucontrol->value.integer.value[0]) {
+	case 0:
+		msm_route_ec_ref_rx = 0;
+		ec_ref_port_id = AFE_PORT_INVALID;
+		break;
+	case 1:
+		msm_route_ec_ref_rx = 1;
+		ec_ref_port_id = SLIMBUS_0_RX;
+		break;
+	case 2:
+		msm_route_ec_ref_rx = 2;
+		ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_RX;
+		break;
+	case 3:
+		msm_route_ec_ref_rx = 3;
+		ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+		break;
+	case 4:
+		msm_route_ec_ref_rx = 4;
+		ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+		break;
+	case 5:
+		msm_route_ec_ref_rx = 5;
+		ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		break;
+	case 6:
+		msm_route_ec_ref_rx = 6;
+		ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+		break;
+	case 7:
+		msm_route_ec_ref_rx = 7;
+		ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_RX;
+		break;
+	case 9:
+		msm_route_ec_ref_rx = 9;
+		ec_ref_port_id = SLIMBUS_5_RX;
+		break;
+	case 10:
+		msm_route_ec_ref_rx = 10;
+		ec_ref_port_id = SLIMBUS_1_TX;
+		break;
+	case 11:
+		msm_route_ec_ref_rx = 11;
+		ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+		break;
+	case 12:
+		msm_route_ec_ref_rx = 12;
+		ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX;
+		break;
+	case 13:
+		msm_route_ec_ref_rx = 13;
+		ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+		break;
+	case 14:
+		msm_route_ec_ref_rx = 14;
+		ec_ref_port_id = AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+		break;
+	case 15:
+		msm_route_ec_ref_rx = 15;
+		ec_ref_port_id = SLIMBUS_6_RX;
+		break;
+	case 16:
+		msm_route_ec_ref_rx = 16;
+		ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_RX;
+		break;
+	case 17:
+		msm_route_ec_ref_rx = 17;
+		ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_RX;
+		break;
+	case 18:
+		msm_route_ec_ref_rx = 18;
+		ec_ref_port_id = AFE_PORT_ID_TERTIARY_TDM_TX;
+		break;
+	case 19:
+		msm_route_ec_ref_rx = 19;
+		ec_ref_port_id = AFE_PORT_ID_USB_RX;
+		break;
+	default:
+		msm_route_ec_ref_rx = 0; /* NONE */
+		pr_err("%s EC ref rx %ld not valid\n",
+			__func__, ucontrol->value.integer.value[0]);
+		ec_ref_port_id = AFE_PORT_INVALID;
+		break;
+	}
+	adm_ec_ref_rx_id(ec_ref_port_id);
+	pr_debug("%s: msm_route_ec_ref_rx = %d\n",
+	    __func__, msm_route_ec_ref_rx);
+	mutex_unlock(&routing_lock);
+	snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e, update);
+	return 0;
+}
+
+static const char *const ec_ref_rx[] = { "None", "SLIM_RX", "I2S_RX",
+	"PRI_MI2S_TX", "SEC_MI2S_TX",
+	"TERT_MI2S_TX", "QUAT_MI2S_TX", "SEC_I2S_RX", "PROXY_RX",
+	"SLIM_5_RX", "SLIM_1_TX", "QUAT_TDM_TX_1",
+	"QUAT_TDM_RX_0", "QUAT_TDM_RX_1", "QUAT_TDM_RX_2", "SLIM_6_RX",
+	"TERT_MI2S_RX", "QUAT_MI2S_RX", "TERT_TDM_TX_0", "USB_AUDIO_RX"};
+
+static const struct soc_enum msm_route_ec_ref_rx_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ec_ref_rx), ec_ref_rx),
+};
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul1 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL1 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul2 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL2 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul3 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL3 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul4 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL4 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul5 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL5 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul6 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL6 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul8 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL8 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul9 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL9 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul17 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL17 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul18 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL18 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static const struct snd_kcontrol_new ext_ec_ref_mux_ul19 =
+	SOC_DAPM_ENUM_EXT("AUDIO_REF_EC_UL19 MUX Mux",
+		msm_route_ec_ref_rx_enum[0],
+		msm_routing_ec_ref_rx_get, msm_routing_ec_ref_rx_put);
+
+static int msm_routing_ext_ec_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s: ext_ec_ref_rx  = %x\n", __func__, msm_route_ext_ec_ref);
+
+	mutex_lock(&routing_lock);
+	ucontrol->value.integer.value[0] = msm_route_ext_ec_ref;
+	mutex_unlock(&routing_lock);
+	return 0;
+}
+
+static int msm_routing_ext_ec_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	struct snd_soc_dapm_widget_list *wlist =
+					dapm_kcontrol_get_wlist(kcontrol);
+	struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+	int mux = ucontrol->value.enumerated.item[0];
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+	int ret = 1;
+	bool state = true;
+	uint16_t ext_ec_ref_port_id;
+	struct snd_soc_dapm_update *update = NULL;
+
+	mutex_lock(&routing_lock);
+	msm_route_ext_ec_ref = ucontrol->value.integer.value[0];
+
+	switch (msm_route_ext_ec_ref) {
+	case EXT_EC_REF_PRI_MI2S_TX:
+		ext_ec_ref_port_id = AFE_PORT_ID_PRIMARY_MI2S_TX;
+		break;
+	case EXT_EC_REF_SEC_MI2S_TX:
+		ext_ec_ref_port_id = AFE_PORT_ID_SECONDARY_MI2S_TX;
+		break;
+	case EXT_EC_REF_TERT_MI2S_TX:
+		ext_ec_ref_port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+		break;
+	case EXT_EC_REF_QUAT_MI2S_TX:
+		ext_ec_ref_port_id = AFE_PORT_ID_QUATERNARY_MI2S_TX;
+		break;
+	case EXT_EC_REF_QUIN_MI2S_TX:
+		ext_ec_ref_port_id = AFE_PORT_ID_QUINARY_MI2S_TX;
+		break;
+	case EXT_EC_REF_SLIM_1_TX:
+		ext_ec_ref_port_id = SLIMBUS_1_TX;
+		break;
+	case EXT_EC_REF_NONE:
+	default:
+		ext_ec_ref_port_id = AFE_PORT_INVALID;
+		state = false;
+		break;
+	}
+
+	pr_debug("%s: val = %d ext_ec_ref_port_id = 0x%0x state = %d\n",
+		__func__, msm_route_ext_ec_ref, ext_ec_ref_port_id, state);
+
+	if (!voc_set_ext_ec_ref(ext_ec_ref_port_id, state)) {
+		mutex_unlock(&routing_lock);
+		snd_soc_dapm_mux_update_power(widget->dapm, kcontrol, mux, e,
+						update);
+	} else {
+		ret = -EINVAL;
+		mutex_unlock(&routing_lock);
+	}
+	return ret;
+}
+
+static const char * const ext_ec_ref_rx[] = {"NONE", "PRI_MI2S_TX",
+					"SEC_MI2S_TX", "TERT_MI2S_TX",
+					"QUAT_MI2S_TX", "QUIN_MI2S_TX",
+					"SLIM_1_TX"};
+
+static const struct soc_enum msm_route_ext_ec_ref_rx_enum[] = {
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(ext_ec_ref_rx), ext_ec_ref_rx),
+};
+
+static const struct snd_kcontrol_new voc_ext_ec_mux =
+	SOC_DAPM_ENUM_EXT("VOC_EXT_EC MUX Mux", msm_route_ext_ec_ref_rx_enum[0],
+			  msm_routing_ext_ec_get, msm_routing_ext_ec_put);
+
+
+static const struct snd_kcontrol_new pri_i2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new spdif_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+
+};
+
+static const struct snd_kcontrol_new slimbus_2_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_5_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quaternary_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quinary_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tertiary_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx2_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new secondary_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new primary_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int0_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int4_mi2s_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new display_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+	/* incall music delivery mixer */
+static const struct snd_kcontrol_new incall_music_delivery_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new incall_music2_delivery_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_4_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_4_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_4_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_4_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_4_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_6_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_7_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new usb_audio_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_sco_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_bt_a2dp_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new int_fm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new auxpcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_auxpcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia17", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia18", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia19", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_auxpcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_auxpcm_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_rx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new pri_tdm_tx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_rx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new sec_tdm_tx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_tx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_1_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_2_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_3_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_tx_0_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_1_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_2_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_3_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia4", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia5", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia6", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia7", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA7, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia8", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia9", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA9, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia10", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA10, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia11", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA11, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia12", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA12, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia13", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA13, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia14", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA14, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia15", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA15, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia16", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA16, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul1_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX", MSM_BACKEND_DAI_PRI_I2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_4_TX", MSM_BACKEND_DAI_SLIMBUS_4_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUINARY_MI2S_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+		MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_routing_get_audio_mixer,
+		msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul2_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_QUINARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul3_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul4_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA4, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul5_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA5, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul6_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA6, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul8_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT2_MI2S_TX", MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_6_TX", MSM_BACKEND_DAI_SLIMBUS_6_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA8, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul17_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA17, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul18_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA18, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+
+static const struct snd_kcontrol_new mmul19_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_DL", MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+	SOC_SINGLE_EXT("VOC_REC_UL", MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_FRONTEND_DAI_MULTIMEDIA19, 1, 0, msm_routing_get_audio_mixer,
+	msm_routing_put_audio_mixer),
+};
+static const struct snd_kcontrol_new pri_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_I2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_6_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new usb_audio_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_USB_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new pri_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new int0_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new int4_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tert_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quat_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quin_mi2s_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new aux_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sec_aux_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tert_aux_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new quat_aux_pcm_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new hdmi_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_7_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_8_rx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("CSVoice", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice2", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voip", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoWLAN", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("DTMF", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_DTMF_RX, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QCHAT", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode1", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("VoiceMMode2", MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new stub_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_EXTPROC_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_EXTPROC_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_EXTPROC_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_1_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new slimbus_3_rx_mixer_controls[] = {
+	SOC_SINGLE_EXT("Voice Stub", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("Voice2 Stub", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("VoLTE Stub", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_Voice", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_Voice", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_CS_VOICE, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_Voice", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_Voice", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voice", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_Voice", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX_Voice", MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_Voice", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_Voice", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_Voice", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_CS_VOICE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice2_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_Voice2", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_Voice2", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_Voice2", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voice2",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICE2, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_Voice2", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voice2", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_Voice2", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_Voice2", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_Voice2", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_Voice2", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_Voice2", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_VOICE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_volte_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_VoLTE", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_VoLTE",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOLTE, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_VoLTE", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_VoLTE", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_VoLTE", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_VoLTE", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_VoLTE", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_VoLTE", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_VoLTE", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_VOLTE, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_vowlan_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_VoWLAN", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_VoWLAN",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOWLAN, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_VoWLAN", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_VoWLAN", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_VoWLAN", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_VoWLAN", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_VoWLAN", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_VoWLAN", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_VOWLAN, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voicemmode1_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_MMode1", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_MMode1", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_MMode1",
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INT_BT_SCO_TX_MMode1",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_MMode1",
+	MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_MMode1",
+	MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_MMode1",
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_MMode1",
+	MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_MMode1",
+	MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_MMode1",
+	MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_MMode1",
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1,
+	1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX_MMode1",
+	MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE1,
+	1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_MMode1",
+	MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_MMode1",
+	MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE1, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_MMode1", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_VOICEMMODE1, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voicemmode2_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_MMode2", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_MMode2", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_MMode2",
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INT_BT_SCO_TX_MMode2",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_MMode2",
+	MSM_BACKEND_DAI_AFE_PCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_MMode2",
+	MSM_BACKEND_DAI_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_MMode2",
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_MMode2",
+	MSM_BACKEND_DAI_TERT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_MMode2",
+	MSM_BACKEND_DAI_QUAT_AUXPCM_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_MMode2",
+	MSM_BACKEND_DAI_PRI_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_MMode2",
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2,
+	1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX_MMode2",
+	MSM_BACKEND_DAI_INT3_MI2S_TX, MSM_FRONTEND_DAI_VOICEMMODE2,
+	1, 0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_MMode2",
+	MSM_BACKEND_DAI_SLIMBUS_7_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_MMode2",
+	MSM_BACKEND_DAI_SLIMBUS_8_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_MMode2",
+	MSM_BACKEND_DAI_USB_TX, MSM_FRONTEND_DAI_VOICEMMODE2, 1,
+	0, msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voip_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_Voip", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_Voip", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_Voip", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_Voip", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_Voip", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_Voip", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_Voip", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_Voip", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX_Voip", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_Voip", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_Voip", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_VOIP, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice_stub_mixer_controls[] = {
+	SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_EXTPROC_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("STUB_1_TX_HL", MSM_BACKEND_DAI_EXTPROC_EC_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_voice2_stub_mixer_controls[] = {
+	SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_EXTPROC_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("STUB_1_TX_HL", MSM_BACKEND_DAI_EXTPROC_EC_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOICE2_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_volte_stub_mixer_controls[] = {
+	SOC_SINGLE_EXT("STUB_TX_HL", MSM_BACKEND_DAI_EXTPROC_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_1_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("STUB_1_TX_HL", MSM_BACKEND_DAI_EXTPROC_EC_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_3_TX", MSM_BACKEND_DAI_SLIMBUS_3_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_VOLTE_STUB, 1, 0, msm_routing_get_voice_stub_mixer,
+	msm_routing_put_voice_stub_mixer),
+};
+
+static const struct snd_kcontrol_new tx_qchat_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_TX_QCHAT", MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX_QCHAT", MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX_QCHAT",
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, MSM_FRONTEND_DAI_QCHAT, 1, 0,
+	msm_routing_get_voice_mixer, msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX_QCHAT", MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("QUAT_AUX_PCM_TX_QCHAT", MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("MI2S_TX_QCHAT", MSM_BACKEND_DAI_MI2S_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX_QCHAT", MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX_QCHAT", MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("INT3_MI2S_TX_QCHAT", MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX_QCHAT", MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX_QCHAT", MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+	SOC_SINGLE_EXT("USB_AUDIO_TX_QCHAT", MSM_BACKEND_DAI_USB_TX,
+	MSM_FRONTEND_DAI_QCHAT, 1, 0, msm_routing_get_voice_mixer,
+	msm_routing_put_voice_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_0_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_RX", MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new aux_pcm_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_auxpcm_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_auxpcm_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_auxpcm_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_1_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_TERT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_AUXPCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_QUAT_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_3_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_AFE_PCM_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_AUXPCM_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_RX", MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_RX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sbus_6_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_7_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_SLIMBUS_7_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_8_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_SLIMBUS_8_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new bt_sco_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new afe_pcm_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+
+static const struct snd_kcontrol_new hdmi_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_HDMI_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new display_port_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_i2s_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new mi2s_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("SLIM_1_TX", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("MI2S_TX", MSM_BACKEND_DAI_MI2S_RX,
+	MSM_BACKEND_DAI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new primary_mi2s_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUIN_MI2S_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_QUINARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_mi2s_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_0_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_1_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_2_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_tdm_rx_3_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_TERT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_0_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_1_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_2_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new quat_tdm_rx_3_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_INT_FM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_BT_SCO_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_INT_BT_SCO_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AFE_PCM_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_AFE_PCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_AUX_PCM_UL_TX", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_SEC_AUXPCM_TX, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_TERT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_0", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_0, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_1", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_1, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_2", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_2, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_TDM_TX_3", MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+		MSM_BACKEND_DAI_QUAT_TDM_TX_3, 1, 0,
+		msm_routing_get_port_mixer,
+		msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new tert_mi2s_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new sec_mi2s_rx_port_mixer_controls[] = {
+	SOC_SINGLE_EXT("PRI_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SEC_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("TERT_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("QUAT_MI2S_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("SLIM_0_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+	SOC_SINGLE_EXT("INTERNAL_FM_TX", MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_INT_FM_TX, 1, 0, msm_routing_get_port_mixer,
+	msm_routing_put_port_mixer),
+};
+
+static const struct snd_kcontrol_new slim_fm_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_switch_mixer,
+	msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim1_fm_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_switch_mixer,
+	msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim3_fm_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_switch_mixer,
+	msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim4_fm_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_switch_mixer,
+	msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new slim6_fm_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_switch_mixer,
+	msm_routing_put_switch_mixer);
+
+static const struct snd_kcontrol_new pcm_rx_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_fm_pcmrx_switch_mixer,
+	msm_routing_put_fm_pcmrx_switch_mixer);
+
+static const struct snd_kcontrol_new pri_mi2s_rx_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_pri_mi2s_switch_mixer,
+	msm_routing_put_pri_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new sec_mi2s_rx_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_sec_mi2s_switch_mixer,
+	msm_routing_put_sec_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new tert_mi2s_rx_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_tert_mi2s_switch_mixer,
+	msm_routing_put_tert_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new quat_mi2s_rx_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_quat_mi2s_switch_mixer,
+	msm_routing_put_quat_mi2s_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_pri_aux_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_hfp_switch_mixer,
+	msm_routing_put_hfp_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_aux_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_hfp_switch_mixer,
+	msm_routing_put_hfp_switch_mixer);
+
+static const struct snd_kcontrol_new hfp_int_switch_mixer_controls =
+	SOC_SINGLE_EXT("Switch", SND_SOC_NOPM,
+	0, 1, 0, msm_routing_get_hfp_switch_mixer,
+	msm_routing_put_hfp_switch_mixer);
+
+static const struct soc_enum lsm_mux_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(mad_audio_mux_text), mad_audio_mux_text);
+
+static const struct snd_kcontrol_new lsm1_mux =
+	SOC_DAPM_ENUM_EXT("LSM1 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+
+static const struct snd_kcontrol_new lsm2_mux =
+	SOC_DAPM_ENUM_EXT("LSM2 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+static const struct snd_kcontrol_new lsm3_mux =
+	SOC_DAPM_ENUM_EXT("LSM3 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+
+static const struct snd_kcontrol_new lsm4_mux =
+	SOC_DAPM_ENUM_EXT("LSM4 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+static const struct snd_kcontrol_new lsm5_mux =
+	SOC_DAPM_ENUM_EXT("LSM5 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+
+static const struct snd_kcontrol_new lsm6_mux =
+	SOC_DAPM_ENUM_EXT("LSM6 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+static const struct snd_kcontrol_new lsm7_mux =
+	SOC_DAPM_ENUM_EXT("LSM7 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+
+static const struct snd_kcontrol_new lsm8_mux =
+	SOC_DAPM_ENUM_EXT("LSM8 MUX", lsm_mux_enum,
+			  msm_routing_lsm_mux_get,
+			  msm_routing_lsm_mux_put);
+
+
+static const char * const lsm_func_text[] = {
+	"None", "AUDIO", "BEACON", "ULTRASOUND", "SWAUDIO",
+};
+static const struct soc_enum lsm_func_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(lsm_func_text), lsm_func_text);
+static const struct snd_kcontrol_new lsm_function[] = {
+	SOC_ENUM_EXT(SLIMBUS_0_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		     msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+	SOC_ENUM_EXT(SLIMBUS_1_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		     msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+	SOC_ENUM_EXT(SLIMBUS_2_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		     msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+	SOC_ENUM_EXT(SLIMBUS_3_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		     msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+	SOC_ENUM_EXT(SLIMBUS_4_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		     msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+	SOC_ENUM_EXT(SLIMBUS_5_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		     msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+	SOC_ENUM_EXT(TERT_MI2S_TX_TEXT" "LSM_FUNCTION_TEXT, lsm_func_enum,
+		    msm_routing_lsm_func_get, msm_routing_lsm_func_put),
+};
+
+static const char * const aanc_slim_0_rx_text[] = {
+	"ZERO", "SLIMBUS_0_TX", "SLIMBUS_1_TX", "SLIMBUS_2_TX", "SLIMBUS_3_TX",
+	"SLIMBUS_4_TX", "SLIMBUS_5_TX", "SLIMBUS_6_TX"
+};
+
+static const struct soc_enum aanc_slim_0_rx_enum =
+	SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(aanc_slim_0_rx_text),
+				aanc_slim_0_rx_text);
+
+static const struct snd_kcontrol_new aanc_slim_0_rx_mux[] = {
+	SOC_ENUM_EXT("AANC_SLIM_0_RX MUX", aanc_slim_0_rx_enum,
+		msm_routing_slim_0_rx_aanc_mux_get,
+		msm_routing_slim_0_rx_aanc_mux_put)
+};
+
+static int msm_routing_get_stereo_to_custom_stereo_control(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = is_custom_stereo_on;
+	return 0;
+}
+
+static int msm_routing_put_stereo_to_custom_stereo_control(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int flag = 0, i = 0, rc = 0, idx = 0;
+	int be_index = 0, port_id, topo_id;
+	unsigned int session_id = 0;
+	uint16_t op_FL_ip_FL_weight = 0;
+	uint16_t op_FL_ip_FR_weight = 0;
+	uint16_t op_FR_ip_FL_weight = 0;
+	uint16_t op_FR_ip_FR_weight = 0;
+
+	flag = ucontrol->value.integer.value[0];
+	pr_debug("%s E flag %d\n", __func__, flag);
+
+	if ((is_custom_stereo_on && flag) || (!is_custom_stereo_on && !flag)) {
+		pr_err("%s: is_custom_stereo_on %d, flag %d\n",
+			__func__, is_custom_stereo_on, flag);
+		return 0;
+	}
+	is_custom_stereo_on = flag ? true : false;
+	pr_debug("%s:is_custom_stereo_on %d\n", __func__, is_custom_stereo_on);
+	for (be_index = 0; be_index < MSM_BACKEND_DAI_MAX; be_index++) {
+		port_id = msm_bedais[be_index].port_id;
+		if (!msm_bedais[be_index].active)
+			continue;
+		if ((port_id != SLIMBUS_0_RX) &&
+		     (port_id != RT_PROXY_PORT_001_RX) &&
+			(port_id != AFE_PORT_ID_PRIMARY_MI2S_RX))
+			continue;
+
+		for_each_set_bit(i, &msm_bedais[be_index].fe_sessions,
+				MSM_FRONTEND_DAI_MM_SIZE) {
+			if (fe_dai_map[i][SESSION_TYPE_RX].perf_mode !=
+			    LEGACY_PCM_MODE)
+				goto skip_send_custom_stereo;
+			session_id =
+				fe_dai_map[i][SESSION_TYPE_RX].strm_id;
+			if (is_custom_stereo_on) {
+				op_FL_ip_FL_weight =
+					Q14_GAIN_ZERO_POINT_FIVE;
+				op_FL_ip_FR_weight =
+					Q14_GAIN_ZERO_POINT_FIVE;
+				op_FR_ip_FL_weight =
+					Q14_GAIN_ZERO_POINT_FIVE;
+				op_FR_ip_FR_weight =
+					Q14_GAIN_ZERO_POINT_FIVE;
+			} else {
+				op_FL_ip_FL_weight = Q14_GAIN_UNITY;
+				op_FL_ip_FR_weight = 0;
+				op_FR_ip_FL_weight = 0;
+				op_FR_ip_FR_weight = Q14_GAIN_UNITY;
+			}
+			for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+				unsigned long copp =
+					session_copp_map[i]
+					[SESSION_TYPE_RX][be_index];
+				if (!test_bit(idx, &copp))
+					goto skip_send_custom_stereo;
+				topo_id = adm_get_topology_for_port_copp_idx(
+					msm_bedais[be_index].port_id, idx);
+				if (topo_id < 0)
+					pr_debug("%s:Err:custom stereo topo %d",
+						 __func__, topo_id);
+					pr_debug("idx %d\n", idx);
+				if (topo_id == DS2_ADM_COPP_TOPOLOGY_ID)
+					rc = msm_ds2_dap_set_custom_stereo_onoff
+						(msm_bedais[be_index].port_id,
+						idx, is_custom_stereo_on);
+				else if (topo_id == DOLBY_ADM_COPP_TOPOLOGY_ID)
+					rc = dolby_dap_set_custom_stereo_onoff(
+						msm_bedais[be_index].port_id,
+						idx, is_custom_stereo_on);
+				else
+				rc = msm_qti_pp_send_stereo_to_custom_stereo_cmd
+						(msm_bedais[be_index].port_id,
+						idx, session_id,
+						op_FL_ip_FL_weight,
+						op_FL_ip_FR_weight,
+						op_FR_ip_FL_weight,
+						op_FR_ip_FR_weight);
+				if (rc < 0)
+skip_send_custom_stereo:
+					pr_err("%s: err setting custom stereo\n",
+						__func__);
+			}
+
+		}
+	}
+	return 0;
+}
+
+static const struct snd_kcontrol_new stereo_to_custom_stereo_controls[] = {
+	SOC_SINGLE_EXT("Set Custom Stereo OnOff", SND_SOC_NOPM, 0,
+	1, 0, msm_routing_get_stereo_to_custom_stereo_control,
+	msm_routing_put_stereo_to_custom_stereo_control),
+};
+
+static int msm_routing_get_app_type_cfg_control(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	return 0;
+}
+
+static int msm_routing_put_app_type_cfg_control(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	int i = 0, j;
+	int num_app_types = ucontrol->value.integer.value[i++];
+
+	pr_debug("%s\n", __func__);
+
+	memset(app_type_cfg, 0, MAX_APP_TYPES*
+				sizeof(struct msm_pcm_routing_app_type_data));
+	if (num_app_types > MAX_APP_TYPES) {
+		pr_err("%s: number of app types exceed the max supported\n",
+			__func__);
+		return -EINVAL;
+	}
+	for (j = 0; j < num_app_types; j++) {
+		app_type_cfg[j].app_type =
+				ucontrol->value.integer.value[i++];
+		app_type_cfg[j].sample_rate =
+				ucontrol->value.integer.value[i++];
+		app_type_cfg[j].bit_width =
+				ucontrol->value.integer.value[i++];
+	}
+
+	return 0;
+}
+
+static const struct snd_kcontrol_new app_type_cfg_controls[] = {
+	SOC_SINGLE_MULTI_EXT("App Type Config", SND_SOC_NOPM, 0,
+	0xFFFFFFFF, 0, 128, msm_routing_get_app_type_cfg_control,
+	msm_routing_put_app_type_cfg_control),
+};
+
+static int msm_routing_get_use_ds1_or_ds2_control(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = is_ds2_on;
+	return 0;
+}
+
+static int msm_routing_put_use_ds1_or_ds2_control(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	is_ds2_on = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static const struct snd_kcontrol_new use_ds1_or_ds2_controls[] = {
+	SOC_SINGLE_EXT("DS2 OnOff", SND_SOC_NOPM, 0,
+	1, 0, msm_routing_get_use_ds1_or_ds2_control,
+	msm_routing_put_use_ds1_or_ds2_control),
+};
+
+int msm_routing_get_rms_value_control(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol) {
+	int rc = 0;
+	int be_idx = 0;
+	char *param_value;
+	int *update_param_value;
+	uint32_t param_length = sizeof(uint32_t);
+	uint32_t param_payload_len = RMS_PAYLOAD_LEN * sizeof(uint32_t);
+
+	param_value = kzalloc(param_length, GFP_KERNEL);
+	if (!param_value)
+		return -ENOMEM;
+
+	for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++)
+		if (msm_bedais[be_idx].port_id == SLIMBUS_0_TX)
+			break;
+	if ((be_idx < MSM_BACKEND_DAI_MAX) && msm_bedais[be_idx].active) {
+		rc = adm_get_params(SLIMBUS_0_TX, 0,
+				RMS_MODULEID_APPI_PASSTHRU,
+				RMS_PARAM_FIRST_SAMPLE,
+				param_length + param_payload_len,
+				param_value);
+		if (rc) {
+			pr_err("%s: get parameters failed:%d\n", __func__, rc);
+			kfree(param_value);
+			return -EINVAL;
+		}
+		update_param_value = (int *)param_value;
+		ucontrol->value.integer.value[0] = update_param_value[0];
+
+		pr_debug("%s: FROM DSP value[0] 0x%x\n",
+			  __func__, update_param_value[0]);
+	}
+	kfree(param_value);
+	return 0;
+}
+
+static int msm_voc_session_id_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	voc_session_id = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: voc_session_id=%u\n", __func__, voc_session_id);
+
+	return 0;
+}
+
+static int msm_voc_session_id_get(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = voc_session_id;
+
+	return 0;
+}
+
+static struct snd_kcontrol_new msm_voc_session_controls[] = {
+	SOC_SINGLE_MULTI_EXT("Voc VSID", SND_SOC_NOPM, 0,
+			     0xFFFFFFFF, 0, 1, msm_voc_session_id_get,
+			     msm_voc_session_id_put),
+};
+
+static int msm_sound_focus_info(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = sizeof(struct sound_focus_param);
+
+	return 0;
+}
+
+static int msm_voice_sound_focus_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct sound_focus_param soundFocusData;
+
+	memcpy((void *)&soundFocusData, ucontrol->value.bytes.data,
+		sizeof(struct sound_focus_param));
+	ret = voc_set_sound_focus(soundFocusData);
+	if (ret) {
+		pr_err("%s: Error setting Sound Focus Params, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int msm_voice_sound_focus_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct sound_focus_param soundFocusData;
+
+	memset(&soundFocusData, 0, sizeof(struct sound_focus_param));
+
+	ret = voc_get_sound_focus(&soundFocusData);
+	if (ret) {
+		pr_err("%s: Error getting Sound Focus Params, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData,
+		sizeof(struct sound_focus_param));
+
+done:
+	return ret;
+}
+
+static int msm_source_tracking_info(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_info *uinfo)
+{
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = sizeof(struct source_tracking_param);
+
+	return 0;
+}
+
+static int msm_voice_source_tracking_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct source_tracking_param sourceTrackingData;
+
+	memset(&sourceTrackingData, 0, sizeof(struct source_tracking_param));
+
+	ret = voc_get_source_tracking(&sourceTrackingData);
+	if (ret) {
+		pr_err("%s: Error getting Source Tracking Params, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData,
+		sizeof(struct source_tracking_param));
+
+done:
+	return ret;
+}
+
+static int msm_audio_get_copp_idx_from_port_id(int port_id, int session_type,
+					 int *copp_idx)
+{
+	int i, idx, be_idx;
+	int ret = 0;
+	unsigned long copp;
+
+	pr_debug("%s: Enter, port_id=%d\n", __func__, port_id);
+
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port validation failed id 0x%x ret %d\n",
+			__func__, port_id, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+		if (msm_bedais[be_idx].port_id == port_id)
+			break;
+	}
+	if (be_idx >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s: Invalid be id %d\n", __func__, be_idx);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions,
+			 MSM_FRONTEND_DAI_MM_SIZE) {
+		for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+			copp = session_copp_map[i]
+				[session_type][be_idx];
+			if (test_bit(idx, &copp))
+				break;
+		}
+		if (idx >= MAX_COPPS_PER_PORT)
+			continue;
+		else
+			break;
+	}
+	if (i >= MSM_FRONTEND_DAI_MM_SIZE) {
+		pr_err("%s: Invalid FE, exiting\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	*copp_idx = idx;
+	pr_debug("%s: copp_idx=%d\n", __func__, *copp_idx);
+
+done:
+	return ret;
+}
+
+static int msm_audio_sound_focus_derive_port_id(struct snd_kcontrol *kcontrol,
+					    const char *prefix, int *port_id)
+{
+	int ret = 0;
+
+	pr_debug("%s: Enter, prefix:%s\n", __func__, prefix);
+
+	/*
+	 * Mixer control name will be like "Sound Focus Audio Tx SLIMBUS_0"
+	 * where the prefix is "Sound Focus Audio Tx ". Skip the prefix
+	 * and compare the string with the backend name to derive the port id.
+	 */
+	if (!strcmp(kcontrol->id.name + strlen(prefix),
+					"SLIMBUS_0")) {
+		*port_id = SLIMBUS_0_TX;
+	} else if (!strcmp(kcontrol->id.name + strlen(prefix),
+					"TERT_MI2S")) {
+		*port_id = AFE_PORT_ID_TERTIARY_MI2S_TX;
+	} else {
+		pr_err("%s: mixer ctl name=%s, could not derive valid port id\n",
+			__func__, kcontrol->id.name);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	pr_debug("%s: mixer ctl name=%s, derived port_id=%d\n",
+		  __func__, kcontrol->id.name, *port_id);
+
+done:
+	return ret;
+}
+
+static int msm_audio_sound_focus_put(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct sound_focus_param soundFocusData;
+	int port_id, copp_idx;
+
+	ret = msm_audio_sound_focus_derive_port_id(kcontrol,
+				"Sound Focus Audio Tx ", &port_id);
+	if (ret != 0) {
+		pr_err("%s: Error in deriving port id, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX,
+					    &copp_idx);
+	if (ret) {
+		pr_err("%s: Could not get copp idx for port_id=%d\n",
+			__func__, port_id);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	memcpy((void *)&soundFocusData, ucontrol->value.bytes.data,
+		sizeof(struct sound_focus_param));
+
+	ret = adm_set_sound_focus(port_id, copp_idx, soundFocusData);
+	if (ret) {
+		pr_err("%s: Error setting Sound Focus Params, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int msm_audio_sound_focus_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct sound_focus_param soundFocusData;
+	int port_id, copp_idx;
+
+	ret = msm_audio_sound_focus_derive_port_id(kcontrol,
+				"Sound Focus Audio Tx ", &port_id);
+	if (ret) {
+		pr_err("%s: Error in deriving port id, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX,
+					    &copp_idx);
+	if (ret) {
+		pr_err("%s: Could not get copp idx for port_id=%d\n",
+			__func__, port_id);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = adm_get_sound_focus(port_id, copp_idx, &soundFocusData);
+	if (ret) {
+		pr_err("%s: Error getting Sound Focus Params, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	memcpy(ucontrol->value.bytes.data, (void *)&soundFocusData,
+		sizeof(struct sound_focus_param));
+
+done:
+	return ret;
+}
+
+static int msm_audio_source_tracking_get(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	struct source_tracking_param sourceTrackingData;
+	int port_id, copp_idx;
+
+	ret = msm_audio_sound_focus_derive_port_id(kcontrol,
+				"Source Tracking Audio Tx ", &port_id);
+	if (ret) {
+		pr_err("%s: Error in deriving port id, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = msm_audio_get_copp_idx_from_port_id(port_id, SESSION_TYPE_TX,
+					    &copp_idx);
+	if (ret) {
+		pr_err("%s: Could not get copp idx for port_id=%d\n",
+			__func__, port_id);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = adm_get_source_tracking(port_id, copp_idx, &sourceTrackingData);
+	if (ret) {
+		pr_err("%s: Error getting Source Tracking Params, err=%d\n",
+			  __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	memcpy(ucontrol->value.bytes.data, (void *)&sourceTrackingData,
+		sizeof(struct source_tracking_param));
+
+done:
+	return ret;
+}
+
+static const struct snd_kcontrol_new msm_source_tracking_controls[] = {
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Sound Focus Voice Tx SLIMBUS_0",
+		.info	= msm_sound_focus_info,
+		.get	= msm_voice_sound_focus_get,
+		.put	= msm_voice_sound_focus_put,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Source Tracking Voice Tx SLIMBUS_0",
+		.info	= msm_source_tracking_info,
+		.get	= msm_voice_source_tracking_get,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Sound Focus Audio Tx SLIMBUS_0",
+		.info	= msm_sound_focus_info,
+		.get	= msm_audio_sound_focus_get,
+		.put	= msm_audio_sound_focus_put,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Source Tracking Audio Tx SLIMBUS_0",
+		.info	= msm_source_tracking_info,
+		.get	= msm_audio_source_tracking_get,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Sound Focus Voice Tx TERT_MI2S",
+		.info	= msm_sound_focus_info,
+		.get	= msm_voice_sound_focus_get,
+		.put	= msm_voice_sound_focus_put,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Source Tracking Voice Tx TERT_MI2S",
+		.info	= msm_source_tracking_info,
+		.get	= msm_voice_source_tracking_get,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Sound Focus Audio Tx TERT_MI2S",
+		.info	= msm_sound_focus_info,
+		.get	= msm_audio_sound_focus_get,
+		.put	= msm_audio_sound_focus_put,
+	},
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "Source Tracking Audio Tx TERT_MI2S",
+		.info	= msm_source_tracking_info,
+		.get	= msm_audio_source_tracking_get,
+	},
+};
+
+static int spkr_prot_put_vi_lch_port(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int item;
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+	pr_debug("%s item is %d\n", __func__,
+		   ucontrol->value.enumerated.item[0]);
+	mutex_lock(&routing_lock);
+	item = ucontrol->value.enumerated.item[0];
+	if (item < e->items) {
+		pr_debug("%s RX DAI ID %d TX DAI id %d\n",
+			__func__, e->shift_l, e->values[item]);
+		if (e->shift_l < MSM_BACKEND_DAI_MAX &&
+			e->values[item] < MSM_BACKEND_DAI_MAX)
+			/* Enable feedback TX path */
+			ret = afe_spk_prot_feed_back_cfg(
+			   msm_bedais[e->values[item]].port_id,
+			   msm_bedais[e->shift_l].port_id, 1, 0, 1);
+		else {
+			pr_debug("%s values are out of range item %d\n",
+			__func__, e->values[item]);
+			/* Disable feedback TX path */
+			if (e->values[item] == MSM_BACKEND_DAI_MAX)
+				ret = afe_spk_prot_feed_back_cfg(0, 0, 0, 0, 0);
+			else
+				ret = -EINVAL;
+		}
+	} else {
+		pr_err("%s item value is out of range item\n", __func__);
+		ret = -EINVAL;
+	}
+	mutex_unlock(&routing_lock);
+	return ret;
+}
+
+static int spkr_prot_put_vi_rch_port(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int item;
+	struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+	pr_debug("%s item is %d\n", __func__,
+			ucontrol->value.enumerated.item[0]);
+	mutex_lock(&routing_lock);
+	item = ucontrol->value.enumerated.item[0];
+	if (item < e->items) {
+		pr_debug("%s RX DAI ID %d TX DAI id %d\n",
+				__func__, e->shift_l, e->values[item]);
+		if (e->shift_l < MSM_BACKEND_DAI_MAX &&
+				e->values[item] < MSM_BACKEND_DAI_MAX)
+			/* Enable feedback TX path */
+			ret = afe_spk_prot_feed_back_cfg(
+					msm_bedais[e->values[item]].port_id,
+					msm_bedais[e->shift_l].port_id,
+					1, 1, 1);
+		else {
+			pr_debug("%s values are out of range item %d\n",
+					__func__, e->values[item]);
+			/* Disable feedback TX path */
+			if (e->values[item] == MSM_BACKEND_DAI_MAX)
+				ret = afe_spk_prot_feed_back_cfg(0,
+						0, 0, 0, 0);
+			else
+				ret = -EINVAL;
+		}
+	} else {
+		pr_err("%s item value is out of range item\n", __func__);
+		ret = -EINVAL;
+	}
+	mutex_unlock(&routing_lock);
+	return ret;
+}
+
+static int spkr_prot_get_vi_lch_port(struct snd_kcontrol *kcontrol,
+	struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s\n", __func__);
+	return 0;
+}
+
+static int spkr_prot_get_vi_rch_port(struct snd_kcontrol *kcontrol,
+		struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s\n", __func__);
+	ucontrol->value.enumerated.item[0] = 0;
+	return 0;
+}
+
+static const char * const slim0_rx_vi_fb_tx_lch_mux_text[] = {
+	"ZERO", "SLIM4_TX"
+};
+
+static const char * const slim0_rx_vi_fb_tx_rch_mux_text[] = {
+	"ZERO", "SLIM4_TX"
+};
+
+static const char * const mi2s_rx_vi_fb_tx_mux_text[] = {
+	"ZERO", "SENARY_TX"
+};
+
+static const int const slim0_rx_vi_fb_tx_lch_value[] = {
+	MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX
+};
+
+static const int const slim0_rx_vi_fb_tx_rch_value[] = {
+	MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SLIMBUS_4_TX
+};
+
+static const int const mi2s_rx_vi_fb_tx_value[] = {
+	MSM_BACKEND_DAI_MAX, MSM_BACKEND_DAI_SENARY_MI2S_TX
+};
+
+static const struct soc_enum slim0_rx_vi_fb_lch_mux_enum =
+	SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0,
+	ARRAY_SIZE(slim0_rx_vi_fb_tx_lch_mux_text),
+	slim0_rx_vi_fb_tx_lch_mux_text, slim0_rx_vi_fb_tx_lch_value);
+
+static const struct soc_enum slim0_rx_vi_fb_rch_mux_enum =
+	SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_SLIMBUS_0_RX, 0, 0,
+	ARRAY_SIZE(slim0_rx_vi_fb_tx_rch_mux_text),
+	slim0_rx_vi_fb_tx_rch_mux_text, slim0_rx_vi_fb_tx_rch_value);
+
+static const struct soc_enum mi2s_rx_vi_fb_mux_enum =
+	SOC_VALUE_ENUM_DOUBLE(0, MSM_BACKEND_DAI_PRI_MI2S_RX, 0, 0,
+	ARRAY_SIZE(mi2s_rx_vi_fb_tx_mux_text),
+	mi2s_rx_vi_fb_tx_mux_text, mi2s_rx_vi_fb_tx_value);
+
+static const struct snd_kcontrol_new slim0_rx_vi_fb_lch_mux =
+	SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_LCH_MUX",
+	slim0_rx_vi_fb_lch_mux_enum, spkr_prot_get_vi_lch_port,
+	spkr_prot_put_vi_lch_port);
+
+static const struct snd_kcontrol_new slim0_rx_vi_fb_rch_mux =
+	SOC_DAPM_ENUM_EXT("SLIM0_RX_VI_FB_RCH_MUX",
+	slim0_rx_vi_fb_rch_mux_enum, spkr_prot_get_vi_rch_port,
+	spkr_prot_put_vi_rch_port);
+
+static const struct snd_kcontrol_new mi2s_rx_vi_fb_mux =
+	SOC_DAPM_ENUM_EXT("PRI_MI2S_RX_VI_FB_MUX",
+	mi2s_rx_vi_fb_mux_enum, spkr_prot_get_vi_lch_port,
+	spkr_prot_put_vi_lch_port);
+
+static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
+	/* Frontend AIF */
+	/* Widget name equals to Front-End DAI name<Need confirmation>,
+	 * Stream name must contains substring of front-end dai name
+	 */
+	SND_SOC_DAPM_AIF_IN("MM_DL1", "MultiMedia1 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL2", "MultiMedia2 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL3", "MultiMedia3 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL4", "MultiMedia4 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL5", "MultiMedia5 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL6", "MultiMedia6 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL7", "MultiMedia7 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL8", "MultiMedia8 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL9", "MultiMedia9 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL10", "MultiMedia10 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL11", "MultiMedia11 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL12", "MultiMedia12 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL13", "MultiMedia13 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL14", "MultiMedia14 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL15", "MultiMedia15 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MM_DL16", "MultiMedia16 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOIP_DL", "VoIP Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL1", "MultiMedia1 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL2", "MultiMedia2 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL3", "MultiMedia3 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL4", "MultiMedia4 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL5", "MultiMedia5 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL6", "MultiMedia6 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL8", "MultiMedia8 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL9", "MultiMedia9 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL17", "MultiMedia17 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL18", "MultiMedia18 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MM_UL19", "MultiMedia19 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("CS-VOICE_DL1", "CS-VOICE Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("CS-VOICE_UL1", "CS-VOICE Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOICE2_DL", "Voice2 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOICE2_UL", "Voice2 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VoLTE_DL", "VoLTE Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VoLTE_UL", "VoLTE Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VoWLAN_DL", "VoWLAN Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VoWLAN_UL", "VoWLAN Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOIP_UL", "VoIP Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOICEMMODE1_DL",
+		"VoiceMMode1 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOICEMMODE1_UL",
+		"VoiceMMode1 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOICEMMODE2_DL",
+		"VoiceMMode2 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOICEMMODE2_UL",
+		"VoiceMMode2 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM0_DL_HL", "SLIMBUS0_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM0_UL_HL", "SLIMBUS0_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("CPE_LSM_UL_HL", "CPE LSM capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM1_DL_HL", "SLIMBUS1_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM1_UL_HL", "SLIMBUS1_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM3_DL_HL", "SLIMBUS3_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM3_UL_HL", "SLIMBUS3_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM4_DL_HL", "SLIMBUS4_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM4_UL_HL", "SLIMBUS4_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM6_DL_HL", "SLIMBUS6_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM6_UL_HL", "SLIMBUS6_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIM8_DL_HL", "SLIMBUS8_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIM8_UL_HL", "SLIMBUS8_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INTFM_DL_HL", "INT_FM_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INTFM_UL_HL", "INT_FM_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INTHFP_DL_HL", "INT_HFP_BT_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INTHFP_UL_HL", "INT_HFP_BT_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("HDMI_DL_HL", "HDMI_HOSTLESS Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_I2S_DL_HL", "SEC_I2S_RX_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INT0_MI2S_DL_HL",
+		"INT0 MI2S_RX Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INT4_MI2S_DL_HL",
+		"INT4 MI2S_RX Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_MI2S_DL_HL",
+		"Primary MI2S_RX Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_MI2S_DL_HL",
+		"Secondary MI2S_RX Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_MI2S_DL_HL",
+		"Tertiary MI2S_RX Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_MI2S_DL_HL",
+		"Quaternary MI2S_RX Hostless Playback",
+		0, 0, 0, 0),
+
+	SND_SOC_DAPM_AIF_IN("AUXPCM_DL_HL", "AUXPCM_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("AUXPCM_UL_HL", "AUXPCM_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MI2S_UL_HL", "MI2S_TX_HOSTLESS Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_MI2S_UL_HL",
+		"Tertiary MI2S_TX Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_MI2S_UL_HL",
+		"Secondary MI2S_TX Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_MI2S_UL_HL",
+		"Primary MI2S_TX Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MI2S_DL_HL", "MI2S_RX_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("DTMF_DL_HL", "DTMF_RX_HOSTLESS Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_UL_HL",
+		"Quaternary MI2S_TX Hostless Capture",
+		0, 0, 0, 0),
+
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_0_DL_HL",
+		"Primary TDM0 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_0_UL_HL",
+		"Primary TDM0 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_1_DL_HL",
+		"Primary TDM1 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_1_UL_HL",
+		"Primary TDM1 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_2_DL_HL",
+		"Primary TDM2 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_2_UL_HL",
+		"Primary TDM2 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_3_DL_HL",
+		"Primary TDM3 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_3_UL_HL",
+		"Primary TDM3 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_4_DL_HL",
+		"Primary TDM4 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_4_UL_HL",
+		"Primary TDM4 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_5_DL_HL",
+		"Primary TDM5 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_5_UL_HL",
+		"Primary TDM5 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_6_DL_HL",
+		"Primary TDM6 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_6_UL_HL",
+		"Primary TDM6 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_RX_7_DL_HL",
+		"Primary TDM7 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_TX_7_UL_HL",
+		"Primary TDM7 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_0_DL_HL",
+		"Secondary TDM0 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_0_UL_HL",
+		"Secondary TDM0 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_1_DL_HL",
+		"Secondary TDM1 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_1_UL_HL",
+		"Secondary TDM1 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_2_DL_HL",
+		"Secondary TDM2 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_2_UL_HL",
+		"Secondary TDM2 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_3_DL_HL",
+		"Secondary TDM3 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_3_UL_HL",
+		"Secondary TDM3 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_4_DL_HL",
+		"Secondary TDM4 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_4_UL_HL",
+		"Secondary TDM4 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_5_DL_HL",
+		"Secondary TDM5 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_5_UL_HL",
+		"Secondary TDM5 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_6_DL_HL",
+		"Secondary TDM6 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_6_UL_HL",
+		"Secondary TDM6 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_RX_7_DL_HL",
+		"Secondary TDM7 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_TX_7_UL_HL",
+		"Secondary TDM7 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_0_DL_HL",
+		"Tertiary TDM0 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_0_UL_HL",
+		"Tertiary TDM0 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_1_DL_HL",
+		"Tertiary TDM1 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_1_UL_HL",
+		"Tertiary TDM1 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_2_DL_HL",
+		"Tertiary TDM2 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_2_UL_HL",
+		"Tertiary TDM2 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_3_DL_HL",
+		"Tertiary TDM3 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_3_UL_HL",
+		"Tertiary TDM3 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_4_DL_HL",
+		"Tertiary TDM4 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_4_UL_HL",
+		"Tertiary TDM4 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_5_DL_HL",
+		"Tertiary TDM5 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_5_UL_HL",
+		"Tertiary TDM5 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_6_DL_HL",
+		"Tertiary TDM6 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_6_UL_HL",
+		"Tertiary TDM6 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_RX_7_DL_HL",
+		"Tertiary TDM7 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_TX_7_UL_HL",
+		"Tertiary TDM7 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_0_DL_HL",
+		"Quaternary TDM0 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_0_UL_HL",
+		"Quaternary TDM0 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_1_DL_HL",
+		"Quaternary TDM1 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_1_UL_HL",
+		"Quaternary TDM1 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_2_DL_HL",
+		"Quaternary TDM2 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_2_UL_HL",
+		"Quaternary TDM2 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_3_DL_HL",
+		"Quaternary TDM3 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_3_UL_HL",
+		"Quaternary TDM3 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_4_DL_HL",
+		"Quaternary TDM4 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_4_UL_HL",
+		"Quaternary TDM4 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_5_DL_HL",
+		"Quaternary TDM5 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_5_UL_HL",
+		"Quaternary TDM5 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_6_DL_HL",
+		"Quaternary TDM6 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_6_UL_HL",
+		"Quaternary TDM6 Hostless Capture",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_RX_7_DL_HL",
+		"Quaternary TDM7 Hostless Playback",
+		0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_TX_7_UL_HL",
+		"Quaternary TDM7 Hostless Capture",
+		0, 0, 0, 0),
+
+	/* LSM */
+	SND_SOC_DAPM_AIF_OUT("LSM1_UL_HL", "Listen 1 Audio Service Capture",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM2_UL_HL", "Listen 2 Audio Service Capture",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM3_UL_HL", "Listen 3 Audio Service Capture",
+				 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM4_UL_HL", "Listen 4 Audio Service Capture",
+						 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM5_UL_HL", "Listen 5 Audio Service Capture",
+				 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM6_UL_HL", "Listen 6 Audio Service Capture",
+				 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM7_UL_HL", "Listen 7 Audio Service Capture",
+				 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("LSM8_UL_HL", "Listen 8 Audio Service Capture",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QCHAT_DL", "QCHAT Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QCHAT_UL", "QCHAT Capture", 0, 0, 0, 0),
+	/* Backend AIF */
+	/* Stream name equals to backend dai link stream name */
+	SND_SOC_DAPM_AIF_OUT("PRI_I2S_RX", "Primary I2S Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_I2S_RX", "Secondary I2S Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SPDIF_RX", "SPDIF Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_0_RX", "Slimbus Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_2_RX", "Slimbus2 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_5_RX", "Slimbus5 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("HDMI", "HDMI Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("DISPLAY_PORT", "Display Port Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("MI2S_RX", "MI2S Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_MI2S_RX", "Quaternary MI2S Playback",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_MI2S_RX", "Tertiary MI2S Playback",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX", "Secondary MI2S Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_MI2S_RX_SD1",
+			"Secondary MI2S Playback SD1",
+			0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_MI2S_RX", "Primary MI2S Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INT0_MI2S_RX", "INT0 MI2S Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INT4_MI2S_RX", "INT4 MI2S Playback",
+			     0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUIN_MI2S_RX", "Quinary MI2S Playback",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_I2S_TX", "Primary I2S Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("MI2S_TX", "MI2S Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_MI2S_TX", "Quaternary MI2S Capture",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_MI2S_TX", "Primary MI2S Capture",
+			    0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_MI2S_TX", "Tertiary MI2S Capture",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INT2_MI2S_TX", "INT2 MI2S Capture",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INT3_MI2S_TX", "INT3 MI2S Capture",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_MI2S_TX", "Secondary MI2S Capture",
+			    0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_0_TX", "Slimbus Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_2_TX", "Slimbus2 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUIN_MI2S_TX", "Quinary MI2S Capture",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SENARY_MI2S_TX", "Senary MI2S Capture",
+						0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INT_BT_SCO_RX", "Internal BT-SCO Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INT_BT_SCO_TX", "Internal BT-SCO Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INT_BT_A2DP_RX", "Internal BT-A2DP Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("INT_FM_RX", "Internal FM Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INT_FM_TX", "Internal FM Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PCM_RX", "AFE Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PCM_TX", "AFE Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_0", "Primary TDM0 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_0", "Primary TDM0 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_1", "Primary TDM1 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_1", "Primary TDM1 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_2", "Primary TDM2 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_2", "Primary TDM2 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_3", "Primary TDM3 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_3", "Primary TDM3 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_4", "Primary TDM4 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_4", "Primary TDM4 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_5", "Primary TDM5 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_5", "Primary TDM5 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_6", "Primary TDM6 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_6", "Primary TDM6 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("PRI_TDM_RX_7", "Primary TDM7 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("PRI_TDM_TX_7", "Primary TDM7 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_0", "Secondary TDM0 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_0", "Secondary TDM0 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_1", "Secondary TDM1 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_1", "Secondary TDM1 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_2", "Secondary TDM2 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_2", "Secondary TDM2 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_3", "Secondary TDM3 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_3", "Secondary TDM3 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_4", "Secondary TDM4 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_4", "Secondary TDM4 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_5", "Secondary TDM5 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_5", "Secondary TDM5 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_6", "Secondary TDM6 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_6", "Secondary TDM6 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_TDM_RX_7", "Secondary TDM7 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_TDM_TX_7", "Secondary TDM7 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_0", "Tertiary TDM0 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_0", "Tertiary TDM0 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_1", "Tertiary TDM1 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_1", "Tertiary TDM1 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_2", "Tertiary TDM2 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_2", "Tertiary TDM2 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_3", "Tertiary TDM3 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_3", "Tertiary TDM3 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_4", "Tertiary TDM4 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_4", "Tertiary TDM4 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_5", "Tertiary TDM5 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_5", "Tertiary TDM5 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_6", "Tertiary TDM6 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_6", "Tertiary TDM6 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_TDM_RX_7", "Tertiary TDM7 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_TDM_TX_7", "Tertiary TDM7 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_0", "Quaternary TDM0 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_0", "Quaternary TDM0 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_1", "Quaternary TDM1 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_1", "Quaternary TDM1 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_2", "Quaternary TDM2 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_2", "Quaternary TDM2 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_3", "Quaternary TDM3 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_3", "Quaternary TDM3 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_4", "Quaternary TDM4 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_4", "Quaternary TDM4 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_5", "Quaternary TDM5 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_5", "Quaternary TDM5 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_6", "Quaternary TDM6 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_6", "Quaternary TDM6 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_TDM_RX_7", "Quaternary TDM7 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_TDM_TX_7", "Quaternary TDM7 Capture",
+				0, 0, 0, 0),
+	/* incall */
+	SND_SOC_DAPM_AIF_OUT("VOICE_PLAYBACK_TX", "Voice Farend Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOICE2_PLAYBACK_TX", "Voice2 Farend Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_4_RX", "Slimbus4 Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INCALL_RECORD_TX", "Voice Uplink Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("INCALL_RECORD_RX", "Voice Downlink Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_4_TX", "Slimbus4 Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SENARY_TX", "Senary_mi2s Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_5_TX", "Slimbus5 Capture", 0, 0, 0, 0),
+
+	SND_SOC_DAPM_AIF_OUT("AUX_PCM_RX", "AUX PCM Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("AUX_PCM_TX", "AUX PCM Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SEC_AUX_PCM_RX", "Sec AUX PCM Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SEC_AUX_PCM_TX", "Sec AUX PCM Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("TERT_AUX_PCM_RX", "Tert AUX PCM Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("TERT_AUX_PCM_TX", "Tert AUX PCM Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("QUAT_AUX_PCM_RX", "Quat AUX PCM Playback",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("QUAT_AUX_PCM_TX", "Quat AUX PCM Capture",
+				0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOICE_STUB_DL", "VOICE_STUB Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOICE_STUB_UL", "VOICE_STUB Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOICE2_STUB_DL", "VOICE2_STUB Playback",
+			    0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOICE2_STUB_UL", "VOICE2_STUB Capture",
+			    0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("VOLTE_STUB_DL", "VOLTE_STUB Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("VOLTE_STUB_UL", "VOLTE_STUB Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("STUB_RX", "Stub Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("STUB_TX", "Stub Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_1_RX", "Slimbus1 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_1_TX", "Slimbus1 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("STUB_1_TX", "Stub1 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_3_RX", "Slimbus3 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_3_TX", "Slimbus3 Capture", 0, 0, 0, 0),
+	/* In- call recording */
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_6_RX", "Slimbus6 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_6_TX", "Slimbus6 Capture", 0, 0, 0, 0),
+
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_7_RX", "Slimbus7 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_7_TX", "Slimbus7 Capture", 0, 0, 0, 0),
+
+	SND_SOC_DAPM_AIF_OUT("SLIMBUS_8_RX", "Slimbus8 Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("SLIMBUS_8_TX", "Slimbus8 Capture", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_OUT("USB_AUDIO_RX", "USB Audio Playback", 0, 0, 0, 0),
+	SND_SOC_DAPM_AIF_IN("USB_AUDIO_TX", "USB Audio Capture", 0, 0, 0, 0),
+
+	/* Switch Definitions */
+	SND_SOC_DAPM_SWITCH("SLIMBUS_DL_HL", SND_SOC_NOPM, 0, 0,
+				&slim_fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS1_DL_HL", SND_SOC_NOPM, 0, 0,
+				&slim1_fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS3_DL_HL", SND_SOC_NOPM, 0, 0,
+				&slim3_fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS4_DL_HL", SND_SOC_NOPM, 0, 0,
+				&slim4_fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SLIMBUS6_DL_HL", SND_SOC_NOPM, 0, 0,
+				&slim6_fm_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("PCM_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+				&pcm_rx_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("PRI_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+				&pri_mi2s_rx_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("SEC_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+				&sec_mi2s_rx_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("TERT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+				&tert_mi2s_rx_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("QUAT_MI2S_RX_DL_HL", SND_SOC_NOPM, 0, 0,
+				&quat_mi2s_rx_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("HFP_PRI_AUX_UL_HL", SND_SOC_NOPM, 0, 0,
+				&hfp_pri_aux_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("HFP_AUX_UL_HL", SND_SOC_NOPM, 0, 0,
+				&hfp_aux_switch_mixer_controls),
+	SND_SOC_DAPM_SWITCH("HFP_INT_UL_HL", SND_SOC_NOPM, 0, 0,
+				&hfp_int_switch_mixer_controls),
+
+	/* Mux Definitions */
+	SND_SOC_DAPM_MUX("LSM1 MUX", SND_SOC_NOPM, 0, 0, &lsm1_mux),
+	SND_SOC_DAPM_MUX("LSM2 MUX", SND_SOC_NOPM, 0, 0, &lsm2_mux),
+	SND_SOC_DAPM_MUX("LSM3 MUX", SND_SOC_NOPM, 0, 0, &lsm3_mux),
+	SND_SOC_DAPM_MUX("LSM4 MUX", SND_SOC_NOPM, 0, 0, &lsm4_mux),
+	SND_SOC_DAPM_MUX("LSM5 MUX", SND_SOC_NOPM, 0, 0, &lsm5_mux),
+	SND_SOC_DAPM_MUX("LSM6 MUX", SND_SOC_NOPM, 0, 0, &lsm6_mux),
+	SND_SOC_DAPM_MUX("LSM7 MUX", SND_SOC_NOPM, 0, 0, &lsm7_mux),
+	SND_SOC_DAPM_MUX("LSM8 MUX", SND_SOC_NOPM, 0, 0, &lsm8_mux),
+
+	/* Mixer definitions */
+	SND_SOC_DAPM_MIXER("PRI_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	pri_i2s_rx_mixer_controls, ARRAY_SIZE(pri_i2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	sec_i2s_rx_mixer_controls, ARRAY_SIZE(sec_i2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_rx_mixer_controls, ARRAY_SIZE(slimbus_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_2_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_2_rx_mixer_controls, ARRAY_SIZE(slimbus_2_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_5_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_5_rx_mixer_controls, ARRAY_SIZE(slimbus_5_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_7_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_7_rx_mixer_controls, ARRAY_SIZE(slimbus_7_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("HDMI Mixer", SND_SOC_NOPM, 0, 0,
+	hdmi_mixer_controls, ARRAY_SIZE(hdmi_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DISPLAY_PORT Mixer", SND_SOC_NOPM, 0, 0,
+	display_port_mixer_controls, ARRAY_SIZE(display_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SPDIF_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	spdif_rx_mixer_controls, ARRAY_SIZE(spdif_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	mi2s_rx_mixer_controls, ARRAY_SIZE(mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quaternary_mi2s_rx_mixer_controls,
+				ARRAY_SIZE(quaternary_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+				tertiary_mi2s_rx_mixer_controls,
+				ARRAY_SIZE(tertiary_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+			   secondary_mi2s_rx_mixer_controls,
+			   ARRAY_SIZE(secondary_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_MI2S_RX_SD1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+			   secondary_mi2s_rx2_mixer_controls,
+			   ARRAY_SIZE(secondary_mi2s_rx2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PRI_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+			   primary_mi2s_rx_mixer_controls,
+			   ARRAY_SIZE(primary_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INT0_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+			   int0_mi2s_rx_mixer_controls,
+			   ARRAY_SIZE(int0_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INT4_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+			   int4_mi2s_rx_mixer_controls,
+			   ARRAY_SIZE(int4_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUIN_MI2S_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quinary_mi2s_rx_mixer_controls,
+				ARRAY_SIZE(quinary_mi2s_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PRI_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				pri_tdm_rx_0_mixer_controls,
+				ARRAY_SIZE(pri_tdm_rx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PRI_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				pri_tdm_tx_0_mixer_controls,
+				ARRAY_SIZE(pri_tdm_tx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				sec_tdm_rx_0_mixer_controls,
+				ARRAY_SIZE(sec_tdm_rx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				sec_tdm_tx_0_mixer_controls,
+				ARRAY_SIZE(sec_tdm_tx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				tert_tdm_rx_0_mixer_controls,
+				ARRAY_SIZE(tert_tdm_rx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				tert_tdm_tx_0_mixer_controls,
+				ARRAY_SIZE(tert_tdm_tx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				tert_tdm_rx_1_mixer_controls,
+				ARRAY_SIZE(tert_tdm_rx_1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				tert_tdm_rx_2_mixer_controls,
+				ARRAY_SIZE(tert_tdm_rx_2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				tert_tdm_rx_3_mixer_controls,
+				ARRAY_SIZE(tert_tdm_rx_3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quat_tdm_rx_0_mixer_controls,
+				ARRAY_SIZE(quat_tdm_rx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_TX_0 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quat_tdm_tx_0_mixer_controls,
+				ARRAY_SIZE(quat_tdm_tx_0_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quat_tdm_rx_1_mixer_controls,
+				ARRAY_SIZE(quat_tdm_rx_1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quat_tdm_rx_2_mixer_controls,
+				ARRAY_SIZE(quat_tdm_rx_2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Audio Mixer", SND_SOC_NOPM, 0, 0,
+				quat_tdm_rx_3_mixer_controls,
+				ARRAY_SIZE(quat_tdm_rx_3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul2_mixer_controls, ARRAY_SIZE(mmul2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia3 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul3_mixer_controls, ARRAY_SIZE(mmul3_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia4 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul4_mixer_controls, ARRAY_SIZE(mmul4_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia5 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul5_mixer_controls, ARRAY_SIZE(mmul5_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia6 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul6_mixer_controls, ARRAY_SIZE(mmul6_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia17 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul17_mixer_controls, ARRAY_SIZE(mmul17_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia18 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul18_mixer_controls, ARRAY_SIZE(mmul18_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MultiMedia19 Mixer", SND_SOC_NOPM, 0, 0,
+	mmul19_mixer_controls, ARRAY_SIZE(mmul19_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	auxpcm_rx_mixer_controls, ARRAY_SIZE(auxpcm_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	sec_auxpcm_rx_mixer_controls, ARRAY_SIZE(sec_auxpcm_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	tert_auxpcm_rx_mixer_controls,
+	ARRAY_SIZE(tert_auxpcm_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	quat_auxpcm_rx_mixer_controls,
+	ARRAY_SIZE(quat_auxpcm_rx_mixer_controls)),
+	/* incall */
+	SND_SOC_DAPM_MIXER("Incall_Music Audio Mixer", SND_SOC_NOPM, 0, 0,
+	incall_music_delivery_mixer_controls,
+	ARRAY_SIZE(incall_music_delivery_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Incall_Music_2 Audio Mixer", SND_SOC_NOPM, 0, 0,
+	incall_music2_delivery_mixer_controls,
+	ARRAY_SIZE(incall_music2_delivery_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_4_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_4_rx_mixer_controls,
+	ARRAY_SIZE(slimbus_4_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_6_rx_mixer_controls,
+	ARRAY_SIZE(slimbus_6_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("USB_AUDIO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	usb_audio_rx_mixer_controls,
+	ARRAY_SIZE(usb_audio_rx_mixer_controls)),
+	/* Voice Mixer */
+	SND_SOC_DAPM_MIXER("PRI_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0, pri_rx_voice_mixer_controls,
+				ARRAY_SIZE(pri_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				sec_i2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(sec_i2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				sec_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(sec_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIM_0_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				slimbus_rx_voice_mixer_controls,
+				ARRAY_SIZE(slimbus_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				bt_sco_rx_voice_mixer_controls,
+				ARRAY_SIZE(bt_sco_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AFE_PCM_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				afe_pcm_rx_voice_mixer_controls,
+				ARRAY_SIZE(afe_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AUX_PCM_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				aux_pcm_rx_voice_mixer_controls,
+				ARRAY_SIZE(aux_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_AUX_PCM_RX_Voice Mixer",
+			      SND_SOC_NOPM, 0, 0,
+			      sec_aux_pcm_rx_voice_mixer_controls,
+			      ARRAY_SIZE(sec_aux_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_AUX_PCM_RX_Voice Mixer",
+			      SND_SOC_NOPM, 0, 0,
+			      tert_aux_pcm_rx_voice_mixer_controls,
+			      ARRAY_SIZE(tert_aux_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_AUX_PCM_RX_Voice Mixer",
+			      SND_SOC_NOPM, 0, 0,
+			      quat_aux_pcm_rx_voice_mixer_controls,
+			      ARRAY_SIZE(quat_aux_pcm_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("HDMI_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				hdmi_rx_voice_mixer_controls,
+				ARRAY_SIZE(hdmi_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PRI_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				pri_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(pri_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INT0_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				int0_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(int0_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INT4_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				int4_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(int4_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				tert_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(tert_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				quat_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(quat_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUIN_MI2S_RX_Voice Mixer",
+				SND_SOC_NOPM, 0, 0,
+				quin_mi2s_rx_voice_mixer_controls,
+				ARRAY_SIZE(quin_mi2s_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Voice_Tx Mixer",
+				SND_SOC_NOPM, 0, 0, tx_voice_mixer_controls,
+				ARRAY_SIZE(tx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Voice2_Tx Mixer",
+			   SND_SOC_NOPM, 0, 0, tx_voice2_mixer_controls,
+			   ARRAY_SIZE(tx_voice2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Voip_Tx Mixer",
+				SND_SOC_NOPM, 0, 0, tx_voip_mixer_controls,
+				ARRAY_SIZE(tx_voip_mixer_controls)),
+	SND_SOC_DAPM_MIXER("VoLTE_Tx Mixer",
+				SND_SOC_NOPM, 0, 0, tx_volte_mixer_controls,
+				ARRAY_SIZE(tx_volte_mixer_controls)),
+	SND_SOC_DAPM_MIXER("VoWLAN_Tx Mixer",
+				SND_SOC_NOPM, 0, 0, tx_vowlan_mixer_controls,
+				ARRAY_SIZE(tx_vowlan_mixer_controls)),
+	SND_SOC_DAPM_MIXER("VoiceMMode1_Tx Mixer",
+			   SND_SOC_NOPM, 0, 0, tx_voicemmode1_mixer_controls,
+			   ARRAY_SIZE(tx_voicemmode1_mixer_controls)),
+	SND_SOC_DAPM_MIXER("VoiceMMode2_Tx Mixer",
+			   SND_SOC_NOPM, 0, 0, tx_voicemmode2_mixer_controls,
+			   ARRAY_SIZE(tx_voicemmode2_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	int_bt_sco_rx_mixer_controls, ARRAY_SIZE(int_bt_sco_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INTERNAL_A2DP_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+			int_bt_a2dp_rx_mixer_controls,
+			ARRAY_SIZE(int_bt_a2dp_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INTERNAL_FM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	int_fm_rx_mixer_controls, ARRAY_SIZE(int_fm_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AFE_PCM_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+	afe_pcm_rx_mixer_controls, ARRAY_SIZE(afe_pcm_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Voice Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+	tx_voice_stub_mixer_controls, ARRAY_SIZE(tx_voice_stub_mixer_controls)),
+	SND_SOC_DAPM_MIXER("Voice2 Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+			   tx_voice2_stub_mixer_controls,
+			   ARRAY_SIZE(tx_voice2_stub_mixer_controls)),
+	SND_SOC_DAPM_MIXER("VoLTE Stub Tx Mixer", SND_SOC_NOPM, 0, 0,
+	tx_volte_stub_mixer_controls, ARRAY_SIZE(tx_volte_stub_mixer_controls)),
+	SND_SOC_DAPM_MIXER("STUB_RX Mixer", SND_SOC_NOPM, 0, 0,
+	stub_rx_mixer_controls, ARRAY_SIZE(stub_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_1_rx_mixer_controls, ARRAY_SIZE(slimbus_1_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_3_RX_Voice Mixer", SND_SOC_NOPM, 0, 0,
+	slimbus_3_rx_mixer_controls, ARRAY_SIZE(slimbus_3_rx_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIM_6_RX_Voice Mixer",
+			SND_SOC_NOPM, 0, 0,
+			slimbus_6_rx_voice_mixer_controls,
+			ARRAY_SIZE(slimbus_6_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIM_7_RX_Voice Mixer", SND_SOC_NOPM, 0, 0,
+			   slimbus_7_rx_voice_mixer_controls,
+			   ARRAY_SIZE(slimbus_7_rx_voice_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIM_8_RX_Voice Mixer", SND_SOC_NOPM, 0, 0,
+			   slimbus_8_rx_voice_mixer_controls,
+			   ARRAY_SIZE(slimbus_8_rx_voice_mixer_controls)),
+	/* port mixer */
+	SND_SOC_DAPM_MIXER("SLIMBUS_0_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, sbus_0_rx_port_mixer_controls,
+	ARRAY_SIZE(sbus_0_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AUX_PCM_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, aux_pcm_rx_port_mixer_controls,
+	ARRAY_SIZE(aux_pcm_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_AUXPCM_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, sec_auxpcm_rx_port_mixer_controls,
+	ARRAY_SIZE(sec_auxpcm_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_AUXPCM_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, tert_auxpcm_rx_port_mixer_controls,
+	ARRAY_SIZE(tert_auxpcm_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_AUXPCM_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, quat_auxpcm_rx_port_mixer_controls,
+	ARRAY_SIZE(quat_auxpcm_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_1_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	sbus_1_rx_port_mixer_controls,
+	ARRAY_SIZE(sbus_1_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("INTERNAL_BT_SCO_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	bt_sco_rx_port_mixer_controls,
+	ARRAY_SIZE(bt_sco_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("AFE_PCM_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, afe_pcm_rx_port_mixer_controls,
+	ARRAY_SIZE(afe_pcm_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("HDMI_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, hdmi_rx_port_mixer_controls,
+	ARRAY_SIZE(hdmi_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("DISPLAY_PORT_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, display_port_rx_port_mixer_controls,
+	ARRAY_SIZE(display_port_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_I2S_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, sec_i2s_rx_port_mixer_controls,
+	ARRAY_SIZE(sec_i2s_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_3_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, sbus_3_rx_port_mixer_controls,
+	ARRAY_SIZE(sbus_3_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SLIMBUS_6_RX Port Mixer",
+	SND_SOC_NOPM, 0, 0, sbus_6_rx_port_mixer_controls,
+	ARRAY_SIZE(sbus_6_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	mi2s_rx_port_mixer_controls, ARRAY_SIZE(mi2s_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("PRI_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	primary_mi2s_rx_port_mixer_controls,
+	ARRAY_SIZE(primary_mi2s_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("SEC_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	sec_mi2s_rx_port_mixer_controls,
+	ARRAY_SIZE(sec_mi2s_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	tert_mi2s_rx_port_mixer_controls,
+	ARRAY_SIZE(tert_mi2s_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_MI2S_RX Port Mixer", SND_SOC_NOPM, 0, 0,
+	quat_mi2s_rx_port_mixer_controls,
+	ARRAY_SIZE(quat_mi2s_rx_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0,
+	tert_tdm_rx_0_port_mixer_controls,
+	ARRAY_SIZE(tert_tdm_rx_0_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0,
+	tert_tdm_rx_1_port_mixer_controls,
+	ARRAY_SIZE(tert_tdm_rx_1_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0,
+	tert_tdm_rx_2_port_mixer_controls,
+	ARRAY_SIZE(tert_tdm_rx_2_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("TERT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0,
+	tert_tdm_rx_3_port_mixer_controls,
+	ARRAY_SIZE(tert_tdm_rx_3_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_0 Port Mixer", SND_SOC_NOPM, 0, 0,
+	quat_tdm_rx_0_port_mixer_controls,
+	ARRAY_SIZE(quat_tdm_rx_0_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_1 Port Mixer", SND_SOC_NOPM, 0, 0,
+	quat_tdm_rx_1_port_mixer_controls,
+	ARRAY_SIZE(quat_tdm_rx_1_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_2 Port Mixer", SND_SOC_NOPM, 0, 0,
+	quat_tdm_rx_2_port_mixer_controls,
+	ARRAY_SIZE(quat_tdm_rx_2_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QUAT_TDM_RX_3 Port Mixer", SND_SOC_NOPM, 0, 0,
+	quat_tdm_rx_3_port_mixer_controls,
+	ARRAY_SIZE(quat_tdm_rx_3_port_mixer_controls)),
+	SND_SOC_DAPM_MIXER("QCHAT_Tx Mixer",
+	SND_SOC_NOPM, 0, 0, tx_qchat_mixer_controls,
+	ARRAY_SIZE(tx_qchat_mixer_controls)),
+	SND_SOC_DAPM_MIXER("USB_AUDIO_RX_Voice Mixer",
+	SND_SOC_NOPM, 0, 0, usb_audio_rx_voice_mixer_controls,
+	ARRAY_SIZE(usb_audio_rx_voice_mixer_controls)),
+	/* Virtual Pins to force backends ON atm */
+	SND_SOC_DAPM_OUTPUT("BE_OUT"),
+	SND_SOC_DAPM_INPUT("BE_IN"),
+
+	SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_LCH_MUX", SND_SOC_NOPM, 0, 0,
+				&slim0_rx_vi_fb_lch_mux),
+	SND_SOC_DAPM_MUX("SLIM0_RX_VI_FB_RCH_MUX", SND_SOC_NOPM, 0, 0,
+				&slim0_rx_vi_fb_rch_mux),
+	SND_SOC_DAPM_MUX("PRI_MI2S_RX_VI_FB_MUX", SND_SOC_NOPM, 0, 0,
+				&mi2s_rx_vi_fb_mux),
+
+	SND_SOC_DAPM_MUX("VOC_EXT_EC MUX", SND_SOC_NOPM, 0, 0,
+			 &voc_ext_ec_mux),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL1 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul1),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL2 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul2),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL3 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul3),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL4 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul4),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL5 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul5),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL6 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul6),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL8 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul8),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL9 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul9),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL17 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul17),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL18 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul18),
+	SND_SOC_DAPM_MUX("AUDIO_REF_EC_UL19 MUX", SND_SOC_NOPM, 0, 0,
+		&ext_ec_ref_mux_ul19),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+	{"PRI_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"PRI_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"PRI_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"PRI_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"PRI_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"PRI_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"PRI_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"PRI_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"PRI_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"PRI_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"PRI_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"PRI_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"PRI_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"PRI_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"PRI_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"PRI_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"PRI_I2S_RX", NULL, "PRI_RX Audio Mixer"},
+
+	{"SEC_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SEC_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SEC_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SEC_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SEC_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SEC_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SEC_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SEC_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SEC_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SEC_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SEC_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SEC_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SEC_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SEC_I2S_RX", NULL, "SEC_RX Audio Mixer"},
+
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SLIMBUS_0_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Audio Mixer"},
+
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SLIMBUS_2_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SLIMBUS_2_RX", NULL, "SLIMBUS_2_RX Audio Mixer"},
+
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SLIMBUS_5_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SLIMBUS_5_RX", NULL, "SLIMBUS_5_RX Audio Mixer"},
+
+	{"HDMI Mixer", "MultiMedia1", "MM_DL1"},
+	{"HDMI Mixer", "MultiMedia2", "MM_DL2"},
+	{"HDMI Mixer", "MultiMedia3", "MM_DL3"},
+	{"HDMI Mixer", "MultiMedia4", "MM_DL4"},
+	{"HDMI Mixer", "MultiMedia5", "MM_DL5"},
+	{"HDMI Mixer", "MultiMedia6", "MM_DL6"},
+	{"HDMI Mixer", "MultiMedia7", "MM_DL7"},
+	{"HDMI Mixer", "MultiMedia8", "MM_DL8"},
+	{"HDMI Mixer", "MultiMedia9", "MM_DL9"},
+	{"HDMI Mixer", "MultiMedia10", "MM_DL10"},
+	{"HDMI Mixer", "MultiMedia11", "MM_DL11"},
+	{"HDMI Mixer", "MultiMedia12", "MM_DL12"},
+	{"HDMI Mixer", "MultiMedia13", "MM_DL13"},
+	{"HDMI Mixer", "MultiMedia14", "MM_DL14"},
+	{"HDMI Mixer", "MultiMedia15", "MM_DL15"},
+	{"HDMI Mixer", "MultiMedia16", "MM_DL16"},
+	{"HDMI", NULL, "HDMI Mixer"},
+
+	{"DISPLAY_PORT Mixer", "MultiMedia1", "MM_DL1"},
+	{"DISPLAY_PORT Mixer", "MultiMedia2", "MM_DL2"},
+	{"DISPLAY_PORT Mixer", "MultiMedia3", "MM_DL3"},
+	{"DISPLAY_PORT Mixer", "MultiMedia4", "MM_DL4"},
+	{"DISPLAY_PORT Mixer", "MultiMedia5", "MM_DL5"},
+	{"DISPLAY_PORT Mixer", "MultiMedia6", "MM_DL6"},
+	{"DISPLAY_PORT Mixer", "MultiMedia7", "MM_DL7"},
+	{"DISPLAY_PORT Mixer", "MultiMedia8", "MM_DL8"},
+	{"DISPLAY_PORT Mixer", "MultiMedia9", "MM_DL9"},
+	{"DISPLAY_PORT Mixer", "MultiMedia10", "MM_DL10"},
+	{"DISPLAY_PORT Mixer", "MultiMedia11", "MM_DL11"},
+	{"DISPLAY_PORT Mixer", "MultiMedia12", "MM_DL12"},
+	{"DISPLAY_PORT Mixer", "MultiMedia13", "MM_DL13"},
+	{"DISPLAY_PORT Mixer", "MultiMedia14", "MM_DL14"},
+	{"DISPLAY_PORT Mixer", "MultiMedia15", "MM_DL15"},
+	{"DISPLAY_PORT Mixer", "MultiMedia16", "MM_DL16"},
+	{"DISPLAY_PORT", NULL, "DISPLAY_PORT Mixer"},
+
+	{"SPDIF_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SPDIF_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SPDIF_RX", NULL, "SPDIF_RX Audio Mixer"},
+
+	/* incall */
+	{"Incall_Music Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"Incall_Music Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"Incall_Music Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"Incall_Music Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"VOICE_PLAYBACK_TX", NULL, "Incall_Music Audio Mixer"},
+	{"Incall_Music_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"Incall_Music_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"Incall_Music_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"Incall_Music_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"VOICE2_PLAYBACK_TX", NULL, "Incall_Music_2 Audio Mixer"},
+	{"SLIMBUS_4_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SLIMBUS_4_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SLIMBUS_4_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SLIMBUS_4_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SLIMBUS_4_RX", NULL, "SLIMBUS_4_RX Audio Mixer"},
+
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SLIMBUS_6_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Audio Mixer"},
+
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SLIMBUS_7_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SLIMBUS_7_RX", NULL, "SLIMBUS_7_RX Audio Mixer"},
+
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"USB_AUDIO_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"USB_AUDIO_RX", NULL, "USB_AUDIO_RX Audio Mixer"},
+
+	{"MultiMedia1 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+	{"MultiMedia4 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+	{"MultiMedia8 Mixer", "VOC_REC_UL", "INCALL_RECORD_TX"},
+	{"MultiMedia1 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+	{"MultiMedia4 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+	{"MultiMedia8 Mixer", "VOC_REC_DL", "INCALL_RECORD_RX"},
+	{"MultiMedia1 Mixer", "SLIM_4_TX", "SLIMBUS_4_TX"},
+	{"MultiMedia1 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+	{"MultiMedia1 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"MultiMedia1 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"MultiMedia8 Mixer", "SLIM_6_TX", "SLIMBUS_6_TX"},
+	{"MultiMedia8 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"MultiMedia4 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia17 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia18 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia19 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia8 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia2 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia4 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia17 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia18 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia19 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia8 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia3 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia5 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia5 Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"MultiMedia5 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"MI2S_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"MI2S_RX", NULL, "MI2S_RX Audio Mixer"},
+
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Audio Mixer"},
+
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Audio Mixer"},
+
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SEC_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Audio Mixer"},
+
+	{"SEC_MI2S_RX_SD1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SEC_MI2S_RX_SD1", NULL, "SEC_MI2S_RX_SD1 Audio Mixer"},
+
+	{"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"PRI_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Audio Mixer"},
+
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"INT0_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"INT0_MI2S_RX", NULL, "INT0_MI2S_RX Audio Mixer"},
+
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"INT4_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"INT4_MI2S_RX", NULL, "INT4_MI2S_RX Audio Mixer"},
+
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUIN_MI2S_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX Audio Mixer"},
+
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"PRI_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0 Audio Mixer"},
+
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"PRI_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"PRI_TDM_TX_0", NULL, "PRI_TDM_TX_0 Audio Mixer"},
+
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SEC_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0 Audio Mixer"},
+
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SEC_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SEC_TDM_TX_0", NULL, "SEC_TDM_TX_0 Audio Mixer"},
+
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Audio Mixer"},
+
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_TDM_TX_0", NULL, "TERT_TDM_TX_0 Audio Mixer"},
+
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Audio Mixer"},
+
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Audio Mixer"},
+
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Audio Mixer"},
+
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_TDM_RX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Audio Mixer"},
+
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_TDM_TX_0 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_TDM_TX_0", NULL, "QUAT_TDM_TX_0 Audio Mixer"},
+
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_TDM_RX_1 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Audio Mixer"},
+
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_TDM_RX_2 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Audio Mixer"},
+
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_TDM_RX_3 Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Audio Mixer"},
+
+	{"MultiMedia1 Mixer", "PRI_TX", "PRI_I2S_TX"},
+	{"MultiMedia1 Mixer", "MI2S_TX", "MI2S_TX"},
+	{"MultiMedia2 Mixer", "MI2S_TX", "MI2S_TX"},
+	{"MultiMedia3 Mixer", "MI2S_TX", "MI2S_TX"},
+	{"MultiMedia5 Mixer", "MI2S_TX", "MI2S_TX"},
+	{"MultiMedia1 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"MultiMedia2 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"MultiMedia6 Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"MultiMedia1 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
+	{"MultiMedia2 Mixer", "QUIN_MI2S_TX", "QUIN_MI2S_TX"},
+	{"MultiMedia1 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"MultiMedia2 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"MultiMedia1 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+	{"MultiMedia2 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+	{"MultiMedia1 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+	{"MultiMedia2 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+	{"MultiMedia1 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia1 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"MultiMedia3 Mixer", "AUX_PCM_TX", "AUX_PCM_TX"},
+	{"MultiMedia5 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"MultiMedia1 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"MultiMedia3 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+	{"MultiMedia5 Mixer", "SEC_AUX_PCM_TX", "SEC_AUX_PCM_TX"},
+	{"MultiMedia1 Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+	{"MultiMedia3 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
+	{"MultiMedia5 Mixer", "TERT_AUX_PCM_TX", "TERT_AUX_PCM_TX"},
+	{"MultiMedia1 Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+	{"MultiMedia3 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+	{"MultiMedia5 Mixer", "QUAT_AUX_PCM_TX", "QUAT_AUX_PCM_TX"},
+	{"MultiMedia2 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia2 Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"MultiMedia1 Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"MultiMedia1 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia6 Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"MultiMedia6 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"MultiMedia3 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"MultiMedia5 Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"MultiMedia6 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+	{"MultiMedia3 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+	{"MultiMedia5 Mixer", "INT2_MI2S_TX", "INT2_MI2S_TX"},
+	{"MultiMedia6 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+	{"MultiMedia3 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+	{"MultiMedia5 Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+	{"MultiMedia6 Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"MultiMedia6 Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"MultiMedia6 Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+
+	{"MultiMedia1 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia1 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia1 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia1 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia1 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia1 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia1 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia1 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia2 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia2 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia2 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia2 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia2 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia2 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia2 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia2 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia3 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia3 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia3 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia3 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia3 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia3 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia3 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia3 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia4 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia4 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia4 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia4 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia4 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia4 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia4 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia4 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia5 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia5 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia5 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia5 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia5 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia5 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia5 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia5 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia6 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia6 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia6 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia6 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia6 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia6 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia6 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia6 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia8 Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"MultiMedia8 Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"MultiMedia8 Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"MultiMedia8 Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"MultiMedia8 Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"MultiMedia8 Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"MultiMedia8 Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"MultiMedia8 Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+
+	{"MultiMedia1 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+	{"MultiMedia2 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+	{"MultiMedia4 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+	{"MultiMedia5 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+	{"MultiMedia6 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+	{"MultiMedia8 Mixer", "USB_AUDIO_TX", "USB_AUDIO_TX"},
+
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"INTERNAL_BT_SCO_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
+	{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Audio Mixer"},
+
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"INTERNAL_A2DP_RX Audio Mixer", "MultiMedia6", "MM_UL6"},
+	{"INT_BT_A2DP_RX", NULL, "INTERNAL_A2DP_RX Audio Mixer"},
+
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"INTERNAL_FM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"INT_FM_RX", NULL, "INTERNAL_FM_RX Audio Mixer"},
+
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"AFE_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"PCM_RX", NULL, "AFE_PCM_RX Audio Mixer"},
+
+	{"MultiMedia1 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia3 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia4 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia17 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia18 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia19 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia5 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia8 Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"MultiMedia1 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia4 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia17 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia18 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia19 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia5 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia6 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MultiMedia8 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+
+	{"MultiMedia1 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia3 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia4 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia17 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia18 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia19 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia5 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MultiMedia8 Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"MM_UL1", NULL, "MultiMedia1 Mixer"},
+	{"MultiMedia2 Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"MM_UL2", NULL, "MultiMedia2 Mixer"},
+	{"MM_UL3", NULL, "MultiMedia3 Mixer"},
+	{"MM_UL4", NULL, "MultiMedia4 Mixer"},
+	{"MM_UL5", NULL, "MultiMedia5 Mixer"},
+	{"MM_UL6", NULL, "MultiMedia6 Mixer"},
+	{"MM_UL8", NULL, "MultiMedia8 Mixer"},
+	{"MM_UL17", NULL, "MultiMedia17 Mixer"},
+	{"MM_UL18", NULL, "MultiMedia18 Mixer"},
+	{"MM_UL19", NULL, "MultiMedia19 Mixer"},
+
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"AUX_PCM_RX", NULL, "AUX_PCM_RX Audio Mixer"},
+
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"SEC_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX Audio Mixer"},
+
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"TERT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX Audio Mixer"},
+
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia1", "MM_DL1"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia2", "MM_DL2"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia3", "MM_DL3"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia4", "MM_DL4"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia5", "MM_DL5"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia6", "MM_DL6"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia7", "MM_DL7"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia8", "MM_DL8"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia9", "MM_DL9"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia10", "MM_DL10"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia11", "MM_DL11"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia12", "MM_DL12"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia13", "MM_DL13"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia14", "MM_DL14"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia15", "MM_DL15"},
+	{"QUAT_AUX_PCM_RX Audio Mixer", "MultiMedia16", "MM_DL16"},
+	{"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX Audio Mixer"},
+
+	{"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
+
+	{"PRI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"PRI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"PRI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"PRI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"PRI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"PRI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"PRI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"PRI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"PRI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"PRI_I2S_RX", NULL, "PRI_RX_Voice Mixer"},
+
+	{"SEC_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SEC_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SEC_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SEC_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SEC_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SEC_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SEC_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SEC_I2S_RX", NULL, "SEC_RX_Voice Mixer"},
+
+	{"SEC_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SEC_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SEC_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SEC_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SEC_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SEC_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SEC_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SEC_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"SEC_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_Voice Mixer"},
+
+	{"SLIM_0_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SLIM_0_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SLIM_0_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SLIM_0_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SLIM_0_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SLIM_0_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SLIM_0_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SLIM_0_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"SLIM_0_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIM_0_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SLIM_0_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"SLIM_0_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"SLIMBUS_0_RX", NULL, "SLIM_0_RX_Voice Mixer"},
+
+	{"SLIM_6_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SLIM_6_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SLIM_6_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SLIM_6_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SLIM_6_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SLIM_6_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SLIM_6_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SLIM_6_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"SLIM_6_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIM_6_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SLIM_6_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"SLIM_6_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"SLIMBUS_6_RX", NULL, "SLIM_6_RX_Voice Mixer"},
+
+	{"USB_AUDIO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"USB_AUDIO_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"USB_AUDIO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"USB_AUDIO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"USB_AUDIO_RX", NULL, "USB_AUDIO_RX_Voice Mixer"},
+
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"INTERNAL_BT_SCO_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX_Voice Mixer"},
+
+	{"AFE_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"AFE_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"AFE_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"PCM_RX", NULL, "AFE_PCM_RX_Voice Mixer"},
+
+	{"AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"AUX_PCM_RX", NULL, "AUX_PCM_RX_Voice Mixer"},
+
+	{"SEC_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"SEC_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"SEC_AUX_PCM_RX", NULL, "SEC_AUX_PCM_RX_Voice Mixer"},
+
+	{"TERT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"TERT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"TERT_AUX_PCM_RX", NULL, "TERT_AUX_PCM_RX_Voice Mixer"},
+
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"QUAT_AUX_PCM_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"QUAT_AUX_PCM_RX", NULL, "QUAT_AUX_PCM_RX_Voice Mixer"},
+
+	{"HDMI_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"HDMI_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"HDMI_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"HDMI_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"HDMI_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"HDMI_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"HDMI_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"HDMI_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"HDMI_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"HDMI", NULL, "HDMI_RX_Voice Mixer"},
+	{"HDMI", NULL, "HDMI_DL_HL"},
+
+	{"MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"MI2S_RX", NULL, "MI2S_RX_Voice Mixer"},
+
+	{"PRI_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"PRI_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"PRI_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"PRI_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_Voice Mixer"},
+
+	{"INT0_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"INT0_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"INT0_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"INT0_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"INT0_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_Voice Mixer"},
+
+	{"INT4_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"INT4_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"INT4_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"INT4_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"INT4_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_Voice Mixer"},
+
+	{"TERT_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"TERT_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"TERT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"TERT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_Voice Mixer"},
+
+	{"QUAT_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"QUAT_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"QUAT_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_Voice Mixer"},
+
+	{"QUIN_MI2S_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"QUIN_MI2S_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"QUIN_MI2S_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"QUIN_MI2S_RX", NULL, "QUIN_MI2S_RX_Voice Mixer"},
+
+	{"VOC_EXT_EC MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"VOC_EXT_EC MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"VOC_EXT_EC MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"VOC_EXT_EC MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"VOC_EXT_EC MUX", "SLIM_1_TX",    "SLIMBUS_1_TX"},
+	{"CS-VOICE_UL1", NULL, "VOC_EXT_EC MUX"},
+	{"VOIP_UL", NULL, "VOC_EXT_EC MUX"},
+	{"VoLTE_UL", NULL, "VOC_EXT_EC MUX"},
+	{"VOICE2_UL", NULL, "VOC_EXT_EC MUX"},
+	{"VOICEMMODE1_UL", NULL, "VOC_EXT_EC MUX"},
+	{"VOICEMMODE2_UL", NULL, "VOC_EXT_EC MUX"},
+
+	{"AUDIO_REF_EC_UL1 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL1 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL1 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL1 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL1 MUX", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_0", "QUAT_TDM_RX_0"},
+	{"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_1", "QUAT_TDM_RX_1"},
+	{"AUDIO_REF_EC_UL1 MUX", "QUAT_TDM_RX_2", "QUAT_TDM_RX_2"},
+	{"AUDIO_REF_EC_UL1 MUX", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+
+	{"AUDIO_REF_EC_UL2 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL2 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL2 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL2 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL3 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL3 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL3 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL3 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL4 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL4 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL4 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL4 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL5 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL5 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL5 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL5 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL6 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL6 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL6 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL6 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL8 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL8 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL8 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL8 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL9 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL9 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL9 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL9 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL17 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL17 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL17 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL17 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL18 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL18 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL18 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL18 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"AUDIO_REF_EC_UL19 MUX", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"AUDIO_REF_EC_UL19 MUX", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"AUDIO_REF_EC_UL19 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"AUDIO_REF_EC_UL19 MUX", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+
+	{"MM_UL1", NULL, "AUDIO_REF_EC_UL1 MUX"},
+	{"MM_UL2", NULL, "AUDIO_REF_EC_UL2 MUX"},
+	{"MM_UL3", NULL, "AUDIO_REF_EC_UL3 MUX"},
+	{"MM_UL4", NULL, "AUDIO_REF_EC_UL4 MUX"},
+	{"MM_UL5", NULL, "AUDIO_REF_EC_UL5 MUX"},
+	{"MM_UL6", NULL, "AUDIO_REF_EC_UL6 MUX"},
+	{"MM_UL8", NULL, "AUDIO_REF_EC_UL8 MUX"},
+	{"MM_UL9", NULL, "AUDIO_REF_EC_UL9 MUX"},
+	{"MM_UL17", NULL, "AUDIO_REF_EC_UL17 MUX"},
+	{"MM_UL18", NULL, "AUDIO_REF_EC_UL18 MUX"},
+	{"MM_UL19", NULL, "AUDIO_REF_EC_UL19 MUX"},
+
+	{"Voice_Tx Mixer", "PRI_TX_Voice", "PRI_I2S_TX"},
+	{"Voice_Tx Mixer", "PRI_MI2S_TX_Voice", "PRI_MI2S_TX"},
+	{"Voice_Tx Mixer", "MI2S_TX_Voice", "MI2S_TX"},
+	{"Voice_Tx Mixer", "TERT_MI2S_TX_Voice", "TERT_MI2S_TX"},
+	{"Voice_Tx Mixer", "SLIM_0_TX_Voice", "SLIMBUS_0_TX"},
+	{"Voice_Tx Mixer", "SLIM_7_TX_Voice", "SLIMBUS_7_TX"},
+	{"Voice_Tx Mixer", "SLIM_8_TX_Voice", "SLIMBUS_8_TX"},
+	{"Voice_Tx Mixer", "USB_AUDIO_TX_Voice", "USB_AUDIO_TX"},
+	{"Voice_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice", "INT_BT_SCO_TX"},
+	{"Voice_Tx Mixer", "AFE_PCM_TX_Voice", "PCM_TX"},
+	{"Voice_Tx Mixer", "AUX_PCM_TX_Voice", "AUX_PCM_TX"},
+	{"Voice_Tx Mixer", "SEC_AUX_PCM_TX_Voice", "SEC_AUX_PCM_TX"},
+	{"Voice_Tx Mixer", "TERT_AUX_PCM_TX_Voice", "TERT_AUX_PCM_TX"},
+	{"Voice_Tx Mixer", "QUAT_AUX_PCM_TX_Voice", "QUAT_AUX_PCM_TX"},
+	{"Voice_Tx Mixer", "SEC_MI2S_TX_Voice", "SEC_MI2S_TX"},
+	{"CS-VOICE_UL1", NULL, "Voice_Tx Mixer"},
+
+	{"Voice2_Tx Mixer", "PRI_TX_Voice2", "PRI_I2S_TX"},
+	{"Voice2_Tx Mixer", "PRI_MI2S_TX_Voice2", "PRI_MI2S_TX"},
+	{"Voice2_Tx Mixer", "MI2S_TX_Voice2", "MI2S_TX"},
+	{"Voice2_Tx Mixer", "TERT_MI2S_TX_Voice2", "TERT_MI2S_TX"},
+	{"Voice2_Tx Mixer", "SLIM_0_TX_Voice2", "SLIMBUS_0_TX"},
+	{"Voice2_Tx Mixer", "SLIM_7_TX_Voice2", "SLIMBUS_7_TX"},
+	{"Voice2_Tx Mixer", "SLIM_8_TX_Voice2", "SLIMBUS_8_TX"},
+	{"Voice2_Tx Mixer", "USB_AUDIO_TX_Voice2", "USB_AUDIO_TX"},
+	{"Voice2_Tx Mixer", "INTERNAL_BT_SCO_TX_Voice2", "INT_BT_SCO_TX"},
+	{"Voice2_Tx Mixer", "AFE_PCM_TX_Voice2", "PCM_TX"},
+	{"Voice2_Tx Mixer", "AUX_PCM_TX_Voice2", "AUX_PCM_TX"},
+	{"Voice2_Tx Mixer", "SEC_AUX_PCM_TX_Voice2", "SEC_AUX_PCM_TX"},
+	{"Voice2_Tx Mixer", "TERT_AUX_PCM_TX_Voice2", "TERT_AUX_PCM_TX"},
+	{"Voice2_Tx Mixer", "QUAT_AUX_PCM_TX_Voice2", "QUAT_AUX_PCM_TX"},
+	{"VOICE2_UL", NULL, "Voice2_Tx Mixer"},
+
+	{"VoLTE_Tx Mixer", "PRI_TX_VoLTE", "PRI_I2S_TX"},
+	{"VoLTE_Tx Mixer", "SLIM_0_TX_VoLTE", "SLIMBUS_0_TX"},
+	{"VoLTE_Tx Mixer", "SLIM_7_TX_VoLTE", "SLIMBUS_7_TX"},
+	{"VoLTE_Tx Mixer", "SLIM_8_TX_VoLTE", "SLIMBUS_8_TX"},
+	{"VoLTE_Tx Mixer", "USB_AUDIO_TX_VoLTE", "USB_AUDIO_TX"},
+	{"VoLTE_Tx Mixer", "INTERNAL_BT_SCO_TX_VoLTE", "INT_BT_SCO_TX"},
+	{"VoLTE_Tx Mixer", "AFE_PCM_TX_VoLTE", "PCM_TX"},
+	{"VoLTE_Tx Mixer", "AUX_PCM_TX_VoLTE", "AUX_PCM_TX"},
+	{"VoLTE_Tx Mixer", "SEC_AUX_PCM_TX_VoLTE", "SEC_AUX_PCM_TX"},
+	{"VoLTE_Tx Mixer", "TERT_AUX_PCM_TX_VoLTE", "TERT_AUX_PCM_TX"},
+	{"VoLTE_Tx Mixer", "QUAT_AUX_PCM_TX_VoLTE", "QUAT_AUX_PCM_TX"},
+	{"VoLTE_Tx Mixer", "MI2S_TX_VoLTE", "MI2S_TX"},
+	{"VoLTE_Tx Mixer", "PRI_MI2S_TX_VoLTE", "PRI_MI2S_TX"},
+	{"VoLTE_Tx Mixer", "TERT_MI2S_TX_VoLTE", "TERT_MI2S_TX"},
+	{"VoLTE_UL", NULL, "VoLTE_Tx Mixer"},
+
+	{"VoWLAN_Tx Mixer", "PRI_TX_VoWLAN", "PRI_I2S_TX"},
+	{"VoWLAN_Tx Mixer", "SLIM_0_TX_VoWLAN", "SLIMBUS_0_TX"},
+	{"VoWLAN_Tx Mixer", "SLIM_7_TX_VoWLAN", "SLIMBUS_7_TX"},
+	{"VoWLAN_Tx Mixer", "SLIM_8_TX_VoWLAN", "SLIMBUS_8_TX"},
+	{"VoWLAN_Tx Mixer", "USB_AUDIO_TX_VoWLAN", "USB_AUDIO_TX"},
+	{"VoWLAN_Tx Mixer", "INTERNAL_BT_SCO_TX_VoWLAN", "INT_BT_SCO_TX"},
+	{"VoWLAN_Tx Mixer", "AFE_PCM_TX_VoWLAN", "PCM_TX"},
+	{"VoWLAN_Tx Mixer", "AUX_PCM_TX_VoWLAN", "AUX_PCM_TX"},
+	{"VoWLAN_Tx Mixer", "SEC_AUX_PCM_TX_VoWLAN", "SEC_AUX_PCM_TX"},
+	{"VoWLAN_Tx Mixer", "TERT_AUX_PCM_TX_VoWLAN", "TERT_AUX_PCM_TX"},
+	{"VoWLAN_Tx Mixer", "QUAT_AUX_PCM_TX_VoWLAN", "QUAT_AUX_PCM_TX"},
+	{"VoWLAN_Tx Mixer", "MI2S_TX_VoWLAN", "MI2S_TX"},
+	{"VoWLAN_Tx Mixer", "PRI_MI2S_TX_VoWLAN", "PRI_MI2S_TX"},
+	{"VoWLAN_Tx Mixer", "TERT_MI2S_TX_VoWLAN", "TERT_MI2S_TX"},
+	{"VoWLAN_UL", NULL, "VoWLAN_Tx Mixer"},
+
+	{"VoiceMMode1_Tx Mixer", "PRI_TX_MMode1", "PRI_I2S_TX"},
+	{"VoiceMMode1_Tx Mixer", "PRI_MI2S_TX_MMode1", "PRI_MI2S_TX"},
+	{"VoiceMMode1_Tx Mixer", "MI2S_TX_MMode1", "MI2S_TX"},
+	{"VoiceMMode1_Tx Mixer", "TERT_MI2S_TX_MMode1", "TERT_MI2S_TX"},
+	{"VoiceMMode1_Tx Mixer", "INT3_MI2S_TX_MMode1", "INT3_MI2S_TX"},
+	{"VoiceMMode1_Tx Mixer", "SLIM_0_TX_MMode1", "SLIMBUS_0_TX"},
+	{"VoiceMMode1_Tx Mixer", "SLIM_7_TX_MMode1", "SLIMBUS_7_TX"},
+	{"VoiceMMode1_Tx Mixer", "SLIM_8_TX_MMode1", "SLIMBUS_8_TX"},
+	{"VoiceMMode1_Tx Mixer", "USB_AUDIO_TX_MMode1", "USB_AUDIO_TX"},
+	{"VoiceMMode1_Tx Mixer", "INT_BT_SCO_TX_MMode1", "INT_BT_SCO_TX"},
+	{"VoiceMMode1_Tx Mixer", "AFE_PCM_TX_MMode1", "PCM_TX"},
+	{"VoiceMMode1_Tx Mixer", "AUX_PCM_TX_MMode1", "AUX_PCM_TX"},
+	{"VoiceMMode1_Tx Mixer", "SEC_AUX_PCM_TX_MMode1", "SEC_AUX_PCM_TX"},
+	{"VoiceMMode1_Tx Mixer", "TERT_AUX_PCM_TX_MMode1", "TERT_AUX_PCM_TX"},
+	{"VoiceMMode1_Tx Mixer", "QUAT_AUX_PCM_TX_MMode1", "QUAT_AUX_PCM_TX"},
+	{"VOICEMMODE1_UL", NULL, "VoiceMMode1_Tx Mixer"},
+
+	{"VoiceMMode2_Tx Mixer", "PRI_TX_MMode2", "PRI_I2S_TX"},
+	{"VoiceMMode2_Tx Mixer", "PRI_MI2S_TX_MMode2", "PRI_MI2S_TX"},
+	{"VoiceMMode2_Tx Mixer", "MI2S_TX_MMode2", "MI2S_TX"},
+	{"VoiceMMode2_Tx Mixer", "TERT_MI2S_TX_MMode2", "TERT_MI2S_TX"},
+	{"VoiceMMode2_Tx Mixer", "INT3_MI2S_TX_MMode2", "INT3_MI2S_TX"},
+	{"VoiceMMode2_Tx Mixer", "SLIM_0_TX_MMode2", "SLIMBUS_0_TX"},
+	{"VoiceMMode2_Tx Mixer", "SLIM_7_TX_MMode2", "SLIMBUS_7_TX"},
+	{"VoiceMMode2_Tx Mixer", "SLIM_8_TX_MMode2", "SLIMBUS_8_TX"},
+	{"VoiceMMode2_Tx Mixer", "USB_AUDIO_TX_MMode2", "USB_AUDIO_TX"},
+	{"VoiceMMode2_Tx Mixer", "INT_BT_SCO_TX_MMode2", "INT_BT_SCO_TX"},
+	{"VoiceMMode2_Tx Mixer", "AFE_PCM_TX_MMode2", "PCM_TX"},
+	{"VoiceMMode2_Tx Mixer", "AUX_PCM_TX_MMode2", "AUX_PCM_TX"},
+	{"VoiceMMode2_Tx Mixer", "SEC_AUX_PCM_TX_MMode2", "SEC_AUX_PCM_TX"},
+	{"VoiceMMode2_Tx Mixer", "TERT_AUX_PCM_TX_MMode2", "TERT_AUX_PCM_TX"},
+	{"VoiceMMode2_Tx Mixer", "QUAT_AUX_PCM_TX_MMode2", "QUAT_AUX_PCM_TX"},
+	{"VOICEMMODE2_UL", NULL, "VoiceMMode2_Tx Mixer"},
+
+	{"Voip_Tx Mixer", "PRI_TX_Voip", "PRI_I2S_TX"},
+	{"Voip_Tx Mixer", "MI2S_TX_Voip", "MI2S_TX"},
+	{"Voip_Tx Mixer", "TERT_MI2S_TX_Voip", "TERT_MI2S_TX"},
+	{"Voip_Tx Mixer", "INT3_MI2S_TX_Voip", "INT3_MI2S_TX"},
+	{"Voip_Tx Mixer", "SLIM_0_TX_Voip", "SLIMBUS_0_TX"},
+	{"Voip_Tx Mixer", "SLIM_7_TX_Voip", "SLIMBUS_7_TX"},
+	{"Voip_Tx Mixer", "SLIM_8_TX_Voip", "SLIMBUS_8_TX"},
+	{"Voip_Tx Mixer", "USB_AUDIO_TX_Voip", "USB_AUDIO_TX"},
+	{"Voip_Tx Mixer", "INTERNAL_BT_SCO_TX_Voip", "INT_BT_SCO_TX"},
+	{"Voip_Tx Mixer", "AFE_PCM_TX_Voip", "PCM_TX"},
+	{"Voip_Tx Mixer", "AUX_PCM_TX_Voip", "AUX_PCM_TX"},
+	{"Voip_Tx Mixer", "SEC_AUX_PCM_TX_Voip", "SEC_AUX_PCM_TX"},
+	{"Voip_Tx Mixer", "TERT_AUX_PCM_TX_Voip", "TERT_AUX_PCM_TX"},
+	{"Voip_Tx Mixer", "QUAT_AUX_PCM_TX_Voip", "QUAT_AUX_PCM_TX"},
+	{"Voip_Tx Mixer", "PRI_MI2S_TX_Voip", "PRI_MI2S_TX"},
+	{"VOIP_UL", NULL, "Voip_Tx Mixer"},
+
+	{"SLIMBUS_DL_HL", "Switch", "SLIM0_DL_HL"},
+	{"SLIMBUS_0_RX", NULL, "SLIMBUS_DL_HL"},
+	{"SLIMBUS1_DL_HL", "Switch", "SLIM1_DL_HL"},
+	{"SLIMBUS_1_RX", NULL, "SLIMBUS1_DL_HL"},
+	{"SLIMBUS3_DL_HL", "Switch", "SLIM3_DL_HL"},
+	{"SLIMBUS_3_RX", NULL, "SLIMBUS3_DL_HL"},
+	{"SLIMBUS4_DL_HL", "Switch", "SLIM4_DL_HL"},
+	{"SLIMBUS_4_RX", NULL, "SLIMBUS4_DL_HL"},
+	{"SLIMBUS6_DL_HL", "Switch", "SLIM0_DL_HL"},
+	{"SLIMBUS_6_RX", NULL, "SLIMBUS6_DL_HL"},
+	{"SLIM0_UL_HL", NULL, "SLIMBUS_0_TX"},
+	{"SLIM1_UL_HL", NULL, "SLIMBUS_1_TX"},
+	{"SLIM3_UL_HL", NULL, "SLIMBUS_3_TX"},
+	{"SLIM4_UL_HL", NULL, "SLIMBUS_4_TX"},
+	{"SLIM8_UL_HL", NULL, "SLIMBUS_8_TX"},
+
+	{"LSM1 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM1 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM1 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM1 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM1 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM1 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"LSM1_UL_HL", NULL, "LSM1 MUX"},
+
+	{"LSM2 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM2 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM2 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM2 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM2 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM2 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"LSM2_UL_HL", NULL, "LSM2 MUX"},
+
+
+	{"LSM3 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM3 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM3 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM3 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM3 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM3 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"LSM3_UL_HL", NULL, "LSM3 MUX"},
+
+
+	{"LSM4 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM4 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM4 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM4 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM4 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM4 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"LSM4_UL_HL", NULL, "LSM4 MUX"},
+
+	{"LSM5 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM5 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM5 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM5 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM5 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM5 MUX", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"LSM5_UL_HL", NULL, "LSM5 MUX"},
+
+	{"LSM6 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM6 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM6 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM6 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM6 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM6_UL_HL", NULL, "LSM6 MUX"},
+
+
+	{"LSM7 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM7 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM7 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM7 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM7 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM7_UL_HL", NULL, "LSM7 MUX"},
+
+
+	{"LSM8 MUX", "SLIMBUS_0_TX", "SLIMBUS_0_TX"},
+	{"LSM8 MUX", "SLIMBUS_1_TX", "SLIMBUS_1_TX"},
+	{"LSM8 MUX", "SLIMBUS_3_TX", "SLIMBUS_3_TX"},
+	{"LSM8 MUX", "SLIMBUS_4_TX", "SLIMBUS_4_TX"},
+	{"LSM8 MUX", "SLIMBUS_5_TX", "SLIMBUS_5_TX"},
+	{"LSM8_UL_HL", NULL, "LSM8 MUX"},
+
+
+	{"CPE_LSM_UL_HL", NULL, "BE_IN"},
+	{"QCHAT_Tx Mixer", "PRI_TX_QCHAT", "PRI_I2S_TX"},
+	{"QCHAT_Tx Mixer", "SLIM_0_TX_QCHAT", "SLIMBUS_0_TX"},
+	{"QCHAT_Tx Mixer", "SLIM_7_TX_QCHAT", "SLIMBUS_7_TX"},
+	{"QCHAT_Tx Mixer", "SLIM_8_TX_QCHAT", "SLIMBUS_8_TX"},
+	{"QCHAT_Tx Mixer", "INTERNAL_BT_SCO_TX_QCHAT", "INT_BT_SCO_TX"},
+	{"QCHAT_Tx Mixer", "AFE_PCM_TX_QCHAT", "PCM_TX"},
+	{"QCHAT_Tx Mixer", "AUX_PCM_TX_QCHAT", "AUX_PCM_TX"},
+	{"QCHAT_Tx Mixer", "SEC_AUX_PCM_TX_QCHAT", "SEC_AUX_PCM_TX"},
+	{"QCHAT_Tx Mixer", "TERT_AUX_PCM_TX_QCHAT", "TERT_AUX_PCM_TX"},
+	{"QCHAT_Tx Mixer", "QUAT_AUX_PCM_TX_QCHAT", "QUAT_AUX_PCM_TX"},
+	{"QCHAT_Tx Mixer", "MI2S_TX_QCHAT", "MI2S_TX"},
+	{"QCHAT_Tx Mixer", "PRI_MI2S_TX_QCHAT", "PRI_MI2S_TX"},
+	{"QCHAT_Tx Mixer", "TERT_MI2S_TX_QCHAT", "TERT_MI2S_TX"},
+	{"QCHAT_Tx Mixer", "INT3_MI2S_TX_QCHAT", "INT3_MI2S_TX"},
+	{"QCHAT_Tx Mixer", "USB_AUDIO_TX_QCHAT", "USB_AUDIO_TX"},
+	{"QCHAT_UL", NULL, "QCHAT_Tx Mixer"},
+
+	{"INT_FM_RX", NULL, "INTFM_DL_HL"},
+	{"INTFM_UL_HL", NULL, "INT_FM_TX"},
+	{"INTHFP_UL_HL", NULL, "HFP_PRI_AUX_UL_HL"},
+	{"HFP_PRI_AUX_UL_HL", "Switch", "AUX_PCM_TX"},
+	{"INTHFP_UL_HL", NULL, "HFP_AUX_UL_HL"},
+	{"HFP_AUX_UL_HL", "Switch", "SEC_AUX_PCM_TX"},
+	{"INTHFP_UL_HL", NULL, "HFP_INT_UL_HL"},
+	{"HFP_INT_UL_HL", "Switch", "INT_BT_SCO_TX"},
+	{"AUX_PCM_RX", NULL, "AUXPCM_DL_HL"},
+	{"AUXPCM_UL_HL", NULL, "AUX_PCM_TX"},
+	{"MI2S_RX", NULL, "MI2S_DL_HL"},
+	{"MI2S_UL_HL", NULL, "MI2S_TX"},
+	{"PCM_RX_DL_HL", "Switch", "SLIM0_DL_HL"},
+	{"PCM_RX", NULL, "PCM_RX_DL_HL"},
+	{"INT0_MI2S_RX_DL_HL", "Switch", "INT0_MI2S_DL_HL"},
+	{"INT0_MI2S_RX", NULL, "INT0_MI2S_RX_DL_HL"},
+	{"INT4_MI2S_RX_DL_HL", "Switch", "INT4_MI2S_DL_HL"},
+	{"INT4_MI2S_RX", NULL, "INT4_MI2S_RX_DL_HL"},
+	{"PRI_MI2S_RX_DL_HL", "Switch", "PRI_MI2S_DL_HL"},
+	{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_DL_HL"},
+	{"SEC_MI2S_RX_DL_HL", "Switch", "SEC_MI2S_DL_HL"},
+	{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX_DL_HL"},
+	{"TERT_MI2S_RX_DL_HL", "Switch", "TERT_MI2S_DL_HL"},
+	{"TERT_MI2S_RX", NULL, "TERT_MI2S_RX_DL_HL"},
+
+	{"QUAT_MI2S_RX_DL_HL", "Switch", "QUAT_MI2S_DL_HL"},
+	{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX_DL_HL"},
+	{"MI2S_UL_HL", NULL, "TERT_MI2S_TX"},
+	{"TERT_MI2S_UL_HL", NULL, "TERT_MI2S_TX"},
+	{"SEC_I2S_RX", NULL, "SEC_I2S_DL_HL"},
+	{"PRI_MI2S_UL_HL", NULL, "PRI_MI2S_TX"},
+	{"SEC_MI2S_RX", NULL, "SEC_MI2S_DL_HL"},
+	{"PRI_MI2S_RX", NULL, "PRI_MI2S_DL_HL"},
+	{"TERT_MI2S_RX", NULL, "TERT_MI2S_DL_HL"},
+	{"QUAT_MI2S_UL_HL", NULL, "QUAT_MI2S_TX"},
+
+	{"PRI_TDM_TX_0_UL_HL", NULL, "PRI_TDM_TX_0"},
+	{"PRI_TDM_RX_0", NULL, "PRI_TDM_RX_0_DL_HL"},
+	{"SEC_TDM_TX_0_UL_HL", NULL, "SEC_TDM_TX_0"},
+	{"SEC_TDM_RX_0", NULL, "SEC_TDM_RX_0_DL_HL"},
+	{"TERT_TDM_TX_0_UL_HL", NULL, "TERT_TDM_TX_0"},
+	{"TERT_TDM_TX_1_UL_HL", NULL, "TERT_TDM_TX_1"},
+	{"TERT_TDM_TX_2_UL_HL", NULL, "TERT_TDM_TX_2"},
+	{"TERT_TDM_TX_3_UL_HL", NULL, "TERT_TDM_TX_3"},
+	{"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0_DL_HL"},
+	{"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_0_DL_HL"},
+	{"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_0_DL_HL"},
+	{"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_0_DL_HL"},
+	{"QUAT_TDM_TX_0_UL_HL", NULL, "QUAT_TDM_TX_0"},
+	{"QUAT_TDM_TX_1_UL_HL", NULL, "QUAT_TDM_TX_1"},
+	{"QUAT_TDM_TX_2_UL_HL", NULL, "QUAT_TDM_TX_2"},
+	{"QUAT_TDM_TX_3_UL_HL", NULL, "QUAT_TDM_TX_3"},
+	{"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0_DL_HL"},
+	{"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1_DL_HL"},
+	{"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2_DL_HL"},
+	{"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3_DL_HL"},
+
+	{"TERT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"TERT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"TERT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"TERT_TDM_RX_0", NULL, "TERT_TDM_RX_0 Port Mixer"},
+
+	{"TERT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"TERT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"TERT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"TERT_TDM_RX_1", NULL, "TERT_TDM_RX_1 Port Mixer"},
+
+	{"TERT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"TERT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"TERT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"TERT_TDM_RX_2", NULL, "TERT_TDM_RX_2 Port Mixer"},
+
+	{"TERT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"TERT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"TERT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"TERT_TDM_RX_3", NULL, "TERT_TDM_RX_3 Port Mixer"},
+
+	{"QUAT_TDM_RX_0 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"QUAT_TDM_RX_0 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"QUAT_TDM_RX_0 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"QUAT_TDM_RX_0", NULL, "QUAT_TDM_RX_0 Port Mixer"},
+
+	{"QUAT_TDM_RX_1 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"QUAT_TDM_RX_1 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"QUAT_TDM_RX_1 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"QUAT_TDM_RX_1", NULL, "QUAT_TDM_RX_1 Port Mixer"},
+
+	{"QUAT_TDM_RX_2 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"QUAT_TDM_RX_2 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"QUAT_TDM_RX_2 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"QUAT_TDM_RX_2", NULL, "QUAT_TDM_RX_2 Port Mixer"},
+
+	{"QUAT_TDM_RX_3 Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_0", "TERT_TDM_TX_0"},
+	{"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_1", "TERT_TDM_TX_1"},
+	{"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_2", "TERT_TDM_TX_2"},
+	{"QUAT_TDM_RX_3 Port Mixer", "TERT_TDM_TX_3", "TERT_TDM_TX_3"},
+	{"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_0", "QUAT_TDM_TX_0"},
+	{"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_1", "QUAT_TDM_TX_1"},
+	{"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_2", "QUAT_TDM_TX_2"},
+	{"QUAT_TDM_RX_3 Port Mixer", "QUAT_TDM_TX_3", "QUAT_TDM_TX_3"},
+	{"QUAT_TDM_RX_3", NULL, "QUAT_TDM_RX_3 Port Mixer"},
+
+	{"SLIMBUS_0_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"SLIMBUS_0_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"SLIMBUS_0_RX", NULL, "SLIMBUS_0_RX Port Mixer"},
+	{"AFE_PCM_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"AFE_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"PCM_RX", NULL, "AFE_PCM_RX Port Mixer"},
+
+	{"AUX_PCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"AUX_PCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"AUX_PCM_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"AUX_PCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"AUX_PCM_RX", NULL, "AUX_PCM_RX Port Mixer"},
+
+	{"SEC_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"SEC_AUXPCM_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"SEC_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"SEC_AUX_PCM_RX", NULL, "SEC_AUXPCM_RX Port Mixer"},
+
+	{"TERT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"TERT_AUXPCM_RX Port Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+	{"TERT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"TERT_AUX_PCM_RX", NULL, "TERT_AUXPCM_RX Port Mixer"},
+
+	{"QUAT_AUXPCM_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"QUAT_AUXPCM_RX Port Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+	{"QUAT_AUXPCM_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"QUAT_AUX_PCM_RX", NULL, "QUAT_AUXPCM_RX Port Mixer"},
+
+	{"Voice Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+	{"Voice Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"Voice Stub Tx Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"Voice Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+	{"Voice Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"Voice Stub Tx Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"Voice Stub Tx Mixer", "TERT_AUXPCM_UL_TX", "TERT_AUX_PCM_TX"},
+	{"Voice Stub Tx Mixer", "QUAT_AUXPCM_UL_TX", "QUAT_AUX_PCM_TX"},
+	{"Voice Stub Tx Mixer", "MI2S_TX", "MI2S_TX"},
+	{"Voice Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"Voice Stub Tx Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"Voice Stub Tx Mixer", "INT3_MI2S_TX", "INT3_MI2S_TX"},
+	{"Voice Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"Voice Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"Voice Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
+	{"Voice Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"Voice Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"Voice Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"VOICE_STUB_UL", NULL, "Voice Stub Tx Mixer"},
+
+	{"VoLTE Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+	{"VoLTE Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"VoLTE Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+	{"VoLTE Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"VoLTE Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"VoLTE Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
+	{"VoLTE Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"VoLTE Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"VoLTE Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"VoLTE Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"VoLTE Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"VOLTE_STUB_UL", NULL, "VoLTE Stub Tx Mixer"},
+
+	{"Voice2 Stub Tx Mixer", "STUB_TX_HL", "STUB_TX"},
+	{"Voice2 Stub Tx Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"Voice2 Stub Tx Mixer", "STUB_1_TX_HL", "STUB_1_TX"},
+	{"Voice2 Stub Tx Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"Voice2 Stub Tx Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"Voice2 Stub Tx Mixer", "SLIM_3_TX", "SLIMBUS_3_TX"},
+	{"Voice2 Stub Tx Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"Voice2 Stub Tx Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"Voice2 Stub Tx Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"Voice2 Stub Tx Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"Voice2 Stub Tx Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"VOICE2_STUB_UL", NULL, "Voice2 Stub Tx Mixer"},
+
+	{"STUB_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"STUB_RX Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"STUB_RX Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"STUB_RX", NULL, "STUB_RX Mixer"},
+	{"SLIMBUS_1_RX Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SLIMBUS_1_RX Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"SLIMBUS_1_RX Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Mixer"},
+	{"AFE_PCM_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"AFE_PCM_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIMBUS_3_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SLIMBUS_3_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"SLIMBUS_3_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX_Voice Mixer"},
+
+	{"SLIM_7_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SLIM_7_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SLIM_7_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SLIM_7_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SLIM_7_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SLIM_7_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SLIM_7_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SLIM_7_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"SLIM_7_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIM_7_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SLIM_7_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"SLIM_7_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"SLIMBUS_7_RX", NULL, "SLIM_7_RX_Voice Mixer"},
+
+	{"SLIM_8_RX_Voice Mixer", "CSVoice", "CS-VOICE_DL1"},
+	{"SLIM_8_RX_Voice Mixer", "Voice2", "VOICE2_DL"},
+	{"SLIM_8_RX_Voice Mixer", "VoLTE", "VoLTE_DL"},
+	{"SLIM_8_RX_Voice Mixer", "VoWLAN", "VoWLAN_DL"},
+	{"SLIM_8_RX_Voice Mixer", "Voip", "VOIP_DL"},
+	{"SLIM_8_RX_Voice Mixer", "DTMF", "DTMF_DL_HL"},
+	{"SLIM_8_RX_Voice Mixer", "Voice Stub", "VOICE_STUB_DL"},
+	{"SLIM_8_RX_Voice Mixer", "Voice2 Stub", "VOICE2_STUB_DL"},
+	{"SLIM_8_RX_Voice Mixer", "VoLTE Stub", "VOLTE_STUB_DL"},
+	{"SLIM_8_RX_Voice Mixer", "QCHAT", "QCHAT_DL"},
+	{"SLIM_8_RX_Voice Mixer", "VoiceMMode1", "VOICEMMODE1_DL"},
+	{"SLIM_8_RX_Voice Mixer", "VoiceMMode2", "VOICEMMODE2_DL"},
+	{"SLIMBUS_8_RX", NULL, "SLIM_8_RX_Voice Mixer"},
+
+	{"SLIMBUS_1_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"SLIMBUS_1_RX Port Mixer", "AFE_PCM_TX", "PCM_TX"},
+	{"SLIMBUS_1_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"SLIMBUS_1_RX", NULL, "SLIMBUS_1_RX Port Mixer"},
+	{"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"INTERNAL_BT_SCO_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"INT_BT_SCO_RX", NULL, "INTERNAL_BT_SCO_RX Port Mixer"},
+	{"SLIMBUS_3_RX Port Mixer", "INTERNAL_BT_SCO_RX", "INT_BT_SCO_RX"},
+	{"SLIMBUS_3_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"SLIMBUS_3_RX Port Mixer", "AFE_PCM_RX", "PCM_RX"},
+	{"SLIMBUS_3_RX Port Mixer", "AUX_PCM_RX", "AUX_PCM_RX"},
+	{"SLIMBUS_3_RX Port Mixer", "SLIM_0_RX", "SLIMBUS_0_RX"},
+	{"SLIMBUS_3_RX", NULL, "SLIMBUS_3_RX Port Mixer"},
+
+	{"SLIMBUS_6_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "SLIM_7_TX", "SLIMBUS_7_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "SLIM_8_TX", "SLIMBUS_8_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "SEC_AUX_PCM_UL_TX", "SEC_AUX_PCM_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"SLIMBUS_6_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"SLIMBUS_6_RX", NULL, "SLIMBUS_6_RX Port Mixer"},
+
+	{"HDMI_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"HDMI", NULL, "HDMI_RX Port Mixer"},
+
+	{"DISPLAY_PORT_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"DISPLAY_PORT", NULL, "DISPLAY_PORT_RX Port Mixer"},
+
+	{"SEC_I2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"SEC_I2S_RX", NULL, "SEC_I2S_RX Port Mixer"},
+
+	{"MI2S_RX Port Mixer", "SLIM_1_TX", "SLIMBUS_1_TX"},
+	{"MI2S_RX Port Mixer", "MI2S_TX", "MI2S_TX"},
+	{"MI2S_RX", NULL, "MI2S_RX Port Mixer"},
+
+	{"PRI_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"PRI_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"PRI_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"PRI_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"PRI_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"PRI_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"PRI_MI2S_RX Port Mixer", "INTERNAL_BT_SCO_TX", "INT_BT_SCO_TX"},
+	{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX Port Mixer"},
+
+	{"SEC_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"SEC_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"SEC_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"SEC_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"SEC_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"SEC_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"SEC_MI2S_RX", NULL, "SEC_MI2S_RX Port Mixer"},
+
+	{"TERT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"TERT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"TERT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"TERT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"TERT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"TERT_MI2S_RX", NULL, "TERT_MI2S_RX Port Mixer"},
+
+	{"QUAT_MI2S_RX Port Mixer", "PRI_MI2S_TX", "PRI_MI2S_TX"},
+	{"QUAT_MI2S_RX Port Mixer", "SEC_MI2S_TX", "SEC_MI2S_TX"},
+	{"QUAT_MI2S_RX Port Mixer", "TERT_MI2S_TX", "TERT_MI2S_TX"},
+	{"QUAT_MI2S_RX Port Mixer", "QUAT_MI2S_TX", "QUAT_MI2S_TX"},
+	{"QUAT_MI2S_RX Port Mixer", "SLIM_0_TX", "SLIMBUS_0_TX"},
+	{"QUAT_MI2S_RX Port Mixer", "INTERNAL_FM_TX", "INT_FM_TX"},
+	{"QUAT_MI2S_RX Port Mixer", "AUX_PCM_UL_TX", "AUX_PCM_TX"},
+	{"QUAT_MI2S_RX", NULL, "QUAT_MI2S_RX Port Mixer"},
+
+	/* Backend Enablement */
+
+	{"BE_OUT", NULL, "PRI_I2S_RX"},
+	{"BE_OUT", NULL, "SEC_I2S_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_0_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_1_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_2_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_3_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_4_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_5_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_6_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_7_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_8_RX"},
+	{"BE_OUT", NULL, "USB_AUDIO_RX"},
+	{"BE_OUT", NULL, "HDMI"},
+	{"BE_OUT", NULL, "DISPLAY_PORT"},
+	{"BE_OUT", NULL, "SPDIF_RX"},
+	{"BE_OUT", NULL, "MI2S_RX"},
+	{"BE_OUT", NULL, "QUAT_MI2S_RX"},
+	{"BE_OUT", NULL, "QUIN_MI2S_RX"},
+	{"BE_OUT", NULL, "TERT_MI2S_RX"},
+	{"BE_OUT", NULL, "SEC_MI2S_RX"},
+	{"BE_OUT", NULL, "SEC_MI2S_RX_SD1"},
+	{"BE_OUT", NULL, "PRI_MI2S_RX"},
+	{"BE_OUT", NULL, "INT0_MI2S_RX"},
+	{"BE_OUT", NULL, "INT4_MI2S_RX"},
+	{"BE_OUT", NULL, "INT_BT_SCO_RX"},
+	{"BE_OUT", NULL, "INT_BT_A2DP_RX"},
+	{"BE_OUT", NULL, "INT_FM_RX"},
+	{"BE_OUT", NULL, "PCM_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_3_RX"},
+	{"BE_OUT", NULL, "AUX_PCM_RX"},
+	{"BE_OUT", NULL, "SEC_AUX_PCM_RX"},
+	{"BE_OUT", NULL, "TERT_AUX_PCM_RX"},
+	{"BE_OUT", NULL, "QUAT_AUX_PCM_RX"},
+	{"BE_OUT", NULL, "INT_BT_SCO_RX"},
+	{"BE_OUT", NULL, "INT_FM_RX"},
+	{"BE_OUT", NULL, "PCM_RX"},
+	{"BE_OUT", NULL, "SLIMBUS_3_RX"},
+	{"BE_OUT", NULL, "VOICE_PLAYBACK_TX"},
+	{"BE_OUT", NULL, "VOICE2_PLAYBACK_TX"},
+	{"BE_OUT", NULL, "PRI_TDM_RX_0"},
+	{"BE_OUT", NULL, "SEC_TDM_RX_0"},
+	{"BE_OUT", NULL, "TERT_TDM_RX_0"},
+	{"BE_OUT", NULL, "TERT_TDM_RX_1"},
+	{"BE_OUT", NULL, "TERT_TDM_RX_2"},
+	{"BE_OUT", NULL, "TERT_TDM_RX_3"},
+	{"BE_OUT", NULL, "QUAT_TDM_RX_0"},
+	{"BE_OUT", NULL, "QUAT_TDM_RX_1"},
+	{"BE_OUT", NULL, "QUAT_TDM_RX_2"},
+	{"BE_OUT", NULL, "QUAT_TDM_RX_3"},
+
+	{"PRI_I2S_TX", NULL, "BE_IN"},
+	{"MI2S_TX", NULL, "BE_IN"},
+	{"QUAT_MI2S_TX", NULL, "BE_IN"},
+	{"QUIN_MI2S_TX", NULL, "BE_IN"},
+	{"PRI_MI2S_TX", NULL, "BE_IN"},
+	{"TERT_MI2S_TX", NULL, "BE_IN"},
+	{"INT2_MI2S_TX", NULL, "BE_IN"},
+	{"INT3_MI2S_TX", NULL, "BE_IN"},
+	{"SEC_MI2S_TX", NULL, "BE_IN"},
+	{"SENARY_MI2S_TX", NULL, "BE_IN" },
+	{"SLIMBUS_0_TX", NULL, "BE_IN" },
+	{"SLIMBUS_1_TX", NULL, "BE_IN" },
+	{"SLIMBUS_3_TX", NULL, "BE_IN" },
+	{"SLIMBUS_4_TX", NULL, "BE_IN" },
+	{"SLIMBUS_5_TX", NULL, "BE_IN" },
+	{"SLIMBUS_6_TX", NULL, "BE_IN" },
+	{"SLIMBUS_7_TX", NULL, "BE_IN" },
+	{"SLIMBUS_8_TX", NULL, "BE_IN" },
+	{"USB_AUDIO_TX", NULL, "BE_IN" },
+	{"INT_BT_SCO_TX", NULL, "BE_IN"},
+	{"INT_FM_TX", NULL, "BE_IN"},
+	{"PCM_TX", NULL, "BE_IN"},
+	{"BE_OUT", NULL, "SLIMBUS_3_RX"},
+	{"BE_OUT", NULL, "STUB_RX"},
+	{"STUB_TX", NULL, "BE_IN"},
+	{"STUB_1_TX", NULL, "BE_IN"},
+	{"BE_OUT", NULL, "AUX_PCM_RX"},
+	{"AUX_PCM_TX", NULL, "BE_IN"},
+	{"SEC_AUX_PCM_TX", NULL, "BE_IN"},
+	{"TERT_AUX_PCM_TX", NULL, "BE_IN"},
+	{"QUAT_AUX_PCM_TX", NULL, "BE_IN"},
+	{"INCALL_RECORD_TX", NULL, "BE_IN"},
+	{"INCALL_RECORD_RX", NULL, "BE_IN"},
+	{"SLIM0_RX_VI_FB_LCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"},
+	{"SLIM0_RX_VI_FB_RCH_MUX", "SLIM4_TX", "SLIMBUS_4_TX"},
+	{"PRI_MI2S_RX_VI_FB_MUX", "SENARY_TX", "SENARY_TX"},
+	{"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_LCH_MUX"},
+	{"SLIMBUS_0_RX", NULL, "SLIM0_RX_VI_FB_RCH_MUX"},
+	{"PRI_MI2S_RX", NULL, "PRI_MI2S_RX_VI_FB_MUX"},
+	{"PRI_TDM_TX_0", NULL, "BE_IN"},
+	{"SEC_TDM_TX_0", NULL, "BE_IN"},
+	{"TERT_TDM_TX_0", NULL, "BE_IN"},
+	{"TERT_TDM_TX_1", NULL, "BE_IN"},
+	{"TERT_TDM_TX_2", NULL, "BE_IN"},
+	{"TERT_TDM_TX_3", NULL, "BE_IN"},
+	{"QUAT_TDM_TX_0", NULL, "BE_IN"},
+	{"QUAT_TDM_TX_1", NULL, "BE_IN"},
+	{"QUAT_TDM_TX_2", NULL, "BE_IN"},
+	{"QUAT_TDM_TX_3", NULL, "BE_IN"},
+};
+
+static int msm_pcm_routing_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int be_id = rtd->dai_link->id;
+
+	if (be_id >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s: unexpected BE id %d\n", __func__, be_id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&routing_lock);
+	msm_bedais[be_id].sample_rate = params_rate(params);
+	msm_bedais[be_id].channel = params_channels(params);
+	msm_bedais[be_id].format = params_format(params);
+	pr_debug("%s: BE Sample Rate (%d) format (%d) BE id %d\n",
+		__func__, msm_bedais[be_id].sample_rate,
+		msm_bedais[be_id].format, be_id);
+	mutex_unlock(&routing_lock);
+	return 0;
+}
+
+static int msm_pcm_routing_close(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int be_id = rtd->dai_link->id;
+	int i, session_type, path_type, topology;
+	struct msm_pcm_routing_bdai_data *bedai;
+	struct msm_pcm_routing_fdai_data *fdai;
+
+	pr_debug("%s: substream->pcm->id:%s\n",
+		 __func__, substream->pcm->id);
+
+	if (be_id >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s: unexpected BE id %d\n", __func__, be_id);
+		return -EINVAL;
+	}
+
+	bedai = &msm_bedais[be_id];
+	session_type = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+		0 : 1);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		path_type = ADM_PATH_PLAYBACK;
+	else
+		path_type = ADM_PATH_LIVE_REC;
+
+	mutex_lock(&routing_lock);
+	for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+		fdai = &fe_dai_map[i][session_type];
+		if (fdai->strm_id != INVALID_SESSION) {
+			int idx;
+			int port_id;
+			unsigned long copp =
+				session_copp_map[i][session_type][be_id];
+			for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+				if (test_bit(idx, &copp))
+					break;
+			fdai->be_srate = bedai->sample_rate;
+			port_id = bedai->port_id;
+			topology = adm_get_topology_for_port_copp_idx(port_id,
+								     idx);
+			adm_close(bedai->port_id, fdai->perf_mode, idx);
+			pr_debug("%s: copp:%ld,idx bit fe:%d, type:%d,be:%d topology=0x%x\n",
+				 __func__, copp, i, session_type, be_id,
+				 topology);
+			clear_bit(idx,
+				  &session_copp_map[i][session_type][be_id]);
+			if ((fdai->perf_mode == LEGACY_PCM_MODE) &&
+				(bedai->compr_passthr_mode == LEGACY_PCM))
+				msm_pcm_routing_deinit_pp(bedai->port_id,
+							  topology);
+		}
+	}
+
+	bedai->compr_passthr_mode = LEGACY_PCM;
+	bedai->active = 0;
+	bedai->sample_rate = 0;
+	bedai->channel = 0;
+	mutex_unlock(&routing_lock);
+
+	return 0;
+}
+
+static int msm_pcm_routing_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_soc_pcm_runtime *rtd = substream->private_data;
+	unsigned int be_id = rtd->dai_link->id;
+	int i, path_type, session_type, topology;
+	struct msm_pcm_routing_bdai_data *bedai;
+	u32 channels, sample_rate;
+	uint16_t bits_per_sample = 16, voc_path_type;
+	struct msm_pcm_routing_fdai_data *fdai;
+	u32 session_id;
+
+	pr_debug("%s: substream->pcm->id:%s\n",
+		 __func__, substream->pcm->id);
+
+	if (be_id >= MSM_BACKEND_DAI_MAX) {
+		pr_err("%s: unexpected BE id %d\n", __func__, be_id);
+		return -EINVAL;
+	}
+
+	bedai = &msm_bedais[be_id];
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		if (bedai->compr_passthr_mode != LEGACY_PCM)
+			path_type = ADM_PATH_COMPRESSED_RX;
+		else
+			path_type = ADM_PATH_PLAYBACK;
+		session_type = SESSION_TYPE_RX;
+	} else {
+		path_type = ADM_PATH_LIVE_REC;
+		session_type = SESSION_TYPE_TX;
+	}
+
+	mutex_lock(&routing_lock);
+	if (bedai->active == 1)
+		goto done; /* Ignore prepare if back-end already active */
+
+	/* AFE port is not active at this point. However, still
+	 * go ahead setting active flag under the notion that
+	 * QDSP6 is able to handle ADM starting before AFE port
+	 * is started.
+	 */
+	bedai->active = 1;
+
+	for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MM_SIZE) {
+		fdai = &fe_dai_map[i][session_type];
+		if (fdai->strm_id != INVALID_SESSION) {
+			int app_type, app_type_idx, copp_idx, acdb_dev_id;
+
+			if (session_type == SESSION_TYPE_TX &&
+			    fdai->be_srate &&
+			    (fdai->be_srate != bedai->sample_rate)) {
+				pr_debug("%s: flush strm %d diff BE rates\n",
+					__func__,
+					fdai->strm_id);
+
+				if (fdai->event_info.event_func)
+					fdai->event_info.event_func(
+						MSM_PCM_RT_EVT_BUF_RECFG,
+						fdai->event_info.priv_data);
+				fdai->be_srate = 0; /* might not need it */
+			}
+			bits_per_sample = msm_routing_get_bit_width(
+						bedai->format);
+
+			app_type =
+				fe_dai_app_type_cfg[i][session_type].app_type;
+			if (app_type) {
+				app_type_idx =
+				msm_pcm_routing_get_app_type_idx(app_type);
+				sample_rate =
+					fe_dai_app_type_cfg[i][session_type].
+						sample_rate;
+				bits_per_sample =
+					app_type_cfg[app_type_idx].bit_width;
+			} else
+				sample_rate = bedai->sample_rate;
+			/*
+			 * check if ADM needs to be configured with different
+			 * channel mapping than backend
+			 */
+			if (!bedai->adm_override_ch)
+				channels = bedai->channel;
+			else
+				channels = bedai->adm_override_ch;
+			acdb_dev_id =
+			fe_dai_app_type_cfg[i][session_type].acdb_dev_id;
+			topology = msm_routing_get_adm_topology(path_type, i,
+						session_type);
+			copp_idx = adm_open(bedai->port_id, path_type,
+					    sample_rate, channels, topology,
+					    fdai->perf_mode, bits_per_sample,
+					    app_type, acdb_dev_id);
+			if ((copp_idx < 0) ||
+				(copp_idx >= MAX_COPPS_PER_PORT)) {
+				pr_err("%s: adm open failed\n", __func__);
+				mutex_unlock(&routing_lock);
+				return -EINVAL;
+			}
+			pr_debug("%s: setting idx bit of fe:%d, type: %d, be:%d\n",
+				 __func__, i, session_type, be_id);
+			set_bit(copp_idx,
+				&session_copp_map[i][session_type][be_id]);
+
+			if (msm_is_resample_needed(
+				sample_rate,
+				bedai->sample_rate))
+				adm_copp_mfc_cfg(
+					bedai->port_id, copp_idx,
+					bedai->sample_rate);
+
+			msm_pcm_routing_build_matrix(i, session_type, path_type,
+						     fdai->perf_mode);
+			if ((fdai->perf_mode == LEGACY_PCM_MODE) &&
+				(bedai->compr_passthr_mode ==
+					LEGACY_PCM))
+				msm_pcm_routing_cfg_pp(bedai->port_id, copp_idx,
+						       topology, channels);
+		}
+	}
+
+	for_each_set_bit(i, &bedai->fe_sessions, MSM_FRONTEND_DAI_MAX) {
+		session_id = msm_pcm_routing_get_voc_sessionid(i);
+		if (session_id) {
+			pr_debug("%s voice session_id: 0x%x",
+				 __func__, session_id);
+
+			if (session_type == SESSION_TYPE_TX)
+				voc_path_type = TX_PATH;
+			else
+				voc_path_type = RX_PATH;
+
+			voc_set_route_flag(session_id, voc_path_type, 1);
+			voc_set_device_config(session_id,  voc_path_type,
+					      bedai->channel, bedai->port_id);
+
+			if (voc_get_route_flag(session_id, RX_PATH) &&
+			    voc_get_route_flag(session_id, TX_PATH))
+				voc_enable_device(session_id);
+		}
+	}
+
+done:
+	mutex_unlock(&routing_lock);
+
+	return 0;
+}
+
+static int msm_routing_send_device_pp_params(int port_id, int copp_idx)
+{
+	int index, topo_id, be_idx;
+	unsigned long pp_config = 0;
+	bool mute_on;
+	int latency;
+
+	pr_debug("%s: port_id %d, copp_idx %d\n", __func__, port_id, copp_idx);
+
+	if (port_id != HDMI_RX && port_id != DISPLAY_PORT_RX) {
+		pr_err("%s: Device pp params on invalid port %d\n",
+			__func__, port_id);
+		return  -EINVAL;
+	}
+
+	for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+		if (port_id == msm_bedais[be_idx].port_id)
+			break;
+	}
+
+	if (be_idx >= MSM_BACKEND_DAI_MAX) {
+		pr_debug("%s: Invalid be id %d\n", __func__, be_idx);
+		return  -EINVAL;
+	}
+
+	for (index = 0; index < MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX; index++) {
+		if (msm_bedais_pp_params[index].port_id == port_id)
+			break;
+	}
+	if (index >= MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX) {
+		pr_err("%s: Invalid backend pp params index %d\n",
+			__func__, index);
+		return -EINVAL;
+	}
+
+	topo_id = adm_get_topology_for_port_copp_idx(port_id, copp_idx);
+	if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY) {
+		pr_err("%s: Invalid passthrough topology 0x%x\n",
+			__func__, topo_id);
+		return -EINVAL;
+	}
+
+	pp_config = msm_bedais_pp_params[index].pp_params_config;
+	if (test_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config)) {
+		pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__);
+		clear_bit(ADM_PP_PARAM_MUTE_BIT, &pp_config);
+		mute_on = msm_bedais_pp_params[index].mute_on;
+		if ((msm_bedais[be_idx].active) &&
+			(msm_bedais[be_idx].compr_passthr_mode !=
+			 LEGACY_PCM))
+			adm_send_compressed_device_mute(port_id,
+								copp_idx,
+								mute_on);
+	}
+	if (test_bit(ADM_PP_PARAM_LATENCY_BIT, &pp_config)) {
+		pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__);
+		clear_bit(ADM_PP_PARAM_LATENCY_BIT,
+			  &pp_config);
+		latency = msm_bedais_pp_params[index].latency;
+		if ((msm_bedais[be_idx].active) &&
+			(msm_bedais[be_idx].compr_passthr_mode !=
+			 LEGACY_PCM))
+			adm_send_compressed_device_latency(port_id,
+							   copp_idx,
+							   latency);
+	}
+	return 0;
+}
+
+static int msm_routing_put_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int pp_id = ucontrol->value.integer.value[0];
+	int port_id = 0;
+	int index, be_idx, i, topo_id, idx;
+	bool mute;
+	int latency;
+
+	pr_debug("%s: pp_id: 0x%x\n", __func__, pp_id);
+
+	for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+		port_id = msm_bedais[be_idx].port_id;
+		if (port_id == HDMI_RX || port_id == DISPLAY_PORT_RX)
+			break;
+	}
+
+	if (be_idx >= MSM_BACKEND_DAI_MAX) {
+		pr_debug("%s: Invalid be id %d\n", __func__, be_idx);
+		return  -EINVAL;
+	}
+
+	for (index = 0; index < MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX; index++) {
+		if (msm_bedais_pp_params[index].port_id == port_id)
+			break;
+	}
+	if (index >= MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX) {
+		pr_err("%s: Invalid pp params backend index %d\n",
+			__func__, index);
+		return -EINVAL;
+	}
+
+	for_each_set_bit(i, &msm_bedais[be_idx].fe_sessions,
+				MSM_FRONTEND_DAI_MM_SIZE) {
+		for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+			unsigned long copp =
+				session_copp_map[i]
+				[SESSION_TYPE_RX][be_idx];
+			if (!test_bit(idx, &copp))
+				continue;
+			topo_id = adm_get_topology_for_port_copp_idx(port_id,
+								     idx);
+			if (topo_id != COMPRESSED_PASSTHROUGH_DEFAULT_TOPOLOGY)
+				continue;
+		pr_debug("%s: port: 0x%x, copp %ld, be active: %d, passt: %d\n",
+			 __func__, port_id, copp, msm_bedais[be_idx].active,
+			 msm_bedais[be_idx].compr_passthr_mode);
+		switch (pp_id) {
+		case ADM_PP_PARAM_MUTE_ID:
+			pr_debug("%s: ADM_PP_PARAM_MUTE\n", __func__);
+			mute = ucontrol->value.integer.value[1] ? true : false;
+			msm_bedais_pp_params[index].mute_on = mute;
+			set_bit(ADM_PP_PARAM_MUTE_BIT,
+				&msm_bedais_pp_params[index].pp_params_config);
+			if ((msm_bedais[be_idx].active) &&
+				(msm_bedais[be_idx].compr_passthr_mode !=
+				LEGACY_PCM))
+				adm_send_compressed_device_mute(port_id,
+					idx, mute);
+			break;
+		case ADM_PP_PARAM_LATENCY_ID:
+			pr_debug("%s: ADM_PP_PARAM_LATENCY\n", __func__);
+			msm_bedais_pp_params[index].latency =
+				ucontrol->value.integer.value[1];
+			set_bit(ADM_PP_PARAM_LATENCY_BIT,
+				&msm_bedais_pp_params[index].pp_params_config);
+			latency = msm_bedais_pp_params[index].latency =
+				ucontrol->value.integer.value[1];
+			if ((msm_bedais[be_idx].active) &&
+				(msm_bedais[be_idx].compr_passthr_mode !=
+				LEGACY_PCM))
+				adm_send_compressed_device_latency(port_id,
+					idx, latency);
+			break;
+		default:
+			pr_info("%s, device pp param %d not supported\n",
+				__func__, pp_id);
+			break;
+		}
+		}
+	}
+	return 0;
+}
+
+static int msm_routing_get_device_pp_params_mixer(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	pr_debug("%s:msm_routing_get_device_pp_params_mixer", __func__);
+	return 0;
+}
+
+static const struct snd_kcontrol_new device_pp_params_mixer_controls[] = {
+	SOC_SINGLE_MULTI_EXT("Device PP Params", SND_SOC_NOPM, 0, 0xFFFFFFFF,
+	0, 3, msm_routing_get_device_pp_params_mixer,
+	msm_routing_put_device_pp_params_mixer),
+};
+
+static const struct snd_pcm_ops msm_routing_pcm_ops = {
+	.hw_params	= msm_pcm_routing_hw_params,
+	.close          = msm_pcm_routing_close,
+	.prepare        = msm_pcm_routing_prepare,
+};
+
+/* Not used but frame seems to require it */
+static int msm_routing_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_dapm_new_controls(&platform->component.dapm, msm_qdsp6_widgets,
+			   ARRAY_SIZE(msm_qdsp6_widgets));
+	snd_soc_dapm_add_routes(&platform->component.dapm, intercon,
+		ARRAY_SIZE(intercon));
+
+	snd_soc_dapm_new_widgets(platform->component.dapm.card);
+
+	snd_soc_add_platform_controls(platform, lsm_function,
+				      ARRAY_SIZE(lsm_function));
+
+	snd_soc_add_platform_controls(platform, aanc_slim_0_rx_mux,
+				      ARRAY_SIZE(aanc_slim_0_rx_mux));
+
+	snd_soc_add_platform_controls(platform, msm_voc_session_controls,
+				      ARRAY_SIZE(msm_voc_session_controls));
+
+	snd_soc_add_platform_controls(platform, app_type_cfg_controls,
+				      ARRAY_SIZE(app_type_cfg_controls));
+
+	snd_soc_add_platform_controls(platform,
+				stereo_to_custom_stereo_controls,
+			ARRAY_SIZE(stereo_to_custom_stereo_controls));
+
+	msm_qti_pp_add_controls(platform);
+
+	msm_dts_srs_tm_add_controls(platform);
+
+	msm_dolby_dap_add_controls(platform);
+
+	snd_soc_add_platform_controls(platform,
+			use_ds1_or_ds2_controls,
+			ARRAY_SIZE(use_ds1_or_ds2_controls));
+
+	snd_soc_add_platform_controls(platform,
+				device_pp_params_mixer_controls,
+				ARRAY_SIZE(device_pp_params_mixer_controls));
+
+	msm_dts_eagle_add_controls(platform);
+
+	snd_soc_add_platform_controls(platform, msm_source_tracking_controls,
+				ARRAY_SIZE(msm_source_tracking_controls));
+	snd_soc_add_platform_controls(platform, adm_channel_config_controls,
+				ARRAY_SIZE(adm_channel_config_controls));
+	return 0;
+}
+
+int msm_routing_pcm_new(struct snd_soc_pcm_runtime *runtime)
+{
+	return msm_pcm_routing_hwdep_new(runtime, msm_bedais);
+}
+
+void msm_routing_pcm_free(struct snd_pcm *pcm)
+{
+	msm_pcm_routing_hwdep_free(pcm);
+}
+
+static struct snd_soc_platform_driver msm_soc_routing_platform = {
+	.ops		= &msm_routing_pcm_ops,
+	.probe		= msm_routing_probe,
+	.pcm_new	= msm_routing_pcm_new,
+	.pcm_free	= msm_routing_pcm_free,
+};
+
+static int msm_routing_pcm_probe(struct platform_device *pdev)
+{
+
+	dev_dbg(&pdev->dev, "dev name %s\n", dev_name(&pdev->dev));
+	return snd_soc_register_platform(&pdev->dev,
+				  &msm_soc_routing_platform);
+}
+
+static int msm_routing_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_pcm_routing_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-routing"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_pcm_routing_dt_match);
+
+static struct platform_driver msm_routing_pcm_driver = {
+	.driver = {
+		.name = "msm-pcm-routing",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_pcm_routing_dt_match,
+	},
+	.probe = msm_routing_pcm_probe,
+	.remove = msm_routing_pcm_remove,
+};
+
+int msm_routing_check_backend_enabled(int fedai_id)
+{
+	int i;
+
+	if (fedai_id > MSM_FRONTEND_DAI_MM_MAX_ID) {
+		/* bad ID assigned in machine driver */
+		pr_err("%s: bad MM ID\n", __func__);
+		return 0;
+	}
+	for (i = 0; i < MSM_BACKEND_DAI_MAX; i++) {
+		if (test_bit(fedai_id, &msm_bedais[i].fe_sessions))
+			return msm_bedais[i].active;
+	}
+	return 0;
+}
+
+static int get_cal_path(int path_type)
+{
+	if (path_type == ADM_PATH_PLAYBACK ||
+	    path_type == ADM_PATH_COMPRESSED_RX)
+		return RX_DEVICE;
+	else
+		return TX_DEVICE;
+}
+
+static int msm_routing_set_cal(int32_t cal_type,
+					size_t data_size, void *data)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	ret = cal_utils_set_cal(data_size, data, cal_data, 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static void msm_routing_delete_cal_data(void)
+{
+	pr_debug("%s\n", __func__);
+
+	cal_utils_destroy_cal_types(1, &cal_data);
+}
+
+static int msm_routing_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info cal_type_info = {
+		{ADM_TOPOLOGY_CAL_TYPE,
+		{NULL, NULL, NULL,
+		msm_routing_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num}
+	};
+	pr_debug("%s\n", __func__);
+
+	ret = cal_utils_create_cal_types(1, &cal_data,
+		&cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: could not create cal type!\n",
+			__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return ret;
+err:
+	msm_routing_delete_cal_data();
+	return ret;
+}
+
+static int __init msm_soc_routing_platform_init(void)
+{
+	mutex_init(&routing_lock);
+	if (msm_routing_init_cal_data())
+		pr_err("%s: could not init cal data!\n", __func__);
+
+	return platform_driver_register(&msm_routing_pcm_driver);
+}
+module_init(msm_soc_routing_platform_init);
+
+static void __exit msm_soc_routing_platform_exit(void)
+{
+	msm_routing_delete_cal_data();
+	platform_driver_unregister(&msm_routing_pcm_driver);
+}
+module_exit(msm_soc_routing_platform_exit);
+
+MODULE_DESCRIPTION("MSM routing platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
new file mode 100644
index 0000000..d64fd64
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-routing-v2.h
@@ -0,0 +1,472 @@
+/* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_ROUTING_H
+#define _MSM_PCM_ROUTING_H
+#include <sound/apr_audio-v2.h>
+
+#define LPASS_BE_PRI_I2S_RX "PRIMARY_I2S_RX"
+#define LPASS_BE_PRI_I2S_TX "PRIMARY_I2S_TX"
+#define LPASS_BE_SLIMBUS_0_RX "SLIMBUS_0_RX"
+#define LPASS_BE_SLIMBUS_0_TX "SLIMBUS_0_TX"
+#define LPASS_BE_HDMI "HDMI"
+#define LPASS_BE_DISPLAY_PORT "DISPLAY_PORT"
+#define LPASS_BE_INT_BT_SCO_RX "INT_BT_SCO_RX"
+#define LPASS_BE_INT_BT_SCO_TX "INT_BT_SCO_TX"
+#define LPASS_BE_INT_BT_A2DP_RX "INT_BT_A2DP_RX"
+#define LPASS_BE_INT_FM_RX "INT_FM_RX"
+#define LPASS_BE_INT_FM_TX "INT_FM_TX"
+#define LPASS_BE_AFE_PCM_RX "RT_PROXY_DAI_001_RX"
+#define LPASS_BE_AFE_PCM_TX "RT_PROXY_DAI_002_TX"
+#define LPASS_BE_AUXPCM_RX "AUX_PCM_RX"
+#define LPASS_BE_AUXPCM_TX "AUX_PCM_TX"
+#define LPASS_BE_SEC_AUXPCM_RX "SEC_AUX_PCM_RX"
+#define LPASS_BE_SEC_AUXPCM_TX "SEC_AUX_PCM_TX"
+#define LPASS_BE_TERT_AUXPCM_RX "TERT_AUX_PCM_RX"
+#define LPASS_BE_TERT_AUXPCM_TX "TERT_AUX_PCM_TX"
+#define LPASS_BE_QUAT_AUXPCM_RX "QUAT_AUX_PCM_RX"
+#define LPASS_BE_QUAT_AUXPCM_TX "QUAT_AUX_PCM_TX"
+#define LPASS_BE_VOICE_PLAYBACK_TX "VOICE_PLAYBACK_TX"
+#define LPASS_BE_VOICE2_PLAYBACK_TX "VOICE2_PLAYBACK_TX"
+#define LPASS_BE_INCALL_RECORD_RX "INCALL_RECORD_RX"
+#define LPASS_BE_INCALL_RECORD_TX "INCALL_RECORD_TX"
+#define LPASS_BE_SEC_I2S_RX "SECONDARY_I2S_RX"
+#define LPASS_BE_SPDIF_RX "SPDIF_RX"
+
+#define LPASS_BE_MI2S_RX "MI2S_RX"
+#define LPASS_BE_MI2S_TX "MI2S_TX"
+#define LPASS_BE_QUAT_MI2S_RX "QUAT_MI2S_RX"
+#define LPASS_BE_QUAT_MI2S_TX "QUAT_MI2S_TX"
+#define LPASS_BE_SEC_MI2S_RX "SEC_MI2S_RX"
+#define LPASS_BE_SEC_MI2S_RX_SD1 "SEC_MI2S_RX_SD1"
+#define LPASS_BE_SEC_MI2S_TX "SEC_MI2S_TX"
+#define LPASS_BE_PRI_MI2S_RX "PRI_MI2S_RX"
+#define LPASS_BE_PRI_MI2S_TX "PRI_MI2S_TX"
+#define LPASS_BE_TERT_MI2S_RX "TERTIARY_MI2S_RX"
+#define LPASS_BE_TERT_MI2S_TX "TERTIARY_MI2S_TX"
+#define LPASS_BE_AUDIO_I2S_RX "AUDIO_I2S_RX"
+#define LPASS_BE_STUB_RX "STUB_RX"
+#define LPASS_BE_STUB_TX "STUB_TX"
+#define LPASS_BE_SLIMBUS_1_RX "SLIMBUS_1_RX"
+#define LPASS_BE_SLIMBUS_1_TX "SLIMBUS_1_TX"
+#define LPASS_BE_STUB_1_TX "STUB_1_TX"
+#define LPASS_BE_SLIMBUS_2_RX "SLIMBUS_2_RX"
+#define LPASS_BE_SLIMBUS_2_TX "SLIMBUS_2_TX"
+#define LPASS_BE_SLIMBUS_3_RX "SLIMBUS_3_RX"
+#define LPASS_BE_SLIMBUS_3_TX "SLIMBUS_3_TX"
+#define LPASS_BE_SLIMBUS_4_RX "SLIMBUS_4_RX"
+#define LPASS_BE_SLIMBUS_4_TX "SLIMBUS_4_TX"
+#define LPASS_BE_SLIMBUS_5_RX "SLIMBUS_5_RX"
+#define LPASS_BE_SLIMBUS_5_TX "SLIMBUS_5_TX"
+#define LPASS_BE_SLIMBUS_6_RX "SLIMBUS_6_RX"
+#define LPASS_BE_SLIMBUS_6_TX "SLIMBUS_6_TX"
+#define LPASS_BE_QUIN_MI2S_RX "QUIN_MI2S_RX"
+#define LPASS_BE_QUIN_MI2S_TX "QUIN_MI2S_TX"
+#define LPASS_BE_SENARY_MI2S_TX "SENARY_MI2S_TX"
+
+#define LPASS_BE_PRI_TDM_RX_0 "PRI_TDM_RX_0"
+#define LPASS_BE_PRI_TDM_TX_0 "PRI_TDM_TX_0"
+#define LPASS_BE_PRI_TDM_RX_1 "PRI_TDM_RX_1"
+#define LPASS_BE_PRI_TDM_TX_1 "PRI_TDM_TX_1"
+#define LPASS_BE_PRI_TDM_RX_2 "PRI_TDM_RX_2"
+#define LPASS_BE_PRI_TDM_TX_2 "PRI_TDM_TX_2"
+#define LPASS_BE_PRI_TDM_RX_3 "PRI_TDM_RX_3"
+#define LPASS_BE_PRI_TDM_TX_3 "PRI_TDM_TX_3"
+#define LPASS_BE_PRI_TDM_RX_4 "PRI_TDM_RX_4"
+#define LPASS_BE_PRI_TDM_TX_4 "PRI_TDM_TX_4"
+#define LPASS_BE_PRI_TDM_RX_5 "PRI_TDM_RX_5"
+#define LPASS_BE_PRI_TDM_TX_5 "PRI_TDM_TX_5"
+#define LPASS_BE_PRI_TDM_RX_6 "PRI_TDM_RX_6"
+#define LPASS_BE_PRI_TDM_TX_6 "PRI_TDM_TX_6"
+#define LPASS_BE_PRI_TDM_RX_7 "PRI_TDM_RX_7"
+#define LPASS_BE_PRI_TDM_TX_7 "PRI_TDM_TX_7"
+#define LPASS_BE_SEC_TDM_RX_0 "SEC_TDM_RX_0"
+#define LPASS_BE_SEC_TDM_TX_0 "SEC_TDM_TX_0"
+#define LPASS_BE_SEC_TDM_RX_1 "SEC_TDM_RX_1"
+#define LPASS_BE_SEC_TDM_TX_1 "SEC_TDM_TX_1"
+#define LPASS_BE_SEC_TDM_RX_2 "SEC_TDM_RX_2"
+#define LPASS_BE_SEC_TDM_TX_2 "SEC_TDM_TX_2"
+#define LPASS_BE_SEC_TDM_RX_3 "SEC_TDM_RX_3"
+#define LPASS_BE_SEC_TDM_TX_3 "SEC_TDM_TX_3"
+#define LPASS_BE_SEC_TDM_RX_4 "SEC_TDM_RX_4"
+#define LPASS_BE_SEC_TDM_TX_4 "SEC_TDM_TX_4"
+#define LPASS_BE_SEC_TDM_RX_5 "SEC_TDM_RX_5"
+#define LPASS_BE_SEC_TDM_TX_5 "SEC_TDM_TX_5"
+#define LPASS_BE_SEC_TDM_RX_6 "SEC_TDM_RX_6"
+#define LPASS_BE_SEC_TDM_TX_6 "SEC_TDM_TX_6"
+#define LPASS_BE_SEC_TDM_RX_7 "SEC_TDM_RX_7"
+#define LPASS_BE_SEC_TDM_TX_7 "SEC_TDM_TX_7"
+#define LPASS_BE_TERT_TDM_RX_0 "TERT_TDM_RX_0"
+#define LPASS_BE_TERT_TDM_TX_0 "TERT_TDM_TX_0"
+#define LPASS_BE_TERT_TDM_RX_1 "TERT_TDM_RX_1"
+#define LPASS_BE_TERT_TDM_TX_1 "TERT_TDM_TX_1"
+#define LPASS_BE_TERT_TDM_RX_2 "TERT_TDM_RX_2"
+#define LPASS_BE_TERT_TDM_TX_2 "TERT_TDM_TX_2"
+#define LPASS_BE_TERT_TDM_RX_3 "TERT_TDM_RX_3"
+#define LPASS_BE_TERT_TDM_TX_3 "TERT_TDM_TX_3"
+#define LPASS_BE_TERT_TDM_RX_4 "TERT_TDM_RX_4"
+#define LPASS_BE_TERT_TDM_TX_4 "TERT_TDM_TX_4"
+#define LPASS_BE_TERT_TDM_RX_5 "TERT_TDM_RX_5"
+#define LPASS_BE_TERT_TDM_TX_5 "TERT_TDM_TX_5"
+#define LPASS_BE_TERT_TDM_RX_6 "TERT_TDM_RX_6"
+#define LPASS_BE_TERT_TDM_TX_6 "TERT_TDM_TX_6"
+#define LPASS_BE_TERT_TDM_RX_7 "TERT_TDM_RX_7"
+#define LPASS_BE_TERT_TDM_TX_7 "TERT_TDM_TX_7"
+#define LPASS_BE_QUAT_TDM_RX_0 "QUAT_TDM_RX_0"
+#define LPASS_BE_QUAT_TDM_TX_0 "QUAT_TDM_TX_0"
+#define LPASS_BE_QUAT_TDM_RX_1 "QUAT_TDM_RX_1"
+#define LPASS_BE_QUAT_TDM_TX_1 "QUAT_TDM_TX_1"
+#define LPASS_BE_QUAT_TDM_RX_2 "QUAT_TDM_RX_2"
+#define LPASS_BE_QUAT_TDM_TX_2 "QUAT_TDM_TX_2"
+#define LPASS_BE_QUAT_TDM_RX_3 "QUAT_TDM_RX_3"
+#define LPASS_BE_QUAT_TDM_TX_3 "QUAT_TDM_TX_3"
+#define LPASS_BE_QUAT_TDM_RX_4 "QUAT_TDM_RX_4"
+#define LPASS_BE_QUAT_TDM_TX_4 "QUAT_TDM_TX_4"
+#define LPASS_BE_QUAT_TDM_RX_5 "QUAT_TDM_RX_5"
+#define LPASS_BE_QUAT_TDM_TX_5 "QUAT_TDM_TX_5"
+#define LPASS_BE_QUAT_TDM_RX_6 "QUAT_TDM_RX_6"
+#define LPASS_BE_QUAT_TDM_TX_6 "QUAT_TDM_TX_6"
+#define LPASS_BE_QUAT_TDM_RX_7 "QUAT_TDM_RX_7"
+#define LPASS_BE_QUAT_TDM_TX_7 "QUAT_TDM_TX_7"
+
+#define LPASS_BE_SLIMBUS_7_RX "SLIMBUS_7_RX"
+#define LPASS_BE_SLIMBUS_7_TX "SLIMBUS_7_TX"
+#define LPASS_BE_SLIMBUS_8_RX "SLIMBUS_8_RX"
+#define LPASS_BE_SLIMBUS_8_TX "SLIMBUS_8_TX"
+
+#define LPASS_BE_USB_AUDIO_RX "USB_AUDIO_RX"
+#define LPASS_BE_USB_AUDIO_TX "USB_AUDIO_TX"
+
+#define LPASS_BE_INT0_MI2S_RX "INT0_MI2S_RX"
+#define LPASS_BE_INT0_MI2S_TX "INT0_MI2S_TX"
+#define LPASS_BE_INT1_MI2S_RX "INT1_MI2S_RX"
+#define LPASS_BE_INT1_MI2S_TX "INT1_MI2S_TX"
+#define LPASS_BE_INT2_MI2S_RX "INT2_MI2S_RX"
+#define LPASS_BE_INT2_MI2S_TX "INT2_MI2S_TX"
+#define LPASS_BE_INT3_MI2S_RX "INT3_MI2S_RX"
+#define LPASS_BE_INT3_MI2S_TX "INT3_MI2S_TX"
+#define LPASS_BE_INT4_MI2S_RX "INT4_MI2S_RX"
+#define LPASS_BE_INT4_MI2S_TX "INT4_MI2S_TX"
+#define LPASS_BE_INT5_MI2S_RX "INT5_MI2S_RX"
+#define LPASS_BE_INT5_MI2S_TX "INT5_MI2S_TX"
+#define LPASS_BE_INT6_MI2S_RX "INT6_MI2S_RX"
+#define LPASS_BE_INT6_MI2S_TX "INT6_MI2S_TX"
+/* For multimedia front-ends, asm session is allocated dynamically.
+ * Hence, asm session/multimedia front-end mapping has to be maintained.
+ * Due to this reason, additional multimedia front-end must be placed before
+ * non-multimedia front-ends.
+ */
+
+enum {
+	MSM_FRONTEND_DAI_MULTIMEDIA1 = 0,
+	MSM_FRONTEND_DAI_MULTIMEDIA2,
+	MSM_FRONTEND_DAI_MULTIMEDIA3,
+	MSM_FRONTEND_DAI_MULTIMEDIA4,
+	MSM_FRONTEND_DAI_MULTIMEDIA5,
+	MSM_FRONTEND_DAI_MULTIMEDIA6,
+	MSM_FRONTEND_DAI_MULTIMEDIA7,
+	MSM_FRONTEND_DAI_MULTIMEDIA8,
+	MSM_FRONTEND_DAI_MULTIMEDIA9,
+	MSM_FRONTEND_DAI_MULTIMEDIA10,
+	MSM_FRONTEND_DAI_MULTIMEDIA11,
+	MSM_FRONTEND_DAI_MULTIMEDIA12,
+	MSM_FRONTEND_DAI_MULTIMEDIA13,
+	MSM_FRONTEND_DAI_MULTIMEDIA14,
+	MSM_FRONTEND_DAI_MULTIMEDIA15,
+	MSM_FRONTEND_DAI_MULTIMEDIA16,
+	MSM_FRONTEND_DAI_MULTIMEDIA17,
+	MSM_FRONTEND_DAI_MULTIMEDIA18,
+	MSM_FRONTEND_DAI_MULTIMEDIA19,
+	MSM_FRONTEND_DAI_CS_VOICE,
+	MSM_FRONTEND_DAI_VOIP,
+	MSM_FRONTEND_DAI_AFE_RX,
+	MSM_FRONTEND_DAI_AFE_TX,
+	MSM_FRONTEND_DAI_VOICE_STUB,
+	MSM_FRONTEND_DAI_VOLTE,
+	MSM_FRONTEND_DAI_DTMF_RX,
+	MSM_FRONTEND_DAI_VOICE2,
+	MSM_FRONTEND_DAI_QCHAT,
+	MSM_FRONTEND_DAI_VOLTE_STUB,
+	MSM_FRONTEND_DAI_LSM1,
+	MSM_FRONTEND_DAI_LSM2,
+	MSM_FRONTEND_DAI_LSM3,
+	MSM_FRONTEND_DAI_LSM4,
+	MSM_FRONTEND_DAI_LSM5,
+	MSM_FRONTEND_DAI_LSM6,
+	MSM_FRONTEND_DAI_LSM7,
+	MSM_FRONTEND_DAI_LSM8,
+	MSM_FRONTEND_DAI_VOICE2_STUB,
+	MSM_FRONTEND_DAI_VOWLAN,
+	MSM_FRONTEND_DAI_VOICEMMODE1,
+	MSM_FRONTEND_DAI_VOICEMMODE2,
+	MSM_FRONTEND_DAI_MAX,
+};
+
+#define MSM_FRONTEND_DAI_MM_SIZE (MSM_FRONTEND_DAI_MULTIMEDIA19 + 1)
+#define MSM_FRONTEND_DAI_MM_MAX_ID MSM_FRONTEND_DAI_MULTIMEDIA19
+
+enum {
+	MSM_BACKEND_DAI_PRI_I2S_RX = 0,
+	MSM_BACKEND_DAI_PRI_I2S_TX,
+	MSM_BACKEND_DAI_SLIMBUS_0_RX,
+	MSM_BACKEND_DAI_SLIMBUS_0_TX,
+	MSM_BACKEND_DAI_HDMI_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_RX,
+	MSM_BACKEND_DAI_INT_BT_SCO_TX,
+	MSM_BACKEND_DAI_INT_FM_RX,
+	MSM_BACKEND_DAI_INT_FM_TX,
+	MSM_BACKEND_DAI_AFE_PCM_RX,
+	MSM_BACKEND_DAI_AFE_PCM_TX,
+	MSM_BACKEND_DAI_AUXPCM_RX,
+	MSM_BACKEND_DAI_AUXPCM_TX,
+	MSM_BACKEND_DAI_VOICE_PLAYBACK_TX,
+	MSM_BACKEND_DAI_VOICE2_PLAYBACK_TX,
+	MSM_BACKEND_DAI_INCALL_RECORD_RX,
+	MSM_BACKEND_DAI_INCALL_RECORD_TX,
+	MSM_BACKEND_DAI_MI2S_RX,
+	MSM_BACKEND_DAI_MI2S_TX,
+	MSM_BACKEND_DAI_SEC_I2S_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_RX,
+	MSM_BACKEND_DAI_SLIMBUS_1_TX,
+	MSM_BACKEND_DAI_SLIMBUS_2_RX,
+	MSM_BACKEND_DAI_SLIMBUS_2_TX,
+	MSM_BACKEND_DAI_SLIMBUS_3_RX,
+	MSM_BACKEND_DAI_SLIMBUS_3_TX,
+	MSM_BACKEND_DAI_SLIMBUS_4_RX,
+	MSM_BACKEND_DAI_SLIMBUS_4_TX,
+	MSM_BACKEND_DAI_SLIMBUS_5_RX,
+	MSM_BACKEND_DAI_SLIMBUS_5_TX,
+	MSM_BACKEND_DAI_SLIMBUS_6_RX,
+	MSM_BACKEND_DAI_SLIMBUS_6_TX,
+	MSM_BACKEND_DAI_SLIMBUS_7_RX,
+	MSM_BACKEND_DAI_SLIMBUS_7_TX,
+	MSM_BACKEND_DAI_SLIMBUS_8_RX,
+	MSM_BACKEND_DAI_SLIMBUS_8_TX,
+	MSM_BACKEND_DAI_EXTPROC_RX,
+	MSM_BACKEND_DAI_EXTPROC_TX,
+	MSM_BACKEND_DAI_EXTPROC_EC_TX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_RX,
+	MSM_BACKEND_DAI_QUATERNARY_MI2S_TX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_TX,
+	MSM_BACKEND_DAI_PRI_MI2S_RX,
+	MSM_BACKEND_DAI_PRI_MI2S_TX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_RX,
+	MSM_BACKEND_DAI_TERTIARY_MI2S_TX,
+	MSM_BACKEND_DAI_AUDIO_I2S_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_RX,
+	MSM_BACKEND_DAI_SEC_AUXPCM_TX,
+	MSM_BACKEND_DAI_SPDIF_RX,
+	MSM_BACKEND_DAI_SECONDARY_MI2S_RX_SD1,
+	MSM_BACKEND_DAI_QUINARY_MI2S_RX,
+	MSM_BACKEND_DAI_QUINARY_MI2S_TX,
+	MSM_BACKEND_DAI_SENARY_MI2S_TX,
+	MSM_BACKEND_DAI_PRI_TDM_RX_0,
+	MSM_BACKEND_DAI_PRI_TDM_TX_0,
+	MSM_BACKEND_DAI_PRI_TDM_RX_1,
+	MSM_BACKEND_DAI_PRI_TDM_TX_1,
+	MSM_BACKEND_DAI_PRI_TDM_RX_2,
+	MSM_BACKEND_DAI_PRI_TDM_TX_2,
+	MSM_BACKEND_DAI_PRI_TDM_RX_3,
+	MSM_BACKEND_DAI_PRI_TDM_TX_3,
+	MSM_BACKEND_DAI_PRI_TDM_RX_4,
+	MSM_BACKEND_DAI_PRI_TDM_TX_4,
+	MSM_BACKEND_DAI_PRI_TDM_RX_5,
+	MSM_BACKEND_DAI_PRI_TDM_TX_5,
+	MSM_BACKEND_DAI_PRI_TDM_RX_6,
+	MSM_BACKEND_DAI_PRI_TDM_TX_6,
+	MSM_BACKEND_DAI_PRI_TDM_RX_7,
+	MSM_BACKEND_DAI_PRI_TDM_TX_7,
+	MSM_BACKEND_DAI_SEC_TDM_RX_0,
+	MSM_BACKEND_DAI_SEC_TDM_TX_0,
+	MSM_BACKEND_DAI_SEC_TDM_RX_1,
+	MSM_BACKEND_DAI_SEC_TDM_TX_1,
+	MSM_BACKEND_DAI_SEC_TDM_RX_2,
+	MSM_BACKEND_DAI_SEC_TDM_TX_2,
+	MSM_BACKEND_DAI_SEC_TDM_RX_3,
+	MSM_BACKEND_DAI_SEC_TDM_TX_3,
+	MSM_BACKEND_DAI_SEC_TDM_RX_4,
+	MSM_BACKEND_DAI_SEC_TDM_TX_4,
+	MSM_BACKEND_DAI_SEC_TDM_RX_5,
+	MSM_BACKEND_DAI_SEC_TDM_TX_5,
+	MSM_BACKEND_DAI_SEC_TDM_RX_6,
+	MSM_BACKEND_DAI_SEC_TDM_TX_6,
+	MSM_BACKEND_DAI_SEC_TDM_RX_7,
+	MSM_BACKEND_DAI_SEC_TDM_TX_7,
+	MSM_BACKEND_DAI_TERT_TDM_RX_0,
+	MSM_BACKEND_DAI_TERT_TDM_TX_0,
+	MSM_BACKEND_DAI_TERT_TDM_RX_1,
+	MSM_BACKEND_DAI_TERT_TDM_TX_1,
+	MSM_BACKEND_DAI_TERT_TDM_RX_2,
+	MSM_BACKEND_DAI_TERT_TDM_TX_2,
+	MSM_BACKEND_DAI_TERT_TDM_RX_3,
+	MSM_BACKEND_DAI_TERT_TDM_TX_3,
+	MSM_BACKEND_DAI_TERT_TDM_RX_4,
+	MSM_BACKEND_DAI_TERT_TDM_TX_4,
+	MSM_BACKEND_DAI_TERT_TDM_RX_5,
+	MSM_BACKEND_DAI_TERT_TDM_TX_5,
+	MSM_BACKEND_DAI_TERT_TDM_RX_6,
+	MSM_BACKEND_DAI_TERT_TDM_TX_6,
+	MSM_BACKEND_DAI_TERT_TDM_RX_7,
+	MSM_BACKEND_DAI_TERT_TDM_TX_7,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_0,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_0,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_1,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_1,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_2,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_2,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_3,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_3,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_4,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_4,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_5,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_5,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_6,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_6,
+	MSM_BACKEND_DAI_QUAT_TDM_RX_7,
+	MSM_BACKEND_DAI_QUAT_TDM_TX_7,
+	MSM_BACKEND_DAI_INT_BT_A2DP_RX,
+	MSM_BACKEND_DAI_USB_RX,
+	MSM_BACKEND_DAI_USB_TX,
+	MSM_BACKEND_DAI_DISPLAY_PORT_RX,
+	MSM_BACKEND_DAI_TERT_AUXPCM_RX,
+	MSM_BACKEND_DAI_TERT_AUXPCM_TX,
+	MSM_BACKEND_DAI_QUAT_AUXPCM_RX,
+	MSM_BACKEND_DAI_QUAT_AUXPCM_TX,
+	MSM_BACKEND_DAI_INT0_MI2S_RX,
+	MSM_BACKEND_DAI_INT0_MI2S_TX,
+	MSM_BACKEND_DAI_INT1_MI2S_RX,
+	MSM_BACKEND_DAI_INT1_MI2S_TX,
+	MSM_BACKEND_DAI_INT2_MI2S_RX,
+	MSM_BACKEND_DAI_INT2_MI2S_TX,
+	MSM_BACKEND_DAI_INT3_MI2S_RX,
+	MSM_BACKEND_DAI_INT3_MI2S_TX,
+	MSM_BACKEND_DAI_INT4_MI2S_RX,
+	MSM_BACKEND_DAI_INT4_MI2S_TX,
+	MSM_BACKEND_DAI_INT5_MI2S_RX,
+	MSM_BACKEND_DAI_INT5_MI2S_TX,
+	MSM_BACKEND_DAI_INT6_MI2S_RX,
+	MSM_BACKEND_DAI_INT6_MI2S_TX,
+	MSM_BACKEND_DAI_MAX,
+};
+
+enum msm_pcm_routing_event {
+	MSM_PCM_RT_EVT_BUF_RECFG,
+	MSM_PCM_RT_EVT_DEVSWITCH,
+	MSM_PCM_RT_EVT_MAX,
+};
+
+enum {
+	EXT_EC_REF_NONE = 0,
+	EXT_EC_REF_PRI_MI2S_TX,
+	EXT_EC_REF_SEC_MI2S_TX,
+	EXT_EC_REF_TERT_MI2S_TX,
+	EXT_EC_REF_QUAT_MI2S_TX,
+	EXT_EC_REF_QUIN_MI2S_TX,
+	EXT_EC_REF_SLIM_1_TX,
+};
+
+#define INVALID_SESSION -1
+#define SESSION_TYPE_RX 0
+#define SESSION_TYPE_TX 1
+#define INT_RX_VOL_MAX_STEPS 0x2000
+#define INT_RX_VOL_GAIN 0x2000
+
+#define RELEASE_LOCK	0
+#define ACQUIRE_LOCK	1
+
+#define MSM_BACKEND_DAI_PP_PARAMS_REQ_MAX	2
+#define HDMI_RX_ID				0x8001
+#define ADM_PP_PARAM_MUTE_ID			0
+#define ADM_PP_PARAM_MUTE_BIT			1
+#define ADM_PP_PARAM_LATENCY_ID			1
+#define ADM_PP_PARAM_LATENCY_BIT		2
+#define BE_DAI_PORT_SESSIONS_IDX_MAX		4
+
+struct msm_pcm_routing_evt {
+	void (*event_func)(enum msm_pcm_routing_event, void *);
+	void *priv_data;
+};
+
+struct msm_pcm_routing_bdai_data {
+	u16 port_id; /* AFE port ID */
+	u8 active; /* track if this backend is enabled */
+	unsigned long fe_sessions; /* Front-end sessions */
+	/*
+	 * Track Tx BE ports -> Rx BE ports.
+	 * port_sessions[0] used to track BE 0 to BE 63.
+	 * port_sessions[1] used to track BE 64 to BE 127.
+	 * port_sessions[2] used to track BE 128 to BE 191.
+	 * port_sessions[3] used to track BE 192 to BE 255.
+	 */
+	u64 port_sessions[BE_DAI_PORT_SESSIONS_IDX_MAX];
+
+	unsigned int  sample_rate;
+	unsigned int  channel;
+	unsigned int  format;
+	unsigned int  adm_override_ch;
+	u32 compr_passthr_mode;
+	char *name;
+};
+
+struct msm_pcm_routing_fdai_data {
+	u16 be_srate; /* track prior backend sample rate for flushing purpose */
+	int strm_id; /* ASM stream ID */
+	int perf_mode;
+	struct msm_pcm_routing_evt event_info;
+};
+
+#define MAX_APP_TYPES	16
+struct msm_pcm_routing_app_type_data {
+	int app_type;
+	u32 sample_rate;
+	int bit_width;
+};
+
+struct msm_pcm_stream_app_type_cfg {
+	int app_type;
+	int acdb_dev_id;
+	int sample_rate;
+};
+
+/* dai_id: front-end ID,
+ * dspst_id:  DSP audio stream ID
+ * stream_type: playback or capture
+ */
+int msm_pcm_routing_reg_phy_stream(int fedai_id, int perf_mode, int dspst_id,
+				   int stream_type);
+void msm_pcm_routing_reg_psthr_stream(int fedai_id, int dspst_id,
+		int stream_type);
+int msm_pcm_routing_reg_phy_compr_stream(int fedai_id, int perf_mode,
+					  int dspst_id, int stream_type,
+					  uint32_t compr_passthr);
+
+int msm_pcm_routing_reg_phy_stream_v2(int fedai_id, int perf_mode,
+				      int dspst_id, int stream_type,
+				      struct msm_pcm_routing_evt event_info);
+
+void msm_pcm_routing_dereg_phy_stream(int fedai_id, int stream_type);
+
+int msm_routing_check_backend_enabled(int fedai_id);
+
+
+void msm_pcm_routing_get_bedai_info(int be_idx,
+				    struct msm_pcm_routing_bdai_data *bedai);
+void msm_pcm_routing_get_fedai_info(int fe_idx, int sess_type,
+				    struct msm_pcm_routing_fdai_data *fe_dai);
+void msm_pcm_routing_acquire_lock(void);
+void msm_pcm_routing_release_lock(void);
+
+void msm_pcm_routing_reg_stream_app_type_cfg(int fedai_id, int app_type,
+			int acdb_dev_id, int sample_rate, int session_type);
+int msm_pcm_routing_get_stream_app_type_cfg(int fedai_id, int session_type,
+			int *app_type, int *acdb_dev_id, int *sample_rate);
+#endif /*_MSM_PCM_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
new file mode 100644
index 0000000..fa71eea
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.c
@@ -0,0 +1,752 @@
+/* Copyright (c) 2012-2015, 2017 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+#include <linux/of_device.h>
+
+#include "msm-pcm-voice-v2.h"
+#include "q6voice.h"
+
+static struct msm_voice voice_info[VOICE_SESSION_INDEX_MAX];
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+
+	.info =                 (SNDRV_PCM_INFO_INTERLEAVED |
+				SNDRV_PCM_INFO_PAUSE |
+				SNDRV_PCM_INFO_RESUME),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE,
+	.rates =                SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000,
+	.rate_min =             8000,
+	.rate_max =             16000,
+	.channels_min =         1,
+	.channels_max =         1,
+
+	.buffer_bytes_max =     4096 * 2,
+	.period_bytes_min =     2048,
+	.period_bytes_max =     4096,
+	.periods_min =          2,
+	.periods_max =          4,
+
+	.fifo_size =            0,
+};
+static bool is_volte(struct msm_voice *pvolte)
+{
+	if (pvolte == &voice_info[VOLTE_SESSION_INDEX])
+		return true;
+	else
+		return false;
+}
+
+static bool is_voice2(struct msm_voice *pvoice2)
+{
+	if (pvoice2 == &voice_info[VOICE2_SESSION_INDEX])
+		return true;
+	else
+		return false;
+}
+
+static bool is_qchat(struct msm_voice *pqchat)
+{
+	if (pqchat == &voice_info[QCHAT_SESSION_INDEX])
+		return true;
+	else
+		return false;
+}
+
+static bool is_vowlan(struct msm_voice *pvowlan)
+{
+	if (pvowlan == &voice_info[VOWLAN_SESSION_INDEX])
+		return true;
+	else
+		return false;
+}
+
+static bool is_voicemmode1(struct msm_voice *pvoicemmode1)
+{
+	if (pvoicemmode1 == &voice_info[VOICEMMODE1_INDEX])
+		return true;
+	else
+		return false;
+}
+
+static bool is_voicemmode2(struct msm_voice *pvoicemmode2)
+{
+	if (pvoicemmode2 == &voice_info[VOICEMMODE2_INDEX])
+		return true;
+	else
+		return false;
+}
+
+static uint32_t get_session_id(struct msm_voice *pvoc)
+{
+	uint32_t session_id = 0;
+
+	if (is_volte(pvoc))
+		session_id = voc_get_session_id(VOLTE_SESSION_NAME);
+	else if (is_voice2(pvoc))
+		session_id = voc_get_session_id(VOICE2_SESSION_NAME);
+	else if (is_qchat(pvoc))
+		session_id = voc_get_session_id(QCHAT_SESSION_NAME);
+	else if (is_vowlan(pvoc))
+		session_id = voc_get_session_id(VOWLAN_SESSION_NAME);
+	else if (is_voicemmode1(pvoc))
+		session_id = voc_get_session_id(VOICEMMODE1_NAME);
+	else if (is_voicemmode2(pvoc))
+		session_id = voc_get_session_id(VOICEMMODE2_NAME);
+	else
+		session_id = voc_get_session_id(VOICE_SESSION_NAME);
+
+	return session_id;
+}
+
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+
+	pr_debug("%s\n", __func__);
+
+	if (!prtd->playback_start)
+		prtd->playback_start = 1;
+
+	return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+
+	pr_debug("%s\n", __func__);
+
+	if (!prtd->capture_start)
+		prtd->capture_start = 1;
+
+	return 0;
+}
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *voice;
+
+	if (!strcmp("VoLTE", substream->pcm->id)) {
+		voice = &voice_info[VOLTE_SESSION_INDEX];
+		pr_debug("%s: Open VoLTE Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	} else if (!strcmp("Voice2", substream->pcm->id)) {
+		voice = &voice_info[VOICE2_SESSION_INDEX];
+		pr_debug("%s: Open Voice2 Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	} else if (!strcmp("QCHAT", substream->pcm->id)) {
+		voice = &voice_info[QCHAT_SESSION_INDEX];
+		pr_debug("%s: Open QCHAT Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	} else if (!strcmp("VoWLAN", substream->pcm->id)) {
+		voice = &voice_info[VOWLAN_SESSION_INDEX];
+		pr_debug("%s: Open VoWLAN Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	} else if (!strcmp("VoiceMMode1", substream->pcm->id)) {
+		voice = &voice_info[VOICEMMODE1_INDEX];
+		pr_debug("%s: Open VoiceMMode1 Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	} else if (!strcmp("VoiceMMode2", substream->pcm->id)) {
+		voice = &voice_info[VOICEMMODE2_INDEX];
+		pr_debug("%s: Open VoiceMMode2 Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	} else {
+		voice = &voice_info[VOICE_SESSION_INDEX];
+		pr_debug("%s: Open VOICE Substream Id=%s\n",
+			 __func__, substream->pcm->id);
+	}
+	mutex_lock(&voice->lock);
+
+	runtime->hw = msm_pcm_hardware;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		voice->playback_substream = substream;
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		voice->capture_substream = substream;
+
+	voice->instance++;
+	pr_debug("%s: Instance = %d, Stream ID = %s\n",
+			__func__, voice->instance, substream->pcm->id);
+	runtime->private_data = voice;
+
+	mutex_unlock(&voice->lock);
+
+	return 0;
+}
+static int msm_pcm_playback_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+
+	pr_debug("%s\n", __func__);
+
+	if (prtd->playback_start)
+		prtd->playback_start = 0;
+
+	prtd->playback_substream = NULL;
+
+	return 0;
+}
+static int msm_pcm_capture_close(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+
+	pr_debug("%s\n", __func__);
+
+	if (prtd->capture_start)
+		prtd->capture_start = 0;
+	prtd->capture_substream = NULL;
+
+	return 0;
+}
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+	uint32_t session_id = 0;
+	int ret = 0;
+
+	mutex_lock(&prtd->lock);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_close(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_close(substream);
+
+	prtd->instance--;
+	if (!prtd->playback_start && !prtd->capture_start) {
+		pr_debug("end voice call\n");
+
+		session_id = get_session_id(prtd);
+		if (session_id)
+			voc_end_voice_call(session_id);
+	}
+	mutex_unlock(&prtd->lock);
+
+	return ret;
+}
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+	uint32_t session_id = 0;
+
+	mutex_lock(&prtd->lock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_prepare(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_prepare(substream);
+
+	if (prtd->playback_start && prtd->capture_start) {
+		session_id = get_session_id(prtd);
+		if (session_id)
+			voc_start_voice_call(session_id);
+	}
+	mutex_unlock(&prtd->lock);
+
+	return ret;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+
+	pr_debug("%s: Voice\n", __func__);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+	uint32_t session_id = 0;
+
+	pr_debug("%s: cmd = %d\n", __func__, cmd);
+
+	session_id = get_session_id(prtd);
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("Start & Stop Voice call not handled in Trigger.\n");
+	break;
+	case SNDRV_PCM_TRIGGER_RESUME:
+	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+		pr_debug("%s: resume call session_id = %d\n", __func__,
+			 session_id);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			ret = msm_pcm_playback_prepare(substream);
+		else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			ret = msm_pcm_capture_prepare(substream);
+		if (prtd->playback_start && prtd->capture_start) {
+			if (session_id)
+				voc_resume_voice_call(session_id);
+		}
+	break;
+	case SNDRV_PCM_TRIGGER_SUSPEND:
+	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+		pr_debug("%s: pause call session_id=%d\n",
+			 __func__, session_id);
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+			if (prtd->playback_start)
+				prtd->playback_start = 0;
+		} else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+			if (prtd->capture_start)
+				prtd->capture_start = 0;
+		}
+		if (session_id)
+			voc_standby_voice_call(session_id);
+		break;
+	default:
+		ret = -EINVAL;
+	break;
+	}
+	return ret;
+}
+
+static int msm_pcm_ioctl(struct snd_pcm_substream *substream,
+			 unsigned int cmd, void *arg)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct msm_voice *prtd = runtime->private_data;
+	uint32_t session_id = get_session_id(prtd);
+	enum voice_lch_mode lch_mode;
+	int ret = 0;
+
+	switch (cmd) {
+	case SNDRV_VOICE_IOCTL_LCH:
+		if (copy_from_user(&lch_mode, (void *)arg,
+				   sizeof(enum voice_lch_mode))) {
+			pr_err("%s: Copy from user failed, size %zd\n",
+				__func__, sizeof(enum voice_lch_mode));
+
+			ret = -EFAULT;
+			break;
+		}
+
+		pr_debug("%s: %s lch_mode:%d\n",
+			 __func__, substream->pcm->id, lch_mode);
+
+		switch (lch_mode) {
+		case VOICE_LCH_START:
+		case VOICE_LCH_STOP:
+			ret = voc_set_lch(session_id, lch_mode);
+			break;
+
+		default:
+			pr_err("%s: Invalid LCH MODE %d\n", __func__, lch_mode);
+
+			ret = -EFAULT;
+		}
+
+		break;
+	default:
+		pr_debug("%s: Falling into default snd_lib_ioctl cmd 0x%x\n",
+			 __func__, cmd);
+
+		ret = snd_pcm_lib_ioctl(substream, cmd, arg);
+		break;
+	}
+
+	if (!ret)
+		pr_debug("%s: ret %d\n", __func__, ret);
+	else
+		pr_err("%s: cmd 0x%x failed %d\n", __func__, cmd, ret);
+
+	return ret;
+}
+
+static int msm_voice_gain_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int volume = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+	int ramp_duration = ucontrol->value.integer.value[2];
+
+	if ((volume < 0) || (ramp_duration < 0)
+		|| (ramp_duration > MAX_RAMP_DURATION)) {
+		pr_err(" %s Invalid arguments", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: volume: %d session_id: %#x ramp_duration: %d\n", __func__,
+		volume, session_id, ramp_duration);
+
+	voc_set_rx_vol_step(session_id, RX_PATH, volume, ramp_duration);
+
+done:
+	return ret;
+}
+
+static int msm_voice_mute_put(struct snd_kcontrol *kcontrol,
+			      struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int mute = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+	int ramp_duration = ucontrol->value.integer.value[2];
+
+	if ((mute < 0) || (mute > 1) || (ramp_duration < 0)
+		|| (ramp_duration > MAX_RAMP_DURATION)) {
+		pr_err(" %s Invalid arguments", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+		mute, session_id, ramp_duration);
+
+	ret = voc_set_tx_mute(session_id, TX_PATH, mute, ramp_duration);
+
+done:
+	return ret;
+}
+
+static int msm_voice_tx_device_mute_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int mute = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+	int ramp_duration = ucontrol->value.integer.value[2];
+
+	if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+	    (ramp_duration > MAX_RAMP_DURATION)) {
+		pr_err(" %s Invalid arguments", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+		 mute, session_id, ramp_duration);
+
+	ret = voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_TX,
+				  mute, ramp_duration);
+
+done:
+	return ret;
+}
+
+static int msm_voice_rx_device_mute_put(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int mute = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+	int ramp_duration = ucontrol->value.integer.value[2];
+
+	if ((mute < 0) || (mute > 1) || (ramp_duration < 0) ||
+	    (ramp_duration > MAX_RAMP_DURATION)) {
+		pr_err(" %s Invalid arguments", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: mute=%d session_id=%#x ramp_duration=%d\n", __func__,
+		 mute, session_id, ramp_duration);
+
+	voc_set_device_mute(session_id, VSS_IVOLUME_DIRECTION_RX,
+			    mute, ramp_duration);
+
+done:
+	return ret;
+}
+
+
+
+static const char * const tty_mode[] = {"OFF", "HCO", "VCO", "FULL"};
+static const struct soc_enum msm_tty_mode_enum[] = {
+		SOC_ENUM_SINGLE_EXT(4, tty_mode),
+};
+
+static int msm_voice_tty_mode_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] =
+		voc_get_tty_mode(voc_get_session_id(VOICE_SESSION_NAME));
+	return 0;
+}
+
+static int msm_voice_tty_mode_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	int tty_mode = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: tty_mode=%d\n", __func__, tty_mode);
+
+	voc_set_tty_mode(voc_get_session_id(VOICE_SESSION_NAME), tty_mode);
+	voc_set_tty_mode(voc_get_session_id(VOICE2_SESSION_NAME), tty_mode);
+	voc_set_tty_mode(voc_get_session_id(VOLTE_SESSION_NAME), tty_mode);
+	voc_set_tty_mode(voc_get_session_id(VOWLAN_SESSION_NAME), tty_mode);
+	voc_set_tty_mode(voc_get_session_id(VOICEMMODE1_NAME), tty_mode);
+	voc_set_tty_mode(voc_get_session_id(VOICEMMODE2_NAME), tty_mode);
+
+	return 0;
+}
+
+static int msm_voice_slowtalk_put(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	int st_enable = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+
+	pr_debug("%s: st enable=%d session_id=%#x\n", __func__, st_enable,
+		 session_id);
+
+	voc_set_pp_enable(session_id,
+			  MODULE_ID_VOICE_MODULE_ST, st_enable);
+
+	return 0;
+}
+
+static int msm_voice_hd_voice_put(struct snd_kcontrol *kcontrol,
+				  struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	uint32_t hd_enable = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+
+	pr_debug("%s: HD Voice enable=%d session_id=%#x\n", __func__, hd_enable,
+		 session_id);
+
+	ret = voc_set_hd_enable(session_id, hd_enable);
+
+	return ret;
+}
+
+static int msm_voice_topology_disable_put(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int disable = ucontrol->value.integer.value[0];
+	uint32_t session_id = ucontrol->value.integer.value[1];
+
+	if ((disable < 0) || (disable > 1)) {
+		pr_err(" %s Invalid arguments: %d\n", __func__, disable);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	pr_debug("%s: disable = %d, session_id = %d\n", __func__, disable,
+		 session_id);
+
+	ret = voc_disable_topology(session_id, disable);
+
+done:
+	return ret;
+}
+
+static int msm_voice_cvd_version_info(struct snd_kcontrol *kcontrol,
+				      struct snd_ctl_elem_info *uinfo)
+{
+	int ret = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+	uinfo->count = CVD_VERSION_STRING_MAX_SIZE;
+
+	return ret;
+}
+
+static int msm_voice_cvd_version_get(struct snd_kcontrol *kcontrol,
+				     struct snd_ctl_elem_value *ucontrol)
+{
+	char cvd_version[CVD_VERSION_STRING_MAX_SIZE] = CVD_VERSION_DEFAULT;
+	int ret;
+
+	pr_debug("%s:\n", __func__);
+
+	ret = voc_get_cvd_version(cvd_version);
+
+	if (ret)
+		pr_err("%s: Error retrieving CVD version, error:%d\n",
+			__func__, ret);
+
+	memcpy(ucontrol->value.bytes.data, cvd_version, sizeof(cvd_version));
+
+	return 0;
+}
+static struct snd_kcontrol_new msm_voice_controls[] = {
+	SOC_SINGLE_MULTI_EXT("Voice Rx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
+				0, 3, NULL, msm_voice_rx_device_mute_put),
+	SOC_SINGLE_MULTI_EXT("Voice Tx Device Mute", SND_SOC_NOPM, 0, VSID_MAX,
+				0, 3, NULL, msm_voice_tx_device_mute_put),
+	SOC_SINGLE_MULTI_EXT("Voice Tx Mute", SND_SOC_NOPM, 0, VSID_MAX,
+				0, 3, NULL, msm_voice_mute_put),
+	SOC_SINGLE_MULTI_EXT("Voice Rx Gain", SND_SOC_NOPM, 0, VSID_MAX, 0, 3,
+				NULL, msm_voice_gain_put),
+	SOC_ENUM_EXT("TTY Mode", msm_tty_mode_enum[0], msm_voice_tty_mode_get,
+				msm_voice_tty_mode_put),
+	SOC_SINGLE_MULTI_EXT("Slowtalk Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+				NULL, msm_voice_slowtalk_put),
+	SOC_SINGLE_MULTI_EXT("Voice Topology Disable", SND_SOC_NOPM, 0,
+			     VSID_MAX, 0, 2, NULL,
+			     msm_voice_topology_disable_put),
+	SOC_SINGLE_MULTI_EXT("HD Voice Enable", SND_SOC_NOPM, 0, VSID_MAX, 0, 2,
+			     NULL, msm_voice_hd_voice_put),
+	{
+		.access = SNDRV_CTL_ELEM_ACCESS_READ,
+		.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+		.name	= "CVD Version",
+		.info	= msm_voice_cvd_version_info,
+		.get	= msm_voice_cvd_version_get,
+	},
+};
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open			= msm_pcm_open,
+	.hw_params		= msm_pcm_hw_params,
+	.close			= msm_pcm_close,
+	.prepare		= msm_pcm_prepare,
+	.trigger		= msm_pcm_trigger,
+	.ioctl			= msm_pcm_ioctl,
+	.compat_ioctl		= msm_pcm_ioctl,
+};
+
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	return ret;
+}
+
+static int msm_pcm_voice_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, msm_voice_controls,
+					ARRAY_SIZE(msm_voice_controls));
+
+	return 0;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.probe		= msm_pcm_voice_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+	int rc;
+	bool destroy_cvd = false;
+	const char *is_destroy_cvd = "qcom,destroy-cvd";
+
+	if (!is_voc_initialized()) {
+		pr_debug("%s: voice module not initialized yet, deferring probe()\n",
+		       __func__);
+
+		rc = -EPROBE_DEFER;
+		goto done;
+	}
+
+	rc = voc_alloc_cal_shared_memory();
+	if (rc == -EPROBE_DEFER) {
+		pr_debug("%s: memory allocation for calibration deferred %d\n",
+			 __func__, rc);
+
+		goto done;
+	} else if (rc < 0) {
+		pr_err("%s: memory allocation for calibration failed %d\n",
+		       __func__, rc);
+	}
+
+	pr_debug("%s: dev name %s\n",
+			__func__, dev_name(&pdev->dev));
+	destroy_cvd = of_property_read_bool(pdev->dev.of_node,
+						is_destroy_cvd);
+	voc_set_destroy_cvd_flag(destroy_cvd);
+
+	rc = snd_soc_register_platform(&pdev->dev,
+				       &msm_soc_platform);
+
+done:
+	return rc;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_voice_dt_match[] = {
+	{.compatible = "qcom,msm-pcm-voice"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_voice_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-pcm-voice",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_voice_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	int i = 0;
+
+	memset(&voice_info, 0, sizeof(voice_info));
+
+	for (i = 0; i < VOICE_SESSION_INDEX_MAX; i++)
+		mutex_init(&voice_info[i].lock);
+
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("Voice PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
new file mode 100644
index 0000000..e00cebc
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voice-v2.h
@@ -0,0 +1,42 @@
+/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef _MSM_PCM_VOICE_H
+#define _MSM_PCM_VOICE_H
+#include <sound/apr_audio-v2.h>
+
+enum {
+	VOICE_SESSION_INDEX,
+	VOLTE_SESSION_INDEX,
+	VOICE2_SESSION_INDEX,
+	QCHAT_SESSION_INDEX,
+	VOWLAN_SESSION_INDEX,
+	VOICEMMODE1_INDEX,
+	VOICEMMODE2_INDEX,
+	VOICE_SESSION_INDEX_MAX,
+};
+
+struct msm_voice {
+	struct snd_pcm_substream *playback_substream;
+	struct snd_pcm_substream *capture_substream;
+
+	int instance;
+
+	struct mutex lock;
+
+	uint32_t samp_rate;
+	uint32_t channel_mode;
+
+	int playback_start;
+	int capture_start;
+};
+
+#endif /*_MSM_PCM_VOICE_H*/
diff --git a/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
new file mode 100644
index 0000000..a82d1cb
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-pcm-voip-v2.c
@@ -0,0 +1,1705 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/of_device.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <asm/dma.h>
+
+#include "msm-pcm-q6-v2.h"
+#include "msm-pcm-routing-v2.h"
+#include "q6voice.h"
+
+#define SHARED_MEM_BUF 2
+#define VOIP_MAX_Q_LEN 10
+#define VOIP_MAX_VOC_PKT_SIZE 4096
+#define VOIP_MIN_VOC_PKT_SIZE 320
+
+/* Length of the DSP frame info header added to the voc packet. */
+#define DSP_FRAME_HDR_LEN 1
+
+#define MODE_IS127		0x2
+#define MODE_4GV_NB		0x3
+#define MODE_4GV_WB		0x4
+#define MODE_AMR		0x5
+#define MODE_AMR_WB		0xD
+#define MODE_PCM		0xC
+#define MODE_4GV_NW		0xE
+#define MODE_G711		0xA
+#define MODE_G711A		0xF
+
+enum msm_audio_g711a_frame_type {
+	MVS_G711A_SPEECH_GOOD,
+	MVS_G711A_SID,
+	MVS_G711A_NO_DATA,
+	MVS_G711A_ERASURE
+};
+
+enum msm_audio_g711a_mode {
+	MVS_G711A_MODE_MULAW,
+	MVS_G711A_MODE_ALAW
+};
+
+enum msm_audio_g711_mode {
+	MVS_G711_MODE_MULAW,
+	MVS_G711_MODE_ALAW
+};
+
+#define VOIP_MODE_MAX		MODE_G711A
+#define VOIP_RATE_MAX		23850
+
+enum format {
+	FORMAT_S16_LE = 2,
+	FORMAT_SPECIAL = 31,
+};
+
+
+enum amr_rate_type {
+	AMR_RATE_4750, /* AMR 4.75 kbps */
+	AMR_RATE_5150, /* AMR 5.15 kbps */
+	AMR_RATE_5900, /* AMR 5.90 kbps */
+	AMR_RATE_6700, /* AMR 6.70 kbps */
+	AMR_RATE_7400, /* AMR 7.40 kbps */
+	AMR_RATE_7950, /* AMR 7.95 kbps */
+	AMR_RATE_10200, /* AMR 10.20 kbps */
+	AMR_RATE_12200, /* AMR 12.20 kbps */
+	AMR_RATE_6600, /* AMR-WB 6.60 kbps */
+	AMR_RATE_8850, /* AMR-WB 8.85 kbps */
+	AMR_RATE_12650, /* AMR-WB 12.65 kbps */
+	AMR_RATE_14250, /* AMR-WB 14.25 kbps */
+	AMR_RATE_15850, /* AMR-WB 15.85 kbps */
+	AMR_RATE_18250, /* AMR-WB 18.25 kbps */
+	AMR_RATE_19850, /* AMR-WB 19.85 kbps */
+	AMR_RATE_23050, /* AMR-WB 23.05 kbps */
+	AMR_RATE_23850, /* AMR-WB 23.85 kbps */
+	AMR_RATE_UNDEF
+};
+
+enum voip_state {
+	VOIP_STOPPED,
+	VOIP_STARTED,
+};
+
+struct voip_frame_hdr {
+	uint32_t timestamp;
+	union {
+		/*
+		 * Bits 0-3: Frame type
+		 * [optional] Bits 16-19: Frame rate
+		 */
+		uint32_t frame_type;
+		uint32_t packet_rate;
+	};
+};
+struct voip_frame {
+	struct voip_frame_hdr frm_hdr;
+	uint32_t pktlen;
+	uint8_t voc_pkt[VOIP_MAX_VOC_PKT_SIZE];
+};
+
+struct voip_buf_node {
+	struct list_head list;
+	struct voip_frame frame;
+};
+
+struct voip_drv_info {
+	enum  voip_state state;
+
+	struct snd_pcm_substream *playback_substream;
+	struct snd_pcm_substream *capture_substream;
+
+	struct list_head in_queue;
+	struct list_head free_in_queue;
+
+	struct list_head out_queue;
+	struct list_head free_out_queue;
+
+	wait_queue_head_t out_wait;
+	wait_queue_head_t in_wait;
+
+	struct mutex lock;
+
+	spinlock_t dsp_lock;
+	spinlock_t dsp_ul_lock;
+
+	bool voip_reset;
+	uint32_t mode;
+	uint32_t rate_type;
+	uint32_t rate;
+	uint32_t dtx_mode;
+
+	uint8_t capture_start;
+	uint8_t playback_start;
+
+	uint8_t playback_prepare;
+	uint8_t capture_prepare;
+
+	unsigned int play_samp_rate;
+	unsigned int cap_samp_rate;
+
+	unsigned int pcm_size;
+	unsigned int pcm_count;
+	unsigned int pcm_playback_irq_pos;      /* IRQ position */
+	unsigned int pcm_playback_buf_pos;      /* position in buffer */
+
+	unsigned int pcm_capture_size;
+	unsigned int pcm_capture_count;
+	unsigned int pcm_capture_irq_pos;       /* IRQ position */
+	unsigned int pcm_capture_buf_pos;       /* position in buffer */
+
+	uint32_t evrc_min_rate;
+	uint32_t evrc_max_rate;
+};
+
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
+				unsigned int samp_rate,
+				unsigned int *media_type);
+static int voip_get_rate_type(uint32_t mode,
+				uint32_t rate,
+				uint32_t *rate_type);
+static int voip_config_vocoder(struct snd_pcm_substream *substream);
+static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol);
+static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol);
+
+static struct voip_drv_info voip_info;
+
+static struct snd_pcm_hardware msm_pcm_hardware = {
+	.info =                 (SNDRV_PCM_INFO_MMAP |
+				SNDRV_PCM_INFO_BLOCK_TRANSFER |
+				SNDRV_PCM_INFO_MMAP_VALID |
+				SNDRV_PCM_INFO_INTERLEAVED),
+	.formats =              SNDRV_PCM_FMTBIT_S16_LE |
+				SNDRV_PCM_FMTBIT_SPECIAL,
+	.rates =                SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+				SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+	.rate_min =             8000,
+	.rate_max =             48000,
+	.channels_min =         1,
+	.channels_max =         1,
+	.buffer_bytes_max =	sizeof(struct voip_buf_node) * VOIP_MAX_Q_LEN,
+	.period_bytes_min =	VOIP_MIN_VOC_PKT_SIZE,
+	.period_bytes_max =	VOIP_MAX_VOC_PKT_SIZE,
+	.periods_min =		VOIP_MAX_Q_LEN,
+	.periods_max =		VOIP_MAX_Q_LEN,
+	.fifo_size =            0,
+};
+
+
+static int msm_voip_mute_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int mute = ucontrol->value.integer.value[0];
+	int ramp_duration = ucontrol->value.integer.value[1];
+
+	if ((mute < 0) || (mute > 1) || (ramp_duration < 0)) {
+		pr_err(" %s Invalid arguments", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: mute=%d ramp_duration=%d\n", __func__, mute,
+		ramp_duration);
+
+	voc_set_tx_mute(voc_get_session_id(VOIP_SESSION_NAME), TX_PATH, mute,
+					ramp_duration);
+
+done:
+	return ret;
+}
+
+static int msm_voip_gain_put(struct snd_kcontrol *kcontrol,
+			     struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int volume = ucontrol->value.integer.value[0];
+	int ramp_duration = ucontrol->value.integer.value[1];
+
+	if ((volume < 0) || (ramp_duration < 0)) {
+		pr_err(" %s Invalid arguments", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	pr_debug("%s: volume: %d ramp_duration: %d\n", __func__, volume,
+		ramp_duration);
+
+	voc_set_rx_vol_step(voc_get_session_id(VOIP_SESSION_NAME),
+						RX_PATH,
+						volume,
+						ramp_duration);
+
+done:
+	return ret;
+}
+
+static int msm_voip_dtx_mode_put(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	mutex_lock(&voip_info.lock);
+
+	voip_info.dtx_mode  = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: dtx: %d\n", __func__, voip_info.dtx_mode);
+
+	mutex_unlock(&voip_info.lock);
+
+	return 0;
+}
+static int msm_voip_dtx_mode_get(struct snd_kcontrol *kcontrol,
+				struct snd_ctl_elem_value *ucontrol)
+{
+	mutex_lock(&voip_info.lock);
+
+	ucontrol->value.integer.value[0] = voip_info.dtx_mode;
+
+	mutex_unlock(&voip_info.lock);
+
+	return 0;
+}
+
+static struct snd_kcontrol_new msm_voip_controls[] = {
+	SOC_SINGLE_MULTI_EXT("Voip Tx Mute", SND_SOC_NOPM, 0,
+			     MAX_RAMP_DURATION,
+			     0, 2, NULL, msm_voip_mute_put),
+	SOC_SINGLE_MULTI_EXT("Voip Rx Gain", SND_SOC_NOPM, 0,
+			     MAX_RAMP_DURATION,
+			     0, 2, NULL, msm_voip_gain_put),
+	SOC_SINGLE_EXT("Voip Mode Config", SND_SOC_NOPM, 0, VOIP_MODE_MAX, 0,
+		       msm_voip_mode_config_get, msm_voip_mode_config_put),
+	SOC_SINGLE_EXT("Voip Rate Config", SND_SOC_NOPM, 0, VOIP_RATE_MAX, 0,
+		       NULL, msm_voip_rate_config_put),
+	SOC_SINGLE_MULTI_EXT("Voip Evrc Min Max Rate Config", SND_SOC_NOPM,
+			     0, VOC_1_RATE, 0, 2,
+			     msm_voip_evrc_min_max_rate_config_get,
+			     msm_voip_evrc_min_max_rate_config_put),
+	SOC_SINGLE_EXT("Voip Dtx Mode", SND_SOC_NOPM, 0, 1, 0,
+		       msm_voip_dtx_mode_get, msm_voip_dtx_mode_put),
+};
+
+static int msm_pcm_voip_probe(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, msm_voip_controls,
+					ARRAY_SIZE(msm_voip_controls));
+
+	return 0;
+}
+
+/* sample rate supported */
+static unsigned int supported_sample_rates[] = {8000, 16000, 32000, 48000};
+
+static void voip_ssr_cb_fn(uint32_t opcode, void *private_data)
+{
+
+	/* Notify ASoC to send next playback/Capture to unblock write/read */
+	struct voip_drv_info *prtd = private_data;
+
+	if (opcode == 0xFFFFFFFF) {
+
+		prtd->voip_reset = true;
+		pr_debug("%s: Notify ASoC to send next playback/Capture\n",
+			__func__);
+
+		prtd->pcm_playback_irq_pos += prtd->pcm_count;
+		if (prtd->state == VOIP_STARTED)
+			snd_pcm_period_elapsed(prtd->playback_substream);
+		wake_up(&prtd->out_wait);
+
+		prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+		if (prtd->state == VOIP_STARTED)
+			snd_pcm_period_elapsed(prtd->capture_substream);
+		wake_up(&prtd->in_wait);
+
+	} else {
+		pr_err("%s: Invalid opcode during reset : %d\n",
+			__func__, opcode);
+	}
+}
+
+/* capture path */
+static void voip_process_ul_pkt(uint8_t *voc_pkt,
+				uint32_t pkt_len,
+				uint32_t timestamp,
+				void *private_data)
+{
+	struct voip_buf_node *buf_node = NULL;
+	struct voip_drv_info *prtd = private_data;
+	unsigned long dsp_flags;
+
+	if (prtd->capture_substream == NULL)
+		return;
+
+	/* Copy up-link packet into out_queue. */
+	spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+
+	/* discarding UL packets till start is received */
+	if (!list_empty(&prtd->free_out_queue) && prtd->capture_start) {
+		buf_node = list_first_entry(&prtd->free_out_queue,
+					struct voip_buf_node, list);
+		list_del(&buf_node->list);
+		switch (prtd->mode) {
+		case MODE_AMR_WB:
+		case MODE_AMR: {
+			/* Remove the DSP frame info header. Header format:
+			 * Bits 0-3: Frame rate
+			 * Bits 4-7: Frame type
+			 */
+			buf_node->frame.frm_hdr.timestamp = timestamp;
+			buf_node->frame.frm_hdr.frame_type =
+						((*voc_pkt) & 0xF0) >> 4;
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+			buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN;
+			memcpy(&buf_node->frame.voc_pkt[0],
+				voc_pkt,
+				buf_node->frame.pktlen);
+
+			list_add_tail(&buf_node->list, &prtd->out_queue);
+			break;
+		}
+		case MODE_IS127:
+		case MODE_4GV_NB:
+		case MODE_4GV_WB:
+		case MODE_4GV_NW: {
+			/* Remove the DSP frame info header.
+			 * Header format:
+			 * Bits 0-3: frame rate
+			 */
+			buf_node->frame.frm_hdr.timestamp = timestamp;
+			buf_node->frame.frm_hdr.packet_rate = (*voc_pkt) & 0x0F;
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+			buf_node->frame.pktlen = pkt_len - DSP_FRAME_HDR_LEN;
+
+			memcpy(&buf_node->frame.voc_pkt[0],
+				voc_pkt,
+				buf_node->frame.pktlen);
+
+			list_add_tail(&buf_node->list, &prtd->out_queue);
+			break;
+		}
+		case MODE_G711:
+		case MODE_G711A:{
+			/* G711 frames are 10ms each, but the DSP works with
+			 * 20ms frames and sends two 10ms frames per buffer.
+			 * Extract the two frames and put them in separate
+			 * buffers.
+			 */
+			/* Remove the first DSP frame info header.
+			 * Header format: G711A
+			 * Bits 0-1: Frame type
+			 * Bits 2-3: Frame rate
+			 *
+			 * Header format: G711
+			 * Bits 2-3: Frame rate
+			 */
+			if (prtd->mode == MODE_G711A)
+				buf_node->frame.frm_hdr.frame_type =
+							(*voc_pkt) & 0x03;
+			buf_node->frame.frm_hdr.timestamp = timestamp;
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+			/* There are two frames in the buffer. Length of the
+			 * first frame:
+			 */
+			buf_node->frame.pktlen = (pkt_len -
+						  2 * DSP_FRAME_HDR_LEN) / 2;
+
+			memcpy(&buf_node->frame.voc_pkt[0],
+			       voc_pkt,
+			       buf_node->frame.pktlen);
+			voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+			list_add_tail(&buf_node->list, &prtd->out_queue);
+
+			/* Get another buffer from the free Q and fill in the
+			 * second frame.
+			 */
+			if (!list_empty(&prtd->free_out_queue)) {
+				buf_node =
+					list_first_entry(&prtd->free_out_queue,
+							 struct voip_buf_node,
+							 list);
+				list_del(&buf_node->list);
+
+				/* Remove the second DSP frame info header.
+				 * Header format:
+				 * Bits 0-1: Frame type
+				 * Bits 2-3: Frame rate
+				 */
+
+				if (prtd->mode == MODE_G711A)
+					buf_node->frame.frm_hdr.frame_type =
+							(*voc_pkt) & 0x03;
+				buf_node->frame.frm_hdr.timestamp = timestamp;
+				voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+				/* There are two frames in the buffer. Length
+				 * of the second frame:
+				 */
+				buf_node->frame.pktlen = (pkt_len -
+						2 * DSP_FRAME_HDR_LEN) / 2;
+
+				memcpy(&buf_node->frame.voc_pkt[0],
+				       voc_pkt,
+				       buf_node->frame.pktlen);
+
+				list_add_tail(&buf_node->list,
+					      &prtd->out_queue);
+			} else {
+				/* Drop the second frame */
+				pr_err("%s: UL data dropped, read is slow\n",
+				       __func__);
+			}
+			break;
+		}
+		default: {
+			buf_node->frame.frm_hdr.timestamp = timestamp;
+			buf_node->frame.pktlen = pkt_len;
+			memcpy(&buf_node->frame.voc_pkt[0],
+			       voc_pkt,
+			       buf_node->frame.pktlen);
+			list_add_tail(&buf_node->list, &prtd->out_queue);
+		}
+		}
+		pr_debug("%s: pkt_len =%d, frame.pktlen=%d, timestamp=%d\n",
+			 __func__, pkt_len, buf_node->frame.pktlen, timestamp);
+
+		if (prtd->mode == MODE_PCM)
+			prtd->pcm_capture_irq_pos += buf_node->frame.pktlen;
+		else
+			prtd->pcm_capture_irq_pos += prtd->pcm_capture_count;
+
+		spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+		snd_pcm_period_elapsed(prtd->capture_substream);
+	} else {
+		spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+		pr_err("UL data dropped\n");
+	}
+
+	wake_up(&prtd->out_wait);
+}
+
+/* playback path */
+static void voip_process_dl_pkt(uint8_t *voc_pkt, void *private_data)
+{
+	struct voip_buf_node *buf_node = NULL;
+	struct voip_drv_info *prtd = private_data;
+	unsigned long dsp_flags;
+	uint32_t rate_type;
+	uint32_t frame_rate;
+	u32 pkt_len;
+	u8 *voc_addr = NULL;
+
+	if (prtd->playback_substream == NULL)
+		return;
+
+	spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+
+	if (!list_empty(&prtd->in_queue) && prtd->playback_start) {
+		buf_node = list_first_entry(&prtd->in_queue,
+				struct voip_buf_node, list);
+		list_del(&buf_node->list);
+		switch (prtd->mode) {
+		case MODE_AMR:
+		case MODE_AMR_WB: {
+			*((uint32_t *)voc_pkt) = buf_node->frame.pktlen +
+							DSP_FRAME_HDR_LEN;
+			/* Advance to the header of voip packet */
+			voc_pkt = voc_pkt + sizeof(uint32_t);
+			/*
+			 * Add the DSP frame info header. Header format:
+			 * Bits 0-3: Frame rate
+			 * Bits 4-7: Frame type
+			 */
+			*voc_pkt = ((buf_node->frame.frm_hdr.frame_type &
+				   0x0F) << 4);
+			frame_rate = (buf_node->frame.frm_hdr.frame_type &
+				     0xFFFF0000) >> 16;
+			if (frame_rate) {
+				if (voip_get_rate_type(prtd->mode, frame_rate,
+						       &rate_type)) {
+					pr_err("%s(): fail at getting rate_type\n",
+						__func__);
+				} else
+					prtd->rate_type = rate_type;
+			}
+			*voc_pkt |= prtd->rate_type & 0x0F;
+
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+			memcpy(voc_pkt,
+				&buf_node->frame.voc_pkt[0],
+				buf_node->frame.pktlen);
+			list_add_tail(&buf_node->list, &prtd->free_in_queue);
+			break;
+		}
+		case MODE_IS127:
+		case MODE_4GV_NB:
+		case MODE_4GV_WB:
+		case MODE_4GV_NW: {
+			*((uint32_t *)voc_pkt) = buf_node->frame.pktlen +
+							 DSP_FRAME_HDR_LEN;
+			/* Advance to the header of voip packet */
+			voc_pkt = voc_pkt + sizeof(uint32_t);
+			/*
+			 * Add the DSP frame info header. Header format:
+			 * Bits 0-3 : Frame rate
+			 */
+			*voc_pkt = buf_node->frame.frm_hdr.packet_rate & 0x0F;
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+			memcpy(voc_pkt,
+				&buf_node->frame.voc_pkt[0],
+				buf_node->frame.pktlen);
+
+			list_add_tail(&buf_node->list, &prtd->free_in_queue);
+			break;
+		}
+		case MODE_G711:
+		case MODE_G711A:{
+			/* G711 frames are 10ms each but the DSP expects 20ms
+			 * worth of data, so send two 10ms frames per buffer.
+			 */
+			/* Add the first DSP frame info header. Header format:
+			 * Bits 0-1: Frame type
+			 * Bits 2-3: Frame rate
+			 */
+			voc_addr = voc_pkt;
+			voc_pkt = voc_pkt + sizeof(uint32_t);
+
+			*voc_pkt = ((prtd->rate_type  & 0x0F) << 2) |
+				    (buf_node->frame.frm_hdr.frame_type & 0x03);
+			voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+			pkt_len = buf_node->frame.pktlen + DSP_FRAME_HDR_LEN;
+
+			memcpy(voc_pkt,
+			       &buf_node->frame.voc_pkt[0],
+			       buf_node->frame.pktlen);
+			voc_pkt = voc_pkt + buf_node->frame.pktlen;
+
+			list_add_tail(&buf_node->list, &prtd->free_in_queue);
+
+			if (!list_empty(&prtd->in_queue)) {
+				/* Get the second buffer. */
+				buf_node = list_first_entry(&prtd->in_queue,
+							struct voip_buf_node,
+							list);
+				list_del(&buf_node->list);
+
+				/* Add the second DSP frame info header.
+				 * Header format:
+				 * Bits 0-1: Frame type
+				 * Bits 2-3: Frame rate
+				 */
+				*voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+				(buf_node->frame.frm_hdr.frame_type & 0x03);
+				voc_pkt = voc_pkt + DSP_FRAME_HDR_LEN;
+
+				pkt_len = pkt_len + buf_node->frame.pktlen +
+					   DSP_FRAME_HDR_LEN;
+
+				memcpy(voc_pkt,
+				       &buf_node->frame.voc_pkt[0],
+				       buf_node->frame.pktlen);
+
+				list_add_tail(&buf_node->list,
+					      &prtd->free_in_queue);
+			} else {
+				/* Only 10ms worth of data is available, signal
+				 * erasure frame.
+				 */
+				*voc_pkt = ((prtd->rate_type & 0x0F) << 2) |
+					    (MVS_G711A_ERASURE & 0x03);
+
+				pkt_len = pkt_len + DSP_FRAME_HDR_LEN;
+				pr_debug("%s, Only 10ms read, erase 2nd frame\n",
+					 __func__);
+			}
+			*((uint32_t *)voc_addr) = pkt_len;
+			break;
+		}
+		default: {
+			*((uint32_t *)voc_pkt) = buf_node->frame.pktlen;
+			voc_pkt = voc_pkt + sizeof(uint32_t);
+			memcpy(voc_pkt,
+			       &buf_node->frame.voc_pkt[0],
+			       buf_node->frame.pktlen);
+			list_add_tail(&buf_node->list, &prtd->free_in_queue);
+		}
+		}
+		pr_debug("%s: frame.pktlen=%d\n", __func__,
+			 buf_node->frame.pktlen);
+
+		if (prtd->mode == MODE_PCM)
+			prtd->pcm_playback_irq_pos += buf_node->frame.pktlen;
+		else
+			prtd->pcm_playback_irq_pos += prtd->pcm_count;
+
+		spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		snd_pcm_period_elapsed(prtd->playback_substream);
+	} else {
+		*((uint32_t *)voc_pkt) = 0;
+		spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		pr_err_ratelimited("DL data not available\n");
+	}
+	wake_up(&prtd->in_wait);
+}
+
+static struct snd_pcm_hw_constraint_list constraints_sample_rates = {
+	.count = ARRAY_SIZE(supported_sample_rates),
+	.list = supported_sample_rates,
+	.mask = 0,
+};
+
+static int msm_pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+
+	prtd->play_samp_rate = runtime->rate;
+	prtd->pcm_size = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_playback_irq_pos = 0;
+	prtd->pcm_playback_buf_pos = 0;
+	prtd->playback_prepare = 1;
+
+	return 0;
+}
+
+static int msm_pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+	int ret = 0;
+
+	prtd->cap_samp_rate = runtime->rate;
+	prtd->pcm_capture_size  = snd_pcm_lib_buffer_bytes(substream);
+	prtd->pcm_capture_count = snd_pcm_lib_period_bytes(substream);
+	prtd->pcm_capture_irq_pos = 0;
+	prtd->pcm_capture_buf_pos = 0;
+	prtd->capture_prepare = 1;
+	return ret;
+}
+
+static int msm_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+	case SNDRV_PCM_TRIGGER_RESUME:
+		pr_debug("%s: Trigger start\n", __func__);
+		if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+			prtd->capture_start = 1;
+		else
+			prtd->playback_start = 1;
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		pr_debug("SNDRV_PCM_TRIGGER_STOP\n");
+		if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+			prtd->playback_start = 0;
+		else
+			prtd->capture_start = 0;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int msm_pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = &voip_info;
+	int ret = 0;
+
+	pr_debug("%s, VoIP\n", __func__);
+	mutex_lock(&prtd->lock);
+
+	runtime->hw = msm_pcm_hardware;
+
+	ret = snd_pcm_hw_constraint_list(runtime, 0,
+					SNDRV_PCM_HW_PARAM_RATE,
+					&constraints_sample_rates);
+	if (ret < 0)
+		pr_debug("snd_pcm_hw_constraint_list failed\n");
+
+	ret = snd_pcm_hw_constraint_integer(runtime,
+					SNDRV_PCM_HW_PARAM_PERIODS);
+	if (ret < 0) {
+		pr_debug("snd_pcm_hw_constraint_integer failed\n");
+		goto err;
+	}
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prtd->playback_substream = substream;
+	else
+		prtd->capture_substream = substream;
+
+	runtime->private_data = prtd;
+err:
+	mutex_unlock(&prtd->lock);
+
+	return ret;
+}
+
+static int msm_pcm_playback_copy(struct snd_pcm_substream *substream, int a,
+	snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	struct voip_buf_node *buf_node = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+	unsigned long dsp_flags;
+
+	int count = frames_to_bytes(runtime, frames);
+
+	pr_debug("%s: count = %d, frames=%d\n", __func__, count, (int)frames);
+
+	if (prtd->voip_reset) {
+		pr_debug("%s: RESET event happened during VoIP\n", __func__);
+		return -ENETRESET;
+	}
+
+	ret = wait_event_interruptible_timeout(prtd->in_wait,
+				(!list_empty(&prtd->free_in_queue) ||
+				prtd->state == VOIP_STOPPED),
+				1 * HZ);
+	if (prtd->voip_reset) {
+		pr_debug("%s: RESET event happened during VoIP\n", __func__);
+		return -ENETRESET;
+	}
+
+	if (ret > 0) {
+		if (count <= VOIP_MAX_VOC_PKT_SIZE) {
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			buf_node =
+				list_first_entry(&prtd->free_in_queue,
+						struct voip_buf_node, list);
+			list_del(&buf_node->list);
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+			if (prtd->mode == MODE_PCM) {
+				ret = copy_from_user(&buf_node->frame.voc_pkt,
+							buf, count);
+				buf_node->frame.pktlen = count;
+			} else {
+				ret = copy_from_user(&buf_node->frame,
+							buf, count);
+				if (buf_node->frame.pktlen >= count)
+					buf_node->frame.pktlen = count -
+					(sizeof(buf_node->frame.frm_hdr) +
+					 sizeof(buf_node->frame.pktlen));
+			}
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			list_add_tail(&buf_node->list, &prtd->in_queue);
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+		} else {
+			pr_err("%s: Write cnt %d is > VOIP_MAX_VOC_PKT_SIZE\n",
+				__func__, count);
+			ret = -ENOMEM;
+		}
+
+	} else if (ret == 0) {
+		pr_err("%s: No free DL buffs\n", __func__);
+		ret = -ETIMEDOUT;
+	} else {
+		pr_err("%s: playback copy was interrupted %d\n", __func__, ret);
+	}
+
+	return  ret;
+}
+static int msm_pcm_capture_copy(struct snd_pcm_substream *substream,
+		int channel, snd_pcm_uframes_t hwoff, void __user *buf,
+						snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+	int count = 0;
+	struct voip_buf_node *buf_node = NULL;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+	unsigned long dsp_flags;
+	int size;
+
+	count = frames_to_bytes(runtime, frames);
+
+	pr_debug("%s: count = %d\n", __func__, count);
+
+	if (prtd->voip_reset) {
+		pr_debug("%s: RESET event happened during VoIP\n", __func__);
+		return -ENETRESET;
+	}
+
+	ret = wait_event_interruptible_timeout(prtd->out_wait,
+				(!list_empty(&prtd->out_queue) ||
+				prtd->state == VOIP_STOPPED),
+				1 * HZ);
+
+	if (prtd->voip_reset) {
+		pr_debug("%s: RESET event happened during VoIP\n", __func__);
+		return -ENETRESET;
+	}
+
+	if (ret > 0) {
+
+		if (count <= VOIP_MAX_VOC_PKT_SIZE) {
+			spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+			buf_node = list_first_entry(&prtd->out_queue,
+					struct voip_buf_node, list);
+			list_del(&buf_node->list);
+			spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+			if (prtd->mode == MODE_PCM) {
+				ret = copy_to_user(buf,
+						   &buf_node->frame.voc_pkt,
+						   buf_node->frame.pktlen);
+			} else {
+				size = sizeof(buf_node->frame.frm_hdr) +
+				       sizeof(buf_node->frame.pktlen) +
+				       buf_node->frame.pktlen;
+
+				ret = copy_to_user(buf,
+						   &buf_node->frame,
+						   size);
+			}
+			if (ret) {
+				pr_err("%s: Copy to user returned %d\n",
+					__func__, ret);
+				ret = -EFAULT;
+			}
+			spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+			list_add_tail(&buf_node->list,
+						&prtd->free_out_queue);
+			spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+		} else {
+			pr_err("%s: Read count %d > VOIP_MAX_VOC_PKT_SIZE\n",
+				__func__, count);
+			ret = -ENOMEM;
+		}
+
+
+	} else if (ret == 0) {
+		pr_err_ratelimited("%s: No UL data available\n", __func__);
+		ret = -ETIMEDOUT;
+	} else {
+		pr_err("%s: Read was interrupted\n", __func__);
+		ret = -ERESTARTSYS;
+	}
+	return ret;
+}
+static int msm_pcm_copy(struct snd_pcm_substream *substream, int a,
+	 snd_pcm_uframes_t hwoff, void __user *buf, snd_pcm_uframes_t frames)
+{
+	int ret = 0;
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_copy(substream, a, hwoff, buf, frames);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_copy(substream, a, hwoff, buf, frames);
+
+	return ret;
+}
+
+static int msm_pcm_close(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct list_head *ptr = NULL;
+	struct list_head *next = NULL;
+	struct voip_buf_node *buf_node = NULL;
+	struct snd_dma_buffer *p_dma_buf, *c_dma_buf;
+	struct snd_pcm_substream *p_substream, *c_substream;
+	struct snd_pcm_runtime *runtime;
+	struct voip_drv_info *prtd;
+	unsigned long dsp_flags;
+
+	if (substream == NULL) {
+		pr_err("substream is NULL\n");
+		return -EINVAL;
+	}
+	runtime = substream->runtime;
+	prtd = runtime->private_data;
+
+	wake_up(&prtd->out_wait);
+
+	mutex_lock(&prtd->lock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		prtd->playback_prepare = 0;
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		prtd->capture_prepare = 0;
+
+	if (!prtd->playback_prepare && !prtd->capture_prepare) {
+		if (prtd->state == VOIP_STARTED) {
+			prtd->voip_reset = false;
+			prtd->state = VOIP_STOPPED;
+			voc_end_voice_call(
+					voc_get_session_id(VOIP_SESSION_NAME));
+			voc_register_mvs_cb(NULL, NULL, NULL, prtd);
+		}
+		/* release all buffer */
+		/* release in_queue and free_in_queue */
+		pr_debug("release all buffer\n");
+		p_substream = prtd->playback_substream;
+		if (p_substream == NULL) {
+			pr_debug("p_substream is NULL\n");
+			goto capt;
+		}
+		p_dma_buf = &p_substream->dma_buffer;
+		if (p_dma_buf == NULL) {
+			pr_debug("p_dma_buf is NULL\n");
+			goto capt;
+		}
+		if (p_dma_buf->area != NULL) {
+			spin_lock_irqsave(&prtd->dsp_lock, dsp_flags);
+			list_for_each_safe(ptr, next, &prtd->in_queue) {
+				buf_node = list_entry(ptr,
+						struct voip_buf_node, list);
+				list_del(&buf_node->list);
+			}
+			list_for_each_safe(ptr, next, &prtd->free_in_queue) {
+				buf_node = list_entry(ptr,
+						struct voip_buf_node, list);
+				list_del(&buf_node->list);
+			}
+			spin_unlock_irqrestore(&prtd->dsp_lock, dsp_flags);
+			dma_free_coherent(p_substream->pcm->card->dev,
+				runtime->hw.buffer_bytes_max, p_dma_buf->area,
+				p_dma_buf->addr);
+			p_dma_buf->area = NULL;
+		}
+		/* release out_queue and free_out_queue */
+capt:		c_substream = prtd->capture_substream;
+		if (c_substream == NULL) {
+			pr_debug("c_substream is NULL\n");
+			goto done;
+		}
+		c_dma_buf = &c_substream->dma_buffer;
+		if (c_substream == NULL) {
+			pr_debug("c_dma_buf is NULL.\n");
+			goto done;
+		}
+		if (c_dma_buf->area != NULL) {
+			spin_lock_irqsave(&prtd->dsp_ul_lock, dsp_flags);
+			list_for_each_safe(ptr, next, &prtd->out_queue) {
+				buf_node = list_entry(ptr,
+						struct voip_buf_node, list);
+				list_del(&buf_node->list);
+			}
+			list_for_each_safe(ptr, next, &prtd->free_out_queue) {
+				buf_node = list_entry(ptr,
+						struct voip_buf_node, list);
+				list_del(&buf_node->list);
+			}
+			spin_unlock_irqrestore(&prtd->dsp_ul_lock, dsp_flags);
+			dma_free_coherent(c_substream->pcm->card->dev,
+				runtime->hw.buffer_bytes_max, c_dma_buf->area,
+				c_dma_buf->addr);
+			c_dma_buf->area = NULL;
+		}
+done:
+		prtd->capture_substream = NULL;
+		prtd->playback_substream = NULL;
+	}
+	mutex_unlock(&prtd->lock);
+
+	return ret;
+}
+
+static int voip_config_vocoder(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+	uint32_t media_type = 0;
+	uint32_t rate_type = 0;
+	uint32_t evrc_min_rate_type = 0;
+	uint32_t evrc_max_rate_type = 0;
+
+	pr_debug("%s(): mode=%d, playback rate=%d, capture rate=%d\n",
+		 __func__, prtd->mode, prtd->play_samp_rate,
+		 prtd->cap_samp_rate);
+
+	if ((runtime->format != FORMAT_S16_LE &&
+	     runtime->format != FORMAT_SPECIAL) &&
+	    ((prtd->mode == MODE_AMR) || (prtd->mode == MODE_AMR_WB) ||
+	    (prtd->mode == MODE_IS127) || (prtd->mode == MODE_4GV_NB) ||
+	    (prtd->mode == MODE_4GV_WB) || (prtd->mode == MODE_4GV_NW) ||
+	    (prtd->mode == MODE_G711) || (prtd->mode == MODE_G711A))) {
+		pr_err("%s(): mode:%d and format:%u are not matched\n",
+			__func__, prtd->mode, (uint32_t)runtime->format);
+
+		ret =  -EINVAL;
+		goto done;
+	}
+
+	if (runtime->format != FORMAT_S16_LE && (prtd->mode == MODE_PCM)) {
+		pr_err("%s(): mode:%d and format:%u are not matched\n",
+		       __func__, prtd->mode, runtime->format);
+
+		ret =  -EINVAL;
+		goto done;
+	}
+
+	if ((prtd->mode == MODE_PCM) ||
+	    (prtd->mode == MODE_AMR) ||
+	    (prtd->mode == MODE_AMR_WB) ||
+	    (prtd->mode == MODE_G711) ||
+	    (prtd->mode == MODE_G711A)) {
+		ret = voip_get_rate_type(prtd->mode,
+					 prtd->rate,
+					 &rate_type);
+		if (ret < 0) {
+			pr_err("%s(): fail at getting rate_type, ret=%d\n",
+				__func__, ret);
+
+			ret = -EINVAL;
+			goto done;
+		}
+		prtd->rate_type = rate_type;
+		pr_debug("rate_type=%d\n", rate_type);
+
+	} else if ((prtd->mode == MODE_IS127) ||
+		   (prtd->mode == MODE_4GV_NB) ||
+		   (prtd->mode == MODE_4GV_WB) ||
+		   (prtd->mode == MODE_4GV_NW)) {
+		ret = voip_get_rate_type(prtd->mode,
+					 prtd->evrc_min_rate,
+					 &evrc_min_rate_type);
+		if (ret < 0) {
+			pr_err("%s(): fail at getting min rate, ret=%d\n",
+				__func__, ret);
+
+			ret = -EINVAL;
+			goto done;
+		}
+		if (evrc_min_rate_type == VOC_0_RATE)
+			evrc_min_rate_type = VOC_8_RATE;
+
+		ret = voip_get_rate_type(prtd->mode,
+					 prtd->evrc_max_rate,
+					 &evrc_max_rate_type);
+		if (ret < 0) {
+			pr_err("%s(): fail at getting max rate, ret=%d\n",
+				__func__, ret);
+
+			ret = -EINVAL;
+			goto done;
+		}
+		if (evrc_max_rate_type == VOC_0_RATE)
+			evrc_max_rate_type = VOC_1_RATE;
+
+		if (evrc_max_rate_type < evrc_min_rate_type) {
+			pr_err("%s(): Invalid EVRC min max rates: %d, %d\n",
+				__func__, evrc_min_rate_type,
+				evrc_max_rate_type);
+
+			ret = -EINVAL;
+			goto done;
+		}
+		pr_debug("%s(): min rate=%d, max rate=%d\n",
+			  __func__, evrc_min_rate_type, evrc_max_rate_type);
+	}
+	ret = voip_get_media_type(prtd->mode,
+				  prtd->rate_type,
+				  prtd->play_samp_rate,
+				  &media_type);
+	if (ret < 0) {
+		pr_err("%s(): fail at getting media_type, ret=%d\n",
+		       __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	pr_debug("%s(): media_type=%d\n", __func__, media_type);
+
+	if ((prtd->play_samp_rate == 8000 && prtd->cap_samp_rate == 8000) ||
+	    (prtd->play_samp_rate == 16000 && prtd->cap_samp_rate == 16000) ||
+	    (prtd->play_samp_rate == 32000 && prtd->cap_samp_rate == 32000) ||
+	    (prtd->play_samp_rate == 48000 && prtd->cap_samp_rate == 48000)) {
+		voc_config_vocoder(media_type, rate_type,
+				   VSS_NETWORK_ID_VOIP,
+				   voip_info.dtx_mode,
+				   evrc_min_rate_type,
+				   evrc_max_rate_type);
+	} else {
+		pr_debug("%s: Invalid rate playback %d, capture %d\n",
+			 __func__, prtd->play_samp_rate,
+			 prtd->cap_samp_rate);
+
+		ret = -EINVAL;
+	}
+done:
+
+	return ret;
+}
+
+static int msm_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	int ret = 0;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+
+	mutex_lock(&prtd->lock);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_prepare(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_prepare(substream);
+
+	if (prtd->playback_prepare && prtd->capture_prepare
+	    && (prtd->state != VOIP_STARTED)) {
+		ret = voip_config_vocoder(substream);
+		if (ret < 0) {
+			pr_err("%s(): fail at configuring vocoder for voip, ret=%d\n",
+				__func__, ret);
+
+			goto done;
+		}
+
+		/* Initialaizing cb variables */
+		voc_register_mvs_cb(voip_process_ul_pkt,
+				    voip_process_dl_pkt,
+				    voip_ssr_cb_fn, prtd);
+
+		ret = voc_start_voice_call(
+				voc_get_session_id(VOIP_SESSION_NAME));
+
+		if (ret < 0) {
+			pr_err("%s: voc_start_voice_call() failed err %d",
+			       __func__, ret);
+
+			goto done;
+		}
+		prtd->state = VOIP_STARTED;
+	}
+done:
+	mutex_unlock(&prtd->lock);
+
+	return ret;
+}
+
+static snd_pcm_uframes_t
+msm_pcm_playback_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+
+	pr_debug("%s\n", __func__);
+	if (prtd->pcm_playback_irq_pos >= prtd->pcm_size)
+		prtd->pcm_playback_irq_pos = 0;
+	return bytes_to_frames(runtime, (prtd->pcm_playback_irq_pos));
+}
+
+static snd_pcm_uframes_t
+msm_pcm_capture_pointer(struct snd_pcm_substream *substream)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct voip_drv_info *prtd = runtime->private_data;
+
+	if (prtd->pcm_capture_irq_pos >= prtd->pcm_capture_size)
+		prtd->pcm_capture_irq_pos = 0;
+	return bytes_to_frames(runtime, (prtd->pcm_capture_irq_pos));
+}
+
+static snd_pcm_uframes_t msm_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	snd_pcm_uframes_t ret = 0;
+
+	pr_debug("%s\n", __func__);
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+		ret = msm_pcm_playback_pointer(substream);
+	else if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+		ret = msm_pcm_capture_pointer(substream);
+	return ret;
+}
+
+static int msm_pcm_mmap(struct snd_pcm_substream *substream,
+			struct vm_area_struct *vma)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+
+	pr_debug("%s\n", __func__);
+	dma_mmap_coherent(substream->pcm->card->dev, vma,
+				     runtime->dma_area,
+				     runtime->dma_addr,
+				     runtime->dma_bytes);
+	return 0;
+}
+
+static int msm_pcm_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *params)
+{
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	struct snd_dma_buffer *dma_buf = &substream->dma_buffer;
+	struct voip_buf_node *buf_node = NULL;
+	int i = 0, offset = 0;
+
+	pr_debug("%s: voip\n", __func__);
+
+	mutex_lock(&voip_info.lock);
+
+	dma_buf->dev.type = SNDRV_DMA_TYPE_DEV;
+	dma_buf->dev.dev = substream->pcm->card->dev;
+	dma_buf->private_data = NULL;
+
+	dma_buf->area = dma_alloc_coherent(substream->pcm->card->dev,
+			runtime->hw.buffer_bytes_max,
+			&dma_buf->addr, GFP_KERNEL);
+	if (!dma_buf->area) {
+		pr_err("%s:MSM VOIP dma_alloc failed\n", __func__);
+		mutex_unlock(&voip_info.lock);
+		return -ENOMEM;
+	}
+
+	dma_buf->bytes = runtime->hw.buffer_bytes_max;
+	memset(dma_buf->area, 0, runtime->hw.buffer_bytes_max);
+
+	if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+		for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+			buf_node = (void *)dma_buf->area + offset;
+
+			list_add_tail(&buf_node->list,
+					&voip_info.free_in_queue);
+			offset = offset + sizeof(struct voip_buf_node);
+		}
+	} else {
+		for (i = 0; i < VOIP_MAX_Q_LEN; i++) {
+			buf_node = (void *) dma_buf->area + offset;
+			list_add_tail(&buf_node->list,
+					&voip_info.free_out_queue);
+			offset = offset + sizeof(struct voip_buf_node);
+		}
+	}
+
+	mutex_unlock(&voip_info.lock);
+
+	snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+
+	return 0;
+}
+
+static int msm_voip_mode_config_get(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	mutex_lock(&voip_info.lock);
+
+	ucontrol->value.integer.value[0] = voip_info.mode;
+
+	mutex_unlock(&voip_info.lock);
+
+	return 0;
+}
+
+static int msm_voip_mode_config_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	mutex_lock(&voip_info.lock);
+
+	voip_info.mode = ucontrol->value.integer.value[0];
+
+	pr_debug("%s: mode=%d\n", __func__, voip_info.mode);
+
+	mutex_unlock(&voip_info.lock);
+
+	return 0;
+}
+
+static int msm_voip_rate_config_put(struct snd_kcontrol *kcontrol,
+				    struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+	int rate = ucontrol->value.integer.value[0];
+
+	mutex_lock(&voip_info.lock);
+
+	if (voip_info.rate != rate) {
+		voip_info.rate = rate;
+		pr_debug("%s: rate=%d\n", __func__, voip_info.rate);
+
+		if (voip_info.state == VOIP_STARTED &&
+		   (voip_info.mode == MODE_AMR ||
+		    voip_info.mode == MODE_AMR_WB)) {
+			ret = voip_config_vocoder(
+					voip_info.capture_substream);
+			if (ret) {
+				pr_err("%s:Failed to configure vocoder, ret=%d\n",
+					__func__, ret);
+
+				goto done;
+			}
+
+			ret = voc_update_amr_vocoder_rate(
+					voc_get_session_id(VOIP_SESSION_NAME));
+			if (ret) {
+				pr_err("%s:Failed to update AMR rate, ret=%d\n",
+					__func__, ret);
+			}
+		}
+	}
+
+done:
+	mutex_unlock(&voip_info.lock);
+
+	return ret;
+}
+
+static int msm_voip_evrc_min_max_rate_config_get(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	mutex_lock(&voip_info.lock);
+
+	ucontrol->value.integer.value[0] = voip_info.evrc_min_rate;
+	ucontrol->value.integer.value[1] = voip_info.evrc_max_rate;
+
+	mutex_unlock(&voip_info.lock);
+
+	return 0;
+}
+
+static int msm_voip_evrc_min_max_rate_config_put(struct snd_kcontrol *kcontrol,
+					 struct snd_ctl_elem_value *ucontrol)
+{
+	mutex_lock(&voip_info.lock);
+
+	voip_info.evrc_min_rate = ucontrol->value.integer.value[0];
+	voip_info.evrc_max_rate = ucontrol->value.integer.value[1];
+
+	pr_debug("%s(): evrc_min_rate=%d,evrc_max_rate=%d\n", __func__,
+		  voip_info.evrc_min_rate, voip_info.evrc_max_rate);
+
+	mutex_unlock(&voip_info.lock);
+
+	return 0;
+}
+
+static int voip_get_rate_type(uint32_t mode, uint32_t rate,
+				 uint32_t *rate_type)
+{
+	int ret = 0;
+
+	switch (mode) {
+	case MODE_AMR: {
+		switch (rate) {
+		case 4750:
+			*rate_type = AMR_RATE_4750;
+			break;
+		case 5150:
+			*rate_type = AMR_RATE_5150;
+			break;
+		case 5900:
+			*rate_type = AMR_RATE_5900;
+			break;
+		case 6700:
+			*rate_type = AMR_RATE_6700;
+			break;
+		case 7400:
+			*rate_type = AMR_RATE_7400;
+			break;
+		case 7950:
+			*rate_type = AMR_RATE_7950;
+			break;
+		case 10200:
+			*rate_type = AMR_RATE_10200;
+			break;
+		case 12200:
+			*rate_type = AMR_RATE_12200;
+			break;
+		default:
+			pr_err("wrong rate for AMR NB.\n");
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	}
+	case MODE_AMR_WB: {
+		switch (rate) {
+		case 6600:
+			*rate_type = AMR_RATE_6600 - AMR_RATE_6600;
+			break;
+		case 8850:
+			*rate_type = AMR_RATE_8850 - AMR_RATE_6600;
+			break;
+		case 12650:
+			*rate_type = AMR_RATE_12650 - AMR_RATE_6600;
+			break;
+		case 14250:
+			*rate_type = AMR_RATE_14250 - AMR_RATE_6600;
+			break;
+		case 15850:
+			*rate_type = AMR_RATE_15850 - AMR_RATE_6600;
+			break;
+		case 18250:
+			*rate_type = AMR_RATE_18250 - AMR_RATE_6600;
+			break;
+		case 19850:
+			*rate_type = AMR_RATE_19850 - AMR_RATE_6600;
+			break;
+		case 23050:
+			*rate_type = AMR_RATE_23050 - AMR_RATE_6600;
+			break;
+		case 23850:
+			*rate_type = AMR_RATE_23850 - AMR_RATE_6600;
+			break;
+		default:
+			pr_err("wrong rate for AMR_WB.\n");
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	}
+	case MODE_PCM: {
+		*rate_type = 0;
+		break;
+	}
+	case MODE_IS127:
+	case MODE_4GV_NB:
+	case MODE_4GV_WB: {
+		switch (rate) {
+		case VOC_0_RATE:
+		case VOC_8_RATE:
+		case VOC_4_RATE:
+		case VOC_2_RATE:
+		case VOC_1_RATE:
+			*rate_type = rate;
+			break;
+		default:
+			pr_err("wrong rate for IS127/4GV_NB/WB.\n");
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	}
+	case MODE_4GV_NW: {
+		switch (rate) {
+		case VOC_0_RATE:
+		case VOC_8_RATE:
+		case VOC_4_RATE:
+		case VOC_2_RATE:
+		case VOC_1_RATE:
+		case VOC_8_RATE_NC:
+			*rate_type = rate;
+			break;
+		default:
+			pr_err("wrong rate for 4GV_NW.\n");
+			ret = -EINVAL;
+			break;
+		}
+		break;
+	}
+	case MODE_G711:
+	case MODE_G711A:
+		*rate_type = rate;
+		break;
+	default:
+		pr_err("wrong mode type.\n");
+		ret = -EINVAL;
+	}
+	pr_debug("%s, mode=%d, rate=%u, rate_type=%d\n",
+		__func__, mode, rate, *rate_type);
+	return ret;
+}
+
+static int voip_get_media_type(uint32_t mode, uint32_t rate_type,
+			       unsigned int samp_rate,
+			       unsigned int *media_type)
+{
+	int ret = 0;
+
+	pr_debug("%s: mode=%d, samp_rate=%d\n", __func__,
+		mode, samp_rate);
+	switch (mode) {
+	case MODE_AMR:
+		*media_type = VSS_MEDIA_ID_AMR_NB_MODEM;
+		break;
+	case MODE_AMR_WB:
+		*media_type = VSS_MEDIA_ID_AMR_WB_MODEM;
+		break;
+	case MODE_PCM:
+		if (samp_rate == 8000)
+			*media_type = VSS_MEDIA_ID_PCM_8_KHZ;
+		else if (samp_rate == 16000)
+			*media_type = VSS_MEDIA_ID_PCM_16_KHZ;
+		else if (samp_rate == 32000)
+			*media_type = VSS_MEDIA_ID_PCM_32_KHZ;
+		else
+			*media_type = VSS_MEDIA_ID_PCM_48_KHZ;
+		break;
+	case MODE_IS127: /* EVRC-A */
+		*media_type = VSS_MEDIA_ID_EVRC_MODEM;
+		break;
+	case MODE_4GV_NB: /* EVRC-B */
+		*media_type = VSS_MEDIA_ID_4GV_NB_MODEM;
+		break;
+	case MODE_4GV_WB: /* EVRC-WB */
+		*media_type = VSS_MEDIA_ID_4GV_WB_MODEM;
+		break;
+	case MODE_4GV_NW: /* EVRC-NW */
+		*media_type = VSS_MEDIA_ID_4GV_NW_MODEM;
+		break;
+	case MODE_G711:
+	case MODE_G711A:
+		if (rate_type == MVS_G711A_MODE_MULAW)
+			*media_type = VSS_MEDIA_ID_G711_MULAW;
+		else
+			*media_type = VSS_MEDIA_ID_G711_ALAW;
+		break;
+	default:
+		pr_debug(" input mode is not supported\n");
+		ret = -EINVAL;
+	}
+
+	pr_debug("%s: media_type is 0x%x\n", __func__, *media_type);
+
+	return ret;
+}
+
+
+static const struct snd_pcm_ops msm_pcm_ops = {
+	.open           = msm_pcm_open,
+	.copy		= msm_pcm_copy,
+	.hw_params	= msm_pcm_hw_params,
+	.close          = msm_pcm_close,
+	.prepare        = msm_pcm_prepare,
+	.trigger        = msm_pcm_trigger,
+	.pointer        = msm_pcm_pointer,
+	.mmap		= msm_pcm_mmap,
+};
+
+static int msm_asoc_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+	struct snd_card *card = rtd->card->snd_card;
+	int ret = 0;
+
+	pr_debug("msm_asoc_pcm_new\n");
+	if (!card->dev->coherent_dma_mask)
+		card->dev->coherent_dma_mask = DMA_BIT_MASK(32);
+	return ret;
+}
+
+static struct snd_soc_platform_driver msm_soc_platform = {
+	.ops		= &msm_pcm_ops,
+	.pcm_new	= msm_asoc_pcm_new,
+	.probe		= msm_pcm_voip_probe,
+};
+
+static int msm_pcm_probe(struct platform_device *pdev)
+{
+	int rc;
+
+	if (!is_voc_initialized()) {
+		pr_debug("%s: voice module not initialized yet, deferring probe()\n",
+		       __func__);
+
+		rc = -EPROBE_DEFER;
+		goto done;
+	}
+
+	rc = voc_alloc_cal_shared_memory();
+	if (rc == -EPROBE_DEFER) {
+		pr_debug("%s: memory allocation for calibration deferred %d\n",
+			 __func__, rc);
+
+		goto done;
+	} else if (rc < 0) {
+		pr_err("%s: memory allocation for calibration failed %d\n",
+		       __func__, rc);
+	}
+
+	rc = voc_alloc_voip_shared_memory();
+	if (rc < 0) {
+		pr_err("%s: error allocating shared mem err %d\n",
+		       __func__, rc);
+	}
+
+
+	pr_debug("%s: dev name %s\n", __func__, dev_name(&pdev->dev));
+	rc = snd_soc_register_platform(&pdev->dev,
+				       &msm_soc_platform);
+
+done:
+	return rc;
+}
+
+static int msm_pcm_remove(struct platform_device *pdev)
+{
+	snd_soc_unregister_platform(&pdev->dev);
+	return 0;
+}
+
+static const struct of_device_id msm_voip_dt_match[] = {
+	{.compatible = "qcom,msm-voip-dsp"},
+	{}
+};
+MODULE_DEVICE_TABLE(of, msm_voip_dt_match);
+
+static struct platform_driver msm_pcm_driver = {
+	.driver = {
+		.name = "msm-voip-dsp",
+		.owner = THIS_MODULE,
+		.of_match_table = msm_voip_dt_match,
+	},
+	.probe = msm_pcm_probe,
+	.remove = msm_pcm_remove,
+};
+
+static int __init msm_soc_platform_init(void)
+{
+	memset(&voip_info, 0, sizeof(voip_info));
+	voip_info.mode = MODE_PCM;
+	mutex_init(&voip_info.lock);
+
+	spin_lock_init(&voip_info.dsp_lock);
+	spin_lock_init(&voip_info.dsp_ul_lock);
+
+	init_waitqueue_head(&voip_info.out_wait);
+	init_waitqueue_head(&voip_info.in_wait);
+
+	INIT_LIST_HEAD(&voip_info.in_queue);
+	INIT_LIST_HEAD(&voip_info.free_in_queue);
+	INIT_LIST_HEAD(&voip_info.out_queue);
+	INIT_LIST_HEAD(&voip_info.free_out_queue);
+
+	return platform_driver_register(&msm_pcm_driver);
+}
+module_init(msm_soc_platform_init);
+
+static void __exit msm_soc_platform_exit(void)
+{
+	platform_driver_unregister(&msm_pcm_driver);
+}
+module_exit(msm_soc_platform_exit);
+
+MODULE_DESCRIPTION("PCM module platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
new file mode 100644
index 0000000..9276231
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.c
@@ -0,0 +1,1035 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/mutex.h>
+#include <sound/control.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/asound.h>
+#include <sound/q6audio-v2.h>
+#include <sound/tlv.h>
+
+#include "msm-qti-pp-config.h"
+#include "msm-pcm-routing-v2.h"
+
+/* EQUALIZER */
+/* Equal to Frontend after last of the MULTIMEDIA SESSIONS */
+#define MAX_EQ_SESSIONS		MSM_FRONTEND_DAI_CS_VOICE
+
+enum {
+	EQ_BAND1 = 0,
+	EQ_BAND2,
+	EQ_BAND3,
+	EQ_BAND4,
+	EQ_BAND5,
+	EQ_BAND6,
+	EQ_BAND7,
+	EQ_BAND8,
+	EQ_BAND9,
+	EQ_BAND10,
+	EQ_BAND11,
+	EQ_BAND12,
+	EQ_BAND_MAX,
+};
+
+struct msm_audio_eq_band {
+	uint16_t     band_idx; /* The band index, 0 .. 11 */
+	uint32_t     filter_type; /* Filter band type */
+	uint32_t     center_freq_hz; /* Filter band center frequency */
+	uint32_t     filter_gain; /* Filter band initial gain (dB) */
+			/* Range is +12 dB to -12 dB with 1dB increments. */
+	uint32_t     q_factor;
+} __packed;
+
+struct msm_audio_eq_stream_config {
+	uint32_t	enable; /* Number of consequtive bands specified */
+	uint32_t	num_bands;
+	struct msm_audio_eq_band	eq_bands[EQ_BAND_MAX];
+} __packed;
+
+/* Audio Sphere data structures */
+struct msm_audio_pp_asphere_state_s {
+	uint32_t enabled;
+	uint32_t strength;
+	uint32_t mode;
+	uint32_t version;
+	int  port_id[AFE_MAX_PORTS];
+	int  copp_idx[AFE_MAX_PORTS];
+	bool  initialized;
+	uint32_t enabled_prev;
+	uint32_t strength_prev;
+};
+
+static struct msm_audio_pp_asphere_state_s asphere_state;
+
+struct msm_audio_eq_stream_config	eq_data[MAX_EQ_SESSIONS];
+
+static int msm_route_hfp_vol_control;
+static const DECLARE_TLV_DB_LINEAR(hfp_rx_vol_gain, 0,
+				INT_RX_VOL_MAX_STEPS);
+
+static int msm_route_pri_auxpcm_lb_vol_ctrl;
+static const DECLARE_TLV_DB_LINEAR(pri_auxpcm_lb_vol_gain, 0,
+				INT_RX_VOL_MAX_STEPS);
+
+static int msm_route_sec_auxpcm_lb_vol_ctrl;
+static const DECLARE_TLV_DB_LINEAR(sec_auxpcm_lb_vol_gain, 0,
+				INT_RX_VOL_MAX_STEPS);
+
+static void msm_qti_pp_send_eq_values_(int eq_idx)
+{
+	int result;
+	struct msm_pcm_routing_fdai_data fe_dai;
+	struct audio_client *ac = NULL;
+
+	msm_pcm_routing_get_fedai_info(eq_idx, SESSION_TYPE_RX, &fe_dai);
+	ac = q6asm_get_audio_client(fe_dai.strm_id);
+
+	if (ac == NULL) {
+		pr_err("%s: Could not get audio client for session: %d\n",
+		      __func__, fe_dai.strm_id);
+		goto done;
+	}
+
+	result = q6asm_equalizer(ac, &eq_data[eq_idx]);
+
+	if (result < 0)
+		pr_err("%s: Call to ASM equalizer failed, returned = %d\n",
+		      __func__, result);
+done:
+	return;
+}
+
+static int msm_qti_pp_get_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	int eq_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+
+	if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+		return -EINVAL;
+
+	ucontrol->value.integer.value[0] = eq_data[eq_idx].enable;
+
+	pr_debug("%s: EQ #%d enable %d\n", __func__,
+		eq_idx, eq_data[eq_idx].enable);
+	return 0;
+}
+
+static int msm_qti_pp_put_eq_enable_mixer(struct snd_kcontrol *kcontrol,
+					  struct snd_ctl_elem_value *ucontrol)
+{
+	int eq_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int value = ucontrol->value.integer.value[0];
+
+	if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+		return -EINVAL;
+	pr_debug("%s: EQ #%d enable %d\n", __func__,
+		eq_idx, value);
+	eq_data[eq_idx].enable = value;
+	msm_pcm_routing_acquire_lock();
+	msm_qti_pp_send_eq_values_(eq_idx);
+	msm_pcm_routing_release_lock();
+	return 0;
+}
+
+static int msm_qti_pp_get_eq_band_count_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int eq_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+
+	if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+		return -EINVAL;
+	ucontrol->value.integer.value[0] = eq_data[eq_idx].num_bands;
+
+	pr_debug("%s: EQ #%d bands %d\n", __func__,
+		eq_idx, eq_data[eq_idx].num_bands);
+	return eq_data[eq_idx].num_bands;
+}
+
+static int msm_qti_pp_put_eq_band_count_audio_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	int eq_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int value = ucontrol->value.integer.value[0];
+
+	if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS))
+		return -EINVAL;
+
+	pr_debug("%s: EQ #%d bands %d\n", __func__,
+		eq_idx, value);
+	eq_data[eq_idx].num_bands = value;
+	return 0;
+}
+
+static int msm_qti_pp_get_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	int eq_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) ||
+	    (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX))
+		return -EINVAL;
+
+	ucontrol->value.integer.value[0] =
+			eq_data[eq_idx].eq_bands[band_idx].band_idx;
+	ucontrol->value.integer.value[1] =
+			eq_data[eq_idx].eq_bands[band_idx].filter_type;
+	ucontrol->value.integer.value[2] =
+			eq_data[eq_idx].eq_bands[band_idx].center_freq_hz;
+	ucontrol->value.integer.value[3] =
+			eq_data[eq_idx].eq_bands[band_idx].filter_gain;
+	ucontrol->value.integer.value[4] =
+			eq_data[eq_idx].eq_bands[band_idx].q_factor;
+
+	pr_debug("%s: band_idx = %d\n", __func__,
+			eq_data[eq_idx].eq_bands[band_idx].band_idx);
+	pr_debug("%s: filter_type = %d\n", __func__,
+			eq_data[eq_idx].eq_bands[band_idx].filter_type);
+	pr_debug("%s: center_freq_hz = %d\n", __func__,
+			eq_data[eq_idx].eq_bands[band_idx].center_freq_hz);
+	pr_debug("%s: filter_gain = %d\n", __func__,
+			eq_data[eq_idx].eq_bands[band_idx].filter_gain);
+	pr_debug("%s: q_factor = %d\n", __func__,
+			eq_data[eq_idx].eq_bands[band_idx].q_factor);
+	return 0;
+}
+
+static int msm_qti_pp_put_eq_band_audio_mixer(struct snd_kcontrol *kcontrol,
+				   struct snd_ctl_elem_value *ucontrol)
+{
+	int eq_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->reg;
+	int band_idx = ((struct soc_multi_mixer_control *)
+					kcontrol->private_value)->shift;
+
+	if ((eq_idx < 0) || (eq_idx >= MAX_EQ_SESSIONS) ||
+	    (band_idx < EQ_BAND1) || (band_idx >= EQ_BAND_MAX))
+		return -EINVAL;
+
+	eq_data[eq_idx].eq_bands[band_idx].band_idx =
+					ucontrol->value.integer.value[0];
+	eq_data[eq_idx].eq_bands[band_idx].filter_type =
+					ucontrol->value.integer.value[1];
+	eq_data[eq_idx].eq_bands[band_idx].center_freq_hz =
+					ucontrol->value.integer.value[2];
+	eq_data[eq_idx].eq_bands[band_idx].filter_gain =
+					ucontrol->value.integer.value[3];
+	eq_data[eq_idx].eq_bands[band_idx].q_factor =
+					ucontrol->value.integer.value[4];
+	return 0;
+}
+
+#ifdef CONFIG_QTI_PP
+void msm_qti_pp_send_eq_values(int fedai_id)
+{
+	if (eq_data[fedai_id].enable)
+		msm_qti_pp_send_eq_values_(fedai_id);
+}
+
+/* CUSTOM MIXING */
+int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
+						unsigned int session_id,
+						uint16_t op_FL_ip_FL_weight,
+						uint16_t op_FL_ip_FR_weight,
+						uint16_t op_FR_ip_FL_weight,
+						uint16_t op_FR_ip_FR_weight)
+{
+	char *params_value;
+	int *update_params_value32, rc = 0;
+	int16_t *update_params_value16 = 0;
+	uint32_t params_length = CUSTOM_STEREO_PAYLOAD_SIZE * sizeof(uint32_t);
+	uint32_t avail_length = params_length;
+
+	pr_debug("%s: port_id - %d, session id - %d\n", __func__, port_id,
+		 session_id);
+	params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		pr_err("%s, params memory alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+	update_params_value32 = (int *)params_value;
+	if (avail_length < 2 * sizeof(uint32_t))
+		goto skip_send_cmd;
+	*update_params_value32++ = MTMX_MODULE_ID_DEFAULT_CHMIXER;
+	*update_params_value32++ = DEFAULT_CHMIXER_PARAM_ID_COEFF;
+	avail_length = avail_length - (2 * sizeof(uint32_t));
+
+	update_params_value16 = (int16_t *)update_params_value32;
+	if (avail_length < 10 * sizeof(uint16_t))
+		goto skip_send_cmd;
+	*update_params_value16++ = CUSTOM_STEREO_CMD_PARAM_SIZE;
+	/*for alignment only*/
+	*update_params_value16++ = 0;
+	/*index is 32-bit param in little endian*/
+	*update_params_value16++ = CUSTOM_STEREO_INDEX_PARAM;
+	*update_params_value16++ = 0;
+	/*for stereo mixing num out ch*/
+	*update_params_value16++ = CUSTOM_STEREO_NUM_OUT_CH;
+	/*for stereo mixing num in ch*/
+	*update_params_value16++ = CUSTOM_STEREO_NUM_IN_CH;
+
+	/* Out ch map FL/FR*/
+	*update_params_value16++ = PCM_CHANNEL_FL;
+	*update_params_value16++ = PCM_CHANNEL_FR;
+
+	/* In ch map FL/FR*/
+	*update_params_value16++ = PCM_CHANNEL_FL;
+	*update_params_value16++ = PCM_CHANNEL_FR;
+	avail_length = avail_length - (10 * sizeof(uint16_t));
+	/* weighting coefficients as name suggests,
+	 * mixing will be done according to these coefficients
+	 */
+	if (avail_length < 4 * sizeof(uint16_t))
+		goto skip_send_cmd;
+	*update_params_value16++ = op_FL_ip_FL_weight;
+	*update_params_value16++ = op_FL_ip_FR_weight;
+	*update_params_value16++ = op_FR_ip_FL_weight;
+	*update_params_value16++ = op_FR_ip_FR_weight;
+	avail_length = avail_length - (4 * sizeof(uint16_t));
+	if (params_length) {
+		rc = adm_set_stereo_to_custom_stereo(port_id,
+						     copp_idx,
+						     session_id,
+						     params_value,
+						     params_length);
+		if (rc) {
+			pr_err("%s: send params failed rc=%d\n", __func__, rc);
+			kfree(params_value);
+			return -EINVAL;
+		}
+	}
+	kfree(params_value);
+	return 0;
+skip_send_cmd:
+		pr_err("%s: insufficient memory, send cmd failed\n",
+			__func__);
+		kfree(params_value);
+		return -ENOMEM;
+}
+#endif /* CONFIG_QTI_PP */
+
+/* RMS */
+static int msm_qti_pp_get_rms_value_control(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	int rc = 0;
+	int be_idx = 0, copp_idx;
+	char *param_value;
+	int *update_param_value;
+	uint32_t param_length = sizeof(uint32_t);
+	uint32_t param_payload_len = RMS_PAYLOAD_LEN * sizeof(uint32_t);
+	struct msm_pcm_routing_bdai_data msm_bedai;
+
+	param_value = kzalloc(param_length, GFP_KERNEL);
+	if (!param_value)
+		return -ENOMEM;
+
+	msm_pcm_routing_acquire_lock();
+	for (be_idx = 0; be_idx < MSM_BACKEND_DAI_MAX; be_idx++) {
+		msm_pcm_routing_get_bedai_info(be_idx, &msm_bedai);
+		if (msm_bedai.port_id == SLIMBUS_0_TX)
+			break;
+	}
+	if ((be_idx >= MSM_BACKEND_DAI_MAX) || !msm_bedai.active) {
+		pr_err("%s, back not active to query rms be_idx:%d\n",
+			__func__, be_idx);
+		rc = -EINVAL;
+		goto get_rms_value_err;
+	}
+	copp_idx = adm_get_default_copp_idx(SLIMBUS_0_TX);
+	if ((copp_idx < 0) || (copp_idx > MAX_COPPS_PER_PORT)) {
+		pr_err("%s, no active copp to query rms copp_idx:%d\n",
+			__func__, copp_idx);
+		rc = -EINVAL;
+		goto get_rms_value_err;
+	}
+	rc = adm_get_params(SLIMBUS_0_TX, copp_idx,
+			RMS_MODULEID_APPI_PASSTHRU,
+			RMS_PARAM_FIRST_SAMPLE,
+			param_length + param_payload_len,
+			param_value);
+	if (rc) {
+		pr_err("%s: get parameters failed rc=%d\n", __func__, rc);
+		rc = -EINVAL;
+		goto get_rms_value_err;
+	}
+	update_param_value = (int *)param_value;
+	ucontrol->value.integer.value[0] = update_param_value[0];
+
+	pr_debug("%s: FROM DSP value[0] 0x%x\n",
+		__func__, update_param_value[0]);
+get_rms_value_err:
+	msm_pcm_routing_release_lock();
+	kfree(param_value);
+	return rc;
+}
+
+static int msm_qti_pp_put_rms_value_control(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	/* not used */
+	return 0;
+}
+
+/* VOLUME */
+static int msm_route_fm_vol_control;
+static int msm_afe_lb_vol_ctrl;
+static int msm_afe_sec_mi2s_lb_vol_ctrl;
+static int msm_afe_tert_mi2s_lb_vol_ctrl;
+static int msm_afe_quat_mi2s_lb_vol_ctrl;
+static int msm_afe_slimbus_8_lb_vol_ctrl;
+static const DECLARE_TLV_DB_LINEAR(fm_rx_vol_gain, 0, INT_RX_VOL_MAX_STEPS);
+static const DECLARE_TLV_DB_LINEAR(afe_lb_vol_gain, 0, INT_RX_VOL_MAX_STEPS);
+
+static int msm_qti_pp_get_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_route_fm_vol_control;
+	return 0;
+}
+
+static int msm_qti_pp_set_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	afe_loopback_gain(INT_FM_TX, ucontrol->value.integer.value[0]);
+
+	msm_route_fm_vol_control = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_afe_lb_vol_ctrl;
+	return 0;
+}
+
+static int msm_qti_pp_set_pri_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	afe_loopback_gain(AFE_PORT_ID_PRIMARY_MI2S_TX,
+			  ucontrol->value.integer.value[0]);
+
+	msm_afe_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_afe_sec_mi2s_lb_vol_ctrl;
+	return 0;
+}
+
+static int msm_qti_pp_set_sec_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	afe_loopback_gain(AFE_PORT_ID_SECONDARY_MI2S_TX,
+			  ucontrol->value.integer.value[0]);
+	msm_afe_sec_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_afe_tert_mi2s_lb_vol_ctrl;
+	return 0;
+}
+
+static int msm_qti_pp_set_tert_mi2s_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	afe_loopback_gain(AFE_PORT_ID_TERTIARY_MI2S_TX,
+			  ucontrol->value.integer.value[0]);
+	msm_afe_tert_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0];
+	return 0;
+}
+
+static int msm_qti_pp_get_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_afe_slimbus_8_lb_vol_ctrl;
+	return 0;
+}
+
+static int msm_qti_pp_set_slimbus_8_lb_vol_mixer(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	int ret = 0;
+
+	ret = afe_loopback_gain(SLIMBUS_8_TX,
+				ucontrol->value.integer.value[0]);
+
+	if (ret)
+		pr_err("%s: failed to set LB vol for SLIMBUS_8_TX", __func__);
+	else
+		msm_afe_slimbus_8_lb_vol_ctrl =
+				ucontrol->value.integer.value[0];
+
+	return ret;
+}
+
+static int msm_qti_pp_get_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+				       struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_afe_quat_mi2s_lb_vol_ctrl;
+	return 0;
+}
+
+static int msm_qti_pp_set_quat_mi2s_fm_vol_mixer(struct snd_kcontrol *kcontrol,
+			    struct snd_ctl_elem_value *ucontrol)
+{
+	afe_loopback_gain(AFE_PORT_ID_QUATERNARY_MI2S_TX,
+			  ucontrol->value.integer.value[0]);
+
+	msm_afe_quat_mi2s_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_hfp_vol_mixer(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_route_hfp_vol_control;
+	return 0;
+}
+
+static int msm_qti_pp_set_hfp_vol_mixer(struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	afe_loopback_gain(INT_BT_SCO_TX, ucontrol->value.integer.value[0]);
+
+	msm_route_hfp_vol_control = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_pri_auxpcm_lb_vol_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_route_pri_auxpcm_lb_vol_ctrl;
+	pr_debug("%s: Volume = %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_qti_pp_set_pri_auxpcm_lb_vol_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+	(struct soc_mixer_control *)kcontrol->private_value;
+
+	afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]);
+
+	msm_route_pri_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_sec_auxpcm_lb_vol_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	ucontrol->value.integer.value[0] = msm_route_sec_auxpcm_lb_vol_ctrl;
+	pr_debug("%s: Volume = %ld\n", __func__,
+		ucontrol->value.integer.value[0]);
+	return 0;
+}
+
+static int msm_qti_pp_set_sec_auxpcm_lb_vol_mixer(
+					struct snd_kcontrol *kcontrol,
+					struct snd_ctl_elem_value *ucontrol)
+{
+	struct soc_mixer_control *mc =
+	(struct soc_mixer_control *)kcontrol->private_value;
+
+	afe_loopback_gain(mc->reg, ucontrol->value.integer.value[0]);
+
+	msm_route_sec_auxpcm_lb_vol_ctrl = ucontrol->value.integer.value[0];
+
+	return 0;
+}
+
+static int msm_qti_pp_get_channel_map_mixer(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL];
+	int i;
+
+	adm_get_multi_ch_map(channel_map, ADM_PATH_PLAYBACK);
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+		ucontrol->value.integer.value[i] =
+			(unsigned int) channel_map[i];
+	return 0;
+}
+
+static int msm_qti_pp_put_channel_map_mixer(struct snd_kcontrol *kcontrol,
+					    struct snd_ctl_elem_value *ucontrol)
+{
+	char channel_map[PCM_FORMAT_MAX_NUM_CHANNEL];
+	int i;
+
+	for (i = 0; i < PCM_FORMAT_MAX_NUM_CHANNEL; i++)
+		channel_map[i] = (char)(ucontrol->value.integer.value[i]);
+	adm_set_multi_ch_map(channel_map, ADM_PATH_PLAYBACK);
+
+	return 0;
+}
+
+/* Audio Sphere functions */
+
+static void msm_qti_pp_asphere_init_state(void)
+{
+	int i;
+
+	if (asphere_state.initialized)
+		return;
+	asphere_state.initialized = true;
+	for (i = 0; i < AFE_MAX_PORTS; i++) {
+		asphere_state.port_id[i] = -1;
+		asphere_state.copp_idx[i] = -1;
+	}
+	asphere_state.enabled = 0;
+	asphere_state.strength = 0;
+	asphere_state.mode = 0;
+	asphere_state.version = 0;
+	asphere_state.enabled_prev = 0;
+	asphere_state.strength_prev = 0;
+}
+
+static int msm_qti_pp_asphere_send_params(int port_id, int copp_idx, bool force)
+{
+	char *params_value = NULL;
+	uint32_t *update_params_value = NULL;
+	uint32_t param_size = sizeof(uint32_t) +
+			sizeof(struct adm_param_data_v5);
+	int params_length = 0, param_count = 0, ret = 0;
+	bool set_enable = force ||
+			(asphere_state.enabled != asphere_state.enabled_prev);
+	bool set_strength = asphere_state.enabled == 1 && (set_enable ||
+		(asphere_state.strength != asphere_state.strength_prev));
+
+	if (set_enable)
+		param_count++;
+	if (set_strength)
+		param_count++;
+	params_length = param_count * param_size;
+
+	pr_debug("%s: port_id %d, copp_id %d, forced %d, param_count %d\n",
+			__func__, port_id, copp_idx, force, param_count);
+	pr_debug("%s: enable prev:%u cur:%u, strength prev:%u cur:%u\n",
+		__func__, asphere_state.enabled_prev, asphere_state.enabled,
+		asphere_state.strength_prev, asphere_state.strength);
+
+	if (params_length > 0)
+		params_value = kzalloc(params_length, GFP_KERNEL);
+	if (!params_value) {
+		pr_err("%s, params memory alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+	update_params_value = (uint32_t *)params_value;
+	params_length = 0;
+	if (set_strength) {
+		/* add strength command */
+		*update_params_value++ = AUDPROC_MODULE_ID_AUDIOSPHERE;
+		*update_params_value++ = AUDPROC_PARAM_ID_AUDIOSPHERE_STRENGTH;
+		*update_params_value++ = sizeof(uint32_t);
+		*update_params_value++ = asphere_state.strength;
+		params_length += param_size;
+	}
+	if (set_enable) {
+		/* add enable command */
+		*update_params_value++ = AUDPROC_MODULE_ID_AUDIOSPHERE;
+		*update_params_value++ = AUDPROC_PARAM_ID_AUDIOSPHERE_ENABLE;
+		*update_params_value++ = sizeof(uint32_t);
+		*update_params_value++ = asphere_state.enabled;
+		params_length += param_size;
+	}
+	pr_debug("%s, param length: %d\n", __func__, params_length);
+	if (params_length) {
+		ret = adm_send_params_v5(port_id, copp_idx,
+					params_value, params_length);
+		if (ret) {
+			pr_err("%s: setting param failed with err=%d\n",
+				__func__, ret);
+			kfree(params_value);
+			return -EINVAL;
+		}
+	}
+	kfree(params_value);
+	return 0;
+}
+
+#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE)
+int msm_qti_pp_asphere_init(int port_id, int copp_idx)
+{
+	int index = adm_validate_and_get_port_index(port_id);
+
+	pr_debug("%s, port_id %d, copp_id %d\n", __func__, port_id, copp_idx);
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+			port_id);
+		return -EINVAL;
+	}
+	msm_qti_pp_asphere_init_state();
+
+	asphere_state.port_id[index] = port_id;
+	asphere_state.copp_idx[index] = copp_idx;
+
+	if (asphere_state.enabled)
+		msm_qti_pp_asphere_send_params(port_id, copp_idx, true);
+
+	return 0;
+}
+
+void msm_qti_pp_asphere_deinit(int port_id)
+{
+	int index = adm_validate_and_get_port_index(port_id);
+
+	pr_debug("%s, port_id %d\n", __func__, port_id);
+	if (index < 0) {
+		pr_err("%s: Invalid port idx %d port_id %#x\n", __func__, index,
+			port_id);
+		return;
+	}
+
+	if (asphere_state.port_id[index] == port_id) {
+		asphere_state.port_id[index] = -1;
+		asphere_state.copp_idx[index] = -1;
+	}
+}
+#endif
+
+static int msm_qti_pp_asphere_get(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	if (!asphere_state.initialized)
+		return -EAGAIN;
+	ucontrol->value.integer.value[0] = asphere_state.enabled;
+	ucontrol->value.integer.value[1] = asphere_state.strength;
+	pr_debug("%s, enable %u, strength %u\n", __func__,
+			asphere_state.enabled, asphere_state.strength);
+	return 0;
+}
+
+static int msm_qti_pp_asphere_set(struct snd_kcontrol *kcontrol,
+			struct snd_ctl_elem_value *ucontrol)
+{
+	int32_t enable = ucontrol->value.integer.value[0];
+	int32_t strength = ucontrol->value.integer.value[1];
+	int i;
+
+	pr_debug("%s, enable %u, strength %u\n", __func__, enable, strength);
+
+	msm_qti_pp_asphere_init_state();
+
+	if (enable == 0 || enable == 1) {
+		asphere_state.enabled_prev = asphere_state.enabled;
+		asphere_state.enabled = enable;
+	}
+
+	if (strength >= 0 && strength <= 1000) {
+		asphere_state.strength_prev = asphere_state.strength;
+		asphere_state.strength = strength;
+	}
+
+	if (asphere_state.strength != asphere_state.strength_prev ||
+		asphere_state.enabled != asphere_state.enabled_prev) {
+		for (i = 0; i < AFE_MAX_PORTS; i++) {
+			if (asphere_state.port_id[i] >= 0)
+				msm_qti_pp_asphere_send_params(
+					asphere_state.port_id[i],
+					asphere_state.copp_idx[i],
+					false);
+		}
+	}
+	return 0;
+}
+
+static const struct snd_kcontrol_new int_fm_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("Internal FM RX Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_fm_vol_mixer,
+	msm_qti_pp_set_fm_vol_mixer, fm_rx_vol_gain),
+	SOC_SINGLE_EXT_TLV("Quat MI2S FM RX Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_quat_mi2s_fm_vol_mixer,
+	msm_qti_pp_set_quat_mi2s_fm_vol_mixer, fm_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new pri_mi2s_lb_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("PRI MI2S LOOPBACK Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_pri_mi2s_lb_vol_mixer,
+	msm_qti_pp_set_pri_mi2s_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new sec_mi2s_lb_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("SEC MI2S LOOPBACK Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_sec_mi2s_lb_vol_mixer,
+	msm_qti_pp_set_sec_mi2s_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new tert_mi2s_lb_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("Tert MI2S LOOPBACK Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_tert_mi2s_lb_vol_mixer,
+	msm_qti_pp_set_tert_mi2s_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new slimbus_8_lb_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("SLIMBUS_8 LOOPBACK Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_slimbus_8_lb_vol_mixer,
+	msm_qti_pp_set_slimbus_8_lb_vol_mixer, afe_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new int_hfp_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("Internal HFP RX Volume", SND_SOC_NOPM, 0,
+	INT_RX_VOL_GAIN, 0, msm_qti_pp_get_hfp_vol_mixer,
+	msm_qti_pp_set_hfp_vol_mixer, hfp_rx_vol_gain),
+};
+
+static const struct snd_kcontrol_new pri_auxpcm_lb_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("PRI AUXPCM LOOPBACK Volume",
+	AFE_PORT_ID_PRIMARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0,
+	msm_qti_pp_get_pri_auxpcm_lb_vol_mixer,
+	msm_qti_pp_set_pri_auxpcm_lb_vol_mixer,
+	pri_auxpcm_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new sec_auxpcm_lb_vol_mixer_controls[] = {
+	SOC_SINGLE_EXT_TLV("SEC AUXPCM LOOPBACK Volume",
+	AFE_PORT_ID_SECONDARY_PCM_TX, 0, INT_RX_VOL_GAIN, 0,
+	msm_qti_pp_get_sec_auxpcm_lb_vol_mixer,
+	msm_qti_pp_set_sec_auxpcm_lb_vol_mixer,
+	sec_auxpcm_lb_vol_gain),
+};
+
+static const struct snd_kcontrol_new multi_ch_channel_map_mixer_controls[] = {
+	SOC_SINGLE_MULTI_EXT("Playback Device Channel Map", SND_SOC_NOPM, 0, 16,
+	0, 8, msm_qti_pp_get_channel_map_mixer,
+	msm_qti_pp_put_channel_map_mixer),
+};
+
+
+static const struct snd_kcontrol_new get_rms_controls[] = {
+	SOC_SINGLE_EXT("Get RMS", SND_SOC_NOPM, 0, 0xFFFFFFFF,
+	0, msm_qti_pp_get_rms_value_control, msm_qti_pp_put_rms_value_control),
+};
+
+static const struct snd_kcontrol_new eq_enable_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1 EQ Enable", SND_SOC_NOPM,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 1, 0, msm_qti_pp_get_eq_enable_mixer,
+	msm_qti_pp_put_eq_enable_mixer),
+	SOC_SINGLE_EXT("MultiMedia2 EQ Enable", SND_SOC_NOPM,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 1, 0, msm_qti_pp_get_eq_enable_mixer,
+	msm_qti_pp_put_eq_enable_mixer),
+	SOC_SINGLE_EXT("MultiMedia3 EQ Enable", SND_SOC_NOPM,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 1, 0, msm_qti_pp_get_eq_enable_mixer,
+	msm_qti_pp_put_eq_enable_mixer),
+};
+
+static const struct snd_kcontrol_new eq_band_mixer_controls[] = {
+	SOC_SINGLE_EXT("MultiMedia1 EQ Band Count", SND_SOC_NOPM,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 11, 0,
+	msm_qti_pp_get_eq_band_count_audio_mixer,
+	msm_qti_pp_put_eq_band_count_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia2 EQ Band Count", SND_SOC_NOPM,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 11, 0,
+	msm_qti_pp_get_eq_band_count_audio_mixer,
+	msm_qti_pp_put_eq_band_count_audio_mixer),
+	SOC_SINGLE_EXT("MultiMedia3 EQ Band Count", SND_SOC_NOPM,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 11, 0,
+	msm_qti_pp_get_eq_band_count_audio_mixer,
+	msm_qti_pp_put_eq_band_count_audio_mixer),
+};
+
+static const struct snd_kcontrol_new eq_coeff_mixer_controls[] = {
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band1", EQ_BAND1,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band2", EQ_BAND2,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band3", EQ_BAND3,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band4", EQ_BAND4,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band5", EQ_BAND5,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band6", EQ_BAND6,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band7", EQ_BAND7,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band8", EQ_BAND8,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band9", EQ_BAND9,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band10", EQ_BAND10,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band11", EQ_BAND11,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia1 EQ Band12", EQ_BAND12,
+	MSM_FRONTEND_DAI_MULTIMEDIA1, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band1", EQ_BAND1,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band2", EQ_BAND2,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band3", EQ_BAND3,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band4", EQ_BAND4,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band5", EQ_BAND5,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band6", EQ_BAND6,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band7", EQ_BAND7,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band8", EQ_BAND8,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band9", EQ_BAND9,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band10", EQ_BAND10,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band11", EQ_BAND11,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia2 EQ Band12", EQ_BAND12,
+	MSM_FRONTEND_DAI_MULTIMEDIA2, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band1", EQ_BAND1,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band2", EQ_BAND2,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band3", EQ_BAND3,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band4", EQ_BAND4,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band5", EQ_BAND5,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band6", EQ_BAND6,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band7", EQ_BAND7,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band8", EQ_BAND8,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band9", EQ_BAND9,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band10", EQ_BAND10,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band11", EQ_BAND11,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+	SOC_SINGLE_MULTI_EXT("MultiMedia3 EQ Band12", EQ_BAND12,
+	MSM_FRONTEND_DAI_MULTIMEDIA3, 255, 0, 5,
+	msm_qti_pp_get_eq_band_audio_mixer, msm_qti_pp_put_eq_band_audio_mixer),
+};
+
+static const struct snd_kcontrol_new asphere_mixer_controls[] = {
+	SOC_SINGLE_MULTI_EXT("MSM ASphere Set Param", SND_SOC_NOPM, 0,
+	0xFFFFFFFF, 0, 2, msm_qti_pp_asphere_get, msm_qti_pp_asphere_set),
+};
+
+#ifdef CONFIG_QTI_PP
+void msm_qti_pp_add_controls(struct snd_soc_platform *platform)
+{
+	snd_soc_add_platform_controls(platform, int_fm_vol_mixer_controls,
+			ARRAY_SIZE(int_fm_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, pri_mi2s_lb_vol_mixer_controls,
+			ARRAY_SIZE(pri_mi2s_lb_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, sec_mi2s_lb_vol_mixer_controls,
+			ARRAY_SIZE(sec_mi2s_lb_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, tert_mi2s_lb_vol_mixer_controls,
+			ARRAY_SIZE(tert_mi2s_lb_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, slimbus_8_lb_vol_mixer_controls,
+			ARRAY_SIZE(slimbus_8_lb_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, int_hfp_vol_mixer_controls,
+			ARRAY_SIZE(int_hfp_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform,
+			pri_auxpcm_lb_vol_mixer_controls,
+			ARRAY_SIZE(pri_auxpcm_lb_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform,
+				sec_auxpcm_lb_vol_mixer_controls,
+			ARRAY_SIZE(sec_auxpcm_lb_vol_mixer_controls));
+
+	snd_soc_add_platform_controls(platform,
+				multi_ch_channel_map_mixer_controls,
+			ARRAY_SIZE(multi_ch_channel_map_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, get_rms_controls,
+			ARRAY_SIZE(get_rms_controls));
+
+	snd_soc_add_platform_controls(platform, eq_enable_mixer_controls,
+			ARRAY_SIZE(eq_enable_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, eq_band_mixer_controls,
+			ARRAY_SIZE(eq_band_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, eq_coeff_mixer_controls,
+			ARRAY_SIZE(eq_coeff_mixer_controls));
+
+	snd_soc_add_platform_controls(platform, asphere_mixer_controls,
+			ARRAY_SIZE(asphere_mixer_controls));
+}
+#endif /* CONFIG_QTI_PP */
diff --git a/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
new file mode 100644
index 0000000..805fb3e
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/msm-qti-pp-config.h
@@ -0,0 +1,43 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _MSM_QTI_PP_H_
+#define _MSM_QTI_PP_H_
+
+#include <sound/soc.h>
+
+#ifdef CONFIG_QTI_PP
+void msm_qti_pp_send_eq_values(int fedai_id);
+int msm_qti_pp_send_stereo_to_custom_stereo_cmd(int port_id, int copp_idx,
+						unsigned int session_id,
+						uint16_t op_FL_ip_FL_weight,
+						uint16_t op_FL_ip_FR_weight,
+						uint16_t op_FR_ip_FL_weight,
+						uint16_t op_FR_ip_FR_weight);
+void msm_qti_pp_add_controls(struct snd_soc_platform *platform);
+#else /* CONFIG_QTI_PP */
+#define msm_qti_pp_send_eq_values(fedai_id) do {} while (0)
+#define msm_qti_pp_send_stereo_to_custom_stereo_cmd(port_id, copp_idx, \
+			session_id, op_FL_ip_FL_weight, op_FL_ip_FR_weight, \
+			op_FR_ip_FL_weight, op_FR_ip_FR_weight) (0)
+#define msm_qti_pp_add_controls(platform) do {} while (0)
+#endif /* CONFIG_QTI_PP */
+
+
+#if defined(CONFIG_QTI_PP) && defined(CONFIG_QTI_PP_AUDIOSPHERE)
+int msm_qti_pp_asphere_init(int port_id, int copp_idx);
+void msm_qti_pp_asphere_deinit(int port_id);
+#else
+#define msm_qti_pp_asphere_init(port_id, copp_idx) (0)
+#define msm_qti_pp_asphere_deinit(port_id) do {} while (0)
+#endif
+
+#endif /* _MSM_QTI_PP_H_ */
diff --git a/sound/soc/msm/qdsp6v2/q6adm.c b/sound/soc/msm/qdsp6v2/q6adm.c
new file mode 100644
index 0000000..26d2b80
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6adm.c
@@ -0,0 +1,4407 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <linux/wait.h>
+#include <linux/qdsp6v2/apr.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6adm-v2.h>
+#include <sound/q6audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/asound.h>
+#include <sound/msm-dts-eagle.h>
+#include "msm-dts-srs-tm-config.h"
+#include <sound/adsp_err.h>
+
+#define TIMEOUT_MS 1000
+
+#define RESET_COPP_ID 99
+#define INVALID_COPP_ID 0xFF
+/* Used for inband payload copy, max size is 4k */
+/* 2 is to account for module & param ID in payload */
+#define ADM_GET_PARAMETER_LENGTH  (4096 - APR_HDR_SIZE - 2 * sizeof(uint32_t))
+
+#define ULL_SUPPORTED_BITS_PER_SAMPLE 16
+#define ULL_SUPPORTED_SAMPLE_RATE 48000
+
+/* ENUM for adm_status */
+enum adm_cal_status {
+	ADM_STATUS_CALIBRATION_REQUIRED = 0,
+	ADM_STATUS_MAX,
+};
+
+struct adm_copp {
+
+	atomic_t id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t cnt[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t topology[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t mode[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t rate[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t bit_width[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t channels[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t app_type[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t acdb_id[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	wait_queue_head_t wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	wait_queue_head_t adm_delay_wait[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	atomic_t adm_delay_stat[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	uint32_t adm_delay[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+	unsigned long adm_status[AFE_MAX_PORTS][MAX_COPPS_PER_PORT];
+};
+
+struct source_tracking_data {
+	struct ion_client *ion_client;
+	struct ion_handle *ion_handle;
+	struct param_outband memmap;
+	int apr_cmd_status;
+};
+
+struct adm_ctl {
+	void *apr;
+
+	struct adm_copp copp;
+
+	atomic_t matrix_map_stat;
+	wait_queue_head_t matrix_map_wait;
+
+	atomic_t adm_stat;
+	wait_queue_head_t adm_wait;
+
+	struct cal_type_data *cal_data[ADM_MAX_CAL_TYPES];
+
+	atomic_t mem_map_handles[ADM_MEM_MAP_INDEX_MAX];
+	atomic_t mem_map_index;
+
+	struct param_outband outband_memmap;
+	struct source_tracking_data sourceTrackingData;
+
+	int set_custom_topology;
+	int ec_ref_rx;
+};
+
+static struct adm_ctl			this_adm;
+
+struct adm_multi_ch_map {
+	bool set_channel_map;
+	char channel_mapping[PCM_FORMAT_MAX_NUM_CHANNEL];
+};
+
+#define ADM_MCH_MAP_IDX_PLAYBACK 0
+#define ADM_MCH_MAP_IDX_REC 1
+static struct adm_multi_ch_map multi_ch_maps[2] = {
+							{ false,
+							{0, 0, 0, 0, 0, 0, 0, 0}
+							},
+							{ false,
+							{0, 0, 0, 0, 0, 0, 0, 0}
+							}
+};
+
+static int adm_get_parameters[MAX_COPPS_PER_PORT * ADM_GET_PARAMETER_LENGTH];
+static int adm_module_topo_list[
+	MAX_COPPS_PER_PORT * ADM_GET_TOPO_MODULE_LIST_LENGTH];
+
+int adm_validate_and_get_port_index(int port_id)
+{
+	int index;
+	int ret;
+
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port validation failed id 0x%x ret %d\n",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	index = afe_get_port_index(port_id);
+	if (index < 0 || index >= AFE_MAX_PORTS) {
+		pr_err("%s: Invalid port idx %d port_id 0x%x\n",
+			__func__, index,
+			port_id);
+		return -EINVAL;
+	}
+	pr_debug("%s: port_idx- %d\n", __func__, index);
+	return index;
+}
+
+int adm_get_default_copp_idx(int port_id)
+{
+	int port_idx = adm_validate_and_get_port_index(port_id), idx;
+
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port id: 0x%x", __func__, port_id);
+		return -EINVAL;
+	}
+	pr_debug("%s: port_idx:%d\n", __func__, port_idx);
+	for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+		if (atomic_read(&this_adm.copp.id[port_idx][idx]) !=
+			RESET_COPP_ID)
+			return idx;
+	}
+	return -EINVAL;
+}
+
+int adm_get_topology_for_port_from_copp_id(int port_id, int copp_id)
+{
+	int port_idx = adm_validate_and_get_port_index(port_id), idx;
+
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port id: 0x%x", __func__, port_id);
+		return 0;
+	}
+	for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+		if (atomic_read(&this_adm.copp.id[port_idx][idx]) == copp_id)
+			return atomic_read(&this_adm.copp.topology[port_idx]
+								  [idx]);
+	pr_err("%s: Invalid copp_id %d port_id 0x%x\n",
+		__func__, copp_id, port_id);
+	return 0;
+}
+
+int adm_get_topology_for_port_copp_idx(int port_id, int copp_idx)
+{
+	int port_idx = adm_validate_and_get_port_index(port_id);
+
+	if (port_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid port: 0x%x copp id: 0x%x",
+				__func__, port_id, copp_idx);
+		return 0;
+	}
+	return atomic_read(&this_adm.copp.topology[port_idx][copp_idx]);
+}
+
+int adm_get_indexes_from_copp_id(int copp_id, int *copp_idx, int *port_idx)
+{
+	int p_idx, c_idx;
+
+	for (p_idx = 0; p_idx < AFE_MAX_PORTS; p_idx++) {
+		for (c_idx = 0; c_idx < MAX_COPPS_PER_PORT; c_idx++) {
+			if (atomic_read(&this_adm.copp.id[p_idx][c_idx])
+								== copp_id) {
+				if (copp_idx != NULL)
+					*copp_idx = c_idx;
+				if (port_idx != NULL)
+					*port_idx = p_idx;
+				return 0;
+			}
+		}
+	}
+	return -EINVAL;
+}
+
+static int adm_get_copp_id(int port_idx, int copp_idx)
+{
+	pr_debug("%s: port_idx:%d copp_idx:%d\n", __func__, port_idx, copp_idx);
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+	return atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+}
+
+static int adm_get_idx_if_copp_exists(int port_idx, int topology, int mode,
+				 int rate, int bit_width, int app_type)
+{
+	int idx;
+
+	pr_debug("%s: port_idx-%d, topology-0x%x, mode-%d, rate-%d, bit_width-%d\n",
+		 __func__, port_idx, topology, mode, rate, bit_width);
+
+	for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++)
+		if ((topology ==
+			atomic_read(&this_adm.copp.topology[port_idx][idx])) &&
+		    (mode == atomic_read(&this_adm.copp.mode[port_idx][idx])) &&
+		    (rate == atomic_read(&this_adm.copp.rate[port_idx][idx])) &&
+		    (bit_width ==
+			atomic_read(&this_adm.copp.bit_width[port_idx][idx])) &&
+		    (app_type ==
+			atomic_read(&this_adm.copp.app_type[port_idx][idx])))
+			return idx;
+	return -EINVAL;
+}
+
+static int adm_get_next_available_copp(int port_idx)
+{
+	int idx;
+
+	pr_debug("%s:\n", __func__);
+	for (idx = 0; idx < MAX_COPPS_PER_PORT; idx++) {
+		pr_debug("%s: copp_id:0x%x port_idx:%d idx:%d\n", __func__,
+			 atomic_read(&this_adm.copp.id[port_idx][idx]),
+			 port_idx, idx);
+		if (atomic_read(&this_adm.copp.id[port_idx][idx]) ==
+								RESET_COPP_ID)
+			break;
+	}
+	return idx;
+}
+
+int adm_dts_eagle_set(int port_id, int copp_idx, int param_id,
+		      void *data, uint32_t size)
+{
+	struct adm_cmd_set_pp_params_v5	admp;
+	int p_idx, ret = 0, *ob_params;
+
+	pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X size %u\n",
+		__func__, port_id, copp_idx, param_id, size);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	p_idx = adm_validate_and_get_port_index(port_id);
+	pr_debug("DTS_EAGLE_ADM: %s - after lookup, port id %i, port idx %i\n",
+		__func__, port_id, p_idx);
+
+	if (p_idx < 0) {
+		pr_err("DTS_EAGLE_ADM: %s: invalid port index 0x%x, port id 0x%x\n",
+			__func__, p_idx, port_id);
+		return -EINVAL;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__,
+			copp_idx);
+		return -EINVAL;
+	}
+
+	ob_params = (int *)this_adm.outband_memmap.kvaddr;
+	if (ob_params == NULL) {
+		pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?\n",
+			__func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	/* check for integer overflow */
+	if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
+		ret = -EINVAL;
+	if ((ret < 0) ||
+	    (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) {
+		pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n",
+			__func__, this_adm.outband_memmap.size,
+			size + APR_CMD_OB_HDR_SZ);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	*ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX;
+	*ob_params++ = param_id;
+	*ob_params++ = size;
+	memcpy(ob_params, data, size);
+
+	admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	admp.hdr.pkt_size = sizeof(admp);
+	admp.hdr.src_svc = APR_SVC_ADM;
+	admp.hdr.src_domain = APR_DOMAIN_APPS;
+	admp.hdr.src_port = port_id;
+	admp.hdr.dest_svc = APR_SVC_ADM;
+	admp.hdr.dest_domain = APR_DOMAIN_ADSP;
+	admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]);
+	admp.hdr.token = p_idx << 16 | copp_idx;
+	admp.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	admp.payload_addr_lsw = lower_32_bits(this_adm.outband_memmap.paddr);
+	admp.payload_addr_msw = msm_audio_populate_upper_32_bits(
+						this_adm.outband_memmap.paddr);
+	admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[
+					  ADM_DTS_EAGLE]);
+	admp.payload_size = size + sizeof(struct adm_param_data_v5);
+
+	pr_debug("DTS_EAGLE_ADM: %s - Command was sent now check Q6 - port id = %d, size %d, module id %x, param id %x.\n",
+			__func__, admp.hdr.dest_port,
+			admp.payload_size, AUDPROC_MODULE_ID_DTS_HPX_POSTMIX,
+			param_id);
+	atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp);
+	if (ret < 0) {
+		pr_err("DTS_EAGLE_ADM: %s - ADM enable for port %d failed\n",
+			__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx],
+			atomic_read(&this_adm.copp.stat
+			[p_idx][copp_idx]) >= 0,
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("DTS_EAGLE_ADM: %s - set params timed out port = %d\n",
+			__func__, port_id);
+		ret = -EINVAL;
+	} else if (atomic_read(&this_adm.copp.stat
+				[p_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[p_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+				[p_idx][copp_idx]));
+	} else {
+		ret = 0;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int adm_dts_eagle_get(int port_id, int copp_idx, int param_id,
+		      void *data, uint32_t size)
+{
+	struct adm_cmd_get_pp_params_v5	admp;
+	int p_idx, ret = 0, *ob_params;
+	uint32_t orig_size = size;
+
+	pr_debug("DTS_EAGLE_ADM: %s - port id %i, copp idx %i, param id 0x%X\n",
+		 __func__, port_id, copp_idx, param_id);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	p_idx = adm_validate_and_get_port_index(port_id);
+	if (p_idx < 0) {
+		pr_err("DTS_EAGLE_ADM: %s - invalid port index %i, port id %i, copp idx %i\n",
+				__func__, p_idx, port_id, copp_idx);
+		return -EINVAL;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("DTS_EAGLE_ADM: %s: Invalid copp_idx: %d\n", __func__,
+			copp_idx);
+		return -EINVAL;
+	}
+
+	if ((size == 0) || !data) {
+		pr_err("DTS_EAGLE_ADM: %s - invalid size %u or pointer %pK.\n",
+			__func__, size, data);
+		return -EINVAL;
+	}
+
+	size = (size+3) & 0xFFFFFFFC;
+
+	ob_params = (int *)(this_adm.outband_memmap.kvaddr);
+	if (ob_params == NULL) {
+		pr_err("DTS_EAGLE_ADM: %s - NULL memmap. Non Eagle topology selected?",
+			__func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	/* check for integer overflow */
+	if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
+		ret = -EINVAL;
+	if ((ret < 0) ||
+	    (size + APR_CMD_OB_HDR_SZ > this_adm.outband_memmap.size)) {
+		pr_err("DTS_EAGLE_ADM - %s: ion alloc of size %zu too small for size requested %u\n",
+			__func__, this_adm.outband_memmap.size,
+			size + APR_CMD_OB_HDR_SZ);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	*ob_params++ = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX;
+	*ob_params++ = param_id;
+	*ob_params++ = size;
+
+	admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			     APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	admp.hdr.pkt_size = sizeof(admp);
+	admp.hdr.src_svc = APR_SVC_ADM;
+	admp.hdr.src_domain = APR_DOMAIN_APPS;
+	admp.hdr.src_port = port_id;
+	admp.hdr.dest_svc = APR_SVC_ADM;
+	admp.hdr.dest_domain = APR_DOMAIN_ADSP;
+	admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]);
+	admp.hdr.token = p_idx << 16 | copp_idx;
+	admp.hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5;
+	admp.data_payload_addr_lsw =
+				lower_32_bits(this_adm.outband_memmap.paddr);
+	admp.data_payload_addr_msw =
+				msm_audio_populate_upper_32_bits(
+						this_adm.outband_memmap.paddr);
+	admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[
+					  ADM_DTS_EAGLE]);
+	admp.module_id = AUDPROC_MODULE_ID_DTS_HPX_POSTMIX;
+	admp.param_id = param_id;
+	admp.param_max_size = size + sizeof(struct adm_param_data_v5);
+	admp.reserved = 0;
+
+	atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1);
+
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp);
+	if (ret < 0) {
+		pr_err("DTS_EAGLE_ADM: %s - Failed to get EAGLE Params on port %d\n",
+			__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx],
+			atomic_read(&this_adm.copp.stat
+			[p_idx][copp_idx]) >= 0,
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("DTS_EAGLE_ADM: %s - EAGLE get params timed out port = %d\n",
+			__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[p_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[p_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[p_idx][copp_idx]));
+		goto fail_cmd;
+	}
+
+	memcpy(data, ob_params, orig_size);
+	ret = 0;
+fail_cmd:
+	return ret;
+}
+
+int srs_trumedia_open(int port_id, int copp_idx, __s32 srs_tech_id,
+		      void *srs_params)
+{
+	struct adm_cmd_set_pp_params_inband_v5 *adm_params = NULL;
+	struct adm_cmd_set_pp_params_v5 *adm_params_ = NULL;
+	__s32 sz = 0, param_id, module_id = SRS_TRUMEDIA_MODULE_ID, outband = 0;
+	int ret = 0, port_idx;
+
+	pr_debug("SRS - %s", __func__);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		return -EINVAL;
+	}
+	switch (srs_tech_id) {
+	case SRS_ID_GLOBAL: {
+		struct srs_trumedia_params_GLOBAL *glb_params = NULL;
+
+		sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
+			sizeof(struct srs_trumedia_params_GLOBAL);
+		adm_params = kzalloc(sz, GFP_KERNEL);
+		if (!adm_params) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+		adm_params->payload_size =
+			sizeof(struct srs_trumedia_params_GLOBAL) +
+			sizeof(struct adm_param_data_v5);
+		param_id = SRS_TRUMEDIA_PARAMS;
+		adm_params->params.param_size =
+				sizeof(struct srs_trumedia_params_GLOBAL);
+		glb_params = (struct srs_trumedia_params_GLOBAL *)
+			((u8 *)adm_params +
+			sizeof(struct adm_cmd_set_pp_params_inband_v5));
+		memcpy(glb_params, srs_params,
+			sizeof(struct srs_trumedia_params_GLOBAL));
+		break;
+	}
+	case SRS_ID_WOWHD: {
+		struct srs_trumedia_params_WOWHD *whd_params = NULL;
+
+		sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
+			sizeof(struct srs_trumedia_params_WOWHD);
+		adm_params = kzalloc(sz, GFP_KERNEL);
+		if (!adm_params) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+		adm_params->payload_size =
+			sizeof(struct srs_trumedia_params_WOWHD) +
+			sizeof(struct adm_param_data_v5);
+		param_id = SRS_TRUMEDIA_PARAMS_WOWHD;
+		adm_params->params.param_size =
+				sizeof(struct srs_trumedia_params_WOWHD);
+		whd_params = (struct srs_trumedia_params_WOWHD *)
+			((u8 *)adm_params +
+			sizeof(struct adm_cmd_set_pp_params_inband_v5));
+		memcpy(whd_params, srs_params,
+				sizeof(struct srs_trumedia_params_WOWHD));
+		break;
+	}
+	case SRS_ID_CSHP: {
+		struct srs_trumedia_params_CSHP *chp_params = NULL;
+
+		sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
+			sizeof(struct srs_trumedia_params_CSHP);
+		adm_params = kzalloc(sz, GFP_KERNEL);
+		if (!adm_params) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+		adm_params->payload_size =
+			sizeof(struct srs_trumedia_params_CSHP) +
+			sizeof(struct adm_param_data_v5);
+		param_id = SRS_TRUMEDIA_PARAMS_CSHP;
+		adm_params->params.param_size =
+				sizeof(struct srs_trumedia_params_CSHP);
+		chp_params = (struct srs_trumedia_params_CSHP *)
+			((u8 *)adm_params +
+			sizeof(struct adm_cmd_set_pp_params_inband_v5));
+		memcpy(chp_params, srs_params,
+				sizeof(struct srs_trumedia_params_CSHP));
+		break;
+	}
+	case SRS_ID_HPF: {
+		struct srs_trumedia_params_HPF *hpf_params = NULL;
+
+		sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
+			sizeof(struct srs_trumedia_params_HPF);
+		adm_params = kzalloc(sz, GFP_KERNEL);
+		if (!adm_params) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+		adm_params->payload_size =
+			sizeof(struct srs_trumedia_params_HPF) +
+			sizeof(struct adm_param_data_v5);
+		param_id = SRS_TRUMEDIA_PARAMS_HPF;
+		adm_params->params.param_size =
+				sizeof(struct srs_trumedia_params_HPF);
+		hpf_params = (struct srs_trumedia_params_HPF *)
+			((u8 *)adm_params +
+			sizeof(struct adm_cmd_set_pp_params_inband_v5));
+		memcpy(hpf_params, srs_params,
+			sizeof(struct srs_trumedia_params_HPF));
+		break;
+	}
+	case SRS_ID_AEQ: {
+		int *update_params_ptr = (int *)this_adm.outband_memmap.kvaddr;
+
+		outband = 1;
+		adm_params = kzalloc(sizeof(struct adm_cmd_set_pp_params_v5),
+				     GFP_KERNEL);
+		adm_params_ = (struct adm_cmd_set_pp_params_v5 *)adm_params;
+		if (!adm_params_) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+
+		sz = sizeof(struct srs_trumedia_params_AEQ);
+		if (update_params_ptr == NULL) {
+			pr_err("ADM_SRS_TRUMEDIA - %s: null memmap for AEQ params\n",
+				__func__);
+			ret = -EINVAL;
+			goto fail_cmd;
+		}
+		param_id = SRS_TRUMEDIA_PARAMS_AEQ;
+		*update_params_ptr++ = module_id;
+		*update_params_ptr++ = param_id;
+		*update_params_ptr++ = sz;
+		memcpy(update_params_ptr, srs_params, sz);
+
+		adm_params_->payload_size = sz + 12;
+
+		break;
+	}
+	case SRS_ID_HL: {
+		struct srs_trumedia_params_HL *hl_params = NULL;
+
+		sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
+			sizeof(struct srs_trumedia_params_HL);
+		adm_params = kzalloc(sz, GFP_KERNEL);
+		if (!adm_params) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+		adm_params->payload_size =
+			sizeof(struct srs_trumedia_params_HL) +
+			sizeof(struct adm_param_data_v5);
+		param_id = SRS_TRUMEDIA_PARAMS_HL;
+		adm_params->params.param_size =
+			sizeof(struct srs_trumedia_params_HL);
+		hl_params = (struct srs_trumedia_params_HL *)
+			((u8 *)adm_params +
+			sizeof(struct adm_cmd_set_pp_params_inband_v5));
+		memcpy(hl_params, srs_params,
+				sizeof(struct srs_trumedia_params_HL));
+		break;
+	}
+	case SRS_ID_GEQ: {
+		struct srs_trumedia_params_GEQ *geq_params = NULL;
+
+		sz = sizeof(struct adm_cmd_set_pp_params_inband_v5) +
+			sizeof(struct srs_trumedia_params_GEQ);
+		adm_params = kzalloc(sz, GFP_KERNEL);
+		if (!adm_params) {
+			pr_err("%s, adm params memory alloc failed\n",
+				__func__);
+			return -ENOMEM;
+		}
+		adm_params->payload_size =
+			sizeof(struct srs_trumedia_params_GEQ) +
+			sizeof(struct adm_param_data_v5);
+		param_id = SRS_TRUMEDIA_PARAMS_GEQ;
+		adm_params->params.param_size =
+			sizeof(struct srs_trumedia_params_GEQ);
+		geq_params = (struct srs_trumedia_params_GEQ *)
+			((u8 *)adm_params +
+			sizeof(struct adm_cmd_set_pp_params_inband_v5));
+		memcpy(geq_params, srs_params,
+			sizeof(struct srs_trumedia_params_GEQ));
+		pr_debug("SRS - %s: GEQ params prepared\n", __func__);
+		break;
+	}
+	default:
+		goto fail_cmd;
+	}
+
+	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_params->hdr.src_svc = APR_SVC_ADM;
+	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params->hdr.src_port = port_id;
+	adm_params->hdr.dest_svc = APR_SVC_ADM;
+	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_params->hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params->hdr.token = port_idx << 16 | copp_idx;
+	adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	if (outband && this_adm.outband_memmap.paddr) {
+		adm_params->hdr.pkt_size =
+					sizeof(struct adm_cmd_set_pp_params_v5);
+		adm_params->payload_addr_lsw = lower_32_bits(
+						this_adm.outband_memmap.paddr);
+		adm_params->payload_addr_msw = msm_audio_populate_upper_32_bits(
+						this_adm.outband_memmap.paddr);
+		adm_params->mem_map_handle = atomic_read(&this_adm.
+					mem_map_handles[ADM_SRS_TRUMEDIA]);
+	} else {
+		adm_params->hdr.pkt_size = sz;
+		adm_params->payload_addr_lsw = 0;
+		adm_params->payload_addr_msw = 0;
+		adm_params->mem_map_handle = 0;
+
+		adm_params->params.module_id = module_id;
+		adm_params->params.param_id = param_id;
+		adm_params->params.reserved = 0;
+	}
+
+	pr_debug("SRS - %s: Command was sent now check Q6 - port id = %d, size %d, module id %x, param id %x.\n",
+			__func__, adm_params->hdr.dest_port,
+			adm_params->payload_size, module_id, param_id);
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+	if (ret < 0) {
+		pr_err("SRS - %s: ADM enable for port %d failed\n", __func__,
+			port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	/* Wait for the callback with copp id */
+	ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+			atomic_read(&this_adm.copp.stat
+			[port_idx][copp_idx]) >= 0,
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: SRS set params timed out port = %d\n",
+			__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	kfree(adm_params);
+	return ret;
+}
+
+int adm_set_stereo_to_custom_stereo(int port_id, int copp_idx,
+				    unsigned int session_id, char *params,
+				    uint32_t params_length)
+{
+	struct adm_cmd_set_pspd_mtmx_strtr_params_v5 *adm_params = NULL;
+	int sz, rc = 0, port_idx;
+
+	pr_debug("%s:\n", __func__);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5) +
+		params_length;
+	adm_params = kzalloc(sz, GFP_KERNEL);
+	if (!adm_params) {
+		pr_err("%s, adm params memory alloc failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(((u8 *)adm_params +
+		sizeof(struct adm_cmd_set_pspd_mtmx_strtr_params_v5)),
+		params, params_length);
+	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_params->hdr.pkt_size = sz;
+	adm_params->hdr.src_svc = APR_SVC_ADM;
+	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params->hdr.src_port = port_id;
+	adm_params->hdr.dest_svc = APR_SVC_ADM;
+	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_params->hdr.dest_port = 0; /* Ignored */;
+	adm_params->hdr.token = port_idx << 16 | copp_idx;
+	adm_params->hdr.opcode = ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5;
+	adm_params->payload_addr_lsw = 0;
+	adm_params->payload_addr_msw = 0;
+	adm_params->mem_map_handle = 0;
+	adm_params->payload_size = params_length;
+	/* direction RX as 0 */
+	adm_params->direction = ADM_MATRIX_ID_AUDIO_RX;
+	/* session id for this cmd to be applied on */
+	adm_params->sessionid = session_id;
+	adm_params->deviceid =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params->reserved = 0;
+	pr_debug("%s: deviceid %d, session_id %d, src_port %d, dest_port %d\n",
+		__func__, adm_params->deviceid, adm_params->sessionid,
+		adm_params->hdr.src_port, adm_params->hdr.dest_port);
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = 0x%x rc %d\n",
+			__func__, port_id, rc);
+		rc = -EINVAL;
+		goto set_stereo_to_custom_stereo_return;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) >= 0,
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: Set params timed out port = 0x%x\n", __func__,
+			port_id);
+		rc = -EINVAL;
+		goto set_stereo_to_custom_stereo_return;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n", __func__,
+			adsp_err_get_err_str(atomic_read(
+			&this_adm.copp.stat
+			[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto set_stereo_to_custom_stereo_return;
+	}
+	rc = 0;
+set_stereo_to_custom_stereo_return:
+	kfree(adm_params);
+	return rc;
+}
+
+int adm_dolby_dap_send_params(int port_id, int copp_idx, char *params,
+			      uint32_t params_length)
+{
+	struct adm_cmd_set_pp_params_v5	*adm_params = NULL;
+	int sz, rc = 0;
+	int port_idx;
+
+	pr_debug("%s:\n", __func__);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct adm_cmd_set_pp_params_v5) + params_length;
+	adm_params = kzalloc(sz, GFP_KERNEL);
+	if (!adm_params) {
+		pr_err("%s, adm params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)),
+			params, params_length);
+	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_params->hdr.pkt_size = sz;
+	adm_params->hdr.src_svc = APR_SVC_ADM;
+	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params->hdr.src_port = port_id;
+	adm_params->hdr.dest_svc = APR_SVC_ADM;
+	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_params->hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params->hdr.token = port_idx << 16 | copp_idx;
+	adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	adm_params->payload_addr_lsw = 0;
+	adm_params->payload_addr_msw = 0;
+	adm_params->mem_map_handle = 0;
+	adm_params->payload_size = params_length;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = 0x%x rc %d\n",
+			__func__, port_id, rc);
+		rc = -EINVAL;
+		goto dolby_dap_send_param_return;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: Set params timed out port = 0x%x\n",
+			 __func__, port_id);
+		rc = -EINVAL;
+		goto dolby_dap_send_param_return;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto dolby_dap_send_param_return;
+	}
+	rc = 0;
+dolby_dap_send_param_return:
+	kfree(adm_params);
+	return rc;
+}
+
+int adm_send_params_v5(int port_id, int copp_idx, char *params,
+			      uint32_t params_length)
+{
+	struct adm_cmd_set_pp_params_v5	*adm_params = NULL;
+	int rc = 0;
+	int sz, port_idx;
+
+	pr_debug("%s:\n", __func__);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct adm_cmd_set_pp_params_v5) + params_length;
+	adm_params = kzalloc(sz, GFP_KERNEL);
+	if (!adm_params) {
+		pr_err("%s, adm params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)),
+			params, params_length);
+	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_params->hdr.pkt_size = sz;
+	adm_params->hdr.src_svc = APR_SVC_ADM;
+	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params->hdr.src_port = port_id;
+	adm_params->hdr.dest_svc = APR_SVC_ADM;
+	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_params->hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params->hdr.token = port_idx << 16 | copp_idx;
+	adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	adm_params->payload_addr_lsw = 0;
+	adm_params->payload_addr_msw = 0;
+	adm_params->mem_map_handle = 0;
+	adm_params->payload_size = params_length;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = 0x%x rc %d\n",
+			__func__, port_id, rc);
+		rc = -EINVAL;
+		goto send_param_return;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: Set params timed out port = 0x%x\n",
+			 __func__, port_id);
+		rc = -EINVAL;
+		goto send_param_return;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto send_param_return;
+	}
+	rc = 0;
+send_param_return:
+	kfree(adm_params);
+	return rc;
+}
+
+int adm_get_params_v2(int port_id, int copp_idx, uint32_t module_id,
+		      uint32_t param_id, uint32_t params_length,
+		      char *params, uint32_t client_id)
+{
+	struct adm_cmd_get_pp_params_v5 *adm_params = NULL;
+	int sz, rc = 0, i = 0;
+	int port_idx, idx;
+	int *params_data = (int *)params;
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct adm_cmd_get_pp_params_v5) + params_length;
+	adm_params = kzalloc(sz, GFP_KERNEL);
+	if (!adm_params) {
+		pr_err("%s: adm params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_get_pp_params_v5)),
+		params, params_length);
+	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+	APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_params->hdr.pkt_size = sz;
+	adm_params->hdr.src_svc = APR_SVC_ADM;
+	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params->hdr.src_port = port_id;
+	adm_params->hdr.dest_svc = APR_SVC_ADM;
+	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_params->hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params->hdr.token = port_idx << 16 | client_id << 8 | copp_idx;
+	adm_params->hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5;
+	adm_params->data_payload_addr_lsw = 0;
+	adm_params->data_payload_addr_msw = 0;
+	adm_params->mem_map_handle = 0;
+	adm_params->module_id = module_id;
+	adm_params->param_id = param_id;
+	adm_params->param_max_size = params_length;
+	adm_params->reserved = 0;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+	if (rc < 0) {
+		pr_err("%s: Failed to Get Params on port_id 0x%x %d\n",
+			__func__, port_id, rc);
+		rc = -EINVAL;
+		goto adm_get_param_return;
+	}
+	/* Wait for the callback with copp id */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+	atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: get params timed out port_id = 0x%x\n", __func__,
+			port_id);
+		rc = -EINVAL;
+		goto adm_get_param_return;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto adm_get_param_return;
+	}
+	idx = ADM_GET_PARAMETER_LENGTH * copp_idx;
+
+	if (adm_get_parameters[idx] < 0) {
+		pr_err("%s: Size is invalid %d\n", __func__,
+			adm_get_parameters[idx]);
+		rc = -EINVAL;
+		goto adm_get_param_return;
+	}
+	if ((params_data) &&
+		(ARRAY_SIZE(adm_get_parameters) >
+		idx) &&
+		(ARRAY_SIZE(adm_get_parameters) >=
+		1+adm_get_parameters[idx]+idx) &&
+		(params_length/sizeof(uint32_t) >=
+		adm_get_parameters[idx])) {
+		for (i = 0; i < adm_get_parameters[idx]; i++)
+			params_data[i] = adm_get_parameters[1+i+idx];
+
+	} else {
+		pr_err("%s: Get param data not copied! get_param array size %zd, index %d, params array size %zd, index %d\n",
+		__func__, ARRAY_SIZE(adm_get_parameters),
+		(1+adm_get_parameters[idx]+idx),
+		params_length/sizeof(int),
+		adm_get_parameters[idx]);
+	}
+	rc = 0;
+adm_get_param_return:
+	kfree(adm_params);
+
+	return rc;
+}
+
+int adm_get_params(int port_id, int copp_idx, uint32_t module_id,
+		   uint32_t param_id, uint32_t params_length, char *params)
+{
+	return adm_get_params_v2(port_id, copp_idx, module_id, param_id,
+				 params_length, params, 0);
+}
+
+int adm_get_pp_topo_module_list(int port_id, int copp_idx, int32_t param_length,
+				char *params)
+{
+	struct adm_cmd_get_pp_topo_module_list_t *adm_pp_module_list = NULL;
+	int sz, rc = 0, i = 0;
+	int port_idx, idx;
+	int32_t *params_data = (int32_t *)params;
+	int *topo_list;
+
+	pr_debug("%s : port_id %x", __func__, port_id);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct adm_cmd_get_pp_topo_module_list_t) + param_length;
+	adm_pp_module_list = kzalloc(sz, GFP_KERNEL);
+	if (!adm_pp_module_list) {
+		pr_err("%s, adm params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+
+	memcpy(((u8 *)adm_pp_module_list +
+		sizeof(struct adm_cmd_get_pp_topo_module_list_t)),
+		params, param_length);
+	adm_pp_module_list->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+	APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_pp_module_list->hdr.pkt_size = sz;
+	adm_pp_module_list->hdr.src_svc = APR_SVC_ADM;
+	adm_pp_module_list->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_pp_module_list->hdr.src_port = port_id;
+	adm_pp_module_list->hdr.dest_svc = APR_SVC_ADM;
+	adm_pp_module_list->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_pp_module_list->hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_pp_module_list->hdr.token =  port_idx << 16 | copp_idx;
+	adm_pp_module_list->hdr.opcode = ADM_CMD_GET_PP_TOPO_MODULE_LIST;
+	adm_pp_module_list->param_max_size = param_length;
+	/* Payload address and mmap handle set to zero by kzalloc */
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_pp_module_list);
+	if (rc < 0) {
+		pr_err("%s: Failed to Get Params on port %d\n", __func__,
+			port_id);
+		rc = -EINVAL;
+		goto adm_pp_module_list_l;
+	}
+	/* Wait for the callback with copp id */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: get params timed out port = %d\n", __func__,
+			port_id);
+		rc = -EINVAL;
+		goto adm_pp_module_list_l;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto adm_pp_module_list_l;
+	}
+	if (params_data) {
+		idx = ADM_GET_TOPO_MODULE_LIST_LENGTH * copp_idx;
+		topo_list = (int *)(adm_module_topo_list + idx);
+		if (param_length <= ADM_GET_TOPO_MODULE_LIST_LENGTH &&
+			idx <
+			(MAX_COPPS_PER_PORT * ADM_GET_TOPO_MODULE_LIST_LENGTH))
+			memcpy(params_data, topo_list, param_length);
+		else
+			pr_debug("%s: i/p size:%d > MAX param size:%d\n",
+				 __func__, param_length,
+				 (int)ADM_GET_TOPO_MODULE_LIST_LENGTH);
+		for (i = 1; i <= params_data[0]; i++)
+			pr_debug("module = 0x%x\n", params_data[i]);
+	}
+	rc = 0;
+adm_pp_module_list_l:
+	kfree(adm_pp_module_list);
+	pr_debug("%s : rc = %d ", __func__, rc);
+	return rc;
+}
+static void adm_callback_debug_print(struct apr_client_data *data)
+{
+	uint32_t *payload;
+
+	payload = data->payload;
+
+	if (data->payload_size >= 8)
+		pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n",
+			__func__, data->opcode, payload[0], payload[1],
+			data->payload_size);
+	else if (data->payload_size >= 4)
+		pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n",
+			__func__, data->opcode, payload[0],
+			data->payload_size);
+	else
+		pr_debug("%s: code = 0x%x, size = %d\n",
+			__func__, data->opcode, data->payload_size);
+}
+
+int adm_set_multi_ch_map(char *channel_map, int path)
+{
+	int idx;
+
+	if (path == ADM_PATH_PLAYBACK) {
+		idx = ADM_MCH_MAP_IDX_PLAYBACK;
+	} else if (path == ADM_PATH_LIVE_REC) {
+		idx = ADM_MCH_MAP_IDX_REC;
+	} else {
+		pr_err("%s: invalid attempt to set path %d\n", __func__, path);
+		return -EINVAL;
+	}
+
+	memcpy(multi_ch_maps[idx].channel_mapping, channel_map,
+		PCM_FORMAT_MAX_NUM_CHANNEL);
+	multi_ch_maps[idx].set_channel_map = true;
+
+	return 0;
+}
+
+int adm_get_multi_ch_map(char *channel_map, int path)
+{
+	int idx;
+
+	if (path == ADM_PATH_PLAYBACK) {
+		idx = ADM_MCH_MAP_IDX_PLAYBACK;
+	} else if (path == ADM_PATH_LIVE_REC) {
+		idx = ADM_MCH_MAP_IDX_REC;
+	} else {
+		pr_err("%s: invalid attempt to get path %d\n", __func__, path);
+		return -EINVAL;
+	}
+
+	if (multi_ch_maps[idx].set_channel_map) {
+		memcpy(channel_map, multi_ch_maps[idx].channel_mapping,
+		       PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	return 0;
+}
+
+static int32_t adm_callback(struct apr_client_data *data, void *priv)
+{
+	uint32_t *payload;
+	int i, j, port_idx, copp_idx, idx, client_id;
+
+	if (data == NULL) {
+		pr_err("%s: data parameter is null\n", __func__);
+		return -EINVAL;
+	}
+
+	payload = data->payload;
+
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
+			__func__,
+			data->reset_event, data->reset_proc, this_adm.apr);
+		if (this_adm.apr) {
+			apr_reset(this_adm.apr);
+			for (i = 0; i < AFE_MAX_PORTS; i++) {
+				for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+					atomic_set(&this_adm.copp.id[i][j],
+						   RESET_COPP_ID);
+					atomic_set(&this_adm.copp.cnt[i][j], 0);
+					atomic_set(
+					   &this_adm.copp.topology[i][j], 0);
+					atomic_set(&this_adm.copp.mode[i][j],
+						   0);
+					atomic_set(&this_adm.copp.stat[i][j],
+						   0);
+					atomic_set(&this_adm.copp.rate[i][j],
+						   0);
+					atomic_set(
+					&this_adm.copp.channels[i][j],
+						   0);
+					atomic_set(
+					    &this_adm.copp.bit_width[i][j], 0);
+					atomic_set(
+					    &this_adm.copp.app_type[i][j], 0);
+					atomic_set(
+					   &this_adm.copp.acdb_id[i][j], 0);
+					this_adm.copp.adm_status[i][j] =
+						ADM_STATUS_CALIBRATION_REQUIRED;
+				}
+			}
+			this_adm.apr = NULL;
+			cal_utils_clear_cal_block_q6maps(ADM_MAX_CAL_TYPES,
+				this_adm.cal_data);
+			mutex_lock(&this_adm.cal_data
+				[ADM_CUSTOM_TOP_CAL]->lock);
+			this_adm.set_custom_topology = 1;
+			mutex_unlock(&this_adm.cal_data[
+				ADM_CUSTOM_TOP_CAL]->lock);
+			rtac_clear_mapping(ADM_RTAC_CAL);
+			/*
+			 * Free the ION memory and clear the map handles
+			 * for Source Tracking
+			 */
+			if (this_adm.sourceTrackingData.memmap.paddr != 0) {
+				msm_audio_ion_free(
+					this_adm.sourceTrackingData.ion_client,
+					this_adm.sourceTrackingData.ion_handle);
+				this_adm.sourceTrackingData.ion_client = NULL;
+				this_adm.sourceTrackingData.ion_handle = NULL;
+				this_adm.sourceTrackingData.memmap.size = 0;
+				this_adm.sourceTrackingData.memmap.kvaddr =
+									 NULL;
+				this_adm.sourceTrackingData.memmap.paddr = 0;
+				this_adm.sourceTrackingData.apr_cmd_status = -1;
+				atomic_set(&this_adm.mem_map_handles[
+					ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0);
+			}
+		}
+		return 0;
+	}
+
+	adm_callback_debug_print(data);
+	if (data->payload_size) {
+		copp_idx = (data->token) & 0XFF;
+		port_idx = ((data->token) >> 16) & 0xFF;
+		client_id = ((data->token) >> 8) & 0xFF;
+		if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+			pr_err("%s: Invalid port idx %d token %d\n",
+				__func__, port_idx, data->token);
+			return 0;
+		}
+		if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+			pr_err("%s: Invalid copp idx %d token %d\n",
+				__func__, copp_idx, data->token);
+			return 0;
+		}
+		if (client_id < 0 || client_id >= ADM_CLIENT_ID_MAX) {
+			pr_err("%s: Invalid client id %d\n", __func__,
+				client_id);
+			return 0;
+		}
+		if (data->opcode == APR_BASIC_RSP_RESULT) {
+			pr_debug("%s: APR_BASIC_RSP_RESULT id 0x%x\n",
+				__func__, payload[0]);
+			if (payload[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, payload[0], payload[1]);
+			}
+			switch (payload[0]) {
+			case ADM_CMD_SET_PP_PARAMS_V5:
+				pr_debug("%s: ADM_CMD_SET_PP_PARAMS_V5\n",
+					__func__);
+				if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING)
+					this_adm.sourceTrackingData.
+						apr_cmd_status = payload[1];
+				else if (rtac_make_adm_callback(payload,
+							data->payload_size))
+					break;
+				/*
+				 * if soft volume is called and already
+				 * interrupted break out of the sequence here
+				 */
+			case ADM_CMD_DEVICE_OPEN_V5:
+			case ADM_CMD_DEVICE_CLOSE_V5:
+				pr_debug("%s: Basic callback received, wake up.\n",
+					__func__);
+				atomic_set(&this_adm.copp.stat[port_idx]
+						[copp_idx], payload[1]);
+				wake_up(
+				&this_adm.copp.wait[port_idx][copp_idx]);
+				break;
+			case ADM_CMD_ADD_TOPOLOGIES:
+				pr_debug("%s: callback received, ADM_CMD_ADD_TOPOLOGIES.\n",
+					__func__);
+				atomic_set(&this_adm.adm_stat, payload[1]);
+				wake_up(&this_adm.adm_wait);
+				break;
+			case ADM_CMD_MATRIX_MAP_ROUTINGS_V5:
+			case ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5:
+				pr_debug("%s: Basic callback received, wake up.\n",
+					__func__);
+				atomic_set(&this_adm.matrix_map_stat,
+					payload[1]);
+				wake_up(&this_adm.matrix_map_wait);
+				break;
+			case ADM_CMD_SHARED_MEM_UNMAP_REGIONS:
+				pr_debug("%s: ADM_CMD_SHARED_MEM_UNMAP_REGIONS\n",
+					__func__);
+				atomic_set(&this_adm.adm_stat, payload[1]);
+				wake_up(&this_adm.adm_wait);
+				break;
+			case ADM_CMD_SHARED_MEM_MAP_REGIONS:
+				pr_debug("%s: ADM_CMD_SHARED_MEM_MAP_REGIONS\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				if (payload[1] != 0) {
+					pr_err("%s: ADM map error, resuming\n",
+						__func__);
+					atomic_set(&this_adm.adm_stat,
+						payload[1]);
+					wake_up(&this_adm.adm_wait);
+				}
+				break;
+			case ADM_CMD_GET_PP_PARAMS_V5:
+				pr_debug("%s: ADM_CMD_GET_PP_PARAMS_V5\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* ADM_CMDRSP_GET_PP_PARAMS_V5 */
+				if (client_id ==
+					ADM_CLIENT_ID_SOURCE_TRACKING) {
+					this_adm.sourceTrackingData.
+						apr_cmd_status = payload[1];
+					if (payload[1] != 0)
+						pr_err("%s: ADM get param error = %d\n",
+							__func__, payload[1]);
+
+					atomic_set(&this_adm.copp.stat
+						[port_idx][copp_idx],
+						payload[1]);
+					wake_up(&this_adm.copp.wait
+							[port_idx][copp_idx]);
+				} else {
+					if (payload[1] != 0) {
+						pr_err("%s: ADM get param error = %d, resuming\n",
+							__func__, payload[1]);
+
+						rtac_make_adm_callback(payload,
+							data->payload_size);
+					}
+				}
+				break;
+			case ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5:
+				pr_debug("%s: ADM_CMD_SET_PSPD_MTMX_STRTR_PARAMS_V5\n",
+					__func__);
+				atomic_set(&this_adm.copp.stat[port_idx]
+						[copp_idx], payload[1]);
+				wake_up(
+				&this_adm.copp.wait[port_idx][copp_idx]);
+				break;
+			case ADM_CMD_GET_PP_TOPO_MODULE_LIST:
+				pr_debug("%s:ADM_CMD_GET_PP_TOPO_MODULE_LIST\n",
+					 __func__);
+				if (payload[1] != 0)
+					pr_err("%s: ADM get topo list error = %d,\n",
+						__func__, payload[1]);
+				break;
+			default:
+				pr_err("%s: Unknown Cmd: 0x%x\n", __func__,
+								payload[0]);
+				break;
+			}
+			return 0;
+		}
+
+		switch (data->opcode) {
+		case ADM_CMDRSP_DEVICE_OPEN_V5: {
+			struct adm_cmd_rsp_device_open_v5 *open =
+			(struct adm_cmd_rsp_device_open_v5 *)data->payload;
+
+			if (open->copp_id == INVALID_COPP_ID) {
+				pr_err("%s: invalid coppid rxed %d\n",
+					__func__, open->copp_id);
+				atomic_set(&this_adm.copp.stat[port_idx]
+						[copp_idx], ADSP_EBADPARAM);
+				wake_up(
+				&this_adm.copp.wait[port_idx][copp_idx]);
+				break;
+			}
+			atomic_set(&this_adm.copp.stat
+				[port_idx][copp_idx], payload[0]);
+			atomic_set(&this_adm.copp.id[port_idx][copp_idx],
+				   open->copp_id);
+			pr_debug("%s: coppid rxed=%d\n", __func__,
+				 open->copp_id);
+			wake_up(&this_adm.copp.wait[port_idx][copp_idx]);
+			}
+			break;
+		case ADM_CMDRSP_GET_PP_PARAMS_V5:
+			pr_debug("%s: ADM_CMDRSP_GET_PP_PARAMS_V5\n", __func__);
+			if (payload[0] != 0)
+				pr_err("%s: ADM_CMDRSP_GET_PP_PARAMS_V5 returned error = 0x%x\n",
+					__func__, payload[0]);
+			if (client_id == ADM_CLIENT_ID_SOURCE_TRACKING)
+				this_adm.sourceTrackingData.apr_cmd_status =
+								payload[0];
+			else if (rtac_make_adm_callback(payload,
+					data->payload_size))
+				break;
+
+			idx = ADM_GET_PARAMETER_LENGTH * copp_idx;
+			if ((payload[0] == 0) && (data->payload_size >
+				(4 * sizeof(*payload))) &&
+				(data->payload_size - 4 >=
+				payload[3]) &&
+				(ARRAY_SIZE(adm_get_parameters) >
+				idx) &&
+				(ARRAY_SIZE(adm_get_parameters)-idx-1 >=
+				payload[3])) {
+				adm_get_parameters[idx] = payload[3] /
+							sizeof(uint32_t);
+				/*
+				 * payload[3] is param_size which is
+				 * expressed in number of bytes
+				 */
+				pr_debug("%s: GET_PP PARAM:received parameter length: 0x%x\n",
+					__func__, adm_get_parameters[idx]);
+				/* storing param size then params */
+				for (i = 0; i < payload[3] /
+						sizeof(uint32_t); i++)
+					adm_get_parameters[idx+1+i] =
+							payload[4+i];
+			} else if (payload[0] == 0) {
+				adm_get_parameters[idx] = -1;
+				pr_err("%s: Out of band case, setting size to %d\n",
+					__func__, adm_get_parameters[idx]);
+			} else {
+				adm_get_parameters[idx] = -1;
+				pr_err("%s: GET_PP_PARAMS failed, setting size to %d\n",
+					__func__, adm_get_parameters[idx]);
+			}
+			atomic_set(&this_adm.copp.stat
+				[port_idx][copp_idx], payload[0]);
+			wake_up(&this_adm.copp.wait[port_idx][copp_idx]);
+			break;
+		case ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST:
+			pr_debug("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST\n",
+				 __func__);
+			if (payload[0] != 0) {
+				pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST",
+					 __func__);
+				pr_err(":err = 0x%x\n", payload[0]);
+			} else if (payload[1] >
+				   ((ADM_GET_TOPO_MODULE_LIST_LENGTH /
+				   sizeof(uint32_t)) - 1)) {
+				pr_err("%s: ADM_CMDRSP_GET_PP_TOPO_MODULE_LIST",
+					 __func__);
+				pr_err(":size = %d\n", payload[1]);
+			} else {
+				idx = ADM_GET_TOPO_MODULE_LIST_LENGTH *
+					copp_idx;
+				pr_debug("%s:Num modules payload[1] %d\n",
+					 __func__, payload[1]);
+				adm_module_topo_list[idx] = payload[1];
+				for (i = 1; i <= payload[1]; i++) {
+					adm_module_topo_list[idx+i] =
+						payload[1+i];
+					pr_debug("%s:payload[%d] = %x\n",
+						 __func__, (i+1), payload[1+i]);
+				}
+			}
+			atomic_set(&this_adm.copp.stat
+				[port_idx][copp_idx], payload[0]);
+			wake_up(&this_adm.copp.wait[port_idx][copp_idx]);
+			break;
+		case ADM_CMDRSP_SHARED_MEM_MAP_REGIONS:
+			pr_debug("%s: ADM_CMDRSP_SHARED_MEM_MAP_REGIONS\n",
+				__func__);
+			atomic_set(&this_adm.mem_map_handles[
+				   atomic_read(&this_adm.mem_map_index)],
+				   *payload);
+			atomic_set(&this_adm.adm_stat, 0);
+			wake_up(&this_adm.adm_wait);
+			break;
+		default:
+			pr_err("%s: Unknown cmd:0x%x\n", __func__,
+				data->opcode);
+			break;
+		}
+	}
+	return 0;
+}
+
+static int adm_memory_map_regions(phys_addr_t *buf_add, uint32_t mempool_id,
+			   uint32_t *bufsz, uint32_t bufcnt)
+{
+	struct  avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+	struct  avs_shared_map_region_payload *mregions = NULL;
+	void    *mmap_region_cmd = NULL;
+	void    *payload = NULL;
+	int     ret = 0;
+	int     i = 0;
+	int     cmd_size = 0;
+
+	pr_debug("%s:\n", __func__);
+	if (this_adm.apr == NULL) {
+		this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+						0xFFFFFFFF, &this_adm);
+		if (this_adm.apr == NULL) {
+			pr_err("%s: Unable to register ADM\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_adm_handle(this_adm.apr);
+	}
+
+	cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+			+ sizeof(struct avs_shared_map_region_payload)
+			* bufcnt;
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (!mmap_region_cmd)
+		return -ENOMEM;
+
+	mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+	mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+								APR_PKT_VER);
+	mmap_regions->hdr.pkt_size = cmd_size;
+	mmap_regions->hdr.src_port = 0;
+
+	mmap_regions->hdr.dest_port = 0;
+	mmap_regions->hdr.token = 0;
+	mmap_regions->hdr.opcode = ADM_CMD_SHARED_MEM_MAP_REGIONS;
+	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
+	mmap_regions->num_regions = bufcnt & 0x00ff;
+	mmap_regions->property_flag = 0x00;
+
+	pr_debug("%s: map_regions->num_regions = %d\n", __func__,
+				mmap_regions->num_regions);
+	payload = ((u8 *) mmap_region_cmd +
+				sizeof(struct avs_cmd_shared_mem_map_regions));
+	mregions = (struct avs_shared_map_region_payload *)payload;
+
+	for (i = 0; i < bufcnt; i++) {
+		mregions->shm_addr_lsw = lower_32_bits(buf_add[i]);
+		mregions->shm_addr_msw =
+				msm_audio_populate_upper_32_bits(buf_add[i]);
+		mregions->mem_size_bytes = bufsz[i];
+		++mregions;
+	}
+
+	atomic_set(&this_adm.adm_stat, -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *) mmap_region_cmd);
+	if (ret < 0) {
+		pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+					mmap_regions->hdr.opcode, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_adm.adm_wait,
+				 atomic_read(&this_adm.adm_stat) >= 0,
+				 5 * HZ);
+	if (!ret) {
+		pr_err("%s: timeout. waited for memory_map\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.adm_stat) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.adm_stat)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.adm_stat));
+		goto fail_cmd;
+	}
+fail_cmd:
+	kfree(mmap_region_cmd);
+	return ret;
+}
+
+static int adm_memory_unmap_regions(void)
+{
+	struct  avs_cmd_shared_mem_unmap_regions unmap_regions;
+	int     ret = 0;
+
+	pr_debug("%s:\n", __func__);
+	if (this_adm.apr == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+							APR_PKT_VER);
+	unmap_regions.hdr.pkt_size = sizeof(unmap_regions);
+	unmap_regions.hdr.src_port = 0;
+	unmap_regions.hdr.dest_port = 0;
+	unmap_regions.hdr.token = 0;
+	unmap_regions.hdr.opcode = ADM_CMD_SHARED_MEM_UNMAP_REGIONS;
+	unmap_regions.mem_map_handle = atomic_read(&this_adm.
+		mem_map_handles[atomic_read(&this_adm.mem_map_index)]);
+	atomic_set(&this_adm.adm_stat, -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *) &unmap_regions);
+	if (ret < 0) {
+		pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+				unmap_regions.hdr.opcode, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_adm.adm_wait,
+				 atomic_read(&this_adm.adm_stat) >= 0,
+				 5 * HZ);
+	if (!ret) {
+		pr_err("%s: timeout. waited for memory_unmap\n",
+		       __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.adm_stat) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.adm_stat)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.adm_stat));
+		goto fail_cmd;
+	} else {
+		pr_debug("%s: Unmap handle 0x%x succeeded\n", __func__,
+			 unmap_regions.mem_map_handle);
+	}
+fail_cmd:
+	return ret;
+}
+
+static int remap_cal_data(struct cal_block_data *cal_block, int cal_index)
+{
+	int ret = 0;
+
+	if (cal_block->map_data.ion_client == NULL) {
+		pr_err("%s: No ION allocation for cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((cal_block->map_data.map_size > 0) &&
+		(cal_block->map_data.q6map_handle == 0)) {
+		atomic_set(&this_adm.mem_map_index, cal_index);
+		ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0,
+				(uint32_t *)&cal_block->map_data.map_size, 1);
+		if (ret < 0) {
+			pr_err("%s: ADM mmap did not work! size = %zd ret %d\n",
+				__func__,
+				cal_block->map_data.map_size, ret);
+			pr_debug("%s: ADM mmap did not work! addr = 0x%pK, size = %zd ret %d\n",
+				__func__,
+				&cal_block->cal_data.paddr,
+				cal_block->map_data.map_size, ret);
+			goto done;
+		}
+		cal_block->map_data.q6map_handle = atomic_read(&this_adm.
+			mem_map_handles[cal_index]);
+	}
+done:
+	return ret;
+}
+
+static void send_adm_custom_topology(void)
+{
+	struct cal_block_data *cal_block = NULL;
+	struct cmd_set_topologies adm_top;
+	int cal_index = ADM_CUSTOM_TOP_CAL;
+	int result;
+
+	if (this_adm.cal_data[cal_index] == NULL)
+		goto done;
+
+	mutex_lock(&this_adm.cal_data[cal_index]->lock);
+	if (!this_adm.set_custom_topology)
+		goto unlock;
+	this_adm.set_custom_topology = 0;
+
+	cal_block = cal_utils_get_only_cal_block(this_adm.cal_data[cal_index]);
+	if (cal_block == NULL)
+		goto unlock;
+
+	pr_debug("%s: Sending cal_index %d\n", __func__, cal_index);
+
+	result = remap_cal_data(cal_block, cal_index);
+	if (result) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, cal_index);
+		goto unlock;
+	}
+	atomic_set(&this_adm.mem_map_index, cal_index);
+	atomic_set(&this_adm.mem_map_handles[cal_index],
+		cal_block->map_data.q6map_handle);
+
+	if (cal_block->cal_data.size == 0) {
+		pr_debug("%s: No ADM cal to send\n", __func__);
+		goto unlock;
+	}
+
+	adm_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(20), APR_PKT_VER);
+	adm_top.hdr.pkt_size = sizeof(adm_top);
+	adm_top.hdr.src_svc = APR_SVC_ADM;
+	adm_top.hdr.src_domain = APR_DOMAIN_APPS;
+	adm_top.hdr.src_port = 0;
+	adm_top.hdr.dest_svc = APR_SVC_ADM;
+	adm_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_top.hdr.dest_port = 0;
+	adm_top.hdr.token = 0;
+	adm_top.hdr.opcode = ADM_CMD_ADD_TOPOLOGIES;
+	adm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr);
+	adm_top.payload_addr_msw = msm_audio_populate_upper_32_bits(
+						cal_block->cal_data.paddr);
+	adm_top.mem_map_handle = cal_block->map_data.q6map_handle;
+	adm_top.payload_size = cal_block->cal_data.size;
+
+	atomic_set(&this_adm.adm_stat, -1);
+	pr_debug("%s: Sending ADM_CMD_ADD_TOPOLOGIES payload = 0x%pK, size = %d\n",
+		__func__, &cal_block->cal_data.paddr,
+		adm_top.payload_size);
+	result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_top);
+	if (result < 0) {
+		pr_err("%s: Set topologies failed payload size = %zd result %d\n",
+			__func__, cal_block->cal_data.size, result);
+		goto unlock;
+	}
+	/* Wait for the callback */
+	result = wait_event_timeout(this_adm.adm_wait,
+				    atomic_read(&this_adm.adm_stat) >= 0,
+				    msecs_to_jiffies(TIMEOUT_MS));
+	if (!result) {
+		pr_err("%s: Set topologies timed out payload size = %zd\n",
+			__func__, cal_block->cal_data.size);
+		goto unlock;
+	} else if (atomic_read(&this_adm.adm_stat) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.adm_stat)));
+		result = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.adm_stat));
+		goto unlock;
+	}
+unlock:
+	mutex_unlock(&this_adm.cal_data[cal_index]->lock);
+done:
+	return;
+}
+
+static int send_adm_cal_block(int port_id, int copp_idx,
+			      struct cal_block_data *cal_block, int perf_mode,
+			      int app_type, int acdb_id, int sample_rate)
+{
+	s32 result = 0;
+	struct adm_cmd_set_pp_params_v5	adm_params;
+	int port_idx;
+
+	pr_debug("%s: Port id 0x%x sample_rate %d ,\n", __func__,
+			port_id, sample_rate);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+	if (!cal_block) {
+		pr_debug("%s: No ADM cal to send for port_id = 0x%x!\n",
+			__func__, port_id);
+		result = -EINVAL;
+		goto done;
+	}
+	if (cal_block->cal_data.size <= 0) {
+		pr_debug("%s: No ADM cal send for port_id = 0x%x!\n",
+			__func__, port_id);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (perf_mode == LEGACY_PCM_MODE &&
+		((atomic_read(&this_adm.copp.topology[port_idx][copp_idx])) ==
+			DS2_ADM_COPP_TOPOLOGY_ID)) {
+		pr_err("%s: perf_mode %d, topology 0x%x\n", __func__, perf_mode,
+			atomic_read(
+				&this_adm.copp.topology[port_idx][copp_idx]));
+		goto done;
+	}
+
+	adm_params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(20), APR_PKT_VER);
+	adm_params.hdr.pkt_size = sizeof(adm_params);
+	adm_params.hdr.src_svc = APR_SVC_ADM;
+	adm_params.hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params.hdr.src_port = port_id;
+	adm_params.hdr.dest_svc = APR_SVC_ADM;
+	adm_params.hdr.dest_domain = APR_DOMAIN_ADSP;
+
+	adm_params.hdr.token = port_idx << 16 | copp_idx;
+	adm_params.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	adm_params.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr);
+	adm_params.payload_addr_msw = msm_audio_populate_upper_32_bits(
+						cal_block->cal_data.paddr);
+	adm_params.mem_map_handle = cal_block->map_data.q6map_handle;
+	adm_params.payload_size = cal_block->cal_data.size;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	pr_debug("%s: Sending SET_PARAMS payload = 0x%pK, size = %d\n",
+		__func__, &cal_block->cal_data.paddr,
+		adm_params.payload_size);
+	result = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_params);
+	if (result < 0) {
+		pr_err("%s: Set params failed port 0x%x result %d\n",
+				__func__, port_id, result);
+		pr_debug("%s: Set params failed port = 0x%x payload = 0x%pK result %d\n",
+			__func__, port_id, &cal_block->cal_data.paddr, result);
+		result = -EINVAL;
+		goto done;
+	}
+	/* Wait for the callback */
+	result = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!result) {
+		pr_err("%s: Set params timed out port = 0x%x\n",
+				__func__, port_id);
+		pr_debug("%s: Set params timed out port = 0x%x, payload = 0x%pK\n",
+			__func__, port_id, &cal_block->cal_data.paddr);
+		result = -EINVAL;
+		goto done;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		result = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto done;
+	}
+
+done:
+	return result;
+}
+
+static struct cal_block_data *adm_find_cal_by_path(int cal_index, int path)
+{
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block = NULL;
+	struct audio_cal_info_audproc *audproc_cal_info = NULL;
+	struct audio_cal_info_audvol *audvol_cal_info = NULL;
+
+	pr_debug("%s:\n", __func__);
+
+	list_for_each_safe(ptr, next,
+		&this_adm.cal_data[cal_index]->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		if (cal_index == ADM_AUDPROC_CAL) {
+			audproc_cal_info = cal_block->cal_info;
+			if ((audproc_cal_info->path == path) &&
+			    (cal_block->cal_data.size > 0))
+				return cal_block;
+		} else if (cal_index == ADM_AUDVOL_CAL) {
+			audvol_cal_info = cal_block->cal_info;
+			if ((audvol_cal_info->path == path) &&
+			    (cal_block->cal_data.size > 0))
+				return cal_block;
+		}
+	}
+	pr_debug("%s: Can't find ADM cal for cal_index %d, path %d\n",
+		__func__, cal_index, path);
+	return NULL;
+}
+
+static struct cal_block_data *adm_find_cal_by_app_type(int cal_index, int path,
+								int app_type)
+{
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block = NULL;
+	struct audio_cal_info_audproc *audproc_cal_info = NULL;
+	struct audio_cal_info_audvol *audvol_cal_info = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	list_for_each_safe(ptr, next,
+		&this_adm.cal_data[cal_index]->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		if (cal_index == ADM_AUDPROC_CAL) {
+			audproc_cal_info = cal_block->cal_info;
+			if ((audproc_cal_info->path == path) &&
+			    (audproc_cal_info->app_type == app_type) &&
+			    (cal_block->cal_data.size > 0))
+				return cal_block;
+		} else if (cal_index == ADM_AUDVOL_CAL) {
+			audvol_cal_info = cal_block->cal_info;
+			if ((audvol_cal_info->path == path) &&
+			    (audvol_cal_info->app_type == app_type) &&
+			    (cal_block->cal_data.size > 0))
+				return cal_block;
+		}
+	}
+	pr_debug("%s: Can't find ADM cali for cal_index %d, path %d, app %d, defaulting to search by path\n",
+		__func__, cal_index, path, app_type);
+	return adm_find_cal_by_path(cal_index, path);
+}
+
+
+static struct cal_block_data *adm_find_cal(int cal_index, int path,
+					   int app_type, int acdb_id,
+					   int sample_rate)
+{
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block = NULL;
+	struct audio_cal_info_audproc *audproc_cal_info = NULL;
+	struct audio_cal_info_audvol *audvol_cal_info = NULL;
+
+	pr_debug("%s:\n", __func__);
+
+	list_for_each_safe(ptr, next,
+		&this_adm.cal_data[cal_index]->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		if (cal_index == ADM_AUDPROC_CAL) {
+			audproc_cal_info = cal_block->cal_info;
+			if ((audproc_cal_info->path == path) &&
+			    (audproc_cal_info->app_type == app_type) &&
+			    (audproc_cal_info->acdb_id == acdb_id) &&
+			    (audproc_cal_info->sample_rate == sample_rate) &&
+			    (cal_block->cal_data.size > 0))
+				return cal_block;
+		} else if (cal_index == ADM_AUDVOL_CAL) {
+			audvol_cal_info = cal_block->cal_info;
+			if ((audvol_cal_info->path == path) &&
+			    (audvol_cal_info->app_type == app_type) &&
+			    (audvol_cal_info->acdb_id == acdb_id) &&
+			    (cal_block->cal_data.size > 0))
+				return cal_block;
+		}
+	}
+	pr_debug("%s: Can't find ADM cal for cal_index %d, path %d, app %d, acdb_id %d sample_rate %d defaulting to search by app type\n",
+		__func__, cal_index, path, app_type, acdb_id, sample_rate);
+	return adm_find_cal_by_app_type(cal_index, path, app_type);
+}
+
+static int adm_remap_and_send_cal_block(int cal_index, int port_id,
+	int copp_idx, struct cal_block_data *cal_block, int perf_mode,
+	int app_type, int acdb_id, int sample_rate)
+{
+	int ret = 0;
+
+	pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index);
+	ret = remap_cal_data(cal_block, cal_index);
+	if (ret) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, cal_index);
+		goto done;
+	}
+	ret = send_adm_cal_block(port_id, copp_idx, cal_block, perf_mode,
+				app_type, acdb_id, sample_rate);
+	if (ret < 0)
+		pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d sample_rate %d\n",
+			__func__, cal_index, port_id, ret, sample_rate);
+done:
+	return ret;
+}
+
+static void send_adm_cal_type(int cal_index, int path, int port_id,
+			      int copp_idx, int perf_mode, int app_type,
+			      int acdb_id, int sample_rate)
+{
+	struct cal_block_data		*cal_block = NULL;
+	int ret;
+
+	pr_debug("%s: cal index %d\n", __func__, cal_index);
+
+	if (this_adm.cal_data[cal_index] == NULL) {
+		pr_debug("%s: cal_index %d not allocated!\n",
+			__func__, cal_index);
+		goto done;
+	}
+
+	mutex_lock(&this_adm.cal_data[cal_index]->lock);
+	cal_block = adm_find_cal(cal_index, path, app_type, acdb_id,
+				sample_rate);
+	if (cal_block == NULL)
+		goto unlock;
+
+	ret = adm_remap_and_send_cal_block(cal_index, port_id, copp_idx,
+		cal_block, perf_mode, app_type, acdb_id, sample_rate);
+unlock:
+	mutex_unlock(&this_adm.cal_data[cal_index]->lock);
+done:
+	return;
+}
+
+static int get_cal_path(int path)
+{
+	if (path == 0x1)
+		return RX_DEVICE;
+	else
+		return TX_DEVICE;
+}
+
+static void send_adm_cal(int port_id, int copp_idx, int path, int perf_mode,
+			 int app_type, int acdb_id, int sample_rate)
+{
+	pr_debug("%s: port id 0x%x copp_idx %d\n", __func__, port_id, copp_idx);
+
+	send_adm_cal_type(ADM_AUDPROC_CAL, path, port_id, copp_idx, perf_mode,
+			  app_type, acdb_id, sample_rate);
+	send_adm_cal_type(ADM_AUDVOL_CAL, path, port_id, copp_idx, perf_mode,
+			  app_type, acdb_id, sample_rate);
+}
+
+int adm_connect_afe_port(int mode, int session_id, int port_id)
+{
+	struct adm_cmd_connect_afe_port_v5	cmd;
+	int ret = 0;
+	int port_idx, copp_idx = 0;
+
+	pr_debug("%s: port_id: 0x%x session id:%d mode:%d\n", __func__,
+				port_id, session_id, mode);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	if (this_adm.apr == NULL) {
+		this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+						0xFFFFFFFF, &this_adm);
+		if (this_adm.apr == NULL) {
+			pr_err("%s: Unable to register ADM\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_adm_handle(this_adm.apr);
+	}
+	pr_debug("%s: Port ID 0x%x, index %d\n", __func__, port_id, port_idx);
+
+	cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cmd.hdr.pkt_size = sizeof(cmd);
+	cmd.hdr.src_svc = APR_SVC_ADM;
+	cmd.hdr.src_domain = APR_DOMAIN_APPS;
+	cmd.hdr.src_port = port_id;
+	cmd.hdr.dest_svc = APR_SVC_ADM;
+	cmd.hdr.dest_domain = APR_DOMAIN_ADSP;
+	cmd.hdr.dest_port = 0; /* Ignored */
+	cmd.hdr.token = port_idx << 16 | copp_idx;
+	cmd.hdr.opcode = ADM_CMD_CONNECT_AFE_PORT_V5;
+
+	cmd.mode = mode;
+	cmd.session_id = session_id;
+	cmd.afe_port_id = port_id;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&cmd);
+	if (ret < 0) {
+		pr_err("%s: ADM enable for port_id: 0x%x failed ret %d\n",
+					__func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	/* Wait for the callback with copp id */
+	ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: ADM connect timedout for port_id: 0x%x\n",
+			__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto fail_cmd;
+	}
+	atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]);
+	return 0;
+
+fail_cmd:
+
+	return ret;
+}
+
+int adm_arrange_mch_map(struct adm_cmd_device_open_v5 *open, int path,
+			 int channel_mode)
+{
+	int rc = 0, idx;
+
+	memset(open->dev_channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+	switch (path) {
+	case ADM_PATH_PLAYBACK:
+		idx = ADM_MCH_MAP_IDX_PLAYBACK;
+		break;
+	case ADM_PATH_LIVE_REC:
+	case ADM_PATH_NONLIVE_REC:
+		idx = ADM_MCH_MAP_IDX_REC;
+		break;
+	default:
+		goto non_mch_path;
+	};
+	if ((open->dev_num_channel > 2) && multi_ch_maps[idx].set_channel_map) {
+		memcpy(open->dev_channel_mapping,
+			multi_ch_maps[idx].channel_mapping,
+			PCM_FORMAT_MAX_NUM_CHANNEL);
+	} else {
+		if (channel_mode == 1) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FC;
+		} else if (channel_mode == 2) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+		} else if (channel_mode == 3) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+		} else if (channel_mode == 4) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open->dev_channel_mapping[2] = PCM_CHANNEL_LS;
+			open->dev_channel_mapping[3] = PCM_CHANNEL_RS;
+		} else if (channel_mode == 5) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+			open->dev_channel_mapping[3] = PCM_CHANNEL_LS;
+			open->dev_channel_mapping[4] = PCM_CHANNEL_RS;
+		} else if (channel_mode == 6) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+			open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+			open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+			open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+		} else if (channel_mode == 7) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open->dev_channel_mapping[2] = PCM_CHANNEL_FC;
+			open->dev_channel_mapping[3] = PCM_CHANNEL_LFE;
+			open->dev_channel_mapping[4] = PCM_CHANNEL_LB;
+			open->dev_channel_mapping[5] = PCM_CHANNEL_RB;
+			open->dev_channel_mapping[6] = PCM_CHANNEL_CS;
+		} else if (channel_mode == 8) {
+			open->dev_channel_mapping[0] = PCM_CHANNEL_FL;
+			open->dev_channel_mapping[1] = PCM_CHANNEL_FR;
+			open->dev_channel_mapping[2] = PCM_CHANNEL_LFE;
+			open->dev_channel_mapping[3] = PCM_CHANNEL_FC;
+			open->dev_channel_mapping[4] = PCM_CHANNEL_LS;
+			open->dev_channel_mapping[5] = PCM_CHANNEL_RS;
+			open->dev_channel_mapping[6] = PCM_CHANNEL_LB;
+			open->dev_channel_mapping[7] = PCM_CHANNEL_RB;
+		} else {
+			pr_err("%s: invalid num_chan %d\n", __func__,
+				channel_mode);
+			rc = -EINVAL;
+			goto inval_ch_mod;
+		}
+	}
+
+non_mch_path:
+inval_ch_mod:
+	return rc;
+}
+
+int adm_open(int port_id, int path, int rate, int channel_mode, int topology,
+	     int perf_mode, uint16_t bit_width, int app_type, int acdb_id)
+{
+	struct adm_cmd_device_open_v5	open;
+	int ret = 0;
+	int port_idx, copp_idx, flags;
+	int tmp_port = q6audio_get_port_id(port_id);
+
+	pr_debug("%s:port %#x path:%d rate:%d mode:%d perf_mode:%d,topo_id %d\n",
+		 __func__, port_id, path, rate, channel_mode, perf_mode,
+		 topology);
+
+	/* For DTS EAGLE only, force 24 bit */
+	if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) &&
+		(perf_mode == LEGACY_PCM_MODE)) {
+		bit_width = 24;
+		pr_debug("%s: Force open adm in 24-bit for DTS HPX topology 0x%x\n",
+			__func__, topology);
+	}
+	port_id = q6audio_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	if (this_adm.apr == NULL) {
+		this_adm.apr = apr_register("ADSP", "ADM", adm_callback,
+						0xFFFFFFFF, &this_adm);
+		if (this_adm.apr == NULL) {
+			pr_err("%s: Unable to register ADM\n", __func__);
+			return -ENODEV;
+		}
+		rtac_set_adm_handle(this_adm.apr);
+	}
+
+	if (perf_mode == ULL_POST_PROCESSING_PCM_MODE) {
+		flags = ADM_ULL_POST_PROCESSING_DEVICE_SESSION;
+		if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
+		    (topology == DS2_ADM_COPP_TOPOLOGY_ID) ||
+		    (topology == SRS_TRUMEDIA_TOPOLOGY_ID) ||
+		    (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX))
+			topology = DEFAULT_COPP_TOPOLOGY;
+	} else if (perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) {
+		flags = ADM_ULTRA_LOW_LATENCY_DEVICE_SESSION;
+		topology = NULL_COPP_TOPOLOGY;
+		rate = ULL_SUPPORTED_SAMPLE_RATE;
+		bit_width = ULL_SUPPORTED_BITS_PER_SAMPLE;
+	} else if (perf_mode == LOW_LATENCY_PCM_MODE) {
+		flags = ADM_LOW_LATENCY_DEVICE_SESSION;
+		if ((topology == DOLBY_ADM_COPP_TOPOLOGY_ID) ||
+		    (topology == DS2_ADM_COPP_TOPOLOGY_ID) ||
+		    (topology == SRS_TRUMEDIA_TOPOLOGY_ID) ||
+		    (topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX))
+			topology = DEFAULT_COPP_TOPOLOGY;
+	} else {
+		if (path == ADM_PATH_COMPRESSED_RX)
+			flags = 0;
+		else
+			flags = ADM_LEGACY_DEVICE_SESSION;
+	}
+
+	if ((topology == VPM_TX_SM_ECNS_COPP_TOPOLOGY) ||
+	    (topology == VPM_TX_DM_FLUENCE_COPP_TOPOLOGY) ||
+	    (topology == VPM_TX_DM_RFECNS_COPP_TOPOLOGY))
+		rate = 16000;
+
+	copp_idx = adm_get_idx_if_copp_exists(port_idx, topology, perf_mode,
+						rate, bit_width, app_type);
+	if (copp_idx < 0) {
+		copp_idx = adm_get_next_available_copp(port_idx);
+		if (copp_idx >= MAX_COPPS_PER_PORT) {
+			pr_err("%s: exceeded copp id %d\n",
+				 __func__, copp_idx);
+			return -EINVAL;
+		}
+		atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.topology[port_idx][copp_idx],
+			   topology);
+		atomic_set(&this_adm.copp.mode[port_idx][copp_idx],
+			   perf_mode);
+		atomic_set(&this_adm.copp.rate[port_idx][copp_idx],
+			   rate);
+		atomic_set(&this_adm.copp.channels[port_idx][copp_idx],
+			   channel_mode);
+		atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx],
+			   bit_width);
+		atomic_set(&this_adm.copp.app_type[port_idx][copp_idx],
+			   app_type);
+		atomic_set(&this_adm.copp.acdb_id[port_idx][copp_idx],
+			   acdb_id);
+		set_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+		(void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
+		if (path != ADM_PATH_COMPRESSED_RX)
+			send_adm_custom_topology();
+	}
+
+	if (this_adm.copp.adm_delay[port_idx][copp_idx] &&
+		perf_mode == LEGACY_PCM_MODE) {
+		atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx],
+			   1);
+		this_adm.copp.adm_delay[port_idx][copp_idx] = 0;
+		wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]);
+	}
+
+	/* Create a COPP if port id are not enabled */
+	if (atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]) == 0) {
+		pr_debug("%s: open ADM: port_idx: %d, copp_idx: %d\n", __func__,
+			 port_idx, copp_idx);
+	if ((topology == SRS_TRUMEDIA_TOPOLOGY_ID) &&
+	     perf_mode == LEGACY_PCM_MODE) {
+		int res;
+
+		atomic_set(&this_adm.mem_map_index, ADM_SRS_TRUMEDIA);
+		msm_dts_srs_tm_ion_memmap(&this_adm.outband_memmap);
+		res = adm_memory_map_regions(&this_adm.outband_memmap.paddr, 0,
+		(uint32_t *)&this_adm.outband_memmap.size, 1);
+		if (res < 0) {
+			pr_err("%s: SRS adm_memory_map_regions failed ! addr = 0x%pK, size = %d\n",
+			 __func__, (void *)this_adm.outband_memmap.paddr,
+		(uint32_t)this_adm.outband_memmap.size);
+		}
+	}
+		if ((topology == ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX) &&
+		    (perf_mode == LEGACY_PCM_MODE)) {
+			int res = 0;
+
+			atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE);
+			msm_dts_ion_memmap(&this_adm.outband_memmap);
+			res = adm_memory_map_regions(
+				      &this_adm.outband_memmap.paddr,
+				      0,
+				      (uint32_t *)&this_adm.outband_memmap.size,
+				      1);
+			if (res < 0)
+				pr_err("%s: DTS_EAGLE mmap did not work!",
+					__func__);
+		}
+		open.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						   APR_HDR_LEN(APR_HDR_SIZE),
+						   APR_PKT_VER);
+		open.hdr.pkt_size = sizeof(open);
+		open.hdr.src_svc = APR_SVC_ADM;
+		open.hdr.src_domain = APR_DOMAIN_APPS;
+		open.hdr.src_port = tmp_port;
+		open.hdr.dest_svc = APR_SVC_ADM;
+		open.hdr.dest_domain = APR_DOMAIN_ADSP;
+		open.hdr.dest_port = tmp_port;
+		open.hdr.token = port_idx << 16 | copp_idx;
+		open.hdr.opcode = ADM_CMD_DEVICE_OPEN_V5;
+		open.flags = flags;
+		open.mode_of_operation = path;
+		open.endpoint_id_1 = tmp_port;
+
+		if (this_adm.ec_ref_rx == -1) {
+			open.endpoint_id_2 = 0xFFFF;
+		} else if (this_adm.ec_ref_rx && (path != 1)) {
+			open.endpoint_id_2 = this_adm.ec_ref_rx;
+			this_adm.ec_ref_rx = -1;
+		}
+
+		open.topology_id = topology;
+
+		open.dev_num_channel = channel_mode & 0x00FF;
+		open.bit_width = bit_width;
+		WARN_ON((perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) &&
+			(rate != ULL_SUPPORTED_SAMPLE_RATE));
+		open.sample_rate  = rate;
+
+		ret = adm_arrange_mch_map(&open, path, channel_mode);
+
+		if (ret)
+			return ret;
+
+		pr_debug("%s: port_id=0x%x rate=%d topology_id=0x%X\n",
+			__func__, open.endpoint_id_1, open.sample_rate,
+			open.topology_id);
+
+		atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+		ret = apr_send_pkt(this_adm.apr, (uint32_t *)&open);
+		if (ret < 0) {
+			pr_err("%s: port_id: 0x%x for[0x%x] failed %d\n",
+			__func__, tmp_port, port_id, ret);
+			return -EINVAL;
+		}
+		/* Wait for the callback with copp id */
+		ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+			atomic_read(&this_adm.copp.stat
+			[port_idx][copp_idx]) >= 0,
+			msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: ADM open timedout for port_id: 0x%x for [0x%x]\n",
+						__func__, tmp_port, port_id);
+			return -EINVAL;
+		} else if (atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]) > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+			return adsp_err_get_lnx_err_code(
+					atomic_read(&this_adm.copp.stat
+						[port_idx][copp_idx]));
+		}
+	}
+	atomic_inc(&this_adm.copp.cnt[port_idx][copp_idx]);
+	return copp_idx;
+}
+
+
+void adm_copp_mfc_cfg(int port_id, int copp_idx, int dst_sample_rate)
+{
+	struct audproc_mfc_output_media_fmt mfc_cfg;
+	struct adm_cmd_device_open_v5 open;
+	int port_idx;
+	int sz = 0;
+	int rc  = 0;
+	int i  = 0;
+
+	port_id = q6audio_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		goto fail_cmd;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		goto fail_cmd;
+	}
+
+	sz = sizeof(struct audproc_mfc_output_media_fmt);
+
+	mfc_cfg.params.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mfc_cfg.params.hdr.pkt_size = sz;
+	mfc_cfg.params.hdr.src_svc = APR_SVC_ADM;
+	mfc_cfg.params.hdr.src_domain = APR_DOMAIN_APPS;
+	mfc_cfg.params.hdr.src_port = port_id;
+	mfc_cfg.params.hdr.dest_svc = APR_SVC_ADM;
+	mfc_cfg.params.hdr.dest_domain = APR_DOMAIN_ADSP;
+	mfc_cfg.params.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	mfc_cfg.params.hdr.token = port_idx << 16 | copp_idx;
+	mfc_cfg.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	mfc_cfg.params.payload_addr_lsw = 0;
+	mfc_cfg.params.payload_addr_msw = 0;
+	mfc_cfg.params.mem_map_handle = 0;
+	mfc_cfg.params.payload_size = sizeof(mfc_cfg) -
+				sizeof(mfc_cfg.params);
+	mfc_cfg.data.module_id = AUDPROC_MODULE_ID_MFC;
+	mfc_cfg.data.param_id =
+			AUDPROC_PARAM_ID_MFC_OUTPUT_MEDIA_FORMAT;
+	mfc_cfg.data.param_size = mfc_cfg.params.payload_size -
+				sizeof(mfc_cfg.data);
+	mfc_cfg.data.reserved = 0;
+	mfc_cfg.sampling_rate = dst_sample_rate;
+	mfc_cfg.bits_per_sample =
+		atomic_read(&this_adm.copp.bit_width[port_idx][copp_idx]);
+	open.dev_num_channel = mfc_cfg.num_channels =
+		atomic_read(&this_adm.copp.channels[port_idx][copp_idx]);
+
+	rc = adm_arrange_mch_map(&open, ADM_PATH_PLAYBACK,
+		mfc_cfg.num_channels);
+	if (rc < 0) {
+		pr_err("%s: unable to get channal map\n", __func__);
+		goto fail_cmd;
+	}
+
+	for (i = 0; i < mfc_cfg.num_channels; i++)
+		mfc_cfg.channel_type[i] =
+			(uint16_t) open.dev_channel_mapping[i];
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+	pr_debug("%s: mfc config: port_idx %d copp_idx  %d copp SR %d copp BW %d copp chan %d o/p SR %d\n",
+			__func__, port_idx, copp_idx,
+			atomic_read(&this_adm.copp.rate[port_idx][copp_idx]),
+			mfc_cfg.bits_per_sample, mfc_cfg.num_channels,
+			mfc_cfg.sampling_rate);
+
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)&mfc_cfg);
+
+	if (rc < 0) {
+		pr_err("%s: port_id: for[0x%x] failed %d\n",
+		__func__, port_id, rc);
+		goto fail_cmd;
+	}
+	/* Wait for the callback with copp id */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat
+		[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: mfc_cfg Set params timed out for port_id: for [0x%x]\n",
+					__func__, port_id);
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_adm.copp.stat
+			[port_idx][copp_idx])));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return;
+}
+
+
+int adm_matrix_map(int path, struct route_payload payload_map, int perf_mode)
+{
+	struct adm_cmd_matrix_map_routings_v5	*route;
+	struct adm_session_map_node_v5 *node;
+	uint16_t *copps_list;
+	int cmd_size = 0;
+	int ret = 0, i = 0;
+	void *payload = NULL;
+	void *matrix_map = NULL;
+	int port_idx, copp_idx;
+
+	/* Assumes port_ids have already been validated during adm_open */
+	cmd_size = (sizeof(struct adm_cmd_matrix_map_routings_v5) +
+			sizeof(struct adm_session_map_node_v5) +
+			(sizeof(uint32_t) * payload_map.num_copps));
+	matrix_map = kzalloc(cmd_size, GFP_KERNEL);
+	if (matrix_map == NULL) {
+		pr_err("%s: Mem alloc failed\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+	route = (struct adm_cmd_matrix_map_routings_v5 *)matrix_map;
+
+	route->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	route->hdr.pkt_size = cmd_size;
+	route->hdr.src_svc = 0;
+	route->hdr.src_domain = APR_DOMAIN_APPS;
+	route->hdr.src_port = 0; /* Ignored */;
+	route->hdr.dest_svc = APR_SVC_ADM;
+	route->hdr.dest_domain = APR_DOMAIN_ADSP;
+	route->hdr.dest_port = 0; /* Ignored */;
+	route->hdr.token = 0;
+	if (path == ADM_PATH_COMPRESSED_RX) {
+		pr_debug("%s: ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5 0x%x\n",
+			 __func__, ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5);
+		route->hdr.opcode = ADM_CMD_STREAM_DEVICE_MAP_ROUTINGS_V5;
+	} else {
+		pr_debug("%s: DM_CMD_MATRIX_MAP_ROUTINGS_V5 0x%x\n",
+			 __func__, ADM_CMD_MATRIX_MAP_ROUTINGS_V5);
+		route->hdr.opcode = ADM_CMD_MATRIX_MAP_ROUTINGS_V5;
+	}
+	route->num_sessions = 1;
+
+	switch (path) {
+	case ADM_PATH_PLAYBACK:
+		route->matrix_id = ADM_MATRIX_ID_AUDIO_RX;
+		break;
+	case ADM_PATH_LIVE_REC:
+	case ADM_PATH_NONLIVE_REC:
+		route->matrix_id = ADM_MATRIX_ID_AUDIO_TX;
+		break;
+	case ADM_PATH_COMPRESSED_RX:
+		route->matrix_id = ADM_MATRIX_ID_COMPRESSED_AUDIO_RX;
+		break;
+	default:
+		pr_err("%s: Wrong path set[%d]\n", __func__, path);
+		break;
+	}
+	payload = ((u8 *)matrix_map +
+			sizeof(struct adm_cmd_matrix_map_routings_v5));
+	node = (struct adm_session_map_node_v5 *)payload;
+
+	node->session_id = payload_map.session_id;
+	node->num_copps = payload_map.num_copps;
+	payload = (u8 *)node + sizeof(struct adm_session_map_node_v5);
+	copps_list = (uint16_t *)payload;
+	for (i = 0; i < payload_map.num_copps; i++) {
+		port_idx =
+		adm_validate_and_get_port_index(payload_map.port_id[i]);
+		if (port_idx < 0) {
+			pr_err("%s: Invalid port_id 0x%x\n", __func__,
+				payload_map.port_id[i]);
+			ret = -EINVAL;
+			goto fail_cmd;
+		}
+		copp_idx = payload_map.copp_idx[i];
+		copps_list[i] = atomic_read(&this_adm.copp.id[port_idx]
+							     [copp_idx]);
+	}
+	atomic_set(&this_adm.matrix_map_stat, -1);
+
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)matrix_map);
+	if (ret < 0) {
+		pr_err("%s: routing for syream %d failed ret %d\n",
+			__func__, payload_map.session_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_adm.matrix_map_wait,
+				atomic_read(&this_adm.matrix_map_stat) >= 0,
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: routing for syream %d failed\n", __func__,
+			payload_map.session_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.matrix_map_stat) > 0) {
+		pr_err("%s: DSP returned error[%s]\n", __func__,
+			adsp_err_get_err_str(atomic_read(
+			&this_adm.matrix_map_stat)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.matrix_map_stat));
+		goto fail_cmd;
+	}
+
+	if ((perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) &&
+		 (path != ADM_PATH_COMPRESSED_RX)) {
+		for (i = 0; i < payload_map.num_copps; i++) {
+			port_idx = afe_get_port_index(payload_map.port_id[i]);
+			copp_idx = payload_map.copp_idx[i];
+			if (port_idx < 0 || copp_idx < 0 ||
+			    (copp_idx > MAX_COPPS_PER_PORT - 1)) {
+				pr_err("%s: Invalid idx port_idx %d copp_idx %d\n",
+					__func__, port_idx, copp_idx);
+				continue;
+			}
+			if (atomic_read(
+				&this_adm.copp.topology[port_idx][copp_idx]) ==
+				ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)
+				continue;
+			rtac_add_adm_device(payload_map.port_id[i],
+					    atomic_read(&this_adm.copp.id
+							[port_idx][copp_idx]),
+					    get_cal_path(path),
+					    payload_map.session_id,
+					    payload_map.app_type,
+					    payload_map.acdb_dev_id);
+
+			if (!test_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+				(void *)&this_adm.copp.adm_status[port_idx]
+								[copp_idx])) {
+				pr_debug("%s: adm copp[0x%x][%d] already sent",
+						__func__, port_idx, copp_idx);
+				continue;
+			}
+			send_adm_cal(payload_map.port_id[i], copp_idx,
+				     get_cal_path(path), perf_mode,
+				     payload_map.app_type,
+				     payload_map.acdb_dev_id,
+				     payload_map.sample_rate);
+			/* ADM COPP calibration is already sent */
+			clear_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+				(void *)&this_adm.copp.
+				adm_status[port_idx][copp_idx]);
+			pr_debug("%s: copp_id: %d\n", __func__,
+				 atomic_read(&this_adm.copp.id[port_idx]
+							      [copp_idx]));
+		}
+	}
+
+fail_cmd:
+	kfree(matrix_map);
+	return ret;
+}
+
+void adm_ec_ref_rx_id(int port_id)
+{
+	this_adm.ec_ref_rx = port_id;
+	pr_debug("%s: ec_ref_rx:%d", __func__, this_adm.ec_ref_rx);
+}
+
+int adm_close(int port_id, int perf_mode, int copp_idx)
+{
+	struct apr_hdr close;
+
+	int ret = 0, port_idx;
+	int copp_id = RESET_COPP_ID;
+
+	pr_debug("%s: port_id=0x%x perf_mode: %d copp_idx: %d\n", __func__,
+		 port_id, perf_mode, copp_idx);
+
+	port_id = q6audio_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n",
+			__func__, port_id);
+		return -EINVAL;
+	}
+
+	if ((copp_idx < 0) || (copp_idx >= MAX_COPPS_PER_PORT)) {
+		pr_err("%s: Invalid copp idx: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	if (this_adm.copp.adm_delay[port_idx][copp_idx] && perf_mode
+		== LEGACY_PCM_MODE) {
+		atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx],
+			   1);
+		this_adm.copp.adm_delay[port_idx][copp_idx] = 0;
+		wake_up(&this_adm.copp.adm_delay_wait[port_idx][copp_idx]);
+	}
+
+	atomic_dec(&this_adm.copp.cnt[port_idx][copp_idx]);
+	if (!(atomic_read(&this_adm.copp.cnt[port_idx][copp_idx]))) {
+		copp_id = adm_get_copp_id(port_idx, copp_idx);
+		pr_debug("%s: Closing ADM port_idx:%d copp_idx:%d copp_id:0x%x\n",
+			 __func__, port_idx, copp_idx, copp_id);
+		if ((!perf_mode) && (this_adm.outband_memmap.paddr != 0) &&
+		    (atomic_read(&this_adm.copp.topology[port_idx][copp_idx]) ==
+			SRS_TRUMEDIA_TOPOLOGY_ID)) {
+			atomic_set(&this_adm.mem_map_index,
+				ADM_SRS_TRUMEDIA);
+			ret = adm_memory_unmap_regions();
+			if (ret < 0) {
+				pr_err("%s: adm mem unmmap err %d",
+					__func__, ret);
+			} else {
+				atomic_set(&this_adm.mem_map_handles
+					   [ADM_SRS_TRUMEDIA], 0);
+			}
+		}
+
+		if ((perf_mode == LEGACY_PCM_MODE) &&
+		    (this_adm.outband_memmap.paddr != 0) &&
+		    (atomic_read(
+			&this_adm.copp.topology[port_idx][copp_idx]) ==
+			ADM_CMD_COPP_OPEN_TOPOLOGY_ID_DTS_HPX)) {
+			atomic_set(&this_adm.mem_map_index, ADM_DTS_EAGLE);
+			ret = adm_memory_unmap_regions();
+			if (ret < 0) {
+				pr_err("%s: adm mem unmmap err %d",
+					__func__, ret);
+			} else {
+				atomic_set(&this_adm.mem_map_handles
+					   [ADM_DTS_EAGLE], 0);
+			}
+		}
+
+		if ((afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) &&
+		    this_adm.sourceTrackingData.memmap.paddr) {
+			atomic_set(&this_adm.mem_map_index,
+				   ADM_MEM_MAP_INDEX_SOURCE_TRACKING);
+			ret = adm_memory_unmap_regions();
+			if (ret < 0) {
+				pr_err("%s: adm mem unmmap err %d",
+					__func__, ret);
+			}
+			msm_audio_ion_free(
+				this_adm.sourceTrackingData.ion_client,
+				this_adm.sourceTrackingData.ion_handle);
+			this_adm.sourceTrackingData.ion_client = NULL;
+			this_adm.sourceTrackingData.ion_handle = NULL;
+			this_adm.sourceTrackingData.memmap.size = 0;
+			this_adm.sourceTrackingData.memmap.kvaddr = NULL;
+			this_adm.sourceTrackingData.memmap.paddr = 0;
+			this_adm.sourceTrackingData.apr_cmd_status = -1;
+			atomic_set(&this_adm.mem_map_handles[
+					ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0);
+		}
+
+		close.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+		close.pkt_size = sizeof(close);
+		close.src_svc = APR_SVC_ADM;
+		close.src_domain = APR_DOMAIN_APPS;
+		close.src_port = port_id;
+		close.dest_svc = APR_SVC_ADM;
+		close.dest_domain = APR_DOMAIN_ADSP;
+		close.dest_port = copp_id;
+		close.token = port_idx << 16 | copp_idx;
+		close.opcode = ADM_CMD_DEVICE_CLOSE_V5;
+
+		atomic_set(&this_adm.copp.id[port_idx][copp_idx],
+			   RESET_COPP_ID);
+		atomic_set(&this_adm.copp.cnt[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.topology[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.mode[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+		atomic_set(&this_adm.copp.rate[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.channels[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.bit_width[port_idx][copp_idx], 0);
+		atomic_set(&this_adm.copp.app_type[port_idx][copp_idx], 0);
+
+		clear_bit(ADM_STATUS_CALIBRATION_REQUIRED,
+			(void *)&this_adm.copp.adm_status[port_idx][copp_idx]);
+
+		ret = apr_send_pkt(this_adm.apr, (uint32_t *)&close);
+		if (ret < 0) {
+			pr_err("%s: ADM close failed %d\n", __func__, ret);
+			return -EINVAL;
+		}
+
+		ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+			atomic_read(&this_adm.copp.stat
+			[port_idx][copp_idx]) >= 0,
+			msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: ADM cmd Route timedout for port 0x%x\n",
+				__func__, port_id);
+			return -EINVAL;
+		} else if (atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]) > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+			return adsp_err_get_lnx_err_code(
+					atomic_read(&this_adm.copp.stat
+						[port_idx][copp_idx]));
+		}
+	}
+
+	if (perf_mode != ULTRA_LOW_LATENCY_PCM_MODE) {
+		pr_debug("%s: remove adm device from rtac\n", __func__);
+		rtac_remove_adm_device(port_id, copp_id);
+	}
+	return 0;
+}
+
+int send_rtac_audvol_cal(void)
+{
+	int ret = 0;
+	int ret2 = 0;
+	int i = 0;
+	int copp_idx, port_idx, acdb_id, app_id, path;
+	struct cal_block_data *cal_block = NULL;
+	struct audio_cal_info_audvol *audvol_cal_info = NULL;
+	struct rtac_adm rtac_adm_data;
+
+	mutex_lock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock);
+
+	cal_block = cal_utils_get_only_cal_block(
+		this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]);
+	if (cal_block == NULL) {
+		pr_err("%s: can't find cal block!\n", __func__);
+		goto unlock;
+	}
+
+	audvol_cal_info = cal_block->cal_info;
+	if (audvol_cal_info == NULL) {
+		pr_err("%s: audvol_cal_info is NULL!\n", __func__);
+		goto unlock;
+	}
+
+	get_rtac_adm_data(&rtac_adm_data);
+	for (; i < rtac_adm_data.num_of_dev; i++) {
+
+		acdb_id = rtac_adm_data.device[i].acdb_dev_id;
+		if (acdb_id == 0)
+			acdb_id = audvol_cal_info->acdb_id;
+
+		app_id = rtac_adm_data.device[i].app_type;
+		if (app_id == 0)
+			app_id = audvol_cal_info->app_type;
+
+		path = afe_get_port_type(rtac_adm_data.device[i].afe_port);
+		if ((acdb_id == audvol_cal_info->acdb_id) &&
+			(app_id == audvol_cal_info->app_type) &&
+			(path == audvol_cal_info->path)) {
+
+			if (adm_get_indexes_from_copp_id(rtac_adm_data.
+				device[i].copp, &copp_idx, &port_idx) != 0) {
+				pr_debug("%s: Copp Id %d is not active\n",
+					__func__,
+					rtac_adm_data.device[i].copp);
+				continue;
+			}
+
+			ret2 = adm_remap_and_send_cal_block(ADM_RTAC_AUDVOL_CAL,
+				rtac_adm_data.device[i].afe_port,
+				copp_idx, cal_block,
+				atomic_read(&this_adm.copp.
+				mode[port_idx][copp_idx]),
+				audvol_cal_info->app_type,
+				audvol_cal_info->acdb_id,
+				atomic_read(&this_adm.copp.
+				rate[port_idx][copp_idx]));
+			if (ret2 < 0) {
+				pr_debug("%s: remap and send failed for copp Id %d, acdb id %d, app type %d, path %d\n",
+					__func__, rtac_adm_data.device[i].copp,
+					audvol_cal_info->acdb_id,
+					audvol_cal_info->app_type,
+					audvol_cal_info->path);
+				ret = ret2;
+			}
+		}
+	}
+unlock:
+	mutex_unlock(&this_adm.cal_data[ADM_RTAC_AUDVOL_CAL]->lock);
+	return ret;
+}
+
+int adm_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+	int result = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->cal_data.paddr == 0) {
+		pr_debug("%s: No address to map!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->map_data.map_size == 0) {
+		pr_debug("%s: map size is 0!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	/* valid port ID needed for callback use primary I2S */
+	atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL);
+	result = adm_memory_map_regions(&cal_block->cal_data.paddr, 0,
+					&cal_block->map_data.map_size, 1);
+	if (result < 0) {
+		pr_err("%s: RTAC mmap did not work! size = %d result %d\n",
+			__func__,
+			cal_block->map_data.map_size, result);
+		pr_debug("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n",
+			__func__,
+			&cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+		goto done;
+	}
+
+	cal_block->map_data.map_handle = atomic_read(
+		&this_adm.mem_map_handles[ADM_RTAC_APR_CAL]);
+done:
+	return result;
+}
+
+int adm_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+	int result = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (mem_map_handle == NULL) {
+		pr_debug("%s: Map handle is NULL, nothing to unmap\n",
+			__func__);
+		goto done;
+	}
+
+	if (*mem_map_handle == 0) {
+		pr_debug("%s: Map handle is 0, nothing to unmap\n",
+			__func__);
+		goto done;
+	}
+
+	if (*mem_map_handle != atomic_read(
+			&this_adm.mem_map_handles[ADM_RTAC_APR_CAL])) {
+		pr_err("%s: Map handles do not match! Unmapping RTAC, RTAC map 0x%x, ADM map 0x%x\n",
+			__func__, *mem_map_handle, atomic_read(
+			&this_adm.mem_map_handles[ADM_RTAC_APR_CAL]));
+
+		/* if mismatch use handle passed in to unmap */
+		atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL],
+			   *mem_map_handle);
+	}
+
+	/* valid port ID needed for callback use primary I2S */
+	atomic_set(&this_adm.mem_map_index, ADM_RTAC_APR_CAL);
+	result = adm_memory_unmap_regions();
+	if (result < 0) {
+		pr_debug("%s: adm_memory_unmap_regions failed, error %d\n",
+			__func__, result);
+	} else {
+		atomic_set(&this_adm.mem_map_handles[ADM_RTAC_APR_CAL], 0);
+		*mem_map_handle = 0;
+	}
+done:
+	return result;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+	int ret = -EINVAL;
+
+	switch (cal_type) {
+	case ADM_AUDPROC_CAL_TYPE:
+		ret = ADM_AUDPROC_CAL;
+		break;
+	case ADM_AUDVOL_CAL_TYPE:
+		ret = ADM_AUDVOL_CAL;
+		break;
+	case ADM_CUST_TOPOLOGY_CAL_TYPE:
+		ret = ADM_CUSTOM_TOP_CAL;
+		break;
+	case ADM_RTAC_INFO_CAL_TYPE:
+		ret = ADM_RTAC_INFO_CAL;
+		break;
+	case ADM_RTAC_APR_CAL_TYPE:
+		ret = ADM_RTAC_APR_CAL;
+		break;
+	case ADM_RTAC_AUDVOL_CAL_TYPE:
+		ret = ADM_RTAC_AUDVOL_CAL;
+		break;
+	default:
+		pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+	}
+	return ret;
+}
+
+static int adm_alloc_cal(int32_t cal_type, size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_alloc_cal(data_size, data,
+		this_adm.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int adm_dealloc_cal(int32_t cal_type, size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+		this_adm.cal_data[cal_index]);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int adm_set_cal(int32_t cal_type, size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_set_cal(data_size, data,
+		this_adm.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_index == ADM_CUSTOM_TOP_CAL) {
+		mutex_lock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock);
+		this_adm.set_custom_topology = 1;
+		mutex_unlock(&this_adm.cal_data[ADM_CUSTOM_TOP_CAL]->lock);
+	} else if (cal_index == ADM_RTAC_AUDVOL_CAL) {
+		send_rtac_audvol_cal();
+	}
+done:
+	return ret;
+}
+
+static int adm_map_cal_data(int32_t cal_type,
+			struct cal_block_data *cal_block)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	atomic_set(&this_adm.mem_map_index, cal_index);
+	ret = adm_memory_map_regions(&cal_block->cal_data.paddr, 0,
+		(uint32_t *)&cal_block->map_data.map_size, 1);
+	if (ret < 0) {
+		pr_err("%s: map did not work! cal_type %i ret %d\n",
+			__func__, cal_index, ret);
+		ret = -ENODEV;
+		goto done;
+	}
+	cal_block->map_data.q6map_handle = atomic_read(&this_adm.
+		mem_map_handles[cal_index]);
+done:
+	return ret;
+}
+
+static int adm_unmap_cal_data(int32_t cal_type,
+			struct cal_block_data *cal_block)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block == NULL) {
+		pr_err("%s: Cal block is NULL!\n",
+						__func__);
+		goto done;
+	}
+
+	if (cal_block->map_data.q6map_handle == 0) {
+		pr_err("%s: Map handle is NULL, nothing to unmap\n",
+				__func__);
+		goto done;
+	}
+
+	atomic_set(&this_adm.mem_map_handles[cal_index],
+		cal_block->map_data.q6map_handle);
+	atomic_set(&this_adm.mem_map_index, cal_index);
+	ret = adm_memory_unmap_regions();
+	if (ret < 0) {
+		pr_err("%s: unmap did not work! cal_type %i ret %d\n",
+			__func__, cal_index, ret);
+		ret = -ENODEV;
+		goto done;
+	}
+	cal_block->map_data.q6map_handle = 0;
+done:
+	return ret;
+}
+
+static void adm_delete_cal_data(void)
+{
+	pr_debug("%s:\n", __func__);
+
+	cal_utils_destroy_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data);
+}
+
+static int adm_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info	cal_type_info[] = {
+		{{ADM_CUST_TOPOLOGY_CAL_TYPE,
+		{adm_alloc_cal, adm_dealloc_cal, NULL,
+		adm_set_cal, NULL, NULL} },
+		{adm_map_cal_data, adm_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{ADM_AUDPROC_CAL_TYPE,
+		{adm_alloc_cal, adm_dealloc_cal, NULL,
+		adm_set_cal, NULL, NULL} },
+		{adm_map_cal_data, adm_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{ADM_AUDVOL_CAL_TYPE,
+		{adm_alloc_cal, adm_dealloc_cal, NULL,
+		adm_set_cal, NULL, NULL} },
+		{adm_map_cal_data, adm_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{ADM_RTAC_INFO_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{ADM_RTAC_APR_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{DTS_EAGLE_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{SRS_TRUMEDIA_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{ADM_RTAC_AUDVOL_CAL_TYPE,
+		{adm_alloc_cal, adm_dealloc_cal, NULL,
+		adm_set_cal, NULL, NULL} },
+		{adm_map_cal_data, adm_unmap_cal_data,
+		cal_utils_match_buf_num} },
+	};
+	pr_debug("%s:\n", __func__);
+
+	ret = cal_utils_create_cal_types(ADM_MAX_CAL_TYPES, this_adm.cal_data,
+		cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: could not create cal type! ret %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return ret;
+err:
+	adm_delete_cal_data();
+	return ret;
+}
+
+int adm_set_volume(int port_id, int copp_idx, int volume)
+{
+	struct audproc_volume_ctrl_master_gain audproc_vol;
+	int sz = 0;
+	int rc  = 0;
+	int port_idx;
+
+	pr_debug("%s: port_id %d, volume %d\n", __func__, port_id, volume);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct audproc_volume_ctrl_master_gain);
+	audproc_vol.params.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	audproc_vol.params.hdr.pkt_size = sz;
+	audproc_vol.params.hdr.src_svc = APR_SVC_ADM;
+	audproc_vol.params.hdr.src_domain = APR_DOMAIN_APPS;
+	audproc_vol.params.hdr.src_port = port_id;
+	audproc_vol.params.hdr.dest_svc = APR_SVC_ADM;
+	audproc_vol.params.hdr.dest_domain = APR_DOMAIN_ADSP;
+	audproc_vol.params.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	audproc_vol.params.hdr.token = port_idx << 16 | copp_idx;
+	audproc_vol.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	audproc_vol.params.payload_addr_lsw = 0;
+	audproc_vol.params.payload_addr_msw = 0;
+	audproc_vol.params.mem_map_handle = 0;
+	audproc_vol.params.payload_size = sizeof(audproc_vol) -
+				sizeof(audproc_vol.params);
+	audproc_vol.data.module_id = AUDPROC_MODULE_ID_VOL_CTRL;
+	audproc_vol.data.param_id = AUDPROC_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+	audproc_vol.data.param_size = audproc_vol.params.payload_size -
+						sizeof(audproc_vol.data);
+	audproc_vol.data.reserved = 0;
+	audproc_vol.master_gain = volume;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)&audproc_vol);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = %#x\n",
+			__func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: Vol cntrl Set params timed out port = %#x\n",
+			 __func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int adm_set_softvolume(int port_id, int copp_idx,
+			struct audproc_softvolume_params *softvol_param)
+{
+	struct audproc_soft_step_volume_params audproc_softvol;
+	int sz = 0;
+	int rc  = 0;
+	int port_idx;
+
+	pr_debug("%s: period %d step %d curve %d\n", __func__,
+		 softvol_param->period, softvol_param->step,
+		 softvol_param->rampingcurve);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct audproc_soft_step_volume_params);
+
+	audproc_softvol.params.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	audproc_softvol.params.hdr.pkt_size = sz;
+	audproc_softvol.params.hdr.src_svc = APR_SVC_ADM;
+	audproc_softvol.params.hdr.src_domain = APR_DOMAIN_APPS;
+	audproc_softvol.params.hdr.src_port = port_id;
+	audproc_softvol.params.hdr.dest_svc = APR_SVC_ADM;
+	audproc_softvol.params.hdr.dest_domain = APR_DOMAIN_ADSP;
+	audproc_softvol.params.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	audproc_softvol.params.hdr.token = port_idx << 16 | copp_idx;
+	audproc_softvol.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	audproc_softvol.params.payload_addr_lsw = 0;
+	audproc_softvol.params.payload_addr_msw = 0;
+	audproc_softvol.params.mem_map_handle = 0;
+	audproc_softvol.params.payload_size = sizeof(audproc_softvol) -
+				sizeof(audproc_softvol.params);
+	audproc_softvol.data.module_id = AUDPROC_MODULE_ID_VOL_CTRL;
+	audproc_softvol.data.param_id =
+			AUDPROC_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS;
+	audproc_softvol.data.param_size = audproc_softvol.params.payload_size -
+				sizeof(audproc_softvol.data);
+	audproc_softvol.data.reserved = 0;
+	audproc_softvol.period = softvol_param->period;
+	audproc_softvol.step = softvol_param->step;
+	audproc_softvol.ramping_curve = softvol_param->rampingcurve;
+
+	pr_debug("%s: period %d, step %d, curve %d\n", __func__,
+		 audproc_softvol.period, audproc_softvol.step,
+		 audproc_softvol.ramping_curve);
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)&audproc_softvol);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = %#x\n",
+			__func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: Soft volume Set params timed out port = %#x\n",
+			 __func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int adm_param_enable(int port_id, int copp_idx, int module_id,  int enable)
+{
+	struct audproc_enable_param_t adm_mod_enable;
+	int sz = 0;
+	int rc  = 0;
+	int port_idx;
+
+	pr_debug("%s port_id %d, module_id 0x%x, enable %d\n",
+		 __func__, port_id,  module_id,  enable);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	sz = sizeof(struct audproc_enable_param_t);
+
+	adm_mod_enable.pp_params.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_mod_enable.pp_params.hdr.pkt_size = sz;
+	adm_mod_enable.pp_params.hdr.src_svc = APR_SVC_ADM;
+	adm_mod_enable.pp_params.hdr.src_domain = APR_DOMAIN_APPS;
+	adm_mod_enable.pp_params.hdr.src_port = port_id;
+	adm_mod_enable.pp_params.hdr.dest_svc = APR_SVC_ADM;
+	adm_mod_enable.pp_params.hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_mod_enable.pp_params.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_mod_enable.pp_params.hdr.token =  port_idx << 16 | copp_idx;
+	adm_mod_enable.pp_params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	adm_mod_enable.pp_params.payload_addr_lsw = 0;
+	adm_mod_enable.pp_params.payload_addr_msw = 0;
+	adm_mod_enable.pp_params.mem_map_handle = 0;
+	adm_mod_enable.pp_params.payload_size = sizeof(adm_mod_enable) -
+				sizeof(adm_mod_enable.pp_params) +
+				sizeof(adm_mod_enable.pp_params.params);
+	adm_mod_enable.pp_params.params.module_id = module_id;
+	adm_mod_enable.pp_params.params.param_id = AUDPROC_PARAM_ID_ENABLE;
+	adm_mod_enable.pp_params.params.param_size =
+		adm_mod_enable.pp_params.payload_size -
+		sizeof(adm_mod_enable.pp_params.params);
+	adm_mod_enable.pp_params.params.reserved = 0;
+	adm_mod_enable.enable = enable;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)&adm_mod_enable);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = %#x\n",
+			__func__, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s:  module %x  enable %d timed out on port = %#x\n",
+			 __func__, module_id, enable, port_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+
+}
+
+int adm_send_calibration(int port_id, int copp_idx, int path, int perf_mode,
+			 int cal_type, char *params, int size)
+{
+
+	struct adm_cmd_set_pp_params_v5	*adm_params = NULL;
+	int sz, rc = 0;
+	int port_idx;
+
+	pr_debug("%s:port_id %d, path %d, perf_mode %d, cal_type %d, size %d\n",
+		 __func__, port_id, path, perf_mode, cal_type, size);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	/* Maps audio_dev_ctrl path definition to ACDB definition */
+	if (get_cal_path(path) != RX_DEVICE) {
+		pr_err("%s: acdb_path %d\n", __func__, path);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	sz = sizeof(struct adm_cmd_set_pp_params_v5) + size;
+	adm_params = kzalloc(sz, GFP_KERNEL);
+	if (!adm_params) {
+		pr_err("%s, adm params memory alloc failed", __func__);
+		rc = -ENOMEM;
+		goto end;
+	}
+
+	memcpy(((u8 *)adm_params + sizeof(struct adm_cmd_set_pp_params_v5)),
+			params, size);
+
+	adm_params->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	adm_params->hdr.pkt_size = sz;
+	adm_params->hdr.src_svc = APR_SVC_ADM;
+	adm_params->hdr.src_domain = APR_DOMAIN_APPS;
+	adm_params->hdr.src_port = port_id;
+	adm_params->hdr.dest_svc = APR_SVC_ADM;
+	adm_params->hdr.dest_domain = APR_DOMAIN_ADSP;
+	adm_params->hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	adm_params->hdr.token = port_idx << 16 | copp_idx;
+	adm_params->hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	/* payload address and mmap handle initialized to zero by kzalloc */
+	adm_params->payload_size = size;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	rc = apr_send_pkt(this_adm.apr, (uint32_t *)adm_params);
+	if (rc < 0) {
+		pr_err("%s: Set params failed port = %#x\n",
+			__func__, port_id);
+		rc = -EINVAL;
+		goto end;
+	}
+	/* Wait for the callback */
+	rc = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!rc) {
+		pr_err("%s: Set params timed out port = %#x\n",
+			 __func__, port_id);
+		rc = -EINVAL;
+		goto end;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto end;
+	}
+	rc = 0;
+
+end:
+	kfree(adm_params);
+	return rc;
+}
+
+/*
+ * adm_update_wait_parameters must be called with routing driver locks.
+ * adm_reset_wait_parameters must be called with routing driver locks.
+ * set and reset parmeters are separated to make sure it is always called
+ * under routing driver lock.
+ * adm_wait_timeout is to block until timeout or interrupted. Timeout is
+ * not a an error.
+ */
+int adm_set_wait_parameters(int port_id, int copp_idx)
+{
+
+	int ret = 0, port_idx;
+
+	pr_debug("%s: port_id 0x%x, copp_idx %d\n", __func__, port_id,
+		 copp_idx);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	this_adm.copp.adm_delay[port_idx][copp_idx] = 1;
+	atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 0);
+
+end:
+	return ret;
+
+}
+
+int adm_reset_wait_parameters(int port_id, int copp_idx)
+{
+	int ret = 0, port_idx;
+
+	pr_debug("%s: port_id 0x%x copp_idx %d\n", __func__, port_id,
+		 copp_idx);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	atomic_set(&this_adm.copp.adm_delay_stat[port_idx][copp_idx], 1);
+	this_adm.copp.adm_delay[port_idx][copp_idx] = 0;
+
+end:
+	return ret;
+}
+
+int adm_wait_timeout(int port_id, int copp_idx, int wait_time)
+{
+	int ret = 0, port_idx;
+
+	pr_debug("%s: port_id 0x%x, copp_idx %d, wait_time %d\n", __func__,
+		 port_id, copp_idx, wait_time);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	ret = wait_event_timeout(
+		this_adm.copp.adm_delay_wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.adm_delay_stat[port_idx][copp_idx]),
+		msecs_to_jiffies(wait_time));
+	pr_debug("%s: return %d\n", __func__, ret);
+	if (ret != 0)
+		ret = -EINTR;
+end:
+	pr_debug("%s: return %d--\n", __func__, ret);
+	return ret;
+}
+
+int adm_store_cal_data(int port_id, int copp_idx, int path, int perf_mode,
+		       int cal_index, char *params, int *size)
+{
+	int rc = 0;
+	struct cal_block_data		*cal_block = NULL;
+	int app_type, acdb_id, port_idx, sample_rate;
+
+	if (this_adm.cal_data[cal_index] == NULL) {
+		pr_debug("%s: cal_index %d not allocated!\n",
+			__func__, cal_index);
+		goto end;
+	}
+
+	if (get_cal_path(path) != RX_DEVICE) {
+		pr_debug("%s: Invalid path to store calibration %d\n",
+			 __func__, path);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		rc = -EINVAL;
+		goto end;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+		return -EINVAL;
+	}
+
+	acdb_id = atomic_read(&this_adm.copp.acdb_id[port_idx][copp_idx]);
+	app_type = atomic_read(&this_adm.copp.app_type[port_idx][copp_idx]);
+	sample_rate = atomic_read(&this_adm.copp.rate[port_idx][copp_idx]);
+
+	mutex_lock(&this_adm.cal_data[cal_index]->lock);
+	cal_block = adm_find_cal(cal_index, get_cal_path(path), app_type,
+				acdb_id, sample_rate);
+	if (cal_block == NULL)
+		goto unlock;
+
+	if (cal_block->cal_data.size <= 0) {
+		pr_debug("%s: No ADM cal send for port_id = 0x%x!\n",
+			__func__, port_id);
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	if (cal_index == ADM_AUDPROC_CAL) {
+		if (cal_block->cal_data.size > AUD_PROC_BLOCK_SIZE) {
+			pr_err("%s:audproc:invalid size exp/actual[%zd, %d]\n",
+				__func__, cal_block->cal_data.size, *size);
+			rc = -ENOMEM;
+			goto unlock;
+		}
+	} else if (cal_index == ADM_AUDVOL_CAL) {
+		if (cal_block->cal_data.size > AUD_VOL_BLOCK_SIZE) {
+			pr_err("%s:aud_vol:invalid size exp/actual[%zd, %d]\n",
+				__func__, cal_block->cal_data.size, *size);
+			rc = -ENOMEM;
+			goto unlock;
+		}
+	} else {
+		pr_debug("%s: Not valid calibration for dolby topolgy\n",
+			 __func__);
+		rc = -EINVAL;
+		goto unlock;
+	}
+	memcpy(params, cal_block->cal_data.kvaddr, cal_block->cal_data.size);
+	*size = cal_block->cal_data.size;
+
+	pr_debug("%s:port_id %d, copp_idx %d, path %d",
+		 __func__, port_id, copp_idx, path);
+	pr_debug("perf_mode %d, cal_type %d, size %d\n",
+		 perf_mode, cal_index, *size);
+
+unlock:
+	mutex_unlock(&this_adm.cal_data[cal_index]->lock);
+end:
+	return rc;
+}
+
+int adm_send_compressed_device_mute(int port_id, int copp_idx, bool mute_on)
+{
+	struct adm_set_compressed_device_mute mute_params;
+	int ret = 0;
+	int port_idx;
+
+	pr_debug("%s port_id: 0x%x, copp_idx %d, mute_on: %d\n",
+		 __func__, port_id, copp_idx, mute_on);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+		pr_err("%s: Invalid port_id %#x copp_idx %d\n",
+			__func__, port_id, copp_idx);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	mute_params.command.hdr.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mute_params.command.hdr.pkt_size =
+			sizeof(struct adm_set_compressed_device_mute);
+	mute_params.command.hdr.src_svc = APR_SVC_ADM;
+	mute_params.command.hdr.src_domain = APR_DOMAIN_APPS;
+	mute_params.command.hdr.src_port = port_id;
+	mute_params.command.hdr.dest_svc = APR_SVC_ADM;
+	mute_params.command.hdr.dest_domain = APR_DOMAIN_ADSP;
+	mute_params.command.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	mute_params.command.hdr.token = port_idx << 16 | copp_idx;
+	mute_params.command.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	mute_params.command.payload_addr_lsw = 0;
+	mute_params.command.payload_addr_msw = 0;
+	mute_params.command.mem_map_handle = 0;
+	mute_params.command.payload_size = sizeof(mute_params) -
+						sizeof(mute_params.command);
+	mute_params.params.module_id = AUDPROC_MODULE_ID_COMPRESSED_MUTE;
+	mute_params.params.param_id = AUDPROC_PARAM_ID_COMPRESSED_MUTE;
+	mute_params.params.param_size = mute_params.command.payload_size -
+					sizeof(mute_params.params);
+	mute_params.params.reserved = 0;
+	mute_params.mute_on = mute_on;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&mute_params);
+	if (ret < 0) {
+		pr_err("%s: device mute for port %d copp %d failed, ret %d\n",
+			__func__, port_id, copp_idx, ret);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	/* Wait for the callback */
+	ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: send device mute for port %d copp %d failed\n",
+			__func__, port_id, copp_idx);
+		ret = -EINVAL;
+		goto end;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto end;
+	}
+	ret = 0;
+end:
+	return ret;
+}
+
+int adm_send_compressed_device_latency(int port_id, int copp_idx, int latency)
+{
+	struct adm_set_compressed_device_latency latency_params;
+	int port_idx;
+	int ret = 0;
+
+	pr_debug("%s port_id: 0x%x, copp_idx %d latency: %d\n", __func__,
+		 port_id, copp_idx, latency);
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0 || port_idx >= AFE_MAX_PORTS) {
+		pr_err("%s: Invalid port_id %#x copp_idx %d\n",
+			__func__, port_id, copp_idx);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	latency_params.command.hdr.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	latency_params.command.hdr.pkt_size =
+			sizeof(struct adm_set_compressed_device_latency);
+	latency_params.command.hdr.src_svc = APR_SVC_ADM;
+	latency_params.command.hdr.src_domain = APR_DOMAIN_APPS;
+	latency_params.command.hdr.src_port = port_id;
+	latency_params.command.hdr.dest_svc = APR_SVC_ADM;
+	latency_params.command.hdr.dest_domain = APR_DOMAIN_ADSP;
+	latency_params.command.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	latency_params.command.hdr.token = port_idx << 16 | copp_idx;
+	latency_params.command.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	latency_params.command.payload_addr_lsw = 0;
+	latency_params.command.payload_addr_msw = 0;
+	latency_params.command.mem_map_handle = 0;
+	latency_params.command.payload_size = sizeof(latency_params) -
+						sizeof(latency_params.command);
+	latency_params.params.module_id = AUDPROC_MODULE_ID_COMPRESSED_LATENCY;
+	latency_params.params.param_id = AUDPROC_PARAM_ID_COMPRESSED_LATENCY;
+	latency_params.params.param_size = latency_params.command.payload_size -
+					sizeof(latency_params.params);
+	latency_params.params.reserved = 0;
+	latency_params.latency = latency;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&latency_params);
+	if (ret < 0) {
+		pr_err("%s: send device latency err %d for port %d copp %d\n",
+			__func__, port_id, copp_idx, ret);
+		ret = -EINVAL;
+		goto end;
+	}
+
+	/* Wait for the callback */
+	ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: send device latency for port %d failed\n", __func__,
+			port_id);
+		ret = -EINVAL;
+		goto end;
+	} else if (atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&this_adm.copp.stat
+				[port_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[port_idx][copp_idx]));
+		goto end;
+	}
+	ret = 0;
+end:
+	return ret;
+}
+
+int adm_set_sound_focus(int port_id, int copp_idx,
+			struct sound_focus_param soundFocusData)
+{
+	struct adm_set_fluence_soundfocus_param soundfocus_params;
+	int sz = 0;
+	int ret  = 0;
+	int port_idx;
+	int i;
+
+	pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+		  __func__, port_id, copp_idx);
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	port_idx = adm_validate_and_get_port_index(port_id);
+	if (port_idx < 0) {
+		pr_err("%s: Invalid port_id %#x\n", __func__, port_id);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (copp_idx < 0 || copp_idx >= MAX_COPPS_PER_PORT) {
+		pr_err("%s: Invalid copp_num: %d\n", __func__, copp_idx);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	sz = sizeof(struct adm_set_fluence_soundfocus_param);
+	soundfocus_params.params.hdr.hdr_field =
+		APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD, APR_HDR_LEN(APR_HDR_SIZE),
+			      APR_PKT_VER);
+	soundfocus_params.params.hdr.pkt_size = sz;
+	soundfocus_params.params.hdr.src_svc = APR_SVC_ADM;
+	soundfocus_params.params.hdr.src_domain = APR_DOMAIN_APPS;
+	soundfocus_params.params.hdr.src_port = port_id;
+	soundfocus_params.params.hdr.dest_svc = APR_SVC_ADM;
+	soundfocus_params.params.hdr.dest_domain = APR_DOMAIN_ADSP;
+	soundfocus_params.params.hdr.dest_port =
+			atomic_read(&this_adm.copp.id[port_idx][copp_idx]);
+	soundfocus_params.params.hdr.token = port_idx << 16 |
+				ADM_CLIENT_ID_SOURCE_TRACKING << 8 | copp_idx;
+	soundfocus_params.params.hdr.opcode = ADM_CMD_SET_PP_PARAMS_V5;
+	soundfocus_params.params.payload_addr_lsw = 0;
+	soundfocus_params.params.payload_addr_msw = 0;
+	soundfocus_params.params.mem_map_handle = 0;
+	soundfocus_params.params.payload_size = sizeof(soundfocus_params) -
+				sizeof(soundfocus_params.params);
+	soundfocus_params.data.module_id = VOICEPROC_MODULE_ID_GENERIC_TX;
+	soundfocus_params.data.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS;
+	soundfocus_params.data.param_size =
+		soundfocus_params.params.payload_size -
+		sizeof(soundfocus_params.data);
+	soundfocus_params.data.reserved = 0;
+
+	memset(&(soundfocus_params.soundfocus_data), 0xFF,
+		sizeof(struct adm_param_fluence_soundfocus_t));
+	for (i = 0; i < MAX_SECTORS; i++) {
+		soundfocus_params.soundfocus_data.start_angles[i] =
+			soundFocusData.start_angle[i];
+		soundfocus_params.soundfocus_data.enables[i] =
+			soundFocusData.enable[i];
+		pr_debug("%s: start_angle[%d] = %d\n",
+			  __func__, i, soundFocusData.start_angle[i]);
+		pr_debug("%s: enable[%d] = %d\n",
+			  __func__, i, soundFocusData.enable[i]);
+	}
+	soundfocus_params.soundfocus_data.gain_step =
+					soundFocusData.gain_step;
+	pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step);
+
+	soundfocus_params.soundfocus_data.reserved = 0;
+
+	atomic_set(&this_adm.copp.stat[port_idx][copp_idx], -1);
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&soundfocus_params);
+	if (ret < 0) {
+		pr_err("%s: Set params failed\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	/* Wait for the callback */
+	ret = wait_event_timeout(this_adm.copp.wait[port_idx][copp_idx],
+		atomic_read(&this_adm.copp.stat[port_idx][copp_idx]) >= 0,
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Set params timed out\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (this_adm.sourceTrackingData.apr_cmd_status != 0) {
+		pr_err("%s - set params returned error [%s]\n",
+			__func__, adsp_err_get_err_str(
+			this_adm.sourceTrackingData.apr_cmd_status));
+
+		ret = adsp_err_get_lnx_err_code(
+				this_adm.sourceTrackingData.apr_cmd_status);
+		goto done;
+	}
+
+	ret = 0;
+
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+int adm_get_sound_focus(int port_id, int copp_idx,
+			struct sound_focus_param *soundFocusData)
+{
+	int ret = 0, i;
+	char *params_value;
+	uint32_t param_payload_len = sizeof(struct adm_param_data_v5) +
+				sizeof(struct adm_param_fluence_soundfocus_t);
+	struct adm_param_fluence_soundfocus_t *soundfocus_params;
+
+	pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+		  __func__, port_id, copp_idx);
+
+	params_value = kzalloc(param_payload_len, GFP_KERNEL);
+	if (!params_value) {
+		ret = -ENOMEM;
+		goto done;
+	}
+	ret = adm_get_params_v2(port_id, copp_idx,
+				VOICEPROC_MODULE_ID_GENERIC_TX,
+				VOICEPROC_PARAM_ID_FLUENCE_SOUNDFOCUS,
+				param_payload_len,
+				params_value,
+				ADM_CLIENT_ID_SOURCE_TRACKING);
+	if (ret) {
+		pr_err("%s: get parameters failed ret:%d\n", __func__, ret);
+
+		kfree(params_value);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (this_adm.sourceTrackingData.apr_cmd_status != 0) {
+		pr_err("%s - get params returned error [%s]\n",
+			__func__, adsp_err_get_err_str(
+			this_adm.sourceTrackingData.apr_cmd_status));
+
+		kfree(params_value);
+		ret = adsp_err_get_lnx_err_code(
+				this_adm.sourceTrackingData.apr_cmd_status);
+		goto done;
+	}
+
+	soundfocus_params = (struct adm_param_fluence_soundfocus_t *)
+								params_value;
+	for (i = 0; i < MAX_SECTORS; i++) {
+		soundFocusData->start_angle[i] =
+					soundfocus_params->start_angles[i];
+		soundFocusData->enable[i] = soundfocus_params->enables[i];
+		pr_debug("%s: start_angle[%d] = %d\n",
+			  __func__, i, soundFocusData->start_angle[i]);
+		pr_debug("%s: enable[%d] = %d\n",
+			  __func__, i, soundFocusData->enable[i]);
+	}
+	soundFocusData->gain_step = soundfocus_params->gain_step;
+	pr_debug("%s: gain_step = %d\n", __func__, soundFocusData->gain_step);
+
+	kfree(params_value);
+
+done:
+	pr_debug("%s: Exit, ret = %d\n", __func__, ret);
+
+	return ret;
+}
+
+static int adm_source_tracking_alloc_map_memory(void)
+{
+	int ret;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	ret = msm_audio_ion_alloc("SOURCE_TRACKING",
+				  &this_adm.sourceTrackingData.ion_client,
+				  &this_adm.sourceTrackingData.ion_handle,
+				  AUD_PROC_BLOCK_SIZE,
+				  &this_adm.sourceTrackingData.memmap.paddr,
+				  &this_adm.sourceTrackingData.memmap.size,
+				  &this_adm.sourceTrackingData.memmap.kvaddr);
+	if (ret) {
+		pr_err("%s: failed to allocate memory\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	atomic_set(&this_adm.mem_map_index, ADM_MEM_MAP_INDEX_SOURCE_TRACKING);
+	ret = adm_memory_map_regions(&this_adm.sourceTrackingData.memmap.paddr,
+			0,
+			(uint32_t *)&this_adm.sourceTrackingData.memmap.size,
+			1);
+	if (ret < 0) {
+		pr_err("%s: failed to map memory, paddr = 0x%pK, size = %d\n",
+			__func__,
+			(void *)this_adm.sourceTrackingData.memmap.paddr,
+			(uint32_t)this_adm.sourceTrackingData.memmap.size);
+
+		msm_audio_ion_free(this_adm.sourceTrackingData.ion_client,
+				   this_adm.sourceTrackingData.ion_handle);
+		this_adm.sourceTrackingData.ion_client = NULL;
+		this_adm.sourceTrackingData.ion_handle = NULL;
+		this_adm.sourceTrackingData.memmap.size = 0;
+		this_adm.sourceTrackingData.memmap.kvaddr = NULL;
+		this_adm.sourceTrackingData.memmap.paddr = 0;
+		this_adm.sourceTrackingData.apr_cmd_status = -1;
+		atomic_set(&this_adm.mem_map_handles
+				[ADM_MEM_MAP_INDEX_SOURCE_TRACKING], 0);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	ret = 0;
+	pr_debug("%s: paddr = 0x%pK, size = %d, mem_map_handle = 0x%x\n",
+		  __func__, (void *)this_adm.sourceTrackingData.memmap.paddr,
+		  (uint32_t)this_adm.sourceTrackingData.memmap.size,
+		  atomic_read(&this_adm.mem_map_handles
+			      [ADM_MEM_MAP_INDEX_SOURCE_TRACKING]));
+
+done:
+	pr_debug("%s: Exit, ret = %d\n", __func__, ret);
+
+	return ret;
+}
+
+int adm_get_source_tracking(int port_id, int copp_idx,
+			    struct source_tracking_param *sourceTrackingData)
+{
+	struct adm_cmd_get_pp_params_v5 admp;
+	int p_idx, ret = 0, i;
+	struct adm_param_fluence_sourcetracking_t *source_tracking_params;
+
+	pr_debug("%s: Enter, port_id %d, copp_idx %d\n",
+		  __func__, port_id, copp_idx);
+
+	if (!this_adm.sourceTrackingData.memmap.paddr) {
+		/* Allocate and map shared memory for out of band usage */
+		ret = adm_source_tracking_alloc_map_memory();
+		if (ret != 0) {
+			ret = -EINVAL;
+			goto done;
+		}
+	}
+
+	port_id = afe_convert_virtual_to_portid(port_id);
+	p_idx = adm_validate_and_get_port_index(port_id);
+	if (p_idx < 0) {
+		pr_err("%s - invalid port index %i, port id %i, copp idx %i\n",
+			__func__, p_idx, port_id, copp_idx);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	admp.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	admp.hdr.pkt_size = sizeof(admp);
+	admp.hdr.src_svc = APR_SVC_ADM;
+	admp.hdr.src_domain = APR_DOMAIN_APPS;
+	admp.hdr.src_port = port_id;
+	admp.hdr.dest_svc = APR_SVC_ADM;
+	admp.hdr.dest_domain = APR_DOMAIN_ADSP;
+	admp.hdr.dest_port = atomic_read(&this_adm.copp.id[p_idx][copp_idx]);
+	admp.hdr.token = p_idx << 16 | ADM_CLIENT_ID_SOURCE_TRACKING << 8 |
+			 copp_idx;
+	admp.hdr.opcode = ADM_CMD_GET_PP_PARAMS_V5;
+	admp.data_payload_addr_lsw =
+		lower_32_bits(this_adm.sourceTrackingData.memmap.paddr);
+	admp.data_payload_addr_msw =
+		msm_audio_populate_upper_32_bits(
+				this_adm.sourceTrackingData.memmap.paddr);
+	admp.mem_map_handle = atomic_read(&this_adm.mem_map_handles[
+					  ADM_MEM_MAP_INDEX_SOURCE_TRACKING]);
+	admp.module_id = VOICEPROC_MODULE_ID_GENERIC_TX;
+	admp.param_id = VOICEPROC_PARAM_ID_FLUENCE_SOURCETRACKING;
+	admp.param_max_size = sizeof(struct adm_param_fluence_sourcetracking_t)
+				+ sizeof(struct adm_param_data_v5);
+	admp.reserved = 0;
+
+	atomic_set(&this_adm.copp.stat[p_idx][copp_idx], -1);
+
+	ret = apr_send_pkt(this_adm.apr, (uint32_t *)&admp);
+	if (ret < 0) {
+		pr_err("%s - failed to get Source Tracking Params\n",
+			__func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	ret = wait_event_timeout(this_adm.copp.wait[p_idx][copp_idx],
+			atomic_read(&this_adm.copp.stat[p_idx][copp_idx]) >= 0,
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s - get params timed out\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	} else if (atomic_read(&this_adm.copp.stat
+				[p_idx][copp_idx]) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_adm.copp.stat
+			[p_idx][copp_idx])));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_adm.copp.stat
+					[p_idx][copp_idx]));
+		goto done;
+	}
+
+	if (this_adm.sourceTrackingData.apr_cmd_status != 0) {
+		pr_err("%s - get params returned error [%s]\n",
+			__func__, adsp_err_get_err_str(
+			this_adm.sourceTrackingData.apr_cmd_status));
+
+		ret = adsp_err_get_lnx_err_code(
+				this_adm.sourceTrackingData.apr_cmd_status);
+		goto done;
+	}
+
+	source_tracking_params = (struct adm_param_fluence_sourcetracking_t *)
+			(this_adm.sourceTrackingData.memmap.kvaddr +
+			 sizeof(struct adm_param_data_v5));
+	for (i = 0; i < MAX_SECTORS; i++) {
+		sourceTrackingData->vad[i] = source_tracking_params->vad[i];
+		pr_debug("%s: vad[%d] = %d\n",
+			  __func__, i, sourceTrackingData->vad[i]);
+	}
+	sourceTrackingData->doa_speech = source_tracking_params->doa_speech;
+	pr_debug("%s: doa_speech = %d\n",
+		  __func__, sourceTrackingData->doa_speech);
+
+	for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) {
+		sourceTrackingData->doa_noise[i] =
+					source_tracking_params->doa_noise[i];
+		pr_debug("%s: doa_noise[%d] = %d\n",
+			  __func__, i, sourceTrackingData->doa_noise[i]);
+	}
+	for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) {
+		sourceTrackingData->polar_activity[i] =
+				source_tracking_params->polar_activity[i];
+		pr_debug("%s: polar_activity[%d] = %d\n",
+			  __func__, i, sourceTrackingData->polar_activity[i]);
+	}
+
+	ret = 0;
+
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int __init adm_init(void)
+{
+	int i = 0, j;
+
+	this_adm.apr = NULL;
+	this_adm.ec_ref_rx = -1;
+	atomic_set(&this_adm.matrix_map_stat, 0);
+	init_waitqueue_head(&this_adm.matrix_map_wait);
+	atomic_set(&this_adm.adm_stat, 0);
+	init_waitqueue_head(&this_adm.adm_wait);
+
+	for (i = 0; i < AFE_MAX_PORTS; i++) {
+		for (j = 0; j < MAX_COPPS_PER_PORT; j++) {
+			atomic_set(&this_adm.copp.id[i][j], RESET_COPP_ID);
+			atomic_set(&this_adm.copp.cnt[i][j], 0);
+			atomic_set(&this_adm.copp.topology[i][j], 0);
+			atomic_set(&this_adm.copp.mode[i][j], 0);
+			atomic_set(&this_adm.copp.stat[i][j], 0);
+			atomic_set(&this_adm.copp.rate[i][j], 0);
+			atomic_set(&this_adm.copp.channels[i][j], 0);
+			atomic_set(&this_adm.copp.bit_width[i][j], 0);
+			atomic_set(&this_adm.copp.app_type[i][j], 0);
+			atomic_set(&this_adm.copp.acdb_id[i][j], 0);
+			init_waitqueue_head(&this_adm.copp.wait[i][j]);
+			atomic_set(&this_adm.copp.adm_delay_stat[i][j], 0);
+			init_waitqueue_head(
+				&this_adm.copp.adm_delay_wait[i][j]);
+			atomic_set(&this_adm.copp.topology[i][j], 0);
+			this_adm.copp.adm_delay[i][j] = 0;
+			this_adm.copp.adm_status[i][j] =
+				ADM_STATUS_CALIBRATION_REQUIRED;
+		}
+	}
+
+	if (adm_init_cal_data())
+		pr_err("%s: could not init cal data!\n", __func__);
+
+	this_adm.sourceTrackingData.ion_client = NULL;
+	this_adm.sourceTrackingData.ion_handle = NULL;
+	this_adm.sourceTrackingData.memmap.size = 0;
+	this_adm.sourceTrackingData.memmap.kvaddr = NULL;
+	this_adm.sourceTrackingData.memmap.paddr = 0;
+	this_adm.sourceTrackingData.apr_cmd_status = -1;
+	atomic_set(&this_adm.mem_map_handles[ADM_MEM_MAP_INDEX_SOURCE_TRACKING],
+		   0);
+
+	return 0;
+}
+
+static void __exit adm_exit(void)
+{
+	adm_delete_cal_data();
+}
+
+device_initcall(adm_init);
+module_exit(adm_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6afe.c b/sound/soc/msm/qdsp6v2/q6afe.c
new file mode 100644
index 0000000..04dd772
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6afe.c
@@ -0,0 +1,6626 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/wakelock.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/delay.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6audio-v2.h>
+#include "msm-pcm-routing-v2.h"
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+#define WAKELOCK_TIMEOUT	5000
+enum {
+	AFE_COMMON_RX_CAL = 0,
+	AFE_COMMON_TX_CAL,
+	AFE_AANC_CAL,
+	AFE_FB_SPKR_PROT_CAL,
+	AFE_HW_DELAY_CAL,
+	AFE_SIDETONE_CAL,
+	AFE_TOPOLOGY_CAL,
+	AFE_CUST_TOPOLOGY_CAL,
+	AFE_FB_SPKR_PROT_TH_VI_CAL,
+	AFE_FB_SPKR_PROT_EX_VI_CAL,
+	MAX_AFE_CAL_TYPES
+};
+
+enum fbsp_state {
+	FBSP_INCORRECT_OP_MODE,
+	FBSP_INACTIVE,
+	FBSP_WARMUP,
+	FBSP_IN_PROGRESS,
+	FBSP_SUCCESS,
+	FBSP_FAILED,
+	MAX_FBSP_STATE
+};
+
+static char fbsp_state[MAX_FBSP_STATE][50] = {
+	[FBSP_INCORRECT_OP_MODE] = "incorrect operation mode",
+	[FBSP_INACTIVE] = "port not started",
+	[FBSP_WARMUP] = "waiting for warmup",
+	[FBSP_IN_PROGRESS] = "in progress state",
+	[FBSP_SUCCESS] = "success",
+	[FBSP_FAILED] = "failed"
+};
+
+enum {
+	USE_CALIBRATED_R0TO,
+	USE_SAFE_R0TO
+};
+
+enum {
+	QUICK_CALIB_DISABLE,
+	QUICK_CALIB_ENABLE
+};
+
+enum {
+	Q6AFE_MSM_SPKR_PROCESSING = 0,
+	Q6AFE_MSM_SPKR_CALIBRATION,
+	Q6AFE_MSM_SPKR_FTM_MODE
+};
+
+struct wlock {
+	struct wakeup_source ws;
+};
+
+static struct wlock wl;
+
+struct afe_ctl {
+	void *apr;
+	atomic_t state;
+	atomic_t status;
+	wait_queue_head_t wait[AFE_MAX_PORTS];
+	struct task_struct *task;
+	void (*tx_cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv);
+	void (*rx_cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv);
+	void *tx_private_data;
+	void *rx_private_data;
+	uint32_t mmap_handle;
+
+	int	topology[AFE_MAX_PORTS];
+	struct cal_type_data *cal_data[MAX_AFE_CAL_TYPES];
+
+	atomic_t mem_map_cal_handles[MAX_AFE_CAL_TYPES];
+	atomic_t mem_map_cal_index;
+	u32 afe_cal_mode[AFE_MAX_PORTS];
+
+	u16 dtmf_gen_rx_portid;
+	struct audio_cal_info_spk_prot_cfg	prot_cfg;
+	struct afe_spkr_prot_calib_get_resp	calib_data;
+	struct audio_cal_info_sp_th_vi_ftm_cfg	th_ftm_cfg;
+	struct audio_cal_info_sp_ex_vi_ftm_cfg	ex_ftm_cfg;
+	struct afe_sp_th_vi_get_param_resp	th_vi_resp;
+	struct afe_sp_ex_vi_get_param_resp	ex_vi_resp;
+	int vi_tx_port;
+	int vi_rx_port;
+	uint32_t afe_sample_rates[AFE_MAX_PORTS];
+	struct aanc_data aanc_info;
+	struct mutex afe_cmd_lock;
+	int set_custom_topology;
+};
+
+static atomic_t afe_ports_mad_type[SLIMBUS_PORT_LAST - SLIMBUS_0_RX];
+static unsigned long afe_configured_cmd;
+
+static struct afe_ctl this_afe;
+
+#define TIMEOUT_MS 1000
+#define Q6AFE_MAX_VOLUME 0x3FFF
+
+static int pcm_afe_instance[2];
+static int proxy_afe_instance[2];
+bool afe_close_done[2] = {true, true};
+
+#define SIZEOF_CFG_CMD(y) \
+		(sizeof(struct apr_hdr) + sizeof(u16) + (sizeof(struct y)))
+
+static int afe_get_cal_hw_delay(int32_t path,
+				struct audio_cal_hw_delay_entry *entry);
+static int remap_cal_data(struct cal_block_data *cal_block, int cal_index);
+
+int afe_get_topology(int port_id)
+{
+	int topology;
+	int port_index = afe_get_port_index(port_id);
+
+	if ((port_index < 0) || (port_index > AFE_MAX_PORTS)) {
+		pr_err("%s: Invalid port index %d\n", __func__, port_index);
+		topology = -EINVAL;
+		goto done;
+	}
+
+	topology = this_afe.topology[port_index];
+done:
+	return topology;
+}
+
+void afe_set_aanc_info(struct aanc_data *q6_aanc_info)
+{
+	this_afe.aanc_info.aanc_active = q6_aanc_info->aanc_active;
+	this_afe.aanc_info.aanc_rx_port = q6_aanc_info->aanc_rx_port;
+	this_afe.aanc_info.aanc_tx_port = q6_aanc_info->aanc_tx_port;
+
+	pr_debug("%s: aanc active is %d rx port is 0x%x, tx port is 0x%x\n",
+		__func__,
+		this_afe.aanc_info.aanc_active,
+		this_afe.aanc_info.aanc_rx_port,
+		this_afe.aanc_info.aanc_tx_port);
+}
+
+static void afe_callback_debug_print(struct apr_client_data *data)
+{
+	uint32_t *payload;
+
+	payload = data->payload;
+
+	if (data->payload_size >= 8)
+		pr_debug("%s: code = 0x%x PL#0[0x%x], PL#1[0x%x], size = %d\n",
+			__func__, data->opcode, payload[0], payload[1],
+			data->payload_size);
+	else if (data->payload_size >= 4)
+		pr_debug("%s: code = 0x%x PL#0[0x%x], size = %d\n",
+			__func__, data->opcode, payload[0],
+			data->payload_size);
+	else
+		pr_debug("%s: code = 0x%x, size = %d\n",
+			__func__, data->opcode, data->payload_size);
+}
+
+static int32_t sp_make_afe_callback(uint32_t *payload, uint32_t payload_size)
+{
+	u32 param_id;
+	struct afe_spkr_prot_calib_get_resp *resp =
+		(struct afe_spkr_prot_calib_get_resp *) payload;
+
+	if (!(&(resp->pdata))) {
+		pr_err("%s: Error: resp pdata is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	param_id = resp->pdata.param_id;
+	if (param_id == AFE_PARAM_ID_CALIB_RES_CFG_V2) {
+		if (payload_size < sizeof(this_afe.calib_data)) {
+			pr_err("%s: Error: received size %d, calib_data size %zu\n",
+				__func__, payload_size,
+				sizeof(this_afe.calib_data));
+			return -EINVAL;
+		}
+		memcpy(&this_afe.calib_data, payload,
+			sizeof(this_afe.calib_data));
+		if (!this_afe.calib_data.status) {
+			atomic_set(&this_afe.state, 0);
+		} else {
+			pr_debug("%s: calib resp status: %d", __func__,
+				  this_afe.calib_data.status);
+			atomic_set(&this_afe.state, -1);
+		}
+	}
+	if (param_id == AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS) {
+		if (payload_size < sizeof(this_afe.th_vi_resp)) {
+			pr_err("%s: Error: received size %d, th_vi_resp size %zu\n",
+				__func__, payload_size,
+				sizeof(this_afe.th_vi_resp));
+			return -EINVAL;
+		}
+		memcpy(&this_afe.th_vi_resp, payload,
+			sizeof(this_afe.th_vi_resp));
+		if (!this_afe.th_vi_resp.status) {
+			atomic_set(&this_afe.state, 0);
+		} else {
+			pr_debug("%s: th vi resp status: %d", __func__,
+				  this_afe.th_vi_resp.status);
+			atomic_set(&this_afe.state, -1);
+		}
+	}
+	if (param_id == AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS) {
+		if (payload_size < sizeof(this_afe.ex_vi_resp)) {
+			pr_err("%s: Error: received size %d, ex_vi_resp size %zu\n",
+				__func__, payload_size,
+				sizeof(this_afe.ex_vi_resp));
+			return -EINVAL;
+		}
+		memcpy(&this_afe.ex_vi_resp, payload,
+			sizeof(this_afe.ex_vi_resp));
+		if (!this_afe.ex_vi_resp.status) {
+			atomic_set(&this_afe.state, 0);
+		} else {
+			pr_debug("%s: ex vi resp status: %d", __func__,
+				  this_afe.ex_vi_resp.status);
+			atomic_set(&this_afe.state, -1);
+		}
+	}
+
+	return 0;
+}
+
+static int32_t afe_callback(struct apr_client_data *data, void *priv)
+{
+	if (!data) {
+		pr_err("%s: Invalid param data\n", __func__);
+		return -EINVAL;
+	}
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: reset event = %d %d apr[%pK]\n",
+			__func__,
+			data->reset_event, data->reset_proc, this_afe.apr);
+
+		cal_utils_clear_cal_block_q6maps(MAX_AFE_CAL_TYPES,
+			this_afe.cal_data);
+
+		/* Reset the custom topology mode: to resend again to AFE. */
+		mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+		this_afe.set_custom_topology = 1;
+		mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+
+		if (this_afe.apr) {
+			apr_reset(this_afe.apr);
+			atomic_set(&this_afe.state, 0);
+			this_afe.apr = NULL;
+			rtac_set_afe_handle(this_afe.apr);
+		}
+		/* send info to user */
+		if (this_afe.task == NULL)
+			this_afe.task = current;
+		pr_debug("%s: task_name = %s pid = %d\n",
+			__func__,
+			this_afe.task->comm, this_afe.task->pid);
+
+		/*
+		 * Pass reset events to proxy driver, if cb is registered
+		 */
+		if (this_afe.tx_cb) {
+			this_afe.tx_cb(data->opcode, data->token,
+					data->payload,
+					this_afe.tx_private_data);
+			this_afe.tx_cb = NULL;
+		}
+		if (this_afe.rx_cb) {
+			this_afe.rx_cb(data->opcode, data->token,
+					data->payload,
+					this_afe.rx_private_data);
+			this_afe.rx_cb = NULL;
+		}
+
+		return 0;
+	}
+	afe_callback_debug_print(data);
+	if (data->opcode == AFE_PORT_CMDRSP_GET_PARAM_V2) {
+		u8 *payload = data->payload;
+
+		if (rtac_make_afe_callback(data->payload, data->payload_size))
+			return 0;
+
+		if (!payload || (data->token >= AFE_MAX_PORTS)) {
+			pr_err("%s: Error: size %d payload %pK token %d\n",
+				__func__, data->payload_size,
+				payload, data->token);
+			return -EINVAL;
+		}
+		if (sp_make_afe_callback(data->payload, data->payload_size))
+			return -EINVAL;
+
+		wake_up(&this_afe.wait[data->token]);
+	} else if (data->payload_size) {
+		uint32_t *payload;
+		uint16_t port_id = 0;
+
+		payload = data->payload;
+		if (data->opcode == APR_BASIC_RSP_RESULT) {
+			pr_debug("%s:opcode = 0x%x cmd = 0x%x status = 0x%x token=%d\n",
+				__func__, data->opcode,
+				payload[0], payload[1], data->token);
+			/* payload[1] contains the error status for response */
+			if (payload[1] != 0) {
+				atomic_set(&this_afe.status, payload[1]);
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, payload[0], payload[1]);
+			}
+			switch (payload[0]) {
+			case AFE_PORT_CMD_SET_PARAM_V2:
+				if (rtac_make_afe_callback(payload,
+					data->payload_size))
+					return 0;
+			case AFE_PORT_CMD_DEVICE_STOP:
+			case AFE_PORT_CMD_DEVICE_START:
+			case AFE_PSEUDOPORT_CMD_START:
+			case AFE_PSEUDOPORT_CMD_STOP:
+			case AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS:
+			case AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS:
+			case AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER:
+			case AFE_PORTS_CMD_DTMF_CTL:
+			case AFE_SVC_CMD_SET_PARAM:
+				atomic_set(&this_afe.state, 0);
+				wake_up(&this_afe.wait[data->token]);
+				break;
+			case AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER:
+				break;
+			case AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2:
+				port_id = RT_PROXY_PORT_001_TX;
+				break;
+			case AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2:
+				port_id = RT_PROXY_PORT_001_RX;
+				break;
+			case AFE_CMD_ADD_TOPOLOGIES:
+				atomic_set(&this_afe.state, 0);
+				wake_up(&this_afe.wait[data->token]);
+				pr_debug("%s: AFE_CMD_ADD_TOPOLOGIES cmd 0x%x\n",
+						__func__, payload[1]);
+				break;
+			default:
+				pr_err("%s: Unknown cmd 0x%x\n", __func__,
+						payload[0]);
+				break;
+			}
+		} else if (data->opcode ==
+				AFE_SERVICE_CMDRSP_SHARED_MEM_MAP_REGIONS) {
+			pr_debug("%s: mmap_handle: 0x%x, cal index %d\n",
+				 __func__, payload[0],
+				 atomic_read(&this_afe.mem_map_cal_index));
+			if (atomic_read(&this_afe.mem_map_cal_index) != -1)
+				atomic_set(&this_afe.mem_map_cal_handles[
+					atomic_read(
+					&this_afe.mem_map_cal_index)],
+					(uint32_t)payload[0]);
+			else
+				this_afe.mmap_handle = payload[0];
+			atomic_set(&this_afe.state, 0);
+			wake_up(&this_afe.wait[data->token]);
+		} else if (data->opcode == AFE_EVENT_RT_PROXY_PORT_STATUS) {
+			port_id = (uint16_t)(0x0000FFFF & payload[0]);
+		}
+		pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+		switch (port_id) {
+		case RT_PROXY_PORT_001_TX: {
+			if (this_afe.tx_cb) {
+				this_afe.tx_cb(data->opcode, data->token,
+					data->payload,
+					this_afe.tx_private_data);
+			}
+			break;
+		}
+		case RT_PROXY_PORT_001_RX: {
+			if (this_afe.rx_cb) {
+				this_afe.rx_cb(data->opcode, data->token,
+					data->payload,
+					this_afe.rx_private_data);
+			}
+			break;
+		}
+		default:
+			pr_debug("%s: default case 0x%x\n", __func__, port_id);
+			break;
+		}
+	}
+	return 0;
+}
+
+int afe_get_port_type(u16 port_id)
+{
+	int ret;
+
+	switch (port_id) {
+	case PRIMARY_I2S_RX:
+	case SECONDARY_I2S_RX:
+	case MI2S_RX:
+	case HDMI_RX:
+	case DISPLAY_PORT_RX:
+	case AFE_PORT_ID_SPDIF_RX:
+	case SLIMBUS_0_RX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_8_RX:
+	case INT_BT_SCO_RX:
+	case INT_BT_A2DP_RX:
+	case INT_FM_RX:
+	case VOICE_PLAYBACK_TX:
+	case VOICE2_PLAYBACK_TX:
+	case RT_PROXY_PORT_001_RX:
+	case AUDIO_PORT_ID_I2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUINARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_INT0_MI2S_RX:
+	case AFE_PORT_ID_INT1_MI2S_RX:
+	case AFE_PORT_ID_INT2_MI2S_RX:
+	case AFE_PORT_ID_INT3_MI2S_RX:
+	case AFE_PORT_ID_INT4_MI2S_RX:
+	case AFE_PORT_ID_INT5_MI2S_RX:
+	case AFE_PORT_ID_INT6_MI2S_RX:
+		ret = MSM_AFE_PORT_TYPE_RX;
+		break;
+
+	case PRIMARY_I2S_TX:
+	case SECONDARY_I2S_TX:
+	case MI2S_TX:
+	case DIGI_MIC_TX:
+	case VOICE_RECORD_TX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_TX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_TX:
+	case INT_FM_TX:
+	case VOICE_RECORD_RX:
+	case INT_BT_SCO_TX:
+	case RT_PROXY_PORT_001_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case AFE_PORT_ID_QUINARY_MI2S_TX:
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+	case AFE_PORT_ID_USB_TX:
+	case AFE_PORT_ID_INT0_MI2S_TX:
+	case AFE_PORT_ID_INT1_MI2S_TX:
+	case AFE_PORT_ID_INT2_MI2S_TX:
+	case AFE_PORT_ID_INT3_MI2S_TX:
+	case AFE_PORT_ID_INT4_MI2S_TX:
+	case AFE_PORT_ID_INT5_MI2S_TX:
+	case AFE_PORT_ID_INT6_MI2S_TX:
+		ret = MSM_AFE_PORT_TYPE_TX;
+		break;
+
+	default:
+		WARN_ON(1);
+		pr_err("%s: Invalid port id = 0x%x\n",
+			__func__, port_id);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+int afe_sizeof_cfg_cmd(u16 port_id)
+{
+	int ret_size;
+
+	switch (port_id) {
+	case PRIMARY_I2S_RX:
+	case PRIMARY_I2S_TX:
+	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
+	case MI2S_RX:
+	case MI2S_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case AFE_PORT_ID_QUINARY_MI2S_RX:
+	case AFE_PORT_ID_QUINARY_MI2S_TX:
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_i2s_cfg);
+		break;
+	case HDMI_RX:
+	case DISPLAY_PORT_RX:
+		ret_size =
+		SIZEOF_CFG_CMD(afe_param_id_hdmi_multi_chan_audio_cfg);
+		break;
+	case SLIMBUS_0_RX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_5_TX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_8_TX:
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_slimbus_cfg);
+		break;
+	case VOICE_PLAYBACK_TX:
+	case VOICE2_PLAYBACK_TX:
+	case VOICE_RECORD_RX:
+	case VOICE_RECORD_TX:
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_pseudo_port_cfg);
+		break;
+	case RT_PROXY_PORT_001_RX:
+	case RT_PROXY_PORT_001_TX:
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_rt_proxy_port_cfg);
+		break;
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_USB_TX:
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_usb_audio_cfg);
+		break;
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+	default:
+		pr_debug("%s: default case 0x%x\n", __func__, port_id);
+		ret_size = SIZEOF_CFG_CMD(afe_param_id_pcm_cfg);
+		break;
+	}
+	return ret_size;
+}
+
+int afe_q6_interface_prepare(void)
+{
+	int ret = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+			0xFFFFFFFF, &this_afe);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+	return ret;
+}
+
+/*
+ * afe_apr_send_pkt : returns 0 on success, negative otherwise.
+ */
+static int afe_apr_send_pkt(void *data, wait_queue_head_t *wait)
+{
+	int ret;
+
+	if (wait)
+		atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, data);
+	if (ret > 0) {
+		if (wait) {
+			ret = wait_event_timeout(*wait,
+					(atomic_read(&this_afe.state) == 0),
+					msecs_to_jiffies(TIMEOUT_MS));
+			if (!ret) {
+				ret = -ETIMEDOUT;
+			} else if (atomic_read(&this_afe.status) > 0) {
+				pr_err("%s: DSP returned error[%s]\n", __func__,
+					adsp_err_get_err_str(atomic_read(
+					&this_afe.status)));
+				ret = adsp_err_get_lnx_err_code(
+						atomic_read(&this_afe.status));
+			} else {
+				ret = 0;
+			}
+		} else {
+			ret = 0;
+		}
+	} else if (ret == 0) {
+		pr_err("%s: packet not transmitted\n", __func__);
+		/* apr_send_pkt can return 0 when nothing is transmitted */
+		ret = -EINVAL;
+	}
+
+	pr_debug("%s: leave %d\n", __func__, ret);
+	return ret;
+}
+
+static int afe_send_cal_block(u16 port_id, struct cal_block_data *cal_block)
+{
+	int						result = 0;
+	int						index = 0;
+	struct afe_audioif_config_command_no_payload	afe_cal;
+
+	if (!cal_block) {
+		pr_debug("%s: No AFE cal to send!\n", __func__);
+		result = -EINVAL;
+		goto done;
+	}
+	if (cal_block->cal_data.size <= 0) {
+		pr_debug("%s: AFE cal has invalid size!\n", __func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		result = -EINVAL;
+		goto done;
+	}
+
+	afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	afe_cal.hdr.pkt_size = sizeof(afe_cal);
+	afe_cal.hdr.src_port = 0;
+	afe_cal.hdr.dest_port = 0;
+	afe_cal.hdr.token = index;
+	afe_cal.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	afe_cal.param.port_id = port_id;
+	afe_cal.param.payload_size = cal_block->cal_data.size;
+	afe_cal.param.payload_address_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	afe_cal.param.payload_address_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	afe_cal.param.mem_map_handle = cal_block->map_data.q6map_handle;
+
+	pr_debug("%s: AFE cal sent for device port = 0x%x, cal size = %zd, cal addr = 0x%pK\n",
+		__func__, port_id,
+		cal_block->cal_data.size, &cal_block->cal_data.paddr);
+
+	result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]);
+	if (result)
+		pr_err("%s: AFE cal for port 0x%x failed %d\n",
+		       __func__, port_id, result);
+
+done:
+	return result;
+}
+
+
+static int afe_send_custom_topology_block(struct cal_block_data *cal_block)
+{
+	int	result = 0;
+	int	index = 0;
+	struct cmd_set_topologies afe_cal;
+
+	if (!cal_block) {
+		pr_err("%s: No AFE SVC cal to send!\n", __func__);
+		return -EINVAL;
+	}
+	if (cal_block->cal_data.size <= 0) {
+		pr_err("%s: AFE SVC cal has invalid size: %zd!\n",
+		__func__, cal_block->cal_data.size);
+		return -EINVAL;
+	}
+
+	afe_cal.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	afe_cal.hdr.pkt_size = sizeof(afe_cal);
+	afe_cal.hdr.src_port = 0;
+	afe_cal.hdr.dest_port = 0;
+	afe_cal.hdr.token = index;
+	afe_cal.hdr.opcode = AFE_CMD_ADD_TOPOLOGIES;
+
+	afe_cal.payload_size = cal_block->cal_data.size;
+	afe_cal.payload_addr_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	afe_cal.payload_addr_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	afe_cal.mem_map_handle = cal_block->map_data.q6map_handle;
+
+	pr_debug("%s:cmd_id:0x%x calsize:%zd memmap_hdl:0x%x caladdr:0x%pK",
+		__func__, AFE_CMD_ADD_TOPOLOGIES, cal_block->cal_data.size,
+		afe_cal.mem_map_handle, &cal_block->cal_data.paddr);
+
+	result = afe_apr_send_pkt(&afe_cal, &this_afe.wait[index]);
+	if (result)
+		pr_err("%s: AFE send topology for command 0x%x failed %d\n",
+		       __func__, AFE_CMD_ADD_TOPOLOGIES, result);
+
+	return result;
+}
+
+static void afe_send_custom_topology(void)
+{
+	struct cal_block_data   *cal_block = NULL;
+	int cal_index = AFE_CUST_TOPOLOGY_CAL;
+	int ret;
+
+	if (this_afe.cal_data[cal_index] == NULL) {
+		pr_err("%s: cal_index %d not allocated!\n",
+			__func__, cal_index);
+		return;
+	}
+	mutex_lock(&this_afe.cal_data[cal_index]->lock);
+
+	if (!this_afe.set_custom_topology)
+		goto unlock;
+	this_afe.set_custom_topology = 0;
+	cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]);
+	if (cal_block == NULL) {
+		pr_err("%s cal_block not found!!\n", __func__);
+		goto unlock;
+	}
+
+	pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index);
+
+	ret = remap_cal_data(cal_block, cal_index);
+	if (ret) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, cal_index);
+		goto unlock;
+	}
+	ret = afe_send_custom_topology_block(cal_block);
+	if (ret < 0) {
+		pr_err("%s: No cal sent for cal_index %d! ret %d\n",
+			__func__, cal_index, ret);
+		goto unlock;
+	}
+	pr_debug("%s:sent custom topology for AFE\n", __func__);
+unlock:
+	mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+}
+
+static int afe_spk_ramp_dn_cfg(int port)
+{
+	int ret = -EINVAL;
+	int index = 0;
+	struct afe_spkr_prot_config_command config;
+
+	if (afe_get_port_type(port) != MSM_AFE_PORT_TYPE_RX) {
+		pr_debug("%s: port doesn't match 0x%x\n", __func__, port);
+		return 0;
+	}
+	if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_DISABLED ||
+		(this_afe.vi_rx_port != port)) {
+		pr_debug("%s: spkr protection disabled port 0x%x %d 0x%x\n",
+				__func__, port, ret, this_afe.vi_rx_port);
+		return 0;
+	}
+	memset(&config, 0, sizeof(config));
+	ret = q6audio_validate_port(port);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d", __func__, port, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	index = q6audio_get_port_index(port);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port);
+	config.param.payload_size =
+		sizeof(config) - sizeof(config.hdr) - sizeof(config.param)
+		- sizeof(config.prot_config);
+	config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
+	config.pdata.param_id = AFE_PARAM_ID_FBSP_PTONE_RAMP_CFG;
+	config.pdata.param_size = 0;
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+	if (ret < 0) {
+		pr_err("%s: port = 0x%x param = 0x%x failed %d\n",
+				__func__, port, config.pdata.param_id, ret);
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+	/* dsp needs atleast 15ms to ramp down pilot tone*/
+	usleep_range(15000, 15010);
+	ret = 0;
+fail_cmd:
+	pr_debug("%s: config.pdata.param_id 0x%x status %d\n",
+		__func__, config.pdata.param_id, ret);
+return ret;
+}
+
+static int afe_spk_prot_prepare(int src_port, int dst_port, int param_id,
+		union afe_spkr_prot_config *prot_config)
+{
+	int ret = -EINVAL;
+	int index = 0;
+	struct afe_spkr_prot_config_command config;
+
+	memset(&config, 0, sizeof(config));
+	if (!prot_config) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto fail_cmd;
+	}
+	ret = q6audio_validate_port(src_port);
+	if (ret < 0) {
+		pr_err("%s: Invalid src port 0x%x ret %d",
+				__func__, src_port, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = q6audio_validate_port(dst_port);
+	if (ret < 0) {
+		pr_err("%s: Invalid dst port 0x%x ret %d", __func__,
+				dst_port, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	index = q6audio_get_port_index(src_port);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	switch (param_id) {
+	case AFE_PARAM_ID_FBSP_MODE_RX_CFG:
+		config.pdata.module_id = AFE_MODULE_FB_SPKR_PROT_V2_RX;
+		break;
+	case AFE_PARAM_ID_FEEDBACK_PATH_CFG:
+		this_afe.vi_tx_port = src_port;
+		this_afe.vi_rx_port = dst_port;
+		config.pdata.module_id = AFE_MODULE_FEEDBACK;
+		break;
+	/*
+	 * AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2 is same as
+	 * AFE_PARAM_ID_SP_V2_TH_VI_MODE_CFG
+	 */
+	case AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2:
+	case AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG:
+		config.pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI;
+		break;
+	case AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG:
+	case AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG:
+		config.pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
+		break;
+	default:
+		pr_err("%s: default case 0x%x\n", __func__, param_id);
+		goto fail_cmd;
+	}
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(src_port);
+	config.param.payload_size = sizeof(config) - sizeof(config.hdr)
+		- sizeof(config.param);
+	config.pdata.param_id = param_id;
+	config.pdata.param_size = sizeof(config.prot_config);
+	config.prot_config = *prot_config;
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+	if (ret < 0) {
+		pr_err("%s: port = 0x%x param = 0x%x failed %d\n",
+		__func__, src_port, param_id, ret);
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+		(atomic_read(&this_afe.state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+	ret = 0;
+fail_cmd:
+	pr_debug("%s: config.pdata.param_id 0x%x status %d 0x%x\n",
+	__func__, config.pdata.param_id, ret, src_port);
+	return ret;
+}
+
+static void afe_send_cal_spkr_prot_tx(int port_id)
+{
+	union afe_spkr_prot_config afe_spk_config;
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL ||
+	    this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+	    this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL)
+		return;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+	if ((this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED) &&
+		(this_afe.vi_tx_port == port_id)) {
+		if (this_afe.prot_cfg.mode ==
+			MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) {
+			afe_spk_config.vi_proc_cfg.operation_mode =
+					Q6AFE_MSM_SPKR_CALIBRATION;
+			afe_spk_config.vi_proc_cfg.quick_calib_flag =
+					this_afe.prot_cfg.quick_calib_flag;
+		} else {
+			afe_spk_config.vi_proc_cfg.operation_mode =
+					Q6AFE_MSM_SPKR_PROCESSING;
+		}
+
+		if (this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE)
+			afe_spk_config.vi_proc_cfg.operation_mode =
+					    Q6AFE_MSM_SPKR_FTM_MODE;
+		afe_spk_config.vi_proc_cfg.minor_version = 1;
+		afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_1] =
+			(uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_1];
+		afe_spk_config.vi_proc_cfg.r0_cali_q24[SP_V2_SPKR_2] =
+			(uint32_t) this_afe.prot_cfg.r0[SP_V2_SPKR_2];
+		afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_1] =
+			(uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_1];
+		afe_spk_config.vi_proc_cfg.t0_cali_q6[SP_V2_SPKR_2] =
+			(uint32_t) this_afe.prot_cfg.t0[SP_V2_SPKR_2];
+		if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_NOT_CALIBRATED) {
+			struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg;
+
+			vi_proc_cfg = &afe_spk_config.vi_proc_cfg;
+			vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] =
+					    USE_CALIBRATED_R0TO;
+			vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] =
+					    USE_CALIBRATED_R0TO;
+		} else {
+			struct asm_spkr_calib_vi_proc_cfg *vi_proc_cfg;
+
+			vi_proc_cfg = &afe_spk_config.vi_proc_cfg;
+			vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_1] =
+							    USE_SAFE_R0TO;
+			vi_proc_cfg->r0_t0_selection_flag[SP_V2_SPKR_2] =
+							    USE_SAFE_R0TO;
+		}
+		if (afe_spk_prot_prepare(port_id, 0,
+			AFE_PARAM_ID_SPKR_CALIB_VI_PROC_CFG_V2,
+				&afe_spk_config))
+			pr_err("%s: SPKR_CALIB_VI_PROC_CFG failed\n",
+				__func__);
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+	if ((this_afe.th_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) &&
+	    (this_afe.vi_tx_port == port_id)) {
+		afe_spk_config.th_vi_ftm_cfg.minor_version = 1;
+		afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] =
+			this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_1];
+		afe_spk_config.th_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] =
+			this_afe.th_ftm_cfg.wait_time[SP_V2_SPKR_2];
+		afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] =
+			this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_1];
+		afe_spk_config.th_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] =
+			this_afe.th_ftm_cfg.ftm_time[SP_V2_SPKR_2];
+
+		if (afe_spk_prot_prepare(port_id, 0,
+					 AFE_PARAM_ID_SP_V2_TH_VI_FTM_CFG,
+					 &afe_spk_config))
+			pr_err("%s: th vi ftm cfg failed\n", __func__);
+		this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+	if ((this_afe.ex_ftm_cfg.mode == MSM_SPKR_PROT_IN_FTM_MODE) &&
+	    (this_afe.vi_tx_port == port_id)) {
+		afe_spk_config.ex_vi_mode_cfg.minor_version = 1;
+		afe_spk_config.ex_vi_mode_cfg.operation_mode =
+						Q6AFE_MSM_SPKR_FTM_MODE;
+		if (afe_spk_prot_prepare(port_id, 0,
+					 AFE_PARAM_ID_SP_V2_EX_VI_MODE_CFG,
+					 &afe_spk_config))
+			pr_err("%s: ex vi mode cfg failed\n", __func__);
+
+		afe_spk_config.ex_vi_ftm_cfg.minor_version = 1;
+		afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_1] =
+			this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_1];
+		afe_spk_config.ex_vi_ftm_cfg.wait_time_ms[SP_V2_SPKR_2] =
+			this_afe.ex_ftm_cfg.wait_time[SP_V2_SPKR_2];
+		afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_1] =
+			this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_1];
+		afe_spk_config.ex_vi_ftm_cfg.ftm_time_ms[SP_V2_SPKR_2] =
+			this_afe.ex_ftm_cfg.ftm_time[SP_V2_SPKR_2];
+
+		if (afe_spk_prot_prepare(port_id, 0,
+					 AFE_PARAM_ID_SP_V2_EX_VI_FTM_CFG,
+					 &afe_spk_config))
+			pr_err("%s: ex vi ftm cfg failed\n", __func__);
+		this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+
+}
+
+static void afe_send_cal_spkr_prot_rx(int port_id)
+{
+	union afe_spkr_prot_config afe_spk_config;
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+		goto done;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+
+	if (this_afe.prot_cfg.mode != MSM_SPKR_PROT_DISABLED &&
+		(this_afe.vi_rx_port == port_id)) {
+		if (this_afe.prot_cfg.mode ==
+			MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+			afe_spk_config.mode_rx_cfg.mode =
+				Q6AFE_MSM_SPKR_CALIBRATION;
+		else
+			afe_spk_config.mode_rx_cfg.mode =
+				Q6AFE_MSM_SPKR_PROCESSING;
+		afe_spk_config.mode_rx_cfg.minor_version = 1;
+		if (afe_spk_prot_prepare(port_id, 0,
+			AFE_PARAM_ID_FBSP_MODE_RX_CFG,
+			&afe_spk_config))
+			pr_err("%s: RX MODE_VI_PROC_CFG failed\n",
+				   __func__);
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+done:
+	return;
+}
+
+static int afe_send_hw_delay(u16 port_id, u32 rate)
+{
+	struct audio_cal_hw_delay_entry		delay_entry;
+	struct afe_audioif_config_command	config;
+	int index = 0;
+	int ret = -EINVAL;
+
+	pr_debug("%s:\n", __func__);
+
+	delay_entry.sample_rate = rate;
+	if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX)
+		ret = afe_get_cal_hw_delay(TX_DEVICE, &delay_entry);
+	else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX)
+		ret = afe_get_cal_hw_delay(RX_DEVICE, &delay_entry);
+
+	/*
+	 * HW delay is only used for IMS calls to sync audio with video
+	 * It is only needed for devices & sample rates used for IMS video
+	 * calls. Values are received from ACDB calbration files
+	 */
+	if (ret != 0) {
+		pr_debug("%s: debug: HW delay info not available %d\n",
+			__func__, ret);
+		goto fail_cmd;
+	}
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id = AFE_PARAM_ID_DEVICE_HW_DELAY;
+	config.pdata.param_size = sizeof(config.port);
+
+	config.port.hw_delay.delay_in_us = delay_entry.delay_usec;
+	config.port.hw_delay.device_hw_delay_minor_version =
+				AFE_API_VERSION_DEVICE_HW_DELAY;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE hw delay for port 0x%x failed %d\n",
+		       __func__, port_id, ret);
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	pr_debug("%s: port_id 0x%x rate %u delay_usec %d status %d\n",
+	__func__, port_id, rate, delay_entry.delay_usec, ret);
+	return ret;
+}
+
+static struct cal_block_data *afe_find_cal_topo_id_by_port(
+			struct cal_type_data *cal_type, u16 port_id)
+{
+	struct list_head		*ptr, *next;
+	struct cal_block_data	*cal_block = NULL;
+	int32_t path;
+	struct audio_cal_info_afe_top *afe_top;
+
+	list_for_each_safe(ptr, next,
+		&cal_type->cal_blocks) {
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		path = ((afe_get_port_type(port_id) ==
+			MSM_AFE_PORT_TYPE_TX)?(TX_DEVICE):(RX_DEVICE));
+		afe_top =
+		(struct audio_cal_info_afe_top *)cal_block->cal_info;
+		if (afe_top->path == path) {
+			pr_debug("%s: top_id:%x acdb_id:%d afe_port:%d\n",
+			__func__, afe_top->topology, afe_top->acdb_id,
+			q6audio_get_port_id(port_id));
+			return cal_block;
+		}
+	}
+	return NULL;
+}
+
+static int afe_get_cal_topology_id(u16 port_id, u32 *topology_id)
+{
+	int ret = 0;
+
+	struct cal_block_data   *cal_block = NULL;
+	struct audio_cal_info_afe_top   *afe_top_info = NULL;
+
+	if (this_afe.cal_data[AFE_TOPOLOGY_CAL] == NULL) {
+		pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized\n", __func__);
+		return -EINVAL;
+	}
+	if (topology_id == NULL) {
+		pr_err("%s: topology_id is NULL\n", __func__);
+		return -EINVAL;
+	}
+	*topology_id = 0;
+
+	mutex_lock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock);
+	cal_block = afe_find_cal_topo_id_by_port(
+		this_afe.cal_data[AFE_TOPOLOGY_CAL], port_id);
+	if (cal_block == NULL) {
+		pr_err("%s: [AFE_TOPOLOGY_CAL] not initialized for this port %d\n",
+				__func__, port_id);
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	afe_top_info = ((struct audio_cal_info_afe_top *)
+		cal_block->cal_info);
+	if (!afe_top_info->topology) {
+		pr_err("%s: invalid topology id : [%d, %d]\n",
+		       __func__, afe_top_info->acdb_id, afe_top_info->topology);
+		ret = -EINVAL;
+		goto unlock;
+	}
+	*topology_id = (u32)afe_top_info->topology;
+
+	pr_debug("%s: port_id = %u acdb_id = %d topology_id = %u ret=%d\n",
+		__func__, port_id, afe_top_info->acdb_id,
+		afe_top_info->topology, ret);
+unlock:
+	mutex_unlock(&this_afe.cal_data[AFE_TOPOLOGY_CAL]->lock);
+	return ret;
+}
+
+static int afe_send_port_topology_id(u16 port_id)
+{
+	struct afe_audioif_config_command	config;
+	int index = 0;
+	int ret = 0;
+	u32 topology_id = 0;
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS - 1) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+
+	ret = afe_get_cal_topology_id(port_id, &topology_id);
+	if (ret || !topology_id) {
+		pr_debug("%s: AFE port[%d] get_cal_topology[%d] invalid!\n",
+				__func__, port_id, topology_id);
+		goto done;
+	}
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id   = AFE_PARAM_ID_SET_TOPOLOGY;
+	config.pdata.param_size =  sizeof(config.port);
+	config.port.topology.minor_version = AFE_API_VERSION_TOPOLOGY_V1;
+	config.port.topology.topology_id = topology_id;
+
+	pr_debug("%s: param PL size=%d iparam_size[%d][%zd %zd %zd %zd] param_id[0x%x]\n",
+		__func__, config.param.payload_size, config.pdata.param_size,
+		sizeof(config), sizeof(config.param), sizeof(config.port),
+		sizeof(struct apr_hdr), config.pdata.param_id);
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE set topology id enable for port 0x%x failed %d\n",
+			__func__, port_id, ret);
+		goto done;
+	}
+
+	this_afe.topology[index] = topology_id;
+done:
+	pr_debug("%s: AFE set topology id 0x%x  enable for port 0x%x ret %d\n",
+			__func__, topology_id, port_id, ret);
+	return ret;
+
+}
+
+static int remap_cal_data(struct cal_block_data *cal_block, int cal_index)
+{
+	int ret = 0;
+
+	if (cal_block->map_data.ion_client == NULL) {
+		pr_err("%s: No ION allocation for cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((cal_block->map_data.map_size > 0) &&
+		(cal_block->map_data.q6map_handle == 0)) {
+		atomic_set(&this_afe.mem_map_cal_index, cal_index);
+		ret = afe_cmd_memory_map(cal_block->cal_data.paddr,
+				cal_block->map_data.map_size);
+		atomic_set(&this_afe.mem_map_cal_index, -1);
+		if (ret < 0) {
+			pr_err("%s: mmap did not work! size = %zd ret %d\n",
+				__func__,
+				cal_block->map_data.map_size, ret);
+			pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n",
+				__func__,
+				&cal_block->cal_data.paddr,
+				cal_block->map_data.map_size);
+			goto done;
+		}
+		cal_block->map_data.q6map_handle = atomic_read(&this_afe.
+			mem_map_cal_handles[cal_index]);
+	}
+done:
+	return ret;
+}
+
+static void send_afe_cal_type(int cal_index, int port_id)
+{
+	struct cal_block_data		*cal_block = NULL;
+	int ret;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.cal_data[cal_index] == NULL) {
+		pr_warn("%s: cal_index %d not allocated!\n",
+			__func__, cal_index);
+		goto done;
+	}
+
+	mutex_lock(&this_afe.cal_data[cal_index]->lock);
+	cal_block = cal_utils_get_only_cal_block(this_afe.cal_data[cal_index]);
+	if (cal_block == NULL) {
+		pr_err("%s cal_block not found!!\n", __func__);
+		goto unlock;
+	}
+
+	pr_debug("%s: Sending cal_index cal %d\n", __func__, cal_index);
+
+	ret = remap_cal_data(cal_block, cal_index);
+	if (ret) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, cal_index);
+		goto unlock;
+	}
+	ret = afe_send_cal_block(port_id, cal_block);
+	if (ret < 0)
+		pr_debug("%s: No cal sent for cal_index %d, port_id = 0x%x! ret %d\n",
+			__func__, cal_index, port_id, ret);
+unlock:
+	mutex_unlock(&this_afe.cal_data[cal_index]->lock);
+done:
+	return;
+}
+
+void afe_send_cal(u16 port_id)
+{
+	pr_debug("%s: port_id=0x%x\n", __func__, port_id);
+
+	if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_TX) {
+		afe_send_cal_spkr_prot_tx(port_id);
+		send_afe_cal_type(AFE_COMMON_TX_CAL, port_id);
+	} else if (afe_get_port_type(port_id) == MSM_AFE_PORT_TYPE_RX) {
+		afe_send_cal_spkr_prot_rx(port_id);
+		send_afe_cal_type(AFE_COMMON_RX_CAL, port_id);
+	}
+}
+
+int afe_turn_onoff_hw_mad(u16 mad_type, u16 enable)
+{
+	int ret;
+	struct afe_cmd_hw_mad_ctrl config;
+
+	pr_debug("%s: enter\n", __func__);
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = SLIMBUS_5_TX;
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_HW_MAD;
+	config.pdata.param_id = AFE_PARAM_ID_HW_MAD_CTRL;
+	config.pdata.param_size = sizeof(config.payload);
+	config.payload.minor_version = 1;
+	config.payload.mad_type = mad_type;
+	config.payload.mad_enable = enable;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret)
+		pr_err("%s: AFE_PARAM_ID_HW_MAD_CTRL failed %d\n", __func__,
+		       ret);
+	return ret;
+}
+
+static int afe_send_slimbus_slave_cfg(
+	struct afe_param_cdc_slimbus_slave_cfg *sb_slave_cfg)
+{
+	int ret;
+	struct afe_svc_cmd_sb_slave_cfg config;
+
+	pr_debug("%s: enter\n", __func__);
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG;
+	config.pdata.param_id = AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG;
+	config.pdata.param_size =
+	    sizeof(struct afe_param_cdc_slimbus_slave_cfg);
+	config.sb_slave_cfg = *sb_slave_cfg;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret)
+		pr_err("%s: AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG failed %d\n",
+		       __func__, ret);
+
+	pr_debug("%s: leave %d\n", __func__, ret);
+	return ret;
+}
+
+static int afe_send_codec_reg_page_config(
+	struct afe_param_cdc_reg_page_cfg *cdc_reg_page_cfg)
+{
+	struct afe_svc_cmd_cdc_reg_page_cfg config;
+	int ret;
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG;
+	config.pdata.param_id = AFE_PARAM_ID_CDC_REG_PAGE_CFG;
+	config.pdata.param_size =
+	    sizeof(struct afe_param_cdc_reg_page_cfg);
+	config.cdc_reg_page_cfg = *cdc_reg_page_cfg;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret)
+		pr_err("%s: AFE_PARAM_ID_CDC_REG_PAGE_CFG failed %d\n",
+		       __func__, ret);
+
+	return ret;
+}
+
+static int afe_send_codec_reg_config(
+	struct afe_param_cdc_reg_cfg_data *cdc_reg_cfg)
+{
+	int i, j, ret = -EINVAL;
+	int pkt_size, payload_size, reg_per_pkt, num_pkts, num_regs;
+	struct afe_svc_cmd_cdc_reg_cfg *config;
+	struct afe_svc_cmd_set_param *param;
+
+	reg_per_pkt = (APR_MAX_BUF - sizeof(*config)) /
+			sizeof(struct afe_param_cdc_reg_cfg_payload);
+	if (reg_per_pkt > 0) {
+		num_pkts = (cdc_reg_cfg->num_registers / reg_per_pkt) +
+			(cdc_reg_cfg->num_registers % reg_per_pkt == 0 ? 0 : 1);
+	} else {
+		pr_err("%s: Failed to build codec reg config APR packet\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	for (j = 0; j < num_pkts; ++j) {
+		/*
+		 * num_regs is set to reg_per_pkt on each pass through the loop
+		 * except the last, when it is set to the number of registers
+		 * remaining from the total
+		 */
+		num_regs = (j < (num_pkts - 1) ? reg_per_pkt :
+				cdc_reg_cfg->num_registers - (reg_per_pkt * j));
+		payload_size = sizeof(struct afe_param_cdc_reg_cfg_payload) *
+				num_regs;
+		pkt_size = sizeof(*config) + payload_size;
+		pr_debug("%s: pkt_size %d, payload_size %d\n", __func__,
+			 pkt_size, payload_size);
+		config = kzalloc(pkt_size, GFP_KERNEL);
+		if (!config)
+			return -ENOMEM;
+
+		config->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						      APR_HDR_LEN(APR_HDR_SIZE),
+						      APR_PKT_VER);
+		config->hdr.pkt_size = pkt_size;
+		config->hdr.src_port = 0;
+		config->hdr.dest_port = 0;
+		config->hdr.token = IDX_GLOBAL_CFG;
+		config->hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+		param = &config->param;
+		param->payload_size = payload_size;
+		param->payload_address_lsw = 0x00;
+		param->payload_address_msw = 0x00;
+		param->mem_map_handle = 0x00;
+
+		for (i = 0; i < num_regs; i++) {
+			config->reg_data[i].common.module_id =
+						AFE_MODULE_CDC_DEV_CFG;
+			config->reg_data[i].common.param_id =
+						AFE_PARAM_ID_CDC_REG_CFG;
+			config->reg_data[i].common.param_size =
+			    sizeof(config->reg_data[i].reg_cfg);
+			config->reg_data[i].reg_cfg =
+				cdc_reg_cfg->reg_data[i + (j * reg_per_pkt)];
+		}
+
+		ret = afe_apr_send_pkt(config, &this_afe.wait[IDX_GLOBAL_CFG]);
+		if (ret) {
+			pr_err("%s: AFE_PARAM_ID_CDC_REG_CFG failed %d\n",
+				__func__, ret);
+			kfree(config);
+			break;
+		}
+		kfree(config);
+	}
+
+	return ret;
+}
+
+static int afe_init_cdc_reg_config(void)
+{
+	int ret;
+	struct afe_svc_cmd_init_cdc_reg_cfg config;
+
+	pr_debug("%s: enter\n", __func__);
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+	config.param.payload_size = sizeof(struct afe_port_param_data_v2);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+
+	config.init.module_id = AFE_MODULE_CDC_DEV_CFG;
+	config.init.param_id = AFE_PARAM_ID_CDC_REG_CFG_INIT;
+	config.init.param_size = 0;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret) {
+		pr_err("%s: AFE_PARAM_ID_CDC_INIT_REG_CFG failed %d\n",
+		       __func__, ret);
+	}
+
+	return ret;
+}
+
+static int afe_send_slimbus_slave_port_cfg(
+	struct afe_param_slimbus_slave_port_cfg *port_config, u16 port_id)
+{
+	int ret, index;
+	struct afe_cmd_hw_mad_slimbus_slave_port_cfg config;
+
+	pr_debug("%s: enter, port_id =  0x%x\n", __func__, port_id);
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id = 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = port_id;
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_HW_MAD;
+	config.pdata.param_id = AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG;
+	config.pdata.param_size = sizeof(*port_config);
+	config.sb_port_cfg = *port_config;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE_PARAM_ID_SLIMBUS_SLAVE_PORT_CFG failed %d\n",
+			__func__, ret);
+	}
+	pr_debug("%s: leave %d\n", __func__, ret);
+	return ret;
+}
+static int afe_aanc_port_cfg(void *apr, uint16_t tx_port, uint16_t rx_port)
+{
+	struct afe_port_cmd_set_aanc_param cfg;
+	int ret = 0;
+	int index = 0;
+
+	pr_debug("%s: tx_port 0x%x, rx_port 0x%x\n",
+		__func__, tx_port, rx_port);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	index = q6audio_get_port_index(tx_port);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(tx_port);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, tx_port, ret);
+		return -EINVAL;
+	}
+	pr_debug("%s: AANC sample rate tx rate: %d rx rate %d\n",
+		__func__, this_afe.aanc_info.aanc_tx_port_sample_rate,
+	       this_afe.aanc_info.aanc_rx_port_sample_rate);
+	/*
+	 * If aanc tx sample rate or rx sample rate is zero, skip aanc
+	 * configuration as AFE resampler will fail for invalid sample
+	 * rates.
+	 */
+	if (!this_afe.aanc_info.aanc_tx_port_sample_rate ||
+	    !this_afe.aanc_info.aanc_rx_port_sample_rate) {
+		return -EINVAL;
+	}
+
+	cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cfg.hdr.pkt_size = sizeof(cfg);
+	cfg.hdr.src_port = 0;
+	cfg.hdr.dest_port = 0;
+	cfg.hdr.token = index;
+	cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+
+	cfg.param.port_id = tx_port;
+	cfg.param.payload_size        = sizeof(struct afe_port_param_data_v2) +
+					sizeof(struct afe_param_aanc_port_cfg);
+	cfg.param.payload_address_lsw     = 0;
+	cfg.param.payload_address_msw     = 0;
+	cfg.param.mem_map_handle	  = 0;
+
+	cfg.pdata.module_id = AFE_MODULE_AANC;
+	cfg.pdata.param_id    = AFE_PARAM_ID_AANC_PORT_CONFIG;
+	cfg.pdata.param_size = sizeof(struct afe_param_aanc_port_cfg);
+	cfg.pdata.reserved    = 0;
+
+	cfg.data.aanc_port_cfg.aanc_port_cfg_minor_version =
+		AFE_API_VERSION_AANC_PORT_CONFIG;
+	cfg.data.aanc_port_cfg.tx_port_sample_rate =
+		this_afe.aanc_info.aanc_tx_port_sample_rate;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[0] = AANC_TX_VOICE_MIC;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[1] = AANC_TX_NOISE_MIC;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[2] = AANC_TX_ERROR_MIC;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[3] = AANC_TX_MIC_UNUSED;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[4] = AANC_TX_MIC_UNUSED;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[5] = AANC_TX_MIC_UNUSED;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[6] = AANC_TX_MIC_UNUSED;
+	cfg.data.aanc_port_cfg.tx_port_channel_map[7] = AANC_TX_MIC_UNUSED;
+	cfg.data.aanc_port_cfg.tx_port_num_channels = 3;
+	cfg.data.aanc_port_cfg.rx_path_ref_port_id = rx_port;
+	cfg.data.aanc_port_cfg.ref_port_sample_rate =
+		 this_afe.aanc_info.aanc_rx_port_sample_rate;
+
+	ret = afe_apr_send_pkt((uint32_t *) &cfg, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE AANC port config failed for tx_port 0x%x, rx_port 0x%x ret %d\n",
+			__func__, tx_port, rx_port, ret);
+	}
+
+	return ret;
+}
+
+static int afe_aanc_mod_enable(void *apr, uint16_t tx_port, uint16_t enable)
+{
+	struct afe_port_cmd_set_aanc_param cfg;
+	int ret = 0;
+	int index = 0;
+
+	pr_debug("%s: tx_port 0x%x\n",
+		__func__, tx_port);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	index = q6audio_get_port_index(tx_port);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(tx_port);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, tx_port, ret);
+		return -EINVAL;
+	}
+
+	cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cfg.hdr.pkt_size = sizeof(cfg);
+	cfg.hdr.src_port = 0;
+	cfg.hdr.dest_port = 0;
+	cfg.hdr.token = index;
+	cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+
+	cfg.param.port_id = tx_port;
+	cfg.param.payload_size        = sizeof(struct afe_port_param_data_v2) +
+					sizeof(struct afe_mod_enable_param);
+	cfg.param.payload_address_lsw     = 0;
+	cfg.param.payload_address_lsw     = 0;
+	cfg.param.mem_map_handle          = 0;
+
+	cfg.pdata.module_id = AFE_MODULE_AANC;
+	cfg.pdata.param_id    = AFE_PARAM_ID_ENABLE;
+	cfg.pdata.param_size = sizeof(struct afe_mod_enable_param);
+	cfg.pdata.reserved    = 0;
+
+	cfg.data.mod_enable.enable = enable;
+	cfg.data.mod_enable.reserved = 0;
+
+	ret = afe_apr_send_pkt((uint32_t *) &cfg, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE AANC enable failed for tx_port 0x%x ret %d\n",
+			__func__, tx_port, ret);
+	}
+	return ret;
+}
+
+static int afe_send_bank_selection_clip(
+		struct afe_param_id_clip_bank_sel *param)
+{
+	int ret;
+	struct afe_svc_cmd_set_clip_bank_selection config;
+
+	if (!param) {
+		pr_err("%s: Invalid params", __func__);
+		return -EINVAL;
+	}
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+	config.param.payload_size = sizeof(struct afe_port_param_data_v2) +
+				sizeof(struct afe_param_id_clip_bank_sel);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+
+	config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG;
+	config.pdata.param_id = AFE_PARAM_ID_CLIP_BANK_SEL_CFG;
+	config.pdata.param_size =
+		sizeof(struct afe_param_id_clip_bank_sel);
+	config.bank_sel = *param;
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret) {
+		pr_err("%s: AFE_PARAM_ID_CLIP_BANK_SEL_CFG failed %d\n",
+		__func__, ret);
+	}
+	return ret;
+}
+int afe_send_aanc_version(
+	struct afe_param_id_cdc_aanc_version *version_cfg)
+{
+	int ret;
+	struct afe_svc_cmd_cdc_aanc_version config;
+
+	pr_debug("%s: enter\n", __func__);
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+	config.param.payload_size = sizeof(struct afe_port_param_data_v2) +
+				sizeof(struct afe_param_id_cdc_aanc_version);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+
+	config.pdata.module_id = AFE_MODULE_CDC_DEV_CFG;
+	config.pdata.param_id = AFE_PARAM_ID_CDC_AANC_VERSION;
+	config.pdata.param_size =
+		sizeof(struct afe_param_id_cdc_aanc_version);
+	config.version = *version_cfg;
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret) {
+		pr_err("%s: AFE_PARAM_ID_CDC_AANC_VERSION failed %d\n",
+		__func__, ret);
+	}
+	return ret;
+}
+
+int afe_port_set_mad_type(u16 port_id, enum afe_mad_type mad_type)
+{
+	int i;
+
+	if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX) {
+		mad_type = MAD_SW_AUDIO;
+		return 0;
+	}
+
+	i = port_id - SLIMBUS_0_RX;
+	if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) {
+		pr_err("%s: Invalid port_id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+	atomic_set(&afe_ports_mad_type[i], mad_type);
+	return 0;
+}
+
+enum afe_mad_type afe_port_get_mad_type(u16 port_id)
+{
+	int i;
+
+	if (port_id == AFE_PORT_ID_TERTIARY_MI2S_TX)
+		return MAD_SW_AUDIO;
+
+	i = port_id - SLIMBUS_0_RX;
+	if (i < 0 || i >= ARRAY_SIZE(afe_ports_mad_type)) {
+		pr_debug("%s: Non Slimbus port_id 0x%x\n", __func__, port_id);
+		return MAD_HW_NONE;
+	}
+	return (enum afe_mad_type) atomic_read(&afe_ports_mad_type[i]);
+}
+
+int afe_set_config(enum afe_config_type config_type, void *config_data, int arg)
+{
+	int ret;
+
+	pr_debug("%s: enter config_type %d\n", __func__, config_type);
+	ret = afe_q6_interface_prepare();
+	if (ret) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	switch (config_type) {
+	case AFE_SLIMBUS_SLAVE_CONFIG:
+		ret = afe_send_slimbus_slave_cfg(config_data);
+		if (!ret)
+			ret = afe_init_cdc_reg_config();
+		else
+			pr_err("%s: Sending slimbus slave config failed %d\n",
+			       __func__, ret);
+		break;
+	case AFE_CDC_REGISTERS_CONFIG:
+		ret = afe_send_codec_reg_config(config_data);
+		break;
+	case AFE_SLIMBUS_SLAVE_PORT_CONFIG:
+		ret = afe_send_slimbus_slave_port_cfg(config_data, arg);
+		break;
+	case AFE_AANC_VERSION:
+		ret = afe_send_aanc_version(config_data);
+		break;
+	case AFE_CLIP_BANK_SEL:
+		ret = afe_send_bank_selection_clip(config_data);
+		break;
+	case AFE_CDC_CLIP_REGISTERS_CONFIG:
+		ret = afe_send_codec_reg_config(config_data);
+		break;
+	case AFE_CDC_REGISTER_PAGE_CONFIG:
+		ret = afe_send_codec_reg_page_config(config_data);
+		break;
+	default:
+		pr_err("%s: unknown configuration type %d",
+			__func__, config_type);
+		ret = -EINVAL;
+	}
+
+	if (!ret)
+		set_bit(config_type, &afe_configured_cmd);
+
+	return ret;
+}
+
+/*
+ * afe_clear_config - If SSR happens ADSP loses AFE configs, let AFE driver know
+ *		      about the state so client driver can wait until AFE is
+ *		      reconfigured.
+ */
+void afe_clear_config(enum afe_config_type config)
+{
+	clear_bit(config, &afe_configured_cmd);
+}
+
+bool afe_has_config(enum afe_config_type config)
+{
+	return !!test_bit(config, &afe_configured_cmd);
+}
+
+int afe_send_spdif_clk_cfg(struct afe_param_id_spdif_clk_cfg *cfg,
+		u16 port_id)
+{
+	struct afe_spdif_clk_config_command clk_cfg;
+	int ret = 0;
+	int index = 0;
+
+	if (!cfg) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+	clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+	clk_cfg.hdr.src_port = 0;
+	clk_cfg.hdr.dest_port = 0;
+	clk_cfg.hdr.token = index;
+
+	clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+	clk_cfg.param.payload_address_lsw = 0x00;
+	clk_cfg.param.payload_address_msw = 0x00;
+	clk_cfg.param.mem_map_handle = 0x00;
+	clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	clk_cfg.pdata.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG;
+	clk_cfg.pdata.param_size =  sizeof(clk_cfg.clk_cfg);
+	clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+		- sizeof(clk_cfg.param);
+	clk_cfg.clk_cfg = *cfg;
+
+	pr_debug("%s: Minor version = 0x%x clk val = %d\n"
+			"clk root = 0x%x\n port id = 0x%x\n",
+			__func__, cfg->clk_cfg_minor_version,
+			cfg->clk_value, cfg->clk_root,
+			q6audio_get_port_id(port_id));
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+	if (ret < 0) {
+		pr_err("%s: AFE send clock config for port 0x%x failed ret = %d\n",
+				__func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n",
+				__func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int afe_send_spdif_ch_status_cfg(struct afe_param_id_spdif_ch_status_cfg
+		*ch_status_cfg,	u16 port_id)
+{
+	struct afe_spdif_chstatus_config_command ch_status;
+	int ret = 0;
+	int index = 0;
+
+	if (!ch_status_cfg) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+	ch_status.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	ch_status.hdr.pkt_size = sizeof(ch_status_cfg);
+	ch_status.hdr.src_port = 0;
+	ch_status.hdr.dest_port = 0;
+	ch_status.hdr.token = index;
+
+	ch_status.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	ch_status.param.port_id = q6audio_get_port_id(port_id);
+	ch_status.param.payload_address_lsw = 0x00;
+	ch_status.param.payload_address_msw = 0x00;
+	ch_status.param.mem_map_handle = 0x00;
+	ch_status.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	ch_status.pdata.param_id = AFE_PARAM_ID_SPDIF_CLK_CONFIG;
+	ch_status.pdata.param_size =  sizeof(ch_status.ch_status);
+	ch_status.param.payload_size = sizeof(ch_status)
+		- sizeof(struct apr_hdr) - sizeof(ch_status.param);
+	ch_status.ch_status = *ch_status_cfg;
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &ch_status);
+	if (ret < 0) {
+		pr_err("%s: AFE send channel status for port 0x%x failed ret = %d\n",
+				__func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n",
+				__func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+static int afe_send_cmd_port_start(u16 port_id)
+{
+	struct afe_port_cmd_device_start start;
+	int ret, index;
+
+	pr_debug("%s: enter\n", __func__);
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					    APR_HDR_LEN(APR_HDR_SIZE),
+					    APR_PKT_VER);
+	start.hdr.pkt_size = sizeof(start);
+	start.hdr.src_port = 0;
+	start.hdr.dest_port = 0;
+	start.hdr.token = index;
+	start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+	start.port_id = q6audio_get_port_id(port_id);
+	pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
+		 __func__, start.hdr.opcode, start.port_id);
+
+	ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__,
+		       port_id, ret);
+	} else if (this_afe.task != current) {
+		this_afe.task = current;
+		pr_debug("task_name = %s pid = %d\n",
+			 this_afe.task->comm, this_afe.task->pid);
+	}
+
+	return ret;
+}
+
+static int afe_aanc_start(uint16_t tx_port_id, uint16_t rx_port_id)
+{
+	int ret;
+
+	pr_debug("%s:  Tx port is 0x%x, Rx port is 0x%x\n",
+		 __func__, tx_port_id, rx_port_id);
+	ret = afe_aanc_port_cfg(this_afe.apr, tx_port_id, rx_port_id);
+	if (ret) {
+		pr_err("%s: Send AANC Port Config failed %d\n",
+			__func__, ret);
+		goto fail_cmd;
+	}
+	send_afe_cal_type(AFE_AANC_CAL, tx_port_id);
+
+fail_cmd:
+	return ret;
+}
+
+int afe_spdif_port_start(u16 port_id, struct afe_spdif_port_config *spdif_port,
+		u32 rate)
+{
+	struct afe_audioif_config_command config;
+	int ret = 0;
+	int index = 0;
+	uint16_t port_index;
+
+	if (!spdif_port) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	afe_send_cal(port_id);
+	afe_send_hw_delay(port_id, rate);
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+		sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id = AFE_PARAM_ID_SPDIF_CONFIG;
+	config.pdata.param_size = sizeof(config.port);
+	config.port.spdif = spdif_port->cfg;
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
+				__func__, port_id, ret);
+		goto fail_cmd;
+	}
+
+	port_index = afe_get_port_index(port_id);
+	if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+		this_afe.afe_sample_rates[port_index] = rate;
+	} else {
+		pr_err("%s: Invalid port index %d\n", __func__, port_index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = afe_send_spdif_ch_status_cfg(&spdif_port->ch_status, port_id);
+	if (ret < 0) {
+		pr_err("%s: afe send failed %d\n", __func__, ret);
+		goto fail_cmd;
+	}
+
+	return afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+	return ret;
+}
+
+int afe_send_slot_mapping_cfg(
+	struct afe_param_id_slot_mapping_cfg *slot_mapping_cfg,
+	u16 port_id)
+{
+	struct afe_slot_mapping_config_command config;
+	int ret = 0;
+	int index = 0;
+
+	if (!slot_mapping_cfg) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config)
+		- sizeof(struct apr_hdr) - sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_TDM;
+	config.pdata.param_id = AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG;
+	config.pdata.param_size =  sizeof(config.slot_mapping);
+	config.slot_mapping = *slot_mapping_cfg;
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+	if (ret < 0) {
+		pr_err("%s: AFE send slot mapping for port 0x%x failed ret = %d\n",
+				__func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n",
+				__func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int afe_send_custom_tdm_header_cfg(
+	struct afe_param_id_custom_tdm_header_cfg *custom_tdm_header_cfg,
+	u16 port_id)
+{
+	struct afe_custom_tdm_header_config_command config;
+	int ret = 0;
+	int index = 0;
+
+	if (!custom_tdm_header_cfg) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config)
+		- sizeof(struct apr_hdr) - sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_TDM;
+	config.pdata.param_id = AFE_PARAM_ID_CUSTOM_TDM_HEADER_CONFIG;
+	config.pdata.param_size =  sizeof(config.custom_tdm_header);
+	config.custom_tdm_header = *custom_tdm_header_cfg;
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &config);
+	if (ret < 0) {
+		pr_err("%s: AFE send custom tdm header for port 0x%x failed ret = %d\n",
+				__func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n",
+				__func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int afe_tdm_port_start(u16 port_id, struct afe_tdm_port_config *tdm_port,
+		u32 rate)
+{
+	struct afe_audioif_config_command config;
+	int ret = 0;
+	int index = 0;
+	uint16_t port_index = 0;
+	enum afe_mad_type mad_type = MAD_HW_NONE;
+
+	if (!tdm_port) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	/* Also send the topology id here: */
+	port_index = afe_get_port_index(port_id);
+	if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) {
+		/* One time call: only for first time */
+		afe_send_custom_topology();
+		afe_send_port_topology_id(port_id);
+		afe_send_cal(port_id);
+		afe_send_hw_delay(port_id, rate);
+	}
+
+	/* Start SW MAD module */
+	mad_type = afe_port_get_mad_type(port_id);
+	pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+		 mad_type);
+	if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+		if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) ||
+			!afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) {
+			pr_err("%s: AFE isn't configured yet for\n"
+				"HW MAD try Again\n", __func__);
+				ret = -EAGAIN;
+				goto fail_cmd;
+		}
+		ret = afe_turn_onoff_hw_mad(mad_type, true);
+		if (ret) {
+			pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+			       __func__, ret);
+			goto fail_cmd;
+		}
+	}
+
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+		sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id = AFE_PARAM_ID_TDM_CONFIG;
+	config.pdata.param_size = sizeof(config.port);
+	config.port.tdm = tdm_port->tdm;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x failed ret = %d\n",
+				__func__, port_id, ret);
+		goto fail_cmd;
+	}
+
+	port_index = afe_get_port_index(port_id);
+	if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+		this_afe.afe_sample_rates[port_index] = rate;
+	} else {
+		pr_err("%s: Invalid port index %d\n", __func__, port_index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = afe_send_slot_mapping_cfg(&tdm_port->slot_mapping, port_id);
+	if (ret < 0) {
+		pr_err("%s: afe send failed %d\n", __func__, ret);
+		goto fail_cmd;
+	}
+
+	if (tdm_port->custom_tdm_header.header_type) {
+		ret = afe_send_custom_tdm_header_cfg(
+			&tdm_port->custom_tdm_header, port_id);
+		if (ret < 0) {
+			pr_err("%s: afe send failed %d\n", __func__, ret);
+			goto fail_cmd;
+		}
+	}
+
+	ret = afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+	return ret;
+}
+
+void afe_set_cal_mode(u16 port_id, enum afe_cal_mode afe_cal_mode)
+{
+	uint16_t port_index;
+
+	port_index = afe_get_port_index(port_id);
+	this_afe.afe_cal_mode[port_index] = afe_cal_mode;
+}
+
+int afe_port_send_usb_dev_param(u16 port_id, union afe_port_config *afe_config)
+{
+	struct afe_usb_audio_dev_param_command config;
+	int ret = 0, index = 0;
+
+	if (!afe_config) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		ret = -EINVAL;
+		goto exit;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid! for port ID 0x%x\n",
+				__func__, index, port_id);
+		ret = -EINVAL;
+		goto exit;
+	}
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id = AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS;
+	config.pdata.param_size = sizeof(config.usb_dev);
+	config.usb_dev.cfg_minor_version =
+				AFE_API_MINIOR_VERSION_USB_AUDIO_CONFIG;
+	config.usb_dev.dev_token = afe_config->usb_audio.dev_token;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE device param cmd failed %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto exit;
+	}
+exit:
+	return ret;
+}
+
+static int q6afe_send_enc_config(u16 port_id,
+				 union afe_enc_config_data *cfg, u32 format,
+				 union afe_port_config afe_config,
+				 u16 afe_in_channels, u16 afe_in_bit_width)
+{
+	struct afe_audioif_config_command config;
+	int index;
+	int ret;
+	int payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				sizeof(config.param) - sizeof(config.port);
+
+	pr_debug("%s:update DSP for enc format = %d\n", __func__, format);
+	if (format != ASM_MEDIA_FMT_SBC && format != ASM_MEDIA_FMT_AAC_V2 &&
+	    format != ASM_MEDIA_FMT_APTX && format != ASM_MEDIA_FMT_APTX_HD) {
+		pr_err("%s:Unsuppported format Ignore AFE config\n", __func__);
+		return 0;
+	}
+	memset(&config, 0, sizeof(config));
+	index = q6audio_get_port_index(port_id);
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = payload_size + sizeof(config.port.enc_fmt);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_ID_ENCODER;
+	config.pdata.param_id = AFE_ENCODER_PARAM_ID_ENC_FMT_ID;
+	config.pdata.param_size = sizeof(config.port.enc_fmt);
+	config.port.enc_fmt.fmt_id = format;
+	pr_debug("%s:sending AFE_ENCODER_PARAM_ID_ENC_FMT_ID payload: %d\n",
+			__func__, config.param.payload_size);
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s:unable to send AFE_ENCODER_PARAM_ID_ENC_FMT_ID",
+				__func__);
+		goto exit;
+	}
+
+	config.param.payload_size = payload_size
+					+ sizeof(config.port.enc_blk_param);
+	pr_debug("%s:send AFE_ENCODER_PARAM_ID_ENC_CFG_BLK to DSP payload:%d\n",
+				__func__, config.param.payload_size);
+	config.pdata.param_id = AFE_ENCODER_PARAM_ID_ENC_CFG_BLK;
+	config.pdata.param_size = sizeof(config.port.enc_blk_param);
+	config.port.enc_blk_param.enc_cfg_blk_size =
+			sizeof(config.port.enc_blk_param.enc_blk_config);
+	config.port.enc_blk_param.enc_blk_config = *cfg;
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE_ENCODER_PARAM_ID_ENC_CFG_BLK for port 0x%x failed %d\n",
+			__func__, port_id, ret);
+		goto exit;
+	}
+
+	config.param.payload_size =
+			payload_size + sizeof(config.port.enc_pkt_id_param);
+	pr_debug("%s:sending AFE_ENCODER_PARAM_ID_PACKETIZER to DSP payload = %d",
+					__func__, config.param.payload_size);
+	config.pdata.param_id = AFE_ENCODER_PARAM_ID_PACKETIZER_ID;
+	config.pdata.param_size = sizeof(config.port.enc_pkt_id_param);
+	config.port.enc_pkt_id_param.enc_packetizer_id =
+					AFE_MODULE_ID_PACKETIZER_COP;
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE_ENCODER_PARAM_ID_PACKETIZER for port 0x%x failed %d\n",
+			__func__, port_id, ret);
+		goto exit;
+	}
+
+	config.param.payload_size =
+			payload_size + sizeof(config.port.media_type);
+	config.pdata.param_size = sizeof(config.port.media_type);
+
+	pr_debug("%s:Sending AFE_API_VERSION_PORT_MEDIA_TYPE to DSP", __func__);
+	config.pdata.module_id = AFE_MODULE_PORT;
+	config.pdata.param_id = AFE_PARAM_ID_PORT_MEDIA_TYPE;
+	config.port.media_type.minor_version = AFE_API_VERSION_PORT_MEDIA_TYPE;
+	config.port.media_type.sample_rate = afe_config.slim_sch.sample_rate;
+	if (afe_in_bit_width)
+		config.port.media_type.bit_width = afe_in_bit_width;
+	else
+		config.port.media_type.bit_width =
+					afe_config.slim_sch.bit_width;
+
+	if (afe_in_channels)
+		config.port.media_type.num_channels = afe_in_channels;
+	else
+		config.port.media_type.num_channels =
+					afe_config.slim_sch.num_channels;
+	config.port.media_type.data_format = AFE_PORT_DATA_FORMAT_PCM;
+	config.port.media_type.reserved = 0;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE_API_VERSION_PORT_MEDIA_TYPE for port 0x%x failed %d\n",
+			__func__, port_id, ret);
+		goto exit;
+	}
+
+exit:
+	return ret;
+}
+
+static int __afe_port_start(u16 port_id, union afe_port_config *afe_config,
+			    u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
+			    union afe_enc_config_data *cfg, u32 enc_format)
+{
+	struct afe_audioif_config_command config;
+	int ret = 0;
+	int cfg_type;
+	int index = 0;
+	enum afe_mad_type mad_type;
+	uint16_t port_index;
+
+	if (!afe_config) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	if ((port_id == RT_PROXY_DAI_001_RX) ||
+		(port_id == RT_PROXY_DAI_002_TX)) {
+		pr_debug("%s: before incrementing pcm_afe_instance %d port_id 0x%x\n",
+			__func__,
+			pcm_afe_instance[port_id & 0x1], port_id);
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+		pcm_afe_instance[port_id & 0x1]++;
+		return 0;
+	}
+	if ((port_id == RT_PROXY_DAI_002_RX) ||
+			(port_id == RT_PROXY_DAI_001_TX)) {
+		pr_debug("%s: before incrementing proxy_afe_instance %d port_id 0x%x\n",
+			__func__,
+			proxy_afe_instance[port_id & 0x1], port_id);
+
+		if (!afe_close_done[port_id & 0x1]) {
+			/*close pcm dai corresponding to the proxy dai*/
+			afe_close(port_id - 0x10);
+			pcm_afe_instance[port_id & 0x1]++;
+			pr_debug("%s: reconfigure afe port again\n", __func__);
+		}
+		proxy_afe_instance[port_id & 0x1]++;
+		afe_close_done[port_id & 0x1] = false;
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+	}
+
+	pr_debug("%s: port id: 0x%x\n", __func__, port_id);
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: port id: 0x%x ret %d\n", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	/* Also send the topology id here: */
+	port_index = afe_get_port_index(port_id);
+	if (!(this_afe.afe_cal_mode[port_index] == AFE_CAL_MODE_NONE)) {
+		/* One time call: only for first time */
+		afe_send_custom_topology();
+		afe_send_port_topology_id(port_id);
+		afe_send_cal(port_id);
+		afe_send_hw_delay(port_id, rate);
+	}
+
+	/* Start SW MAD module */
+	mad_type = afe_port_get_mad_type(port_id);
+	pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+		 mad_type);
+	if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+		if (!afe_has_config(AFE_CDC_REGISTERS_CONFIG) ||
+			!afe_has_config(AFE_SLIMBUS_SLAVE_CONFIG)) {
+			pr_err("%s: AFE isn't configured yet for\n"
+				"HW MAD try Again\n", __func__);
+				ret = -EAGAIN;
+				goto fail_cmd;
+		}
+		ret = afe_turn_onoff_hw_mad(mad_type, true);
+		if (ret) {
+			pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+			       __func__, ret);
+			goto fail_cmd;
+		}
+	}
+
+	if ((this_afe.aanc_info.aanc_active) &&
+	    (this_afe.aanc_info.aanc_tx_port == port_id)) {
+		this_afe.aanc_info.aanc_tx_port_sample_rate = rate;
+		port_index =
+			afe_get_port_index(this_afe.aanc_info.aanc_rx_port);
+		if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+			this_afe.aanc_info.aanc_rx_port_sample_rate =
+				this_afe.afe_sample_rates[port_index];
+		} else {
+			pr_err("%s: Invalid port index %d\n",
+				__func__, port_index);
+			ret = -EINVAL;
+			goto fail_cmd;
+		}
+		ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port,
+				this_afe.aanc_info.aanc_rx_port);
+		pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret);
+	}
+
+	if ((port_id == AFE_PORT_ID_USB_RX) ||
+	    (port_id == AFE_PORT_ID_USB_TX)) {
+		ret = afe_port_send_usb_dev_param(port_id, afe_config);
+		if (ret) {
+			pr_err("%s: AFE device param for port 0x%x failed %d\n",
+				   __func__, port_id, ret);
+			ret = -EINVAL;
+			goto fail_cmd;
+		}
+	}
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+
+	switch (port_id) {
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+		cfg_type = AFE_PARAM_ID_PCM_CONFIG;
+		break;
+	case PRIMARY_I2S_RX:
+	case PRIMARY_I2S_TX:
+	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
+	case MI2S_RX:
+	case MI2S_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case AFE_PORT_ID_QUINARY_MI2S_RX:
+	case AFE_PORT_ID_QUINARY_MI2S_TX:
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+		cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+		break;
+	case HDMI_RX:
+	case DISPLAY_PORT_RX:
+		cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
+		break;
+	case VOICE_PLAYBACK_TX:
+	case VOICE2_PLAYBACK_TX:
+	case VOICE_RECORD_RX:
+	case VOICE_RECORD_TX:
+		cfg_type = AFE_PARAM_ID_PSEUDO_PORT_CONFIG;
+		break;
+	case SLIMBUS_0_RX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_5_TX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_8_TX:
+		cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
+		break;
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_USB_TX:
+		cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG;
+		break;
+	case RT_PROXY_PORT_001_RX:
+	case RT_PROXY_PORT_001_TX:
+		cfg_type = AFE_PARAM_ID_RT_PROXY_CONFIG;
+		break;
+	case INT_BT_SCO_RX:
+	case INT_BT_A2DP_RX:
+	case INT_BT_SCO_TX:
+	case INT_FM_RX:
+	case INT_FM_TX:
+		cfg_type = AFE_PARAM_ID_INTERNAL_BT_FM_CONFIG;
+		break;
+	default:
+		pr_err("%s: Invalid port id 0x%x\n", __func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id = cfg_type;
+	config.pdata.param_size = sizeof(config.port);
+
+	config.port = *afe_config;
+	if ((enc_format != ASM_MEDIA_FMT_NONE) &&
+	    (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) {
+		config.port.slim_sch.data_format =
+				AFE_SB_DATA_FORMAT_GENERIC_COMPRESSED;
+	}
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x failed %d\n",
+			__func__, port_id, ret);
+		goto fail_cmd;
+	}
+
+	if ((enc_format != ASM_MEDIA_FMT_NONE) &&
+	    (cfg_type == AFE_PARAM_ID_SLIMBUS_CONFIG)) {
+		pr_debug("%s: Found AFE encoder support for SLIMBUS enc_format = %d\n",
+					__func__, enc_format);
+		ret = q6afe_send_enc_config(port_id, cfg, enc_format,
+					    *afe_config, afe_in_channels,
+					    afe_in_bit_width);
+		if (ret) {
+			pr_err("%s: AFE encoder config for port 0x%x failed %d\n",
+				__func__, port_id, ret);
+			goto fail_cmd;
+		}
+	}
+
+	port_index = afe_get_port_index(port_id);
+	if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+		this_afe.afe_sample_rates[port_index] = rate;
+		/*
+		 * If afe_port_start() for tx port called before
+		 * rx port, then aanc rx sample rate is zero. So,
+		 * AANC state machine in AFE will not get triggered.
+		 * Make sure to check whether aanc is active during
+		 * afe_port_start() for rx port and if aanc rx
+		 * sample rate is zero, call afe_aanc_start to configure
+		 * aanc with valid sample rates.
+		 */
+		if (this_afe.aanc_info.aanc_active &&
+		    !this_afe.aanc_info.aanc_rx_port_sample_rate) {
+			this_afe.aanc_info.aanc_rx_port_sample_rate =
+				this_afe.afe_sample_rates[port_index];
+			ret = afe_aanc_start(this_afe.aanc_info.aanc_tx_port,
+					this_afe.aanc_info.aanc_rx_port);
+			pr_debug("%s: afe_aanc_start ret %d\n", __func__, ret);
+		}
+	} else {
+		pr_err("%s: Invalid port index %d\n", __func__, port_index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = afe_send_cmd_port_start(port_id);
+
+fail_cmd:
+	mutex_unlock(&this_afe.afe_cmd_lock);
+	return ret;
+}
+
+/**
+ * afe_port_start - to configure AFE session with
+ * specified port configuration
+ *
+ * @port_id: AFE port id number
+ * @afe_config: port configutation
+ * @rate: sampling rate of port
+ *
+ * Returns 0 on success or error value on port start failure.
+ */
+int afe_port_start(u16 port_id, union afe_port_config *afe_config,
+		   u32 rate)
+{
+	return __afe_port_start(port_id, afe_config, rate,
+				0, 0, NULL, ASM_MEDIA_FMT_NONE);
+}
+EXPORT_SYMBOL(afe_port_start);
+
+/**
+ * afe_port_start_v2 - to configure AFE session with
+ * specified port configuration and encoder params
+ *
+ * @port_id: AFE port id number
+ * @afe_config: port configutation
+ * @rate: sampling rate of port
+ * @cfg: AFE encoder configuration information to setup encoder
+ * @afe_in_channels: AFE input channel configuration, this needs
+ *  update only if input channel is differ from AFE output
+ *
+ * Returns 0 on success or error value on port start failure.
+ */
+int afe_port_start_v2(u16 port_id, union afe_port_config *afe_config,
+		      u32 rate, u16 afe_in_channels, u16 afe_in_bit_width,
+		      struct afe_enc_config *enc_cfg)
+{
+	return __afe_port_start(port_id, afe_config, rate,
+				afe_in_channels, afe_in_bit_width,
+				&enc_cfg->data, enc_cfg->format);
+}
+EXPORT_SYMBOL(afe_port_start_v2);
+
+int afe_get_port_index(u16 port_id)
+{
+	switch (port_id) {
+	case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
+	case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_PCM_RX;
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+		return IDX_AFE_PORT_ID_PRIMARY_PCM_TX;
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+		return IDX_AFE_PORT_ID_SECONDARY_PCM_TX;
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+		return IDX_AFE_PORT_ID_TERTIARY_PCM_RX;
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+		return IDX_AFE_PORT_ID_TERTIARY_PCM_TX;
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX;
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX;
+	case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
+	case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
+	case MI2S_RX: return IDX_MI2S_RX;
+	case MI2S_TX: return IDX_MI2S_TX;
+	case HDMI_RX: return IDX_HDMI_RX;
+	case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
+	case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
+	case RSVD_2: return IDX_RSVD_2;
+	case RSVD_3: return IDX_RSVD_3;
+	case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
+	case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
+	case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
+	case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+	case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX;
+	case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
+	case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
+	case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
+	case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
+	case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
+	case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
+	case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
+	case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
+	case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
+	case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
+	case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
+	case INT_FM_RX: return IDX_INT_FM_RX;
+	case INT_FM_TX: return IDX_INT_FM_TX;
+	case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
+	case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
+	case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
+	case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+	case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX;
+	case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
+	case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX;
+	case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX;
+	case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX;
+	case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX;
+	case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX;
+	case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX;
+	case AFE_PORT_ID_USB_RX: return IDX_AFE_PORT_ID_USB_RX;
+	case AFE_PORT_ID_USB_TX: return IDX_AFE_PORT_ID_USB_TX;
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX;
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+		return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+	case AFE_PORT_ID_QUINARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_QUINARY_MI2S_RX;
+	case AFE_PORT_ID_QUINARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_QUINARY_MI2S_TX;
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_SENARY_MI2S_TX;
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0;
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0;
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0;
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7;
+	default:
+		pr_err("%s: port 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+}
+
+int afe_open(u16 port_id,
+		union afe_port_config *afe_config, int rate)
+{
+	struct afe_port_cmd_device_start start;
+	struct afe_audioif_config_command config;
+	int ret = 0;
+	int cfg_type;
+	int index = 0;
+
+	if (!afe_config) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	pr_err("%s: port_id 0x%x rate %d\n", __func__, port_id, rate);
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	if ((port_id == RT_PROXY_DAI_001_RX) ||
+		(port_id == RT_PROXY_DAI_002_TX)) {
+		pr_err("%s: wrong port 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+	if ((port_id == RT_PROXY_DAI_002_RX) ||
+		(port_id == RT_PROXY_DAI_001_TX))
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return -EINVAL;
+	}
+	/* Also send the topology id here: */
+	afe_send_custom_topology(); /* One time call: only for first time  */
+	afe_send_port_topology_id(port_id);
+
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+	mutex_lock(&this_afe.afe_cmd_lock);
+
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = index;
+	switch (port_id) {
+	case PRIMARY_I2S_RX:
+	case PRIMARY_I2S_TX:
+		cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+		break;
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+		cfg_type = AFE_PARAM_ID_PCM_CONFIG;
+		break;
+	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case MI2S_RX:
+	case MI2S_TX:
+	case AFE_PORT_ID_QUINARY_MI2S_RX:
+	case AFE_PORT_ID_QUINARY_MI2S_TX:
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+		cfg_type = AFE_PARAM_ID_I2S_CONFIG;
+		break;
+	case HDMI_RX:
+	case DISPLAY_PORT_RX:
+		cfg_type = AFE_PARAM_ID_HDMI_CONFIG;
+		break;
+	case SLIMBUS_0_RX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_8_TX:
+		cfg_type = AFE_PARAM_ID_SLIMBUS_CONFIG;
+		break;
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_USB_TX:
+		cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG;
+		break;
+	default:
+		pr_err("%s: Invalid port id 0x%x\n",
+			__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	config.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	config.param.port_id = q6audio_get_port_id(port_id);
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr)
+				 - sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	config.pdata.param_id = cfg_type;
+	config.pdata.param_size =  sizeof(config.port);
+
+	config.port = *afe_config;
+	pr_debug("%s: param PL size=%d iparam_size[%d][%zd %zd %zd %zd] param_id[0x%x]\n",
+		__func__, config.param.payload_size, config.pdata.param_size,
+		sizeof(config), sizeof(config.param), sizeof(config.port),
+		sizeof(struct apr_hdr), config.pdata.param_id);
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x opcode[0x%x]failed %d\n",
+			__func__, port_id, cfg_type, ret);
+		goto fail_cmd;
+	}
+	start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	start.hdr.pkt_size = sizeof(start);
+	start.hdr.src_port = 0;
+	start.hdr.dest_port = 0;
+	start.hdr.token = index;
+	start.hdr.opcode = AFE_PORT_CMD_DEVICE_START;
+	start.port_id = q6audio_get_port_id(port_id);
+	pr_debug("%s: cmd device start opcode[0x%x] port id[0x%x]\n",
+		__func__, start.hdr.opcode, start.port_id);
+
+	ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x failed %d\n", __func__,
+				port_id, ret);
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	mutex_unlock(&this_afe.afe_cmd_lock);
+	return ret;
+}
+
+int afe_loopback(u16 enable, u16 rx_port, u16 tx_port)
+{
+	struct afe_loopback_cfg_v1 lb_cmd;
+	int ret = 0;
+	int index = 0;
+
+	if (rx_port == MI2S_RX)
+		rx_port = AFE_PORT_ID_PRIMARY_MI2S_RX;
+	if (tx_port == MI2S_TX)
+		tx_port = AFE_PORT_ID_PRIMARY_MI2S_TX;
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	index = q6audio_get_port_index(rx_port);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(rx_port);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d", __func__, rx_port, ret);
+		return -EINVAL;
+	}
+
+	lb_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(20), APR_PKT_VER);
+	lb_cmd.hdr.pkt_size = sizeof(lb_cmd);
+	lb_cmd.hdr.src_port = 0;
+	lb_cmd.hdr.dest_port = 0;
+	lb_cmd.hdr.token = index;
+	lb_cmd.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	lb_cmd.param.port_id = tx_port;
+	lb_cmd.param.payload_size = (sizeof(lb_cmd) - sizeof(struct apr_hdr) -
+				     sizeof(struct afe_port_cmd_set_param_v2));
+	lb_cmd.param.payload_address_lsw = 0x00;
+	lb_cmd.param.payload_address_msw = 0x00;
+	lb_cmd.param.mem_map_handle = 0x00;
+	lb_cmd.pdata.module_id = AFE_MODULE_LOOPBACK;
+	lb_cmd.pdata.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG;
+	lb_cmd.pdata.param_size = lb_cmd.param.payload_size -
+				  sizeof(struct afe_port_param_data_v2);
+
+	lb_cmd.dst_port_id = rx_port;
+	lb_cmd.routing_mode = LB_MODE_DEFAULT;
+	lb_cmd.enable = (enable ? 1 : 0);
+	lb_cmd.loopback_cfg_minor_version = AFE_API_VERSION_LOOPBACK_CONFIG;
+
+	ret = afe_apr_send_pkt(&lb_cmd, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: AFE loopback failed %d\n", __func__, ret);
+	return ret;
+}
+
+int afe_loopback_gain(u16 port_id, u16 volume)
+{
+	struct afe_loopback_gain_per_path_param set_param;
+	int ret = 0;
+	int index = 0;
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Failed : Invalid Port id = 0x%x ret %d\n",
+			__func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	/* RX ports numbers are even .TX ports numbers are odd. */
+	if (port_id % 2 == 0) {
+		pr_err("%s: Failed : afe loopback gain only for TX ports. port_id %d\n",
+				__func__, port_id);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	pr_debug("%s: port 0x%x volume %d\n", __func__, port_id, volume);
+
+	set_param.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	set_param.hdr.pkt_size = sizeof(set_param);
+	set_param.hdr.src_port = 0;
+	set_param.hdr.dest_port = 0;
+	set_param.hdr.token = index;
+	set_param.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+
+	set_param.param.port_id	= port_id;
+	set_param.param.payload_size =
+	    (sizeof(struct afe_loopback_gain_per_path_param) -
+	     sizeof(struct apr_hdr) - sizeof(struct afe_port_cmd_set_param_v2));
+	set_param.param.payload_address_lsw	= 0;
+	set_param.param.payload_address_msw	= 0;
+	set_param.param.mem_map_handle        = 0;
+
+	set_param.pdata.module_id = AFE_MODULE_LOOPBACK;
+	set_param.pdata.param_id = AFE_PARAM_ID_LOOPBACK_GAIN_PER_PATH;
+	set_param.pdata.param_size =
+	    (set_param.param.payload_size -
+	     sizeof(struct afe_port_param_data_v2));
+	set_param.rx_port_id = port_id;
+	set_param.gain = volume;
+
+	ret = afe_apr_send_pkt(&set_param, &this_afe.wait[index]);
+	if (ret) {
+		pr_err("%s: AFE param set failed for port 0x%x ret %d\n",
+					__func__, port_id, ret);
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int afe_pseudo_port_start_nowait(u16 port_id)
+{
+	struct afe_pseudoport_start_command start;
+	int ret = 0;
+
+	pr_debug("%s: port_id=0x%x\n", __func__, port_id);
+	if (this_afe.apr == NULL) {
+		pr_err("%s: AFE APR is not registered\n", __func__);
+		return -ENODEV;
+	}
+
+
+	start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	start.hdr.pkt_size = sizeof(start);
+	start.hdr.src_port = 0;
+	start.hdr.dest_port = 0;
+	start.hdr.token = 0;
+	start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+	start.port_id = port_id;
+	start.timing = 1;
+
+	ret = afe_apr_send_pkt(&start, NULL);
+	if (ret) {
+		pr_err("%s: AFE enable for port 0x%x failed %d\n",
+		       __func__, port_id, ret);
+		return ret;
+	}
+	return 0;
+}
+
+int afe_start_pseudo_port(u16 port_id)
+{
+	int ret = 0;
+	struct afe_pseudoport_start_command start;
+	int index = 0;
+
+	pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	start.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	start.hdr.pkt_size = sizeof(start);
+	start.hdr.src_port = 0;
+	start.hdr.dest_port = 0;
+	start.hdr.token = 0;
+	start.hdr.opcode = AFE_PSEUDOPORT_CMD_START;
+	start.port_id = port_id;
+	start.timing = 1;
+	start.hdr.token = index;
+
+	ret = afe_apr_send_pkt(&start, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: AFE enable for port 0x%x failed %d\n",
+		       __func__, port_id, ret);
+	return ret;
+}
+
+int afe_pseudo_port_stop_nowait(u16 port_id)
+{
+	int ret = 0;
+	struct afe_pseudoport_stop_command stop;
+	int index = 0;
+
+	pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: AFE is already closed\n", __func__);
+		return -EINVAL;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	stop.hdr.pkt_size = sizeof(stop);
+	stop.hdr.src_port = 0;
+	stop.hdr.dest_port = 0;
+	stop.hdr.token = 0;
+	stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+	stop.port_id = port_id;
+	stop.reserved = 0;
+	stop.hdr.token = index;
+
+	ret = afe_apr_send_pkt(&stop, NULL);
+	if (ret)
+		pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+	return ret;
+}
+
+int afe_port_group_set_param(u16 group_id,
+	union afe_port_group_config *afe_group_config)
+{
+	int ret;
+	struct afe_port_group_create config;
+	int cfg_type;
+
+	if (!afe_group_config) {
+		pr_err("%s: Error, no configuration data\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: group id: 0x%x\n", __func__, group_id);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	switch (group_id) {
+	case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_PRIMARY_TDM_TX:
+	case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_SECONDARY_TDM_TX:
+	case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_TERTIARY_TDM_TX:
+	case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_RX:
+	case AFE_GROUP_DEVICE_ID_QUATERNARY_TDM_TX:
+		cfg_type = AFE_PARAM_ID_GROUP_DEVICE_TDM_CONFIG;
+		break;
+	default:
+		pr_err("%s: Invalid group id 0x%x\n", __func__, group_id);
+		return -EINVAL;
+	}
+
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_GROUP_DEVICE;
+	config.pdata.param_id = cfg_type;
+	config.pdata.param_size = sizeof(config.data);
+	config.data = *afe_group_config;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret)
+		pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_CFG failed %d\n",
+			__func__, ret);
+
+	return ret;
+}
+
+int afe_port_group_enable(u16 group_id,
+	union afe_port_group_config *afe_group_config,
+	u16 enable)
+{
+	int ret;
+	struct afe_port_group_create config;
+
+	pr_debug("%s: group id: 0x%x enable: %d\n", __func__,
+		group_id, enable);
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	if (enable) {
+		ret = afe_port_group_set_param(group_id, afe_group_config);
+		if (ret < 0) {
+			pr_err("%s: afe send failed %d\n", __func__, ret);
+			return ret;
+		}
+	}
+
+	memset(&config, 0, sizeof(config));
+	config.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	config.hdr.pkt_size = sizeof(config);
+	config.hdr.src_port = 0;
+	config.hdr.dest_port = 0;
+	config.hdr.token = IDX_GLOBAL_CFG;
+	config.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+
+	config.param.payload_size = sizeof(config) - sizeof(struct apr_hdr) -
+				    sizeof(config.param);
+	config.param.payload_address_lsw = 0x00;
+	config.param.payload_address_msw = 0x00;
+	config.param.mem_map_handle = 0x00;
+	config.pdata.module_id = AFE_MODULE_GROUP_DEVICE;
+	config.pdata.param_id = AFE_PARAM_ID_GROUP_DEVICE_ENABLE;
+	config.pdata.param_size = sizeof(config.data);
+	config.data.group_enable.group_id = group_id;
+	config.data.group_enable.enable = enable;
+
+	ret = afe_apr_send_pkt(&config, &this_afe.wait[IDX_GLOBAL_CFG]);
+	if (ret)
+		pr_err("%s: AFE_PARAM_ID_GROUP_DEVICE_ENABLE failed %d\n",
+			__func__, ret);
+
+	return ret;
+}
+
+int afe_stop_pseudo_port(u16 port_id)
+{
+	int ret = 0;
+	struct afe_pseudoport_stop_command stop;
+	int index = 0;
+
+	pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: AFE is already closed\n", __func__);
+		return -EINVAL;
+	}
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d\n",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	stop.hdr.pkt_size = sizeof(stop);
+	stop.hdr.src_port = 0;
+	stop.hdr.dest_port = 0;
+	stop.hdr.token = 0;
+	stop.hdr.opcode = AFE_PSEUDOPORT_CMD_STOP;
+	stop.port_id = port_id;
+	stop.reserved = 0;
+	stop.hdr.token = index;
+
+	ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+	return ret;
+}
+
+uint32_t afe_req_mmap_handle(struct afe_audio_client *ac)
+{
+	return ac->mem_map_handle;
+}
+
+struct afe_audio_client *q6afe_audio_client_alloc(void *priv)
+{
+	struct afe_audio_client *ac;
+	int lcnt = 0;
+
+	ac = kzalloc(sizeof(struct afe_audio_client), GFP_KERNEL);
+	if (!ac)
+		return NULL;
+
+	ac->priv = priv;
+
+	init_waitqueue_head(&ac->cmd_wait);
+	INIT_LIST_HEAD(&ac->port[0].mem_map_handle);
+	INIT_LIST_HEAD(&ac->port[1].mem_map_handle);
+	pr_debug("%s: mem_map_handle list init'ed\n", __func__);
+	mutex_init(&ac->cmd_lock);
+	for (lcnt = 0; lcnt <= OUT; lcnt++) {
+		mutex_init(&ac->port[lcnt].lock);
+		spin_lock_init(&ac->port[lcnt].dsp_lock);
+	}
+	atomic_set(&ac->cmd_state, 0);
+
+	return ac;
+}
+
+int q6afe_audio_client_buf_alloc_contiguous(unsigned int dir,
+			struct afe_audio_client *ac,
+			unsigned int bufsz,
+			unsigned int bufcnt)
+{
+	int cnt = 0;
+	int rc = 0;
+	struct afe_audio_buffer *buf;
+	size_t len;
+
+	if (!(ac) || ((dir != IN) && (dir != OUT))) {
+		pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: bufsz[%d]bufcnt[%d]\n",
+			__func__,
+			bufsz, bufcnt);
+
+	if (ac->port[dir].buf) {
+		pr_debug("%s: buffer already allocated\n", __func__);
+		return 0;
+	}
+	mutex_lock(&ac->cmd_lock);
+	buf = kzalloc(((sizeof(struct afe_audio_buffer))*bufcnt),
+			GFP_KERNEL);
+
+	if (!buf) {
+		pr_err("%s: null buf\n", __func__);
+		mutex_unlock(&ac->cmd_lock);
+		goto fail;
+	}
+
+	ac->port[dir].buf = buf;
+
+	rc = msm_audio_ion_alloc("afe_client", &buf[0].client,
+				&buf[0].handle, bufsz*bufcnt,
+				&buf[0].phys, &len,
+				&buf[0].data);
+	if (rc) {
+		pr_err("%s: audio ION alloc failed, rc = %d\n",
+			__func__, rc);
+		mutex_unlock(&ac->cmd_lock);
+		goto fail;
+	}
+
+	buf[0].used = dir ^ 1;
+	buf[0].size = bufsz;
+	buf[0].actual_size = bufsz;
+	cnt = 1;
+	while (cnt < bufcnt) {
+		if (bufsz > 0) {
+			buf[cnt].data =  buf[0].data + (cnt * bufsz);
+			buf[cnt].phys =  buf[0].phys + (cnt * bufsz);
+			if (!buf[cnt].data) {
+				pr_err("%s: Buf alloc failed\n",
+							__func__);
+				mutex_unlock(&ac->cmd_lock);
+				goto fail;
+			}
+			buf[cnt].used = dir ^ 1;
+			buf[cnt].size = bufsz;
+			buf[cnt].actual_size = bufsz;
+			pr_debug("%s:  data[%pK]phys[%pK][%pK]\n", __func__,
+				   buf[cnt].data,
+				   &buf[cnt].phys,
+				   &buf[cnt].phys);
+		}
+		cnt++;
+	}
+	ac->port[dir].max_buf_cnt = cnt;
+	mutex_unlock(&ac->cmd_lock);
+	return 0;
+fail:
+	pr_err("%s: jump fail\n", __func__);
+	q6afe_audio_client_buf_free_contiguous(dir, ac);
+	return -EINVAL;
+}
+
+int afe_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz,
+			struct afe_audio_client *ac)
+{
+	int ret = 0;
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	ac->mem_map_handle = 0;
+	ret = afe_cmd_memory_map(dma_addr_p, dma_buf_sz);
+	if (ret < 0) {
+		pr_err("%s: afe_cmd_memory_map failed %d\n",
+			__func__, ret);
+
+		mutex_unlock(&this_afe.afe_cmd_lock);
+		return ret;
+	}
+	ac->mem_map_handle = this_afe.mmap_handle;
+	mutex_unlock(&this_afe.afe_cmd_lock);
+
+	return ret;
+}
+
+int afe_cmd_memory_map(phys_addr_t dma_addr_p, u32 dma_buf_sz)
+{
+	int ret = 0;
+	int cmd_size = 0;
+	void    *payload = NULL;
+	void    *mmap_region_cmd = NULL;
+	struct afe_service_cmd_shared_mem_map_regions *mregion = NULL;
+	struct  afe_service_shared_map_region_payload *mregion_pl = NULL;
+	int index = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+	if (dma_buf_sz % SZ_4K != 0) {
+		/*
+		 * The memory allocated by msm_audio_ion_alloc is always 4kB
+		 * aligned, ADSP expects the size to be 4kB aligned as well
+		 * so re-adjusts the  buffer size before passing to ADSP.
+		 */
+		dma_buf_sz = PAGE_ALIGN(dma_buf_sz);
+	}
+
+	cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions)
+		+ sizeof(struct afe_service_shared_map_region_payload);
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (!mmap_region_cmd)
+		return -ENOMEM;
+
+	mregion = (struct afe_service_cmd_shared_mem_map_regions *)
+							mmap_region_cmd;
+	mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mregion->hdr.pkt_size = cmd_size;
+	mregion->hdr.src_port = 0;
+	mregion->hdr.dest_port = 0;
+	mregion->hdr.token = 0;
+	mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS;
+	mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	mregion->num_regions = 1;
+	mregion->property_flag = 0x00;
+	/* Todo */
+	index = mregion->hdr.token = IDX_RSVD_2;
+
+	payload = ((u8 *) mmap_region_cmd +
+		   sizeof(struct afe_service_cmd_shared_mem_map_regions));
+
+	mregion_pl = (struct afe_service_shared_map_region_payload *)payload;
+
+	mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p);
+	mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+	mregion_pl->mem_size_bytes = dma_buf_sz;
+
+	pr_debug("%s: dma_addr_p 0x%pK , size %d\n", __func__,
+					&dma_addr_p, dma_buf_sz);
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	this_afe.mmap_handle = 0;
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) mmap_region_cmd);
+	if (ret < 0) {
+		pr_err("%s: AFE memory map cmd failed %d\n",
+		       __func__, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+				 (atomic_read(&this_afe.state) == 0),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+	kfree(mmap_region_cmd);
+	return 0;
+fail_cmd:
+	kfree(mmap_region_cmd);
+	pr_err("%s: fail_cmd\n", __func__);
+	return ret;
+}
+
+int afe_cmd_memory_map_nowait(int port_id, phys_addr_t dma_addr_p,
+		u32 dma_buf_sz)
+{
+	int ret = 0;
+	int cmd_size = 0;
+	void    *payload = NULL;
+	void    *mmap_region_cmd = NULL;
+	struct afe_service_cmd_shared_mem_map_regions *mregion = NULL;
+	struct  afe_service_shared_map_region_payload *mregion_pl = NULL;
+	int index = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	cmd_size = sizeof(struct afe_service_cmd_shared_mem_map_regions)
+		+ sizeof(struct afe_service_shared_map_region_payload);
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (!mmap_region_cmd)
+		return -ENOMEM;
+
+	mregion = (struct afe_service_cmd_shared_mem_map_regions *)
+						mmap_region_cmd;
+	mregion->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mregion->hdr.pkt_size = sizeof(mregion);
+	mregion->hdr.src_port = 0;
+	mregion->hdr.dest_port = 0;
+	mregion->hdr.token = 0;
+	mregion->hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_MAP_REGIONS;
+	mregion->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	mregion->num_regions = 1;
+	mregion->property_flag = 0x00;
+
+	payload = ((u8 *) mmap_region_cmd +
+		sizeof(struct afe_service_cmd_shared_mem_map_regions));
+	mregion_pl = (struct afe_service_shared_map_region_payload *)payload;
+
+	mregion_pl->shm_addr_lsw = lower_32_bits(dma_addr_p);
+	mregion_pl->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+	mregion_pl->mem_size_bytes = dma_buf_sz;
+
+	ret = afe_apr_send_pkt(mmap_region_cmd, NULL);
+	if (ret)
+		pr_err("%s: AFE memory map cmd failed %d\n",
+		       __func__, ret);
+	kfree(mmap_region_cmd);
+	return ret;
+}
+int q6afe_audio_client_buf_free_contiguous(unsigned int dir,
+			struct afe_audio_client *ac)
+{
+	struct afe_audio_port_data *port;
+	int cnt = 0;
+
+	mutex_lock(&ac->cmd_lock);
+	port = &ac->port[dir];
+	if (!port->buf) {
+		pr_err("%s: buf is null\n", __func__);
+		mutex_unlock(&ac->cmd_lock);
+		return 0;
+	}
+	cnt = port->max_buf_cnt - 1;
+
+	if (port->buf[0].data) {
+		pr_debug("%s: data[%pK]phys[%pK][%pK] , client[%pK] handle[%pK]\n",
+			__func__,
+			port->buf[0].data,
+			&port->buf[0].phys,
+			&port->buf[0].phys,
+			port->buf[0].client,
+			port->buf[0].handle);
+		msm_audio_ion_free(port->buf[0].client, port->buf[0].handle);
+		port->buf[0].client = NULL;
+		port->buf[0].handle = NULL;
+	}
+
+	while (cnt >= 0) {
+		port->buf[cnt].data = NULL;
+		port->buf[cnt].phys = 0;
+		cnt--;
+	}
+	port->max_buf_cnt = 0;
+	kfree(port->buf);
+	port->buf = NULL;
+	mutex_unlock(&ac->cmd_lock);
+	return 0;
+}
+
+void q6afe_audio_client_free(struct afe_audio_client *ac)
+{
+	int loopcnt;
+	struct afe_audio_port_data *port;
+
+	if (!ac) {
+		pr_err("%s: audio client is NULL\n", __func__);
+		return;
+	}
+	for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+		port = &ac->port[loopcnt];
+		if (!port->buf)
+			continue;
+		pr_debug("%s: loopcnt = %d\n", __func__, loopcnt);
+		q6afe_audio_client_buf_free_contiguous(loopcnt, ac);
+	}
+	kfree(ac);
+}
+
+int afe_cmd_memory_unmap(u32 mem_map_handle)
+{
+	int ret = 0;
+	struct afe_service_cmd_shared_mem_unmap_regions mregion;
+	int index = 0;
+
+	pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+
+	mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mregion.hdr.pkt_size = sizeof(mregion);
+	mregion.hdr.src_port = 0;
+	mregion.hdr.dest_port = 0;
+	mregion.hdr.token = 0;
+	mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS;
+	mregion.mem_map_handle = mem_map_handle;
+
+	/* Todo */
+	index = mregion.hdr.token = IDX_RSVD_2;
+
+	atomic_set(&this_afe.status, 0);
+	ret = afe_apr_send_pkt(&mregion, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: AFE memory unmap cmd failed %d\n",
+		       __func__, ret);
+
+	return ret;
+}
+
+int afe_cmd_memory_unmap_nowait(u32 mem_map_handle)
+{
+	int ret = 0;
+	struct afe_service_cmd_shared_mem_unmap_regions mregion;
+
+	pr_debug("%s: handle 0x%x\n", __func__, mem_map_handle);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+
+	mregion.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mregion.hdr.pkt_size = sizeof(mregion);
+	mregion.hdr.src_port = 0;
+	mregion.hdr.dest_port = 0;
+	mregion.hdr.token = 0;
+	mregion.hdr.opcode = AFE_SERVICE_CMD_SHARED_MEM_UNMAP_REGIONS;
+	mregion.mem_map_handle = mem_map_handle;
+
+	ret = afe_apr_send_pkt(&mregion, NULL);
+	if (ret)
+		pr_err("%s: AFE memory unmap cmd failed %d\n",
+			__func__, ret);
+	return ret;
+}
+
+int afe_register_get_events(u16 port_id,
+		void (*cb)(uint32_t opcode,
+		uint32_t token, uint32_t *payload, void *priv),
+		void *private_data)
+{
+	int ret = 0;
+	struct afe_service_cmd_register_rt_port_driver rtproxy;
+
+	pr_debug("%s: port_id: 0x%x\n", __func__, port_id);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+	if ((port_id == RT_PROXY_DAI_002_RX) ||
+		(port_id == RT_PROXY_DAI_001_TX)) {
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+	} else {
+		pr_err("%s: wrong port id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	if (port_id == RT_PROXY_PORT_001_TX) {
+		this_afe.tx_cb = cb;
+		this_afe.tx_private_data = private_data;
+	} else if (port_id == RT_PROXY_PORT_001_RX) {
+		this_afe.rx_cb = cb;
+		this_afe.rx_private_data = private_data;
+	}
+
+	rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	rtproxy.hdr.pkt_size = sizeof(rtproxy);
+	rtproxy.hdr.src_port = 1;
+	rtproxy.hdr.dest_port = 1;
+	rtproxy.hdr.opcode = AFE_SERVICE_CMD_REGISTER_RT_PORT_DRIVER;
+	rtproxy.port_id = port_id;
+	rtproxy.reserved = 0;
+
+	ret = afe_apr_send_pkt(&rtproxy, NULL);
+	if (ret)
+		pr_err("%s: AFE  reg. rtproxy_event failed %d\n",
+			   __func__, ret);
+	return ret;
+}
+
+int afe_unregister_get_events(u16 port_id)
+{
+	int ret = 0;
+	struct afe_service_cmd_unregister_rt_port_driver rtproxy;
+	int index = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+
+	if ((port_id == RT_PROXY_DAI_002_RX) ||
+		(port_id == RT_PROXY_DAI_001_TX)) {
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+	} else {
+		pr_err("%s: wrong port id 0x%x\n", __func__, port_id);
+		return -EINVAL;
+	}
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x ret %d", __func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	rtproxy.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	rtproxy.hdr.pkt_size = sizeof(rtproxy);
+	rtproxy.hdr.src_port = 0;
+	rtproxy.hdr.dest_port = 0;
+	rtproxy.hdr.token = 0;
+	rtproxy.hdr.opcode = AFE_SERVICE_CMD_UNREGISTER_RT_PORT_DRIVER;
+	rtproxy.port_id = port_id;
+	rtproxy.reserved = 0;
+
+	rtproxy.hdr.token = index;
+
+	if (port_id == RT_PROXY_PORT_001_TX) {
+		this_afe.tx_cb = NULL;
+		this_afe.tx_private_data = NULL;
+	} else if (port_id == RT_PROXY_PORT_001_RX) {
+		this_afe.rx_cb = NULL;
+		this_afe.rx_private_data = NULL;
+	}
+
+	ret = afe_apr_send_pkt(&rtproxy, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: AFE enable Unreg. rtproxy_event failed %d\n",
+			   __func__, ret);
+	return ret;
+}
+
+int afe_rt_proxy_port_write(phys_addr_t buf_addr_p,
+		u32 mem_map_handle, int bytes)
+{
+	int ret = 0;
+	struct afe_port_data_cmd_rt_proxy_port_write_v2 afecmd_wr;
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: register to AFE is not done\n", __func__);
+		ret = -ENODEV;
+		return ret;
+	}
+	pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__,
+						&buf_addr_p, bytes);
+
+	afecmd_wr.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	afecmd_wr.hdr.pkt_size = sizeof(afecmd_wr);
+	afecmd_wr.hdr.src_port = 0;
+	afecmd_wr.hdr.dest_port = 0;
+	afecmd_wr.hdr.token = 0;
+	afecmd_wr.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_WRITE_V2;
+	afecmd_wr.port_id = RT_PROXY_PORT_001_TX;
+	afecmd_wr.buffer_address_lsw = lower_32_bits(buf_addr_p);
+	afecmd_wr.buffer_address_msw =
+			msm_audio_populate_upper_32_bits(buf_addr_p);
+	afecmd_wr.mem_map_handle = mem_map_handle;
+	afecmd_wr.available_bytes = bytes;
+	afecmd_wr.reserved = 0;
+
+	ret = afe_apr_send_pkt(&afecmd_wr, NULL);
+	if (ret)
+		pr_err("%s: AFE rtproxy write to port 0x%x failed %d\n",
+			   __func__, afecmd_wr.port_id, ret);
+	return ret;
+
+}
+
+int afe_rt_proxy_port_read(phys_addr_t buf_addr_p,
+		u32 mem_map_handle, int bytes)
+{
+	int ret = 0;
+	struct afe_port_data_cmd_rt_proxy_port_read_v2 afecmd_rd;
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: register to AFE is not done\n", __func__);
+		ret = -ENODEV;
+		return ret;
+	}
+	pr_debug("%s: buf_addr_p = 0x%pK bytes = %d\n", __func__,
+						&buf_addr_p, bytes);
+
+	afecmd_rd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	afecmd_rd.hdr.pkt_size = sizeof(afecmd_rd);
+	afecmd_rd.hdr.src_port = 0;
+	afecmd_rd.hdr.dest_port = 0;
+	afecmd_rd.hdr.token = 0;
+	afecmd_rd.hdr.opcode = AFE_PORT_DATA_CMD_RT_PROXY_PORT_READ_V2;
+	afecmd_rd.port_id = RT_PROXY_PORT_001_RX;
+	afecmd_rd.buffer_address_lsw = lower_32_bits(buf_addr_p);
+	afecmd_rd.buffer_address_msw =
+				msm_audio_populate_upper_32_bits(buf_addr_p);
+	afecmd_rd.available_bytes = bytes;
+	afecmd_rd.mem_map_handle = mem_map_handle;
+
+	ret = afe_apr_send_pkt(&afecmd_rd, NULL);
+	if (ret)
+		pr_err("%s: AFE rtproxy read  cmd to port 0x%x failed %d\n",
+			   __func__, afecmd_rd.port_id, ret);
+	return ret;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *debugfs_afelb;
+static struct dentry *debugfs_afelb_gain;
+
+static int afe_debug_open(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	pr_info("%s: debug intf %s\n", __func__, (char *) file->private_data);
+	return 0;
+}
+
+static int afe_get_parameters(char *buf, long int *param1, int num_of_par)
+{
+	char *token;
+	int base, cnt;
+
+	token = strsep(&buf, " ");
+
+	for (cnt = 0; cnt < num_of_par; cnt++) {
+		if (token != NULL) {
+			if ((token[1] == 'x') || (token[1] == 'X'))
+				base = 16;
+			else
+				base = 10;
+
+			if (kstrtoul(token, base, &param1[cnt]) != 0) {
+				pr_err("%s: kstrtoul failed\n",
+					__func__);
+				return -EINVAL;
+			}
+
+			token = strsep(&buf, " ");
+		} else {
+			pr_err("%s: token NULL\n", __func__);
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+#define AFE_LOOPBACK_ON (1)
+#define AFE_LOOPBACK_OFF (0)
+static ssize_t afe_debug_write(struct file *filp,
+	const char __user *ubuf, size_t cnt, loff_t *ppos)
+{
+	char *lb_str = filp->private_data;
+	char lbuf[32];
+	int rc;
+	unsigned long param[5];
+
+	if (cnt > sizeof(lbuf) - 1) {
+		pr_err("%s: cnt %zd size %zd\n", __func__, cnt, sizeof(lbuf)-1);
+		return -EINVAL;
+	}
+
+	rc = copy_from_user(lbuf, ubuf, cnt);
+	if (rc) {
+		pr_err("%s: copy from user failed %d\n", __func__, rc);
+		return -EFAULT;
+	}
+
+	lbuf[cnt] = '\0';
+
+	if (!strcmp(lb_str, "afe_loopback")) {
+		rc = afe_get_parameters(lbuf, param, 3);
+		if (!rc) {
+			pr_info("%s: %lu %lu %lu\n", lb_str, param[0], param[1],
+				param[2]);
+
+			if ((param[0] != AFE_LOOPBACK_ON) && (param[0] !=
+				AFE_LOOPBACK_OFF)) {
+				pr_err("%s: Error, parameter 0 incorrect\n",
+					__func__);
+				rc = -EINVAL;
+				goto afe_error;
+			}
+			if ((q6audio_validate_port(param[1]) < 0) ||
+			    (q6audio_validate_port(param[2])) < 0) {
+				pr_err("%s: Error, invalid afe port\n",
+					__func__);
+			}
+			if (this_afe.apr == NULL) {
+				pr_err("%s: Error, AFE not opened\n", __func__);
+				rc = -EINVAL;
+			} else {
+				rc = afe_loopback(param[0], param[1], param[2]);
+			}
+		} else {
+			pr_err("%s: Error, invalid parameters\n", __func__);
+			rc = -EINVAL;
+		}
+
+	} else if (!strcmp(lb_str, "afe_loopback_gain")) {
+		rc = afe_get_parameters(lbuf, param, 2);
+		if (!rc) {
+			pr_info("%s: %s %lu %lu\n",
+				__func__, lb_str, param[0], param[1]);
+
+			rc = q6audio_validate_port(param[0]);
+			if (rc < 0) {
+				pr_err("%s: Error, invalid afe port %d %lu\n",
+					__func__, rc, param[0]);
+				rc = -EINVAL;
+				goto afe_error;
+			}
+
+			if (param[1] > 100) {
+				pr_err("%s: Error, volume should be 0 to 100 percentage param = %lu\n",
+					__func__, param[1]);
+				rc = -EINVAL;
+				goto afe_error;
+			}
+
+			param[1] = (Q6AFE_MAX_VOLUME * param[1]) / 100;
+
+			if (this_afe.apr == NULL) {
+				pr_err("%s: Error, AFE not opened\n", __func__);
+				rc = -EINVAL;
+			} else {
+				rc = afe_loopback_gain(param[0], param[1]);
+			}
+		} else {
+			pr_err("%s: Error, invalid parameters\n", __func__);
+			rc = -EINVAL;
+		}
+	}
+
+afe_error:
+	if (rc == 0)
+		rc = cnt;
+	else
+		pr_err("%s: rc = %d\n", __func__, rc);
+
+	return rc;
+}
+
+static const struct file_operations afe_debug_fops = {
+	.open = afe_debug_open,
+	.write = afe_debug_write
+};
+
+static void config_debug_fs_init(void)
+{
+	debugfs_afelb = debugfs_create_file("afe_loopback",
+	0664, NULL, (void *) "afe_loopback",
+	&afe_debug_fops);
+
+	debugfs_afelb_gain = debugfs_create_file("afe_loopback_gain",
+	0664, NULL, (void *) "afe_loopback_gain",
+	&afe_debug_fops);
+}
+static void config_debug_fs_exit(void)
+{
+	debugfs_remove(debugfs_afelb);
+	debugfs_remove(debugfs_afelb_gain);
+}
+#else
+static void config_debug_fs_init(void)
+{
+}
+static void config_debug_fs_exit(void)
+{
+}
+#endif
+
+void afe_set_dtmf_gen_rx_portid(u16 port_id, int set)
+{
+	if (set)
+		this_afe.dtmf_gen_rx_portid = port_id;
+	else if (this_afe.dtmf_gen_rx_portid == port_id)
+		this_afe.dtmf_gen_rx_portid = -1;
+}
+
+int afe_dtmf_generate_rx(int64_t duration_in_ms,
+			 uint16_t high_freq,
+			 uint16_t low_freq, uint16_t gain)
+{
+	int ret = 0;
+	int index = 0;
+	struct afe_dtmf_generation_command cmd_dtmf;
+
+	pr_debug("%s: DTMF AFE Gen\n", __func__);
+
+	if (afe_validate_port(this_afe.dtmf_gen_rx_portid) < 0) {
+		pr_err("%s: Failed : Invalid Port id = 0x%x\n",
+		       __func__, this_afe.dtmf_gen_rx_portid);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (this_afe.apr == NULL) {
+		this_afe.apr = apr_register("ADSP", "AFE", afe_callback,
+					    0xFFFFFFFF, &this_afe);
+		pr_debug("%s: Register AFE\n", __func__);
+		if (this_afe.apr == NULL) {
+			pr_err("%s: Unable to register AFE\n", __func__);
+			ret = -ENODEV;
+			return ret;
+		}
+		rtac_set_afe_handle(this_afe.apr);
+	}
+
+	pr_debug("%s: dur=%lld: hfreq=%d lfreq=%d gain=%d portid=0x%x\n",
+		__func__,
+		duration_in_ms, high_freq, low_freq, gain,
+		this_afe.dtmf_gen_rx_portid);
+
+	cmd_dtmf.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cmd_dtmf.hdr.pkt_size = sizeof(cmd_dtmf);
+	cmd_dtmf.hdr.src_port = 0;
+	cmd_dtmf.hdr.dest_port = 0;
+	cmd_dtmf.hdr.token = 0;
+	cmd_dtmf.hdr.opcode = AFE_PORTS_CMD_DTMF_CTL;
+	cmd_dtmf.duration_in_ms = duration_in_ms;
+	cmd_dtmf.high_freq = high_freq;
+	cmd_dtmf.low_freq = low_freq;
+	cmd_dtmf.gain = gain;
+	cmd_dtmf.num_ports = 1;
+	cmd_dtmf.port_ids = q6audio_get_port_id(this_afe.dtmf_gen_rx_portid);
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &cmd_dtmf);
+	if (ret < 0) {
+		pr_err("%s: AFE DTMF failed for num_ports:%d ids:0x%x\n",
+		       __func__, cmd_dtmf.num_ports, cmd_dtmf.port_ids);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	index = q6audio_get_port_index(this_afe.dtmf_gen_rx_portid);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+		(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+	return 0;
+
+fail_cmd:
+	pr_err("%s: failed %d\n", __func__, ret);
+	return ret;
+}
+
+int afe_sidetone(u16 tx_port_id, u16 rx_port_id, u16 enable, uint16_t gain)
+{
+	struct afe_loopback_cfg_v1 cmd_sidetone;
+	int ret = 0;
+	int index = 0;
+
+	pr_info("%s: tx_port_id: 0x%x rx_port_id: 0x%x enable:%d gain:%d\n",
+			__func__, tx_port_id, rx_port_id, enable, gain);
+	index = q6audio_get_port_index(rx_port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(rx_port_id);
+	if (ret < 0) {
+		pr_err("%s: Invalid port 0x%x %d", __func__, rx_port_id, ret);
+		return -EINVAL;
+	}
+
+	cmd_sidetone.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cmd_sidetone.hdr.pkt_size = sizeof(cmd_sidetone);
+	cmd_sidetone.hdr.src_port = 0;
+	cmd_sidetone.hdr.dest_port = 0;
+	cmd_sidetone.hdr.token = 0;
+	cmd_sidetone.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	/* should it be rx or tx port id ?? , bharath*/
+	cmd_sidetone.param.port_id = tx_port_id;
+	/* size of data param & payload */
+	cmd_sidetone.param.payload_size = (sizeof(cmd_sidetone) -
+			sizeof(struct apr_hdr) -
+			sizeof(struct afe_port_cmd_set_param_v2));
+	cmd_sidetone.param.payload_address_lsw = 0x00;
+	cmd_sidetone.param.payload_address_msw = 0x00;
+	cmd_sidetone.param.mem_map_handle = 0x00;
+	cmd_sidetone.pdata.module_id = AFE_MODULE_LOOPBACK;
+	cmd_sidetone.pdata.param_id = AFE_PARAM_ID_LOOPBACK_CONFIG;
+	/* size of actual payload only */
+	cmd_sidetone.pdata.param_size =  cmd_sidetone.param.payload_size -
+				sizeof(struct afe_port_param_data_v2);
+
+	cmd_sidetone.loopback_cfg_minor_version =
+					AFE_API_VERSION_LOOPBACK_CONFIG;
+	cmd_sidetone.dst_port_id = rx_port_id;
+	cmd_sidetone.routing_mode = LB_MODE_SIDETONE;
+	cmd_sidetone.enable = enable;
+
+	ret = afe_apr_send_pkt(&cmd_sidetone, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: sidetone failed tx_port:0x%x rx_port:0x%x ret%d\n",
+		__func__, tx_port_id, rx_port_id, ret);
+	return ret;
+}
+
+int afe_validate_port(u16 port_id)
+{
+	int ret;
+
+	switch (port_id) {
+	case PRIMARY_I2S_RX:
+	case PRIMARY_I2S_TX:
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
+	case MI2S_RX:
+	case MI2S_TX:
+	case HDMI_RX:
+	case DISPLAY_PORT_RX:
+	case AFE_PORT_ID_SPDIF_RX:
+	case RSVD_2:
+	case RSVD_3:
+	case DIGI_MIC_TX:
+	case VOICE_RECORD_RX:
+	case VOICE_RECORD_TX:
+	case VOICE_PLAYBACK_TX:
+	case VOICE2_PLAYBACK_TX:
+	case SLIMBUS_0_RX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_RX:
+	case INT_BT_SCO_RX:
+	case INT_BT_SCO_TX:
+	case INT_BT_A2DP_RX:
+	case INT_FM_RX:
+	case INT_FM_TX:
+	case RT_PROXY_PORT_001_RX:
+	case RT_PROXY_PORT_001_TX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_8_TX:
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_USB_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+	case AFE_PORT_ID_QUINARY_MI2S_RX:
+	case AFE_PORT_ID_QUINARY_MI2S_TX:
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+	{
+		ret = 0;
+		break;
+	}
+
+	default:
+		pr_err("%s: default ret 0x%x\n", __func__, port_id);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+int afe_convert_virtual_to_portid(u16 port_id)
+{
+	int ret;
+
+	/*
+	 * if port_id is virtual, convert to physical..
+	 * if port_id is already physical, return physical
+	 */
+	if (afe_validate_port(port_id) < 0) {
+		if (port_id == RT_PROXY_DAI_001_RX ||
+		    port_id == RT_PROXY_DAI_001_TX ||
+		    port_id == RT_PROXY_DAI_002_RX ||
+		    port_id == RT_PROXY_DAI_002_TX) {
+			ret = VIRTUAL_ID_TO_PORTID(port_id);
+		} else {
+			pr_err("%s: wrong port 0x%x\n",
+				__func__, port_id);
+			ret = -EINVAL;
+		}
+	} else
+		ret = port_id;
+
+	return ret;
+}
+int afe_port_stop_nowait(int port_id)
+{
+	struct afe_port_cmd_device_stop stop;
+	int ret = 0;
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: AFE is already closed\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+	port_id = q6audio_convert_virtual_to_portid(port_id);
+
+	stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	stop.hdr.pkt_size = sizeof(stop);
+	stop.hdr.src_port = 0;
+	stop.hdr.dest_port = 0;
+	stop.hdr.token = 0;
+	stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP;
+	stop.port_id = port_id;
+	stop.reserved = 0;
+
+	ret = afe_apr_send_pkt(&stop, NULL);
+	if (ret)
+		pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+fail_cmd:
+	return ret;
+
+}
+
+int afe_close(int port_id)
+{
+	struct afe_port_cmd_device_stop stop;
+	enum afe_mad_type mad_type;
+	int ret = 0;
+	int index = 0;
+	uint16_t port_index;
+
+	if (this_afe.apr == NULL) {
+		pr_err("%s: AFE is already closed\n", __func__);
+		if ((port_id == RT_PROXY_DAI_001_RX) ||
+		    (port_id == RT_PROXY_DAI_002_TX))
+			pcm_afe_instance[port_id & 0x1] = 0;
+		if ((port_id == RT_PROXY_DAI_002_RX) ||
+		    (port_id == RT_PROXY_DAI_001_TX))
+			proxy_afe_instance[port_id & 0x1] = 0;
+		afe_close_done[port_id & 0x1] = true;
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	pr_debug("%s: port_id = 0x%x\n", __func__, port_id);
+	if ((port_id == RT_PROXY_DAI_001_RX) ||
+			(port_id == RT_PROXY_DAI_002_TX)) {
+		pr_debug("%s: before decrementing pcm_afe_instance %d\n",
+			__func__, pcm_afe_instance[port_id & 0x1]);
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+		pcm_afe_instance[port_id & 0x1]--;
+		if ((!(pcm_afe_instance[port_id & 0x1] == 0 &&
+			proxy_afe_instance[port_id & 0x1] == 0)) ||
+			afe_close_done[port_id & 0x1] == true)
+			return 0;
+
+		afe_close_done[port_id & 0x1] = true;
+	}
+
+	if ((port_id == RT_PROXY_DAI_002_RX) ||
+		(port_id == RT_PROXY_DAI_001_TX)) {
+		pr_debug("%s: before decrementing proxy_afe_instance %d\n",
+			__func__, proxy_afe_instance[port_id & 0x1]);
+		port_id = VIRTUAL_ID_TO_PORTID(port_id);
+		proxy_afe_instance[port_id & 0x1]--;
+		if ((!(pcm_afe_instance[port_id & 0x1] == 0 &&
+			proxy_afe_instance[port_id & 0x1] == 0)) ||
+			afe_close_done[port_id & 0x1] == true)
+			return 0;
+
+		afe_close_done[port_id & 0x1] = true;
+	}
+
+	port_id = q6audio_convert_virtual_to_portid(port_id);
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_validate_port(port_id);
+	if (ret < 0) {
+		pr_warn("%s: Not a valid port id 0x%x ret %d\n",
+			__func__, port_id, ret);
+		return -EINVAL;
+	}
+
+	mad_type = afe_port_get_mad_type(port_id);
+	pr_debug("%s: port_id 0x%x, mad_type %d\n", __func__, port_id,
+		 mad_type);
+	if (mad_type != MAD_HW_NONE && mad_type != MAD_SW_AUDIO) {
+		pr_debug("%s: Turn off MAD\n", __func__);
+		ret = afe_turn_onoff_hw_mad(mad_type, false);
+		if (ret) {
+			pr_err("%s: afe_turn_onoff_hw_mad failed %d\n",
+			       __func__, ret);
+			return ret;
+		}
+	} else {
+		pr_debug("%s: Not a MAD port\n", __func__);
+	}
+
+	port_index = afe_get_port_index(port_id);
+	if ((port_index >= 0) && (port_index < AFE_MAX_PORTS)) {
+		this_afe.afe_sample_rates[port_index] = 0;
+		this_afe.topology[port_index] = 0;
+	} else {
+		pr_err("%s: port %d\n", __func__, port_index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if ((port_id == this_afe.aanc_info.aanc_tx_port) &&
+	    (this_afe.aanc_info.aanc_active)) {
+		memset(&this_afe.aanc_info, 0x00, sizeof(this_afe.aanc_info));
+		ret = afe_aanc_mod_enable(this_afe.apr, port_id, 0);
+		if (ret)
+			pr_err("%s: AFE mod disable failed %d\n",
+				__func__, ret);
+	}
+
+	/*
+	 * even if ramp down configuration failed it is not serious enough to
+	 * warrant bailaing out.
+	 */
+	if (afe_spk_ramp_dn_cfg(port_id) < 0)
+		pr_err("%s: ramp down configuration failed\n", __func__);
+
+	stop.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	stop.hdr.pkt_size = sizeof(stop);
+	stop.hdr.src_port = 0;
+	stop.hdr.dest_port = 0;
+	stop.hdr.token = index;
+	stop.hdr.opcode = AFE_PORT_CMD_DEVICE_STOP;
+	stop.port_id = q6audio_get_port_id(port_id);
+	stop.reserved = 0;
+
+	ret = afe_apr_send_pkt(&stop, &this_afe.wait[index]);
+	if (ret)
+		pr_err("%s: AFE close failed %d\n", __func__, ret);
+
+fail_cmd:
+	return ret;
+}
+
+int afe_set_digital_codec_core_clock(u16 port_id,
+				struct afe_digital_clk_cfg *cfg)
+{
+	struct afe_lpass_digital_clk_config_command clk_cfg;
+	int index = 0;
+	int ret = 0;
+
+	if (!cfg) {
+		pr_err("%s: clock cfg is NULL\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+	clk_cfg.hdr.src_port = 0;
+	clk_cfg.hdr.dest_port = 0;
+	clk_cfg.hdr.token = index;
+
+	clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	/*default rx port is taken to enable the codec digital clock*/
+	clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+	clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+						- sizeof(clk_cfg.param);
+	clk_cfg.param.payload_address_lsw = 0x00;
+	clk_cfg.param.payload_address_msw = 0x00;
+	clk_cfg.param.mem_map_handle = 0x00;
+	clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	clk_cfg.pdata.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG;
+	clk_cfg.pdata.param_size =  sizeof(clk_cfg.clk_cfg);
+	clk_cfg.clk_cfg = *cfg;
+
+	pr_debug("%s: Minor version =0x%x clk val = %d\n"
+		 "clk root = 0x%x resrv = 0x%x\n",
+		 __func__, cfg->i2s_cfg_minor_version,
+		 cfg->clk_val, cfg->clk_root, cfg->reserved);
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+	if (ret < 0) {
+		pr_err("%s: AFE enable for port 0x%x ret %d\n",
+		       __func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int afe_set_lpass_clock(u16 port_id, struct afe_clk_cfg *cfg)
+{
+	struct afe_lpass_clk_config_command clk_cfg;
+	int index = 0;
+	int ret = 0;
+
+	if (!cfg) {
+		pr_err("%s: clock cfg is NULL\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_is_digital_pcm_interface(port_id);
+	if (ret < 0) {
+		pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+			__func__, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+	clk_cfg.hdr.src_port = 0;
+	clk_cfg.hdr.dest_port = 0;
+	clk_cfg.hdr.token = index;
+
+	clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+	clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+						- sizeof(clk_cfg.param);
+	clk_cfg.param.payload_address_lsw = 0x00;
+	clk_cfg.param.payload_address_msw = 0x00;
+	clk_cfg.param.mem_map_handle = 0x00;
+	clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	clk_cfg.pdata.param_id = AFE_PARAM_ID_LPAIF_CLK_CONFIG;
+	clk_cfg.pdata.param_size =  sizeof(clk_cfg.clk_cfg);
+	clk_cfg.clk_cfg = *cfg;
+
+	pr_debug("%s: Minor version =0x%x clk val1 = %d\n"
+		 "clk val2 = %d, clk src = 0x%x\n"
+		 "clk root = 0x%x clk mode = 0x%x resrv = 0x%x\n"
+		 "port id = 0x%x\n",
+		 __func__, cfg->i2s_cfg_minor_version,
+		 cfg->clk_val1, cfg->clk_val2, cfg->clk_src,
+		 cfg->clk_root, cfg->clk_set_mode,
+		 cfg->reserved, q6audio_get_port_id(port_id));
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+	if (ret < 0) {
+		pr_err("%s: AFE enable for port 0x%x ret %d\n",
+		       __func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	mutex_unlock(&this_afe.afe_cmd_lock);
+	return ret;
+}
+
+int afe_set_lpass_clk_cfg(int index, struct afe_clk_set *cfg)
+{
+	struct afe_lpass_clk_config_command_v2 clk_cfg;
+	int ret = 0;
+
+	if (!cfg) {
+		pr_err("%s: clock cfg is NULL\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+
+	if (index < 0 || index >= AFE_MAX_PORTS) {
+		pr_err("%s: index[%d] invalid!\n", __func__, index);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+	clk_cfg.hdr.src_port = 0;
+	clk_cfg.hdr.dest_port = 0;
+	clk_cfg.hdr.token = index;
+
+	clk_cfg.hdr.opcode = AFE_SVC_CMD_SET_PARAM;
+	clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+						- sizeof(clk_cfg.param);
+	clk_cfg.param.payload_address_lsw = 0x00;
+	clk_cfg.param.payload_address_msw = 0x00;
+	clk_cfg.param.mem_map_handle = 0x00;
+	clk_cfg.pdata.module_id = AFE_MODULE_CLOCK_SET;
+	clk_cfg.pdata.param_id = AFE_PARAM_ID_CLOCK_SET;
+	clk_cfg.pdata.param_size =  sizeof(clk_cfg.clk_cfg);
+	clk_cfg.clk_cfg = *cfg;
+
+
+	pr_debug("%s: Minor version =0x%x clk id = %d\n"
+		 "clk freq (Hz) = %d, clk attri = 0x%x\n"
+		 "clk root = 0x%x clk enable = 0x%x\n",
+		 __func__, cfg->clk_set_minor_version,
+		 cfg->clk_id, cfg->clk_freq_in_hz, cfg->clk_attri,
+		 cfg->clk_root, cfg->enable);
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+	if (ret < 0) {
+		pr_err("%s: AFE clk cfg failed with ret %d\n",
+		       __func__, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	} else {
+		/* set ret to 0 as no timeout happened */
+		ret = 0;
+	}
+	if (atomic_read(&this_afe.status) != 0) {
+		pr_err("%s: config cmd failed\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	mutex_unlock(&this_afe.afe_cmd_lock);
+	return ret;
+}
+
+int afe_set_lpass_clock_v2(u16 port_id, struct afe_clk_set *cfg)
+{
+	int index = 0;
+	int ret = 0;
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_is_digital_pcm_interface(port_id);
+	if (ret < 0) {
+		pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+			__func__, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_set_lpass_clk_cfg(index, cfg);
+	if (ret)
+		pr_err("%s: afe_set_lpass_clk_cfg_v2 failed %d\n",
+			__func__, ret);
+
+	return ret;
+}
+
+int afe_set_lpass_internal_digital_codec_clock(u16 port_id,
+			struct afe_digital_clk_cfg *cfg)
+{
+	struct afe_lpass_digital_clk_config_command clk_cfg;
+	int index = 0;
+	int ret = 0;
+
+	if (!cfg) {
+		pr_err("%s: clock cfg is NULL\n", __func__);
+		ret = -EINVAL;
+		return ret;
+	}
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_is_digital_pcm_interface(port_id);
+	if (ret < 0) {
+		pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+			__func__, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+	clk_cfg.hdr.src_port = 0;
+	clk_cfg.hdr.dest_port = 0;
+	clk_cfg.hdr.token = index;
+
+	clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+	clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+						- sizeof(clk_cfg.param);
+	clk_cfg.param.payload_address_lsw = 0x00;
+	clk_cfg.param.payload_address_msw = 0x00;
+	clk_cfg.param.mem_map_handle = 0x00;
+	clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	clk_cfg.pdata.param_id = AFE_PARAM_ID_INTERNAL_DIGIATL_CDC_CLK_CONFIG;
+	clk_cfg.pdata.param_size =  sizeof(clk_cfg.clk_cfg);
+	clk_cfg.clk_cfg = *cfg;
+
+	pr_debug("%s: Minor version =0x%x clk val = %d\n"
+		 "clk root = 0x%x resrv = 0x%x port id = 0x%x\n",
+		 __func__, cfg->i2s_cfg_minor_version,
+		 cfg->clk_val, cfg->clk_root, cfg->reserved,
+		 q6audio_get_port_id(port_id));
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+	if (ret < 0) {
+		pr_err("%s: AFE enable for port 0x0x%x ret %d\n",
+		       __func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	return ret;
+}
+
+int afe_enable_lpass_core_shared_clock(u16 port_id, u32 enable)
+{
+	struct afe_lpass_core_shared_clk_config_command clk_cfg;
+	int index = 0;
+	int ret = 0;
+
+	index = q6audio_get_port_index(port_id);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		return -EINVAL;
+	}
+	ret = q6audio_is_digital_pcm_interface(port_id);
+	if (ret < 0) {
+		pr_err("%s: q6audio_is_digital_pcm_interface fail %d\n",
+		       __func__, ret);
+		return -EINVAL;
+	}
+
+	ret = afe_q6_interface_prepare();
+	if (ret != 0) {
+		pr_err("%s: Q6 interface prepare failed %d\n", __func__, ret);
+		return ret;
+	}
+
+	mutex_lock(&this_afe.afe_cmd_lock);
+	clk_cfg.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	clk_cfg.hdr.pkt_size = sizeof(clk_cfg);
+	clk_cfg.hdr.src_port = 0;
+	clk_cfg.hdr.dest_port = 0;
+	clk_cfg.hdr.token = index;
+
+	clk_cfg.hdr.opcode = AFE_PORT_CMD_SET_PARAM_V2;
+	clk_cfg.param.port_id = q6audio_get_port_id(port_id);
+	clk_cfg.param.payload_size = sizeof(clk_cfg) - sizeof(struct apr_hdr)
+						- sizeof(clk_cfg.param);
+	clk_cfg.param.payload_address_lsw = 0x00;
+	clk_cfg.param.payload_address_msw = 0x00;
+	clk_cfg.param.mem_map_handle = 0x00;
+	clk_cfg.pdata.module_id = AFE_MODULE_AUDIO_DEV_INTERFACE;
+	clk_cfg.pdata.param_id = AFE_PARAM_ID_LPASS_CORE_SHARED_CLOCK_CONFIG;
+	clk_cfg.pdata.param_size =  sizeof(clk_cfg.clk_cfg);
+	clk_cfg.clk_cfg.lpass_core_shared_clk_cfg_minor_version =
+				AFE_API_VERSION_LPASS_CORE_SHARED_CLK_CONFIG;
+	clk_cfg.clk_cfg.enable = enable;
+
+	pr_debug("%s: port id = %d, enable = %d\n",
+		 __func__, q6audio_get_port_id(port_id), enable);
+
+	atomic_set(&this_afe.state, 1);
+	atomic_set(&this_afe.status, 0);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *) &clk_cfg);
+	if (ret < 0) {
+		pr_err("%s: AFE enable for port 0x%x ret %d\n",
+		       __func__, port_id, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+
+	ret = wait_event_timeout(this_afe.wait[index],
+			(atomic_read(&this_afe.state) == 0),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+
+fail_cmd:
+	mutex_unlock(&this_afe.afe_cmd_lock);
+	return ret;
+}
+
+int q6afe_check_osr_clk_freq(u32 freq)
+{
+	int ret = 0;
+
+	switch (freq) {
+	case Q6AFE_LPASS_OSR_CLK_12_P288_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_8_P192_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_6_P144_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_4_P096_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_3_P072_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_2_P048_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_1_P536_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_1_P024_MHZ:
+	case Q6AFE_LPASS_OSR_CLK_768_kHZ:
+	case Q6AFE_LPASS_OSR_CLK_512_kHZ:
+		break;
+	default:
+		pr_err("%s: deafault freq 0x%x\n",
+			__func__, freq);
+		ret = -EINVAL;
+	}
+	return ret;
+}
+
+int afe_get_sp_th_vi_ftm_data(struct afe_sp_th_vi_get_param *th_vi)
+{
+	int ret = -EINVAL;
+	int index = 0, port = SLIMBUS_4_TX;
+
+	if (!th_vi) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto done;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	ret = q6audio_validate_port(port);
+	if (ret < 0) {
+		pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret);
+		goto done;
+	}
+	index = q6audio_get_port_index(port);
+	if (index < 0) {
+		pr_err("%s: invalid port 0x%x, index %d\n",
+			__func__, port, index);
+		ret = -EINVAL;
+		goto done;
+	}
+	th_vi->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	th_vi->hdr.pkt_size = sizeof(*th_vi);
+	th_vi->hdr.src_port = 0;
+	th_vi->hdr.dest_port = 0;
+	th_vi->hdr.token = index;
+	th_vi->hdr.opcode =  AFE_PORT_CMD_GET_PARAM_V2;
+	th_vi->get_param.mem_map_handle = 0;
+	th_vi->get_param.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI;
+	th_vi->get_param.param_id = AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS;
+	th_vi->get_param.payload_address_lsw = 0;
+	th_vi->get_param.payload_address_msw = 0;
+	th_vi->get_param.payload_size = sizeof(*th_vi)
+				- sizeof(th_vi->get_param) - sizeof(th_vi->hdr);
+	th_vi->get_param.port_id = q6audio_get_port_id(port);
+	th_vi->pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_TH_VI;
+	th_vi->pdata.param_id = AFE_PARAM_ID_SP_V2_TH_VI_FTM_PARAMS;
+	th_vi->pdata.param_size = sizeof(th_vi->param);
+	atomic_set(&this_afe.status, 0);
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *)th_vi);
+	if (ret < 0) {
+		pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+			 __func__, port, th_vi->get_param.param_id, ret);
+		goto done;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+				 (atomic_read(&this_afe.state) == 0),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(atomic_read(&this_afe.status));
+		goto done;
+	}
+	memcpy(&th_vi->param, &this_afe.th_vi_resp.param,
+		sizeof(this_afe.th_vi_resp.param));
+	pr_debug("%s: DC resistance %d %d temp %d %d status %d %d\n",
+		 __func__, th_vi->param.dc_res_q24[SP_V2_SPKR_1],
+		 th_vi->param.dc_res_q24[SP_V2_SPKR_2],
+		 th_vi->param.temp_q22[SP_V2_SPKR_1],
+		 th_vi->param.temp_q22[SP_V2_SPKR_2],
+		 th_vi->param.status[SP_V2_SPKR_1],
+		 th_vi->param.status[SP_V2_SPKR_2]);
+	ret = 0;
+done:
+	return ret;
+}
+
+int afe_get_sp_ex_vi_ftm_data(struct afe_sp_ex_vi_get_param *ex_vi)
+{
+	int ret = -EINVAL;
+	int index = 0, port = SLIMBUS_4_TX;
+
+	if (!ex_vi) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto done;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	ret = q6audio_validate_port(port);
+	if (ret < 0) {
+		pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret);
+		goto done;
+	}
+
+	index = q6audio_get_port_index(port);
+	if (index < 0) {
+		pr_err("%s: invalid index %d port 0x%x\n", __func__,
+			index, port);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ex_vi->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	ex_vi->hdr.pkt_size = sizeof(*ex_vi);
+	ex_vi->hdr.src_port = 0;
+	ex_vi->hdr.dest_port = 0;
+	ex_vi->hdr.token = index;
+	ex_vi->hdr.opcode =  AFE_PORT_CMD_GET_PARAM_V2;
+	ex_vi->get_param.mem_map_handle = 0;
+	ex_vi->get_param.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
+	ex_vi->get_param.param_id = AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS;
+	ex_vi->get_param.payload_address_lsw = 0;
+	ex_vi->get_param.payload_address_msw = 0;
+	ex_vi->get_param.payload_size = sizeof(*ex_vi)
+		- sizeof(ex_vi->get_param) - sizeof(ex_vi->hdr);
+	ex_vi->get_param.port_id = q6audio_get_port_id(port);
+	ex_vi->pdata.module_id = AFE_MODULE_SPEAKER_PROTECTION_V2_EX_VI;
+	ex_vi->pdata.param_id = AFE_PARAM_ID_SP_V2_EX_VI_FTM_PARAMS;
+	ex_vi->pdata.param_size = sizeof(ex_vi->param);
+	atomic_set(&this_afe.status, 0);
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *)ex_vi);
+	if (ret < 0) {
+		pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+			__func__, port, ex_vi->get_param.param_id, ret);
+		goto done;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+				 (atomic_read(&this_afe.state) == 0),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(atomic_read(&this_afe.status));
+		goto done;
+	}
+	memcpy(&ex_vi->param, &this_afe.ex_vi_resp.param,
+		sizeof(this_afe.ex_vi_resp.param));
+	pr_debug("%s: freq %d %d resistance %d %d qfactor %d %d state %d %d\n",
+		 __func__, ex_vi->param.freq_q20[SP_V2_SPKR_1],
+		 ex_vi->param.freq_q20[SP_V2_SPKR_2],
+		 ex_vi->param.resis_q24[SP_V2_SPKR_1],
+		 ex_vi->param.resis_q24[SP_V2_SPKR_2],
+		 ex_vi->param.qmct_q24[SP_V2_SPKR_1],
+		 ex_vi->param.qmct_q24[SP_V2_SPKR_2],
+		 ex_vi->param.status[SP_V2_SPKR_1],
+		 ex_vi->param.status[SP_V2_SPKR_2]);
+	ret = 0;
+done:
+	return ret;
+}
+
+int afe_spk_prot_get_calib_data(struct afe_spkr_prot_get_vi_calib *calib_resp)
+{
+	int ret = -EINVAL;
+	int index = 0, port = SLIMBUS_4_TX;
+
+	if (!calib_resp) {
+		pr_err("%s: Invalid params\n", __func__);
+		goto fail_cmd;
+	}
+	if (this_afe.vi_tx_port != -1)
+		port = this_afe.vi_tx_port;
+
+	ret = q6audio_validate_port(port);
+	if (ret < 0) {
+		pr_err("%s: invalid port 0x%x ret %d\n", __func__, port, ret);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	index = q6audio_get_port_index(port);
+	if (index < 0 || index > AFE_MAX_PORTS) {
+		pr_err("%s: AFE port index[%d] invalid!\n",
+				__func__, index);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	calib_resp->hdr.hdr_field =
+	APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+	APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	calib_resp->hdr.pkt_size = sizeof(*calib_resp);
+	calib_resp->hdr.src_port = 0;
+	calib_resp->hdr.dest_port = 0;
+	calib_resp->hdr.token = index;
+	calib_resp->hdr.opcode =  AFE_PORT_CMD_GET_PARAM_V2;
+	calib_resp->get_param.mem_map_handle = 0;
+	calib_resp->get_param.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2;
+	calib_resp->get_param.param_id = AFE_PARAM_ID_CALIB_RES_CFG_V2;
+	calib_resp->get_param.payload_address_lsw = 0;
+	calib_resp->get_param.payload_address_msw = 0;
+	calib_resp->get_param.payload_size = sizeof(*calib_resp)
+		- sizeof(calib_resp->get_param) - sizeof(calib_resp->hdr);
+	calib_resp->get_param.port_id = q6audio_get_port_id(port);
+	calib_resp->pdata.module_id = AFE_MODULE_FB_SPKR_PROT_VI_PROC_V2;
+	calib_resp->pdata.param_id = AFE_PARAM_ID_CALIB_RES_CFG_V2;
+	calib_resp->pdata.param_size = sizeof(calib_resp->res_cfg);
+	atomic_set(&this_afe.status, 0);
+	atomic_set(&this_afe.state, 1);
+	ret = apr_send_pkt(this_afe.apr, (uint32_t *)calib_resp);
+	if (ret < 0) {
+		pr_err("%s: get param port 0x%x param id[0x%x]failed %d\n",
+			   __func__, port, calib_resp->get_param.param_id, ret);
+		goto fail_cmd;
+	}
+	ret = wait_event_timeout(this_afe.wait[index],
+		(atomic_read(&this_afe.state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto fail_cmd;
+	}
+	if (atomic_read(&this_afe.status) > 0) {
+		pr_err("%s: config cmd failed [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&this_afe.status)));
+		ret = adsp_err_get_lnx_err_code(
+				atomic_read(&this_afe.status));
+		goto fail_cmd;
+	}
+	memcpy(&calib_resp->res_cfg, &this_afe.calib_data.res_cfg,
+		sizeof(this_afe.calib_data.res_cfg));
+	pr_info("%s: state %s resistance %d %d\n", __func__,
+			 fbsp_state[calib_resp->res_cfg.th_vi_ca_state],
+			 calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_1],
+			 calib_resp->res_cfg.r0_cali_q24[SP_V2_SPKR_2]);
+	ret = 0;
+fail_cmd:
+	return ret;
+}
+
+int afe_spk_prot_feed_back_cfg(int src_port, int dst_port,
+	int l_ch, int r_ch, u32 enable)
+{
+	int ret = -EINVAL;
+	union afe_spkr_prot_config prot_config;
+	int index = 0;
+
+	if (!enable) {
+		pr_debug("%s: Disable Feedback tx path", __func__);
+		this_afe.vi_tx_port = -1;
+		this_afe.vi_rx_port = -1;
+		return 0;
+	}
+
+	if ((q6audio_validate_port(src_port) < 0) ||
+		(q6audio_validate_port(dst_port) < 0)) {
+		pr_err("%s: invalid ports src 0x%x dst 0x%x",
+			__func__, src_port, dst_port);
+		goto fail_cmd;
+	}
+	if (!l_ch && !r_ch) {
+		pr_err("%s: error ch values zero\n", __func__);
+		goto fail_cmd;
+	}
+	pr_debug("%s: src_port 0x%x  dst_port 0x%x l_ch %d r_ch %d\n",
+		 __func__, src_port, dst_port, l_ch, r_ch);
+	memset(&prot_config, 0, sizeof(prot_config));
+	prot_config.feedback_path_cfg.dst_portid =
+		q6audio_get_port_id(dst_port);
+	if (l_ch) {
+		prot_config.feedback_path_cfg.chan_info[index++] = 1;
+		prot_config.feedback_path_cfg.chan_info[index++] = 2;
+	}
+	if (r_ch) {
+		prot_config.feedback_path_cfg.chan_info[index++] = 3;
+		prot_config.feedback_path_cfg.chan_info[index++] = 4;
+	}
+	prot_config.feedback_path_cfg.num_channels = index;
+	pr_debug("%s no of channels: %d\n", __func__, index);
+	prot_config.feedback_path_cfg.minor_version = 1;
+	ret = afe_spk_prot_prepare(src_port, dst_port,
+			AFE_PARAM_ID_FEEDBACK_PATH_CFG, &prot_config);
+fail_cmd:
+	return ret;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+	int ret = -EINVAL;
+
+	switch (cal_type) {
+	case AFE_COMMON_RX_CAL_TYPE:
+		ret = AFE_COMMON_RX_CAL;
+		break;
+	case AFE_COMMON_TX_CAL_TYPE:
+		ret = AFE_COMMON_TX_CAL;
+		break;
+	case AFE_AANC_CAL_TYPE:
+		ret = AFE_AANC_CAL;
+		break;
+	case AFE_HW_DELAY_CAL_TYPE:
+		ret = AFE_HW_DELAY_CAL;
+		break;
+	case AFE_FB_SPKR_PROT_CAL_TYPE:
+		ret = AFE_FB_SPKR_PROT_CAL;
+		break;
+	case AFE_SIDETONE_CAL_TYPE:
+		ret = AFE_SIDETONE_CAL;
+		break;
+	case AFE_TOPOLOGY_CAL_TYPE:
+		ret = AFE_TOPOLOGY_CAL;
+		break;
+	case AFE_CUST_TOPOLOGY_CAL_TYPE:
+		ret = AFE_CUST_TOPOLOGY_CAL;
+		break;
+	default:
+		pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+	}
+	return ret;
+}
+
+int afe_alloc_cal(int32_t cal_type, size_t data_size,
+						void *data)
+{
+	int				ret = 0;
+	int				cal_index;
+
+	cal_index = get_cal_type_index(cal_type);
+	pr_debug("%s: cal_type = %d cal_index = %d\n",
+		  __func__, cal_type, cal_index);
+
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_alloc_cal(data_size, data,
+		this_afe.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int afe_dealloc_cal(int32_t cal_type, size_t data_size,
+							void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+		this_afe.cal_data[cal_index]);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int afe_set_cal(int32_t cal_type, size_t data_size,
+						void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_set_cal(data_size, data,
+		this_afe.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_index == AFE_CUST_TOPOLOGY_CAL) {
+		mutex_lock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+		this_afe.set_custom_topology = 1;
+		pr_debug("%s:[AFE_CUSTOM_TOPOLOGY] ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		mutex_unlock(&this_afe.cal_data[AFE_CUST_TOPOLOGY_CAL]->lock);
+	}
+
+done:
+	return ret;
+}
+
+static struct cal_block_data *afe_find_hw_delay_by_path(
+			struct cal_type_data *cal_type, int path)
+{
+	struct list_head *ptr, *next;
+	struct cal_block_data *cal_block = NULL;
+
+	pr_debug("%s:\n", __func__);
+
+	list_for_each_safe(ptr, next,
+		&cal_type->cal_blocks) {
+
+		cal_block = list_entry(ptr,
+			struct cal_block_data, list);
+
+		if (((struct audio_cal_info_hw_delay *)cal_block->cal_info)
+			->path == path) {
+			return cal_block;
+		}
+	}
+	return NULL;
+}
+
+static int afe_get_cal_hw_delay(int32_t path,
+				struct audio_cal_hw_delay_entry *entry)
+{
+	int ret = 0;
+	int i;
+	struct cal_block_data		*cal_block = NULL;
+	struct audio_cal_hw_delay_data	*hw_delay_info = NULL;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.cal_data[AFE_HW_DELAY_CAL] == NULL) {
+		pr_err("%s: AFE_HW_DELAY_CAL not initialized\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (entry == NULL) {
+		pr_err("%s: entry is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	if ((path >= MAX_PATH_TYPE) || (path < 0)) {
+		pr_err("%s: bad path: %d\n",
+		       __func__, path);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock);
+	cal_block = afe_find_hw_delay_by_path(
+		this_afe.cal_data[AFE_HW_DELAY_CAL], path);
+	if (cal_block == NULL)
+		goto unlock;
+
+	hw_delay_info = &((struct audio_cal_info_hw_delay *)
+		cal_block->cal_info)->data;
+	if (hw_delay_info->num_entries > MAX_HW_DELAY_ENTRIES) {
+		pr_err("%s: invalid num entries: %d\n",
+		       __func__, hw_delay_info->num_entries);
+		ret = -EINVAL;
+		goto unlock;
+	}
+
+	for (i = 0; i < hw_delay_info->num_entries; i++) {
+		if (hw_delay_info->entry[i].sample_rate ==
+			entry->sample_rate) {
+			entry->delay_usec = hw_delay_info->entry[i].delay_usec;
+			break;
+		}
+	}
+	if (i == hw_delay_info->num_entries) {
+		pr_err("%s: Unable to find delay for sample rate %d\n",
+		       __func__, entry->sample_rate);
+		ret = -EFAULT;
+		goto unlock;
+	}
+	pr_debug("%s: Path = %d samplerate = %u usec = %u status %d\n",
+		 __func__, path, entry->sample_rate, entry->delay_usec, ret);
+unlock:
+	mutex_unlock(&this_afe.cal_data[AFE_HW_DELAY_CAL]->lock);
+done:
+	return ret;
+}
+
+static int afe_set_cal_sp_th_vi_ftm_cfg(int32_t cal_type, size_t data_size,
+					void *data)
+{
+	int ret = 0;
+	struct audio_cal_type_sp_th_vi_ftm_cfg *cal_data = data;
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+	    cal_data == NULL ||
+	    data_size != sizeof(*cal_data))
+		goto done;
+
+	pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+	memcpy(&this_afe.th_ftm_cfg, &cal_data->cal_info,
+		sizeof(this_afe.th_ftm_cfg));
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+done:
+	return ret;
+}
+
+static int afe_set_cal_sp_ex_vi_ftm_cfg(int32_t cal_type, size_t data_size,
+					void *data)
+{
+	int ret = 0;
+	struct audio_cal_type_sp_ex_vi_ftm_cfg *cal_data = data;
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL ||
+	    cal_data == NULL ||
+	    data_size != sizeof(*cal_data))
+		goto done;
+
+	pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+	memcpy(&this_afe.ex_ftm_cfg, &cal_data->cal_info,
+		sizeof(this_afe.ex_ftm_cfg));
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+done:
+	return ret;
+}
+
+static int afe_set_cal_fb_spkr_prot(int32_t cal_type, size_t data_size,
+								void *data)
+{
+	int ret = 0;
+	struct audio_cal_type_fb_spk_prot_cfg	*cal_data = data;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+		goto done;
+	if (cal_data == NULL)
+		goto done;
+	if (data_size != sizeof(*cal_data))
+		goto done;
+
+	if (cal_data->cal_info.mode == MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS)
+		__pm_wakeup_event(&wl.ws, jiffies_to_msecs(WAKELOCK_TIMEOUT));
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+	memcpy(&this_afe.prot_cfg, &cal_data->cal_info,
+		sizeof(this_afe.prot_cfg));
+	this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode;
+	this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode;
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+done:
+	return ret;
+}
+
+static int afe_get_cal_sp_th_vi_ftm_param(int32_t cal_type, size_t data_size,
+					  void *data)
+{
+	int i, ret = 0;
+	struct audio_cal_type_sp_th_vi_param *cal_data = data;
+	struct afe_sp_th_vi_get_param th_vi;
+
+	pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL] == NULL ||
+	    cal_data == NULL ||
+	    data_size != sizeof(*cal_data))
+		goto done;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+	for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+		cal_data->cal_info.status[i] = -EINVAL;
+		cal_data->cal_info.r_dc_q24[i] = -1;
+		cal_data->cal_info.temp_q22[i] = -1;
+	}
+	if (!afe_get_sp_th_vi_ftm_data(&th_vi)) {
+		for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+			pr_debug("%s: ftm param status = %d\n",
+				  __func__, th_vi.param.status[i]);
+			if (th_vi.param.status[i] == FBSP_IN_PROGRESS) {
+				cal_data->cal_info.status[i] = -EAGAIN;
+			} else if (th_vi.param.status[i] == FBSP_SUCCESS) {
+				cal_data->cal_info.status[i] = 0;
+				cal_data->cal_info.r_dc_q24[i] =
+					th_vi.param.dc_res_q24[i];
+				cal_data->cal_info.temp_q22[i] =
+					th_vi.param.temp_q22[i];
+			}
+		}
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_TH_VI_CAL]->lock);
+done:
+	return ret;
+}
+
+static int afe_get_cal_sp_ex_vi_ftm_param(int32_t cal_type, size_t data_size,
+					  void *data)
+{
+	int i, ret = 0;
+	struct audio_cal_type_sp_ex_vi_param *cal_data = data;
+	struct afe_sp_ex_vi_get_param ex_vi;
+
+	pr_debug("%s: cal_type = %d\n", __func__, cal_type);
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL] == NULL ||
+	    cal_data == NULL ||
+	    data_size != sizeof(*cal_data))
+		goto done;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+	for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+		cal_data->cal_info.status[i] = -EINVAL;
+		cal_data->cal_info.freq_q20[i] = -1;
+		cal_data->cal_info.resis_q24[i] = -1;
+		cal_data->cal_info.qmct_q24[i] = -1;
+	}
+	if (!afe_get_sp_ex_vi_ftm_data(&ex_vi)) {
+		for (i = 0; i < SP_V2_NUM_MAX_SPKRS; i++) {
+			pr_debug("%s: ftm param status = %d\n",
+				  __func__, ex_vi.param.status[i]);
+			if (ex_vi.param.status[i] == FBSP_IN_PROGRESS) {
+				cal_data->cal_info.status[i] = -EAGAIN;
+			} else if (ex_vi.param.status[i] == FBSP_SUCCESS) {
+				cal_data->cal_info.status[i] = 0;
+				cal_data->cal_info.freq_q20[i] =
+					ex_vi.param.freq_q20[i];
+				cal_data->cal_info.resis_q24[i] =
+					ex_vi.param.resis_q24[i];
+				cal_data->cal_info.qmct_q24[i] =
+					ex_vi.param.qmct_q24[i];
+			}
+		}
+	}
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_EX_VI_CAL]->lock);
+done:
+	return ret;
+}
+
+static int afe_get_cal_fb_spkr_prot(int32_t cal_type, size_t data_size,
+								void *data)
+{
+	int ret = 0;
+	struct audio_cal_type_fb_spk_prot_status *cal_data = data;
+	struct afe_spkr_prot_get_vi_calib calib_resp;
+
+	pr_debug("%s:\n", __func__);
+
+	if (this_afe.cal_data[AFE_FB_SPKR_PROT_CAL] == NULL)
+		goto done;
+	if (cal_data == NULL)
+		goto done;
+	if (data_size != sizeof(*cal_data))
+		goto done;
+
+	mutex_lock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+	if (this_afe.prot_cfg.mode == MSM_SPKR_PROT_CALIBRATED) {
+		cal_data->cal_info.r0[SP_V2_SPKR_1] =
+			this_afe.prot_cfg.r0[SP_V2_SPKR_1];
+		cal_data->cal_info.r0[SP_V2_SPKR_2] =
+			this_afe.prot_cfg.r0[SP_V2_SPKR_2];
+		cal_data->cal_info.status = 0;
+	} else if (this_afe.prot_cfg.mode ==
+			MSM_SPKR_PROT_CALIBRATION_IN_PROGRESS) {
+		/*Call AFE to query the status*/
+		cal_data->cal_info.status = -EINVAL;
+		cal_data->cal_info.r0[SP_V2_SPKR_1] = -1;
+		cal_data->cal_info.r0[SP_V2_SPKR_2] = -1;
+		if (!afe_spk_prot_get_calib_data(&calib_resp)) {
+			if (calib_resp.res_cfg.th_vi_ca_state ==
+							FBSP_IN_PROGRESS)
+				cal_data->cal_info.status = -EAGAIN;
+			else if (calib_resp.res_cfg.th_vi_ca_state ==
+							FBSP_SUCCESS) {
+				cal_data->cal_info.status = 0;
+				cal_data->cal_info.r0[SP_V2_SPKR_1] =
+				calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_1];
+				cal_data->cal_info.r0[SP_V2_SPKR_2] =
+				calib_resp.res_cfg.r0_cali_q24[SP_V2_SPKR_2];
+			}
+		}
+		if (!cal_data->cal_info.status) {
+			this_afe.prot_cfg.mode =
+				MSM_SPKR_PROT_CALIBRATED;
+			this_afe.prot_cfg.r0[SP_V2_SPKR_1] =
+				cal_data->cal_info.r0[SP_V2_SPKR_1];
+			this_afe.prot_cfg.r0[SP_V2_SPKR_2] =
+				cal_data->cal_info.r0[SP_V2_SPKR_2];
+		}
+	} else {
+		/*Indicates calibration data is invalid*/
+		cal_data->cal_info.status = -EINVAL;
+		cal_data->cal_info.r0[SP_V2_SPKR_1] = -1;
+		cal_data->cal_info.r0[SP_V2_SPKR_2] = -1;
+	}
+	this_afe.th_ftm_cfg.mode = this_afe.prot_cfg.mode;
+	this_afe.ex_ftm_cfg.mode = this_afe.prot_cfg.mode;
+	mutex_unlock(&this_afe.cal_data[AFE_FB_SPKR_PROT_CAL]->lock);
+	__pm_relax(&wl.ws);
+done:
+	return ret;
+}
+
+static int afe_map_cal_data(int32_t cal_type,
+				struct cal_block_data *cal_block)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+
+	atomic_set(&this_afe.mem_map_cal_index, cal_index);
+	ret = afe_cmd_memory_map(cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+	atomic_set(&this_afe.mem_map_cal_index, -1);
+	if (ret < 0) {
+		pr_err("%s: mmap did not work! size = %zd ret %d\n",
+			__func__,
+			cal_block->map_data.map_size, ret);
+		pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n",
+			__func__,
+			&cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+		goto done;
+	}
+	cal_block->map_data.q6map_handle = atomic_read(&this_afe.
+		mem_map_cal_handles[cal_index]);
+done:
+	return ret;
+}
+
+static int afe_unmap_cal_data(int32_t cal_type,
+				struct cal_block_data *cal_block)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block == NULL) {
+		pr_err("%s: Cal block is NULL!\n",
+						__func__);
+		goto done;
+	}
+
+	if (cal_block->map_data.q6map_handle == 0) {
+		pr_err("%s: Map handle is NULL, nothing to unmap\n",
+				__func__);
+		goto done;
+	}
+
+	atomic_set(&this_afe.mem_map_cal_handles[cal_index],
+		cal_block->map_data.q6map_handle);
+	atomic_set(&this_afe.mem_map_cal_index, cal_index);
+	ret = afe_cmd_memory_unmap_nowait(
+		cal_block->map_data.q6map_handle);
+	atomic_set(&this_afe.mem_map_cal_index, -1);
+	if (ret < 0) {
+		pr_err("%s: unmap did not work! cal_type %i ret %d\n",
+			__func__, cal_index, ret);
+	}
+	cal_block->map_data.q6map_handle = 0;
+done:
+	return ret;
+}
+
+static void afe_delete_cal_data(void)
+{
+	pr_debug("%s:\n", __func__);
+
+	cal_utils_destroy_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data);
+}
+
+static int afe_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info	cal_type_info[] = {
+		{{AFE_COMMON_RX_CAL_TYPE,
+		{afe_alloc_cal, afe_dealloc_cal, NULL,
+		afe_set_cal, NULL, NULL} },
+		{afe_map_cal_data, afe_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{AFE_COMMON_TX_CAL_TYPE,
+		{afe_alloc_cal, afe_dealloc_cal, NULL,
+		afe_set_cal, NULL, NULL} },
+		{afe_map_cal_data, afe_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{AFE_AANC_CAL_TYPE,
+		{afe_alloc_cal, afe_dealloc_cal, NULL,
+		afe_set_cal, NULL, NULL} },
+		{afe_map_cal_data, afe_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{AFE_FB_SPKR_PROT_CAL_TYPE,
+		{NULL, NULL, NULL, afe_set_cal_fb_spkr_prot,
+		afe_get_cal_fb_spkr_prot, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{AFE_HW_DELAY_CAL_TYPE,
+		{NULL, NULL, NULL,
+		afe_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{AFE_SIDETONE_CAL_TYPE,
+		{NULL, NULL, NULL,
+		afe_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{AFE_TOPOLOGY_CAL_TYPE,
+		{NULL, NULL, NULL,
+		afe_set_cal, NULL, NULL} },
+		{NULL, NULL,
+		cal_utils_match_buf_num} },
+
+		{{AFE_CUST_TOPOLOGY_CAL_TYPE,
+		{afe_alloc_cal, afe_dealloc_cal, NULL,
+		afe_set_cal, NULL, NULL} },
+		{afe_map_cal_data, afe_unmap_cal_data,
+		cal_utils_match_buf_num} },
+
+		{{AFE_FB_SPKR_PROT_TH_VI_CAL_TYPE,
+		{NULL, NULL, NULL, afe_set_cal_sp_th_vi_ftm_cfg,
+		afe_get_cal_sp_th_vi_ftm_param, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{AFE_FB_SPKR_PROT_EX_VI_CAL_TYPE,
+		{NULL, NULL, NULL, afe_set_cal_sp_ex_vi_ftm_cfg,
+		afe_get_cal_sp_ex_vi_ftm_param, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+	};
+	pr_debug("%s:\n", __func__);
+
+	ret = cal_utils_create_cal_types(MAX_AFE_CAL_TYPES, this_afe.cal_data,
+		cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: could not create cal type! %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return ret;
+err:
+	afe_delete_cal_data();
+	return ret;
+}
+
+int afe_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+	int result = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->cal_data.paddr == 0) {
+		pr_debug("%s: No address to map!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->map_data.map_size == 0) {
+		pr_debug("%s: map size is 0!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	result = afe_cmd_memory_map(cal_block->cal_data.paddr,
+		cal_block->map_data.map_size);
+	if (result < 0) {
+		pr_err("%s: afe_cmd_memory_map failed for addr = 0x%pK, size = %d, err %d\n",
+			__func__, &cal_block->cal_data.paddr,
+			cal_block->map_data.map_size, result);
+		return result;
+	}
+	cal_block->map_data.map_handle = this_afe.mmap_handle;
+
+done:
+	return result;
+}
+
+int afe_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+	int result = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (mem_map_handle == NULL) {
+		pr_err("%s: Map handle is NULL, nothing to unmap\n",
+			__func__);
+		goto done;
+	}
+
+	if (*mem_map_handle == 0) {
+		pr_debug("%s: Map handle is 0, nothing to unmap\n",
+			__func__);
+		goto done;
+	}
+
+	result = afe_cmd_memory_unmap(*mem_map_handle);
+	if (result) {
+		pr_err("%s: AFE memory unmap failed %d, handle 0x%x\n",
+		     __func__, result, *mem_map_handle);
+		goto done;
+	} else {
+		*mem_map_handle = 0;
+	}
+
+done:
+	return result;
+}
+
+static int __init afe_init(void)
+{
+	int i = 0, ret;
+
+	atomic_set(&this_afe.state, 0);
+	atomic_set(&this_afe.status, 0);
+	atomic_set(&this_afe.mem_map_cal_index, -1);
+	this_afe.apr = NULL;
+	this_afe.dtmf_gen_rx_portid = -1;
+	this_afe.mmap_handle = 0;
+	this_afe.vi_tx_port = -1;
+	this_afe.vi_rx_port = -1;
+	this_afe.prot_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	this_afe.th_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	this_afe.ex_ftm_cfg.mode = MSM_SPKR_PROT_DISABLED;
+	mutex_init(&this_afe.afe_cmd_lock);
+	for (i = 0; i < AFE_MAX_PORTS; i++) {
+		this_afe.afe_cal_mode[i] = AFE_CAL_MODE_DEFAULT;
+		init_waitqueue_head(&this_afe.wait[i]);
+	}
+	wakeup_source_init(&wl.ws, "spkr-prot");
+	ret = afe_init_cal_data();
+	if (ret)
+		pr_err("%s: could not init cal data! %d\n", __func__, ret);
+
+	config_debug_fs_init();
+	return 0;
+}
+
+static void __exit afe_exit(void)
+{
+	afe_delete_cal_data();
+
+	config_debug_fs_exit();
+	mutex_destroy(&this_afe.afe_cmd_lock);
+	wakeup_source_trash(&wl.ws);
+}
+
+device_initcall(afe_init);
+__exitcall(afe_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6asm.c b/sound/soc/msm/qdsp6v2/q6asm.c
new file mode 100644
index 0000000..779bdd5
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6asm.c
@@ -0,0 +1,8578 @@
+/*
+ * Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ * Author: Brian Swetland <swetland@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/dma-mapping.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/msm_audio.h>
+
+#include <linux/debugfs.h>
+#include <linux/time.h>
+#include <linux/atomic.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/mm.h>
+
+#include <asm/ioctls.h>
+
+#include <linux/memory.h>
+
+#include <sound/apr_audio-v2.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6audio-v2.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/msm-dts-eagle.h>
+#include <sound/adsp_err.h>
+
+#define TRUE        0x01
+#define FALSE       0x00
+#define SESSION_MAX 8
+
+enum {
+	ASM_TOPOLOGY_CAL = 0,
+	ASM_CUSTOM_TOP_CAL,
+	ASM_AUDSTRM_CAL,
+	ASM_RTAC_APR_CAL,
+	ASM_MAX_CAL_TYPES
+};
+
+union asm_token_struct {
+	struct {
+		u8 stream_id;
+		u8 session_id;
+		u8 buf_index;
+		u8 flags;
+	} _token;
+	u32 token;
+} __packed;
+
+
+enum {
+	ASM_DIRECTION_OFFSET,
+	ASM_CMD_NO_WAIT_OFFSET,
+	/*
+	 * Offset is limited to 7 because flags is stored in u8
+	 * field in asm_token_structure defined above. The offset
+	 * starts from 0.
+	 */
+	ASM_MAX_OFFSET = 7,
+};
+
+enum {
+	WAIT_CMD,
+	NO_WAIT_CMD
+};
+
+#define ASM_SET_BIT(n, x)	(n |= 1 << x)
+#define ASM_TEST_BIT(n, x)	((n >> x) & 1)
+
+/* TODO, combine them together */
+static DEFINE_MUTEX(session_lock);
+struct asm_mmap {
+	atomic_t ref_cnt;
+	void *apr;
+};
+
+static struct asm_mmap this_mmap;
+/* session id: 0 reserved */
+static struct audio_client *session[ASM_ACTIVE_STREAMS_ALLOWED + 1];
+
+struct asm_buffer_node {
+	struct list_head list;
+	phys_addr_t buf_phys_addr;
+	uint32_t  mmap_hdl;
+};
+static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv);
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv);
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+			uint32_t pkt_size, uint32_t cmd_flg);
+static void q6asm_add_hdr_custom_topology(struct audio_client *ac,
+					  struct apr_hdr *hdr,
+					  uint32_t pkt_size);
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+			uint32_t pkt_size, uint32_t cmd_flg);
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+				uint32_t bufsz, uint32_t bufcnt,
+				bool is_contiguous);
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir);
+static void q6asm_reset_buf_state(struct audio_client *ac);
+
+static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels,
+				bool use_back_flavor);
+void *q6asm_mmap_apr_reg(void);
+
+static int q6asm_is_valid_session(struct apr_client_data *data, void *priv);
+static int q6asm_get_asm_topology_cal(void);
+static int q6asm_get_asm_app_type_cal(void);
+
+/* for ASM custom topology */
+static struct cal_type_data *cal_data[ASM_MAX_CAL_TYPES];
+static struct audio_buffer common_buf[2];
+static struct audio_client common_client;
+static int set_custom_topology;
+static int topology_map_handle;
+
+struct generic_get_data_ {
+	int valid;
+	int is_inband;
+	int size_in_ints;
+	int ints[];
+};
+static struct generic_get_data_ *generic_get_data;
+
+#ifdef CONFIG_DEBUG_FS
+#define OUT_BUFFER_SIZE 56
+#define IN_BUFFER_SIZE 24
+
+static struct timeval out_cold_tv;
+static struct timeval out_warm_tv;
+static struct timeval out_cont_tv;
+static struct timeval in_cont_tv;
+static long out_enable_flag;
+static long in_enable_flag;
+static struct dentry *out_dentry;
+static struct dentry *in_dentry;
+static int in_cont_index;
+/*This var is used to keep track of first write done for cold output latency */
+static int out_cold_index;
+static char *out_buffer;
+static char *in_buffer;
+
+static inline void q6asm_set_flag_in_token(union asm_token_struct *asm_token,
+					   int flag, int flag_offset)
+{
+	if (flag)
+		ASM_SET_BIT(asm_token->_token.flags, flag_offset);
+}
+
+static inline int q6asm_get_flag_from_token(union asm_token_struct *asm_token,
+					    int flag_offset)
+{
+	return ASM_TEST_BIT(asm_token->_token.flags, flag_offset);
+}
+
+static inline void q6asm_update_token(u32 *token, u8 session_id, u8 stream_id,
+				      u8 buf_index, u8 dir, u8 nowait_flag)
+{
+	union asm_token_struct asm_token;
+
+	asm_token.token = 0;
+	asm_token._token.session_id = session_id;
+	asm_token._token.stream_id = stream_id;
+	asm_token._token.buf_index = buf_index;
+	q6asm_set_flag_in_token(&asm_token, dir, ASM_DIRECTION_OFFSET);
+	q6asm_set_flag_in_token(&asm_token, nowait_flag,
+				  ASM_CMD_NO_WAIT_OFFSET);
+	*token = asm_token.token;
+}
+
+static inline uint32_t q6asm_get_pcm_format_id(uint32_t media_format_block_ver)
+{
+	uint32_t pcm_format_id;
+
+	switch (media_format_block_ver) {
+	case PCM_MEDIA_FORMAT_V4:
+		pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V4;
+		break;
+	case PCM_MEDIA_FORMAT_V3:
+		pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+		break;
+	case PCM_MEDIA_FORMAT_V2:
+	default:
+		pcm_format_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+		break;
+	}
+	return pcm_format_id;
+}
+
+/*
+ * q6asm_get_buf_index_from_token:
+ *       Retrieve buffer index from token.
+ *
+ * @token: token value sent to ASM service on q6.
+ * Returns buffer index in the read/write commands.
+ */
+uint8_t q6asm_get_buf_index_from_token(uint32_t token)
+{
+	union asm_token_struct asm_token;
+
+	asm_token.token = token;
+	return asm_token._token.buf_index;
+}
+EXPORT_SYMBOL(q6asm_get_buf_index_from_token);
+
+/*
+ * q6asm_get_stream_id_from_token:
+ *       Retrieve stream id from token.
+ *
+ * @token: token value sent to ASM service on q6.
+ * Returns stream id.
+ */
+uint8_t q6asm_get_stream_id_from_token(uint32_t token)
+{
+	union asm_token_struct asm_token;
+
+	asm_token.token = token;
+	return asm_token._token.stream_id;
+}
+EXPORT_SYMBOL(q6asm_get_stream_id_from_token);
+
+static int audio_output_latency_dbgfs_open(struct inode *inode,
+							struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+static ssize_t audio_output_latency_dbgfs_read(struct file *file,
+				char __user *buf, size_t count, loff_t *ppos)
+{
+	if (out_buffer == NULL) {
+		pr_err("%s: out_buffer is null\n", __func__);
+		return 0;
+	}
+	snprintf(out_buffer, OUT_BUFFER_SIZE, "%ld,%ld,%ld,%ld,%ld,%ld,",
+		out_cold_tv.tv_sec, out_cold_tv.tv_usec, out_warm_tv.tv_sec,
+		out_warm_tv.tv_usec, out_cont_tv.tv_sec, out_cont_tv.tv_usec);
+	return  simple_read_from_buffer(buf, OUT_BUFFER_SIZE, ppos,
+						out_buffer, OUT_BUFFER_SIZE);
+}
+static ssize_t audio_output_latency_dbgfs_write(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos)
+{
+	char *temp;
+
+	if (count > 2*sizeof(char)) {
+		pr_err("%s: err count is more %zd\n", __func__, count);
+		return -EINVAL;
+	}
+	temp  = kmalloc(2*sizeof(char), GFP_KERNEL);
+
+	out_cold_index = 0;
+
+	if (temp) {
+		if (copy_from_user(temp, buf, 2*sizeof(char))) {
+			pr_err("%s: copy from user failed for size %zd\n",
+				__func__, 2*sizeof(char));
+			kfree(temp);
+			return -EFAULT;
+		}
+		if (!kstrtol(temp, 10, &out_enable_flag)) {
+			kfree(temp);
+			return count;
+		}
+		kfree(temp);
+	}
+	return -EINVAL;
+}
+static const struct file_operations audio_output_latency_debug_fops = {
+	.open = audio_output_latency_dbgfs_open,
+	.read = audio_output_latency_dbgfs_read,
+	.write = audio_output_latency_dbgfs_write
+};
+static int audio_input_latency_dbgfs_open(struct inode *inode,
+							struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+static ssize_t audio_input_latency_dbgfs_read(struct file *file,
+				char __user *buf, size_t count, loff_t *ppos)
+{
+	if (in_buffer == NULL) {
+		pr_err("%s: in_buffer is null\n", __func__);
+		return 0;
+	}
+	snprintf(in_buffer, IN_BUFFER_SIZE, "%ld,%ld,",
+				in_cont_tv.tv_sec, in_cont_tv.tv_usec);
+	return  simple_read_from_buffer(buf, IN_BUFFER_SIZE, ppos,
+						in_buffer, IN_BUFFER_SIZE);
+}
+static ssize_t audio_input_latency_dbgfs_write(struct file *file,
+			const char __user *buf, size_t count, loff_t *ppos)
+{
+	char *temp;
+
+	if (count > 2*sizeof(char)) {
+		pr_err("%s: err count is more %zd\n", __func__, count);
+		return -EINVAL;
+	}
+	temp  = kmalloc(2*sizeof(char), GFP_KERNEL);
+
+	if (temp) {
+		if (copy_from_user(temp, buf, 2*sizeof(char))) {
+			pr_err("%s: copy from user failed for size %zd\n",
+				__func__, 2*sizeof(char));
+			kfree(temp);
+			return -EFAULT;
+		}
+		if (!kstrtol(temp, 10, &in_enable_flag)) {
+			kfree(temp);
+			return count;
+		}
+		kfree(temp);
+	}
+	return -EINVAL;
+}
+static const struct file_operations audio_input_latency_debug_fops = {
+	.open = audio_input_latency_dbgfs_open,
+	.read = audio_input_latency_dbgfs_read,
+	.write = audio_input_latency_dbgfs_write
+};
+
+static void config_debug_fs_write_cb(void)
+{
+	if (out_enable_flag) {
+		/* For first Write done log the time and reset
+		 * out_cold_index
+		 */
+		if (out_cold_index != 1) {
+			do_gettimeofday(&out_cold_tv);
+			pr_debug("COLD: apr_send_pkt at %ld sec %ld microsec\n",
+				out_cold_tv.tv_sec,
+				out_cold_tv.tv_usec);
+			out_cold_index = 1;
+		}
+		pr_debug("%s: out_enable_flag %ld\n",
+			__func__, out_enable_flag);
+	}
+}
+static void config_debug_fs_read_cb(void)
+{
+	if (in_enable_flag) {
+		/* when in_cont_index == 7, DSP would be
+		 * writing into the 8th 512 byte buffer and this
+		 * timestamp is tapped here.Once done it then writes
+		 * to 9th 512 byte buffer.These two buffers(8th, 9th)
+		 * reach the test application in 5th iteration and that
+		 * timestamp is tapped at user level. The difference
+		 * of these two timestamps gives us the time between
+		 * the time at which dsp started filling the sample
+		 * required and when it reached the test application.
+		 * Hence continuous input latency
+		 */
+		if (in_cont_index == 7) {
+			do_gettimeofday(&in_cont_tv);
+			pr_info("%s: read buffer at %ld sec %ld microsec\n",
+				__func__,
+				in_cont_tv.tv_sec, in_cont_tv.tv_usec);
+		}
+		in_cont_index++;
+	}
+}
+
+static void config_debug_fs_reset_index(void)
+{
+	in_cont_index = 0;
+}
+
+static void config_debug_fs_run(void)
+{
+	if (out_enable_flag) {
+		do_gettimeofday(&out_cold_tv);
+		pr_debug("%s: COLD apr_send_pkt at %ld sec %ld microsec\n",
+			__func__, out_cold_tv.tv_sec, out_cold_tv.tv_usec);
+	}
+}
+
+static void config_debug_fs_write(struct audio_buffer *ab)
+{
+	if (out_enable_flag) {
+		char zero_pattern[2] = {0x00, 0x00};
+		/* If First two byte is non zero and last two byte
+		 * is zero then it is warm output pattern
+		 */
+		if ((strcmp(((char *)ab->data), zero_pattern)) &&
+		(!strcmp(((char *)ab->data + 2), zero_pattern))) {
+			do_gettimeofday(&out_warm_tv);
+			pr_debug("%s: WARM:apr_send_pkt at %ld sec %ld microsec\n",
+			 __func__,
+			 out_warm_tv.tv_sec,
+			out_warm_tv.tv_usec);
+			pr_debug("%s: Warm Pattern Matched\n", __func__);
+		}
+		/* If First two byte is zero and last two byte is
+		 * non zero then it is cont output pattern
+		 */
+		else if ((!strcmp(((char *)ab->data), zero_pattern))
+		&& (strcmp(((char *)ab->data + 2), zero_pattern))) {
+			do_gettimeofday(&out_cont_tv);
+			pr_debug("%s: CONT:apr_send_pkt at %ld sec %ld microsec\n",
+			__func__,
+			out_cont_tv.tv_sec,
+			out_cont_tv.tv_usec);
+			pr_debug("%s: Cont Pattern Matched\n", __func__);
+		}
+	}
+}
+static void config_debug_fs_init(void)
+{
+	out_buffer = kzalloc(OUT_BUFFER_SIZE, GFP_KERNEL);
+	if (out_buffer == NULL)
+		goto outbuf_fail;
+
+	in_buffer = kzalloc(IN_BUFFER_SIZE, GFP_KERNEL);
+	if (in_buffer == NULL)
+		goto inbuf_fail;
+
+	out_dentry = debugfs_create_file("audio_out_latency_measurement_node",
+				0664,
+				NULL, NULL, &audio_output_latency_debug_fops);
+	if (IS_ERR(out_dentry)) {
+		pr_err("%s: debugfs_create_file failed\n", __func__);
+		goto file_fail;
+	}
+	in_dentry = debugfs_create_file("audio_in_latency_measurement_node",
+				0664,
+				NULL, NULL, &audio_input_latency_debug_fops);
+	if (IS_ERR(in_dentry)) {
+		pr_err("%s: debugfs_create_file failed\n", __func__);
+		goto file_fail;
+	}
+	return;
+file_fail:
+	kfree(in_buffer);
+inbuf_fail:
+	kfree(out_buffer);
+outbuf_fail:
+	in_buffer = NULL;
+	out_buffer = NULL;
+}
+#else
+static void config_debug_fs_write(struct audio_buffer *ab)
+{
+}
+static void config_debug_fs_run(void)
+{
+}
+static void config_debug_fs_reset_index(void)
+{
+}
+static void config_debug_fs_read_cb(void)
+{
+}
+static void config_debug_fs_write_cb(void)
+{
+}
+static void config_debug_fs_init(void)
+{
+}
+#endif
+
+int q6asm_mmap_apr_dereg(void)
+{
+	int c;
+
+	c = atomic_sub_return(1, &this_mmap.ref_cnt);
+	if (c == 0) {
+		apr_deregister(this_mmap.apr);
+		common_client.mmap_apr = NULL;
+		pr_debug("%s: APR De-Register common port\n", __func__);
+	} else if (c < 0) {
+		pr_err("%s: APR Common Port Already Closed %d\n",
+			__func__, c);
+		atomic_set(&this_mmap.ref_cnt, 0);
+	}
+
+	return 0;
+}
+
+static int q6asm_session_alloc(struct audio_client *ac)
+{
+	int n;
+
+	for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+		if (!session[n]) {
+			session[n] = ac;
+			return n;
+		}
+	}
+	pr_err("%s: session not available\n", __func__);
+	return -ENOMEM;
+}
+
+static bool q6asm_is_valid_audio_client(struct audio_client *ac)
+{
+	int n;
+
+	for (n = 1; n <= ASM_ACTIVE_STREAMS_ALLOWED; n++) {
+		if (session[n] == ac)
+			return 1;
+	}
+	return 0;
+}
+
+static void q6asm_session_free(struct audio_client *ac)
+{
+	pr_debug("%s: sessionid[%d]\n", __func__, ac->session);
+	rtac_remove_popp_from_adm_devices(ac->session);
+	session[ac->session] = 0;
+	ac->session = 0;
+	ac->perf_mode = LEGACY_PCM_MODE;
+	ac->fptr_cache_ops = NULL;
+}
+
+static uint32_t q6asm_get_next_buf(struct audio_client *ac,
+		uint32_t curr_buf, uint32_t max_buf_cnt)
+{
+	dev_vdbg(ac->dev, "%s: curr_buf = %d, max_buf_cnt = %d\n",
+		 __func__, curr_buf, max_buf_cnt);
+	curr_buf += 1;
+	return (curr_buf >= max_buf_cnt) ? 0 : curr_buf;
+}
+
+static int q6asm_map_cal_memory(int32_t cal_type,
+	struct cal_block_data *cal_block)
+{
+	int result = 0;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n",
+			__func__);
+		goto done;
+	}
+
+	if (cal_block->cal_data.paddr == 0) {
+		pr_debug("%s: No address to map!\n",
+			__func__);
+		goto done;
+	}
+
+	common_client.mmap_apr = q6asm_mmap_apr_reg();
+	if (common_client.mmap_apr == NULL) {
+		pr_err("%s: q6asm_mmap_apr_reg failed\n",
+			__func__);
+		result = -EPERM;
+		goto done;
+	}
+	common_client.apr = common_client.mmap_apr;
+	if (cal_block->map_data.map_size == 0) {
+		pr_debug("%s: map size is 0!\n",
+			__func__);
+		goto done;
+	}
+
+	/* Use second asm buf to map memory */
+	if (common_client.port[IN].buf == NULL) {
+		pr_err("%s: common buf is NULL\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	common_client.port[IN].buf->phys = cal_block->cal_data.paddr;
+
+	result = q6asm_memory_map_regions(&common_client,
+			IN, cal_block->map_data.map_size, 1, 1);
+	if (result < 0) {
+		pr_err("%s: mmap did not work! size = %zd result %d\n",
+			__func__,
+			cal_block->map_data.map_size, result);
+		pr_debug("%s: mmap did not work! addr = 0x%pK, size = %zd\n",
+			__func__,
+			&cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+		goto done;
+	}
+
+	list_for_each_safe(ptr, next,
+		&common_client.port[IN].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+					list);
+		if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) {
+			cal_block->map_data.q6map_handle =  buf_node->mmap_hdl;
+			break;
+		}
+	}
+done:
+	return result;
+}
+
+static int remap_cal_data(int32_t cal_type, struct cal_block_data *cal_block)
+{
+	int ret = 0;
+
+	if (cal_block->map_data.ion_client == NULL) {
+		pr_err("%s: No ION allocation for cal type %d!\n",
+			__func__, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((cal_block->map_data.map_size > 0) &&
+		(cal_block->map_data.q6map_handle == 0)) {
+
+		ret = q6asm_map_cal_memory(cal_type, cal_block);
+		if (ret < 0) {
+			pr_err("%s: mmap did not work! size = %zd ret %d\n",
+				__func__, cal_block->map_data.map_size, ret);
+			goto done;
+		}
+	}
+done:
+	return ret;
+}
+
+static int q6asm_unmap_cal_memory(int32_t cal_type,
+	struct cal_block_data *cal_block)
+{
+	int			result = 0;
+	int			result2 = 0;
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->map_data.q6map_handle == 0) {
+		pr_debug("%s: No address to unmap!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (common_client.mmap_apr == NULL) {
+		common_client.mmap_apr = q6asm_mmap_apr_reg();
+		if (common_client.mmap_apr == NULL) {
+			pr_err("%s: q6asm_mmap_apr_reg failed\n",
+				__func__);
+			result = -EPERM;
+			goto done;
+		}
+	}
+
+	result2 = q6asm_memory_unmap_regions(&common_client, IN);
+	if (result2 < 0) {
+		pr_err("%s: unmap failed, err %d\n",
+			__func__, result2);
+		result = result2;
+	}
+
+	cal_block->map_data.q6map_handle = 0;
+done:
+	return result;
+}
+
+int q6asm_unmap_cal_data(int cal_type, struct cal_block_data *cal_block)
+{
+	int ret = 0;
+
+	if ((cal_block->map_data.map_size > 0) &&
+		(cal_block->map_data.q6map_handle != 0)) {
+
+		ret = q6asm_unmap_cal_memory(cal_type, cal_block);
+		if (ret < 0) {
+			pr_err("%s: unmap did not work! size = %zd ret %d\n",
+				__func__, cal_block->map_data.map_size, ret);
+			goto done;
+		}
+	}
+done:
+	return ret;
+}
+
+int send_asm_custom_topology(struct audio_client *ac)
+{
+	struct cal_block_data		*cal_block = NULL;
+	struct cmd_set_topologies	asm_top;
+	int result = 0;
+	int result1 = 0;
+
+	if (cal_data[ASM_CUSTOM_TOP_CAL] == NULL)
+		goto done;
+
+	mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+	if (!set_custom_topology)
+		goto unlock;
+	set_custom_topology = 0;
+
+	cal_block = cal_utils_get_only_cal_block(cal_data[ASM_CUSTOM_TOP_CAL]);
+	if (cal_block == NULL)
+		goto unlock;
+
+	if (cal_block->cal_data.size == 0) {
+		pr_debug("%s: No cal to send!\n", __func__);
+		goto unlock;
+	}
+
+	pr_debug("%s: Sending cal_index %d\n", __func__, ASM_CUSTOM_TOP_CAL);
+
+	result = remap_cal_data(ASM_CUST_TOPOLOGY_CAL_TYPE, cal_block);
+	if (result) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, ASM_CUSTOM_TOP_CAL);
+		goto unlock;
+	}
+
+	q6asm_add_hdr_custom_topology(ac, &asm_top.hdr, sizeof(asm_top));
+	atomic_set(&ac->mem_state, -1);
+	asm_top.hdr.opcode = ASM_CMD_ADD_TOPOLOGIES;
+	asm_top.payload_addr_lsw = lower_32_bits(cal_block->cal_data.paddr);
+	asm_top.payload_addr_msw = msm_audio_populate_upper_32_bits(
+						cal_block->cal_data.paddr);
+	asm_top.mem_map_handle = cal_block->map_data.q6map_handle;
+	asm_top.payload_size = cal_block->cal_data.size;
+
+	 pr_debug("%s: Sending ASM_CMD_ADD_TOPOLOGIES payload = %pK, size = %d, map handle = 0x%x\n",
+		__func__, &cal_block->cal_data.paddr,
+		asm_top.payload_size, asm_top.mem_map_handle);
+
+	result = apr_send_pkt(ac->apr, (uint32_t *) &asm_top);
+	if (result < 0) {
+		pr_err("%s: Set topologies failed result %d\n",
+			__func__, result);
+		pr_debug("%s: Set topologies failed payload = 0x%pK\n",
+			__func__, &cal_block->cal_data.paddr);
+		goto unmap;
+
+	}
+
+	result = wait_event_timeout(ac->mem_wait,
+			(atomic_read(&ac->mem_state) >= 0), 5*HZ);
+	if (!result) {
+		pr_err("%s: Set topologies failed timeout\n", __func__);
+		pr_debug("%s: Set topologies failed after timedout payload = 0x%pK\n",
+			__func__, &cal_block->cal_data.paddr);
+		result = -ETIMEDOUT;
+		goto unmap;
+	}
+	if (atomic_read(&ac->mem_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->mem_state)));
+		result = adsp_err_get_lnx_err_code(
+			atomic_read(&ac->mem_state));
+		goto unmap;
+	}
+
+unmap:
+	result1 = q6asm_unmap_cal_memory(ASM_CUST_TOPOLOGY_CAL_TYPE,
+		cal_block);
+	if (result1 < 0) {
+		result = result1;
+		pr_debug("%s: unmap cal failed! %d\n", __func__, result);
+	}
+unlock:
+	mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+done:
+	return result;
+}
+
+int q6asm_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+	int result = 0;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+
+	pr_debug("%s:\n", __func__);
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->cal_data.paddr == 0) {
+		pr_debug("%s: No address to map!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (common_client.mmap_apr == NULL) {
+		common_client.mmap_apr = q6asm_mmap_apr_reg();
+		if (common_client.mmap_apr == NULL) {
+			pr_err("%s: q6asm_mmap_apr_reg failed\n",
+				__func__);
+			result = -EPERM;
+			goto done;
+		}
+	}
+
+	if (cal_block->map_data.map_size == 0) {
+		pr_debug("%s: map size is 0!\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	/* Use second asm buf to map memory */
+	if (common_client.port[OUT].buf == NULL) {
+		pr_err("%s: common buf is NULL\n",
+			__func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	common_client.port[OUT].buf->phys = cal_block->cal_data.paddr;
+
+	result = q6asm_memory_map_regions(&common_client,
+			OUT, cal_block->map_data.map_size, 1, 1);
+	if (result < 0) {
+		pr_err("%s: mmap did not work! size = %d result %d\n",
+			__func__,
+			cal_block->map_data.map_size, result);
+		pr_debug("%s: mmap did not work! addr = 0x%pK, size = %d\n",
+			__func__,
+			&cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+		goto done;
+	}
+
+	list_for_each_safe(ptr, next,
+		&common_client.port[OUT].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+					list);
+		if (buf_node->buf_phys_addr == cal_block->cal_data.paddr) {
+			cal_block->map_data.map_handle =  buf_node->mmap_hdl;
+			break;
+		}
+	}
+done:
+	return result;
+}
+
+int q6asm_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+	int result = 0;
+	int result2 = 0;
+
+	pr_debug("%s:\n", __func__);
+
+	if (mem_map_handle == NULL) {
+		pr_debug("%s: Map handle is NULL, nothing to unmap\n",
+			__func__);
+		goto done;
+	}
+
+	if (*mem_map_handle == 0) {
+		pr_debug("%s: Map handle is 0, nothing to unmap\n",
+			__func__);
+		goto done;
+	}
+
+	if (common_client.mmap_apr == NULL) {
+		common_client.mmap_apr = q6asm_mmap_apr_reg();
+		if (common_client.mmap_apr == NULL) {
+			pr_err("%s: q6asm_mmap_apr_reg failed\n",
+				__func__);
+			result = -EPERM;
+			goto done;
+		}
+	}
+
+
+	result2 = q6asm_memory_unmap_regions(&common_client, OUT);
+	if (result2 < 0) {
+		pr_err("%s: unmap failed, err %d\n",
+			__func__, result2);
+		result = result2;
+	} else {
+		mem_map_handle = 0;
+	}
+
+	result2 = q6asm_mmap_apr_dereg();
+	if (result2 < 0) {
+		pr_err("%s: q6asm_mmap_apr_dereg failed, err %d\n",
+			__func__, result2);
+		result = result2;
+	}
+done:
+	return result;
+}
+
+int q6asm_audio_client_buf_free(unsigned int dir,
+			struct audio_client *ac)
+{
+	struct audio_port_data *port;
+	int cnt = 0;
+	int rc = 0;
+
+	pr_debug("%s: Session id %d\n", __func__, ac->session);
+	mutex_lock(&ac->cmd_lock);
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[dir];
+		if (!port->buf) {
+			pr_err("%s: buf NULL\n", __func__);
+			mutex_unlock(&ac->cmd_lock);
+			return 0;
+		}
+		cnt = port->max_buf_cnt - 1;
+
+		if (cnt >= 0) {
+			rc = q6asm_memory_unmap_regions(ac, dir);
+			if (rc < 0)
+				pr_err("%s: Memory_unmap_regions failed %d\n",
+								__func__, rc);
+		}
+
+		while (cnt >= 0) {
+			if (port->buf[cnt].data) {
+				if (!rc || atomic_read(&ac->reset))
+					msm_audio_ion_free(
+						port->buf[cnt].client,
+						port->buf[cnt].handle);
+
+				port->buf[cnt].client = NULL;
+				port->buf[cnt].handle = NULL;
+				port->buf[cnt].data = NULL;
+				port->buf[cnt].phys = 0;
+				--(port->max_buf_cnt);
+			}
+			--cnt;
+		}
+		kfree(port->buf);
+		port->buf = NULL;
+	}
+	mutex_unlock(&ac->cmd_lock);
+	return 0;
+}
+
+int q6asm_audio_client_buf_free_contiguous(unsigned int dir,
+			struct audio_client *ac)
+{
+	struct audio_port_data *port;
+	int cnt = 0;
+	int rc = 0;
+
+	pr_debug("%s: Session id %d\n", __func__, ac->session);
+	mutex_lock(&ac->cmd_lock);
+	port = &ac->port[dir];
+	if (!port->buf) {
+		mutex_unlock(&ac->cmd_lock);
+		return 0;
+	}
+	cnt = port->max_buf_cnt - 1;
+
+	if (cnt >= 0) {
+		rc = q6asm_memory_unmap(ac, port->buf[0].phys, dir);
+		if (rc < 0)
+			pr_err("%s: Memory_unmap_regions failed %d\n",
+							__func__, rc);
+	}
+
+	if (port->buf[0].data) {
+		pr_debug("%s: data[%pK]phys[%pK][%pK] , client[%pK] handle[%pK]\n",
+			__func__,
+			port->buf[0].data,
+			&port->buf[0].phys,
+			&port->buf[0].phys,
+			port->buf[0].client,
+			port->buf[0].handle);
+		if (!rc || atomic_read(&ac->reset))
+			msm_audio_ion_free(port->buf[0].client,
+					   port->buf[0].handle);
+		port->buf[0].client = NULL;
+		port->buf[0].handle = NULL;
+	}
+
+	while (cnt >= 0) {
+		port->buf[cnt].data = NULL;
+		port->buf[cnt].phys = 0;
+		cnt--;
+	}
+	port->max_buf_cnt = 0;
+	kfree(port->buf);
+	port->buf = NULL;
+	mutex_unlock(&ac->cmd_lock);
+	return 0;
+}
+
+void q6asm_audio_client_free(struct audio_client *ac)
+{
+	int loopcnt;
+	struct audio_port_data *port;
+
+	if (!ac) {
+		pr_err("%s: ac %pK\n", __func__, ac);
+		return;
+	}
+	if (!ac->session) {
+		pr_err("%s: ac session invalid\n", __func__);
+		return;
+	}
+
+	mutex_lock(&session_lock);
+
+	pr_debug("%s: Session id %d\n", __func__, ac->session);
+	if (ac->io_mode & SYNC_IO_MODE) {
+		for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+			port = &ac->port[loopcnt];
+			if (!port->buf)
+				continue;
+			pr_debug("%s: loopcnt = %d\n",
+				__func__, loopcnt);
+			q6asm_audio_client_buf_free(loopcnt, ac);
+		}
+	}
+
+	rtac_set_asm_handle(ac->session, NULL);
+	apr_deregister(ac->apr2);
+	apr_deregister(ac->apr);
+	q6asm_mmap_apr_dereg();
+	ac->apr2 = NULL;
+	ac->apr = NULL;
+	ac->mmap_apr = NULL;
+	q6asm_session_free(ac);
+
+	pr_debug("%s: APR De-Register\n", __func__);
+
+/*done:*/
+	kfree(ac);
+	ac = NULL;
+	mutex_unlock(&session_lock);
+}
+
+int q6asm_set_io_mode(struct audio_client *ac, uint32_t mode1)
+{
+	uint32_t mode;
+	int ret = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	ac->io_mode &= 0xFF00;
+	mode = (mode1 & 0xF);
+
+	pr_debug("%s: ac->mode after anding with FF00:0x%x,\n",
+		__func__, ac->io_mode);
+
+	if ((mode == ASYNC_IO_MODE) || (mode == SYNC_IO_MODE)) {
+		ac->io_mode |= mode1;
+		pr_debug("%s: Set Mode to 0x%x\n", __func__, ac->io_mode);
+	} else {
+		pr_err("%s: Not an valid IO Mode:%d\n", __func__, ac->io_mode);
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+void *q6asm_mmap_apr_reg(void)
+{
+	if ((atomic_read(&this_mmap.ref_cnt) == 0) ||
+	    (this_mmap.apr == NULL)) {
+		this_mmap.apr = apr_register("ADSP", "ASM",
+					(apr_fn)q6asm_srvc_callback,
+					0x0FFFFFFFF, &this_mmap);
+		if (this_mmap.apr == NULL) {
+			pr_debug("%s: Unable to register APR ASM common port\n",
+			 __func__);
+			goto fail;
+		}
+	}
+	atomic_inc(&this_mmap.ref_cnt);
+
+	return this_mmap.apr;
+fail:
+	return NULL;
+}
+
+struct audio_client *q6asm_audio_client_alloc(app_cb cb, void *priv)
+{
+	struct audio_client *ac;
+	int n;
+	int lcnt = 0;
+	int rc = 0;
+
+	ac = kzalloc(sizeof(struct audio_client), GFP_KERNEL);
+	if (!ac)
+		return NULL;
+
+	mutex_lock(&session_lock);
+	n = q6asm_session_alloc(ac);
+	if (n <= 0) {
+		pr_err("%s: ASM Session alloc fail n=%d\n", __func__, n);
+		mutex_unlock(&session_lock);
+		goto fail_session;
+	}
+	ac->session = n;
+	ac->cb = cb;
+	ac->path_delay = UINT_MAX;
+	ac->priv = priv;
+	ac->io_mode = SYNC_IO_MODE;
+	ac->perf_mode = LEGACY_PCM_MODE;
+	ac->fptr_cache_ops = NULL;
+	/* DSP expects stream id from 1 */
+	ac->stream_id = 1;
+	ac->apr = apr_register("ADSP", "ASM",
+			(apr_fn)q6asm_callback,
+			((ac->session) << 8 | 0x0001),
+			ac);
+
+	if (ac->apr == NULL) {
+		pr_err("%s: Registration with APR failed\n", __func__);
+		mutex_unlock(&session_lock);
+		goto fail_apr1;
+	}
+	ac->apr2 = apr_register("ADSP", "ASM",
+			(apr_fn)q6asm_callback,
+			((ac->session) << 8 | 0x0002),
+			ac);
+
+	if (ac->apr2 == NULL) {
+		pr_err("%s: Registration with APR-2 failed\n", __func__);
+		mutex_unlock(&session_lock);
+		goto fail_apr2;
+	}
+
+	rtac_set_asm_handle(n, ac->apr);
+
+	pr_debug("%s: Registering the common port with APR\n", __func__);
+	ac->mmap_apr = q6asm_mmap_apr_reg();
+	if (ac->mmap_apr == NULL) {
+		mutex_unlock(&session_lock);
+		goto fail_mmap;
+	}
+
+	init_waitqueue_head(&ac->cmd_wait);
+	init_waitqueue_head(&ac->time_wait);
+	init_waitqueue_head(&ac->mem_wait);
+	atomic_set(&ac->time_flag, 1);
+	atomic_set(&ac->reset, 0);
+	INIT_LIST_HEAD(&ac->port[0].mem_map_handle);
+	INIT_LIST_HEAD(&ac->port[1].mem_map_handle);
+	pr_debug("%s: mem_map_handle list init'ed\n", __func__);
+	mutex_init(&ac->cmd_lock);
+	for (lcnt = 0; lcnt <= OUT; lcnt++) {
+		mutex_init(&ac->port[lcnt].lock);
+		spin_lock_init(&ac->port[lcnt].dsp_lock);
+	}
+	atomic_set(&ac->cmd_state, 0);
+	atomic_set(&ac->mem_state, 0);
+
+	rc = send_asm_custom_topology(ac);
+	if (rc < 0) {
+		mutex_unlock(&session_lock);
+		goto fail_mmap;
+	}
+
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+	mutex_unlock(&session_lock);
+
+	return ac;
+fail_mmap:
+	apr_deregister(ac->apr2);
+fail_apr2:
+	apr_deregister(ac->apr);
+fail_apr1:
+	q6asm_session_free(ac);
+fail_session:
+	kfree(ac);
+	return NULL;
+}
+
+struct audio_client *q6asm_get_audio_client(int session_id)
+{
+	if (session_id == ASM_CONTROL_SESSION)
+		return &common_client;
+
+	if ((session_id <= 0) || (session_id > ASM_ACTIVE_STREAMS_ALLOWED)) {
+		pr_err("%s: invalid session: %d\n", __func__, session_id);
+		goto err;
+	}
+
+	if (!session[session_id]) {
+		pr_err("%s: session not active: %d\n", __func__, session_id);
+		goto err;
+	}
+	return session[session_id];
+err:
+	return NULL;
+}
+
+int q6asm_audio_client_buf_alloc(unsigned int dir,
+			struct audio_client *ac,
+			unsigned int bufsz,
+			uint32_t bufcnt)
+{
+	int cnt = 0;
+	int rc = 0;
+	struct audio_buffer *buf;
+	size_t len;
+
+	if (!(ac) || !(bufsz) || ((dir != IN) && (dir != OUT))) {
+		pr_err("%s: ac %pK bufsz %d dir %d\n", __func__, ac, bufsz,
+			dir);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n", __func__, ac->session,
+		bufsz, bufcnt);
+
+	if (ac->session <= 0 || ac->session > 8) {
+		pr_err("%s: Session ID is invalid, session = %d\n", __func__,
+			ac->session);
+		goto fail;
+	}
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		if (ac->port[dir].buf) {
+			pr_debug("%s: buffer already allocated\n", __func__);
+			return 0;
+		}
+		mutex_lock(&ac->cmd_lock);
+		if (bufcnt > (U32_MAX/sizeof(struct audio_buffer))) {
+			pr_err("%s: Buffer size overflows", __func__);
+			mutex_unlock(&ac->cmd_lock);
+			goto fail;
+		}
+		buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+				GFP_KERNEL);
+
+		if (!buf) {
+			mutex_unlock(&ac->cmd_lock);
+			goto fail;
+		}
+
+		ac->port[dir].buf = buf;
+
+		while (cnt < bufcnt) {
+			if (bufsz > 0) {
+				if (!buf[cnt].data) {
+					rc = msm_audio_ion_alloc("asm_client",
+					&buf[cnt].client, &buf[cnt].handle,
+					      bufsz,
+					      (ion_phys_addr_t *)&buf[cnt].phys,
+					      &len,
+					      &buf[cnt].data);
+					if (rc) {
+						pr_err("%s: ION Get Physical for AUDIO failed, rc = %d\n",
+							__func__, rc);
+						mutex_unlock(&ac->cmd_lock);
+					goto fail;
+					}
+
+					buf[cnt].used = 1;
+					buf[cnt].size = bufsz;
+					buf[cnt].actual_size = bufsz;
+					pr_debug("%s: data[%pK]phys[%pK][%pK]\n",
+						__func__,
+					   buf[cnt].data,
+					   &buf[cnt].phys,
+					   &buf[cnt].phys);
+					cnt++;
+				}
+			}
+		}
+		ac->port[dir].max_buf_cnt = cnt;
+
+		mutex_unlock(&ac->cmd_lock);
+		rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 0);
+		if (rc < 0) {
+			pr_err("%s: CMD Memory_map_regions failed %d for size %d\n",
+				__func__, rc, bufsz);
+			goto fail;
+		}
+	}
+	return 0;
+fail:
+	q6asm_audio_client_buf_free(dir, ac);
+	return -EINVAL;
+}
+
+int q6asm_audio_client_buf_alloc_contiguous(unsigned int dir,
+			struct audio_client *ac,
+			unsigned int bufsz,
+			unsigned int bufcnt)
+{
+	int cnt = 0;
+	int rc = 0;
+	struct audio_buffer *buf;
+	size_t len;
+	int bytes_to_alloc;
+
+	if (!(ac) || ((dir != IN) && (dir != OUT))) {
+		pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: session[%d]bufsz[%d]bufcnt[%d]\n",
+			__func__, ac->session,
+			bufsz, bufcnt);
+
+	if (ac->session <= 0 || ac->session > 8) {
+		pr_err("%s: Session ID is invalid, session = %d\n", __func__,
+			ac->session);
+		goto fail;
+	}
+
+	if (ac->port[dir].buf) {
+		pr_err("%s: buffer already allocated\n", __func__);
+		return 0;
+	}
+	mutex_lock(&ac->cmd_lock);
+	buf = kzalloc(((sizeof(struct audio_buffer))*bufcnt),
+			GFP_KERNEL);
+
+	if (!buf) {
+		pr_err("%s: buffer allocation failed\n", __func__);
+		mutex_unlock(&ac->cmd_lock);
+		goto fail;
+	}
+
+	ac->port[dir].buf = buf;
+
+	/* check for integer overflow */
+	if ((bufcnt > 0) && ((INT_MAX / bufcnt) < bufsz)) {
+		pr_err("%s: integer overflow\n", __func__);
+		mutex_unlock(&ac->cmd_lock);
+		goto fail;
+	}
+	bytes_to_alloc = bufsz * bufcnt;
+
+	/* The size to allocate should be multiple of 4K bytes */
+	bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc);
+
+	rc = msm_audio_ion_alloc("asm_client", &buf[0].client, &buf[0].handle,
+		bytes_to_alloc,
+		(ion_phys_addr_t *)&buf[0].phys, &len,
+		&buf[0].data);
+	if (rc) {
+		pr_err("%s: Audio ION alloc is failed, rc = %d\n",
+			__func__, rc);
+		mutex_unlock(&ac->cmd_lock);
+		goto fail;
+	}
+
+	buf[0].used = dir ^ 1;
+	buf[0].size = bufsz;
+	buf[0].actual_size = bufsz;
+	cnt = 1;
+	while (cnt < bufcnt) {
+		if (bufsz > 0) {
+			buf[cnt].data =  buf[0].data + (cnt * bufsz);
+			buf[cnt].phys =  buf[0].phys + (cnt * bufsz);
+			if (!buf[cnt].data) {
+				pr_err("%s: Buf alloc failed\n",
+							__func__);
+				mutex_unlock(&ac->cmd_lock);
+				goto fail;
+			}
+			buf[cnt].used = dir ^ 1;
+			buf[cnt].size = bufsz;
+			buf[cnt].actual_size = bufsz;
+			pr_debug("%s: data[%pK]phys[%pK][%pK]\n",
+				__func__,
+				buf[cnt].data,
+				&buf[cnt].phys,
+				&buf[cnt].phys);
+		}
+		cnt++;
+	}
+	ac->port[dir].max_buf_cnt = cnt;
+	mutex_unlock(&ac->cmd_lock);
+	rc = q6asm_memory_map_regions(ac, dir, bufsz, cnt, 1);
+	if (rc < 0) {
+		pr_err("%s: CMD Memory_map_regions failed %d for size %d\n",
+			__func__, rc, bufsz);
+		goto fail;
+	}
+	return 0;
+fail:
+	q6asm_audio_client_buf_free_contiguous(dir, ac);
+	return -EINVAL;
+}
+
+static int32_t q6asm_srvc_callback(struct apr_client_data *data, void *priv)
+{
+	uint32_t dir = 0;
+	uint32_t i = IN;
+	uint32_t *payload;
+	unsigned long dsp_flags;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+	union asm_token_struct asm_token;
+
+	struct audio_client *ac = NULL;
+	struct audio_port_data *port;
+
+	if (!data) {
+		pr_err("%s: Invalid CB\n", __func__);
+		return 0;
+	}
+
+	payload = data->payload;
+
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
+				__func__,
+				data->reset_event,
+				data->reset_proc,
+				this_mmap.apr);
+		atomic_set(&this_mmap.ref_cnt, 0);
+		apr_reset(this_mmap.apr);
+		this_mmap.apr = NULL;
+		for (; i <= OUT; i++) {
+			list_for_each_safe(ptr, next,
+				&common_client.port[i].mem_map_handle) {
+				buf_node = list_entry(ptr,
+						struct asm_buffer_node,
+						list);
+				if (buf_node->buf_phys_addr ==
+				common_client.port[i].buf->phys) {
+					list_del(&buf_node->list);
+					kfree(buf_node);
+				}
+			}
+			pr_debug("%s: Clearing custom topology\n", __func__);
+		}
+		this_mmap.apr = NULL;
+
+		cal_utils_clear_cal_block_q6maps(ASM_MAX_CAL_TYPES, cal_data);
+		common_client.mmap_apr = NULL;
+		mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+		set_custom_topology = 1;
+		mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+		topology_map_handle = 0;
+		rtac_clear_mapping(ASM_RTAC_CAL);
+		return 0;
+	}
+	asm_token.token = data->token;
+	ac = q6asm_get_audio_client(asm_token._token.session_id);
+	dir = q6asm_get_flag_from_token(&asm_token, ASM_DIRECTION_OFFSET);
+
+	if (!ac) {
+		pr_debug("%s: session[%d] already freed\n",
+			 __func__, asm_token._token.session_id);
+		return 0;
+	}
+
+	if (data->payload_size > sizeof(int)) {
+		pr_debug("%s:ptr0[0x%x]ptr1[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
+			__func__, payload[0], payload[1], data->opcode,
+			data->token, data->payload_size, data->src_port,
+			data->dest_port, asm_token._token.session_id, dir);
+		pr_debug("%s:Payload = [0x%x] status[0x%x]\n",
+			__func__, payload[0], payload[1]);
+	} else if (data->payload_size == sizeof(int)) {
+		pr_debug("%s:ptr0[0x%x]opcode[0x%x] token[0x%x]payload_s[%d] src[%d] dest[%d]sid[%d]dir[%d]\n",
+			__func__, payload[0], data->opcode,
+			data->token, data->payload_size, data->src_port,
+			data->dest_port, asm_token._token.session_id, dir);
+		pr_debug("%s:Payload = [0x%x]\n",
+			__func__, payload[0]);
+	}
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		switch (payload[0]) {
+		case ASM_CMD_SHARED_MEM_MAP_REGIONS:
+		case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:
+		case ASM_CMD_ADD_TOPOLOGIES:
+			if (payload[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x sid:%d\n",
+				       __func__, payload[0], payload[1],
+				       asm_token._token.session_id);
+				if (payload[0] ==
+				    ASM_CMD_SHARED_MEM_UNMAP_REGIONS)
+					atomic_set(&ac->unmap_cb_success, 0);
+
+				atomic_set(&ac->mem_state, payload[1]);
+				wake_up(&ac->mem_wait);
+			} else {
+				if (payload[0] ==
+				    ASM_CMD_SHARED_MEM_UNMAP_REGIONS)
+					atomic_set(&ac->unmap_cb_success, 1);
+			}
+
+			if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
+				wake_up(&ac->mem_wait);
+			dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x]\n",
+					__func__, payload[0], payload[1]);
+			break;
+		default:
+			pr_debug("%s: command[0x%x] not expecting rsp\n",
+						__func__, payload[0]);
+			break;
+		}
+		return 0;
+	}
+
+	port = &ac->port[dir];
+
+	switch (data->opcode) {
+	case ASM_CMDRSP_SHARED_MEM_MAP_REGIONS:{
+		pr_debug("%s:PL#0[0x%x] dir=0x%x s_id=0x%x\n",
+		       __func__, payload[0], dir, asm_token._token.session_id);
+		spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+		if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1) {
+			ac->port[dir].tmp_hdl = payload[0];
+			wake_up(&ac->mem_wait);
+		}
+		spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+		break;
+	}
+	case ASM_CMD_SHARED_MEM_UNMAP_REGIONS:{
+		pr_debug("%s: PL#0[0x%x]PL#1 [0x%x]\n",
+					__func__, payload[0], payload[1]);
+		spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+		if (atomic_cmpxchg(&ac->mem_state, -1, 0) == -1)
+			wake_up(&ac->mem_wait);
+		spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+		break;
+	}
+	default:
+		pr_debug("%s: command[0x%x]success [0x%x]\n",
+					__func__, payload[0], payload[1]);
+	}
+	if (ac->cb)
+		ac->cb(data->opcode, data->token,
+			data->payload, ac->priv);
+	return 0;
+}
+
+static void q6asm_process_mtmx_get_param_rsp(struct audio_client *ac,
+				struct asm_mtmx_strtr_get_params_cmdrsp *cmdrsp)
+{
+	struct asm_session_mtmx_strtr_param_session_time_v3_t *time;
+
+	if (cmdrsp->err_code) {
+		dev_err_ratelimited(ac->dev,
+				    "%s: err=%x, mod_id=%x, param_id=%x\n",
+				    __func__, cmdrsp->err_code,
+				    cmdrsp->param_info.module_id,
+				    cmdrsp->param_info.param_id);
+		return;
+	}
+	dev_dbg_ratelimited(ac->dev,
+			    "%s: mod_id=%x, param_id=%x\n", __func__,
+			    cmdrsp->param_info.module_id,
+			    cmdrsp->param_info.param_id);
+
+	switch (cmdrsp->param_info.module_id) {
+	case ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC:
+		switch (cmdrsp->param_info.param_id) {
+		case ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3:
+			time = &cmdrsp->param_data.session_time;
+			dev_vdbg(ac->dev, "%s: GET_TIME_V3, time_lsw=%x, time_msw=%x\n",
+				 __func__, time->session_time_lsw,
+				 time->session_time_msw);
+			ac->time_stamp = (uint64_t)(((uint64_t)
+					 time->session_time_msw << 32) |
+					 time->session_time_lsw);
+			if (time->flags &
+			    ASM_SESSION_MTMX_STRTR_PARAM_STIME_TSTMP_FLG_BMASK)
+				dev_warn_ratelimited(ac->dev,
+						     "%s: recv inval tstmp\n",
+						     __func__);
+			if (atomic_cmpxchg(&ac->time_flag, 1, 0))
+				wake_up(&ac->time_wait);
+
+			break;
+		default:
+			dev_err(ac->dev, "%s: unexpected param_id %x\n",
+				__func__, cmdrsp->param_info.param_id);
+			break;
+		}
+		break;
+	default:
+		dev_err(ac->dev, "%s: unexpected mod_id %x\n",  __func__,
+			cmdrsp->param_info.module_id);
+		break;
+	}
+}
+
+static int32_t q6asm_callback(struct apr_client_data *data, void *priv)
+{
+	int i = 0;
+	struct audio_client *ac = (struct audio_client *)priv;
+	unsigned long dsp_flags;
+	uint32_t *payload;
+	uint32_t wakeup_flag = 1;
+	int32_t  ret = 0;
+	union asm_token_struct asm_token;
+	uint8_t buf_index;
+
+	if (ac == NULL) {
+		pr_err("%s: ac NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (data == NULL) {
+		pr_err("%s: data NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (!q6asm_is_valid_audio_client(ac)) {
+		pr_err("%s: audio client pointer is invalid, ac = %pK\n",
+				__func__, ac);
+		return -EINVAL;
+	}
+
+	if (ac->session <= 0 || ac->session > 8) {
+		pr_err("%s: Session ID is invalid, session = %d\n", __func__,
+			ac->session);
+		return -EINVAL;
+	}
+
+	payload = data->payload;
+	asm_token.token = data->token;
+	if (q6asm_get_flag_from_token(&asm_token, ASM_CMD_NO_WAIT_OFFSET)) {
+		pr_debug("%s: No wait command opcode[0x%x] cmd_opcode:%x\n",
+			 __func__, data->opcode, payload ? payload[0] : 0);
+		wakeup_flag = 0;
+	}
+
+	if (data->opcode == RESET_EVENTS) {
+		mutex_lock(&ac->cmd_lock);
+		atomic_set(&ac->reset, 1);
+		if (ac->apr == NULL)
+			ac->apr = ac->apr2;
+		pr_debug("%s: Reset event is received: %d %d apr[%pK]\n",
+			__func__,
+			data->reset_event, data->reset_proc, ac->apr);
+		if (ac->cb)
+			ac->cb(data->opcode, data->token,
+				(uint32_t *)data->payload, ac->priv);
+		apr_reset(ac->apr);
+		ac->apr = NULL;
+		atomic_set(&ac->time_flag, 0);
+		atomic_set(&ac->cmd_state, 0);
+		atomic_set(&ac->mem_state, 0);
+		wake_up(&ac->time_wait);
+		wake_up(&ac->cmd_wait);
+		wake_up(&ac->mem_wait);
+		mutex_unlock(&ac->cmd_lock);
+		return 0;
+	}
+
+	dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x] token[0x%x]payload_size[%d] src[%d] dest[%d]\n",
+		 __func__,
+		ac->session, data->opcode,
+		data->token, data->payload_size, data->src_port,
+		data->dest_port);
+	if ((data->opcode != ASM_DATA_EVENT_RENDERED_EOS) &&
+	    (data->opcode != ASM_DATA_EVENT_EOS) &&
+	    (data->opcode != ASM_SESSION_EVENT_RX_UNDERFLOW)) {
+		if (payload == NULL) {
+			pr_err("%s: payload is null\n", __func__);
+			return -EINVAL;
+		}
+		dev_vdbg(ac->dev, "%s: Payload = [0x%x] status[0x%x] opcode 0x%x\n",
+			__func__, payload[0], payload[1], data->opcode);
+	}
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		switch (payload[0]) {
+		case ASM_STREAM_CMD_SET_PP_PARAMS_V2:
+			if (rtac_make_asm_callback(ac->session, payload,
+					data->payload_size))
+				break;
+		case ASM_SESSION_CMD_PAUSE:
+		case ASM_SESSION_CMD_SUSPEND:
+		case ASM_DATA_CMD_EOS:
+		case ASM_STREAM_CMD_CLOSE:
+		case ASM_STREAM_CMD_FLUSH:
+		case ASM_SESSION_CMD_RUN_V2:
+		case ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS:
+		case ASM_STREAM_CMD_FLUSH_READBUFS:
+		pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] src %d dest %d\n",
+			__func__, ac->session, data->opcode, data->token,
+			payload[0], data->src_port, data->dest_port);
+		ret = q6asm_is_valid_session(data, priv);
+		if (ret != 0) {
+			pr_err("%s: session invalid %d\n", __func__, ret);
+			return ret;
+		}
+		case ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2:
+		case ASM_STREAM_CMD_OPEN_READ_V3:
+		case ASM_STREAM_CMD_OPEN_WRITE_V3:
+		case ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE:
+		case ASM_STREAM_CMD_OPEN_PUSH_MODE_READ:
+		case ASM_STREAM_CMD_OPEN_READWRITE_V2:
+		case ASM_STREAM_CMD_OPEN_LOOPBACK_V2:
+		case ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2:
+		case ASM_STREAM_CMD_SET_ENCDEC_PARAM:
+		case ASM_DATA_CMD_REMOVE_INITIAL_SILENCE:
+		case ASM_DATA_CMD_REMOVE_TRAILING_SILENCE:
+		case ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS:
+		case ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED:
+			pr_debug("%s: session %d opcode 0x%x token 0x%x Payload = [0x%x] stat 0x%x src %d dest %d\n",
+				__func__, ac->session,
+				data->opcode, data->token,
+				payload[0], payload[1],
+				data->src_port, data->dest_port);
+			if (payload[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, payload[0], payload[1]);
+				if (wakeup_flag) {
+					atomic_set(&ac->cmd_state, payload[1]);
+					wake_up(&ac->cmd_wait);
+				}
+				return 0;
+			}
+			if (atomic_read(&ac->cmd_state) && wakeup_flag) {
+				atomic_set(&ac->cmd_state, 0);
+				wake_up(&ac->cmd_wait);
+			}
+			if (ac->cb)
+				ac->cb(data->opcode, data->token,
+					(uint32_t *)data->payload, ac->priv);
+			break;
+		case ASM_CMD_ADD_TOPOLOGIES:
+			pr_debug("%s:Payload = [0x%x]stat[0x%x]\n",
+				 __func__, payload[0], payload[1]);
+			if (payload[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					 __func__, payload[0], payload[1]);
+				if (wakeup_flag) {
+					atomic_set(&ac->mem_state, payload[1]);
+					wake_up(&ac->mem_wait);
+				}
+				return 0;
+			}
+			if (atomic_read(&ac->mem_state) && wakeup_flag) {
+				atomic_set(&ac->mem_state, 0);
+				wake_up(&ac->mem_wait);
+			}
+			if (ac->cb)
+				ac->cb(data->opcode, data->token,
+					(uint32_t *)data->payload, ac->priv);
+			break;
+		case ASM_DATA_EVENT_WATERMARK: {
+			pr_debug("%s: Watermark opcode[0x%x] status[0x%x]",
+				 __func__, payload[0], payload[1]);
+			break;
+		}
+		case ASM_STREAM_CMD_GET_PP_PARAMS_V2:
+			pr_debug("%s: ASM_STREAM_CMD_GET_PP_PARAMS_V2 session %d opcode 0x%x token 0x%x src %d dest %d\n",
+				__func__, ac->session,
+				data->opcode, data->token,
+				data->src_port, data->dest_port);
+			/* Should only come here if there is an APR */
+			/* error or malformed APR packet. Otherwise */
+			/* response will be returned as */
+			/* ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 */
+			if (payload[1] != 0) {
+				pr_err("%s: ASM get param error = %d, resuming\n",
+					__func__, payload[1]);
+				rtac_make_asm_callback(ac->session, payload,
+							data->payload_size);
+			}
+			break;
+		default:
+			pr_debug("%s: command[0x%x] not expecting rsp\n",
+							__func__, payload[0]);
+			break;
+		}
+		return 0;
+	}
+
+	switch (data->opcode) {
+	case ASM_DATA_EVENT_WRITE_DONE_V2:{
+		struct audio_port_data *port = &ac->port[IN];
+
+		dev_vdbg(ac->dev, "%s: Rxed opcode[0x%x] status[0x%x] token[%d]",
+				__func__, payload[0], payload[1],
+				data->token);
+		if (ac->io_mode & SYNC_IO_MODE) {
+			if (port->buf == NULL) {
+				pr_err("%s: Unexpected Write Done\n",
+								__func__);
+				return -EINVAL;
+			}
+			spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+			buf_index = asm_token._token.buf_index;
+			if (lower_32_bits(port->buf[buf_index].phys) !=
+			payload[0] ||
+			msm_audio_populate_upper_32_bits(
+				port->buf[buf_index].phys) !=	payload[1]) {
+				pr_debug("%s: Expected addr %pK\n",
+				__func__, &port->buf[buf_index].phys);
+				pr_err("%s: rxedl[0x%x] rxedu [0x%x]\n",
+					__func__, payload[0], payload[1]);
+				spin_unlock_irqrestore(&port->dsp_lock,
+								dsp_flags);
+				return -EINVAL;
+			}
+			port->buf[buf_index].used = 1;
+			spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+
+			config_debug_fs_write_cb();
+
+			for (i = 0; i < port->max_buf_cnt; i++)
+				dev_vdbg(ac->dev, "%s %d\n",
+					__func__, port->buf[i].used);
+
+		}
+		break;
+	}
+	case ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2:
+		pr_debug("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 session %d opcode 0x%x token 0x%x src %d dest %d\n",
+				__func__, ac->session, data->opcode,
+				data->token,
+				data->src_port, data->dest_port);
+		if (payload[0] != 0) {
+			pr_err("%s: ASM_STREAM_CMDRSP_GET_PP_PARAMS_V2 returned error = 0x%x\n",
+				__func__, payload[0]);
+		} else if (generic_get_data) {
+			generic_get_data->valid = 1;
+			if (generic_get_data->is_inband) {
+				pr_debug("%s: payload[1] = 0x%x, payload[2]=0x%x, payload[3]=0x%x\n",
+				  __func__, payload[1], payload[2], payload[3]);
+				generic_get_data->size_in_ints = payload[3]>>2;
+				for (i = 0; i < payload[3]>>2; i++) {
+					generic_get_data->ints[i] =
+								   payload[4+i];
+					pr_debug("%s: ASM callback val %i = %i\n",
+						 __func__, i, payload[4+i]);
+				}
+				pr_debug("%s: callback size in ints = %i\n",
+					 __func__,
+					generic_get_data->size_in_ints);
+			}
+			if (atomic_read(&ac->cmd_state) && wakeup_flag) {
+				atomic_set(&ac->cmd_state, 0);
+				wake_up(&ac->cmd_wait);
+			}
+			break;
+		}
+		rtac_make_asm_callback(ac->session, payload,
+			data->payload_size);
+		break;
+	case ASM_DATA_EVENT_READ_DONE_V2:{
+
+		struct audio_port_data *port = &ac->port[OUT];
+
+		config_debug_fs_read_cb();
+
+		dev_vdbg(ac->dev, "%s: ReadDone: status=%d buff_add=0x%x act_size=%d offset=%d\n",
+				__func__, payload[READDONE_IDX_STATUS],
+				payload[READDONE_IDX_BUFADD_LSW],
+				payload[READDONE_IDX_SIZE],
+				payload[READDONE_IDX_OFFSET]);
+
+		dev_vdbg(ac->dev, "%s: ReadDone:msw_ts=%d lsw_ts=%d memmap_hdl=0x%x flags=%d id=%d num=%d\n",
+				__func__, payload[READDONE_IDX_MSW_TS],
+				payload[READDONE_IDX_LSW_TS],
+				payload[READDONE_IDX_MEMMAP_HDL],
+				payload[READDONE_IDX_FLAGS],
+				payload[READDONE_IDX_SEQ_ID],
+				payload[READDONE_IDX_NUMFRAMES]);
+
+		if (ac->io_mode & SYNC_IO_MODE) {
+			if (port->buf == NULL) {
+				pr_err("%s: Unexpected Write Done\n", __func__);
+				return -EINVAL;
+			}
+			spin_lock_irqsave(&port->dsp_lock, dsp_flags);
+			buf_index = asm_token._token.buf_index;
+			port->buf[buf_index].used = 0;
+			if (lower_32_bits(port->buf[buf_index].phys) !=
+			payload[READDONE_IDX_BUFADD_LSW] ||
+			msm_audio_populate_upper_32_bits(
+				port->buf[buf_index].phys) !=
+					payload[READDONE_IDX_BUFADD_MSW]) {
+				dev_vdbg(ac->dev, "%s: Expected addr %pK\n",
+					__func__, &port->buf[buf_index].phys);
+				pr_err("%s: rxedl[0x%x] rxedu[0x%x]\n",
+					__func__,
+				payload[READDONE_IDX_BUFADD_LSW],
+				payload[READDONE_IDX_BUFADD_MSW]);
+				spin_unlock_irqrestore(&port->dsp_lock,
+							dsp_flags);
+				break;
+			}
+			port->buf[buf_index].actual_size =
+				payload[READDONE_IDX_SIZE];
+			spin_unlock_irqrestore(&port->dsp_lock, dsp_flags);
+		}
+		break;
+	}
+	case ASM_DATA_EVENT_EOS:
+	case ASM_DATA_EVENT_RENDERED_EOS:
+		pr_debug("%s: EOS ACK received: rxed session %d opcode 0x%x token 0x%x src %d dest %d\n",
+				__func__, ac->session,
+				data->opcode, data->token,
+				data->src_port, data->dest_port);
+		break;
+	case ASM_SESSION_EVENTX_OVERFLOW:
+		pr_debug("%s: ASM_SESSION_EVENTX_OVERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n",
+				__func__, ac->session,
+				data->opcode, data->token,
+				data->src_port, data->dest_port);
+		break;
+	case ASM_SESSION_EVENT_RX_UNDERFLOW:
+		pr_debug("%s: ASM_SESSION_EVENT_RX_UNDERFLOW session %d opcode 0x%x token 0x%x src %d dest %d\n",
+				__func__, ac->session,
+				data->opcode, data->token,
+				data->src_port, data->dest_port);
+		break;
+	case ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3:
+		dev_vdbg(ac->dev, "%s: ASM_SESSION_CMDRSP_GET_SESSIONTIME_V3, payload[0] = %d, payload[1] = %d, payload[2] = %d\n",
+				 __func__,
+				 payload[0], payload[1], payload[2]);
+		ac->time_stamp = (uint64_t)(((uint64_t)payload[2] << 32) |
+				payload[1]);
+		if (atomic_cmpxchg(&ac->time_flag, 1, 0))
+			wake_up(&ac->time_wait);
+		break;
+	case ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY:
+	case ASM_DATA_EVENT_ENC_SR_CM_CHANGE_NOTIFY:
+		pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY session %d opcode 0x%x token 0x%x src %d dest %d\n",
+				__func__, ac->session,
+				data->opcode, data->token,
+				data->src_port, data->dest_port);
+		pr_debug("%s: ASM_DATA_EVENT_SR_CM_CHANGE_NOTIFY, payload[0] = %d, payload[1] = %d, payload[2] = %d, payload[3] = %d\n",
+				 __func__,
+				payload[0], payload[1], payload[2],
+				payload[3]);
+		break;
+	case ASM_SESSION_CMDRSP_GET_MTMX_STRTR_PARAMS_V2:
+		q6asm_process_mtmx_get_param_rsp(ac, (void *) payload);
+		break;
+	case ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2:
+		pr_debug("%s: ASM_SESSION_CMDRSP_GET_PATH_DELAY_V2 session %d status 0x%x msw %u lsw %u\n",
+				__func__, ac->session, payload[0], payload[2],
+				payload[1]);
+		if (payload[0] == 0) {
+			atomic_set(&ac->cmd_state, 0);
+			/* ignore msw, as a delay that large shouldn't happen */
+			ac->path_delay = payload[1];
+		} else {
+			atomic_set(&ac->cmd_state, payload[0]);
+			ac->path_delay = UINT_MAX;
+		}
+		wake_up(&ac->cmd_wait);
+		break;
+	}
+	if (ac->cb)
+		ac->cb(data->opcode, data->token,
+			data->payload, ac->priv);
+
+	return 0;
+}
+
+void *q6asm_is_cpu_buf_avail(int dir, struct audio_client *ac, uint32_t *size,
+				uint32_t *index)
+{
+	void *data;
+	unsigned char idx;
+	struct audio_port_data *port;
+
+	if (!ac || ((dir != IN) && (dir != OUT))) {
+		pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+		return NULL;
+	}
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[dir];
+
+		mutex_lock(&port->lock);
+		idx = port->cpu_buf;
+		if (port->buf == NULL) {
+			pr_err("%s: Buffer pointer null\n", __func__);
+			mutex_unlock(&port->lock);
+			return NULL;
+		}
+		/* dir 0: used = 0 means buf in use
+		 * dir 1: used = 1 means buf in use
+		 */
+		if (port->buf[idx].used == dir) {
+			/* To make it more robust, we could loop and get the
+			 * next avail buf, its risky though
+			 */
+			pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n",
+			 __func__, idx, dir);
+			mutex_unlock(&port->lock);
+			return NULL;
+		}
+		*size = port->buf[idx].actual_size;
+		*index = port->cpu_buf;
+		data = port->buf[idx].data;
+		dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n",
+						__func__,
+						ac->session,
+						port->cpu_buf,
+						data, *size);
+		/* By default increase the cpu_buf cnt
+		 * user accesses this function,increase cpu
+		 * buf(to avoid another api)
+		 */
+		port->buf[idx].used = dir;
+		port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf,
+						   port->max_buf_cnt);
+		mutex_unlock(&port->lock);
+		return data;
+	}
+	return NULL;
+}
+
+int q6asm_cpu_buf_release(int dir, struct audio_client *ac)
+{
+	struct audio_port_data *port;
+	int ret = 0;
+	int idx;
+
+	if (!ac || ((dir != IN) && (dir != OUT))) {
+		pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[dir];
+		mutex_lock(&port->lock);
+		idx = port->cpu_buf;
+		if (port->cpu_buf == 0) {
+			port->cpu_buf = port->max_buf_cnt - 1;
+		} else if (port->cpu_buf < port->max_buf_cnt) {
+			port->cpu_buf = port->cpu_buf - 1;
+		} else {
+			pr_err("%s: buffer index(%d) out of range\n",
+			       __func__, port->cpu_buf);
+			ret = -EINVAL;
+			mutex_unlock(&port->lock);
+			goto exit;
+		}
+		port->buf[port->cpu_buf].used = dir ^ 1;
+		mutex_unlock(&port->lock);
+	}
+exit:
+	return ret;
+}
+
+void *q6asm_is_cpu_buf_avail_nolock(int dir, struct audio_client *ac,
+					uint32_t *size, uint32_t *index)
+{
+	void *data;
+	unsigned char idx;
+	struct audio_port_data *port;
+
+	if (!ac || ((dir != IN) && (dir != OUT))) {
+		pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+		return NULL;
+	}
+
+	port = &ac->port[dir];
+
+	idx = port->cpu_buf;
+	if (port->buf == NULL) {
+		pr_err("%s: Buffer pointer null\n", __func__);
+		return NULL;
+	}
+	/*
+	 * dir 0: used = 0 means buf in use
+	 * dir 1: used = 1 means buf in use
+	 */
+	if (port->buf[idx].used == dir) {
+		/*
+		 * To make it more robust, we could loop and get the
+		 * next avail buf, its risky though
+		 */
+		pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n",
+		 __func__, idx, dir);
+		return NULL;
+	}
+	*size = port->buf[idx].actual_size;
+	*index = port->cpu_buf;
+	data = port->buf[idx].data;
+	dev_vdbg(ac->dev, "%s: session[%d]index[%d] data[%pK]size[%d]\n",
+		__func__, ac->session, port->cpu_buf,
+		data, *size);
+	/*
+	 * By default increase the cpu_buf cnt
+	 * user accesses this function,increase cpu
+	 * buf(to avoid another api)
+	 */
+	port->buf[idx].used = dir;
+	port->cpu_buf = q6asm_get_next_buf(ac, port->cpu_buf,
+					   port->max_buf_cnt);
+	return data;
+}
+
+int q6asm_is_dsp_buf_avail(int dir, struct audio_client *ac)
+{
+	int ret = -1;
+	struct audio_port_data *port;
+	uint32_t idx;
+
+	if (!ac || (dir != OUT)) {
+		pr_err("%s: ac %pK dir %d\n", __func__, ac, dir);
+		return ret;
+	}
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[dir];
+
+		mutex_lock(&port->lock);
+		idx = port->dsp_buf;
+
+		if (port->buf[idx].used == (dir ^ 1)) {
+			/* To make it more robust, we could loop and get the
+			 * next avail buf, its risky though
+			 */
+			pr_err("%s: Next buf idx[0x%x] not available, dir[%d]\n",
+				__func__, idx, dir);
+			mutex_unlock(&port->lock);
+			return ret;
+		}
+		dev_vdbg(ac->dev, "%s: session[%d]dsp_buf=%d cpu_buf=%d\n",
+			__func__,
+			ac->session, port->dsp_buf, port->cpu_buf);
+		ret = ((port->dsp_buf != port->cpu_buf) ? 0 : -1);
+		mutex_unlock(&port->lock);
+	}
+	return ret;
+}
+
+static void __q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+			uint32_t pkt_size, uint32_t cmd_flg, uint32_t stream_id)
+{
+	dev_vdbg(ac->dev, "%s: pkt_size=%d cmd_flg=%d session=%d stream_id=%d\n",
+			__func__, pkt_size, cmd_flg, ac->session, stream_id);
+	mutex_lock(&ac->cmd_lock);
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL", __func__);
+		mutex_unlock(&ac->cmd_lock);
+		return;
+	}
+
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(sizeof(struct apr_hdr)),
+			APR_PKT_VER);
+	hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->dest_svc = APR_SVC_ASM;
+	hdr->dest_domain = APR_DOMAIN_ADSP;
+	hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+	hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+	if (cmd_flg)
+		q6asm_update_token(&hdr->token,
+				   ac->session,
+				   0, /* Stream ID is NA */
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+	hdr->pkt_size  = pkt_size;
+	mutex_unlock(&ac->cmd_lock);
+}
+
+static void q6asm_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+			uint32_t pkt_size, uint32_t cmd_flg)
+{
+	__q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, ac->stream_id);
+}
+
+static void q6asm_stream_add_hdr(struct audio_client *ac, struct apr_hdr *hdr,
+			uint32_t pkt_size, uint32_t cmd_flg, int32_t stream_id)
+{
+	__q6asm_add_hdr(ac, hdr, pkt_size, cmd_flg, stream_id);
+}
+
+static void __q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+				  uint32_t pkt_size, uint32_t cmd_flg,
+				  uint32_t stream_id, u8 no_wait_flag)
+{
+	dev_vdbg(ac->dev, "%s: pkt_size = %d, cmd_flg = %d, session = %d stream_id=%d\n",
+			__func__, pkt_size, cmd_flg, ac->session, stream_id);
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(sizeof(struct apr_hdr)),
+			APR_PKT_VER);
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR is NULL", __func__);
+		return;
+	}
+	hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->dest_svc = APR_SVC_ASM;
+	hdr->dest_domain = APR_DOMAIN_ADSP;
+	hdr->src_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+	hdr->dest_port = ((ac->session << 8) & 0xFF00) | (stream_id);
+	if (cmd_flg) {
+		q6asm_update_token(&hdr->token,
+				   ac->session,
+				   0, /* Stream ID is NA */
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   no_wait_flag);
+
+	}
+	hdr->pkt_size  = pkt_size;
+}
+
+static void q6asm_add_hdr_async(struct audio_client *ac, struct apr_hdr *hdr,
+				uint32_t pkt_size, uint32_t cmd_flg)
+{
+	__q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg,
+			      ac->stream_id, WAIT_CMD);
+}
+
+static void q6asm_stream_add_hdr_async(struct audio_client *ac,
+					struct apr_hdr *hdr, uint32_t pkt_size,
+					uint32_t cmd_flg, int32_t stream_id)
+{
+	__q6asm_add_hdr_async(ac, hdr, pkt_size, cmd_flg,
+			      stream_id, NO_WAIT_CMD);
+}
+
+static void q6asm_add_hdr_custom_topology(struct audio_client *ac,
+					  struct apr_hdr *hdr,
+					  uint32_t pkt_size)
+{
+	pr_debug("%s: pkt_size=%d session=%d\n",
+			__func__, pkt_size, ac->session);
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return;
+	}
+
+	mutex_lock(&ac->cmd_lock);
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(sizeof(struct apr_hdr)),
+			APR_PKT_VER);
+	hdr->src_svc = ((struct apr_svc *)ac->apr)->id;
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->dest_svc = APR_SVC_ASM;
+	hdr->dest_domain = APR_DOMAIN_ADSP;
+	hdr->src_port = ((ac->session << 8) & 0xFF00) | 0x01;
+	hdr->dest_port = 0;
+	q6asm_update_token(&hdr->token,
+			   ac->session,
+			   0, /* Stream ID is NA */
+			   0, /* Buffer index is NA */
+			   0, /* Direction flag is NA */
+			   WAIT_CMD);
+	hdr->pkt_size  = pkt_size;
+	mutex_unlock(&ac->cmd_lock);
+}
+
+static void q6asm_add_mmaphdr(struct audio_client *ac, struct apr_hdr *hdr,
+			u32 pkt_size, int dir)
+{
+	pr_debug("%s: pkt size=%d\n",
+		__func__, pkt_size);
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr->src_port = 0;
+	hdr->dest_port = 0;
+	q6asm_update_token(&hdr->token,
+			   ac->session,
+			   0, /* Stream ID is NA */
+			   0, /* Buffer index is NA */
+			   dir,
+			   WAIT_CMD);
+	hdr->pkt_size  = pkt_size;
+}
+
+static int __q6asm_open_read(struct audio_client *ac,
+			     uint32_t format, uint16_t bits_per_sample,
+			     uint32_t pcm_format_block_ver,
+			     bool ts_mode)
+{
+	int rc = 0x00;
+	struct asm_stream_cmd_open_read_v3 open;
+
+	config_debug_fs_reset_index();
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_READ_V3;
+	/* Stream prio : High, provide meta info with encoded frames */
+	open.src_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+
+	open.preprocopo_id = q6asm_get_asm_topology_cal();
+	if ((open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
+	    (open.preprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS))
+		open.preprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE;
+	open.bits_per_sample = bits_per_sample;
+	open.mode_flags = 0x0;
+
+	ac->topology = open.preprocopo_id;
+	ac->app_type = q6asm_get_asm_app_type_cal();
+	if (ac->perf_mode == LOW_LATENCY_PCM_MODE) {
+		open.mode_flags |= ASM_LOW_LATENCY_TX_STREAM_SESSION <<
+			ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ;
+	} else {
+		open.mode_flags |= ASM_LEGACY_STREAM_SESSION <<
+			ASM_SHIFT_STREAM_PERF_MODE_FLAG_IN_OPEN_READ;
+	}
+
+	switch (format) {
+	case FORMAT_LINEAR_PCM:
+		open.mode_flags |= 0x00;
+		open.enc_cfg_id = q6asm_get_pcm_format_id(pcm_format_block_ver);
+		if (ts_mode)
+			open.mode_flags |= ABSOLUTE_TIMESTAMP_ENABLE;
+		break;
+	case FORMAT_MPEG4_AAC:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2;
+		break;
+	case FORMAT_G711_ALAW_FS:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS;
+		break;
+	case FORMAT_G711_MLAW_FS:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS;
+		break;
+	case FORMAT_V13K:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS;
+		break;
+	case FORMAT_EVRC:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS;
+		break;
+	case FORMAT_AMRNB:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS;
+		break;
+	case FORMAT_AMRWB:
+		open.mode_flags |= BUFFER_META_ENABLE;
+		open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS;
+		break;
+	default:
+		pr_err("%s: Invalid format 0x%x\n",
+			__func__, format);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n",
+				__func__, open.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for open read\n",
+				__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	ac->io_mode |= TUN_READ_IO_MODE;
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_open_read(struct audio_client *ac,
+		uint32_t format)
+{
+	return __q6asm_open_read(ac, format, 16,
+				PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/,
+				false/*ts_mode*/);
+}
+
+int q6asm_open_read_v2(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample)
+{
+	return __q6asm_open_read(ac, format, bits_per_sample,
+				 PCM_MEDIA_FORMAT_V2 /*media fmt block ver*/,
+				 false/*ts_mode*/);
+}
+
+/*
+ * asm_open_read_v3 - Opens audio capture session
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ */
+int q6asm_open_read_v3(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample)
+{
+	return __q6asm_open_read(ac, format, bits_per_sample,
+				 PCM_MEDIA_FORMAT_V3/*media fmt block ver*/,
+				 false/*ts_mode*/);
+}
+EXPORT_SYMBOL(q6asm_open_read_v3);
+
+/*
+ * asm_open_read_v4 - Opens audio capture session
+ *
+ * @ac: Client session handle
+ * @format: encoder format
+ * @bits_per_sample: bit width of capture session
+ */
+int q6asm_open_read_v4(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample)
+{
+	return __q6asm_open_read(ac, format, bits_per_sample,
+				 PCM_MEDIA_FORMAT_V4 /*media fmt block ver*/,
+				 true/*ts_mode*/);
+}
+EXPORT_SYMBOL(q6asm_open_read_v4);
+
+int q6asm_open_write_compressed(struct audio_client *ac, uint32_t format,
+				uint32_t passthrough_flag)
+{
+	int rc = 0;
+	struct asm_stream_cmd_open_write_compressed open;
+
+	if (ac == NULL) {
+		pr_err("%s: ac[%pK] NULL\n",  __func__, ac);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (ac->apr == NULL) {
+		pr_err("%s: APR handle[%pK] NULL\n", __func__,  ac->apr);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	pr_debug("%s: session[%d] wr_format[0x%x]", __func__, ac->session,
+		format);
+
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_COMPRESSED;
+	atomic_set(&ac->cmd_state, -1);
+
+	switch (format) {
+	case FORMAT_AC3:
+		open.fmt_id = ASM_MEDIA_FMT_AC3;
+		break;
+	case FORMAT_EAC3:
+		open.fmt_id = ASM_MEDIA_FMT_EAC3;
+		break;
+	case FORMAT_DTS:
+		open.fmt_id = ASM_MEDIA_FMT_DTS;
+		break;
+	case FORMAT_DSD:
+		open.fmt_id = ASM_MEDIA_FMT_DSD;
+		break;
+	default:
+		pr_err("%s: Invalid format[%d]\n", __func__, format);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	/* Below flag indicates the DSP that Compressed audio input
+	 * stream is not IEC 61937 or IEC 60958 packetizied
+	 */
+	if (passthrough_flag == COMPRESSED_PASSTHROUGH ||
+		passthrough_flag == COMPRESSED_PASSTHROUGH_DSD) {
+		open.flags = 0x0;
+		pr_debug("%s: Flag 0 COMPRESSED_PASSTHROUGH\n", __func__);
+	} else if (passthrough_flag == COMPRESSED_PASSTHROUGH_CONVERT) {
+		open.flags = 0x8;
+		pr_debug("%s: Flag 8 - COMPRESSED_PASSTHROUGH_CONVERT\n",
+			 __func__);
+	} else {
+		pr_err("%s: Invalid passthrough type[%d]\n",
+			__func__, passthrough_flag);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n",
+			__func__, open.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for OPEN_WRITE_COMPR rc[%d]\n",
+			__func__, rc);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	return 0;
+
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_open_write(struct audio_client *ac, uint32_t format,
+			      uint16_t bits_per_sample, uint32_t stream_id,
+			      bool is_gapless_mode,
+			      uint32_t pcm_format_block_ver)
+{
+	int rc = 0x00;
+	struct asm_stream_cmd_open_write_v3 open;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_vdbg(ac->dev, "%s: session[%d] wr_format[0x%x]\n",
+		__func__, ac->session, format);
+
+	q6asm_stream_add_hdr(ac, &open.hdr, sizeof(open), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&open.hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+	dev_vdbg(ac->dev, "%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+			__func__, open.hdr.token, stream_id, ac->session);
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_WRITE_V3;
+	open.mode_flags = 0x00;
+	if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE)
+		open.mode_flags |= ASM_ULL_POST_PROCESSING_STREAM_SESSION;
+	else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE)
+		open.mode_flags |= ASM_ULTRA_LOW_LATENCY_STREAM_SESSION;
+	else if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+		open.mode_flags |= ASM_LOW_LATENCY_STREAM_SESSION;
+	else {
+		open.mode_flags |= ASM_LEGACY_STREAM_SESSION;
+		if (is_gapless_mode)
+			open.mode_flags |= 1 << ASM_SHIFT_GAPLESS_MODE_FLAG;
+	}
+
+	/* source endpoint : matrix */
+	open.sink_endpointype = ASM_END_POINT_DEVICE_MATRIX;
+	open.bits_per_sample = bits_per_sample;
+
+	open.postprocopo_id = q6asm_get_asm_topology_cal();
+	if ((ac->perf_mode != LEGACY_PCM_MODE) &&
+	    ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
+	     (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS)))
+		open.postprocopo_id = ASM_STREAM_POSTPROCOPO_ID_NONE;
+
+	/* For DTS EAGLE only, force 24 bit */
+	if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
+	     (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_PLUS))
+		open.bits_per_sample = 24;
+
+	pr_debug("%s: perf_mode %d asm_topology 0x%x bps %d\n", __func__,
+		 ac->perf_mode, open.postprocopo_id, open.bits_per_sample);
+
+	/*
+	 * For Gapless playback it will use the same session for next stream,
+	 * So use the same topology
+	 */
+	if (!ac->topology) {
+		ac->topology = open.postprocopo_id;
+		ac->app_type = q6asm_get_asm_app_type_cal();
+	}
+	switch (format) {
+	case FORMAT_LINEAR_PCM:
+		open.dec_fmt_id = q6asm_get_pcm_format_id(pcm_format_block_ver);
+		break;
+	case FORMAT_MPEG4_AAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+		break;
+	case FORMAT_MPEG4_MULTI_AAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+		break;
+	case FORMAT_WMA_V9:
+		open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2;
+		break;
+	case FORMAT_WMA_V10PRO:
+		open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2;
+		break;
+	case FORMAT_MP3:
+		open.dec_fmt_id = ASM_MEDIA_FMT_MP3;
+		break;
+	case FORMAT_AC3:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AC3;
+		break;
+	case FORMAT_EAC3:
+		open.dec_fmt_id = ASM_MEDIA_FMT_EAC3;
+		break;
+	case FORMAT_MP2:
+		open.dec_fmt_id = ASM_MEDIA_FMT_MP2;
+		break;
+	case FORMAT_FLAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_FLAC;
+		break;
+	case FORMAT_ALAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_ALAC;
+		break;
+	case FORMAT_VORBIS:
+		open.dec_fmt_id = ASM_MEDIA_FMT_VORBIS;
+		break;
+	case FORMAT_APE:
+		open.dec_fmt_id = ASM_MEDIA_FMT_APE;
+		break;
+	case FORMAT_DSD:
+		open.dec_fmt_id = ASM_MEDIA_FMT_DSD;
+		break;
+	default:
+		pr_err("%s: Invalid format 0x%x\n", __func__, format);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n",
+				__func__, open.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for open write\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	ac->io_mode |= TUN_WRITE_IO_MODE;
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_open_write(struct audio_client *ac, uint32_t format)
+{
+	return __q6asm_open_write(ac, format, 16, ac->stream_id,
+				  false /*gapless*/,
+				  PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
+}
+
+int q6asm_open_write_v2(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  ac->stream_id, false /*gapless*/,
+				  PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
+}
+
+/*
+ * q6asm_open_write_v3 - Opens audio playback session
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_v3(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  ac->stream_id, false /*gapless*/,
+				  PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_write_v3);
+
+/*
+ * q6asm_open_write_v4 - Opens audio playback session
+ *
+ * @ac: Client session handle
+ * @format: decoder format
+ * @bits_per_sample: bit width of playback session
+ */
+int q6asm_open_write_v4(struct audio_client *ac, uint32_t format,
+			uint16_t bits_per_sample)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  ac->stream_id, false /*gapless*/,
+				  PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_open_write_v4);
+
+int q6asm_stream_open_write_v2(struct audio_client *ac, uint32_t format,
+			       uint16_t bits_per_sample, int32_t stream_id,
+			       bool is_gapless_mode)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  stream_id, is_gapless_mode,
+				  PCM_MEDIA_FORMAT_V2 /*pcm_format_block_ver*/);
+}
+
+/*
+ * q6asm_stream_open_write_v3 - Creates audio stream for playback
+ *
+ * @ac: Client session handle
+ * @format: asm playback format
+ * @bits_per_sample: bit width of requested stream
+ * @stream_id: stream id of stream to be associated with this session
+ * @is_gapless_mode: true if gapless mode needs to be enabled
+ */
+int q6asm_stream_open_write_v3(struct audio_client *ac, uint32_t format,
+			       uint16_t bits_per_sample, int32_t stream_id,
+			       bool is_gapless_mode)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  stream_id, is_gapless_mode,
+				  PCM_MEDIA_FORMAT_V3 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_stream_open_write_v3);
+
+/*
+ * q6asm_stream_open_write_v4 - Creates audio stream for playback
+ *
+ * @ac: Client session handle
+ * @format: asm playback format
+ * @bits_per_sample: bit width of requested stream
+ * @stream_id: stream id of stream to be associated with this session
+ * @is_gapless_mode: true if gapless mode needs to be enabled
+ */
+int q6asm_stream_open_write_v4(struct audio_client *ac, uint32_t format,
+			       uint16_t bits_per_sample, int32_t stream_id,
+			       bool is_gapless_mode)
+{
+	return __q6asm_open_write(ac, format, bits_per_sample,
+				  stream_id, is_gapless_mode,
+				  PCM_MEDIA_FORMAT_V4 /*pcm_format_block_ver*/);
+}
+EXPORT_SYMBOL(q6asm_stream_open_write_v4);
+
+static int __q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format,
+				   uint32_t wr_format, bool is_meta_data_mode,
+				   uint32_t bits_per_sample,
+				   bool overwrite_topology, int topology)
+{
+	int rc = 0x00;
+	struct asm_stream_cmd_open_readwrite_v2 open;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+	pr_debug("%s: wr_format[0x%x]rd_format[0x%x]\n",
+			__func__, wr_format, rd_format);
+
+	ac->io_mode |= NT_MODE;
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_READWRITE_V2;
+
+	open.mode_flags = is_meta_data_mode ? BUFFER_META_ENABLE : 0;
+	open.bits_per_sample = bits_per_sample;
+	/* source endpoint : matrix */
+	open.postprocopo_id = q6asm_get_asm_topology_cal();
+
+	open.postprocopo_id = overwrite_topology ?
+			      topology : open.postprocopo_id;
+	ac->topology = open.postprocopo_id;
+	ac->app_type = q6asm_get_asm_app_type_cal();
+
+	/* For DTS EAGLE only, force 24 bit */
+	if ((open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_DTS_HPX) ||
+	     (open.postprocopo_id == ASM_STREAM_POSTPROC_TOPO_ID_HPX_MASTER))
+		open.bits_per_sample = 24;
+
+	switch (wr_format) {
+	case FORMAT_LINEAR_PCM:
+	case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+		open.dec_fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+		break;
+	case FORMAT_MPEG4_AAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+		break;
+	case FORMAT_MPEG4_MULTI_AAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AAC_V2;
+		break;
+	case FORMAT_WMA_V9:
+		open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V9_V2;
+		break;
+	case FORMAT_WMA_V10PRO:
+		open.dec_fmt_id = ASM_MEDIA_FMT_WMA_V10PRO_V2;
+		break;
+	case FORMAT_AMRNB:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AMRNB_FS;
+		break;
+	case FORMAT_AMRWB:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AMRWB_FS;
+		break;
+	case FORMAT_AMR_WB_PLUS:
+		open.dec_fmt_id = ASM_MEDIA_FMT_AMR_WB_PLUS_V2;
+		break;
+	case FORMAT_V13K:
+		open.dec_fmt_id = ASM_MEDIA_FMT_V13K_FS;
+		break;
+	case FORMAT_EVRC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_EVRC_FS;
+		break;
+	case FORMAT_EVRCB:
+		open.dec_fmt_id = ASM_MEDIA_FMT_EVRCB_FS;
+		break;
+	case FORMAT_EVRCWB:
+		open.dec_fmt_id = ASM_MEDIA_FMT_EVRCWB_FS;
+		break;
+	case FORMAT_MP3:
+		open.dec_fmt_id = ASM_MEDIA_FMT_MP3;
+		break;
+	case FORMAT_ALAC:
+		open.dec_fmt_id = ASM_MEDIA_FMT_ALAC;
+		break;
+	case FORMAT_APE:
+		open.dec_fmt_id = ASM_MEDIA_FMT_APE;
+		break;
+	case FORMAT_DSD:
+		open.dec_fmt_id = ASM_MEDIA_FMT_DSD;
+	case FORMAT_G711_ALAW_FS:
+		open.dec_fmt_id = ASM_MEDIA_FMT_G711_ALAW_FS;
+		break;
+	case FORMAT_G711_MLAW_FS:
+		open.dec_fmt_id = ASM_MEDIA_FMT_G711_MLAW_FS;
+		break;
+	default:
+		pr_err("%s: Invalid format 0x%x\n",
+				__func__, wr_format);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	switch (rd_format) {
+	case FORMAT_LINEAR_PCM:
+	case FORMAT_MULTI_CHANNEL_LINEAR_PCM:
+		open.enc_cfg_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V2;
+		break;
+	case FORMAT_MPEG4_AAC:
+		open.enc_cfg_id = ASM_MEDIA_FMT_AAC_V2;
+		break;
+	case FORMAT_G711_ALAW_FS:
+		open.enc_cfg_id = ASM_MEDIA_FMT_G711_ALAW_FS;
+		break;
+	case FORMAT_G711_MLAW_FS:
+		open.enc_cfg_id = ASM_MEDIA_FMT_G711_MLAW_FS;
+		break;
+	case FORMAT_V13K:
+		open.enc_cfg_id = ASM_MEDIA_FMT_V13K_FS;
+		break;
+	case FORMAT_EVRC:
+		open.enc_cfg_id = ASM_MEDIA_FMT_EVRC_FS;
+		break;
+	case FORMAT_AMRNB:
+		open.enc_cfg_id = ASM_MEDIA_FMT_AMRNB_FS;
+		break;
+	case FORMAT_AMRWB:
+		open.enc_cfg_id = ASM_MEDIA_FMT_AMRWB_FS;
+		break;
+	case FORMAT_ALAC:
+		open.enc_cfg_id = ASM_MEDIA_FMT_ALAC;
+		break;
+	case FORMAT_APE:
+		open.enc_cfg_id = ASM_MEDIA_FMT_APE;
+		break;
+	default:
+		pr_err("%s: Invalid format 0x%x\n",
+				__func__, rd_format);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	dev_vdbg(ac->dev, "%s: rdformat[0x%x]wrformat[0x%x]\n", __func__,
+			open.enc_cfg_id, open.dec_fmt_id);
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n",
+				__func__, open.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for open read-write\n",
+				__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_open_read_write(struct audio_client *ac, uint32_t rd_format,
+			  uint32_t wr_format)
+{
+	return __q6asm_open_read_write(ac, rd_format, wr_format,
+				       true/*meta data mode*/,
+				       16 /*bits_per_sample*/,
+				       false /*overwrite_topology*/, 0);
+}
+
+int q6asm_open_read_write_v2(struct audio_client *ac, uint32_t rd_format,
+			     uint32_t wr_format, bool is_meta_data_mode,
+			     uint32_t bits_per_sample, bool overwrite_topology,
+			     int topology)
+{
+	return __q6asm_open_read_write(ac, rd_format, wr_format,
+				       is_meta_data_mode, bits_per_sample,
+				       overwrite_topology, topology);
+}
+
+int q6asm_open_loopback_v2(struct audio_client *ac, uint16_t bits_per_sample)
+{
+	int rc = 0x00;
+	struct asm_stream_cmd_open_loopback_v2 open;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+	q6asm_add_hdr(ac, &open.hdr, sizeof(open), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	open.hdr.opcode = ASM_STREAM_CMD_OPEN_LOOPBACK_V2;
+
+	open.mode_flags = 0;
+	open.src_endpointype = 0;
+	open.sink_endpointype = 0;
+	/* source endpoint : matrix */
+	open.postprocopo_id = q6asm_get_asm_topology_cal();
+
+	ac->app_type = q6asm_get_asm_app_type_cal();
+	ac->topology = open.postprocopo_id;
+	open.bits_per_sample = bits_per_sample;
+	open.reserved = 0;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &open);
+	if (rc < 0) {
+		pr_err("%s: open failed op[0x%x]rc[%d]\n", __func__,
+				open.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for open_loopback\n",
+				__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static
+int q6asm_set_shared_circ_buff(struct audio_client *ac,
+			       struct asm_stream_cmd_open_shared_io *open,
+			       int bufsz, int bufcnt,
+			       int dir)
+{
+	struct audio_buffer *buf_circ;
+	int bytes_to_alloc, rc;
+	size_t len;
+
+	buf_circ = kzalloc(sizeof(struct audio_buffer), GFP_KERNEL);
+
+	if (!buf_circ) {
+		rc = -ENOMEM;
+		goto done;
+	}
+
+	mutex_lock(&ac->cmd_lock);
+
+	ac->port[dir].buf = buf_circ;
+
+	bytes_to_alloc = bufsz * bufcnt;
+	bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc);
+
+	rc = msm_audio_ion_alloc("audio_client", &buf_circ->client,
+			&buf_circ->handle, bytes_to_alloc,
+			(ion_phys_addr_t *)&buf_circ->phys,
+			&len, &buf_circ->data);
+
+	if (rc) {
+		pr_err("%s: Audio ION alloc is failed, rc = %d\n", __func__,
+				rc);
+		mutex_unlock(&ac->cmd_lock);
+		kfree(buf_circ);
+		goto done;
+	}
+
+	buf_circ->used = dir ^ 1;
+	buf_circ->size = bytes_to_alloc;
+	buf_circ->actual_size = bytes_to_alloc;
+	memset(buf_circ->data, 0, buf_circ->actual_size);
+
+	ac->port[dir].max_buf_cnt = 1;
+
+	open->shared_circ_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	open->shared_circ_buf_num_regions = 1;
+	open->shared_circ_buf_property_flag = 0x00;
+	open->shared_circ_buf_start_phy_addr_lsw =
+			lower_32_bits(buf_circ->phys);
+	open->shared_circ_buf_start_phy_addr_msw =
+			upper_32_bits(buf_circ->phys);
+	open->shared_circ_buf_size = bufsz * bufcnt;
+
+	open->map_region_circ_buf.shm_addr_lsw = lower_32_bits(buf_circ->phys);
+	open->map_region_circ_buf.shm_addr_msw = upper_32_bits(buf_circ->phys);
+	open->map_region_circ_buf.mem_size_bytes = bytes_to_alloc;
+
+	mutex_unlock(&ac->cmd_lock);
+done:
+	return rc;
+}
+
+
+static
+int q6asm_set_shared_pos_buff(struct audio_client *ac,
+			       struct asm_stream_cmd_open_shared_io *open,
+			       int dir)
+{
+	struct audio_buffer *buf_pos = &ac->shared_pos_buf;
+	int rc;
+	size_t len;
+	int bytes_to_alloc = sizeof(struct asm_shared_position_buffer);
+
+	mutex_lock(&ac->cmd_lock);
+
+	bytes_to_alloc = PAGE_ALIGN(bytes_to_alloc);
+
+	rc = msm_audio_ion_alloc("audio_client", &buf_pos->client,
+			&buf_pos->handle, bytes_to_alloc,
+			(ion_phys_addr_t *)&buf_pos->phys, &len,
+			&buf_pos->data);
+
+	if (rc) {
+		pr_err("%s: Audio pos buf ION alloc is failed, rc = %d\n",
+				__func__, rc);
+		goto done;
+	}
+
+	buf_pos->used = dir ^ 1;
+	buf_pos->size = bytes_to_alloc;
+	buf_pos->actual_size = bytes_to_alloc;
+
+	open->shared_pos_buf_mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	open->shared_pos_buf_num_regions = 1;
+	open->shared_pos_buf_property_flag = 0x00;
+	open->shared_pos_buf_phy_addr_lsw = lower_32_bits(buf_pos->phys);
+	open->shared_pos_buf_phy_addr_msw = upper_32_bits(buf_pos->phys);
+
+	open->map_region_pos_buf.shm_addr_lsw = lower_32_bits(buf_pos->phys);
+	open->map_region_pos_buf.shm_addr_msw = upper_32_bits(buf_pos->phys);
+	open->map_region_pos_buf.mem_size_bytes = bytes_to_alloc;
+
+done:
+	mutex_unlock(&ac->cmd_lock);
+	return rc;
+}
+
+/*
+ * q6asm_open_shared_io: Open an ASM session for pull mode (playback)
+ * or push mode (capture).
+ * parameters
+ *   config - session parameters (channels, bits_per_sample, sr)
+ *   dir - stream direction (IN for playback, OUT for capture)
+ * returns 0 if successful, error code otherwise
+ */
+int q6asm_open_shared_io(struct audio_client *ac,
+			 struct shared_io_config *config,
+			 int dir)
+{
+	struct asm_stream_cmd_open_shared_io *open;
+	u8 *channel_mapping;
+	int i, size_of_open, num_watermarks, bufsz, bufcnt, rc, flags = 0;
+
+	if (!ac || !config)
+		return -EINVAL;
+
+	bufsz = config->bufsz;
+	bufcnt = config->bufcnt;
+	num_watermarks = 0;
+
+	ac->config = *config;
+
+	if (ac->session <= 0 || ac->session > SESSION_MAX) {
+		pr_err("%s: Session %d is out of bounds\n",
+			__func__, ac->session);
+		return -EINVAL;
+	}
+
+	size_of_open = sizeof(struct asm_stream_cmd_open_shared_io) +
+		(sizeof(struct asm_shared_watermark_level) * num_watermarks);
+
+	open = kzalloc(PAGE_ALIGN(size_of_open), GFP_KERNEL);
+	if (!open)
+		return -ENOMEM;
+
+	q6asm_stream_add_hdr(ac, &open->hdr, size_of_open, TRUE,
+				ac->stream_id);
+
+	atomic_set(&ac->cmd_state, 1);
+
+	pr_debug("%s: token = 0x%x, stream_id %d, session 0x%x, perf %d\n",
+		 __func__, open->hdr.token, ac->stream_id, ac->session,
+		 ac->perf_mode);
+
+	open->hdr.opcode =
+		dir == IN ? ASM_STREAM_CMD_OPEN_PULL_MODE_WRITE :
+		ASM_STREAM_CMD_OPEN_PUSH_MODE_READ;
+
+	pr_debug("%s perf_mode %d\n", __func__, ac->perf_mode);
+	if (dir == IN)
+		if (ac->perf_mode == ULL_POST_PROCESSING_PCM_MODE)
+			flags = 4 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE;
+		else if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE)
+			flags = 2 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE;
+		else if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+			flags = 1 << ASM_SHIFT_STREAM_PERF_FLAG_PULL_MODE_WRITE;
+		else
+			pr_err("Invalid perf mode for pull write\n");
+	else
+		if (ac->perf_mode == LOW_LATENCY_PCM_MODE)
+			flags = ASM_LOW_LATENCY_TX_STREAM_SESSION <<
+				ASM_SHIFT_STREAM_PERF_FLAG_PUSH_MODE_READ;
+		else
+			pr_err("Invalid perf mode for push read\n");
+
+	if (flags == 0) {
+		pr_err("%s: Invalid mode[%d]\n", __func__,
+		       ac->perf_mode);
+		kfree(open);
+		return -EINVAL;
+
+	}
+
+	pr_debug("open.mode_flags = 0x%x\n", flags);
+	open->mode_flags = flags;
+	open->endpoint_type = ASM_END_POINT_DEVICE_MATRIX;
+	open->topo_bits_per_sample = config->bits_per_sample;
+
+	open->topo_id = q6asm_get_asm_topology_cal();
+
+	if (config->format == FORMAT_LINEAR_PCM)
+		open->fmt_id = ASM_MEDIA_FMT_MULTI_CHANNEL_PCM_V3;
+	else {
+		pr_err("%s: Invalid format[%d]\n", __func__, config->format);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	if (ac->port[dir].buf) {
+		pr_err("%s: Buffer already allocated\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	rc = q6asm_set_shared_circ_buff(ac, open, bufsz, bufcnt, dir);
+
+	if (rc)
+		goto done;
+
+	ac->port[dir].tmp_hdl = 0;
+
+	rc = q6asm_set_shared_pos_buff(ac, open, dir);
+
+	if (rc)
+		goto done;
+
+	/* asm_multi_channel_pcm_fmt_blk_v3 */
+	open->fmt.num_channels = config->channels;
+	open->fmt.bits_per_sample = config->bits_per_sample;
+	open->fmt.sample_rate = config->rate;
+	open->fmt.is_signed = 1;
+	open->fmt.sample_word_size = config->sample_word_size;
+
+	channel_mapping = open->fmt.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	rc = q6asm_map_channels(channel_mapping, config->channels, false);
+	if (rc) {
+		pr_err("%s: Map channels failed, ret: %d\n", __func__, rc);
+		goto done;
+	}
+
+	open->num_watermark_levels = num_watermarks;
+	for (i = 0; i < num_watermarks; i++) {
+		open->watermark[i].watermark_level_bytes = i *
+				((bufsz * bufcnt) / num_watermarks);
+		pr_debug("%s: Watermark level set for %i\n",
+				__func__,
+				open->watermark[i].watermark_level_bytes);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) open);
+	if (rc < 0) {
+		pr_err("%s: Open failed op[0x%x]rc[%d]\n",
+		       __func__, open->hdr.opcode, rc);
+		goto done;
+	}
+
+	pr_debug("%s: sent open apr pkt\n", __func__);
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) <= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: Timeout. Waited for open write apr pkt rc[%d]\n",
+		       __func__, rc);
+		rc = -ETIMEDOUT;
+		goto done;
+	}
+
+	if (atomic_read(&ac->cmd_state) < 0) {
+		pr_err("%s: DSP returned error [%d]\n", __func__,
+				atomic_read(&ac->cmd_state));
+		rc = -EINVAL;
+		goto done;
+	}
+
+	ac->io_mode |= TUN_WRITE_IO_MODE;
+	rc = 0;
+done:
+	kfree(open);
+	return rc;
+}
+EXPORT_SYMBOL(q6asm_open_shared_io);
+
+/*
+ * q6asm_shared_io_buf: Returns handle to the shared circular buffer being
+ * used for pull/push mode.
+ * parameters
+ *   dir - used to identify input/output port
+ * returns buffer handle
+ */
+struct audio_buffer *q6asm_shared_io_buf(struct audio_client *ac,
+					 int dir)
+{
+	struct audio_port_data *port;
+
+	if (!ac) {
+		pr_err("%s: ac is null\n", __func__);
+		return NULL;
+	}
+	port = &ac->port[dir];
+	return port->buf;
+}
+EXPORT_SYMBOL(q6asm_shared_io_buf);
+
+/*
+ * q6asm_shared_io_free: Frees memory allocated for a pull/push session
+ * parameters
+ *  dir - port direction
+ * returns 0 if successful, error otherwise
+ */
+int q6asm_shared_io_free(struct audio_client *ac, int dir)
+{
+	struct audio_port_data *port;
+
+	if (!ac) {
+		pr_err("%s: audio client is null\n", __func__);
+		return -EINVAL;
+	}
+	port = &ac->port[dir];
+	mutex_lock(&ac->cmd_lock);
+	if (port->buf && port->buf->data) {
+		msm_audio_ion_free(port->buf->client, port->buf->handle);
+		port->buf->client = NULL;
+		port->buf->handle = NULL;
+		port->max_buf_cnt = 0;
+		kfree(port->buf);
+		port->buf = NULL;
+	}
+	if (ac->shared_pos_buf.data) {
+		msm_audio_ion_free(ac->shared_pos_buf.client,
+				ac->shared_pos_buf.handle);
+		ac->shared_pos_buf.client = NULL;
+		ac->shared_pos_buf.handle = NULL;
+	}
+	mutex_unlock(&ac->cmd_lock);
+	return 0;
+}
+EXPORT_SYMBOL(q6asm_shared_io_free);
+
+/*
+ * q6asm_get_shared_pos: Returns current read index/write index as observed
+ * by the DSP. Note that this is an offset and iterates from [0,BUF_SIZE - 1]
+ * parameters - (all output)
+ *   read_index - offset
+ *   wall_clk_msw1 - ADSP wallclock msw
+ *   wall_clk_lsw1 - ADSP wallclock lsw
+ * returns 0 if successful, -EAGAIN if DSP failed to update after some
+ * retries
+ */
+int q6asm_get_shared_pos(struct audio_client *ac, uint32_t *read_index,
+			 uint32_t *wall_clk_msw1, uint32_t *wall_clk_lsw1)
+{
+	struct asm_shared_position_buffer *pos_buf;
+	uint32_t frame_cnt1, frame_cnt2;
+	int i, j;
+
+	if (!ac) {
+		pr_err("%s: audio client is null\n", __func__);
+		return -EINVAL;
+	}
+
+	pos_buf = ac->shared_pos_buf.data;
+
+	/* always try to get the latest update in the shared pos buffer */
+	for (i = 0; i < 2; i++) {
+		/* retry until there is an update from DSP */
+		for (j = 0; j < 5; j++) {
+			frame_cnt1 = pos_buf->frame_counter;
+			if (frame_cnt1 != 0)
+				break;
+		}
+
+		*wall_clk_msw1 = pos_buf->wall_clock_us_msw;
+		*wall_clk_lsw1 = pos_buf->wall_clock_us_lsw;
+		*read_index = pos_buf->index;
+		frame_cnt2 = pos_buf->frame_counter;
+
+		if (frame_cnt1 != frame_cnt2)
+			continue;
+		return 0;
+	}
+	pr_err("%s out of tries trying to get a good read, try again\n",
+	       __func__);
+	return -EAGAIN;
+}
+
+int q6asm_run(struct audio_client *ac, uint32_t flags,
+		uint32_t msw_ts, uint32_t lsw_ts)
+{
+	struct asm_session_cmd_run_v2 run;
+	int rc;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+	q6asm_add_hdr(ac, &run.hdr, sizeof(run), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
+	run.flags    = flags;
+	run.time_lsw = lsw_ts;
+	run.time_msw = msw_ts;
+
+	config_debug_fs_run();
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+	if (rc < 0) {
+		pr_err("%s: Commmand run failed[%d]",
+				__func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for run success",
+				__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+		uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id)
+{
+	struct asm_session_cmd_run_v2 run;
+	int rc;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+
+	q6asm_stream_add_hdr_async(ac, &run.hdr, sizeof(run), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, 1);
+	run.hdr.opcode = ASM_SESSION_CMD_RUN_V2;
+	run.flags    = flags;
+	run.time_lsw = lsw_ts;
+	run.time_msw = msw_ts;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &run);
+	if (rc < 0) {
+		pr_err("%s: Commmand run failed[%d]", __func__, rc);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int q6asm_run_nowait(struct audio_client *ac, uint32_t flags,
+			uint32_t msw_ts, uint32_t lsw_ts)
+{
+	return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, ac->stream_id);
+}
+
+int q6asm_stream_run_nowait(struct audio_client *ac, uint32_t flags,
+			uint32_t msw_ts, uint32_t lsw_ts, uint32_t stream_id)
+{
+	return __q6asm_run_nowait(ac, flags, msw_ts, lsw_ts, stream_id);
+}
+
+int q6asm_enc_cfg_blk_aac(struct audio_client *ac,
+			 uint32_t frames_per_buf,
+			uint32_t sample_rate, uint32_t channels,
+			uint32_t bit_rate, uint32_t mode, uint32_t format)
+{
+	struct asm_aac_enc_cfg_v2 enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]frames[%d]SR[%d]ch[%d]bitrate[%d]mode[%d] format[%d]\n",
+		 __func__, ac->session, frames_per_buf,
+		sample_rate, channels, bit_rate, mode, format);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(struct asm_aac_enc_cfg_v2) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+	enc_cfg.bit_rate = bit_rate;
+	enc_cfg.enc_mode = mode;
+	enc_cfg.aac_fmt_flag = format;
+	enc_cfg.channel_cfg = channels;
+	enc_cfg.sample_rate = sample_rate;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd %d failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n",
+			__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_enc_cfg_blk_g711(struct audio_client *ac,
+			uint32_t frames_per_buf,
+			uint32_t sample_rate)
+{
+	struct asm_g711_enc_cfg_v2 enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]frames[%d]SR[%d]\n",
+		 __func__, ac->session, frames_per_buf,
+		sample_rate);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(struct asm_g711_enc_cfg_v2) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+	enc_cfg.sample_rate = sample_rate;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd %d failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n",
+			__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_set_encdec_chan_map(struct audio_client *ac,
+			uint32_t num_channels)
+{
+	struct asm_dec_out_chan_map_param chan_map;
+	u8 *channel_mapping;
+	int rc = 0;
+
+	pr_debug("%s: Session %d, num_channels = %d\n",
+			 __func__, ac->session, num_channels);
+	q6asm_add_hdr(ac, &chan_map.hdr, sizeof(chan_map), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	chan_map.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	chan_map.encdec.param_id = ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP;
+	chan_map.encdec.param_size = sizeof(struct asm_dec_out_chan_map_param) -
+			 (sizeof(struct apr_hdr) +
+			 sizeof(struct asm_stream_cmd_set_encdec_param));
+	chan_map.num_channels = num_channels;
+	channel_mapping = chan_map.channel_mapping;
+	memset(channel_mapping, PCM_CHANNEL_NULL, MAX_CHAN_MAP_CHANNELS);
+
+	if (q6asm_map_channels(channel_mapping, num_channels, false)) {
+		pr_err("%s: map channels failed %d\n", __func__, num_channels);
+		return -EINVAL;
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &chan_map);
+	if (rc < 0) {
+		pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+			   __func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+			   ASM_PARAM_ID_DEC_OUTPUT_CHAN_MAP, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				 (atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n", __func__,
+			   chan_map.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+		return rc;
+}
+
+/*
+ * q6asm_enc_cfg_blk_pcm_v4 - sends encoder configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map  to be used
+ * @use_back_flavor: to configure back left and right channel
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+			     uint32_t rate, uint32_t channels,
+			     uint16_t bits_per_sample, bool use_default_chmap,
+			     bool use_back_flavor, u8 *channel_map,
+			     uint16_t sample_word_size, uint16_t endianness,
+			     uint16_t mode)
+{
+	struct asm_multi_channel_pcm_enc_cfg_v4 enc_cfg;
+	struct asm_enc_cfg_blk_param_v2 enc_fg_blk;
+	u8 *channel_mapping;
+	u32 frames_per_buf = 0;
+	int rc;
+
+	if (!use_default_chmap && (channel_map == NULL)) {
+		pr_err("%s: No valid chan map and can't use default\n",
+				__func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+		 ac->session, rate, channels,
+		 bits_per_sample, sample_word_size);
+
+	memset(&enc_cfg, 0, sizeof(enc_cfg));
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+				    sizeof(enc_cfg.encdec);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+					  sizeof(enc_fg_blk);
+	enc_cfg.num_channels = channels;
+	enc_cfg.bits_per_sample = bits_per_sample;
+	enc_cfg.sample_rate = rate;
+	enc_cfg.is_signed = 1;
+	enc_cfg.sample_word_size = sample_word_size;
+	enc_cfg.endianness = endianness;
+	enc_cfg.mode = mode;
+	channel_mapping = enc_cfg.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		pr_debug("%s: setting default channel map for %d channels",
+			 __func__, channels);
+		if (q6asm_map_channels(channel_mapping, channels,
+					use_back_flavor)) {
+			pr_err("%s: map channels failed %d\n",
+			       __func__, channels);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+	} else {
+		pr_debug("%s: Using pre-defined channel map", __func__);
+		memcpy(channel_mapping, channel_map,
+			PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Command open failed %d\n", __func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n",
+		       __func__, enc_cfg.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+		       __func__, adsp_err_get_err_str(
+		       atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v4);
+
+/*
+ * q6asm_enc_cfg_blk_pcm_v3 - sends encoder configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map  to be used
+ * @use_back_flavor: to configure back left and right channel
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
+			     uint32_t rate, uint32_t channels,
+			     uint16_t bits_per_sample, bool use_default_chmap,
+			     bool use_back_flavor, u8 *channel_map,
+			     uint16_t sample_word_size)
+{
+	struct asm_multi_channel_pcm_enc_cfg_v3 enc_cfg;
+	struct asm_enc_cfg_blk_param_v2 enc_fg_blk;
+	u8 *channel_mapping;
+	u32 frames_per_buf = 0;
+	int rc;
+
+	if (!use_default_chmap && (channel_map == NULL)) {
+		pr_err("%s: No valid chan map and can't use default\n",
+				__func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+		 ac->session, rate, channels,
+		 bits_per_sample, sample_word_size);
+
+	memset(&enc_cfg, 0, sizeof(enc_cfg));
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+				    sizeof(enc_cfg.encdec);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size = enc_cfg.encdec.param_size -
+					  sizeof(enc_fg_blk);
+	enc_cfg.num_channels = channels;
+	enc_cfg.bits_per_sample = bits_per_sample;
+	enc_cfg.sample_rate = rate;
+	enc_cfg.is_signed = 1;
+	enc_cfg.sample_word_size = sample_word_size;
+	channel_mapping = enc_cfg.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		pr_debug("%s: setting default channel map for %d channels",
+			 __func__, channels);
+		if (q6asm_map_channels(channel_mapping, channels,
+					use_back_flavor)) {
+			pr_err("%s: map channels failed %d\n",
+			       __func__, channels);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+	} else {
+		pr_debug("%s: Using pre-defined channel map", __func__);
+		memcpy(channel_mapping, channel_map,
+			PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n",
+		       __func__, enc_cfg.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+		       __func__, adsp_err_get_err_str(
+		       atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_v3);
+
+int q6asm_enc_cfg_blk_pcm_v2(struct audio_client *ac,
+		uint32_t rate, uint32_t channels, uint16_t bits_per_sample,
+		bool use_default_chmap, bool use_back_flavor, u8 *channel_map)
+{
+	struct asm_multi_channel_pcm_enc_cfg_v2  enc_cfg;
+	u8 *channel_mapping;
+	u32 frames_per_buf = 0;
+
+	int rc = 0;
+
+	if (!use_default_chmap && (channel_map == NULL)) {
+		pr_err("%s: No valid chan map and can't use default\n",
+				__func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+			 ac->session, rate, channels);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+				sizeof(enc_cfg.encdec);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+					sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	enc_cfg.num_channels = channels;
+	enc_cfg.bits_per_sample = bits_per_sample;
+	enc_cfg.sample_rate = rate;
+	enc_cfg.is_signed = 1;
+	channel_mapping = enc_cfg.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		pr_debug("%s: setting default channel map for %d channels",
+		__func__, channels);
+		if (q6asm_map_channels(channel_mapping, channels,
+					use_back_flavor)) {
+			pr_err("%s: map channels failed %d\n",
+			 __func__, channels);
+			return -EINVAL;
+		}
+	} else {
+		pr_debug("%s: Using pre-defined channel map", __func__);
+		memcpy(channel_mapping, channel_map,
+			PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n",
+			__func__, enc_cfg.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_enc_cfg_blk_pcm_v4(struct audio_client *ac,
+				      uint32_t rate, uint32_t channels,
+				      uint16_t bits_per_sample,
+				      uint16_t sample_word_size,
+				      uint16_t endianness,
+				      uint16_t mode)
+{
+	return q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels,
+					bits_per_sample, true, false, NULL,
+					sample_word_size, endianness, mode);
+}
+
+static int __q6asm_enc_cfg_blk_pcm_v3(struct audio_client *ac,
+				      uint32_t rate, uint32_t channels,
+				      uint16_t bits_per_sample,
+				      uint16_t sample_word_size)
+{
+	return q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels,
+					bits_per_sample, true, false, NULL,
+					sample_word_size);
+}
+
+static int __q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+		uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
+{
+	return q6asm_enc_cfg_blk_pcm_v2(ac, rate, channels,
+					bits_per_sample, true, false, NULL);
+}
+
+int q6asm_enc_cfg_blk_pcm(struct audio_client *ac,
+			uint32_t rate, uint32_t channels)
+{
+	return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, 16);
+}
+
+int q6asm_enc_cfg_blk_pcm_format_support(struct audio_client *ac,
+		uint32_t rate, uint32_t channels, uint16_t bits_per_sample)
+{
+	return __q6asm_enc_cfg_blk_pcm(ac, rate, channels, bits_per_sample);
+}
+
+/*
+ * q6asm_enc_cfg_blk_pcm_format_support_v3 - sends encoder configuration
+ *                                           parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_enc_cfg_blk_pcm_format_support_v3(struct audio_client *ac,
+					    uint32_t rate, uint32_t channels,
+					    uint16_t bits_per_sample,
+					    uint16_t sample_word_size)
+{
+	return __q6asm_enc_cfg_blk_pcm_v3(ac, rate, channels,
+					  bits_per_sample, sample_word_size);
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v3);
+
+/*
+ * q6asm_enc_cfg_blk_pcm_format_support_v4 - sends encoder configuration
+ *                                           parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_enc_cfg_blk_pcm_format_support_v4(struct audio_client *ac,
+					    uint32_t rate, uint32_t channels,
+					    uint16_t bits_per_sample,
+					    uint16_t sample_word_size,
+					    uint16_t endianness,
+					    uint16_t mode)
+{
+	return __q6asm_enc_cfg_blk_pcm_v4(ac, rate, channels,
+					   bits_per_sample, sample_word_size,
+					   endianness, mode);
+}
+EXPORT_SYMBOL(q6asm_enc_cfg_blk_pcm_format_support_v4);
+
+int q6asm_enc_cfg_blk_pcm_native(struct audio_client *ac,
+			uint32_t rate, uint32_t channels)
+{
+	struct asm_multi_channel_pcm_enc_cfg_v2  enc_cfg;
+	u8 *channel_mapping;
+	u32 frames_per_buf = 0;
+
+	int rc = 0;
+
+	pr_debug("%s: Session %d, rate = %d, channels = %d\n", __func__,
+			 ac->session, rate, channels);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(enc_cfg) - sizeof(enc_cfg.hdr) -
+				 sizeof(enc_cfg.encdec);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	enc_cfg.num_channels = 0;/*channels;*/
+	enc_cfg.bits_per_sample = 16;
+	enc_cfg.sample_rate = 0;/*rate;*/
+	enc_cfg.is_signed = 1;
+	channel_mapping = enc_cfg.channel_mapping;
+
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (q6asm_map_channels(channel_mapping, channels, false)) {
+		pr_err("%s: map channels failed %d\n", __func__, channels);
+		return -EINVAL;
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n",
+			__func__, enc_cfg.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int q6asm_map_channels(u8 *channel_mapping, uint32_t channels,
+		bool use_back_flavor)
+{
+	u8 *lchannel_mapping;
+
+	lchannel_mapping = channel_mapping;
+	pr_debug("%s:  channels passed: %d\n", __func__, channels);
+	if (channels == 1)  {
+		lchannel_mapping[0] = PCM_CHANNEL_FC;
+	} else if (channels == 2) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+	} else if (channels == 3) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_FC;
+	} else if (channels == 4) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = use_back_flavor ?
+			PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+		lchannel_mapping[3] = use_back_flavor ?
+			PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+	} else if (channels == 5) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_FC;
+		lchannel_mapping[3] = use_back_flavor ?
+			PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+		lchannel_mapping[4] = use_back_flavor ?
+			PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+	} else if (channels == 6) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_FC;
+		lchannel_mapping[3] = PCM_CHANNEL_LFE;
+		lchannel_mapping[4] = use_back_flavor ?
+			PCM_CHANNEL_LB : PCM_CHANNEL_LS;
+		lchannel_mapping[5] = use_back_flavor ?
+			PCM_CHANNEL_RB : PCM_CHANNEL_RS;
+	} else if (channels == 8) {
+		lchannel_mapping[0] = PCM_CHANNEL_FL;
+		lchannel_mapping[1] = PCM_CHANNEL_FR;
+		lchannel_mapping[2] = PCM_CHANNEL_FC;
+		lchannel_mapping[3] = PCM_CHANNEL_LFE;
+		lchannel_mapping[4] = PCM_CHANNEL_LB;
+		lchannel_mapping[5] = PCM_CHANNEL_RB;
+		lchannel_mapping[6] = PCM_CHANNEL_LS;
+		lchannel_mapping[7] = PCM_CHANNEL_RS;
+	} else {
+		pr_err("%s: ERROR.unsupported num_ch = %u\n",
+		 __func__, channels);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+int q6asm_enable_sbrps(struct audio_client *ac,
+			uint32_t sbr_ps_enable)
+{
+	struct asm_aac_sbr_ps_flag_param  sbrps;
+	u32 frames_per_buf = 0;
+
+	int rc = 0;
+
+	pr_debug("%s: Session %d\n", __func__, ac->session);
+
+	q6asm_add_hdr(ac, &sbrps.hdr, sizeof(sbrps), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	sbrps.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	sbrps.encdec.param_id = ASM_PARAM_ID_AAC_SBR_PS_FLAG;
+	sbrps.encdec.param_size = sizeof(struct asm_aac_sbr_ps_flag_param) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	sbrps.encblk.frames_per_buf = frames_per_buf;
+	sbrps.encblk.enc_cfg_blk_size  = sbrps.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	sbrps.sbr_ps_flag = sbr_ps_enable;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &sbrps);
+	if (rc < 0) {
+		pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+				__func__,
+				ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+				ASM_PARAM_ID_AAC_SBR_PS_FLAG, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x] ", __func__, sbrps.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_cfg_dual_mono_aac(struct audio_client *ac,
+			uint16_t sce_left, uint16_t sce_right)
+{
+	struct asm_aac_dual_mono_mapping_param dual_mono;
+
+	int rc = 0;
+
+	pr_debug("%s: Session %d, sce_left = %d, sce_right = %d\n",
+			 __func__, ac->session, sce_left, sce_right);
+
+	q6asm_add_hdr(ac, &dual_mono.hdr, sizeof(dual_mono), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	dual_mono.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	dual_mono.encdec.param_id = ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING;
+	dual_mono.encdec.param_size = sizeof(dual_mono.left_channel_sce) +
+				      sizeof(dual_mono.right_channel_sce);
+	dual_mono.left_channel_sce = sce_left;
+	dual_mono.right_channel_sce = sce_right;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &dual_mono);
+	if (rc < 0) {
+		pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+				__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+				ASM_PARAM_ID_AAC_DUAL_MONO_MAPPING, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n", __func__,
+						dual_mono.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+/* Support for selecting stereo mixing coefficients for B family not done */
+int q6asm_cfg_aac_sel_mix_coef(struct audio_client *ac, uint32_t mix_coeff)
+{
+	struct asm_aac_stereo_mix_coeff_selection_param_v2 aac_mix_coeff;
+	int rc = 0;
+
+	q6asm_add_hdr(ac, &aac_mix_coeff.hdr, sizeof(aac_mix_coeff), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	aac_mix_coeff.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	aac_mix_coeff.param_id =
+		ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2;
+	aac_mix_coeff.param_size =
+		sizeof(struct asm_aac_stereo_mix_coeff_selection_param_v2);
+	aac_mix_coeff.aac_stereo_mix_coeff_flag = mix_coeff;
+	pr_debug("%s: mix_coeff = %u\n", __func__, mix_coeff);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &aac_mix_coeff);
+	if (rc < 0) {
+		pr_err("%s: Command opcode[0x%x]paramid[0x%x] failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM,
+			ASM_PARAM_ID_AAC_STEREO_MIX_COEFF_SELECTION_FLAG_V2,
+			rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n",
+			__func__, aac_mix_coeff.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_enc_cfg_blk_qcelp(struct audio_client *ac, uint32_t frames_per_buf,
+		uint16_t min_rate, uint16_t max_rate,
+		uint16_t reduced_rate_level, uint16_t rate_modulation_cmd)
+{
+	struct asm_v13k_enc_cfg enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] reduced_rate_level[0x%4x]rate_modulation_cmd[0x%4x]\n",
+		 __func__,
+		ac->session, frames_per_buf, min_rate, max_rate,
+		reduced_rate_level, rate_modulation_cmd);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(struct asm_v13k_enc_cfg) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	enc_cfg.min_rate = min_rate;
+	enc_cfg.max_rate = max_rate;
+	enc_cfg.reduced_rate_cmd = reduced_rate_level;
+	enc_cfg.rate_mod_cmd = rate_modulation_cmd;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd %d failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for setencdec v13k resp\n",
+			__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_enc_cfg_blk_evrc(struct audio_client *ac, uint32_t frames_per_buf,
+		uint16_t min_rate, uint16_t max_rate,
+		uint16_t rate_modulation_cmd)
+{
+	struct asm_evrc_enc_cfg enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]frames[%d]min_rate[0x%4x]max_rate[0x%4x] rate_modulation_cmd[0x%4x]\n",
+		 __func__, ac->session,
+		frames_per_buf,	min_rate, max_rate, rate_modulation_cmd);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(struct asm_evrc_enc_cfg) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	enc_cfg.min_rate = min_rate;
+	enc_cfg.max_rate = max_rate;
+	enc_cfg.rate_mod_cmd = rate_modulation_cmd;
+	enc_cfg.reserved = 0;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd %d failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for encdec evrc\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_enc_cfg_blk_amrnb(struct audio_client *ac, uint32_t frames_per_buf,
+			uint16_t band_mode, uint16_t dtx_enable)
+{
+	struct asm_amrnb_enc_cfg enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n",
+		__func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(struct asm_amrnb_enc_cfg) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	enc_cfg.enc_mode = band_mode;
+	enc_cfg.dtx_mode = dtx_enable;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd %d failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for set encdec amrnb\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_enc_cfg_blk_amrwb(struct audio_client *ac, uint32_t frames_per_buf,
+			uint16_t band_mode, uint16_t dtx_enable)
+{
+	struct asm_amrwb_enc_cfg enc_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]frames[%d]band_mode[0x%4x]dtx_enable[0x%4x]\n",
+		__func__, ac->session, frames_per_buf, band_mode, dtx_enable);
+
+	q6asm_add_hdr(ac, &enc_cfg.hdr, sizeof(enc_cfg), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	enc_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	enc_cfg.encdec.param_id = ASM_PARAM_ID_ENCDEC_ENC_CFG_BLK_V2;
+	enc_cfg.encdec.param_size = sizeof(struct asm_amrwb_enc_cfg) -
+				sizeof(struct asm_stream_cmd_set_encdec_param);
+	enc_cfg.encblk.frames_per_buf = frames_per_buf;
+	enc_cfg.encblk.enc_cfg_blk_size  = enc_cfg.encdec.param_size -
+				sizeof(struct asm_enc_cfg_blk_param_v2);
+
+	enc_cfg.enc_mode = band_mode;
+	enc_cfg.dtx_mode = dtx_enable;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &enc_cfg);
+	if (rc < 0) {
+		pr_err("%s: Comamnd %d failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+
+static int __q6asm_media_format_block_pcm(struct audio_client *ac,
+				uint32_t rate, uint32_t channels,
+				uint16_t bits_per_sample, int stream_id,
+				bool use_default_chmap, char *channel_map)
+{
+	struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
+	u8 *channel_mapping;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+		channels);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&fmt.hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+		  __func__, fmt.hdr.token, stream_id, ac->session);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.num_channels = channels;
+	fmt.bits_per_sample = bits_per_sample;
+	fmt.sample_rate = rate;
+	fmt.is_signed = 1;
+
+	channel_mapping = fmt.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		if (q6asm_map_channels(channel_mapping, channels, false)) {
+			pr_err("%s: map channels failed %d\n",
+				__func__, channels);
+			return -EINVAL;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for format update\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_media_format_block_pcm_v3(struct audio_client *ac,
+					     uint32_t rate, uint32_t channels,
+					     uint16_t bits_per_sample,
+					     int stream_id,
+					     bool use_default_chmap,
+					     char *channel_map,
+					     uint16_t sample_word_size)
+{
+	struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt;
+	u8 *channel_mapping;
+	int rc;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+		 ac->session, rate, channels,
+		 bits_per_sample, sample_word_size);
+
+	memset(&fmt, 0, sizeof(fmt));
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+				(stream_id & 0xFF);
+
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+		 __func__, fmt.hdr.token, stream_id, ac->session);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.param.num_channels = channels;
+	fmt.param.bits_per_sample = bits_per_sample;
+	fmt.param.sample_rate = rate;
+	fmt.param.is_signed = 1;
+	fmt.param.sample_word_size = sample_word_size;
+	channel_mapping = fmt.param.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		if (q6asm_map_channels(channel_mapping, channels, false)) {
+			pr_err("%s: map channels failed %d\n",
+			       __func__, channels);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for format update\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_media_format_block_pcm_v4(struct audio_client *ac,
+					     uint32_t rate, uint32_t channels,
+					     uint16_t bits_per_sample,
+					     int stream_id,
+					     bool use_default_chmap,
+					     char *channel_map,
+					     uint16_t sample_word_size,
+					     uint16_t endianness,
+					     uint16_t mode)
+{
+	struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt;
+	u8 *channel_mapping;
+	int rc;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+		 ac->session, rate, channels,
+		 bits_per_sample, sample_word_size);
+
+	memset(&fmt, 0, sizeof(fmt));
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		fmt.hdr.token = ((ac->session << 8) & 0xFFFF00) |
+				(stream_id & 0xFF);
+
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+		 __func__, fmt.hdr.token, stream_id, ac->session);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.param.num_channels = channels;
+	fmt.param.bits_per_sample = bits_per_sample;
+	fmt.param.sample_rate = rate;
+	fmt.param.is_signed = 1;
+	fmt.param.sample_word_size = sample_word_size;
+	fmt.param.endianness = endianness;
+	fmt.param.mode = mode;
+	channel_mapping = fmt.param.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		if (q6asm_map_channels(channel_mapping, channels, false)) {
+			pr_err("%s: map channels failed %d\n",
+			       __func__, channels);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for format update\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_pcm(struct audio_client *ac,
+				uint32_t rate, uint32_t channels)
+{
+	return __q6asm_media_format_block_pcm(ac, rate,
+				channels, 16, ac->stream_id,
+				true, NULL);
+}
+
+int q6asm_media_format_block_pcm_format_support(struct audio_client *ac,
+				uint32_t rate, uint32_t channels,
+				uint16_t bits_per_sample)
+{
+	return __q6asm_media_format_block_pcm(ac, rate,
+				channels, bits_per_sample, ac->stream_id,
+				true, NULL);
+}
+
+int q6asm_media_format_block_pcm_format_support_v2(struct audio_client *ac,
+				uint32_t rate, uint32_t channels,
+				uint16_t bits_per_sample, int stream_id,
+				bool use_default_chmap, char *channel_map)
+{
+	if (!use_default_chmap && (channel_map == NULL)) {
+		pr_err("%s: No valid chan map and can't use default\n",
+			__func__);
+		return -EINVAL;
+	}
+	return __q6asm_media_format_block_pcm(ac, rate,
+				channels, bits_per_sample, stream_id,
+				use_default_chmap, channel_map);
+}
+
+/*
+ * q6asm_media_format_block_pcm_format_support_v3- sends pcm decoder
+ *						    configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @stream_id: stream id of stream to be associated with this session
+ * @use_default_chmap: true if default channel map  to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_media_format_block_pcm_format_support_v3(struct audio_client *ac,
+						   uint32_t rate,
+						   uint32_t channels,
+						   uint16_t bits_per_sample,
+						   int stream_id,
+						   bool use_default_chmap,
+						   char *channel_map,
+						   uint16_t sample_word_size)
+{
+	if (!use_default_chmap && (channel_map == NULL)) {
+		pr_err("%s: No valid chan map and can't use default\n",
+			__func__);
+		return -EINVAL;
+	}
+	return __q6asm_media_format_block_pcm_v3(ac, rate,
+				channels, bits_per_sample, stream_id,
+				use_default_chmap, channel_map,
+				sample_word_size);
+
+}
+EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v3);
+
+/*
+ * q6asm_media_format_block_pcm_format_support_v4- sends pcm decoder
+ *						    configuration parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @stream_id: stream id of stream to be associated with this session
+ * @use_default_chmap: true if default channel map  to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_pcm_format_support_v4(struct audio_client *ac,
+						   uint32_t rate,
+						   uint32_t channels,
+						   uint16_t bits_per_sample,
+						   int stream_id,
+						   bool use_default_chmap,
+						   char *channel_map,
+						   uint16_t sample_word_size,
+						   uint16_t endianness,
+						   uint16_t mode)
+{
+	if (!use_default_chmap && (channel_map == NULL)) {
+		pr_err("%s: No valid chan map and can't use default\n",
+			__func__);
+		return -EINVAL;
+	}
+	return __q6asm_media_format_block_pcm_v4(ac, rate,
+				channels, bits_per_sample, stream_id,
+				use_default_chmap, channel_map,
+				sample_word_size, endianness,
+				mode);
+
+}
+EXPORT_SYMBOL(q6asm_media_format_block_pcm_format_support_v4);
+
+
+static int __q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+				uint32_t rate, uint32_t channels,
+				bool use_default_chmap, char *channel_map,
+				uint16_t bits_per_sample)
+{
+	struct asm_multi_channel_pcm_fmt_blk_v2 fmt;
+	u8 *channel_mapping;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session, rate,
+		channels);
+
+	q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.num_channels = channels;
+	fmt.bits_per_sample = bits_per_sample;
+	fmt.sample_rate = rate;
+	fmt.is_signed = 1;
+
+	channel_mapping = fmt.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		if (q6asm_map_channels(channel_mapping, channels, false)) {
+			pr_err("%s: map channels failed %d\n",
+				__func__, channels);
+			return -EINVAL;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for format update\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
+						      uint32_t rate,
+						      uint32_t channels,
+						      bool use_default_chmap,
+						      char *channel_map,
+						      uint16_t bits_per_sample,
+						      uint16_t sample_word_size)
+{
+	struct asm_multi_channel_pcm_fmt_blk_param_v3 fmt;
+	u8 *channel_mapping;
+	int rc;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+		 ac->session, rate, channels,
+		 bits_per_sample, sample_word_size);
+
+	memset(&fmt, 0, sizeof(fmt));
+	q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.param.num_channels = channels;
+	fmt.param.bits_per_sample = bits_per_sample;
+	fmt.param.sample_rate = rate;
+	fmt.param.is_signed = 1;
+	fmt.param.sample_word_size = sample_word_size;
+	channel_mapping = fmt.param.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		if (q6asm_map_channels(channel_mapping, channels, false)) {
+			pr_err("%s: map channels failed %d\n",
+			       __func__, channels);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for format update\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+		       __func__, adsp_err_get_err_str(
+		       atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+						      uint32_t rate,
+						      uint32_t channels,
+						      bool use_default_chmap,
+						      char *channel_map,
+						      uint16_t bits_per_sample,
+						      uint16_t sample_word_size,
+						      uint16_t endianness,
+						      uint16_t mode)
+{
+	struct asm_multi_channel_pcm_fmt_blk_param_v4 fmt;
+	u8 *channel_mapping;
+	int rc;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]bps[%d]wordsize[%d]\n", __func__,
+		 ac->session, rate, channels,
+		 bits_per_sample, sample_word_size);
+
+	memset(&fmt, 0, sizeof(fmt));
+	q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.param.num_channels = channels;
+	fmt.param.bits_per_sample = bits_per_sample;
+	fmt.param.sample_rate = rate;
+	fmt.param.is_signed = 1;
+	fmt.param.sample_word_size = sample_word_size;
+	fmt.param.endianness = endianness;
+	fmt.param.mode = mode;
+	channel_mapping = fmt.param.channel_mapping;
+
+	memset(channel_mapping, 0, PCM_FORMAT_MAX_NUM_CHANNEL);
+
+	if (use_default_chmap) {
+		if (q6asm_map_channels(channel_mapping, channels, false)) {
+			pr_err("%s: map channels failed %d\n",
+			       __func__, channels);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+	} else {
+		memcpy(channel_mapping, channel_map,
+			 PCM_FORMAT_MAX_NUM_CHANNEL);
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for format update\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+		       __func__, adsp_err_get_err_str(
+		       atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_multi_ch_pcm(struct audio_client *ac,
+		uint32_t rate, uint32_t channels,
+		bool use_default_chmap, char *channel_map)
+{
+	return __q6asm_media_format_block_multi_ch_pcm(ac, rate,
+			channels, use_default_chmap, channel_map, 16);
+}
+
+int q6asm_media_format_block_multi_ch_pcm_v2(
+		struct audio_client *ac,
+		uint32_t rate, uint32_t channels,
+		bool use_default_chmap, char *channel_map,
+		uint16_t bits_per_sample)
+{
+	return __q6asm_media_format_block_multi_ch_pcm(ac, rate,
+			channels, use_default_chmap, channel_map,
+			bits_per_sample);
+}
+
+/*
+ * q6asm_media_format_block_multi_ch_pcm_v3 - sends pcm decoder configuration
+ *                                            parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map  to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ */
+int q6asm_media_format_block_multi_ch_pcm_v3(struct audio_client *ac,
+					     uint32_t rate, uint32_t channels,
+					     bool use_default_chmap,
+					     char *channel_map,
+					     uint16_t bits_per_sample,
+					     uint16_t sample_word_size)
+{
+	return __q6asm_media_format_block_multi_ch_pcm_v3(ac, rate, channels,
+							  use_default_chmap,
+							  channel_map,
+							  bits_per_sample,
+							  sample_word_size);
+}
+EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v3);
+
+/*
+ * q6asm_media_format_block_multi_ch_pcm_v4 - sends pcm decoder configuration
+ *                                            parameters
+ *
+ * @ac: Client session handle
+ * @rate: sample rate
+ * @channels: number of channels
+ * @bits_per_sample: bit width of encoder session
+ * @use_default_chmap: true if default channel map  to be used
+ * @channel_map: input channel map
+ * @sample_word_size: Size in bits of the word that holds a sample of a channel
+ * @endianness: endianness of the pcm data
+ * @mode: Mode to provide additional info about the pcm input data
+ */
+int q6asm_media_format_block_multi_ch_pcm_v4(struct audio_client *ac,
+					     uint32_t rate, uint32_t channels,
+					     bool use_default_chmap,
+					     char *channel_map,
+					     uint16_t bits_per_sample,
+					     uint16_t sample_word_size,
+					     uint16_t endianness,
+					     uint16_t mode)
+{
+	return __q6asm_media_format_block_multi_ch_pcm_v4(ac, rate, channels,
+							  use_default_chmap,
+							  channel_map,
+							  bits_per_sample,
+							  sample_word_size,
+							  endianness,
+							  mode);
+}
+EXPORT_SYMBOL(q6asm_media_format_block_multi_ch_pcm_v4);
+
+static int __q6asm_media_format_block_multi_aac(struct audio_client *ac,
+				struct asm_aac_cfg *cfg, int stream_id)
+{
+	struct asm_aac_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]rate[%d]ch[%d]\n", __func__, ac->session,
+		cfg->sample_rate, cfg->ch_cfg);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&fmt.hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+		  __func__, fmt.hdr.token, stream_id, ac->session);
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmt_blk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmt_blk);
+	fmt.aac_fmt_flag = cfg->format;
+	fmt.audio_objype = cfg->aot;
+	/* If zero, PCE is assumed to be available in bitstream*/
+	fmt.total_size_of_PCE_bits = 0;
+	fmt.channel_config = cfg->ch_cfg;
+	fmt.sample_rate = cfg->sample_rate;
+
+	pr_debug("%s: format=0x%x cfg_size=%d aac-cfg=0x%x aot=%d ch=%d sr=%d\n",
+			__func__, fmt.aac_fmt_flag, fmt.fmt_blk.fmt_blk_size,
+			fmt.aac_fmt_flag,
+			fmt.audio_objype,
+			fmt.channel_config,
+			fmt.sample_rate);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_multi_aac(struct audio_client *ac,
+				struct asm_aac_cfg *cfg)
+{
+	return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id);
+}
+
+int q6asm_media_format_block_aac(struct audio_client *ac,
+			struct asm_aac_cfg *cfg)
+{
+	return __q6asm_media_format_block_multi_aac(ac, cfg, ac->stream_id);
+}
+
+int q6asm_stream_media_format_block_aac(struct audio_client *ac,
+			struct asm_aac_cfg *cfg, int stream_id)
+{
+	return __q6asm_media_format_block_multi_aac(ac, cfg, stream_id);
+}
+
+int q6asm_media_format_block_wma(struct audio_client *ac,
+				void *cfg, int stream_id)
+{
+	struct asm_wmastdv9_fmt_blk_v2 fmt;
+	struct asm_wma_cfg *wma_cfg = (struct asm_wma_cfg *)cfg;
+	int rc = 0;
+
+	pr_debug("session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x]\n",
+		ac->session, wma_cfg->format_tag, wma_cfg->sample_rate,
+		wma_cfg->ch_cfg, wma_cfg->avg_bytes_per_sec,
+		wma_cfg->block_align, wma_cfg->valid_bits_per_sample,
+		wma_cfg->ch_mask, wma_cfg->encode_opt);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmtblk);
+	fmt.fmtag = wma_cfg->format_tag;
+	fmt.num_channels = wma_cfg->ch_cfg;
+	fmt.sample_rate = wma_cfg->sample_rate;
+	fmt.avg_bytes_per_sec = wma_cfg->avg_bytes_per_sec;
+	fmt.blk_align = wma_cfg->block_align;
+	fmt.bits_per_sample =
+			wma_cfg->valid_bits_per_sample;
+	fmt.channel_mask = wma_cfg->ch_mask;
+	fmt.enc_options = wma_cfg->encode_opt;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_wmapro(struct audio_client *ac,
+				void *cfg, int stream_id)
+{
+	struct asm_wmaprov10_fmt_blk_v2 fmt;
+	struct asm_wmapro_cfg *wmapro_cfg = (struct asm_wmapro_cfg *)cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]format_tag[0x%4x] rate[%d] ch[0x%4x] bps[%d], balign[0x%4x], bit_sample[0x%4x], ch_msk[%d], enc_opt[0x%4x], adv_enc_opt[0x%4x], adv_enc_opt2[0x%8x]\n",
+		__func__,
+		ac->session, wmapro_cfg->format_tag, wmapro_cfg->sample_rate,
+		wmapro_cfg->ch_cfg,  wmapro_cfg->avg_bytes_per_sec,
+		wmapro_cfg->block_align, wmapro_cfg->valid_bits_per_sample,
+		wmapro_cfg->ch_mask, wmapro_cfg->encode_opt,
+		wmapro_cfg->adv_encode_opt, wmapro_cfg->adv_encode_opt2);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+						sizeof(fmt.fmtblk);
+
+	fmt.fmtag = wmapro_cfg->format_tag;
+	fmt.num_channels = wmapro_cfg->ch_cfg;
+	fmt.sample_rate = wmapro_cfg->sample_rate;
+	fmt.avg_bytes_per_sec =
+				wmapro_cfg->avg_bytes_per_sec;
+	fmt.blk_align = wmapro_cfg->block_align;
+	fmt.bits_per_sample = wmapro_cfg->valid_bits_per_sample;
+	fmt.channel_mask = wmapro_cfg->ch_mask;
+	fmt.enc_options = wmapro_cfg->encode_opt;
+	fmt.usAdvancedEncodeOpt = wmapro_cfg->adv_encode_opt;
+	fmt.advanced_enc_options2 = wmapro_cfg->adv_encode_opt2;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd open failed %d\n", __func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_amrwbplus(struct audio_client *ac,
+				struct asm_amrwbplus_cfg *cfg)
+{
+	struct asm_amrwbplus_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	pr_debug("%s: session[%d]band-mode[%d]frame-fmt[%d]ch[%d]\n",
+		__func__,
+		ac->session,
+		cfg->amr_band_mode,
+		cfg->amr_frame_fmt,
+		cfg->num_channels);
+
+	q6asm_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmtblk);
+	fmt.amr_frame_fmt = cfg->amr_frame_fmt;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Comamnd media format update failed.. %d\n",
+			__func__, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_stream_media_format_block_flac(struct audio_client *ac,
+				struct asm_flac_cfg *cfg, int stream_id)
+{
+	struct asm_flac_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	pr_debug("%s :session[%d] rate[%d] ch[%d] size[%d] stream_id[%d]\n",
+		__func__, ac->session, cfg->sample_rate, cfg->ch_cfg,
+		cfg->sample_size, stream_id);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+						sizeof(fmt.fmtblk);
+
+	fmt.is_stream_info_present = cfg->stream_info_present;
+	fmt.num_channels = cfg->ch_cfg;
+	fmt.min_blk_size = cfg->min_blk_size;
+	fmt.max_blk_size = cfg->max_blk_size;
+	fmt.sample_rate = cfg->sample_rate;
+	fmt.min_frame_size = cfg->min_frame_size;
+	fmt.max_frame_size = cfg->max_frame_size;
+	fmt.sample_size = cfg->sample_size;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s :Comamnd media format update failed %d\n",
+				__func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_alac(struct audio_client *ac,
+				struct asm_alac_cfg *cfg, int stream_id)
+{
+	struct asm_alac_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__,
+		ac->session, cfg->sample_rate, cfg->num_channels);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+						sizeof(fmt.fmtblk);
+
+	fmt.frame_length = cfg->frame_length;
+	fmt.compatible_version = cfg->compatible_version;
+	fmt.bit_depth = cfg->bit_depth;
+	fmt.pb = cfg->pb;
+	fmt.mb = cfg->mb;
+	fmt.kb = cfg->kb;
+	fmt.num_channels = cfg->num_channels;
+	fmt.max_run = cfg->max_run;
+	fmt.max_frame_bytes = cfg->max_frame_bytes;
+	fmt.avg_bit_rate = cfg->avg_bit_rate;
+	fmt.sample_rate = cfg->sample_rate;
+	fmt.channel_layout_tag = cfg->channel_layout_tag;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s :Comamnd media format update failed %d\n",
+				__func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+/*
+ * q6asm_media_format_block_g711 - sends g711 decoder configuration
+ *                                            parameters
+ * @ac: Client session handle
+ * @cfg: Audio stream manager configuration parameters
+ * @stream_id: Stream id
+ */
+int q6asm_media_format_block_g711(struct audio_client *ac,
+				struct asm_g711_dec_cfg *cfg, int stream_id)
+{
+	struct asm_g711_dec_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	if (!ac) {
+		pr_err("%s: audio client is null\n", __func__);
+		return -EINVAL;
+	}
+	if (!cfg) {
+		pr_err("%s: Invalid ASM config\n", __func__);
+		return -EINVAL;
+	}
+
+	if (stream_id <= 0) {
+		pr_err("%s: Invalid stream id\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s :session[%d]rate[%d]\n", __func__,
+		ac->session, cfg->sample_rate);
+
+	memset(&fmt, 0, sizeof(struct asm_g711_dec_fmt_blk_v2));
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+						sizeof(fmt.fmtblk);
+
+	fmt.sample_rate = cfg->sample_rate;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s :Command media format update failed %d\n",
+				__func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+EXPORT_SYMBOL(q6asm_media_format_block_g711);
+
+int q6asm_stream_media_format_block_vorbis(struct audio_client *ac,
+				struct asm_vorbis_cfg *cfg, int stream_id)
+{
+	struct asm_vorbis_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	pr_debug("%s :session[%d] bit_stream_fmt[%d] stream_id[%d]\n",
+		__func__, ac->session, cfg->bit_stream_fmt, stream_id);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+						sizeof(fmt.fmtblk);
+
+	fmt.bit_stream_fmt = cfg->bit_stream_fmt;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s :Comamnd media format update failed %d\n",
+				__func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_media_format_block_ape(struct audio_client *ac,
+				struct asm_ape_cfg *cfg, int stream_id)
+{
+	struct asm_ape_fmt_blk_v2 fmt;
+	int rc = 0;
+
+	pr_debug("%s :session[%d]rate[%d]ch[%d]\n", __func__,
+			ac->session, cfg->sample_rate, cfg->num_channels);
+
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+		sizeof(fmt.fmtblk);
+
+	fmt.compatible_version = cfg->compatible_version;
+	fmt.compression_level = cfg->compression_level;
+	fmt.format_flags = cfg->format_flags;
+	fmt.blocks_per_frame = cfg->blocks_per_frame;
+	fmt.final_frame_blocks = cfg->final_frame_blocks;
+	fmt.total_frames = cfg->total_frames;
+	fmt.bits_per_sample = cfg->bits_per_sample;
+	fmt.num_channels = cfg->num_channels;
+	fmt.sample_rate = cfg->sample_rate;
+	fmt.seek_table_present = cfg->seek_table_present;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s :Comamnd media format update failed %d\n",
+				__func__, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s :timeout. waited for FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+/*
+ * q6asm_media_format_block_dsd- Sends DSD Decoder
+ * configuration parameters
+ *
+ * @ac: Client session handle
+ * @cfg: DSD Media Format Configuration.
+ * @stream_id: stream id of stream to be associated with this session
+ *
+ * Return 0 on success or negative error code on failure
+ */
+int q6asm_media_format_block_dsd(struct audio_client *ac,
+				struct asm_dsd_cfg *cfg, int stream_id)
+{
+	struct asm_dsd_fmt_blk_v2 fmt;
+	int rc;
+
+	pr_debug("%s: session[%d] data_rate[%d] ch[%d]\n", __func__,
+		 ac->session, cfg->dsd_data_rate, cfg->num_channels);
+
+	memset(&fmt, 0, sizeof(fmt));
+	q6asm_stream_add_hdr(ac, &fmt.hdr, sizeof(fmt), TRUE, stream_id);
+
+	fmt.hdr.opcode = ASM_DATA_CMD_MEDIA_FMT_UPDATE_V2;
+	fmt.fmtblk.fmt_blk_size = sizeof(fmt) - sizeof(fmt.hdr) -
+					sizeof(fmt.fmtblk);
+
+	fmt.num_version = cfg->num_version;
+	fmt.is_bitwise_big_endian = cfg->is_bitwise_big_endian;
+	fmt.dsd_channel_block_size = cfg->dsd_channel_block_size;
+	fmt.num_channels = cfg->num_channels;
+	fmt.dsd_data_rate = cfg->dsd_data_rate;
+	atomic_set(&ac->cmd_state, -1);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &fmt);
+	if (rc < 0) {
+		pr_err("%s: Command DSD media format update failed, err: %d\n",
+			__func__, rc);
+		goto done;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for DSD FORMAT_UPDATE\n", __func__);
+		rc = -ETIMEDOUT;
+		goto done;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto done;
+	}
+	return 0;
+done:
+	return rc;
+}
+EXPORT_SYMBOL(q6asm_media_format_block_dsd);
+
+static int __q6asm_ds1_set_endp_params(struct audio_client *ac, int param_id,
+				int param_value, int stream_id)
+{
+	struct asm_dec_ddp_endp_param_v2 ddp_cfg;
+	int rc = 0;
+
+	pr_debug("%s: session[%d] stream[%d],param_id[%d]param_value[%d]",
+		 __func__, ac->session, stream_id, param_id, param_value);
+
+	q6asm_stream_add_hdr(ac, &ddp_cfg.hdr, sizeof(ddp_cfg), TRUE,
+			     stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&ddp_cfg.hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+	ddp_cfg.hdr.opcode = ASM_STREAM_CMD_SET_ENCDEC_PARAM;
+	ddp_cfg.encdec.param_id = param_id;
+	ddp_cfg.encdec.param_size = sizeof(struct asm_dec_ddp_endp_param_v2) -
+				(sizeof(struct apr_hdr) +
+				sizeof(struct asm_stream_cmd_set_encdec_param));
+	ddp_cfg.endp_param_value = param_value;
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &ddp_cfg);
+	if (rc < 0) {
+		pr_err("%s: Command opcode[0x%x] failed %d\n",
+			__func__, ASM_STREAM_CMD_SET_ENCDEC_PARAM, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+		(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout opcode[0x%x]\n", __func__,
+			ddp_cfg.hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_ds1_set_endp_params(struct audio_client *ac,
+			      int param_id, int param_value)
+{
+	return __q6asm_ds1_set_endp_params(ac, param_id, param_value,
+					   ac->stream_id);
+}
+
+int q6asm_ds1_set_stream_endp_params(struct audio_client *ac,
+				     int param_id, int param_value,
+				     int stream_id)
+{
+	return __q6asm_ds1_set_endp_params(ac, param_id, param_value,
+					   stream_id);
+}
+
+int q6asm_memory_map(struct audio_client *ac, phys_addr_t buf_add, int dir,
+				uint32_t bufsz, uint32_t bufcnt)
+{
+	struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+	struct avs_shared_map_region_payload  *mregions = NULL;
+	struct audio_port_data *port = NULL;
+	void	*mmap_region_cmd = NULL;
+	void	*payload = NULL;
+	struct asm_buffer_node *buffer_node = NULL;
+	int	rc = 0;
+	int	cmd_size = 0;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->mmap_apr == NULL) {
+		pr_err("%s: mmap APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+	buffer_node = kmalloc(sizeof(struct asm_buffer_node), GFP_KERNEL);
+	if (!buffer_node)
+		return -ENOMEM;
+
+	cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+			+ sizeof(struct avs_shared_map_region_payload) * bufcnt;
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (mmap_region_cmd == NULL) {
+		rc = -EINVAL;
+		kfree(buffer_node);
+		return rc;
+	}
+	mmap_regions = (struct avs_cmd_shared_mem_map_regions *)
+							mmap_region_cmd;
+	q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir);
+	atomic_set(&ac->mem_state, -1);
+	mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
+	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	mmap_regions->num_regions = bufcnt & 0x00ff;
+	mmap_regions->property_flag = 0x00;
+	payload = ((u8 *) mmap_region_cmd +
+		sizeof(struct avs_cmd_shared_mem_map_regions));
+	mregions = (struct avs_shared_map_region_payload *)payload;
+
+	ac->port[dir].tmp_hdl = 0;
+	port = &ac->port[dir];
+	pr_debug("%s: buf_add 0x%pK, bufsz: %d\n", __func__,
+		&buf_add, bufsz);
+	mregions->shm_addr_lsw = lower_32_bits(buf_add);
+	mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(buf_add);
+	mregions->mem_size_bytes = bufsz;
+	++mregions;
+
+	rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd);
+	if (rc < 0) {
+		pr_err("%s: mmap op[0x%x]rc[%d]\n", __func__,
+					mmap_regions->hdr.opcode, rc);
+		rc = -EINVAL;
+		kfree(buffer_node);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->mem_wait,
+			(atomic_read(&ac->mem_state) >= 0 &&
+			 ac->port[dir].tmp_hdl), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for memory_map\n", __func__);
+		rc = -ETIMEDOUT;
+		kfree(buffer_node);
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->mem_state) > 0) {
+		pr_err("%s: DSP returned error[%s] for memory_map\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->mem_state)));
+		rc = adsp_err_get_lnx_err_code(
+			atomic_read(&ac->mem_state));
+		kfree(buffer_node);
+		goto fail_cmd;
+	}
+	buffer_node->buf_phys_addr = buf_add;
+	buffer_node->mmap_hdl = ac->port[dir].tmp_hdl;
+	list_add_tail(&buffer_node->list, &ac->port[dir].mem_map_handle);
+	ac->port[dir].tmp_hdl = 0;
+	rc = 0;
+
+fail_cmd:
+	kfree(mmap_region_cmd);
+	return rc;
+}
+
+int q6asm_memory_unmap(struct audio_client *ac, phys_addr_t buf_add, int dir)
+{
+	struct avs_cmd_shared_mem_unmap_regions mem_unmap;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+
+	int rc = 0;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (this_mmap.apr == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+	q6asm_add_mmaphdr(ac, &mem_unmap.hdr,
+			sizeof(struct avs_cmd_shared_mem_unmap_regions),
+			dir);
+	atomic_set(&ac->mem_state, -1);
+	mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+	mem_unmap.mem_map_handle = 0;
+	list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+						list);
+		if (buf_node->buf_phys_addr == buf_add) {
+			pr_debug("%s: Found the element\n", __func__);
+			mem_unmap.mem_map_handle = buf_node->mmap_hdl;
+			break;
+		}
+	}
+	pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n",
+		__func__, mem_unmap.mem_map_handle);
+
+	if (mem_unmap.mem_map_handle == 0) {
+		pr_err("%s: Do not send null mem handle to DSP\n", __func__);
+		rc = 0;
+		goto fail_cmd;
+	}
+	rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap);
+	if (rc < 0) {
+		pr_err("%s: mem_unmap op[0x%x]rc[%d]\n", __func__,
+					mem_unmap.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->mem_wait,
+			(atomic_read(&ac->mem_state) >= 0), 5 * HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n",
+			__func__, mem_unmap.mem_map_handle);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	} else if (atomic_read(&ac->mem_state) > 0) {
+		pr_err("%s DSP returned error [%s] map handle 0x%x\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->mem_state)),
+			mem_unmap.mem_map_handle);
+		rc = adsp_err_get_lnx_err_code(
+			atomic_read(&ac->mem_state));
+		goto fail_cmd;
+	} else if (atomic_read(&ac->unmap_cb_success) == 0) {
+		pr_err("%s: Error in mem unmap callback of handle 0x%x\n",
+			__func__, mem_unmap.mem_map_handle);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = 0;
+fail_cmd:
+	list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+						list);
+		if (buf_node->buf_phys_addr == buf_add) {
+			list_del(&buf_node->list);
+			kfree(buf_node);
+			break;
+		}
+	}
+	return rc;
+}
+
+
+static int q6asm_memory_map_regions(struct audio_client *ac, int dir,
+				uint32_t bufsz, uint32_t bufcnt,
+				bool is_contiguous)
+{
+	struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+	struct avs_shared_map_region_payload  *mregions = NULL;
+	struct audio_port_data *port = NULL;
+	struct audio_buffer *ab = NULL;
+	void	*mmap_region_cmd = NULL;
+	void	*payload = NULL;
+	struct asm_buffer_node *buffer_node = NULL;
+	int	rc = 0;
+	int    i = 0;
+	uint32_t cmd_size = 0;
+	uint32_t bufcnt_t;
+	uint32_t bufsz_t;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->mmap_apr == NULL) {
+		pr_err("%s: mmap APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+	bufcnt_t = (is_contiguous) ? 1 : bufcnt;
+	bufsz_t = (is_contiguous) ? (bufsz * bufcnt) : bufsz;
+
+	if (is_contiguous) {
+		/* The size to memory map should be multiple of 4K bytes */
+		bufsz_t = PAGE_ALIGN(bufsz_t);
+	}
+
+	if (bufcnt_t > (UINT_MAX
+			- sizeof(struct avs_cmd_shared_mem_map_regions))
+			/ sizeof(struct avs_shared_map_region_payload)) {
+		pr_err("%s: Unsigned Integer Overflow. bufcnt_t = %u\n",
+				__func__, bufcnt_t);
+		return -EINVAL;
+	}
+
+	cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+			+ (sizeof(struct avs_shared_map_region_payload)
+							* bufcnt_t);
+
+
+	if (bufcnt > (UINT_MAX / sizeof(struct asm_buffer_node))) {
+		pr_err("%s: Unsigned Integer Overflow. bufcnt = %u\n",
+				__func__, bufcnt);
+		return -EINVAL;
+	}
+
+	buffer_node = kzalloc(sizeof(struct asm_buffer_node) * bufcnt,
+				GFP_KERNEL);
+	if (!buffer_node)
+		return -ENOMEM;
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (mmap_region_cmd == NULL) {
+		rc = -EINVAL;
+		kfree(buffer_node);
+		return rc;
+	}
+	mmap_regions = (struct avs_cmd_shared_mem_map_regions *)
+							mmap_region_cmd;
+	q6asm_add_mmaphdr(ac, &mmap_regions->hdr, cmd_size, dir);
+	atomic_set(&ac->mem_state, -1);
+	pr_debug("%s: mmap_region=0x%pK token=0x%x\n", __func__,
+		mmap_regions, ((ac->session << 8) | dir));
+
+	mmap_regions->hdr.opcode = ASM_CMD_SHARED_MEM_MAP_REGIONS;
+	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	mmap_regions->num_regions = bufcnt_t; /*bufcnt & 0x00ff; */
+	mmap_regions->property_flag = 0x00;
+	pr_debug("%s: map_regions->nregions = %d\n", __func__,
+		mmap_regions->num_regions);
+	payload = ((u8 *) mmap_region_cmd +
+		sizeof(struct avs_cmd_shared_mem_map_regions));
+	mregions = (struct avs_shared_map_region_payload *)payload;
+
+	ac->port[dir].tmp_hdl = 0;
+	port = &ac->port[dir];
+	for (i = 0; i < bufcnt_t; i++) {
+		ab = &port->buf[i];
+		mregions->shm_addr_lsw = lower_32_bits(ab->phys);
+		mregions->shm_addr_msw =
+				msm_audio_populate_upper_32_bits(ab->phys);
+		mregions->mem_size_bytes = bufsz_t;
+		++mregions;
+	}
+
+	rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) mmap_region_cmd);
+	if (rc < 0) {
+		pr_err("%s: mmap_regions op[0x%x]rc[%d]\n", __func__,
+					mmap_regions->hdr.opcode, rc);
+		rc = -EINVAL;
+		kfree(buffer_node);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->mem_wait,
+			(atomic_read(&ac->mem_state) >= 0)
+			 , 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for memory_map\n", __func__);
+		rc = -ETIMEDOUT;
+		kfree(buffer_node);
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->mem_state) > 0) {
+		pr_err("%s DSP returned error for memory_map [%s]\n",
+			__func__, adsp_err_get_err_str(
+			atomic_read(&ac->mem_state)));
+		rc = adsp_err_get_lnx_err_code(
+			atomic_read(&ac->mem_state));
+		kfree(buffer_node);
+		goto fail_cmd;
+	}
+	mutex_lock(&ac->cmd_lock);
+
+	for (i = 0; i < bufcnt; i++) {
+		ab = &port->buf[i];
+		buffer_node[i].buf_phys_addr = ab->phys;
+		buffer_node[i].mmap_hdl = ac->port[dir].tmp_hdl;
+		list_add_tail(&buffer_node[i].list,
+			&ac->port[dir].mem_map_handle);
+		pr_debug("%s: i=%d, bufadd[i] = 0x%pK, maphdl[i] = 0x%x\n",
+			__func__, i, &buffer_node[i].buf_phys_addr,
+			buffer_node[i].mmap_hdl);
+	}
+	ac->port[dir].tmp_hdl = 0;
+	mutex_unlock(&ac->cmd_lock);
+	rc = 0;
+fail_cmd:
+	kfree(mmap_region_cmd);
+	return rc;
+}
+
+static int q6asm_memory_unmap_regions(struct audio_client *ac, int dir)
+{
+	struct avs_cmd_shared_mem_unmap_regions mem_unmap;
+	struct audio_port_data *port = NULL;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+	phys_addr_t buf_add;
+	int	rc = 0;
+	int	cmd_size = 0;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->mmap_apr == NULL) {
+		pr_err("%s: mmap APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: Session[%d]\n", __func__, ac->session);
+
+	cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
+	q6asm_add_mmaphdr(ac, &mem_unmap.hdr, cmd_size, dir);
+	atomic_set(&ac->mem_state, -1);
+	port = &ac->port[dir];
+	buf_add = port->buf->phys;
+	mem_unmap.hdr.opcode = ASM_CMD_SHARED_MEM_UNMAP_REGIONS;
+	mem_unmap.mem_map_handle = 0;
+	list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+						list);
+		if (buf_node->buf_phys_addr == buf_add) {
+			pr_debug("%s: Found the element\n", __func__);
+			mem_unmap.mem_map_handle = buf_node->mmap_hdl;
+			break;
+		}
+	}
+
+	pr_debug("%s: mem_unmap-mem_map_handle: 0x%x\n",
+			__func__, mem_unmap.mem_map_handle);
+
+	if (mem_unmap.mem_map_handle == 0) {
+		pr_err("%s: Do not send null mem handle to DSP\n", __func__);
+		rc = 0;
+		goto fail_cmd;
+	}
+	rc = apr_send_pkt(ac->mmap_apr, (uint32_t *) &mem_unmap);
+	if (rc < 0) {
+		pr_err("mmap_regions op[0x%x]rc[%d]\n",
+				mem_unmap.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->mem_wait,
+			(atomic_read(&ac->mem_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for memory_unmap of handle 0x%x\n",
+			__func__, mem_unmap.mem_map_handle);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	} else if (atomic_read(&ac->mem_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->mem_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->mem_state));
+		goto fail_cmd;
+	} else if (atomic_read(&ac->unmap_cb_success) == 0) {
+		pr_err("%s: Error in mem unmap callback of handle 0x%x\n",
+			__func__, mem_unmap.mem_map_handle);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = 0;
+
+fail_cmd:
+	list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+						list);
+		if (buf_node->buf_phys_addr == buf_add) {
+			list_del(&buf_node->list);
+			kfree(buf_node);
+			break;
+		}
+	}
+	return rc;
+}
+
+int q6asm_set_lrgain(struct audio_client *ac, int left_gain, int right_gain)
+{
+	struct asm_volume_ctrl_multichannel_gain multi_ch_gain;
+	int sz = 0;
+	int rc  = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	memset(&multi_ch_gain, 0, sizeof(multi_ch_gain));
+	sz = sizeof(struct asm_volume_ctrl_multichannel_gain);
+	q6asm_add_hdr_async(ac, &multi_ch_gain.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	multi_ch_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	multi_ch_gain.param.data_payload_addr_lsw = 0;
+	multi_ch_gain.param.data_payload_addr_msw = 0;
+	multi_ch_gain.param.mem_map_handle = 0;
+	multi_ch_gain.param.data_payload_size = sizeof(multi_ch_gain) -
+		sizeof(multi_ch_gain.hdr) - sizeof(multi_ch_gain.param);
+	multi_ch_gain.data.module_id = ASM_MODULE_ID_VOL_CTRL;
+	multi_ch_gain.data.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN;
+	multi_ch_gain.data.param_size = multi_ch_gain.param.data_payload_size -
+		sizeof(multi_ch_gain.data);
+	multi_ch_gain.data.reserved = 0;
+	multi_ch_gain.gain_data[0].channeltype = PCM_CHANNEL_FL;
+	multi_ch_gain.gain_data[0].gain = left_gain << 15;
+	multi_ch_gain.gain_data[1].channeltype = PCM_CHANNEL_FR;
+	multi_ch_gain.gain_data[1].gain = right_gain << 15;
+	multi_ch_gain.num_channels = 2;
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &multi_ch_gain);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, multi_ch_gain.data.param_id, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+				multi_ch_gain.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] , set-params paramid[0x%x]\n",
+					__func__, adsp_err_get_err_str(
+					atomic_read(&ac->cmd_state)),
+					multi_ch_gain.data.param_id);
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+/*
+ * q6asm_set_multich_gain: set multiple channel gains on an ASM session
+ * @ac: audio client handle
+ * @channels: number of channels caller intends to set gains
+ * @gains: list of gains of audio channels
+ * @ch_map: list of channel mapping. Only valid if use_default is false
+ * @use_default: flag to indicate whether to use default mapping
+ */
+int q6asm_set_multich_gain(struct audio_client *ac, uint32_t channels,
+			   uint32_t *gains, uint8_t *ch_map, bool use_default)
+{
+	struct asm_volume_ctrl_multichannel_gain multich_gain;
+	int sz = 0;
+	int rc  = 0;
+	int i;
+	u8 default_chmap[VOLUME_CONTROL_MAX_CHANNELS];
+
+	if (ac == NULL) {
+		pr_err("%s: ac is NULL\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+	if (ac->apr == NULL) {
+		dev_err(ac->dev, "%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+	if (gains == NULL) {
+		dev_err(ac->dev, "%s: gain_list is NULL\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+	if (channels > VOLUME_CONTROL_MAX_CHANNELS) {
+		dev_err(ac->dev, "%s: Invalid channel count %d\n",
+			__func__, channels);
+		rc = -EINVAL;
+		goto done;
+	}
+	if (!use_default && ch_map == NULL) {
+		dev_err(ac->dev, "%s: NULL channel map\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	memset(&multich_gain, 0, sizeof(multich_gain));
+	sz = sizeof(struct asm_volume_ctrl_multichannel_gain);
+	q6asm_add_hdr_async(ac, &multich_gain.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, 1);
+	multich_gain.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	multich_gain.param.data_payload_addr_lsw = 0;
+	multich_gain.param.data_payload_addr_msw = 0;
+	multich_gain.param.mem_map_handle = 0;
+	multich_gain.param.data_payload_size = sizeof(multich_gain) -
+		sizeof(multich_gain.hdr) - sizeof(multich_gain.param);
+	multich_gain.data.module_id = ASM_MODULE_ID_VOL_CTRL;
+	multich_gain.data.param_id = ASM_PARAM_ID_MULTICHANNEL_GAIN;
+	multich_gain.data.param_size = multich_gain.param.data_payload_size -
+		sizeof(multich_gain.data);
+	multich_gain.data.reserved = 0;
+
+	if (use_default) {
+		rc = q6asm_map_channels(default_chmap, channels, false);
+		if (rc < 0)
+			goto done;
+		for (i = 0; i < channels; i++) {
+			multich_gain.gain_data[i].channeltype =
+				default_chmap[i];
+			multich_gain.gain_data[i].gain = gains[i] << 15;
+		}
+	} else {
+		for (i = 0; i < channels; i++) {
+			multich_gain.gain_data[i].channeltype = ch_map[i];
+			multich_gain.gain_data[i].gain = gains[i] << 15;
+		}
+	}
+	multich_gain.num_channels = channels;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &multich_gain);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, multich_gain.data.param_id, rc);
+		goto done;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) <= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+				multich_gain.data.param_id);
+		rc = -EINVAL;
+		goto done;
+	}
+	if (atomic_read(&ac->cmd_state) < 0) {
+		pr_err("%s: DSP returned error[%d] , set-params paramid[0x%x]\n",
+					__func__, atomic_read(&ac->cmd_state),
+					multich_gain.data.param_id);
+		rc = -EINVAL;
+		goto done;
+	}
+	rc = 0;
+done:
+	return rc;
+}
+
+int q6asm_set_mute(struct audio_client *ac, int muteflag)
+{
+	struct asm_volume_ctrl_mute_config mute;
+	int sz = 0;
+	int rc  = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	sz = sizeof(struct asm_volume_ctrl_mute_config);
+	q6asm_add_hdr_async(ac, &mute.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	mute.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	mute.param.data_payload_addr_lsw = 0;
+	mute.param.data_payload_addr_msw = 0;
+	mute.param.mem_map_handle = 0;
+	mute.param.data_payload_size = sizeof(mute) -
+		sizeof(mute.hdr) - sizeof(mute.param);
+	mute.data.module_id = ASM_MODULE_ID_VOL_CTRL;
+	mute.data.param_id = ASM_PARAM_ID_VOL_CTRL_MUTE_CONFIG;
+	mute.data.param_size = mute.param.data_payload_size - sizeof(mute.data);
+	mute.data.reserved = 0;
+	mute.mute_flag = muteflag;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &mute);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, mute.data.param_id, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+				mute.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)),
+				mute.data.param_id);
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_dts_eagle_set(struct audio_client *ac, int param_id, uint32_t size,
+			void *data, struct param_outband *po, int m_id)
+{
+	int rc = 0, *ob_params = NULL;
+	uint32_t sz = sizeof(struct asm_dts_eagle_param) + (po ? 0 : size);
+	struct asm_dts_eagle_param *ad;
+
+	if (!ac || ac->apr == NULL || (size == 0) || !data) {
+		pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK.\n",
+			__func__, size, data);
+		return -EINVAL;
+	}
+
+	ad = kzalloc(sz, GFP_KERNEL);
+	if (!ad)
+		return -ENOMEM;
+
+	pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n",
+		__func__, ac, param_id, size, data, m_id);
+	q6asm_add_hdr_async(ac, &ad->hdr, sz, 1);
+	ad->hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	ad->param.data_payload_addr_lsw = 0;
+	ad->param.data_payload_addr_msw = 0;
+
+	ad->param.mem_map_handle = 0;
+	ad->param.data_payload_size = size +
+					sizeof(struct asm_stream_param_data_v2);
+	ad->data.module_id = m_id;
+	ad->data.param_id = param_id;
+	ad->data.param_size = size;
+	ad->data.reserved = 0;
+	atomic_set(&ac->cmd_state, -1);
+
+	if (po) {
+		struct list_head *ptr, *next;
+		struct asm_buffer_node *node;
+
+		pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n",
+			__func__, po->kvaddr, (long)po->paddr);
+		ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr);
+		ad->param.data_payload_addr_msw =
+				msm_audio_populate_upper_32_bits(po->paddr);
+		list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
+			node = list_entry(ptr, struct asm_buffer_node, list);
+			if (node->buf_phys_addr == po->paddr) {
+				ad->param.mem_map_handle = node->mmap_hdl;
+				break;
+			}
+		}
+		if (ad->param.mem_map_handle == 0) {
+			pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n",
+				__func__);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+		/* check for integer overflow */
+		if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
+			rc = -EINVAL;
+		if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) {
+			pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n",
+				__func__, po->size, size + APR_CMD_OB_HDR_SZ);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+		ob_params = (int *)po->kvaddr;
+		*ob_params++ = m_id;
+		*ob_params++ = param_id;
+		*ob_params++ = size;
+		memcpy(ob_params, data, size);
+	} else {
+		pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__);
+		memcpy(((char *)ad) + sizeof(struct asm_dts_eagle_param),
+			data, size);
+	}
+	rc = apr_send_pkt(ac->apr, (uint32_t *)ad);
+	if (rc < 0) {
+		pr_err("DTS_EAGLE_ASM - %s: set-params send failed paramid[0x%x]\n",
+			__func__, ad->data.param_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+	if (!rc) {
+		pr_err("DTS_EAGLE_ASM - %s: timeout, set-params paramid[0x%x]\n",
+			__func__, ad->data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	kfree(ad);
+	return rc;
+}
+
+int q6asm_dts_eagle_get(struct audio_client *ac, int param_id, uint32_t size,
+			void *data, struct param_outband *po, int m_id)
+{
+	struct asm_dts_eagle_param_get *ad;
+	int rc = 0, *ob_params = NULL;
+	uint32_t sz = sizeof(struct asm_dts_eagle_param) + APR_CMD_GET_HDR_SZ +
+		 (po ? 0 : size);
+
+	if (!ac || ac->apr == NULL || (size == 0) || !data) {
+		pr_err("DTS_EAGLE_ASM - %s: APR handle NULL, invalid size %u or pointer %pK\n",
+			__func__, size, data);
+		return -EINVAL;
+	}
+	ad = kzalloc(sz, GFP_KERNEL);
+	if (!ad)
+		return -ENOMEM;
+
+	pr_debug("DTS_EAGLE_ASM - %s: ac %pK param_id 0x%x size %u data %pK m_id 0x%x\n",
+		__func__, ac, param_id, size, data, m_id);
+	q6asm_add_hdr(ac, &ad->hdr, sz, TRUE);
+	ad->hdr.opcode = ASM_STREAM_CMD_GET_PP_PARAMS_V2;
+	ad->param.data_payload_addr_lsw = 0;
+	ad->param.data_payload_addr_msw = 0;
+	ad->param.mem_map_handle = 0;
+	ad->param.module_id = m_id;
+	ad->param.param_id = param_id;
+	ad->param.param_max_size = size + APR_CMD_GET_HDR_SZ;
+	ad->param.reserved = 0;
+	atomic_set(&ac->cmd_state, -1);
+
+	generic_get_data = kzalloc(size + sizeof(struct generic_get_data_),
+				   GFP_KERNEL);
+	if (!generic_get_data) {
+		rc = -ENOMEM;
+		goto fail_cmd;
+	}
+
+	if (po) {
+		struct list_head *ptr, *next;
+		struct asm_buffer_node *node;
+
+		pr_debug("DTS_EAGLE_ASM - %s: using out of band memory (virtual %pK, physical %lu)\n",
+			 __func__, po->kvaddr, (long)po->paddr);
+		ad->param.data_payload_addr_lsw = lower_32_bits(po->paddr);
+		ad->param.data_payload_addr_msw =
+				msm_audio_populate_upper_32_bits(po->paddr);
+		list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
+			node = list_entry(ptr, struct asm_buffer_node, list);
+			if (node->buf_phys_addr == po->paddr) {
+				ad->param.mem_map_handle = node->mmap_hdl;
+				break;
+			}
+		}
+		if (ad->param.mem_map_handle == 0) {
+			pr_err("DTS_EAGLE_ASM - %s: mem map handle not found\n",
+				__func__);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+		/* check for integer overflow */
+		if (size > (UINT_MAX - APR_CMD_OB_HDR_SZ))
+			rc = -EINVAL;
+		if ((rc < 0) || (size + APR_CMD_OB_HDR_SZ > po->size)) {
+			pr_err("DTS_EAGLE_ASM - %s: ion alloc of size %zu too small for size requested %u\n",
+				__func__, po->size, size + APR_CMD_OB_HDR_SZ);
+			rc = -EINVAL;
+			goto fail_cmd;
+		}
+		ob_params = (int *)po->kvaddr;
+		*ob_params++ = m_id;
+		*ob_params++ = param_id;
+		*ob_params++ = size;
+		generic_get_data->is_inband = 0;
+	} else {
+		pr_debug("DTS_EAGLE_ASM - %s: using in band\n", __func__);
+		generic_get_data->is_inband = 1;
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *)ad);
+	if (rc < 0) {
+		pr_err("DTS_EAGLE_ASM - %s: Commmand 0x%x failed\n", __func__,
+			ad->hdr.opcode);
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+	if (!rc) {
+		pr_err("DTS_EAGLE_ASM - %s: timeout in get\n",
+			__func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	if (generic_get_data->valid) {
+		rc = 0;
+		memcpy(data, po ? ob_params : generic_get_data->ints, size);
+	} else {
+		rc = -EINVAL;
+		pr_err("DTS_EAGLE_ASM - %s: EAGLE get params problem getting data - check callback error value\n",
+			__func__);
+	}
+fail_cmd:
+	kfree(ad);
+	kfree(generic_get_data);
+	generic_get_data = NULL;
+	return rc;
+}
+
+static int __q6asm_set_volume(struct audio_client *ac, int volume, int instance)
+{
+	struct asm_volume_ctrl_master_gain vol;
+	int sz = 0;
+	int rc  = 0;
+	int module_id;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	switch (instance) {
+	case SOFT_VOLUME_INSTANCE_2:
+		module_id = ASM_MODULE_ID_VOL_CTRL2;
+		break;
+	case SOFT_VOLUME_INSTANCE_1:
+	default:
+		module_id = ASM_MODULE_ID_VOL_CTRL;
+		break;
+	}
+
+	sz = sizeof(struct asm_volume_ctrl_master_gain);
+	q6asm_add_hdr_async(ac, &vol.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	vol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	vol.param.data_payload_addr_lsw = 0;
+	vol.param.data_payload_addr_msw = 0;
+	vol.param.mem_map_handle = 0;
+	vol.param.data_payload_size = sizeof(vol) -
+		sizeof(vol.hdr) - sizeof(vol.param);
+	vol.data.module_id = module_id;
+	vol.data.param_id = ASM_PARAM_ID_VOL_CTRL_MASTER_GAIN;
+	vol.data.param_size = vol.param.data_payload_size - sizeof(vol.data);
+	vol.data.reserved = 0;
+	vol.master_gain = volume;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &vol);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, vol.data.param_id, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+				vol.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)),
+				vol.data.param_id);
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_set_volume(struct audio_client *ac, int volume)
+{
+	return __q6asm_set_volume(ac, volume, SOFT_VOLUME_INSTANCE_1);
+}
+
+int q6asm_set_volume_v2(struct audio_client *ac, int volume, int instance)
+{
+	return __q6asm_set_volume(ac, volume, instance);
+}
+
+int q6asm_set_softpause(struct audio_client *ac,
+			struct asm_softpause_params *pause_param)
+{
+	struct asm_soft_pause_params softpause;
+	int sz = 0;
+	int rc  = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	sz = sizeof(struct asm_soft_pause_params);
+	q6asm_add_hdr_async(ac, &softpause.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	softpause.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+
+	softpause.param.data_payload_addr_lsw = 0;
+	softpause.param.data_payload_addr_msw = 0;
+	softpause.param.mem_map_handle = 0;
+	softpause.param.data_payload_size = sizeof(softpause) -
+		sizeof(softpause.hdr) - sizeof(softpause.param);
+	softpause.data.module_id = ASM_MODULE_ID_VOL_CTRL;
+	softpause.data.param_id = ASM_PARAM_ID_SOFT_PAUSE_PARAMETERS;
+	softpause.data.param_size = softpause.param.data_payload_size -
+		sizeof(softpause.data);
+	softpause.data.reserved = 0;
+	softpause.enable_flag = pause_param->enable;
+	softpause.period = pause_param->period;
+	softpause.step = pause_param->step;
+	softpause.ramping_curve = pause_param->rampingcurve;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &softpause);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, softpause.data.param_id, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+						softpause.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)),
+				softpause.data.param_id);
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_set_softvolume(struct audio_client *ac,
+				  struct asm_softvolume_params *softvol_param,
+				  int instance)
+{
+	struct asm_soft_step_volume_params softvol;
+	int sz = 0;
+	int rc  = 0;
+	int module_id;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	switch (instance) {
+	case SOFT_VOLUME_INSTANCE_2:
+		module_id = ASM_MODULE_ID_VOL_CTRL2;
+		break;
+	case SOFT_VOLUME_INSTANCE_1:
+	default:
+		module_id = ASM_MODULE_ID_VOL_CTRL;
+		break;
+	}
+
+	sz = sizeof(struct asm_soft_step_volume_params);
+	q6asm_add_hdr_async(ac, &softvol.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	softvol.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	softvol.param.data_payload_addr_lsw = 0;
+	softvol.param.data_payload_addr_msw = 0;
+	softvol.param.mem_map_handle = 0;
+	softvol.param.data_payload_size = sizeof(softvol) -
+		sizeof(softvol.hdr) - sizeof(softvol.param);
+	softvol.data.module_id = module_id;
+	softvol.data.param_id = ASM_PARAM_ID_SOFT_VOL_STEPPING_PARAMETERS;
+	softvol.data.param_size = softvol.param.data_payload_size -
+		sizeof(softvol.data);
+	softvol.data.reserved = 0;
+	softvol.period = softvol_param->period;
+	softvol.step = softvol_param->step;
+	softvol.ramping_curve = softvol_param->rampingcurve;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &softvol);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, softvol.data.param_id, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+						softvol.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)),
+				softvol.data.param_id);
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_set_softvolume(struct audio_client *ac,
+			 struct asm_softvolume_params *softvol_param)
+{
+	return __q6asm_set_softvolume(ac, softvol_param,
+				      SOFT_VOLUME_INSTANCE_1);
+}
+
+int q6asm_set_softvolume_v2(struct audio_client *ac,
+			    struct asm_softvolume_params *softvol_param,
+			    int instance)
+{
+	return __q6asm_set_softvolume(ac, softvol_param, instance);
+}
+
+int q6asm_equalizer(struct audio_client *ac, void *eq_p)
+{
+	struct asm_eq_params eq;
+	struct msm_audio_eq_stream_config *eq_params = NULL;
+	int i  = 0;
+	int sz = 0;
+	int rc  = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (eq_p == NULL) {
+		pr_err("%s: [%d]: Invalid Eq param\n", __func__, ac->session);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	sz = sizeof(struct asm_eq_params);
+	eq_params = (struct msm_audio_eq_stream_config *) eq_p;
+	q6asm_add_hdr(ac, &eq.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	eq.hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	eq.param.data_payload_addr_lsw = 0;
+	eq.param.data_payload_addr_msw = 0;
+	eq.param.mem_map_handle = 0;
+	eq.param.data_payload_size = sizeof(eq) -
+		sizeof(eq.hdr) - sizeof(eq.param);
+	eq.data.module_id = ASM_MODULE_ID_EQUALIZER;
+	eq.data.param_id = ASM_PARAM_ID_EQUALIZER_PARAMETERS;
+	eq.data.param_size = eq.param.data_payload_size - sizeof(eq.data);
+	eq.enable_flag = eq_params->enable;
+	eq.num_bands = eq_params->num_bands;
+
+	pr_debug("%s: enable:%d numbands:%d\n", __func__, eq_params->enable,
+			eq_params->num_bands);
+	for (i = 0; i < eq_params->num_bands; i++) {
+		eq.eq_bands[i].band_idx =
+			eq_params->eq_bands[i].band_idx;
+		eq.eq_bands[i].filterype =
+			eq_params->eq_bands[i].filter_type;
+		eq.eq_bands[i].center_freq_hz =
+			eq_params->eq_bands[i].center_freq_hz;
+		eq.eq_bands[i].filter_gain =
+			eq_params->eq_bands[i].filter_gain;
+		eq.eq_bands[i].q_factor =
+			eq_params->eq_bands[i].q_factor;
+		pr_debug("%s: filter_type:%u bandnum:%d\n", __func__,
+				eq_params->eq_bands[i].filter_type, i);
+		pr_debug("%s: center_freq_hz:%u bandnum:%d\n", __func__,
+				eq_params->eq_bands[i].center_freq_hz, i);
+		pr_debug("%s: filter_gain:%d bandnum:%d\n", __func__,
+				eq_params->eq_bands[i].filter_gain, i);
+		pr_debug("%s: q_factor:%d bandnum:%d\n", __func__,
+				eq_params->eq_bands[i].q_factor, i);
+	}
+	rc = apr_send_pkt(ac->apr, (uint32_t *)&eq);
+	if (rc < 0) {
+		pr_err("%s: set-params send failed paramid[0x%x] rc %d\n",
+				__func__, eq.data.param_id, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, set-params paramid[0x%x]\n", __func__,
+						eq.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] set-params paramid[0x%x]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)),
+				eq.data.param_id);
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+}
+
+static int __q6asm_read(struct audio_client *ac, bool is_custom_len_reqd,
+			int len)
+{
+	struct asm_data_cmd_read_v2 read;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+	struct audio_buffer        *ab;
+	int dsp_buf;
+	struct audio_port_data     *port;
+	int rc;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[OUT];
+
+		q6asm_add_hdr(ac, &read.hdr, sizeof(read), FALSE);
+
+		mutex_lock(&port->lock);
+
+		dsp_buf = port->dsp_buf;
+		if (port->buf == NULL) {
+			pr_err("%s: buf is NULL\n", __func__);
+			mutex_unlock(&port->lock);
+			return -EINVAL;
+		}
+		ab = &port->buf[dsp_buf];
+
+		dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n",
+				__func__,
+				ac->session,
+				dsp_buf,
+				port->buf[dsp_buf].data,
+				port->cpu_buf,
+				&port->buf[port->cpu_buf].phys);
+
+		read.hdr.opcode = ASM_DATA_CMD_READ_V2;
+		read.buf_addr_lsw = lower_32_bits(ab->phys);
+		read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+
+		list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) {
+			buf_node = list_entry(ptr, struct asm_buffer_node,
+					list);
+			if (buf_node->buf_phys_addr == ab->phys)
+				read.mem_map_handle = buf_node->mmap_hdl;
+		}
+		dev_vdbg(ac->dev, "memory_map handle in q6asm_read: [%0x]:",
+				read.mem_map_handle);
+		read.buf_size = is_custom_len_reqd ? len : ab->size;
+		read.seq_id = port->dsp_buf;
+		q6asm_update_token(&read.hdr.token,
+				   0, /* Session ID is NA */
+				   0, /* Stream ID is NA */
+				   port->dsp_buf,
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+		port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+						   port->max_buf_cnt);
+		mutex_unlock(&port->lock);
+		dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n",
+				__func__, &ab->phys, read.hdr.token,
+				read.seq_id);
+		rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+		if (rc < 0) {
+			pr_err("%s: read op[0x%x]rc[%d]\n",
+					__func__, read.hdr.opcode, rc);
+			goto fail_cmd;
+		}
+		return 0;
+	}
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_read(struct audio_client *ac)
+{
+	return __q6asm_read(ac, false/*is_custom_len_reqd*/, 0);
+}
+int q6asm_read_v2(struct audio_client *ac, uint32_t len)
+{
+	return __q6asm_read(ac, true /*is_custom_len_reqd*/, len);
+}
+
+int q6asm_read_nolock(struct audio_client *ac)
+{
+	struct asm_data_cmd_read_v2 read;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+	struct audio_buffer        *ab;
+	int dsp_buf;
+	struct audio_port_data     *port;
+	int rc;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[OUT];
+
+		q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+
+		dsp_buf = port->dsp_buf;
+		ab = &port->buf[dsp_buf];
+
+		dev_vdbg(ac->dev, "%s: session[%d]dsp-buf[%d][%pK]cpu_buf[%d][%pK]\n",
+				__func__,
+				ac->session,
+				dsp_buf,
+				port->buf[dsp_buf].data,
+				port->cpu_buf,
+				&port->buf[port->cpu_buf].phys);
+
+		read.hdr.opcode = ASM_DATA_CMD_READ_V2;
+		read.buf_addr_lsw = lower_32_bits(ab->phys);
+		read.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+		read.buf_size = ab->size;
+		read.seq_id = port->dsp_buf;
+		q6asm_update_token(&read.hdr.token,
+				   0, /* Session ID is NA */
+				   0, /* Stream ID is NA */
+				   port->dsp_buf,
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+		list_for_each_safe(ptr, next, &ac->port[OUT].mem_map_handle) {
+			buf_node = list_entry(ptr, struct asm_buffer_node,
+					list);
+			if (buf_node->buf_phys_addr == ab->phys) {
+				read.mem_map_handle = buf_node->mmap_hdl;
+				break;
+			}
+		}
+
+		port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+						   port->max_buf_cnt);
+		dev_vdbg(ac->dev, "%s: buf add[%pK] token[0x%x] uid[%d]\n",
+				__func__, &ab->phys, read.hdr.token,
+				read.seq_id);
+		rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+		if (rc < 0) {
+			pr_err("%s: read op[0x%x]rc[%d]\n",
+					__func__, read.hdr.opcode, rc);
+			goto fail_cmd;
+		}
+		return 0;
+	}
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_async_write(struct audio_client *ac,
+					  struct audio_aio_write_param *param)
+{
+	int rc = 0;
+	struct asm_data_cmd_write_v2 write;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+	struct audio_buffer        *ab;
+	struct audio_port_data     *port;
+	phys_addr_t lbuf_phys_addr;
+	u32 liomode;
+	u32 io_compressed;
+	u32 io_compressed_stream;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6asm_stream_add_hdr_async(
+			ac, &write.hdr, sizeof(write), FALSE, ac->stream_id);
+	port = &ac->port[IN];
+	ab = &port->buf[port->dsp_buf];
+
+	/* Pass session id as token for AIO scheme */
+	write.hdr.token = param->uid;
+	write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+	write.buf_addr_lsw = lower_32_bits(param->paddr);
+	write.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr);
+	write.buf_size = param->len;
+	write.timestamp_msw = param->msw_ts;
+	write.timestamp_lsw = param->lsw_ts;
+	liomode = (ASYNC_IO_MODE | NT_MODE);
+	io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
+	io_compressed_stream = (ASYNC_IO_MODE | COMPRESSED_STREAM_IO);
+
+	if (ac->io_mode == liomode)
+		lbuf_phys_addr = (param->paddr - 32);
+	else if (ac->io_mode == io_compressed ||
+			ac->io_mode == io_compressed_stream)
+		lbuf_phys_addr = (param->paddr - param->metadata_len);
+	else
+		lbuf_phys_addr = param->paddr;
+
+	dev_vdbg(ac->dev, "%s: token[0x%x], buf_addr[%pK], buf_size[0x%x], ts_msw[0x%x], ts_lsw[0x%x], lbuf_phys_addr: 0x[%pK]\n",
+			__func__,
+			write.hdr.token, &param->paddr,
+			write.buf_size, write.timestamp_msw,
+			write.timestamp_lsw, &lbuf_phys_addr);
+
+	/* Use 0xFF00 for disabling timestamps */
+	if (param->flags == 0xFF00)
+		write.flags = (0x00000000 | (param->flags & 0x800000FF));
+	else
+		write.flags = (0x80000000 | param->flags);
+	write.flags |= param->last_buffer << ASM_SHIFT_LAST_BUFFER_FLAG;
+	write.seq_id = param->uid;
+	list_for_each_safe(ptr, next, &ac->port[IN].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+				list);
+		if (buf_node->buf_phys_addr == lbuf_phys_addr) {
+			write.mem_map_handle = buf_node->mmap_hdl;
+			break;
+		}
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+	if (rc < 0) {
+		pr_err("%s: write op[0x%x]rc[%d]\n", __func__,
+				write.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_async_read(struct audio_client *ac,
+					  struct audio_aio_read_param *param)
+{
+	int rc = 0;
+	struct asm_data_cmd_read_v2 read;
+	struct asm_buffer_node *buf_node = NULL;
+	struct list_head *ptr, *next;
+	phys_addr_t lbuf_phys_addr;
+	u32 liomode;
+	u32 io_compressed;
+	int dir = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6asm_add_hdr_async(ac, &read.hdr, sizeof(read), FALSE);
+
+	/* Pass session id as token for AIO scheme */
+	read.hdr.token = param->uid;
+	read.hdr.opcode = ASM_DATA_CMD_READ_V2;
+	read.buf_addr_lsw = lower_32_bits(param->paddr);
+	read.buf_addr_msw = msm_audio_populate_upper_32_bits(param->paddr);
+	read.buf_size = param->len;
+	read.seq_id = param->uid;
+	liomode = (NT_MODE | ASYNC_IO_MODE);
+	io_compressed = (ASYNC_IO_MODE | COMPRESSED_IO);
+	if (ac->io_mode == liomode) {
+		lbuf_phys_addr = (param->paddr - 32);
+		/*legacy wma driver case*/
+		dir = IN;
+	} else if (ac->io_mode == io_compressed) {
+		lbuf_phys_addr = (param->paddr - 64);
+		dir = OUT;
+	} else {
+		if (param->flags & COMPRESSED_TIMESTAMP_FLAG)
+			lbuf_phys_addr = param->paddr -
+				 sizeof(struct snd_codec_metadata);
+		else
+			lbuf_phys_addr = param->paddr;
+		dir = OUT;
+	}
+
+	list_for_each_safe(ptr, next, &ac->port[dir].mem_map_handle) {
+		buf_node = list_entry(ptr, struct asm_buffer_node,
+				list);
+		if (buf_node->buf_phys_addr == lbuf_phys_addr) {
+			read.mem_map_handle = buf_node->mmap_hdl;
+			break;
+		}
+	}
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &read);
+	if (rc < 0) {
+		pr_err("%s: read op[0x%x]rc[%d]\n", __func__,
+				read.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_write(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+		uint32_t lsw_ts, uint32_t flags)
+{
+	int rc = 0;
+	struct asm_data_cmd_write_v2 write;
+	struct asm_buffer_node *buf_node = NULL;
+	struct audio_port_data *port;
+	struct audio_buffer    *ab;
+	int dsp_buf = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_vdbg(ac->dev, "%s: session[%d] len=%d\n",
+			__func__, ac->session, len);
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[IN];
+
+		q6asm_add_hdr(ac, &write.hdr, sizeof(write),
+				FALSE);
+		mutex_lock(&port->lock);
+
+		dsp_buf = port->dsp_buf;
+		ab = &port->buf[dsp_buf];
+
+		q6asm_update_token(&write.hdr.token,
+				   0, /* Session ID is NA */
+				   0, /* Stream ID is NA */
+				   port->dsp_buf,
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+		write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+		write.buf_addr_lsw = lower_32_bits(ab->phys);
+		write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+		write.buf_size = len;
+		write.seq_id = port->dsp_buf;
+		write.timestamp_lsw = lsw_ts;
+		write.timestamp_msw = msw_ts;
+		/* Use 0xFF00 for disabling timestamps */
+		if (flags == 0xFF00)
+			write.flags = (0x00000000 | (flags & 0x800000FF));
+		else
+			write.flags = (0x80000000 | flags);
+		port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+						   port->max_buf_cnt);
+		buf_node = list_first_entry(&ac->port[IN].mem_map_handle,
+				struct asm_buffer_node,
+				list);
+		write.mem_map_handle = buf_node->mmap_hdl;
+
+		dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x] token[0x%x]buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]"
+				, __func__,
+				&ab->phys,
+				write.buf_addr_lsw,
+				write.hdr.token,
+				write.seq_id,
+				write.buf_size,
+				write.mem_map_handle);
+		mutex_unlock(&port->lock);
+
+		config_debug_fs_write(ab);
+
+		rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+		if (rc < 0) {
+			pr_err("%s: write op[0x%x]rc[%d]\n",
+					__func__, write.hdr.opcode, rc);
+			goto fail_cmd;
+		}
+		return 0;
+	}
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_write_nolock(struct audio_client *ac, uint32_t len, uint32_t msw_ts,
+			uint32_t lsw_ts, uint32_t flags)
+{
+	int rc = 0;
+	struct asm_data_cmd_write_v2 write;
+	struct asm_buffer_node *buf_node = NULL;
+	struct audio_port_data *port;
+	struct audio_buffer    *ab;
+	int dsp_buf = 0;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	dev_vdbg(ac->dev, "%s: session[%d] len=%d\n",
+			__func__, ac->session, len);
+	if (ac->io_mode & SYNC_IO_MODE) {
+		port = &ac->port[IN];
+
+		q6asm_add_hdr_async(ac, &write.hdr, sizeof(write),
+				FALSE);
+
+		dsp_buf = port->dsp_buf;
+		ab = &port->buf[dsp_buf];
+
+		q6asm_update_token(&write.hdr.token,
+				   0, /* Session ID is NA */
+				   0, /* Stream ID is NA */
+				   port->dsp_buf,
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+		write.hdr.opcode = ASM_DATA_CMD_WRITE_V2;
+		write.buf_addr_lsw = lower_32_bits(ab->phys);
+		write.buf_addr_msw = msm_audio_populate_upper_32_bits(ab->phys);
+		write.buf_size = len;
+		write.seq_id = port->dsp_buf;
+		write.timestamp_lsw = lsw_ts;
+		write.timestamp_msw = msw_ts;
+		buf_node = list_first_entry(&ac->port[IN].mem_map_handle,
+				struct asm_buffer_node,
+				list);
+		write.mem_map_handle = buf_node->mmap_hdl;
+		/* Use 0xFF00 for disabling timestamps */
+		if (flags == 0xFF00)
+			write.flags = (0x00000000 | (flags & 0x800000FF));
+		else
+			write.flags = (0x80000000 | flags);
+		port->dsp_buf = q6asm_get_next_buf(ac, port->dsp_buf,
+						   port->max_buf_cnt);
+
+		dev_vdbg(ac->dev, "%s: ab->phys[%pK]bufadd[0x%x]token[0x%x] buf_id[0x%x]buf_size[0x%x]mmaphdl[0x%x]"
+				, __func__,
+				&ab->phys,
+				write.buf_addr_lsw,
+				write.hdr.token,
+				write.seq_id,
+				write.buf_size,
+				write.mem_map_handle);
+
+		rc = apr_send_pkt(ac->apr, (uint32_t *) &write);
+		if (rc < 0) {
+			pr_err("%s: write op[0x%x]rc[%d]\n",
+					__func__, write.hdr.opcode, rc);
+			goto fail_cmd;
+		}
+		return 0;
+	}
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_get_session_time(struct audio_client *ac, uint64_t *tstamp)
+{
+	struct asm_mtmx_strtr_get_params mtmx_params;
+	int rc;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (tstamp == NULL) {
+		pr_err("%s: tstamp NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6asm_add_hdr(ac, &mtmx_params.hdr, sizeof(mtmx_params), TRUE);
+	mtmx_params.hdr.opcode = ASM_SESSION_CMD_GET_MTMX_STRTR_PARAMS_V2;
+	mtmx_params.param_info.data_payload_addr_lsw = 0;
+	mtmx_params.param_info.data_payload_addr_msw = 0;
+	mtmx_params.param_info.mem_map_handle = 0;
+	mtmx_params.param_info.direction = (ac->io_mode & TUN_READ_IO_MODE
+					    ? 1 : 0);
+	mtmx_params.param_info.module_id =
+		ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+	mtmx_params.param_info.param_id =
+		ASM_SESSION_MTMX_STRTR_PARAM_SESSION_TIME_V3;
+	mtmx_params.param_info.param_max_size =
+		sizeof(struct asm_stream_param_data_v2) +
+		sizeof(struct asm_session_mtmx_strtr_param_session_time_v3_t);
+	atomic_set(&ac->time_flag, 1);
+
+	dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__,
+		 ac->session, mtmx_params.hdr.opcode);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &mtmx_params);
+	if (rc < 0) {
+		pr_err("%s: Commmand 0x%x failed %d\n", __func__,
+		       mtmx_params.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->time_wait,
+			(atomic_read(&ac->time_flag) == 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout in getting session time from DSP\n",
+				__func__);
+		goto fail_cmd;
+	}
+
+	*tstamp = ac->time_stamp;
+	return 0;
+
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_get_session_time_legacy(struct audio_client *ac, uint64_t *tstamp)
+{
+	struct apr_hdr hdr;
+	int rc;
+
+	if (ac == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (tstamp == NULL) {
+		pr_err("%s: tstamp NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+	hdr.opcode = ASM_SESSION_CMD_GET_SESSIONTIME_V3;
+	atomic_set(&ac->time_flag, 1);
+
+	dev_vdbg(ac->dev, "%s: session[%d]opcode[0x%x]\n", __func__,
+			ac->session,
+			hdr.opcode);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+	if (rc < 0) {
+		pr_err("%s: Commmand 0x%x failed %d\n",
+				__func__, hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->time_wait,
+			(atomic_read(&ac->time_flag) == 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout in getting session time from DSP\n",
+				__func__);
+		goto fail_cmd;
+	}
+
+	*tstamp = ac->time_stamp;
+	return 0;
+
+fail_cmd:
+	return -EINVAL;
+}
+
+
+int q6asm_send_audio_effects_params(struct audio_client *ac, char *params,
+				    uint32_t params_length)
+{
+	char *asm_params = NULL;
+	struct apr_hdr hdr;
+	struct asm_stream_cmd_set_pp_params_v2 payload_params;
+	int sz, rc;
+
+	pr_debug("%s:\n", __func__);
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (params == NULL) {
+		pr_err("%s: params NULL\n", __func__);
+		return -EINVAL;
+	}
+	sz = sizeof(struct apr_hdr) +
+		sizeof(struct asm_stream_cmd_set_pp_params_v2) +
+		params_length;
+	asm_params = kzalloc(sz, GFP_KERNEL);
+	if (!asm_params) {
+		pr_err("%s, asm params memory alloc failed", __func__);
+		return -ENOMEM;
+	}
+	q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) +
+				sizeof(struct asm_stream_cmd_set_pp_params_v2) +
+				params_length), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	payload_params.data_payload_addr_lsw = 0;
+	payload_params.data_payload_addr_msw = 0;
+	payload_params.mem_map_handle = 0;
+	payload_params.data_payload_size = params_length;
+	memcpy(((u8 *)asm_params), &hdr, sizeof(struct apr_hdr));
+	memcpy(((u8 *)asm_params + sizeof(struct apr_hdr)), &payload_params,
+			sizeof(struct asm_stream_cmd_set_pp_params_v2));
+	memcpy(((u8 *)asm_params + sizeof(struct apr_hdr) +
+				sizeof(struct asm_stream_cmd_set_pp_params_v2)),
+			params, params_length);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
+	if (rc < 0) {
+		pr_err("%s: audio effects set-params send failed\n", __func__);
+		rc = -EINVAL;
+		goto fail_send_param;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 1*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, audio effects set-params\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_send_param;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s] set-params\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_send_param;
+	}
+
+	rc = 0;
+fail_send_param:
+	kfree(asm_params);
+	return rc;
+}
+
+int q6asm_send_mtmx_strtr_window(struct audio_client *ac,
+		struct asm_session_mtmx_strtr_param_window_v2_t *window_param,
+		uint32_t param_id)
+{
+	struct asm_mtmx_strtr_params matrix;
+	int sz = 0;
+	int rc  = 0;
+
+	pr_debug("%s: Window lsw is %d, window msw is %d\n", __func__,
+		  window_param->window_lsw, window_param->window_msw);
+
+	if (!ac) {
+		pr_err("%s: audio client handle is NULL\n", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	if (ac->apr == NULL) {
+		pr_err("%s: ac->apr is NULL", __func__);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	sz = sizeof(struct asm_mtmx_strtr_params);
+	q6asm_add_hdr(ac, &matrix.hdr, sz, TRUE);
+	atomic_set(&ac->cmd_state, -1);
+	matrix.hdr.opcode = ASM_SESSION_CMD_SET_MTMX_STRTR_PARAMS_V2;
+
+	matrix.param.data_payload_addr_lsw = 0;
+	matrix.param.data_payload_addr_msw = 0;
+	matrix.param.mem_map_handle = 0;
+	matrix.param.data_payload_size = sizeof(matrix) -
+			sizeof(matrix.hdr) - sizeof(matrix.param);
+	matrix.param.direction = 0; /* RX */
+	matrix.data.module_id = ASM_SESSION_MTMX_STRTR_MODULE_ID_AVSYNC;
+	matrix.data.param_id = param_id;
+	matrix.data.param_size = matrix.param.data_payload_size -
+			sizeof(matrix.data);
+	matrix.data.reserved = 0;
+	matrix.window_lsw = window_param->window_lsw;
+	matrix.window_msw = window_param->window_msw;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &matrix);
+	if (rc < 0) {
+		pr_err("%s: Render window start send failed paramid [0x%x]\n",
+			__func__, matrix.data.param_id);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout, Render window start paramid[0x%x]\n",
+			__func__, matrix.data.param_id);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+	rc = 0;
+fail_cmd:
+	return rc;
+};
+
+static int __q6asm_cmd(struct audio_client *ac, int cmd, uint32_t stream_id)
+{
+	struct apr_hdr hdr;
+	int rc;
+	atomic_t *state;
+	int cnt = 0;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	q6asm_stream_add_hdr(ac, &hdr, sizeof(hdr), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, -1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+			__func__, hdr.token, stream_id, ac->session);
+	switch (cmd) {
+	case CMD_PAUSE:
+		pr_debug("%s: CMD_PAUSE\n", __func__);
+		hdr.opcode = ASM_SESSION_CMD_PAUSE;
+		state = &ac->cmd_state;
+		break;
+	case CMD_SUSPEND:
+		pr_debug("%s: CMD_SUSPEND\n", __func__);
+		hdr.opcode = ASM_SESSION_CMD_SUSPEND;
+		state = &ac->cmd_state;
+		break;
+	case CMD_FLUSH:
+		pr_debug("%s: CMD_FLUSH\n", __func__);
+		hdr.opcode = ASM_STREAM_CMD_FLUSH;
+		state = &ac->cmd_state;
+		break;
+	case CMD_OUT_FLUSH:
+		pr_debug("%s: CMD_OUT_FLUSH\n", __func__);
+		hdr.opcode = ASM_STREAM_CMD_FLUSH_READBUFS;
+		state = &ac->cmd_state;
+		break;
+	case CMD_EOS:
+		pr_debug("%s: CMD_EOS\n", __func__);
+		hdr.opcode = ASM_DATA_CMD_EOS;
+		atomic_set(&ac->cmd_state, 0);
+		state = &ac->cmd_state;
+		break;
+	case CMD_CLOSE:
+		pr_debug("%s: CMD_CLOSE\n", __func__);
+		hdr.opcode = ASM_STREAM_CMD_CLOSE;
+		state = &ac->cmd_state;
+		break;
+	default:
+		pr_err("%s: Invalid format[%d]\n", __func__, cmd);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	pr_debug("%s: session[%d]opcode[0x%x]\n", __func__,
+			ac->session,
+			hdr.opcode);
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+	if (rc < 0) {
+		pr_err("%s: Commmand 0x%x failed %d\n",
+				__func__, hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait, (atomic_read(state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for response opcode[0x%x]\n",
+				__func__, hdr.opcode);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(state) > 0) {
+		pr_err("%s: DSP returned error[%s] opcode %d\n",
+					__func__, adsp_err_get_err_str(
+					atomic_read(state)),
+					hdr.opcode);
+		rc = adsp_err_get_lnx_err_code(atomic_read(state));
+		goto fail_cmd;
+	}
+
+	if (cmd == CMD_FLUSH)
+		q6asm_reset_buf_state(ac);
+	if (cmd == CMD_CLOSE) {
+		/* check if DSP return all buffers */
+		if (ac->port[IN].buf) {
+			for (cnt = 0; cnt < ac->port[IN].max_buf_cnt;
+					cnt++) {
+				if (ac->port[IN].buf[cnt].used == IN) {
+					dev_vdbg(ac->dev, "Write Buf[%d] not returned\n",
+							cnt);
+				}
+			}
+		}
+		if (ac->port[OUT].buf) {
+			for (cnt = 0; cnt < ac->port[OUT].max_buf_cnt; cnt++) {
+				if (ac->port[OUT].buf[cnt].used == OUT) {
+					dev_vdbg(ac->dev, "Read Buf[%d] not returned\n",
+							cnt);
+				}
+			}
+		}
+	}
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_cmd(struct audio_client *ac, int cmd)
+{
+	return __q6asm_cmd(ac, cmd, ac->stream_id);
+}
+
+int q6asm_stream_cmd(struct audio_client *ac, int cmd, uint32_t stream_id)
+{
+	return __q6asm_cmd(ac, cmd, stream_id);
+}
+
+static int __q6asm_cmd_nowait(struct audio_client *ac, int cmd,
+			      uint32_t stream_id)
+{
+	struct apr_hdr hdr;
+	int rc;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	q6asm_stream_add_hdr_async(ac, &hdr, sizeof(hdr), TRUE, stream_id);
+	atomic_set(&ac->cmd_state, 1);
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+			__func__, hdr.token, stream_id, ac->session);
+	switch (cmd) {
+	case CMD_PAUSE:
+		pr_debug("%s: CMD_PAUSE\n", __func__);
+		hdr.opcode = ASM_SESSION_CMD_PAUSE;
+		break;
+	case CMD_EOS:
+		pr_debug("%s: CMD_EOS\n", __func__);
+		hdr.opcode = ASM_DATA_CMD_EOS;
+		break;
+	case CMD_CLOSE:
+		pr_debug("%s: CMD_CLOSE\n", __func__);
+		hdr.opcode = ASM_STREAM_CMD_CLOSE;
+		break;
+	default:
+		pr_err("%s: Invalid format[%d]\n", __func__, cmd);
+		goto fail_cmd;
+	}
+	pr_debug("%s: session[%d]opcode[0x%x]\n", __func__,
+			ac->session,
+			hdr.opcode);
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+	if (rc < 0) {
+		pr_err("%s: Commmand 0x%x failed %d\n",
+				__func__, hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_cmd_nowait(struct audio_client *ac, int cmd)
+{
+	pr_debug("%s: stream_id: %d\n", __func__, ac->stream_id);
+	return __q6asm_cmd_nowait(ac, cmd, ac->stream_id);
+}
+
+int q6asm_stream_cmd_nowait(struct audio_client *ac, int cmd,
+			    uint32_t stream_id)
+{
+	pr_debug("%s: stream_id: %d\n", __func__, stream_id);
+	return __q6asm_cmd_nowait(ac, cmd, stream_id);
+}
+
+int __q6asm_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+			  uint32_t initial_samples, uint32_t trailing_samples)
+{
+	struct asm_data_cmd_remove_silence silence;
+	int rc = 0;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]\n", __func__, ac->session);
+	q6asm_stream_add_hdr_async(ac, &silence.hdr, sizeof(silence), FALSE,
+			stream_id);
+
+	/*
+	 * Updated the token field with stream/session for compressed playback
+	 * Platform driver must know the the stream with which the command is
+	 * associated
+	 */
+	if (ac->io_mode & COMPRESSED_STREAM_IO)
+		q6asm_update_token(&silence.hdr.token,
+				   ac->session,
+				   stream_id,
+				   0, /* Buffer index is NA */
+				   0, /* Direction flag is NA */
+				   WAIT_CMD);
+	pr_debug("%s: token = 0x%x, stream_id  %d, session 0x%x\n",
+			__func__, silence.hdr.token, stream_id, ac->session);
+
+	silence.hdr.opcode = ASM_DATA_CMD_REMOVE_INITIAL_SILENCE;
+	silence.num_samples_to_remove    = initial_samples;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &silence);
+	if (rc < 0) {
+		pr_err("%s: Commmand silence failed[%d]", __func__, rc);
+
+		goto fail_cmd;
+	}
+
+	silence.hdr.opcode = ASM_DATA_CMD_REMOVE_TRAILING_SILENCE;
+	silence.num_samples_to_remove    = trailing_samples;
+
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &silence);
+	if (rc < 0) {
+		pr_err("%s: Commmand silence failed[%d]", __func__, rc);
+		goto fail_cmd;
+	}
+
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
+int q6asm_stream_send_meta_data(struct audio_client *ac, uint32_t stream_id,
+		uint32_t initial_samples, uint32_t trailing_samples)
+{
+	return __q6asm_send_meta_data(ac, stream_id, initial_samples,
+				     trailing_samples);
+}
+
+int q6asm_send_meta_data(struct audio_client *ac, uint32_t initial_samples,
+		uint32_t trailing_samples)
+{
+	return __q6asm_send_meta_data(ac, ac->stream_id, initial_samples,
+				     trailing_samples);
+}
+
+static void q6asm_reset_buf_state(struct audio_client *ac)
+{
+	int cnt = 0;
+	int loopcnt = 0;
+	int used;
+	struct audio_port_data *port = NULL;
+
+	if (ac->io_mode & SYNC_IO_MODE) {
+		used = (ac->io_mode & TUN_WRITE_IO_MODE ? 1 : 0);
+		mutex_lock(&ac->cmd_lock);
+		for (loopcnt = 0; loopcnt <= OUT; loopcnt++) {
+			port = &ac->port[loopcnt];
+			cnt = port->max_buf_cnt - 1;
+			port->dsp_buf = 0;
+			port->cpu_buf = 0;
+			while (cnt >= 0) {
+				if (!port->buf)
+					continue;
+				port->buf[cnt].used = used;
+				cnt--;
+			}
+		}
+		mutex_unlock(&ac->cmd_lock);
+	}
+}
+
+int q6asm_reg_tx_overflow(struct audio_client *ac, uint16_t enable)
+{
+	struct asm_session_cmd_regx_overflow tx_overflow;
+	int rc;
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]enable[%d]\n", __func__,
+			ac->session, enable);
+	q6asm_add_hdr(ac, &tx_overflow.hdr, sizeof(tx_overflow), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	tx_overflow.hdr.opcode =
+				 ASM_SESSION_CMD_REGISTER_FORX_OVERFLOW_EVENTS;
+	/* tx overflow event: enable */
+	tx_overflow.enable_flag = enable;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &tx_overflow);
+	if (rc < 0) {
+		pr_err("%s: tx overflow op[0x%x]rc[%d]\n",
+				__func__, tx_overflow.hdr.opcode, rc);
+		rc = -EINVAL;
+		goto fail_cmd;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) >= 0), 5*HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for tx overflow\n", __func__);
+		rc = -ETIMEDOUT;
+		goto fail_cmd;
+	}
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		goto fail_cmd;
+	}
+
+	return 0;
+fail_cmd:
+	return rc;
+}
+
+int q6asm_reg_rx_underflow(struct audio_client *ac, uint16_t enable)
+{
+	struct asm_session_cmd_rgstr_rx_underflow rx_underflow;
+	int rc;
+
+	if (!ac) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: session[%d]enable[%d]\n", __func__,
+			ac->session, enable);
+	q6asm_add_hdr_async(ac, &rx_underflow.hdr, sizeof(rx_underflow), FALSE);
+
+	rx_underflow.hdr.opcode =
+		ASM_SESSION_CMD_REGISTER_FOR_RX_UNDERFLOW_EVENTS;
+	/* tx overflow event: enable */
+	rx_underflow.enable_flag = enable;
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &rx_underflow);
+	if (rc < 0) {
+		pr_err("%s: tx overflow op[0x%x]rc[%d]\n",
+				__func__, rx_underflow.hdr.opcode, rc);
+		goto fail_cmd;
+	}
+	return 0;
+fail_cmd:
+	return -EINVAL;
+}
+
+/*
+ * q6asm_get_path_delay() - get the path delay for an audio session
+ * @ac: audio client handle
+ *
+ * Retrieves the current audio DSP path delay for the given audio session.
+ *
+ * Return: 0 on success, error code otherwise
+ */
+int q6asm_get_path_delay(struct audio_client *ac)
+{
+	int rc = 0;
+	struct apr_hdr hdr;
+
+	if (!ac || ac->apr == NULL) {
+		pr_err("%s: invalid audio client\n", __func__);
+		return -EINVAL;
+	}
+
+	hdr.opcode = ASM_SESSION_CMD_GET_PATH_DELAY_V2;
+	q6asm_add_hdr(ac, &hdr, sizeof(hdr), TRUE);
+	atomic_set(&ac->cmd_state, -1);
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) &hdr);
+	if (rc < 0) {
+		pr_err("%s: Commmand 0x%x failed %d\n", __func__,
+				hdr.opcode, rc);
+		return rc;
+	}
+
+	rc = wait_event_timeout(ac->cmd_wait,
+			(atomic_read(&ac->cmd_state) >= 0), 5 * HZ);
+	if (!rc) {
+		pr_err("%s: timeout. waited for response opcode[0x%x]\n",
+				__func__, hdr.opcode);
+		return -ETIMEDOUT;
+	}
+
+	if (atomic_read(&ac->cmd_state) > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				atomic_read(&ac->cmd_state)));
+		rc = adsp_err_get_lnx_err_code(
+				atomic_read(&ac->cmd_state));
+		return rc;
+	}
+
+	return 0;
+}
+
+int q6asm_get_apr_service_id(int session_id)
+{
+	pr_debug("%s:\n", __func__);
+
+	if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+		pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+		return -EINVAL;
+	}
+
+	return ((struct apr_svc *)session[session_id]->apr)->id;
+}
+
+int q6asm_get_asm_topology(int session_id)
+{
+	int topology;
+
+	if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+		pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+		topology = -EINVAL;
+		goto done;
+	}
+
+	topology = session[session_id]->topology;
+done:
+	return topology;
+}
+
+int q6asm_get_asm_app_type(int session_id)
+{
+	int app_type;
+
+	if (session_id <= 0 || session_id > ASM_ACTIVE_STREAMS_ALLOWED) {
+		pr_err("%s: invalid session_id = %d\n", __func__, session_id);
+		app_type = -EINVAL;
+		goto done;
+	}
+
+	app_type = session[session_id]->app_type;
+done:
+	return app_type;
+}
+
+static int q6asm_get_asm_topology_cal(void)
+{
+	int topology = DEFAULT_POPP_TOPOLOGY;
+	struct cal_block_data *cal_block = NULL;
+
+	if (cal_data[ASM_TOPOLOGY_CAL] == NULL)
+		goto done;
+
+	mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+	cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]);
+	if (cal_block == NULL)
+		goto unlock;
+
+	topology = ((struct audio_cal_info_asm_top *)
+		cal_block->cal_info)->topology;
+unlock:
+	mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+done:
+	pr_debug("%s: Using topology %d\n", __func__, topology);
+	return topology;
+}
+
+static int q6asm_get_asm_app_type_cal(void)
+{
+	int app_type = DEFAULT_APP_TYPE;
+	struct cal_block_data *cal_block = NULL;
+
+	if (cal_data[ASM_TOPOLOGY_CAL] == NULL)
+		goto done;
+
+	mutex_lock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+	cal_block = cal_utils_get_only_cal_block(cal_data[ASM_TOPOLOGY_CAL]);
+	if (cal_block == NULL)
+		goto unlock;
+
+	app_type = ((struct audio_cal_info_asm_top *)
+		cal_block->cal_info)->app_type;
+
+	if (app_type == 0)
+		app_type = DEFAULT_APP_TYPE;
+unlock:
+	mutex_unlock(&cal_data[ASM_TOPOLOGY_CAL]->lock);
+done:
+	pr_debug("%s: Using app_type %d\n", __func__, app_type);
+	return app_type;
+}
+
+int q6asm_send_cal(struct audio_client *ac)
+{
+	struct cal_block_data *cal_block = NULL;
+	struct apr_hdr	hdr;
+	char *asm_params = NULL;
+	struct asm_stream_cmd_set_pp_params_v2 payload_params;
+	int sz, rc = -EINVAL;
+
+	pr_debug("%s:\n", __func__);
+
+	if (!ac) {
+		pr_err("%s: APR handle NULL\n", __func__);
+		goto done;
+	}
+	if (ac->apr == NULL) {
+		pr_err("%s: AC APR handle NULL\n", __func__);
+		goto done;
+	}
+	if (ac->io_mode & NT_MODE) {
+		pr_debug("%s: called for NT MODE, exiting\n", __func__);
+		goto done;
+	}
+
+	if (cal_data[ASM_AUDSTRM_CAL] == NULL)
+		goto done;
+
+	if (ac->perf_mode == ULTRA_LOW_LATENCY_PCM_MODE) {
+		rc = 0; /* no cal is required, not error case */
+		goto done;
+	}
+
+	mutex_lock(&cal_data[ASM_AUDSTRM_CAL]->lock);
+	cal_block = cal_utils_get_only_cal_block(cal_data[ASM_AUDSTRM_CAL]);
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL\n",
+			__func__);
+		goto unlock;
+	}
+
+	if (cal_block->cal_data.size == 0) {
+		rc = 0; /* not error case */
+		pr_debug("%s: cal_data.size is 0, don't send cal data\n",
+			__func__);
+		goto unlock;
+	}
+
+	rc = remap_cal_data(ASM_AUDSTRM_CAL_TYPE, cal_block);
+	if (rc) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, ASM_AUDSTRM_CAL);
+		goto unlock;
+	}
+
+	sz = sizeof(struct apr_hdr) +
+		sizeof(struct asm_stream_cmd_set_pp_params_v2);
+	asm_params = kzalloc(sz, GFP_KERNEL);
+	if (!asm_params) {
+		pr_err("%s, asm params memory alloc failed", __func__);
+		rc = -ENOMEM;
+		goto unlock;
+	}
+
+	/* asm_stream_cmd_set_pp_params_v2 has no APR header in it */
+	q6asm_add_hdr_async(ac, &hdr, (sizeof(struct apr_hdr) +
+		sizeof(struct asm_stream_cmd_set_pp_params_v2)), TRUE);
+
+	atomic_set(&ac->cmd_state, 1);
+	hdr.opcode = ASM_STREAM_CMD_SET_PP_PARAMS_V2;
+	payload_params.data_payload_addr_lsw =
+			lower_32_bits(cal_block->cal_data.paddr);
+	payload_params.data_payload_addr_msw =
+			msm_audio_populate_upper_32_bits(
+						cal_block->cal_data.paddr);
+	payload_params.mem_map_handle = cal_block->map_data.q6map_handle;
+	payload_params.data_payload_size = cal_block->cal_data.size;
+	memcpy(((u8 *)asm_params), &hdr, sizeof(struct apr_hdr));
+	memcpy(((u8 *)asm_params + sizeof(struct apr_hdr)), &payload_params,
+			sizeof(struct asm_stream_cmd_set_pp_params_v2));
+
+	pr_debug("%s: phyaddr lsw = %x msw = %x, maphdl = %x calsize = %d\n",
+		__func__, payload_params.data_payload_addr_lsw,
+		payload_params.data_payload_addr_msw,
+		payload_params.mem_map_handle,
+		payload_params.data_payload_size);
+
+	rc = apr_send_pkt(ac->apr, (uint32_t *) asm_params);
+	if (rc < 0) {
+		pr_err("%s: audio audstrm cal send failed\n", __func__);
+		rc = -EINVAL;
+		goto free;
+	}
+	rc = wait_event_timeout(ac->cmd_wait,
+				(atomic_read(&ac->cmd_state) <= 0), 5 * HZ);
+	if (!rc) {
+		pr_err("%s: timeout, audio audstrm cal send\n", __func__);
+		rc = -ETIMEDOUT;
+		goto free;
+	}
+	if (atomic_read(&ac->cmd_state) < 0) {
+		pr_err("%s: DSP returned error[%d] audio audstrm cal send\n",
+				__func__, atomic_read(&ac->cmd_state));
+		rc = -EINVAL;
+		goto free;
+	}
+
+	rc = 0;
+
+free:
+	kfree(asm_params);
+unlock:
+	mutex_unlock(&cal_data[ASM_AUDSTRM_CAL]->lock);
+done:
+	return rc;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+	int ret = -EINVAL;
+
+	switch (cal_type) {
+	case ASM_TOPOLOGY_CAL_TYPE:
+		ret = ASM_TOPOLOGY_CAL;
+		break;
+	case ASM_CUST_TOPOLOGY_CAL_TYPE:
+		ret = ASM_CUSTOM_TOP_CAL;
+		break;
+	case ASM_AUDSTRM_CAL_TYPE:
+		ret = ASM_AUDSTRM_CAL;
+		break;
+	case ASM_RTAC_APR_CAL_TYPE:
+		ret = ASM_RTAC_APR_CAL;
+		break;
+	default:
+		pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+	}
+	return ret;
+}
+
+static int q6asm_alloc_cal(int32_t cal_type,
+				size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_alloc_cal(data_size, data,
+		cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6asm_dealloc_cal(int32_t cal_type,
+				size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+		cal_data[cal_index]);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6asm_set_cal(int32_t cal_type,
+			size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_set_cal(data_size, data,
+		cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_index == ASM_CUSTOM_TOP_CAL) {
+		mutex_lock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+		set_custom_topology = 1;
+		mutex_unlock(&cal_data[ASM_CUSTOM_TOP_CAL]->lock);
+	}
+done:
+	return ret;
+}
+
+static void q6asm_delete_cal_data(void)
+{
+	pr_debug("%s:\n", __func__);
+	cal_utils_destroy_cal_types(ASM_MAX_CAL_TYPES, cal_data);
+}
+
+static int q6asm_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info	cal_type_info[] = {
+		{{ASM_TOPOLOGY_CAL_TYPE,
+		{NULL, NULL, NULL,
+		q6asm_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{ASM_CUST_TOPOLOGY_CAL_TYPE,
+		{q6asm_alloc_cal, q6asm_dealloc_cal, NULL,
+		q6asm_set_cal, NULL, NULL} },
+		{NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} },
+
+		{{ASM_AUDSTRM_CAL_TYPE,
+		{q6asm_alloc_cal, q6asm_dealloc_cal, NULL,
+		q6asm_set_cal, NULL, NULL} },
+		{NULL, q6asm_unmap_cal_memory, cal_utils_match_buf_num} },
+
+		{{ASM_RTAC_APR_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} }
+	};
+	pr_debug("%s\n", __func__);
+
+	ret = cal_utils_create_cal_types(ASM_MAX_CAL_TYPES, cal_data,
+		cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: could not create cal type! %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return ret;
+err:
+	q6asm_delete_cal_data();
+	return ret;
+}
+
+static int q6asm_is_valid_session(struct apr_client_data *data, void *priv)
+{
+	struct audio_client *ac = (struct audio_client *)priv;
+	union asm_token_struct asm_token;
+
+	asm_token.token = data->token;
+	if (asm_token._token.session_id != ac->session) {
+		pr_err("%s: Invalid session[%d] rxed expected[%d]",
+			__func__, asm_token._token.session_id, ac->session);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int __init q6asm_init(void)
+{
+	int lcnt, ret;
+
+	pr_debug("%s:\n", __func__);
+
+	memset(session, 0, sizeof(session));
+	set_custom_topology = 1;
+
+	/*setup common client used for cal mem map */
+	common_client.session = ASM_CONTROL_SESSION;
+	common_client.port[0].buf = &common_buf[0];
+	common_client.port[1].buf = &common_buf[1];
+	init_waitqueue_head(&common_client.cmd_wait);
+	init_waitqueue_head(&common_client.time_wait);
+	init_waitqueue_head(&common_client.mem_wait);
+	atomic_set(&common_client.time_flag, 1);
+	INIT_LIST_HEAD(&common_client.port[0].mem_map_handle);
+	INIT_LIST_HEAD(&common_client.port[1].mem_map_handle);
+	mutex_init(&common_client.cmd_lock);
+	for (lcnt = 0; lcnt <= OUT; lcnt++) {
+		mutex_init(&common_client.port[lcnt].lock);
+		spin_lock_init(&common_client.port[lcnt].dsp_lock);
+	}
+	atomic_set(&common_client.cmd_state, 0);
+	atomic_set(&common_client.mem_state, 0);
+
+	ret = q6asm_init_cal_data();
+	if (ret)
+		pr_err("%s: could not init cal data! ret %d\n",
+			__func__, ret);
+
+	config_debug_fs_init();
+
+	return 0;
+}
+
+static void __exit q6asm_exit(void)
+{
+	q6asm_delete_cal_data();
+}
+
+device_initcall(q6asm_init);
+__exitcall(q6asm_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6audio-v2.c b/sound/soc/msm/qdsp6v2/q6audio-v2.c
new file mode 100644
index 0000000..ea78e6a
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6audio-v2.c
@@ -0,0 +1,807 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/jiffies.h>
+#include <linux/uaccess.h>
+#include <linux/atomic.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6audio-v2.h>
+
+int q6audio_get_port_index(u16 port_id)
+{
+	switch (port_id) {
+	case PRIMARY_I2S_RX: return IDX_PRIMARY_I2S_RX;
+	case PRIMARY_I2S_TX: return IDX_PRIMARY_I2S_TX;
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_PCM_RX;
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+		return IDX_AFE_PORT_ID_PRIMARY_PCM_TX;
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_PCM_RX;
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+		return IDX_AFE_PORT_ID_SECONDARY_PCM_TX;
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+		return IDX_AFE_PORT_ID_TERTIARY_PCM_RX;
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+		return IDX_AFE_PORT_ID_TERTIARY_PCM_TX;
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_PCM_RX;
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_PCM_TX;
+	case SECONDARY_I2S_RX: return IDX_SECONDARY_I2S_RX;
+	case SECONDARY_I2S_TX: return IDX_SECONDARY_I2S_TX;
+	case MI2S_RX: return IDX_MI2S_RX;
+	case MI2S_TX: return IDX_MI2S_TX;
+	case HDMI_RX: return IDX_HDMI_RX;
+	case DISPLAY_PORT_RX: return IDX_DISPLAY_PORT_RX;
+	case AFE_PORT_ID_SPDIF_RX: return IDX_SPDIF_RX;
+	case RSVD_2: return IDX_RSVD_2;
+	case RSVD_3: return IDX_RSVD_3;
+	case DIGI_MIC_TX: return IDX_DIGI_MIC_TX;
+	case VOICE_RECORD_RX: return IDX_VOICE_RECORD_RX;
+	case VOICE_RECORD_TX: return IDX_VOICE_RECORD_TX;
+	case VOICE_PLAYBACK_TX: return IDX_VOICE_PLAYBACK_TX;
+	case VOICE2_PLAYBACK_TX: return IDX_VOICE2_PLAYBACK_TX;
+	case SLIMBUS_0_RX: return IDX_SLIMBUS_0_RX;
+	case SLIMBUS_0_TX: return IDX_SLIMBUS_0_TX;
+	case SLIMBUS_1_RX: return IDX_SLIMBUS_1_RX;
+	case SLIMBUS_1_TX: return IDX_SLIMBUS_1_TX;
+	case SLIMBUS_2_RX: return IDX_SLIMBUS_2_RX;
+	case SLIMBUS_2_TX: return IDX_SLIMBUS_2_TX;
+	case SLIMBUS_3_RX: return IDX_SLIMBUS_3_RX;
+	case SLIMBUS_3_TX: return IDX_SLIMBUS_3_TX;
+	case SLIMBUS_4_RX: return IDX_SLIMBUS_4_RX;
+	case SLIMBUS_4_TX: return IDX_SLIMBUS_4_TX;
+	case SLIMBUS_5_RX: return IDX_SLIMBUS_5_RX;
+	case SLIMBUS_5_TX: return IDX_SLIMBUS_5_TX;
+	case SLIMBUS_6_RX: return IDX_SLIMBUS_6_RX;
+	case SLIMBUS_6_TX: return IDX_SLIMBUS_6_TX;
+	case SLIMBUS_7_RX: return IDX_SLIMBUS_7_RX;
+	case SLIMBUS_7_TX: return IDX_SLIMBUS_7_TX;
+	case SLIMBUS_8_RX: return IDX_SLIMBUS_8_RX;
+	case SLIMBUS_8_TX: return IDX_SLIMBUS_8_TX;
+	case INT_BT_SCO_RX: return IDX_INT_BT_SCO_RX;
+	case INT_BT_SCO_TX: return IDX_INT_BT_SCO_TX;
+	case INT_BT_A2DP_RX: return IDX_INT_BT_A2DP_RX;
+	case INT_FM_RX: return IDX_INT_FM_RX;
+	case INT_FM_TX: return IDX_INT_FM_TX;
+	case RT_PROXY_PORT_001_RX: return IDX_RT_PROXY_PORT_001_RX;
+	case RT_PROXY_PORT_001_TX: return IDX_RT_PROXY_PORT_001_TX;
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_MI2S_RX;
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_PRIMARY_MI2S_TX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_RX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_MI2S_TX;
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX;
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_SECONDARY_MI2S_TX;
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+		return IDX_AFE_PORT_ID_TERTIARY_MI2S_RX;
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_TERTIARY_MI2S_TX;
+	case AUDIO_PORT_ID_I2S_RX:
+		return IDX_AUDIO_PORT_ID_I2S_RX;
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+		return IDX_AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_0;
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_0;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_RX_7;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_PRIMARY_TDM_TX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_0;
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_0;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_RX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_SECONDARY_TDM_TX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_0;
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_0;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_RX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_TERTIARY_TDM_TX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_0;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_0;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_RX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		return IDX_AFE_PORT_ID_QUATERNARY_TDM_TX_7;
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+		return IDX_AFE_PORT_ID_SENARY_MI2S_TX;
+	case AFE_PORT_ID_USB_RX:
+		return IDX_AFE_PORT_ID_USB_RX;
+	case AFE_PORT_ID_USB_TX:
+		return IDX_AFE_PORT_ID_USB_TX;
+	case AFE_PORT_ID_INT0_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT0_MI2S_RX;
+	case AFE_PORT_ID_INT0_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT0_MI2S_TX;
+	case AFE_PORT_ID_INT1_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT1_MI2S_RX;
+	case AFE_PORT_ID_INT1_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT1_MI2S_TX;
+	case AFE_PORT_ID_INT2_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT2_MI2S_RX;
+	case AFE_PORT_ID_INT2_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT2_MI2S_TX;
+	case AFE_PORT_ID_INT3_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT3_MI2S_RX;
+	case AFE_PORT_ID_INT3_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT3_MI2S_TX;
+	case AFE_PORT_ID_INT4_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT4_MI2S_RX;
+	case AFE_PORT_ID_INT4_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT4_MI2S_TX;
+	case AFE_PORT_ID_INT5_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT5_MI2S_RX;
+	case AFE_PORT_ID_INT5_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT5_MI2S_TX;
+	case AFE_PORT_ID_INT6_MI2S_RX:
+		return IDX_AFE_PORT_ID_INT6_MI2S_RX;
+	case AFE_PORT_ID_INT6_MI2S_TX:
+		return IDX_AFE_PORT_ID_INT6_MI2S_TX;
+	default: return -EINVAL;
+	}
+}
+
+int q6audio_get_port_id(u16 port_id)
+{
+	switch (port_id) {
+	case PRIMARY_I2S_RX: return PRIMARY_I2S_RX;
+	case PRIMARY_I2S_TX: return PRIMARY_I2S_TX;
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+			return AFE_PORT_ID_PRIMARY_PCM_RX;
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+			return AFE_PORT_ID_PRIMARY_PCM_TX;
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+			return AFE_PORT_ID_SECONDARY_PCM_RX;
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+			return AFE_PORT_ID_SECONDARY_PCM_TX;
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+			return AFE_PORT_ID_TERTIARY_PCM_RX;
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+			return AFE_PORT_ID_TERTIARY_PCM_TX;
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+			return AFE_PORT_ID_QUATERNARY_PCM_RX;
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+			return AFE_PORT_ID_QUATERNARY_PCM_TX;
+	case SECONDARY_I2S_RX: return AFE_PORT_ID_SECONDARY_MI2S_RX;
+	case SECONDARY_I2S_TX: return AFE_PORT_ID_SECONDARY_MI2S_TX;
+	case MI2S_RX: return AFE_PORT_ID_PRIMARY_MI2S_RX;
+	case MI2S_TX: return AFE_PORT_ID_PRIMARY_MI2S_TX;
+	case HDMI_RX: return AFE_PORT_ID_MULTICHAN_HDMI_RX;
+	case DISPLAY_PORT_RX:
+			return AFE_PORT_ID_HDMI_OVER_DP_RX;
+	case AFE_PORT_ID_SPDIF_RX: return AFE_PORT_ID_SPDIF_RX;
+	case RSVD_2: return IDX_RSVD_2;
+	case RSVD_3: return IDX_RSVD_3;
+	case DIGI_MIC_TX: return AFE_PORT_ID_DIGITAL_MIC_TX;
+	case VOICE_RECORD_RX: return AFE_PORT_ID_VOICE_RECORD_RX;
+	case VOICE_RECORD_TX: return AFE_PORT_ID_VOICE_RECORD_TX;
+	case VOICE_PLAYBACK_TX: return AFE_PORT_ID_VOICE_PLAYBACK_TX;
+	case VOICE2_PLAYBACK_TX: return AFE_PORT_ID_VOICE2_PLAYBACK_TX;
+	case SLIMBUS_0_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_RX;
+	case SLIMBUS_0_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_0_TX;
+	case SLIMBUS_1_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_RX;
+	case SLIMBUS_1_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_1_TX;
+	case SLIMBUS_2_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_RX;
+	case SLIMBUS_2_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_2_TX;
+	case SLIMBUS_3_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_RX;
+	case SLIMBUS_3_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_3_TX;
+	case SLIMBUS_4_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_RX;
+	case SLIMBUS_4_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_4_TX;
+	case SLIMBUS_5_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_RX;
+	case SLIMBUS_5_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_5_TX;
+	case SLIMBUS_6_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_RX;
+	case SLIMBUS_6_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_6_TX;
+	case SLIMBUS_7_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_RX;
+	case SLIMBUS_7_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_7_TX;
+	case SLIMBUS_8_RX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_RX;
+	case SLIMBUS_8_TX: return AFE_PORT_ID_SLIMBUS_MULTI_CHAN_8_TX;
+	case INT_BT_SCO_RX: return AFE_PORT_ID_INTERNAL_BT_SCO_RX;
+	case INT_BT_SCO_TX: return AFE_PORT_ID_INTERNAL_BT_SCO_TX;
+	case INT_BT_A2DP_RX: return AFE_PORT_ID_INTERNAL_BT_A2DP_RX;
+	case INT_FM_RX: return AFE_PORT_ID_INTERNAL_FM_RX;
+	case INT_FM_TX: return AFE_PORT_ID_INTERNAL_FM_TX;
+	case RT_PROXY_PORT_001_RX: return AFE_PORT_ID_RT_PROXY_PORT_001_RX;
+	case RT_PROXY_PORT_001_TX: return AFE_PORT_ID_RT_PROXY_PORT_001_TX;
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+			return AFE_PORT_ID_PRIMARY_MI2S_RX;
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+			return AFE_PORT_ID_PRIMARY_MI2S_TX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+			return AFE_PORT_ID_QUATERNARY_MI2S_RX;
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+			return AFE_PORT_ID_QUATERNARY_MI2S_TX;
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+			return AFE_PORT_ID_SECONDARY_MI2S_RX;
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+			return AFE_PORT_ID_SECONDARY_MI2S_TX;
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+			return AFE_PORT_ID_TERTIARY_MI2S_RX;
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+			return AFE_PORT_ID_TERTIARY_MI2S_TX;
+	case AUDIO_PORT_ID_I2S_RX:
+			return AUDIO_PORT_ID_I2S_RX;
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+			return AFE_PORT_ID_SECONDARY_MI2S_RX_SD1;
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+		return AFE_PORT_ID_PRIMARY_TDM_RX;
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+		return AFE_PORT_ID_PRIMARY_TDM_TX;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_1;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_2;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_3;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_4;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_5;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_6;
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+		return AFE_PORT_ID_PRIMARY_TDM_RX_7;
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+		return AFE_PORT_ID_PRIMARY_TDM_TX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+		return AFE_PORT_ID_SECONDARY_TDM_RX;
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+		return AFE_PORT_ID_SECONDARY_TDM_TX;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_1;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_2;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_3;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_4;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_5;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_6;
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+		return AFE_PORT_ID_SECONDARY_TDM_RX_7;
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+		return AFE_PORT_ID_SECONDARY_TDM_TX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+		return AFE_PORT_ID_TERTIARY_TDM_RX;
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+		return AFE_PORT_ID_TERTIARY_TDM_TX;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_1;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_2;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_3;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_4;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_5;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_6;
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+		return AFE_PORT_ID_TERTIARY_TDM_RX_7;
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+		return AFE_PORT_ID_TERTIARY_TDM_TX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_1;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_2;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_3;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_4;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_5;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_6;
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+		return AFE_PORT_ID_QUATERNARY_TDM_RX_7;
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+		return AFE_PORT_ID_QUATERNARY_TDM_TX_7;
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+		return AFE_PORT_ID_SENARY_MI2S_TX;
+	case AFE_PORT_ID_USB_RX:
+		return AFE_PORT_ID_USB_RX;
+	case AFE_PORT_ID_USB_TX:
+		return AFE_PORT_ID_USB_TX;
+	case AFE_PORT_ID_INT0_MI2S_RX:
+		return AFE_PORT_ID_INT0_MI2S_RX;
+	case AFE_PORT_ID_INT0_MI2S_TX:
+		return AFE_PORT_ID_INT0_MI2S_TX;
+	case AFE_PORT_ID_INT1_MI2S_RX:
+		return AFE_PORT_ID_INT1_MI2S_RX;
+	case AFE_PORT_ID_INT1_MI2S_TX:
+		return AFE_PORT_ID_INT1_MI2S_TX;
+	case AFE_PORT_ID_INT2_MI2S_RX:
+		return AFE_PORT_ID_INT2_MI2S_RX;
+	case AFE_PORT_ID_INT2_MI2S_TX:
+		return AFE_PORT_ID_INT2_MI2S_TX;
+	case AFE_PORT_ID_INT3_MI2S_RX:
+		return AFE_PORT_ID_INT3_MI2S_RX;
+	case AFE_PORT_ID_INT3_MI2S_TX:
+		return AFE_PORT_ID_INT3_MI2S_TX;
+	case AFE_PORT_ID_INT4_MI2S_RX:
+		return AFE_PORT_ID_INT4_MI2S_RX;
+	case AFE_PORT_ID_INT4_MI2S_TX:
+		return AFE_PORT_ID_INT4_MI2S_TX;
+	case AFE_PORT_ID_INT5_MI2S_RX:
+		return AFE_PORT_ID_INT5_MI2S_RX;
+	case AFE_PORT_ID_INT5_MI2S_TX:
+		return AFE_PORT_ID_INT5_MI2S_TX;
+	case AFE_PORT_ID_INT6_MI2S_RX:
+		return AFE_PORT_ID_INT6_MI2S_RX;
+	case AFE_PORT_ID_INT6_MI2S_TX:
+		return AFE_PORT_ID_INT6_MI2S_TX;
+	default:
+		pr_warn("%s: Invalid port_id %d\n", __func__, port_id);
+		return -EINVAL;
+	}
+}
+int q6audio_convert_virtual_to_portid(u16 port_id)
+{
+	int ret;
+
+	/* if port_id is virtual, convert to physical..
+	 * if port_id is already physical, return physical
+	 */
+	if (q6audio_validate_port(port_id) < 0) {
+		if (port_id == RT_PROXY_DAI_001_RX ||
+			port_id == RT_PROXY_DAI_001_TX ||
+			port_id == RT_PROXY_DAI_002_RX ||
+			port_id == RT_PROXY_DAI_002_TX)
+			ret = VIRTUAL_ID_TO_PORTID(port_id);
+		else
+			ret = -EINVAL;
+	} else
+		ret = port_id;
+
+	return ret;
+}
+
+int q6audio_is_digital_pcm_interface(u16 port_id)
+{
+	int ret = 0;
+
+	switch (port_id) {
+	case PRIMARY_I2S_RX:
+	case PRIMARY_I2S_TX:
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
+	case MI2S_RX:
+	case MI2S_TX:
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+	case AUDIO_PORT_ID_I2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+	case AFE_PORT_ID_INT0_MI2S_RX:
+	case AFE_PORT_ID_INT0_MI2S_TX:
+	case AFE_PORT_ID_INT1_MI2S_RX:
+	case AFE_PORT_ID_INT1_MI2S_TX:
+	case AFE_PORT_ID_INT2_MI2S_RX:
+	case AFE_PORT_ID_INT2_MI2S_TX:
+	case AFE_PORT_ID_INT3_MI2S_RX:
+	case AFE_PORT_ID_INT3_MI2S_TX:
+	case AFE_PORT_ID_INT4_MI2S_RX:
+	case AFE_PORT_ID_INT4_MI2S_TX:
+	case AFE_PORT_ID_INT5_MI2S_RX:
+	case AFE_PORT_ID_INT5_MI2S_TX:
+	case AFE_PORT_ID_INT6_MI2S_RX:
+	case AFE_PORT_ID_INT6_MI2S_TX:
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+int q6audio_validate_port(u16 port_id)
+{
+	int ret;
+
+	switch (port_id) {
+	case PRIMARY_I2S_RX:
+	case PRIMARY_I2S_TX:
+	case AFE_PORT_ID_PRIMARY_PCM_RX:
+	case AFE_PORT_ID_PRIMARY_PCM_TX:
+	case AFE_PORT_ID_SECONDARY_PCM_RX:
+	case AFE_PORT_ID_SECONDARY_PCM_TX:
+	case AFE_PORT_ID_TERTIARY_PCM_RX:
+	case AFE_PORT_ID_TERTIARY_PCM_TX:
+	case AFE_PORT_ID_QUATERNARY_PCM_RX:
+	case AFE_PORT_ID_QUATERNARY_PCM_TX:
+	case SECONDARY_I2S_RX:
+	case SECONDARY_I2S_TX:
+	case MI2S_RX:
+	case MI2S_TX:
+	case HDMI_RX:
+	case DISPLAY_PORT_RX:
+	case RSVD_2:
+	case RSVD_3:
+	case DIGI_MIC_TX:
+	case VOICE_RECORD_RX:
+	case VOICE_RECORD_TX:
+	case VOICE_PLAYBACK_TX:
+	case VOICE2_PLAYBACK_TX:
+	case SLIMBUS_0_RX:
+	case SLIMBUS_0_TX:
+	case SLIMBUS_1_RX:
+	case SLIMBUS_1_TX:
+	case SLIMBUS_2_RX:
+	case SLIMBUS_2_TX:
+	case SLIMBUS_3_RX:
+	case SLIMBUS_3_TX:
+	case SLIMBUS_4_RX:
+	case SLIMBUS_4_TX:
+	case SLIMBUS_5_RX:
+	case SLIMBUS_5_TX:
+	case SLIMBUS_6_RX:
+	case SLIMBUS_6_TX:
+	case SLIMBUS_7_RX:
+	case SLIMBUS_7_TX:
+	case SLIMBUS_8_RX:
+	case SLIMBUS_8_TX:
+	case INT_BT_SCO_RX:
+	case INT_BT_SCO_TX:
+	case INT_BT_A2DP_RX:
+	case INT_FM_RX:
+	case INT_FM_TX:
+	case RT_PROXY_PORT_001_RX:
+	case RT_PROXY_PORT_001_TX:
+	case AFE_PORT_ID_PRIMARY_MI2S_RX:
+	case AFE_PORT_ID_PRIMARY_MI2S_TX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_RX:
+	case AFE_PORT_ID_QUATERNARY_MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX:
+	case AFE_PORT_ID_SECONDARY_MI2S_TX:
+	case AFE_PORT_ID_SPDIF_RX:
+	case AFE_PORT_ID_TERTIARY_MI2S_RX:
+	case AFE_PORT_ID_TERTIARY_MI2S_TX:
+	case AFE_PORT_ID_SECONDARY_MI2S_RX_SD1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX:
+	case AFE_PORT_ID_PRIMARY_TDM_TX:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_1:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_2:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_3:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_4:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_5:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_6:
+	case AFE_PORT_ID_PRIMARY_TDM_RX_7:
+	case AFE_PORT_ID_PRIMARY_TDM_TX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_RX:
+	case AFE_PORT_ID_SECONDARY_TDM_TX:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_1:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_2:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_3:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_4:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_5:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_6:
+	case AFE_PORT_ID_SECONDARY_TDM_RX_7:
+	case AFE_PORT_ID_SECONDARY_TDM_TX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_RX:
+	case AFE_PORT_ID_TERTIARY_TDM_TX:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_1:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_2:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_3:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_4:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_5:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_6:
+	case AFE_PORT_ID_TERTIARY_TDM_RX_7:
+	case AFE_PORT_ID_TERTIARY_TDM_TX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_1:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_2:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_3:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_4:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_5:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_6:
+	case AFE_PORT_ID_QUATERNARY_TDM_RX_7:
+	case AFE_PORT_ID_QUATERNARY_TDM_TX_7:
+	case AFE_PORT_ID_SENARY_MI2S_TX:
+	case AFE_PORT_ID_USB_RX:
+	case AFE_PORT_ID_USB_TX:
+	case AFE_PORT_ID_INT0_MI2S_RX:
+	case AFE_PORT_ID_INT0_MI2S_TX:
+	case AFE_PORT_ID_INT1_MI2S_RX:
+	case AFE_PORT_ID_INT1_MI2S_TX:
+	case AFE_PORT_ID_INT2_MI2S_RX:
+	case AFE_PORT_ID_INT2_MI2S_TX:
+	case AFE_PORT_ID_INT3_MI2S_RX:
+	case AFE_PORT_ID_INT3_MI2S_TX:
+	case AFE_PORT_ID_INT4_MI2S_RX:
+	case AFE_PORT_ID_INT4_MI2S_TX:
+	case AFE_PORT_ID_INT5_MI2S_RX:
+	case AFE_PORT_ID_INT5_MI2S_TX:
+	case AFE_PORT_ID_INT6_MI2S_RX:
+	case AFE_PORT_ID_INT6_MI2S_TX:
+	{
+		ret = 0;
+		break;
+	}
+
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
diff --git a/sound/soc/msm/qdsp6v2/q6core.c b/sound/soc/msm/qdsp6v2/q6core.c
new file mode 100644
index 0000000..f544393
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6core.c
@@ -0,0 +1,950 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/qdsp6v2/apr.h>
+#include <sound/q6core.h>
+#include <sound/audio_cal_utils.h>
+
+#define TIMEOUT_MS 1000
+/*
+ * AVS bring up in the modem is optimitized for the new
+ * Sub System Restart design and 100 milliseconds timeout
+ * is sufficient to make sure the Q6 will be ready.
+ */
+#define Q6_READY_TIMEOUT_MS 100
+
+enum {
+	META_CAL,
+	CUST_TOP_CAL,
+	CORE_MAX_CAL
+};
+
+struct q6core_str {
+	struct apr_svc *core_handle_q;
+	wait_queue_head_t bus_bw_req_wait;
+	wait_queue_head_t cmd_req_wait;
+	u32 bus_bw_resp_received;
+	enum cmd_flags {
+		FLAG_NONE,
+		FLAG_CMDRSP_LICENSE_RESULT
+	} cmd_resp_received_flag;
+	struct mutex cmd_lock;
+	union {
+		struct avcs_cmdrsp_get_license_validation_result
+						cmdrsp_license_result;
+	} cmd_resp_payload;
+	u32 param;
+	struct cal_type_data *cal_data[CORE_MAX_CAL];
+	uint32_t mem_map_cal_handle;
+	int32_t adsp_status;
+};
+
+static struct q6core_str q6core_lcl;
+
+struct generic_get_data_ {
+	int valid;
+	int size_in_ints;
+	int ints[];
+};
+static struct generic_get_data_ *generic_get_data;
+
+static int32_t aprv2_core_fn_q(struct apr_client_data *data, void *priv)
+{
+	uint32_t *payload1;
+
+	if (data == NULL) {
+		pr_err("%s: data argument is null\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: core msg: payload len = %u, apr resp opcode = 0x%x\n",
+		__func__,
+		data->payload_size, data->opcode);
+
+	switch (data->opcode) {
+
+	case APR_BASIC_RSP_RESULT:{
+
+		if (data->payload_size == 0) {
+			pr_err("%s: APR_BASIC_RSP_RESULT No Payload ",
+					__func__);
+			return 0;
+		}
+
+		payload1 = data->payload;
+
+		switch (payload1[0]) {
+
+		case AVCS_CMD_SHARED_MEM_UNMAP_REGIONS:
+			pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS status[0x%x]\n",
+				__func__, payload1[1]);
+			q6core_lcl.bus_bw_resp_received = 1;
+			wake_up(&q6core_lcl.bus_bw_req_wait);
+			break;
+		case AVCS_CMD_SHARED_MEM_MAP_REGIONS:
+			pr_debug("%s: Cmd = AVCS_CMD_SHARED_MEM_MAP_REGIONS status[0x%x]\n",
+				__func__, payload1[1]);
+			q6core_lcl.bus_bw_resp_received = 1;
+			wake_up(&q6core_lcl.bus_bw_req_wait);
+			break;
+		case AVCS_CMD_REGISTER_TOPOLOGIES:
+			pr_debug("%s: Cmd = AVCS_CMD_REGISTER_TOPOLOGIES status[0x%x]\n",
+				__func__, payload1[1]);
+			/* -ADSP status to match Linux error standard */
+			q6core_lcl.adsp_status = -payload1[1];
+			q6core_lcl.bus_bw_resp_received = 1;
+			wake_up(&q6core_lcl.bus_bw_req_wait);
+			break;
+		case AVCS_CMD_DEREGISTER_TOPOLOGIES:
+			pr_debug("%s: Cmd = AVCS_CMD_DEREGISTER_TOPOLOGIES status[0x%x]\n",
+				__func__, payload1[1]);
+			q6core_lcl.bus_bw_resp_received = 1;
+			wake_up(&q6core_lcl.bus_bw_req_wait);
+			break;
+		default:
+			pr_err("%s: Invalid cmd rsp[0x%x][0x%x] opcode %d\n",
+					__func__,
+					payload1[0], payload1[1], data->opcode);
+			break;
+		}
+		break;
+	}
+
+	case RESET_EVENTS:{
+		pr_debug("%s: Reset event received in Core service\n",
+			__func__);
+		apr_reset(q6core_lcl.core_handle_q);
+		q6core_lcl.core_handle_q = NULL;
+		break;
+	}
+	case AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS:
+		payload1 = data->payload;
+		pr_debug("%s: AVCS_CMDRSP_SHARED_MEM_MAP_REGIONS handle %d\n",
+			__func__, payload1[0]);
+		q6core_lcl.mem_map_cal_handle = payload1[0];
+		q6core_lcl.bus_bw_resp_received = 1;
+		wake_up(&q6core_lcl.bus_bw_req_wait);
+		break;
+	case AVCS_CMDRSP_ADSP_EVENT_GET_STATE:
+		payload1 = data->payload;
+		q6core_lcl.param = payload1[0];
+		pr_debug("%s: Received ADSP get state response 0x%x\n",
+			 __func__, q6core_lcl.param);
+		/* ensure .param is updated prior to .bus_bw_resp_received */
+		wmb();
+		q6core_lcl.bus_bw_resp_received = 1;
+		wake_up(&q6core_lcl.bus_bw_req_wait);
+		break;
+	case AVCS_CMDRSP_GET_LICENSE_VALIDATION_RESULT:
+		payload1 = data->payload;
+		pr_debug("%s: cmd = LICENSE_VALIDATION_RESULT, result = 0x%x\n",
+				__func__, payload1[0]);
+		q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result
+								= payload1[0];
+		q6core_lcl.cmd_resp_received_flag = FLAG_CMDRSP_LICENSE_RESULT;
+		wake_up(&q6core_lcl.cmd_req_wait);
+		break;
+	default:
+		pr_err("%s: Message id from adsp core svc: 0x%x\n",
+			__func__, data->opcode);
+		if (generic_get_data) {
+			generic_get_data->valid = 1;
+			generic_get_data->size_in_ints =
+				data->payload_size/sizeof(int);
+			pr_debug("DTS_EAGLE_CORE callback size = %i\n",
+				 data->payload_size);
+			memcpy(generic_get_data->ints, data->payload,
+				data->payload_size);
+			q6core_lcl.bus_bw_resp_received = 1;
+			wake_up(&q6core_lcl.bus_bw_req_wait);
+			break;
+		}
+		break;
+	}
+
+	return 0;
+}
+
+void ocm_core_open(void)
+{
+	if (q6core_lcl.core_handle_q == NULL)
+		q6core_lcl.core_handle_q = apr_register("ADSP", "CORE",
+					aprv2_core_fn_q, 0xFFFFFFFF, NULL);
+	pr_debug("%s: Open_q %pK\n", __func__, q6core_lcl.core_handle_q);
+	if (q6core_lcl.core_handle_q == NULL)
+		pr_err("%s: Unable to register CORE\n", __func__);
+}
+
+int32_t core_set_license(uint32_t key, uint32_t module_id)
+{
+	struct avcs_cmd_set_license *cmd_setl = NULL;
+	struct audio_cal_info_metainfo *metainfo = NULL;
+	struct cal_block_data *cal_block = NULL;
+	int rc = 0, packet_size = 0;
+
+	pr_debug("%s: key:0x%x, id:0x%x\n", __func__, key, module_id);
+
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	if (q6core_lcl.cal_data[META_CAL] == NULL) {
+		pr_err("%s: cal_data not initialized yet!!\n", __func__);
+		rc = -EINVAL;
+		goto cmd_unlock;
+	}
+
+	mutex_lock(&((q6core_lcl.cal_data[META_CAL])->lock));
+	cal_block =
+		cal_utils_get_only_cal_block(q6core_lcl.cal_data[META_CAL]);
+	if (cal_block == NULL ||
+		cal_block->cal_data.kvaddr == NULL ||
+		cal_block->cal_data.size <= 0) {
+		pr_err("%s: Invalid cal block to send", __func__);
+		rc = -EINVAL;
+		goto cal_data_unlock;
+	}
+	metainfo = (struct audio_cal_info_metainfo *)cal_block->cal_info;
+	if (metainfo == NULL) {
+		pr_err("%s: No metainfo!!!", __func__);
+		rc = -EINVAL;
+		goto cal_data_unlock;
+	}
+	if (metainfo->nKey != key) {
+		pr_err("%s: metainfo key mismatch!!! found:%x, needed:%x\n",
+				__func__, metainfo->nKey, key);
+		rc = -EINVAL;
+		goto cal_data_unlock;
+	} else if (key == 0) {
+		pr_err("%s: metainfo key is %d a invalid key", __func__, key);
+		goto cal_data_unlock;
+	}
+
+	packet_size = sizeof(struct avcs_cmd_set_license) +
+					cal_block->cal_data.size;
+	/*round up total packet_size to next 4 byte boundary*/
+	packet_size = ((packet_size + 0x3)>>2)<<2;
+
+	cmd_setl = kzalloc(packet_size, GFP_KERNEL);
+	if (cmd_setl == NULL) {
+		rc  = -ENOMEM;
+		goto cal_data_unlock;
+	}
+
+	ocm_core_open();
+	if (q6core_lcl.core_handle_q == NULL) {
+		pr_err("%s: apr registration for CORE failed\n", __func__);
+		rc  = -ENODEV;
+		goto fail_cmd;
+	}
+
+	cmd_setl->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cmd_setl->hdr.pkt_size = packet_size;
+	cmd_setl->hdr.src_port = 0;
+	cmd_setl->hdr.dest_port = 0;
+	cmd_setl->hdr.token = 0;
+	cmd_setl->hdr.opcode = AVCS_CMD_SET_LICENSE;
+	cmd_setl->id = module_id;
+	cmd_setl->overwrite = 1;
+	cmd_setl->size = cal_block->cal_data.size;
+	memcpy((uint8_t *)cmd_setl + sizeof(struct avcs_cmd_set_license),
+		cal_block->cal_data.kvaddr,
+		cal_block->cal_data.size);
+	pr_info("%s: Set license opcode=0x%x ,key=0x%x, id =0x%x, size = %d\n",
+			__func__, cmd_setl->hdr.opcode,
+			metainfo->nKey, cmd_setl->id, cmd_setl->size);
+	rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)cmd_setl);
+	if (rc < 0)
+		pr_err("%s: SET_LICENSE failed op[0x%x]rc[%d]\n",
+					__func__, cmd_setl->hdr.opcode, rc);
+
+fail_cmd:
+	kfree(cmd_setl);
+cal_data_unlock:
+	mutex_unlock(&((q6core_lcl.cal_data[META_CAL])->lock));
+cmd_unlock:
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+
+	return rc;
+}
+
+int32_t core_get_license_status(uint32_t module_id)
+{
+	struct avcs_cmd_get_license_validation_result get_lvr_cmd;
+	int ret = 0;
+
+	pr_debug("%s: module_id 0x%x", __func__, module_id);
+
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	ocm_core_open();
+	if (q6core_lcl.core_handle_q == NULL) {
+		pr_err("%s: apr registration for CORE failed\n", __func__);
+		ret  = -ENODEV;
+		goto fail_cmd;
+	}
+
+	get_lvr_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	get_lvr_cmd.hdr.pkt_size =
+		sizeof(struct avcs_cmd_get_license_validation_result);
+
+	get_lvr_cmd.hdr.src_port = 0;
+	get_lvr_cmd.hdr.dest_port = 0;
+	get_lvr_cmd.hdr.token = 0;
+	get_lvr_cmd.hdr.opcode = AVCS_CMD_GET_LICENSE_VALIDATION_RESULT;
+	get_lvr_cmd.id = module_id;
+
+
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &get_lvr_cmd);
+	if (ret < 0) {
+		pr_err("%s: license_validation request failed, err %d\n",
+							__func__, ret);
+		ret = -EREMOTE;
+		goto fail_cmd;
+	}
+
+	q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT);
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+	ret = wait_event_timeout(q6core_lcl.cmd_req_wait,
+			(q6core_lcl.cmd_resp_received_flag ==
+				FLAG_CMDRSP_LICENSE_RESULT),
+				msecs_to_jiffies(TIMEOUT_MS));
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	if (!ret) {
+		pr_err("%s: wait_event timeout for CMDRSP_LICENSE_RESULT\n",
+				__func__);
+		ret = -ETIME;
+		goto fail_cmd;
+	}
+	q6core_lcl.cmd_resp_received_flag &= ~(FLAG_CMDRSP_LICENSE_RESULT);
+	ret = q6core_lcl.cmd_resp_payload.cmdrsp_license_result.result;
+
+fail_cmd:
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+	pr_info("%s: cmdrsp_license_result.result = 0x%x for module 0x%x\n",
+				__func__, ret, module_id);
+	return ret;
+}
+
+int core_dts_eagle_set(int size, char *data)
+{
+	struct adsp_dts_eagle *payload = NULL;
+	int rc = 0, size_aligned4byte;
+
+	pr_debug("DTS_EAGLE_CORE - %s\n", __func__);
+	if (size <= 0 || !data) {
+		pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n",
+			__func__, size, data);
+		return -EINVAL;
+	}
+
+	size_aligned4byte = (size+3) & 0xFFFFFFFC;
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	ocm_core_open();
+	if (q6core_lcl.core_handle_q) {
+		payload = kzalloc(sizeof(struct adsp_dts_eagle) +
+				  size_aligned4byte, GFP_KERNEL);
+		if (!payload) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+		payload->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+		payload->hdr.pkt_size = sizeof(struct adsp_dts_eagle) +
+					       size_aligned4byte;
+		payload->hdr.src_port = 0;
+		payload->hdr.dest_port = 0;
+		payload->hdr.token = 0;
+		payload->hdr.opcode = ADSP_CMD_SET_DTS_EAGLE_DATA_ID;
+		payload->id = DTS_EAGLE_LICENSE_ID;
+		payload->overwrite = 1;
+		payload->size = size;
+		memcpy(payload->data, data, size);
+		rc = apr_send_pkt(q6core_lcl.core_handle_q,
+				(uint32_t *)payload);
+		if (rc < 0) {
+			pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n",
+				__func__, payload->hdr.opcode, rc);
+		}
+		kfree(payload);
+	}
+
+exit:
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+	return rc;
+}
+
+int core_dts_eagle_get(int id, int size, char *data)
+{
+	struct apr_hdr ah;
+	int rc = 0;
+
+	pr_debug("DTS_EAGLE_CORE - %s\n", __func__);
+	if (size <= 0 || !data) {
+		pr_err("DTS_EAGLE_CORE - %s: invalid size %i or pointer %pK.\n",
+			__func__, size, data);
+		return -EINVAL;
+	}
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	ocm_core_open();
+	if (q6core_lcl.core_handle_q) {
+		ah.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		ah.pkt_size = sizeof(struct apr_hdr);
+		ah.src_port = 0;
+		ah.dest_port = 0;
+		ah.token = 0;
+		ah.opcode = id;
+
+		q6core_lcl.bus_bw_resp_received = 0;
+		generic_get_data = kzalloc(sizeof(struct generic_get_data_)
+					   + size, GFP_KERNEL);
+		if (!generic_get_data) {
+			rc = -ENOMEM;
+			goto exit;
+		}
+
+		rc = apr_send_pkt(q6core_lcl.core_handle_q,
+				(uint32_t *)&ah);
+		if (rc < 0) {
+			pr_err("DTS_EAGLE_CORE - %s: failed op[0x%x]rc[%d]\n",
+				__func__, ah.opcode, rc);
+			goto exit;
+		}
+
+		rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+				(q6core_lcl.bus_bw_resp_received == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+		if (!rc) {
+			pr_err("DTS_EAGLE_CORE - %s: EAGLE get params timed out\n",
+				__func__);
+			rc = -EINVAL;
+			goto exit;
+		}
+		if (generic_get_data->valid) {
+			rc = 0;
+			memcpy(data, generic_get_data->ints, size);
+		} else {
+			rc = -EINVAL;
+			pr_err("DTS_EAGLE_CORE - %s: EAGLE get params problem getting data - check callback error value\n",
+				__func__);
+		}
+	}
+
+exit:
+	kfree(generic_get_data);
+	generic_get_data = NULL;
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+	return rc;
+}
+
+uint32_t core_set_dolby_manufacturer_id(int manufacturer_id)
+{
+	struct adsp_dolby_manufacturer_id payload;
+	int rc = 0;
+
+	pr_debug("%s: manufacturer_id :%d\n", __func__, manufacturer_id);
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	ocm_core_open();
+	if (q6core_lcl.core_handle_q) {
+		payload.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_EVENT,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		payload.hdr.pkt_size =
+			sizeof(struct adsp_dolby_manufacturer_id);
+		payload.hdr.src_port = 0;
+		payload.hdr.dest_port = 0;
+		payload.hdr.token = 0;
+		payload.hdr.opcode = ADSP_CMD_SET_DOLBY_MANUFACTURER_ID;
+		payload.manufacturer_id = manufacturer_id;
+		pr_debug("%s: Send Dolby security opcode=0x%x manufacturer ID = %d\n",
+			__func__,
+			payload.hdr.opcode, payload.manufacturer_id);
+		rc = apr_send_pkt(q6core_lcl.core_handle_q,
+						(uint32_t *)&payload);
+		if (rc < 0)
+			pr_err("%s: SET_DOLBY_MANUFACTURER_ID failed op[0x%x]rc[%d]\n",
+				__func__, payload.hdr.opcode, rc);
+	}
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+	return rc;
+}
+
+bool q6core_is_adsp_ready(void)
+{
+	int rc;
+	bool ret = false;
+	struct apr_hdr hdr;
+
+	pr_debug("%s: enter\n", __func__);
+	memset(&hdr, 0, sizeof(hdr));
+	hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				      APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE, 0);
+	hdr.opcode = AVCS_CMD_ADSP_EVENT_GET_STATE;
+
+	mutex_lock(&(q6core_lcl.cmd_lock));
+	ocm_core_open();
+	q6core_lcl.bus_bw_resp_received = 0;
+	rc = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)&hdr);
+	if (rc < 0) {
+		pr_err("%s: Get ADSP state APR packet send event %d\n",
+			__func__, rc);
+		goto bail;
+	}
+
+	rc = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+				(q6core_lcl.bus_bw_resp_received == 1),
+				msecs_to_jiffies(Q6_READY_TIMEOUT_MS));
+	if (rc > 0 && q6core_lcl.bus_bw_resp_received) {
+		/* ensure to read updated param by callback thread */
+		rmb();
+		ret = !!q6core_lcl.param;
+	}
+bail:
+	pr_debug("%s: leave, rc %d, adsp ready %d\n", __func__, rc, ret);
+	mutex_unlock(&(q6core_lcl.cmd_lock));
+	return ret;
+}
+
+
+static int q6core_map_memory_regions(phys_addr_t *buf_add, uint32_t mempool_id,
+			uint32_t *bufsz, uint32_t bufcnt, uint32_t *map_handle)
+{
+	struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+	struct avs_shared_map_region_payload *mregions = NULL;
+	void *mmap_region_cmd = NULL;
+	void *payload = NULL;
+	int ret = 0;
+	int i = 0;
+	int cmd_size = 0;
+
+	cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions)
+			+ sizeof(struct avs_shared_map_region_payload)
+			* bufcnt;
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (mmap_region_cmd == NULL)
+		return -ENOMEM;
+
+	mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+	mmap_regions->hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+								APR_PKT_VER);
+	mmap_regions->hdr.pkt_size = cmd_size;
+	mmap_regions->hdr.src_port = 0;
+	mmap_regions->hdr.dest_port = 0;
+	mmap_regions->hdr.token = 0;
+	mmap_regions->hdr.opcode = AVCS_CMD_SHARED_MEM_MAP_REGIONS;
+	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL & 0x00ff;
+	mmap_regions->num_regions = bufcnt & 0x00ff;
+	mmap_regions->property_flag = 0x00;
+
+	payload = ((u8 *) mmap_region_cmd +
+				sizeof(struct avs_cmd_shared_mem_map_regions));
+	mregions = (struct avs_shared_map_region_payload *)payload;
+
+	for (i = 0; i < bufcnt; i++) {
+		mregions->shm_addr_lsw = lower_32_bits(buf_add[i]);
+		mregions->shm_addr_msw =
+				msm_audio_populate_upper_32_bits(buf_add[i]);
+		mregions->mem_size_bytes = bufsz[i];
+		++mregions;
+	}
+
+	pr_debug("%s: sending memory map, addr %pK, size %d, bufcnt = %d\n",
+		__func__, buf_add, bufsz[0], mmap_regions->num_regions);
+
+	*map_handle = 0;
+	q6core_lcl.bus_bw_resp_received = 0;
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)
+		mmap_regions);
+	if (ret < 0) {
+		pr_err("%s: mmap regions failed %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+				(q6core_lcl.bus_bw_resp_received == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: timeout. waited for memory map\n", __func__);
+		ret = -ETIME;
+		goto done;
+	}
+
+	*map_handle = q6core_lcl.mem_map_cal_handle;
+done:
+	kfree(mmap_region_cmd);
+	return ret;
+}
+
+static int q6core_memory_unmap_regions(uint32_t mem_map_handle)
+{
+	struct avs_cmd_shared_mem_unmap_regions unmap_regions;
+	int ret = 0;
+
+	memset(&unmap_regions, 0, sizeof(unmap_regions));
+	unmap_regions.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	unmap_regions.hdr.pkt_size = sizeof(unmap_regions);
+	unmap_regions.hdr.src_svc = APR_SVC_ADSP_CORE;
+	unmap_regions.hdr.src_domain = APR_DOMAIN_APPS;
+	unmap_regions.hdr.src_port = 0;
+	unmap_regions.hdr.dest_svc = APR_SVC_ADSP_CORE;
+	unmap_regions.hdr.dest_domain = APR_DOMAIN_ADSP;
+	unmap_regions.hdr.dest_port = 0;
+	unmap_regions.hdr.token = 0;
+	unmap_regions.hdr.opcode = AVCS_CMD_SHARED_MEM_UNMAP_REGIONS;
+	unmap_regions.mem_map_handle = mem_map_handle;
+
+	q6core_lcl.bus_bw_resp_received = 0;
+
+	pr_debug("%s: unmap regions map handle %d\n",
+		__func__, mem_map_handle);
+
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *)
+		&unmap_regions);
+	if (ret < 0) {
+		pr_err("%s: unmap regions failed %d\n",
+			__func__, ret);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+				(q6core_lcl.bus_bw_resp_received == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: timeout. waited for memory_unmap\n",
+		       __func__);
+		ret = -ETIME;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6core_dereg_all_custom_topologies(void)
+{
+	int ret = 0;
+	struct avcs_cmd_deregister_topologies dereg_top;
+
+	memset(&dereg_top, 0, sizeof(dereg_top));
+	dereg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	dereg_top.hdr.pkt_size = sizeof(dereg_top);
+	dereg_top.hdr.src_svc = APR_SVC_ADSP_CORE;
+	dereg_top.hdr.src_domain = APR_DOMAIN_APPS;
+	dereg_top.hdr.src_port = 0;
+	dereg_top.hdr.dest_svc = APR_SVC_ADSP_CORE;
+	dereg_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+	dereg_top.hdr.dest_port = 0;
+	dereg_top.hdr.token = 0;
+	dereg_top.hdr.opcode = AVCS_CMD_DEREGISTER_TOPOLOGIES;
+	dereg_top.payload_addr_lsw = 0;
+	dereg_top.payload_addr_msw = 0;
+	dereg_top.mem_map_handle = 0;
+	dereg_top.payload_size = 0;
+	dereg_top.mode = AVCS_MODE_DEREGISTER_ALL_CUSTOM_TOPOLOGIES;
+
+	q6core_lcl.bus_bw_resp_received = 0;
+
+	pr_debug("%s: Deregister topologies mode %d\n",
+		__func__, dereg_top.mode);
+
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &dereg_top);
+	if (ret < 0) {
+		pr_err("%s: Deregister topologies failed %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+				(q6core_lcl.bus_bw_resp_received == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout for Deregister topologies\n",
+			__func__);
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6core_send_custom_topologies(void)
+{
+	int ret = 0;
+	int ret2 = 0;
+	struct cal_block_data *cal_block = NULL;
+	struct avcs_cmd_register_topologies reg_top;
+
+	if (!q6core_is_adsp_ready()) {
+		pr_err("%s: ADSP is not ready!\n", __func__);
+		return -ENODEV;
+	}
+
+	memset(&reg_top, 0, sizeof(reg_top));
+	mutex_lock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock);
+	mutex_lock(&q6core_lcl.cmd_lock);
+
+	cal_block = cal_utils_get_only_cal_block(
+		q6core_lcl.cal_data[CUST_TOP_CAL]);
+	if (cal_block == NULL) {
+		pr_debug("%s: cal block is NULL!\n", __func__);
+		goto unlock;
+	}
+	if (cal_block->cal_data.size <= 0) {
+		pr_debug("%s: cal size is %zd not sending\n",
+			__func__, cal_block->cal_data.size);
+		goto unlock;
+	}
+
+	q6core_dereg_all_custom_topologies();
+
+	ret = q6core_map_memory_regions(&cal_block->cal_data.paddr, 0,
+		(uint32_t *)&cal_block->map_data.map_size, 1,
+		&cal_block->map_data.q6map_handle);
+	if (!ret)  {
+		pr_err("%s: q6core_map_memory_regions failed\n", __func__);
+		goto unlock;
+	}
+
+	reg_top.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	reg_top.hdr.pkt_size = sizeof(reg_top);
+	reg_top.hdr.src_svc = APR_SVC_ADSP_CORE;
+	reg_top.hdr.src_domain = APR_DOMAIN_APPS;
+	reg_top.hdr.src_port = 0;
+	reg_top.hdr.dest_svc = APR_SVC_ADSP_CORE;
+	reg_top.hdr.dest_domain = APR_DOMAIN_ADSP;
+	reg_top.hdr.dest_port = 0;
+	reg_top.hdr.token = 0;
+	reg_top.hdr.opcode = AVCS_CMD_REGISTER_TOPOLOGIES;
+	reg_top.payload_addr_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	reg_top.payload_addr_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	reg_top.mem_map_handle = cal_block->map_data.q6map_handle;
+	reg_top.payload_size = cal_block->cal_data.size;
+
+	q6core_lcl.adsp_status = 0;
+	q6core_lcl.bus_bw_resp_received = 0;
+
+	pr_debug("%s: Register topologies addr %pK, size %zd, map handle %d\n",
+		__func__, &cal_block->cal_data.paddr, cal_block->cal_data.size,
+		cal_block->map_data.q6map_handle);
+
+	ret = apr_send_pkt(q6core_lcl.core_handle_q, (uint32_t *) &reg_top);
+	if (ret < 0) {
+		pr_err("%s: Register topologies failed %d\n",
+			__func__, ret);
+		goto unmap;
+	}
+
+	ret = wait_event_timeout(q6core_lcl.bus_bw_req_wait,
+				(q6core_lcl.bus_bw_resp_received == 1),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout for Register topologies\n",
+			__func__);
+		goto unmap;
+	}
+
+	if (q6core_lcl.adsp_status < 0)
+		ret = q6core_lcl.adsp_status;
+unmap:
+	ret2 = q6core_memory_unmap_regions(cal_block->map_data.q6map_handle);
+	if (!ret2)  {
+		pr_err("%s: q6core_memory_unmap_regions failed for map handle %d\n",
+			__func__, cal_block->map_data.q6map_handle);
+		ret = ret2;
+		goto unlock;
+	}
+
+unlock:
+	mutex_unlock(&q6core_lcl.cmd_lock);
+	mutex_unlock(&q6core_lcl.cal_data[CUST_TOP_CAL]->lock);
+
+	return ret;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+	int ret = -EINVAL;
+
+	switch (cal_type) {
+	case AUDIO_CORE_METAINFO_CAL_TYPE:
+		ret = META_CAL;
+		break;
+	case CORE_CUSTOM_TOPOLOGIES_CAL_TYPE:
+		ret = CUST_TOP_CAL;
+		break;
+	default:
+		pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+	}
+	return ret;
+}
+
+static int q6core_alloc_cal(int32_t cal_type,
+			    size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+
+	ret = cal_utils_alloc_cal(data_size, data,
+		q6core_lcl.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6core_dealloc_cal(int32_t cal_type,
+			      size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+					q6core_lcl.cal_data[cal_index]);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6core_set_cal(int32_t cal_type,
+	size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+
+	ret = cal_utils_set_cal(data_size, data,
+				    q6core_lcl.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+		__func__, ret, cal_type);
+		goto done;
+	}
+
+	if (cal_index == CUST_TOP_CAL)
+		ret = q6core_send_custom_topologies();
+done:
+	return ret;
+}
+
+static void q6core_delete_cal_data(void)
+{
+	pr_debug("%s:\n", __func__);
+
+	cal_utils_destroy_cal_types(CORE_MAX_CAL, q6core_lcl.cal_data);
+}
+
+
+static int q6core_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info    cal_type_info[] = {
+		{{AUDIO_CORE_METAINFO_CAL_TYPE,
+		{q6core_alloc_cal, q6core_dealloc_cal, NULL,
+		q6core_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{CORE_CUSTOM_TOPOLOGIES_CAL_TYPE,
+		{q6core_alloc_cal, q6core_dealloc_cal, NULL,
+		q6core_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} }
+	};
+	pr_debug("%s:\n", __func__);
+
+	ret = cal_utils_create_cal_types(CORE_MAX_CAL,
+		q6core_lcl.cal_data, cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: could not create cal type!\n",
+			__func__);
+		goto err;
+	}
+
+	return ret;
+err:
+	q6core_delete_cal_data();
+	return ret;
+}
+
+static int __init core_init(void)
+{
+	init_waitqueue_head(&q6core_lcl.bus_bw_req_wait);
+	q6core_lcl.bus_bw_resp_received = 0;
+
+	q6core_lcl.core_handle_q = NULL;
+
+	init_waitqueue_head(&q6core_lcl.cmd_req_wait);
+	q6core_lcl.cmd_resp_received_flag = FLAG_NONE;
+	mutex_init(&q6core_lcl.cmd_lock);
+	q6core_lcl.mem_map_cal_handle = 0;
+	q6core_lcl.adsp_status = 0;
+
+	q6core_init_cal_data();
+	return 0;
+}
+module_init(core_init);
+
+static void __exit core_exit(void)
+{
+	mutex_destroy(&q6core_lcl.cmd_lock);
+	q6core_delete_cal_data();
+}
+module_exit(core_exit);
+MODULE_DESCRIPTION("ADSP core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/msm/qdsp6v2/q6lsm.c b/sound/soc/msm/qdsp6v2/q6lsm.c
new file mode 100644
index 0000000..d761cf5
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6lsm.c
@@ -0,0 +1,1950 @@
+/*
+ * Copyright (c) 2013-2017, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/fs.h>
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/uaccess.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/time.h>
+#include <linux/atomic.h>
+#include <sound/apr_audio-v2.h>
+#include <sound/lsm_params.h>
+#include <sound/q6core.h>
+#include <sound/q6lsm.h>
+#include <asm/ioctls.h>
+#include <linux/memory.h>
+#include <linux/msm_audio_ion.h>
+#include <sound/q6afe-v2.h>
+#include <sound/audio_cal_utils.h>
+#include <sound/adsp_err.h>
+
+#define APR_TIMEOUT	(5 * HZ)
+#define LSM_ALIGN_BOUNDARY 512
+#define LSM_SAMPLE_RATE 16000
+#define QLSM_PARAM_ID_MINOR_VERSION 1
+static int lsm_afe_port;
+
+enum {
+	LSM_CUSTOM_TOP_IDX,
+	LSM_TOP_IDX,
+	LSM_CAL_IDX,
+	LSM_MAX_CAL_IDX
+};
+
+enum {
+	CMD_STATE_CLEARED = 0,
+	CMD_STATE_WAIT_RESP = 1,
+};
+
+enum {
+	LSM_INVALID_SESSION_ID = 0,
+	LSM_MIN_SESSION_ID = 1,
+	LSM_MAX_SESSION_ID = 8,
+	LSM_CONTROL_SESSION = 0x0F,
+};
+
+#define CHECK_SESSION(x) (x < LSM_MIN_SESSION_ID || x > LSM_MAX_SESSION_ID)
+struct lsm_common {
+	void *apr;
+	atomic_t apr_users;
+	struct lsm_client	common_client[LSM_MAX_SESSION_ID + 1];
+
+	int set_custom_topology;
+	struct cal_type_data	*cal_data[LSM_MAX_CAL_IDX];
+
+	struct mutex apr_lock;
+};
+
+struct lsm_module_param_ids {
+	uint32_t module_id;
+	uint32_t param_id;
+};
+
+static struct lsm_common lsm_common;
+/*
+ * mmap_handle_p can point either client->sound_model.mem_map_handle or
+ * lsm_common.mmap_handle_for_cal.
+ * mmap_lock must be held while accessing this.
+ */
+static spinlock_t mmap_lock;
+static uint32_t *mmap_handle_p;
+
+static spinlock_t lsm_session_lock;
+static struct lsm_client *lsm_session[LSM_MAX_SESSION_ID + 1];
+
+static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv);
+static int q6lsm_send_cal(struct lsm_client *client, u32 set_params_opcode);
+static int q6lsm_memory_map_regions(struct lsm_client *client,
+				    dma_addr_t dma_addr_p, uint32_t dma_buf_sz,
+				    uint32_t *mmap_p);
+static int q6lsm_memory_unmap_regions(struct lsm_client *client,
+				      uint32_t handle);
+
+static void q6lsm_set_param_hdr_info(
+		struct lsm_set_params_hdr *param_hdr,
+		u32 payload_size, u32 addr_lsw, u32 addr_msw,
+		u32 mmap_handle)
+{
+	param_hdr->data_payload_size = payload_size;
+	param_hdr->data_payload_addr_lsw = addr_lsw;
+	param_hdr->data_payload_addr_msw = addr_msw;
+	param_hdr->mem_map_handle = mmap_handle;
+}
+
+static void q6lsm_set_param_common(
+		struct lsm_param_payload_common *common,
+		struct lsm_module_param_ids *ids,
+		u32 param_size, u32 set_param_version)
+{
+	common->module_id = ids->module_id;
+	common->param_id = ids->param_id;
+
+	switch (set_param_version) {
+	case LSM_SESSION_CMD_SET_PARAMS_V2:
+		common->p_size.param_size = param_size;
+		break;
+	case LSM_SESSION_CMD_SET_PARAMS:
+	default:
+		common->p_size.sr.param_size =
+			(u16) param_size;
+		common->p_size.sr.reserved = 0;
+		break;
+	}
+}
+
+static int q6lsm_callback(struct apr_client_data *data, void *priv)
+{
+	struct lsm_client *client = (struct lsm_client *)priv;
+	uint32_t token;
+	uint32_t *payload;
+
+	if (!client || !data) {
+		pr_err("%s: client %pK data %pK\n",
+			__func__, client, data);
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: SSR event received 0x%x, event 0x%x, proc 0x%x\n",
+			 __func__, data->opcode, data->reset_event,
+			 data->reset_proc);
+
+		cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX,
+			lsm_common.cal_data);
+		mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+		lsm_common.set_custom_topology = 1;
+		mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+		return 0;
+	}
+
+	payload = data->payload;
+	pr_debug("%s: Session %d opcode 0x%x token 0x%x payload size %d\n"
+			 "payload [0] = 0x%x\n", __func__, client->session,
+		data->opcode, data->token, data->payload_size, payload[0]);
+	if (data->opcode == LSM_DATA_EVENT_READ_DONE) {
+		struct lsm_cmd_read_done read_done;
+
+		token = data->token;
+		if (data->payload_size > sizeof(read_done)) {
+			pr_err("%s: read done error payload size %d expected size %zd\n",
+				__func__, data->payload_size,
+				sizeof(read_done));
+			return -EINVAL;
+		}
+		pr_debug("%s: opcode %x status %x lsw %x msw %x mem_map handle %x\n",
+			__func__, data->opcode, payload[0], payload[1],
+			payload[2], payload[3]);
+		read_done.status = payload[0];
+		read_done.buf_addr_lsw = payload[1];
+		read_done.buf_addr_msw = payload[2];
+		read_done.mem_map_handle = payload[3];
+		read_done.total_size = payload[4];
+		read_done.offset = payload[5];
+		if (client->cb)
+			client->cb(data->opcode, data->token,
+					(void *)&read_done,
+					client->priv);
+		return 0;
+	} else if (data->opcode == APR_BASIC_RSP_RESULT) {
+		token = data->token;
+		switch (payload[0]) {
+		case LSM_SESSION_CMD_START:
+		case LSM_SESSION_CMD_STOP:
+		case LSM_SESSION_CMD_SET_PARAMS:
+		case LSM_SESSION_CMD_OPEN_TX:
+		case LSM_SESSION_CMD_CLOSE_TX:
+		case LSM_SESSION_CMD_REGISTER_SOUND_MODEL:
+		case LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL:
+		case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS:
+		case LSM_SESSION_CMD_EOB:
+		case LSM_SESSION_CMD_READ:
+		case LSM_SESSION_CMD_OPEN_TX_V2:
+		case LSM_CMD_ADD_TOPOLOGIES:
+		case LSM_SESSION_CMD_SET_PARAMS_V2:
+			if (token != client->session &&
+			    payload[0] !=
+				LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL) {
+				pr_err("%s: Invalid session %d receivced expected %d\n",
+					__func__, token, client->session);
+				return -EINVAL;
+			}
+			client->cmd_err_code = payload[1];
+			if (client->cmd_err_code)
+				pr_err("%s: cmd 0x%x failed status %d\n",
+				__func__, payload[0], client->cmd_err_code);
+			if (atomic_cmpxchg(&client->cmd_state,
+					   CMD_STATE_WAIT_RESP,
+					   CMD_STATE_CLEARED) ==
+					       CMD_STATE_WAIT_RESP)
+				wake_up(&client->cmd_wait);
+			break;
+		default:
+			pr_debug("%s: Unknown command 0x%x\n",
+				__func__, payload[0]);
+			break;
+		}
+		return 0;
+	}
+
+	if (client->cb)
+		client->cb(data->opcode, data->token, data->payload,
+			   client->priv);
+
+	return 0;
+}
+
+static int q6lsm_session_alloc(struct lsm_client *client)
+{
+	unsigned long flags;
+	int n, ret = -ENOMEM;
+
+	spin_lock_irqsave(&lsm_session_lock, flags);
+	for (n = LSM_MIN_SESSION_ID; n <= LSM_MAX_SESSION_ID; n++) {
+		if (!lsm_session[n]) {
+			lsm_session[n] = client;
+			ret = n;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&lsm_session_lock, flags);
+	pr_debug("%s: Alloc Session %d", __func__, n);
+	return ret;
+}
+
+static void q6lsm_session_free(struct lsm_client *client)
+{
+	unsigned long flags;
+
+	pr_debug("%s: Freeing session ID %d\n", __func__, client->session);
+	spin_lock_irqsave(&lsm_session_lock, flags);
+	lsm_session[client->session] = LSM_INVALID_SESSION_ID;
+	spin_unlock_irqrestore(&lsm_session_lock, flags);
+	client->session = LSM_INVALID_SESSION_ID;
+}
+
+static void *q6lsm_mmap_apr_reg(void)
+{
+	if (atomic_inc_return(&lsm_common.apr_users) == 1) {
+		lsm_common.apr =
+		    apr_register("ADSP", "LSM", q6lsm_mmapcallback,
+				 0x0FFFFFFFF, &lsm_common);
+		if (!lsm_common.apr) {
+			pr_debug("%s: Unable to register APR LSM common port\n",
+				 __func__);
+			atomic_dec(&lsm_common.apr_users);
+		}
+	}
+	return lsm_common.apr;
+}
+
+static int q6lsm_mmap_apr_dereg(void)
+{
+	if (atomic_read(&lsm_common.apr_users) <= 0) {
+		WARN("%s: APR common port already closed\n", __func__);
+	} else {
+		if (atomic_dec_return(&lsm_common.apr_users) == 0) {
+			apr_deregister(lsm_common.apr);
+			pr_debug("%s: APR De-Register common port\n", __func__);
+		}
+	}
+	return 0;
+}
+
+struct lsm_client *q6lsm_client_alloc(lsm_app_cb cb, void *priv)
+{
+	struct lsm_client *client;
+	int n;
+
+	client = kzalloc(sizeof(struct lsm_client), GFP_KERNEL);
+	if (!client)
+		return NULL;
+	n = q6lsm_session_alloc(client);
+	if (n <= 0) {
+		kfree(client);
+		return NULL;
+	}
+	client->session = n;
+	client->cb = cb;
+	client->priv = priv;
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: Client session %d\n",
+			__func__, client->session);
+		kfree(client);
+		return NULL;
+	}
+	pr_debug("%s: Client Session %d\n", __func__, client->session);
+	client->apr = apr_register("ADSP", "LSM", q6lsm_callback,
+				   ((client->session) << 8 | client->session),
+				   client);
+
+	if (client->apr == NULL) {
+		pr_err("%s: Registration with APR failed\n", __func__);
+		goto fail;
+	}
+
+	pr_debug("%s: Registering the common port with APR\n", __func__);
+	client->mmap_apr = q6lsm_mmap_apr_reg();
+	if (!client->mmap_apr) {
+		pr_err("%s: APR registration failed\n", __func__);
+		goto fail;
+	}
+
+	init_waitqueue_head(&client->cmd_wait);
+	mutex_init(&client->cmd_lock);
+	atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+	pr_debug("%s: New client allocated\n", __func__);
+	return client;
+fail:
+	q6lsm_client_free(client);
+	return NULL;
+}
+
+void q6lsm_client_free(struct lsm_client *client)
+{
+	if (!client)
+		return;
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: Invalid Session %d\n", __func__, client->session);
+		return;
+	}
+	apr_deregister(client->apr);
+	client->mmap_apr = NULL;
+	q6lsm_session_free(client);
+	q6lsm_mmap_apr_dereg();
+	mutex_destroy(&client->cmd_lock);
+	kfree(client);
+	client = NULL;
+}
+
+/*
+ * q6lsm_apr_send_pkt : If wait == true, hold mutex to prevent from preempting
+ *			other thread's wait.
+ *			If mmap_handle_p != NULL, disable irq and spin lock to
+ *			protect mmap_handle_p
+ */
+static int q6lsm_apr_send_pkt(struct lsm_client *client, void *handle,
+			      void *data, bool wait, uint32_t *mmap_p)
+{
+	int ret;
+	unsigned long flags = 0;
+	struct apr_hdr *msg_hdr = (struct apr_hdr *) data;
+
+	pr_debug("%s: enter wait %d\n", __func__, wait);
+	if (wait)
+		mutex_lock(&lsm_common.apr_lock);
+	if (mmap_p) {
+		WARN_ON(!wait);
+		spin_lock_irqsave(&mmap_lock, flags);
+		mmap_handle_p = mmap_p;
+	}
+	atomic_set(&client->cmd_state, CMD_STATE_WAIT_RESP);
+	client->cmd_err_code = 0;
+	ret = apr_send_pkt(handle, data);
+	if (mmap_p)
+		spin_unlock_irqrestore(&mmap_lock, flags);
+
+	if (ret < 0) {
+		pr_err("%s: apr_send_pkt failed %d\n", __func__, ret);
+	} else if (wait) {
+		ret = wait_event_timeout(client->cmd_wait,
+					 (atomic_read(&client->cmd_state) ==
+					      CMD_STATE_CLEARED),
+					 APR_TIMEOUT);
+		if (likely(ret)) {
+			/* q6 returned error */
+			if (client->cmd_err_code) {
+				pr_err("%s: DSP returned error[%s]\n",
+					__func__, adsp_err_get_err_str(
+					client->cmd_err_code));
+				ret = adsp_err_get_lnx_err_code(
+						client->cmd_err_code);
+			} else {
+				ret = 0;
+			}
+		} else {
+			pr_err("%s: wait timedout, apr_opcode = 0x%x, size = %d\n",
+				__func__, msg_hdr->opcode, msg_hdr->pkt_size);
+			/* ret = 0 means wait timed out */
+			ret = -ETIMEDOUT;
+		}
+	} else {
+		ret = 0;
+	}
+	if (wait)
+		mutex_unlock(&lsm_common.apr_lock);
+
+	pr_debug("%s: leave ret %d\n", __func__, ret);
+	return ret;
+}
+
+static void q6lsm_add_hdr(struct lsm_client *client, struct apr_hdr *hdr,
+			uint32_t pkt_size, bool cmd_flg)
+{
+	pr_debug("%s: pkt_size %d cmd_flg %d session %d\n", __func__,
+		pkt_size, cmd_flg, client->session);
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				       APR_HDR_LEN(sizeof(struct apr_hdr)),
+				       APR_PKT_VER);
+	hdr->src_svc = APR_SVC_LSM;
+	hdr->src_domain = APR_DOMAIN_APPS;
+	hdr->dest_svc = APR_SVC_LSM;
+	hdr->dest_domain = APR_DOMAIN_ADSP;
+	hdr->src_port = ((client->session << 8) & 0xFF00) | client->session;
+	hdr->dest_port = ((client->session << 8) & 0xFF00) | client->session;
+	hdr->pkt_size = pkt_size;
+	if (cmd_flg)
+		hdr->token = client->session;
+}
+
+
+static int q6lsm_send_custom_topologies(struct lsm_client *client)
+{
+	int rc;
+	struct cal_block_data *cal_block = NULL;
+	struct lsm_custom_topologies cstm_top;
+
+	if (lsm_common.cal_data[LSM_CUSTOM_TOP_IDX] == NULL) {
+		pr_err("%s: LSM_CUSTOM_TOP_IDX invalid\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	lsm_common.set_custom_topology = 0;
+
+	mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+	cal_block = cal_utils_get_only_cal_block(
+			lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]);
+	if (!cal_block) {
+		pr_err("%s: Cal block for LSM_CUSTOM_TOP_IDX not found\n",
+			__func__);
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	if (cal_block->cal_data.size <= 0) {
+		pr_err("%s: Invalid size for LSM_CUSTOM_TOP %zd\n",
+			__func__, cal_block->cal_data.size);
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	memset(&cstm_top, 0, sizeof(cstm_top));
+	/* Map the memory for out-of-band data */
+	rc = q6lsm_memory_map_regions(client, cal_block->cal_data.paddr,
+				      cal_block->map_data.map_size,
+				      &cal_block->map_data.q6map_handle);
+	if (rc < 0) {
+		pr_err("%s: Failed to map custom topologied, err = %d\n",
+			__func__, rc);
+		goto unlock;
+	}
+
+	q6lsm_add_hdr(client, &cstm_top.hdr,
+		      sizeof(cstm_top), true);
+	cstm_top.hdr.opcode = LSM_CMD_ADD_TOPOLOGIES;
+
+	/*
+	 * For ADD_TOPOLOGIES, the dest_port should be 0
+	 * Note that source port cannot be zero as it is used
+	 * to route the response to a specific client registered
+	 * on APR
+	 */
+	cstm_top.hdr.dest_port = 0;
+
+	cstm_top.data_payload_addr_lsw =
+			lower_32_bits(cal_block->cal_data.paddr);
+	cstm_top.data_payload_addr_msw =
+			msm_audio_populate_upper_32_bits(
+					cal_block->cal_data.paddr);
+	cstm_top.mem_map_handle = cal_block->map_data.q6map_handle;
+	cstm_top.buffer_size = cal_block->cal_data.size;
+
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				&cstm_top, true, NULL);
+	if (rc)
+		pr_err("%s: Failed to add custom top, err = %d\n",
+			__func__, rc);
+	/* go ahead and unmap even if custom top failed */
+	rc = q6lsm_memory_unmap_regions(client,
+					cal_block->map_data.q6map_handle);
+	if (rc) {
+		pr_err("%s: Failed to unmap, err = %d\n",
+			__func__, rc);
+		/* Even if mem unmap failed, treat the cmd as success */
+		rc = 0;
+	}
+
+unlock:
+	mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+done:
+	return rc;
+}
+
+static int q6lsm_do_open_v2(struct lsm_client *client,
+		uint16_t app_id)
+{
+	int rc;
+	struct cal_block_data *cal_block = NULL;
+	struct audio_cal_info_lsm_top *lsm_top;
+	struct lsm_stream_cmd_open_tx_v2 open_v2;
+
+	if (lsm_common.cal_data[LSM_TOP_IDX] == NULL) {
+		pr_err("%s: LSM_TOP_IDX invalid\n", __func__);
+		rc = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&lsm_common.cal_data[LSM_TOP_IDX]->lock);
+	cal_block = cal_utils_get_only_cal_block(
+			lsm_common.cal_data[LSM_TOP_IDX]);
+	if (!cal_block) {
+		pr_err("%s: Cal block for LSM_TOP_IDX not found\n",
+			__func__);
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	lsm_top = (struct audio_cal_info_lsm_top *)
+			cal_block->cal_info;
+	if (!lsm_top) {
+		pr_err("%s: cal_info for LSM_TOP_IDX not found\n",
+			__func__);
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	pr_debug("%s: topology_id = 0x%x, acdb_id = 0x%x, app_type = 0x%x\n",
+		 __func__, lsm_top->topology, lsm_top->acdb_id,
+		 lsm_top->app_type);
+
+	if (lsm_top->topology == 0) {
+		pr_err("%s: toplogy id not sent for app_type 0x%x\n",
+			__func__, lsm_top->app_type);
+		rc = -EINVAL;
+		goto unlock;
+	}
+
+	client->app_id = lsm_top->app_type;
+	memset(&open_v2, 0, sizeof(open_v2));
+	q6lsm_add_hdr(client, &open_v2.hdr,
+		      sizeof(open_v2), true);
+	open_v2.topology_id = lsm_top->topology;
+	open_v2.hdr.opcode = LSM_SESSION_CMD_OPEN_TX_V2;
+
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				&open_v2, true, NULL);
+	if (rc)
+		pr_err("%s: open_v2 failed, err = %d\n",
+			__func__, rc);
+	else
+		client->use_topology = true;
+unlock:
+	mutex_unlock(&lsm_common.cal_data[LSM_TOP_IDX]->lock);
+done:
+	return rc;
+
+}
+
+void q6lsm_sm_set_param_data(struct lsm_client *client,
+		struct lsm_params_info *p_info,
+		size_t *offset)
+{
+	struct lsm_param_payload_common *param;
+
+	param = (struct lsm_param_payload_common *)
+			client->sound_model.data;
+	param->module_id = p_info->module_id;
+	param->param_id = p_info->param_id;
+	param->p_size.param_size = client->sound_model.size;
+	*offset = sizeof(*param);
+}
+
+int q6lsm_open(struct lsm_client *client, uint16_t app_id)
+{
+	int rc = 0;
+	struct lsm_stream_cmd_open_tx open;
+
+	/* Add Custom topologies if needed */
+	if (lsm_common.set_custom_topology) {
+		rc = q6lsm_send_custom_topologies(client);
+		if (rc)
+			pr_err("%s: Failed to send cust_top, err = %d\n",
+				__func__, rc);
+	}
+
+	/* Try to open with topology first */
+	rc = q6lsm_do_open_v2(client, app_id);
+	if (!rc)
+		/* open_v2 was successful */
+		goto done;
+
+	pr_debug("%s: try without topology\n",
+		 __func__);
+
+	memset(&open, 0, sizeof(open));
+	q6lsm_add_hdr(client, &open.hdr, sizeof(open), true);
+	switch (client->app_id) {
+	case LSM_VOICE_WAKEUP_APP_ID_V2:
+		open.app_id = client->app_id;
+		break;
+	default:
+		pr_err("%s:  default err 0x%x\n", __func__, client->app_id);
+		rc = -EINVAL;
+		break;
+	}
+
+	open.sampling_rate = LSM_SAMPLE_RATE;
+	open.hdr.opcode = LSM_SESSION_CMD_OPEN_TX;
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				&open, true, NULL);
+	if (rc)
+		pr_err("%s: Open failed opcode 0x%x, rc %d\n",
+		       __func__, open.hdr.opcode, rc);
+	else
+		client->use_topology = false;
+done:
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+
+static int q6lsm_send_confidence_levels(
+		struct lsm_client *client,
+		struct lsm_module_param_ids *ids,
+		u32 set_param_opcode)
+{
+	u8 *packet;
+	size_t pkt_size;
+	struct lsm_cmd_set_params_conf *conf_params;
+	struct apr_hdr *msg_hdr;
+	struct lsm_param_min_confidence_levels *cfl;
+	uint8_t i = 0;
+	uint8_t padd_size = 0;
+	u8 *conf_levels;
+	int rc;
+	u32 payload_size, param_size;
+
+	padd_size = (4 - (client->num_confidence_levels % 4)) - 1;
+	pkt_size = sizeof(*conf_params) + padd_size +
+		   client->num_confidence_levels;
+
+	packet = kzalloc(pkt_size, GFP_KERNEL);
+	if (!packet)
+		return -ENOMEM;
+
+	conf_params = (struct lsm_cmd_set_params_conf *) packet;
+	conf_levels = (u8 *) (packet + sizeof(*conf_params));
+	msg_hdr = &conf_params->msg_hdr;
+	q6lsm_add_hdr(client, msg_hdr,
+		      pkt_size, true);
+	msg_hdr->opcode = set_param_opcode;
+	payload_size = pkt_size - sizeof(*msg_hdr) -
+		       sizeof(conf_params->params_hdr);
+	q6lsm_set_param_hdr_info(&conf_params->params_hdr,
+				 payload_size, 0, 0, 0);
+	cfl = &conf_params->conf_payload;
+	param_size = ((sizeof(uint8_t) + padd_size +
+		       client->num_confidence_levels)) *
+		      sizeof(uint8_t);
+	q6lsm_set_param_common(&cfl->common, ids,
+			       param_size, set_param_opcode);
+	cfl->num_confidence_levels = client->num_confidence_levels;
+
+	pr_debug("%s: CMD PARAM SIZE = %d\n",
+		 __func__, param_size);
+	pr_debug("%s: Num conf_level = %d\n",
+		 __func__, client->num_confidence_levels);
+
+	memcpy(conf_levels, client->confidence_levels,
+	       client->num_confidence_levels);
+	for (i = 0; i < client->num_confidence_levels; i++)
+		pr_debug("%s: Confidence_level[%d] = %d\n",
+			 __func__, i, conf_levels[i]);
+
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				packet, true, NULL);
+	if (rc)
+		pr_err("%s: confidence_levels cmd failed, err = %d\n",
+			__func__, rc);
+	kfree(packet);
+	return rc;
+}
+
+static int q6lsm_send_params(struct lsm_client *client,
+		struct lsm_module_param_ids *opmode_ids,
+		struct lsm_module_param_ids *connectport_ids,
+		u32 set_param_opcode)
+{
+	int rc;
+	struct lsm_cmd_set_opmode_connectport opmode_connectport;
+	struct apr_hdr  *msg_hdr;
+	struct lsm_param_connect_to_port *connect_to_port;
+	struct lsm_param_op_mode *op_mode;
+	u32 data_payload_size, param_size;
+
+	msg_hdr = &opmode_connectport.msg_hdr;
+	q6lsm_add_hdr(client, msg_hdr,
+		      sizeof(opmode_connectport), true);
+	msg_hdr->opcode = set_param_opcode;
+	data_payload_size = sizeof(opmode_connectport) -
+			    sizeof(*msg_hdr) -
+			    sizeof(opmode_connectport.params_hdr);
+	q6lsm_set_param_hdr_info(&opmode_connectport.params_hdr,
+				 data_payload_size, 0, 0, 0);
+	connect_to_port = &opmode_connectport.connect_to_port;
+	op_mode = &opmode_connectport.op_mode;
+
+	param_size = sizeof(struct lsm_param_op_mode) -
+		     sizeof(op_mode->common);
+	q6lsm_set_param_common(&op_mode->common,
+			       opmode_ids, param_size,
+			       set_param_opcode);
+	op_mode->minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+	op_mode->mode = client->mode;
+	op_mode->reserved = 0;
+	pr_debug("%s: mode = 0x%x", __func__, op_mode->mode);
+
+	param_size = (sizeof(struct lsm_param_connect_to_port) -
+		      sizeof(connect_to_port->common));
+	q6lsm_set_param_common(&connect_to_port->common,
+			       connectport_ids, param_size,
+			       set_param_opcode);
+	connect_to_port->minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+	connect_to_port->port_id = client->connect_to_port;
+	connect_to_port->reserved = 0;
+	pr_debug("%s: port= %d", __func__, connect_to_port->port_id);
+
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				&opmode_connectport, true, NULL);
+	if (rc)
+		pr_err("%s: Failed set_params opcode 0x%x, rc %d\n",
+		       __func__, msg_hdr->opcode, rc);
+
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+
+void set_lsm_port(int lsm_port)
+{
+	lsm_afe_port = lsm_port;
+}
+
+int get_lsm_port(void)
+{
+	return lsm_afe_port;
+}
+
+int q6lsm_set_data(struct lsm_client *client,
+			   enum lsm_detection_mode mode,
+			   bool detectfailure)
+{
+	int rc = 0;
+	struct lsm_module_param_ids opmode_ids, connectport_ids;
+	struct lsm_module_param_ids conf_levels_ids;
+
+	if (!client->confidence_levels) {
+		/*
+		 * It is possible that confidence levels are
+		 * not provided. This is not a error condition.
+		 * Return gracefully without any error
+		 */
+		pr_debug("%s: no conf levels to set\n",
+			__func__);
+		return rc;
+	}
+
+	if (mode == LSM_MODE_KEYWORD_ONLY_DETECTION) {
+		client->mode = 0x01;
+	} else if (mode == LSM_MODE_USER_KEYWORD_DETECTION) {
+		client->mode = 0x03;
+	} else {
+		pr_err("%s: Incorrect detection mode %d\n", __func__, mode);
+		rc = -EINVAL;
+		goto err_ret;
+	}
+	client->mode |= detectfailure << 2;
+	client->connect_to_port = get_lsm_port();
+
+	opmode_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+	opmode_ids.param_id = LSM_PARAM_ID_OPERATION_MODE;
+
+	connectport_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+	connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+	rc = q6lsm_send_params(client, &opmode_ids, &connectport_ids,
+			      LSM_SESSION_CMD_SET_PARAMS);
+	if (rc) {
+		pr_err("%s: Failed to set lsm config params %d\n",
+			__func__, rc);
+		goto err_ret;
+	}
+
+	conf_levels_ids.module_id = LSM_MODULE_ID_VOICE_WAKEUP;
+	conf_levels_ids.param_id = LSM_PARAM_ID_MIN_CONFIDENCE_LEVELS;
+
+	rc = q6lsm_send_confidence_levels(client, &conf_levels_ids,
+					 LSM_SESSION_CMD_SET_PARAMS);
+	if (rc) {
+		pr_err("%s: Failed to send conf_levels, err = %d\n",
+			__func__, rc);
+		goto err_ret;
+	}
+
+	rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS);
+	if (rc) {
+		pr_err("%s: Failed to send calibration data %d\n",
+			__func__, rc);
+		goto err_ret;
+	}
+
+err_ret:
+	return rc;
+}
+
+int q6lsm_register_sound_model(struct lsm_client *client,
+			       enum lsm_detection_mode mode,
+			       bool detectfailure)
+{
+	int rc;
+	struct lsm_cmd_reg_snd_model cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	rc = q6lsm_set_data(client, mode, detectfailure);
+	if (rc) {
+		pr_err("%s: Failed to set lsm data, err = %d\n",
+			__func__, rc);
+		return rc;
+	}
+
+	q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd), true);
+	cmd.hdr.opcode = LSM_SESSION_CMD_REGISTER_SOUND_MODEL;
+	cmd.model_addr_lsw = lower_32_bits(client->sound_model.phys);
+	cmd.model_addr_msw = msm_audio_populate_upper_32_bits(
+						client->sound_model.phys);
+	cmd.model_size = client->sound_model.size;
+	/* read updated mem_map_handle by q6lsm_mmapcallback */
+	rmb();
+	cmd.mem_map_handle = client->sound_model.mem_map_handle;
+
+	pr_debug("%s: addr %pK, size %d, handle 0x%x\n", __func__,
+		&client->sound_model.phys, cmd.model_size, cmd.mem_map_handle);
+	rc = q6lsm_apr_send_pkt(client, client->apr, &cmd, true, NULL);
+	if (rc)
+		pr_err("%s: Failed cmd op[0x%x]rc[%d]\n", __func__,
+		       cmd.hdr.opcode, rc);
+	else
+		pr_debug("%s: Register sound model succeeded\n", __func__);
+
+	return rc;
+}
+
+int q6lsm_deregister_sound_model(struct lsm_client *client)
+{
+	int rc;
+	struct lsm_cmd_reg_snd_model cmd;
+
+	if (!client) {
+		pr_err("APR handle NULL\n");
+		return -EINVAL;
+	}
+	if (!client->apr) {
+		pr_err("APR client handle NULL\n");
+		return -EINVAL;
+	}
+
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: session[%d]", __func__, client->session);
+		return -EINVAL;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	q6lsm_add_hdr(client, &cmd.hdr, sizeof(cmd.hdr), false);
+	cmd.hdr.opcode = LSM_SESSION_CMD_DEREGISTER_SOUND_MODEL;
+
+	rc = q6lsm_apr_send_pkt(client, client->apr, &cmd.hdr, true, NULL);
+	if (rc) {
+		pr_err("%s: Failed cmd opcode 0x%x, rc %d\n", __func__,
+		       cmd.hdr.opcode, rc);
+	} else {
+		pr_debug("%s: Deregister sound model succeeded\n", __func__);
+	}
+
+	q6lsm_snd_model_buf_free(client);
+
+	return rc;
+}
+
+static void q6lsm_add_mmaphdr(struct lsm_client *client, struct apr_hdr *hdr,
+			      u32 pkt_size, u32 cmd_flg, u32 token)
+{
+	pr_debug("%s: pkt size=%d cmd_flg=%d session=%d\n", __func__, pkt_size,
+		 cmd_flg, client->session);
+	hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				       APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	hdr->src_port = 0x00;
+	hdr->dest_port = client->session;
+	if (cmd_flg)
+		hdr->token = token;
+	hdr->pkt_size = pkt_size;
+}
+
+static int q6lsm_memory_map_regions(struct lsm_client *client,
+				    dma_addr_t dma_addr_p, uint32_t dma_buf_sz,
+				    uint32_t *mmap_p)
+{
+	struct avs_cmd_shared_mem_map_regions *mmap_regions = NULL;
+	struct avs_shared_map_region_payload *mregions = NULL;
+	void *mmap_region_cmd = NULL;
+	void *payload = NULL;
+	int rc;
+	int cmd_size = 0;
+
+	pr_debug("%s: dma_addr_p 0x%pK, dma_buf_sz %d, mmap_p 0x%pK, session %d\n",
+		__func__, &dma_addr_p, dma_buf_sz, mmap_p,
+		client->session);
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: session[%d]", __func__, client->session);
+		return -EINVAL;
+	}
+	cmd_size = sizeof(struct avs_cmd_shared_mem_map_regions) +
+		   sizeof(struct avs_shared_map_region_payload);
+
+	mmap_region_cmd = kzalloc(cmd_size, GFP_KERNEL);
+	if (!mmap_region_cmd)
+		return -ENOMEM;
+
+	mmap_regions = (struct avs_cmd_shared_mem_map_regions *)mmap_region_cmd;
+	q6lsm_add_mmaphdr(client, &mmap_regions->hdr, cmd_size, true,
+			  (client->session << 8));
+
+	mmap_regions->hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS;
+	mmap_regions->mem_pool_id = ADSP_MEMORY_MAP_SHMEM8_4K_POOL;
+	mmap_regions->num_regions = 1;
+	mmap_regions->property_flag = 0x00;
+	payload = ((u8 *)mmap_region_cmd +
+		   sizeof(struct avs_cmd_shared_mem_map_regions));
+	mregions = (struct avs_shared_map_region_payload *)payload;
+
+	mregions->shm_addr_lsw = lower_32_bits(dma_addr_p);
+	mregions->shm_addr_msw = msm_audio_populate_upper_32_bits(dma_addr_p);
+	mregions->mem_size_bytes = dma_buf_sz;
+
+	rc = q6lsm_apr_send_pkt(client, client->mmap_apr, mmap_region_cmd,
+				true, mmap_p);
+	if (rc)
+		pr_err("%s: Failed mmap_regions opcode 0x%x, rc %d\n",
+			__func__, mmap_regions->hdr.opcode, rc);
+
+	pr_debug("%s: leave %d\n", __func__, rc);
+	kfree(mmap_region_cmd);
+	return rc;
+}
+
+static int q6lsm_memory_unmap_regions(struct lsm_client *client,
+				      uint32_t handle)
+{
+	struct avs_cmd_shared_mem_unmap_regions unmap;
+	int rc = 0;
+	int cmd_size = 0;
+
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: session[%d]", __func__, client->session);
+		return -EINVAL;
+	}
+	cmd_size = sizeof(struct avs_cmd_shared_mem_unmap_regions);
+	q6lsm_add_mmaphdr(client, &unmap.hdr, cmd_size,
+			  true, (client->session << 8));
+	unmap.hdr.opcode = LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS;
+	unmap.mem_map_handle = handle;
+
+	pr_debug("%s: unmap handle 0x%x\n", __func__, unmap.mem_map_handle);
+	rc = q6lsm_apr_send_pkt(client, client->mmap_apr, &unmap, true,
+				NULL);
+	if (rc)
+		pr_err("%s: Failed mmap_regions opcode 0x%x rc %d\n",
+		       __func__, unmap.hdr.opcode, rc);
+
+	return rc;
+}
+
+static int q6lsm_send_cal(struct lsm_client *client,
+			  u32 set_params_opcode)
+{
+	int rc = 0;
+	struct lsm_cmd_set_params params;
+	struct lsm_set_params_hdr *params_hdr = &params.param_hdr;
+	struct apr_hdr *msg_hdr = &params.msg_hdr;
+	struct cal_block_data *cal_block = NULL;
+
+	pr_debug("%s: Session id %d\n", __func__, client->session);
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: session[%d]", __func__, client->session);
+		return -EINVAL;
+	}
+
+	if (lsm_common.cal_data[LSM_CAL_IDX] == NULL)
+		goto done;
+
+	mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+	cal_block = cal_utils_get_only_cal_block(
+		lsm_common.cal_data[LSM_CAL_IDX]);
+	if (cal_block == NULL)
+		goto unlock;
+
+	if (cal_block->cal_data.size <= 0) {
+		pr_debug("%s: No cal to send!\n", __func__);
+		rc = -EINVAL;
+		goto unlock;
+	}
+	if (cal_block->cal_data.size != client->lsm_cal_size) {
+		pr_err("%s: Cal size %zd doesn't match lsm cal size %d\n",
+			__func__, cal_block->cal_data.size,
+			client->lsm_cal_size);
+		rc = -EINVAL;
+		goto unlock;
+	}
+	/* Cache mmap address, only map once or if new addr */
+	lsm_common.common_client[client->session].session = client->session;
+	q6lsm_add_hdr(client, msg_hdr, sizeof(params), true);
+	msg_hdr->opcode = set_params_opcode;
+	q6lsm_set_param_hdr_info(params_hdr,
+			cal_block->cal_data.size,
+			lower_32_bits(client->lsm_cal_phy_addr),
+			msm_audio_populate_upper_32_bits(
+				client->lsm_cal_phy_addr),
+			client->sound_model.mem_map_handle);
+
+	pr_debug("%s: Cal Size = %zd", __func__,
+		cal_block->cal_data.size);
+	rc = q6lsm_apr_send_pkt(client, client->apr, &params, true, NULL);
+	if (rc)
+		pr_err("%s: Failed set_params opcode 0x%x, rc %d\n",
+		       __func__, msg_hdr->opcode, rc);
+unlock:
+	mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+done:
+	return rc;
+}
+
+
+int q6lsm_snd_model_buf_free(struct lsm_client *client)
+{
+	int rc;
+
+	pr_debug("%s: Session id %d\n", __func__, client->session);
+	if (CHECK_SESSION(client->session)) {
+		pr_err("%s: session[%d]", __func__, client->session);
+		return -EINVAL;
+	}
+
+	mutex_lock(&client->cmd_lock);
+	rc = q6lsm_memory_unmap_regions(client,
+					client->sound_model.mem_map_handle);
+	if (rc)
+		pr_err("%s: CMD Memory_unmap_regions failed %d\n",
+			__func__, rc);
+
+	if (client->sound_model.data) {
+		msm_audio_ion_free(client->sound_model.client,
+				 client->sound_model.handle);
+		client->sound_model.client = NULL;
+		client->sound_model.handle = NULL;
+		client->sound_model.data = NULL;
+		client->sound_model.phys = 0;
+		client->lsm_cal_phy_addr = 0;
+		client->lsm_cal_size = 0;
+	}
+	mutex_unlock(&client->cmd_lock);
+	return rc;
+}
+
+static struct lsm_client *q6lsm_get_lsm_client(int session_id)
+{
+	unsigned long flags;
+	struct lsm_client *client = NULL;
+
+	spin_lock_irqsave(&lsm_session_lock, flags);
+	if (session_id < LSM_MIN_SESSION_ID || session_id > LSM_MAX_SESSION_ID)
+		pr_err("%s: Invalid session %d\n", __func__, session_id);
+	else if (!lsm_session[session_id])
+		pr_err("%s: Not an active session %d\n", __func__, session_id);
+	else
+		client = lsm_session[session_id];
+	spin_unlock_irqrestore(&lsm_session_lock, flags);
+	return client;
+}
+
+/*
+ * q6lsm_mmapcallback : atomic context
+ */
+static int q6lsm_mmapcallback(struct apr_client_data *data, void *priv)
+{
+	unsigned long flags;
+	uint32_t command;
+	uint32_t retcode;
+	uint32_t sid;
+	const uint32_t *payload = data->payload;
+	struct lsm_client *client = NULL;
+
+	if (data->opcode == RESET_EVENTS) {
+		sid = (data->token >> 8) & 0x0F;
+		pr_debug("%s: SSR event received 0x%x, event 0x%x,\n"
+			 "proc 0x%x SID 0x%x\n", __func__, data->opcode,
+			 data->reset_event, data->reset_proc, sid);
+		lsm_common.common_client[sid].lsm_cal_phy_addr = 0;
+		cal_utils_clear_cal_block_q6maps(LSM_MAX_CAL_IDX,
+			lsm_common.cal_data);
+		lsm_common.set_custom_topology = 1;
+		return 0;
+	}
+
+	command = payload[0];
+	retcode = payload[1];
+	sid = (data->token >> 8) & 0x0F;
+	pr_debug("%s: opcode 0x%x command 0x%x return code 0x%x SID 0x%x\n",
+		 __func__, data->opcode, command, retcode, sid);
+	client = q6lsm_get_lsm_client(sid);
+	if (!client) {
+		pr_debug("%s: Session %d already freed\n", __func__, sid);
+		return 0;
+	}
+
+	switch (data->opcode) {
+	case LSM_SESSION_CMDRSP_SHARED_MEM_MAP_REGIONS:
+		if (atomic_read(&client->cmd_state) == CMD_STATE_WAIT_RESP) {
+			spin_lock_irqsave(&mmap_lock, flags);
+			*mmap_handle_p = command;
+			/* spin_unlock_irqrestore implies barrier */
+			spin_unlock_irqrestore(&mmap_lock, flags);
+			atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+			wake_up(&client->cmd_wait);
+		}
+		break;
+	case APR_BASIC_RSP_RESULT:
+		switch (command) {
+		case LSM_SESSION_CMD_SHARED_MEM_UNMAP_REGIONS:
+			atomic_set(&client->cmd_state, CMD_STATE_CLEARED);
+			wake_up(&client->cmd_wait);
+			break;
+		case LSM_SESSION_CMD_SHARED_MEM_MAP_REGIONS:
+			if (retcode != 0) {
+				/* error state, signal to stop waiting */
+				if (atomic_read(&client->cmd_state) ==
+					CMD_STATE_WAIT_RESP) {
+					spin_lock_irqsave(&mmap_lock, flags);
+					/* implies barrier */
+					spin_unlock_irqrestore(&mmap_lock,
+						flags);
+					atomic_set(&client->cmd_state,
+						CMD_STATE_CLEARED);
+					wake_up(&client->cmd_wait);
+				}
+			}
+			break;
+		default:
+			pr_warn("%s: Unexpected command 0x%x\n", __func__,
+				command);
+		}
+		/* fallthrough */
+	default:
+		pr_debug("%s: command 0x%x return code 0x%x opcode 0x%x\n",
+			 __func__, command, retcode, data->opcode);
+		break;
+	}
+	if (client->cb)
+		client->cb(data->opcode, data->token,
+			   data->payload, client->priv);
+	return 0;
+}
+
+int q6lsm_snd_model_buf_alloc(struct lsm_client *client, size_t len,
+			      bool allocate_module_data)
+{
+	int rc = -EINVAL;
+	struct cal_block_data		*cal_block = NULL;
+
+	size_t pad_zero = 0, total_mem = 0;
+
+	if (!client || len <= LSM_ALIGN_BOUNDARY)
+		return rc;
+
+	mutex_lock(&client->cmd_lock);
+
+	mutex_lock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+	cal_block = cal_utils_get_only_cal_block(
+		lsm_common.cal_data[LSM_CAL_IDX]);
+	if (cal_block == NULL)
+		goto fail;
+
+	pr_debug("%s:Snd Model len = %zd cal size %zd phys addr %pK", __func__,
+		len, cal_block->cal_data.size,
+		&cal_block->cal_data.paddr);
+	if (!cal_block->cal_data.paddr) {
+		pr_err("%s: No LSM calibration set for session", __func__);
+		rc = -EINVAL;
+		goto fail;
+	}
+	if (!client->sound_model.data) {
+
+		/*
+		 * if sound module is sent as set_param
+		 * Then memory needs to be allocated for
+		 * set_param payload as well.
+		 */
+		if (allocate_module_data)
+			len += sizeof(struct lsm_param_payload_common);
+
+		client->sound_model.size = len;
+		pad_zero = (LSM_ALIGN_BOUNDARY -
+			    (len % LSM_ALIGN_BOUNDARY));
+		if ((len > SIZE_MAX - pad_zero) ||
+		    (len + pad_zero >
+		     SIZE_MAX - cal_block->cal_data.size)) {
+			pr_err("%s: invalid allocation size, len = %zd, pad_zero =%zd, cal_size = %zd\n",
+				__func__, len, pad_zero,
+				cal_block->cal_data.size);
+			rc = -EINVAL;
+			goto fail;
+		}
+
+		total_mem = PAGE_ALIGN(pad_zero + len +
+			cal_block->cal_data.size);
+		pr_debug("%s: Pad zeros sound model %zd Total mem %zd\n",
+				 __func__, pad_zero, total_mem);
+		rc = msm_audio_ion_alloc("lsm_client",
+				&client->sound_model.client,
+				&client->sound_model.handle,
+				total_mem,
+				&client->sound_model.phys,
+				&len,
+				&client->sound_model.data);
+		if (rc) {
+			pr_err("%s: Audio ION alloc is failed, rc = %d\n",
+				__func__, rc);
+			goto fail;
+		}
+	pr_debug("%s: Length = %zd\n", __func__, len);
+	client->lsm_cal_phy_addr = (pad_zero +
+				    client->sound_model.phys +
+				    client->sound_model.size);
+	client->lsm_cal_size = cal_block->cal_data.size;
+	memcpy((client->sound_model.data + pad_zero +
+		client->sound_model.size),
+	       (uint32_t *)cal_block->cal_data.kvaddr, client->lsm_cal_size);
+	pr_debug("%s: Copy cal start virt_addr %pK phy_addr %pK\n"
+			 "Offset cal virtual Addr %pK\n", __func__,
+			 client->sound_model.data, &client->sound_model.phys,
+			 (pad_zero + client->sound_model.data +
+			 client->sound_model.size));
+	} else {
+		pr_err("%s: sound model busy\n", __func__);
+		rc = -EBUSY;
+		goto fail;
+	}
+	mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+	mutex_unlock(&client->cmd_lock);
+
+	rc = q6lsm_memory_map_regions(client, client->sound_model.phys,
+				      len,
+				      &client->sound_model.mem_map_handle);
+	if (rc) {
+		pr_err("%s: CMD Memory_map_regions failed %d\n", __func__, rc);
+		goto exit;
+	}
+
+	return 0;
+fail:
+	mutex_unlock(&lsm_common.cal_data[LSM_CAL_IDX]->lock);
+	mutex_unlock(&client->cmd_lock);
+exit:
+	q6lsm_snd_model_buf_free(client);
+	return rc;
+}
+
+static int q6lsm_cmd(struct lsm_client *client, int opcode, bool wait)
+{
+	struct apr_hdr hdr;
+	int rc;
+
+	pr_debug("%s: enter opcode %x wait %d\n", __func__, opcode, wait);
+	q6lsm_add_hdr(client, &hdr, sizeof(hdr), true);
+	switch (opcode) {
+	case LSM_SESSION_CMD_START:
+	case LSM_SESSION_CMD_STOP:
+	case LSM_SESSION_CMD_CLOSE_TX:
+	case LSM_SESSION_CMD_EOB:
+		hdr.opcode = opcode;
+		break;
+	default:
+		pr_err("%s: Invalid opcode 0x%x\n", __func__, opcode);
+		return -EINVAL;
+	}
+	rc = q6lsm_apr_send_pkt(client, client->apr, &hdr, wait, NULL);
+	if (rc)
+		pr_err("%s: Failed commmand 0x%x\n", __func__, hdr.opcode);
+
+	pr_debug("%s: leave %d\n", __func__, rc);
+	return rc;
+}
+
+static int q6lsm_send_param_epd_thres(
+		struct lsm_client *client,
+		void *data, struct lsm_module_param_ids *ids)
+{
+	struct snd_lsm_ep_det_thres *ep_det_data;
+	struct lsm_cmd_set_epd_threshold epd_cmd;
+	struct apr_hdr *msg_hdr = &epd_cmd.msg_hdr;
+	struct lsm_set_params_hdr *param_hdr =
+			&epd_cmd.param_hdr;
+	struct lsm_param_epd_thres *epd_thres =
+			&epd_cmd.epd_thres;
+	int rc;
+
+	ep_det_data = (struct snd_lsm_ep_det_thres *) data;
+	q6lsm_add_hdr(client, msg_hdr,
+		      sizeof(epd_cmd), true);
+	msg_hdr->opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
+	q6lsm_set_param_hdr_info(param_hdr,
+		sizeof(*epd_thres), 0, 0, 0);
+	q6lsm_set_param_common(&epd_thres->common, ids,
+		sizeof(*epd_thres) - sizeof(epd_thres->common),
+		LSM_SESSION_CMD_SET_PARAMS_V2);
+	epd_thres->minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+	epd_thres->epd_begin = ep_det_data->epd_begin;
+	epd_thres->epd_end = ep_det_data->epd_end;
+
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				&epd_cmd, true, NULL);
+	if (unlikely(rc))
+		pr_err("%s: EPD_THRESHOLD failed, rc %d\n",
+			__func__, rc);
+	return rc;
+}
+
+static int q6lsm_send_param_gain(
+		struct lsm_client *client,
+		u16 gain, struct lsm_module_param_ids *ids)
+{
+	struct lsm_cmd_set_gain lsm_cmd_gain;
+	struct apr_hdr *msg_hdr = &lsm_cmd_gain.msg_hdr;
+	struct lsm_param_gain *lsm_gain = &lsm_cmd_gain.lsm_gain;
+	int rc;
+
+	q6lsm_add_hdr(client, msg_hdr,
+		      sizeof(lsm_cmd_gain), true);
+	msg_hdr->opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
+	q6lsm_set_param_hdr_info(&lsm_cmd_gain.param_hdr,
+			sizeof(*lsm_gain), 0, 0, 0);
+	q6lsm_set_param_common(&lsm_gain->common, ids,
+		sizeof(*lsm_gain) - sizeof(lsm_gain->common),
+		LSM_SESSION_CMD_SET_PARAMS_V2);
+	lsm_gain->minor_version = QLSM_PARAM_ID_MINOR_VERSION;
+	lsm_gain->gain = gain;
+	lsm_gain->reserved = 0;
+
+	rc = q6lsm_apr_send_pkt(client, client->apr,
+				&lsm_cmd_gain, true, NULL);
+	if (unlikely(rc))
+		pr_err("%s: LSM_GAIN CMD send failed, rc %d\n",
+			 __func__, rc);
+	return rc;
+}
+
+int q6lsm_set_one_param(struct lsm_client *client,
+	struct lsm_params_info *p_info, void *data,
+	enum LSM_PARAM_TYPE param_type)
+{
+	int rc = 0, pkt_sz;
+	struct lsm_module_param_ids ids;
+	u8 *packet;
+
+	memset(&ids, 0, sizeof(ids));
+	switch (param_type) {
+	case LSM_ENDPOINT_DETECT_THRESHOLD: {
+		ids.module_id = p_info->module_id;
+		ids.param_id = p_info->param_id;
+		rc = q6lsm_send_param_epd_thres(client, data,
+						&ids);
+		break;
+	}
+
+	case LSM_OPERATION_MODE: {
+		struct snd_lsm_detect_mode *det_mode = data;
+		struct lsm_module_param_ids opmode_ids;
+		struct lsm_module_param_ids connectport_ids;
+
+		if (det_mode->mode == LSM_MODE_KEYWORD_ONLY_DETECTION) {
+			client->mode = 0x01;
+		} else if (det_mode->mode == LSM_MODE_USER_KEYWORD_DETECTION) {
+			client->mode = 0x03;
+		} else {
+			pr_err("%s: Incorrect detection mode %d\n",
+				__func__, det_mode->mode);
+			return -EINVAL;
+		}
+
+		client->mode |= det_mode->detect_failure << 2;
+		client->connect_to_port = get_lsm_port();
+
+		opmode_ids.module_id = p_info->module_id;
+		opmode_ids.param_id = p_info->param_id;
+
+		connectport_ids.module_id = LSM_MODULE_ID_FRAMEWORK;
+		connectport_ids.param_id = LSM_PARAM_ID_CONNECT_TO_PORT;
+
+		rc = q6lsm_send_params(client, &opmode_ids, &connectport_ids,
+				       LSM_SESSION_CMD_SET_PARAMS_V2);
+		if (rc)
+			pr_err("%s: OPERATION_MODE failed, rc %d\n",
+				__func__, rc);
+		break;
+	}
+
+	case LSM_GAIN: {
+		struct snd_lsm_gain *lsm_gain = (struct snd_lsm_gain *) data;
+
+		ids.module_id = p_info->module_id;
+		ids.param_id = p_info->param_id;
+		rc = q6lsm_send_param_gain(client, lsm_gain->gain, &ids);
+		if (rc)
+			pr_err("%s: LSM_GAIN command failed, rc %d\n",
+				__func__, rc);
+		break;
+	}
+
+	case LSM_MIN_CONFIDENCE_LEVELS:
+		ids.module_id = p_info->module_id;
+		ids.param_id = p_info->param_id;
+		rc = q6lsm_send_confidence_levels(client, &ids,
+				LSM_SESSION_CMD_SET_PARAMS_V2);
+		if (rc)
+			pr_err("%s: CONFIDENCE_LEVELS cmd failed, rc %d\n",
+				 __func__, rc);
+		break;
+	case LSM_REG_SND_MODEL: {
+		struct lsm_cmd_set_params model_param;
+		u32 payload_size;
+
+		memset(&model_param, 0, sizeof(model_param));
+		q6lsm_add_hdr(client, &model_param.msg_hdr,
+			      sizeof(model_param), true);
+		model_param.msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
+		payload_size = p_info->param_size +
+			       sizeof(struct lsm_param_payload_common);
+		q6lsm_set_param_hdr_info(&model_param.param_hdr,
+				payload_size,
+				lower_32_bits(client->sound_model.phys),
+				msm_audio_populate_upper_32_bits(
+					client->sound_model.phys),
+				client->sound_model.mem_map_handle);
+
+		rc = q6lsm_apr_send_pkt(client, client->apr,
+					&model_param, true, NULL);
+		if (rc) {
+			pr_err("%s: REG_SND_MODEL failed, rc %d\n",
+				__func__, rc);
+			return rc;
+		}
+
+		rc = q6lsm_send_cal(client, LSM_SESSION_CMD_SET_PARAMS);
+		if (rc)
+			pr_err("%s: Failed to send lsm cal, err = %d\n",
+				__func__, rc);
+		break;
+	}
+
+	case LSM_DEREG_SND_MODEL: {
+		struct lsm_param_payload_common *common;
+		struct lsm_cmd_set_params *param;
+
+		pkt_sz = sizeof(*param) + sizeof(*common);
+		packet = kzalloc(pkt_sz, GFP_KERNEL);
+		if (!packet) {
+			pr_err("%s: No memory for DEREG_SND_MODEL pkt, size = %d\n",
+				__func__, pkt_sz);
+			return -ENOMEM;
+		}
+
+		param = (struct lsm_cmd_set_params *) packet;
+		common = (struct lsm_param_payload_common *)
+				(packet + sizeof(*param));
+		q6lsm_add_hdr(client, &param->msg_hdr, pkt_sz, true);
+		param->msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
+		q6lsm_set_param_hdr_info(&param->param_hdr,
+					 sizeof(*common),
+					 0, 0, 0);
+		ids.module_id = p_info->module_id;
+		ids.param_id = p_info->param_id;
+		q6lsm_set_param_common(common, &ids, 0,
+				       LSM_SESSION_CMD_SET_PARAMS_V2);
+		rc = q6lsm_apr_send_pkt(client, client->apr,
+					packet, true, NULL);
+		if (rc)
+			pr_err("%s: DEREG_SND_MODEL failed, rc %d\n",
+				__func__, rc);
+		kfree(packet);
+		break;
+	}
+
+	case LSM_CUSTOM_PARAMS: {
+		struct apr_hdr *hdr;
+		u8 *custom_data;
+
+		if (p_info->param_size <
+		    sizeof(struct lsm_param_payload_common)) {
+			pr_err("%s: Invalid param_size %d\n",
+				__func__, p_info->param_size);
+			return -EINVAL;
+		}
+
+		pkt_sz = p_info->param_size + sizeof(*hdr);
+		packet = kzalloc(pkt_sz, GFP_KERNEL);
+		if (!packet) {
+			pr_err("%s: no memory for CUSTOM_PARAMS, size = %d\n",
+				__func__, pkt_sz);
+			return -ENOMEM;
+		}
+
+		hdr = (struct apr_hdr *) packet;
+		custom_data = (u8 *) (packet + sizeof(*hdr));
+		q6lsm_add_hdr(client, hdr, pkt_sz, true);
+		hdr->opcode = LSM_SESSION_CMD_SET_PARAMS_V2;
+		memcpy(custom_data, data, p_info->param_size);
+
+		rc = q6lsm_apr_send_pkt(client, client->apr,
+					packet, true, NULL);
+		if (rc)
+			pr_err("%s: CUSTOM_PARAMS failed, rc %d\n",
+				__func__, rc);
+		kfree(packet);
+		break;
+	}
+	default:
+		pr_err("%s: wrong param_type 0x%x\n",
+			__func__, p_info->param_type);
+	}
+
+	return rc;
+}
+
+
+int q6lsm_start(struct lsm_client *client, bool wait)
+{
+	return q6lsm_cmd(client, LSM_SESSION_CMD_START, wait);
+}
+
+int q6lsm_stop(struct lsm_client *client, bool wait)
+{
+	return q6lsm_cmd(client, LSM_SESSION_CMD_STOP, wait);
+}
+
+int q6lsm_close(struct lsm_client *client)
+{
+	return q6lsm_cmd(client, LSM_SESSION_CMD_CLOSE_TX, true);
+}
+
+int q6lsm_lab_control(struct lsm_client *client, u32 enable)
+{
+	int rc = 0;
+	struct lsm_params_lab_enable lab_enable;
+	struct lsm_params_lab_config lab_config;
+	struct lsm_module_param_ids lab_ids;
+	u32 param_size;
+
+	if (!client) {
+		pr_err("%s: invalid param client %pK\n", __func__, client);
+		return -EINVAL;
+	}
+	/* enable/disable lab on dsp */
+	q6lsm_add_hdr(client, &lab_enable.msg_hdr, sizeof(lab_enable), true);
+	lab_enable.msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS;
+	q6lsm_set_param_hdr_info(&lab_enable.params_hdr,
+				 sizeof(struct lsm_lab_enable),
+				 0, 0, 0);
+	param_size = (sizeof(struct lsm_lab_enable) -
+		      sizeof(struct lsm_param_payload_common));
+	lab_ids.module_id = LSM_MODULE_ID_LAB;
+	lab_ids.param_id = LSM_PARAM_ID_LAB_ENABLE;
+	q6lsm_set_param_common(&lab_enable.lab_enable.common,
+				&lab_ids, param_size,
+				LSM_SESSION_CMD_SET_PARAMS);
+	lab_enable.lab_enable.enable = (enable) ? 1 : 0;
+	rc = q6lsm_apr_send_pkt(client, client->apr, &lab_enable, true, NULL);
+	if (rc) {
+		pr_err("%s: Lab enable failed rc %d\n", __func__, rc);
+		return rc;
+	}
+	if (!enable)
+		goto exit;
+	/* lab session is being enabled set the config values */
+	q6lsm_add_hdr(client, &lab_config.msg_hdr, sizeof(lab_config), true);
+	lab_config.msg_hdr.opcode = LSM_SESSION_CMD_SET_PARAMS;
+	q6lsm_set_param_hdr_info(&lab_config.params_hdr,
+				 sizeof(struct lsm_lab_config),
+				 0, 0, 0);
+	lab_ids.module_id = LSM_MODULE_ID_LAB;
+	lab_ids.param_id = LSM_PARAM_ID_LAB_CONFIG;
+	param_size = (sizeof(struct lsm_lab_config) -
+		      sizeof(struct lsm_param_payload_common));
+	q6lsm_set_param_common(&lab_config.lab_config.common,
+			       &lab_ids, param_size,
+			       LSM_SESSION_CMD_SET_PARAMS);
+	lab_config.lab_config.minor_version = 1;
+	lab_config.lab_config.wake_up_latency_ms = 250;
+	rc = q6lsm_apr_send_pkt(client, client->apr, &lab_config, true, NULL);
+	if (rc) {
+		pr_err("%s: Lab config failed rc %d disable lab\n",
+		 __func__, rc);
+		/* Lab config failed disable lab */
+		lab_enable.lab_enable.enable = 0;
+		if (q6lsm_apr_send_pkt(client, client->apr,
+			&lab_enable, true, NULL))
+			pr_err("%s: Lab disable failed\n", __func__);
+	}
+exit:
+	return rc;
+}
+
+int q6lsm_stop_lab(struct lsm_client *client)
+{
+	int rc = 0;
+
+	if (!client) {
+		pr_err("%s: invalid param client %pK\n", __func__, client);
+		return -EINVAL;
+	}
+	rc = q6lsm_cmd(client, LSM_SESSION_CMD_EOB, true);
+	if (rc)
+		pr_err("%s: Lab stop failed %d\n", __func__, rc);
+	return rc;
+}
+
+int q6lsm_read(struct lsm_client *client, struct lsm_cmd_read *read)
+{
+	int rc = 0;
+
+	if (!client || !read) {
+		pr_err("%s: Invalid params client %pK read %pK\n", __func__,
+			client, read);
+		return -EINVAL;
+	}
+	pr_debug("%s: read call memmap handle %x address %x%x size %d\n",
+		 __func__, read->mem_map_handle, read->buf_addr_msw,
+		read->buf_addr_lsw, read->buf_size);
+	q6lsm_add_hdr(client, &read->hdr, sizeof(struct lsm_cmd_read), true);
+	read->hdr.opcode = LSM_SESSION_CMD_READ;
+	rc = q6lsm_apr_send_pkt(client, client->apr, read, false, NULL);
+	if (rc)
+		pr_err("%s: read buffer call failed rc %d\n", __func__, rc);
+	return rc;
+}
+
+int q6lsm_lab_buffer_alloc(struct lsm_client *client, bool alloc)
+{
+	int ret = 0, i = 0;
+	size_t allocate_size = 0, len = 0;
+
+	if (!client) {
+		pr_err("%s: invalid client\n", __func__);
+		return -EINVAL;
+	}
+	if (alloc) {
+		if (client->lab_buffer) {
+			pr_err("%s: buffers are allocated period count %d period size %d\n",
+				__func__,
+				client->hw_params.period_count,
+				client->hw_params.buf_sz);
+			return -EINVAL;
+		}
+		allocate_size = client->hw_params.period_count *
+				client->hw_params.buf_sz;
+		allocate_size = PAGE_ALIGN(allocate_size);
+		client->lab_buffer =
+			kzalloc(sizeof(struct lsm_lab_buffer) *
+			client->hw_params.period_count, GFP_KERNEL);
+		if (!client->lab_buffer) {
+			pr_err("%s: memory allocation for lab buffer failed count %d\n"
+				, __func__,
+				client->hw_params.period_count);
+			return -ENOMEM;
+		}
+		ret = msm_audio_ion_alloc("lsm_lab",
+			&client->lab_buffer[0].client,
+			&client->lab_buffer[0].handle,
+			allocate_size, &client->lab_buffer[0].phys,
+			&len,
+			&client->lab_buffer[0].data);
+		if (ret)
+			pr_err("%s: ion alloc failed ret %d size %zd\n",
+				__func__, ret, allocate_size);
+		else {
+			ret = q6lsm_memory_map_regions(client,
+				client->lab_buffer[0].phys, len,
+				&client->lab_buffer[0].mem_map_handle);
+			if (ret) {
+				pr_err("%s: memory map filed ret %d size %zd\n",
+					__func__, ret, len);
+				msm_audio_ion_free(
+				client->lab_buffer[0].client,
+				client->lab_buffer[0].handle);
+			}
+		}
+		if (ret) {
+			pr_err("%s: alloc lab buffer failed ret %d\n",
+				__func__, ret);
+			kfree(client->lab_buffer);
+			client->lab_buffer = NULL;
+		} else {
+			pr_debug("%s: Memory map handle %x phys %pK size %d\n",
+				__func__,
+				client->lab_buffer[0].mem_map_handle,
+				&client->lab_buffer[0].phys,
+				client->hw_params.buf_sz);
+			for (i = 0; i < client->hw_params.period_count; i++) {
+				client->lab_buffer[i].phys =
+				client->lab_buffer[0].phys +
+				(i * client->hw_params.buf_sz);
+				client->lab_buffer[i].size =
+				client->hw_params.buf_sz;
+				client->lab_buffer[i].data =
+				(u8 *)(client->lab_buffer[0].data) +
+				(i * client->hw_params.buf_sz);
+				client->lab_buffer[i].mem_map_handle =
+				client->lab_buffer[0].mem_map_handle;
+			}
+		}
+	} else {
+		ret = q6lsm_memory_unmap_regions(client,
+			client->lab_buffer[0].mem_map_handle);
+		if (!ret)
+			msm_audio_ion_free(
+			client->lab_buffer[0].client,
+			client->lab_buffer[0].handle);
+		else
+			pr_err("%s: unmap failed not freeing memory\n",
+			__func__);
+		kfree(client->lab_buffer);
+		client->lab_buffer = NULL;
+	}
+	return ret;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+	int ret = -EINVAL;
+
+	switch (cal_type) {
+	case LSM_CUST_TOPOLOGY_CAL_TYPE:
+		ret = LSM_CUSTOM_TOP_IDX;
+		break;
+	case LSM_TOPOLOGY_CAL_TYPE:
+		ret = LSM_TOP_IDX;
+		break;
+	case LSM_CAL_TYPE:
+		ret = LSM_CAL_IDX;
+		break;
+	default:
+		pr_err("%s: invalid cal type %d!\n", __func__, cal_type);
+	}
+	return ret;
+}
+
+static int q6lsm_alloc_cal(int32_t cal_type,
+				size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_alloc_cal(data_size, data,
+		lsm_common.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6lsm_dealloc_cal(int32_t cal_type,
+				size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+		lsm_common.cal_data[cal_index]);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int q6lsm_set_cal(int32_t cal_type,
+			size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s:\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_set_cal(data_size, data,
+		lsm_common.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (cal_index == LSM_CUSTOM_TOP_IDX) {
+		mutex_lock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+		lsm_common.set_custom_topology = 1;
+		mutex_unlock(&lsm_common.cal_data[LSM_CUSTOM_TOP_IDX]->lock);
+	}
+
+done:
+	return ret;
+}
+
+static void lsm_delete_cal_data(void)
+{
+	pr_debug("%s:\n", __func__);
+
+	cal_utils_destroy_cal_types(LSM_MAX_CAL_IDX, lsm_common.cal_data);
+}
+
+static int q6lsm_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info	cal_type_info[] = {
+		{{LSM_CUST_TOPOLOGY_CAL_TYPE,
+		{q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL,
+		q6lsm_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{LSM_TOPOLOGY_CAL_TYPE,
+		{NULL, NULL, NULL,
+		q6lsm_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{LSM_CAL_TYPE,
+		{q6lsm_alloc_cal, q6lsm_dealloc_cal, NULL,
+		q6lsm_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} }
+	};
+	pr_debug("%s:\n", __func__);
+
+	ret = cal_utils_create_cal_types(LSM_MAX_CAL_IDX,
+		lsm_common.cal_data, cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: could not create cal type!\n",
+			__func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return ret;
+err:
+	lsm_delete_cal_data();
+	return ret;
+}
+
+static int __init q6lsm_init(void)
+{
+	int i = 0;
+
+	pr_debug("%s:\n", __func__);
+	spin_lock_init(&lsm_session_lock);
+	spin_lock_init(&mmap_lock);
+	mutex_init(&lsm_common.apr_lock);
+	for (; i <= LSM_MAX_SESSION_ID; i++) {
+		lsm_common.common_client[i].session = LSM_CONTROL_SESSION;
+		init_waitqueue_head(&lsm_common.common_client[i].cmd_wait);
+		mutex_init(&lsm_common.common_client[i].cmd_lock);
+		atomic_set(&lsm_common.common_client[i].cmd_state,
+			   CMD_STATE_CLEARED);
+	}
+
+	if (q6lsm_init_cal_data())
+		pr_err("%s: could not init cal data!\n", __func__);
+
+	return 0;
+}
+
+static void __exit q6lsm_exit(void)
+{
+	lsm_delete_cal_data();
+}
+
+device_initcall(q6lsm_init);
+__exitcall(q6lsm_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.c b/sound/soc/msm/qdsp6v2/q6voice.c
new file mode 100644
index 0000000..1adb177
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6voice.c
@@ -0,0 +1,8381 @@
+/*  Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/mutex.h>
+#include <linux/msm_audio_ion.h>
+
+#include <soc/qcom/socinfo.h>
+#include <linux/qdsp6v2/apr_tal.h>
+
+#include "sound/q6audio-v2.h"
+#include "sound/apr_audio-v2.h"
+#include "sound/q6afe-v2.h"
+#include <sound/audio_cal_utils.h>
+#include "q6voice.h"
+#include <sound/adsp_err.h>
+
+#define TIMEOUT_MS 300
+
+
+#define CMD_STATUS_SUCCESS 0
+#define CMD_STATUS_FAIL 1
+
+enum {
+	VOC_TOKEN_NONE,
+	VOIP_MEM_MAP_TOKEN,
+	VOC_CAL_MEM_MAP_TOKEN,
+	VOC_VOICE_HOST_PCM_MAP_TOKEN,
+	VOC_RTAC_MEM_MAP_TOKEN,
+	VOC_SOURCE_TRACKING_MEM_MAP_TOKEN
+};
+
+struct cvd_version_table cvd_version_table_mapping[CVD_INT_VERSION_MAX] = {
+		{CVD_VERSION_DEFAULT, CVD_INT_VERSION_DEFAULT},
+		{CVD_VERSION_0_0, CVD_INT_VERSION_0_0},
+		{CVD_VERSION_2_1, CVD_INT_VERSION_2_1},
+		{CVD_VERSION_2_2, CVD_INT_VERSION_2_2},
+		{CVD_VERSION_2_3, CVD_INT_VERSION_2_3},
+};
+
+static struct common_data common;
+static bool module_initialized;
+
+static int voice_send_enable_vocproc_cmd(struct voice_data *v);
+static int voice_send_netid_timing_cmd(struct voice_data *v);
+static int voice_send_attach_vocproc_cmd(struct voice_data *v);
+static int voice_send_set_device_cmd(struct voice_data *v);
+static int voice_send_vol_step_cmd(struct voice_data *v);
+static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
+						    uint32_t mem_handle);
+static int voice_send_mvm_cal_network_cmd(struct voice_data *v);
+static int voice_send_mvm_media_type_cmd(struct voice_data *v);
+static int voice_send_mvm_cvd_version_cmd(struct voice_data *v);
+static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v);
+static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v);
+static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
+						     uint32_t mode);
+
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_create_cmd(struct voice_data *v);
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v);
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v);
+static int voice_send_cvp_device_channels_cmd(struct voice_data *v);
+static int voice_send_cvp_topology_commit_cmd(struct voice_data *v);
+
+static int voice_cvs_stop_playback(struct voice_data *v);
+static int voice_cvs_start_playback(struct voice_data *v);
+static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode);
+static int voice_cvs_stop_record(struct voice_data *v);
+
+static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv);
+static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv);
+static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv);
+
+static int voice_send_set_pp_enable_cmd(struct voice_data *v,
+					uint32_t module_id, int enable);
+static int is_cal_memory_allocated(void);
+static bool is_cvd_version_queried(void);
+static int is_voip_memory_allocated(void);
+static int voice_get_cvd_int_version(char *cvd_ver_string);
+static int voice_alloc_cal_mem_map_table(void);
+static int voice_alloc_rtac_mem_map_table(void);
+static int voice_alloc_oob_shared_mem(void);
+static int voice_free_oob_shared_mem(void);
+static int voice_alloc_oob_mem_table(void);
+static int voice_alloc_and_map_oob_mem(struct voice_data *v);
+
+static struct voice_data *voice_get_session_by_idx(int idx);
+
+static int remap_cal_data(struct cal_block_data *cal_block,
+			  uint32_t session_id);
+static int voice_unmap_cal_memory(int32_t cal_type,
+				  struct cal_block_data *cal_block);
+
+static int is_source_tracking_shared_memomry_allocated(void);
+static int voice_alloc_source_tracking_shared_memory(void);
+static int voice_alloc_and_map_source_tracking_shared_memory(
+						struct voice_data *v);
+static int voice_unmap_and_free_source_tracking_shared_memory(
+						struct voice_data *v);
+static int voice_send_set_sound_focus_cmd(struct voice_data *v,
+				struct sound_focus_param soundFocusData);
+static int voice_send_get_sound_focus_cmd(struct voice_data *v,
+				struct sound_focus_param *soundFocusData);
+static int voice_send_get_source_tracking_cmd(struct voice_data *v,
+			struct source_tracking_param *sourceTrackingData);
+
+static void voice_itr_init(struct voice_session_itr *itr,
+			   u32 session_id)
+{
+	if (itr == NULL)
+		return;
+	itr->session_idx = voice_get_idx_for_session(session_id);
+	if (session_id == ALL_SESSION_VSID)
+		itr->cur_idx = 0;
+	else
+		itr->cur_idx = itr->session_idx;
+
+}
+
+static bool voice_itr_get_next_session(struct voice_session_itr *itr,
+					struct voice_data **voice)
+{
+	bool ret = false;
+
+	if (itr == NULL)
+		return false;
+	pr_debug("%s : cur idx = %d session idx = %d\n",
+			 __func__, itr->cur_idx, itr->session_idx);
+
+	if (itr->cur_idx <= itr->session_idx) {
+		ret = true;
+		*voice = voice_get_session_by_idx(itr->cur_idx);
+		itr->cur_idx++;
+	} else {
+		*voice = NULL;
+	}
+
+	return ret;
+}
+
+static bool voice_is_valid_session_id(uint32_t session_id)
+{
+	bool ret = false;
+
+	switch (session_id) {
+	case VOICE_SESSION_VSID:
+	case VOICE2_SESSION_VSID:
+	case VOLTE_SESSION_VSID:
+	case VOIP_SESSION_VSID:
+	case QCHAT_SESSION_VSID:
+	case VOWLAN_SESSION_VSID:
+	case VOICEMMODE1_VSID:
+	case VOICEMMODE2_VSID:
+	case ALL_SESSION_VSID:
+		ret = true;
+		break;
+	default:
+		pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+		break;
+	}
+
+	return ret;
+}
+
+static u16 voice_get_mvm_handle(struct voice_data *v)
+{
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return 0;
+	}
+
+	pr_debug("%s: mvm_handle %d\n", __func__, v->mvm_handle);
+
+	return v->mvm_handle;
+}
+
+static void voice_set_mvm_handle(struct voice_data *v, u16 mvm_handle)
+{
+	pr_debug("%s: mvm_handle %d\n", __func__, mvm_handle);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return;
+	}
+
+	v->mvm_handle = mvm_handle;
+}
+
+static u16 voice_get_cvs_handle(struct voice_data *v)
+{
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return 0;
+	}
+
+	pr_debug("%s: cvs_handle %d\n", __func__, v->cvs_handle);
+
+	return v->cvs_handle;
+}
+
+static void voice_set_cvs_handle(struct voice_data *v, u16 cvs_handle)
+{
+	pr_debug("%s: cvs_handle %d\n", __func__, cvs_handle);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return;
+	}
+
+	v->cvs_handle = cvs_handle;
+}
+
+static u16 voice_get_cvp_handle(struct voice_data *v)
+{
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return 0;
+	}
+
+	pr_debug("%s: cvp_handle %d\n", __func__, v->cvp_handle);
+
+	return v->cvp_handle;
+}
+
+static void voice_set_cvp_handle(struct voice_data *v, u16 cvp_handle)
+{
+	pr_debug("%s: cvp_handle %d\n", __func__, cvp_handle);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return;
+	}
+
+	v->cvp_handle = cvp_handle;
+}
+
+char *voc_get_session_name(u32 session_id)
+{
+	char *session_name = NULL;
+
+	if (session_id == common.voice[VOC_PATH_PASSIVE].session_id) {
+		session_name = VOICE_SESSION_NAME;
+	} else if (session_id ==
+			common.voice[VOC_PATH_VOLTE_PASSIVE].session_id) {
+		session_name = VOLTE_SESSION_NAME;
+	} else if (session_id ==
+			common.voice[VOC_PATH_QCHAT_PASSIVE].session_id) {
+		session_name = QCHAT_SESSION_NAME;
+	} else if (session_id ==
+			common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id) {
+		session_name = VOWLAN_SESSION_NAME;
+	} else if (session_id ==
+		common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id) {
+		session_name = VOICEMMODE1_NAME;
+	} else if (session_id ==
+		common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id) {
+		session_name = VOICEMMODE2_NAME;
+	} else if (session_id == common.voice[VOC_PATH_FULL].session_id) {
+		session_name = VOIP_SESSION_NAME;
+	}
+	return session_name;
+}
+
+uint32_t voc_get_session_id(char *name)
+{
+	u32 session_id = 0;
+
+	if (name != NULL) {
+		if (!strcmp(name, "Voice session"))
+			session_id = common.voice[VOC_PATH_PASSIVE].session_id;
+		else if (!strcmp(name, "Voice2 session"))
+			session_id =
+			common.voice[VOC_PATH_VOICE2_PASSIVE].session_id;
+		else if (!strcmp(name, "VoLTE session"))
+			session_id =
+			common.voice[VOC_PATH_VOLTE_PASSIVE].session_id;
+		else if (!strcmp(name, "QCHAT session"))
+			session_id =
+			common.voice[VOC_PATH_QCHAT_PASSIVE].session_id;
+		else if (!strcmp(name, "VoWLAN session"))
+			session_id =
+			common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id;
+		else if (!strcmp(name, "VoiceMMode1"))
+			session_id =
+			common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id;
+		else if (!strcmp(name, "VoiceMMode2"))
+			session_id =
+			common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id;
+		else
+			session_id = common.voice[VOC_PATH_FULL].session_id;
+
+		pr_debug("%s: %s has session id 0x%x\n", __func__, name,
+				session_id);
+	}
+
+	return session_id;
+}
+
+static struct voice_data *voice_get_session(u32 session_id)
+{
+	struct voice_data *v = NULL;
+
+	switch (session_id) {
+	case VOICE_SESSION_VSID:
+		v = &common.voice[VOC_PATH_PASSIVE];
+		break;
+
+	case VOICE2_SESSION_VSID:
+		v = &common.voice[VOC_PATH_VOICE2_PASSIVE];
+		break;
+
+	case VOLTE_SESSION_VSID:
+		v = &common.voice[VOC_PATH_VOLTE_PASSIVE];
+		break;
+
+	case VOIP_SESSION_VSID:
+		v = &common.voice[VOC_PATH_FULL];
+		break;
+
+	case QCHAT_SESSION_VSID:
+		v = &common.voice[VOC_PATH_QCHAT_PASSIVE];
+		break;
+
+	case VOWLAN_SESSION_VSID:
+		v = &common.voice[VOC_PATH_VOWLAN_PASSIVE];
+		break;
+
+	case VOICEMMODE1_VSID:
+		v = &common.voice[VOC_PATH_VOICEMMODE1_PASSIVE];
+		break;
+
+	case VOICEMMODE2_VSID:
+		v = &common.voice[VOC_PATH_VOICEMMODE2_PASSIVE];
+		break;
+
+	case ALL_SESSION_VSID:
+		break;
+
+	default:
+		pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+		break;
+	}
+
+	pr_debug("%s:session_id 0x%x session handle %pK\n",
+		__func__, session_id, v);
+
+	return v;
+}
+
+int voice_get_idx_for_session(u32 session_id)
+{
+	int idx = 0;
+
+	switch (session_id) {
+	case VOICE_SESSION_VSID:
+		idx = VOC_PATH_PASSIVE;
+		break;
+
+	case VOICE2_SESSION_VSID:
+		idx = VOC_PATH_VOICE2_PASSIVE;
+		break;
+
+	case VOLTE_SESSION_VSID:
+		idx = VOC_PATH_VOLTE_PASSIVE;
+		break;
+
+	case VOIP_SESSION_VSID:
+		idx = VOC_PATH_FULL;
+		break;
+
+	case QCHAT_SESSION_VSID:
+		idx = VOC_PATH_QCHAT_PASSIVE;
+		break;
+
+	case VOWLAN_SESSION_VSID:
+		idx = VOC_PATH_VOWLAN_PASSIVE;
+		break;
+
+	case VOICEMMODE1_VSID:
+		idx = VOC_PATH_VOICEMMODE1_PASSIVE;
+		break;
+
+	case VOICEMMODE2_VSID:
+		idx = VOC_PATH_VOICEMMODE2_PASSIVE;
+		break;
+
+	case ALL_SESSION_VSID:
+		idx = MAX_VOC_SESSIONS - 1;
+		break;
+
+	default:
+		pr_err("%s: Invalid session_id : %x\n", __func__, session_id);
+
+		break;
+	}
+
+	return idx;
+}
+
+static struct voice_data *voice_get_session_by_idx(int idx)
+{
+	return ((idx < 0 || idx >= MAX_VOC_SESSIONS) ?
+				NULL : &common.voice[idx]);
+}
+
+static bool is_voip_session(u32 session_id)
+{
+	return (session_id == common.voice[VOC_PATH_FULL].session_id);
+}
+
+static bool is_volte_session(u32 session_id)
+{
+	return (session_id == common.voice[VOC_PATH_VOLTE_PASSIVE].session_id);
+}
+
+static bool is_voice2_session(u32 session_id)
+{
+	return (session_id == common.voice[VOC_PATH_VOICE2_PASSIVE].session_id);
+}
+
+static bool is_qchat_session(u32 session_id)
+{
+	return (session_id == common.voice[VOC_PATH_QCHAT_PASSIVE].session_id);
+}
+
+static bool is_vowlan_session(u32 session_id)
+{
+	return (session_id == common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id);
+}
+
+static bool is_voicemmode1(u32 session_id)
+{
+	return session_id ==
+			common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id;
+}
+
+static bool is_voicemmode2(u32 session_id)
+{
+	return session_id ==
+			common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id;
+}
+
+static bool is_voc_state_active(int voc_state)
+{
+	if ((voc_state == VOC_RUN) ||
+		(voc_state == VOC_CHANGE) ||
+		(voc_state == VOC_STANDBY))
+		return true;
+
+	return false;
+}
+
+static void voc_set_error_state(uint16_t reset_proc)
+{
+	struct voice_data *v = NULL;
+	int i;
+
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		v = &common.voice[i];
+		if (v != NULL)
+			v->voc_state = VOC_ERROR;
+	}
+}
+
+static bool is_other_session_active(u32 session_id)
+{
+	int i;
+	bool ret = false;
+
+	/* Check if there is other active session except the input one */
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		if (common.voice[i].session_id == session_id)
+			continue;
+
+		if (is_voc_state_active(common.voice[i].voc_state)) {
+			ret = true;
+			break;
+		}
+	}
+	pr_debug("%s: ret %d\n", __func__, ret);
+
+	return ret;
+}
+
+static bool is_sub1_vsid(u32 session_id)
+{
+	bool ret;
+
+	switch (session_id) {
+	case VOICE_SESSION_VSID:
+	case VOLTE_SESSION_VSID:
+	case VOWLAN_SESSION_VSID:
+	case VOICEMMODE1_VSID:
+		ret = true;
+		break;
+	default:
+		ret = false;
+	}
+
+	return ret;
+}
+
+static bool is_sub2_vsid(u32 session_id)
+{
+	bool ret;
+
+	switch (session_id) {
+	case VOICE2_SESSION_VSID:
+	case VOICEMMODE2_VSID:
+		ret = true;
+		break;
+	default:
+		ret = false;
+	}
+
+	return ret;
+}
+
+static bool is_voice_app_id(u32 session_id)
+{
+	return is_sub1_vsid(session_id) || is_sub2_vsid(session_id);
+}
+
+static void init_session_id(void)
+{
+	common.voice[VOC_PATH_PASSIVE].session_id = VOICE_SESSION_VSID;
+	common.voice[VOC_PATH_VOLTE_PASSIVE].session_id = VOLTE_SESSION_VSID;
+	common.voice[VOC_PATH_VOICE2_PASSIVE].session_id = VOICE2_SESSION_VSID;
+	common.voice[VOC_PATH_FULL].session_id = VOIP_SESSION_VSID;
+	common.voice[VOC_PATH_QCHAT_PASSIVE].session_id = QCHAT_SESSION_VSID;
+	common.voice[VOC_PATH_VOWLAN_PASSIVE].session_id = VOWLAN_SESSION_VSID;
+	common.voice[VOC_PATH_VOICEMMODE1_PASSIVE].session_id =
+							VOICEMMODE1_VSID;
+	common.voice[VOC_PATH_VOICEMMODE2_PASSIVE].session_id =
+							VOICEMMODE2_VSID;
+}
+
+static bool is_cvd_version_queried(void)
+{
+	bool ret = 0;
+
+	if (!strcmp(common.cvd_version, CVD_VERSION_DEFAULT))
+		ret = false;
+	else
+		ret = true;
+
+	return ret;
+}
+
+static int voice_get_cvd_int_version(char *cvd_ver_string)
+{
+	unsigned int idx;
+	int cvd_int_ver = CVD_INT_VERSION_DEFAULT;
+
+	for (idx = 0; idx < CVD_INT_VERSION_MAX; idx++) {
+		if (strcmp((char *)cvd_ver_string,
+			  cvd_version_table_mapping[idx].cvd_ver) == 0) {
+			cvd_int_ver =
+			cvd_version_table_mapping[idx].cvd_ver_int;
+			break;
+		}
+	}
+	return cvd_int_ver;
+}
+
+static int voice_apr_register(uint32_t session_id)
+{
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&common.common_lock);
+
+	/* register callback to APR */
+	if (common.apr_q6_mvm == NULL) {
+		pr_debug("%s: Start to register MVM callback\n", __func__);
+
+		common.apr_q6_mvm = apr_register("ADSP", "MVM",
+						 qdsp_mvm_callback,
+						 0xFFFFFFFF, &common);
+
+		if (common.apr_q6_mvm == NULL) {
+			pr_err("%s: Unable to register MVM\n", __func__);
+			goto err;
+		}
+	}
+
+	if (common.apr_q6_cvs == NULL) {
+		pr_debug("%s: Start to register CVS callback\n", __func__);
+
+		common.apr_q6_cvs = apr_register("ADSP", "CVS",
+						 qdsp_cvs_callback,
+						 0xFFFFFFFF, &common);
+
+		if (common.apr_q6_cvs == NULL) {
+			pr_err("%s: Unable to register CVS\n", __func__);
+			goto err;
+		}
+		rtac_set_voice_handle(RTAC_CVS, common.apr_q6_cvs);
+	}
+
+	if (common.apr_q6_cvp == NULL) {
+		pr_debug("%s: Start to register CVP callback\n", __func__);
+
+		common.apr_q6_cvp = apr_register("ADSP", "CVP",
+						 qdsp_cvp_callback,
+						 0xFFFFFFFF, &common);
+
+		if (common.apr_q6_cvp == NULL) {
+			pr_err("%s: Unable to register CVP\n", __func__);
+			goto err;
+		}
+		rtac_set_voice_handle(RTAC_CVP, common.apr_q6_cvp);
+	}
+
+	mutex_unlock(&common.common_lock);
+
+	return 0;
+
+err:
+	if (common.apr_q6_cvs != NULL) {
+		apr_deregister(common.apr_q6_cvs);
+		common.apr_q6_cvs = NULL;
+		rtac_set_voice_handle(RTAC_CVS, NULL);
+	}
+	if (common.apr_q6_mvm != NULL) {
+		apr_deregister(common.apr_q6_mvm);
+		common.apr_q6_mvm = NULL;
+	}
+
+	mutex_unlock(&common.common_lock);
+
+	return -ENODEV;
+}
+
+static int voice_send_mvm_cvd_version_cmd(struct voice_data *v)
+{
+	int ret;
+	struct apr_hdr cvd_version_get_cmd;
+	void *apr_mvm;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	apr_mvm = common.apr_q6_mvm;
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Send command to CVD to retrieve Version */
+	cvd_version_get_cmd.hdr_field = APR_HDR_FIELD(
+				APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE),
+				APR_PKT_VER);
+	cvd_version_get_cmd.pkt_size = APR_PKT_SIZE(
+				APR_HDR_SIZE,
+				sizeof(cvd_version_get_cmd) -
+				APR_HDR_SIZE);
+	cvd_version_get_cmd.src_port =
+		voice_get_idx_for_session(v->session_id);
+	cvd_version_get_cmd.dest_port = 0;
+	cvd_version_get_cmd.token = 0;
+	cvd_version_get_cmd.opcode = VSS_IVERSION_CMD_GET;
+
+	pr_debug("%s: send CVD version get cmd, pkt size = %d\n",
+		 __func__, cvd_version_get_cmd.pkt_size);
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm,
+			   (uint32_t *) &cvd_version_get_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error sending command\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+			(v->mvm_state == CMD_STATUS_SUCCESS),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout, fall back to default\n",
+		       __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+			v->async_err);
+		goto done;
+	}
+	ret = 0;
+
+done:
+	if (ret) {
+		strlcpy(common.cvd_version, CVD_VERSION_0_0,
+				sizeof(common.cvd_version));
+	}
+	pr_debug("%s: CVD Version retrieved=%s\n",
+		 __func__, common.cvd_version);
+
+	return ret;
+}
+
+static int voice_send_dual_control_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	struct mvm_modem_dual_control_session_cmd mvm_voice_ctl_cmd;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: Send Dual Control command to MVM\n", __func__);
+	if (!is_voip_session(v->session_id)) {
+		mvm_handle = voice_get_mvm_handle(v);
+		mvm_voice_ctl_cmd.hdr.hdr_field = APR_HDR_FIELD(
+						APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+		mvm_voice_ctl_cmd.hdr.pkt_size = APR_PKT_SIZE(
+						APR_HDR_SIZE,
+						sizeof(mvm_voice_ctl_cmd) -
+						APR_HDR_SIZE);
+		pr_debug("%s: send mvm Voice Ctl pkt size = %d\n",
+			__func__, mvm_voice_ctl_cmd.hdr.pkt_size);
+		mvm_voice_ctl_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		mvm_voice_ctl_cmd.hdr.dest_port = mvm_handle;
+		mvm_voice_ctl_cmd.hdr.token = 0;
+		mvm_voice_ctl_cmd.hdr.opcode =
+					VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL;
+		mvm_voice_ctl_cmd.voice_ctl.enable_flag = true;
+		v->mvm_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_voice_ctl_cmd);
+		if (ret < 0) {
+			pr_err("%s: Error sending MVM Voice CTL CMD\n",
+							__func__);
+			ret = -EINVAL;
+			goto fail;
+		}
+		ret = wait_event_timeout(v->mvm_wait,
+				(v->mvm_state == CMD_STATUS_SUCCESS),
+				msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait_event timeout\n", __func__);
+			ret = -EINVAL;
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+	}
+	ret = 0;
+fail:
+	return ret;
+}
+
+
+static int voice_create_mvm_cvs_session(struct voice_data *v)
+{
+	int ret = 0;
+	struct mvm_create_ctl_session_cmd mvm_session_cmd;
+	struct cvs_create_passive_ctl_session_cmd cvs_session_cmd;
+	struct cvs_create_full_ctl_session_cmd cvs_full_ctl_cmd;
+	struct mvm_attach_stream_cmd attach_stream_cmd;
+	void *apr_mvm, *apr_cvs, *apr_cvp;
+	u16 mvm_handle, cvs_handle, cvp_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+	apr_cvs = common.apr_q6_cvs;
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_mvm || !apr_cvs || !apr_cvp) {
+		pr_err("%s: apr_mvm or apr_cvs or apr_cvp is NULL\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+	cvs_handle = voice_get_cvs_handle(v);
+	cvp_handle = voice_get_cvp_handle(v);
+
+	pr_debug("%s: mvm_hdl=%d, cvs_hdl=%d\n", __func__,
+		mvm_handle, cvs_handle);
+	/* send cmd to create mvm session and wait for response */
+
+	if (!mvm_handle) {
+		memset(mvm_session_cmd.mvm_session.name, 0,
+			sizeof(mvm_session_cmd.mvm_session.name));
+		if (!is_voip_session(v->session_id)) {
+			mvm_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
+						APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+			mvm_session_cmd.hdr.pkt_size = APR_PKT_SIZE(
+						APR_HDR_SIZE,
+						sizeof(mvm_session_cmd) -
+						APR_HDR_SIZE);
+			pr_debug("%s: send mvm create session pkt size = %d\n",
+				 __func__, mvm_session_cmd.hdr.pkt_size);
+			mvm_session_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+			mvm_session_cmd.hdr.dest_port = 0;
+			mvm_session_cmd.hdr.token = 0;
+			mvm_session_cmd.hdr.opcode =
+				VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION;
+			if (is_volte_session(v->session_id)) {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				"default volte voice",
+				strlen("default volte voice")+1);
+			} else if (is_voice2_session(v->session_id)) {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				VOICE2_SESSION_VSID_STR,
+				strlen(VOICE2_SESSION_VSID_STR)+1);
+			} else if (is_qchat_session(v->session_id)) {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				QCHAT_SESSION_VSID_STR,
+				strlen(QCHAT_SESSION_VSID_STR)+1);
+			} else if (is_vowlan_session(v->session_id)) {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				VOWLAN_SESSION_VSID_STR,
+				strlen(VOWLAN_SESSION_VSID_STR)+1);
+			} else if (is_voicemmode1(v->session_id)) {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				VOICEMMODE1_VSID_STR,
+				strlen(VOICEMMODE1_VSID_STR) + 1);
+			} else if (is_voicemmode2(v->session_id)) {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				VOICEMMODE2_VSID_STR,
+				strlen(VOICEMMODE2_VSID_STR) + 1);
+			} else {
+				strlcpy(mvm_session_cmd.mvm_session.name,
+				"default modem voice",
+				strlen("default modem voice")+1);
+			}
+
+			v->mvm_state = CMD_STATUS_FAIL;
+			v->async_err = 0;
+
+			ret = apr_send_pkt(apr_mvm,
+					(uint32_t *) &mvm_session_cmd);
+			if (ret < 0) {
+				pr_err("%s: Error sending MVM_CONTROL_SESSION\n",
+				       __func__);
+				goto fail;
+			}
+			ret = wait_event_timeout(v->mvm_wait,
+					(v->mvm_state == CMD_STATUS_SUCCESS),
+					msecs_to_jiffies(TIMEOUT_MS));
+			if (!ret) {
+				pr_err("%s: wait_event timeout\n", __func__);
+				goto fail;
+			}
+			if (v->async_err > 0) {
+				pr_err("%s: DSP returned error[%s]\n",
+					__func__, adsp_err_get_err_str(
+					v->async_err));
+				ret = adsp_err_get_lnx_err_code(
+						v->async_err);
+				goto fail;
+			}
+		} else {
+			pr_debug("%s: creating MVM full ctrl\n", __func__);
+			mvm_session_cmd.hdr.hdr_field =
+					APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+			mvm_session_cmd.hdr.pkt_size =
+					APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(mvm_session_cmd) -
+					APR_HDR_SIZE);
+			mvm_session_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+			mvm_session_cmd.hdr.dest_port = 0;
+			mvm_session_cmd.hdr.token = 0;
+			mvm_session_cmd.hdr.opcode =
+				VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION;
+			strlcpy(mvm_session_cmd.mvm_session.name,
+				"default voip",
+				strlen("default voip")+1);
+
+			v->mvm_state = CMD_STATUS_FAIL;
+			v->async_err = 0;
+
+			ret = apr_send_pkt(apr_mvm,
+					(uint32_t *) &mvm_session_cmd);
+			if (ret < 0) {
+				pr_err("Fail in sending MVM_CONTROL_SESSION\n");
+				goto fail;
+			}
+			ret = wait_event_timeout(v->mvm_wait,
+					 (v->mvm_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+			if (!ret) {
+				pr_err("%s: wait_event timeout\n", __func__);
+				goto fail;
+			}
+			if (v->async_err > 0) {
+				pr_err("%s: DSP returned error[%s]\n",
+					__func__, adsp_err_get_err_str(
+					v->async_err));
+				ret = adsp_err_get_lnx_err_code(
+						v->async_err);
+				goto fail;
+			}
+		}
+		/* Get the created MVM handle. */
+		mvm_handle = voice_get_mvm_handle(v);
+	}
+	/* send cmd to create cvs session */
+	if (!cvs_handle) {
+		memset(cvs_session_cmd.cvs_session.name, 0,
+			sizeof(cvs_session_cmd.cvs_session.name));
+		if (!is_voip_session(v->session_id)) {
+			pr_debug("%s: creating CVS passive session\n",
+				 __func__);
+
+			cvs_session_cmd.hdr.hdr_field = APR_HDR_FIELD(
+						APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+			cvs_session_cmd.hdr.pkt_size =
+						APR_PKT_SIZE(APR_HDR_SIZE,
+						sizeof(cvs_session_cmd) -
+						APR_HDR_SIZE);
+			cvs_session_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+			cvs_session_cmd.hdr.dest_port = 0;
+			cvs_session_cmd.hdr.token = 0;
+			cvs_session_cmd.hdr.opcode =
+				VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION;
+			if (is_volte_session(v->session_id)) {
+				strlcpy(cvs_session_cmd.cvs_session.name,
+				"default volte voice",
+				strlen("default volte voice")+1);
+			} else if (is_voice2_session(v->session_id)) {
+				strlcpy(cvs_session_cmd.cvs_session.name,
+				VOICE2_SESSION_VSID_STR,
+				strlen(VOICE2_SESSION_VSID_STR)+1);
+			} else if (is_qchat_session(v->session_id)) {
+				strlcpy(cvs_session_cmd.cvs_session.name,
+				QCHAT_SESSION_VSID_STR,
+				strlen(QCHAT_SESSION_VSID_STR)+1);
+			} else if (is_vowlan_session(v->session_id)) {
+				strlcpy(cvs_session_cmd.cvs_session.name,
+				VOWLAN_SESSION_VSID_STR,
+				strlen(VOWLAN_SESSION_VSID_STR)+1);
+			} else if (is_voicemmode1(v->session_id)) {
+				strlcpy(cvs_session_cmd.cvs_session.name,
+				VOICEMMODE1_VSID_STR,
+				strlen(VOICEMMODE1_VSID_STR) + 1);
+			} else if (is_voicemmode2(v->session_id)) {
+				strlcpy(cvs_session_cmd.cvs_session.name,
+				VOICEMMODE2_VSID_STR,
+				strlen(VOICEMMODE2_VSID_STR) + 1);
+			} else {
+			strlcpy(cvs_session_cmd.cvs_session.name,
+				"default modem voice",
+				strlen("default modem voice")+1);
+			}
+			v->cvs_state = CMD_STATUS_FAIL;
+			v->async_err = 0;
+
+			ret = apr_send_pkt(apr_cvs,
+					(uint32_t *) &cvs_session_cmd);
+			if (ret < 0) {
+				pr_err("Fail in sending STREAM_CONTROL_SESSION\n");
+				goto fail;
+			}
+			ret = wait_event_timeout(v->cvs_wait,
+					 (v->cvs_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+			if (!ret) {
+				pr_err("%s: wait_event timeout\n", __func__);
+				goto fail;
+			}
+			if (v->async_err > 0) {
+				pr_err("%s: DSP returned error[%s]\n",
+					__func__, adsp_err_get_err_str(
+					v->async_err));
+				ret = adsp_err_get_lnx_err_code(
+						v->async_err);
+				goto fail;
+			}
+			/* Get the created CVS handle. */
+			cvs_handle = voice_get_cvs_handle(v);
+
+		} else {
+			pr_debug("%s: creating CVS full session\n", __func__);
+
+			cvs_full_ctl_cmd.hdr.hdr_field =
+					APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+
+			cvs_full_ctl_cmd.hdr.pkt_size =
+					APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(cvs_full_ctl_cmd) -
+					APR_HDR_SIZE);
+
+			cvs_full_ctl_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+			cvs_full_ctl_cmd.hdr.dest_port = 0;
+			cvs_full_ctl_cmd.hdr.token = 0;
+			cvs_full_ctl_cmd.hdr.opcode =
+				VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION;
+			cvs_full_ctl_cmd.cvs_session.direction = 2;
+			cvs_full_ctl_cmd.cvs_session.enc_media_type =
+						common.mvs_info.media_type;
+			cvs_full_ctl_cmd.cvs_session.dec_media_type =
+						common.mvs_info.media_type;
+			cvs_full_ctl_cmd.cvs_session.network_id =
+					       common.mvs_info.network_type;
+			strlcpy(cvs_full_ctl_cmd.cvs_session.name,
+				"default q6 voice",
+				strlen("default q6 voice")+1);
+
+			v->cvs_state = CMD_STATUS_FAIL;
+			v->async_err = 0;
+
+			ret = apr_send_pkt(apr_cvs,
+					   (uint32_t *) &cvs_full_ctl_cmd);
+
+			if (ret < 0) {
+				pr_err("%s: Err %d sending CREATE_FULL_CTRL\n",
+					__func__, ret);
+				goto fail;
+			}
+			ret = wait_event_timeout(v->cvs_wait,
+					(v->cvs_state == CMD_STATUS_SUCCESS),
+					msecs_to_jiffies(TIMEOUT_MS));
+			if (!ret) {
+				pr_err("%s: wait_event timeout\n", __func__);
+				goto fail;
+			}
+			if (v->async_err > 0) {
+				pr_err("%s: DSP returned error[%s]\n",
+					__func__, adsp_err_get_err_str(
+					v->async_err));
+				ret = adsp_err_get_lnx_err_code(
+						v->async_err);
+				goto fail;
+			}
+			/* Get the created CVS handle. */
+			cvs_handle = voice_get_cvs_handle(v);
+
+			/* Attach MVM to CVS. */
+			pr_debug("%s: Attach MVM to stream\n", __func__);
+
+			attach_stream_cmd.hdr.hdr_field =
+					APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+			attach_stream_cmd.hdr.pkt_size =
+					APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(attach_stream_cmd) -
+					APR_HDR_SIZE);
+			attach_stream_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+			attach_stream_cmd.hdr.dest_port = mvm_handle;
+			attach_stream_cmd.hdr.token = 0;
+			attach_stream_cmd.hdr.opcode =
+						VSS_IMVM_CMD_ATTACH_STREAM;
+			attach_stream_cmd.attach_stream.handle = cvs_handle;
+
+			v->mvm_state = CMD_STATUS_FAIL;
+			v->async_err = 0;
+			ret = apr_send_pkt(apr_mvm,
+					   (uint32_t *) &attach_stream_cmd);
+			if (ret < 0) {
+				pr_err("%s: Error %d sending ATTACH_STREAM\n",
+				       __func__, ret);
+				goto fail;
+			}
+			ret = wait_event_timeout(v->mvm_wait,
+					 (v->mvm_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+			if (!ret) {
+				pr_err("%s: wait_event timeout\n", __func__);
+				goto fail;
+			}
+			if (v->async_err > 0) {
+				pr_err("%s: DSP returned error[%s]\n",
+					__func__, adsp_err_get_err_str(
+					v->async_err));
+				ret = adsp_err_get_lnx_err_code(
+						v->async_err);
+				goto fail;
+			}
+		}
+	}
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_unmap_cal_block(struct voice_data *v, int cal_index)
+{
+	int result = 0;
+	struct cal_block_data *cal_block;
+
+	if (common.cal_data[cal_index] == NULL) {
+		pr_err("%s: Cal type is NULL, index %d!\n",
+			__func__, cal_index);
+
+		goto done;
+	}
+
+	mutex_lock(&common.cal_data[cal_index]->lock);
+	cal_block = cal_utils_get_only_cal_block(
+		common.cal_data[cal_index]);
+	if (cal_block == NULL) {
+		pr_err("%s: Cal block is NULL, index %d!\n",
+			__func__, cal_index);
+
+		result = -EINVAL;
+		goto unlock;
+	}
+
+	if (cal_block->map_data.q6map_handle == 0) {
+		pr_debug("%s: Q6 handle is not set!\n", __func__);
+
+		result = -EINVAL;
+		goto unlock;
+	}
+
+	mutex_lock(&common.common_lock);
+	result = voice_send_mvm_unmap_memory_physical_cmd(
+		v, cal_block->map_data.q6map_handle);
+	if (result)
+		pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n",
+			__func__, v->session_id, result);
+
+	cal_block->map_data.q6map_handle = 0;
+	mutex_unlock(&common.common_lock);
+unlock:
+	mutex_unlock(&common.cal_data[cal_index]->lock);
+done:
+	return result;
+}
+
+static int voice_destroy_mvm_cvs_session(struct voice_data *v)
+{
+	int ret = 0;
+	struct mvm_detach_stream_cmd detach_stream;
+	struct apr_hdr mvm_destroy;
+	struct apr_hdr cvs_destroy;
+	void *apr_mvm, *apr_cvs;
+	u16 mvm_handle, cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_mvm || !apr_cvs) {
+		pr_err("%s: apr_mvm or apr_cvs is NULL\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+	cvs_handle = voice_get_cvs_handle(v);
+
+	/* MVM, CVS sessions are destroyed only for Full control sessions. */
+	if (is_voip_session(v->session_id)) {
+		pr_debug("%s: MVM detach stream, VOC_STATE: %d\n", __func__,
+				v->voc_state);
+
+		/* Detach voice stream. */
+		detach_stream.hdr.hdr_field =
+					APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+		detach_stream.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(detach_stream) - APR_HDR_SIZE);
+		detach_stream.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		detach_stream.hdr.dest_port = mvm_handle;
+		detach_stream.hdr.token = 0;
+		detach_stream.hdr.opcode = VSS_IMVM_CMD_DETACH_STREAM;
+		detach_stream.detach_stream.handle = cvs_handle;
+
+		v->mvm_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+		ret = apr_send_pkt(apr_mvm, (uint32_t *) &detach_stream);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending DETACH_STREAM\n",
+			       __func__, ret);
+
+			goto fail;
+		}
+		ret = wait_event_timeout(v->mvm_wait,
+					 (v->mvm_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+
+		/* Unmap memory */
+		if (v->shmem_info.mem_handle != 0) {
+			ret = voice_send_mvm_unmap_memory_physical_cmd(v,
+						v->shmem_info.mem_handle);
+			if (ret < 0) {
+				pr_err("%s Memory_unmap for voip failed %d\n",
+				       __func__, ret);
+
+				goto fail;
+			}
+			v->shmem_info.mem_handle = 0;
+		}
+	}
+
+	/* Unmap Source Tracking shared memory if mapped earlier */
+	voice_unmap_and_free_source_tracking_shared_memory(v);
+
+	if (is_voip_session(v->session_id) ||
+	    is_qchat_session(v->session_id) ||
+	    is_volte_session(v->session_id) ||
+	    is_vowlan_session(v->session_id) ||
+	    v->voc_state == VOC_ERROR || common.is_destroy_cvd) {
+		/* Destroy CVS. */
+		pr_debug("%s: CVS destroy session\n", __func__);
+
+		cvs_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						      APR_HDR_LEN(APR_HDR_SIZE),
+						      APR_PKT_VER);
+		cvs_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(cvs_destroy) - APR_HDR_SIZE);
+		cvs_destroy.src_port =
+				voice_get_idx_for_session(v->session_id);
+		cvs_destroy.dest_port = cvs_handle;
+		cvs_destroy.token = 0;
+		cvs_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+		v->cvs_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+		ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_destroy);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending CVS DESTROY\n",
+			       __func__, ret);
+
+			goto fail;
+		}
+		ret = wait_event_timeout(v->cvs_wait,
+					 (v->cvs_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+		cvs_handle = 0;
+		voice_set_cvs_handle(v, cvs_handle);
+
+		/* Unmap physical memory for all calibration buffers */
+		if (!is_other_session_active(v->session_id)) {
+			if (voice_unmap_cal_block(v, CVP_VOCPROC_CAL))
+				pr_err("%s: Unmap VOCPROC cal failed\n",
+					__func__);
+			if (voice_unmap_cal_block(v, CVP_VOCVOL_CAL))
+				pr_err("%s: Unmap VOCVOL cal failed\n",
+					__func__);
+			if (voice_unmap_cal_block(v, CVP_VOCDEV_CFG_CAL))
+				pr_err("%s: Unmap VOCDEV_CFG cal failed\n",
+					__func__);
+			if (voice_unmap_cal_block(v, CVS_VOCSTRM_CAL))
+				pr_err("%s: Unmap VOCSTRM cal failed\n",
+					__func__);
+		}
+
+		/* Destroy MVM. */
+		pr_debug("%s: MVM destroy session\n", __func__);
+
+		mvm_destroy.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						      APR_HDR_LEN(APR_HDR_SIZE),
+						      APR_PKT_VER);
+		mvm_destroy.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					    sizeof(mvm_destroy) - APR_HDR_SIZE);
+		mvm_destroy.src_port =
+				voice_get_idx_for_session(v->session_id);
+		mvm_destroy.dest_port = mvm_handle;
+		mvm_destroy.token = 0;
+		mvm_destroy.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+		v->mvm_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_destroy);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending MVM DESTROY\n",
+			       __func__, ret);
+
+			goto fail;
+		}
+		ret = wait_event_timeout(v->mvm_wait,
+					 (v->mvm_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait event timeout\n", __func__);
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+		mvm_handle = 0;
+		voice_set_mvm_handle(v, mvm_handle);
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_tty_mode_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	struct mvm_set_tty_mode_cmd mvm_tty_mode_cmd;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	/* send tty mode cmd to mvm */
+	mvm_tty_mode_cmd.hdr.hdr_field = APR_HDR_FIELD(
+					APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+	mvm_tty_mode_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(mvm_tty_mode_cmd) -
+					APR_HDR_SIZE);
+	pr_debug("%s: pkt size = %d\n",
+		 __func__, mvm_tty_mode_cmd.hdr.pkt_size);
+	mvm_tty_mode_cmd.hdr.src_port =
+			voice_get_idx_for_session(v->session_id);
+	mvm_tty_mode_cmd.hdr.dest_port = mvm_handle;
+	mvm_tty_mode_cmd.hdr.token = 0;
+	mvm_tty_mode_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_TTY_MODE;
+	mvm_tty_mode_cmd.tty_mode.mode = v->tty_mode;
+	pr_debug("tty mode =%d\n", mvm_tty_mode_cmd.tty_mode.mode);
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_tty_mode_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_TTY_MODE\n",
+		       __func__, ret);
+		goto fail;
+	}
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_set_pp_enable_cmd(struct voice_data *v,
+					uint32_t module_id, int enable)
+{
+	struct cvs_set_pp_enable_cmd cvs_set_pp_cmd;
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvs_handle = voice_get_cvs_handle(v);
+
+	cvs_set_pp_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						     APR_HDR_LEN(APR_HDR_SIZE),
+						     APR_PKT_VER);
+	cvs_set_pp_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+						   sizeof(cvs_set_pp_cmd) -
+						   APR_HDR_SIZE);
+	cvs_set_pp_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+	cvs_set_pp_cmd.hdr.dest_port = cvs_handle;
+	cvs_set_pp_cmd.hdr.token = 0;
+	cvs_set_pp_cmd.hdr.opcode = VSS_ICOMMON_CMD_SET_UI_PROPERTY;
+
+	cvs_set_pp_cmd.vss_set_pp.module_id = module_id;
+	cvs_set_pp_cmd.vss_set_pp.param_id = VOICE_PARAM_MOD_ENABLE;
+	cvs_set_pp_cmd.vss_set_pp.param_size = MOD_ENABLE_PARAM_LEN;
+	cvs_set_pp_cmd.vss_set_pp.reserved = 0;
+	cvs_set_pp_cmd.vss_set_pp.enable = enable;
+	cvs_set_pp_cmd.vss_set_pp.reserved_field = 0;
+	pr_debug("voice_send_set_pp_enable_cmd, module_id=%d, enable=%d\n",
+		module_id, enable);
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_pp_cmd);
+	if (ret < 0) {
+		pr_err("Fail: sending cvs set pp enable,\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_hd_cmd(struct voice_data *v, int enable)
+{
+	struct mvm_set_hd_enable_cmd mvm_set_hd_cmd;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	apr_mvm = common.apr_q6_mvm;
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mvm_handle = voice_get_mvm_handle(v);
+	if (!mvm_handle) {
+		pr_err("%s: mvm_handle is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mvm_set_hd_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						     APR_HDR_LEN(APR_HDR_SIZE),
+						     APR_PKT_VER);
+	mvm_set_hd_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+						   sizeof(mvm_set_hd_cmd) -
+						   APR_HDR_SIZE);
+	mvm_set_hd_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+	mvm_set_hd_cmd.hdr.dest_port = mvm_handle;
+	mvm_set_hd_cmd.hdr.token = 0;
+
+	if (enable)
+		mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_ENABLE;
+	else
+		mvm_set_hd_cmd.hdr.opcode = VSS_IHDVOICE_CMD_DISABLE;
+
+	pr_debug("%s: enable=%d\n", __func__, enable);
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_hd_cmd);
+	if (ret < 0) {
+		pr_err("%s: Failed to sending mvm set HD Voice enable %d\n",
+		       __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_set_dtx(struct voice_data *v)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+	struct cvs_set_enc_dtx_mode_cmd cvs_set_dtx;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	/* Set DTX */
+	cvs_set_dtx.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					      APR_HDR_LEN(APR_HDR_SIZE),
+					      APR_PKT_VER);
+	cvs_set_dtx.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(cvs_set_dtx) - APR_HDR_SIZE);
+	cvs_set_dtx.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvs_set_dtx.hdr.dest_port = cvs_handle;
+	cvs_set_dtx.hdr.token = 0;
+	cvs_set_dtx.hdr.opcode = VSS_ISTREAM_CMD_SET_ENC_DTX_MODE;
+	cvs_set_dtx.dtx_mode.enable = common.mvs_info.dtx_mode;
+
+	pr_debug("%s: Setting DTX %d\n", __func__, common.mvs_info.dtx_mode);
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_dtx);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_DTX\n", __func__, ret);
+		return -EINVAL;
+	}
+
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		return -EINVAL;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int voice_send_mvm_media_type_cmd(struct voice_data *v)
+{
+	struct vss_imvm_cmd_set_cal_media_type_t mvm_set_cal_media_type;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	mvm_set_cal_media_type.hdr.hdr_field =
+					APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+	mvm_set_cal_media_type.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(mvm_set_cal_media_type) -
+					APR_HDR_SIZE);
+	mvm_set_cal_media_type.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_set_cal_media_type.hdr.dest_port = mvm_handle;
+	mvm_set_cal_media_type.hdr.token = 0;
+	mvm_set_cal_media_type.hdr.opcode = VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE;
+	mvm_set_cal_media_type.media_id = common.mvs_info.media_type;
+	pr_debug("%s: setting media_id as %x\n",
+		 __func__, mvm_set_cal_media_type.media_id);
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_media_type);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending media type\n", __func__, ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				(v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_dtmf_rx_detection_cmd(struct voice_data *v,
+					    uint32_t enable)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+	struct cvs_set_rx_dtmf_detection_cmd cvs_dtmf_rx_detection;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	/* Set SET_DTMF_RX_DETECTION */
+	cvs_dtmf_rx_detection.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					      APR_HDR_LEN(APR_HDR_SIZE),
+					      APR_PKT_VER);
+	cvs_dtmf_rx_detection.hdr.pkt_size =
+				APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_dtmf_rx_detection) - APR_HDR_SIZE);
+	cvs_dtmf_rx_detection.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvs_dtmf_rx_detection.hdr.dest_port = cvs_handle;
+	cvs_dtmf_rx_detection.hdr.token = 0;
+	cvs_dtmf_rx_detection.hdr.opcode =
+					VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION;
+	cvs_dtmf_rx_detection.cvs_dtmf_det.enable = enable;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_dtmf_rx_detection);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_DTMF_RX_DETECTION\n",
+		       __func__,
+		       ret);
+		return -EINVAL;
+	}
+
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		return -EINVAL;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		return ret;
+	}
+
+	return ret;
+}
+
+void voc_disable_dtmf_det_on_active_sessions(void)
+{
+	struct voice_data *v = NULL;
+	int i;
+
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		v = &common.voice[i];
+		if ((v->dtmf_rx_detect_en) &&
+			is_voc_state_active(v->voc_state)) {
+
+			pr_debug("disable dtmf det on ses_id=%d\n",
+				 v->session_id);
+			voice_send_dtmf_rx_detection_cmd(v, 0);
+		}
+	}
+}
+
+int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+	v->dtmf_rx_detect_en = enable;
+
+	if (is_voc_state_active(v->voc_state))
+		ret = voice_send_dtmf_rx_detection_cmd(v,
+						       v->dtmf_rx_detect_en);
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+void voc_set_destroy_cvd_flag(bool is_destroy_cvd)
+{
+	pr_debug("%s: %d\n", __func__, is_destroy_cvd);
+	common.is_destroy_cvd = is_destroy_cvd;
+}
+
+int voc_alloc_cal_shared_memory(void)
+{
+	int rc = 0;
+
+	mutex_lock(&common.common_lock);
+	if (is_cal_memory_allocated()) {
+		pr_debug("%s: Calibration shared buffer already allocated",
+			 __func__);
+	} else {
+		/* Allocate memory for calibration memory map table. */
+		rc = voice_alloc_cal_mem_map_table();
+		if ((rc < 0) && (rc != -EPROBE_DEFER)) {
+			pr_err("%s: Failed to allocate cal memory, err=%d",
+			       __func__, rc);
+		}
+	}
+	mutex_unlock(&common.common_lock);
+
+	return rc;
+}
+
+int voc_alloc_voip_shared_memory(void)
+{
+	int rc = 0;
+
+	/* Allocate shared memory for OOB Voip */
+	rc = voice_alloc_oob_shared_mem();
+	if (rc < 0) {
+		pr_err("%s: Failed to alloc shared memory for OOB rc:%d\n",
+			   __func__, rc);
+	} else {
+		/* Allocate mem map table for OOB */
+		rc = voice_alloc_oob_mem_table();
+		if (rc < 0) {
+			pr_err("%s: Failed to alloc mem map talbe rc:%d\n",
+			       __func__, rc);
+
+			voice_free_oob_shared_mem();
+		}
+	}
+
+	return rc;
+}
+
+static int is_cal_memory_allocated(void)
+{
+	bool ret;
+
+	if (common.cal_mem_map_table.client != NULL &&
+	    common.cal_mem_map_table.handle != NULL)
+		ret = true;
+	else
+		ret = false;
+
+	return ret;
+}
+
+
+static int free_cal_map_table(void)
+{
+	int ret = 0;
+
+	if ((common.cal_mem_map_table.client == NULL) ||
+		(common.cal_mem_map_table.handle == NULL))
+		goto done;
+
+	ret = msm_audio_ion_free(common.cal_mem_map_table.client,
+		common.cal_mem_map_table.handle);
+	if (ret < 0)
+		pr_err("%s: msm_audio_ion_free failed:\n", __func__);
+
+done:
+	common.cal_mem_map_table.client = NULL;
+	common.cal_mem_map_table.handle = NULL;
+	return ret;
+}
+
+static int is_rtac_memory_allocated(void)
+{
+	bool ret;
+
+	if (common.rtac_mem_map_table.client != NULL &&
+	    common.rtac_mem_map_table.handle != NULL)
+		ret = true;
+	else
+		ret = false;
+
+	return ret;
+}
+
+static int free_rtac_map_table(void)
+{
+	int ret = 0;
+
+	if ((common.rtac_mem_map_table.client == NULL) ||
+		(common.rtac_mem_map_table.handle == NULL))
+		goto done;
+
+	ret = msm_audio_ion_free(common.rtac_mem_map_table.client,
+		common.rtac_mem_map_table.handle);
+	if (ret < 0)
+		pr_err("%s: msm_audio_ion_free failed:\n", __func__);
+
+done:
+	common.rtac_mem_map_table.client = NULL;
+	common.rtac_mem_map_table.handle = NULL;
+	return ret;
+}
+
+
+static int is_voip_memory_allocated(void)
+{
+	bool ret;
+	struct voice_data *v = voice_get_session(
+				common.voice[VOC_PATH_FULL].session_id);
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL, session_id:%d\n", __func__,
+		common.voice[VOC_PATH_FULL].session_id);
+
+		ret = false;
+		goto done;
+	}
+
+	mutex_lock(&common.common_lock);
+	if (v->shmem_info.sh_buf.client != NULL &&
+	    v->shmem_info.sh_buf.handle != NULL)
+		ret = true;
+	else
+		ret = false;
+	mutex_unlock(&common.common_lock);
+
+done:
+	return ret;
+}
+
+static int voice_config_cvs_vocoder_amr_rate(struct voice_data *v)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+	struct cvs_set_amr_enc_rate_cmd cvs_set_amr_rate;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	pr_debug("%s: Setting AMR rate. Media Type: %d\n", __func__,
+		 common.mvs_info.media_type);
+
+	cvs_set_amr_rate.hdr.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE),
+			APR_PKT_VER);
+	cvs_set_amr_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+			       sizeof(cvs_set_amr_rate) - APR_HDR_SIZE);
+	cvs_set_amr_rate.hdr.src_port =
+			voice_get_idx_for_session(v->session_id);
+	cvs_set_amr_rate.hdr.dest_port = cvs_handle;
+	cvs_set_amr_rate.hdr.token = 0;
+
+	if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_NB_MODEM)
+		cvs_set_amr_rate.hdr.opcode =
+				VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE;
+	else if (common.mvs_info.media_type == VSS_MEDIA_ID_AMR_WB_MODEM)
+		cvs_set_amr_rate.hdr.opcode =
+				VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE;
+
+	cvs_set_amr_rate.amr_rate.mode = common.mvs_info.rate;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_amr_rate);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_AMR_RATE\n",
+		       __func__, ret);
+
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+	return 0;
+done:
+	return ret;
+}
+
+static int voice_config_cvs_vocoder(struct voice_data *v)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+	/* Set media type. */
+	struct cvs_set_media_type_cmd cvs_set_media_cmd;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	cvs_set_media_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvs_set_media_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+						sizeof(cvs_set_media_cmd) -
+						APR_HDR_SIZE);
+	cvs_set_media_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvs_set_media_cmd.hdr.dest_port = cvs_handle;
+	cvs_set_media_cmd.hdr.token = 0;
+	cvs_set_media_cmd.hdr.opcode = VSS_ISTREAM_CMD_SET_MEDIA_TYPE;
+	cvs_set_media_cmd.media_type.tx_media_id = common.mvs_info.media_type;
+	cvs_set_media_cmd.media_type.rx_media_id = common.mvs_info.media_type;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_media_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_MEDIA_TYPE\n",
+			__func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	/* Set encoder properties. */
+	switch (common.mvs_info.media_type) {
+	case VSS_MEDIA_ID_EVRC_MODEM:
+	case VSS_MEDIA_ID_4GV_NB_MODEM:
+	case VSS_MEDIA_ID_4GV_WB_MODEM:
+	case VSS_MEDIA_ID_4GV_NW_MODEM: {
+		struct cvs_set_cdma_enc_minmax_rate_cmd cvs_set_cdma_rate;
+
+		pr_debug("Setting EVRC min-max rate\n");
+
+		cvs_set_cdma_rate.hdr.hdr_field =
+					APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+		cvs_set_cdma_rate.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				      sizeof(cvs_set_cdma_rate) - APR_HDR_SIZE);
+		cvs_set_cdma_rate.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		cvs_set_cdma_rate.hdr.dest_port = cvs_handle;
+		cvs_set_cdma_rate.hdr.token = 0;
+		cvs_set_cdma_rate.hdr.opcode =
+				VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE;
+		cvs_set_cdma_rate.cdma_rate.min_rate =
+				common.mvs_info.evrc_min_rate;
+		cvs_set_cdma_rate.cdma_rate.max_rate =
+				common.mvs_info.evrc_max_rate;
+
+		v->cvs_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_set_cdma_rate);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending SET_EVRC_MINMAX_RATE\n",
+			       __func__, ret);
+			goto fail;
+		}
+		ret = wait_event_timeout(v->cvs_wait,
+					 (v->cvs_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait_event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+
+		if (common.mvs_info.media_type != VSS_MEDIA_ID_EVRC_MODEM) {
+			ret = voice_set_dtx(v);
+			if (ret < 0)
+				goto fail;
+		}
+
+		break;
+	}
+	case VSS_MEDIA_ID_AMR_NB_MODEM:
+	case VSS_MEDIA_ID_AMR_WB_MODEM: {
+		ret = voice_config_cvs_vocoder_amr_rate(v);
+		if (ret) {
+			pr_err("%s: Failed to update vocoder rate. %d\n",
+			       __func__, ret);
+
+			goto fail;
+		}
+
+		ret = voice_set_dtx(v);
+		if (ret < 0)
+			goto fail;
+
+		break;
+	}
+	case VSS_MEDIA_ID_G729:
+	case VSS_MEDIA_ID_G711_ALAW:
+	case VSS_MEDIA_ID_G711_MULAW: {
+		ret = voice_set_dtx(v);
+
+		break;
+	}
+	default:
+		/* Do nothing. */
+		break;
+	}
+	return 0;
+
+fail:
+	return ret;
+}
+
+int voc_update_amr_vocoder_rate(uint32_t session_id)
+{
+	int ret = 0;
+	struct voice_data *v;
+
+	pr_debug("%s: session_id:%d", __func__, session_id);
+
+	v = voice_get_session(session_id);
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL, session_id:%d\n", __func__,
+		       session_id);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&v->lock);
+	ret = voice_config_cvs_vocoder_amr_rate(v);
+	mutex_unlock(&v->lock);
+
+done:
+	return ret;
+}
+
+static int voice_send_start_voice_cmd(struct voice_data *v)
+{
+	struct apr_hdr mvm_start_voice_cmd;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	mvm_start_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_start_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_start_voice_cmd) - APR_HDR_SIZE);
+	pr_debug("send mvm_start_voice_cmd pkt size = %d\n",
+				mvm_start_voice_cmd.pkt_size);
+	mvm_start_voice_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_start_voice_cmd.dest_port = mvm_handle;
+	mvm_start_voice_cmd.token = 0;
+	mvm_start_voice_cmd.opcode = VSS_IMVM_CMD_START_VOICE;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_start_voice_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IMVM_CMD_START_VOICE\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static void voc_get_tx_rx_topology(struct voice_data *v,
+				   uint32_t *tx_topology_id,
+				   uint32_t *rx_topology_id)
+{
+	uint32_t tx_id = 0;
+	uint32_t rx_id = 0;
+
+	if (v->lch_mode == VOICE_LCH_START || v->disable_topology) {
+		pr_debug("%s: Setting TX and RX topology to NONE for LCH\n",
+			 __func__);
+
+		tx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE;
+		rx_id = VSS_IVOCPROC_TOPOLOGY_ID_NONE;
+	} else {
+		tx_id = voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL);
+		rx_id = voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL);
+	}
+
+	*tx_topology_id = tx_id;
+	*rx_topology_id = rx_id;
+}
+
+static int voice_send_set_device_cmd(struct voice_data *v)
+{
+	struct cvp_set_device_cmd  cvp_setdev_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* set device and wait for response */
+	cvp_setdev_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_setdev_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_setdev_cmd) - APR_HDR_SIZE);
+	pr_debug(" send create cvp setdev, pkt size = %d\n",
+			cvp_setdev_cmd.hdr.pkt_size);
+	cvp_setdev_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_setdev_cmd.hdr.dest_port = cvp_handle;
+	cvp_setdev_cmd.hdr.token = 0;
+
+	if (voice_get_cvd_int_version(common.cvd_version) >=
+	    CVD_INT_VERSION_2_2)
+		cvp_setdev_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_SET_DEVICE_V3;
+	else
+		cvp_setdev_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_SET_DEVICE_V2;
+
+	voc_get_tx_rx_topology(v,
+			&cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id,
+			&cvp_setdev_cmd.cvp_set_device_v2.rx_topology_id);
+
+	cvp_setdev_cmd.cvp_set_device_v2.tx_port_id = v->dev_tx.port_id;
+	cvp_setdev_cmd.cvp_set_device_v2.rx_port_id = v->dev_rx.port_id;
+
+	if (common.ec_ref_ext) {
+		cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode =
+				VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING;
+		cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id =
+				common.ec_port_id;
+	} else {
+		cvp_setdev_cmd.cvp_set_device_v2.vocproc_mode =
+				    VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING;
+		cvp_setdev_cmd.cvp_set_device_v2.ec_ref_port_id =
+				    VSS_IVOCPROC_PORT_ID_NONE;
+	}
+	pr_debug("topology=%d , tx_port_id=%d, rx_port_id=%d\n",
+		cvp_setdev_cmd.cvp_set_device_v2.tx_topology_id,
+		cvp_setdev_cmd.cvp_set_device_v2.tx_port_id,
+		cvp_setdev_cmd.cvp_set_device_v2.rx_port_id);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_setdev_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IVOCPROC_CMD_SET_DEVICE\n");
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->cvp_wait,
+			(v->cvp_state == CMD_STATUS_SUCCESS),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_stop_voice_cmd(struct voice_data *v)
+{
+	struct apr_hdr mvm_stop_voice_cmd;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	mvm_stop_voice_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_stop_voice_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_stop_voice_cmd) - APR_HDR_SIZE);
+	pr_debug("send mvm_stop_voice_cmd pkt size = %d\n",
+				mvm_stop_voice_cmd.pkt_size);
+	mvm_stop_voice_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_stop_voice_cmd.dest_port = mvm_handle;
+	mvm_stop_voice_cmd.token = 0;
+	mvm_stop_voice_cmd.opcode = VSS_IMVM_CMD_STOP_VOICE;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_stop_voice_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IMVM_CMD_STOP_VOICE\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+static int voice_get_cal(struct cal_block_data **cal_block,
+			 int cal_block_idx,
+			 struct cal_block_data **col_data,
+			 int col_data_idx, int session_id)
+{
+	int ret = 0;
+
+	*cal_block = cal_utils_get_only_cal_block(
+		common.cal_data[cal_block_idx]);
+	if (*cal_block == NULL) {
+		pr_err("%s: No cal data for cal %d!\n",
+			__func__, cal_block_idx);
+
+		ret = -ENODEV;
+		goto done;
+	}
+	ret = remap_cal_data(*cal_block, session_id);
+	if (ret < 0) {
+		pr_err("%s: Remap_cal_data failed for cal %d!\n",
+			__func__, cal_block_idx);
+
+		ret = -ENODEV;
+		goto done;
+	}
+
+	if (col_data == NULL)
+		goto done;
+
+	*col_data = cal_utils_get_only_cal_block(
+		common.cal_data[col_data_idx]);
+	if (*col_data == NULL) {
+		pr_err("%s: No cal data for cal %d!\n",
+			__func__, col_data_idx);
+
+		ret = -ENODEV;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int voice_send_cvs_register_cal_cmd(struct voice_data *v)
+{
+	struct cvs_register_cal_data_cmd cvs_reg_cal_cmd;
+	struct cal_block_data *cal_block = NULL;
+	struct cal_block_data *col_data = NULL;
+	int ret = 0;
+
+	memset(&cvs_reg_cal_cmd, 0, sizeof(cvs_reg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvs) {
+		pr_err("%s: apr_cvs is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&common.cal_data[CVS_VOCSTRM_CAL]->lock);
+	mutex_lock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock);
+
+	ret = voice_get_cal(&cal_block, CVS_VOCSTRM_CAL, &col_data,
+		CVS_VOCSTRM_COL_CAL, v->session_id);
+	if (ret < 0) {
+		pr_err("%s: Voice_get_cal failed for cal %d!\n",
+			__func__, CVS_VOCSTRM_CAL);
+
+		goto unlock;
+	}
+
+	memcpy(&cvs_reg_cal_cmd.cvs_cal_data.column_info[0],
+	       (void *) &((struct audio_cal_info_voc_col *)
+	       col_data->cal_info)->data,
+	       col_data->cal_data.size);
+
+	cvs_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvs_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_reg_cal_cmd) - APR_HDR_SIZE);
+	cvs_reg_cal_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvs_reg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+	cvs_reg_cal_cmd.hdr.token = 0;
+	if (common.is_per_vocoder_cal_enabled)
+		cvs_reg_cal_cmd.hdr.opcode =
+			VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA;
+	else
+		cvs_reg_cal_cmd.hdr.opcode =
+			VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_handle =
+		cal_block->map_data.q6map_handle;
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_address_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	cvs_reg_cal_cmd.cvs_cal_data.cal_mem_size =
+		cal_block->cal_data.size;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_reg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVS cal\n", __func__, ret);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto unlock;
+	}
+unlock:
+	mutex_unlock(&common.cal_data[CVS_VOCSTRM_COL_CAL]->lock);
+	mutex_unlock(&common.cal_data[CVS_VOCSTRM_CAL]->lock);
+done:
+	return ret;
+}
+
+static int voice_send_cvs_deregister_cal_cmd(struct voice_data *v)
+{
+	struct cvs_deregister_cal_data_cmd cvs_dereg_cal_cmd;
+	int ret = 0;
+
+	memset(&cvs_dereg_cal_cmd, 0, sizeof(cvs_dereg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvs) {
+		pr_err("%s: apr_cvs is NULL\n", __func__);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	cvs_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvs_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_dereg_cal_cmd) - APR_HDR_SIZE);
+	cvs_dereg_cal_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvs_dereg_cal_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+	cvs_dereg_cal_cmd.hdr.token = 0;
+	if (common.is_per_vocoder_cal_enabled)
+		cvs_dereg_cal_cmd.hdr.opcode =
+			VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA;
+	else
+		cvs_dereg_cal_cmd.hdr.opcode =
+			VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_dereg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVS cal\n", __func__, ret);
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command  timeout\n", __func__);
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+
+}
+
+static int voice_send_cvp_create_cmd(struct voice_data *v)
+{
+	struct cvp_create_full_ctl_session_cmd cvp_session_cmd;
+	void *apr_cvp;
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	apr_cvp = common.apr_q6_cvp;
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* create cvp session and wait for response */
+	cvp_session_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_session_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_session_cmd) - APR_HDR_SIZE);
+	pr_debug("%s: send create cvp session, pkt size = %d\n",
+		 __func__, cvp_session_cmd.hdr.pkt_size);
+	cvp_session_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_session_cmd.hdr.dest_port = 0;
+	cvp_session_cmd.hdr.token = 0;
+
+	if (voice_get_cvd_int_version(common.cvd_version) >=
+	    CVD_INT_VERSION_2_2)
+		cvp_session_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3;
+	else
+		cvp_session_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2;
+
+	voc_get_tx_rx_topology(v,
+			&cvp_session_cmd.cvp_session.tx_topology_id,
+			&cvp_session_cmd.cvp_session.rx_topology_id);
+
+	cvp_session_cmd.cvp_session.direction = 2; /*tx and rx*/
+	cvp_session_cmd.cvp_session.tx_port_id = v->dev_tx.port_id;
+	cvp_session_cmd.cvp_session.rx_port_id = v->dev_rx.port_id;
+	cvp_session_cmd.cvp_session.profile_id =
+					 VSS_ICOMMON_CAL_NETWORK_ID_NONE;
+	if (common.ec_ref_ext) {
+		cvp_session_cmd.cvp_session.vocproc_mode =
+				VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING;
+		cvp_session_cmd.cvp_session.ec_ref_port_id =
+					common.ec_port_id;
+	} else {
+		cvp_session_cmd.cvp_session.vocproc_mode =
+				 VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING;
+		cvp_session_cmd.cvp_session.ec_ref_port_id =
+						 VSS_IVOCPROC_PORT_ID_NONE;
+	}
+
+	pr_debug("tx_topology: %d tx_port_id=%d, rx_port_id=%d, mode: 0x%x\n",
+		cvp_session_cmd.cvp_session.tx_topology_id,
+		cvp_session_cmd.cvp_session.tx_port_id,
+		cvp_session_cmd.cvp_session.rx_port_id,
+		cvp_session_cmd.cvp_session.vocproc_mode);
+	pr_debug("rx_topology: %d, profile_id: 0x%x, pkt_size: %d\n",
+		cvp_session_cmd.cvp_session.rx_topology_id,
+		cvp_session_cmd.cvp_session.profile_id,
+		cvp_session_cmd.hdr.pkt_size);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_session_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VOCPROC_FULL_CONTROL_SESSION\n");
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_send_cvp_register_dev_cfg_cmd(struct voice_data *v)
+{
+	struct cvp_register_dev_cfg_cmd cvp_reg_dev_cfg_cmd;
+	struct cal_block_data *cal_block = NULL;
+	int ret = 0;
+
+	memset(&cvp_reg_dev_cfg_cmd, 0, sizeof(cvp_reg_dev_cfg_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	mutex_lock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock);
+
+	ret = voice_get_cal(&cal_block, CVP_VOCDEV_CFG_CAL, NULL,
+		0, v->session_id);
+	if (ret < 0) {
+		pr_err("%s: Voice_get_cal failed for cal %d!\n",
+			__func__, CVP_VOCDEV_CFG_CAL);
+
+		goto unlock;
+	}
+
+	cvp_reg_dev_cfg_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_reg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_reg_dev_cfg_cmd) - APR_HDR_SIZE);
+	cvp_reg_dev_cfg_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_reg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_reg_dev_cfg_cmd.hdr.token = 0;
+	cvp_reg_dev_cfg_cmd.hdr.opcode =
+					VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG;
+
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_handle =
+		cal_block->map_data.q6map_handle;
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_address_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	cvp_reg_dev_cfg_cmd.cvp_dev_cfg_data.mem_size =
+		cal_block->cal_data.size;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_reg_dev_cfg_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVP dev cfg cal\n",
+		       __func__, ret);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto unlock;
+	}
+unlock:
+	mutex_unlock(&common.cal_data[CVP_VOCDEV_CFG_CAL]->lock);
+done:
+	return ret;
+}
+
+static int voice_send_cvp_deregister_dev_cfg_cmd(struct voice_data *v)
+{
+	struct cvp_deregister_dev_cfg_cmd cvp_dereg_dev_cfg_cmd;
+	int ret = 0;
+
+	memset(&cvp_dereg_dev_cfg_cmd, 0, sizeof(cvp_dereg_dev_cfg_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	cvp_dereg_dev_cfg_cmd.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_dereg_dev_cfg_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_dereg_dev_cfg_cmd) - APR_HDR_SIZE);
+	cvp_dereg_dev_cfg_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_dereg_dev_cfg_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_dereg_dev_cfg_cmd.hdr.token = 0;
+	cvp_dereg_dev_cfg_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_dereg_dev_cfg_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVP dev cfg cal\n",
+		       __func__, ret);
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_send_cvp_register_cal_cmd(struct voice_data *v)
+{
+	struct cvp_register_cal_data_cmd cvp_reg_cal_cmd;
+	struct cal_block_data *cal_block = NULL;
+	struct cal_block_data *col_data = NULL;
+	int ret = 0;
+
+	memset(&cvp_reg_cal_cmd, 0, sizeof(cvp_reg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&common.cal_data[CVP_VOCPROC_CAL]->lock);
+	mutex_lock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock);
+
+	ret = voice_get_cal(&cal_block, CVP_VOCPROC_CAL, &col_data,
+		CVP_VOCPROC_COL_CAL, v->session_id);
+	if (ret < 0) {
+		pr_err("%s: Voice_get_cal failed for cal %d!\n",
+			__func__, CVP_VOCPROC_CAL);
+
+		goto unlock;
+	}
+
+	v->dev_tx.dev_id = ((struct audio_cal_info_vocproc *)
+				cal_block->cal_info)->tx_acdb_id;
+	v->dev_rx.dev_id = ((struct audio_cal_info_vocproc *)
+				cal_block->cal_info)->rx_acdb_id;
+	pr_debug("%s: %s: Tx acdb id = %d and Rx acdb id = %d", __func__,
+		 voc_get_session_name(v->session_id), v->dev_tx.dev_id,
+		 v->dev_rx.dev_id);
+
+	memcpy(&cvp_reg_cal_cmd.cvp_cal_data.column_info[0],
+	       (void *) &((struct audio_cal_info_voc_col *)
+	       col_data->cal_info)->data,
+	       col_data->cal_data.size);
+
+	cvp_reg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_reg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_reg_cal_cmd) - APR_HDR_SIZE);
+	cvp_reg_cal_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_reg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_reg_cal_cmd.hdr.token = 0;
+	if (common.is_per_vocoder_cal_enabled)
+		cvp_reg_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA;
+	else
+		cvp_reg_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2;
+
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_handle =
+		cal_block->map_data.q6map_handle;
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_address_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	cvp_reg_cal_cmd.cvp_cal_data.cal_mem_size =
+		cal_block->cal_data.size;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_reg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVP cal\n", __func__, ret);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto unlock;
+	}
+unlock:
+	mutex_unlock(&common.cal_data[CVP_VOCPROC_COL_CAL]->lock);
+	mutex_unlock(&common.cal_data[CVP_VOCPROC_CAL]->lock);
+done:
+	return ret;
+}
+
+static int voice_send_cvp_deregister_cal_cmd(struct voice_data *v)
+{
+	struct cvp_deregister_cal_data_cmd cvp_dereg_cal_cmd;
+	int ret = 0;
+
+	memset(&cvp_dereg_cal_cmd, 0, sizeof(cvp_dereg_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	cvp_dereg_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_dereg_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_dereg_cal_cmd) - APR_HDR_SIZE);
+	cvp_dereg_cal_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_dereg_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_dereg_cal_cmd.hdr.token = 0;
+	if (common.is_per_vocoder_cal_enabled)
+		cvp_dereg_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA;
+	else
+		cvp_dereg_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_dereg_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVP cal\n", __func__, ret);
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_send_cvp_register_vol_cal_cmd(struct voice_data *v)
+{
+	struct cvp_register_vol_cal_data_cmd cvp_reg_vol_cal_cmd;
+	struct cal_block_data *cal_block = NULL;
+	struct cal_block_data *col_data = NULL;
+	int ret = 0;
+
+	memset(&cvp_reg_vol_cal_cmd, 0, sizeof(cvp_reg_vol_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&common.cal_data[CVP_VOCVOL_CAL]->lock);
+	mutex_lock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock);
+
+	ret = voice_get_cal(&cal_block, CVP_VOCVOL_CAL, &col_data,
+		CVP_VOCVOL_COL_CAL, v->session_id);
+	if (ret < 0) {
+		pr_err("%s: Voice_get_cal failed for cal %d!\n",
+			__func__, CVP_VOCVOL_CAL);
+
+		goto unlock;
+	}
+
+	memcpy(&cvp_reg_vol_cal_cmd.cvp_vol_cal_data.column_info[0],
+	       (void *) &((struct audio_cal_info_voc_col *)
+	       col_data->cal_info)->data,
+	       col_data->cal_data.size);
+
+	cvp_reg_vol_cal_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_reg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_reg_vol_cal_cmd) - APR_HDR_SIZE);
+	cvp_reg_vol_cal_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_reg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_reg_vol_cal_cmd.hdr.token = 0;
+	if (common.is_per_vocoder_cal_enabled)
+		cvp_reg_vol_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA;
+	else
+		cvp_reg_vol_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA;
+
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_handle =
+		cal_block->map_data.q6map_handle;
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_lsw =
+		lower_32_bits(cal_block->cal_data.paddr);
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_address_msw =
+		msm_audio_populate_upper_32_bits(cal_block->cal_data.paddr);
+	cvp_reg_vol_cal_cmd.cvp_vol_cal_data.cal_mem_size =
+		cal_block->cal_data.size;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_reg_vol_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d registering CVP vol cal\n", __func__, ret);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto unlock;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto unlock;
+	}
+unlock:
+	mutex_unlock(&common.cal_data[CVP_VOCVOL_COL_CAL]->lock);
+	mutex_unlock(&common.cal_data[CVP_VOCVOL_CAL]->lock);
+done:
+	return ret;
+}
+
+static int voice_send_cvp_deregister_vol_cal_cmd(struct voice_data *v)
+{
+	struct cvp_deregister_vol_cal_data_cmd cvp_dereg_vol_cal_cmd;
+	int ret = 0;
+
+	memset(&cvp_dereg_vol_cal_cmd, 0, sizeof(cvp_dereg_vol_cal_cmd));
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		ret = -EPERM;
+		goto done;
+	}
+
+	cvp_dereg_vol_cal_cmd.hdr.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_dereg_vol_cal_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_dereg_vol_cal_cmd) - APR_HDR_SIZE);
+	cvp_dereg_vol_cal_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_dereg_vol_cal_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_dereg_vol_cal_cmd.hdr.token = 0;
+	if (common.is_per_vocoder_cal_enabled)
+		cvp_dereg_vol_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA;
+	else
+		cvp_dereg_vol_cal_cmd.hdr.opcode =
+			VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp,
+			   (uint32_t *) &cvp_dereg_vol_cal_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d de-registering CVP vol cal\n",
+		       __func__, ret);
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_map_memory_physical_cmd(struct voice_data *v,
+					 struct mem_map_table *table_info,
+					 dma_addr_t phys,
+					 uint32_t size,
+					 uint32_t token)
+{
+	struct vss_imemory_cmd_map_physical_t mvm_map_phys_cmd;
+	uint32_t *memtable;
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!common.apr_q6_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!table_info->data) {
+		pr_err("%s: memory table is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	memtable = (uint32_t *) table_info->data;
+
+	/*
+	 * Store next table descriptor's address(64 bit) as NULL as there
+	 * is only one memory block
+	 */
+	memtable[0] = 0;
+	memtable[1] = 0;
+
+	/* Store next table descriptor's size */
+	memtable[2] = 0;
+
+	/* Store shared mem adddress (64 bit) */
+	memtable[3] = lower_32_bits(phys);
+	memtable[4] = msm_audio_populate_upper_32_bits(phys);
+
+	/* Store shared memory size */
+	memtable[5] = size;
+
+	mvm_map_phys_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mvm_map_phys_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_map_phys_cmd) - APR_HDR_SIZE);
+	mvm_map_phys_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_map_phys_cmd.hdr.dest_port = voice_get_mvm_handle(v);
+	mvm_map_phys_cmd.hdr.token = token;
+	mvm_map_phys_cmd.hdr.opcode = VSS_IMEMORY_CMD_MAP_PHYSICAL;
+
+	mvm_map_phys_cmd.table_descriptor.mem_address_lsw =
+			lower_32_bits(table_info->phys);
+	mvm_map_phys_cmd.table_descriptor.mem_address_msw =
+			msm_audio_populate_upper_32_bits(table_info->phys);
+	mvm_map_phys_cmd.table_descriptor.mem_size =
+			sizeof(struct vss_imemory_block_t) +
+			sizeof(struct vss_imemory_table_descriptor_t);
+	mvm_map_phys_cmd.is_cached = true;
+	mvm_map_phys_cmd.cache_line_size = 128;
+	mvm_map_phys_cmd.access_mask = 3;
+	mvm_map_phys_cmd.page_align = 4096;
+	mvm_map_phys_cmd.min_data_width = 8;
+	mvm_map_phys_cmd.max_data_width = 64;
+
+	pr_debug("%s: next table desc: add: %lld, size: %d\n",
+		 __func__, *((uint64_t *) memtable),
+		 *(((uint32_t *) memtable) + 2));
+	pr_debug("%s: phy add of of mem being mapped LSW:0x%x, MSW:0x%x size: %d\n",
+		 __func__, *(((uint32_t *) memtable) + 3),
+		*(((uint32_t *) memtable) + 4), *(((uint32_t *) memtable) + 5));
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_mvm, (uint32_t *) &mvm_map_phys_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending mvm map phy cmd\n", __func__, ret);
+
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_pause_voice_call(struct voice_data *v)
+{
+	struct apr_hdr	mvm_pause_voice_cmd;
+	void		*apr_mvm;
+	int		ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (v == NULL) {
+		pr_err("%s: Voice data is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	apr_mvm = common.apr_q6_mvm;
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mvm_pause_voice_cmd.hdr_field =
+		APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	mvm_pause_voice_cmd.pkt_size =
+		APR_PKT_SIZE(APR_HDR_SIZE,
+		sizeof(mvm_pause_voice_cmd) - APR_HDR_SIZE);
+	mvm_pause_voice_cmd.src_port =
+			voice_get_idx_for_session(v->session_id);
+	mvm_pause_voice_cmd.dest_port = voice_get_mvm_handle(v);
+	mvm_pause_voice_cmd.token = 0;
+	mvm_pause_voice_cmd.opcode = VSS_IMVM_CMD_PAUSE_VOICE;
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	pr_debug("%s: send mvm_pause_voice_cmd pkt size = %d\n",
+		__func__, mvm_pause_voice_cmd.pkt_size);
+
+	ret = apr_send_pkt(apr_mvm,
+		(uint32_t *)&mvm_pause_voice_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IMVM_CMD_PAUSE_VOICE\n");
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+		(v->mvm_state == CMD_STATUS_SUCCESS),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_map_cal_memory(struct cal_block_data *cal_block,
+				uint32_t session_id)
+{
+	int result = 0;
+	int voc_index;
+	struct voice_data *v = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_block == NULL) {
+		pr_err("%s: Cal block is NULL!\n", __func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->cal_data.paddr == 0) {
+		pr_debug("%s: No address to map!\n", __func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->map_data.map_size == 0) {
+		pr_debug("%s: Map size is 0!\n", __func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	voc_index = voice_get_idx_for_session(session_id);
+	if (voc_index < 0) {
+		pr_err("%s:  Invalid session ID %d\n", __func__, session_id);
+
+		goto done;
+	}
+
+	mutex_lock(&common.common_lock);
+	v = &common.voice[voc_index];
+
+	result = voice_map_memory_physical_cmd(v,
+		&common.cal_mem_map_table,
+		(dma_addr_t)cal_block->cal_data.paddr,
+		cal_block->map_data.map_size,
+		VOC_CAL_MEM_MAP_TOKEN);
+	if (result < 0) {
+		pr_err("%s: Mmap did not work! addr = 0x%pK, size = %zd\n",
+			__func__,
+			&cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+
+		goto done_unlock;
+	}
+
+	cal_block->map_data.q6map_handle = common.cal_mem_handle;
+done_unlock:
+	mutex_unlock(&common.common_lock);
+done:
+	return result;
+}
+
+static int remap_cal_data(struct cal_block_data *cal_block,
+			   uint32_t session_id)
+{
+	int ret = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_block->map_data.ion_client == NULL) {
+		pr_err("%s: No ION allocation for session_id %d!\n",
+			__func__, session_id);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if ((cal_block->map_data.map_size > 0) &&
+		(cal_block->map_data.q6map_handle == 0)) {
+
+		/* cal type not used */
+		ret = voice_map_cal_memory(cal_block, session_id);
+		if (ret < 0) {
+			pr_err("%s: Mmap did not work! size = %zd\n",
+				__func__, cal_block->map_data.map_size);
+
+			goto done;
+		}
+	} else {
+		pr_debug("%s:  Cal block 0x%pK, size %zd already mapped. Q6 map handle = %d\n",
+			__func__, &cal_block->cal_data.paddr,
+			cal_block->map_data.map_size,
+			cal_block->map_data.q6map_handle);
+	}
+done:
+	return ret;
+}
+
+static int voice_unmap_cal_memory(int32_t cal_type,
+				  struct cal_block_data *cal_block)
+{
+	int result = 0;
+	int result2 = 0;
+	int i;
+	struct voice_data *v = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_block == NULL) {
+		pr_err("%s: Cal block is NULL!\n", __func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->map_data.q6map_handle == 0) {
+		pr_debug("%s: Q6 handle is not set!\n", __func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&common.common_lock);
+
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		v = &common.voice[i];
+
+		mutex_lock(&v->lock);
+		if (is_voc_state_active(v->voc_state)) {
+			result2 = voice_pause_voice_call(v);
+			if (result2 < 0) {
+				pr_err("%s: Voice_pause_voice_call failed for session 0x%x, err %d!\n",
+					__func__, v->session_id, result2);
+
+				result = result2;
+			}
+
+			if (cal_type == CVP_VOCPROC_DYNAMIC_CAL_TYPE)
+				voice_send_cvp_deregister_vol_cal_cmd(v);
+			else if (cal_type == CVP_VOCPROC_STATIC_CAL_TYPE)
+				voice_send_cvp_deregister_cal_cmd(v);
+			else if (cal_type == CVP_VOCDEV_CFG_CAL_TYPE)
+				voice_send_cvp_deregister_dev_cfg_cmd(v);
+			else if (cal_type == CVS_VOCSTRM_STATIC_CAL_TYPE)
+				voice_send_cvs_deregister_cal_cmd(v);
+			else
+				pr_err("%s: Invalid cal type %d!\n",
+					__func__, cal_type);
+
+			result2 = voice_send_start_voice_cmd(v);
+			if (result2) {
+				pr_err("%s: Voice_send_start_voice_cmd failed for session 0x%x, err %d!\n",
+					__func__, v->session_id, result2);
+
+				result = result2;
+			}
+		}
+
+		if ((cal_block->map_data.q6map_handle != 0) &&
+			(!is_other_session_active(v->session_id))) {
+
+			result2 = voice_send_mvm_unmap_memory_physical_cmd(
+				v, cal_block->map_data.q6map_handle);
+			if (result2) {
+				pr_err("%s: Voice_send_mvm_unmap_memory_physical_cmd failed for session 0x%x, err %d!\n",
+					__func__, v->session_id, result2);
+
+				result = result2;
+			}
+			cal_block->map_data.q6map_handle = 0;
+		}
+		mutex_unlock(&v->lock);
+	}
+	mutex_unlock(&common.common_lock);
+done:
+	return result;
+}
+
+int voc_register_vocproc_vol_table(void)
+{
+	int			result = 0;
+	int			result2 = 0;
+	int			i;
+	struct voice_data	*v = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&common.common_lock);
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		v = &common.voice[i];
+
+		mutex_lock(&v->lock);
+		if (is_voc_state_active(v->voc_state)) {
+			result2 = voice_send_cvp_register_vol_cal_cmd(v);
+			if (result2 < 0) {
+				pr_err("%s: Failed to register vocvol table for session 0x%x!\n",
+					__func__, v->session_id);
+
+				result = result2;
+				/* Still try to register other sessions */
+			}
+		}
+		mutex_unlock(&v->lock);
+	}
+
+	mutex_unlock(&common.common_lock);
+	return result;
+}
+
+int voc_deregister_vocproc_vol_table(void)
+{
+	int			result = 0;
+	int			success = 0;
+	int			i;
+	struct voice_data	*v = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&common.common_lock);
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+		v = &common.voice[i];
+
+		mutex_lock(&v->lock);
+		if (is_voc_state_active(v->voc_state)) {
+			result = voice_send_cvp_deregister_vol_cal_cmd(v);
+			if (result < 0) {
+				pr_err("%s: Failed to deregister vocvol table for session 0x%x!\n",
+					__func__, v->session_id);
+
+				mutex_unlock(&v->lock);
+				mutex_unlock(&common.common_lock);
+				if (success) {
+					pr_err("%s: Try to re-register all deregistered sessions!\n",
+						__func__);
+
+					voc_register_vocproc_vol_table();
+				}
+				goto done;
+			} else {
+				success = 1;
+			}
+		}
+		mutex_unlock(&v->lock);
+	}
+	mutex_unlock(&common.common_lock);
+done:
+	return result;
+}
+
+int voc_map_rtac_block(struct rtac_cal_block_data *cal_block)
+{
+	int			result = 0;
+	struct voice_data	*v = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_block == NULL) {
+		pr_err("%s: cal_block is NULL!\n",
+			__func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->cal_data.paddr == 0) {
+		pr_debug("%s: No address to map!\n",
+			__func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	if (cal_block->map_data.map_size == 0) {
+		pr_debug("%s: map size is 0!\n",
+			__func__);
+
+		result = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&common.common_lock);
+	/* use first session */
+	v = &common.voice[0];
+	mutex_lock(&v->lock);
+
+	if (!is_rtac_memory_allocated()) {
+		result = voice_alloc_rtac_mem_map_table();
+		if (result < 0) {
+			pr_err("%s: RTAC alloc mem map table did not work! addr = 0x%pK, size = %d\n",
+				__func__,
+				&cal_block->cal_data.paddr,
+				cal_block->map_data.map_size);
+
+			goto done_unlock;
+		}
+	}
+
+	result = voice_map_memory_physical_cmd(v,
+		&common.rtac_mem_map_table,
+		(dma_addr_t)cal_block->cal_data.paddr,
+		cal_block->map_data.map_size,
+		VOC_RTAC_MEM_MAP_TOKEN);
+	if (result < 0) {
+		pr_err("%s: RTAC mmap did not work! addr = 0x%pK, size = %d\n",
+			__func__,
+			&cal_block->cal_data.paddr,
+			cal_block->map_data.map_size);
+
+		free_rtac_map_table();
+		goto done_unlock;
+	}
+
+	cal_block->map_data.map_handle = common.rtac_mem_handle;
+done_unlock:
+	mutex_unlock(&v->lock);
+	mutex_unlock(&common.common_lock);
+done:
+	return result;
+}
+
+int voc_unmap_rtac_block(uint32_t *mem_map_handle)
+{
+	int			result = 0;
+	struct voice_data	*v = NULL;
+
+	pr_debug("%s\n", __func__);
+
+	if (mem_map_handle == NULL) {
+		pr_debug("%s: Map handle is NULL, nothing to unmap\n",
+			__func__);
+
+		goto done;
+	}
+
+	if (*mem_map_handle == 0) {
+		pr_debug("%s: Map handle is 0, nothing to unmap\n",
+			__func__);
+
+		goto done;
+	}
+
+	mutex_lock(&common.common_lock);
+	/* Use first session */
+	/* Only used for apr wait lock */
+	v = &common.voice[0];
+	mutex_lock(&v->lock);
+
+	result = voice_send_mvm_unmap_memory_physical_cmd(
+			v, *mem_map_handle);
+	if (result) {
+		pr_err("%s: voice_send_mvm_unmap_memory_physical_cmd Failed for session 0x%x!\n",
+			__func__, v->session_id);
+	} else {
+		*mem_map_handle = 0;
+		common.rtac_mem_handle = 0;
+		free_rtac_map_table();
+	}
+	mutex_unlock(&v->lock);
+	mutex_unlock(&common.common_lock);
+done:
+	return result;
+}
+
+static int voice_setup_vocproc(struct voice_data *v)
+{
+	int ret = 0;
+
+	ret = voice_send_cvp_create_cmd(v);
+	if (ret < 0) {
+		pr_err("%s: CVP create failed err:%d\n", __func__, ret);
+		goto fail;
+	}
+
+	ret = voice_send_cvp_device_channels_cmd(v);
+	if (ret < 0) {
+		pr_err("%s: Set device channels failed err:%d\n",
+		       __func__, ret);
+		goto fail;
+	}
+
+	ret = voice_send_cvp_topology_commit_cmd(v);
+	if (ret < 0) {
+		pr_err("%s: Set topology commit failed err:%d\n",
+		       __func__, ret);
+		goto fail;
+	}
+
+	voice_send_cvs_register_cal_cmd(v);
+	voice_send_cvp_register_dev_cfg_cmd(v);
+	voice_send_cvp_register_cal_cmd(v);
+	voice_send_cvp_register_vol_cal_cmd(v);
+
+	/* enable vocproc */
+	ret = voice_send_enable_vocproc_cmd(v);
+	if (ret < 0)
+		goto fail;
+
+	/* attach vocproc */
+	ret = voice_send_attach_vocproc_cmd(v);
+	if (ret < 0)
+		goto fail;
+
+	/* send tty mode if tty device is used */
+	voice_send_tty_mode_cmd(v);
+
+	if (is_voip_session(v->session_id)) {
+		ret = voice_send_mvm_cal_network_cmd(v);
+		if (ret < 0)
+			pr_err("%s: voice_send_mvm_cal_network_cmd: %d\n",
+				__func__, ret);
+
+		ret = voice_send_mvm_media_type_cmd(v);
+		if (ret < 0)
+			pr_err("%s: voice_send_mvm_media_type_cmd: %d\n",
+				__func__, ret);
+
+		voice_send_netid_timing_cmd(v);
+	}
+
+	if (v->st_enable && !v->tty_mode)
+		voice_send_set_pp_enable_cmd(v,
+					     MODULE_ID_VOICE_MODULE_ST,
+					     v->st_enable);
+	/* Start in-call music delivery if this feature is enabled */
+	if (v->music_info.play_enable)
+		voice_cvs_start_playback(v);
+
+	/* Start in-call recording if this feature is enabled */
+	if (v->rec_info.rec_enable)
+		voice_cvs_start_record(v, v->rec_info.rec_mode);
+
+	if (v->dtmf_rx_detect_en)
+		voice_send_dtmf_rx_detection_cmd(v, v->dtmf_rx_detect_en);
+
+	if (v->hd_enable)
+		voice_send_hd_cmd(v, v->hd_enable);
+
+	rtac_add_voice(voice_get_cvs_handle(v),
+		voice_get_cvp_handle(v),
+		v->dev_rx.port_id, v->dev_tx.port_id,
+		v->dev_rx.dev_id, v->dev_tx.dev_id,
+		v->session_id);
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_send_cvp_device_channels_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	struct  cvp_set_dev_channels_cmd cvp_set_dev_channels_cmd;
+	void *apr_cvp;
+	u16 cvp_handle;
+
+	if (!(voice_get_cvd_int_version(common.cvd_version) >=
+	      CVD_INT_VERSION_2_2)) {
+		pr_debug("%s CVD ver %s doesn't support send_device_channels cmd\n",
+			 __func__, common.cvd_version);
+
+		goto done;
+	}
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	apr_cvp = common.apr_q6_cvp;
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cvp_handle = voice_get_cvp_handle(v);
+	cvp_set_dev_channels_cmd.hdr.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE),
+			APR_PKT_VER);
+	cvp_set_dev_channels_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+			sizeof(cvp_set_dev_channels_cmd) - APR_HDR_SIZE);
+	cvp_set_dev_channels_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_set_dev_channels_cmd.hdr.dest_port = cvp_handle;
+	cvp_set_dev_channels_cmd.hdr.token = 0;
+	cvp_set_dev_channels_cmd.hdr.opcode =
+				VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS;
+	cvp_set_dev_channels_cmd.cvp_set_channels.rx_num_channels =
+				VSS_NUM_DEV_CHANNELS_1;
+	cvp_set_dev_channels_cmd.cvp_set_channels.tx_num_channels =
+				v->dev_tx.no_of_channels;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_set_dev_channels_cmd);
+	if (ret < 0) {
+		pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS\n",
+		       __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(v->cvp_wait,
+				(v->cvp_state == CMD_STATUS_SUCCESS),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_send_cvp_topology_commit_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	struct apr_hdr cvp_topology_commit_cmd;
+	void *apr_cvp;
+	u16 cvp_handle;
+
+	if (!(voice_get_cvd_int_version(common.cvd_version) >=
+	      CVD_INT_VERSION_2_2)) {
+		pr_debug("%s CVD version string %s doesn't support this command\n",
+			 __func__, common.cvd_version);
+
+		goto done;
+	}
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	apr_cvp = common.apr_q6_cvp;
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cvp_handle = voice_get_cvp_handle(v);
+	cvp_topology_commit_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_topology_commit_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_topology_commit_cmd) - APR_HDR_SIZE);
+	cvp_topology_commit_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_topology_commit_cmd.dest_port = cvp_handle;
+	cvp_topology_commit_cmd.token = 0;
+	cvp_topology_commit_cmd.opcode = VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_topology_commit_cmd);
+	if (ret < 0) {
+		pr_err("%s: Fail in sending VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT\n",
+		       __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = wait_event_timeout(v->cvp_wait,
+				(v->cvp_state == CMD_STATUS_SUCCESS),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+			__func__, adsp_err_get_err_str(
+			v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+static int voice_send_enable_vocproc_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	struct apr_hdr cvp_enable_cmd;
+	void *apr_cvp;
+	u16 cvp_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* enable vocproc and wait for respose */
+	cvp_enable_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_enable_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_enable_cmd) - APR_HDR_SIZE);
+	pr_debug("cvp_enable_cmd pkt size = %d, cvp_handle=%d\n",
+		cvp_enable_cmd.pkt_size, cvp_handle);
+	cvp_enable_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_enable_cmd.dest_port = cvp_handle;
+	cvp_enable_cmd.token = 0;
+	cvp_enable_cmd.opcode = VSS_IVOCPROC_CMD_ENABLE;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_enable_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IVOCPROC_CMD_ENABLE\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				(v->cvp_state == CMD_STATUS_SUCCESS),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_mvm_cal_network_cmd(struct voice_data *v)
+{
+	struct vss_imvm_cmd_set_cal_network_t mvm_set_cal_network;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	mvm_set_cal_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_set_cal_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_set_cal_network) - APR_HDR_SIZE);
+	mvm_set_cal_network.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_set_cal_network.hdr.dest_port = mvm_handle;
+	mvm_set_cal_network.hdr.token = 0;
+	mvm_set_cal_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK;
+	mvm_set_cal_network.network_id = VSS_ICOMMON_CAL_NETWORK_ID_NONE;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_cal_network);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				(v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_netid_timing_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+	struct mvm_set_network_cmd mvm_set_network;
+	struct mvm_set_voice_timing_cmd mvm_set_voice_timing;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	ret = voice_config_cvs_vocoder(v);
+	if (ret < 0) {
+		pr_err("%s: Error %d configuring CVS voc",
+					__func__, ret);
+		goto fail;
+	}
+	/* Set network ID. */
+	pr_debug("Setting network ID %x\n", common.mvs_info.network_type);
+
+	mvm_set_network.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_set_network.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(mvm_set_network) - APR_HDR_SIZE);
+	mvm_set_network.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_set_network.hdr.dest_port = mvm_handle;
+	mvm_set_network.hdr.token = 0;
+	mvm_set_network.hdr.opcode = VSS_IMVM_CMD_SET_CAL_NETWORK;
+	mvm_set_network.network.network_id = common.mvs_info.network_type;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_network);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_NETWORK\n", __func__, ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				(v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	/* Set voice timing. */
+	 pr_debug("Setting voice timing\n");
+
+	mvm_set_voice_timing.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_set_voice_timing.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+						sizeof(mvm_set_voice_timing) -
+						APR_HDR_SIZE);
+	mvm_set_voice_timing.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_set_voice_timing.hdr.dest_port = mvm_handle;
+	mvm_set_voice_timing.hdr.token = 0;
+	mvm_set_voice_timing.hdr.opcode = VSS_ICOMMON_CMD_SET_VOICE_TIMING;
+	mvm_set_voice_timing.timing.mode = 0;
+	mvm_set_voice_timing.timing.enc_offset = 8000;
+	mvm_set_voice_timing.timing.dec_req_offset = 3300;
+	mvm_set_voice_timing.timing.dec_offset = 8300;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_set_voice_timing);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending SET_TIMING\n", __func__, ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				(v->mvm_state == CMD_STATUS_SUCCESS),
+				msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_attach_vocproc_cmd(struct voice_data *v)
+{
+	int ret = 0;
+	struct mvm_attach_vocproc_cmd mvm_a_vocproc_cmd;
+	void *apr_mvm;
+	u16 mvm_handle, cvp_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* attach vocproc and wait for response */
+	mvm_a_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_a_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_a_vocproc_cmd) - APR_HDR_SIZE);
+	pr_debug("send mvm_a_vocproc_cmd pkt size = %d\n",
+		mvm_a_vocproc_cmd.hdr.pkt_size);
+	mvm_a_vocproc_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_a_vocproc_cmd.hdr.dest_port = mvm_handle;
+	mvm_a_vocproc_cmd.hdr.token = 0;
+	mvm_a_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_ATTACH_VOCPROC;
+	mvm_a_vocproc_cmd.mvm_attach_cvp_handle.handle = cvp_handle;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_a_vocproc_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IMVM_CMD_ATTACH_VOCPROC\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+
+static void voc_update_session_params(struct voice_data *v)
+{
+	/* reset LCH mode */
+	v->lch_mode = 0;
+
+	/* clear disable topology setting */
+	v->disable_topology = false;
+
+	/* clear mute setting */
+	v->dev_rx.dev_mute =  common.default_mute_val;
+	v->dev_tx.dev_mute =  common.default_mute_val;
+	v->stream_rx.stream_mute = common.default_mute_val;
+	v->stream_tx.stream_mute = common.default_mute_val;
+}
+
+static int voice_destroy_vocproc(struct voice_data *v)
+{
+	struct mvm_detach_vocproc_cmd mvm_d_vocproc_cmd;
+	struct apr_hdr cvp_destroy_session_cmd;
+	int ret = 0;
+	void *apr_mvm, *apr_cvp;
+	u16 mvm_handle, cvp_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_mvm || !apr_cvp) {
+		pr_err("%s: apr_mvm or apr_cvp is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* disable slowtalk if st_enable is set */
+	if (v->st_enable)
+		voice_send_set_pp_enable_cmd(v, MODULE_ID_VOICE_MODULE_ST, 0);
+
+	/* Disable HD Voice if hd_enable is set */
+	if (v->hd_enable)
+		voice_send_hd_cmd(v, 0);
+
+	/* stop playback or recording */
+	v->music_info.force = 1;
+	voice_cvs_stop_playback(v);
+	voice_cvs_stop_record(v);
+	/* If voice call is active during VoLTE, SRVCC happens.
+	 * Start recording on voice session if recording started during VoLTE.
+	 */
+	if (is_volte_session(v->session_id) &&
+	    ((common.voice[VOC_PATH_PASSIVE].voc_state == VOC_RUN) ||
+	     (common.voice[VOC_PATH_PASSIVE].voc_state == VOC_CHANGE))) {
+		if (v->rec_info.rec_enable) {
+			voice_cvs_start_record(
+				&common.voice[VOC_PATH_PASSIVE],
+				v->rec_info.rec_mode);
+			common.srvcc_rec_flag = true;
+
+			pr_debug("%s: switch recording, srvcc_rec_flag %d\n",
+				 __func__, common.srvcc_rec_flag);
+		}
+	}
+
+	/* send stop voice cmd */
+	voice_send_stop_voice_cmd(v);
+
+	/* send stop dtmf detecton cmd */
+	if (v->dtmf_rx_detect_en)
+		voice_send_dtmf_rx_detection_cmd(v, 0);
+
+	/* detach VOCPROC and wait for response from mvm */
+	mvm_d_vocproc_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mvm_d_vocproc_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mvm_d_vocproc_cmd) - APR_HDR_SIZE);
+	pr_debug("mvm_d_vocproc_cmd  pkt size = %d\n",
+		mvm_d_vocproc_cmd.hdr.pkt_size);
+	mvm_d_vocproc_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mvm_d_vocproc_cmd.hdr.dest_port = mvm_handle;
+	mvm_d_vocproc_cmd.hdr.token = 0;
+	mvm_d_vocproc_cmd.hdr.opcode = VSS_IMVM_CMD_DETACH_VOCPROC;
+	mvm_d_vocproc_cmd.mvm_detach_cvp_handle.handle = cvp_handle;
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mvm_d_vocproc_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending VSS_IMVM_CMD_DETACH_VOCPROC\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	voice_send_cvp_deregister_vol_cal_cmd(v);
+	voice_send_cvp_deregister_cal_cmd(v);
+	voice_send_cvp_deregister_dev_cfg_cmd(v);
+	voice_send_cvs_deregister_cal_cmd(v);
+
+	/* destrop cvp session */
+	cvp_destroy_session_cmd.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_destroy_session_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_destroy_session_cmd) - APR_HDR_SIZE);
+	pr_debug("cvp_destroy_session_cmd pkt size = %d\n",
+		cvp_destroy_session_cmd.pkt_size);
+	cvp_destroy_session_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_destroy_session_cmd.dest_port = cvp_handle;
+	cvp_destroy_session_cmd.token = 0;
+	cvp_destroy_session_cmd.opcode = APRV2_IBASIC_CMD_DESTROY_SESSION;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_destroy_session_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending APRV2_IBASIC_CMD_DESTROY_SESSION\n");
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	rtac_remove_voice(voice_get_cvs_handle(v));
+	cvp_handle = 0;
+	voice_set_cvp_handle(v, cvp_handle);
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_mvm_unmap_memory_physical_cmd(struct voice_data *v,
+						    uint32_t mem_handle)
+{
+	struct vss_imemory_cmd_unmap_t mem_unmap;
+	int ret = 0;
+	void *apr_mvm;
+	u16 mvm_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_mvm = common.apr_q6_mvm;
+
+	if (!apr_mvm) {
+		pr_err("%s: apr_mvm is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	mvm_handle = voice_get_mvm_handle(v);
+
+	mem_unmap.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	mem_unmap.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(mem_unmap) - APR_HDR_SIZE);
+	mem_unmap.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	mem_unmap.hdr.dest_port = mvm_handle;
+	mem_unmap.hdr.token = 0;
+	mem_unmap.hdr.opcode = VSS_IMEMORY_CMD_UNMAP;
+	mem_unmap.mem_handle = mem_handle;
+
+	pr_debug("%s: mem_handle: 0x%x\n", __func__, mem_unmap.mem_handle);
+
+	v->mvm_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_mvm, (uint32_t *) &mem_unmap);
+	if (ret < 0) {
+		pr_err("mem_unmap op[0x%x]ret[%d]\n",
+			mem_unmap.hdr.opcode, ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->mvm_wait,
+				 (v->mvm_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_send_cvs_packet_exchange_config_cmd(struct voice_data *v)
+{
+	struct vss_istream_cmd_set_oob_packet_exchange_config_t
+						 packet_exchange_config_pkt;
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvs_handle = voice_get_cvs_handle(v);
+
+	packet_exchange_config_pkt.hdr.hdr_field = APR_HDR_FIELD(
+						APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	packet_exchange_config_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(packet_exchange_config_pkt) -
+					 APR_HDR_SIZE);
+	packet_exchange_config_pkt.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	packet_exchange_config_pkt.hdr.dest_port = cvs_handle;
+	packet_exchange_config_pkt.hdr.token = 0;
+	packet_exchange_config_pkt.hdr.opcode =
+			 VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG;
+	packet_exchange_config_pkt.mem_handle = v->shmem_info.mem_handle;
+	/* dec buffer address */
+	packet_exchange_config_pkt.dec_buf_addr_lsw =
+		lower_32_bits(v->shmem_info.sh_buf.buf[0].phys);
+	packet_exchange_config_pkt.dec_buf_addr_msw =
+		msm_audio_populate_upper_32_bits(
+					v->shmem_info.sh_buf.buf[0].phys);
+	packet_exchange_config_pkt.dec_buf_size = 4096;
+	/* enc buffer address */
+	packet_exchange_config_pkt.enc_buf_addr_lsw =
+		lower_32_bits(v->shmem_info.sh_buf.buf[1].phys);
+	packet_exchange_config_pkt.enc_buf_addr_msw =
+		msm_audio_populate_upper_32_bits(
+					v->shmem_info.sh_buf.buf[1].phys);
+	packet_exchange_config_pkt.enc_buf_size = 4096;
+
+	pr_debug("%s: dec buf add: lsw %0x msw %0x, size %d, enc buf add: lsw %0x msw %0x, size %d\n",
+		__func__,
+		packet_exchange_config_pkt.dec_buf_addr_lsw,
+		packet_exchange_config_pkt.dec_buf_addr_msw,
+		packet_exchange_config_pkt.dec_buf_size,
+		packet_exchange_config_pkt.enc_buf_addr_lsw,
+		packet_exchange_config_pkt.enc_buf_addr_msw,
+		packet_exchange_config_pkt.enc_buf_size);
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &packet_exchange_config_pkt);
+	if (ret < 0) {
+		pr_err("Failed to send packet exchange config cmd %d\n", ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret)
+		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_cvs_data_exchange_mode_cmd(struct voice_data *v)
+{
+	struct vss_istream_cmd_set_packet_exchange_mode_t data_exchange_pkt;
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvs_handle = voice_get_cvs_handle(v);
+
+	data_exchange_pkt.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	data_exchange_pkt.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(data_exchange_pkt) - APR_HDR_SIZE);
+	data_exchange_pkt.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	data_exchange_pkt.hdr.dest_port = cvs_handle;
+	data_exchange_pkt.hdr.token = 0;
+	data_exchange_pkt.hdr.opcode = VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE;
+	data_exchange_pkt.mode = VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvs, (uint32_t *) &data_exchange_pkt);
+	if (ret < 0) {
+		pr_err("Failed to send data exchange mode %d\n", ret);
+		goto fail;
+	}
+
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret)
+		pr_err("%s: wait_event timeout %d\n", __func__, ret);
+
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+	return 0;
+fail:
+	return ret;
+}
+
+static int voice_send_stream_mute_cmd(struct voice_data *v, uint16_t direction,
+				     uint16_t mute_flag, uint32_t ramp_duration)
+{
+	struct cvs_set_mute_cmd cvs_mute_cmd;
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	/* send mute/unmute to cvs */
+	cvs_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvs_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(cvs_mute_cmd) - APR_HDR_SIZE);
+	cvs_mute_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvs_mute_cmd.hdr.dest_port = voice_get_cvs_handle(v);
+	cvs_mute_cmd.hdr.token = 0;
+	cvs_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+	cvs_mute_cmd.cvs_set_mute.direction = direction;
+	cvs_mute_cmd.cvs_set_mute.mute_flag = mute_flag;
+	cvs_mute_cmd.cvs_set_mute.ramp_duration_ms = ramp_duration;
+
+	v->cvs_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvs, (uint32_t *) &cvs_mute_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending stream mute\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_send_device_mute_cmd(struct voice_data *v, uint16_t direction,
+				     uint16_t mute_flag, uint32_t ramp_duration)
+{
+	struct cvp_set_mute_cmd cvp_mute_cmd;
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	if (!common.apr_q6_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	cvp_mute_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_mute_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(cvp_mute_cmd) - APR_HDR_SIZE);
+	cvp_mute_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_mute_cmd.hdr.dest_port = voice_get_cvp_handle(v);
+	cvp_mute_cmd.hdr.token = 0;
+	cvp_mute_cmd.hdr.opcode = VSS_IVOLUME_CMD_MUTE_V2;
+	cvp_mute_cmd.cvp_set_mute.direction = direction;
+	cvp_mute_cmd.cvp_set_mute.mute_flag = mute_flag;
+	cvp_mute_cmd.cvp_set_mute.ramp_duration_ms = ramp_duration;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(common.apr_q6_cvp, (uint32_t *) &cvp_mute_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error %d sending rx device cmd\n", __func__, ret);
+
+		goto fail;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: Command timeout\n", __func__);
+		goto fail;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_send_vol_step_cmd(struct voice_data *v)
+{
+	struct cvp_set_rx_volume_step_cmd cvp_vol_step_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		return -EINVAL;
+	}
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* send volume index to cvp */
+	cvp_vol_step_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+						APR_HDR_LEN(APR_HDR_SIZE),
+						APR_PKT_VER);
+	cvp_vol_step_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvp_vol_step_cmd) - APR_HDR_SIZE);
+	cvp_vol_step_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_vol_step_cmd.hdr.dest_port = cvp_handle;
+	cvp_vol_step_cmd.hdr.token = 0;
+	cvp_vol_step_cmd.hdr.opcode = VSS_IVOLUME_CMD_SET_STEP;
+	cvp_vol_step_cmd.cvp_set_vol_step.direction = VSS_IVOLUME_DIRECTION_RX;
+	cvp_vol_step_cmd.cvp_set_vol_step.value = v->dev_rx.volume_step_value;
+	cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms =
+					v->dev_rx.volume_ramp_duration_ms;
+	 pr_debug("%s step_value:%d, ramp_duration_ms:%d",
+			__func__,
+			cvp_vol_step_cmd.cvp_set_vol_step.value,
+			cvp_vol_step_cmd.cvp_set_vol_step.ramp_duration_ms);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_vol_step_cmd);
+	if (ret < 0) {
+		pr_err("Fail in sending RX VOL step\n");
+		return -EINVAL;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		return -EINVAL;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		return ret;
+	}
+	return 0;
+}
+
+static int voice_cvs_start_record(struct voice_data *v, uint32_t rec_mode)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+
+	struct cvs_start_record_cmd cvs_start_record;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	if (!v->rec_info.recording) {
+		cvs_start_record.hdr.hdr_field = APR_HDR_FIELD(
+					APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+		cvs_start_record.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				  sizeof(cvs_start_record) - APR_HDR_SIZE);
+		cvs_start_record.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		cvs_start_record.hdr.dest_port = cvs_handle;
+		cvs_start_record.hdr.token = 0;
+		cvs_start_record.hdr.opcode = VSS_IRECORD_CMD_START;
+
+		cvs_start_record.rec_mode.port_id =
+					VSS_IRECORD_PORT_ID_DEFAULT;
+		if (rec_mode == VOC_REC_UPLINK) {
+			cvs_start_record.rec_mode.rx_tap_point =
+					VSS_IRECORD_TAP_POINT_NONE;
+			cvs_start_record.rec_mode.tx_tap_point =
+					VSS_IRECORD_TAP_POINT_STREAM_END;
+		} else if (rec_mode == VOC_REC_DOWNLINK) {
+			cvs_start_record.rec_mode.rx_tap_point =
+					VSS_IRECORD_TAP_POINT_STREAM_END;
+			cvs_start_record.rec_mode.tx_tap_point =
+					VSS_IRECORD_TAP_POINT_NONE;
+		} else if (rec_mode == VOC_REC_BOTH) {
+			cvs_start_record.rec_mode.rx_tap_point =
+					VSS_IRECORD_TAP_POINT_STREAM_END;
+			cvs_start_record.rec_mode.tx_tap_point =
+					VSS_IRECORD_TAP_POINT_STREAM_END;
+		} else {
+			pr_err("%s: Invalid in-call rec_mode %d\n", __func__,
+				rec_mode);
+
+			ret = -EINVAL;
+			goto fail;
+		}
+
+		v->cvs_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_record);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending START_RECORD\n", __func__,
+				ret);
+
+			goto fail;
+		}
+
+		ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+
+		if (!ret) {
+			pr_err("%s: wait_event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+		v->rec_info.recording = 1;
+	} else {
+		pr_debug("%s: Start record already sent\n", __func__);
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_cvs_stop_record(struct voice_data *v)
+{
+	int ret = 0;
+	void *apr_cvs;
+	u16 cvs_handle;
+	struct apr_hdr cvs_stop_record;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	if (v->rec_info.recording) {
+		cvs_stop_record.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				  APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		cvs_stop_record.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				  sizeof(cvs_stop_record) - APR_HDR_SIZE);
+		cvs_stop_record.src_port =
+				voice_get_idx_for_session(v->session_id);
+		cvs_stop_record.dest_port = cvs_handle;
+		cvs_stop_record.token = 0;
+		cvs_stop_record.opcode = VSS_IRECORD_CMD_STOP;
+
+		v->cvs_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_record);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending STOP_RECORD\n",
+				__func__, ret);
+
+			goto fail;
+		}
+
+		ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait_event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+		v->rec_info.recording = 0;
+	} else {
+		pr_debug("%s: Stop record already sent\n", __func__);
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id)
+{
+	int ret = 0;
+	int rec_mode = 0;
+	u16 cvs_handle;
+	int rec_set = 0;
+	struct voice_session_itr itr;
+	struct voice_data *v = NULL;
+
+	/* check if session_id is valid */
+	if (!voice_is_valid_session_id(session_id)) {
+		pr_err("%s: Invalid session id:%u\n", __func__,
+		       session_id);
+
+		return -EINVAL;
+	}
+
+	voice_itr_init(&itr, session_id);
+	pr_debug("%s: session_id:%u\n", __func__, session_id);
+
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v == NULL) {
+			pr_err("%s: v is NULL, sessionid:%u\n", __func__,
+				session_id);
+
+			break;
+		}
+		pr_debug("%s: port_id: %d, set: %d, v: %pK\n",
+			 __func__, port_id, set, v);
+
+		mutex_lock(&v->lock);
+		rec_mode = v->rec_info.rec_mode;
+		rec_set = set;
+		if (set) {
+			if ((v->rec_route_state.ul_flag != 0) &&
+				(v->rec_route_state.dl_flag != 0)) {
+				pr_debug("%s: rec mode already set.\n",
+					__func__);
+
+				mutex_unlock(&v->lock);
+				continue;
+			}
+
+			if (port_id == VOICE_RECORD_TX) {
+				if ((v->rec_route_state.ul_flag == 0)
+				&& (v->rec_route_state.dl_flag == 0)) {
+					rec_mode = VOC_REC_UPLINK;
+					v->rec_route_state.ul_flag = 1;
+				} else if ((v->rec_route_state.ul_flag == 0)
+					&& (v->rec_route_state.dl_flag != 0)) {
+					voice_cvs_stop_record(v);
+					rec_mode = VOC_REC_BOTH;
+					v->rec_route_state.ul_flag = 1;
+				}
+			} else if (port_id == VOICE_RECORD_RX) {
+				if ((v->rec_route_state.ul_flag == 0)
+					&& (v->rec_route_state.dl_flag == 0)) {
+					rec_mode = VOC_REC_DOWNLINK;
+					v->rec_route_state.dl_flag = 1;
+				} else if ((v->rec_route_state.ul_flag != 0)
+					&& (v->rec_route_state.dl_flag == 0)) {
+					voice_cvs_stop_record(v);
+					rec_mode = VOC_REC_BOTH;
+					v->rec_route_state.dl_flag = 1;
+				}
+			}
+			rec_set = 1;
+		} else {
+			if ((v->rec_route_state.ul_flag == 0) &&
+				(v->rec_route_state.dl_flag == 0)) {
+				pr_debug("%s: rec already stops.\n",
+					__func__);
+				mutex_unlock(&v->lock);
+				continue;
+			}
+
+			if (port_id == VOICE_RECORD_TX) {
+				if ((v->rec_route_state.ul_flag != 0)
+					&& (v->rec_route_state.dl_flag == 0)) {
+					v->rec_route_state.ul_flag = 0;
+					rec_set = 0;
+				} else if ((v->rec_route_state.ul_flag != 0)
+					&& (v->rec_route_state.dl_flag != 0)) {
+					voice_cvs_stop_record(v);
+					v->rec_route_state.ul_flag = 0;
+					rec_mode = VOC_REC_DOWNLINK;
+					rec_set = 1;
+				}
+			} else if (port_id == VOICE_RECORD_RX) {
+				if ((v->rec_route_state.ul_flag == 0)
+					&& (v->rec_route_state.dl_flag != 0)) {
+					v->rec_route_state.dl_flag = 0;
+					rec_set = 0;
+				} else if ((v->rec_route_state.ul_flag != 0)
+					&& (v->rec_route_state.dl_flag != 0)) {
+					voice_cvs_stop_record(v);
+					v->rec_route_state.dl_flag = 0;
+					rec_mode = VOC_REC_UPLINK;
+					rec_set = 1;
+				}
+			}
+		}
+		pr_debug("%s: mode =%d, set =%d\n", __func__,
+			 rec_mode, rec_set);
+		cvs_handle = voice_get_cvs_handle(v);
+
+		if (cvs_handle != 0) {
+			if (rec_set)
+				ret = voice_cvs_start_record(v, rec_mode);
+			else
+				ret = voice_cvs_stop_record(v);
+		}
+
+		/* During SRVCC, recording will switch from VoLTE session to
+		 * voice session.
+		 * Then stop recording, need to stop recording on voice session.
+		 */
+		if ((!rec_set) && common.srvcc_rec_flag) {
+			pr_debug("%s, srvcc_rec_flag:%d\n",  __func__,
+				 common.srvcc_rec_flag);
+
+			voice_cvs_stop_record(&common.voice[VOC_PATH_PASSIVE]);
+			common.srvcc_rec_flag = false;
+		}
+
+		/* Cache the value */
+		v->rec_info.rec_enable = rec_set;
+		v->rec_info.rec_mode = rec_mode;
+
+		mutex_unlock(&v->lock);
+	}
+
+	return ret;
+}
+
+static int voice_cvs_start_playback(struct voice_data *v)
+{
+	int ret = 0;
+	struct cvs_start_playback_cmd cvs_start_playback;
+	void *apr_cvs;
+	u16 cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	if (!v->music_info.playing && v->music_info.count) {
+		cvs_start_playback.hdr.hdr_field = APR_HDR_FIELD(
+					APR_MSG_TYPE_SEQ_CMD,
+					APR_HDR_LEN(APR_HDR_SIZE),
+					APR_PKT_VER);
+		cvs_start_playback.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_start_playback) - APR_HDR_SIZE);
+		cvs_start_playback.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		cvs_start_playback.hdr.dest_port = cvs_handle;
+		cvs_start_playback.hdr.token = 0;
+		cvs_start_playback.hdr.opcode = VSS_IPLAYBACK_CMD_START;
+		cvs_start_playback.playback_mode.port_id =
+						v->music_info.port_id;
+
+		v->cvs_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_start_playback);
+
+		if (ret < 0) {
+			pr_err("%s: Error %d sending START_PLAYBACK\n",
+				__func__, ret);
+
+			goto fail;
+		}
+
+		ret = wait_event_timeout(v->cvs_wait,
+				 (v->cvs_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait_event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+
+		v->music_info.playing = 1;
+	} else {
+		pr_debug("%s: Start playback already sent\n", __func__);
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voice_cvs_stop_playback(struct voice_data *v)
+{
+	 int ret = 0;
+	 struct apr_hdr cvs_stop_playback;
+	 void *apr_cvs;
+	 u16 cvs_handle;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvs = common.apr_q6_cvs;
+
+	if (!apr_cvs) {
+		pr_err("%s: apr_cvs is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvs_handle = voice_get_cvs_handle(v);
+
+	if (v->music_info.playing && ((!v->music_info.count) ||
+						(v->music_info.force))) {
+		cvs_stop_playback.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		cvs_stop_playback.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(cvs_stop_playback) - APR_HDR_SIZE);
+		cvs_stop_playback.src_port =
+				voice_get_idx_for_session(v->session_id);
+		cvs_stop_playback.dest_port = cvs_handle;
+		cvs_stop_playback.token = 0;
+
+		cvs_stop_playback.opcode = VSS_IPLAYBACK_CMD_STOP;
+
+		v->cvs_state = CMD_STATUS_FAIL;
+		v->async_err = 0;
+
+		ret = apr_send_pkt(apr_cvs, (uint32_t *) &cvs_stop_playback);
+		if (ret < 0) {
+			pr_err("%s: Error %d sending STOP_PLAYBACK\n",
+			       __func__, ret);
+
+
+			goto fail;
+		}
+
+		ret = wait_event_timeout(v->cvs_wait,
+					 (v->cvs_state == CMD_STATUS_SUCCESS),
+					 msecs_to_jiffies(TIMEOUT_MS));
+		if (!ret) {
+			pr_err("%s: wait_event timeout\n", __func__);
+
+			goto fail;
+		}
+		if (v->async_err > 0) {
+			pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+			ret = adsp_err_get_lnx_err_code(
+					v->async_err);
+			goto fail;
+		}
+
+		v->music_info.playing = 0;
+		v->music_info.force = 0;
+	} else {
+		pr_debug("%s: Stop playback already sent\n", __func__);
+	}
+
+	return 0;
+
+fail:
+	return ret;
+}
+
+static int voc_lch_ops(struct voice_data *v, enum voice_lch_mode lch_mode)
+{
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	switch (lch_mode) {
+	case VOICE_LCH_START:
+
+		ret = voc_end_voice_call(v->session_id);
+		if (ret < 0)
+			pr_err("%s: voice call end failed %d\n",
+				__func__, ret);
+		break;
+	case VOICE_LCH_STOP:
+
+		ret = voc_start_voice_call(v->session_id);
+		if (ret < 0) {
+			pr_err("%s: voice call start failed %d\n",
+				__func__, ret);
+			goto done;
+		}
+		break;
+	default:
+		pr_err("%s: Invalid LCH mode: %d\n",
+			__func__, v->lch_mode);
+		break;
+	}
+done:
+	return ret;
+}
+
+int voc_start_playback(uint32_t set, uint16_t port_id)
+{
+	struct voice_data *v = NULL;
+	int ret = 0;
+	struct voice_session_itr itr;
+	u16 cvs_handle;
+
+	pr_debug("%s port_id = %#x set = %d", __func__, port_id, set);
+
+	voice_itr_init(&itr, ALL_SESSION_VSID);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if ((v != NULL) &&
+		    (((port_id == VOICE_PLAYBACK_TX) &&
+		       is_sub1_vsid(v->session_id)) ||
+		     ((port_id == VOICE2_PLAYBACK_TX) &&
+		       is_sub2_vsid(v->session_id)))) {
+
+			mutex_lock(&v->lock);
+			v->music_info.port_id = port_id;
+			v->music_info.play_enable = set;
+			if (set)
+				v->music_info.count++;
+			else
+				v->music_info.count--;
+			pr_debug("%s: music_info count=%d\n", __func__,
+				 v->music_info.count);
+
+			cvs_handle = voice_get_cvs_handle(v);
+			if (cvs_handle != 0) {
+				if (set)
+					ret = voice_cvs_start_playback(v);
+				else
+					ret = voice_cvs_stop_playback(v);
+			}
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: Invalid session\n", __func__);
+		}
+	}
+
+	return ret;
+}
+
+int voc_disable_topology(uint32_t session_id, uint32_t disable)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+
+	v->disable_topology = disable;
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+static int voice_set_packet_exchange_mode_and_config(uint32_t session_id,
+						 uint32_t mode)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+		return -EINVAL;
+	}
+
+	if (v->voc_state != VOC_RUN)
+		ret = voice_send_cvs_data_exchange_mode_cmd(v);
+
+	if (ret) {
+		pr_err("%s: Error voice_send_data_exchange_mode_cmd %d\n",
+			__func__, ret);
+		goto fail;
+	}
+
+	ret = voice_send_cvs_packet_exchange_config_cmd(v);
+	if (ret) {
+		pr_err("%s: Error: voice_send_packet_exchange_config_cmd %d\n",
+			__func__, ret);
+		goto fail;
+	}
+
+	return ret;
+fail:
+	return -EINVAL;
+}
+
+int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+		    uint32_t ramp_duration)
+{
+	struct voice_data *v = NULL;
+	int ret = 0;
+	struct voice_session_itr itr;
+
+	voice_itr_init(&itr, session_id);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			v->stream_tx.stream_mute = mute;
+			v->stream_tx.stream_mute_ramp_duration_ms =
+								ramp_duration;
+			if (is_voc_state_active(v->voc_state) &&
+				(v->lch_mode == 0))
+				ret = voice_send_stream_mute_cmd(v,
+				VSS_IVOLUME_DIRECTION_TX,
+				v->stream_tx.stream_mute,
+				v->stream_tx.stream_mute_ramp_duration_ms);
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session_id 0x%x\n", __func__,
+				session_id);
+
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+			uint32_t ramp_duration)
+{
+	struct voice_data *v = NULL;
+	int ret = 0;
+	struct voice_session_itr itr;
+
+	voice_itr_init(&itr, session_id);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			if (dir == VSS_IVOLUME_DIRECTION_TX) {
+				v->dev_tx.dev_mute = mute;
+				v->dev_tx.dev_mute_ramp_duration_ms =
+							ramp_duration;
+			} else {
+				v->dev_rx.dev_mute = mute;
+				v->dev_rx.dev_mute_ramp_duration_ms =
+							ramp_duration;
+			}
+
+			if (((v->voc_state == VOC_RUN) ||
+				(v->voc_state == VOC_STANDBY)) &&
+				(v->lch_mode == 0))
+				ret = voice_send_device_mute_cmd(v,
+							dir,
+							mute,
+							ramp_duration);
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session_id 0x%x\n", __func__,
+				session_id);
+
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int voc_get_rx_device_mute(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+
+	ret = v->dev_rx.dev_mute;
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+
+	v->tty_mode = tty_mode;
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+uint8_t voc_get_tty_mode(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+
+	ret = v->tty_mode;
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+int voc_set_pp_enable(uint32_t session_id, uint32_t module_id, uint32_t enable)
+{
+	struct voice_data *v = NULL;
+	int ret = 0;
+	struct voice_session_itr itr;
+
+	voice_itr_init(&itr, session_id);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			if (!(is_voice_app_id(v->session_id)))
+				continue;
+
+			mutex_lock(&v->lock);
+			if (module_id == MODULE_ID_VOICE_MODULE_ST)
+				v->st_enable = enable;
+
+			if (v->voc_state == VOC_RUN) {
+				if ((module_id == MODULE_ID_VOICE_MODULE_ST) &&
+				    (!v->tty_mode))
+					ret = voice_send_set_pp_enable_cmd(v,
+						MODULE_ID_VOICE_MODULE_ST,
+						enable);
+			}
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session_id 0x%x\n", __func__,
+								session_id);
+			ret =  -EINVAL;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int voc_set_hd_enable(uint32_t session_id, uint32_t enable)
+{
+	struct voice_data *v = NULL;
+	int ret = 0;
+	struct voice_session_itr itr;
+
+	voice_itr_init(&itr, session_id);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			v->hd_enable = enable;
+
+			if (v->voc_state == VOC_RUN)
+				ret = voice_send_hd_cmd(v, enable);
+
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session_id 0x%x\n", __func__,
+			       session_id);
+			ret =  -EINVAL;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int voc_get_pp_enable(uint32_t session_id, uint32_t module_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+	if (module_id == MODULE_ID_VOICE_MODULE_ST)
+		ret = v->st_enable;
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step,
+			uint32_t ramp_duration)
+{
+	struct voice_data *v = NULL;
+	int ret = 0;
+	struct voice_session_itr itr;
+
+	pr_debug("%s session id = %#x vol = %u", __func__, session_id,
+		vol_step);
+
+	voice_itr_init(&itr, session_id);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			v->dev_rx.volume_step_value = vol_step;
+			v->dev_rx.volume_ramp_duration_ms = ramp_duration;
+			if (is_voc_state_active(v->voc_state))
+				ret = voice_send_vol_step_cmd(v);
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session_id 0x%x\n", __func__,
+				session_id);
+
+			ret = -EINVAL;
+			break;
+		}
+	}
+
+	return ret;
+}
+
+int voc_set_device_config(uint32_t session_id, uint8_t path_dir,
+			  uint8_t no_of_channels, uint32_t port_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+
+	if (v == NULL) {
+		pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	pr_debug("%s: path_dir=%d port_id=%x, channels=%d\n",
+		 __func__, path_dir, port_id, no_of_channels);
+
+	mutex_lock(&v->lock);
+	if (path_dir == RX_PATH) {
+		v->dev_rx.port_id = q6audio_get_port_id(port_id);
+		v->dev_rx.no_of_channels = no_of_channels;
+	} else {
+		v->dev_tx.port_id = q6audio_get_port_id(port_id);
+		v->dev_tx.no_of_channels = no_of_channels;
+	}
+	mutex_unlock(&v->lock);
+
+	return 0;
+}
+
+int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set)
+{
+	struct voice_data *v = voice_get_session(session_id);
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	pr_debug("%s: path_dir=%d, set=%d\n", __func__, path_dir, set);
+
+	mutex_lock(&v->lock);
+
+	if (path_dir == RX_PATH)
+		v->voc_route_state.rx_route_flag = set;
+	else
+		v->voc_route_state.tx_route_flag = set;
+
+	mutex_unlock(&v->lock);
+
+	return 0;
+}
+
+uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return 0;
+	}
+
+	mutex_lock(&v->lock);
+
+	if (path_dir == RX_PATH)
+		ret = v->voc_route_state.rx_route_flag;
+	else
+		ret = v->voc_route_state.tx_route_flag;
+
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+int voc_end_voice_call(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+
+	if (v->voc_state == VOC_RUN || v->voc_state == VOC_ERROR ||
+	    v->voc_state == VOC_CHANGE || v->voc_state == VOC_STANDBY) {
+
+		pr_debug("%s: VOC_STATE: %d\n", __func__, v->voc_state);
+
+		ret = voice_destroy_vocproc(v);
+		if (ret < 0)
+			pr_err("%s:  destroy voice failed\n", __func__);
+
+		voc_update_session_params(v);
+
+		voice_destroy_mvm_cvs_session(v);
+		v->voc_state = VOC_RELEASE;
+	} else {
+		pr_err("%s: Error: End voice called in state %d\n",
+			__func__, v->voc_state);
+
+		ret = -EINVAL;
+	}
+
+	mutex_unlock(&v->lock);
+	return ret;
+}
+
+int voc_standby_voice_call(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	struct apr_hdr mvm_standby_voice_cmd;
+	void *apr_mvm;
+	u16 mvm_handle;
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	pr_debug("%s: voc state=%d", __func__, v->voc_state);
+
+	if (v->voc_state == VOC_RUN) {
+		apr_mvm = common.apr_q6_mvm;
+		if (!apr_mvm) {
+			pr_err("%s: apr_mvm is NULL.\n", __func__);
+			ret = -EINVAL;
+			goto fail;
+		}
+		mvm_handle = voice_get_mvm_handle(v);
+		mvm_standby_voice_cmd.hdr_field =
+			APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+			APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+		mvm_standby_voice_cmd.pkt_size =
+			APR_PKT_SIZE(APR_HDR_SIZE,
+			sizeof(mvm_standby_voice_cmd) - APR_HDR_SIZE);
+		pr_debug("send mvm_standby_voice_cmd pkt size = %d\n",
+			 mvm_standby_voice_cmd.pkt_size);
+		mvm_standby_voice_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+		mvm_standby_voice_cmd.dest_port = mvm_handle;
+		mvm_standby_voice_cmd.token = 0;
+		mvm_standby_voice_cmd.opcode = VSS_IMVM_CMD_STANDBY_VOICE;
+		v->mvm_state = CMD_STATUS_FAIL;
+		ret = apr_send_pkt(apr_mvm,
+				(uint32_t *)&mvm_standby_voice_cmd);
+		if (ret < 0) {
+			pr_err("Fail in sending VSS_IMVM_CMD_STANDBY_VOICE\n");
+			ret = -EINVAL;
+			goto fail;
+		}
+		v->voc_state = VOC_STANDBY;
+	}
+fail:
+	return ret;
+}
+
+int voc_disable_device(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+
+	mutex_lock(&v->lock);
+	if (v->voc_state == VOC_RUN) {
+		ret = voice_pause_voice_call(v);
+		if (ret < 0) {
+			pr_err("%s: Pause Voice Call failed for session 0x%x, err %d!\n",
+			       __func__, v->session_id, ret);
+			goto done;
+		}
+		rtac_remove_voice(voice_get_cvs_handle(v));
+		voice_send_cvp_deregister_vol_cal_cmd(v);
+		voice_send_cvp_deregister_cal_cmd(v);
+		voice_send_cvp_deregister_dev_cfg_cmd(v);
+
+		v->voc_state = VOC_CHANGE;
+	} else {
+		pr_debug("%s: called in voc state=%d, No_OP\n",
+			 __func__, v->voc_state);
+	}
+
+done:
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+int voc_enable_device(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	pr_debug("%s: voc state=%d\n", __func__, v->voc_state);
+	mutex_lock(&v->lock);
+	if (v->voc_state == VOC_CHANGE) {
+		ret = voice_send_tty_mode_cmd(v);
+		if (ret < 0) {
+			pr_err("%s: Sending TTY mode failed, ret=%d\n",
+			       __func__, ret);
+			/* Not a critical error, allow voice call to continue */
+		}
+
+		if (v->tty_mode) {
+			/* disable slowtalk */
+			voice_send_set_pp_enable_cmd(v,
+						     MODULE_ID_VOICE_MODULE_ST,
+						     0);
+		} else {
+			/* restore slowtalk */
+			voice_send_set_pp_enable_cmd(v,
+						     MODULE_ID_VOICE_MODULE_ST,
+						     v->st_enable);
+		}
+
+		ret = voice_send_set_device_cmd(v);
+		if (ret < 0) {
+			pr_err("%s: Set device failed, ret=%d\n",
+			       __func__, ret);
+			goto done;
+		}
+
+		ret = voice_send_cvp_device_channels_cmd(v);
+		if (ret < 0) {
+			pr_err("%s:  Set device channels failed\n", __func__);
+			goto done;
+		}
+
+		ret = voice_send_cvp_topology_commit_cmd(v);
+		if (ret < 0) {
+			pr_err("%s:  Set topology commit failed\n", __func__);
+			goto done;
+		}
+
+		voice_send_cvp_register_dev_cfg_cmd(v);
+		voice_send_cvp_register_cal_cmd(v);
+		voice_send_cvp_register_vol_cal_cmd(v);
+
+		rtac_add_voice(voice_get_cvs_handle(v),
+			       voice_get_cvp_handle(v),
+			       v->dev_rx.port_id, v->dev_tx.port_id,
+			       v->dev_rx.dev_id, v->dev_tx.dev_id,
+			       v->session_id);
+
+		ret = voice_send_start_voice_cmd(v);
+		if (ret < 0) {
+			pr_err("%s: Fail in sending START_VOICE, ret=%d\n",
+			       __func__, ret);
+			goto done;
+		}
+		v->voc_state = VOC_RUN;
+	} else {
+		pr_debug("%s: called in voc state=%d, No_OP\n",
+			 __func__, v->voc_state);
+	}
+
+done:
+	mutex_unlock(&v->lock);
+
+	return ret;
+}
+
+int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: Invalid session_id 0x%x\n", __func__, session_id);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	mutex_lock(&v->lock);
+	if (v->lch_mode == lch_mode) {
+		pr_debug("%s: Session %d already in LCH mode %d\n",
+				 __func__, session_id, lch_mode);
+
+		mutex_unlock(&v->lock);
+		goto done;
+	}
+
+	v->lch_mode = lch_mode;
+	mutex_unlock(&v->lock);
+
+	ret = voc_lch_ops(v, v->lch_mode);
+	if (ret < 0) {
+		pr_err("%s: lch ops failed %d\n", __func__, ret);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+int voc_resume_voice_call(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	ret = voice_send_start_voice_cmd(v);
+	if (ret < 0) {
+		pr_err("Fail in sending START_VOICE\n");
+		goto fail;
+	}
+	v->voc_state = VOC_RUN;
+	return 0;
+fail:
+	return -EINVAL;
+}
+
+int voc_start_voice_call(uint32_t session_id)
+{
+	struct voice_data *v = voice_get_session(session_id);
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n", __func__, session_id);
+
+		return -EINVAL;
+	}
+
+	mutex_lock(&v->lock);
+
+	if (v->voc_state == VOC_ERROR) {
+		pr_debug("%s: VOC in ERR state\n", __func__);
+
+		voice_destroy_mvm_cvs_session(v);
+		v->voc_state = VOC_INIT;
+	}
+
+	if ((v->voc_state == VOC_INIT) ||
+		(v->voc_state == VOC_RELEASE)) {
+		ret = voice_apr_register(session_id);
+		if (ret < 0) {
+			pr_err("%s:  apr register failed\n", __func__);
+			goto fail;
+		}
+
+		if (is_cvd_version_queried()) {
+			pr_debug("%s: Returning the cached value %s\n",
+				 __func__, common.cvd_version);
+		} else {
+			ret = voice_send_mvm_cvd_version_cmd(v);
+			if (ret < 0)
+				pr_debug("%s: Error retrieving CVD version %d\n",
+					 __func__, ret);
+		}
+
+		ret = voice_create_mvm_cvs_session(v);
+		if (ret < 0) {
+			pr_err("create mvm and cvs failed\n");
+			goto fail;
+		}
+
+		if (is_voip_session(session_id)) {
+			/* Allocate oob mem if not already allocated and
+			 * memory map the oob memory block.
+			 */
+			ret = voice_alloc_and_map_oob_mem(v);
+			if (ret < 0) {
+				pr_err("%s: voice_alloc_and_map_oob_mem() failed, ret:%d\n",
+				       __func__, ret);
+
+				goto fail;
+			}
+
+			ret = voice_set_packet_exchange_mode_and_config(
+				session_id,
+				VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND);
+			if (ret) {
+				pr_err("%s: Err: exchange_mode_and_config  %d\n",
+					__func__, ret);
+
+				goto fail;
+			}
+		}
+		ret = voice_send_dual_control_cmd(v);
+		if (ret < 0) {
+			pr_err("Err Dual command failed\n");
+			goto fail;
+		}
+		ret = voice_setup_vocproc(v);
+		if (ret < 0) {
+			pr_err("setup voice failed\n");
+			goto fail;
+		}
+
+		ret = voice_send_vol_step_cmd(v);
+		if (ret < 0)
+			pr_err("voice volume failed\n");
+
+		ret = voice_send_stream_mute_cmd(v,
+				VSS_IVOLUME_DIRECTION_TX,
+				v->stream_tx.stream_mute,
+				v->stream_tx.stream_mute_ramp_duration_ms);
+		if (ret < 0)
+			pr_err("voice mute failed\n");
+
+		ret = voice_send_start_voice_cmd(v);
+		if (ret < 0) {
+			pr_err("start voice failed\n");
+			goto fail;
+		}
+
+		v->voc_state = VOC_RUN;
+	} else {
+		pr_err("%s: Error: Start voice called in state %d\n",
+			__func__, v->voc_state);
+
+		ret = -EINVAL;
+		goto fail;
+	}
+fail:
+	mutex_unlock(&v->lock);
+	return ret;
+}
+
+int voc_set_ext_ec_ref(uint16_t port_id, bool state)
+{
+	int ret = 0;
+
+	mutex_lock(&common.common_lock);
+	if (state == true) {
+		if (port_id == AFE_PORT_INVALID) {
+			pr_err("%s: Invalid port id", __func__);
+			ret = -EINVAL;
+			goto exit;
+		}
+		common.ec_port_id = port_id;
+		common.ec_ref_ext = true;
+	} else {
+		common.ec_ref_ext = false;
+		common.ec_port_id = port_id;
+	}
+exit:
+	mutex_unlock(&common.common_lock);
+	return ret;
+}
+
+void voc_register_mvs_cb(ul_cb_fn ul_cb,
+			   dl_cb_fn dl_cb,
+			   voip_ssr_cb ssr_cb,
+			   void *private_data)
+{
+	common.mvs_info.ul_cb = ul_cb;
+	common.mvs_info.dl_cb = dl_cb;
+	common.mvs_info.ssr_cb = ssr_cb;
+	common.mvs_info.private_data = private_data;
+}
+
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+				       void *private_data)
+{
+	common.dtmf_info.dtmf_rx_ul_cb = dtmf_rx_ul_cb;
+	common.dtmf_info.private_data = private_data;
+}
+
+void voc_config_vocoder(uint32_t media_type,
+			uint32_t rate,
+			uint32_t network_type,
+			uint32_t dtx_mode,
+			uint32_t evrc_min_rate,
+			uint32_t evrc_max_rate)
+{
+	common.mvs_info.media_type = media_type;
+	common.mvs_info.rate = rate;
+	common.mvs_info.network_type = network_type;
+	common.mvs_info.dtx_mode = dtx_mode;
+	common.mvs_info.evrc_min_rate = evrc_min_rate;
+	common.mvs_info.evrc_max_rate = evrc_max_rate;
+}
+
+static int32_t qdsp_mvm_callback(struct apr_client_data *data, void *priv)
+{
+	uint32_t *ptr = NULL;
+	struct common_data *c = NULL;
+	struct voice_data *v = NULL;
+	int i = 0;
+	struct vss_iversion_rsp_get_t *version_rsp = NULL;
+
+	if ((data == NULL) || (priv == NULL)) {
+		pr_err("%s: data or priv is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	c = priv;
+
+	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+		data->payload_size, data->opcode);
+
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: Reset event received in Voice service\n",
+				__func__);
+
+		if (common.mvs_info.ssr_cb) {
+			pr_debug("%s: Informing reset event to VoIP\n",
+					__func__);
+			common.mvs_info.ssr_cb(data->opcode,
+					common.mvs_info.private_data);
+		}
+
+		apr_reset(c->apr_q6_mvm);
+		c->apr_q6_mvm = NULL;
+
+		/* clean up memory handle */
+		c->cal_mem_handle = 0;
+		c->rtac_mem_handle = 0;
+		cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES,
+				common.cal_data);
+		rtac_clear_mapping(VOICE_RTAC_CAL);
+
+		/* Sub-system restart is applicable to all sessions. */
+		for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+			c->voice[i].mvm_handle = 0;
+			c->voice[i].shmem_info.mem_handle = 0;
+		}
+
+		/* Free the ION memory and clear handles for Source Tracking */
+		if (is_source_tracking_shared_memomry_allocated()) {
+			msm_audio_ion_free(
+			common.source_tracking_sh_mem.sh_mem_block.client,
+			common.source_tracking_sh_mem.sh_mem_block.handle);
+			common.source_tracking_sh_mem.mem_handle = 0;
+			common.source_tracking_sh_mem.sh_mem_block.client =
+									NULL;
+			common.source_tracking_sh_mem.sh_mem_block.handle =
+									NULL;
+		}
+		/* clean up srvcc rec flag */
+		c->srvcc_rec_flag = false;
+		voc_set_error_state(data->reset_proc);
+		return 0;
+	}
+
+	pr_debug("%s: session_idx 0x%x\n", __func__, data->dest_port);
+
+	v = voice_get_session_by_idx(data->dest_port);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		if (data->payload_size) {
+			ptr = data->payload;
+
+			pr_debug("%x %x\n", ptr[0], ptr[1]);
+			/* ping mvm service ACK */
+			switch (ptr[0]) {
+			case VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
+			case VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION:
+				/* Passive session is used for CS call
+				 * Full session is used for VoIP call.
+				 */
+				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+				if (!ptr[1]) {
+					pr_debug("%s: MVM handle is %d\n",
+						 __func__, data->src_port);
+					voice_set_mvm_handle(v, data->src_port);
+				} else
+					pr_err("got NACK for sending MVM create session\n");
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->mvm_wait);
+				break;
+			case VSS_IMVM_CMD_START_VOICE:
+			case VSS_IMVM_CMD_ATTACH_VOCPROC:
+			case VSS_IMVM_CMD_STOP_VOICE:
+			case VSS_IMVM_CMD_DETACH_VOCPROC:
+			case VSS_ISTREAM_CMD_SET_TTY_MODE:
+			case APRV2_IBASIC_CMD_DESTROY_SESSION:
+			case VSS_IMVM_CMD_ATTACH_STREAM:
+			case VSS_IMVM_CMD_DETACH_STREAM:
+			case VSS_ICOMMON_CMD_SET_NETWORK:
+			case VSS_ICOMMON_CMD_SET_VOICE_TIMING:
+			case VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL:
+			case VSS_IMVM_CMD_SET_CAL_NETWORK:
+			case VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE:
+			case VSS_IMEMORY_CMD_MAP_PHYSICAL:
+			case VSS_IMEMORY_CMD_UNMAP:
+			case VSS_IMVM_CMD_PAUSE_VOICE:
+			case VSS_IMVM_CMD_STANDBY_VOICE:
+			case VSS_IHDVOICE_CMD_ENABLE:
+			case VSS_IHDVOICE_CMD_DISABLE:
+				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->mvm_wait);
+				break;
+			case VSS_IVERSION_CMD_GET:
+				pr_debug("%s: Error retrieving CVD Version, error:%d\n",
+					 __func__, ptr[1]);
+
+				strlcpy(common.cvd_version, CVD_VERSION_0_0,
+					sizeof(common.cvd_version));
+				pr_debug("%s: Fall back to default value, CVD Version = %s\n",
+					 __func__, common.cvd_version);
+
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->mvm_wait);
+				break;
+			default:
+				pr_debug("%s: not match cmd = 0x%x\n",
+					__func__, ptr[0]);
+				break;
+			}
+		}
+	} else if (data->opcode == VSS_IMEMORY_RSP_MAP) {
+		pr_debug("%s, Revd VSS_IMEMORY_RSP_MAP response\n", __func__);
+
+		if (data->payload_size && data->token == VOIP_MEM_MAP_TOKEN) {
+			ptr = data->payload;
+			if (ptr[0]) {
+				v->shmem_info.mem_handle = ptr[0];
+				pr_debug("%s: shared mem_handle: 0x[%x]\n",
+					 __func__, v->shmem_info.mem_handle);
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				wake_up(&v->mvm_wait);
+			}
+		} else if (data->payload_size &&
+			   data->token == VOC_CAL_MEM_MAP_TOKEN) {
+			ptr = data->payload;
+			if (ptr[0]) {
+				c->cal_mem_handle = ptr[0];
+
+				pr_debug("%s: cal mem handle 0x%x\n",
+					 __func__, c->cal_mem_handle);
+
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				wake_up(&v->mvm_wait);
+			}
+		} else if (data->payload_size &&
+			   data->token == VOC_VOICE_HOST_PCM_MAP_TOKEN) {
+			ptr = data->payload;
+			if (ptr[0]) {
+				common.voice_host_pcm_mem_handle = ptr[0];
+
+				pr_debug("%s: vhpcm mem handle 0x%x\n",
+					 __func__,
+					 common.voice_host_pcm_mem_handle);
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				wake_up(&v->mvm_wait);
+			}
+		} else if (data->payload_size &&
+				data->token == VOC_RTAC_MEM_MAP_TOKEN) {
+			ptr = data->payload;
+			if (ptr[0]) {
+				c->rtac_mem_handle = ptr[0];
+
+				pr_debug("%s: cal mem handle 0x%x\n",
+					 __func__, c->rtac_mem_handle);
+
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				wake_up(&v->mvm_wait);
+			}
+		} else if (data->payload_size &&
+			   data->token == VOC_SOURCE_TRACKING_MEM_MAP_TOKEN) {
+			ptr = data->payload;
+			if (ptr[0]) {
+				common.source_tracking_sh_mem.mem_handle =
+									ptr[0];
+
+				pr_debug("%s: Source Tracking shared mem handle 0x%x\n",
+					 __func__,
+				   common.source_tracking_sh_mem.mem_handle);
+
+				v->mvm_state = CMD_STATUS_SUCCESS;
+				wake_up(&v->mvm_wait);
+			}
+		} else {
+			pr_err("%s: Unknown mem map token %d\n",
+			       __func__, data->token);
+		}
+	} else if (data->opcode == VSS_IVERSION_RSP_GET) {
+		pr_debug("%s: Received VSS_IVERSION_RSP_GET\n", __func__);
+
+		if (data->payload_size) {
+			version_rsp =
+				(struct vss_iversion_rsp_get_t *)data->payload;
+			memcpy(common.cvd_version, version_rsp->version,
+			       CVD_VERSION_STRING_MAX_SIZE);
+			pr_debug("%s: CVD Version = %s\n",
+				 __func__, common.cvd_version);
+
+			v->mvm_state = CMD_STATUS_SUCCESS;
+			wake_up(&v->mvm_wait);
+		}
+	}
+	return 0;
+}
+
+static int32_t qdsp_cvs_callback(struct apr_client_data *data, void *priv)
+{
+	uint32_t *ptr = NULL;
+	struct common_data *c = NULL;
+	struct voice_data *v = NULL;
+	int i = 0;
+
+	if ((data == NULL) || (priv == NULL)) {
+		pr_err("%s: data or priv is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	c = priv;
+
+	pr_debug("%s: session_id 0x%x\n", __func__, data->dest_port);
+	pr_debug("%s: Payload Length = %d, opcode=%x\n", __func__,
+		data->payload_size, data->opcode);
+
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: Reset event received in Voice service\n",
+				__func__);
+
+		apr_reset(c->apr_q6_cvs);
+		c->apr_q6_cvs = NULL;
+
+		/* Sub-system restart is applicable to all sessions. */
+		for (i = 0; i < MAX_VOC_SESSIONS; i++)
+			c->voice[i].cvs_handle = 0;
+
+		cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES,
+				common.cal_data);
+
+		/* Free the ION memory and clear handles for Source Tracking */
+		if (is_source_tracking_shared_memomry_allocated()) {
+			msm_audio_ion_free(
+			common.source_tracking_sh_mem.sh_mem_block.client,
+			common.source_tracking_sh_mem.sh_mem_block.handle);
+			common.source_tracking_sh_mem.mem_handle = 0;
+			common.source_tracking_sh_mem.sh_mem_block.client =
+									NULL;
+			common.source_tracking_sh_mem.sh_mem_block.handle =
+									NULL;
+		}
+		voc_set_error_state(data->reset_proc);
+		return 0;
+	}
+
+	v = voice_get_session_by_idx(data->dest_port);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		if (data->payload_size) {
+			ptr = data->payload;
+
+			pr_debug("%x %x\n", ptr[0], ptr[1]);
+			if (ptr[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, ptr[0], ptr[1]);
+			}
+			/*response from  CVS */
+			switch (ptr[0]) {
+			case VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION:
+			case VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION:
+				if (!ptr[1]) {
+					pr_debug("%s: CVS handle is %d\n",
+						 __func__, data->src_port);
+					voice_set_cvs_handle(v, data->src_port);
+				} else
+					pr_err("got NACK for sending CVS create session\n");
+				v->cvs_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvs_wait);
+				break;
+			case VSS_IVOLUME_CMD_MUTE_V2:
+			case VSS_ISTREAM_CMD_SET_MEDIA_TYPE:
+			case VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE:
+			case VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE:
+			case VSS_ISTREAM_CMD_SET_ENC_DTX_MODE:
+			case VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE:
+			case APRV2_IBASIC_CMD_DESTROY_SESSION:
+			case VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2:
+			case VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA:
+			case VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA:
+			case VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA:
+			case VSS_ICOMMON_CMD_MAP_MEMORY:
+			case VSS_ICOMMON_CMD_UNMAP_MEMORY:
+			case VSS_ICOMMON_CMD_SET_UI_PROPERTY:
+			case VSS_IPLAYBACK_CMD_START:
+			case VSS_IPLAYBACK_CMD_STOP:
+			case VSS_IRECORD_CMD_START:
+			case VSS_IRECORD_CMD_STOP:
+			case VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE:
+			case VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG:
+			case VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION:
+				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+				v->cvs_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvs_wait);
+				break;
+			case VOICE_CMD_SET_PARAM:
+				pr_debug("%s: VOICE_CMD_SET_PARAM\n", __func__);
+				rtac_make_voice_callback(RTAC_CVS, ptr,
+							data->payload_size);
+				break;
+			case VOICE_CMD_GET_PARAM:
+				pr_debug("%s: VOICE_CMD_GET_PARAM\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* VOICE_EVT_GET_PARAM_ACK */
+				if (ptr[1] != 0) {
+					pr_err("%s: CVP get param error = %d, resuming\n",
+						__func__, ptr[1]);
+					rtac_make_voice_callback(RTAC_CVP,
+						data->payload,
+						data->payload_size);
+				}
+				break;
+			default:
+				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+				break;
+			}
+		}
+	} else if (data->opcode ==
+			 VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY) {
+		int ret = 0;
+		u16 cvs_handle;
+		uint32_t *cvs_voc_pkt;
+		struct cvs_enc_buffer_consumed_cmd send_enc_buf_consumed_cmd;
+		void *apr_cvs;
+
+		pr_debug("Encoder buffer is ready\n");
+
+		apr_cvs = common.apr_q6_cvs;
+		if (!apr_cvs) {
+			pr_err("%s: apr_cvs is NULL\n", __func__);
+			return -EINVAL;
+		}
+		cvs_handle = voice_get_cvs_handle(v);
+
+		send_enc_buf_consumed_cmd.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE),
+				APR_PKT_VER);
+		send_enc_buf_consumed_cmd.hdr.pkt_size =
+			APR_PKT_SIZE(APR_HDR_SIZE,
+			sizeof(send_enc_buf_consumed_cmd) - APR_HDR_SIZE);
+
+		send_enc_buf_consumed_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		send_enc_buf_consumed_cmd.hdr.dest_port = cvs_handle;
+		send_enc_buf_consumed_cmd.hdr.token = 0;
+		send_enc_buf_consumed_cmd.hdr.opcode =
+			VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED;
+
+		cvs_voc_pkt = v->shmem_info.sh_buf.buf[1].data;
+		if (cvs_voc_pkt != NULL &&  common.mvs_info.ul_cb != NULL) {
+			/* cvs_voc_pkt[0] contains tx timestamp */
+			common.mvs_info.ul_cb((uint8_t *)&cvs_voc_pkt[3],
+					      cvs_voc_pkt[2],
+					      cvs_voc_pkt[0],
+					      common.mvs_info.private_data);
+		} else
+			pr_err("%s: cvs_voc_pkt or ul_cb is NULL\n", __func__);
+
+		ret = apr_send_pkt(apr_cvs,
+			(uint32_t *) &send_enc_buf_consumed_cmd);
+		if (ret < 0) {
+			pr_err("%s: Err send ENC_BUF_CONSUMED_NOTIFY %d\n",
+				__func__, ret);
+			goto fail;
+		}
+	} else if (data->opcode == VSS_ISTREAM_EVT_SEND_ENC_BUFFER) {
+		pr_debug("Recd VSS_ISTREAM_EVT_SEND_ENC_BUFFER\n");
+	} else if (data->opcode ==
+			 VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST) {
+		int ret = 0;
+		u16 cvs_handle;
+		uint32_t *cvs_voc_pkt;
+		struct cvs_dec_buffer_ready_cmd send_dec_buf;
+		void *apr_cvs;
+
+		apr_cvs = common.apr_q6_cvs;
+
+		if (!apr_cvs) {
+			pr_err("%s: apr_cvs is NULL\n", __func__);
+			return -EINVAL;
+		}
+		cvs_handle = voice_get_cvs_handle(v);
+
+		send_dec_buf.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE),
+				APR_PKT_VER);
+
+		send_dec_buf.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					sizeof(send_dec_buf) - APR_HDR_SIZE);
+
+		send_dec_buf.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+		send_dec_buf.hdr.dest_port = cvs_handle;
+		send_dec_buf.hdr.token = 0;
+		send_dec_buf.hdr.opcode =
+				 VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY;
+
+		cvs_voc_pkt = (uint32_t *)(v->shmem_info.sh_buf.buf[0].data);
+		if (cvs_voc_pkt != NULL && common.mvs_info.dl_cb != NULL) {
+			/* Set timestamp to 0 and advance the pointer */
+			cvs_voc_pkt[0] = 0;
+			/* Set media_type and advance the pointer */
+			cvs_voc_pkt[1] = common.mvs_info.media_type;
+			common.mvs_info.dl_cb(
+					      (uint8_t *)&cvs_voc_pkt[2],
+					      common.mvs_info.private_data);
+			ret = apr_send_pkt(apr_cvs, (uint32_t *) &send_dec_buf);
+			if (ret < 0) {
+				pr_err("%s: Err send DEC_BUF_READY_NOTIFI %d\n",
+					__func__, ret);
+				goto fail;
+			}
+		} else {
+			pr_debug("%s: voc_pkt or dl_cb is NULL\n", __func__);
+			goto fail;
+		}
+	} else if (data->opcode == VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER) {
+		pr_debug("Recd VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER\n");
+	} else if (data->opcode == VSS_ISTREAM_EVT_SEND_DEC_BUFFER) {
+		pr_debug("Send dec buf resp\n");
+	} else if (data->opcode == APR_RSP_ACCEPTED) {
+		ptr = data->payload;
+		if (ptr[0])
+			pr_debug("%s: APR_RSP_ACCEPTED for 0x%x:\n",
+				 __func__, ptr[0]);
+	} else if (data->opcode == VSS_ISTREAM_EVT_NOT_READY) {
+		pr_debug("Recd VSS_ISTREAM_EVT_NOT_READY\n");
+	} else if (data->opcode == VSS_ISTREAM_EVT_READY) {
+		pr_debug("Recd VSS_ISTREAM_EVT_READY\n");
+	} else if (data->opcode ==  VOICE_EVT_GET_PARAM_ACK) {
+		pr_debug("%s: VOICE_EVT_GET_PARAM_ACK\n", __func__);
+		ptr = data->payload;
+		if (ptr[0] != 0) {
+			pr_err("%s: VOICE_EVT_GET_PARAM_ACK returned error = 0x%x\n",
+				__func__, ptr[0]);
+		}
+		rtac_make_voice_callback(RTAC_CVS, data->payload,
+					data->payload_size);
+	}  else if (data->opcode == VSS_ISTREAM_EVT_RX_DTMF_DETECTED) {
+		struct vss_istream_evt_rx_dtmf_detected *dtmf_rx_detected;
+		uint32_t *voc_pkt = data->payload;
+		uint32_t pkt_len = data->payload_size;
+
+		if ((voc_pkt != NULL) &&
+		    (pkt_len ==
+			sizeof(struct vss_istream_evt_rx_dtmf_detected))) {
+
+			dtmf_rx_detected =
+			(struct vss_istream_evt_rx_dtmf_detected *) voc_pkt;
+			pr_debug("RX_DTMF_DETECTED low_freq=%d high_freq=%d\n",
+				 dtmf_rx_detected->low_freq,
+				 dtmf_rx_detected->high_freq);
+			if (c->dtmf_info.dtmf_rx_ul_cb)
+				c->dtmf_info.dtmf_rx_ul_cb((uint8_t *)voc_pkt,
+					voc_get_session_name(v->session_id),
+					c->dtmf_info.private_data);
+		} else {
+			pr_err("Invalid packet\n");
+		}
+	}  else
+		pr_debug("Unknown opcode 0x%x\n", data->opcode);
+
+fail:
+	return 0;
+}
+
+static int32_t qdsp_cvp_callback(struct apr_client_data *data, void *priv)
+{
+	uint32_t *ptr = NULL;
+	struct common_data *c = NULL;
+	struct voice_data *v = NULL;
+	int i = 0;
+
+	if ((data == NULL) || (priv == NULL)) {
+		pr_err("%s: data or priv is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	c = priv;
+
+	if (data->opcode == RESET_EVENTS) {
+		pr_debug("%s: Reset event received in Voice service\n",
+				__func__);
+
+		apr_reset(c->apr_q6_cvp);
+		c->apr_q6_cvp = NULL;
+		cal_utils_clear_cal_block_q6maps(MAX_VOICE_CAL_TYPES,
+				common.cal_data);
+
+		/* Sub-system restart is applicable to all sessions. */
+		for (i = 0; i < MAX_VOC_SESSIONS; i++)
+			c->voice[i].cvp_handle = 0;
+
+		/*
+		 * Free the ION memory and clear handles for
+		 * Source Tracking
+		 */
+		if (is_source_tracking_shared_memomry_allocated()) {
+			msm_audio_ion_free(
+			common.source_tracking_sh_mem.sh_mem_block.client,
+			common.source_tracking_sh_mem.sh_mem_block.handle);
+			common.source_tracking_sh_mem.mem_handle = 0;
+			common.source_tracking_sh_mem.sh_mem_block.client =
+									NULL;
+			common.source_tracking_sh_mem.sh_mem_block.handle =
+									NULL;
+		}
+		voc_set_error_state(data->reset_proc);
+		return 0;
+	}
+
+	v = voice_get_session_by_idx(data->dest_port);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	if (data->opcode == APR_BASIC_RSP_RESULT) {
+		if (data->payload_size) {
+			ptr = data->payload;
+
+			pr_debug("%x %x\n", ptr[0], ptr[1]);
+			if (ptr[1] != 0) {
+				pr_err("%s: cmd = 0x%x returned error = 0x%x\n",
+					__func__, ptr[0], ptr[1]);
+			}
+			switch (ptr[0]) {
+			case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2:
+			case VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3:
+			/*response from  CVP */
+				pr_debug("%s: cmd = 0x%x\n", __func__, ptr[0]);
+				if (!ptr[1]) {
+					voice_set_cvp_handle(v, data->src_port);
+					pr_debug("status: %d, cvphdl=%d\n",
+						 ptr[1], data->src_port);
+				} else
+					pr_err("got NACK from CVP create session response\n");
+				v->cvp_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvp_wait);
+				break;
+			case VSS_IVOCPROC_CMD_SET_DEVICE_V2:
+			case VSS_IVOCPROC_CMD_SET_DEVICE_V3:
+			case VSS_IVOLUME_CMD_SET_STEP:
+			case VSS_IVOCPROC_CMD_ENABLE:
+			case VSS_IVOCPROC_CMD_DISABLE:
+			case APRV2_IBASIC_CMD_DESTROY_SESSION:
+			case VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2:
+			case VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA:
+			case VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG:
+			case VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG:
+			case VSS_ICOMMON_CMD_MAP_MEMORY:
+			case VSS_ICOMMON_CMD_UNMAP_MEMORY:
+			case VSS_IVOLUME_CMD_MUTE_V2:
+			case VSS_IVPCM_CMD_START_V2:
+			case VSS_IVPCM_CMD_STOP:
+			case VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS:
+			case VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT:
+				v->cvp_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvp_wait);
+				break;
+			case VSS_IVPCM_EVT_PUSH_BUFFER_V2:
+				break;
+			case VOICE_CMD_SET_PARAM:
+				pr_debug("%s: VOICE_CMD_SET_PARAM\n", __func__);
+				rtac_make_voice_callback(RTAC_CVP, ptr,
+							data->payload_size);
+				break;
+			case VOICE_CMD_GET_PARAM:
+				pr_debug("%s: VOICE_CMD_GET_PARAM\n",
+					__func__);
+				/* Should only come here if there is an APR */
+				/* error or malformed APR packet. Otherwise */
+				/* response will be returned as */
+				/* VOICE_EVT_GET_PARAM_ACK */
+				if (ptr[1] != 0) {
+					pr_err("%s: CVP get param error = %d, resuming\n",
+						__func__, ptr[1]);
+					rtac_make_voice_callback(RTAC_CVP,
+						data->payload,
+						data->payload_size);
+				}
+				break;
+			case VSS_ISOUNDFOCUS_CMD_SET_SECTORS:
+				if (!ptr[1])
+					common.is_sound_focus_resp_success =
+									true;
+				else
+					common.is_sound_focus_resp_success =
+									false;
+				v->cvp_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvp_wait);
+				break;
+			case VSS_ISOUNDFOCUS_CMD_GET_SECTORS:
+				/*
+				 * Should only come here if there is an error
+				 * response received from ADSP. Otherwise
+				 * response will be returned as
+				 * VSS_ISOUNDFOCUS_RSP_GET_SECTORS
+				 */
+				pr_err("%s: VSS_ISOUNDFOCUS_CMD_GET_SECTORS failed\n",
+					__func__);
+
+				common.is_sound_focus_resp_success = false;
+				v->cvp_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvp_wait);
+				break;
+			case VSS_ISOURCETRACK_CMD_GET_ACTIVITY:
+				if (!ptr[1]) {
+					/* Read data from shared memory */
+					memcpy(&common.sourceTrackingResponse,
+					       common.source_tracking_sh_mem.
+							sh_mem_block.data,
+					       sizeof(struct
+					 vss_isourcetrack_activity_data_t));
+					common.is_source_tracking_resp_success =
+									true;
+				} else {
+					common.is_source_tracking_resp_success =
+									false;
+					pr_err("%s: Error received for source tracking params\n",
+						__func__);
+				}
+				v->cvp_state = CMD_STATUS_SUCCESS;
+				v->async_err = ptr[1];
+				wake_up(&v->cvp_wait);
+				break;
+			default:
+				pr_debug("%s: not match cmd = 0x%x\n",
+					  __func__, ptr[0]);
+				break;
+			}
+		}
+	} else if (data->opcode ==  VOICE_EVT_GET_PARAM_ACK) {
+		pr_debug("%s: VOICE_EVT_GET_PARAM_ACK\n", __func__);
+		ptr = data->payload;
+		if (ptr[0] != 0) {
+			pr_err("%s: VOICE_EVT_GET_PARAM_ACK returned error = 0x%x\n",
+				__func__, ptr[0]);
+		}
+		rtac_make_voice_callback(RTAC_CVP, data->payload,
+			data->payload_size);
+	} else if (data->opcode == VSS_IVPCM_EVT_NOTIFY_V2) {
+		if ((data->payload != NULL) && data->payload_size ==
+		    sizeof(struct vss_ivpcm_evt_notify_v2_t) &&
+		    common.hostpcm_info.hostpcm_evt_cb != NULL) {
+			common.hostpcm_info.hostpcm_evt_cb(data->payload,
+					voc_get_session_name(v->session_id),
+					common.hostpcm_info.private_data);
+		}
+	} else if (data->opcode == VSS_ISOUNDFOCUS_RSP_GET_SECTORS) {
+		if (data->payload && (data->payload_size ==
+			sizeof(struct vss_isoundfocus_rsp_get_sectors_t))) {
+			common.is_sound_focus_resp_success = true;
+			memcpy(&common.soundFocusResponse,
+			       (struct vss_isoundfocus_rsp_get_sectors_t *)
+			       data->payload,
+			       sizeof(struct
+					 vss_isoundfocus_rsp_get_sectors_t));
+		} else {
+			common.is_sound_focus_resp_success = false;
+			pr_debug("%s: Invalid payload received from CVD\n",
+				 __func__);
+		}
+		v->cvp_state = CMD_STATUS_SUCCESS;
+		wake_up(&v->cvp_wait);
+	}
+	return 0;
+}
+
+static int voice_free_oob_shared_mem(void)
+{
+	int rc = 0;
+	int cnt = 0;
+	int bufcnt = NUM_OF_BUFFERS;
+	struct voice_data *v = voice_get_session(
+				common.voice[VOC_PATH_FULL].session_id);
+
+	mutex_lock(&common.common_lock);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		rc = -EINVAL;
+		goto done;
+	}
+
+	rc = msm_audio_ion_free(v->shmem_info.sh_buf.client,
+				v->shmem_info.sh_buf.handle);
+	v->shmem_info.sh_buf.client = NULL;
+	v->shmem_info.sh_buf.handle = NULL;
+	if (rc < 0) {
+		pr_err("%s: Error:%d freeing memory\n", __func__, rc);
+
+		goto done;
+	}
+
+
+	while (cnt < bufcnt) {
+		v->shmem_info.sh_buf.buf[cnt].data =  NULL;
+		v->shmem_info.sh_buf.buf[cnt].phys =  0;
+		cnt++;
+	}
+
+	v->shmem_info.sh_buf.client = NULL;
+	v->shmem_info.sh_buf.handle = NULL;
+
+done:
+	mutex_unlock(&common.common_lock);
+	return rc;
+}
+
+static int voice_alloc_oob_shared_mem(void)
+{
+	int cnt = 0;
+	int rc = 0;
+	size_t len;
+	void *mem_addr;
+	dma_addr_t phys;
+	int bufsz = BUFFER_BLOCK_SIZE;
+	int bufcnt = NUM_OF_BUFFERS;
+	struct voice_data *v = voice_get_session(
+				common.voice[VOC_PATH_FULL].session_id);
+
+	mutex_lock(&common.common_lock);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		rc = -EINVAL;
+		goto done;
+	}
+
+	rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.sh_buf.client),
+			&(v->shmem_info.sh_buf.handle),
+			bufsz*bufcnt,
+			&phys, &len,
+			&mem_addr);
+	if (rc < 0) {
+		pr_err("%s: audio ION alloc failed, rc = %d\n",
+			__func__, rc);
+
+		goto done;
+	}
+
+	while (cnt < bufcnt) {
+		v->shmem_info.sh_buf.buf[cnt].data =  mem_addr  + (cnt * bufsz);
+		v->shmem_info.sh_buf.buf[cnt].phys =  phys + (cnt * bufsz);
+		v->shmem_info.sh_buf.buf[cnt].size = bufsz;
+		cnt++;
+	}
+
+	pr_debug("%s buf[0].data:[%pK], buf[0].phys:[%pK], &buf[0].phys:[%pK],\n",
+		 __func__,
+		(void *)v->shmem_info.sh_buf.buf[0].data,
+		&v->shmem_info.sh_buf.buf[0].phys,
+		(void *)&v->shmem_info.sh_buf.buf[0].phys);
+	pr_debug("%s: buf[1].data:[%pK], buf[1].phys[%pK], &buf[1].phys[%pK]\n",
+		__func__,
+		(void *)v->shmem_info.sh_buf.buf[1].data,
+		&v->shmem_info.sh_buf.buf[1].phys,
+		(void *)&v->shmem_info.sh_buf.buf[1].phys);
+
+	memset((void *)v->shmem_info.sh_buf.buf[0].data, 0, (bufsz * bufcnt));
+
+done:
+	mutex_unlock(&common.common_lock);
+	return rc;
+}
+
+static int voice_alloc_oob_mem_table(void)
+{
+	int rc = 0;
+	size_t len;
+	struct voice_data *v = voice_get_session(
+				common.voice[VOC_PATH_FULL].session_id);
+
+	mutex_lock(&common.common_lock);
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		rc = -EINVAL;
+		goto done;
+	}
+
+	rc = msm_audio_ion_alloc("voip_client", &(v->shmem_info.memtbl.client),
+				&(v->shmem_info.memtbl.handle),
+				sizeof(struct vss_imemory_table_t),
+				&v->shmem_info.memtbl.phys,
+				&len,
+				&(v->shmem_info.memtbl.data));
+	if (rc < 0) {
+		pr_err("%s: audio ION alloc failed, rc = %d\n",
+			__func__, rc);
+
+		goto done;
+	}
+
+	v->shmem_info.memtbl.size = sizeof(struct vss_imemory_table_t);
+	pr_debug("%s data[%pK]phys[%pK][%pK]\n", __func__,
+		 (void *)v->shmem_info.memtbl.data,
+		 &v->shmem_info.memtbl.phys,
+		 (void *)&v->shmem_info.memtbl.phys);
+
+done:
+	mutex_unlock(&common.common_lock);
+	return rc;
+}
+
+int voc_send_cvp_start_vocpcm(uint32_t session_id,
+			      struct vss_ivpcm_tap_point *vpcm_tp,
+			      uint32_t no_of_tp)
+{
+	struct cvp_start_cmd cvp_start_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+	struct voice_data *v = voice_get_session(session_id);
+	int i = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* Fill the header */
+	cvp_start_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	cvp_start_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+		sizeof(struct vss_ivpcm_tap_point) * no_of_tp) +
+		sizeof(cvp_start_cmd.vpcm_start_cmd.num_tap_points) +
+		sizeof(cvp_start_cmd.vpcm_start_cmd.mem_handle);
+	cvp_start_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+	cvp_start_cmd.hdr.dest_port = cvp_handle;
+	cvp_start_cmd.hdr.token = 0;
+	cvp_start_cmd.hdr.opcode = VSS_IVPCM_CMD_START_V2;
+
+	for (i = 0; i < no_of_tp; i++) {
+		cvp_start_cmd.vpcm_start_cmd.tap_points[i].tap_point =
+							vpcm_tp[i].tap_point;
+		cvp_start_cmd.vpcm_start_cmd.tap_points[i].direction =
+							vpcm_tp[i].direction;
+		cvp_start_cmd.vpcm_start_cmd.tap_points[i].sampling_rate =
+						    vpcm_tp[i].sampling_rate;
+		cvp_start_cmd.vpcm_start_cmd.tap_points[i].duration = 0;
+	}
+
+	cvp_start_cmd.vpcm_start_cmd.mem_handle =
+				common.voice_host_pcm_mem_handle;
+	cvp_start_cmd.vpcm_start_cmd.num_tap_points = no_of_tp;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &cvp_start_cmd);
+	if (ret < 0) {
+		pr_err("%s: Fail: sending vocpcm map memory,\n", __func__);
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+			(v->cvp_state == CMD_STATUS_SUCCESS),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto done;
+	}
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+int voc_send_cvp_stop_vocpcm(uint32_t session_id)
+{
+	struct cvp_command vpcm_stop_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+	struct voice_data *v = voice_get_session(session_id);
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* fill in the header */
+	vpcm_stop_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				 APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	vpcm_stop_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+			 sizeof(vpcm_stop_cmd) - APR_HDR_SIZE);
+	vpcm_stop_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+	vpcm_stop_cmd.hdr.dest_port = cvp_handle;
+	vpcm_stop_cmd.hdr.token = 0;
+	vpcm_stop_cmd.hdr.opcode = VSS_IVPCM_CMD_STOP;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_stop_cmd);
+	if (ret < 0) {
+		pr_err("Fail: sending vocpcm stop,\n");
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+			(v->cvp_state == CMD_STATUS_SUCCESS),
+			msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+		goto done;
+	}
+
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+int voc_send_cvp_map_vocpcm_memory(uint32_t session_id,
+				   struct mem_map_table *tp_mem_table,
+				   phys_addr_t paddr, uint32_t bufsize)
+{
+	return  voice_map_memory_physical_cmd(voice_get_session(session_id),
+					      tp_mem_table,
+					      (dma_addr_t) paddr, bufsize,
+					      VOC_VOICE_HOST_PCM_MAP_TOKEN);
+}
+
+int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id)
+{
+	int ret = 0;
+
+	ret =  voice_send_mvm_unmap_memory_physical_cmd(
+				voice_get_session(session_id),
+				common.voice_host_pcm_mem_handle);
+
+	if (ret == 0)
+		common.voice_host_pcm_mem_handle = 0;
+
+	return ret;
+}
+
+int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id,
+			struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt)
+{
+	struct cvp_push_buf_cmd vpcm_push_buf_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+	struct voice_data *v = voice_get_session(session_id);
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	memset(&vpcm_push_buf_cmd, 0, sizeof(vpcm_push_buf_cmd));
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* fill in the header */
+	vpcm_push_buf_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+				APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	vpcm_push_buf_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+				sizeof(vpcm_push_buf_cmd) - APR_HDR_SIZE);
+	vpcm_push_buf_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	vpcm_push_buf_cmd.hdr.dest_port = cvp_handle;
+	vpcm_push_buf_cmd.hdr.token = 0;
+	vpcm_push_buf_cmd.hdr.opcode = VSS_IVPCM_EVT_PUSH_BUFFER_V2;
+
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.tap_point =
+					push_buff_evt->tap_point;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.push_buf_mask =
+					push_buff_evt->push_buf_mask;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_address =
+					push_buff_evt->out_buf_mem_address;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_address =
+					push_buff_evt->in_buf_mem_address;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.out_buf_mem_size =
+					push_buff_evt->out_buf_mem_size;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.in_buf_mem_size =
+					push_buff_evt->in_buf_mem_size;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.sampling_rate =
+					push_buff_evt->sampling_rate;
+	vpcm_push_buf_cmd.vpcm_evt_push_buffer.num_in_channels =
+					push_buff_evt->num_in_channels;
+
+	ret = apr_send_pkt(apr_cvp, (uint32_t *) &vpcm_push_buf_cmd);
+	if (ret < 0) {
+		pr_err("Fail: sending vocpcm map memory,\n");
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb,
+			      void *private_data)
+{
+	common.hostpcm_info.hostpcm_evt_cb = hostpcm_cb;
+	common.hostpcm_info.private_data = private_data;
+}
+
+void voc_deregister_hpcm_evt_cb(void)
+{
+	common.hostpcm_info.hostpcm_evt_cb = NULL;
+	common.hostpcm_info.private_data = NULL;
+}
+
+int voc_get_cvd_version(char *cvd_version)
+{
+	int ret = 0;
+	struct voice_data *v = voice_get_session(VOICE_SESSION_VSID);
+
+
+	if (v == NULL) {
+		pr_err("%s: invalid session_id 0x%x\n",
+		       __func__, VOICE_SESSION_VSID);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (is_cvd_version_queried()) {
+		pr_debug("%s: Returning the cached value %s\n",
+			 __func__, common.cvd_version);
+
+		goto done;
+	}
+
+	/* Register callback to APR */
+	ret = voice_apr_register(VOICE_SESSION_VSID);
+	if (ret < 0) {
+		pr_err("%s: apr register failed\n", __func__);
+		goto done;
+	}
+
+	mutex_lock(&common.common_lock);
+	mutex_lock(&v->lock);
+	ret = voice_send_mvm_cvd_version_cmd(v);
+	if (ret < 0) {
+		pr_err("%s: voice_send_mvm_cvd_version_cmd failed\n", __func__);
+		goto unlock;
+	}
+	ret = 0;
+
+unlock:
+	mutex_unlock(&v->lock);
+	mutex_unlock(&common.common_lock);
+
+done:
+	if (cvd_version)
+		memcpy(cvd_version, common.cvd_version,
+		       CVD_VERSION_STRING_MAX_SIZE);
+
+	return ret;
+}
+
+static int voice_alloc_cal_mem_map_table(void)
+{
+	int ret = 0;
+	size_t len;
+
+	ret = msm_audio_ion_alloc("voc_cal",
+				&(common.cal_mem_map_table.client),
+				&(common.cal_mem_map_table.handle),
+				sizeof(struct vss_imemory_table_t),
+				&common.cal_mem_map_table.phys,
+				&len,
+				&(common.cal_mem_map_table.data));
+	if ((ret < 0) && (ret != -EPROBE_DEFER)) {
+		pr_err("%s: audio ION alloc failed, rc = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	common.cal_mem_map_table.size = sizeof(struct vss_imemory_table_t);
+	pr_debug("%s: data %pK phys %pK\n", __func__,
+		 common.cal_mem_map_table.data,
+		 &common.cal_mem_map_table.phys);
+
+done:
+	return ret;
+}
+
+static int voice_alloc_rtac_mem_map_table(void)
+{
+	int ret = 0;
+	size_t len;
+
+	ret = msm_audio_ion_alloc("voc_rtac_cal",
+			&(common.rtac_mem_map_table.client),
+			&(common.rtac_mem_map_table.handle),
+			sizeof(struct vss_imemory_table_t),
+			&common.rtac_mem_map_table.phys,
+			&len,
+			&(common.rtac_mem_map_table.data));
+	if (ret < 0) {
+		pr_err("%s: audio ION alloc failed, rc = %d\n",
+			__func__, ret);
+		goto done;
+	}
+
+	common.rtac_mem_map_table.size = sizeof(struct vss_imemory_table_t);
+	pr_debug("%s: data %pK phys %pK\n", __func__,
+		 common.rtac_mem_map_table.data,
+		 &common.rtac_mem_map_table.phys);
+
+done:
+	return ret;
+}
+
+static int voice_alloc_and_map_oob_mem(struct voice_data *v)
+{
+	int ret = 0;
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		return -EINVAL;
+	}
+
+	if (!is_voip_memory_allocated()) {
+		ret = voc_alloc_voip_shared_memory();
+		if (ret < 0) {
+			pr_err("%s: Failed to create voip oob memory %d\n",
+				   __func__, ret);
+
+			goto done;
+		}
+	}
+
+	ret = voice_map_memory_physical_cmd(v,
+			&v->shmem_info.memtbl,
+			v->shmem_info.sh_buf.buf[0].phys,
+			v->shmem_info.sh_buf.buf[0].size * NUM_OF_BUFFERS,
+			VOIP_MEM_MAP_TOKEN);
+	if (ret) {
+		pr_err("%s: mvm_map_memory_phy failed %d\n",
+			   __func__, ret);
+
+		goto done;
+	}
+
+done:
+	return ret;
+}
+
+uint32_t voice_get_topology(uint32_t topology_idx)
+{
+	uint32_t topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+	struct cal_block_data *cal_block = NULL;
+
+	/* initialize as default topology */
+	if (topology_idx == CVP_VOC_RX_TOPOLOGY_CAL) {
+		topology = VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT;
+	} else if (topology_idx == CVP_VOC_TX_TOPOLOGY_CAL) {
+		topology = VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS;
+	} else {
+		pr_err("%s: cal index %x is invalid!\n",
+			__func__, topology_idx);
+
+		goto done;
+	}
+
+	if (common.cal_data[topology_idx] == NULL) {
+		pr_err("%s: cal type is NULL for cal index %x\n",
+			__func__, topology_idx);
+
+		goto done;
+	}
+
+	mutex_lock(&common.cal_data[topology_idx]->lock);
+	cal_block = cal_utils_get_only_cal_block(
+		common.cal_data[topology_idx]);
+	if (cal_block == NULL) {
+		pr_debug("%s: cal_block not found for cal index %x\n",
+			__func__, topology_idx);
+
+		goto unlock;
+	}
+
+	topology = ((struct audio_cal_info_voc_top *)
+		cal_block->cal_info)->topology;
+unlock:
+	mutex_unlock(&common.cal_data[topology_idx]->lock);
+done:
+	pr_debug("%s: Using topology %d\n", __func__, topology);
+
+	return topology;
+}
+
+static int get_cal_type_index(int32_t cal_type)
+{
+	int ret = -EINVAL;
+
+	switch (cal_type) {
+	case CVP_VOC_RX_TOPOLOGY_CAL_TYPE:
+		ret = CVP_VOC_RX_TOPOLOGY_CAL;
+		break;
+	case CVP_VOC_TX_TOPOLOGY_CAL_TYPE:
+		ret = CVP_VOC_TX_TOPOLOGY_CAL;
+		break;
+	case CVP_VOCPROC_STATIC_CAL_TYPE:
+		ret = CVP_VOCPROC_CAL;
+		break;
+	case CVP_VOCPROC_DYNAMIC_CAL_TYPE:
+		ret = CVP_VOCVOL_CAL;
+		break;
+	case CVS_VOCSTRM_STATIC_CAL_TYPE:
+		ret = CVS_VOCSTRM_CAL;
+		break;
+	case CVP_VOCDEV_CFG_CAL_TYPE:
+		ret = CVP_VOCDEV_CFG_CAL;
+		break;
+	case CVP_VOCPROC_STATIC_COL_CAL_TYPE:
+		ret = CVP_VOCPROC_COL_CAL;
+		break;
+	case CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE:
+		ret = CVP_VOCVOL_COL_CAL;
+		break;
+	case CVS_VOCSTRM_STATIC_COL_CAL_TYPE:
+		ret = CVS_VOCSTRM_COL_CAL;
+		break;
+	case VOICE_RTAC_INFO_CAL_TYPE:
+		ret = VOICE_RTAC_INFO_CAL;
+		break;
+	case VOICE_RTAC_APR_CAL_TYPE:
+		ret = VOICE_RTAC_APR_CAL;
+		break;
+	default:
+		pr_err("%s: Invalid cal type %d!\n", __func__, cal_type);
+	}
+	return ret;
+}
+
+static int voice_prepare_volume_boost(int32_t cal_type,
+					size_t data_size, void *data)
+{
+	return voc_deregister_vocproc_vol_table();
+}
+
+static int voice_enable_volume_boost(int32_t cal_type,
+				size_t data_size, void *data)
+{
+	return voc_register_vocproc_vol_table();
+}
+
+static int voice_alloc_cal(int32_t cal_type,
+			   size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+	int cal_version;
+
+	pr_debug("%s\n", __func__);
+
+	cal_version = cal_utils_get_cal_type_version(data);
+	common.is_per_vocoder_cal_enabled =
+			!!(cal_version & PER_VOCODER_CAL_BIT_MASK);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: Could not get cal index %d!\n",
+			__func__, cal_index);
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_alloc_cal(data_size, data,
+		common.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: Cal_utils_alloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int voice_dealloc_cal(int32_t cal_type,
+			     size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: Could not get cal index %d!\n",
+			__func__, cal_index);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_dealloc_cal(data_size, data,
+		common.cal_data[cal_index]);
+	if (ret < 0) {
+		pr_err("%s: Cal_utils_dealloc_block failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static int voice_set_cal(int32_t cal_type,
+			 size_t data_size, void *data)
+{
+	int ret = 0;
+	int cal_index;
+
+	pr_debug("%s\n", __func__);
+
+	cal_index = get_cal_type_index(cal_type);
+	if (cal_index < 0) {
+		pr_err("%s: Could not get cal index %d!\n",
+			__func__, cal_index);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = cal_utils_set_cal(data_size, data,
+		common.cal_data[cal_index], 0, NULL);
+	if (ret < 0) {
+		pr_err("%s: Cal_utils_set_cal failed, ret = %d, cal type = %d!\n",
+			__func__, ret, cal_type);
+
+		ret = -EINVAL;
+		goto done;
+	}
+done:
+	return ret;
+}
+
+static void voice_delete_cal_data(void)
+{
+	pr_debug("%s\n", __func__);
+
+	cal_utils_destroy_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data);
+}
+
+static int voice_init_cal_data(void)
+{
+	int ret = 0;
+	struct cal_type_info cal_type_info[] = {
+		{{CVP_VOC_RX_TOPOLOGY_CAL_TYPE,
+		{NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{CVP_VOC_TX_TOPOLOGY_CAL_TYPE,
+		{NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{CVP_VOCPROC_STATIC_CAL_TYPE,
+		{voice_alloc_cal, voice_dealloc_cal, NULL,
+		voice_set_cal, NULL, NULL} },
+		{NULL, voice_unmap_cal_memory,
+		cal_utils_match_buf_num} },
+
+		{{CVP_VOCPROC_DYNAMIC_CAL_TYPE,
+		{voice_alloc_cal, voice_dealloc_cal,
+		voice_prepare_volume_boost,
+		voice_set_cal, NULL,
+		voice_enable_volume_boost} },
+		{NULL, voice_unmap_cal_memory,
+		cal_utils_match_buf_num} },
+
+		{{CVP_VOCDEV_CFG_CAL_TYPE,
+		{voice_alloc_cal, voice_dealloc_cal, NULL,
+		voice_set_cal, NULL, NULL} },
+		{NULL, voice_unmap_cal_memory,
+		cal_utils_match_buf_num} },
+
+		{{CVP_VOCPROC_STATIC_COL_CAL_TYPE,
+		{NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{CVP_VOCPROC_DYNAMIC_COL_CAL_TYPE,
+		{NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{CVS_VOCSTRM_STATIC_CAL_TYPE,
+		{voice_alloc_cal, voice_dealloc_cal, NULL,
+		voice_set_cal, NULL, NULL} },
+		{NULL, voice_unmap_cal_memory,
+		cal_utils_match_buf_num} },
+
+		{{CVS_VOCSTRM_STATIC_COL_CAL_TYPE,
+		{NULL, NULL, NULL, voice_set_cal, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{VOICE_RTAC_INFO_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+
+		{{VOICE_RTAC_APR_CAL_TYPE,
+		{NULL, NULL, NULL, NULL, NULL, NULL} },
+		{NULL, NULL, cal_utils_match_buf_num} },
+	};
+
+	ret = cal_utils_create_cal_types(MAX_VOICE_CAL_TYPES, common.cal_data,
+		cal_type_info);
+	if (ret < 0) {
+		pr_err("%s: Could not create cal type!\n",
+			__func__);
+
+		ret = -EINVAL;
+		goto err;
+	}
+
+	return ret;
+err:
+	voice_delete_cal_data();
+	memset(&common, 0, sizeof(struct common_data));
+	return ret;
+}
+
+static int voice_send_set_sound_focus_cmd(struct voice_data *v,
+				 struct sound_focus_param soundFocusData)
+{
+	struct cvp_set_sound_focus_param_cmd_t cvp_set_sound_focus_param_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+	int i;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	if (v == NULL) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* send Sound Focus Params to cvp */
+	cvp_set_sound_focus_param_cmd.hdr.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					      APR_HDR_LEN(APR_HDR_SIZE),
+					      APR_PKT_VER);
+	cvp_set_sound_focus_param_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+			sizeof(cvp_set_sound_focus_param_cmd) - APR_HDR_SIZE);
+	cvp_set_sound_focus_param_cmd.hdr.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_set_sound_focus_param_cmd.hdr.dest_port = cvp_handle;
+	cvp_set_sound_focus_param_cmd.hdr.token = 0;
+	cvp_set_sound_focus_param_cmd.hdr.opcode =
+					 VSS_ISOUNDFOCUS_CMD_SET_SECTORS;
+
+	memset(&(cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param), 0xFF,
+		sizeof(struct vss_isoundfocus_cmd_set_sectors_t));
+	for (i = 0; i < MAX_SECTORS; i++) {
+		cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.
+			start_angles[i] = soundFocusData.start_angle[i];
+		cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.
+			enables[i] = soundFocusData.enable[i];
+		pr_debug("%s: start_angle[%d] = %d\n",
+			  __func__, i, soundFocusData.start_angle[i]);
+		pr_debug("%s: enable[%d] = %d\n",
+			  __func__, i, soundFocusData.enable[i]);
+	}
+	cvp_set_sound_focus_param_cmd.cvp_set_sound_focus_param.gain_step =
+					soundFocusData.gain_step;
+	pr_debug("%s: gain_step = %d\n", __func__, soundFocusData.gain_step);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+
+	ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_set_sound_focus_param_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error in sending APR command\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+	if (common.is_sound_focus_resp_success) {
+		ret = 0;
+	} else {
+		pr_err("%s: Error in setting sound focus params\n", __func__);
+
+		ret = -EINVAL;
+	}
+
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+int voc_set_sound_focus(struct sound_focus_param soundFocusData)
+{
+	struct voice_data *v = NULL;
+	int ret = -EINVAL;
+	struct voice_session_itr itr;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	mutex_lock(&common.common_lock);
+	voice_itr_init(&itr, ALL_SESSION_VSID);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			if (is_voc_state_active(v->voc_state) &&
+				(v->lch_mode != VOICE_LCH_START) &&
+				!v->disable_topology)
+				ret = voice_send_set_sound_focus_cmd(v,
+							soundFocusData);
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session\n", __func__);
+
+			ret = -EINVAL;
+			break;
+		}
+	}
+	mutex_unlock(&common.common_lock);
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int voice_send_get_sound_focus_cmd(struct voice_data *v,
+				struct sound_focus_param *soundFocusData)
+{
+	struct apr_hdr cvp_get_sound_focus_param_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+	int i;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	if (!v) {
+		pr_err("%s: v is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	cvp_handle = voice_get_cvp_handle(v);
+
+	/* send APR command to retrieve Sound Focus Params */
+	cvp_get_sound_focus_param_cmd.hdr_field =
+				APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					      APR_HDR_LEN(APR_HDR_SIZE),
+					      APR_PKT_VER);
+	cvp_get_sound_focus_param_cmd.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+			sizeof(cvp_get_sound_focus_param_cmd) - APR_HDR_SIZE);
+	cvp_get_sound_focus_param_cmd.src_port =
+				voice_get_idx_for_session(v->session_id);
+	cvp_get_sound_focus_param_cmd.dest_port = cvp_handle;
+	cvp_get_sound_focus_param_cmd.token = 0;
+	cvp_get_sound_focus_param_cmd.opcode = VSS_ISOUNDFOCUS_CMD_GET_SECTORS;
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp, (uint32_t *)&cvp_get_sound_focus_param_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error in sending APR command\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+	if (common.is_sound_focus_resp_success) {
+		for (i = 0; i < MAX_SECTORS; i++) {
+			soundFocusData->start_angle[i] =
+				common.soundFocusResponse.start_angles[i];
+			soundFocusData->enable[i] =
+				common.soundFocusResponse.enables[i];
+			pr_debug("%s: start_angle[%d] = %d\n",
+				  __func__, i, soundFocusData->start_angle[i]);
+			pr_debug("%s: enable[%d] = %d\n",
+				  __func__, i, soundFocusData->enable[i]);
+		}
+		soundFocusData->gain_step = common.soundFocusResponse.gain_step;
+		pr_debug("%s: gain_step = %d\n", __func__,
+			  soundFocusData->gain_step);
+
+		common.is_sound_focus_resp_success = false;
+		ret = 0;
+	} else {
+		pr_err("%s: Invalid payload received from CVD\n", __func__);
+
+		ret = -EINVAL;
+	}
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+int voc_get_sound_focus(struct sound_focus_param *soundFocusData)
+{
+	struct voice_data *v = NULL;
+	int ret = -EINVAL;
+	struct voice_session_itr itr;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	mutex_lock(&common.common_lock);
+	voice_itr_init(&itr, ALL_SESSION_VSID);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v) {
+			mutex_lock(&v->lock);
+			if (is_voc_state_active(v->voc_state) &&
+				(v->lch_mode != VOICE_LCH_START) &&
+				!v->disable_topology)
+				ret = voice_send_get_sound_focus_cmd(v,
+							soundFocusData);
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session\n", __func__);
+
+			ret =  -EINVAL;
+			break;
+		}
+	}
+	mutex_unlock(&common.common_lock);
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int is_source_tracking_shared_memomry_allocated(void)
+{
+	bool ret;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	if (common.source_tracking_sh_mem.sh_mem_block.client != NULL &&
+	    common.source_tracking_sh_mem.sh_mem_block.handle != NULL)
+		ret = true;
+	else
+		ret = false;
+
+	pr_debug("%s: Exit\n", __func__);
+
+	return ret;
+}
+
+static int voice_alloc_source_tracking_shared_memory(void)
+{
+	int ret = 0;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	ret = msm_audio_ion_alloc("source_tracking_sh_mem_block",
+		&(common.source_tracking_sh_mem.sh_mem_block.client),
+		&(common.source_tracking_sh_mem.sh_mem_block.handle),
+		BUFFER_BLOCK_SIZE,
+		&(common.source_tracking_sh_mem.sh_mem_block.phys),
+		(size_t *)&(common.source_tracking_sh_mem.sh_mem_block.size),
+		&(common.source_tracking_sh_mem.sh_mem_block.data));
+	if (ret < 0) {
+		pr_err("%s: audio ION alloc failed for sh_mem block, ret = %d\n",
+			__func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	memset((void *)(common.source_tracking_sh_mem.sh_mem_block.data), 0,
+		   common.source_tracking_sh_mem.sh_mem_block.size);
+
+	pr_debug("%s: sh_mem_block: phys:[%pK], data:[0x%pK], size:[%zd]\n",
+		 __func__,
+		&(common.source_tracking_sh_mem.sh_mem_block.phys),
+		(void *)(common.source_tracking_sh_mem.sh_mem_block.data),
+		(size_t)(common.source_tracking_sh_mem.sh_mem_block.size));
+
+	ret = msm_audio_ion_alloc("source_tracking_sh_mem_table",
+		&(common.source_tracking_sh_mem.sh_mem_table.client),
+		&(common.source_tracking_sh_mem.sh_mem_table.handle),
+		sizeof(struct vss_imemory_table_t),
+		&(common.source_tracking_sh_mem.sh_mem_table.phys),
+		(size_t *)&(common.source_tracking_sh_mem.sh_mem_table.size),
+		&(common.source_tracking_sh_mem.sh_mem_table.data));
+	if (ret < 0) {
+		pr_err("%s: audio ION alloc failed for sh_mem table, ret = %d\n",
+			__func__, ret);
+
+		ret = msm_audio_ion_free(
+			common.source_tracking_sh_mem.sh_mem_block.client,
+			common.source_tracking_sh_mem.sh_mem_block.handle);
+		common.source_tracking_sh_mem.sh_mem_block.client = NULL;
+		common.source_tracking_sh_mem.sh_mem_block.handle = NULL;
+		if (ret < 0)
+			pr_err("%s: Error:%d freeing memory\n", __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	memset((void *)(common.source_tracking_sh_mem.sh_mem_table.data), 0,
+		common.source_tracking_sh_mem.sh_mem_table.size);
+
+	pr_debug("%s sh_mem_table: phys:[%pK], data:[0x%pK], size:[%zd],\n",
+		 __func__,
+		&(common.source_tracking_sh_mem.sh_mem_table.phys),
+		(void *)(common.source_tracking_sh_mem.sh_mem_table.data),
+		(size_t)(common.source_tracking_sh_mem.sh_mem_table.size));
+
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int voice_alloc_and_map_source_tracking_shared_memory(
+						struct voice_data *v)
+{
+	int ret = 0;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	ret = voice_alloc_source_tracking_shared_memory();
+	if (ret) {
+		pr_err("%s: Failed to allocate shared memory %d\n",
+			__func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	ret = voice_map_memory_physical_cmd(v,
+			&(common.source_tracking_sh_mem.sh_mem_table),
+			common.source_tracking_sh_mem.sh_mem_block.phys,
+			common.source_tracking_sh_mem.sh_mem_block.size,
+			VOC_SOURCE_TRACKING_MEM_MAP_TOKEN);
+	if (ret) {
+		pr_err("%s: memory mapping failed %d\n",
+			__func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int voice_unmap_and_free_source_tracking_shared_memory(
+							struct voice_data *v)
+{
+	int ret = 0;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	if (common.source_tracking_sh_mem.mem_handle != 0) {
+		ret = voice_send_mvm_unmap_memory_physical_cmd(v,
+				common.source_tracking_sh_mem.mem_handle);
+		if (ret < 0) {
+			pr_err("%s: Memory_unmap failed err %d\n",
+				 __func__, ret);
+
+			ret = -EINVAL;
+			goto done;
+		}
+	}
+
+	if ((common.source_tracking_sh_mem.sh_mem_block.client == NULL) ||
+	    (common.source_tracking_sh_mem.sh_mem_block.handle == NULL))
+		goto done;
+
+	ret = msm_audio_ion_free(
+			common.source_tracking_sh_mem.sh_mem_block.client,
+			common.source_tracking_sh_mem.sh_mem_block.handle);
+	if (ret < 0) {
+		pr_err("%s: Error:%d freeing memory\n", __func__, ret);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+done:
+	common.source_tracking_sh_mem.mem_handle = 0;
+	common.source_tracking_sh_mem.sh_mem_block.client = NULL;
+	common.source_tracking_sh_mem.sh_mem_block.handle = NULL;
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+static int voice_send_get_source_tracking_cmd(struct voice_data *v,
+			struct source_tracking_param *sourceTrackingData)
+{
+	struct cvp_get_source_tracking_param_cmd_t st_cmd;
+	int ret = 0;
+	void *apr_cvp;
+	u16 cvp_handle;
+	int i;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	if (!v) {
+		pr_err("%s: v is NULL\n", __func__);
+		return -EINVAL;
+	}
+	apr_cvp = common.apr_q6_cvp;
+
+	if (!apr_cvp) {
+		pr_err("%s: apr_cvp is NULL.\n", __func__);
+		return -EINVAL;
+	}
+
+	cvp_handle = voice_get_cvp_handle(v);
+
+	if (!is_source_tracking_shared_memomry_allocated()) {
+		ret = voice_alloc_and_map_source_tracking_shared_memory(v);
+		if (ret) {
+			pr_err("%s: Fail in allocating/mapping shared memory\n",
+				__func__);
+
+			ret = -EINVAL;
+			goto done;
+		}
+	}
+	st_cmd.hdr.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+					     APR_HDR_LEN(APR_HDR_SIZE),
+					     APR_PKT_VER);
+	st_cmd.hdr.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+					   sizeof(st_cmd) - APR_HDR_SIZE);
+	st_cmd.hdr.src_port = voice_get_idx_for_session(v->session_id);
+	st_cmd.hdr.dest_port = cvp_handle;
+	st_cmd.hdr.token = 0;
+	st_cmd.hdr.opcode = VSS_ISOURCETRACK_CMD_GET_ACTIVITY;
+
+	st_cmd.cvp_get_source_tracking_param.mem_handle	=
+				 common.source_tracking_sh_mem.mem_handle;
+	st_cmd.cvp_get_source_tracking_param.mem_address_lsw =
+		lower_32_bits(common.source_tracking_sh_mem.sh_mem_block.phys);
+	st_cmd.cvp_get_source_tracking_param.mem_address_msw =
+		msm_audio_populate_upper_32_bits(common.source_tracking_sh_mem.
+					sh_mem_block.phys);
+	st_cmd.cvp_get_source_tracking_param.mem_size =
+		(uint32_t)common.source_tracking_sh_mem.sh_mem_block.size;
+	pr_debug("%s: mem_handle=0x%x, mem_address_lsw=0x%x, msw=0x%x, mem_size=%d\n",
+		 __func__,
+		 st_cmd.cvp_get_source_tracking_param.mem_handle,
+		 st_cmd.cvp_get_source_tracking_param.mem_address_lsw,
+		 st_cmd.cvp_get_source_tracking_param.mem_address_msw,
+		 (uint32_t)st_cmd.cvp_get_source_tracking_param.mem_size);
+
+	v->cvp_state = CMD_STATUS_FAIL;
+	v->async_err = 0;
+	ret = apr_send_pkt(apr_cvp,
+			   (uint32_t *) &st_cmd);
+	if (ret < 0) {
+		pr_err("%s: Error in sending APR command\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+	ret = wait_event_timeout(v->cvp_wait,
+				 (v->cvp_state == CMD_STATUS_SUCCESS),
+				 msecs_to_jiffies(TIMEOUT_MS));
+	if (!ret) {
+		pr_err("%s: wait_event timeout\n", __func__);
+
+		ret = -EINVAL;
+		goto done;
+	}
+
+	if (v->async_err > 0) {
+		pr_err("%s: DSP returned error[%s]\n",
+				__func__, adsp_err_get_err_str(
+				v->async_err));
+		ret = adsp_err_get_lnx_err_code(
+				v->async_err);
+		goto done;
+	}
+
+	if (common.is_source_tracking_resp_success) {
+		for (i = 0; i < MAX_SECTORS; i++) {
+			sourceTrackingData->vad[i] =
+				common.sourceTrackingResponse.voice_active[i];
+			pr_debug("%s: vad[%d] = %d\n",
+				  __func__, i, sourceTrackingData->vad[i]);
+		}
+		sourceTrackingData->doa_speech =
+				common.sourceTrackingResponse.talker_doa;
+		pr_debug("%s: doa_speech = %d\n",
+			  __func__, sourceTrackingData->doa_speech);
+
+		for (i = 0; i < MAX_NOISE_SOURCE_INDICATORS; i++) {
+			sourceTrackingData->doa_noise[i] =
+			 common.sourceTrackingResponse.interferer_doa[i];
+			pr_debug("%s: doa_noise[%d] = %d\n",
+			 __func__, i, sourceTrackingData->doa_noise[i]);
+		}
+		for (i = 0; i < MAX_POLAR_ACTIVITY_INDICATORS; i++) {
+			sourceTrackingData->polar_activity[i] =
+			 common.sourceTrackingResponse.sound_strength[i];
+			pr_debug("%s: polar_activity[%d] = %d\n",
+			 __func__, i, sourceTrackingData->polar_activity[i]);
+		}
+		common.is_source_tracking_resp_success = false;
+		ret = 0;
+	} else {
+		pr_err("%s: Error response received from CVD\n", __func__);
+
+		ret = -EINVAL;
+	}
+done:
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData)
+{
+	struct voice_data *v = NULL;
+	int ret = -EINVAL;
+	struct voice_session_itr itr;
+
+	pr_debug("%s: Enter\n", __func__);
+
+	mutex_lock(&common.common_lock);
+
+	voice_itr_init(&itr, ALL_SESSION_VSID);
+	while (voice_itr_get_next_session(&itr, &v)) {
+		if (v != NULL) {
+			mutex_lock(&v->lock);
+			if (is_voc_state_active(v->voc_state) &&
+				(v->lch_mode != VOICE_LCH_START) &&
+				!v->disable_topology)
+				ret = voice_send_get_source_tracking_cmd(v,
+							sourceTrackingData);
+			mutex_unlock(&v->lock);
+		} else {
+			pr_err("%s: invalid session\n", __func__);
+
+			break;
+		}
+	}
+
+	mutex_unlock(&common.common_lock);
+	pr_debug("%s: Exit, ret=%d\n", __func__, ret);
+
+	return ret;
+}
+
+int is_voc_initialized(void)
+{
+	return module_initialized;
+}
+
+static int __init voice_init(void)
+{
+	int rc = 0, i = 0;
+
+	memset(&common, 0, sizeof(struct common_data));
+
+	/* set default value */
+	common.default_mute_val = 0;  /* default is un-mute */
+	common.default_sample_val = 8000;
+	common.default_vol_step_val = 0;
+	common.default_vol_ramp_duration_ms = DEFAULT_VOLUME_RAMP_DURATION;
+	common.default_mute_ramp_duration_ms = DEFAULT_MUTE_RAMP_DURATION;
+	common.ec_ref_ext = false;
+	/* Initialize MVS info. */
+	common.mvs_info.network_type = VSS_NETWORK_ID_DEFAULT;
+
+	/* Initialize is low memory flag */
+	common.is_destroy_cvd = false;
+
+	/* Initialize CVD version */
+	strlcpy(common.cvd_version, CVD_VERSION_DEFAULT,
+		sizeof(common.cvd_version));
+	/* Initialize Per-Vocoder Calibration flag */
+	common.is_per_vocoder_cal_enabled = false;
+
+	mutex_init(&common.common_lock);
+
+	/* Initialize session id with vsid */
+	init_session_id();
+
+	for (i = 0; i < MAX_VOC_SESSIONS; i++) {
+
+		/* initialize dev_rx and dev_tx */
+		common.voice[i].dev_rx.dev_mute =  common.default_mute_val;
+		common.voice[i].dev_tx.dev_mute =  common.default_mute_val;
+		common.voice[i].dev_rx.volume_step_value =
+					common.default_vol_step_val;
+		common.voice[i].dev_rx.volume_ramp_duration_ms =
+					common.default_vol_ramp_duration_ms;
+		common.voice[i].dev_rx.dev_mute_ramp_duration_ms =
+					common.default_mute_ramp_duration_ms;
+		common.voice[i].dev_tx.dev_mute_ramp_duration_ms =
+					common.default_mute_ramp_duration_ms;
+		common.voice[i].stream_rx.stream_mute = common.default_mute_val;
+		common.voice[i].stream_tx.stream_mute = common.default_mute_val;
+
+		common.voice[i].dev_tx.port_id = 0x100B;
+		common.voice[i].dev_rx.port_id = 0x100A;
+		common.voice[i].dev_tx.dev_id = 0;
+		common.voice[i].dev_rx.dev_id = 0;
+		common.voice[i].dev_rx.no_of_channels = 0;
+		common.voice[i].dev_tx.no_of_channels = 0;
+		common.voice[i].sidetone_gain = 0x512;
+		common.voice[i].dtmf_rx_detect_en = 0;
+		common.voice[i].lch_mode = 0;
+		common.voice[i].disable_topology = false;
+
+		common.voice[i].voc_state = VOC_INIT;
+
+		init_waitqueue_head(&common.voice[i].mvm_wait);
+		init_waitqueue_head(&common.voice[i].cvs_wait);
+		init_waitqueue_head(&common.voice[i].cvp_wait);
+
+		mutex_init(&common.voice[i].lock);
+	}
+
+	if (voice_init_cal_data())
+		pr_err("%s: Could not init cal data!\n", __func__);
+
+	if (rc == 0)
+		module_initialized = true;
+
+	pr_debug("%s: rc=%d\n", __func__, rc);
+	return rc;
+}
+
+device_initcall(voice_init);
+
+static void __exit voice_exit(void)
+{
+	voice_delete_cal_data();
+	free_cal_map_table();
+}
+
+__exitcall(voice_exit);
diff --git a/sound/soc/msm/qdsp6v2/q6voice.h b/sound/soc/msm/qdsp6v2/q6voice.h
new file mode 100644
index 0000000..aa58574
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/q6voice.h
@@ -0,0 +1,1784 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#ifndef __QDSP6VOICE_H__
+#define __QDSP6VOICE_H__
+
+#include <linux/qdsp6v2/apr.h>
+#include <linux/qdsp6v2/rtac.h>
+#include <linux/msm_ion.h>
+#include <sound/voice_params.h>
+
+#define MAX_VOC_PKT_SIZE 642
+#define SESSION_NAME_LEN 20
+#define NUM_OF_MEMORY_BLOCKS 1
+#define NUM_OF_BUFFERS 2
+/*
+ * BUFFER BLOCK SIZE based on
+ * the supported page size
+ */
+#define BUFFER_BLOCK_SIZE       4096
+
+#define MAX_COL_INFO_SIZE	324
+
+#define VOC_REC_UPLINK		0x00
+#define VOC_REC_DOWNLINK	0x01
+#define VOC_REC_BOTH		0x02
+
+#define VSS_IVERSION_CMD_GET                 0x00011378
+#define VSS_IVERSION_RSP_GET                 0x00011379
+#define CVD_VERSION_STRING_MAX_SIZE          31
+#define CVD_VERSION_DEFAULT                  ""
+#define CVD_VERSION_0_0                      "0.0"
+#define CVD_VERSION_2_1                      "2.1"
+#define CVD_VERSION_2_2                      "2.2"
+#define CVD_VERSION_2_3                      "2.3"
+
+#define CVD_INT_VERSION_DEFAULT              0
+#define CVD_INT_VERSION_0_0                  1
+#define CVD_INT_VERSION_2_1                  2
+#define CVD_INT_VERSION_2_2                  3
+#define CVD_INT_VERSION_2_3                  4
+#define CVD_INT_VERSION_LAST                 CVD_INT_VERSION_2_3
+#define CVD_INT_VERSION_MAX                  (CVD_INT_VERSION_LAST + 1)
+
+struct cvd_version_table {
+	char cvd_ver[CVD_VERSION_STRING_MAX_SIZE];
+	int cvd_ver_int;
+};
+
+int voc_get_cvd_version(char *cvd_version);
+
+/* Payload structure for the VSS_IVERSION_RSP_GET command response */
+struct vss_iversion_rsp_get_t {
+	char version[CVD_VERSION_STRING_MAX_SIZE];
+	/* NULL-terminated version string */
+};
+
+enum {
+	CVP_VOC_RX_TOPOLOGY_CAL = 0,
+	CVP_VOC_TX_TOPOLOGY_CAL,
+	CVP_VOCPROC_CAL,
+	CVP_VOCVOL_CAL,
+	CVP_VOCDEV_CFG_CAL,
+	CVP_VOCPROC_COL_CAL,
+	CVP_VOCVOL_COL_CAL,
+	CVS_VOCSTRM_CAL,
+	CVS_VOCSTRM_COL_CAL,
+	VOICE_RTAC_INFO_CAL,
+	VOICE_RTAC_APR_CAL,
+	MAX_VOICE_CAL_TYPES
+};
+
+struct voice_header {
+	uint32_t id;
+	uint32_t data_len;
+};
+
+struct voice_init {
+	struct voice_header hdr;
+	void *cb_handle;
+};
+
+/* Stream information payload structure */
+struct stream_data {
+	uint32_t stream_mute;
+	uint32_t stream_mute_ramp_duration_ms;
+};
+
+/* Device information payload structure */
+struct device_data {
+	uint32_t dev_mute;
+	uint32_t sample;
+	uint32_t enabled;
+	uint32_t dev_id;
+	uint32_t port_id;
+	uint32_t volume_step_value;
+	uint32_t volume_ramp_duration_ms;
+	uint32_t dev_mute_ramp_duration_ms;
+	uint32_t no_of_channels;
+};
+
+struct voice_dev_route_state {
+	u16 rx_route_flag;
+	u16 tx_route_flag;
+};
+
+struct voice_rec_route_state {
+	u16 ul_flag;
+	u16 dl_flag;
+};
+
+enum {
+	VOC_INIT = 0,
+	VOC_RUN,
+	VOC_CHANGE,
+	VOC_RELEASE,
+	VOC_ERROR,
+	VOC_STANDBY,
+};
+
+struct mem_buffer {
+	dma_addr_t		phys;
+	void			*data;
+	uint32_t		size; /* size of buffer */
+};
+
+struct share_mem_buf {
+	struct ion_handle	*handle;
+	struct ion_client	*client;
+	struct mem_buffer	buf[NUM_OF_BUFFERS];
+};
+
+struct mem_map_table {
+	dma_addr_t		phys;
+	void			*data;
+	size_t			size; /* size of buffer */
+	struct ion_handle	*handle;
+	struct ion_client	*client;
+};
+
+/* Common */
+#define VSS_ICOMMON_CMD_SET_UI_PROPERTY 0x00011103
+/* Set a UI property */
+#define VSS_ICOMMON_CMD_MAP_MEMORY   0x00011025
+#define VSS_ICOMMON_CMD_UNMAP_MEMORY 0x00011026
+/* General shared memory; byte-accessible, 4 kB-aligned. */
+#define VSS_ICOMMON_MAP_MEMORY_SHMEM8_4K_POOL  3
+
+struct vss_icommon_cmd_map_memory_t {
+	uint32_t phys_addr;
+	/* Physical address of a memory region; must be at least
+	 *  4 kB aligned.
+	 */
+
+	uint32_t mem_size;
+	/* Number of bytes in the region; should be a multiple of 32. */
+
+	uint16_t mem_pool_id;
+	/* Type of memory being provided. The memory ID implicitly defines
+	 *  the characteristics of the memory. The characteristics might include
+	 *  alignment type, permissions, etc.
+	 * Memory pool ID. Possible values:
+	 * 3 -- VSS_ICOMMON_MEM_TYPE_SHMEM8_4K_POOL.
+	 */
+} __packed;
+
+struct vss_icommon_cmd_unmap_memory_t {
+	uint32_t phys_addr;
+	/* Physical address of a memory region; must be at least
+	 *  4 kB aligned.
+	 */
+} __packed;
+
+struct vss_map_memory_cmd {
+	struct apr_hdr hdr;
+	struct vss_icommon_cmd_map_memory_t vss_map_mem;
+} __packed;
+
+struct vss_unmap_memory_cmd {
+	struct apr_hdr hdr;
+	struct vss_icommon_cmd_unmap_memory_t vss_unmap_mem;
+} __packed;
+
+/* TO MVM commands */
+#define VSS_IMVM_CMD_CREATE_PASSIVE_CONTROL_SESSION	0x000110FF
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL	0x00011327
+/*
+ * VSS_IMVM_CMD_SET_POLICY_DUAL_CONTROL
+ * Description: This command is required to let MVM know
+ * who is in control of session.
+ * Payload: Defined by vss_imvm_cmd_set_policy_dual_control_t.
+ * Result: Wait for APRV2_IBASIC_RSP_RESULT response.
+ */
+
+#define VSS_IMVM_CMD_CREATE_FULL_CONTROL_SESSION	0x000110FE
+/* Create a new full control MVM session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION		0x0001003C
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_ATTACH_STREAM			0x0001123C
+/* Attach a stream to the MVM. */
+
+#define VSS_IMVM_CMD_DETACH_STREAM			0x0001123D
+/* Detach a stream from the MVM. */
+
+#define VSS_IMVM_CMD_ATTACH_VOCPROC		       0x0001123E
+/* Attach a vocproc to the MVM. The MVM will symmetrically connect this vocproc
+ * to all the streams currently attached to it.
+ */
+
+#define VSS_IMVM_CMD_DETACH_VOCPROC			0x0001123F
+/* Detach a vocproc from the MVM. The MVM will symmetrically disconnect this
+ * vocproc from all the streams to which it is currently attached.
+ */
+
+#define VSS_IMVM_CMD_START_VOICE			0x00011190
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_STANDBY_VOICE                       0x00011191
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_STOP_VOICE				0x00011192
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IMVM_CMD_PAUSE_VOICE			0x0001137D
+/* No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_ATTACH_VOCPROC			0x000110F8
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_DETACH_VOCPROC			0x000110F9
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+
+#define VSS_ISTREAM_CMD_SET_TTY_MODE			0x00011196
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ICOMMON_CMD_SET_NETWORK			0x0001119C
+/* Set the network type. */
+
+#define VSS_ICOMMON_CMD_SET_VOICE_TIMING		0x000111E0
+/* Set the voice timing parameters. */
+
+#define VSS_IMEMORY_CMD_MAP_PHYSICAL			0x00011334
+#define VSS_IMEMORY_RSP_MAP				0x00011336
+#define VSS_IMEMORY_CMD_UNMAP				0x00011337
+#define VSS_IMVM_CMD_SET_CAL_NETWORK			0x0001137A
+#define VSS_IMVM_CMD_SET_CAL_MEDIA_TYPE		0x0001137B
+#define VSS_IHDVOICE_CMD_ENABLE				0x000130A2
+#define VSS_IHDVOICE_CMD_DISABLE			0x000130A3
+
+enum msm_audio_voc_rate {
+		VOC_0_RATE, /* Blank frame */
+		VOC_8_RATE, /* 1/8 rate    */
+		VOC_4_RATE, /* 1/4 rate    */
+		VOC_2_RATE, /* 1/2 rate    */
+		VOC_1_RATE,  /* Full rate   */
+		VOC_8_RATE_NC  /* Noncritical 1/8 rate   */
+};
+
+struct vss_istream_cmd_set_tty_mode_t {
+	uint32_t mode;
+	/**<
+	 * TTY mode.
+	 *
+	 * 0 : TTY disabled
+	 * 1 : HCO
+	 * 2 : VCO
+	 * 3 : FULL
+	 */
+} __packed;
+
+struct vss_istream_cmd_attach_vocproc_t {
+	uint16_t handle;
+	/**< Handle of vocproc being attached. */
+} __packed;
+
+struct vss_istream_cmd_detach_vocproc_t {
+	uint16_t handle;
+	/**< Handle of vocproc being detached. */
+} __packed;
+
+struct vss_imvm_cmd_attach_stream_t {
+	uint16_t handle;
+	/* The stream handle to attach. */
+} __packed;
+
+struct vss_imvm_cmd_detach_stream_t {
+	uint16_t handle;
+	/* The stream handle to detach. */
+} __packed;
+
+struct vss_icommon_cmd_set_network_t {
+	uint32_t network_id;
+	/* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+} __packed;
+
+struct vss_icommon_cmd_set_voice_timing_t {
+	uint16_t mode;
+	/*
+	 * The vocoder frame synchronization mode.
+	 *
+	 * 0 : No frame sync.
+	 * 1 : Hard VFR (20ms Vocoder Frame Reference interrupt).
+	 */
+	uint16_t enc_offset;
+	/*
+	 * The offset in microseconds from the VFR to deliver a Tx vocoder
+	 * packet. The offset should be less than 20000us.
+	 */
+	uint16_t dec_req_offset;
+	/*
+	 * The offset in microseconds from the VFR to request for an Rx vocoder
+	 * packet. The offset should be less than 20000us.
+	 */
+	uint16_t dec_offset;
+	/*
+	 * The offset in microseconds from the VFR to indicate the deadline to
+	 * receive an Rx vocoder packet. The offset should be less than 20000us.
+	 * Rx vocoder packets received after this deadline are not guaranteed to
+	 * be processed.
+	 */
+} __packed;
+
+struct vss_imvm_cmd_create_control_session_t {
+	char name[SESSION_NAME_LEN];
+	/*
+	 * A variable-sized stream name.
+	 *
+	 * The stream name size is the payload size minus the size of the other
+	 * fields.
+	 */
+} __packed;
+
+
+struct vss_imvm_cmd_set_policy_dual_control_t {
+	bool enable_flag;
+	/* Set to TRUE to enable modem state machine control */
+} __packed;
+
+struct mvm_attach_vocproc_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_attach_vocproc_t mvm_attach_cvp_handle;
+} __packed;
+
+struct mvm_detach_vocproc_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_detach_vocproc_t mvm_detach_cvp_handle;
+} __packed;
+
+struct mvm_create_ctl_session_cmd {
+	struct apr_hdr hdr;
+	struct vss_imvm_cmd_create_control_session_t mvm_session;
+} __packed;
+
+struct mvm_modem_dual_control_session_cmd {
+	struct apr_hdr hdr;
+	struct vss_imvm_cmd_set_policy_dual_control_t voice_ctl;
+} __packed;
+
+struct mvm_set_tty_mode_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_set_tty_mode_t tty_mode;
+} __packed;
+
+struct mvm_attach_stream_cmd {
+	struct apr_hdr hdr;
+	struct vss_imvm_cmd_attach_stream_t attach_stream;
+} __packed;
+
+struct mvm_detach_stream_cmd {
+	struct apr_hdr hdr;
+	struct vss_imvm_cmd_detach_stream_t detach_stream;
+} __packed;
+
+struct mvm_set_network_cmd {
+	struct apr_hdr hdr;
+	struct vss_icommon_cmd_set_network_t network;
+} __packed;
+
+struct mvm_set_voice_timing_cmd {
+	struct apr_hdr hdr;
+	struct vss_icommon_cmd_set_voice_timing_t timing;
+} __packed;
+
+struct mvm_set_hd_enable_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct vss_imemory_table_descriptor_t {
+	uint32_t mem_address_lsw;
+	uint32_t mem_address_msw;
+	/*
+	 * Base physical address of the table. The address must be aligned
+	 * to LCM( cache_line_size, page_align, max_data_width ), where the
+	 * attributes are specified in #VSS_IMEMORY_CMD_MAP_PHYSICAL, and
+	 * LCM = Least Common Multiple. The table at the address must have
+	 * the format specified by #vss_imemory_table_t.
+	 */
+	uint32_t mem_size;
+	/* Size in bytes of the table. */
+} __packed;
+
+struct vss_imemory_block_t {
+	uint64_t mem_address;
+	/*
+	 * Base address of the memory block. The address is virtual for virtual
+	 * memory and physical for physical memory. The address must be aligned
+	 * to LCM( cache_line_size, page_align, max_data_width ), where the
+	 * attributes are specified in VSS_IMEMORY_CMD_MAP_VIRTUAL or
+	 * VSS_IMEMORY_CMD_MAP_PHYSICAL, and LCM = Least Common Multiple.
+	 */
+	uint32_t mem_size;
+	/*
+	 * Size in bytes of the memory block. The size must be multiple of
+	 * page_align, where page_align is specified in
+	 * VSS_IMEMORY_CMD_MAP_VIRTUAL or #VSS_IMEMORY_CMD_MAP_PHYSICAL.
+	 */
+} __packed;
+
+struct vss_imemory_table_t {
+	struct vss_imemory_table_descriptor_t next_table_descriptor;
+	/*
+	 * Specifies the next table. If there is no next table,
+	 * set the size of the table to 0 and the table address is ignored.
+	 */
+	struct vss_imemory_block_t blocks[NUM_OF_MEMORY_BLOCKS];
+	/* Specifies one ore more memory blocks. */
+} __packed;
+
+struct vss_imemory_cmd_map_physical_t {
+	struct apr_hdr hdr;
+	struct vss_imemory_table_descriptor_t table_descriptor;
+	bool is_cached;
+	/*
+	 * Indicates cached or uncached memory. Supported values:
+	 * TRUE - Cached.
+	 */
+	uint16_t cache_line_size;
+	/* Cache line size in bytes. Supported values: 128 */
+	uint32_t access_mask;
+	/*
+	 * CVD's access permission to the memory while it is mapped.
+	 * Supported values:
+	 * bit 0 - If set, the memory is readable.
+	 * bit 1 - If set, the memory is writable.
+	 */
+	uint32_t page_align;
+	/* Page frame alignment in bytes. Supported values: 4096 */
+	uint8_t min_data_width;
+	/*
+	 * Minimum native data type width in bits that can be accessed.
+	 * Supported values: 8
+	 */
+	uint8_t max_data_width;
+	/*
+	 * Maximum native data type width in bits that can be accessed.
+	 * Supported values: 64
+	 */
+} __packed;
+
+struct vss_imvm_cmd_set_cal_network_t {
+	struct apr_hdr hdr;
+	uint32_t network_id;
+} __packed;
+
+struct vss_imvm_cmd_set_cal_media_type_t {
+	struct apr_hdr hdr;
+	uint32_t media_id;
+} __packed;
+
+struct vss_imemory_cmd_unmap_t {
+	struct apr_hdr hdr;
+	uint32_t mem_handle;
+} __packed;
+
+/* TO CVS commands */
+#define VSS_ISTREAM_CMD_CREATE_PASSIVE_CONTROL_SESSION	0x00011140
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_ISTREAM_CMD_CREATE_FULL_CONTROL_SESSION	0x000110F7
+/* Create a new full control stream session. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION		0x0001003C
+
+/*
+ * This command changes the mute setting. The new mute setting will
+ * be applied over the specified ramp duration.
+ */
+#define VSS_IVOLUME_CMD_MUTE_V2				0x0001138B
+
+#define VSS_ISTREAM_CMD_REGISTER_CALIBRATION_DATA_V2    0x00011369
+
+#define VSS_ISTREAM_CMD_DEREGISTER_CALIBRATION_DATA     0x0001127A
+
+#define VSS_ISTREAM_CMD_REGISTER_STATIC_CALIBRATION_DATA        0x0001307D
+#define VSS_ISTREAM_CMD_DEREGISTER_STATIC_CALIBRATION_DATA      0x0001307E
+
+#define VSS_ISTREAM_CMD_SET_MEDIA_TYPE			0x00011186
+/* Set media type on the stream. */
+
+#define VSS_ISTREAM_EVT_SEND_ENC_BUFFER			0x00011015
+/* Event sent by the stream to its client to provide an encoded packet. */
+
+#define VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER		0x00011017
+/* Event sent by the stream to its client requesting for a decoder packet.
+ * The client should respond with a VSS_ISTREAM_EVT_SEND_DEC_BUFFER event.
+ */
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_REQUEST	0x0001136E
+
+#define VSS_ISTREAM_EVT_SEND_DEC_BUFFER			0x00011016
+/* Event sent by the client to the stream in response to a
+ * VSS_ISTREAM_EVT_REQUEST_DEC_BUFFER event, providing a decoder packet.
+ */
+
+#define VSS_ISTREAM_CMD_VOC_AMR_SET_ENC_RATE		0x0001113E
+/* Set AMR encoder rate. */
+
+#define VSS_ISTREAM_CMD_VOC_AMRWB_SET_ENC_RATE		0x0001113F
+/* Set AMR-WB encoder rate. */
+
+#define VSS_ISTREAM_CMD_CDMA_SET_ENC_MINMAX_RATE	0x00011019
+/* Set encoder minimum and maximum rate. */
+
+#define VSS_ISTREAM_CMD_SET_ENC_DTX_MODE		0x0001101D
+/* Set encoder DTX mode. */
+
+#define MODULE_ID_VOICE_MODULE_ST			0x00010EE3
+#define VOICE_PARAM_MOD_ENABLE				0x00010E00
+#define MOD_ENABLE_PARAM_LEN				4
+
+#define VSS_IPLAYBACK_CMD_START				0x000112BD
+/* Start in-call music delivery on the Tx voice path. */
+
+#define VSS_IPLAYBACK_CMD_STOP				0x00011239
+/* Stop the in-call music delivery on the Tx voice path. */
+
+#define VSS_IPLAYBACK_PORT_ID_DEFAULT			0x0000FFFF
+/* Default AFE port ID. */
+
+#define VSS_IPLAYBACK_PORT_ID_VOICE			0x00008005
+/* AFE port ID for VOICE 1. */
+
+#define VSS_IPLAYBACK_PORT_ID_VOICE2			0x00008002
+/* AFE port ID for VOICE 2. */
+
+#define VSS_IRECORD_CMD_START				0x000112BE
+/* Start in-call conversation recording. */
+#define VSS_IRECORD_CMD_STOP				0x00011237
+/* Stop in-call conversation recording. */
+
+#define VSS_IRECORD_PORT_ID_DEFAULT			0x0000FFFF
+/* Default AFE port ID. */
+
+#define VSS_IRECORD_TAP_POINT_NONE			0x00010F78
+/* Indicates no tapping for specified path. */
+
+#define VSS_IRECORD_TAP_POINT_STREAM_END		0x00010F79
+/* Indicates that specified path should be tapped at the end of the stream. */
+
+#define VSS_IRECORD_MODE_TX_RX_STEREO			0x00010F7A
+/* Select Tx on left channel and Rx on right channel. */
+
+#define VSS_IRECORD_MODE_TX_RX_MIXING			0x00010F7B
+/* Select mixed Tx and Rx paths. */
+
+#define VSS_ISTREAM_EVT_NOT_READY			0x000110FD
+
+#define VSS_ISTREAM_EVT_READY				0x000110FC
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_DEC_BUFFER_READY	0x0001136F
+/*notify dsp that decoder buffer is ready*/
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_READY	0x0001136C
+/*dsp notifying client that encoder buffer is ready*/
+
+#define VSS_ISTREAM_EVT_OOB_NOTIFY_ENC_BUFFER_CONSUMED	0x0001136D
+/*notify dsp that encoder buffer is consumed*/
+
+#define VSS_ISTREAM_CMD_SET_OOB_PACKET_EXCHANGE_CONFIG	0x0001136B
+
+#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_INBAND	0
+/* In-band packet exchange mode. */
+
+#define VSS_ISTREAM_PACKET_EXCHANGE_MODE_OUT_OF_BAND	1
+/* Out-of-band packet exchange mode. */
+
+#define VSS_ISTREAM_CMD_SET_PACKET_EXCHANGE_MODE	0x0001136A
+
+struct vss_iplayback_cmd_start_t {
+	uint16_t port_id;
+	/*
+	 * AFE Port ID from which the audio samples are available.
+	 * To use the default AFE pseudo port (0x8005), set this value to
+	 * #VSS_IPLAYBACK_PORT_ID_DEFAULT.
+	 */
+}  __packed;
+
+struct vss_irecord_cmd_start_t {
+	uint32_t rx_tap_point;
+	/* Tap point to use on the Rx path. Supported values are:
+	 * VSS_IRECORD_TAP_POINT_NONE : Do not record Rx path.
+	 * VSS_IRECORD_TAP_POINT_STREAM_END : Rx tap point is at the end of
+	 * the stream.
+	 */
+	uint32_t tx_tap_point;
+	/* Tap point to use on the Tx path. Supported values are:
+	 * VSS_IRECORD_TAP_POINT_NONE : Do not record tx path.
+	 * VSS_IRECORD_TAP_POINT_STREAM_END : Tx tap point is at the end of
+	 * the stream.
+	 */
+	uint16_t port_id;
+	/* AFE Port ID to which the conversation recording stream needs to be
+	 * sent. Set this to #VSS_IRECORD_PORT_ID_DEFAULT to use default AFE
+	 * pseudo ports (0x8003 for Rx and 0x8004 for Tx).
+	 */
+	uint32_t mode;
+	/* Recording Mode. The mode parameter value is ignored if the port_id
+	 * is set to #VSS_IRECORD_PORT_ID_DEFAULT.
+	 * The supported values:
+	 * #VSS_IRECORD_MODE_TX_RX_STEREO
+	 * #VSS_IRECORD_MODE_TX_RX_MIXING
+	 */
+} __packed;
+
+struct vss_istream_cmd_create_passive_control_session_t {
+	char name[SESSION_NAME_LEN];
+	/**<
+	 * A variable-sized stream name.
+	 *
+	 * The stream name size is the payload size minus the size of the other
+	 * fields.
+	 */
+} __packed;
+
+#define VSS_IVOLUME_DIRECTION_TX	0
+#define VSS_IVOLUME_DIRECTION_RX	1
+
+#define VSS_IVOLUME_MUTE_OFF		0
+#define VSS_IVOLUME_MUTE_ON		1
+
+#define DEFAULT_MUTE_RAMP_DURATION	500
+#define DEFAULT_VOLUME_RAMP_DURATION	20
+#define MAX_RAMP_DURATION		5000
+
+struct vss_ivolume_cmd_mute_v2_t {
+	uint16_t direction;
+	/*
+	 * The direction field sets the direction to apply the mute command.
+	 * The Supported values:
+	 * VSS_IVOLUME_DIRECTION_TX
+	 * VSS_IVOLUME_DIRECTION_RX
+	 */
+	uint16_t mute_flag;
+	/*
+	 * Turn mute on or off. The Supported values:
+	 * VSS_IVOLUME_MUTE_OFF
+	 * VSS_IVOLUME_MUTE_ON
+	 */
+	uint16_t ramp_duration_ms;
+	/*
+	 * Mute change ramp duration in milliseconds.
+	 * The Supported values: 0 to 5000.
+	 */
+} __packed;
+
+struct vss_istream_cmd_create_full_control_session_t {
+	uint16_t direction;
+	/*
+	 * Stream direction.
+	 *
+	 * 0 : TX only
+	 * 1 : RX only
+	 * 2 : TX and RX
+	 * 3 : TX and RX loopback
+	 */
+	uint32_t enc_media_type;
+	/* Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+	uint32_t dec_media_type;
+	/* Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+	uint32_t network_id;
+	/* Network ID. (Refer to VSS_NETWORK_ID_XXX). */
+	char name[SESSION_NAME_LEN];
+	/*
+	 * A variable-sized stream name.
+	 *
+	 * The stream name size is the payload size minus the size of the other
+	 * fields.
+	 */
+} __packed;
+
+struct vss_istream_cmd_set_media_type_t {
+	uint32_t rx_media_id;
+	/* Set the Rx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+	uint32_t tx_media_id;
+	/* Set the Tx vocoder type. (Refer to VSS_MEDIA_ID_XXX). */
+} __packed;
+
+struct vss_istream_evt_send_enc_buffer_t {
+	uint32_t media_id;
+      /* Media ID of the packet. */
+	uint8_t packet_data[MAX_VOC_PKT_SIZE];
+      /* Packet data buffer. */
+} __packed;
+
+struct vss_istream_evt_send_dec_buffer_t {
+	uint32_t media_id;
+      /* Media ID of the packet. */
+	uint8_t packet_data[MAX_VOC_PKT_SIZE];
+      /* Packet data. */
+} __packed;
+
+struct vss_istream_cmd_voc_amr_set_enc_rate_t {
+	uint32_t mode;
+	/* Set the AMR encoder rate.
+	 *
+	 * 0x00000000 : 4.75 kbps
+	 * 0x00000001 : 5.15 kbps
+	 * 0x00000002 : 5.90 kbps
+	 * 0x00000003 : 6.70 kbps
+	 * 0x00000004 : 7.40 kbps
+	 * 0x00000005 : 7.95 kbps
+	 * 0x00000006 : 10.2 kbps
+	 * 0x00000007 : 12.2 kbps
+	 */
+} __packed;
+
+struct vss_istream_cmd_voc_amrwb_set_enc_rate_t {
+	uint32_t mode;
+	/* Set the AMR-WB encoder rate.
+	 *
+	 * 0x00000000 :  6.60 kbps
+	 * 0x00000001 :  8.85 kbps
+	 * 0x00000002 : 12.65 kbps
+	 * 0x00000003 : 14.25 kbps
+	 * 0x00000004 : 15.85 kbps
+	 * 0x00000005 : 18.25 kbps
+	 * 0x00000006 : 19.85 kbps
+	 * 0x00000007 : 23.05 kbps
+	 * 0x00000008 : 23.85 kbps
+	 */
+} __packed;
+
+struct vss_istream_cmd_cdma_set_enc_minmax_rate_t {
+	uint16_t min_rate;
+	/* Set the lower bound encoder rate.
+	 *
+	 * 0x0000 : Blank frame
+	 * 0x0001 : Eighth rate
+	 * 0x0002 : Quarter rate
+	 * 0x0003 : Half rate
+	 * 0x0004 : Full rate
+	 */
+	uint16_t max_rate;
+	/* Set the upper bound encoder rate.
+	 *
+	 * 0x0000 : Blank frame
+	 * 0x0001 : Eighth rate
+	 * 0x0002 : Quarter rate
+	 * 0x0003 : Half rate
+	 * 0x0004 : Full rate
+	 */
+} __packed;
+
+struct vss_istream_cmd_set_enc_dtx_mode_t {
+	uint32_t enable;
+	/* Toggle DTX on or off.
+	 *
+	 * 0 : Disables DTX
+	 * 1 : Enables DTX
+	 */
+} __packed;
+
+struct vss_istream_cmd_register_calibration_data_v2_t {
+	uint32_t cal_mem_handle;
+	/* Handle to the shared memory that holds the calibration data. */
+	uint32_t cal_mem_address_lsw;
+	uint32_t cal_mem_address_msw;
+	/* Location of calibration data. */
+	uint32_t cal_mem_size;
+	/* Size of the calibration data in bytes. */
+	uint8_t column_info[MAX_COL_INFO_SIZE];
+	/*
+	 * Column info contains the number of columns and the array of columns
+	 * in the calibration table. The order in which the columns are provided
+	 * here must match the order in which they exist in the calibration
+	 * table provided.
+	 */
+} __packed;
+
+struct vss_icommon_cmd_set_ui_property_enable_t {
+	uint32_t module_id;
+	/* Unique ID of the module. */
+	uint32_t param_id;
+	/* Unique ID of the parameter. */
+	uint16_t param_size;
+	/* Size of the parameter in bytes: MOD_ENABLE_PARAM_LEN */
+	uint16_t reserved;
+	/* Reserved; set to 0. */
+	uint16_t enable;
+	uint16_t reserved_field;
+	/* Reserved, set to 0. */
+};
+
+/*
+ * Event sent by the stream to the client that enables Rx DTMF
+ * detection whenever DTMF is detected in the Rx path.
+ *
+ * The DTMF detection feature can only be used to detect DTMF
+ * frequencies as listed in the vss_istream_evt_rx_dtmf_detected_t
+ * structure.
+ */
+
+#define VSS_ISTREAM_EVT_RX_DTMF_DETECTED 0x0001101A
+
+struct vss_istream_cmd_set_rx_dtmf_detection {
+	/*
+	 * Enables/disables Rx DTMF detection
+	 *
+	 * Possible values are
+	 * 0 - disable
+	 * 1 - enable
+	 *
+	 */
+	uint32_t enable;
+};
+
+#define VSS_ISTREAM_CMD_SET_RX_DTMF_DETECTION 0x00011027
+
+struct vss_istream_evt_rx_dtmf_detected {
+	uint16_t low_freq;
+	/*
+	 * Detected low frequency. Possible values:
+	 * 697 Hz
+	 * 770 Hz
+	 * 852 Hz
+	 * 941 Hz
+	 */
+	uint16_t high_freq;
+	/*
+	 * Detected high frequency. Possible values:
+	 * 1209 Hz
+	 * 1336 Hz
+	 * 1477 Hz
+	 * 1633 Hz
+	 */
+};
+
+struct cvs_set_rx_dtmf_detection_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_set_rx_dtmf_detection cvs_dtmf_det;
+} __packed;
+
+
+struct cvs_create_passive_ctl_session_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_create_passive_control_session_t cvs_session;
+} __packed;
+
+struct cvs_create_full_ctl_session_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_create_full_control_session_t cvs_session;
+} __packed;
+
+struct cvs_destroy_session_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvs_set_mute_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivolume_cmd_mute_v2_t cvs_set_mute;
+} __packed;
+
+struct cvs_set_media_type_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_set_media_type_t media_type;
+} __packed;
+
+struct cvs_send_dec_buf_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_evt_send_dec_buffer_t dec_buf;
+} __packed;
+
+struct cvs_set_amr_enc_rate_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_voc_amr_set_enc_rate_t amr_rate;
+} __packed;
+
+struct cvs_set_amrwb_enc_rate_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_voc_amrwb_set_enc_rate_t amrwb_rate;
+} __packed;
+
+struct cvs_set_cdma_enc_minmax_rate_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_cdma_set_enc_minmax_rate_t cdma_rate;
+} __packed;
+
+struct cvs_set_enc_dtx_mode_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_set_enc_dtx_mode_t dtx_mode;
+} __packed;
+
+struct cvs_register_cal_data_cmd {
+	struct apr_hdr hdr;
+	struct vss_istream_cmd_register_calibration_data_v2_t cvs_cal_data;
+} __packed;
+
+struct cvs_deregister_cal_data_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvs_set_pp_enable_cmd {
+	struct apr_hdr hdr;
+	struct vss_icommon_cmd_set_ui_property_enable_t vss_set_pp;
+} __packed;
+struct cvs_start_record_cmd {
+	struct apr_hdr hdr;
+	struct vss_irecord_cmd_start_t rec_mode;
+} __packed;
+
+struct cvs_start_playback_cmd {
+	struct apr_hdr hdr;
+	struct vss_iplayback_cmd_start_t playback_mode;
+} __packed;
+
+struct cvs_dec_buffer_ready_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvs_enc_buffer_consumed_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct vss_istream_cmd_set_oob_packet_exchange_config_t {
+	struct apr_hdr hdr;
+	uint32_t mem_handle;
+	uint32_t enc_buf_addr_lsw;
+	uint32_t enc_buf_addr_msw;
+	uint32_t enc_buf_size;
+	uint32_t dec_buf_addr_lsw;
+	uint32_t dec_buf_addr_msw;
+	uint32_t dec_buf_size;
+} __packed;
+
+struct vss_istream_cmd_set_packet_exchange_mode_t {
+	struct apr_hdr hdr;
+	uint32_t mode;
+} __packed;
+
+/* TO CVP commands */
+
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION	0x000100C3
+/**< Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define APRV2_IBASIC_CMD_DESTROY_SESSION		0x0001003C
+
+#define VSS_IVOCPROC_CMD_SET_DEVICE_V2			0x000112C6
+
+#define VSS_IVOCPROC_CMD_SET_DEVICE_V3			0x0001316A
+
+#define VSS_IVOCPROC_CMD_TOPOLOGY_SET_DEV_CHANNELS	0x00013199
+
+#define VSS_IVOCPROC_CMD_TOPOLOGY_COMMIT		0x00013198
+
+#define VSS_IVOCPROC_CMD_SET_VP3_DATA			0x000110EB
+
+#define VSS_IVOLUME_CMD_SET_STEP			0x000112C2
+
+#define VSS_IVOCPROC_CMD_ENABLE				0x000100C6
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+#define VSS_IVOCPROC_CMD_DISABLE			0x000110E1
+/**< No payload. Wait for APRV2_IBASIC_RSP_RESULT response. */
+
+/*
+ * Registers the memory that contains device specific configuration data with
+ * the vocproc. The client must register device configuration data with the
+ * vocproc that corresponds with the device being set on the vocproc.
+ */
+#define VSS_IVOCPROC_CMD_REGISTER_DEVICE_CONFIG		0x00011371
+
+/*
+ * Deregisters the memory that holds device configuration data from the
+  vocproc.
+*/
+#define VSS_IVOCPROC_CMD_DEREGISTER_DEVICE_CONFIG	0x00011372
+
+#define VSS_IVOCPROC_CMD_REGISTER_CALIBRATION_DATA_V2	0x00011373
+#define VSS_IVOCPROC_CMD_DEREGISTER_CALIBRATION_DATA	0x00011276
+
+#define VSS_IVOCPROC_CMD_REGISTER_VOL_CALIBRATION_DATA	0x00011374
+#define VSS_IVOCPROC_CMD_DEREGISTER_VOL_CALIBRATION_DATA	0x00011375
+
+#define VSS_IVOCPROC_CMD_REGISTER_STATIC_CALIBRATION_DATA       0x00013079
+#define VSS_IVOCPROC_CMD_DEREGISTER_STATIC_CALIBRATION_DATA     0x0001307A
+
+#define VSS_IVOCPROC_CMD_REGISTER_DYNAMIC_CALIBRATION_DATA      0x0001307B
+#define VSS_IVOCPROC_CMD_DEREGISTER_DYNAMIC_CALIBRATION_DATA    0x0001307C
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_NONE			0x00010F70
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_SM_ECNS		0x00010F71
+#define VSS_IVOCPROC_TOPOLOGY_ID_TX_DM_FLUENCE		0x00010F72
+
+#define VSS_IVOCPROC_TOPOLOGY_ID_RX_DEFAULT		0x00010F77
+
+/* Newtwork IDs */
+#define VSS_ICOMMON_CAL_NETWORK_ID_NONE		0x0001135E
+
+/* Select internal mixing mode. */
+#define VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING	0x00010F7C
+
+/* Select external mixing mode. */
+#define VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING	0x00010F7D
+
+/* Default AFE port ID. Applicable to Tx and Rx. */
+#define VSS_IVOCPROC_PORT_ID_NONE		0xFFFF
+
+#define VSS_NETWORK_ID_DEFAULT		0x00010037
+
+/* Voice over Internet Protocol (VoIP) network ID. Common for all bands.*/
+#define VSS_NETWORK_ID_VOIP		0x00011362
+
+/* Media types */
+#define VSS_MEDIA_ID_EVRC_MODEM		0x00010FC2
+/* 80-VF690-47 CDMA enhanced variable rate vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_NB_MODEM	0x00010FC6
+/* 80-VF690-47 UMTS AMR-NB vocoder modem format. */
+#define VSS_MEDIA_ID_AMR_WB_MODEM	0x00010FC7
+/* 80-VF690-47 UMTS AMR-WB vocoder modem format. */
+
+#define VSS_MEDIA_ID_PCM_8_KHZ		0x00010FCB
+#define VSS_MEDIA_ID_PCM_16_KHZ		0x00010FCC
+#define VSS_MEDIA_ID_PCM_32_KHZ		0x00010FD9
+#define VSS_MEDIA_ID_PCM_48_KHZ		0x00010FD6
+
+/* Linear PCM (16-bit, little-endian). */
+#define VSS_MEDIA_ID_G711_ALAW		0x00010FCD
+/* G.711 a-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G711_MULAW		0x00010FCE
+/* G.711 mu-law (contains two 10ms vocoder frames). */
+#define VSS_MEDIA_ID_G729		0x00010FD0
+/* G.729AB (contains two 10ms vocoder frames. */
+#define VSS_MEDIA_ID_4GV_NB_MODEM	0x00010FC3
+/*CDMA EVRC-B vocoder modem format */
+#define VSS_MEDIA_ID_4GV_WB_MODEM	0x00010FC4
+/*CDMA EVRC-WB vocoder modem format */
+#define VSS_MEDIA_ID_4GV_NW_MODEM	0x00010FC5
+/*CDMA EVRC-NW vocoder modem format */
+
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V2	0x000112BF
+#define VSS_IVOCPROC_CMD_CREATE_FULL_CONTROL_SESSION_V3	0x00013169
+
+#define VSS_NUM_DEV_CHANNELS_1 1
+#define VSS_NUM_DEV_CHANNELS_2 2
+#define VSS_NUM_DEV_CHANNELS_3 3
+#define VSS_NUM_DEV_CHANNELS_4 4
+
+struct vss_ivocproc_cmd_create_full_control_session_v2_t {
+	uint16_t direction;
+	/*
+	 * Vocproc direction. The supported values:
+	 * VSS_IVOCPROC_DIRECTION_RX
+	 * VSS_IVOCPROC_DIRECTION_TX
+	 * VSS_IVOCPROC_DIRECTION_RX_TX
+	 */
+	uint16_t tx_port_id;
+	/*
+	 * Tx device port ID to which the vocproc connects. If a port ID is
+	 * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE.
+	 */
+	uint32_t tx_topology_id;
+	/*
+	 * Tx path topology ID. If a topology ID is not being supplied, set
+	 * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+	 */
+	uint16_t rx_port_id;
+	/*
+	 * Rx device port ID to which the vocproc connects. If a port ID is
+	 * not being supplied, set this to #VSS_IVOCPROC_PORT_ID_NONE.
+	 */
+	uint32_t rx_topology_id;
+	/*
+	 * Rx path topology ID. If a topology ID is not being supplied, set
+	 * this to #VSS_IVOCPROC_TOPOLOGY_ID_NONE.
+	 */
+	uint32_t profile_id;
+	/* Voice calibration profile ID. */
+	uint32_t vocproc_mode;
+	/*
+	 * Vocproc mode. The supported values:
+	 * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING
+	 * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING
+	 */
+	uint16_t ec_ref_port_id;
+	/*
+	 * Port ID to which the vocproc connects for receiving echo
+	 * cancellation reference signal. If a port ID is not being supplied,
+	 * set this to #VSS_IVOCPROC_PORT_ID_NONE. This parameter value is
+	 * ignored when the vocproc_mode parameter is set to
+	 * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING.
+	 */
+	char name[SESSION_NAME_LEN];
+	/*
+	 * Session name string used to identify a session that can be shared
+	 * with passive controllers (optional). The string size, including the
+	 * NULL termination character, is limited to 31 characters.
+	 */
+} __packed;
+
+struct vss_ivocproc_cmd_set_volume_index_t {
+	uint16_t vol_index;
+	/*
+	 * Volume index utilized by the vocproc to index into the volume table
+	 * provided in VSS_IVOCPROC_CMD_CACHE_VOLUME_CALIBRATION_TABLE and set
+	 * volume on the VDSP.
+	 */
+} __packed;
+
+struct vss_ivolume_cmd_set_step_t {
+	uint16_t direction;
+	/*
+	 * The direction field sets the direction to apply the volume command.
+	 * The supported values:
+	 * #VSS_IVOLUME_DIRECTION_RX
+	 */
+	uint32_t value;
+	/*
+	 * Volume step used to find the corresponding linear volume and
+	 * the best match index in the registered volume calibration table.
+	 */
+	uint16_t ramp_duration_ms;
+	/*
+	 * Volume change ramp duration in milliseconds.
+	 * The supported values: 0 to 5000.
+	 */
+} __packed;
+
+struct vss_ivocproc_cmd_set_device_v2_t {
+	uint16_t tx_port_id;
+	/*
+	 * TX device port ID which vocproc will connect to.
+	 * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+	 */
+	uint32_t tx_topology_id;
+	/*
+	 * TX leg topology ID.
+	 * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+	 * pre/post-processing blocks and is pass-through.
+	 */
+	uint16_t rx_port_id;
+	/*
+	 * RX device port ID which vocproc will connect to.
+	 * VSS_IVOCPROC_PORT_ID_NONE means vocproc will not connect to any port.
+	 */
+	uint32_t rx_topology_id;
+	/*
+	 * RX leg topology ID.
+	 * VSS_IVOCPROC_TOPOLOGY_ID_NONE means vocproc does not contain any
+	 * pre/post-processing blocks and is pass-through.
+	 */
+	uint32_t vocproc_mode;
+	/* Vocproc mode. The supported values:
+	 * VSS_IVOCPROC_VOCPROC_MODE_EC_INT_MIXING - 0x00010F7C
+	 * VSS_IVOCPROC_VOCPROC_MODE_EC_EXT_MIXING - 0x00010F7D
+	 */
+	uint16_t ec_ref_port_id;
+	/* Port ID to which the vocproc connects for receiving
+	 * echo
+	 */
+} __packed;
+
+struct vss_ivocproc_cmd_register_device_config_t {
+	uint32_t mem_handle;
+	/*
+	 * Handle to the shared memory that holds the per-network calibration
+	 * data.
+	 */
+	uint32_t mem_address_lsw;
+	uint32_t mem_address_msw;
+	/* Location of calibration data. */
+	uint32_t mem_size;
+	/* Size of the calibration data in bytes. */
+} __packed;
+
+struct vss_ivocproc_cmd_register_calibration_data_v2_t {
+	uint32_t cal_mem_handle;
+	/*
+	 * Handle to the shared memory that holds the per-network calibration
+	 * data.
+	 */
+	uint32_t cal_mem_address_lsw;
+	uint32_t cal_mem_address_msw;
+	/* Location of calibration data. */
+	uint32_t cal_mem_size;
+	/* Size of the calibration data in bytes. */
+	uint8_t column_info[MAX_COL_INFO_SIZE];
+	/*
+	 * Column info contains the number of columns and the array of columns
+	 * in the calibration table. The order in which the columns are provided
+	 * here must match the order in which they exist in the calibration
+	 * table provided.
+	 */
+} __packed;
+
+struct vss_ivocproc_cmd_register_volume_cal_data_t {
+	uint32_t cal_mem_handle;
+	/*
+	 * Handle to the shared memory that holds the volume calibration
+	 * data.
+	 */
+	uint32_t cal_mem_address_lsw;
+	uint32_t cal_mem_address_msw;
+	/* Location of volume calibration data. */
+	uint32_t cal_mem_size;
+	/* Size of the volume calibration data in bytes. */
+	uint8_t column_info[MAX_COL_INFO_SIZE];
+	/*
+	 * Column info contains the number of columns and the array of columns
+	 * in the calibration table. The order in which the columns are provided
+	 * here must match the order in which they exist in the calibration
+	 * table provided.
+	 */
+} __packed;
+
+struct vss_ivocproc_cmd_topology_set_dev_channels_t {
+	uint16_t tx_num_channels;
+	/*
+	 * Number of Mics.
+	 * Supported values
+	 * 1  VSS_NUM_DEV_CHANNELS_1
+	 * 2  VSS_NUM_DEV_CHANNELS_2
+	 * 3  VSS_NUM_DEV_CHANNELS_3
+	 * 4  VSS_NUM_DEV_CHANNELS_4
+	 */
+	uint16_t rx_num_channels;
+	/*
+	 * Number of speaker channels.
+	 * Supported values
+	 * 1 VSS_NUM_DEV_CHANNELS_1
+	 */
+} __packed;
+
+/* Starts a vocoder PCM session */
+#define VSS_IVPCM_CMD_START_V2	0x00011339
+
+/* Default tap point location on the TX path. */
+#define VSS_IVPCM_TAP_POINT_TX_DEFAULT	0x00011289
+
+/* Default tap point location on the RX path. */
+#define VSS_IVPCM_TAP_POINT_RX_DEFAULT	0x0001128A
+
+/* Indicates tap point direction is output. */
+#define VSS_IVPCM_TAP_POINT_DIR_OUT	0
+
+/* Indicates tap point direction is input. */
+#define VSS_IVPCM_TAP_POINT_DIR_IN	1
+
+/* Indicates tap point direction is output and input. */
+#define VSS_IVPCM_TAP_POINT_DIR_OUT_IN	2
+
+
+#define VSS_IVPCM_SAMPLING_RATE_AUTO	0
+
+/* Indicates 8 KHz vocoder PCM sampling rate. */
+#define VSS_IVPCM_SAMPLING_RATE_8K	8000
+
+/* Indicates 16 KHz vocoder PCM sampling rate. */
+#define VSS_IVPCM_SAMPLING_RATE_16K	16000
+
+/* RX and TX */
+#define MAX_TAP_POINTS_SUPPORTED	2
+
+struct vss_ivpcm_tap_point {
+	uint32_t tap_point;
+	uint16_t direction;
+	uint16_t sampling_rate;
+	uint16_t duration;
+} __packed;
+
+
+struct vss_ivpcm_cmd_start_v2_t {
+	uint32_t mem_handle;
+	uint32_t num_tap_points;
+	struct vss_ivpcm_tap_point tap_points[MAX_TAP_POINTS_SUPPORTED];
+} __packed;
+
+#define VSS_IVPCM_EVT_PUSH_BUFFER_V2	0x0001133A
+
+/* Push buffer event mask indicating output buffer is filled. */
+#define VSS_IVPCM_PUSH_BUFFER_MASK_OUTPUT_BUFFER 1
+
+/* Push buffer event mask indicating input buffer is consumed. */
+#define VSS_IVPCM_PUSH_BUFFER_MASK_INPUT_BUFFER 2
+
+
+struct vss_ivpcm_evt_push_buffer_v2_t {
+	uint32_t tap_point;
+	uint32_t push_buf_mask;
+	uint64_t out_buf_mem_address;
+	uint16_t out_buf_mem_size;
+	uint64_t in_buf_mem_address;
+	uint16_t in_buf_mem_size;
+	uint16_t sampling_rate;
+	uint16_t num_in_channels;
+} __packed;
+
+#define VSS_IVPCM_EVT_NOTIFY_V2 0x0001133B
+
+/* Notify event mask indicates output buffer is filled. */
+#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_BUFFER 1
+
+/* Notify event mask indicates input buffer is consumed. */
+#define VSS_IVPCM_NOTIFY_MASK_INPUT_BUFFER 2
+
+/* Notify event mask indicates a timetick */
+#define VSS_IVPCM_NOTIFY_MASK_TIMETICK 4
+
+/* Notify event mask indicates an error occurred in output buffer operation */
+#define VSS_IVPCM_NOTIFY_MASK_OUTPUT_ERROR 8
+
+/* Notify event mask indicates an error occurred in input buffer operation */
+#define VSS_IVPCM_NOTIFY_MASK_INPUT_ERROR 16
+
+
+struct vss_ivpcm_evt_notify_v2_t {
+	uint32_t tap_point;
+	uint32_t notify_mask;
+	uint64_t out_buff_addr;
+	uint64_t in_buff_addr;
+	uint16_t filled_out_size;
+	uint16_t request_buf_size;
+	uint16_t sampling_rate;
+	uint16_t num_out_channels;
+} __packed;
+
+struct cvp_start_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivpcm_cmd_start_v2_t vpcm_start_cmd;
+} __packed;
+
+struct cvp_push_buf_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivpcm_evt_push_buffer_v2_t vpcm_evt_push_buffer;
+} __packed;
+
+#define VSS_IVPCM_CMD_STOP 0x0001100B
+
+struct cvp_create_full_ctl_session_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_create_full_control_session_v2_t cvp_session;
+} __packed;
+
+struct cvp_command {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_device_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_set_device_v2_t cvp_set_device_v2;
+} __packed;
+
+struct cvp_set_dev_channels_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_topology_set_dev_channels_t cvp_set_channels;
+} __packed;
+
+struct cvp_set_vp3_data_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_rx_volume_index_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_set_volume_index_t cvp_set_vol_idx;
+} __packed;
+
+struct cvp_set_rx_volume_step_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivolume_cmd_set_step_t cvp_set_vol_step;
+} __packed;
+
+struct cvp_register_dev_cfg_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_register_device_config_t cvp_dev_cfg_data;
+} __packed;
+
+struct cvp_deregister_dev_cfg_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvp_register_cal_data_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_register_calibration_data_v2_t cvp_cal_data;
+} __packed;
+
+struct cvp_deregister_cal_data_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvp_register_vol_cal_data_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivocproc_cmd_register_volume_cal_data_t cvp_vol_cal_data;
+} __packed;
+
+struct cvp_deregister_vol_cal_data_cmd {
+	struct apr_hdr hdr;
+} __packed;
+
+struct cvp_set_mute_cmd {
+	struct apr_hdr hdr;
+	struct vss_ivolume_cmd_mute_v2_t cvp_set_mute;
+} __packed;
+
+/* CB for up-link packets. */
+typedef void (*ul_cb_fn)(uint8_t *voc_pkt,
+			 uint32_t pkt_len,
+			 uint32_t timestamp,
+			 void *private_data);
+
+/* CB for down-link packets. */
+typedef void (*dl_cb_fn)(uint8_t *voc_pkt,
+			 void *private_data);
+
+/* CB for DTMF RX Detection */
+typedef void (*dtmf_rx_det_cb_fn)(uint8_t *pkt,
+				  char *session,
+				  void *private_data);
+
+typedef void (*voip_ssr_cb) (uint32_t opcode,
+				void *private_data);
+
+typedef void (*hostpcm_cb_fn)(uint8_t *data,
+			   char *session,
+			   void *private_data);
+
+struct mvs_driver_info {
+	uint32_t media_type;
+	uint32_t rate;
+	uint32_t network_type;
+	uint32_t dtx_mode;
+	ul_cb_fn ul_cb;
+	dl_cb_fn dl_cb;
+	voip_ssr_cb ssr_cb;
+	void *private_data;
+	uint32_t evrc_min_rate;
+	uint32_t evrc_max_rate;
+};
+
+struct dtmf_driver_info {
+	dtmf_rx_det_cb_fn dtmf_rx_ul_cb;
+	void *private_data;
+};
+
+struct hostpcm_driver_info {
+	hostpcm_cb_fn hostpcm_evt_cb;
+	void *private_data;
+};
+
+struct incall_rec_info {
+	uint32_t rec_enable;
+	uint32_t rec_mode;
+	uint32_t recording;
+};
+
+struct incall_music_info {
+	uint32_t play_enable;
+	uint32_t playing;
+	int count;
+	int force;
+	uint16_t port_id;
+};
+
+struct share_memory_info {
+	u32			mem_handle;
+	struct share_mem_buf	sh_buf;
+	struct mem_map_table	memtbl;
+};
+
+#define VSS_ISOUNDFOCUS_CMD_SET_SECTORS     0x00013133
+#define VSS_ISOUNDFOCUS_CMD_GET_SECTORS     0x00013134
+#define VSS_ISOUNDFOCUS_RSP_GET_SECTORS     0x00013135
+#define VSS_ISOURCETRACK_CMD_GET_ACTIVITY   0x00013136
+
+struct vss_isoundfocus_cmd_set_sectors_t {
+	uint16_t start_angles[8];
+	uint8_t enables[8];
+	uint16_t gain_step;
+} __packed;
+
+/* Payload of the VSS_ISOUNDFOCUS_RSP_GET_SECTORS response */
+struct vss_isoundfocus_rsp_get_sectors_t {
+	uint16_t start_angles[8];
+	uint8_t enables[8];
+	uint16_t gain_step;
+} __packed;
+
+struct cvp_set_sound_focus_param_cmd_t {
+	struct apr_hdr hdr;
+	struct vss_isoundfocus_cmd_set_sectors_t cvp_set_sound_focus_param;
+} __packed;
+
+/* Payload structure for the VSS_ISOURCETRACK_CMD_GET_ACTIVITY command */
+struct vss_isourcetrack_cmd_get_activity_t {
+	uint32_t mem_handle;
+	uint32_t mem_address_lsw;
+	uint32_t mem_address_msw;
+	uint32_t mem_size;
+} __packed;
+
+struct cvp_get_source_tracking_param_cmd_t {
+	struct apr_hdr hdr;
+	struct vss_isourcetrack_cmd_get_activity_t
+			cvp_get_source_tracking_param;
+} __packed;
+
+/* Structure for the sound activity data */
+struct vss_isourcetrack_activity_data_t {
+	uint8_t voice_active[8];
+	uint16_t talker_doa;
+	uint16_t interferer_doa[3];
+	uint8_t sound_strength[360];
+} __packed;
+
+struct shared_mem_info {
+	uint32_t mem_handle;
+	struct mem_map_table sh_mem_block;
+	struct mem_map_table sh_mem_table;
+};
+
+struct voice_data {
+	int voc_state;/*INIT, CHANGE, RELEASE, RUN */
+
+	/* Shared mem to store decoder and encoder packets */
+	struct share_memory_info	shmem_info;
+
+	wait_queue_head_t mvm_wait;
+	wait_queue_head_t cvs_wait;
+	wait_queue_head_t cvp_wait;
+
+	/* Cache the values related to Rx and Tx devices */
+	struct device_data dev_rx;
+	struct device_data dev_tx;
+
+	/* Cache the values related to Rx and Tx streams */
+	struct stream_data stream_rx;
+	struct stream_data stream_tx;
+
+	u32 mvm_state;
+	u32 cvs_state;
+	u32 cvp_state;
+
+	u32 async_err;
+
+	/* Handle to MVM in the Q6 */
+	u16 mvm_handle;
+	/* Handle to CVS in the Q6 */
+	u16 cvs_handle;
+	/* Handle to CVP in the Q6 */
+	u16 cvp_handle;
+
+	struct mutex lock;
+
+	bool disable_topology;
+
+	uint16_t sidetone_gain;
+	uint8_t tty_mode;
+	/* slowtalk enable value */
+	uint32_t st_enable;
+	uint32_t hd_enable;
+	uint32_t dtmf_rx_detect_en;
+	/* Local Call Hold mode */
+	uint8_t lch_mode;
+
+	struct voice_dev_route_state voc_route_state;
+
+	u32 session_id;
+
+	struct incall_rec_info rec_info;
+
+	struct incall_music_info music_info;
+
+	struct voice_rec_route_state rec_route_state;
+};
+
+struct cal_mem {
+	struct ion_handle *handle;
+	uint32_t phy;
+	void *buf;
+};
+
+#define MAX_VOC_SESSIONS 8
+
+struct common_data {
+	/* these default values are for all devices */
+	uint32_t default_mute_val;
+	uint32_t default_sample_val;
+	uint32_t default_vol_step_val;
+	uint32_t default_vol_ramp_duration_ms;
+	uint32_t default_mute_ramp_duration_ms;
+	bool ec_ref_ext;
+	uint16_t ec_port_id;
+
+	/* APR to MVM in the Q6 */
+	void *apr_q6_mvm;
+	/* APR to CVS in the Q6 */
+	void *apr_q6_cvs;
+	/* APR to CVP in the Q6 */
+	void *apr_q6_cvp;
+
+	struct cal_type_data *cal_data[MAX_VOICE_CAL_TYPES];
+
+	struct mem_map_table cal_mem_map_table;
+	uint32_t cal_mem_handle;
+
+	struct mem_map_table rtac_mem_map_table;
+	uint32_t rtac_mem_handle;
+
+	uint32_t voice_host_pcm_mem_handle;
+
+	struct cal_mem cvp_cal;
+	struct cal_mem cvs_cal;
+
+	struct mutex common_lock;
+
+	struct mvs_driver_info mvs_info;
+
+	struct dtmf_driver_info dtmf_info;
+
+	struct hostpcm_driver_info hostpcm_info;
+
+	struct voice_data voice[MAX_VOC_SESSIONS];
+
+	bool srvcc_rec_flag;
+	bool is_destroy_cvd;
+	char cvd_version[CVD_VERSION_STRING_MAX_SIZE];
+	bool is_per_vocoder_cal_enabled;
+	bool is_sound_focus_resp_success;
+	bool is_source_tracking_resp_success;
+	struct vss_isoundfocus_rsp_get_sectors_t soundFocusResponse;
+	struct shared_mem_info source_tracking_sh_mem;
+	struct vss_isourcetrack_activity_data_t sourceTrackingResponse;
+};
+
+struct voice_session_itr {
+	int cur_idx;
+	int session_idx;
+};
+
+void voc_register_mvs_cb(ul_cb_fn ul_cb,
+			dl_cb_fn dl_cb,
+			voip_ssr_cb ssr_cb,
+			void *private_data);
+
+void voc_register_dtmf_rx_detection_cb(dtmf_rx_det_cb_fn dtmf_rx_ul_cb,
+				       void *private_data);
+
+void voc_config_vocoder(uint32_t media_type,
+			uint32_t rate,
+			uint32_t network_type,
+			uint32_t dtx_mode,
+			uint32_t evrc_min_rate,
+			uint32_t evrc_max_rate);
+
+enum {
+	DEV_RX = 0,
+	DEV_TX,
+};
+
+enum {
+	RX_PATH = 0,
+	TX_PATH,
+};
+
+
+#define VOC_PATH_PASSIVE 0
+#define VOC_PATH_FULL 1
+#define VOC_PATH_VOLTE_PASSIVE 2
+#define VOC_PATH_VOICE2_PASSIVE 3
+#define VOC_PATH_QCHAT_PASSIVE 4
+#define VOC_PATH_VOWLAN_PASSIVE 5
+#define VOC_PATH_VOICEMMODE1_PASSIVE 6
+#define VOC_PATH_VOICEMMODE2_PASSIVE 7
+
+#define MAX_SESSION_NAME_LEN 32
+#define VOICE_SESSION_NAME   "Voice session"
+#define VOIP_SESSION_NAME    "VoIP session"
+#define VOLTE_SESSION_NAME   "VoLTE session"
+#define VOICE2_SESSION_NAME  "Voice2 session"
+#define QCHAT_SESSION_NAME   "QCHAT session"
+#define VOWLAN_SESSION_NAME  "VoWLAN session"
+#define VOICEMMODE1_NAME     "VoiceMMode1"
+#define VOICEMMODE2_NAME     "VoiceMMode2"
+
+#define VOICE2_SESSION_VSID_STR      "10DC1000"
+#define QCHAT_SESSION_VSID_STR       "10803000"
+#define VOWLAN_SESSION_VSID_STR      "10002000"
+#define VOICEMMODE1_VSID_STR         "11C05000"
+#define VOICEMMODE2_VSID_STR         "11DC5000"
+#define VOICE_SESSION_VSID           0x10C01000
+#define VOICE2_SESSION_VSID          0x10DC1000
+#define VOLTE_SESSION_VSID           0x10C02000
+#define VOIP_SESSION_VSID            0x10004000
+#define QCHAT_SESSION_VSID           0x10803000
+#define VOWLAN_SESSION_VSID          0x10002000
+#define VOICEMMODE1_VSID             0x11C05000
+#define VOICEMMODE2_VSID             0x11DC5000
+#define ALL_SESSION_VSID             0xFFFFFFFF
+#define VSID_MAX                     ALL_SESSION_VSID
+
+/* called  by alsa driver */
+int voc_set_pp_enable(uint32_t session_id, uint32_t module_id,
+		      uint32_t enable);
+int voc_get_pp_enable(uint32_t session_id, uint32_t module_id);
+int voc_set_hd_enable(uint32_t session_id, uint32_t enable);
+uint8_t voc_get_tty_mode(uint32_t session_id);
+int voc_set_tty_mode(uint32_t session_id, uint8_t tty_mode);
+int voc_start_voice_call(uint32_t session_id);
+int voc_end_voice_call(uint32_t session_id);
+int voc_standby_voice_call(uint32_t session_id);
+int voc_resume_voice_call(uint32_t session_id);
+int voc_set_lch(uint32_t session_id, enum voice_lch_mode lch_mode);
+int voc_set_rx_vol_step(uint32_t session_id, uint32_t dir, uint32_t vol_step,
+			uint32_t ramp_duration);
+int voc_set_tx_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+		    uint32_t ramp_duration);
+int voc_set_device_mute(uint32_t session_id, uint32_t dir, uint32_t mute,
+			uint32_t ramp_duration);
+int voc_get_rx_device_mute(uint32_t session_id);
+int voc_set_route_flag(uint32_t session_id, uint8_t path_dir, uint8_t set);
+uint8_t voc_get_route_flag(uint32_t session_id, uint8_t path_dir);
+int voc_enable_dtmf_rx_detection(uint32_t session_id, uint32_t enable);
+void voc_disable_dtmf_det_on_active_sessions(void);
+int voc_alloc_cal_shared_memory(void);
+int voc_alloc_voip_shared_memory(void);
+int is_voc_initialized(void);
+int voc_register_vocproc_vol_table(void);
+int voc_deregister_vocproc_vol_table(void);
+int voc_send_cvp_map_vocpcm_memory(uint32_t session_id,
+				   struct mem_map_table *tp_mem_table,
+				   phys_addr_t paddr, uint32_t bufsize);
+int voc_send_cvp_unmap_vocpcm_memory(uint32_t session_id);
+int voc_send_cvp_start_vocpcm(uint32_t session_id,
+			      struct vss_ivpcm_tap_point *vpcm_tp,
+			      uint32_t no_of_tp);
+int voc_send_cvp_vocpcm_push_buf_evt(uint32_t session_id,
+			struct vss_ivpcm_evt_push_buffer_v2_t *push_buff_evt);
+int voc_send_cvp_stop_vocpcm(uint32_t session_id);
+void voc_register_hpcm_evt_cb(hostpcm_cb_fn hostpcm_cb,
+			      void *private_data);
+void voc_deregister_hpcm_evt_cb(void);
+
+int voc_map_rtac_block(struct rtac_cal_block_data *cal_block);
+int voc_unmap_rtac_block(uint32_t *mem_map_handle);
+
+uint32_t voc_get_session_id(char *name);
+
+int voc_start_playback(uint32_t set, uint16_t port_id);
+int voc_start_record(uint32_t port_id, uint32_t set, uint32_t session_id);
+int voice_get_idx_for_session(u32 session_id);
+int voc_set_ext_ec_ref(uint16_t port_id, bool state);
+int voc_update_amr_vocoder_rate(uint32_t session_id);
+int voc_disable_device(uint32_t session_id);
+int voc_enable_device(uint32_t session_id);
+void voc_set_destroy_cvd_flag(bool is_destroy_cvd);
+int voc_disable_topology(uint32_t session_id, uint32_t disable);
+int voc_set_device_config(uint32_t session_id, uint8_t path_dir,
+			  uint8_t no_of_channels, uint32_t dev_port_id);
+uint32_t voice_get_topology(uint32_t topology_idx);
+int voc_set_sound_focus(struct sound_focus_param sound_focus_param);
+int voc_get_sound_focus(struct sound_focus_param *soundFocusData);
+int voc_get_source_tracking(struct source_tracking_param *sourceTrackingData);
+#endif
diff --git a/sound/soc/msm/qdsp6v2/rtac.c b/sound/soc/msm/qdsp6v2/rtac.c
new file mode 100644
index 0000000..923908f
--- /dev/null
+++ b/sound/soc/msm/qdsp6v2/rtac.c
@@ -0,0 +1,1904 @@
+/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/msm_audio_calibration.h>
+#include <linux/atomic.h>
+#include <linux/msm_audio_ion.h>
+#include <linux/qdsp6v2/rtac.h>
+#include <linux/compat.h>
+#include <sound/q6asm-v2.h>
+#include <sound/q6afe-v2.h>
+#include <sound/q6adm-v2.h>
+#include <sound/apr_audio-v2.h>
+#include "q6voice.h"
+#include "msm-pcm-routing-v2.h"
+#include <sound/adsp_err.h>
+
+
+/* Max size of payload (buf size - apr header) */
+#define MAX_PAYLOAD_SIZE		4076
+#define RTAC_MAX_ACTIVE_VOICE_COMBOS	2
+#define RTAC_MAX_ACTIVE_POPP		8
+#define RTAC_BUF_SIZE			163840
+
+#define TIMEOUT_MS	1000
+
+struct rtac_cal_block_data	rtac_cal[MAX_RTAC_BLOCKS] = {
+/* ADM_RTAC_CAL */
+	{{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} },
+/* ASM_RTAC_CAL */
+	{{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} },
+/* VOICE_RTAC_CAL */
+	{{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} },
+/* AFE_RTAC_CAL */
+	{{RTAC_BUF_SIZE, 0, 0, 0}, {0, 0, 0} }
+};
+
+struct rtac_common_data {
+	atomic_t			usage_count;
+	atomic_t			apr_err_code;
+};
+
+static struct rtac_common_data		rtac_common;
+
+/* APR data */
+struct rtac_apr_data {
+	void			*apr_handle;
+	atomic_t		cmd_state;
+	wait_queue_head_t	cmd_wait;
+};
+
+static struct rtac_apr_data rtac_adm_apr_data;
+static struct rtac_apr_data rtac_asm_apr_data[ASM_ACTIVE_STREAMS_ALLOWED + 1];
+static struct rtac_apr_data	rtac_afe_apr_data;
+static struct rtac_apr_data	rtac_voice_apr_data[RTAC_VOICE_MODES];
+
+/* ADM info & APR */
+static struct rtac_adm		rtac_adm_data;
+static u32			*rtac_adm_buffer;
+
+
+/* ASM APR */
+static u32			*rtac_asm_buffer;
+
+static u32			*rtac_afe_buffer;
+
+/* Voice info & APR */
+struct rtac_voice_data_t {
+	uint32_t	tx_topology_id;
+	uint32_t	rx_topology_id;
+	uint32_t	tx_afe_topology;
+	uint32_t	rx_afe_topology;
+	uint32_t	tx_afe_port;
+	uint32_t	rx_afe_port;
+	uint16_t	cvs_handle;
+	uint16_t	cvp_handle;
+	uint32_t	tx_acdb_id;
+	uint32_t	rx_acdb_id;
+};
+
+struct rtac_voice {
+	uint32_t			num_of_voice_combos;
+	struct rtac_voice_data_t	voice[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+};
+
+struct rtac_afe_user_data {
+	uint32_t	buf_size;
+	uint32_t	cmd_size;
+	uint32_t	port_id;
+	union {
+		struct rtac_afe_set {
+			struct afe_port_cmd_set_param_v2 cmd;
+			struct afe_port_param_data_v2    data;
+		} rtac_afe_set;
+		struct rtac_afe_get {
+			struct afe_port_cmd_get_param_v2 cmd;
+			struct afe_port_param_data_v2    data;
+		} rtac_afe_get;
+	};
+}  __packed;
+
+static struct rtac_voice	rtac_voice_data;
+static u32			*rtac_voice_buffer;
+static u32			voice_session_id[RTAC_MAX_ACTIVE_VOICE_COMBOS];
+
+
+struct mutex			rtac_adm_mutex;
+struct mutex			rtac_adm_apr_mutex;
+struct mutex			rtac_asm_apr_mutex;
+struct mutex			rtac_voice_mutex;
+struct mutex			rtac_voice_apr_mutex;
+struct mutex			rtac_afe_apr_mutex;
+
+int rtac_clear_mapping(uint32_t cal_type)
+{
+	int result = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type >= MAX_RTAC_BLOCKS) {
+		pr_debug("%s: invalid cal type %d\n", __func__, cal_type);
+		result = -EINVAL;
+		goto done;
+	}
+
+	rtac_cal[cal_type].map_data.map_handle = 0;
+done:
+	return result;
+}
+
+int rtac_allocate_cal_buffer(uint32_t cal_type)
+{
+	int result = 0;
+	size_t len;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type >= MAX_RTAC_BLOCKS) {
+		pr_err("%s: cal_type %d is invalid!\n",
+		       __func__, cal_type);
+		result =  -EINVAL;
+		goto done;
+	}
+
+	if (rtac_cal[cal_type].cal_data.paddr != 0) {
+		pr_err("%s: memory already allocated! cal_type %d, paddr 0x%pK\n",
+		       __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr);
+		result = -EPERM;
+		goto done;
+	}
+
+	result = msm_audio_ion_alloc("rtac_client",
+		&rtac_cal[cal_type].map_data.ion_client,
+		&rtac_cal[cal_type].map_data.ion_handle,
+		rtac_cal[cal_type].map_data.map_size,
+		&rtac_cal[cal_type].cal_data.paddr,
+		&len,
+		&rtac_cal[cal_type].cal_data.kvaddr);
+	if (result < 0) {
+		pr_err("%s: ION create client for RTAC failed\n",
+		       __func__);
+		goto done;
+	}
+
+	pr_debug("%s: cal_type %d, paddr 0x%pK, kvaddr 0x%pK, map_size 0x%x\n",
+		__func__, cal_type,
+		&rtac_cal[cal_type].cal_data.paddr,
+		rtac_cal[cal_type].cal_data.kvaddr,
+		rtac_cal[cal_type].map_data.map_size);
+done:
+	return result;
+}
+
+int rtac_free_cal_buffer(uint32_t cal_type)
+{
+	int result = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type >= MAX_RTAC_BLOCKS) {
+		pr_err("%s: cal_type %d is invalid!\n",
+		       __func__, cal_type);
+		result =  -EINVAL;
+		goto done;
+	}
+
+	if (rtac_cal[cal_type].map_data.ion_client == NULL) {
+		pr_debug("%s: cal_type %d not allocated!\n",
+		       __func__, cal_type);
+		goto done;
+	}
+
+	result = msm_audio_ion_free(rtac_cal[cal_type].map_data.ion_client,
+				rtac_cal[cal_type].map_data.ion_handle);
+	if (result < 0) {
+		pr_err("%s: ION free for RTAC failed! cal_type %d, paddr 0x%pK\n",
+		       __func__, cal_type, &rtac_cal[cal_type].cal_data.paddr);
+		goto done;
+	}
+
+	rtac_cal[cal_type].map_data.map_handle = 0;
+	rtac_cal[cal_type].map_data.ion_client = NULL;
+	rtac_cal[cal_type].map_data.ion_handle = NULL;
+	rtac_cal[cal_type].cal_data.size = 0;
+	rtac_cal[cal_type].cal_data.kvaddr = 0;
+	rtac_cal[cal_type].cal_data.paddr = 0;
+done:
+	return result;
+}
+
+int rtac_map_cal_buffer(uint32_t cal_type)
+{
+	int result = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type >= MAX_RTAC_BLOCKS) {
+		pr_err("%s: cal_type %d is invalid!\n",
+		       __func__, cal_type);
+		result =  -EINVAL;
+		goto done;
+	}
+
+	if (rtac_cal[cal_type].map_data.map_handle != 0) {
+		pr_err("%s: already mapped cal_type %d\n",
+			__func__, cal_type);
+		result =  -EPERM;
+		goto done;
+	}
+
+	if (rtac_cal[cal_type].cal_data.paddr == 0) {
+		pr_err("%s: physical address is NULL cal_type %d\n",
+			__func__, cal_type);
+		result =  -EPERM;
+		goto done;
+	}
+
+	switch (cal_type) {
+	case ADM_RTAC_CAL:
+		result = adm_map_rtac_block(&rtac_cal[cal_type]);
+		break;
+	case ASM_RTAC_CAL:
+		result = q6asm_map_rtac_block(&rtac_cal[cal_type]);
+		break;
+	case VOICE_RTAC_CAL:
+		result = voc_map_rtac_block(&rtac_cal[cal_type]);
+		break;
+	case AFE_RTAC_CAL:
+		result = afe_map_rtac_block(&rtac_cal[cal_type]);
+		break;
+	}
+	if (result < 0) {
+		pr_err("%s: map RTAC failed! cal_type %d\n",
+		       __func__, cal_type);
+		goto done;
+	}
+done:
+	return result;
+}
+
+int rtac_unmap_cal_buffer(uint32_t cal_type)
+{
+	int result = 0;
+
+	pr_debug("%s\n", __func__);
+
+	if (cal_type >= MAX_RTAC_BLOCKS) {
+		pr_err("%s: cal_type %d is invalid!\n",
+		       __func__, cal_type);
+		result =  -EINVAL;
+		goto done;
+	}
+
+	if (rtac_cal[cal_type].map_data.map_handle == 0) {
+		pr_debug("%s: nothing to unmap cal_type %d\n",
+			__func__, cal_type);
+		goto done;
+	}
+
+	switch (cal_type) {
+	case ADM_RTAC_CAL:
+		result = adm_unmap_rtac_block(
+			&rtac_cal[cal_type].map_data.map_handle);
+		break;
+	case ASM_RTAC_CAL:
+		result = q6asm_unmap_rtac_block(
+			&rtac_cal[cal_type].map_data.map_handle);
+		break;
+	case VOICE_RTAC_CAL:
+		result = voc_unmap_rtac_block(
+			&rtac_cal[cal_type].map_data.map_handle);
+		break;
+	case AFE_RTAC_CAL:
+		result = afe_unmap_rtac_block(
+			&rtac_cal[cal_type].map_data.map_handle);
+		break;
+	}
+	if (result < 0) {
+		pr_err("%s: unmap RTAC failed! cal_type %d\n",
+		       __func__, cal_type);
+		goto done;
+	}
+done:
+	return result;
+}
+
+static int rtac_open(struct inode *inode, struct file *f)
+{
+	int result = 0;
+
+	pr_debug("%s\n", __func__);
+
+	atomic_inc(&rtac_common.usage_count);
+	return result;
+}
+
+static int rtac_release(struct inode *inode, struct file *f)
+{
+	int result = 0;
+	int result2 = 0;
+	int i;
+
+	pr_debug("%s\n", __func__);
+
+	atomic_dec(&rtac_common.usage_count);
+	pr_debug("%s: ref count %d!\n", __func__,
+		atomic_read(&rtac_common.usage_count));
+
+	if (atomic_read(&rtac_common.usage_count) > 0)
+		goto done;
+
+	for (i = 0; i < MAX_RTAC_BLOCKS; i++) {
+		result2 = rtac_unmap_cal_buffer(i);
+		if (result2 < 0) {
+			pr_err("%s: unmap buffer failed! error %d!\n",
+				__func__, result2);
+			result = result2;
+		}
+
+		result2 = rtac_free_cal_buffer(i);
+		if (result2 < 0) {
+			pr_err("%s: free buffer failed! error %d!\n",
+				__func__, result2);
+			result = result2;
+		}
+	}
+done:
+	return result;
+}
+
+
+/* ADM Info */
+void add_popp(u32 dev_idx, u32 port_id, u32 popp_id)
+{
+	u32 i = 0;
+
+	for (; i < rtac_adm_data.device[dev_idx].num_of_popp; i++)
+		if (rtac_adm_data.device[dev_idx].popp[i].popp == popp_id)
+			goto done;
+
+	if (rtac_adm_data.device[dev_idx].num_of_popp ==
+			RTAC_MAX_ACTIVE_POPP) {
+		pr_err("%s, Max POPP!\n", __func__);
+		goto done;
+	}
+	rtac_adm_data.device[dev_idx].popp[
+		rtac_adm_data.device[dev_idx].num_of_popp].popp = popp_id;
+	rtac_adm_data.device[dev_idx].popp[
+		rtac_adm_data.device[dev_idx].num_of_popp].popp_topology =
+		q6asm_get_asm_topology(popp_id);
+	rtac_adm_data.device[dev_idx].popp[
+		rtac_adm_data.device[dev_idx].num_of_popp++].app_type =
+		q6asm_get_asm_app_type(popp_id);
+
+	pr_debug("%s: popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n",
+		__func__,
+		rtac_adm_data.device[dev_idx].popp[
+			rtac_adm_data.device[dev_idx].num_of_popp - 1].popp,
+		rtac_adm_data.device[dev_idx].popp[
+		rtac_adm_data.device[dev_idx].num_of_popp - 1].popp_topology,
+		rtac_adm_data.device[dev_idx].popp[
+		rtac_adm_data.device[dev_idx].num_of_popp - 1].app_type);
+done:
+	return;
+}
+
+void rtac_add_adm_device(u32 port_id, u32 copp_id, u32 path_id, u32 popp_id,
+			 u32 app_type, u32 acdb_id)
+{
+	u32 i = 0;
+
+	pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n",
+		__func__, rtac_adm_data.num_of_dev, port_id, copp_id);
+
+	mutex_lock(&rtac_adm_mutex);
+	if (rtac_adm_data.num_of_dev == RTAC_MAX_ACTIVE_DEVICES) {
+		pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+		goto done;
+	}
+
+	/* Check if device already added */
+	if (rtac_adm_data.num_of_dev != 0) {
+		for (; i < rtac_adm_data.num_of_dev; i++) {
+			if (rtac_adm_data.device[i].afe_port == port_id &&
+			    rtac_adm_data.device[i].copp == copp_id) {
+				add_popp(i, port_id, popp_id);
+				goto done;
+			}
+			if (rtac_adm_data.device[i].num_of_popp ==
+						RTAC_MAX_ACTIVE_POPP) {
+				pr_err("%s, Max POPP!\n", __func__);
+				goto done;
+			}
+		}
+	}
+
+	/* Add device */
+	rtac_adm_data.num_of_dev++;
+
+	rtac_adm_data.device[i].topology_id =
+		adm_get_topology_for_port_from_copp_id(port_id, copp_id);
+	rtac_adm_data.device[i].afe_topology =
+		afe_get_topology(port_id);
+	rtac_adm_data.device[i].afe_port = port_id;
+	rtac_adm_data.device[i].copp = copp_id;
+	rtac_adm_data.device[i].app_type = app_type;
+	rtac_adm_data.device[i].acdb_dev_id = acdb_id;
+	rtac_adm_data.device[i].popp[
+		rtac_adm_data.device[i].num_of_popp].popp = popp_id;
+	rtac_adm_data.device[i].popp[
+		rtac_adm_data.device[i].num_of_popp].popp_topology =
+		q6asm_get_asm_topology(popp_id);
+	rtac_adm_data.device[i].popp[
+		rtac_adm_data.device[i].num_of_popp++].app_type =
+		q6asm_get_asm_app_type(popp_id);
+
+	pr_debug("%s: topology = 0x%x, afe_topology = 0x%x, port_id = %d, copp_id = %d, app id = 0x%x, acdb id = %d, popp_id = %d, popp topology = 0x%x, popp app type = 0x%x\n",
+		__func__,
+		rtac_adm_data.device[i].topology_id,
+		rtac_adm_data.device[i].afe_topology,
+		rtac_adm_data.device[i].afe_port,
+		rtac_adm_data.device[i].copp,
+		rtac_adm_data.device[i].app_type,
+		rtac_adm_data.device[i].acdb_dev_id,
+		rtac_adm_data.device[i].popp[
+			rtac_adm_data.device[i].num_of_popp - 1].popp,
+		rtac_adm_data.device[i].popp[
+		rtac_adm_data.device[i].num_of_popp - 1].popp_topology,
+		rtac_adm_data.device[i].popp[
+		rtac_adm_data.device[i].num_of_popp - 1].app_type);
+done:
+	mutex_unlock(&rtac_adm_mutex);
+}
+
+static void shift_adm_devices(u32 dev_idx)
+{
+	for (; dev_idx < rtac_adm_data.num_of_dev; dev_idx++) {
+		memcpy(&rtac_adm_data.device[dev_idx],
+			&rtac_adm_data.device[dev_idx + 1],
+			sizeof(rtac_adm_data.device[dev_idx]));
+		memset(&rtac_adm_data.device[dev_idx + 1], 0,
+			   sizeof(rtac_adm_data.device[dev_idx]));
+	}
+}
+
+static void shift_popp(u32 copp_idx, u32 popp_idx)
+{
+	for (; popp_idx < rtac_adm_data.device[copp_idx].num_of_popp;
+							popp_idx++) {
+		memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx].popp,
+			&rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+			popp, sizeof(uint32_t));
+		memcpy(&rtac_adm_data.device[copp_idx].popp[popp_idx].
+			popp_topology,
+			&rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+			popp_topology,
+			sizeof(uint32_t));
+		memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+			popp, 0, sizeof(uint32_t));
+		memset(&rtac_adm_data.device[copp_idx].popp[popp_idx + 1].
+			popp_topology, 0, sizeof(uint32_t));
+	}
+}
+
+void rtac_remove_adm_device(u32 port_id, u32 copp_id)
+{
+	s32 i;
+
+	pr_debug("%s: num rtac devices %d port_id = %d, copp_id = %d\n",
+		__func__, rtac_adm_data.num_of_dev, port_id, copp_id);
+
+	mutex_lock(&rtac_adm_mutex);
+	/* look for device */
+	for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+		if (rtac_adm_data.device[i].afe_port == port_id &&
+		    rtac_adm_data.device[i].copp == copp_id) {
+			memset(&rtac_adm_data.device[i], 0,
+				   sizeof(rtac_adm_data.device[i]));
+			rtac_adm_data.num_of_dev--;
+
+			if (rtac_adm_data.num_of_dev >= 1) {
+				shift_adm_devices(i);
+				break;
+			}
+		}
+	}
+
+	mutex_unlock(&rtac_adm_mutex);
+}
+
+void rtac_remove_popp_from_adm_devices(u32 popp_id)
+{
+	s32 i, j;
+
+	pr_debug("%s: popp_id = %d\n", __func__, popp_id);
+
+	mutex_lock(&rtac_adm_mutex);
+	for (i = 0; i < rtac_adm_data.num_of_dev; i++) {
+		for (j = 0; j < rtac_adm_data.device[i].num_of_popp; j++) {
+			if (rtac_adm_data.device[i].popp[j].popp ==
+								popp_id) {
+				rtac_adm_data.device[i].popp[j].popp = 0;
+				rtac_adm_data.device[i].popp[j].
+					popp_topology = 0;
+				rtac_adm_data.device[i].num_of_popp--;
+				shift_popp(i, j);
+			}
+		}
+	}
+	mutex_unlock(&rtac_adm_mutex);
+}
+
+
+/* Voice Info */
+static void set_rtac_voice_data(int idx, u32 cvs_handle, u32 cvp_handle,
+					u32 rx_afe_port, u32 tx_afe_port,
+					u32 rx_acdb_id, u32 tx_acdb_id,
+					u32 session_id)
+{
+	rtac_voice_data.voice[idx].tx_topology_id =
+		voice_get_topology(CVP_VOC_TX_TOPOLOGY_CAL);
+	rtac_voice_data.voice[idx].rx_topology_id =
+		voice_get_topology(CVP_VOC_RX_TOPOLOGY_CAL);
+	rtac_voice_data.voice[idx].tx_afe_topology =
+		afe_get_topology(tx_afe_port);
+	rtac_voice_data.voice[idx].rx_afe_topology =
+		afe_get_topology(rx_afe_port);
+	rtac_voice_data.voice[idx].tx_afe_port = tx_afe_port;
+	rtac_voice_data.voice[idx].rx_afe_port = rx_afe_port;
+	rtac_voice_data.voice[idx].tx_acdb_id = tx_acdb_id;
+	rtac_voice_data.voice[idx].rx_acdb_id = rx_acdb_id;
+	rtac_voice_data.voice[idx].cvs_handle = cvs_handle;
+	rtac_voice_data.voice[idx].cvp_handle = cvp_handle;
+	pr_debug("%s\n%s: %x\n%s: %d %s: %d\n%s: %d %s: %d\n %s: %d\n %s: %d\n%s: %d %s: %d\n%s",
+		 "<---- Voice Data Info ---->", "Session id", session_id,
+		 "cvs_handle", cvs_handle, "cvp_handle", cvp_handle,
+		 "rx_afe_topology", rtac_voice_data.voice[idx].rx_afe_topology,
+		 "tx_afe_topology", rtac_voice_data.voice[idx].tx_afe_topology,
+		 "rx_afe_port", rx_afe_port, "tx_afe_port", tx_afe_port,
+		 "rx_acdb_id", rx_acdb_id, "tx_acdb_id", tx_acdb_id,
+		 "<-----------End----------->");
+
+	/* Store session ID for voice RTAC */
+	voice_session_id[idx] = session_id;
+}
+
+void rtac_add_voice(u32 cvs_handle, u32 cvp_handle, u32 rx_afe_port,
+			u32 tx_afe_port, u32 rx_acdb_id, u32 tx_acdb_id,
+			u32 session_id)
+{
+	u32 i = 0;
+
+	pr_debug("%s\n", __func__);
+	mutex_lock(&rtac_voice_mutex);
+
+	if (rtac_voice_data.num_of_voice_combos ==
+			RTAC_MAX_ACTIVE_VOICE_COMBOS) {
+		pr_err("%s, Can't add anymore RTAC devices!\n", __func__);
+		goto done;
+	}
+
+	/* Check if device already added */
+	if (rtac_voice_data.num_of_voice_combos != 0) {
+		for (; i < rtac_voice_data.num_of_voice_combos; i++) {
+			if (rtac_voice_data.voice[i].cvs_handle ==
+							cvs_handle) {
+				set_rtac_voice_data(i, cvs_handle, cvp_handle,
+					rx_afe_port, tx_afe_port, rx_acdb_id,
+					tx_acdb_id, session_id);
+				goto done;
+			}
+		}
+	}
+
+	/* Add device */
+	rtac_voice_data.num_of_voice_combos++;
+	set_rtac_voice_data(i, cvs_handle, cvp_handle,
+				rx_afe_port, tx_afe_port,
+				rx_acdb_id, tx_acdb_id,
+				session_id);
+done:
+	mutex_unlock(&rtac_voice_mutex);
+}
+
+static void shift_voice_devices(u32 idx)
+{
+	for (; idx < rtac_voice_data.num_of_voice_combos - 1; idx++) {
+		memcpy(&rtac_voice_data.voice[idx],
+			&rtac_voice_data.voice[idx + 1],
+			sizeof(rtac_voice_data.voice[idx]));
+		voice_session_id[idx] = voice_session_id[idx + 1];
+	}
+}
+
+void rtac_remove_voice(u32 cvs_handle)
+{
+	u32 i = 0;
+
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&rtac_voice_mutex);
+	/* look for device */
+	for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+		if (rtac_voice_data.voice[i].cvs_handle == cvs_handle) {
+			shift_voice_devices(i);
+			rtac_voice_data.num_of_voice_combos--;
+			memset(&rtac_voice_data.voice[
+				rtac_voice_data.num_of_voice_combos], 0,
+				sizeof(rtac_voice_data.voice
+				[rtac_voice_data.num_of_voice_combos]));
+			voice_session_id[rtac_voice_data.num_of_voice_combos]
+				= 0;
+			break;
+		}
+	}
+	mutex_unlock(&rtac_voice_mutex);
+}
+
+static u32 get_voice_session_id_cvs(u32 cvs_handle)
+{
+	u32 i;
+
+	for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+		if (rtac_voice_data.voice[i].cvs_handle == cvs_handle)
+			return voice_session_id[i];
+	}
+
+	pr_err("%s: No voice index for CVS handle %d found returning 0\n",
+	       __func__, cvs_handle);
+	return 0;
+}
+
+static u32 get_voice_session_id_cvp(u32 cvp_handle)
+{
+	u32 i;
+
+	for (i = 0; i < rtac_voice_data.num_of_voice_combos; i++) {
+		if (rtac_voice_data.voice[i].cvp_handle == cvp_handle)
+			return voice_session_id[i];
+	}
+
+	pr_err("%s: No voice index for CVP handle %d found returning 0\n",
+	       __func__, cvp_handle);
+	return 0;
+}
+
+static int get_voice_index(u32 mode, u32 handle)
+{
+	if (mode == RTAC_CVP)
+		return voice_get_idx_for_session(
+			get_voice_session_id_cvp(handle));
+	if (mode == RTAC_CVS)
+		return voice_get_idx_for_session(
+			get_voice_session_id_cvs(handle));
+
+	pr_err("%s: Invalid mode %d, returning 0\n",
+	       __func__, mode);
+	return 0;
+}
+
+
+/* ADM APR */
+void rtac_set_adm_handle(void *handle)
+{
+	pr_debug("%s: handle = %pK\n", __func__, handle);
+
+	mutex_lock(&rtac_adm_apr_mutex);
+	rtac_adm_apr_data.apr_handle = handle;
+	mutex_unlock(&rtac_adm_apr_mutex);
+}
+
+bool rtac_make_adm_callback(uint32_t *payload, u32 payload_size)
+{
+	pr_debug("%s:cmd_state = %d\n", __func__,
+			atomic_read(&rtac_adm_apr_data.cmd_state));
+	if (atomic_read(&rtac_adm_apr_data.cmd_state) != 1)
+		return false;
+
+	pr_debug("%s\n", __func__);
+	if (payload_size == sizeof(uint32_t))
+		atomic_set(&rtac_common.apr_err_code, payload[0]);
+	else if (payload_size == (2*sizeof(uint32_t)))
+		atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+	atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+	wake_up(&rtac_adm_apr_data.cmd_wait);
+	return true;
+}
+
+int send_adm_apr(void *buf, u32 opcode)
+{
+	s32	result;
+	u32	user_buf_size = 0;
+	u32	bytes_returned = 0;
+	u32	copp_id;
+	u32	payload_size;
+	u32	data_size = 0;
+	int	copp_idx;
+	int	port_idx;
+	struct apr_hdr	adm_params;
+
+	pr_debug("%s\n", __func__);
+
+	if (rtac_cal[ADM_RTAC_CAL].map_data.ion_handle == NULL) {
+		result = rtac_allocate_cal_buffer(ADM_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: allocate buffer failed!",
+				__func__);
+			goto done;
+		}
+	}
+
+	if (rtac_cal[ADM_RTAC_CAL].map_data.map_handle == 0) {
+		result = rtac_map_cal_buffer(ADM_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: map buffer failed!",
+				__func__);
+			goto done;
+		}
+	}
+
+	if (copy_from_user(&user_buf_size, (void *)buf,
+						sizeof(user_buf_size))) {
+		pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+		       __func__, buf);
+		goto done;
+	}
+	if (user_buf_size <= 0) {
+		pr_err("%s: Invalid buffer size = %d\n",
+			__func__, user_buf_size);
+		goto done;
+	}
+
+	if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+		pr_err("%s: Could not copy payload size from user buffer\n",
+			__func__);
+		goto done;
+	}
+
+	if (copy_from_user(&copp_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+		pr_err("%s: Could not copy port id from user buffer\n",
+			__func__);
+		goto done;
+	}
+
+	if (adm_get_indexes_from_copp_id(copp_id, &copp_idx, &port_idx) != 0) {
+		pr_err("%s: Copp Id-%d is not active\n", __func__, copp_id);
+		goto done;
+	}
+
+	mutex_lock(&rtac_adm_apr_mutex);
+	if (rtac_adm_apr_data.apr_handle == NULL) {
+		pr_err("%s: APR not initialized\n", __func__);
+		result = -EINVAL;
+		goto err;
+	}
+
+	if (opcode == ADM_CMD_SET_PP_PARAMS_V5) {
+		/* set payload size to in-band payload */
+		/* set data size to actual out of band payload size */
+		data_size = payload_size - 4 * sizeof(u32);
+		if (data_size > rtac_cal[ADM_RTAC_CAL].map_data.map_size) {
+			pr_err("%s: Invalid data size = %d\n",
+				__func__, data_size);
+			result = -EINVAL;
+			goto err;
+		}
+		payload_size = 4 * sizeof(u32);
+
+		/* Copy buffer to out-of-band payload */
+		if (copy_from_user((void *)
+				rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr,
+				buf + 7 * sizeof(u32), data_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+		/* set payload size in packet */
+		rtac_adm_buffer[8] = data_size;
+	} else {
+		if (payload_size > MAX_PAYLOAD_SIZE) {
+			pr_err("%s: Invalid payload size = %d\n",
+				__func__, payload_size);
+			result = -EINVAL;
+			goto err;
+		}
+
+		/* Copy buffer to in-band payload */
+		if (copy_from_user(rtac_adm_buffer +
+				sizeof(adm_params)/sizeof(u32),
+				buf + 3 * sizeof(u32), payload_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+	}
+
+	/* Pack header */
+	adm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(20), APR_PKT_VER);
+	adm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+		payload_size);
+	adm_params.src_svc = APR_SVC_ADM;
+	adm_params.src_domain = APR_DOMAIN_APPS;
+	adm_params.src_port = copp_id;
+	adm_params.dest_svc = APR_SVC_ADM;
+	adm_params.dest_domain = APR_DOMAIN_ADSP;
+	adm_params.dest_port = copp_id;
+	adm_params.token = port_idx << 16 | copp_idx;
+	adm_params.opcode = opcode;
+
+	/* fill for out-of-band */
+	rtac_adm_buffer[5] =
+		lower_32_bits(rtac_cal[ADM_RTAC_CAL].cal_data.paddr);
+	rtac_adm_buffer[6] =
+		msm_audio_populate_upper_32_bits(
+				rtac_cal[ADM_RTAC_CAL].cal_data.paddr);
+	rtac_adm_buffer[7] = rtac_cal[ADM_RTAC_CAL].map_data.map_handle;
+
+	memcpy(rtac_adm_buffer, &adm_params, sizeof(adm_params));
+	atomic_set(&rtac_adm_apr_data.cmd_state, 1);
+
+	pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+		__func__, opcode,
+		&rtac_cal[ADM_RTAC_CAL].cal_data.paddr);
+
+	result = apr_send_pkt(rtac_adm_apr_data.apr_handle,
+					(uint32_t *)rtac_adm_buffer);
+	if (result < 0) {
+		pr_err("%s: Set params failed copp = %d\n", __func__, copp_id);
+		goto err;
+	}
+	/* Wait for the callback */
+	result = wait_event_timeout(rtac_adm_apr_data.cmd_wait,
+		(atomic_read(&rtac_adm_apr_data.cmd_state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!result) {
+		pr_err("%s: Set params timed out copp = %d\n", __func__,
+			copp_id);
+		goto err;
+	}
+	if (atomic_read(&rtac_common.apr_err_code)) {
+		pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+			__func__, adsp_err_get_err_str(atomic_read(
+			&rtac_common.apr_err_code)),
+			opcode);
+		result = adsp_err_get_lnx_err_code(
+					atomic_read(
+					&rtac_common.apr_err_code));
+		goto err;
+	}
+
+	if (opcode == ADM_CMD_GET_PP_PARAMS_V5) {
+		bytes_returned = ((u32 *)rtac_cal[ADM_RTAC_CAL].cal_data.
+			kvaddr)[2] + 3 * sizeof(u32);
+
+		if (bytes_returned > user_buf_size) {
+			pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n",
+				__func__, user_buf_size, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+
+		if (copy_to_user(buf, (void *)
+				rtac_cal[ADM_RTAC_CAL].cal_data.kvaddr,
+				bytes_returned)) {
+			pr_err("%s: Could not copy buffer to user,size = %d\n",
+				__func__, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+	} else {
+		bytes_returned = data_size;
+	}
+	mutex_unlock(&rtac_adm_apr_mutex);
+done:
+	return bytes_returned;
+err:
+	mutex_unlock(&rtac_adm_apr_mutex);
+	return result;
+}
+
+
+/* ASM APR */
+void rtac_set_asm_handle(u32 session_id, void *handle)
+{
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&rtac_asm_apr_mutex);
+	rtac_asm_apr_data[session_id].apr_handle = handle;
+	mutex_unlock(&rtac_asm_apr_mutex);
+}
+
+bool rtac_make_asm_callback(u32 session_id, uint32_t *payload,
+	u32 payload_size)
+{
+	if (atomic_read(&rtac_asm_apr_data[session_id].cmd_state) != 1)
+		return false;
+
+	pr_debug("%s\n", __func__);
+	if (payload_size == sizeof(uint32_t))
+		atomic_set(&rtac_common.apr_err_code, payload[0]);
+	else if (payload_size == (2*sizeof(uint32_t)))
+		atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+	atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 0);
+	wake_up(&rtac_asm_apr_data[session_id].cmd_wait);
+	return true;
+}
+
+int send_rtac_asm_apr(void *buf, u32 opcode)
+{
+	s32 result;
+	u32 user_buf_size = 0;
+	u32 bytes_returned = 0;
+	u32 session_id = 0;
+	u32 payload_size;
+	u32 data_size = 0;
+	struct apr_hdr asm_params;
+
+	pr_debug("%s\n", __func__);
+
+	if (rtac_cal[ASM_RTAC_CAL].map_data.ion_handle == NULL) {
+		result = rtac_allocate_cal_buffer(ASM_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: allocate buffer failed!",
+				__func__);
+			goto done;
+		}
+	}
+
+	if (rtac_cal[ASM_RTAC_CAL].map_data.map_handle == 0) {
+		result = rtac_map_cal_buffer(ASM_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: map buffer failed!",
+				__func__);
+			goto done;
+		}
+	}
+
+	if (copy_from_user(&user_buf_size, (void *)buf,
+						sizeof(user_buf_size))) {
+		pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+		       __func__, buf);
+		goto done;
+	}
+	if (user_buf_size <= 0) {
+		pr_err("%s: Invalid buffer size = %d\n",
+			__func__, user_buf_size);
+		goto done;
+	}
+
+	if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+		pr_err("%s: Could not copy payload size from user buffer\n",
+			__func__);
+		goto done;
+	}
+
+	if (copy_from_user(&session_id, buf + 2 * sizeof(u32), sizeof(u32))) {
+		pr_err("%s: Could not copy session id from user buffer\n",
+			__func__);
+		goto done;
+	}
+	if (session_id >= (ASM_ACTIVE_STREAMS_ALLOWED + 1)) {
+		pr_err("%s: Invalid Session = %d\n", __func__, session_id);
+		goto done;
+	}
+
+	mutex_lock(&rtac_asm_apr_mutex);
+	if (rtac_asm_apr_data[session_id].apr_handle == NULL) {
+		pr_err("%s: APR not initialized\n", __func__);
+		result = -EINVAL;
+		goto err;
+	}
+
+	if (opcode == ASM_STREAM_CMD_SET_PP_PARAMS_V2) {
+		/* set payload size to in-band payload */
+		/* set data size to actual out of band payload size */
+		data_size = payload_size - 4 * sizeof(u32);
+		if (data_size > rtac_cal[ASM_RTAC_CAL].map_data.map_size) {
+			pr_err("%s: Invalid data size = %d\n",
+				__func__, data_size);
+			result = -EINVAL;
+			goto err;
+		}
+		payload_size = 4 * sizeof(u32);
+
+		/* Copy buffer to out-of-band payload */
+		if (copy_from_user((void *)
+				rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr,
+				buf + 7 * sizeof(u32), data_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+		/* set payload size in packet */
+		rtac_asm_buffer[8] = data_size;
+
+	} else {
+		if (payload_size > MAX_PAYLOAD_SIZE) {
+			pr_err("%s: Invalid payload size = %d\n",
+				__func__, payload_size);
+			result = -EINVAL;
+			goto err;
+		}
+
+		/* Copy buffer to in-band payload */
+		if (copy_from_user(rtac_asm_buffer +
+				sizeof(asm_params)/sizeof(u32),
+				buf + 3 * sizeof(u32), payload_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+	}
+
+	/* Pack header */
+	asm_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(20), APR_PKT_VER);
+	asm_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+		payload_size);
+	asm_params.src_svc = q6asm_get_apr_service_id(session_id);
+	asm_params.src_domain = APR_DOMAIN_APPS;
+	asm_params.src_port = (session_id << 8) | 0x0001;
+	asm_params.dest_svc = APR_SVC_ASM;
+	asm_params.dest_domain = APR_DOMAIN_ADSP;
+	asm_params.dest_port = (session_id << 8) | 0x0001;
+	asm_params.token = session_id;
+	asm_params.opcode = opcode;
+
+	/* fill for out-of-band */
+	rtac_asm_buffer[5] =
+		lower_32_bits(rtac_cal[ASM_RTAC_CAL].cal_data.paddr);
+	rtac_asm_buffer[6] =
+		msm_audio_populate_upper_32_bits(
+				rtac_cal[ASM_RTAC_CAL].cal_data.paddr);
+	rtac_asm_buffer[7] = rtac_cal[ASM_RTAC_CAL].map_data.map_handle;
+
+	memcpy(rtac_asm_buffer, &asm_params, sizeof(asm_params));
+	atomic_set(&rtac_asm_apr_data[session_id].cmd_state, 1);
+
+	pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+		__func__, opcode,
+		&rtac_cal[ASM_RTAC_CAL].cal_data.paddr);
+
+	result = apr_send_pkt(rtac_asm_apr_data[session_id].apr_handle,
+				(uint32_t *)rtac_asm_buffer);
+	if (result < 0) {
+		pr_err("%s: Set params failed session = %d\n",
+			__func__, session_id);
+		goto err;
+	}
+
+	/* Wait for the callback */
+	result = wait_event_timeout(rtac_asm_apr_data[session_id].cmd_wait,
+		(atomic_read(&rtac_asm_apr_data[session_id].cmd_state) == 0),
+		5 * HZ);
+	if (!result) {
+		pr_err("%s: Set params timed out session = %d\n",
+			__func__, session_id);
+		goto err;
+	}
+	if (atomic_read(&rtac_common.apr_err_code)) {
+		pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+			__func__, adsp_err_get_err_str(atomic_read(
+			&rtac_common.apr_err_code)),
+			opcode);
+		result = adsp_err_get_lnx_err_code(
+					atomic_read(
+					&rtac_common.apr_err_code));
+		goto err;
+	}
+
+	if (opcode == ASM_STREAM_CMD_GET_PP_PARAMS_V2) {
+		bytes_returned = ((u32 *)rtac_cal[ASM_RTAC_CAL].cal_data.
+			kvaddr)[2] + 3 * sizeof(u32);
+
+		if (bytes_returned > user_buf_size) {
+			pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n",
+				__func__, user_buf_size, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+
+		if (copy_to_user(buf, (void *)
+				rtac_cal[ASM_RTAC_CAL].cal_data.kvaddr,
+				bytes_returned)) {
+			pr_err("%s: Could not copy buffer to user,size = %d\n",
+				 __func__, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+	} else {
+		bytes_returned = data_size;
+	}
+	mutex_unlock(&rtac_asm_apr_mutex);
+done:
+	return bytes_returned;
+err:
+	mutex_unlock(&rtac_asm_apr_mutex);
+	return result;
+}
+
+/* AFE APR */
+void rtac_set_afe_handle(void *handle)
+{
+	mutex_lock(&rtac_afe_apr_mutex);
+	rtac_afe_apr_data.apr_handle = handle;
+	mutex_unlock(&rtac_afe_apr_mutex);
+}
+
+bool rtac_make_afe_callback(uint32_t *payload, uint32_t payload_size)
+{
+	pr_debug("%s:cmd_state = %d\n", __func__,
+			atomic_read(&rtac_afe_apr_data.cmd_state));
+	if (atomic_read(&rtac_afe_apr_data.cmd_state) != 1)
+		return false;
+
+	if (payload_size == sizeof(uint32_t))
+		atomic_set(&rtac_common.apr_err_code, payload[0]);
+	else if (payload_size == (2*sizeof(uint32_t)))
+		atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+	atomic_set(&rtac_afe_apr_data.cmd_state, 0);
+	wake_up(&rtac_afe_apr_data.cmd_wait);
+	return true;
+}
+
+static int fill_afe_apr_hdr(struct apr_hdr *apr_hdr, uint32_t port,
+			 uint32_t opcode, uint32_t apr_msg_size)
+{
+	if (apr_hdr == NULL) {
+		pr_err("%s: invalid APR pointer", __func__);
+		return -EINVAL;
+	}
+
+	apr_hdr->hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(APR_HDR_SIZE), APR_PKT_VER);
+	apr_hdr->pkt_size = apr_msg_size;
+	apr_hdr->src_svc = APR_SVC_AFE;
+	apr_hdr->src_domain = APR_DOMAIN_APPS;
+	apr_hdr->src_port = 0;
+	apr_hdr->dest_svc = APR_SVC_AFE;
+	apr_hdr->dest_domain = APR_DOMAIN_ADSP;
+	apr_hdr->dest_port = 0;
+	apr_hdr->token = port;
+	apr_hdr->opcode = opcode;
+
+	return 0;
+
+}
+static int send_rtac_afe_apr(void *buf, uint32_t opcode)
+{
+	int32_t result;
+	uint32_t bytes_returned = 0;
+	uint32_t port_index = 0;
+	uint32_t apr_msg_size = 0;
+	struct rtac_afe_user_data user_afe_buf;
+
+	pr_debug("%s\n", __func__);
+
+	if (rtac_cal[AFE_RTAC_CAL].map_data.ion_handle == NULL) {
+		result = rtac_allocate_cal_buffer(AFE_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: allocate buffer failed! ret = %d\n",
+				__func__, result);
+			goto done;
+		}
+	}
+
+	if (rtac_cal[AFE_RTAC_CAL].map_data.map_handle == 0) {
+		result = rtac_map_cal_buffer(AFE_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: map buffer failed! ret = %d\n",
+				__func__, result);
+			goto done;
+		}
+	}
+
+	if (copy_from_user(&user_afe_buf, (void *)buf,
+		sizeof(struct rtac_afe_user_data))) {
+		pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+		       __func__, buf);
+		goto done;
+	}
+
+	if (user_afe_buf.buf_size <= 0) {
+		pr_err("%s: Invalid buffer size = %d\n",
+			__func__, user_afe_buf.buf_size);
+		goto done;
+	}
+
+	port_index = q6audio_get_port_index(user_afe_buf.port_id);
+	if (port_index >= AFE_MAX_PORTS) {
+		pr_err("%s: Invalid AFE port = 0x%x\n",
+		       __func__, user_afe_buf.port_id);
+		goto done;
+	}
+
+	mutex_lock(&rtac_afe_apr_mutex);
+	if (rtac_afe_apr_data.apr_handle == NULL) {
+		pr_err("%s: APR not initialized\n", __func__);
+		result = -EINVAL;
+		goto err;
+	}
+	if (opcode == AFE_PORT_CMD_SET_PARAM_V2) {
+		struct afe_port_cmd_set_param_v2 *afe_set_apr_msg;
+
+		/* set data size to actual out of band payload size */
+		if (user_afe_buf.rtac_afe_set.cmd.payload_size >
+			rtac_cal[AFE_RTAC_CAL].map_data.map_size) {
+			pr_err("%s: Invalid data size = %d\n",
+				   __func__,
+				   user_afe_buf.rtac_afe_set.cmd.payload_size);
+			result = -EINVAL;
+			goto err;
+		}
+
+		/* Copy buffer to out-of-band payload */
+		if (copy_from_user((void *)
+				rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
+				buf+offsetof(struct rtac_afe_user_data,
+				rtac_afe_set.data),
+				user_afe_buf.rtac_afe_set.cmd.payload_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+
+		/* Copy AFE APR Message */
+		afe_set_apr_msg = (struct afe_port_cmd_set_param_v2 *)
+				((u8 *)rtac_afe_buffer +
+				sizeof(struct apr_hdr));
+		if (copy_from_user((void *)
+				afe_set_apr_msg,
+				buf + offsetof(struct rtac_afe_user_data,
+				rtac_afe_set.cmd),
+				sizeof(struct afe_port_cmd_set_param_v2))) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+
+		afe_set_apr_msg->payload_address_lsw =
+			lower_32_bits(rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+		afe_set_apr_msg->payload_address_msw =
+				msm_audio_populate_upper_32_bits(
+					rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+		afe_set_apr_msg->mem_map_handle =
+				rtac_cal[AFE_RTAC_CAL].map_data.map_handle;
+
+		apr_msg_size = sizeof(struct apr_hdr) +
+				sizeof(struct afe_port_cmd_set_param_v2);
+
+	} else {
+		struct afe_port_cmd_get_param_v2 *afe_get_apr_msg;
+
+		if (user_afe_buf.cmd_size > MAX_PAYLOAD_SIZE) {
+			pr_err("%s: Invalid payload size = %d\n",
+				__func__, user_afe_buf.cmd_size);
+			result = -EINVAL;
+			goto err;
+		}
+
+		/* Copy buffer to in-band payload */
+		afe_get_apr_msg = (struct afe_port_cmd_get_param_v2 *)
+					((u8 *) rtac_afe_buffer +
+					sizeof(struct apr_hdr));
+		if (copy_from_user((void *)afe_get_apr_msg,
+				buf+offsetof(struct rtac_afe_user_data,
+				rtac_afe_get.cmd),
+			sizeof(struct afe_port_cmd_get_param_v2))) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+
+		afe_get_apr_msg->payload_address_lsw =
+			lower_32_bits(rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+		afe_get_apr_msg->payload_address_msw =
+				msm_audio_populate_upper_32_bits(
+					rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+		afe_get_apr_msg->mem_map_handle =
+				rtac_cal[AFE_RTAC_CAL].map_data.map_handle;
+		afe_get_apr_msg->payload_size -= sizeof(struct apr_hdr);
+		apr_msg_size = sizeof(struct apr_hdr) +
+				sizeof(struct afe_port_cmd_get_param_v2);
+	}
+
+	fill_afe_apr_hdr((struct apr_hdr *) rtac_afe_buffer,
+			port_index, opcode, apr_msg_size);
+
+	atomic_set(&rtac_afe_apr_data.cmd_state, 1);
+
+	pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+		__func__, opcode,
+		&rtac_cal[AFE_RTAC_CAL].cal_data.paddr);
+
+	result = apr_send_pkt(rtac_afe_apr_data.apr_handle,
+					(uint32_t *)rtac_afe_buffer);
+	if (result < 0) {
+		pr_err("%s: Set params failed port = 0x%x, ret = %d\n",
+			__func__, user_afe_buf.port_id, result);
+		goto err;
+	}
+	/* Wait for the callback */
+	result = wait_event_timeout(rtac_afe_apr_data.cmd_wait,
+		(atomic_read(&rtac_afe_apr_data.cmd_state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!result) {
+		pr_err("%s: Set params timed out port = 0x%x, ret = %d\n",
+			__func__, user_afe_buf.port_id, result);
+		goto err;
+	}
+	if (atomic_read(&rtac_common.apr_err_code)) {
+		pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+			__func__, adsp_err_get_err_str(atomic_read(
+			&rtac_common.apr_err_code)),
+			opcode);
+		result = adsp_err_get_lnx_err_code(
+					atomic_read(
+					&rtac_common.apr_err_code));
+		goto err;
+	}
+
+	if (opcode == AFE_PORT_CMD_GET_PARAM_V2) {
+		struct afe_port_param_data_v2 *get_resp;
+
+		get_resp = (struct afe_port_param_data_v2 *)
+				rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr;
+
+		bytes_returned = get_resp->param_size +
+				sizeof(struct afe_port_param_data_v2);
+
+		if (bytes_returned > user_afe_buf.buf_size) {
+			pr_err("%s: user size = 0x%x, returned size = 0x%x\n",
+				__func__, user_afe_buf.buf_size,
+				bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+
+		if (copy_to_user(buf, (void *)
+				rtac_cal[AFE_RTAC_CAL].cal_data.kvaddr,
+				bytes_returned)) {
+			pr_err("%s: Could not copy buffer to user,size = %d\n",
+				__func__, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+	} else {
+		bytes_returned = user_afe_buf.rtac_afe_set.cmd.payload_size;
+	}
+	mutex_unlock(&rtac_afe_apr_mutex);
+done:
+	return bytes_returned;
+err:
+	mutex_unlock(&rtac_afe_apr_mutex);
+	return result;
+}
+
+/* Voice APR */
+void rtac_set_voice_handle(u32 mode, void *handle)
+{
+	pr_debug("%s\n", __func__);
+
+	mutex_lock(&rtac_voice_apr_mutex);
+	rtac_voice_apr_data[mode].apr_handle = handle;
+	mutex_unlock(&rtac_voice_apr_mutex);
+}
+
+bool rtac_make_voice_callback(u32 mode, uint32_t *payload, u32 payload_size)
+{
+	if ((atomic_read(&rtac_voice_apr_data[mode].cmd_state) != 1) ||
+		(mode >= RTAC_VOICE_MODES))
+		return false;
+
+	pr_debug("%s\n", __func__);
+	if (payload_size == sizeof(uint32_t))
+		atomic_set(&rtac_common.apr_err_code, payload[0]);
+	else if (payload_size == (2*sizeof(uint32_t)))
+		atomic_set(&rtac_common.apr_err_code, payload[1]);
+
+	atomic_set(&rtac_voice_apr_data[mode].cmd_state, 0);
+	wake_up(&rtac_voice_apr_data[mode].cmd_wait);
+	return true;
+}
+
+int send_voice_apr(u32 mode, void *buf, u32 opcode)
+{
+	s32 result;
+	u32 user_buf_size = 0;
+	u32 bytes_returned = 0;
+	u32 payload_size;
+	u32 dest_port;
+	u32 data_size = 0;
+	struct apr_hdr voice_params;
+
+	pr_debug("%s\n", __func__);
+
+	if (rtac_cal[VOICE_RTAC_CAL].map_data.ion_handle == NULL) {
+		result = rtac_allocate_cal_buffer(VOICE_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: allocate buffer failed!",
+				__func__);
+			goto done;
+		}
+	}
+
+	if (rtac_cal[VOICE_RTAC_CAL].map_data.map_handle == 0) {
+		result = rtac_map_cal_buffer(VOICE_RTAC_CAL);
+		if (result < 0) {
+			pr_err("%s: map buffer failed!",
+				__func__);
+			goto done;
+		}
+	}
+
+	if (copy_from_user(&user_buf_size, (void *)buf,
+						sizeof(user_buf_size))) {
+		pr_err("%s: Copy from user failed! buf = 0x%pK\n",
+		       __func__, buf);
+		goto done;
+	}
+	if (user_buf_size <= 0) {
+		pr_err("%s: Invalid buffer size = %d\n",
+			__func__, user_buf_size);
+		goto done;
+	}
+
+	if (copy_from_user(&payload_size, buf + sizeof(u32), sizeof(u32))) {
+		pr_err("%s: Could not copy payload size from user buffer\n",
+			__func__);
+		goto done;
+	}
+
+	if (copy_from_user(&dest_port, buf + 2 * sizeof(u32), sizeof(u32))) {
+		pr_err("%s: Could not copy port id from user buffer\n",
+			__func__);
+		goto done;
+	}
+
+	if ((mode != RTAC_CVP) && (mode != RTAC_CVS)) {
+		pr_err("%s: Invalid Mode for APR, mode = %d\n",
+			__func__, mode);
+		goto done;
+	}
+
+	mutex_lock(&rtac_voice_apr_mutex);
+	if (rtac_voice_apr_data[mode].apr_handle == NULL) {
+		pr_err("%s: APR not initialized\n", __func__);
+		result = -EINVAL;
+		goto err;
+	}
+
+	if (opcode == VOICE_CMD_SET_PARAM) {
+		/* set payload size to in-band payload */
+		/* set data size to actual out of band payload size */
+		data_size = payload_size - 4 * sizeof(u32);
+		if (data_size > rtac_cal[VOICE_RTAC_CAL].map_data.map_size) {
+			pr_err("%s: Invalid data size = %d\n",
+				__func__, data_size);
+			result = -EINVAL;
+			goto err;
+		}
+		payload_size = 4 * sizeof(u32);
+
+		/* Copy buffer to out-of-band payload */
+		if (copy_from_user((void *)
+				rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr,
+				buf + 7 * sizeof(u32), data_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+		/* set payload size in packet */
+		rtac_voice_buffer[8] = data_size;
+	} else {
+		if (payload_size > MAX_PAYLOAD_SIZE) {
+			pr_err("%s: Invalid payload size = %d\n",
+					__func__, payload_size);
+			result = -EINVAL;
+			goto err;
+		}
+
+		/* Copy buffer to in-band payload */
+		if (copy_from_user(rtac_voice_buffer +
+				sizeof(voice_params)/sizeof(u32),
+				buf + 3 * sizeof(u32), payload_size)) {
+			pr_err("%s: Could not copy payload from user buffer\n",
+				__func__);
+			result = -EINVAL;
+			goto err;
+		}
+	}
+
+	/* Pack header */
+	voice_params.hdr_field = APR_HDR_FIELD(APR_MSG_TYPE_SEQ_CMD,
+		APR_HDR_LEN(20), APR_PKT_VER);
+	voice_params.pkt_size = APR_PKT_SIZE(APR_HDR_SIZE,
+		payload_size);
+	voice_params.src_svc = 0;
+	voice_params.src_domain = APR_DOMAIN_APPS;
+	voice_params.src_port = get_voice_index(mode, dest_port);
+	voice_params.dest_svc = 0;
+	voice_params.dest_domain = APR_DOMAIN_MODEM;
+	voice_params.dest_port = (u16)dest_port;
+	voice_params.token = 0;
+	voice_params.opcode = opcode;
+
+	/* fill for out-of-band */
+	rtac_voice_buffer[5] = rtac_cal[VOICE_RTAC_CAL].map_data.map_handle;
+	rtac_voice_buffer[6] =
+		lower_32_bits(rtac_cal[VOICE_RTAC_CAL].cal_data.paddr);
+	rtac_voice_buffer[7] =
+		msm_audio_populate_upper_32_bits(
+				rtac_cal[VOICE_RTAC_CAL].cal_data.paddr);
+
+	memcpy(rtac_voice_buffer, &voice_params, sizeof(voice_params));
+	atomic_set(&rtac_voice_apr_data[mode].cmd_state, 1);
+
+	pr_debug("%s: Sending RTAC command ioctl 0x%x, paddr 0x%pK\n",
+		__func__, opcode,
+		&rtac_cal[VOICE_RTAC_CAL].cal_data.paddr);
+
+	result = apr_send_pkt(rtac_voice_apr_data[mode].apr_handle,
+					(uint32_t *)rtac_voice_buffer);
+	if (result < 0) {
+		pr_err("%s: apr_send_pkt failed opcode = %x\n",
+			__func__, opcode);
+		goto err;
+	}
+	/* Wait for the callback */
+	result = wait_event_timeout(rtac_voice_apr_data[mode].cmd_wait,
+		(atomic_read(&rtac_voice_apr_data[mode].cmd_state) == 0),
+		msecs_to_jiffies(TIMEOUT_MS));
+	if (!result) {
+		pr_err("%s: apr_send_pkt timed out opcode = %x\n",
+			__func__, opcode);
+		goto err;
+	}
+	if (atomic_read(&rtac_common.apr_err_code)) {
+		pr_err("%s: DSP returned error code = [%s], opcode = 0x%x\n",
+			__func__, adsp_err_get_err_str(atomic_read(
+			&rtac_common.apr_err_code)),
+			opcode);
+		result = adsp_err_get_lnx_err_code(
+					atomic_read(
+					&rtac_common.apr_err_code));
+		goto err;
+	}
+
+	if (opcode == VOICE_CMD_GET_PARAM) {
+		bytes_returned = ((u32 *)rtac_cal[VOICE_RTAC_CAL].cal_data.
+			kvaddr)[2] + 3 * sizeof(u32);
+
+		if (bytes_returned > user_buf_size) {
+			pr_err("%s: User buf not big enough, size = 0x%x, returned size = 0x%x\n",
+				__func__, user_buf_size, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+
+		if (copy_to_user(buf, (void *)
+				rtac_cal[VOICE_RTAC_CAL].cal_data.kvaddr,
+				bytes_returned)) {
+			pr_err("%s: Could not copy buffer to user, size = %d\n",
+				 __func__, bytes_returned);
+			result = -EINVAL;
+			goto err;
+		}
+	} else {
+		bytes_returned = data_size;
+	}
+	mutex_unlock(&rtac_voice_apr_mutex);
+done:
+	return bytes_returned;
+err:
+	mutex_unlock(&rtac_voice_apr_mutex);
+	return result;
+}
+
+void get_rtac_adm_data(struct rtac_adm *adm_data)
+{
+	mutex_lock(&rtac_adm_mutex);
+	memcpy(adm_data, &rtac_adm_data, sizeof(struct rtac_adm));
+	mutex_unlock(&rtac_adm_mutex);
+}
+
+
+static long rtac_ioctl_shared(struct file *f,
+		unsigned int cmd, void *arg)
+{
+	int result = 0;
+
+	if (!arg) {
+		pr_err("%s: No data sent to driver!\n", __func__);
+		result = -EFAULT;
+		goto done;
+	}
+
+	switch (cmd) {
+	case AUDIO_GET_RTAC_ADM_INFO: {
+		mutex_lock(&rtac_adm_mutex);
+		if (copy_to_user((void *)arg, &rtac_adm_data,
+						sizeof(rtac_adm_data))) {
+			pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_ADM_INFO\n",
+					__func__);
+			mutex_unlock(&rtac_adm_mutex);
+			return -EFAULT;
+		}
+		result = sizeof(rtac_adm_data);
+		mutex_unlock(&rtac_adm_mutex);
+		break;
+	}
+	case AUDIO_GET_RTAC_VOICE_INFO: {
+		mutex_lock(&rtac_voice_mutex);
+		if (copy_to_user((void *)arg, &rtac_voice_data,
+						sizeof(rtac_voice_data))) {
+			pr_err("%s: copy_to_user failed for AUDIO_GET_RTAC_VOICE_INFO\n",
+					__func__);
+			mutex_unlock(&rtac_voice_mutex);
+			return -EFAULT;
+		}
+		result = sizeof(rtac_voice_data);
+		mutex_unlock(&rtac_voice_mutex);
+		break;
+	}
+
+	case AUDIO_GET_RTAC_ADM_CAL:
+		result = send_adm_apr((void *)arg, ADM_CMD_GET_PP_PARAMS_V5);
+		break;
+	case AUDIO_SET_RTAC_ADM_CAL:
+		result = send_adm_apr((void *)arg, ADM_CMD_SET_PP_PARAMS_V5);
+		break;
+	case AUDIO_GET_RTAC_ASM_CAL:
+		result = send_rtac_asm_apr((void *)arg,
+			ASM_STREAM_CMD_GET_PP_PARAMS_V2);
+		break;
+	case AUDIO_SET_RTAC_ASM_CAL:
+		result = send_rtac_asm_apr((void *)arg,
+			ASM_STREAM_CMD_SET_PP_PARAMS_V2);
+		break;
+	case AUDIO_GET_RTAC_CVS_CAL:
+		result = send_voice_apr(RTAC_CVS, (void *)arg,
+			VOICE_CMD_GET_PARAM);
+		break;
+	case AUDIO_SET_RTAC_CVS_CAL:
+		result = send_voice_apr(RTAC_CVS, (void *)arg,
+			VOICE_CMD_SET_PARAM);
+		break;
+	case AUDIO_GET_RTAC_CVP_CAL:
+		result = send_voice_apr(RTAC_CVP, (void *)arg,
+			VOICE_CMD_GET_PARAM);
+		break;
+	case AUDIO_SET_RTAC_CVP_CAL:
+		result = send_voice_apr(RTAC_CVP, (void *)arg,
+			VOICE_CMD_SET_PARAM);
+		break;
+	case AUDIO_GET_RTAC_AFE_CAL:
+		result = send_rtac_afe_apr((void *)arg,
+			AFE_PORT_CMD_GET_PARAM_V2);
+		break;
+	case AUDIO_SET_RTAC_AFE_CAL:
+		result = send_rtac_afe_apr((void *)arg,
+			AFE_PORT_CMD_SET_PARAM_V2);
+		break;
+	default:
+		pr_err("%s: Invalid IOCTL, command = %d!\n",
+		       __func__, cmd);
+		result = -EINVAL;
+	}
+done:
+	return result;
+}
+
+static long rtac_ioctl(struct file *f,
+		unsigned int cmd, unsigned long arg)
+{
+	int result = 0;
+
+	if (!arg) {
+		pr_err("%s: No data sent to driver!\n", __func__);
+		result = -EFAULT;
+	} else {
+		result = rtac_ioctl_shared(f, cmd, (void __user *)arg);
+	}
+
+	return result;
+}
+
+#ifdef CONFIG_COMPAT
+#define AUDIO_GET_RTAC_ADM_INFO_32   _IOR(CAL_IOCTL_MAGIC, 207, compat_uptr_t)
+#define AUDIO_GET_RTAC_VOICE_INFO_32 _IOR(CAL_IOCTL_MAGIC, 208, compat_uptr_t)
+#define AUDIO_GET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 209, compat_uptr_t)
+#define AUDIO_SET_RTAC_ADM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 210, compat_uptr_t)
+#define AUDIO_GET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 211, compat_uptr_t)
+#define AUDIO_SET_RTAC_ASM_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 212, compat_uptr_t)
+#define AUDIO_GET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 213, compat_uptr_t)
+#define AUDIO_SET_RTAC_CVS_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 214, compat_uptr_t)
+#define AUDIO_GET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 215, compat_uptr_t)
+#define AUDIO_SET_RTAC_CVP_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 216, compat_uptr_t)
+#define AUDIO_GET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 217, compat_uptr_t)
+#define AUDIO_SET_RTAC_AFE_CAL_32 _IOWR(CAL_IOCTL_MAGIC, 218, compat_uptr_t)
+
+static long rtac_compat_ioctl(struct file *f,
+		unsigned int cmd, unsigned long arg)
+{
+	int result = 0;
+
+	if (!arg) {
+		pr_err("%s: No data sent to driver!\n", __func__);
+		result = -EINVAL;
+		goto done;
+	}
+
+	switch (cmd) {
+	case AUDIO_GET_RTAC_ADM_INFO_32:
+		cmd = AUDIO_GET_RTAC_ADM_INFO;
+		goto process;
+	case AUDIO_GET_RTAC_VOICE_INFO_32:
+		cmd = AUDIO_GET_RTAC_VOICE_INFO;
+		goto process;
+	case AUDIO_GET_RTAC_AFE_CAL_32:
+		cmd = AUDIO_GET_RTAC_AFE_CAL;
+		goto process;
+	case AUDIO_SET_RTAC_AFE_CAL_32:
+		cmd = AUDIO_SET_RTAC_AFE_CAL;
+		goto process;
+	case AUDIO_GET_RTAC_ADM_CAL_32:
+		cmd = AUDIO_GET_RTAC_ADM_CAL;
+		goto process;
+	case AUDIO_SET_RTAC_ADM_CAL_32:
+		cmd = AUDIO_SET_RTAC_ADM_CAL;
+		goto process;
+	case AUDIO_GET_RTAC_ASM_CAL_32:
+		cmd = AUDIO_GET_RTAC_ASM_CAL;
+		goto process;
+	case AUDIO_SET_RTAC_ASM_CAL_32:
+		cmd =  AUDIO_SET_RTAC_ASM_CAL;
+		goto process;
+	case AUDIO_GET_RTAC_CVS_CAL_32:
+		cmd = AUDIO_GET_RTAC_CVS_CAL;
+		goto process;
+	case AUDIO_SET_RTAC_CVS_CAL_32:
+		cmd = AUDIO_SET_RTAC_CVS_CAL;
+		goto process;
+	case AUDIO_GET_RTAC_CVP_CAL_32:
+		cmd =  AUDIO_GET_RTAC_CVP_CAL;
+		goto process;
+	case AUDIO_SET_RTAC_CVP_CAL_32:
+		cmd = AUDIO_SET_RTAC_CVP_CAL;
+process:
+		result = rtac_ioctl_shared(f, cmd, compat_ptr(arg));
+		break;
+	default:
+		result = -EINVAL;
+		pr_err("%s: Invalid IOCTL, command = %d!\n",
+		       __func__, cmd);
+		break;
+	}
+done:
+	return result;
+}
+#else
+#define rtac_compat_ioctl NULL
+#endif
+
+static const struct file_operations rtac_fops = {
+	.owner = THIS_MODULE,
+	.open = rtac_open,
+	.release = rtac_release,
+	.unlocked_ioctl = rtac_ioctl,
+	.compat_ioctl = rtac_compat_ioctl,
+};
+
+struct miscdevice rtac_misc = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "msm_rtac",
+	.fops	= &rtac_fops,
+};
+
+static int __init rtac_init(void)
+{
+	int i = 0;
+
+	/* Driver */
+	atomic_set(&rtac_common.usage_count, 0);
+	atomic_set(&rtac_common.apr_err_code, 0);
+
+	/* ADM */
+	memset(&rtac_adm_data, 0, sizeof(rtac_adm_data));
+	rtac_adm_apr_data.apr_handle = NULL;
+	atomic_set(&rtac_adm_apr_data.cmd_state, 0);
+	init_waitqueue_head(&rtac_adm_apr_data.cmd_wait);
+	mutex_init(&rtac_adm_mutex);
+	mutex_init(&rtac_adm_apr_mutex);
+
+	rtac_adm_buffer = kzalloc(
+		rtac_cal[ADM_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+	if (rtac_adm_buffer == NULL)
+		goto nomem;
+
+	/* ASM */
+	for (i = 0; i < ASM_ACTIVE_STREAMS_ALLOWED+1; i++) {
+		rtac_asm_apr_data[i].apr_handle = NULL;
+		atomic_set(&rtac_asm_apr_data[i].cmd_state, 0);
+		init_waitqueue_head(&rtac_asm_apr_data[i].cmd_wait);
+	}
+	mutex_init(&rtac_asm_apr_mutex);
+
+	rtac_asm_buffer = kzalloc(
+		rtac_cal[ASM_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+	if (rtac_asm_buffer == NULL) {
+		kzfree(rtac_adm_buffer);
+		goto nomem;
+	}
+
+	/* AFE */
+	rtac_afe_apr_data.apr_handle = NULL;
+	atomic_set(&rtac_afe_apr_data.cmd_state, 0);
+	init_waitqueue_head(&rtac_afe_apr_data.cmd_wait);
+	mutex_init(&rtac_afe_apr_mutex);
+
+	rtac_afe_buffer = kzalloc(
+		rtac_cal[AFE_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+	if (rtac_afe_buffer == NULL) {
+		kzfree(rtac_adm_buffer);
+		kzfree(rtac_asm_buffer);
+		goto nomem;
+	}
+
+	/* Voice */
+	memset(&rtac_voice_data, 0, sizeof(rtac_voice_data));
+	for (i = 0; i < RTAC_VOICE_MODES; i++) {
+		rtac_voice_apr_data[i].apr_handle = NULL;
+		atomic_set(&rtac_voice_apr_data[i].cmd_state, 0);
+		init_waitqueue_head(&rtac_voice_apr_data[i].cmd_wait);
+	}
+	mutex_init(&rtac_voice_mutex);
+	mutex_init(&rtac_voice_apr_mutex);
+
+	rtac_voice_buffer = kzalloc(
+		rtac_cal[VOICE_RTAC_CAL].map_data.map_size, GFP_KERNEL);
+	if (rtac_voice_buffer == NULL) {
+		kzfree(rtac_adm_buffer);
+		kzfree(rtac_asm_buffer);
+		kzfree(rtac_afe_buffer);
+		goto nomem;
+	}
+
+	return misc_register(&rtac_misc);
+nomem:
+	return -ENOMEM;
+}
+
+module_init(rtac_init);
+
+MODULE_DESCRIPTION("SoC QDSP6v2 Real-Time Audio Calibration driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 8ec1e3c..e6a67cdd 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -911,7 +911,17 @@ EXPORT_SYMBOL_GPL(snd_soc_resume);
 static const struct snd_soc_dai_ops null_dai_ops = {
 };
 
-static struct snd_soc_component *soc_find_component(
+/**
+ * soc_find_component: find a component from component_list in ASoC core
+ *
+ * @of_node: of_node of the component to query.
+ * @name: name of the component to query.
+ *
+ * function to find out if a component is already registered with ASoC core.
+ *
+ * Returns component handle for success, else NULL error.
+ */
+struct snd_soc_component *soc_find_component(
 	const struct device_node *of_node, const char *name)
 {
 	struct snd_soc_component *component;
@@ -935,6 +945,7 @@ static struct snd_soc_component *soc_find_component(
 
 	return NULL;
 }
+EXPORT_SYMBOL(soc_find_component);
 
 /**
  * snd_soc_find_dai - Find a registered DAI